module hub75e ( input clk, input write_trig, output reg [2:0] panel_rgb0, output reg [2:0] panel_rgb1, output reg display_clk = 0, output reg out_enable = 1, output reg latch = 0, output reg done = 0, // bram interface (using clk) output reg [8:0] pixbuf_addr, input [35:0] pixbuf_data ); parameter ROW_DEPTH = 128, BIT_DEPTH = 8, BCM_LEN = 32; reg [31:0] counter = 0; // which bit of the colors are we currently exposing. reg [$clog2(BIT_DEPTH) - 1:0] bcm_shift = 7; localparam StateInit = 0; localparam StateWriteRow = 1; localparam StateLatchout = 2; // the last data that we clock out for the row won't be exposed // in the next writerow state because we'll change addresses. localparam StateFinishExpose = 3; // this state is used to prefetch the first pixel so the cycle can work. localparam StatePreload = 4; reg [7:0] state = StateInit; // our state // The FSM is a bit confusing since it's optimized for *speed* // We can basically display the previous line of data while we write the // next one. So instead of having WRITEROW -> LATCH -> EXPOSE // like most modules do, we can instead do (WRITEROW + EXPOSE_PREV) -> LATCH // There is an edge case for the first/last bits of the color depth. // When we start writing a line, we can't flash anything since there's no // previous. Likewise, when we end a line, we have to have an extra expose // period since there is no next writerow for this address. As a result, // we want to go MSB to LSB for our BCM so that the trailing expose time is // short! wire should_clock, should_expose; // the plus 1 is for the falling edge! assign should_clock = (counter < (ROW_DEPTH << 2) + 1); assign should_expose = (counter < (BCM_LEN << bcm_shift + 1)) && (bcm_shift != 7); wire [7:0] ram_r, ram_g, ram_b; assign ram_r = pixbuf_data[23:16]; assign ram_g = pixbuf_data[15:8]; assign ram_b = pixbuf_data[7:0]; wire [2:0] ram_rgb_slice; assign ram_rgb_slice = { (ram_r[bcm_shift]), (ram_g[bcm_shift]), (ram_b[bcm_shift]) }; reg [7:0] pixnum; reg pixrow = 0; assign pixbuf_addr = {pixrow, pixnum}; always_ff @(posedge clk) begin counter <= counter + 1; case (state) StateInit: begin bcm_shift <= 7; counter <= 0; done <= 0; pixnum <= ROW_DEPTH - 1; pixrow <= 0; // wait for the signal to write out our lines. if (write_trig) begin state <= StatePreload; end end StatePreload: begin case (counter) 0: begin pixrow <= 0; end 1: begin // load pix 1 panel_rgb0 <= ram_rgb_slice; // wait for pix 1 pixrow <= 1; end 2: begin panel_rgb1 <= ram_rgb_slice; pixrow <= 0; counter <= 0; state <= StateWriteRow; end default: begin counter <= 0; end endcase end StateWriteRow: begin if (should_clock) begin // we have data to clock display_clk <= counter[1]; case (counter[1:0]) 2'b10: begin // rising edge // fetch pixel 1 pixrow <= 0; end 2'b11: begin // midpoint of high clk. // fetch pixel 2, load pixel 1 pixrow <= 1; panel_rgb0 <= ram_rgb_slice; end 2'b00: begin // falling edge // load pixel 2 end 2'b01: begin // midpoint of low clk panel_rgb1 <= ram_rgb_slice; // decrement pixnum pixnum <= pixnum - 1; pixrow <= 0; end default: begin end endcase end if (should_expose) begin out_enable <= 0; end else begin out_enable <= 1; end // if we're done with our data clock out and also done with exposing // the previous line, go to the latchout stage. if (~should_clock && ~should_expose) begin counter <= 0; pixnum <= 0; state <= StateLatchout; display_clk <= 0; end end StateLatchout: begin // raise latch high; compute next bcm. latch <= 1; out_enable <= 1; pixnum <= ROW_DEPTH - 1; if (counter > 3) begin counter <= 0; if (bcm_shift == 0) begin // we've reached the lsb of this data, go to the next one! state <= StateFinishExpose; latch <= 0; end else begin bcm_shift <= bcm_shift - 1; state <= StatePreload; latch <= 0; end end end StateFinishExpose: begin assert (bcm_shift == 0); if (counter < (BCM_LEN << bcm_shift)) begin out_enable <= 0; end else begin out_enable <= 1; state <= StateInit; done <= 1; // we are done! end end default: begin state <= StateInit; end endcase end endmodule