| 1 |
69 |
zero_gravi |
<<<
|
| 2 |
|
|
:sectnums:
|
| 3 |
|
|
== Simulating the Processor
|
| 4 |
|
|
|
| 5 |
|
|
The NEORV32 project includes a core CPU, built-in peripherals in the Processor Subsystem, and additional peripherals in
|
| 6 |
|
|
the templates and examples.
|
| 7 |
|
|
Therefore, there is a wide range of possible testing and verification strategies.
|
| 8 |
|
|
|
| 9 |
|
|
On the one hand, a simple smoke testbench allows ensuring that functionality is correct from a software point of view.
|
| 10 |
|
|
That is used for running the RISC-V architecture tests, in order to guarantee compliance with the ISA specification(s).
|
| 11 |
|
|
|
| 12 |
|
|
On the other hand, http://vunit.github.io/[VUnit] and http://vunit.github.io/verification_components/user_guide.html[Verification Components]
|
| 13 |
|
|
are used for verifying the functionality of the various peripherals from a hardware point of view.
|
| 14 |
|
|
|
| 15 |
|
|
[TIP]
|
| 16 |
|
|
The processor can check if it is being simulated by checking the SYSINFO _SYSINFO_SOC_IS_SIM_ flag
|
| 17 |
|
|
(see https://stnolting.github.io/neorv32/#_system_configuration_information_memory_sysinfo).
|
| 18 |
|
|
Note that this flag is not guaranteed to be set correctly (depending on the HDL toolchain's pragma support).
|
| 19 |
|
|
|
| 20 |
|
|
:sectnums:
|
| 21 |
|
|
=== Testbench
|
| 22 |
|
|
|
| 23 |
|
|
A plain-VHDL (no third-party libraries) testbench (`sim/simple/neorv32_tb.simple.vhd`) can be used for simulating and
|
| 24 |
|
|
testing the processor.
|
| 25 |
|
|
This testbench features a 100MHz clock and enables all optional peripheral and CPU extensions except for the `E`
|
| 26 |
|
|
extension and the TRNG IO module (that CANNOT be simulated due to its combinatorial (looped) architecture).
|
| 27 |
|
|
|
| 28 |
|
|
The simulation setup is configured via the "User Configuration" section located right at the beginning of
|
| 29 |
|
|
the testbench's architecture. Each configuration constant provides comments to explain the functionality.
|
| 30 |
|
|
|
| 31 |
|
|
Besides the actual NEORV32 Processor, the testbench also simulates "external" components that are connected
|
| 32 |
|
|
to the processor's external bus/memory interface. These components are:
|
| 33 |
|
|
|
| 34 |
|
|
* an external instruction memory (that also allows booting from it)
|
| 35 |
|
|
* an external data memory
|
| 36 |
|
|
* an external memory to simulate "external IO devices"
|
| 37 |
|
|
* a memory-mapped registers to trigger the processor's interrupt signals
|
| 38 |
|
|
|
| 39 |
|
|
The following table shows the base addresses of these four components and their default configuration and
|
| 40 |
|
|
properties:
|
| 41 |
|
|
|
| 42 |
|
|
[NOTE]
|
| 43 |
|
|
====
|
| 44 |
|
|
Attributes:
|
| 45 |
|
|
|
| 46 |
|
|
* `r` = read
|
| 47 |
|
|
* `w` = write
|
| 48 |
|
|
* `e` = execute
|
| 49 |
|
|
* `a` = atomic accesses possible
|
| 50 |
|
|
* `8` = byte-accessible
|
| 51 |
|
|
* `16` = half-word-accessible
|
| 52 |
|
|
* `32` = word-accessible
|
| 53 |
|
|
====
|
| 54 |
|
|
|
| 55 |
|
|
.Testbench: processor-external memories
|
| 56 |
|
|
[cols="^4,>3,^5,<11"]
|
| 57 |
|
|
[options="header",grid="rows"]
|
| 58 |
|
|
|=======================
|
| 59 |
|
|
| Base address | Size | Attributes | Description
|
| 60 |
|
|
| `0x00000000` | `imem_size_c` | `r/w/e, a, 8/16/32` | external IMEM (initialized with application image)
|
| 61 |
|
|
| `0x80000000` | `dmem_size_c` | `r/w/e, a, 8/16/32` | external DMEM
|
| 62 |
|
|
| `0xf0000000` | 64 bytes | `r/w/e, !a, 8/16/32` | external "IO" memory, atomic accesses will fail
|
| 63 |
|
|
| `0xff000000` | 4 bytes | `-/w/-, a, -/-/32` | memory-mapped register to trigger "machine external", "machine software" and "SoC Fast Interrupt" interrupts
|
| 64 |
|
|
|=======================
|
| 65 |
|
|
|
| 66 |
|
|
[IMPORTANT]
|
| 67 |
|
|
The simulated NEORV32 does not use the bootloader and _directly boots_ the current application image (from
|
| 68 |
|
|
the `rtl/core/neorv32_application_image.vhd` image file).
|
| 69 |
|
|
|
| 70 |
|
|
.UART output during simulation
|
| 71 |
|
|
[IMPORTANT]
|
| 72 |
|
|
Data written to the NEORV32 UART0 / UART1 transmitter is send to a virtual UART receiver implemented
|
| 73 |
|
|
as part of the testbench. Received chars are send to the simulator console and are also stored to a log file
|
| 74 |
|
|
(`neorv32.testbench_uart0.out` for UART0, `neorv32.testbench_uart1.out` for UART1) inside the simulation's home folder.
|
| 75 |
|
|
**Please note that printing via the native UART receiver takes a lot of time.** For faster simulation console output
|
| 76 |
|
|
see section <<_faster_simulation_console_output>>.
|
| 77 |
|
|
|
| 78 |
|
|
|
| 79 |
|
|
:sectnums:
|
| 80 |
|
|
=== Faster Simulation Console Output
|
| 81 |
|
|
|
| 82 |
|
|
When printing data via the UART the communication speed will always be based on the configured BAUD
|
| 83 |
|
|
rate. For a simulation this might take some time. To have faster output you can enable the **simulation mode**
|
| 84 |
|
|
for UART0/UART1 (see section https://stnolting.github.io/neorv32/#_primary_universal_asynchronous_receiver_and_transmitter_uart0[Documentation: Primary Universal Asynchronous Receiver and Transmitter (UART0)]).
|
| 85 |
|
|
|
| 86 |
|
|
ASCII data sent to UART0|UART1 will be immediately printed to the simulator console and logged to files in the simulator
|
| 87 |
|
|
execution directory:
|
| 88 |
|
|
|
| 89 |
|
|
* `neorv32.uart?.sim_mode.text.out`: ASCII data.
|
| 90 |
|
|
* `neorv32.uart?.sim_mode.data.out`: all written 32-bit dumped as 8-char hexadecimal values.
|
| 91 |
|
|
|
| 92 |
|
|
You can "automatically" enable the simulation mode of UART0/UART1 when compiling an application.
|
| 93 |
|
|
In this case, the "real" UART0/UART1 transmitter unit is permanently disabled.
|
| 94 |
|
|
To enable the simulation mode just compile and install your application and add _UART?_SIM_MODE_ to the compiler's
|
| 95 |
|
|
_USER_FLAGS_ variable (do not forget the `-D` suffix flag):
|
| 96 |
|
|
|
| 97 |
|
|
[source, bash]
|
| 98 |
|
|
----
|
| 99 |
|
|
sw/example/blink_led$ make USER_FLAGS+=-DUART0_SIM_MODE clean_all all
|
| 100 |
|
|
----
|
| 101 |
|
|
|
| 102 |
|
|
The provided define will change the default UART0/UART1 setup function in order to set the simulation
|
| 103 |
|
|
mode flag in the according UART's control register.
|
| 104 |
|
|
|
| 105 |
|
|
[NOTE]
|
| 106 |
|
|
The UART simulation output (to file and to screen) outputs "complete lines" at once. A line is
|
| 107 |
|
|
completed with a line feed (newline, ASCII `\n` = 10).
|
| 108 |
|
|
|
| 109 |
|
|
|
| 110 |
|
|
:sectnums:
|
| 111 |
|
|
=== Simulation using a shell script (with GHDL)
|
| 112 |
|
|
|
| 113 |
|
|
To simulate the processor using _GHDL_ navigate to the `sim/simple/` folder and run the provided shell script.
|
| 114 |
|
|
Any arguments that are provided while executing this script are passed to GHDL.
|
| 115 |
|
|
For example the simulation time can be set to 20ms using `--stop-time=20ms` as argument.
|
| 116 |
|
|
|
| 117 |
|
|
[source, bash]
|
| 118 |
|
|
----
|
| 119 |
|
|
neorv32/sim/simple$ sh ghdl_sim.sh --stop-time=20ms
|
| 120 |
|
|
----
|
| 121 |
|
|
|
| 122 |
|
|
|
| 123 |
|
|
:sectnums:
|
| 124 |
|
|
=== Simulation using Application Makefiles (In-Console with GHDL)
|
| 125 |
|
|
|
| 126 |
|
|
To directly compile and run a program in the console (using the default testbench and GHDL
|
| 127 |
|
|
as simulator) you can use the `sim` makefile target. Make sure to use the UART simulation mode
|
| 128 |
|
|
(`USER_FLAGS+=-DUART0_SIM_MODE` and/or `USER_FLAGS+=-DUART1_SIM_MODE`) to get
|
| 129 |
|
|
faster / direct-to-console UART output.
|
| 130 |
|
|
|
| 131 |
|
|
[source, bash]
|
| 132 |
|
|
----
|
| 133 |
|
|
sw/example/blink_led$ make USER_FLAGS+=-DUART0_SIM_MODE clean_all sim
|
| 134 |
|
|
[...]
|
| 135 |
|
|
Blinking LED demo program
|
| 136 |
|
|
----
|
| 137 |
|
|
|
| 138 |
|
|
|
| 139 |
|
|
:sectnums:
|
| 140 |
|
|
==== Hello World!
|
| 141 |
|
|
|
| 142 |
|
|
To do a quick test of the NEORV32 make sure to have https://github.com/ghdl/ghdl[GHDL] and a
|
| 143 |
|
|
https://github.com/stnolting/riscv-gcc-prebuilt[RISC-V gcc toolchain] installed.
|
| 144 |
|
|
Navigate to the project's `sw/example/hello_world` folder and run `make USER_FLAGS+=-DUART0_SIM_MODE MARCH=rv32imac clean_all sim`:
|
| 145 |
|
|
|
| 146 |
|
|
[TIP]
|
| 147 |
|
|
The simulator will output some _sanity check_ notes (and warnings or even errors if something is ill-configured)
|
| 148 |
|
|
right at the beginning of the simulation to give a brief overview of the actual NEORV32 SoC and CPU configurations.
|
| 149 |
|
|
|
| 150 |
|
|
[source, bash]
|
| 151 |
|
|
----
|
| 152 |
|
|
stnolting@Einstein:/mnt/n/Projects/neorv32/sw/example/hello_world$ make USER_FLAGS+=-DUART0_SIM_MODE MARCH=rv32imac clean_all sim
|
| 153 |
|
|
../../../sw/lib/source/neorv32_uart.c: In function 'neorv32_uart0_setup':
|
| 154 |
|
|
../../../sw/lib/source/neorv32_uart.c:301:4: warning: #warning UART0_SIM_MODE (primary UART) enabled! Sending all UART0.TX data to text.io simulation output instead of real UART0 transmitter. Use this for simulations only! [-Wcpp]
|
| 155 |
|
|
301 | #warning UART0_SIM_MODE (primary UART) enabled! Sending all UART0.TX data to text.io simulation output instead of real UART0 transmitter. Use this for simulations only! <1>
|
| 156 |
|
|
| ^~~~~~~
|
| 157 |
|
|
Memory utilization:
|
| 158 |
|
|
text data bss dec hex filename
|
| 159 |
|
|
4612 0 120 4732 127c main.elf <2>
|
| 160 |
|
|
Compiling ../../../sw/image_gen/image_gen
|
| 161 |
|
|
Installing application image to ../../../rtl/core/neorv32_application_image.vhd <3>
|
| 162 |
|
|
Simulating neorv32_application_image.vhd...
|
| 163 |
|
|
Tip: Compile application with USER_FLAGS+=-DUART[0/1]_SIM_MODE to auto-enable UART[0/1]'s simulation mode (redirect UART output to simulator console). <4>
|
| 164 |
|
|
Using simulation runtime args: --stop-time=10ms <5>
|
| 165 |
|
|
../rtl/core/neorv32_top.vhd:347:3:@0ms:(assertion note): NEORV32 PROCESSOR IO Configuration: GPIO MTIME UART0 UART1 SPI TWI PWM WDT CFS SLINK NEOLED XIRQ <6>
|
| 166 |
|
|
../rtl/core/neorv32_top.vhd:370:3:@0ms:(assertion note): NEORV32 PROCESSOR CONFIG NOTE: Boot configuration: Direct boot from memory (processor-internal IMEM).
|
| 167 |
|
|
../rtl/core/neorv32_top.vhd:394:3:@0ms:(assertion note): NEORV32 PROCESSOR CONFIG NOTE: Implementing on-chip debugger (OCD).
|
| 168 |
|
|
../rtl/core/neorv32_cpu.vhd:169:3:@0ms:(assertion note): NEORV32 CPU ISA Configuration (MARCH): RV32IMACU_Zbb_Zicsr_Zifencei_Zfinx_Debug
|
| 169 |
|
|
../rtl/core/neorv32_cpu.vhd:189:3:@0ms:(assertion note): NEORV32 CPU CONFIG NOTE: Implementing NO dedicated hardware reset for uncritical registers (default, might reduce area). Set package constant = TRUE to configure a DEFINED reset value for all CPU registers.
|
| 170 |
|
|
../rtl/core/neorv32_imem.vhd:107:3:@0ms:(assertion note): NEORV32 PROCESSOR CONFIG NOTE: Implementing processor-internal IMEM as ROM (16384 bytes), pre-initialized with application (4612 bytes).
|
| 171 |
|
|
../rtl/core/neorv32_dmem.vhd:89:3:@0ms:(assertion note): NEORV32 PROCESSOR CONFIG NOTE: Implementing processor-internal DMEM (RAM, 8192 bytes).
|
| 172 |
|
|
../rtl/core/neorv32_wishbone.vhd:136:3:@0ms:(assertion note): NEORV32 PROCESSOR CONFIG NOTE: External Bus Interface - Implementing STANDARD Wishbone protocol.
|
| 173 |
|
|
../rtl/core/neorv32_wishbone.vhd:140:3:@0ms:(assertion note): NEORV32 PROCESSOR CONFIG NOTE: External Bus Interface - Implementing auto-timeout (255 cycles).
|
| 174 |
|
|
../rtl/core/neorv32_wishbone.vhd:144:3:@0ms:(assertion note): NEORV32 PROCESSOR CONFIG NOTE: External Bus Interface - Implementing LITTLE-endian byte order.
|
| 175 |
|
|
../rtl/core/neorv32_wishbone.vhd:148:3:@0ms:(assertion note): NEORV32 PROCESSOR CONFIG NOTE: External Bus Interface - Implementing registered RX path.
|
| 176 |
|
|
../rtl/core/neorv32_slink.vhd:161:3:@0ms:(assertion note): NEORV32 PROCESSOR CONFIG NOTE: Implementing 8 RX and 8 TX stream links.
|
| 177 |
|
|
<7>
|
| 178 |
|
|
##
|
| 179 |
|
|
## ## ## ##
|
| 180 |
|
|
## ## ######### ######## ######## ## ## ######## ######## ## ################
|
| 181 |
|
|
#### ## ## ## ## ## ## ## ## ## ## ## ## ## #### ####
|
| 182 |
|
|
## ## ## ## ## ## ## ## ## ## ## ## ## ## ###### ##
|
| 183 |
|
|
## ## ## ######### ## ## ######### ## ## ##### ## ## #### ###### ####
|
| 184 |
|
|
## ## ## ## ## ## ## ## ## ## ## ## ## ## ###### ##
|
| 185 |
|
|
## #### ## ## ## ## ## ## ## ## ## ## ## #### ####
|
| 186 |
|
|
## ## ######### ######## ## ## ## ######## ########## ## ################
|
| 187 |
|
|
## ## ## ##
|
| 188 |
|
|
##
|
| 189 |
|
|
Hello world! :)
|
| 190 |
|
|
----
|
| 191 |
|
|
<1> Notifier that "simulation mode" of UART0 is enabled (by the `USER_FLAGS+=-DUART0_SIM_MODE` makefile flag). All UART0 output is send to the simulator console.
|
| 192 |
|
|
<2> Final executable size (`text`) and _static_ data memory requirements (`data`, `bss`).
|
| 193 |
|
|
<3> The application code is _installed_ as pre-initialized IMEM. This is the default approach for simulation.
|
| 194 |
|
|
<4> A note regarding UART "simulation mode", but we have already enabled that.
|
| 195 |
|
|
<5> List of (default) arguments that were send to the simulator. Here: maximum simulation time (10ms).
|
| 196 |
|
|
<6> "Sanity checks" from the core's VHDL files. These reports give some brief information about the SoC/CPU configuration (-> generics). If there are problems with the current configuration, an ERROR will appear.
|
| 197 |
|
|
<7> Execution of the actual program starts.
|
| 198 |
|
|
|
| 199 |
|
|
|
| 200 |
|
|
:sectnums:
|
| 201 |
|
|
=== Advanced Simulation using VUnit
|
| 202 |
|
|
|
| 203 |
|
|
https://vunit.github.io/[VUnit] is an open source unit testing framework for VHDL/SystemVerilog.
|
| 204 |
|
|
It allows continuous and automated testing of HDL code by complementing traditional testing methodologies.
|
| 205 |
|
|
The motto of VUnit is _"testing early and often"_ through automation.
|
| 206 |
|
|
|
| 207 |
|
|
VUnit is composed by a http://vunit.github.io/py/ui.html[Python interface] and multiple optional
|
| 208 |
|
|
http://vunit.github.io/vhdl_libraries.html[VHDL libraries].
|
| 209 |
|
|
The Python interface allows declaring sources and simulation options, and it handles the compilation, execution and
|
| 210 |
|
|
gathering of the results regardless of the simulator used.
|
| 211 |
|
|
That allows having a single `run.py` script to be used with GHDL, ModelSim/QuestaSim, Riviera PRO, etc.
|
| 212 |
|
|
On the other hand, the VUnit's VHDL libraries provide utilities for assertions, logging, having virtual queues, handling CSV files, etc.
|
| 213 |
|
|
The http://vunit.github.io/verification_components/user_guide.html[Verification Component Library] uses those features
|
| 214 |
|
|
for abstracting away bit-toggling when verifying standard interfaces such as Wishbone, AXI, Avalon, UARTs, etc.
|
| 215 |
|
|
|
| 216 |
|
|
Testbench sources in `sim` (such as `sim/neorv32_tb.vhd` and `sim/uart_rx*.vhd`) use VUnit's VHDL libraries for testing
|
| 217 |
|
|
NEORV32 and peripherals.
|
| 218 |
|
|
The entry-point for executing the tests is `sim/run.py`.
|
| 219 |
|
|
|
| 220 |
|
|
[source, bash]
|
| 221 |
|
|
----
|
| 222 |
|
|
# ./sim/run.py -l
|
| 223 |
|
|
neorv32.neorv32_tb.all
|
| 224 |
|
|
Listed 1 tests
|
| 225 |
|
|
|
| 226 |
|
|
# ./sim/run.py -v
|
| 227 |
|
|
Compiling into neorv32: rtl/core/neorv32_uart.vhd passed
|
| 228 |
|
|
Compiling into neorv32: rtl/core/neorv32_twi.vhd passed
|
| 229 |
|
|
Compiling into neorv32: rtl/core/neorv32_trng.vhd passed
|
| 230 |
|
|
...
|
| 231 |
|
|
----
|
| 232 |
|
|
|
| 233 |
|
|
See http://vunit.github.io/user_guide.html[VUnit: User Guide] and http://vunit.github.io/cli.html[VUnit: Command Line Interface] for further info about VUnit's features.
|