refactoring: move dma simulation to verilog

This commit is contained in:
Peter McGoron 2023-01-30 13:54:17 +00:00
parent 4afc655104
commit b3a79f41ec
6 changed files with 148 additions and 59 deletions

View File

@ -2,18 +2,18 @@
.PHONY: test clean .PHONY: test clean
test: obj_dir/Vbram_interface test: obj_dir/Vbram_interface_sim
bram_SRC= bram_interface.v bram_interface_sim.cpp bram_SRC= bram_interface_sim.v dma_sim.v bram_interface.v bram_interface_sim.cpp
obj_dir/Vbram_interface.mk: $(bram_SRC) obj_dir/Vbram_interface_sim.mk: $(bram_SRC)
verilator --cc --exe -Wall --trace --trace-fst \ verilator --cc --exe -Wall --trace --trace-fst \
-CFLAGS -DWORD_AMNT=2048 \ -CFLAGS -DWORD_AMNT=2048 \
-CFLAGS -DRAM_WID=32 \ -CFLAGS -DRAM_WID=32 \
$(bram_SRC) $(bram_SRC)
obj_dir/Vbram_interface: obj_dir/Vbram_interface.mk obj_dir/Vbram_interface_sim: obj_dir/Vbram_interface_sim.mk
cd obj_dir && make -f Vbram_interface.mk cd obj_dir && make -f Vbram_interface_sim.mk
./obj_dir/Vbram_interface ./obj_dir/Vbram_interface_sim
clean: clean:
rm -rf obj_dir/ rm -rf obj_dir/

View File

@ -105,11 +105,4 @@ end else if (!word_next && word_ok) begin
word_ok <= 0; word_ok <= 0;
end end
`ifdef VERILATOR
initial begin
$dumpfile("bram.fst");
$dumpvars;
end
`endif
endmodule endmodule

View File

