generated from saji/ecp5-template
initial verilog
This commit is contained in:
parent
a0e1dcbdb0
commit
00ebfa8009
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -2,3 +2,5 @@
|
|||
*.o
|
||||
*.bin
|
||||
build/*
|
||||
*.vcd
|
||||
*.out
|
||||
|
|
9
Makefile
9
Makefile
|
@ -1,4 +1,9 @@
|
|||
|
||||
NEXTPNR_FLAGS= --45k --package CABGA381 --speed 6 --freq 65
|
||||
YOSYS_FLAGS=
|
||||
|
||||
|
||||
|
||||
.PHONY all clean test
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -28,15 +28,11 @@ class Hub75Driver(Module):
|
|||
if base_freq // 2 > 30e6:
|
||||
raise RuntimeError("hi")
|
||||
|
||||
self.phase = Signal() # divider/counter
|
||||
self.addr = Signal(5)
|
||||
self.latch = Signal()
|
||||
self.output_en = Signal()
|
||||
self.rgb = Signal(6, reset=0b111010)
|
||||
|
||||
|
||||
# clk-en acts as a gate.
|
||||
clock_en = Signal()
|
||||
color = Signal(24, reset=0xA0FF00)
|
||||
self.rgb = Signal(6)
|
||||
|
||||
self.clock_out = Signal()
|
||||
|
||||
|
@ -46,21 +42,34 @@ class Hub75Driver(Module):
|
|||
|
||||
counter = Signal(32)
|
||||
|
||||
should_expose = (counter < (16 << bcm_value)) & (bcm_value != 0)
|
||||
|
||||
|
||||
# this state both sets the OE low to drive the display with the previous frame
|
||||
# while also loading the next row
|
||||
# FIXME: there's a bug on the starting conditions right now, we are losing the lowest bit.
|
||||
fsm.act("WRITEROW",
|
||||
self.output_en.eq(1),
|
||||
If(counter < 256,
|
||||
self.clock_out.eq(counter[0]),
|
||||
self.output_en.eq(~should_expose),
|
||||
self.rgb[0].eq((color >> bcm_value) & 1),
|
||||
self.rgb[3].eq((color >> bcm_value) & 1),
|
||||
self.rgb[1].eq((color >> (bcm_value + 8)) & 1),
|
||||
self.rgb[4].eq((color >> (bcm_value + 8)) & 1),
|
||||
self.rgb[2].eq((color >> (bcm_value + 16)) & 1),
|
||||
self.rgb[5].eq((color >> (bcm_value + 16)) & 1),
|
||||
|
||||
NextValue(counter, counter + 1),
|
||||
If(counter[0], NextValue(self.rgb, self.rgb + 3)),
|
||||
).Else(
|
||||
If(counter < linedepth * 2,
|
||||
self.clock_out.eq(counter[0]),
|
||||
),
|
||||
If(~(counter < linedepth * 2) & ~should_expose,
|
||||
NextValue(counter, 0),
|
||||
NextState("EXPOSE"),
|
||||
NextState("LATCH"),
|
||||
),
|
||||
)
|
||||
|
||||
fsm.act("EXPOSE",
|
||||
self.output_en.eq(0),
|
||||
If(counter < (1000 << bcm_value),
|
||||
If(counter < (16 << bcm_value),
|
||||
NextValue(counter, counter + 1),
|
||||
).Else(
|
||||
NextValue(counter, 0),
|
||||
|
@ -70,52 +79,14 @@ class Hub75Driver(Module):
|
|||
|
||||
fsm.act("LATCH",
|
||||
self.latch.eq(1),
|
||||
self.output_en.eq(1),
|
||||
NextValue(counter, 0),
|
||||
If(bcm_value == 7,
|
||||
NextValue(bcm_value, 0),
|
||||
NextValue(self.addr, self.addr + 1),
|
||||
).Else(NextValue(bcm_value, 1)),
|
||||
).Else(
|
||||
NextValue(bcm_value, bcm_value + 1),
|
||||
),
|
||||
|
||||
NextState("WRITEROW"),
|
||||
)
|
||||
|
||||
|
||||
# fsm.act("ready",
|
||||
# NextValue(self.output_en, 1),
|
||||
# NextValue(self.pixnum, linedepth - 1),
|
||||
# NextValue(self.latch, 0),
|
||||
# If(self.phase == 1,
|
||||
# NextValue(self.addr, self.addr + 1),
|
||||
# NextValue(clock_en, 1),
|
||||
# NextState("transmit"),
|
||||
# ),
|
||||
# # If((self.state_count == 7), NextValue(clock_en, ~clock_en)),
|
||||
# )
|
||||
# fsm.act("transmit",
|
||||
# If(self.phase == 1,
|
||||
# NextValue(self.pixnum, self.pixnum - 1),
|
||||
# If(self.pixnum == 0,
|
||||
# NextState("latch_delay"),
|
||||
# )
|
||||
# )
|
||||
# )
|
||||
# fsm.act("latch_delay",
|
||||
# NextValue(clock_en, 0),
|
||||
# If(self.phase == 1,
|
||||
# NextState("latchout")
|
||||
# )
|
||||
# )
|
||||
# fsm.act("latchout",
|
||||
# If(self.phase == 1,
|
||||
# NextValue(self.latch, 1),
|
||||
# NextValue(counter, 0),
|
||||
# NextState("done")
|
||||
# )
|
||||
# )
|
||||
# fsm.act("done",
|
||||
# NextValue(self.output_en, 0),
|
||||
# NextValue(self.latch, 0),
|
||||
# NextValue(counter, counter + 1),
|
||||
# If(counter == 255, NextState("ready"))
|
||||
# )
|
||||
#
|
||||
|
|
125
verilog/hub75e.sv
Normal file
125
verilog/hub75e.sv
Normal file
|
@ -0,0 +1,125 @@
|
|||
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
|
48
verilog/tb/hub75e_tb.sv
Normal file
48
verilog/tb/hub75e_tb.sv
Normal file
|
@ -0,0 +1,48 @@
|
|||
`timescale 1ns / 100ps // 1 ns time unit, 100 ps resolution
|
||||
module hub75e_tb;
|
||||
|
||||
reg clk;
|
||||
reg write_trig;
|
||||
|
||||
reg [1:0][7:0] rgb_row[128];
|
||||
|
||||
wire [4:0] addr_out;
|
||||
wire [2:0] rgb0;
|
||||
wire [2:0] rgb1;
|
||||
wire display_clk;
|
||||
wire out_enable;
|
||||
wire latch;
|
||||
wire done;
|
||||
|
||||
|
||||
hub75e dut (
|
||||
.clk(clk),
|
||||
.write_trig(write_trig),
|
||||
.rgb_row(rgb_row),
|
||||
.addr(addr_out),
|
||||
.panel_rgb0(rgb0),
|
||||
.panel_rgb1(rgb1),
|
||||
.display_clk(display_clk),
|
||||
.out_enable(out_enable),
|
||||
.latch(latch),
|
||||
.done(done)
|
||||
);
|
||||
|
||||
always #5 clk = !clk;
|
||||
initial begin
|
||||
$dumpfile("wave.vcd");
|
||||
$dumpvars(0, hub75e_tb);
|
||||
clk = 0;
|
||||
write_trig = 1;
|
||||
repeat (2) @(posedge clk);
|
||||
write_trig = 0;
|
||||
|
||||
@(done);
|
||||
repeat (20) @(posedge clk);
|
||||
$finish();
|
||||
end
|
||||
initial begin
|
||||
repeat (100000) @(posedge clk);
|
||||
$finish();
|
||||
end
|
||||
endmodule
|
Loading…
Reference in a new issue