verilog part deux

 

 

Finished NO SCHOOL ! Here are some things I learned regarding the board itself :

  • the low profile VGA connector is a nightmare, never again.
  • the reset button needs to go elsewhere as Lattice IceCube doesn’t like pin 66 for some reason
  • the 3D printed cases suck, not necessary and doesn’t have NO SCHOOL on it
  • the pads for the bias trim knob kept breaking, not sure if it is bad PCB manufacturing or poor design. Also a bit too hard for beginners to solder this part.
  • the portable low power soldering irons sucked for this
  • I can remove the caps and 1K resistors for the button debouncing
  • the video pass through mode is annoying to set up (needing an audio cable, remembering which pin is H and V sync etc.)
  • People like the mechanical keys
  • The VCC and GND is useful for powering a breadboard with other video mods

Regarding the hands on video glitching workshop :

  • Breadboards and pinout of the VGA connector tough to get the hang of.
  • Important to make a safe space – you can’t hurt yourself, nothing valuable here, etc.
  • I did a free-style open-ended workshop but still ended up starting with asking the participants to :
    • use jumpers to plug in their board to a VGA cable (though I should have skipped this and went directly to plugging in to the breadboard)
    • use jumpers to plug in their board to a breadboard and then to the VGA cable
    • use switches to turn on or off different channels
    • use pots to attenuate different color channels
    • use coil of wire to make a delay
    • use LM386 to amplify
    • 74*04 to invert
    • Circuit bending the schmitt trigger
    • Mixing signals: 74*86 to XOR, or just pots
  • I should have gotten them to put audio in the pass through mode !
  • Forgot to ask them to make BLACK AND WHITE from color
  • There was no oscilloscope or function generator to work with, I should have brought a logic analyzer too
  • Karl Marx was a big hit
  • I hand drew some schematics (pinouts of various chips, circuits etc.) and printed them, then would project and draw on the big board the pinouts
  • They have difficulty with the translation of schematic to actual connections.
  • I can imagine a board made specifically for this workshop which would have an FPGA or Arduino (but without any buttons just as a kind of screen saver) and would connect to a breadboard, breaking out the different pins, where you could do things. This would have gotten around the pain of plugging in jumpers to the VGA connectors, and would be far less work than making the entire FPGA béchamel board. It could be a super basic atmega with just a knob to select patterns.

****

Paul suggests adding to my open source FPGA video synth page :

  • A nicer README that explains the project in depth and uses Markdown to make things easier and nicer to read and navigate
  • An explanation of how people can use and contribute to the project
  • A better organization of the files in the project
  • Selecting a licence
  • Using git on the PC instead of Github

I really like the idea of doing a super page with gifs explaining what codes do, compiled binaries and commented verilog codes.

I wonder if I could work on a series of separate effects, each with it’s own module, that would allow people to add effects.

Looking in to open source hardware projects for inspiration:

  • OPEN MV (https://github.com/openmv/openmv): Uses an 32-bit Arm Cortex-M4 chip with DSPs.
  • MUTABLE INSTRUMENTS (https://pichenettes.github.io/mutable-instruments-documentation/) Open source versions of classic eurorack modules

I have rebuilt the git repo and it looks way better now :

What I’ve learned :

  • I can import Eagle boards (but not my schematics so far) into KiCAD but I should build using KiCAD from now on for open source projects
  • I can take all the energy I was using to make this website and put it into making an awesome README for my project
  • A project can almost start with the README, so that it is clear from the beginning what the thing is and what it does

I’ve also promised to send boards to people, perhaps a little prematurely ! Ideally it would be easy for people to upgrade their firmware, this way I could freeze the hardware design and just focus on making code. But this would require giving them a programmer ?

******

Check out this FPGA graphics tutorial : https://projectf.io/posts/fpga-graphics/

Essentially, start by generating a pixel clock for the chosen resolution. Look up standard VGA timings and set up the parameters for blanking etc. Set up a counter and a display enable signal, and then create shapes by using equality operators and colors.

This racing the beam tutorial (https://tomverbeure.github.io/rtl/2018/11/26/Racing-the-Beam-Ray-Tracer.html) explains that if you don’t have enough space for a frame buffer you can have a line buffer, or even a pixel buffer, if you can compute things fast enough. Later in the series using raster font in a simple way, sprites in ROM and scaling them, and creating a simple color LUT.

The screen buffer article is informative : https://projectf.io/posts/framebuffers/ . Especially how to scale up 160×120 framebuffer up to 640×480. Also this one on the three different strategies for making FPGA animations : https://projectf.io/posts/animated-shapes/

This looks like a good FPGA tutorial resource I wasn’t aware of:

  • http://asic-world.com/verilog/index.html
  • https://projectf.io/tutorials/
  • http://fpgacpu.ca/fpga/index.html
  • https://zipcpu.com/tutorial/
  • http://www.doe.carleton.ca/~gallan/478/pdfs/PeterVrlR.pdf
  • https://fpgaer.tech/?p=191

To decode HDMI found this : https://warmcat.com/hardware%20design/hdmi/fpga/2015/10/22/hdmi-capture-and-analysis-fpga-project-3.html

**********

I think this project has been a victim of a certain amount of magical thinking. The machine will only do what it can do and what it is asked to do. Yes, it’s possible to glitch the machine but ultimately there are no surprises unless you don’t fully understand what you’re asking the machine to do. It’s all deterministic. Learning the history of graphics is super cool but you are just reproducing old techniques, there isn’t going to be some hidden type of graphic expression that was missed by everyone that you are going to unlock somehow. It’s just colors on a screen in the end…

It’s ultimately an activity I enjoy, and it is best to share things you enjoy with other people. I should connect with other people making things !

*****

Here’s the final pcf :

// CLOCK

set_io clk_in 49

// HDMI OUT

set_io hdmi_p[0] 139 -io_std SB_LVCMOS
set_io hdmi_p[2] 78 -io_std SB_LVCMOS
set_io hdmi_p[1] 80 -io_std SB_LVCMOS
set_io hdmi_p[3] 137 -io_std SB_LVCMOS

set_io hdmi_n[0] 138 -io_std SB_LVCMOS
set_io hdmi_n[2] 79 -io_std SB_LVCMOS
set_io hdmi_n[1] 81 -io_std SB_LVCMOS
set_io hdmi_n[3] 136 -io_std SB_LVCMOS

// VGA OUT

set_io v_sync 97
set_io h_sync 76

set_io r_out[0] 91
set_io r_out[1] 95
set_io r_out[2] 96

set_io g_out[0] 75
set_io g_out[1] 74
set_io g_out[2] 73

set_io b_out[0] 87
set_io b_out[1] 88
set_io b_out[2] 90

// SOUND OR VGA ANALOG IN

set_io analog_in 56

// POTENTIOMETER IN

set_io pot_in 52

// KEYS IN

set_io key[0] 37
set_io key[1] 38
set_io key[2] 39
set_io key[3] 41
set_io key[4] 42
set_io key[5] 43
set_io key[6] 44
set_io key[7] 45
set_io key[8] 47

// LEDS OUT

set_io green_led 23
set_io red_led 24

// VGA IN

set_io rpi_hsync 34
set_io rpi_vsync 31

 

I am working on a demo code that brings everything together : sound in, pot in, button reactivity, random, sine, time, VGA (and maybe HDMI?). Here is what it looks like so far :

module vga_sync_test(
input wire clk_in,
input wire [8:0] key,
input wire rpi_vsync,
input wire rpi_hsync,
input wire analog_in,
inout pot_in,
output [3:0] hdmi_p,
output [3:0] hdmi_n,
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,
output wire h_sync,
output wire v_sync,
output wire red_led,
output wire green_led
);

wire display_en;
//reg [9:0] h_count;
wire [11:0] h_count;
//reg [9:0] v_count;
wire [11:0] v_count;

localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;


reg [4:0] addr;
reg [7:0] data;
reg [7:0] data_out;
always@(addr) begin
case (addr)

0 : data = 0;
1 : data = 16;
2 : data = 31;
3 : data = 45;
4 : data = 58;
5 : data = 67;
6 : data = 74;
7 : data = 77;
8 : data = 77;
9 : data = 74;
10 : data = 67;
11 : data = 58;
12 : data = 45;
13 : data = 31;
14 : data = 16;
15 : data = 0;
16 : data = -16;
17 : data = -31;
18 : data = -45;
19 : data = -58;
20 : data = -67;
21 : data = -74;
22 : data = -77;
23 : data = -77;
24 : data = -74;
25 : data = -67;
26 : data = -58;
27 : data = -45;
28 : data = -31;
29 : data = -16;
default : data = 0;
endcase
end

reg[22:0] div_cntr1;
reg[22:0] div_cntr2;
reg half_sec_pulse;

always@(posedge clk_in)
begin
div_cntr1 <= div_cntr1 + 1;
if (div_cntr1 == 0)
if (div_cntr2 == 0)
begin
div_cntr2 <= 0;
half_sec_pulse <= 1;
end
else
div_cntr2 <= div_cntr2 + 1;
else
half_sec_pulse <= 0;

end


always @(posedge half_sec_pulse) begin

data_out <= data;
addr <= addr + 1;
if(addr == 29)
addr <= 0;

end

// FOR AUDIO IN INTERACTION
assign red_led = analog_in;
assign green_led = analog_in;

// FOR KEY INTERACTION key[8:0]

// FOR POT INTERACTION

reg [15:0] adc_count= 0; //16 bits to count how long it's taking to charge. 32,768 is the max value. 1 extra MSB bit for just discharging the cap.
reg [14:0] pot_value= 0; //to store cap charge time value
reg [14:0] final_pot_value= 0;

wire adc_in;
reg adc_sample = 1; //first time through we want to sample the cap value

wire adc;


assign pot_in = (adc_sample) ? 1'bZ : 1'b0;
assign adc_in = pot_in;

// for a 25MHz clock we divide 100MHz by 4
reg [1:0] clk_div = 0;

always @(posedge clk_in)
begin
clk_div <= clk_div + 1;
end


always @(posedge clk_div[1]) begin //25MHz clock

adc_count <= adc_count + 1; // start counting

if(adc_count>=16'b1000000000000000 )begin // once we have rolled over 15 bit counter...

adc_sample <= 1'b0; // ...discharge cap for a while to restart the process.

end

else begin

adc_sample<=1'b1; //put cap pin in high 'Z'

if(adc_in==1'b1) begin // if cap charged...

pot_value[14:0]<=adc_count[14:0]; //store this value in pot


end

end

end


// FOR SOUND INTERACTION

// FOR AUDIO IN INTERACTION

//Check if we can create RGB colors
always @(posedge clk_in) begin



if (display_en) begin
if (h_count < h_pixel_half + data_out
&& v_count < v_pixel_half - data_out) begin
//Assign here your test color



//pot changing colors
case(pot_value[14:13])

2'b00: begin //yellow
r_out <= 3'b010;
g_out <= 3'b110;
b_out <= 3'b011;
end
2'b01: begin //cyan
r_out <= 3'b011;
g_out <= 3'b100;
b_out <= 3'b001;
end
2'b10: begin //magenta
r_out <= 3'b110;
g_out <= 3'b110;
b_out <= 3'b011;
end
2'b11: begin //blue
r_out <= 3'b010;
g_out <= 3'b111;
b_out <= 3'b111;
end

default: begin // white
r_out <= 3'b010;
g_out <= 3'b010;
b_out <= 3'b011;
end
endcase




end else if (h_count > h_pixel_half - data_out
&& v_count < v_pixel_half + data_out) begin

if (analog_in == 1) begin
//Assign here your test color
r_out <= 3'b010;
g_out <= 3'b110;
b_out <= 3'b011;
end
else begin
r_out <= 3'b001;
g_out <= 3'b010;
b_out <= 3'b100;
end
end else if (h_count < h_pixel_half - data_out
&& v_count > v_pixel_half - data_out) begin
//Assign here your test color
r_out <= key[2:0];
g_out <= key[5:3];
b_out <= key[8:6];
end else begin
//Assign here your test color
r_out <= 3'b010;
g_out <= 3'b010;
b_out <= 3'b010;
end
end else begin
r_out <= 3'b000;
g_out <= 3'b000;
b_out <= 3'b000;
end
end

vga_sync vga_s(
.clk_in(clk_in), //12MHz clock input
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);

endmodule

**************

What is next ?

I am tasked with making an installation using a camera and the FPGA to demonstrate its ability to modify an incoming video stream. I’m going with this simple OV7670 Camera. I would also like to have something to mail to my new video and audio synthesis contacts in Lyon.

I have displayed an image saved in ROM, but I would also like to sample a 120×160 pixel image into BRAM from an incoming video stream. (If this is not possible, I’lll use the SRAM on the Cyber Campus board.)

I’m planning on starting this process with an RPI set to output 60Hz 640 x 480 (https://pimylifeup.com/raspberry-pi-screen-resolution/) and use an HDMI to VGA converter to send this along to the FPGA.

I can access all 16 x 4k BRAMS now :

module ram (din, write_en, waddr, wclk, raddr, rclk, dout);// 32768‬ x 2

parameter addr_width = 15; // to access all 32768‬ bits of the address
parameter data_width = 2;
input [addr_width-1:0] waddr, raddr;
input [data_width-1:0] din;
input write_en, wclk, rclk;
output reg [data_width-1:0] dout;
reg [data_width-1:0] mem [(1<<addr_width)-1:0]
;

always @(posedge wclk) // Write memory.
begin
if (write_en)
begin
mem[waddr] <= din; // Using write address bus.
end
end
always @(posedge rclk) // Read memory.
begin
dout <= mem[raddr]; // Using read address bus.
end
endmodule

But so far once I start using it, it disappears ! Moving back to SRAM.

It’s working really nicely taking the 160×120 ROM image and using it as a screen buffer.


addr <= (((hc-hbp)>>2)+(((vc-vbp)>>2)*160));

Here’s the input from this pattern :

assign data_in[7:0] = (again_xnor[7:0] > key[7:0]) ? 8'b11111111 : 8'b00000000;

And here is the recording which stays nice and still !

The horizontal lines appeared when I moved away from

this :

if ((rec==0) && (a>=8'b10000000))

to this :
if ((rec==0) && (a==8'b00000000))

 

I am trying to record incoming VGA color but then I remembered the HDMI clock is 25MHz and the VGA is 50 MHz (?). I can solder the VGA connector to the SRAM version of the board and switch to VGA, or I could modify the vga.sync of the input VGA board to run at 25MHz with standard 640×480 using a PLL.

I could also now try some framebuffer animations !

I can now output standard 25MHz 640×480 VGA. I changed vga_sync to have 640×480 timings by selecting these parameters :

localparam h_pixel_total = 800;
localparam h_pixel_display = 640;
localparam h_pixel_front_porch_amount = 16;
localparam h_pixel_sync_amount = 96;
localparam h_pixel_back_porch_amount = 48;

localparam v_pixel_total = 525;
localparam v_pixel_display = 480;
localparam v_pixel_front_porch_amount = 10;
localparam v_pixel_sync_amount = 2;
localparam v_pixel_back_porch_amount = 33;

And then changing the values in the verilog file making graphics :

localparam h_pixel_max = 640;
localparam v_pixel_max = 480;
localparam h_pixel_half = 320;
localparam v_pixel_half = 240;

...


// for a 25MHz clock we divide the 100MHz clk_in by 4
reg [1:0] clk_div = 0;

always @(posedge clk_in)
begin
clk_div <= clk_div + 1;
end
...

vga_sync vga_s(
.clk_in(clk_div[1]), // giving vga_sync the 25MHz clock 
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);

endmodule

*****

I can now record relatively stable VGA, it still appears to drift a little though (doesn’t help to use test if HSYNC high). Will need to try with rpi in 640×480 mode. From now on I want to use standard timings for all the rpi video out, the VGA and HDMI timings for the FPGAs so that transitioning is as painless as possible. I’ll try with the rpi in 25MHz mode later today.

I want to try changing the size of the screen buffer, and eventually recording video by moving the buffer deeper into SRAM memory over time. Success ! I just divided the horizontal and vertical counters by 2 instead of 4 and increased the horizontal line width from 160 bits to 320.

addr <= (((hc-hbp)>>1)+(((vc-vbp)>>1)*320));

320 x 240 (76,800 pixels) screen buffer !! I have a total of 256,000 I could use.

 

Much finer tune recording possible (despite the added noise)

I am trying to record 3 frames but it’s making extra glitchy things happen (most important stuff bold) :

...
else
begin
hc <= 0;
if (vc < vlines) begin
vc <= vc + 1;
screen_count <= screen_count + 1;
end
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

always @(posedge latch_high_speed[2]) // display 100% saturation colourbars
begin

case(screen_count[9])

1'b0 : multiplier = 1;
1'b1 : multiplier = 2;

default : multiplier = 1;

endcase

// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------
if(hc >= hbp && hc < hfp) begin

//pixel <= heart[(((hc-hbp)>>2)+(((vc-vbp)>>2)*160))];
addr <= (((hc-hbp)>>1)+(((vc-vbp)>>1)*320*multiplier));

...

In theory though this would be useful for double buffering.

I am trying to record in three different parts of the SRAM based on the key pressed.


...

case(key[7:6])
2'b00: multiplier = 1;
2'b01: multiplier = 2;
2'b10: multiplier = 3;
2'b11: multiplier = 1;

default: multiplier = 1;
endcase

if (vc >= vbp && vc < vfp)
begin

if(hc >= hbp && hc < hfp) begin

addr <= (((hc-hbp)>>1)+(((vc-vbp)>>1)*320*multiplier));

...

They seem to be partially overwriting one another however :

SOLVED : Needed to add an offset which was higher than the highest memory location that could be reached in the first screen buffer (78,000). I have a total of 256,000 so I could save a few more images.

...

case(key[7])
2'b0: offset = 0;
2'b1: offset = 78000;
default: offset = 0;
endcase


if (vc >= vbp && vc < vfp)
begin

if(hc >= hbp && hc < hfp) begin

addr <= (((hc-hbp)>>1)+(((vc-vbp)>>1)*320)+ offset);

...

Going to try an animation but using the smaller 160×120 sized buffer.

SUCCESS ! I incremented screen_count every 600 frames :

if (vc < vlines) begin
vc <= vc + 1;
frame <= frame + 1;
if(screen_count>12)begin
screen_count <= 0;
end
if (frame > 6000) begin
screen_count <= screen_count + 1;
frame <= 0;
end
end

...

addr <= (((hc-hbp)>>2)+(((vc-vbp)>>2)*160)+(offset*screen_count));

*****

The three ways I could start doing cool stuff with this screen buffer (from https://projectf.io/posts/animated-shapes/):

  1. Use hardware sprites – suitable for simple 2D graphics
  2. Use a blitter to cut out and move a framebuffer region – effective for small 2D objects
  3. Clear the framebuffer and draw from scratch – versatile but requires plenty of bandwidth

Animations might start to reveal the limitations of the (non dual ported) SRAM. The website suggests either using the blanking space to update SRAM or double buffering with a finite state machine to switch and clear them. I could use the FPGA internal memory for ROM storage of colour look up tables, sprites and textures?

I want to try overlaying text from ROM onto the screen, to have a splash screen when the FPGA powers up.

****

To solve the setting of the top_module, I think I have a solution (untested so far):

  • In iCECube 2, right click on Run Synplify Pro Synthesis and select options
  • Under tool options, click on To set Synplify Pro option, click here
  • A new Synplify Pro window opens up
  • Select the Design Hierarchy tab to set the top module

  • Now click RUN and close the Synplify Pro window afterwards
  • iCECube2 Should now have green ticks next to Run Synplify Pro Synthesis and Select Implementation
  • Finish the process by executing the remaining steps not yet checked in green

Some learningz reading a french magazine for beginner engineers :

To find out how many bits are required to store a max number, you take the log base 2 of a number, and round up to the nearest integer.

ceiling of log₂(12000000) = 23.51, we round up to get 24 bits needed to store the 12,000,000 decimal number.

If we want to know what the final frequency of an LED blinking will be with a given counter and clock speed :

(2^24 -1) / 12MHz = 1.4Hz = blink frequency (if toggling every roll over of a 24 bit register with a 12MHz clock)

Also learned about linting, the process of testing a code for suspicious constructs that might have been missed by a compiler : https://projectf.io/posts/verilog-lint-with-verilator/

 

****

Check out the Poietic Generator :

Recording the first real-time group interaction without any center (1996)

Also Looking at different raster memory editors for games in the 1980s :

undefined

Interface, Multicast version (1994)

https://segaretro.org/images/e/e2/Tlp.png

File:TIDE Tile Map Editor Thumb 01.png

File:Warzone 2100 - EditWorld - 1.jpg

There is also this project Viewpoint that appears to allow the user to edit sprites in real time : https://www.youtube.com/watch?app=desktop&v=9G0r7jL3xl8

Some other cool links from a Hacker News deep dive :

https://bost.ocks.org/mike/algorithms/

https://pixel.essenmitsosse.de/?showcase=true&slide=4

https://chrisbuilds.github.io/terminaltexteffects/showroom/

http://web.archive.org/web/20240307215347/http://www.buthowdoitknow.com/but_how_do_it_know_cpu_model.html

Below the root game :

Below the Root | The Obscuritory

https://obsolescence.wixsite.com/obsolescence/pidp-11

https://madebyevan.com/fsm/

On the SNES graphics systems :

https://fabiensanglard.net/snes_ppus_how/index.html

https://fabiensanglard.net/snes_ppus_why/

Coding a reverb :

https://signalsmith-audio.co.uk/writing/2021/lets-write-a-reverb/

verilog

 

After failing to do various “simple” things in verilog, I’m doing a little refresher course. Resources :

  • https://hackaday.com/series_of_posts/williams-icestick-tutorial/
  • https://www.doulos.com/knowhow/verilog_designers_guide/
  • https://vol.verilog.com/
//VERILOG CODING PRACTICES

//verilog is case sensitive !

reg THIS; // not the same as this !
reg this;

//to repeat : {repeat_count{}}

vec[1:20] = {4{5'b10110}};

**********

// this is better than just [23:0] for readability :
parameter buswidth = 24;
input [buswidth-1:0] bus;

//this is better than just 47:0 for readability :
reg [8*6:1] string6;

***********

//scalars (single bit vs vectors which are multi-bit) can have the following values:

0 - logic zero (false)
1 - logic one (true)
x - unknown value
z - high-impedance value

// therefore an equality test can return unknown if one of the operands is x or z !

if (expression) is evaluated :

0 false
1 true
x false
z false

//nets receive signal values
//net or register port expressions send values

//regs need initial conditions, wires need NOT to have initial conditions assigned.

//keep in mind if you're making combinational or sequential logic !

*******

//legal assignments :

lhs = rhs;
lhs[bit] = rhs;
lhs[partmsb:partlsb] = rhs;
{lhs1, lhs2} = rhs;
***********
//for a null operation use the semi-colon :

if (control)
;
else
x = f(y);

*******
//You can give names to binary combinations :

`define A 3'b001
`define B 3'b010
`define C 3'b100

initial state = `A;

********
//You can deassign :

else if (!preset)
assign q = 1;
else
deassign q;

*********

// in simulation you can use the $random system function

integer rand;
rand = $random;

*************
//don't do this :
assign n1 = w1 + f(w2) + g(w3);

//do this instead :
assign t2 = f(w2);
assign t3 = g(w3);
assign n1 = w1 + t2 + t3;

************
//this :
wire AB;
assign AB = A & B;

// can be written like this :
wire AB = A & B;

**************

//This creates an implicit sensitivity list that contains all the signals whose values are 
//read in the statements of the always block :

always @(*)

//Remember : To synthesize combinational logic using an always block, 
//ALL inputs to the design must appear in the sensitivity list.

********

// LATCHES 
// https://nandland.com/how-to-avoid-creating-a-latch/
//Latches are bad, they are combinational and not driven by a clock. If you use a clocked process 
//you avoid the possibility of unintentionally making a latch.

//All combinational processes need to explicit assignments for every possibility. Don't leave a conditional test
//that has only an if and no else to account for the other possible values.
// TEST BENCHES

//for test benches use # to make things happen at certain times :

x = 0;
#10 x = x + 1;
#20 x = x + 2;
#30 x = x + 3;
#20 x = x + 2;
#10 x = x + 1;

time:       value of x :
0                0
10               1
30               3
60               6
80               8
90               9

// for test bench clocks :
always
begin
#period/2 clk = ~clk;
end
// CLOCK BUFFERING 

Do external clocks need buffering ? If so, is this how to buffer an external clock ?

SB_GB_IO gb_io1 (
.PACKAGE_PIN( clk ),
.GLOBAL_BUFFER_OUTPUT( gclk )
);

...or with extra params ?

SB_GB_IO gb_io1
( .PACKAGE_PIN(clk),
.OUTPUT_ENABLE(1'b1),
.GLOBAL_BUFFER_OUTPUT(gclk)
);
defparam gb_io1.PIN_TYPE = 6'b000001;
defparam gb_io1.PULLUP = 1'b0;
defparam gb_io1.NEG_TRIGGER = 1'b0;
defparam gb_io1.IO_STANDARD = "SB_LVCMOS";
// MEMORY

// to instantiate 1 bit memory :
reg membits [1023:0]; // 1024 1-bit registers

//You can't take a bit-select or part-select of a memory element.
//Thus, if you want to get the 3rd bit out of the 10th element of a memory, 
//you need to do it in two steps:

reg [0:31] temp, mem[1:1024];
...
temp = mem[10];
bit = temp[3];
// RACE CONDITIONS 
// This code produces a 'race condition' :

always @(posedge clock) always @(posedge clock)
dff1 = f(x); dff2 = dff1;

// it can be fixed with non-blocking assignments :

always @(posedge clock) always @(posedge clock)
dff1 <= f(x); dff2 <= dff1;

//CROSSING CLOCK DOMAINS 

one solution is to double latch everything.

**********

I am systematically trying to figure out why I can’t take rpi input and redisplay it with the simple HDMI code.

I am starting by sending signals in a pass through way like this :

always @(posedge rpi_pixel_clock) begin

b_in_repeat <= b_in;

end

I get the same input signal but one clock later on the ‘scope so as expected.

Also tried to move between clock domains with double D-flops, it works and just delays the input signal a bit more :

`default_nettype none // disable implicit definitions by Verilog

module top(
input wire clk100, // replace with pixel clock of the rpi ? Slow to speed of rpi ?

input wire rpi_pixel_clock,

input wire b_in,
output reg b_in_repeat

);


reg r1_data = 0;
reg r2_data = 0;

always @(posedge rpi_pixel_clock) begin


r1_data <= b_in;
r2_data <= r1_data;

end

always @(posedge clk100) begin


b_in_repeat <= r2_data;

end

endmodule

 

Here’s a new code that at least kind of displays incoming data from the rpi :

`default_nettype none // disable implicit definitions by Verilog
//-----------------------------------------------------------------
// minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
//
// Author: Mike Field <hamster@snap.net.nz>
//
// DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
// value is mapped to one or two 10-bit symbols, depending on how
// many 1s or 0s have been sent. This makes it a DC balanced protocol,
// as a correctly implemented stream will have (almost) an equal
// number of 1s and 0s.
//
// Because of this implementation quite complex. By restricting the
// symbols to a subset of eight symbols, all of which having have
// five ones (and therefore five zeros) this complexity drops away
// leaving a simple implementation. Combined with a DDR register to
// send the symbols the complexity is kept very low.
//-----------------------------------------------------------------

module top(

clk100, hdmi_p, hdmi_n, rpi_pixel_clock, b_in, rpi_DEN

);

input clk100;
output [3:0] hdmi_p;
output [3:0] hdmi_n;
input wire rpi_pixel_clock;
input wire b_in;
input wire rpi_DEN;

// For holding the outward bound TMDS symbols in the slow and fast domain
reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
reg [9:0] clk_high_speed;

reg [1:0] c2_output_bits;
reg [1:0] c1_output_bits;
reg [1:0] c0_output_bits;
reg [1:0] clk_output_bits;

wire clk_x5;
reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain

wire vsync, hsync;
wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
assign syncs = {vsync, hsync};

// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 525; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch (96 + 48)
parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
parameter vbp = 35; // end of vertical back porch (2 + 33)
parameter vfp = 515; // beginning of vertical front porch (525 - 10)

// registers for storing the horizontal & vertical counters
reg [9:0] vc;
reg [9:0] hc;
// generate sync pulses (active high)
assign vsync = (vc < vpulse);
assign hsync = (hc < hpulse);

reg r1_data = 0;
reg r2_data = 0;

reg r3_data = 0;
reg r4_data = 0;

always @(posedge rpi_pixel_clock) begin

r1_data <= b_in;
r2_data <= r1_data;

r3_data <= rpi_DEN;
r4_data <= r3_data;

end


always @(posedge clk_x5) begin
//-------------------------------------------------------------
// Now take the 10-bit words and take it into the high-speed
// clock domain once every five cycles.
//
// Then send out two bits every clock cycle using DDR output
// registers.
//-------------------------------------------------------------
c0_output_bits <= c0_high_speed[1:0];
c1_output_bits <= c1_high_speed[1:0];
c2_output_bits <= c2_high_speed[1:0];
clk_output_bits <= clk_high_speed[1:0];
if (latch_high_speed[2]) begin // pixel clock 25MHz
c0_high_speed <= c0_symbol;
c1_high_speed <= c1_symbol;
c2_high_speed <= c2_symbol;
clk_high_speed <= 10'b0000011111;
latch_high_speed <= 3'b000;
if (hc < hpixels)
hc <= hc + 1;
else
begin
hc <= 0;
if (vc < vlines)
vc <= vc + 1;
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

always @(*) // display 100% saturation colourbars
begin
// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------
// display white bar
if (hc >= hbp && hc < hfp && r2_data == 1'b1 && r4_data == 1'b1 ) //
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display black bar
else if (hc >= hbp && hc < hfp && r2_data == 1'b0 && r4_data == 1'b1)
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b0111110000; // blue
end
// display red bar
else if (hc >= hbp && hc < hfp && r2_data != 1'b0 && r2_data != 1'b1 && r4_data == 1'b1)// if r2_data == x or r2_data == z
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b0111110000; // blue
end
// we're outside active horizontal range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end
// we're outside active vertical range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end

// red N
defparam hdmin2.PIN_TYPE = 6'b010000;
defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin2 (
.PACKAGE_PIN (hdmi_n[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c2_output_bits[1]),
.D_OUT_1 (~c2_output_bits[0])
);

// red P
defparam hdmip2.PIN_TYPE = 6'b010000;
defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip2 (
.PACKAGE_PIN (hdmi_p[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c2_output_bits[1]),
.D_OUT_1 (c2_output_bits[0])
);

// green N
defparam hdmin1.PIN_TYPE = 6'b010000;
defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin1 (
.PACKAGE_PIN (hdmi_n[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c1_output_bits[1]),
.D_OUT_1 (~c1_output_bits[0])
);

// green P
defparam hdmip1.PIN_TYPE = 6'b010000;
defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip1 (
.PACKAGE_PIN (hdmi_p[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c1_output_bits[1]),
.D_OUT_1 (c1_output_bits[0])
);


// blue N
defparam hdmin0.PIN_TYPE = 6'b010000;
defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin0 (
.PACKAGE_PIN (hdmi_n[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c0_output_bits[1]),
.D_OUT_1 (~c0_output_bits[0])
);

// blue P
defparam hdmip0.PIN_TYPE = 6'b010000;
defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip0 (
.PACKAGE_PIN (hdmi_p[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c0_output_bits[1]),
.D_OUT_1 (c0_output_bits[0])
);

// clock N
defparam hdmin3.PIN_TYPE = 6'b010000;
defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin3 (
.PACKAGE_PIN (hdmi_n[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~clk_output_bits[1]),
.D_OUT_1 (~clk_output_bits[0])
);


// clock P
defparam hdmip3.PIN_TYPE = 6'b010000;
defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip3 (
.PACKAGE_PIN (hdmi_p[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (clk_output_bits[1]),
.D_OUT_1 (clk_output_bits[0])
);
// D_OUT_0 and D_OUT_1 swapped?
// https://github.com/YosysHQ/yosys/issues/330


SB_PLL40_PAD #(
.FEEDBACK_PATH ("SIMPLE"),
.DIVR (4'b0000),
.DIVF (7'b0001001),
.DIVQ (3'b011),
.FILTER_RANGE (3'b101)
) uut (
.RESETB (1'b1),
.BYPASS (1'b0),
.PACKAGEPIN (clk100),
.PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
);

endmodule

 

I am trying taking every single rpi input signal, buffering it, and then replacing the sync counters with these inputs :

I am guessing that it is successfully sending the video information but that 720×720 is not an acceptable HDMI format ?

`default_nettype none // disable implicit definitions by Verilog
//-----------------------------------------------------------------
// minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
//
// Author: Mike Field <hamster@snap.net.nz>
//
// DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
// value is mapped to one or two 10-bit symbols, depending on how
// many 1s or 0s have been sent. This makes it a DC balanced protocol,
// as a correctly implemented stream will have (almost) an equal
// number of 1s and 0s.
//
// Because of this implementation quite complex. By restricting the
// symbols to a subset of eight symbols, all of which having have
// five ones (and therefore five zeros) this complexity drops away
// leaving a simple implementation. Combined with a DDR register to
// send the symbols the complexity is kept very low.
//-----------------------------------------------------------------

module top(

clk100, hdmi_p, hdmi_n, rpi_pixel_clock, b_in, rpi_DEN, rpi_vsync

);

input clk100;
output [3:0] hdmi_p;
output [3:0] hdmi_n;
input wire rpi_pixel_clock;
input wire b_in;
input wire rpi_DEN;
input wire rpi_vsync;

/*

pcf :

set_io rpi_pixel_clock 61
set_io b_in 62
set_io rpi_DEN 52

set_io rpi_vsync 63

*/

// For holding the outward bound TMDS symbols in the slow and fast domain
reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
reg [9:0] clk_high_speed;

reg [1:0] c2_output_bits;
reg [1:0] c1_output_bits;
reg [1:0] c0_output_bits;
reg [1:0] clk_output_bits;

wire clk_x5;
reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain

wire vsync, hsync;
wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
assign syncs = {vsync, hsync};

// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 525; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch (96 + 48)
parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
parameter vbp = 35; // end of vertical back porch (2 + 33)
parameter vfp = 515; // beginning of vertical front porch (525 - 10)

// registers for storing the horizontal & vertical counters
reg [9:0] vc;
reg [9:0] hc;
// generate sync pulses (active high)
assign vsync = (vc < vpulse);
assign hsync = (hc < hpulse);

reg r1_data = 0;
reg r2_data = 0;

reg r3_data = 0;
reg r4_data = 0;

reg r7_data = 0;
reg r8_data = 0;

always @(posedge rpi_pixel_clock) begin // shouod be clk_x5 ?

r1_data <= b_in;
r2_data <= r1_data;

r3_data <= rpi_DEN;
r4_data <= r3_data;

r7_data <= rpi_vsync;
r8_data <= r7_data;

end


always @(posedge clk_x5) begin
//-------------------------------------------------------------
// Now take the 10-bit words and take it into the high-speed
// clock domain once every five cycles.
//
// Then send out two bits every clock cycle using DDR output
// registers.
//-------------------------------------------------------------
c0_output_bits <= c0_high_speed[1:0];
c1_output_bits <= c1_high_speed[1:0];
c2_output_bits <= c2_high_speed[1:0];
clk_output_bits <= clk_high_speed[1:0];
if (latch_high_speed[2]) begin // pixel clock 25MHz
c0_high_speed <= c0_symbol;
c1_high_speed <= c1_symbol;
c2_high_speed <= c2_symbol;
clk_high_speed <= 10'b0000011111;
latch_high_speed <= 3'b000;
if (hc < hpixels)
hc <= hc + 1;
else
begin
hc <= 0;
if (vc < vlines)
vc <= vc + 1;
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

always @(*) // display 100% saturation colourbars
begin
// first check if we're within vertical active video range
if (r8_data)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------
// display white bar
if (r2_data == 1'b1 && r4_data == 1'b1 ) //
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display black bar
else if (r2_data == 1'b0 && r4_data == 1'b1)
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b0111110000; // blue
end
// we're outside active horizontal range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end
// we're outside active vertical range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end

// red N
defparam hdmin2.PIN_TYPE = 6'b010000;
defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin2 (
.PACKAGE_PIN (hdmi_n[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c2_output_bits[1]),
.D_OUT_1 (~c2_output_bits[0])
);

// red P
defparam hdmip2.PIN_TYPE = 6'b010000;
defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip2 (
.PACKAGE_PIN (hdmi_p[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c2_output_bits[1]),
.D_OUT_1 (c2_output_bits[0])
);

// green N
defparam hdmin1.PIN_TYPE = 6'b010000;
defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin1 (
.PACKAGE_PIN (hdmi_n[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c1_output_bits[1]),
.D_OUT_1 (~c1_output_bits[0])
);

// green P
defparam hdmip1.PIN_TYPE = 6'b010000;
defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip1 (
.PACKAGE_PIN (hdmi_p[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c1_output_bits[1]),
.D_OUT_1 (c1_output_bits[0])
);


// blue N
defparam hdmin0.PIN_TYPE = 6'b010000;
defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin0 (
.PACKAGE_PIN (hdmi_n[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c0_output_bits[1]),
.D_OUT_1 (~c0_output_bits[0])
);

// blue P
defparam hdmip0.PIN_TYPE = 6'b010000;
defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip0 (
.PACKAGE_PIN (hdmi_p[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c0_output_bits[1]),
.D_OUT_1 (c0_output_bits[0])
);

// clock N
defparam hdmin3.PIN_TYPE = 6'b010000;
defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin3 (
.PACKAGE_PIN (hdmi_n[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~clk_output_bits[1]),
.D_OUT_1 (~clk_output_bits[0])
);


// clock P
defparam hdmip3.PIN_TYPE = 6'b010000;
defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip3 (
.PACKAGE_PIN (hdmi_p[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (clk_output_bits[1]),
.D_OUT_1 (clk_output_bits[0])
);
// D_OUT_0 and D_OUT_1 swapped?
// https://github.com/YosysHQ/yosys/issues/330


SB_PLL40_PAD #(
.FEEDBACK_PATH ("SIMPLE"),
.DIVR (4'b0000),
.DIVF (7'b0001001),
.DIVQ (3'b011),
.FILTER_RANGE (3'b101)
) uut (
.RESETB (1'b1),
.BYPASS (1'b0),
.PACKAGEPIN (clk100),
.PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
);

endmodule

 

I tried to use the target clock to clock the double d-flops but this didn’t improve things. I spent too much time trying different rapsberry pi overlays and dpi display parameters.

I’ve decided the next thing to do is display a static image an image from a memory array. After this I could sample an incoming signal to replace the data in this array and then display it every time interval.

Could I use initial to fill the BRAM ? Illegal to re-initialize BRAM in an always block ? Can only be done once at the start.

initial begin
	for(i = 0; i < 8096; i++)
		mem1[i] <= 8'd0; // unclear if = or <= needs to be used here
end

// OR this ? 

initial begin
    mem[4'h0] = 8'h00;
    // ...
    mem[4'hF] = 8'h00;
end

You can describe a large amount of data using hex like this :

256'h0000000000000000000000000000000000000000000000000000000000000000;

/OR this ?

@000000e0 2c 20 61 20 6e 65 77 20 6e 61 74 ...
@000000f0 63 6f 6e 63 65 69 76 65 64 20 69 ...

Could I use readmemh to load data into the RAM ? It appears to be very picky about file types (https://stackoverflow.com/questions/628603/what-is-the-function-of-readmemh-and-writememh-in-verilog)…

$readmem[hb]("File", ArrayName, StartAddr, EndAddr)

Memory length (MEMLN) must be power of 2 minus one (if starting from zero) :

reg [ W - 1 : 0 ] ram [ 0 : MEMLN - 1 ] ;

Memory access needs to happen in its own always block (https://zipcpu.com/tutorial/lsn-08-memory.pdf) and shouldn’t have any other combinational logic in with it:

always @ ( posedge i_clk )
          if ( write )
             ram [ write_addr ] <= write_value ;
always @ ( posedge i_clk )
          if ( read )
              read_value <= ram [ read_addr ] ;

I am having all kinds of issues infering BRAM, whether as a module or integrated into the top level. Hard to know what I’m doing wrong. I used the verilog from the Lattice Memory Manual. I switched to just making a big array :

reg [19:0] multi_line = {131072{8'b10101111}};

...

multi_line[hc*vc];

...

if (hc >= hbp && hc < hfp && pixel == 1'b1)

...

What would be cool would be to be able to load a bitmap image somehow. I’m scared to work with a two dimensional array because it seems not supported in all versions of verilog. I’m trying to scale a smaller bitmap up by dividing the hc and vc counters.

reg pixel = 1'b0;

reg [7:0] heart = {

{1111111111111111},
{1111111111111111},
{1111111111111111},
{1111101110111111},
{1111000100011111},
{1110111011101111},
{1110111111101111},
{1110111111101111},
{1110111111101111},
{1111011111011111},
{1111101110111111},
{1111110001111111},
{1111111011111111},
{1111111111111111},
{1111111111111111},
{1111111111111111}

};

...

pixel<=heart[(hc>>6)*(vc>>6)];

...

// display green
if (hc >= hbp && hc < hfp && pixel == 1'b1)

Almost ! I don’t think the bit shifting or bit selecting – like this [(hc[9:6])*(vc[9:6])]; -is working. I’m trying to divide hc and vc into 16 segments but not finding the right way. Verilog may not support modulo with non powers of 2.

I’ve tried all kinds of different work arounds without any success. It looks like the screen is divided into a 16×16 grid, but within each box there are stripes instead of solid colors…

I’ve changed the size of the image to 12 by 8 so that binary divisions of the horizontal and vertical counters could be used directly because, as I’ve learned recently, verilog thinks in binary. I’ve messed with the padding at the top and bottom of the 256 bit array. I’ve tried countless different syntax for various options of bit selecting and concatenating.

I suppose I could put a massive 640×480 bitmap so that no scaling is necessary, or possibly copy my smaller bitmap into a larger one in an initial block ? It would be so much easier if the size of the screen were the max value of a power of 2, or if I could use a 2D array, or if I could use modulo to test for decimal divisions ! I could also have some more test patterns to input in so I could see in which way the test image is being displayed incorrectly and diagnose the problem there ?

It seems that working with external SRAM and a stream of data that is unorganized into start and end points is comparably easier as there is no framing taking place.

******

OK, so I misunderstood how a vector works in verilog. It should have the number of elements in the declaration, NOT the bit address depth as I previously had thought. This lack of understanding of how the matrix was represented made everything a total mess.

reg [69:0] heart =

{
10'b1000100011,
10'b0111011101,
10'b0111111101,
10'b0111111011,
10'b1101110111,
10'b1110001111,
10'b1111011111
};

Here’s the code:

`default_nettype none // disable implicit definitions by Verilog
//-----------------------------------------------------------------
// minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
//
// Author: Mike Field <hamster@snap.net.nz>
//
// DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
// value is mapped to one or two 10-bit symbols, depending on how
// many 1s or 0s have been sent. This makes it a DC balanced protocol,
// as a correctly implemented stream will have (almost) an equal
// number of 1s and 0s.
//
// Because of this implementation quite complex. By restricting the
// symbols to a subset of eight symbols, all of which having have
// five ones (and therefore five zeros) this complexity drops away
// leaving a simple implementation. Combined with a DDR register to
// send the symbols the complexity is kept very low.
//-----------------------------------------------------------------

module top(
clk100, hdmi_p, hdmi_n
);

input clk100;
output [3:0] hdmi_p;
output [3:0] hdmi_n;

reg [69:0] heart =

{
10'b1000100011,
10'b0111011101,
10'b0111111101,
10'b0111111011,
10'b1101110111,
10'b1110001111,
10'b1111011111
};


// For holding the outward bound TMDS symbols in the slow and fast domain
reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
reg [9:0] clk_high_speed;

reg [1:0] c2_output_bits;
reg [1:0] c1_output_bits;
reg [1:0] c0_output_bits;
reg [1:0] clk_output_bits;

wire clk_x5;
reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain

wire vsync, hsync;
wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
assign syncs = {vsync, hsync};

// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 525; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch (96 + 48)
parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
parameter vbp = 35; // end of vertical back porch (2 + 33)
parameter vfp = 515; // beginning of vertical front porch (525 - 10)

parameter active_h_pixels = 640;
parameter active_v_pixels = 480;

// registers for storing the horizontal & vertical counters
reg [9:0] vc;
reg [9:0] hc;
// generate sync pulses (active high)
assign vsync = (vc < vpulse);
assign hsync = (hc < hpulse);

reg [7:0] hcount;
reg [7:0] vcount;
reg [7:0] count;
reg pixel;

always @(posedge clk_x5) begin
//-------------------------------------------------------------
// Now take the 10-bit words and take it into the high-speed
// clock domain once every five cycles.
//
// Then send out two bits every clock cycle using DDR output
// registers.
//-------------------------------------------------------------
c0_output_bits <= c0_high_speed[1:0];
c1_output_bits <= c1_high_speed[1:0];
c2_output_bits <= c2_high_speed[1:0];
clk_output_bits <= clk_high_speed[1:0];
if (latch_high_speed[2]) begin // pixel clock 25MHz
c0_high_speed <= c0_symbol;
c1_high_speed <= c1_symbol;
c2_high_speed <= c2_symbol;
clk_high_speed <= 10'b0000011111;
latch_high_speed <= 3'b000;

//multiply the vertical count by 10 to get to the next "row" of the bitmap
count <=(vcount*10)+hcount;
pixel <= heart[count];

//the visible area is 640 pixels horizontally, divide this into 10
if (((hc-hbp)%64==0) && hc >= hbp && hc <= hfp) begin

if(hcount>9) begin
hcount <= 0;
end

else begin
hcount <= hcount+1;
end

end

//the visible area is 480 pixels vertically, divide this into 7.5
if (((vc-vbp)%64==0) && vc >= vbp && vc <= vfp) begin

if(vcount>6) begin
vcount <= 0;
end

else begin
vcount <= vcount+1;
end

end

if (hc < hpixels)
hc <= hc + 1;
else
begin
hc <= 0;
if (vc < vlines)
vc <= vc + 1;
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

always @(*) // display 100% saturation colourbars
begin
// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------
// display white bar
if (hc >= hbp && hc < hfp && (pixel == 1'b1))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display black bar
else if (hc >= hbp && hc < hfp && (pixel == 1'b0))
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b0111110000; // blue
end
// we're outside active horizontal range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end
// we're outside active vertical range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end

// red N
defparam hdmin2.PIN_TYPE = 6'b010000;
defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin2 (
.PACKAGE_PIN (hdmi_n[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c2_output_bits[1]),
.D_OUT_1 (~c2_output_bits[0])
);

// red P
defparam hdmip2.PIN_TYPE = 6'b010000;
defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip2 (
.PACKAGE_PIN (hdmi_p[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c2_output_bits[1]),
.D_OUT_1 (c2_output_bits[0])
);

// green N
defparam hdmin1.PIN_TYPE = 6'b010000;
defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin1 (
.PACKAGE_PIN (hdmi_n[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c1_output_bits[1]),
.D_OUT_1 (~c1_output_bits[0])
);

// green P
defparam hdmip1.PIN_TYPE = 6'b010000;
defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip1 (
.PACKAGE_PIN (hdmi_p[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c1_output_bits[1]),
.D_OUT_1 (c1_output_bits[0])
);


// blue N
defparam hdmin0.PIN_TYPE = 6'b010000;
defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin0 (
.PACKAGE_PIN (hdmi_n[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c0_output_bits[1]),
.D_OUT_1 (~c0_output_bits[0])
);

// blue P
defparam hdmip0.PIN_TYPE = 6'b010000;
defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip0 (
.PACKAGE_PIN (hdmi_p[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c0_output_bits[1]),
.D_OUT_1 (c0_output_bits[0])
);

// clock N
defparam hdmin3.PIN_TYPE = 6'b010000;
defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin3 (
.PACKAGE_PIN (hdmi_n[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~clk_output_bits[1]),
.D_OUT_1 (~clk_output_bits[0])
);


// clock P
defparam hdmip3.PIN_TYPE = 6'b010000;
defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip3 (
.PACKAGE_PIN (hdmi_p[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (clk_output_bits[1]),
.D_OUT_1 (clk_output_bits[0])
);
// D_OUT_0 and D_OUT_1 swapped?
// https://github.com/YosysHQ/yosys/issues/330


SB_PLL40_PAD #(
.FEEDBACK_PATH ("SIMPLE"),
.DIVR (4'b0000),
.DIVF (7'b0001001),
.DIVQ (3'b011),
.FILTER_RANGE (3'b101)
) uut (
.RESETB (1'b1),
.BYPASS (1'b0),
.PACKAGEPIN (clk100),
.PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
);

endmodule

I have a tool that converts images into 0s and 1s of any square size :

https://www.dcode.fr/binary-image

I am trying to get to the bottom of how to expand the register size to show larger images. Everytime I try I either get bits that don’t correspond to the initialized values, or I get an out of sync image.

I tried to diagram the states of various counters here

In parallel, I’m reading that initializing values in a register in IceCube 2 is problematic. Some posts say that IceCube can only initialize memory as 0. Others say you should use an initial block, or a reset signal which changes the values of the memory.

After reading the IceCube2 user’s guide, I found out you can inspect the BRAM created in your design in the floor planner :

 

You must use non-blocking (<=) assignments to get data in and out of RAM :

You can instantiate a ROM like this with case of if statements :

I’m currently trying to use readmemh with a full source path for the file, I don’t think it is working however.

N.B. Good to know that to reverse the order of the data in the RAM you can change from 255:0 to 0:255.

This appears to be working with the following code :

reg [7:0] ram [0:255];

initial
begin
$readmemh ("../../../../../Users/jm225306/OneDrive - De Vinci/Bureau/JONAH PERSO/# ICE 40 HX8K FPGA BOARD/CYBER CAMPUS DESIGN/4_SRAM+HDMI/ram.ini", ram);
end

....

with the ram.ini file looking like this :

0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C
0A
23
5C

******

Cool project doing face detection in real time with an FPGA using spatial and temporal filters :

http://people.ece.cornell.edu/land/courses/eceprojectsland/STUDENTPROJ/2012to2013/tnn7/tnn7_report_201212141110.pdf

An example of a heat camera developed in part using an FPGA :

https://www.researchgate.net/publication/319118786_A_low_cost_FPGA_based_thermal_imaging_camera_for_fault_detection_in_PV_panels

*******

Reasons to work with hardware vs. software synths :

  • charisma of the physical instrument object, muscle memory of the instrument, and the fun of it !
  • limits good for creativity
  • no pc needed
  • you can make your own custom input (ADC) and output (amp) stages
  • performance and low start-up time

************

I have a simple code to display a 64 bit bitmap :

`default_nettype none // disable implicit definitions by Verilog
//-----------------------------------------------------------------
// minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
//
// Author: Mike Field <hamster@snap.net.nz>
//
// DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
// value is mapped to one or two 10-bit symbols, depending on how
// many 1s or 0s have been sent. This makes it a DC balanced protocol,
// as a correctly implemented stream will have (almost) an equal
// number of 1s and 0s.
//
// Because of this implementation quite complex. By restricting the
// symbols to a subset of eight symbols, all of which having have
// five ones (and therefore five zeros) this complexity drops away
// leaving a simple implementation. Combined with a DDR register to
// send the symbols the complexity is kept very low.
//-----------------------------------------------------------------

module top(
clk100, hdmi_p, hdmi_n
);

input clk100;
output [3:0] hdmi_p;
output [3:0] hdmi_n;


reg [0:63] heart =

{
8'b11111111,
8'b00010001,
8'b01101101,
8'b01111101,
8'b01111101,
8'b10111011,
8'b11000111,
8'b11101111
};

reg [10:0] hcount;
reg [10:0] vcount;
reg pixel;


// For holding the outward bound TMDS symbols in the slow and fast domain
reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
reg [9:0] clk_high_speed;

reg [1:0] c2_output_bits;
reg [1:0] c1_output_bits;
reg [1:0] c0_output_bits;
reg [1:0] clk_output_bits;

wire clk_x5;
reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain

wire vsync, hsync;
wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
assign syncs = {vsync, hsync};

// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 525; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch (96 + 48)
parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
parameter vbp = 35; // end of vertical back porch (2 + 33)
parameter vfp = 515; // beginning of vertical front porch (525 - 10)

// registers for storing the horizontal & vertical counters
reg [9:0] vc;
reg [9:0] hc;
// generate sync pulses (active high)
assign vsync = (vc < vpulse);
assign hsync = (hc < hpulse);

always @(posedge clk_x5) begin
//-------------------------------------------------------------
// Now take the 10-bit words and take it into the high-speed
// clock domain once every five cycles.
//
// Then send out two bits every clock cycle using DDR output
// registers.
//-------------------------------------------------------------
c0_output_bits <= c0_high_speed[1:0];
c1_output_bits <= c1_high_speed[1:0];
c2_output_bits <= c2_high_speed[1:0];
clk_output_bits <= clk_high_speed[1:0];
if (latch_high_speed[2]) begin // pixel clock 25MHz
c0_high_speed <= c0_symbol;
c1_high_speed <= c1_symbol;
c2_high_speed <= c2_symbol;
clk_high_speed <= 10'b0000011111;
latch_high_speed <= 3'b000;
if (hc < hpixels)
hc <= hc + 1;
else
begin
hc <= 0;
if (vc < vlines)
vc <= vc + 1;
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

always @(*) // display 100% saturation colourbars
begin
pixel = heart[hcount+vcount];
// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------

if (vc >= vbp && vc < (vbp+60))
begin
vcount = 0;
end

else if (vc >= (vbp+60) && vc < (vbp+120))
begin
vcount = 8;
end

else if (vc >= (vbp+120) && vc < (vbp+180))
begin
vcount = 16;
end

else if (vc >= (vbp+180) && vc < (vbp+240))
begin
vcount = 24;
end

else if (vc >= (vbp+240) && vc < (vbp+300))
begin
vcount = 32;
end

else if (vc >= (vbp+300) && vc < (vbp+360))
begin
vcount = 40;
end

else if (vc >= (vbp+360) && vc < (vbp+420))
begin
vcount = 48;
end

else
begin
vcount = 56;
end


// display white bar
if (hc >= hbp && hc < hfp)
begin
if (pixel == 1'b1) begin
//yellow
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
else begin
//magenta
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b1011110000; // blue
end


if (hc >= hbp && hc < (hbp+80))
begin
hcount = 0;
end

else if (hc >= (hbp+80) && hc < (hbp+160))
begin
hcount = 1;
end

else if (hc >= (hbp+160) && hc < (hbp+240))
begin
hcount = 2;
end

else if (hc >= (hbp+240) && hc < (hbp+320))
begin
hcount = 3;
end

else if (hc >= (hbp+320) && hc < (hbp+400))
begin
hcount = 4;
end

else if (hc >= (hbp+400) && hc < (hbp+480))
begin
hcount = 5;
end

else if (hc >= (hbp+480) && hc < (hbp+560))
begin
hcount = 6;
end

else if (hc >= (hbp+560) && hc < hfp)
begin
hcount = 7;
end


end


// we're outside active horizontal range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end
// we're outside active vertical range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end

// red N
defparam hdmin2.PIN_TYPE = 6'b010000;
defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin2 (
.PACKAGE_PIN (hdmi_n[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c2_output_bits[1]),
.D_OUT_1 (~c2_output_bits[0])
);

// red P
defparam hdmip2.PIN_TYPE = 6'b010000;
defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip2 (
.PACKAGE_PIN (hdmi_p[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c2_output_bits[1]),
.D_OUT_1 (c2_output_bits[0])
);

// green N
defparam hdmin1.PIN_TYPE = 6'b010000;
defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin1 (
.PACKAGE_PIN (hdmi_n[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c1_output_bits[1]),
.D_OUT_1 (~c1_output_bits[0])
);

// green P
defparam hdmip1.PIN_TYPE = 6'b010000;
defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip1 (
.PACKAGE_PIN (hdmi_p[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c1_output_bits[1]),
.D_OUT_1 (c1_output_bits[0])
);


// blue N
defparam hdmin0.PIN_TYPE = 6'b010000;
defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin0 (
.PACKAGE_PIN (hdmi_n[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c0_output_bits[1]),
.D_OUT_1 (~c0_output_bits[0])
);

// blue P
defparam hdmip0.PIN_TYPE = 6'b010000;
defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip0 (
.PACKAGE_PIN (hdmi_p[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c0_output_bits[1]),
.D_OUT_1 (c0_output_bits[0])
);

// clock N
defparam hdmin3.PIN_TYPE = 6'b010000;
defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin3 (
.PACKAGE_PIN (hdmi_n[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~clk_output_bits[1]),
.D_OUT_1 (~clk_output_bits[0])
);


// clock P
defparam hdmip3.PIN_TYPE = 6'b010000;
defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip3 (
.PACKAGE_PIN (hdmi_p[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (clk_output_bits[1]),
.D_OUT_1 (clk_output_bits[0])
);
// D_OUT_0 and D_OUT_1 swapped?
// https://github.com/YosysHQ/yosys/issues/330


SB_PLL40_PAD #(
.FEEDBACK_PATH ("SIMPLE"),
.DIVR (4'b0000),
.DIVF (7'b0001001),
.DIVQ (3'b011),
.FILTER_RANGE (3'b101)
) uut (
.RESETB (1'b1),
.BYPASS (1'b0),
.PACKAGEPIN (clk100),
.PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
);

endmodule

******

Now I am trying to use hc and vc directly as the index of a 640×480 array. I used this site (https://www.dcode.fr/binary-image) to convert an image that was 640×480. I then used Notepad++ to add ‘640’b’ at the start of each line and ‘,’ at the end of each line. To do this, in the Find and Replace dialog box select Regular expression and use ‘^’ for the start of the sentence and ‘$’ for the end of the sentence.

The Ice40HX1K has 64 kbit of memory, 16 x 4kbit chunks. I am asking it to remember more than 300K bits.

//don't forget to subtract 1 from the total 640x480 (307200)

reg [0:307199] heart =

{
640'b .... ,
};

reg pixel;

...

always @(*) 
begin
pixel = heart[hc+(vc*640)];

if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------

// display white bar
if (hc >= hbp && hc < hfp)
begin
if (pixel == 1'b1) begin
//yellow
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
else begin
//magenta
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b1011110000; // blue
end


end


// we're outside the h range...

...

************

I have 16 bit by 16 !


`default_nettype none // disable implicit definitions by Verilog
//-----------------------------------------------------------------
// minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
//
// Author: Mike Field <hamster@snap.net.nz>
//
// DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
// value is mapped to one or two 10-bit symbols, depending on how
// many 1s or 0s have been sent. This makes it a DC balanced protocol,
// as a correctly implemented stream will have (almost) an equal
// number of 1s and 0s.
//
// Because of this implementation quite complex. By restricting the
// symbols to a subset of eight symbols, all of which having have
// five ones (and therefore five zeros) this complexity drops away
// leaving a simple implementation. Combined with a DDR register to
// send the symbols the complexity is kept very low.
//-----------------------------------------------------------------

module top(
clk100, hdmi_p, hdmi_n
);

input clk100;
output [3:0] hdmi_p;
output [3:0] hdmi_n;


reg [0:255] heart =

{
16'b1111111111111111,
16'b0001000100010001,
16'b0110110101101101,
16'b0111110101111101,
16'b0111110101111101,
16'b1011101110111011,
16'b1100011111000111,
16'b1110111111101111,
16'b1111111111111111,
16'b0001000100010001,
16'b0110110101101101,
16'b0111110101111101,
16'b0111110101111101,
16'b1011101110111011,
16'b1100011111000111,
16'b1110111111101111
};

reg [10:0] hcount;
reg [10:0] vcount;
reg pixel;


// For holding the outward bound TMDS symbols in the slow and fast domain
reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
reg [9:0] clk_high_speed;

reg [1:0] c2_output_bits;
reg [1:0] c1_output_bits;
reg [1:0] c0_output_bits;
reg [1:0] clk_output_bits;

wire clk_x5;
reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain

wire vsync, hsync;
wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
assign syncs = {vsync, hsync};

// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 525; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch (96 + 48)
parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
parameter vbp = 35; // end of vertical back porch (2 + 33)
parameter vfp = 515; // beginning of vertical front porch (525 - 10)

// registers for storing the horizontal & vertical counters
reg [9:0] vc;
reg [9:0] hc;
// generate sync pulses (active high)
assign vsync = (vc < vpulse);
assign hsync = (hc < hpulse);

always @(posedge clk_x5) begin
//-------------------------------------------------------------
// Now take the 10-bit words and take it into the high-speed
// clock domain once every five cycles.
//
// Then send out two bits every clock cycle using DDR output
// registers.
//-------------------------------------------------------------
c0_output_bits <= c0_high_speed[1:0];
c1_output_bits <= c1_high_speed[1:0];
c2_output_bits <= c2_high_speed[1:0];
clk_output_bits <= clk_high_speed[1:0];
if (latch_high_speed[2]) begin // pixel clock 25MHz
c0_high_speed <= c0_symbol;
c1_high_speed <= c1_symbol;
c2_high_speed <= c2_symbol;
clk_high_speed <= 10'b0000011111;
latch_high_speed <= 3'b000;
if (hc < hpixels)
hc <= hc + 1;
else
begin
hc <= 0;
if (vc < vlines)
vc <= vc + 1;
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

always @(*) // display 100% saturation colourbars
begin
pixel = heart[hcount+vcount];
// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------
if (vc >= vbp && vc < (vbp+30)) vcount = 0;
else if (vc >= (vbp+30) && vc < (vbp+60)) vcount = 16;
else if (vc >= (vbp+60) && vc < (vbp+90)) vcount = 32;
else if (vc >= (vbp+90) && vc < (vbp+120)) vcount = 48;
else if (vc >= (vbp+120) && vc < (vbp+150)) vcount = 64;
else if (vc >= (vbp+150) && vc < (vbp+180)) vcount = 80;
else if (vc >= (vbp+180) && vc < (vbp+210)) vcount = 96;
else if (vc >= (vbp+210) && vc < (vbp+240)) vcount = 112;
else if (vc >= (vbp+240) && vc < (vbp+270)) vcount = 128;
else if (vc >= (vbp+270) && vc < (vbp+300)) vcount = 144;
else if (vc >= (vbp+300) && vc < (vbp+330)) vcount = 160;
else if (vc >= (vbp+330) && vc < (vbp+360)) vcount = 176;
else if (vc >= (vbp+360) && vc < (vbp+390)) vcount = 192;
else if (vc >= (vbp+390) && vc < (vbp+420)) vcount = 208;
else if (vc >= (vbp+420) && vc < (vbp+450)) vcount = 224;
else vcount = 240;

// display white bar
if (hc >= hbp && hc < hfp)
begin
if (pixel == 1'b1) begin
//yellow
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
else begin
//magenta
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b1011110000; // blue
end


if (hc >= hbp && hc < (hbp+40)) hcount = 0;
else if (hc >= (hbp+40) && hc < (hbp+80)) hcount = 1;
else if (hc >= (hbp+80) && hc < (hbp+120)) hcount = 2;
else if (hc >= (hbp+120) && hc < (hbp+160)) hcount = 3;
else if (hc >= (hbp+160) && hc < (hbp+200)) hcount = 4;
else if (hc >= (hbp+200) && hc < (hbp+240)) hcount = 5;
else if (hc >= (hbp+240) && hc < (hbp+280)) hcount = 6;
else if (hc >= (hbp+280) && hc < (hbp+320)) hcount = 7;
else if (hc >= (hbp+320) && hc < (hbp+380)) hcount = 8;
else if (hc >= (hbp+380) && hc < (hbp+420)) hcount = 9;
else if (hc >= (hbp+420) && hc < (hbp+440)) hcount = 10;
else if (hc >= (hbp+440) && hc < (hbp+480)) hcount = 11;
else if (hc >= (hbp+480) && hc < (hbp+520)) hcount = 12;
else if (hc >= (hbp+520) && hc < (hbp+560)) hcount = 13;
else if (hc >= (hbp+560) && hc < (hbp+600)) hcount = 14;
else hcount = 15;


end


// we're outside active horizontal range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end
// we're outside active vertical range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end

// red N
defparam hdmin2.PIN_TYPE = 6'b010000;
defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin2 (
.PACKAGE_PIN (hdmi_n[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c2_output_bits[1]),
.D_OUT_1 (~c2_output_bits[0])
);

// red P
defparam hdmip2.PIN_TYPE = 6'b010000;
defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip2 (
.PACKAGE_PIN (hdmi_p[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c2_output_bits[1]),
.D_OUT_1 (c2_output_bits[0])
);

// green N
defparam hdmin1.PIN_TYPE = 6'b010000;
defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin1 (
.PACKAGE_PIN (hdmi_n[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c1_output_bits[1]),
.D_OUT_1 (~c1_output_bits[0])
);

// green P
defparam hdmip1.PIN_TYPE = 6'b010000;
defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip1 (
.PACKAGE_PIN (hdmi_p[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c1_output_bits[1]),
.D_OUT_1 (c1_output_bits[0])
);


// blue N
defparam hdmin0.PIN_TYPE = 6'b010000;
defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin0 (
.PACKAGE_PIN (hdmi_n[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c0_output_bits[1]),
.D_OUT_1 (~c0_output_bits[0])
);

// blue P
defparam hdmip0.PIN_TYPE = 6'b010000;
defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip0 (
.PACKAGE_PIN (hdmi_p[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c0_output_bits[1]),
.D_OUT_1 (c0_output_bits[0])
);

// clock N
defparam hdmin3.PIN_TYPE = 6'b010000;
defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin3 (
.PACKAGE_PIN (hdmi_n[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~clk_output_bits[1]),
.D_OUT_1 (~clk_output_bits[0])
);


// clock P
defparam hdmip3.PIN_TYPE = 6'b010000;
defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip3 (
.PACKAGE_PIN (hdmi_p[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (clk_output_bits[1]),
.D_OUT_1 (clk_output_bits[0])
);
// D_OUT_0 and D_OUT_1 swapped?
// https://github.com/YosysHQ/yosys/issues/330


SB_PLL40_PAD #(
.FEEDBACK_PATH ("SIMPLE"),
.DIVR (4'b0000),
.DIVF (7'b0001001),
.DIVQ (3'b011),
.FILTER_RANGE (3'b101)
) uut (
.RESETB (1'b1),
.BYPASS (1'b0),
.PACKAGEPIN (clk100),
.PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
);

endmodule

 

 

******

Finally, I have a non painful way of displaying images. This is the heart of it :

//divide hc (once in visible area) by 4, divide vc (once in visible area) by 4 and then multiply it by the row width

pixel = heart[(((hc-hbp)>>2)+(((vc-vbp)>>2)*160))];

IceCube2 appears to be happy with register sizes that are not powers of two and that extend beyond 4K. That said there were no BRAMs inferred in this and I’m around 900/1.2K (!!) blocks used up. Next step is to figure out how to use BRAM and the same simple binary division principle.

`default_nettype none // disable implicit definitions by Verilog
//-----------------------------------------------------------------
// minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
//
// Author: Mike Field <hamster@snap.net.nz>
//
// DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
// value is mapped to one or two 10-bit symbols, depending on how
// many 1s or 0s have been sent. This makes it a DC balanced protocol,
// as a correctly implemented stream will have (almost) an equal
// number of 1s and 0s.
//
// Because of this implementation quite complex. By restricting the
// symbols to a subset of eight symbols, all of which having have
// five ones (and therefore five zeros) this complexity drops away
// leaving a simple implementation. Combined with a DDR register to
// send the symbols the complexity is kept very low.
//-----------------------------------------------------------------

module top(
clk100, hdmi_p, hdmi_n
);

input clk100;
output [3:0] hdmi_p;
output [3:0] hdmi_n;

//160x120
reg [0:19199] heart =

{
160'b0000000000000000000000000000000000000011111111111111111111111111111111111101111100001111110000011111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000000011111111111111111111111111111111111100000111111111111111000011100111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000110000110001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000110000011011111111111111111111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000110000011111111111111111111111111111111111111111111111111111111111111111100111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111000111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111000111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000000000001101111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000000000101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000000000111111111111111111111111001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000000001111001111111111111111111111111111111111111111111111111111111111111111111111111101111111111111111111111111111111111111111,
160'b0000000000000000000000000000000111000001111001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000010011001111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000001000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000011000000000000000101111001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000001111110000000011111111001111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000001111111110000011111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000011111111111000011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000001111111111111111111111111111111111111111111111111111011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000001100111111111111111111111111111111111111111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000111111111111111111111111111111101111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000100000111111111111111111111111111111111111111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000111111111111111111111111111111111111111111111101111100000011111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000000000111111111111111111111111111111111111111111111111111111110001111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000110000001111111111111111111111111111111111111111111111110001111111000001111100001111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000001110000001111111111111111111111111111111111111111111111100001111111000001111110001111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000110000000011111111111111111111111111111111111111111111111000000111111100000111111000111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000100000000111111111111111111111111111111111111110111111110000000000000000001111111000000001111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000111111111111111111111111111111111111111110111111111100000000001111111111111110110000111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000111111111111111111111111111111111111111111111111111100000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000000011111101111111111111111111111111111111111111111111100011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000001100000111000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000001100011111100111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000001100011111111111111111111111111111111111111111111111110011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000110011111111111111111111111111111111111111111111111110001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000001110111111111111111111111111111111111111111111111111110001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000011110011111111111111111111111111111111111111111111111110001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000111111011111111111111111111111111111111111111111111111110001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000111111111111111111111111111111111111111111111111111111111110001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1000111011111111111111111111111111111111111111111111111111111110001111111111110011111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1100100000001111111111110111111111111111111111111110111111111100001111111111110000111011111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1100000000000111111110011111111111111111111111111100111111111100001111111111110000010001111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1000000000000111111100011111111111111111111111111000111111111000000111111111110000000000111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1000000000000111111100111111111111111111111111111100111111111100010111111111100000000000111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0000000000001111111111111111111111111111111111111111111111111111110111111111000110000100111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0111000000000111111111111111111111111111111111110110011111111111111111100010001111111110011111111111111111111111111111111111111111111111111111111111111111111111,
160'b0111100000000001011111111111111111111111111111111110011111111111101111000000001111111110001111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111110000000000011111111111111111111111111111111111011111111111100110000000000110000000011111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111110000011111111111111111111111111111111111111111111111111000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111101110001111111111111111111111111111111111111111111111011110000000000000000000000000000011111111111111111111111111111111111111111111111111111111111111111111,
160'b1110011111001111111111111111111111111111111111111111111111111100000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111001111111111111111111111111111111111111111111111111000000000000000000000110000000000111111111111111111111111111111111111111111111111111111111111111111,
160'b1001111111000001001111111111111111111111111111111111111111111000000000000000000001111000000000011111111111111111111111111111111111111111111111111111111111111111,
160'b0001111111000001000011101111111111111111111111111111111111111110000000000000000011111110000000000111111111111111111111111111111111111111111111111111111111111111,
160'b0001111111000001100011100011111111111111111111111111111111111111100000000000000011110111100000000011111111111111111111111111111111111111111111111111111111111111,
160'b0000111111000001110011100001111111111111111111111111111111111111100000000111111111000111110000000001111111111111111111111111001111111111111111111111111111111111,
160'b1111001111110011111111110001111111111111111111111111111111111111100000001111111111011111100000000001111111111111111111111000000111111111111111111111111111111111,
160'b1111100111111011111111111110011111111111111111111111111111111111111111111111111111111111111011100001111111111111111111111000001111111111111111111111111111111111,
160'b1111100111110011111111111111111111111111111111111111111111111111111111111111111111111111111111110011111111111111111111111100011111111111111111111111111111111111,
160'b0111111111110111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100011111111111111111111111111111111111,
160'b0111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110001111111111111111111111111111111111,
160'b0111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110001111111111111111111111111111111111,
160'b0111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110001111111111111111111111111111111111,
160'b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111011111111111111111111111111111111111,
160'b1111111111000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0111111111101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b0111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111111111111111111111111111111111111100000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111111111111111111111111111111111000000000000011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111111111111111111111111111111000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111111111111111111111111111100000000000000000000011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111,
160'b1111111111111111111111111111111111000000000000000000000001111111111111111111111111111111111111111111111111111111111111111110000111111111111111111111111111111111,
160'b1111111111111111111111111111111110000000000000000000000001111111111111111111111111111111111111111111111111111111111111111000000001111111111111111111111111111111,
160'b1111111111111111111111111111111110000000000000000000000000111111111111111111111111111111111111111111111111111111111111111000000000111111111111111111111111111111,
160'b1111111111111111111111111111111100000000000000000000000000111111111111111111111111111111111111111111111111111111111110000000000000011111111111111111111111111111,
160'b1111111111111111111111111111111000000000000000000000000000011111111111111111111111111111111111111111111111111111111110000000000000000111111111111111111111111111,
160'b1111111111111111111111111111111000000000000000000000000000001111111111111111111111111111111111111111111111111111111110000000000000000001111111111111111111111111,
160'b1111111111111111111111111111110000000000000000000000000000000111111111111111111111111111111111111111111111111111111110000000000100000000011111111111111111111111,
160'b1111111111111111111111111111110000000000000000000000000000000111111111111111111111111111111111111111111110011111111100000000000000001100000111111111111111111111,
160'b1111111111111111111111111111110000000000000000000000000000001111111111111111111111111111111111111111111000000111111100000000000000001110000011111111111111111111,
160'b1111111111111111111111111111100000000000000000000000000000011111111111111111111111111111111111111111000000000011100000000000000000001110000000111111111111111111,
160'b1111111111111111111111111111000000000000000000000000000001111111111111111111111111111111111111111110000000000000000000000000000000001111000000011111111111111111,
160'b1111111111111111111111111110000000000000000000000000000011111111111111111111111111111111111100000000000000000000000000000000000001111000000000001111111111111111,
160'b1111111111111111111111111110000000000000000000000000000111111111111111111111111111111111111000000000000000000000000000000011110011111000000100001111111111111111,
160'b1111111111111111111111111100000000000000000000000000001111111111111111111111111111001111000000000000000000000000000000100011111111111000011000111111111111111111,
160'b1111111111111111111111111000000000000000000000000000011111111111111111111111111110000000000000000000000000000000000011110000111111110000111000111111111111111111,
160'b1111111111111111111111110000000000000000000000000000111111111111111111111111111000000000000000000000000110000000001111000001111111110011110001111111111111111111,
160'b1111111111111111111111100000000000000000000000000001111111111111111111111111110000000000000000000000001100000001111000000011111111100111100011111111111111111111,
160'b1111111111111111111111000000000000000000000000000011111111111111111111111111100000000000000000000000011100000011111000001111111111000111000111111111111111111111,
160'b1111111111111111111110000000000000000000000000000111111111111111111111111111000000000000000000000000111000000111110000011111111110001110001111111111111111111111,
160'b1111111111111111111110000000000000000000000000001111111111111111111111111111000000000000000000000001110000001111110000111111111110001111111111111111111111111111,
160'b1111111111111111111100000000000000000000000000011111111111111111111111111110001011001100000000000001100000111111110011111111111100011111111111111111111111111111,
160'b1111111111111111111100000000000000000000010000111111111111111111111111111100011111111110001100000011100011111111100011111111111100111111111111111111111111111111,
160'b1111111111111111111000000000000000000000000001111111111111111111111111111000111111111110001110000111000111111111100111111111111101111111111111111111111111111111,
160'b1111111111111111110000000000000000000001000011111111111111111111111111110001111111111110001110001111001111111111000111111111111011111111111111111111111111111111,
160'b1111111111111111110000000000000000000010000111111111111111111111111111110011111111111110001111111110011111111111011111111111111111111111111111111111111001111111,
160'b1111111111111111100000000000000000000100001111111111111111111111111111101111111111111110000111111111111111111110111111111111111111111111111111111111110011111111,
160'b1111111111111111100000000000000000000000011111111111111111111111111111001111111111111111000000011111111111111111111111101111111111111111111111111111100011111111,
160'b1111111111111111000000000000000000000000111111111111111111111111111100011111111111111111100000000111111111111111111111001111111111111111111111111111100111111111
};

reg pixel;


// For holding the outward bound TMDS symbols in the slow and fast domain
reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
reg [9:0] clk_high_speed;

reg [1:0] c2_output_bits;
reg [1:0] c1_output_bits;
reg [1:0] c0_output_bits;
reg [1:0] clk_output_bits;

wire clk_x5;
reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain

wire vsync, hsync;
wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
assign syncs = {vsync, hsync};

// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 525; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch (96 + 48)
parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
parameter vbp = 35; // end of vertical back porch (2 + 33)
parameter vfp = 515; // beginning of vertical front porch (525 - 10)

// registers for storing the horizontal & vertical counters
reg [9:0] vc;
reg [9:0] hc;
// generate sync pulses (active high)
assign vsync = (vc < vpulse);
assign hsync = (hc < hpulse);

always @(posedge clk_x5) begin
//-------------------------------------------------------------
// Now take the 10-bit words and take it into the high-speed
// clock domain once every five cycles.
//
// Then send out two bits every clock cycle using DDR output
// registers.
//-------------------------------------------------------------
c0_output_bits <= c0_high_speed[1:0];
c1_output_bits <= c1_high_speed[1:0];
c2_output_bits <= c2_high_speed[1:0];
clk_output_bits <= clk_high_speed[1:0];
if (latch_high_speed[2]) begin // pixel clock 25MHz
c0_high_speed <= c0_symbol;
c1_high_speed <= c1_symbol;
c2_high_speed <= c2_symbol;
clk_high_speed <= 10'b0000011111;
latch_high_speed <= 3'b000;
if (hc < hpixels)
hc <= hc + 1;
else
begin
hc <= 0;
if (vc < vlines)
vc <= vc + 1;
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

always @(*) // display 100% saturation colourbars
begin

// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------

// display white bar
if (hc >= hbp && hc < hfp)
begin
//divide hc (once in visible area) by 4, divide vc (once in visible area) by 4 and multiply by the row width
pixel = heart[(((hc-hbp)>>2)+(((vc-vbp)>>2)*160))];

if (pixel == 1'b1) begin
//yellow
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
else begin
//magenta
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b1011110000; // blue
end


end


// we're outside active horizontal range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end
// we're outside active vertical range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end

// red N
defparam hdmin2.PIN_TYPE = 6'b010000;
defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin2 (
.PACKAGE_PIN (hdmi_n[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c2_output_bits[1]),
.D_OUT_1 (~c2_output_bits[0])
);

// red P
defparam hdmip2.PIN_TYPE = 6'b010000;
defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip2 (
.PACKAGE_PIN (hdmi_p[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c2_output_bits[1]),
.D_OUT_1 (c2_output_bits[0])
);

// green N
defparam hdmin1.PIN_TYPE = 6'b010000;
defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin1 (
.PACKAGE_PIN (hdmi_n[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c1_output_bits[1]),
.D_OUT_1 (~c1_output_bits[0])
);

// green P
defparam hdmip1.PIN_TYPE = 6'b010000;
defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip1 (
.PACKAGE_PIN (hdmi_p[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c1_output_bits[1]),
.D_OUT_1 (c1_output_bits[0])
);


// blue N
defparam hdmin0.PIN_TYPE = 6'b010000;
defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin0 (
.PACKAGE_PIN (hdmi_n[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c0_output_bits[1]),
.D_OUT_1 (~c0_output_bits[0])
);

// blue P
defparam hdmip0.PIN_TYPE = 6'b010000;
defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip0 (
.PACKAGE_PIN (hdmi_p[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c0_output_bits[1]),
.D_OUT_1 (c0_output_bits[0])
);

// clock N
defparam hdmin3.PIN_TYPE = 6'b010000;
defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin3 (
.PACKAGE_PIN (hdmi_n[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~clk_output_bits[1]),
.D_OUT_1 (~clk_output_bits[0])
);


// clock P
defparam hdmip3.PIN_TYPE = 6'b010000;
defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip3 (
.PACKAGE_PIN (hdmi_p[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (clk_output_bits[1]),
.D_OUT_1 (clk_output_bits[0])
);
// D_OUT_0 and D_OUT_1 swapped?
// https://github.com/YosysHQ/yosys/issues/330


SB_PLL40_PAD #(
.FEEDBACK_PATH ("SIMPLE"),
.DIVR (4'b0000),
.DIVF (7'b0001001),
.DIVQ (3'b011),
.FILTER_RANGE (3'b101)
) uut (
.RESETB (1'b1),
.BYPASS (1'b0),
.PACKAGEPIN (clk100),
.PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
);

endmodule

 

*********

I can now start to take video info in from the rpi and then output it on the FPGA clock !!

The key was using rpi_pixel_clock to trigger storing color in data and storing it in a single 90 bit register, then later using that as the horizontal line to display.

always @(posedge rpi_pixel_clock) begin

if(rpi_DEN)begin
      rpi_hc <= rpi_hc+1; // count horizontal pixels while DEN
      heart[(rpi_hc>>2)]<=b_in; // put color data in a 90 bit long register (using posedge rpi pixel clock already divides once)
end
else begin
rpi_hc <= 0;
end
end

...

always @(*) 
begin

// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// while we're within the active vertical range
// -----------------

if (hc >= hbp && hc < hfp)
begin
// while we're within the active horizontal range
// -----------------
pixel = heart[(hc>>3)]; // read a portion of the 90 bit long register

if (pixel == 1'b1) begin
//yellow
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
else begin
//magenta
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b1011110000; // blue
end

end

*****

 

I’ve got rpi outputing 640 x 480 with this added to config.txt :

gpio=0-9=a2
gpio=12-17=a2
gpio=20-25=a2
gpio=26-27=a2

dtoverlay=dpi24
enable_dpi_lcd=1
display_default_lcd=1
dpi_group=2
dpi_mode=87
dpi_output_format=516118
dpi_timings=640 1 44 2 42 480 1 16 2 14 0 0 0 60 0 32000000 1

But when I try timng based on a 25MHz ,or 25.175MHz, clock at 640×480 it doesn’t work passing through the FPGA with a 25MHz pixel clock.

dpi_timings=640 1 16 96 48 480 1 10 2 33 0 0 0 60 0 25000000 1

#FPGA timings

#<hactive> 640
#<h_sync_polarity> 1
#<hfp> 16
#<hsync> 96
#<hbp> 48
#<vactive> 480
#<v_sync_polarity> 1
#<vfp> 10
#<vsync> 2
#<vbp> 33
#<n/a>
#<n/a>
#<n/a>
#<n/a>
#<n/a>
#<clock-frequency> 25000000
#<n/a>
*****
I did get 90 in the horizontal and vertical however by recording every time vc has the last 4 bits high :

...

always @(posedge rpi_pixel_clock) begin


if(rpi_DEN && trig_record)begin
rpi_hc <= rpi_hc+1; // count horizontal pixels while DEN
heart[(rpi_hc>>2)]<=b_in; // put color data in a 90 bit long register (using posedge rpi pixel clock already divides once)
end

...

end
...

always @(posedge clk_x5) begin

if(vc[3:0] == 4'b1111) begin

trig_record <= 1;

end
else begin
trig_record <= 0;
end

...

end

*************

NO SCHOOL NEVERS BOARD:

  1. Removing things I don’t yet master with the FPGA like SD card, SRAM and rpi header. This will force me to learn to use the internal BRAM.
  2. Adding NO SCHOOL 24 logo
  3. Removing ground plane grid as it will interfere with text and soldering
  4. adding Cold Boot Sel Bits and a reset and a little table. This will be cool to learn how to make 4 small FPGA images that can be selected on restart.
  5. Added text to identify programming pins for instance.
  6. Changed the potentiometer so that it can be read by the FPGA
  7. Removed the comparator and changed to a cap + resistor bias + op amp follower setup so that analog input signal (via audio jack) can be read by FPGA.
  8. I tried to break out extra pins but just with small pads, but I also added 4 digital in pins.
  9. added quote !
  10. I’ve found leftover components at my lab which I will subtract from the Mouser inventory in order to order some fun parts like tiny 2pin DIP switches for the cold boot select and some op amps.
  11. I realized the VGA I ordered are male and mirrored. But I like the slim footprint (this way it doesn’t overhang over the edge of the board so much) so I ordered female slim and added to the board.

The plan for the 10h-13h, 14h-17h workshop is to start with basic VGA signal modification.

Start by cutting VGA cable and putting pins on a breadboard.

  • What happens when you remove a color ? GND ? V or H sync ?
  • How about switching colors ?
  • Look at V and H sync on the oscilloscope

Let’s make waves with a function generator and see what they look like on screen.

  • What do different frequencies look like on screen ?
  • How about different wave types and duty cycles ?

What can we do with analog electronics ?

  • A simple low-pass filter ?
  • An amp

Get a second VGA video source.

  • What happens when you mix colors from one source and colors from another source ?
  • Which H and V signals do you send along to the screen ?
  • Try using a potentiometer to mix two colors together, or fade a color out.

Introduction to logic:

  • Let’s use a comparator to convert analog into ON or OFF
  • Let’s try using an AND, OR and XOR with two video sources.

Introduction to FPGAs : The FPGA is reconfigurable logic which you can program to mess with video signals and respond to a tactile interface. To program the logic gates we use a Hardware Description Language (HDL) like verilog.

  • Here’s how we generate a video signal using an FPGA
  • Here’s how we generate some simple patterns
  • Here’s how we make it react to button touches

The FPGA excels at doing things in parallel, with low latency, and consuming little power. In the world of video that means filters, basically what you can do with photoshop: spatial (2D, pixel based) : contrast, blur, edge detection, denoising, upsampling, downsampling. But this also as applied to a series of images (i.e. the same pixel location but at different moments in time) as in a video which is called temporal (or 3D) filtering. CPUs, by contrast, like an Arduino, have a series of instructions, and keep track of a program counter to know what line of code they are currently executing. (Yes GPUs can do things in parallel, but there aren’t GPUs we can solder ourselves and program in the same way.) FPGAs can be found in high-end networking equipment, scientific measurement equipement (like oscilloscopes and logic analyzers), hardware accelerators for certain types of AI, computer vision and FLIR heat cameras, and things sent into space (FPGAs are better at resisting radiation because of their architecture). They are good, flexible practice for ASICs, which are even faster but not reconfigurable and only cost effective when made on very large scales. 

There are two types of digital logic we can implement in the FGPA : combinatorial and sequential. Combinatorial circuits are just like “raw” logic gates, reacting quickly to changing values and living in the near present. Combinatorial circuits are hard-wired together, they are limited mainly by the speed of electricity which is around that of light (1ns / foot). Sequential circuits, on the other hand, remember a past state, and therefore need some concept of discrete time like a clock ticking. A counter, which adds one each edge of a clock pulse until it rolls over, is an example of a sequential circuit.

The FPGA isn’t so much programmed as it is configured, by connecting all of it’s internal logic blocks together in various ways. (Yes, there are also specialized I/O blocks and BRAMs to store data, we’ll hopefully use these).

We’re going to assemble this kit that allows us to generate video (VGA or HDMI), and react to use button presses and knob turning. This could be done with an Arduino UNO, but it wouldn’t be able to do it as fast and as precisely (nor can it output HDMI). It’s primarily an FPGA development board, to get you started working with FPGAs, that has the addition of fun buttons. (Yes, FPGAs have been around for a while but they are still percolating into the DIY world and remain technically challenging for beginners).

****

I’ll need to prepare the following things for NO SCHOOL NEVERS 24:

  1. Four different images that fit into the 1200 or so LUTs of the Lattice ICE40 that just need to fit into the 4MB flash (each image is in kilobytes), that respond to 9 keys, 1 pot, music (and video?) in, and VGA out (primarily) and HDMI out. 
  2. 18 kits, with all necessary components, and PCBs with at least FPGA, power and programming and debugging related components, soldered.
  3. The VGA workshop materials : VGA cables, breadboards, jumpers, passive components, logic chips, pots + oscilloscope, function generator.
  4. A talk that could include a chapter on video hardware experiments.

****

I’m working on getting the four demo codes reacting to buttons, potentiometer and audio in. I’ve had various problems, mostly due to mysterious VGA behaviour only outputting v and h sync when a certain buttons is pushed, for instance, and perhaps (haven’t confirmed this) IceCube2 having difficulty identifying the top module when there are multiple .v files.

I have combined the random, tempo and sine files into the principle vga_test file and looked at the VGA signals with the microscope to debug.

Some codes, like random walk or grid of forms + noise, would be easier with a screen buffer.

So far the coolest thing I have is this automatic rectangle color composition, but I haven’t been able to make it interactive in any way :

I have learned that the best way to switch between different form generating codes is just to run them all in parallel and then select which of them you want to use like with a MUX :

// CONDITIONS

wire a,b,c,d;
reg condition;

assign a = ((bit_or[random_number_2[3:0]] == 1) || (bit_or[random_number_6[3:0]] == 0)) ? 1 : 0;
assign b = ((h_count % 100 < (random_number_0[9:0] % (100+data_out))) && (v_count % 100 < (random_number_1[9:0] % (100-data_out)))) ? 1 : 0;
assign c = ((h_count % 100 < random_number_0[9:0] % 100) && (v_count % 100 < random_number_1[9:0] % 100)) ? 1 : 0;
assign d = ((out_bit[random_number_2[3:0]] == 1) || (out_bit[random_number_6[3:0]] == 0)) ? 1 : 0;

//priority encoder
always @(*) begin
casez(key)
9'b1????????: condition = a;
9'b?1???????: condition = b;
9'b??1??????: condition = c;
9'b???1?????: condition = d;
9'b????1????: condition = a;
9'b?????1???: condition = b;
9'b??????1??: condition = c;
9'b???????1?: condition = d;
9'b????????1: condition = a;
default: condition = a;
endcase
end

Apparently casez can be a dangerous thing to use in verilog though (see https://www.verilogpro.com/verilog-case-casez-casex/)…

****

I’ve got a simple sine wave demo setup using a LUT :

reg [4:0] addr;
reg [7:0] data;
reg [7:0] data_out;
always@(addr) begin
case (addr)

0 : data = 0;
1 : data = 16;
2 : data = 31;
3 : data = 45;
4 : data = 58;
5 : data = 67;
6 : data = 74;
7 : data = 77;
8 : data = 77;
9 : data = 74;
10 : data = 67;
11 : data = 58;
12 : data = 45;
13 : data = 31;
14 : data = 16;
15 : data = 0;
16 : data = -16;
17 : data = -31;
18 : data = -45;
19 : data = -58;
20 : data = -67;
21 : data = -74;
22 : data = -77;
23 : data = -77;
24 : data = -74;
25 : data = -67;
26 : data = -58;
27 : data = -45;
28 : data = -31;
29 : data = -16;
default : data = 0;
endcase
end

reg[25:0] div_cntr1;
reg[25:0] div_cntr2;
reg half_sec_pulse;

always@(posedge clk_in)
begin
div_cntr1 <= div_cntr1 + 1;
if (div_cntr1 == 0)
if (div_cntr2 == 0)
begin
div_cntr2 <= 0;
half_sec_pulse <= 1;
end
else
div_cntr2 <= div_cntr2 + 1;
else
half_sec_pulse <= 0;

end


always @(posedge half_sec_pulse) begin

data_out <= data;
addr <= addr + 1;
if(addr == 29)
addr <= 0;

end

 

*****

Synplify Pro by Lattice lets you see the RTL :

You can also simulate verilog code live in browser (https://digitaljs.tilk.eu/):

 

Check out how pretty these FPGA layouts are :

FFGlitch that Rémi told me about :

 

*****

Project summary of last 2 years so far :

So far in this project I’ve worked with video in a naive, hands-on way by taking a stream and modifying it and recording it directly, without processing it first and taking into account the framing, into memory. I’ve learned to use higher level programs like Processing to generate simple patterns. I’ve learned how to configure digital circuits with FPGAs to generate video. On the product design side, I’ve practiced designing PCBs with different interfaces, worked with color and black and white, and experimented with SD cards, HDMI out, and rpi DPI for a future product idea. I’m starting to learn how to process incoming video with the FPGA and I can display an image stored in ROM on-screen but next I’d like to display dynamic memory on screen as a screen buffer (either using cascaded internal BRAM or external SRAM). Then I could start leaving traces of past frames as is fun in Processing, and blitting (changing the memory contents “directly” at a memory level) and seeing the result on the screen directly. Next would come spatial and temporal filtering at a hardware level.

I am starting to wonder if my home is DIY not design. I don’t know if I want to sell niche designy synth objects to bobos for 600 euros, I think I’d prefer to make inexpensive open source kits and workshops.

****

The title of the NO SCHOOL event is NØ VIDEO GLITCH FPGA, so where are my glitches ?! N.B. Glitch might come from the German word glitschen ‘to slip’ and the Yiddish word glitshn ‘to slide, to skid’.

  • The HDMI DC imbalanced glitch
  • Desynchronized and zoomed out video
  • messing with the functioning of the recording module creating choppy scrabled recordings
  • Images being sampled at such a ridiculously low rate that one byte represents a whole image frozen on screen
  • ghost recordings when recording parallel tracks in SRAM

******

Met a cool NY-based, sound engineer named Noah Ross who gave me some cool references :

The metaphore of the cone of the possible with a tool, some offering more exploration and failure than others.

Chase bliss – Generation Loss (https://www.chasebliss.eu/generation-loss-mkii) They explain that they took apart VCRs and other tape media to create this device which allows for precise calibration of tape effects. This connects with what Noah was describing when talking about tools that let you change the heartbeat of the machine, like letting you alter one part of a digestive tract without knowing what impact it will have on the end output.

Noah also mentioned a sampler that takes varying sizes of mini samples from a sample in different looping and sub sample selections dynamically.

I’m really impressed with the text they have developed to describe the functionality of these :

Spatial chemistry, Living landscapes, Spectral surprises.

Generation Loss MKII_Dip Switches_Pedal_Chase Bliss.jpg

Chord synths with buttons for chords :

ICON on X: "ボタンとジョイスティックでコードを演奏できるポケット・シンセ、Pocket Audio「HiChord」が誕生 - ICON https://t.co/bdQOVxMhQy https://t.co/tVvnr6lAtz" / X

This Monome grid interface (https://monome.org/docs/grid/) which lets you interact with an array of samples by moving through them in different patterns :

Monome teletype (https://monome.org/docs/teletype/), an algorithmic ecosystem where you code your own triggers for events :

An Oil can delay, which can have a knob for “viscosity” ! I don’t understand how this works yet :

Morley Oil Can Delay

 

 

 

 

 

 

 

******

COLD BOOT process :

In diamond Programmer go to Design > Utilities > Deployment Tool then select External Memory and Advanced SPI Flash

…and then select a binary file :

Change the SPI Flash Size to 32Mb and then select the Multiple Boot binary files (Ice erases any folders you make in its output file) :

The last step is to generate the mcs file.

 

I tested with different speeds of LED blinking and it works with the reset button and the dip switch.

 

****

https://en.wikipedia.org/wiki/Image_scaling

****

FEEDBACK FROM PRACTICE WORKSHOP :

print an image of what to solder where, numbers of each component needed, pieces of paper with tape and pens, tweezers, soldering iron, solder, flux

solder everything that needs a microscope !

Intro to SMD soldering, tacking, boards to practice soldering on.

have other programs open to do simulate and show the floor

only talk about stuff that is directly applicable in the workshop, with an eye to the finality (why am I soldering this specific thing). No theory, just practice

to solder 12 things takes a while !!! The buttons are the coolest.

to cut into mini projects with a mini success at the end.

Should talk about why it is worth learning hardware nowadays.

how to make it so they can use it, reprogram it,

perhaps let them code something super super simple with you, like you push this button and this color appears on screen ! Prepare a code where they do tiny change and can see the effect.

BE LESS AMBITIOUS for this workshop, it would take a whole week to do what you have planned dude. You could imagine a tiny thing with four buttons and an HDMI out and they do a code that changes the color that appears on screen, or sents a bitmap.

explain how HDMI and/or VGA formats work ?

Perhaps the best demonstration of what the FPGA can do is to have a camera in and show if filtering the live input and outputting that to HDMI ?

what is the minimum ? (HDMI resistors + just the keys without resistors or caps?)

intro to electronics ? intro to digital electronics ? Intro to logic ? Intro to binary ? Into to FPGA ??

Have already prepared rock solid codes which work 100% and are already loaded onto the boards on organized with correct pcf + v + auxillary files

Have the four codes prepped and reacting to music + key touch + potentiometer !

Thinking back to things I’ve learned from Arduino and Processing workshops of the past : Go to the simplest/easiest + coolest things as fast as possible. Stay away from difficult tech stuff with little reward and give them an opportunity to be creative fast.

****

To do in the next 3 weeks :

Finish soldering the 17 boards, everything except knobs, buttons and audio-in jack. (necessary to solder the 10K + 1uF for buttons or just 1K ? Should solder HDMI as I won’t be using it in the workshop?)

Develop 4 different VGA (+ HDMI?) codes which react to pot, 9 keys, and audio in.

Make website explaining how to upload new code with rpi + open source toolchain.

Gather parts for hands-on VGA portion.

Finish one complete kit to be able to show what it can do.

Assemble the parts in bags with printouts.

*****

Tommy sent me this awesome shader tutorial series : https://thebookofshaders.com/

Then there is a chapter on Image processing :

  • Textures (https://en.wikipedia.org/wiki/Texture_mapping)
  • Image operations (https://www.cse.unr.edu/~bebis/CS791E/Notes/PointProcess.pdf)
  • Kernel convolutions (https://en.wikipedia.org/wiki/Kernel_(image_processing))
  • Filters
  • Others effects

I think I’m gradually understqnding what FFT is thanks to this text from (https://en.wikipedia.org/wiki/Digital_image_processing) :

“Filtering can be performed by…masking specific frequency regions in the frequency (Fourier) domain”

Also new to me is the idea of modifying a historigram https://en.wikipedia.org/wiki/Histogram_equalization

Also watched a video on Touch Designer and understood what all the hype is about ! Visual programming like with Grasshopper but can do Processing video and image transformations really didactically.

Blender is also more and more powerful and popular of course for mesh and texture editing. It can also do animation / video.

*****

CONWAY’S LAW :

Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure.

OPEN SOURCE primer from Paul:

FSFE https://media.fsfe.org/w/p/9gYSyoEYggsqBExLWjRejL?playlistPosition=4&resume=true

The Fuz hackerspace manifesto : https://fuz.re/

Great teardown videos from the long running EEV blog. Fascinating to see different eras of technology (through hole, SMD, flexible circuits, varying degrees of integration), the easter eggs (signatures of Apple employees on the inside of the metal casing, Amiga computer chips named after girlfriends), the bodges (reusing a film camera body for the first digital SLR, jumper wires, last minute added boards), the unusual and mysterious decisions, the design for multiple versions of a product including multiple boards that are likely to be redesigned at different frequencies, the different ages of boards and parts from inside the same device (by looking at the year and week codes), all the work to shield different things inside, the different between a cheap product that was mass produced and a smaller scale high end product on the inside, the attempts to obfuscate or make it difficult to open something up, the efficient and ingenious solutions vs the cost effective solutions, the typical points of failure (battery leaks, mechanical failure of plugs), varying soldering and board cleanliness quality, the use of plastic films and sheet metal in conjunction with boards, the 3D integration challenge of small portable consumer electronics that integrate moving parts; multiple boards with connectors and optics, serviceability considerations, moment where space was wasted, exotic and rare obsolete technologies (late CRT pocket monitor), some sublime documentation with hand drawn systems diagrams and plenty of information (https://www.youtube.com/watch?v=tQyX3F4ggM8).

la béchamel part trois

 Thoughts on this as a product now that SD card seems possible :

  • I could put “sprites” like the bechamel logo and some numbers in some location in memory.
  • I could be able to save screen shots on the SD card with a button combo.
  • Could there be a kind of database, which would be like how rhino saves files of 3D models one is working on ?
  • Am I making a mini computer (ALU, slow big memory, fast SRAM, user interface, screen output) ? What do I think about a scroll wheel and a mini screen then ?
  • What could I do with a stack of images from the SD card ?

Just a reminder of what a fully fleshed out video system looks like with the Gameduino : https://excamera.com/files/gameduino/synth/doc/gen/poster.pdf

*********

Checking-in with my project goals for 2024.

  • This year the goal was to have a finished open-source project that could be shared with people online or as a kit/product via prepared instruments.com. I would be able to show it to students as an example of a finished design/engineering project that I have completed. The idea would be to stabilize the design, to stop making endless boards, and begin exploring more verilog codes that run on it.

Currently, the Cyber Campus version of the board is basically complete enough to transition into just trying new code. There is just the SD card that remains before the object has a feel of being an independent finished object that isn’t dependent on any other products (like rpi) to function.

Perhaps a distinction between the product, art and workshop kit would be useful :

Kit : reprogrammable, built around the rpi, solderable by beginners, etc.

Product : HDMI IN (a compromise would be that chip that handles everything like the TFP401PZP) and HDMI OUT, plug and play with modern tech. Has a larger FPGA to be able to do more cool stuff.

Art : The Cyber Campus version has everything I need to have fun with new codes, especially ones that interact with time and sequence (like temporal compression?), like differences between images in time impacting the kinds of transformations instead of staying with just static filters.

 

*********

I need to make the SD card code more generalizable with a function that takes CMD IN.

Here is the command format :

For example CMD8, the first byte is 01001000 with : 0 (start bit) + 1 (tx bit) + 010000 (six bits of the CMD number 8 in binary). After this are 32 bits of arguments (such as address you want to select) the 6 bit CRC7 and then 1.

NOTE ** I may need to change the speed from 50KHz to 100KHz or 200KHz for things to work. I am unclear about when I need to send ticks but forum user writes this :


; There must be 8 clocks after
; * a command with no response
; * the response of a command with a response
; * the end of a read data block
; * the CRC status token on a write

Here is the sequence of commands I have so far :

//TICKS
parameter TICKS = 48'b11111111_11111111_11111111_11111111_11111111_11111111;

//CMD0 “GO_IDLE_STATE”
parameter CMD0 = 48'b01000000_00000000_00000000_00000000_00000000_10010101;

//CMD8 “SEND_IF_COND”
parameter CMD8 = 48'b01001000_00000000_00000000_00000001_10101010_10000111;

//CMD55 “APP_CMD”
parameter CMD55 = 48'b01110111_00000000_00000000_00000000_00000000_01100101;

//ACMD41 “SD_SEND_OP_COND”
parameter ACMD41 = 48'b01101001_01000000_00010000_00000000_00000000_11001101;

//CMD2 “ALL_SEND_CID”
parameter CMD2 = 48'b01000010_00000000_00000000_00000000_00000000_01001101;

//CMD3 “SEND_RELATIVE_ADDR”
parameter CMD3 = 48'b01000011_00000000_00000000_00000000_00000000_00100001;

//CMD7 “SELECT/DESELECT_CARD”
parameter CMD7 = 48'b01000111_00000000_00000000_00000000_00000000_CRC+1; // need to generate my own CRC for this

//CMD17 “READ_SINGLE_BLOCK”
parameter CMD17 = 48'b01010001_00000000_00000000_00000000_00000000_CRC+1; 

NOTE ** There is a read multiple block command CMD18 READ_MULTIPLE_BLOCK that takes a 32 bit address and continuously transfers data blocks until interrupted by the STOP_TRANSMISSION command. By default sends 512 bytes followed by a CRC.

I am currently breaking up the code into cycles of 48 clock pulses to send and receive commands in sequence.

counter <= (counter==47) ? 0 : counter+1; // to count the steps in the sending of a single CMD
phase <= (counter==47) ? phase+1: phase; // to count how many CMDs we have sent

Each command is currently sent in order by checking the phase. For example :

if (phase == 8) begin
      rec <= 1; // set CMD to output
      CMD <= ACMD41[counter]; // iterate through the 48 bit command
end

Listening to input is tougher because of my poor understanding of inout, I am using inout to define the CMD pin. If we are receiving it is put in a high Z state.

assign CMD = rec ? CMD : 1'bz;

I am not sure if this will work :

if (phase == 9) begin
      rec <= 0; // set CMD inout pin to high Z
      data_in[counter] <= CMD; //store the data in data_in
end

Here I’m trying to check if a ready bit 31 is still unset :

if ((data_in & (1<<31))==0)

Here I’m trying to compose the CMD7 by concatenating the appropriate bytes.

//CMD7 “SELECT/DESELECT_CARD”
if (phase == 16) begin
   rec <= 1;
   RCA[15:0] = data_in[31:15]; // retrieve the RCA from the previous response
   CMD7[47:0] = {8'b01000111,RCA[15:0],16'b0000000000000000,CRC+1}; // compose CMD7 from parts
   CMD <= CMD7[counter]; // send the command
end

****

I’m on this site to calculate the CRC7 (https://www.ghsi.de/pages/subpages/Online%20CRC%20Calculation/)

  • First I enter the CRC polynomial which for the SD card is x7 +x3 + 1
  • Then I translate the 5 bytes I want to send into hex
  • The 7 bit value generated at the end of the page is then added to the end of the message plus the end bit (1)

I need to figure out how to incorporate the CRC in verilog for certain commands however :

The above website automatically generates this verilog code that takes one bit at a time :

// ==========================================================================
// CRC Generation Unit - Linear Feedback Shift Register implementation
// (c) Kay Gorontzi, GHSi.de, distributed under the terms of LGPL
// ==========================================================================
module CRC_Unit(BITVAL, BITSTRB, CLEAR, CRC);
input BITVAL; // Next input bit
input BITSTRB; // Current bit valid (Clock)
input CLEAR; // Init CRC value
output [6:0] CRC; // Current output CRC value

reg [6:0] CRC; // We need output registers
wire inv;

assign inv = BITVAL ^ CRC[6]; // XOR required?

always @(posedge BITSTRB or posedge CLEAR) begin
if (CLEAR) begin
CRC = 0; // Init before calculation
end
else begin
CRC[6] = CRC[5];
CRC[5] = CRC[4];
CRC[4] = CRC[3];
CRC[3] = CRC[2] ^ inv;
CRC[2] = CRC[1];
CRC[1] = CRC[0];
CRC[0] = inv;
end
end

endmodule

I am not sure exactly how to feed this module one bit at a time. I also feel unsure about how to properly incorporate modules from different files.

****

Here is my test code (once the timescale is uncommented) that I’m hoping will not require CRC (I’ll just look on the oscilloscope to see what the RCA is and then use that). It is the simplest possible setup.

Possible things that need changing :

  • I am not waiting to see if bit 31 is set after sending ACMD41, so I probably will need to add a delay here ? *EDIT* I am getting a busy bit 31 the first time I ask at the moment.
  • 8 clock ticks instead of 48 ? *EDIT* DONE
  • No clock ticks after certain commands?
  • The RCA (and CRC) if it is not 0001
  • The block address (and CRC) if it is not 0
//-----------------------------------------------------------------
// - Attempt at SD 1 bit communication -
//CMD0 “GO_IDLE_STATE”
//CMD8 “SEND_IF_COND”
//CMD55 “APP_CMD”, ACMD41 “SD_SEND_OP_COND” with argument 40100000 (HCS=1, voltage = 3.3).
//The R3 response has HCS and the supported voltages set. Repeat until bit 31 of the response is 1.
//CMD2 “ALL_SEND_CID” with argument 00000000. The R2 response has the CID. I ignore it.
//CMD3 “SEND_RELATIVE_ADDR” with argument 00000000. The R6 response contains the RCA and some status that I ignore.

//The card is now in the stand-by state. Reading a block is accomplished by
//CMD7 “SELECT/DESELECT_CARD” with argument bits 31-16 = the RCA you were given, 15-0 = 0. The R1b response I ignore.
//CMD17 “READ_SINGLE_BLOCK” with argument = the block address.
//The R1 response I ignore, but there will also be the block data.
//Keep toggling the clock, and you’ll eventually see a start bit followed by a block of data and a CRC.
//-----------------------------------------------------------------

`default_nettype none // disable implicit definitions by Verilog
//`timescale 1us/1ns

module top(
input wire clk100,
input wire reset,
//input reg DAT,
output reg SCK,
output reg CMD
);

//CMD0 “GO_IDLE_STATE”
parameter CMD0 = 48'b01000000_00000000_00000000_00000000_00000000_10010101;

//CMD8 “SEND_IF_COND”
parameter CMD8 = 48'b01001000_00000000_00000000_00000001_10101010_10000111;

//CMD55 “APP_CMD”
parameter CMD55 = 48'b01110111_00000000_00000000_00000000_00000000_01100101;

//ACMD41 “SD_SEND_OP_COND”
parameter ACMD41 = 48'b01101001_01000000_00010000_00000000_00000000_11001101;

//CMD2 “ALL_SEND_CID”
parameter CMD2 = 48'b01000010_00000000_00000000_00000000_00000000_01001101;

//CMD3 “SEND_RELATIVE_ADDR”
parameter CMD3 = 48'b01000011_00000000_00000000_00000000_00000000_00100001;

//CMD7 “SELECT/DESELECT_CARD”
parameter CMD7 = 48'b01000111_00000000_00000001_00000000_00000000_11011101; // ASSUMING RCA IS 0001

//CMD17 “READ_SINGLE_BLOCK”
parameter CMD17 = 48'b01010001_00000000_00000000_00000000_00000000_01010101; // ASSUMING ADDR ZERO EXISTS

//CMD18 “READ_MULTIPLE_BLOCK”
parameter CMD18 = 48'b01010010_00000000_00000000_00000000_00000000_11100001; // ASSUMING ADDR ZERO EXISTS


//some counters
reg [9:0] counter=0;
reg [9:0] phase=0;
reg [7:0] q=0;


// divide 100MHz clock by 255 to equal 196.0784315 KHz which is in the 100KHz - 400KHz range

always @(posedge clk100) begin

q <= q+1;
if(q == 0) begin
SCK <= ~SCK;
end
end

//to count 48 SCK cycles and also how many which step of the communication sequence we're on


always @(posedge SCK) begin


//send 8 ticks
if (phase == 0) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end
end

//send CMD0

if (phase == 1) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD0[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end


//send 8 clock ticks while CMD high

if (phase == 2) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//send CMD8


if (phase == 3) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD8[47-counter];
end
else begin
counter<=0;
phase<=phase+1;
end


end


//set CMD to input

if (phase == 4) begin


counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end


end



//NEED THIS ? send 8 clock ticks while CMD high

if (phase == 5) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//send CMD55


if (phase == 6) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD55[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end



end

//listen to response

if (phase == 7) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end


end


//NEED THIS ? send 8 clock ticks while CMD high

if (phase == 8) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//send ACMD41


if (phase == 9) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= ACMD41[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end

//listen to response and check bit 31 is 1

if (phase == 10) begin


counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end


//send CMD2

if (phase == 11) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD2[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end


end


//listen to 136 bit CID CMD from SD

if (phase == 12) begin

counter <= counter+1;

if (counter <= 135) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end


//send CMD3

if (phase == 13) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD3[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end

if (phase == 14) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//CMD7 “SELECT/DESELECT_CARD”
if (phase == 15) begin


counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD7[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end

//listen to CMD from SD

if (phase == 16) begin


counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//CMD17 “READ_SINGLE_BLOCK” with argument = the block address.

if (phase == 17) begin



counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD17[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end

if (phase == 18) begin



counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end

// Should start to see data coming in on the DAT pin !!
if (phase >= 19) begin

// DAT pin

end


end

endmodule

The sequence for ModelSim was :

  • Open ModelSim, reselect the licence.dat file
  • Open a “new project”
  • Click Add Existing Files (pick only the test bench verilog file) and then Close
  • Now Compilation > Compile All
  • Now Simulation > Start Simulation
  • In the console type <<view wave>>…
  • …then <<add wave * >>… (note the space between wave and *)
  • I right clicked on the SCK signal and selected Clock and then set the speed.
  • …and a time period for example : <<run 10000ms>>.

 

I should have been able to see that the bits were the wrong order and that the clock ticks were with CMD low instead of HIGH here !!

 

*****

So I have gotten as far as CMD0 -> CMD8 -> CMD55 -> ACM41

At this point the SD card returns a busy bit 31, and when I ask again and again it eventually tries to talk while I am controlling the CMD pin and communications break down. Reading the documentation it looks like I can just keep pulsing the clock between 100-400KHz and delaying before checking the busy bit again (so getting rid of the 5x checks I do back to back and adding a seriously long delay could be the answer). This is the next step !

Here is the code so far :

//-----------------------------------------------------------------
// - Attempt at SD 1 bit communication -
//CMD0 “GO_IDLE_STATE”
//CMD8 “SEND_IF_COND”
//CMD55 “APP_CMD”, ACMD41 “SD_SEND_OP_COND” with argument 40100000 (HCS=1, voltage = 3.3).
//The R3 response has HCS and the supported voltages set. Repeat until bit 31 of the response is 1.
//CMD2 “ALL_SEND_CID” with argument 00000000. The R2 response has the CID. I ignore it.
//CMD3 “SEND_RELATIVE_ADDR” with argument 00000000. The R6 response contains the RCA and some status that I ignore.

//The card is now in the stand-by state. Reading a block is accomplished by
//CMD7 “SELECT/DESELECT_CARD” with argument bits 31-16 = the RCA you were given, 15-0 = 0. The R1b response I ignore.
//CMD17 “READ_SINGLE_BLOCK” with argument = the block address.
//The R1 response I ignore, but there will also be the block data.
//Keep toggling the clock, and you’ll eventually see a start bit followed by a block of data and a CRC.
//-----------------------------------------------------------------

`default_nettype none // disable implicit definitions by Verilog
//`timescale 1us/1ns

module top(
input wire clk100,
input wire reset,
//input reg DAT,
output reg SCK,
output reg CMD
);

//CMD0 “GO_IDLE_STATE”
parameter CMD0 = 48'b01000000_00000000_00000000_00000000_00000000_10010101;

//CMD8 “SEND_IF_COND”
parameter CMD8 = 48'b01001000_00000000_00000000_00000001_10101010_10000111;

//CMD55 “APP_CMD”
parameter CMD55 = 48'b01110111_00000000_00000000_00000000_00000000_01100101;

//ACMD41 “SD_SEND_OP_COND”
parameter ACMD41 = 48'b01101001_01000000_00010000_00000000_00000000_11001101;

//CMD2 “ALL_SEND_CID”
parameter CMD2 = 48'b01000010_00000000_00000000_00000000_00000000_01001101;

//CMD3 “SEND_RELATIVE_ADDR”
parameter CMD3 = 48'b01000011_00000000_00000000_00000000_00000000_00100001;

//CMD7 “SELECT/DESELECT_CARD”
parameter CMD7 = 48'b01000111_00000000_00000001_00000000_00000000_11011101; // ASSUMING RCA IS 0001

//CMD17 “READ_SINGLE_BLOCK”
parameter CMD17 = 48'b01010001_00000000_00000000_00000000_00000000_01010101; // ASSUMING ADDR ZERO EXISTS

//CMD18 “READ_MULTIPLE_BLOCK”
parameter CMD18 = 48'b01010010_00000000_00000000_00000000_00000000_11100001; // ASSUMING ADDR ZERO EXISTS


//some counters
reg [9:0] counter=0;
reg [9:0] phase=0;
reg [7:0] q=0;


// divide 100MHz clock by 255 to equal 196.0784315 KHz which is in the 100KHz - 400KHz range

always @(posedge clk100) begin

q <= q+1;
if(q == 0) begin
SCK <= ~SCK;
end
end

//to count 48 SCK cycles and also how many which step of the communication sequence we're on


always @(posedge SCK) begin


//send 8 ticks
if (phase == 0) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end
end

//send CMD0

if (phase == 1) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD0[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end


//send 8 clock ticks while CMD high

if (phase == 2) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//send CMD8


if (phase == 3) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD8[47-counter];
end
else begin
counter<=0;
phase<=phase+1;
end


end


//set CMD to input

if (phase == 4) begin


counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end


end



//NEED THIS ? send 8 clock ticks while CMD high

if (phase == 5) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//send CMD55


if (phase == 6) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD55[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end



end

//listen to response

if (phase == 7) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end


end


//NEED THIS ? send 8 clock ticks while CMD high

if (phase == 8) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//send ACMD41


if (phase == 9) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= ACMD41[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end

//listen to response and check bit 31 is 1

if (phase == 10) begin


counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end


// START OF SEND CMD55 + ACMD41 again (X2)


//START OF CONTINUOUS CLOCK BEFORE POLLING AGAIN


//END OF CONTINUOUS CLOCK BEFORE POLLING AGAIN

//


//NEED THIS ? send 8 clock ticks while CMD high

if (phase == 11) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end


//send CMD55
if (phase == 12) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD55[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end



end

//listen to response

if (phase == 13) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end


end


//NEED THIS ? send 8 clock ticks while CMD high

if (phase == 14) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//send ACMD41


if (phase == 15) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= ACMD41[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end

//listen to response and check bit 31 is 1

if (phase == 16) begin


counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end

// END OF SEND CMD55 + ACMD41 again (x2)

// START OF SEND CMD55 + ACMD41 again (x3)


//NEED THIS ? send 8 clock ticks while CMD high

if (phase == 17) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end


//send CMD55
if (phase == 18) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD55[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end



end

//listen to response

if (phase == 19) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end


end


//NEED THIS ? send 8 clock ticks while CMD high

if (phase == 20) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//send ACMD41


if (phase == 21) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= ACMD41[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end

//listen to response and check bit 31 is 1

if (phase == 22) begin


counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end

// END OF SEND CMD55 + ACMD41 again (x3)


// START OF SEND CMD55 + ACMD41 again (x4)

//NEED THIS ? send 8 clock ticks while CMD high

if (phase == 23) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end


//send CMD55
if (phase == 24) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD55[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end



end

//listen to response

if (phase == 25) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end


end


//NEED THIS ? send 8 clock ticks while CMD high

if (phase == 26) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//send ACMD41


if (phase == 27) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= ACMD41[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end

//listen to response and check bit 31 is 1

if (phase == 28) begin


counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end


// END OF SEND CMD55 + ACMD41 again (x4)

// START OF SEND CMD55 + ACMD41 again (x5)

//NEED THIS ? send 8 clock ticks while CMD high

if (phase == 29) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end


//send CMD55
if (phase == 30) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD55[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end



end

//listen to response

if (phase == 31) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end


end


//NEED THIS ? send 8 clock ticks while CMD high

if (phase == 32) begin

counter <= counter+1;

if (counter <= 7) begin
CMD <= 1'b1;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//send ACMD41


if (phase == 33) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= ACMD41[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end

//listen to response and check bit 31 is 1

if (phase == 34) begin


counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end


// END OF SEND CMD55 + ACMD41 again (x5)


//ADD TICKS HERE ??????????

//send CMD2

if (phase == 35) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD2[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end


end


//listen to 136 bit CID CMD from SD

if (phase == 36) begin

counter <= counter+1;

if (counter <= 135) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end


//send CMD3

if (phase == 37) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD3[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end

if (phase == 38) begin

counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//CMD7 “SELECT/DESELECT_CARD”
if (phase == 39) begin


counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD7[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end

//listen to CMD from SD

if (phase == 40) begin


counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end

//CMD17 “READ_SINGLE_BLOCK” with argument = the block address.

if (phase == 41) begin



counter <= counter+1;

if (counter <= 47) begin
CMD <= CMD17[47-counter];
end
else begin
counter<=0;
phase<=phase+1;

end

end

if (phase == 42) begin



counter <= counter+1;

if (counter <= 47) begin
CMD <= 1'bz;
end
else begin
counter<=0;
phase<=phase+1;

end

end

// Should start to see data coming in on the DAT pin !!
if (phase >= 43) begin

// DAT pin

end


end

endmodule

Here is what it looks like on the scope with just one exchange :

I sent CMD8 “SEND_IF_COND” : 01001000_00000000_00000000_00000001_10101010_10000111

Received this R7 response :             00001000_00000000_00000000_00000001_10101010_00010011

…and a series of back and forth commands :

….

To go further I would need to switch the logic analyser.

*UPDATE*

I think that I need to listen to what I am getting back from the SD card and using the first zero as a sign that the response message has begun. Once I get the first zero I need to start counting from that point on. This allows wiggle room if the SD card doesn’t respond instantly to my message. To do this I would need to use a proper inout pin for CMD and not just send high Z while waiting for messages.

*****

Feedback from Kristy on the prototype :

  • Mini Screen is essential for aesthetic world, transported into that world. No interface with modern world. Part of movement of analog disconnected products. Not ticktock. In contrast to the video editing reel gem z. And ability to plug into projector. 
  • It’s a tool for Artists and music videos makers but also a business card for a Luxury synth audience would pay a lot for customizable 
  • 3D printed case is practical and cool 
  • Keys put it in the 90s, signal that it is not too sophisticated but is capable.
  • Purity thing is OK, don’t make it entirely software

Doing the demo, I realized some things :

  • There are still so many cables…
  • It would be cool to be able to change which bits are entering the device from the rpi.

Looking into some single board retro computers out there  :

Motorola MEK6800D2 – Time-Line Computer Archive

 

The READY! Computer Model 100 Blends '80s and Modern Aesthetics to Expand Your Single-Board Computer - Hackster.io

A new project utilises the Raspberry Pi as a portable mobile ...

On second thought, it makes the device double the size, quite heavy and there is the problem of how to mount the screen or disconnect/re-lock it in place. Perhaps I should just get a mini battery powered hdmi screen that it can plug in to ? I could even have a cool twirly wire going to it from the device. A usb battery bank also seems like a smart way to avoid having to do all that power management and boosting with batteries on the board.

****

Took some nice photos of recent synths :

 

****

Finally changed Notepad++ interface to midnight and set it to interpret verilog. Fewer errors due to begin…end mismatches and mispellings. Also easier on the eyes !

****

Tough day at the lab !

SPI has fewer steps overall, better docs but SPI requires doing thing during positive and negative edge of clock. So you need to do things every time there is a change in the state of the SCLK, so a clock that has a positive edge 2x faster than SCLK. You need two registers to tell us if we’re in rising or falling clock. (It appears that you can’t use both posedge and negedge in a single verilog even though it will work in a testbench.) This resource, and the others it links to, is still the best I can find : http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf

I’m working in test bench for the SPI code, and I can confirm the SCLK and toggle detection signals. For some reason I’m getting stuck with other always block and I can’t work out why at the moment. For some reason when I move counter and phase into the first block, both blocks seem to work, it feels a bit buggy.

Here is my code so far :

/*

From http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf
and http://elm-chan.org/docs/mmc/mmc_e.html
and the flow chart http://elm-chan.org/docs/mmc/m/sdinit.png

The master IC and the slave IC are tied with three signal lines:

SCLK (Serial Clock),
MISO (Master-In Slave-Out) and
MOSI (Master-Out Slave-In).

The contents of both 8-bit shift registers are exchanged with the shift clock driven by master IC. An
additional fourth signal:

SS (Slave Select),

is utilized to synchronize the start of packet or byte boundary
and to facilitate working with multiple slave devices simultaneously.

The CS signal must be driven high to low prior to send a command frame
and held it low during the transaction (command, response and data transfer if exist).


In SPI, data shift and data latch are done on opposite clock edges.

We're in Mode 0 :
Positive Pulse.
Latch, then Shift.
(CPHA=0, CPOL=0)

This means that data can only change state when the clock is transitioning from HIGH to LOW (the NEG edge).
These new values are then recorded on the positive edge.

This means that sending data (MOSI) is syncronized with the neg edge, but recording data in (MISO) is syncronized with the pos edge.

(bit 47) (bit 0)
01 + CMD number (6 bits) + Argument (32 bits) + CRC (7 bits) + 1
(MSB) (LSB)

To communicate with the SD card, your program has to place the SD card into the SPI mode. To do this, set
the MOSI and CS lines to logic value 1 and toggle SD CLK for at least 74 cycles.


*/

`timescale 1us/1ns

module top(
input wire clk100,
input wire reset,

input reg MISO,

output wire SCLK,
output reg MOSI,
output reg CS
);

//used to detect rising or falling edges
reg rising_edge = 0;
reg falling_edge = 0;

wire clk_change=0;

// counters
reg [7:0] q=0;
reg [9:0] counter=0;
reg [7:0] phase=0;
reg [9:0] wait_counter=0;

// to hold incoming message
reg [7:0] response = 0;

//CMD0 “GO_IDLE_STATE”
parameter CMD0 = 48'b01000000_00000000_00000000_00000000_00000000_10010101;

//CMD8 “SEND_IF_COND”
parameter CMD8 = 48'b01001000_00000000_00000000_00000001_10101010_10000111;

//CMD55 “APP_CMD”
parameter CMD55 = 48'b01110111_00000000_00000000_00000000_00000000_01100101;

//ACMD41 “SD_SEND_OP_COND”
parameter ACMD41 = 48'b01101001_01000000_00010000_00000000_00000000_11001101;

//CMD2 “ALL_SEND_CID”
parameter CMD2 = 48'b01000010_00000000_00000000_00000000_00000000_01001101;

//CMD3 “SEND_RELATIVE_ADDR”
parameter CMD3 = 48'b01000011_00000000_00000000_00000000_00000000_00100001;

//CMD7 “SELECT/DESELECT_CARD”
parameter CMD7 = 48'b01000111_00000000_00000001_00000000_00000000_11011101; // ASSUMING RCA IS 0001

//CMD17 “READ_SINGLE_BLOCK”
parameter CMD17 = 48'b01010001_00000000_00000000_00000000_00000000_01010101; // ASSUMING ADDR ZERO EXISTS

//CMD18 “READ_MULTIPLE_BLOCK”
parameter CMD18 = 48'b01010010_00000000_00000000_00000000_00000000_11100001; // ASSUMING ADDR ZERO EXISTS

assign SCLK = rising_edge;// toggle SCLK every 256 clock100 cycles

assign clk_change = (q[7] == 0) ? 1 : 0;
// divide 100MHz clock by 256 which is in the 100KHz - 400KHz range

always @(posedge clk100) begin

q <= q+1;

if(q[7] == 0) begin
// every 127 clock100 cycles detect which edge we're currently on
rising_edge <= 1;
falling_edge <= 0;
end
else begin
rising_edge <= 0;
falling_edge <= 1;
end
end

always @(posedge clk_change) begin // positive edge every transition of SCLK


counter <= counter+1; // WHEN I MOVE THIS TO THE FIRST BLOCK IT WORKS BUT NOT HERE FOR SOME REASON
if (counter <= 400) begin
counter<=counter;
end
else begin
counter<=0;
phase<=phase+1;
end

// Place SD in SPI Mode
if ((phase == 1) && (falling_edge==1)) begin

counter <= counter+1;

if (counter <= 75) begin
CS <= 1'b1; // DESELECT
MOSI <= 1'b1;

end
else begin
counter<=0;
phase<=phase+1;
end
end

// CMD0
if ((phase == 2) && (falling_edge==1)) begin

counter <= counter+1;
CS <= 1'b0; // SELECT

if (counter <= 47) begin
MOSI <= CMD0[counter];
end
else begin
counter<=0;
phase<=phase+1;
end
end

// Listen to MISO, we want to receive: (00000001)2
// but it takes a few clock cycles to get a message,
// so wait max 16 cycles until receive a zero on MISO
// we have to send high on MOSI during this time

if ((phase == 3) && (rising_edge==1)) begin

CS <= 1'b0; // SELECT

if (MISO == 0 || counter > 0) begin
counter <= counter+1;

if (counter <= 7) begin
response[counter] <= MISO;
MOSI<=1;
end
else begin
counter<=0;
phase<=phase+1;
end
end

else begin
//after 16 clock cycles with no response we try again
wait_counter <= wait_counter+1;

if (wait_counter >= 15) begin
CS <= 1'b1; // DESELECT
phase<=0;
end

end
end


// CMD8
if ((phase == 4) && (falling_edge==1)) begin

counter <= counter+1;
CS <= 1'b1; // DESELECT

if (response == 7'b00000001) begin

if (counter <= 47) begin
CS <= 1'b0; // SELECT
MOSI <= CMD8[counter];
end
else begin
counter<=0;
phase<=phase+1;
end
end
end

end

endmodule

****

I am doing more research and also am listening to the Arduino communicate with an SD card on the ‘scope. I can’t find my logic analyzer but I have increased the memory of a trigger recording on the ‘scope so I can see a fair amount of the conversation.

We have :

  1. Wait
  2. Place SD in SPI mode
  3. CMD0
  4. CMD8
  5. CMD55
  6. ACMD41

then repeating CMD55 + ACMD41 until the end of the recording.

 

This image (from http://elm-chan.org/docs/mmc/mmc_e.html#spimode) helps explain my earlier mistakes :

cmd frame

  • Preliminary stuff : The card is ready to receive a command frame when it drives DO high (from http://elm-chan.org/docs/mmc/mmc_e.html#spimode).
  • The NCR is a response time which varies between 0 and 8 clock cycles, but it can take longer than this to respond! Because of this, I have to DETECT the first 0 from the SD card (WHILE of course keeping DI HIGH OxFF), then COUNT 8 bits (for an R1 response), then INTERPRET the message.
  • The CS signal must be driven high to low prior to send a command frame and held it low during the transaction (command, response and data transfer if exist) (from http://elm-chan.org/docs/mmc/mmc_e.html#spimode).

From wikipedia (https://en.wikipedia.org/wiki/Serial_Peripheral_Interface) explaining the pseudo code of bit-banging SPI as master :

  • Initialize SCLK as low and CS as high
  • Pull CS low to select the sub
  • Loop for however many number of bytes to transfer:[note 9]
    • Initializebyte_outwith the next output byte to transmit
    • Loop 8 times:
      • Left-Shift[note 10] the next output bit frombyte_outto MOSI
      • NOP for the sub’s setup time
      • Pull SCLK high
      • Left-Shift the next input bit from MISO intobyte_in
      • NOP for the sub’s hold time
      • Pull SCLK low
    • byte_innow contains that recently-received byte and can be used as desired
  • Pull CS high to unselect the sub

I think I should build a shift register, put it in a module, and use it like a function. Just did the exercise on hdlbits :

module top_module(
input clk,
input areset,
input load,
input ena,
input [3:0] data,
output reg [3:0] q);

// Asynchronous reset: Notice the sensitivity list.
// The shift register has four modes:
// reset
// load
// enable shift
// idle -- preserve q (i.e., DFFs)
always @(posedge clk, posedge areset) begin
      if (areset) // reset
          q <= 0;
      else if (load) // load
          q <= data;
      else if (ena) // shift is enabled
         q <= q[3:1]; // Use vector part select to express a shift.
end

endmodule

****

Got a logic analyzer now that decodes SPI, changes everything !

 

 

I was able to decode the conversation and it took more than 125ms for the SD to send a ready bit after the CMD55+ACMD41 requests. After the response, CMD58 was sent and with R1 response as 00 C0 FF 80 00. After this the CS was set HIGH and the clock speed was ramped way up (because it read the TRAN_SPEED field in the CSD register?). Then  CMD17 for READ_SINGLE_BLOCK was sent, there was a response then a delay before some data was sent on MISO. (At this point I could send CMD18 for READ_MULTIPLE_BLOCK instead and be receiving data continuously ?)

Some thoughts :

  1. If I were to make a product with an SD card I would need to ship it with a specific SD card type, as my code wouldn’t be flexible enough to adapt to different kinds of cards, find best speeds etc. I would need to use a proper SD card library to run on the FPGA, which would require me learning how to do that and would require some space on the FPGA and probably a larger FPGA as a result.
  2. It I had a microchip to handle the interaction with the SD card I think it would be a small compromise but would make life considerably easier for me.
  3. I had previously imagined that the majority of the data being sent from the SD card could represent images and that this could just be channeled directly to a video out. I don’t think this is very realistic, most of the data is waiting for things to be ready, sending info before and after the actual data which represents a small amount of things. Basically there is a step after which is digesting the received data and repackaging it to the FPGA I would say.
  4. I could prepare this step by doing some tests getting Arduino to send image data from the SD card to the FPGA to see how hard this is for me.

****

I also am taking a new approach, use the SPI_master library from NANDLAND (https://github.com/nandland/spi-master/blob/master/Verilog/source/SPI_Master.v)

I’ve started a code which compiles (!) and which references the SPI_Master.v file with this :

module top(

input wire clk100,
input wire reset,
input wire MISO,
output wire SCLK,
output wire MOSI,
output reg CS

);

reg [7:0] TX_Byte=0; // Byte to transmit on MOSI
reg TX_DV=0; // Data Valid Pulse with i_TX_Byte
wire TX_Ready=0; // Transmit Ready for next byte
wire RX_DV=0; // Data Valid pulse (1 clock cycle)
wire [7:0] RX_Byte=0; // Byte received on MISO

....

SPI_Master (

.i_Rst_L(reset),
.i_Clk(clk100),
.i_TX_Byte(TX_Byte), // Byte to transmit on MOSI
.i_TX_DV(TX_DV), // Data Valid Pulse with i_TX_Byte
.o_TX_Ready(TX_Ready), // Transmit Ready for next byte
.o_RX_DV(RX_DV), // Data Valid pulse (1 clock cycle)
.o_RX_Byte(RX_Byte), // Byte received on MISO
.o_SPI_Clk(SCLK),
.i_SPI_MISO(MISO),
.o_SPI_MOSI(MOSI)

);

endmodule //module top

I can’t figure out how to make a testbench with it yet.

For the main body of the code I think I will need to have some kind of way of keeping track of which phase of the SPI transaction we’re on like before.
Also found a super post that explains initializing the card into SPI mode here  succinctly :
https://electronics.stackexchange.com/questions/77417/what-is-the-correct-command-sequence-for-microsd-card-initialization-in-spi

CMD0 arg: 0x0, CRC: 0x95 (response: 0x01) – note that in case of 0xFF or garbled response you should simply repeat this step; see below for more info.

CMD8 arg: 0x000001AA, CRC: 0x87 (response: 0x01, followed by echo of arg, in this case 0x000001AA) – while it may seem that this command is optional, it’s completely mandatory for newer cards. While 0x1AA is a common arg value here, you may actually pass other values as well; see “Table 7-5: Card Operation for CMD8 in SPI Mode”, p. 108 in spec for details.

3a. CMD55 arg: 0x0, CRC: any, 0x65 actually (response: 0x01; CMD55 being the prefix to every ACMD; if the response is 0x05, you’ve got an old card – repeat CMD1 with arg 0x0 [CRC 0xF9] instead of CMD55/ACMD41)

3b. ACMD41 , arg: 0x40000000, CRC: any, 0x77 actually (note that this argument assumes the card is a HCS one, which is usually the case; use 0x0 arg [CRC 0xE5] for older cards). If response is 0x0, you’re OK; if it’s 0x01, goto 3a; if it’s 0x05, see note on it above (in 3a.); if it’s neither, something’s wrong with it (also see below).

****

Cool HDMI wires that make the synth + screen separation a plus (https://kondorblue.com/products/12-to-24-coiled-braided-hdmi-cable-for-camera-mounted-monitors) :

Kondor Blue Coiled Right-Angle High-Speed HDMI Cable (12 to 24 ...

The footprint for the rpi is mirrored on this board so the header will need to be soldered on reverse.
I spent a wire soldering tiny flexible wires to different pins on the SD card. I should have a debug header for SD next board.
Got a basic HDMI bit pattern code that takes buttons as inputs (You also need tempo.v, LFSR.v, sine_wave_gen.v from my github https://github.com/merlinmarrs/iCE40HX-verilog-video-patterns):
`default_nettype none // disable implicit definitions by Verilog
//-----------------------------------------------------------------
// minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
//
// Author: Mike Field <hamster@snap.net.nz>
//
// DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
// value is mapped to one or two 10-bit symbols, depending on how
// many 1s or 0s have been sent. This makes it a DC balanced protocol,
// as a correctly implemented stream will have (almost) an equal
// number of 1s and 0s.
//
// Because of this implementation quite complex. By restricting the
// symbols to a subset of eight symbols, all of which having have
// five ones (and therefore five zeros) this complexity drops away
// leaving a simple implementation. Combined with a DDR register to
// send the symbols the complexity is kept very low.
//-----------------------------------------------------------------

module top(
clk100, key, hdmi_p, hdmi_n, reset
);

input reset;
input clk100;
input [8:0] key;
output [3:0] hdmi_p;
output [3:0] hdmi_n;

// to store values from other modules
wire half_sec;

wire [12:0] random_number_0;
wire [12:0] random_number_1;
wire [12:0] random_number_2;
wire [12:0] random_number_3;
wire [12:0] random_number_4;
wire [12:0] random_number_5;
wire [12:0] random_number_6;
wire [12:0] random_number_7;
wire [12:0] random_number_8;
wire [12:0] random_number_9;
wire [7:0] sine_wave;

// for our bittiness
wire [9:0] bit_or;
assign bit_or = hc[9:0]|vc[9:0];

wire [9:0] bit_xor;
assign bit_xor = hc[9:0]^vc[9:0];

wire [9:0] bit_and;
assign bit_and = hc[9:0]&vc[9:0];

wire [9:0] bit_nand;
assign bit_nand = hc[9:0]& ~vc[9:0];

wire [9:0] bit_xnor;
assign bit_xnor = hc[9:0]^ ~vc[9:0];

reg [9:0] out_bit ;


// For holding the outward bound TMDS symbols in the slow and fast domain
reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
reg [9:0] clk_high_speed;

reg [1:0] c2_output_bits;
reg [1:0] c1_output_bits;
reg [1:0] c0_output_bits;
reg [1:0] clk_output_bits;

wire clk_x5;
reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain

wire vsync, hsync;
wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
assign syncs = {vsync, hsync};

// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 525; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch (96 + 48)
parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
parameter vbp = 35; // end of vertical back porch (2 + 33)
parameter vfp = 515; // beginning of vertical front porch (525 - 10)

// registers for storing the horizontal & vertical counters
reg [9:0] vc;
reg [9:0] hc;
// generate sync pulses (active high)
assign vsync = (vc < vpulse);
assign hsync = (hc < hpulse);

always @(posedge clk_x5) begin
//-------------------------------------------------------------
// Now take the 10-bit words and take it into the high-speed
// clock domain once every five cycles.
//
// Then send out two bits every clock cycle using DDR output
// registers.
//-------------------------------------------------------------
c0_output_bits <= c0_high_speed[1:0];
c1_output_bits <= c1_high_speed[1:0];
c2_output_bits <= c2_high_speed[1:0];
clk_output_bits <= clk_high_speed[1:0];
if (latch_high_speed[2]) begin // pixel clock 25MHz
c0_high_speed <= c0_symbol;
c1_high_speed <= c1_symbol;
c2_high_speed <= c2_symbol;
clk_high_speed <= 10'b0000011111;
latch_high_speed <= 3'b000;
if (hc < hpixels)
hc <= hc + 1;
else
begin
hc <= 0;
if (vc < vlines)
vc <= vc + 1;
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

always @(*) // display 100% saturation colourbars
begin
// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------
// display white bar

if (hc >= hbp && hc < hfp && (bit_xor[8:0] == key[8:0]))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display black bar
else if (hc >= hbp && hc < hfp)
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b0111110000; // blue
end
// we're outside active horizontal range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end
// we're outside active vertical range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end

// red N
defparam hdmin2.PIN_TYPE = 6'b010000;
defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin2 (
.PACKAGE_PIN (hdmi_n[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c2_output_bits[1]),
.D_OUT_1 (~c2_output_bits[0])
);

// red P
defparam hdmip2.PIN_TYPE = 6'b010000;
defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip2 (
.PACKAGE_PIN (hdmi_p[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c2_output_bits[1]),
.D_OUT_1 (c2_output_bits[0])
);

// green N
defparam hdmin1.PIN_TYPE = 6'b010000;
defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin1 (
.PACKAGE_PIN (hdmi_n[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c1_output_bits[1]),
.D_OUT_1 (~c1_output_bits[0])
);

// green P
defparam hdmip1.PIN_TYPE = 6'b010000;
defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip1 (
.PACKAGE_PIN (hdmi_p[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c1_output_bits[1]),
.D_OUT_1 (c1_output_bits[0])
);


// blue N
defparam hdmin0.PIN_TYPE = 6'b010000;
defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin0 (
.PACKAGE_PIN (hdmi_n[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c0_output_bits[1]),
.D_OUT_1 (~c0_output_bits[0])
);

// blue P
defparam hdmip0.PIN_TYPE = 6'b010000;
defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip0 (
.PACKAGE_PIN (hdmi_p[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c0_output_bits[1]),
.D_OUT_1 (c0_output_bits[0])
);

// clock N
defparam hdmin3.PIN_TYPE = 6'b010000;
defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin3 (
.PACKAGE_PIN (hdmi_n[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~clk_output_bits[1]),
.D_OUT_1 (~clk_output_bits[0])
);


// clock P
defparam hdmip3.PIN_TYPE = 6'b010000;
defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip3 (
.PACKAGE_PIN (hdmi_p[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (clk_output_bits[1]),
.D_OUT_1 (clk_output_bits[0])
);
// D_OUT_0 and D_OUT_1 swapped?
// https://github.com/YosysHQ/yosys/issues/330


SB_PLL40_PAD #(
.FEEDBACK_PATH ("SIMPLE"),
.DIVR (4'b0000),
.DIVF (7'b0001001),
.DIVQ (3'b011),
.FILTER_RANGE (3'b101)
) uut (
.RESETB (1'b1),
.BYPASS (1'b0),
.PACKAGEPIN (clk100),
.PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
);

LFSR random_s(
.clock(clk100),
.reset(reset),
.half_sec_pulse(half_sec),
.rnd_0(random_number_0),
.rnd_1(random_number_1),
.rnd_2(random_number_2),
.rnd_3(random_number_3),
.rnd_4(random_number_4),
.rnd_5(random_number_5),
.rnd_6(random_number_6),
.rnd_7(random_number_7),
.rnd_8(random_number_8),
.rnd_9(random_number_9)
);

sine_wave_gen sine_wave_s(
.clk(clk100),
.data_out(sine_wave)
);

tempo tempo_s(
.clk(clk100),
.half_sec_pulse(half_sec)
);


endmodule

 

*****

Nothing is easy today. Put the FPGA in pass-though mode taking rpi color info in and outputing HDMI and nothing seems to be working.

  • the rpi on the CC board should be powered by 5V and not 3.3V.
  • I put the mirror of the typical rpi header situation on the board, which requires the header to be put under the board…
  • the rpi seems to pull too much power to be plugged in to the FPGA board. I should see if this is still the case after powering directly from the 5V from USB instead of the 5V post PFET.
  • Perhaps 125MHz is so fast that sampling the slower incoming video from the rpi was never going to work without using the H and V sync. I was expecting a badly distorted image but what I’m seeing is just thick white bands. Next step would be to take in rpi H, V and pixel clock, and to use these to display images.

*EDIT* I’ve confirmed that the rpi is super power hungry and cannot be powered by the same power supply as the FPGA, even if connected before the fuse. What works currently is me plugging it in seperately and then just jumping the necessary wires to the board.

****

For these (https://www.reddit.com/r/synthesizers/comments/1626o2k/recommend_some_hardware_video_synthesizers/?share_id=4daCLEXF208G0iw0aUnJD&utm_content=1&utm_medium=android_app&utm_name=androidcss&utm_source=share&utm_term=1) and other video synth boards I want to make a table with price, video in, video out, etc.

Just starting this and I’m realizing that there are some €400-600 expensive video synths that are just rpis (called compute modules which don’t have any conectors) with knobs. The analog video synths seem to be more composite video focused and eurorack compatible. People are selling these on etsy. There is also the refurbished, spray painted, and circuit bent video devices, digital cameras, from the 90s + 2000s market.

This brand has nice design : https://www.afterlifelaboratories.com/

I also want to calculate the size for series of bitmaps as vidéo would take on the on SD, then look at max speeds for reading.

The 100×100 pixel bitmaps I output with ffmpeg are 10.8KB each. If I had three frames a second, I could store an hour of film in

10 kilobytes x 10 800 = 10MB.

Also watch spi tutorial and debug with the Arduino sd card !! Will then have proved my understanding and will have oscilloscope/ Logic analyser recordings 

****

After the experience trying to create an SD interface in verilog, and some reflection on the complexity of reconstructing high speed HDMI video from SD bmp stills incoming at a completely different speed, I’ve been reconsidering the TFP401A Digital Receiver chip (7 euros). It would essentially be like the rpi in DPI mode (it outputs DE, HSYNC, VSYNC, Pixel CLK, etc.) but much faster and with some configuration options to play with (staggering off/on, odd or even color sel, one/two pixels per clock sel, output drive strength sel).

Instead of generating signals with the FPGA, could I also take timing signals from the chip ?

This approach seems to be the most pragmatic for a viable video synth product. After it would be just having fun coding with video in and out.

Two possibilities for double HDMI plugs. Left is way more convenient.

****

Sam’s advice :

  • Think about language around the product, naming aspects. “Rewilding circuits !” “Post-colonial circuitry”
  • More deliberate about the screen selection, bigger screen or screen selected for the experience of watching ?
  • Culture of where buttons are in synth culture, play them to learn.
  • Ergonomics ?!

Check out funky Rule90 esque thing :

*****

Trying to get potentiometer reading working based on this post https://web.archive.org/web/20191115162412/http://hamsterworks.co.nz/mediawiki/index.php/Cheap_Analogue_Input :

reg [18:0] adc_count= 0; //to count how long it's taking to charge
reg [16:0] pot_value= 0; //to store cap charge time value

reg [12:0] counter = 0;

wire adc_in = 0;
reg adc_sample = 0;

assign adc = (adc_sample) ? 1'bZ : 1'b0;
assign adc_in = adc;

always @(posedge latch_high_speed[2]) begin
counter = counter + 1;
                if (counter==0) begin
                       
                               adc_count <= adc_count + 1; // start counting

                                               if(adc_count==0)begin // once we have rolled over counter...

                                                               adc_sample <= 1'b0; // discharge cap to restart the process

                                               end

                                               if(adc_count==19'b1111111111111111111)begin // if max amount of time has passed
                                                               pot_value <=17'b11111111111111111; //store max value in pot

                                               end

                                               else begin

                                                               if(adc_in==1) begin // if cap charged...

                                                                               pot_value<=adc_count[18:2]; // top 17 bits are pot

                                                               end

                                                      adc_sample<=1'b1; //put cap pin in high 'Z'

                                               end
                end
end

Later on I check to see the value of pot_value in order to draw pixels or not :

if (hc >= hbp && hc < hfp && (out_bit[8:0] == key[8:0]) && (pot_value < 17'b11111000000000000))begin
...
end

 

FAST CHARGE :

Fastest charging time possible is when the 10K is set at zero. The resistance in that case is just the 1K resistor in series.

The time it takes the cap to charge in this case is is 0.7 x tau = 0.00007‬ seconds.

I’m using 25MHz clock which takes 1,750 cycles (with a period of 0.00000004 seconds) to count that long.

SLOW CHARGE :

Slowest charging time possible is when the 10K is set at full. The resistance in that case is 10K + the 1K resistor in series.

The time it takes the cap to charge in this case is is 0.7 x tau = 0.00077 seconds.

I’m using 25MHz clock which takes 19,250 cycles (with a period of 0.00000004 seconds) to count that long.

This means I have a range of time to count between 1,750 and 19,250 cycles.

I need at least a 15 bit counter (2^15 max value of 32,768‬) to be able to count up to 19,250.

10,500‬ cycles would make a good threshold as it is between the min and the max. This is equal to 010100100000100 in binary.

****

It works ! Here is the final pot reading part of the code :

reg [15:0] adc_count= 0; //15 bits to count how long it's taking to charge. 32,768 is the max value. The MSB is to discharge the cap.
reg [14:0] pot_value= 0; //to store cap charge time value

wire adc_in;
reg adc_sample = 1; //first time through we want to sample the cap value


assign adc = (adc_sample) ? 1'bZ : 1'b0;
assign adc_in = adc;

always @(posedge latch_high_speed[2]) begin //25MHz clock

adc_count <= adc_count + 1; // start counting

if(adc_count>=16'b1000000000000000)begin // once we have rolled over 15 bit counter...

adc_sample <= 1'b0; // ...discharge cap for a while to restart the process.
end

else begin

adc_sample<=1'b1; //put cap pin in high 'Z'

if(adc_in==1'b1) begin // if cap charged...

pot_value[14:0]<=adc_count[14:0]; //store this value in pot

end


end

end

 

It’s a bit jumpy to look at the lower bits but taking the top 2 bits you can have a realiable dividing of the pot’s range into 4.

Looks like you can average binary numbers by just adding then and then shifting the bits to the right !

****

HDMI IN and OUT board with tightly packed buttons and two pots. Had some fun with chamfer vs fillet and square vs. diamond :

****

Fabien reminded me about the MILKY MIST FPGA video synth he mentioned earlier (https://m-labs.hk/gateware/m1/)

Notes from meeting with Rémi GEORGES who kindly shared many references with me :

Bastien FPGA’s office Syntonie (https://syntonie.fr/)
Isohélie (https://reverb.com/fr/item/70201236-syntonie-isohelie-3-channel-3-bit-dac-keyer-for-eurorack-video-synthesis)
Eric Schlappi
Faust DST (https://faust.grame.fr/)
Grame Lyon Fast (https://fast.grame.fr/)
BPMC HDK-1 FPGA glitch (https://bpmcglitch.com/product/hdk-01/)
L’ERG école recherche graphique (https://wiki.erg.be/m/#%C3%89cole_de_recherche_graphique)
Karlsruhe live coding masters (https://hfm-karlsruhe.de/en/university/institutes/institute-music-informatics-and-musicology/courses-imwi)
Hacklabio.org zagreb fubar (https://fubar.space/)
Fflive (https://ffglitch.org/gallery/)
Vector live
A discord called Syntonie (
Recur boy – an rpi based video synth (https://github.com/cyberboy666/recurBOY)
Lintz conf
P5live (https://teddavis.org/p5live/)
Chair de poule cookie coolllectif (https://www.instagram.com/cookiecollectif/)
Raphaël Bastide (https://raphaelbastide.com/)
The HDMI IN and OUT board seems like a good idea as there aren’t many on the market. Lots of boards for composite stuff. I should be more in touch with other people who make these things, especially on discord etc.
****
Trying to map the overall architecture this thing to have, based on the F(x) selection structure from Recur boy :

Instead of looking under every single rock on the beach I’m thinking I should only do things with this board that are not possible with Processessing on a computer. So, things that have to do with time like that heat camera and extra heavy parallel stuff ? Otherwise I am just putting myself through suffering for no real benefit as far as I can see.

 

****

Taking another crack at getting rpi to pass video into the Cyber Campus version of the board.

Because I didn’t include the pixel clock or DEN, I’ve made life a little harder for myself. I’ve adapted the HDMI code to change symbols based on the rpi h and v syncs.

always @(posedge rpi_pixel_clock) // display 100% saturation colourbars

begin

                // first check if we're within vertical active video range

                if (rpi_vsync)

                begin

                // now display different colours every 80 pixels
                // while we're within the active horizontal range

                // -----------------

                               if (rpi_hsync)
                               begin

                                               if ((b_in == 1'b1))
                                               begin

                                                    c2_symbol = 10'b0111110000; // red
                                                    c1_symbol = 10'b1011110000; // green
                                                    c0_symbol = 10'b1011110000; // blue

                                               end

                                               if ((b_in == 1'b0))
                                               begin

                                                    c2_symbol = 10'b1011110000; // red
                                                    c1_symbol = 10'b0111110000; // green
                                                    c0_symbol = 10'b1011110000; // blue

                                               end

                               end

                               // we're outside active horizontal range

                               else
                               begin

                                               c2_symbol = 10'b1101010100; // red
                                               c1_symbol = 10'b1101010100; // green

//---------------------------------------------

// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.

//---------------------------------------------

                                               case (syncs)

                                               2'b00 : c0_symbol = 10'b1101010100;
                                               2'b01 : c0_symbol = 10'b0010101011;
                                               2'b10 : c0_symbol = 10'b0101010100;
                                               default : c0_symbol = 10'b1010101011;

                                               endcase

                               end
                end

// we're outside active vertical range

                else
                begin

                               c2_symbol = 10'b1101010100; // red
                               c1_symbol = 10'b1101010100; // green

//---------------------------------------------

// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.

//---------------------------------------------

                               case (syncs)

                               2'b00 : c0_symbol = 10'b1101010100;
                               2'b01 : c0_symbol = 10'b0010101011;
                               2'b10 : c0_symbol = 10'b0101010100;
                               default : c0_symbol = 10'b1010101011;

                               endcase
                end
end

It draws the video to the left side and then has a bunch of blank space.

By changing the rpi_pixel_clock to a division of the that clock, we get multiple pi images on screen :

******

I can now record from the FPGA (not yet RPI) on the little SRAM. I had run into a few issues :

  • The reset pin I selected isn’t valid according to IceCube so the always block that tested the pin didn’t work and the address didn’t count up.
  • I was using one of the keys, key[8] as a reset, and the compiler didn’t like that. So I just defined in the pcf that there were only 8 keys and the 9th was not part of the array and was called rec.

On the left is the original pattern from the FPGA, on the right what the SRAM is playing back :

 

Here is the pcf :

set_io hdmi_p[0] 139 -io_std SB_LVCMOS
set_io hdmi_p[2] 78 -io_std SB_LVCMOS
set_io hdmi_p[1] 80 -io_std SB_LVCMOS
set_io hdmi_p[3] 137 -io_std SB_LVCMOS

set_io hdmi_n[0] 138 -io_std SB_LVCMOS
set_io hdmi_n[2] 79 -io_std SB_LVCMOS
set_io hdmi_n[1] 81 -io_std SB_LVCMOS
set_io hdmi_n[3] 136 -io_std SB_LVCMOS

set_io key[0] 37
set_io key[1] 38
set_io key[2] 39
set_io key[3] 41
set_io key[4] 42
set_io key[5] 43
set_io key[6] 44
set_io key[7] 45
set_io rec 47

set_io SCK 32
set_io CMD 33

set_io LED1 29
set_io LED5 28

set_io reset 66

set_io clk100 49

set_io b_in 63

set_io rpi_pixel_clock 61
set_io rpi_hsync 52
set_io rpi_vsync 58
set_io rpi_DEN 62

set_io v_sync 97 //for vga
set_io h_sync 76 //for vga

set_io io[0] 7
set_io io[1] 8
set_io io[2] 9
set_io io[3] 10
set_io io[4] 11
set_io io[5] 12
set_io io[6] 19
set_io io[7] 22

set_io addr[0] 4
set_io addr[1] 3
set_io addr[2] 2
set_io addr[3] 1
set_io addr[4] 144
set_io addr[5] 143
set_io addr[6] 142
set_io addr[7] 141
set_io addr[8] 120
set_io addr[9] 121
set_io addr[10] 24
set_io addr[11] 122
set_io addr[12] 135
set_io addr[13] 119
set_io addr[14] 134
set_io addr[15] 116
set_io addr[16] 129
set_io addr[17] 128


set_io cs 25
set_io oe 23
set_io we 118

Here is the verilog :

`default_nettype none // disable implicit definitions by Verilog
//-----------------------------------------------------------------
// minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
//
// Author: Mike Field <hamster@snap.net.nz>
//
// DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
// value is mapped to one or two 10-bit symbols, depending on how
// many 1s or 0s have been sent. This makes it a DC balanced protocol,
// as a correctly implemented stream will have (almost) an equal
// number of 1s and 0s.
//
// Because of this implementation quite complex. By restricting the
// symbols to a subset of eight symbols, all of which having have
// five ones (and therefore five zeros) this complexity drops away
// leaving a simple implementation. Combined with a DDR register to
// send the symbols the complexity is kept very low.
//-----------------------------------------------------------------

module top(
clk100, hdmi_p, hdmi_n, rec, addr, io, cs, we, oe, key, reset
);


input wire rec, // the record button IS ACTIVE LOW !!
input clk100;
output [3:0] hdmi_p;
output [3:0] hdmi_n;

input reset; // 66 is not an accepted pin, DO NOT USE in ALWAYS BLOCK !

input [7:0] key;

output reg [17:0] addr;
inout wire [7:0] io; // inout must be type wire

output wire cs;
output reg we;
output wire oe;

wire [7:0] data_in;
wire [7:0] data_out;

reg [7:0] a, b;

assign io = (rec==0) ? a : 8'bzzzzzzzz; // rec==0 is when rec button pushed

assign data_out = b;

// for our bittiness
reg [9:0] again_xnor;

// registers for storing the horizontal & vertical counters
reg [9:0] vc;
reg [9:0] hc;


assign cs = 0; //change this
assign oe = 0; //change this


// to store values from other modules
wire half_sec;

wire [12:0] random_number_0;
wire [12:0] random_number_1;
wire [12:0] random_number_2;
wire [12:0] random_number_3;
wire [12:0] random_number_4;
wire [12:0] random_number_5;
wire [12:0] random_number_6;
wire [12:0] random_number_7;
wire [12:0] random_number_8;
wire [12:0] random_number_9;
wire [7:0] sine_wave;


// For holding the outward bound TMDS symbols in the slow and fast domain
reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
reg [9:0] clk_high_speed;

reg [1:0] c2_output_bits;
reg [1:0] c1_output_bits;
reg [1:0] c0_output_bits;
reg [1:0] clk_output_bits;

wire clk_x5;
reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain

wire vsync, hsync;
wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
assign syncs = {vsync, hsync};

// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 525; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch (96 + 48)
parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
parameter vbp = 35; // end of vertical back porch (2 + 33)
parameter vfp = 515; // beginning of vertical front porch (525 - 10)


// generate sync pulses (active high)
assign vsync = (vc < vpulse);
assign hsync = (hc < hpulse);


assign data_in[7:0] = (again_xnor[7:0] == key[7:0]) ? 8'b11111111 : 8'b00000000; // output from FPGA

always @(*) begin

again_xnor[9:0] = hc[9:0]^ ~vc[9:0];

end

//SRAM address counter

always @(posedge latch_high_speed[2]) begin

addr <= addr+1;

end

//REC control


always @(posedge latch_high_speed[2]) begin
b <= io;
a <= data_in;
if (rec==0) begin
we <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope. Maybe because blocking vs non blocking ?
end
else begin
we <= 1;
end
end

always @(posedge clk_x5) begin
//-------------------------------------------------------------
// Now take the 10-bit words and take it into the high-speed
// clock domain once every five cycles.
//
// Then send out two bits every clock cycle using DDR output
// registers.
//-------------------------------------------------------------
c0_output_bits <= c0_high_speed[1:0];
c1_output_bits <= c1_high_speed[1:0];
c2_output_bits <= c2_high_speed[1:0];
clk_output_bits <= clk_high_speed[1:0];
if (latch_high_speed[2]) begin // pixel clock 25MHz
c0_high_speed <= c0_symbol;
c1_high_speed <= c1_symbol;
c2_high_speed <= c2_symbol;
clk_high_speed <= 10'b0000011111;
latch_high_speed <= 3'b000;
if (hc < hpixels)
hc <= hc + 1;
else
begin
hc <= 0;
if (vc < vlines)
vc <= vc + 1;
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

always @(posedge latch_high_speed[2]) // display 100% saturation colourbars
begin
// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------
if(hc >= hbp && hc < hfp) begin
// display white bar
if ((rec==0) && (a==8'b11111111))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
// display black bar
else if ((rec==0) && (a==8'b00000000))
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue
end

else if ((rec==1) && (b==8'b11111111)) //data_out not b ??
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
// display black bar
else
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b1011110000; // blue
end

end

// we're outside active horizontal range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end //if (vc >= vbp && vc < vfp)
// we're outside active vertical range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end //always @(posedge latch_high_speed[2])

// red N
defparam hdmin2.PIN_TYPE = 6'b010000;
defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin2 (
.PACKAGE_PIN (hdmi_n[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c2_output_bits[1]),
.D_OUT_1 (~c2_output_bits[0])
);

// red P
defparam hdmip2.PIN_TYPE = 6'b010000;
defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip2 (
.PACKAGE_PIN (hdmi_p[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c2_output_bits[1]),
.D_OUT_1 (c2_output_bits[0])
);

// green N
defparam hdmin1.PIN_TYPE = 6'b010000;
defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin1 (
.PACKAGE_PIN (hdmi_n[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c1_output_bits[1]),
.D_OUT_1 (~c1_output_bits[0])
);

// green P
defparam hdmip1.PIN_TYPE = 6'b010000;
defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip1 (
.PACKAGE_PIN (hdmi_p[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c1_output_bits[1]),
.D_OUT_1 (c1_output_bits[0])
);


// blue N
defparam hdmin0.PIN_TYPE = 6'b010000;
defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin0 (
.PACKAGE_PIN (hdmi_n[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c0_output_bits[1]),
.D_OUT_1 (~c0_output_bits[0])
);

// blue P
defparam hdmip0.PIN_TYPE = 6'b010000;
defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip0 (
.PACKAGE_PIN (hdmi_p[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c0_output_bits[1]),
.D_OUT_1 (c0_output_bits[0])
);

// clock N
defparam hdmin3.PIN_TYPE = 6'b010000;
defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin3 (
.PACKAGE_PIN (hdmi_n[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~clk_output_bits[1]),
.D_OUT_1 (~clk_output_bits[0])
);


// clock P
defparam hdmip3.PIN_TYPE = 6'b010000;
defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip3 (
.PACKAGE_PIN (hdmi_p[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (clk_output_bits[1]),
.D_OUT_1 (clk_output_bits[0])
);
// D_OUT_0 and D_OUT_1 swapped?
// https://github.com/YosysHQ/yosys/issues/330


SB_PLL40_PAD #(
.FEEDBACK_PATH ("SIMPLE"),
.DIVR (4'b0000),
.DIVF (7'b0001001),
.DIVQ (3'b011),
.FILTER_RANGE (3'b101)
) uut (
.RESETB (1'b1),
.BYPASS (1'b0),
.PACKAGEPIN (clk100),
.PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
);

endmodule

********
I have updated my github with the new HDMI codes : https://github.com/merlinmarrs/iCE40HX-verilog-video-patterns
I have formalized the functionality of the knobs and buttons and modes :
    • VIDEO IN MODE : selector knob choses f(x) to modify incoming video
    • PATTERN GEN MODE: selector knob choses abstract patterns to output

In both modes, recordings can be made by pushing the rec + N key to save an image of the effect. Pushing N from then on plays back the effect (but then this means I can’t edit the pattern with the keys…)?  Up to 8 channels can be layered, each with a different color ?

*******

I’m trying to cross clock domains between the rpi video and the FPGA HDMI using BRAM. I’m trying to do with dual ported (one clock for reading the rpi at 50MHz), another for writing. I’ve got BRAMs of different sizes, and eventually able to max out the 16x 4k BRAMs :

reg [14:0] BRAM_ADDR_R;

reg [14:0] BRAM_ADDR_W;

reg [1:0] mem [100000:0]; // randomly picked this

2 x 100 001 = 200 002 ?

2^15 x 2^15 = 1 073 741 824‬ ?

Checked out a nandland video explaining how to move between clock domains, you just need a total of three latches between a slow and a fast clock :

if (rpi_hsync) begin
    //crossing clock domains
    r1_data <= b_in;
    r2_data <= r1_data;
//r2_data is usable with a faster clock
        end
end

Also could try to receive a whole line from rpi, and then scale it and give it to the HDMI as a line? So far failing at this.

Also tried making a switch case controlled by the pot :

case(pot_value[14:12])


3'b000: condition = (hc < random_number_0[6:0] + random_number_2[9:2] + random_number_4[9:2] + random_number_5[8:1]+ random_number_6[5:1]+ random_number_7[6:0]);
3'b001: condition = (out_bit[random_number_2[3:0]] == 1 | out_bit[random_number_6[3:0]]);
3'b010: condition = (hc < (400 - random_number_5[5:0]) && vc > (400 - random_number_5[5:0]));
3'b011: condition = (bit_or[random_number_2[3:0]] == 1 | bit_or[random_number_6[3:0]] == 0);
3'b100: condition = (hc < (500 - random_number_2[5:0]) && vc < (500 - random_number_3[5:0]));
3'b101: condition = ((hc % 100 < random_number_0[9:0] % 100) && (vc % 100 < random_number_1[9:0] % 100));
3'b110: condition = (hc > random_number_2[2:0]*vc+random_number_2[5:0]);
3'b111: condition = ((hc < random_number_0[3:0]*vc+random_number_3[3:0]+random_number_9[7:0]) && (hc > random_number_0[3:0]*vc+random_number_3[3:0]-random_number_9[7:0]));


default: condition = (out_bit[random_number_2[3:0]] == 1 | out_bit[random_number_6[3:0]]);

endcase

la béchamel part deux

Update:

  1. I can output HDMI video now
  2. I can now program the FPGA with raspberry pi
  3. I have finalized the all-logic, two board version of the béchamel and just need a Mouser order to put together the final version.
  4. After this béchamel 1 experience I have concluded that running a video synth operation developing with discrete logic chips would be so incredibly impractical (expensive, time consuming, physically large, inflexible) compared to working with reprogrammable logic like the FPGA.
  5. I want to finish this chapter of my project and then take a moment, to stop working, to think and to reflect. I either want to shift my attention to the preparedinstruments site, and trying to manage the making and selling of this board, or to take a break from electronics/programming research to read and explore elsewhere for a bit. I also want to show my project to people, like Vincent Rioux, and get their honest feedback about the meaning of this thing outside of my own interests and learning. I want to revisit my strategies and practices, how I work with lead solder and how I decide what boards to make. For instance, it was cool to design and produce lots of boards but it is inefficient when a single board that is just partially populated can do the work of various boards. Another thing : I have spent all this time testing stuff and have ended up with a board that is the same as everyone else’s : FPGA w/ SRAM, single board, rpi programmable. Should I start by doing things that other people are doing instead of trying to do everything else first ?

Update 2 :

Getting the SD card interfaced with FPGA isn’t a gimmick (like having an integrated screen, for instance) as it would make turn this object into a fully fledged “product” which isn’t reliant on another consumer product (like Arduino or rpi) to function. It makes sense to use an FPGA as Arduino cannot output HDMI, and is limited in terms of its ability to read an SD card, manage an SRAM, listen for buttons, and modify video simoultaneously. The system would be kind of complete with both large memory stockage, fast memory, input management, video output. But would it be fun to sell a product ? I would have to do do customer service, repairs, answer questions on the forum, handle shipping and customs. I think I would rather do art installations and kits / workshops ?

Recent Wintergatan youtube video suggested making explicit these :

  • Problem definition : There is a lack of inexpensive, HDMI-compatible, live-playable, lo-fi hardware video synthesis devices on the synth market. But to be honest, this thing is not a response to a problem so much as a gratuitous problem-generating project (a.k.a a creative project).
  • Design requirements : HDMI out, video in via SD card, USB-C power, tactile interface, (audio in ?). Live-playable, relatively small and light (to be easily shipped), inexpensive (~25 euros). Minimilist in design : no bloated software, everything implemented as simply and as fundamental/low-level as possible and everything using only 1,200 LUTs and 2MB of SRAM. On the other hand the device should be fun, quirky, expressive and playful in it’s output and physical incarnation. Everything on the board should be solderable by hand, nothing so small or specialized that you need special tools apart from a fine soldering iron.

I am working on a low-cost, NO SCHOOL NEVERS prototype that is just the Villette Makers PEW model with the addition of a small SRAM. This will be fun because I’ll be able to test the HDMI synth with memory and buttons. Before sending this design off to be made, I want to experiment more with SRAM and FPGA (to figure out what the smallest size memory I can get away with and if I should consider adding anything else to this board based on what I learn from these experiments).

  1. How can I record and playback with sync so images don’t jump around (taking H SYNC or Display Enable information in, presumably) ?
  2. How can I reduce the resolution of recordings (with clock divisions)?
  3. How can I record video to a single channel and mix several channels at output (put them on different colors is the easiest) ?

****

I mentioned Nosedrip, I also want to be able to make art like You Are Listening To. There is radio communication from various city systems like the train network, airports, waterways, etc. combined with ambient sounds.

****

Working on slides for NO SCHOOL to explain what an FPGA is. Looking at some possible analogies. I can’t yet totally understand the difference between control flow programming (like in C) which performs one operation at a time (they digest?) and working with data flow pipelines that just kind of react (like a mirror or a lens) because of their form. I can’t tell if microfluidics and parallel marble machines are like FPGAs if you say that fluid moves super fast…

 

***

Trick to read analog signals with FPGA ( basically time how long it takes to charge a capacitor ) is genius : https://web.archive.org/web/20190615204217/https://hamsterworks.co.nz/mediawiki/index.php/Cheap_Analogue_Input * TO ADD TO NEXT BOARD*

***

In order to test SRAM recording, I’ve got the 4x16MB board back up and running. Here’s how I modified the board to get a test setup running :

  • I took out the SD card from the previous RPI that was no longer appearing to output video from DPI. I have inserted it into a fresh raspberry pi and soldered a thinner profile 20×2 header “pi hat” (which makes the board less vulnerable to bending and losing connections).
  • I desoldered the third and fourth SRAM chips (C&D) as they were causing issues for mysterious reasons I couldn’t debug. In any case I want to see what I can do with very little memory for the next board so not an issue.
  • I replaced the PFET as I noticed before that it was getting hot and also that the power was fading after being initially plugged in.
  • I removed the 50MHz oscillator on the board because it was unreliable and my attempts to resolder it just made it worse. So now it works with either rpi pixel clock or an external 25MHz from the function generator.
  • I desoldered the two jumper wires I had soldered to access rpi_DEN & another non-connected rpi pin. All references to DEN have also been removed in the verilog.

 

There are two verilog codes which I’ve named vga_sync.v and vga_sync_test.v that need to be added :

/*
640x480 VGA singal generator
============================

- Creates h_sync,v_sync signals
- Creates display enable signal and horizontal, vertical
pixel position in display (h,v)
*/

`default_nettype none


module vga_sync(
input wire clk_in,
input wire reset,
output reg h_sync,
output reg v_sync,
output wire clk_sys,
output reg [11:0] h_count,
output reg [11:0] v_count,
output reg display_en
);


// Pixel counters
reg [11:0] h_counter = 0;
reg [11:0] v_counter = 0;

/*

//FOR 100MHz

localparam h_pixel_total = 3200;
localparam h_pixel_display = 2560;
localparam h_pixel_front_porch_amount = 64;
localparam h_pixel_sync_amount = 384;
localparam h_pixel_back_porch_amount = 192;

localparam v_pixel_total = 2100;
localparam v_pixel_display = 1920;
localparam v_pixel_front_porch_amount = 40;
localparam v_pixel_sync_amount = 8;
localparam v_pixel_back_porch_amount = 132;

*/

// FOR 50MHz

localparam h_pixel_total = 1600;
localparam h_pixel_display = 1280;
localparam h_pixel_front_porch_amount = 32;
localparam h_pixel_sync_amount = 192;
localparam h_pixel_back_porch_amount = 96;

localparam v_pixel_total = 1050;
localparam v_pixel_display = 960;
localparam v_pixel_front_porch_amount = 20;
localparam v_pixel_sync_amount = 4;
localparam v_pixel_back_porch_amount = 66;


/*

//FOR 25MHz

localparam h_pixel_total = 800;
localparam h_pixel_display = 640;
localparam h_pixel_front_porch_amount = 16;
localparam h_pixel_sync_amount = 96;
localparam h_pixel_back_porch_amount = 48;

localparam v_pixel_total = 525;
localparam v_pixel_display = 480;
localparam v_pixel_front_porch_amount = 10;
localparam v_pixel_sync_amount = 2;
localparam v_pixel_back_porch_amount = 33;

*/

always @(posedge clk_in) begin

if (reset) begin
//Reset counter values
h_counter <= 0;
v_counter <= 0;
display_en <= 0;
end
else
begin
// Generate display enable signal
if (h_counter < h_pixel_display && v_counter < v_pixel_display)
display_en <= 1;
else
display_en <= 0;

//Check if horizontal has arrived to the end
if (h_counter >= h_pixel_total)
begin
h_counter <= 0;
v_counter <= v_counter + 1;
end
else
//horizontal increment pixel value
h_counter <= h_counter + 1;
// check if vertical has arrived to the end
if (v_counter >= v_pixel_total)
v_counter <= 0;
end
end

always @(posedge clk_in) begin
// Check if sync_pulse needs to be created
if (h_counter >= (h_pixel_display + h_pixel_front_porch_amount)
&& h_counter < (h_pixel_display + h_pixel_front_porch_amount + h_pixel_sync_amount) )
h_sync <= 0;
else
h_sync <= 1;
// Check if sync_pulse needs to be created
if (v_counter >= (v_pixel_display + v_pixel_front_porch_amount)
&& v_counter < (v_pixel_display + v_pixel_front_porch_amount + v_pixel_sync_amount) )
v_sync <= 0;
else
v_sync <= 1;
end

// Route h_/v_counter to out
always @ (posedge clk_in) begin
h_count <= h_counter;
v_count <= v_counter;
end

endmodule

*****************************************************************************************************************************

// vga_sync_test verilog code 

`default_nettype none

module vga_sync_test(

input wire clk_in,
input wire reset,

input wire rec, // Direction of io, 1 = set output, 0 = read input

//RASPBERRY PI
input wire [3:0] r_in,
input wire [3:0] b_in,
input wire [3:0] g_in,

//VGA OUT
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,

output wire h_sync,
output wire v_sync,

//SRAM

output reg [20:0] addr,
inout wire [7:0] io, // inout must be type wire

output wire cs_1,
output wire cs_0,

output reg we_1,
output reg we_0

);

wire [7:0] data_in;
wire [7:0] data_out;

reg toggle;

reg [7:0] a, b;

assign io = rec ? a : 8'bzzzzzzzz;

assign data_out = b;

assign data_in[1:0] = r_in[3:2];
assign data_in[3:2] = b_in[3:2];
assign data_in[5:4] = g_in[3:2];
assign data_in[7:6] = 2'b00;


wire display_en;
wire [11:0] h_count;
wire [11:0] v_count;

localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;

// CS: low to select, high to deselect

assign cs_0 = toggle ? 1 : 0;
assign cs_1 = toggle ? 0 : 1;

//SRAM address counter

always @(posedge clk_in) begin

if (addr == 0) begin
toggle <= toggle+1;
end

if (reset) begin
addr <= 0;
end else begin
addr <= addr+1;
end
end

//REC control

always @(posedge clk_in) begin

b <= io;
a <= data_in;
if (rec) begin
we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
else begin
we_0 <= 1;
end
end

//VGA COLOR OUT

always @(posedge clk_in) begin
if (display_en) begin

r_out[3:2] <= data_out[1:0];
r_out[1:0] <= data_out[1:0];
g_out[3:2] <= data_out[3:2];
g_out[1:0] <= data_out[3:2];
b_out[3:2] <= data_out[5:4];
b_out[1:0] <= data_out[5:4];

end else begin
r_out <= 4'b0000;
g_out <= 4'b0000;
b_out <= 4'b0000;
end
end

vga_sync vga_s(
.clk_in(clk_in),
.reset(reset),
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);

endmodule
Here is the pcf :
set_io v_sync 87
set_io h_sync 88
set_io clk_in 64
set_io reset 66
set_io io[0] 1
set_io io[1] 2
set_io io[2] 3
set_io io[3] 4
set_io io[4] 122
set_io io[5] 121
set_io io[6] 120
set_io io[7] 119
set_io addr[0] 138
set_io addr[1] 139
set_io addr[2] 141
set_io addr[3] 142
set_io addr[4] 143
set_io addr[5] 8
set_io addr[6] 9
set_io addr[7] 10
set_io addr[8] 11
set_io addr[9] 12
set_io addr[10] 136
set_io addr[11] 135
set_io addr[12] 134
set_io addr[13] 129
set_io addr[14] 128
set_io addr[15] 117
set_io addr[16] 116
set_io addr[17] 115
set_io addr[18] 114
set_io addr[19] 137
set_io addr[20] 113
set_io cs_0 144
set_io cs_1 71
set_io we_0 7
set_io we_1 50
set_io rec 62
set_io r_in[0] 107
set_io r_in[1] 106
set_io r_in[2] 105
set_io r_in[3] 104
set_io g_in[0] 97
set_io g_in[1] 96
set_io g_in[2] 95
set_io g_in[3] 112
set_io b_in[0] 102
set_io b_in[1] 101
set_io b_in[2] 99
set_io b_in[3] 98
set_io r_out[0] 81
set_io r_out[1] 80
set_io r_out[2] 79
set_io r_out[3] 78
set_io g_out[0] 94
set_io g_out[1] 93
set_io g_out[2] 91
set_io g_out[3] 90
set_io b_out[0] 76
set_io b_out[1] 75
set_io b_out[2] 74
set_io b_out[3] 73




**************************************************************************************************************************
I describe here how I loaded videos to the rpi and setup the .desktop autorun code : 

"Setting up another rpi to loop VLC videos" from https://www.marrs.io/la-bechamel/

Here is the raspberry pi config file I've used to get DPI working: 
# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details

# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1

# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16

# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720

# uncomment if hdmi display is not detected and composite is being output
#hdmi_force_hotplug=1

# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
#hdmi_mode=1

# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2

# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4

# uncomment for composite PAL
#sdtv_mode=2

#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800

# Uncomment some or all of these to enable the optional hardware interfaces
dtparam=i2c_arm=off
#dtparam=i2s=on
dtparam=spi=off

# Uncomment this to enable infrared communication.
#dtoverlay=gpio-ir,gpio_pin=17
#dtoverlay=gpio-ir-tx,gpio_pin=18

# Additional overlays and parameters are documented /boot/overlays/README

# Enable audio (loads snd_bcm2835)
dtparam=audio=on

# Automatically load overlays for detected cameras
camera_auto_detect=1

# Automatically load overlays for detected DSI displays
#display_auto_detect=1

# Enable DRM VC4 V3D driver
#dtoverlay=vc4-kms-v3d
max_framebuffers=2

# Disable compensation for displays with overscan
disable_overscan=1

[cm4]
# Enable host mode on the 2711 built-in XHCI USB controller.
# This line should be removed if the legacy DWC2 controller is required
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1

[all]

[pi4]
# Run as fast as firmware / board allows
arm_boost=1

[all]

gpio=0-9=a2
gpio=12-17=a2
gpio=20-25=a2
gpio=26-27=a2

dtoverlay=dpi24

enable_dpi_lcd=1
display_default_lcd=1

dpi_group=2
dpi_mode=87

extra_transpose_buffer=2

dpi_output_format=0x7f216
dpi_timings=720 0 40 48 128 720 0 13 3 15 0 0 0 60 0 50000000 1

******

OK so the challenge with this old board that is now up and running is again :
  1. How can I record and playback with sync so images don’t jump around (taking H SYNC or Display Enable information in, presumably) ?
  2. How can I reduce the resolution of recordings (with clock divisions)?
  3. How can I record video to a single channel and mix several channels at output (put them on different colors is the easiest) ?

SYNC

I have tried using Data Enable (DEN) from the rpi and inputting that, and also using H_SYNC and V_SYNC from the r_pi. So far the best result is taking V_SYNC, but I am still getting drifting. Testing to see if both H_SYNC and V_SYNC are high isn’t more stable for some reason. Here is that code :

`default_nettype none

module vga_sync_test(

input wire clk_in,
input wire reset,

input wire rec, // Direction of io, 1 = set output, 0 = read input

//RASPBERRY PI
input wire [3:0] r_in,
input wire [3:0] b_in,
input wire [3:0] g_in,

input wire DEN,


//VGA OUT
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,

output wire h_sync,
output wire v_sync,

//SRAM

output reg [20:0] addr,
inout wire [7:0] io, // inout must be type wire

output wire cs_1,
output wire cs_0,

output reg we_1,
output reg we_0

);

wire [7:0] data_in;
wire [7:0] data_out;

reg toggle;

reg [7:0] a, b;

assign io = rec ? a : 8'bzzzzzzzz;

assign data_out = b;

assign data_in[1:0] = DEN ? r_in[3:2] : 0;

assign data_in[1:0] = DEN ? r_in[3:2] : 0;
assign data_in[3:2] = DEN ? b_in[3:2] : 0;
assign data_in[5:4] = DEN ? g_in[3:2] : 0;
assign data_in[7:6] = 2'b00;


wire display_en;
wire [11:0] h_count;
wire [11:0] v_count;

localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;

// CS: low to select, high to deselect

assign cs_0 = toggle ? 1 : 0;
assign cs_1 = toggle ? 0 : 1;

//SRAM address counter

always @(posedge clk_in) begin

if (addr == 0) begin
toggle <= toggle+1;
end

if (reset) begin
addr <= 0;
end else begin

//only increment address is DEN high
if (DEN) begin
addr <= addr+1;
end
end
end

//REC control

always @(posedge clk_in) begin

b <= io;
a <= data_in;
if (rec) begin

//only enable WR if DEN high
if (DEN) begin
we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
end
else begin
we_0 <= 1;
end
end

//VGA COLOR OUT

always @(posedge clk_in) begin

//only display pixels if DEN high
if (DEN) begin

r_out[3:2] <= data_out[1:0];
r_out[1:0] <= data_out[1:0];
g_out[3:2] <= data_out[3:2];
g_out[1:0] <= data_out[3:2];
b_out[3:2] <= data_out[5:4];
b_out[1:0] <= data_out[5:4];

end else begin
r_out <= 4'b0000;
g_out <= 4'b0000;
b_out <= 4'b0000;
end
end

vga_sync vga_s(
.clk_in(clk_in),
.reset(reset),
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);

endmodule

I am going to try to count V_SYNCs and use this to vary SRAM address incrementing.

I can get an image to freeze pretty well now, I just needed to find the right size of the V_SYNC_COUNT counter to make it work. Here is the code :

`default_nettype none

module vga_sync_test(

input wire clk_in,
input wire reset,

input wire rec, // Direction of io, 1 = set output, 0 = read input

//RASPBERRY PI
input wire [3:0] r_in,
input wire [3:0] b_in,
input wire [3:0] g_in,

input wire DEN,
output reg [2:0] V_SYNC_COUNT, // the size of this counter impact how many V_SYNC signals it takes to reset the SRAM ADD counter


//VGA OUT
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,

output wire h_sync,
output wire v_sync,

//SRAM

output reg [20:0] addr,
inout wire [7:0] io, // inout must be type wire

output wire cs_1,
output wire cs_0,

output reg we_1,
output reg we_0

);

wire [7:0] data_in;
wire [7:0] data_out;

reg toggle;

reg [7:0] a, b;

assign io = rec ? a : 8'bzzzzzzzz;

assign data_out = b;

assign data_in[1:0] = DEN ? r_in[3:2] : 0;

assign data_in[1:0] = DEN ? r_in[3:2] : 0;
assign data_in[3:2] = DEN ? b_in[3:2] : 0;
assign data_in[5:4] = DEN ? g_in[3:2] : 0;
assign data_in[7:6] = 2'b00;


wire display_en;
wire [11:0] h_count;
wire [11:0] v_count;

localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;

// CS: low to select, high to deselect

assign cs_0 = toggle ? 1 : 0;
assign cs_1 = toggle ? 0 : 1;

//V SYNC counter

always @(posedge DEN) begin
V_SYNC_COUNT <= V_SYNC_COUNT+1;
if(reset == 1) begin
V_SYNC_COUNT <=0;
end
end


//SRAM address counter

always @(posedge clk_in) begin

if (addr == 0) begin
toggle <= toggle+1;
end

// reset the address once we've rolled over V_SYNC_COUNT

if (reset | V_SYNC_COUNT == 0 ) begin
addr <= 0;
end else begin

//only increment address is DEN high
if (DEN) begin
addr <= addr+1;
end
end
end

//REC control

always @(posedge clk_in) begin

b <= io;
a <= data_in;
if (rec) begin

//only enable WR if DEN high
if (DEN) begin
we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
end
else begin
we_0 <= 1;
end
end

//VGA COLOR OUT

always @(posedge clk_in) begin

//only display pixels if DEN high
if (DEN) begin

r_out[3:2] <= data_out[1:0];
r_out[1:0] <= data_out[1:0];
g_out[3:2] <= data_out[3:2];
g_out[1:0] <= data_out[3:2];
b_out[3:2] <= data_out[5:4];
b_out[1:0] <= data_out[5:4];

end else begin
r_out <= 4'b0000;
g_out <= 4'b0000;
b_out <= 4'b0000;
end
end

vga_sync vga_s(
.clk_in(clk_in),
.reset(reset),
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);

endmodule

I just realized I could also take the AND of various bits of the V_SYNC_COUNTER like so :

if (reset | ((V_SYNC_COUNT[0] == 1) & (V_SYNC_COUNT[2] == 1) & (V_SYNC_COUNT[3] == 1))) begin

addr <= 0;

end 

 

RESOLUTION

This is cool, just change clk_in with a divided clock and you get bigger pixels !

`default_nettype none

module vga_sync_test(

input wire clk_in,

output reg [2:0] clk_div, // remember to change clk_div[MSB] also below ! Changing the size of this register changes how big the pixels are :D !

input wire reset,

input wire rec, // Direction of io, 1 = set output, 0 = read input

//RASPBERRY PI
input wire [3:0] r_in,
input wire [3:0] b_in,
input wire [3:0] g_in,

input wire DEN,
output reg [1:0] V_SYNC_COUNT,


//VGA OUT
output reg [3:0] r_out,
output reg [3:0] b_out,
output reg [3:0] g_out,

output wire h_sync,
output wire v_sync,

//SRAM

output reg [20:0] addr,
inout wire [7:0] io, // inout must be type wire

output wire cs_1,
output wire cs_0,

output reg we_1,
output reg we_0

);

wire [7:0] data_in;
wire [7:0] data_out;

reg toggle;

reg [7:0] a, b;

assign io = rec ? a : 8'bzzzzzzzz;

assign data_out = b;

assign data_in[1:0] = DEN ? r_in[3:2] : 0;

assign data_in[1:0] = DEN ? r_in[3:2] : 0;
assign data_in[3:2] = DEN ? b_in[3:2] : 0;
assign data_in[5:4] = DEN ? g_in[3:2] : 0;
assign data_in[7:6] = 2'b00;


wire display_en;
wire [11:0] h_count;
wire [11:0] v_count;

localparam h_pixel_max = 1280;
localparam v_pixel_max = 960;
localparam h_pixel_half = 640;
localparam v_pixel_half = 480;

// CS: low to select, high to deselect

assign cs_0 = toggle ? 1 : 0;
assign cs_1 = toggle ? 0 : 1;

//V SYNC counter

always @(posedge DEN) begin
V_SYNC_COUNT <= V_SYNC_COUNT+1;
if(reset == 1) begin
V_SYNC_COUNT <=0;
end
end

//increment our clock divider

always @(posedge clk_in) begin
clk_div <= clk_div+1;
if(reset == 1) begin
clk_div <=0;
end
end

//SRAM address counter

always @(posedge clk_div[1]) begin

if (addr == 0) begin
toggle <= toggle+1;
end

// reset the address once we've rolled over V_SYNC_COUNT

if (reset | V_SYNC_COUNT == 0 ) begin
addr <= 0;
end else begin

//only increment address if DEN high
if (DEN) begin
addr <= addr+1;
end
end
end

//REC control

always @(posedge clk_div[1]) begin

b <= io;
a <= data_in;
if (rec) begin

//only enable WR if DEN high
if (DEN) begin
we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
end
else begin
we_0 <= 1;
end
end

//VGA COLOR OUT

always @(posedge clk_div[1]) begin

//only display pixels if DEN high
if (DEN) begin

r_out[3:2] <= data_out[1:0];
r_out[1:0] <= data_out[1:0];
g_out[3:2] <= data_out[3:2];
g_out[1:0] <= data_out[3:2];
b_out[3:2] <= data_out[5:4];
b_out[1:0] <= data_out[5:4];

end else begin
r_out <= 4'b0000;
g_out <= 4'b0000;
b_out <= 4'b0000;
end
end

vga_sync vga_s(
.clk_in(clk_in),
.reset(reset),
.h_sync(h_sync),
.v_sync(v_sync),
.h_count(h_count),
.v_count(v_count),
.display_en(display_en) // '1' => pixel region
);

endmodule

The V_SYNC rollover and the clock division need to change in relation to one another.
*********
I’m trying to clear up the recordings, they get super messy with bits everywhere. But the pixelation looks awesome :

Considering removing the amps at the output, as the NO SCHOOL version won’t have these either.

I should definitely change the shape of the pixels to make them rectangular in both X and Y.

**********

I might be entering a new career phase as a prototyper. I am currently working to make an e-ink QR code device, a rotating clock mechanism, and may be tasked with making permanent versions of the FLV DSAA prototypes we proposed !

I can imagine a transition where I do more prototyping professionally and spend my time doing more fine art work – less design of circuits and more experimental video making with my existing boards + new code. Routing circuits is so meticulous, I don’t know how I’ve spent so many hours doing this.

**************

I have finalized the next iteration of the PEW board with a small inexpensive 2MB SRAM, an rpi header (which is also hard-wired to the FLASH programming pins), and an SD card set up to work in either 1 or 4 bit mode (not SPI0) to eventually take in uncompressed video directly, and a test of the potentiometer reading capabilities of the FPGA. The music in + comparator has been removed from this version. I also haven’t been able to learn quickly enough a minimal setup for the FTDI chip so that has not been included either.

I have soldered the first test board :

Experimented with round keys…not sure it’s a keeper.

HDMI is working :

Strange issue that I have faced before : I need to press down on the clock, FPGA or flash memory to get it working…

Fabien’s super flux is making a world of difference, it was hard not to solder perfectly.

The HDMI USB recorder works super well with OBS, and is less hassle than the VGA recorder by far.

To keep in mind for the NO SCHOOL board :

  • Include the 3 SEL BITs to make three different codes possible
  • Have the resistors always in the same configuration for the VGA colors
  • Always have the same pattern for the 1K, 10K, and 0.1uF for the keys
  • Reorder green LEDs
  • The VGA plugs I ordered for this kit are mirrored or the footprint is mirrored ?

Here is the verilog code I set up to test the buttons :

`default_nettype none // disable implicit definitions by Verilog
//-----------------------------------------------------------------
// minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
//
// Author: Mike Field <hamster@snap.net.nz>
//
// DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
// value is mapped to one or two 10-bit symbols, depending on how
// many 1s or 0s have been sent. This makes it a DC balanced protocol,
// as a correctly implemented stream will have (almost) an equal
// number of 1s and 0s.
//
// Because of this implementation quite complex. By restricting the
// symbols to a subset of eight symbols, all of which having have
// five ones (and therefore five zeros) this complexity drops away
// leaving a simple implementation. Combined with a DDR register to
// send the symbols the complexity is kept very low.
//-----------------------------------------------------------------

module top(
clk100, key, hdmi_p, hdmi_n
);

input clk100;
input wire key[8:0],
output [3:0] hdmi_p;
output [3:0] hdmi_n;

// For holding the outward bound TMDS symbols in the slow and fast domain
reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
reg [9:0] clk_high_speed;

reg [1:0] c2_output_bits;
reg [1:0] c1_output_bits;
reg [1:0] c0_output_bits;
reg [1:0] clk_output_bits;

wire clk_x5;
reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain

wire vsync, hsync;
wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
assign syncs = {vsync, hsync};

// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 525; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch (96 + 48)
parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
parameter vbp = 35; // end of vertical back porch (2 + 33)
parameter vfp = 515; // beginning of vertical front porch (525 - 10)

// registers for storing the horizontal & vertical counters
reg [9:0] vc;
reg [9:0] hc;
// generate sync pulses (active high)
assign vsync = (vc < vpulse);
assign hsync = (hc < hpulse);

always @(posedge clk_x5) begin
//-------------------------------------------------------------
// Now take the 10-bit words and take it into the high-speed
// clock domain once every five cycles.
//
// Then send out two bits every clock cycle using DDR output
// registers.
//-------------------------------------------------------------
c0_output_bits <= c0_high_speed[1:0];
c1_output_bits <= c1_high_speed[1:0];
c2_output_bits <= c2_high_speed[1:0];
clk_output_bits <= clk_high_speed[1:0];
if (latch_high_speed[2]) begin // pixel clock 25MHz
c0_high_speed <= c0_symbol;
c1_high_speed <= c1_symbol;
c2_high_speed <= c2_symbol;
clk_high_speed <= 10'b0000011111;
latch_high_speed <= 3'b000;
if (hc < hpixels)
hc <= hc + 1;
else
begin
hc <= 0;
if (vc < vlines)
vc <= vc + 1;
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

always @(*) // display 100% saturation colourbars
begin
// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// based on which buttons are pressed
// -----------------
// display white bar

if (key[0] == 0 && hc >= hbp && hc < (hbp+80))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display yellow bar
else if (key[1] == 0 && hc >= (hbp+80) && hc < (hbp+160))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
// display cyan bar
else if (key[2] == 0 && hc >= (hbp+160) && hc < (hbp+240))
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display green bar
else if (key[3] == 0 && hc >= (hbp+240) && hc < (hbp+320))
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
// display magenta bar
else if (key[4] == 0 && hc >= (hbp+320) && hc < (hbp+400))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display red bar
else if (key[5] == 0 && hc >= (hbp+400) && hc < (hbp+480))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b0111110000; // blue
end
// display blue bar
else if (key[6] == 0 && hc >= (hbp+480) && hc < (hbp+560))
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display black bar
else if (key[7] == 0 && hc >= (hbp+560) && hc < hfp)
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b0111110000; // blue
end
// we're outside active horizontal range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end
// we're outside active vertical range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end

// red N
defparam hdmin2.PIN_TYPE = 6'b010000;
defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin2 (
.PACKAGE_PIN (hdmi_n[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c2_output_bits[1]),
.D_OUT_1 (~c2_output_bits[0])
);

// red P
defparam hdmip2.PIN_TYPE = 6'b010000;
defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip2 (
.PACKAGE_PIN (hdmi_p[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c2_output_bits[1]),
.D_OUT_1 (c2_output_bits[0])
);

// green N
defparam hdmin1.PIN_TYPE = 6'b010000;
defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin1 (
.PACKAGE_PIN (hdmi_n[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c1_output_bits[1]),
.D_OUT_1 (~c1_output_bits[0])
);

// green P
defparam hdmip1.PIN_TYPE = 6'b010000;
defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip1 (
.PACKAGE_PIN (hdmi_p[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c1_output_bits[1]),
.D_OUT_1 (c1_output_bits[0])
);


// blue N
defparam hdmin0.PIN_TYPE = 6'b010000;
defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin0 (
.PACKAGE_PIN (hdmi_n[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c0_output_bits[1]),
.D_OUT_1 (~c0_output_bits[0])
);

// blue P
defparam hdmip0.PIN_TYPE = 6'b010000;
defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip0 (
.PACKAGE_PIN (hdmi_p[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c0_output_bits[1]),
.D_OUT_1 (c0_output_bits[0])
);

// clock N
defparam hdmin3.PIN_TYPE = 6'b010000;
defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin3 (
.PACKAGE_PIN (hdmi_n[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~clk_output_bits[1]),
.D_OUT_1 (~clk_output_bits[0])
);


// clock P
defparam hdmip3.PIN_TYPE = 6'b010000;
defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip3 (
.PACKAGE_PIN (hdmi_p[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (clk_output_bits[1]),
.D_OUT_1 (clk_output_bits[0])
);
// D_OUT_0 and D_OUT_1 swapped?
// https://github.com/YosysHQ/yosys/issues/330


SB_PLL40_PAD #(
.FEEDBACK_PATH ("SIMPLE"),
.DIVR (4'b0000),
.DIVF (7'b0001001),
.DIVQ (3'b011),
.FILTER_RANGE (3'b101)
) uut (
.RESETB (1'b1),
.BYPASS (1'b0),
.PACKAGEPIN (clk100),
.PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
);

endmodule

Here is the pcf :

set_io hdmi_p[0] 139 -io_std SB_LVCMOS
set_io hdmi_p[2] 78 -io_std SB_LVCMOS
set_io hdmi_p[1] 80 -io_std SB_LVCMOS
set_io hdmi_p[3] 137 -io_std SB_LVCMOS
set_io hdmi_n[0] 138 -io_std SB_LVCMOS
set_io hdmi_n[2] 79 -io_std SB_LVCMOS
set_io hdmi_n[1] 81 -io_std SB_LVCMOS
set_io hdmi_n[3] 136 -io_std SB_LVCMOS

set_io key[0] 37
set_io key[1] 38
set_io key[2] 39
set_io key[3] 41
set_io key[4] 42
set_io key[5] 43
set_io key[6] 44
set_io key[7] 45
set_io key[8] 47
set_io clk100 49

Makes some nice buggy HDMI images :

**************

UPDATE : It appears that SPI mode, not SD 1-bit mode, is the more thoroughly documented way of interfacing SD cards.

Here is where the SD 1-bit mode communication protocol is explained : https://www.sdcard.org/downloads/pls/ and select Physical Layer Simplified Specification where chapter 4 describes in detail.

I think I should aim for the Multiple Block Read Operation (CMD18) which keeps going until you send a stop command. 1 bit mode seems the simplest, everything is just in serial.

Here are some sample diagrams :

*************************

General stuff : CMD is used for sending messages, DAT0 for sending data. 3.3V is the standard. Default clock speed is 400KHz, default RCA is 0x0000. Phase 1 is Card ID mode then Data TX mode. 512 bytes sent at a time. According to this post (http://forum.6502.org/viewtopic.php?f=4&t=7300) data is read on the low-to-high transition, and starts with the highest bit (47, for example) going to the lowest bit. Here is a site to calculate CRC (https://www.lddgo.net/en/encrypt/crc)

Here is my, so far successful, attempt directly copying the messages sent here (http://forum.6502.org/viewtopic.php?f=4&t=7300) :

//-----------------------------------------------------------------
// Attempt at SD 1 bit communication
//-----------------------------------------------------------------

`default_nettype none // disable implicit definitions by Verilog

module top(
input wire clk100,
input wire reset,
output reg SCK,
output reg CMD,
output wire LED1,
output wire LED5
);

//some counters
reg [9:0] counter=0;
reg [9:0] q=0;

reg half_sec_pulse = 0;

assign LED1 = half_sec_pulse;
assign LED5 = half_sec_pulse;

reg[25:0] div_cntr1;
reg[25:0] div_cntr2;

always@(posedge clk100)
begin

div_cntr1 <= div_cntr1 + 1;
if (div_cntr1 == 0)
if (div_cntr2 == 0)
begin
div_cntr2 <= 0;
half_sec_pulse <= ~half_sec_pulse;
end

else
div_cntr2 <= div_cntr2 + 1;
else
half_sec_pulse <= half_sec_pulse;
end


// divide 100MHz clock by 1024 to equal 97.65625 KHz (but I'm seeing half that on the 'scope)

always @(posedge clk100) begin

q <= q+1;
if(q == 0) begin
SCK <= ~SCK;
end
end

//start with 80 clock ticks while CMD high

always @(posedge SCK) begin
//wait a half second after power up
if(half_sec_pulse==1)begin
//only do all this one time
counter <= (counter==400) ? counter : counter+1;

if (counter <= 79) begin
CMD <= 1;
end

//send CMD0

if ((counter > 79) && (counter <= 128)) begin

if(counter==81||counter==120||counter==123||counter==125||counter==127||counter==128)begin
CMD <= 1;
end
else begin
CMD <= 0;
end
end

//send 80 clock ticks while CMD high

if ((counter > 129) && (counter <= 209)) begin
CMD <= 1;
end


//send CMD8
if ((counter > 209) && (counter <= 257)) begin

if(counter==211||counter==214||counter==241||counter==242||counter==244||counter==246||counter==248||counter==250||counter==255||counter==256||counter==257)begin
CMD <= 1;
end
else begin
CMD <= 0;
end
end

//set CMD to input and listen !
if ((counter > 257) && (counter <= 400)) begin

CMD <= 1'bz; // put in high Z
end

end
end
endmodule

Here is the pcf :

set_io hdmi_p[0] 139 -io_std SB_LVCMOS
set_io hdmi_p[2] 78 -io_std SB_LVCMOS
set_io hdmi_p[1] 80 -io_std SB_LVCMOS
set_io hdmi_p[3] 137 -io_std SB_LVCMOS

set_io hdmi_n[0] 138 -io_std SB_LVCMOS
set_io hdmi_n[2] 79 -io_std SB_LVCMOS
set_io hdmi_n[1] 81 -io_std SB_LVCMOS
set_io hdmi_n[3] 136 -io_std SB_LVCMOS

set_io key[0] 37
set_io key[1] 38
set_io key[2] 39
set_io key[3] 41
set_io key[4] 42
set_io key[5] 43
set_io key[6] 44
set_io key[7] 45
set_io key[8] 47

set_io SCK 32
set_io CMD 33

set_io LED1 29
set_io LED5 28

set_io reset 66

set_io clk100 49

This works, thanks to this post for all the answers and detailed oscilloscope screen shots from AndrewP and John West on this forum : http://forum.6502.org/viewtopic.php?f=4&t=7300

 

And I’m following this overall sequence :

 

John West writes that the steps are :

  • CMD0 “GO_IDLE_STATE”
  • CMD8 “SEND_IF_COND”
  • CMD55 “APP_CMD”, ACMD41 “SD_SEND_OP_COND” with argument 40100000 (HCS=1, voltage = 3.3). The R3 response has HCS and the supported voltages set. Repeat until bit 31 of the response is 1.
  • CMD2 “ALL_SEND_CID” with argument 00000000. The R2 response has the CID. I ignore it.
  • CMD3 “SEND_RELATIVE_ADDR” with argument 00000000. The R6 response contains the RCA and some status that I ignore.

The card is now in the stand-by state. Reading a block is accomplished by

  • CMD7 “SELECT/DESELECT_CARD” with argument bits 31-16 = the RCA you were given, 15-0 = 0. The R1b response I ignore.
  • CMD17 “READ_SINGLE_BLOCK” with argument = the block address. The R1 response I ignore, but there will also be the block data. Keep toggling the clock, and you’ll eventually see a start bit followed by a block of data and a CRC.
*******************

Here is the start-up procedure :

**********************

Card ID mode: After power on, the Host sends to the SD card first :

   0. CMD0 (enter IDLE mode). (And now pull CMD high and tick the CLK a series of times ?)

  1. CMD8  (operating condition validation)

2. ACMD41 (SD_SEND_OP_COND)

3. CMD2 (ALL_SEND_CID) : CID is a unique card ID number.

4. CMD3 (SEND_RELATIVE_ADDR) which asks the card to send it’s relative card address (RCA).

*********************************

Now to enter Data TX mode :

5. CMD7 : Puts the card into TX mode.

6. CMD18 : Multiple Block Read (continues until CMD12 STOP_TRANSMISSION)

7. DAT0 bus sends first 1 of 4 bits LOW followed by a continuous data stream synchronized with the CLK.

**************************

FTDI

I am just now thinking that if I am making a product I don’t necessarily need to worry about making it USB programmable. I can just leave the programming header and a link to how to program with Arduino/raspberry pi.

Looking in to FTDI replacements. This post (https://forum.arduino.cc/t/explanation-of-ftdi/267058/4) suggests these :

  • Sil Labs CP2102
  • Prolific PL2303
  • WCH CH340 (Olimex has a board here : https://www.olimex.com/Products/Breadboarding/BB-CH340T/open-source-hardware)

Fabien says that these chips only do UART however. (But the NANDLAND GO board seems to have one https://nandland.com/wp-content/uploads/2022/06/Go_Board_V1.pdf)

There are some open JTAG techniques. Another option he suggests is to have a bootloader as is done with the following boards :

  • tinyFPGA-BX (Uses the FPGA itself to program the flash chip : https://github.com/tinyfpga/TinyFPGA-Bootloader)
  • Fomu (Uses the FPGA itself to program the flash chip : https://github.com/im-tomu/foboot)
  • ICE40HX1K-EVB (Uses an Arduino board – olimexino-32u4 – to program the flash chip)

****

Saw on a blog that controlling a USB flash drive with SPI might be easier than an SD card for some reason ? It looks like a state machine is how you make an FPGA behave like a microcontroller for this type of situation.

Perhaps the usual SPI for SD card is the correct choice. Some things I’m looking at :

  • https://hackaday.io/project/160928/logs
  • https://www.fpga4fun.com/SPI2.html
  • https://hackaday.io/project/119133-rops/log/144622-starting-with-verilog-and-spi
  • SPI slave : https://github.com/joeferner/fpga-spi/blob/master/spi.v
  • ***NANDLAND SPI Master : https://github.com/nandland/spi-master/blob/master/Verilog/source/SPI_Master.v***
  • Optimized SPI Master: https://github.com/freecores/tiny_spi/blob/master/rtl/verilog/tiny_spi.v
  • https://alchitry.com/serial-peripheral-interface-spi-verilog

The Nandland SPI Master compiles. Looking at these documents now explaining SD card protocol with SPI :

  • http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf
  • https://users.ece.utexas.edu/~valvano/EE345M/view12_SPI_SDC.pdf

SD interfacing with SPI :

HxD is a tool for raw SD card editing. It could help finding the start address of the first file for instance.

I think step 1 would be to try to do this manually (using an SPI library ?) with Arduino and a logic analyzer / oscilloscope to confirm that what I’m trying to do works with the particular card I am using. With screenshots of the communication I could then attempt to replicate this with the FTDI FPGA which has an SD card and LEDs for indicators.

Explaining how to use Arduino SPI library to send individual bytes :

https://docs.arduino.cc/tutorials/generic/introduction-to-the-serial-peripheral-interface/

https://docs.arduino.cc/learn/communication/spi/?_gl=1*4f5308*_ga*MTk5MjE1MjA0My4xNzA1NzQyNDIx*_ga_NEXN8H46L5*MTcxMTEzOTU0Ny4zOS4wLjE3MTExMzk1NTIuMC4wLjY5OTM2NjkyOQ..*_fplc*SVhlSkEzOXkwVlFqc2MlMkZ6ZlU5NTlzYTJteWJnTWZ5eUVlSUFDSlRZSTl3bVJHd3lMWVZiWTNFenpMUjhOOTVFR2drR3lPdU1NR0pSQWluMGRWYWZRM3htUWFBd3JYeFFiViUyQndybE1SZk9VU1JFT0Y0cXpldG5HVnZXbE1RUSUzRCUzRA..

.

****

Looking at enclosures and found these resources :

https://www.protocase.com/blog/2020/02/20/how-to-choose-between-enclosure-styles/

L-Shape Enclosure with printed circuit board installed.

SBm49_SandwichEnclosure-1

image3

 

 

 

ponoko.com/blog/how-to-make/making-enclosures-for-electronics-with-ponoko/

Electronics Project Boxes 8 - 2x4 Wood Enclosure

 

 

 

Cardboard and Interactive Tech | Aylesbury Box Company

 

Here are some experiments with enclosures :

****

Here’s a more precise model of the 3D print option :

****

Trying to output low resolution images from film using ffmpeg (https://www.bannerbear.com/blog/how-to-extract-images-from-a-video-using-ffmpeg/):

ffmpeg -i input.mp4 -s 100x100 -vf fps=1,extractplanes=y %04d.bmp
  1. This outputs 1 frame a second super small in black and white. The input video needs to be in the same directory of the ffmpeg.exe file. Super easy ! I renamed one of the .bmp as a .raw and opened it in photoshop at 100×100 :
  2. *** A couple tests using the screen itself as a display with text overlay : *******
  3. Learning about setting up multiple binary files for cold booting here https://www.latticesemi.com/support/answerdatabase/3/3/4/3344 In Diamond Programmer, Design > Utilities > Deployment Tool. You can then select Advanced SPI flash and start adding binaries and clicking next.
  4. After generating the .mcs file I select it as the binary input in Diamond Programmer. I wish I had added a little 2 switch DIP connected to CBSEL1, CBSEL0 so that when CRESET_B is activated you could select different images. Probably also smart to have a much bigger SPI Flash so that I could also store other types of files ? iCE40-I2C-and-SPI-Hardened-IP-User-Guide : https://www.latticesemi.com/-/media/LatticeSemi/
    Documents/ApplicationNotes/IK/FPGA-TN-02010-1-8-iCE40-I2C-and-SPI-Hardened-IP-User-Guide.ashx?document_id=50480 ******** Am finally finishing the béchamel 2 with the new board : 

Takes so long to assemble !

*Update* : Finished the béchamel 2 !

  • With some nice flexible silicone wire I hardwired the 590 CLK with the RCLK,
  • I connected VSYNC and 4040 Counter Reset to the unused board jumper pins,
  • I cut the traces going from the output butter enables to the board jumper and pulled all the ENs to the ground so that all channels are displayed all the time. I did this on the backside of the board by conecting with vias.
  • I put electrical tape to hold wires down and insolate bare contacts that touch when the board are stacked.

It is kinda cool ! Maybe not freezing images with sync as well as could have hoped but having the keypad definitely makes it feel “playable” and the colors and palipmsest are nice so far 🙂

Some screen shots showing the multi-color action :

Some things I am realizing about this version :

It’s possible to get the adress divisions and frequency divisions just right so that recordings stay in the same place and can be truly stacked like screen printing different layers. The idea of having a video with a clear sequence (zoom out, rotate around, pan through) and using this record and paste technique could lead to cool effects.

I also wonder if I’ve kind of reached the end of what is possible with this SRAM recording exercise. EDIT : TRY FEEDBACK LOOPING !

*EDIT* I took one pin from the VGA COLOR OUT and connected it in place of a RPI IN pin. It’s a bit hard to understand what’s happening but things get muddy quickly. When it isn’t oversaturating it gives this nice overlay (in white) effect.

Just a reminder also that this board requires some physical pressure on either the flash chip or the clock or the south end of the FPGA to get programming to work for some reason. Here is the error message :

ERROR - Function:CHECK_ID

Data Expected: h16 Actual: h1F

Found a thing that helps in Diamond Programmer :  EDIT > SETTINGS >PROGRAMMING > CONTINUE DOWNLOAD ON ERROR

EDIT: I wonder if I could explore the concept of time more directly in these animations. Now that I can freeze a past frame and place it directly over a present frame, I can start to play with sequence a little. Sean Cubitt’s article about the digital image: http://www.visualfields.co.uk/Cubitt.pdf

*******

Reviewing the open source FPGA process.

1. Migen (https://m-labs.hk/gateware/migen/) is for Python to Verilo. It doesn’t look to me (http://m-labs.hk/docs/migen-tutorial.pdf) like a shortcut however…

2. IceStorm flow (https://clifford.at/icestorm) which includes Yosys, Arachne-pnr / Nextpnr, and IceStorm.

IceStorm – The core of the project, IceStorm is the part that handles the bitstream documentation and provides the tools for bitstream analysis and creation for iCE40 FPGAs.

Arachne-PNR – A place-and-route tool initially used in conjunction with IceStorm (later replaced by nextpnr).

Nextpnr – An improved and versatile place-and-route tool that has largely supplanted Arachne-PNR in the open-source FPGA community. It is designed to be a vendor-neutral, timing-driven place and route tool.

Yosys – An open-source synthesis tool for converting Verilog (a hardware description language) code into a gate-level representation that can be used with the place-and-route tools. Yosys is highly flexible and supports various synthesis tasks.

*****

What would a video workshop look like ?

CRT:

  • Inputing fixed voltages, then simple waves (square, triangle, sawtooth, sine) at different frequencies. What is a Hertz ? What kind of frequencies can the eye see ? Drawing and redrawing.
  • Arduino sending stored voltages.

COMPOSITE?

VGA:

  • The VGA protocol and pinouts.
  • Arduino generating timing signals and us sending voltages, simple waves.
  • Taking a video in and switching the colors, doing some mixing and filtering.
  • Using VGAX to generate different patterns based on code.

HDMI:

  • The HDMI protocol and pinouts. Differential signals and the DC balanced concept.
  •  FPGA generating timing signals, us sending signals.
  • Using Verilog to generate some simple patterns.

****

Doing some wiki surfing while watching the great series Le Temps des Ouvriers on Arte :

  • Une perruque : An object made on company machines during company time, but for the satisfaction or purposes of the worker.

Armes de la Critique on X: "Ce 2e épisode se clôt avec des images ...

  • That barricades during the Paris Commune were constructed with military precision and centrally-organized. Organic barricades were banned.

Barricades de la Commune, avril 71. Coin de la place Hotel de Ville & de la rue de Rivoli, Pierre-Ambrose Richebourg (French, 1810–1893), Albumen silver print

  • Sabotage, window and machine breaking, la grève, taking over factories, la désobéissance.

On Sabotage - Politics/Letters QuarterlyPolitics/Letters Quarterly

  • Detournement, découpé (cut up) as a technique for chance recombining of texts which encourages the subconscious to come to the fore.

File:Vorsprung durch Graffit cropped.jpg

The (surprisingly long) history of the cut-up technique

  • The only creative operations are copy, transform and recombine according to Kirby Ferguson

File:Copy-transform-combine-scheme.svg

  • On this same theme, there are aparrently seven principle elements of art (https://en.wikipedia.org/wiki/Elements_of_art) : line, shape, texture, form, space, color and value, with the additions of mark making, and materiality.
  • Types of cinematic transitions / cuts : https://screencraft.org/blog/everything-screenwriters-need-to-know-about-transitions/
  • The experimental method involves reproducing a phenomenon and varying a single parameter related to the hypothesis to be tested.
  • The various views on the filmmaking technique of montage.

**********

Serge had some advice for my project :

  • Make a short text (and title!) that explains the project to someone with zero tech refs and background. Universalize the explanation, don’t get stuck in your head referencing yourself. He thinks that prepared instruments is already too much of an wink wink. He likes the water analogy.
  • Keep the purism, it is the central interest of the project !! If you remove this aspect and just make an app it takes away all of the value (like switching real turn tables and actual vinyl with those fake turn table interfaces, or the Strandbeest with a motor !). The situation where the button you push “directly” influences the video signals makes video like a material medium. 

Short text attempt :

A screen takes video signals in and displays them as changing colors in a window. A video cable is like a river containing signals which are like ripples in the water. This device intervenes inbetween the creation of the video signals and their display on the screen; it allows for direct manipulation of these video signals, creating loops like eddies and other kinds of turbulence, all are instantly visible on the screen.

Like a sketchpad for video !

Live video collaging machine / Live montage machine.

*****

Proposal for video workshop with DSAA / NO SCHOOL:

Start by cutting VGA cable and putting pins on a breadboard.

  • What happens when you remove a color ? GND ? V or H sync ?
  • How about switching colors ?
  • Look at V and H sync on the oscilloscope

Let’s make waves with a function generator and see what they look like on screen.

  • What do different frequencies look like on screen ?
  • How about different wave types and duty cycles ?

(INCLUDE THIS?) What can we do with analog electronics ?

  • A simple low-pass filter ?
  • An amp
  • A delay wire (just a coil of wire) ?

Get a second VGA video source.

  • What happens when you mix colors from one source and colors from another source ?
  • Which H and V signals do you send along to the screen ?
  • Try using a potentiometer to mix two colors together, or fade a color out.

Introduction to logic:

  • Let’s use a comparator to convert analog into ON or OFF
  • Let’s try using an AND, OR and XOR with two video sources.

Introduction to FPGAs : The FPGA is reconfigurable logic which you can program to mess with video signals and respond to a tactile interface. To program the logic gates we use a Hardware Description Language (HDL) like verilog.

  • Let’s generate a video signal using an FPGA
  • Let’s generate some simple patterns
  • Lets’s make it react to button touches

************

Trying to get SRAM + HDMI working together with the CC version. So far it doesn’t like using clk100 for the PLL and for always block. Also fails during placement for unknown reason.

//-----------------------------------------------------------------
// Attempt at SRAM and HDMI OUT
//-----------------------------------------------------------------

`default_nettype none // disable implicit definitions by Verilog

module top(
input wire clk100,

input wire [8:0]key,

output [3:0] hdmi_p,
output [3:0] hdmi_n,

input wire reset,

input wire r_in, //need to be soldered on the Cyber Campus board
input wire b_in, //need to be soldered on the Cyber Campus board
input wire g_in, //need to be soldered on the Cyber Campus board


output reg [17:0] addr,
inout wire [7:0] io, // inout must be type wire

output wire cs,
output wire oe,
output reg we

);

// for translating between input and output with the io pins
wire [7:0] data_in;
wire [7:0] data_out;

reg [7:0] a, b;

assign io = key[0] ? a : 8'bzzzzzzzz;

assign cs = 1;
assign oe = 1;

assign data_out = b;

assign data_in[0] = r_in;
assign data_in[1] = r_in;
assign data_in[2] = b_in;
assign data_in[3] = b_in;
assign data_in[4] = g_in;
assign data_in[5] = g_in;
assign data_in[7:6] = 2'b00;


// For holding the outward bound TMDS symbols in the slow and fast domain
reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
reg [9:0] clk_high_speed;

reg [1:0] c2_output_bits;
reg [1:0] c1_output_bits;
reg [1:0] c0_output_bits;
reg [1:0] clk_output_bits;

wire clk_x5;
reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain

wire vsync, hsync;
wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
assign syncs = {vsync, hsync};

// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 525; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch (96 + 48)
parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
parameter vbp = 35; // end of vertical back porch (2 + 33)
parameter vfp = 515; // beginning of vertical front porch (525 - 10)

// registers for storing the horizontal & vertical counters
reg [9:0] vc;
reg [9:0] hc;
// generate sync pulses (active high)
assign vsync = (vc < vpulse);
assign hsync = (hc < hpulse);

always @(posedge clk_x5) begin
//-------------------------------------------------------------
// Now take the 10-bit words and take it into the high-speed
// clock domain once every five cycles.
//
// Then send out two bits every clock cycle using DDR output
// registers.
//-------------------------------------------------------------
c0_output_bits <= c0_high_speed[1:0];
c1_output_bits <= c1_high_speed[1:0];
c2_output_bits <= c2_high_speed[1:0];
clk_output_bits <= clk_high_speed[1:0];
if (latch_high_speed[2]) begin // pixel clock 25MHz
c0_high_speed <= c0_symbol;
c1_high_speed <= c1_symbol;
c2_high_speed <= c2_symbol;
clk_high_speed <= 10'b0000011111;
latch_high_speed <= 3'b000;
if (hc < hpixels)
hc <= hc + 1;
else
begin
hc <= 0;
if (vc < vlines)
vc <= vc + 1;
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

//SRAM address counter
always @(posedge clk100) begin

if (reset) begin
addr <= 0;
end

else begin
addr <= addr+1;
end

end


//REC control

always @(posedge clk100) begin

b <= io;
a <= data_in;
if (key[0]==1) begin
we <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
end
else begin
we <= 1;
end

end

always @(*) // display 100% saturation colourbars
begin
// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------
// display white bar


if (hc >= hbp && hc < hfp)
begin
if (data_out[1:0] == 1)
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue
end
else
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b0111110000; // blue
end
end
// we're outside active horizontal range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end
// we're outside active vertical range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end

// red N
defparam hdmin2.PIN_TYPE = 6'b010000;
defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin2 (
.PACKAGE_PIN (hdmi_n[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c2_output_bits[1]),
.D_OUT_1 (~c2_output_bits[0])
);

// red P
defparam hdmip2.PIN_TYPE = 6'b010000;
defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip2 (
.PACKAGE_PIN (hdmi_p[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c2_output_bits[1]),
.D_OUT_1 (c2_output_bits[0])
);

// green N
defparam hdmin1.PIN_TYPE = 6'b010000;
defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin1 (
.PACKAGE_PIN (hdmi_n[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c1_output_bits[1]),
.D_OUT_1 (~c1_output_bits[0])
);

// green P
defparam hdmip1.PIN_TYPE = 6'b010000;
defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip1 (
.PACKAGE_PIN (hdmi_p[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c1_output_bits[1]),
.D_OUT_1 (c1_output_bits[0])
);


// blue N
defparam hdmin0.PIN_TYPE = 6'b010000;
defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin0 (
.PACKAGE_PIN (hdmi_n[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c0_output_bits[1]),
.D_OUT_1 (~c0_output_bits[0])
);

// blue P
defparam hdmip0.PIN_TYPE = 6'b010000;
defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip0 (
.PACKAGE_PIN (hdmi_p[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c0_output_bits[1]),
.D_OUT_1 (c0_output_bits[0])
);

// clock N
defparam hdmin3.PIN_TYPE = 6'b010000;
defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin3 (
.PACKAGE_PIN (hdmi_n[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~clk_output_bits[1]),
.D_OUT_1 (~clk_output_bits[0])
);


// clock P
defparam hdmip3.PIN_TYPE = 6'b010000;
defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip3 (
.PACKAGE_PIN (hdmi_p[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (clk_output_bits[1]),
.D_OUT_1 (clk_output_bits[0])
);
// D_OUT_0 and D_OUT_1 swapped?
// https://github.com/YosysHQ/yosys/issues/330


SB_PLL40_PAD #(
.FEEDBACK_PATH ("SIMPLE"),
.DIVR (4'b0000),
.DIVF (7'b0001001),
.DIVQ (3'b011),
.FILTER_RANGE (3'b101)
) uut (
.RESETB (1'b1),
.BYPASS (1'b0),
.PACKAGEPIN (clk100),
.PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
);

endmodule
Here is the pcf :

set_io hdmi_p[0] 139 -io_std SB_LVCMOS
set_io hdmi_p[2] 78 -io_std SB_LVCMOS
set_io hdmi_p[1] 80 -io_std SB_LVCMOS
set_io hdmi_p[3] 137 -io_std SB_LVCMOS
set_io hdmi_n[0] 138 -io_std SB_LVCMOS
set_io hdmi_n[2] 79 -io_std SB_LVCMOS
set_io hdmi_n[1] 81 -io_std SB_LVCMOS
set_io hdmi_n[3] 136 -io_std SB_LVCMOS

set_io key[0] 37
set_io key[1] 38
set_io key[2] 39
set_io key[3] 41
set_io key[4] 42
set_io key[5] 43
set_io key[6] 44
set_io key[7] 45
set_io key[8] 47

set_io SCK 32

set_io CMD 33

set_io LED1 29

set_io LED5 28

set_io reset 66

set_io clk100 49

set_io v_sync 97

set_io h_sync 76

set_io io[0] 7
set_io io[1] 8
set_io io[2] 9
set_io io[3] 10
set_io io[4] 11
set_io io[5] 12
set_io io[6] 19
set_io io[7] 22

set_io addr[0] 4
set_io addr[1] 3
set_io addr[2] 2
set_io addr[3] 1
set_io addr[4] 144
set_io addr[5] 143
set_io addr[6] 142
set_io addr[7] 141
set_io addr[8] 120
set_io addr[9] 121
set_io addr[10] 24
set_io addr[11] 122
set_io addr[12] 135
set_io addr[13] 119
set_io addr[14] 134
set_io addr[15] 116
set_io addr[16] 129
set_io addr[17] 128

set_io cs 25

set_io oe 23

set_io we 118
set_io r_in 52
set_io g_in 58
set_io b_in 63

 

la béchamel

For my first finished product for prepared instruments (preparedinstruments.com), the goal is to remake the hardware 7ch palimpsest.OS SRAM but with :

  • a square keyboard grid interface like the PEW design (knobs and switches replaced with keys)
  • an aluminium PCB substrate (with a single-sided board) ?
  • a simpler amp in path with only one pot
  • all 0805 and SOIC packages

I’ve been thinking about it and I can’t say that this board will be inexpensive. If you want to explore video synthesis inexpensively, you should do things for free in code on the computer. This is a physical digital logic / analog device so it will cost money.

Because it’s the first in a series, I would like this board to set the format of the others. I’m imagining them all like Ritter Sport square bars. Like the eurorack format, I would like to have a standard way of powering the boards and I need to decide on a way of jumping between the boards (VGA cables?).

I’d also like the boards and the packaging to be ecologically responsible. I don’t know if this means using an aluminium substrate (which can be easily recyclable in theory) or a flexible circuit (with either a thin plastic or stained steel stiffener – in theory recyclable).

Looking back on the synths from this year, I never really wanted to code/configure the things I made. Doing hardware synths makes sense as it puts me in a different spot in the market place, makes the designs harder to just copy, and it allows me to savour the suffering in the design process. The analog function board was ultimately underwhelming. Going the direction of installation pieces isn’t satisfying because they are too site-specific and local for my project I feel (though perhaps making films is the solution here). The gameboy design made me feel bad about the waste of buying a mini screen and the software challenge of communicating with it seems not crucial to my project (but I will finish at least the physical side of the project to test the layout). The memory boards were the coolest, and of them the layering board is the most elegant design and the most interesting in what it can do.

Here’s the new site where the béchamel will appear:

Thoughts from Vincent :

  • Connect with other people making video synths !
  • Try shooting some of your own film to put through the machines !
  • What is it you are trying to SAY ? Hmm

What is it you are trying to SAY:

I think I’m trying to say with this project is : Look at what I like doing, isn’t it fun and cool/silly/odd what is to be found ?!  More specifically, to share the preliminary results of my creative research project method. The project I guess is trying to say that it is possible and fun to a embark on an explorative creative process with technology at a low level (and not just use pre-packaged tech things) with some of the wrestling with constraints that that requires. I like that the objects are idiosyncratic, playful compositions but also that they are ornately routed and articulated. I like the message that machines can look different, and be more human and odd ? I guess I’m also trying to have fun with the ideas of usefulness and futility, so it is partially supposed to be humorous? I like the détournement of industrial objects for playful means, the implication that old things can be exciting and that deconstructing a system reveals unexpected inner worlds. I like that the deconstructing of machines is also the deconstructing of how they interact with our world, like the data they save and how they represent our information.

Thinking that Alterity, oddity, idiosyncracy and animism in objects and people and culture I think are totally up there. This was a fascination of my dad’s.

To echo some artists’ quotes that resonate :  “I don’t know what I’m looking for until I see it.” (Cindy Sherman)

“The reason I play so many sounds, maybe it sounds angry, is because I’m trying so many things at one time, you see? I haven’t sorted them out. I have a whole bag of things that I’m trying to work through and get the one essential.” (John Coltrane)

 I want to learn things all the time, I want to understand the patterns behind things. I experience science through craft. With craft I try to cultivate sensitivity in a practiced, purposeful way. If you work with marbling you know just as much about viscosity and flow than a scientist, through your fingertips in a different way. (Tauba Auerbach)

 

Try shooting some of your own film to put through the machines?

I started thinking about what I would want to film and how. I thought about the beautiful cinematography I appreciate in special films like The Souvenir I & II. I think it would be cool to shoot some video because I spend a lot of time watching videos (not the case for writing because I don’t do enough reading). I thought about recording things I see on commutes, and about the forest walk in Saclay. This has made me consider that what I find interesting about the electronics side is memory, more than just modifying things with math. How the computer represents our world is mainly impacted by its ability to remember it. I’m starting to think that this project could be about memory, putting my own memories into the computer memory. I’m thinking about Iain and Brian’s film Lifesaver and the impact it’s had on me. Also Iain’s photography in general, especially his black and white stage.

I think DRAM board could be a cool project because of how inexpensive large memory is and the memory degradation which could be really poetic in a project about memory. This goes back to Paolo’s suggestion to make a project around an image put into UV erasable EEPROM and watching it slowly degrade in the sun.

I’ve been thinking about how to record as well, with a gopro discretely or with my G2 Cannon more deliberately. I also think that humor should be the primary part of my shooting, not some wanna-be poetic stuff.

**********

Trying to design the interface :

Might need latched buttons like this :

Some all metal heavy duty toggle switches look cool in an array but I’m worried toggle switches will require too much force to turn on and will therefore be annoying all arrayed together on a tiny board.

The other option is to stick with tactile buttons and to convert them into on/off buttons with a JK flipflop. When J and K are pulled high, the outputs Q and !Q toggle every time the clock rises (once per push). This would mean I could have an LED turn on when you push too (helps in the dark for performances!) but I wouldn’t have the pushed-in versus fully extended visual feedback from the button and it would add ICs to my packed board.

I am considering doing this on two boards, the top one would be a single-sided aluminium board which would look cool but also make the top interface more durable. This board would have the leds, keys, JK flip flops. The board below could have lots of space to be laid out in a relaxed, rational way without having to dodge buttons. I could enclose it in a little box or I could leave the two boards exposed on one side or all around.

This follows from the constraint that this will be a real product. I don’t want to make a super difficult to solder and debug highly compact circuit. I want to keep things easy to repair. It also makes the device modular, I could switch a new keyboard and keep the base circuit or vice versa. I would like to keep this device affordable and not using rare / exotic components. I’m more in the robust, standard design world now that Tommy is good at working with perhaps.

*******

I just cleaned by apartment and went through my lab equipment. I will be sending tens of boards and plastic bags to the recycling which makes me sad. I would like to avoid finding myself in this situation in the future.

Check out this Soviet-developed visual programming language DRAKON (https://en.wikipedia.org/wiki/DRAKON)

It would be cool to make a flow diagram like this for the FPGA code I’ve already made.

Also this automation representation called GRAFCET (https://fr.wikipedia.org/wiki/Grafcet)

*******

Check out these user interface principles (https://en.wikipedia.org/wiki/User_interface_design):

  • Clarity: the information content is conveyed quickly and accurately.
  • Discriminability: the displayed information can be distinguished accurately.
  • Conciseness: users are not overloaded with extraneous information.
  • Consistency: a unique design, conformity with user’s expectation.
  • Detectability: the user’s attention is directed towards information required.
  • Legibility: information is easy to read.
  • Comprehensibility: the meaning is clearly understandable, unambiguous, interpretable, and recognizable.

********

Just watched a Youtube video by Code Aesthetic called Don’t Write Comments. The implication is that the code should be so readable that you don’t need to comment it. (He organizes code in such a way that the high level architecture comes through and if you want to get into the details you can check out individual functions). I’m trying to do something similar with the interface, have it be directly readable without text just based on color, button size and location.

I like the idea of being more pragmatic with the design and less obsessive about composition. The cut away in the top board could be nice because it show which color bits are going from rpi to the memory.

The question of the colors is tough – RED is for recording ? Should colors follow RGB ? What colors for clock division, and for selecting between MSB and LSB ? What about the LEDs versus the buttons ? EDIT : I’ve sinced moved to greyscale…

****

Being creative in different ways : Apollonian (rational thinking, order, logic, prudence and purity) vs. Dionysian (irrationality, chaos, passion, emotion and instinct). Electronics and logic circuits are clearly Apollonian, and the effects they create on screen are more Dionysian.

****

Looking into code bloat I learned the Unix philosophy of “do one thing and do it well”. Other programming credos :

  • less functionality (“worse”) is a preferable option (“better”)’https://en.wikipedia.org/wiki/Worse_is_better)
  • “Keep it simple, stupid!” (https://en.wikipedia.org/wiki/KISS_principle)
  • rule of least power is a design principle that “suggests choosing the least powerful [computer] language suitable for a given purpose” (https://en.wikipedia.org/wiki/Rule_of_least_power)
  • a simple (low complexity) solution to a problem of high complexity is seen as elegant. (https://en.wikipedia.org/wiki/Elegance)

**********

New Prepared Instruments github : https://github.com/preparedinstruments/bechamel

OK, here’s the final version of the boards. I will finally be able to test the one-sided aluminum surface reversed with buttons going through-hole. This is (my first) double-board design, keyboard on one and memory on the other with a ribbon jumping between them. This sampler will have two bits of data for the videos which will be new. Layers will now have different colors associated with them. The gen lock turned out to not be what I thought it was so I’m trying to sync by using V and H syncs (and their inverses) connected to the 590 freq division somehow (either to OE, CLR, or RCLK). I like this new setup to select different bits of the RPI. Cool things here are the double use of the buttons in momentary and latched mode with led indicators. New in this version will also be the triple diode packages for the VGA out.

I’ve taken my favorite interface and mixed it with my favorite circuit, let’s see how it works out ! At least I got to use up a bunch of components I already had lying around. Though, I have to say that no one could pay me enough money to assemble one of these myself, and despite my best efforts it is not minimal and easy to put together. Another issue is the fact that I don’t have the skill of imagining what “customers” want, I seem to be more focused on what I want. Does that make me a wanne-be artist as opposed to a wanna-be designer ?

And some renderings :

 

**********

Having a think about what my hardware synths could do better than computers. There is the tangible interface, and the speed and fixed-low latency of using hardware to manipulate data instead of software. While I was once again looking into what I can do with a simple FPGA I came across :

  • this Elphel NC393 camera device board for handling multiple video streams using an FPGA https://wiki.elphel.com/wiki/File:Nc393-dev-sm.jpeg. Combining multiple video streams, or overlaying, sounds like an application? But in VGA you can do this with just a pot !
  • User Jeremy from eevblog’s comment about FLIR explaining that they use FPGAs in order to do “spatial (across pixels) and temporal (across time) filtering simultaneously”.
  • video transcoding from one resolution ‘https://en.wikipedia.org/wiki/Transcoding)
  • real-time edge detection
  • Mister FPGA video game console has a bunch of retro CRT scanline filters here https://boogermann.github.io/Bible_MiSTer/getting-started/extras/video-filters/
  • FPGA student projects from this Cornell class : http://people.ece.cornell.edu/land/courses/ece5760/FinalProjects/

********************

I think that this project is becoming about memory, and possibly the parallels between human memory and computer memory. I wonder what role the FPGA would have here. Would it be positioned in between the video input and the memory to act as a kind of variable filter ? I can imagine an interface that would allow you to select between many different filters by moving your hand like on a theremin or an array of trackballs?

*****

Check out this visualization of randomness :

****

First feedback : it needs HDMI – Sam

This Texas Instruments TFP410PAP is back in stock at Mouser and seems easy enough to integrate onto the board at 8euros. It can be controlled without I2C so it could be connected directly to the raspberry pi DEN, HSYNC, VSYNC, color data.  Black Mesa Labs did a board using this IC here : https://blackmesalabs.wordpress.com/2017/12/15/bml-hdmi-video-for-fpgas-over-pmod/

I also found a VGA to HDMI converter on amazon : FOINNEX Adaptateur VGA vers HDMI avec Audio, Convertisseur VGA a HDMI Vieux PC vers TV Moniteur, 1080P VGA to HDMI Adapter pour Anciens Ordinateur 

TI also makes the TFP401x TI PanelBus™ Digital Receiver so I could have HDMI IN and HDMI OUT with the addition of 15 euros of chips…The alternative is to learn to do this with the FPGA.

*****

Check out these snazy metal container for this synth circuit :

Division 6 Business Card Synthesizer PCB and IC

https://store.synthrotek.com/Division-6-Business-Card-Synthesizer-PCB-and-IC_p_829.html

Check out this beautiful project Sam sent my by Jurg Lehni which compares different screen technologies : https://juerglehni.com/works/four-transitions

******

Thinking about modularity. If I wanted to have a signal chain with filters etc before the memory. I would need a way of daisy-chaining the power, and a way of patching cables between modules. I want it such that the thing functions with just one module but also can with multiple modules too.

Would the video in/out have to be HDMI to be in line with modern VJ practice ? Found some Chinese chips that do HDMI to VGA like the Lontium LT8511A and the Quintile CH7101B. I guess I would need one of these plus an HDMI out chip for each board for them to be truly modular.

I am thinking right now of a simple custom filter like the one in Photoshop (Nice explanation here : https://ian-albert.com/old/custom_filters/) accomplished with an FPGA and some kind of cool interface (track ball matrix ? capacitive touch array ?) :

Photoshop's custom filter dialog

pixel(x,y) = (sum(i=1..5)(sum(j=1..5)(pixel(i+x,j+y)*weight(i,j))))/scale + offset

***************

Check out this company OKW that makes cool modular knobs just outside of Paris : https://www.okw.fr/fr/Boutons-de-commande

 

**************

Working with the array of simple, robust, electronic components (switches, knobs, linear pots, track balls, etc.). Trying to think about how they could be daisy-chained together.

 

****************

Received and soldrerd PCBs !

  • The aluminum layer can be engraved ! I could put “bechamel” on it !
  • The standoff holes are too small for standard size.
  • The boards are not perfectly the same dimensions.
  • Perhaps I should have chosen white for the FR4 board instead of green
  • No caps on the keyboard board
  • Blurg, soldering components on the aluminum board without a heated plate is near impossible. I could have at least selected bigger format ICs.
  • The latched keys which are connected to the output buffer enables aren’t really useful. They should be constantly on as the amp can always be just turned down to turn off the channel.

 

ERRATA MOTHERBOARD :

  • From now on I think I will solder with a stencil and an oven, it’s too much work to solder manually.
  • The latched keys don’t activate the same channels via buffer that they activate via AND gate to analog switch SEL.
  • Also the text BLU, RED etc next to the knobs isn’t really accurate
  • Should have VCC and GND on the silkscreen next to keyboard jumper
  • Pin 13 and 11 of the 590 should by default be connected together on the next board
  • I replaced the caps at the VGA out with 0ohm jumpers because I couldn’t see anything passing through on the oscilloscope
  • I should add the turn off switch for rpi
  • I am considering getting rid of the integrated rpi, despite its advantages, for a more straightforward HDMI IN or VGA IN. It adds considerable thickness to the board with the bulky 40 pin jumper situation and the fact that it is headless makes debugging a real pain.

ERRATA KEYPAD :

  • I’m not sure the idea of using the aluminium sided boards really works, it’s all a bit too risky in terms of short circuits and it’s a real pain to solder (I need a heated plate, a nice microscope, loads of flux and a soldering iron). Fabien’s suggestion is to make the holes larger, so there is little risk of the pins touching the aluminium substrate, and then wiring flexible wire to make the necessary connections. Perhaps add some glue to keep the keys in place and move all the circuitry to the reverse side of the motherboard.
  • Fabien had the idea of using the square holes of the mechanical switches possibly to mount them in the metal, perhaps they would even clip into place ?
  • I roasted the ICs during soldering I think, the LEDs and JK latches are not working as expected.
  • The added height for the two female ribbon cable connectors butting up against each other is insane. If it’s possible to just plug one board directly into the other you can achieve some nice compact sandwich board.
  • Once again the 2mm standoff holes are too tiny
  • The slot is perfect and the LEDs passing through the holes works as expected (though I should really use insulation on the two legs).

**************

Could I use coils from floppy disk player in a matrix as capacitive touch array ?

PCB mounted motor coils in a floppy drive : r/electronics

Could I make a super low resolution LED monitor to replace the HDMI IN and HDMI OUT DSLR field monitor ? (https://www.amazon.fr/Feelworld-Moniteur-Monitor-1920×1080-Peaking/dp/B07J5C98NW/ref=sr_1_1_sspa?crid=2SJEJTOJ1C6HB&keywords=Cam%C3%A9ra+Moniteur+DSLR&qid=1707557998&s=electronics&sprefix=cam%C3%A9ra+moniteur+dslr+%2Celectronics%2C89&sr=1-1-spons&sp_csd=d2lkZ2V0TmFtZT1zcF9hdGY&psc=1) :

I could also just use the simple rpi 5″ HDMI screen.

Other connection options if I had HDMI out :

Would it be smart to keep a VGA out as a plan B if ever the HDMI stopped working ?

It would be super useful to have a micro usb out to power the portable screen / pico projector. If so not much space left, would need to maybe bring the USB C power cable to the rpi side.

*************

Check out Jim Campbell’s low res display :!

Image may contain Rug Light Text and Lighting

Jim Campbell

Jim Campbell

References to reconstructing memories from the mind too !

***************

Setting up another rpi to loop VLC videos. I used primarily this tutorial: https://forums.raspberrypi.com/viewtopic.php?t=17051

First off, I kept the language as english to simplify things (despite having a french keyboard). I believe I did the rpi Lite installation (and then downloaded VLC?) but I’m not 100% sure.

In the place of

nano /home/pi  I have my user name :

nano /home/marrs

I also needed to go to View > Show Hidden to see the .config file then autostart then autovlc.desktop

nano /home/marrs/films/.config/autostart/autovlc.desktop

NOTE : This can’t be a text file, easiest way is to create the file directly in the terminal to avoid any problems as explained here :https://learn.sparkfun.com/tutorials/how-to-run-a-raspberry-pi-program-on-startup/method-2-autostart

I put this in the file (there is nothing else there)

[Desktop Entry] 
Type=Application
Exec=vlc --loop --fullscreen /home/marrs/playlist.m3u

Then control+O to save the file
 I put the m3u playlist here : home/marrs/playlist.m3u
The playlist file looks like this :
/home/marrs/Desktop/films/2001.mp4
etc. and avoid spaces !!
I created a folder called films on my desktop with the films.autostart
To share videos from another computer via the SD card, you can create a folder in the part of the SD card that is accessible, and whatever folder you make there can be found under bootfs / firmware
Finally, AFTER DOING THE FILM FILE TRANSFER WITH THE HDMI SCREEN, I modified the config.txt file to output DPI and not respond to HDMI:

# For more options and information see
# http://rpf.io/configtxt
# Some settings may impact device functionality. See link above for details

# uncomment if you get no picture on HDMI for a default "safe" mode
#hdmi_safe=1

# uncomment the following to adjust overscan. Use positive numbers if console
# goes off screen, and negative if there is too much border
#overscan_left=16
#overscan_right=16
#overscan_top=16
#overscan_bottom=16

# uncomment to force a console size. By default it will be display's size minus
# overscan.
#framebuffer_width=1280
#framebuffer_height=720

# uncomment if hdmi display is not detected and composite is being output
#hdmi_force_hotplug=1

# uncomment to force a specific HDMI mode (this will force VGA)
#hdmi_group=1
#hdmi_mode=1

# uncomment to force a HDMI mode rather than DVI. This can make audio work in
# DMT (computer monitor) modes
#hdmi_drive=2

# uncomment to increase signal to HDMI, if you have interference, blanking, or
# no display
#config_hdmi_boost=4

# uncomment for composite PAL
#sdtv_mode=2

#uncomment to overclock the arm. 700 MHz is the default.
#arm_freq=800

# Uncomment some or all of these to enable the optional hardware interfaces
dtparam=i2c_arm=off
#dtparam=i2s=on
dtparam=spi=off

# Uncomment this to enable infrared communication.
#dtoverlay=gpio-ir,gpio_pin=17
#dtoverlay=gpio-ir-tx,gpio_pin=18

# Additional overlays and parameters are documented /boot/overlays/README

# Enable audio (loads snd_bcm2835)
dtparam=audio=on

# Automatically load overlays for detected cameras
camera_auto_detect=1

# Automatically load overlays for detected DSI displays
#display_auto_detect=1

# Enable DRM VC4 V3D driver
#dtoverlay=vc4-kms-v3d
max_framebuffers=2

# Disable compensation for displays with overscan
disable_overscan=1

[cm4]
# Enable host mode on the 2711 built-in XHCI USB controller.
# This line should be removed if the legacy DWC2 controller is required
# (e.g. for USB device mode) or if USB support is not required.
otg_mode=1

[all]

[pi4]
# Run as fast as firmware / board allows
arm_boost=1

[all]

gpio=0-9=a2
gpio=12-17=a2
gpio=20-25=a2
gpio=26-27=a2

dtoverlay=dpi24

enable_dpi_lcd=1
display_default_lcd=1

dpi_group=2
dpi_mode=87

extra_transpose_buffer=2

dpi_output_format=0x7f216
dpi_timings=720 0 40 48 128 720 0 13 3 15 0 0 0 60 0 50000000 1

*************

A new vision for this project : it’s a lo-fi device that takes HDMI and outputs HDMI but allows all kinds of very low res transformations like super low resolution GIFs, and saving every Nth frame of a film.

****TESTING ***

  • Get sync sorted on this board. **EDIT** Unfortunately taking HSYNC, VSYNC and their inverses, and sending them to the 590 on various pins is not syncing the video. Thinking back, I think I managed this only when I was ANDing a bunch of freq divisions and then inverting the output to record images only every nth frame. *EDIT* OK I’ve got something taking VSYNC as the freq and the AND’ind the outputs to be used to reset the 4040 counters.
  • Test the keyboard interface experience. ***EDIT** oof this is painful to do with jumpers and breadboards. Everything seems to be very sensitive to wierdness. It makes me want to eliminate the double board concept and just stick with hard soldered stuff on a single board. In the moments when things were briefly working it was super cool to control things with the keyboard.
  • See what other video distortion is possible with the circuit
  • I tried out the new HDMI converter, it works well.
  • I figured out that you can save a multicolor multichannel image by recording on several channels at once !
  • I like the idea of avoiding mission creep – not going into the screen or video codec business but instead staying with the theme of the project : video memory recording and playback.
  • I like the idea of emphasizing how little memory there is here (4MB), and how I am trying to use every part of the bison rather than going for more memory.
  • the new board should have usb micro female plugs to power an rpi, a VGA to HDMI converter, or the 5″ HDMI screen.
  • No more connectors between boards that are mirrored… Thinking of a single-sided board where the buttons relate the the function (latched push button for things that need to be latched like the freq sel) keyboard keys for selecting layers to record to. I thought that having an array of the same button would make things clean but it ends up being confusing what button does what. I like the idiosyncratic and non orthogonal matrix approach.
  • Just realized that I only need the single pole analog switch and don’t need the double pole anymore.
  • I have higher quality pots I could use for smoother action

Super cool to see everything on the tiny screen actually (but when I plugged in everything from the v synth I think it went above the fuse limit so I’ll have to FIX THIS IN THE NEXT VERSION if I want all to be powered by the board):

*

Trying to make a list of cool new video distortions possible with this board :

  • RCK and CLK of 590 could be connected to different frequencies
  • could have two different counter pairs for two memories that could be desynchronized

**********

A quick redesign :

  • Different components for different functions that they control (flat pots for inputs)
  • Added DIP switches to turn on and off diff V SYNC divisions going to 4040 counter reset
  • Only two speed buttons now, and they are physically latched
  • Nicer quality top facing pots
  • HDMI in and out with TI chips
  • got rid of some replaceable components
  • two micro USB connections to allow for these options :

 

*****

Trying out some hyper symmetrical design here.

Some considerations :

  • Leaving enough room to be able to comfortably twist the knobs
  • having mechanic keys close to one another so that they can be easily tapped with adjacent fingers
  • associating each key with it’s respective knob spatially and clustering controls that are thematically related (like the dip switch and the two frequency setting pushbuttons which are both temporal)
  • components that extend beyond the board on the top and bottom leaving room for hands on the keys and knobs
  • Tommy thinks it should be hand held.

 

And a more controller-like option :

*******

Check out Vincent Rioux’s muq machine and how he show the grid of screen shots :

 

***

I want to make videos like this guy makes experimental sound sets :

****

I’m going to remake the keyboard board (but on FR4, with connector aligned to motherboard, no latched colors to activate channels) and add the sync lock functionality. This will allow me to finish that project and test it easily.

The videos I have already of the board functioning are cool : diff colors allow for multiple images to share the same frame and be legible. The freeze frame(s) is very cool and being able to freeze different chunks of time I think will add a lot of things to test.

After this board I’ll move back to FPGA as they represent the future of the project I think (also the NO SCHOOL workshop this summer). Before I make I new board I should play more with the FPGA SRAM boards I have already made. Can I , for instance, reproduce the bechamel board with an FPGA? Perhaps it is time to upgrade to an FPGA with more LUTs, even if it is harder to solder in Ball Grid Array packaging (and then why not SRAM in BGA also), and the HDMI helper chips.

The dream would be able to throw 20 of the new boards in the oven and have them all work at the end. I could then send them to friends around the world ! Would be clever to use capacitive touch in this case to avoid needing a physical interface like so : https://github.com/stnolting/captouch.

TTP226 8-way touch module Capacitive touch switch digital touch sensor - Calcutta Electronics

I could also design a silicone ruber keypad for it ! https://en.wikipedia.org/wiki/Silicone_rubber_keypad

Looks like an ESP32 with SD card can generate video as input, that or a rpi integrated into the board.

PCB Design for FPGA SoMs and Carrier Boards | NWES Blog

This might be one of those “already solved” problems which isn’t that much fun to explore…

***

Next steps for the HX8K board :

  1. FFMPEG can output into monob or monow 1 bit pixel format and different dimensions. Test making a low resolution video and putting it on an SD card and having Arduino read it and transfer it to FPGA on the gameboy console board I made. (This will test if can replace rpi or HDMI IN with SD card in on next board)
  2. Try again to see if HDMI out is possible with FPGA using the two boards I made to test this. (This will decide if need an HDMI helper chip or not on next board)
  3. Try to use internal FPGA BRAM to save a screenshot on the Villette Makerz board. (This will test if need SRAM or not on the next board).
  4. Replace mechanical keys with capacitive touch so everything is as light as possible.
  5. Plan to get JLC PCB to assemble it ?

****

New top board, no longer aluminum, no more LEDs, which has the V SYNC ADD circuitry, debounced keys, and a female header to connect directly to the motherboard.

 

*****

Realized that I haven’t used the BRAM on the FPGA and that I could store low res recordings there. *EDIT* But it’s only 64K in total for the HX1K which is very little space. Probably more useful to store a mini buffer before sending it along to the SRAM. I’ve tried to create BRAM primitives in IceCube but it’s not working so far. This code from TinLethax appears to work in IceCube :

module BRAM(
input R_CLK,
input W_CLK,

input [7:0]BRAM_IN,
output reg [7:0]BRAM_OUT,

input [11:0] BRAM_ADDR_R,
input [11:0] BRAM_ADDR_W,

input B_CE_W,
input B_CE_R);

reg [7:0] mem [3003:0];

always@(posedge R_CLK) begin// reading from RAM sync with system clock 
if(!B_CE_R)
    BRAM_OUT <= mem[BRAM_ADDR_R];   
end 

always@(posedge W_CLK) begin// writing to RAM sync with Slave SPI clock.
if(!B_CE_W)
    mem[BRAM_ADDR_W] <= BRAM_IN;
end

endmodule

 

 

This code from the Lattice Memory Guide (file:///C:/Users/jm225306/Downloads/MemoryUsageGuideforiCE40Devices.pdf) also works :

 module ram (din, addr, write_en, clk, dout);// 512x8
parameter addr_width = 9;
parameter data_width = 8;
input [addr_width-1:0] addr;
input [data_width-1:0] din;
input write_en, clk;
output [data_width-1:0] dout;
reg [data_width-1:0] dout; // Register for output.
reg [data_width-1:0] mem [(1<<addr_width)-1:0];

always @(posedge clk)
begin
if (write_en)
mem[(addr)] <= din;
dout = mem[addr]; // Output register controlled by clock.
end
endmodule 

After sorting out the parameters BRAM_ADDR_WIDTH and BRAM_DATA_WIDTH, this code also works : https://github.com/Megamemnon/bram/blob/master/bram.v

****

I am swinging back to the SD card and a very simple video codec begin read/or initialized by Arduino and sent along to the FPGA. I think that this could lead to some new kinds of images and animations, and I think it could move away from having a computer generate video which kind of seems like a cop out for an experimental low-fi video device.

I am also swinging back in the direction of mechanical keys and, if I stick with discrete logic chips at least, by extension the two board option. Not having a real physical interface, and replacing with capacitive touch sensing, seems like losing the plot of an instrument like this. However, I am still interested in learning if I can do everything simpler with an FPGA and eventually add cool functionality like digital filtering that is impossible at the moment.

If I could output VGA and HDMI it would be more robust. It would basically have power in and video out only.

So basically, I am imagining a new corrected/optimized version of the bechamel (higher quality pots, streamlining components, etc.) that takes SD video in with an atmega 328p, outputs in HDMI and VGA, but basically has the same look as before with a 4×4 keyboard and motherboard. This new version could conceivably replace logic with an FPGA also but that might be too much of a jump ?

***

Downloaded ffmpeg windows build here : https://www.gyan.dev/ffmpeg/builds/

I tried the following command as a test and it worked nicely :

C:\ffmpeg-2024-02-29-git-4a134eb14a-full_build\bin>ffmpeg -i C:\ffmpeg-2024-02-29-git-4a134eb14a-full_build\bin\input\input.mp4 output.avi

To see the list of formats I could output to I typed in ffmpeg -pix_fmts

There is monow and monob each at 1 bit depth and 1 bits per pixel.

But now I am realizing that I probably want to output just a series of simple bmp images, rather than a video format perhaps.

****

Trying to debug Ice40HXIK HDMI :

  • I soldered pin 19 to 5V. No change. I could also solder the HDMI metal case to GND with the jumper.
  • It appears that the HDMI signals only appear when some keys are activated, I guess an imperfect connection somewhere ?
  • Tested on two diff HDMI screens, not being recognized.
  • Looked a the signals with the ‘scope and I’m not seeing differential signals, they are the same signal twice.
  • I’m getting various warnings from IceCube when compiling the code (minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation by Author: Mike Field <hamster@snap.net.nz>).
  • It is kind of working now after I bitwise (~) inverted the hdmi n pins 🙂

These are the differential signals before inverting properly :

These are the same differential signals after inverting properly :

Here is the code for the Villette Makers Paris Electronic Week board with modifications :

The PCF :

set_io hdmi_p[0] 139 -io_std SB_LVCMOS
set_io hdmi_p[2] 78 -io_std SB_LVCMOS
set_io hdmi_p[1] 80 -io_std SB_LVCMOS
set_io hdmi_p[3] 137 -io_std SB_LVCMOS

set_io hdmi_n[0] 138 -io_std SB_LVCMOS
set_io hdmi_n[2] 79 -io_std SB_LVCMOS
set_io hdmi_n[1] 81 -io_std SB_LVCMOS
set_io hdmi_n[3] 136 -io_std SB_LVCMOS

set_io clk100 49

Mike Field’s minimal HDMI verilog with just the addition of inverted hdmi n pins :

`default_nettype none // disable implicit definitions by Verilog
//-----------------------------------------------------------------
// minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
//
// Author: Mike Field <hamster@snap.net.nz>
//
// DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
// value is mapped to one or two 10-bit symbols, depending on how
// many 1s or 0s have been sent. This makes it a DC balanced protocol,
// as a correctly implemented stream will have (almost) an equal
// number of 1s and 0s.
//
// Because of this implementation quite complex. By restricting the
// symbols to a subset of eight symbols, all of which having have
// five ones (and therefore five zeros) this complexity drops away
// leaving a simple implementation. Combined with a DDR register to
// send the symbols the complexity is kept very low.
//-----------------------------------------------------------------

module top(
clk100, hdmi_p, hdmi_n
);

input clk100;
output [3:0] hdmi_p;
output [3:0] hdmi_n;

// For holding the outward bound TMDS symbols in the slow and fast domain
reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
reg [9:0] clk_high_speed;

reg [1:0] c2_output_bits;
reg [1:0] c1_output_bits;
reg [1:0] c0_output_bits;
reg [1:0] clk_output_bits;

wire clk_x5;
reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain

wire vsync, hsync;
wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
assign syncs = {vsync, hsync};

// video structure constants
parameter hpixels = 800; // horizontal pixels per line
parameter vlines = 525; // vertical lines per frame
parameter hpulse = 96; // hsync pulse length
parameter vpulse = 2; // vsync pulse length
parameter hbp = 144; // end of horizontal back porch (96 + 48)
parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
parameter vbp = 35; // end of vertical back porch (2 + 33)
parameter vfp = 515; // beginning of vertical front porch (525 - 10)

// registers for storing the horizontal & vertical counters
reg [9:0] vc;
reg [9:0] hc;
// generate sync pulses (active high)
assign vsync = (vc < vpulse);
assign hsync = (hc < hpulse);

always @(posedge clk_x5) begin
//-------------------------------------------------------------
// Now take the 10-bit words and take it into the high-speed
// clock domain once every five cycles.
//
// Then send out two bits every clock cycle using DDR output
// registers.
//-------------------------------------------------------------
c0_output_bits <= c0_high_speed[1:0];
c1_output_bits <= c1_high_speed[1:0];
c2_output_bits <= c2_high_speed[1:0];
clk_output_bits <= clk_high_speed[1:0];
if (latch_high_speed[2]) begin // pixel clock 25MHz
c0_high_speed <= c0_symbol;
c1_high_speed <= c1_symbol;
c2_high_speed <= c2_symbol;
clk_high_speed <= 10'b0000011111;
latch_high_speed <= 3'b000;
if (hc < hpixels)
hc <= hc + 1;
else
begin
hc <= 0;
if (vc < vlines)
vc <= vc + 1;
else
vc <= 0;
end
end
else begin
c0_high_speed <= {2'b00, c0_high_speed[9:2]};
c1_high_speed <= {2'b00, c1_high_speed[9:2]};
c2_high_speed <= {2'b00, c2_high_speed[9:2]};
clk_high_speed <= {2'b00, clk_high_speed[9:2]};
latch_high_speed <= latch_high_speed + 1'b1;
end
end

always @(*) // display 100% saturation colourbars
begin
// first check if we're within vertical active video range
if (vc >= vbp && vc < vfp)
begin
// now display different colours every 80 pixels
// while we're within the active horizontal range
// -----------------
// display white bar
if (hc >= hbp && hc < (hbp+80))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display yellow bar
else if (hc >= (hbp+80) && hc < (hbp+160))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
// display cyan bar
else if (hc >= (hbp+160) && hc < (hbp+240))
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display green bar
else if (hc >= (hbp+240) && hc < (hbp+320))
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b0111110000; // blue
end
// display magenta bar
else if (hc >= (hbp+320) && hc < (hbp+400))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display red bar
else if (hc >= (hbp+400) && hc < (hbp+480))
begin
c2_symbol = 10'b1011110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b0111110000; // blue
end
// display blue bar
else if (hc >= (hbp+480) && hc < (hbp+560))
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b1011110000; // blue
end
// display black bar
else if (hc >= (hbp+560) && hc < hfp)
begin
c2_symbol = 10'b0111110000; // red
c1_symbol = 10'b0111110000; // green
c0_symbol = 10'b0111110000; // blue
end
// we're outside active horizontal range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end
// we're outside active vertical range
else
begin
c2_symbol = 10'b1101010100; // red
c1_symbol = 10'b1101010100; // green
//---------------------------------------------
// Channel 0 carries the blue pixels, and also
// includes the HSYNC and VSYNCs during
// the CTL (blanking) periods.
//---------------------------------------------
case (syncs)
2'b00 : c0_symbol = 10'b1101010100;
2'b01 : c0_symbol = 10'b0010101011;
2'b10 : c0_symbol = 10'b0101010100;
default : c0_symbol = 10'b1010101011;
endcase
end
end

// red N
defparam hdmin2.PIN_TYPE = 6'b010000;
defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin2 (
.PACKAGE_PIN (hdmi_n[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c2_output_bits[1]),
.D_OUT_1 (~c2_output_bits[0])
);

// red P
defparam hdmip2.PIN_TYPE = 6'b010000;
defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip2 (
.PACKAGE_PIN (hdmi_p[2]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c2_output_bits[1]),
.D_OUT_1 (c2_output_bits[0])
);

// green N
defparam hdmin1.PIN_TYPE = 6'b010000;
defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin1 (
.PACKAGE_PIN (hdmi_n[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c1_output_bits[1]),
.D_OUT_1 (~c1_output_bits[0])
);

// green P
defparam hdmip1.PIN_TYPE = 6'b010000;
defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip1 (
.PACKAGE_PIN (hdmi_p[1]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c1_output_bits[1]),
.D_OUT_1 (c1_output_bits[0])
);


// blue N
defparam hdmin0.PIN_TYPE = 6'b010000;
defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin0 (
.PACKAGE_PIN (hdmi_n[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~c0_output_bits[1]),
.D_OUT_1 (~c0_output_bits[0])
);

// blue P
defparam hdmip0.PIN_TYPE = 6'b010000;
defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip0 (
.PACKAGE_PIN (hdmi_p[0]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (c0_output_bits[1]),
.D_OUT_1 (c0_output_bits[0])
);

// clock N
defparam hdmin3.PIN_TYPE = 6'b010000;
defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmin3 (
.PACKAGE_PIN (hdmi_n[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (~clk_output_bits[1]),
.D_OUT_1 (~clk_output_bits[0])
);


// clock P
defparam hdmip3.PIN_TYPE = 6'b010000;
defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
SB_IO hdmip3 (
.PACKAGE_PIN (hdmi_p[3]),
.CLOCK_ENABLE (1'b1),
.OUTPUT_CLK (clk_x5),
.OUTPUT_ENABLE (1'b1),
.D_OUT_0 (clk_output_bits[1]),
.D_OUT_1 (clk_output_bits[0])
);
// D_OUT_0 and D_OUT_1 swapped?
// https://github.com/YosysHQ/yosys/issues/330


SB_PLL40_PAD #(
.FEEDBACK_PATH ("SIMPLE"),
.DIVR (4'b0000),
.DIVF (7'b0001001),
.DIVQ (3'b011),
.FILTER_RANGE (3'b101)
) uut (
.RESETB (1'b1),
.BYPASS (1'b0),
.PACKAGEPIN (clk100),
.PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
);

endmodule

Proof 🙂 !

After moving my figure around the board it seemed like the amplifier was creating noise. I removed it and now things seem to work better, however, nothing happens unless I push a button. Could it be because the buttons pass right next to the 100MHz Clock and input pin 49 ? I wonder if I should also make the differential signals closer to the HDMI out. Not sure how I’ll make this work with a noisy board having multiple op amps.

Best thing that has improved reliability has been to push hard on the oscillator one time. It still isn’t solid though, pushing buttons can make it lose connection. I’m going to have to be very careful how I reduce noise in future versions, possibly dividing up the board and having distinct zones. Maybe the double board is more noise resistant also ?

*EDIT* I resoldered the 100MHz clock and now everything is 100% working even when I mash buttons and lay hands on all parts of the board. It also works on the mini HDMI without any issues. YAY !

***

Trying to debug the Ice40HX4K on the gameboy board. I can now upload code, and the clock looks fine, but it’s not outputting anything to the test led pin other than being pulled constantly high.

***

Thought : They (the people who may one day own this kit?) will need an rpi to program the FPGA so I can assume they will have one to produce video. The FPGA would just need to be conected to the programming pins and be able to switch between programming mode and sending pixels. The rpi could actually connect to the same output screen as the video device if there were a switch. It would be more like a marriage of rpi and device, and with a wireless keyboard it could be nice ? At the same time I feel like it complicates the object, and the lo-fi-ness. It also begs the question : why not do everything on the rpi ?

Another thought: if it is HDMI out there are no outputs amps, it’s all digital. I could make a new version without the amps but with the keys. I don’t think it makes sense to make the board with both VGA and HDMI in this case. So, next version could be my first HDMI FPGA synth that replicates the functionality of the bechamel (diff I/O pins as video channels). Not sure if one or two layers.

Might be smart to reuse this board for the NO SCHOOL ? And therefore chose a cheap SRAM. And if this is the case, I can maybe select an inexpensive SRAM (like 1MB or 2MB that runs at 3.3V) and show them how to record incoming video. Perhaps keeping HDMI and VGA out would be smart. Also, perhaps a single sided board too, to minimize cost and weight of this thing. (So basically add small SRAM to the villette makerz 3×3 board and make one key a double if there isn’t enough room). So, forget about SD card video, higher LUT FPGAs and BGA soldering.

  • In order to prep for this I could do some more experiments with SRAM FPGA board reproducing béchamel functionality and doing some other low LUT requiring FPGA things.
  • I could also program FPGA with rpi to prep

Just one other thought : if FPGA is too challenging for beginners to program, should I make a board with Arduino that commands FPGA to do stuff ?

Another thought : One reason why I have had to keep on ordering new boards is because I always make boards that don’t have every element on it. For example I’ve made boards with SRAM and VGA but not SRAM and HDMI. Perhaps I should include in the following board an atmega 328 and an SD card if I ever get around to doing that in the future. I won’t need to populate it now but basically I can transition into coming up with cool codes and can kind of freeze the board developement.

I can also start by resoldering just the parts of SRAM card that I need to go further in testing bechamel functionality : FPGA, 16MB SRAM, VGA out. This would help understand how important having 16MB of RAM is, because 2MB would be a lot cheaper.

***

Oof, just tried to get the SRAM FPGA back up and running and the complexity of the board, plus the amount of different code versions on the website. The fact that I never sorted out the faulty SRAM pin, the board needing to be bent just so for the counting to start, the hand soldered rpi in colors (can’t remember why I did that), and that the rpi is now restarting itself everytime for some reason. All of it makes me think that I should KEEP THE NEXT BOARD SIMPLE. I also need a proper file structure to store verilog code versions as IceCube loads files and doesn’t just keep the text that you have written in the program.

Tried getting the FTDI board working with HDMI despite not having the differntial resistors. Confirmed that there is differential signals, but it doesn’t appear to work for an HDMI screen.

The mini projector is awesome though :

Also works on the small screen :

****

A thought : In order to save time, I think I should do the exercise of assuming that I can manage something technically, and doing the thought experiment of trying to understand what would be possible if I can indeed make the thing. For instance, with the HDMI FPGA, I can assume right now that I can do several colors only, but cannot change the intensity of the colors. Having four channels would reaquire having solid colors that overlap.

*****

TLDR : Follow this (https://github.com/anse1/olimex-ice40-notes) but in the place of -w dump put your file name and manually make sure it is exactly 4194304 bytes long (don’t try their -dd code which I can’t get to work).

The long painful version :

Some other research around programming FPGA with rpi :

  • https://github.com/squarewavedot/iceprog-rpispi
  • https://j-marjanovic.io/lattice-ice40-configuration-using-raspberry-pi.html
  • https://github.com/plex1/raspice40
  • https://www.olimex.com/wiki/ICE40HX1K-EVB#Get_started_under_Linux  IF SCROLL TO THE RPI SECTION

I am following the OLIMEX tutorials because it seems like the best documented and least in progress. Starting with this page (https://github.com/plex1/raspice40) I began by downloading rapice40 and uploading putting an FPGA .bin file I had generated with IceCube2. I activated SSH and SPI on the rpi. I cd’d to the raspice40-master directory and typed sh install.sh. After this I tried to program with program.sh programming_file.bin but it said it didn’t have flashrom. So I started following this website instead (https://www.olimex.com/wiki/ICE40HX1K-EVB#Get_started_under_Linux) and I typed:

git clone https://github.com/flashrom/flashrom.git

Then :

cd flashrommake

sudo CONFIG_ENABLE_LIBPCI_PROGRAMMERS=no CONFIG_ENABLE_LIBUSB0_PROGRAMMERS=no CONFIG_ENABLE_LIBUSB1_PROGRAMMERS=no install

I made the connections between my board and the raspberry pi Zero pins (not the same as the rpi 2+ pins as far as I can see) as per this image :

And then :

claim GPIO24 for sysfs-control

echo 24 > /sys/class/gpio/export

Pull GPIO24 low to put the ice40 into reset. The cdone-LED on the board should turn off.

echo out > /sys/class/gpio/gpio24/direction

Read the flash chip at 20MHz (for short cabling)

flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=20000 -r dump

I tried this but when I looked inside the dump file it was empty.

Simply swap -r for -w to write the dump back

flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=20000 -w dump

As generated bitstreams are smaller than size of the flash chip, you need to add padding for flashrom to accept them as image. I used the follwing commands to do that:

tr '\0' '\377' < /dev/zero | dd bs=2M count=1 of=image
dd if=my_bitstream conv=notrunc of=image

*EDIT*

This is where I am getting stuck. It can even read the flash chip^on the FPGA board and identify it correctly ! It just keeps on saying that the binary fie is too small to upload.

I can now use the dd command to change the size of the file, and I know the file needs to be 4194304b in size. But it wants that in another number format, but when I try 4194kB it makes it only 31.5 something. I will try asking it for 4M next time and see if that works.

Looking in to padding with dd in different ways to get it up to the required 4194304b size but so far stuck here.

I eventually just used the following command in windows cmd  fsutil file createnew <filename> <length> :

fsutil file createnew FPGA.bin 4194304

I then opened this file in hexed.it, and pasted the binary file from IceCube2 into this file (make sure to replace the data and not add it or else you will need to move to the address 4194304 and erase what comes after). I exported it as a bin file, and passed this file (which was precisely the right size) NOT to the program.sh (which I now understand was automating the padding of the input file) but with the following commands in sequence (from https://github.com/anse1/olimex-ice40-notes) :

echo 24 > /sys/class/gpio/export

echo out > /sys/class/gpio/gpio24/direction

flashrom -p linux_spi:dev=/dev/spidev0.0,spispeed=20000 -w FPGA.bin

And I get this success message :

However when I tried it again it could not see the FLASH chip. But to fix this I manually plugged RESET to GND thanks to Fabien’s advice.

*EDIT*

Deassert creset to let the ice40 read the configuration from the bus:

echo in > /sys/class/gpio/gpio24/direction

 

****

Tried an HDMI splitter and it didn’t split. Also tried to send HDMI to USB C using my adapter, no dice ! I need an HDMI recorder like this :

****

Messed with the HDMI color symbols and can generate many different colors !

// A nice Azure blue for example :
c2_symbol = 10'b1111010000; // red
c1_symbol = 10'b1011110000; // green
c0_symbol = 10'b1011110000; // blue

If you send it something that isn’t balanced colorwise it goes haywire :

c2_symbol = 10'b1111111111; // red
c1_symbol = 10'b1111111111; // green
c0_symbol = 10'b1111111111; // blue

c2_symbol = 10'b0000000000; // red
c1_symbol = 10'b0000000000; // green
c0_symbol = 10'b0000000000; // blue

Informatique préparée / Assemblages informatiques

I have an idea for a next “chapter” in my project : prepared computers or assemblages.

Each computer would be a hodge-podge of different tech from different eras, but would each include some kind of I/O, memory, ALU, and display, cooling. I could return to the e-waste side of things, but still make circuits to control them.

I could use a corps exquis method to combine different types of screens/displays :

  • mini CRT
  • LCD
  • projector
  • printer
  • e-ink
  • galvo lasers

Types of memory :

  • floppy
  • SRAM
  • DDR
  • CD
  • Tape

ALU :

  • FPGA
  • microcontroller
  • raspberry pi
  • mechanical

I/O :

  • mouse
  • keyboard
  • trackball
  • touchpad
  • speakers
  • internet

Cooling

  • Peltier
  • Fan
  • Radiator

The actual functions of the computers would be unclear, they would generate mysterious images and sequences and react to the interface. They would allow me to very clearly be in the practice of making art.

They would highlight what I’ve come to find interesting about the world of computers, the hand-madeness and social dimension of the objects. Each would be unique, idiosyncratic, and I would no longer be in the situation of working with screens and not knowing what exactly the “object” is. The object here would be clear, it would be the computer assemblage. I could revisit my obsession with miniatureness, and oddity in these hybrid “monsters”. They would also require lots of technical challenges to make work, and would not produce waste like the mass production of circuit boards does when making a kit.

This is building on Vivien’s idea to be inspired by full computer builds to emphasize materiality :

https://cdn.cnx-software.com/wp-content/uploads/2022/12/LapPi-2.0-DIY-Raspberry-Pi-laptop.jpg?lossy=0&strip=none&ssl=1

Clockwork's uConsole is a modular portable computer & "fantasy console" for $139 and up - Liliputing

It would be really cool to make everything from e-waste, but also to keep the miniature dimensions. It looks like phone screens from Nokia can be controlled by Arduino, but that others are undocumented and hard to control without finding a datasheet :

Nokia 5510 84*48 LCD and the Arduino Nano – thesolaruniverse

Some ressources for controlling a TFT display directly with Arduino :

https://community.element14.com/members-area/personalblogs/b/blog/posts/screen-success-hack-like-heck-build-update

Looks like it is doable with a raspberry pi and therefore doable with an FPGA.

None of these will be possible if I don’t have an autonomous board though – one that produces it’s own videos with data (instead of relying on video from a computer or rapsberry pi). So I will have to make this work first, and why not try to have this done for the expo next month in Germany ?

******

This Autonomous board could play videos from memory and display it all on the same board, without any wires !

 

I am now seeing this as a two board construction. The keyboard is too dense to pack any electronics into really, so it is better just as an interface with a ribbon cable going to the board behind the screen which has the FPGA and microcontroller. The bottom could be in aluminium which would make it stable and could have tabs which bend on the edges. This would provide a solid mounting place for the micrometer knob and the laptop hinge. It would also give a different look to the board as it would be all metal, combining with the micrometer, the screen and the screen hinge to make an all metal looking assemblage.

Not yet sure how to convert the linear motion of the micrometer knob, and I feel that the screen + board might look and be a little top heavy / too thick for the effect.

Check out these beautiful machines from Love Hulten : https://www.lovehulten.com/ Thanks Martin De bie for the ref !

Now I’m thinking it should be flat in fact :

Looking in to TFT LCDs, here’s what I’m finding :

  • 40 pins seems to be a kind of standard
  • There seems to be at least two modes : ‘RGB’ mode and SPI mode.
  • rectangular screens with 800×480 in 4.3″, 5″ or 7″ seems to be standard
  • TFTs seem to come with a driver chip like the ILI9341 or the ST7701S
  • Some seem to require being initialized by SPI

*****

Kristy has helped me select a nice sized screen, 2.8″ seems to be cute but not trinket-like. Adafruit’s screen sells on Mouser : https://www.adafruit.com/product/1774

It appears to have a simple 8 bit parallel mode by default with a straight-forward timing diagram :

And here’s another explanation of the same thing showing two different options :

The datasheet is detailed and I have Adafruit code to base things on as well.

****

The screen also appears to also have a “raw” Parallel RGB Interface mode which seems to involve the HSYNC, VSYNC, DOTCLOCK, DE, while sending color data to the D pins…..but it looks like first bits (RCM, RIM, and DPI) must be set from the SPI mode ? Also, it looks like you have to calculate yourself the timings of the screen based on the clock and screen dimensions (and also set these bits (DIV, RTN, etc.) ? But it looks super simple afterward :

 

*************

PROJECT UPDATE :

This project seems to be unlucky :

  • My Mouser order was stolen from inside my building or was never actually delivered.
  • The idea of having doublesided PCB + aluminum went out the window when I saw the cost (approx. 10x a normal PCB)
  • The replacement screen I ordered from Amazon has only 20 connections (instead of 50) and only allows SPI, which is very difficult for the FPGA to do and so would require a microchip + FPGA teamwork which seems technically a real headache.
  • Currently the HX4K is not starting up, despite having both PLLs connected now. I’m not sure what I’m missing but it is different enough from the HX1K to merit more research before using I guess.

    On the other hand I have been preparing for my ESDAC creative coding class and enjoying learning about the different kinds of P5JS codes that exist. I made an image to show the typologies I found so far :

    I should also have a class on Glitching !!! And on WEBGL Shader, check out this awesome book : https://thebookofshaders.com/03/

    Moiré

    Field

    Random walk

    Spirograph

    Cavegen

    Radial

    Hyper-realistic face generation

    **********

    Trying to put together an analysis of the kind of hardware effects I have been producing so far :

    Here is a slide trying to show the process of designing a board :

    And some sketches of circuits I’ve made over the course of the project :

    ****

    Older board I never photographed :

    ****

    I finally have access to opencores.org ! Some things of interest so far :

    • Math – https://opencores.org/projects/verilog_fixed_point_math_library
    • Noise and PRNG  – https://opencores.org/projects/gng
    • CORDIC core (for trig functions) – https://opencores.org/projects/verilog_cordic_core
    • ATTINY core – https://opencores.org/projects/attiny_atmega_xmega_core

    There is plenty more, like H.264 and JPEG decoding, SoC, sorting algorithms, DDR controllers, Real-time clocks, PCI bus controllers, DSP Filters, SD card controllers, SPI/UART/USB/I2C etc interfaces, etc.

     

    ****

    Some aesthetic references I was reminded of this week :

    • The fractal, “trippy”, spaceship-like camera movements, and kaleidescopic winamp visuals : https://www.youtube.com/watch?v=RBkhUg1oVIE

    Winamp Milkdrop Visualization Plugin - YouTube

    • The black and white retro dithering from Return Of The Obra Dinn https://www.youtube.com/watch?v=k2y5qH2Jn14

    *************

    Post Darmstadt workshop thoughts :

    • CNC building workshops are hard. In the Fab Academy it takes an entire team two weeks. My two attempts, a while back at CMU and in Germany this week were too ambitious. If I want to have everyone leave with a working machine I need to do a lot of prep before hand. This weekend I could have 3D printed parts before hand that clipped together in the spirit of my linear actuators for the Fab Ac (https://www.marrs.io/medium-sized-cnc-foam-cutter-with-leon-reboul/). Alternatively I could have bought a cheap drawing kit from Aliexpress for around 100 euros if I had gotten myself organized early enough. I think threaded rods may also be a better solution than belt drives which require more skill to design with. The linear rails were cool but they required special low-profile screws so the students struggled a bit with this. It also seems to be the case that servos can simply replace all the challenges of stepper motors requiring drivers for everyone but experts. Essentially : Don’t overthink it, do the most obvious tech thing that is the shortest path for beginners. This will be my strategy for the Beaux Arts workshop coming up where I’ll focus on servos only.
    • I should probably keep in mind the challenge of the workshop. The higher the challenge for the participants, the more work I need to do to prepare everything. More plug and play = less challenge.
    • Learning to be a free lancer will take time but I need to consider myself as a professional, not doing things on a shoestring budget and exhausting myself. As I move around and workshop in different places, I need to keep in mind my audience – engineering students are different from artists, designers, enthusiasts and kids.
    • It might be cool to learn to make video PDFs for future presentations when talking about video synthesis. Also maybe live coding with toy shader / P5JS to illustrate things ?
    • For the expo, I was happy to have a plug and play FPGA solution that just generated images. The boards I have made that record video all need heavy intervention on the part of the player and are not intuitive at all. I am still hoping to find a way to order the remaining parts from Mouser soon so I can test SD card reading and FPGA screen displaying.
    • For my talk, I still don’t have a way of directly connecting my earlier projects with the new video synth project.

    Next steps :

    I am feeling positively about just following my interests, and not necessarily knowing where I am going with this project. I want to :

    • make abstract generating form code with FPGA using the new trig functions, multiplication and division from opencores !
    • experiment with more filters using the FPGA.
    • move beyond my current aesthetic, which will require making the recorded image not jump around. I should be able to do this pretty easily when the FPGA is sending the sync signals.

    *************

    Post BA workshop thoughts :

    • The students this time were really into following a more formal series of prepared slides with exercises, and seemed less interested in building their own drawing machines. The Arduino exercises were well received, as well as the glitch exercise, and also the soldering workshop. I like that they bring their own projects and integrate them.
    • I tried to be extra professional this time, staying late with students to continue helping them, being extra careful to put everything back in its proper place, and generally being as uncompromising as possible in the quality I’m trying to bring to the workshop.
    • My little polyphonic synth, portable speaker, and VGA arduino board were a good thing to bring. I should have something interactive that puts images on the screen I think and a lamp to show off the solar sunflower too.
    • I had too many projects to manage in parallel at one point and was rushing about everywhere. Not sure what the solution is there, there were around 8/9 people for the first two days.
    • We need some basic boards like mic amp, speaker amp, easy stepper motor drivers, relay boards, etc. that are useful at the BD.
    • Vincent was suggesting that we think of ways of creating a virtual expo space for digital works somehow in relation to his radio station at the school which is appreciated.

    *************

    Post website being down, looking at previous pages now and having some perspective thoughts :

    • pure p5js coding I find far less exciting than hardware visual effects, it’s all too immediate and mediated. It’s not raw and unstable like analogue. I think I’m more convinced that the painful process of doing simple things the hardest way possible is actually productive creatively for the friction it affords.
    • Putting a series of bitmaps on an SD card (possibly with a custom FFMPEG running on raspberry pi) and then onto the screen would be cool. The idea for a custom FFMPEG comes from this tutorial http://dranger.com/ffmpeg/tutorial01.html which I found here https://github.com/codecrafters-io/build-your-own-x which I in turn found on reddit FREE MEDIA HECK YES 
    • I need to get the sync working to test some other (less jarring) visual experiences.
    • The Real Time Corrupter methods (piping, etc.) are brilliant and I have to copy them to experiment with FPGA memory filters. Beyond the RTC it would be very cool to experiment with DCT compression filter spectra.
    • I don’t think I need to look under each hole and check under each rock. Getting one type of memory working, and one video protocol, is good enough and I don’t need to suffer with DDR and HDMI in this way of working I’m assuming currently.
    • For marketing video synths, there is the website php like Marc did (https://muca.cc/fr) or the instagram/facebook marketplace option. Alternatively there are services that host the payment side of things like gumroad, prestashop, and wordpress https://wordpress.com/fr/ecommerce/
    • I’m doing NO SCHOOL this summer !! Planning on getting a simple FPGA v synth programmable with Arduino / RPI in python and made on an aluminum-backed board (with SMD VGA).

    *************

    Learned about live coding with hydra :

    hydra >” jsname=”kn3ccd” aria-hidden=”false”></p>
<p>Kind of like Processing, I find it too easy to make wacky stuff and unsatisfying to not have the friction of analogue.</p>
<p class=I will be VJing this month, we’ll see what that experience is like. Perhaps it will help me design boards that VJs could use. Here’s what I learned :

    • The stress of doing a live performance when everything isn’t set up and rehearsed many times before hand is high. One would need to have a bunch of backups, and a very rehearsed performance to lower stress.
    • Having a small lamp is helpful to see what connections and knobs you are messing with
    • One question is how to react to music, does a beat equal a varying of a parameter ? How to react to song changes ?
    • How should “effects” be introduced ? I defaulted towards starting simple and then going towards the more complex effects
    • Clearly having performance grade equipment means everything is super robust, easy to use, etc. This makes me think that it would make sense to make a really reliable simple filter, for instance. This also connects with a larger issue for me : I want to do things differently but there are often good reasons why things are done a particular way
    • Sending my crazy VGA to a machine that digitizes it and sends it on as HDMI is probably safer than directly sending things to the projector…

    I want to use an older media device (cassette tape, floppy, mini-VHS?) in my next board. I realized that if my practice is about “doing things the hard way”, that should be visually communicated in the design (no one understands if the electronics are done the hard way or not…). This would also signal that I am not trying to make cutting edge new things but that my interests are elsewhere.

    Here is a first look at some video synth companies and their designs :

    Trying again with the floppy drive :

    Check out this video extracting motion from video by just inverting and sliding videos sideways :

    ****************

    I finally bought preparedinstruments.com ! Here are some potential designs for the page :

     

    Paris Electronic Week Synth Workshop

    Making a simple FPGA video synth for PEW 2023 at Villette Makerz. It takes audio in and generates patterns based on the voltage threshold and the 9 keys being pressed.

    The code is here : https://github.com/merlinmarrs/iCE40HX-verilog-video-patterns

    This time the top interface side has only buttons and no electronics. I have a simplified VGA out and HDMI with the correct LVDS resistor setup this time. I am relying on raspberry pi or arduino for programming.

    Trying a grid ground pour on the Stop layer :

     

    Here’s the final soldered version :

    Nicer photos :

    Found a site called OpenCores (https://opencores.org) that has free FPGA IP like math libraries.

    Here are some tests with cool looking patterns :

    Because dividing, trig functions, etc. aren’t supported by default, the most promising is the bitwise boolean logic operating on the entire vector :

    wire[11:0] bit_or;
    assign bit_or = h_count[11:0] | v_count[11:0];
    
    [...]
    
    if (bit_or[5] == 1 | bit_or[3] == 0) begin
    wire[11:0] bit_xor;
    assign bit_xor = h_count[11:0]^v_count[11:0];
    
    [...]
    
    if (bit_xor[2] == 1 | bit_xor[4] == 1) begin
    wire[11:0] bit_and;
    assign bit_and = h_count[11:0]&v_count[11:0];
    
    ...
    
    if (bit_and[6] == 1 | bit_xor[7] == 1) begin

    I could imagine having the keys vary which logic operations are in play and having the music threshold change the color of the pixels for instance.

    ***

    This site has a module for a quasi random number generator : https://simplefpga.blogspot.com/2013/02/random-number-generator-in-verilog-fpga.html

    This site has some good Processing examples : http://www.generative-gestaltung.de/2/

    ***

    If I do this workshop again I’d like to design it to be made on aluminum as, it is at least in principle, recyclable.

    PCBWay’s diagram of a double layer board (https://www.pcbway.fr/pcb_prototype/General_introduction_of_Aluminum_PCB.html) :

    Two-layer_single_side.png

    ***

    Simplest imaginable keypad switch (and audio in post comparator) test works :

    module top (
    input hwclk,
    output led1,
    input keypad_c1
    );
    
    assign led1=keypad_c1;
    
    endmodule
    Modified and restructured the random code from this site : https://simplefpga.blogspot.com/2013/02/random-number-generator-in-verilog-fpga.html and it appears to work :
    `default_nettype none
    
    module LFSR (
    input clock,
    input reset,
    output [12:0] rnd
    );
    
    wire feedback;
    reg [12:0] random, random_done;
    reg [3:0] count; //to keep track of the shifts
    
    assign feedback = random[12] ^ random[3] ^ random[2] ^ random[0];
    assign rnd = random_done;
    always @ (posedge clock)
    begin
    if (reset)
    begin
    random <= 13'hF; //An LFSR cannot have an all 0 state, thus reset to FF
    count <= 0;
    end
    
    else
    begin
    if (count == 13)
    begin
    count <= 0;
    random_done <= random; //assign the random number to output after 13 shifts
    end
    
    else
    begin
    random <= {random[11:0], feedback}; //shift left the xor'd every posedge clock
    count <= count + 1;
    end
    end
    end
    endmodule

     

    I am using it in the top code like this :

    wire [12:0] random_number;
    
    [...]
    
    LFSR random_s(
    .clock(clk_in),
    .reset(reset),
    .rnd(random_number)
    );

    ***

    Sine gen :

    • https://miscircuitos.com/sinus-wave-generation-with-verilog-using-vivado-for-a-fpga/
    • https://zipcpu.com/dsp/2017/07/11/simplest-sinewave-generator.html
    • https://verilogcodes.blogspot.com/2015/11/verilog-code-for-simple-sine-wave.html

    I added a time delay module I modified from the led_rotation example :

    `default_nettype none
    module tempo(
    input wire clk,
    output reg half_sec_pulse
    );
    
    reg[25:0] div_cntr1;
    reg[25:0] div_cntr2;
    always@(posedge clk)
    begin
    div_cntr1 <= div_cntr1 + 1;
    if (div_cntr1 == 0)
    if (div_cntr2 == 0)
    begin
    div_cntr2 <= 0;
    half_sec_pulse <= ~half_sec_pulse;
    end
    
    else
    div_cntr2 <= div_cntr2 + 1;
    else
    half_sec_pulse <= half_sec_pulse;
    end
    
    endmodule

     

    I can’t figure out why this makes 1/2 second ish though. Google says it should last 130.312488 days…Perhaps the compiler is deciding it doesn’t have enough space to store it and it truncating it?

    From the led_rotation code I get 2^16 = 65536, this happens 91 times so 65536x91, then multiplied by the period of a 12MHz clock (83.3ns) gets me to around 0.49 seconds.

    I’m guessing it is getting clipped because the register is too big ?

    *****

    Fabien showed me that on AliExpress you can buy things like this tracking ball for cents :

    Blackberry Trackball

    https://fr.aliexpress.com/item/1005004324983317.html?spm=a2g0o.detail.0.0.3f40hagUhagUgw&gps-id=pcDetailTopMoreOtherSeller&scm=1007.40000.327270.0&scm_id=1007.40000.327270.0&scm-url=1007.40000.327270.0&pvid=83e84367-84b9-424f-b42a-31b87563ff5e&_t=gps-id:pcDetailTopMoreOtherSeller,scm-url:1007.40000.327270.0,pvid:83e84367-84b9-424f-b42a-31b87563ff5e,tpp_buckets:668%232846%238114%231999&pdp_npi=4%40dis%21EUR%210.80%210.72%21%21%216.14%21%21%40211b801516946910696348428ea8cb%2112000030031307832%21rec%21FR%21%21ABS&search_p4p_id=202309140431096642081558243384177015_4

    Each one requires four little hall effect sensors which output HI-LOW pulses. The one made by Sparkfun is the AN48841B : high sensitivity CMOS Hall IC Operates on Alternating Magnetic Field (low-speed rotation for lock detection). Trying to find an alternative for not too much on Mouser.

    https://www.mouser.fr/ProductDetail/Infineon-Technologies/TLI49632MXTSA1?qs=0DP5yvOrqYlmAP5lA6MTRA%3D%3D

    I am also thinking about an HDMI mini – not micro – (if I can get HDMI working, if not get a helper chip), an atmega2560 to handle the SD card communication. Possibly a USB charger and a battery too – so that there can really be a minimum of cables when the battery is charged up.

    A dumb-proof programming connector with an automatically controlled bus to disconnect the programmer from the FPGA when reset not asserted so I don’t need to unplug and replug every time ?

    *****

    NEXT BOARD THOUGHTS :

    Maybe I don’t need to rebuild the FPGA etc each time, but should just make interfaces that plug into it ?

    The only thing left to try in hardware is the SD card fetching video data and then putting it on screen, and perhaps test having way more bits for each color and using the 4K LUT version of the iCE40.

    I could also try using the specialized HDMI chip ? This would allow me to try the mini screen.

    VGA with like 64 bit color !!!

    put the remaining video ICs that I haven’t played with yet (video differentiator, video sync module, etc.) on the board !

    Combine the possibility of reading a video from an SD card, having memory to store it in, and having a bunch of keys to press, AND coming up with code that makes meaningful patterns.

    My leftover video ICs :

    • ADV7125 8 bit Triple HS Video – https://www.mouser.fr/pdfdocs/ADI_ADV7125_Datasheet.PDF
    • LT1251CN Video Fader – https://www.mouser.fr/datasheet/2/609/12516fa-3123106.pdf
    • 660GILFT Digital Video Clock – https://www.mouser.fr/datasheet/2/698/REN_660_DST_20100514-1995937.pdf
    • NJM2274R Low Power CI video w/ Y-C mixer – https://www.mouser.com/datasheet/2/294/NJM2274_E-1917180.pdf
    • FMS6143CSX Video Filter Driver – https://www.mouser.fr/datasheet/2/308/1/FMS6143_D-2313627.pdf
    • AD724JRZ – https://www.mouser.fr/datasheet/2/609/AD724-3118543.pdf

    *****

    PATTERN CODE THAT WORKS :

    Got a code that works :

    `default_nettype none
    
    module vga_sync_test(
    
    input wire clk_in,
    input wire reset,
    input wire key[8:0],
    input wire adc_in,
    output reg led_red,
    output reg led_green,
    
    //VGA OUT
    
    output reg [3:0] r_out,
    output reg [3:0] b_out,
    output reg [3:0] g_out,
    output wire h_sync,
    output wire v_sync
    
    );
    
    wire half_sec;
    wire [12:0] random_number_0;
    wire [12:0] random_number_1;
    wire [12:0] random_number_2;
    wire [12:0] random_number_3;
    wire [12:0] random_number_4;
    wire [12:0] random_number_5;
    wire [12:0] random_number_6;
    wire [12:0] random_number_7;
    wire [12:0] random_number_8;
    wire [12:0] random_number_9;
    wire [7:0] sine_wave;
    wire display_en;
    wire [11:0] h_count;
    wire [11:0] v_count;
    
    localparam h_pixel_max = 1280;
    localparam v_pixel_max = 960;
    localparam h_pixel_half = 640;
    localparam v_pixel_half = 480;
    localparam h_pixel_25 = 320;
    localparam v_pixel_25 = 240;
    localparam h_pixel_75 = 960;
    localparam v_pixel_75 = 700;
    
    //VGA COLOR OUT
    
    always @(posedge clk_in) begin
    
    if (display_en) begin
    
    if ( key[0] == 0 &&
    
    h_count < h_pixel_25 + random_number_1[4:0] && v_count < v_pixel_25 + random_number_7[8:5] &&
    h_count > h_pixel_25 - random_number_2[4:0] && v_count > v_pixel_25 - random_number_2[8:5]
    
    ) begin
    
    r_out <= random_number_1[3:0];
    g_out <= random_number_5[3:0];
    b_out <= random_number_9[6:4];
    
    led_green <= 1;
    
    end
    
    else if ( key[1] == 0 &&
    
    h_count < h_pixel_half + random_number_7[5:0] && v_count < v_pixel_half + random_number_7[10:5] &&
    h_count > h_pixel_25 - random_number_6[5:0] && v_count > v_pixel_25 - random_number_6[10:5]
    
    ) begin
    
    r_out <= random_number_3[7:5];
    g_out <= random_number_3[3:0];
    b_out <= random_number_6[6:4];
    
    led_green <= 1;
    
    end
    
    else if ( key[2] == 0 &&
    
    h_count < h_pixel_25 + random_number_8[6:0] && v_count < v_pixel_25 + random_number_8[12:6] &&
    h_count > h_pixel_half - random_number_9[6:0] && v_count > v_pixel_half - random_number_9[11:5]
    
    ) begin
    
    r_out <= random_number_4[7:5];
    g_out <= random_number_5[3:0];
    b_out <= random_number_7[6:4];
    led_green <= 1;
    
    end
    
    else if ( key[3] == 0 &&
    
    h_count < h_pixel_half + random_number_0[7:0] && v_count < v_pixel_half + random_number_0[7:0] &&
    h_count > h_pixel_half - random_number_3[7:0] && v_count > v_pixel_half - random_number_3[11:4]
    
    ) begin
    
    r_out <= random_number_8[7:5];
    g_out <= random_number_8[3:0];
    b_out <= random_number_8[6:4];
    led_green <= 1;
    
    end
    
    else if ( key[4] == 0 &&
    
    h_count < h_pixel_75 + random_number_2[8:0] && v_count < v_pixel_half + random_number_2[11:3] &&
    h_count > h_pixel_75 - random_number_4[8:0] && v_count > v_pixel_half - random_number_4[11:3]
    
    ) begin
    
    r_out <= random_number_7[7:5];
    g_out <= random_number_7[3:0];
    b_out <= random_number_7[6:4];
    
    led_green <= 1;
    
    end
    
    else if ( key[5] == 0 &&
    h_count < h_pixel_half + random_number_3[9:0] && v_count < v_pixel_75 + random_number_3[11:2] &&
    h_count > h_pixel_half - random_number_8[9:0] && v_count > v_pixel_75 - random_number_8[11:2]
    
    ) begin
    
    r_out <= random_number_5[7:5];
    g_out <= random_number_5[3:0];
    b_out <= random_number_5[6:4];
    led_green <= 1;
    
    end
    
    else if ( key[6] == 0 &&
    
    h_count < h_pixel_75 + random_number_4[10:0] && v_count < v_pixel_75 + random_number_4[11:1] &&
    h_count > h_pixel_75 - random_number_6[10:0] && v_count > v_pixel_75 - random_number_6[11:1]
    
    ) begin
    r_out <= random_number_4[7:5];
    g_out <= random_number_4[3:0];
    b_out <= random_number_4[6:4];
    led_green <= 1;
    end
    else begin
    r_out <= random_number_2[3:0];
    g_out <= random_number_3[6:4];
    b_out <= random_number_4[3:0];
    led_green <= 0;
    end
    end
    else begin
    r_out <= 4'b0000;
    g_out <= 4'b0000;
    b_out <= 4'b0000;
    led_green <= 0;
    end
    end
    
    vga_sync vga_s(
    
    .clk_in(clk_in),
    .reset(reset),
    .h_sync(h_sync),
    .v_sync(v_sync),
    .h_count(h_count),
    .v_count(v_count),
    
    .display_en(display_en) // '1' => pixel region
    
    );
    
    LFSR random_s(
    
    .clock(clk_in),
    .reset(reset),
    .half_sec_pulse(half_sec),
    .rnd_0(random_number_0),
    .rnd_1(random_number_1),
    .rnd_2(random_number_2),
    .rnd_3(random_number_3),
    .rnd_4(random_number_4),
    .rnd_5(random_number_5),
    .rnd_6(random_number_6),
    .rnd_7(random_number_7),
    .rnd_8(random_number_8),
    .rnd_9(random_number_9)
    );
    
    sine_wave_gen sine_wave_s(
    .clk(clk_in),
    .data_out(sine_wave)
    );
    
    tempo tempo_s(
    .clk(clk_in),
    .half_sec_pulse(half_sec)
    );
    
    endmodule

    and here is the PCF file :

    set_io v_sync 97
    set_io h_sync 76
    set_io clk_in 49
    set_io reset 66
    
    set_io r_out[0] 91
    set_io r_out[1] 95
    set_io r_out[2] 96
    
    set_io g_out[0] 75
    set_io g_out[1] 74
    set_io g_out[2] 73
    
    set_io b_out[0] 87
    set_io b_out[1] 88
    set_io b_out[2] 90
    
    set_io key[0] 37
    set_io key[1] 38
    set_io key[2] 39
    set_io key[3] 41
    set_io key[4] 42
    set_io key[5] 43
    set_io key[6] 44
    set_io key[7] 45
    set_io key[8] 47
    set_io led_red 23
    set_io led_green 24
    set_io adc_in 56

     

    ***************

    TAKEAWAYS FROM VILLETTE MAKERZ

    Getting feedback on my circuits has been really cool, getting “users” to try my boards and tell me what they would like to be able to do has to become part of my process of design and iteration if I am ever to make something people want to buy.

    Some feedback so far :

    • If people have hard time understanding the interest in “doing something the hard way” even if it is not obvious for the outcome, then you have to show them what your process is. You need to share your workspace and tools with them so they can see the advantages of doing something the hard way.
    • Using hand to modulate speaker for sequencer is cool
    • The 7 layer board is fun to let people play with. Difficult to see different layers though, would be cool if could have diff colors.
    • People like the idea of deforming an image of themselves like from a webcam and seeing it projected on the big screen
    • Kids like colors and the 9 key board board is colorful. Would be cool if once you select a pattern is stays though
    • The Arduino boards are less cool because they are not interactive or colorful
    • It would be cool for the FPGA 9 key things to have more of a relationship with sound 
    • putting circuits ontop of a screen is cool.
    • Projections kind of get lost if not in total darkness.
    • Curious to try a tiny screen, or a really big screen, or a tiling of screens ?
    • the combo of the oscilloscope and the function generator + speaker is cool to understand the basics of electronics signals.

    Aliexpress has components that are insanely inexpensive compared to Mouser. If I were ever to sell something for profit I would need to source the components from China entirely.

    I think I also need to address the environmental question too : how is continuing to manufacture circuits responsible ? Could I demonstrate how to recycle an aluminum PCB ? Obviously using e-waste is cool and should be emphasized more in my projects perhaps

    *****************

    SD CARD EXPERIMENTS

    Modified this code to write the contents of a file on the SD card as a byte to PORT D.

    Need to find a way to convert an image into a collection of bytes and store it. Then I need to iterate through the file and output the values to a port.

    /*
     * Created by ArduinoGetStarted.com
     *
     * This example code is in the public domain
     *
     * Tutorial page: https://arduinogetstarted.com/reference/library/arduino-file.readbytes
     */
    
    
    #include <SD.h>
    
    
    #define PIN_SPI_CS 4
    
    
    File file;
    char buf[200];
    
    
    void setup() {
      Serial.begin(9600);
      DDRD = B11111111;
    
    
      if (!SD.begin(PIN_SPI_CS)) {
        Serial.println(F("SD CARD FAILED, OR NOT PRESENT!"));
        while (1); // don't do anything more:
      }
    
    
      // open file for reading
      file = SD.open("albers.hex", FILE_READ);
      if (file) {
        int avail_len = file.available();
        int read_len = file.readBytes(buf, avail_len); // read all to buffer to buffer
    
    
        Serial.print("Length:");
        Serial.println(read_len);
        Serial.println("Content:");
    
    
                  for (int i=0; i<200; i++) {
       
        Serial.print(byte(buf[i]));
        PORTD = byte(buf[i]);
              }
       
    
    
        file.close();
      } else {
        Serial.print(F("SD Card: error on opening file"));
      }
    }
    
    
    void loop() {
    }

    I consulted this site : https://www.dcode.fr/binary-image to convert an image into binary (not sure yet how I will do this for video but I’ll cross that bridge later). I chose resolution 200 for this image :

    For the video I am looking into FFMPEG. It can export still images every X seconds (ffmpeg -i input.mp4 frame%04d.png) which I could then turn into bits with a simple python or processing code.

    Processing also has code to take video and convert it to ASCII : https://github.com/processing/processing-video/blob/main/examples/Capture/AsciiVideo/AsciiVideo.pde

    Maybe the best setup would be to have Processing create a series of ASCII text files based on frames from a video, and then put that on the SD card.

    Here is a schematic of how it could work with two SRAM banks in dual banking mode, writing to one while reading from the other and then switching :

    ******

    PROGRAMMING FPGA WITH ARDUINO OR RPI

    I had been trying to open the OLIMEX demo code pilot incorrectly. It does indeed work from the command line. I’ll have to test with the MEGA next.

    ****

    An attempt at mixing the pattern generation with the recording to memory functionality :

    `default_nettype none
    
    module vga_sync_test(
    
    input wire clk_in,
    input wire reset,
    
    input wire rec, // Direction of io, 1 = set output, 0 = read input
    
    //RASPBERRY PI
    input wire [3:0] r_in,
    input wire [3:0] b_in,
    input wire [3:0] g_in,
    
    //VGA OUT
    output reg [3:0] r_out,
    output reg [3:0] b_out,
    output reg [3:0] g_out,
    
    output wire h_sync,
    output wire v_sync,
    
    //SRAM
    
    output reg [20:0] addr,
    inout wire [7:0] io, // inout must be type wire
    
    output wire cs_1,
    output wire cs_0,
    output reg we_0
    
    );
    
    
    wire half_sec;
    
    wire [12:0] random_number_0;
    wire [12:0] random_number_1;
    wire [12:0] random_number_2;
    wire [12:0] random_number_3;
    wire [12:0] random_number_4;
    wire [12:0] random_number_5;
    wire [12:0] random_number_6;
    wire [12:0] random_number_7;
    wire [12:0] random_number_8;
    wire [12:0] random_number_9;
    wire [7:0] sine_wave;
    
    wire [7:0] data_in;
    wire [7:0] data_out;
    
    reg toggle;
    
    reg [7:0] a, b;
    
    assign io = rec ? a : 8'bzzzzzzzz;
    
    assign data_out = b;
    
    assign data_in[1:0] = r_in[3:2];
    assign data_in[3:2] = b_in[3:2];
    assign data_in[5:4] = g_in[3:2];
    assign data_in[7:6] = 2'b00;
    
    wire display_en;
    wire [11:0] h_count;
    wire [11:0] v_count;
    
    localparam h_pixel_max = 1280;
    localparam v_pixel_max = 960;
    localparam h_pixel_half = 640;
    localparam v_pixel_half = 480;
    
    
    reg [1:0] count2; //to keep track of the shifts
    
    wire [11:0] bit_or;
    assign bit_or = h_count[11:0]|v_count[11:0];
    
    wire [11:0] bit_xor;
    assign bit_xor = h_count[11:0]^v_count[11:0];
    
    wire [11:0] bit_and;
    assign bit_and = h_count[11:0]&v_count[11:0];
    
    wire [11:0] bit_nand;
    assign bit_nand = h_count[11:0]& ~v_count[11:0];
    
    wire [11:0] bit_xnor;
    assign bit_xnor = h_count[11:0]^ ~v_count[11:0];
    
    reg [11:0] out_bit ;
    
    
    // CS: low to select, high to deselect
    
    assign cs_0 = toggle ? 1 : 0;
    assign cs_1 = toggle ? 0 : 1;
    
    //SRAM address counter
    
    always @(posedge clk_in) begin
    
    if (addr == 0) begin
    toggle <= toggle+1;
    end
    
    if (reset) begin
    addr <= 0;
    end else begin
    addr <= addr+1;
    
    end
    end
    
    
    //REC control
    
    always @(posedge clk_in) begin
    
    b <= io;
    a <= data_in;
    
    if (rec) begin
    we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
    end
    else begin
    we_0 <= 1;
    end
    end
    
    //VGA COLOR OUT
    
    always @(posedge clk_in) begin
    
    case(count2)
    2'b00: out_bit = bit_or;
    2'b01: out_bit = bit_xor;
    2'b10: out_bit = bit_and;
    2'b11: out_bit = bit_nand;
    
    default: out_bit = bit_xnor;
    endcase
    count2 <= count2 + 1;
    end
    
    //VGA COLOR OUT
    
    always @(posedge clk_in) begin
    if (display_en) begin
    
    if (out_bit[random_number_2[3:0]] == 1 | out_bit[random_number_6[3:0]] == 0) begin
    
    r_out <= random_number_2[3:0];
    g_out <= random_number_3[3:0];
    b_out <= random_number_4[3:0];
    
    end
    
    else begin
    
    r_out[3:2] <= data_out[1:0];
    r_out[1:0] <= data_out[1:0];
    g_out[3:2] <= data_out[3:2];
    g_out[1:0] <= data_out[3:2];
    b_out[3:2] <= data_out[5:4];
    b_out[1:0] <= data_out[5:4];
    
    end
    
    end else begin
    
    r_out <= 4'b0000;
    g_out <= 4'b0000;
    b_out <= 4'b0000;
    
    end
    end
    
    vga_sync vga_s(
    .clk_in(clk_in),
    .reset(reset),
    .h_sync(h_sync),
    .v_sync(v_sync),
    .h_count(h_count),
    .v_count(v_count),
    .display_en(display_en) // '1' => pixel region
    );
    
    LFSR random_s(
    .clock(clk_in),
    .reset(reset),
    .half_sec_pulse(half_sec),
    .rnd_0(random_number_0),
    .rnd_1(random_number_1),
    .rnd_2(random_number_2),
    .rnd_3(random_number_3),
    .rnd_4(random_number_4),
    .rnd_5(random_number_5),
    .rnd_6(random_number_6),
    .rnd_7(random_number_7),
    .rnd_8(random_number_8),
    .rnd_9(random_number_9)
    );
    
    sine_wave_gen sine_wave_s(
    .clk(clk_in),
    .data_out(sine_wave)
    );
    
    tempo tempo_s(
    .clk(clk_in),
    .half_sec_pulse(half_sec)
    );
    
    endmodule
    

     

    I just realized that I could connect the rpi pixel clock to the FPGA to have a totally synchronized clock between the videos and the graphics ! I can also connect the DEN pin. Not really working currently though.

    *****

    Miniature discovery, if you just take a single bit of information from raspberry pi sending a video, it looks really blocky and cool like a badly compressed video :

    Check out this artist Ni Hao’s  broken screens ( https://haoni.art/spin-III) :

    FPGA SRAM board

    FPGA SRAM board

     

    Shadertoy is the future of this project, so what am I doing ? This new board is a reprogrammable FPGA 2D pixel shader (aka fragment shader) which can control the color of pixels and know only of the coordinate of the pixel being drawn. Pixel shaders can also sample the values of nearby pixels. As a result, they can do 2D post-processing (https://en.wikipedia.org/wiki/Video_post-processing) or filtering for a video streams. Why do this on an FPGA rather than in-brower ? You are super low level, and you have constraints based on the number of LUTs, it has a live-playable interface. In any case, I think this is my last board for a while, unless I can make an AI board somehow.

    On the other hand, I’m not sure how profound it is to make photoshop like filters. (You can test a 5×5 grid if you go under Filters>Custom in PS). They are all kind of superficial…

    I am starting to realize that I spend loads of time debugging soldering errors (yes, I get the learning that comes with that but still). If I made boards and had them assembled elsewhere I would not have this issue. Should I also, in the same spirit, just jump ahead to what everyone else is doing, with the thinking that they are not wasting time on problems that have already been solved and are exploring an interesting space.

    I also spend a lot of time designing and building boards, interfaces, and less time programming them: the SCARA robot arm that I didn’t even plug in, the covid temperature interface board, the drone, and the MIT version of the mini swarm robot. On the other side are the projects I’ve perhaps spent too must time making and incrementally remaking perhaps : 3105 power harvester + radio transciever, the Ben Eater style memory. A nice balance was perhaps the solar sunflower protytpes which were experiments with form and function.

    *EDIT* I am now thinking that I must work on things that would be of interest to other people, like Machine Learning for instance. I must be humble, just because something is interesting to me doesn’t mean anyone else cares ! Turns our that FPGAs consume a lot less current than GPUs so they are not bad for implementing ML once the training is done. Here is a info about ML with FPGAs :

    • https://www.youtube.com/@marcowinzker3682
    • https://www.mdpi.com/2076-3417/12/1/89
    • https://github.com/verimake-team/MLonFPGA
    • https://qengineering.eu/deep-learning-with-fpga-aka-bnn.html
    • https://qengineering.eu/embedded-vision.html#FPGA_stitching

    Great post about the marble machine project : https://www.reddit.com/r/MarbleMachineX/comments/pzh5kw/an_open_letter_to_martin_on_the_state_of_the/

    • The machine is not about reliability and precision, it is about imperfection and the journey
    • It’s an art project, it is a project for the project’s sake and the documentation.
    • The project shouldn’t run like a start-up, and we are not the sum of what we produce for capitalism
    • Not specializing in one thing is what makes this project cool.

    ERRATA :

    • I think the 590 is not clock dividing as expected…Is it not 3.3V compatible ? I think so, even though unclear on Mouser website.
    • Currently only 2 bits of each raspberry pi color input are appearing despite having selected dpi24 in the rpi config file.
    • The fine pots are the wrong footprint for the pots I have.
    • I could have used DEN (display enable) from the raspi.
    • sometimes the rpi starts up and then restarts after playing only a few seconds of video…not sure why.

    To change the PLL, there is a tool in IceCube2 under Tool>Configure>Configure PLL Module

    Here is the user guide explaining how to use it : https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/IK2/FPGA-TN-02052-1-4-iCE40-sysCLOCK-PLL-Design-User-Guide.ashx?document_id=47778

    I want the following options :

    • General Purpose I/O Pad or Core Logic
    • Using a feedback path internal to the PLL
    • Input Frequency (MHz) : 100
    • Output Frequency (MHz) : 25

    Here is the new PCF file :

    set_io v_sync 87
    set_io h_sync 88
    set_io clk_in 64
    set_io reset 66
    set_io r0 81
    set_io r1 80
    set_io r2 79
    set_io g0 94
    set_io g1 93
    set_io g2 91
    set_io b0 76
    set_io b1 75
    set_io b2 74
    set_io led 45
    set_io locked_led 37
    
    Tested with a 12MHz external clock, it works to output VGA.
    No luck so far with 100MHz clock and multiplying all the pixel drawing, syncing and front and back porches by 4. Thinking of just switching out for a 12MHz clock like on the first version.
    *EDIT* Also working with 25MHz external clock and removing the PLL.
    *EDIT* Very strange but working with 100MHz now, except the code should only work with 50MHz…Here are the two files being used to generate the signal :
    
    module vga_sync_test(
    input wire clk_in,
    input wire reset,
    output reg r0,
    output reg r1,
    output reg r2,
    output reg b0,
    output reg b1,
    output reg b2,
    output reg g0,
    output reg g1,
    output reg g2,
    output wire h_sync,
    output wire v_sync
    );
    
    wire display_en;
    //reg [9:0] h_count;
    wire [11:0] h_count;
    //reg [9:0] v_count;
    wire [11:0] v_count;
    
    /*
    //for 100MHZ
    localparam h_pixel_max = 2560;
    localparam v_pixel_max = 1920;
    localparam h_pixel_half = 1280;
    localparam v_pixel_half = 960;
    */
    
    
    //for 50MHZ
    
    localparam h_pixel_max = 1280;
    localparam v_pixel_max = 960;
    localparam h_pixel_half = 640;
    localparam v_pixel_half = 480;
    
    /*
    //for 25MHZ
    
    localparam h_pixel_max = 640;
    localparam v_pixel_max = 480;
    localparam h_pixel_half = 320;
    localparam v_pixel_half = 240;
    */
    
    //Check if we can create RGB colors
    always @(posedge clk_in) begin
    if (display_en) begin
    if (h_count < h_pixel_half
    && v_count < v_pixel_half) begin
    //Assign here your test color
    r0 <= 1'b0;
    r1 <= 1'b0;
    r2 <= 1'b0;
    g0 <= 1'b0;
    g1 <= 1'b0;
    g2 <= 1'b0;
    b0 <= 1'b1;
    b1 <= 1'b1;
    b2 <= 1'b1;
    end else if (h_count > h_pixel_half
    && v_count < v_pixel_half) begin
    //Assign here your test color
    r0 <= 1'b0;
    r1 <= 1'b0;
    r2 <= 1'b0;
    g0 <= 1'b1;
    g1 <= 1'b1;
    g2 <= 1'b1;
    b0 <= 1'b0;
    b1 <= 1'b0;
    b2 <= 1'b0;
    end else if (h_count < h_pixel_half
    && v_count > v_pixel_half) begin
    //Assign here your test color
    r0 <= 1'b1;
    r1 <= 1'b1;
    r2 <= 1'b1;
    g0 <= 1'b0;
    g1 <= 1'b0;
    g2 <= 1'b0;
    b0 <= 1'b0;
    b1 <= 1'b0;
    b2 <= 1'b0;
    end else begin
    //Assign here your test color
    r0 <= 1'b1;
    r1 <= 1'b1;
    r2 <= 1'b1;
    g0 <= 1'b1;
    g1 <= 1'b1;
    g2 <= 1'b1;
    b0 <= 1'b1;
    b1 <= 1'b1;
    b2 <= 1'b1;
    end
    end else begin
    r0 <= 1'b0;
    r1 <= 1'b0;
    r2 <= 1'b0;
    g0 <= 1'b0;
    g1 <= 1'b0;
    g2 <= 1'b0;
    b0 <= 1'b0;
    b1 <= 1'b0;
    b2 <= 1'b0;
    end
    end
    
    vga_sync vga_s(
    .clk_in(clk_in), //100,50, OR 25MHz clock input
    .reset(reset), // RST assigned to SW1
    .h_sync(h_sync),
    .v_sync(v_sync),
    .h_count(h_count),
    .v_count(v_count),
    .display_en(display_en) // '1' => pixel region
    );
    
    endmodule
    /*
    640x480 VGA singal generator
    ============================
    
    - Creates h_sync,v_sync signals
    - Creates display enable signal and horizontal, vertical
    pixel position in display (h,v)
    */
    
    `default_nettype none
    
    
    module vga_sync(
    input wire clk_in,
    input wire reset,
    output reg h_sync,
    output reg v_sync,
    output wire clk_sys,
    output reg [11:0] h_count,
    output reg [11:0] v_count,
    output reg display_en
    );
    
    
    // Pixel counters
    reg [11:0] h_counter = 0;
    reg [11:0] v_counter = 0;
    
    /*
    
    //FOR 100MHz
    
    localparam h_pixel_total = 3200;
    localparam h_pixel_display = 2560;
    localparam h_pixel_front_porch_amount = 64;
    localparam h_pixel_sync_amount = 384;
    localparam h_pixel_back_porch_amount = 192;
    
    localparam v_pixel_total = 2100;
    localparam v_pixel_display = 1920;
    localparam v_pixel_front_porch_amount = 40;
    localparam v_pixel_sync_amount = 8;
    localparam v_pixel_back_porch_amount = 132;
    
    */
    
    // FOR 50MHz
    
    localparam h_pixel_total = 1600;
    localparam h_pixel_display = 1280;
    localparam h_pixel_front_porch_amount = 32;
    localparam h_pixel_sync_amount = 192;
    localparam h_pixel_back_porch_amount = 96;
    
    localparam v_pixel_total = 1050;
    localparam v_pixel_display = 960;
    localparam v_pixel_front_porch_amount = 20;
    localparam v_pixel_sync_amount = 4;
    localparam v_pixel_back_porch_amount = 66;
    
    
    /*
    
    //FOR 25MHz
    
    localparam h_pixel_total = 800;
    localparam h_pixel_display = 640;
    localparam h_pixel_front_porch_amount = 16;
    localparam h_pixel_sync_amount = 96;
    localparam h_pixel_back_porch_amount = 48;
    
    localparam v_pixel_total = 525;
    localparam v_pixel_display = 480;
    localparam v_pixel_front_porch_amount = 10;
    localparam v_pixel_sync_amount = 2;
    localparam v_pixel_back_porch_amount = 33;
    
    */
    
    always @(posedge clk_in) begin
    
    if (reset) begin
    //Reset counter values
    h_counter <= 0;
    v_counter <= 0;
    display_en <= 0;
    end
    else
    begin
    // Generate display enable signal
    if (h_counter < h_pixel_display && v_counter < v_pixel_display)
    display_en <= 1;
    else
    display_en <= 0;
    
    //Check if horizontal has arrived to the end
    if (h_counter >= h_pixel_total)
    begin
    h_counter <= 0;
    v_counter <= v_counter + 1;
    end
    else
    //horizontal increment pixel value
    h_counter <= h_counter + 1;
    // check if vertical has arrived to the end
    if (v_counter >= v_pixel_total)
    v_counter <= 0;
    end
    end
    
    always @(posedge clk_in) begin
    // Check if sync_pulse needs to be created
    if (h_counter >= (h_pixel_display + h_pixel_front_porch_amount)
    && h_counter < (h_pixel_display + h_pixel_front_porch_amount + h_pixel_sync_amount) )
    h_sync <= 0;
    else
    h_sync <= 1;
    // Check if sync_pulse needs to be created
    if (v_counter >= (v_pixel_display + v_pixel_front_porch_amount)
    && v_counter < (v_pixel_display + v_pixel_front_porch_amount + v_pixel_sync_amount) )
    v_sync <= 0;
    else
    v_sync <= 1;
    end
    
    // Route h_/v_counter to out
    always @ (posedge clk_in) begin
    h_count <= h_counter;
    v_count <= v_counter;
    end
    
    endmodule

    ***
    Trying to reconfigure the raspberry pi now for DPI output based on this document : https://www.raspberrypi.com/documentation/computers/raspberry-pi.html
    Here’s what I did :
    dtparam=spi=off
    dtparam=i2c_arm=off
    #display_auto_detect=1 // comment this
    #dtoverlay=vc4-kms-v3d // comment this
    gpio=0-9=a2
    gpio=12-17=a2
    gpio=20-25=a2
    gpio=26-27=a2
    dtoverlay=dpi24
    enable_dpi_lcd=1
    display_default_lcd=1
    extra_transpose_buffer=2
    dpi_group=2
    dpi_mode=87
    dpi_output_format=0x7f216
    dpi_timings=720 0 40 48 128 720 0 13 3 15 0 0 0 60 0 41000000 4
    Will now try to get the raspberry pi going at 50000000. Doesn’t seem to help FPGA with FPGA H&V to record or play anything back.
    Testing to see if I can very simply just let the raspberry pi signals pass through the FPGA :
    module vga_sync(
    input wire R7IN,
    input wire R6IN,
    input wire R5IN,
    input wire R4IN,
    input wire G7IN,
    input wire G6IN,
    input wire G5IN,
    input wire G4IN,
    input wire B7IN,
    input wire B6IN,
    input wire B5IN,
    input wire B4IN,
    output wire R7OUT,
    output wire R6OUT,
    output wire R5OUT,
    output wire R4OUT,
    output wire G7OUT,
    output wire G6OUT,
    output wire G5OUT,
    output wire G4OUT,
    output wire B7OUT,
    output wire B6OUT,
    output wire B5OUT,
    output wire B4OUT
    );
    
    assign R7OUT = R7IN;
    assign R6OUT = R6IN;
    assign R5OUT = R5IN;
    assign R4OUT = R4IN;
    assign G7OUT = G7IN;
    assign G6OUT = G6IN;
    assign G5OUT = G5IN;
    assign G4OUT = G4IN;
    assign B7OUT = B7IN;
    assign B6OUT = B6IN;
    assign B5OUT = B5IN;
    assign B4OUT = B4IN;
    

    I made a pcf to go with this test code :

    set_io R7IN 107
    set_io R6IN 106
    set_io R5IN 105
    set_io R4IN 104
    set_io G7IN 97
    set_io G6IN 96
    set_io G5IN 95
    set_io G4IN 112
    set_io B7IN 102
    set_io B6IN 101
    set_io B5IN 99
    set_io B4IN 98
    set_io R7OUT 81
    set_io R6OUT 80
    set_io R5OUT 79
    set_io R4OUT 78
    set_io G7OUT 94
    set_io G6OUT 93
    set_io G5OUT 91
    set_io G4OUT 90
    set_io B7OUT 76
    set_io B6OUT 75
    set_io B5OUT 74
    set_io B4OUT 73

    Memory soldering done, added to the pcf :

    set_io v_sync 87
    set_io h_sync 88
    set_io clk_in 64
    set_io reset 66
    set_io r0 81
    set_io r1 80
    set_io r2 79
    set_io g0 94
    set_io g1 93
    set_io g2 91
    set_io b0 76
    set_io b1 75
    set_io b2 74
    
    set_io io0 1
    set_io io1 2
    set_io io2 3
    set_io io3 4
    set_io io4 122
    set_io io5 121
    set_io io6 120
    set_io io7 119
    
    set_io a0 138
    set_io a1 139
    set_io a2 141
    set_io a3 142
    set_io a4 143
    set_io a5 8
    set_io a6 9
    set_io a7 10
    set_io a8 11
    set_io a9 12
    set_io a10 136
    set_io a11 135
    set_io a12 134
    set_io a13 129
    set_io a14 128
    set_io a15 117
    set_io a16 116
    set_io a17 115
    set_io a18 114
    set_io a19 137
    set_io a20 113
    
    set_io cs1 71
    set_io we0 7

    *********************

    Refreshing my verilog learning from HDLbits :

    A note on wire vs. reg: The right-hand-side of an assign statement must be a net type (e.g., wire), while the left-hand-side of a procedural assignment (in an always block) must be a variable type (e.g., reg).

                                                                                                      *******

    you can do assign w = a; outside of an always block

    assign continuously drives a into w

    assign out = a&b; makes out equal to a AND’ed with b

    it’s a combinational (i.e., memory-less, with no hidden state) function

    using assign is the same as using always @(*)

                                                                                                *****

    if you don’t declare it, output [3:0] a; will be a vector of type wire

    to declare a vector : type [upper:lower] vector_name;

                                                                                                  *****

    A bitwise operation between two N-bit vectors replicates the operation for each bit of the vector and produces a N-bit output, while a logical operation treats the entire vector as a boolean value (true = non-zero, false = zero) and produces a 1-bit output.

    Eg.

    output [2:0] out_or_bitwise,
    output out_or_logical,

    assign out_or_bitwise = a | b;
    assign out_or_logical = a || b;

    Beware the different symbols for bitwise or logical !
                                                                                                    ****

    module mod_a ( input in1, input in2, output out );
    // Module body
    endmodule

    When instantiating mod_a inside top_module :

    module top_module (
    input a,
    input b,
    output out
    );

    // Create an instance of “mod_a” named “inst1”, and connect ports by name:
    mod_a inst1 (
    .in1(a), // Port”in1″connects to wire “a”
    .in2(b), // Port “in2” connects to wire “b”
    .out(out) // Port “out” connects to wire “out”
    // (Note: mod_a’s port “out” is not related to top_module’s wire “out”.
    // It is simply coincidence that they have the same name)
    );

                                                                                                    ***

    ALWAYS BLOCKS :

    Clocked: always @(posedge clk)

    Procedural blocks have a richer set of statements (e.g., if-then, case), cannot contain continuous assignments,

    Clocked always blocks create a blob of combinational logic just like combinational always blocks, but also creates a set of flip-flops (or “registers”) at the output of the blob of combinational logic. Instead of the outputs of the blob of logic being visible immediately, the outputs are visible only immediately after the next (posedge clk).

    In a locked always block, only use procedural non-blocking assignment: (x <= y;)

                                                                                                ***

    Combinational circuits must have a value assigned to all outputs under all conditions. This usually means you always need else clauses or a default value assigned to the outputs.

    This can cause the Warning (10240): … inferring latch(es) error message

                                                                                                ***

    Saw some inspiring machines in Munich :

    Barrels would be cool to incorporate into my next prototype.

    I wonder if I could make an analog feedback set up with linear actuators, a screen and camera…

    I love the scientific instrument aspect. The monitor hovering on the side could be cool to incorporate.

     

    *****

    Check out this analog video feedback setup… could I do a miniature version ? :

     

    ****

    One reaction to the museum visit it to think of what makes something look like a machine.

    • an agglomeration of mainly metal (especially machined aluminum and folded steel) and plastic modules
    • rotation and linear motion, symmetry about a central axis or a pile of modules facing the user

    If I wanted to focus on the technical object itself, I could work on a custom cooling setup, with thin curving pipes, knobs that allow for various kinds of custom fine control, screens and cameras on various linear axes for feedback loop control.

    A DIY water cooler :

    Amazing! DIY Mini Liquid Cooling System - YouTube

    Importance apprenti Gonfle diy cpu water block la toux mosaïque sans rapport

    A custom keyboard from https://keyboards.tanebox.com/a/blog/939/ :

    I like the exposed keyboard that extends into a circuit board with chips on the top. Could it be possible to make a kind of reprogrammable FPGA video synth computer ? Perhaps I could also learn things about video by taking a video file from a an SD card and then moving it into a dual port DRAM memory to be displayed.

    This gets at something that feels important : I am always trying to get myself to some harder to get to place, with the help of DIY tech, before making artsy stuff. It’s almost like I feel I’m not competitive enough starting from the most accessible starting point (classical art like drawing, painting, sculpture). Sam suggested that I combine my interest in hardware with software. Can I make a kind of custom programmable computer that is my own and then program it in a low level language (like assembly or verilog)?

    MechBoardsのPlaid // ThroughHole – コトコト

    I love the windy wire plug found on the mechanical keyboard subreddit :

    r/CustomKeyboards - I Can't Get No

    r/CustomKeyboards - My totally customized split keyboard

    From https://spectrum.ieee.org/yugoslavia-diy-microcomputer :

    Keyboard buttons from Mouser : https://www.mouser.fr/ProductDetail/E-Switch/KS1100OA1AF060?qs=gt1LBUVyoHkJwqhwHM2wRw%3D%3D

    ***

    Or, in the totally opposite direction (not going for gimmicks or flashiness), super inexpensive DRAM chips (1 euro for 16Mbit) :

    https://www.mouser.fr/ProductDetail/ISSI/IS42S16100H-7TL?qs=yfIbTn1BQ2BEUPfKEYbGCA%3D%3D

    ****

    Had a thought : I am basically trying to “play” the VGA format itself like an instrument.

    ****

    So making a pin that is both input and output for an FPGA is not so simple ! This post helped : https://electronics.stackexchange.com/questions/33144/birectional-i-o-pin-in-verilog

    My attempts to make diagrams :

     

    I also cleaned up the code a bit, making vectors with indices and following naming conventions :

    I can see evidence of something being recorded and played back out of sync (only when rpi input provides H&V and the FPGA used the rpi 41MHz pixel clock or the 100MHz clock) but it’s far from working completely.

    I can send external clocks that are divisions of 41MHz clock (20.5MHz, 10.25MHz) when rpi is supplying the H&V and it just barely works.

    The FPGA works with external 25MHz clock also, but I can’t see any recordings.

    *EDIT* I can get pretty clear recordings with the RPI clock and H&V after fiddling. Still no luck with recording RPI color information and then showing it at FPGA H&V with either pixel clock. I have gotten rid of the test colors and now am showing full screen FPGA. Twiddled 4 versus 3 bits on the output.

    `default_nettype none
    
    module vga_sync_test(
    
    input wire clk_in,
    input wire reset,
    
    input wire rec, // Direction of io, 1 = set output, 0 = read input
    
    //RASPBERRY PI
    input wire [3:0] r_in,
    input wire [3:0] b_in,
    input wire [3:0] g_in,
    
    //VGA OUT
    output reg [3:0] r_out,
    output reg [3:0] b_out,
    output reg [3:0] g_out,
    
    output wire h_sync,
    output wire v_sync,
    
    //SRAM
    
    output reg [20:0] addr,
    inout wire [7:0] io, // inout must be type wire
    
    output wire cs_1,
    output reg we_0
    
    );
    
    wire [7:0] data_in;
    wire [7:0] data_out;
    
    reg [7:0] a, b;
    
    assign io = rec ? a : 8'bzzzzzzzz;
    
    assign data_out = b;
    
    assign data_in[1:0] = r_in[3:2];
    assign data_in[3:2] = b_in[3:2];
    assign data_in[5:4] = g_in[3:2];
    assign data_in[7:6] = 2'b00;
    
    wire display_en;
    wire [11:0] h_count;
    wire [11:0] v_count;
    
    assign cs_1 = 0; // low to select, high to deselect
    
    localparam h_pixel_max = 1280;
    localparam v_pixel_max = 960;
    localparam h_pixel_half = 640;
    localparam v_pixel_half = 480;
    
    
    //SRAM address counter
    
    always @(posedge clk_in) begin
        if (reset)
            addr <= 0;
        else
            addr <= addr+1;
        end
    
    
    //REC control
    
    always @(posedge clk_in) begin
          b <= io;
          a <= data_in;
         if (rec) begin
              we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
         end
         else begin
              we_0 <= 1;
         end
    end
    
    //VGA COLOR OUT
    
    always @(posedge clk_in) begin
    if (display_en) begin
    if (h_count < h_pixel_half
    && v_count < v_pixel_half) begin
    
    r_out[1:0] <= data_out[1:0];
    r_out[2] <= 1'b0;
    g_out[1:0] <= data_out[3:2];
    g_out[2] <= 1'b0;
    b_out[1:0] <= data_out[5:4];
    b_out[2] <= 1'b0;
    
    end else if (h_count > h_pixel_half
    && v_count < v_pixel_half) begin
    
    
    r_out <= 3'b000;
    g_out <= 3'b111;
    b_out <= 3'b000;
    
    end else if (h_count < h_pixel_half
    && v_count > v_pixel_half) begin
    
    
    r_out <= 3'b111;
    g_out <= 3'b000;
    b_out <= 3'b000;
    
    end else begin
    
    
    r_out <= 3'b111;
    g_out <= 3'b111;
    b_out <= 3'b111;
    
    end
    end else begin
    
    r_out <= 3'b000;
    g_out <= 3'b000;
    b_out <= 3'b000;
    end
    end
    
    vga_sync vga_s(
    .clk_in(clk_in),
    .reset(reset),
    .h_sync(h_sync),
    .v_sync(v_sync),
    .h_count(h_count),
    .v_count(v_count),
    .display_en(display_en) // '1' => pixel region
    );
    
    endmodule
    
    

     

    Here is the complete pcf :

    set_io v_sync 87
    set_io h_sync 88
    set_io clk_in 64
    set_io reset 66
    
    set_io io[0] 1
    set_io io[1] 2
    set_io io[2] 3
    set_io io[3] 4
    set_io io[4] 122
    set_io io[5] 121
    set_io io[6] 120
    set_io io[7] 119
    
    set_io io[8] 45
    set_io io[9] 47
    set_io io[10] 48
    set_io io[11] 49
    set_io io[12] 28
    set_io io[13] 26
    set_io io[14] 25
    set_io io[15] 24
    
    set_io addr[0] 138
    set_io addr[1] 139
    set_io addr[2] 141
    set_io addr[3] 142
    set_io addr[4] 143
    set_io addr[5] 8
    set_io addr[6] 9
    set_io addr[7] 10
    set_io addr[8] 11
    set_io addr[9] 12
    set_io addr[10] 136
    set_io addr[11] 135
    set_io addr[12] 134
    set_io addr[13] 129
    set_io addr[14] 128
    set_io addr[15] 117
    set_io addr[16] 116
    set_io addr[17] 115
    set_io addr[18] 114
    set_io addr[19] 137
    set_io addr[20] 113
    
    set_io addr[21] 38
    set_io addr[22] 39
    set_io addr[23] 41
    set_io addr[24] 42
    set_io addr[25] 43
    set_io addr[26] 52
    set_io addr[27] 56
    set_io addr[28] 58
    set_io addr[29] 60
    set_io addr[30] 61
    set_io addr[31] 34
    set_io addr[32] 33
    set_io addr[33] 32
    set_io addr[34] 31
    set_io addr[35] 29
    set_io addr[36] 23
    set_io addr[37] 22
    set_io addr[38] 21
    set_io addr[39] 20
    set_io addr[40] 37
    set_io addr[41] 19
    
    set_io cs_0 144
    set_io cs_1 71
    set_io cs_2 44
    set_io cs_3 63
    
    set_io we_0 7
    set_io we_1 50
    
    set_io rec 62
    
    set_io r_in[0] 107
    set_io r_in[1] 106
    set_io r_in[2] 105
    set_io r_in[3] 104
    set_io g_in[0] 97
    set_io g_in[1] 96
    set_io g_in[2] 95
    set_io g_in[3] 112
    set_io b_in[0] 102
    set_io b_in[1] 101
    set_io b_in[2] 99
    set_io b_in[3] 98
    
    set_io r_out[0] 81
    set_io r_out[1] 80
    set_io r_out[2] 79
    set_io r_out[3] 78
    set_io g_out[0] 94
    set_io g_out[1] 93
    set_io g_out[2] 91
    set_io g_out[3] 90
    set_io b_out[0] 76
    set_io b_out[1] 75
    set_io b_out[2] 74
    set_io b_out[3] 73

    ****

    Added the third SRAM and having issues, commented out one new pin at a time and it seems like address 18 (on pin 20) is problematic. When I remove this pin everything else functions well enough for a memory with a missing pin however.

    `default_nettype none
    
    module vga_sync_test(
    
    input wire clk_in,
    input wire reset,
    
    input wire rec, // Direction of io, 1 = set output, 0 = read input
    
    //RASPBERRY PI
    input wire [3:0] r_in,
    input wire [3:0] b_in,
    input wire [3:0] g_in,
    
    //VGA OUT
    output reg [3:0] r_out,
    output reg [3:0] b_out,
    output reg [3:0] g_out,
    
    output wire h_sync,
    output wire v_sync,
    
    //SRAM
    
    output reg [20:0] addr,
    inout wire [7:0] io, // inout must be type wire
    
    output wire cs_1,
    output wire cs_0,
    output reg we_0
    
    );
    
    wire [7:0] data_in;
    wire [7:0] data_out;
    
    reg toggle;
    
    reg [7:0] a, b;
    
    assign io = rec ? a : 8'bzzzzzzzz;
    
    assign data_out = b;
    
    assign data_in[1:0] = r_in[3:2];
    assign data_in[3:2] = b_in[3:2];
    assign data_in[5:4] = g_in[3:2];
    assign data_in[7:6] = 2'b00;
    
    wire display_en;
    wire [11:0] h_count;
    wire [11:0] v_count;
    
    localparam h_pixel_max = 1280;
    localparam v_pixel_max = 960;
    localparam h_pixel_half = 640;
    localparam v_pixel_half = 480;
    
    // CS: low to select, high to deselect
    
    assign cs_0 = toggle ? 1 : 0;
    assign cs_1 = toggle ? 0 : 1;
    
    //SRAM address counter
    
    always @(posedge clk_in) begin
    
    if (addr == 0) begin
    toggle <= toggle+1;
    end
    
    if (reset) begin
    addr <= 0;
    end else begin
    addr <= addr+1;
    
    end
    end
    
    
    //REC control
    
    always @(posedge clk_in) begin
    
    b <= io;
    a <= data_in;
    
    if (rec) begin
    we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
    end
    else begin
    we_0 <= 1;
    end
    end
    
    //VGA COLOR OUT
    
    always @(posedge clk_in) begin
    if (display_en) begin
    
    r_out[3:2] <= data_out[1:0];
    r_out[1:0] <= data_out[1:0];
    g_out[3:2] <= data_out[3:2];
    g_out[1:0] <= data_out[3:2];
    b_out[3:2] <= data_out[5:4];
    b_out[1:0] <= data_out[5:4];
    
    end else begin
    
    r_out <= 4'b0000;
    g_out <= 4'b0000;
    b_out <= 4'b0000;
    end
    end
    
    vga_sync vga_s(
    .clk_in(clk_in),
    .reset(reset),
    .h_sync(h_sync),
    .v_sync(v_sync),
    .h_count(h_count),
    .v_count(v_count),
    .display_en(display_en) // '1' => pixel region
    );
    
    endmodule

    ***

    I made an animated video of the assembly of the board :

    When I make the next board I’ll  record the eagle CAD and make another video.

    Here are some images of what a new device could look like !

    What about an FPGA chip holder ? And a snazzy pivotable SD card holder ? Using meanders this time? With its own fictional programming language like in that assembly video game ? With a mini PCI-E connector somewhere ?

    To pre-digest the videos for the FPGA before putting them on a gigantic SD card, I could use ffmpeg to output a less compressed format like .avi or an .mkv and then erase the header and footer ?

    Have a name (little Lebowski urban achiever is too long), and a format 100×80 (free Eagle max), and a rough layout (HDMI super close, USB next to FTDI and to flash memory, all the memory to the right equidistant to the chip). AND, some kind of transparent cover, a first for me, make it seem more like a legitimate product and would reduce the risk of short circuits from something conductive falling on the board. For the symbols on the keyboard, there is the Comodore 64 keyboard symbol set which is cool. Or some ancient symbols ??

    I am now moving away from SDRAM, challenging and I’m not sure why I’m putting myself through it. The real interest is going to be decoding videos stored on the SD card and putting them on screen. I want to try to do it like the pros – drawing to the screen in the blanking period and using only one SRAM.

    ****

    Still trying to resolve the A18 issue.

    • I tried soldering another IO pin to pin 20, but this led to total chaos, none of the address pins working. I could have tried severing the link between the pin 20 and the SRAM but I don’t want to damage the board before I identify the problem (it may be in the code?).
    • Trying to lower the size of the address counter to stop at 17 instead of going to 18.
    • I connected to the rpi’s DEN and changed the code to work with it instead of the FPGAs display en (it doesn’t work anyways). It is cool as it is more likely to capture the image on the rpi if I understand correctly.

    ****

    I am now trying some of the code I was dreaming about – modifying the input and output video stream based on other parallel data.

    Here is one strategy based on two bits of the same color having an impact on recording and playback.

    if (b[3]==b[2]) begin
    a <= data_in;
    end
    else begin
    a <= 8'b00000000;
    end

    I have the two memories working (excepted A18 on the second SRAM), and also tried reading DEN from the rpi. It makes some pretty funky glitches by touching the RAM pins with my hands !

    Also check out the bitmap to program the FPGA :

     

    `default_nettype none
    
    module vga_sync_test(
    
    input wire clk_in,
    input wire reset,
    
    input wire rec, // Direction of io, 1 = set output, 0 = read input
    
    //RASPBERRY PI
    input wire [3:0] r_in,
    input wire [3:0] b_in,
    input wire [3:0] g_in,
    input wire DEN, // I SOLDERED A WIRE HERE TO ACCESS DEN ON THE RPI !!
    
    //VGA OUT
    output reg [3:0] r_out,
    output reg [3:0] b_out,
    output reg [3:0] g_out,
    
    output wire h_sync,
    output wire v_sync,
    
    //SRAM
    
    output reg [20:0] addr,
    output reg [20:0] addr_copy,
    inout wire [15:0] io, // inout must be type wire
    
    output wire cs_3,
    output wire cs_2,
    output wire cs_1,
    output wire cs_0,
    
    output reg we_1,
    output reg we_0
    
    );
    
    wire [15:0] data_in;
    wire [15:0] data_out;
    
    reg [1:0] toggle;
    
    reg [15:0] a, b;
    
    assign io = rec ? a : 16'bzzzzzzzzzzzzzzzz;
    
    assign data_out = b;
    
    assign data_in[1:0] = DEN ? r_in[3:2] : 0; // I SOLDERED A WIRE HERE TO ACCESS DEN ON THE RPI !!
    assign data_in[3:2] = DEN ? b_in[3:2] : 0; // I SOLDERED A WIRE HERE TO ACCESS DEN ON THE RPI !!
    assign data_in[5:4] = DEN ? g_in[3:2] : 0; // I SOLDERED A WIRE HERE TO ACCESS DEN ON THE RPI !!
    assign data_in[7:6] = 2'b00;
    
    assign data_in[9:8] = DEN ? r_in[3:2] : 0;
    assign data_in[11:10] = DEN ? b_in[3:2] : 0;
    assign data_in[13:12] = DEN ? g_in[3:2] : 0;
    assign data_in[15:14] = 2'b00;
    
    wire display_en;
    wire [11:0] h_count;
    wire [11:0] v_count;
    
    localparam h_pixel_max = 1280;
    localparam v_pixel_max = 960;
    localparam h_pixel_half = 640;
    localparam v_pixel_half = 480;
    
    // CS: low to select, high to deselect
    
    assign cs_0 = toggle == 2'b00 ? 1 : 0;
    assign cs_1 = toggle == 2'b01 ? 1 : 0;
    assign cs_2 = toggle == 2'b10 ? 1 : 0;
    assign cs_3 = toggle == 2'b11 ? 1 : 0;
    
    //SRAM address counter
    
    always @(posedge clk_in) begin
    
    if (addr == 0) begin
       toggle <= toggle+1;
    end
    
    if (reset) begin
       addr <= 0;
    end else begin
       addr <= addr+1;
       addr_copy <= addr_copy+1;
    
    end
    end
    
    //REC control
    
    always @(posedge clk_in) begin
    
       b <= io;
       a <= data_in;
    if (rec) begin
       we_0 <= addr[0]; //not sure why it isn't the inverse of addr[0] but that doesn't make the inverse on 'scope
       we_1 <= addr[0];
    end
    else begin
       we_0 <= 1;
       we_1 <= 1;
    end
    end
    
    //VGA COLOR OUT
    
    always @(posedge clk_in) begin
    if (DEN) begin // I SOLDERED A WIRE HERE TO ACCESS DEN ON THE RPI !!
    if (toggle==2'b00 || toggle==2'b01 ) begin
       r_out[3:2] <= data_out[1:0];
       r_out[1:0] <= data_out[1:0];
       g_out[3:2] <= data_out[3:2];
       g_out[1:0] <= data_out[3:2];
       b_out[3:2] <= data_out[5:4];
       b_out[1:0] <= data_out[5:4];
    
    end else begin
       r_out[3:2]<= data_out[9:8];
       r_out[1:0]<= data_out[9:8];
       g_out[3:2]<= data_out[11:10];
       g_out[1:0]<= data_out[11:10];
       b_out[3:2]<= data_out[13:12];
       b_out[1:0]<= data_out[13:12];
    end
    end else begin
       r_out <= 4'b0000;
       g_out <= 4'b0000;
       b_out <= 4'b0000;
    end
    end
    
    vga_sync vga_s(
    .clk_in(clk_in),
    .reset(reset),
    .h_sync(h_sync),
    .v_sync(v_sync),
    .h_count(h_count),
    .v_count(v_count),
    .display_en(display_en) // '1' => pixel region
    );
    
    endmodule

    From https://www.fpga4fun.com/VerilogTips.html:

    • I need to initialize verilog counters !!
    • Another way of selecting 4 bits in a vector : wire [3:0] thisis4also = myvalue[16+:4];
    • It might be smarter to use a PLL than to take the external clock directly.
    • I need to debounce all input buttons !

    ****

    Check out shaders that take video as input :

    https://www.shadertoy.com/view/ctsyzN

    https://www.shadertoy.com/view/XtcSRs

    ****

    Tried to make some vignettes of the new board to show the impedence matching :

    I think I will order it in white with the meanders exposed like on the VGA spaghettini.

    Color options (I’m leaning towards white or maybe green):

    ***************************

    I am now thinking about how I will be able to share all that I am trying to learn about verilog and FPGAs in workshops. The bottleneck is programming the FPGA without special hardware.

    OLIMEX has a link to a rasberry pi utility called flashrom that can program and read FLASH memory documented here : https://github.com/OLIMEX/iCE40HX1K-EVB/tree/master/programmer/olimexino-32u4%20firmware

    These raspberry pi examples program the ice40 and looks simple :

    • https://j-marjanovic.io/lattice-ice40-configuration-using-raspberry-pi.html
    • https://github.com/plex1/raspice40

    The steps have been automated here : https://notabug.org/sagaracharya/swarajya/src/master/hdl_to_hx8k/on_pi

    Also for the rpi Pico : https://github.com/dan-rodrigues/pico-hx

    I could knock off the 2232 FTDI and have made something like this: https://shop.trenz-electronic.de/en/TEM0009-02-FPGA-USB-programmer-JTAG-for-development-with-Microchip-FPGAs

    Most interestingly, it seems like Arduino Uno could program an FPGA using a library like SPIMemory by Prajwal Bhattaram : https://github.com/Marzogh/SPIMemory/tree/v2.2.0

    Finally, I could make a board like the gameduino that has an API for the Arduino to use to talk to the FPGA which handles fast screen drawing. This would mean everything could be done within the Arduino IDE.

    EDIT* Come to think of it, even if the FTDI adds cost, it would make everything so much easier for a workshop – one piece of software and no extra hardware to program after for the participants. But when I plug it in it says USB device malfunctioned…(I also realized that I think I can forgo the RS232 connections between the FTDI and the FPGA and just connect it to the EEPROM and FLASH.)

    EDIT* The OLIMEX code for Arduino is also not compiling and winIceProgDuino.exe (to program FPGA by sending serial commands to Arduino firmware I suppose) is not opening either.

    EDIT*2 The Arduino sketch compiles but only for MEGA, Leonardo, and Micro so far (no UNO or NANO or MINI) but only after removing the iceprog.cpp file.

    ******************

    Some thoughts on low-level versus high-level programming :

    Something unsatisfying about making something from a pre-digested meta language (fast.ai for instance). Lost the feeling of ownership of the thing you’re making.

    Obviously you can do much more when you stand atop of the shoulders of giants, but you are less connected with the surface of the ground because you are working with abstractions. If something goes wrong, you have no recourse because you are just a customer.
    Low level languages make the architecture of the machine visible by laying it bear. There is agency. Then again, to do something very simple can be hard and require lots of code which provides many opportunities for silly mistakes.
    Yet for workshops it seems good to avoid low level stuff. It’s not impressive, it’s discouraging, and it’s hard to debug.
    *******************
    I have soldered the new board, I love the keys !! But HDMI is still not working.

    Lots of fun could be had rearranging the keys using the same row heights  :

    I wonder if I should just transition to making interfaces directly – I am a designer after all !

    The FPGA4fun.com code is not synthesizing, I think because it requires clock dividing modules that I don’t have.
    I tried this code and it compiles but I don’t see anything on the ‘scope: https://gist.github.com/uXeBoy/0d46e2f1560f73dd573d83e78309bfa0
    This code eventually produced signals on HDMI out, will have to wait to see if it is acceptable HDMI once I have the proper resistor LVDS setup.
    `default_nettype none // disable implicit definitions by Verilog
    //-----------------------------------------------------------------
    // minimalDVID_encoder.vhd : A quick and dirty DVI-D implementation
    //
    // Author: Mike Field <hamster@snap.net.nz>
    //
    // DVI-D uses TMDS as the 'on the wire' protocol, where each 8-bit
    // value is mapped to one or two 10-bit symbols, depending on how
    // many 1s or 0s have been sent. This makes it a DC balanced protocol,
    // as a correctly implemented stream will have (almost) an equal
    // number of 1s and 0s.
    //
    // Because of this implementation quite complex. By restricting the
    // symbols to a subset of eight symbols, all of which having have
    // five ones (and therefore five zeros) this complexity drops away
    // leaving a simple implementation. Combined with a DDR register to
    // send the symbols the complexity is kept very low.
    //-----------------------------------------------------------------
    
    module top(
    clk100, hdmi_p, hdmi_n
    );
    
    input clk100;
    output [3:0] hdmi_p;
    output [3:0] hdmi_n;
    
    // For holding the outward bound TMDS symbols in the slow and fast domain
    reg [9:0] c0_symbol; reg [9:0] c0_high_speed;
    reg [9:0] c1_symbol; reg [9:0] c1_high_speed;
    reg [9:0] c2_symbol; reg [9:0] c2_high_speed;
    reg [9:0] clk_high_speed;
    
    reg [1:0] c2_output_bits;
    reg [1:0] c1_output_bits;
    reg [1:0] c0_output_bits;
    reg [1:0] clk_output_bits;
    
    wire clk_x5;
    reg [2:0] latch_high_speed = 3'b100; // Controlling the transfers into the high speed domain
    
    wire vsync, hsync;
    wire [1:0] syncs; // To glue the HSYNC and VSYNC into the control character
    assign syncs = {vsync, hsync};
    
    // video structure constants
    parameter hpixels = 800; // horizontal pixels per line
    parameter vlines = 525; // vertical lines per frame
    parameter hpulse = 96; // hsync pulse length
    parameter vpulse = 2; // vsync pulse length
    parameter hbp = 144; // end of horizontal back porch (96 + 48)
    parameter hfp = 784; // beginning of horizontal front porch (800 - 16)
    parameter vbp = 35; // end of vertical back porch (2 + 33)
    parameter vfp = 515; // beginning of vertical front porch (525 - 10)
    
    // registers for storing the horizontal & vertical counters
    reg [9:0] vc;
    reg [9:0] hc;
    // generate sync pulses (active high)
    assign vsync = (vc < vpulse);
    assign hsync = (hc < hpulse);
    
    always @(posedge clk_x5) begin
    //-------------------------------------------------------------
    // Now take the 10-bit words and take it into the high-speed
    // clock domain once every five cycles.
    //
    // Then send out two bits every clock cycle using DDR output
    // registers.
    //-------------------------------------------------------------
    c0_output_bits <= c0_high_speed[1:0];
    c1_output_bits <= c1_high_speed[1:0];
    c2_output_bits <= c2_high_speed[1:0];
    clk_output_bits <= clk_high_speed[1:0];
    if (latch_high_speed[2]) begin // pixel clock 25MHz
    c0_high_speed <= c0_symbol;
    c1_high_speed <= c1_symbol;
    c2_high_speed <= c2_symbol;
    clk_high_speed <= 10'b0000011111;
    latch_high_speed <= 3'b000;
    if (hc < hpixels)
    hc <= hc + 1;
    else
    begin
    hc <= 0;
    if (vc < vlines)
    vc <= vc + 1;
    else
    vc <= 0;
    end
    end
    else begin
    c0_high_speed <= {2'b00, c0_high_speed[9:2]};
    c1_high_speed <= {2'b00, c1_high_speed[9:2]};
    c2_high_speed <= {2'b00, c2_high_speed[9:2]};
    clk_high_speed <= {2'b00, clk_high_speed[9:2]};
    latch_high_speed <= latch_high_speed + 1'b1;
    end
    end
    
    always @(*) // display 100% saturation colourbars
    begin
    // first check if we're within vertical active video range
    if (vc >= vbp && vc < vfp)
    begin
    // now display different colours every 80 pixels
    // while we're within the active horizontal range
    // -----------------
    // display white bar
    if (hc >= hbp && hc < (hbp+80))
    begin
    c2_symbol = 10'b1011110000; // red
    c1_symbol = 10'b1011110000; // green
    c0_symbol = 10'b1011110000; // blue
    end
    // display yellow bar
    else if (hc >= (hbp+80) && hc < (hbp+160))
    begin
    c2_symbol = 10'b1011110000; // red
    c1_symbol = 10'b1011110000; // green
    c0_symbol = 10'b0111110000; // blue
    end
    // display cyan bar
    else if (hc >= (hbp+160) && hc < (hbp+240))
    begin
    c2_symbol = 10'b0111110000; // red
    c1_symbol = 10'b1011110000; // green
    c0_symbol = 10'b1011110000; // blue
    end
    // display green bar
    else if (hc >= (hbp+240) && hc < (hbp+320))
    begin
    c2_symbol = 10'b0111110000; // red
    c1_symbol = 10'b1011110000; // green
    c0_symbol = 10'b0111110000; // blue
    end
    // display magenta bar
    else if (hc >= (hbp+320) && hc < (hbp+400))
    begin
    c2_symbol = 10'b1011110000; // red
    c1_symbol = 10'b0111110000; // green
    c0_symbol = 10'b1011110000; // blue
    end
    // display red bar
    else if (hc >= (hbp+400) && hc < (hbp+480))
    begin
    c2_symbol = 10'b1011110000; // red
    c1_symbol = 10'b0111110000; // green
    c0_symbol = 10'b0111110000; // blue
    end
    // display blue bar
    else if (hc >= (hbp+480) && hc < (hbp+560))
    begin
    c2_symbol = 10'b0111110000; // red
    c1_symbol = 10'b0111110000; // green
    c0_symbol = 10'b1011110000; // blue
    end
    // display black bar
    else if (hc >= (hbp+560) && hc < hfp)
    begin
    c2_symbol = 10'b0111110000; // red
    c1_symbol = 10'b0111110000; // green
    c0_symbol = 10'b0111110000; // blue
    end
    // we're outside active horizontal range
    else
    begin
    c2_symbol = 10'b1101010100; // red
    c1_symbol = 10'b1101010100; // green
    //---------------------------------------------
    // Channel 0 carries the blue pixels, and also
    // includes the HSYNC and VSYNCs during
    // the CTL (blanking) periods.
    //---------------------------------------------
    case (syncs)
    2'b00 : c0_symbol = 10'b1101010100;
    2'b01 : c0_symbol = 10'b0010101011;
    2'b10 : c0_symbol = 10'b0101010100;
    default : c0_symbol = 10'b1010101011;
    endcase
    end
    end
    // we're outside active vertical range
    else
    begin
    c2_symbol = 10'b1101010100; // red
    c1_symbol = 10'b1101010100; // green
    //---------------------------------------------
    // Channel 0 carries the blue pixels, and also
    // includes the HSYNC and VSYNCs during
    // the CTL (blanking) periods.
    //---------------------------------------------
    case (syncs)
    2'b00 : c0_symbol = 10'b1101010100;
    2'b01 : c0_symbol = 10'b0010101011;
    2'b10 : c0_symbol = 10'b0101010100;
    default : c0_symbol = 10'b1010101011;
    endcase
    end
    end
    
    // red N
    defparam hdmin2.PIN_TYPE = 6'b010000;
    defparam hdmin2.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmin2 (
    .PACKAGE_PIN (hdmi_n[2]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (c2_output_bits[1]),
    .D_OUT_1 (c2_output_bits[0])
    );
    
    // red P
    defparam hdmip2.PIN_TYPE = 6'b010000;
    defparam hdmip2.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmip2 (
    .PACKAGE_PIN (hdmi_p[2]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (c2_output_bits[1]),
    .D_OUT_1 (c2_output_bits[0])
    );
    
    // green N
    defparam hdmin1.PIN_TYPE = 6'b010000;
    defparam hdmin1.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmin1 (
    .PACKAGE_PIN (hdmi_n[1]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (c1_output_bits[1]),
    .D_OUT_1 (c1_output_bits[0])
    );
    
    // green P
    defparam hdmip1.PIN_TYPE = 6'b010000;
    defparam hdmip1.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmip1 (
    .PACKAGE_PIN (hdmi_p[1]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (c1_output_bits[1]),
    .D_OUT_1 (c1_output_bits[0])
    );
    
    
    // blue N
    defparam hdmin0.PIN_TYPE = 6'b010000;
    defparam hdmin0.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmin0 (
    .PACKAGE_PIN (hdmi_n[0]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (c0_output_bits[1]),
    .D_OUT_1 (c0_output_bits[0])
    );
    
    // blue P
    defparam hdmip0.PIN_TYPE = 6'b010000;
    defparam hdmip0.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmip0 (
    .PACKAGE_PIN (hdmi_p[0]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (c0_output_bits[1]),
    .D_OUT_1 (c0_output_bits[0])
    );
    
    // clock N
    defparam hdmin3.PIN_TYPE = 6'b010000;
    defparam hdmin3.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmin3 (
    .PACKAGE_PIN (hdmi_n[3]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (clk_output_bits[1]),
    .D_OUT_1 (clk_output_bits[0])
    );
    
    
    // clock P
    defparam hdmip3.PIN_TYPE = 6'b010000;
    defparam hdmip3.IO_STANDARD = "SB_LVCMOS";
    SB_IO hdmip3 (
    .PACKAGE_PIN (hdmi_p[3]),
    .CLOCK_ENABLE (1'b1),
    .OUTPUT_CLK (clk_x5),
    .OUTPUT_ENABLE (1'b1),
    .D_OUT_0 (clk_output_bits[1]),
    .D_OUT_1 (clk_output_bits[0])
    );
    // D_OUT_0 and D_OUT_1 swapped?
    // https://github.com/YosysHQ/yosys/issues/330
    
    
    SB_PLL40_PAD #(
    .FEEDBACK_PATH ("SIMPLE"),
    .DIVR (4'b0000),
    .DIVF (7'b0001001),
    .DIVQ (3'b011),
    .FILTER_RANGE (3'b101)
    ) uut (
    .RESETB (1'b1),
    .BYPASS (1'b0),
    .PACKAGEPIN (clk100),
    .PLLOUTGLOBAL (clk_x5) // DVI clock 125MHz
    );
    
    endmodule
    
    I learned a bit about how HDMI is encoded to reduce strings of 1s or Os and how transitions are minimized. I currently suspect that the PLL is not working but I’m not sure why yet. I will turn on the PLL locked LED feature to check and then mess with other parameters (see https://zipcpu.com/blog/2017/09/14/even-i-get-stuck.html) if it isn’t working.
    *EDIT 1 Apparently IceCube2 doesn’t want me to attach the CLK to any pin other than 49 (GBIN5) or 129 (GBIN0) ! This explains why nothing is happening, the clock wasn’t even entering the FPGA.
    *EDIT 2 I connected the clock pin to 49, and made the original connection to pin 64 high Z, along with the accidental connection to pin 48 nextdoor, and now I see signals being output to the HDMI. (It’s too tight to try and solder in resistors to test the proper LVDS simulation as described in the Using Differential I/O (LVDS, Sub-LVDS) in iCE40 LP/HX Devices annex.) I used the pin constraints editor in IceCube and it added some info to the pcf which may have helped get things working ? :
    set_io hdmi_p[0] 139 -io_std SB_LVCMOS
    set_io hdmi_p[2] 144 -io_std SB_LVCMOS
    set_io hdmi_p[1] 142 -io_std SB_LVCMOS
    set_io hdmi_p[3] 137 -io_std SB_LVCMOS
    set_io clk100 49
    
    For the next version, I think I need to include resistors to simulate the LVDS signals and I should connect the clock at one of the dedicated input pins which seem to be optimized for this.
    Just realizing now that all the code I have been looking at involves using a dedicated chip (the TFP410 @ 8 euros) to convert the FPGA signals into. From the blogs it also seems like this solution works for some TVs but not all, and is better for monitors. Perhaps I should include a VGA + HDMI in case this is just too finicky. Looking at the OLIMEX VGA PMOD board, it looks like I only need a few resistors to get VGA working : https://www.olimex.com/Products/FPGA/iCE40/iCE40-IO/open-source-hardware
    I am looking for more ressources on DVI with Ice40 FPGAs and found these :
    • https://projectf.io/posts/fpga-graphics/
    • https://github.com/lawrie/hdmi_examples/tree/master
    The fact that the code above (before my modifications at least) is being used on another ice40 makes me think it should work in the end.
    **********
     

    Technische Universität München Lecture

    I have a month to work on a 45 minute lecture on my work at the Technische Universität München. I will also be doing a lecture at TU Darmstadt in November. I would like to use this opportunity to try to bring my video experiments into focus and incorporate it into my previous time-lapse projects.

    This could take the form of a video series of me playing the prepared video buffer, possibly with explanations about what is causing what effect and demonstrative images / videos ? It could be contrasted with a “typical” workflow using the constraints of a software platform like photoshop? I could prepare this a similarly focused investigation of how the prepared piano altered sound, how Sonia Sheridan altered the photocopier, Paul Schafer made custom concrete music machines, etc.

    EDIT* : I had a try !

    • It’s kind of long and I also need to get a better top view.
    • Adding text to describe what’s happening ?
    • I also see that when I’m turning knobs the device is kind of hidden. I haven’t really designed the interface to be playable and to look good and be legible while being played.
    • Looked into some other examples of video synth top down videos :

    This is making me think more about the physical presence of my devices, and how I could incorporate more mechanical parts – instead of purely making electronics. I may have to play the game a bit more, make things that look like what they do and help people understand the associations I’m trying to make.

    I could have micrometer heads for the potentiometers :

    I could have fiber optic cables too :

    Fiber Optic Cable Connectors & Assemblies | Clearfield

    I think if I every try to make a product though I would need to team up with someone who has skills I don’t.

    ***

    Why make hardware ?

    • it can be analog and digital
    • it’s faster at doing things with video than software
    • it can be custom
    • it has a live-playable interface

    ***

    What effects am I creating ? (check out Premiere to see what kinds of effects exist).

    • Recording and playing things back at different speeds (video transposition), assigning to different color channels
    • desynchronizing recordings with the screen sync so that they jump around
    • recording videos side by side in memory so that they bleed over one another.
    • Video collage and montage composer (video sampling, video looping), video plastifier, étude II, micro-montage, noise research, play, do and see, polyvideo layering, defamiliarizing of the image + reflecting the mediascape we inhabit, video as “palpable”, “nontheoretical”, and “experiential” (quotes from musique concrete wiki).
    • The structure of the videos is based on the indexicality of the medium (the SRAM and how it stores). So the recording technique is also the synthesis technique.

      Struggling to represent the painterliness of the raster videos without showing them as a video. It is completely different from my experiments with lines and triangles, and is less intuitive for me. An attempt at a kind of chronogram of a video :

      It gives some idea of the range of images I guess?

      Here are some notes I took about a year ago after visiting a painting expo that relate to my painterly video experiments :

      • Layering
      • Contour
      • Surface
      • Saturation
      • Flow vs. Choppiness
      • Roughness, relief 
      • Edges versus centers
      • Movement vs stasis 
      • Enriching of detail
      • Abstracting, cosmic making
      • Fine lines versus coarse
      • Exposure 
      • Color fields
      • Texture
      • Deep friedness
      • Films of cows, ocean. Stills of still life? Flowers, birds, drapes folding, mold, fruit, bones
      • Color mixing vs melting vs meeting
      • Paint viscosity (runny vs clotted)
      • Direction of brush strokes 
      • Contour
      • Focus direction in canvas
      • Translucent flesh, shine vs glow from interior, waxiness 
      • Graininess 
      • Composition, color pairings and form
      • Foreground displaced to background vice versa 
      • Meeting pattern planes 
      • Lighting and shadow
      • Illusion of depth
      • Cloudiness 
      • Turbulence

      Check out this “glitch” video artist Jacques Perconte that Marc shared with me : https://www.youtube.com/watch?v=8_Xhu9Vx5XM

      He works, since the 1990s, with compression and achieves a painterly, impressionistic effects and exposes them on immense projection surfaces.

      Check out also Tauba Auerbach’s work :

      Tauba Auerbach

      Tauba Auerbach at the ICA | Inside LDN

      Jack Whitten :

      Jack Whitten: Artwork Survey: 1970s | Art21

      *************

      Some quotes from Art21 series :

      James Turrel – what you are perceiving is perception itself. We don’t receive the world around us we create it.

      Jeff Wall – Art gives you an experience that alters something, it doesn’t tell you or convince you.

      Jeff Wall – When I started photography there were potential energies in the medium that weren’t being realized.

      Roland Barthes – The punctum points to those features of a photograph that seem to produce or convey a meaning without invoking any recognizable symbolic system.

      Olafur Eliasson – Art provokes a negotiation : why I am seeing this the way that I am seeing it, what does looking mean? Instead of questioning the object you are questioning yourself. Art offers the opportunity for self-evaluation.

      Tauba Auerbach – I want to learn things all the time, I want to understand the patterns behind things. I experience science through craft. With craft I try to cultivate sensitivity in a practiced, purposeful way. If you work with marbling you know just as much about viscosity and flow than a scientist, through your fingertips in a different way.

      Tauba Auerbach – Ideally I want to make an image that has a tiny effect on all the future images that you see.

      Tauba Auerbach – The pursuit of the sweet spot, cultivate the place of boundaries, limits of fraying, not hard edges.

      Cindy Sherman – I don’t know what I’m looking for until I see it.

      Richard Serra – Artists invent strategies, tools, techniques, that allow themselves to see in a way they haven’t seen before to extend their vision beyond the standardized classic reflex actions. These help us to see inside what we’re doing so we don’t get into a lock step notion of how to do what you do.

      Louise Despont – If you’re always making work for someone, and not for yourself, maybe you don’t let yourself make the mistakes that are necessary.

      Louise Despont – Each drawing is a series of tiny discoveries, it reveals/unfolds the drawing from total control. I own at most 1/4 of the drawing. The rest is something else, this is what’s exciting.

      Louise Despont – It’s best explained in the drawings, words are clumsy to describe something like the concrete feeling of spirituality.

      Liz Magor – My Studio is a space to reconcile dissonance, no one knows I’m here. When I’m not in my studio I want to be in my studio working. My tools are rudimentary. I’m here for pleasure but it’s not fun. Everyone should have a studio for mental health. I can filter out the noise and see the ever present under the radar stuff.

      Liz Magor – I’m not an animist but objects have stuff in them that comes out. I try to resurrect these objects from the netherworld.

      Liz Magor – The slowness of the material process matches the slowness of my thinking process.

      Liz Magor – I’m creating an experience for looking.

      Liz Magor – I give myself my own program, I give myself my own assignments. And art is the choices I make.

      Liz Magor – Maintaining the conditions for this production, which is not very logical, uncalled for. No one is asking me to do this, I’m barely asking me to do this. To do this I have to overlook my making with the journey of the things into the world.

      Liz Magor – After the casting I unwrap it like a little gift, there are surprises.

      Elliott Hundley – This photoshoot is so elaborate but not because I want a certain effect, because I want to not control the effects, and have a layer of unexpected results. In relinquishing control, the piece gives me something back I didn’t expect.

      Jack Whitten – I’m not a narrative painter.

      Jack Whitten – I built a device to move large amounts of acrylic paint in one gesture.

      Katherina Gross – Painting is not linear. The synchronicity in painting is compelling for your thought process. I’m trying to grasp some of those fast thoughts that are moving through my brain.

      *************

      Reading about the history of video synthesis.

      From https://museumzero.blogspot.com/2013/12/its-all-baseball-nam-june-paik-starts.html)

      Nam June Paik sees Taoist energy in working with the flow of TV video :

      “To receive simultaneously the parallel flows of many independent movements is, as Paik pointed out at the time, a classical Taoist way of meditating: by becoming aware of everything going on in the present, you discover eternity right now.” 

      “I love Chinese history, surprise, nonlinearity.  You know what judo means?  A way to be soft.  You let other guy do all the work.  I think in some Oriental way.  In TV I try not to be boss, to stay as small as possible.  So that seems to work. “ 

      “So then when I started TV, the best decision I made in life is not really to go into TV, but to do work inside TV.”

      “Imagine everything that exists is flowing, like the tao, in a perpetual motion, with endless waves, like the ocean.  We come from it; we go back to it.  Whatever surges forward falls back, so Lao-tse, who wrote the Tao Te Ching (On the Nature of the Way), urges us not to try too hard.  Relax; be like a baby; follow whatever path presents itself.  That is the way. ”

      Not striving to fit yourself to some preconceived ideal, like a Buddhist or a Confucian; simply being, following breath wherever it blows.  In this sense, the television signal is like the tao; endlessly variable, continually reversing itself, dying, being reborn, it is never idle, yet it is so vast and perpetual that it often seems calm as the sea.

      Beginning with whatever live programs came along, distorting them, then recombining his distortions like waves on the ocean surface, Paik created an indeterminate and endless show for the Galerie Parnass, in Wuppertal, Germany, in 1963.  

      From Scanimate: The Origins of Computer Motion Graphics :

      • This is about the history of television, these were the first ways to put text and make graphics on screen (Media Archaeology!). Nowadays visuals are gratuitous. What did we lose in the transition to digital ? The imperfections, the proximity to the functioning.
      • Had a role in music videos, and news segments, so was part of culture.
      • “Blooming” is the term for the glowing edges in CRT Scanimate.
      • It’s real life artefacts. It’s like exploding scale models instead of GCI.
      • They made cookbooks with the recipes of different effects that created while working with clients.

      From https://wearethemutants.com/2018/01/09/a-sloppy-machine-like-me-the-history-of-video-synthesizers/

      Like its close temporal and conceptual counterpart, the audio synthesizer, the video synthesizer was created iteratively by academics, artists, and tinkerers, then eagerly snatched up by the world’s biggest media producers once prototypes had proved their power and versatility.

      • Started with experimental musicians like John Cage and la musique concrète.
      • Early pirate TV station in NYC.
      • Amiga supplants the Scanimate and analog synths.
      • The 1967 Portapak release and it’s importance for activism and experimental art.
      • Video comes from the root video of Latin video (“I see”).
      • There is a relationship between images and waves  :

      https://upload.wikimedia.org/wikipedia/commons/6/61/P-type-chirplets-for-image-processing.png

      CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=641492

      This is so hard for me to understand but this somehow relates to chirping also :

      Published in IEEE International Conference on Acoustics, Speech, and Signal Processing 1992

      Time-frequency perspectives: the ‘chirplet’ transform

      Steve MannS. Haykin

      From article about DCT for image compression :

      undefined

      By Drummyfish – Own work, CC0, https://commons.wikimedia.org/w/index.php?curid=77608151

      More cool matrix math image modifications here : https://en.wikipedia.org/wiki/Digital_image_processing

      Also this website which describes it all visually ! https://setosa.io/ev/image-kernels/

      From Video Art : An Anthology :

        • The synthesizer let’s you see things that exist only in the mind’s eye.
        • visual ingredients
        • video synthesizers churn out…images based on their own electronic structure.
        • We make the image with our eyes and brain. It’s psycho-visual
        • video as concretized imagination.
        • video is surreal, the images are ephemeral products of micro electronic pulses.
        • “electronic imagery”
        • video is a medium which works with time

      Au commencement était le bruit, la poésie électronique de Steina et Woody Vasulka | sonore visuel

      The Vasulkas

      *************

      Here is an attempt to describe what could be a model of a potential “practice” :

      *EDIT* This should include me blogging ! Maybe add a RESEARCH and/or DOCUMENTATION phase ?

      *******

      Check out this pyramid of types of work :

      Bloom's revised taxonomy organized as a pyramid of learning levels with explanations of each

      I feel like my project is about analysing (and curating), I’m not sure if I take a real position.

      *******

      Check out this amazing NIME article called The Concentric Sampler: A musical instrument from a repurposed floppy disk drive by Timothy Tate about “the redundancy and physicality of magnetic recording media” which utilizes “time-based granular synthesis” and lo-fi:

      https://nime.pubpub.org/pub/uh76shf0/release/1

      Some quotes :

      • Physical media offer an opportunity to “foreground the physical medium as a tool for musical expression.”
      • “The deterioration of old magnetic tape as it is looped, defining a … musical structure.” The “failure of the medium is in focus, rather than the mechanism.”
      • “as soon as something’s on tape, it becomes a substance which is malleable and mutable and cuttable and reversible in ways that discs aren’t.”
      • “This study emphasises working within a medium’s limitation, rather than prescribing a compositional framework or sonic agenda upon it”
      • Motivation to “foreground musical and technical possibilities…and generative sonic potentials…and novel, unique expressive and
      • compositional possibilities”…to be “discoveredexplored”
      • The device is a “performable instrument and a generative sonic tool”
      • “Rather than focusing on the authenticity of the sound reproduction, the Concentric Sampler draws focus to the byproducts and the resultant indexicality and generative sonic possibilities of the floppy disk as a medium.”
      • “the term ‘musical expression’ here places value on the ability to shape and define a musical grammar from the indexicality of themedium and mechanism.”
      • subverting traditional modes ofexpression
      • “Each section is accompanied by a demonstrative video” !!!
      • The live playable instrument can “achieve the dissociation of pitch and time
      • “imperfections in … reproduction”
      • “possibilities within a framework of … making”

      Now following up on some of the references from this article:

      • You can think of an instrument as a framework that has a predisposed range (affordances). Our tools provide a certain range of suggested usages. Some things are easier and some things harder to do with an instrument, this is the instrument’s spectrum. If you stay within those ranges, you accept the silent grain of the instrument. If you make an unusual tool then, do you risk finding yourself in an unusual affordance ? (J. Mooney) Could it be possible to make videos subtractively, instead of additively ?
      • From New Media Reader : The different between representation and simulation. P.18″Digital media process the physical properties of the input data, light and sound waves, are not converted into another object but into numbers; that is, into abstract symlbols rather than analogous objects and physical surfaces. Hence, media processes are brought into the symbolic realm of math rather than physics or chemistry. Once coded numerically, the input data in a digital media production can immediately be subjected to the math processes of addition, subtraction, multiplication and division…” AND compressed AND manipulated more easily than analogue forms because they are more mutable than objects. Also easy access and quick movement of the data of an enormous mass of data. This causes the materiality of the world around us to become unsteady…The televisual
      • Because everything else is also digitized, there is a “convergence of previously discreet media forms, now a new fluid area of media decentralized production and consumption. Remediation, Multilinearity.
      • Hand-made videos. Exploring the boundary between hand tools and large-scale machines which is a distinction Marx makes P.91.

      Check out these Bauhaus era artist’s work with the audio band in video :

      Paul Schaeffer (coiner of the “club d’essai“) designed specific tools like the morphophone and phonogène for modifying audio for musique concrete :

      *******

      Watching ENSCI diploma presentations I’ve thought about how nice the sequence is : first you write something that explores a topic and cites thinkers on the subject, and then you work on a design project that relates to this somehow. Also looking back at some videos, there is already a lot of content I have produced that I could try to learn from ! Like that Amsterdam-based artist told me, I could also focus on using my tools.

      I am also doing a synth workshop in late September at Villette Makerz but I’m not sure if I can connect to that or not here. *EDIT* an FPGA board that generates patterns based on audio input is a GO !*

      I also want to make a next FPGA board which will be a software reconfigurable device so that I can move to verilog for the summer instead of making a new board for every experiment. Not sure if this could also take the form of a simple video synth for Villette Makerz or at least a simplified version perhaps.

      *******

      Some quick looking in to GPUs :

      GPUs are all about direct memory access (DMA) not mediated through the CPU. A thought about the FPGA board – perhaps I should first have Arduino mess with the clock while playing back / recording something on the 16MB board (but I’m limited to the number of pins for address and I/O – it would essentially be a counter that could change in frequency on the fly)? Having memory connected to the screen (a screen buffer) and modifying it is the fundamental computer display setup. From 1951 a memory pattern on CRT :

      It would be cool to experiment with sprites and copying parts of memory from one bank to another too like a blitter https://en.wikipedia.org/wiki/Blitter .

      It looks like what makes shaders cool is that they are programmable and have their own code languages now. Before shaders there were hardware rendering pipelines called fixed-function. Different units would be responsible for specific functions. It seems like these units would take in vertex data then output pixel colors.

      I looked in to how to do matrix multiplication in hardware. It actually seems to be more natural as an analog circuit with resistor arrays. It can also be done by multiplying two 2-bit binary numbers using this kind of logic setup :

      **********

      I am becoming more and more curious about the difference from having a screen buffer that is written to during the blanking period and having a massive bank of memory that can be recorded to like in the 128Kbit. Check out these articles about how writing and reading to video memory is dealt with in different systems :

      https://en.wikipedia.org/wiki/Tiled_rendering

      https://en.wikipedia.org/wiki/Multiple_buffering

      https://en.wikipedia.org/wiki/Vertical_blank_interrupt ,quoted from : 

      During the vertical blanking interval, the driver orders the video card to either rapidly copy the off-screen graphics area into the active display area (double buffering), or treat both memory areas as displayable, and simply switch back and forth between them (page flipping). [The two buffers are called the front and the back buffer] Some graphics systems let the software perform its memory accesses so that they stay at the same time point relative to the display hardware’s refresh cycle, known as raster interrupt or racing the beam.” [i.e. you can also draw things during the horizontal blanking interval]

      **********

      The Real-Time Corrupter is fascinating https://redscientist.com/rtc . The kinds of memory modifications it allows for are fascinating and far more dynamic than static, text-based find-and-replace experiments I’ve done in workshops. Looking at this video I learned some cool stuff (https://www.youtube.com/watch?v=n9HS6zftuSk&list=PLItZ3jvJKD7rnoxmqJJ0B5E1B9_WW9L5E&index=2). For instance, copying values from one address (in video RAM, scrolling backdrops memory space a.k.a. “nametables”, NES RAM, ROM etc.) and then reapplying them to another address every frame (called piping) or just one time, the ability to isolate specific effects with the sanitize function. It can generate noise between certain values, or shift values up or down on (called tilting), listen to a value and then reapply it after a fixed number of frames, freezing the current value somewhere for future frames, replacing values intelligently with values in certain sets (for 3D glitching). It’s like performing brain surgery while the patient is fully conscious and telling a story. All of these memory transformations could be applied with the FPGA board !

      **********

      In a similar spirit, this game called Code War involves creating a program that competes with others to write to all of the memory space. There are competing strategies (quoted from https://corewar-docs.readthedocs.io/en/latest/corewar/strategies/) for warriors :

      Rock - a warrior which rapidly bombs the core with dat instructions
      Paper - a warrior which replicates, creating multiple, parallel copies
      Scissors - a warrior which scans the core looking for other warriors

      The battle is visualized in a memory grid (https://corewar-docs.readthedocs.io/en/latest/corewar/visualisation/)

      https://upload.wikimedia.org/wikipedia/commons/c/c2/Core_War_PMars_Screenshot.png

      You can simulate and battle warrior code here : https://crypto.stanford.edu/~blynn/play/redcode.html

      **********

      Visualizing memory access in time. From https://bling.kapsi.fi/blog/x86-memory-access-visualization.html:

      *********

      Some thoughts on HCI from people at work :

      it’s research if it follows scientific method and has possible applications.

      Find your peers, try to publish in their journals.

      The idea of publishing is to get feedback from your peers.

      If you make a pedagogical tool, then you could test how effective it is at helping people learn.

      Based on this conversation, I thought about how I don’t necessarily want to promote engineering education and that I always want to be on the design/art side of things. I also have learned that I find doing something I already know how to do less exciting, and so making simple kits or small series of artworks that are guaranteed to work for days on end, is not always super rewarding for me intellectually. This leads me to the current conclusion : I want to teach and then do a kind of artistic research on the side. I should then keep building my teaching career and try to get expos of my work. I am still trying to figure out how exactly my artistic research should be communicated (through articles in design magazines, through conferences with expos connected to them?).

      **********

      I am preparing a solar workshop at Villette Makerz based on the simple solar engine with optimized SMD components. The idea is an art bot that will draw, paint, in the sun. I am working on testing the various combinations of solar panels, capacitors and voltage supervisors.

      We are also making a version 2 of the video synth modules which are all Eurorack compatible in terms of dimensions, use 3.5mm audio jacks, the eurorack power supply. The idea is that the modules be both manual and automatable for the purpose of demos and to be didactic for students learning about analog electronics.

       

      Marc is doing a great job reimagining this project – he is good at compromising and imposing the vision of the final thing on each step of the construction. I am, in contrast, driven astray I think by my fascination by what “the machine itself wants to express”…

      ********

      Finally getting around to testing the Arduino-controllable analog switch to activate record or READ mode on the SRAM 16MB board. The idea is to test taking a recorded video and then doing some bit manipulations with it and saving it back to the memory. Predictably though, I’m having issues just writing anything to the thing…

      I/O pins on Arduino 0-7
      CLK on Arduino 12
      REC on Arduino 13 connected to analog switch connecting the WR pin on SRAM to either VCC or !CLK
      
      unsigned long memory; //total number of 8bit words on the 16Mb SRAM = 2048000
      
      const byte IO_0 = 7; 
      const byte IO_1 = 6; 
      const byte IO_2 = 5; 
      const byte IO_3 = 4; 
      const byte IO_4 = 3; 
      const byte IO_5 = 2; 
      const byte IO_6 = 1; 
      const byte IO_7 = 0; 
      
      void setup() {
      //Serial.begin(115200);
      pinMode(13, OUTPUT);
      pinMode(12, OUTPUT);
      
      pinMode(IO_0, OUTPUT);
      pinMode(IO_1, OUTPUT);
      pinMode(IO_2, OUTPUT);
      pinMode(IO_3, OUTPUT);
      pinMode(IO_4, OUTPUT);
      pinMode(IO_5, OUTPUT);
      pinMode(IO_6, OUTPUT);
      pinMode(IO_7, OUTPUT);
      
      //WRITE
      while(memory<= 2048000){
      memory++;
      
      // if (memory%10000==0){
      // Serial.println(memory);
      // }
      
      if (memory%5==0){
      PORTD = B11110000; // the IO pins. simple pattern test.
      
      }
      else{
      PORTD = B00001111;// the IO pins. simple pattern test.
      }
      //WRITE MODE
      PORTB = B11101111; // PIN 12 ARDUINO CLK goes LOW/HIGH
      PORTB = B11111111; // PIN 13 ARDUINO WR STAYS HIGH
      
      }
      // Serial.println("finished writing");
      }
      
      void loop() {
      pinMode(IO_0, INPUT);
      pinMode(IO_1, INPUT);
      pinMode(IO_2, INPUT);
      pinMode(IO_3, INPUT);
      pinMode(IO_4, INPUT);
      pinMode(IO_5, INPUT);
      pinMode(IO_6, INPUT);
      pinMode(IO_7, INPUT);
      
      digitalWrite(IO_0, LOW);
      digitalWrite(IO_1, LOW);
      digitalWrite(IO_2, LOW);
      digitalWrite(IO_3, LOW);
      digitalWrite(IO_4, LOW);
      digitalWrite(IO_5, LOW);
      digitalWrite(IO_6, LOW);
      digitalWrite(IO_7, LOW);
      
      //PIN 13 REC IN LOW READ MODE
      PORTB = B11011111; // PIN 12 ARDUINO CLK goes LOW/HIGH
      PORTB = B11001111; // PIN 12 ARDUINO CLK goes LOW/HIGH
      }
      

      Not working…

      unsigned long memory; //total number of 8bit words = 2048000
      byte cell; // store the word at this address
      
      void setup() {
      
      //PD0 - PD7 are IO
      pinMode(13, OUTPUT); // REC - PB5
      pinMode(12, OUTPUT);// CLK - PB4
      
       while(memory<= 2048000){
          memory++;
      //READ
              DDRD = B00000000; // all IOs in INPUT
              PORTD = B00000000; // no pull-ups
      
              PORTB = B11011111; // WR stays LOW + CLK HIGH
              delayMicroseconds(10);
              cell = PIND; // read the cell at this memory address and store it in the variable cell
              delayMicroseconds(10);
              PORTB = B11001111; // WR stays LOW + CLK LOW
              delayMicroseconds(10);
      
      //WRITE
             
              DDRD = B11111111; // all IOs in OUTPUT
      
              PORTD = ~cell; // write  a transformed cell back to memory
              PORTB = B11111111; // WR stays HIGH + CLK HIGH
              delayMicroseconds(10);
              PORTB = B11101111; // WR stays HIGH + CLK goes LOW
              delayMicroseconds(10);
        }
      }
      
      
      void loop() {
      //READ MODE TO SEE WHAT WE HAVE IN MEMORY
              PORTB = B11011111; // WR stays LOW + CLK HIGH
              delay(500);
              PORTB = B11001111; //  WR stays LOW +  CLK LOW
              delay(500);
      }
      Not working either...

       

      ****

      I reread the manual for the SRAM and found that in the writing mode where OE is held LOW, you need to not force the SRAM IO pins for a duration of 10ns after WE goes low or else the “previously read data will drive the IO buffer”. So I need to put the IO pins into HIZ for this period of time.

      I also found that the SRAM needs a startup time of 150ns after the voltage has settled. This needs to be added in the setup.

      Looking at the 74HC590 datasheet, I am realizing that there is a significant propogation delay for the CLK to convert into a new address on the ADD pins. I can’t figure out the exact delay but the highest delays I see for any step in the pipeline are in the 100s of ns. To be conservative, after the CLK goes HIGH and WR LOW, I’m adding a big delay so that the address lines can catch up.

      *EDIT* I forgot, the SRAM is 3.3V logic level so I need a level shifter or I’ll damage it !

      However, even with that, still no dice ! Very curious…

      *****

      I just tried some different op amps with the Serpentin 128KB board.

      Recorded some different speeds in the same recording which was cool.

      Here are the things I’ve played with :

      1. Recording versus playing
      2. Clock speeds
      3. Amplifying input / output and threshold
      4. Count resetting, memory chunk selection
      5. The memory size
      6. The bit width
      7. combining several recordings

      What I want to play with :

      1. editing tiny parts of memory, possibly based on the contents of the memory
      2. moving through the memory non-linearly, and at different speeds/accelerations
      3. copying parts of one memory to another  

        ********

      FPGA synth

      Heading towards a more reliable, deployable and software-leaning approach (but not so much about automated choreography as with previous atmega boards as augmented live playable machine for manual performances) to working with video now.

      I am also trying to be a bit more strategic and attempt to think ahead a little bit beyond the one board one experiment situation. It takes a long time to trouble shoot a single board and learn all the lessons from it, I have to give each board the time it deserves and I need to be perhaps less productive and more thorough.

      I am also coming to the awareness of what is my scope with this next board :

      • I won’t be doing challenging engineering things (like implementing different ways of programming the same FPGA, interfacing with differential pair protocols like HDMI) that have no impact on the images and take loads of time to do.
      • These are boards for experimenting and live playing, so they also need to have controls on the board.
      • I also need to think about safety, avoiding the possibility of easily making short circuits and damaging the board.
      • I’d also like to move back to color, and away from the intense limits of exclusively 1bit resolution.
      • I want to keep removing superfluous cables and adapters too, this one will have only a VGA out.

      I made a map of my prototypes so far for this project :

      It’s been around two years I’ve been working on this !

      After talking with Zach at work, his idea was to show not just the screen but also me turning knobs etc. Here’s what this could look like :

      Or,

      …or,

       

      It would be nice to have a computer interfaced oscilloscope to record the signals.

      *****

      The most plug and play option would appear to already exist : a tiny raspberry pi zero can already run Processing scripts and control small screens https://learn.adafruit.com/processing-on-the-raspberry-pi-and-pitft/processing !

      Here’s a good intro to making simple patterns with processing : https://processing.org/examples/

      We would be more in the zone of software video synth : https://learn.adafruit.com/feather-rp2040-dvi-video-synth

      Also seem to be companies making just this kind of thing : https://store.excamera.com/

      Arduino talks to the FPGA by SPI as if it were a RAM space. https://excamera.com/files/gameduino/synth/doc/gen/poster.pdf

      Design / Engineer friend Paolo Salvagione pointed me to Configurable mixed-signal ICs. It appears to be like an FPGA but for mixed analog and digital circuitry and is configured in design software.

      There is also the world of ASICs and the google open-source silicon project https://developers.google.com/silicon

      *************

      My project was originally about slowing down computer processes and representing them in space. Now I just seem to order things on Amazon and design increasing numbers of PCBs. It seems that I have so little time to do the art in the end because of how much investment the technical stuff requires. How could I get back to my original project here ?

      My project also had a collection of related techniques :

      • Try to free machines from their corseted, factory settings and reveal their full range of expression that would otherwise just exist in a parallel unseen dimension
      • Try to show our files from the perspective of / through the eyes of the machines that are processing them.
      • Curate series of abstract formal compositions which emerge from figurative, architecturally-themed or culturally iconic ones.
      • Try to describe the behaviour / idiosyncracies of algorithms and make them tangible
      • Isolate specific moments of machine-to-machine interface in a larger system
      • Get one’s hands inside the black box, and make prepared machines which expose their parameters
      • Try to rebuild electronic systems based on help from the DIY internet and then hope to stumble on something unintentional
      • All the while emphasize the materiality of technology
      • Trying to go super low-level and avoiding abstract computer “visualizations”
      • Exploring the link between visual chaos and harmony, that sweet spot of medium entropy.
      • Riffing off of the music synthesizer movement of knobs and playful electronics interfaces and its anti-theory vibe
      • Exploring the surreal, bizarre space of the computational unconscious
      • Do “artistic research” projects that are educational, at least for me
      • play with form versus content

      I wonder if the pixel based screen is not really appropriate for my methodology. It’s the opposite of over-looked and black boxed. It’s an obsession for an entire culture. It also does a pretty good job of “representing” what happens inside the computer already. It doesn’t really need my help !

      ********

      I also need to do a debrief on this leg of the video synth project. What have I learned ?

      Technical stuff :

          • How to make and debug PCBs
          • To work with video + SRAM with counters as well as some op amp circuits. The difference between digital and analog in the video context.
          • To work with the VGA protocols, about resolution and brightness of the image.
          • I learned about oscillation, the difference between KHz and MHz in the context of video and what sampling is.

      Stuff about media representation

          • some aspects of the nature of visual memory, how some of our sensory apparatus work at different speeds
          • the curious experience of “searching” for an image (like when messing with ADC knobs and CLK speeds).
          • what is required to identify / recognize and image or scene, how much resolution and shape
          • Deconstructing the illusion of television by interacting with its materiality and mutability
          • Repeating historical art explorations with television (see Sans Soleil – Chris Marker)
          • synchronizing the data with the frame of the image is a key part of the illusion of video. If you mess with this suddenly the video frame becomes a malleable object. If it is desynchronized, the image is out of frame and will jump around with the SRAM recording. If you change the speed of recording or of playback you can zoom.
          • Layering of different images in a palimpsest can create textures from them.
          • I’ve made some nice abstract pattern discoveries through knob twisting.
          • Exploring the gap between legible figurative image and abstract patterns, contours, fields,
          • the texture and grain of data at various scales
          • the connection between video and abstract field painting

      What does it mean to be trying to make super simple hardware video filter in the age of AI generated video and images ? How is this activity relevant ? I feel like I’m looking for low hanging fruit here but I’m not sure there is any left. Also, the time it takes to make a single experiment in hardware is astronomical compared to the software option…

      I think the project started off about the screen itself, how to send signals and display them. At some point it became clear that the screen just displays data stored in memory.

      In this spirit, I could try to represent memory access patterns : https://en.wikipedia.org/wiki/Memory_access_pattern Or the patterns that memory is refreshed in : https://en.wikipedia.org/wiki/Memory_refresh

      http://www.overbyte.com.au/misc/Lesson3/CacheFun.html

      I could also sample images and then modify them algorithmically? But it kind of ends up looking like simple Photoshop filters. It would have to be modified in some way based on the way the image is stored in the memory ?

      Check out this image stored in DDR ram decaying over time from J. Alex Halderman’s paper : https://jhalderm.com/pub/papers/coldboot-sec08.pdf

      Also it seems like different DDR has different decay rates (from A Trustworthy Key Generation Prototype Based on DDR3 PUF for Wireless Sensor Networks https://www.researchgate.net/figure/DRAM-cells-decay-feature-with-power-switch-interval-time-of-120-s-a-DDR3-device-1-b_fig3_263586301):

       

      For comparison, check out the same Mona Lisa image sent through the air and damaged by the earth’s atmosphere :

      undefined

      ********

      I’ve never thought of this before but I guess I could generate a simple arduino code, and then take the .hex file and glitch it with any of the glitching techniques I’ve been using and see what it puts on screen.

      I could also test overclocking / overheating the atmel and seeing how they collapse !!

      ********

      I should also finish the boards I’ve already designed and had made, they offer learning opportunities and possible discoveries ! The bucket brigade, the digitally-controlled pots (meh), and the band splitter (*EDIT* completed !) are the three that are left from that series. Also the Palimpsest.OS 2 board when controlled by microchip – especially if I can get the sync working ! Of course I have the new 128Kbit memory board (*EDIT* all done !) once I get the parts too and then a final (?) FPGA v2 board.

      ********

      I am still interested in exploring FPGAs and getting my hands in the tech to possibly stumble on cool stuff though so here is a second attempt at a video FPGA board based on the iCE40HX1K-TQ144 to output HDMI, DVI, VGA, and composite video.

      For generating verilog, there is migen for python 3 (https://github.com/m-labs/migen). I’m going to try a super simple thing in Verilog first to get a feeling for it. 

      I am learning verilog here : https://hdlbits.01xz.net/wiki/Main_Page

      It looks like generating HDMI is doable even though there is a difference between LVDS and TMDS (involving possibly 100nF series capacitors ?) :

      Here is the official guide : https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/UZ/FPGA-TN-02213-1-7-Using-Differential-IO-LVDS-Sub-LVDS-iCE40-LP-HX.ashx?document_id=47960

      https://www.fpga4fun.com/HDMI.html

      blackmesalabs.wordpress.com/2017/12/15/bml-hdmi-video-for-fpgas-over-pmod/

      https://github.com/hdl-util/hdmi/

      Different chips being used like the PTN3360D HDMI / DVI Level Shifter (IN STOCK!), a TFP410PAP TFP410 TI PanelBus™ Digital Transmitter (NO STOCK!),

      Question - Pluto IIX GCvideo help needed | BitBuilt - Giving Life to Old Consoles

      According to the datasheet, “The LVDS25E/subLVDSE differential output buffers are available on all banks but the LVDS/subLVDS input buffers are only available on Bank 3 ofiCE40 LP/HXdevices.” ***Those are INPUTS but there are no special OUTPUT pins.***

      I have replaced the LDO with was giving me trouble previously and am making this version USB programmable so it could be a useful for others as a cool video kit.

      *****

      Revisiting FPGA programming, here is where I got to last time :

      Here is my process for the FPGA programming so far :

      1. I signed up for an account at www.latticesemi.com.
      2. I got a licence for iCEcube2, put it somewhere easy to find and took note of the directory, and downloaded iCEcube2 and the Lattice Diamond software from here : https://www.latticesemi.com/en/Products/DesignSoftwareAndIP
      3. I plugged in the iCEstick and it blinked in a circular pattern. I also checked that it had a port under Device Manager (it had 2!). 
      4. I went into iCEcube2 then to Help > Tutorial to open a pdf called “Implementing the Design” and followed the following steps :
        1. Start new project (iCE40, HX1K, ?TQ144?)
        2. Add Design file iCElab.v and Constraint file iCElab.sdc and checked the Device Info.
        3. Run Synplify Pro Synthesis
        4. Select Implementation
        5. Add constraint file iCElab.pcf under Import P&R Input Files.
        6. Run Placer
        7. Check the Floorplan, package view, timing analysis, power estimation
        8. Generate the bitmap (.bit and .hex files) which is saved wherever the project was saved. (For instance C:\Users\Jonah\Desktop\FPGA LATTICE DEMO PROJECTS\quick_start\quick_start\quick_start_Implmnt\sbt\outputs\bitmap).
        9. Here is what everything should look like once finished :
      5. I then transitioned to Youtube for a detailed explaination of using Diamond Programmer with the iCEstick here : https://www.youtube.com/watch?v=Df9k1T0bHmA&ab_channel=Dom
        1. Launch Diamond Programmer and take the default options (or click scan and take those). Important to note that you must launch Lattice Diamond Programmer directly, not through Lattice Diamond, in order to find the iCE40HX1K chip.
        2. It will mistake the board for a JTAG interface and give the following errors : “Failed to scan board”, “Scan Failed – Creating Blank Programmer Project.”
        3. Now change the Device Family to iCE40, the device to iCE40HX1K, and under Program enter the following information :
          1. Change Access mode to SPI Flash Programming and now some other options appear. Select > Vendor: Micron, Device: N25Q032, Package:  8-pin VDFPN8
          2. Now load the bitmap (.bin) file and check that it has a non-zero datasize :
          3. Click OK and then click the green traffic light on the next to top row of icons. You should then see a completion message and have the program on your board :
          4. Presto !

      Just for info :

      For the actual pinout of the ice40 (which you can change in Pin Constraints Editor):

      ***

      And here is my attempt programming my own board with the Lattice Programmer.

      I followed the same steps as above but instead selected the HW-USBN-2B (FTDI) programmer. Important to note that you must launch Lattice Diamond Programmer directly, not through Lattice Diamond, in order to find the iCE40HX1K chip. Also note that you need Lattice iCEcube2™ Software, not just Diamond for some reason.

      Here are the color connections needed from the Lattice Programming Cables User Guide :

      The USB power and Board power lights should be on on the programmer and the Prog blue LED should flash a few times before the Done blue LED will flash and Lattice Programmer will display Operation: successful.

      I am taking the example LED rotation verilog .v code :

      module LED_Rotation(
      
          input  clk,
          output LED1,
          output LED2,
          output LED3,
          output LED4,
          output LED5
      
          );
      
                     reg[15:0] div_cntr1;
                     reg[6:0] div_cntr2;
                     reg[1:0] dec_cntr;
                     reg half_sec_pulse;                         
      
                     always@(posedge clk)
                                    begin
                                    div_cntr1 <= div_cntr1 + 1;
                                    if (div_cntr1 == 0)
                                                  if (div_cntr2 == 91)
      
                                                                 begin
                                                                 div_cntr2 <= 0;
                                                                 half_sec_pulse <= 1; 
                                                                 end
                                                  else
                                                                 div_cntr2 <= div_cntr2 + 1;
                                    else
                                                  half_sec_pulse <= 0;                            
      
                                    if (half_sec_pulse == 1) 
                                                  dec_cntr <= dec_cntr + 1;                                          
                                    end                                                            
                     assign LED1 = (dec_cntr == 0) ;
                     assign LED2 = (dec_cntr == 1) ;
                     assign LED3 = (dec_cntr == 2) ;
                     assign LED4 = (dec_cntr == 3) ;
                     assign LED5 = 1'b1;
                                                           
      endmodule
      

      Under Tool > Pin Constraints Editor, we can change the pins activated in the code but mine is already set up connected to pin 99:

      I am soldering a second FPGA board. I noticed that the clock was not outputing anything, and also that the max temperature for the IC for reflow soldering is around 250°C. I am making sure to use the soldering iron “kissing” technique along with loads of flux applied.

      Reading through the datasheet :

      • Bank 3 additionally supports differential LVDS25 input buffers.
      • Each bank can have it’s own voltage level.
      • I may have to power the VCC, VCCIO_2, VCC_SPI, VCC_PLL, and VPP_2V5 (with a diode) even if I don’t plan on using them all just to get the device to turn on. (There is even a proper sequence to power them up in).
      • GNDPLL must NOT be connected to the board’s ground !
      • Bank 2 has two dedicated Input pins.

      ****

      FPGA BOARD ERRATA / DIARY :

      • 1.2V is connected to 5V throughout because of an EAGLE issue 🙁
      • I tried the voltage regulator 3 times and still no luck. *UPDATE* I tried a second 1.2V LDO that I ordered and it still reads 2.2V or so. No idea why this is happening when I can power the pin directly with 1.2V with my power supply no problem (therefore it can’t be a bad connection somewhere between 1.2V and another trace right?). Positive news is that the FPGA appears to have survived the over-voltage !!
      • Maybe I should give it a higher frequency oscillator, at least 25MHz, as I’m working with video?
      • I connected pin 9 of VGA to GND, it is connected to 5V on the signal side !
      • The GNDPLL should not be connected to GND.
      • The R2R ladder has a 270ohm going to ground instead of a 536 on each ladder.
      • Unclear which pins to plug where for programming – *EDIT* SOLVED
      • I had a hard time at first soldering the FPGA but then discovered a soldering “kiss” technique that was reliable and effective. For the clock, I melted the solder and then placed it gently on top.
      • I plugged it in with 2.2V instead of 1.2V and likely destroyed the chip the first time…
      • 0805 is doable and better for compact designs
      • I can’t find the name of the chip in the Target Device menu of Diamond Programmer. *EDIT* SOLVED : You need to open Diamond Programmer directly.
      • I need to get nicer boards if I am going to be working on FPGA boards, Aisler not inexpensively manufactured.
      • It seems like exploiting the parallelism of the FPGA could be interesting to explore. For this project it would probably be to logically mix several input videos together while transforming ?

      ***

      I’ve got it blinking at different frequencies !

      Now I want to generate some VGA signals as quickly as possible and start creating images by tweaking pre-made code like for VGAX.

      And here is demo code for the OLIMEX ice40 developement board which has RAM, DAC, ADC and VGA (but it needs a 100MHz clock) : https://github.com/OLIMEX/iCE40HX1K-EVB/tree/master/demo/ice40-io-video

      Also super simple (but uses a 25MHz clock) : https://www.fpga4fun.com/PongGame.html

      Here is the one I ended up using (takes a 12MHz clock and turns it into a 25MHz with the PLL) : https://github.com/imuguruza/alhambra_II_test/blob/master/vga/vga_test/

      Here is the key part of the code that actually draws on screen :

      module vga_sync_test(
          input wire clk_in,
          input wire reset,
          output reg r0,
          output reg r1,
          output reg r2,
          output reg b0,
          output reg b1,
          output reg b2,
          output reg g0,
          output reg g1,
          output reg g2,
          output wire h_sync,
          output wire v_sync,
          output wire led,
          output wire locked_led
        );
      
      wire clk_sys;
      wire display_en;
      //reg [9:0] h_count;
      wire [9:0] h_count;
      //reg [9:0] v_count;
      wire [9:0] v_count;
      assign  led = clk_sys;
      
      localparam  h_pixel_max = 640;
      localparam  v_pixel_max = 480;
      localparam  h_pixel_half = 320;
      localparam  v_pixel_half = 240;
      
      //Check if we can create RGB colors
      always @(posedge clk_sys) begin
        if (display_en) begin
          if (h_count < h_pixel_half
              && v_count < v_pixel_half) begin
            //Assign here your test color
            r0 <= 1'b0;
            r1 <= 1'b0;
            r2 <= 1'b0;
            g0 <= 1'b0;
            g1 <= 1'b0;
            g2 <= 1'b0;
            b0 <= 1'b1;
            b1 <= 1'b1;
            b2 <= 1'b1;
          end else if (h_count > h_pixel_half
                  && v_count < v_pixel_half) begin
            //Assign here your test color
            r0 <= 1'b0;
            r1 <= 1'b0;
            r2 <= 1'b0;
            g0 <= 1'b1;
            g1 <= 1'b1;
            g2 <= 1'b1;
            b0 <= 1'b0;
            b1 <= 1'b0;
            b2 <= 1'b0;
           end else if (h_count < h_pixel_half
                  && v_count > v_pixel_half) begin
            //Assign here your test color
            r0 <= 1'b1;
            r1 <= 1'b1;
            r2 <= 1'b1;
            g0 <= 1'b0;
            g1 <= 1'b0;
            g2 <= 1'b0;
            b0 <= 1'b0;
            b1 <= 1'b0;
            b2 <= 1'b0;
          end else begin
            //Assign here your test color
            r0 <= 1'b1;
            r1 <= 1'b1;
            r2 <= 1'b1;
            g0 <= 1'b1;
            g1 <= 1'b1;
            g2 <= 1'b1;
            b0 <= 1'b1;
            b1 <= 1'b1;
            b2 <= 1'b1;
            end
        end else begin
          r0 <= 1'b0;
          r1 <= 1'b0;
          r2 <= 1'b0;
          g0 <= 1'b0;
          g1 <= 1'b0;
          g2 <= 1'b0;
          b0 <= 1'b0;
          b1 <= 1'b0;
          b2 <= 1'b0;
        end
      end
      
      vga_sync vga_s(
            .clk_in(clk_in),         //12MHz clock input
            .reset(reset),           // RST assigned to SW1
            .h_sync(h_sync),
            .v_sync(v_sync),
            .clk_sys(clk_sys),       //25.125 MHz clock generated by PLL
            .h_count(h_count),
            .v_count(v_count),
            .display_en(display_en), // '1' => pixel region
            .locked(locked_led)      // PLL signal, '1' => OK
            );
      
      endmodule
      
       

      I would like to modify this code in the spirit of VGAX bytebeat-inspired video synthesis.

      I made sure to load the three dependent .v files and then changed the pin constraints in the .pcf as follows :

      set_io b0 76
      set_io clk_in 21
      set_io led 99
      set_io r0 81
      set_io b1 75
      set_io g2 91
      set_io r1 80
      set_io v_sync 87
      set_io b2 74
      set_io g1 95
      set_io locked_led 142
      set_io r2 79
      set_io reset 2
      set_io g0 96
      set_io h_sync 88
      
      

       

      It works !

      Just waiting for the LDOs to make it more practical to plug in. I will now test some actual patterns !

      ***

      Here is my first test bench with ModelSim ! Thanks to this tutorial from Nandland : https://nandland.com/tutorial-your-fpga-program-an-led-blinker-part-2/

      I am using ModelSim Lattice FPGA 2020.3 (which downloaded with either Icecube2 or Lattice Programmer?) and followed this PDF called
      Using Mentor ModelSim Simulator with Lattice iCEcube2
      (https://www.latticesemi.com/-/media/LatticeSemi/Documents/ApplicationNotes/MP2/Modelsim_AN006_Dec2020.ashx?document_id=50795)

      The steps to follow :

      1. Open a new project
      2. Click Add Existing Files (pick only the test bench verilog file) and then Close
      3. Now Compilation > Compile All
      4. Now Simulation > Start Simulation
      5. In the console type <<view wave>>…
      6. …then <<add wave * >>…
      7. …and a time period for example : <<run 100ms>>. (It takes us, ns, ms, and sec it appears)

      The signals are a little small and it is apparently possible to change this under Tools -> Edit Preferences, Wave Font and Footer Font…but when I try it crashes !

      Looks like printing to the console is going to be useful ! They work by using display like so…

      $display("0.5 seconds");

      …and then in the console area of ModelSim:

      run -all

      Here’s a simpler version of a tb for a 10 ps period of the following module :

      module dut ( input clk ); 
      `timescale 1ps / 1ps
      module top_module ( output reg clk );
      
            dut instance2 (.clk(clk));
      
                 initial begin
                 clk = 1'b0;
                 forever #5 clk = ~clk;
           end
      endmodule

      ***

      Thinking about different ways of generating patterns on a screen :

      1. Screen coordinate based (plotting functions that take an input x and produce an output y)
      2. Time based + screen coordinate based (a counter t increases over time and modifies the function’s behaviour)

      Turns out FPGAs can’t do math like microprocessors ! To implement trig functions you need to build a CORDIC, which is like a computer to calculate functions.

      Then again there are some things FPGAs can do better than microcontrollers like cellular automata (one study I checked out said 36 times faster than a GPU and up to 2,800 times faster than software). HDL Bits has an exercise to build the rule 30, 90, rule 101, rule 184 cellular automata.

      (Similar to cellular automata, it’s possible to make a hardware psuedo random number generator using a linear feedback shift register : https://en.wikipedia.org/wiki/Linear-feedback_shift_register )

      Here was the solution to the rule 90 challenge that I had to look up in the end :

      module top_module(
      input clk,
      input load,
      input [511:0] data,
      output reg [511:0] q);
      
      always @(posedge clk) begin
             if (load)
                 q <= data; // Load the DFFs with a value.
             else begin
                // At each clock, the DFF storing each bit position becomes the XOR of its left neighbour
                // and its right neighbour. Since the operation is the same for every
                // bit position, it can be written as a single operation on vectors.
                // The shifts are accomplished using part select and concatenation operators.
      
               // left                     right
               // neighbour              neighbour
                 q <= q[511:1] ^ {q[510:0], 1'b0} ;
              end
          end
      endmodule

      Check out this processing sketch that is evocative of cellular automata :

      https://openprocessing.org/sketch/1879926 or sketch 1908904

      Could it be possible to do this live to recorded video with the FPGA and be able to vary the number of generations, speed, etc. ?

      Try changing the “rules” live (by clicking “random”) while this cellular automata scrolls : https://devinacker.github.io/celldemo/

      https://en.wikipedia.org/wiki/Cyclic_cellular_automaton

      Makes one think that you could make a video game entirely from these patterns being activated at different times and in different zones…Or a dedicated FPGA board that takes in audio and divides it into different bands, or other control signals directly, to control which rules are being used to generate the pattern + a bunch of knobs to change the speeds etc. It could be super minimal, just some knobs, an FPGA and an HDMI out.

      Hardware version of CA : tinkerings.org/2016/09/24/cellular-automata-explorer/

      Also check out rules that go beyond logic operations with neighboring cells and into modulo values : https://en.wikipedia.org/wiki/Cyclic_cellular_automaton

      I am now trying to use the test bench to better understand why this code isn’t putting any pixels on the screen currently.

      Here is my test bench. An important note is that variables that are not modified in an always or initial block should not be reg but wire instead.

      `include "rule90.v"
      `timescale 1ps / 1ps
      
      
      module rule90_tb();
      reg clk;
      reg load;
      reg [511:0] data;
      wire [511:0] q;
      
      rule90 DUT (
      .clk(clk),
      .load(load),
      .data(data),
      .q(q)
      );
      
      initial begin
      #10
      clk = 1'b1;
      #10;
      clk = 1'b0;
      #10;
      load = 1'b1;
      data = 1'b1;
      clk = 1'b1;
      #10;
      clk = 1'b0;
      #10;
      load = 1'b0;
      
      end
      
      always #10 clk = ~clk;
      endmodule

      This shows that the rule90 circuit is working. Every clock cycle it advances. If I could show one bit per pixel and refresh every clock cycle ?

      ***

       

      I’m getting some cool images :

      ***

      For the next FPGA board, I’m aiming for the following features :

      • It could have a handy mini screen (raspberry pi zero using DPI I/0 pins with video splitter to pass on the VGA to FPGA). I wonder if this isn’t a gimmick and not really in line with the “artistic research” aspect of my project.
      • FTDI chip for USB programming (this is too technical I have decided)
      • 100MHz oscillator with divider
      • 4x SRAM, possibly in two banks as I only have a total of 95 pins to work with
      • HDMI out, (possibly composite and DVI also ?). I wonder if this isn’t a gimmick and not really in line with the “artistic research” aspect of my project.
      • Some kind of basic interface ? (Knobs etc? But this would require an ADC unless I just use dip switches). One solution is to make hardware controls on the peripherals (clock divider, bit selections for incoming and outgoing signals, reset and CS sel on SRAM, etc.).
      • An atmega which can ask the FPGA to do things? Not sure this is necessary.
      • A fan and heat sink for the FPGA?? But this will hide it. Also a gimmick.
      • An FPGA with more Logic Modules – like 4K instead of 1K? But this doesn’t come in the easier to solder format…
      • A reset button ?
      • A fuse and reverse polarity protection !
      • microcontroller-accessed SPI FLASH so we can test moving between different types of memory. Except I haven’t had a lot of luck with SPI memory and video recording so far.

      Reading a bit more about what FPGAs can do well ;

      • they have tons more I/O pins than an Arduino? This means I can control multiple SRAM address and I/0 pins.
      • Architecture can be completely reconfigurable (unlike a microcontroller which keeps the same architecture).
      • low latency applications that take data as a stream in real time from sensors, cameras, etc. This means I can perform fast logic manipulations of saved data.
      • high throughput applications, pipelining
      • encryption and networking
      • digital signal processing (IIR, FIR, FFT, ADC, DAC) like video filtering
      • parallelism, like doing 128 small tasks at the same time, versus microcontrollers doing things in serial
      • algorithms that can be solved with LUTs, tasks with lots of small adding and subtracting or multiplication (?)
      • K-means clustering, Monte Carlo, neural nets, Mandelbrot fractals, cellular automata ?
      • Seems that there are more and more systems that include micrcontrollers + FPGA + DSP, and you can of course put a microchip inside an FPGA. Here the FPGA accelerates certain tasks for the CPU

      *****

      Here’s what I think I could do with an FPGA and two individually-accessible SRAM I/0 banks   banks :

      • Reverse, scramble, or move through memory addressing with different patterns
      • could change the sample rate of recorded or incoming images
      • could perform logic operations on incoming /recorded video
      • could place sprites on screen, or other parts of memory, through blitting I could selectively overwrite memory.
      • It could record 8 tracks in parallel at 1 bit resolution, or record super long 1 bit resolution videos like on previous boards.
      • It could do matrix transformations on memory like shear, rotation
      • Copying values from one address and then reapplying them to another address every frame (called piping) or just one time. I could generate noise between certain values, or shift values up or down on (called tilting), listen to a value and then reapply it after a fixed number of frames, freezing the current value somewhere for future frames, replacing values intelligently with values in certain sets. Could this be called in-memory processing (https://en.wikipedia.org/wiki/In-memory_processing)? I could copy all the kinds of ways memory is accessed : https://en.wikipedia.org/wiki/Memory_access_pattern !

      With this setup, the FPGA can increment the ADD of all memories while individually controlling which SRAM is reading or writing. Additionally, and simultaneously, it can send and receive different data between two connected SRAM banks. Bascially, it can’t simultaneously access two different parts of memory for reading or writing, if it wanted to do that it would need to remember some info in a buffer and then change the address in sequence like a normal microchip (this is all to give a greater color range to the board). So as far as I can tell the following memory reading and writing operations could be accomplished :

      1. while incrementing memory ADD, record incoming video on A, B, C, D in sequence (at various bit depths)
      2. while incrementing memory ADD, record incoming video on A, B, C, D in parallel (make 4 copies)
      3. while incrementing memory ADD, record incoming video on A, B, C, D and playback previously recorded video
      4. while incrementing memory ADD, play back video on A, B, C, D in sequence
      5. while incrementing memory ADD, playback video 4 copies of a video on A, B, C, D in sequence (i.e. loop)
      6. while decrementing memory ADD, playback a video in reverse
      7. access recorded video with different ADD patterns
      8. while incrementing memory ADD, take stored sprites from the top of one SRAM and insert them into over SRAMs at later addresses
      9. while incrementing memory ADD, perform logic operation on incoming video + an SRAM reading while writing it to a second SRAM
      10. while incrementing memory ADD, perform bit level transformation on an SRAM recording while writing it to a second SRAM

      I am starting to think that losing some color information is worth having more flexibility to work in parallel with SRAM and save me headaches later on when I’m doing verilog hardware descriptions. Here is a simpler proposal that would have a single input (possibly record/pb or “function” meaning do something to the incoming video and then show it on a loop) :

      • I lose LEDs, but I replace them with the most honest indicators possible, the signals themselves buffered.
      • Lost the ability to control individually each quarter memory with WE control.
      • No more DIP select buttons…How will I be able to play this thing live once it’s going through its code (apart from being able to mess with the clock, the RGB bias out, VGA pins coming in, and being able to reset SRAMs and the FPGA)? Should I be able to interrupt it’s functioning by directly controlling CS and WE pins ? (This is possibly dangerous as two memories both set to READ sharing the same I/O pins would be bad.) How about a single button, which activates some hardware function? I would need to load a new code if I wanted to test a new hardware function.

      I’m trying to justify doing this thing in hardware by having tons of indicator LEDs, knobs to change parameters etc. I have to acknowledge that there is a certain amount of manipulation in making art, I am pushing it further in the direction I want so that it is easier to explain to people.

      Reminder : Why am I not using an atMega 2580 which has 86 programmable IO lines and 16MHz frequency ? FPGA is parallel, and it is faster, several people in different contexts have suggested FPGAs for my project, and it’s a learning opportunity.

      In general, doing things in harder is (Fabien) Faster than software, (Vivien) emphasizing materiality, and (Marc) pushing against the genericness of software and hardware that we are increasingly using.

      ****

      Using BRAM seems pretty simple to implicity create :

      reg [15:0] memory [start_address : end_address];

      //memory[4] addresses word 4;

      Fabien at work suggested two things : Not using a resettable fuse as they take longer to stop high current destroying things. And possibly having an SD card with the videos which the FPGA can read directly and getting rid of the RPI. This is because, while writing to SD takes time, reading can be fast. This would be a kind of ROM where I could store videos.

      The best mode for an FPGA apparently is one bit SD mode, instead of SPI, as it only requires 3 pins: CMD, CLK and DAT0.

      https://www.fpga4fun.com/SD2.html

      It looks like inside the SD card is just NAND Flash memory, so maybe I could interface directly with this ?

      https://upload.wikimedia.org/wikipedia/commons/f/f0/Pretec_16GB_SDHC_without_cover_20090420.jpg

      Or I could have Arduino doing the interface with an SD card ? I’m not sure why but I feel like this is going in the engineering direction and not the artistic one. The Rasbpi solution works, why not just keep it and save the headache that won’t in any way change the artistic output of the board.

      *****

      ATmega2560 SRAM board with SD card and I2C Flash memory. Raspberry pi as video in.

      But not very interactive. More about messing with different kinds of memory, and as a parallel test as the FPGA but more familiar and constrained in C.

      ***

      Don’t know what this is yet but, some kind of typology of memory modifications ?

      Choreographable video synth (PART III)

      Making things easier, simpler, more plug and play.

      ****

      Workshop
      Organize parts 
      
      Palimpsest.OS
      Get all channels working 
      Get raspberry pi VGA out working 
      Test with raspberry pi VGA put 
      Fix VGA input/output (I know what the problem is at least :)!
      SPI SRAM 
      Get recording as fast as possible 
      Try with 4MB FRAM ?
      New board with several + raspberry pi + snake-like channel overwriting?
      
      FPGA
      Try simple VGA with LatticIce40
      Solder FPGA board 
      ****
      La Generale experience :

      So many cables and wires and machines….something more minimal / smaller and portable possible ?

      Would love to have an integrated thing with raspberry pi / FPGA generating video and the thing directly outputting it.
      Do not rely on computers to output VGA signals ! Try raspberry pi.
      Physical objects is what I should make, not projections, because my project is about materiality ! I could make circuits that go beyond just recording and have moving parts and tiny screens eventually ? Then again this is a dangerous dream as I’ve learned with VHS and Floppy Disk tests…
      Don’t make experimental stuff during residencies, test tools you’ve already built and deploy tested things.
      My project is all about trying to touch video, get my hands into the inside of the machine. It’s also all about making mistakes, and stumbling upon unexpected output as a result.
      Somewhere between a kit/workshop and an object. (It’s not a performance) 
      ****

      I’ve got a Raspberry Pi Zero outputting VGA nicely. On startup it launches VLC in fullscreen and plays a playlist of iconic film excerpts. I activated hotplugging so it keeps looping even if the adapter is unplugged for long periods of time.

      I used Win32DiskImager and downloaded the ISO Image Raspberry Pi OS with desktop (around 1GB) from the officiql website (https://www.raspberrypi.com/software/operating-systems/) and put it onto an 8GB SD Card.

      In terms of connectors I needed a SD Micro > SD card adapter, HDMI mini > HDMI, an HDMI > VGA, a USB A > USB Micro for power, and a USB Micro to USB A female in order to plug in a mouse / keyboard.

      I followed these three tutorials for setting up VLC autoplay and turning off annoying VLC text:

      https://forums.raspberrypi.com/viewtopic.php?t=17051

      Raspberry Pi: Run VLC on startup and play slideshow videos/pictures from folder

      https://www.shellhacks.com/raspberry-pi-force-hdmi-hotplug/

       

      Now adding a power off button (not good to pull the plug while running)

      https://howchoo.com/g/mwnlytk3zmm/how-to-add-a-power-button-to-your-raspberry-pi

      **EDIT The rasbpi turns off when I unplug HDMI or plug into the palimpsestOS. Is it because RGB are mixed with only 100ohm resistors ? Should I have 3 blocking caps and then mix after ?

      Here’s a write up on how to VGA :

      https://chipnetics.com/tutorials/understanding-75-ohm-video-signals/

      Looks like parallel 75ohm resistors going to GND from the input is good practice + a cap and 75 ohm resistor on output?

       

      VGA OUTPUT examples :

      MAX4032 5V, 6dB Video Buffer with Sync-Tip Clamp, Output Sag Correction, and 150nA Shutdown Current | Analog Devices

      If I understand this last one, three parallel 75ohm resistors makes a 25ohm resistor. 97ohms gets 80% of 3.3V gone before the 25ohm dissipates the rest so that there is 0.7V left for the 3 channels. For a 5V supply like mine, I’ll need to remove 86% of 5V to get it down to 0.7V.  A 160 ohm resistor then ?

      VGA INPUT examples :

       

       

      *EDIT Made the changes (RGB colors in to GND through 75ohm, one color through cap to bias + 160ohm and now there is no output…But maybe it was already broken at this point? Can test when new converters arrive.)

      Looked at some VGA in/out boards I had in my possession and saw the following :

      • H and V sync can go to a 74HC125 Line Driver / Buffer.
      • Color returns can be conected together and grounded along with chassis.
      • Examples of color pins going to grounded 75ohm resistors then on to blocking caps. Also colors to gates of transistors.
      • Colors also going to video signal switcher like this : QS4A210 – 2-Channel 4:1 Analog Mux/Demux
      • Definitely DO NOT ground pin 9 (which is 5V), as I have done in all previous boards…

      ****

      An older HDMI to VGA converter broke so I opened it up to see if I could reproduce it. It’s an ALGOLTEK AG6201 which appears to be not accessible anywhere but the manufacturer’s website.

      The alternative is using a video chip which can trasmit HDMI like this project : https://hackaday.com/2019/07/26/hdmi-from-your-arduino/ which uses a CH7035B HDMI encoder.

      Alternatively I use the HDMI to VGA adapter and solder a D Sub Standard Male connector (https://www.mouser.fr/ProductDetail/TE-Connectivity-AMP/2301843-1?qs=rrS6PyfT74crws9wAQVNoA%3D%3D&mgh=1&vip=1&gclid=Cj0KCQjw27mhBhC9ARIsAIFsETE7Rp9JFGXhYFAXh_Z2YbQUB2NfHqO8rM7yPueb-T3yFiKbUMvJkOEaAtjJEALw_wcB) to the board.

      ***

      I will be testing a pico projector and a VGA capture device soon…

      Video Coverter VGA Capture Card,VGA to USB2.0 Converter,Audio and Video Capture Device,Plug-and-Play,USB Drive-Free HD 108...Mini Projector, PVO Portable Projector for Cartoon, Kids Gift, Outdoor Movie Projector, LED Pico Video Projector for Home ...

      ***

      Trying again with SPI SRAM

      This code seems solid to me but I can’t go beyond 100Hz…

      #include <SPI.h>
      #include <SRAM_23LC.h>
      
      // SPI bus can be SPI, SPI1 (if present), etc.
      #define SPI_PERIPHERAL    SPI
      #define CHIP_SELECT_PIN   8
      
      /* Device can be:
       * 128KB: SRAM_23LCV1024, SRAM_23LC1024, SRAM_23A1024
       * 64KB: SRAM_23LCV512, SRAM_23LC512, SRAM_23A512
       * 32KB: SRAM_23A256, SRAM_23K256
       * 8KB: SRAM_23A640, SRAM_23K640
       */
      SRAM_23LC SRAM(&SPI_PERIPHERAL, CHIP_SELECT_PIN, SRAM_23LCV1024);
      
      // Additional SRAM chips
      // SRAM_23LC SRAM1(&SPI_PERIPHERAL1, CHIP_SELECT_PIN1, SRAM_23LC512);
      
      #define START_ADDRESS   0
      
      //uint8_t buffer[BUFFER_SIZE];
      //#define BUFFER_SIZE  320
      
      char buffer[1000];
      #define BUFFER_SIZE  (sizeof(buffer) / sizeof(uint8_t))
      
      void setup(void)
      {
        pinMode(2, OUTPUT);
        /* Without parameters, begin() uses the default speed for this
         * library (12MHz for samd, 14MHz for sam, and 4MHz for avr).
         * Note that SPI transaction support is required.
         */
        SRAM.begin();
       // SRAM.begin(8000000UL);      // or specify speed
        //Serial.begin(9600);
      }
      
      void loop(void)
      {
        //while (!Serial); // Wait for serial monitor to connect
      
      // Record a series of values
        for (size_t i=0; i < BUFFER_SIZE; i++) {
          //buffer[i] = byte(digitalRead(A2));
              if ( (PINC & (1 << PINC2)) == (1 << PINC2) ) { //PC2 is what we're reading
              buffer[i] = 0xFF;// pin is high
              }
              else {
              buffer[i] = 0x00;// pin is low
              }
        }
      /*
        // Print buffer to serial monitor
        Serial.print("Write Block: ");
        for (size_t i=0; i < BUFFER_SIZE; i++) {
          Serial.print(buffer[i], DEC);
        }
        Serial.println();
        */
      
        // Write block
        if (!SRAM.writeBlock(START_ADDRESS, BUFFER_SIZE, buffer)) {
          //Serial.println("Write Block Failure");
        }
      
        // Clear buffer
        //memset(&buffer[0], 0, BUFFER_SIZE);
      
        // Read block
        //Serial.print("Read Block:  ");
        if (!SRAM.readBlock(START_ADDRESS, BUFFER_SIZE, buffer)) {
          //Serial.println("Read Block Failure");
        }
      
        // Print buffer to serial monitor
        for (size_t i=0; i < BUFFER_SIZE; i++) {
          //Serial.print(buffer[i], DEC);
         if(buffer[i] == 0x00){
           //digitalWrite(2, LOW);
            PORTD &= ~(1 << PD2);    // set pin 2 of Port D low
         }  
         else{
          // digitalWrite(2, HIGH);
            PORTD |= (1 << PD2);     // set pin 2 of Port D high
         }
        }
        //Serial.println();
        //delay(1000);
      }

      I just can’t manage to get the same level of control with an SPI device as with parallel SRAM and counters.

      ****

      New board time 😀 ! Test new things but keep the plug and play idea from last board and make even more so. SPEND MORE TIME SANITY CHECKING THE DESIGN and double checking pull ups and downs.

      Goals :

      • How about using a 16Mbit SRAM to store 128MBits of data with only a few added components ?! The plan is a pair of shift registers to convert serial output from a comparator into 8 bits to be sent all at once to the SRAM and the inverse on the output side. I would need the shift register clocks to be 8 times faster than the SRAM I think?
      • Make it so power (5V – with an LDO on board to eliminate possibility for accidents) can be shared with a double power jack. Reverse polarity protection.
      • A fine and coarse threshold selection !
      • proper VGA input and output good practices that will never harm anything sending video signals in or recieving signals sent out (blocking caps, 75 ohm impedence matching, Vref for top side of variable comparator bias!!, buffers for H and V sync!!, two cables close together!! and shielding – guard rings? – on the PCB for these traces!! different grounds for different parts of the VGA cable, use BNC connectors for RGB??)
      • Raspberry pi as input VGA through converter which plugs into a male VGA on the board, add an OFF/ON switch to safely turn off
      • pass thru VGA signal switch !!
      • Have different clock cans to switch between?
      • Extended Display Identification Data chip as a test?
      • Have audio out option ?

      Here’s the preliminary circuit :

      From https://www.cs.unca.edu/~bruce/Fall11/255/Labs/Lab13AnalogInput.html

      comparator

      From https://electronics.stackexchange.com/questions/144530/circuit-for-a-coarse-and-fine-setting-potentiometer

      ****

      As for PCB inspiration, perhaps something that evokes the computer video graphics card :

      Rectangular, with metal bracket (these are called Computer brackets in the Circuit Board Hardware – PCB category of Mouser.fr. They come in low profile or high profile), fan, heatsink, memory bank in a row, name of the board somewhere, PCI connector

      Palit Daytona NVIDIA GeForce2 MX 400 AGP 32MB VGA Vintage Retro Graphics Card | eBay

      16 MB PCI VGA Video Adapter Card - Our video cards and sound cards enable you to add smooth video performance or high-end audio capability to your PC computer, through a motherboard

      143 8MB Graphics Card, VGA PCI 8MB 32Bit Desktop Computer Accessories Multi- Display for ATI Rage XL Supporting All Motherboards with PCI Plug : Amazon.co.uk: Computers & Accessories

      ****

      Here is my first attempt at simulating the logic :

      The only tricky part was activating parallel load of the output register only once per 8 clock cylces.

      This design includes a giant FPGA to control enables all broken out along the PCI-E connector (that could be played like a piano with an alligator going to GND) in time and also to match the aesthetic of the video card. It would feature a stunning 128 Mbits (16MBits SRAM x 8) of memory. Would be cool to have LEDs next to each memory which light up when that block is recording.

      ***

      Building on the idea of making physical artefacts, Vivien Roussel had the idea of moving towards displaying the chassis of the computer along with these boards. There are loads of cool test bench computer cases that could be an inspiration for this :

       

      ****

      Or a DDR SDRAM module as inspiration :

      Diferenças entre as memórias DDR2, DDR3 e DDR4 | Crucial BR

      Here’s what it’s looking like in Eagle so far :

      I still like the idea of a kind of piano controlling :

      • 3x SRAM CS sel bits
      • 3x COUNTER EN + individual RST

      And of course :

      • Threshold control (fine and coarse this time !)
      • Clock DIV
      • REC/PB

      Here’s the board as ordered :

      CIRCUIT DIARY :

      • The fine and coarse pots appear to work very well.
      • The clock divider works as expected. Though it might be interesting to be able to play with different frequencies too as it seems to be important to get the combination of sampling versus pixel frequencies.
      • I checked and the serial clock is indeed being divided by 8 and inverted (albeit super noisy!) :
      • The Overflow and CS signals appear to be working well (though I had a hard time soldering the resistor networks!)
      • The !WR signal is as expected
      • Possibly should have put pull-downs for the SRAM I/0 pins ?
      • I should have made a timing diagram not just the simulation..
      • The option to manually advance the OVERFLOW signals is cool because you can solder one SRAM and test only it without having to wait for its turn to come around !
      • I have reached a problem : the 596 is not shifting the serial into parallel. I can see it trying (little mini signals getting through), but it is as if the I/O pins are being pulled low, but by what ? The SRAM ? I’ve confirmed we’re in write mode so they should be high Z. The Serial to Parallel converter somehow? But the I/O pins are inputs for this IC…Hmmm. Is it something to do with the open drain outputs (or the Schmitt trigger inputs?) on the 596 ? **EDIT : Had a 595 with the same footprint and substituted it, seems to be working now.
      • So, the output is very stripey. I have removed the 75ohm resistor to ground and shorted the 100ohm to the output RGB pins. I’ve tried shorting the blocking cap and it makes things a tiny bit easier to see but overall it is barely legible as an image. I think it has something to do with the output parallel to serial conversion. Here is a sample of a capture :

      • For some reason it can also work much better :

      • I don’t understand how it is possible to see the screen while it is recording as there is no “pass-thru” option…. There is also sometimes quite a difference from what is somehow passing through while in record mode and what plays during PB mode. *EDIT*There is a noise issue, when the VGA input is plugged in there is static that is entering somewhere. Trying to locate it currently. *EDIT* it is coming from the VGA IN. Tried connecting a bunch of GNDs (digital GND, chassis GND, color return RED) but still getting nowhere trying to eliminate the noise.
      • I ordered the wrong type of bracket, this one is for a double row VGA…
      • I had an issue with SRAM D but then replaced it and all is good.
      • I forgot to have a pre-amp before the comparator, this is essential otherwise you don’t get any in screen peaks and basically don’t see anything unless you’re really lucky. I used the Palimpsest.OS preamp and bias setup and it works fine.
      • The tiny trimpots need to be replaced with actual knobs !!
      • I forgot to add loop LEDs ! It would have also been cool to know which SRAM is currently recording.
      • EVERYTHING IS WORKING I CAN’T BELIEVE IT. The solution was to just test everything with a simple ramp using the function generator and look at the play back. Then I checked the pins on the SRAM and saw funky signals for several address pins that looked like they were several signals being added together. Clearly when I soldered in a bunch more SRAM and there were tiny connections between five different address pins and an I/O that I did not notice. This was causing chaos. I found the connections and fixed them, now it’s recording just as expected and it’s so glorious. NOTES : I am using the op amp from Palimpsest.OS AND crucially, am connecting both Analog and Digital Grounds between the two boards and the VGA IN with VGA OUT.

      ***

      While waiting for this board to arrive :

      • Try different function generated patterns of hitting count 0 enable while recording on the 16Mbit version
      • Test the 8 layer board ! *DONE*
        • Remove pin 9 GND and fix other issues with VGA in and OUT with VGA beakout PCB
        • Test on raspberry pi / computer IN *DONE*
        • Test feedback ! *DONE*
      • Try mini projector *DONE*
      • Try VGA capture device *DONE*
      • Put restart button on rasbpi

      *******************

      *EDIT* It works with rasbpi when I connect only a color, GND, H and V syncs !! So much more convenient than previously.

      • It might be cool to be able vary the mixing of the different channels
      • The output image is quite faintweak, I think I should rexamine the resistors I put at the termination and maybe switch to B+W.
      • It’s a bit muddy with several channels, would be cool to be able to filter that
      • I need a path to feedback with a pot !
      • Switched the output resistors to a 150ohm going to R,G,B and it works nicely.
      • Dip switch hard to use without tool.
      • Hard to see which direction switches are pointing in darker light

      More technical ERRATA :

      • Pin 1 of the 40MHz clock should be pulled HIGH to be enabled, not LOW as it is on the board !
      • The THRESHOLD pot isn’t connected to GND on one side…
      • I should have tied the CCLR (pin 10, which resets when pulled LOW) of the 590 to VCC with a 10K instead of leaving it floating connected to VSYNC. This made it malfunction.
      • BIG PROBLEM : I ordered multiplexers (unidirectional) instead of analog switches (bidirectional) ! The ‘157 multiplexers mess with SRAM B’s I/Os when it is in READ mode (when the SRAM is outputing values on the I/O pins). I ordered a replacement that is pin compatible, the 74LVC1G3157 (DATASHEET : https://www.ti.com/lit/ds/symlink/sn74lvc1g3157.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1679316452048&ref_url=https%253A%252F%252Fwww.mouser.de%252F). EDIT* The new components work perfectly – everything is good !
      • The Sync Clock IC is 3.3V not 5V…
      • Image jumps around of course. When I try to record with the Burst AND IC which outputs !CLK, no writing can occur. I’m guessing this gate messes with the timing somehow? As a workaround I’m trying to connect the 4 AND’ed V SYNC counts to either the address counter IC Reset or to another reset somewhere. 
      • Lots of noise on V SYNC, I should maybe have made the input and output VGA jacks right next to one another…
      • Pin 9 of the VGA D-SUB 15 is VCC for the Display ID EEPROM chip – but I have it connected to GND !! In general I need to respect the rules around the different grounds and return pins in the VGA protocol and not just connect them all together. Hoping to fix this in the next version.

      ***

      Thinking about making a board with a small 5″ screen / or a mini projector incorperated in it

      Fabien suggests using an LCD driver from a normal sized computer screen for a smaller screen (but he says it will will in 16:9 format). The other option he suggests is to use an FPGA to control a screen directly with LVDS.

      Artist Andreas Gysin’s LCD 1 from http://lcd.ertdfgcvb.xyz/

      ***

      Just realized that I could have an raspberry pi using VGA directly from I/0 pins : https://fr.pinout.xyz/pinout/dpi#

       

      ****

      Currently thinking the next PCB should be raspberry pi sending video through I/O (not through HDMI) and FPGA controlling SRAM. This would allow messing with the sequence of the memory recording / playback. Could make a compact version of this project (a kind of black box, deployable version) without many external components but with sililar functionality. I could test making the FPGA USB programmable board too. Of course it would also be programmable.

      To get here :

      • make the automated palimpsest.OS board
      • make the simple FPGA VGA board

      ****

      I am revisiting the SPI SRAM code with renewed patience, I actually wrote it first on paper !

      Here is my simple buffer filling code first off :

      const int buffer_size = 2000;  // Only 2K bytes of SRAM on the 328P
      byte buffer[buffer_size];
      void setup() {
        DDRC = 0b00000000;  // set PC2 as input pin
        DDRD = 0b00000100;  // set PD2 as output pin
      }
      
      void loop() {
      
        //  fill the buffer
        for (int i = 0; i < buffer_size; i++) {  // iterate through buffer
          for (byte mask = 0b00000001; mask > 0; mask <<= 1) {  // iterate through a bitmask
            if ((PINC & 0b0000100) == 0b00000100) {  // if input pin high...
              buffer[i] = (buffer[i] | mask);  // set this bit
            }
            else {
              buffer[i] = (buffer[i] & ~(mask));  // clear this bit
            }
          }
        }
        // read buffer back
        for (int i = 0; i < buffer_size; i++) {  // iterate through buffer
          for (byte mask = 0b00000001; mask > 0; mask <<= 1) {  // iterate through mask
            if ((buffer[i] & mask) == mask) {  // if bit in buffer is high...
              PORTD |= 0b00000100;  // ...write output pin high
            }
            else {
              PORTD &= ~(0b00000100);  // else, write output pin low
            }
          }
        }
      }
      
      
      

      Tried to see what this produce on the screen. I’m using a comparator from the previous board to do ADC and then feeding the ouput through a 470ohm resistor to red. It just leaves little comets which don’t appear to relate to the image…

      On the scope it doesn’t look far away though :

      Hmm…

      ****************

      After skimming from Nand2Tetris and checking out the VTECH Video Painter, I realized I could make a really basic screen interface :

      • I could record an image and then have an algorithm go through it and make changes (like photoshop effects like blur or polarize).
      • I could have sprites stored and then copy them into the screen-memory mapped SRAM
      • I could even have a joystick move from pixel to pixel and let you draw, copy and paste and draw circles !
      • It could also be a non uniform mapping from memory to the screen !

      ****************

      The VGA Capture is working and it’s fantastic ! I’m using OBS, I just clicked ADD SOURCE and then Video Capture device. It needs to be plugged in to USB A, not C, for the LED to come on.

       

      I tried the mini projector but it didn’t like the signals I was sending it. I think I have to avoid intelligent interfaces that can decide to send or not along my signals. Wondering about the camcorder CRT viewfinder and if I should head back in that direction.

      Here is a popular 4″ screen :

      Il Mini Monitor CRT da 4 pollici in bianco e nero supporta l'ingresso Video 12V4W tubo per immagini elettronico da 4 pollici - AliExpress

      Choreographable video synth (PART II)

      Inspired by the history of mounting circuits on wood with screws and wire, evoking John Cage’s prepared pianos. 

      This iteration of the project is born of a few realizations :

      1. VGA is an analog signal, it’s not digital.
      2. The coolest effects are the simplest ones : XOR, HP and LP filters, comparators, etc. which can be easily made with discrete components.
      3. A simple low frequency oscillator could control these effects with an LED > LDR voltage controlled resistor set-up. 
      4. Two out of sync oscillators easily make a pattern that doesn’t repeat often and is difficult to predict.

      Pedagogically and aesthetically I like this idea : it goes back to the basics of electronics, the history of electronics, and will show the materiality of video signals. This in contrast to the overly complex use of digital components to do basic modifications…

      The three basic circuits :

        

      *TESTED* This works (powered at 5V, with pull down 10K on input and the rest 1K and two BD135s) at lower frequencies (<100KHz) around 5V.  I added a 5K variable resistor (or an LDR + resistor) to increase the resistance of R1 and it varies the threshold !

      *TESTED* Works using BD136 PNP, 10K for the pull down and 1K for the base protection. General purpose diodes. I replaced the pull down with a 5K variable resistor and got some messed up results in the MHz. Doesn’t need power – just takes it from the input signal ! (But VGA signal would need to be amplified in order to work. 

      *TESTED* With a 104 cap and 5K variable resistor I can vary the filtering of a 5V function generator signal. 

       

      ****

      OK we’re back to the original plan ! Here is the Palimsest.OS Rev.2 :

      WHY ?

      • This board is for the inauguration of the IFT at the DVIC. It has to demonstrate technical proficiency on behalf of the staff, and must run independently as an tech-art installation, modifying video independently day in day out. 
      • Rev.1 didn’t work for microchip automation, this one should work smoothly(?). If everything works I could write code that executes different video manipulations. I also added selection LEDs to show the microchip doing its thing.
      • This tests the 8 channel 1-bit WRITE/REWRITE memory idea (but not the more elegant SRAM + buffer idea of Fabien), with certain effects tied to certain channels
      • Incorporates a simple to control XOR, comparator and HP/LP/BP filter into one board
      • Has a stable clock setup with metal can oscilloscope, a gen lock IC, and V SYNC burst setup
      • Tests an (expensive) programmable filter IC
      • It is a test for a simpler, inexpensive 8 ch recorder board that could be a cool kit
      • Does away with the ADC and all its pesky issues and trades it for a simpler 1bit “ADC”. 
      • I’m also using 5V SRAM to avoid the headache of different logic levels (though this means I only have 4MB of SRAM versus 16MB).
      • For testing purposes it can be soldered as a logic-controlled board or as a physical switch controlled-board. 
      • Instead of having lots of jumpers for wires, I went for rotary switches to make things more plug and play. However this also means that there are fewer experminental tests that are facilitated (like disabling a single counter while recording etc.) that were possible on rev.1
      • Can layer different recorded portions easily in theory
      • Battery switch and edge potentiometers instead of knobs
      • Theoretically could do feedback and echo tests with this board as it can read and write from and into memory simoultaneously 
      • This board should be easier to work with than the previous stacked PCI-E motherboard design which made knob turning and mounting hard. 
      • Instead of using generic octal logic ICs I went for more specific purpose ones.
      • The board will be made at https://aisler.net/ instead of JLCPCB so I’m hoping for a really nice finish

      Here’s a possible minimal installation with it :

      ****

      PALIMPSEST.OS v2 ERRATA :

      • Pin 1 of the 40MHz clock should be pulled HIGH to be enabled, not LOW as it is on the board !
      • The THRESHOLD pot isn’t connected to GND on one side…
      • I should have tied the CCLR (pin 10, which resets when pulled LOW) of the 590 to VCC with a 10K instead of leaving it floating connected to VSYNC. This made it malfunction.
      • BIG PROBLEM : I ordered multiplexers (unidirectional) instead of analog switches (bidirectional) ! The ‘157 multiplexers mess with SRAM B’s I/Os when it is in READ mode (when the SRAM is outputing values on the I/O pins). I ordered a replacement that is pin compatible, the 74LVC1G3157 (DATASHEET : https://www.ti.com/lit/ds/symlink/sn74lvc1g3157.pdf?HQS=dis-mous-null-mousermode-dsf-pf-null-wwe&ts=1679316452048&ref_url=https%253A%252F%252Fwww.mouser.de%252F). EDIT* The new components work perfectly – everything is good !
      • The Sync Clock IC is 3.3V not 5V…
      • Image jumps around of course. When I try to record with the Burst AND IC which outputs !CLK, no writing can occur. I’m guessing this gate messes with the timing somehow? As a workaround I’m trying to connect the 4 AND’ed V SYNC counts to either the address counter IC Reset or to another reset somewhere. 
      • Lots of noise on V SYNC, I should maybe have made the input and output VGA jacks right next to one another…
      • Pin 9 of the VGA D-SUB 15 is VCC for the Display ID EEPROM chip – but I have it connected to GND !! In general I need to respect the rules around the different grounds and return pins in the VGA protocol and not just connect them all together. Hoping to fix this in the next version.

      More Meta errata :

      • It’s a bit muddy with several channels, would be cool to be able to filter that
      • I need a path to feedback with a pot !
      • Switched the output resistors to a 150ohm going to R,G,B and it works nicely.
      • Dip switch hard to use without tool.
      • Hard to see which direction switches are pointing in darker light

      Oy vey ! I realized that I could just use an I2C RAM IC and save about a billion components. Here are the more plug and play simple boards I made in the aftermath :

      Good news: The 328 works. 

      ERRATA : I fried the logic chips with 5V :(. Resoldered new ones and looks good.  I think I need to be feeding in a signal that has already been digitized (like by a comparator). Otherwise I need to mess around with knobs to get the signal right around the threshold of the logic gates with bias setup. 

      Good news: The 328 works. And I can see the effect of selecting different filters. I’m not sure why but it works best when the signal is coming in on A and I’m taking the output on B (or vice versa). I’ve also got a cap going from the output of the filter board before amplifying it again.

      ERRATA: I ordered the wrong length of bussed resistor network. 

      Good news: The 328 works. 

      errata ^ Wrong footprint (and value!) for the digital potentiometer here… Will have to wait for the correct part to test this.

      Good news: The 328 works and at least it produces an image !

      errata ^ H SYNC and V SYNC plugged in to wrong pins on Arduino (should be PD3 and PB1 respectively).

      Good news: The 328 works.

      ERRATA : the PD pin (which enables ADC IOs when low) is pulled HIGH ! I resoldered it and now it’s working great. 

      I chose a non PWM pin on the atmega to be the voltage control pin of the VCO… I soldered it to the nextdoor pin which can do PWM and all is good. 

       

      ERRATA: I tried two chips, they both come up as unidentified FRAM when I have them speak on the serial running the demo codes…I should probably pull Write Protect (WP) HIGH just in case. Still not working…I will try replacing with a 23LC1024 serial SPI SRAM as it has the same pinout. 

      ***EDIT: Just saw this on the Adafruit website : “For the 4Mbit version, you should change this to: fram.begin(3)”…Still doesn’t work though.

      I replaced with the 23LC1024 and am using the SRAMsimple libary which appears to work well. For some reason the digitalRead I’m doing in the setup is always returning 0 even if connected to 5V.

      #include <SRAMsimple.h>
      #defineCSPIN8       // Default Chip Select Line for Uno (change as needed)
      SRAMsimple sram;       //initialize an instance of this class
      /*******  Set up