@ -4,42 +4,11 @@
#include <unistd.h> #include <unistd.h>
#include <verilated.h> #include <verilated.h>
#include "Vbram_interface.h" #include "Vbram_interface_sim.h"
#include "../testbench.hpp" #include "../testbench.hpp"
TB<Vbram_interface> *tb;
constexpr uint32_t start_addr = 0x12340;
std::array<uint32_t, WORD_AMNT> ram_refresh_data; std::array<uint32_t, WORD_AMNT> ram_refresh_data;
TB<Vbram_interface_sim> *tb;
static void handle_ram() {
static int timer = 0;
constexpr auto TIMER_MAX = 10;
bool flip_flop = false;
if (tb->mod.ram_read) {
timer++;
if (timer == TIMER_MAX) {
tb->mod.ram_valid = 1;
if (tb->mod.ram_dma_addr < start_addr ||
tb->mod.ram_dma_addr >= start_addr + WORD_AMNT*4) {
printf("bad address %x\n", tb->mod.ram_dma_addr);
exit(1);
}
my_assert(tb->mod.ram_dma_addr >= start_addr, "left oob access %x", tb->mod.ram_dma_addr);
my_assert(tb->mod.ram_dma_addr < start_addr + WORD_AMNT*4, "right oob access %x", tb->mod.ram_dma_addr);
my_assert(tb->mod.ram_dma_addr % 2 == 0, "unaligned access %x", tb->mod.ram_dma_addr);
if (tb->mod.ram_dma_addr % 4 == 0) {
tb->mod.ram_word = ram_refresh_data[(tb->mod.ram_dma_addr - start_addr)/4]& 0xFFFF;
} else {
tb->mod.ram_word = ram_refresh_data[(tb->mod.ram_dma_addr - start_addr)/4] >> 16;
}
}
} else {
tb->mod.ram_valid = 0;
timer = 0;
}
}
static void handle_read_aa(size_t &i) { static void handle_read_aa(size_t &i) {
if (tb->mod.word_ok) { if (tb->mod.word_ok) {
@ -95,17 +64,18 @@ static void test_aa_read_interrupted() {
static void refresh_data() { static void refresh_data() {
for (size_t i = 0; i < RAM_WID; i++) { for (size_t i = 0; i < RAM_WID; i++) {
ram_refresh_data[i] = mask_extend(rand(), 20); uint32_t val = mask_extend(rand(), 20);
ram_refresh_data[i] = val;
tb->mod.backing_store[i*2] = val & 0xFFFF;
tb->mod.backing_store[i*2+1] = val >> 16;
} }
tb->mod.refresh_start = 1; tb->mod.refresh_start = 1;
tb->mod.start_addr = start_addr; tb->mod.start_addr = 0x12340;
tb->run_clock(); tb->run_clock();
while (!tb->mod.refresh_finished) { while (!tb->mod.refresh_finished)
handle_ram();
tb->run_clock(); tb->run_clock();
}
tb->mod.refresh_start = 0; tb->mod.refresh_start = 0;
tb->run_clock(); tb->run_clock();
@ -113,8 +83,11 @@ static void refresh_data() {
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
Verilated::commandArgs(argc, argv);
Verilated::traceEverOn(true); Verilated::traceEverOn(true);
tb = new TB<Vbram_interface>(argc, argv); Verilated::fatalOnError(false);
tb = new TB<Vbram_interface_sim>(argc, argv);
printf("test basic read/write\n"); printf("test basic read/write\n");
refresh_data(); refresh_data();

View File

@ -0,0 +1,81 @@
module bram_interface_sim #(
parameter WORD_WID = 24,
parameter WORD_AMNT_WID = 11,
parameter [WORD_AMNT_WID-1:0] WORD_AMNT = 2047,
parameter RAM_WID = 32,
parameter RAM_WORD_WID = 16,
parameter RAM_REAL_START = 32'h12340,
parameter RAM_CNTR_LEN = 12,
parameter TOTAL_RAM_WORD_MINUS_ONE = 4095,
parameter DELAY_CNTR_LEN = 8,
parameter DELAY_TOTAL = 12,
parameter RAM_WORD_INCR = 2
) (
input clk,
/* autoapproach interface */
output [WORD_WID-1:0] word,
input word_next,
output word_last,
output word_ok,
input word_rst,
/* User interface */
input refresh_start,
input [RAM_WID-1:0] start_addr,
output refresh_finished,
input[RAM_WORD_WID-1:0] backing_store [TOTAL_RAM_WORD_MINUS_ONE:0]
);
wire [RAM_WID-1:0] ram_dma_addr;
wire [RAM_WORD_WID-1:0] ram_word;
wire ram_read;
wire ram_valid;
dma_sim #(
.RAM_WID(RAM_WID),
.RAM_WORD_WID(RAM_WORD_WID),
.RAM_REAL_START(RAM_REAL_START),
.RAM_CNTR_LEN(RAM_CNTR_LEN),
.TOTAL_RAM_WORD_MINUS_ONE(TOTAL_RAM_WORD_MINUS_ONE),
.DELAY_CNTR_LEN(DELAY_CNTR_LEN),
.DELAY_TOTAL(DELAY_TOTAL)
) dma_sim (
.clk(clk),
.ram_dma_addr(ram_dma_addr),
.ram_word(ram_word),
.ram_read(ram_read),
.ram_valid(ram_valid),
.backing_store(backing_store)
);
bram_interface #(
.WORD_WID(WORD_WID),
.WORD_AMNT_WID(WORD_AMNT_WID),
.WORD_AMNT(WORD_AMNT),
.RAM_WID(RAM_WID),
.RAM_WORD_WID(RAM_WORD_WID),
.RAM_WORD_INCR(RAM_WORD_INCR)
) bram_interface (
.clk(clk),
.word(word),
.word_next(word_next),
.word_last(word_last),
.word_ok(word_ok),
.word_rst(word_rst),
.refresh_start(refresh_start),
.start_addr(start_addr),
.refresh_finished(refresh_finished),
.ram_dma_addr(ram_dma_addr),
.ram_word(ram_word),
.ram_read(ram_read),
.ram_valid(ram_valid)
);
initial begin
$dumpfile("bram.fst");
$dumpvars();
end
endmodule

View File

@ -0,0 +1,44 @@
/* This module is used to simulate direct memory access, where only
* a small amount of memory is valid to read.
*/
module dma_sim #(
parameter RAM_WID = 32,
parameter RAM_WORD_WID = 16,
parameter RAM_REAL_START = 32'h12340,
parameter RAM_CNTR_LEN = 12,
parameter TOTAL_RAM_WORD_MINUS_ONE = 4095,
parameter DELAY_CNTR_LEN = 8,
parameter DELAY_TOTAL = 12
) (
input clk,
/* DMA interface */
input [RAM_WID-1:0] ram_dma_addr,
output reg [RAM_WORD_WID-1:0] ram_word,
input ram_read,
output reg ram_valid,
/*- Verilator interface */
input [RAM_WORD_WID-1:0] backing_store[TOTAL_RAM_WORD_MINUS_ONE:0]
);
reg [DELAY_CNTR_LEN-1:0] delay_cntr = 0;
always @ (posedge clk) begin
if (!ram_read) begin
delay_cntr <= 0;
ram_valid <= 0;
end else if (delay_cntr < DELAY_TOTAL) begin
delay_cntr <= delay_cntr + 1;
end else if (!ram_valid) begin
if (ram_dma_addr < RAM_REAL_START || ram_dma_addr > RAM_REAL_START + 2*TOTAL_RAM_WORD_MINUS_ONE) begin
$display("ram_dma_addr %x out of bounds", ram_dma_addr);
$stop();
end else begin
ram_word <= backing_store[(RAM_CNTR_LEN)'((ram_dma_addr - RAM_REAL_START)/2)];
ram_valid <= 1;
end
end
end
endmodule

View File

@ -9,12 +9,8 @@ template <class TOP> class TB {
public: public:
TOP mod; TOP mod;
VerilatedContext vc;
TB(int argc, char *argv[], int _bailout = 0) : mod(), bailout(_bailout), vc() {
vc.commandArgs(argc, argv);
vc.traceEverOn(true);
TB(int argc, char *argv[], int _bailout = 0) : mod(), bailout(_bailout) {
mod.clk = 0; mod.clk = 0;
tick_count = 0; tick_count = 0;
} }
@ -26,14 +22,16 @@ template <class TOP> class TB {
void run_clock() { void run_clock() {
mod.clk = !mod.clk; mod.clk = !mod.clk;
mod.eval(); mod.eval();
vc.timeInc(1); Verilated::timeInc(1);
mod.clk = !mod.clk; mod.clk = !mod.clk;
mod.eval(); mod.eval();
vc.timeInc(1); Verilated::timeInc(1);
tick_count++; tick_count++;
if (bailout > 0 && tick_count >= bailout) if (bailout > 0 && tick_count >= bailout)
exit(1); exit(1);
if (Verilated::gotError())
exit(1);
} }
}; };