Category Archives: Verilog

LEDDMD 128×32 Complete

So I finally finished the prototype of the 128 x 32 Light Emitting Diode Dot Matrix Display V1.0 (LEDDMD). The protocol to write to the display is almost exactly like a shift register. There is a clock, latch, and data lines. It works in either 1 bit mode or 8 bit mode data line modes.

In 1 bit mode there is only 1 data line and in 8 bit mode this is 8 data lines. 8 bit mode enables you to clock in an entire byte of data at a time speeding up the transfer process by a factor of 8. In either mode you can do animations smoothly. All processing of the data is done on the microcontroller. The Display stores the data and takes care of running the display.

The bulk of the hardware is in the FPGA. I am using a Cyclone II EP2C8Q208C8N FPGA breakout board. There are some darlington transistor arrays that sink the current from a single row. To expand the I/O of the FPGA some decoders are used.

Here are the links to all the code:
FPGA:
Main Routine
Memory

Propeller:
Transmission Protocol and Test

128×32 DMD Update

The verilog code is almost fully debugged. The demo code that will run on a Parallax Propeller is in the works. Right now the demo is fairly basic. Today I wrote a C program that takes a 4-bit bitmap image and strips out the header and and converts it to a 2-bit image. It then reorientates the data so the image is “correct”. Bitmap images data reads the image from bottom left to top right. This is essentially backwards. So the program corrects this which means less work for the microcontroller and faster transmission of pixels.

16×96 DMD Code for Reset Vector

While developing the larger 36×128 DMD I ran out of registers and logic elements in the FPGA I was using. I could have switched to a larger FPGA but that would put the final product out of the price range I was aiming for. FPGAs have loads of dedicated ram so I decided to move the display memory from registers to a chunk of block ram. To test to see if the RAM code works I wrote a program for the 16×96 display in RESET_VECTOR. It took a bit of tweaking as I have never used block ram before but I was able to get it working.


module LED_Matrix_16x96
(
  clk,row,col,SDRAM_CS,SRAM_CS,LAT,INPUT,SCLK
);

input  wire    clk;
input  wire    LAT;
input  wire    INPUT;
input  wire    SCLK;

output  reg [0:15]  row;
output  reg [95:0]  col;
output  reg [0:0]   SDRAM_CS;
output  reg [0:0]   SRAM_CS;

    reg [24:0]  clk_slow;
    reg [4:0]  row_cnt;
    reg [1535:0]  col_buffer;
    
    reg  [3:0]  read_addr;
    reg  [3:0]  write_addr;
    reg  [95:0]  disp_mem_data;
    reg      disp_mem_wren;
    wire  [95:0]  disp_mem_q;
    
    reg [3:0]  sel;

initial 
  begin
    row <= 16'b0000000000000001;
    col <= 16'b0000000000000000;
    SDRAM_CS <= 1'b0;
    SRAM_CS <= 1'b0;
    clk_slow <= 16'b0000000000000000;
    row_cnt <= 5'b00000;
    write_addr <= 4'b0000;
    read_addr <= 4'b0000;
    disp_mem_wren <= 0;
    sel <= 4'b0000;
  end
  
