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