From 3291d86ec38031e191ec1e7e5e8ddfa74b77cb7c Mon Sep 17 00:00:00 2001 From: Yann Herklotz Date: Thu, 24 Nov 2022 18:49:44 +0000 Subject: Squashed 'picorv32/' content from commit f00a88c git-subtree-dir: picorv32 git-subtree-split: f00a88c36eaab478b64ee27d8162e421049bcc66 --- scripts/torture/.gitignore | 14 +++ scripts/torture/Makefile | 101 +++++++++++++++++++++ scripts/torture/README | 6 ++ scripts/torture/asmcheck.py | 36 ++++++++ scripts/torture/config.py | 35 +++++++ scripts/torture/riscv-isa-sim-notrap.diff | 16 ++++ scripts/torture/riscv-isa-sim-sbreak.diff | 26 ++++++ scripts/torture/riscv-torture-genloop.diff | 40 ++++++++ scripts/torture/riscv-torture-rv32.diff | 141 +++++++++++++++++++++++++++++ scripts/torture/riscv_test.h | 13 +++ scripts/torture/sections.lds | 9 ++ scripts/torture/test.sh | 32 +++++++ scripts/torture/testbench.cc | 18 ++++ scripts/torture/testbench.v | 134 +++++++++++++++++++++++++++ 14 files changed, 621 insertions(+) create mode 100644 scripts/torture/.gitignore create mode 100644 scripts/torture/Makefile create mode 100644 scripts/torture/README create mode 100644 scripts/torture/asmcheck.py create mode 100644 scripts/torture/config.py create mode 100644 scripts/torture/riscv-isa-sim-notrap.diff create mode 100644 scripts/torture/riscv-isa-sim-sbreak.diff create mode 100644 scripts/torture/riscv-torture-genloop.diff create mode 100644 scripts/torture/riscv-torture-rv32.diff create mode 100644 scripts/torture/riscv_test.h create mode 100644 scripts/torture/sections.lds create mode 100644 scripts/torture/test.sh create mode 100644 scripts/torture/testbench.cc create mode 100644 scripts/torture/testbench.v (limited to 'scripts/torture') diff --git a/scripts/torture/.gitignore b/scripts/torture/.gitignore new file mode 100644 index 0000000..b58f70b --- /dev/null +++ b/scripts/torture/.gitignore @@ -0,0 +1,14 @@ +.looplog +riscv-fesvr +riscv-isa-sim +riscv-torture +config.vh +obj_dir +tests +test.S +test.elf +test.bin +test.hex +test.ref +test.vvp +test.vcd diff --git a/scripts/torture/Makefile b/scripts/torture/Makefile new file mode 100644 index 0000000..4dd075c --- /dev/null +++ b/scripts/torture/Makefile @@ -0,0 +1,101 @@ + +# Icarus Verilog +#TESTBENCH_EXE = tests/testbench.vvp + +# Verilator +TESTBENCH_EXE = obj_dir/Vtestbench + +test: riscv-torture/build.ok riscv-isa-sim/build.ok + bash test.sh + +riscv-torture/build.ok: riscv-torture-rv32.diff + rm -rf riscv-torture + git clone https://github.com/ucb-bar/riscv-torture.git riscv-torture + cd riscv-torture && git checkout 2bc0c42 + cd riscv-torture && patch -p1 < ../riscv-torture-rv32.diff + cd riscv-torture && patch -p1 < ../riscv-torture-genloop.diff + cd riscv-torture && ./sbt generator/run && touch build.ok + +riscv-fesvr/build.ok: + rm -rf riscv-fesvr + git clone https://github.com/riscv/riscv-fesvr.git riscv-fesvr + +cd riscv-fesvr && git checkout 1c02bd6 && ./configure && make && touch build.ok + +riscv-isa-sim/build.ok: riscv-fesvr/build.ok + rm -rf riscv-isa-sim + git clone https://github.com/riscv/riscv-isa-sim.git riscv-isa-sim + cd riscv-isa-sim && git checkout 10ae74e + cd riscv-isa-sim && patch -p1 < ../riscv-isa-sim-sbreak.diff + cd riscv-isa-sim && patch -p1 < ../riscv-isa-sim-notrap.diff + cd riscv-isa-sim && LDFLAGS="-L../riscv-fesvr" ./configure --with-isa=RV32IMC + +cd riscv-isa-sim && ln -s ../riscv-fesvr/fesvr . && make && touch build.ok + +batch_size = 1000 +batch_list = $(shell bash -c 'for i in {0..$(shell expr $(batch_size) - 1)}; do printf "%03d\n" $$i; done') + +batch: $(addprefix tests/test_,$(addsuffix .ok,$(batch_list))) + +config.vh: config.py riscv-torture/build.ok + python3 config.py + +obj_dir/Vtestbench: testbench.v testbench.cc ../../picorv32.v config.vh + verilator --exe -Wno-fatal -DDEBUGASM --cc --top-module testbench testbench.v ../../picorv32.v testbench.cc + $(MAKE) -C obj_dir -f Vtestbench.mk + +tests/testbench.vvp: testbench.v ../../picorv32.v + mkdir -p tests + iverilog -o tests/testbench.vvp testbench.v ../../picorv32.v + +tests/generated.ok: config.vh riscv-torture/build.ok + mkdir -p tests + rm -f riscv-torture/output/test_* + cd riscv-torture && ./sbt 'generator/run -C config/test.config -n $(batch_size)' + touch tests/generated.ok + +define test_template +tests/test_$(1).S: tests/generated.ok + mv riscv-torture/output/test_$(1).S tests/ + touch tests/test_$(1).S + +tests/test_$(1).elf: tests/test_$(1).S + riscv32-unknown-elf-gcc `sed '/march=/ ! d; s,^// ,-,; y/RVIMC/rvimc/;' config.vh` -ffreestanding -nostdlib \ + -Wl,-Bstatic,-T,sections.lds -I. -o tests/test_$(1).elf tests/test_$(1).S + +tests/test_$(1).bin: tests/test_$(1).elf + riscv32-unknown-elf-objcopy -O binary tests/test_$(1).elf tests/test_$(1).bin + +tests/test_$(1).dmp: tests/test_$(1).elf + riscv32-unknown-elf-objdump -d tests/test_$(1).elf > tests/test_$(1).dmp + +tests/test_$(1).hex: tests/test_$(1).bin + python3 ../../firmware/makehex.py tests/test_$(1).bin 4096 > tests/test_$(1).hex + +tests/test_$(1).ref: tests/test_$(1).elf riscv-isa-sim/build.ok + LD_LIBRARY_PATH="./riscv-isa-sim:./riscv-fesvr" ./riscv-isa-sim/spike tests/test_$(1).elf > tests/test_$(1).ref + +tests/test_$(1).ok: $(TESTBENCH_EXE) tests/test_$(1).hex tests/test_$(1).ref tests/test_$(1).dmp + $(TESTBENCH_EXE) +hex=tests/test_$(1).hex +ref=tests/test_$(1).ref > tests/test_$(1).out + grep -q PASSED tests/test_$(1).out || { cat tests/test_$(1).out; false; } + python3 asmcheck.py tests/test_$(1).out tests/test_$(1).dmp + mv tests/test_$(1).out tests/test_$(1).ok +endef + +$(foreach id,$(batch_list),$(eval $(call test_template,$(id)))) + +loop: + date +"%s %Y-%m-%d %H:%M:%S START" >> .looplog + +set -ex; while true; do \ + rm -rf tests obj_dir config.vh; $(MAKE) batch; \ + date +"%s %Y-%m-%d %H:%M:%S NEXT" >> .looplog; \ + done + +clean: + rm -rf tests obj_dir + rm -f config.vh test.S test.elf test.bin + rm -f test.hex test.ref test.vvp test.vcd + +mrproper: clean + rm -rf riscv-torture riscv-fesvr riscv-isa-sim + +.PHONY: test batch loop clean mrproper + diff --git a/scripts/torture/README b/scripts/torture/README new file mode 100644 index 0000000..d62671d --- /dev/null +++ b/scripts/torture/README @@ -0,0 +1,6 @@ +Use UCB-BAR's RISC-V Torture Test Generator to test PicoRV32. + +You might need to install the following addition dependecies: + +sudo apt-get install python3-pip +pip3 install numpy diff --git a/scripts/torture/asmcheck.py b/scripts/torture/asmcheck.py new file mode 100644 index 0000000..8a22c67 --- /dev/null +++ b/scripts/torture/asmcheck.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +import sys, re + +dmp_pattern = re.compile('^\s+([0-9a-f]+):\s+([0-9a-f]+)\s+(\S+)') +disassembled_elf = dict() + +def match_insns(s1, s2): + if s1 == "*" or s1 == s2: return True + if s1 == "jal" and s2.startswith("j"): return True + if s1 == "addi" and s2 in ["li", "mv"]: return True + if s1 == "xori" and s2 == "not": return True + if s1 == "sub" and s2 == "neg": return True + if s1.startswith("b") and s2.startswith("b"): return True + if s1.startswith("s") and s2.startswith("s"): return True + print(s1, s2) + return False + +with open(sys.argv[2], "r") as f: + for line in f: + match = dmp_pattern.match(line) + if match: + addr = match.group(1).rjust(8, '0') + opcode = match.group(2).rjust(8, '0') + insn = match.group(3) + disassembled_elf[addr] = (opcode, insn) + +with open(sys.argv[1], "r") as f: + for line in f: + line = line.split() + if len(line) < 1 or line[0] != "debugasm": + continue + assert(line[1] in disassembled_elf) + assert(line[2] == disassembled_elf[line[1]][0]) + assert(match_insns(line[3], disassembled_elf[line[1]][1])) + diff --git a/scripts/torture/config.py b/scripts/torture/config.py new file mode 100644 index 0000000..478f046 --- /dev/null +++ b/scripts/torture/config.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +import numpy as np + +compressed_isa = np.random.randint(2) +enable_mul = np.random.randint(2) +enable_div = np.random.randint(2) + +with open("config.vh", "w") as f: + print("// march=RV32I%s%s" % ( + "M" if enable_mul or enable_div else "", + "C" if compressed_isa else ""), file=f) + print(".ENABLE_COUNTERS(%d)," % np.random.randint(2), file=f) + print(".ENABLE_COUNTERS64(%d)," % np.random.randint(2), file=f) + print(".ENABLE_REGS_DUALPORT(%d)," % np.random.randint(2), file=f) + print(".TWO_STAGE_SHIFT(%d)," % np.random.randint(2), file=f) + print(".BARREL_SHIFTER(%d)," % np.random.randint(2), file=f) + print(".TWO_CYCLE_COMPARE(%d)," % np.random.randint(2), file=f) + print(".TWO_CYCLE_ALU(%d)," % np.random.randint(2), file=f) + print(".CATCH_MISALIGN(%d)," % np.random.randint(2), file=f) + print(".CATCH_ILLINSN(%d)," % np.random.randint(2), file=f) + print(".COMPRESSED_ISA(%d)," % compressed_isa, file=f) + print(".ENABLE_MUL(%d)," % enable_mul, file=f) + print(".ENABLE_DIV(%d)" % enable_div, file=f) + +with open("riscv-torture/config/default.config", "r") as fi: + with open("riscv-torture/config/test.config", "w") as fo: + for line in fi: + line = line.strip() + if line.startswith("torture.generator.mul "): + line = "torture.generator.mul %s" % ("true" if enable_mul else "false") + if line.startswith("torture.generator.divider "): + line = "torture.generator.divider %s" % ("true" if enable_div else "false") + print(line, file=fo) + diff --git a/scripts/torture/riscv-isa-sim-notrap.diff b/scripts/torture/riscv-isa-sim-notrap.diff new file mode 100644 index 0000000..df7c059 --- /dev/null +++ b/scripts/torture/riscv-isa-sim-notrap.diff @@ -0,0 +1,16 @@ +diff --git a/riscv/processor.cc b/riscv/processor.cc +index 3b834c5..e112029 100644 +--- a/riscv/processor.cc ++++ b/riscv/processor.cc +@@ -201,9 +201,10 @@ void processor_t::set_privilege(reg_t prv) + + void processor_t::take_trap(trap_t& t, reg_t epc) + { +- if (debug) ++ // if (debug) + fprintf(stderr, "core %3d: exception %s, epc 0x%016" PRIx64 "\n", + id, t.name(), epc); ++ exit(1); + + // by default, trap to M-mode, unless delegated to S-mode + reg_t bit = t.cause(); diff --git a/scripts/torture/riscv-isa-sim-sbreak.diff b/scripts/torture/riscv-isa-sim-sbreak.diff new file mode 100644 index 0000000..9728fc8 --- /dev/null +++ b/scripts/torture/riscv-isa-sim-sbreak.diff @@ -0,0 +1,26 @@ +diff --git a/riscv/insns/c_ebreak.h b/riscv/insns/c_ebreak.h +index a17200f..af3a7ad 100644 +--- a/riscv/insns/c_ebreak.h ++++ b/riscv/insns/c_ebreak.h +@@ -1,2 +1,9 @@ + require_extension('C'); ++ ++for (int i = 0; i < 16*1024; i += 4) { ++ unsigned int dat = MMU.load_int32(i); ++ printf("%08x\n", dat); ++} ++exit(0); ++ + throw trap_breakpoint(); +diff --git a/riscv/insns/sbreak.h b/riscv/insns/sbreak.h +index c22776c..31397dd 100644 +--- a/riscv/insns/sbreak.h ++++ b/riscv/insns/sbreak.h +@@ -1 +1,7 @@ ++for (int i = 0; i < 16*1024; i += 4) { ++ unsigned int dat = MMU.load_int32(i); ++ printf("%08x\n", dat); ++} ++exit(0); ++ + throw trap_breakpoint(); diff --git a/scripts/torture/riscv-torture-genloop.diff b/scripts/torture/riscv-torture-genloop.diff new file mode 100644 index 0000000..a0a2fd1 --- /dev/null +++ b/scripts/torture/riscv-torture-genloop.diff @@ -0,0 +1,40 @@ +diff --git a/generator/src/main/scala/main.scala b/generator/src/main/scala/main.scala +index 7c78982..1572771 100644 +--- a/generator/src/main/scala/main.scala ++++ b/generator/src/main/scala/main.scala +@@ -8,7 +8,7 @@ import java.util.Properties + import scala.collection.JavaConversions._ + + case class Options(var outFileName: String = "test", +- var confFileName: String = "config/default.config") ++ var confFileName: String = "config/default.config", var numOutFiles: Int = 0) + + object Generator extends App + { +@@ -17,15 +17,25 @@ object Generator extends App + val parser = new OptionParser[Options]("generator/run") { + opt[String]('C', "config") valueName("") text("config file") action {(s: String, c) => c.copy(confFileName = s)} + opt[String]('o', "output") valueName("") text("output filename") action {(s: String, c) => c.copy(outFileName = s)} ++ opt[Int]('n', "numfiles") valueName("") text("number of output files") action {(n: Int, c) => c.copy(numOutFiles = n)} + } + parser.parse(args, Options()) match { + case Some(opts) => +- generate(opts.confFileName, opts.outFileName) ++ generate_loop(opts.confFileName, opts.outFileName, opts.numOutFiles) + case None => + System.exit(1) //error message printed by parser + } + } + ++ def generate_loop(confFile: String, outFileName: String, numOutFiles: Int) = { ++ if (numOutFiles > 0) { ++ for (i <- 0 to (numOutFiles-1)) ++ generate(confFile, outFileName + ("_%03d" format (i))) ++ } else { ++ generate(confFile, outFileName) ++ } ++ } ++ + def generate(confFile: String, outFileName: String): String = { + val config = new Properties() + val in = new FileInputStream(confFile) diff --git a/scripts/torture/riscv-torture-rv32.diff b/scripts/torture/riscv-torture-rv32.diff new file mode 100644 index 0000000..fef49b3 --- /dev/null +++ b/scripts/torture/riscv-torture-rv32.diff @@ -0,0 +1,141 @@ +diff --git a/config/default.config b/config/default.config +index b671223..c0b2bb4 100644 +--- a/config/default.config ++++ b/config/default.config +@@ -1,18 +1,18 @@ + torture.generator.nseqs 1000 + torture.generator.memsize 1024 + torture.generator.fprnd 0 +-torture.generator.amo true ++torture.generator.amo false + torture.generator.mul true + torture.generator.divider true + torture.generator.run_twice true + + torture.generator.mix.xmem 10 + torture.generator.mix.xbranch 20 +-torture.generator.mix.xalu 50 +-torture.generator.mix.fgen 10 +-torture.generator.mix.fpmem 5 +-torture.generator.mix.fax 3 +-torture.generator.mix.fdiv 2 ++torture.generator.mix.xalu 70 ++torture.generator.mix.fgen 0 ++torture.generator.mix.fpmem 0 ++torture.generator.mix.fax 0 ++torture.generator.mix.fdiv 0 + torture.generator.mix.vec 0 + + torture.generator.vec.vf 1 +diff --git a/generator/src/main/scala/HWRegPool.scala b/generator/src/main/scala/HWRegPool.scala +index de2ad8d..864bcc4 100644 +--- a/generator/src/main/scala/HWRegPool.scala ++++ b/generator/src/main/scala/HWRegPool.scala +@@ -86,7 +86,7 @@ trait PoolsMaster extends HWRegPool + + class XRegsPool extends ScalarRegPool + { +- val (name, regname, ldinst, stinst) = ("xreg", "reg_x", "ld", "sd") ++ val (name, regname, ldinst, stinst) = ("xreg", "reg_x", "lw", "sw") + + hwregs += new HWReg("x0", true, false) + for (i <- 1 to 31) +diff --git a/generator/src/main/scala/Prog.scala b/generator/src/main/scala/Prog.scala +index 6fb49e2..685c2f8 100644 +--- a/generator/src/main/scala/Prog.scala ++++ b/generator/src/main/scala/Prog.scala +@@ -385,7 +385,7 @@ class Prog(memsize: Int, veccfg: Map[String,String], run_twice: Boolean) + "\n" + + (if (using_vec) "RVTEST_RV64UV\n" + else if (using_fpu) "RVTEST_RV64UF\n" +- else "RVTEST_RV64U\n") + ++ else "RVTEST_RV32U\n") + + "RVTEST_CODE_BEGIN\n" + + (if (using_vec) init_vector() else "") + + "\n" + +diff --git a/generator/src/main/scala/Rand.scala b/generator/src/main/scala/Rand.scala +index a677d2d..ec0745f 100644 +--- a/generator/src/main/scala/Rand.scala ++++ b/generator/src/main/scala/Rand.scala +@@ -15,7 +15,7 @@ object Rand + low + Random.nextInt(span) + } + +- def rand_shamt() = rand_range(0, 63) ++ def rand_shamt() = rand_range(0, 31) + def rand_shamtw() = rand_range(0, 31) + def rand_seglen() = rand_range(0, 7) + def rand_imm() = rand_range(-2048, 2047) +diff --git a/generator/src/main/scala/SeqALU.scala b/generator/src/main/scala/SeqALU.scala +index a1f27a5..18d6d7b 100644 +--- a/generator/src/main/scala/SeqALU.scala ++++ b/generator/src/main/scala/SeqALU.scala +@@ -68,17 +68,12 @@ class SeqALU(xregs: HWRegPool, use_mul: Boolean, use_div: Boolean) extends InstS + candidates += seq_src1_immfn(SRAI, rand_shamt) + candidates += seq_src1_immfn(ORI, rand_imm) + candidates += seq_src1_immfn(ANDI, rand_imm) +- candidates += seq_src1_immfn(ADDIW, rand_imm) +- candidates += seq_src1_immfn(SLLIW, rand_shamtw) +- candidates += seq_src1_immfn(SRLIW, rand_shamtw) +- candidates += seq_src1_immfn(SRAIW, rand_shamtw) + + val oplist = new ArrayBuffer[Opcode] + + oplist += (ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND) +- oplist += (ADDW, SUBW, SLLW, SRLW, SRAW) +- if (use_mul) oplist += (MUL, MULH, MULHSU, MULHU, MULW) +- if (use_div) oplist += (DIV, DIVU, REM, REMU, DIVW, DIVUW, REMW, REMUW) ++ if (use_mul) oplist += (MUL, MULH, MULHSU, MULHU) ++ if (use_div) oplist += (DIV, DIVU, REM, REMU) + + for (op <- oplist) + { +diff --git a/generator/src/main/scala/SeqBranch.scala b/generator/src/main/scala/SeqBranch.scala +index bba9895..0d257d7 100644 +--- a/generator/src/main/scala/SeqBranch.scala ++++ b/generator/src/main/scala/SeqBranch.scala +@@ -75,7 +75,7 @@ class SeqBranch(xregs: HWRegPool) extends InstSeq + val reg_mask = reg_write_visible(xregs) + + insts += ADDI(reg_one, reg_read_zero(xregs), Imm(1)) +- insts += SLL(reg_one, reg_one, Imm(63)) ++ insts += SLL(reg_one, reg_one, Imm(31)) + insts += ADDI(reg_mask, reg_read_zero(xregs), Imm(-1)) + insts += XOR(reg_mask, reg_mask, reg_one) + insts += AND(reg_dst1, reg_src, reg_mask) +@@ -95,7 +95,7 @@ class SeqBranch(xregs: HWRegPool) extends InstSeq + val reg_mask = reg_write_visible(xregs) + + insts += ADDI(reg_one, reg_read_zero(xregs), Imm(1)) +- insts += SLL(reg_one, reg_one, Imm(63)) ++ insts += SLL(reg_one, reg_one, Imm(31)) + insts += ADDI(reg_mask, reg_read_zero(xregs), Imm(-1)) + insts += XOR(reg_mask, reg_mask, reg_one) + insts += AND(reg_dst1, reg_src1, reg_mask) +diff --git a/generator/src/main/scala/SeqMem.scala b/generator/src/main/scala/SeqMem.scala +index 3c180ed..89200f6 100644 +--- a/generator/src/main/scala/SeqMem.scala ++++ b/generator/src/main/scala/SeqMem.scala +@@ -51,7 +51,7 @@ class SeqMem(xregs: HWRegPool, mem: Mem, use_amo: Boolean) extends InstSeq + + def getRandOpAndAddr (dw_addr: Int, is_store: Boolean): (Opcode, Int) = + { +- val typ = AccessType.values.toIndexedSeq(rand_range(0,6)) ++ val typ = AccessType.values.toIndexedSeq(rand_range(0,4)) + if (is_store) + { + if (typ == byte || typ ==ubyte) (SB, dw_addr + rand_addr_b(8)) +@@ -110,13 +110,10 @@ class SeqMem(xregs: HWRegPool, mem: Mem, use_amo: Boolean) extends InstSeq + candidates += seq_load_addrfn(LH, rand_addr_h) + candidates += seq_load_addrfn(LHU, rand_addr_h) + candidates += seq_load_addrfn(LW, rand_addr_w) +- candidates += seq_load_addrfn(LWU, rand_addr_w) +- candidates += seq_load_addrfn(LD, rand_addr_d) + + candidates += seq_store_addrfn(SB, rand_addr_b) + candidates += seq_store_addrfn(SH, rand_addr_h) + candidates += seq_store_addrfn(SW, rand_addr_w) +- candidates += seq_store_addrfn(SD, rand_addr_d) + + if (use_amo) + { diff --git a/scripts/torture/riscv_test.h b/scripts/torture/riscv_test.h new file mode 100644 index 0000000..36c5b54 --- /dev/null +++ b/scripts/torture/riscv_test.h @@ -0,0 +1,13 @@ +#ifndef RISCV_TEST_H +#define RISCV_TEST_H + +#define RVTEST_RV32U +#define RVTEST_CODE_BEGIN +#define RVTEST_CODE_END +#define RVTEST_DATA_BEGIN +#define RVTEST_DATA_END + +#define RVTEST_FAIL ebreak +#define RVTEST_PASS ebreak + +#endif diff --git a/scripts/torture/sections.lds b/scripts/torture/sections.lds new file mode 100644 index 0000000..a9487e2 --- /dev/null +++ b/scripts/torture/sections.lds @@ -0,0 +1,9 @@ +SECTIONS { + .memory : { + . = 0x000000; + *(.text); + *(*); + end = .; + } +} + diff --git a/scripts/torture/test.sh b/scripts/torture/test.sh new file mode 100644 index 0000000..17c5a7c --- /dev/null +++ b/scripts/torture/test.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -ex + + +## Generate test case + +if ! test -f config.vh; then + python3 config.py +fi + +if ! test -f test.S; then + cd riscv-torture + ./sbt "generator/run -C config/test.config" + cp output/test.S ../test.S + cd .. +fi + + +## Compile test case and create reference + +riscv32-unknown-elf-gcc `sed '/march=/ ! d; s,^// ,-,; y/RVIMC/rvimc/;' config.vh` -ffreestanding -nostdlib -Wl,-Bstatic,-T,sections.lds -o test.elf test.S +LD_LIBRARY_PATH="./riscv-isa-sim:./riscv-fesvr" ./riscv-isa-sim/spike test.elf > test.ref +riscv32-unknown-elf-objcopy -O binary test.elf test.bin +python3 ../../firmware/makehex.py test.bin 4096 > test.hex + + +## Run test + +iverilog -o test.vvp testbench.v ../../picorv32.v +vvp test.vvp +vcd +hex=test.hex +ref=test.ref + diff --git a/scripts/torture/testbench.cc b/scripts/torture/testbench.cc new file mode 100644 index 0000000..2925d0b --- /dev/null +++ b/scripts/torture/testbench.cc @@ -0,0 +1,18 @@ +#include "Vtestbench.h" +#include "verilated.h" + +int main(int argc, char **argv, char **env) +{ + Verilated::commandArgs(argc, argv); + Vtestbench* top = new Vtestbench; + + top->clk = 0; + while (!Verilated::gotFinish()) { + top->clk = !top->clk; + top->eval(); + } + + delete top; + exit(0); +} + diff --git a/scripts/torture/testbench.v b/scripts/torture/testbench.v new file mode 100644 index 0000000..326de0e --- /dev/null +++ b/scripts/torture/testbench.v @@ -0,0 +1,134 @@ +module testbench ( +`ifdef VERILATOR + input clk +`endif +); +`ifndef VERILATOR + reg clk = 1; + always #5 clk = ~clk; +`endif + reg resetn = 0; + wire trap; + + wire mem_valid; + wire mem_instr; + reg mem_ready; + wire [31:0] mem_addr; + wire [31:0] mem_wdata; + wire [3:0] mem_wstrb; + reg [31:0] mem_rdata; + + wire mem_la_read; + wire mem_la_write; + wire [31:0] mem_la_addr; + wire [31:0] mem_la_wdata; + wire [3:0] mem_la_wstrb; + + reg [31:0] x32 = 314159265; + reg [31:0] next_x32; + + always @(posedge clk) begin + if (resetn) begin + next_x32 = x32; + next_x32 = next_x32 ^ (next_x32 << 13); + next_x32 = next_x32 ^ (next_x32 >> 17); + next_x32 = next_x32 ^ (next_x32 << 5); + x32 <= next_x32; + end + end + + picorv32 #( +`include "config.vh" + ) uut ( + .clk (clk ), + .resetn (resetn ), + .trap (trap ), + + .mem_valid (mem_valid ), + .mem_instr (mem_instr ), + .mem_ready (mem_ready ), + .mem_addr (mem_addr ), + .mem_wdata (mem_wdata ), + .mem_wstrb (mem_wstrb ), + .mem_rdata (mem_rdata ), + + .mem_la_read (mem_la_read ), + .mem_la_write(mem_la_write), + .mem_la_addr (mem_la_addr ), + .mem_la_wdata(mem_la_wdata), + .mem_la_wstrb(mem_la_wstrb) + ); + + localparam integer filename_len = 18; + reg [8*filename_len-1:0] hex_filename; + reg [8*filename_len-1:0] ref_filename; + + reg [31:0] memory [0:4095]; + reg [31:0] memory_ref [0:4095]; + integer i, errcount; + integer cycle = 0; + + initial begin + if ($value$plusargs("hex=%s", hex_filename)) $readmemh(hex_filename, memory); + if ($value$plusargs("ref=%s", ref_filename)) $readmemh(ref_filename, memory_ref); +`ifndef VERILATOR + if ($test$plusargs("vcd")) begin + $dumpfile("test.vcd"); + $dumpvars(0, testbench); + end +`endif + end + + always @(posedge clk) begin + mem_ready <= 0; + mem_rdata <= 'bx; + + if (!trap || !resetn) begin + if (x32[0] && resetn) begin + if (mem_la_read) begin + mem_ready <= 1; + mem_rdata <= memory[mem_la_addr >> 2]; + end else + if (mem_la_write) begin + mem_ready <= 1; + if (mem_la_wstrb[0]) memory[mem_la_addr >> 2][ 7: 0] <= mem_la_wdata[ 7: 0]; + if (mem_la_wstrb[1]) memory[mem_la_addr >> 2][15: 8] <= mem_la_wdata[15: 8]; + if (mem_la_wstrb[2]) memory[mem_la_addr >> 2][23:16] <= mem_la_wdata[23:16]; + if (mem_la_wstrb[3]) memory[mem_la_addr >> 2][31:24] <= mem_la_wdata[31:24]; + end else + if (mem_valid && !mem_ready) begin + mem_ready <= 1; + if (mem_wstrb) begin + if (mem_wstrb[0]) memory[mem_addr >> 2][ 7: 0] <= mem_wdata[ 7: 0]; + if (mem_wstrb[1]) memory[mem_addr >> 2][15: 8] <= mem_wdata[15: 8]; + if (mem_wstrb[2]) memory[mem_addr >> 2][23:16] <= mem_wdata[23:16]; + if (mem_wstrb[3]) memory[mem_addr >> 2][31:24] <= mem_wdata[31:24]; + end else begin + mem_rdata <= memory[mem_addr >> 2]; + end + end + end + end else begin + errcount = 0; + for (i=0; i < 4096; i=i+1) begin + if (memory[i] !== memory_ref[i]) begin + $display("Signature check failed at %04x: mem=%08x ref=%08x", i << 2, memory[i], memory_ref[i]); + errcount = errcount + 1; + end + end + if (errcount) + $display("FAILED: Got %1d errors for %1s => %1s!", errcount, hex_filename, ref_filename); + else + $display("PASSED %1s => %1s.", hex_filename, ref_filename); + $finish; + end + + if (cycle > 100000) begin + $display("FAILED: Timeout!"); + $finish; + end + + resetn <= cycle > 10; + cycle <= cycle + 1; + end +endmodule -- cgit