From 55b8324987d6253bfd70f069bff804b359e79cf5 Mon Sep 17 00:00:00 2001 From: Flavian Kaufmann Date: Sat, 4 May 2024 17:16:41 +0200 Subject: added support for multiple testbenches --- Makefile | 142 +++++++++++++++++++++++++++----------------- constraints/tangnano9k.cst | 38 ------------ cst/tangnano9k.cst | 38 ++++++++++++ gentestvec/gentestvec_alu.c | 112 ++++++++++++++++++++++++++++++++++ sim/testbench.v | 66 -------------------- sim/testbench_alu.v | 86 +++++++++++++++++++++++++++ src/register_file.v | 41 ------------- tests/alu_testvec.c | 112 ---------------------------------- 8 files changed, 324 insertions(+), 311 deletions(-) delete mode 100644 constraints/tangnano9k.cst create mode 100644 cst/tangnano9k.cst create mode 100644 gentestvec/gentestvec_alu.c delete mode 100644 sim/testbench.v create mode 100644 sim/testbench_alu.v delete mode 100644 src/register_file.v delete mode 100644 tests/alu_testvec.c diff --git a/Makefile b/Makefile index 3591fb5..e23abd1 100644 --- a/Makefile +++ b/Makefile @@ -1,58 +1,81 @@ -PROJ_NAME = riscv_cpu +PRJ_NAME = riscv_cpu TOP_MODULE = top -BUILD_DIR = build - +# Directories SRC_DIR = src -CONSTRAINTS_DIR = constraints SIM_DIR = sim -GENTESTS_DIR = tests +GENTESTVEC_DIR = gentestvec +CST_DIR = cst +BUILD_DIR = build -SOURCES = $(wildcard $(SRC_DIR)/*.v) -TESTBENCH = $(SIM_DIR)/testbench.v -CONSTRAINTS = $(CONSTRAINTS_DIR)/tangnano9k.cst +# Source Files +SRC_FILES = $(wildcard $(SRC_DIR)/*.v) +SIM_FILES = $(wildcard $(SIM_DIR)/testbench_*.v) +GENTESTVEC_FILES = $(wildcard $(GENTESTVEC_DIR)/gentestvec_*.c) +CST_FILES = $(wildcard $(CST_DIR)/*.cst) -GENTESTS_SOURCES = $(wildcard $(GENTESTS_DIR)/*.c) -GENTESTS_BINARIES = $(patsubst $(GENTESTS_DIR)/%.c,$(BUILD_DIR)/%,$(GENTESTS_SOURCES)) +# Output Files +SIM_EXECUTABLES = $(patsubst $(SIM_DIR)/testbench_%.v, $(BUILD_DIR)/testbench_%,$(SIM_FILES)) +GENTESTVEC_EXECUTABLES = $(patsubst $(GENTESTVEC_DIR)/gentestvec_%.c, $(BUILD_DIR)/gentestvec_%,$(GENTESTVEC_FILES)) +TESTVECTOR_FILES = $(patsubst $(BUILD_DIR)/gentestvec_%, $(BUILD_DIR)/testvec_%.txt, $(GENTESTVEC_EXECUTABLES)) +WAVEFORM_FILES = $(patsubst $(BUILD_DIR)/testbench_%, $(BUILD_DIR)/waveform_%.vcd, $(SIM_EXECUTABLES)) -BITSTREAM = $(BUILD_DIR)/$(PROJ_NAME).fs +BITSTREAM = $(BUILD_DIR)/$(PRJ_NAME).fs + +# Programs +CC = clang +CFLAGS = -O3 + +IVERILOG = iverilog +VVP = vvp +GTKWAVE = gtkwave YOSYS = yosys NEXTPNR = nextpnr-gowin GOWIN_PACK = gowin_pack PROGRAMMER = openFPGALoader -IVERILOG = iverilog -VVP = vvp -GTKWAVE = gtkwave -CC = clang FAMILY = GW1N-9C DEVICE = GW1NR-LV9QN88PC6/I5 BOARD = tangnano9k -all: $(BITSTREAM) +all: simulate -$(BUILD_DIR)/$(PROJ_NAME).json: $(SOURCES) - @echo "==================================================" - @echo "Synthesizing" - @echo "==================================================" - - @mkdir -p $(BUILD_DIR) - $(YOSYS) -p "synth_gowin -top $(TOP_MODULE)" -o $(BUILD_DIR)/$(PROJ_NAME).json $(SOURCES) - -$(BUILD_DIR)/$(PROJ_NAME)_pnr.json: $(BUILD_DIR)/$(PROJ_NAME).json $(CONSTRAINTS) + +$(BUILD_DIR)/$(PRJ_NAME).json: $(SRC_FILES) | $(BUILD_DIR) + @echo @echo "==================================================" - @echo "Routing" + @echo " Synthesizing" @echo "==================================================" - - $(NEXTPNR) --json $(BUILD_DIR)/$(PROJ_NAME).json --write $(BUILD_DIR)/$(PROJ_NAME)_pnr.json --device $(DEVICE) --family $(FAMILY) --cst $(CONSTRAINTS) - -$(BITSTREAM): $(BUILD_DIR)/$(PROJ_NAME)_pnr.json + $(YOSYS) -p "synth_gowin -top $(TOP_MODULE)" -o $(BUILD_DIR)/$(PRJ_NAME).json $(SRC_FILES) @echo "==================================================" - @echo "Generating Bitstream" + @echo " Completed Synthesis" @echo "==================================================" - - $(GOWIN_PACK) -d $(FAMILY) -o $(BITSTREAM) $(BUILD_DIR)/$(PROJ_NAME)_pnr.json + @echo + +$(BUILD_DIR)/$(PRJ_NAME)_pnr.json: $(BUILD_DIR)/$(PRJ_NAME).json $(CST_FILES) + @echo + @echo "===================================================" + @echo " Routing" + @echo "===================================================" + $(NEXTPNR) --json $(BUILD_DIR)/$(PRJ_NAME).json --write $(BUILD_DIR)/$(PRJ_NAME)_pnr.json --device $(DEVICE) --family $(FAMILY) --cst $(CST_FILES) + @echo "===================================================" + @echo " Completed Routing" + @echo "===================================================" + @echo + +$(BITSTREAM): $(BUILD_DIR)/$(PRJ_NAME)_pnr.json + @echo + @echo "===================================================" + @echo " Generating Bitstream" + @echo "===================================================" + $(GOWIN_PACK) -d $(FAMILY) -o $(BITSTREAM) $(BUILD_DIR)/$(PRJ_NAME)_pnr.json + @echo "===================================================" + @echo " Generated Bitstream" + @echo "===================================================" + @echo + +bitstream: $(BITSTREAM) program: $(BITSTREAM) $(PROGRAMMER) -b $(BOARD) $(BITSTREAM) @@ -60,28 +83,39 @@ program: $(BITSTREAM) flash: $(BITSTREAM) $(PROGRAMMER) -b $(BOARD) -f $(BITSTREAM) +simulate: $(WAVEFORM_FILES) + +# Build the testbench executables +$(BUILD_DIR)/testbench_%: $(SIM_DIR)/testbench_%.v $(SRC_FILES) | $(BUILD_DIR) + $(IVERILOG) -o $@ $^ + +# Build the test vector generator executables +$(BUILD_DIR)/gentestvec_%: $(GENTESTVEC_DIR)/gentestvec_%.c | $(BUILD_DIR) + $(CC) $(CFLAGS) -o $@ $< + +# Generate the test vector files +$(BUILD_DIR)/testvec_%.txt: $(BUILD_DIR)/gentestvec_% + $< > $@ + +# Run the simulation and generate the waveform files +$(BUILD_DIR)/waveform_%.vcd: $(BUILD_DIR)/testbench_% $(BUILD_DIR)/testvec_%.txt + @echo + @echo "===================================================" + @echo " Running Testbench ($*)" + @echo "===================================================" + $(VVP) $< +testvec=$(BUILD_DIR)/testvec_$*.txt +waveform=$@ + @echo "===================================================" + @echo " Completed Testbench ($*)" + @echo "===================================================" + @echo + +# Create the build directory +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +# Clean clean: rm -rf $(BUILD_DIR) -$(BUILD_DIR)/%: $(GENTESTS_DIR)/%.c - @mkdir -p $(BUILD_DIR) - $(CC) -o $@ $< - -tests: $(GENTESTS_BINARIES) - @for bin in $(GENTESTS_BINARIES); do \ - ./$$bin > $$bin.txt; \ - done - -simulate: $(BUILD_DIR)/testbench.vcd - -wave: $(BUILD_DIR)/testbench.vcd - $(GTKWAVE) $(BUILD_DIR)/testbench.vcd - -$(BUILD_DIR)/testbench: $(SOURCES) $(TESTBENCH) - @mkdir -p $(BUILD_DIR) - $(IVERILOG) -o $(BUILD_DIR)/testbench $(SOURCES) $(TESTBENCH) - -$(BUILD_DIR)/testbench.vcd: $(BUILD_DIR)/testbench tests - cd $(BUILD_DIR); $(VVP) testbench +.PHONY: all simulate bitsream program flash clean -.PHONY: all program flash simulate wave clean tests diff --git a/constraints/tangnano9k.cst b/constraints/tangnano9k.cst deleted file mode 100644 index e909e36..0000000 --- a/constraints/tangnano9k.cst +++ /dev/null @@ -1,38 +0,0 @@ -//Part Number: GW1NR-LV9QN88PC6/I5 - -IO_LOC "clk" 52; -IO_LOC "led[0]" 10; -IO_LOC "led[1]" 11; -IO_LOC "led[2]" 13; -IO_LOC "led[3]" 14; -IO_LOC "led[4]" 15; -IO_LOC "led[5]" 16; -IO_LOC "key" 3; -IO_LOC "rst" 4; - -IO_LOC "LCD_B[0]" 54; -IO_LOC "LCD_B[1]" 53; -IO_LOC "LCD_B[2]" 51; -IO_LOC "LCD_B[3]" 42; -IO_LOC "LCD_B[4]" 41; -IO_LOC "LCD_CLK" 35; -IO_LOC "LCD_DEN" 33; -IO_LOC "LCD_G[0]" 70; -IO_LOC "LCD_G[1]" 69; -IO_LOC "LCD_G[2]" 68; -IO_LOC "LCD_G[3]" 57; -IO_LOC "LCD_G[4]" 56; -IO_LOC "LCD_G[5]" 55; -IO_LOC "LCD_HYNC" 40; -IO_LOC "LCD_R[0]" 75; -IO_LOC "LCD_R[1]" 74; -IO_LOC "LCD_R[2]" 73; -IO_LOC "LCD_R[3]" 72; -IO_LOC "LCD_R[4]" 71; -IO_LOC "LCD_SYNC" 34; -IO_LOC "LCD_XR" 32; -IO_LOC "LCD_XL" 39; - - -// true LVDS pins -IO_LOC "tlvds_p" 25,26; diff --git a/cst/tangnano9k.cst b/cst/tangnano9k.cst new file mode 100644 index 0000000..e909e36 --- /dev/null +++ b/cst/tangnano9k.cst @@ -0,0 +1,38 @@ +//Part Number: GW1NR-LV9QN88PC6/I5 + +IO_LOC "clk" 52; +IO_LOC "led[0]" 10; +IO_LOC "led[1]" 11; +IO_LOC "led[2]" 13; +IO_LOC "led[3]" 14; +IO_LOC "led[4]" 15; +IO_LOC "led[5]" 16; +IO_LOC "key" 3; +IO_LOC "rst" 4; + +IO_LOC "LCD_B[0]" 54; +IO_LOC "LCD_B[1]" 53; +IO_LOC "LCD_B[2]" 51; +IO_LOC "LCD_B[3]" 42; +IO_LOC "LCD_B[4]" 41; +IO_LOC "LCD_CLK" 35; +IO_LOC "LCD_DEN" 33; +IO_LOC "LCD_G[0]" 70; +IO_LOC "LCD_G[1]" 69; +IO_LOC "LCD_G[2]" 68; +IO_LOC "LCD_G[3]" 57; +IO_LOC "LCD_G[4]" 56; +IO_LOC "LCD_G[5]" 55; +IO_LOC "LCD_HYNC" 40; +IO_LOC "LCD_R[0]" 75; +IO_LOC "LCD_R[1]" 74; +IO_LOC "LCD_R[2]" 73; +IO_LOC "LCD_R[3]" 72; +IO_LOC "LCD_R[4]" 71; +IO_LOC "LCD_SYNC" 34; +IO_LOC "LCD_XR" 32; +IO_LOC "LCD_XL" 39; + + +// true LVDS pins +IO_LOC "tlvds_p" 25,26; diff --git a/gentestvec/gentestvec_alu.c b/gentestvec/gentestvec_alu.c new file mode 100644 index 0000000..39de43e --- /dev/null +++ b/gentestvec/gentestvec_alu.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +typedef enum { + ADD = 0b0000, + SUB = 0b0001, + SLT = 0b0011, + + AND = 0b0100, + OR = 0b0101, + XOR = 0b0110, + + SLL = 0b1000, + SRL = 0b1001, + SRA = 0b1011, +} OP; + +void test_op(OP op, uint32_t a, uint32_t b) { + uint32_t result; + bool zero; + + switch (op) { + case ADD: + result = a + b; + break; + case SUB: + result = a - b; + break; + case SLT: + result = (int32_t)a < (int32_t)b; + break; + + case AND: + result = a & b; + break; + case OR: + result = a | b; + break; + case XOR: + result = a ^ b; + break; + + case SLL: + result = a << b % 32; + break; + case SRL: + result = a >> b % 32; + break; + case SRA: + result = ((int32_t)a) >> b % 32; + break; + } + + zero = result == 0; + + printf("%01X__%08X_%08X__%08X_%01X\n", op & 0x0f, a, b, result, zero); +} + +void test_op_random(OP op, int num) { + for (int i = 0; i < num; ++i) { + uint32_t a = (rand() << 16) | rand(); + uint32_t b = (rand() << 16) | rand(); + test_op(op, a, b); + } +} + +int main(int argc, const char *argv[]) { + srand(time(NULL)); + + test_op_random(ADD, 1000); + test_op(ADD, 0x00000000, 0x00000000); + test_op(ADD, 0xffffffff, 0xffffffff); + test_op(ADD, 0xffffffff, 0x00000001); + test_op_random(SUB, 1000); + test_op(SUB, 0xffffffff, 0xffffffff); + test_op_random(SLT, 1000); + test_op(SLT, 0x8fffffff, 0xffffffff); + test_op(SLT, 0xffffffff, 0x00000001); + test_op(SLT, 0x00000001, 0xffffffff); + + test_op_random(OR, 1000); + test_op(OR, 0x00000000, 0x00000000); + test_op(OR, 0xffffffff, 0x00000000); + test_op(OR, 0x00000000, 0xffffffff); + test_op(OR, 0xffffffff, 0xffffffff); + test_op_random(AND, 1000); + test_op(AND, 0x00000000, 0x00000000); + test_op(AND, 0xffffffff, 0x00000000); + test_op(AND, 0x00000000, 0xffffffff); + test_op(AND, 0xffffffff, 0xffffffff); + test_op_random(XOR, 1000); + test_op(XOR, 0x00000000, 0x00000000); + test_op(XOR, 0xffffffff, 0x00000000); + test_op(XOR, 0x00000000, 0xffffffff); + test_op(XOR, 0xffffffff, 0xffffffff); + + test_op_random(SLL, 1000); + test_op(SLL, 0x0000000f, 0x00000004); + test_op(SLL, 0xffffffff, 0x0000001c); + test_op(SLL, 0xf0000000, 0x00000002); + test_op(SLL, 0x01234567, 0x00000001); + test_op_random(SRL, 1000); + test_op(SRL, 0xf0000000, 0x0000001c); + test_op(SRL, 0x0000000f, 0x0000004); + test_op_random(SRA, 1000); + test_op(SRA, 0xf0000000, 0x0000001c); + test_op(SRA, 0x0000000f, 0x0000004); + + return 0; +} diff --git a/sim/testbench.v b/sim/testbench.v deleted file mode 100644 index 2d485ea..0000000 --- a/sim/testbench.v +++ /dev/null @@ -1,66 +0,0 @@ -`timescale 1ns / 1ps - -module testbench(); - - reg reset = 0; - - initial begin - $dumpfile("testbench.vcd"); - $dumpvars(0,testbench); - end - - reg clk = 0; - always #32 clk = !clk; - - - reg [31:0] a, b, exp_result; - reg [3:0] op; - reg [3:0] exp_flags; - wire [31:0] result; - wire zero, exp_zero; - - assign exp_zero = exp_flags[0]; - - reg [31:0] alu_test_count, alu_error_count; - reg [103:0] alu_testvec [0:9999]; - - initial begin - $readmemh("alu_testvec.txt", alu_testvec); - alu_test_count = 0; - alu_error_count = 0; - end - - always @ (posedge clk) begin - #16; - {op, a, b, exp_result, exp_flags} = alu_testvec[alu_test_count]; - #32; - if ((result !== exp_result) | (zero !== exp_zero)) begin - $display("ERROR (ALU) time: %5d, test: %d", $time, alu_test_count); - $display(" op: %b, a: %h b: %h", op, a, b); - $display(" result: %h (expected %h)", result, exp_result); - $display(" zero: %b (expected %b)", zero, exp_zero); - alu_error_count = alu_error_count + 1; - end - - alu_test_count = alu_test_count + 1; - - if ((alu_test_count == 9027)) begin - $display("FINISHED (ALU), %d tests completed with %d errors", alu_test_count, alu_error_count); - #16; - - $finish; - end - end - - - - - alu #(.N(32)) alu ( - .A(a), - .B(b), - .OP(op), - .RESULT(result), - .ZERO(zero) - ); - -endmodule diff --git a/sim/testbench_alu.v b/sim/testbench_alu.v new file mode 100644 index 0000000..7d67d5c --- /dev/null +++ b/sim/testbench_alu.v @@ -0,0 +1,86 @@ +`timescale 1ns / 1ps + +module testbench_alu(); + + reg reset = 0; + + reg [1023:0] testvec_filename; + reg [1023:0] waveform_filename; + + initial begin + if ($value$plusargs("testvec=%s", testvec_filename)) begin + $display("Using test vector file: %s", testvec_filename); + end else begin + $display("Error: no test vector file specified."); + $finish; + end + + if ($value$plusargs("waveform=%s", waveform_filename)) begin + $display("Using waveform file: %s", waveform_filename); + end else begin + $display("Error: no waveform file specified."); + $finish; + end + end + + initial begin + $dumpfile(waveform_filename); + $dumpvars(0,testbench_alu); + end + + reg clk = 0; + always #32 clk = !clk; + + + reg [31:0] a, b, exp_result; + reg [3:0] op; + reg [3:0] exp_flags; + wire [31:0] result; + wire zero, exp_zero; + + assign exp_zero = exp_flags[0]; + + reg [31:0] alu_test_count, alu_error_count; + reg [103:0] alu_testvec [0:9999]; + + initial begin + #5; + $readmemh(testvec_filename, alu_testvec); + alu_test_count = 0; + alu_error_count = 0; + end + + always @ (posedge clk) begin + #16; + {op, a, b, exp_result, exp_flags} = alu_testvec[alu_test_count]; + #32; + if ((result !== exp_result) | (zero !== exp_zero)) begin + $display("ERROR (ALU) time: %5d, test: %d", $time, alu_test_count); + $display(" op: %b, a: %h b: %h", op, a, b); + $display(" result: %h (expected %h)", result, exp_result); + $display(" zero: %b (expected %b)", zero, exp_zero); + alu_error_count = alu_error_count + 1; + end + + alu_test_count = alu_test_count + 1; + + if ((alu_test_count == 9027)) begin + $display("FINISHED (ALU), %d tests completed with %d errors", alu_test_count, alu_error_count); + #16; + + $finish; + end + end + + + + + alu #(.N(32)) alu ( + .A(a), + .B(b), + .OP(op), + .RESULT(result), + .ZERO(zero) + ); + +endmodule diff --git a/src/register_file.v b/src/register_file.v deleted file mode 100644 index 4f09773..0000000 --- a/src/register_file.v +++ /dev/null @@ -1,41 +0,0 @@ -module register_file #( - parameter N = 32, - parameter XLEN = 32 -)( - input [CLOG2(XLEN)-1:0] A1, A2, A3, - input CLK, - input RST, - input WE3, - input [N-1:0] WD3, - output [N-1:0] RD1, RD2 -); - - reg [N-1:0] registers[XLEN-1:0]; - - assign RD1 = A1 == 0 ? 0 : registers[A1]; - assign RD2 = A2 == 0 ? 0 : registers[A2]; - - always @ (posedge CLK or negedge RST) begin - if (!RST) begin - integer i; - for (i = 0; i < XLEN; i = i + 1) begin - registers[i] <= 0; - end - end else if (WE3 && (A3 != 0)) begin - registers[A3] <= WD3; - end - end - - - - function integer CLOG2(input integer value); - integer i; - begin - value = value - 1; - for (i = 0; value > 0; i = i + 1) - value = value >> 1; - CLOG2 = i; - end - endfunction - -endmodule diff --git a/tests/alu_testvec.c b/tests/alu_testvec.c deleted file mode 100644 index 39de43e..0000000 --- a/tests/alu_testvec.c +++ /dev/null @@ -1,112 +0,0 @@ -#include -#include -#include -#include - -typedef enum { - ADD = 0b0000, - SUB = 0b0001, - SLT = 0b0011, - - AND = 0b0100, - OR = 0b0101, - XOR = 0b0110, - - SLL = 0b1000, - SRL = 0b1001, - SRA = 0b1011, -} OP; - -void test_op(OP op, uint32_t a, uint32_t b) { - uint32_t result; - bool zero; - - switch (op) { - case ADD: - result = a + b; - break; - case SUB: - result = a - b; - break; - case SLT: - result = (int32_t)a < (int32_t)b; - break; - - case AND: - result = a & b; - break; - case OR: - result = a | b; - break; - case XOR: - result = a ^ b; - break; - - case SLL: - result = a << b % 32; - break; - case SRL: - result = a >> b % 32; - break; - case SRA: - result = ((int32_t)a) >> b % 32; - break; - } - - zero = result == 0; - - printf("%01X__%08X_%08X__%08X_%01X\n", op & 0x0f, a, b, result, zero); -} - -void test_op_random(OP op, int num) { - for (int i = 0; i < num; ++i) { - uint32_t a = (rand() << 16) | rand(); - uint32_t b = (rand() << 16) | rand(); - test_op(op, a, b); - } -} - -int main(int argc, const char *argv[]) { - srand(time(NULL)); - - test_op_random(ADD, 1000); - test_op(ADD, 0x00000000, 0x00000000); - test_op(ADD, 0xffffffff, 0xffffffff); - test_op(ADD, 0xffffffff, 0x00000001); - test_op_random(SUB, 1000); - test_op(SUB, 0xffffffff, 0xffffffff); - test_op_random(SLT, 1000); - test_op(SLT, 0x8fffffff, 0xffffffff); - test_op(SLT, 0xffffffff, 0x00000001); - test_op(SLT, 0x00000001, 0xffffffff); - - test_op_random(OR, 1000); - test_op(OR, 0x00000000, 0x00000000); - test_op(OR, 0xffffffff, 0x00000000); - test_op(OR, 0x00000000, 0xffffffff); - test_op(OR, 0xffffffff, 0xffffffff); - test_op_random(AND, 1000); - test_op(AND, 0x00000000, 0x00000000); - test_op(AND, 0xffffffff, 0x00000000); - test_op(AND, 0x00000000, 0xffffffff); - test_op(AND, 0xffffffff, 0xffffffff); - test_op_random(XOR, 1000); - test_op(XOR, 0x00000000, 0x00000000); - test_op(XOR, 0xffffffff, 0x00000000); - test_op(XOR, 0x00000000, 0xffffffff); - test_op(XOR, 0xffffffff, 0xffffffff); - - test_op_random(SLL, 1000); - test_op(SLL, 0x0000000f, 0x00000004); - test_op(SLL, 0xffffffff, 0x0000001c); - test_op(SLL, 0xf0000000, 0x00000002); - test_op(SLL, 0x01234567, 0x00000001); - test_op_random(SRL, 1000); - test_op(SRL, 0xf0000000, 0x0000001c); - test_op(SRL, 0x0000000f, 0x0000004); - test_op_random(SRA, 1000); - test_op(SRA, 0xf0000000, 0x0000001c); - test_op(SRA, 0x0000000f, 0x0000004); - - return 0; -} -- cgit v1.2.3