From 285b6d95015bbda137e46f47d70a0015c1c59903 Mon Sep 17 00:00:00 2001 From: Peter McGoron Date: Fri, 27 Jan 2023 22:27:20 +0000 Subject: [PATCH] refactor bram interface simulation --- firmware/rtl/autoapproach/autoapproach.v | 1 + .../rtl/autoapproach/bram_interface_sim.cpp | 96 +++++++++---------- firmware/rtl/testbench.hpp | 63 ++++++++++++ 3 files changed, 110 insertions(+), 50 deletions(-) create mode 100644 firmware/rtl/testbench.hpp diff --git a/firmware/rtl/autoapproach/autoapproach.v b/firmware/rtl/autoapproach/autoapproach.v index 4f848c7..e4ab970 100644 --- a/firmware/rtl/autoapproach/autoapproach.v +++ b/firmware/rtl/autoapproach/autoapproach.v @@ -64,6 +64,7 @@ DO_WAIT: if (!arm) begin end else if (wait_timer == 0) begin word_next <= 1; state <= RECV_WORD; + wait_timer <= time_to_wait; end else begin wait_timer <= wait_timer - 1; end diff --git a/firmware/rtl/autoapproach/bram_interface_sim.cpp b/firmware/rtl/autoapproach/bram_interface_sim.cpp index 13dabd6..97270f7 100644 --- a/firmware/rtl/autoapproach/bram_interface_sim.cpp +++ b/firmware/rtl/autoapproach/bram_interface_sim.cpp @@ -10,16 +10,10 @@ #include #include "Vbram_interface.h" +#include "../testbench.hpp" -using ModType = Vbram_interface; -using V = uint32_t; -ModType *mod; - +TB *tb; constexpr uint32_t start_addr = 0x12340; - -// #define BAILOUT_NUMBER 100000 -#include "../boilerplate.cpp" - std::array ram_refresh_data; static void handle_ram() { @@ -27,40 +21,40 @@ static void handle_ram() { constexpr auto TIMER_MAX = 10; bool flip_flop = false; - if (mod->ram_read) { + if (tb->mod.ram_read) { timer++; if (timer == TIMER_MAX) { - mod->ram_valid = 1; - if (mod->ram_dma_addr < start_addr || - mod->ram_dma_addr >= start_addr + WORD_AMNT*4) { - printf("bad address %x\n", mod->ram_dma_addr); + 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(mod->ram_dma_addr >= start_addr, "left oob access %x", mod->ram_dma_addr); - my_assert(mod->ram_dma_addr < start_addr + WORD_AMNT*4, "right oob access %x", mod->ram_dma_addr); - my_assert(mod->ram_dma_addr % 2 == 0, "unaligned access %x", mod->ram_dma_addr); + 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 (mod->ram_dma_addr % 4 == 0) { - mod->ram_word = ram_refresh_data[(mod->ram_dma_addr - start_addr)/4]& 0xFFFF; + 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 { - mod->ram_word = ram_refresh_data[(mod->ram_dma_addr - start_addr)/4] >> 16; + tb->mod.ram_word = ram_refresh_data[(tb->mod.ram_dma_addr - start_addr)/4] >> 16; } } } else { - mod->ram_valid = 0; + tb->mod.ram_valid = 0; timer = 0; } } static void handle_read_aa(size_t &i) { - if (mod->word_ok) { - uint32_t val = sign_extend(mod->word, 20); - mod->word_next = 0; + if (tb->mod.word_ok) { + uint32_t val = sign_extend(tb->mod.word, 20); + tb->mod.word_next = 0; my_assert(val == ram_refresh_data[i], "received value %x (%zu) != %x", i, val, ram_refresh_data[i]); i++; - } else if (!mod->word_next) { - mod->word_next = 1; + } else if (!tb->mod.word_next) { + tb->mod.word_next = 1; } } @@ -68,20 +62,20 @@ static void handle_read_aa(size_t &i) { static void test_aa_read_1() { size_t ind = 0; - mod->word_next = 1; - run_clock(); - while (!mod->word_last || (mod->word_last && mod->word_next)) { + tb->mod.word_next = 1; + tb->run_clock(); + while (!tb->mod.word_last || (tb->mod.word_last && tb->mod.word_next)) { handle_read_aa(ind); - run_clock(); + tb->run_clock(); } my_assert(ind == WORD_AMNT, "read value %zu != %d\n", ind, WORD_AMNT); - mod->word_next = 1; - run_clock(); + tb->mod.word_next = 1; + tb->run_clock(); ind = 0; - while (!mod->word_last || (mod->word_last && mod->word_next)) { + while (!tb->mod.word_last || (tb->mod.word_last && tb->mod.word_next)) { handle_read_aa(ind); - run_clock(); + tb->run_clock(); } my_assert(ind == WORD_AMNT, "second read value %zu != %d\n", ind, WORD_AMNT); } @@ -89,17 +83,17 @@ static void test_aa_read_1() { static void test_aa_read_interrupted() { size_t ind = 0; - mod->word_next = 1; - run_clock(); + tb->mod.word_next = 1; + tb->run_clock(); for (int i = 0; i < 100; i++) { handle_read_aa(ind); - run_clock(); - my_assert(!mod->word_last, "too many reads"); + tb->run_clock(); + my_assert(!tb->mod.word_last, "too many reads"); } - mod->word_rst = 1; - run_clock(); - mod->word_rst = 0; - run_clock(); + tb->mod.word_rst = 1; + tb->run_clock(); + tb->mod.word_rst = 0; + tb->run_clock(); test_aa_read_1(); } @@ -109,22 +103,24 @@ static void refresh_data() { ram_refresh_data[i] = mask_extend(rand(), 20); } - mod->refresh_start = 1; - mod->start_addr = start_addr; - run_clock(); + tb->mod.refresh_start = 1; + tb->mod.start_addr = start_addr; + tb->run_clock(); - while (!mod->refresh_finished) { + while (!tb->mod.refresh_finished) { handle_ram(); - run_clock(); + tb->run_clock(); } - mod->refresh_start = 0; - run_clock(); + tb->mod.refresh_start = 0; + tb->run_clock(); } -int main(int argc, char **argv) { - init(argc, argv); +int main(int argc, char *argv[]) { + Verilated::commandArgs(argc, argv); + Verilated::traceEverOn(true); + tb = new TB(); printf("test basic read/write\n"); refresh_data(); diff --git a/firmware/rtl/testbench.hpp b/firmware/rtl/testbench.hpp new file mode 100644 index 0000000..17b2ed9 --- /dev/null +++ b/firmware/rtl/testbench.hpp @@ -0,0 +1,63 @@ +#pragma once +#include + +/* https://zipcpu.com/blog/2017/06/21/looking-at-verilator.html */ +template class TB { + int tick_count; + int bailout; + + public: TOP mod; + TB(int _bailout = 0) : mod(), bailout(_bailout) { + mod.clk = 0; + tick_count = 0; + } + + virtual ~TB() { + mod.final(); + } + + virtual void run_clock() { + mod.clk = !mod.clk; + mod.eval(); + Verilated::timeInc(1); + mod.clk = !mod.clk; + mod.eval(); + Verilated::timeInc(1); + tick_count++; + + if (bailout > 0 && tick_count >= bailout) + exit(1); + } +}; + +static inline void _assert(const char *file, int line, const char *exp, bool ev, const char *fmt, ...) { + if (!ev) { + va_list va; + va_start(va, fmt); + fprintf(stderr, "%s:%d: assertion failed: %s\n", file, line, exp); + vfprintf(stderr, fmt, va); + fprintf(stderr, "\n"); + va_end(va); + exit(1); + } +} +#define STRINGIFY(s) #s +/* ,##__VA_ARGS__ is a GNU C extension */ +#define my_assert(e, fmt, ...) _assert(__FILE__, __LINE__, STRINGIFY(e), (e), fmt ,##__VA_ARGS__) + +template +static inline V sign_extend(V x, unsigned len) { + /* if high bit is 1 */ + if (x >> (len - 1) & 1) { + V mask = (1 << len) - 1; + return ~mask | x; + } else { + return x; + } +} + +#define MASK(x,v) ((x) & ((1 << (v)) - 1)) +template +static inline V mask_extend(V x, unsigned len) { + return sign_extend(MASK(x,len), len); +}