groovylight/verilog/hub75e.sv
2024-04-28 16:42:41 -05:00

126 lines
3.7 KiB
Systemverilog

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