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 --- .gitignore | 37 + COPYING | 15 + Makefile | 184 ++ README.md | 738 +++++++ dhrystone/Makefile | 69 + dhrystone/README | 1 + dhrystone/dhry.h | 428 ++++ dhrystone/dhry_1.c | 427 ++++ dhrystone/dhry_1_orig.c | 385 ++++ dhrystone/dhry_2.c | 192 ++ dhrystone/sections.lds | 18 + dhrystone/start.S | 51 + dhrystone/stdlib.c | 210 ++ dhrystone/syscalls.c | 95 + dhrystone/testbench.v | 126 ++ dhrystone/testbench_nola.v | 95 + firmware/README | 2 + firmware/custom_ops.S | 102 + firmware/firmware.h | 43 + firmware/hello.c | 14 + firmware/irq.c | 140 ++ firmware/makehex.py | 27 + firmware/multest.c | 151 ++ firmware/print.c | 41 + firmware/riscv.ld | 200 ++ firmware/riscv.ld.orig | 236 +++ firmware/sections.lds | 25 + firmware/sieve.c | 84 + firmware/start.S | 537 +++++ firmware/stats.c | 42 + picorv32.core | 78 + picorv32.v | 3044 ++++++++++++++++++++++++++++ picosoc/.gitignore | 29 + picosoc/Makefile | 123 ++ picosoc/README.md | 114 ++ picosoc/firmware.c | 770 +++++++ picosoc/hx8kdemo.core | 35 + picosoc/hx8kdemo.pcf | 40 + picosoc/hx8kdemo.v | 139 ++ picosoc/hx8kdemo_tb.v | 108 + picosoc/ice40up5k_spram.v | 91 + picosoc/icebreaker.core | 36 + picosoc/icebreaker.pcf | 25 + picosoc/icebreaker.v | 151 ++ picosoc/icebreaker_tb.v | 122 ++ picosoc/overview.svg | 757 +++++++ picosoc/performance.png | Bin 0 -> 62502 bytes picosoc/performance.py | 165 ++ picosoc/picosoc.core | 27 + picosoc/picosoc.v | 262 +++ picosoc/sections.lds | 71 + picosoc/simpleuart.v | 137 ++ picosoc/spiflash.core | 24 + picosoc/spiflash.v | 409 ++++ picosoc/spiflash_tb.v | 366 ++++ picosoc/spimemio.v | 579 ++++++ picosoc/start.s | 159 ++ scripts/csmith/.gitignore | 13 + scripts/csmith/Makefile | 85 + scripts/csmith/riscv-isa-sim.diff | 62 + scripts/csmith/start.S | 51 + scripts/csmith/syscalls.c | 95 + scripts/csmith/testbench.cc | 18 + scripts/csmith/testbench.v | 100 + scripts/cxxdemo/.gitignore | 9 + scripts/cxxdemo/Makefile | 38 + scripts/cxxdemo/firmware.cc | 87 + scripts/cxxdemo/hex8tohex32.py | 34 + scripts/cxxdemo/start.S | 52 + scripts/cxxdemo/start.ld | 5 + scripts/cxxdemo/syscalls.c | 95 + scripts/cxxdemo/testbench.v | 114 ++ scripts/icestorm/.gitignore | 5 + scripts/icestorm/Makefile | 104 + scripts/icestorm/example.pcf | 9 + scripts/icestorm/example.v | 80 + scripts/icestorm/example_tb.v | 30 + scripts/icestorm/firmware.S | 12 + scripts/icestorm/firmware.c | 58 + scripts/icestorm/firmware.lds | 11 + scripts/icestorm/readme.md | 12 + scripts/presyn/.gitignore | 7 + scripts/presyn/Makefile | 22 + scripts/presyn/README | 5 + scripts/presyn/firmware.S | 47 + scripts/presyn/firmware.c | 43 + scripts/presyn/firmware.lds | 11 + scripts/presyn/picorv32_presyn.ys | 5 + scripts/presyn/picorv32_regs.txt | 16 + scripts/presyn/testbench.v | 81 + scripts/quartus/.gitignore | 11 + scripts/quartus/Makefile | 62 + scripts/quartus/firmware.S | 12 + scripts/quartus/firmware.c | 43 + scripts/quartus/firmware.lds | 11 + scripts/quartus/synth_area.sdc | 1 + scripts/quartus/synth_area_large.qsf | 6 + scripts/quartus/synth_area_regular.qsf | 6 + scripts/quartus/synth_area_small.qsf | 6 + scripts/quartus/synth_area_top.v | 140 ++ scripts/quartus/synth_speed.qsf | 5 + scripts/quartus/synth_speed.sdc | 1 + scripts/quartus/synth_system.qsf | 6 + scripts/quartus/synth_system.sdc | 1 + scripts/quartus/synth_system.tcl | 17 + scripts/quartus/system.v | 101 + scripts/quartus/system_tb.v | 38 + scripts/quartus/table.sh | 17 + scripts/quartus/tabtest.sh | 78 + scripts/quartus/tabtest.v | 118 ++ scripts/romload/.gitignore | 11 + scripts/romload/Makefile | 41 + scripts/romload/firmware.c | 21 + scripts/romload/hex8tohex32.py | 34 + scripts/romload/map2debug.py | 30 + scripts/romload/sections.ld | 45 + scripts/romload/start.S | 52 + scripts/romload/syscalls.c | 95 + scripts/romload/testbench.v | 140 ++ scripts/smtbmc/.gitignore | 18 + scripts/smtbmc/axicheck.sh | 13 + scripts/smtbmc/axicheck.v | 210 ++ scripts/smtbmc/axicheck2.sh | 12 + scripts/smtbmc/axicheck2.smtc | 2 + scripts/smtbmc/axicheck2.v | 147 ++ scripts/smtbmc/mulcmp.sh | 12 + scripts/smtbmc/mulcmp.v | 87 + scripts/smtbmc/notrap_validop.sh | 13 + scripts/smtbmc/notrap_validop.v | 67 + scripts/smtbmc/opcode.v | 104 + scripts/smtbmc/tracecmp.gtkw | 71 + scripts/smtbmc/tracecmp.sh | 12 + scripts/smtbmc/tracecmp.smtc | 12 + scripts/smtbmc/tracecmp.v | 109 + scripts/smtbmc/tracecmp2.sh | 12 + scripts/smtbmc/tracecmp2.smtc | 2 + scripts/smtbmc/tracecmp2.v | 196 ++ scripts/smtbmc/tracecmp3.sh | 17 + scripts/smtbmc/tracecmp3.v | 135 ++ scripts/tomthumbtg/.gitignore | 15 + scripts/tomthumbtg/README | 7 + scripts/tomthumbtg/run.sh | 43 + scripts/tomthumbtg/sections.lds | 7 + scripts/tomthumbtg/start.S | 57 + scripts/tomthumbtg/testbench.v | 83 + 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 ++ scripts/vivado/.gitignore | 18 + scripts/vivado/Makefile | 69 + scripts/vivado/firmware.S | 12 + scripts/vivado/firmware.c | 43 + scripts/vivado/firmware.lds | 11 + scripts/vivado/synth_area.tcl | 8 + scripts/vivado/synth_area.xdc | 1 + scripts/vivado/synth_area_large.tcl | 10 + scripts/vivado/synth_area_regular.tcl | 10 + scripts/vivado/synth_area_small.tcl | 10 + scripts/vivado/synth_area_top.v | 140 ++ scripts/vivado/synth_speed.tcl | 13 + scripts/vivado/synth_speed.xdc | 1 + scripts/vivado/synth_system.tcl | 17 + scripts/vivado/synth_system.xdc | 34 + scripts/vivado/system.v | 101 + scripts/vivado/system_tb.v | 38 + scripts/vivado/table.sh | 21 + scripts/vivado/tabtest.sh | 103 + scripts/vivado/tabtest.v | 118 ++ scripts/yosys-cmp/README.md | 62 + scripts/yosys-cmp/lse.sh | 53 + scripts/yosys-cmp/synplify.sh | 74 + scripts/yosys-cmp/vivado.tcl | 3 + scripts/yosys-cmp/yosys_ice40.ys | 2 + scripts/yosys-cmp/yosys_xilinx.ys | 2 + scripts/yosys/.gitignore | 1 + scripts/yosys/synth_gates.lib | 38 + scripts/yosys/synth_gates.v | 30 + scripts/yosys/synth_gates.ys | 14 + scripts/yosys/synth_osu018.sh | 8 + scripts/yosys/synth_sim.ys | 8 + shell.nix | 139 ++ showtrace.py | 66 + testbench.cc | 44 + testbench.v | 479 +++++ testbench_ez.v | 86 + testbench_wb.v | 293 +++ tests/LICENSE | 24 + tests/README | 1 + tests/add.S | 85 + tests/addi.S | 71 + tests/and.S | 69 + tests/andi.S | 55 + tests/auipc.S | 39 + tests/beq.S | 73 + tests/bge.S | 76 + tests/bgeu.S | 76 + tests/blt.S | 73 + tests/bltu.S | 73 + tests/bne.S | 73 + tests/div.S | 41 + tests/divu.S | 41 + tests/j.S | 49 + tests/jal.S | 62 + tests/jalr.S | 90 + tests/lb.S | 92 + tests/lbu.S | 92 + tests/lh.S | 92 + tests/lhu.S | 92 + tests/lui.S | 36 + tests/lw.S | 92 + tests/mul.S | 84 + tests/mulh.S | 81 + tests/mulhsu.S | 83 + tests/mulhu.S | 82 + tests/or.S | 69 + tests/ori.S | 55 + tests/rem.S | 41 + tests/remu.S | 41 + tests/riscv_test.h | 64 + tests/sb.S | 96 + tests/sh.S | 96 + tests/simple.S | 27 + tests/sll.S | 90 + tests/slli.S | 68 + tests/slt.S | 84 + tests/slti.S | 70 + tests/sra.S | 90 + tests/srai.S | 68 + tests/srl.S | 90 + tests/srli.S | 69 + tests/sub.S | 83 + tests/sw.S | 92 + tests/test_macros.h | 585 ++++++ tests/xor.S | 69 + tests/xori.S | 55 + 246 files changed, 23304 insertions(+) create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 Makefile create mode 100644 README.md create mode 100644 dhrystone/Makefile create mode 100644 dhrystone/README create mode 100644 dhrystone/dhry.h create mode 100644 dhrystone/dhry_1.c create mode 100644 dhrystone/dhry_1_orig.c create mode 100644 dhrystone/dhry_2.c create mode 100644 dhrystone/sections.lds create mode 100644 dhrystone/start.S create mode 100644 dhrystone/stdlib.c create mode 100644 dhrystone/syscalls.c create mode 100644 dhrystone/testbench.v create mode 100644 dhrystone/testbench_nola.v create mode 100644 firmware/README create mode 100644 firmware/custom_ops.S create mode 100644 firmware/firmware.h create mode 100644 firmware/hello.c create mode 100644 firmware/irq.c create mode 100644 firmware/makehex.py create mode 100644 firmware/multest.c create mode 100644 firmware/print.c create mode 100644 firmware/riscv.ld create mode 100644 firmware/riscv.ld.orig create mode 100644 firmware/sections.lds create mode 100644 firmware/sieve.c create mode 100644 firmware/start.S create mode 100644 firmware/stats.c create mode 100644 picorv32.core create mode 100644 picorv32.v create mode 100644 picosoc/.gitignore create mode 100644 picosoc/Makefile create mode 100644 picosoc/README.md create mode 100644 picosoc/firmware.c create mode 100644 picosoc/hx8kdemo.core create mode 100644 picosoc/hx8kdemo.pcf create mode 100644 picosoc/hx8kdemo.v create mode 100644 picosoc/hx8kdemo_tb.v create mode 100644 picosoc/ice40up5k_spram.v create mode 100644 picosoc/icebreaker.core create mode 100644 picosoc/icebreaker.pcf create mode 100644 picosoc/icebreaker.v create mode 100644 picosoc/icebreaker_tb.v create mode 100644 picosoc/overview.svg create mode 100644 picosoc/performance.png create mode 100644 picosoc/performance.py create mode 100644 picosoc/picosoc.core create mode 100644 picosoc/picosoc.v create mode 100644 picosoc/sections.lds create mode 100644 picosoc/simpleuart.v create mode 100644 picosoc/spiflash.core create mode 100644 picosoc/spiflash.v create mode 100644 picosoc/spiflash_tb.v create mode 100644 picosoc/spimemio.v create mode 100644 picosoc/start.s create mode 100644 scripts/csmith/.gitignore create mode 100644 scripts/csmith/Makefile create mode 100644 scripts/csmith/riscv-isa-sim.diff create mode 100644 scripts/csmith/start.S create mode 100644 scripts/csmith/syscalls.c create mode 100644 scripts/csmith/testbench.cc create mode 100644 scripts/csmith/testbench.v create mode 100644 scripts/cxxdemo/.gitignore create mode 100644 scripts/cxxdemo/Makefile create mode 100644 scripts/cxxdemo/firmware.cc create mode 100644 scripts/cxxdemo/hex8tohex32.py create mode 100644 scripts/cxxdemo/start.S create mode 100644 scripts/cxxdemo/start.ld create mode 100644 scripts/cxxdemo/syscalls.c create mode 100644 scripts/cxxdemo/testbench.v create mode 100644 scripts/icestorm/.gitignore create mode 100644 scripts/icestorm/Makefile create mode 100644 scripts/icestorm/example.pcf create mode 100644 scripts/icestorm/example.v create mode 100644 scripts/icestorm/example_tb.v create mode 100644 scripts/icestorm/firmware.S create mode 100644 scripts/icestorm/firmware.c create mode 100644 scripts/icestorm/firmware.lds create mode 100644 scripts/icestorm/readme.md create mode 100644 scripts/presyn/.gitignore create mode 100644 scripts/presyn/Makefile create mode 100644 scripts/presyn/README create mode 100644 scripts/presyn/firmware.S create mode 100644 scripts/presyn/firmware.c create mode 100644 scripts/presyn/firmware.lds create mode 100644 scripts/presyn/picorv32_presyn.ys create mode 100644 scripts/presyn/picorv32_regs.txt create mode 100644 scripts/presyn/testbench.v create mode 100644 scripts/quartus/.gitignore create mode 100644 scripts/quartus/Makefile create mode 100644 scripts/quartus/firmware.S create mode 100644 scripts/quartus/firmware.c create mode 100644 scripts/quartus/firmware.lds create mode 100644 scripts/quartus/synth_area.sdc create mode 100644 scripts/quartus/synth_area_large.qsf create mode 100644 scripts/quartus/synth_area_regular.qsf create mode 100644 scripts/quartus/synth_area_small.qsf create mode 100644 scripts/quartus/synth_area_top.v create mode 100644 scripts/quartus/synth_speed.qsf create mode 100644 scripts/quartus/synth_speed.sdc create mode 100644 scripts/quartus/synth_system.qsf create mode 100644 scripts/quartus/synth_system.sdc create mode 100644 scripts/quartus/synth_system.tcl create mode 100644 scripts/quartus/system.v create mode 100644 scripts/quartus/system_tb.v create mode 100644 scripts/quartus/table.sh create mode 100644 scripts/quartus/tabtest.sh create mode 100644 scripts/quartus/tabtest.v create mode 100644 scripts/romload/.gitignore create mode 100644 scripts/romload/Makefile create mode 100644 scripts/romload/firmware.c create mode 100644 scripts/romload/hex8tohex32.py create mode 100644 scripts/romload/map2debug.py create mode 100644 scripts/romload/sections.ld create mode 100644 scripts/romload/start.S create mode 100644 scripts/romload/syscalls.c create mode 100644 scripts/romload/testbench.v create mode 100644 scripts/smtbmc/.gitignore create mode 100644 scripts/smtbmc/axicheck.sh create mode 100644 scripts/smtbmc/axicheck.v create mode 100644 scripts/smtbmc/axicheck2.sh create mode 100644 scripts/smtbmc/axicheck2.smtc create mode 100644 scripts/smtbmc/axicheck2.v create mode 100644 scripts/smtbmc/mulcmp.sh create mode 100644 scripts/smtbmc/mulcmp.v create mode 100644 scripts/smtbmc/notrap_validop.sh create mode 100644 scripts/smtbmc/notrap_validop.v create mode 100644 scripts/smtbmc/opcode.v create mode 100644 scripts/smtbmc/tracecmp.gtkw create mode 100644 scripts/smtbmc/tracecmp.sh create mode 100644 scripts/smtbmc/tracecmp.smtc create mode 100644 scripts/smtbmc/tracecmp.v create mode 100644 scripts/smtbmc/tracecmp2.sh create mode 100644 scripts/smtbmc/tracecmp2.smtc create mode 100644 scripts/smtbmc/tracecmp2.v create mode 100644 scripts/smtbmc/tracecmp3.sh create mode 100644 scripts/smtbmc/tracecmp3.v create mode 100644 scripts/tomthumbtg/.gitignore create mode 100644 scripts/tomthumbtg/README create mode 100644 scripts/tomthumbtg/run.sh create mode 100644 scripts/tomthumbtg/sections.lds create mode 100644 scripts/tomthumbtg/start.S create mode 100644 scripts/tomthumbtg/testbench.v 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 create mode 100644 scripts/vivado/.gitignore create mode 100644 scripts/vivado/Makefile create mode 100644 scripts/vivado/firmware.S create mode 100644 scripts/vivado/firmware.c create mode 100644 scripts/vivado/firmware.lds create mode 100644 scripts/vivado/synth_area.tcl create mode 100644 scripts/vivado/synth_area.xdc create mode 100644 scripts/vivado/synth_area_large.tcl create mode 100644 scripts/vivado/synth_area_regular.tcl create mode 100644 scripts/vivado/synth_area_small.tcl create mode 100644 scripts/vivado/synth_area_top.v create mode 100644 scripts/vivado/synth_speed.tcl create mode 100644 scripts/vivado/synth_speed.xdc create mode 100644 scripts/vivado/synth_system.tcl create mode 100644 scripts/vivado/synth_system.xdc create mode 100644 scripts/vivado/system.v create mode 100644 scripts/vivado/system_tb.v create mode 100644 scripts/vivado/table.sh create mode 100644 scripts/vivado/tabtest.sh create mode 100644 scripts/vivado/tabtest.v create mode 100644 scripts/yosys-cmp/README.md create mode 100644 scripts/yosys-cmp/lse.sh create mode 100644 scripts/yosys-cmp/synplify.sh create mode 100644 scripts/yosys-cmp/vivado.tcl create mode 100644 scripts/yosys-cmp/yosys_ice40.ys create mode 100644 scripts/yosys-cmp/yosys_xilinx.ys create mode 100644 scripts/yosys/.gitignore create mode 100644 scripts/yosys/synth_gates.lib create mode 100644 scripts/yosys/synth_gates.v create mode 100644 scripts/yosys/synth_gates.ys create mode 100644 scripts/yosys/synth_osu018.sh create mode 100644 scripts/yosys/synth_sim.ys create mode 100644 shell.nix create mode 100644 showtrace.py create mode 100644 testbench.cc create mode 100644 testbench.v create mode 100644 testbench_ez.v create mode 100644 testbench_wb.v create mode 100644 tests/LICENSE create mode 100644 tests/README create mode 100644 tests/add.S create mode 100644 tests/addi.S create mode 100644 tests/and.S create mode 100644 tests/andi.S create mode 100644 tests/auipc.S create mode 100644 tests/beq.S create mode 100644 tests/bge.S create mode 100644 tests/bgeu.S create mode 100644 tests/blt.S create mode 100644 tests/bltu.S create mode 100644 tests/bne.S create mode 100644 tests/div.S create mode 100644 tests/divu.S create mode 100644 tests/j.S create mode 100644 tests/jal.S create mode 100644 tests/jalr.S create mode 100644 tests/lb.S create mode 100644 tests/lbu.S create mode 100644 tests/lh.S create mode 100644 tests/lhu.S create mode 100644 tests/lui.S create mode 100644 tests/lw.S create mode 100644 tests/mul.S create mode 100644 tests/mulh.S create mode 100644 tests/mulhsu.S create mode 100644 tests/mulhu.S create mode 100644 tests/or.S create mode 100644 tests/ori.S create mode 100644 tests/rem.S create mode 100644 tests/remu.S create mode 100644 tests/riscv_test.h create mode 100644 tests/sb.S create mode 100644 tests/sh.S create mode 100644 tests/simple.S create mode 100644 tests/sll.S create mode 100644 tests/slli.S create mode 100644 tests/slt.S create mode 100644 tests/slti.S create mode 100644 tests/sra.S create mode 100644 tests/srai.S create mode 100644 tests/srl.S create mode 100644 tests/srli.S create mode 100644 tests/sub.S create mode 100644 tests/sw.S create mode 100644 tests/test_macros.h create mode 100644 tests/xor.S create mode 100644 tests/xori.S diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..487adcc --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +/tests/*.o +/firmware/*.o +/firmware/firmware.bin +/firmware/firmware.elf +/firmware/firmware.hex +/firmware/firmware.map +/dhrystone/dhry.bin +/dhrystone/dhry.elf +/dhrystone/dhry.hex +/dhrystone/dhry.map +/dhrystone/testbench.vvp +/dhrystone/testbench.vcd +/dhrystone/testbench_nola.vvp +/dhrystone/testbench_nola.vcd +/dhrystone/timing.vvp +/dhrystone/timing.txt +/dhrystone/*.d +/dhrystone/*.o +/riscv-gnu-toolchain-riscv32i +/riscv-gnu-toolchain-riscv32ic +/riscv-gnu-toolchain-riscv32im +/riscv-gnu-toolchain-riscv32imc +/testbench.vvp +/testbench_wb.vvp +/testbench_ez.vvp +/testbench_sp.vvp +/testbench_rvf.vvp +/testbench_synth.vvp +/testbench.gtkw +/testbench.vcd +/testbench.trace +/testbench_verilator* +/check.smt2 +/check.vcd +/synth.log +/synth.v +.*.swp diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..3027e19 --- /dev/null +++ b/COPYING @@ -0,0 +1,15 @@ +ISC License + +Copyright (C) 2015 - 2021 Claire Xenia Wolf + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d7027e3 --- /dev/null +++ b/Makefile @@ -0,0 +1,184 @@ + +RISCV_GNU_TOOLCHAIN_GIT_REVISION = 411d134 +RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX = /opt/riscv32 + +# Give the user some easy overrides for local configuration quirks. +# If you change one of these and it breaks, then you get to keep both pieces. +SHELL = bash +PYTHON = python3 +VERILATOR = verilator +ICARUS_SUFFIX = +IVERILOG = iverilog$(ICARUS_SUFFIX) +VVP = vvp$(ICARUS_SUFFIX) + +TEST_OBJS = $(addsuffix .o,$(basename $(wildcard tests/*.S))) +FIRMWARE_OBJS = firmware/start.o firmware/irq.o firmware/print.o firmware/hello.o firmware/sieve.o firmware/multest.o firmware/stats.o +GCC_WARNS = -Werror -Wall -Wextra -Wshadow -Wundef -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings +GCC_WARNS += -Wredundant-decls -Wstrict-prototypes -Wmissing-prototypes -pedantic # -Wconversion +TOOLCHAIN_PREFIX = $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX)i/bin/riscv32-unknown-elf- +COMPRESSED_ISA = C + +# Add things like "export http_proxy=... https_proxy=..." here +GIT_ENV = true + +test: testbench.vvp firmware/firmware.hex + $(VVP) -N $< + +test_vcd: testbench.vvp firmware/firmware.hex + $(VVP) -N $< +vcd +trace +noerror + +test_rvf: testbench_rvf.vvp firmware/firmware.hex + $(VVP) -N $< +vcd +trace +noerror + +test_wb: testbench_wb.vvp firmware/firmware.hex + $(VVP) -N $< + +test_wb_vcd: testbench_wb.vvp firmware/firmware.hex + $(VVP) -N $< +vcd +trace +noerror + +test_ez: testbench_ez.vvp + $(VVP) -N $< + +test_ez_vcd: testbench_ez.vvp + $(VVP) -N $< +vcd + +test_sp: testbench_sp.vvp firmware/firmware.hex + $(VVP) -N $< + +test_axi: testbench.vvp firmware/firmware.hex + $(VVP) -N $< +axi_test + +test_synth: testbench_synth.vvp firmware/firmware.hex + $(VVP) -N $< + +test_verilator: testbench_verilator firmware/firmware.hex + ./testbench_verilator + +testbench.vvp: testbench.v picorv32.v + $(IVERILOG) -o $@ $(subst C,-DCOMPRESSED_ISA,$(COMPRESSED_ISA)) $^ + chmod -x $@ + +testbench_rvf.vvp: testbench.v picorv32.v rvfimon.v + $(IVERILOG) -o $@ -D RISCV_FORMAL $(subst C,-DCOMPRESSED_ISA,$(COMPRESSED_ISA)) $^ + chmod -x $@ + +testbench_wb.vvp: testbench_wb.v picorv32.v + $(IVERILOG) -o $@ $(subst C,-DCOMPRESSED_ISA,$(COMPRESSED_ISA)) $^ + chmod -x $@ + +testbench_ez.vvp: testbench_ez.v picorv32.v + $(IVERILOG) -o $@ $(subst C,-DCOMPRESSED_ISA,$(COMPRESSED_ISA)) $^ + chmod -x $@ + +testbench_sp.vvp: testbench.v picorv32.v + $(IVERILOG) -o $@ $(subst C,-DCOMPRESSED_ISA,$(COMPRESSED_ISA)) -DSP_TEST $^ + chmod -x $@ + +testbench_synth.vvp: testbench.v synth.v + $(IVERILOG) -o $@ -DSYNTH_TEST $^ + chmod -x $@ + +testbench_verilator: testbench.v picorv32.v testbench.cc + $(VERILATOR) --cc --exe -Wno-lint -trace --top-module picorv32_wrapper testbench.v picorv32.v testbench.cc \ + $(subst C,-DCOMPRESSED_ISA,$(COMPRESSED_ISA)) --Mdir testbench_verilator_dir + $(MAKE) -C testbench_verilator_dir -f Vpicorv32_wrapper.mk + cp testbench_verilator_dir/Vpicorv32_wrapper testbench_verilator + +check: check-yices + +check-%: check.smt2 + yosys-smtbmc -s $(subst check-,,$@) -t 30 --dump-vcd check.vcd check.smt2 + yosys-smtbmc -s $(subst check-,,$@) -t 25 --dump-vcd check.vcd -i check.smt2 + +check.smt2: picorv32.v + yosys -v2 -p 'read_verilog -formal picorv32.v' \ + -p 'prep -top picorv32 -nordff' \ + -p 'assertpmux -noinit; opt -fast; dffunmap' \ + -p 'write_smt2 -wires check.smt2' + +synth.v: picorv32.v scripts/yosys/synth_sim.ys + yosys -qv3 -l synth.log scripts/yosys/synth_sim.ys + +firmware/firmware.hex: firmware/firmware.bin firmware/makehex.py + $(PYTHON) firmware/makehex.py $< 32768 > $@ + +firmware/firmware.bin: firmware/firmware.elf + $(TOOLCHAIN_PREFIX)objcopy -O binary $< $@ + chmod -x $@ + +firmware/firmware.elf: $(FIRMWARE_OBJS) $(TEST_OBJS) firmware/sections.lds + $(TOOLCHAIN_PREFIX)gcc -Os -mabi=ilp32 -march=rv32im$(subst C,c,$(COMPRESSED_ISA)) -ffreestanding -nostdlib -o $@ \ + -Wl,--build-id=none,-Bstatic,-T,firmware/sections.lds,-Map,firmware/firmware.map,--strip-debug \ + $(FIRMWARE_OBJS) $(TEST_OBJS) -lgcc + chmod -x $@ + +firmware/start.o: firmware/start.S + $(TOOLCHAIN_PREFIX)gcc -c -mabi=ilp32 -march=rv32im$(subst C,c,$(COMPRESSED_ISA)) -o $@ $< + +firmware/%.o: firmware/%.c + $(TOOLCHAIN_PREFIX)gcc -c -mabi=ilp32 -march=rv32i$(subst C,c,$(COMPRESSED_ISA)) -Os --std=c99 $(GCC_WARNS) -ffreestanding -nostdlib -o $@ $< + +tests/%.o: tests/%.S tests/riscv_test.h tests/test_macros.h + $(TOOLCHAIN_PREFIX)gcc -c -mabi=ilp32 -march=rv32im -o $@ -DTEST_FUNC_NAME=$(notdir $(basename $<)) \ + -DTEST_FUNC_TXT='"$(notdir $(basename $<))"' -DTEST_FUNC_RET=$(notdir $(basename $<))_ret $< + +download-tools: + sudo bash -c 'set -ex; mkdir -p /var/cache/distfiles; $(GIT_ENV); \ + $(foreach REPO,riscv-gnu-toolchain riscv-binutils-gdb riscv-gcc riscv-glibc riscv-newlib, \ + if ! test -d /var/cache/distfiles/$(REPO).git; then rm -rf /var/cache/distfiles/$(REPO).git.part; \ + git clone --bare https://github.com/riscv/$(REPO) /var/cache/distfiles/$(REPO).git.part; \ + mv /var/cache/distfiles/$(REPO).git.part /var/cache/distfiles/$(REPO).git; else \ + (cd /var/cache/distfiles/$(REPO).git; git fetch https://github.com/riscv/$(REPO)); fi;)' + +define build_tools_template +build-$(1)-tools: + @read -p "This will remove all existing data from $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX)$(subst riscv32,,$(1)). Type YES to continue: " reply && [[ "$$$$reply" == [Yy][Ee][Ss] || "$$$$reply" == [Yy] ]] + sudo bash -c "set -ex; rm -rf $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX)$(subst riscv32,,$(1)); mkdir -p $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX)$(subst riscv32,,$(1)); chown $$$${USER}: $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX)$(subst riscv32,,$(1))" + +$(MAKE) build-$(1)-tools-bh + +build-$(1)-tools-bh: + +set -ex; $(GIT_ENV); \ + if [ -d /var/cache/distfiles/riscv-gnu-toolchain.git ]; then reference_riscv_gnu_toolchain="--reference /var/cache/distfiles/riscv-gnu-toolchain.git"; else reference_riscv_gnu_toolchain=""; fi; \ + if [ -d /var/cache/distfiles/riscv-binutils-gdb.git ]; then reference_riscv_binutils_gdb="--reference /var/cache/distfiles/riscv-binutils-gdb.git"; else reference_riscv_binutils_gdb=""; fi; \ + if [ -d /var/cache/distfiles/riscv-gcc.git ]; then reference_riscv_gcc="--reference /var/cache/distfiles/riscv-gcc.git"; else reference_riscv_gcc=""; fi; \ + if [ -d /var/cache/distfiles/riscv-glibc.git ]; then reference_riscv_glibc="--reference /var/cache/distfiles/riscv-glibc.git"; else reference_riscv_glibc=""; fi; \ + if [ -d /var/cache/distfiles/riscv-newlib.git ]; then reference_riscv_newlib="--reference /var/cache/distfiles/riscv-newlib.git"; else reference_riscv_newlib=""; fi; \ + rm -rf riscv-gnu-toolchain-$(1); git clone $$$$reference_riscv_gnu_toolchain https://github.com/riscv/riscv-gnu-toolchain riscv-gnu-toolchain-$(1); \ + cd riscv-gnu-toolchain-$(1); git checkout $(RISCV_GNU_TOOLCHAIN_GIT_REVISION); \ + git submodule update --init $$$$reference_riscv_binutils_gdb riscv-binutils; \ + git submodule update --init $$$$reference_riscv_binutils_gdb riscv-gdb; \ + git submodule update --init $$$$reference_riscv_gcc riscv-gcc; \ + git submodule update --init $$$$reference_riscv_glibc riscv-glibc; \ + git submodule update --init $$$$reference_riscv_newlib riscv-newlib; \ + mkdir build; cd build; ../configure --with-arch=$(2) --prefix=$(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX)$(subst riscv32,,$(1)); make + +.PHONY: build-$(1)-tools +endef + +$(eval $(call build_tools_template,riscv32i,rv32i)) +$(eval $(call build_tools_template,riscv32ic,rv32ic)) +$(eval $(call build_tools_template,riscv32im,rv32im)) +$(eval $(call build_tools_template,riscv32imc,rv32imc)) + +build-tools: + @echo "This will remove all existing data from $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX)i, $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX)ic, $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX)im, and $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX)imc." + @read -p "Type YES to continue: " reply && [[ "$$reply" == [Yy][Ee][Ss] || "$$reply" == [Yy] ]] + sudo bash -c "set -ex; rm -rf $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX){i,ic,im,imc}; mkdir -p $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX){i,ic,im,imc}; chown $${USER}: $(RISCV_GNU_TOOLCHAIN_INSTALL_PREFIX){i,ic,im,imc}" + +$(MAKE) build-riscv32i-tools-bh + +$(MAKE) build-riscv32ic-tools-bh + +$(MAKE) build-riscv32im-tools-bh + +$(MAKE) build-riscv32imc-tools-bh + +toc: + gawk '/^-+$$/ { y=tolower(x); gsub("[^a-z0-9]+", "-", y); gsub("-$$", "", y); printf("- [%s](#%s)\n", x, y); } { x=$$0; }' README.md + +clean: + rm -rf riscv-gnu-toolchain-riscv32i riscv-gnu-toolchain-riscv32ic \ + riscv-gnu-toolchain-riscv32im riscv-gnu-toolchain-riscv32imc + rm -vrf $(FIRMWARE_OBJS) $(TEST_OBJS) check.smt2 check.vcd synth.v synth.log \ + firmware/firmware.elf firmware/firmware.bin firmware/firmware.hex firmware/firmware.map \ + testbench.vvp testbench_sp.vvp testbench_synth.vvp testbench_ez.vvp \ + testbench_rvf.vvp testbench_wb.vvp testbench.vcd testbench.trace \ + testbench_verilator testbench_verilator_dir + +.PHONY: test test_vcd test_sp test_axi test_wb test_wb_vcd test_ez test_ez_vcd test_synth download-tools build-tools toc clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..bcd8fa1 --- /dev/null +++ b/README.md @@ -0,0 +1,738 @@ + +PicoRV32 - A Size-Optimized RISC-V CPU +====================================== + +PicoRV32 is a CPU core that implements the [RISC-V RV32IMC Instruction Set](http://riscv.org/). +It can be configured as RV32E, RV32I, RV32IC, RV32IM, or RV32IMC core, and optionally +contains a built-in interrupt controller. + +Tools (gcc, binutils, etc..) can be obtained via the [RISC-V Website](https://riscv.org/software-status/). +The examples bundled with PicoRV32 expect various RV32 toolchains to be installed in `/opt/riscv32i[m][c]`. See +the [build instructions below](#building-a-pure-rv32i-toolchain) for details. +Many Linux distributions now include the tools for RISC-V (for example +Ubuntu 20.04 has `gcc-riscv64-unknown-elf`). To compile using those set +`TOOLCHAIN_PREFIX` accordingly (eg. `make TOOLCHAIN_PREFIX=riscv64-unknown-elf-`). + +PicoRV32 is free and open hardware licensed under the [ISC license](http://en.wikipedia.org/wiki/ISC_license) +(a license that is similar in terms to the MIT license or the 2-clause BSD license). + +#### Table of Contents + +- [Features and Typical Applications](#features-and-typical-applications) +- [Files in this Repository](#files-in-this-repository) +- [Verilog Module Parameters](#verilog-module-parameters) +- [Cycles per Instruction Performance](#cycles-per-instruction-performance) +- [PicoRV32 Native Memory Interface](#picorv32-native-memory-interface) +- [Pico Co-Processor Interface (PCPI)](#pico-co-processor-interface-pcpi) +- [Custom Instructions for IRQ Handling](#custom-instructions-for-irq-handling) +- [Building a pure RV32I Toolchain](#building-a-pure-rv32i-toolchain) +- [Linking binaries with newlib for PicoRV32](#linking-binaries-with-newlib-for-picorv32) +- [Evaluation: Timing and Utilization on Xilinx 7-Series FPGAs](#evaluation-timing-and-utilization-on-xilinx-7-series-fpgas) + + +Features and Typical Applications +--------------------------------- + +- Small (750-2000 LUTs in 7-Series Xilinx Architecture) +- High fmax (250-450 MHz on 7-Series Xilinx FPGAs) +- Selectable native memory interface or AXI4-Lite master +- Optional IRQ support (using a simple custom ISA) +- Optional Co-Processor Interface + +This CPU is meant to be used as auxiliary processor in FPGA designs and ASICs. Due +to its high fmax it can be integrated in most existing designs without crossing +clock domains. When operated on a lower frequency, it will have a lot of timing +slack and thus can be added to a design without compromising timing closure. + +For even smaller size it is possible disable support for registers `x16`..`x31` as +well as `RDCYCLE[H]`, `RDTIME[H]`, and `RDINSTRET[H]` instructions, turning the +processor into an RV32E core. + +Furthermore it is possible to choose between a dual-port and a single-port +register file implementation. The former provides better performance while +the latter results in a smaller core. + +*Note: In architectures that implement the register file in dedicated memory +resources, such as many FPGAs, disabling the 16 upper registers and/or +disabling the dual-port register file may not further reduce the core size.* + +The core exists in three variations: `picorv32`, `picorv32_axi` and `picorv32_wb`. +The first provides a simple native memory interface, that is easy to use in simple +environments. `picorv32_axi` provides an AXI-4 Lite Master interface that can +easily be integrated with existing systems that are already using the AXI +standard. `picorv32_wb` provides a Wishbone master interface. + +A separate core `picorv32_axi_adapter` is provided to bridge between the native +memory interface and AXI4. This core can be used to create custom cores that +include one or more PicoRV32 cores together with local RAM, ROM, and +memory-mapped peripherals, communicating with each other using the native +interface, and communicating with the outside world via AXI4. + +The optional IRQ feature can be used to react to events from the outside, implement +fault handlers, or catch instructions from a larger ISA and emulate them in +software. + +The optional Pico Co-Processor Interface (PCPI) can be used to implement +non-branching instructions in an external coprocessor. Implementations +of PCPI cores that implement the M Standard Extension instructions +`MUL[H[SU|U]]` and `DIV[U]/REM[U]` are included in this package. + + +Files in this Repository +------------------------ + +#### README.md + +You are reading it right now. + +#### picorv32.v + +This Verilog file contains the following Verilog modules: + +| Module | Description | +| ------------------------ | --------------------------------------------------------------------- | +| `picorv32` | The PicoRV32 CPU | +| `picorv32_axi` | The version of the CPU with AXI4-Lite interface | +| `picorv32_axi_adapter` | Adapter from PicoRV32 Memory Interface to AXI4-Lite | +| `picorv32_wb` | The version of the CPU with Wishbone Master interface | +| `picorv32_pcpi_mul` | A PCPI core that implements the `MUL[H[SU\|U]]` instructions | +| `picorv32_pcpi_fast_mul` | A version of `picorv32_pcpi_fast_mul` using a single cycle multiplier | +| `picorv32_pcpi_div` | A PCPI core that implements the `DIV[U]/REM[U]` instructions | + +Simply copy this file into your project. + +#### Makefile and testbenches + +A basic test environment. Run `make test` to run the standard test bench (`testbench.v`) +in the standard configurations. There are other test benches and configurations. See +the `test_*` make target in the Makefile for details. + +Run `make test_ez` to run `testbench_ez.v`, a very simple test bench that does +not require an external firmware .hex file. This can be useful in environments +where the RISC-V compiler toolchain is not available. + +*Note: The test bench is using Icarus Verilog. However, Icarus Verilog 0.9.7 +(the latest release at the time of writing) has a few bugs that prevent the +test bench from running. Upgrade to the latest github master of Icarus Verilog +to run the test bench.* + +#### firmware/ + +A simple test firmware. This runs the basic tests from `tests/`, some C code, tests IRQ +handling and the multiply PCPI core. + +All the code in `firmware/` is in the public domain. Simply copy whatever you can use. + +#### tests/ + +Simple instruction-level tests from [riscv-tests](https://github.com/riscv/riscv-tests). + +#### dhrystone/ + +Another simple test firmware that runs the Dhrystone benchmark. + +#### picosoc/ + +A simple example SoC using PicoRV32 that can execute code directly from a +memory mapped SPI flash. + +#### scripts/ + +Various scripts and examples for different (synthesis) tools and hardware architectures. + + +Verilog Module Parameters +------------------------- + +The following Verilog module parameters can be used to configure the PicoRV32 +core. + +#### ENABLE_COUNTERS (default = 1) + +This parameter enables support for the `RDCYCLE[H]`, `RDTIME[H]`, and +`RDINSTRET[H]` instructions. This instructions will cause a hardware +trap (like any other unsupported instruction) if `ENABLE_COUNTERS` is set to zero. + +*Note: Strictly speaking the `RDCYCLE[H]`, `RDTIME[H]`, and `RDINSTRET[H]` +instructions are not optional for an RV32I core. But chances are they are not +going to be missed after the application code has been debugged and profiled. +This instructions are optional for an RV32E core.* + +#### ENABLE_COUNTERS64 (default = 1) + +This parameter enables support for the `RDCYCLEH`, `RDTIMEH`, and `RDINSTRETH` +instructions. If this parameter is set to 0, and `ENABLE_COUNTERS` is set to 1, +then only the `RDCYCLE`, `RDTIME`, and `RDINSTRET` instructions are available. + +#### ENABLE_REGS_16_31 (default = 1) + +This parameter enables support for registers the `x16`..`x31`. The RV32E ISA +excludes this registers. However, the RV32E ISA spec requires a hardware trap +for when code tries to access this registers. This is not implemented in PicoRV32. + +#### ENABLE_REGS_DUALPORT (default = 1) + +The register file can be implemented with two or one read ports. A dual ported +register file improves performance a bit, but can also increase the size of +the core. + +#### LATCHED_MEM_RDATA (default = 0) + +Set this to 1 if the `mem_rdata` is kept stable by the external circuit after a +transaction. In the default configuration the PicoRV32 core only expects the +`mem_rdata` input to be valid in the cycle with `mem_valid && mem_ready` and +latches the value internally. + +This parameter is only available for the `picorv32` core. In the +`picorv32_axi` and `picorv32_wb` core this is implicitly set to 0. + +#### TWO_STAGE_SHIFT (default = 1) + +By default shift operations are performed in two stages: first shifts in units +of 4 bits and then shifts in units of 1 bit. This speeds up shift operations, +but adds additional hardware. Set this parameter to 0 to disable the two-stage +shift to further reduce the size of the core. + +#### BARREL_SHIFTER (default = 0) + +By default shift operations are performed by successively shifting by a +small amount (see `TWO_STAGE_SHIFT` above). With this option set, a barrel +shifter is used instead. + +#### TWO_CYCLE_COMPARE (default = 0) + +This relaxes the longest data path a bit by adding an additional FF stage +at the cost of adding an additional clock cycle delay to the conditional +branch instructions. + +*Note: Enabling this parameter will be most effective when retiming (aka +"register balancing") is enabled in the synthesis flow.* + +#### TWO_CYCLE_ALU (default = 0) + +This adds an additional FF stage in the ALU data path, improving timing +at the cost of an additional clock cycle for all instructions that use +the ALU. + +*Note: Enabling this parameter will be most effective when retiming (aka +"register balancing") is enabled in the synthesis flow.* + +#### COMPRESSED_ISA (default = 0) + +This enables support for the RISC-V Compressed Instruction Set. + +#### CATCH_MISALIGN (default = 1) + +Set this to 0 to disable the circuitry for catching misaligned memory +accesses. + +#### CATCH_ILLINSN (default = 1) + +Set this to 0 to disable the circuitry for catching illegal instructions. + +The core will still trap on `EBREAK` instructions with this option +set to 0. With IRQs enabled, an `EBREAK` normally triggers an IRQ 1. With +this option set to 0, an `EBREAK` will trap the processor without +triggering an interrupt. + +#### ENABLE_PCPI (default = 0) + +Set this to 1 to enable the Pico Co-Processor Interface (PCPI). + +#### ENABLE_MUL (default = 0) + +This parameter internally enables PCPI and instantiates the `picorv32_pcpi_mul` +core that implements the `MUL[H[SU|U]]` instructions. The external PCPI +interface only becomes functional when ENABLE_PCPI is set as well. + +#### ENABLE_FAST_MUL (default = 0) + +This parameter internally enables PCPI and instantiates the `picorv32_pcpi_fast_mul` +core that implements the `MUL[H[SU|U]]` instructions. The external PCPI +interface only becomes functional when ENABLE_PCPI is set as well. + +If both ENABLE_MUL and ENABLE_FAST_MUL are set then the ENABLE_MUL setting +will be ignored and the fast multiplier core will be instantiated. + +#### ENABLE_DIV (default = 0) + +This parameter internally enables PCPI and instantiates the `picorv32_pcpi_div` +core that implements the `DIV[U]/REM[U]` instructions. The external PCPI +interface only becomes functional when ENABLE_PCPI is set as well. + +#### ENABLE_IRQ (default = 0) + +Set this to 1 to enable IRQs. (see "Custom Instructions for IRQ Handling" below +for a discussion of IRQs) + +#### ENABLE_IRQ_QREGS (default = 1) + +Set this to 0 to disable support for the `getq` and `setq` instructions. Without +the q-registers, the irq return address will be stored in x3 (gp) and the IRQ +bitmask in x4 (tp), the global pointer and thread pointer registers according +to the RISC-V ABI. Code generated from ordinary C code will not interact with +those registers. + +Support for q-registers is always disabled when ENABLE_IRQ is set to 0. + +#### ENABLE_IRQ_TIMER (default = 1) + +Set this to 0 to disable support for the `timer` instruction. + +Support for the timer is always disabled when ENABLE_IRQ is set to 0. + +#### ENABLE_TRACE (default = 0) + +Produce an execution trace using the `trace_valid` and `trace_data` output ports. +For a demontration of this feature run `make test_vcd` to create a trace file +and then run `python3 showtrace.py testbench.trace firmware/firmware.elf` to decode +it. + +#### REGS_INIT_ZERO (default = 0) + +Set this to 1 to initialize all registers to zero (using a Verilog `initial` block). +This can be useful for simulation or formal verification. + +#### MASKED_IRQ (default = 32'h 0000_0000) + +A 1 bit in this bitmask corresponds to a permanently disabled IRQ. + +#### LATCHED_IRQ (default = 32'h ffff_ffff) + +A 1 bit in this bitmask indicates that the corresponding IRQ is "latched", i.e. +when the IRQ line is high for only one cycle, the interrupt will be marked as +pending and stay pending until the interrupt handler is called (aka "pulse +interrupts" or "edge-triggered interrupts"). + +Set a bit in this bitmask to 0 to convert an interrupt line to operate +as "level sensitive" interrupt. + +#### PROGADDR_RESET (default = 32'h 0000_0000) + +The start address of the program. + +#### PROGADDR_IRQ (default = 32'h 0000_0010) + +The start address of the interrupt handler. + +#### STACKADDR (default = 32'h ffff_ffff) + +When this parameter has a value different from 0xffffffff, then register `x2` (the +stack pointer) is initialized to this value on reset. (All other registers remain +uninitialized.) Note that the RISC-V calling convention requires the stack pointer +to be aligned on 16 bytes boundaries (4 bytes for the RV32I soft float calling +convention). + + +Cycles per Instruction Performance +---------------------------------- + +*A short reminder: This core is optimized for size and fmax, not performance.* + +Unless stated otherwise, the following numbers apply to a PicoRV32 with +ENABLE_REGS_DUALPORT active and connected to a memory that can accommodate +requests within one clock cycle. + +The average Cycles per Instruction (CPI) is approximately 4, depending on the mix of +instructions in the code. The CPI numbers for the individual instructions can +be found in the table below. The column "CPI (SP)" contains the CPI numbers for +a core built without ENABLE_REGS_DUALPORT. + +| Instruction | CPI | CPI (SP) | +| ---------------------| ----:| --------:| +| direct jump (jal) | 3 | 3 | +| ALU reg + immediate | 3 | 3 | +| ALU reg + reg | 3 | 4 | +| branch (not taken) | 3 | 4 | +| memory load | 5 | 5 | +| memory store | 5 | 6 | +| branch (taken) | 5 | 6 | +| indirect jump (jalr) | 6 | 6 | +| shift operations | 4-14 | 4-15 | + +When `ENABLE_MUL` is activated, then a `MUL` instruction will execute +in 40 cycles and a `MULH[SU|U]` instruction will execute in 72 cycles. + +When `ENABLE_DIV` is activated, then a `DIV[U]/REM[U]` instruction will +execute in 40 cycles. + +When `BARREL_SHIFTER` is activated, a shift operation takes as long as +any other ALU operation. + +The following dhrystone benchmark results are for a core with enabled +`ENABLE_FAST_MUL`, `ENABLE_DIV`, and `BARREL_SHIFTER` options. + +Dhrystone benchmark results: 0.516 DMIPS/MHz (908 Dhrystones/Second/MHz) + +For the Dhrystone benchmark the average CPI is 4.100. + +Without using the look-ahead memory interface (usually required for max +clock speed), this results drop to 0.305 DMIPS/MHz and 5.232 CPI. + + +PicoRV32 Native Memory Interface +-------------------------------- + +The native memory interface of PicoRV32 is a simple valid-ready interface +that can run one memory transfer at a time: + + output mem_valid + output mem_instr + input mem_ready + + output [31:0] mem_addr + output [31:0] mem_wdata + output [ 3:0] mem_wstrb + input [31:0] mem_rdata + +The core initiates a memory transfer by asserting `mem_valid`. The valid +signal stays high until the peer asserts `mem_ready`. All core outputs +are stable over the `mem_valid` period. If the memory transfer is an +instruction fetch, the core asserts `mem_instr`. + +#### Read Transfer + +In a read transfer `mem_wstrb` has the value 0 and `mem_wdata` is unused. + +The memory reads the address `mem_addr` and makes the read value available on +`mem_rdata` in the cycle `mem_ready` is high. + +There is no need for an external wait cycle. The memory read can be implemented +asynchronously with `mem_ready` going high in the same cycle as `mem_valid`, or +`mem_ready` being tied to constant 1. + +#### Write Transfer + +In a write transfer `mem_wstrb` is not 0 and `mem_rdata` is unused. The memory +write the data at `mem_wdata` to the address `mem_addr` and acknowledges the +transfer by asserting `mem_ready`. + +The 4 bits of `mem_wstrb` are write enables for the four bytes in the addressed +word. Only the 8 values `0000`, `1111`, `1100`, `0011`, `1000`, `0100`, `0010`, +and `0001` are possible, i.e. no write, write 32 bits, write upper 16 bits, +write lower 16, or write a single byte respectively. + +There is no need for an external wait cycle. The memory can acknowledge the +write immediately with `mem_ready` going high in the same cycle as +`mem_valid`, or `mem_ready` being tied to constant 1. + +#### Look-Ahead Interface + +The PicoRV32 core also provides a "Look-Ahead Memory Interface" that provides +all information about the next memory transfer one clock cycle earlier than the +normal interface. + + output mem_la_read + output mem_la_write + output [31:0] mem_la_addr + output [31:0] mem_la_wdata + output [ 3:0] mem_la_wstrb + +In the clock cycle before `mem_valid` goes high, this interface will output a +pulse on `mem_la_read` or `mem_la_write` to indicate the start of a read or +write transaction in the next clock cycle. + +*Note: The signals `mem_la_read`, `mem_la_write`, and `mem_la_addr` are driven +by combinatorial circuits within the PicoRV32 core. It might be harder to +achieve timing closure with the look-ahead interface than with the normal +memory interface described above.* + + +Pico Co-Processor Interface (PCPI) +---------------------------------- + +The Pico Co-Processor Interface (PCPI) can be used to implement non-branching +instructions in external cores: + + output pcpi_valid + output [31:0] pcpi_insn + output [31:0] pcpi_rs1 + output [31:0] pcpi_rs2 + input pcpi_wr + input [31:0] pcpi_rd + input pcpi_wait + input pcpi_ready + +When an unsupported instruction is encountered and the PCPI feature is +activated (see ENABLE_PCPI above), then `pcpi_valid` is asserted, the +instruction word itself is output on `pcpi_insn`, the `rs1` and `rs2` +fields are decoded and the values in those registers are output +on `pcpi_rs1` and `pcpi_rs2`. + +An external PCPI core can then decode the instruction, execute it, and assert +`pcpi_ready` when execution of the instruction is finished. Optionally a +result value can be written to `pcpi_rd` and `pcpi_wr` asserted. The +PicoRV32 core will then decode the `rd` field of the instruction and +write the value from `pcpi_rd` to the respective register. + +When no external PCPI core acknowledges the instruction within 16 clock +cycles, then an illegal instruction exception is raised and the respective +interrupt handler is called. A PCPI core that needs more than a couple of +cycles to execute an instruction, should assert `pcpi_wait` as soon as +the instruction has been decoded successfully and keep it asserted until +it asserts `pcpi_ready`. This will prevent the PicoRV32 core from raising +an illegal instruction exception. + + +Custom Instructions for IRQ Handling +------------------------------------ + +*Note: The IRQ handling features in PicoRV32 do not follow the RISC-V +Privileged ISA specification. Instead a small set of very simple custom +instructions is used to implement IRQ handling with minimal hardware +overhead.* + +The following custom instructions are only supported when IRQs are enabled +via the `ENABLE_IRQ` parameter (see above). + +The PicoRV32 core has a built-in interrupt controller with 32 interrupt inputs. An +interrupt can be triggered by asserting the corresponding bit in the `irq` +input of the core. + +When the interrupt handler is started, the `eoi` End Of Interrupt (EOI) signals +for the handled interrupts go high. The `eoi` signals go low again when the +interrupt handler returns. + +The IRQs 0-2 can be triggered internally by the following built-in interrupt sources: + +| IRQ | Interrupt Source | +| ---:| ------------------------------------| +| 0 | Timer Interrupt | +| 1 | EBREAK/ECALL or Illegal Instruction | +| 2 | BUS Error (Unalign Memory Access) | + +This interrupts can also be triggered by external sources, such as co-processors +connected via PCPI. + +The core has 4 additional 32-bit registers `q0 .. q3` that are used for IRQ +handling. When the IRQ handler is called, the register `q0` contains the return +address and `q1` contains a bitmask of all IRQs to be handled. This means one +call to the interrupt handler needs to service more than one IRQ when more than +one bit is set in `q1`. + +When support for compressed instructions is enabled, then the LSB of q0 is set +when the interrupted instruction is a compressed instruction. This can be used if +the IRQ handler wants to decode the interrupted instruction. + +Registers `q2` and `q3` are uninitialized and can be used as temporary storage +when saving/restoring register values in the IRQ handler. + +All of the following instructions are encoded under the `custom0` opcode. The f3 +and rs2 fields are ignored in all this instructions. + +See [firmware/custom_ops.S](firmware/custom_ops.S) for GNU assembler macros that +implement mnemonics for this instructions. + +See [firmware/start.S](firmware/start.S) for an example implementation of an +interrupt handler assembler wrapper, and [firmware/irq.c](firmware/irq.c) for +the actual interrupt handler. + +#### getq rd, qs + +This instruction copies the value from a q-register to a general-purpose +register. + + 0000000 ----- 000XX --- XXXXX 0001011 + f7 rs2 qs f3 rd opcode + +Example: + + getq x5, q2 + +#### setq qd, rs + +This instruction copies the value from a general-purpose register to a +q-register. + + 0000001 ----- XXXXX --- 000XX 0001011 + f7 rs2 rs f3 qd opcode + +Example: + + setq q2, x5 + +#### retirq + +Return from interrupt. This instruction copies the value from `q0` +to the program counter and re-enables interrupts. + + 0000010 ----- 00000 --- 00000 0001011 + f7 rs2 rs f3 rd opcode + +Example: + + retirq + +#### maskirq + +The "IRQ Mask" register contains a bitmask of masked (disabled) interrupts. +This instruction writes a new value to the irq mask register and reads the old +value. + + 0000011 ----- XXXXX --- XXXXX 0001011 + f7 rs2 rs f3 rd opcode + +Example: + + maskirq x1, x2 + +The processor starts with all interrupts disabled. + +An illegal instruction or bus error while the illegal instruction or bus error +interrupt is disabled will cause the processor to halt. + +#### waitirq + +Pause execution until an interrupt becomes pending. The bitmask of pending IRQs +is written to `rd`. + + 0000100 ----- 00000 --- XXXXX 0001011 + f7 rs2 rs f3 rd opcode + +Example: + + waitirq x1 + +#### timer + +Reset the timer counter to a new value. The counter counts down clock cycles and +triggers the timer interrupt when transitioning from 1 to 0. Setting the +counter to zero disables the timer. The old value of the counter is written to +`rd`. + + 0000101 ----- XXXXX --- XXXXX 0001011 + f7 rs2 rs f3 rd opcode + +Example: + + timer x1, x2 + + +Building a pure RV32I Toolchain +------------------------------- + +TL;DR: Run the following commands to build the complete toolchain: + + make download-tools + make -j$(nproc) build-tools + +The default settings in the [riscv-tools](https://github.com/riscv/riscv-tools) build +scripts will build a compiler, assembler and linker that can target any RISC-V ISA, +but the libraries are built for RV32G and RV64G targets. Follow the instructions +below to build a complete toolchain (including libraries) that target a pure RV32I +CPU. + +The following commands will build the RISC-V GNU toolchain and libraries for a +pure RV32I target, and install it in `/opt/riscv32i`: + + # Ubuntu packages needed: + sudo apt-get install autoconf automake autotools-dev curl libmpc-dev \ + libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo \ + gperf libtool patchutils bc zlib1g-dev git libexpat1-dev + + sudo mkdir /opt/riscv32i + sudo chown $USER /opt/riscv32i + + git clone https://github.com/riscv/riscv-gnu-toolchain riscv-gnu-toolchain-rv32i + cd riscv-gnu-toolchain-rv32i + git checkout 411d134 + git submodule update --init --recursive + + mkdir build; cd build + ../configure --with-arch=rv32i --prefix=/opt/riscv32i + make -j$(nproc) + +The commands will all be named using the prefix `riscv32-unknown-elf-`, which +makes it easy to install them side-by-side with the regular riscv-tools (those +are using the name prefix `riscv64-unknown-elf-` by default). + +Alternatively you can simply use one of the following make targets from PicoRV32's +Makefile to build a `RV32I[M][C]` toolchain. You still need to install all +prerequisites, as described above. Then run any of the following commands in the +PicoRV32 source directory: + +| Command | Install Directory | ISA | +|:---------------------------------------- |:------------------ |:-------- | +| `make -j$(nproc) build-riscv32i-tools` | `/opt/riscv32i/` | `RV32I` | +| `make -j$(nproc) build-riscv32ic-tools` | `/opt/riscv32ic/` | `RV32IC` | +| `make -j$(nproc) build-riscv32im-tools` | `/opt/riscv32im/` | `RV32IM` | +| `make -j$(nproc) build-riscv32imc-tools` | `/opt/riscv32imc/` | `RV32IMC` | + +Or simply run `make -j$(nproc) build-tools` to build and install all four tool chains. + +By default calling any of those make targets will (re-)download the toolchain +sources. Run `make download-tools` to download the sources to `/var/cache/distfiles/` +once in advance. + +*Note: These instructions are for git rev 411d134 (2018-02-14) of riscv-gnu-toolchain.* + + +Linking binaries with newlib for PicoRV32 +----------------------------------------- + +The tool chains (see last section for install instructions) come with a version of +the newlib C standard library. + +Use the linker script [firmware/riscv.ld](firmware/riscv.ld) for linking binaries +against the newlib library. Using this linker script will create a binary that +has its entry point at 0x10000. (The default linker script does not have a static +entry point, thus a proper ELF loader would be needed that can determine the +entry point at runtime while loading the program.) + +Newlib comes with a few syscall stubs. You need to provide your own implementation +of those syscalls and link your program with this implementation, overwriting the +default stubs from newlib. See `syscalls.c` in [scripts/cxxdemo/](scripts/cxxdemo/) +for an example of how to do that. + + +Evaluation: Timing and Utilization on Xilinx 7-Series FPGAs +----------------------------------------------------------- + +The following evaluations have been performed with Vivado 2017.3. + +#### Timing on Xilinx 7-Series FPGAs + +The `picorv32_axi` module with enabled `TWO_CYCLE_ALU` has been placed and +routed for Xilinx Artix-7T, Kintex-7T, Virtex-7T, Kintex UltraScale, and Virtex +UltraScale devices in all speed grades. A binary search is used to find the +shortest clock period for which the design meets timing. + +See `make table.txt` in [scripts/vivado/](scripts/vivado/). + +| Device | Device | Speedgrade | Clock Period (Freq.) | +|:------------------------- |:---------------------|:----------:| --------------------:| +| Xilinx Kintex-7T | xc7k70t-fbg676-2 | -2 | 2.4 ns (416 MHz) | +| Xilinx Kintex-7T | xc7k70t-fbg676-3 | -3 | 2.2 ns (454 MHz) | +| Xilinx Virtex-7T | xc7v585t-ffg1761-2 | -2 | 2.3 ns (434 MHz) | +| Xilinx Virtex-7T | xc7v585t-ffg1761-3 | -3 | 2.2 ns (454 MHz) | +| Xilinx Kintex UltraScale | xcku035-fbva676-2-e | -2 | 2.0 ns (500 MHz) | +| Xilinx Kintex UltraScale | xcku035-fbva676-3-e | -3 | 1.8 ns (555 MHz) | +| Xilinx Virtex UltraScale | xcvu065-ffvc1517-2-e | -2 | 2.1 ns (476 MHz) | +| Xilinx Virtex UltraScale | xcvu065-ffvc1517-3-e | -3 | 2.0 ns (500 MHz) | +| Xilinx Kintex UltraScale+ | xcku3p-ffva676-2-e | -2 | 1.4 ns (714 MHz) | +| Xilinx Kintex UltraScale+ | xcku3p-ffva676-3-e | -3 | 1.3 ns (769 MHz) | +| Xilinx Virtex UltraScale+ | xcvu3p-ffvc1517-2-e | -2 | 1.5 ns (666 MHz) | +| Xilinx Virtex UltraScale+ | xcvu3p-ffvc1517-3-e | -3 | 1.4 ns (714 MHz) | + +#### Utilization on Xilinx 7-Series FPGAs + +The following table lists the resource utilization in area-optimized synthesis +for the following three cores: + +- **PicoRV32 (small):** The `picorv32` module without counter instructions, + without two-stage shifts, with externally latched `mem_rdata`, and without + catching of misaligned memory accesses and illegal instructions. + +- **PicoRV32 (regular):** The `picorv32` module in its default configuration. + +- **PicoRV32 (large):** The `picorv32` module with enabled PCPI, IRQ, MUL, + DIV, BARREL_SHIFTER, and COMPRESSED_ISA features. + +See `make area` in [scripts/vivado/](scripts/vivado/). + +| Core Variant | Slice LUTs | LUTs as Memory | Slice Registers | +|:------------------ | ----------:| --------------:| ---------------:| +| PicoRV32 (small) | 761 | 48 | 442 | +| PicoRV32 (regular) | 917 | 48 | 583 | +| PicoRV32 (large) | 2019 | 88 | 1085 | + diff --git a/dhrystone/Makefile b/dhrystone/Makefile new file mode 100644 index 0000000..89cb110 --- /dev/null +++ b/dhrystone/Makefile @@ -0,0 +1,69 @@ +USE_MYSTDLIB = 0 +OBJS = dhry_1.o dhry_2.o stdlib.o +CFLAGS = -MD -O3 -mabi=ilp32 -march=rv32im -DTIME -DRISCV +TOOLCHAIN_PREFIX = /opt/riscv32im/bin/riscv32-unknown-elf- + +ifeq ($(USE_MYSTDLIB),1) +CFLAGS += -DUSE_MYSTDLIB -ffreestanding -nostdlib +OBJS += start.o +else +OBJS += syscalls.o +endif + +test: testbench.vvp dhry.hex + vvp -N testbench.vvp + +test_trace: testbench.vvp dhry.hex + vvp -N $< +trace + python3 ../showtrace.py testbench.trace dhry.elf > testbench.ins + +test_nola: testbench_nola.vvp dhry.hex + vvp -N testbench_nola.vvp + +timing: timing.txt + grep '^##' timing.txt | gawk 'x != "" {print x,$$3-y;} {x=$$2;y=$$3;}' | sort | uniq -c | \ + gawk '{printf("%03d-%-7s %2d %-8s (%d)\n",$$3,$$2,$$3,$$2,$$1);}' | sort | cut -c13- + +timing.txt: timing.vvp dhry.hex + vvp -N timing.vvp > timing.txt + +testbench.vvp: testbench.v ../picorv32.v + iverilog -o testbench.vvp testbench.v ../picorv32.v + chmod -x testbench.vvp + +testbench_nola.vvp: testbench_nola.v ../picorv32.v + iverilog -o testbench_nola.vvp testbench_nola.v ../picorv32.v + chmod -x testbench_nola.vvp + +timing.vvp: testbench.v ../picorv32.v + iverilog -o timing.vvp -DTIMING testbench.v ../picorv32.v + chmod -x timing.vvp + +dhry.hex: dhry.elf + $(TOOLCHAIN_PREFIX)objcopy -O verilog $< $@ + +ifeq ($(USE_MYSTDLIB),1) +dhry.elf: $(OBJS) sections.lds + $(TOOLCHAIN_PREFIX)gcc $(CFLAGS) -Wl,-Bstatic,-T,sections.lds,-Map,dhry.map,--strip-debug -o $@ $(OBJS) -lgcc + chmod -x $@ +else +dhry.elf: $(OBJS) + $(TOOLCHAIN_PREFIX)gcc $(CFLAGS) -Wl,-Bstatic,-T,../firmware/riscv.ld,-Map,dhry.map,--strip-debug -o $@ $(OBJS) -lgcc -lc + chmod -x $@ +endif + +%.o: %.c + $(TOOLCHAIN_PREFIX)gcc -c $(CFLAGS) $< + +%.o: %.S + $(TOOLCHAIN_PREFIX)gcc -c $(CFLAGS) $< + +dhry_1.o dhry_2.o: CFLAGS += -Wno-implicit-int -Wno-implicit-function-declaration + +clean: + rm -rf *.o *.d dhry.elf dhry.map dhry.bin dhry.hex testbench.vvp testbench.vcd timing.vvp timing.txt testbench_nola.vvp + +.PHONY: test clean + +-include *.d + diff --git a/dhrystone/README b/dhrystone/README new file mode 100644 index 0000000..e08b25b --- /dev/null +++ b/dhrystone/README @@ -0,0 +1 @@ +The Dhrystone benchmark and a verilog testbench to run it. diff --git a/dhrystone/dhry.h b/dhrystone/dhry.h new file mode 100644 index 0000000..41f1495 --- /dev/null +++ b/dhrystone/dhry.h @@ -0,0 +1,428 @@ +/* + **************************************************************************** + * + * "DHRYSTONE" Benchmark Program + * ----------------------------- + * + * Version: C, Version 2.1 + * + * File: dhry.h (part 1 of 3) + * + * Date: May 25, 1988 + * + * Author: Reinhold P. Weicker + * Siemens AG, AUT E 51 + * Postfach 3220 + * 8520 Erlangen + * Germany (West) + * Phone: [+49]-9131-7-20330 + * (8-17 Central European Time) + * Usenet: ..!mcsun!unido!estevax!weicker + * + * Original Version (in Ada) published in + * "Communications of the ACM" vol. 27., no. 10 (Oct. 1984), + * pp. 1013 - 1030, together with the statistics + * on which the distribution of statements etc. is based. + * + * In this C version, the following C library functions are used: + * - strcpy, strcmp (inside the measurement loop) + * - printf, scanf (outside the measurement loop) + * In addition, Berkeley UNIX system calls "times ()" or "time ()" + * are used for execution time measurement. For measurements + * on other systems, these calls have to be changed. + * + * Collection of Results: + * Reinhold Weicker (address see above) and + * + * Rick Richardson + * PC Research. Inc. + * 94 Apple Orchard Drive + * Tinton Falls, NJ 07724 + * Phone: (201) 389-8963 (9-17 EST) + * Usenet: ...!uunet!pcrat!rick + * + * Please send results to Rick Richardson and/or Reinhold Weicker. + * Complete information should be given on hardware and software used. + * Hardware information includes: Machine type, CPU, type and size + * of caches; for microprocessors: clock frequency, memory speed + * (number of wait states). + * Software information includes: Compiler (and runtime library) + * manufacturer and version, compilation switches, OS version. + * The Operating System version may give an indication about the + * compiler; Dhrystone itself performs no OS calls in the measurement loop. + * + * The complete output generated by the program should be mailed + * such that at least some checks for correctness can be made. + * + *************************************************************************** + * + * History: This version C/2.1 has been made for two reasons: + * + * 1) There is an obvious need for a common C version of + * Dhrystone, since C is at present the most popular system + * programming language for the class of processors + * (microcomputers, minicomputers) where Dhrystone is used most. + * There should be, as far as possible, only one C version of + * Dhrystone such that results can be compared without + * restrictions. In the past, the C versions distributed + * by Rick Richardson (Version 1.1) and by Reinhold Weicker + * had small (though not significant) differences. + * + * 2) As far as it is possible without changes to the Dhrystone + * statistics, optimizing compilers should be prevented from + * removing significant statements. + * + * This C version has been developed in cooperation with + * Rick Richardson (Tinton Falls, NJ), it incorporates many + * ideas from the "Version 1.1" distributed previously by + * him over the UNIX network Usenet. + * I also thank Chaim Benedelac (National Semiconductor), + * David Ditzel (SUN), Earl Killian and John Mashey (MIPS), + * Alan Smith and Rafael Saavedra-Barrera (UC at Berkeley) + * for their help with comments on earlier versions of the + * benchmark. + * + * Changes: In the initialization part, this version follows mostly + * Rick Richardson's version distributed via Usenet, not the + * version distributed earlier via floppy disk by Reinhold Weicker. + * As a concession to older compilers, names have been made + * unique within the first 8 characters. + * Inside the measurement loop, this version follows the + * version previously distributed by Reinhold Weicker. + * + * At several places in the benchmark, code has been added, + * but within the measurement loop only in branches that + * are not executed. The intention is that optimizing compilers + * should be prevented from moving code out of the measurement + * loop, or from removing code altogether. Since the statements + * that are executed within the measurement loop have NOT been + * changed, the numbers defining the "Dhrystone distribution" + * (distribution of statements, operand types and locality) + * still hold. Except for sophisticated optimizing compilers, + * execution times for this version should be the same as + * for previous versions. + * + * Since it has proven difficult to subtract the time for the + * measurement loop overhead in a correct way, the loop check + * has been made a part of the benchmark. This does have + * an impact - though a very minor one - on the distribution + * statistics which have been updated for this version. + * + * All changes within the measurement loop are described + * and discussed in the companion paper "Rationale for + * Dhrystone version 2". + * + * Because of the self-imposed limitation that the order and + * distribution of the executed statements should not be + * changed, there are still cases where optimizing compilers + * may not generate code for some statements. To a certain + * degree, this is unavoidable for small synthetic benchmarks. + * Users of the benchmark are advised to check code listings + * whether code is generated for all statements of Dhrystone. + * + * Version 2.1 is identical to version 2.0 distributed via + * the UNIX network Usenet in March 1988 except that it corrects + * some minor deficiencies that were found by users of version 2.0. + * The only change within the measurement loop is that a + * non-executed "else" part was added to the "if" statement in + * Func_3, and a non-executed "else" part removed from Proc_3. + * + *************************************************************************** + * + * Defines: The following "Defines" are possible: + * -DREG=register (default: Not defined) + * As an approximation to what an average C programmer + * might do, the "register" storage class is applied + * (if enabled by -DREG=register) + * - for local variables, if they are used (dynamically) + * five or more times + * - for parameters if they are used (dynamically) + * six or more times + * Note that an optimal "register" strategy is + * compiler-dependent, and that "register" declarations + * do not necessarily lead to faster execution. + * -DNOSTRUCTASSIGN (default: Not defined) + * Define if the C compiler does not support + * assignment of structures. + * -DNOENUMS (default: Not defined) + * Define if the C compiler does not support + * enumeration types. + * -DTIMES (default) + * -DTIME + * The "times" function of UNIX (returning process times) + * or the "time" function (returning wallclock time) + * is used for measurement. + * For single user machines, "time ()" is adequate. For + * multi-user machines where you cannot get single-user + * access, use the "times ()" function. If you have + * neither, use a stopwatch in the dead of night. + * "printf"s are provided marking the points "Start Timer" + * and "Stop Timer". DO NOT use the UNIX "time(1)" + * command, as this will measure the total time to + * run this program, which will (erroneously) include + * the time to allocate storage (malloc) and to perform + * the initialization. + * -DHZ=nnn + * In Berkeley UNIX, the function "times" returns process + * time in 1/HZ seconds, with HZ = 60 for most systems. + * CHECK YOUR SYSTEM DESCRIPTION BEFORE YOU JUST APPLY + * A VALUE. + * + *************************************************************************** + * + * Compilation model and measurement (IMPORTANT): + * + * This C version of Dhrystone consists of three files: + * - dhry.h (this file, containing global definitions and comments) + * - dhry_1.c (containing the code corresponding to Ada package Pack_1) + * - dhry_2.c (containing the code corresponding to Ada package Pack_2) + * + * The following "ground rules" apply for measurements: + * - Separate compilation + * - No procedure merging + * - Otherwise, compiler optimizations are allowed but should be indicated + * - Default results are those without register declarations + * See the companion paper "Rationale for Dhrystone Version 2" for a more + * detailed discussion of these ground rules. + * + * For 16-Bit processors (e.g. 80186, 80286), times for all compilation + * models ("small", "medium", "large" etc.) should be given if possible, + * together with a definition of these models for the compiler system used. + * + ************************************************************************** + * + * Dhrystone (C version) statistics: + * + * [Comment from the first distribution, updated for version 2. + * Note that because of language differences, the numbers are slightly + * different from the Ada version.] + * + * The following program contains statements of a high level programming + * language (here: C) in a distribution considered representative: + * + * assignments 52 (51.0 %) + * control statements 33 (32.4 %) + * procedure, function calls 17 (16.7 %) + * + * 103 statements are dynamically executed. The program is balanced with + * respect to the three aspects: + * + * - statement type + * - operand type + * - operand locality + * operand global, local, parameter, or constant. + * + * The combination of these three aspects is balanced only approximately. + * + * 1. Statement Type: + * ----------------- number + * + * V1 = V2 9 + * (incl. V1 = F(..) + * V = Constant 12 + * Assignment, 7 + * with array element + * Assignment, 6 + * with record component + * -- + * 34 34 + * + * X = Y +|-|"&&"|"|" Z 5 + * X = Y +|-|"==" Constant 6 + * X = X +|- 1 3 + * X = Y *|/ Z 2 + * X = Expression, 1 + * two operators + * X = Expression, 1 + * three operators + * -- + * 18 18 + * + * if .... 14 + * with "else" 7 + * without "else" 7 + * executed 3 + * not executed 4 + * for ... 7 | counted every time + * while ... 4 | the loop condition + * do ... while 1 | is evaluated + * switch ... 1 + * break 1 + * declaration with 1 + * initialization + * -- + * 34 34 + * + * P (...) procedure call 11 + * user procedure 10 + * library procedure 1 + * X = F (...) + * function call 6 + * user function 5 + * library function 1 + * -- + * 17 17 + * --- + * 103 + * + * The average number of parameters in procedure or function calls + * is 1.82 (not counting the function values as implicit parameters). + * + * + * 2. Operators + * ------------ + * number approximate + * percentage + * + * Arithmetic 32 50.8 + * + * + 21 33.3 + * - 7 11.1 + * * 3 4.8 + * / (int div) 1 1.6 + * + * Comparison 27 42.8 + * + * == 9 14.3 + * /= 4 6.3 + * > 1 1.6 + * < 3 4.8 + * >= 1 1.6 + * <= 9 14.3 + * + * Logic 4 6.3 + * + * && (AND-THEN) 1 1.6 + * | (OR) 1 1.6 + * ! (NOT) 2 3.2 + * + * -- ----- + * 63 100.1 + * + * + * 3. Operand Type (counted once per operand reference): + * --------------- + * number approximate + * percentage + * + * Integer 175 72.3 % + * Character 45 18.6 % + * Pointer 12 5.0 % + * String30 6 2.5 % + * Array 2 0.8 % + * Record 2 0.8 % + * --- ------- + * 242 100.0 % + * + * When there is an access path leading to the final operand (e.g. a record + * component), only the final data type on the access path is counted. + * + * + * 4. Operand Locality: + * ------------------- + * number approximate + * percentage + * + * local variable 114 47.1 % + * global variable 22 9.1 % + * parameter 45 18.6 % + * value 23 9.5 % + * reference 22 9.1 % + * function result 6 2.5 % + * constant 55 22.7 % + * --- ------- + * 242 100.0 % + * + * + * The program does not compute anything meaningful, but it is syntactically + * and semantically correct. All variables have a value assigned to them + * before they are used as a source operand. + * + * There has been no explicit effort to account for the effects of a + * cache, or to balance the use of long or short displacements for code or + * data. + * + *************************************************************************** + */ + +/* Compiler and system dependent definitions: */ + +#ifndef TIME +#define TIMES +#endif + /* Use times(2) time function unless */ + /* explicitly defined otherwise */ + +#ifdef TIMES +#include +#include + /* for "times" */ +#endif + +#define Mic_secs_Per_Second 1000000.0 + /* Berkeley UNIX C returns process times in seconds/HZ */ + +#ifdef NOSTRUCTASSIGN +#define structassign(d, s) memcpy(&(d), &(s), sizeof(d)) +#else +#define structassign(d, s) d = s +#endif + +#ifdef NOENUM +#define Ident_1 0 +#define Ident_2 1 +#define Ident_3 2 +#define Ident_4 3 +#define Ident_5 4 + typedef int Enumeration; +#else + typedef enum {Ident_1, Ident_2, Ident_3, Ident_4, Ident_5} + Enumeration; +#endif + /* for boolean and enumeration types in Ada, Pascal */ + +/* General definitions: */ + +#ifdef USE_MYSTDLIB +extern char *strcpy(char *dest, const char *src); +extern int strcmp(const char *s1, const char *s2); +#else +#include + /* for strcpy, strcmp */ +#endif + +#define Null 0 + /* Value of a Null pointer */ +#define true 1 +#define false 0 + +typedef int One_Thirty; +typedef int One_Fifty; +typedef char Capital_Letter; +typedef int Boolean; +typedef char Str_30 [31]; +typedef int Arr_1_Dim [50]; +typedef int Arr_2_Dim [50] [50]; + +typedef struct record + { + struct record *Ptr_Comp; + Enumeration Discr; + union { + struct { + Enumeration Enum_Comp; + int Int_Comp; + char Str_Comp [31]; + } var_1; + struct { + Enumeration E_Comp_2; + char Str_2_Comp [31]; + } var_2; + struct { + char Ch_1_Comp; + char Ch_2_Comp; + } var_3; + } variant; + } Rec_Type, *Rec_Pointer; + + diff --git a/dhrystone/dhry_1.c b/dhrystone/dhry_1.c new file mode 100644 index 0000000..fa0d4d9 --- /dev/null +++ b/dhrystone/dhry_1.c @@ -0,0 +1,427 @@ +/* + **************************************************************************** + * + * "DHRYSTONE" Benchmark Program + * ----------------------------- + * + * Version: C, Version 2.1 + * + * File: dhry_1.c (part 2 of 3) + * + * Date: May 25, 1988 + * + * Author: Reinhold P. Weicker + * + **************************************************************************** + */ + +#include "dhry.h" + +#ifdef USE_MYSTDLIB +extern char *malloc (); +#else +# include +# include +#endif + +/* Global Variables: */ + +Rec_Pointer Ptr_Glob, + Next_Ptr_Glob; +int Int_Glob; +Boolean Bool_Glob; +char Ch_1_Glob, + Ch_2_Glob; +int Arr_1_Glob [50]; +int Arr_2_Glob [50] [50]; + +Enumeration Func_1 (); + /* forward declaration necessary since Enumeration may not simply be int */ + +#ifndef REG + Boolean Reg = false; +#define REG + /* REG becomes defined as empty */ + /* i.e. no register variables */ +#else + Boolean Reg = true; +#endif + +/* variables for time measurement: */ + +#ifdef IGN_TIMES +struct tms time_info; +extern int times (); + /* see library function "times" */ +#define Too_Small_Time 120 + /* Measurements should last at least about 2 seconds */ +#endif +#ifdef TIME +extern long time(); +#ifdef RISCV +extern long insn(); +#endif + /* see library function "time" */ +#define Too_Small_Time 2 + /* Measurements should last at least 2 seconds */ +#endif + +long Begin_Time, + End_Time, + User_Time; +#ifdef RISCV +long Begin_Insn, + End_Insn, + User_Insn; +#endif +float Microseconds, + Dhrystones_Per_Second; + +/* end of variables for time measurement */ + + +main () +/*****/ + + /* main program, corresponds to procedures */ + /* Main and Proc_0 in the Ada version */ +{ + One_Fifty Int_1_Loc; + REG One_Fifty Int_2_Loc; + One_Fifty Int_3_Loc; + REG char Ch_Index; + Enumeration Enum_Loc; + Str_30 Str_1_Loc; + Str_30 Str_2_Loc; + REG int Run_Index; + REG int Number_Of_Runs; + + /* Initializations */ + + Next_Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type)); + Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type)); + + Ptr_Glob->Ptr_Comp = Next_Ptr_Glob; + Ptr_Glob->Discr = Ident_1; + Ptr_Glob->variant.var_1.Enum_Comp = Ident_3; + Ptr_Glob->variant.var_1.Int_Comp = 40; + strcpy (Ptr_Glob->variant.var_1.Str_Comp, + "DHRYSTONE PROGRAM, SOME STRING"); + strcpy (Str_1_Loc, "DHRYSTONE PROGRAM, 1'ST STRING"); + + Arr_2_Glob [8][7] = 10; + /* Was missing in published program. Without this statement, */ + /* Arr_2_Glob [8][7] would have an undefined value. */ + /* Warning: With 16-Bit processors and Number_Of_Runs > 32000, */ + /* overflow may occur for this array element. */ + + printf ("\n"); + printf ("Dhrystone Benchmark, Version 2.1 (Language: C)\n"); + printf ("\n"); + if (Reg) + { + printf ("Program compiled with 'register' attribute\n"); + printf ("\n"); + } + else + { + printf ("Program compiled without 'register' attribute\n"); + printf ("\n"); + } + printf ("Please give the number of runs through the benchmark: "); + { + // int n; + // scanf ("%d", &n); + Number_Of_Runs = 100; + } + printf ("\n"); + + printf ("Execution starts, %d runs through Dhrystone\n", Number_Of_Runs); + + /***************/ + /* Start timer */ + /***************/ + +#ifdef IGN_TIMES + times (&time_info); + Begin_Time = (long) time_info.tms_utime; +#endif +#ifdef TIME + Begin_Time = time ( (long *) 0); +#ifdef RISCV + Begin_Insn = insn ( (long *) 0); +#endif +#endif + + for (Run_Index = 1; Run_Index <= Number_Of_Runs; ++Run_Index) + { + + Proc_5(); + Proc_4(); + /* Ch_1_Glob == 'A', Ch_2_Glob == 'B', Bool_Glob == true */ + Int_1_Loc = 2; + Int_2_Loc = 3; + strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 2'ND STRING"); + Enum_Loc = Ident_2; + Bool_Glob = ! Func_2 (Str_1_Loc, Str_2_Loc); + /* Bool_Glob == 1 */ + while (Int_1_Loc < Int_2_Loc) /* loop body executed once */ + { + Int_3_Loc = 5 * Int_1_Loc - Int_2_Loc; + /* Int_3_Loc == 7 */ + Proc_7 (Int_1_Loc, Int_2_Loc, &Int_3_Loc); + /* Int_3_Loc == 7 */ + Int_1_Loc += 1; + } /* while */ + /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */ + Proc_8 (Arr_1_Glob, Arr_2_Glob, Int_1_Loc, Int_3_Loc); + /* Int_Glob == 5 */ + Proc_1 (Ptr_Glob); + for (Ch_Index = 'A'; Ch_Index <= Ch_2_Glob; ++Ch_Index) + /* loop body executed twice */ + { + if (Enum_Loc == Func_1 (Ch_Index, 'C')) + /* then, not executed */ + { + Proc_6 (Ident_1, &Enum_Loc); + strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 3'RD STRING"); + Int_2_Loc = Run_Index; + Int_Glob = Run_Index; + } + } + /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */ + Int_2_Loc = Int_2_Loc * Int_1_Loc; + Int_1_Loc = Int_2_Loc / Int_3_Loc; + Int_2_Loc = 7 * (Int_2_Loc - Int_3_Loc) - Int_1_Loc; + /* Int_1_Loc == 1, Int_2_Loc == 13, Int_3_Loc == 7 */ + Proc_2 (&Int_1_Loc); + /* Int_1_Loc == 5 */ + + } /* loop "for Run_Index" */ + + /**************/ + /* Stop timer */ + /**************/ + +#ifdef IGN_TIMES + times (&time_info); + End_Time = (long) time_info.tms_utime; +#endif +#ifdef TIME + End_Time = time ( (long *) 0); +#ifdef RISCV + End_Insn = insn ( (long *) 0); +#endif +#endif + + printf ("Execution ends\n"); + printf ("\n"); + printf ("Final values of the variables used in the benchmark:\n"); + printf ("\n"); + printf ("Int_Glob: %d\n", Int_Glob); + printf (" should be: %d\n", 5); + printf ("Bool_Glob: %d\n", Bool_Glob); + printf (" should be: %d\n", 1); + printf ("Ch_1_Glob: %c\n", Ch_1_Glob); + printf (" should be: %c\n", 'A'); + printf ("Ch_2_Glob: %c\n", Ch_2_Glob); + printf (" should be: %c\n", 'B'); + printf ("Arr_1_Glob[8]: %d\n", Arr_1_Glob[8]); + printf (" should be: %d\n", 7); + printf ("Arr_2_Glob[8][7]: %d\n", Arr_2_Glob[8][7]); + printf (" should be: Number_Of_Runs + 10\n"); + printf ("Ptr_Glob->\n"); + printf (" Ptr_Comp: %d\n", (int) Ptr_Glob->Ptr_Comp); + printf (" should be: (implementation-dependent)\n"); + printf (" Discr: %d\n", Ptr_Glob->Discr); + printf (" should be: %d\n", 0); + printf (" Enum_Comp: %d\n", Ptr_Glob->variant.var_1.Enum_Comp); + printf (" should be: %d\n", 2); + printf (" Int_Comp: %d\n", Ptr_Glob->variant.var_1.Int_Comp); + printf (" should be: %d\n", 17); + printf (" Str_Comp: %s\n", Ptr_Glob->variant.var_1.Str_Comp); + printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n"); + printf ("Next_Ptr_Glob->\n"); + printf (" Ptr_Comp: %d\n", (int) Next_Ptr_Glob->Ptr_Comp); + printf (" should be: (implementation-dependent), same as above\n"); + printf (" Discr: %d\n", Next_Ptr_Glob->Discr); + printf (" should be: %d\n", 0); + printf (" Enum_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Enum_Comp); + printf (" should be: %d\n", 1); + printf (" Int_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Int_Comp); + printf (" should be: %d\n", 18); + printf (" Str_Comp: %s\n", + Next_Ptr_Glob->variant.var_1.Str_Comp); + printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n"); + printf ("Int_1_Loc: %d\n", Int_1_Loc); + printf (" should be: %d\n", 5); + printf ("Int_2_Loc: %d\n", Int_2_Loc); + printf (" should be: %d\n", 13); + printf ("Int_3_Loc: %d\n", Int_3_Loc); + printf (" should be: %d\n", 7); + printf ("Enum_Loc: %d\n", Enum_Loc); + printf (" should be: %d\n", 1); + printf ("Str_1_Loc: %s\n", Str_1_Loc); + printf (" should be: DHRYSTONE PROGRAM, 1'ST STRING\n"); + printf ("Str_2_Loc: %s\n", Str_2_Loc); + printf (" should be: DHRYSTONE PROGRAM, 2'ND STRING\n"); + printf ("\n"); + + User_Time = End_Time - Begin_Time; + +#ifdef RISCV + User_Insn = End_Insn - Begin_Insn; + + printf("Number_Of_Runs: %d\n", Number_Of_Runs); + printf("User_Time: %d cycles, %d insn\n", User_Time, User_Insn); + + int Cycles_Per_Instruction_x1000 = (1000 * User_Time) / User_Insn; + printf("Cycles_Per_Instruction: %d.%d%d%d\n", Cycles_Per_Instruction_x1000 / 1000, + (Cycles_Per_Instruction_x1000 / 100) % 10, + (Cycles_Per_Instruction_x1000 / 10) % 10, + (Cycles_Per_Instruction_x1000 / 1) % 10); + + int Dhrystones_Per_Second_Per_MHz = (Number_Of_Runs * 1000000) / User_Time; + printf("Dhrystones_Per_Second_Per_MHz: %d\n", Dhrystones_Per_Second_Per_MHz); + + int DMIPS_Per_MHz_x1000 = (1000 * Dhrystones_Per_Second_Per_MHz) / 1757; + printf("DMIPS_Per_MHz: %d.%d%d%d\n", DMIPS_Per_MHz_x1000 / 1000, + (DMIPS_Per_MHz_x1000 / 100) % 10, + (DMIPS_Per_MHz_x1000 / 10) % 10, + (DMIPS_Per_MHz_x1000 / 1) % 10); +#else + if (User_Time < Too_Small_Time) + { + printf ("Measured time too small to obtain meaningful results\n"); + printf ("Please increase number of runs\n"); + printf ("\n"); + } + else + { +#ifdef TIME + Microseconds = (float) User_Time * Mic_secs_Per_Second + / (float) Number_Of_Runs; + Dhrystones_Per_Second = (float) Number_Of_Runs / (float) User_Time; +#else + Microseconds = (float) User_Time * Mic_secs_Per_Second + / ((float) HZ * ((float) Number_Of_Runs)); + Dhrystones_Per_Second = ((float) HZ * (float) Number_Of_Runs) + / (float) User_Time; +#endif + printf ("Microseconds for one run through Dhrystone: "); + printf ("%6.1f \n", Microseconds); + printf ("Dhrystones per Second: "); + printf ("%6.1f \n", Dhrystones_Per_Second); + printf ("\n"); + } +#endif + +} + + +Proc_1 (Ptr_Val_Par) +/******************/ + +REG Rec_Pointer Ptr_Val_Par; + /* executed once */ +{ + REG Rec_Pointer Next_Record = Ptr_Val_Par->Ptr_Comp; + /* == Ptr_Glob_Next */ + /* Local variable, initialized with Ptr_Val_Par->Ptr_Comp, */ + /* corresponds to "rename" in Ada, "with" in Pascal */ + + structassign (*Ptr_Val_Par->Ptr_Comp, *Ptr_Glob); + Ptr_Val_Par->variant.var_1.Int_Comp = 5; + Next_Record->variant.var_1.Int_Comp + = Ptr_Val_Par->variant.var_1.Int_Comp; + Next_Record->Ptr_Comp = Ptr_Val_Par->Ptr_Comp; + Proc_3 (&Next_Record->Ptr_Comp); + /* Ptr_Val_Par->Ptr_Comp->Ptr_Comp + == Ptr_Glob->Ptr_Comp */ + if (Next_Record->Discr == Ident_1) + /* then, executed */ + { + Next_Record->variant.var_1.Int_Comp = 6; + Proc_6 (Ptr_Val_Par->variant.var_1.Enum_Comp, + &Next_Record->variant.var_1.Enum_Comp); + Next_Record->Ptr_Comp = Ptr_Glob->Ptr_Comp; + Proc_7 (Next_Record->variant.var_1.Int_Comp, 10, + &Next_Record->variant.var_1.Int_Comp); + } + else /* not executed */ + structassign (*Ptr_Val_Par, *Ptr_Val_Par->Ptr_Comp); +} /* Proc_1 */ + + +Proc_2 (Int_Par_Ref) +/******************/ + /* executed once */ + /* *Int_Par_Ref == 1, becomes 4 */ + +One_Fifty *Int_Par_Ref; +{ + One_Fifty Int_Loc; + Enumeration Enum_Loc; + + Int_Loc = *Int_Par_Ref + 10; + do /* executed once */ + if (Ch_1_Glob == 'A') + /* then, executed */ + { + Int_Loc -= 1; + *Int_Par_Ref = Int_Loc - Int_Glob; + Enum_Loc = Ident_1; + } /* if */ + while (Enum_Loc != Ident_1); /* true */ +} /* Proc_2 */ + + +Proc_3 (Ptr_Ref_Par) +/******************/ + /* executed once */ + /* Ptr_Ref_Par becomes Ptr_Glob */ + +Rec_Pointer *Ptr_Ref_Par; + +{ + if (Ptr_Glob != Null) + /* then, executed */ + *Ptr_Ref_Par = Ptr_Glob->Ptr_Comp; + Proc_7 (10, Int_Glob, &Ptr_Glob->variant.var_1.Int_Comp); +} /* Proc_3 */ + + +Proc_4 () /* without parameters */ +/*******/ + /* executed once */ +{ + Boolean Bool_Loc; + + Bool_Loc = Ch_1_Glob == 'A'; + Bool_Glob = Bool_Loc | Bool_Glob; + Ch_2_Glob = 'B'; +} /* Proc_4 */ + + +Proc_5 () /* without parameters */ +/*******/ + /* executed once */ +{ + Ch_1_Glob = 'A'; + Bool_Glob = false; +} /* Proc_5 */ + + + /* Procedure for the assignment of structures, */ + /* if the C compiler doesn't support this feature */ +#ifdef NOSTRUCTASSIGN +memcpy (d, s, l) +register char *d; +register char *s; +register int l; +{ + while (l--) *d++ = *s++; +} +#endif + + diff --git a/dhrystone/dhry_1_orig.c b/dhrystone/dhry_1_orig.c new file mode 100644 index 0000000..848fc5b --- /dev/null +++ b/dhrystone/dhry_1_orig.c @@ -0,0 +1,385 @@ +/* + **************************************************************************** + * + * "DHRYSTONE" Benchmark Program + * ----------------------------- + * + * Version: C, Version 2.1 + * + * File: dhry_1.c (part 2 of 3) + * + * Date: May 25, 1988 + * + * Author: Reinhold P. Weicker + * + **************************************************************************** + */ + +#include "dhry.h" + +/* Global Variables: */ + +Rec_Pointer Ptr_Glob, + Next_Ptr_Glob; +int Int_Glob; +Boolean Bool_Glob; +char Ch_1_Glob, + Ch_2_Glob; +int Arr_1_Glob [50]; +int Arr_2_Glob [50] [50]; + +extern char *malloc (); +Enumeration Func_1 (); + /* forward declaration necessary since Enumeration may not simply be int */ + +#ifndef REG + Boolean Reg = false; +#define REG + /* REG becomes defined as empty */ + /* i.e. no register variables */ +#else + Boolean Reg = true; +#endif + +/* variables for time measurement: */ + +#ifdef TIMES +struct tms time_info; +extern int times (); + /* see library function "times" */ +#define Too_Small_Time 120 + /* Measurements should last at least about 2 seconds */ +#endif +#ifdef TIME +extern long time(); + /* see library function "time" */ +#define Too_Small_Time 2 + /* Measurements should last at least 2 seconds */ +#endif + +long Begin_Time, + End_Time, + User_Time; +float Microseconds, + Dhrystones_Per_Second; + +/* end of variables for time measurement */ + + +main () +/*****/ + + /* main program, corresponds to procedures */ + /* Main and Proc_0 in the Ada version */ +{ + One_Fifty Int_1_Loc; + REG One_Fifty Int_2_Loc; + One_Fifty Int_3_Loc; + REG char Ch_Index; + Enumeration Enum_Loc; + Str_30 Str_1_Loc; + Str_30 Str_2_Loc; + REG int Run_Index; + REG int Number_Of_Runs; + + /* Initializations */ + + Next_Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type)); + Ptr_Glob = (Rec_Pointer) malloc (sizeof (Rec_Type)); + + Ptr_Glob->Ptr_Comp = Next_Ptr_Glob; + Ptr_Glob->Discr = Ident_1; + Ptr_Glob->variant.var_1.Enum_Comp = Ident_3; + Ptr_Glob->variant.var_1.Int_Comp = 40; + strcpy (Ptr_Glob->variant.var_1.Str_Comp, + "DHRYSTONE PROGRAM, SOME STRING"); + strcpy (Str_1_Loc, "DHRYSTONE PROGRAM, 1'ST STRING"); + + Arr_2_Glob [8][7] = 10; + /* Was missing in published program. Without this statement, */ + /* Arr_2_Glob [8][7] would have an undefined value. */ + /* Warning: With 16-Bit processors and Number_Of_Runs > 32000, */ + /* overflow may occur for this array element. */ + + printf ("\n"); + printf ("Dhrystone Benchmark, Version 2.1 (Language: C)\n"); + printf ("\n"); + if (Reg) + { + printf ("Program compiled with 'register' attribute\n"); + printf ("\n"); + } + else + { + printf ("Program compiled without 'register' attribute\n"); + printf ("\n"); + } + printf ("Please give the number of runs through the benchmark: "); + { + int n; + scanf ("%d", &n); + Number_Of_Runs = n; + } + printf ("\n"); + + printf ("Execution starts, %d runs through Dhrystone\n", Number_Of_Runs); + + /***************/ + /* Start timer */ + /***************/ + +#ifdef TIMES + times (&time_info); + Begin_Time = (long) time_info.tms_utime; +#endif +#ifdef TIME + Begin_Time = time ( (long *) 0); +#endif + + for (Run_Index = 1; Run_Index <= Number_Of_Runs; ++Run_Index) + { + + Proc_5(); + Proc_4(); + /* Ch_1_Glob == 'A', Ch_2_Glob == 'B', Bool_Glob == true */ + Int_1_Loc = 2; + Int_2_Loc = 3; + strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 2'ND STRING"); + Enum_Loc = Ident_2; + Bool_Glob = ! Func_2 (Str_1_Loc, Str_2_Loc); + /* Bool_Glob == 1 */ + while (Int_1_Loc < Int_2_Loc) /* loop body executed once */ + { + Int_3_Loc = 5 * Int_1_Loc - Int_2_Loc; + /* Int_3_Loc == 7 */ + Proc_7 (Int_1_Loc, Int_2_Loc, &Int_3_Loc); + /* Int_3_Loc == 7 */ + Int_1_Loc += 1; + } /* while */ + /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */ + Proc_8 (Arr_1_Glob, Arr_2_Glob, Int_1_Loc, Int_3_Loc); + /* Int_Glob == 5 */ + Proc_1 (Ptr_Glob); + for (Ch_Index = 'A'; Ch_Index <= Ch_2_Glob; ++Ch_Index) + /* loop body executed twice */ + { + if (Enum_Loc == Func_1 (Ch_Index, 'C')) + /* then, not executed */ + { + Proc_6 (Ident_1, &Enum_Loc); + strcpy (Str_2_Loc, "DHRYSTONE PROGRAM, 3'RD STRING"); + Int_2_Loc = Run_Index; + Int_Glob = Run_Index; + } + } + /* Int_1_Loc == 3, Int_2_Loc == 3, Int_3_Loc == 7 */ + Int_2_Loc = Int_2_Loc * Int_1_Loc; + Int_1_Loc = Int_2_Loc / Int_3_Loc; + Int_2_Loc = 7 * (Int_2_Loc - Int_3_Loc) - Int_1_Loc; + /* Int_1_Loc == 1, Int_2_Loc == 13, Int_3_Loc == 7 */ + Proc_2 (&Int_1_Loc); + /* Int_1_Loc == 5 */ + + } /* loop "for Run_Index" */ + + /**************/ + /* Stop timer */ + /**************/ + +#ifdef TIMES + times (&time_info); + End_Time = (long) time_info.tms_utime; +#endif +#ifdef TIME + End_Time = time ( (long *) 0); +#endif + + printf ("Execution ends\n"); + printf ("\n"); + printf ("Final values of the variables used in the benchmark:\n"); + printf ("\n"); + printf ("Int_Glob: %d\n", Int_Glob); + printf (" should be: %d\n", 5); + printf ("Bool_Glob: %d\n", Bool_Glob); + printf (" should be: %d\n", 1); + printf ("Ch_1_Glob: %c\n", Ch_1_Glob); + printf (" should be: %c\n", 'A'); + printf ("Ch_2_Glob: %c\n", Ch_2_Glob); + printf (" should be: %c\n", 'B'); + printf ("Arr_1_Glob[8]: %d\n", Arr_1_Glob[8]); + printf (" should be: %d\n", 7); + printf ("Arr_2_Glob[8][7]: %d\n", Arr_2_Glob[8][7]); + printf (" should be: Number_Of_Runs + 10\n"); + printf ("Ptr_Glob->\n"); + printf (" Ptr_Comp: %d\n", (int) Ptr_Glob->Ptr_Comp); + printf (" should be: (implementation-dependent)\n"); + printf (" Discr: %d\n", Ptr_Glob->Discr); + printf (" should be: %d\n", 0); + printf (" Enum_Comp: %d\n", Ptr_Glob->variant.var_1.Enum_Comp); + printf (" should be: %d\n", 2); + printf (" Int_Comp: %d\n", Ptr_Glob->variant.var_1.Int_Comp); + printf (" should be: %d\n", 17); + printf (" Str_Comp: %s\n", Ptr_Glob->variant.var_1.Str_Comp); + printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n"); + printf ("Next_Ptr_Glob->\n"); + printf (" Ptr_Comp: %d\n", (int) Next_Ptr_Glob->Ptr_Comp); + printf (" should be: (implementation-dependent), same as above\n"); + printf (" Discr: %d\n", Next_Ptr_Glob->Discr); + printf (" should be: %d\n", 0); + printf (" Enum_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Enum_Comp); + printf (" should be: %d\n", 1); + printf (" Int_Comp: %d\n", Next_Ptr_Glob->variant.var_1.Int_Comp); + printf (" should be: %d\n", 18); + printf (" Str_Comp: %s\n", + Next_Ptr_Glob->variant.var_1.Str_Comp); + printf (" should be: DHRYSTONE PROGRAM, SOME STRING\n"); + printf ("Int_1_Loc: %d\n", Int_1_Loc); + printf (" should be: %d\n", 5); + printf ("Int_2_Loc: %d\n", Int_2_Loc); + printf (" should be: %d\n", 13); + printf ("Int_3_Loc: %d\n", Int_3_Loc); + printf (" should be: %d\n", 7); + printf ("Enum_Loc: %d\n", Enum_Loc); + printf (" should be: %d\n", 1); + printf ("Str_1_Loc: %s\n", Str_1_Loc); + printf (" should be: DHRYSTONE PROGRAM, 1'ST STRING\n"); + printf ("Str_2_Loc: %s\n", Str_2_Loc); + printf (" should be: DHRYSTONE PROGRAM, 2'ND STRING\n"); + printf ("\n"); + + User_Time = End_Time - Begin_Time; + + if (User_Time < Too_Small_Time) + { + printf ("Measured time too small to obtain meaningful results\n"); + printf ("Please increase number of runs\n"); + printf ("\n"); + } + else + { +#ifdef TIME + Microseconds = (float) User_Time * Mic_secs_Per_Second + / (float) Number_Of_Runs; + Dhrystones_Per_Second = (float) Number_Of_Runs / (float) User_Time; +#else + Microseconds = (float) User_Time * Mic_secs_Per_Second + / ((float) HZ * ((float) Number_Of_Runs)); + Dhrystones_Per_Second = ((float) HZ * (float) Number_Of_Runs) + / (float) User_Time; +#endif + printf ("Microseconds for one run through Dhrystone: "); + printf ("%6.1f \n", Microseconds); + printf ("Dhrystones per Second: "); + printf ("%6.1f \n", Dhrystones_Per_Second); + printf ("\n"); + } + +} + + +Proc_1 (Ptr_Val_Par) +/******************/ + +REG Rec_Pointer Ptr_Val_Par; + /* executed once */ +{ + REG Rec_Pointer Next_Record = Ptr_Val_Par->Ptr_Comp; + /* == Ptr_Glob_Next */ + /* Local variable, initialized with Ptr_Val_Par->Ptr_Comp, */ + /* corresponds to "rename" in Ada, "with" in Pascal */ + + structassign (*Ptr_Val_Par->Ptr_Comp, *Ptr_Glob); + Ptr_Val_Par->variant.var_1.Int_Comp = 5; + Next_Record->variant.var_1.Int_Comp + = Ptr_Val_Par->variant.var_1.Int_Comp; + Next_Record->Ptr_Comp = Ptr_Val_Par->Ptr_Comp; + Proc_3 (&Next_Record->Ptr_Comp); + /* Ptr_Val_Par->Ptr_Comp->Ptr_Comp + == Ptr_Glob->Ptr_Comp */ + if (Next_Record->Discr == Ident_1) + /* then, executed */ + { + Next_Record->variant.var_1.Int_Comp = 6; + Proc_6 (Ptr_Val_Par->variant.var_1.Enum_Comp, + &Next_Record->variant.var_1.Enum_Comp); + Next_Record->Ptr_Comp = Ptr_Glob->Ptr_Comp; + Proc_7 (Next_Record->variant.var_1.Int_Comp, 10, + &Next_Record->variant.var_1.Int_Comp); + } + else /* not executed */ + structassign (*Ptr_Val_Par, *Ptr_Val_Par->Ptr_Comp); +} /* Proc_1 */ + + +Proc_2 (Int_Par_Ref) +/******************/ + /* executed once */ + /* *Int_Par_Ref == 1, becomes 4 */ + +One_Fifty *Int_Par_Ref; +{ + One_Fifty Int_Loc; + Enumeration Enum_Loc; + + Int_Loc = *Int_Par_Ref + 10; + do /* executed once */ + if (Ch_1_Glob == 'A') + /* then, executed */ + { + Int_Loc -= 1; + *Int_Par_Ref = Int_Loc - Int_Glob; + Enum_Loc = Ident_1; + } /* if */ + while (Enum_Loc != Ident_1); /* true */ +} /* Proc_2 */ + + +Proc_3 (Ptr_Ref_Par) +/******************/ + /* executed once */ + /* Ptr_Ref_Par becomes Ptr_Glob */ + +Rec_Pointer *Ptr_Ref_Par; + +{ + if (Ptr_Glob != Null) + /* then, executed */ + *Ptr_Ref_Par = Ptr_Glob->Ptr_Comp; + Proc_7 (10, Int_Glob, &Ptr_Glob->variant.var_1.Int_Comp); +} /* Proc_3 */ + + +Proc_4 () /* without parameters */ +/*******/ + /* executed once */ +{ + Boolean Bool_Loc; + + Bool_Loc = Ch_1_Glob == 'A'; + Bool_Glob = Bool_Loc | Bool_Glob; + Ch_2_Glob = 'B'; +} /* Proc_4 */ + + +Proc_5 () /* without parameters */ +/*******/ + /* executed once */ +{ + Ch_1_Glob = 'A'; + Bool_Glob = false; +} /* Proc_5 */ + + + /* Procedure for the assignment of structures, */ + /* if the C compiler doesn't support this feature */ +#ifdef NOSTRUCTASSIGN +memcpy (d, s, l) +register char *d; +register char *s; +register int l; +{ + while (l--) *d++ = *s++; +} +#endif + + diff --git a/dhrystone/dhry_2.c b/dhrystone/dhry_2.c new file mode 100644 index 0000000..ac5be3b --- /dev/null +++ b/dhrystone/dhry_2.c @@ -0,0 +1,192 @@ +/* + **************************************************************************** + * + * "DHRYSTONE" Benchmark Program + * ----------------------------- + * + * Version: C, Version 2.1 + * + * File: dhry_2.c (part 3 of 3) + * + * Date: May 25, 1988 + * + * Author: Reinhold P. Weicker + * + **************************************************************************** + */ + +#include "dhry.h" + +#ifndef REG +#define REG + /* REG becomes defined as empty */ + /* i.e. no register variables */ +#endif + +extern int Int_Glob; +extern char Ch_1_Glob; + + +Proc_6 (Enum_Val_Par, Enum_Ref_Par) +/*********************************/ + /* executed once */ + /* Enum_Val_Par == Ident_3, Enum_Ref_Par becomes Ident_2 */ + +Enumeration Enum_Val_Par; +Enumeration *Enum_Ref_Par; +{ + *Enum_Ref_Par = Enum_Val_Par; + if (! Func_3 (Enum_Val_Par)) + /* then, not executed */ + *Enum_Ref_Par = Ident_4; + switch (Enum_Val_Par) + { + case Ident_1: + *Enum_Ref_Par = Ident_1; + break; + case Ident_2: + if (Int_Glob > 100) + /* then */ + *Enum_Ref_Par = Ident_1; + else *Enum_Ref_Par = Ident_4; + break; + case Ident_3: /* executed */ + *Enum_Ref_Par = Ident_2; + break; + case Ident_4: break; + case Ident_5: + *Enum_Ref_Par = Ident_3; + break; + } /* switch */ +} /* Proc_6 */ + + +Proc_7 (Int_1_Par_Val, Int_2_Par_Val, Int_Par_Ref) +/**********************************************/ + /* executed three times */ + /* first call: Int_1_Par_Val == 2, Int_2_Par_Val == 3, */ + /* Int_Par_Ref becomes 7 */ + /* second call: Int_1_Par_Val == 10, Int_2_Par_Val == 5, */ + /* Int_Par_Ref becomes 17 */ + /* third call: Int_1_Par_Val == 6, Int_2_Par_Val == 10, */ + /* Int_Par_Ref becomes 18 */ +One_Fifty Int_1_Par_Val; +One_Fifty Int_2_Par_Val; +One_Fifty *Int_Par_Ref; +{ + One_Fifty Int_Loc; + + Int_Loc = Int_1_Par_Val + 2; + *Int_Par_Ref = Int_2_Par_Val + Int_Loc; +} /* Proc_7 */ + + +Proc_8 (Arr_1_Par_Ref, Arr_2_Par_Ref, Int_1_Par_Val, Int_2_Par_Val) +/*********************************************************************/ + /* executed once */ + /* Int_Par_Val_1 == 3 */ + /* Int_Par_Val_2 == 7 */ +Arr_1_Dim Arr_1_Par_Ref; +Arr_2_Dim Arr_2_Par_Ref; +int Int_1_Par_Val; +int Int_2_Par_Val; +{ + REG One_Fifty Int_Index; + REG One_Fifty Int_Loc; + + Int_Loc = Int_1_Par_Val + 5; + Arr_1_Par_Ref [Int_Loc] = Int_2_Par_Val; + Arr_1_Par_Ref [Int_Loc+1] = Arr_1_Par_Ref [Int_Loc]; + Arr_1_Par_Ref [Int_Loc+30] = Int_Loc; + for (Int_Index = Int_Loc; Int_Index <= Int_Loc+1; ++Int_Index) + Arr_2_Par_Ref [Int_Loc] [Int_Index] = Int_Loc; + Arr_2_Par_Ref [Int_Loc] [Int_Loc-1] += 1; + Arr_2_Par_Ref [Int_Loc+20] [Int_Loc] = Arr_1_Par_Ref [Int_Loc]; + Int_Glob = 5; +} /* Proc_8 */ + + +Enumeration Func_1 (Ch_1_Par_Val, Ch_2_Par_Val) +/*************************************************/ + /* executed three times */ + /* first call: Ch_1_Par_Val == 'H', Ch_2_Par_Val == 'R' */ + /* second call: Ch_1_Par_Val == 'A', Ch_2_Par_Val == 'C' */ + /* third call: Ch_1_Par_Val == 'B', Ch_2_Par_Val == 'C' */ + +Capital_Letter Ch_1_Par_Val; +Capital_Letter Ch_2_Par_Val; +{ + Capital_Letter Ch_1_Loc; + Capital_Letter Ch_2_Loc; + + Ch_1_Loc = Ch_1_Par_Val; + Ch_2_Loc = Ch_1_Loc; + if (Ch_2_Loc != Ch_2_Par_Val) + /* then, executed */ + return (Ident_1); + else /* not executed */ + { + Ch_1_Glob = Ch_1_Loc; + return (Ident_2); + } +} /* Func_1 */ + + +Boolean Func_2 (Str_1_Par_Ref, Str_2_Par_Ref) +/*************************************************/ + /* executed once */ + /* Str_1_Par_Ref == "DHRYSTONE PROGRAM, 1'ST STRING" */ + /* Str_2_Par_Ref == "DHRYSTONE PROGRAM, 2'ND STRING" */ + +Str_30 Str_1_Par_Ref; +Str_30 Str_2_Par_Ref; +{ + REG One_Thirty Int_Loc; + Capital_Letter Ch_Loc; + + Int_Loc = 2; + while (Int_Loc <= 2) /* loop body executed once */ + if (Func_1 (Str_1_Par_Ref[Int_Loc], + Str_2_Par_Ref[Int_Loc+1]) == Ident_1) + /* then, executed */ + { + Ch_Loc = 'A'; + Int_Loc += 1; + } /* if, while */ + if (Ch_Loc >= 'W' && Ch_Loc < 'Z') + /* then, not executed */ + Int_Loc = 7; + if (Ch_Loc == 'R') + /* then, not executed */ + return (true); + else /* executed */ + { + if (strcmp (Str_1_Par_Ref, Str_2_Par_Ref) > 0) + /* then, not executed */ + { + Int_Loc += 7; + Int_Glob = Int_Loc; + return (true); + } + else /* executed */ + return (false); + } /* if Ch_Loc */ +} /* Func_2 */ + + +Boolean Func_3 (Enum_Par_Val) +/***************************/ + /* executed once */ + /* Enum_Par_Val == Ident_3 */ +Enumeration Enum_Par_Val; +{ + Enumeration Enum_Loc; + + Enum_Loc = Enum_Par_Val; + if (Enum_Loc == Ident_3) + /* then, executed */ + return (true); + else /* not executed */ + return (false); +} /* Func_3 */ + diff --git a/dhrystone/sections.lds b/dhrystone/sections.lds new file mode 100644 index 0000000..3efb873 --- /dev/null +++ b/dhrystone/sections.lds @@ -0,0 +1,18 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. +*/ + +SECTIONS { + .memory : { + . = 0x10000; + start*(.text); + *(.text); + *(*); + end = .; + } +} diff --git a/dhrystone/start.S b/dhrystone/start.S new file mode 100644 index 0000000..9fb2359 --- /dev/null +++ b/dhrystone/start.S @@ -0,0 +1,51 @@ + .section .text + .global start + .global main + +start: + /* print "START\n" */ + lui a0,0x10000000>>12 + addi a1,zero,'S' + addi a2,zero,'T' + addi a3,zero,'A' + addi a4,zero,'R' + addi a5,zero,'\n' + sw a1,0(a0) + sw a2,0(a0) + sw a3,0(a0) + sw a4,0(a0) + sw a2,0(a0) + sw a5,0(a0) + + /* execute some insns for "make timing" */ + lui a0,0 + auipc a0,0 + slli a0,a0,0 + slli a0,a0,31 + addi a1,zero,0 + sll a0,a0,a1 + addi a1,zero,31 + sll a0,a0,a1 + + /* set stack pointer */ + lui sp,(64*1024)>>12 + + /* jump to main C code */ + jal ra,main + + /* print "DONE\n" */ + lui a0,0x10000000>>12 + addi a1,zero,'D' + addi a2,zero,'O' + addi a3,zero,'N' + addi a4,zero,'E' + addi a5,zero,'\n' + sw a1,0(a0) + sw a2,0(a0) + sw a3,0(a0) + sw a4,0(a0) + sw a5,0(a0) + + /* trap */ + ebreak + diff --git a/dhrystone/stdlib.c b/dhrystone/stdlib.c new file mode 100644 index 0000000..f88023b --- /dev/null +++ b/dhrystone/stdlib.c @@ -0,0 +1,210 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#include +#include + +extern long time(); +extern long insn(); + +#ifdef USE_MYSTDLIB +extern char *malloc(); +extern int printf(const char *format, ...); + +extern void *memcpy(void *dest, const void *src, long n); +extern char *strcpy(char *dest, const char *src); +extern int strcmp(const char *s1, const char *s2); + +char heap_memory[1024]; +int heap_memory_used = 0; +#endif + +long time() +{ + int cycles; + asm volatile ("rdcycle %0" : "=r"(cycles)); + // printf("[time() -> %d]", cycles); + return cycles; +} + +long insn() +{ + int insns; + asm volatile ("rdinstret %0" : "=r"(insns)); + // printf("[insn() -> %d]", insns); + return insns; +} + +#ifdef USE_MYSTDLIB +char *malloc(int size) +{ + char *p = heap_memory + heap_memory_used; + // printf("[malloc(%d) -> %d (%d..%d)]", size, (int)p, heap_memory_used, heap_memory_used + size); + heap_memory_used += size; + if (heap_memory_used > 1024) + asm volatile ("ebreak"); + return p; +} + +static void printf_c(int c) +{ + *((volatile int*)0x10000000) = c; +} + +static void printf_s(char *p) +{ + while (*p) + *((volatile int*)0x10000000) = *(p++); +} + +static void printf_d(int val) +{ + char buffer[32]; + char *p = buffer; + if (val < 0) { + printf_c('-'); + val = -val; + } + while (val || p == buffer) { + *(p++) = '0' + val % 10; + val = val / 10; + } + while (p != buffer) + printf_c(*(--p)); +} + +int printf(const char *format, ...) +{ + int i; + va_list ap; + + va_start(ap, format); + + for (i = 0; format[i]; i++) + if (format[i] == '%') { + while (format[++i]) { + if (format[i] == 'c') { + printf_c(va_arg(ap,int)); + break; + } + if (format[i] == 's') { + printf_s(va_arg(ap,char*)); + break; + } + if (format[i] == 'd') { + printf_d(va_arg(ap,int)); + break; + } + } + } else + printf_c(format[i]); + + va_end(ap); +} + +void *memcpy(void *aa, const void *bb, long n) +{ + // printf("**MEMCPY**\n"); + char *a = aa; + const char *b = bb; + while (n--) *(a++) = *(b++); + return aa; +} + +char *strcpy(char* dst, const char* src) +{ + char *r = dst; + + while ((((uint32_t)dst | (uint32_t)src) & 3) != 0) + { + char c = *(src++); + *(dst++) = c; + if (!c) return r; + } + + while (1) + { + uint32_t v = *(uint32_t*)src; + + if (__builtin_expect((((v) - 0x01010101UL) & ~(v) & 0x80808080UL), 0)) + { + dst[0] = v & 0xff; + if ((v & 0xff) == 0) + return r; + v = v >> 8; + + dst[1] = v & 0xff; + if ((v & 0xff) == 0) + return r; + v = v >> 8; + + dst[2] = v & 0xff; + if ((v & 0xff) == 0) + return r; + v = v >> 8; + + dst[3] = v & 0xff; + return r; + } + + *(uint32_t*)dst = v; + src += 4; + dst += 4; + } +} + +int strcmp(const char *s1, const char *s2) +{ + while ((((uint32_t)s1 | (uint32_t)s2) & 3) != 0) + { + char c1 = *(s1++); + char c2 = *(s2++); + + if (c1 != c2) + return c1 < c2 ? -1 : +1; + else if (!c1) + return 0; + } + + while (1) + { + uint32_t v1 = *(uint32_t*)s1; + uint32_t v2 = *(uint32_t*)s2; + + if (__builtin_expect(v1 != v2, 0)) + { + char c1, c2; + + c1 = v1 & 0xff, c2 = v2 & 0xff; + if (c1 != c2) return c1 < c2 ? -1 : +1; + if (!c1) return 0; + v1 = v1 >> 8, v2 = v2 >> 8; + + c1 = v1 & 0xff, c2 = v2 & 0xff; + if (c1 != c2) return c1 < c2 ? -1 : +1; + if (!c1) return 0; + v1 = v1 >> 8, v2 = v2 >> 8; + + c1 = v1 & 0xff, c2 = v2 & 0xff; + if (c1 != c2) return c1 < c2 ? -1 : +1; + if (!c1) return 0; + v1 = v1 >> 8, v2 = v2 >> 8; + + c1 = v1 & 0xff, c2 = v2 & 0xff; + if (c1 != c2) return c1 < c2 ? -1 : +1; + return 0; + } + + if (__builtin_expect((((v1) - 0x01010101UL) & ~(v1) & 0x80808080UL), 0)) + return 0; + + s1 += 4; + s2 += 4; + } +} +#endif + diff --git a/dhrystone/syscalls.c b/dhrystone/syscalls.c new file mode 100644 index 0000000..8ea84ca --- /dev/null +++ b/dhrystone/syscalls.c @@ -0,0 +1,95 @@ +// An extremely minimalist syscalls.c for newlib +// Based on riscv newlib libgloss/riscv/sys_*.c +// Written by Clifford Wolf. + +#include +#include +#include + +#define UNIMPL_FUNC(_f) ".globl " #_f "\n.type " #_f ", @function\n" #_f ":\n" + +asm ( + ".text\n" + ".align 2\n" + UNIMPL_FUNC(_open) + UNIMPL_FUNC(_openat) + UNIMPL_FUNC(_lseek) + UNIMPL_FUNC(_stat) + UNIMPL_FUNC(_lstat) + UNIMPL_FUNC(_fstatat) + UNIMPL_FUNC(_isatty) + UNIMPL_FUNC(_access) + UNIMPL_FUNC(_faccessat) + UNIMPL_FUNC(_link) + UNIMPL_FUNC(_unlink) + UNIMPL_FUNC(_execve) + UNIMPL_FUNC(_getpid) + UNIMPL_FUNC(_fork) + UNIMPL_FUNC(_kill) + UNIMPL_FUNC(_wait) + UNIMPL_FUNC(_times) + UNIMPL_FUNC(_gettimeofday) + UNIMPL_FUNC(_ftime) + UNIMPL_FUNC(_utime) + UNIMPL_FUNC(_chown) + UNIMPL_FUNC(_chmod) + UNIMPL_FUNC(_chdir) + UNIMPL_FUNC(_getcwd) + UNIMPL_FUNC(_sysconf) + "j unimplemented_syscall\n" +); + +void unimplemented_syscall() +{ + const char *p = "Unimplemented system call called!\n"; + while (*p) + *(volatile int*)0x10000000 = *(p++); + asm volatile ("ebreak"); + __builtin_unreachable(); +} + +ssize_t _read(int file, void *ptr, size_t len) +{ + // always EOF + return 0; +} + +ssize_t _write(int file, const void *ptr, size_t len) +{ + const void *eptr = ptr + len; + while (ptr != eptr) + *(volatile int*)0x10000000 = *(char*)(ptr++); + return len; +} + +int _close(int file) +{ + // close is called before _exit() + return 0; +} + +int _fstat(int file, struct stat *st) +{ + // fstat is called during libc startup + errno = ENOENT; + return -1; +} + +void *_sbrk(ptrdiff_t incr) +{ + extern unsigned char _end[]; // Defined by linker + static unsigned long heap_end; + + if (heap_end == 0) + heap_end = (long)_end; + + heap_end += incr; + return (void *)(heap_end - incr); +} + +void _exit(int exit_status) +{ + asm volatile ("ebreak"); + __builtin_unreachable(); +} + diff --git a/dhrystone/testbench.v b/dhrystone/testbench.v new file mode 100644 index 0000000..9c365a6 --- /dev/null +++ b/dhrystone/testbench.v @@ -0,0 +1,126 @@ +`timescale 1 ns / 1 ps + +module testbench; + reg clk = 1; + reg resetn = 0; + wire trap; + + always #5 clk = ~clk; + + initial begin + repeat (100) @(posedge clk); + resetn <= 1; + end + + wire mem_valid; + wire mem_instr; + wire 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; + + wire trace_valid; + wire [35:0] trace_data; + + picorv32 #( + .BARREL_SHIFTER(1), + .ENABLE_FAST_MUL(1), + .ENABLE_DIV(1), + .PROGADDR_RESET('h10000), + .STACKADDR('h10000), + .ENABLE_TRACE(1) + ) 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), + .trace_valid (trace_valid), + .trace_data (trace_data ) + ); + + reg [7:0] memory [0:256*1024-1]; + initial $readmemh("dhry.hex", memory); + + assign mem_ready = 1; + + always @(posedge clk) begin + mem_rdata[ 7: 0] <= mem_la_read ? memory[mem_la_addr + 0] : 'bx; + mem_rdata[15: 8] <= mem_la_read ? memory[mem_la_addr + 1] : 'bx; + mem_rdata[23:16] <= mem_la_read ? memory[mem_la_addr + 2] : 'bx; + mem_rdata[31:24] <= mem_la_read ? memory[mem_la_addr + 3] : 'bx; + if (mem_la_write) begin + case (mem_la_addr) + 32'h1000_0000: begin +`ifndef TIMING + $write("%c", mem_la_wdata); + $fflush(); +`endif + end + default: begin + if (mem_la_wstrb[0]) memory[mem_la_addr + 0] <= mem_la_wdata[ 7: 0]; + if (mem_la_wstrb[1]) memory[mem_la_addr + 1] <= mem_la_wdata[15: 8]; + if (mem_la_wstrb[2]) memory[mem_la_addr + 2] <= mem_la_wdata[23:16]; + if (mem_la_wstrb[3]) memory[mem_la_addr + 3] <= mem_la_wdata[31:24]; + end + endcase + end + end + + initial begin + $dumpfile("testbench.vcd"); + $dumpvars(0, testbench); + end + + integer trace_file; + + initial begin + if ($test$plusargs("trace")) begin + trace_file = $fopen("testbench.trace", "w"); + repeat (10) @(posedge clk); + while (!trap) begin + @(posedge clk); + if (trace_valid) + $fwrite(trace_file, "%x\n", trace_data); + end + $fclose(trace_file); + $display("Finished writing testbench.trace."); + end + end + + always @(posedge clk) begin + if (resetn && trap) begin + repeat (10) @(posedge clk); + $display("TRAP"); + $finish; + end + end + +`ifdef TIMING + initial begin + repeat (100000) @(posedge clk); + $finish; + end + always @(posedge clk) begin + if (uut.dbg_next) + $display("## %-s %d", uut.dbg_ascii_instr ? uut.dbg_ascii_instr : "pcpi", uut.count_cycle); + end +`endif +endmodule diff --git a/dhrystone/testbench_nola.v b/dhrystone/testbench_nola.v new file mode 100644 index 0000000..b1a154c --- /dev/null +++ b/dhrystone/testbench_nola.v @@ -0,0 +1,95 @@ +// A version of the dhrystone test bench that isn't using the look-ahead interface + +`timescale 1 ns / 1 ps + +module testbench; + reg clk = 1; + reg resetn = 0; + wire trap; + + always #5 clk = ~clk; + + initial begin + repeat (100) @(posedge clk); + resetn <= 1; + end + + 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; + + picorv32 #( + .BARREL_SHIFTER(1), + .ENABLE_FAST_MUL(1), + .ENABLE_DIV(1), + .PROGADDR_RESET('h10000), + .STACKADDR('h10000) + ) 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 ) + ); + + reg [7:0] memory [0:256*1024-1]; + initial $readmemh("dhry.hex", memory); + + always @(posedge clk) begin + mem_ready <= 1'b0; + + mem_rdata[ 7: 0] <= 'bx; + mem_rdata[15: 8] <= 'bx; + mem_rdata[23:16] <= 'bx; + mem_rdata[31:24] <= 'bx; + + if (mem_valid & !mem_ready) begin + if (|mem_wstrb) begin + mem_ready <= 1'b1; + + case (mem_addr) + 32'h1000_0000: begin + $write("%c", mem_wdata); + $fflush(); + end + default: begin + if (mem_wstrb[0]) memory[mem_addr + 0] <= mem_wdata[ 7: 0]; + if (mem_wstrb[1]) memory[mem_addr + 1] <= mem_wdata[15: 8]; + if (mem_wstrb[2]) memory[mem_addr + 2] <= mem_wdata[23:16]; + if (mem_wstrb[3]) memory[mem_addr + 3] <= mem_wdata[31:24]; + end + endcase + end + else begin + mem_ready <= 1'b1; + + mem_rdata[ 7: 0] <= memory[mem_addr + 0]; + mem_rdata[15: 8] <= memory[mem_addr + 1]; + mem_rdata[23:16] <= memory[mem_addr + 2]; + mem_rdata[31:24] <= memory[mem_addr + 3]; + end + end + end + + initial begin + $dumpfile("testbench_nola.vcd"); + $dumpvars(0, testbench); + end + + always @(posedge clk) begin + if (resetn && trap) begin + repeat (10) @(posedge clk); + $display("TRAP"); + $finish; + end + end +endmodule diff --git a/firmware/README b/firmware/README new file mode 100644 index 0000000..2ade487 --- /dev/null +++ b/firmware/README @@ -0,0 +1,2 @@ +A simple test firmware. This code is in the public domain. Simply copy whatever +you can use. diff --git a/firmware/custom_ops.S b/firmware/custom_ops.S new file mode 100644 index 0000000..71889b9 --- /dev/null +++ b/firmware/custom_ops.S @@ -0,0 +1,102 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define regnum_q0 0 +#define regnum_q1 1 +#define regnum_q2 2 +#define regnum_q3 3 + +#define regnum_x0 0 +#define regnum_x1 1 +#define regnum_x2 2 +#define regnum_x3 3 +#define regnum_x4 4 +#define regnum_x5 5 +#define regnum_x6 6 +#define regnum_x7 7 +#define regnum_x8 8 +#define regnum_x9 9 +#define regnum_x10 10 +#define regnum_x11 11 +#define regnum_x12 12 +#define regnum_x13 13 +#define regnum_x14 14 +#define regnum_x15 15 +#define regnum_x16 16 +#define regnum_x17 17 +#define regnum_x18 18 +#define regnum_x19 19 +#define regnum_x20 20 +#define regnum_x21 21 +#define regnum_x22 22 +#define regnum_x23 23 +#define regnum_x24 24 +#define regnum_x25 25 +#define regnum_x26 26 +#define regnum_x27 27 +#define regnum_x28 28 +#define regnum_x29 29 +#define regnum_x30 30 +#define regnum_x31 31 + +#define regnum_zero 0 +#define regnum_ra 1 +#define regnum_sp 2 +#define regnum_gp 3 +#define regnum_tp 4 +#define regnum_t0 5 +#define regnum_t1 6 +#define regnum_t2 7 +#define regnum_s0 8 +#define regnum_s1 9 +#define regnum_a0 10 +#define regnum_a1 11 +#define regnum_a2 12 +#define regnum_a3 13 +#define regnum_a4 14 +#define regnum_a5 15 +#define regnum_a6 16 +#define regnum_a7 17 +#define regnum_s2 18 +#define regnum_s3 19 +#define regnum_s4 20 +#define regnum_s5 21 +#define regnum_s6 22 +#define regnum_s7 23 +#define regnum_s8 24 +#define regnum_s9 25 +#define regnum_s10 26 +#define regnum_s11 27 +#define regnum_t3 28 +#define regnum_t4 29 +#define regnum_t5 30 +#define regnum_t6 31 + +// x8 is s0 and also fp +#define regnum_fp 8 + +#define r_type_insn(_f7, _rs2, _rs1, _f3, _rd, _opc) \ +.word (((_f7) << 25) | ((_rs2) << 20) | ((_rs1) << 15) | ((_f3) << 12) | ((_rd) << 7) | ((_opc) << 0)) + +#define picorv32_getq_insn(_rd, _qs) \ +r_type_insn(0b0000000, 0, regnum_ ## _qs, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_setq_insn(_qd, _rs) \ +r_type_insn(0b0000001, 0, regnum_ ## _rs, 0b010, regnum_ ## _qd, 0b0001011) + +#define picorv32_retirq_insn() \ +r_type_insn(0b0000010, 0, 0, 0b000, 0, 0b0001011) + +#define picorv32_maskirq_insn(_rd, _rs) \ +r_type_insn(0b0000011, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + +#define picorv32_waitirq_insn(_rd) \ +r_type_insn(0b0000100, 0, 0, 0b100, regnum_ ## _rd, 0b0001011) + +#define picorv32_timer_insn(_rd, _rs) \ +r_type_insn(0b0000101, 0, regnum_ ## _rs, 0b110, regnum_ ## _rd, 0b0001011) + diff --git a/firmware/firmware.h b/firmware/firmware.h new file mode 100644 index 0000000..59d3f11 --- /dev/null +++ b/firmware/firmware.h @@ -0,0 +1,43 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#ifndef FIRMWARE_H +#define FIRMWARE_H + +#include +#include + +// irq.c +uint32_t *irq(uint32_t *regs, uint32_t irqs); + +// print.c +void print_chr(char ch); +void print_str(const char *p); +void print_dec(unsigned int val); +void print_hex(unsigned int val, int digits); + +// hello.c +void hello(void); + +// sieve.c +void sieve(void); + +// multest.c +uint32_t hard_mul(uint32_t a, uint32_t b); +uint32_t hard_mulh(uint32_t a, uint32_t b); +uint32_t hard_mulhsu(uint32_t a, uint32_t b); +uint32_t hard_mulhu(uint32_t a, uint32_t b); +uint32_t hard_div(uint32_t a, uint32_t b); +uint32_t hard_divu(uint32_t a, uint32_t b); +uint32_t hard_rem(uint32_t a, uint32_t b); +uint32_t hard_remu(uint32_t a, uint32_t b); +void multest(void); + +// stats.c +void stats(void); + +#endif diff --git a/firmware/hello.c b/firmware/hello.c new file mode 100644 index 0000000..b7e0b96 --- /dev/null +++ b/firmware/hello.c @@ -0,0 +1,14 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#include "firmware.h" + +void hello(void) +{ + print_str("hello world\n"); +} + diff --git a/firmware/irq.c b/firmware/irq.c new file mode 100644 index 0000000..9fc1735 --- /dev/null +++ b/firmware/irq.c @@ -0,0 +1,140 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#include "firmware.h" + +uint32_t *irq(uint32_t *regs, uint32_t irqs) +{ + static unsigned int ext_irq_4_count = 0; + static unsigned int ext_irq_5_count = 0; + static unsigned int timer_irq_count = 0; + + // checking compressed isa q0 reg handling + if ((irqs & 6) != 0) { + uint32_t pc = (regs[0] & 1) ? regs[0] - 3 : regs[0] - 4; + uint32_t instr = *(uint16_t*)pc; + + if ((instr & 3) == 3) + instr = instr | (*(uint16_t*)(pc + 2)) << 16; + + if (((instr & 3) != 3) != (regs[0] & 1)) { + print_str("Mismatch between q0 LSB and decoded instruction word! q0=0x"); + print_hex(regs[0], 8); + print_str(", instr=0x"); + if ((instr & 3) == 3) + print_hex(instr, 8); + else + print_hex(instr, 4); + print_str("\n"); + __asm__ volatile ("ebreak"); + } + } + + if ((irqs & (1<<4)) != 0) { + ext_irq_4_count++; + // print_str("[EXT-IRQ-4]"); + } + + if ((irqs & (1<<5)) != 0) { + ext_irq_5_count++; + // print_str("[EXT-IRQ-5]"); + } + + if ((irqs & 1) != 0) { + timer_irq_count++; + // print_str("[TIMER-IRQ]"); + } + + if ((irqs & 6) != 0) + { + uint32_t pc = (regs[0] & 1) ? regs[0] - 3 : regs[0] - 4; + uint32_t instr = *(uint16_t*)pc; + + if ((instr & 3) == 3) + instr = instr | (*(uint16_t*)(pc + 2)) << 16; + + print_str("\n"); + print_str("------------------------------------------------------------\n"); + + if ((irqs & 2) != 0) { + if (instr == 0x00100073 || instr == 0x9002) { + print_str("EBREAK instruction at 0x"); + print_hex(pc, 8); + print_str("\n"); + } else { + print_str("Illegal Instruction at 0x"); + print_hex(pc, 8); + print_str(": 0x"); + print_hex(instr, ((instr & 3) == 3) ? 8 : 4); + print_str("\n"); + } + } + + if ((irqs & 4) != 0) { + print_str("Bus error in Instruction at 0x"); + print_hex(pc, 8); + print_str(": 0x"); + print_hex(instr, ((instr & 3) == 3) ? 8 : 4); + print_str("\n"); + } + + for (int i = 0; i < 8; i++) + for (int k = 0; k < 4; k++) + { + int r = i + k*8; + + if (r == 0) { + print_str("pc "); + } else + if (r < 10) { + print_chr('x'); + print_chr('0' + r); + print_chr(' '); + print_chr(' '); + } else + if (r < 20) { + print_chr('x'); + print_chr('1'); + print_chr('0' + r - 10); + print_chr(' '); + } else + if (r < 30) { + print_chr('x'); + print_chr('2'); + print_chr('0' + r - 20); + print_chr(' '); + } else { + print_chr('x'); + print_chr('3'); + print_chr('0' + r - 30); + print_chr(' '); + } + + print_hex(regs[r], 8); + print_str(k == 3 ? "\n" : " "); + } + + print_str("------------------------------------------------------------\n"); + + print_str("Number of fast external IRQs counted: "); + print_dec(ext_irq_4_count); + print_str("\n"); + + print_str("Number of slow external IRQs counted: "); + print_dec(ext_irq_5_count); + print_str("\n"); + + print_str("Number of timer IRQs counted: "); + print_dec(timer_irq_count); + print_str("\n"); + + __asm__ volatile ("ebreak"); + } + + return regs; +} + diff --git a/firmware/makehex.py b/firmware/makehex.py new file mode 100644 index 0000000..419b378 --- /dev/null +++ b/firmware/makehex.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. + +from sys import argv + +binfile = argv[1] +nwords = int(argv[2]) + +with open(binfile, "rb") as f: + bindata = f.read() + +assert len(bindata) < 4*nwords +assert len(bindata) % 4 == 0 + +for i in range(nwords): + if i < len(bindata) // 4: + w = bindata[4*i : 4*i+4] + print("%02x%02x%02x%02x" % (w[3], w[2], w[1], w[0])) + else: + print("0") + diff --git a/firmware/multest.c b/firmware/multest.c new file mode 100644 index 0000000..1706400 --- /dev/null +++ b/firmware/multest.c @@ -0,0 +1,151 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#include "firmware.h" + +static uint32_t xorshift32(void) { + static uint32_t x = 314159265; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return x; +} + +void multest(void) +{ + for (int i = 0; i < 15; i++) + { + uint32_t a = xorshift32(); + uint32_t b = xorshift32(); + + switch (i) + { + case 0: + a = 0x80000000; + b = 0xFFFFFFFF; + break; + case 1: + a = 0; + b = 0; + break; + case 2: + a |= 0x80000000; + b = 0; + break; + case 3: + a &= 0x7FFFFFFF; + b = 0; + break; + } + + uint64_t au = a, bu = b; + int64_t as = (int32_t)a, bs = (int32_t)b; + + print_str("input ["); + print_hex(as >> 32, 8); + print_str("] "); + print_hex(a, 8); + print_str(" ["); + print_hex(bs >> 32, 8); + print_str("] "); + print_hex(b, 8); + print_chr('\n'); + + uint32_t h_mul, h_mulh, h_mulhsu, h_mulhu; + print_str("hard mul "); + + h_mul = hard_mul(a, b); + print_hex(h_mul, 8); + print_str(" "); + + h_mulh = hard_mulh(a, b); + print_hex(h_mulh, 8); + print_str(" "); + + h_mulhsu = hard_mulhsu(a, b); + print_hex(h_mulhsu, 8); + print_str(" "); + + h_mulhu = hard_mulhu(a, b); + print_hex(h_mulhu, 8); + print_chr('\n'); + + uint32_t s_mul, s_mulh, s_mulhsu, s_mulhu; + print_str("soft mul "); + + s_mul = a * b; + print_hex(s_mul, 8); + print_str(" "); + + s_mulh = (as * bs) >> 32; + print_hex(s_mulh, 8); + print_str(" "); + + s_mulhsu = (as * bu) >> 32; + print_hex(s_mulhsu, 8); + print_str(" "); + + s_mulhu = (au * bu) >> 32; + print_hex(s_mulhu, 8); + print_str(" "); + + if (s_mul != h_mul || s_mulh != h_mulh || s_mulhsu != h_mulhsu || s_mulhu != h_mulhu) { + print_str("ERROR!\n"); + __asm__ volatile ("ebreak"); + return; + } + + print_str(" OK\n"); + + uint32_t h_div, h_divu, h_rem, h_remu; + print_str("hard div "); + + h_div = hard_div(a, b); + print_hex(h_div, 8); + print_str(" "); + + h_divu = hard_divu(a, b); + print_hex(h_divu, 8); + print_str(" "); + + h_rem = hard_rem(a, b); + print_hex(h_rem, 8); + print_str(" "); + + h_remu = hard_remu(a, b); + print_hex(h_remu, 8); + print_chr('\n'); + + uint32_t s_div, s_divu, s_rem, s_remu; + print_str("soft div "); + + s_div = b ? as / bs : 0xffffffff; + print_hex(s_div, 8); + print_str(" "); + + s_divu = b ? au / bu : 0xffffffff; + print_hex(s_divu, 8); + print_str(" "); + + s_rem = b ? as % bs : a; + print_hex(s_rem, 8); + print_str(" "); + + s_remu = b ? au % bu : a; + print_hex(s_remu, 8); + print_str(" "); + + if (s_div != h_div || s_divu != h_divu || s_rem != h_rem || s_remu != h_remu) { + print_str("ERROR!\n"); + __asm__ volatile ("ebreak"); + return; + } + + print_str(" OK\n"); + } +} + diff --git a/firmware/print.c b/firmware/print.c new file mode 100644 index 0000000..accce26 --- /dev/null +++ b/firmware/print.c @@ -0,0 +1,41 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#include "firmware.h" + +#define OUTPORT 0x10000000 + +void print_chr(char ch) +{ + *((volatile uint32_t*)OUTPORT) = ch; +} + +void print_str(const char *p) +{ + while (*p != 0) + *((volatile uint32_t*)OUTPORT) = *(p++); +} + +void print_dec(unsigned int val) +{ + char buffer[10]; + char *p = buffer; + while (val || p == buffer) { + *(p++) = val % 10; + val = val / 10; + } + while (p != buffer) { + *((volatile uint32_t*)OUTPORT) = '0' + *(--p); + } +} + +void print_hex(unsigned int val, int digits) +{ + for (int i = (4*digits)-4; i >= 0; i -= 4) + *((volatile uint32_t*)OUTPORT) = "0123456789ABCDEF"[(val >> i) % 16]; +} + diff --git a/firmware/riscv.ld b/firmware/riscv.ld new file mode 100644 index 0000000..16fde8d --- /dev/null +++ b/firmware/riscv.ld @@ -0,0 +1,200 @@ +/* ---- Original Script: /opt/riscv32i/riscv32-unknown-elf/lib/ldscripts/elf32lriscv.x ---- */ +/* Default linker script, for normal executables */ +/* Copyright (C) 2014-2017 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", + "elf32-littleriscv") +OUTPUT_ARCH(riscv) +ENTRY(_start) +SECTIONS +{ + . = 0x00010000; + .text : + { + *(.text) + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } + .init : + { + KEEP (*(SORT_NONE(.init))) + } + .plt : { *(.plt) } + .iplt : { *(.iplt) } + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .sdata2 : + { + *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) + } + .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table + .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges + .exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } + /* Thread Local Storage sections */ + .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + . = DATA_SEGMENT_RELRO_END (0, .); + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : + { + __global_pointer$ = . + 0x800; + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .sbss : + { + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + } + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we don't + pad the .data section. */ + . = ALIGN(. != 0 ? 32 / 8 : 1); + } + . = ALIGN(32 / 8); + . = SEGMENT_START("ldata-segment", .); + . = ALIGN(32 / 8); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF Extension. */ + .debug_macro 0 : { *(.debug_macro) } + .debug_addr 0 : { *(.debug_addr) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} diff --git a/firmware/riscv.ld.orig b/firmware/riscv.ld.orig new file mode 100644 index 0000000..6d40e3d --- /dev/null +++ b/firmware/riscv.ld.orig @@ -0,0 +1,236 @@ +/* ---- Original Script: /opt/riscv32i/riscv32-unknown-elf/lib/ldscripts/elf32lriscv.x ---- */ +/* Default linker script, for normal executables */ +/* Copyright (C) 2014-2017 Free Software Foundation, Inc. + Copying and distribution of this script, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. */ +OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", + "elf32-littleriscv") +OUTPUT_ARCH(riscv) +ENTRY(_start) +SEARCH_DIR("/opt/riscv32i/riscv32-unknown-elf/lib"); +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x10000)); . = SEGMENT_START("text-segment", 0x10000) + SIZEOF_HEADERS; + .interp : { *(.interp) } + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rela.init : { *(.rela.init) } + .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) } + .rela.fini : { *(.rela.fini) } + .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) } + .rela.data.rel.ro : { *(.rela.data.rel.ro .rela.data.rel.ro.* .rela.gnu.linkonce.d.rel.ro.*) } + .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) } + .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) } + .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) } + .rela.ctors : { *(.rela.ctors) } + .rela.dtors : { *(.rela.dtors) } + .rela.got : { *(.rela.got) } + .rela.sdata : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) } + .rela.sbss : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) } + .rela.sdata2 : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) } + .rela.sbss2 : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) } + .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) } + .rela.iplt : + { + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + .rela.plt : + { + *(.rela.plt) + } + .init : + { + KEEP (*(SORT_NONE(.init))) + } + .plt : { *(.plt) } + .iplt : { *(.iplt) } + .text : + { + *(.text.unlikely .text.*_unlikely .text.unlikely.*) + *(.text.exit .text.exit.*) + *(.text.startup .text.startup.*) + *(.text.hot .text.hot.*) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + } + .fini : + { + KEEP (*(SORT_NONE(.fini))) + } + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .sdata2 : + { + *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) + } + .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } + .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table + .gcc_except_table.*) } + .gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } + /* These sections are generated by the Sun/Oracle C++ compiler. */ + .exception_ranges : ONLY_IF_RO { *(.exception_ranges + .exception_ranges*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } + .gnu_extab : ONLY_IF_RW { *(.gnu_extab) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + .exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } + /* Thread Local Storage sections */ + .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + KEEP (*crtbegin?.o(.ctors)) + /* We don't want to include the .ctor section from + the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*crtbegin?.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + . = DATA_SEGMENT_RELRO_END (0, .); + .data : + { + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : + { + __global_pointer$ = . + 0x800; + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + } + _edata = .; PROVIDE (edata = .); + . = .; + __bss_start = .; + .sbss : + { + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + } + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we don't + pad the .data section. */ + . = ALIGN(. != 0 ? 32 / 8 : 1); + } + . = ALIGN(32 / 8); + . = SEGMENT_START("ldata-segment", .); + . = ALIGN(32 / 8); + _end = .; PROVIDE (end = .); + . = DATA_SEGMENT_END (.); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end ) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + /* DWARF Extension. */ + .debug_macro 0 : { *(.debug_macro) } + .debug_addr 0 : { *(.debug_addr) } + .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} diff --git a/firmware/sections.lds b/firmware/sections.lds new file mode 100644 index 0000000..f914b46 --- /dev/null +++ b/firmware/sections.lds @@ -0,0 +1,25 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. +*/ + +MEMORY { + /* the memory in the testbench is 128k in size; + * set LENGTH=96k and leave at least 32k for stack */ + mem : ORIGIN = 0x00000000, LENGTH = 0x00018000 +} + +SECTIONS { + .memory : { + . = 0x000000; + start*(.text); + *(.text); + *(*); + end = .; + . = ALIGN(4); + } > mem +} diff --git a/firmware/sieve.c b/firmware/sieve.c new file mode 100644 index 0000000..ff945eb --- /dev/null +++ b/firmware/sieve.c @@ -0,0 +1,84 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +// A simple Sieve of Eratosthenes + +#include "firmware.h" + +#define BITMAP_SIZE 64 + +static uint32_t bitmap[BITMAP_SIZE/32]; +static uint32_t hash; + +static uint32_t mkhash(uint32_t a, uint32_t b) +{ + // The XOR version of DJB2 + return ((a << 5) + a) ^ b; +} + +static void bitmap_set(int idx) +{ + bitmap[idx/32] |= 1 << (idx % 32); +} + +static bool bitmap_get(int idx) +{ + return (bitmap[idx/32] & (1 << (idx % 32))) != 0; +} + +static void print_prime(int idx, int val) +{ + if (idx < 10) + print_str(" "); + print_dec(idx); + if (idx / 10 == 1) + goto force_th; + switch (idx % 10) { + case 1: print_str("st"); break; + case 2: print_str("nd"); break; + case 3: print_str("rd"); break; + force_th: + default: print_str("th"); break; + } + print_str(" prime is "); + print_dec(val); + print_str(".\n"); + + hash = mkhash(hash, idx); + hash = mkhash(hash, val); +} + +void sieve(void) +{ + int idx = 1; + hash = 5381; + print_prime(idx++, 2); + for (int i = 0; i < BITMAP_SIZE; i++) { + if (bitmap_get(i)) + continue; + print_prime(idx++, 3+2*i); + for (int j = 2*(3+2*i);; j += 3+2*i) { + if (j%2 == 0) + continue; + int k = (j-3)/2; + if (k >= BITMAP_SIZE) + break; + bitmap_set(k); + } + } + + print_str("checksum: "); + print_hex(hash, 8); + + if (hash == 0x1772A48F) { + print_str(" OK\n"); + } else { + print_str(" ERROR\n"); + __asm__ volatile ("ebreak"); + } +} + diff --git a/firmware/start.S b/firmware/start.S new file mode 100644 index 0000000..d95f04c --- /dev/null +++ b/firmware/start.S @@ -0,0 +1,537 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#define ENABLE_QREGS +#define ENABLE_HELLO +#define ENABLE_RVTST +#define ENABLE_SIEVE +#define ENABLE_MULTST +#define ENABLE_STATS + +#ifndef ENABLE_QREGS +# undef ENABLE_RVTST +#endif + +// Only save registers in IRQ wrapper that are to be saved by the caller in +// the RISC-V ABI, with the excpetion of the stack pointer. The IRQ handler +// will save the rest if necessary. I.e. skip x3, x4, x8, x9, and x18-x27. +#undef ENABLE_FASTIRQ + +#include "custom_ops.S" + + .section .text + .global irq + .global hello + .global sieve + .global multest + .global hard_mul + .global hard_mulh + .global hard_mulhsu + .global hard_mulhu + .global hard_div + .global hard_divu + .global hard_rem + .global hard_remu + .global stats + +reset_vec: + // no more than 16 bytes here ! + picorv32_waitirq_insn(zero) + picorv32_maskirq_insn(zero, zero) + j start + + +/* Interrupt handler + **********************************/ + +.balign 16 +irq_vec: + /* save registers */ + +#ifdef ENABLE_QREGS + + picorv32_setq_insn(q2, x1) + picorv32_setq_insn(q3, x2) + + lui x1, %hi(irq_regs) + addi x1, x1, %lo(irq_regs) + + picorv32_getq_insn(x2, q0) + sw x2, 0*4(x1) + + picorv32_getq_insn(x2, q2) + sw x2, 1*4(x1) + + picorv32_getq_insn(x2, q3) + sw x2, 2*4(x1) + +#ifdef ENABLE_FASTIRQ + sw x5, 5*4(x1) + sw x6, 6*4(x1) + sw x7, 7*4(x1) + sw x10, 10*4(x1) + sw x11, 11*4(x1) + sw x12, 12*4(x1) + sw x13, 13*4(x1) + sw x14, 14*4(x1) + sw x15, 15*4(x1) + sw x16, 16*4(x1) + sw x17, 17*4(x1) + sw x28, 28*4(x1) + sw x29, 29*4(x1) + sw x30, 30*4(x1) + sw x31, 31*4(x1) +#else + sw x3, 3*4(x1) + sw x4, 4*4(x1) + sw x5, 5*4(x1) + sw x6, 6*4(x1) + sw x7, 7*4(x1) + sw x8, 8*4(x1) + sw x9, 9*4(x1) + sw x10, 10*4(x1) + sw x11, 11*4(x1) + sw x12, 12*4(x1) + sw x13, 13*4(x1) + sw x14, 14*4(x1) + sw x15, 15*4(x1) + sw x16, 16*4(x1) + sw x17, 17*4(x1) + sw x18, 18*4(x1) + sw x19, 19*4(x1) + sw x20, 20*4(x1) + sw x21, 21*4(x1) + sw x22, 22*4(x1) + sw x23, 23*4(x1) + sw x24, 24*4(x1) + sw x25, 25*4(x1) + sw x26, 26*4(x1) + sw x27, 27*4(x1) + sw x28, 28*4(x1) + sw x29, 29*4(x1) + sw x30, 30*4(x1) + sw x31, 31*4(x1) +#endif + +#else // ENABLE_QREGS + +#ifdef ENABLE_FASTIRQ + sw gp, 0*4+0x200(zero) + sw x1, 1*4+0x200(zero) + sw x2, 2*4+0x200(zero) + sw x5, 5*4+0x200(zero) + sw x6, 6*4+0x200(zero) + sw x7, 7*4+0x200(zero) + sw x10, 10*4+0x200(zero) + sw x11, 11*4+0x200(zero) + sw x12, 12*4+0x200(zero) + sw x13, 13*4+0x200(zero) + sw x14, 14*4+0x200(zero) + sw x15, 15*4+0x200(zero) + sw x16, 16*4+0x200(zero) + sw x17, 17*4+0x200(zero) + sw x28, 28*4+0x200(zero) + sw x29, 29*4+0x200(zero) + sw x30, 30*4+0x200(zero) + sw x31, 31*4+0x200(zero) +#else + sw gp, 0*4+0x200(zero) + sw x1, 1*4+0x200(zero) + sw x2, 2*4+0x200(zero) + sw x3, 3*4+0x200(zero) + sw x4, 4*4+0x200(zero) + sw x5, 5*4+0x200(zero) + sw x6, 6*4+0x200(zero) + sw x7, 7*4+0x200(zero) + sw x8, 8*4+0x200(zero) + sw x9, 9*4+0x200(zero) + sw x10, 10*4+0x200(zero) + sw x11, 11*4+0x200(zero) + sw x12, 12*4+0x200(zero) + sw x13, 13*4+0x200(zero) + sw x14, 14*4+0x200(zero) + sw x15, 15*4+0x200(zero) + sw x16, 16*4+0x200(zero) + sw x17, 17*4+0x200(zero) + sw x18, 18*4+0x200(zero) + sw x19, 19*4+0x200(zero) + sw x20, 20*4+0x200(zero) + sw x21, 21*4+0x200(zero) + sw x22, 22*4+0x200(zero) + sw x23, 23*4+0x200(zero) + sw x24, 24*4+0x200(zero) + sw x25, 25*4+0x200(zero) + sw x26, 26*4+0x200(zero) + sw x27, 27*4+0x200(zero) + sw x28, 28*4+0x200(zero) + sw x29, 29*4+0x200(zero) + sw x30, 30*4+0x200(zero) + sw x31, 31*4+0x200(zero) +#endif + +#endif // ENABLE_QREGS + + /* call interrupt handler C function */ + + lui sp, %hi(irq_stack) + addi sp, sp, %lo(irq_stack) + + // arg0 = address of regs + lui a0, %hi(irq_regs) + addi a0, a0, %lo(irq_regs) + + // arg1 = interrupt type +#ifdef ENABLE_QREGS + picorv32_getq_insn(a1, q1) +#else + addi a1, tp, 0 +#endif + + // call to C function + jal ra, irq + + /* restore registers */ + +#ifdef ENABLE_QREGS + + // new irq_regs address returned from C code in a0 + addi x1, a0, 0 + + lw x2, 0*4(x1) + picorv32_setq_insn(q0, x2) + + lw x2, 1*4(x1) + picorv32_setq_insn(q1, x2) + + lw x2, 2*4(x1) + picorv32_setq_insn(q2, x2) + +#ifdef ENABLE_FASTIRQ + lw x5, 5*4(x1) + lw x6, 6*4(x1) + lw x7, 7*4(x1) + lw x10, 10*4(x1) + lw x11, 11*4(x1) + lw x12, 12*4(x1) + lw x13, 13*4(x1) + lw x14, 14*4(x1) + lw x15, 15*4(x1) + lw x16, 16*4(x1) + lw x17, 17*4(x1) + lw x28, 28*4(x1) + lw x29, 29*4(x1) + lw x30, 30*4(x1) + lw x31, 31*4(x1) +#else + lw x3, 3*4(x1) + lw x4, 4*4(x1) + lw x5, 5*4(x1) + lw x6, 6*4(x1) + lw x7, 7*4(x1) + lw x8, 8*4(x1) + lw x9, 9*4(x1) + lw x10, 10*4(x1) + lw x11, 11*4(x1) + lw x12, 12*4(x1) + lw x13, 13*4(x1) + lw x14, 14*4(x1) + lw x15, 15*4(x1) + lw x16, 16*4(x1) + lw x17, 17*4(x1) + lw x18, 18*4(x1) + lw x19, 19*4(x1) + lw x20, 20*4(x1) + lw x21, 21*4(x1) + lw x22, 22*4(x1) + lw x23, 23*4(x1) + lw x24, 24*4(x1) + lw x25, 25*4(x1) + lw x26, 26*4(x1) + lw x27, 27*4(x1) + lw x28, 28*4(x1) + lw x29, 29*4(x1) + lw x30, 30*4(x1) + lw x31, 31*4(x1) +#endif + + picorv32_getq_insn(x1, q1) + picorv32_getq_insn(x2, q2) + +#else // ENABLE_QREGS + + // new irq_regs address returned from C code in a0 + addi a1, zero, 0x200 + beq a0, a1, 1f + ebreak +1: + +#ifdef ENABLE_FASTIRQ + lw gp, 0*4+0x200(zero) + lw x1, 1*4+0x200(zero) + lw x2, 2*4+0x200(zero) + lw x5, 5*4+0x200(zero) + lw x6, 6*4+0x200(zero) + lw x7, 7*4+0x200(zero) + lw x10, 10*4+0x200(zero) + lw x11, 11*4+0x200(zero) + lw x12, 12*4+0x200(zero) + lw x13, 13*4+0x200(zero) + lw x14, 14*4+0x200(zero) + lw x15, 15*4+0x200(zero) + lw x16, 16*4+0x200(zero) + lw x17, 17*4+0x200(zero) + lw x28, 28*4+0x200(zero) + lw x29, 29*4+0x200(zero) + lw x30, 30*4+0x200(zero) + lw x31, 31*4+0x200(zero) +#else + lw gp, 0*4+0x200(zero) + lw x1, 1*4+0x200(zero) + lw x2, 2*4+0x200(zero) + // do not restore x3 (gp) + lw x4, 4*4+0x200(zero) + lw x5, 5*4+0x200(zero) + lw x6, 6*4+0x200(zero) + lw x7, 7*4+0x200(zero) + lw x8, 8*4+0x200(zero) + lw x9, 9*4+0x200(zero) + lw x10, 10*4+0x200(zero) + lw x11, 11*4+0x200(zero) + lw x12, 12*4+0x200(zero) + lw x13, 13*4+0x200(zero) + lw x14, 14*4+0x200(zero) + lw x15, 15*4+0x200(zero) + lw x16, 16*4+0x200(zero) + lw x17, 17*4+0x200(zero) + lw x18, 18*4+0x200(zero) + lw x19, 19*4+0x200(zero) + lw x20, 20*4+0x200(zero) + lw x21, 21*4+0x200(zero) + lw x22, 22*4+0x200(zero) + lw x23, 23*4+0x200(zero) + lw x24, 24*4+0x200(zero) + lw x25, 25*4+0x200(zero) + lw x26, 26*4+0x200(zero) + lw x27, 27*4+0x200(zero) + lw x28, 28*4+0x200(zero) + lw x29, 29*4+0x200(zero) + lw x30, 30*4+0x200(zero) + lw x31, 31*4+0x200(zero) +#endif + +#endif // ENABLE_QREGS + + picorv32_retirq_insn() + +#ifndef ENABLE_QREGS +.balign 0x200 +#endif +irq_regs: + // registers are saved to this memory region during interrupt handling + // the program counter is saved as register 0 + .fill 32,4 + + // stack for the interrupt handler + .fill 128,4 +irq_stack: + + +/* Main program + **********************************/ + +start: + /* zero-initialize all registers */ + + addi x1, zero, 0 + addi x2, zero, 0 + addi x3, zero, 0 + addi x4, zero, 0 + addi x5, zero, 0 + addi x6, zero, 0 + addi x7, zero, 0 + addi x8, zero, 0 + addi x9, zero, 0 + addi x10, zero, 0 + addi x11, zero, 0 + addi x12, zero, 0 + addi x13, zero, 0 + addi x14, zero, 0 + addi x15, zero, 0 + addi x16, zero, 0 + addi x17, zero, 0 + addi x18, zero, 0 + addi x19, zero, 0 + addi x20, zero, 0 + addi x21, zero, 0 + addi x22, zero, 0 + addi x23, zero, 0 + addi x24, zero, 0 + addi x25, zero, 0 + addi x26, zero, 0 + addi x27, zero, 0 + addi x28, zero, 0 + addi x29, zero, 0 + addi x30, zero, 0 + addi x31, zero, 0 + +#ifdef ENABLE_HELLO + /* set stack pointer */ + lui sp,(128*1024)>>12 + + /* call hello C code */ + jal ra,hello +#endif + + /* running tests from riscv-tests */ + +#ifdef ENABLE_RVTST +# define TEST(n) \ + .global n; \ + addi x1, zero, 1000; \ + picorv32_timer_insn(zero, x1); \ + jal zero,n; \ + .global n ## _ret; \ + n ## _ret: +#else +# define TEST(n) \ + .global n ## _ret; \ + n ## _ret: +#endif + + TEST(lui) + TEST(auipc) + TEST(j) + TEST(jal) + TEST(jalr) + + TEST(beq) + TEST(bne) + TEST(blt) + TEST(bge) + TEST(bltu) + TEST(bgeu) + + TEST(lb) + TEST(lh) + TEST(lw) + TEST(lbu) + TEST(lhu) + + TEST(sb) + TEST(sh) + TEST(sw) + + TEST(addi) + TEST(slti) // also tests sltiu + TEST(xori) + TEST(ori) + TEST(andi) + TEST(slli) + TEST(srli) + TEST(srai) + + TEST(add) + TEST(sub) + TEST(sll) + TEST(slt) // what is with sltu ? + TEST(xor) + TEST(srl) + TEST(sra) + TEST(or) + TEST(and) + + TEST(mulh) + TEST(mulhsu) + TEST(mulhu) + TEST(mul) + + TEST(div) + TEST(divu) + TEST(rem) + TEST(remu) + + TEST(simple) + + /* set stack pointer */ + lui sp,(128*1024)>>12 + + /* set gp and tp */ + lui gp, %hi(0xdeadbeef) + addi gp, gp, %lo(0xdeadbeef) + addi tp, gp, 0 + +#ifdef ENABLE_SIEVE + /* call sieve C code */ + jal ra,sieve +#endif + +#ifdef ENABLE_MULTST + /* call multest C code */ + jal ra,multest +#endif + +#ifdef ENABLE_STATS + /* call stats C code */ + jal ra,stats +#endif + + /* print "DONE\n" */ + lui a0,0x10000000>>12 + addi a1,zero,'D' + addi a2,zero,'O' + addi a3,zero,'N' + addi a4,zero,'E' + addi a5,zero,'\n' + sw a1,0(a0) + sw a2,0(a0) + sw a3,0(a0) + sw a4,0(a0) + sw a5,0(a0) + + li a0, 0x20000000 + li a1, 123456789 + sw a1,0(a0) + + /* trap */ + ebreak + + +/* Hard mul functions for multest.c + **********************************/ + +hard_mul: + mul a0, a0, a1 + ret + +hard_mulh: + mulh a0, a0, a1 + ret + +hard_mulhsu: + mulhsu a0, a0, a1 + ret + +hard_mulhu: + mulhu a0, a0, a1 + ret + +hard_div: + div a0, a0, a1 + ret + +hard_divu: + divu a0, a0, a1 + ret + +hard_rem: + rem a0, a0, a1 + ret + +hard_remu: + remu a0, a0, a1 + ret + diff --git a/firmware/stats.c b/firmware/stats.c new file mode 100644 index 0000000..80e22dd --- /dev/null +++ b/firmware/stats.c @@ -0,0 +1,42 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +#include "firmware.h" + +static void stats_print_dec(unsigned int val, int digits, bool zero_pad) +{ + char buffer[32]; + char *p = buffer; + while (val || digits > 0) { + if (val) + *(p++) = '0' + val % 10; + else + *(p++) = zero_pad ? '0' : ' '; + val = val / 10; + digits--; + } + while (p != buffer) { + if (p[-1] == ' ' && p[-2] == ' ') p[-1] = '.'; + print_chr(*(--p)); + } +} + +void stats(void) +{ + unsigned int num_cycles, num_instr; + __asm__ volatile ("rdcycle %0; rdinstret %1;" : "=r"(num_cycles), "=r"(num_instr)); + print_str("Cycle counter ........"); + stats_print_dec(num_cycles, 8, false); + print_str("\nInstruction counter .."); + stats_print_dec(num_instr, 8, false); + print_str("\nCPI: "); + stats_print_dec((num_cycles / num_instr), 0, false); + print_str("."); + stats_print_dec(((100 * num_cycles) / num_instr) % 100, 2, true); + print_str("\n"); +} + diff --git a/picorv32.core b/picorv32.core new file mode 100644 index 0000000..08ee704 --- /dev/null +++ b/picorv32.core @@ -0,0 +1,78 @@ +CAPI=2: +name : ::picorv32:0-r1 + +filesets: + rtl: + files: [picorv32.v] + file_type : verilogSource + tb: + files: [testbench.v] + file_type : verilogSource + depend: + tb_ez: + files: [testbench_ez.v] + file_type : verilogSource + tb_wb: + files: [testbench_wb.v] + file_type : verilogSource + tb_verilator: + files: + - testbench.cc : {file_type : cppSource} + +targets: + default: + filesets: [rtl] + lint: + filesets: [rtl] + default_tool : verilator + tools: + verilator: + mode : lint-only + toplevel : [picorv32_axi] + test: + default_tool: icarus + filesets: [rtl, tb, "tool_verilator? (tb_verilator)"] + parameters: [COMPRESSED_ISA, axi_test, firmware, noerror, trace, vcd, verbose] + toplevel: + - "tool_verilator? (picorv32_wrapper)" + - "!tool_verilator? (testbench)" + + tools: + verilator : + cli_parser : fusesoc + mode : cc + verilator_options : [-Wno-fatal, --trace] + test_ez: + default_tool: icarus + filesets: [rtl, tb_ez] + parameters: [vcd] + toplevel: [testbench] + test_wb: + default_tool: icarus + filesets: [rtl, tb_wb] + parameters: [COMPRESSED_ISA, firmware, noerror, trace, vcd] + toplevel: [testbench] + +parameters: + COMPRESSED_ISA: + datatype : str + default : 1 + paramtype : vlogdefine + axi_test: + datatype : bool + paramtype : plusarg + firmware: + datatype : file + paramtype : plusarg + noerror: + datatype : bool + paramtype : plusarg + trace: + datatype : bool + paramtype : plusarg + vcd: + datatype : bool + paramtype : plusarg + verbose: + datatype : bool + paramtype : plusarg diff --git a/picorv32.v b/picorv32.v new file mode 100644 index 0000000..cfc7ce0 --- /dev/null +++ b/picorv32.v @@ -0,0 +1,3044 @@ +/* + * PicoRV32 -- A Small RISC-V (RV32I) Processor Core + * + * Copyright (C) 2015 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* verilator lint_off WIDTH */ +/* verilator lint_off PINMISSING */ +/* verilator lint_off CASEOVERLAP */ +/* verilator lint_off CASEINCOMPLETE */ + +`timescale 1 ns / 1 ps +// `default_nettype none +// `define DEBUGNETS +// `define DEBUGREGS +// `define DEBUGASM +// `define DEBUG + +`ifdef DEBUG + `define debug(debug_command) debug_command +`else + `define debug(debug_command) +`endif + +`ifdef FORMAL + `define FORMAL_KEEP (* keep *) + `define assert(assert_expr) assert(assert_expr) +`else + `ifdef DEBUGNETS + `define FORMAL_KEEP (* keep *) + `else + `define FORMAL_KEEP + `endif + `define assert(assert_expr) empty_statement +`endif + +// uncomment this for register file in extra module +// `define PICORV32_REGS picorv32_regs + +// this macro can be used to check if the verilog files in your +// design are read in the correct order. +`define PICORV32_V + + +/*************************************************************** + * picorv32 + ***************************************************************/ + +module picorv32 #( + parameter [ 0:0] ENABLE_COUNTERS = 1, + parameter [ 0:0] ENABLE_COUNTERS64 = 1, + parameter [ 0:0] ENABLE_REGS_16_31 = 1, + parameter [ 0:0] ENABLE_REGS_DUALPORT = 1, + parameter [ 0:0] LATCHED_MEM_RDATA = 0, + parameter [ 0:0] TWO_STAGE_SHIFT = 1, + parameter [ 0:0] BARREL_SHIFTER = 0, + parameter [ 0:0] TWO_CYCLE_COMPARE = 0, + parameter [ 0:0] TWO_CYCLE_ALU = 0, + parameter [ 0:0] COMPRESSED_ISA = 0, + parameter [ 0:0] CATCH_MISALIGN = 1, + parameter [ 0:0] CATCH_ILLINSN = 1, + parameter [ 0:0] ENABLE_PCPI = 0, + parameter [ 0:0] ENABLE_MUL = 0, + parameter [ 0:0] ENABLE_FAST_MUL = 0, + parameter [ 0:0] ENABLE_DIV = 0, + parameter [ 0:0] ENABLE_IRQ = 0, + parameter [ 0:0] ENABLE_IRQ_QREGS = 1, + parameter [ 0:0] ENABLE_IRQ_TIMER = 1, + parameter [ 0:0] ENABLE_TRACE = 0, + parameter [ 0:0] REGS_INIT_ZERO = 0, + parameter [31:0] MASKED_IRQ = 32'h 0000_0000, + parameter [31:0] LATCHED_IRQ = 32'h ffff_ffff, + parameter [31:0] PROGADDR_RESET = 32'h 0000_0000, + parameter [31:0] PROGADDR_IRQ = 32'h 0000_0010, + parameter [31:0] STACKADDR = 32'h ffff_ffff +) ( + input clk, resetn, + output reg trap, + + output reg mem_valid, + output reg mem_instr, + input mem_ready, + + output reg [31:0] mem_addr, + output reg [31:0] mem_wdata, + output reg [ 3:0] mem_wstrb, + input [31:0] mem_rdata, + + // Look-Ahead Interface + output mem_la_read, + output mem_la_write, + output [31:0] mem_la_addr, + output reg [31:0] mem_la_wdata, + output reg [ 3:0] mem_la_wstrb, + + // Pico Co-Processor Interface (PCPI) + output reg pcpi_valid, + output reg [31:0] pcpi_insn, + output [31:0] pcpi_rs1, + output [31:0] pcpi_rs2, + input pcpi_wr, + input [31:0] pcpi_rd, + input pcpi_wait, + input pcpi_ready, + + // IRQ Interface + input [31:0] irq, + output reg [31:0] eoi, + +`ifdef RISCV_FORMAL + output reg rvfi_valid, + output reg [63:0] rvfi_order, + output reg [31:0] rvfi_insn, + output reg rvfi_trap, + output reg rvfi_halt, + output reg rvfi_intr, + output reg [ 1:0] rvfi_mode, + output reg [ 1:0] rvfi_ixl, + output reg [ 4:0] rvfi_rs1_addr, + output reg [ 4:0] rvfi_rs2_addr, + output reg [31:0] rvfi_rs1_rdata, + output reg [31:0] rvfi_rs2_rdata, + output reg [ 4:0] rvfi_rd_addr, + output reg [31:0] rvfi_rd_wdata, + output reg [31:0] rvfi_pc_rdata, + output reg [31:0] rvfi_pc_wdata, + output reg [31:0] rvfi_mem_addr, + output reg [ 3:0] rvfi_mem_rmask, + output reg [ 3:0] rvfi_mem_wmask, + output reg [31:0] rvfi_mem_rdata, + output reg [31:0] rvfi_mem_wdata, + + output reg [63:0] rvfi_csr_mcycle_rmask, + output reg [63:0] rvfi_csr_mcycle_wmask, + output reg [63:0] rvfi_csr_mcycle_rdata, + output reg [63:0] rvfi_csr_mcycle_wdata, + + output reg [63:0] rvfi_csr_minstret_rmask, + output reg [63:0] rvfi_csr_minstret_wmask, + output reg [63:0] rvfi_csr_minstret_rdata, + output reg [63:0] rvfi_csr_minstret_wdata, +`endif + + // Trace Interface + output reg trace_valid, + output reg [35:0] trace_data +); + localparam integer irq_timer = 0; + localparam integer irq_ebreak = 1; + localparam integer irq_buserror = 2; + + localparam integer irqregs_offset = ENABLE_REGS_16_31 ? 32 : 16; + localparam integer regfile_size = (ENABLE_REGS_16_31 ? 32 : 16) + 4*ENABLE_IRQ*ENABLE_IRQ_QREGS; + localparam integer regindex_bits = (ENABLE_REGS_16_31 ? 5 : 4) + ENABLE_IRQ*ENABLE_IRQ_QREGS; + + localparam WITH_PCPI = ENABLE_PCPI || ENABLE_MUL || ENABLE_FAST_MUL || ENABLE_DIV; + + localparam [35:0] TRACE_BRANCH = {4'b 0001, 32'b 0}; + localparam [35:0] TRACE_ADDR = {4'b 0010, 32'b 0}; + localparam [35:0] TRACE_IRQ = {4'b 1000, 32'b 0}; + + reg [63:0] count_cycle, count_instr; + reg [31:0] reg_pc, reg_next_pc, reg_op1, reg_op2, reg_out; + reg [4:0] reg_sh; + + reg [31:0] next_insn_opcode; + reg [31:0] dbg_insn_opcode; + reg [31:0] dbg_insn_addr; + + wire dbg_mem_valid = mem_valid; + wire dbg_mem_instr = mem_instr; + wire dbg_mem_ready = mem_ready; + wire [31:0] dbg_mem_addr = mem_addr; + wire [31:0] dbg_mem_wdata = mem_wdata; + wire [ 3:0] dbg_mem_wstrb = mem_wstrb; + wire [31:0] dbg_mem_rdata = mem_rdata; + + assign pcpi_rs1 = reg_op1; + assign pcpi_rs2 = reg_op2; + + wire [31:0] next_pc; + + reg irq_delay; + reg irq_active; + reg [31:0] irq_mask; + reg [31:0] irq_pending; + reg [31:0] timer; + +`ifndef PICORV32_REGS + reg [31:0] cpuregs [0:regfile_size-1]; + + integer i; + initial begin + if (REGS_INIT_ZERO) begin + for (i = 0; i < regfile_size; i = i+1) + cpuregs[i] = 0; + end + end +`endif + + task empty_statement; + // This task is used by the `assert directive in non-formal mode to + // avoid empty statement (which are unsupported by plain Verilog syntax). + begin end + endtask + +`ifdef DEBUGREGS + wire [31:0] dbg_reg_x0 = 0; + wire [31:0] dbg_reg_x1 = cpuregs[1]; + wire [31:0] dbg_reg_x2 = cpuregs[2]; + wire [31:0] dbg_reg_x3 = cpuregs[3]; + wire [31:0] dbg_reg_x4 = cpuregs[4]; + wire [31:0] dbg_reg_x5 = cpuregs[5]; + wire [31:0] dbg_reg_x6 = cpuregs[6]; + wire [31:0] dbg_reg_x7 = cpuregs[7]; + wire [31:0] dbg_reg_x8 = cpuregs[8]; + wire [31:0] dbg_reg_x9 = cpuregs[9]; + wire [31:0] dbg_reg_x10 = cpuregs[10]; + wire [31:0] dbg_reg_x11 = cpuregs[11]; + wire [31:0] dbg_reg_x12 = cpuregs[12]; + wire [31:0] dbg_reg_x13 = cpuregs[13]; + wire [31:0] dbg_reg_x14 = cpuregs[14]; + wire [31:0] dbg_reg_x15 = cpuregs[15]; + wire [31:0] dbg_reg_x16 = cpuregs[16]; + wire [31:0] dbg_reg_x17 = cpuregs[17]; + wire [31:0] dbg_reg_x18 = cpuregs[18]; + wire [31:0] dbg_reg_x19 = cpuregs[19]; + wire [31:0] dbg_reg_x20 = cpuregs[20]; + wire [31:0] dbg_reg_x21 = cpuregs[21]; + wire [31:0] dbg_reg_x22 = cpuregs[22]; + wire [31:0] dbg_reg_x23 = cpuregs[23]; + wire [31:0] dbg_reg_x24 = cpuregs[24]; + wire [31:0] dbg_reg_x25 = cpuregs[25]; + wire [31:0] dbg_reg_x26 = cpuregs[26]; + wire [31:0] dbg_reg_x27 = cpuregs[27]; + wire [31:0] dbg_reg_x28 = cpuregs[28]; + wire [31:0] dbg_reg_x29 = cpuregs[29]; + wire [31:0] dbg_reg_x30 = cpuregs[30]; + wire [31:0] dbg_reg_x31 = cpuregs[31]; +`endif + + // Internal PCPI Cores + + wire pcpi_mul_wr; + wire [31:0] pcpi_mul_rd; + wire pcpi_mul_wait; + wire pcpi_mul_ready; + + wire pcpi_div_wr; + wire [31:0] pcpi_div_rd; + wire pcpi_div_wait; + wire pcpi_div_ready; + + reg pcpi_int_wr; + reg [31:0] pcpi_int_rd; + reg pcpi_int_wait; + reg pcpi_int_ready; + + generate if (ENABLE_FAST_MUL) begin + picorv32_pcpi_fast_mul pcpi_mul ( + .clk (clk ), + .resetn (resetn ), + .pcpi_valid(pcpi_valid ), + .pcpi_insn (pcpi_insn ), + .pcpi_rs1 (pcpi_rs1 ), + .pcpi_rs2 (pcpi_rs2 ), + .pcpi_wr (pcpi_mul_wr ), + .pcpi_rd (pcpi_mul_rd ), + .pcpi_wait (pcpi_mul_wait ), + .pcpi_ready(pcpi_mul_ready ) + ); + end else if (ENABLE_MUL) begin + picorv32_pcpi_mul pcpi_mul ( + .clk (clk ), + .resetn (resetn ), + .pcpi_valid(pcpi_valid ), + .pcpi_insn (pcpi_insn ), + .pcpi_rs1 (pcpi_rs1 ), + .pcpi_rs2 (pcpi_rs2 ), + .pcpi_wr (pcpi_mul_wr ), + .pcpi_rd (pcpi_mul_rd ), + .pcpi_wait (pcpi_mul_wait ), + .pcpi_ready(pcpi_mul_ready ) + ); + end else begin + assign pcpi_mul_wr = 0; + assign pcpi_mul_rd = 32'bx; + assign pcpi_mul_wait = 0; + assign pcpi_mul_ready = 0; + end endgenerate + + generate if (ENABLE_DIV) begin + picorv32_pcpi_div pcpi_div ( + .clk (clk ), + .resetn (resetn ), + .pcpi_valid(pcpi_valid ), + .pcpi_insn (pcpi_insn ), + .pcpi_rs1 (pcpi_rs1 ), + .pcpi_rs2 (pcpi_rs2 ), + .pcpi_wr (pcpi_div_wr ), + .pcpi_rd (pcpi_div_rd ), + .pcpi_wait (pcpi_div_wait ), + .pcpi_ready(pcpi_div_ready ) + ); + end else begin + assign pcpi_div_wr = 0; + assign pcpi_div_rd = 32'bx; + assign pcpi_div_wait = 0; + assign pcpi_div_ready = 0; + end endgenerate + + always @* begin + pcpi_int_wr = 0; + pcpi_int_rd = 32'bx; + pcpi_int_wait = |{ENABLE_PCPI && pcpi_wait, (ENABLE_MUL || ENABLE_FAST_MUL) && pcpi_mul_wait, ENABLE_DIV && pcpi_div_wait}; + pcpi_int_ready = |{ENABLE_PCPI && pcpi_ready, (ENABLE_MUL || ENABLE_FAST_MUL) && pcpi_mul_ready, ENABLE_DIV && pcpi_div_ready}; + + (* parallel_case *) + case (1'b1) + ENABLE_PCPI && pcpi_ready: begin + pcpi_int_wr = ENABLE_PCPI ? pcpi_wr : 0; + pcpi_int_rd = ENABLE_PCPI ? pcpi_rd : 0; + end + (ENABLE_MUL || ENABLE_FAST_MUL) && pcpi_mul_ready: begin + pcpi_int_wr = pcpi_mul_wr; + pcpi_int_rd = pcpi_mul_rd; + end + ENABLE_DIV && pcpi_div_ready: begin + pcpi_int_wr = pcpi_div_wr; + pcpi_int_rd = pcpi_div_rd; + end + endcase + end + + + // Memory Interface + + reg [1:0] mem_state; + reg [1:0] mem_wordsize; + reg [31:0] mem_rdata_word; + reg [31:0] mem_rdata_q; + reg mem_do_prefetch; + reg mem_do_rinst; + reg mem_do_rdata; + reg mem_do_wdata; + + wire mem_xfer; + reg mem_la_secondword, mem_la_firstword_reg, last_mem_valid; + wire mem_la_firstword = COMPRESSED_ISA && (mem_do_prefetch || mem_do_rinst) && next_pc[1] && !mem_la_secondword; + wire mem_la_firstword_xfer = COMPRESSED_ISA && mem_xfer && (!last_mem_valid ? mem_la_firstword : mem_la_firstword_reg); + + reg prefetched_high_word; + reg clear_prefetched_high_word; + reg [15:0] mem_16bit_buffer; + + wire [31:0] mem_rdata_latched_noshuffle; + wire [31:0] mem_rdata_latched; + + wire mem_la_use_prefetched_high_word = COMPRESSED_ISA && mem_la_firstword && prefetched_high_word && !clear_prefetched_high_word; + assign mem_xfer = (mem_valid && mem_ready) || (mem_la_use_prefetched_high_word && mem_do_rinst); + + wire mem_busy = |{mem_do_prefetch, mem_do_rinst, mem_do_rdata, mem_do_wdata}; + wire mem_done = resetn && ((mem_xfer && |mem_state && (mem_do_rinst || mem_do_rdata || mem_do_wdata)) || (&mem_state && mem_do_rinst)) && + (!mem_la_firstword || (~&mem_rdata_latched[1:0] && mem_xfer)); + + assign mem_la_write = resetn && !mem_state && mem_do_wdata; + assign mem_la_read = resetn && ((!mem_la_use_prefetched_high_word && !mem_state && (mem_do_rinst || mem_do_prefetch || mem_do_rdata)) || + (COMPRESSED_ISA && mem_xfer && (!last_mem_valid ? mem_la_firstword : mem_la_firstword_reg) && !mem_la_secondword && &mem_rdata_latched[1:0])); + assign mem_la_addr = (mem_do_prefetch || mem_do_rinst) ? {next_pc[31:2] + mem_la_firstword_xfer, 2'b00} : {reg_op1[31:2], 2'b00}; + + assign mem_rdata_latched_noshuffle = (mem_xfer || LATCHED_MEM_RDATA) ? mem_rdata : mem_rdata_q; + + assign mem_rdata_latched = COMPRESSED_ISA && mem_la_use_prefetched_high_word ? {16'bx, mem_16bit_buffer} : + COMPRESSED_ISA && mem_la_secondword ? {mem_rdata_latched_noshuffle[15:0], mem_16bit_buffer} : + COMPRESSED_ISA && mem_la_firstword ? {16'bx, mem_rdata_latched_noshuffle[31:16]} : mem_rdata_latched_noshuffle; + + always @(posedge clk) begin + if (!resetn) begin + mem_la_firstword_reg <= 0; + last_mem_valid <= 0; + end else begin + if (!last_mem_valid) + mem_la_firstword_reg <= mem_la_firstword; + last_mem_valid <= mem_valid && !mem_ready; + end + end + + always @* begin + (* full_case *) + case (mem_wordsize) + 0: begin + mem_la_wdata = reg_op2; + mem_la_wstrb = 4'b1111; + mem_rdata_word = mem_rdata; + end + 1: begin + mem_la_wdata = {2{reg_op2[15:0]}}; + mem_la_wstrb = reg_op1[1] ? 4'b1100 : 4'b0011; + case (reg_op1[1]) + 1'b0: mem_rdata_word = {16'b0, mem_rdata[15: 0]}; + 1'b1: mem_rdata_word = {16'b0, mem_rdata[31:16]}; + endcase + end + 2: begin + mem_la_wdata = {4{reg_op2[7:0]}}; + mem_la_wstrb = 4'b0001 << reg_op1[1:0]; + case (reg_op1[1:0]) + 2'b00: mem_rdata_word = {24'b0, mem_rdata[ 7: 0]}; + 2'b01: mem_rdata_word = {24'b0, mem_rdata[15: 8]}; + 2'b10: mem_rdata_word = {24'b0, mem_rdata[23:16]}; + 2'b11: mem_rdata_word = {24'b0, mem_rdata[31:24]}; + endcase + end + endcase + end + + always @(posedge clk) begin + if (mem_xfer) begin + mem_rdata_q <= COMPRESSED_ISA ? mem_rdata_latched : mem_rdata; + next_insn_opcode <= COMPRESSED_ISA ? mem_rdata_latched : mem_rdata; + end + + if (COMPRESSED_ISA && mem_done && (mem_do_prefetch || mem_do_rinst)) begin + case (mem_rdata_latched[1:0]) + 2'b00: begin // Quadrant 0 + case (mem_rdata_latched[15:13]) + 3'b000: begin // C.ADDI4SPN + mem_rdata_q[14:12] <= 3'b000; + mem_rdata_q[31:20] <= {2'b0, mem_rdata_latched[10:7], mem_rdata_latched[12:11], mem_rdata_latched[5], mem_rdata_latched[6], 2'b00}; + end + 3'b010: begin // C.LW + mem_rdata_q[31:20] <= {5'b0, mem_rdata_latched[5], mem_rdata_latched[12:10], mem_rdata_latched[6], 2'b00}; + mem_rdata_q[14:12] <= 3'b 010; + end + 3'b 110: begin // C.SW + {mem_rdata_q[31:25], mem_rdata_q[11:7]} <= {5'b0, mem_rdata_latched[5], mem_rdata_latched[12:10], mem_rdata_latched[6], 2'b00}; + mem_rdata_q[14:12] <= 3'b 010; + end + endcase + end + 2'b01: begin // Quadrant 1 + case (mem_rdata_latched[15:13]) + 3'b 000: begin // C.ADDI + mem_rdata_q[14:12] <= 3'b000; + mem_rdata_q[31:20] <= $signed({mem_rdata_latched[12], mem_rdata_latched[6:2]}); + end + 3'b 010: begin // C.LI + mem_rdata_q[14:12] <= 3'b000; + mem_rdata_q[31:20] <= $signed({mem_rdata_latched[12], mem_rdata_latched[6:2]}); + end + 3'b 011: begin + if (mem_rdata_latched[11:7] == 2) begin // C.ADDI16SP + mem_rdata_q[14:12] <= 3'b000; + mem_rdata_q[31:20] <= $signed({mem_rdata_latched[12], mem_rdata_latched[4:3], + mem_rdata_latched[5], mem_rdata_latched[2], mem_rdata_latched[6], 4'b 0000}); + end else begin // C.LUI + mem_rdata_q[31:12] <= $signed({mem_rdata_latched[12], mem_rdata_latched[6:2]}); + end + end + 3'b100: begin + if (mem_rdata_latched[11:10] == 2'b00) begin // C.SRLI + mem_rdata_q[31:25] <= 7'b0000000; + mem_rdata_q[14:12] <= 3'b 101; + end + if (mem_rdata_latched[11:10] == 2'b01) begin // C.SRAI + mem_rdata_q[31:25] <= 7'b0100000; + mem_rdata_q[14:12] <= 3'b 101; + end + if (mem_rdata_latched[11:10] == 2'b10) begin // C.ANDI + mem_rdata_q[14:12] <= 3'b111; + mem_rdata_q[31:20] <= $signed({mem_rdata_latched[12], mem_rdata_latched[6:2]}); + end + if (mem_rdata_latched[12:10] == 3'b011) begin // C.SUB, C.XOR, C.OR, C.AND + if (mem_rdata_latched[6:5] == 2'b00) mem_rdata_q[14:12] <= 3'b000; + if (mem_rdata_latched[6:5] == 2'b01) mem_rdata_q[14:12] <= 3'b100; + if (mem_rdata_latched[6:5] == 2'b10) mem_rdata_q[14:12] <= 3'b110; + if (mem_rdata_latched[6:5] == 2'b11) mem_rdata_q[14:12] <= 3'b111; + mem_rdata_q[31:25] <= mem_rdata_latched[6:5] == 2'b00 ? 7'b0100000 : 7'b0000000; + end + end + 3'b 110: begin // C.BEQZ + mem_rdata_q[14:12] <= 3'b000; + { mem_rdata_q[31], mem_rdata_q[7], mem_rdata_q[30:25], mem_rdata_q[11:8] } <= + $signed({mem_rdata_latched[12], mem_rdata_latched[6:5], mem_rdata_latched[2], + mem_rdata_latched[11:10], mem_rdata_latched[4:3]}); + end + 3'b 111: begin // C.BNEZ + mem_rdata_q[14:12] <= 3'b001; + { mem_rdata_q[31], mem_rdata_q[7], mem_rdata_q[30:25], mem_rdata_q[11:8] } <= + $signed({mem_rdata_latched[12], mem_rdata_latched[6:5], mem_rdata_latched[2], + mem_rdata_latched[11:10], mem_rdata_latched[4:3]}); + end + endcase + end + 2'b10: begin // Quadrant 2 + case (mem_rdata_latched[15:13]) + 3'b000: begin // C.SLLI + mem_rdata_q[31:25] <= 7'b0000000; + mem_rdata_q[14:12] <= 3'b 001; + end + 3'b010: begin // C.LWSP + mem_rdata_q[31:20] <= {4'b0, mem_rdata_latched[3:2], mem_rdata_latched[12], mem_rdata_latched[6:4], 2'b00}; + mem_rdata_q[14:12] <= 3'b 010; + end + 3'b100: begin + if (mem_rdata_latched[12] == 0 && mem_rdata_latched[6:2] == 0) begin // C.JR + mem_rdata_q[14:12] <= 3'b000; + mem_rdata_q[31:20] <= 12'b0; + end + if (mem_rdata_latched[12] == 0 && mem_rdata_latched[6:2] != 0) begin // C.MV + mem_rdata_q[14:12] <= 3'b000; + mem_rdata_q[31:25] <= 7'b0000000; + end + if (mem_rdata_latched[12] != 0 && mem_rdata_latched[11:7] != 0 && mem_rdata_latched[6:2] == 0) begin // C.JALR + mem_rdata_q[14:12] <= 3'b000; + mem_rdata_q[31:20] <= 12'b0; + end + if (mem_rdata_latched[12] != 0 && mem_rdata_latched[6:2] != 0) begin // C.ADD + mem_rdata_q[14:12] <= 3'b000; + mem_rdata_q[31:25] <= 7'b0000000; + end + end + 3'b110: begin // C.SWSP + {mem_rdata_q[31:25], mem_rdata_q[11:7]} <= {4'b0, mem_rdata_latched[8:7], mem_rdata_latched[12:9], 2'b00}; + mem_rdata_q[14:12] <= 3'b 010; + end + endcase + end + endcase + end + end + + always @(posedge clk) begin + if (resetn && !trap) begin + if (mem_do_prefetch || mem_do_rinst || mem_do_rdata) + `assert(!mem_do_wdata); + + if (mem_do_prefetch || mem_do_rinst) + `assert(!mem_do_rdata); + + if (mem_do_rdata) + `assert(!mem_do_prefetch && !mem_do_rinst); + + if (mem_do_wdata) + `assert(!(mem_do_prefetch || mem_do_rinst || mem_do_rdata)); + + if (mem_state == 2 || mem_state == 3) + `assert(mem_valid || mem_do_prefetch); + end + end + + always @(posedge clk) begin + if (!resetn || trap) begin + if (!resetn) + mem_state <= 0; + if (!resetn || mem_ready) + mem_valid <= 0; + mem_la_secondword <= 0; + prefetched_high_word <= 0; + end else begin + if (mem_la_read || mem_la_write) begin + mem_addr <= mem_la_addr; + mem_wstrb <= mem_la_wstrb & {4{mem_la_write}}; + end + if (mem_la_write) begin + mem_wdata <= mem_la_wdata; + end + case (mem_state) + 0: begin + if (mem_do_prefetch || mem_do_rinst || mem_do_rdata) begin + mem_valid <= !mem_la_use_prefetched_high_word; + mem_instr <= mem_do_prefetch || mem_do_rinst; + mem_wstrb <= 0; + mem_state <= 1; + end + if (mem_do_wdata) begin + mem_valid <= 1; + mem_instr <= 0; + mem_state <= 2; + end + end + 1: begin + `assert(mem_wstrb == 0); + `assert(mem_do_prefetch || mem_do_rinst || mem_do_rdata); + `assert(mem_valid == !mem_la_use_prefetched_high_word); + `assert(mem_instr == (mem_do_prefetch || mem_do_rinst)); + if (mem_xfer) begin + if (COMPRESSED_ISA && mem_la_read) begin + mem_valid <= 1; + mem_la_secondword <= 1; + if (!mem_la_use_prefetched_high_word) + mem_16bit_buffer <= mem_rdata[31:16]; + end else begin + mem_valid <= 0; + mem_la_secondword <= 0; + if (COMPRESSED_ISA && !mem_do_rdata) begin + if (~&mem_rdata[1:0] || mem_la_secondword) begin + mem_16bit_buffer <= mem_rdata[31:16]; + prefetched_high_word <= 1; + end else begin + prefetched_high_word <= 0; + end + end + mem_state <= mem_do_rinst || mem_do_rdata ? 0 : 3; + end + end + end + 2: begin + `assert(mem_wstrb != 0); + `assert(mem_do_wdata); + if (mem_xfer) begin + mem_valid <= 0; + mem_state <= 0; + end + end + 3: begin + `assert(mem_wstrb == 0); + `assert(mem_do_prefetch); + if (mem_do_rinst) begin + mem_state <= 0; + end + end + endcase + end + + if (clear_prefetched_high_word) + prefetched_high_word <= 0; + end + + + // Instruction Decoder + + reg instr_lui, instr_auipc, instr_jal, instr_jalr; + reg instr_beq, instr_bne, instr_blt, instr_bge, instr_bltu, instr_bgeu; + reg instr_lb, instr_lh, instr_lw, instr_lbu, instr_lhu, instr_sb, instr_sh, instr_sw; + reg instr_addi, instr_slti, instr_sltiu, instr_xori, instr_ori, instr_andi, instr_slli, instr_srli, instr_srai; + reg instr_add, instr_sub, instr_sll, instr_slt, instr_sltu, instr_xor, instr_srl, instr_sra, instr_or, instr_and; + reg instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh, instr_ecall_ebreak; + reg instr_getq, instr_setq, instr_retirq, instr_maskirq, instr_waitirq, instr_timer; + wire instr_trap; + + reg [regindex_bits-1:0] decoded_rd, decoded_rs1, decoded_rs2; + reg [31:0] decoded_imm, decoded_imm_j; + reg decoder_trigger; + reg decoder_trigger_q; + reg decoder_pseudo_trigger; + reg decoder_pseudo_trigger_q; + reg compressed_instr; + + reg is_lui_auipc_jal; + reg is_lb_lh_lw_lbu_lhu; + reg is_slli_srli_srai; + reg is_jalr_addi_slti_sltiu_xori_ori_andi; + reg is_sb_sh_sw; + reg is_sll_srl_sra; + reg is_lui_auipc_jal_jalr_addi_add_sub; + reg is_slti_blt_slt; + reg is_sltiu_bltu_sltu; + reg is_beq_bne_blt_bge_bltu_bgeu; + reg is_lbu_lhu_lw; + reg is_alu_reg_imm; + reg is_alu_reg_reg; + reg is_compare; + + assign instr_trap = (CATCH_ILLINSN || WITH_PCPI) && !{instr_lui, instr_auipc, instr_jal, instr_jalr, + instr_beq, instr_bne, instr_blt, instr_bge, instr_bltu, instr_bgeu, + instr_lb, instr_lh, instr_lw, instr_lbu, instr_lhu, instr_sb, instr_sh, instr_sw, + instr_addi, instr_slti, instr_sltiu, instr_xori, instr_ori, instr_andi, instr_slli, instr_srli, instr_srai, + instr_add, instr_sub, instr_sll, instr_slt, instr_sltu, instr_xor, instr_srl, instr_sra, instr_or, instr_and, + instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh, + instr_getq, instr_setq, instr_retirq, instr_maskirq, instr_waitirq, instr_timer}; + + wire is_rdcycle_rdcycleh_rdinstr_rdinstrh; + assign is_rdcycle_rdcycleh_rdinstr_rdinstrh = |{instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh}; + + reg [63:0] new_ascii_instr; + `FORMAL_KEEP reg [63:0] dbg_ascii_instr; + `FORMAL_KEEP reg [31:0] dbg_insn_imm; + `FORMAL_KEEP reg [4:0] dbg_insn_rs1; + `FORMAL_KEEP reg [4:0] dbg_insn_rs2; + `FORMAL_KEEP reg [4:0] dbg_insn_rd; + `FORMAL_KEEP reg [31:0] dbg_rs1val; + `FORMAL_KEEP reg [31:0] dbg_rs2val; + `FORMAL_KEEP reg dbg_rs1val_valid; + `FORMAL_KEEP reg dbg_rs2val_valid; + + always @* begin + new_ascii_instr = ""; + + if (instr_lui) new_ascii_instr = "lui"; + if (instr_auipc) new_ascii_instr = "auipc"; + if (instr_jal) new_ascii_instr = "jal"; + if (instr_jalr) new_ascii_instr = "jalr"; + + if (instr_beq) new_ascii_instr = "beq"; + if (instr_bne) new_ascii_instr = "bne"; + if (instr_blt) new_ascii_instr = "blt"; + if (instr_bge) new_ascii_instr = "bge"; + if (instr_bltu) new_ascii_instr = "bltu"; + if (instr_bgeu) new_ascii_instr = "bgeu"; + + if (instr_lb) new_ascii_instr = "lb"; + if (instr_lh) new_ascii_instr = "lh"; + if (instr_lw) new_ascii_instr = "lw"; + if (instr_lbu) new_ascii_instr = "lbu"; + if (instr_lhu) new_ascii_instr = "lhu"; + if (instr_sb) new_ascii_instr = "sb"; + if (instr_sh) new_ascii_instr = "sh"; + if (instr_sw) new_ascii_instr = "sw"; + + if (instr_addi) new_ascii_instr = "addi"; + if (instr_slti) new_ascii_instr = "slti"; + if (instr_sltiu) new_ascii_instr = "sltiu"; + if (instr_xori) new_ascii_instr = "xori"; + if (instr_ori) new_ascii_instr = "ori"; + if (instr_andi) new_ascii_instr = "andi"; + if (instr_slli) new_ascii_instr = "slli"; + if (instr_srli) new_ascii_instr = "srli"; + if (instr_srai) new_ascii_instr = "srai"; + + if (instr_add) new_ascii_instr = "add"; + if (instr_sub) new_ascii_instr = "sub"; + if (instr_sll) new_ascii_instr = "sll"; + if (instr_slt) new_ascii_instr = "slt"; + if (instr_sltu) new_ascii_instr = "sltu"; + if (instr_xor) new_ascii_instr = "xor"; + if (instr_srl) new_ascii_instr = "srl"; + if (instr_sra) new_ascii_instr = "sra"; + if (instr_or) new_ascii_instr = "or"; + if (instr_and) new_ascii_instr = "and"; + + if (instr_rdcycle) new_ascii_instr = "rdcycle"; + if (instr_rdcycleh) new_ascii_instr = "rdcycleh"; + if (instr_rdinstr) new_ascii_instr = "rdinstr"; + if (instr_rdinstrh) new_ascii_instr = "rdinstrh"; + + if (instr_getq) new_ascii_instr = "getq"; + if (instr_setq) new_ascii_instr = "setq"; + if (instr_retirq) new_ascii_instr = "retirq"; + if (instr_maskirq) new_ascii_instr = "maskirq"; + if (instr_waitirq) new_ascii_instr = "waitirq"; + if (instr_timer) new_ascii_instr = "timer"; + end + + reg [63:0] q_ascii_instr; + reg [31:0] q_insn_imm; + reg [31:0] q_insn_opcode; + reg [4:0] q_insn_rs1; + reg [4:0] q_insn_rs2; + reg [4:0] q_insn_rd; + reg dbg_next; + + wire launch_next_insn; + reg dbg_valid_insn; + + reg [63:0] cached_ascii_instr; + reg [31:0] cached_insn_imm; + reg [31:0] cached_insn_opcode; + reg [4:0] cached_insn_rs1; + reg [4:0] cached_insn_rs2; + reg [4:0] cached_insn_rd; + + always @(posedge clk) begin + q_ascii_instr <= dbg_ascii_instr; + q_insn_imm <= dbg_insn_imm; + q_insn_opcode <= dbg_insn_opcode; + q_insn_rs1 <= dbg_insn_rs1; + q_insn_rs2 <= dbg_insn_rs2; + q_insn_rd <= dbg_insn_rd; + dbg_next <= launch_next_insn; + + if (!resetn || trap) + dbg_valid_insn <= 0; + else if (launch_next_insn) + dbg_valid_insn <= 1; + + if (decoder_trigger_q) begin + cached_ascii_instr <= new_ascii_instr; + cached_insn_imm <= decoded_imm; + if (&next_insn_opcode[1:0]) + cached_insn_opcode <= next_insn_opcode; + else + cached_insn_opcode <= {16'b0, next_insn_opcode[15:0]}; + cached_insn_rs1 <= decoded_rs1; + cached_insn_rs2 <= decoded_rs2; + cached_insn_rd <= decoded_rd; + end + + if (launch_next_insn) begin + dbg_insn_addr <= next_pc; + end + end + + always @* begin + dbg_ascii_instr = q_ascii_instr; + dbg_insn_imm = q_insn_imm; + dbg_insn_opcode = q_insn_opcode; + dbg_insn_rs1 = q_insn_rs1; + dbg_insn_rs2 = q_insn_rs2; + dbg_insn_rd = q_insn_rd; + + if (dbg_next) begin + if (decoder_pseudo_trigger_q) begin + dbg_ascii_instr = cached_ascii_instr; + dbg_insn_imm = cached_insn_imm; + dbg_insn_opcode = cached_insn_opcode; + dbg_insn_rs1 = cached_insn_rs1; + dbg_insn_rs2 = cached_insn_rs2; + dbg_insn_rd = cached_insn_rd; + end else begin + dbg_ascii_instr = new_ascii_instr; + if (&next_insn_opcode[1:0]) + dbg_insn_opcode = next_insn_opcode; + else + dbg_insn_opcode = {16'b0, next_insn_opcode[15:0]}; + dbg_insn_imm = decoded_imm; + dbg_insn_rs1 = decoded_rs1; + dbg_insn_rs2 = decoded_rs2; + dbg_insn_rd = decoded_rd; + end + end + end + +`ifdef DEBUGASM + always @(posedge clk) begin + if (dbg_next) begin + $display("debugasm %x %x %s", dbg_insn_addr, dbg_insn_opcode, dbg_ascii_instr ? dbg_ascii_instr : "*"); + end + end +`endif + +`ifdef DEBUG + always @(posedge clk) begin + if (dbg_next) begin + if (&dbg_insn_opcode[1:0]) + $display("DECODE: 0x%08x 0x%08x %-0s", dbg_insn_addr, dbg_insn_opcode, dbg_ascii_instr ? dbg_ascii_instr : "UNKNOWN"); + else + $display("DECODE: 0x%08x 0x%04x %-0s", dbg_insn_addr, dbg_insn_opcode[15:0], dbg_ascii_instr ? dbg_ascii_instr : "UNKNOWN"); + end + end +`endif + + always @(posedge clk) begin + is_lui_auipc_jal <= |{instr_lui, instr_auipc, instr_jal}; + is_lui_auipc_jal_jalr_addi_add_sub <= |{instr_lui, instr_auipc, instr_jal, instr_jalr, instr_addi, instr_add, instr_sub}; + is_slti_blt_slt <= |{instr_slti, instr_blt, instr_slt}; + is_sltiu_bltu_sltu <= |{instr_sltiu, instr_bltu, instr_sltu}; + is_lbu_lhu_lw <= |{instr_lbu, instr_lhu, instr_lw}; + is_compare <= |{is_beq_bne_blt_bge_bltu_bgeu, instr_slti, instr_slt, instr_sltiu, instr_sltu}; + + if (mem_do_rinst && mem_done) begin + instr_lui <= mem_rdata_latched[6:0] == 7'b0110111; + instr_auipc <= mem_rdata_latched[6:0] == 7'b0010111; + instr_jal <= mem_rdata_latched[6:0] == 7'b1101111; + instr_jalr <= mem_rdata_latched[6:0] == 7'b1100111 && mem_rdata_latched[14:12] == 3'b000; + instr_retirq <= mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000010 && ENABLE_IRQ; + instr_waitirq <= mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000100 && ENABLE_IRQ; + + is_beq_bne_blt_bge_bltu_bgeu <= mem_rdata_latched[6:0] == 7'b1100011; + is_lb_lh_lw_lbu_lhu <= mem_rdata_latched[6:0] == 7'b0000011; + is_sb_sh_sw <= mem_rdata_latched[6:0] == 7'b0100011; + is_alu_reg_imm <= mem_rdata_latched[6:0] == 7'b0010011; + is_alu_reg_reg <= mem_rdata_latched[6:0] == 7'b0110011; + + { decoded_imm_j[31:20], decoded_imm_j[10:1], decoded_imm_j[11], decoded_imm_j[19:12], decoded_imm_j[0] } <= $signed({mem_rdata_latched[31:12], 1'b0}); + + decoded_rd <= mem_rdata_latched[11:7]; + decoded_rs1 <= mem_rdata_latched[19:15]; + decoded_rs2 <= mem_rdata_latched[24:20]; + + if (mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000000 && ENABLE_IRQ && ENABLE_IRQ_QREGS) + decoded_rs1[regindex_bits-1] <= 1; // instr_getq + + if (mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000010 && ENABLE_IRQ) + decoded_rs1 <= ENABLE_IRQ_QREGS ? irqregs_offset : 3; // instr_retirq + + compressed_instr <= 0; + if (COMPRESSED_ISA && mem_rdata_latched[1:0] != 2'b11) begin + compressed_instr <= 1; + decoded_rd <= 0; + decoded_rs1 <= 0; + decoded_rs2 <= 0; + + { decoded_imm_j[31:11], decoded_imm_j[4], decoded_imm_j[9:8], decoded_imm_j[10], decoded_imm_j[6], + decoded_imm_j[7], decoded_imm_j[3:1], decoded_imm_j[5], decoded_imm_j[0] } <= $signed({mem_rdata_latched[12:2], 1'b0}); + + case (mem_rdata_latched[1:0]) + 2'b00: begin // Quadrant 0 + case (mem_rdata_latched[15:13]) + 3'b000: begin // C.ADDI4SPN + is_alu_reg_imm <= |mem_rdata_latched[12:5]; + decoded_rs1 <= 2; + decoded_rd <= 8 + mem_rdata_latched[4:2]; + end + 3'b010: begin // C.LW + is_lb_lh_lw_lbu_lhu <= 1; + decoded_rs1 <= 8 + mem_rdata_latched[9:7]; + decoded_rd <= 8 + mem_rdata_latched[4:2]; + end + 3'b110: begin // C.SW + is_sb_sh_sw <= 1; + decoded_rs1 <= 8 + mem_rdata_latched[9:7]; + decoded_rs2 <= 8 + mem_rdata_latched[4:2]; + end + endcase + end + 2'b01: begin // Quadrant 1 + case (mem_rdata_latched[15:13]) + 3'b000: begin // C.NOP / C.ADDI + is_alu_reg_imm <= 1; + decoded_rd <= mem_rdata_latched[11:7]; + decoded_rs1 <= mem_rdata_latched[11:7]; + end + 3'b001: begin // C.JAL + instr_jal <= 1; + decoded_rd <= 1; + end + 3'b 010: begin // C.LI + is_alu_reg_imm <= 1; + decoded_rd <= mem_rdata_latched[11:7]; + decoded_rs1 <= 0; + end + 3'b 011: begin + if (mem_rdata_latched[12] || mem_rdata_latched[6:2]) begin + if (mem_rdata_latched[11:7] == 2) begin // C.ADDI16SP + is_alu_reg_imm <= 1; + decoded_rd <= mem_rdata_latched[11:7]; + decoded_rs1 <= mem_rdata_latched[11:7]; + end else begin // C.LUI + instr_lui <= 1; + decoded_rd <= mem_rdata_latched[11:7]; + decoded_rs1 <= 0; + end + end + end + 3'b100: begin + if (!mem_rdata_latched[11] && !mem_rdata_latched[12]) begin // C.SRLI, C.SRAI + is_alu_reg_imm <= 1; + decoded_rd <= 8 + mem_rdata_latched[9:7]; + decoded_rs1 <= 8 + mem_rdata_latched[9:7]; + decoded_rs2 <= {mem_rdata_latched[12], mem_rdata_latched[6:2]}; + end + if (mem_rdata_latched[11:10] == 2'b10) begin // C.ANDI + is_alu_reg_imm <= 1; + decoded_rd <= 8 + mem_rdata_latched[9:7]; + decoded_rs1 <= 8 + mem_rdata_latched[9:7]; + end + if (mem_rdata_latched[12:10] == 3'b011) begin // C.SUB, C.XOR, C.OR, C.AND + is_alu_reg_reg <= 1; + decoded_rd <= 8 + mem_rdata_latched[9:7]; + decoded_rs1 <= 8 + mem_rdata_latched[9:7]; + decoded_rs2 <= 8 + mem_rdata_latched[4:2]; + end + end + 3'b101: begin // C.J + instr_jal <= 1; + end + 3'b110: begin // C.BEQZ + is_beq_bne_blt_bge_bltu_bgeu <= 1; + decoded_rs1 <= 8 + mem_rdata_latched[9:7]; + decoded_rs2 <= 0; + end + 3'b111: begin // C.BNEZ + is_beq_bne_blt_bge_bltu_bgeu <= 1; + decoded_rs1 <= 8 + mem_rdata_latched[9:7]; + decoded_rs2 <= 0; + end + endcase + end + 2'b10: begin // Quadrant 2 + case (mem_rdata_latched[15:13]) + 3'b000: begin // C.SLLI + if (!mem_rdata_latched[12]) begin + is_alu_reg_imm <= 1; + decoded_rd <= mem_rdata_latched[11:7]; + decoded_rs1 <= mem_rdata_latched[11:7]; + decoded_rs2 <= {mem_rdata_latched[12], mem_rdata_latched[6:2]}; + end + end + 3'b010: begin // C.LWSP + if (mem_rdata_latched[11:7]) begin + is_lb_lh_lw_lbu_lhu <= 1; + decoded_rd <= mem_rdata_latched[11:7]; + decoded_rs1 <= 2; + end + end + 3'b100: begin + if (mem_rdata_latched[12] == 0 && mem_rdata_latched[11:7] != 0 && mem_rdata_latched[6:2] == 0) begin // C.JR + instr_jalr <= 1; + decoded_rd <= 0; + decoded_rs1 <= mem_rdata_latched[11:7]; + end + if (mem_rdata_latched[12] == 0 && mem_rdata_latched[6:2] != 0) begin // C.MV + is_alu_reg_reg <= 1; + decoded_rd <= mem_rdata_latched[11:7]; + decoded_rs1 <= 0; + decoded_rs2 <= mem_rdata_latched[6:2]; + end + if (mem_rdata_latched[12] != 0 && mem_rdata_latched[11:7] != 0 && mem_rdata_latched[6:2] == 0) begin // C.JALR + instr_jalr <= 1; + decoded_rd <= 1; + decoded_rs1 <= mem_rdata_latched[11:7]; + end + if (mem_rdata_latched[12] != 0 && mem_rdata_latched[6:2] != 0) begin // C.ADD + is_alu_reg_reg <= 1; + decoded_rd <= mem_rdata_latched[11:7]; + decoded_rs1 <= mem_rdata_latched[11:7]; + decoded_rs2 <= mem_rdata_latched[6:2]; + end + end + 3'b110: begin // C.SWSP + is_sb_sh_sw <= 1; + decoded_rs1 <= 2; + decoded_rs2 <= mem_rdata_latched[6:2]; + end + endcase + end + endcase + end + end + + if (decoder_trigger && !decoder_pseudo_trigger) begin + pcpi_insn <= WITH_PCPI ? mem_rdata_q : 'bx; + + instr_beq <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b000; + instr_bne <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b001; + instr_blt <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b100; + instr_bge <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b101; + instr_bltu <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b110; + instr_bgeu <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b111; + + instr_lb <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b000; + instr_lh <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b001; + instr_lw <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b010; + instr_lbu <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b100; + instr_lhu <= is_lb_lh_lw_lbu_lhu && mem_rdata_q[14:12] == 3'b101; + + instr_sb <= is_sb_sh_sw && mem_rdata_q[14:12] == 3'b000; + instr_sh <= is_sb_sh_sw && mem_rdata_q[14:12] == 3'b001; + instr_sw <= is_sb_sh_sw && mem_rdata_q[14:12] == 3'b010; + + instr_addi <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b000; + instr_slti <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b010; + instr_sltiu <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b011; + instr_xori <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b100; + instr_ori <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b110; + instr_andi <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b111; + + instr_slli <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000; + instr_srli <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000; + instr_srai <= is_alu_reg_imm && mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000; + + instr_add <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b000 && mem_rdata_q[31:25] == 7'b0000000; + instr_sub <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b000 && mem_rdata_q[31:25] == 7'b0100000; + instr_sll <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000; + instr_slt <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b010 && mem_rdata_q[31:25] == 7'b0000000; + instr_sltu <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b011 && mem_rdata_q[31:25] == 7'b0000000; + instr_xor <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b100 && mem_rdata_q[31:25] == 7'b0000000; + instr_srl <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000; + instr_sra <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000; + instr_or <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b110 && mem_rdata_q[31:25] == 7'b0000000; + instr_and <= is_alu_reg_reg && mem_rdata_q[14:12] == 3'b111 && mem_rdata_q[31:25] == 7'b0000000; + + instr_rdcycle <= ((mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11000000000000000010) || + (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11000000000100000010)) && ENABLE_COUNTERS; + instr_rdcycleh <= ((mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11001000000000000010) || + (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11001000000100000010)) && ENABLE_COUNTERS && ENABLE_COUNTERS64; + instr_rdinstr <= (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11000000001000000010) && ENABLE_COUNTERS; + instr_rdinstrh <= (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11001000001000000010) && ENABLE_COUNTERS && ENABLE_COUNTERS64; + + instr_ecall_ebreak <= ((mem_rdata_q[6:0] == 7'b1110011 && !mem_rdata_q[31:21] && !mem_rdata_q[19:7]) || + (COMPRESSED_ISA && mem_rdata_q[15:0] == 16'h9002)); + + instr_getq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000000 && ENABLE_IRQ && ENABLE_IRQ_QREGS; + instr_setq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000001 && ENABLE_IRQ && ENABLE_IRQ_QREGS; + instr_maskirq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000011 && ENABLE_IRQ; + instr_timer <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000101 && ENABLE_IRQ && ENABLE_IRQ_TIMER; + + is_slli_srli_srai <= is_alu_reg_imm && |{ + mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000, + mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000, + mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000 + }; + + is_jalr_addi_slti_sltiu_xori_ori_andi <= instr_jalr || is_alu_reg_imm && |{ + mem_rdata_q[14:12] == 3'b000, + mem_rdata_q[14:12] == 3'b010, + mem_rdata_q[14:12] == 3'b011, + mem_rdata_q[14:12] == 3'b100, + mem_rdata_q[14:12] == 3'b110, + mem_rdata_q[14:12] == 3'b111 + }; + + is_sll_srl_sra <= is_alu_reg_reg && |{ + mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000, + mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000, + mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0100000 + }; + + is_lui_auipc_jal_jalr_addi_add_sub <= 0; + is_compare <= 0; + + (* parallel_case *) + case (1'b1) + instr_jal: + decoded_imm <= decoded_imm_j; + |{instr_lui, instr_auipc}: + decoded_imm <= mem_rdata_q[31:12] << 12; + |{instr_jalr, is_lb_lh_lw_lbu_lhu, is_alu_reg_imm}: + decoded_imm <= $signed(mem_rdata_q[31:20]); + is_beq_bne_blt_bge_bltu_bgeu: + decoded_imm <= $signed({mem_rdata_q[31], mem_rdata_q[7], mem_rdata_q[30:25], mem_rdata_q[11:8], 1'b0}); + is_sb_sh_sw: + decoded_imm <= $signed({mem_rdata_q[31:25], mem_rdata_q[11:7]}); + default: + decoded_imm <= 1'bx; + endcase + end + + if (!resetn) begin + is_beq_bne_blt_bge_bltu_bgeu <= 0; + is_compare <= 0; + + instr_beq <= 0; + instr_bne <= 0; + instr_blt <= 0; + instr_bge <= 0; + instr_bltu <= 0; + instr_bgeu <= 0; + + instr_addi <= 0; + instr_slti <= 0; + instr_sltiu <= 0; + instr_xori <= 0; + instr_ori <= 0; + instr_andi <= 0; + + instr_add <= 0; + instr_sub <= 0; + instr_sll <= 0; + instr_slt <= 0; + instr_sltu <= 0; + instr_xor <= 0; + instr_srl <= 0; + instr_sra <= 0; + instr_or <= 0; + instr_and <= 0; + end + end + + + // Main State Machine + + localparam cpu_state_trap = 8'b10000000; + localparam cpu_state_fetch = 8'b01000000; + localparam cpu_state_ld_rs1 = 8'b00100000; + localparam cpu_state_ld_rs2 = 8'b00010000; + localparam cpu_state_exec = 8'b00001000; + localparam cpu_state_shift = 8'b00000100; + localparam cpu_state_stmem = 8'b00000010; + localparam cpu_state_ldmem = 8'b00000001; + + reg [7:0] cpu_state; + reg [1:0] irq_state; + + `FORMAL_KEEP reg [127:0] dbg_ascii_state; + + always @* begin + dbg_ascii_state = ""; + if (cpu_state == cpu_state_trap) dbg_ascii_state = "trap"; + if (cpu_state == cpu_state_fetch) dbg_ascii_state = "fetch"; + if (cpu_state == cpu_state_ld_rs1) dbg_ascii_state = "ld_rs1"; + if (cpu_state == cpu_state_ld_rs2) dbg_ascii_state = "ld_rs2"; + if (cpu_state == cpu_state_exec) dbg_ascii_state = "exec"; + if (cpu_state == cpu_state_shift) dbg_ascii_state = "shift"; + if (cpu_state == cpu_state_stmem) dbg_ascii_state = "stmem"; + if (cpu_state == cpu_state_ldmem) dbg_ascii_state = "ldmem"; + end + + reg set_mem_do_rinst; + reg set_mem_do_rdata; + reg set_mem_do_wdata; + + reg latched_store; + reg latched_stalu; + reg latched_branch; + reg latched_compr; + reg latched_trace; + reg latched_is_lu; + reg latched_is_lh; + reg latched_is_lb; + reg [regindex_bits-1:0] latched_rd; + + reg [31:0] current_pc; + assign next_pc = latched_store && latched_branch ? reg_out & ~1 : reg_next_pc; + + reg [3:0] pcpi_timeout_counter; + reg pcpi_timeout; + + reg [31:0] next_irq_pending; + reg do_waitirq; + + reg [31:0] alu_out, alu_out_q; + reg alu_out_0, alu_out_0_q; + reg alu_wait, alu_wait_2; + + reg [31:0] alu_add_sub; + reg [31:0] alu_shl, alu_shr; + reg alu_eq, alu_ltu, alu_lts; + + generate if (TWO_CYCLE_ALU) begin + always @(posedge clk) begin + alu_add_sub <= instr_sub ? reg_op1 - reg_op2 : reg_op1 + reg_op2; + alu_eq <= reg_op1 == reg_op2; + alu_lts <= $signed(reg_op1) < $signed(reg_op2); + alu_ltu <= reg_op1 < reg_op2; + alu_shl <= reg_op1 << reg_op2[4:0]; + alu_shr <= $signed({instr_sra || instr_srai ? reg_op1[31] : 1'b0, reg_op1}) >>> reg_op2[4:0]; + end + end else begin + always @* begin + alu_add_sub = instr_sub ? reg_op1 - reg_op2 : reg_op1 + reg_op2; + alu_eq = reg_op1 == reg_op2; + alu_lts = $signed(reg_op1) < $signed(reg_op2); + alu_ltu = reg_op1 < reg_op2; + alu_shl = reg_op1 << reg_op2[4:0]; + alu_shr = $signed({instr_sra || instr_srai ? reg_op1[31] : 1'b0, reg_op1}) >>> reg_op2[4:0]; + end + end endgenerate + + always @* begin + alu_out_0 = 'bx; + (* parallel_case, full_case *) + case (1'b1) + instr_beq: + alu_out_0 = alu_eq; + instr_bne: + alu_out_0 = !alu_eq; + instr_bge: + alu_out_0 = !alu_lts; + instr_bgeu: + alu_out_0 = !alu_ltu; + is_slti_blt_slt && (!TWO_CYCLE_COMPARE || !{instr_beq,instr_bne,instr_bge,instr_bgeu}): + alu_out_0 = alu_lts; + is_sltiu_bltu_sltu && (!TWO_CYCLE_COMPARE || !{instr_beq,instr_bne,instr_bge,instr_bgeu}): + alu_out_0 = alu_ltu; + endcase + + alu_out = 'bx; + (* parallel_case, full_case *) + case (1'b1) + is_lui_auipc_jal_jalr_addi_add_sub: + alu_out = alu_add_sub; + is_compare: + alu_out = alu_out_0; + instr_xori || instr_xor: + alu_out = reg_op1 ^ reg_op2; + instr_ori || instr_or: + alu_out = reg_op1 | reg_op2; + instr_andi || instr_and: + alu_out = reg_op1 & reg_op2; + BARREL_SHIFTER && (instr_sll || instr_slli): + alu_out = alu_shl; + BARREL_SHIFTER && (instr_srl || instr_srli || instr_sra || instr_srai): + alu_out = alu_shr; + endcase + +`ifdef RISCV_FORMAL_BLACKBOX_ALU + alu_out_0 = $anyseq; + alu_out = $anyseq; +`endif + end + + reg clear_prefetched_high_word_q; + always @(posedge clk) clear_prefetched_high_word_q <= clear_prefetched_high_word; + + always @* begin + clear_prefetched_high_word = clear_prefetched_high_word_q; + if (!prefetched_high_word) + clear_prefetched_high_word = 0; + if (latched_branch || irq_state || !resetn) + clear_prefetched_high_word = COMPRESSED_ISA; + end + + reg cpuregs_write; + reg [31:0] cpuregs_wrdata; + reg [31:0] cpuregs_rs1; + reg [31:0] cpuregs_rs2; + reg [regindex_bits-1:0] decoded_rs; + + always @* begin + cpuregs_write = 0; + cpuregs_wrdata = 'bx; + + if (cpu_state == cpu_state_fetch) begin + (* parallel_case *) + case (1'b1) + latched_branch: begin + cpuregs_wrdata = reg_pc + (latched_compr ? 2 : 4); + cpuregs_write = 1; + end + latched_store && !latched_branch: begin + cpuregs_wrdata = latched_stalu ? alu_out_q : reg_out; + cpuregs_write = 1; + end + ENABLE_IRQ && irq_state[0]: begin + cpuregs_wrdata = reg_next_pc | latched_compr; + cpuregs_write = 1; + end + ENABLE_IRQ && irq_state[1]: begin + cpuregs_wrdata = irq_pending & ~irq_mask; + cpuregs_write = 1; + end + endcase + end + end + +`ifndef PICORV32_REGS + always @(posedge clk) begin + if (resetn && cpuregs_write && latched_rd) +`ifdef PICORV32_TESTBUG_001 + cpuregs[latched_rd ^ 1] <= cpuregs_wrdata; +`elsif PICORV32_TESTBUG_002 + cpuregs[latched_rd] <= cpuregs_wrdata ^ 1; +`else + cpuregs[latched_rd] <= cpuregs_wrdata; +`endif + end + + always @* begin + decoded_rs = 'bx; + if (ENABLE_REGS_DUALPORT) begin +`ifndef RISCV_FORMAL_BLACKBOX_REGS + cpuregs_rs1 = decoded_rs1 ? cpuregs[decoded_rs1] : 0; + cpuregs_rs2 = decoded_rs2 ? cpuregs[decoded_rs2] : 0; +`else + cpuregs_rs1 = decoded_rs1 ? $anyseq : 0; + cpuregs_rs2 = decoded_rs2 ? $anyseq : 0; +`endif + end else begin + decoded_rs = (cpu_state == cpu_state_ld_rs2) ? decoded_rs2 : decoded_rs1; +`ifndef RISCV_FORMAL_BLACKBOX_REGS + cpuregs_rs1 = decoded_rs ? cpuregs[decoded_rs] : 0; +`else + cpuregs_rs1 = decoded_rs ? $anyseq : 0; +`endif + cpuregs_rs2 = cpuregs_rs1; + end + end +`else + wire[31:0] cpuregs_rdata1; + wire[31:0] cpuregs_rdata2; + + wire [5:0] cpuregs_waddr = latched_rd; + wire [5:0] cpuregs_raddr1 = ENABLE_REGS_DUALPORT ? decoded_rs1 : decoded_rs; + wire [5:0] cpuregs_raddr2 = ENABLE_REGS_DUALPORT ? decoded_rs2 : 0; + + `PICORV32_REGS cpuregs ( + .clk(clk), + .wen(resetn && cpuregs_write && latched_rd), + .waddr(cpuregs_waddr), + .raddr1(cpuregs_raddr1), + .raddr2(cpuregs_raddr2), + .wdata(cpuregs_wrdata), + .rdata1(cpuregs_rdata1), + .rdata2(cpuregs_rdata2) + ); + + always @* begin + decoded_rs = 'bx; + if (ENABLE_REGS_DUALPORT) begin + cpuregs_rs1 = decoded_rs1 ? cpuregs_rdata1 : 0; + cpuregs_rs2 = decoded_rs2 ? cpuregs_rdata2 : 0; + end else begin + decoded_rs = (cpu_state == cpu_state_ld_rs2) ? decoded_rs2 : decoded_rs1; + cpuregs_rs1 = decoded_rs ? cpuregs_rdata1 : 0; + cpuregs_rs2 = cpuregs_rs1; + end + end +`endif + + assign launch_next_insn = cpu_state == cpu_state_fetch && decoder_trigger && (!ENABLE_IRQ || irq_delay || irq_active || !(irq_pending & ~irq_mask)); + + always @(posedge clk) begin + trap <= 0; + reg_sh <= 'bx; + reg_out <= 'bx; + set_mem_do_rinst = 0; + set_mem_do_rdata = 0; + set_mem_do_wdata = 0; + + alu_out_0_q <= alu_out_0; + alu_out_q <= alu_out; + + alu_wait <= 0; + alu_wait_2 <= 0; + + if (launch_next_insn) begin + dbg_rs1val <= 'bx; + dbg_rs2val <= 'bx; + dbg_rs1val_valid <= 0; + dbg_rs2val_valid <= 0; + end + + if (WITH_PCPI && CATCH_ILLINSN) begin + if (resetn && pcpi_valid && !pcpi_int_wait) begin + if (pcpi_timeout_counter) + pcpi_timeout_counter <= pcpi_timeout_counter - 1; + end else + pcpi_timeout_counter <= ~0; + pcpi_timeout <= !pcpi_timeout_counter; + end + + if (ENABLE_COUNTERS) begin + count_cycle <= resetn ? count_cycle + 1 : 0; + if (!ENABLE_COUNTERS64) count_cycle[63:32] <= 0; + end else begin + count_cycle <= 'bx; + count_instr <= 'bx; + end + + next_irq_pending = ENABLE_IRQ ? irq_pending & LATCHED_IRQ : 'bx; + + if (ENABLE_IRQ && ENABLE_IRQ_TIMER && timer) begin + timer <= timer - 1; + end + + decoder_trigger <= mem_do_rinst && mem_done; + decoder_trigger_q <= decoder_trigger; + decoder_pseudo_trigger <= 0; + decoder_pseudo_trigger_q <= decoder_pseudo_trigger; + do_waitirq <= 0; + + trace_valid <= 0; + + if (!ENABLE_TRACE) + trace_data <= 'bx; + + if (!resetn) begin + reg_pc <= PROGADDR_RESET; + reg_next_pc <= PROGADDR_RESET; + if (ENABLE_COUNTERS) + count_instr <= 0; + latched_store <= 0; + latched_stalu <= 0; + latched_branch <= 0; + latched_trace <= 0; + latched_is_lu <= 0; + latched_is_lh <= 0; + latched_is_lb <= 0; + pcpi_valid <= 0; + pcpi_timeout <= 0; + irq_active <= 0; + irq_delay <= 0; + irq_mask <= ~0; + next_irq_pending = 0; + irq_state <= 0; + eoi <= 0; + timer <= 0; + if (~STACKADDR) begin + latched_store <= 1; + latched_rd <= 2; + reg_out <= STACKADDR; + end + cpu_state <= cpu_state_fetch; + end else + (* parallel_case, full_case *) + case (cpu_state) + cpu_state_trap: begin + trap <= 1; + end + + cpu_state_fetch: begin + mem_do_rinst <= !decoder_trigger && !do_waitirq; + mem_wordsize <= 0; + + current_pc = reg_next_pc; + + (* parallel_case *) + case (1'b1) + latched_branch: begin + current_pc = latched_store ? (latched_stalu ? alu_out_q : reg_out) & ~1 : reg_next_pc; + `debug($display("ST_RD: %2d 0x%08x, BRANCH 0x%08x", latched_rd, reg_pc + (latched_compr ? 2 : 4), current_pc);) + end + latched_store && !latched_branch: begin + `debug($display("ST_RD: %2d 0x%08x", latched_rd, latched_stalu ? alu_out_q : reg_out);) + end + ENABLE_IRQ && irq_state[0]: begin + current_pc = PROGADDR_IRQ; + irq_active <= 1; + mem_do_rinst <= 1; + end + ENABLE_IRQ && irq_state[1]: begin + eoi <= irq_pending & ~irq_mask; + next_irq_pending = next_irq_pending & irq_mask; + end + endcase + + if (ENABLE_TRACE && latched_trace) begin + latched_trace <= 0; + trace_valid <= 1; + if (latched_branch) + trace_data <= (irq_active ? TRACE_IRQ : 0) | TRACE_BRANCH | (current_pc & 32'hfffffffe); + else + trace_data <= (irq_active ? TRACE_IRQ : 0) | (latched_stalu ? alu_out_q : reg_out); + end + + reg_pc <= current_pc; + reg_next_pc <= current_pc; + + latched_store <= 0; + latched_stalu <= 0; + latched_branch <= 0; + latched_is_lu <= 0; + latched_is_lh <= 0; + latched_is_lb <= 0; + latched_rd <= decoded_rd; + latched_compr <= compressed_instr; + + if (ENABLE_IRQ && ((decoder_trigger && !irq_active && !irq_delay && |(irq_pending & ~irq_mask)) || irq_state)) begin + irq_state <= + irq_state == 2'b00 ? 2'b01 : + irq_state == 2'b01 ? 2'b10 : 2'b00; + latched_compr <= latched_compr; + if (ENABLE_IRQ_QREGS) + latched_rd <= irqregs_offset | irq_state[0]; + else + latched_rd <= irq_state[0] ? 4 : 3; + end else + if (ENABLE_IRQ && (decoder_trigger || do_waitirq) && instr_waitirq) begin + if (irq_pending) begin + latched_store <= 1; + reg_out <= irq_pending; + reg_next_pc <= current_pc + (compressed_instr ? 2 : 4); + mem_do_rinst <= 1; + end else + do_waitirq <= 1; + end else + if (decoder_trigger) begin + `debug($display("-- %-0t", $time);) + irq_delay <= irq_active; + reg_next_pc <= current_pc + (compressed_instr ? 2 : 4); + if (ENABLE_TRACE) + latched_trace <= 1; + if (ENABLE_COUNTERS) begin + count_instr <= count_instr + 1; + if (!ENABLE_COUNTERS64) count_instr[63:32] <= 0; + end + if (instr_jal) begin + mem_do_rinst <= 1; + reg_next_pc <= current_pc + decoded_imm_j; + latched_branch <= 1; + end else begin + mem_do_rinst <= 0; + mem_do_prefetch <= !instr_jalr && !instr_retirq; + cpu_state <= cpu_state_ld_rs1; + end + end + end + + cpu_state_ld_rs1: begin + reg_op1 <= 'bx; + reg_op2 <= 'bx; + + (* parallel_case *) + case (1'b1) + (CATCH_ILLINSN || WITH_PCPI) && instr_trap: begin + if (WITH_PCPI) begin + `debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);) + reg_op1 <= cpuregs_rs1; + dbg_rs1val <= cpuregs_rs1; + dbg_rs1val_valid <= 1; + if (ENABLE_REGS_DUALPORT) begin + pcpi_valid <= 1; + `debug($display("LD_RS2: %2d 0x%08x", decoded_rs2, cpuregs_rs2);) + reg_sh <= cpuregs_rs2; + reg_op2 <= cpuregs_rs2; + dbg_rs2val <= cpuregs_rs2; + dbg_rs2val_valid <= 1; + if (pcpi_int_ready) begin + mem_do_rinst <= 1; + pcpi_valid <= 0; + reg_out <= pcpi_int_rd; + latched_store <= pcpi_int_wr; + cpu_state <= cpu_state_fetch; + end else + if (CATCH_ILLINSN && (pcpi_timeout || instr_ecall_ebreak)) begin + pcpi_valid <= 0; + `debug($display("EBREAK OR UNSUPPORTED INSN AT 0x%08x", reg_pc);) + if (ENABLE_IRQ && !irq_mask[irq_ebreak] && !irq_active) begin + next_irq_pending[irq_ebreak] = 1; + cpu_state <= cpu_state_fetch; + end else + cpu_state <= cpu_state_trap; + end + end else begin + cpu_state <= cpu_state_ld_rs2; + end + end else begin + `debug($display("EBREAK OR UNSUPPORTED INSN AT 0x%08x", reg_pc);) + if (ENABLE_IRQ && !irq_mask[irq_ebreak] && !irq_active) begin + next_irq_pending[irq_ebreak] = 1; + cpu_state <= cpu_state_fetch; + end else + cpu_state <= cpu_state_trap; + end + end + ENABLE_COUNTERS && is_rdcycle_rdcycleh_rdinstr_rdinstrh: begin + (* parallel_case, full_case *) + case (1'b1) + instr_rdcycle: + reg_out <= count_cycle[31:0]; + instr_rdcycleh && ENABLE_COUNTERS64: + reg_out <= count_cycle[63:32]; + instr_rdinstr: + reg_out <= count_instr[31:0]; + instr_rdinstrh && ENABLE_COUNTERS64: + reg_out <= count_instr[63:32]; + endcase + latched_store <= 1; + cpu_state <= cpu_state_fetch; + end + is_lui_auipc_jal: begin + reg_op1 <= instr_lui ? 0 : reg_pc; + reg_op2 <= decoded_imm; + if (TWO_CYCLE_ALU) + alu_wait <= 1; + else + mem_do_rinst <= mem_do_prefetch; + cpu_state <= cpu_state_exec; + end + ENABLE_IRQ && ENABLE_IRQ_QREGS && instr_getq: begin + `debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);) + reg_out <= cpuregs_rs1; + dbg_rs1val <= cpuregs_rs1; + dbg_rs1val_valid <= 1; + latched_store <= 1; + cpu_state <= cpu_state_fetch; + end + ENABLE_IRQ && ENABLE_IRQ_QREGS && instr_setq: begin + `debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);) + reg_out <= cpuregs_rs1; + dbg_rs1val <= cpuregs_rs1; + dbg_rs1val_valid <= 1; + latched_rd <= latched_rd | irqregs_offset; + latched_store <= 1; + cpu_state <= cpu_state_fetch; + end + ENABLE_IRQ && instr_retirq: begin + eoi <= 0; + irq_active <= 0; + latched_branch <= 1; + latched_store <= 1; + `debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);) + reg_out <= CATCH_MISALIGN ? (cpuregs_rs1 & 32'h fffffffe) : cpuregs_rs1; + dbg_rs1val <= cpuregs_rs1; + dbg_rs1val_valid <= 1; + cpu_state <= cpu_state_fetch; + end + ENABLE_IRQ && instr_maskirq: begin + latched_store <= 1; + reg_out <= irq_mask; + `debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);) + irq_mask <= cpuregs_rs1 | MASKED_IRQ; + dbg_rs1val <= cpuregs_rs1; + dbg_rs1val_valid <= 1; + cpu_state <= cpu_state_fetch; + end + ENABLE_IRQ && ENABLE_IRQ_TIMER && instr_timer: begin + latched_store <= 1; + reg_out <= timer; + `debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);) + timer <= cpuregs_rs1; + dbg_rs1val <= cpuregs_rs1; + dbg_rs1val_valid <= 1; + cpu_state <= cpu_state_fetch; + end + is_lb_lh_lw_lbu_lhu && !instr_trap: begin + `debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);) + reg_op1 <= cpuregs_rs1; + dbg_rs1val <= cpuregs_rs1; + dbg_rs1val_valid <= 1; + cpu_state <= cpu_state_ldmem; + mem_do_rinst <= 1; + end + is_slli_srli_srai && !BARREL_SHIFTER: begin + `debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);) + reg_op1 <= cpuregs_rs1; + dbg_rs1val <= cpuregs_rs1; + dbg_rs1val_valid <= 1; + reg_sh <= decoded_rs2; + cpu_state <= cpu_state_shift; + end + is_jalr_addi_slti_sltiu_xori_ori_andi, is_slli_srli_srai && BARREL_SHIFTER: begin + `debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);) + reg_op1 <= cpuregs_rs1; + dbg_rs1val <= cpuregs_rs1; + dbg_rs1val_valid <= 1; + reg_op2 <= is_slli_srli_srai && BARREL_SHIFTER ? decoded_rs2 : decoded_imm; + if (TWO_CYCLE_ALU) + alu_wait <= 1; + else + mem_do_rinst <= mem_do_prefetch; + cpu_state <= cpu_state_exec; + end + default: begin + `debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);) + reg_op1 <= cpuregs_rs1; + dbg_rs1val <= cpuregs_rs1; + dbg_rs1val_valid <= 1; + if (ENABLE_REGS_DUALPORT) begin + `debug($display("LD_RS2: %2d 0x%08x", decoded_rs2, cpuregs_rs2);) + reg_sh <= cpuregs_rs2; + reg_op2 <= cpuregs_rs2; + dbg_rs2val <= cpuregs_rs2; + dbg_rs2val_valid <= 1; + (* parallel_case *) + case (1'b1) + is_sb_sh_sw: begin + cpu_state <= cpu_state_stmem; + mem_do_rinst <= 1; + end + is_sll_srl_sra && !BARREL_SHIFTER: begin + cpu_state <= cpu_state_shift; + end + default: begin + if (TWO_CYCLE_ALU || (TWO_CYCLE_COMPARE && is_beq_bne_blt_bge_bltu_bgeu)) begin + alu_wait_2 <= TWO_CYCLE_ALU && (TWO_CYCLE_COMPARE && is_beq_bne_blt_bge_bltu_bgeu); + alu_wait <= 1; + end else + mem_do_rinst <= mem_do_prefetch; + cpu_state <= cpu_state_exec; + end + endcase + end else + cpu_state <= cpu_state_ld_rs2; + end + endcase + end + + cpu_state_ld_rs2: begin + `debug($display("LD_RS2: %2d 0x%08x", decoded_rs2, cpuregs_rs2);) + reg_sh <= cpuregs_rs2; + reg_op2 <= cpuregs_rs2; + dbg_rs2val <= cpuregs_rs2; + dbg_rs2val_valid <= 1; + + (* parallel_case *) + case (1'b1) + WITH_PCPI && instr_trap: begin + pcpi_valid <= 1; + if (pcpi_int_ready) begin + mem_do_rinst <= 1; + pcpi_valid <= 0; + reg_out <= pcpi_int_rd; + latched_store <= pcpi_int_wr; + cpu_state <= cpu_state_fetch; + end else + if (CATCH_ILLINSN && (pcpi_timeout || instr_ecall_ebreak)) begin + pcpi_valid <= 0; + `debug($display("EBREAK OR UNSUPPORTED INSN AT 0x%08x", reg_pc);) + if (ENABLE_IRQ && !irq_mask[irq_ebreak] && !irq_active) begin + next_irq_pending[irq_ebreak] = 1; + cpu_state <= cpu_state_fetch; + end else + cpu_state <= cpu_state_trap; + end + end + is_sb_sh_sw: begin + cpu_state <= cpu_state_stmem; + mem_do_rinst <= 1; + end + is_sll_srl_sra && !BARREL_SHIFTER: begin + cpu_state <= cpu_state_shift; + end + default: begin + if (TWO_CYCLE_ALU || (TWO_CYCLE_COMPARE && is_beq_bne_blt_bge_bltu_bgeu)) begin + alu_wait_2 <= TWO_CYCLE_ALU && (TWO_CYCLE_COMPARE && is_beq_bne_blt_bge_bltu_bgeu); + alu_wait <= 1; + end else + mem_do_rinst <= mem_do_prefetch; + cpu_state <= cpu_state_exec; + end + endcase + end + + cpu_state_exec: begin + reg_out <= reg_pc + decoded_imm; + if ((TWO_CYCLE_ALU || TWO_CYCLE_COMPARE) && (alu_wait || alu_wait_2)) begin + mem_do_rinst <= mem_do_prefetch && !alu_wait_2; + alu_wait <= alu_wait_2; + end else + if (is_beq_bne_blt_bge_bltu_bgeu) begin + latched_rd <= 0; + latched_store <= TWO_CYCLE_COMPARE ? alu_out_0_q : alu_out_0; + latched_branch <= TWO_CYCLE_COMPARE ? alu_out_0_q : alu_out_0; + if (mem_done) + cpu_state <= cpu_state_fetch; + if (TWO_CYCLE_COMPARE ? alu_out_0_q : alu_out_0) begin + decoder_trigger <= 0; + set_mem_do_rinst = 1; + end + end else begin + latched_branch <= instr_jalr; + latched_store <= 1; + latched_stalu <= 1; + cpu_state <= cpu_state_fetch; + end + end + + cpu_state_shift: begin + latched_store <= 1; + if (reg_sh == 0) begin + reg_out <= reg_op1; + mem_do_rinst <= mem_do_prefetch; + cpu_state <= cpu_state_fetch; + end else if (TWO_STAGE_SHIFT && reg_sh >= 4) begin + (* parallel_case, full_case *) + case (1'b1) + instr_slli || instr_sll: reg_op1 <= reg_op1 << 4; + instr_srli || instr_srl: reg_op1 <= reg_op1 >> 4; + instr_srai || instr_sra: reg_op1 <= $signed(reg_op1) >>> 4; + endcase + reg_sh <= reg_sh - 4; + end else begin + (* parallel_case, full_case *) + case (1'b1) + instr_slli || instr_sll: reg_op1 <= reg_op1 << 1; + instr_srli || instr_srl: reg_op1 <= reg_op1 >> 1; + instr_srai || instr_sra: reg_op1 <= $signed(reg_op1) >>> 1; + endcase + reg_sh <= reg_sh - 1; + end + end + + cpu_state_stmem: begin + if (ENABLE_TRACE) + reg_out <= reg_op2; + if (!mem_do_prefetch || mem_done) begin + if (!mem_do_wdata) begin + (* parallel_case, full_case *) + case (1'b1) + instr_sb: mem_wordsize <= 2; + instr_sh: mem_wordsize <= 1; + instr_sw: mem_wordsize <= 0; + endcase + if (ENABLE_TRACE) begin + trace_valid <= 1; + trace_data <= (irq_active ? TRACE_IRQ : 0) | TRACE_ADDR | ((reg_op1 + decoded_imm) & 32'hffffffff); + end + reg_op1 <= reg_op1 + decoded_imm; + set_mem_do_wdata = 1; + end + if (!mem_do_prefetch && mem_done) begin + cpu_state <= cpu_state_fetch; + decoder_trigger <= 1; + decoder_pseudo_trigger <= 1; + end + end + end + + cpu_state_ldmem: begin + latched_store <= 1; + if (!mem_do_prefetch || mem_done) begin + if (!mem_do_rdata) begin + (* parallel_case, full_case *) + case (1'b1) + instr_lb || instr_lbu: mem_wordsize <= 2; + instr_lh || instr_lhu: mem_wordsize <= 1; + instr_lw: mem_wordsize <= 0; + endcase + latched_is_lu <= is_lbu_lhu_lw; + latched_is_lh <= instr_lh; + latched_is_lb <= instr_lb; + if (ENABLE_TRACE) begin + trace_valid <= 1; + trace_data <= (irq_active ? TRACE_IRQ : 0) | TRACE_ADDR | ((reg_op1 + decoded_imm) & 32'hffffffff); + end + reg_op1 <= reg_op1 + decoded_imm; + set_mem_do_rdata = 1; + end + if (!mem_do_prefetch && mem_done) begin + (* parallel_case, full_case *) + case (1'b1) + latched_is_lu: reg_out <= mem_rdata_word; + latched_is_lh: reg_out <= $signed(mem_rdata_word[15:0]); + latched_is_lb: reg_out <= $signed(mem_rdata_word[7:0]); + endcase + decoder_trigger <= 1; + decoder_pseudo_trigger <= 1; + cpu_state <= cpu_state_fetch; + end + end + end + endcase + + if (ENABLE_IRQ) begin + next_irq_pending = next_irq_pending | irq; + if(ENABLE_IRQ_TIMER && timer) + if (timer - 1 == 0) + next_irq_pending[irq_timer] = 1; + end + + if (CATCH_MISALIGN && resetn && (mem_do_rdata || mem_do_wdata)) begin + if (mem_wordsize == 0 && reg_op1[1:0] != 0) begin + `debug($display("MISALIGNED WORD: 0x%08x", reg_op1);) + if (ENABLE_IRQ && !irq_mask[irq_buserror] && !irq_active) begin + next_irq_pending[irq_buserror] = 1; + end else + cpu_state <= cpu_state_trap; + end + if (mem_wordsize == 1 && reg_op1[0] != 0) begin + `debug($display("MISALIGNED HALFWORD: 0x%08x", reg_op1);) + if (ENABLE_IRQ && !irq_mask[irq_buserror] && !irq_active) begin + next_irq_pending[irq_buserror] = 1; + end else + cpu_state <= cpu_state_trap; + end + end + if (CATCH_MISALIGN && resetn && mem_do_rinst && (COMPRESSED_ISA ? reg_pc[0] : |reg_pc[1:0])) begin + `debug($display("MISALIGNED INSTRUCTION: 0x%08x", reg_pc);) + if (ENABLE_IRQ && !irq_mask[irq_buserror] && !irq_active) begin + next_irq_pending[irq_buserror] = 1; + end else + cpu_state <= cpu_state_trap; + end + if (!CATCH_ILLINSN && decoder_trigger_q && !decoder_pseudo_trigger_q && instr_ecall_ebreak) begin + cpu_state <= cpu_state_trap; + end + + if (!resetn || mem_done) begin + mem_do_prefetch <= 0; + mem_do_rinst <= 0; + mem_do_rdata <= 0; + mem_do_wdata <= 0; + end + + if (set_mem_do_rinst) + mem_do_rinst <= 1; + if (set_mem_do_rdata) + mem_do_rdata <= 1; + if (set_mem_do_wdata) + mem_do_wdata <= 1; + + irq_pending <= next_irq_pending & ~MASKED_IRQ; + + if (!CATCH_MISALIGN) begin + if (COMPRESSED_ISA) begin + reg_pc[0] <= 0; + reg_next_pc[0] <= 0; + end else begin + reg_pc[1:0] <= 0; + reg_next_pc[1:0] <= 0; + end + end + current_pc = 'bx; + end + +`ifdef RISCV_FORMAL + reg dbg_irq_call; + reg dbg_irq_enter; + reg [31:0] dbg_irq_ret; + always @(posedge clk) begin + rvfi_valid <= resetn && (launch_next_insn || trap) && dbg_valid_insn; + rvfi_order <= resetn ? rvfi_order + rvfi_valid : 0; + + rvfi_insn <= dbg_insn_opcode; + rvfi_rs1_addr <= dbg_rs1val_valid ? dbg_insn_rs1 : 0; + rvfi_rs2_addr <= dbg_rs2val_valid ? dbg_insn_rs2 : 0; + rvfi_pc_rdata <= dbg_insn_addr; + rvfi_rs1_rdata <= dbg_rs1val_valid ? dbg_rs1val : 0; + rvfi_rs2_rdata <= dbg_rs2val_valid ? dbg_rs2val : 0; + rvfi_trap <= trap; + rvfi_halt <= trap; + rvfi_intr <= dbg_irq_enter; + rvfi_mode <= 3; + rvfi_ixl <= 1; + + if (!resetn) begin + dbg_irq_call <= 0; + dbg_irq_enter <= 0; + end else + if (rvfi_valid) begin + dbg_irq_call <= 0; + dbg_irq_enter <= dbg_irq_call; + end else + if (irq_state == 1) begin + dbg_irq_call <= 1; + dbg_irq_ret <= next_pc; + end + + if (!resetn) begin + rvfi_rd_addr <= 0; + rvfi_rd_wdata <= 0; + end else + if (cpuregs_write && !irq_state) begin +`ifdef PICORV32_TESTBUG_003 + rvfi_rd_addr <= latched_rd ^ 1; +`else + rvfi_rd_addr <= latched_rd; +`endif +`ifdef PICORV32_TESTBUG_004 + rvfi_rd_wdata <= latched_rd ? cpuregs_wrdata ^ 1 : 0; +`else + rvfi_rd_wdata <= latched_rd ? cpuregs_wrdata : 0; +`endif + end else + if (rvfi_valid) begin + rvfi_rd_addr <= 0; + rvfi_rd_wdata <= 0; + end + + casez (dbg_insn_opcode) + 32'b 0000000_?????_000??_???_?????_0001011: begin // getq + rvfi_rs1_addr <= 0; + rvfi_rs1_rdata <= 0; + end + 32'b 0000001_?????_?????_???_000??_0001011: begin // setq + rvfi_rd_addr <= 0; + rvfi_rd_wdata <= 0; + end + 32'b 0000010_?????_00000_???_00000_0001011: begin // retirq + rvfi_rs1_addr <= 0; + rvfi_rs1_rdata <= 0; + end + endcase + + if (!dbg_irq_call) begin + if (dbg_mem_instr) begin + rvfi_mem_addr <= 0; + rvfi_mem_rmask <= 0; + rvfi_mem_wmask <= 0; + rvfi_mem_rdata <= 0; + rvfi_mem_wdata <= 0; + end else + if (dbg_mem_valid && dbg_mem_ready) begin + rvfi_mem_addr <= dbg_mem_addr; + rvfi_mem_rmask <= dbg_mem_wstrb ? 0 : ~0; + rvfi_mem_wmask <= dbg_mem_wstrb; + rvfi_mem_rdata <= dbg_mem_rdata; + rvfi_mem_wdata <= dbg_mem_wdata; + end + end + end + + always @* begin +`ifdef PICORV32_TESTBUG_005 + rvfi_pc_wdata = (dbg_irq_call ? dbg_irq_ret : dbg_insn_addr) ^ 4; +`else + rvfi_pc_wdata = dbg_irq_call ? dbg_irq_ret : dbg_insn_addr; +`endif + + rvfi_csr_mcycle_rmask = 0; + rvfi_csr_mcycle_wmask = 0; + rvfi_csr_mcycle_rdata = 0; + rvfi_csr_mcycle_wdata = 0; + + rvfi_csr_minstret_rmask = 0; + rvfi_csr_minstret_wmask = 0; + rvfi_csr_minstret_rdata = 0; + rvfi_csr_minstret_wdata = 0; + + if (rvfi_valid && rvfi_insn[6:0] == 7'b 1110011 && rvfi_insn[13:12] == 3'b010) begin + if (rvfi_insn[31:20] == 12'h C00) begin + rvfi_csr_mcycle_rmask = 64'h 0000_0000_FFFF_FFFF; + rvfi_csr_mcycle_rdata = {32'h 0000_0000, rvfi_rd_wdata}; + end + if (rvfi_insn[31:20] == 12'h C80) begin + rvfi_csr_mcycle_rmask = 64'h FFFF_FFFF_0000_0000; + rvfi_csr_mcycle_rdata = {rvfi_rd_wdata, 32'h 0000_0000}; + end + if (rvfi_insn[31:20] == 12'h C02) begin + rvfi_csr_minstret_rmask = 64'h 0000_0000_FFFF_FFFF; + rvfi_csr_minstret_rdata = {32'h 0000_0000, rvfi_rd_wdata}; + end + if (rvfi_insn[31:20] == 12'h C82) begin + rvfi_csr_minstret_rmask = 64'h FFFF_FFFF_0000_0000; + rvfi_csr_minstret_rdata = {rvfi_rd_wdata, 32'h 0000_0000}; + end + end + end +`endif + + // Formal Verification +`ifdef FORMAL + reg [3:0] last_mem_nowait; + always @(posedge clk) + last_mem_nowait <= {last_mem_nowait, mem_ready || !mem_valid}; + + // stall the memory interface for max 4 cycles + restrict property (|last_mem_nowait || mem_ready || !mem_valid); + + // resetn low in first cycle, after that resetn high + restrict property (resetn != $initstate); + + // this just makes it much easier to read traces. uncomment as needed. + // assume property (mem_valid || !mem_ready); + + reg ok; + always @* begin + if (resetn) begin + // instruction fetches are read-only + if (mem_valid && mem_instr) + assert (mem_wstrb == 0); + + // cpu_state must be valid + ok = 0; + if (cpu_state == cpu_state_trap) ok = 1; + if (cpu_state == cpu_state_fetch) ok = 1; + if (cpu_state == cpu_state_ld_rs1) ok = 1; + if (cpu_state == cpu_state_ld_rs2) ok = !ENABLE_REGS_DUALPORT; + if (cpu_state == cpu_state_exec) ok = 1; + if (cpu_state == cpu_state_shift) ok = 1; + if (cpu_state == cpu_state_stmem) ok = 1; + if (cpu_state == cpu_state_ldmem) ok = 1; + assert (ok); + end + end + + reg last_mem_la_read = 0; + reg last_mem_la_write = 0; + reg [31:0] last_mem_la_addr; + reg [31:0] last_mem_la_wdata; + reg [3:0] last_mem_la_wstrb = 0; + + always @(posedge clk) begin + last_mem_la_read <= mem_la_read; + last_mem_la_write <= mem_la_write; + last_mem_la_addr <= mem_la_addr; + last_mem_la_wdata <= mem_la_wdata; + last_mem_la_wstrb <= mem_la_wstrb; + + if (last_mem_la_read) begin + assert(mem_valid); + assert(mem_addr == last_mem_la_addr); + assert(mem_wstrb == 0); + end + if (last_mem_la_write) begin + assert(mem_valid); + assert(mem_addr == last_mem_la_addr); + assert(mem_wdata == last_mem_la_wdata); + assert(mem_wstrb == last_mem_la_wstrb); + end + if (mem_la_read || mem_la_write) begin + assert(!mem_valid || mem_ready); + end + end +`endif +endmodule + +// This is a simple example implementation of PICORV32_REGS. +// Use the PICORV32_REGS mechanism if you want to use custom +// memory resources to implement the processor register file. +// Note that your implementation must match the requirements of +// the PicoRV32 configuration. (e.g. QREGS, etc) +module picorv32_regs ( + input clk, wen, + input [5:0] waddr, + input [5:0] raddr1, + input [5:0] raddr2, + input [31:0] wdata, + output [31:0] rdata1, + output [31:0] rdata2 +); + reg [31:0] regs [0:30]; + + always @(posedge clk) + if (wen) regs[~waddr[4:0]] <= wdata; + + assign rdata1 = regs[~raddr1[4:0]]; + assign rdata2 = regs[~raddr2[4:0]]; +endmodule + + +/*************************************************************** + * picorv32_pcpi_mul + ***************************************************************/ + +module picorv32_pcpi_mul #( + parameter STEPS_AT_ONCE = 1, + parameter CARRY_CHAIN = 4 +) ( + input clk, resetn, + + input pcpi_valid, + input [31:0] pcpi_insn, + input [31:0] pcpi_rs1, + input [31:0] pcpi_rs2, + output reg pcpi_wr, + output reg [31:0] pcpi_rd, + output reg pcpi_wait, + output reg pcpi_ready +); + reg instr_mul, instr_mulh, instr_mulhsu, instr_mulhu; + wire instr_any_mul = |{instr_mul, instr_mulh, instr_mulhsu, instr_mulhu}; + wire instr_any_mulh = |{instr_mulh, instr_mulhsu, instr_mulhu}; + wire instr_rs1_signed = |{instr_mulh, instr_mulhsu}; + wire instr_rs2_signed = |{instr_mulh}; + + reg pcpi_wait_q; + wire mul_start = pcpi_wait && !pcpi_wait_q; + + always @(posedge clk) begin + instr_mul <= 0; + instr_mulh <= 0; + instr_mulhsu <= 0; + instr_mulhu <= 0; + + if (resetn && pcpi_valid && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001) begin + case (pcpi_insn[14:12]) + 3'b000: instr_mul <= 1; + 3'b001: instr_mulh <= 1; + 3'b010: instr_mulhsu <= 1; + 3'b011: instr_mulhu <= 1; + endcase + end + + pcpi_wait <= instr_any_mul; + pcpi_wait_q <= pcpi_wait; + end + + reg [63:0] rs1, rs2, rd, rdx; + reg [63:0] next_rs1, next_rs2, this_rs2; + reg [63:0] next_rd, next_rdx, next_rdt; + reg [6:0] mul_counter; + reg mul_waiting; + reg mul_finish; + integer i, j; + + // carry save accumulator + always @* begin + next_rd = rd; + next_rdx = rdx; + next_rs1 = rs1; + next_rs2 = rs2; + + for (i = 0; i < STEPS_AT_ONCE; i=i+1) begin + this_rs2 = next_rs1[0] ? next_rs2 : 0; + if (CARRY_CHAIN == 0) begin + next_rdt = next_rd ^ next_rdx ^ this_rs2; + next_rdx = ((next_rd & next_rdx) | (next_rd & this_rs2) | (next_rdx & this_rs2)) << 1; + next_rd = next_rdt; + end else begin + next_rdt = 0; + for (j = 0; j < 64; j = j + CARRY_CHAIN) + {next_rdt[j+CARRY_CHAIN-1], next_rd[j +: CARRY_CHAIN]} = + next_rd[j +: CARRY_CHAIN] + next_rdx[j +: CARRY_CHAIN] + this_rs2[j +: CARRY_CHAIN]; + next_rdx = next_rdt << 1; + end + next_rs1 = next_rs1 >> 1; + next_rs2 = next_rs2 << 1; + end + end + + always @(posedge clk) begin + mul_finish <= 0; + if (!resetn) begin + mul_waiting <= 1; + end else + if (mul_waiting) begin + if (instr_rs1_signed) + rs1 <= $signed(pcpi_rs1); + else + rs1 <= $unsigned(pcpi_rs1); + + if (instr_rs2_signed) + rs2 <= $signed(pcpi_rs2); + else + rs2 <= $unsigned(pcpi_rs2); + + rd <= 0; + rdx <= 0; + mul_counter <= (instr_any_mulh ? 63 - STEPS_AT_ONCE : 31 - STEPS_AT_ONCE); + mul_waiting <= !mul_start; + end else begin + rd <= next_rd; + rdx <= next_rdx; + rs1 <= next_rs1; + rs2 <= next_rs2; + + mul_counter <= mul_counter - STEPS_AT_ONCE; + if (mul_counter[6]) begin + mul_finish <= 1; + mul_waiting <= 1; + end + end + end + + always @(posedge clk) begin + pcpi_wr <= 0; + pcpi_ready <= 0; + if (mul_finish && resetn) begin + pcpi_wr <= 1; + pcpi_ready <= 1; + pcpi_rd <= instr_any_mulh ? rd >> 32 : rd; + end + end +endmodule + +module picorv32_pcpi_fast_mul #( + parameter EXTRA_MUL_FFS = 0, + parameter EXTRA_INSN_FFS = 0, + parameter MUL_CLKGATE = 0 +) ( + input clk, resetn, + + input pcpi_valid, + input [31:0] pcpi_insn, + input [31:0] pcpi_rs1, + input [31:0] pcpi_rs2, + output pcpi_wr, + output [31:0] pcpi_rd, + output pcpi_wait, + output pcpi_ready +); + reg instr_mul, instr_mulh, instr_mulhsu, instr_mulhu; + wire instr_any_mul = |{instr_mul, instr_mulh, instr_mulhsu, instr_mulhu}; + wire instr_any_mulh = |{instr_mulh, instr_mulhsu, instr_mulhu}; + wire instr_rs1_signed = |{instr_mulh, instr_mulhsu}; + wire instr_rs2_signed = |{instr_mulh}; + + reg shift_out; + reg [3:0] active; + reg [32:0] rs1, rs2, rs1_q, rs2_q; + reg [63:0] rd, rd_q; + + wire pcpi_insn_valid = pcpi_valid && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001; + reg pcpi_insn_valid_q; + + always @* begin + instr_mul = 0; + instr_mulh = 0; + instr_mulhsu = 0; + instr_mulhu = 0; + + if (resetn && (EXTRA_INSN_FFS ? pcpi_insn_valid_q : pcpi_insn_valid)) begin + case (pcpi_insn[14:12]) + 3'b000: instr_mul = 1; + 3'b001: instr_mulh = 1; + 3'b010: instr_mulhsu = 1; + 3'b011: instr_mulhu = 1; + endcase + end + end + + always @(posedge clk) begin + pcpi_insn_valid_q <= pcpi_insn_valid; + if (!MUL_CLKGATE || active[0]) begin + rs1_q <= rs1; + rs2_q <= rs2; + end + if (!MUL_CLKGATE || active[1]) begin + rd <= $signed(EXTRA_MUL_FFS ? rs1_q : rs1) * $signed(EXTRA_MUL_FFS ? rs2_q : rs2); + end + if (!MUL_CLKGATE || active[2]) begin + rd_q <= rd; + end + end + + always @(posedge clk) begin + if (instr_any_mul && !(EXTRA_MUL_FFS ? active[3:0] : active[1:0])) begin + if (instr_rs1_signed) + rs1 <= $signed(pcpi_rs1); + else + rs1 <= $unsigned(pcpi_rs1); + + if (instr_rs2_signed) + rs2 <= $signed(pcpi_rs2); + else + rs2 <= $unsigned(pcpi_rs2); + active[0] <= 1; + end else begin + active[0] <= 0; + end + + active[3:1] <= active; + shift_out <= instr_any_mulh; + + if (!resetn) + active <= 0; + end + + assign pcpi_wr = active[EXTRA_MUL_FFS ? 3 : 1]; + assign pcpi_wait = 0; + assign pcpi_ready = active[EXTRA_MUL_FFS ? 3 : 1]; +`ifdef RISCV_FORMAL_ALTOPS + assign pcpi_rd = + instr_mul ? (pcpi_rs1 + pcpi_rs2) ^ 32'h5876063e : + instr_mulh ? (pcpi_rs1 + pcpi_rs2) ^ 32'hf6583fb7 : + instr_mulhsu ? (pcpi_rs1 - pcpi_rs2) ^ 32'hecfbe137 : + instr_mulhu ? (pcpi_rs1 + pcpi_rs2) ^ 32'h949ce5e8 : 1'bx; +`else + assign pcpi_rd = shift_out ? (EXTRA_MUL_FFS ? rd_q : rd) >> 32 : (EXTRA_MUL_FFS ? rd_q : rd); +`endif +endmodule + + +/*************************************************************** + * picorv32_pcpi_div + ***************************************************************/ + +module picorv32_pcpi_div ( + input clk, resetn, + + input pcpi_valid, + input [31:0] pcpi_insn, + input [31:0] pcpi_rs1, + input [31:0] pcpi_rs2, + output reg pcpi_wr, + output reg [31:0] pcpi_rd, + output reg pcpi_wait, + output reg pcpi_ready +); + reg instr_div, instr_divu, instr_rem, instr_remu; + wire instr_any_div_rem = |{instr_div, instr_divu, instr_rem, instr_remu}; + + reg pcpi_wait_q; + wire start = pcpi_wait && !pcpi_wait_q; + + always @(posedge clk) begin + instr_div <= 0; + instr_divu <= 0; + instr_rem <= 0; + instr_remu <= 0; + + if (resetn && pcpi_valid && !pcpi_ready && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001) begin + case (pcpi_insn[14:12]) + 3'b100: instr_div <= 1; + 3'b101: instr_divu <= 1; + 3'b110: instr_rem <= 1; + 3'b111: instr_remu <= 1; + endcase + end + + pcpi_wait <= instr_any_div_rem && resetn; + pcpi_wait_q <= pcpi_wait && resetn; + end + + reg [31:0] dividend; + reg [62:0] divisor; + reg [31:0] quotient; + reg [31:0] quotient_msk; + reg running; + reg outsign; + + always @(posedge clk) begin + pcpi_ready <= 0; + pcpi_wr <= 0; + pcpi_rd <= 'bx; + + if (!resetn) begin + running <= 0; + end else + if (start) begin + running <= 1; + dividend <= (instr_div || instr_rem) && pcpi_rs1[31] ? -pcpi_rs1 : pcpi_rs1; + divisor <= ((instr_div || instr_rem) && pcpi_rs2[31] ? -pcpi_rs2 : pcpi_rs2) << 31; + outsign <= (instr_div && (pcpi_rs1[31] != pcpi_rs2[31]) && |pcpi_rs2) || (instr_rem && pcpi_rs1[31]); + quotient <= 0; + quotient_msk <= 1 << 31; + end else + if (!quotient_msk && running) begin + running <= 0; + pcpi_ready <= 1; + pcpi_wr <= 1; +`ifdef RISCV_FORMAL_ALTOPS + case (1) + instr_div: pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h7f8529ec; + instr_divu: pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h10e8fd70; + instr_rem: pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h8da68fa5; + instr_remu: pcpi_rd <= (pcpi_rs1 - pcpi_rs2) ^ 32'h3138d0e1; + endcase +`else + if (instr_div || instr_divu) + pcpi_rd <= outsign ? -quotient : quotient; + else + pcpi_rd <= outsign ? -dividend : dividend; +`endif + end else begin + if (divisor <= dividend) begin + dividend <= dividend - divisor; + quotient <= quotient | quotient_msk; + end + divisor <= divisor >> 1; +`ifdef RISCV_FORMAL_ALTOPS + quotient_msk <= quotient_msk >> 5; +`else + quotient_msk <= quotient_msk >> 1; +`endif + end + end +endmodule + + +/*************************************************************** + * picorv32_axi + ***************************************************************/ + +module picorv32_axi #( + parameter [ 0:0] ENABLE_COUNTERS = 1, + parameter [ 0:0] ENABLE_COUNTERS64 = 1, + parameter [ 0:0] ENABLE_REGS_16_31 = 1, + parameter [ 0:0] ENABLE_REGS_DUALPORT = 1, + parameter [ 0:0] TWO_STAGE_SHIFT = 1, + parameter [ 0:0] BARREL_SHIFTER = 0, + parameter [ 0:0] TWO_CYCLE_COMPARE = 0, + parameter [ 0:0] TWO_CYCLE_ALU = 0, + parameter [ 0:0] COMPRESSED_ISA = 0, + parameter [ 0:0] CATCH_MISALIGN = 1, + parameter [ 0:0] CATCH_ILLINSN = 1, + parameter [ 0:0] ENABLE_PCPI = 0, + parameter [ 0:0] ENABLE_MUL = 0, + parameter [ 0:0] ENABLE_FAST_MUL = 0, + parameter [ 0:0] ENABLE_DIV = 0, + parameter [ 0:0] ENABLE_IRQ = 0, + parameter [ 0:0] ENABLE_IRQ_QREGS = 1, + parameter [ 0:0] ENABLE_IRQ_TIMER = 1, + parameter [ 0:0] ENABLE_TRACE = 0, + parameter [ 0:0] REGS_INIT_ZERO = 0, + parameter [31:0] MASKED_IRQ = 32'h 0000_0000, + parameter [31:0] LATCHED_IRQ = 32'h ffff_ffff, + parameter [31:0] PROGADDR_RESET = 32'h 0000_0000, + parameter [31:0] PROGADDR_IRQ = 32'h 0000_0010, + parameter [31:0] STACKADDR = 32'h ffff_ffff +) ( + input clk, resetn, + output trap, + + // AXI4-lite master memory interface + + output mem_axi_awvalid, + input mem_axi_awready, + output [31:0] mem_axi_awaddr, + output [ 2:0] mem_axi_awprot, + + output mem_axi_wvalid, + input mem_axi_wready, + output [31:0] mem_axi_wdata, + output [ 3:0] mem_axi_wstrb, + + input mem_axi_bvalid, + output mem_axi_bready, + + output mem_axi_arvalid, + input mem_axi_arready, + output [31:0] mem_axi_araddr, + output [ 2:0] mem_axi_arprot, + + input mem_axi_rvalid, + output mem_axi_rready, + input [31:0] mem_axi_rdata, + + // Pico Co-Processor Interface (PCPI) + output pcpi_valid, + output [31:0] pcpi_insn, + output [31:0] pcpi_rs1, + output [31:0] pcpi_rs2, + input pcpi_wr, + input [31:0] pcpi_rd, + input pcpi_wait, + input pcpi_ready, + + // IRQ interface + input [31:0] irq, + output [31:0] eoi, + +`ifdef RISCV_FORMAL + output rvfi_valid, + output [63:0] rvfi_order, + output [31:0] rvfi_insn, + output rvfi_trap, + output rvfi_halt, + output rvfi_intr, + output [ 4:0] rvfi_rs1_addr, + output [ 4:0] rvfi_rs2_addr, + output [31:0] rvfi_rs1_rdata, + output [31:0] rvfi_rs2_rdata, + output [ 4:0] rvfi_rd_addr, + output [31:0] rvfi_rd_wdata, + output [31:0] rvfi_pc_rdata, + output [31:0] rvfi_pc_wdata, + output [31:0] rvfi_mem_addr, + output [ 3:0] rvfi_mem_rmask, + output [ 3:0] rvfi_mem_wmask, + output [31:0] rvfi_mem_rdata, + output [31:0] rvfi_mem_wdata, +`endif + + // Trace Interface + output trace_valid, + output [35:0] trace_data +); + wire mem_valid; + wire [31:0] mem_addr; + wire [31:0] mem_wdata; + wire [ 3:0] mem_wstrb; + wire mem_instr; + wire mem_ready; + wire [31:0] mem_rdata; + + picorv32_axi_adapter axi_adapter ( + .clk (clk ), + .resetn (resetn ), + .mem_axi_awvalid(mem_axi_awvalid), + .mem_axi_awready(mem_axi_awready), + .mem_axi_awaddr (mem_axi_awaddr ), + .mem_axi_awprot (mem_axi_awprot ), + .mem_axi_wvalid (mem_axi_wvalid ), + .mem_axi_wready (mem_axi_wready ), + .mem_axi_wdata (mem_axi_wdata ), + .mem_axi_wstrb (mem_axi_wstrb ), + .mem_axi_bvalid (mem_axi_bvalid ), + .mem_axi_bready (mem_axi_bready ), + .mem_axi_arvalid(mem_axi_arvalid), + .mem_axi_arready(mem_axi_arready), + .mem_axi_araddr (mem_axi_araddr ), + .mem_axi_arprot (mem_axi_arprot ), + .mem_axi_rvalid (mem_axi_rvalid ), + .mem_axi_rready (mem_axi_rready ), + .mem_axi_rdata (mem_axi_rdata ), + .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 ) + ); + + picorv32 #( + .ENABLE_COUNTERS (ENABLE_COUNTERS ), + .ENABLE_COUNTERS64 (ENABLE_COUNTERS64 ), + .ENABLE_REGS_16_31 (ENABLE_REGS_16_31 ), + .ENABLE_REGS_DUALPORT(ENABLE_REGS_DUALPORT), + .TWO_STAGE_SHIFT (TWO_STAGE_SHIFT ), + .BARREL_SHIFTER (BARREL_SHIFTER ), + .TWO_CYCLE_COMPARE (TWO_CYCLE_COMPARE ), + .TWO_CYCLE_ALU (TWO_CYCLE_ALU ), + .COMPRESSED_ISA (COMPRESSED_ISA ), + .CATCH_MISALIGN (CATCH_MISALIGN ), + .CATCH_ILLINSN (CATCH_ILLINSN ), + .ENABLE_PCPI (ENABLE_PCPI ), + .ENABLE_MUL (ENABLE_MUL ), + .ENABLE_FAST_MUL (ENABLE_FAST_MUL ), + .ENABLE_DIV (ENABLE_DIV ), + .ENABLE_IRQ (ENABLE_IRQ ), + .ENABLE_IRQ_QREGS (ENABLE_IRQ_QREGS ), + .ENABLE_IRQ_TIMER (ENABLE_IRQ_TIMER ), + .ENABLE_TRACE (ENABLE_TRACE ), + .REGS_INIT_ZERO (REGS_INIT_ZERO ), + .MASKED_IRQ (MASKED_IRQ ), + .LATCHED_IRQ (LATCHED_IRQ ), + .PROGADDR_RESET (PROGADDR_RESET ), + .PROGADDR_IRQ (PROGADDR_IRQ ), + .STACKADDR (STACKADDR ) + ) picorv32_core ( + .clk (clk ), + .resetn (resetn), + .trap (trap ), + + .mem_valid(mem_valid), + .mem_addr (mem_addr ), + .mem_wdata(mem_wdata), + .mem_wstrb(mem_wstrb), + .mem_instr(mem_instr), + .mem_ready(mem_ready), + .mem_rdata(mem_rdata), + + .pcpi_valid(pcpi_valid), + .pcpi_insn (pcpi_insn ), + .pcpi_rs1 (pcpi_rs1 ), + .pcpi_rs2 (pcpi_rs2 ), + .pcpi_wr (pcpi_wr ), + .pcpi_rd (pcpi_rd ), + .pcpi_wait (pcpi_wait ), + .pcpi_ready(pcpi_ready), + + .irq(irq), + .eoi(eoi), + +`ifdef RISCV_FORMAL + .rvfi_valid (rvfi_valid ), + .rvfi_order (rvfi_order ), + .rvfi_insn (rvfi_insn ), + .rvfi_trap (rvfi_trap ), + .rvfi_halt (rvfi_halt ), + .rvfi_intr (rvfi_intr ), + .rvfi_rs1_addr (rvfi_rs1_addr ), + .rvfi_rs2_addr (rvfi_rs2_addr ), + .rvfi_rs1_rdata(rvfi_rs1_rdata), + .rvfi_rs2_rdata(rvfi_rs2_rdata), + .rvfi_rd_addr (rvfi_rd_addr ), + .rvfi_rd_wdata (rvfi_rd_wdata ), + .rvfi_pc_rdata (rvfi_pc_rdata ), + .rvfi_pc_wdata (rvfi_pc_wdata ), + .rvfi_mem_addr (rvfi_mem_addr ), + .rvfi_mem_rmask(rvfi_mem_rmask), + .rvfi_mem_wmask(rvfi_mem_wmask), + .rvfi_mem_rdata(rvfi_mem_rdata), + .rvfi_mem_wdata(rvfi_mem_wdata), +`endif + + .trace_valid(trace_valid), + .trace_data (trace_data) + ); +endmodule + + +/*************************************************************** + * picorv32_axi_adapter + ***************************************************************/ + +module picorv32_axi_adapter ( + input clk, resetn, + + // AXI4-lite master memory interface + + output mem_axi_awvalid, + input mem_axi_awready, + output [31:0] mem_axi_awaddr, + output [ 2:0] mem_axi_awprot, + + output mem_axi_wvalid, + input mem_axi_wready, + output [31:0] mem_axi_wdata, + output [ 3:0] mem_axi_wstrb, + + input mem_axi_bvalid, + output mem_axi_bready, + + output mem_axi_arvalid, + input mem_axi_arready, + output [31:0] mem_axi_araddr, + output [ 2:0] mem_axi_arprot, + + input mem_axi_rvalid, + output mem_axi_rready, + input [31:0] mem_axi_rdata, + + // Native PicoRV32 memory interface + + input mem_valid, + input mem_instr, + output mem_ready, + input [31:0] mem_addr, + input [31:0] mem_wdata, + input [ 3:0] mem_wstrb, + output [31:0] mem_rdata +); + reg ack_awvalid; + reg ack_arvalid; + reg ack_wvalid; + reg xfer_done; + + assign mem_axi_awvalid = mem_valid && |mem_wstrb && !ack_awvalid; + assign mem_axi_awaddr = mem_addr; + assign mem_axi_awprot = 0; + + assign mem_axi_arvalid = mem_valid && !mem_wstrb && !ack_arvalid; + assign mem_axi_araddr = mem_addr; + assign mem_axi_arprot = mem_instr ? 3'b100 : 3'b000; + + assign mem_axi_wvalid = mem_valid && |mem_wstrb && !ack_wvalid; + assign mem_axi_wdata = mem_wdata; + assign mem_axi_wstrb = mem_wstrb; + + assign mem_ready = mem_axi_bvalid || mem_axi_rvalid; + assign mem_axi_bready = mem_valid && |mem_wstrb; + assign mem_axi_rready = mem_valid && !mem_wstrb; + assign mem_rdata = mem_axi_rdata; + + always @(posedge clk) begin + if (!resetn) begin + ack_awvalid <= 0; + end else begin + xfer_done <= mem_valid && mem_ready; + if (mem_axi_awready && mem_axi_awvalid) + ack_awvalid <= 1; + if (mem_axi_arready && mem_axi_arvalid) + ack_arvalid <= 1; + if (mem_axi_wready && mem_axi_wvalid) + ack_wvalid <= 1; + if (xfer_done || !mem_valid) begin + ack_awvalid <= 0; + ack_arvalid <= 0; + ack_wvalid <= 0; + end + end + end +endmodule + + +/*************************************************************** + * picorv32_wb + ***************************************************************/ + +module picorv32_wb #( + parameter [ 0:0] ENABLE_COUNTERS = 1, + parameter [ 0:0] ENABLE_COUNTERS64 = 1, + parameter [ 0:0] ENABLE_REGS_16_31 = 1, + parameter [ 0:0] ENABLE_REGS_DUALPORT = 1, + parameter [ 0:0] TWO_STAGE_SHIFT = 1, + parameter [ 0:0] BARREL_SHIFTER = 0, + parameter [ 0:0] TWO_CYCLE_COMPARE = 0, + parameter [ 0:0] TWO_CYCLE_ALU = 0, + parameter [ 0:0] COMPRESSED_ISA = 0, + parameter [ 0:0] CATCH_MISALIGN = 1, + parameter [ 0:0] CATCH_ILLINSN = 1, + parameter [ 0:0] ENABLE_PCPI = 0, + parameter [ 0:0] ENABLE_MUL = 0, + parameter [ 0:0] ENABLE_FAST_MUL = 0, + parameter [ 0:0] ENABLE_DIV = 0, + parameter [ 0:0] ENABLE_IRQ = 0, + parameter [ 0:0] ENABLE_IRQ_QREGS = 1, + parameter [ 0:0] ENABLE_IRQ_TIMER = 1, + parameter [ 0:0] ENABLE_TRACE = 0, + parameter [ 0:0] REGS_INIT_ZERO = 0, + parameter [31:0] MASKED_IRQ = 32'h 0000_0000, + parameter [31:0] LATCHED_IRQ = 32'h ffff_ffff, + parameter [31:0] PROGADDR_RESET = 32'h 0000_0000, + parameter [31:0] PROGADDR_IRQ = 32'h 0000_0010, + parameter [31:0] STACKADDR = 32'h ffff_ffff +) ( + output trap, + + // Wishbone interfaces + input wb_rst_i, + input wb_clk_i, + + output reg [31:0] wbm_adr_o, + output reg [31:0] wbm_dat_o, + input [31:0] wbm_dat_i, + output reg wbm_we_o, + output reg [3:0] wbm_sel_o, + output reg wbm_stb_o, + input wbm_ack_i, + output reg wbm_cyc_o, + + // Pico Co-Processor Interface (PCPI) + output pcpi_valid, + output [31:0] pcpi_insn, + output [31:0] pcpi_rs1, + output [31:0] pcpi_rs2, + input pcpi_wr, + input [31:0] pcpi_rd, + input pcpi_wait, + input pcpi_ready, + + // IRQ interface + input [31:0] irq, + output [31:0] eoi, + +`ifdef RISCV_FORMAL + output rvfi_valid, + output [63:0] rvfi_order, + output [31:0] rvfi_insn, + output rvfi_trap, + output rvfi_halt, + output rvfi_intr, + output [ 4:0] rvfi_rs1_addr, + output [ 4:0] rvfi_rs2_addr, + output [31:0] rvfi_rs1_rdata, + output [31:0] rvfi_rs2_rdata, + output [ 4:0] rvfi_rd_addr, + output [31:0] rvfi_rd_wdata, + output [31:0] rvfi_pc_rdata, + output [31:0] rvfi_pc_wdata, + output [31:0] rvfi_mem_addr, + output [ 3:0] rvfi_mem_rmask, + output [ 3:0] rvfi_mem_wmask, + output [31:0] rvfi_mem_rdata, + output [31:0] rvfi_mem_wdata, +`endif + + // Trace Interface + output trace_valid, + output [35:0] trace_data, + + output mem_instr +); + wire mem_valid; + wire [31:0] mem_addr; + wire [31:0] mem_wdata; + wire [ 3:0] mem_wstrb; + reg mem_ready; + reg [31:0] mem_rdata; + + wire clk; + wire resetn; + + assign clk = wb_clk_i; + assign resetn = ~wb_rst_i; + + picorv32 #( + .ENABLE_COUNTERS (ENABLE_COUNTERS ), + .ENABLE_COUNTERS64 (ENABLE_COUNTERS64 ), + .ENABLE_REGS_16_31 (ENABLE_REGS_16_31 ), + .ENABLE_REGS_DUALPORT(ENABLE_REGS_DUALPORT), + .TWO_STAGE_SHIFT (TWO_STAGE_SHIFT ), + .BARREL_SHIFTER (BARREL_SHIFTER ), + .TWO_CYCLE_COMPARE (TWO_CYCLE_COMPARE ), + .TWO_CYCLE_ALU (TWO_CYCLE_ALU ), + .COMPRESSED_ISA (COMPRESSED_ISA ), + .CATCH_MISALIGN (CATCH_MISALIGN ), + .CATCH_ILLINSN (CATCH_ILLINSN ), + .ENABLE_PCPI (ENABLE_PCPI ), + .ENABLE_MUL (ENABLE_MUL ), + .ENABLE_FAST_MUL (ENABLE_FAST_MUL ), + .ENABLE_DIV (ENABLE_DIV ), + .ENABLE_IRQ (ENABLE_IRQ ), + .ENABLE_IRQ_QREGS (ENABLE_IRQ_QREGS ), + .ENABLE_IRQ_TIMER (ENABLE_IRQ_TIMER ), + .ENABLE_TRACE (ENABLE_TRACE ), + .REGS_INIT_ZERO (REGS_INIT_ZERO ), + .MASKED_IRQ (MASKED_IRQ ), + .LATCHED_IRQ (LATCHED_IRQ ), + .PROGADDR_RESET (PROGADDR_RESET ), + .PROGADDR_IRQ (PROGADDR_IRQ ), + .STACKADDR (STACKADDR ) + ) picorv32_core ( + .clk (clk ), + .resetn (resetn), + .trap (trap ), + + .mem_valid(mem_valid), + .mem_addr (mem_addr ), + .mem_wdata(mem_wdata), + .mem_wstrb(mem_wstrb), + .mem_instr(mem_instr), + .mem_ready(mem_ready), + .mem_rdata(mem_rdata), + + .pcpi_valid(pcpi_valid), + .pcpi_insn (pcpi_insn ), + .pcpi_rs1 (pcpi_rs1 ), + .pcpi_rs2 (pcpi_rs2 ), + .pcpi_wr (pcpi_wr ), + .pcpi_rd (pcpi_rd ), + .pcpi_wait (pcpi_wait ), + .pcpi_ready(pcpi_ready), + + .irq(irq), + .eoi(eoi), + +`ifdef RISCV_FORMAL + .rvfi_valid (rvfi_valid ), + .rvfi_order (rvfi_order ), + .rvfi_insn (rvfi_insn ), + .rvfi_trap (rvfi_trap ), + .rvfi_halt (rvfi_halt ), + .rvfi_intr (rvfi_intr ), + .rvfi_rs1_addr (rvfi_rs1_addr ), + .rvfi_rs2_addr (rvfi_rs2_addr ), + .rvfi_rs1_rdata(rvfi_rs1_rdata), + .rvfi_rs2_rdata(rvfi_rs2_rdata), + .rvfi_rd_addr (rvfi_rd_addr ), + .rvfi_rd_wdata (rvfi_rd_wdata ), + .rvfi_pc_rdata (rvfi_pc_rdata ), + .rvfi_pc_wdata (rvfi_pc_wdata ), + .rvfi_mem_addr (rvfi_mem_addr ), + .rvfi_mem_rmask(rvfi_mem_rmask), + .rvfi_mem_wmask(rvfi_mem_wmask), + .rvfi_mem_rdata(rvfi_mem_rdata), + .rvfi_mem_wdata(rvfi_mem_wdata), +`endif + + .trace_valid(trace_valid), + .trace_data (trace_data) + ); + + localparam IDLE = 2'b00; + localparam WBSTART = 2'b01; + localparam WBEND = 2'b10; + + reg [1:0] state; + + wire we; + assign we = (mem_wstrb[0] | mem_wstrb[1] | mem_wstrb[2] | mem_wstrb[3]); + + always @(posedge wb_clk_i) begin + if (wb_rst_i) begin + wbm_adr_o <= 0; + wbm_dat_o <= 0; + wbm_we_o <= 0; + wbm_sel_o <= 0; + wbm_stb_o <= 0; + wbm_cyc_o <= 0; + state <= IDLE; + end else begin + case (state) + IDLE: begin + if (mem_valid) begin + wbm_adr_o <= mem_addr; + wbm_dat_o <= mem_wdata; + wbm_we_o <= we; + wbm_sel_o <= mem_wstrb; + + wbm_stb_o <= 1'b1; + wbm_cyc_o <= 1'b1; + state <= WBSTART; + end else begin + mem_ready <= 1'b0; + + wbm_stb_o <= 1'b0; + wbm_cyc_o <= 1'b0; + wbm_we_o <= 1'b0; + end + end + WBSTART:begin + if (wbm_ack_i) begin + mem_rdata <= wbm_dat_i; + mem_ready <= 1'b1; + + state <= WBEND; + + wbm_stb_o <= 1'b0; + wbm_cyc_o <= 1'b0; + wbm_we_o <= 1'b0; + end + end + WBEND: begin + mem_ready <= 1'b0; + + state <= IDLE; + end + default: + state <= IDLE; + endcase + end + end +endmodule diff --git a/picosoc/.gitignore b/picosoc/.gitignore new file mode 100644 index 0000000..4ac8239 --- /dev/null +++ b/picosoc/.gitignore @@ -0,0 +1,29 @@ +/spiflash_tb.vcd +/spiflash_tb.vvp +/hx8kdemo.asc +/hx8kdemo.bin +/hx8kdemo.json +/hx8kdemo.log +/hx8kdemo.rpt +/hx8kdemo_syn.v +/hx8kdemo_syn_tb.vvp +/hx8kdemo_tb.vvp +/hx8kdemo_fw.elf +/hx8kdemo_fw.hex +/hx8kdemo_fw.bin +/hx8kdemo_sections.lds +/icebreaker.asc +/icebreaker.bin +/icebreaker.json +/icebreaker.log +/icebreaker.rpt +/icebreaker_syn.v +/icebreaker_syn_tb.vvp +/icebreaker_tb.vvp +/icebreaker_fw.elf +/icebreaker_fw.hex +/icebreaker_fw.bin +/icebreaker_sections.lds +/testbench.vcd +/cmos.log + diff --git a/picosoc/Makefile b/picosoc/Makefile new file mode 100644 index 0000000..291a12d --- /dev/null +++ b/picosoc/Makefile @@ -0,0 +1,123 @@ + +CROSS=riscv32-unknown-elf- +CFLAGS= + +# ---- iCE40 HX8K Breakout Board ---- + +hx8ksim: hx8kdemo_tb.vvp hx8kdemo_fw.hex + vvp -N $< +firmware=hx8kdemo_fw.hex + +hx8ksynsim: hx8kdemo_syn_tb.vvp hx8kdemo_fw.hex + vvp -N $< +firmware=hx8kdemo_fw.hex + +hx8kdemo.json: hx8kdemo.v spimemio.v simpleuart.v picosoc.v ../picorv32.v + yosys -ql hx8kdemo.log -p 'synth_ice40 -top hx8kdemo -json hx8kdemo.json' $^ + +hx8kdemo_tb.vvp: hx8kdemo_tb.v hx8kdemo.v spimemio.v simpleuart.v picosoc.v ../picorv32.v spiflash.v + iverilog -s testbench -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` -DNO_ICE40_DEFAULT_ASSIGNMENTS + +hx8kdemo_syn_tb.vvp: hx8kdemo_tb.v hx8kdemo_syn.v spiflash.v + iverilog -s testbench -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` -DNO_ICE40_DEFAULT_ASSIGNMENTS + +hx8kdemo_syn.v: hx8kdemo.json + yosys -p 'read_json hx8kdemo.json; write_verilog hx8kdemo_syn.v' + +hx8kdemo.asc: hx8kdemo.pcf hx8kdemo.json + nextpnr-ice40 --hx8k --package ct256 --asc hx8kdemo.asc --json hx8kdemo.json --pcf hx8kdemo.pcf + +hx8kdemo.bin: hx8kdemo.asc + icetime -d hx8k -c 12 -mtr hx8kdemo.rpt hx8kdemo.asc + icepack hx8kdemo.asc hx8kdemo.bin + +hx8kprog: hx8kdemo.bin hx8kdemo_fw.bin + iceprog hx8kdemo.bin + iceprog -o 1M hx8kdemo_fw.bin + +hx8kprog_fw: hx8kdemo_fw.bin + iceprog -o 1M hx8kdemo_fw.bin + +hx8kdemo_sections.lds: sections.lds + $(CROSS)cpp -P -DHX8KDEMO -o $@ $^ + +hx8kdemo_fw.elf: hx8kdemo_sections.lds start.s firmware.c + $(CROSS)gcc $(CFLAGS) -DHX8KDEMO -mabi=ilp32 -march=rv32imc -Wl,--build-id=none,-Bstatic,-T,hx8kdemo_sections.lds,--strip-debug -ffreestanding -nostdlib -o hx8kdemo_fw.elf start.s firmware.c + +hx8kdemo_fw.hex: hx8kdemo_fw.elf + $(CROSS)objcopy -O verilog hx8kdemo_fw.elf hx8kdemo_fw.hex + +hx8kdemo_fw.bin: hx8kdemo_fw.elf + $(CROSS)objcopy -O binary hx8kdemo_fw.elf hx8kdemo_fw.bin + +# ---- iCE40 IceBreaker Board ---- + +icebsim: icebreaker_tb.vvp icebreaker_fw.hex + vvp -N $< +firmware=icebreaker_fw.hex + +icebsynsim: icebreaker_syn_tb.vvp icebreaker_fw.hex + vvp -N $< +firmware=icebreaker_fw.hex + +icebreaker.json: icebreaker.v ice40up5k_spram.v spimemio.v simpleuart.v picosoc.v ../picorv32.v + yosys -ql icebreaker.log -p 'synth_ice40 -dsp -top icebreaker -json icebreaker.json' $^ + +icebreaker_tb.vvp: icebreaker_tb.v icebreaker.v ice40up5k_spram.v spimemio.v simpleuart.v picosoc.v ../picorv32.v spiflash.v + iverilog -s testbench -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` -DNO_ICE40_DEFAULT_ASSIGNMENTS + +icebreaker_syn_tb.vvp: icebreaker_tb.v icebreaker_syn.v spiflash.v + iverilog -s testbench -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` -DNO_ICE40_DEFAULT_ASSIGNMENTS + +icebreaker_syn.v: icebreaker.json + yosys -p 'read_json icebreaker.json; write_verilog icebreaker_syn.v' + +icebreaker.asc: icebreaker.pcf icebreaker.json + nextpnr-ice40 --freq 13 --up5k --package sg48 --asc icebreaker.asc --pcf icebreaker.pcf --json icebreaker.json + +icebreaker.bin: icebreaker.asc + icetime -d up5k -c 12 -mtr icebreaker.rpt icebreaker.asc + icepack icebreaker.asc icebreaker.bin + +icebprog: icebreaker.bin icebreaker_fw.bin + iceprog icebreaker.bin + iceprog -o 1M icebreaker_fw.bin + +icebprog_fw: icebreaker_fw.bin + iceprog -o 1M icebreaker_fw.bin + +icebreaker_sections.lds: sections.lds + $(CROSS)cpp -P -DICEBREAKER -o $@ $^ + +icebreaker_fw.elf: icebreaker_sections.lds start.s firmware.c + $(CROSS)gcc $(CFLAGS) -DICEBREAKER -mabi=ilp32 -march=rv32ic -Wl,-Bstatic,-T,icebreaker_sections.lds,--strip-debug -ffreestanding -nostdlib -o icebreaker_fw.elf start.s firmware.c + +icebreaker_fw.hex: icebreaker_fw.elf + $(CROSS)objcopy -O verilog icebreaker_fw.elf icebreaker_fw.hex + +icebreaker_fw.bin: icebreaker_fw.elf + $(CROSS)objcopy -O binary icebreaker_fw.elf icebreaker_fw.bin + +# ---- Testbench for SPI Flash Model ---- + +spiflash_tb: spiflash_tb.vvp icebreaker_fw.hex + vvp -N $< +firmware=icebreaker_fw.hex + +spiflash_tb.vvp: spiflash.v spiflash_tb.v + iverilog -s testbench -o $@ $^ + +# ---- ASIC Synthesis Tests ---- + +cmos.log: spimemio.v simpleuart.v picosoc.v ../picorv32.v + yosys -l cmos.log -p 'synth -top picosoc; abc -g cmos2; opt -fast; stat' $^ + +# ---- Clean ---- + +clean: + rm -f testbench.vvp testbench.vcd spiflash_tb.vvp spiflash_tb.vcd + rm -f hx8kdemo_fw.elf hx8kdemo_fw.hex hx8kdemo_fw.bin cmos.log + rm -f icebreaker_fw.elf icebreaker_fw.hex icebreaker_fw.bin + rm -f hx8kdemo.blif hx8kdemo.log hx8kdemo.asc hx8kdemo.rpt hx8kdemo.bin + rm -f hx8kdemo_syn.v hx8kdemo_syn_tb.vvp hx8kdemo_tb.vvp + rm -f icebreaker.json icebreaker.log icebreaker.asc icebreaker.rpt icebreaker.bin + rm -f icebreaker_syn.v icebreaker_syn_tb.vvp icebreaker_tb.vvp + +.PHONY: spiflash_tb clean +.PHONY: hx8kprog hx8kprog_fw hx8ksim hx8ksynsim +.PHONY: icebprog icebprog_fw icebsim icebsynsim diff --git a/picosoc/README.md b/picosoc/README.md new file mode 100644 index 0000000..1708709 --- /dev/null +++ b/picosoc/README.md @@ -0,0 +1,114 @@ + +PicoSoC - A simple example SoC using PicoRV32 +============================================= + +![](overview.svg) + +This is a simple PicoRV32 example design that can run code directly from an SPI +flash chip. It can be used as a turn-key solution for simple control tasks in +ASIC and FPGA designs. + +An example implementation targeting the Lattice iCE40-HX8K Breakout Board is +included. + +The flash is mapped to the memory regions starting at 0x00000000 and +0x01000000, with the SRAM overlayed for the mapping at 0x00000000. The SRAM +is just a small scratchpad memory (default 256 words, i.e. 1 kB). + +The reset vector is set to 0x00100000, i.e. at 1MB into in the flash memory. + +See the included demo firmware and linker script for how to build a firmware +image for this system. + +Run `make hx8ksim` or `make icebsim` to run the test bench (and create `testbench.vcd`). + +Run `make hx8kprog` to build the configuration bit-stream and firmware images +and upload them to a connected iCE40-HX8K Breakout Board. + +Run `make icebprog` to build the configuration bit-stream and firmware images +and upload them to a connected iCEBreaker Board. + +| File | Description | +| ----------------------------------- | --------------------------------------------------------------- | +| [picosoc.v](picosoc.v) | Top-level PicoSoC Verilog module | +| [spimemio.v](spimemio.v) | Memory controller that interfaces to external SPI flash | +| [simpleuart.v](simpleuart.v) | Simple UART core connected directly to SoC TX/RX lines | +| [start.s](start.s) | Assembler source for firmware.hex/firmware.bin | +| [firmware.c](firmware.c) | C source for firmware.hex/firmware.bin | +| [sections.lds](sections.lds) | Linker script for firmware.hex/firmware.bin | +| [hx8kdemo.v](hx8kdemo.v) | FPGA-based example implementation on iCE40-HX8K Breakout Board | +| [hx8kdemo.pcf](hx8kdemo.pcf) | Pin constraints for implementation on iCE40-HX8K Breakout Board | +| [hx8kdemo\_tb.v](hx8kdemo_tb.v) | Testbench for implementation on iCE40-HX8K Breakout Board | +| [icebreaker.v](icebreaker.v) | FPGA-based example implementation on iCEBreaker Board | +| [icebreaker.pcf](icebreaker.pcf) | Pin constraints for implementation on iCEBreaker Board | +| [icebreaker\_tb.v](icebreaker_tb.v) | Testbench for implementation on iCEBreaker Board | + +### Memory map: + +| Address Range | Description | +| ------------------------ | --------------------------------------- | +| 0x00000000 .. 0x00FFFFFF | Internal SRAM | +| 0x01000000 .. 0x01FFFFFF | External Serial Flash | +| 0x02000000 .. 0x02000003 | SPI Flash Controller Config Register | +| 0x02000004 .. 0x02000007 | UART Clock Divider Register | +| 0x02000008 .. 0x0200000B | UART Send/Recv Data Register | +| 0x03000000 .. 0xFFFFFFFF | Memory mapped user peripherals | + +Reading from the addresses in the internal SRAM region beyond the end of the +physical SRAM will read from the corresponding addresses in serial flash. + +Reading from the UART Send/Recv Data Register will return the last received +byte, or -1 (all 32 bits set) when the receive buffer is empty. + +The UART Clock Divider Register must be set to the system clock frequency +divided by the baud rate. + +The example design (hx8kdemo.v) has the 8 LEDs on the iCE40-HX8K Breakout Board +mapped to the low byte of the 32 bit word at address 0x03000000. + +### SPI Flash Controller Config Register: + +| Bit(s) | Description | +| -----: | --------------------------------------------------------- | +| 31 | MEMIO Enable (reset=1, set to 0 to bit bang SPI commands) | +| 30:23 | Reserved (read 0) | +| 22 | DDR Enable bit (reset=0) | +| 21 | QSPI Enable bit (reset=0) | +| 20 | CRM Enable bit (reset=0) | +| 19:16 | Read latency (dummy) cycles (reset=8) | +| 15:12 | Reserved (read 0) | +| 11:8 | IO Output enable bits in bit bang mode | +| 7:6 | Reserved (read 0) | +| 5 | Chip select (CS) line in bit bang mode | +| 4 | Serial clock line in bit bang mode | +| 3:0 | IO data bits in bit bang mode | + +The following settings for CRM/DDR/QSPI modes are valid: + +| CRM | QSPI | DDR | Read Command Byte | Mode Byte | +| :-: | :--: | :-: | :-------------------- | :-------: | +| 0 | 0 | 0 | 03h Read | N/A | +| 0 | 0 | 1 | BBh Dual I/O Read | FFh | +| 1 | 0 | 1 | BBh Dual I/O Read | A5h | +| 0 | 1 | 0 | EBh Quad I/O Read | FFh | +| 1 | 1 | 0 | EBh Quad I/O Read | A5h | +| 0 | 1 | 1 | EDh DDR Quad I/O Read | FFh | +| 1 | 1 | 1 | EDh DDR Quad I/O Read | A5h | + +The following plot visualizes the relative performance of the different configurations: + +![](performance.png) + +Consult the datasheet for your SPI flash to learn which configurations are supported +by the chip and what the maximum clock frequencies are for each configuration. + +For Quad I/O mode the QUAD flag in CR1V must be set before enabling Quad I/O in the +SPI master. Either set it by writing the corresponding bit in CR1NV once, or by writing +it from your device firmware at every bootup. (See `set_flash_qspi_flag()` in +`firmware.c` for an example for the latter.) + +Note that some changes to the Lattice iCE40-HX8K Breakout Board are required to support +the faster configurations: (1) The flash chip must be replaced with one that supports the +faster read commands and (2) the IO2 and IO3 pins on the flash chip must be connected to +the FPGA IO pins T9 and T8 (near the center of J3). + diff --git a/picosoc/firmware.c b/picosoc/firmware.c new file mode 100644 index 0000000..aadf31b --- /dev/null +++ b/picosoc/firmware.c @@ -0,0 +1,770 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include +#include + +#ifdef ICEBREAKER +# define MEM_TOTAL 0x20000 /* 128 KB */ +#elif HX8KDEMO +# define MEM_TOTAL 0x200 /* 2 KB */ +#else +# error "Set -DICEBREAKER or -DHX8KDEMO when compiling firmware.c" +#endif + +// a pointer to this is a null pointer, but the compiler does not +// know that because "sram" is a linker symbol from sections.lds. +extern uint32_t sram; + +#define reg_spictrl (*(volatile uint32_t*)0x02000000) +#define reg_uart_clkdiv (*(volatile uint32_t*)0x02000004) +#define reg_uart_data (*(volatile uint32_t*)0x02000008) +#define reg_leds (*(volatile uint32_t*)0x03000000) + +// -------------------------------------------------------- + +extern uint32_t flashio_worker_begin; +extern uint32_t flashio_worker_end; + +void flashio(uint8_t *data, int len, uint8_t wrencmd) +{ + uint32_t func[&flashio_worker_end - &flashio_worker_begin]; + + uint32_t *src_ptr = &flashio_worker_begin; + uint32_t *dst_ptr = func; + + while (src_ptr != &flashio_worker_end) + *(dst_ptr++) = *(src_ptr++); + + ((void(*)(uint8_t*, uint32_t, uint32_t))func)(data, len, wrencmd); +} + +#ifdef HX8KDEMO +void set_flash_qspi_flag() +{ + uint8_t buffer[8]; + uint32_t addr_cr1v = 0x800002; + + // Read Any Register (RDAR 65h) + buffer[0] = 0x65; + buffer[1] = addr_cr1v >> 16; + buffer[2] = addr_cr1v >> 8; + buffer[3] = addr_cr1v; + buffer[4] = 0; // dummy + buffer[5] = 0; // rdata + flashio(buffer, 6, 0); + uint8_t cr1v = buffer[5]; + + // Write Enable (WREN 06h) + Write Any Register (WRAR 71h) + buffer[0] = 0x71; + buffer[1] = addr_cr1v >> 16; + buffer[2] = addr_cr1v >> 8; + buffer[3] = addr_cr1v; + buffer[4] = cr1v | 2; // Enable QSPI + flashio(buffer, 5, 0x06); +} + +void set_flash_latency(uint8_t value) +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | ((value & 15) << 16); + + uint32_t addr = 0x800004; + uint8_t buffer_wr[5] = {0x71, addr >> 16, addr >> 8, addr, 0x70 | value}; + flashio(buffer_wr, 5, 0x06); +} + +void set_flash_mode_spi() +{ + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00000000; +} + +void set_flash_mode_dual() +{ + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00400000; +} + +void set_flash_mode_quad() +{ + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00200000; +} + +void set_flash_mode_qddr() +{ + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00600000; +} +#endif + +#ifdef ICEBREAKER +void set_flash_qspi_flag() +{ + uint8_t buffer[8]; + + // Read Configuration Registers (RDCR1 35h) + buffer[0] = 0x35; + buffer[1] = 0x00; // rdata + flashio(buffer, 2, 0); + uint8_t sr2 = buffer[1]; + + // Write Enable Volatile (50h) + Write Status Register 2 (31h) + buffer[0] = 0x31; + buffer[1] = sr2 | 2; // Enable QSPI + flashio(buffer, 2, 0x50); +} + +void set_flash_mode_spi() +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | 0x00000000; +} + +void set_flash_mode_dual() +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | 0x00400000; +} + +void set_flash_mode_quad() +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | 0x00240000; +} + +void set_flash_mode_qddr() +{ + reg_spictrl = (reg_spictrl & ~0x007f0000) | 0x00670000; +} + +void enable_flash_crm() +{ + reg_spictrl |= 0x00100000; +} +#endif + +// -------------------------------------------------------- + +void putchar(char c) +{ + if (c == '\n') + putchar('\r'); + reg_uart_data = c; +} + +void print(const char *p) +{ + while (*p) + putchar(*(p++)); +} + +void print_hex(uint32_t v, int digits) +{ + for (int i = 7; i >= 0; i--) { + char c = "0123456789abcdef"[(v >> (4*i)) & 15]; + if (c == '0' && i >= digits) continue; + putchar(c); + digits = i; + } +} + +void print_dec(uint32_t v) +{ + if (v >= 1000) { + print(">=1000"); + return; + } + + if (v >= 900) { putchar('9'); v -= 900; } + else if (v >= 800) { putchar('8'); v -= 800; } + else if (v >= 700) { putchar('7'); v -= 700; } + else if (v >= 600) { putchar('6'); v -= 600; } + else if (v >= 500) { putchar('5'); v -= 500; } + else if (v >= 400) { putchar('4'); v -= 400; } + else if (v >= 300) { putchar('3'); v -= 300; } + else if (v >= 200) { putchar('2'); v -= 200; } + else if (v >= 100) { putchar('1'); v -= 100; } + + if (v >= 90) { putchar('9'); v -= 90; } + else if (v >= 80) { putchar('8'); v -= 80; } + else if (v >= 70) { putchar('7'); v -= 70; } + else if (v >= 60) { putchar('6'); v -= 60; } + else if (v >= 50) { putchar('5'); v -= 50; } + else if (v >= 40) { putchar('4'); v -= 40; } + else if (v >= 30) { putchar('3'); v -= 30; } + else if (v >= 20) { putchar('2'); v -= 20; } + else if (v >= 10) { putchar('1'); v -= 10; } + + if (v >= 9) { putchar('9'); v -= 9; } + else if (v >= 8) { putchar('8'); v -= 8; } + else if (v >= 7) { putchar('7'); v -= 7; } + else if (v >= 6) { putchar('6'); v -= 6; } + else if (v >= 5) { putchar('5'); v -= 5; } + else if (v >= 4) { putchar('4'); v -= 4; } + else if (v >= 3) { putchar('3'); v -= 3; } + else if (v >= 2) { putchar('2'); v -= 2; } + else if (v >= 1) { putchar('1'); v -= 1; } + else putchar('0'); +} + +char getchar_prompt(char *prompt) +{ + int32_t c = -1; + + uint32_t cycles_begin, cycles_now, cycles; + __asm__ volatile ("rdcycle %0" : "=r"(cycles_begin)); + + reg_leds = ~0; + + if (prompt) + print(prompt); + + while (c == -1) { + __asm__ volatile ("rdcycle %0" : "=r"(cycles_now)); + cycles = cycles_now - cycles_begin; + if (cycles > 12000000) { + if (prompt) + print(prompt); + cycles_begin = cycles_now; + reg_leds = ~reg_leds; + } + c = reg_uart_data; + } + + reg_leds = 0; + return c; +} + +char getchar() +{ + return getchar_prompt(0); +} + +void cmd_print_spi_state() +{ + print("SPI State:\n"); + + print(" LATENCY "); + print_dec((reg_spictrl >> 16) & 15); + print("\n"); + + print(" DDR "); + if ((reg_spictrl & (1 << 22)) != 0) + print("ON\n"); + else + print("OFF\n"); + + print(" QSPI "); + if ((reg_spictrl & (1 << 21)) != 0) + print("ON\n"); + else + print("OFF\n"); + + print(" CRM "); + if ((reg_spictrl & (1 << 20)) != 0) + print("ON\n"); + else + print("OFF\n"); +} + +uint32_t xorshift32(uint32_t *state) +{ + /* Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs" */ + uint32_t x = *state; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + *state = x; + + return x; +} + +void cmd_memtest() +{ + int cyc_count = 5; + int stride = 256; + uint32_t state; + + volatile uint32_t *base_word = (uint32_t *) 0; + volatile uint8_t *base_byte = (uint8_t *) 0; + + print("Running memtest "); + + // Walk in stride increments, word access + for (int i = 1; i <= cyc_count; i++) { + state = i; + + for (int word = 0; word < MEM_TOTAL / sizeof(int); word += stride) { + *(base_word + word) = xorshift32(&state); + } + + state = i; + + for (int word = 0; word < MEM_TOTAL / sizeof(int); word += stride) { + if (*(base_word + word) != xorshift32(&state)) { + print(" ***FAILED WORD*** at "); + print_hex(4*word, 4); + print("\n"); + return; + } + } + + print("."); + } + + // Byte access + for (int byte = 0; byte < 128; byte++) { + *(base_byte + byte) = (uint8_t) byte; + } + + for (int byte = 0; byte < 128; byte++) { + if (*(base_byte + byte) != (uint8_t) byte) { + print(" ***FAILED BYTE*** at "); + print_hex(byte, 4); + print("\n"); + return; + } + } + + print(" passed\n"); +} + +// -------------------------------------------------------- + +void cmd_read_flash_id() +{ + uint8_t buffer[17] = { 0x9F, /* zeros */ }; + flashio(buffer, 17, 0); + + for (int i = 1; i <= 16; i++) { + putchar(' '); + print_hex(buffer[i], 2); + } + putchar('\n'); +} + +// -------------------------------------------------------- + +#ifdef HX8KDEMO +uint8_t cmd_read_flash_regs_print(uint32_t addr, const char *name) +{ + set_flash_latency(8); + + uint8_t buffer[6] = {0x65, addr >> 16, addr >> 8, addr, 0, 0}; + flashio(buffer, 6, 0); + + print("0x"); + print_hex(addr, 6); + print(" "); + print(name); + print(" 0x"); + print_hex(buffer[5], 2); + print("\n"); + + return buffer[5]; +} + +void cmd_read_flash_regs() +{ + print("\n"); + uint8_t sr1v = cmd_read_flash_regs_print(0x800000, "SR1V"); + uint8_t sr2v = cmd_read_flash_regs_print(0x800001, "SR2V"); + uint8_t cr1v = cmd_read_flash_regs_print(0x800002, "CR1V"); + uint8_t cr2v = cmd_read_flash_regs_print(0x800003, "CR2V"); + uint8_t cr3v = cmd_read_flash_regs_print(0x800004, "CR3V"); + uint8_t vdlp = cmd_read_flash_regs_print(0x800005, "VDLP"); +} +#endif + +#ifdef ICEBREAKER +uint8_t cmd_read_flash_reg(uint8_t cmd) +{ + uint8_t buffer[2] = {cmd, 0}; + flashio(buffer, 2, 0); + return buffer[1]; +} + +void print_reg_bit(int val, const char *name) +{ + for (int i = 0; i < 12; i++) { + if (*name == 0) + putchar(' '); + else + putchar(*(name++)); + } + + putchar(val ? '1' : '0'); + putchar('\n'); +} + +void cmd_read_flash_regs() +{ + putchar('\n'); + + uint8_t sr1 = cmd_read_flash_reg(0x05); + uint8_t sr2 = cmd_read_flash_reg(0x35); + uint8_t sr3 = cmd_read_flash_reg(0x15); + + print_reg_bit(sr1 & 0x01, "S0 (BUSY)"); + print_reg_bit(sr1 & 0x02, "S1 (WEL)"); + print_reg_bit(sr1 & 0x04, "S2 (BP0)"); + print_reg_bit(sr1 & 0x08, "S3 (BP1)"); + print_reg_bit(sr1 & 0x10, "S4 (BP2)"); + print_reg_bit(sr1 & 0x20, "S5 (TB)"); + print_reg_bit(sr1 & 0x40, "S6 (SEC)"); + print_reg_bit(sr1 & 0x80, "S7 (SRP)"); + putchar('\n'); + + print_reg_bit(sr2 & 0x01, "S8 (SRL)"); + print_reg_bit(sr2 & 0x02, "S9 (QE)"); + print_reg_bit(sr2 & 0x04, "S10 ----"); + print_reg_bit(sr2 & 0x08, "S11 (LB1)"); + print_reg_bit(sr2 & 0x10, "S12 (LB2)"); + print_reg_bit(sr2 & 0x20, "S13 (LB3)"); + print_reg_bit(sr2 & 0x40, "S14 (CMP)"); + print_reg_bit(sr2 & 0x80, "S15 (SUS)"); + putchar('\n'); + + print_reg_bit(sr3 & 0x01, "S16 ----"); + print_reg_bit(sr3 & 0x02, "S17 ----"); + print_reg_bit(sr3 & 0x04, "S18 (WPS)"); + print_reg_bit(sr3 & 0x08, "S19 ----"); + print_reg_bit(sr3 & 0x10, "S20 ----"); + print_reg_bit(sr3 & 0x20, "S21 (DRV0)"); + print_reg_bit(sr3 & 0x40, "S22 (DRV1)"); + print_reg_bit(sr3 & 0x80, "S23 (HOLD)"); + putchar('\n'); +} +#endif + +// -------------------------------------------------------- + +uint32_t cmd_benchmark(bool verbose, uint32_t *instns_p) +{ + uint8_t data[256]; + uint32_t *words = (void*)data; + + uint32_t x32 = 314159265; + + uint32_t cycles_begin, cycles_end; + uint32_t instns_begin, instns_end; + __asm__ volatile ("rdcycle %0" : "=r"(cycles_begin)); + __asm__ volatile ("rdinstret %0" : "=r"(instns_begin)); + + for (int i = 0; i < 20; i++) + { + for (int k = 0; k < 256; k++) + { + x32 ^= x32 << 13; + x32 ^= x32 >> 17; + x32 ^= x32 << 5; + data[k] = x32; + } + + for (int k = 0, p = 0; k < 256; k++) + { + if (data[k]) + data[p++] = k; + } + + for (int k = 0, p = 0; k < 64; k++) + { + x32 = x32 ^ words[k]; + } + } + + __asm__ volatile ("rdcycle %0" : "=r"(cycles_end)); + __asm__ volatile ("rdinstret %0" : "=r"(instns_end)); + + if (verbose) + { + print("Cycles: 0x"); + print_hex(cycles_end - cycles_begin, 8); + putchar('\n'); + + print("Instns: 0x"); + print_hex(instns_end - instns_begin, 8); + putchar('\n'); + + print("Chksum: 0x"); + print_hex(x32, 8); + putchar('\n'); + } + + if (instns_p) + *instns_p = instns_end - instns_begin; + + return cycles_end - cycles_begin; +} + +// -------------------------------------------------------- + +#ifdef HX8KDEMO +void cmd_benchmark_all() +{ + uint32_t instns = 0; + + print("default "); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00000000; + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + for (int i = 8; i > 0; i--) + { + print("dspi-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00400000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("dspi-crm-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00500000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("qspi-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00200000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("qspi-crm-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00300000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("qspi-ddr-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00600000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + for (int i = 8; i > 0; i--) + { + print("qspi-ddr-crm-"); + print_dec(i); + print(" "); + + set_flash_latency(i); + reg_spictrl = (reg_spictrl & ~0x00700000) | 0x00700000; + + print(": "); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + } + + print("instns : "); + print_hex(instns, 8); + putchar('\n'); +} +#endif + +#ifdef ICEBREAKER +void cmd_benchmark_all() +{ + uint32_t instns = 0; + + print("default "); + set_flash_mode_spi(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + print("dual "); + set_flash_mode_dual(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + // print("dual-crm "); + // enable_flash_crm(); + // print_hex(cmd_benchmark(false, &instns), 8); + // putchar('\n'); + + print("quad "); + set_flash_mode_quad(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + print("quad-crm "); + enable_flash_crm(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + print("qddr "); + set_flash_mode_qddr(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + + print("qddr-crm "); + enable_flash_crm(); + print_hex(cmd_benchmark(false, &instns), 8); + putchar('\n'); + +} +#endif + +void cmd_echo() +{ + print("Return to menu by sending '!'\n\n"); + char c; + while ((c = getchar()) != '!') + putchar(c); +} + +// -------------------------------------------------------- + +void main() +{ + reg_leds = 31; + reg_uart_clkdiv = 104; + print("Booting..\n"); + + reg_leds = 63; + set_flash_qspi_flag(); + + reg_leds = 127; + while (getchar_prompt("Press ENTER to continue..\n") != '\r') { /* wait */ } + + print("\n"); + print(" ____ _ ____ ____\n"); + print(" | _ \\(_) ___ ___/ ___| ___ / ___|\n"); + print(" | |_) | |/ __/ _ \\___ \\ / _ \\| |\n"); + print(" | __/| | (_| (_) |__) | (_) | |___\n"); + print(" |_| |_|\\___\\___/____/ \\___/ \\____|\n"); + print("\n"); + + print("Total memory: "); + print_dec(MEM_TOTAL / 1024); + print(" KiB\n"); + print("\n"); + + //cmd_memtest(); // test overwrites bss and data memory + print("\n"); + + cmd_print_spi_state(); + print("\n"); + + while (1) + { + print("\n"); + + print("Select an action:\n"); + print("\n"); + print(" [1] Read SPI Flash ID\n"); + print(" [2] Read SPI Config Regs\n"); + print(" [3] Switch to default mode\n"); + print(" [4] Switch to Dual I/O mode\n"); + print(" [5] Switch to Quad I/O mode\n"); + print(" [6] Switch to Quad DDR mode\n"); + print(" [7] Toggle continuous read mode\n"); + print(" [9] Run simplistic benchmark\n"); + print(" [0] Benchmark all configs\n"); + print(" [M] Run Memtest\n"); + print(" [S] Print SPI state\n"); + print(" [e] Echo UART\n"); + print("\n"); + + for (int rep = 10; rep > 0; rep--) + { + print("Command> "); + char cmd = getchar(); + if (cmd > 32 && cmd < 127) + putchar(cmd); + print("\n"); + + switch (cmd) + { + case '1': + cmd_read_flash_id(); + break; + case '2': + cmd_read_flash_regs(); + break; + case '3': + set_flash_mode_spi(); + break; + case '4': + set_flash_mode_dual(); + break; + case '5': + set_flash_mode_quad(); + break; + case '6': + set_flash_mode_qddr(); + break; + case '7': + reg_spictrl = reg_spictrl ^ 0x00100000; + break; + case '9': + cmd_benchmark(true, 0); + break; + case '0': + cmd_benchmark_all(); + break; + case 'M': + cmd_memtest(); + break; + case 'S': + cmd_print_spi_state(); + break; + case 'e': + cmd_echo(); + break; + default: + continue; + } + + break; + } + } +} diff --git a/picosoc/hx8kdemo.core b/picosoc/hx8kdemo.core new file mode 100644 index 0000000..97a1989 --- /dev/null +++ b/picosoc/hx8kdemo.core @@ -0,0 +1,35 @@ +CAPI=2: + +name : ::hx8kdemo:0 + +filesets: + hx8kdemo: + files: [hx8kdemo.v] + file_type : verilogSource + depend : [picosoc] + hx8ksim: + files: + - hx8kdemo_tb.v + file_type : verilogSource + depend : [spiflash, "yosys:techlibs:ice40"] + + constraints: + files: [hx8kdemo.pcf] + file_type : PCF + +targets: + synth: + default_tool : icestorm + filesets : [constraints, hx8kdemo] + tools: + icestorm: + arachne_pnr_options : [-d, 8k] + toplevel : [hx8kdemo] + sim: + default_tool : icarus + filesets : [hx8kdemo, hx8ksim] + tools: + xsim: + xelab_options : [--timescale, 1ns/1ps] + + toplevel : [testbench] diff --git a/picosoc/hx8kdemo.pcf b/picosoc/hx8kdemo.pcf new file mode 100644 index 0000000..418bae8 --- /dev/null +++ b/picosoc/hx8kdemo.pcf @@ -0,0 +1,40 @@ + +# Pinout for the iCE40-HX8K Breakout Board + +set_io clk J3 + +set_io flash_csb R12 +set_io flash_clk R11 +set_io flash_io0 P12 +set_io flash_io1 P11 + +# for QSPI mode the flash chip on the iCE40-HX8K Breakout Board +# must be replaced with one that supports QSPI and the IO2 and IO3 +# pins must be soldered to T9 and P8 (center on J3) +set_io flash_io2 T9 +set_io flash_io3 P8 + +set_io ser_tx B12 +set_io ser_rx B10 + +# left on J3 +set_io debug_ser_tx T1 +set_io debug_ser_rx R3 + +# right on J3 +set_io debug_flash_csb T15 +set_io debug_flash_clk R16 +set_io debug_flash_io0 N12 +set_io debug_flash_io1 P13 +set_io debug_flash_io2 T13 +set_io debug_flash_io3 T14 + +set_io leds[7] B5 # D9 +set_io leds[6] B4 # D8 +set_io leds[5] A2 # D7 +set_io leds[4] A1 # D6 +set_io leds[3] C5 # D5 +set_io leds[2] C4 # D4 +set_io leds[1] B3 # D3 +set_io leds[0] C3 # D2 + diff --git a/picosoc/hx8kdemo.v b/picosoc/hx8kdemo.v new file mode 100644 index 0000000..f9a82f1 --- /dev/null +++ b/picosoc/hx8kdemo.v @@ -0,0 +1,139 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +module hx8kdemo ( + input clk, + + output ser_tx, + input ser_rx, + + output [7:0] leds, + + output flash_csb, + output flash_clk, + inout flash_io0, + inout flash_io1, + inout flash_io2, + inout flash_io3, + + output debug_ser_tx, + output debug_ser_rx, + + output debug_flash_csb, + output debug_flash_clk, + output debug_flash_io0, + output debug_flash_io1, + output debug_flash_io2, + output debug_flash_io3 +); + reg [5:0] reset_cnt = 0; + wire resetn = &reset_cnt; + + always @(posedge clk) begin + reset_cnt <= reset_cnt + !resetn; + end + + wire flash_io0_oe, flash_io0_do, flash_io0_di; + wire flash_io1_oe, flash_io1_do, flash_io1_di; + wire flash_io2_oe, flash_io2_do, flash_io2_di; + wire flash_io3_oe, flash_io3_do, flash_io3_di; + + SB_IO #( + .PIN_TYPE(6'b 1010_01), + .PULLUP(1'b 0) + ) flash_io_buf [3:0] ( + .PACKAGE_PIN({flash_io3, flash_io2, flash_io1, flash_io0}), + .OUTPUT_ENABLE({flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}), + .D_OUT_0({flash_io3_do, flash_io2_do, flash_io1_do, flash_io0_do}), + .D_IN_0({flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}) + ); + + wire iomem_valid; + reg iomem_ready; + wire [3:0] iomem_wstrb; + wire [31:0] iomem_addr; + wire [31:0] iomem_wdata; + reg [31:0] iomem_rdata; + + reg [31:0] gpio; + assign leds = gpio; + + always @(posedge clk) begin + if (!resetn) begin + gpio <= 0; + end else begin + iomem_ready <= 0; + if (iomem_valid && !iomem_ready && iomem_addr[31:24] == 8'h 03) begin + iomem_ready <= 1; + iomem_rdata <= gpio; + if (iomem_wstrb[0]) gpio[ 7: 0] <= iomem_wdata[ 7: 0]; + if (iomem_wstrb[1]) gpio[15: 8] <= iomem_wdata[15: 8]; + if (iomem_wstrb[2]) gpio[23:16] <= iomem_wdata[23:16]; + if (iomem_wstrb[3]) gpio[31:24] <= iomem_wdata[31:24]; + end + end + end + + picosoc soc ( + .clk (clk ), + .resetn (resetn ), + + .ser_tx (ser_tx ), + .ser_rx (ser_rx ), + + .flash_csb (flash_csb ), + .flash_clk (flash_clk ), + + .flash_io0_oe (flash_io0_oe), + .flash_io1_oe (flash_io1_oe), + .flash_io2_oe (flash_io2_oe), + .flash_io3_oe (flash_io3_oe), + + .flash_io0_do (flash_io0_do), + .flash_io1_do (flash_io1_do), + .flash_io2_do (flash_io2_do), + .flash_io3_do (flash_io3_do), + + .flash_io0_di (flash_io0_di), + .flash_io1_di (flash_io1_di), + .flash_io2_di (flash_io2_di), + .flash_io3_di (flash_io3_di), + + .irq_5 (1'b0 ), + .irq_6 (1'b0 ), + .irq_7 (1'b0 ), + + .iomem_valid (iomem_valid ), + .iomem_ready (iomem_ready ), + .iomem_wstrb (iomem_wstrb ), + .iomem_addr (iomem_addr ), + .iomem_wdata (iomem_wdata ), + .iomem_rdata (iomem_rdata ) + ); + + assign debug_ser_tx = ser_tx; + assign debug_ser_rx = ser_rx; + + assign debug_flash_csb = flash_csb; + assign debug_flash_clk = flash_clk; + assign debug_flash_io0 = flash_io0_di; + assign debug_flash_io1 = flash_io1_di; + assign debug_flash_io2 = flash_io2_di; + assign debug_flash_io3 = flash_io3_di; +endmodule diff --git a/picosoc/hx8kdemo_tb.v b/picosoc/hx8kdemo_tb.v new file mode 100644 index 0000000..3fe9c1d --- /dev/null +++ b/picosoc/hx8kdemo_tb.v @@ -0,0 +1,108 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +`timescale 1 ns / 1 ps + +module testbench; + reg clk; + always #5 clk = (clk === 1'b0); + + localparam ser_half_period = 53; + event ser_sample; + + initial begin + $dumpfile("testbench.vcd"); + $dumpvars(0, testbench); + + repeat (6) begin + repeat (50000) @(posedge clk); + $display("+50000 cycles"); + end + $finish; + end + + integer cycle_cnt = 0; + + always @(posedge clk) begin + cycle_cnt <= cycle_cnt + 1; + end + + wire [7:0] leds; + + wire ser_rx; + wire ser_tx; + + wire flash_csb; + wire flash_clk; + wire flash_io0; + wire flash_io1; + wire flash_io2; + wire flash_io3; + + always @(leds) begin + #1 $display("%b", leds); + end + + hx8kdemo uut ( + .clk (clk ), + .leds (leds ), + .ser_rx (ser_rx ), + .ser_tx (ser_tx ), + .flash_csb(flash_csb), + .flash_clk(flash_clk), + .flash_io0(flash_io0), + .flash_io1(flash_io1), + .flash_io2(flash_io2), + .flash_io3(flash_io3) + ); + + spiflash spiflash ( + .csb(flash_csb), + .clk(flash_clk), + .io0(flash_io0), + .io1(flash_io1), + .io2(flash_io2), + .io3(flash_io3) + ); + + reg [7:0] buffer; + + always begin + @(negedge ser_tx); + + repeat (ser_half_period) @(posedge clk); + -> ser_sample; // start bit + + repeat (8) begin + repeat (ser_half_period) @(posedge clk); + repeat (ser_half_period) @(posedge clk); + buffer = {ser_tx, buffer[7:1]}; + -> ser_sample; // data bit + end + + repeat (ser_half_period) @(posedge clk); + repeat (ser_half_period) @(posedge clk); + -> ser_sample; // stop bit + + if (buffer < 32 || buffer >= 127) + $display("Serial data: %d", buffer); + else + $display("Serial data: '%c'", buffer); + end +endmodule diff --git a/picosoc/ice40up5k_spram.v b/picosoc/ice40up5k_spram.v new file mode 100644 index 0000000..6d010f2 --- /dev/null +++ b/picosoc/ice40up5k_spram.v @@ -0,0 +1,91 @@ + +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +module ice40up5k_spram #( + // We current always use the whole SPRAM (128 kB) + parameter integer WORDS = 32768 +) ( + input clk, + input [3:0] wen, + input [21:0] addr, + input [31:0] wdata, + output [31:0] rdata +); + + wire cs_0, cs_1; + wire [31:0] rdata_0, rdata_1; + + assign cs_0 = !addr[14]; + assign cs_1 = addr[14]; + assign rdata = addr[14] ? rdata_1 : rdata_0; + + SB_SPRAM256KA ram00 ( + .ADDRESS(addr[13:0]), + .DATAIN(wdata[15:0]), + .MASKWREN({wen[1], wen[1], wen[0], wen[0]}), + .WREN(wen[1]|wen[0]), + .CHIPSELECT(cs_0), + .CLOCK(clk), + .STANDBY(1'b0), + .SLEEP(1'b0), + .POWEROFF(1'b1), + .DATAOUT(rdata_0[15:0]) + ); + + SB_SPRAM256KA ram01 ( + .ADDRESS(addr[13:0]), + .DATAIN(wdata[31:16]), + .MASKWREN({wen[3], wen[3], wen[2], wen[2]}), + .WREN(wen[3]|wen[2]), + .CHIPSELECT(cs_0), + .CLOCK(clk), + .STANDBY(1'b0), + .SLEEP(1'b0), + .POWEROFF(1'b1), + .DATAOUT(rdata_0[31:16]) + ); + + SB_SPRAM256KA ram10 ( + .ADDRESS(addr[13:0]), + .DATAIN(wdata[15:0]), + .MASKWREN({wen[1], wen[1], wen[0], wen[0]}), + .WREN(wen[1]|wen[0]), + .CHIPSELECT(cs_1), + .CLOCK(clk), + .STANDBY(1'b0), + .SLEEP(1'b0), + .POWEROFF(1'b1), + .DATAOUT(rdata_1[15:0]) + ); + + SB_SPRAM256KA ram11 ( + .ADDRESS(addr[13:0]), + .DATAIN(wdata[31:16]), + .MASKWREN({wen[3], wen[3], wen[2], wen[2]}), + .WREN(wen[3]|wen[2]), + .CHIPSELECT(cs_1), + .CLOCK(clk), + .STANDBY(1'b0), + .SLEEP(1'b0), + .POWEROFF(1'b1), + .DATAOUT(rdata_1[31:16]) + ); + +endmodule diff --git a/picosoc/icebreaker.core b/picosoc/icebreaker.core new file mode 100644 index 0000000..5af5fd6 --- /dev/null +++ b/picosoc/icebreaker.core @@ -0,0 +1,36 @@ +CAPI=2: + +name : ::icebreaker:0 + +filesets: + top: + files: [icebreaker.v] + file_type : verilogSource + depend : [picosoc] + tb: + files: + - icebreaker_tb.v + file_type : verilogSource + depend : [spiflash, "yosys:techlibs:ice40"] + + constraints: + files: [icebreaker.pcf] + file_type : PCF + +targets: + synth: + default_tool : icestorm + filesets : [constraints, top] + tools: + icestorm: + nextpnr_options : [--freq, 13, --up5k] + pnr : next + toplevel : [icebreaker] + sim: + default_tool : icarus + filesets : [top, tb] + tools: + xsim: + xelab_options : [--timescale, 1ns/1ps] + + toplevel : [testbench] diff --git a/picosoc/icebreaker.pcf b/picosoc/icebreaker.pcf new file mode 100644 index 0000000..86cf78e --- /dev/null +++ b/picosoc/icebreaker.pcf @@ -0,0 +1,25 @@ +# 12 MHz clock +set_io clk 35 + +# RS232 +set_io ser_rx 6 +set_io ser_tx 9 + +# SPI Flash +set_io flash_clk 15 +set_io flash_csb 16 +set_io flash_io0 14 +set_io flash_io1 17 +set_io flash_io2 12 +set_io flash_io3 13 + +# LEDs (PMOD 2) +set_io led1 27 +set_io led2 25 +set_io led3 21 +set_io led4 23 +set_io led5 26 + +# Onboard LEDs +set_io ledr_n 11 +set_io ledg_n 37 diff --git a/picosoc/icebreaker.v b/picosoc/icebreaker.v new file mode 100644 index 0000000..a943bc6 --- /dev/null +++ b/picosoc/icebreaker.v @@ -0,0 +1,151 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +`ifdef PICOSOC_V +`error "icebreaker.v must be read before picosoc.v!" +`endif + +`define PICOSOC_MEM ice40up5k_spram + +module icebreaker ( + input clk, + + output ser_tx, + input ser_rx, + + output led1, + output led2, + output led3, + output led4, + output led5, + + output ledr_n, + output ledg_n, + + output flash_csb, + output flash_clk, + inout flash_io0, + inout flash_io1, + inout flash_io2, + inout flash_io3 +); + parameter integer MEM_WORDS = 32768; + + reg [5:0] reset_cnt = 0; + wire resetn = &reset_cnt; + + always @(posedge clk) begin + reset_cnt <= reset_cnt + !resetn; + end + + wire [7:0] leds; + + assign led1 = leds[1]; + assign led2 = leds[2]; + assign led3 = leds[3]; + assign led4 = leds[4]; + assign led5 = leds[5]; + + assign ledr_n = !leds[6]; + assign ledg_n = !leds[7]; + + wire flash_io0_oe, flash_io0_do, flash_io0_di; + wire flash_io1_oe, flash_io1_do, flash_io1_di; + wire flash_io2_oe, flash_io2_do, flash_io2_di; + wire flash_io3_oe, flash_io3_do, flash_io3_di; + + SB_IO #( + .PIN_TYPE(6'b 1010_01), + .PULLUP(1'b 0) + ) flash_io_buf [3:0] ( + .PACKAGE_PIN({flash_io3, flash_io2, flash_io1, flash_io0}), + .OUTPUT_ENABLE({flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}), + .D_OUT_0({flash_io3_do, flash_io2_do, flash_io1_do, flash_io0_do}), + .D_IN_0({flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}) + ); + + wire iomem_valid; + reg iomem_ready; + wire [3:0] iomem_wstrb; + wire [31:0] iomem_addr; + wire [31:0] iomem_wdata; + reg [31:0] iomem_rdata; + + reg [31:0] gpio; + assign leds = gpio; + + always @(posedge clk) begin + if (!resetn) begin + gpio <= 0; + end else begin + iomem_ready <= 0; + if (iomem_valid && !iomem_ready && iomem_addr[31:24] == 8'h 03) begin + iomem_ready <= 1; + iomem_rdata <= gpio; + if (iomem_wstrb[0]) gpio[ 7: 0] <= iomem_wdata[ 7: 0]; + if (iomem_wstrb[1]) gpio[15: 8] <= iomem_wdata[15: 8]; + if (iomem_wstrb[2]) gpio[23:16] <= iomem_wdata[23:16]; + if (iomem_wstrb[3]) gpio[31:24] <= iomem_wdata[31:24]; + end + end + end + + picosoc #( + .BARREL_SHIFTER(0), + .ENABLE_MUL(0), + .ENABLE_DIV(0), + .ENABLE_FAST_MUL(1), + .MEM_WORDS(MEM_WORDS) + ) soc ( + .clk (clk ), + .resetn (resetn ), + + .ser_tx (ser_tx ), + .ser_rx (ser_rx ), + + .flash_csb (flash_csb ), + .flash_clk (flash_clk ), + + .flash_io0_oe (flash_io0_oe), + .flash_io1_oe (flash_io1_oe), + .flash_io2_oe (flash_io2_oe), + .flash_io3_oe (flash_io3_oe), + + .flash_io0_do (flash_io0_do), + .flash_io1_do (flash_io1_do), + .flash_io2_do (flash_io2_do), + .flash_io3_do (flash_io3_do), + + .flash_io0_di (flash_io0_di), + .flash_io1_di (flash_io1_di), + .flash_io2_di (flash_io2_di), + .flash_io3_di (flash_io3_di), + + .irq_5 (1'b0 ), + .irq_6 (1'b0 ), + .irq_7 (1'b0 ), + + .iomem_valid (iomem_valid ), + .iomem_ready (iomem_ready ), + .iomem_wstrb (iomem_wstrb ), + .iomem_addr (iomem_addr ), + .iomem_wdata (iomem_wdata ), + .iomem_rdata (iomem_rdata ) + ); +endmodule diff --git a/picosoc/icebreaker_tb.v b/picosoc/icebreaker_tb.v new file mode 100644 index 0000000..c09c10d --- /dev/null +++ b/picosoc/icebreaker_tb.v @@ -0,0 +1,122 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +`timescale 1 ns / 1 ps + +module testbench; + reg clk; + always #5 clk = (clk === 1'b0); + + localparam ser_half_period = 53; + event ser_sample; + + initial begin + $dumpfile("testbench.vcd"); + $dumpvars(0, testbench); + + repeat (6) begin + repeat (50000) @(posedge clk); + $display("+50000 cycles"); + end + $finish; + end + + integer cycle_cnt = 0; + + always @(posedge clk) begin + cycle_cnt <= cycle_cnt + 1; + end + + wire led1, led2, led3, led4, led5; + wire ledr_n, ledg_n; + + wire [6:0] leds = {!ledg_n, !ledr_n, led5, led4, led3, led2, led1}; + + wire ser_rx; + wire ser_tx; + + wire flash_csb; + wire flash_clk; + wire flash_io0; + wire flash_io1; + wire flash_io2; + wire flash_io3; + + always @(leds) begin + #1 $display("%b", leds); + end + + icebreaker #( + // We limit the amount of memory in simulation + // in order to avoid reduce simulation time + // required for intialization of RAM + .MEM_WORDS(256) + ) uut ( + .clk (clk ), + .led1 (led1 ), + .led2 (led2 ), + .led3 (led3 ), + .led4 (led4 ), + .led5 (led5 ), + .ledr_n (ledr_n ), + .ledg_n (ledg_n ), + .ser_rx (ser_rx ), + .ser_tx (ser_tx ), + .flash_csb(flash_csb), + .flash_clk(flash_clk), + .flash_io0(flash_io0), + .flash_io1(flash_io1), + .flash_io2(flash_io2), + .flash_io3(flash_io3) + ); + + spiflash spiflash ( + .csb(flash_csb), + .clk(flash_clk), + .io0(flash_io0), + .io1(flash_io1), + .io2(flash_io2), + .io3(flash_io3) + ); + + reg [7:0] buffer; + + always begin + @(negedge ser_tx); + + repeat (ser_half_period) @(posedge clk); + -> ser_sample; // start bit + + repeat (8) begin + repeat (ser_half_period) @(posedge clk); + repeat (ser_half_period) @(posedge clk); + buffer = {ser_tx, buffer[7:1]}; + -> ser_sample; // data bit + end + + repeat (ser_half_period) @(posedge clk); + repeat (ser_half_period) @(posedge clk); + -> ser_sample; // stop bit + + if (buffer < 32 || buffer >= 127) + $display("Serial data: %d", buffer); + else + $display("Serial data: '%c'", buffer); + end +endmodule diff --git a/picosoc/overview.svg b/picosoc/overview.svg new file mode 100644 index 0000000..2335f76 --- /dev/null +++ b/picosoc/overview.svg @@ -0,0 +1,757 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + Your Chip + + PicoSoC + + XIP SPIFlashController + + UART + + SRAM(default 1kB) + 32 Bit System Bus + + PicoRV32 + + + + + + + + + + + + + FLASH_SCK + FLASH_CSB + FLASH_IO0 + FLASH_IO1 + FLASH_IO2 + FLASH_IO3 + SERIAL_TX + SERIAL_RX + + + + + + Your Circuit + SYSTEM_CLOCK + SYSTEM_RESET + + + GPIO Controller + + Custom Block + + Custom Block + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - RISC-V ISA- RV32IMC- GNU Toolchain + + + + + + + + + + + + + + + + + + + + diff --git a/picosoc/performance.png b/picosoc/performance.png new file mode 100644 index 0000000..aac75b2 Binary files /dev/null and b/picosoc/performance.png differ diff --git a/picosoc/performance.py b/picosoc/performance.py new file mode 100644 index 0000000..92c50c5 --- /dev/null +++ b/picosoc/performance.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 + +import matplotlib.pyplot as plt +import numpy as np + +uncompr_text = """ +default : 010f52ef +dspi-8 : 008dc82f +dspi-7 : 008d6d63 +dspi-6 : 008d1297 +dspi-5 : 008cb7cb +dspi-4 : 008c5cff +dspi-3 : 008c0233 +dspi-2 : 008ba767 +dspi-1 : 008b4c9b +dspi-crm-8 : 008af1cf +dspi-crm-7 : 008a9703 +dspi-crm-6 : 008a3c37 +dspi-crm-5 : 0089e16b +dspi-crm-4 : 0089869f +dspi-crm-3 : 00892bd3 +dspi-crm-2 : 0088d107 +dspi-crm-1 : 0088763b +qspi-8 : 004a2c6f +qspi-7 : 0049d1a3 +qspi-6 : 004976d7 +qspi-5 : 00491c0b +qspi-4 : 0048c13f +qspi-3 : 00486673 +qspi-2 : 00480ba7 +qspi-1 : 0047b0db +qspi-crm-8 : 0047560f +qspi-crm-7 : 0046fb43 +qspi-crm-6 : 0046a077 +qspi-crm-5 : 004645ab +qspi-crm-4 : 0045eadf +qspi-crm-3 : 00459013 +qspi-crm-2 : 00453547 +qspi-crm-1 : 0044da7b +qspi-ddr-8 : 00288bf5 +qspi-ddr-7 : 00283129 +qspi-ddr-6 : 0027d65d +qspi-ddr-5 : 00277b91 +qspi-ddr-4 : 002720c5 +qspi-ddr-3 : 0026c5f9 +qspi-ddr-2 : 00266b2d +qspi-ddr-1 : 00261061 +qspi-ddr-crm-8 : 0025b595 +qspi-ddr-crm-7 : 00255ac9 +qspi-ddr-crm-6 : 0024fffd +qspi-ddr-crm-5 : 0024a531 +qspi-ddr-crm-4 : 00244a65 +qspi-ddr-crm-3 : 0023ef99 +qspi-ddr-crm-2 : 002394cd +qspi-ddr-crm-1 : 00233a01 +instns : 0003df2d +""" + +compr_text = """ +default : 00f3d36d +dspi-8 : 008008ad +dspi-7 : 007fade1 +dspi-6 : 007f5315 +dspi-5 : 007ef849 +dspi-4 : 007e9d7d +dspi-3 : 007e42b1 +dspi-2 : 007de7e5 +dspi-1 : 007d8d19 +dspi-crm-8 : 007d324d +dspi-crm-7 : 007cd781 +dspi-crm-6 : 007c7cb5 +dspi-crm-5 : 007c21e9 +dspi-crm-4 : 007bc71d +dspi-crm-3 : 007b6c51 +dspi-crm-2 : 007b1185 +dspi-crm-1 : 007ab6b9 +qspi-8 : 00434ced +qspi-7 : 0042f221 +qspi-6 : 00429755 +qspi-5 : 00423c89 +qspi-4 : 0041e1bd +qspi-3 : 004186f1 +qspi-2 : 00412c25 +qspi-1 : 0040d159 +qspi-crm-8 : 0040768d +qspi-crm-7 : 00401bc1 +qspi-crm-6 : 003fc0f5 +qspi-crm-5 : 003f6629 +qspi-crm-4 : 003f0b5d +qspi-crm-3 : 003eb091 +qspi-crm-2 : 003e55c5 +qspi-crm-1 : 003dfaf9 +qspi-ddr-8 : 00255d87 +qspi-ddr-7 : 002502bb +qspi-ddr-6 : 0024a7ef +qspi-ddr-5 : 00244d23 +qspi-ddr-4 : 0023f257 +qspi-ddr-3 : 0023978b +qspi-ddr-2 : 00233cbf +qspi-ddr-1 : 0022e1f3 +qspi-ddr-crm-8 : 00228727 +qspi-ddr-crm-7 : 00222c5b +qspi-ddr-crm-6 : 0021d18f +qspi-ddr-crm-5 : 002176c3 +qspi-ddr-crm-4 : 00211bf7 +qspi-ddr-crm-3 : 0020c12b +qspi-ddr-crm-2 : 0020665f +qspi-ddr-crm-1 : 00200b93 +instns : 0003df2d +""" + +labels = list() +uncompr_values = list() +compr_values = list() + +for line in uncompr_text.split("\n"): + if line != "": + line = line.split() + if line[0] == "instns": + for i in range(len(uncompr_values)): + uncompr_values[i] = int(line[2], 16) / uncompr_values[i] + else: + labels.append(line[0]) + uncompr_values.append(int(line[2], 16)) + +for line in compr_text.split("\n"): + if line != "": + line = line.split() + if line[0] == "instns": + for i in range(len(compr_values)): + compr_values[i] = int(line[2], 16) / compr_values[i] + else: + compr_values.append(int(line[2], 16)) + +print(np.array(compr_values) / np.array(uncompr_values)) + +values = list() +for i in range(len(compr_values)): + values.append(uncompr_values[i] / uncompr_values[0]) + # values.append(compr_values[i] / compr_values[0]) + +values = np.array(values) +print(values) + +plt.figure(figsize=(10, 5)) +plt.title("Performance comparison for different PicoSoC SPI flash configurations") +plt.plot(range(len(labels)), values) +plt.xticks(range(len(labels)), labels, rotation=80) + +for color, x1, x2 in [["black", 0, 0], ["red", 1, 8], ["green", 9, 16], + ["red", 17, 24], ["green", 25, 32], ["red", 33, 40], ["green", 41, 48]]: + for t in plt.axes().xaxis.get_ticklabels()[x1:x2+1]: + t.set_color(color) + plt.plot([x1, x1], [0, values[x1] - 0.2], color=color) + plt.plot([x2, x2], [0, values[x2] - 0.2], color=color) + plt.plot([x1], [values[x1]], "k.") + plt.plot([x2], [values[x2]], "k.") + +plt.xlim(-1, len(labels)) +plt.ylim(0, 9) +plt.grid() + +plt.gcf().subplots_adjust(bottom=0.3) +plt.savefig("performance.png") +# plt.show() diff --git a/picosoc/picosoc.core b/picosoc/picosoc.core new file mode 100644 index 0000000..eb0988a --- /dev/null +++ b/picosoc/picosoc.core @@ -0,0 +1,27 @@ +CAPI=2: + +name : ::picosoc:0 + +filesets: + picosoc: + files: + - simpleuart.v + - spimemio.v + - picosoc.v + file_type : verilogSource + depend : [picorv32] + +targets: + default: + filesets : [picosoc] + parameters : [PICORV32_REGS, PICOSOC_MEM] + +parameters: + PICORV32_REGS: + datatype : str + default : picosoc_regs + paramtype : vlogdefine + PICOSOC_MEM: + datatype : str + default : picosoc_mem + paramtype : vlogdefine diff --git a/picosoc/picosoc.v b/picosoc/picosoc.v new file mode 100644 index 0000000..9790791 --- /dev/null +++ b/picosoc/picosoc.v @@ -0,0 +1,262 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +`ifndef PICORV32_REGS +`ifdef PICORV32_V +`error "picosoc.v must be read before picorv32.v!" +`endif + +`define PICORV32_REGS picosoc_regs +`endif + +`ifndef PICOSOC_MEM +`define PICOSOC_MEM picosoc_mem +`endif + +// this macro can be used to check if the verilog files in your +// design are read in the correct order. +`define PICOSOC_V + +module picosoc ( + input clk, + input resetn, + + output iomem_valid, + input iomem_ready, + output [ 3:0] iomem_wstrb, + output [31:0] iomem_addr, + output [31:0] iomem_wdata, + input [31:0] iomem_rdata, + + input irq_5, + input irq_6, + input irq_7, + + output ser_tx, + input ser_rx, + + output flash_csb, + output flash_clk, + + output flash_io0_oe, + output flash_io1_oe, + output flash_io2_oe, + output flash_io3_oe, + + output flash_io0_do, + output flash_io1_do, + output flash_io2_do, + output flash_io3_do, + + input flash_io0_di, + input flash_io1_di, + input flash_io2_di, + input flash_io3_di +); + parameter [0:0] BARREL_SHIFTER = 1; + parameter [0:0] ENABLE_MUL = 1; + parameter [0:0] ENABLE_DIV = 1; + parameter [0:0] ENABLE_FAST_MUL = 0; + parameter [0:0] ENABLE_COMPRESSED = 1; + parameter [0:0] ENABLE_COUNTERS = 1; + parameter [0:0] ENABLE_IRQ_QREGS = 0; + + parameter integer MEM_WORDS = 256; + parameter [31:0] STACKADDR = (4*MEM_WORDS); // end of memory + parameter [31:0] PROGADDR_RESET = 32'h 0010_0000; // 1 MB into flash + parameter [31:0] PROGADDR_IRQ = 32'h 0000_0000; + + reg [31:0] irq; + wire irq_stall = 0; + wire irq_uart = 0; + + always @* begin + irq = 0; + irq[3] = irq_stall; + irq[4] = irq_uart; + irq[5] = irq_5; + irq[6] = irq_6; + irq[7] = irq_7; + end + + wire mem_valid; + wire mem_instr; + wire mem_ready; + wire [31:0] mem_addr; + wire [31:0] mem_wdata; + wire [3:0] mem_wstrb; + wire [31:0] mem_rdata; + + wire spimem_ready; + wire [31:0] spimem_rdata; + + reg ram_ready; + wire [31:0] ram_rdata; + + assign iomem_valid = mem_valid && (mem_addr[31:24] > 8'h 01); + assign iomem_wstrb = mem_wstrb; + assign iomem_addr = mem_addr; + assign iomem_wdata = mem_wdata; + + wire spimemio_cfgreg_sel = mem_valid && (mem_addr == 32'h 0200_0000); + wire [31:0] spimemio_cfgreg_do; + + wire simpleuart_reg_div_sel = mem_valid && (mem_addr == 32'h 0200_0004); + wire [31:0] simpleuart_reg_div_do; + + wire simpleuart_reg_dat_sel = mem_valid && (mem_addr == 32'h 0200_0008); + wire [31:0] simpleuart_reg_dat_do; + wire simpleuart_reg_dat_wait; + + assign mem_ready = (iomem_valid && iomem_ready) || spimem_ready || ram_ready || spimemio_cfgreg_sel || + simpleuart_reg_div_sel || (simpleuart_reg_dat_sel && !simpleuart_reg_dat_wait); + + assign mem_rdata = (iomem_valid && iomem_ready) ? iomem_rdata : spimem_ready ? spimem_rdata : ram_ready ? ram_rdata : + spimemio_cfgreg_sel ? spimemio_cfgreg_do : simpleuart_reg_div_sel ? simpleuart_reg_div_do : + simpleuart_reg_dat_sel ? simpleuart_reg_dat_do : 32'h 0000_0000; + + picorv32 #( + .STACKADDR(STACKADDR), + .PROGADDR_RESET(PROGADDR_RESET), + .PROGADDR_IRQ(PROGADDR_IRQ), + .BARREL_SHIFTER(BARREL_SHIFTER), + .COMPRESSED_ISA(ENABLE_COMPRESSED), + .ENABLE_COUNTERS(ENABLE_COUNTERS), + .ENABLE_MUL(ENABLE_MUL), + .ENABLE_DIV(ENABLE_DIV), + .ENABLE_FAST_MUL(ENABLE_FAST_MUL), + .ENABLE_IRQ(1), + .ENABLE_IRQ_QREGS(ENABLE_IRQ_QREGS) + ) cpu ( + .clk (clk ), + .resetn (resetn ), + .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 ), + .irq (irq ) + ); + + spimemio spimemio ( + .clk (clk), + .resetn (resetn), + .valid (mem_valid && mem_addr >= 4*MEM_WORDS && mem_addr < 32'h 0200_0000), + .ready (spimem_ready), + .addr (mem_addr[23:0]), + .rdata (spimem_rdata), + + .flash_csb (flash_csb ), + .flash_clk (flash_clk ), + + .flash_io0_oe (flash_io0_oe), + .flash_io1_oe (flash_io1_oe), + .flash_io2_oe (flash_io2_oe), + .flash_io3_oe (flash_io3_oe), + + .flash_io0_do (flash_io0_do), + .flash_io1_do (flash_io1_do), + .flash_io2_do (flash_io2_do), + .flash_io3_do (flash_io3_do), + + .flash_io0_di (flash_io0_di), + .flash_io1_di (flash_io1_di), + .flash_io2_di (flash_io2_di), + .flash_io3_di (flash_io3_di), + + .cfgreg_we(spimemio_cfgreg_sel ? mem_wstrb : 4'b 0000), + .cfgreg_di(mem_wdata), + .cfgreg_do(spimemio_cfgreg_do) + ); + + simpleuart simpleuart ( + .clk (clk ), + .resetn (resetn ), + + .ser_tx (ser_tx ), + .ser_rx (ser_rx ), + + .reg_div_we (simpleuart_reg_div_sel ? mem_wstrb : 4'b 0000), + .reg_div_di (mem_wdata), + .reg_div_do (simpleuart_reg_div_do), + + .reg_dat_we (simpleuart_reg_dat_sel ? mem_wstrb[0] : 1'b 0), + .reg_dat_re (simpleuart_reg_dat_sel && !mem_wstrb), + .reg_dat_di (mem_wdata), + .reg_dat_do (simpleuart_reg_dat_do), + .reg_dat_wait(simpleuart_reg_dat_wait) + ); + + always @(posedge clk) + ram_ready <= mem_valid && !mem_ready && mem_addr < 4*MEM_WORDS; + + `PICOSOC_MEM #( + .WORDS(MEM_WORDS) + ) memory ( + .clk(clk), + .wen((mem_valid && !mem_ready && mem_addr < 4*MEM_WORDS) ? mem_wstrb : 4'b0), + .addr(mem_addr[23:2]), + .wdata(mem_wdata), + .rdata(ram_rdata) + ); +endmodule + +// Implementation note: +// Replace the following two modules with wrappers for your SRAM cells. + +module picosoc_regs ( + input clk, wen, + input [5:0] waddr, + input [5:0] raddr1, + input [5:0] raddr2, + input [31:0] wdata, + output [31:0] rdata1, + output [31:0] rdata2 +); + reg [31:0] regs [0:31]; + + always @(posedge clk) + if (wen) regs[waddr[4:0]] <= wdata; + + assign rdata1 = regs[raddr1[4:0]]; + assign rdata2 = regs[raddr2[4:0]]; +endmodule + +module picosoc_mem #( + parameter integer WORDS = 256 +) ( + input clk, + input [3:0] wen, + input [21:0] addr, + input [31:0] wdata, + output reg [31:0] rdata +); + reg [31:0] mem [0:WORDS-1]; + + always @(posedge clk) begin + rdata <= mem[addr]; + if (wen[0]) mem[addr][ 7: 0] <= wdata[ 7: 0]; + if (wen[1]) mem[addr][15: 8] <= wdata[15: 8]; + if (wen[2]) mem[addr][23:16] <= wdata[23:16]; + if (wen[3]) mem[addr][31:24] <= wdata[31:24]; + end +endmodule + diff --git a/picosoc/sections.lds b/picosoc/sections.lds new file mode 100644 index 0000000..f38d813 --- /dev/null +++ b/picosoc/sections.lds @@ -0,0 +1,71 @@ +#ifdef ICEBREAKER +# define MEM_TOTAL 0x20000 /* 128 KB */ +#elif HX8KDEMO +# define MEM_TOTAL 0x200 /* 2 KB */ +#else +# error "Set -DICEBREAKER or -DHX8KDEMO when compiling firmware.c" +#endif + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00100000, LENGTH = 0x400000 /* entire flash, 4 MiB */ + RAM (xrw) : ORIGIN = 0x00000000, LENGTH = MEM_TOTAL +} + +SECTIONS { + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.srodata) /* .rodata sections (constants, strings, etc.) */ + *(.srodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + _etext = .; /* define a global symbol at end of code */ + _sidata = _etext; /* This is used by the startup in order to initialize the .data secion */ + } >FLASH + + + /* This is the initialized data section + The program executes knowing that the data is in the RAM + but the loader puts the initial values in the FLASH (inidata). + It is one task of the startup to copy the initial values from FLASH to RAM. */ + .data : AT ( _sidata ) + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */ + _ram_start = .; /* create a global symbol at ram start for garbage collector */ + . = ALIGN(4); + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + *(.sdata) /* .sdata sections */ + *(.sdata*) /* .sdata* sections */ + . = ALIGN(4); + _edata = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */ + } >RAM + + /* Uninitialized data section */ + .bss : + { + . = ALIGN(4); + _sbss = .; /* define a global symbol at bss start; used by startup code */ + *(.bss) + *(.bss*) + *(.sbss) + *(.sbss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end; used by startup code */ + } >RAM + + /* this is to define the start of the heap, and make sure we have a minimum size */ + .heap : + { + . = ALIGN(4); + _heap_start = .; /* define a global symbol at heap start */ + } >RAM +} diff --git a/picosoc/simpleuart.v b/picosoc/simpleuart.v new file mode 100644 index 0000000..5ffef77 --- /dev/null +++ b/picosoc/simpleuart.v @@ -0,0 +1,137 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +module simpleuart #(parameter integer DEFAULT_DIV = 1) ( + input clk, + input resetn, + + output ser_tx, + input ser_rx, + + input [3:0] reg_div_we, + input [31:0] reg_div_di, + output [31:0] reg_div_do, + + input reg_dat_we, + input reg_dat_re, + input [31:0] reg_dat_di, + output [31:0] reg_dat_do, + output reg_dat_wait +); + reg [31:0] cfg_divider; + + reg [3:0] recv_state; + reg [31:0] recv_divcnt; + reg [7:0] recv_pattern; + reg [7:0] recv_buf_data; + reg recv_buf_valid; + + reg [9:0] send_pattern; + reg [3:0] send_bitcnt; + reg [31:0] send_divcnt; + reg send_dummy; + + assign reg_div_do = cfg_divider; + + assign reg_dat_wait = reg_dat_we && (send_bitcnt || send_dummy); + assign reg_dat_do = recv_buf_valid ? recv_buf_data : ~0; + + always @(posedge clk) begin + if (!resetn) begin + cfg_divider <= DEFAULT_DIV; + end else begin + if (reg_div_we[0]) cfg_divider[ 7: 0] <= reg_div_di[ 7: 0]; + if (reg_div_we[1]) cfg_divider[15: 8] <= reg_div_di[15: 8]; + if (reg_div_we[2]) cfg_divider[23:16] <= reg_div_di[23:16]; + if (reg_div_we[3]) cfg_divider[31:24] <= reg_div_di[31:24]; + end + end + + always @(posedge clk) begin + if (!resetn) begin + recv_state <= 0; + recv_divcnt <= 0; + recv_pattern <= 0; + recv_buf_data <= 0; + recv_buf_valid <= 0; + end else begin + recv_divcnt <= recv_divcnt + 1; + if (reg_dat_re) + recv_buf_valid <= 0; + case (recv_state) + 0: begin + if (!ser_rx) + recv_state <= 1; + recv_divcnt <= 0; + end + 1: begin + if (2*recv_divcnt > cfg_divider) begin + recv_state <= 2; + recv_divcnt <= 0; + end + end + 10: begin + if (recv_divcnt > cfg_divider) begin + recv_buf_data <= recv_pattern; + recv_buf_valid <= 1; + recv_state <= 0; + end + end + default: begin + if (recv_divcnt > cfg_divider) begin + recv_pattern <= {ser_rx, recv_pattern[7:1]}; + recv_state <= recv_state + 1; + recv_divcnt <= 0; + end + end + endcase + end + end + + assign ser_tx = send_pattern[0]; + + always @(posedge clk) begin + if (reg_div_we) + send_dummy <= 1; + send_divcnt <= send_divcnt + 1; + if (!resetn) begin + send_pattern <= ~0; + send_bitcnt <= 0; + send_divcnt <= 0; + send_dummy <= 1; + end else begin + if (send_dummy && !send_bitcnt) begin + send_pattern <= ~0; + send_bitcnt <= 15; + send_divcnt <= 0; + send_dummy <= 0; + end else + if (reg_dat_we && !send_bitcnt) begin + send_pattern <= {1'b1, reg_dat_di[7:0], 1'b0}; + send_bitcnt <= 10; + send_divcnt <= 0; + end else + if (send_divcnt > cfg_divider && send_bitcnt) begin + send_pattern <= {1'b1, send_pattern[9:1]}; + send_bitcnt <= send_bitcnt - 1; + send_divcnt <= 0; + end + end + end +endmodule diff --git a/picosoc/spiflash.core b/picosoc/spiflash.core new file mode 100644 index 0000000..1b7d153 --- /dev/null +++ b/picosoc/spiflash.core @@ -0,0 +1,24 @@ +CAPI=2: + +name : ::spiflash:0 + +filesets: + model: + files : [spiflash.v] + file_type : verilogSource + tb: + files : [spiflash_tb.v] + file_type : verilogSource + +targets: + default: + default_tool : icarus + filesets : [model, "is_toplevel? (tb)"] + parameters : [firmware] + toplevel : [testbench] + +parameters : + firmware: + datatype : file + description : Initial SPI Flash contents (in verilog hex format) + paramtype : plusarg diff --git a/picosoc/spiflash.v b/picosoc/spiflash.v new file mode 100644 index 0000000..22b337b --- /dev/null +++ b/picosoc/spiflash.v @@ -0,0 +1,409 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +`timescale 1 ns / 1 ps + +// +// Simple SPI flash simulation model +// +// This model samples io input signals 1ns before the SPI clock edge and +// updates output signals 1ns after the SPI clock edge. +// +// Supported commands: +// AB, B9, FF, 03, BB, EB, ED +// +// Well written SPI flash data sheets: +// Cypress S25FL064L http://www.cypress.com/file/316661/download +// Cypress S25FL128L http://www.cypress.com/file/316171/download +// +// SPI flash used on iCEBreaker board: +// https://www.winbond.com/resource-files/w25q128jv%20dtr%20revb%2011042016.pdf +// + +module spiflash ( + input csb, + input clk, + inout io0, // MOSI + inout io1, // MISO + inout io2, + inout io3 +); + localparam verbose = 0; + localparam integer latency = 8; + + reg [7:0] buffer; + integer bitcount = 0; + integer bytecount = 0; + integer dummycount = 0; + + reg [7:0] spi_cmd; + reg [7:0] xip_cmd = 0; + reg [23:0] spi_addr; + + reg [7:0] spi_in; + reg [7:0] spi_out; + reg spi_io_vld; + + reg powered_up = 0; + + localparam [3:0] mode_spi = 1; + localparam [3:0] mode_dspi_rd = 2; + localparam [3:0] mode_dspi_wr = 3; + localparam [3:0] mode_qspi_rd = 4; + localparam [3:0] mode_qspi_wr = 5; + localparam [3:0] mode_qspi_ddr_rd = 6; + localparam [3:0] mode_qspi_ddr_wr = 7; + + reg [3:0] mode = 0; + reg [3:0] next_mode = 0; + + reg io0_oe = 0; + reg io1_oe = 0; + reg io2_oe = 0; + reg io3_oe = 0; + + reg io0_dout = 0; + reg io1_dout = 0; + reg io2_dout = 0; + reg io3_dout = 0; + + assign #1 io0 = io0_oe ? io0_dout : 1'bz; + assign #1 io1 = io1_oe ? io1_dout : 1'bz; + assign #1 io2 = io2_oe ? io2_dout : 1'bz; + assign #1 io3 = io3_oe ? io3_dout : 1'bz; + + wire io0_delayed; + wire io1_delayed; + wire io2_delayed; + wire io3_delayed; + + assign #1 io0_delayed = io0; + assign #1 io1_delayed = io1; + assign #1 io2_delayed = io2; + assign #1 io3_delayed = io3; + + // 16 MB (128Mb) Flash + reg [7:0] memory [0:16*1024*1024-1]; + + reg [1023:0] firmware_file; + initial begin + if (!$value$plusargs("firmware=%s", firmware_file)) + firmware_file = "firmware.hex"; + $readmemh(firmware_file, memory); + end + + task spi_action; + begin + spi_in = buffer; + + if (bytecount == 1) begin + spi_cmd = buffer; + + if (spi_cmd == 8'h ab) + powered_up = 1; + + if (spi_cmd == 8'h b9) + powered_up = 0; + + if (spi_cmd == 8'h ff) + xip_cmd = 0; + end + + if (powered_up && spi_cmd == 'h 03) begin + if (bytecount == 2) + spi_addr[23:16] = buffer; + + if (bytecount == 3) + spi_addr[15:8] = buffer; + + if (bytecount == 4) + spi_addr[7:0] = buffer; + + if (bytecount >= 4) begin + buffer = memory[spi_addr]; + spi_addr = spi_addr + 1; + end + end + + if (powered_up && spi_cmd == 'h bb) begin + if (bytecount == 1) + mode = mode_dspi_rd; + + if (bytecount == 2) + spi_addr[23:16] = buffer; + + if (bytecount == 3) + spi_addr[15:8] = buffer; + + if (bytecount == 4) + spi_addr[7:0] = buffer; + + if (bytecount == 5) begin + xip_cmd = (buffer == 8'h a5) ? spi_cmd : 8'h 00; + mode = mode_dspi_wr; + dummycount = latency; + end + + if (bytecount >= 5) begin + buffer = memory[spi_addr]; + spi_addr = spi_addr + 1; + end + end + + if (powered_up && spi_cmd == 'h eb) begin + if (bytecount == 1) + mode = mode_qspi_rd; + + if (bytecount == 2) + spi_addr[23:16] = buffer; + + if (bytecount == 3) + spi_addr[15:8] = buffer; + + if (bytecount == 4) + spi_addr[7:0] = buffer; + + if (bytecount == 5) begin + xip_cmd = (buffer == 8'h a5) ? spi_cmd : 8'h 00; + mode = mode_qspi_wr; + dummycount = latency; + end + + if (bytecount >= 5) begin + buffer = memory[spi_addr]; + spi_addr = spi_addr + 1; + end + end + + if (powered_up && spi_cmd == 'h ed) begin + if (bytecount == 1) + next_mode = mode_qspi_ddr_rd; + + if (bytecount == 2) + spi_addr[23:16] = buffer; + + if (bytecount == 3) + spi_addr[15:8] = buffer; + + if (bytecount == 4) + spi_addr[7:0] = buffer; + + if (bytecount == 5) begin + xip_cmd = (buffer == 8'h a5) ? spi_cmd : 8'h 00; + mode = mode_qspi_ddr_wr; + dummycount = latency; + end + + if (bytecount >= 5) begin + buffer = memory[spi_addr]; + spi_addr = spi_addr + 1; + end + end + + spi_out = buffer; + spi_io_vld = 1; + + if (verbose) begin + if (bytecount == 1) + $write(""); + $write("", spi_in, spi_out); + end + + end + endtask + + task ddr_rd_edge; + begin + buffer = {buffer, io3_delayed, io2_delayed, io1_delayed, io0_delayed}; + bitcount = bitcount + 4; + if (bitcount == 8) begin + bitcount = 0; + bytecount = bytecount + 1; + spi_action; + end + end + endtask + + task ddr_wr_edge; + begin + io0_oe = 1; + io1_oe = 1; + io2_oe = 1; + io3_oe = 1; + + io0_dout = buffer[4]; + io1_dout = buffer[5]; + io2_dout = buffer[6]; + io3_dout = buffer[7]; + + buffer = {buffer, 4'h 0}; + bitcount = bitcount + 4; + if (bitcount == 8) begin + bitcount = 0; + bytecount = bytecount + 1; + spi_action; + end + end + endtask + + always @(csb) begin + if (csb) begin + if (verbose) begin + $display(""); + $fflush; + end + buffer = 0; + bitcount = 0; + bytecount = 0; + mode = mode_spi; + io0_oe = 0; + io1_oe = 0; + io2_oe = 0; + io3_oe = 0; + end else + if (xip_cmd) begin + buffer = xip_cmd; + bitcount = 0; + bytecount = 1; + spi_action; + end + end + + always @(csb, clk) begin + spi_io_vld = 0; + if (!csb && !clk) begin + if (dummycount > 0) begin + io0_oe = 0; + io1_oe = 0; + io2_oe = 0; + io3_oe = 0; + end else + case (mode) + mode_spi: begin + io0_oe = 0; + io1_oe = 1; + io2_oe = 0; + io3_oe = 0; + io1_dout = buffer[7]; + end + mode_dspi_rd: begin + io0_oe = 0; + io1_oe = 0; + io2_oe = 0; + io3_oe = 0; + end + mode_dspi_wr: begin + io0_oe = 1; + io1_oe = 1; + io2_oe = 0; + io3_oe = 0; + io0_dout = buffer[6]; + io1_dout = buffer[7]; + end + mode_qspi_rd: begin + io0_oe = 0; + io1_oe = 0; + io2_oe = 0; + io3_oe = 0; + end + mode_qspi_wr: begin + io0_oe = 1; + io1_oe = 1; + io2_oe = 1; + io3_oe = 1; + io0_dout = buffer[4]; + io1_dout = buffer[5]; + io2_dout = buffer[6]; + io3_dout = buffer[7]; + end + mode_qspi_ddr_rd: begin + ddr_rd_edge; + end + mode_qspi_ddr_wr: begin + ddr_wr_edge; + end + endcase + if (next_mode) begin + case (next_mode) + mode_qspi_ddr_rd: begin + io0_oe = 0; + io1_oe = 0; + io2_oe = 0; + io3_oe = 0; + end + mode_qspi_ddr_wr: begin + io0_oe = 1; + io1_oe = 1; + io2_oe = 1; + io3_oe = 1; + io0_dout = buffer[4]; + io1_dout = buffer[5]; + io2_dout = buffer[6]; + io3_dout = buffer[7]; + end + endcase + mode = next_mode; + next_mode = 0; + end + end + end + + always @(posedge clk) begin + if (!csb) begin + if (dummycount > 0) begin + dummycount = dummycount - 1; + end else + case (mode) + mode_spi: begin + buffer = {buffer, io0}; + bitcount = bitcount + 1; + if (bitcount == 8) begin + bitcount = 0; + bytecount = bytecount + 1; + spi_action; + end + end + mode_dspi_rd, mode_dspi_wr: begin + buffer = {buffer, io1, io0}; + bitcount = bitcount + 2; + if (bitcount == 8) begin + bitcount = 0; + bytecount = bytecount + 1; + spi_action; + end + end + mode_qspi_rd, mode_qspi_wr: begin + buffer = {buffer, io3, io2, io1, io0}; + bitcount = bitcount + 4; + if (bitcount == 8) begin + bitcount = 0; + bytecount = bytecount + 1; + spi_action; + end + end + mode_qspi_ddr_rd: begin + ddr_rd_edge; + end + mode_qspi_ddr_wr: begin + ddr_wr_edge; + end + endcase + end + end +endmodule diff --git a/picosoc/spiflash_tb.v b/picosoc/spiflash_tb.v new file mode 100644 index 0000000..a5b5edc --- /dev/null +++ b/picosoc/spiflash_tb.v @@ -0,0 +1,366 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +`timescale 1 ns / 1 ps + +module testbench; + reg flash_csb = 1; + reg flash_clk = 0; + + wire flash_io0; + wire flash_io1; + wire flash_io2; + wire flash_io3; + + reg flash_io0_oe = 0; + reg flash_io1_oe = 0; + reg flash_io2_oe = 0; + reg flash_io3_oe = 0; + + reg flash_io0_dout = 0; + reg flash_io1_dout = 0; + reg flash_io2_dout = 0; + reg flash_io3_dout = 0; + + assign flash_io0 = flash_io0_oe ? flash_io0_dout : 1'bz; + assign flash_io1 = flash_io1_oe ? flash_io1_dout : 1'bz; + assign flash_io2 = flash_io2_oe ? flash_io2_dout : 1'bz; + assign flash_io3 = flash_io3_oe ? flash_io3_dout : 1'bz; + + spiflash uut ( + .csb(flash_csb), + .clk(flash_clk), + .io0(flash_io0), + .io1(flash_io1), + .io2(flash_io2), + .io3(flash_io3) + ); + + localparam [23:0] offset = 24'h100000; + localparam [31:0] word0 = 32'h 00000093; + localparam [31:0] word1 = 32'h 00000193; + + reg [7:0] rdata; + integer errcount = 0; + + task expect; + input [7:0] data; + begin + if (data !== rdata) begin + $display("ERROR: Got %x (%b) but expected %x (%b).", rdata, rdata, data, data); + errcount = errcount + 1; + end + end + endtask + + task xfer_begin; + begin + #5; + flash_csb = 0; + $display("-- BEGIN"); + #5; + end + endtask + + task xfer_dummy; + begin + flash_io0_oe = 0; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + + #5; + flash_clk = 1; + #5; + flash_clk = 0; + #5; + end + endtask + + task xfer_end; + begin + #5; + flash_csb = 1; + flash_io0_oe = 0; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + $display("-- END"); + $display(""); + #5; + end + endtask + + task xfer_spi; + input [7:0] data; + integer i; + begin + flash_io0_oe = 1; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + + for (i = 0; i < 8; i=i+1) begin + flash_io0_dout = data[7-i]; + #5; + flash_clk = 1; + rdata[7-i] = flash_io1; + #5; + flash_clk = 0; + end + + $display("-- SPI SDR %02x %02x", data, rdata); + #5; + end + endtask + + task xfer_qspi_wr; + input [7:0] data; + integer i; + begin + flash_io0_oe = 1; + flash_io1_oe = 1; + flash_io2_oe = 1; + flash_io3_oe = 1; + + flash_io0_dout = data[4]; + flash_io1_dout = data[5]; + flash_io2_dout = data[6]; + flash_io3_dout = data[7]; + + #5; + flash_clk = 1; + + #5; + flash_clk = 0; + flash_io0_dout = data[0]; + flash_io1_dout = data[1]; + flash_io2_dout = data[2]; + flash_io3_dout = data[3]; + + #5; + flash_clk = 1; + #5; + flash_clk = 0; + + $display("-- QSPI SDR %02x --", data); + #5; + end + endtask + + task xfer_qspi_rd; + integer i; + begin + flash_io0_oe = 0; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + + #5; + flash_clk = 1; + rdata[4] = flash_io0; + rdata[5] = flash_io1; + rdata[6] = flash_io2; + rdata[7] = flash_io3; + + #5; + flash_clk = 0; + + #5; + flash_clk = 1; + rdata[0] = flash_io0; + rdata[1] = flash_io1; + rdata[2] = flash_io2; + rdata[3] = flash_io3; + + #5; + flash_clk = 0; + + $display("-- QSPI SDR -- %02x", rdata); + #5; + end + endtask + + task xfer_qspi_ddr_wr; + input [7:0] data; + integer i; + begin + flash_io0_oe = 1; + flash_io1_oe = 1; + flash_io2_oe = 1; + flash_io3_oe = 1; + + flash_io0_dout = data[4]; + flash_io1_dout = data[5]; + flash_io2_dout = data[6]; + flash_io3_dout = data[7]; + + #5; + flash_clk = 1; + flash_io0_dout = data[0]; + flash_io1_dout = data[1]; + flash_io2_dout = data[2]; + flash_io3_dout = data[3]; + + #5; + flash_clk = 0; + + $display("-- QSPI DDR %02x --", data); + #5; + end + endtask + + task xfer_qspi_ddr_rd; + integer i; + begin + flash_io0_oe = 0; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + + #5; + flash_clk = 1; + rdata[4] = flash_io0; + rdata[5] = flash_io1; + rdata[6] = flash_io2; + rdata[7] = flash_io3; + + #5; + flash_clk = 0; + rdata[0] = flash_io0; + rdata[1] = flash_io1; + rdata[2] = flash_io2; + rdata[3] = flash_io3; + + $display("-- QSPI DDR -- %02x", rdata); + #5; + end + endtask + + initial begin + $dumpfile("spiflash_tb.vcd"); + $dumpvars(0, testbench); + $display(""); + + $display("Reset (FFh)"); + xfer_begin; + xfer_spi(8'h ff); + xfer_end; + + $display("Power Up (ABh)"); + xfer_begin; + xfer_spi(8'h ab); + xfer_end; + + $display("Read Data (03h)"); + xfer_begin; + xfer_spi(8'h 03); + xfer_spi(offset[23:16]); + xfer_spi(offset[15:8]); + xfer_spi(offset[7:0]); + xfer_spi(8'h 00); expect(word0[7:0]); + xfer_spi(8'h 00); expect(word0[15:8]); + xfer_spi(8'h 00); expect(word0[23:16]); + xfer_spi(8'h 00); expect(word0[31:24]); + xfer_spi(8'h 00); expect(word1[7:0]); + xfer_spi(8'h 00); expect(word1[15:8]); + xfer_spi(8'h 00); expect(word1[23:16]); + xfer_spi(8'h 00); expect(word1[31:24]); + xfer_end; + + $display("Quad I/O Read (EBh)"); + xfer_begin; + xfer_spi(8'h eb); + xfer_qspi_wr(offset[23:16]); + xfer_qspi_wr(offset[15:8]); + xfer_qspi_wr(offset[7:0]); + xfer_qspi_wr(8'h a5); + repeat (8) xfer_dummy; + xfer_qspi_rd; expect(word0[7:0]); + xfer_qspi_rd; expect(word0[15:8]); + xfer_qspi_rd; expect(word0[23:16]); + xfer_qspi_rd; expect(word0[31:24]); + xfer_qspi_rd; expect(word1[7:0]); + xfer_qspi_rd; expect(word1[15:8]); + xfer_qspi_rd; expect(word1[23:16]); + xfer_qspi_rd; expect(word1[31:24]); + xfer_end; + + $display("Continous Quad I/O Read"); + xfer_begin; + xfer_qspi_wr(offset[23:16]); + xfer_qspi_wr(offset[15:8]); + xfer_qspi_wr(offset[7:0]); + xfer_qspi_wr(8'h ff); + repeat (8) xfer_dummy; + xfer_qspi_rd; expect(word0[7:0]); + xfer_qspi_rd; expect(word0[15:8]); + xfer_qspi_rd; expect(word0[23:16]); + xfer_qspi_rd; expect(word0[31:24]); + xfer_qspi_rd; expect(word1[7:0]); + xfer_qspi_rd; expect(word1[15:8]); + xfer_qspi_rd; expect(word1[23:16]); + xfer_qspi_rd; expect(word1[31:24]); + xfer_end; + + $display("DDR Quad I/O Read (EDh)"); + xfer_begin; + xfer_spi(8'h ed); + xfer_qspi_ddr_wr(offset[23:16]); + xfer_qspi_ddr_wr(offset[15:8]); + xfer_qspi_ddr_wr(offset[7:0]); + xfer_qspi_ddr_wr(8'h a5); + repeat (8) xfer_dummy; + xfer_qspi_ddr_rd; expect(word0[7:0]); + xfer_qspi_ddr_rd; expect(word0[15:8]); + xfer_qspi_ddr_rd; expect(word0[23:16]); + xfer_qspi_ddr_rd; expect(word0[31:24]); + xfer_qspi_ddr_rd; expect(word1[7:0]); + xfer_qspi_ddr_rd; expect(word1[15:8]); + xfer_qspi_ddr_rd; expect(word1[23:16]); + xfer_qspi_ddr_rd; expect(word1[31:24]); + xfer_end; + + $display("Continous DDR Quad I/O Read"); + xfer_begin; + xfer_qspi_ddr_wr(offset[23:16]); + xfer_qspi_ddr_wr(offset[15:8]); + xfer_qspi_ddr_wr(offset[7:0]); + xfer_qspi_ddr_wr(8'h ff); + repeat (8) xfer_dummy; + xfer_qspi_ddr_rd; expect(word0[7:0]); + xfer_qspi_ddr_rd; expect(word0[15:8]); + xfer_qspi_ddr_rd; expect(word0[23:16]); + xfer_qspi_ddr_rd; expect(word0[31:24]); + xfer_qspi_ddr_rd; expect(word1[7:0]); + xfer_qspi_ddr_rd; expect(word1[15:8]); + xfer_qspi_ddr_rd; expect(word1[23:16]); + xfer_qspi_ddr_rd; expect(word1[31:24]); + xfer_end; + + #5; + + if (errcount) begin + $display("FAIL"); + $stop; + end else begin + $display("PASS"); + end + end +endmodule diff --git a/picosoc/spimemio.v b/picosoc/spimemio.v new file mode 100644 index 0000000..b4ee446 --- /dev/null +++ b/picosoc/spimemio.v @@ -0,0 +1,579 @@ +/* + * PicoSoC - A simple example SoC using PicoRV32 + * + * Copyright (C) 2017 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +module spimemio ( + input clk, resetn, + + input valid, + output ready, + input [23:0] addr, + output reg [31:0] rdata, + + output flash_csb, + output flash_clk, + + output flash_io0_oe, + output flash_io1_oe, + output flash_io2_oe, + output flash_io3_oe, + + output flash_io0_do, + output flash_io1_do, + output flash_io2_do, + output flash_io3_do, + + input flash_io0_di, + input flash_io1_di, + input flash_io2_di, + input flash_io3_di, + + input [3:0] cfgreg_we, + input [31:0] cfgreg_di, + output [31:0] cfgreg_do +); + reg xfer_resetn; + reg din_valid; + wire din_ready; + reg [7:0] din_data; + reg [3:0] din_tag; + reg din_cont; + reg din_qspi; + reg din_ddr; + reg din_rd; + + wire dout_valid; + wire [7:0] dout_data; + wire [3:0] dout_tag; + + reg [23:0] buffer; + + reg [23:0] rd_addr; + reg rd_valid; + reg rd_wait; + reg rd_inc; + + assign ready = valid && (addr == rd_addr) && rd_valid; + wire jump = valid && !ready && (addr != rd_addr+4) && rd_valid; + + reg softreset; + + reg config_en; // cfgreg[31] + reg config_ddr; // cfgreg[22] + reg config_qspi; // cfgreg[21] + reg config_cont; // cfgreg[20] + reg [3:0] config_dummy; // cfgreg[19:16] + reg [3:0] config_oe; // cfgreg[11:8] + reg config_csb; // cfgreg[5] + reg config_clk; // cfgref[4] + reg [3:0] config_do; // cfgreg[3:0] + + assign cfgreg_do[31] = config_en; + assign cfgreg_do[30:23] = 0; + assign cfgreg_do[22] = config_ddr; + assign cfgreg_do[21] = config_qspi; + assign cfgreg_do[20] = config_cont; + assign cfgreg_do[19:16] = config_dummy; + assign cfgreg_do[15:12] = 0; + assign cfgreg_do[11:8] = {flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}; + assign cfgreg_do[7:6] = 0; + assign cfgreg_do[5] = flash_csb; + assign cfgreg_do[4] = flash_clk; + assign cfgreg_do[3:0] = {flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}; + + always @(posedge clk) begin + softreset <= !config_en || cfgreg_we; + if (!resetn) begin + softreset <= 1; + config_en <= 1; + config_csb <= 0; + config_clk <= 0; + config_oe <= 0; + config_do <= 0; + config_ddr <= 0; + config_qspi <= 0; + config_cont <= 0; + config_dummy <= 8; + end else begin + if (cfgreg_we[0]) begin + config_csb <= cfgreg_di[5]; + config_clk <= cfgreg_di[4]; + config_do <= cfgreg_di[3:0]; + end + if (cfgreg_we[1]) begin + config_oe <= cfgreg_di[11:8]; + end + if (cfgreg_we[2]) begin + config_ddr <= cfgreg_di[22]; + config_qspi <= cfgreg_di[21]; + config_cont <= cfgreg_di[20]; + config_dummy <= cfgreg_di[19:16]; + end + if (cfgreg_we[3]) begin + config_en <= cfgreg_di[31]; + end + end + end + + wire xfer_csb; + wire xfer_clk; + + wire xfer_io0_oe; + wire xfer_io1_oe; + wire xfer_io2_oe; + wire xfer_io3_oe; + + wire xfer_io0_do; + wire xfer_io1_do; + wire xfer_io2_do; + wire xfer_io3_do; + + reg xfer_io0_90; + reg xfer_io1_90; + reg xfer_io2_90; + reg xfer_io3_90; + + always @(negedge clk) begin + xfer_io0_90 <= xfer_io0_do; + xfer_io1_90 <= xfer_io1_do; + xfer_io2_90 <= xfer_io2_do; + xfer_io3_90 <= xfer_io3_do; + end + + assign flash_csb = config_en ? xfer_csb : config_csb; + assign flash_clk = config_en ? xfer_clk : config_clk; + + assign flash_io0_oe = config_en ? xfer_io0_oe : config_oe[0]; + assign flash_io1_oe = config_en ? xfer_io1_oe : config_oe[1]; + assign flash_io2_oe = config_en ? xfer_io2_oe : config_oe[2]; + assign flash_io3_oe = config_en ? xfer_io3_oe : config_oe[3]; + + assign flash_io0_do = config_en ? (config_ddr ? xfer_io0_90 : xfer_io0_do) : config_do[0]; + assign flash_io1_do = config_en ? (config_ddr ? xfer_io1_90 : xfer_io1_do) : config_do[1]; + assign flash_io2_do = config_en ? (config_ddr ? xfer_io2_90 : xfer_io2_do) : config_do[2]; + assign flash_io3_do = config_en ? (config_ddr ? xfer_io3_90 : xfer_io3_do) : config_do[3]; + + wire xfer_dspi = din_ddr && !din_qspi; + wire xfer_ddr = din_ddr && din_qspi; + + spimemio_xfer xfer ( + .clk (clk ), + .resetn (xfer_resetn ), + .din_valid (din_valid ), + .din_ready (din_ready ), + .din_data (din_data ), + .din_tag (din_tag ), + .din_cont (din_cont ), + .din_dspi (xfer_dspi ), + .din_qspi (din_qspi ), + .din_ddr (xfer_ddr ), + .din_rd (din_rd ), + .dout_valid (dout_valid ), + .dout_data (dout_data ), + .dout_tag (dout_tag ), + .flash_csb (xfer_csb ), + .flash_clk (xfer_clk ), + .flash_io0_oe (xfer_io0_oe ), + .flash_io1_oe (xfer_io1_oe ), + .flash_io2_oe (xfer_io2_oe ), + .flash_io3_oe (xfer_io3_oe ), + .flash_io0_do (xfer_io0_do ), + .flash_io1_do (xfer_io1_do ), + .flash_io2_do (xfer_io2_do ), + .flash_io3_do (xfer_io3_do ), + .flash_io0_di (flash_io0_di), + .flash_io1_di (flash_io1_di), + .flash_io2_di (flash_io2_di), + .flash_io3_di (flash_io3_di) + ); + + reg [3:0] state; + + always @(posedge clk) begin + xfer_resetn <= 1; + din_valid <= 0; + + if (!resetn || softreset) begin + state <= 0; + xfer_resetn <= 0; + rd_valid <= 0; + din_tag <= 0; + din_cont <= 0; + din_qspi <= 0; + din_ddr <= 0; + din_rd <= 0; + end else begin + if (dout_valid && dout_tag == 1) buffer[ 7: 0] <= dout_data; + if (dout_valid && dout_tag == 2) buffer[15: 8] <= dout_data; + if (dout_valid && dout_tag == 3) buffer[23:16] <= dout_data; + if (dout_valid && dout_tag == 4) begin + rdata <= {dout_data, buffer}; + rd_addr <= rd_inc ? rd_addr + 4 : addr; + rd_valid <= 1; + rd_wait <= rd_inc; + rd_inc <= 1; + end + + if (valid) + rd_wait <= 0; + + case (state) + 0: begin + din_valid <= 1; + din_data <= 8'h ff; + din_tag <= 0; + if (din_ready) begin + din_valid <= 0; + state <= 1; + end + end + 1: begin + if (dout_valid) begin + xfer_resetn <= 0; + state <= 2; + end + end + 2: begin + din_valid <= 1; + din_data <= 8'h ab; + din_tag <= 0; + if (din_ready) begin + din_valid <= 0; + state <= 3; + end + end + 3: begin + if (dout_valid) begin + xfer_resetn <= 0; + state <= 4; + end + end + 4: begin + rd_inc <= 0; + din_valid <= 1; + din_tag <= 0; + case ({config_ddr, config_qspi}) + 2'b11: din_data <= 8'h ED; + 2'b01: din_data <= 8'h EB; + 2'b10: din_data <= 8'h BB; + 2'b00: din_data <= 8'h 03; + endcase + if (din_ready) begin + din_valid <= 0; + state <= 5; + end + end + 5: begin + if (valid && !ready) begin + din_valid <= 1; + din_tag <= 0; + din_data <= addr[23:16]; + din_qspi <= config_qspi; + din_ddr <= config_ddr; + if (din_ready) begin + din_valid <= 0; + state <= 6; + end + end + end + 6: begin + din_valid <= 1; + din_tag <= 0; + din_data <= addr[15:8]; + if (din_ready) begin + din_valid <= 0; + state <= 7; + end + end + 7: begin + din_valid <= 1; + din_tag <= 0; + din_data <= addr[7:0]; + if (din_ready) begin + din_valid <= 0; + din_data <= 0; + state <= config_qspi || config_ddr ? 8 : 9; + end + end + 8: begin + din_valid <= 1; + din_tag <= 0; + din_data <= config_cont ? 8'h A5 : 8'h FF; + if (din_ready) begin + din_rd <= 1; + din_data <= config_dummy; + din_valid <= 0; + state <= 9; + end + end + 9: begin + din_valid <= 1; + din_tag <= 1; + if (din_ready) begin + din_valid <= 0; + state <= 10; + end + end + 10: begin + din_valid <= 1; + din_data <= 8'h 00; + din_tag <= 2; + if (din_ready) begin + din_valid <= 0; + state <= 11; + end + end + 11: begin + din_valid <= 1; + din_tag <= 3; + if (din_ready) begin + din_valid <= 0; + state <= 12; + end + end + 12: begin + if (!rd_wait || valid) begin + din_valid <= 1; + din_tag <= 4; + if (din_ready) begin + din_valid <= 0; + state <= 9; + end + end + end + endcase + + if (jump) begin + rd_inc <= 0; + rd_valid <= 0; + xfer_resetn <= 0; + if (config_cont) begin + state <= 5; + end else begin + state <= 4; + din_qspi <= 0; + din_ddr <= 0; + end + din_rd <= 0; + end + end + end +endmodule + +module spimemio_xfer ( + input clk, resetn, + + input din_valid, + output din_ready, + input [7:0] din_data, + input [3:0] din_tag, + input din_cont, + input din_dspi, + input din_qspi, + input din_ddr, + input din_rd, + + output dout_valid, + output [7:0] dout_data, + output [3:0] dout_tag, + + output reg flash_csb, + output reg flash_clk, + + output reg flash_io0_oe, + output reg flash_io1_oe, + output reg flash_io2_oe, + output reg flash_io3_oe, + + output reg flash_io0_do, + output reg flash_io1_do, + output reg flash_io2_do, + output reg flash_io3_do, + + input flash_io0_di, + input flash_io1_di, + input flash_io2_di, + input flash_io3_di +); + reg [7:0] obuffer; + reg [7:0] ibuffer; + + reg [3:0] count; + reg [3:0] dummy_count; + + reg xfer_cont; + reg xfer_dspi; + reg xfer_qspi; + reg xfer_ddr; + reg xfer_ddr_q; + reg xfer_rd; + reg [3:0] xfer_tag; + reg [3:0] xfer_tag_q; + + reg [7:0] next_obuffer; + reg [7:0] next_ibuffer; + reg [3:0] next_count; + + reg fetch; + reg next_fetch; + reg last_fetch; + + always @(posedge clk) begin + xfer_ddr_q <= xfer_ddr; + xfer_tag_q <= xfer_tag; + end + + assign din_ready = din_valid && resetn && next_fetch; + + assign dout_valid = (xfer_ddr_q ? fetch && !last_fetch : next_fetch && !fetch) && resetn; + assign dout_data = ibuffer; + assign dout_tag = xfer_tag_q; + + always @* begin + flash_io0_oe = 0; + flash_io1_oe = 0; + flash_io2_oe = 0; + flash_io3_oe = 0; + + flash_io0_do = 0; + flash_io1_do = 0; + flash_io2_do = 0; + flash_io3_do = 0; + + next_obuffer = obuffer; + next_ibuffer = ibuffer; + next_count = count; + next_fetch = 0; + + if (dummy_count == 0) begin + casez ({xfer_ddr, xfer_qspi, xfer_dspi}) + 3'b 000: begin + flash_io0_oe = 1; + flash_io0_do = obuffer[7]; + + if (flash_clk) begin + next_obuffer = {obuffer[6:0], 1'b 0}; + next_count = count - |count; + end else begin + next_ibuffer = {ibuffer[6:0], flash_io1_di}; + end + + next_fetch = (next_count == 0); + end + 3'b 01?: begin + flash_io0_oe = !xfer_rd; + flash_io1_oe = !xfer_rd; + flash_io2_oe = !xfer_rd; + flash_io3_oe = !xfer_rd; + + flash_io0_do = obuffer[4]; + flash_io1_do = obuffer[5]; + flash_io2_do = obuffer[6]; + flash_io3_do = obuffer[7]; + + if (flash_clk) begin + next_obuffer = {obuffer[3:0], 4'b 0000}; + next_count = count - {|count, 2'b00}; + end else begin + next_ibuffer = {ibuffer[3:0], flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}; + end + + next_fetch = (next_count == 0); + end + 3'b 11?: begin + flash_io0_oe = !xfer_rd; + flash_io1_oe = !xfer_rd; + flash_io2_oe = !xfer_rd; + flash_io3_oe = !xfer_rd; + + flash_io0_do = obuffer[4]; + flash_io1_do = obuffer[5]; + flash_io2_do = obuffer[6]; + flash_io3_do = obuffer[7]; + + next_obuffer = {obuffer[3:0], 4'b 0000}; + next_ibuffer = {ibuffer[3:0], flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di}; + next_count = count - {|count, 2'b00}; + + next_fetch = (next_count == 0); + end + 3'b ??1: begin + flash_io0_oe = !xfer_rd; + flash_io1_oe = !xfer_rd; + + flash_io0_do = obuffer[6]; + flash_io1_do = obuffer[7]; + + if (flash_clk) begin + next_obuffer = {obuffer[5:0], 2'b 00}; + next_count = count - {|count, 1'b0}; + end else begin + next_ibuffer = {ibuffer[5:0], flash_io1_di, flash_io0_di}; + end + + next_fetch = (next_count == 0); + end + endcase + end + end + + always @(posedge clk) begin + if (!resetn) begin + fetch <= 1; + last_fetch <= 1; + flash_csb <= 1; + flash_clk <= 0; + count <= 0; + dummy_count <= 0; + xfer_tag <= 0; + xfer_cont <= 0; + xfer_dspi <= 0; + xfer_qspi <= 0; + xfer_ddr <= 0; + xfer_rd <= 0; + end else begin + fetch <= next_fetch; + last_fetch <= xfer_ddr ? fetch : 1; + if (dummy_count) begin + flash_clk <= !flash_clk && !flash_csb; + dummy_count <= dummy_count - flash_clk; + end else + if (count) begin + flash_clk <= !flash_clk && !flash_csb; + obuffer <= next_obuffer; + ibuffer <= next_ibuffer; + count <= next_count; + end + if (din_valid && din_ready) begin + flash_csb <= 0; + flash_clk <= 0; + + count <= 8; + dummy_count <= din_rd ? din_data : 0; + obuffer <= din_data; + + xfer_tag <= din_tag; + xfer_cont <= din_cont; + xfer_dspi <= din_dspi; + xfer_qspi <= din_qspi; + xfer_ddr <= din_ddr; + xfer_rd <= din_rd; + end + end + end +endmodule diff --git a/picosoc/start.s b/picosoc/start.s new file mode 100644 index 0000000..e9e18db --- /dev/null +++ b/picosoc/start.s @@ -0,0 +1,159 @@ +.section .text + +start: + +# zero-initialize register file +addi x1, zero, 0 +# x2 (sp) is initialized by reset +addi x3, zero, 0 +addi x4, zero, 0 +addi x5, zero, 0 +addi x6, zero, 0 +addi x7, zero, 0 +addi x8, zero, 0 +addi x9, zero, 0 +addi x10, zero, 0 +addi x11, zero, 0 +addi x12, zero, 0 +addi x13, zero, 0 +addi x14, zero, 0 +addi x15, zero, 0 +addi x16, zero, 0 +addi x17, zero, 0 +addi x18, zero, 0 +addi x19, zero, 0 +addi x20, zero, 0 +addi x21, zero, 0 +addi x22, zero, 0 +addi x23, zero, 0 +addi x24, zero, 0 +addi x25, zero, 0 +addi x26, zero, 0 +addi x27, zero, 0 +addi x28, zero, 0 +addi x29, zero, 0 +addi x30, zero, 0 +addi x31, zero, 0 + +# Update LEDs +li a0, 0x03000000 +li a1, 1 +sw a1, 0(a0) + +# zero initialize entire scratchpad memory +li a0, 0x00000000 +setmemloop: +sw a0, 0(a0) +addi a0, a0, 4 +blt a0, sp, setmemloop + +# Update LEDs +li a0, 0x03000000 +li a1, 3 +sw a1, 0(a0) + +# copy data section +la a0, _sidata +la a1, _sdata +la a2, _edata +bge a1, a2, end_init_data +loop_init_data: +lw a3, 0(a0) +sw a3, 0(a1) +addi a0, a0, 4 +addi a1, a1, 4 +blt a1, a2, loop_init_data +end_init_data: + +# Update LEDs +li a0, 0x03000000 +li a1, 7 +sw a1, 0(a0) + +# zero-init bss section +la a0, _sbss +la a1, _ebss +bge a0, a1, end_init_bss +loop_init_bss: +sw zero, 0(a0) +addi a0, a0, 4 +blt a0, a1, loop_init_bss +end_init_bss: + +# Update LEDs +li a0, 0x03000000 +li a1, 15 +sw a1, 0(a0) + +# call main +call main +loop: +j loop + +.global flashio_worker_begin +.global flashio_worker_end + +.balign 4 + +flashio_worker_begin: +# a0 ... data pointer +# a1 ... data length +# a2 ... optional WREN cmd (0 = disable) + +# address of SPI ctrl reg +li t0, 0x02000000 + +# Set CS high, IO0 is output +li t1, 0x120 +sh t1, 0(t0) + +# Enable Manual SPI Ctrl +sb zero, 3(t0) + +# Send optional WREN cmd +beqz a2, flashio_worker_L1 +li t5, 8 +andi t2, a2, 0xff +flashio_worker_L4: +srli t4, t2, 7 +sb t4, 0(t0) +ori t4, t4, 0x10 +sb t4, 0(t0) +slli t2, t2, 1 +andi t2, t2, 0xff +addi t5, t5, -1 +bnez t5, flashio_worker_L4 +sb t1, 0(t0) + +# SPI transfer +flashio_worker_L1: +beqz a1, flashio_worker_L3 +li t5, 8 +lbu t2, 0(a0) +flashio_worker_L2: +srli t4, t2, 7 +sb t4, 0(t0) +ori t4, t4, 0x10 +sb t4, 0(t0) +lbu t4, 0(t0) +andi t4, t4, 2 +srli t4, t4, 1 +slli t2, t2, 1 +or t2, t2, t4 +andi t2, t2, 0xff +addi t5, t5, -1 +bnez t5, flashio_worker_L2 +sb t2, 0(a0) +addi a0, a0, 1 +addi a1, a1, -1 +j flashio_worker_L1 +flashio_worker_L3: + +# Back to MEMIO mode +li t1, 0x80 +sb t1, 3(t0) + +ret + +.balign 4 +flashio_worker_end: diff --git a/scripts/csmith/.gitignore b/scripts/csmith/.gitignore new file mode 100644 index 0000000..efd00f7 --- /dev/null +++ b/scripts/csmith/.gitignore @@ -0,0 +1,13 @@ +obj_dir +riscv-fesvr +riscv-isa-sim +output_ref.txt +output_sim.txt +platform.info +test.c +test.ld +test.elf +test_ref +test.hex +testbench.vvp +testbench.vcd diff --git a/scripts/csmith/Makefile b/scripts/csmith/Makefile new file mode 100644 index 0000000..fd5107f --- /dev/null +++ b/scripts/csmith/Makefile @@ -0,0 +1,85 @@ +RISCV_TOOLS_DIR = /opt/riscv32imc +RISCV_TOOLS_PREFIX = $(RISCV_TOOLS_DIR)/bin/riscv32-unknown-elf- +CSMITH_INCDIR = $(shell ls -d /usr/local/include/csmith-* | head -n1) +CC = $(RISCV_TOOLS_PREFIX)gcc +SHELL = /bin/bash + +help: + @echo "Usage: make { loop | verilator | iverilog | spike }" + +loop: riscv-fesvr/build.ok riscv-isa-sim/build.ok obj_dir/Vtestbench + +set -e; x() { echo "$$*" >&2; "$$@"; }; i=1; j=1; while true; do echo; echo; \ + echo "---------------- $$((i++)) ($$j) ----------------"; \ + x rm -f test.hex test.elf test.c test_ref test.ld output_ref.txt output_sim.txt; \ + x make spike test.hex || { echo SKIP; continue; }; x rm -f output_sim.txt; \ + x obj_dir/Vtestbench | grep -v '$$finish' > output_sim.txt; \ + x diff -u output_ref.txt output_sim.txt; echo OK; ! ((j++)); \ + done + +verilator: test_ref test.hex obj_dir/Vtestbench + timeout 2 ./test_ref > output_ref.txt && cat output_ref.txt + obj_dir/Vtestbench | grep -v '$$finish' > output_sim.txt + diff -u output_ref.txt output_sim.txt + +iverilog: test_ref test.hex testbench.vvp + timeout 2 ./test_ref > output_ref.txt && cat output_ref.txt + vvp -N testbench.vvp > output_sim.txt + diff -u output_ref.txt output_sim.txt + +spike: riscv-fesvr/build.ok riscv-isa-sim/build.ok test_ref test.elf + timeout 2 ./test_ref > output_ref.txt && cat output_ref.txt + LD_LIBRARY_PATH="./riscv-isa-sim:./riscv-fesvr" ./riscv-isa-sim/spike test.elf > output_sim.txt + diff -u output_ref.txt output_sim.txt + +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.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 + +testbench.vvp: testbench.v ../../picorv32.v + iverilog -o testbench.vvp testbench.v ../../picorv32.v + chmod -x testbench.vvp + +obj_dir/Vtestbench: testbench.v testbench.cc ../../picorv32.v + verilator --exe -Wno-fatal --cc --top-module testbench testbench.v ../../picorv32.v testbench.cc + $(MAKE) -C obj_dir -f Vtestbench.mk + +test.hex: test.elf + $(RISCV_TOOLS_PREFIX)objcopy -O verilog test.elf test.hex + +start.elf: start.S start.ld + $(CC) -nostdlib -o start.elf start.S -T start.ld + chmod -x start.elf + +test_ref: test.c + gcc -m32 -o test_ref -w -Os -I $(CSMITH_INCDIR) test.c + +test.elf: test.c syscalls.c start.S + sed -e '/SECTIONS/,+1 s/{/{ . = 0x00000000; .start : { *(.text.start) } application_entry_point = 0x00010000;/;' \ + $(RISCV_TOOLS_DIR)/riscv32-unknown-elf/lib/riscv.ld > test.ld + $(CC) -o test.elf -w -Os -I $(CSMITH_INCDIR) -T test.ld test.c syscalls.c start.S + chmod -x test.elf + +test.c: + echo "integer size = 4" > platform.info + echo "pointer size = 4" >> platform.info + csmith --no-packed-struct -o test.c + gawk '/Seed:/ {print$$2,$$3;}' test.c + +clean: + rm -rf platform.info test.c test.ld test.elf test.hex test_ref obj_dir + rm -rf testbench.vvp testbench.vcd output_ref.txt output_sim.txt + +mrproper: clean + rm -rf riscv-fesvr riscv-isa-sim + +.PHONY: help loop verilator iverilog spike clean mrproper + diff --git a/scripts/csmith/riscv-isa-sim.diff b/scripts/csmith/riscv-isa-sim.diff new file mode 100644 index 0000000..fd22280 --- /dev/null +++ b/scripts/csmith/riscv-isa-sim.diff @@ -0,0 +1,62 @@ +diff --git a/riscv/execute.cc b/riscv/execute.cc +index 5c3fdf7..4d914b3 100644 +--- a/riscv/execute.cc ++++ b/riscv/execute.cc +@@ -124,6 +124,10 @@ miss: + } + + state.minstret += instret; ++ if (state.minstret > 1000000) { ++ printf("Reached limit of 1000000 instructions.\n"); ++ exit(0); ++ } + n -= instret; + } + } +diff --git a/riscv/insns/c_ebreak.h b/riscv/insns/c_ebreak.h +index a17200f..f06d8d9 100644 +--- a/riscv/insns/c_ebreak.h ++++ b/riscv/insns/c_ebreak.h +@@ -1,2 +1,4 @@ + require_extension('C'); ++ ++exit(0); + throw trap_breakpoint(); +diff --git a/riscv/insns/sbreak.h b/riscv/insns/sbreak.h +index c22776c..d38bd22 100644 +--- a/riscv/insns/sbreak.h ++++ b/riscv/insns/sbreak.h +@@ -1 +1,2 @@ ++exit(0); + throw trap_breakpoint(); +diff --git a/riscv/mmu.h b/riscv/mmu.h +index b9948c5..bee1f8b 100644 +--- a/riscv/mmu.h ++++ b/riscv/mmu.h +@@ -67,7 +67,8 @@ public: + if (addr & (sizeof(type##_t)-1)) \ + throw trap_store_address_misaligned(addr); \ + reg_t vpn = addr >> PGSHIFT; \ +- if (likely(tlb_store_tag[vpn % TLB_ENTRIES] == vpn)) \ ++ if (addr == 0x10000000) putchar(val), fflush(stdout); \ ++ else if (likely(tlb_store_tag[vpn % TLB_ENTRIES] == vpn)) \ + *(type##_t*)(tlb_data[vpn % TLB_ENTRIES] + addr) = val; \ + else \ + store_slow_path(addr, sizeof(type##_t), (const uint8_t*)&val); \ +diff --git a/riscv/processor.cc b/riscv/processor.cc +index 3b834c5..f407543 100644 +--- a/riscv/processor.cc ++++ b/riscv/processor.cc +@@ -201,9 +201,9 @@ void processor_t::set_privilege(reg_t prv) + + void processor_t::take_trap(trap_t& t, reg_t epc) + { +- if (debug) +- fprintf(stderr, "core %3d: exception %s, epc 0x%016" PRIx64 "\n", +- id, t.name(), epc); ++ printf("core %3d: exception %s, epc 0x%016" PRIx64 "\n", ++ id, t.name(), epc); ++ exit(0); + + // by default, trap to M-mode, unless delegated to S-mode + reg_t bit = t.cause(); diff --git a/scripts/csmith/start.S b/scripts/csmith/start.S new file mode 100644 index 0000000..d8f110e --- /dev/null +++ b/scripts/csmith/start.S @@ -0,0 +1,51 @@ +.section .text.start +.global application_entry_point + +/* zero-initialize all registers */ +addi x1, zero, 0 +addi x2, zero, 0 +addi x3, zero, 0 +addi x4, zero, 0 +addi x5, zero, 0 +addi x6, zero, 0 +addi x7, zero, 0 +addi x8, zero, 0 +addi x9, zero, 0 +addi x10, zero, 0 +addi x11, zero, 0 +addi x12, zero, 0 +addi x13, zero, 0 +addi x14, zero, 0 +addi x15, zero, 0 +addi x16, zero, 0 +addi x17, zero, 0 +addi x18, zero, 0 +addi x19, zero, 0 +addi x20, zero, 0 +addi x21, zero, 0 +addi x22, zero, 0 +addi x23, zero, 0 +addi x24, zero, 0 +addi x25, zero, 0 +addi x26, zero, 0 +addi x27, zero, 0 +addi x28, zero, 0 +addi x29, zero, 0 +addi x30, zero, 0 +addi x31, zero, 0 + +/* set stack pointer */ +lui sp, %hi(4*1024*1024) +addi sp, sp, %lo(4*1024*1024) + +/* push zeros on the stack for argc and argv */ +/* (stack is aligned to 16 bytes in riscv calling convention) */ +addi sp,sp,-16 +sw zero,0(sp) +sw zero,4(sp) +sw zero,8(sp) +sw zero,12(sp) + +/* jump to libc init */ +j application_entry_point + diff --git a/scripts/csmith/syscalls.c b/scripts/csmith/syscalls.c new file mode 100644 index 0000000..8ea84ca --- /dev/null +++ b/scripts/csmith/syscalls.c @@ -0,0 +1,95 @@ +// An extremely minimalist syscalls.c for newlib +// Based on riscv newlib libgloss/riscv/sys_*.c +// Written by Clifford Wolf. + +#include +#include +#include + +#define UNIMPL_FUNC(_f) ".globl " #_f "\n.type " #_f ", @function\n" #_f ":\n" + +asm ( + ".text\n" + ".align 2\n" + UNIMPL_FUNC(_open) + UNIMPL_FUNC(_openat) + UNIMPL_FUNC(_lseek) + UNIMPL_FUNC(_stat) + UNIMPL_FUNC(_lstat) + UNIMPL_FUNC(_fstatat) + UNIMPL_FUNC(_isatty) + UNIMPL_FUNC(_access) + UNIMPL_FUNC(_faccessat) + UNIMPL_FUNC(_link) + UNIMPL_FUNC(_unlink) + UNIMPL_FUNC(_execve) + UNIMPL_FUNC(_getpid) + UNIMPL_FUNC(_fork) + UNIMPL_FUNC(_kill) + UNIMPL_FUNC(_wait) + UNIMPL_FUNC(_times) + UNIMPL_FUNC(_gettimeofday) + UNIMPL_FUNC(_ftime) + UNIMPL_FUNC(_utime) + UNIMPL_FUNC(_chown) + UNIMPL_FUNC(_chmod) + UNIMPL_FUNC(_chdir) + UNIMPL_FUNC(_getcwd) + UNIMPL_FUNC(_sysconf) + "j unimplemented_syscall\n" +); + +void unimplemented_syscall() +{ + const char *p = "Unimplemented system call called!\n"; + while (*p) + *(volatile int*)0x10000000 = *(p++); + asm volatile ("ebreak"); + __builtin_unreachable(); +} + +ssize_t _read(int file, void *ptr, size_t len) +{ + // always EOF + return 0; +} + +ssize_t _write(int file, const void *ptr, size_t len) +{ + const void *eptr = ptr + len; + while (ptr != eptr) + *(volatile int*)0x10000000 = *(char*)(ptr++); + return len; +} + +int _close(int file) +{ + // close is called before _exit() + return 0; +} + +int _fstat(int file, struct stat *st) +{ + // fstat is called during libc startup + errno = ENOENT; + return -1; +} + +void *_sbrk(ptrdiff_t incr) +{ + extern unsigned char _end[]; // Defined by linker + static unsigned long heap_end; + + if (heap_end == 0) + heap_end = (long)_end; + + heap_end += incr; + return (void *)(heap_end - incr); +} + +void _exit(int exit_status) +{ + asm volatile ("ebreak"); + __builtin_unreachable(); +} + diff --git a/scripts/csmith/testbench.cc b/scripts/csmith/testbench.cc new file mode 100644 index 0000000..2925d0b --- /dev/null +++ b/scripts/csmith/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/csmith/testbench.v b/scripts/csmith/testbench.v new file mode 100644 index 0000000..9d9d8f6 --- /dev/null +++ b/scripts/csmith/testbench.v @@ -0,0 +1,100 @@ +`timescale 1 ns / 1 ps + +module testbench ( +`ifdef VERILATOR + input clk +`endif +); +`ifndef VERILATOR + reg clk = 1; + always #5 clk = ~clk; +`endif + + reg resetn = 0; + integer resetn_cnt = 0; + wire trap; + + initial begin + // $dumpfile("testbench.vcd"); + // $dumpvars(0, testbench); + end + + always @(posedge clk) begin + if (resetn_cnt < 100) + resetn_cnt <= resetn_cnt + 1; + else + resetn <= 1; + end + + wire mem_valid; + wire mem_instr; + wire mem_ready; + wire [31:0] mem_addr; + wire [31:0] mem_wdata; + wire [3:0] mem_wstrb; + wire [31:0] mem_rdata; + + 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 #( + .COMPRESSED_ISA(1), + .ENABLE_MUL(1), + .ENABLE_DIV(1) + ) 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 ) + ); + + reg [7:0] memory [0:4*1024*1024-1]; + initial $readmemh("test.hex", memory); + + assign mem_ready = x32[0] && mem_valid; + + assign mem_rdata[ 7: 0] = memory[mem_addr + 0]; + assign mem_rdata[15: 8] = memory[mem_addr + 1]; + assign mem_rdata[23:16] = memory[mem_addr + 2]; + assign mem_rdata[31:24] = memory[mem_addr + 3]; + + always @(posedge clk) begin + if (mem_valid && mem_ready) begin + if (mem_wstrb && mem_addr == 'h10000000) begin + $write("%c", mem_wdata[ 7: 0]); +`ifndef VERILATOR + $fflush; +`endif + end else begin + if (mem_wstrb[0]) memory[mem_addr + 0] <= mem_wdata[ 7: 0]; + if (mem_wstrb[1]) memory[mem_addr + 1] <= mem_wdata[15: 8]; + if (mem_wstrb[2]) memory[mem_addr + 2] <= mem_wdata[23:16]; + if (mem_wstrb[3]) memory[mem_addr + 3] <= mem_wdata[31:24]; + end + end + end + + always @(posedge clk) begin + if (resetn && trap) begin + // repeat (10) @(posedge clk); + // $display("TRAP"); + $finish; + end + end +endmodule diff --git a/scripts/cxxdemo/.gitignore b/scripts/cxxdemo/.gitignore new file mode 100644 index 0000000..47e6b5c --- /dev/null +++ b/scripts/cxxdemo/.gitignore @@ -0,0 +1,9 @@ +firmware.d +firmware.elf +firmware.hex +firmware32.hex +firmware.o +syscalls.o +testbench.vvp +testbench.vcd +start.elf diff --git a/scripts/cxxdemo/Makefile b/scripts/cxxdemo/Makefile new file mode 100644 index 0000000..2d95019 --- /dev/null +++ b/scripts/cxxdemo/Makefile @@ -0,0 +1,38 @@ +RISCV_TOOLS_PREFIX = /opt/riscv32ic/bin/riscv32-unknown-elf- +CXX = $(RISCV_TOOLS_PREFIX)g++ +CC = $(RISCV_TOOLS_PREFIX)gcc +AS = $(RISCV_TOOLS_PREFIX)gcc +CXXFLAGS = -MD -Os -Wall -std=c++11 +CCFLAGS = -MD -Os -Wall -std=c++11 +LDFLAGS = -Wl,--gc-sections +LDLIBS = -lstdc++ + +test: testbench.vvp firmware32.hex + vvp -N testbench.vvp + +testbench.vvp: testbench.v ../../picorv32.v + iverilog -o testbench.vvp testbench.v ../../picorv32.v + chmod -x testbench.vvp + +firmware32.hex: firmware.elf start.elf hex8tohex32.py + $(RISCV_TOOLS_PREFIX)objcopy -O verilog start.elf start.tmp + $(RISCV_TOOLS_PREFIX)objcopy -O verilog firmware.elf firmware.tmp + cat start.tmp firmware.tmp > firmware.hex + python3 hex8tohex32.py firmware.hex > firmware32.hex + rm -f start.tmp firmware.tmp + +firmware.elf: firmware.o syscalls.o + $(CC) $(LDFLAGS) -o $@ $^ -T ../../firmware/riscv.ld $(LDLIBS) + chmod -x firmware.elf + +start.elf: start.S start.ld + $(CC) -nostdlib -o start.elf start.S -T start.ld $(LDLIBS) + chmod -x start.elf + +clean: + rm -f *.o *.d *.tmp start.elf + rm -f firmware.elf firmware.hex firmware32.hex + rm -f testbench.vvp testbench.vcd + +-include *.d +.PHONY: test clean diff --git a/scripts/cxxdemo/firmware.cc b/scripts/cxxdemo/firmware.cc new file mode 100644 index 0000000..638c0dd --- /dev/null +++ b/scripts/cxxdemo/firmware.cc @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +class ExampleBaseClass +{ +public: + ExampleBaseClass() { + std::cout << "ExampleBaseClass()" << std::endl; + } + + virtual ~ExampleBaseClass() { + std::cout << "~ExampleBaseClass()" << std::endl; + } + + virtual void print_something_virt() { + std::cout << "ExampleBaseClass::print_something_virt()" << std::endl; + } + + void print_something_novirt() { + std::cout << "ExampleBaseClass::print_something_novirt()" << std::endl; + } +}; + +class ExampleSubClass : public ExampleBaseClass +{ +public: + ExampleSubClass() { + std::cout << "ExampleSubClass()" << std::endl; + } + + virtual ~ExampleSubClass() { + std::cout << "~ExampleSubClass()" << std::endl; + } + + virtual void print_something_virt() { + std::cout << "ExampleSubClass::print_something_virt()" << std::endl; + } + + void print_something_novirt() { + std::cout << "ExampleSubClass::print_something_novirt()" << std::endl; + } +}; + +int main() +{ + printf("Hello World, C!\n"); + + std::cout << "Hello World, C++!" << std::endl; + + ExampleBaseClass *obj = new ExampleBaseClass; + obj->print_something_virt(); + obj->print_something_novirt(); + delete obj; + + obj = new ExampleSubClass; + obj->print_something_virt(); + obj->print_something_novirt(); + delete obj; + + std::vector some_ints; + some_ints.push_back(0x48c9b3e4); + some_ints.push_back(0x79109b6a); + some_ints.push_back(0x16155039); + some_ints.push_back(0xa3635c9a); + some_ints.push_back(0x8d2f4702); + some_ints.push_back(0x38d232ae); + some_ints.push_back(0x93924a17); + some_ints.push_back(0x62b895cc); + some_ints.push_back(0x6130d459); + some_ints.push_back(0x837c8b44); + some_ints.push_back(0x3d59b4fe); + some_ints.push_back(0x444914d8); + some_ints.push_back(0x3a3dc660); + some_ints.push_back(0xe5a121ef); + some_ints.push_back(0xff00866d); + some_ints.push_back(0xb843b879); + + std::sort(some_ints.begin(), some_ints.end()); + + for (auto n : some_ints) + std::cout << std::hex << n << std::endl; + + std::cout << "All done." << std::endl; + return 0; +} diff --git a/scripts/cxxdemo/hex8tohex32.py b/scripts/cxxdemo/hex8tohex32.py new file mode 100644 index 0000000..ae44101 --- /dev/null +++ b/scripts/cxxdemo/hex8tohex32.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import fileinput +import itertools + +ptr = 0 +data = [] + +def write_data(): + if len(data) != 0: + print("@%08x" % (ptr >> 2)) + while len(data) % 4 != 0: + data.append(0) + for word_bytes in zip(*([iter(data)]*4)): + print("".join(["%02x" % b for b in reversed(word_bytes)])) + +for line in fileinput.input(): + if line.startswith("@"): + addr = int(line[1:], 16) + if addr > ptr+4: + write_data() + ptr = addr + data = [] + while ptr % 4 != 0: + data.append(0) + ptr -= 1 + else: + while ptr + len(data) < addr: + data.append(0) + else: + data += [int(tok, 16) for tok in line.split()] + +write_data() + diff --git a/scripts/cxxdemo/start.S b/scripts/cxxdemo/start.S new file mode 100644 index 0000000..f872a14 --- /dev/null +++ b/scripts/cxxdemo/start.S @@ -0,0 +1,52 @@ +.section .text +.global _ftext +.global _pvstart + +_pvstart: +/* zero-initialize all registers */ +addi x1, zero, 0 +addi x2, zero, 0 +addi x3, zero, 0 +addi x4, zero, 0 +addi x5, zero, 0 +addi x6, zero, 0 +addi x7, zero, 0 +addi x8, zero, 0 +addi x9, zero, 0 +addi x10, zero, 0 +addi x11, zero, 0 +addi x12, zero, 0 +addi x13, zero, 0 +addi x14, zero, 0 +addi x15, zero, 0 +addi x16, zero, 0 +addi x17, zero, 0 +addi x18, zero, 0 +addi x19, zero, 0 +addi x20, zero, 0 +addi x21, zero, 0 +addi x22, zero, 0 +addi x23, zero, 0 +addi x24, zero, 0 +addi x25, zero, 0 +addi x26, zero, 0 +addi x27, zero, 0 +addi x28, zero, 0 +addi x29, zero, 0 +addi x30, zero, 0 +addi x31, zero, 0 + +/* set stack pointer */ +lui sp, %hi(4*1024*1024) +addi sp, sp, %lo(4*1024*1024) + +/* push zeros on the stack for argc and argv */ +/* (stack is aligned to 16 bytes in riscv calling convention) */ +addi sp,sp,-16 +sw zero,0(sp) +sw zero,4(sp) +sw zero,8(sp) +sw zero,12(sp) + +/* jump to libc init */ +j _ftext diff --git a/scripts/cxxdemo/start.ld b/scripts/cxxdemo/start.ld new file mode 100644 index 0000000..773eee2 --- /dev/null +++ b/scripts/cxxdemo/start.ld @@ -0,0 +1,5 @@ +SECTIONS { +. = 0x00000000; +.text : { *(.text) } +_ftext = 0x00010000; +} diff --git a/scripts/cxxdemo/syscalls.c b/scripts/cxxdemo/syscalls.c new file mode 100644 index 0000000..8ea84ca --- /dev/null +++ b/scripts/cxxdemo/syscalls.c @@ -0,0 +1,95 @@ +// An extremely minimalist syscalls.c for newlib +// Based on riscv newlib libgloss/riscv/sys_*.c +// Written by Clifford Wolf. + +#include +#include +#include + +#define UNIMPL_FUNC(_f) ".globl " #_f "\n.type " #_f ", @function\n" #_f ":\n" + +asm ( + ".text\n" + ".align 2\n" + UNIMPL_FUNC(_open) + UNIMPL_FUNC(_openat) + UNIMPL_FUNC(_lseek) + UNIMPL_FUNC(_stat) + UNIMPL_FUNC(_lstat) + UNIMPL_FUNC(_fstatat) + UNIMPL_FUNC(_isatty) + UNIMPL_FUNC(_access) + UNIMPL_FUNC(_faccessat) + UNIMPL_FUNC(_link) + UNIMPL_FUNC(_unlink) + UNIMPL_FUNC(_execve) + UNIMPL_FUNC(_getpid) + UNIMPL_FUNC(_fork) + UNIMPL_FUNC(_kill) + UNIMPL_FUNC(_wait) + UNIMPL_FUNC(_times) + UNIMPL_FUNC(_gettimeofday) + UNIMPL_FUNC(_ftime) + UNIMPL_FUNC(_utime) + UNIMPL_FUNC(_chown) + UNIMPL_FUNC(_chmod) + UNIMPL_FUNC(_chdir) + UNIMPL_FUNC(_getcwd) + UNIMPL_FUNC(_sysconf) + "j unimplemented_syscall\n" +); + +void unimplemented_syscall() +{ + const char *p = "Unimplemented system call called!\n"; + while (*p) + *(volatile int*)0x10000000 = *(p++); + asm volatile ("ebreak"); + __builtin_unreachable(); +} + +ssize_t _read(int file, void *ptr, size_t len) +{ + // always EOF + return 0; +} + +ssize_t _write(int file, const void *ptr, size_t len) +{ + const void *eptr = ptr + len; + while (ptr != eptr) + *(volatile int*)0x10000000 = *(char*)(ptr++); + return len; +} + +int _close(int file) +{ + // close is called before _exit() + return 0; +} + +int _fstat(int file, struct stat *st) +{ + // fstat is called during libc startup + errno = ENOENT; + return -1; +} + +void *_sbrk(ptrdiff_t incr) +{ + extern unsigned char _end[]; // Defined by linker + static unsigned long heap_end; + + if (heap_end == 0) + heap_end = (long)_end; + + heap_end += incr; + return (void *)(heap_end - incr); +} + +void _exit(int exit_status) +{ + asm volatile ("ebreak"); + __builtin_unreachable(); +} + diff --git a/scripts/cxxdemo/testbench.v b/scripts/cxxdemo/testbench.v new file mode 100644 index 0000000..ac9af70 --- /dev/null +++ b/scripts/cxxdemo/testbench.v @@ -0,0 +1,114 @@ +`timescale 1 ns / 1 ps +`undef VERBOSE_MEM +`undef WRITE_VCD +`undef MEM8BIT + +module testbench; + reg clk = 1; + reg resetn = 0; + wire trap; + + always #5 clk = ~clk; + + initial begin + repeat (100) @(posedge clk); + resetn <= 1; + end + + 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; + + picorv32 #( + .COMPRESSED_ISA(1) + ) 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 ) + ); + + localparam MEM_SIZE = 4*1024*1024; +`ifdef MEM8BIT + reg [7:0] memory [0:MEM_SIZE-1]; + initial $readmemh("firmware.hex", memory); +`else + reg [31:0] memory [0:MEM_SIZE/4-1]; + initial $readmemh("firmware32.hex", memory); +`endif + + always @(posedge clk) begin + mem_ready <= 0; + if (mem_valid && !mem_ready) begin + mem_ready <= 1; + mem_rdata <= 'bx; + case (1) + mem_addr < MEM_SIZE: begin +`ifdef MEM8BIT + if (|mem_wstrb) begin + if (mem_wstrb[0]) memory[mem_addr + 0] <= mem_wdata[ 7: 0]; + if (mem_wstrb[1]) memory[mem_addr + 1] <= mem_wdata[15: 8]; + if (mem_wstrb[2]) memory[mem_addr + 2] <= mem_wdata[23:16]; + if (mem_wstrb[3]) memory[mem_addr + 3] <= mem_wdata[31:24]; + end else begin + mem_rdata <= {memory[mem_addr+3], memory[mem_addr+2], memory[mem_addr+1], memory[mem_addr]}; + end +`else + 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 +`endif + end + mem_addr == 32'h 1000_0000: begin + $write("%c", mem_wdata[7:0]); + end + endcase + end + if (mem_valid && mem_ready) begin +`ifdef VERBOSE_MEM + if (|mem_wstrb) + $display("WR: ADDR=%x DATA=%x MASK=%b", mem_addr, mem_wdata, mem_wstrb); + else + $display("RD: ADDR=%x DATA=%x%s", mem_addr, mem_rdata, mem_instr ? " INSN" : ""); +`endif + if (^mem_addr === 1'bx || + (mem_wstrb[0] && ^mem_wdata[ 7: 0] == 1'bx) || + (mem_wstrb[1] && ^mem_wdata[15: 8] == 1'bx) || + (mem_wstrb[2] && ^mem_wdata[23:16] == 1'bx) || + (mem_wstrb[3] && ^mem_wdata[31:24] == 1'bx)) begin + $display("CRITICAL UNDEF MEM TRANSACTION"); + $finish; + end + end + end + +`ifdef WRITE_VCD + initial begin + $dumpfile("testbench.vcd"); + $dumpvars(0, testbench); + end +`endif + + always @(posedge clk) begin + if (resetn && trap) begin + repeat (10) @(posedge clk); + $display("TRAP"); + $finish; + end + end +endmodule diff --git a/scripts/icestorm/.gitignore b/scripts/icestorm/.gitignore new file mode 100644 index 0000000..9502e6b --- /dev/null +++ b/scripts/icestorm/.gitignore @@ -0,0 +1,5 @@ +synth.blif +synth.log +synth.bin +synth.txt +firmware.hex diff --git a/scripts/icestorm/Makefile b/scripts/icestorm/Makefile new file mode 100644 index 0000000..7672b41 --- /dev/null +++ b/scripts/icestorm/Makefile @@ -0,0 +1,104 @@ +TOOLCHAIN_PREFIX = riscv32-unknown-elf- + +ICE40_SIM_CELLS=$(shell yosys-config --datdir/ice40/cells_sim.v) + +# set to 4 for simulation +FIRMWARE_COUNTER_BITS=18 + +all: example.bin + +## ------------------- +## firmware generation + +firmware.elf: firmware.S firmware.c firmware.lds + $(TOOLCHAIN_PREFIX)gcc \ + -DSHIFT_COUNTER_BITS=$(FIRMWARE_COUNTER_BITS) \ + -march=rv32i -Os -ffreestanding -nostdlib \ + -o $@ firmware.S firmware.c \ + --std=gnu99 -Wl,-Bstatic,-T,firmware.lds,-Map,firmware.map,--strip-debug + chmod -x $@ + +firmware.bin: firmware.elf + $(TOOLCHAIN_PREFIX)objcopy -O binary $< $@ + chmod -x $@ + +firmware.hex: firmware.bin + python3 ../../firmware/makehex.py $< 128 > $@ + +## ------------------------------ +## main flow: synth/p&r/bitstream + +synth.json: example.v ../../picorv32.v firmware.hex + yosys -v3 -l synth.log -p 'synth_ice40 -top top -json $@; write_verilog -attr2comment synth.v' $(filter %.v, $^) + +example.asc: synth.json example.pcf + nextpnr-ice40 --hx8k --package ct256 --json $< --pcf example.pcf --asc $@ + +example.bin: example.asc + icepack $< $@ + +## ----------------- +## icarus simulation + +example_tb.vvp: example.v example_tb.v ../../picorv32.v firmware.hex + iverilog -o $@ -s testbench $(filter %.v, $^) + chmod -x $@ + +example_sim: example_tb.vvp + vvp -N $< + +example_sim_vcd: example_tb.vvp + vvp -N $< +vcd + +## --------------------- +## post-synth simulation + +synth_tb.vvp: example_tb.v synth.json + iverilog -o $@ -s testbench synth.v example_tb.v $(ICE40_SIM_CELLS) + chmod -x $@ + +synth_sim: synth_tb.vvp + vvp -N $< + +synth_sim_vcd: synth_tb.vvp + vvp -N $< +vcd + +## --------------------- +## post-route simulation + +route.v: example.asc example.pcf + icebox_vlog -L -n top -sp example.pcf $< > $@ + +route_tb.vvp: route.v example_tb.v + iverilog -o $@ -s testbench $^ $(ICE40_SIM_CELLS) + chmod -x $@ + +route_sim: route_tb.vvp + vvp -N $< + +route_sim_vcd: route_tb.vvp + vvp -N $< +vcd + +## --------------------- +## miscellaneous targets + +prog_sram: example.bin + iceprog -S $< + +timing: example.asc example.pcf + icetime -c 62 -tmd hx8k -P ct256 -p example.pcf -t $< + +view: example.vcd + gtkwave $< example.gtkw + +## ------ +## el fin + +clean: + rm -f firmware.elf firmware.map firmware.bin firmware.hex + rm -f synth.log synth.v synth.json route.v example.asc example.bin + rm -f example_tb.vvp synth_tb.vvp route_tb.vvp example.vcd + +.PHONY: all prog_sram view clean +.PHONY: example_sim synth_sim route_sim timing +.PHONY: example_sim_vcd synth_sim_vcd route_sim_vcd diff --git a/scripts/icestorm/example.pcf b/scripts/icestorm/example.pcf new file mode 100644 index 0000000..a5c7398 --- /dev/null +++ b/scripts/icestorm/example.pcf @@ -0,0 +1,9 @@ +set_io clk J3 +set_io LED0 B5 +set_io LED1 B4 +set_io LED2 A2 +set_io LED3 A1 +set_io LED4 C5 +set_io LED5 C4 +set_io LED6 B3 +set_io LED7 C3 diff --git a/scripts/icestorm/example.v b/scripts/icestorm/example.v new file mode 100644 index 0000000..e1c64b4 --- /dev/null +++ b/scripts/icestorm/example.v @@ -0,0 +1,80 @@ +`timescale 1 ns / 1 ps + +module top ( + input clk, + output reg LED0, LED1, LED2, LED3, LED4, LED5, LED6, LED7 +); + // ------------------------------- + // Reset Generator + + reg [7:0] resetn_counter = 0; + wire resetn = &resetn_counter; + + always @(posedge clk) begin + if (!resetn) + resetn_counter <= resetn_counter + 1; + end + + + // ------------------------------- + // PicoRV32 Core + + wire mem_valid; + wire [31:0] mem_addr; + wire [31:0] mem_wdata; + wire [3:0] mem_wstrb; + + reg mem_ready; + reg [31:0] mem_rdata; + + picorv32 #( + .ENABLE_COUNTERS(0), + .LATCHED_MEM_RDATA(1), + .TWO_STAGE_SHIFT(0), + .TWO_CYCLE_ALU(1), + .CATCH_MISALIGN(0), + .CATCH_ILLINSN(0) + ) cpu ( + .clk (clk ), + .resetn (resetn ), + .mem_valid(mem_valid), + .mem_ready(mem_ready), + .mem_addr (mem_addr ), + .mem_wdata(mem_wdata), + .mem_wstrb(mem_wstrb), + .mem_rdata(mem_rdata) + ); + + + // ------------------------------- + // Memory/IO Interface + + // 128 32bit words = 512 bytes memory + localparam MEM_SIZE = 128; + reg [31:0] memory [0:MEM_SIZE-1]; + initial $readmemh("firmware.hex", memory); + + always @(posedge clk) begin + mem_ready <= 0; + if (resetn && mem_valid && !mem_ready) begin + (* parallel_case *) + case (1) + !mem_wstrb && (mem_addr >> 2) < MEM_SIZE: begin + mem_rdata <= memory[mem_addr >> 2]; + mem_ready <= 1; + end + |mem_wstrb && (mem_addr >> 2) < MEM_SIZE: 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]; + mem_ready <= 1; + end + |mem_wstrb && mem_addr == 32'h1000_0000: begin + {LED7, LED6, LED5, LED4, LED3, LED2, LED1, LED0} <= mem_wdata; + mem_ready <= 1; + end + endcase + end + end +endmodule diff --git a/scripts/icestorm/example_tb.v b/scripts/icestorm/example_tb.v new file mode 100644 index 0000000..f04f8f8 --- /dev/null +++ b/scripts/icestorm/example_tb.v @@ -0,0 +1,30 @@ +`timescale 1 ns / 1 ps + +module testbench; + reg clk = 1; + always #5 clk = ~clk; + wire LED0, LED1, LED2, LED3, LED4, LED5, LED6, LED7; + + top uut ( + .clk(clk), + .LED0(LED0), + .LED1(LED1), + .LED2(LED2), + .LED3(LED3), + .LED4(LED4), + .LED5(LED5), + .LED6(LED6), + .LED7(LED7) + ); + + initial begin + if ($test$plusargs("vcd")) begin + $dumpfile("example.vcd"); + $dumpvars(0, testbench); + end + + $monitor(LED7, LED6, LED5, LED4, LED3, LED2, LED1, LED0); + repeat (10000) @(posedge clk); + $finish; + end +endmodule diff --git a/scripts/icestorm/firmware.S b/scripts/icestorm/firmware.S new file mode 100644 index 0000000..d19783d --- /dev/null +++ b/scripts/icestorm/firmware.S @@ -0,0 +1,12 @@ +.section .init +.global main + +/* set stack pointer */ +lui sp, %hi(512) +addi sp, sp, %lo(512) + +/* call main */ +jal ra, main + +/* break */ +ebreak diff --git a/scripts/icestorm/firmware.c b/scripts/icestorm/firmware.c new file mode 100644 index 0000000..80a4661 --- /dev/null +++ b/scripts/icestorm/firmware.c @@ -0,0 +1,58 @@ +#include + +#ifndef SHIFT_COUNTER_BITS +#error SHIFT_COUNTER_BITS must be defined as 4 (for simulation) or 18 (for hardware bitstreams)! +#endif + +void output(uint8_t c) +{ + *(volatile char*)0x10000000 = c; +} + +uint8_t gray_encode_simple(uint8_t c) +{ + return c ^ (c >> 1); +} + +uint8_t gray_encode_bitwise(uint8_t c) +{ + unsigned int in_buf = c, out_buf = 0, bit = 1; + for (int i = 0; i < 8; i++) { + if ((in_buf & 1) ^ ((in_buf >> 1) & 1)) + out_buf |= bit; + in_buf = in_buf >> 1; + bit = bit << 1; + } + return out_buf; +} + +uint8_t gray_decode(uint8_t c) +{ + uint8_t t = c >> 1; + while (t) { + c = c ^ t; + t = t >> 1; + } + return c; +} + +void gray(uint8_t c) +{ + uint8_t gray_simple = gray_encode_simple(c); + uint8_t gray_bitwise = gray_encode_bitwise(c); + uint8_t gray_decoded = gray_decode(gray_simple); + + if (gray_simple != gray_bitwise || gray_decoded != c) + while (1) asm volatile ("ebreak"); + + output(gray_simple); +} + +void main() +{ + for (uint32_t counter = (2+4+32+64) << SHIFT_COUNTER_BITS;; counter++) { + asm volatile ("" : : "r"(counter)); + if ((counter & ~(~0 << SHIFT_COUNTER_BITS)) == 0) + gray(counter >> SHIFT_COUNTER_BITS); + } +} diff --git a/scripts/icestorm/firmware.lds b/scripts/icestorm/firmware.lds new file mode 100644 index 0000000..970000a --- /dev/null +++ b/scripts/icestorm/firmware.lds @@ -0,0 +1,11 @@ +SECTIONS { + .memory : { + . = 0x000000; + *(.init); + *(.text); + *(*); + . = ALIGN(4); + end = .; + } +} + diff --git a/scripts/icestorm/readme.md b/scripts/icestorm/readme.md new file mode 100644 index 0000000..101391f --- /dev/null +++ b/scripts/icestorm/readme.md @@ -0,0 +1,12 @@ +To build the example LED-blinking firmware for an HX8K Breakout Board and get +a timing report (checked against the default 12MHz oscillator): + + $ make clean example.bin timing + +To run all the simulation tests: + + $ make clean example_sim synth_sim route_sim FIRMWARE_COUNTER_BITS=4 + +(You must run the `clean` target to rebuild the firmware with the updated +`FIRMWARE_COUNTER_BITS` parameter; the firmware source must be recompiled for +simulation vs hardware, but this is not tracked as a Makefile dependency.) diff --git a/scripts/presyn/.gitignore b/scripts/presyn/.gitignore new file mode 100644 index 0000000..4281b17 --- /dev/null +++ b/scripts/presyn/.gitignore @@ -0,0 +1,7 @@ +firmware.bin +firmware.elf +firmware.hex +firmware.map +picorv32_presyn.v +testbench.vcd +testbench.vvp diff --git a/scripts/presyn/Makefile b/scripts/presyn/Makefile new file mode 100644 index 0000000..d1c367e --- /dev/null +++ b/scripts/presyn/Makefile @@ -0,0 +1,22 @@ + +TOOLCHAIN_PREFIX = /opt/riscv32ic/bin/riscv32-unknown-elf- + +run: testbench.vvp firmware.hex + vvp -N testbench.vvp + +firmware.hex: firmware.S firmware.c firmware.lds + $(TOOLCHAIN_PREFIX)gcc -Os -ffreestanding -nostdlib -o firmware.elf firmware.S firmware.c \ + --std=gnu99 -Wl,-Bstatic,-T,firmware.lds,-Map,firmware.map,--strip-debug -lgcc + $(TOOLCHAIN_PREFIX)objcopy -O binary firmware.elf firmware.bin + python3 ../../firmware/makehex.py firmware.bin 4096 > firmware.hex + +picorv32_presyn.v: picorv32_presyn.ys picorv32_regs.txt ../../picorv32.v + yosys -v0 picorv32_presyn.ys + +testbench.vvp: testbench.v picorv32_presyn.v + iverilog -o testbench.vvp testbench.v picorv32_presyn.v + +clean: + rm -f firmware.bin firmware.elf firmware.hex firmware.map + rm -f picorv32_presyn.v testbench.vvp testbench.vcd + diff --git a/scripts/presyn/README b/scripts/presyn/README new file mode 100644 index 0000000..14aea33 --- /dev/null +++ b/scripts/presyn/README @@ -0,0 +1,5 @@ +A simple example for how to use Yosys to "pre-synthesize" PicoRV32 in +a way that can utilize an external memory module for the register file. + +See also: +https://github.com/cliffordwolf/picorv32/issues/30 diff --git a/scripts/presyn/firmware.S b/scripts/presyn/firmware.S new file mode 100644 index 0000000..ec5caaa --- /dev/null +++ b/scripts/presyn/firmware.S @@ -0,0 +1,47 @@ +.section .init +.global main + +entry: + +/* zero-initialize all registers */ +addi x1, zero, 0 +addi x2, zero, 0 +addi x3, zero, 0 +addi x4, zero, 0 +addi x5, zero, 0 +addi x6, zero, 0 +addi x7, zero, 0 +addi x8, zero, 0 +addi x9, zero, 0 +addi x10, zero, 0 +addi x11, zero, 0 +addi x12, zero, 0 +addi x13, zero, 0 +addi x14, zero, 0 +addi x15, zero, 0 +addi x16, zero, 0 +addi x17, zero, 0 +addi x18, zero, 0 +addi x19, zero, 0 +addi x20, zero, 0 +addi x21, zero, 0 +addi x22, zero, 0 +addi x23, zero, 0 +addi x24, zero, 0 +addi x25, zero, 0 +addi x26, zero, 0 +addi x27, zero, 0 +addi x28, zero, 0 +addi x29, zero, 0 +addi x30, zero, 0 +addi x31, zero, 0 + +/* set stack pointer */ +lui sp, %hi(16*1024) +addi sp, sp, %lo(16*1024) + +/* call main */ +jal ra, main + +/* break */ +ebreak diff --git a/scripts/presyn/firmware.c b/scripts/presyn/firmware.c new file mode 100644 index 0000000..6c62169 --- /dev/null +++ b/scripts/presyn/firmware.c @@ -0,0 +1,43 @@ +void putc(char c) +{ + *(volatile char*)0x10000000 = c; +} + +void puts(const char *s) +{ + while (*s) putc(*s++); +} + +void *memcpy(void *dest, const void *src, int n) +{ + while (n) { + n--; + ((char*)dest)[n] = ((char*)src)[n]; + } + return dest; +} + +void main() +{ + char message[] = "$Uryyb+Jbeyq!+Vs+lbh+pna+ernq+guvf+zrffntr+gura$gur+CvpbEI32+PCH" + "+frrzf+gb+or+jbexvat+whfg+svar.$$++++++++++++++++GRFG+CNFFRQ!$$"; + for (int i = 0; message[i]; i++) + switch (message[i]) + { + case 'a' ... 'm': + case 'A' ... 'M': + message[i] += 13; + break; + case 'n' ... 'z': + case 'N' ... 'Z': + message[i] -= 13; + break; + case '$': + message[i] = '\n'; + break; + case '+': + message[i] = ' '; + break; + } + puts(message); +} diff --git a/scripts/presyn/firmware.lds b/scripts/presyn/firmware.lds new file mode 100644 index 0000000..970000a --- /dev/null +++ b/scripts/presyn/firmware.lds @@ -0,0 +1,11 @@ +SECTIONS { + .memory : { + . = 0x000000; + *(.init); + *(.text); + *(*); + . = ALIGN(4); + end = .; + } +} + diff --git a/scripts/presyn/picorv32_presyn.ys b/scripts/presyn/picorv32_presyn.ys new file mode 100644 index 0000000..5855a21 --- /dev/null +++ b/scripts/presyn/picorv32_presyn.ys @@ -0,0 +1,5 @@ +read_verilog ../../picorv32.v +chparam -set COMPRESSED_ISA 1 picorv32 +prep -top picorv32 +memory_bram -rules picorv32_regs.txt +write_verilog -noattr picorv32_presyn.v diff --git a/scripts/presyn/picorv32_regs.txt b/scripts/presyn/picorv32_regs.txt new file mode 100644 index 0000000..1cfbbeb --- /dev/null +++ b/scripts/presyn/picorv32_regs.txt @@ -0,0 +1,16 @@ +bram picorv32_regs + init 0 + abits 5 + dbits 32 + groups 2 + ports 2 1 + wrmode 0 1 + enable 0 1 + transp 0 0 + clocks 1 1 + clkpol 1 1 +endbram + +match picorv32_regs + make_transp +endmatch diff --git a/scripts/presyn/testbench.v b/scripts/presyn/testbench.v new file mode 100644 index 0000000..59ff66b --- /dev/null +++ b/scripts/presyn/testbench.v @@ -0,0 +1,81 @@ +module testbench; + reg clk = 1; + always #5 clk = ~clk; + + reg resetn = 0; + always @(posedge clk) resetn <= 1; + + 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; + + picorv32 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) + ); + + // 4096 32bit words = 16kB memory + localparam MEM_SIZE = 4096; + + reg [31:0] memory [0:MEM_SIZE-1]; + initial $readmemh("firmware.hex", memory); + + always @(posedge clk) begin + mem_ready <= 0; + mem_rdata <= 'bx; + + if (resetn && mem_valid && !mem_ready) begin + mem_ready <= 1; + if (mem_wstrb) begin + if (mem_addr == 32'h1000_0000) begin + $write("%c", mem_wdata[7:0]); + $fflush; + end else 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 + end else begin + mem_rdata <= memory[mem_addr >> 2]; + end + end + + if (resetn && trap) begin + $display("TRAP."); + $finish; + end + end + + initial begin + $dumpfile("testbench.vcd"); + $dumpvars(0, testbench); + end +endmodule + +module picorv32_regs ( + input [4:0] A1ADDR, A2ADDR, B1ADDR, + output reg [31:0] A1DATA, A2DATA, + input [31:0] B1DATA, + input B1EN, CLK1 +); + reg [31:0] memory [0:31]; + always @(posedge CLK1) begin + A1DATA <= memory[A1ADDR]; + A2DATA <= memory[A2ADDR]; + if (B1EN) memory[B1ADDR] <= B1DATA; + end +endmodule diff --git a/scripts/quartus/.gitignore b/scripts/quartus/.gitignore new file mode 100644 index 0000000..d37ca0a --- /dev/null +++ b/scripts/quartus/.gitignore @@ -0,0 +1,11 @@ +firmware.bin +firmware.elf +firmware.hex +firmware.map +synth_*.log +synth_*.mmi +synth_*.bit +synth_system.v +table.txt +tab_*/ +system_tb diff --git a/scripts/quartus/Makefile b/scripts/quartus/Makefile new file mode 100644 index 0000000..c644609 --- /dev/null +++ b/scripts/quartus/Makefile @@ -0,0 +1,62 @@ + +export QUARTUS_ROOTDIR = /opt/altera_lite/16.0 +export QUARTUS_BIN = $(QUARTUS_ROOTDIR)/quartus/bin + +VLOG = iverilog +TOOLCHAIN_PREFIX = /opt/riscv32i/bin/riscv32-unknown-elf- + +help: + @echo "" + @echo "Simple synthesis tests:" + @echo " make synth_area_{small|regular|large}" + @echo " make synth_speed" + @echo "" + @echo "Example system:" + @echo " make synth_system" + @echo " make sim_system" + @echo "" + @echo "Timing and Utilization Evaluation:" + @echo " make table.txt" + @echo " make area" + @echo "" + +synth_%: + rm -f $@.log + mkdir -p $@_build + cp $@.qsf $@_build + cd $@_build && $(QUARTUS_BIN)/quartus_map $@.qsf + cd $@_build && $(QUARTUS_BIN)/quartus_fit --read_settings_files=off -write_settings_files=off $@ -c $@ + cd $@_build && $(QUARTUS_BIN)/quartus_sta $@ -c $@ + -cd $@_build && grep -A3 "Total logic elements" output_files/$@.fit.summary + -cd $@_build && grep -B1 "Slack" output_files/$@.sta.summary + +synth_system: firmware.hex + +sim_system: firmware.hex system_tb.v system.v ../../picorv32.v + $(VLOG) -o system_tb system_tb.v system.v ../../picorv32.v + ./system_tb + +firmware.hex: firmware.S firmware.c firmware.lds + $(TOOLCHAIN_PREFIX)gcc -Os -ffreestanding -nostdlib -o firmware.elf firmware.S firmware.c \ + --std=gnu99 -Wl,-Bstatic,-T,firmware.lds,-Map,firmware.map,--strip-debug -lgcc + $(TOOLCHAIN_PREFIX)objcopy -O binary firmware.elf firmware.bin + python3 ../../firmware/makehex.py firmware.bin 4096 > firmware.hex + +tab_%/results.txt: + bash tabtest.sh $@ + +area: synth_area_small synth_area_regular synth_area_large + -grep -A3 "Total logic elements" synth_area_*_build/output_files/synth_area_*.fit.summary + +table.txt: tab_small_ep4ce_c7/results.txt +table.txt: tab_small_ep4cgx_c7/results.txt +table.txt: tab_small_5cgx_c7/results.txt + +table.txt: + bash table.sh > table.txt + +clean: + rm -rf firmware.bin firmware.elf firmware.hex firmware.map synth_*.log + rm -rf table.txt tab_*/ + rm -rf synth_*_build + diff --git a/scripts/quartus/firmware.S b/scripts/quartus/firmware.S new file mode 100644 index 0000000..c55a3ba --- /dev/null +++ b/scripts/quartus/firmware.S @@ -0,0 +1,12 @@ +.section .init +.global main + +/* set stack pointer */ +lui sp, %hi(16*1024) +addi sp, sp, %lo(16*1024) + +/* call main */ +jal ra, main + +/* break */ +ebreak diff --git a/scripts/quartus/firmware.c b/scripts/quartus/firmware.c new file mode 100644 index 0000000..6c62169 --- /dev/null +++ b/scripts/quartus/firmware.c @@ -0,0 +1,43 @@ +void putc(char c) +{ + *(volatile char*)0x10000000 = c; +} + +void puts(const char *s) +{ + while (*s) putc(*s++); +} + +void *memcpy(void *dest, const void *src, int n) +{ + while (n) { + n--; + ((char*)dest)[n] = ((char*)src)[n]; + } + return dest; +} + +void main() +{ + char message[] = "$Uryyb+Jbeyq!+Vs+lbh+pna+ernq+guvf+zrffntr+gura$gur+CvpbEI32+PCH" + "+frrzf+gb+or+jbexvat+whfg+svar.$$++++++++++++++++GRFG+CNFFRQ!$$"; + for (int i = 0; message[i]; i++) + switch (message[i]) + { + case 'a' ... 'm': + case 'A' ... 'M': + message[i] += 13; + break; + case 'n' ... 'z': + case 'N' ... 'Z': + message[i] -= 13; + break; + case '$': + message[i] = '\n'; + break; + case '+': + message[i] = ' '; + break; + } + puts(message); +} diff --git a/scripts/quartus/firmware.lds b/scripts/quartus/firmware.lds new file mode 100644 index 0000000..970000a --- /dev/null +++ b/scripts/quartus/firmware.lds @@ -0,0 +1,11 @@ +SECTIONS { + .memory : { + . = 0x000000; + *(.init); + *(.text); + *(*); + . = ALIGN(4); + end = .; + } +} + diff --git a/scripts/quartus/synth_area.sdc b/scripts/quartus/synth_area.sdc new file mode 100644 index 0000000..3c3d5a1 --- /dev/null +++ b/scripts/quartus/synth_area.sdc @@ -0,0 +1 @@ +create_clock -period 20.00 [get_ports clk] diff --git a/scripts/quartus/synth_area_large.qsf b/scripts/quartus/synth_area_large.qsf new file mode 100644 index 0000000..c09700b --- /dev/null +++ b/scripts/quartus/synth_area_large.qsf @@ -0,0 +1,6 @@ +set_global_assignment -name DEVICE ep4ce40f29c7 +set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files +set_global_assignment -name TOP_LEVEL_ENTITY top_large +set_global_assignment -name VERILOG_FILE ../synth_area_top.v +set_global_assignment -name VERILOG_FILE ../../../picorv32.v +set_global_assignment -name SDC_FILE ../synth_area.sdc diff --git a/scripts/quartus/synth_area_regular.qsf b/scripts/quartus/synth_area_regular.qsf new file mode 100644 index 0000000..8507413 --- /dev/null +++ b/scripts/quartus/synth_area_regular.qsf @@ -0,0 +1,6 @@ +set_global_assignment -name DEVICE ep4ce40f29c7 +set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files +set_global_assignment -name TOP_LEVEL_ENTITY top_regular +set_global_assignment -name VERILOG_FILE ../synth_area_top.v +set_global_assignment -name VERILOG_FILE ../../../picorv32.v +set_global_assignment -name SDC_FILE ../synth_area.sdc diff --git a/scripts/quartus/synth_area_small.qsf b/scripts/quartus/synth_area_small.qsf new file mode 100644 index 0000000..048ff96 --- /dev/null +++ b/scripts/quartus/synth_area_small.qsf @@ -0,0 +1,6 @@ +set_global_assignment -name DEVICE ep4ce40f29c7 +set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files +set_global_assignment -name TOP_LEVEL_ENTITY top_small +set_global_assignment -name VERILOG_FILE ../synth_area_top.v +set_global_assignment -name VERILOG_FILE ../../../picorv32.v +set_global_assignment -name SDC_FILE ../synth_area.sdc diff --git a/scripts/quartus/synth_area_top.v b/scripts/quartus/synth_area_top.v new file mode 100644 index 0000000..6298a86 --- /dev/null +++ b/scripts/quartus/synth_area_top.v @@ -0,0 +1,140 @@ + +module top_small ( + input clk, resetn, + + output mem_valid, + output mem_instr, + input mem_ready, + + output [31:0] mem_addr, + output [31:0] mem_wdata, + output [ 3:0] mem_wstrb, + input [31:0] mem_rdata +); + picorv32 #( + .ENABLE_COUNTERS(0), + .LATCHED_MEM_RDATA(1), + .TWO_STAGE_SHIFT(0), + .CATCH_MISALIGN(0), + .CATCH_ILLINSN(0) + ) picorv32 ( + .clk (clk ), + .resetn (resetn ), + .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) + ); +endmodule + +module top_regular ( + input clk, resetn, + output trap, + + output mem_valid, + output mem_instr, + input mem_ready, + + output [31:0] mem_addr, + output [31:0] mem_wdata, + output [ 3:0] mem_wstrb, + input [31:0] mem_rdata, + + // Look-Ahead Interface + output mem_la_read, + output mem_la_write, + output [31:0] mem_la_addr, + output [31:0] mem_la_wdata, + output [ 3:0] mem_la_wstrb +); + picorv32 picorv32 ( + .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) + ); +endmodule + +module top_large ( + input clk, resetn, + output trap, + + output mem_valid, + output mem_instr, + input mem_ready, + + output [31:0] mem_addr, + output [31:0] mem_wdata, + output [ 3:0] mem_wstrb, + input [31:0] mem_rdata, + + // Look-Ahead Interface + output mem_la_read, + output mem_la_write, + output [31:0] mem_la_addr, + output [31:0] mem_la_wdata, + output [ 3:0] mem_la_wstrb, + + // Pico Co-Processor Interface (PCPI) + output pcpi_valid, + output [31:0] pcpi_insn, + output [31:0] pcpi_rs1, + output [31:0] pcpi_rs2, + input pcpi_wr, + input [31:0] pcpi_rd, + input pcpi_wait, + input pcpi_ready, + + // IRQ Interface + input [31:0] irq, + output [31:0] eoi +); + picorv32 #( + .COMPRESSED_ISA(1), + .BARREL_SHIFTER(1), + .ENABLE_PCPI(1), + .ENABLE_MUL(1), + .ENABLE_IRQ(1) + ) picorv32 ( + .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 ), + .pcpi_valid (pcpi_valid ), + .pcpi_insn (pcpi_insn ), + .pcpi_rs1 (pcpi_rs1 ), + .pcpi_rs2 (pcpi_rs2 ), + .pcpi_wr (pcpi_wr ), + .pcpi_rd (pcpi_rd ), + .pcpi_wait (pcpi_wait ), + .pcpi_ready (pcpi_ready ), + .irq (irq ), + .eoi (eoi ) + ); +endmodule + diff --git a/scripts/quartus/synth_speed.qsf b/scripts/quartus/synth_speed.qsf new file mode 100644 index 0000000..64490d4 --- /dev/null +++ b/scripts/quartus/synth_speed.qsf @@ -0,0 +1,5 @@ +set_global_assignment -name DEVICE ep4ce40f29c7 +set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files +set_global_assignment -name TOP_LEVEL_ENTITY picorv32_axi +set_global_assignment -name VERILOG_FILE ../../../picorv32.v +set_global_assignment -name SDC_FILE ../synth_speed.sdc diff --git a/scripts/quartus/synth_speed.sdc b/scripts/quartus/synth_speed.sdc new file mode 100644 index 0000000..fef5704 --- /dev/null +++ b/scripts/quartus/synth_speed.sdc @@ -0,0 +1 @@ +create_clock -period 2.5 [get_ports clk] diff --git a/scripts/quartus/synth_system.qsf b/scripts/quartus/synth_system.qsf new file mode 100644 index 0000000..1a84293 --- /dev/null +++ b/scripts/quartus/synth_system.qsf @@ -0,0 +1,6 @@ +set_global_assignment -name DEVICE ep4ce40f29c7 +set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files +set_global_assignment -name TOP_LEVEL_ENTITY system +set_global_assignment -name VERILOG_FILE ../system.v +set_global_assignment -name VERILOG_FILE ../../../picorv32.v +set_global_assignment -name SDC_FILE ../synth_system.sdc diff --git a/scripts/quartus/synth_system.sdc b/scripts/quartus/synth_system.sdc new file mode 100644 index 0000000..90ee3a2 --- /dev/null +++ b/scripts/quartus/synth_system.sdc @@ -0,0 +1 @@ +create_clock -period 10.00 [get_ports clk] diff --git a/scripts/quartus/synth_system.tcl b/scripts/quartus/synth_system.tcl new file mode 100644 index 0000000..26ea01c --- /dev/null +++ b/scripts/quartus/synth_system.tcl @@ -0,0 +1,17 @@ + +read_verilog system.v +read_verilog ../../picorv32.v +read_xdc synth_system.xdc + +synth_design -part xc7a35t-cpg236-1 -top system +opt_design +place_design +route_design + +report_utilization +report_timing + +write_verilog -force synth_system.v +write_bitstream -force synth_system.bit +# write_mem_info -force synth_system.mmi + diff --git a/scripts/quartus/system.v b/scripts/quartus/system.v new file mode 100644 index 0000000..19a4b8d --- /dev/null +++ b/scripts/quartus/system.v @@ -0,0 +1,101 @@ +`timescale 1 ns / 1 ps + +module system ( + input clk, + input resetn, + output trap, + output reg [7:0] out_byte, + output reg out_byte_en +); + // set this to 0 for better timing but less performance/MHz + parameter FAST_MEMORY = 0; + + // 4096 32bit words = 16kB memory + parameter MEM_SIZE = 4096; + + 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; + + picorv32 picorv32_core ( + .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) + ); + + reg [31:0] memory [0:MEM_SIZE-1]; + initial $readmemh("firmware.hex", memory); + + reg [31:0] m_read_data; + reg m_read_en; + + generate if (FAST_MEMORY) begin + always @(posedge clk) begin + mem_ready <= 1; + out_byte_en <= 0; + mem_rdata <= memory[mem_la_addr >> 2]; + if (mem_la_write && (mem_la_addr >> 2) < MEM_SIZE) begin + 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_la_write && mem_la_addr == 32'h1000_0000) begin + out_byte_en <= 1; + out_byte <= mem_la_wdata; + end + end + end else begin + always @(posedge clk) begin + m_read_en <= 0; + mem_ready <= mem_valid && !mem_ready && m_read_en; + + m_read_data <= memory[mem_addr >> 2]; + mem_rdata <= m_read_data; + + out_byte_en <= 0; + + (* parallel_case *) + case (1) + mem_valid && !mem_ready && !mem_wstrb && (mem_addr >> 2) < MEM_SIZE: begin + m_read_en <= 1; + end + mem_valid && !mem_ready && |mem_wstrb && (mem_addr >> 2) < MEM_SIZE: 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]; + mem_ready <= 1; + end + mem_valid && !mem_ready && |mem_wstrb && mem_addr == 32'h1000_0000: begin + out_byte_en <= 1; + out_byte <= mem_wdata; + mem_ready <= 1; + end + endcase + end + end endgenerate +endmodule diff --git a/scripts/quartus/system_tb.v b/scripts/quartus/system_tb.v new file mode 100644 index 0000000..a66d612 --- /dev/null +++ b/scripts/quartus/system_tb.v @@ -0,0 +1,38 @@ +`timescale 1 ns / 1 ps + +module system_tb; + reg clk = 1; + always #5 clk = ~clk; + + reg resetn = 0; + initial begin + if ($test$plusargs("vcd")) begin + $dumpfile("system.vcd"); + $dumpvars(0, system_tb); + end + repeat (100) @(posedge clk); + resetn <= 1; + end + + wire trap; + wire [7:0] out_byte; + wire out_byte_en; + + system uut ( + .clk (clk ), + .resetn (resetn ), + .trap (trap ), + .out_byte (out_byte ), + .out_byte_en(out_byte_en) + ); + + always @(posedge clk) begin + if (resetn && out_byte_en) begin + $write("%c", out_byte); + $fflush; + end + if (resetn && trap) begin + $finish; + end + end +endmodule diff --git a/scripts/quartus/table.sh b/scripts/quartus/table.sh new file mode 100644 index 0000000..f5e6efe --- /dev/null +++ b/scripts/quartus/table.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +dashes="----------------------------------------------------------------" +printf '| %-25s | %-10s | %-20s |\n' "Device" "Speedgrade" "Clock Period (Freq.)" +printf '|:%.25s |:%.10s:| %.20s:|\n' $dashes $dashes $dashes + +for x in $( grep -H . tab_*/results.txt ) +do + read _ size device grade _ speed < <( echo "$x" | tr _/: ' ' ) + case "$device" in + ep4ce) d="Altera Cyclone IV E" ;; + ep4cgx) d="Altera Cyclone IV GX" ;; + 5cgx) d="Altera Cyclone V GX" ;; + esac + speedtxt=$( printf '%s.%s ns (%d MHz)' ${speed%?} ${speed#?} $((10000 / speed)) ) + printf '| %-25s | %-10s | %20s |\n' "$d" "-$grade" "$speedtxt" +done diff --git a/scripts/quartus/tabtest.sh b/scripts/quartus/tabtest.sh new file mode 100644 index 0000000..2fd1b40 --- /dev/null +++ b/scripts/quartus/tabtest.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +set -e +read _ ip dev grade _ < <( echo $* | tr '_/' ' '; ) + +# rm -rf tab_${ip}_${dev}_${grade} +mkdir -p tab_${ip}_${dev}_${grade} +cd tab_${ip}_${dev}_${grade} + +max_speed=99 +min_speed=01 +best_speed=99 + +synth_case() { + if [ -f test_${1}.txt ]; then + echo "Reusing cached tab_${ip}_${dev}_${grade}/test_${1}." + return + fi + + case "${dev}" in + ep4ce) al_device="ep4ce30f23${grade}" ;; + ep4cgx) al_device="ep4cgx50df27${grade}" ;; + 5cgx) al_device="5cgxbc9c6f23${grade}" ;; + esac + + cat > test_${1}.qsf <<- EOT +set_global_assignment -name DEVICE ${al_device} +set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files +set_global_assignment -name TOP_LEVEL_ENTITY top +set_global_assignment -name VERILOG_FILE ../tabtest.v +set_global_assignment -name VERILOG_FILE ../../../picorv32.v +set_global_assignment -name SDC_FILE test_${1}.sdc + EOT + + cat > test_${1}.sdc <<- EOT + create_clock -period ${speed%?}.${speed#?} [get_ports clk] + EOT + + echo "Running tab_${ip}_${dev}_${grade}/test_${1}.." + + if ! $QUARTUS_BIN/quartus_map test_${1}; then + exit 1 + fi + if ! $QUARTUS_BIN/quartus_fit --read_settings_files=off --write_settings_files=off test_${1} -c test_${1}; then + exit 1 + fi + if ! $QUARTUS_BIN/quartus_sta test_${1} -c test_${1}; then + exit 1 + fi + + cp output_files/test_${1}.sta.summary test_${1}.txt +} + +countdown=7 +while [ $countdown -gt 0 ]; do + speed=$(((max_speed+min_speed)/2)) + synth_case $speed + + if grep -q '^Slack : -' test_${speed}.txt; then + echo " tab_${ip}_${dev}_${grade}/test_${speed} VIOLATED" + min_speed=$((speed)) + elif grep -q '^Slack : [^-]' test_${speed}.txt; then + echo " tab_${ip}_${dev}_${grade}/test_${speed} MET" + [ $speed -lt $best_speed ] && best_speed=$speed + max_speed=$((speed)) + else + echo "ERROR: No slack line found in $PWD/test_${speed}.txt!" + exit 1 + fi + + countdown=$((countdown-1)) +done + +echo "-----------------------" +echo "Best speed for tab_${ip}_${dev}_${grade}: $best_speed" +echo "-----------------------" +echo $best_speed > results.txt + diff --git a/scripts/quartus/tabtest.v b/scripts/quartus/tabtest.v new file mode 100644 index 0000000..cdf2057 --- /dev/null +++ b/scripts/quartus/tabtest.v @@ -0,0 +1,118 @@ + +module top ( + input clk, io_resetn, + output io_trap, + + output io_mem_axi_awvalid, + input io_mem_axi_awready, + output [31:0] io_mem_axi_awaddr, + output [ 2:0] io_mem_axi_awprot, + + output io_mem_axi_wvalid, + input io_mem_axi_wready, + output [31:0] io_mem_axi_wdata, + output [ 3:0] io_mem_axi_wstrb, + + input io_mem_axi_bvalid, + output io_mem_axi_bready, + + output io_mem_axi_arvalid, + input io_mem_axi_arready, + output [31:0] io_mem_axi_araddr, + output [ 2:0] io_mem_axi_arprot, + + input io_mem_axi_rvalid, + output io_mem_axi_rready, + input [31:0] io_mem_axi_rdata, + + input [31:0] io_irq, + output [31:0] io_eoi +); + wire resetn; + wire trap; + wire mem_axi_awvalid; + wire mem_axi_awready; + wire [31:0] mem_axi_awaddr; + wire [2:0] mem_axi_awprot; + wire mem_axi_wvalid; + wire mem_axi_wready; + wire [31:0] mem_axi_wdata; + wire [3:0] mem_axi_wstrb; + wire mem_axi_bvalid; + wire mem_axi_bready; + wire mem_axi_arvalid; + wire mem_axi_arready; + wire [31:0] mem_axi_araddr; + wire [2:0] mem_axi_arprot; + wire mem_axi_rvalid; + wire mem_axi_rready; + wire [31:0] mem_axi_rdata; + wire [31:0] irq; + wire [31:0] eoi; + + delay4 #( 1) delay_resetn (clk, io_resetn , resetn ); + delay4 #( 1) delay_trap (clk, trap , io_trap ); + delay4 #( 1) delay_mem_axi_awvalid (clk, mem_axi_awvalid, io_mem_axi_awvalid); + delay4 #( 1) delay_mem_axi_awready (clk, io_mem_axi_awready, mem_axi_awready); + delay4 #(32) delay_mem_axi_awaddr (clk, mem_axi_awaddr , io_mem_axi_awaddr ); + delay4 #( 3) delay_mem_axi_awprot (clk, mem_axi_awprot , io_mem_axi_awprot ); + delay4 #( 1) delay_mem_axi_wvalid (clk, mem_axi_wvalid , io_mem_axi_wvalid ); + delay4 #( 1) delay_mem_axi_wready (clk, io_mem_axi_wready , mem_axi_wready ); + delay4 #(32) delay_mem_axi_wdata (clk, mem_axi_wdata , io_mem_axi_wdata ); + delay4 #( 4) delay_mem_axi_wstrb (clk, mem_axi_wstrb , io_mem_axi_wstrb ); + delay4 #( 1) delay_mem_axi_bvalid (clk, io_mem_axi_bvalid , mem_axi_bvalid ); + delay4 #( 1) delay_mem_axi_bready (clk, mem_axi_bready , io_mem_axi_bready ); + delay4 #( 1) delay_mem_axi_arvalid (clk, mem_axi_arvalid, io_mem_axi_arvalid); + delay4 #( 1) delay_mem_axi_arready (clk, io_mem_axi_arready, mem_axi_arready); + delay4 #(32) delay_mem_axi_araddr (clk, mem_axi_araddr , io_mem_axi_araddr ); + delay4 #( 3) delay_mem_axi_arprot (clk, mem_axi_arprot , io_mem_axi_arprot ); + delay4 #( 1) delay_mem_axi_rvalid (clk, io_mem_axi_rvalid , mem_axi_rvalid ); + delay4 #( 1) delay_mem_axi_rready (clk, mem_axi_rready , io_mem_axi_rready ); + delay4 #(32) delay_mem_axi_rdata (clk, io_mem_axi_rdata , mem_axi_rdata ); + delay4 #(32) delay_irq (clk, io_irq , irq ); + delay4 #(32) delay_eoi (clk, eoi , io_eoi ); + + picorv32_axi #( + .TWO_CYCLE_ALU(1) + ) cpu ( + .clk (clk ), + .resetn (resetn ), + .trap (trap ), + .mem_axi_awvalid(mem_axi_awvalid), + .mem_axi_awready(mem_axi_awready), + .mem_axi_awaddr (mem_axi_awaddr ), + .mem_axi_awprot (mem_axi_awprot ), + .mem_axi_wvalid (mem_axi_wvalid ), + .mem_axi_wready (mem_axi_wready ), + .mem_axi_wdata (mem_axi_wdata ), + .mem_axi_wstrb (mem_axi_wstrb ), + .mem_axi_bvalid (mem_axi_bvalid ), + .mem_axi_bready (mem_axi_bready ), + .mem_axi_arvalid(mem_axi_arvalid), + .mem_axi_arready(mem_axi_arready), + .mem_axi_araddr (mem_axi_araddr ), + .mem_axi_arprot (mem_axi_arprot ), + .mem_axi_rvalid (mem_axi_rvalid ), + .mem_axi_rready (mem_axi_rready ), + .mem_axi_rdata (mem_axi_rdata ), + .irq (irq ), + .eoi (eoi ) + ); +endmodule + +module delay4 #( + parameter WIDTH = 1 +) ( + input clk, + input [WIDTH-1:0] in, + output reg [WIDTH-1:0] out +); + reg [WIDTH-1:0] q1, q2, q3; + always @(posedge clk) begin + q1 <= in; + q2 <= q1; + q3 <= q2; + out <= q3; + end +endmodule + diff --git a/scripts/romload/.gitignore b/scripts/romload/.gitignore new file mode 100644 index 0000000..6f1295b --- /dev/null +++ b/scripts/romload/.gitignore @@ -0,0 +1,11 @@ +firmware.d +firmware.elf +firmware.hex +firmware32.hex +firmware.o +firmware_addr.txt +firmware_dbg.v +syscalls.o +testbench.vvp +testbench.vcd +start.elf diff --git a/scripts/romload/Makefile b/scripts/romload/Makefile new file mode 100644 index 0000000..d510fa8 --- /dev/null +++ b/scripts/romload/Makefile @@ -0,0 +1,41 @@ +ifndef RISCV_TOOLS_PREFIX +RISCV_TOOLS_PREFIX = /opt/riscv32ic/bin/riscv32-unknown-elf- +endif +CXX = $(RISCV_TOOLS_PREFIX)g++ -march=rv32i +CC = $(RISCV_TOOLS_PREFIX)gcc -march=rv32i +AS = $(RISCV_TOOLS_PREFIX)gcc -march=rv32i +CXXFLAGS = -MD -Os -Wall -std=c++11 +CCFLAGS = -MD -Os -Wall +#LDFLAGS = -Wl,--gc-sections,--no-relax +LDFLAGS = -Wl,--gc-sections +LDLIBS = + +test: testbench.vvp firmware32.hex + vvp -l testbench.log -N testbench.vvp + +testbench.vvp: testbench.v ../../picorv32.v firmware_dbg.v + iverilog -o testbench.vvp testbench.v ../../picorv32.v + chmod -x testbench.vvp + +firmware32.hex: firmware.elf hex8tohex32.py + $(RISCV_TOOLS_PREFIX)objcopy -O verilog firmware.elf firmware.tmp + python3 hex8tohex32.py firmware.tmp > firmware32.hex + + +firmware_dbg.v: firmware.map + python3 map2debug.py + +start.o: start.S + $(CC) -c -nostdlib start.S $(LDLIBS) + +firmware.elf: firmware.o syscalls.o start.o + $(CC) $(LDFLAGS),-Map=firmware.map -o $@ $^ -T sections.ld $(LDLIBS) + chmod -x firmware.elf + +clean: + rm -f *.o *.d *.tmp start.elf + rm -f firmware.elf firmware.hex firmware32.hex + rm -f testbench.vvp testbench.vcd + +-include *.d +.PHONY: test clean diff --git a/scripts/romload/firmware.c b/scripts/romload/firmware.c new file mode 100644 index 0000000..4bc9ed8 --- /dev/null +++ b/scripts/romload/firmware.c @@ -0,0 +1,21 @@ +#include +#include + +int x1 = 1000; +int x2 = 2000; + +void main() +{ + int z; + x1 = 50; + x2 = 50; + + printf("hello\n"); + z = (x1 + x2); + if (z == 100) + printf("TEST PASSED\n"); + else + printf("TEST FAILED, z=%d\n", z); + exit(0); +} + diff --git a/scripts/romload/hex8tohex32.py b/scripts/romload/hex8tohex32.py new file mode 100644 index 0000000..ae44101 --- /dev/null +++ b/scripts/romload/hex8tohex32.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 + +import fileinput +import itertools + +ptr = 0 +data = [] + +def write_data(): + if len(data) != 0: + print("@%08x" % (ptr >> 2)) + while len(data) % 4 != 0: + data.append(0) + for word_bytes in zip(*([iter(data)]*4)): + print("".join(["%02x" % b for b in reversed(word_bytes)])) + +for line in fileinput.input(): + if line.startswith("@"): + addr = int(line[1:], 16) + if addr > ptr+4: + write_data() + ptr = addr + data = [] + while ptr % 4 != 0: + data.append(0) + ptr -= 1 + else: + while ptr + len(data) < addr: + data.append(0) + else: + data += [int(tok, 16) for tok in line.split()] + +write_data() + diff --git a/scripts/romload/map2debug.py b/scripts/romload/map2debug.py new file mode 100644 index 0000000..fc5c97c --- /dev/null +++ b/scripts/romload/map2debug.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +import re + +symbol = re.compile("\s*0x([0-9a-f]+)\s+([\w_]+)") +symbol_map = {} +with open("firmware.map", "r") as fh: + for fd in fh: + sym = symbol.match(fd) + if (sym): + addr = int(sym.group(1), 16) + symbol_map[addr] = sym.group(2) + +with open("firmware_dbg.v", "w") as fh: + for k, v in symbol_map.items(): + fh.write("`define C_SYM_{1:s} 32'h{0:08x}\n".format(k, v.upper())) + fh.write(" task firmware_dbg;\n") + fh.write(" input [31:0] addr;\n"); + fh.write(" begin\n"); + fh.write(" case (addr)\n"); + for k, v in symbol_map.items(): + fh.write(" 32'h{0:08x} : $display(\"%t: FCALL: {1:s}\", $time);\n".format(k, v)) + fh.write(" endcase\n"); + fh.write(" end\n"); + fh.write(" endtask\n"); + +with open("firmware_addr.txt", "w") as fh: + for k, v in symbol_map.items(): + fh.write("{0:08x} {1:s}\n".format(k,v)) + diff --git a/scripts/romload/sections.ld b/scripts/romload/sections.ld new file mode 100644 index 0000000..2ec3954 --- /dev/null +++ b/scripts/romload/sections.ld @@ -0,0 +1,45 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. +*/ + +/* starting address needs to be > 0 due to known bug in RISCV/GNU linker */ +MEMORY { + rom(rwx) : ORIGIN = 0x00000100, LENGTH = 63k + ram(rwx) : ORIGIN = 0x00020000, LENGTH = 16k +} + +ENTRY(_pvstart); + +SECTIONS { + .rom : { + _pvstart*(.text); + start*(.text); + . = 0x100; + . = ALIGN(4); + *(.text); + } > rom + + .data : { + _data_lma = LOADADDR(.data); + _data = .; + __global_pointer$ = . ; + *(.data .data.* ) + *(.sdata .sdata.*) + . = ALIGN(4); + _edata = .; + } >ram AT>rom + + .bss : { + _bss_start = .; + *(.bss .bss.*) + . = ALIGN(4); + _bss_end = .; + _end = .; + } >ram + +} diff --git a/scripts/romload/start.S b/scripts/romload/start.S new file mode 100644 index 0000000..be59808 --- /dev/null +++ b/scripts/romload/start.S @@ -0,0 +1,52 @@ +.section .text +.global _start +.global _pvstart + +_pvstart: +/* zero-initialize all registers */ +addi x1, zero, 0 +addi x2, zero, 0 +addi x3, zero, 0 +addi x4, zero, 0 +addi x5, zero, 0 +addi x6, zero, 0 +addi x7, zero, 0 +addi x8, zero, 0 +addi x9, zero, 0 +addi x10, zero, 0 +addi x11, zero, 0 +addi x12, zero, 0 +addi x13, zero, 0 +addi x14, zero, 0 +addi x15, zero, 0 +addi x16, zero, 0 +addi x17, zero, 0 +addi x18, zero, 0 +addi x19, zero, 0 +addi x20, zero, 0 +addi x21, zero, 0 +addi x22, zero, 0 +addi x23, zero, 0 +addi x24, zero, 0 +addi x25, zero, 0 +addi x26, zero, 0 +addi x27, zero, 0 +addi x28, zero, 0 +addi x29, zero, 0 +addi x30, zero, 0 +addi x31, zero, 0 + +/* set stack pointer */ + +lui sp, %hi(4*1024*1024) +addi sp, sp, %lo(4*1024*1024) + +/* push zeros on the stack for argc and argv */ +/* (stack is aligned to 16 bytes in riscv calling convention) */ +addi sp,sp,-16 +sw zero,0(sp) +sw zero,4(sp) +sw zero,8(sp) +sw zero,12(sp) + +j _start diff --git a/scripts/romload/syscalls.c b/scripts/romload/syscalls.c new file mode 100644 index 0000000..8ea84ca --- /dev/null +++ b/scripts/romload/syscalls.c @@ -0,0 +1,95 @@ +// An extremely minimalist syscalls.c for newlib +// Based on riscv newlib libgloss/riscv/sys_*.c +// Written by Clifford Wolf. + +#include +#include +#include + +#define UNIMPL_FUNC(_f) ".globl " #_f "\n.type " #_f ", @function\n" #_f ":\n" + +asm ( + ".text\n" + ".align 2\n" + UNIMPL_FUNC(_open) + UNIMPL_FUNC(_openat) + UNIMPL_FUNC(_lseek) + UNIMPL_FUNC(_stat) + UNIMPL_FUNC(_lstat) + UNIMPL_FUNC(_fstatat) + UNIMPL_FUNC(_isatty) + UNIMPL_FUNC(_access) + UNIMPL_FUNC(_faccessat) + UNIMPL_FUNC(_link) + UNIMPL_FUNC(_unlink) + UNIMPL_FUNC(_execve) + UNIMPL_FUNC(_getpid) + UNIMPL_FUNC(_fork) + UNIMPL_FUNC(_kill) + UNIMPL_FUNC(_wait) + UNIMPL_FUNC(_times) + UNIMPL_FUNC(_gettimeofday) + UNIMPL_FUNC(_ftime) + UNIMPL_FUNC(_utime) + UNIMPL_FUNC(_chown) + UNIMPL_FUNC(_chmod) + UNIMPL_FUNC(_chdir) + UNIMPL_FUNC(_getcwd) + UNIMPL_FUNC(_sysconf) + "j unimplemented_syscall\n" +); + +void unimplemented_syscall() +{ + const char *p = "Unimplemented system call called!\n"; + while (*p) + *(volatile int*)0x10000000 = *(p++); + asm volatile ("ebreak"); + __builtin_unreachable(); +} + +ssize_t _read(int file, void *ptr, size_t len) +{ + // always EOF + return 0; +} + +ssize_t _write(int file, const void *ptr, size_t len) +{ + const void *eptr = ptr + len; + while (ptr != eptr) + *(volatile int*)0x10000000 = *(char*)(ptr++); + return len; +} + +int _close(int file) +{ + // close is called before _exit() + return 0; +} + +int _fstat(int file, struct stat *st) +{ + // fstat is called during libc startup + errno = ENOENT; + return -1; +} + +void *_sbrk(ptrdiff_t incr) +{ + extern unsigned char _end[]; // Defined by linker + static unsigned long heap_end; + + if (heap_end == 0) + heap_end = (long)_end; + + heap_end += incr; + return (void *)(heap_end - incr); +} + +void _exit(int exit_status) +{ + asm volatile ("ebreak"); + __builtin_unreachable(); +} + diff --git a/scripts/romload/testbench.v b/scripts/romload/testbench.v new file mode 100644 index 0000000..e38819d --- /dev/null +++ b/scripts/romload/testbench.v @@ -0,0 +1,140 @@ +`timescale 1 ns / 1 ps +`undef VERBOSE_MEM +//`undef WRITE_VCD +`undef MEM8BIT + +// define the size of our ROM +// simulates ROM by suppressing writes below this address +`define ROM_SIZE 32'h0001_00FF + +module testbench; + reg clk = 1; + reg resetn = 0; + wire trap; + + always #5 clk = ~clk; + + initial begin + repeat (100) @(posedge clk); + resetn <= 1; + end + + 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; + +`include "firmware_dbg.v" + + picorv32 #( + .COMPRESSED_ISA(1), + .PROGADDR_RESET(32'h100) + ) 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 ) + ); + + localparam MEM_SIZE = 4*1024*1024; +`ifdef MEM8BIT + reg [7:0] memory [0:MEM_SIZE-1]; + initial + $readmemh("firmware.hex", memory); + end +`else + reg [31:0] memory [0:MEM_SIZE/4-1]; + integer x; + + // simulate hardware assist of clearing RAM and copying ROM data into + // memory + initial + begin + // clear memory + for (x=0; x> 2]; + + always @(posedge clk) begin + if (resetn && mem_valid && mem_ready) begin + if (mem_wstrb[3]) mem[mem_addr >> 2][31:24] <= mem_wdata[31:24]; + if (mem_wstrb[2]) mem[mem_addr >> 2][23:16] <= mem_wdata[23:16]; + if (mem_wstrb[1]) mem[mem_addr >> 2][15: 8] <= mem_wdata[15: 8]; + if (mem_wstrb[0]) mem[mem_addr >> 2][ 7: 0] <= mem_wdata[ 7: 0]; + end + end + + reg [1:0] mem_ready_stall = 0; + + always @(posedge clk) begin + mem_ready_stall <= {mem_ready_stall, mem_valid && !mem_ready}; + restrict(&mem_ready_stall == 0); + + if (mem_instr && mem_ready && mem_valid) begin + assume(opcode_valid(mem_rdata)); + assume(!opcode_branch(mem_rdata)); + assume(!opcode_load(mem_rdata)); + assume(!opcode_store(mem_rdata)); + end + + if (!mem_valid) + assume(!mem_ready); + + if (resetn) + assert(!trap); + end + + picorv32 #( + // change this settings as you like + .ENABLE_REGS_DUALPORT(1), + .TWO_STAGE_SHIFT(1), + .BARREL_SHIFTER(0), + .TWO_CYCLE_COMPARE(0), + .TWO_CYCLE_ALU(0), + .COMPRESSED_ISA(0), + .ENABLE_MUL(0), + .ENABLE_DIV(0) + ) cpu ( + .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 ) + ); +endmodule diff --git a/scripts/smtbmc/opcode.v b/scripts/smtbmc/opcode.v new file mode 100644 index 0000000..7a13bd2 --- /dev/null +++ b/scripts/smtbmc/opcode.v @@ -0,0 +1,104 @@ +function opcode_jump; + input [31:0] opcode; + begin + opcode_jump = 0; + if (opcode[6:0] == 7'b1101111) opcode_jump = 1; // JAL + if (opcode[14:12] == 3'b000 && opcode[6:0] == 7'b1100111) opcode_jump = 1; // JALR + end +endfunction + +function opcode_branch; + input [31:0] opcode; + begin + opcode_branch = 0; + if (opcode[14:12] == 3'b000 && opcode[6:0] == 7'b1100011) opcode_branch = 1; // BEQ + if (opcode[14:12] == 3'b001 && opcode[6:0] == 7'b1100011) opcode_branch = 1; // BNE + if (opcode[14:12] == 3'b100 && opcode[6:0] == 7'b1100011) opcode_branch = 1; // BLT + if (opcode[14:12] == 3'b101 && opcode[6:0] == 7'b1100011) opcode_branch = 1; // BGE + if (opcode[14:12] == 3'b110 && opcode[6:0] == 7'b1100011) opcode_branch = 1; // BLTU + if (opcode[14:12] == 3'b111 && opcode[6:0] == 7'b1100011) opcode_branch = 1; // BGEU + end +endfunction + +function opcode_load; + input [31:0] opcode; + begin + opcode_load = 0; + if (opcode[14:12] == 3'b000 && opcode[6:0] == 7'b0000011) opcode_load = 1; // LB + if (opcode[14:12] == 3'b001 && opcode[6:0] == 7'b0000011) opcode_load = 1; // LH + if (opcode[14:12] == 3'b010 && opcode[6:0] == 7'b0000011) opcode_load = 1; // LW + if (opcode[14:12] == 3'b100 && opcode[6:0] == 7'b0000011) opcode_load = 1; // LBU + if (opcode[14:12] == 3'b101 && opcode[6:0] == 7'b0000011) opcode_load = 1; // LHU + end +endfunction + +function opcode_store; + input [31:0] opcode; + begin + opcode_store = 0; + if (opcode[14:12] == 3'b000 && opcode[6:0] == 7'b0100011) opcode_store = 1; // SB + if (opcode[14:12] == 3'b001 && opcode[6:0] == 7'b0100011) opcode_store = 1; // SH + if (opcode[14:12] == 3'b010 && opcode[6:0] == 7'b0100011) opcode_store = 1; // SW + end +endfunction + +function opcode_alui; + input [31:0] opcode; + begin + opcode_alui = 0; + if (opcode[14:12] == 3'b000 && opcode[6:0] == 7'b0010011) opcode_alui = 1; // ADDI + if (opcode[14:12] == 3'b010 && opcode[6:0] == 7'b0010011) opcode_alui = 1; // SLTI + if (opcode[14:12] == 3'b011 && opcode[6:0] == 7'b0010011) opcode_alui = 1; // SLTIU + if (opcode[14:12] == 3'b100 && opcode[6:0] == 7'b0010011) opcode_alui = 1; // XORI + if (opcode[14:12] == 3'b110 && opcode[6:0] == 7'b0010011) opcode_alui = 1; // ORI + if (opcode[14:12] == 3'b111 && opcode[6:0] == 7'b0010011) opcode_alui = 1; // ANDI + if (opcode[31:25] == 7'b0000000 && opcode[14:12] == 3'b001 && opcode[6:0] == 7'b0010011) opcode_alui = 1; // SLLI + if (opcode[31:25] == 7'b0000000 && opcode[14:12] == 3'b101 && opcode[6:0] == 7'b0010011) opcode_alui = 1; // SRLI + if (opcode[31:25] == 7'b0100000 && opcode[14:12] == 3'b101 && opcode[6:0] == 7'b0010011) opcode_alui = 1; // SRAI + end +endfunction + +function opcode_alu; + input [31:0] opcode; + begin + opcode_alu = 0; + if (opcode[31:25] == 7'b0000000 && opcode[14:12] == 3'b000 && opcode[6:0] == 7'b0110011) opcode_alu = 1; // ADD + if (opcode[31:25] == 7'b0100000 && opcode[14:12] == 3'b000 && opcode[6:0] == 7'b0110011) opcode_alu = 1; // SUB + if (opcode[31:25] == 7'b0000000 && opcode[14:12] == 3'b001 && opcode[6:0] == 7'b0110011) opcode_alu = 1; // SLL + if (opcode[31:25] == 7'b0000000 && opcode[14:12] == 3'b010 && opcode[6:0] == 7'b0110011) opcode_alu = 1; // SLT + if (opcode[31:25] == 7'b0000000 && opcode[14:12] == 3'b011 && opcode[6:0] == 7'b0110011) opcode_alu = 1; // SLTU + if (opcode[31:25] == 7'b0000000 && opcode[14:12] == 3'b100 && opcode[6:0] == 7'b0110011) opcode_alu = 1; // XOR + if (opcode[31:25] == 7'b0000000 && opcode[14:12] == 3'b101 && opcode[6:0] == 7'b0110011) opcode_alu = 1; // SRL + if (opcode[31:25] == 7'b0100000 && opcode[14:12] == 3'b101 && opcode[6:0] == 7'b0110011) opcode_alu = 1; // SRA + if (opcode[31:25] == 7'b0000000 && opcode[14:12] == 3'b110 && opcode[6:0] == 7'b0110011) opcode_alu = 1; // OR + if (opcode[31:25] == 7'b0000000 && opcode[14:12] == 3'b111 && opcode[6:0] == 7'b0110011) opcode_alu = 1; // AND + end +endfunction + +function opcode_sys; + input [31:0] opcode; + begin + opcode_sys = 0; + if (opcode[31:20] == 12'hC00 && opcode[19:12] == 3'b010 && opcode[6:0] == 7'b1110011) opcode_sys = 1; // RDCYCLE + if (opcode[31:20] == 12'hC01 && opcode[19:12] == 3'b010 && opcode[6:0] == 7'b1110011) opcode_sys = 1; // RDTIME + if (opcode[31:20] == 12'hC02 && opcode[19:12] == 3'b010 && opcode[6:0] == 7'b1110011) opcode_sys = 1; // RDINSTRET + if (opcode[31:20] == 12'hC80 && opcode[19:12] == 3'b010 && opcode[6:0] == 7'b1110011) opcode_sys = 1; // RDCYCLEH + if (opcode[31:20] == 12'hC81 && opcode[19:12] == 3'b010 && opcode[6:0] == 7'b1110011) opcode_sys = 1; // RDTIMEH + if (opcode[31:20] == 12'hC82 && opcode[19:12] == 3'b010 && opcode[6:0] == 7'b1110011) opcode_sys = 1; // RDINSTRETH + end + +endfunction + +function opcode_valid; + input [31:0] opcode; + begin + opcode_valid = 0; + if (opcode_jump (opcode)) opcode_valid = 1; + if (opcode_branch(opcode)) opcode_valid = 1; + if (opcode_load (opcode)) opcode_valid = 1; + if (opcode_store (opcode)) opcode_valid = 1; + if (opcode_alui (opcode)) opcode_valid = 1; + if (opcode_alu (opcode)) opcode_valid = 1; + if (opcode_sys (opcode)) opcode_valid = 1; + end +endfunction diff --git a/scripts/smtbmc/tracecmp.gtkw b/scripts/smtbmc/tracecmp.gtkw new file mode 100644 index 0000000..09dd9b2 --- /dev/null +++ b/scripts/smtbmc/tracecmp.gtkw @@ -0,0 +1,71 @@ +[*] +[*] GTKWave Analyzer v3.3.65 (w)1999-2015 BSI +[*] Fri Aug 26 15:42:37 2016 +[*] +[dumpfile] "/home/clifford/Work/picorv32/scripts/smtbmc/output.vcd" +[dumpfile_mtime] "Fri Aug 26 15:33:18 2016" +[dumpfile_size] 80106 +[savefile] "/home/clifford/Work/picorv32/scripts/smtbmc/tracecmp.gtkw" +[timestart] 0 +[size] 1216 863 +[pos] -1 -1 +*-2.860312 10 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] testbench. +[sst_width] 241 +[signals_width] 337 +[sst_expanded] 1 +[sst_vpaned_height] 252 +@28 +smt_clock +testbench.resetn +testbench.trap_0 +testbench.trap_1 +@200 +- +-Trace CMP +@28 +testbench.trace_valid_0 +testbench.trace_valid_1 +@22 +testbench.trace_data_0[35:0] +testbench.trace_data_1[35:0] +@420 +testbench.trace_balance[7:0] +@200 +- +-CPU #0 +@28 +testbench.mem_valid_0 +testbench.mem_ready_0 +testbench.mem_instr_0 +@22 +testbench.mem_addr_0[31:0] +testbench.mem_rdata_0[31:0] +testbench.mem_wdata_0[31:0] +@28 +testbench.mem_wstrb_0[3:0] +@22 +testbench.cpu_0.cpu_state[7:0] +@28 +testbench.cpu_0.mem_state[1:0] +@200 +- +-CPU #1 +@28 +testbench.mem_valid_1 +testbench.mem_ready_1 +testbench.mem_instr_1 +@22 +testbench.mem_addr_1[31:0] +testbench.mem_rdata_1[31:0] +testbench.mem_wdata_1[31:0] +@28 +testbench.mem_wstrb_1[3:0] +@22 +testbench.cpu_1.cpu_state[7:0] +@28 +testbench.cpu_1.mem_state[1:0] +@200 +- +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/scripts/smtbmc/tracecmp.sh b/scripts/smtbmc/tracecmp.sh new file mode 100644 index 0000000..449af52 --- /dev/null +++ b/scripts/smtbmc/tracecmp.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -ex + +yosys -ql tracecmp.yslog \ + -p 'read_verilog -formal -norestrict -assume-asserts ../../picorv32.v' \ + -p 'read_verilog -formal tracecmp.v' \ + -p 'prep -top testbench -nordff' \ + -p 'write_smt2 -wires tracecmp.smt2' + +yosys-smtbmc -s yices --smtc tracecmp.smtc --dump-vcd output.vcd --dump-smtc output.smtc tracecmp.smt2 + diff --git a/scripts/smtbmc/tracecmp.smtc b/scripts/smtbmc/tracecmp.smtc new file mode 100644 index 0000000..477c7d0 --- /dev/null +++ b/scripts/smtbmc/tracecmp.smtc @@ -0,0 +1,12 @@ +initial +assume (= [mem_0] [mem_1]) +assume (= [cpu_0.cpuregs] [cpu_1.cpuregs]) +assume (= [trace_data_0] [trace_data_1]) + +always +assume (=> (not [mem_valid_0]) (not [mem_ready_0])) +assume (=> (not [mem_valid_1]) (not [mem_ready_1])) +# assume (= [mem_ready_0] [mem_ready_1]) + +always -1 +assert (=> (= [trace_balance] #x00) (= [trace_data_0] [trace_data_1])) diff --git a/scripts/smtbmc/tracecmp.v b/scripts/smtbmc/tracecmp.v new file mode 100644 index 0000000..8ac4157 --- /dev/null +++ b/scripts/smtbmc/tracecmp.v @@ -0,0 +1,109 @@ +module testbench(input clk, mem_ready_0, mem_ready_1); + // set this to 1 to test generation of counter examples + localparam ENABLE_COUNTERS = 0; + + reg resetn = 0; + always @(posedge clk) resetn <= 1; + + (* keep *) wire trap_0, trace_valid_0, mem_valid_0, mem_instr_0; + (* keep *) wire [3:0] mem_wstrb_0; + (* keep *) wire [31:0] mem_addr_0, mem_wdata_0, mem_rdata_0; + (* keep *) wire [35:0] trace_data_0; + + (* keep *) wire trap_1, trace_valid_1, mem_valid_1, mem_instr_1; + (* keep *) wire [3:0] mem_wstrb_1; + (* keep *) wire [31:0] mem_addr_1, mem_wdata_1, mem_rdata_1; + (* keep *) wire [35:0] trace_data_1; + + reg [31:0] mem_0 [0:2**30-1]; + reg [31:0] mem_1 [0:2**30-1]; + + assign mem_rdata_0 = mem_0[mem_addr_0 >> 2]; + assign mem_rdata_1 = mem_1[mem_addr_1 >> 2]; + + always @(posedge clk) begin + if (resetn && mem_valid_0 && mem_ready_0) begin + if (mem_wstrb_0[3]) mem_0[mem_addr_0 >> 2][31:24] <= mem_wdata_0[31:24]; + if (mem_wstrb_0[2]) mem_0[mem_addr_0 >> 2][23:16] <= mem_wdata_0[23:16]; + if (mem_wstrb_0[1]) mem_0[mem_addr_0 >> 2][15: 8] <= mem_wdata_0[15: 8]; + if (mem_wstrb_0[0]) mem_0[mem_addr_0 >> 2][ 7: 0] <= mem_wdata_0[ 7: 0]; + end + if (resetn && mem_valid_1 && mem_ready_1) begin + if (mem_wstrb_1[3]) mem_1[mem_addr_1 >> 2][31:24] <= mem_wdata_1[31:24]; + if (mem_wstrb_1[2]) mem_1[mem_addr_1 >> 2][23:16] <= mem_wdata_1[23:16]; + if (mem_wstrb_1[1]) mem_1[mem_addr_1 >> 2][15: 8] <= mem_wdata_1[15: 8]; + if (mem_wstrb_1[0]) mem_1[mem_addr_1 >> 2][ 7: 0] <= mem_wdata_1[ 7: 0]; + end + end + + (* keep *) reg [7:0] trace_balance; + reg [7:0] trace_balance_q; + + always @* begin + trace_balance = trace_balance_q; + if (trace_valid_0) trace_balance = trace_balance + 1; + if (trace_valid_1) trace_balance = trace_balance - 1; + end + + always @(posedge clk) begin + trace_balance_q <= resetn ? trace_balance : 0; + end + + picorv32 #( + // do not change this settings + .ENABLE_COUNTERS(ENABLE_COUNTERS), + .ENABLE_TRACE(1), + + // change this settings as you like + .ENABLE_REGS_DUALPORT(1), + .TWO_STAGE_SHIFT(1), + .BARREL_SHIFTER(0), + .TWO_CYCLE_COMPARE(0), + .TWO_CYCLE_ALU(0), + .COMPRESSED_ISA(0), + .ENABLE_MUL(0), + .ENABLE_DIV(0) + ) cpu_0 ( + .clk (clk ), + .resetn (resetn ), + .trap (trap_0 ), + .mem_valid (mem_valid_0 ), + .mem_instr (mem_instr_0 ), + .mem_ready (mem_ready_0 ), + .mem_addr (mem_addr_0 ), + .mem_wdata (mem_wdata_0 ), + .mem_wstrb (mem_wstrb_0 ), + .mem_rdata (mem_rdata_0 ), + .trace_valid (trace_valid_0), + .trace_data (trace_data_0 ) + ); + + picorv32 #( + // do not change this settings + .ENABLE_COUNTERS(ENABLE_COUNTERS), + .ENABLE_TRACE(1), + + // change this settings as you like + .ENABLE_REGS_DUALPORT(1), + .TWO_STAGE_SHIFT(1), + .BARREL_SHIFTER(0), + .TWO_CYCLE_COMPARE(0), + .TWO_CYCLE_ALU(0), + .COMPRESSED_ISA(0), + .ENABLE_MUL(0), + .ENABLE_DIV(0) + ) cpu_1 ( + .clk (clk ), + .resetn (resetn ), + .trap (trap_1 ), + .mem_valid (mem_valid_1 ), + .mem_instr (mem_instr_1 ), + .mem_ready (mem_ready_1 ), + .mem_addr (mem_addr_1 ), + .mem_wdata (mem_wdata_1 ), + .mem_wstrb (mem_wstrb_1 ), + .mem_rdata (mem_rdata_1 ), + .trace_valid (trace_valid_1), + .trace_data (trace_data_1 ) + ); +endmodule diff --git a/scripts/smtbmc/tracecmp2.sh b/scripts/smtbmc/tracecmp2.sh new file mode 100644 index 0000000..ddaf285 --- /dev/null +++ b/scripts/smtbmc/tracecmp2.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -ex + +yosys -ql tracecmp2.yslog \ + -p 'read_verilog -formal -norestrict -assume-asserts ../../picorv32.v' \ + -p 'read_verilog -formal tracecmp2.v' \ + -p 'prep -top testbench -nordff' \ + -p 'write_smt2 -wires tracecmp2.smt2' + +yosys-smtbmc -s boolector --smtc tracecmp2.smtc --dump-vcd output.vcd --dump-smtc output.smtc tracecmp2.smt2 + diff --git a/scripts/smtbmc/tracecmp2.smtc b/scripts/smtbmc/tracecmp2.smtc new file mode 100644 index 0000000..3b7fd0a --- /dev/null +++ b/scripts/smtbmc/tracecmp2.smtc @@ -0,0 +1,2 @@ +initial +assume (= [cpu_0.cpuregs] [cpu_1.cpuregs]) diff --git a/scripts/smtbmc/tracecmp2.v b/scripts/smtbmc/tracecmp2.v new file mode 100644 index 0000000..42f39a9 --- /dev/null +++ b/scripts/smtbmc/tracecmp2.v @@ -0,0 +1,196 @@ +module testbench( + input clk, mem_ready_0, mem_ready_1, + input [31:0] mem_rdata +); + // set this to 1 to test generation of counterexamples + localparam ENABLE_COUNTERS = 0; + + reg resetn = 0; + always @(posedge clk) resetn <= 1; + + (* keep *) wire trap_0, trace_valid_0, mem_valid_0, mem_instr_0; + (* keep *) wire [3:0] mem_wstrb_0; + (* keep *) wire [31:0] mem_addr_0, mem_wdata_0, mem_rdata_0; + (* keep *) wire [35:0] trace_data_0; + + (* keep *) wire trap_1, trace_valid_1, mem_valid_1, mem_instr_1; + (* keep *) wire [3:0] mem_wstrb_1; + (* keep *) wire [31:0] mem_addr_1, mem_wdata_1, mem_rdata_1; + (* keep *) wire [35:0] trace_data_1; + + reg [31:0] last_mem_rdata; + + assign mem_rdata_0 = mem_rdata; + assign mem_rdata_1 = mem_rdata; + + wire mem_xfer_0 = resetn && mem_valid_0 && mem_ready_0; + wire mem_xfer_1 = resetn && mem_valid_1 && mem_ready_1; + + reg [1:0] cmp_mem_state = 0; + reg [31:0] cmp_mem_addr; + reg [31:0] cmp_mem_wdata; + reg [3:0] cmp_mem_wstrb; + + always @* begin + if (mem_valid_0 == 0) + assume(!mem_ready_0 == 0); + if (mem_valid_1 == 0) + assume(mem_ready_1 == 0); + end + + always @(posedge clk) begin + if (cmp_mem_state) + assume(last_mem_rdata == mem_rdata); + last_mem_rdata <= mem_rdata; + end + + always @(posedge clk) begin + case (cmp_mem_state) + 2'b 00: begin + case ({mem_xfer_1, mem_xfer_0}) + 2'b 11: begin + assert(mem_addr_0 == mem_addr_1); + assert(mem_wstrb_0 == mem_wstrb_1); + if (mem_wstrb_0[3]) assert(mem_wdata_0[31:24] == mem_wdata_1[31:24]); + if (mem_wstrb_0[2]) assert(mem_wdata_0[23:16] == mem_wdata_1[23:16]); + if (mem_wstrb_0[1]) assert(mem_wdata_0[15: 8] == mem_wdata_1[15: 8]); + if (mem_wstrb_0[0]) assert(mem_wdata_0[ 7: 0] == mem_wdata_1[ 7: 0]); + end + 2'b 01: begin + cmp_mem_state <= 2'b 01; + cmp_mem_addr <= mem_addr_0; + cmp_mem_wdata <= mem_wdata_0; + cmp_mem_wstrb <= mem_wstrb_0; + end + 2'b 10: begin + cmp_mem_state <= 2'b 10; + cmp_mem_addr <= mem_addr_1; + cmp_mem_wdata <= mem_wdata_1; + cmp_mem_wstrb <= mem_wstrb_1; + end + endcase + end + 2'b 01: begin + assume(!mem_xfer_0); + if (mem_xfer_1) begin + cmp_mem_state <= 2'b 00; + assert(cmp_mem_addr == mem_addr_1); + assert(cmp_mem_wstrb == mem_wstrb_1); + if (cmp_mem_wstrb[3]) assert(cmp_mem_wdata[31:24] == mem_wdata_1[31:24]); + if (cmp_mem_wstrb[2]) assert(cmp_mem_wdata[23:16] == mem_wdata_1[23:16]); + if (cmp_mem_wstrb[1]) assert(cmp_mem_wdata[15: 8] == mem_wdata_1[15: 8]); + if (cmp_mem_wstrb[0]) assert(cmp_mem_wdata[ 7: 0] == mem_wdata_1[ 7: 0]); + end + end + 2'b 10: begin + assume(!mem_xfer_1); + if (mem_xfer_0) begin + cmp_mem_state <= 2'b 00; + assert(cmp_mem_addr == mem_addr_0); + assert(cmp_mem_wstrb == mem_wstrb_0); + if (cmp_mem_wstrb[3]) assert(cmp_mem_wdata[31:24] == mem_wdata_0[31:24]); + if (cmp_mem_wstrb[2]) assert(cmp_mem_wdata[23:16] == mem_wdata_0[23:16]); + if (cmp_mem_wstrb[1]) assert(cmp_mem_wdata[15: 8] == mem_wdata_0[15: 8]); + if (cmp_mem_wstrb[0]) assert(cmp_mem_wdata[ 7: 0] == mem_wdata_0[ 7: 0]); + end + end + endcase + end + + reg [1:0] cmp_trace_state = 0; + reg [35:0] cmp_trace_data; + + always @(posedge clk) begin + if (resetn) begin + case (cmp_trace_state) + 2'b 00: begin + case ({trace_valid_1, trace_valid_0}) + 2'b 11: begin + assert(trace_data_0 == trace_data_1); + end + 2'b 01: begin + cmp_trace_state <= 2'b 01; + cmp_trace_data <= trace_data_0; + end + 2'b 10: begin + cmp_trace_state <= 2'b 10; + cmp_trace_data <= trace_data_1; + end + endcase + end + 2'b 01: begin + assume(!trace_valid_0); + if (trace_valid_1) begin + cmp_trace_state <= 2'b 00; + assert(cmp_trace_data == trace_data_1); + end + end + 2'b 10: begin + assume(!trace_valid_1); + if (trace_valid_0) begin + cmp_trace_state <= 2'b 00; + assert(cmp_trace_data == trace_data_0); + end + end + endcase + end + end + + picorv32 #( + // do not change this settings + .ENABLE_COUNTERS(ENABLE_COUNTERS), + .ENABLE_TRACE(1), + + // change this settings as you like + .ENABLE_REGS_DUALPORT(1), + .TWO_STAGE_SHIFT(0), + .BARREL_SHIFTER(0), + .TWO_CYCLE_COMPARE(0), + .TWO_CYCLE_ALU(0), + .COMPRESSED_ISA(1), + .ENABLE_MUL(0), + .ENABLE_DIV(0) + ) cpu_0 ( + .clk (clk ), + .resetn (resetn ), + .trap (trap_0 ), + .mem_valid (mem_valid_0 ), + .mem_instr (mem_instr_0 ), + .mem_ready (mem_ready_0 ), + .mem_addr (mem_addr_0 ), + .mem_wdata (mem_wdata_0 ), + .mem_wstrb (mem_wstrb_0 ), + .mem_rdata (mem_rdata_0 ), + .trace_valid (trace_valid_0), + .trace_data (trace_data_0 ) + ); + + picorv32 #( + // do not change this settings + .ENABLE_COUNTERS(ENABLE_COUNTERS), + .ENABLE_TRACE(1), + + // change this settings as you like + .ENABLE_REGS_DUALPORT(1), + .TWO_STAGE_SHIFT(1), + .BARREL_SHIFTER(0), + .TWO_CYCLE_COMPARE(0), + .TWO_CYCLE_ALU(0), + .COMPRESSED_ISA(1), + .ENABLE_MUL(0), + .ENABLE_DIV(0) + ) cpu_1 ( + .clk (clk ), + .resetn (resetn ), + .trap (trap_1 ), + .mem_valid (mem_valid_1 ), + .mem_instr (mem_instr_1 ), + .mem_ready (mem_ready_1 ), + .mem_addr (mem_addr_1 ), + .mem_wdata (mem_wdata_1 ), + .mem_wstrb (mem_wstrb_1 ), + .mem_rdata (mem_rdata_1 ), + .trace_valid (trace_valid_1), + .trace_data (trace_data_1 ) + ); +endmodule diff --git a/scripts/smtbmc/tracecmp3.sh b/scripts/smtbmc/tracecmp3.sh new file mode 100644 index 0000000..bfa0b3c --- /dev/null +++ b/scripts/smtbmc/tracecmp3.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -ex + +yosys -l tracecmp3.yslog \ + -p 'read_verilog ../../picorv32.v' \ + -p 'read_verilog -formal tracecmp3.v' \ + -p 'prep -top testbench -nordff' \ + -p 'write_smt2 -wires tracecmp3.smt2' \ + -p 'miter -assert -flatten testbench miter' \ + -p 'hierarchy -top miter; memory_map; opt -full' \ + -p 'techmap; opt; abc; opt -fast' \ + -p 'write_blif tracecmp3.blif' + +yosys-abc -c 'read_blif tracecmp3.blif; undc; strash; zero; sim3 -v; undc -c; write_cex -n tracecmp3.cex' +yosys-smtbmc -s yices --cex tracecmp3.cex --dump-vcd output.vcd --dump-smtc output.smtc tracecmp3.smt2 + diff --git a/scripts/smtbmc/tracecmp3.v b/scripts/smtbmc/tracecmp3.v new file mode 100644 index 0000000..a1bb63b --- /dev/null +++ b/scripts/smtbmc/tracecmp3.v @@ -0,0 +1,135 @@ +// Based on the benchmark from 2016 Yosys-SMTBMC presentation, which in turn is +// based on the tracecmp2 test from this directory. + +module testbench ( + input clk, + input [31:0] mem_rdata_in, + + input pcpi_wr, + input [31:0] pcpi_rd, + input pcpi_wait, + input pcpi_ready +); + reg resetn = 0; + + always @(posedge clk) + resetn <= 1; + + wire cpu0_trap; + wire cpu0_mem_valid; + wire cpu0_mem_instr; + wire cpu0_mem_ready; + wire [31:0] cpu0_mem_addr; + wire [31:0] cpu0_mem_wdata; + wire [3:0] cpu0_mem_wstrb; + wire [31:0] cpu0_mem_rdata; + wire cpu0_trace_valid; + wire [35:0] cpu0_trace_data; + + wire cpu1_trap; + wire cpu1_mem_valid; + wire cpu1_mem_instr; + wire cpu1_mem_ready; + wire [31:0] cpu1_mem_addr; + wire [31:0] cpu1_mem_wdata; + wire [3:0] cpu1_mem_wstrb; + wire [31:0] cpu1_mem_rdata; + wire cpu1_trace_valid; + wire [35:0] cpu1_trace_data; + + wire mem_ready; + wire [31:0] mem_rdata; + + assign mem_ready = cpu0_mem_valid && cpu1_mem_valid; + assign mem_rdata = mem_rdata_in; + + assign cpu0_mem_ready = mem_ready; + assign cpu0_mem_rdata = mem_rdata; + + assign cpu1_mem_ready = mem_ready; + assign cpu1_mem_rdata = mem_rdata; + + reg [ 2:0] trace_balance = 3'b010; + reg [35:0] trace_buffer_cpu0 = 0, trace_buffer_cpu1 = 0; + + always @(posedge clk) begin + if (resetn) begin + if (cpu0_trace_valid) + trace_buffer_cpu0 <= cpu0_trace_data; + + if (cpu1_trace_valid) + trace_buffer_cpu1 <= cpu1_trace_data; + + if (cpu0_trace_valid && !cpu1_trace_valid) + trace_balance <= trace_balance << 1; + + if (!cpu0_trace_valid && cpu1_trace_valid) + trace_balance <= trace_balance >> 1; + end + end + + always @* begin + if (resetn && cpu0_mem_ready) begin + assert(cpu0_mem_addr == cpu1_mem_addr); + assert(cpu0_mem_wstrb == cpu1_mem_wstrb); + if (cpu0_mem_wstrb[3]) assert(cpu0_mem_wdata[31:24] == cpu1_mem_wdata[31:24]); + if (cpu0_mem_wstrb[2]) assert(cpu0_mem_wdata[23:16] == cpu1_mem_wdata[23:16]); + if (cpu0_mem_wstrb[1]) assert(cpu0_mem_wdata[15: 8] == cpu1_mem_wdata[15: 8]); + if (cpu0_mem_wstrb[0]) assert(cpu0_mem_wdata[ 7: 0] == cpu1_mem_wdata[ 7: 0]); + end + if (trace_balance == 3'b010) begin + assert(trace_buffer_cpu0 == trace_buffer_cpu1); + end + end + + picorv32 #( + .ENABLE_COUNTERS(0), + .REGS_INIT_ZERO(1), + .COMPRESSED_ISA(1), + .ENABLE_TRACE(1), + + .TWO_STAGE_SHIFT(0), + .ENABLE_PCPI(1) + ) cpu0 ( + .clk (clk ), + .resetn (resetn ), + .trap (cpu0_trap ), + .mem_valid (cpu0_mem_valid ), + .mem_instr (cpu0_mem_instr ), + .mem_ready (cpu0_mem_ready ), + .mem_addr (cpu0_mem_addr ), + .mem_wdata (cpu0_mem_wdata ), + .mem_wstrb (cpu0_mem_wstrb ), + .mem_rdata (cpu0_mem_rdata ), + .pcpi_wr (pcpi_wr ), + .pcpi_rd (pcpi_rd ), + .pcpi_wait (pcpi_wait ), + .pcpi_ready (pcpi_ready ), + .trace_valid (cpu0_trace_valid), + .trace_data (cpu0_trace_data ) + ); + + picorv32 #( + .ENABLE_COUNTERS(0), + .REGS_INIT_ZERO(1), + .COMPRESSED_ISA(1), + .ENABLE_TRACE(1), + + .TWO_STAGE_SHIFT(1), + .TWO_CYCLE_COMPARE(1), + .TWO_CYCLE_ALU(1) + ) cpu1 ( + .clk (clk ), + .resetn (resetn ), + .trap (cpu1_trap ), + .mem_valid (cpu1_mem_valid ), + .mem_instr (cpu1_mem_instr ), + .mem_ready (cpu1_mem_ready ), + .mem_addr (cpu1_mem_addr ), + .mem_wdata (cpu1_mem_wdata ), + .mem_wstrb (cpu1_mem_wstrb ), + .mem_rdata (cpu1_mem_rdata ), + .trace_valid (cpu1_trace_valid), + .trace_data (cpu1_trace_data ) + ); +endmodule diff --git a/scripts/tomthumbtg/.gitignore b/scripts/tomthumbtg/.gitignore new file mode 100644 index 0000000..c1e6b04 --- /dev/null +++ b/scripts/tomthumbtg/.gitignore @@ -0,0 +1,15 @@ +testbench_a +testbench_b +testbench_c +testbench_d +testbench_e +testbench_f +testbench_g +testbench_h +testbench_i +testbench_j +testbench_k +testbench_l +testgen.tgz +testgen +tests diff --git a/scripts/tomthumbtg/README b/scripts/tomthumbtg/README new file mode 100644 index 0000000..4e12ba8 --- /dev/null +++ b/scripts/tomthumbtg/README @@ -0,0 +1,7 @@ + +Testing PicoRV32 using the test case generator from +the Tom Thumb RISC-V CPU project: + +https://github.com/maikmerten/riscv-tomthumb +https://github.com/maikmerten/riscv-tomthumb-testgen + diff --git a/scripts/tomthumbtg/run.sh b/scripts/tomthumbtg/run.sh new file mode 100644 index 0000000..63a6935 --- /dev/null +++ b/scripts/tomthumbtg/run.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +set -ex + +if [ ! -f testgen.tgz ]; then + rm -f testgen.tgz.part + wget -O testgen.tgz.part http://maikmerten.de/testgen.tgz + mv testgen.tgz.part testgen.tgz +fi + +rm -rf tests testgen/ +tar xvzf testgen.tgz + +iverilog -o testbench_a -s testbench testbench.v ../../picorv32.v -DTWO_STAGE_SHIFT=0 -DBARREL_SHIFTER=0 -DTWO_CYCLE_COMPARE=0 -DTWO_CYCLE_ALU=0 +iverilog -o testbench_b -s testbench testbench.v ../../picorv32.v -DTWO_STAGE_SHIFT=1 -DBARREL_SHIFTER=0 -DTWO_CYCLE_COMPARE=0 -DTWO_CYCLE_ALU=0 +iverilog -o testbench_c -s testbench testbench.v ../../picorv32.v -DTWO_STAGE_SHIFT=0 -DBARREL_SHIFTER=1 -DTWO_CYCLE_COMPARE=0 -DTWO_CYCLE_ALU=0 + +iverilog -o testbench_d -s testbench testbench.v ../../picorv32.v -DTWO_STAGE_SHIFT=0 -DBARREL_SHIFTER=0 -DTWO_CYCLE_COMPARE=1 -DTWO_CYCLE_ALU=0 +iverilog -o testbench_e -s testbench testbench.v ../../picorv32.v -DTWO_STAGE_SHIFT=1 -DBARREL_SHIFTER=0 -DTWO_CYCLE_COMPARE=1 -DTWO_CYCLE_ALU=0 +iverilog -o testbench_f -s testbench testbench.v ../../picorv32.v -DTWO_STAGE_SHIFT=0 -DBARREL_SHIFTER=1 -DTWO_CYCLE_COMPARE=1 -DTWO_CYCLE_ALU=0 + +iverilog -o testbench_g -s testbench testbench.v ../../picorv32.v -DTWO_STAGE_SHIFT=0 -DBARREL_SHIFTER=0 -DTWO_CYCLE_COMPARE=0 -DTWO_CYCLE_ALU=1 +iverilog -o testbench_h -s testbench testbench.v ../../picorv32.v -DTWO_STAGE_SHIFT=1 -DBARREL_SHIFTER=0 -DTWO_CYCLE_COMPARE=0 -DTWO_CYCLE_ALU=1 +iverilog -o testbench_i -s testbench testbench.v ../../picorv32.v -DTWO_STAGE_SHIFT=0 -DBARREL_SHIFTER=1 -DTWO_CYCLE_COMPARE=0 -DTWO_CYCLE_ALU=1 + +iverilog -o testbench_j -s testbench testbench.v ../../picorv32.v -DTWO_STAGE_SHIFT=0 -DBARREL_SHIFTER=0 -DTWO_CYCLE_COMPARE=1 -DTWO_CYCLE_ALU=1 +iverilog -o testbench_k -s testbench testbench.v ../../picorv32.v -DTWO_STAGE_SHIFT=1 -DBARREL_SHIFTER=0 -DTWO_CYCLE_COMPARE=1 -DTWO_CYCLE_ALU=1 +iverilog -o testbench_l -s testbench testbench.v ../../picorv32.v -DTWO_STAGE_SHIFT=0 -DBARREL_SHIFTER=1 -DTWO_CYCLE_COMPARE=1 -DTWO_CYCLE_ALU=1 + +mkdir -p tests +for i in {0..999}; do + fn="tests/test_`printf '%03d' $i`" + + { + cat start.S + java -jar testgen/tomthumb-testgen-1.0-SNAPSHOT.jar + } > $fn.s + + riscv32-unknown-elf-gcc -ffreestanding -nostdlib -Wl,-Bstatic,-T,sections.lds -o $fn.elf $fn.s + riscv32-unknown-elf-objcopy -O binary $fn.elf $fn.bin + python3 ../../firmware/makehex.py $fn.bin 16384 > $fn.hex + for tb in testbench_{a,b,c,d,e,f,g,h,i,j,k,l}; do vvp -N $tb +hex=$fn.hex; done +done diff --git a/scripts/tomthumbtg/sections.lds b/scripts/tomthumbtg/sections.lds new file mode 100644 index 0000000..8962f5c --- /dev/null +++ b/scripts/tomthumbtg/sections.lds @@ -0,0 +1,7 @@ +SECTIONS { + .memory : { + . = 0; + *(.text); + *(*); + } +} diff --git a/scripts/tomthumbtg/start.S b/scripts/tomthumbtg/start.S new file mode 100644 index 0000000..541c8a4 --- /dev/null +++ b/scripts/tomthumbtg/start.S @@ -0,0 +1,57 @@ +.section .text.start +.global testcollection + +/* zero-initialize all registers */ +addi x1, zero, 0 +addi x2, zero, 0 +addi x3, zero, 0 +addi x4, zero, 0 +addi x5, zero, 0 +addi x6, zero, 0 +addi x7, zero, 0 +addi x8, zero, 0 +addi x9, zero, 0 +addi x10, zero, 0 +addi x11, zero, 0 +addi x12, zero, 0 +addi x13, zero, 0 +addi x14, zero, 0 +addi x15, zero, 0 +addi x16, zero, 0 +addi x17, zero, 0 +addi x18, zero, 0 +addi x19, zero, 0 +addi x20, zero, 0 +addi x21, zero, 0 +addi x22, zero, 0 +addi x23, zero, 0 +addi x24, zero, 0 +addi x25, zero, 0 +addi x26, zero, 0 +addi x27, zero, 0 +addi x28, zero, 0 +addi x29, zero, 0 +addi x30, zero, 0 +addi x31, zero, 0 + +/* set stack pointer */ +lui sp, %hi(64*1024) +addi sp, sp, %lo(64*1024) + +/* push zeros on the stack for argc and argv */ +/* (stack is aligned to 16 bytes in riscv calling convention) */ +addi sp,sp,-16 +sw zero,0(sp) +sw zero,4(sp) +sw zero,8(sp) +sw zero,12(sp) + +/* call test */ +call testcollection + +/* write test results */ +lui x1, %hi(0x10000000) +addi x1, x1, %lo(0x10000000) +sw x5, 0(x1) + +ebreak diff --git a/scripts/tomthumbtg/testbench.v b/scripts/tomthumbtg/testbench.v new file mode 100644 index 0000000..c39ebca --- /dev/null +++ b/scripts/tomthumbtg/testbench.v @@ -0,0 +1,83 @@ +`timescale 1 ns / 1 ps + +module testbench; + reg clk = 1; + reg resetn = 0; + wire trap; + + always #5 clk = ~clk; + + initial begin + repeat (100) @(posedge clk); + resetn <= 1; + end + + 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; + + picorv32 #( + .TWO_STAGE_SHIFT(`TWO_STAGE_SHIFT), + .BARREL_SHIFTER(`BARREL_SHIFTER), + .TWO_CYCLE_COMPARE(`TWO_CYCLE_COMPARE), + .TWO_CYCLE_ALU(`TWO_CYCLE_ALU) + ) 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 ) + ); + + reg [31:0] memory [0:16*1024-1]; + reg [1023:0] hex_filename; + + initial begin + if ($value$plusargs("hex=%s", hex_filename)) + $readmemh(hex_filename, memory); + end + + initial begin + // $dumpfile("testbench.vcd"); + // $dumpvars(0, testbench); + end + + always @(posedge clk) begin + if (resetn && trap) begin + repeat (10) @(posedge clk); + $display("TRAP"); + $stop; + end + end + + always @(posedge clk) begin + mem_ready <= 0; + if (mem_valid && !mem_ready) begin + mem_ready <= 1; + if (mem_addr == 32'h 1000_0000) begin + if (mem_wdata != -32'd1) begin + $display("Failed test case: %d", mem_wdata); + $stop; + end else begin + $display("OK."); + $finish; + end + end else begin + mem_rdata <= memory[mem_addr >> 2]; + 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 + end + end +endmodule 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 diff --git a/scripts/vivado/.gitignore b/scripts/vivado/.gitignore new file mode 100644 index 0000000..2374269 --- /dev/null +++ b/scripts/vivado/.gitignore @@ -0,0 +1,18 @@ +.Xil/ +firmware.bin +firmware.elf +firmware.hex +firmware.map +synth_*.log +synth_*.mmi +synth_*.bit +synth_system.v +table.txt +tab_*/ +webtalk.jou +webtalk.log +webtalk_*.jou +webtalk_*.log +xelab.* +xsim.* +xvlog.* diff --git a/scripts/vivado/Makefile b/scripts/vivado/Makefile new file mode 100644 index 0000000..3c92901 --- /dev/null +++ b/scripts/vivado/Makefile @@ -0,0 +1,69 @@ + +VIVADO_BASE = /opt/Xilinx/Vivado/2018.2 +VIVADO = $(VIVADO_BASE)/bin/vivado +XVLOG = $(VIVADO_BASE)/bin/xvlog +XELAB = $(VIVADO_BASE)/bin/xelab +GLBL = $(VIVADO_BASE)/data/verilog/src/glbl.v +TOOLCHAIN_PREFIX = riscv32-unknown-elf- + +export VIVADO + +# work-around for http://svn.clifford.at/handicraft/2016/vivadosig11 +export RDI_VERBOSE = False + +help: + @echo "" + @echo "Simple synthesis tests:" + @echo " make synth_area_{small|regular|large}" + @echo " make synth_speed" + @echo "" + @echo "Example system:" + @echo " make synth_system" + @echo " make sim_system" + @echo "" + @echo "Timing and Utilization Evaluation:" + @echo " make table.txt" + @echo " make area" + @echo "" + +synth_%: + rm -f $@.log + $(VIVADO) -nojournal -log $@.log -mode batch -source $@.tcl + rm -rf .Xil fsm_encoding.os synth_*.backup.log usage_statistics_webtalk.* + -grep -B4 -A10 'Slice LUTs' $@.log + -grep -B1 -A9 ^Slack $@.log && echo + +synth_system: firmware.hex + +sim_system: + $(XVLOG) system_tb.v synth_system.v + $(XVLOG) $(GLBL) + $(XELAB) -L unifast_ver -L unisims_ver -R system_tb glbl + +firmware.hex: firmware.S firmware.c firmware.lds + $(TOOLCHAIN_PREFIX)gcc -Os -ffreestanding -nostdlib -o firmware.elf firmware.S firmware.c \ + --std=gnu99 -Wl,-Bstatic,-T,firmware.lds,-Map,firmware.map,--strip-debug -lgcc + $(TOOLCHAIN_PREFIX)objcopy -O binary firmware.elf firmware.bin + python3 ../../firmware/makehex.py firmware.bin 4096 > firmware.hex + +tab_%/results.txt: + bash tabtest.sh $@ + +area: synth_area_small synth_area_regular synth_area_large + -grep -B4 -A10 'Slice LUTs' synth_area_small.log synth_area_regular.log synth_area_large.log + +table.txt: tab_small_xc7k_2/results.txt tab_small_xc7k_3/results.txt +table.txt: tab_small_xc7v_2/results.txt tab_small_xc7v_3/results.txt +table.txt: tab_small_xcku_2/results.txt tab_small_xcku_3/results.txt +table.txt: tab_small_xcvu_2/results.txt tab_small_xcvu_3/results.txt +table.txt: tab_small_xckup_2/results.txt tab_small_xckup_3/results.txt +table.txt: tab_small_xcvup_2/results.txt tab_small_xcvup_3/results.txt + +table.txt: + bash table.sh > table.txt + +clean: + rm -rf .Xil/ firmware.bin firmware.elf firmware.hex firmware.map synth_*.log + rm -rf synth_*.mmi synth_*.bit synth_system.v table.txt tab_*/ webtalk.jou + rm -rf webtalk.log webtalk_*.jou webtalk_*.log xelab.* xsim[._]* xvlog.* + diff --git a/scripts/vivado/firmware.S b/scripts/vivado/firmware.S new file mode 100644 index 0000000..c55a3ba --- /dev/null +++ b/scripts/vivado/firmware.S @@ -0,0 +1,12 @@ +.section .init +.global main + +/* set stack pointer */ +lui sp, %hi(16*1024) +addi sp, sp, %lo(16*1024) + +/* call main */ +jal ra, main + +/* break */ +ebreak diff --git a/scripts/vivado/firmware.c b/scripts/vivado/firmware.c new file mode 100644 index 0000000..6c62169 --- /dev/null +++ b/scripts/vivado/firmware.c @@ -0,0 +1,43 @@ +void putc(char c) +{ + *(volatile char*)0x10000000 = c; +} + +void puts(const char *s) +{ + while (*s) putc(*s++); +} + +void *memcpy(void *dest, const void *src, int n) +{ + while (n) { + n--; + ((char*)dest)[n] = ((char*)src)[n]; + } + return dest; +} + +void main() +{ + char message[] = "$Uryyb+Jbeyq!+Vs+lbh+pna+ernq+guvf+zrffntr+gura$gur+CvpbEI32+PCH" + "+frrzf+gb+or+jbexvat+whfg+svar.$$++++++++++++++++GRFG+CNFFRQ!$$"; + for (int i = 0; message[i]; i++) + switch (message[i]) + { + case 'a' ... 'm': + case 'A' ... 'M': + message[i] += 13; + break; + case 'n' ... 'z': + case 'N' ... 'Z': + message[i] -= 13; + break; + case '$': + message[i] = '\n'; + break; + case '+': + message[i] = ' '; + break; + } + puts(message); +} diff --git a/scripts/vivado/firmware.lds b/scripts/vivado/firmware.lds new file mode 100644 index 0000000..970000a --- /dev/null +++ b/scripts/vivado/firmware.lds @@ -0,0 +1,11 @@ +SECTIONS { + .memory : { + . = 0x000000; + *(.init); + *(.text); + *(*); + . = ALIGN(4); + end = .; + } +} + diff --git a/scripts/vivado/synth_area.tcl b/scripts/vivado/synth_area.tcl new file mode 100644 index 0000000..c222a00 --- /dev/null +++ b/scripts/vivado/synth_area.tcl @@ -0,0 +1,8 @@ +read_verilog ../../picorv32.v +read_xdc synth_area.xdc + +synth_design -part xc7k70t-fbg676 -top picorv32_axi +opt_design -resynth_seq_area + +report_utilization +report_timing diff --git a/scripts/vivado/synth_area.xdc b/scripts/vivado/synth_area.xdc new file mode 100644 index 0000000..3c3d5a1 --- /dev/null +++ b/scripts/vivado/synth_area.xdc @@ -0,0 +1 @@ +create_clock -period 20.00 [get_ports clk] diff --git a/scripts/vivado/synth_area_large.tcl b/scripts/vivado/synth_area_large.tcl new file mode 100644 index 0000000..af611b5 --- /dev/null +++ b/scripts/vivado/synth_area_large.tcl @@ -0,0 +1,10 @@ +read_verilog ../../picorv32.v +read_verilog synth_area_top.v +read_xdc synth_area.xdc + +synth_design -part xc7k70t-fbg676 -top top_large +opt_design -sweep -propconst -resynth_seq_area +opt_design -directive ExploreSequentialArea + +report_utilization +report_timing diff --git a/scripts/vivado/synth_area_regular.tcl b/scripts/vivado/synth_area_regular.tcl new file mode 100644 index 0000000..2bf6b4c --- /dev/null +++ b/scripts/vivado/synth_area_regular.tcl @@ -0,0 +1,10 @@ +read_verilog ../../picorv32.v +read_verilog synth_area_top.v +read_xdc synth_area.xdc + +synth_design -part xc7k70t-fbg676 -top top_regular +opt_design -sweep -propconst -resynth_seq_area +opt_design -directive ExploreSequentialArea + +report_utilization +report_timing diff --git a/scripts/vivado/synth_area_small.tcl b/scripts/vivado/synth_area_small.tcl new file mode 100644 index 0000000..11d2104 --- /dev/null +++ b/scripts/vivado/synth_area_small.tcl @@ -0,0 +1,10 @@ +read_verilog ../../picorv32.v +read_verilog synth_area_top.v +read_xdc synth_area.xdc + +synth_design -part xc7k70t-fbg676 -top top_small +opt_design -sweep -propconst -resynth_seq_area +opt_design -directive ExploreSequentialArea + +report_utilization +report_timing diff --git a/scripts/vivado/synth_area_top.v b/scripts/vivado/synth_area_top.v new file mode 100644 index 0000000..6298a86 --- /dev/null +++ b/scripts/vivado/synth_area_top.v @@ -0,0 +1,140 @@ + +module top_small ( + input clk, resetn, + + output mem_valid, + output mem_instr, + input mem_ready, + + output [31:0] mem_addr, + output [31:0] mem_wdata, + output [ 3:0] mem_wstrb, + input [31:0] mem_rdata +); + picorv32 #( + .ENABLE_COUNTERS(0), + .LATCHED_MEM_RDATA(1), + .TWO_STAGE_SHIFT(0), + .CATCH_MISALIGN(0), + .CATCH_ILLINSN(0) + ) picorv32 ( + .clk (clk ), + .resetn (resetn ), + .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) + ); +endmodule + +module top_regular ( + input clk, resetn, + output trap, + + output mem_valid, + output mem_instr, + input mem_ready, + + output [31:0] mem_addr, + output [31:0] mem_wdata, + output [ 3:0] mem_wstrb, + input [31:0] mem_rdata, + + // Look-Ahead Interface + output mem_la_read, + output mem_la_write, + output [31:0] mem_la_addr, + output [31:0] mem_la_wdata, + output [ 3:0] mem_la_wstrb +); + picorv32 picorv32 ( + .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) + ); +endmodule + +module top_large ( + input clk, resetn, + output trap, + + output mem_valid, + output mem_instr, + input mem_ready, + + output [31:0] mem_addr, + output [31:0] mem_wdata, + output [ 3:0] mem_wstrb, + input [31:0] mem_rdata, + + // Look-Ahead Interface + output mem_la_read, + output mem_la_write, + output [31:0] mem_la_addr, + output [31:0] mem_la_wdata, + output [ 3:0] mem_la_wstrb, + + // Pico Co-Processor Interface (PCPI) + output pcpi_valid, + output [31:0] pcpi_insn, + output [31:0] pcpi_rs1, + output [31:0] pcpi_rs2, + input pcpi_wr, + input [31:0] pcpi_rd, + input pcpi_wait, + input pcpi_ready, + + // IRQ Interface + input [31:0] irq, + output [31:0] eoi +); + picorv32 #( + .COMPRESSED_ISA(1), + .BARREL_SHIFTER(1), + .ENABLE_PCPI(1), + .ENABLE_MUL(1), + .ENABLE_IRQ(1) + ) picorv32 ( + .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 ), + .pcpi_valid (pcpi_valid ), + .pcpi_insn (pcpi_insn ), + .pcpi_rs1 (pcpi_rs1 ), + .pcpi_rs2 (pcpi_rs2 ), + .pcpi_wr (pcpi_wr ), + .pcpi_rd (pcpi_rd ), + .pcpi_wait (pcpi_wait ), + .pcpi_ready (pcpi_ready ), + .irq (irq ), + .eoi (eoi ) + ); +endmodule + diff --git a/scripts/vivado/synth_speed.tcl b/scripts/vivado/synth_speed.tcl new file mode 100644 index 0000000..f3874e4 --- /dev/null +++ b/scripts/vivado/synth_speed.tcl @@ -0,0 +1,13 @@ + +read_verilog ../../picorv32.v +read_xdc synth_speed.xdc + +synth_design -part xc7k70t-fbg676 -top picorv32_axi +opt_design +place_design +phys_opt_design +route_design + +report_utilization +report_timing + diff --git a/scripts/vivado/synth_speed.xdc b/scripts/vivado/synth_speed.xdc new file mode 100644 index 0000000..877ec8d --- /dev/null +++ b/scripts/vivado/synth_speed.xdc @@ -0,0 +1 @@ +create_clock -period 2.50 [get_ports clk] diff --git a/scripts/vivado/synth_system.tcl b/scripts/vivado/synth_system.tcl new file mode 100644 index 0000000..26ea01c --- /dev/null +++ b/scripts/vivado/synth_system.tcl @@ -0,0 +1,17 @@ + +read_verilog system.v +read_verilog ../../picorv32.v +read_xdc synth_system.xdc + +synth_design -part xc7a35t-cpg236-1 -top system +opt_design +place_design +route_design + +report_utilization +report_timing + +write_verilog -force synth_system.v +write_bitstream -force synth_system.bit +# write_mem_info -force synth_system.mmi + diff --git a/scripts/vivado/synth_system.xdc b/scripts/vivado/synth_system.xdc new file mode 100644 index 0000000..5748466 --- /dev/null +++ b/scripts/vivado/synth_system.xdc @@ -0,0 +1,34 @@ + +# XDC File for Basys3 Board +########################### + +set_property PACKAGE_PIN W5 [get_ports clk] +set_property IOSTANDARD LVCMOS33 [get_ports clk] +create_clock -period 10.00 [get_ports clk] + +# Pmod Header JA (JA0..JA7) +set_property PACKAGE_PIN J1 [get_ports {out_byte[0]}] +set_property IOSTANDARD LVCMOS33 [get_ports {out_byte[0]}] +set_property PACKAGE_PIN L2 [get_ports {out_byte[1]}] +set_property IOSTANDARD LVCMOS33 [get_ports {out_byte[1]}] +set_property PACKAGE_PIN J2 [get_ports {out_byte[2]}] +set_property IOSTANDARD LVCMOS33 [get_ports {out_byte[2]}] +set_property PACKAGE_PIN G2 [get_ports {out_byte[3]}] +set_property IOSTANDARD LVCMOS33 [get_ports {out_byte[3]}] +set_property PACKAGE_PIN H1 [get_ports {out_byte[4]}] +set_property IOSTANDARD LVCMOS33 [get_ports {out_byte[4]}] +set_property PACKAGE_PIN K2 [get_ports {out_byte[5]}] +set_property IOSTANDARD LVCMOS33 [get_ports {out_byte[5]}] +set_property PACKAGE_PIN H2 [get_ports {out_byte[6]}] +set_property IOSTANDARD LVCMOS33 [get_ports {out_byte[6]}] +set_property PACKAGE_PIN G3 [get_ports {out_byte[7]}] +set_property IOSTANDARD LVCMOS33 [get_ports {out_byte[7]}] + +# Pmod Header JB (JB0..JB2) +set_property PACKAGE_PIN A14 [get_ports {resetn}] +set_property IOSTANDARD LVCMOS33 [get_ports {resetn}] +set_property PACKAGE_PIN A16 [get_ports {trap}] +set_property IOSTANDARD LVCMOS33 [get_ports {trap}] +set_property PACKAGE_PIN B15 [get_ports {out_byte_en}] +set_property IOSTANDARD LVCMOS33 [get_ports {out_byte_en}] + diff --git a/scripts/vivado/system.v b/scripts/vivado/system.v new file mode 100644 index 0000000..c4882a1 --- /dev/null +++ b/scripts/vivado/system.v @@ -0,0 +1,101 @@ +`timescale 1 ns / 1 ps + +module system ( + input clk, + input resetn, + output trap, + output reg [7:0] out_byte, + output reg out_byte_en +); + // set this to 0 for better timing but less performance/MHz + parameter FAST_MEMORY = 1; + + // 4096 32bit words = 16kB memory + parameter MEM_SIZE = 4096; + + 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; + + picorv32 picorv32_core ( + .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) + ); + + reg [31:0] memory [0:MEM_SIZE-1]; + initial $readmemh("firmware.hex", memory); + + reg [31:0] m_read_data; + reg m_read_en; + + generate if (FAST_MEMORY) begin + always @(posedge clk) begin + mem_ready <= 1; + out_byte_en <= 0; + mem_rdata <= memory[mem_la_addr >> 2]; + if (mem_la_write && (mem_la_addr >> 2) < MEM_SIZE) begin + 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_la_write && mem_la_addr == 32'h1000_0000) begin + out_byte_en <= 1; + out_byte <= mem_la_wdata; + end + end + end else begin + always @(posedge clk) begin + m_read_en <= 0; + mem_ready <= mem_valid && !mem_ready && m_read_en; + + m_read_data <= memory[mem_addr >> 2]; + mem_rdata <= m_read_data; + + out_byte_en <= 0; + + (* parallel_case *) + case (1) + mem_valid && !mem_ready && !mem_wstrb && (mem_addr >> 2) < MEM_SIZE: begin + m_read_en <= 1; + end + mem_valid && !mem_ready && |mem_wstrb && (mem_addr >> 2) < MEM_SIZE: 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]; + mem_ready <= 1; + end + mem_valid && !mem_ready && |mem_wstrb && mem_addr == 32'h1000_0000: begin + out_byte_en <= 1; + out_byte <= mem_wdata; + mem_ready <= 1; + end + endcase + end + end endgenerate +endmodule diff --git a/scripts/vivado/system_tb.v b/scripts/vivado/system_tb.v new file mode 100644 index 0000000..a66d612 --- /dev/null +++ b/scripts/vivado/system_tb.v @@ -0,0 +1,38 @@ +`timescale 1 ns / 1 ps + +module system_tb; + reg clk = 1; + always #5 clk = ~clk; + + reg resetn = 0; + initial begin + if ($test$plusargs("vcd")) begin + $dumpfile("system.vcd"); + $dumpvars(0, system_tb); + end + repeat (100) @(posedge clk); + resetn <= 1; + end + + wire trap; + wire [7:0] out_byte; + wire out_byte_en; + + system uut ( + .clk (clk ), + .resetn (resetn ), + .trap (trap ), + .out_byte (out_byte ), + .out_byte_en(out_byte_en) + ); + + always @(posedge clk) begin + if (resetn && out_byte_en) begin + $write("%c", out_byte); + $fflush; + end + if (resetn && trap) begin + $finish; + end + end +endmodule diff --git a/scripts/vivado/table.sh b/scripts/vivado/table.sh new file mode 100644 index 0000000..81e2cf4 --- /dev/null +++ b/scripts/vivado/table.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +dashes="----------------------------------------------------------------" +printf '| %-25s | %-10s | %-20s |\n' "Device" "Speedgrade" "Clock Period (Freq.)" +printf '|:%.25s |:%.10s:| %.20s:|\n' $dashes $dashes $dashes + +for x in $( grep -H . tab_*/results.txt ) +do + read _ size device grade _ speed < <( echo "$x" | tr _/: ' ' ) + case "$device" in + xc7a) d="Xilinx Artix-7T" ;; + xc7k) d="Xilinx Kintex-7T" ;; + xc7v) d="Xilinx Virtex-7T" ;; + xcku) d="Xilinx Kintex UltraScale" ;; + xcvu) d="Xilinx Virtex UltraScale" ;; + xckup) d="Xilinx Kintex UltraScale+" ;; + xcvup) d="Xilinx Virtex UltraScale+" ;; + esac + speedtxt=$( printf '%s.%s ns (%d MHz)' ${speed%?} ${speed#?} $((10000 / speed)) ) + printf '| %-25s | %-10s | %20s |\n' "$d" "-$grade" "$speedtxt" +done diff --git a/scripts/vivado/tabtest.sh b/scripts/vivado/tabtest.sh new file mode 100644 index 0000000..bc3d840 --- /dev/null +++ b/scripts/vivado/tabtest.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +set -e +read _ ip dev grade _ < <( echo $* | tr '_/' ' '; ) + +# rm -rf tab_${ip}_${dev}_${grade} +mkdir -p tab_${ip}_${dev}_${grade} +cd tab_${ip}_${dev}_${grade} + +best_speed=99 +speed=20 +step=16 + +synth_case() { + if [ -f test_${1}.txt ]; then + echo "Reusing cached tab_${ip}_${dev}_${grade}/test_${1}." + return + fi + + case "${dev}" in + xc7k) xl_device="xc7k70t-fbg676-${grade}" ;; + xc7v) xl_device="xc7v585t-ffg1761-${grade}" ;; + xcku) xl_device="xcku035-fbva676-${grade}-e" ;; + xcvu) xl_device="xcvu065-ffvc1517-${grade}-e" ;; + xckup) xl_device="xcku3p-ffva676-${grade}-e" ;; + xcvup) xl_device="xcvu3p-ffvc1517-${grade}-e" ;; + esac + + cat > test_${1}.tcl <<- EOT + read_verilog ../tabtest.v + read_verilog ../../../picorv32.v + read_xdc test_${1}.xdc + synth_design -flatten_hierarchy full -part ${xl_device} -top top + opt_design -sweep -remap -propconst + opt_design -directive Explore + place_design -directive Explore + phys_opt_design -retime -rewire -critical_pin_opt -placement_opt -critical_cell_opt + route_design -directive Explore + place_design -post_place_opt + phys_opt_design -retime + route_design -directive NoTimingRelaxation + report_utilization + report_timing + EOT + + cat > test_${1}.xdc <<- EOT + create_clock -period ${speed%?}.${speed#?} [get_ports clk] + EOT + + echo "Running tab_${ip}_${dev}_${grade}/test_${1}.." + if ! $VIVADO -nojournal -log test_${1}.log -mode batch -source test_${1}.tcl > /dev/null 2>&1; then + cat test_${1}.log + exit 1 + fi + mv test_${1}.log test_${1}.txt +} + +got_violated=false +got_met=false + +countdown=2 +while [ $countdown -gt 0 ]; do + synth_case $speed + + if grep -q '^Slack.*(VIOLATED)' test_${speed}.txt; then + echo " tab_${ip}_${dev}_${grade}/test_${speed} VIOLATED" + step=$((step / 2)) + speed=$((speed + step)) + got_violated=true + elif grep -q '^Slack.*(MET)' test_${speed}.txt; then + echo " tab_${ip}_${dev}_${grade}/test_${speed} MET" + [ $speed -lt $best_speed ] && best_speed=$speed + step=$((step / 2)) + speed=$((speed - step)) + got_met=true + else + echo "ERROR: No slack line found in $PWD/test_${speed}.txt!" + exit 1 + fi + + if [ $step -eq 0 ]; then + countdown=$((countdown - 1)) + speed=$((best_speed - 2)) + step=1 + fi +done + +if ! $got_violated; then + echo "ERROR: No timing violated in $PWD!" + exit 1 +fi + +if ! $got_met; then + echo "ERROR: No timing met in $PWD!" + exit 1 +fi + + +echo "-----------------------" +echo "Best speed for tab_${ip}_${dev}_${grade}: $best_speed" +echo "-----------------------" +echo $best_speed > results.txt + diff --git a/scripts/vivado/tabtest.v b/scripts/vivado/tabtest.v new file mode 100644 index 0000000..cdf2057 --- /dev/null +++ b/scripts/vivado/tabtest.v @@ -0,0 +1,118 @@ + +module top ( + input clk, io_resetn, + output io_trap, + + output io_mem_axi_awvalid, + input io_mem_axi_awready, + output [31:0] io_mem_axi_awaddr, + output [ 2:0] io_mem_axi_awprot, + + output io_mem_axi_wvalid, + input io_mem_axi_wready, + output [31:0] io_mem_axi_wdata, + output [ 3:0] io_mem_axi_wstrb, + + input io_mem_axi_bvalid, + output io_mem_axi_bready, + + output io_mem_axi_arvalid, + input io_mem_axi_arready, + output [31:0] io_mem_axi_araddr, + output [ 2:0] io_mem_axi_arprot, + + input io_mem_axi_rvalid, + output io_mem_axi_rready, + input [31:0] io_mem_axi_rdata, + + input [31:0] io_irq, + output [31:0] io_eoi +); + wire resetn; + wire trap; + wire mem_axi_awvalid; + wire mem_axi_awready; + wire [31:0] mem_axi_awaddr; + wire [2:0] mem_axi_awprot; + wire mem_axi_wvalid; + wire mem_axi_wready; + wire [31:0] mem_axi_wdata; + wire [3:0] mem_axi_wstrb; + wire mem_axi_bvalid; + wire mem_axi_bready; + wire mem_axi_arvalid; + wire mem_axi_arready; + wire [31:0] mem_axi_araddr; + wire [2:0] mem_axi_arprot; + wire mem_axi_rvalid; + wire mem_axi_rready; + wire [31:0] mem_axi_rdata; + wire [31:0] irq; + wire [31:0] eoi; + + delay4 #( 1) delay_resetn (clk, io_resetn , resetn ); + delay4 #( 1) delay_trap (clk, trap , io_trap ); + delay4 #( 1) delay_mem_axi_awvalid (clk, mem_axi_awvalid, io_mem_axi_awvalid); + delay4 #( 1) delay_mem_axi_awready (clk, io_mem_axi_awready, mem_axi_awready); + delay4 #(32) delay_mem_axi_awaddr (clk, mem_axi_awaddr , io_mem_axi_awaddr ); + delay4 #( 3) delay_mem_axi_awprot (clk, mem_axi_awprot , io_mem_axi_awprot ); + delay4 #( 1) delay_mem_axi_wvalid (clk, mem_axi_wvalid , io_mem_axi_wvalid ); + delay4 #( 1) delay_mem_axi_wready (clk, io_mem_axi_wready , mem_axi_wready ); + delay4 #(32) delay_mem_axi_wdata (clk, mem_axi_wdata , io_mem_axi_wdata ); + delay4 #( 4) delay_mem_axi_wstrb (clk, mem_axi_wstrb , io_mem_axi_wstrb ); + delay4 #( 1) delay_mem_axi_bvalid (clk, io_mem_axi_bvalid , mem_axi_bvalid ); + delay4 #( 1) delay_mem_axi_bready (clk, mem_axi_bready , io_mem_axi_bready ); + delay4 #( 1) delay_mem_axi_arvalid (clk, mem_axi_arvalid, io_mem_axi_arvalid); + delay4 #( 1) delay_mem_axi_arready (clk, io_mem_axi_arready, mem_axi_arready); + delay4 #(32) delay_mem_axi_araddr (clk, mem_axi_araddr , io_mem_axi_araddr ); + delay4 #( 3) delay_mem_axi_arprot (clk, mem_axi_arprot , io_mem_axi_arprot ); + delay4 #( 1) delay_mem_axi_rvalid (clk, io_mem_axi_rvalid , mem_axi_rvalid ); + delay4 #( 1) delay_mem_axi_rready (clk, mem_axi_rready , io_mem_axi_rready ); + delay4 #(32) delay_mem_axi_rdata (clk, io_mem_axi_rdata , mem_axi_rdata ); + delay4 #(32) delay_irq (clk, io_irq , irq ); + delay4 #(32) delay_eoi (clk, eoi , io_eoi ); + + picorv32_axi #( + .TWO_CYCLE_ALU(1) + ) cpu ( + .clk (clk ), + .resetn (resetn ), + .trap (trap ), + .mem_axi_awvalid(mem_axi_awvalid), + .mem_axi_awready(mem_axi_awready), + .mem_axi_awaddr (mem_axi_awaddr ), + .mem_axi_awprot (mem_axi_awprot ), + .mem_axi_wvalid (mem_axi_wvalid ), + .mem_axi_wready (mem_axi_wready ), + .mem_axi_wdata (mem_axi_wdata ), + .mem_axi_wstrb (mem_axi_wstrb ), + .mem_axi_bvalid (mem_axi_bvalid ), + .mem_axi_bready (mem_axi_bready ), + .mem_axi_arvalid(mem_axi_arvalid), + .mem_axi_arready(mem_axi_arready), + .mem_axi_araddr (mem_axi_araddr ), + .mem_axi_arprot (mem_axi_arprot ), + .mem_axi_rvalid (mem_axi_rvalid ), + .mem_axi_rready (mem_axi_rready ), + .mem_axi_rdata (mem_axi_rdata ), + .irq (irq ), + .eoi (eoi ) + ); +endmodule + +module delay4 #( + parameter WIDTH = 1 +) ( + input clk, + input [WIDTH-1:0] in, + output reg [WIDTH-1:0] out +); + reg [WIDTH-1:0] q1, q2, q3; + always @(posedge clk) begin + q1 <= in; + q2 <= q1; + q3 <= q2; + out <= q3; + end +endmodule + diff --git a/scripts/yosys-cmp/README.md b/scripts/yosys-cmp/README.md new file mode 100644 index 0000000..acb2c6c --- /dev/null +++ b/scripts/yosys-cmp/README.md @@ -0,0 +1,62 @@ + +Synthesis results for the PicoRV32 core (in its default configuration) with Yosys 0.5+383 (git sha1 8648089), Synplify Pro and Lattice LSE from iCEcube2.2014.08, and Xilinx Vivado 2015.3. + +No timing constraints were used for synthesis; only resource utilisation is compared. + +Last updated: 2015-10-30 + + +Results for iCE40 Synthesis +--------------------------- + +| Cell | Yosys | Synplify Pro | Lattice LSE | +|:--------------|------:|-------------:|------------:| +| `SB_CARRY` | 405 | 349 | 309 | +| `SB_DFF` | 125 | 256 | 114 | +| `SB_DFFE` | 251 | 268 | 76 | +| `SB_DFFESR` | 172 | 39 | 147 | +| `SB_DFFESS` | 1 | 0 | 69 | +| `SB_DFFSR` | 69 | 137 | 134 | +| `SB_DFFSS` | 0 | 0 | 36 | +| `SB_LUT4` | 1795 | 1657 | 1621 | +| `SB_RAM40_4K` | 4 | 4 | 4 | + +Summary: + +| Cell | Yosys | Synplify Pro | Lattice LSE | +|:--------------|------:|-------------:|------------:| +| `SB_CARRY` | 405 | 349 | 309 | +| `SB_DFF*` | 618 | 700 | 576 | +| `SB_LUT4` | 1795 | 1657 | 1621 | +| `SB_RAM40_4K` | 4 | 4 | 4 | + + +Results for Xilinx 7-Series Synthesis +------------------------------------- + +| Cell | Yosys | Vivado | +|:------------|------:|-------:| +| `FDRE` | 671 | 553 | +| `FDSE` | 0 | 21 | +| `LUT1` | 41 | 160 | +| `LUT2` | 517 | 122 | +| `LUT3` | 77 | 120 | +| `LUT4` | 136 | 204 | +| `LUT5` | 142 | 135 | +| `LUT6` | 490 | 405 | +| `MUXF7` | 54 | 0 | +| `MUXF8` | 15 | 0 | +| `MUXCY` | 420 | 0 | +| `XORCY` | 359 | 0 | +| `CARRY4` | 0 | 83 | +| `RAMD32` | 0 | 72 | +| `RAMS32` | 0 | 24 | +| `RAM64X1D` | 64 | 0 | + +Summary: + +| Cell | Yosys | Vivado | +|:------------|------:|-------:| +| `FD*` | 671 | 574 | +| `LUT*` | 1403 | 1146 | + diff --git a/scripts/yosys-cmp/lse.sh b/scripts/yosys-cmp/lse.sh new file mode 100644 index 0000000..a802c67 --- /dev/null +++ b/scripts/yosys-cmp/lse.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +set -ex + +rm -rf lse.tmp +mkdir lse.tmp +cd lse.tmp + +cat > lse.prj << EOT +#device +-a SBTiCE40 +-d iCE40HX8K +-t CT256 +#constraint file + +#options +-frequency 200 +-optimization_goal Area +-twr_paths 3 +-bram_utilization 100.00 +-ramstyle Auto +-romstyle Auto +-use_carry_chain 1 +-carry_chain_length 0 +-resource_sharing 1 +-propagate_constants 1 +-remove_duplicate_regs 1 +-max_fanout 10000 +-fsm_encoding_style Auto +-use_io_insertion 1 +-use_io_reg auto +-ifd +-resolve_mixed_drivers 0 +-RWCheckOnRam 0 +-fix_gated_clocks 1 +-top picorv32 + +-ver "../../../picorv32.v" +-p "." + +#set result format/file last +-output_edif output.edf + +#set log file +-logfile "lse.log" +EOT + +icecubedir="${ICECUBEDIR:-/opt/lscc/iCEcube2.2014.08}" +export FOUNDRY="$icecubedir/LSE" +export LD_LIBRARY_PATH="$LD_LIBRARY_PATH${LD_LIBRARY_PATH:+:}$icecubedir/LSE/bin/lin" +"$icecubedir"/LSE/bin/lin/synthesis -f lse.prj + +grep 'viewRef.*cellRef' output.edf | sed 's,.*cellRef *,,; s,[ )].*,,;' | sort | uniq -c diff --git a/scripts/yosys-cmp/synplify.sh b/scripts/yosys-cmp/synplify.sh new file mode 100644 index 0000000..d0a2a08 --- /dev/null +++ b/scripts/yosys-cmp/synplify.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +set -ex + +rm -rf synplify.tmp +mkdir synplify.tmp +cd synplify.tmp + +cat > impl_syn.prj << EOT +add_file -verilog -lib work ../../../picorv32.v +impl -add impl -type fpga + +# implementation attributes +set_option -vlog_std v2001 +set_option -project_relative_includes 1 + +# device options +set_option -technology SBTiCE40 +set_option -part iCE40HX8K +set_option -package CT256 +set_option -speed_grade +set_option -part_companion "" + +# compilation/mapping options +set_option -top_module "picorv32" + +# mapper_options +set_option -frequency auto +set_option -write_verilog 0 +set_option -write_vhdl 0 + +# Silicon Blue iCE40 +set_option -maxfan 10000 +set_option -disable_io_insertion 0 +set_option -pipe 1 +set_option -retiming 0 +set_option -update_models_cp 0 +set_option -fixgatedclocks 2 +set_option -fixgeneratedclocks 0 + +# NFilter +set_option -popfeed 0 +set_option -constprop 0 +set_option -createhierarchy 0 + +# sequential_optimization_options +set_option -symbolic_fsm_compiler 1 + +# Compiler Options +set_option -compiler_compatible 0 +set_option -resource_sharing 1 + +# automatic place and route (vendor) options +set_option -write_apr_constraint 1 + +# set result format/file last +project -result_format edif +project -result_file impl.edf +impl -active impl +project -run synthesis -clean +EOT + +icecubedir="${ICECUBEDIR:-/opt/lscc/iCEcube2.2014.08}" +export SBT_DIR="$icecubedir/sbt_backend" +export SYNPLIFY_PATH="$icecubedir/synpbase" +export LM_LICENSE_FILE="$icecubedir/license.dat" +export TCL_LIBRARY="$icecubedir/sbt_backend/bin/linux/lib/tcl8.4" +export LD_LIBRARY_PATH="$LD_LIBRARY_PATH${LD_LIBRARY_PATH:+:}$icecubedir/sbt_backend/bin/linux/opt" +export LD_LIBRARY_PATH="$LD_LIBRARY_PATH${LD_LIBRARY_PATH:+:}$icecubedir/sbt_backend/bin/linux/opt/synpwrap" +export LD_LIBRARY_PATH="$LD_LIBRARY_PATH${LD_LIBRARY_PATH:+:}$icecubedir/sbt_backend/lib/linux/opt" +export LD_LIBRARY_PATH="$LD_LIBRARY_PATH${LD_LIBRARY_PATH:+:}$icecubedir/LSE/bin/lin" +"$icecubedir"/sbt_backend/bin/linux/opt/synpwrap/synpwrap -prj impl_syn.prj -log impl.srr + +grep 'instance.*cellRef' impl/impl.edf | sed 's,.*cellRef *,,; s,[ )].*,,;' | sort | uniq -c diff --git a/scripts/yosys-cmp/vivado.tcl b/scripts/yosys-cmp/vivado.tcl new file mode 100644 index 0000000..560b880 --- /dev/null +++ b/scripts/yosys-cmp/vivado.tcl @@ -0,0 +1,3 @@ +read_verilog ../../picorv32.v +synth_design -part xc7k70t-fbg676 -top picorv32 +report_utilization diff --git a/scripts/yosys-cmp/yosys_ice40.ys b/scripts/yosys-cmp/yosys_ice40.ys new file mode 100644 index 0000000..b14b338 --- /dev/null +++ b/scripts/yosys-cmp/yosys_ice40.ys @@ -0,0 +1,2 @@ +read_verilog ../../picorv32.v +synth_ice40 -top picorv32 diff --git a/scripts/yosys-cmp/yosys_xilinx.ys b/scripts/yosys-cmp/yosys_xilinx.ys new file mode 100644 index 0000000..ead52a4 --- /dev/null +++ b/scripts/yosys-cmp/yosys_xilinx.ys @@ -0,0 +1,2 @@ +read_verilog ../../picorv32.v +synth_xilinx -top picorv32 diff --git a/scripts/yosys/.gitignore b/scripts/yosys/.gitignore new file mode 100644 index 0000000..d6fc3e3 --- /dev/null +++ b/scripts/yosys/.gitignore @@ -0,0 +1 @@ +osu018_stdcells.lib diff --git a/scripts/yosys/synth_gates.lib b/scripts/yosys/synth_gates.lib new file mode 100644 index 0000000..be706dd --- /dev/null +++ b/scripts/yosys/synth_gates.lib @@ -0,0 +1,38 @@ +library(gates) { + cell(NOT) { + area: 2; // 7404 hex inverter + pin(A) { direction: input; } + pin(Y) { direction: output; + function: "A'"; } + } + cell(BUF) { + area: 4; // 2x 7404 hex inverter + pin(A) { direction: input; } + pin(Y) { direction: output; + function: "A"; } + } + cell(NAND) { + area: 3; // 7400 quad 2-input NAND gate + pin(A) { direction: input; } + pin(B) { direction: input; } + pin(Y) { direction: output; + function: "(A*B)'"; } + } + cell(NOR) { + area: 3; // 7402 quad 2-input NOR gate + pin(A) { direction: input; } + pin(B) { direction: input; } + pin(Y) { direction: output; + function: "(A+B)'"; } + } + cell(DFF) { + area: 6; // 7474 dual D positive edge triggered flip-flop + ff(IQ, IQN) { clocked_on: C; + next_state: D; } + pin(C) { direction: input; + clock: true; } + pin(D) { direction: input; } + pin(Q) { direction: output; + function: "IQ"; } + } +} diff --git a/scripts/yosys/synth_gates.v b/scripts/yosys/synth_gates.v new file mode 100644 index 0000000..8e2504e --- /dev/null +++ b/scripts/yosys/synth_gates.v @@ -0,0 +1,30 @@ +module top ( + input clk, resetn, + + output mem_valid, + output mem_instr, + input mem_ready, + + output [31:0] mem_addr, + output [31:0] mem_wdata, + output [ 3:0] mem_wstrb, + input [31:0] mem_rdata +); + picorv32 #( + .ENABLE_COUNTERS(0), + .LATCHED_MEM_RDATA(1), + .TWO_STAGE_SHIFT(0), + .CATCH_MISALIGN(0), + .CATCH_ILLINSN(0) + ) picorv32 ( + .clk (clk ), + .resetn (resetn ), + .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) + ); +endmodule diff --git a/scripts/yosys/synth_gates.ys b/scripts/yosys/synth_gates.ys new file mode 100644 index 0000000..311d767 --- /dev/null +++ b/scripts/yosys/synth_gates.ys @@ -0,0 +1,14 @@ +read_verilog synth_gates.v +read_verilog ../../picorv32.v + +hierarchy -top top +proc; flatten + +synth + +dfflibmap -prepare -liberty synth_gates.lib +abc -dff -liberty synth_gates.lib +dfflibmap -liberty synth_gates.lib + +stat +write_blif synth_gates.blif diff --git a/scripts/yosys/synth_osu018.sh b/scripts/yosys/synth_osu018.sh new file mode 100644 index 0000000..7a8693d --- /dev/null +++ b/scripts/yosys/synth_osu018.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -ex +if test ! -s osu018_stdcells.lib; then + wget --continue -O osu018_stdcells.lib.part http://vlsiarch.ecen.okstate.edu/flows/MOSIS_SCMOS/` + `latest/cadence/lib/tsmc018/signalstorm/osu018_stdcells.lib + mv osu018_stdcells.lib.part osu018_stdcells.lib +fi +yosys -p 'synth -top picorv32; dfflibmap -liberty osu018_stdcells.lib; abc -liberty osu018_stdcells.lib; stat' ../../picorv32.v diff --git a/scripts/yosys/synth_sim.ys b/scripts/yosys/synth_sim.ys new file mode 100644 index 0000000..ded89d9 --- /dev/null +++ b/scripts/yosys/synth_sim.ys @@ -0,0 +1,8 @@ +# yosys synthesis script for post-synthesis simulation (make test_synth) + +read_verilog picorv32.v +chparam -set COMPRESSED_ISA 1 -set ENABLE_MUL 1 -set ENABLE_DIV 1 \ + -set ENABLE_IRQ 1 -set ENABLE_TRACE 1 picorv32_axi +hierarchy -top picorv32_axi +synth +write_verilog synth.v diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..92ec1ff --- /dev/null +++ b/shell.nix @@ -0,0 +1,139 @@ +# nix.shell: PicoRV32 Development Environment +# +# This file allows you to use the Nix Package Manager (https://nixos.org/nix) +# in order to download, install, and prepare a working environment for doing +# PicoRV32/PicoSoC development on _any_ existing Linux distribution, provided +# the Nix package manager is installed. +# +# Current included tools: +# +# - Synthesis: Recent Yosys and SymbiYosys +# - Place and Route: arachne-pnr and nextpnr (ICE40, ECP5, Python, no GUI) +# - Packing: Project IceStorm (Trellis tools may be included later?) +# - SMT Solvers: Z3 4.7.x, Yices 2.6.x, and Boolector 3.0.x +# - Verification: Recent Verilator, Recent (unreleased) Icarus Verilog +# - A bare-metal RISC-V cross compiler toolchain, based on GCC 8.2.x +# +# With these tools, you can immediately begin development, simulation, firmware +# hacking, etc with almost no need to fiddle with recent tools yourself. Almost +# all of the tools will be downloaded on-demand (except the GCC toolchain) +# meaning you don't have to compile any recent tools yourself. Due to the +# "hermetic" nature of Nix, these packages should also work on practically any +# Linux distribution, as well. +# +# (This environment should also be suitable for running riscv-formal test +# harnesses on PicoRV32, as well. In fact it is probably useful for almost +# _any_ RTL implementation of the RV32I core.) +# +# Usage +# ----- +# +# At the top-level of the picorv32 directory, simply run the 'nix-shell' command, +# which will then drop you into a bash prompt: +# +# +# $ nix-shell +# ... +# [nix-shell:~/src/picorv32]$ +# +# +# When you run 'nix-shell', you will automatically begin downloading all of the +# various tools you need from an upstream "cache", so most of this will execute +# very quickly. However, this may take a while, as you will at least have to +# build a cross-compiled RISC-V toolchain, which may take some time. (These +# binaries are not available from the cache, so they must be built by you.) Once +# you have done this once, you do not need to do it again. +# +# At this point, once you are inside the shell, you can begin running tests +# like normal. For example, to run the Verilator tests with the included test +# firmware, which is substantially faster than Icarus: +# +# [nix-shell:~/src/picorv32]$ make test_verilator TOOLCHAIN_PREFIX=riscv32-unknown-elf- +# ... +# +# +# Note that you must override TOOLCHAIN_PREFIX (in the top-level Makefile, it +# looks in /opt by default). +# +# This will work immediately with no extra fiddling necessary. You can also run +# formal verification tests using a provided SMT solver, for example, yices and +# boolector (Z3 is not used since it does not complete in a reasonable amount +# of time for these examples): +# +# [nix-shell:~/src/picorv32]$ make check-yices check-boolector +# ... +# +# You can also run the PicoSoC tests and build bitstreams. To run the +# simulation tests and then build bitstreams for the HX8K and IceBreaker +# boards: +# +# [nix-shell:~/src/picorv32]$ cd picosoc/ +# [nix-shell:~/src/picorv32/picosoc]$ make hx8ksynsim icebsynsim +# ... +# [nix-shell:~/src/picorv32/picosoc]$ make hx8kdemo.bin icebreaker.bin +# ... +# +# The HX8K simulation and IceBreaker simulation will be synthesized with Yosys +# and then run with Icarus Verilog. The bitstreams for HX8K and IceBreaker will +# be P&R'd with arachne-pnr and nextpnr, respectively. +# + +{ architecture ? "rv32imc" +}: + +# TODO FIXME: fix this to a specific version of nixpkgs. +# ALSO: maybe use cachix to make it easier for contributors(?) +with import {}; + +let + # risc-v toolchain source code. TODO FIXME: this should be replaced with + # upstream versions of GCC. in the future we could also include LLVM (the + # upstream nixpkgs LLVM expression should be built with it in time) + riscv-toolchain-ver = "8.2.0"; + riscv-src = pkgs.fetchFromGitHub { + owner = "riscv"; + repo = "riscv-gnu-toolchain"; + rev = "c3ad5556197e374c25bc475ffc9285b831f869f8"; + sha256 = "1j9y3ai42xzzph9rm116sxfzhdlrjrk4z0v4yrk197j72isqyxbc"; + fetchSubmodules = true; + }; + + # given an architecture like 'rv32i', this will generate the given + # toolchain derivation based on the above source code. + make-riscv-toolchain = arch: + stdenv.mkDerivation rec { + name = "riscv-${arch}-toolchain-${version}"; + version = "${riscv-toolchain-ver}-${builtins.substring 0 7 src.rev}"; + src = riscv-src; + + configureFlags = [ "--with-arch=${arch}" ]; + installPhase = ":"; # 'make' installs on its own + hardeningDisable = [ "all" ]; + enableParallelBuilding = true; + + # Stripping/fixups break the resulting libgcc.a archives, somehow. + # Maybe something in stdenv that does this... + dontStrip = true; + dontFixup = true; + + nativeBuildInputs = with pkgs; [ curl gawk texinfo bison flex gperf ]; + buildInputs = with pkgs; [ libmpc mpfr gmp expat ]; + }; + + riscv-toolchain = make-riscv-toolchain architecture; + + # These are all the packages that will be available inside the nix-shell + # environment. + buildInputs = with pkgs; + # these are generally useful packages for tests, verification, synthesis + # and deployment, etc + [ python3 gcc + yosys symbiyosys nextpnr arachne-pnr icestorm + z3 boolector yices + verilog verilator + # also include the RISC-V toolchain + riscv-toolchain + ]; + +# Export a usable shell environment +in runCommand "picorv32-shell" { inherit buildInputs; } "" diff --git a/showtrace.py b/showtrace.py new file mode 100644 index 0000000..a5d1021 --- /dev/null +++ b/showtrace.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import sys, re, subprocess + +trace_filename = sys.argv[1] +elf_filename = sys.argv[2] + +insns = dict() + +with subprocess.Popen(["riscv32-unknown-elf-objdump", "-d", elf_filename], stdout=subprocess.PIPE) as proc: + while True: + line = proc.stdout.readline().decode("ascii") + if line == '': break + match = re.match(r'^\s*([0-9a-f]+):\s+([0-9a-f]+)\s*(.*)', line) + if match: insns[int(match.group(1), 16)] = (int(match.group(2), 16), match.group(3).replace("\t", " ")) + +with open(trace_filename, "r") as f: + pc = -1 + last_irq = False + for line in f: + raw_data = int(line.replace("x", "0"), 16) + payload = raw_data & 0xffffffff + irq_active = (raw_data & 0x800000000) != 0 + is_addr = (raw_data & 0x200000000) != 0 + is_branch = (raw_data & 0x100000000) != 0 + info = "%s %s%08x" % ("IRQ" if irq_active or last_irq else " ", + ">" if is_branch else "@" if is_addr else "=", payload) + + if irq_active and not last_irq: + pc = 0x10 + + if pc >= 0: + if pc in insns: + insn_opcode, insn_desc = insns[pc] + opname = insn_desc.split()[0] + + if insn_opcode == 0x0400000b: + insn_desc = "retirq" + opname = "retirq" + + if is_branch and opname not in ["j", "jal", "jr", "jalr", "ret", "retirq", + "beq", "bne", "blt", "ble", "bge", "bgt", "bltu", "bleu", "bgeu", "bgtu", + "beqz", "bnez", "blez", "bgez", "bltz", "bgtz"]: + print("%s ** UNEXPECTED BRANCH DATA FOR INSN AT %08x! **" % (info, pc)) + + if is_addr and opname not in ["lb", "lh", "lw", "lbu", "lhu", "sb", "sh", "sw"]: + print("%s ** UNEXPECTED ADDR DATA FOR INSN AT %08x! **" % (info, pc)) + + opcode_fmt = "%08x" if (insn_opcode & 3) == 3 else " %04x" + print(("%s | %08x | " + opcode_fmt + " | %s") % (info, pc, insn_opcode, insn_desc)) + if not is_addr: + pc += 4 if (insn_opcode & 3) == 3 else 2 + else: + print("%s ** NO INFORMATION ON INSN AT %08x! **" % (info, pc)) + pc = -1 + else: + if is_branch: + print("%s ** FOUND BRANCH AND STARTING DECODING **" % info) + else: + print("%s ** SKIPPING DATA UNTIL NEXT BRANCH **" % info) + + if is_branch: + pc = payload + + last_irq = irq_active + diff --git a/testbench.cc b/testbench.cc new file mode 100644 index 0000000..61c4366 --- /dev/null +++ b/testbench.cc @@ -0,0 +1,44 @@ +#include "Vpicorv32_wrapper.h" +#include "verilated_vcd_c.h" + +int main(int argc, char **argv, char **env) +{ + printf("Built with %s %s.\n", Verilated::productName(), Verilated::productVersion()); + printf("Recommended: Verilator 4.0 or later.\n"); + + Verilated::commandArgs(argc, argv); + Vpicorv32_wrapper* top = new Vpicorv32_wrapper; + + // Tracing (vcd) + VerilatedVcdC* tfp = NULL; + const char* flag_vcd = Verilated::commandArgsPlusMatch("vcd"); + if (flag_vcd && 0==strcmp(flag_vcd, "+vcd")) { + Verilated::traceEverOn(true); + tfp = new VerilatedVcdC; + top->trace (tfp, 99); + tfp->open("testbench.vcd"); + } + + // Tracing (data bus, see showtrace.py) + FILE *trace_fd = NULL; + const char* flag_trace = Verilated::commandArgsPlusMatch("trace"); + if (flag_trace && 0==strcmp(flag_trace, "+trace")) { + trace_fd = fopen("testbench.trace", "w"); + } + + top->clk = 0; + int t = 0; + while (!Verilated::gotFinish()) { + if (t > 200) + top->resetn = 1; + top->clk = !top->clk; + top->eval(); + if (tfp) tfp->dump (t); + if (trace_fd && top->clk && top->trace_valid) fprintf(trace_fd, "%9.9lx\n", top->trace_data); + t += 5; + } + if (tfp) tfp->close(); + delete top; + exit(0); +} + diff --git a/testbench.v b/testbench.v new file mode 100644 index 0000000..9d8e249 --- /dev/null +++ b/testbench.v @@ -0,0 +1,479 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +`timescale 1 ns / 1 ps + +`ifndef VERILATOR +module testbench #( + parameter AXI_TEST = 0, + parameter VERBOSE = 0 +); + reg clk = 1; + reg resetn = 0; + wire trap; + + always #5 clk = ~clk; + + initial begin + repeat (100) @(posedge clk); + resetn <= 1; + end + + initial begin + if ($test$plusargs("vcd")) begin + $dumpfile("testbench.vcd"); + $dumpvars(0, testbench); + end + repeat (1000000) @(posedge clk); + $display("TIMEOUT"); + $finish; + end + + wire trace_valid; + wire [35:0] trace_data; + integer trace_file; + + initial begin + if ($test$plusargs("trace")) begin + trace_file = $fopen("testbench.trace", "w"); + repeat (10) @(posedge clk); + while (!trap) begin + @(posedge clk); + if (trace_valid) + $fwrite(trace_file, "%x\n", trace_data); + end + $fclose(trace_file); + $display("Finished writing testbench.trace."); + end + end + + picorv32_wrapper #( + .AXI_TEST (AXI_TEST), + .VERBOSE (VERBOSE) + ) top ( + .clk(clk), + .resetn(resetn), + .trap(trap), + .trace_valid(trace_valid), + .trace_data(trace_data) + ); +endmodule +`endif + +module picorv32_wrapper #( + parameter AXI_TEST = 0, + parameter VERBOSE = 0 +) ( + input clk, + input resetn, + output trap, + output trace_valid, + output [35:0] trace_data +); + wire tests_passed; + reg [31:0] irq = 0; + + reg [15:0] count_cycle = 0; + always @(posedge clk) count_cycle <= resetn ? count_cycle + 1 : 0; + + always @* begin + irq = 0; + irq[4] = &count_cycle[12:0]; + irq[5] = &count_cycle[15:0]; + end + + wire mem_axi_awvalid; + wire mem_axi_awready; + wire [31:0] mem_axi_awaddr; + wire [ 2:0] mem_axi_awprot; + + wire mem_axi_wvalid; + wire mem_axi_wready; + wire [31:0] mem_axi_wdata; + wire [ 3:0] mem_axi_wstrb; + + wire mem_axi_bvalid; + wire mem_axi_bready; + + wire mem_axi_arvalid; + wire mem_axi_arready; + wire [31:0] mem_axi_araddr; + wire [ 2:0] mem_axi_arprot; + + wire mem_axi_rvalid; + wire mem_axi_rready; + wire [31:0] mem_axi_rdata; + + axi4_memory #( + .AXI_TEST (AXI_TEST), + .VERBOSE (VERBOSE) + ) mem ( + .clk (clk ), + .mem_axi_awvalid (mem_axi_awvalid ), + .mem_axi_awready (mem_axi_awready ), + .mem_axi_awaddr (mem_axi_awaddr ), + .mem_axi_awprot (mem_axi_awprot ), + + .mem_axi_wvalid (mem_axi_wvalid ), + .mem_axi_wready (mem_axi_wready ), + .mem_axi_wdata (mem_axi_wdata ), + .mem_axi_wstrb (mem_axi_wstrb ), + + .mem_axi_bvalid (mem_axi_bvalid ), + .mem_axi_bready (mem_axi_bready ), + + .mem_axi_arvalid (mem_axi_arvalid ), + .mem_axi_arready (mem_axi_arready ), + .mem_axi_araddr (mem_axi_araddr ), + .mem_axi_arprot (mem_axi_arprot ), + + .mem_axi_rvalid (mem_axi_rvalid ), + .mem_axi_rready (mem_axi_rready ), + .mem_axi_rdata (mem_axi_rdata ), + + .tests_passed (tests_passed ) + ); + +`ifdef RISCV_FORMAL + wire rvfi_valid; + wire [63:0] rvfi_order; + wire [31:0] rvfi_insn; + wire rvfi_trap; + wire rvfi_halt; + wire rvfi_intr; + wire [4:0] rvfi_rs1_addr; + wire [4:0] rvfi_rs2_addr; + wire [31:0] rvfi_rs1_rdata; + wire [31:0] rvfi_rs2_rdata; + wire [4:0] rvfi_rd_addr; + wire [31:0] rvfi_rd_wdata; + wire [31:0] rvfi_pc_rdata; + wire [31:0] rvfi_pc_wdata; + wire [31:0] rvfi_mem_addr; + wire [3:0] rvfi_mem_rmask; + wire [3:0] rvfi_mem_wmask; + wire [31:0] rvfi_mem_rdata; + wire [31:0] rvfi_mem_wdata; +`endif + + picorv32_axi #( +`ifndef SYNTH_TEST +`ifdef SP_TEST + .ENABLE_REGS_DUALPORT(0), +`endif +`ifdef COMPRESSED_ISA + .COMPRESSED_ISA(1), +`endif + .ENABLE_MUL(1), + .ENABLE_DIV(1), + .ENABLE_IRQ(1), + .ENABLE_TRACE(1) +`endif + ) uut ( + .clk (clk ), + .resetn (resetn ), + .trap (trap ), + .mem_axi_awvalid(mem_axi_awvalid), + .mem_axi_awready(mem_axi_awready), + .mem_axi_awaddr (mem_axi_awaddr ), + .mem_axi_awprot (mem_axi_awprot ), + .mem_axi_wvalid (mem_axi_wvalid ), + .mem_axi_wready (mem_axi_wready ), + .mem_axi_wdata (mem_axi_wdata ), + .mem_axi_wstrb (mem_axi_wstrb ), + .mem_axi_bvalid (mem_axi_bvalid ), + .mem_axi_bready (mem_axi_bready ), + .mem_axi_arvalid(mem_axi_arvalid), + .mem_axi_arready(mem_axi_arready), + .mem_axi_araddr (mem_axi_araddr ), + .mem_axi_arprot (mem_axi_arprot ), + .mem_axi_rvalid (mem_axi_rvalid ), + .mem_axi_rready (mem_axi_rready ), + .mem_axi_rdata (mem_axi_rdata ), + .irq (irq ), +`ifdef RISCV_FORMAL + .rvfi_valid (rvfi_valid ), + .rvfi_order (rvfi_order ), + .rvfi_insn (rvfi_insn ), + .rvfi_trap (rvfi_trap ), + .rvfi_halt (rvfi_halt ), + .rvfi_intr (rvfi_intr ), + .rvfi_rs1_addr (rvfi_rs1_addr ), + .rvfi_rs2_addr (rvfi_rs2_addr ), + .rvfi_rs1_rdata (rvfi_rs1_rdata ), + .rvfi_rs2_rdata (rvfi_rs2_rdata ), + .rvfi_rd_addr (rvfi_rd_addr ), + .rvfi_rd_wdata (rvfi_rd_wdata ), + .rvfi_pc_rdata (rvfi_pc_rdata ), + .rvfi_pc_wdata (rvfi_pc_wdata ), + .rvfi_mem_addr (rvfi_mem_addr ), + .rvfi_mem_rmask (rvfi_mem_rmask ), + .rvfi_mem_wmask (rvfi_mem_wmask ), + .rvfi_mem_rdata (rvfi_mem_rdata ), + .rvfi_mem_wdata (rvfi_mem_wdata ), +`endif + .trace_valid (trace_valid ), + .trace_data (trace_data ) + ); + +`ifdef RISCV_FORMAL + picorv32_rvfimon rvfi_monitor ( + .clock (clk ), + .reset (!resetn ), + .rvfi_valid (rvfi_valid ), + .rvfi_order (rvfi_order ), + .rvfi_insn (rvfi_insn ), + .rvfi_trap (rvfi_trap ), + .rvfi_halt (rvfi_halt ), + .rvfi_intr (rvfi_intr ), + .rvfi_rs1_addr (rvfi_rs1_addr ), + .rvfi_rs2_addr (rvfi_rs2_addr ), + .rvfi_rs1_rdata (rvfi_rs1_rdata), + .rvfi_rs2_rdata (rvfi_rs2_rdata), + .rvfi_rd_addr (rvfi_rd_addr ), + .rvfi_rd_wdata (rvfi_rd_wdata ), + .rvfi_pc_rdata (rvfi_pc_rdata ), + .rvfi_pc_wdata (rvfi_pc_wdata ), + .rvfi_mem_addr (rvfi_mem_addr ), + .rvfi_mem_rmask (rvfi_mem_rmask), + .rvfi_mem_wmask (rvfi_mem_wmask), + .rvfi_mem_rdata (rvfi_mem_rdata), + .rvfi_mem_wdata (rvfi_mem_wdata) + ); +`endif + + reg [1023:0] firmware_file; + initial begin + if (!$value$plusargs("firmware=%s", firmware_file)) + firmware_file = "firmware/firmware.hex"; + $readmemh(firmware_file, mem.memory); + end + + integer cycle_counter; + always @(posedge clk) begin + cycle_counter <= resetn ? cycle_counter + 1 : 0; + if (resetn && trap) begin +`ifndef VERILATOR + repeat (10) @(posedge clk); +`endif + $display("TRAP after %1d clock cycles", cycle_counter); + if (tests_passed) begin + $display("ALL TESTS PASSED."); + $finish; + end else begin + $display("ERROR!"); + if ($test$plusargs("noerror")) + $finish; + $stop; + end + end + end +endmodule + +module axi4_memory #( + parameter AXI_TEST = 0, + parameter VERBOSE = 0 +) ( + /* verilator lint_off MULTIDRIVEN */ + + input clk, + input mem_axi_awvalid, + output reg mem_axi_awready, + input [31:0] mem_axi_awaddr, + input [ 2:0] mem_axi_awprot, + + input mem_axi_wvalid, + output reg mem_axi_wready, + input [31:0] mem_axi_wdata, + input [ 3:0] mem_axi_wstrb, + + output reg mem_axi_bvalid, + input mem_axi_bready, + + input mem_axi_arvalid, + output reg mem_axi_arready, + input [31:0] mem_axi_araddr, + input [ 2:0] mem_axi_arprot, + + output reg mem_axi_rvalid, + input mem_axi_rready, + output reg [31:0] mem_axi_rdata, + + output reg tests_passed +); + reg [31:0] memory [0:128*1024/4-1] /* verilator public */; + reg verbose; + initial verbose = $test$plusargs("verbose") || VERBOSE; + + reg axi_test; + initial axi_test = $test$plusargs("axi_test") || AXI_TEST; + + initial begin + mem_axi_awready = 0; + mem_axi_wready = 0; + mem_axi_bvalid = 0; + mem_axi_arready = 0; + mem_axi_rvalid = 0; + tests_passed = 0; + end + + reg [63:0] xorshift64_state = 64'd88172645463325252; + + task xorshift64_next; + begin + // see page 4 of Marsaglia, George (July 2003). "Xorshift RNGs". Journal of Statistical Software 8 (14). + xorshift64_state = xorshift64_state ^ (xorshift64_state << 13); + xorshift64_state = xorshift64_state ^ (xorshift64_state >> 7); + xorshift64_state = xorshift64_state ^ (xorshift64_state << 17); + end + endtask + + reg [2:0] fast_axi_transaction = ~0; + reg [4:0] async_axi_transaction = ~0; + reg [4:0] delay_axi_transaction = 0; + + always @(posedge clk) begin + if (axi_test) begin + xorshift64_next; + {fast_axi_transaction, async_axi_transaction, delay_axi_transaction} <= xorshift64_state; + end + end + + reg latched_raddr_en = 0; + reg latched_waddr_en = 0; + reg latched_wdata_en = 0; + + reg fast_raddr = 0; + reg fast_waddr = 0; + reg fast_wdata = 0; + + reg [31:0] latched_raddr; + reg [31:0] latched_waddr; + reg [31:0] latched_wdata; + reg [ 3:0] latched_wstrb; + reg latched_rinsn; + + task handle_axi_arvalid; begin + mem_axi_arready <= 1; + latched_raddr = mem_axi_araddr; + latched_rinsn = mem_axi_arprot[2]; + latched_raddr_en = 1; + fast_raddr <= 1; + end endtask + + task handle_axi_awvalid; begin + mem_axi_awready <= 1; + latched_waddr = mem_axi_awaddr; + latched_waddr_en = 1; + fast_waddr <= 1; + end endtask + + task handle_axi_wvalid; begin + mem_axi_wready <= 1; + latched_wdata = mem_axi_wdata; + latched_wstrb = mem_axi_wstrb; + latched_wdata_en = 1; + fast_wdata <= 1; + end endtask + + task handle_axi_rvalid; begin + if (verbose) + $display("RD: ADDR=%08x DATA=%08x%s", latched_raddr, memory[latched_raddr >> 2], latched_rinsn ? " INSN" : ""); + if (latched_raddr < 128*1024) begin + mem_axi_rdata <= memory[latched_raddr >> 2]; + mem_axi_rvalid <= 1; + latched_raddr_en = 0; + end else begin + $display("OUT-OF-BOUNDS MEMORY READ FROM %08x", latched_raddr); + $finish; + end + end endtask + + task handle_axi_bvalid; begin + if (verbose) + $display("WR: ADDR=%08x DATA=%08x STRB=%04b", latched_waddr, latched_wdata, latched_wstrb); + if (latched_waddr < 128*1024) begin + if (latched_wstrb[0]) memory[latched_waddr >> 2][ 7: 0] <= latched_wdata[ 7: 0]; + if (latched_wstrb[1]) memory[latched_waddr >> 2][15: 8] <= latched_wdata[15: 8]; + if (latched_wstrb[2]) memory[latched_waddr >> 2][23:16] <= latched_wdata[23:16]; + if (latched_wstrb[3]) memory[latched_waddr >> 2][31:24] <= latched_wdata[31:24]; + end else + if (latched_waddr == 32'h1000_0000) begin + if (verbose) begin + if (32 <= latched_wdata && latched_wdata < 128) + $display("OUT: '%c'", latched_wdata[7:0]); + else + $display("OUT: %3d", latched_wdata); + end else begin + $write("%c", latched_wdata[7:0]); +`ifndef VERILATOR + $fflush(); +`endif + end + end else + if (latched_waddr == 32'h2000_0000) begin + if (latched_wdata == 123456789) + tests_passed = 1; + end else begin + $display("OUT-OF-BOUNDS MEMORY WRITE TO %08x", latched_waddr); + $finish; + end + mem_axi_bvalid <= 1; + latched_waddr_en = 0; + latched_wdata_en = 0; + end endtask + + always @(negedge clk) begin + if (mem_axi_arvalid && !(latched_raddr_en || fast_raddr) && async_axi_transaction[0]) handle_axi_arvalid; + if (mem_axi_awvalid && !(latched_waddr_en || fast_waddr) && async_axi_transaction[1]) handle_axi_awvalid; + if (mem_axi_wvalid && !(latched_wdata_en || fast_wdata) && async_axi_transaction[2]) handle_axi_wvalid; + if (!mem_axi_rvalid && latched_raddr_en && async_axi_transaction[3]) handle_axi_rvalid; + if (!mem_axi_bvalid && latched_waddr_en && latched_wdata_en && async_axi_transaction[4]) handle_axi_bvalid; + end + + always @(posedge clk) begin + mem_axi_arready <= 0; + mem_axi_awready <= 0; + mem_axi_wready <= 0; + + fast_raddr <= 0; + fast_waddr <= 0; + fast_wdata <= 0; + + if (mem_axi_rvalid && mem_axi_rready) begin + mem_axi_rvalid <= 0; + end + + if (mem_axi_bvalid && mem_axi_bready) begin + mem_axi_bvalid <= 0; + end + + if (mem_axi_arvalid && mem_axi_arready && !fast_raddr) begin + latched_raddr = mem_axi_araddr; + latched_rinsn = mem_axi_arprot[2]; + latched_raddr_en = 1; + end + + if (mem_axi_awvalid && mem_axi_awready && !fast_waddr) begin + latched_waddr = mem_axi_awaddr; + latched_waddr_en = 1; + end + + if (mem_axi_wvalid && mem_axi_wready && !fast_wdata) begin + latched_wdata = mem_axi_wdata; + latched_wstrb = mem_axi_wstrb; + latched_wdata_en = 1; + end + + if (mem_axi_arvalid && !(latched_raddr_en || fast_raddr) && !delay_axi_transaction[0]) handle_axi_arvalid; + if (mem_axi_awvalid && !(latched_waddr_en || fast_waddr) && !delay_axi_transaction[1]) handle_axi_awvalid; + if (mem_axi_wvalid && !(latched_wdata_en || fast_wdata) && !delay_axi_transaction[2]) handle_axi_wvalid; + + if (!mem_axi_rvalid && latched_raddr_en && !delay_axi_transaction[3]) handle_axi_rvalid; + if (!mem_axi_bvalid && latched_waddr_en && latched_wdata_en && !delay_axi_transaction[4]) handle_axi_bvalid; + end +endmodule diff --git a/testbench_ez.v b/testbench_ez.v new file mode 100644 index 0000000..55c9c69 --- /dev/null +++ b/testbench_ez.v @@ -0,0 +1,86 @@ +// This is free and unencumbered software released into the public domain. +// +// Anyone is free to copy, modify, publish, use, compile, sell, or +// distribute this software, either in source code form or as a compiled +// binary, for any purpose, commercial or non-commercial, and by any +// means. + +`timescale 1 ns / 1 ps + +module testbench; + reg clk = 1; + reg resetn = 0; + wire trap; + + always #5 clk = ~clk; + + initial begin + if ($test$plusargs("vcd")) begin + $dumpfile("testbench.vcd"); + $dumpvars(0, testbench); + end + repeat (100) @(posedge clk); + resetn <= 1; + repeat (1000) @(posedge clk); + $finish; + end + + 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; + + always @(posedge clk) begin + if (mem_valid && mem_ready) begin + if (mem_instr) + $display("ifetch 0x%08x: 0x%08x", mem_addr, mem_rdata); + else if (mem_wstrb) + $display("write 0x%08x: 0x%08x (wstrb=%b)", mem_addr, mem_wdata, mem_wstrb); + else + $display("read 0x%08x: 0x%08x", mem_addr, mem_rdata); + end + end + + picorv32 #( + ) 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 ) + ); + + reg [31:0] memory [0:255]; + + initial begin + memory[0] = 32'h 3fc00093; // li x1,1020 + memory[1] = 32'h 0000a023; // sw x0,0(x1) + memory[2] = 32'h 0000a103; // loop: lw x2,0(x1) + memory[3] = 32'h 00110113; // addi x2,x2,1 + memory[4] = 32'h 0020a023; // sw x2,0(x1) + memory[5] = 32'h ff5ff06f; // j + end + + always @(posedge clk) begin + mem_ready <= 0; + if (mem_valid && !mem_ready) begin + if (mem_addr < 1024) begin + mem_ready <= 1; + mem_rdata <= memory[mem_addr >> 2]; + 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 + /* add memory-mapped IO here */ + end + end +endmodule diff --git a/testbench_wb.v b/testbench_wb.v new file mode 100644 index 0000000..4e1a8eb --- /dev/null +++ b/testbench_wb.v @@ -0,0 +1,293 @@ +`timescale 1 ns / 1 ps + +`ifndef VERILATOR +module testbench #( + parameter VERBOSE = 0 +); + reg clk = 1; + reg resetn = 1; + wire trap; + + always #5 clk = ~clk; + + initial begin + repeat (100) @(posedge clk); + resetn <= 0; + end + + initial begin + if ($test$plusargs("vcd")) begin + $dumpfile("testbench.vcd"); + $dumpvars(0, testbench); + end + repeat (1000000) @(posedge clk); + $display("TIMEOUT"); + $finish; + end + + wire trace_valid; + wire [35:0] trace_data; + integer trace_file; + + initial begin + if ($test$plusargs("trace")) begin + trace_file = $fopen("testbench.trace", "w"); + repeat (10) @(posedge clk); + while (!trap) begin + @(posedge clk); + if (trace_valid) + $fwrite(trace_file, "%x\n", trace_data); + end + $fclose(trace_file); + $display("Finished writing testbench.trace."); + end + end + + picorv32_wrapper #( + .VERBOSE (VERBOSE) + ) top ( + .wb_clk(clk), + .wb_rst(resetn), + .trap(trap), + .trace_valid(trace_valid), + .trace_data(trace_data) + ); +endmodule +`endif + +module picorv32_wrapper #( + parameter VERBOSE = 0 +) ( + input wb_clk, + input wb_rst, + output trap, + output trace_valid, + output [35:0] trace_data +); + wire tests_passed; + reg [31:0] irq = 0; + wire mem_instr; + + reg [15:0] count_cycle = 0; + always @(posedge wb_clk) count_cycle <= !wb_rst ? count_cycle + 1 : 0; + + always @* begin + irq = 0; + irq[4] = &count_cycle[12:0]; + irq[5] = &count_cycle[15:0]; + end + + wire [31:0] wb_m2s_adr; + wire [31:0] wb_m2s_dat; + wire [3:0] wb_m2s_sel; + wire wb_m2s_we; + wire wb_m2s_cyc; + wire wb_m2s_stb; + wire [31:0] wb_s2m_dat; + wire wb_s2m_ack; + + wb_ram #( + .depth (128*1024), + .VERBOSE (VERBOSE) + ) ram ( // Wishbone interface + .wb_clk_i(wb_clk), + .wb_rst_i(wb_rst), + + .wb_adr_i(wb_m2s_adr), + .wb_dat_i(wb_m2s_dat), + .wb_stb_i(wb_m2s_stb), + .wb_cyc_i(wb_m2s_cyc), + .wb_dat_o(wb_s2m_dat), + .wb_ack_o(wb_s2m_ack), + .wb_sel_i(wb_m2s_sel), + .wb_we_i(wb_m2s_we), + + .mem_instr(mem_instr), + .tests_passed(tests_passed) + ); + + picorv32_wb #( +`ifndef SYNTH_TEST +`ifdef SP_TEST + .ENABLE_REGS_DUALPORT(0), +`endif +`ifdef COMPRESSED_ISA + .COMPRESSED_ISA(1), +`endif + .ENABLE_MUL(1), + .ENABLE_DIV(1), + .ENABLE_IRQ(1), + .ENABLE_TRACE(1) +`endif + ) uut ( + .trap (trap), + .irq (irq), + .trace_valid (trace_valid), + .trace_data (trace_data), + .mem_instr(mem_instr), + + .wb_clk_i(wb_clk), + .wb_rst_i(wb_rst), + + .wbm_adr_o(wb_m2s_adr), + .wbm_dat_i(wb_s2m_dat), + .wbm_stb_o(wb_m2s_stb), + .wbm_ack_i(wb_s2m_ack), + .wbm_cyc_o(wb_m2s_cyc), + .wbm_dat_o(wb_m2s_dat), + .wbm_we_o(wb_m2s_we), + .wbm_sel_o(wb_m2s_sel) + ); + + reg [1023:0] firmware_file; + initial begin + if (!$value$plusargs("firmware=%s", firmware_file)) + firmware_file = "firmware/firmware.hex"; + $readmemh(firmware_file, ram.mem); + end + + integer cycle_counter; + always @(posedge wb_clk) begin + cycle_counter <= !wb_rst ? cycle_counter + 1 : 0; + if (!wb_rst && trap) begin +`ifndef VERILATOR + repeat (10) @(posedge wb_clk); +`endif + $display("TRAP after %1d clock cycles", cycle_counter); + if (tests_passed) begin + $display("ALL TESTS PASSED."); + $finish; + end else begin + $display("ERROR!"); + if ($test$plusargs("noerror")) + $finish; + $stop; + end + end + end +endmodule + +/* ISC License + * + * Verilog on-chip RAM with Wishbone interface + * + * Copyright (C) 2014, 2016 Olof Kindgren + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +module wb_ram #( + parameter depth = 256, + parameter memfile = "", + parameter VERBOSE = 0 +) ( + input wb_clk_i, + input wb_rst_i, + + input [31:0] wb_adr_i, + input [31:0] wb_dat_i, + input [3:0] wb_sel_i, + input wb_we_i, + input wb_cyc_i, + input wb_stb_i, + + output reg wb_ack_o, + output reg [31:0] wb_dat_o, + + input mem_instr, + output reg tests_passed +); + + reg verbose; + initial verbose = $test$plusargs("verbose") || VERBOSE; + + initial tests_passed = 0; + + reg [31:0] adr_r; + wire valid = wb_cyc_i & wb_stb_i; + + always @(posedge wb_clk_i) begin + adr_r <= wb_adr_i; + // Ack generation + wb_ack_o <= valid & !wb_ack_o; + if (wb_rst_i) + begin + adr_r <= {32{1'b0}}; + wb_ack_o <= 1'b0; + end + end + + wire ram_we = wb_we_i & valid & wb_ack_o; + + wire [31:0] waddr = adr_r[31:2]; + wire [31:0] raddr = wb_adr_i[31:2]; + wire [3:0] we = {4{ram_we}} & wb_sel_i; + + wire [$clog2(depth/4)-1:0] raddr2 = raddr[$clog2(depth/4)-1:0]; + wire [$clog2(depth/4)-1:0] waddr2 = waddr[$clog2(depth/4)-1:0]; + + reg [31:0] mem [0:depth/4-1] /* verilator public */; + + always @(posedge wb_clk_i) begin + if (ram_we) begin + if (verbose) + $display("WR: ADDR=%08x DATA=%08x STRB=%04b", + adr_r, wb_dat_i, we); + + if (adr_r[31:0] == 32'h1000_0000) + if (verbose) begin + if (32 <= wb_dat_i[7:0] && wb_dat_i[7:0] < 128) + $display("OUT: '%c'", wb_dat_i[7:0]); + else + $display("OUT: %3d", wb_dat_i[7:0]); + end else begin + $write("%c", wb_dat_i[7:0]); +`ifndef VERILATOR + $fflush(); +`endif + end + else + if (adr_r[31:0] == 32'h2000_0000) + if (wb_dat_i[31:0] == 123456789) + tests_passed = 1; + end + end + + always @(posedge wb_clk_i) begin + if (waddr2 < 128 * 1024 / 4) begin + if (we[0]) + mem[waddr2][7:0] <= wb_dat_i[7:0]; + + if (we[1]) + mem[waddr2][15:8] <= wb_dat_i[15:8]; + + if (we[2]) + mem[waddr2][23:16] <= wb_dat_i[23:16]; + + if (we[3]) + mem[waddr2][31:24] <= wb_dat_i[31:24]; + + end + + if (valid & wb_ack_o & !ram_we) + if (verbose) + $display("RD: ADDR=%08x DATA=%08x%s", adr_r, mem[raddr2], mem_instr ? " INSN" : ""); + + wb_dat_o <= mem[raddr2]; + end + + initial begin + if (memfile != "") + $readmemh(memfile, mem); + end +endmodule diff --git a/tests/LICENSE b/tests/LICENSE new file mode 100644 index 0000000..48fe522 --- /dev/null +++ b/tests/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2012-2015, The Regents of the University of California (Regents). +All Rights Reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of the Regents nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING +OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS +BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED +HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE +MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/tests/README b/tests/README new file mode 100644 index 0000000..3c1a912 --- /dev/null +++ b/tests/README @@ -0,0 +1 @@ +Tests from https://github.com/riscv/riscv-tests/tree/master/isa/rv32ui diff --git a/tests/add.S b/tests/add.S new file mode 100644 index 0000000..2eb330e --- /dev/null +++ b/tests/add.S @@ -0,0 +1,85 @@ +# See LICENSE for license details. + +#***************************************************************************** +# add.S +#----------------------------------------------------------------------------- +# +# Test add instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, add, 0x00000000, 0x00000000, 0x00000000 ); + TEST_RR_OP( 3, add, 0x00000002, 0x00000001, 0x00000001 ); + TEST_RR_OP( 4, add, 0x0000000a, 0x00000003, 0x00000007 ); + + TEST_RR_OP( 5, add, 0xffff8000, 0x00000000, 0xffff8000 ); + TEST_RR_OP( 6, add, 0x80000000, 0x80000000, 0x00000000 ); + TEST_RR_OP( 7, add, 0x7fff8000, 0x80000000, 0xffff8000 ); + + TEST_RR_OP( 8, add, 0x00007fff, 0x00000000, 0x00007fff ); + TEST_RR_OP( 9, add, 0x7fffffff, 0x7fffffff, 0x00000000 ); + TEST_RR_OP( 10, add, 0x80007ffe, 0x7fffffff, 0x00007fff ); + + TEST_RR_OP( 11, add, 0x80007fff, 0x80000000, 0x00007fff ); + TEST_RR_OP( 12, add, 0x7fff7fff, 0x7fffffff, 0xffff8000 ); + + TEST_RR_OP( 13, add, 0xffffffff, 0x00000000, 0xffffffff ); + TEST_RR_OP( 14, add, 0x00000000, 0xffffffff, 0x00000001 ); + TEST_RR_OP( 15, add, 0xfffffffe, 0xffffffff, 0xffffffff ); + + TEST_RR_OP( 16, add, 0x80000000, 0x00000001, 0x7fffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 17, add, 24, 13, 11 ); + TEST_RR_SRC2_EQ_DEST( 18, add, 25, 14, 11 ); + TEST_RR_SRC12_EQ_DEST( 19, add, 26, 13 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 20, 0, add, 24, 13, 11 ); + TEST_RR_DEST_BYPASS( 21, 1, add, 25, 14, 11 ); + TEST_RR_DEST_BYPASS( 22, 2, add, 26, 15, 11 ); + + TEST_RR_SRC12_BYPASS( 23, 0, 0, add, 24, 13, 11 ); + TEST_RR_SRC12_BYPASS( 24, 0, 1, add, 25, 14, 11 ); + TEST_RR_SRC12_BYPASS( 25, 0, 2, add, 26, 15, 11 ); + TEST_RR_SRC12_BYPASS( 26, 1, 0, add, 24, 13, 11 ); + TEST_RR_SRC12_BYPASS( 27, 1, 1, add, 25, 14, 11 ); + TEST_RR_SRC12_BYPASS( 28, 2, 0, add, 26, 15, 11 ); + + TEST_RR_SRC21_BYPASS( 29, 0, 0, add, 24, 13, 11 ); + TEST_RR_SRC21_BYPASS( 30, 0, 1, add, 25, 14, 11 ); + TEST_RR_SRC21_BYPASS( 31, 0, 2, add, 26, 15, 11 ); + TEST_RR_SRC21_BYPASS( 32, 1, 0, add, 24, 13, 11 ); + TEST_RR_SRC21_BYPASS( 33, 1, 1, add, 25, 14, 11 ); + TEST_RR_SRC21_BYPASS( 34, 2, 0, add, 26, 15, 11 ); + + TEST_RR_ZEROSRC1( 35, add, 15, 15 ); + TEST_RR_ZEROSRC2( 36, add, 32, 32 ); + TEST_RR_ZEROSRC12( 37, add, 0 ); + TEST_RR_ZERODEST( 38, add, 16, 30 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/addi.S b/tests/addi.S new file mode 100644 index 0000000..f7a418b --- /dev/null +++ b/tests/addi.S @@ -0,0 +1,71 @@ +# See LICENSE for license details. + +#***************************************************************************** +# addi.S +#----------------------------------------------------------------------------- +# +# Test addi instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_IMM_OP( 2, addi, 0x00000000, 0x00000000, 0x000 ); + TEST_IMM_OP( 3, addi, 0x00000002, 0x00000001, 0x001 ); + TEST_IMM_OP( 4, addi, 0x0000000a, 0x00000003, 0x007 ); + + TEST_IMM_OP( 5, addi, 0xfffff800, 0x00000000, 0x800 ); + TEST_IMM_OP( 6, addi, 0x80000000, 0x80000000, 0x000 ); + TEST_IMM_OP( 7, addi, 0x7ffff800, 0x80000000, 0x800 ); + + TEST_IMM_OP( 8, addi, 0x000007ff, 0x00000000, 0x7ff ); + TEST_IMM_OP( 9, addi, 0x7fffffff, 0x7fffffff, 0x000 ); + TEST_IMM_OP( 10, addi, 0x800007fe, 0x7fffffff, 0x7ff ); + + TEST_IMM_OP( 11, addi, 0x800007ff, 0x80000000, 0x7ff ); + TEST_IMM_OP( 12, addi, 0x7ffff7ff, 0x7fffffff, 0x800 ); + + TEST_IMM_OP( 13, addi, 0xffffffff, 0x00000000, 0xfff ); + TEST_IMM_OP( 14, addi, 0x00000000, 0xffffffff, 0x001 ); + TEST_IMM_OP( 15, addi, 0xfffffffe, 0xffffffff, 0xfff ); + + TEST_IMM_OP( 16, addi, 0x80000000, 0x7fffffff, 0x001 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_IMM_SRC1_EQ_DEST( 17, addi, 24, 13, 11 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_IMM_DEST_BYPASS( 18, 0, addi, 24, 13, 11 ); + TEST_IMM_DEST_BYPASS( 19, 1, addi, 23, 13, 10 ); + TEST_IMM_DEST_BYPASS( 20, 2, addi, 22, 13, 9 ); + + TEST_IMM_SRC1_BYPASS( 21, 0, addi, 24, 13, 11 ); + TEST_IMM_SRC1_BYPASS( 22, 1, addi, 23, 13, 10 ); + TEST_IMM_SRC1_BYPASS( 23, 2, addi, 22, 13, 9 ); + + TEST_IMM_ZEROSRC1( 24, addi, 32, 32 ); + TEST_IMM_ZERODEST( 25, addi, 33, 50 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/and.S b/tests/and.S new file mode 100644 index 0000000..561ae3b --- /dev/null +++ b/tests/and.S @@ -0,0 +1,69 @@ +# See LICENSE for license details. + +#***************************************************************************** +# and.S +#----------------------------------------------------------------------------- +# +# Test and instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_OP( 3, and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_OP( 4, and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RR_OP( 5, and, 0xf000f000, 0xf00ff00f, 0xf0f0f0f0 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 6, and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC2_EQ_DEST( 7, and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC12_EQ_DEST( 8, and, 0xff00ff00, 0xff00ff00 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 9, 0, and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_DEST_BYPASS( 10, 1, and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_DEST_BYPASS( 11, 2, and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RR_SRC12_BYPASS( 12, 0, 0, and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC12_BYPASS( 13, 0, 1, and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC12_BYPASS( 14, 0, 2, and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RR_SRC12_BYPASS( 15, 1, 0, and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC12_BYPASS( 16, 1, 1, and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC12_BYPASS( 17, 2, 0, and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RR_SRC21_BYPASS( 18, 0, 0, and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC21_BYPASS( 19, 0, 1, and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC21_BYPASS( 20, 0, 2, and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RR_SRC21_BYPASS( 21, 1, 0, and, 0x0f000f00, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC21_BYPASS( 22, 1, 1, and, 0x00f000f0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC21_BYPASS( 23, 2, 0, and, 0x000f000f, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RR_ZEROSRC1( 24, and, 0, 0xff00ff00 ); + TEST_RR_ZEROSRC2( 25, and, 0, 0x00ff00ff ); + TEST_RR_ZEROSRC12( 26, and, 0 ); + TEST_RR_ZERODEST( 27, and, 0x11111111, 0x22222222 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/andi.S b/tests/andi.S new file mode 100644 index 0000000..c2ae94d --- /dev/null +++ b/tests/andi.S @@ -0,0 +1,55 @@ +# See LICENSE for license details. + +#***************************************************************************** +# andi.S +#----------------------------------------------------------------------------- +# +# Test andi instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_IMM_OP( 2, andi, 0xff00ff00, 0xff00ff00, 0xf0f ); + TEST_IMM_OP( 3, andi, 0x000000f0, 0x0ff00ff0, 0x0f0 ); + TEST_IMM_OP( 4, andi, 0x0000000f, 0x00ff00ff, 0x70f ); + TEST_IMM_OP( 5, andi, 0x00000000, 0xf00ff00f, 0x0f0 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_IMM_SRC1_EQ_DEST( 6, andi, 0x00000000, 0xff00ff00, 0x0f0 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_IMM_DEST_BYPASS( 7, 0, andi, 0x00000700, 0x0ff00ff0, 0x70f ); + TEST_IMM_DEST_BYPASS( 8, 1, andi, 0x000000f0, 0x00ff00ff, 0x0f0 ); + TEST_IMM_DEST_BYPASS( 9, 2, andi, 0xf00ff00f, 0xf00ff00f, 0xf0f ); + + TEST_IMM_SRC1_BYPASS( 10, 0, andi, 0x00000700, 0x0ff00ff0, 0x70f ); + TEST_IMM_SRC1_BYPASS( 11, 1, andi, 0x000000f0, 0x00ff00ff, 0x0f0 ); + TEST_IMM_SRC1_BYPASS( 12, 2, andi, 0x0000000f, 0xf00ff00f, 0x70f ); + + TEST_IMM_ZEROSRC1( 13, andi, 0, 0x0f0 ); + TEST_IMM_ZERODEST( 14, andi, 0x00ff00ff, 0x70f ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/auipc.S b/tests/auipc.S new file mode 100644 index 0000000..c67e3c9 --- /dev/null +++ b/tests/auipc.S @@ -0,0 +1,39 @@ +# See LICENSE for license details. + +#***************************************************************************** +# auipc.S +#----------------------------------------------------------------------------- +# +# Test auipc instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + TEST_CASE(2, a0, 10000, \ + .align 3; \ + lla a0, 1f + 10000; \ + jal a1, 1f; \ + 1: sub a0, a0, a1; \ + ) + + TEST_CASE(3, a0, -10000, \ + .align 3; \ + lla a0, 1f - 10000; \ + jal a1, 1f; \ + 1: sub a0, a0, a1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/beq.S b/tests/beq.S new file mode 100644 index 0000000..c972eff --- /dev/null +++ b/tests/beq.S @@ -0,0 +1,73 @@ +# See LICENSE for license details. + +#***************************************************************************** +# beq.S +#----------------------------------------------------------------------------- +# +# Test beq instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Branch tests + #------------------------------------------------------------- + + # Each test checks both forward and backward branches + + TEST_BR2_OP_TAKEN( 2, beq, 0, 0 ); + TEST_BR2_OP_TAKEN( 3, beq, 1, 1 ); + TEST_BR2_OP_TAKEN( 4, beq, -1, -1 ); + + TEST_BR2_OP_NOTTAKEN( 5, beq, 0, 1 ); + TEST_BR2_OP_NOTTAKEN( 6, beq, 1, 0 ); + TEST_BR2_OP_NOTTAKEN( 7, beq, -1, 1 ); + TEST_BR2_OP_NOTTAKEN( 8, beq, 1, -1 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_BR2_SRC12_BYPASS( 9, 0, 0, beq, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 10, 0, 1, beq, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 11, 0, 2, beq, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 12, 1, 0, beq, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 13, 1, 1, beq, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 14, 2, 0, beq, 0, -1 ); + + TEST_BR2_SRC12_BYPASS( 15, 0, 0, beq, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 16, 0, 1, beq, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 17, 0, 2, beq, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 18, 1, 0, beq, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 19, 1, 1, beq, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 20, 2, 0, beq, 0, -1 ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 21, x1, 3, \ + li x1, 1; \ + beq x0, x0, 1f; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ +1: addi x1, x1, 1; \ + addi x1, x1, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/bge.S b/tests/bge.S new file mode 100644 index 0000000..d6aea7c --- /dev/null +++ b/tests/bge.S @@ -0,0 +1,76 @@ +# See LICENSE for license details. + +#***************************************************************************** +# bge.S +#----------------------------------------------------------------------------- +# +# Test bge instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Branch tests + #------------------------------------------------------------- + + # Each test checks both forward and backward branches + + TEST_BR2_OP_TAKEN( 2, bge, 0, 0 ); + TEST_BR2_OP_TAKEN( 3, bge, 1, 1 ); + TEST_BR2_OP_TAKEN( 4, bge, -1, -1 ); + TEST_BR2_OP_TAKEN( 5, bge, 1, 0 ); + TEST_BR2_OP_TAKEN( 6, bge, 1, -1 ); + TEST_BR2_OP_TAKEN( 7, bge, -1, -2 ); + + TEST_BR2_OP_NOTTAKEN( 8, bge, 0, 1 ); + TEST_BR2_OP_NOTTAKEN( 9, bge, -1, 1 ); + TEST_BR2_OP_NOTTAKEN( 10, bge, -2, -1 ); + TEST_BR2_OP_NOTTAKEN( 11, bge, -2, 1 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_BR2_SRC12_BYPASS( 12, 0, 0, bge, -1, 0 ); + TEST_BR2_SRC12_BYPASS( 13, 0, 1, bge, -1, 0 ); + TEST_BR2_SRC12_BYPASS( 14, 0, 2, bge, -1, 0 ); + TEST_BR2_SRC12_BYPASS( 15, 1, 0, bge, -1, 0 ); + TEST_BR2_SRC12_BYPASS( 16, 1, 1, bge, -1, 0 ); + TEST_BR2_SRC12_BYPASS( 17, 2, 0, bge, -1, 0 ); + + TEST_BR2_SRC12_BYPASS( 18, 0, 0, bge, -1, 0 ); + TEST_BR2_SRC12_BYPASS( 19, 0, 1, bge, -1, 0 ); + TEST_BR2_SRC12_BYPASS( 20, 0, 2, bge, -1, 0 ); + TEST_BR2_SRC12_BYPASS( 21, 1, 0, bge, -1, 0 ); + TEST_BR2_SRC12_BYPASS( 22, 1, 1, bge, -1, 0 ); + TEST_BR2_SRC12_BYPASS( 23, 2, 0, bge, -1, 0 ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 24, x1, 3, \ + li x1, 1; \ + bge x1, x0, 1f; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ +1: addi x1, x1, 1; \ + addi x1, x1, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/bgeu.S b/tests/bgeu.S new file mode 100644 index 0000000..114c845 --- /dev/null +++ b/tests/bgeu.S @@ -0,0 +1,76 @@ +# See LICENSE for license details. + +#***************************************************************************** +# bgeu.S +#----------------------------------------------------------------------------- +# +# Test bgeu instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Branch tests + #------------------------------------------------------------- + + # Each test checks both forward and backward branches + + TEST_BR2_OP_TAKEN( 2, bgeu, 0x00000000, 0x00000000 ); + TEST_BR2_OP_TAKEN( 3, bgeu, 0x00000001, 0x00000001 ); + TEST_BR2_OP_TAKEN( 4, bgeu, 0xffffffff, 0xffffffff ); + TEST_BR2_OP_TAKEN( 5, bgeu, 0x00000001, 0x00000000 ); + TEST_BR2_OP_TAKEN( 6, bgeu, 0xffffffff, 0xfffffffe ); + TEST_BR2_OP_TAKEN( 7, bgeu, 0xffffffff, 0x00000000 ); + + TEST_BR2_OP_NOTTAKEN( 8, bgeu, 0x00000000, 0x00000001 ); + TEST_BR2_OP_NOTTAKEN( 9, bgeu, 0xfffffffe, 0xffffffff ); + TEST_BR2_OP_NOTTAKEN( 10, bgeu, 0x00000000, 0xffffffff ); + TEST_BR2_OP_NOTTAKEN( 11, bgeu, 0x7fffffff, 0x80000000 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_BR2_SRC12_BYPASS( 12, 0, 0, bgeu, 0xefffffff, 0xf0000000 ); + TEST_BR2_SRC12_BYPASS( 13, 0, 1, bgeu, 0xefffffff, 0xf0000000 ); + TEST_BR2_SRC12_BYPASS( 14, 0, 2, bgeu, 0xefffffff, 0xf0000000 ); + TEST_BR2_SRC12_BYPASS( 15, 1, 0, bgeu, 0xefffffff, 0xf0000000 ); + TEST_BR2_SRC12_BYPASS( 16, 1, 1, bgeu, 0xefffffff, 0xf0000000 ); + TEST_BR2_SRC12_BYPASS( 17, 2, 0, bgeu, 0xefffffff, 0xf0000000 ); + + TEST_BR2_SRC12_BYPASS( 18, 0, 0, bgeu, 0xefffffff, 0xf0000000 ); + TEST_BR2_SRC12_BYPASS( 19, 0, 1, bgeu, 0xefffffff, 0xf0000000 ); + TEST_BR2_SRC12_BYPASS( 20, 0, 2, bgeu, 0xefffffff, 0xf0000000 ); + TEST_BR2_SRC12_BYPASS( 21, 1, 0, bgeu, 0xefffffff, 0xf0000000 ); + TEST_BR2_SRC12_BYPASS( 22, 1, 1, bgeu, 0xefffffff, 0xf0000000 ); + TEST_BR2_SRC12_BYPASS( 23, 2, 0, bgeu, 0xefffffff, 0xf0000000 ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 24, x1, 3, \ + li x1, 1; \ + bgeu x1, x0, 1f; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ +1: addi x1, x1, 1; \ + addi x1, x1, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/blt.S b/tests/blt.S new file mode 100644 index 0000000..12e28c3 --- /dev/null +++ b/tests/blt.S @@ -0,0 +1,73 @@ +# See LICENSE for license details. + +#***************************************************************************** +# blt.S +#----------------------------------------------------------------------------- +# +# Test blt instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Branch tests + #------------------------------------------------------------- + + # Each test checks both forward and backward branches + + TEST_BR2_OP_TAKEN( 2, blt, 0, 1 ); + TEST_BR2_OP_TAKEN( 3, blt, -1, 1 ); + TEST_BR2_OP_TAKEN( 4, blt, -2, -1 ); + + TEST_BR2_OP_NOTTAKEN( 5, blt, 1, 0 ); + TEST_BR2_OP_NOTTAKEN( 6, blt, 1, -1 ); + TEST_BR2_OP_NOTTAKEN( 7, blt, -1, -2 ); + TEST_BR2_OP_NOTTAKEN( 8, blt, 1, -2 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_BR2_SRC12_BYPASS( 9, 0, 0, blt, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 10, 0, 1, blt, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 11, 0, 2, blt, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 12, 1, 0, blt, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 13, 1, 1, blt, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 14, 2, 0, blt, 0, -1 ); + + TEST_BR2_SRC12_BYPASS( 15, 0, 0, blt, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 16, 0, 1, blt, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 17, 0, 2, blt, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 18, 1, 0, blt, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 19, 1, 1, blt, 0, -1 ); + TEST_BR2_SRC12_BYPASS( 20, 2, 0, blt, 0, -1 ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 21, x1, 3, \ + li x1, 1; \ + blt x0, x1, 1f; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ +1: addi x1, x1, 1; \ + addi x1, x1, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/bltu.S b/tests/bltu.S new file mode 100644 index 0000000..5dcfe7e --- /dev/null +++ b/tests/bltu.S @@ -0,0 +1,73 @@ +# See LICENSE for license details. + +#***************************************************************************** +# bltu.S +#----------------------------------------------------------------------------- +# +# Test bltu instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Branch tests + #------------------------------------------------------------- + + # Each test checks both forward and backward branches + + TEST_BR2_OP_TAKEN( 2, bltu, 0x00000000, 0x00000001 ); + TEST_BR2_OP_TAKEN( 3, bltu, 0xfffffffe, 0xffffffff ); + TEST_BR2_OP_TAKEN( 4, bltu, 0x00000000, 0xffffffff ); + + TEST_BR2_OP_NOTTAKEN( 5, bltu, 0x00000001, 0x00000000 ); + TEST_BR2_OP_NOTTAKEN( 6, bltu, 0xffffffff, 0xfffffffe ); + TEST_BR2_OP_NOTTAKEN( 7, bltu, 0xffffffff, 0x00000000 ); + TEST_BR2_OP_NOTTAKEN( 8, bltu, 0x80000000, 0x7fffffff ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_BR2_SRC12_BYPASS( 9, 0, 0, bltu, 0xf0000000, 0xefffffff ); + TEST_BR2_SRC12_BYPASS( 10, 0, 1, bltu, 0xf0000000, 0xefffffff ); + TEST_BR2_SRC12_BYPASS( 11, 0, 2, bltu, 0xf0000000, 0xefffffff ); + TEST_BR2_SRC12_BYPASS( 12, 1, 0, bltu, 0xf0000000, 0xefffffff ); + TEST_BR2_SRC12_BYPASS( 13, 1, 1, bltu, 0xf0000000, 0xefffffff ); + TEST_BR2_SRC12_BYPASS( 14, 2, 0, bltu, 0xf0000000, 0xefffffff ); + + TEST_BR2_SRC12_BYPASS( 15, 0, 0, bltu, 0xf0000000, 0xefffffff ); + TEST_BR2_SRC12_BYPASS( 16, 0, 1, bltu, 0xf0000000, 0xefffffff ); + TEST_BR2_SRC12_BYPASS( 17, 0, 2, bltu, 0xf0000000, 0xefffffff ); + TEST_BR2_SRC12_BYPASS( 18, 1, 0, bltu, 0xf0000000, 0xefffffff ); + TEST_BR2_SRC12_BYPASS( 19, 1, 1, bltu, 0xf0000000, 0xefffffff ); + TEST_BR2_SRC12_BYPASS( 20, 2, 0, bltu, 0xf0000000, 0xefffffff ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 21, x1, 3, \ + li x1, 1; \ + bltu x0, x1, 1f; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ +1: addi x1, x1, 1; \ + addi x1, x1, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/bne.S b/tests/bne.S new file mode 100644 index 0000000..0b33753 --- /dev/null +++ b/tests/bne.S @@ -0,0 +1,73 @@ +# See LICENSE for license details. + +#***************************************************************************** +# bne.S +#----------------------------------------------------------------------------- +# +# Test bne instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Branch tests + #------------------------------------------------------------- + + # Each test checks both forward and backward branches + + TEST_BR2_OP_TAKEN( 2, bne, 0, 1 ); + TEST_BR2_OP_TAKEN( 3, bne, 1, 0 ); + TEST_BR2_OP_TAKEN( 4, bne, -1, 1 ); + TEST_BR2_OP_TAKEN( 5, bne, 1, -1 ); + + TEST_BR2_OP_NOTTAKEN( 6, bne, 0, 0 ); + TEST_BR2_OP_NOTTAKEN( 7, bne, 1, 1 ); + TEST_BR2_OP_NOTTAKEN( 8, bne, -1, -1 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_BR2_SRC12_BYPASS( 9, 0, 0, bne, 0, 0 ); + TEST_BR2_SRC12_BYPASS( 10, 0, 1, bne, 0, 0 ); + TEST_BR2_SRC12_BYPASS( 11, 0, 2, bne, 0, 0 ); + TEST_BR2_SRC12_BYPASS( 12, 1, 0, bne, 0, 0 ); + TEST_BR2_SRC12_BYPASS( 13, 1, 1, bne, 0, 0 ); + TEST_BR2_SRC12_BYPASS( 14, 2, 0, bne, 0, 0 ); + + TEST_BR2_SRC12_BYPASS( 15, 0, 0, bne, 0, 0 ); + TEST_BR2_SRC12_BYPASS( 16, 0, 1, bne, 0, 0 ); + TEST_BR2_SRC12_BYPASS( 17, 0, 2, bne, 0, 0 ); + TEST_BR2_SRC12_BYPASS( 18, 1, 0, bne, 0, 0 ); + TEST_BR2_SRC12_BYPASS( 19, 1, 1, bne, 0, 0 ); + TEST_BR2_SRC12_BYPASS( 20, 2, 0, bne, 0, 0 ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 21, x1, 3, \ + li x1, 1; \ + bne x1, x0, 1f; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ +1: addi x1, x1, 1; \ + addi x1, x1, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/div.S b/tests/div.S new file mode 100644 index 0000000..24dc9ff --- /dev/null +++ b/tests/div.S @@ -0,0 +1,41 @@ +# See LICENSE for license details. + +#***************************************************************************** +# div.S +#----------------------------------------------------------------------------- +# +# Test div instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, div, 3, 20, 6 ); + TEST_RR_OP( 3, div, -3, -20, 6 ); + TEST_RR_OP( 4, div, -3, 20, -6 ); + TEST_RR_OP( 5, div, 3, -20, -6 ); + + TEST_RR_OP( 6, div, -1<<31, -1<<31, 1 ); + TEST_RR_OP( 7, div, -1<<31, -1<<31, -1 ); + + TEST_RR_OP( 8, div, -1, -1<<31, 0 ); + TEST_RR_OP( 9, div, -1, 1, 0 ); + TEST_RR_OP(10, div, -1, 0, 0 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/divu.S b/tests/divu.S new file mode 100644 index 0000000..cd348c9 --- /dev/null +++ b/tests/divu.S @@ -0,0 +1,41 @@ +# See LICENSE for license details. + +#***************************************************************************** +# divu.S +#----------------------------------------------------------------------------- +# +# Test divu instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, divu, 3, 20, 6 ); + TEST_RR_OP( 3, divu, 715827879, -20, 6 ); + TEST_RR_OP( 4, divu, 0, 20, -6 ); + TEST_RR_OP( 5, divu, 0, -20, -6 ); + + TEST_RR_OP( 6, divu, -1<<31, -1<<31, 1 ); + TEST_RR_OP( 7, divu, 0, -1<<31, -1 ); + + TEST_RR_OP( 8, divu, -1, -1<<31, 0 ); + TEST_RR_OP( 9, divu, -1, 1, 0 ); + TEST_RR_OP(10, divu, -1, 0, 0 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/j.S b/tests/j.S new file mode 100644 index 0000000..259a236 --- /dev/null +++ b/tests/j.S @@ -0,0 +1,49 @@ +# See LICENSE for license details. + +#***************************************************************************** +# j.S +#----------------------------------------------------------------------------- +# +# Test j instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Test basic + #------------------------------------------------------------- + + li TESTNUM, 2; + j test_2; + j fail; +test_2: + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 3, x1, 3, \ + li x1, 1; \ + j 1f; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ +1: addi x1, x1, 1; \ + addi x1, x1, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/jal.S b/tests/jal.S new file mode 100644 index 0000000..38a1c76 --- /dev/null +++ b/tests/jal.S @@ -0,0 +1,62 @@ +# See LICENSE for license details. + +#***************************************************************************** +# jal.S +#----------------------------------------------------------------------------- +# +# Test jal instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +.option norvc + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Test 2: Basic test + #------------------------------------------------------------- + +test_2: + li TESTNUM, 2 + li ra, 0 + +linkaddr_2: + jal target_2 + nop + nop + + j fail + +target_2: + la x2, linkaddr_2 + addi x2, x2, 4 + bne x2, ra, fail + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 3, x2, 3, \ + li x2, 1; \ + jal 1f; \ + addi x2, x2, 1; \ + addi x2, x2, 1; \ + addi x2, x2, 1; \ + addi x2, x2, 1; \ +1: addi x2, x2, 1; \ + addi x2, x2, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/jalr.S b/tests/jalr.S new file mode 100644 index 0000000..52117ab --- /dev/null +++ b/tests/jalr.S @@ -0,0 +1,90 @@ +# See LICENSE for license details. + +#***************************************************************************** +# jalr.S +#----------------------------------------------------------------------------- +# +# Test jalr instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +.option norvc + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Test 2: Basic test + #------------------------------------------------------------- + +test_2: + li TESTNUM, 2 + li x31, 0 + la x2, target_2 + +linkaddr_2: + jalr x19, x2, 0 + nop + nop + + j fail + +target_2: + la x1, linkaddr_2 + addi x1, x1, 4 + bne x1, x19, fail + + #------------------------------------------------------------- + # Test 3: Check r0 target and that r31 is not modified + #------------------------------------------------------------- + +test_3: + li TESTNUM, 3 + li x31, 0 + la x3, target_3 + +linkaddr_3: + jalr x0, x3, 0 + nop + + j fail + +target_3: + bne x31, x0, fail + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_JALR_SRC1_BYPASS( 4, 0, jalr ); + TEST_JALR_SRC1_BYPASS( 5, 1, jalr ); + TEST_JALR_SRC1_BYPASS( 6, 2, jalr ); + + #------------------------------------------------------------- + # Test delay slot instructions not executed nor bypassed + #------------------------------------------------------------- + + TEST_CASE( 7, x1, 4, \ + li x1, 1; \ + la x2, 1f; + jalr x19, x2, -4; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ + addi x1, x1, 1; \ +1: addi x1, x1, 1; \ + addi x1, x1, 1; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/lb.S b/tests/lb.S new file mode 100644 index 0000000..eaf7902 --- /dev/null +++ b/tests/lb.S @@ -0,0 +1,92 @@ +# See LICENSE for license details. + +#***************************************************************************** +# lb.S +#----------------------------------------------------------------------------- +# +# Test lb instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_LD_OP( 2, lb, 0xffffffff, 0, tdat ); + TEST_LD_OP( 3, lb, 0x00000000, 1, tdat ); + TEST_LD_OP( 4, lb, 0xfffffff0, 2, tdat ); + TEST_LD_OP( 5, lb, 0x0000000f, 3, tdat ); + + # Test with negative offset + + TEST_LD_OP( 6, lb, 0xffffffff, -3, tdat4 ); + TEST_LD_OP( 7, lb, 0x00000000, -2, tdat4 ); + TEST_LD_OP( 8, lb, 0xfffffff0, -1, tdat4 ); + TEST_LD_OP( 9, lb, 0x0000000f, 0, tdat4 ); + + # Test with a negative base + + TEST_CASE( 10, x3, 0xffffffff, \ + la x1, tdat; \ + addi x1, x1, -32; \ + lb x3, 32(x1); \ + ) + + # Test with unaligned base + + TEST_CASE( 11, x3, 0x00000000, \ + la x1, tdat; \ + addi x1, x1, -6; \ + lb x3, 7(x1); \ + ) + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_LD_DEST_BYPASS( 12, 0, lb, 0xfffffff0, 1, tdat2 ); + TEST_LD_DEST_BYPASS( 13, 1, lb, 0x0000000f, 1, tdat3 ); + TEST_LD_DEST_BYPASS( 14, 2, lb, 0x00000000, 1, tdat1 ); + + TEST_LD_SRC1_BYPASS( 15, 0, lb, 0xfffffff0, 1, tdat2 ); + TEST_LD_SRC1_BYPASS( 16, 1, lb, 0x0000000f, 1, tdat3 ); + TEST_LD_SRC1_BYPASS( 17, 2, lb, 0x00000000, 1, tdat1 ); + + #------------------------------------------------------------- + # Test write-after-write hazard + #------------------------------------------------------------- + + TEST_CASE( 18, x2, 2, \ + la x3, tdat; \ + lb x2, 0(x3); \ + li x2, 2; \ + ) + + TEST_CASE( 19, x2, 2, \ + la x3, tdat; \ + lb x2, 0(x3); \ + nop; \ + li x2, 2; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .byte 0xff +tdat2: .byte 0x00 +tdat3: .byte 0xf0 +tdat4: .byte 0x0f + +RVTEST_DATA_END diff --git a/tests/lbu.S b/tests/lbu.S new file mode 100644 index 0000000..027b643 --- /dev/null +++ b/tests/lbu.S @@ -0,0 +1,92 @@ +# See LICENSE for license details. + +#***************************************************************************** +# lbu.S +#----------------------------------------------------------------------------- +# +# Test lbu instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_LD_OP( 2, lbu, 0x000000ff, 0, tdat ); + TEST_LD_OP( 3, lbu, 0x00000000, 1, tdat ); + TEST_LD_OP( 4, lbu, 0x000000f0, 2, tdat ); + TEST_LD_OP( 5, lbu, 0x0000000f, 3, tdat ); + + # Test with negative offset + + TEST_LD_OP( 6, lbu, 0x000000ff, -3, tdat4 ); + TEST_LD_OP( 7, lbu, 0x00000000, -2, tdat4 ); + TEST_LD_OP( 8, lbu, 0x000000f0, -1, tdat4 ); + TEST_LD_OP( 9, lbu, 0x0000000f, 0, tdat4 ); + + # Test with a negative base + + TEST_CASE( 10, x3, 0x000000ff, \ + la x1, tdat; \ + addi x1, x1, -32; \ + lbu x3, 32(x1); \ + ) + + # Test with unaligned base + + TEST_CASE( 11, x3, 0x00000000, \ + la x1, tdat; \ + addi x1, x1, -6; \ + lbu x3, 7(x1); \ + ) + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_LD_DEST_BYPASS( 12, 0, lbu, 0x000000f0, 1, tdat2 ); + TEST_LD_DEST_BYPASS( 13, 1, lbu, 0x0000000f, 1, tdat3 ); + TEST_LD_DEST_BYPASS( 14, 2, lbu, 0x00000000, 1, tdat1 ); + + TEST_LD_SRC1_BYPASS( 15, 0, lbu, 0x000000f0, 1, tdat2 ); + TEST_LD_SRC1_BYPASS( 16, 1, lbu, 0x0000000f, 1, tdat3 ); + TEST_LD_SRC1_BYPASS( 17, 2, lbu, 0x00000000, 1, tdat1 ); + + #------------------------------------------------------------- + # Test write-after-write hazard + #------------------------------------------------------------- + + TEST_CASE( 18, x2, 2, \ + la x3, tdat; \ + lbu x2, 0(x3); \ + li x2, 2; \ + ) + + TEST_CASE( 19, x2, 2, \ + la x3, tdat; \ + lbu x2, 0(x3); \ + nop; \ + li x2, 2; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .byte 0xff +tdat2: .byte 0x00 +tdat3: .byte 0xf0 +tdat4: .byte 0x0f + +RVTEST_DATA_END diff --git a/tests/lh.S b/tests/lh.S new file mode 100644 index 0000000..d8eda91 --- /dev/null +++ b/tests/lh.S @@ -0,0 +1,92 @@ +# See LICENSE for license details. + +#***************************************************************************** +# lh.S +#----------------------------------------------------------------------------- +# +# Test lh instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_LD_OP( 2, lh, 0x000000ff, 0, tdat ); + TEST_LD_OP( 3, lh, 0xffffff00, 2, tdat ); + TEST_LD_OP( 4, lh, 0x00000ff0, 4, tdat ); + TEST_LD_OP( 5, lh, 0xfffff00f, 6, tdat ); + + # Test with negative offset + + TEST_LD_OP( 6, lh, 0x000000ff, -6, tdat4 ); + TEST_LD_OP( 7, lh, 0xffffff00, -4, tdat4 ); + TEST_LD_OP( 8, lh, 0x00000ff0, -2, tdat4 ); + TEST_LD_OP( 9, lh, 0xfffff00f, 0, tdat4 ); + + # Test with a negative base + + TEST_CASE( 10, x3, 0x000000ff, \ + la x1, tdat; \ + addi x1, x1, -32; \ + lh x3, 32(x1); \ + ) + + # Test with unaligned base + + TEST_CASE( 11, x3, 0xffffff00, \ + la x1, tdat; \ + addi x1, x1, -5; \ + lh x3, 7(x1); \ + ) + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_LD_DEST_BYPASS( 12, 0, lh, 0x00000ff0, 2, tdat2 ); + TEST_LD_DEST_BYPASS( 13, 1, lh, 0xfffff00f, 2, tdat3 ); + TEST_LD_DEST_BYPASS( 14, 2, lh, 0xffffff00, 2, tdat1 ); + + TEST_LD_SRC1_BYPASS( 15, 0, lh, 0x00000ff0, 2, tdat2 ); + TEST_LD_SRC1_BYPASS( 16, 1, lh, 0xfffff00f, 2, tdat3 ); + TEST_LD_SRC1_BYPASS( 17, 2, lh, 0xffffff00, 2, tdat1 ); + + #------------------------------------------------------------- + # Test write-after-write hazard + #------------------------------------------------------------- + + TEST_CASE( 18, x2, 2, \ + la x3, tdat; \ + lh x2, 0(x3); \ + li x2, 2; \ + ) + + TEST_CASE( 19, x2, 2, \ + la x3, tdat; \ + lh x2, 0(x3); \ + nop; \ + li x2, 2; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .half 0x00ff +tdat2: .half 0xff00 +tdat3: .half 0x0ff0 +tdat4: .half 0xf00f + +RVTEST_DATA_END diff --git a/tests/lhu.S b/tests/lhu.S new file mode 100644 index 0000000..71daec6 --- /dev/null +++ b/tests/lhu.S @@ -0,0 +1,92 @@ +# See LICENSE for license details. + +#***************************************************************************** +# lhu.S +#----------------------------------------------------------------------------- +# +# Test lhu instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_LD_OP( 2, lhu, 0x000000ff, 0, tdat ); + TEST_LD_OP( 3, lhu, 0x0000ff00, 2, tdat ); + TEST_LD_OP( 4, lhu, 0x00000ff0, 4, tdat ); + TEST_LD_OP( 5, lhu, 0x0000f00f, 6, tdat ); + + # Test with negative offset + + TEST_LD_OP( 6, lhu, 0x000000ff, -6, tdat4 ); + TEST_LD_OP( 7, lhu, 0x0000ff00, -4, tdat4 ); + TEST_LD_OP( 8, lhu, 0x00000ff0, -2, tdat4 ); + TEST_LD_OP( 9, lhu, 0x0000f00f, 0, tdat4 ); + + # Test with a negative base + + TEST_CASE( 10, x3, 0x000000ff, \ + la x1, tdat; \ + addi x1, x1, -32; \ + lhu x3, 32(x1); \ + ) + + # Test with unaligned base + + TEST_CASE( 11, x3, 0x0000ff00, \ + la x1, tdat; \ + addi x1, x1, -5; \ + lhu x3, 7(x1); \ + ) + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_LD_DEST_BYPASS( 12, 0, lhu, 0x00000ff0, 2, tdat2 ); + TEST_LD_DEST_BYPASS( 13, 1, lhu, 0x0000f00f, 2, tdat3 ); + TEST_LD_DEST_BYPASS( 14, 2, lhu, 0x0000ff00, 2, tdat1 ); + + TEST_LD_SRC1_BYPASS( 15, 0, lhu, 0x00000ff0, 2, tdat2 ); + TEST_LD_SRC1_BYPASS( 16, 1, lhu, 0x0000f00f, 2, tdat3 ); + TEST_LD_SRC1_BYPASS( 17, 2, lhu, 0x0000ff00, 2, tdat1 ); + + #------------------------------------------------------------- + # Test write-after-write hazard + #------------------------------------------------------------- + + TEST_CASE( 18, x2, 2, \ + la x3, tdat; \ + lhu x2, 0(x3); \ + li x2, 2; \ + ) + + TEST_CASE( 19, x2, 2, \ + la x3, tdat; \ + lhu x2, 0(x3); \ + nop; \ + li x2, 2; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .half 0x00ff +tdat2: .half 0xff00 +tdat3: .half 0x0ff0 +tdat4: .half 0xf00f + +RVTEST_DATA_END diff --git a/tests/lui.S b/tests/lui.S new file mode 100644 index 0000000..50822d1 --- /dev/null +++ b/tests/lui.S @@ -0,0 +1,36 @@ +# See LICENSE for license details. + +#***************************************************************************** +# lui.S +#----------------------------------------------------------------------------- +# +# Test lui instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_CASE( 2, x1, 0x00000000, lui x1, 0x00000 ); + TEST_CASE( 3, x1, 0xfffff800, lui x1, 0xfffff;sra x1,x1,1); + TEST_CASE( 4, x1, 0x000007ff, lui x1, 0x7ffff;sra x1,x1,20); + TEST_CASE( 5, x1, 0xfffff800, lui x1, 0x80000;sra x1,x1,20); + + TEST_CASE( 6, x0, 0, lui x0, 0x80000 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/lw.S b/tests/lw.S new file mode 100644 index 0000000..4a07838 --- /dev/null +++ b/tests/lw.S @@ -0,0 +1,92 @@ +# See LICENSE for license details. + +#***************************************************************************** +# lw.S +#----------------------------------------------------------------------------- +# +# Test lw instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_LD_OP( 2, lw, 0x00ff00ff, 0, tdat ); + TEST_LD_OP( 3, lw, 0xff00ff00, 4, tdat ); + TEST_LD_OP( 4, lw, 0x0ff00ff0, 8, tdat ); + TEST_LD_OP( 5, lw, 0xf00ff00f, 12, tdat ); + + # Test with negative offset + + TEST_LD_OP( 6, lw, 0x00ff00ff, -12, tdat4 ); + TEST_LD_OP( 7, lw, 0xff00ff00, -8, tdat4 ); + TEST_LD_OP( 8, lw, 0x0ff00ff0, -4, tdat4 ); + TEST_LD_OP( 9, lw, 0xf00ff00f, 0, tdat4 ); + + # Test with a negative base + + TEST_CASE( 10, x3, 0x00ff00ff, \ + la x1, tdat; \ + addi x1, x1, -32; \ + lw x3, 32(x1); \ + ) + + # Test with unaligned base + + TEST_CASE( 11, x3, 0xff00ff00, \ + la x1, tdat; \ + addi x1, x1, -3; \ + lw x3, 7(x1); \ + ) + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_LD_DEST_BYPASS( 12, 0, lw, 0x0ff00ff0, 4, tdat2 ); + TEST_LD_DEST_BYPASS( 13, 1, lw, 0xf00ff00f, 4, tdat3 ); + TEST_LD_DEST_BYPASS( 14, 2, lw, 0xff00ff00, 4, tdat1 ); + + TEST_LD_SRC1_BYPASS( 15, 0, lw, 0x0ff00ff0, 4, tdat2 ); + TEST_LD_SRC1_BYPASS( 16, 1, lw, 0xf00ff00f, 4, tdat3 ); + TEST_LD_SRC1_BYPASS( 17, 2, lw, 0xff00ff00, 4, tdat1 ); + + #------------------------------------------------------------- + # Test write-after-write hazard + #------------------------------------------------------------- + + TEST_CASE( 18, x2, 2, \ + la x3, tdat; \ + lw x2, 0(x3); \ + li x2, 2; \ + ) + + TEST_CASE( 19, x2, 2, \ + la x3, tdat; \ + lw x2, 0(x3); \ + nop; \ + li x2, 2; \ + ) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .word 0x00ff00ff +tdat2: .word 0xff00ff00 +tdat3: .word 0x0ff00ff0 +tdat4: .word 0xf00ff00f + +RVTEST_DATA_END diff --git a/tests/mul.S b/tests/mul.S new file mode 100644 index 0000000..0368629 --- /dev/null +++ b/tests/mul.S @@ -0,0 +1,84 @@ +# See LICENSE for license details. + +#***************************************************************************** +# mul.S +#----------------------------------------------------------------------------- +# +# Test mul instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP(32, mul, 0x00001200, 0x00007e00, 0xb6db6db7 ); + TEST_RR_OP(33, mul, 0x00001240, 0x00007fc0, 0xb6db6db7 ); + + TEST_RR_OP( 2, mul, 0x00000000, 0x00000000, 0x00000000 ); + TEST_RR_OP( 3, mul, 0x00000001, 0x00000001, 0x00000001 ); + TEST_RR_OP( 4, mul, 0x00000015, 0x00000003, 0x00000007 ); + + TEST_RR_OP( 5, mul, 0x00000000, 0x00000000, 0xffff8000 ); + TEST_RR_OP( 6, mul, 0x00000000, 0x80000000, 0x00000000 ); + TEST_RR_OP( 7, mul, 0x00000000, 0x80000000, 0xffff8000 ); + + TEST_RR_OP(30, mul, 0x0000ff7f, 0xaaaaaaab, 0x0002fe7d ); + TEST_RR_OP(31, mul, 0x0000ff7f, 0x0002fe7d, 0xaaaaaaab ); + + TEST_RR_OP(34, mul, 0x00000000, 0xff000000, 0xff000000 ); + + TEST_RR_OP(35, mul, 0x00000001, 0xffffffff, 0xffffffff ); + TEST_RR_OP(36, mul, 0xffffffff, 0xffffffff, 0x00000001 ); + TEST_RR_OP(37, mul, 0xffffffff, 0x00000001, 0xffffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 8, mul, 143, 13, 11 ); + TEST_RR_SRC2_EQ_DEST( 9, mul, 154, 14, 11 ); + TEST_RR_SRC12_EQ_DEST( 10, mul, 169, 13 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 11, 0, mul, 143, 13, 11 ); + TEST_RR_DEST_BYPASS( 12, 1, mul, 154, 14, 11 ); + TEST_RR_DEST_BYPASS( 13, 2, mul, 165, 15, 11 ); + + TEST_RR_SRC12_BYPASS( 14, 0, 0, mul, 143, 13, 11 ); + TEST_RR_SRC12_BYPASS( 15, 0, 1, mul, 154, 14, 11 ); + TEST_RR_SRC12_BYPASS( 16, 0, 2, mul, 165, 15, 11 ); + TEST_RR_SRC12_BYPASS( 17, 1, 0, mul, 143, 13, 11 ); + TEST_RR_SRC12_BYPASS( 18, 1, 1, mul, 154, 14, 11 ); + TEST_RR_SRC12_BYPASS( 19, 2, 0, mul, 165, 15, 11 ); + + TEST_RR_SRC21_BYPASS( 20, 0, 0, mul, 143, 13, 11 ); + TEST_RR_SRC21_BYPASS( 21, 0, 1, mul, 154, 14, 11 ); + TEST_RR_SRC21_BYPASS( 22, 0, 2, mul, 165, 15, 11 ); + TEST_RR_SRC21_BYPASS( 23, 1, 0, mul, 143, 13, 11 ); + TEST_RR_SRC21_BYPASS( 24, 1, 1, mul, 154, 14, 11 ); + TEST_RR_SRC21_BYPASS( 25, 2, 0, mul, 165, 15, 11 ); + + TEST_RR_ZEROSRC1( 26, mul, 0, 31 ); + TEST_RR_ZEROSRC2( 27, mul, 0, 32 ); + TEST_RR_ZEROSRC12( 28, mul, 0 ); + TEST_RR_ZERODEST( 29, mul, 33, 34 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/mulh.S b/tests/mulh.S new file mode 100644 index 0000000..e583f5f --- /dev/null +++ b/tests/mulh.S @@ -0,0 +1,81 @@ +# See LICENSE for license details. + +#***************************************************************************** +# mulh.S +#----------------------------------------------------------------------------- +# +# Test mulh instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, mulh, 0x00000000, 0x00000000, 0x00000000 ); + TEST_RR_OP( 3, mulh, 0x00000000, 0x00000001, 0x00000001 ); + TEST_RR_OP( 4, mulh, 0x00000000, 0x00000003, 0x00000007 ); + + TEST_RR_OP( 5, mulh, 0x00000000, 0x00000000, 0xffff8000 ); + TEST_RR_OP( 6, mulh, 0x00000000, 0x80000000, 0x00000000 ); + TEST_RR_OP( 7, mulh, 0x00000000, 0x80000000, 0x00000000 ); + + TEST_RR_OP(30, mulh, 0xffff0081, 0xaaaaaaab, 0x0002fe7d ); + TEST_RR_OP(31, mulh, 0xffff0081, 0x0002fe7d, 0xaaaaaaab ); + + TEST_RR_OP(32, mulh, 0x00010000, 0xff000000, 0xff000000 ); + + TEST_RR_OP(33, mulh, 0x00000000, 0xffffffff, 0xffffffff ); + TEST_RR_OP(34, mulh, 0xffffffff, 0xffffffff, 0x00000001 ); + TEST_RR_OP(35, mulh, 0xffffffff, 0x00000001, 0xffffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 8, mulh, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC2_EQ_DEST( 9, mulh, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC12_EQ_DEST( 10, mulh, 43264, 13<<20 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 11, 0, mulh, 36608, 13<<20, 11<<20 ); + TEST_RR_DEST_BYPASS( 12, 1, mulh, 39424, 14<<20, 11<<20 ); + TEST_RR_DEST_BYPASS( 13, 2, mulh, 42240, 15<<20, 11<<20 ); + + TEST_RR_SRC12_BYPASS( 14, 0, 0, mulh, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 15, 0, 1, mulh, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 16, 0, 2, mulh, 42240, 15<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 17, 1, 0, mulh, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 18, 1, 1, mulh, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 19, 2, 0, mulh, 42240, 15<<20, 11<<20 ); + + TEST_RR_SRC21_BYPASS( 20, 0, 0, mulh, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 21, 0, 1, mulh, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 22, 0, 2, mulh, 42240, 15<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 23, 1, 0, mulh, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 24, 1, 1, mulh, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 25, 2, 0, mulh, 42240, 15<<20, 11<<20 ); + + TEST_RR_ZEROSRC1( 26, mulh, 0, 31<<26 ); + TEST_RR_ZEROSRC2( 27, mulh, 0, 32<<26 ); + TEST_RR_ZEROSRC12( 28, mulh, 0 ); + TEST_RR_ZERODEST( 29, mulh, 33<<20, 34<<20 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/mulhsu.S b/tests/mulhsu.S new file mode 100644 index 0000000..28b3690 --- /dev/null +++ b/tests/mulhsu.S @@ -0,0 +1,83 @@ +# See LICENSE for license details. + +#***************************************************************************** +# mulhsu.S +#----------------------------------------------------------------------------- +# +# Test mulhsu instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, mulhsu, 0x00000000, 0x00000000, 0x00000000 ); + TEST_RR_OP( 3, mulhsu, 0x00000000, 0x00000001, 0x00000001 ); + TEST_RR_OP( 4, mulhsu, 0x00000000, 0x00000003, 0x00000007 ); + + TEST_RR_OP( 5, mulhsu, 0x00000000, 0x00000000, 0xffff8000 ); + TEST_RR_OP( 6, mulhsu, 0x00000000, 0x80000000, 0x00000000 ); + TEST_RR_OP( 7, mulhsu, 0x80004000, 0x80000000, 0xffff8000 ); + + TEST_RR_OP(30, mulhsu, 0xffff0081, 0xaaaaaaab, 0x0002fe7d ); + TEST_RR_OP(31, mulhsu, 0x0001fefe, 0x0002fe7d, 0xaaaaaaab ); + + TEST_RR_OP(32, mulhsu, 0xff010000, 0xff000000, 0xff000000 ); + + TEST_RR_OP(33, mulhsu, 0xffffffff, 0xffffffff, 0xffffffff ); + TEST_RR_OP(34, mulhsu, 0xffffffff, 0xffffffff, 0x00000001 ); + TEST_RR_OP(35, mulhsu, 0x00000000, 0x00000001, 0xffffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 8, mulhsu, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC2_EQ_DEST( 9, mulhsu, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC12_EQ_DEST( 10, mulhsu, 43264, 13<<20 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 11, 0, mulhsu, 36608, 13<<20, 11<<20 ); + TEST_RR_DEST_BYPASS( 12, 1, mulhsu, 39424, 14<<20, 11<<20 ); + TEST_RR_DEST_BYPASS( 13, 2, mulhsu, 42240, 15<<20, 11<<20 ); + + TEST_RR_SRC12_BYPASS( 14, 0, 0, mulhsu, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 15, 0, 1, mulhsu, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 16, 0, 2, mulhsu, 42240, 15<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 17, 1, 0, mulhsu, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 18, 1, 1, mulhsu, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 19, 2, 0, mulhsu, 42240, 15<<20, 11<<20 ); + + TEST_RR_SRC21_BYPASS( 20, 0, 0, mulhsu, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 21, 0, 1, mulhsu, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 22, 0, 2, mulhsu, 42240, 15<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 23, 1, 0, mulhsu, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 24, 1, 1, mulhsu, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 25, 2, 0, mulhsu, 42240, 15<<20, 11<<20 ); + + TEST_RR_ZEROSRC1( 26, mulhsu, 0, 31<<26 ); + TEST_RR_ZEROSRC2( 27, mulhsu, 0, 32<<26 ); + TEST_RR_ZEROSRC12( 28, mulhsu, 0 ); + TEST_RR_ZERODEST( 29, mulhsu, 33<<20, 34<<20 ); + + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/mulhu.S b/tests/mulhu.S new file mode 100644 index 0000000..601dcff --- /dev/null +++ b/tests/mulhu.S @@ -0,0 +1,82 @@ +# See LICENSE for license details. + +#***************************************************************************** +# mulhu.S +#----------------------------------------------------------------------------- +# +# Test mulhu instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, mulhu, 0x00000000, 0x00000000, 0x00000000 ); + TEST_RR_OP( 3, mulhu, 0x00000000, 0x00000001, 0x00000001 ); + TEST_RR_OP( 4, mulhu, 0x00000000, 0x00000003, 0x00000007 ); + + TEST_RR_OP( 5, mulhu, 0x00000000, 0x00000000, 0xffff8000 ); + TEST_RR_OP( 6, mulhu, 0x00000000, 0x80000000, 0x00000000 ); + TEST_RR_OP( 7, mulhu, 0x7fffc000, 0x80000000, 0xffff8000 ); + + TEST_RR_OP(30, mulhu, 0x0001fefe, 0xaaaaaaab, 0x0002fe7d ); + TEST_RR_OP(31, mulhu, 0x0001fefe, 0x0002fe7d, 0xaaaaaaab ); + + TEST_RR_OP(32, mulhu, 0xfe010000, 0xff000000, 0xff000000 ); + + TEST_RR_OP(33, mulhu, 0xfffffffe, 0xffffffff, 0xffffffff ); + TEST_RR_OP(34, mulhu, 0x00000000, 0xffffffff, 0x00000001 ); + TEST_RR_OP(35, mulhu, 0x00000000, 0x00000001, 0xffffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 8, mulhu, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC2_EQ_DEST( 9, mulhu, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC12_EQ_DEST( 10, mulhu, 43264, 13<<20 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 11, 0, mulhu, 36608, 13<<20, 11<<20 ); + TEST_RR_DEST_BYPASS( 12, 1, mulhu, 39424, 14<<20, 11<<20 ); + TEST_RR_DEST_BYPASS( 13, 2, mulhu, 42240, 15<<20, 11<<20 ); + + TEST_RR_SRC12_BYPASS( 14, 0, 0, mulhu, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 15, 0, 1, mulhu, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 16, 0, 2, mulhu, 42240, 15<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 17, 1, 0, mulhu, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 18, 1, 1, mulhu, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC12_BYPASS( 19, 2, 0, mulhu, 42240, 15<<20, 11<<20 ); + + TEST_RR_SRC21_BYPASS( 20, 0, 0, mulhu, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 21, 0, 1, mulhu, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 22, 0, 2, mulhu, 42240, 15<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 23, 1, 0, mulhu, 36608, 13<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 24, 1, 1, mulhu, 39424, 14<<20, 11<<20 ); + TEST_RR_SRC21_BYPASS( 25, 2, 0, mulhu, 42240, 15<<20, 11<<20 ); + + TEST_RR_ZEROSRC1( 26, mulhu, 0, 31<<26 ); + TEST_RR_ZEROSRC2( 27, mulhu, 0, 32<<26 ); + TEST_RR_ZEROSRC12( 28, mulhu, 0 ); + TEST_RR_ZERODEST( 29, mulhu, 33<<20, 34<<20 ); + + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/or.S b/tests/or.S new file mode 100644 index 0000000..110bcc4 --- /dev/null +++ b/tests/or.S @@ -0,0 +1,69 @@ +# See LICENSE for license details. + +#***************************************************************************** +# or.S +#----------------------------------------------------------------------------- +# +# Test or instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_OP( 3, or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_OP( 4, or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RR_OP( 5, or, 0xf0fff0ff, 0xf00ff00f, 0xf0f0f0f0 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 6, or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC2_EQ_DEST( 7, or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC12_EQ_DEST( 8, or, 0xff00ff00, 0xff00ff00 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 9, 0, or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_DEST_BYPASS( 10, 1, or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_DEST_BYPASS( 11, 2, or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RR_SRC12_BYPASS( 12, 0, 0, or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC12_BYPASS( 13, 0, 1, or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC12_BYPASS( 14, 0, 2, or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RR_SRC12_BYPASS( 15, 1, 0, or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC12_BYPASS( 16, 1, 1, or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC12_BYPASS( 17, 2, 0, or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RR_SRC21_BYPASS( 18, 0, 0, or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC21_BYPASS( 19, 0, 1, or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC21_BYPASS( 20, 0, 2, or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RR_SRC21_BYPASS( 21, 1, 0, or, 0xff0fff0f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC21_BYPASS( 22, 1, 1, or, 0xfff0fff0, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC21_BYPASS( 23, 2, 0, or, 0x0fff0fff, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RR_ZEROSRC1( 24, or, 0xff00ff00, 0xff00ff00 ); + TEST_RR_ZEROSRC2( 25, or, 0x00ff00ff, 0x00ff00ff ); + TEST_RR_ZEROSRC12( 26, or, 0 ); + TEST_RR_ZERODEST( 27, or, 0x11111111, 0x22222222 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/ori.S b/tests/ori.S new file mode 100644 index 0000000..a674784 --- /dev/null +++ b/tests/ori.S @@ -0,0 +1,55 @@ +# See LICENSE for license details. + +#***************************************************************************** +# ori.S +#----------------------------------------------------------------------------- +# +# Test ori instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_IMM_OP( 2, ori, 0xffffff0f, 0xff00ff00, 0xf0f ); + TEST_IMM_OP( 3, ori, 0x0ff00ff0, 0x0ff00ff0, 0x0f0 ); + TEST_IMM_OP( 4, ori, 0x00ff07ff, 0x00ff00ff, 0x70f ); + TEST_IMM_OP( 5, ori, 0xf00ff0ff, 0xf00ff00f, 0x0f0 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_IMM_SRC1_EQ_DEST( 6, ori, 0xff00fff0, 0xff00ff00, 0x0f0 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_IMM_DEST_BYPASS( 7, 0, ori, 0x0ff00ff0, 0x0ff00ff0, 0x0f0 ); + TEST_IMM_DEST_BYPASS( 8, 1, ori, 0x00ff07ff, 0x00ff00ff, 0x70f ); + TEST_IMM_DEST_BYPASS( 9, 2, ori, 0xf00ff0ff, 0xf00ff00f, 0x0f0 ); + + TEST_IMM_SRC1_BYPASS( 10, 0, ori, 0x0ff00ff0, 0x0ff00ff0, 0x0f0 ); + TEST_IMM_SRC1_BYPASS( 11, 1, ori, 0xffffffff, 0x00ff00ff, 0xf0f ); + TEST_IMM_SRC1_BYPASS( 12, 2, ori, 0xf00ff0ff, 0xf00ff00f, 0x0f0 ); + + TEST_IMM_ZEROSRC1( 13, ori, 0x0f0, 0x0f0 ); + TEST_IMM_ZERODEST( 14, ori, 0x00ff00ff, 0x70f ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/rem.S b/tests/rem.S new file mode 100644 index 0000000..7955736 --- /dev/null +++ b/tests/rem.S @@ -0,0 +1,41 @@ +# See LICENSE for license details. + +#***************************************************************************** +# rem.S +#----------------------------------------------------------------------------- +# +# Test rem instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, rem, 2, 20, 6 ); + TEST_RR_OP( 3, rem, -2, -20, 6 ); + TEST_RR_OP( 4, rem, 2, 20, -6 ); + TEST_RR_OP( 5, rem, -2, -20, -6 ); + + TEST_RR_OP( 6, rem, 0, -1<<31, 1 ); + TEST_RR_OP( 7, rem, 0, -1<<31, -1 ); + + TEST_RR_OP( 8, rem, -1<<31, -1<<31, 0 ); + TEST_RR_OP( 9, rem, 1, 1, 0 ); + TEST_RR_OP(10, rem, 0, 0, 0 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/remu.S b/tests/remu.S new file mode 100644 index 0000000..a96cfc1 --- /dev/null +++ b/tests/remu.S @@ -0,0 +1,41 @@ +# See LICENSE for license details. + +#***************************************************************************** +# remu.S +#----------------------------------------------------------------------------- +# +# Test remu instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, remu, 2, 20, 6 ); + TEST_RR_OP( 3, remu, 2, -20, 6 ); + TEST_RR_OP( 4, remu, 20, 20, -6 ); + TEST_RR_OP( 5, remu, -20, -20, -6 ); + + TEST_RR_OP( 6, remu, 0, -1<<31, 1 ); + TEST_RR_OP( 7, remu, -1<<31, -1<<31, -1 ); + + TEST_RR_OP( 8, remu, -1<<31, -1<<31, 0 ); + TEST_RR_OP( 9, remu, 1, 1, 0 ); + TEST_RR_OP(10, remu, 0, 0, 0 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/riscv_test.h b/tests/riscv_test.h new file mode 100644 index 0000000..71a4366 --- /dev/null +++ b/tests/riscv_test.h @@ -0,0 +1,64 @@ +#ifndef _ENV_PICORV32_TEST_H +#define _ENV_PICORV32_TEST_H + +#ifndef TEST_FUNC_NAME +# define TEST_FUNC_NAME mytest +# define TEST_FUNC_TXT "mytest" +# define TEST_FUNC_RET mytest_ret +#endif + +#define RVTEST_RV32U +#define TESTNUM x28 + +#define RVTEST_CODE_BEGIN \ + .text; \ + .global TEST_FUNC_NAME; \ + .global TEST_FUNC_RET; \ +TEST_FUNC_NAME: \ + lui a0,%hi(.test_name); \ + addi a0,a0,%lo(.test_name); \ + lui a2,0x10000000>>12; \ +.prname_next: \ + lb a1,0(a0); \ + beq a1,zero,.prname_done; \ + sw a1,0(a2); \ + addi a0,a0,1; \ + jal zero,.prname_next; \ +.test_name: \ + .ascii TEST_FUNC_TXT; \ + .byte 0x00; \ + .balign 4, 0; \ +.prname_done: \ + addi a1,zero,'.'; \ + sw a1,0(a2); \ + sw a1,0(a2); + +#define RVTEST_PASS \ + lui a0,0x10000000>>12; \ + addi a1,zero,'O'; \ + addi a2,zero,'K'; \ + addi a3,zero,'\n'; \ + sw a1,0(a0); \ + sw a2,0(a0); \ + sw a3,0(a0); \ + jal zero,TEST_FUNC_RET; + +#define RVTEST_FAIL \ + lui a0,0x10000000>>12; \ + addi a1,zero,'E'; \ + addi a2,zero,'R'; \ + addi a3,zero,'O'; \ + addi a4,zero,'\n'; \ + sw a1,0(a0); \ + sw a2,0(a0); \ + sw a2,0(a0); \ + sw a3,0(a0); \ + sw a2,0(a0); \ + sw a4,0(a0); \ + ebreak; + +#define RVTEST_CODE_END +#define RVTEST_DATA_BEGIN .balign 4; +#define RVTEST_DATA_END + +#endif diff --git a/tests/sb.S b/tests/sb.S new file mode 100644 index 0000000..05d1894 --- /dev/null +++ b/tests/sb.S @@ -0,0 +1,96 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sb.S +#----------------------------------------------------------------------------- +# +# Test sb instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_ST_OP( 2, lb, sb, 0xffffffaa, 0, tdat ); + TEST_ST_OP( 3, lb, sb, 0x00000000, 1, tdat ); + TEST_ST_OP( 4, lh, sb, 0xffffefa0, 2, tdat ); + TEST_ST_OP( 5, lb, sb, 0x0000000a, 3, tdat ); + + # Test with negative offset + + TEST_ST_OP( 6, lb, sb, 0xffffffaa, -3, tdat8 ); + TEST_ST_OP( 7, lb, sb, 0x00000000, -2, tdat8 ); + TEST_ST_OP( 8, lb, sb, 0xffffffa0, -1, tdat8 ); + TEST_ST_OP( 9, lb, sb, 0x0000000a, 0, tdat8 ); + + # Test with a negative base + + TEST_CASE( 10, x3, 0x78, \ + la x1, tdat9; \ + li x2, 0x12345678; \ + addi x4, x1, -32; \ + sb x2, 32(x4); \ + lb x3, 0(x1); \ + ) + + # Test with unaligned base + + TEST_CASE( 11, x3, 0xffffff98, \ + la x1, tdat9; \ + li x2, 0x00003098; \ + addi x1, x1, -6; \ + sb x2, 7(x1); \ + la x4, tdat10; \ + lb x3, 0(x4); \ + ) + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_ST_SRC12_BYPASS( 12, 0, 0, lb, sb, 0xffffffdd, 0, tdat ); + TEST_ST_SRC12_BYPASS( 13, 0, 1, lb, sb, 0xffffffcd, 1, tdat ); + TEST_ST_SRC12_BYPASS( 14, 0, 2, lb, sb, 0xffffffcc, 2, tdat ); + TEST_ST_SRC12_BYPASS( 15, 1, 0, lb, sb, 0xffffffbc, 3, tdat ); + TEST_ST_SRC12_BYPASS( 16, 1, 1, lb, sb, 0xffffffbb, 4, tdat ); + TEST_ST_SRC12_BYPASS( 17, 2, 0, lb, sb, 0xffffffab, 5, tdat ); + + TEST_ST_SRC21_BYPASS( 18, 0, 0, lb, sb, 0x33, 0, tdat ); + TEST_ST_SRC21_BYPASS( 19, 0, 1, lb, sb, 0x23, 1, tdat ); + TEST_ST_SRC21_BYPASS( 20, 0, 2, lb, sb, 0x22, 2, tdat ); + TEST_ST_SRC21_BYPASS( 21, 1, 0, lb, sb, 0x12, 3, tdat ); + TEST_ST_SRC21_BYPASS( 22, 1, 1, lb, sb, 0x11, 4, tdat ); + TEST_ST_SRC21_BYPASS( 23, 2, 0, lb, sb, 0x01, 5, tdat ); + + li a0, 0xef + la a1, tdat + sb a0, 3(a1) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .byte 0xef +tdat2: .byte 0xef +tdat3: .byte 0xef +tdat4: .byte 0xef +tdat5: .byte 0xef +tdat6: .byte 0xef +tdat7: .byte 0xef +tdat8: .byte 0xef +tdat9: .byte 0xef +tdat10: .byte 0xef + +RVTEST_DATA_END diff --git a/tests/sh.S b/tests/sh.S new file mode 100644 index 0000000..387e181 --- /dev/null +++ b/tests/sh.S @@ -0,0 +1,96 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sh.S +#----------------------------------------------------------------------------- +# +# Test sh instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_ST_OP( 2, lh, sh, 0x000000aa, 0, tdat ); + TEST_ST_OP( 3, lh, sh, 0xffffaa00, 2, tdat ); + TEST_ST_OP( 4, lw, sh, 0xbeef0aa0, 4, tdat ); + TEST_ST_OP( 5, lh, sh, 0xffffa00a, 6, tdat ); + + # Test with negative offset + + TEST_ST_OP( 6, lh, sh, 0x000000aa, -6, tdat8 ); + TEST_ST_OP( 7, lh, sh, 0xffffaa00, -4, tdat8 ); + TEST_ST_OP( 8, lh, sh, 0x00000aa0, -2, tdat8 ); + TEST_ST_OP( 9, lh, sh, 0xffffa00a, 0, tdat8 ); + + # Test with a negative base + + TEST_CASE( 10, x3, 0x5678, \ + la x1, tdat9; \ + li x2, 0x12345678; \ + addi x4, x1, -32; \ + sh x2, 32(x4); \ + lh x3, 0(x1); \ + ) + + # Test with unaligned base + + TEST_CASE( 11, x3, 0x3098, \ + la x1, tdat9; \ + li x2, 0x00003098; \ + addi x1, x1, -5; \ + sh x2, 7(x1); \ + la x4, tdat10; \ + lh x3, 0(x4); \ + ) + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_ST_SRC12_BYPASS( 12, 0, 0, lh, sh, 0xffffccdd, 0, tdat ); + TEST_ST_SRC12_BYPASS( 13, 0, 1, lh, sh, 0xffffbccd, 2, tdat ); + TEST_ST_SRC12_BYPASS( 14, 0, 2, lh, sh, 0xffffbbcc, 4, tdat ); + TEST_ST_SRC12_BYPASS( 15, 1, 0, lh, sh, 0xffffabbc, 6, tdat ); + TEST_ST_SRC12_BYPASS( 16, 1, 1, lh, sh, 0xffffaabb, 8, tdat ); + TEST_ST_SRC12_BYPASS( 17, 2, 0, lh, sh, 0xffffdaab, 10, tdat ); + + TEST_ST_SRC21_BYPASS( 18, 0, 0, lh, sh, 0x2233, 0, tdat ); + TEST_ST_SRC21_BYPASS( 19, 0, 1, lh, sh, 0x1223, 2, tdat ); + TEST_ST_SRC21_BYPASS( 20, 0, 2, lh, sh, 0x1122, 4, tdat ); + TEST_ST_SRC21_BYPASS( 21, 1, 0, lh, sh, 0x0112, 6, tdat ); + TEST_ST_SRC21_BYPASS( 22, 1, 1, lh, sh, 0x0011, 8, tdat ); + TEST_ST_SRC21_BYPASS( 23, 2, 0, lh, sh, 0x3001, 10, tdat ); + + li a0, 0xbeef + la a1, tdat + sh a0, 6(a1) + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .half 0xbeef +tdat2: .half 0xbeef +tdat3: .half 0xbeef +tdat4: .half 0xbeef +tdat5: .half 0xbeef +tdat6: .half 0xbeef +tdat7: .half 0xbeef +tdat8: .half 0xbeef +tdat9: .half 0xbeef +tdat10: .half 0xbeef + +RVTEST_DATA_END diff --git a/tests/simple.S b/tests/simple.S new file mode 100644 index 0000000..92cf203 --- /dev/null +++ b/tests/simple.S @@ -0,0 +1,27 @@ +# See LICENSE for license details. + +#***************************************************************************** +# simple.S +#----------------------------------------------------------------------------- +# +# This is the most basic self checking test. If your simulator does not +# pass thiss then there is little chance that it will pass any of the +# more complicated self checking tests. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + +RVTEST_PASS + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/sll.S b/tests/sll.S new file mode 100644 index 0000000..d297ac5 --- /dev/null +++ b/tests/sll.S @@ -0,0 +1,90 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sll.S +#----------------------------------------------------------------------------- +# +# Test sll instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, sll, 0x00000001, 0x00000001, 0 ); + TEST_RR_OP( 3, sll, 0x00000002, 0x00000001, 1 ); + TEST_RR_OP( 4, sll, 0x00000080, 0x00000001, 7 ); + TEST_RR_OP( 5, sll, 0x00004000, 0x00000001, 14 ); + TEST_RR_OP( 6, sll, 0x80000000, 0x00000001, 31 ); + + TEST_RR_OP( 7, sll, 0xffffffff, 0xffffffff, 0 ); + TEST_RR_OP( 8, sll, 0xfffffffe, 0xffffffff, 1 ); + TEST_RR_OP( 9, sll, 0xffffff80, 0xffffffff, 7 ); + TEST_RR_OP( 10, sll, 0xffffc000, 0xffffffff, 14 ); + TEST_RR_OP( 11, sll, 0x80000000, 0xffffffff, 31 ); + + TEST_RR_OP( 12, sll, 0x21212121, 0x21212121, 0 ); + TEST_RR_OP( 13, sll, 0x42424242, 0x21212121, 1 ); + TEST_RR_OP( 14, sll, 0x90909080, 0x21212121, 7 ); + TEST_RR_OP( 15, sll, 0x48484000, 0x21212121, 14 ); + TEST_RR_OP( 16, sll, 0x80000000, 0x21212121, 31 ); + + # Verify that shifts only use bottom five bits + + TEST_RR_OP( 17, sll, 0x21212121, 0x21212121, 0xffffffe0 ); + TEST_RR_OP( 18, sll, 0x42424242, 0x21212121, 0xffffffe1 ); + TEST_RR_OP( 19, sll, 0x90909080, 0x21212121, 0xffffffe7 ); + TEST_RR_OP( 20, sll, 0x48484000, 0x21212121, 0xffffffee ); + TEST_RR_OP( 21, sll, 0x00000000, 0x21212120, 0xffffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 22, sll, 0x00000080, 0x00000001, 7 ); + TEST_RR_SRC2_EQ_DEST( 23, sll, 0x00004000, 0x00000001, 14 ); + TEST_RR_SRC12_EQ_DEST( 24, sll, 24, 3 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 25, 0, sll, 0x00000080, 0x00000001, 7 ); + TEST_RR_DEST_BYPASS( 26, 1, sll, 0x00004000, 0x00000001, 14 ); + TEST_RR_DEST_BYPASS( 27, 2, sll, 0x80000000, 0x00000001, 31 ); + + TEST_RR_SRC12_BYPASS( 28, 0, 0, sll, 0x00000080, 0x00000001, 7 ); + TEST_RR_SRC12_BYPASS( 29, 0, 1, sll, 0x00004000, 0x00000001, 14 ); + TEST_RR_SRC12_BYPASS( 30, 0, 2, sll, 0x80000000, 0x00000001, 31 ); + TEST_RR_SRC12_BYPASS( 31, 1, 0, sll, 0x00000080, 0x00000001, 7 ); + TEST_RR_SRC12_BYPASS( 32, 1, 1, sll, 0x00004000, 0x00000001, 14 ); + TEST_RR_SRC12_BYPASS( 33, 2, 0, sll, 0x80000000, 0x00000001, 31 ); + + TEST_RR_SRC21_BYPASS( 34, 0, 0, sll, 0x00000080, 0x00000001, 7 ); + TEST_RR_SRC21_BYPASS( 35, 0, 1, sll, 0x00004000, 0x00000001, 14 ); + TEST_RR_SRC21_BYPASS( 36, 0, 2, sll, 0x80000000, 0x00000001, 31 ); + TEST_RR_SRC21_BYPASS( 37, 1, 0, sll, 0x00000080, 0x00000001, 7 ); + TEST_RR_SRC21_BYPASS( 38, 1, 1, sll, 0x00004000, 0x00000001, 14 ); + TEST_RR_SRC21_BYPASS( 39, 2, 0, sll, 0x80000000, 0x00000001, 31 ); + + TEST_RR_ZEROSRC1( 40, sll, 0, 15 ); + TEST_RR_ZEROSRC2( 41, sll, 32, 32 ); + TEST_RR_ZEROSRC12( 42, sll, 0 ); + TEST_RR_ZERODEST( 43, sll, 1024, 2048 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/slli.S b/tests/slli.S new file mode 100644 index 0000000..a51eec9 --- /dev/null +++ b/tests/slli.S @@ -0,0 +1,68 @@ +# See LICENSE for license details. + +#***************************************************************************** +# slli.S +#----------------------------------------------------------------------------- +# +# Test slli instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_IMM_OP( 2, slli, 0x00000001, 0x00000001, 0 ); + TEST_IMM_OP( 3, slli, 0x00000002, 0x00000001, 1 ); + TEST_IMM_OP( 4, slli, 0x00000080, 0x00000001, 7 ); + TEST_IMM_OP( 5, slli, 0x00004000, 0x00000001, 14 ); + TEST_IMM_OP( 6, slli, 0x80000000, 0x00000001, 31 ); + + TEST_IMM_OP( 7, slli, 0xffffffff, 0xffffffff, 0 ); + TEST_IMM_OP( 8, slli, 0xfffffffe, 0xffffffff, 1 ); + TEST_IMM_OP( 9, slli, 0xffffff80, 0xffffffff, 7 ); + TEST_IMM_OP( 10, slli, 0xffffc000, 0xffffffff, 14 ); + TEST_IMM_OP( 11, slli, 0x80000000, 0xffffffff, 31 ); + + TEST_IMM_OP( 12, slli, 0x21212121, 0x21212121, 0 ); + TEST_IMM_OP( 13, slli, 0x42424242, 0x21212121, 1 ); + TEST_IMM_OP( 14, slli, 0x90909080, 0x21212121, 7 ); + TEST_IMM_OP( 15, slli, 0x48484000, 0x21212121, 14 ); + TEST_IMM_OP( 16, slli, 0x80000000, 0x21212121, 31 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_IMM_SRC1_EQ_DEST( 17, slli, 0x00000080, 0x00000001, 7 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_IMM_DEST_BYPASS( 18, 0, slli, 0x00000080, 0x00000001, 7 ); + TEST_IMM_DEST_BYPASS( 19, 1, slli, 0x00004000, 0x00000001, 14 ); + TEST_IMM_DEST_BYPASS( 20, 2, slli, 0x80000000, 0x00000001, 31 ); + + TEST_IMM_SRC1_BYPASS( 21, 0, slli, 0x00000080, 0x00000001, 7 ); + TEST_IMM_SRC1_BYPASS( 22, 1, slli, 0x00004000, 0x00000001, 14 ); + TEST_IMM_SRC1_BYPASS( 23, 2, slli, 0x80000000, 0x00000001, 31 ); + + TEST_IMM_ZEROSRC1( 24, slli, 0, 31 ); + TEST_IMM_ZERODEST( 25, slli, 33, 20 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/slt.S b/tests/slt.S new file mode 100644 index 0000000..3abf82b --- /dev/null +++ b/tests/slt.S @@ -0,0 +1,84 @@ +# See LICENSE for license details. + +#***************************************************************************** +# slt.S +#----------------------------------------------------------------------------- +# +# Test slt instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, slt, 0, 0x00000000, 0x00000000 ); + TEST_RR_OP( 3, slt, 0, 0x00000001, 0x00000001 ); + TEST_RR_OP( 4, slt, 1, 0x00000003, 0x00000007 ); + TEST_RR_OP( 5, slt, 0, 0x00000007, 0x00000003 ); + + TEST_RR_OP( 6, slt, 0, 0x00000000, 0xffff8000 ); + TEST_RR_OP( 7, slt, 1, 0x80000000, 0x00000000 ); + TEST_RR_OP( 8, slt, 1, 0x80000000, 0xffff8000 ); + + TEST_RR_OP( 9, slt, 1, 0x00000000, 0x00007fff ); + TEST_RR_OP( 10, slt, 0, 0x7fffffff, 0x00000000 ); + TEST_RR_OP( 11, slt, 0, 0x7fffffff, 0x00007fff ); + + TEST_RR_OP( 12, slt, 1, 0x80000000, 0x00007fff ); + TEST_RR_OP( 13, slt, 0, 0x7fffffff, 0xffff8000 ); + + TEST_RR_OP( 14, slt, 0, 0x00000000, 0xffffffff ); + TEST_RR_OP( 15, slt, 1, 0xffffffff, 0x00000001 ); + TEST_RR_OP( 16, slt, 0, 0xffffffff, 0xffffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 17, slt, 0, 14, 13 ); + TEST_RR_SRC2_EQ_DEST( 18, slt, 1, 11, 13 ); + TEST_RR_SRC12_EQ_DEST( 19, slt, 0, 13 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 20, 0, slt, 1, 11, 13 ); + TEST_RR_DEST_BYPASS( 21, 1, slt, 0, 14, 13 ); + TEST_RR_DEST_BYPASS( 22, 2, slt, 1, 12, 13 ); + + TEST_RR_SRC12_BYPASS( 23, 0, 0, slt, 0, 14, 13 ); + TEST_RR_SRC12_BYPASS( 24, 0, 1, slt, 1, 11, 13 ); + TEST_RR_SRC12_BYPASS( 25, 0, 2, slt, 0, 15, 13 ); + TEST_RR_SRC12_BYPASS( 26, 1, 0, slt, 1, 10, 13 ); + TEST_RR_SRC12_BYPASS( 27, 1, 1, slt, 0, 16, 13 ); + TEST_RR_SRC12_BYPASS( 28, 2, 0, slt, 1, 9, 13 ); + + TEST_RR_SRC21_BYPASS( 29, 0, 0, slt, 0, 17, 13 ); + TEST_RR_SRC21_BYPASS( 30, 0, 1, slt, 1, 8, 13 ); + TEST_RR_SRC21_BYPASS( 31, 0, 2, slt, 0, 18, 13 ); + TEST_RR_SRC21_BYPASS( 32, 1, 0, slt, 1, 7, 13 ); + TEST_RR_SRC21_BYPASS( 33, 1, 1, slt, 0, 19, 13 ); + TEST_RR_SRC21_BYPASS( 34, 2, 0, slt, 1, 6, 13 ); + + TEST_RR_ZEROSRC1( 35, slt, 0, -1 ); + TEST_RR_ZEROSRC2( 36, slt, 1, -1 ); + TEST_RR_ZEROSRC12( 37, slt, 0 ); + TEST_RR_ZERODEST( 38, slt, 16, 30 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/slti.S b/tests/slti.S new file mode 100644 index 0000000..730cbe2 --- /dev/null +++ b/tests/slti.S @@ -0,0 +1,70 @@ +# See LICENSE for license details. + +#***************************************************************************** +# slti.S +#----------------------------------------------------------------------------- +# +# Test slti instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_IMM_OP( 2, slti, 0, 0x00000000, 0x000 ); + TEST_IMM_OP( 3, slti, 0, 0x00000001, 0x001 ); + TEST_IMM_OP( 4, slti, 1, 0x00000003, 0x007 ); + TEST_IMM_OP( 5, slti, 0, 0x00000007, 0x003 ); + + TEST_IMM_OP( 6, slti, 0, 0x00000000, 0x800 ); + TEST_IMM_OP( 7, slti, 1, 0x80000000, 0x000 ); + TEST_IMM_OP( 8, slti, 1, 0x80000000, 0x800 ); + + TEST_IMM_OP( 9, slti, 1, 0x00000000, 0x7ff ); + TEST_IMM_OP( 10, slti, 0, 0x7fffffff, 0x000 ); + TEST_IMM_OP( 11, slti, 0, 0x7fffffff, 0x7ff ); + + TEST_IMM_OP( 12, slti, 1, 0x80000000, 0x7ff ); + TEST_IMM_OP( 13, slti, 0, 0x7fffffff, 0x800 ); + + TEST_IMM_OP( 14, slti, 0, 0x00000000, 0xfff ); + TEST_IMM_OP( 15, slti, 1, 0xffffffff, 0x001 ); + TEST_IMM_OP( 16, slti, 0, 0xffffffff, 0xfff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_IMM_SRC1_EQ_DEST( 17, sltiu, 1, 11, 13 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_IMM_DEST_BYPASS( 18, 0, slti, 0, 15, 10 ); + TEST_IMM_DEST_BYPASS( 19, 1, slti, 1, 10, 16 ); + TEST_IMM_DEST_BYPASS( 20, 2, slti, 0, 16, 9 ); + + TEST_IMM_SRC1_BYPASS( 21, 0, slti, 1, 11, 15 ); + TEST_IMM_SRC1_BYPASS( 22, 1, slti, 0, 17, 8 ); + TEST_IMM_SRC1_BYPASS( 23, 2, slti, 1, 12, 14 ); + + TEST_IMM_ZEROSRC1( 24, slti, 0, 0xfff ); + TEST_IMM_ZERODEST( 25, slti, 0x00ff00ff, 0xfff ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/sra.S b/tests/sra.S new file mode 100644 index 0000000..8c1de36 --- /dev/null +++ b/tests/sra.S @@ -0,0 +1,90 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sra.S +#----------------------------------------------------------------------------- +# +# Test sra instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, sra, 0x80000000, 0x80000000, 0 ); + TEST_RR_OP( 3, sra, 0xc0000000, 0x80000000, 1 ); + TEST_RR_OP( 4, sra, 0xff000000, 0x80000000, 7 ); + TEST_RR_OP( 5, sra, 0xfffe0000, 0x80000000, 14 ); + TEST_RR_OP( 6, sra, 0xffffffff, 0x80000001, 31 ); + + TEST_RR_OP( 7, sra, 0x7fffffff, 0x7fffffff, 0 ); + TEST_RR_OP( 8, sra, 0x3fffffff, 0x7fffffff, 1 ); + TEST_RR_OP( 9, sra, 0x00ffffff, 0x7fffffff, 7 ); + TEST_RR_OP( 10, sra, 0x0001ffff, 0x7fffffff, 14 ); + TEST_RR_OP( 11, sra, 0x00000000, 0x7fffffff, 31 ); + + TEST_RR_OP( 12, sra, 0x81818181, 0x81818181, 0 ); + TEST_RR_OP( 13, sra, 0xc0c0c0c0, 0x81818181, 1 ); + TEST_RR_OP( 14, sra, 0xff030303, 0x81818181, 7 ); + TEST_RR_OP( 15, sra, 0xfffe0606, 0x81818181, 14 ); + TEST_RR_OP( 16, sra, 0xffffffff, 0x81818181, 31 ); + + # Verify that shifts only use bottom five bits + + TEST_RR_OP( 17, sra, 0x81818181, 0x81818181, 0xffffffc0 ); + TEST_RR_OP( 18, sra, 0xc0c0c0c0, 0x81818181, 0xffffffc1 ); + TEST_RR_OP( 19, sra, 0xff030303, 0x81818181, 0xffffffc7 ); + TEST_RR_OP( 20, sra, 0xfffe0606, 0x81818181, 0xffffffce ); + TEST_RR_OP( 21, sra, 0xffffffff, 0x81818181, 0xffffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 22, sra, 0xff000000, 0x80000000, 7 ); + TEST_RR_SRC2_EQ_DEST( 23, sra, 0xfffe0000, 0x80000000, 14 ); + TEST_RR_SRC12_EQ_DEST( 24, sra, 0, 7 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 25, 0, sra, 0xff000000, 0x80000000, 7 ); + TEST_RR_DEST_BYPASS( 26, 1, sra, 0xfffe0000, 0x80000000, 14 ); + TEST_RR_DEST_BYPASS( 27, 2, sra, 0xffffffff, 0x80000000, 31 ); + + TEST_RR_SRC12_BYPASS( 28, 0, 0, sra, 0xff000000, 0x80000000, 7 ); + TEST_RR_SRC12_BYPASS( 29, 0, 1, sra, 0xfffe0000, 0x80000000, 14 ); + TEST_RR_SRC12_BYPASS( 30, 0, 2, sra, 0xffffffff, 0x80000000, 31 ); + TEST_RR_SRC12_BYPASS( 31, 1, 0, sra, 0xff000000, 0x80000000, 7 ); + TEST_RR_SRC12_BYPASS( 32, 1, 1, sra, 0xfffe0000, 0x80000000, 14 ); + TEST_RR_SRC12_BYPASS( 33, 2, 0, sra, 0xffffffff, 0x80000000, 31 ); + + TEST_RR_SRC21_BYPASS( 34, 0, 0, sra, 0xff000000, 0x80000000, 7 ); + TEST_RR_SRC21_BYPASS( 35, 0, 1, sra, 0xfffe0000, 0x80000000, 14 ); + TEST_RR_SRC21_BYPASS( 36, 0, 2, sra, 0xffffffff, 0x80000000, 31 ); + TEST_RR_SRC21_BYPASS( 37, 1, 0, sra, 0xff000000, 0x80000000, 7 ); + TEST_RR_SRC21_BYPASS( 38, 1, 1, sra, 0xfffe0000, 0x80000000, 14 ); + TEST_RR_SRC21_BYPASS( 39, 2, 0, sra, 0xffffffff, 0x80000000, 31 ); + + TEST_RR_ZEROSRC1( 40, sra, 0, 15 ); + TEST_RR_ZEROSRC2( 41, sra, 32, 32 ); + TEST_RR_ZEROSRC12( 42, sra, 0 ); + TEST_RR_ZERODEST( 43, sra, 1024, 2048 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/srai.S b/tests/srai.S new file mode 100644 index 0000000..4638190 --- /dev/null +++ b/tests/srai.S @@ -0,0 +1,68 @@ +# See LICENSE for license details. + +#***************************************************************************** +# srai.S +#----------------------------------------------------------------------------- +# +# Test srai instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_IMM_OP( 2, srai, 0x00000000, 0x00000000, 0 ); + TEST_IMM_OP( 3, srai, 0xc0000000, 0x80000000, 1 ); + TEST_IMM_OP( 4, srai, 0xff000000, 0x80000000, 7 ); + TEST_IMM_OP( 5, srai, 0xfffe0000, 0x80000000, 14 ); + TEST_IMM_OP( 6, srai, 0xffffffff, 0x80000001, 31 ); + + TEST_IMM_OP( 7, srai, 0x7fffffff, 0x7fffffff, 0 ); + TEST_IMM_OP( 8, srai, 0x3fffffff, 0x7fffffff, 1 ); + TEST_IMM_OP( 9, srai, 0x00ffffff, 0x7fffffff, 7 ); + TEST_IMM_OP( 10, srai, 0x0001ffff, 0x7fffffff, 14 ); + TEST_IMM_OP( 11, srai, 0x00000000, 0x7fffffff, 31 ); + + TEST_IMM_OP( 12, srai, 0x81818181, 0x81818181, 0 ); + TEST_IMM_OP( 13, srai, 0xc0c0c0c0, 0x81818181, 1 ); + TEST_IMM_OP( 14, srai, 0xff030303, 0x81818181, 7 ); + TEST_IMM_OP( 15, srai, 0xfffe0606, 0x81818181, 14 ); + TEST_IMM_OP( 16, srai, 0xffffffff, 0x81818181, 31 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_IMM_SRC1_EQ_DEST( 17, srai, 0xff000000, 0x80000000, 7 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_IMM_DEST_BYPASS( 18, 0, srai, 0xff000000, 0x80000000, 7 ); + TEST_IMM_DEST_BYPASS( 19, 1, srai, 0xfffe0000, 0x80000000, 14 ); + TEST_IMM_DEST_BYPASS( 20, 2, srai, 0xffffffff, 0x80000001, 31 ); + + TEST_IMM_SRC1_BYPASS( 21, 0, srai, 0xff000000, 0x80000000, 7 ); + TEST_IMM_SRC1_BYPASS( 22, 1, srai, 0xfffe0000, 0x80000000, 14 ); + TEST_IMM_SRC1_BYPASS( 23, 2, srai, 0xffffffff, 0x80000001, 31 ); + + TEST_IMM_ZEROSRC1( 24, srai, 0, 31 ); + TEST_IMM_ZERODEST( 25, srai, 33, 20 ); +# + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/srl.S b/tests/srl.S new file mode 100644 index 0000000..35cc4c7 --- /dev/null +++ b/tests/srl.S @@ -0,0 +1,90 @@ +# See LICENSE for license details. + +#***************************************************************************** +# srl.S +#----------------------------------------------------------------------------- +# +# Test srl instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, srl, 0xffff8000, 0xffff8000, 0 ); + TEST_RR_OP( 3, srl, 0x7fffc000, 0xffff8000, 1 ); + TEST_RR_OP( 4, srl, 0x01ffff00, 0xffff8000, 7 ); + TEST_RR_OP( 5, srl, 0x0003fffe, 0xffff8000, 14 ); + TEST_RR_OP( 6, srl, 0x0001ffff, 0xffff8001, 15 ); + + TEST_RR_OP( 7, srl, 0xffffffff, 0xffffffff, 0 ); + TEST_RR_OP( 8, srl, 0x7fffffff, 0xffffffff, 1 ); + TEST_RR_OP( 9, srl, 0x01ffffff, 0xffffffff, 7 ); + TEST_RR_OP( 10, srl, 0x0003ffff, 0xffffffff, 14 ); + TEST_RR_OP( 11, srl, 0x00000001, 0xffffffff, 31 ); + + TEST_RR_OP( 12, srl, 0x21212121, 0x21212121, 0 ); + TEST_RR_OP( 13, srl, 0x10909090, 0x21212121, 1 ); + TEST_RR_OP( 14, srl, 0x00424242, 0x21212121, 7 ); + TEST_RR_OP( 15, srl, 0x00008484, 0x21212121, 14 ); + TEST_RR_OP( 16, srl, 0x00000000, 0x21212121, 31 ); + + # Verify that shifts only use bottom five bits + + TEST_RR_OP( 17, srl, 0x21212121, 0x21212121, 0xffffffe0 ); + TEST_RR_OP( 18, srl, 0x10909090, 0x21212121, 0xffffffe1 ); + TEST_RR_OP( 19, srl, 0x00424242, 0x21212121, 0xffffffe7 ); + TEST_RR_OP( 20, srl, 0x00008484, 0x21212121, 0xffffffee ); + TEST_RR_OP( 21, srl, 0x00000000, 0x21212121, 0xffffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 22, srl, 0x7fffc000, 0xffff8000, 1 ); + TEST_RR_SRC2_EQ_DEST( 23, srl, 0x0003fffe, 0xffff8000, 14 ); + TEST_RR_SRC12_EQ_DEST( 24, srl, 0, 7 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 25, 0, srl, 0x7fffc000, 0xffff8000, 1 ); + TEST_RR_DEST_BYPASS( 26, 1, srl, 0x0003fffe, 0xffff8000, 14 ); + TEST_RR_DEST_BYPASS( 27, 2, srl, 0x0001ffff, 0xffff8000, 15 ); + + TEST_RR_SRC12_BYPASS( 28, 0, 0, srl, 0x7fffc000, 0xffff8000, 1 ); + TEST_RR_SRC12_BYPASS( 29, 0, 1, srl, 0x01ffff00, 0xffff8000, 7 ); + TEST_RR_SRC12_BYPASS( 30, 0, 2, srl, 0x0001ffff, 0xffff8000, 15 ); + TEST_RR_SRC12_BYPASS( 31, 1, 0, srl, 0x7fffc000, 0xffff8000, 1 ); + TEST_RR_SRC12_BYPASS( 32, 1, 1, srl, 0x01ffff00, 0xffff8000, 7 ); + TEST_RR_SRC12_BYPASS( 33, 2, 0, srl, 0x0001ffff, 0xffff8000, 15 ); + + TEST_RR_SRC21_BYPASS( 34, 0, 0, srl, 0x7fffc000, 0xffff8000, 1 ); + TEST_RR_SRC21_BYPASS( 35, 0, 1, srl, 0x01ffff00, 0xffff8000, 7 ); + TEST_RR_SRC21_BYPASS( 36, 0, 2, srl, 0x0001ffff, 0xffff8000, 15 ); + TEST_RR_SRC21_BYPASS( 37, 1, 0, srl, 0x7fffc000, 0xffff8000, 1 ); + TEST_RR_SRC21_BYPASS( 38, 1, 1, srl, 0x01ffff00, 0xffff8000, 7 ); + TEST_RR_SRC21_BYPASS( 39, 2, 0, srl, 0x0001ffff, 0xffff8000, 15 ); + + TEST_RR_ZEROSRC1( 40, srl, 0, 15 ); + TEST_RR_ZEROSRC2( 41, srl, 32, 32 ); + TEST_RR_ZEROSRC12( 42, srl, 0 ); + TEST_RR_ZERODEST( 43, srl, 1024, 2048 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/srli.S b/tests/srli.S new file mode 100644 index 0000000..be56dc3 --- /dev/null +++ b/tests/srli.S @@ -0,0 +1,69 @@ +# See LICENSE for license details. + +#***************************************************************************** +# srli.S +#----------------------------------------------------------------------------- +# +# Test srli instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_IMM_OP( 2, srli, 0xffff8000, 0xffff8000, 0 ); + TEST_IMM_OP( 3, srli, 0x7fffc000, 0xffff8000, 1 ); + TEST_IMM_OP( 4, srli, 0x01ffff00, 0xffff8000, 7 ); + TEST_IMM_OP( 5, srli, 0x0003fffe, 0xffff8000, 14 ); + TEST_IMM_OP( 6, srli, 0x0001ffff, 0xffff8001, 15 ); + + TEST_IMM_OP( 7, srli, 0xffffffff, 0xffffffff, 0 ); + TEST_IMM_OP( 8, srli, 0x7fffffff, 0xffffffff, 1 ); + TEST_IMM_OP( 9, srli, 0x01ffffff, 0xffffffff, 7 ); + TEST_IMM_OP( 10, srli, 0x0003ffff, 0xffffffff, 14 ); + TEST_IMM_OP( 11, srli, 0x00000001, 0xffffffff, 31 ); + + TEST_IMM_OP( 12, srli, 0x21212121, 0x21212121, 0 ); + TEST_IMM_OP( 13, srli, 0x10909090, 0x21212121, 1 ); + TEST_IMM_OP( 14, srli, 0x00424242, 0x21212121, 7 ); + TEST_IMM_OP( 15, srli, 0x00008484, 0x21212121, 14 ); + TEST_IMM_OP( 16, srli, 0x00000000, 0x21212121, 31 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_IMM_SRC1_EQ_DEST( 21, srli, 0x7fffc000, 0xffff8000, 1 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_IMM_DEST_BYPASS( 22, 0, srl, 0x7fffc000, 0xffff8000, 1 ); + TEST_IMM_DEST_BYPASS( 23, 1, srl, 0x0003fffe, 0xffff8000, 14 ); + TEST_IMM_DEST_BYPASS( 24, 2, srl, 0x0001ffff, 0xffff8000, 15 ); + + TEST_IMM_SRC1_BYPASS( 25, 0, srl, 0x7fffc000, 0xffff8000, 1 ); + TEST_IMM_SRC1_BYPASS( 26, 1, srl, 0x0003fffe, 0xffff8000, 14 ); + TEST_IMM_SRC1_BYPASS( 27, 2, srl, 0x0001ffff, 0xffff8000, 15 ); + + + TEST_IMM_ZEROSRC1( 28, srli, 0, 31 ); + TEST_IMM_ZERODEST( 29, srli, 33, 20 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/sub.S b/tests/sub.S new file mode 100644 index 0000000..ad56e3e --- /dev/null +++ b/tests/sub.S @@ -0,0 +1,83 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sub.S +#----------------------------------------------------------------------------- +# +# Test sub instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, sub, 0x00000000, 0x00000000, 0x00000000 ); + TEST_RR_OP( 3, sub, 0x00000000, 0x00000001, 0x00000001 ); + TEST_RR_OP( 4, sub, 0xfffffffc, 0x00000003, 0x00000007 ); + + TEST_RR_OP( 5, sub, 0x00008000, 0x00000000, 0xffff8000 ); + TEST_RR_OP( 6, sub, 0x80000000, 0x80000000, 0x00000000 ); + TEST_RR_OP( 7, sub, 0x80008000, 0x80000000, 0xffff8000 ); + + TEST_RR_OP( 8, sub, 0xffff8001, 0x00000000, 0x00007fff ); + TEST_RR_OP( 9, sub, 0x7fffffff, 0x7fffffff, 0x00000000 ); + TEST_RR_OP( 10, sub, 0x7fff8000, 0x7fffffff, 0x00007fff ); + + TEST_RR_OP( 11, sub, 0x7fff8001, 0x80000000, 0x00007fff ); + TEST_RR_OP( 12, sub, 0x80007fff, 0x7fffffff, 0xffff8000 ); + + TEST_RR_OP( 13, sub, 0x00000001, 0x00000000, 0xffffffff ); + TEST_RR_OP( 14, sub, 0xfffffffe, 0xffffffff, 0x00000001 ); + TEST_RR_OP( 15, sub, 0x00000000, 0xffffffff, 0xffffffff ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 16, sub, 2, 13, 11 ); + TEST_RR_SRC2_EQ_DEST( 17, sub, 3, 14, 11 ); + TEST_RR_SRC12_EQ_DEST( 18, sub, 0, 13 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 19, 0, sub, 2, 13, 11 ); + TEST_RR_DEST_BYPASS( 20, 1, sub, 3, 14, 11 ); + TEST_RR_DEST_BYPASS( 21, 2, sub, 4, 15, 11 ); + + TEST_RR_SRC12_BYPASS( 22, 0, 0, sub, 2, 13, 11 ); + TEST_RR_SRC12_BYPASS( 23, 0, 1, sub, 3, 14, 11 ); + TEST_RR_SRC12_BYPASS( 24, 0, 2, sub, 4, 15, 11 ); + TEST_RR_SRC12_BYPASS( 25, 1, 0, sub, 2, 13, 11 ); + TEST_RR_SRC12_BYPASS( 26, 1, 1, sub, 3, 14, 11 ); + TEST_RR_SRC12_BYPASS( 27, 2, 0, sub, 4, 15, 11 ); + + TEST_RR_SRC21_BYPASS( 28, 0, 0, sub, 2, 13, 11 ); + TEST_RR_SRC21_BYPASS( 29, 0, 1, sub, 3, 14, 11 ); + TEST_RR_SRC21_BYPASS( 30, 0, 2, sub, 4, 15, 11 ); + TEST_RR_SRC21_BYPASS( 31, 1, 0, sub, 2, 13, 11 ); + TEST_RR_SRC21_BYPASS( 32, 1, 1, sub, 3, 14, 11 ); + TEST_RR_SRC21_BYPASS( 33, 2, 0, sub, 4, 15, 11 ); + + TEST_RR_ZEROSRC1( 34, sub, 15, -15 ); + TEST_RR_ZEROSRC2( 35, sub, 32, 32 ); + TEST_RR_ZEROSRC12( 36, sub, 0 ); + TEST_RR_ZERODEST( 37, sub, 16, 30 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/sw.S b/tests/sw.S new file mode 100644 index 0000000..fbf76cc --- /dev/null +++ b/tests/sw.S @@ -0,0 +1,92 @@ +# See LICENSE for license details. + +#***************************************************************************** +# sw.S +#----------------------------------------------------------------------------- +# +# Test sw instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Basic tests + #------------------------------------------------------------- + + TEST_ST_OP( 2, lw, sw, 0x00aa00aa, 0, tdat ); + TEST_ST_OP( 3, lw, sw, 0xaa00aa00, 4, tdat ); + TEST_ST_OP( 4, lw, sw, 0x0aa00aa0, 8, tdat ); + TEST_ST_OP( 5, lw, sw, 0xa00aa00a, 12, tdat ); + + # Test with negative offset + + TEST_ST_OP( 6, lw, sw, 0x00aa00aa, -12, tdat8 ); + TEST_ST_OP( 7, lw, sw, 0xaa00aa00, -8, tdat8 ); + TEST_ST_OP( 8, lw, sw, 0x0aa00aa0, -4, tdat8 ); + TEST_ST_OP( 9, lw, sw, 0xa00aa00a, 0, tdat8 ); + + # Test with a negative base + + TEST_CASE( 10, x3, 0x12345678, \ + la x1, tdat9; \ + li x2, 0x12345678; \ + addi x4, x1, -32; \ + sw x2, 32(x4); \ + lw x3, 0(x1); \ + ) + + # Test with unaligned base + + TEST_CASE( 11, x3, 0x58213098, \ + la x1, tdat9; \ + li x2, 0x58213098; \ + addi x1, x1, -3; \ + sw x2, 7(x1); \ + la x4, tdat10; \ + lw x3, 0(x4); \ + ) + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_ST_SRC12_BYPASS( 12, 0, 0, lw, sw, 0xaabbccdd, 0, tdat ); + TEST_ST_SRC12_BYPASS( 13, 0, 1, lw, sw, 0xdaabbccd, 4, tdat ); + TEST_ST_SRC12_BYPASS( 14, 0, 2, lw, sw, 0xddaabbcc, 8, tdat ); + TEST_ST_SRC12_BYPASS( 15, 1, 0, lw, sw, 0xcddaabbc, 12, tdat ); + TEST_ST_SRC12_BYPASS( 16, 1, 1, lw, sw, 0xccddaabb, 16, tdat ); + TEST_ST_SRC12_BYPASS( 17, 2, 0, lw, sw, 0xbccddaab, 20, tdat ); + + TEST_ST_SRC21_BYPASS( 18, 0, 0, lw, sw, 0x00112233, 0, tdat ); + TEST_ST_SRC21_BYPASS( 19, 0, 1, lw, sw, 0x30011223, 4, tdat ); + TEST_ST_SRC21_BYPASS( 20, 0, 2, lw, sw, 0x33001122, 8, tdat ); + TEST_ST_SRC21_BYPASS( 21, 1, 0, lw, sw, 0x23300112, 12, tdat ); + TEST_ST_SRC21_BYPASS( 22, 1, 1, lw, sw, 0x22330011, 16, tdat ); + TEST_ST_SRC21_BYPASS( 23, 2, 0, lw, sw, 0x12233001, 20, tdat ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +tdat: +tdat1: .word 0xdeadbeef +tdat2: .word 0xdeadbeef +tdat3: .word 0xdeadbeef +tdat4: .word 0xdeadbeef +tdat5: .word 0xdeadbeef +tdat6: .word 0xdeadbeef +tdat7: .word 0xdeadbeef +tdat8: .word 0xdeadbeef +tdat9: .word 0xdeadbeef +tdat10: .word 0xdeadbeef + +RVTEST_DATA_END diff --git a/tests/test_macros.h b/tests/test_macros.h new file mode 100644 index 0000000..05ed7c8 --- /dev/null +++ b/tests/test_macros.h @@ -0,0 +1,585 @@ +// See LICENSE for license details. + +#ifndef __TEST_MACROS_SCALAR_H +#define __TEST_MACROS_SCALAR_H + + +#----------------------------------------------------------------------- +# Helper macros +#----------------------------------------------------------------------- + +#define TEST_CASE( testnum, testreg, correctval, code... ) \ +test_ ## testnum: \ + code; \ + li x29, correctval; \ + li TESTNUM, testnum; \ + bne testreg, x29, fail; + +# We use a macro hack to simpify code generation for various numbers +# of bubble cycles. + +#define TEST_INSERT_NOPS_0 +#define TEST_INSERT_NOPS_1 nop; TEST_INSERT_NOPS_0 +#define TEST_INSERT_NOPS_2 nop; TEST_INSERT_NOPS_1 +#define TEST_INSERT_NOPS_3 nop; TEST_INSERT_NOPS_2 +#define TEST_INSERT_NOPS_4 nop; TEST_INSERT_NOPS_3 +#define TEST_INSERT_NOPS_5 nop; TEST_INSERT_NOPS_4 +#define TEST_INSERT_NOPS_6 nop; TEST_INSERT_NOPS_5 +#define TEST_INSERT_NOPS_7 nop; TEST_INSERT_NOPS_6 +#define TEST_INSERT_NOPS_8 nop; TEST_INSERT_NOPS_7 +#define TEST_INSERT_NOPS_9 nop; TEST_INSERT_NOPS_8 +#define TEST_INSERT_NOPS_10 nop; TEST_INSERT_NOPS_9 + + +#----------------------------------------------------------------------- +# RV64UI MACROS +#----------------------------------------------------------------------- + +#----------------------------------------------------------------------- +# Tests for instructions with immediate operand +#----------------------------------------------------------------------- + +#define SEXT_IMM(x) ((x) | (-(((x) >> 11) & 1) << 11)) + +#define TEST_IMM_OP( testnum, inst, result, val1, imm ) \ + TEST_CASE( testnum, x3, result, \ + li x1, val1; \ + inst x3, x1, SEXT_IMM(imm); \ + ) + +#define TEST_IMM_SRC1_EQ_DEST( testnum, inst, result, val1, imm ) \ + TEST_CASE( testnum, x1, result, \ + li x1, val1; \ + inst x1, x1, SEXT_IMM(imm); \ + ) + +#define TEST_IMM_DEST_BYPASS( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x6, result, \ + li x4, 0; \ +1: li x1, val1; \ + inst x3, x1, SEXT_IMM(imm); \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x3, 0; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_IMM_SRC1_BYPASS( testnum, nop_cycles, inst, result, val1, imm ) \ + TEST_CASE( testnum, x3, result, \ + li x4, 0; \ +1: li x1, val1; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x3, x1, SEXT_IMM(imm); \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_IMM_ZEROSRC1( testnum, inst, result, imm ) \ + TEST_CASE( testnum, x1, result, \ + inst x1, x0, SEXT_IMM(imm); \ + ) + +#define TEST_IMM_ZERODEST( testnum, inst, val1, imm ) \ + TEST_CASE( testnum, x0, 0, \ + li x1, val1; \ + inst x0, x1, SEXT_IMM(imm); \ + ) + +#----------------------------------------------------------------------- +# Tests for vector config instructions +#----------------------------------------------------------------------- + +#define TEST_VSETCFGIVL( testnum, nxpr, nfpr, bank, vl, result ) \ + TEST_CASE( testnum, x1, result, \ + li x1, (bank << 12); \ + vsetcfg x1,nxpr,nfpr; \ + li x1, vl; \ + vsetvl x1,x1; \ + ) + +#define TEST_VVCFG( testnum, nxpr, nfpr, bank, vl, result ) \ + TEST_CASE( testnum, x1, result, \ + li x1, (bank << 12) | (nfpr << 6) | nxpr; \ + vsetcfg x1; \ + li x1, vl; \ + vsetvl x1,x1; \ + ) + +#define TEST_VSETVL( testnum, nxpr, nfpr, bank, vl, result ) \ + TEST_CASE( testnum, x1, result, \ + li x1, (bank << 12); \ + vsetcfg x1,nxpr,nfpr; \ + li x1, vl; \ + vsetvl x1, x1; \ + ) + +#----------------------------------------------------------------------- +# Tests for an instruction with register operands +#----------------------------------------------------------------------- + +#define TEST_R_OP( testnum, inst, result, val1 ) \ + TEST_CASE( testnum, x3, result, \ + li x1, val1; \ + inst x3, x1; \ + ) + +#define TEST_R_SRC1_EQ_DEST( testnum, inst, result, val1 ) \ + TEST_CASE( testnum, x1, result, \ + li x1, val1; \ + inst x1, x1; \ + ) + +#define TEST_R_DEST_BYPASS( testnum, nop_cycles, inst, result, val1 ) \ + TEST_CASE( testnum, x6, result, \ + li x4, 0; \ +1: li x1, val1; \ + inst x3, x1; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x3, 0; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#----------------------------------------------------------------------- +# Tests for an instruction with register-register operands +#----------------------------------------------------------------------- + +#define TEST_RR_OP( testnum, inst, result, val1, val2 ) \ + TEST_CASE( testnum, x3, result, \ + li x1, val1; \ + li x2, val2; \ + inst x3, x1, x2; \ + ) + +#define TEST_RR_SRC1_EQ_DEST( testnum, inst, result, val1, val2 ) \ + TEST_CASE( testnum, x1, result, \ + li x1, val1; \ + li x2, val2; \ + inst x1, x1, x2; \ + ) + +#define TEST_RR_SRC2_EQ_DEST( testnum, inst, result, val1, val2 ) \ + TEST_CASE( testnum, x2, result, \ + li x1, val1; \ + li x2, val2; \ + inst x2, x1, x2; \ + ) + +#define TEST_RR_SRC12_EQ_DEST( testnum, inst, result, val1 ) \ + TEST_CASE( testnum, x1, result, \ + li x1, val1; \ + inst x1, x1, x1; \ + ) + +#define TEST_RR_DEST_BYPASS( testnum, nop_cycles, inst, result, val1, val2 ) \ + TEST_CASE( testnum, x6, result, \ + li x4, 0; \ +1: li x1, val1; \ + li x2, val2; \ + inst x3, x1, x2; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x3, 0; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_RR_SRC12_BYPASS( testnum, src1_nops, src2_nops, inst, result, val1, val2 ) \ + TEST_CASE( testnum, x3, result, \ + li x4, 0; \ +1: li x1, val1; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x2, val2; \ + TEST_INSERT_NOPS_ ## src2_nops \ + inst x3, x1, x2; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_RR_SRC21_BYPASS( testnum, src1_nops, src2_nops, inst, result, val1, val2 ) \ + TEST_CASE( testnum, x3, result, \ + li x4, 0; \ +1: li x2, val2; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x1, val1; \ + TEST_INSERT_NOPS_ ## src2_nops \ + inst x3, x1, x2; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + ) + +#define TEST_RR_ZEROSRC1( testnum, inst, result, val ) \ + TEST_CASE( testnum, x2, result, \ + li x1, val; \ + inst x2, x0, x1; \ + ) + +#define TEST_RR_ZEROSRC2( testnum, inst, result, val ) \ + TEST_CASE( testnum, x2, result, \ + li x1, val; \ + inst x2, x1, x0; \ + ) + +#define TEST_RR_ZEROSRC12( testnum, inst, result ) \ + TEST_CASE( testnum, x1, result, \ + inst x1, x0, x0; \ + ) + +#define TEST_RR_ZERODEST( testnum, inst, val1, val2 ) \ + TEST_CASE( testnum, x0, 0, \ + li x1, val1; \ + li x2, val2; \ + inst x0, x1, x2; \ + ) + +#----------------------------------------------------------------------- +# Test memory instructions +#----------------------------------------------------------------------- + +#define TEST_LD_OP( testnum, inst, result, offset, base ) \ + TEST_CASE( testnum, x3, result, \ + la x1, base; \ + inst x3, offset(x1); \ + ) + +#define TEST_ST_OP( testnum, load_inst, store_inst, result, offset, base ) \ + TEST_CASE( testnum, x3, result, \ + la x1, base; \ + li x2, result; \ + store_inst x2, offset(x1); \ + load_inst x3, offset(x1); \ + ) + +#define TEST_LD_DEST_BYPASS( testnum, nop_cycles, inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x1, base; \ + inst x3, offset(x1); \ + TEST_INSERT_NOPS_ ## nop_cycles \ + addi x6, x3, 0; \ + li x29, result; \ + bne x6, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b; \ + +#define TEST_LD_SRC1_BYPASS( testnum, nop_cycles, inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x1, base; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x3, offset(x1); \ + li x29, result; \ + bne x3, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_ST_SRC12_BYPASS( testnum, src1_nops, src2_nops, load_inst, store_inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: li x1, result; \ + TEST_INSERT_NOPS_ ## src1_nops \ + la x2, base; \ + TEST_INSERT_NOPS_ ## src2_nops \ + store_inst x1, offset(x2); \ + load_inst x3, offset(x2); \ + li x29, result; \ + bne x3, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_ST_SRC21_BYPASS( testnum, src1_nops, src2_nops, load_inst, store_inst, result, offset, base ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x2, base; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x1, result; \ + TEST_INSERT_NOPS_ ## src2_nops \ + store_inst x1, offset(x2); \ + load_inst x3, offset(x2); \ + li x29, result; \ + bne x3, x29, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#----------------------------------------------------------------------- +# Test branch instructions +#----------------------------------------------------------------------- + +#define TEST_BR1_OP_TAKEN( testnum, inst, val1 ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x1, val1; \ + inst x1, 2f; \ + bne x0, TESTNUM, fail; \ +1: bne x0, TESTNUM, 3f; \ +2: inst x1, 1b; \ + bne x0, TESTNUM, fail; \ +3: + +#define TEST_BR1_OP_NOTTAKEN( testnum, inst, val1 ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x1, val1; \ + inst x1, 1f; \ + bne x0, TESTNUM, 2f; \ +1: bne x0, TESTNUM, fail; \ +2: inst x1, 1b; \ +3: + +#define TEST_BR1_SRC1_BYPASS( testnum, nop_cycles, inst, val1 ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: li x1, val1; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x1, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_BR2_OP_TAKEN( testnum, inst, val1, val2 ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x1, val1; \ + li x2, val2; \ + inst x1, x2, 2f; \ + bne x0, TESTNUM, fail; \ +1: bne x0, TESTNUM, 3f; \ +2: inst x1, x2, 1b; \ + bne x0, TESTNUM, fail; \ +3: + +#define TEST_BR2_OP_NOTTAKEN( testnum, inst, val1, val2 ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x1, val1; \ + li x2, val2; \ + inst x1, x2, 1f; \ + bne x0, TESTNUM, 2f; \ +1: bne x0, TESTNUM, fail; \ +2: inst x1, x2, 1b; \ +3: + +#define TEST_BR2_SRC12_BYPASS( testnum, src1_nops, src2_nops, inst, val1, val2 ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: li x1, val1; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x2, val2; \ + TEST_INSERT_NOPS_ ## src2_nops \ + inst x1, x2, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_BR2_SRC21_BYPASS( testnum, src1_nops, src2_nops, inst, val1, val2 ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: li x2, val2; \ + TEST_INSERT_NOPS_ ## src1_nops \ + li x1, val1; \ + TEST_INSERT_NOPS_ ## src2_nops \ + inst x1, x2, fail; \ + addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#----------------------------------------------------------------------- +# Test jump instructions +#----------------------------------------------------------------------- + +#define TEST_JR_SRC1_BYPASS( testnum, nop_cycles, inst ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x6, 2f; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x6; \ + bne x0, TESTNUM, fail; \ +2: addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + +#define TEST_JALR_SRC1_BYPASS( testnum, nop_cycles, inst ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + li x4, 0; \ +1: la x6, 2f; \ + TEST_INSERT_NOPS_ ## nop_cycles \ + inst x19, x6, 0; \ + bne x0, TESTNUM, fail; \ +2: addi x4, x4, 1; \ + li x5, 2; \ + bne x4, x5, 1b \ + + +#----------------------------------------------------------------------- +# RV64UF MACROS +#----------------------------------------------------------------------- + +#----------------------------------------------------------------------- +# Tests floating-point instructions +#----------------------------------------------------------------------- + +#define TEST_FP_OP_S_INTERNAL( testnum, flags, result, val1, val2, val3, code... ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + la a0, test_ ## testnum ## _data ;\ + flw f0, 0(a0); \ + flw f1, 4(a0); \ + flw f2, 8(a0); \ + lw a3, 12(a0); \ + code; \ + fsflags a1, x0; \ + li a2, flags; \ + bne a0, a3, fail; \ + bne a1, a2, fail; \ + j 2f; \ + .align 2; \ + .data; \ + test_ ## testnum ## _data: \ + .float val1; \ + .float val2; \ + .float val3; \ + .result; \ + .text; \ +2: + +#define TEST_FP_OP_D_INTERNAL( testnum, flags, result, val1, val2, val3, code... ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + la a0, test_ ## testnum ## _data ;\ + fld f0, 0(a0); \ + fld f1, 8(a0); \ + fld f2, 16(a0); \ + ld a3, 24(a0); \ + code; \ + fsflags a1, x0; \ + li a2, flags; \ + bne a0, a3, fail; \ + bne a1, a2, fail; \ + j 2f; \ + .data; \ + .align 3; \ + test_ ## testnum ## _data: \ + .double val1; \ + .double val2; \ + .double val3; \ + .result; \ + .text; \ +2: + +#define TEST_FCVT_S_D( testnum, result, val1 ) \ + TEST_FP_OP_D_INTERNAL( testnum, 0, double result, val1, 0.0, 0.0, \ + fcvt.s.d f3, f0; fcvt.d.s f3, f3; fmv.x.d a0, f3) + +#define TEST_FCVT_D_S( testnum, result, val1 ) \ + TEST_FP_OP_S_INTERNAL( testnum, 0, float result, val1, 0.0, 0.0, \ + fcvt.d.s f3, f0; fcvt.s.d f3, f3; fmv.x.s a0, f3) + +#define TEST_FP_OP1_S( testnum, inst, flags, result, val1 ) \ + TEST_FP_OP_S_INTERNAL( testnum, flags, float result, val1, 0.0, 0.0, \ + inst f3, f0; fmv.x.s a0, f3) + +#define TEST_FP_OP1_D( testnum, inst, flags, result, val1 ) \ + TEST_FP_OP_D_INTERNAL( testnum, flags, double result, val1, 0.0, 0.0, \ + inst f3, f0; fmv.x.d a0, f3) + +#define TEST_FP_OP2_S( testnum, inst, flags, result, val1, val2 ) \ + TEST_FP_OP_S_INTERNAL( testnum, flags, float result, val1, val2, 0.0, \ + inst f3, f0, f1; fmv.x.s a0, f3) + +#define TEST_FP_OP2_D( testnum, inst, flags, result, val1, val2 ) \ + TEST_FP_OP_D_INTERNAL( testnum, flags, double result, val1, val2, 0.0, \ + inst f3, f0, f1; fmv.x.d a0, f3) + +#define TEST_FP_OP3_S( testnum, inst, flags, result, val1, val2, val3 ) \ + TEST_FP_OP_S_INTERNAL( testnum, flags, float result, val1, val2, val3, \ + inst f3, f0, f1, f2; fmv.x.s a0, f3) + +#define TEST_FP_OP3_D( testnum, inst, flags, result, val1, val2, val3 ) \ + TEST_FP_OP_D_INTERNAL( testnum, flags, double result, val1, val2, val3, \ + inst f3, f0, f1, f2; fmv.x.d a0, f3) + +#define TEST_FP_INT_OP_S( testnum, inst, flags, result, val1, rm ) \ + TEST_FP_OP_S_INTERNAL( testnum, flags, word result, val1, 0.0, 0.0, \ + inst a0, f0, rm) + +#define TEST_FP_INT_OP_D( testnum, inst, flags, result, val1, rm ) \ + TEST_FP_OP_D_INTERNAL( testnum, flags, dword result, val1, 0.0, 0.0, \ + inst a0, f0, rm) + +#define TEST_FP_CMP_OP_S( testnum, inst, result, val1, val2 ) \ + TEST_FP_OP_S_INTERNAL( testnum, 0, word result, val1, val2, 0.0, \ + inst a0, f0, f1) + +#define TEST_FP_CMP_OP_D( testnum, inst, result, val1, val2 ) \ + TEST_FP_OP_D_INTERNAL( testnum, 0, dword result, val1, val2, 0.0, \ + inst a0, f0, f1) + +#define TEST_INT_FP_OP_S( testnum, inst, result, val1 ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + la a0, test_ ## testnum ## _data ;\ + lw a3, 0(a0); \ + li a0, val1; \ + inst f0, a0; \ + fsflags x0; \ + fmv.x.s a0, f0; \ + bne a0, a3, fail; \ + j 1f; \ + .align 2; \ + test_ ## testnum ## _data: \ + .float result; \ +1: + +#define TEST_INT_FP_OP_D( testnum, inst, result, val1 ) \ +test_ ## testnum: \ + li TESTNUM, testnum; \ + la a0, test_ ## testnum ## _data ;\ + ld a3, 0(a0); \ + li a0, val1; \ + inst f0, a0; \ + fsflags x0; \ + fmv.x.d a0, f0; \ + bne a0, a3, fail; \ + j 1f; \ + .align 3; \ + test_ ## testnum ## _data: \ + .double result; \ +1: + +#----------------------------------------------------------------------- +# Pass and fail code (assumes test num is in TESTNUM) +#----------------------------------------------------------------------- + +#define TEST_PASSFAIL \ + bne x0, TESTNUM, pass; \ +fail: \ + RVTEST_FAIL; \ +pass: \ + RVTEST_PASS \ + + +#----------------------------------------------------------------------- +# Test data section +#----------------------------------------------------------------------- + +#define TEST_DATA + +#endif diff --git a/tests/xor.S b/tests/xor.S new file mode 100644 index 0000000..72875fe --- /dev/null +++ b/tests/xor.S @@ -0,0 +1,69 @@ +# See LICENSE for license details. + +#***************************************************************************** +# xor.S +#----------------------------------------------------------------------------- +# +# Test xor instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_OP( 3, xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_OP( 4, xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RR_OP( 5, xor, 0x00ff00ff, 0xf00ff00f, 0xf0f0f0f0 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_RR_SRC1_EQ_DEST( 6, xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC2_EQ_DEST( 7, xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC12_EQ_DEST( 8, xor, 0x00000000, 0xff00ff00 ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_RR_DEST_BYPASS( 9, 0, xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_DEST_BYPASS( 10, 1, xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_DEST_BYPASS( 11, 2, xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RR_SRC12_BYPASS( 12, 0, 0, xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC12_BYPASS( 13, 0, 1, xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC12_BYPASS( 14, 0, 2, xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RR_SRC12_BYPASS( 15, 1, 0, xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC12_BYPASS( 16, 1, 1, xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC12_BYPASS( 17, 2, 0, xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RR_SRC21_BYPASS( 18, 0, 0, xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC21_BYPASS( 19, 0, 1, xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC21_BYPASS( 20, 0, 2, xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + TEST_RR_SRC21_BYPASS( 21, 1, 0, xor, 0xf00ff00f, 0xff00ff00, 0x0f0f0f0f ); + TEST_RR_SRC21_BYPASS( 22, 1, 1, xor, 0xff00ff00, 0x0ff00ff0, 0xf0f0f0f0 ); + TEST_RR_SRC21_BYPASS( 23, 2, 0, xor, 0x0ff00ff0, 0x00ff00ff, 0x0f0f0f0f ); + + TEST_RR_ZEROSRC1( 24, xor, 0xff00ff00, 0xff00ff00 ); + TEST_RR_ZEROSRC2( 25, xor, 0x00ff00ff, 0x00ff00ff ); + TEST_RR_ZEROSRC12( 26, xor, 0 ); + TEST_RR_ZERODEST( 27, xor, 0x11111111, 0x22222222 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/xori.S b/tests/xori.S new file mode 100644 index 0000000..7f2207c --- /dev/null +++ b/tests/xori.S @@ -0,0 +1,55 @@ +# See LICENSE for license details. + +#***************************************************************************** +# xori.S +#----------------------------------------------------------------------------- +# +# Test xori instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Logical tests + #------------------------------------------------------------- + + TEST_IMM_OP( 2, xori, 0xff00f00f, 0x00ff0f00, 0xf0f ); + TEST_IMM_OP( 3, xori, 0x0ff00f00, 0x0ff00ff0, 0x0f0 ); + TEST_IMM_OP( 4, xori, 0x00ff0ff0, 0x00ff08ff, 0x70f ); + TEST_IMM_OP( 5, xori, 0xf00ff0ff, 0xf00ff00f, 0x0f0 ); + + #------------------------------------------------------------- + # Source/Destination tests + #------------------------------------------------------------- + + TEST_IMM_SRC1_EQ_DEST( 6, xori, 0xff00f00f, 0xff00f700, 0x70f ); + + #------------------------------------------------------------- + # Bypassing tests + #------------------------------------------------------------- + + TEST_IMM_DEST_BYPASS( 7, 0, xori, 0x0ff00f00, 0x0ff00ff0, 0x0f0 ); + TEST_IMM_DEST_BYPASS( 8, 1, xori, 0x00ff0ff0, 0x00ff08ff, 0x70f ); + TEST_IMM_DEST_BYPASS( 9, 2, xori, 0xf00ff0ff, 0xf00ff00f, 0x0f0 ); + + TEST_IMM_SRC1_BYPASS( 10, 0, xori, 0x0ff00f00, 0x0ff00ff0, 0x0f0 ); + TEST_IMM_SRC1_BYPASS( 11, 1, xori, 0x00ff0ff0, 0x00ff0fff, 0x00f ); + TEST_IMM_SRC1_BYPASS( 12, 2, xori, 0xf00ff0ff, 0xf00ff00f, 0x0f0 ); + + TEST_IMM_ZEROSRC1( 13, xori, 0x0f0, 0x0f0 ); + TEST_IMM_ZERODEST( 14, xori, 0x00ff00ff, 0x70f ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END -- cgit