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
|
*.o
|
||||||
*.bin
|
*.bin
|
||||||
build/*
|
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:
|
if base_freq // 2 > 30e6:
|
||||||
raise RuntimeError("hi")
|
raise RuntimeError("hi")
|
||||||
|
|
||||||
self.phase = Signal() # divider/counter
|
|
||||||
self.addr = Signal(5)
|
self.addr = Signal(5)
|
||||||
self.latch = Signal()
|
self.latch = Signal()
|
||||||
self.output_en = Signal()
|
self.output_en = Signal()
|
||||||
self.rgb = Signal(6, reset=0b111010)
|
color = Signal(24, reset=0xA0FF00)
|
||||||
|
self.rgb = Signal(6)
|
||||||
|
|
||||||
# clk-en acts as a gate.
|
|
||||||
clock_en = Signal()
|
|
||||||
|
|
||||||
self.clock_out = Signal()
|
self.clock_out = Signal()
|
||||||
|
|
||||||
|
@ -46,21 +42,34 @@ class Hub75Driver(Module):
|
||||||
|
|
||||||
counter = Signal(32)
|
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",
|
fsm.act("WRITEROW",
|
||||||
self.output_en.eq(1),
|
self.output_en.eq(~should_expose),
|
||||||
If(counter < 256,
|
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 < linedepth * 2,
|
||||||
self.clock_out.eq(counter[0]),
|
self.clock_out.eq(counter[0]),
|
||||||
NextValue(counter, counter + 1),
|
),
|
||||||
If(counter[0], NextValue(self.rgb, self.rgb + 3)),
|
If(~(counter < linedepth * 2) & ~should_expose,
|
||||||
).Else(
|
|
||||||
NextValue(counter, 0),
|
NextValue(counter, 0),
|
||||||
NextState("EXPOSE"),
|
NextState("LATCH"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
fsm.act("EXPOSE",
|
fsm.act("EXPOSE",
|
||||||
self.output_en.eq(0),
|
self.output_en.eq(0),
|
||||||
If(counter < (1000 << bcm_value),
|
If(counter < (16 << bcm_value),
|
||||||
NextValue(counter, counter + 1),
|
NextValue(counter, counter + 1),
|
||||||
).Else(
|
).Else(
|
||||||
NextValue(counter, 0),
|
NextValue(counter, 0),
|
||||||
|
@ -70,52 +79,14 @@ class Hub75Driver(Module):
|
||||||
|
|
||||||
fsm.act("LATCH",
|
fsm.act("LATCH",
|
||||||
self.latch.eq(1),
|
self.latch.eq(1),
|
||||||
|
self.output_en.eq(1),
|
||||||
NextValue(counter, 0),
|
NextValue(counter, 0),
|
||||||
If(bcm_value == 7,
|
If(bcm_value == 7,
|
||||||
NextValue(bcm_value, 0),
|
NextValue(bcm_value, 0),
|
||||||
NextValue(self.addr, self.addr + 1),
|
NextValue(self.addr, self.addr + 1),
|
||||||
).Else(NextValue(bcm_value, 1)),
|
).Else(
|
||||||
|
NextValue(bcm_value, bcm_value + 1),
|
||||||
|
),
|
||||||
|
|
||||||
NextState("WRITEROW"),
|
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