groovylight/verilog/hub75e.sv

182 lines
5.1 KiB
Systemverilog
Raw Normal View History

2024-04-28 21:42:41 +00:00
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,
2024-04-29 06:16:36 +00:00
output reg done = 0,
// bram interface (using clk)
2024-05-09 22:31:15 +00:00
output reg [8:0] pixbuf_addr,
input [35:0] pixbuf_data
2024-04-28 21:42:41 +00:00
);
2024-05-09 22:31:15 +00:00
parameter ROW_DEPTH = 128, BIT_DEPTH = 8, BCM_LEN = 32;
2024-04-28 21:42:41 +00:00
reg [31:0] counter = 0;
2024-05-09 22:31:15 +00:00
// which bit of the colors are we currently exposing.
reg [$clog2(BIT_DEPTH) - 1:0] bcm_shift = 7;
2024-04-28 21:42:41 +00:00
2024-05-09 22:31:15 +00:00
localparam StateInit = 0;
localparam StateWriteRow = 1;
localparam StateLatchout = 2;
2024-04-28 21:42:41 +00:00
// the last data that we clock out for the row won't be exposed
// in the next writerow state because we'll change addresses.
2024-05-09 22:31:15 +00:00
localparam StateFinishExpose = 3;
2024-04-28 21:42:41 +00:00
2024-05-09 22:31:15 +00:00
// this state is used to prefetch the first pixel so the cycle can work.
localparam StatePreload = 4;
2024-04-28 21:42:41 +00:00
2024-05-09 22:31:15 +00:00
reg [7:0] state = StateInit; // our state
2024-04-28 21:42:41 +00:00
// 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;
2024-05-09 22:31:15 +00:00
// 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])
};
2024-04-28 21:42:41 +00:00
reg [7:0] pixnum;
reg pixrow = 0;
assign pixbuf_addr = {pixrow, pixnum};
2024-04-28 21:42:41 +00:00
always_ff @(posedge clk) begin
counter <= counter + 1;
case (state)
StateInit: begin
2024-04-29 06:16:36 +00:00
bcm_shift <= 7;
counter <= 0;
done <= 0;
pixnum <= ROW_DEPTH - 1;
pixrow <= 0;
2024-04-28 21:42:41 +00:00
// wait for the signal to write out our lines.
if (write_trig) begin
2024-05-09 22:31:15 +00:00
state <= StatePreload;
2024-04-28 21:42:41 +00:00
end
end
2024-05-09 22:31:15 +00:00
StatePreload: begin
case (counter)
0: begin
pixrow <= 0;
2024-05-09 22:31:15 +00:00
end
1: begin
2024-05-09 22:31:15 +00:00
// load pix 1
panel_rgb0 <= ram_rgb_slice;
// wait for pix 1
pixrow <= 1;
2024-05-09 22:31:15 +00:00
end
2: begin
2024-05-09 22:31:15 +00:00
panel_rgb1 <= ram_rgb_slice;
pixrow <= 0;
2024-05-09 22:31:15 +00:00
counter <= 0;
state <= StateWriteRow;
end
default: begin
counter <= 0;
end
endcase
end
2024-04-28 21:42:41 +00:00
StateWriteRow: begin
if (should_clock) begin
// we have data to clock
2024-05-09 22:31:15 +00:00
display_clk <= counter[1];
case (counter[1:0])
2'b10: begin // rising edge
// fetch pixel 1
pixrow <= 0;
2024-05-09 22:31:15 +00:00
end
2'b11: begin // midpoint of high clk.
// fetch pixel 2, load pixel 1
pixrow <= 1;
2024-05-09 22:31:15 +00:00
panel_rgb0 <= ram_rgb_slice;
end
2'b00: begin // falling edge
// load pixel 2
panel_rgb1 <= ram_rgb_slice;
pixnum <= pixnum - 1;
pixrow <= 0;
2024-05-09 22:31:15 +00:00
end
2'b01: begin // midpoint of low clk
// decrement pixnum
end
default: begin
end
endcase
2024-04-28 21:42:41 +00:00
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
2024-04-29 06:16:36 +00:00
// the previous line, go to the latchout stage.
2024-04-28 21:42:41 +00:00
if (~should_clock && ~should_expose) begin
counter <= 0;
pixnum <= 0;
state <= StateLatchout;
display_clk <= 0;
2024-04-28 21:42:41 +00:00
end
end
StateLatchout: begin
// raise latch high; compute next bcm.
latch <= 1;
out_enable <= 1;
pixnum <= ROW_DEPTH - 1;
2024-04-28 21:42:41 +00:00
if (counter > 3) begin
counter <= 0;
2024-04-28 21:42:41 +00:00
if (bcm_shift == 0) begin
2024-04-29 06:16:36 +00:00
// we've reached the lsb of this data, go to the next one!
2024-04-28 21:42:41 +00:00
state <= StateFinishExpose;
latch <= 0;
end else begin
bcm_shift <= bcm_shift - 1;
state <= StatePreload;
2024-04-28 21:42:41 +00:00
latch <= 0;
end
end
end
StateFinishExpose: begin
assert (bcm_shift == 0);
2024-05-09 22:31:15 +00:00
if (counter < (BCM_LEN << bcm_shift)) begin
2024-04-28 21:42:41 +00:00
out_enable <= 0;
end else begin
out_enable <= 1;
state <= StateInit;
done <= 1;
// we are done!
end
end
default: begin
state <= StateInit;
2024-04-28 21:42:41 +00:00
end
endcase
end
endmodule