## 1. Overview

The NEORV32[1] is an open-source RISC-V compatible processor system that is intended as ready-to-go auxiliary processor within a larger SoC designs or as stand-alone custom / customizable microcontroller.

The system is highly configurable and provides optional common peripherals like embedded memories, timers, serial interfaces, general purpose IO ports and an external bus interface to connect custom IP like memories, NoCs and other peripherals. On-line and in-system debugging is supported by an OpenOCD/gdb compatible on-chip debugger accessible via JTAG.

The software framework of the processor comes with application makefiles, software libraries for all CPU and processor features, a bootloader, a runtime environment and several example programs – including a port of the CoreMark MCU benchmark and the official RISC-V architecture test suite. RISC-V GCC is used as default toolchain (prebuilt toolchains are also provided).

 Check out the processor’s online User Guide that provides hands-on tutorial to get you started.
 The project’s change log is available in CHANGELOG.md in the root directory of the NEORV32 repository. Please also check out the Legal section.

Structure

 Links in this document are highlighted.

### 1.1. Rationale

Why did you make this?

I am fascinated by processor and CPU architecture design: it is the magic frontier where software meets hardware. This project has started as something like a journey into this magic realm to understand how things actually work down on this very low level.

But there is more! When I started to dive into the emerging RISC-V ecosystem I felt overwhelmed by the complexity. As a beginner it is hard to get an overview - especially when you want to setup a minimal platform to tinker with: Which core to use? How to get the right toolchain? What features do I need? How does the booting work? How do I create an actual executable? How to get that into the hardware? How to customize things? Where to start???

So this project aims to provides a simple to understand and easy to use yet powerful and flexible platform that targets FPGA and RISC-V beginners as well as advanced users. Join me and us on this journey! 🙃

Why a soft-core processor?

As a matter of fact soft-core processors cannot compete with discrete or FPGA hard-macro processors in terms of performance, energy and size. But they do fill a niche in FPGA design space. For example, soft-core processors allow to implement the control flow part of certain applications (like communication protocol handling) using software like plain C. This provides high flexibility as software can be easily changed, re-compiled and re-uploaded again.

Furthermore, the concept of flexibility applies to all aspects of a soft-core processor. The user can add exactly the features that are required by the application: additional memories, custom interfaces, specialized IP and even user-defined instructions.

Why RISC-V?

RISC-V is a free and open ISA enabling a new era of processor innovation through open standard collaboration.

— RISC-V International

I love the idea of open-source. Knowledge can help best if it is freely available. While open-source has already become quite popular in software, hardware projects still need to catch up. Admittedly, there has been quite a development, but mainly in terms of platforms and applications (so schematics, PCBs, etc.). Although processors and CPUs are the heart of almost every digital system, having a true open-source silicon is still a rarity. RISC-V aims to change that. Even it is just one approach, it helps paving the road for future development.

Furthermore, I welcome the community aspect of RISC-V. The ISA and everything beyond is developed with direct contact to the community: this includes businesses and professionals but also hobbyist, amateurs and people that are just curious. Everyone can join discussions and contribute to RISC-V in their very own way.

Finally, I really like the RISC-V ISA itself. It aims to be a clean, orthogonal and "intuitive" ISA that resembles with the basic concepts of RISC: simple yet effective.

Yet another RISC-V core? What makes it special?

The NEORV32 is not based on another RISC-V core. It was build entirely from ground up (just following the official ISA specs) having a different design goal in mind. The project does not intend to replace certain RISC-V cores or just beat existing ones like VexRISC in terms of performance or SERV in terms of size.

The project aims to provide another option in the RISC-V / soft-core design space with a different performance vs. size trade-off and a different focus: embrace concepts like documentation, platform-independence / portability, RISC-V compatibility, customization and ease of use. See the Project Key Features below.

### 1.2. Project Key Features

• open-source and documented; including user guides to get started

• completely described in behavioral, platform-independent VHDL (yet platform-optimized modules are provided)

• fully synchronous design, no latches, no gated clocks

• small hardware footprint and high operating frequency for easy integration

• NEORV32 CPU: 32-bit `rv32i` RISC-V CPU

• RISC-V compatibility: passes the official architecture tests

• base architecture + privileged architecture (optional) + ISA extensions (optional)

• rich set of customization options (ISA extensions, design goal: performance / area (/ energy), …​)

• aims to support Full Virtualization capabilities (CPU and SoC) to increase execution safety

• NEORV32 Processor (SoC): highly-configurable full-scale microcontroller-like processor system

• based on the NEORV32 CPU

• optional serial interfaces (UARTs, TWI, SPI)

• optional timers and counters (WDT, MTIME)

• optional general purpose IO and PWM and native NeoPixel (c) compatible smart LED interface

• optional embedded memories / caches for data, instructions and bootloader

• optional external memory interface (Wishbone / AXI4-Lite) and stream link interface (AXI4-Stream) for custom connectivity

• on-chip debugger compatible with OpenOCD and gdb

• Software framework

• GCC-based toolchain - prebuilt toolchains available; application compilation based on GNU makefiles

• internal bootloader with serial user interface

• core libraries for high-level usage of the provided functions and peripherals

• runtime environment and several example programs

• doxygen-based documentation of the software framework; a deployed version is available at https://stnolting.github.io/neorv32/sw/files.html

• FreeRTOS port + demos available

 For more in-depth details regarding the feature provided by he hardware see the according sections: NEORV32 Central Processing Unit (CPU) and NEORV32 Processor (SoC).

### 1.3. Project Folder Structure

```neorv32                - Project home folder
│
├docs                  - Project documentation
│├datasheet            - .adoc sources for NEORV32 data sheet
│├doxygen_build        - Software framework documentation (generated by doxygen)
│├figures              - Figures and logos
│├icons                - Misc. symbols
│├references           - Data sheets and RISC-V specs.
│└src_adoc             - AsciiDoc sources for this document
│
├rtl                   - VHDL sources
│├core                 - Core sources of the CPU & SoC
││└mem                 - SoC-internal memories (default architectures)
│├processor_templates  - Pre-configured SoC wrappers
│├system_integration   - System wrappers for advanced connectivity
│└test_setups          - Minimal test setup "SoCs" used in the User Guide
│
├setups                - Example setups for various FPGAs, boards and toolchains
│└...
│
├sim                   - Simulation files (see User Guide)
│
└sw                    - Software framework
├common               - Linker script and crt0.S start-up code
├example              - Various example programs
│└...
├isa-test
│├riscv-arch-test     - RISC-V spec. compatibility test framework (submodule)
│└port-neorv32        - Port files for the official RISC-V architecture tests
├ocd_firmware         - source code for on-chip debugger's "park loop"
├openocd              - OpenOCD on-chip debugger configuration files
├image_gen            - Helper program to generate NEORV32 executables
└lib                  - Processor core library
└source              - Source files (*.c)```

### 1.4. VHDL File Hierarchy

All necessary VHDL hardware description files are located in the project’s `rtl/core folder`. The top entity of the entire processor including all the required configuration generics is `neorv32_top.vhd`.

 All core VHDL files from the list below have to be assigned to a new design library named `neorv32`. Additional files, like alternative top entities, can be assigned to any library.
```neorv32_top.vhd                  - NEORV32 Processor top entity
│
├neorv32_fifo.vhd                - General purpose FIFO component
├neorv32_package.vhd             - Processor/CPU main VHDL package file
│
├neorv32_cpu.vhd                 - NEORV32 CPU top entity
│├neorv32_cpu_alu.vhd            - Arithmetic/logic unit
││├neorv32_cpu_cp_bitmanip.vhd   - Bit-manipulation co-processor (B ext.)
││├neorv32_cpu_cp_fpu.vhd        - Floating-point co-processor (Zfinx ext.)
││├neorv32_cpu_cp_muldiv.vhd     - Mul/Div co-processor (M extension)
││└neorv32_cpu_cp_shifter.vhd    - Bit-shift co-processor
│├neorv32_cpu_bus.vhd            - Bus interface + physical memory protection
│├neorv32_cpu_control.vhd        - CPU control, exception/IRQ system and CSRs
││└neorv32_cpu_decompressor.vhd  - Compressed instructions decoder
│└neorv32_cpu_regfile.vhd        - Data register file
│
├neorv32_busswitch.vhd           - Processor bus switch for CPU buses (I&D)
├neorv32_bus_keeper.vhd          - Processor-internal bus monitor
├neorv32_cfs.vhd                 - Custom functions subsystem
├neorv32_debug_dm.vhd            - on-chip debugger: debug module
├neorv32_debug_dtm.vhd           - on-chip debugger: debug transfer module
├neorv32_dmem.entity.vhd         - Processor-internal data memory (entity-only!)
├neorv32_gpio.vhd                - General purpose input/output port unit
├neorv32_icache.vhd              - Processor-internal instruction cache
├neorv32_imem.entity.vhd         - Processor-internal instruction memory (entity-only!)
│└neor32_application_image.vhd   - IMEM application initialization image
├neorv32_mtime.vhd               - Machine system timer
├neorv32_neoled.vhd              - NeoPixel (TM) compatible smart LED interface
├neorv32_pwm.vhd                 - Pulse-width modulation controller
├neorv32_spi.vhd                 - Serial peripheral interface controller
├neorv32_sysinfo.vhd             - System configuration information memory
├neorv32_trng.vhd                - True random number generator
├neorv32_twi.vhd                 - Two wire serial interface controller
├neorv32_wdt.vhd                 - Watchdog timer
├neorv32_wishbone.vhd            - External (Wishbone) bus interface
│
├mem/neorv32_dmem.default.vhd    - _Default_ data memory (architecture-only!)
└mem/neorv32_imem.default.vhd    - _Default_ instruction memory (architecture-only!)```
 The processor-internal instruction and data memories (IMEM and DMEM) are split into two design files each: a plain entity definition (`neorv32_*mem.entity.vhd`) and the actual architecture definition (`mem/neorv32_*mem.default.vhd`). The default architecture definitions from `rtl/core/mem` provide a generic and platform independent memory design that (should) infers embedded memory blocks. You can replace/modify the architecture source file in order to use platform-specific features (like advanced memory resources) or to improve technology mapping and/or timing.

### 1.5. FPGA Implementation Results

This chapter shows exemplary implementation results of the NEORV32 CPU and NEORV32 Processor.

#### 1.5.1. CPU

 Hardware version: `1.5.7.10` Top entity: `rtl/core/neorv32_cpu.vhd`
CPU LEs FFs MEM bits DSPs fmax

`rv32i`

806

359

1024

0

125 MHz

`rv32i_Zicsr`

1729

813

1024

0

124 MHz

`rv32im_Zicsr`

2269

1055

1024

0

124 MHz

`rv32imc_Zicsr`

2501

1070

1024

0

124 MHz

`rv32imac_Zicsr`

2511

1074

1024

0

124 MHz

`rv32imacu_Zicsr`

2521

1079

1024

0

124 MHz

`rv32imacu_Zicsr_Zifencei`

2522

1079

1024

0

122 MHz

`rv32imacu_Zicsr_Zifencei_Zfinx`

3807

1731

1024

7

116 MHz

`rv32imacu_Zicsr_Zifencei_Zfinx_DebugMode`

3974

1815

1024

7

116 MHz

 No HPM counters and no PMP regions were implemented for generating these results.
 The CPU provides further options to reduce the area footprint (for example by constraining the CPU-internal counter sizes) or to increase performance (for example by using a barrel-shifter; at cost of extra hardware). See section Processor Top Entity - Generics for more information. Also, take a look at the User Guide section Application-Specific Processor Configuration.

#### 1.5.2. Processor Modules

 Hardware version: `1.5.7.15` Top entity: `rtl/core/neorv32_top.vhd`
Table 1. Hardware utilization by the processor modules (mandatory core modules in bold)
Module Description LEs FFs MEM bits DSPs

Boot ROM

2

1

32768

0

BUSKEEPER

Processor-internal bus monitor

9

6

0

0

BUSSWITCH

Bus mux for CPU instr. and data interface

63

8

0

0

CFS

Custom functions subsystem[2]

-

-

-

-

DMEM

Processor-internal data memory (8kB)

19

2

65536

0

DM

On-chip debugger - debug module

493

240

0

0

DTM

On-chip debugger - debug transfer module (JTAG)

254

218

0

0

GPIO

General purpose input/output ports

134

161

0

0

iCACHE

Instruction cache (1x4 blocks, 256 bytes per block)

2 21

156

8192

0

IMEM

Processor-internal instruction memory (16kB)

13

2

131072

0

MTIME

Machine system timer

319

167

0

0

NEOLED

Smart LED Interface (NeoPixel/WS28128) [FIFO_depth=1]

226

182

0

0

Stream link interface (2xRX, 2xTX, FIFO_depth=1)

208

181

0

0

PWM

Pulse_width modulation controller (4 channels)

71

69

0

0

SPI

Serial peripheral interface

148

127

0

0

SYSINFO

System configuration information memory

14

11

0

0

TRNG

True random number generator

89

76

0

0

TWI

Two-wire interface

77

43

0

0

UART0/1

183

132

0

0

WDT

Watchdog timer

53

43

0

0

WISHBONE

External memory interface

114

110

0

0

XIRQ

External interrupt controller (32 channels)

241

201

0

0

#### 1.5.3. Exemplary Setups

