From 034f76da4120449f686a8035500176df414ffd6c Mon Sep 17 00:00:00 2001 From: Peter McGoron Date: Mon, 23 Jan 2023 04:43:51 +0000 Subject: [PATCH] autoapproach: add bram and test --- firmware/rtl/autoapproach/Makefile | 19 +++ firmware/rtl/autoapproach/autoapproach.v | 28 +++-- firmware/rtl/autoapproach/bram_interface.v | 112 ++++++++++++++++++ .../rtl/autoapproach/bram_interface_sim.cpp | 112 ++++++++++++++++++ firmware/rtl/boilerplate.cpp | 76 ++++++++++++ 5 files changed, 334 insertions(+), 13 deletions(-) create mode 100644 firmware/rtl/autoapproach/Makefile create mode 100644 firmware/rtl/autoapproach/bram_interface.v create mode 100644 firmware/rtl/autoapproach/bram_interface_sim.cpp create mode 100644 firmware/rtl/boilerplate.cpp diff --git a/firmware/rtl/autoapproach/Makefile b/firmware/rtl/autoapproach/Makefile new file mode 100644 index 0000000..6ed7edd --- /dev/null +++ b/firmware/rtl/autoapproach/Makefile @@ -0,0 +1,19 @@ +# Makefile for tests and hardware verification. + +.PHONY: test clean + +test: obj_dir/Vbram_interface + +bram_SRC= bram_interface.v bram_interface_sim.cpp + +obj_dir/Vbram_interface.mk: $(bram_SRC) + verilator --cc --exe -Wall --trace --trace-fst \ + -CFLAGS -DWORD_AMNT=2048 \ + -CFLAGS -DRAM_WID=32 \ + $(bram_SRC) +obj_dir/Vbram_interface: obj_dir/Vbram_interface.mk + cd obj_dir && make -f Vbram_interface.mk + ./obj_dir/Vbram_interface + +clean: + rm -rf obj_dir/ diff --git a/firmware/rtl/autoapproach/autoapproach.v b/firmware/rtl/autoapproach/autoapproach.v index 30297ae..d06205d 100644 --- a/firmware/rtl/autoapproach/autoapproach.v +++ b/firmware/rtl/autoapproach/autoapproach.v @@ -1,9 +1,12 @@ /* Autoapproach module. This module applies a waveform located in memory * (and copied into Block RAM). This waveform is arbitrary but of fixed * length. + * time in between sent sample, total period 10-50ms */ module autoapproach #( - parameter DAC_WID = 24 + parameter DAC_WID = 24, + parameter DAC_DATA_WID = 20, + parameter ADC_WID = 24 ) ( input clk, input arm, @@ -11,13 +14,13 @@ module autoapproach #( output detected, input polarity, - input [`ADC_WID-1:0] setpoint, + input [ADC_WID-1:0] setpoint, /* BRAM memory interface. Each pulse returns the next value in * the sequence, and also informs the module if the sequence * is completed. The kernel interacts primarily with this interface. */ - input [`DAC_DATA_WID-1:0] word, + input [DAC_DATA_WID-1:0] word, output word_next, input word_last, input word_ok, @@ -26,12 +29,12 @@ module autoapproach #( /* DAC wires. */ input dac_finished, output dac_arm, - input [`DAC_WID-1:0] dac_in, - output [`DAC_WID-1:0] dac_out, + input [DAC_WID-1:0] dac_in, + output [DAC_WID-1:0] dac_out, input adc_finished, output adc_arm, - input [`ADC_WID-1:0] measurement + input [ADC_WID-1:0] measurement ); @@ -41,7 +44,6 @@ localparam WAIT_ON_DAC = 2; localparam WAIT_ON_DETECTION = 3; localparam DETECTED = 4; reg [2:0] state = WAIT_ON_ARM; -reg save_word_last = 0; always @ (posedge clk) case (state) WAIT_ON_ARM: if (arm) begin @@ -55,25 +57,25 @@ end RECV_WORD: if (word_ok) begin dac_out <= {4'b0001, word}; dac_arm <= 1; - save_word_last <= word_last; + word_next <= 0; state <= WAIT_ON_DAC; end WAIT_ON_DAC: if (dac_finished) begin dac_arm <= 0; - if (save_word_last) begin + /* Was the last word read *the* last word? */ + if (word_last) begin state <= WAIT_ON_DETECTION; - adc_arm <= 0; + adc_arm <= 1; end else begin state <= WAIT_ON_ARM; end endcase WAIT_ON_DETECTION: if (adc_finished) begin - if (polarity && measurement >= setpt) begin + if ((polarity && measurement >= setpt) || + (!polarity && measurement <= setpt)) begin state <= DETECTED; detected <= 1; - end else if (measurement <= setpt) begin - state <= WAIT_ON_ARM; end end DETECTED: if (!arm) begin diff --git a/firmware/rtl/autoapproach/bram_interface.v b/firmware/rtl/autoapproach/bram_interface.v new file mode 100644 index 0000000..a59a6c5 --- /dev/null +++ b/firmware/rtl/autoapproach/bram_interface.v @@ -0,0 +1,112 @@ +module bram_interface #( + parameter WORD_WID = 24, + parameter WORD_AMNT_WID = 11, + /* This is the last INDEX, not the LENGTH of the word array. */ + parameter [WORD_AMNT_WID-1:0] WORD_AMNT = 2047, + parameter RAM_WID = 32, + parameter RAM_WORD_WID = 16, + parameter RAM_WORD_INCR = 2 +) ( + input clk, + + /* autoapproach interface */ + output reg [WORD_WID-1:0] word, + input word_next, + output reg word_last, + output reg word_ok, + input word_rst, + + /* User interface */ + input refresh_start, + input [RAM_WID-1:0] start_addr, + output reg refresh_finished, + + /* RAM interface */ + output reg [RAM_WID-1:0] ram_dma_addr, + input [RAM_WORD_WID-1:0] ram_word, + output reg ram_read, + input ram_valid +); + +initial word = 0; +initial word_last = 0; +initial word_ok = 0; +initial refresh_finished = 0; +initial ram_dma_addr = 0; +initial ram_read = 0; + +reg [WORD_WID-1:0] backing_buffer [WORD_AMNT:0]; + +localparam WAIT_ON_REFRESH = 0; +localparam READ_LOW_WORD = 1; +localparam READ_HIGH_WORD = 2; +localparam WAIT_ON_REFRESH_DEASSERT = 3; + +reg [1:0] refresh_state = 0; +reg [WORD_AMNT_WID-1:0] word_cntr_refresh = 0; + +always @ (posedge clk) case (refresh_state) +WAIT_ON_REFRESH: if (refresh_start) begin + ram_dma_addr <= start_addr; + refresh_state <= READ_LOW_WORD; + word_cntr_refresh <= 0; +end +READ_LOW_WORD: if (!ram_read) begin + ram_read <= 1; +end else if (ram_valid) begin + refresh_state <= READ_HIGH_WORD; + ram_dma_addr <= ram_dma_addr + RAM_WORD_INCR; + ram_read <= 0; + backing_buffer[word_cntr_refresh][RAM_WORD_WID-1:0] <= ram_word; +end +READ_HIGH_WORD: if (!ram_read) begin + ram_read <= 1; +end else if (ram_valid) begin + ram_dma_addr <= ram_dma_addr + RAM_WORD_INCR; + ram_read <= 0; + word_cntr_refresh <= word_cntr_refresh + 1; + backing_buffer[word_cntr_refresh][WORD_WID-1:RAM_WORD_WID] <= ram_word[WORD_WID-RAM_WORD_WID-1:0]; + + if (word_cntr_refresh == WORD_AMNT) + refresh_state <= WAIT_ON_REFRESH_DEASSERT; + else + refresh_state <= READ_LOW_WORD; +end +WAIT_ON_REFRESH_DEASSERT: begin + if (!refresh_start) begin + refresh_finished <= 0; + refresh_state <= WAIT_ON_REFRESH; + end else begin + refresh_finished <= 1; + end +end +endcase + +reg [WORD_AMNT_WID-1:0] auto_cntr = 0; + +always @ (posedge clk) if (word_rst) begin + auto_cntr <= 0; +end else if (word_next && !word_ok) begin + if (refresh_state == WAIT_ON_REFRESH) begin + word <= backing_buffer[auto_cntr]; + word_ok <= 1; + if (auto_cntr == WORD_AMNT) begin + auto_cntr <= 0; + word_last <= 1; + end else begin + auto_cntr <= auto_cntr + 1; + word_last <= 0; + end + end +end else if (!word_next && word_ok) begin + word_ok <= 0; +end + +`ifdef VERILATOR +initial begin + $dumpfile("bram.fst"); + $dumpvars; +end +`endif + +endmodule diff --git a/firmware/rtl/autoapproach/bram_interface_sim.cpp b/firmware/rtl/autoapproach/bram_interface_sim.cpp new file mode 100644 index 0000000..224ec29 --- /dev/null +++ b/firmware/rtl/autoapproach/bram_interface_sim.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Vbram_interface.h" + +using ModType = Vbram_interface; +using V = uint32_t; +ModType *mod; + +constexpr uint32_t start_addr = 0x12340; + +// #define BAILOUT_NUMBER 100000 +#include "../boilerplate.cpp" + +std::array ram_refresh_data; + +static void handle_ram() { + static int timer = 0; + constexpr auto TIMER_MAX = 10; + bool flip_flop = false; + + if (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); + 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); + + if (mod->ram_dma_addr % 4 == 0) { + mod->ram_word = ram_refresh_data[(mod->ram_dma_addr - start_addr)/4]& 0xFFFF; + } else { + mod->ram_word = ram_refresh_data[(mod->ram_dma_addr - start_addr)/4] >> 16; + } + } + } else { + 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; + + 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; + } +} + +/* Test reading the entire array twice. */ +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)) { + handle_read_aa(ind); + run_clock(); + } + my_assert(ind == WORD_AMNT, "read value %zu != %d\n", ind, WORD_AMNT); + + mod->word_next = 1; + run_clock(); + ind = 0; + while (!mod->word_last || (mod->word_last && mod->word_next)) { + handle_read_aa(ind); + run_clock(); + } + my_assert(ind == WORD_AMNT, "second read value %zu != %d\n", ind, WORD_AMNT); +} + +int main(int argc, char **argv) { + init(argc, argv); + + for (size_t i = 0; i < RAM_WID; i++) { + ram_refresh_data[i] = mask_extend(rand(), 20); + } + + mod->refresh_start = 1; + mod->start_addr = start_addr; + run_clock(); + + while (!mod->refresh_finished) { + handle_ram(); + run_clock(); + } + + mod->refresh_start = 0; + run_clock(); + + test_aa_read_1(); + printf("ok\n"); + + return 0; +} diff --git a/firmware/rtl/boilerplate.cpp b/firmware/rtl/boilerplate.cpp new file mode 100644 index 0000000..4520cc5 --- /dev/null +++ b/firmware/rtl/boilerplate.cpp @@ -0,0 +1,76 @@ +#include + +uint32_t main_time = 0; + +double sc_time_stamp() { + return main_time; +} + +static 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__) + +#ifdef BAILOUT_NUMBER +# define BAILOUT(...) __VA_ARGS__ +#else +# define BAILOUT(...) +#endif + +static void run_clock() { + BAILOUT(static int bailout;) + for (int i = 0; i < 2; i++) { + mod->clk = !mod->clk; + mod->eval(); + main_time++; + BAILOUT(bailout++;) + } + BAILOUT(if (bailout >= BAILOUT_NUMBER) exit(1);) +} + +#undef BAILOUT + +static void cleanup_exit() { + mod->final(); + delete mod; +} + +static void init(int argc, char **argv) { + Verilated::commandArgs(argc, argv); + Verilated::traceEverOn(true); + mod = new ModType; + mod->clk = 0; + atexit(cleanup_exit); + + char *seed = getenv("RANDOM_SEED"); + if (seed) { + unsigned long i = strtoul(seed, NULL, 10); + srand((unsigned int)i); + } +} + +static 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)) + +static V mask_extend(V x, unsigned len) { + return sign_extend(MASK(x,len), len); +}