module hub75e ( input clk, input write_trig, input [1:0][BIT_DEPTH - 1:0] rgb_row[ROW_DEPTH], output reg [4:0] addr, 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 ); 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 assign addr = 5'b10101; assign panel_rgb0 = 3'b101; assign panel_rgb1 = 3'b010; // 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 // wait for the signal to write out our lines. if (write_trig) begin bcm_shift <= 7; counter <= 0; done <= 0; state <= StateWriteRow; end end StateWriteRow: begin if (should_clock) begin // we have data to clock display_clk <= counter[0]; if (counter[0]) begin // load the next pixel data. this is the falling edge since the // previous value is 1. end end if (should_expose) begin // we are still in our expose state, and our bcm shift is not the // first one, so we should expose. 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 frame, go next. 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; counter <= counter + 1; if (counter > 3) begin if (bcm_shift == 0) begin // done with the line. do the last (short) expose 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 end endcase end endmodule