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