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 [10:0] pixbuf_addr, input [8:0] pixbuf_data ); parameter integer ROW_DEPTH = 128, BIT_DEPTH = 8; reg [31:0] counter = 0; reg [ 3:0] bcm_shift = 7; // which bit of the colors are we currently exposing. localparam integer StateInit = 0; localparam integer StateWriteRow = 1; localparam integer 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 integer StateFinishExpose = 3; reg [7:0] state = StateInit; // our state // initial begin // state <= StateInit; // counter <= 0; // bcm_shift <= 7; // end // 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; assign should_clock = counter < ROW_DEPTH * 2; assign should_expose = (counter < (16 << bcm_shift + 1)) && (bcm_shift != 7); always_ff @(posedge clk) begin counter <= counter + 1; case (state) StateInit: begin bcm_shift <= 7; counter <= 0; done <= 0; pixbuf_addr <= 0; // wait for the signal to write out our lines. if (write_trig) begin state <= StateWriteRow; end end StateWriteRow: begin if (should_clock) begin // we have data to clock display_clk <= counter[0]; if (~counter[0]) begin // the data from the previous cycle is now ready. panel_rgb0 <= { (pixbuf_data[bcm_shift]), (pixbuf_data[bcm_shift + 8]), (pixbuf_data[bcm_shift + 16]) }; panel_rgb1 <= { (pixbuf_data[bcm_shift]), (pixbuf_data[bcm_shift + 8]), (pixbuf_data[bcm_shift + 16]) }; // write it out! end else begin // update the bram address so it's ready at the next clock cycle. pixbuf_addr <= pixbuf_addr + 1; end 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; state <= StateLatchout; end end StateLatchout: begin // raise latch high; compute next bcm. latch <= 1; out_enable <= 1; pixbuf_addr <= 0; counter <= counter + 1; if (counter > 3) begin 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 <= StateWriteRow; latch <= 0; end end end StateFinishExpose: begin assert (bcm_shift == 0); if (counter < (16 << 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