always @(posedge clk) 
begin
  clk_slow <= clk_slow + 1'b1;
  
  if(!LAT & row_cnt == 5'b01111)
  begin
  case(sel)
    4'b0000:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[1535:1440];
      sel <= 4'b0001;
    end
    4'b0001:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[1439:1344];
      sel <= 4'b0010;
    end    
    4'b0010:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[1343:1248];
      sel <= 4'b0011;
    end    
    4'b0011:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[1247:1152];
      sel <= 4'b0100;
    end    
    4'b0100:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[1151:1056];
      sel <= 4'b0101;
    end    
    4'b0101:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[1055:960];
      sel <= 4'b0110;
    end
    4'b0110:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[959:864];
      sel <= 4'b0111;
    end
    4'b0111:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[863:768];
      sel <= 4'b1000;
    end
    4'b1000:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[767:672];
      sel <= 4'b1001;
    end
    4'b1001:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[671:576];
      sel <= 4'b1010;
    end    
    4'b1010:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[575:480];
      sel <= 4'b1011;
    end
    4'b1011:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[479:384];
      sel <= 4'b1100;
    end
    4'b1100:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[383:288];
      sel <= 4'b1101;
    end
    4'b1101:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[287:192];
      sel <= 4'b1110;
    end
    4'b1110:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[191:96];
      sel <= 4'b1111;
    end
    4'b1111:
    begin
      write_addr <= sel;
      disp_mem_wren <= 1'b1;
      disp_mem_data <= col_buffer[95:0];
      sel <= 4'b0000;
    end
    default: 
    begin
      sel <= 4'b0000;
      write_addr <= 4'b0000;
      disp_mem_wren <= 1'b0;
    end
  endcase
  end
  else
  begin
    disp_mem_wren <= 1'b0;
    sel <= 4'b0000;
  end
end

always @(posedge clk_slow[10]) 
begin
  if(row_cnt > 5'b01111)
  begin
    row_cnt <= 5'b00000;
  end
  col <= disp_mem_q;  
  row_cnt <= row_cnt + 1'b1;
  row <= {row[15],row[0:14]};
  read_addr <= read_addr + 1'b1;
  
end
  
always @(posedge SCLK)
begin
  if(LAT)
  begin
    col_buffer <= {col_buffer[1534:0],INPUT};
  end
end

display_ram disp_mem_inst (
        .rdaddress (read_addr),
        .wraddress (write_addr),
        .clock  (clk),
        .data   (disp_mem_data),
        .wren   (disp_mem_wren),
        .q      (disp_mem_q)
);

endmodule

Updated Code for the 128×32 DMD

The old program took to make Logic elements to fit. Now it uses a 2-port RAM element to replace the huge registers. The protocol for shifting in is a bit different now. Instead of shifting in all the data at once and then Latching the user shifts in the data 64 bits at a time then latches.


module LED_Matrix_32x128
(
  clk,e_cnt,row_cnt,col,SDRAM_CS,SRAM_CS,LAT,INPUT,SCLK
);

input  wire  clk;
input  wire  LAT;
input  wire  [7:0]  INPUT;
input  wire  SCLK;

output  reg [63:0]  col;
output  reg [0:0]   SDRAM_CS;
output  reg [0:0]   SRAM_CS;
output  reg [5:0]  row_cnt;
output  reg [4:0]  e_cnt;

    reg [24:0]  clk_slow;
    reg [63:0]  col_buffer;
    
    reg  [5:0]  read_addr;
    reg  [5:0]  write_addr;
    reg  [63:0]  disp_mem_data;
    reg      disp_mem_wren;
    wire  [63:0]  disp_mem_q;
initial 
  begin
    SDRAM_CS <= 1'b0;
    SRAM_CS <= 1'b0;
    clk_slow <= 16'b0000000000000000;
    row_cnt <= 5'b00000;
    e_cnt <= 4'b1000;
    write_addr <= 5'b00000;
    disp_mem_wren <= 0;
  end

always @(posedge clk) 
begin
  clk_slow <= clk_slow + 1'b1;
end

always @(posedge clk_slow[10]) 
begin
  if(row_cnt > 5'b01111)
  begin
    row_cnt <= 5'b00000;
    e_cnt <= {e_cnt[2:0],e_cnt[3]};
  end

  col <= disp_mem_q;  
  row_cnt <= row_cnt + 1'b1;
  read_addr <= read_addr + 1'b1;
  
end
  
always @(posedge SCLK)
begin
  if(LAT)
  begin
    col_buffer <= {col_buffer[62:0],INPUT[0]};
  end
  else
  begin
    disp_mem_data <= col_buffer;
    disp_mem_wren <= 1;
    disp_mem_wren <= 0;
    write_addr <= write_addr + 1'b1;
  end
end

display_memory disp_mem_inst (
        .rdaddress (read_addr),
        .wraddress (write_addr),
        .clock  (clk),
        .data   (disp_mem_data),
        .wren   (disp_mem_wren),
        .q      (disp_mem_q)
);

endmodule

128×32 DMD Verilog Code

Here is some Verilog code for a 32×128 DMD I am designing.


module LED_Matrix_32x128
(
  clk,e_cnt,row_cnt,col,SDRAM_CS,SRAM_CS,LAT,DATA,SCLK
);

input  wire    clk;
input  wire    LAT;
input  wire    DATA;
input  wire    SCLK;

output  reg [63:0]  col;
output  reg [0:0]   SDRAM_CS;
output  reg [0:0]   SRAM_CS;
output  reg [5:0]  row_cnt;
output  reg [4:0]  e_cnt;

    reg [24:0]  clk_slow;
    reg [4096:0]  col_buffer;
    reg [63:0]  col_temp [63:0];

initial 
  begin
    col <= 16'b0000000000000000;
    SDRAM_CS <= 1'b0;
    SRAM_CS <= 1'b0;
    clk_slow <= 16'b0000000000000000;
    row_cnt <= 5'b00000;
    e_cnt <= 4'b1000;
  end
  
always @(posedge clk) clk_slow=clk_slow + 1'b1;  

always @(posedge clk_slow[10]) 
begin
  if(row_cnt > 5'b01111)
  begin
    row_cnt <= 5'b00000;
    e_cnt <= {row[2:0],row[3]};
  end
  col <= col_temp[row_cnt];  
  row_cnt <= row_cnt + 1'b1;
end
  
always @(posedge SCLK)
begin
  if(LAT)
  begin
    col_buffer <= {col_buffer[4094:0],DATA[0]};
  end
  else
  begin
    col_temp [0] <= col_buffer [4095:4032];
    col_temp [1] <= col_buffer [4031:3968];
    col_temp [2] <= col_buffer [3967:3904];
    col_temp [3] <= col_buffer [3903:3840];
    col_temp [4] <= col_buffer [3839:3776];
    col_temp [5] <= col_buffer [3775:3712];
    col_temp [6] <= col_buffer [3711:3648];
    col_temp [7] <= col_buffer [3647:3584];
    col_temp [8] <= col_buffer [3583:3520];
    col_temp [9] <= col_buffer [3519:3456];
    col_temp [10] <= col_buffer [3455:3392];
    col_temp [11] <= col_buffer [3391:3328];
    col_temp [12] <= col_buffer [3327:3264];
    col_temp [13] <= col_buffer [3263:3200];
    col_temp [14] <= col_buffer [3199:3136];
    col_temp [15] <= col_buffer [3135:3072];
    col_temp [16] <= col_buffer [3071:3008];
    col_temp [17] <= col_buffer [3007:2944];
    col_temp [18] <= col_buffer [2943:2880];
    col_temp [19] <= col_buffer [2879:2816];
    col_temp [20] <= col_buffer [2815:2752];
    col_temp [21] <= col_buffer [2751:2688];
    col_temp [22] <= col_buffer [2687:2624];
    col_temp [23] <= col_buffer [2623:2560];
    col_temp [24] <= col_buffer [2559:2496];
    col_temp [25] <= col_buffer [2495:2432];
    col_temp [26] <= col_buffer [2431:2368];
    col_temp [27] <= col_buffer [2367:2304];
    col_temp [28] <= col_buffer [2303:2240];
    col_temp [29] <= col_buffer [2239:2176];
    col_temp [30] <= col_buffer [2175:2112];
    col_temp [31] <= col_buffer [2111:2048];
    col_temp [32] <= col_buffer [2047:1984];
    col_temp [33] <= col_buffer [1983:1920];
    col_temp [34] <= col_buffer [1919:1856];
    col_temp [35] <= col_buffer [1855:1792];
    col_temp [36] <= col_buffer [1791:1728];
    col_temp [37] <= col_buffer [1727:1664];
    col_temp [38] <= col_buffer [1663:1600];
    col_temp [39] <= col_buffer [1599:1536];
    col_temp [40] <= col_buffer [1535:1472];
    col_temp [41] <= col_buffer [1471:1408];
    col_temp [42] <= col_buffer [1407:1344];
    col_temp [43] <= col_buffer [1343:1280];
    col_temp [44] <= col_buffer [1279:1216];
    col_temp [45] <= col_buffer [1215:1152];
    col_temp [46] <= col_buffer [1151:1088];
    col_temp [47] <= col_buffer [1087:1024];
    col_temp [48] <= col_buffer [1023:960];
    col_temp [49] <= col_buffer [959:896];
    col_temp [50] <= col_buffer [895:832];
    col_temp [51] <= col_buffer [831:768];
    col_temp [52] <= col_buffer [767:704];
    col_temp [53] <= col_buffer [703:640];
    col_temp [54] <= col_buffer [639:576];
    col_temp [55] <= col_buffer [575:512];
    col_temp [56] <= col_buffer [511:448];
    col_temp [57] <= col_buffer [447:384];
    col_temp [58] <= col_buffer [383:320];
    col_temp [59] <= col_buffer [319:256];
    col_temp [60] <= col_buffer [255:192];
    col_temp [61] <= col_buffer [191:128];
    col_temp [62] <= col_buffer [127:64];
    col_temp [63] <= col_buffer [63:0];
  end
end
endmodule