generated from saji/ecp5-template
add basic verilator sim
This commit is contained in:
parent
b248c4d731
commit
d4df01f5c6
|
@ -58,6 +58,9 @@
|
||||||
# loader
|
# loader
|
||||||
openfpgaloader
|
openfpgaloader
|
||||||
ecpdap # easier to poke probes.
|
ecpdap # easier to poke probes.
|
||||||
|
|
||||||
|
# for building the simulator
|
||||||
|
cmake
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
2
sim/.gitignore
vendored
Normal file
2
sim/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
build/
|
||||||
|
.cache/
|
36
sim/CMakeLists.txt
Normal file
36
sim/CMakeLists.txt
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
project(groovylight-sim)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
dear_imgui
|
||||||
|
GIT_REPOSITORY https://github.com/ocornut/imgui.git
|
||||||
|
GIT_TAG 231cbee0fc4f59dbe5b8b853a11b08dc84e57c65 # version 1.90.5
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
sokol
|
||||||
|
GIT_REPOSITORY https://github.com/floooh/sokol.git
|
||||||
|
GIT_TAG 55bc9cf3fa4051d485d10412c75c893c3135e885
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(sokol dear_imgui)
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||||
|
|
||||||
|
find_package(verilator HINTS $ENV{VERILATOR_ROOT})
|
||||||
|
add_executable(sim)
|
||||||
|
|
||||||
|
target_sources(sim PRIVATE
|
||||||
|
src/main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
list(APPEND VSOURCES ../verilog/hub75e.sv ../verilog/lineram.v)
|
||||||
|
|
||||||
|
verilate(sim SOURCES ${VSOURCES} TRACE VERILATOR_ARGS -Wno-MULTITOP)
|
160
sim/src/main.cpp
Normal file
160
sim/src/main.cpp
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
#include "Vhub75e.h"
|
||||||
|
#include "verilated.h"
|
||||||
|
#include "verilated_vcd_c.h"
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// slices the RGB values for us.
|
||||||
|
uint8_t rgb_slice(uint32_t rgb, uint8_t bit) {
|
||||||
|
if (bit > 8) {
|
||||||
|
// todo: panic
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint8_t r = (rgb >> (16 + bit)) & 1;
|
||||||
|
uint8_t g = (rgb >> (8 + bit)) & 1;
|
||||||
|
uint8_t b = (rgb >> bit) & 1;
|
||||||
|
return (r << 2) & (g << 1) & (b << 1);
|
||||||
|
}
|
||||||
|
class HUB75Reciever {
|
||||||
|
|
||||||
|
typedef std::vector<unsigned char> row_array;
|
||||||
|
int xsize;
|
||||||
|
int ysize;
|
||||||
|
|
||||||
|
row_array row_upper;
|
||||||
|
row_array row_lower;
|
||||||
|
|
||||||
|
// the previous row values that were latched in.
|
||||||
|
std::vector<row_array> past_rows;
|
||||||
|
// the pulse width for each output, in clock cycles.
|
||||||
|
std::vector<int> pulse_widths;
|
||||||
|
|
||||||
|
int bit_position = 7; // the bit that is currently being shifted in
|
||||||
|
|
||||||
|
int output_period_cnt;
|
||||||
|
// if oe = 0, count clocks. when oe = 1, store value into
|
||||||
|
// pulse_widths[display_bit];
|
||||||
|
|
||||||
|
// previous latch value, used to identify when to latch.
|
||||||
|
unsigned char prev_latch = 0;
|
||||||
|
// previous display clock value, used to detect rising edge.
|
||||||
|
unsigned char prev_display_clk = 0;
|
||||||
|
|
||||||
|
unsigned char prev_oe = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HUB75Reciever(int xsize, int ysize) : row_upper(xsize, 0) {
|
||||||
|
this->xsize = xsize;
|
||||||
|
this->ysize = ysize;
|
||||||
|
};
|
||||||
|
|
||||||
|
// evaluates the reciever.
|
||||||
|
void eval(const int oe, const int latch, const int display_clk,
|
||||||
|
const int rgb0) {
|
||||||
|
|
||||||
|
if (prev_display_clk == 0 && display_clk == 1) {
|
||||||
|
// display clock rising edge.
|
||||||
|
row_upper.push_back(rgb0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev_latch == 0 && latch == 1) {
|
||||||
|
// latch in the data: reverse the rows, and pu
|
||||||
|
std::reverse(row_upper.begin(), row_upper.end());
|
||||||
|
past_rows.push_back(row_upper);
|
||||||
|
row_upper.clear();
|
||||||
|
}
|
||||||
|
if (oe == 0) {
|
||||||
|
if (prev_oe == 1) {
|
||||||
|
// falling edge.
|
||||||
|
output_period_cnt = 0;
|
||||||
|
} else {
|
||||||
|
output_period_cnt++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// rising edge: store the output
|
||||||
|
}
|
||||||
|
|
||||||
|
// update previous values
|
||||||
|
prev_display_clk = display_clk;
|
||||||
|
prev_latch = latch;
|
||||||
|
prev_oe = oe;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FakeBRAM {
|
||||||
|
std::array<uint64_t, 512> ram{};
|
||||||
|
|
||||||
|
std::queue<uint16_t> addr_lookup_q;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FakeBRAM(int latency) : addr_lookup_q() {
|
||||||
|
for (int i = 0; i < latency; i++) {
|
||||||
|
addr_lookup_q.push(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ram.size(); i++) {
|
||||||
|
ram[i] = i;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
uint64_t tick(int addr) {
|
||||||
|
// we push and pop in the same tick: this way we keep the queue the same
|
||||||
|
// size, acting as a pipeline delay.
|
||||||
|
addr_lookup_q.push(addr);
|
||||||
|
|
||||||
|
auto addr_to_load = addr_lookup_q.front();
|
||||||
|
addr_lookup_q.pop();
|
||||||
|
return ram.at(addr_to_load);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: allow accessing/setting the data
|
||||||
|
};
|
||||||
|
|
||||||
|
void LineDriverTest(VerilatedContext &ctx) {
|
||||||
|
// create the hub75e driver and run some basic tests
|
||||||
|
|
||||||
|
// we generate 512 random color values (24 bits)
|
||||||
|
// we load them in.
|
||||||
|
unsigned long counter = 0; // counts positive edges.
|
||||||
|
unsigned long simtime = 0;
|
||||||
|
auto dut = std::make_unique<Vhub75e>(&ctx, "dut");
|
||||||
|
|
||||||
|
VerilatedVcdC *m_trace = new VerilatedVcdC;
|
||||||
|
dut->trace(m_trace, 5);
|
||||||
|
|
||||||
|
auto bram = FakeBRAM(1);
|
||||||
|
printf("Performing basic test\n");
|
||||||
|
bool done = false;
|
||||||
|
m_trace->open("waveform.vcd");
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
dut->clk ^= 1; // toggle clock
|
||||||
|
dut->eval();
|
||||||
|
|
||||||
|
if (dut->clk == 1) {
|
||||||
|
// rising edge.
|
||||||
|
counter++;
|
||||||
|
dut->pixbuf_data = bram.tick(dut->pixbuf_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
dut->write_trig = counter < 20 ? 1 : 0;
|
||||||
|
|
||||||
|
if (counter >= 250000) {
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
m_trace->dump(simtime);
|
||||||
|
simtime++;
|
||||||
|
}
|
||||||
|
m_trace->close();
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto ctx = std::make_unique<VerilatedContext>();
|
||||||
|
ctx->traceEverOn(true);
|
||||||
|
printf("hello world!\n");
|
||||||
|
LineDriverTest(*ctx);
|
||||||
|
};
|
|
@ -1,115 +1,119 @@
|
||||||
module lineram (
|
module lineram (
|
||||||
input [35:0] din,
|
input [35:0] din,
|
||||||
input [8:0] addr_w,
|
input [8:0] addr_w,
|
||||||
output wire [35:0] dout,
|
output reg [35:0] dout,
|
||||||
input [8:0] addr_r,
|
input [8:0] addr_r,
|
||||||
input write_en,
|
input write_en,
|
||||||
input read_clk,
|
input read_clk,
|
||||||
input write_clk
|
input write_clk
|
||||||
);
|
);
|
||||||
|
|
||||||
// `ifdef YOSYS
|
`ifdef YOSYS
|
||||||
// // use the ECP5 primitive.
|
// use the ECP5 primitive.
|
||||||
// PDPW16KD ram (
|
PDPW16KD #(.REGMODE("OUTREG")) ram (
|
||||||
// .DI0 (din[0]),
|
.DI0 (din[0]),
|
||||||
// .DI1 (din[1]),
|
.DI1 (din[1]),
|
||||||
// .DI2 (din[2]),
|
.DI2 (din[2]),
|
||||||
// .DI3 (din[3]),
|
.DI3 (din[3]),
|
||||||
// .DI4 (din[4]),
|
.DI4 (din[4]),
|
||||||
// .DI5 (din[5]),
|
.DI5 (din[5]),
|
||||||
// .DI6 (din[6]),
|
.DI6 (din[6]),
|
||||||
// .DI7 (din[7]),
|
.DI7 (din[7]),
|
||||||
// .DI8 (din[8]),
|
.DI8 (din[8]),
|
||||||
// .DI9 (din[9]),
|
.DI9 (din[9]),
|
||||||
// .DI10(din[10]),
|
.DI10(din[10]),
|
||||||
// .DI11(din[11]),
|
.DI11(din[11]),
|
||||||
// .DI12(din[12]),
|
.DI12(din[12]),
|
||||||
// .DI13(din[13]),
|
.DI13(din[13]),
|
||||||
// .DI14(din[14]),
|
.DI14(din[14]),
|
||||||
// .DI15(din[15]),
|
.DI15(din[15]),
|
||||||
// .DI16(din[16]),
|
.DI16(din[16]),
|
||||||
// .DI17(din[17]),
|
.DI17(din[17]),
|
||||||
// .DI18(din[18]),
|
.DI18(din[18]),
|
||||||
// .DI19(din[19]),
|
.DI19(din[19]),
|
||||||
// .DI20(din[20]),
|
.DI20(din[20]),
|
||||||
// .DI21(din[21]),
|
.DI21(din[21]),
|
||||||
// .DI22(din[22]),
|
.DI22(din[22]),
|
||||||
// .DI23(din[23]),
|
.DI23(din[23]),
|
||||||
// .DI24(din[24]),
|
.DI24(din[24]),
|
||||||
// .DI25(din[25]),
|
.DI25(din[25]),
|
||||||
// .DI26(din[26]),
|
.DI26(din[26]),
|
||||||
// .DI27(din[27]),
|
.DI27(din[27]),
|
||||||
// .DI28(din[28]),
|
.DI28(din[28]),
|
||||||
// .DI29(din[29]),
|
.DI29(din[29]),
|
||||||
// .DI30(din[30]),
|
.DI30(din[30]),
|
||||||
// .DI31(din[31]),
|
.DI31(din[31]),
|
||||||
// .DI32(din[32]),
|
.DI32(din[32]),
|
||||||
// .DI33(din[33]),
|
.DI33(din[33]),
|
||||||
// .DI34(din[34]),
|
.DI34(din[34]),
|
||||||
// .DI35(din[35]),
|
.DI35(din[35]),
|
||||||
// .ADW0(addr_w[0]),
|
.ADW0(addr_w[0]),
|
||||||
// .ADW1(addr_w[1]),
|
.ADW1(addr_w[1]),
|
||||||
// .ADW2(addr_w[2]),
|
.ADW2(addr_w[2]),
|
||||||
// .ADW3(addr_w[3]),
|
.ADW3(addr_w[3]),
|
||||||
// .ADW4(addr_w[4]),
|
.ADW4(addr_w[4]),
|
||||||
// .ADW5(addr_w[5]),
|
.ADW5(addr_w[5]),
|
||||||
// .ADW6(addr_w[6]),
|
.ADW6(addr_w[6]),
|
||||||
// .ADW7(addr_w[7]),
|
.ADW7(addr_w[7]),
|
||||||
// .ADW8(addr_w[8]),
|
.ADW8(addr_w[8]),
|
||||||
// .DO0 (dout[0]),
|
.DO0 (dout[0]),
|
||||||
// .DO1 (dout[1]),
|
.DO1 (dout[1]),
|
||||||
// .DO2 (dout[2]),
|
.DO2 (dout[2]),
|
||||||
// .DO3 (dout[3]),
|
.DO3 (dout[3]),
|
||||||
// .DO4 (dout[4]),
|
.DO4 (dout[4]),
|
||||||
// .DO5 (dout[5]),
|
.DO5 (dout[5]),
|
||||||
// .DO6 (dout[6]),
|
.DO6 (dout[6]),
|
||||||
// .DO7 (dout[7]),
|
.DO7 (dout[7]),
|
||||||
// .DO8 (dout[8]),
|
.DO8 (dout[8]),
|
||||||
// .DO9 (dout[9]),
|
.DO9 (dout[9]),
|
||||||
// .DO10(dout[10]),
|
.DO10(dout[10]),
|
||||||
// .DO11(dout[11]),
|
.DO11(dout[11]),
|
||||||
// .DO12(dout[12]),
|
.DO12(dout[12]),
|
||||||
// .DO13(dout[13]),
|
.DO13(dout[13]),
|
||||||
// .DO14(dout[14]),
|
.DO14(dout[14]),
|
||||||
// .DO15(dout[15]),
|
.DO15(dout[15]),
|
||||||
// .DO16(dout[16]),
|
.DO16(dout[16]),
|
||||||
// .DO17(dout[17]),
|
.DO17(dout[17]),
|
||||||
// .DO18(dout[18]),
|
.DO18(dout[18]),
|
||||||
// .DO19(dout[19]),
|
.DO19(dout[19]),
|
||||||
// .DO20(dout[20]),
|
.DO20(dout[20]),
|
||||||
// .DO21(dout[21]),
|
.DO21(dout[21]),
|
||||||
// .DO22(dout[22]),
|
.DO22(dout[22]),
|
||||||
// .DO23(dout[23]),
|
.DO23(dout[23]),
|
||||||
// .DO24(dout[24]),
|
.DO24(dout[24]),
|
||||||
// .DO25(dout[25]),
|
.DO25(dout[25]),
|
||||||
// .DO26(dout[26]),
|
.DO26(dout[26]),
|
||||||
// .DO27(dout[27]),
|
.DO27(dout[27]),
|
||||||
// .DO28(dout[28]),
|
.DO28(dout[28]),
|
||||||
// .DO29(dout[29]),
|
.DO29(dout[29]),
|
||||||
// .DO30(dout[30]),
|
.DO30(dout[30]),
|
||||||
// .DO31(dout[31]),
|
.DO31(dout[31]),
|
||||||
// .DO32(dout[32]),
|
.DO32(dout[32]),
|
||||||
// .DO33(dout[33]),
|
.DO33(dout[33]),
|
||||||
// .DO34(dout[34]),
|
.DO34(dout[34]),
|
||||||
// .DO35(dout[35]),
|
.DO35(dout[35]),
|
||||||
// .ADR0(addr_r[0]),
|
.ADR0(addr_r[0]),
|
||||||
// .ADR1(addr_r[1]),
|
.ADR1(addr_r[1]),
|
||||||
// .ADR2(addr_r[2]),
|
.ADR2(addr_r[2]),
|
||||||
// .ADR3(addr_r[3]),
|
.ADR3(addr_r[3]),
|
||||||
// .ADR4(addr_r[4]),
|
.ADR4(addr_r[4]),
|
||||||
// .ADR5(addr_r[5]),
|
.ADR5(addr_r[5]),
|
||||||
// .ADR6(addr_r[6]),
|
.ADR6(addr_r[6]),
|
||||||
// .ADR7(addr_r[7]),
|
.ADR7(addr_r[7]),
|
||||||
// .ADR8(addr_r[8]),
|
.ADR8(addr_r[8]),
|
||||||
// .CER(1),
|
.CER(1),
|
||||||
// .CEW(1),
|
.CEW(1),
|
||||||
// .CLKR(read_clk),
|
.CLKR(read_clk),
|
||||||
// .CLKW(write_clk),
|
.CLKW(write_clk),
|
||||||
//
|
.CSW1(0),
|
||||||
//
|
.CSW2(0),
|
||||||
// );
|
.CSW3(0),
|
||||||
// `else
|
.CSR1(0),
|
||||||
reg [35:0] ram [2**9];
|
.CSR2(0),
|
||||||
|
.CSR3(0),
|
||||||
|
);
|
||||||
|
`else
|
||||||
|
reg [35:0] ram [2**9]/*verilator public*/;
|
||||||
|
|
||||||
`ifndef YOSYS
|
`ifndef YOSYS
|
||||||
initial begin
|
initial begin
|
||||||
|
@ -125,5 +129,5 @@ module lineram (
|
||||||
always @(posedge read_clk) begin
|
always @(posedge read_clk) begin
|
||||||
dout <= ram[addr_r];
|
dout <= ram[addr_r];
|
||||||
end
|
end
|
||||||
// `endif
|
`endif
|
||||||
endmodule
|
endmodule
|
||||||
|
|
Loading…
Reference in a new issue