Check out the `setups` folder (@GitHub: https://github.com/stnolting/neorv32/tree/master/setups), which provides several demo setups for various FPGA boards and toolchains.

### 1.6. CPU Performance

The performance of the NEORV32 was tested and evaluated using the Core Mark CPU benchmark. This benchmark focuses on testing the capabilities of the CPU core itself rather than the performance of the whole system. The according sources can be found in the `sw/example/coremark` folder.

 Dhrystone A simple port of the Dhrystone benchmark is also available in `sw/example/dhrystone`.

The resulting CoreMark score is defined as CoreMark iterations per second. The execution time is determined via the RISC-V `[m]cycle[h]` CSRs. The relative CoreMark score is defined as CoreMark score divided by the CPU’s clock frequency in MHz.

 HW version: `1.5.7.10` Hardware: 32kB int. IMEM, 16kB int. DMEM, no caches, 100MHz clock CoreMark: 2000 iterations, MEM_METHOD is MEM_STACK Compiler: RISCV32-GCC 10.2.0 Compiler flags: default, see makefile
Table 3. CoreMark results
CPU CoreMark Score CoreMarks/Mhz Average CPI

small (`rv32i_Zicsr`)

33.89

0.3389

4.04

medium (`rv32imc_Zicsr`)

62.50

0.6250

5.34

performance(`rv32imc_Zicsr` + perf. options)

95.23

0.9523

3.54

 The "performance" CPU configuration uses the FAST_MUL_EN and FAST_SHIFT_EN options.
 The NEORV32 CPU is based on a multi-cycle architecture. Each instruction is executed in a sequence of several consecutive micro operations.
 The average CPI (cycles per instruction) depends on the instruction mix of a specific applications and also on the available CPU extensions. The average CPI is computed by dividing the total number of required clock cycles (only the timed core to avoid distortion due to IO wait cycles) by the number of executed instructions (`[m]instret[h]` CSRs).
 More information regarding the execution time of each implemented instruction can be found in chapter Instruction Timing.

## 2. NEORV32 Processor (SoC)

The NEORV32 Processor is based on the NEORV32 CPU. Together with common peripheral interfaces and embedded memories it provides a RISC-V-based full-scale microcontroller-like SoC platform.

Key Features

• optional processor-internal data and instruction memories (DMEM/IMEM) + cache (iCACHE)

• optional internal bootloader (BOOTROM) with UART console & SPI flash boot option

• optional machine system timer (MTIME), RISC-V-compatible

• optional two independent universal asynchronous receivers and transmitters (UART0, UART1) with optional hardware flow control (RTS/CTS)

• optional 8/16/24/32-bit serial peripheral interface controller (SPI) with 8 dedicated CS lines

• optional two wire serial interface controller (TWI), compatible to the I²C standard

• optional general purpose parallel IO port (GPIO), 64xOut, 64xIn

• optional 32-bit external bus interface, Wishbone b4 / AXI4-Lite compatible (WISHBONE)

• optional watchdog timer (WDT)

• optional PWM controller with up to 60 channels & 8-bit duty cycle resolution (PWM)

• optional ring-oscillator-based true random number generator (TRNG)

• optional custom functions subsystem for custom co-processor extensions (CFS)

• optional NeoPixel™/WS2812-compatible smart LED interface (NEOLED)

• optional external interrupt controller with up to 32 channels (XIRQ)

• optional on-chip debugger with JTAG TAP (OCD)

• system configuration information memory to check HW configuration via software (SYSINFO)

### 2.1. Processor Top Entity - Signals

The following table shows signals of the processor top entity (`rtl/core/neorv32_top.vhd`). The type of all signals is `std_ulogic` or `std_ulogic_vector`, respectively.

All input signals provide default values in case they are not explicitly assigned during instantiation. For control signals the value `L` (weak pull-down) is used. For serial and parallel data signals the value `U` (unknown) is used. Pulled-down signals will not cause "accidental" system crashes since all control signals have defined level.

Signal Width Dir. Function

Global Control

`clk_i`

1

in

global clock line, all registers triggering on rising edge

`rstn_i`

1

in

global reset, asynchronous, low-active

JTAG Access Port for On-Chip Debugger (OCD)

`jtag_trst_i`

1

in

TAP reset, low-active (optional[3])

`jtag_tck_i`

1

in

serial clock

`jtag_tdi_i`

1

in

serial data input

`jtag_tdo_o`

1

out

serial data output[4]

`jtag_tms_i`

1

in

mode select

External Bus Interface (WISHBONE)

`wb_tag_o`

3

out

tag (access type identifier)

`wb_adr_o`

32

out

`wb_dat_i`

32

in

write data

`wb_dat_o`

32

out

`wb_we_o`

1

out

write enable ('0' = read transfer)

`wb_sel_o`

4

out

byte enable

`wb_stb_o`

1

out

strobe

`wb_cyc_o`

1

out

valid cycle

`wb_lock_o`

1

out

exclusive access request

`wb_ack_i`

1

in

transfer acknowledge

`wb_err_i`

1

in

transfer error

`fence_o`

1

out

indicates an executed fence instruction

`fencei_o`

1

out

indicates an executed fencei instruction

`slink_tx_dat_o`

8x32

out

`slink_tx_val_o`

8

out

`slink_tx_rdy_i`

8

in

TX link n allowed to send

`slink_rx_dat_i`

8x32

in

`slink_rx_val_i`

8

in

`slink_rx_rdy_o`

8

out

General Purpose Inputs & Outputs (GPIO)

`gpio_o`

64

out

general purpose parallel output

`gpio_i`

64

in

general purpose parallel input

`uart0_txd_o`

1

out

UART0 serial transmitter

`uart0_rxd_i`

1

in

`uart0_rts_o`

1

out

`uart0_cts_i`

1

in

UART0 TX allowed to start sending

`uart1_txd_o`

1

out

UART1 serial transmitter

`uart1_rxd_i`

1

in

`uart1_rts_o`

1

out

`uart1_cts_i`

1

in

UART1 TX allowed to start sending

Serial Peripheral Interface Controller (SPI)

`spi_sck_o`

1

out

SPI controller clock line

`spi_sdo_o`

1

out

SPI serial data output

`spi_sdi_i`

1

in

SPI serial data input

`spi_csn_o`

8

out

SPI dedicated chip select (low-active)

Two-Wire Interface Controller (TWI)

`twi_sda_io`

1

inout

TWI serial data line

`twi_scl_io`

1

inout

TWI serial clock line

Custom Functions Subsystem (CFS)

`cfs_in_i`

32

in

custom CFS input signal conduit

`cfs_out_o`

32

out

custom CFS output signal conduit

Pulse-Width Modulation Channels (PWM)

`pwm_o`

4

out

pulse-width modulated channels

Smart LED Interface - NeoPixel™ compatible (NEOLED)

`neoled_o`

1

out

asynchronous serial data output

System time (MTIME)

`mtime_i`

64

in

machine timer time (to `time[h]` CSRs) from external MTIME unit if the processor-internal MTIME unit is NOT implemented

`mtime_o`

64

out

machine timer time from internal MTIME unit if processor-internal MTIME unit IS implemented

External Interrupts

`xirq_i`

32

in

external interrupt requests (up to 32 channels)

CPU Interrupts

`mtime_irq_i`

1

in

machine timer interrupt13 (RISC-V), high-active

`msw_irq_i`

1

in

machine software interrupt (RISC-V), high-active

`mext_irq_i`

1

in

machine external interrupt (RISC-V), high-active

### 2.2. Processor Top Entity - Generics

This is a list of all configuration generics of the NEORV32 processor top entity rtl/neorv32_top.vhd. The generic name is shown in orange, followed by the type in printed in black and concluded by the default value printed in light gray.

 The NEORV32 generics allow to configure the system according to your needs. The generics are used to control implementation of certain CPU extensions and peripheral modules and even allow to optimize the system for certain design goals like minimal area or maximum performance. More information can be found in the user guides' section Application-Specific Processor Configuration.
 Privileged software can determine the actual CPU and processor configuration via the `misa` and the SYSINFO registers.
 If optional modules (like CPU extensions or peripheral devices) are not enabled the according circuitry will not be synthesized at all. Hence, the disabled modules do not increase area and power requirements and do not impact the timing.
 Not all configuration combinations are valid. The processor RTL code provides sanity checks to inform the user during synthesis/simulation if an invalid combination has been detected.

Generic Description

The description of each generic provides the following summary:

 Generic name type default value Description

#### 2.2.1. General

##### CLOCK_FREQUENCY
 CLOCK_FREQUENCY natural none The clock frequency of the processor’s `clk_i` input port in Hertz (Hz). This value can be retrieved by software from the SYSINFO module.
 INT_BOOTLOADER_EN boolean false Implement the processor-internal boot ROM, pre-initialized with the default bootloader image when true. This will also change the processor’s boot address from the beginning of the instruction memory address space (default = 0x00000000) to the base address of the boot ROM. See section Boot Configuration for more information.
 HW_THREAD_ID natural 0 The hart ID of the CPU. Software can retrieve this value from the `mhartid` CSR. Note that hart IDs must be unique within a system.
##### ON_CHIP_DEBUGGER_EN
 ON_CHIP_DEBUGGER_EN boolean false Implement the on-chip debugger (OCD) and the CPU debug mode. See chapter On-Chip Debugger (OCD) for more information.

#### 2.2.2. RISC-V CPU Extensions

 See section Instruction Sets and Extensions for more information. The configuration of the RISC-V main ISA extensions (like `M`) can be determined via the `misa` CSR. The configuration of ISA sub-extensions (like `Zicsr`) and extension options can be determined via memory-mapped registers of the System Configuration Information Memory (SYSINFO) module.
##### CPU_EXTENSION_RISCV_A
 CPU_EXTENSION_RISCV_A boolean false Implement atomic memory access operations when true. See section `A` - Atomic Memory Access.
##### CPU_EXTENSION_RISCV_C
 CPU_EXTENSION_RISCV_C boolean false Implement compressed instructions (16-bit) when true. Compressed instructions can reduce program code size by approx. 30%. See section `C` - Compressed Instructions.
##### CPU_EXTENSION_RISCV_E
 CPU_EXTENSION_RISCV_E boolean false Implement the embedded CPU extension (only implement the first 16 data registers) when true. This reduces embedded memory requirements for the register file. See section `E` - Embedded CPU for more information. Note that this RISC-V extensions requires a different application binary interface (ABI).
##### CPU_EXTENSION_RISCV_M
 CPU_EXTENSION_RISCV_M boolean false Implement hardware accelerators for integer multiplication and division instructions when true. If this extensions is not enabled, multiplication and division operations (not instructions) will be computed entirely in software. If only a hardware multiplier is required use the CPU_EXTENSION_RISCV_Zmmul extension. Multiplication can also be mapped to DSP slices via the FAST_MUL_EN generic. See section `M` - Integer Multiplication and Division for more information.
##### CPU_EXTENSION_RISCV_U
 CPU_EXTENSION_RISCV_U boolean false Implement less-privileged user mode when true. See section `U` - Less-Privileged User Mode for more information.
##### CPU_EXTENSION_RISCV_Zbb
 CPU_EXTENSION_RISCV_Zbb boolean false Implement the `Zbb` basic bit-manipulation sub-extension when true. See section `Zbb` Basic Bit-Manipulation Operations for more information.
##### CPU_EXTENSION_RISCV_Zfinx
 CPU_EXTENSION_RISCV_Zfinx boolean false Implement the 32-bit single-precision floating-point extension (using integer registers) when true. See section `Zfinx` Single-Precision Floating-Point Operations for more information.
##### CPU_EXTENSION_RISCV_Zicsr
 CPU_EXTENSION_RISCV_Zicsr boolean true Implement the control and status register (CSR) access instructions when true. Note: When this option is disabled, the complete privileged architecture / trap system will be excluded from synthesis. Hence, no interrupts, no exceptions and no machine information will be available. See section `Zicsr` Control and Status Register Access / Privileged Architecture for more information.
##### CPU_EXTENSION_RISCV_Zifencei
 CPU_EXTENSION_RISCV_Zifencei boolean false Implement the instruction fetch synchronization instruction `fence.i`. For example, this option is required for self-modifying code (and/or for instruction cache and CPU prefetch buffer flushes). See section `Zifencei` Instruction Stream Synchronization for more information.
##### CPU_EXTENSION_RISCV_Zmmul
 CPU_EXTENSION_RISCV_Zmmul boolean false Implement integer multiplication-only instructions when true. This is a sub-extension of the `M` extension, which cannot be used together with the `M` extension. See section `Zmmul` - Integer Multiplication for more information.

#### 2.2.3. Extension Options

##### FAST_MUL_EN
 FAST_MUL_EN boolean false When this generic is enabled, the multiplier of the `M` extension is implemented using DSPs blocks instead of an iterative bit-serial approach. Performance will be increased and LUT utilization will be reduced at the cost of DSP slice utilization. This generic is only relevant when a hardware multiplier CPU extension is enabled (CPU_EXTENSION_RISCV_M or CPU_EXTENSION_RISCV_Zmmul is true). Note that the multipliers of the `Zfinx` Single-Precision Floating-Point Operations extension are always mapped to DSP block (if available).
##### FAST_SHIFT_EN
 FAST_SHIFT_EN boolean false If this generic is set true the shifter unit of the CPU’s ALU is implemented as fast barrel shifter (requiring more hardware resources but completing within two clock cycles). If it is set false, the CPU uses a serial shifter that only performs a single bit shift per cycle (requiring less hardware resources, but requires up to 32 clock cycles to complete - depending on shift amount). Note that this option also implements barrel shifters for all shift-related operations of the `Zbb` Basic Bit-Manipulation Operations extension.
##### CPU_CNT_WIDTH
 CPU_CNT_WIDTH natural 64 This generic configures the total size of the CPU’s `[m]cycle` and `[m]instret` CSRs (low word + high word). The maximum value is 64, the minimum value is 0. See section (Machine) Counters and Timers for more information. Note: configurations with CPU_CNT_WIDTH less than 64 bits do not comply to the RISC-V specs.
##### CPU_IPB_ENTRIES
 CPU_IPB_ENTRIES natural 2 This generic configures the number of entries in the CPU’s instruction prefetch buffer (a FIFO). The value has to be a power of two and has to be greater than zero. Long linear sequences of code can benefit from an increased IPB size.

#### 2.2.4. Physical Memory Protection (PMP)

See section `PMP` Physical Memory Protection for more information.

##### PMP_NUM_REGIONS
 PMP_NUM_REGIONS natural 0 Total number of implemented protections regions (0..64). If this generics is zero no physical memory protection logic will be implemented at all. Setting PMP_NUM_REGIONS_ > 0 will set the SYSINFO_CPU_PMP flag in the `CPU` SYSINFO register.
##### PMP_MIN_GRANULARITY
 PMP_MIN_GRANULARITY natural 64*1024 Minimal region granularity in bytes. Has to be a power of two. Has to be at least 8 bytes.

#### 2.2.5. Hardware Performance Monitors (HPM)

See section `HPM` Hardware Performance Monitors for more information.

##### HPM_NUM_CNTS
 HPM_NUM_CNTS natural 0 Total number of implemented hardware performance monitor counters (0..29). If this generics is zero, no hardware performance monitor logic will be implemented at all. Setting HPM_NUM_CNTS > 0 will set the SYSINFO_CPU_HPM flag in the `CPU` SYSINFO register.
##### HPM_CNT_WIDTH
 HPM_CNT_WIDTH natural 40 This generic defines the total LSB-aligned size of each HPM counter (`size([m]hpmcounter*h)` `size([m]hpmcounter*)`). The maximum value is 64, the minimal is 0. If the size is less than 64-bit, the unused MSB-aligned counter bits are hardwired to zero.

#### 2.2.6. Internal Instruction Memory

##### MEM_INT_IMEM_EN
 MEM_INT_IMEM_EN boolean false Implement processor internal instruction memory (IMEM) when true.
##### MEM_INT_IMEM_SIZE
 MEM_INT_IMEM_SIZE natural 16*1024 Size in bytes of the processor internal instruction memory (IMEM). Has no effect when MEM_INT_IMEM_EN is false.

#### 2.2.7. Internal Data Memory

##### MEM_INT_DMEM_EN
 MEM_INT_DMEM_EN boolean false Implement processor internal data memory (DMEM) when true.
##### MEM_INT_DMEM_SIZE
 MEM_INT_DMEM_SIZE natural 8*1024 Size in bytes of the processor-internal data memory (DMEM). Has no effect when MEM_INT_DMEM_EN is false.

#### 2.2.8. Internal Cache Memory

##### ICACHE_EN
 ICACHE_EN boolean false Implement processor internal instruction cache when true. Note: if the setup only uses processor-internal data and instruction memories there is not point of implementing the i-cache.
##### ICACHE_NUM_BLOCK
 ICACHE_NUM_BLOCKS natural 4 Number of blocks (cache "pages" or "lines") in the instruction cache. Has to be a power of two. Has no effect when [_icache_dmem_en] is false.
##### ICACHE_BLOCK_SIZE
 ICACHE_BLOCK_SIZE natural 64 Size in bytes of each block in the instruction cache. Has to be a power of two. Has no effect when [_icache_dmem_en] is false.
##### ICACHE_ASSOCIATIVITY
 ICACHE_ASSOCIATIVITY natural 1 Associativity (= number of sets) of the instruction cache. Has to be a power of two. Allowed configurations: `1` = 1 set, direct mapped; `2` = 2-way set-associative. Has no effect when [_icache_dmem_en] is false.

#### 2.2.9. External Memory Interface

##### MEM_EXT_EN
 MEM_EXT_EN boolean false Implement external bus interface (WISHBONE) when true.
##### MEM_EXT_TIMEOUT
 MEM_EXT_TIMEOUT natural 255 Clock cycles after which a pending external bus access will auto-terminate and raise a bus fault exception. If set to zero, there will be no auto-timeout and no bus fault exception (might permanently stall system!).
##### MEM_EXT_PIPE_MODE
 MEM_EXT_PIPE_MODE boolean false Use standard ("classic") Wishbone protocol for external bus when false. Use pipelined Wishbone protocol when true.
##### MEM_EXT_BIG_ENDIAN
 MEM_EXT_BIG_ENDIAN boolean false Use BIG endian interface for external bus when true. Use little endian interface when false.
##### MEM_EXT_ASYNC_RX
 MEM_EXT_ASYNC_RX boolen false By default, MEM_EXT_ASYNC_RX = false implements a registered read-back path (RX) for incoming data in the bus interface in order to shorten the critical path. By setting MEM_EXT_ASYNC_RX = true an asynchronous ("direct") read-back path is implemented reducing access latency by one cycle but eventually increasing the critical path.

 SLINK_NUM_TX natural 0 Number of TX (send) links to implement. Valid values are 0..8.
 SLINK_TX_FIFO natural 1 Internal FIFO depth for all implemented TX links. Valid values are 1..32k and have to be a power of two.
 SLINK_RX_FIFO natural 1 Internal FIFO depth for all implemented RX links. Valid values are 1..32k and have to be a power of two.

#### 2.2.11. External Interrupt Controller

##### XIRQ_NUM_CH
 XIRQ_NUM_CH natural 0 Number of external interrupt channels o implement. Valid values are 0..32.
##### XIRQ_TRIGGER_TYPE
 XIRQ_TRIGGER_TYPE std_ulogic_vector(31 downto 0) 0xFFFFFFFF Interrupt trigger type configuration (one bit for each IRQ channel): `0` = level-triggered, '1' = edge triggered. XIRQ_TRIGGER_POLARITY generic is used to specify the actual level (high/low) or edge (falling/rising).
##### XIRQ_TRIGGER_POLARITY
 XIRQ_TRIGGER_POLARITY std_ulogic_vector(31 downto 0) 0xFFFFFFFF Interrupt trigger polarity configuration (one bit for each IRQ channel): `0` = low-level/falling-edge, '1' = high-level/rising-edge. XIRQ_TRIGGER_TYPE generic is used to specify the actual type (level or edge).

#### 2.2.12. Processor Peripheral/IO Modules

##### IO_GPIO_EN
 IO_GPIO_EN boolean false Implement general purpose input/output port unit (GPIO) when true. See section General Purpose Input and Output Port (GPIO) for more information.
##### IO_MTIME_EN
 IO_MTIME_EN boolean false Implement machine system timer (MTIME) when true. See section Machine System Timer (MTIME) for more information.
##### IO_UART0_EN
 IO_UART0_EN boolean false Implement primary universal asynchronous receiver/transmitter (UART0) when true. See section Primary Universal Asynchronous Receiver and Transmitter (UART0) for more information.
##### IO_UART1_EN
 IO_UART1_EN boolean false Implement secondary universal asynchronous receiver/transmitter (UART1) when true. See section Secondary Universal Asynchronous Receiver and Transmitter (UART1) for more information.
##### IO_SPI_EN
 IO_SPI_EN boolean false Implement serial peripheral interface controller (SPI) when true. See section Serial Peripheral Interface Controller (SPI) for more information.
##### IO_TWI_EN
 IO_TWI_EN boolean false Implement two-wire interface controller (TWI) when true. See section Two-Wire Serial Interface Controller (TWI) for more information.
##### IO_PWM_NUM_CH
 IO_PWM_NUM_CH natural 0 Number of pulse-width modulation (PWM) channels (0..60) to implement. The PWM controller is not implemented if zero. See section Pulse-Width Modulation Controller (PWM) for more information.
##### IO_WDT_EN
 IO_WDT_EN boolean false Implement watchdog timer (WDT) when true. See section Watchdog Timer (WDT) for more information.
##### IO_TRNG_EN
 IO_TRNG_EN boolean false Implement true-random number generator (TRNG) when true. See section True Random-Number Generator (TRNG) for more information.
##### IO_CFS_EN
 IO_CFS_EN boolean false Implement custom functions subsystem (CFS) when true. See section Custom Functions Subsystem (CFS) for more information.
##### IO_CFS_CONFIG
 IO_CFS_CONFIG std_ulogic_vector(31 downto 0) 0x"00000000" This is a "conduit" generic that can be used to pass user-defined CFS implementation flags to the custom functions subsystem entity. See section Custom Functions Subsystem (CFS) for more information.
##### IO_CFS_IN_SIZE
 IO_CFS_IN_SIZE positive 32 Defines the size of the CFS input signal conduit (`cfs_in_i`). See section Custom Functions Subsystem (CFS) for more information.
##### IO_CFS_OUT_SIZE
 IO_CFS_OUT_SIZE positive 32 Defines the size of the CFS output signal conduit (`cfs_out_o`). See section Custom Functions Subsystem (CFS) for more information.
##### IO_NEOLED_EN
 IO_NEOLED_EN boolean false Implement smart LED interface (WS2812 / NeoPixel™-compatible) (NEOLED) when true. See section Smart LED Interface (NEOLED) for more information.
##### IO_NEOLED_TX_FIFO
 IO_NEOLED_TX_FIFO natural 1 TX FIFO depth of the the NEOLED module. Minimal value is 1, maximal value is 32k, has to be a power of two. See section Smart LED Interface (NEOLED) for more information.

### 2.3. Processor Interrupts

The NEORV32 Processor provides several interrupt request signals (IRQs) for custom platform use.

#### 2.3.1. RISC-V Standard Interrupts

The processor setup features the standard machine-level RISC-V interrupt lines for "machine timer interrupt", "machine software interrupt" and "machine external interrupt". Their usage is defined by the RISC-V privileged architecture specifications. However, bare-metal system can also repurpose these interrupts. See CPU section Traps, Exceptions and Interrupts for more information.

Top signal Width Description

`mtime_irq_i`

1

Machine timer interrupt from processor-external MTIME unit. This IRQ is only available if the processor-internal MTIME unit is not used (IO_MTIME_EN = false).

`msw_irq_i`

1

Machine software interrupt. This interrupt is used for inter-processor interrupts in multi-core systems. However, it can also be used for any custom purpose.

`mext_irq_i`

1

Machine external interrupt. This interrupt is used for any processor-external interrupt source (like a platform interrupt controller).

 Trigger type These IRQs trigger on high-level and must stay asserted until explicitly acknowledged by the CPU (for example by writing to a specific memory-mapped register).

#### 2.3.2. Platform External Interrupts

Top signal Width Description

`xirq_i`

up to 32

External platform interrupts (user-defined).

The processor provides an optional interrupt controller for up to 32 user-defined external interrupts (see section External Interrupt Controller (XIRQ)). These external IRQs are mapped to a single CPU fast interrupt request so a software handler is required to differentiate / prioritize these interrupts.

 Trigger type The trigger for these interrupt can be defined via generics. See section External Interrupt Controller (XIRQ) for more information. Depending on the trigger type, users can implement custom acknowledge mechanisms.

#### 2.3.3. NEORV32-Specific Fast Interrupt Requests

As part of the custom/NEORV32-specific CPU extensions, the CPU features 16 fast interrupt request signals (`FIRQ0``FIRQ15`). These are used for processor-internal modules only (for example for the communication interfaces to signal "available incoming data" or "ready to send new data").

The mapping of the 16 FIRQ channels is shown in the following table (the channel number also corresponds to the according FIRQ priority; 0 = highest, 15 = lowest):

Table 5. NEORV32 fast interrupt channel mapping
Channel Source Description

0

WDT

watchdog timeout interrupt

1

CFS

custom functions subsystem (CFS) interrupt (user-defined)

2

UART0

UART0 data received interrupt (RX complete)

3

UART0

UART0 sending done interrupt (TX complete)

4

UART1

UART1 data received interrupt (RX complete)

5

UART1

UART1 sending done interrupt (TX complete)

6

SPI

SPI transmission done interrupt

7

TWI

TWI transmission done interrupt

8

XIRQ

External interrupt controller interrupt

9

NEOLED

NEOLED buffer TX empty / not full interrupt

10

11

TX data send

12:15

-

reserved, will never fire

 Trigger type The fast interrupt request channel trigger on a single rising-edge and do not require any kind of explicit acknowledgment at all.

The NEORV32 Processor provides 32-bit physical addresses accessing up to 4GB of address space. By default, this address space is divided into four main regions:

1. Instruction address space – for instructions (=code) and constants. A configurable section of this address space is used by internal and/or external instruction memory (IMEM).

2. Data address space – for application runtime data (heap, stack, etc.). A configurable section of this address space is used by internal and/or external data memory (DMEM).

4. IO/peripheral address space – for the processor-internal IO/peripheral devices (e.g., UART).

 These four memory regions are handled by the linker when compiling a NEORV32 executable. See section Executable Image Format for more information.
Figure 1. NEORV32 processor - address space (default configuration)

#### 2.4.1. CPU Data and Instruction Access

The CPU can access all of the 4GB address space from the instruction fetch interface (I) and also from the data access interface (D). These two CPU interfaces are multiplexed by a simple bus switch (`rtl/core/neorv32_busswitch.vhd`) into a single processor-internal bus. All processor-internal memories, peripherals and also the external memory interface are connected to this bus. Hence, both CPU interfaces (instruction fetch & data access) have access to the same (identical) address space making the setup a modified von-Neumann architecture.

Figure 2. Processor-internal bus architecture
 The internal processor bus might appear as bottleneck. In order to reduce traffic jam on this bus (when instruction fetch and data interface access the bus at the same time) the instruction fetch of the CPU is equipped with a prefetch buffer. Instruction fetches can be further buffered using the i-cache. Furthermore, data accesses (loads and stores) have higher priority than instruction fetch accesses.
 Please note that all processor-internal components including the peripheral/IO devices can also be accessed from programs running in less-privileged user mode. For example, if the system relies on a periodic interrupt from the MTIME timer unit, user-level programs could alter the MTIME configuration corrupting this interrupt. This kind of security issues can be compensated using the PMP system (see Machine Physical Memory Protection).

The general address space layout consists of two main configuration constants: `ispace_base_c` defining the base address of the instruction memory address space and `dspace_base_c` defining the base address of the data memory address space. Both constants are defined in the NEORV32 VHDL package file `rtl/core/neorv32_package.vhd`:

``````-- Architecture Configuration ----------------------------------------------------
-- ----------------------------------------------------------------------------------
constant ispace_base_c : std_ulogic_vector(31 downto 0) := x"00000000";
constant dspace_base_c : std_ulogic_vector(31 downto 0) := x"80000000";``````

The default configuration assumes the instruction memory address space starting at address 0x00000000 and the data memory address space starting at 0x80000000. Both values can be modified for a specific setup and the address space may overlap or can be completely identical. Make sure that both base addresses are aligned to a 4-byte boundary.

 The base address of the internal bootloader (at 0xFFFF0000) and the internal IO region (at 0xFFFFFE00) for peripheral devices are also defined in the package and are fixed. These address regions cannot not be used for other applications – even if the bootloader or all IO devices are not implemented - without modifying the core’s hardware sources.

#### 2.4.3. Physical Memory Attributes

The processor setup defines fixed attributes for the four processor-internal address space regions. Accessing a memory region in a way that violates any of these attributes will raise an according access exception..

• `r` – read access (from CPU data access interface, e.g. via "load")

• `w` – write access (from CPU data access interface, e.g. via "store")

• `x` – execute access (from CPU instruction fetch interface)

• `a` – atomic access (from CPU data access interface)

• `8` – byte (8-bit)-accessible (when writing)

• `16` – half-word (16-bit)-accessible (when writing)

• `32` – word (32-bit)-accessible (when writing)

 Read accesses (i.e. loads) can always access data in word, half-word and byte quantities (requiring an accordingly aligned address).
# Region Base address Size Attributes

4

IO/peripheral devices

0xfffffe00

512 bytes

`r/w/a/32`

3

0xffff0000

up to 32kB

`r/x/a`

2

DMEM

0x80000000

up to 2GB (-64kB)

`r/w/x/a/8/16/32`

1

IMEM

0x00000000

up to 2GB

`r/w/x/a/8/16/32`

 The following table shows the provided physical memory attributes of each region. Additional attributes (for example controlling certain right for specific address space regions) can be provided using the RISC-V Machine Physical Memory Protection extension.

#### 2.4.4. Memory Configuration

The NEORV32 Processor was designed to provide maximum flexibility for the memory configuration. The processor can populate the instruction address space and/or the data address space with internal memories for instructions (IMEM) and data (DMEM). Processor external memories can be used as an alternative or even in combination with the internal ones. The figure below show some exemplary memory configurations.

Figure 3. Exemplary memory configurations
##### Internal Memories

The processor-internal memories (Instruction Memory (IMEM) and Data Memory (DMEM)) are enabled (=implemented) via the MEM_INT_IMEM_EN and MEM_INT_DMEM_EN generics. Their sizes are configures via the according MEM_INT_IMEM_SIZE and MEM_INT_DMEM_SIZE generics.

If the processor-internal IMEM is implemented, it is located right at the base address of the instruction address space (default `ispace_base_c` = 0x00000000). Vice versa, the processor-internal data memory is located right at the beginning of the data address space (default `dspace_base_c` = 0x80000000) when implemented.

 The default processor setup uses only internal memories.
 If the IMEM (internal or external) is less than the (default) maximum size (2GB), there is a "dead address space" between it and the DMEM. This provides an additional safety feature since data corrupting scenarios like stack overflow cannot directly corrupt the content of the IMEM: any access to the "dead address space" in between will raise an exception that can be caught by the runtime environment.
##### External Memories

If external memories (or further IP modules) shall be connected via the processor’s external bus interface, the interface has to be enabled via MEM_EXT_EN generic (=true). More information regarding this interface can be found in section Processor-External Memory Interface (WISHBONE) (AXI4-Lite).

Any CPU access (data or instructions), which does not fulfill at least one of the following conditions, is forwarded via the processor’s bus interface to external components:

• access to the bootloader ROM and beyond → addresses >= BOOTROM_BASE (default 0xFFFF0000) will never be forwarded to the external memory interface

If no (or not all) processor-internal memories are implemented, the according base addresses are mapped to external memories. For example, if the processor-internal IMEM is not implemented (MEM_INT_IMEM_EN = false), the processor will forward any access to the instruction address space (starting at `ispace_base_c`) via the external bus interface to the external memory system.

 If the external interface is deactivated, any access exceeding the internal memory address space (instruction, data, bootloader) or the internal peripheral address space will trigger a bus access fault exception.

#### 2.4.5. Boot Configuration

Due to the flexible memory configuration concept, the NEORV32 Processor provides several different boot concepts. The figure below shows the exemplary concepts for the two most common boot scenarios.

Figure 4. NEORV32 boot configurations
 The configuration of internal or external data memory (DMEM; MEM_INT_DMEM_EN = true / false) is not further relevant for the boot configuration itself. Hence, it is not further illustrated here.

There are two general boot scenarios: Indirect Boot (1a and 1b) and Direct Boot (2a and 2b) configured via the INT_BOOTLOADER_EN generic If this generic is set true the indirect boot scenario is used. This is also the default boot configuration of the processor. If INT_BOOTLOADER_EN is set false the direct boot scenario is used.

 Please note that the provided boot scenarios are just exemplary setups that (should) fit most common requirements. Much more sophisticated boot scenarios are possible by combining internal and external memories. For example, the default internal bootloader could be used as first-level bootloader that loads (from extern SPI flash) a second-level bootloader that is placed and execute in internal IMEM. This second-level bootloader could then fetch the actual application and store it to external data memory and transfers CPU control to that.
##### Indirect Boot

The indirect boot scenarios 1a and 1b use the processor-internal Bootloader. This general setup is enabled by setting the INT_BOOTLOADER_EN generic to true, which will implement the processor-internal Bootloader ROM (BOOTROM). This read-only memory is pre-initialized during synthesis with the default bootloader firmware.

The bootloader provides several options to upload an executable (via UART or from external SPI flash) and store it to the instruction address space so the CPU can execute it. Boot scenario 1a uses the processor-internal IMEM (MEM_INT_IMEM_EN = true). This scenario implements the internal Instruction Memory (IMEM) as non-initialized RAM so the bootloader can write the actual executable to it.

Boot scenario 1b uses a processor-external IMEM (MEM_INT_IMEM_EN = false) that is connected via the processor’s bus interface. In this scenario the internal Instruction Memory (IMEM) is not implemented at all and the bootloader will write the executable to the processor-external memory.

##### Direct Boot

The direct boot scenarios 2a and 2b do not use the processor-internal bootloader. Hence, the INT_BOOTLOADER_EN generic is set false. In this configuration the Bootloader ROM (BOOTROM) is not implemented at all and the CPU will directly begin executing code from the instruction address space after reset. A "pre-initialization mechanism is required in order to provide an executable in memory.

Boot scenario 2a uses the processor-internal IMEM (MEM_INT_IMEM_EN = true) that is implemented as read-only memory in this scenario. It is pre-initialized (by the bitstream) with the actual application executable.

In contrast, boot scenario 2b uses a processor-external IMEM (MEM_INT_IMEM_EN = false). In this scenario the system designer is responsible for providing a initialized external memory that contains the actual application to be executed.

### 2.5. Processor-Internal Modules

Basically, the processor is a SoC consisting of the NEORV32 CPU, peripheral/IO devices, embedded memories, an external memory interface and a bus infrastructure to interconnect all units. Additionally, the system implements an internal reset generator and a global clock generator/divider.

Internal Reset Generator

Most processor-internal modules – except for the CPU and the watchdog timer – do not have a dedicated reset signal. However, all devices can be reset by software by clearing the corresponding unit’s control register. The automatically included application start-up code (`crt0.S`) will perform a software-reset of all modules to ensure a clean system reset state.

The hardware reset signal of the processor can either be triggered via the external reset pin (`rstn_i`, low-active) or by the internal watchdog timer (if implemented). Before the external reset signal is applied to the system, it is extended to have a minimal duration of eight clock cycles.

Internal Clock Divider

An internal clock divider generates 8 clock signals derived from the processor’s main clock input `clk_i`. These derived clock signals are not actual clock signals. Instead, they are derived from a simple counter and are used as "clock enable" signal by the different processor modules. Thus, the whole design operates using only the main clock signal (single clock domain). Some of the processor peripherals like the Watchdog or the UARTs can select one of the derived clock enabled signals for their internal operation. If none of the connected modules require a clock signal from the divider, it is automatically deactivated to reduce dynamic power.

The peripheral devices, which feature a time-based configuration, provide a three-bit prescaler select in their according control register to select one out of the eight available clocks. The mapping of the prescaler select bits to the actually obtained clock are shown in the table below. Here, f represents the processor main clock from the top entity’s `clk_i` signal.

 Prescaler bits: `0b000` `0b001` `0b010` `0b011` `0b100` `0b101` `0b110` `0b111` Resulting clock: f/2 f/4 f/8 f/64 f/128 f/1024 f/2048 f/4096

Peripheral / IO Devices

The processor-internal peripheral/IO devices are located at the end of the 32-bit address space at base address 0xFFFFFE00. A region of 512 bytes is reserved for this devices. Hence, all peripheral/IO devices are accessed using a memory-mapped scheme. A special linker script as well as the NEORV32 core software library abstract the specific memory layout for the user.

 The base address of each component/module has to be aligned to the total size of the module’s occupied address space! The occupied address space has to be a power of two (minimum 4 bytes)! Address spaces must not overlap!
 When accessing an IO device that hast not been implemented (via the according IO_x_EN generic), a load/store access fault exception is triggered.
 The peripheral/IO devices can only be written in full-word mode (i.e. 32-bit). Byte or half-word (8/16-bit) writes will trigger a store access fault exception. Read accesses are not size constrained. Processor-internal memories as well as modules connected to the external memory interface can still be written with a byte-wide granularity.
 You should use the provided core software library to interact with the peripheral devices. This prevents incompatibilities with future versions, since the hardware driver functions handle all the register and register bit accesses.
 Most of the IO devices do not have a hardware reset. Instead, the devices are reset via software by writing zero to the unit’s control register. A general software-based reset of all devices is done by the application start-up code `crt0.S`.

Interrupts of Processor-Internal Modules

Most peripheral/IO devices provide some kind of interrupt (for example to signal available incoming data). These interrupts are entirely mapped to the CPU’s Custom Fast Interrupt Request Lines. Note that all these interrupt lines are triggered by a "one-shot" signal (hich for exactly one cycle) and do not require any explicit acknowledgment.

Nomenclature for the Peripheral / IO Devices Listing

Each peripheral device chapter features a register map showing accessible control and data registers of the according device including the implemented control and status bits. C-language code can directly interact with these registers via pre-defined `struct`. Each IO/peripheral module provides a unique `struct`. All accessible interface registers of this module are defined as members of this `struct`. The pre-defined `struct` are defined int the main processor core library include file `sw/lib/include/neorv32.h`.

The naming scheme of these low-level hardware access structs is `NEORV32_<module_name>.<register_name>`.

Listing 1. Low-level hardware access example in C using the pre-defined `struct`
``````// Read from SYSINFO "CLK" register
uint32_t temp = NEORV32_SYSINFO.CLK;``````

The registers and/or register bits, which can be accessed directly using plain C-code, are marked with a "[C]". Not all registers or register bits can be arbitrarily read/written. The following read/write access types are available:

• `r/w` registers / bits can be read and written

• `r/-` registers / bits are read-only; any write access to them has no effect

• `-/w` these registers / bits are write-only; they auto-clear in the next cycle and are always read as zero

 Bits / registers that are not listed in the register map tables are not (yet) implemented. These registers / bits are always read as zero. A write access to them has no effect, but user programs should only write zero to them to keep compatible with future extension.
 When writing to read-only registers, the access is nevertheless acknowledged, but no actual data is written. When reading data from a write-only register the result is undefined.

#### 2.5.1. Instruction Memory (IMEM)

 Hardware source file(s): neorv32_imem.entity.vhd entity-only definition mem/neorv32_imem.default.vhd default platform-agnostic memory architecture Software driver file(s): none implicitly used Top entity port: none Configuration generics: MEM_INT_IMEM_EN implement processor-internal IMEM when true MEM_INT_IMEM_SIZE IMEM size in bytes INT_BOOTLOADER_EN use internal bootlodaer when true (implements IMEM as uninitialized RAM) CPU interrupts: none
 The actual IMEM is split into two design files: a plain entity definition (`neorv32_imem.entity.vhd`) and the actual architecture definition (`mem/neorv32_imem.default.vhd`). This default architecture provides a generic and platform independent memory design that (should) infers embedded memory block. You can replace/modify the architecture source file in order to use platform-specific features (like advanced memory resources) or to improve technology mapping and/or timing.

Implementation of the processor-internal instruction memory is enabled via the processor’s MEM_INT_IMEM_EN generic. The size in bytes is defined via the MEM_INT_IMEM_SIZE generic. If the IMEM is implemented, the memory is mapped into the instruction memory space and located right at the beginning of the instruction memory space (default `ispace_base_c` = 0x00000000).

By default, the IMEM is implemented as RAM, so the content can be modified during run time. This is required when using a bootloader that can update the content of the IMEM at any time. If you do not need the bootloader anymore – since your application development has completed and you want the program to permanently reside in the internal instruction memory – the IMEM is automatically implemented as pre-intialized ROM when the processor-internal bootloader is disabled (INT_BOOTLOADER_EN = false).

When the IMEM is implemented as ROM, it will be initialized during synthesis with the actual application program image. The compiler toolchain will generate a VHDL initialization file `rtl/core/neorv32_application_image.vhd`, which is automatically inserted into the IMEM. If the IMEM is implemented as RAM (default), the memory will not be initialized at all.

#### 2.5.2. Data Memory (DMEM)

 Hardware source file(s): neorv32_dmem.entity.vhd entity-only definition mem/neorv32_dmem.default.vhd default platform-agnostic memory architecture Software driver file(s): none implicitly used Top entity port: none Configuration generics: MEM_INT_DMEM_EN implement processor-internal DMEM when true MEM_INT_DMEM_SIZE DMEM size in bytes CPU interrupts: none
 The actual DMEM is split into two design files: a plain entity definition (`neorv32_dmem.entity.vhd`) and the actual architecture definition (`mem/neorv32_dmem.default.vhd`). This default architecture provides a generic and platform independent memory design that (should) infers embedded memory block. You can replace/modify the architecture source file in order to use platform-specific features (like advanced memory resources) or to improve technology mapping and/or timing.

Implementation of the processor-internal data memory is enabled via the processor’s MEM_INT_DMEM_EN generic. The size in bytes is defined via the MEM_INT_DMEM_SIZE generic. If the DMEM is implemented, the memory is mapped into the data memory space and located right at the beginning of the data memory space (default `dspace_base_c` = 0x80000000). The DMEM is always implemented as RAM.

 Hardware source file(s): neorv32_boot_rom.vhd Software driver file(s): none implicitly used Top entity port: none Configuration generics: INT_BOOTLOADER_EN implement processor-internal bootloader when true CPU interrupts: none
 The default `neorv32_boot_rom.vhd` HDL source file provides a generic memory design that infers embedded memory for larger memory configurations. You might need to replace/modify the source file in order to use platform-specific features (like advanced memory resources) or to improve technology mapping and/or timing.

This HDL modules provides a read-only memory that contain the executable code image of the bootloader. If the INT_BOOTLOADER_EN generic is true this module will be implemented and the CPU boot address is modified to directly execute the code from the bootloader ROM after reset.

The bootloader ROM is located at address `0xFFFF0000` and can occupy a address space of up to 32kB. The base address as well as the maximum address space size are fixed and cannot (should not!) be modified as this might address collision with other processor modules.

The bootloader memory is read-only and is automatically initialized with the bootloader executable image `rtl/core/neorv32_bootloader_image.vhd` during synthesis. The actual physical size of the ROM is also determined via synthesis and expanded to the next power of two. For example, if the bootloader code requires 10kB of storage, a ROM with 16kB will be generated. The maximum size must not exceed 32kB.

 Boot Configuration See section Boot Configuration for more information regarding the processor’s different boot scenarios.

#### 2.5.4. Processor-Internal Instruction Cache (iCACHE)

 Hardware source file(s): neorv32_icache.vhd Software driver file(s): none implicitly used Top entity port: none Configuration generics: ICACHE_EN implement processor-internal instruction cache when true ICACHE_NUM_BLOCKS number of cache blocks (pages/lines) ICACHE_BLOCK_SIZE size of a cache block in bytes ICACHE_ASSOCIATIVITY associativity / number of sets CPU interrupts: none
 The default `neorv32_icache.vhd` HDL source file provides a generic memory design that infers embedded memory. You might need to replace/modify the source file in order to use platform-specific features (like advanced memory resources) or to improve technology mapping and/or timing.

The processor features an optional cache for instructions to compensate memories with high latency. The cache is directly connected to the CPU’s instruction fetch interface and provides a full-transparent buffering of instruction fetch accesses to the entire 4GB address space.

 The instruction cache is intended to accelerate instruction fetch via the external memory interface. Since all processor-internal memories provide an access latency of one cycle (by default), caching internal memories does not bring any performance gain. However, it might reduce traffic on the processor-internal bus.

The cache is implemented if the ICACHE_EN generic is true. The size of the cache memory is defined via ICACHE_BLOCK_SIZE (the size of a single cache block/page/line in bytes; has to be a power of two and >= 4 bytes), ICACHE_NUM_BLOCKS (the total amount of cache blocks; has to be a power of two and >= 1) and the actual cache associativity ICACHE_ASSOCIATIVITY (number of sets; 1 = direct-mapped, 2 = 2-way set-associative, has to be a power of two and >= 1).

If the cache associativity (ICACHE_ASSOCIATIVITY) is > 1 the LRU replacement policy (least recently used) is used.

 Keep the features of the targeted FPGA’s memory resources (block RAM) in mind when configuring the cache size/layout to maximize and optimize resource utilization.

By executing the `ifence.i` instruction (`Zifencei` CPU extension) the cache is cleared and a reload from main memory is forced. Among other things, this allows to implement self-modifying code.

Bus Access Fault Handling

The cache always loads a complete cache block (ICACHE_BLOCK_SIZE bytes) aligned to the size of a cache block if a miss is detected. If any of the accessed addresses within a single block do not successfully acknowledge (i.e. issuing an error signal or timing out) the whole cache block is invalidate and any access to an address within this cache block will also raise an instruction fetch bus error fault exception.

#### 2.5.5. Processor-External Memory Interface (WISHBONE) (AXI4-Lite)

 Hardware source file(s): neorv32_wishbone.vhd Software driver file(s): none implicitly used Top entity port: `wb_tag_o` request tag output (3-bit) `wb_adr_o` address output (32-bit) `wb_dat_i` data input (32-bit) `wb_dat_o` data output (32-bit) `wb_we_o` write enable (1-bit) `wb_sel_o` byte enable (4-bit) `wb_stb_o` strobe (1-bit) `wb_cyc_o` valid cycle (1-bit) `wb_lock_o` exclusive access request (1-bit) `wb_ack_i` acknowledge (1-bit) `wb_err_i` bus error (1-bit) `fence_o` an executed `fence` instruction `fencei_o` an executed `fence.i` instruction Configuration generics: MEM_EXT_EN enable external memory interface when true MEM_EXT_TIMEOUT number of clock cycles after which an unacknowledged external bus access will auto-terminate (0 = disabled) MEM_EXT_PIPE_MODE when false (default): classic/standard Wishbone protocol; when true: pipelined Wishbone protocol MEM_EXT_BIG_ENDIAN byte-order (Endianness) of external memory interface; true=BIG, false=little (default) MEM_EXT_ASYNC_RX use registered RX path when false (default); use async/direct RX path when true CPU interrupts: none

The external memory interface uses the Wishbone interface protocol. The external interface port is available when the MEM_EXT_EN generic is true. This interface can be used to attach external memories, custom hardware accelerators additional IO devices or all other kinds of IP blocks. All memory accesses from the CPU, that do not target the internal bootloader ROM, the internal IO region or the internal data/instruction memories (if implemented at all) are forwarded to the Wishbone gateway and thus to the external memory interface.

 When using the default processor setup, all access addresses between 0x00000000 and 0xffff0000 (= beginning of processor-internal BOOT ROM) are delegated to the external memory / bus interface if they are not targeting the (actually enabled/implemented) processor-internal instruction memory (IMEM) or the (actually enabled/implemented) processor-internal data memory (DMEM). See section Address Space for more information.

Wishbone Bus Protocol

The external memory interface either uses standard ("classic") Wishbone transactions (default) or pipelined Wishbone transactions. The transaction protocol is configured via the MEM_EXT_PIPE_MODE generic:

When MEM_EXT_PIPE_MODE is false, all bus control signals including STB are active (and stable) until the transfer is acknowledged/terminated. If MEM_EXT_PIPE_MODE is true, all bus control except STB are active (and stable) until the transfer is acknowledged/terminated. In this case, STB is active only during the very first bus clock cycle.

 Classic Wishbone read access Pipelined Wishbone write access

A detailed description of the implemented Wishbone bus protocol and the according interface signals can be found in the data sheet "Wishbone B4 – WISHBONE System-on-Chip (SoC) Interconnection Architecture for Portable IP Cores". A copy of this document can be found in the docs folder of this project.

Interface Latency

By default, the Wishbone gateway introduces two additional latency cycles: processor-outgoing ("TX") and processor-incoming ("RX") signals are fully registered. Thus, any access from the CPU to a processor-external devices via Wishbone requires 2 additional clock cycles (at least; depending on device’s latency).

If the attached Wishbone network / peripheral already provides output registers or if the Wishbone network is not relevant for timing closure, the default buffering of incoming ("RX") data within the gateway can be disabled by implementing an "asynchronous" RX path. The configuration is done via the MEM_EXT_ASYNC_RX generic.

Bus Access Timeout

The Wishbone bus interface provides an option to configure a bus access timeout counter. The MEM_EXT_TIMEOUT top generic is used to specify the maximum time (in clock cycles) a bus access can be pending before it is automatically terminated. If MEM_EXT_TIMEOUT is set to zero, the timeout disabled an a bus access can take an arbitrary number of cycles to complete.

When MEM_EXT_TIMEOUT is greater than zero, the WIshbone adapter starts an internal countdown whenever the CPU accesses a memory address via the external memory interface. If the accessed memory / device does not acknowledge (via `wb_ack_i`) or terminate (via `wb_err_i`) the transfer within MEM_EXT_TIMEOUT clock cycles, the bus access is automatically canceled (setting `wb_cyc_o` low again) and a load/store/instruction fetch bus access fault exception is raised.

 This feature can be used as safety guard if the external memory system does not check for "address space holes". That means that addresses, which do not belong to a certain memory or device, do not permanently stall the processor due to an unacknowledged/unterminated bus access. If the external memory system can guarantee to access any bus access (even it targets an unimplemented address) the timeout feature should be disabled (MEM_EXT_TIMEOUT = 0).

Wishbone Tag

The 3-bit wishbone `wb_tag_o` signal provides additional information regarding the access type. This signal is compatible to the AXI4 AxPROT signal.

• `wb_tag_o(0)` 1: privileged access (CPU is in machine mode); 0: unprivileged access

• `wb_tag_o(1)` always zero (indicating "secure access")

• `wb_tag_o(2)` 1: instruction fetch access, 0: data access

Exclusive / Atomic Bus Access

If the atomic memory access CPU extension (via CPU_EXTENSION_RISCV_A) is enabled, the CPU can request an atomic/exclusive bus access via the external memory interface.

The load-reservate instruction (`lr.w`) will set the `wb_lock_o` signal telling the bus interconnect to establish a reservation for the current accessed address (start of an exclusive access). This signal will stay asserted until another memory access instruction is executed (for example a `sc.w`).

The memory system has to make sure that no other entity can access the reservated address until `wb_lock_o` is released again. If this attempt fails, the memory system has to assert `wb_err_i` in order to indicate that the reservation was broken.

 See section Bus Interface for the CPU bus interface protocol.

Endianness

The NEORV32 CPU and the Processor setup are little-endian architectures. To allow direct connection to a big-endian memory system the external bus interface provides an Endianness configuration. The Endianness (of the external memory interface) can be configured via the MEM_EXT_BIG_ENDIAN generic. By default, the external memory interface uses little-endian byte-order (like the rest of the processor / CPU).

Application software can check the Endianness configuration of the external bus interface via the SYSINFO module (see section System Configuration Information Memory (SYSINFO) for more information).

AXI4-Lite Connectivity

The AXI4-Lite wrapper (`rtl/system_integration/neorv32_SystemTop_axi4lite.vhd`) provides a Wishbone-to- AXI4-Lite bridge, compatible with Xilinx Vivado (IP packager and block design editor). All entity signals of this wrapper are of type std_logic or std_logic_vector, respectively.

The AXI Interface has been verified using Xilinx Vivado IP Packager and Block Designer. The AXI interface port signals are automatically detected when packaging the core.

Figure 5. Example AXI SoC using Xilinx Vivado
 Using the auto-termination timeout feature (MEM_EXT_TIMEOUT greater than zero) is not AXI4 compliant as the AXI protocol does not support canceling of bus transactions. Therefore, the NEORV32 top wrapper with AXI4-Lite interface (`rtl/system_integration/neorv32_SystemTop_axi4lite`) configures MEM_EXT_TIMEOUT = 0 by default.
 Hardware source file(s): neorv32_slink.vhd Software driver file(s): neorv32_slink.c neorv32_slink.h Top entity port: `slink_tx_dat_o` TX link data (8x32-bit) `slink_tx_val_o` TX link data valid (8-bit) `slink_tx_rdy_i` TX link allowed to send (8-bit) `slink_rx_dat_i` RX link data (8x32-bit) `slink_rx_val_i` RX link data valid (8-bit) `slink_rx_rdy_o` RX link ready to receive (8-bit) Configuration generics: SLINK_NUM_TX Number of TX links to implement (0..8) SLINK_NUM_RX Number of RX links to implement (0..8) SLINK_TX_FIFO FIFO depth (1..32k) of TX links, has to be a power of two SLINK_RX_FIFO FIFO depth (1..32k) of RX links, has to be a power of two CPU interrupts: fast IRQ channel 10 SLINK RX IRQ (see Processor Interrupts) fast IRQ channel 11 SLINK TX IRQ (see Processor Interrupts)

The SLINK component provides up to 8 independent RX (receiving) and TX (sending) links for transmitting stream data. The interface provides higher bandwidth (and less latency) than the external memory bus interface, which makes it ideally suited to couple custom stream processing units (like CORDIC, FFTs or cryptographic accelerators).

Each individual link provides an internal FIFO for data buffering. The FIFO depth is globally defined for all TX links via the SLINK_TX_FIFO generic and for all RX links via the SLINK_RX_FIFO generic. The FIFO depth has to be at least 1, which will implement a simple input/output register. The maximum value is limited to 32768 entries. Note that the FIFO depth has to be a power of two (for optimal logic mapping).

The actual number of implemented RX/TX links is configured by the SLINK_NUM_RX and SLINK_NUM_TX generics. The SLINK module will be synthesized only if at least one of these generics is greater than zero. All unimplemented links are internally terminated and their according output signals are pulled to low level.

 The SLINK interface does not provide any additional tag signals (for example to define a "stream destination address" or to indicate the last data word of a "package"). Use a custom controller connected via the external memory bus interface or use some of the processor’s GPIO ports to implement custom data tag signals.

Theory of Operation

The SLINK provides eight data registers (`DATA[i]`) to access the links (read accesses will access the RX links, write accesses will access the TX links), one control register (`CTRL`) and one status register (`STATUS`).

The SLINK is globally activated by setting the control register’s enable bit SLINK_CTRL_EN. The actual data links are accessed by reading or writing the according link data registers `DATA[0]` to `DATA[7]`. For example, writing the `DATA[0]` will put the according data into the FIFO of TX link 0. Accordingly, reading from `DATA[0]` will return one data word from the FIFO of RX link 0.

The configuration (done via the SLINK generics) can be checked by software by evaluating bit fields in the control register. The SLINK_CTRL_TX_FIFO_Sx and SLINK_CTRL_RX_FIFO_Sx indicate the TX & RX FIFO sizes. The SLINK_CTRL_TX_NUMx and SLINK_CTRL_RX_NUMx bits represent the absolute number of implemented TX and RX links.

The status register shows the FIFO status flags of each RX and TX link. The SLINK_CTRL_RXx_AVAIL flags indicate that there is at least one data word in the according RX link’s FIFO. The SLINK_CTRL_TXx_FREE flags indicate there is at least one free entry in the according TX link’s FIFO. The SLINK_STATUS_RXx_HALF and SLINK_STATUS_RXx_HALF flags show if a certain FIFO’s fill level has exceeded half of its capacity.

When directly accessing the link data registers (without checking the according FIFO status flags) the access is as blocking. That means the CPU access will stall until the accessed link responds. For example, when reading RX link 0 (via `DATA[0]` register) the CPU will stall, if there is not data available in the according FIFO yet. The CPU access will complete as soon as RX link 0 receives new data.

Vice versa, writing data to TX link 0 (via `DATA[0]` register) will stall the CPU access until there is at least one free entry in the link’s FIFO.

For a non-blocking link access concept, the FIFO status flags in `STATUS` need to be checked before reading/writing the actual link data register. For example, a non-blocking write access to a TX link 0 has to check SLINK_STATUS_TX0_FREE first. If the bit is set, the FIFO of TX link 0 can take another data word and the actual data can be written to `DATA[0]`. If the bit is cleared, the link’s FIFO is full and the status flag can be polled until it there is free space in the available.

This concept will not raise any exception as there is no "direct" access to the link data registers. However, non-blocking accesses require additional instructions to check the according status flags prior to the actual link access, which will reduce performance for high-bandwidth data streams.

Interrupts

The stream interface provides two interrupts that are globally driven by the RX and TX link’s FIFO fill level status. The behavior of these interrupts differs if the FIFO depth is exactly 1 (minimal) or if it is greater than 1.

When SLINK*X_FIFO_ is 1 a TX interrupt will fire if any TX link was full and becomes empty again. Accordingly, if the FIFO of any RX link was empty and a new data word appears in it, the RX interrupt fires.

When SLINK*X_FIFO_ is greater than 1 the TX interrupt will fire if any TX link’s FIFO falls below half-full fill level. Accordingly, the RX interrupt will fire if any RX link’s FIFO exceeds half-full fill level.

The interrupt service handler has to evaluate the SLINK status register is order to detect which link(s) has caused the interrupt. No further interrupt can fire until the CPU acknowledges the last interrupt by reading the SLINK status register. However, further IRQ conditions are buffered and will trigger another interrupt after the current one has been acknowledged.

Note that these interrupts can only fire if the SLINK module is actually enabled by setting the SLINK_CTRL_EN bit in the unit’s control register.

The SLINK interface consists of three signals `dat`, `val` and `rdy` for each RX and TX link. Each signal is an "array" with eight entires (one for each link). Note that an entry in `slink_*x_dat` is 32-bit wide while entries in `slink_*x_val` and `slink_*x_rdy` are are just 1-bit wide.

The stream link protocol is based on a simple FIFO-like interface between a source (sender) and a sink (receiver). Each link provides two signals for implementing a simple FIFO-style handshake. The `slink_*x_val` signal is set by the source if the according `slink_*x_dat` (also set by the source) contains valid data. The stream source has to ensure that both signals remain stable until the according `slink_*x_rdy` signal is set by the stream sink to indicate it can accept another data word.

In summary, a data word is transferred if both `slink_*x_val(i)` and `slink_*x_rdy(i)` are high.

Figure 6. Exemplary stream link transfer
 The SLINK handshake protocol is compatible with the AXI4-Stream base protocol.
Table 7. SLINK register map (`struct NEORV32_SLINK`)
Address Name [C] Bit(s) R/W Function

`0xfffffec0`

`NEORV32_SLINK.CTRL`

`31` SLINK_CTRL_EN

r/w

`30:16` reserved

r/-

`15:12` SLINK_CTRL_TX_FIFO_S3 : SLINK_CTRL_TX_FIFO_S0

r/-

`11:8` SLINK_CTRL_RX_FIFO_S3 : SLINK_CTRL_RX_FIFO_S0

r/-

`7:4` SLINK_CTRL_TX_NUM3 : SLINK_CTRL_TX_NUM0

r/-

`3:0` SLINK_CTRL_RX_NUM3 : SLINK_CTRL_RX_NUM0

r/-

`0xfffffec4` : `0xfffffeec`

-

`31:0`

_reserved

`0xfffffed0`

`NEORV32_SLINK.STATUS`

`31:24` SLINK_STATUS_TX7_HALF : SLINK_STATUS_TX0_HALF

r/-

TX link 7..0 FIFO fill level is > half-full

`23:16` SLINK_STATUS_RX7_HALF : SLINK_STATUS_RX0_HALF

r/-

RX link 7..0 FIFO fill level is >= half-full

`15:8` SLINK_STATUS_TX7_FREE : SLINK_STATUS_TX0_FREE

r/-

At least one free TX FIFO entry available for link 7..0

`7:0` SLINK_STATUS_RX7_AVAIL : SLINK_STATUS_RX0_AVAIL

r/-

At least one data word in RX FIFO available for link 7..0

`0xfffffed4` : `0xfffffedc`

-

`31:0`

reserved

`0xfffffee0`

`NEORV32_SLINK.DATA[0]`

`31:0`

r/w

`0xfffffee4`

`NEORV32_SLINK.DATA[1]`

`31:0`

r/w

`0xfffffee8`

`NEORV32_SLINK.DATA[2]`

`31:0`

r/w

`0xfffffeec`

`NEORV32_SLINK.DATA[3]`

`31:0`

r/w

`0xfffffef0`

`NEORV32_SLINK.DATA[4]`

`31:0`

r/w

`0xfffffef4`

`NEORV32_SLINK.DATA[5]`

`31:0`

r/w

`0xfffffef8`

`NEORV32_SLINK.DATA[6]`

`31:0`

r/w

`0xfffffefc`

`NEORV32_SLINK.DATA[7]`

`31:0`

r/w

#### 2.5.7. General Purpose Input and Output Port (GPIO)

 Hardware source file(s): neorv32_gpio.vhd Software driver file(s): neorv32_gpio.c neorv32_gpio.h Top entity port: `gpio_o` 64-bit parallel output port `gpio_i` 64-bit parallel input port Configuration generics: IO_GPIO_EN implement GPIO port when true CPU interrupts: none

Theory of Operation

The general purpose parallel IO port unit provides a simple 64-bit parallel input port and a 64-bit parallel output port. These ports can be used chip-externally (for example to drive status LEDs, connect buttons, etc.) or system-internally to provide control signals for other IP modules. The component is disabled for implementation when the IO_GPIO_EN generic is set false. In this case GPIO output port is tied to all-zero.

 Access atomicity The GPIO modules uses two memory-mapped registers (each 32-bit) each for accessing the input and output signals. Since the CPU can only process 32-bit "at once" updating the entire output cannot be performed within a single clock cycle.
Table 8. GPIO unit register map (`struct NEORV32_GPIO`)
Address Name [C] Bit(s) R/W Function

`0xffffffc0`

`NEORV32_GPIO.INPUT_LO`

31:0

r/-

parallel input port pins 31:0 (write accesses are ignored)

`0xffffffc4`

`NEORV32_GPIO.INPUT_HI`

31:0

r/-

parallel input port pins 63:32 (write accesses are ignored)

`0xffffffc8`

`NEORV32_GPIO.OUTPUT_LO`

31:0

r/w

parallel output port pins 31:0

`0xffffffcc`

`NEORV32_GPIO.OUTPUT_HI`

31:0

r/w

parallel output port pins 63:32

#### 2.5.8. Watchdog Timer (WDT)

 Hardware source file(s): neorv32_wdt.vhd Software driver file(s): neorv32_wdt.c neorv32_wdt.h Top entity port: none Configuration generics: IO_WDT_EN implement GPIO port when true CPU interrupts: fast IRQ channel 0 watchdog timer overflow (see Processor Interrupts)

Theory of Operation

The watchdog (WDT) provides a last resort for safety-critical applications. The WDT has an internal 20-bit wide counter that needs to be reset every now and then by the user program. If the counter overflows, either a system reset or an interrupt is generated (depending on the configured operation mode).

Configuration of the watchdog is done by a single control register `CTRL`. The watchdog is enabled by setting the WDT_CTRL_EN bit. The clock used to increment the internal counter is selected via the 3-bit WDT_CTRL_CLK_SELx prescaler:

`WDT_CTRL_CLK_SELx` Main clock prescaler Timeout period in clock cycles

`0b000`

2

2 097 152

`0b001`

4

4 194 304

`0b010`

8

8 388 608

`0b011`

64

67 108 864

`0b100`

128

134 217 728

`0b101`

1024

1 073 741 824

`0b110`

2048

2 147 483 648

`0b111`

4096

4 294 967 296

Whenever the internal timer overflows the watchdog executes one of two possible actions: Either a hard processor reset is triggered or an interrupt is requested at CPU’s fast interrupt channel #0. The WDT_CTRL_MODE bit defines the action to be taken on an overflow: When cleared, the Watchdog will trigger an IRQ, when set the WDT will cause a system reset. The configured actions can also be triggered manually at any time by setting the WDT_CTRL_FORCE bit. The watchdog is reset by setting the WDT_CTRL_RESET bit.

The cause of the last action of the watchdog can be determined via the WDT_CTRL_RCAUSE flag. If this flag is zero, the processor has been reset via the external reset signal. If this flag is set the last system reset was initiated by the watchdog.

The Watchdog control register can be locked in order to protect the current configuration. The lock is activated by setting bit WDT_CTRL_LOCK. In the locked state any write access to the configuration flags is ignored (see table below, "accessible if locked"). Read accesses to the control register are not effected. The lock can only be removed by a system reset (via external reset signal or via a watchdog reset action).

Table 9. WDT register map (`struct NEORV32_WDT`)
Address Name [C] Bit(s), Name [C] R/W Writable if locked Function

`0xffffffbc`

`NEORV32_WDT.CTRL`

`0` WDT_CTRL_EN

r/w

no

watchdog enable

`1` WDT_CTRL_CLK_SEL0

r/w

no

3-bit clock prescaler select

`2` WDT_CTRL_CLK_SEL1

r/w

no

`3` WDT_CTRL_CLK_SEL2

r/w

no

`4` WDT_CTRL_MODE

r/w

no

overflow action: `1`=reset, `0`=IRQ

`5` WDT_CTRL_RCAUSE

r/-

-

cause of last system reset: `0`=caused by external reset signal, `1`=caused by watchdog

`6` WDT_CTRL_RESET

-/w

yes

watchdog reset when set, auto-clears

`7` WDT_CTRL_FORCE

-/w

yes

force configured watchdog action when set, auto-clears

`8` WDT_CTRL_LOCK

r/w

no

lock access to configuration when set, clears only on system reset (via external reset signal OR watchdog reset action = reset)

#### 2.5.9. Machine System Timer (MTIME)

 Hardware source file(s): neorv32_mtime.vhd Software driver file(s): neorv32_mtime.c neorv32_mtime.h Top entity port: `mtime_i` System time input from external MTIME `mtime_o` System time output (64-bit) for SoC Configuration generics: IO_MTIME_EN implement MTIME when true CPU interrupts: `MTI` machine timer interrupt (see Processor Interrupts)

Theory of Operation

The MTIME machine system timer implements the memory-mapped MTIME timer from the official RISC-V specifications. This unit features a 64-bit system timer incremented with the primary processor clock. The current system time can also be obtained using the `time[h]` CSRs and is made available for processor-external use via the top’s `mtime_o` signal.

 If the processor-internal MTIME unit is NOT implemented, the top’s `mtime_i` input signal is used to update the `time[h]` CSRs and the `MTI` machine timer CPU interrupt (`MTI`) is directly connected to the top’s `mtime_irq_i` input.

The 64-bit system time can be accessed via the `TIME_LO` and `TIME_HI` memory-mapped registers (read/write) and also via the CPU’s `time[h]` CSRs (read-only). A 64-bit time compare register – accessible via memory-mapped `TIMECMP_LO` and `TIMECMP_HI` registers – is used to configure an interrupt to the CPU. The interrupt is triggered whenever `TIME` (high & low part) >= `TIMECMP` (high & low part) and is directly forwarded to the CPU’s `MTI` interrupt.

Table 10. MTIME register map (`struct NEORV32_MTIME`)
Address Name [C] Bits R/W Function

`0xffffff90`

`NEORV32_MTIME.TIME_LO`

31:0

r/w

machine system time, low word

`0xffffff94`

`NEORV32_MTIME.TIME_HI`

31:0

r/w

machine system time, high word

`0xffffff98`

`NEORV32_MTIME.TIMECMP_LO`

31:0

r/w

time compare, low word

`0xffffff9c`

`NEORV32_MTIME.TIMECMP_HI`

31:0

r/w

time compare, high word

#### 2.5.10. Primary Universal Asynchronous Receiver and Transmitter (UART0)

 Hardware source file(s): neorv32_uart.vhd Software driver file(s): neorv32_uart.c neorv32_uart.h Top entity port: `uart0_txd_o` serial transmitter output UART0 `uart0_rxd_i` serial receiver input UART0 `uart0_rts_o` flow control: RX ready to receive `uart0_cts_i` flow control: TX allowed to send Configuration generics: IO_UART0_EN implement UART0 when true CPU interrupts: fast IRQ channel 2 RX done interrupt fast IRQ channel 3 TX done interrupt (see Processor Interrupts)
 Please note that ALL default example programs and software libraries of the NEORV32 software framework (including the bootloader and the runtime environment) use the primary UART (UART0) as default user console interface. For compatibility, all C-language function calls to `neorv32_uart_*` are mapped to the according primary UART (UART0) `neorv32_uart0_*` functions.

Theory of Operation

In most cases, the UART is a standard interface used to establish a communication channel between the computer/user and an application running on the processor platform. The NEORV32 UARTs features a standard configuration frame configuration: 8 data bits, an optional parity bit (even or odd) and 1 stop bit. The parity and the actual Baudrate are configurable by software.

The UART0 is enabled by setting the UART_CTRL_EN bit in the UART control register `CTRL`. The actual transmission Baudrate (like 19200) is configured via the 12-bit UART_CTRL_BAUDxx baud prescaler (`baud_rate`) and the 3-bit UART_CTRL_PRSCx clock prescaler.

Table 11. UART prescaler configuration
`UART_CTRL_PRSCx` `0b000` `0b001` `0b010` `0b011` `0b100` `0b101` `0b110` `0b111`

Resulting `clock_prescaler`

2

4

8

64

128

1024

2048

4096

Baudrate = (fmain[Hz] / `clock_prescaler`) / (`baud_rate` + 1)

A new transmission is started by writing the data byte to be send to the lowest byte of the `DATA` register. The transfer is completed when the UART_CTRL_TX_BUSY control register flag returns to zero. A new received byte is available when the UART_DATA_AVAIL flag of the UART0_DATA register is set. A "frame error" in a received byte (broken stop bit) is indicated via the UART_DATA_FERR flag in the UART0_DATA register.

RX Double-Buffering

The UART receive engine provides a simple data buffer with two entries. These two entries are transparent for the user. The transmitting device can send up to 2 chars to the UART without risking data loss. If another char is sent before at least one char has been read from the buffer data loss occurs. This situation can be detected via the receiver overrun flag UART_DATA_OVERR in the `DATA` register. The flag is automatically cleared after reading `DATA`.

Parity Modes

The parity flag is added if the UART_CTRL_PMODE1 flag is set. When UART_CTRL_PMODE0 is zero the UART operates in "even parity" mode. If this flag is set, the UART operates in "odd parity" mode. Parity errors in received data are indicated via the UART_DATA_PERR flag in the UART_DATA registers. This flag is updated with each new received character. A frame error in the received data (i.e. stop bit is not set) is indicated via the UART_DATA_FERR flag in the `DATA`. This flag is also updated with each new received character

Hardware Flow Control – RTS/CTS

The UART supports hardware flow control using the standard CTS (clear to send) and/or RTS (ready to send / ready to receive "RTR") signals. Both hardware control flow mechanisms can be individually enabled.

If RTS hardware flow control is enabled by setting the UART_CTRL_RTS_EN control register flag, the UART will pull the `uart0_rts_o` signal low if the UART’s receiver is idle and no received data is waiting to get read by application software. As long as this signal is low the connected device can send new data. `uart0_rts_o` is always LOW if the UART is disabled.

The RTS line is de-asserted (going high) as soon as the start bit of a new incoming char has been detected. The transmitting device continues sending the current char and can also send another char (due to the RX double-buffering), which is done by most terminal programs. Any additional data send when RTS is still asserted will override the RX input buffer causing data loss. This will set the UART_DATA_OVERR flag in the `DATA` register. Any read access to this register clears the flag again.

If CTS hardware flow control is enabled by setting the UART_CTRL_CTS_EN control register flag, the UART’s transmitter will not start sending a new char until the `uart0_cts_i` signal goes low. If a new data to be send is written to the UART data register while `uart0_cts_i` is not asserted (=low), the UART will wait for `uart0_cts_i` to become asserted (=high) before sending starts. During this time, the UART busy flag UART_CTRL_TX_BUSY remains set.

If `uart0_cts_i` is asserted, no new data transmission will be started by the UART. The state of the `uart0_cts_i` signals has no effect on a transmission being already in progress.

Signal changes on `uart0_cts_i` during an active transmission are ignored. Application software can check the current state of the `uart0_cts_o` input signal via the UART_CTRL_CTS control register flag.

 Please note that – just like the RXD and TXD signals – the RTS and CTS signals have to be cross-coupled between devices.

Interrupts

The UART features two interrupts: the "TX done interrupt" is triggered when a transmit operation (sending) has finished. The "RX done interrupt" is triggered when a data byte has been received. If the UART0 is not implemented, the UART0 interrupts are permanently tied to zero.

 The UART’s RX interrupt is always triggered when a new data word has arrived – regardless of the state of the RX double-buffer.

Simulation Mode

The default UART0 operation will transmit any data written to the `DATA` register via the serial TX line at the defined baud rate. Even though the default testbench provides a simulated UART0 receiver, which outputs any received char to the simulator console, such a transmission takes a lot of time. To accelerate UART0 output during simulation (and also to dump large amounts of data for further processing like verification) the UART0 features a simulation mode.

The simulation mode is enabled by setting the UART_CTRL_SIM_MODE bit in the UART0’s control register `CTRL`. Any other UART0 configuration bits are irrelevant, but the UART0 has to be enabled via the UART_CTRL_EN bit. When the simulation mode is enabled, any written char to `DATA` (bits 7:0) is directly output as ASCII char to the simulator console. Additionally, all text is also stored to a text file `neorv32.uart0.sim_mode.text.out` in the simulation home folder. Furthermore, the whole 32-bit word written to `DATA` is stored as plain 8-char hexadecimal value to a second text file `neorv32.uart0.sim_mode.data.out` also located in the simulation home folder.

If the UART is configured for simulation mode there will be NO physical UART0 transmissions via `uart0_txd_o` at all. Furthermore, no interrupts (RX done or TX done) will be triggered in any situation.

 More information regarding the simulation-mode of the UART0 can be found in the Uer Guide section Simulating the Processor.
Table 12. UART0 register map (`struct NEORV32_UART0`)
Address Name [C] Bit(s), Name [C] R/W Function

`0xffffffa0`

`NEORV32_UART0.CTRL`

`11:0` UART_CT_BAUDxx

r/w

12-bit BAUD value configuration value

`12` UART_CT_SIM_MODE

r/w

enable simulation mode

`20` UART_CT_RTS_EN

r/w

enable RTS hardware flow control

`21` UART_CT_CTS_EN

r/w

enable CTS hardware flow control

`22` UART_CT_PMODE0

r/w

parity bit enable and configuration (`00`/`01`= no parity; `10`=even parity; `11`=odd parity)

`23` UART_CT_PMODE1

r/w

`24` UART_CT_PRSC0

r/w

3-bit baudrate clock prescaler select

`25` UART_CT_PRSC1

r/w

`26` UART_CT_PRSC2

r/w

`27` UART_CT_CTS

r/-

current state of UART’s CTS input signal

`28` UART_CT_EN

r/w

UART enable

`31` UART_CT_TX_BUSY

r/-

trasmitter busy flag

`0xffffffa4`

`NEORV32_UART0.DATA`

`7:0` UART_DATA_MSB : UART_DATA_LSB

r/w

`31:0` -

-/w

simulation data output

`28` UART_DATA_PERR

r/-

RX parity error

`29` UART_DATA_FERR

r/-

RX data frame error (stop bit nt set)

`30` UART_DATA_OVERR

r/-

RX data overrun

`31` UART_DATA_AVAIL

r/-

RX data available when set

#### 2.5.11. Secondary Universal Asynchronous Receiver and Transmitter (UART1)

 Hardware source file(s): neorv32_uart.vhd Software driver file(s): neorv32_uart.c neorv32_uart.h Top entity port: `uart1_txd_o` serial transmitter output UART1 `uart1_rxd_i` serial receiver input UART1 `uart1_rts_o` flow control: RX ready to receive `uart1_cts_i` flow control: TX allowed to send Configuration generics: IO_UART1_EN implement UART1 when true CPU interrupts: fast IRQ channel 4 RX done interrupt fast IRQ channel 5 TX done interrupt (see Processor Interrupts)

Theory of Operation

The secondary UART (UART1) is functional identical to the primary UART (Primary Universal Asynchronous Receiver and Transmitter (UART0)). Obviously, UART1 has different addresses for the control register (`CTRL`) and the data register (`DATA`) – see the register map below. However, the register bits/flags use the same bit positions and naming. Furthermore, the "RX done" and "TX done" interrupts are mapped to different CPU fast interrupt channels.

Simulation Mode

The secondary UART (UART1) provides the same simulation options as the primary UART. However, output data is written to UART1-specific files: `neorv32.uart1.sim_mode.text.out` is used to store plain ASCII text and `neorv32.uart1.sim_mode.data.out` is used to store full 32-bit hexadecimal encoded data words.

Table 13. UART1 register map (`struct NEORV32_UART1`)
Address Name [C] Bit(s), Name [C] R/W Function

`0xffffffd0`

`NEORV32_UART1.CTRL`

`11:0` UART_CT_BAUDxx

r/w

12-bit BAUD value configuration value

`12` UART_CT_SIM_MODE

r/w

enable simulation mode

`20` UART_CT_RTS_EN

r/w

enable RTS hardware flow control

`21` UART_CT_CTS_EN

r/w

enable CTS hardware flow control

`22` UART_CT_PMODE0

r/w

parity bit enable and configuration (`00`/`01`= no parity; `10`=even parity; `11`=odd parity)

`23` UART_CT_PMODE1

r/w

`24` UART_CT_PRSC0

r/w

3-bit baudrate clock prescaler select

`25` UART_CT_PRSC1

r/w

`26` UART_CT_PRSC2

r/w

`27` UART_CT_CTS

r/-

current state of UART’s CTS input signal

`28` UART_CT_EN

r/w

UART enable

`31` UART_CT_TX_BUSY

r/-

trasmitter busy flag

`0xffffffd4`

`NEORV32_UART1.DATA`

`7:0` UART_DATA_MSB : UART_DATA_LSB

r/w

`31:0` -

-/w

simulation data output

`28` UART_DATA_PERR

r/-

RX parity error

`29` UART_DATA_FERR

r/-

RX data frame error (stop bit nt set)

`30` UART_DATA_OVERR

r/-

RX data overrun

`31` UART_DATA_AVAIL

r/-

RX data available when set

#### 2.5.12. Serial Peripheral Interface Controller (SPI)

 Hardware source file(s): neorv32_spi.vhd Software driver file(s): neorv32_spi.c neorv32_spi.h Top entity port: `spi_sck_o` 1-bit serial clock output `spi_sdo_i` 1-bit serial data output `spi_sdi_o` 1-bit serial data input `spi_csn_i` 8-bit dedicated chip select (low-active) Configuration generics: IO_SPI_EN implement SPI controller when true CPU interrupts: fast IRQ channel 6 transmission done interrupt (see Processor Interrupts)

Theory of Operation

SPI is a synchronous serial transmission interface. The NEORV32 SPI transceiver allows 8-, 16-, 24- and 32- bit long transmissions. The unit provides 8 dedicated chip select signals via the top entity’s `spi_csn_o` signal.

The SPI unit is enabled via the SPI_CTRL_EN bit in the `CTRL` control register. The idle clock polarity is configured via the SPI_CTRL_CPHA bit and can be low (`0`) or high (`1`) during idle. The data quantity to be transferred within a single transmission is defined via the SPI_CTRL_SIZEx bits. The unit supports 8-bit (`00`), 16-bit (`01`), 24- bit (`10`) and 32-bit (`11`) transfers. Whenever a transfer is completed, the "transmission done interrupt" is triggered. A transmission is still in progress as long as the SPI_CTRL_BUSY flag is set.

The SPI controller features 8 dedicated chip-select lines. These lines are controlled via the control register’s SPI_CTRL_CSx bits. When a specifc SPI_CTRL_CSx bit is set, the according chip select line `spi_csn_o(x)` goes low (low-active chip select lines).

The SPI clock frequency is defined via the 3-bit SPI_CTRL_PRSCx clock prescaler. The following prescalers are available:

Table 14. SPI prescaler configuration
`SPI_CTRL_PRSCx` `0b000` `0b001` `0b010` `0b011` `0b100` `0b101` `0b110` `0b111`

Resulting `clock_prescaler`

2

4

8

64

128

1024

2048

4096

Based on the SPI_CTRL_PRSCx configuration, the actual SPI clock frequency fSPI is derived from the processor’s main clock fmain and is determined by:

fSPI = fmain[Hz] / (2 * `clock_prescaler`)

A transmission is started when writing data to the `DATA` register. The data must be LSB-aligned. So if the SPI transceiver is configured for less than 32-bit transfers data quantity, the transmit data must be placed into the lowest 8/16/24 bit of `DATA`. Vice versa, the received data is also always LSB-aligned.

Table 15. SPI register map (`struct NEORV32_SPI`)
Address Name [C] Bit(s), Name [C] R/W Function

`0xffffffa8`

`NEORV32_SPI.CTRL`

`0` SPI_CTRL_CS0

r/w

Direct chip-select 0..7; setting `spi_csn_o(x)` low when set

`1` SPI_CTRL_CS1

r/w

`2` SPI_CTRL_CS2

r/w

`3` SPI_CTRL_CS3

r/w

`4` SPI_CTRL_CS4

r/w

`5` SPI_CTRL_CS5

r/w

`6` SPI_CTRL_CS6

r/w

`7` SPI_CTRL_CS7

r/w

`8` SPI_CTRL_EN

r/w

SPI enable

`9` SPI_CTRL_CPHA

r/w

polarity of `spi_sck_o` when idle

`10` SPI_CTRL_PRSC0

r/w

3-bit clock prescaler select

`11` SPI_CTRL_PRSC1

r/w

`12` SPI_CTRL_PRSC2

r/w

`14` SPI_CTRL_SIZE0

r/w

transfer size (`00`=8-bit, `01`=16-bit, `10`=24-bit, `11`=32-bit)

`15` SPI_CTRL_SIZE1

r/w

`31` SPI_CTRL_BUSY

r/-

transmission in progress when set

`0xffffffac`

`NEORV32_SPI.DATA`

`31:0`

r/w

#### 2.5.13. Two-Wire Serial Interface Controller (TWI)

 Hardware source file(s): neorv32_twi.vhd Software driver file(s): neorv32_twi.c neorv32_twi.h Top entity port: `twi_sda_io` 1-bit bi-directional serial data `twi_scl_io` 1-bit bi-directional serial clock Configuration generics: IO_TWI_EN implement TWI controller when true CPU interrupts: fast IRQ channel 7 transmission done interrupt (see Processor Interrupts)

Theory of Operation

The two wire interface – also called "I²C" – is a quite famous interface for connecting several on-board components. Since this interface only needs two signals (the serial data line `twi_sda_io` and the serial clock line `twi_scl_io`) – despite of the number of connected devices – it allows easy interconnections of several peripheral nodes.

The NEORV32 TWI implements a TWI controller. It features "clock stretching" (if enabled via the control register), so a slow peripheral can halt the transmission by pulling the SCL line low. Currently, no multi-controller support is available. Also, the NEORV32 TWI unit cannot operate in peripheral mode.

The TWI is enabled via the TWI_CTRL_EN bit in the `CTRL` control register. The user program can start / stop a transmission by issuing a START or STOP condition. These conditions are generated by setting the according bits (TWI_CTRL_START or TWI_CTRL_STOP) in the control register.

Data is send by writing a byte to the `DATA` register. Received data can also be read from this register. The TWI controller is busy (transmitting data or performing a START or STOP condition) as long as the TWI_CTRL_BUSY bit in the control register is set.

An accessed peripheral has to acknowledge each transferred byte. When the TWI_CTRL_ACK bit is set after a completed transmission, the accessed peripheral has send an acknowledge. If it is cleared after a transmission, the peripheral has send a not-acknowledge (NACK). The NEORV32 TWI controller can also send an ACK by itself ("controller acknowledge MACK") after a transmission by pulling SDA low during the ACK time slot. Set the TWI_CTRL_MACK bit to activate this feature. If this bit is cleared, the ACK/NACK of the peripheral is sampled in this time slot instead (normal mode).

In summary, the following independent TWI operations can be triggered by the application program:

• send START condition (also as REPEATED START condition)

• send STOP condition

• send (at least) one byte while also sampling one byte from the bus

 The serial clock (SCL) and the serial data (SDA) lines can only be actively driven low by the controller. Hence, external pull-up resistors are required for these lines.

The TWI clock frequency is defined via the 3-bit TWI_CTRL_PRSCx clock prescaler. The following prescalers are available:

Table 16. TWI prescaler configuration
`TWI_CTRL_PRSCx` `0b000` `0b001` `0b010` `0b011` `0b100` `0b101` `0b110` `0b111`

Resulting `clock_prescaler`

2

4

8

64

128

1024

2048

4096

Based on the TWI_CTRL_PRSCx configuration, the actual TWI clock frequency fSCL is derived from the processor main clock fmain and is determined by:

fSCL = fmain[Hz] / (4 * `clock_prescaler`)

Table 17. TWI register map (`struct NEORV32_TWI`)
Address Name [C] Bit(s), Name [C] R/W Function

`0xffffffb0`

`NEORV32_TWI.CTRL`

`0` TWI_CTRL_EN

r/w

TWI enable

`1` TWI_CTRL_START

r/w

generate START condition

`2` TWI_CTRL_STOP

r/w

generate STOP condition

`3` TWI_CTRL_PRSC0

r/w

3-bit clock prescaler select

`4` TWI_CTRL_PRSC1

r/w

`5` TWI_CTRL_PRSC2

r/w

`6` TWI_CTRL_MACK

r/w

generate controller ACK for each transmission ("MACK")

`7` TWI_CTRL_CKSTEN

r/w

allow clock-stretching by peripherals when set

`30` TWI_CTRL_ACK

r/-

`31` TWI_CTRL_BUSY

r/-

transfer/START/STOP in progress when set

`0xffffffb4`

`NEORV32_TWI.DATA`

`7:0` TWI_DATA_MSB : TWI_DATA_LSB_

r/w

#### 2.5.14. Pulse-Width Modulation Controller (PWM)

 Hardware source file(s): neorv32_pwm.vhd Software driver file(s): neorv32_pwm.c neorv32_pwm.h Top entity port: `pwm_o` up to 60 PWM output channels (1-bit per channel) Configuration generics: IO_PWM_NUM_CH number of PWM channels to implement (0..60) CPU interrupts: none

The PWM controller implements a pulse-width modulation controller with up to 60 independent channels and 8- bit resolution per channel. The actual number of implemented channels is defined by the IO_PWM_NUM_CH generic. Setting this generic to zero will completely remove the PWM controller from the design.

The PWM controller is based on an 8-bit base counter with a programmable threshold comparators for each channel that defines the actual duty cycle. The controller can be used to drive fancy RGB-LEDs with 24- bit true color, to dim LCD back-lights or even for "analog" control. An external integrator (RC low-pass filter) can be used to smooth the generated "analog" signals.

Theory of Operation

The PWM controller is activated by setting the PWM_CTRL_EN bit in the module’s control register `CTRL`. When this bit is cleared, the unit is reset and all PWM output channels are set to zero. The 8-bit duty cycle for each channel, which represents the channel’s "intensity", is defined via an 8-bit value. The module provides up to 15 duty cycle registers `DUTY[0]` to `DUTY[14]` (depending on the number of implemented channels). Each register contains the duty cycle configuration for 4 consecutive channels. For example, the duty cycle of channel 0 is defined via bits 7:0 in `DUTY[0]`. The duty cycle of channel 2 is defined via bits 15:0 in `DUTY[0]`. Channel 4’s duty cycle is defined via bits 7:0 in `DUTY[1]` and so on.

 Regardless of the configuration of IO_PWM_NUM_CH all module registers can be accessed without raising an exception. Software can discover the number of available channels by writing 0xff to all duty cycle configuration bytes and reading those values back. The duty-cycle of channels that were not implemented always reads as zero.

Based on the configured duty cycle the according intensity of the channel can be computed by the following formula:

Intensityx = `DUTY[y](i*8+7 downto i*8)` / (28)

The base frequency of the generated PWM signals is defined by the PWM core clock. This clock is derived from the main processor clock and divided by a prescaler via the 3-bit PWM_CTRL_PRSCx in the unit’s control register. The following prescalers are available:

Table 18. PWM prescaler configuration
`PWM_CTRL_PRSCx` `0b000` `0b001` `0b010` `0b011` `0b100` `0b101` `0b110` `0b111`

Resulting `clock_prescaler`

2

4

8

64

128

1024

2048

4096

The resulting PWM base frequency is defined by:

fPWM = fmain[Hz] / (28 * `clock_prescaler`)

Table 19. PWM register map (`struct neorv32_pwm_t`)
Address Name [C] Bit(s), Name [C] R/W Function

`0xfffffe80`

`NEORV32_PWM.CTRL`

`0` PWM_CTRL_EN

r/w

PWM enable

`1` PWM_CTRL_PRSC0

r/w

3-bit clock prescaler select

`2` PWM_CTRL_PRSC1

r/w

`3` PWM_CTRL_PRSC2

r/w

`0xfffffe84`

`NEORV32_PWM.DUTY[0]`

`7:0`

r/w

8-bit duty cycle for channel 0

`15:8`

r/w

8-bit duty cycle for channel 1

`23:16`

r/w

8-bit duty cycle for channel 2

`31:24`

r/w

8-bit duty cycle for channel 3

…​

…​

…​

r/w

…​

`0xfffffebc`

`NEORV32_PWM.DUTY[14]`

`7:0`

r/w

8-bit duty cycle for channel 56

`15:8`

r/w

8-bit duty cycle for channel 57

`23:16`

r/w

8-bit duty cycle for channel 58

`31:24`

r/w

8-bit duty cycle for channel 59

#### 2.5.15. True Random-Number Generator (TRNG)

 Hardware source file(s): neorv32_trng.vhd Software driver file(s): neorv32_trng.c neorv32_trng.h Top entity port: none Configuration generics: IO_TRNG_EN implement TRNG when true CPU interrupts: none

Theory of Operation

The NEORV32 true random number generator provides physical true random numbers for your application. Instead of using a pseudo RNG like a LFSR, the TRNG of the processor uses a simple, straight-forward ring oscillator as physical entropy source. Hence, voltage and thermal fluctuations are used to provide true physical random data.

 The TRNG features a platform independent architecture without FPGA-specific primitives, macros or attributes.

Architecture

The NEORV32 TRNG is based on simple ring oscillators, which are implemented as an inverter chain with an odd number of inverters. A latch is used to decouple each individual inverter. Basically, this architecture is some king of asynchronous LFSR.

The output of several ring oscillators are synchronized using two registers and are XORed together. The resulting output is de-biased using a von-Neumann randomness extractor. This de-biased output is further processed by a simple 8-bit Fibonacci LFSR to improve whitening. After at least 8 clock cycles the state of the LFSR is sampled and provided as final data output.

To prevent the synthesis tool from doing logic optimization and thus, removing all but one inverter, the TRNG uses simple latches to decouple an inverter and its actual output. The latches are reset when the TRNG is disabled and are enabled one by one by a "real" shift register when the TRNG is activated. This construct can be synthesized for any FPGA platform. Thus, the NEORV32 TRNG provides a platform independent architecture.

TRNG Configuration

The TRNG uses several ring-oscillators, where the next oscillator provides a slightly longer chain (more inverters) than the one before. This increment is constant for all implemented oscillators. This setup can be customized by modifying the "Advanced Configuration" constants in the TRNG’s VHDL file:

• The `num_roscs_c` constant defines the total number of ring oscillators in the system. num_inv_start_c defines the number of inverters used by the first ring oscillators (has to be an odd number). Each additional ring oscillator provides `num_inv_inc_c` more inverters that the one before (has to be an even number).

• The LFSR-based post-processing can be deactivated using the `lfsr_en_c` constant. The polynomial tap mask of the LFSR can be customized using `lfsr_taps_c`.

Using the TRNG

The TRNG features a single register for status and data access. When the TRNG_CTRL_EN control register (`CTRL`) bit is set, the TRNG is enabled and starts operation. As soon as the TRNG_CTRL_VALID bit is set, the currently sampled 8-bit random data byte can be obtained from the lowest 8 bits of the `CTRL` register (TRNG_CTRL_DATA_MSB : TRNG_CTRL_DATA_LSB). The TRNG_CTRL_VALID bit is automatically cleared when reading the control register.

 The TRNG needs at least 8 clock cycles to generate a new random byte. During this sampling time the current output random data is kept stable in the output register until a valid sampling of the new byte has completed.

Randomness "Quality" I have not verified the quality of the generated random numbers (for example using NIST test suites). The quality is highly effected by the actual configuration of the TRNG and the resulting FPGA mapping/routing. However, generating larger histograms of the generated random number shows an equal distribution (binary average of the random numbers = 127). A simple evaluation test/demo program can be found in `sw/example/demo_trng`.

Table 20. TRNG register map (`struct NEORV32_TRNG`)
Address Name [C] Bit(s), Name [C] R/W Function

`0xffffffb8`

`NEORV32_TRNG.CTRL`

`7:0` TRNG_CTRL_DATA_MSB : TRNG_CTRL_DATA_MSB

r/-

8-bit random data output

`30` TRNG_CTRL_EN

r/w

TRNG enable

`31` TRNG_CTRL_VALID

r/-

random data output is valid when set

#### 2.5.16. Custom Functions Subsystem (CFS)

 Hardware source file(s): neorv32_gfs.vhd Software driver file(s): neorv32_gfs.c neorv32_gfs.h Top entity port: `cfs_in_i` custom input conduit `cfs_out_o` custom output conduit Configuration generics: IO_CFS_EN implement CFS when true IO_CFS_CONFIG custom generic conduit IO_CFS_IN_SIZE size of `cfs_in_i` IO_CFS_OUT_SIZE size of `cfs_out_o` CPU interrupts: fast IRQ channel 1 CFS interrupt (see Processor Interrupts)

Theory of Operation

The custom functions subsystem can be used to implement application-specific user-defined co-processors (like encryption or arithmetic accelerators) or peripheral/communication interfaces. In contrast to connecting custom hardware accelerators via the external memory interface, the CFS provide a convenient and low-latency extension and customization option.

The CFS provides up to 32x 32-bit memory-mapped registers `REG` (see register map table below). The actual functionality of these register has to be defined by the hardware designer.

Take a look at the template CFS VHDL source file (`rtl/core/neorv32_cfs.vhd`). The file is highly commented to illustrate all aspects that are relevant for implementing custom CFS-based co-processor designs.

CFS Software Access

The CFS memory-mapped registers can be accessed by software using the provided C-language aliases (see register map table below). Note that all interface registers provide 32-bit access data of type `uint32_t`.

``````// C-code CFS usage example
NEORV32_CFS.REG[0] = (uint32_t)some_data_array(i); // write to CFS register 0
uint32_t temp = NEORV32_CFS.REG[20]; // read from CFS register 20``````

CFS Interrupt

The CFS provides a single one-shot interrupt request signal mapped to the CPU’s fast interrupt channel 1. See section Processor Interrupts for more information.

CFS Configuration Generic

By default, the CFS provides a single 32-bit `std_(u)logic_vector` configuration generic IO_CFS_CONFIG that is available in the processor’s top entity. This generic can be used to pass custom configuration options from the top entity down to the CFS entity.

CFS Custom IOs

By default, the CFS also provides two unidirectional input and output conduits `cfs_in_i` and `cfs_out_o`. These signals are propagated to the processor’s top entity. The actual use of these signals has to be defined by the hardware designer. The size of the input signal conduit `cfs_in_i` is defined via the (top’s) IO_CFS_IN_SIZE configuration generic (default = 32-bit). The size of the output signal conduit `cfs_out_o` is defined via the (top’s) IO_CFS_OUT_SIZE configuration generic (default = 32-bit). If the custom function subsystem is not implemented (IO_CFS_EN = false) the `cfs_out_o` signal is tied to all-zero.

Table 21. CFS register map (`struct NEORV32_CFS`)
Address Name [C] Bit(s) R/W Function

`0xfffffe00`

`NEORV32_CFS.REG[0]`

`31:0`

(r)/(w)

custom CFS interface register 0

`0xfffffe04`

`NEORV32_CFS.REG[1]`

`31:0`

(r)/(w)

custom CFS interface register 1

…​

…​

`31:0`

(r)/(w)

…​

`0xfffffe78`

`NEORV32_CFS.REG[30]`

`31:0`

(r)/(w)

custom CFS interface register 30

`0xfffffe7c`

`NEORV32_CFS.REG[31]`

`31:0`

(r)/(w)

custom CFS interface register 31

#### 2.5.17. Smart LED Interface (NEOLED)

 Hardware source file(s): neorv32_neoled.vhd Software driver file(s): neorv32_neoled.c neorv32_neoled.h Top entity port: `neoled_o` 1-bit serial data output Configuration generics: IO_NEOLED_EN implement NEOLED when true IO_NEOLED_TX_FIFO TX FIFO depth (1..32k, has to be a power of two) CPU interrupts: fast IRQ channel 9 NEOLED interrupt (see Processor Interrupts)

Theory of Operation

The NEOLED module provides a dedicated interface for "smart RGB LEDs" like the WS2812 or WS2811. These LEDs provide a single interface wire that uses an asynchronous serial protocol for transmitting color data. Basically, data is transferred via LED-internal shift registers, which allows to cascade an unlimited number of smart LEDs. The protocol provides a RESET command to strobe the transmitted data into the LED PWM driver registers after data has shifted throughout all LEDs in a chain.

The interface provides a single 1-bit output `neoled_o` to drive an arbitrary number of cascaded LEDs. Since the NEOLED module provides 24-bit and 32-bit operating modes, a mixed setup with RGB LEDs (24-bit color) and RGBW LEDs (32-bit color including a dedicated white LED chip) is possible.

Theory of Operation – NEOLED Module

The NEOLED modules provides two accessible interface registers: the control register `CTRL` and the TX data register `DATA`. The NEOLED module is globally enabled via the control register’s NEOLED_CTRL_EN bit. Clearing this bit will terminate any current operation, clear the TX buffer, reset the module and set the `neoled_o` output to zero. The precise timing (implementing the WS2812 protocol) and transmission mode are fully programmable via the `CTRL` register to provide maximum flexibility.

RGB / RGBW Configuration

NeoPixel are available in two "color" version: LEDs with three chips providing RGB color and LEDs with four chips providing RGB color plus a dedicated white LED chip (= RGBW). Since the intensity of every LED chip is defined via an 8-bit value the RGB LEDs require a frame of 24-bit per module and the RGBW LEDs require a frame of 32-bit per module.

The data transfer quantity of the NEOLED module can be configured via the NEOLED_MODE_EN control register bit. If this bit is cleared, the NEOLED interface operates in 24-bit mode and will transmit bits `23:0` of the data written to `DATA` to the LEDs. If NEOLED_MODE_EN is set, the NEOLED interface operates in 32-bit mode and will transmit bits `31:0` of the data written to `DATA` to the LEDs.

The mode bit can be configured before writing each new data word in order to support an arbitrary setup of RGB and RGBW LEDs.

Theory of Operation – Protocol

The interface of the WS2812 LEDs uses an 800kHz carrier signal. Data is transmitted in a serial manner starting with LSB-first. The intensity for each R, G & B (& W) LED chip (= color code) is defined via an 8-bit value. The actual data bits are transferred by modifying the duty cycle of the signal (the timings for the WS2812 are shown below). A RESET command is "send" by pulling the data line LOW for at least 50μs.

Figure 7. WS2812 bit-level protocol - taken from the "Adafruit NeoPixel Überguide"
 Ttotal (Tcarrier) 1.25μs +/- 300ns period for a single bit T0H 0.4μs +/- 150ns high-time for sending a `1` T0L 0.8μs +/- 150ns low-time for sending a `1` T1H 0.85μs +/- 150ns high-time for sending a `0` T1L 0.45μs +/- 150 ns low-time for sending a `0` RESET Above 50μs low-time for sending a RESET command

Timing Configuration

The basic carrier frequency (800kHz for the WS2812 LEDs) is configured via a 3-bit main clock prescaler (NEOLED_CTRL_PRSCx, see table below) that scales the main processor clock fmain and a 5-bit cycle multiplier NEOLED_CTRL_T_TOT_x.

Table 23. NEOLED prescaler configuration
`NEOLED_CTRL_PRSCx` `0b000` `0b001` `0b010` `0b011` `0b100` `0b101` `0b110` `0b111`

Resulting `clock_prescaler`

2

4

8

64

128

1024

2048

4096

The duty-cycles (or more precisely: the high- and low-times for sending either a '1' bit or a '0' bit) are defined via the 5-bit NEOLED_CTRL_T_ONE_H_x and NEOLED_CTRL_T_ZERO_H_x values, respectively. These programmable timing constants allow to adapt the interface for a wide variety of smart LED protocol (for example WS2812 vs. WS2811).

Timing Configuration – Example (WS2812)

Generate the base clock fTX for the NEOLED TX engine:

• processor clock fmain = 100 MHz

• NEOLED_CTRL_PRSCx = `0b001` = fmain / 4

fTX = fmain[Hz] / `clock_prescaler` = 100MHz / 4 = 25MHz

TTX = 1 / fTX = 40ns

Generate carrier period (Tcarrier) and high-times (duty cycle) for sending `0` (T0H) and `1` (T1H) bits:

• NEOLED_CTRL_T_TOT = `0b11110` (= decimal 30)

• NEOLED_CTRL_T_ZERO_H = `0b01010` (= decimal 10)

• NEOLED_CTRL_T_ONE_H = `0b10100` (= decimal 20)

Tcarrier = TTX * NEOLED_CTRL_T_TOT = 40ns * 30 = 1.4µs

T0H = TTX * NEOLED_CTRL_T_ZERO_H = 40ns * 10 = 0.4µs

T1H = TTX * NEOLED_CTRL_T_ONE_H = 40ns * 20 = 0.8µs

 The NEOLED SW driver library (`neorv32_neoled.h`) provides a simplified configuration function that configures all timing parameters for driving WS2812 LEDs based on the processor clock frequency.

TX Data FIFO

The interface features a TX data buffer (a FIFO) to allow more CPU-independent operation. The buffer depth is configured via the IO_NEOLED_TX_FIFO top generic (default = 1 entry). The FIFO size configuration can be read via the NEOLED_CTRL_BUFS_x control register bits, which result log2(IO_NEOLED_TX_FIFO).

When writing data to the `DATA` register the data is automatically written to the TX buffer. Whenever data is available in the buffer the serial transmission engine will take it and transmit it to the LEDs. The data transfer size (NEOLED_MODE_EN) can be modified at every time since this control register bit is also buffered in the FIFO. This allows to arbitrarily mixing RGB and RGBW LEDs in the chain.

Software can check the FIFO fill level via the control register’s NEOLED_CTRL_TX_EMPTY, NEOLED_CTRL_TX_HALF and NEOLED_CTRL_TX_FULL flags. The NEOLED_CTRL_TX_BUSY flags provides additional information if the the TX unit is still busy sending data.

 Please note that the timing configurations (NEOLED_CTRL_PRSCx, NEOLED_CTRL_T_TOT_x, NEOLED_CTRL_T_ONE_H_x and NEOLED_CTRL_T_ZERO_H_x) are NOT stored to the buffer. Changing these value while the buffer is not empty or the TX engine is still busy will cause data corruption.
• Strobe Command ("RESET") **

According to the WS2812 specs the data written to the LED’s shift registers is strobed to the actual PWM driver registers when the data line is low for 50μs ("RESET" command, see table above). This can be implemented using busy-wait for at least 50μs. Obviously, this concept wastes a lot of processing power.

To circumvent this, the NEOLED module provides an option to automatically issue an idle time for creating the RESET command. If the NEOLED_CTRL_STROBE control register bit is set, all data written to the data FIFO (via `DATA`, the actually written data is irrelevant) will trigger an idle phase (`neoled_o` = zero) of 127 periods (= Tcarrier). This idle time will cause the LEDs to strobe the color data into the PWM driver registers.

Since the NEOLED_CTRL_STROBE flag is also buffered in the TX buffer, the RESET command is treated as just another data word being written to the TX buffer making busy wait concepts obsolete and allowing maximum refresh rates.

Interrupt

The NEOLED modules features a single interrupt that is triggered whenever the TX FIFO’s fill level falls below half-full level. In this case software can write up to IO_NEOLED_TX_FIFO/2 new data words to `DATA` without checking the FIFO status flags.

This highly relaxes time constraints for sending a continuous data stream to the LEDs (as an idle time beyond 50μs will trigger the LED’s a RESET command).

Table 24. NEOLED register map (`struct NEORV32_NEOLED`)
Address Name [C] Bit(s), Name [C] R/W Function

`0xffffffd8`

`NEORV32_NEOLED.CTRL`

`0` NEOLED_CTRL_EN

r/w

NEOLED enable

`1` NEOLED_CTRL_MODE

r/w

data transfer size; `0`=24-bit; `1`=32-bit

`2` NEOLED_CTRL_STROBE

r/w

`0`=send normal color data; `1`=send RESET command on data write access

`3` NEOLED_CTRL_PRSC0

r/w

3-bit clock prescaler, bit 0

`4` NEOLED_CTRL_PRSC1

r/w

3-bit clock prescaler, bit 1

`5` NEOLED_CTRL_PRSC2

r/w

3-bit clock prescaler, bit 2

`6` NEOLED_CTRL_BUFS0

r/-

4-bit log2(IO_NEOLED_TX_FIFO)

`7` NEOLED_CTRL_BUFS1

r/-

`8` NEOLED_CTRL_BUFS2

r/-

`9` NEOLED_CTRL_BUFS3

r/-

`10` NEOLED_CTRL_T_TOT_0

r/w

5-bit pulse clock ticks per total single-bit period (Ttotal)

`11` NEOLED_CTRL_T_TOT_1

r/w

`12` NEOLED_CTRL_T_TOT_2

r/w

`13` NEOLED_CTRL_T_TOT_3

r/w

`14` NEOLED_CTRL_T_TOT_4

r/w

`20` NEOLED_CTRL_ONE_H_0

r/w

5-bit pulse clock ticks per high-time for sending a one-bit (TH1)

`21` NEOLED_CTRL_ONE_H_1

r/w

`22` NEOLED_CTRL_ONE_H_2

r/w

`23` NEOLED_CTRL_ONE_H_3

r/w

`24` NEOLED_CTRL_ONE_H_4

r/w

`30` NEOLED_CTRL_TX_STATUS

r/-

transmit engine busy when `1`

`31` NEOLED_CTRL_TX_EMPTY

r/-

TX FIFO is empty

`31` NEOLED_CTRL_TX_HALF

r/-

TX FIFO is at least half full

`31` NEOLED_CTRL_TX_FULL

r/-

TX FIFO is full

`31` NEOLED_CTRL_TX_BUSY

r/-

TX serial engine is busy when set

`0xffffffdc`

`NEORV32_NEOLED.DATA`

`31:0` / `23:0`

-/w

TX data (32-/24-bit)

#### 2.5.18. External Interrupt Controller (XIRQ)

 Hardware source file(s): neorv32_xirq.vhd Software driver file(s): neorv32_xirq.c neorv32_xirq.h Top entity port: `xirq_i` IRQ input (up to 32-bit) Configuration generics: XIRQ_NUM_CH Number of IRQs to implement (0..32) XIRQ_TRIGGER_TYPE IRQ trigger type configuration XIRQ_TRIGGER_POLARITY IRQ trigger polarity configuration CPU interrupts: fast IRQ channel 8 XIRQ (see Processor Interrupts)

The eXternal interrupt controller provides a simple mechanism to implement up to 32 processor-external interrupt request signals. The external IRQ requests are prioritized, queued and signaled to the CPU via a single CPU fast interrupt request.

Theory of Operation

The XIRQ provides up to 32 interrupt channels (configured via the XIRQ_NUM_CH generic). Each bit in the `xirq_i` input signal vector represents one interrupt channel. An interrupt channel is enabled by setting the according bit in the interrupt enable register `IER`.

If the configured trigger (see below) of an enabled channel fires, the request is stored into an internal buffer. This buffer is available via the interrupt pending register `IPR`. A `1` in this register indicates that the corresponding interrupt channel has fired but has not yet been serviced (so it is pending). An interrupt channel can become pending if the according `IER` bit is set. Pending IRQs can be cleared by writing `0` to the according `IPR` bit. As soon as there is a least one pending interrupt in the buffer, an interrupt request is send to the CPU.

 A disabled interrupt channel can still be pending if it has been triggered before clearing the according `IER` bit.

The CPU can determine firing interrupt request either by checking the bits in the `IPR` register, which show all pending interrupt channels, or by reading the interrupt source register `SCR`. This register provides a 5-bit wide ID (0..31) that shows the interrupt request with highest priority. Interrupt channel `xirq_i(0)` has highest priority and `xirq_i(XIRQ_NUM_CH-1)` has lowest priority. This priority assignment is fixed and cannot be altered by software. The CPU can use the ID from `SCR` to service IRQ according to their priority. To acknowledge the according interrupt the CPU can write `1 << SCR` to `IPR`.

In order to acknowledge the interrupt from the external interrupt controller, the CPU has to write any value to interrupt source register `SRC`.

 An interrupt handler should clear the interrupt pending bit that caused the interrupt first before acknowledging the interrupt by writing the `SCR` register.

IRQ Trigger Configuration

The controller does not provide a configuration option to define the IRQ triggers during runtime. Instead, two generics are provided to configure the trigger of each interrupt channel before synthesis: the XIRQ_TRIGGER_TYPE and XIRQ_TRIGGER_POLARITY generic. Both generics are 32 bit wide representing one bit per interrupt channel. If less than 32 interrupt channels are implemented the remaining configuration bits are ignored.

XIRQ_TRIGGER_TYPE is used to define the general trigger type. This can be either level-triggered (`0`) or edge-triggered (`1`). XIRQ_TRIGGER_POLARITY is used to configure the polarity of the trigger: a `0` defines low-level or falling-edge and a `1` defines high-level or rising-edge.

Listing 2. Example trigger configuration: channel 0 for rising-edge, IRQ channels 1 to 31 for high-level
``````XIRQ_TRIGGER_TYPE     => x"00000001";
XIRQ_TRIGGER_POLARITY => x"ffffffff";``````
Table 25. XIRQ register map (`struct NEORV32_XIRQ`)
Address Name [C] Bit(s) R/W Function

`0xffffff80`

`NEORV32_XIRQ.IER`

`31:0`

r/w

Interrupt enable register (one bit per channel, LSB-aligned)

`0xffffff84`

`NEORV32_XIRQ.IPR`

`31:0`

r/w

Interrupt pending register (one bit per channel, LSB-aligned); writing 0 to a bit clears according pending interrupt

`0xffffff88`

`NEORV32_XIRQ.SCR`

`4:0`

r/w

Channel id (0..31) of firing IRQ (prioritized!); writing any value will acknowledge the current interrupt

`0xffffff8c`

-

`31:0`

r/-

#### 2.5.19. System Configuration Information Memory (SYSINFO)

 Hardware source file(s): neorv32_sysinfo.vhd Software driver file(s): neorv32.h Top entity port: none Configuration generics: * most of the top’s configuration generics CPU interrupts: none

Theory of Operation

The SYSINFO allows the application software to determine the setting of most of the processor’s top entity generics that are related to processor/SoC configuration. All registers of this unit are read-only.

This device is always implemented – regardless of the actual hardware configuration. The bootloader as well as the NEORV32 software runtime environment require information from this device (like memory layout and default clock speed) for correct operation.

Table 26. SYSINFO register map (`struct NEORV32_SYSINFO`)

`0xffffffe0`

`NEORV32_SYSINFO.CLK`

clock speed in Hz (via top’s CLOCK_FREQUENCY generic)

`0xffffffe4`

`NEORV32_SYSINFO.CPU`

specific CPU configuration (see SYSINFO - CPU Configuration)

`0xffffffe8`

`NEORV32_SYSINFO.SOC`

specific SoC configuration (see SYSINFO - SoC Configuration)

`0xffffffec`

`NEORV32_SYSINFO.CACHE`

cache configuration information (see SYSINFO - Cache Configuration)

`0xfffffff0`

`NEORV32_SYSINFO.ISPACE_BASE`

instruction address space base (via package’s `ispace_base_c` constant)

`0xfffffff4`

`NEORV32_SYSINFO.IMEM_SIZE`

internal IMEM size in bytes (via top’s MEM_INT_IMEM_SIZE generic)

`0xfffffff8`

`NEORV32_SYSINFO.DSPACE_BASE`

data address space base (via package’s `sdspace_base_c` constant)

`0xfffffffc`

`NEORV32_SYSINFO.DMEM_SIZE`

internal DMEM size in bytes (via top’s MEM_INT_DMEM_SIZE generic)

##### SYSINFO - CPU Configuration
Table 27. SYSINFO_CPU bits
Bit Name [C] Function

`0`

SYSINFO_CPU_ZICSR

`Zicsr` extension (`I` sub-extension) available when set (via top’s CPU_EXTENSION_RISCV_Zicsr generic)

`1`

SYSINFO_CPU_ZIFENCEI

`Zifencei` extension (`I` sub-extension) available when set (via top’s CPU_EXTENSION_RISCV_Zifencei generic)

`2`

SYSINFO_CPU_ZMMUL

`Zmmul` extension (`M` sub-extension) available when set (via top’s CPU_EXTENSION_RISCV_Zmmul generic)

`3`

SYSINFO_CPU_ZBB

`Zbb` extension (`B` sub-extension) available when set (via top’s CPU_EXTENSION_RISCV_Zbb generic)

`5`

SYSINFO_CPU_ZFINX

`Zfinx` extension (`F` sub-/alternative-extension) available when set (via top’s CPU_EXTENSION_RISCV_Zfinx generic)

`6`

SYSINFO_CPU_ZXSCNT

Custom extension - Small CPU counters: `[m]cycle` & `[m]instret` CSRs have less than 64-bit when set (via top’s CPU_CNT_WIDTH generic)

`7`

SYSINFO_CPU_ZXNOCNT

Custom extension - NO CPU counters: `[m]cycle` & `[m]instret` CSRs are NOT available at all when set (via top’s CPU_CNT_WIDTH generic)

`8`

SYSINFO_CPU_PMP

`PMP` (physical memory protection) extension available when set (via top’s [_] generic)

`9`

SYSINFO_CPU_HPM

`HPM` (hardware performance monitors) extension available when set (via top’s [_] generic)

`10`

SYSINFO_CPU_DEBUGMODE

RISC-V CPU `debug_mode` available when set (via top’s [_] generic)

`30

SYSINFO_CPU_FASTMUL

fast multiplication available when set (via top’s FAST_MUL_EN generic)

`31`

SYSINFO_CPU_FASTSHIFT

fast shifts available when set (via top’s FAST_SHIFT_EN generic)

##### SYSINFO - SoC Configuration
Table 28. SYSINFO_SOC bits
Bit Name [C] Function

`0`

`1`

SYSINFO_SOC_MEM_EXT

set if the external Wishbone bus interface is implemented (via top’s MEM_EXT_EN generic)

`2`

SYSINFO_SOC_MEM_INT_IMEM

set if the processor-internal DMEM implemented (via top’s MEM_INT_DMEM_EN generic)

`3`

SYSINFO_SOC_MEM_INT_DMEM

set if the processor-internal IMEM is implemented (via top’s MEM_INT_IMEM_EN generic)

`4`

SYSINFO_SOC_MEM_EXT_ENDIAN

set if external bus interface uses BIG-endian byte-order (via top’s MEM_EXT_BIG_ENDIAN generic)

`5`

SYSINFO_SOC_ICACHE

set if processor-internal instruction cache is implemented (via top’s ICACHE_EN generic)

`14`

SYSINFO_SOC_OCD

set if on-chip debugger implemented (via top’s ON_CHIP_DEBUGGER_EN generic)

`15`

SYSINFO_SOC_HW_RESET

set if a dedicated hardware reset of all core registers is implemented (via package’s `dedicated_reset_c` constant)

`16`

SYSINFO_SOC_IO_GPIO

set if the GPIO is implemented (via top’s IO_GPIO_EN generic)

`17`

SYSINFO_SOC_IO_MTIME

set if the MTIME is implemented (via top’s IO_MTIME_EN generic)

`18`

SYSINFO_SOC_IO_UART0

set if the primary UART0 is implemented (via top’s IO_UART0_EN generic)

`19`

SYSINFO_SOC_IO_SPI

set if the SPI is implemented (via top’s IO_SPI_EN generic)

`20`

SYSINFO_SOC_IO_TWI

set if the TWI is implemented (via top’s IO_TWI_EN generic)

`21`

SYSINFO_SOC_IO_PWM

set if the PWM is implemented (via top’s [_io_pwm_en] generic)

`22`

SYSINFO_SOC_IO_WDT

set if the WDT is implemented (via top’s IO_WDT_EN generic)

`23`

SYSINFO_SOC_IO_CFS

set if the custom functions subsystem is implemented (via top’s IO_CFS_EN generic)

`24`

SYSINFO_SOC_IO_TRNG

set if the TRNG is implemented (via top’s IO_TRNG_EN generic)

`25`

`26`

SYSINFO_SOC_IO_UART1

set if the secondary UART1 is implemented (via top’s IO_UART1_EN generic)

`27`

SYSINFO_SOC_IO_NEOLED

set if the NEOLED is implemented (via top’s IO_NEOLED_EN generic)

##### SYSINFO - Cache Configuration
 Bit fields in this register are set to all-zero if the according cache is not implemented.
Table 29. SYSINFO_CACHE bits
Bit Name [C] Function

`3:0`

SYSINFO_CACHE_IC_BLOCK_SIZE_3 : SYSINFO_CACHE_IC_BLOCK_SIZE_0

log2(i-cache block size in bytes), via top’s ICACHE_BLOCK_SIZE generic

`7:4`

SYSINFO_CACHE_IC_NUM_BLOCKS_3 : SYSINFO_CACHE_IC_NUM_BLOCKS_0

log2(i-cache number of cache blocks), via top’s [_icache_num_blocks] generic

`11:9`

SYSINFO_CACHE_IC_ASSOCIATIVITY_3 : SYSINFO_CACHE_IC_ASSOCIATIVITY_0

log2(i-cache associativity), via top’s ICACHE_ASSOCIATIVITY generic

`15:12`

SYSINFO_CACHE_IC_REPLACEMENT_3 : SYSINFO_CACHE_IC_REPLACEMENT_0

i-cache replacement policy (`0001` = LRU if associativity > 0)

`32:16`

-

zero, reserved for d-cache

## 3. NEORV32 Central Processing Unit (CPU)

Key Features

• 32-bit pipelined/multi-cycle in-order `rv32` RISC-V CPU

• Optional RISC-V extensions:

• `A` - atomic memory access operations

• `C` - 16-bit compressed instructions

• `I` - integer base ISA (always enabled)

• `E` - embedded CPU version (reduced register file size)

• `M` - integer multiplication and division hardware

• `U` - less-privileged user mode

• `Zbb` - basic bit-manipulation operations

• `Zfinx` - single-precision floating-point unit

• `Zicsr` - control and status register access (privileged architecture)

• `Zifencei` - instruction stream synchronization

• `Zmmul` - integer multiplication hardware

• `PMP` - physical memory protection

• `HPM` - hardware performance monitors

• `DB` - debug mode

• Compatible to the RISC-V user specifications and a subset of the RISC-V privileged architecture specifications – passes the official RISC-V Architecture Tests (v2+)

• Official RISC-V open-source architecture ID

• Standard RISC-V interrupts (external, timer, software) plus 16 fast interrupts and 1 non-maskable interrupt

• Supports most of the traps from the RISC-V specifications (including bus access exceptions) and traps on all unimplemented/illegal/malformed instructions

• Optional physical memory configuration (PMP), compatible to the RISC-V specifications

• Optional hardware performance monitors (HPM) for application benchmarking

• Separated interfaces for instruction fetch and data access (merged into single bus via a bus switch for the NEORV32 processor)

• little-endian byte order

• Configurable hardware reset

• No hardware support of unaligned data/instruction accesses – they will trigger an exception.

 It is recommended to use the NEORV32 Processor as default top instance even if you only want to use the actual CPU. Simply disable all the processor-internal modules via the generics and you will get a "CPU wrapper" that provides a minimal CPU environment and an external bus interface (like AXI4). This setup also allows to further use the default bootloader and software framework. From this base you can start building your own SoC. Of course you can also use the CPU in it’s true stand-alone mode.
 This documentation assumes the reader is familiar with the official RISC-V "User" and "Privileged Architecture" specifications.

### 3.1. Architecture

The NEORV32 CPU was designed from scratch based only on the official ISA / privileged architecture specifications. The following figure shows the simplified architecture of the CPU.

The CPU uses a pipelined architecture with basically two main stages. The first stage (IF – instruction fetch) is responsible for fetching new instruction data from memory via the fetch engine. The instruction data is stored to a FIFO – the instruction prefetch buffer. The issue engine takes this data and assembles 32-bit instruction words for the next pipeline stage. Compressed instructions – if enabled – are also decompressed in this stage. The second stage (EX – execution) is responsible for actually executing the fetched instructions via the execute engine.

These two pipeline stages are based on a multi-cycle processing engine. So the processing of each stage for a certain operations can take several cycles. Since the IF and EX stages are decoupled via the instruction prefetch buffer, both stages can operate in parallel and with overlapping operations. Hence, the optimal CPI (cycles per instructions) is 2, but it can be significantly higher: For instance when executing loads/stores multi-cycle operations like divisions or when the instruction fetch engine has to reload the prefetch buffers due to a taken branch.

Basically, the NEORV32 CPU is somewhere between a classical pipelined architecture, where each stage requires exactly one processing cycle (if not stalled) and a classical multi-cycle architecture, which executes every single instruction in a series of consecutive micro-operations. The combination of these two classical design paradigms allows an increased instruction execution in contrast to a pure multi-cycle approach (due to the pipelined approach) at a reduced hardware footprint (due to the multi-cycle approach).

The CPU provides independent interfaces for instruction fetch and data access. These two bus interfaces are merged into a single processor-internal bus via a bus switch. Hence, memory locations including peripheral devices are mapped to a single 32-bit address space making the architecture a modified Von-Neumann Architecture.

### 3.2. RISC-V Compatibility

The NEORV32 CPU passes the rv32_m/I, rv32_m/M, rv32_m/C, rv32_m/privilege, and rv32_m/Zifencei tests of the official RISC-V Architecture Tests (GitHub). The port files for the NEORV32 processor are located in the repository’s `sw/isa-test` folder.

 See section User Guide: RISC-V Architecture Test Framework for information how to run the tests on the NEORV32.
RISC-V `rv32_m/C` Tests
```Check cadd-01           ... OK
Check cand-01           ... OK
Check candi-01          ... OK
Check cbeqz-01          ... OK
Check cbnez-01          ... OK
Check cebreak-01        ... OK
Check cj-01             ... OK
Check cjal-01           ... OK
Check cjalr-01          ... OK
Check cjr-01            ... OK
Check cli-01            ... OK
Check clui-01           ... OK
Check clw-01            ... OK
Check clwsp-01          ... OK
Check cmv-01            ... OK
Check cnop-01           ... OK
Check cor-01            ... OK
Check cslli-01          ... OK
Check csrai-01          ... OK
Check csrli-01          ... OK
Check csub-01           ... OK
Check csw-01            ... OK
Check cswsp-01          ... OK
Check cxor-01           ... OK
--------------------------------
OK: 27/27 RISCV_TARGET=neorv32 RISCV_DEVICE=C XLEN=32```
RISC-V `rv32_m/I` Tests
```Check add-01            ... OK
Check and-01            ... OK
Check andi-01           ... OK
Check auipc-01          ... OK
Check beq-01            ... OK
Check bge-01            ... OK
Check bgeu-01           ... OK
Check blt-01            ... OK
Check bltu-01           ... OK
Check bne-01            ... OK
Check fence-01          ... OK
Check jal-01            ... OK
Check jalr-01           ... OK
Check lb-align-01       ... OK
Check lbu-align-01      ... OK
Check lh-align-01       ... OK
Check lhu-align-01      ... OK
Check lui-01            ... OK
Check lw-align-01       ... OK
Check or-01             ... OK
Check ori-01            ... OK
Check sb-align-01       ... OK
Check sh-align-01       ... OK
Check sll-01            ... OK
Check slli-01           ... OK
Check slt-01            ... OK
Check slti-01           ... OK
Check sltiu-01          ... OK
Check sltu-01           ... OK
Check sra-01            ... OK
Check srai-01           ... OK
Check srl-01            ... OK
Check srli-01           ... OK
Check sub-01            ... OK
Check sw-align-01       ... OK
Check xor-01            ... OK
Check xori-01           ... OK
--------------------------------
OK: 38/38 RISCV_TARGET=neorv32 RISCV_DEVICE=I XLEN=32```
RISC-V `rv32_m/M` Tests
```Check div-01            ... OK
Check divu-01           ... OK
Check mul-01            ... OK
Check mulh-01           ... OK
Check mulhsu-01         ... OK
Check mulhu-01          ... OK
Check rem-01            ... OK
Check remu-01           ... OK
--------------------------------
OK: 8/8 RISCV_TARGET=neorv32 RISCV_DEVICE=M XLEN=32```
RISC-V `rv32_m/privilege` Tests
```Check ebreak            ... OK
Check ecall             ... OK
Check misalign-beq-01   ... OK
Check misalign-bge-01   ... OK
Check misalign-bgeu-01  ... OK
Check misalign-blt-01   ... OK
Check misalign-bltu-01  ... OK
Check misalign-bne-01   ... OK
Check misalign-jal-01   ... OK
Check misalign-lh-01    ... OK
Check misalign-lhu-01   ... OK
Check misalign-lw-01    ... OK
Check misalign-sh-01    ... OK
Check misalign-sw-01    ... OK
Check misalign1-jalr-01 ... OK
Check misalign2-jalr-01 ... OK
--------------------------------
OK: 16/16 RISCV_TARGET=neorv32 RISCV_DEVICE=privilege XLEN=32```
RISC-V `rv32_m/Zifencei` Tests
```Check Fencei            ... OK
--------------------------------
OK: 1/1 RISCV_TARGET=neorv32 RISCV_DEVICE=Zifencei XLEN=32```

#### 3.2.1. RISC-V Incompatibility Issues and Limitations

This list shows the currently identified issues regarding full RISC-V-compatibility. More specific information can be found in section Instruction Sets and Extensions.

 Hardwired R/W CSRs The `misa`, `mip` and `mtval` CSRs in the NEORV32 are read-only. Any write access to it (in machine mode) to them are ignored and will not cause any exceptions or side-effects.
 Physical memory protection The physical memory protection (see section Machine Physical Memory Protection) only supports the modes OFF and NAPOT yet and a minimal granularity of 8 bytes per region.
 Atomic memory operations The `A` CPU extension only implements the `lr.w` and `sc.w` instructions yet. However, these instructions are sufficient to emulate all further atomic memory operations.
 Instruction Misalignment This is not a real RISC-V incompatibility, but something that might not be clear when studying the RISC-V privileged architecture specifications: for 32-bit only instructions (no `C` extension) the misaligned instruction exception is raised if bit 1 of the access address is set (i.e. not on 32-bit boundary). If the `C` extension is implemented there will be no misaligned instruction exceptions at all. In both cases bit 0 of the program counter and all related registers is hardwired to zero.

### 3.3. CPU Top Entity - Signals

The following table shows all interface signals of the CPU top entity `rtl/core/neorv32_cpu.vhd`. The type of all signals is std_ulogic or std_ulogic_vector, respectively. The "Dir." column shows the signal direction seen from the CPU.

Table 30. NEORV32 CPU top entity signals
Signal Width Dir. Function

Global Signals

`clk_i`

1

in

global clock line, all registers triggering on rising edge

`rstn_i`

1

in

global reset, low-active

`sleep_o`

1

out

CPU is in sleep mode when set

Instruction Bus Interface (Bus Interface)

`i_bus_addr_o`

32

out

`i_bus_rdata_i`

32

in

`i_bus_wdata_o`

32

out

write data (always zero)

`i_bus_ben_o`

4

out

byte enable

`i_bus_we_o`

1

out

write transaction (always zero)

`i_bus_re_o`

1

out

`i_bus_lock_o`

1

out

exclusive access request (always zero)

`i_bus_ack_i`

1

in

bus transfer acknowledge from accessed peripheral

`i_bus_err_i`

1

in

bus transfer terminate from accessed peripheral

`i_bus_fence_o`

1

out

indicates an executed fence.i instruction

`i_bus_priv_o`

2

out

current CPU privilege level

Data Bus Interface (Bus Interface)

`d_bus_addr_o`

32

out

`d_bus_rdata_i`

32

in

`d_bus_wdata_o`

32

out

write data

`d_bus_ben_o`

4

out

byte enable

`d_bus_we_o`

1

out

write transaction

`d_bus_re_o`

1

out

`d_bus_lock_o`

1

out

exclusive access request

`d_bus_ack_i`

1

in

bus transfer acknowledge from accessed peripheral

`d_bus_err_i`

1

in

bus transfer terminate from accessed peripheral

`d_bus_fence_o`

1

out

indicates an executed fence instruction

`d_bus_priv_o`

2

out

current CPU privilege level

System Time (see `time[h]` CSR)

`time_i`

64

in

system time input (from MTIME)

Interrupts, RISC-V-compatible (Traps, Exceptions and Interrupts)

`msw_irq_i`

1

in

RISC-V machine software interrupt

`mext_irq_i`

1

in

RISC-V machine external interrupt

`mtime_irq_i`

1

in

RISC-V machine timer interrupt

Fast Interrupts, NEORV32-specific (Traps, Exceptions and Interrupts)

`firq_i`

16

in

fast interrupt request signals

Enter Debug Mode Request (On-Chip Debugger (OCD))

`db_halt_req_i`

1

in

request CPU to halt and enter debug mode

### 3.4. CPU Top Entity - Generics

Most of the CPU configuration generics are a subset of the actual Processor configuration generics (see section Processor Top Entity - Generics). and are not listed here. However, the CPU provides some specific generics that are used to configure the CPU for the NEORV32 processor setup. These generics are assigned by the processor setup only and are not available for user defined configuration. The specific generics are listed below.

 CPU_DEBUG_ADDR std_ulogic_vector(31 downto 0) 0x00000000 This address defines the entry address for the "execution based" on-chip debugger. By default, this generic is configured with the base address of the debugger memory. See section On-Chip Debugger (OCD) for more information.
 CPU_EXTENSION_RISCV_DEBUG boolean false Implement RISC-V-compatible "debug" CPU operation mode. See section CPU Debug Mode for more information.

### 3.5. Instruction Sets and Extensions

The NEORV32 is an RISC-V `rv32i` architecture that provides several optional RISC-V CPU and ISA (instruction set architecture) extensions. For more information regarding the RISC-V ISA extensions please see the The RISC-V Instruction Set Manual – Volume I: Unprivileged ISA and The RISC-V Instruction Set Manual Volume II: Privileged Architecture, which are available in the projects `docs/references` folder.

 The CPU can discover available ISA extensions via the `misa` CSR and the `CPU` SYSINFO register or by executing an instruction and checking for an illegal instruction exception.
 Executing an instruction from an extension that is not implemented or not enabled (for example via the according top entity generic) will raise an illegal instruction exception.

#### 3.5.1. `A` - Atomic Memory Access

Atomic memory access instructions (for implementing semaphores and mutexes) are available when the `CPU_EXTENSION_RISCV_A` configuration generic is true. In this case the following additional instructions are available:

• `lr.w`: load-reservate

• `sc.w`: store-conditional

 Even though only `lr.w` and `sc.w` instructions are implemented yet, all further atomic operations (load-modify-write instruction) can be emulated using these two instruction. Furthermore, the instruction’s ordering flags (`aq` and `lr`) are ignored by the CPU hardware. Using any other (not yet implemented) AMO (atomic memory operation) will trigger an illegal instruction exception.
 The atomic instructions have special requirements for memory system / bus interconnect. More information can be found in sections Bus Interface and Processor-External Memory Interface (WISHBONE) (AXI4-Lite), respectively.

#### 3.5.2. `C` - Compressed Instructions

Compressed 16-bit instructions are available when the `CPU_EXTENSION_RISCV_C` configuration generic is true. In this case the following instructions are available:

• `c.addi4spn`, `c.lw`, `c.sw`, `c.nop`, `c.addi`, `c.jal`, `c.li`, `c.addi16sp`, `c.lui`, `c.srli`, `c.srai` `c.andi`, `c.sub`, `c.xor`, `c.or`, `c.and`, `c.j`, `c.beqz`, `c.bnez`, `c.slli`, `c.lwsp`, `c.jr`, `c.mv`, `c.ebreak`, `c.jalr`, `c.add`, `c.swsp`

 When the compressed instructions extension is enabled, branches to an unaligned and uncompressed address require an additional instruction fetch to load the required second half-word of that instruction. The performance can be increased again by forcing a 32-bit alignment of branch target addresses. By default, this is enforced via the GCC `-falign-functions=4`, `-falign-labels=4`, `-falign-loops=4` and `-falign-jumps=4` compile flags (via the makefile).

#### 3.5.3. `E` - Embedded CPU

The embedded CPU extensions reduces the size of the general purpose register file from 32 entries to 16 entries to reduce hardware requirements. This extensions is enabled when the `CPU_EXTENSION_RISCV_E` configuration generic is true. Accesses to registers beyond `x15` will raise and illegal instruction exception.

 Due to the reduced register file size an alternate toolchain ABI (`ilp32e`) is required.

#### 3.5.4. `I` - Base Integer ISA

The CPU always supports the complete `rv32i` base integer instruction set. This base set is always enabled regardless of the setting of the remaining exceptions. The base instruction set includes the following instructions:

• immediates: `lui`, `auipc`

• jumps: `jal`, `jalr`

• branches: `beq`, `bne`, `blt`, `bge`, `bltu`, `bgeu`

• memory: `lb`, `lh`, `lw`, `lbu`, `lhu`, `sb`, `sh`, `sw`

• alu: `addi`, `slti`, `sltiu`, `xori`, `ori`, `andi`, `slli`, `srli`, `srai`, `add`, `sub`, `sll`, `slt`, `sltu`, `xor`, `srl`, `sra`, `or`, `and`

• environment: `ecall`, `ebreak`, `fence`

 In order to keep the hardware footprint low, the CPU’s shift unit uses a bit-serial serial approach. Hence, shift operations take up to 32 cycles (plus overhead) depending on the actual shift amount. Alternatively, the shift operations can be processed completely in parallels by a fast (but large) barrel shifter when the `FAST_SHIFT_EN` generic is true. In that case, shift operations complete within 2 cycles (plus overhead) regardless of the actual shift amount.
 Internally, the `fence` instruction does not perform any operation inside the CPU. It only sets the top’s `d_bus_fence_o` signal high for one cycle to inform the memory system a `fence` instruction has been executed. Any flags within the `fence` instruction word are ignore by the hardware.

#### 3.5.5. `M` - Integer Multiplication and Division

Hardware-accelerated integer multiplication and division instructions are available when the `CPU_EXTENSION_RISCV_M` configuration generic is true. In this case the following instructions are available:

• multiplication: `mul`, `mulh`, `mulhsu`, `mulhu`

• division: `div`, `divu`, `rem`, `remu`

 By default, multiplication and division operations are executed in a bit-serial approach. Alternatively, the multiplier core can be implemented using DSP blocks if the `FAST_MUL_EN` generic is true allowing faster execution. Multiplications and divisions always require a fixed amount of cycles to complete - regardless of the input operands.

#### 3.5.6. `Zmmul` - Integer Multiplication

This is a sub-extension of the `M` ISA extension. It implements the multiplication-only operations of the `M` extensions and is intended for small scale applications, that require hardware-based integer multiplications but not hardware-based divisions, which will be computed entirely in software. This extension requires only ~50% of the hardware utilization of the `M` extension.

• multiplication: `mul`, `mulh`, `mulhsu`, `mulhu`

If `Zmmul` is enabled, executing any division instruction from the `M` ISA extension (`div`, `divu`, `rem`, `remu`) will raise an illegal instruction exception.

Note that `M` and `Zmmul` extensions cannot be enabled at the same time.

#### 4.3.1. Targets

Just executing `make` (or executing `make help`) will show the help menu listing all available targets.

``````$make <<< NEORV32 Application Makefile >>> Make sure to add the bin folder of RISC-V GCC to your PATH variable. Targets: help - show this text check - check toolchain info - show makefile/toolchain configuration exe - compile and generate <neorv32_exe.bin> executable for upload via bootloader hex - compile and generate <neorv32_exe.hex> executable raw file install - compile, generate and install VHDL IMEM boot image (for application) sim - in-console simulation using the default testbench and GHDL all - exe + hex + install elf_info - show ELF layout info clean - clean up project clean_all - clean up project, core libraries and image generator bootloader - compile, generate and install VHDL BOOTROM boot image (for bootloader only!)`````` #### 4.3.2. Configuration The compilation flow is configured via variables right at the beginning of the central makefile (`sw/common/common.mk`):  The makefile configuration variables can be (re-)defined directly when invoking the makefile. For example via `$ make MARCH=-march=rv32ic clean_all exe`. You can also make project-specific definitions of all variables inside the project’s actual makefile (e.g., `sw/example/blink_led/makefile`).
``````# *****************************************************************************
# USER CONFIGURATION
# *****************************************************************************
# User's application sources (*.c, *.cpp, *.s, *.S); add additional files here
APP_SRC ?= $(wildcard ./*.c)$(wildcard ./*.s) $(wildcard ./*.cpp)$(wildcard ./*.S)
# User's application include folders (don't forget the '-I' before each entry)
APP_INC ?= -I .
# User's application include folders - for assembly files only (don't forget the '-I' before each
entry)
ASM_INC ?= -I .
# Optimization
EFFORT ?= -Os
# Compiler toolchain
RISCV_PREFIX ?= riscv32-unknown-elf-
# CPU architecture and ABI
MARCH ?= -march=rv32i
MABI  ?= -mabi=ilp32
# User flags for additional configuration (will be added to compiler flags)
USER_FLAGS ?=
# Relative or absolute path to the NEORV32 home folder
NEORV32_HOME ?= ../../..
# *****************************************************************************``````
 APP_SRC The source files of the application (`.c`, `.cpp`, `.S` and `.s` files are allowed; file of these types in the project folder are automatically added via wildcards). Additional files can be added; separated by white spaces APP_INC Include file folders; separated by white spaces; must be defined with `-I` prefix ASM_INC Include file folders that are used only for the assembly source files (`.S`/`.s`). EFFORT Optimization level, optimize for size (`-Os`) is default; legal values: `-O0`, `-O1`, `-O2`, `-O3`, `-Os` RISCV_PREFIX The toolchain prefix to be used; follows the naming convention "architecture-vendor-output-" MARCH The targetd RISC-V architecture/ISA. Only `rv32` is supported by the NEORV32. Enable compiler support of optional CPU extension by adding the according extension letter (e.g. `rv32im` for M CPU extension). See User Guide: Enabling RISC-V CPU Extensions for more information. MABI The default 32-bit integer ABI. USER_FLAGS Additional flags that will be forwarded to the compiler tools NEORV32_HOME Relative or absolute path to the NEORV32 project home folder. Adapt this if the makefile/project is not in the project’s `sw/example folder`. COM_PORT Default serial port for executable upload to bootloader.

#### 4.3.3. Default Compiler Flags

The following default compiler flags are used for compiling an application. These flags are defined via the `CC_OPTS` variable. Custom flags can be appended via the `USER_FLAGS` variable to the `CC_OPTS` variable.

 `-Wall` Enable all compiler warnings. `-ffunction-sections` Put functions and data segment in independent sections. This allows a code optimization as dead code and unused data can be easily removed. `-nostartfiles` Do not use the default start code. The makefiles use the NEORV32-specific start-up code instead (`sw/common/crt0.S`). `-Wl,--gc-sections` Make the linker perform dead code elimination. `-lm` Include/link with `math.h`. `-lc` Search for the standard C library when linking. `-lgcc` Make sure we have no unresolved references to internal GCC library subroutines. `-mno-fdiv` Use builtin software functions for floating-point divisions and square roots (since the according instructions are not supported yet). `-falign-functions=4` Force a 32-bit alignment of functions and labels (branch/jump/call targets). This increases performance as it simplifies instruction fetch when using the C extension. As a drawback this will also slightly increase the program code. `-falign-labels=4` `-falign-loops=4` `-falign-jumps=4`

### 4.4. Executable Image Format

In order to generate a file, which can be executed by the processor, all source files have to be compiler, linked and packed into a final executable.

When all the application sources have been compiled, they need to be linked in order to generate a unified program file. For this purpose the makefile uses the NEORV32-specific linker script `sw/common/neorv32.ld` for linking all object files that were generated during compilation.

The linker script defines three memory sections: `rom`, `ram` and `iodev`. Each section provides specific access attributes: read access (`r`), write access (`w`) and executable (`x`).

Table 48. Linker memory sections - general
Memory section Attributes Description

`ram`

`rwx`

Data memory address space (processor-internal/external DMEM)

`rom`

`rx`

`iodev`

`rw`

Processor-internal memory-mapped IO/peripheral devices address space

These sections are defined right at the beginning of the linker script:

Listing 3. Linker memory sections - cut-out from linker script `neorv32.ld`
``````MEMORY
{
ram  (rwx) : ORIGIN = 0x80000000, LENGTH = DEFINED(make_bootloader) ? 512 : 8*1024
rom   (rx) : ORIGIN = DEFINED(make_bootloader) ? 0xFFFF0000 : 0x00000000, LENGTH = DEFINED(make_bootloader) ? 32K : 2048M
iodev (rw) : ORIGIN = 0xFFFFFE00, LENGTH = 512
}``````

Each memory section provides a base address `ORIGIN` and a size `LENGTH`. The base address and size of the `iodev` section is fixed and must not be altered. The base addresses and sizes of the `ram` and `rom` regions correspond to the total available instruction and data memory address space (see section Address Space Layout).

 `ORIGIN` of the `ram` section has to be always identical to the processor’s `dspace_base_c` hardware configuration. Additionally, `ORIGIN` of the `rom` section has to be always identical to the processor’s `ispace_base_c` hardware configuration.

The sizes of `ram` section has to be equal to the size of the physical available data instruction memory. For example, if the processor setup only uses processor-internal DMEM (MEM_INT_DMEM_EN = true and no external data memory attached) the `LENGTH` parameter of this memory section has to be equal to the size configured by the MEM_INT_DMEM_SIZE generic.

The sizes of `rom` section is a little bit more complicated. The default linker script configuration assumes a maximum of 2GB logical memory space, which is also the default configuration of the processor’s hardware instruction memory address space. This size does not have to reflect the actual physical size of the instruction memory (internal IMEM and/or processor-external memory). It just provides a maximum limit. When uploading new executable via the bootloader, the bootloader itself checks if sufficient physical instruction memory is available. If a new executable is embedded right into the internal-IMEM the synthesis tool will check, if the configured instruction memory size is sufficient (e.g., via the MEM_INT_IMEM_SIZE generic).

 The `rom` region uses a conditional assignment (via the `make_bootloader` symbol) for `ORIGIN` and `LENGTH` that is used to place "normal executable" (i.e. for the IMEM) or "the bootloader image" to their according memories. The `ram` region also uses a conditional assignment (via the `make_bootloader` symbol) for `LENGTH`. When compiling the bootloader (`make_bootloader` symbol is set) the generated bootloader will only use the first 512 bytes of the data address space. This is a fall-back to ensure the bootloader can operate independently of the actual physical data memory size.

The linker maps all the regions from the compiled object files into four final sections: `.text`, `.rodata`, `.data` and `.bss`. These four regions contain everything required for the application to run:

Region Description

`.text`

Executable instructions generated from the start-up code and all application sources.

`.rodata`

Constants (like strings) from the application; also the initial data for initialized variables.

`.data`

This section is required for the address generation of fixed (= global) variables only.

`.bss`

This section is required for the address generation of dynamic memory constructs only.

The `.text` and `.rodata` sections are mapped to processor’s instruction memory space and the `.data` and `.bss` sections are mapped to the processor’s data memory space. Finally, the `.text`, `.rodata` and `.data` sections are extracted and concatenated into a single file `main.bin`.

#### 4.4.2. Executable Image Generator

The `main.bin` file is packed by the NEORV32 image generator (`sw/image_gen`) to generate the final executable file.

 The sources of the image generator are automatically compiled when invoking the makefile.

The image generator can generate three types of executables, selected by a flag when calling the generator:

 `-app_bin` Generates an executable binary file `neorv32_exe.bin` (for UART uploading via the bootloader). `-app_hex` Generates a plain ASCII hex-char file `neorv32_exe.hex` that can be used to initialize custom (instruction-) memories (in synthesis/simulation). `-app_img` Generates an executable VHDL memory initialization image for the processor-internal IMEM. This option generates the `rtl/core/neorv32_application_image.vhd` file. `-bld_img` Generates an executable VHDL memory initialization image for the processor-internal BOOT ROM. This option generates the `rtl/core/neorv32_bootloader_image.vhd` file.

All these options are managed by the makefile. The normal application compilation flow will generate the `neorv32_exe.bin` executable to be upload via UART to the NEORV32 bootloader.

The image generator add a small header to the `neorv32_exe.bin` executable, which consists of three 32-bit words located right at the beginning of the file. The first word of the executable is the signature word and is always `0x4788cafe`. Based on this word the bootloader can identify a valid image file. The next word represents the size in bytes of the actual program image in bytes. A simple "complement" checksum of the actual program image is given by the third word. This provides a simple protection against data transmission or storage errors.

#### 4.4.3. Start-Up Code (crt0)

The CPU and also the processor require a minimal start-up and initialization code to bring the CPU (and the SoC) into a stable and initialized state and to initialize the C runtime environment before the actual application can be executed. This start-up code is located in `sw/common/crt0.S` and is automatically linked every application program and placed right before the actual application code so it gets executed right after reset.

The `crt0.S` start-up performs the following operations:

1. Initialize all integer registers `x1 - x31` (or jsut `x1 - x15` when using the `E` CPU extension) to a defined value.

2. Initialize the global pointer `gp` and the stack pointer `sp` according to the `.data` segment layout provided by the linker script.

3. Initialize all CPU core CSRs and also install a default "dummy" trap handler for all traps. This handler catches all traps during the early boot phase.

4. Clear IO area: Write zero to all memory-mapped registers within the IO region (`iodev` section). If certain devices have not been implemented, a bus access fault exception will occur. This exception is captured by the dummy trap handler.

5. Clear the `.bss` section defined by the linker script.

6. Copy read-only data from the `.text` section to the `.data` section to set initialized variables.

7. Call the application’s `main` function (with no arguments: `argc` = `argv` = 0).

8. If the `main` function returns `crt0` can call an "after-main handler" (see below)

9. If there is no after-main handler or after returning from the after-main handler the processor goes to an endless sleep mode (using a simple loop or via the `wfi` instruction if available).

##### After-Main Handler

If the application’s `main()` function actually returns, an after main handler can be executed. This handler can be a normal function since the C runtime is still available when executed. If this handler uses any kind of peripheral/IO modules make sure these are already initialized within the application or you have to initialize them inside the handler.

Listing 4. After-main handler - function prototype
``int __neorv32_crt0_after_main(int32_t return_code);``

The function has exactly one argument (`return_code`) that provides the return value of the application’s main function. For instance, this variable contains -1 if the main function returned with `return -1;`. The return value of the `__neorv32_crt0_after_main` function is irrelevant as there is no further "software instance" executed afterwards that can check this. However, the on-chip debugger could still evaluate the return value of the after-main handler.

A simple `printf` can be used to inform the user when the application main function return (this example assumes that UART0 has been already properly configured in the actual application):

Listing 5. After-main handler - example
``````int __neorv32_crt0_after_main(int32_t return_code) {

neorv32_uart_printf("Main returned with code: %i\n", return_code);
return 0;
}``````

 This section illustrated the default bootloader from the repository. The bootloader can be customized to target application-specific scenarios. See User Guide section Customizing the Internal Bootloader for more information.

The default NEORV32 bootloader (source code `sw/bootloader/bootloader.c`) provides a build-in firmware that allows to upload new application executables via UART at every time and to optionally store/boot them to/from an external SPI flash. It features a simple "automatic boot" feature that will try to fetch an executable from SPI flash if there is no UART user interaction. This allows to build processor setup with non-volatile application storage, which can be updated at any time.

The bootloader is only implemented if the INT_BOOTLOADER_EN generic is true. This will select the Indirect Boot boot configuration.

 Hardware requirements of the default NEORV32 bootloader REQUIRED: The bootloader requires the CSR access CPU extension (CPU_EXTENSION_RISCV_Zicsr generic is true) and at least 512 bytes of data memory (processor-internal DMEM or external DMEM). RECOMMENDED: For user interaction via UART (like uploading executables) the primary UART (UART0) has to be implemented (IO_UART0_EN generic is true). Without UART the bootloader does not make much sense. However, auto-boot via SPI is still supported but the bootloader should be customized (see User Guide) for this purpose. OPTIONAL: The default bootloader uses bit 0 of the GPIO output port as "heart beat" and status LED if the GPIO controller is implemented (IO_GPIO_EN generic is true). OPTIONAL: The MTIME machine timer (IO_MTIME_EN generic is true) and the SPI controller (IO_SPI_EN generic is true) are required in order to use the bootloader’s auto-boot feature (automatic boot from external SPI flash if there is no user interaction via UART).

To interact with the bootloader, connect the primary UART (UART0) signals (`uart0_txd_o` and `uart0_rxd_o`) of the processor’s top entity via a serial port (-adapter) to your computer (hardware flow control is not used so the according interface signals can be ignored.), configure your terminal program using the following settings and perform a reset of the processor.

Terminal console settings (`19200-8-N-1`):

• 19200 Baud

• 8 data bits

• no parity bit

• 1 stop bit

• newline on `\r\n` (carriage return, newline)

• no transfer protocol / control flow protocol - just the raw byte stuff

The bootloader uses the LSB of the top entity’s `gpio_o` output port as high-active status LED (all other output pin are set to low level by the bootloader). After reset, this LED will start blinking at ~2Hz and the following intro screen should show up in your terminal:

``````<< NEORV32 Bootloader >>

BLDV: Mar 23 2021
HWV:  0x01050208
CLK:  0x05F5E100
MISA: 0x40901105
CPU:  0x00000023
SOC:  0x0EFF0037
IMEM: 0x00004000 bytes @ 0x00000000
DMEM: 0x00002000 bytes @ 0x80000000

Autoboot in 8s. Press key to abort.``````

This start-up screen also gives some brief information about the bootloader and several system configuration parameters:

 `BLDV` Bootloader version (built date). `HWV` Processor hardware version (from the `mimpid` CSR) in BCD format (example: `0x01040606` = v1.4.6.6). `CLK` Processor clock speed in Hz (via the SYSINFO module, from the CLOCK_FREQUENCY generic). `MISA` CPU extensions (from the `misa` CSR). `CPU` CPU sub-extensions (via the `CPU` register in the SYSINFO module) `SOC` Processor configuration (via the `SOC` register in the SYSINFO module / from the IO_* and MEM_* configuration generics). `IMEM` IMEM memory base address and size in byte (from the MEM_INT_IMEM_SIZE generic). `DMEM` DMEM memory base address and size in byte (from the MEM_INT_DMEM_SIZE generic).

Now you have 8 seconds to press any key. Otherwise, the bootloader starts the auto boot sequence. When you press any key within the 8 seconds, the actual bootloader user console starts:

``````<< NEORV32 Bootloader >>

BLDV: Mar 23 2021
HWV:  0x01050208
CLK:  0x05F5E100
USER: 0x10000DE0
MISA: 0x40901105
CPU:  0x00000023
SOC:  0x0EFF0037
IMEM: 0x00004000 bytes @ 0x00000000
DMEM: 0x00002000 bytes @ 0x80000000

Autoboot in 8s. Press key to abort.
Aborted.

Available commands:
h: Help
r: Restart
s: Store to flash
e: Execute
CMD:>``````

The auto-boot countdown is stopped and now you can enter a command from the list to perform the corresponding operation:

• `h`: Show the help text (again)

• `r`: Restart the bootloader and the auto-boot sequence

• `u`: Upload new program executable (`neorv32_exe.bin`) via UART into the instruction memory

• `s`: Store executable to SPI flash at `spi_csn_o(0)`

• `l`: Load executable from SPI flash at `spi_csn_o(0)`

• `e`: Start the application, which is currently stored in the instruction memory (IMEM)

A new executable can be uploaded via UART by executing the `u` command. After that, the executable can be directly executed via the `e` command. To store the recently uploaded executable to an attached SPI flash press `s`. To directly load an executable from the SPI flash press `l`. The bootloader and the auto-boot sequence can be manually restarted via the `r` command.

 The CPU is in machine level privilege mode after reset. When the bootloader boots an application, this application is also started in machine level privilege mode.
 For detailed information on using an SPI flash for application storage see User Guide section Programming an External SPI Flash via the Bootloader.

#### 4.5.1. Auto Boot Sequence

When you reset the NEORV32 processor, the bootloader waits 8 seconds for a UART console input before it starts the automatic boot sequence. This sequence tries to fetch a valid boot image from the external SPI flash, connected to SPI chip select `spi_csn_o(0)`. If a valid boot image is found that can be successfully transferred into the instruction memory, it is automatically started. If no SPI flash is detected or if there is no valid boot image found, and error code will be shown.

If something goes wrong during bootloader operation, an error code is shown. In this case the processor stalls, a bell command and one of the following error codes are send to the terminal, the bootloader status LED is permanently activated and the system must be manually reset.

 `ERROR_0` If you try to transfer an invalid executable (via UART or from the external SPI flash), this error message shows up. There might be a transfer protocol configuration error in the terminal program. Also, if no SPI flash was found during an auto-boot attempt, this message will be displayed. `ERROR_1` Your program is way too big for the internal processor’s instructions memory. Increase the memory size or reduce your application code. `ERROR_2` This indicates a checksum error. Something went wrong during the transfer of the program image (upload via UART or loading from the external SPI flash). If the error was caused by a UART upload, just try it again. When the error was generated during a flash access, the stored image might be corrupted. `ERROR_3` This error occurs if the attached SPI flash cannot be accessed. Make sure you have the right type of flash and that it is properly connected to the NEORV32 SPI port using chip select #0.

### 4.6. NEORV32 Runtime Environment

The NEORV32 provides a minimal runtime environment (RTE) that takes care of a stable and safe execution environment by handling all traps (including interrupts).

 Using the RTE is optional. The RTE provides a simple and comfortable way of delegating traps while making sure that all traps (even though they are not explicitly used by the application) are handled correctly. Performance-optimized applications or embedded operating systems should not use the RTE for delegating traps.

When execution enters the application’s `main` function, the actual runtime environment is responsible for catching all implemented exceptions and interrupts. To activate the NEORV32 RTE execute the following function:

``void neorv32_rte_setup(void);``

This setup initializes the `mtvec` CSR, which provides the base entry point for all trap handlers. The address stored to this register reflects the first-level exception handler provided by the NEORV32 RTE. Whenever an exception or interrupt is triggered, this first-level handler is called.

The first-level handler performs a complete context save, analyzes the source of the exception/interrupt and calls the according second-level exception handler, which actually takes care of the exception/interrupt handling. For this, the RTE manages a private look-up table to store the addresses of the according trap handlers.

After the initial setup of the RTE, each entry in the trap handler’s look-up table is initialized with a debug handler, that outputs detailed hardware information via the primary UART (UART0) when triggered. This is intended as a fall-back for debugging or for accidentally-triggered exceptions/interrupts. For instance, an illegal instruction exception catched by the RTE debug handler might look like this in the UART0 output:

``<RTE> Illegal instruction @0x000002d6, MTVAL=0x00001537 </RTE>``

To install the actual application’s trap handlers the NEORV32 RTE provides functions for installing and un-installing trap handler for each implemented exception/interrupt source.

``int neorv32_rte_exception_install(uint8_t id, void (*handler)(void));``
ID name [C] Description / trap causing entry

`RTE_TRAP_I_MISALIGNED`

`RTE_TRAP_I_ACCESS`

instruction (bus) access fault

`RTE_TRAP_I_ILLEGAL`

illegal instruction

`RTE_TRAP_BREAKPOINT`

breakpoint (`ebreak` instruction)

`RTE_TRAP_L_MISALIGNED`

`RTE_TRAP_L_ACCESS`

`RTE_TRAP_S_MISALIGNED`

`RTE_TRAP_S_ACCESS`

store (bus) access fault

`RTE_TRAP_MENV_CALL`

environment call from machine mode (`ecall` instruction)

`RTE_TRAP_UENV_CALL`

environment call from user mode (`ecall` instruction)

`RTE_TRAP_MTI`

machine timer interrupt

`RTE_TRAP_MEI`

machine external interrupt

`RTE_TRAP_MSI`

machine software interrupt

`RTE_TRAP_FIRQ_0` : `RTE_TRAP_FIRQ_15`

fast interrupt channel 0..15

When installing a custom handler function for any of these exception/interrupts, make sure the function uses no attributes (especially no interrupt attribute!), has no arguments and no return value like in the following example:

``````void handler_xyz(void) {

// handle exception/interrupt...
}``````
 Do NOT use the `interrupt` attribute for the application exception handler functions! This will place an `mret` instruction to the end of it making it impossible to return to the first-level exception handler of the RTE, which will cause stack corruption.

Example: Installation of the MTIME interrupt handler:

``neorv32_rte_exception_install(EXC_MTI, handler_xyz);``

To remove a previously installed exception handler call the according un-install function from the NEORV32 runtime environment. This will replace the previously installed handler by the initial debug handler, so even un-installed exceptions and interrupts are further captured.

``int neorv32_rte_exception_uninstall(uint8_t id);``

Example: Removing the MTIME interrupt handler:

``neorv32_rte_exception_uninstall(EXC_MTI);``
 More information regarding the NEORV32 runtime environment can be found in the doxygen software documentation (also available online at GitHub pages).

## 5. On-Chip Debugger (OCD)

The NEORV32 Processor features an on-chip debugger (OCD) implementing execution-based debugging that is compatible to the Minimal RISC-V Debug Specification Version 0.13.2. Please refer to this spec for in-deep information. A copy of the specification is available in `docs/references/riscv-debug-release.pdf`. The NEORV32 OCD provides the following key features:

• JTAG test access port

• run-control of the CPU: halting, single-stepping and resuming

• executing arbitrary programs during debugging

• compatible to the RISC-V port of OpenOCD; pre-built binaries can be obtained for example from SiFive

 OCD Security Note Access via the OCD is always authenticated (`dmstatus.authenticated` == `1`). Hence, the whole system can always be accessed via the on-chip debugger.
 The OCD requires additional resources for implementation and might also increase the critical path resulting in less performance. If the OCD is not really required for the final implementation, it can be disabled and thus, discarded from implementation. In this case all circuitry of the debugger is completely removed (no impact on area, energy or timing at all).
 A simple example on how to use NEORV32 on-chip debugger in combination with `OpenOCD` and `gdb` is shown in section Debugging using the On-Chip Debugger of the User Guide.

The NEORV32 on-chip debugger complex is based on three hardware modules:

Figure 8. NEORV32 on-chip debugger complex
1. Debug Transport Module (DTM) (`rtl/core/neorv32_debug_dtm.vhd`): External JTAG access tap to allow an external adapter to interface with the debug module(DM) using the debug module interface (dmi).

2. Debug Module (DM) (`rtl/core/neorv32_debug_tm.vhd`): Debugger control unit that is configured by the DTM via the the dmi. Form the CPU’s "point of view" this module behaves as a memory-mapped "peripheral" that can be accessed via the processor-internal bus. The memory-mapped registers provide an internal data buffer for data transfer from/to the DM, a code ROM containing the "park loop" code, a program buffer to allow the debugger to execute small programs defined by the DM and a status register that is used to communicate halt, resume and execute requests/acknowledges from/to the DM.

3. CPU CPU Debug Mode extension (part of`rtl/core/neorv32_cpu_control.vhd`): This extension provides the "debug execution mode" which executes the "park loop" code from the DM. The mode also provides additional CSRs.

Theory of Operation

When debugging the system using the OCD, the debugger issues a halt request to the CPU (via the CPU’s `db_halt_req_i` signal) to make the CPU enter debug mode. In this state, the application-defined architectural state of the system/CPU is "frozen" so the debugger can monitor and even modify it. While in debug mode, the CPU executes the "park loop" code from the code ROM of the DM. This park loop implements an endless loop, in which the CPU polls the memory-mapped status register that is controlled by the debug module (DM). The flags of these register are used to communicate requests from the DM and to acknowledge them by the CPU: trigger execution of the program buffer or resume the halted application.

### 5.1. Debug Transport Module (DTM)

The debug transport module (VHDL module: `rtl/core/neorv32_debug_dtm.vhd`) provides a JTAG test access port (TAP). The DTM is the first entity in the debug system, which connects and external debugger via JTAG to the next debugging entity: the debug module (DM). External JTAG access is provided by the following top-level ports.

Table 50. JTAG top level signals
Name Width Direction Description

`jtag_trst_i`

1

in

TAP reset (low-active); this signal is optional, make sure to pull it high if it is not used

`jtag_tck_i`

1

in

serial clock

`jtag_tdi_i`

1

in

serial data input

`jtag_tdo_o`

1

out

serial data output

`jtag_tms_i`

1

in

mode select

 JTAG Clock The actual JTAG clock signal is not used as primary clock. Instead it is used to synchronize JTGA accesses, while all internal operations trigger on the system clock. Hence, no additional clock domain is required for integration of this module. However, this constraints the maximal JTAG clock (`jtag_tck_i`) frequency to be less than or equal to 1/4 of the system clock (`clk_i`) frequency.
 If the on-chip debugger is disabled (ON_CHIP_DEBUGGER_EN = false) the JTAG serial input `jtag_tdi_i` is directly connected to the JTAG serial output `jtag_tdo_o` to maintain the JTAG chain.
 The NEORV32 JTAG TAP does not provide a boundary check function (yet?). Hence, physical device pins cannot be accessed.

The DTM uses the "debug module interface (dmi)" to access the actual debug module (DM). These accesses are controlled by TAP-internal registers. Each registers is selected by the JTAG instruction register (`IR`) and accessed through the JTAG data register (`DR`).

 The DTM’s instruction and data registers can be accessed using OpenOCDs `irscan` and `drscan` commands. The RISC-V port of OpenOCD also provides low-level command (`riscv dmi_read` & `riscv dmi_write`) to access the dmi debug module interface.

JTAG access is conducted via the instruction register `IR`, which is 5 bit wide, and several data registers `DR` with different sizes. The data registers are accessed by writing the according address to the instruction register. The following table shows the available data registers:

Table 51. JTAG TAP registers
Address (via `IR`) Name Size [bits] Description

`00001`

`IDCODE`

32

identifier, default: `0x0CAFE001` (configurable via package’s `jtag_tap_idcode_*` constants)

`10000`

`DTMCS`

32

debug transport module control and status register

`10001`

`DMI`

41

debug module interface (dmi); 7-bit address, 32-bit read/write data, 2-bit operation (`00` = NOP; `10` = write; `01` = read)

others

`BYPASS`

1

default JTAG bypass register

See the RISC-V debug specification for more information regarding the data registers and operations. A local copy can be found in `docs/references`.

### 5.2. Debug Module (DM)

According to the RISC-V debug specification, the DM (VHDL module: `rtl/core/neorv32_debug_dm.vhd`) acts as a translation interface between abstract operations issued by the debugger and the platform-specific debugger implementation. It supports the following features (excerpt from the debug spec):

• Gives the debugger necessary information about the implementation.

• Allows the hart to be halted and resumed and provides status of the current state.

• Provides access to a reset signal that allows debugging from the very first instruction after reset.

• Provides a mechanism to allow debugging the hart immediately out of reset. (still experimental)

• Provides a Program Buffer to force the hart to execute arbitrary instructions.

• Allows memory access from a hart’s point of view.

The NEORV32 DM follows the "Minimal RISC-V External Debug Specification" to provide full debugging capabilities while keeping resource (area) requirements at a minimum level. It implements the execution based debugging scheme for a single hart and provides the following hardware features:

• program buffer with 2 entries and implicit `ebreak` instruction afterwards

• no direct bus access (indirect bus access via the CPU)

• abstract commands: "access register" plus auto-execution

• no dedicated halt-on-reset capabilities yet (but can be emulated)

The DM provides two "sides of access": access from the DTM via the debug module interface (dmi) and access from the CPU via the processor-internal bus. From the DTM’s point of view, the DM implements a set of DM Registers that are used to control and monitor the actual debugging. From the CPU’s point of view, the DM implements several memory-mapped registers (within the normal address space) that are used for communicating debugging control and status (DM CPU Access).

#### 5.2.1. DM Registers

The DM is controlled via a set of registers that are accessed via the DTM’s dmi. The "Minimal RISC-V Debug Specification" requires only a subset of the registers specified in the spec. The following registers are implemented. Write accesses to any other registers are ignored and read accesses will always return zero. Register names that are encapsulated in "( )" are not actually implemented; however, they are listed to explicitly show their functionality.

Table 52. Available DM registers

`0x04`

`data0`

Abstract data 0, used for data transfer between debugger and processor

`0x10`

`dmcontrol`

Debug module control

`0x11`

`dmstatus`

Debug module status

`0x12`

`hartinfo`

Hart information

`0x16`

`abstracts`

Abstract control and status

`0x17`

`command`

Abstract command

`0x18`

`abstractauto`

Abstract command auto-execution

`0x1d`

(`nextdm`)

Base address of next DM; read as zero to indicate there is only one DM

`0x20`

`progbuf0`

Program buffer 0

`0x21`

`progbuf1`

Program buffer 1

`0x38`

(`sbcs`)

System bus access control and status; read as zero to indicate there is no direct system bus access

`0x40`

`haltsum0`

Halt summary 0

##### `data`
 0x04 Abstract data 0 `data0` Reset value: UNDEFINED Basic read/write registers to be used with abstract command (for example to read/write data from/to CPU GPRs).
##### `dmcontrol`
 0x10 Debug module control register `dmcontrol` Reset value: 0x00000000 Control of the overall debug module and the hart. The following table shows all implemented bits. All remaining bits/bit-fields are configures as "zero" and are read-only. Writing '1' to these bits/fields will be ignored.
Table 53. `dmcontrol` - debug module control register bits
Bit Name [RISC-V] R/W Description

31

`haltreq`

-/w

set/clear hart halt request

30

`resumereq`

-/w

request hart to resume

28

`ackhavereset`

-/w

write `1` to clear `*havereset` flags

1

`ndmreset`

r/w

put whole processor into reset when `1`

0

`dmactive`

r/w

DM enable; writing `0`-`1` will reset the DM

##### `dmstatus`
 0x11 Debug module status register `dmstatus` Reset value: 0x00000000 Current status of the overall debug module and the hart. The entire register is read-only.
Table 54. `dmstatus` - debug module status register bits
Bit Name [RISC-V] Description

31:23

reserved

reserved; always zero

22

`impebreak`

always `1`; indicates an implicit `ebreak` instruction after the last program buffer entry

21:20

reserved

reserved; always zero

19

`allhavereset`

`1` when the hart is in reset

18

`anyhavereset`

17

`allresumeack`

`1` when the hart has acknowledged a resume request

16

`anyresumeack`

15

`allnonexistent`

always zero to indicate the hart is always existent

14

`anynonexistent`

13

`allunavail`

`1` when the DM is disabled to indicate the hart is unavailable

12

`anyunavail`

11

`allrunning`

`1` when the hart is running

10

`anyrunning`

9

`allhalted`

`1` when the hart is halted

8

`anyhalted`

7

`authenticated`

always `1`; there is no authentication

6

`authbusy`

always `0`; there is no authentication

5

`hasresethaltreq`

always `0`; halt-on-reset is not supported (directly)

4

`confstrptrvalid`

always `0`; no configuration string available

3:0

`version`

`0010` - DM is compatible to version 0.13

##### `hartinfo`
 0x12 Hart information `hartinfo` Reset value: see below This register gives information about the hart. The entire register is read-only.
Table 55. `hartinfo` - hart information register bits
Bit Name [RISC-V] Description

31:24

reserved

reserved; always zero

23:20

`nscratch`

`0001`, number of `dscratch*` CPU registers = 1

19:17

reserved

reserved; always zero

16

`dataccess`

`0`, the `data` registers are shadowed in the hart’s address space

15:12

`datasize`

`0001`, number of 32-bit words in the address space dedicated to shadowing the `data` registers = 1

11:0

`dataaddr`

= `dm_data_base_c(11:0)`, signed base address of `data` words (see address map in DM CPU Access)

##### `abstracts`
 0x16 Abstract control and status `abstracts` Reset value: see below Command execution info and status.
Table 56. `abstracts` - abstract control and status register bits
Bit Name [RISC-V] R/W Description

31:29

reserved

r/-

reserved; always zero

28:24

`progbufsize`

r/-

`0010`; size of the program buffer (`progbuf`) = 2 entries

23:11

reserved

r/-

reserved; always zero

12

`busy`

r/-

`1` when a command is being executed

11

reserved

r/-

reserved; always zero

10:8

`cmerr`

r/w

error during command execution (see below); has to be cleared by writing `111`

7:4

reserved

r/-

reserved; always zero

3:0

`datacount`

r/-

`0001`; number of implemented `data` registers for abstract commands = 1

Error codes in `cmderr` (highest priority first):

• `000` - no error

• `100` - command cannot be executed since hart is not in expected state

• `011` - exception during command execution

• `010` - unsupported command

• `001` - invalid DM register read/write while command is/was executing

##### `command`
 0x17 Abstract command `command` Reset value: 0x00000000 Writing this register will trigger the execution of an abstract command. New command can only be executed if `cmderr` is zero. The entire register in write-only (reads will return zero).
 The NEORV32 DM only supports Access Register abstract commands. These commands can only access the hart’s GPRs (abstract command register index `0x1000` - `0x101f`).
Table 57. `command` - abstract command register - "access register" commands only
Bit Name [RISC-V] Description / required value

31:24

`cmdtype`

`00000000` to indicate "access register" command

23

reserved

reserved, has to be `0` when writing

22:20

`aarsize`

`010` to indicate 32-bit accesses

21

`aarpostincrement`

`0`, postincrement is not supported

18

`postexec`

if set the program buffer is executed after the command

17

`transfer`

if set the operation in `write` is conducted

16

`write`

`1`: copy `data0` to `[regno]`; `0` copy `[regno]` to `data0`

15:0

`regno`

GPR-access only; has to be `0x1000` - `0x101f`

##### `abstractauto`
 0x18 Abstract command auto-execution `abstractauto` Reset value: 0x00000000s Register to configure when a read/write access to a DM repeats execution of the last abstract command.
Table 58. `abstractauto` - Abstract command auto-execution register bits
Bit Name [RISC-V] R/W Description

17

`autoexecprogbuf[1]`

r/w

when set reading/writing from/to `progbuf1` will execute `command again`

16

`autoexecprogbuf[0]`

r/w

when set reading/writing from/to `progbuf0` will execute `command again`

0

`autoexecdata[0]`

r/w

when set reading/writing from/to `data0` will execute `command again`

##### `progbuf`
 0x20 Program buffer 0 `progbuf0` 0x21 Program buffer 1 `progbuf1` Reset value: `NOP`-instruction General purpose program buffer for the DM.
##### `haltsum0`
 0x40 Halt summary 0 `haltsum0` Reset value: UNDEFINED Bit 0 of this register is set if the hart is halted (all remaining bits are always zero). The entire register is read-only.

#### 5.2.2. DM CPU Access

From the CPU’s point of view, the DM behaves as a memory-mapped peripheral that includes

• a small ROM that contains the code for the "park loop", which is executed when the CPU is in debug mode.

• a program buffer populated by the debugger host to execute small programs

• a data buffer to transfer data between the processor and the debugger host

• a status register to communicate debugging requests

 Park Loop Code Sources The assembly sources of the park loop code are available in `sw/ocd-firmware/park_loop.S`. Please note, that these sources are not intended to be changed by the used. Hence, the makefile does not provide an automatic option to compile and "install" the debugger ROM code into the HDL sources and require a manual copy (see `sw/ocd-firmware/README.md`).

The DM uses a total address space of 128 words of the CPU’s address space (= 512 bytes) divided into four sections of 32 words (= 128 bytes) each. Please note, that the program buffer, the data buffer and the status register only uses a few effective words in this address space. However, these effective addresses are mirrored to fill up the whole 128 bytes of the section. Hence, any CPU access within this address space will succeed.

Table 59. DM CPU access - address map (divided into four sections)
Base address Name [VHDL package] Actual size Description

`0xfffff800`

`dm_code_base_c` (= `dm_base_c`)

128 bytes

Code ROM for the "park loop" code

`0xfffff880`

`dm_pbuf_base_c`

16 bytes

Program buffer, provided by DM

`0xfffff900`

`dm_data_base_c`

4 bytes

Data buffer (`dm.data0`)

`0xfffff980`

`dm_sreg_base_c`

4 bytes

Control and status register

 From the CPU’s point of view, the DM is mapped to an "unused" address range within the processor’s Address Space right between the bootloader ROM (BOOTROM) and the actual processor-internal IO space at addresses `0xfffff800` - `0xfffff9ff`

When the CPU enters or re-enters (for example via `ebreak` in the DM’s program buffer) debug mode, it jumps to the beginning of the DM’s "park loop" code ROM at `dm_code_base_c`. This is the normal entry point for the park loop code. If an exception is encountered during debug mode, the CPU jumps to `dm_code_base_c + 4`, which is the exception entry point.

Status Register

The status register provides a direct communication channel between the CPU executing the park loop and the host-controlled controller of the DM. Note that all bits that can be written by the CPU (acknowledge flags) cause a single-shot (1-cycle) signal to the DM controller and auto-clear (always read as zero). The bits that are driven by the DM controller and are read-only to the CPU and keep their state until the CPU acknowledges the according request.

Table 60. DM CPU access - status register
Bit Name CPU access Description

0

`halt_ack`

-/w

Set by the CPU to indicate that the CPU is halted and keeps iterating in the park loop

1

`resume_req`

r/-

Set by the DM to tell the CPU to resume normal operation (leave parking loop and leave debug mode via `dret` instruction)

2

`resume_ack`

-/w

Set by the CPU to acknowledge that the CPU is now going to leave parking loop & debug mode

3

`execute_req`

r/-

Set by the DM to tell the CPU to leave debug mode and execute the instructions from the program buffer; CPU will re-enter parking loop afterwards

4

`execute_ack`

-/w

Set by the CPU to acknowledge that the CPU is now going to execute the program buffer

5

`exception_ack`

-/w

Set by the CPU to inform the DM that an exception occurred during execution of the park loop or during execution of the program buffer

### 5.3. CPU Debug Mode

The NEORV32 CPU Debug Mode `DB` (part of `rtl/core/neorv32_cpu_control.vhd`) is compatible to the "Minimal RISC-V Debug Specification 0.13.2". It is enabled/implemented by setting the CPU generic CPU_EXTENSION_RISCV_DEBUG to "true" (done by setting processor generic ON_CHIP_DEBUGGER_EN). It provides a new operation mode called "debug mode". When enabled, three additional CSRs are available (section CPU Debug Mode CSRs) and also the "return from debug mode" instruction `dret` is available when the CPU is "in" debug mode.

 The CPU debug mode requires the `Zicsr` CPU extension to be implemented (top generic CPU_EXTENSION_RISCV_Zicsr = true).

The CPU debug mode is entered when one of the following events appear:

1. executing `ebreak` instruction (when `dcsr.ebreakm` is set and in machine mode OR when `dcsr.ebreaku` is set and in user mode)

2. debug halt request from external DM (via CPU signal `db_halt_req_i`, high-active, triggering on rising-edge)

3. finished executing of a single instruction while in single-step debugging mode (enabled via `dcsr.step`)

From a hardware point of view, these "entry conditions" are special synchronous (`ebreak` instruction) or asynchronous (single-stepping "interrupt"; halt request "interrupt") traps, that are handled invisibly by the control logic.

Whenever the CPU enters debug mode it performs the following operations:

• move `pc` to `dpcs`

• copy the hart’s current privilege level to `dcsr.prv`

• set `dcrs.cause` according to the cause why debug mode is entered

• no update of `mtval`, `mcause`, `mtval` and `mstatus` CSRs

• load the address configured via the CPU CPU_DEBUG_ADDR generic to the `pc` to jump to "debugger park loop" code in the debug module (DM)

When the CPU is in debug mode the following things are important:

• while in debug mode, the CPU executes the parking loop and the program buffer provided by the DM if requested

• effective CPU privilege level is `machine` mode, PMP is not active

• if an exception occurs

• if the exception was caused by any debug-mode entry action the CPU jumps to the normal entry point ( = CPU_DEBUG_ADDR) of the park loop again (for example when executing `ebreak` in debug mode)

• for all other exception sources the CPU jumps to the exception entry point ( = CPU_DEBUG_ADDR + 4) to signal an exception to the DM and restarts the park loop again afterwards

• interrupts are disabled; however, they will be remain pending and will get executed after the CPU has left debug mode

• if the DM makes a resume request, the park loop exits and the CPU leaves debug mode (executing `dret`)

Debug mode is left either by executing the `dret` instruction [15] (in debug mode) or by performing a hardware reset of the CPU. Executing `dret` outside of debug mode will raise an illegal instruction exception. Whenever the CPU leaves debug mode the following things happen:

• set the hart’s current privilege level according to `dcsr.prv`

• restore `pc` from `dpcs`

• resume normal operation at `pc`

#### 5.3.1. CPU Debug Mode CSRs

Two additional CSRs are required by the Minimal RISC-V Debug Specification: The debug mode control and status register `dcsr` and the program counter `dpc`. Providing a general purpose scratch register for debug mode (`dscratch0`) allows faster execution of program provided by the debugger, since one general purpose register can be backup-ed and directly used.

 The debug-mode control and status registers (CSRs) are only accessible when the CPU is in debug mode. If these CSRs are accessed outside of debug mode (for example when in `machine` mode) an illegal instruction exception is raised.
##### `dcsr`
 0x7b0 Debug control and status register `dcsr` Reset value: 0x00000000 The `dcsr` CSR is compatible to the RISC-V debug spec. It is used to configure debug mode and provides additional status information. The following bits are implemented. The reaming bits are read-only and always read as zero.
Table 61. Debug control and status register bits
Bit Name [RISC-V] R/W Event

31:28

`xdebugver`

r/-

always `0100` - indicates external debug support exists

27:16

-

r/-

15

`ebereakm`

r/w

`ebreak` instructions in `machine` mode will enter debug mode when set

14

`ebereakh`

r/-

`0` - hypervisor mode not supported

13

`ebereaks`

r/-

`0` - supervisor mode not supported

12

`ebereaku`

r/w

`ebreak` instructions in `user` mode will enter debug mode when set

11

`stepie`

r/-

`0` - IRQs are disabled during single-stepping

10

`stopcount`

r/-

`0` - counters increment as usual

9

`stoptime`

r/-

`0` - timers increment as usual

8:6

`cause`

r/-

cause identifier - why debug mode was entered

5

-

r/-

4

`mprven`

r/-

`0` - `mstatus.mprv` is ignored when in debug mode

3

`nmip`

r/-

`0` - non-maskable interrupt is pending

2

`step`

r/w

enable single-stepping when set

1:0

`prv`

r/w

CPU privilege level before/after debug mode

##### `dpc`
 0x7b1 Debug program counter `dpc` Reset value: UNDEFINED The `dcsr` CSR is compatible to the RISC-V debug spec. It is used to store the current program counter when debug mode is entered. The `dret` instruction will return to `dpc` by moving `dpc` to `pc`.
##### `dscratch0`
 0x7b2 Debug scratch register 0 `dscratch0` Reset value: UNDEFINED The `dscratch0` CSR is compatible to the RISC-V debug spec. It provides a general purpose debug mode-only scratch register.

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 copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF

The NEORV32 RISC-V Processor
HQ: https://github.com/stnolting/neorv32
Contact: stnolting@gmail.com

### Proprietary Notice

• "GitHub" is a Subsidiary of Microsoft Corporation.

• "AXI", "AXI4-Lite" and "AXI4-Stream" are trademarks of Arm Holdings plc.

• "ModelSim" is a trademark of Mentor Graphics – A Siemens Business.

• "Quartus Prime" and "Cyclone" are trademarks of Intel Corporation.

• "Windows" is a trademark of Microsoft Corporation.

• "Tera Term" copyright by T. Teranishi.

• Timing diagrams made with WaveDrom Editor.

• Documentation made with `asciidoctor`.

### Disclaimer

This project is released under the BSD 3-Clause license. No copyright infringement intended. Other implied or used projects might have different licensing – see their documentation to get more information.

This document contains links to the websites of third parties ("external links"). As the content of these websites is not under our control, we cannot assume any liability for such external content. In all cases, the provider of information of the linked websites is liable for the content and accuracy of the information provided. At the point in time when the links were placed, no infringements of the law were recognizable to us. As soon as an infringement of the law becomes known to us, we will immediately remove the link in question.

### Citing

If you are using the NEORV32 or parts of the project in some kind of publication, please cite it as follows:

 Contributors ❤️ Please add as many contributors as possible to the `authors` field 😉. This project would not be where it is without them. Full names can be found in the repository’s `.mailmap`.
Listing 6. BibTeX
``````@misc{nolting20,
author       = {Nolting, S.},
title        = {The NEORV32 RISC-V Processor},
year         = {2020},
publisher    = {GitHub},
journal      = {GitHub repository},
howpublished = {\url{https://github.com/stnolting/neorv32}}
}``````
 DOI This project also provides a digital object identifier provided by zenodo: https://zenodo.org/record/5121427

### Acknowledgments

A big shoutout to all contributors, who helped improving this project! ❤️

RISC-V - instruction sets want to be free!

### Impressum (Imprint)

1. Pronounced "neo-R-V-thirty-two" or "neo-risc-five-thirty-two" in its long form.
2. Resource utilization depends on actually implemented custom functionality.
3. Pull high if not used.
4. If the on-chip debugger is not implemented (ON_CHIP_DEBUGGER_EN = false) `jtag_tdi_i` is directly forwarded to `jtag_tdo_o` to maintain the JTAG chain.
5. Shift amount.
6. Barrel shift when `FAST_SHIFT_EN` is enabled.
7. Serial shift when `TINY_SHIFT_EN` is enabled.
8. Shift amount (0..31).
9. Barrel shifter when `FAST_SHIFT_EN` is enabled.
10. Memory latency.
11. Memory latency.
12. DSP-based multiplication; enabled via `FAST_MUL_EN`.
13. If the `Zicsr` CPU extension is enabled (implementing the full set of the privileged architecture).
14. This driver file only represents a stub, since the real CFS drivers are defined by the actual CFS implementation.
15. `dret` should only be executed inside the debugger "park loop" code (→ code ROM in the debug module (DM).)