[docs] rework, update and cleanup entire datasheet

This commit is contained in:
stnolting 2023-03-15 22:09:19 +01:00
parent 01f0bb9d31
commit 33a729ca01
32 changed files with 1824 additions and 3318 deletions

View file

@ -1,4 +1,4 @@
:author: Stephan Nolting (M.Sc.)
:author: by Stephan Nolting (M.Sc.)
:keywords: neorv32, risc-v, riscv, rv32, fpga, soft-core, vhdl, microcontroller, cpu, soc, processor, gcc, openocd, gdb
:description: A size-optimized, customizable and highly extensible MCU-class 32-bit RISC-V soft-core CPU and microcontroller-like SoC written in platform-independent VHDL.
:revnumber: v1.8.2
@ -7,6 +7,6 @@
:stem:
:reproducible:
:listing-caption: Listing
:toclevels: 4
:toclevels: 3
:title-logo-image: neorv32_logo_riscv.png[pdfwidth=6.25in,align=center]
:favicon: img/icon.png

File diff suppressed because it is too large Load diff

View file

@ -2,9 +2,8 @@
:sectnums:
=== Custom Functions Unit (CFU)
The Custom Functions Unit is the central part of the <<_zxcfu_custom_instructions_extension_cfu>> and represents
the actual hardware module, which is used to implement _custom RISC-V instructions_. The concept of the NEORV32
CFU has been highly inspired by https://github.com/google/CFU-Playground[Google's CFU-Playground].
The Custom Functions Unit is the central part of the <<_zxcfu_isa_extension>> and represents
the actual hardware module, which can be used to implement _custom RISC-V instructions_.
The CFU is intended for operations that are inefficient in terms of performance, latency, energy consumption or
program memory requirements when implemented entirely in software. Some potential application fields and exemplary
@ -19,8 +18,8 @@ use-cases might include:
[NOTE]
The CFU is not intended for complex and _CPU-independent_ functional units that implement complete accelerators
(like block-based AES encryption). These kind of accelerators should be implemented as memory-mapped
<<_custom_functions_subsystem_cfs>>.
A comparison of all NEORV32-specific chip-internal hardware extension options is provided in the user guide section
<<_custom_functions_subsystem_cfs>>. A comparison of all NEORV32-specific chip-internal hardware extension
options is provided in the user guide section
https://stnolting.github.io/neorv32/ug/#_adding_custom_hardware_modules[Adding Custom Hardware Modules].
@ -28,34 +27,24 @@ https://stnolting.github.io/neorv32/ug/#_adding_custom_hardware_modules[Adding C
==== CFU Instruction Formats
The custom instructions executed by the CFU utilize a specific opcode space in the `rv32` 32-bit instruction
space that has been explicitly reserved for user-defined extensions by the RISC-V specifications ("_Guaranteed Non-Standard
Encoding Space_"). The NEORV32 CFU uses the `custom-x` opcodes to identify the instructions implemented
by the CFU and to differentiate between the different instruction formats.
The according binary encoding of these opcodes is shown below:
space that has been explicitly reserved for user-defined extensions by the RISC-V specifications ("Guaranteed
Non-Standard Encoding Space"). The NEORV32 CFU uses the `custom` opcodes to identify the instructions implemented
by the CFU and to differentiate between the different instruction formats. The according binary encoding of these
opcodes is shown below:
* `custom-0`: `0001011` (R3-type instructions, RISC-V standard)
* `custom-1`: `0101011` (R4-type instructions, RISC-V standard)
* `custom-2`: `1011011` (R5-type instruction A, NEORV32-specific)
* `custom-3`: `1111011` (R5-type instruction B, NEORV32-specific)
.CFU Instructions - Exceptions
[IMPORTANT]
The CPU control logic only analyzes the opcode of the custom instructions to check if the _entire_
instruction word is valid. All remaining bit-fields are **not checked** at all.
This also means that the MSBs of the register fields are **not checked** even if the `E` ISA extension
is enabled (for standard RISC-V instructions this would cause an exception).
Hence, a custom CFU instruction can never raise an illegal instruction exception. If the CFU is not
implemented at all (`Zxcfu` ISA extension is not enabled) any instruction with `custom-x` opcode
will raise an illegal instruction exception.
* `custom-0`: `0001011` RISC-V standard, used for CFU R3-type instructions
* `custom-1`: `0101011` RISC-V standard, used for CFU R4-type instructions
* `custom-2`: `1011011` NEORV32-specific, used for CFU R5-type instruction A
* `custom-3`: `1111011` NEORV32-specific, used for CFU R5-type instruction B
:sectnums:
==== CFU R3-Type Instructions
The R3-type CFU instructions operate on two source registers and return the processing result to the destination register.
The actual operation can be defined by using the `funct7` and `funct3` bit fields. These immediates can also be used to
pass additional data to the CFU like offsets, look-up-tables addresses or shift-amounts. However, the actual
functionality is entirely user-defined.
The R3-type CFU instructions operate on two source registers `rs1` and `rs2` and return the processing result to
the destination register `rd`. The actual operation can be defined by using the `funct7` and `funct3` bit fields.
These immediates can also be used to pass additional data to the CFU like offsets, look-up-tables addresses or
shift-amounts. However, the actual functionality is entirely user-defined.
Example operation: `rd <= rs1 xnor rs2`
@ -75,17 +64,17 @@ The CFU R3-type instruction format is compliant to the RISC-V ISA specification.
.Instruction encoding space
[NOTE]
By using the `funct7` and `funct3` bit fields entirely for selecting the actual operation a total of 1024 custom R3-type
instructions can be implemented (7-bit + 3-bit = 10 bit -> 1024 different values).
By using the `funct7` and `funct3` bit fields entirely for selecting the actual operation a total of 1024 custom
R3-type instructions can be implemented (7-bit + 3-bit = 10 bit -> 1024 different values).
:sectnums:
==== CFU R4-Type Instructions
The R4-type CFU instructions operate on three source registers and return the processing result to the destination register.
The actual operation can be defined by using the `funct3` bit field. Alternatively, this immediate can also be used to
pass additional data to the CFU like offsets, look-up-tables addresses or shift-amounts. However, the actual
functionality is entirely user-defined.
The R4-type CFU instructions operate on three source registers `rs1, `rs2` and `rs2` and return the processing
result to the destination register `rd`. The actual operation can be defined by using the `funct3` bit field.
Alternatively, this immediate can also be used to pass additional data to the CFU like offsets, look-up-tables
addresses or shift-amounts. However, the actual functionality is entirely user-defined.
Example operation: `rd <= (rs1 * rs2 + rs3)[31:0]`
@ -105,23 +94,24 @@ The CFU R4-type instruction format is compliant to the RISC-V ISA specification.
.Unused instruction bits
[NOTE]
The RISC-V ISA specification defines bits [26:25] of the R4-type instruction word to be all-zero. These bits are ignored
by the hardware (CFU and illegal instruction check logic) and should be set to all-zero to preserve compatibility with
future implementations.
The RISC-V ISA specification defines bits [26:25] of the R4-type instruction word to be all-zero. These bits
are ignored by the hardware (CFU and illegal instruction check logic) and should be set to all-zero to preserve
compatibility with future ISA spec. versions.
.Instruction encoding space
[NOTE]
By using the `funct3` bit field entirely for selecting the actual operation a total of 8 custom R4-type instructions
can be implemented (3-bit -> 8 different values).
By using the `funct3` bit field entirely for selecting the actual operation a total of 8 custom R4-type
instructions can be implemented (3-bit -> 8 different values).
:sectnums:
==== CFU R5-Type Instructions
The R5-type CFU instructions operate on three source registers and return the processing result to the destination register.
As all bits of the instruction word are used to encode the five registers and the opcode, no further immediate bits
are available to specify the actual operation. There are two different R5-type instruction with two different opcodes
available. Hence, only two R5-type operations can be implemented out of the box.
The R5-type CFU instructions operate on four source registers `rs1`, `rs2`, `rs3` and `r4` and return the
processing result to the destination register `rd`. As all bits of the instruction word are used to encode the
five registers and the opcode, no further immediate bits are available to specify the actual operation. There
are two different R5-type instruction with two different opcodes available. Hence, only two R5-type operations
can be implemented out of the box.
Example operation: `rd <= rs1 & rs2 & rs3 & rs4`
@ -146,7 +136,7 @@ decoding logic as the location of the remaining register fields is identical to
.RISC-V compatibility
[IMPORTANT]
The RISC-V ISA specifications does not specify a R5-type instruction format. Hence, this instruction
layout is NEORV32-specific.
format is NEORV32-specific.
.Instruction encoding space
[IMPORTANT]
@ -160,9 +150,9 @@ writing operation information to a CFU-internal "command" register.
==== Using Custom Instructions in Software
The custom instructions provided by the CFU can be used in plain C code by using **intrinsics**. Intrinsics
behave like "normal" functions but under the hood they are a set of macros that hide the complexity of inline assembly.
Using intrinsics removes the need to modify the compiler, built-in libraries or the assembler when including custom
instructions. Each intrinsic will result in a single 32-bit instruction word providing maximum code efficiency.
behave like "normal" C functions but under the hood they are a set of macros that hide the complexity of inline assembly.
Using intrinsics removes the need to modify the compiler, built-in libraries or the assembler when using custom
instructions. Each intrinsic will be compiled into a single 32-bit instruction word providing maximum code efficiency.
The NEORV32 software framework provides four pre-defined prototypes for custom instructions, which are defined in
`sw/lib/include/neorv32_cpu_cfu.h`:
@ -177,18 +167,18 @@ neorv32_cfu_r5_instr_b(rs1, rs2, rs3, rs4) // R5-type instruction B
----
The intrinsic functions always return a 32-bit value of type `uint32_t` (the processing result), which can be discarded
when not needed. Each intrinsic function requires several arguments depending on the instruction type/format:
if not needed. Each intrinsic function requires several arguments depending on the instruction type/format:
* `funct7` - 7-bit immediate (R3-type only)
* `funct3` - 3-bit immediate (R3-type, R4-type)
* `rs1` - source operand 1, 32-bit (R3-type, R4-type)
* `rs2` - source operand 2, 32-bit (R3-type, R4-type)
* `rs3` - source operand 2, 32-bit (R3-type, R4-type, R5-type)
* `rs4` - source operand 2, 32-bit (R4-type, R4-type, R5-type)
* `rs3` - source operand 3, 32-bit (R3-type, R4-type, R5-type)
* `rs4` - source operand 4, 32-bit (R4-type, R4-type, R5-type)
The `funct3` and `funct7` bit-fields are used to pass 3-bit or 7-bit literals to the CFU. The `rs1`, `rs2` and `rs3`
arguments pass the actual data to the CFU. These register arguments can be populated with variables or literals.
The following example shows how to pass arguments when executing both CFU instruction types:
The `funct3` and `funct7` bit-fields are used to pass 3-bit or 7-bit literals to the CFU. The `rs1`, `rs2`, `rs3`
and `r4` arguments pass the actual data to the CFU. These register arguments can be populated with variables or
literals. The following example shows how to pass arguments when executing all exemplary CFU instruction types:
.CFU instruction usage example
[source,c]
@ -196,7 +186,7 @@ The following example shows how to pass arguments when executing both CFU instru
uint32_t tmp = some_function();
...
uint32_t res = neorv32_cfu_r3_instr(0b0000000, 0b101, tmp, 123);
uint32_t foo = neorv32_cfu_r4_instr(0b011, tmp, res, some_array[i]);
uint32_t foo = neorv32_cfu_r4_instr(0b011, tmp, res, (uint32_t)some_array[i]);
uint32_t bar = neorv32_cfu_r5_instr_a(tmp, res, foo, tmp);
----
@ -212,6 +202,11 @@ This example program is located in `sw/example/demo_cfu`.
The actual functionality of the CFU's custom instructions is defined by the user-defined logic inside
the CFU hardware module `rtl/core/neorv32_cpu_cp_cfu.vhd`.
CFU operations can be entirely combinatorial (like bit-reversal) so the result is available at the end of
the current clock cycle. Operations can also take several clock cycles to complete (like multiplications)
and may also include internal states and memories. The CFU's internal control unit takes care of
interfacing the custom user logic to the CPU pipeline.
.CFU Hardware Example & More Details
[TIP]
The default CFU hardware module already implement some exemplary instructions that are used for illustration
@ -224,13 +219,14 @@ Enabling the CFU and actually implementing R4-type and/or R5-type instructions (
the according operands for the CFU hardware) will add one or two additional read ports to the core's
register file increasing resource requirements.
CFU operations can be entirely combinatorial (like bit-reversal) so the result is available at the end of
the current clock cycle. Operations can also take several clock cycles to complete (like multiplications)
and may also include internal states and memories. The CFU's internal control/proxy unit takes care of
interfacing the custom user logic to the CPU pipeline.
.CFU Execution Time
[NOTE]
The CFU has to complete computation within a **bound time window**. Otherwise, the CFU operation is terminated
by the hardware and an illegal instruction exception is raised. See section <<_cpu_arithmetic_logic_unit>>
for more information.
.CFU Exception
[NOTE]
The CFU can intentionally raise an illegal instruction exception by not asserting the "done" signal within
a bound time window. For example this can be used to signal invalid configurations/operations to the runtime
environment. See the CFU's VHDL file for more information.

File diff suppressed because it is too large Load diff

View file

@ -2,9 +2,9 @@
:sectnums:
== On-Chip Debugger (OCD)
The NEORV32 Processor features an _on-chip debugger_ (OCD) implementing **execution-based debugging** compatible
to the **Minimal RISC-V Debug Specification Version 1.0**. Please refer to this spec for in-deep information.
A copy of the specification is available in `docs/references`.
The NEORV32 Processor features an _on-chip debugger_ (OCD) implementing the **execution-based debugging** scheme,
which is compatible to the **Minimal RISC-V Debug Specification Version 1.0**. A copy of the specification is
available in `docs/references`.
**Section Structure**
@ -13,9 +13,9 @@ A copy of the specification is available in `docs/references`.
* <<_cpu_debug_mode>>
* <<_trigger_module>>
The NEORV32 OCD provides the following key features:
**Key Features**
* JTAG access port
* standard JTAG access port
* run-control of the CPU: halting, single-stepping and resuming
* executing arbitrary programs during debugging
* indirect access to all core registers (via program buffer)
@ -25,15 +25,13 @@ The NEORV32 OCD provides the following key features:
.OCD Security Note
[NOTE]
Access via the OCD is _always authenticated_ (`dmstatus.authenticated = 1`). Hence, the
_whole system_ can always be accessed via the on-chip debugger. Currently, there is no option
to disable the OCD via software - the OCD can only be disabled by disabling implementation
(setting <<_on_chip_debugger_en>> generic to _false_).
Access via the OCD is **always authenticated** (`dmstatus.authenticated = 1`). Hence, the entire system can always
be accessed via the on-chip debugger.
.Hands-On Tutorial
[TIP]
A simple example on how to use NEORV32 on-chip debugger in combination with OpenOCD and the GNU debugger
is shown in section https://stnolting.github.io/neorv32/ug/#_debugging_using_the_on_chip_debugger[Debugging using the On-Chip Debugger]
A simple example on how to use NEORV32 on-chip debugger in combination with OpenOCD and the GNU debugger is shown in
section https://stnolting.github.io/neorv32/ug/#_debugging_using_the_on_chip_debugger[Debugging using the On-Chip Debugger]
of the User Guide.
The NEORV32 on-chip debugger complex is based on four hardware modules:
@ -43,19 +41,17 @@ image::neorv32_ocd_complex.png[align=center]
[start=1]
. <<_debug_transport_module_dtm>> (`rtl/core/neorv32_debug_dtm.vhd`): JTAG access tap to allow an external
adapter to interface with the _debug module(DM)_ using the _debug module interface (dmi)_ - this interface is compatible to
the interface description shown in Appendix 3 of the "RISC-V debug stable" specification.
. <<_debug_module_dm>> (`rtl/core/neorv32_debug_tm.vhd`): Debugger control unit that is configured by the DTM via the
the _dmi_. From the CPU's "point of view" this module behaves as another 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
_exception, _halt_, _resume_ and _execute_ requests/acknowledges from/to the DM.
. 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.
. CPU <<_trigger_module>> (also part of `rtl/core/neorv32_cpu_control.vhd`):
This module provides a single _hardware_ breakpoint, which allows to debug code executed from ROM.
adapter to interface with the _debug module (DM)_ using the _debug module interface (dmi)_ - this interface is compatible to
the interface description shown in Appendix 3 of the "RISC-V debug" specification.
. <<_debug_module_dm>> (`rtl/core/neorv32_debug_tm.vhd`): Debugger control unit that is configured by the DTM via the _dmi_.
From the CPU's "point of view" this module behaves as another 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 _exception, _halt_, _resume_ and _execute_ requests/acknowledges from/to the DM.
. 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.
. CPU <<_trigger_module>> (also part of `rtl/core/neorv32_cpu_control.vhd`): This module provides a single _hardware_ breakpoint,
which allows to debug code executed from ROM.
**Theory of Operation**
@ -65,12 +61,11 @@ state of the system/CPU is "frozen" so the debugger can monitor if without inter
However, the OCD can also modify the entire architectural state at any time.
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
This park loop implements an endless loop, where the CPU polls the memory-mapped _status register_ that is
controlled by the _debug module (DM)_. The flags in this 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. Furthermore, the CPU uses this register to signal that the CPU has halted after a halt request
and to signal that an exception has fired while in debug mode.
and to signal that an exception has fired while being in debug mode.
<<<
@ -79,8 +74,6 @@ and to signal that an exception has fired while in debug mode.
=== 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.
.JTAG top level signals
@ -88,7 +81,7 @@ External JTAG access is provided by the following top-level ports.
[options="header",grid="rows"]
|=======================
| 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 by the JTAG adapter
| `jtag_trst_i` | 1 | in | TAP reset (low-active); this signal is optional, make sure to pull it _high_ if 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
@ -97,25 +90,14 @@ External JTAG access is provided by the following top-level ports.
.Maximum JTAG Clock
[IMPORTANT]
All JTAG signals are synchronized to the processor clock domain by oversampling them in the DTM. Hence, no additional
clock domain is required for the DTM. However, this constraints the maximal JTAG clock frequency (`jtag_tck_i`) to be less
than or equal to **1/5** of the processor clock frequency (`clk_i`).
All JTAG signals are synchronized to the processor's clock domain. Hence, no additional clock domain is required for the DTM.
However, this constraints the maximal JTAG clock frequency (`jtag_tck_i`) to be less than or equal to **1/5** of the processor
clock frequency (`clk_i`).
[NOTE]
If the on-chip debugger is disabled (<<_on_chip_debugger_en>> = false) the JTAG serial input `jtag_tdi_i` is directly
If the on-chip debugger is disabled the JTAG serial input `jtag_tdi_i` is directly
connected to the JTAG serial output `jtag_tdo_o` to maintain the JTAG chain.
[NOTE]
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).
The accesses are controlled by TAP-internal registers, which are selected by the JTAG instruction register (`IR`)
and accessed through the JTAG data register (`DR`).
[NOTE]
The DTM's instruction and data registers can be accessed using OpenOCD's `irscan` and `drscan` commands.
OpenOCD also provides low-level RISC-V-specific commands for direct DMI accesses (`riscv dmi_read` & `riscv dmi_write`).
JTAG accesses are based on a single _instruction register_ `IR`, which is 5 bit wide, and several _data registers_ `DR`
with different sizes. The individual data registers are accessed by writing the according address to the instruction
register. The following table shows the available data registers and their addresses:
@ -146,16 +128,9 @@ register. The following table shows the available data registers and their addre
| 3:0 | `version` | r/- | `0001` = DTM is compatible to spec. version 0.13 & 1.0
|=======================
[INFO]
See the https://github.com/riscv/riscv-debug-spec[RISC-V debug specification] for more information regarding the data
registers and operations. A local copy can be found in `docs/references`.
[NOTE]
Most FPGAs are programmed over a JTAG connection itself and support the use of it in user designs with instantiation of
platform-specific entities. So instead of two JTAG connections, one to program the FPGA and one to debug the core,
only one connection is needed. See the setups in [`neorv32-setups`](https://github.com/stnolting/neorv32-setups)
for example implementations.
The DTM's instruction and data registers can be accessed using OpenOCD's `irscan` and `drscan` commands.
OpenOCD also provides low-level RISC-V-specific commands for direct DMI accesses (`riscv dmi_read` & `riscv dmi_write`).
<<<
@ -163,9 +138,9 @@ for example implementations.
:sectnums:
=== 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 (application) and the
platform-specific debugger (circuit) implementation. It supports the following features (excerpt from the debug spec):
The debug module "DM" (VHDL module: `rtl/core/neorv32_debug_dm.vhd`) acts as a translation interface between abstract
operations issued by the debugger (application) and the platform-specific debugger (circuit) implementation.
It supports the following features:
* Gives the debugger necessary information about the implementation.
* Allows the hart to be halted and resumed and provides status of the current state.
@ -175,10 +150,9 @@ platform-specific debugger (circuit) implementation. It supports the following f
* 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:
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 using the program buffer
@ -195,9 +169,7 @@ debugging control and status (<<_dm_cpu_access>>).
:sectnums:
==== 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:
The DM is controlled via a set of registers that are accessed via the DTM's _dmi_. The following registers are implemented:
[NOTE]
Write accesses to registers that are not implemented are simply ignored and read accesses will always return zero.
@ -219,7 +191,7 @@ their functionality.
| `0x1d` | (`nextdm`) | Base address of _next_ DM; reads 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; reads as zero to indicate there is no _direct_ system bus access
| `0x38` | (`sbcs`) | System bus access control and status; reads as zero to indicate there is **no** direct system bus access
|=======================
@ -247,7 +219,7 @@ their functionality.
are configured as "zero" and are read-only. Writing '1' to these bits/fields will be ignored.
|======
.`dmcontrol` - debug module control register bits
.`dmcontrol` Register Bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
@ -255,7 +227,7 @@ are configured as "zero" and are read-only. Writing '1' to these bits/fields wil
| 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`
| 1 | `ndmreset` | r/w | put whole processor into reset sate when `1`
| 0 | `dmactive` | r/w | DM enable; writing `0`-`1` will reset the DM
|=======================
@ -271,7 +243,7 @@ are configured as "zero" and are read-only. Writing '1' to these bits/fields wil
3+| Current status of the overall debug module and the hart. The entire register is read-only.
|======
.`dmstatus` - debug module status register bits
.`dmstatus` Register Bits
[cols="^1,^2,<10"]
[options="header",grid="rows"]
|=======================
@ -310,7 +282,7 @@ are configured as "zero" and are read-only. Writing '1' to these bits/fields wil
3+| This register gives information about the hart. The entire register is read-only.
|======
.`hartinfo` - hart information register bits
.`hartinfo` Register Bits
[cols="^1,^2,<8"]
[options="header",grid="rows"]
|=======================
@ -335,7 +307,7 @@ are configured as "zero" and are read-only. Writing '1' to these bits/fields wil
3+| Command execution info and status.
|======
.`abstracts` - abstract control and status register bits
.`abstracts` Register Bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
@ -358,10 +330,6 @@ Error codes in `cmderr` (highest priority first):
* `010` - unsupported command
* `001` - invalid DM register read/write while command is/was executing
.PMP Rules
[NOTE]
When in debug-mode all PMP rules are ignored making the debugger have maximum access rights.
:sectnums!:
===== **`command`**
@ -379,7 +347,7 @@ When in debug-mode all PMP rules are ignored making the debugger have maximum ac
The NEORV32 DM only supports **Access Register** abstract commands. These commands can only access the
hart's GPRs (abstract command register index `0x1000` - `0x101f`).
.`command` - abstract command register - "access register" commands only
.`command` Register Bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
@ -406,7 +374,7 @@ hart's GPRs (abstract command register index `0x1000` - `0x101f`).
3+| Register to configure when a read/write access to a DM repeats execution of the last abstract command.
|======
.`abstractauto` - Abstract command auto-execution register bits
.`abstractauto` Register Bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
@ -439,13 +407,11 @@ From the CPU's perspective, the DM behaves as a memory-mapped peripheral that in
* 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 and status
(see `sw/ocd-firmware/README.md`).
The DM occupies 256 bytes of the CPU's address space starting at address `dm_base_c` (see table below).
This address space is divided into four sections of 64 bytes each to provide access to the _park loop code ROM_,
the _program buffer_, the _data buffer_ and the _status register_. The program buffer, the data buffer and the
status register do not fully occupy the 64-byte-wide sections. However, the according registers are mirrored
to fill the entire section.
status register do not fully occupy the 64-byte-wide sections and are mirrored to fill the entire section.
.DM CPU access - address map (divided into four sections)
[cols="^2,^4,^2,<7"]
@ -488,11 +454,11 @@ has occurred _while executing_ the park loop itself.
|=======================
When the CPU enters or re-enters debug mode (for example via an `ebreak` in the DM's program buffer), it jumps to
address of the _normal entry point_ for the park loop code defined by the <<_cpu_debug_park_addr>> generic.
By default, this generic is set to `dm_park_entry_c`, which is defined in main package file.
If an exception is encountered during debug mode, the CPU jumps to the address of the _exception entry point_
defined by the <<_cpu_debug_exc_addr>> generic. By default, this generic is set to `dm_exc_entry_c`, which is
also defined in main package file.
address of the _normal entry point_ for the park loop code defined by the `CPU_DEBUG_PARK_ADDR` generic
(<<_cpu_top_entity_generics>>). By default, this generic is set to `dm_park_entry_c`, which is defined in main
package file. If an exception is encountered during debug mode, the CPU jumps to the address of the _exception
entry point_ defined by the `CPU_DEBUG_EXC_ADDR` generic (<<_cpu_top_entity_generics>>). By default, this generic
is set to `dm_exc_entry_c`, which is also defined in main package file.
:sectnums:
@ -522,12 +488,6 @@ and faster execution.
| `sreg_execute_ack` | write <| Set by the CPU if an exception occurs while being in debug mode
|=======================
.Access Details
[NOTE]
The underlaying hardware to implement the CPU access to the status register is highly optimized to provide
fastest access times while requiring minimal code and hardware size: the actual data written by the CPU is irrelevant
as only the sub-byte accesses (so, the actual bus transactions) are tracked by the status register hardware.
<<<
// ####################################################################################################################
@ -535,23 +495,25 @@ as only the sub-byte accesses (so, the actual bus transactions) are tracked by t
=== CPU Debug Mode
The NEORV32 CPU Debug Mode `DB` or `DEBUG` is compatible to the **Minimal RISC-V Debug Specification 1.0**
`Sdext` (external debug) ISA extension. When enabled via the <<_cpu_extension_riscv_sdext>> generic (CPU) and/or
the <<_on_chip_debugger_en>> (Processor) it adds a new CPU operation mode ("debug mode"), three additional CSRs
`Sdext` (external debug) ISA extension. When enabled via the <<_sdext_isa_extension>> generic (CPU) and/or
the `ON_CHIP_DEBUGGER_EN` (Processor) it adds a new CPU operation mode ("debug mode"), three additional CSRs
(section <<_cpu_debug_mode_csrs>>) and one additional instruction (`dret`) to the core.
[IMPORTANT]
The CPU debug mode requires the `Zicsr` and `Zifencei` CPU extension to be implemented (top generics
<<_cpu_extension_riscv_zicsr>> and <<_cpu_extension_riscv_zifencei>> = true).
<<_zicsr_isa_extension>> and <<_zifencei_isa_extension>> = true).
The CPU debug-mode is entered when one of the following events appear:
The CPU debug-mode is entered on any of the following events:
[start=1]
. executed `ebreak` instruction (when in machine-mode and `dcsr.ebreakm` is set OR when in user-mode and `dcsr.ebreaku` is set)
. executed `ebreak` instruction (when in machine-mode and <<_dcsr>>`.ebreakm` is set OR when in user-mode and <<_dcsr>>`.ebreaku` is set)
. debug halt request from external DM (via CPU signal `db_halt_req_i`, high-active, triggering on rising-edge)
. finished executing of a single instruction while in single-step debugging mode (enabled via `dcsr.step`)
. finished executing of a single instruction while in single-step debugging mode (enabled via <<_dcsr>>`.step`)
. hardware trigger by the <<_trigger_module>>
From a hardware point of view, these "entry conditions" are special traps that are handled transparently by the control logic.
[NOTE]
From a hardware point of view, these "entry conditions" are special traps that are handled transparently by
the control logic.
**Whenever the CPU enters debug-mode it performs the following operations:**
@ -560,7 +522,7 @@ From a hardware point of view, these "entry conditions" are special traps that a
* 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's <<_cpu_debug_park_addr>> generic to the `pc` to jump to the
* load the address configured via the CPU's `CPU_DEBUG_PARK_ADDR` (<<_cpu_top_entity_generics>>) generic to the `pc` to jump to the
"debugger park loop" code stored in the debug module (DM)
**When the CPU is in debug-mode the following things are important:**
@ -570,8 +532,8 @@ From a hardware point of view, these "entry conditions" are special traps that a
* the `wfi` instruction acts as a `nop` (also during single-stepping)
* if an exception occurs while being in debug mode:
** if the exception was caused by any debug-mode entry action the CPU jumps to the _normal entry point_
(defined by <<_cpu_debug_park_addr>> generic) of the park loop again (for example when executing `ebreak` while in debug-mode)
** for all other exception sources the CPU jumps to the _exception entry point_ (defined by <<_cpu_debug_exc_addr>> generic)
(defined by `CPU_DEBUG_PARK_ADDR` generic of the <<_cpu_top_entity_generics>>) of the park loop again (for example when executing `ebreak` while in debug-mode)
** for all other exception sources the CPU jumps to the _exception entry point_ (defined by `CPU_DEBUG_EXC_ADDR` generic of the <<_cpu_top_entity_generics>>)
to signal an exception to the DM; the CPU restarts the park loop again afterwards
* interrupts are disabled; however, they will 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`)
@ -579,8 +541,8 @@ From a hardware point of view, these "entry conditions" are special traps that a
<<_machine_system_timer_mtime>> keep running as well as it's shadowed copies in the `[m]time[h]` CSRs
* all <<_hardware_performance_monitors_hpm_csrs>> are stopped
Debug mode is left either by executing the `dret` instruction or by performing
a hardware reset of the CPU. Executing `dret` outside of debug mode will raise an illegal instruction exception.
Debug mode is left either by executing the `dret` instruction 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 it performs the following operations:**
@ -610,7 +572,6 @@ an illegal instruction exception is raised.
| 0x7b0 | **Debug control and status register** | `dcsr`
3+<| Reset value: `0x40000413`
3+<| 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.
|======
.Debug control and status register `dcsr` bits
@ -637,7 +598,7 @@ The following bits are implemented. The reaming bits are read-only and always re
Cause codes in `dcsr.cause` (highest priority first):
* `010` - trigger by hardware <<_trigger_module>>
* `010` - triggered by hardware <<_trigger_module>>
* `001` - executed `EBREAK` instruction
* `011` - external halt request (from DM)
* `100` - return from single-stepping
@ -674,7 +635,7 @@ debug mode is entered. The `dret` instruction will return to `dpc` by moving `dp
=== Trigger Module
The RISC-V `Sdtrig` ISA extension add a programmable _trigger module_ to the processor when enabled
(via the <<_cpu_extension_riscv_sdtrig>>). The NEORV32 trigger module implements a subset of the features
(via the <<_sdtrig_isa_extension>>). The NEORV32 trigger module implements a subset of the features
described in the "RISC-V Debug Specification / Trigger Module".
The trigger module only provides a _single_ trigger supporting only the "instruction address match" type. This limitation
@ -720,7 +681,7 @@ for implementing a "hardware breakpoint"
Write attempts to the hardwired bits are ignored.
|======
.Match control CSR (`tdata1`) bits
.Match Control CSR (`tdata1`) Bits
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================

View file

@ -1,9 +1,9 @@
:sectnums:
== Overview
The NEORV32footnote:[Pronounced "neo-R-V-thirty-two" or "neo-risc-five-thirty-two" in its long form.] 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 NEORV32 RISC-V Processor 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
@ -63,24 +63,24 @@ include::rationale.adoc[]
* all-in-one package: **CPU** + **SoC** + **Software Framework & Tooling**
* completely described in behavioral, platform-independent VHDL - no vendor- or technology-specific primitives, attributes, macros, libraries, etc. are used at all
* all-Verilog "version" https://github.com/stnolting/neorv32-verilog[available] (auto-generated netlist)
* all-Verilog "version" available (auto-generated netlist)
* extensive configuration options for adapting the processor to the requirements of the application
* highly https://stnolting.github.io/neorv32/ug/#_comparative_summary[extensible hardware] - on CPU, SoC and system level
* highly extensible hardware - on CPU, SoC and system level
* aims to be as small as possible while being as RISC-V-compliant as possible - with a reasonable area-vs-performance trade-off
* FPGA friendly (e.g. _all_ internal memories can be mapped to block RAM - including the register file)
* FPGA friendly (e.g. all internal memories can be mapped to block RAM - including the register file)
* optimized for high clock frequencies to ease timing closure and integration
* from zero to _"hello world!"_ - completely open source and documented
* easy to use even for FPGA/RISC-V starters intended to _work out of the box_
**NEORV32 CPU (the core)**
* 32-bit `rv32i` RISC-V CPU
* fully RISC-V ISA compatible - checked by the https://github.com/stnolting/neorv32-riscof[official RISCOF architecture tests]
* 32-bit RISC-V CPU
* fully compatible to the RISC-V ISA specs. - checked by the https://github.com/stnolting/neorv32-riscof[official RISCOF architecture tests]
* base ISA + privileged ISA + several optional standard and custom ISA extensions
* option to add custom RISC-V instructions as custom ISA extension
* rich set of customization options (ISA extensions, design goal: performance / area (/ energy), ...)
* aims to support <<_full_virtualization>> capabilities to increase execution safety
* official https://github.com/riscv/riscv-isa-manual/blob/master/marchid.md[RISC-V open source architecture ID]: decimal **19**; hexadecimal `0x00000013`
* option to add user-defined RISC-V instructions as custom ISA extension
* rich set of customization options (ISA extensions, design goal: performance / area / energy, tuning options, ...)
* <<_full_virtualization>> capabilities to increase execution safety
* official RISC-V open source architecture ID
**NEORV32 Processor (the SoC)**
@ -90,8 +90,8 @@ include::rationale.adoc[]
* optional timers and counters (watchdog, system timer)
* optional general purpose IO and PWM; a 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
* optional execute_in_place (XIP) module to execute code _directly_ form external SPI flash
* optional external memory interface for custom connectivity
* optional execute in-place (XIP) module to execute code directly form an external SPI flash
* on-chip debugger compatible with OpenOCD and gdb including hardware trigger module
**Software framework**
@ -103,17 +103,14 @@ include::rationale.adoc[]
* 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
[TIP]
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>>.
**Extensibility and Customization**
The NEORV32 processor was designed to ease customization and extensibility and provides several options for adding
The NEORV32 processor is designed to ease customization and extensibility and provides several options for adding
application-specific custom hardware modules and accelerators. The three most common options for adding custom
on-chip modules are listed below.
* <<_processor_external_memory_interface_wishbone_axi4_lite>> for processor-external modules
* <<_processor_external_memory_interface_wishbone>> to attach processor-external IP modules
* <<_custom_functions_subsystem_cfs>> for tightly-coupled processor-internal co-processors
* <<_custom_functions_unit_cfu>> for custom RISC-V instructions
@ -169,11 +166,11 @@ neorv32 - Project home folder
=== 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`**.
of the entire processor including all the required configuration generics is `neorv32_top.vhd`.
.NEORV32 VHDL Library
[IMPORTANT]
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.
All core VHDL files from the list below have to be assigned to a new design library named `neorv32`.
...................................
neorv32_top.vhd - NEORV32 Processor top entity
@ -230,7 +227,7 @@ neorv32_top.vhd - NEORV32 Processor top entity
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.vhd` 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
_platform independent_ memory design (inferring 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.
@ -240,12 +237,10 @@ and/or timing.
:sectnums:
=== FPGA Implementation Results
This section shows _exemplary_ FPGA implementation results for the NEORV32 CPU and NEORV32 Processor modules.
Note that certain configuration options might also have an impact on other configuration options. Furthermore,
this report cannot cover all possible option combinations. Hence, the presented implementation results are
just _exemplary_. If not otherwise mentioned all implementations use the default generic configurations.
[NOTE]
This section shows **exemplary** FPGA implementation results for the NEORV32 CPU and NEORV32 Processor modules.
:sectnums:
[discrete]
==== CPU
[cols="<2,<8"]
@ -273,19 +268,14 @@ just _exemplary_. If not otherwise mentioned all implementations use the default
| `rv32imcbu_Zicsr_Zicntr_Zifencei_Zfinx_DebugMode` | 4825 | 2018 | 1024 | 7 | 123 MHz
|=======================
[NOTE]
The table above does not show _all_ CPU ISA extensions. More sophisticated and application-specific
options like PMP and HMP are not included in this overview.
.Goal-Driven Optimization
[TIP]
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).
The CPU provides further options to reduce the area footprint or to increase performance.
See section <<_processor_top_entity_generics>> for more information. Also, take a look at the User Guide section
https://stnolting.github.io/neorv32/ug/#_application_specific_processor_configuration[Application-Specific Processor Configuration].
:sectnums:
[discrete]
==== Processor - Modules
[cols="<2,<8"]
@ -330,13 +320,9 @@ https://stnolting.github.io/neorv32/ug/#_application_specific_processor_configur
| XIRQ | External interrupt controller (32 channels) | 245 | 200 | 0 | 0
|=======================
[NOTE]
Note that not all IOs were actually connected to FPGA pins (for example some GPIO inputs and outputs)
when generating these reports.
:sectnums:
==== Processor - Exemplary Setups
[discrete]
==== Processor - Exemplary Setup
[cols="<2,<8"]
[grid="topbot"]
@ -356,7 +342,7 @@ when generating these reports.
| 2488 | 1807 | 7 | 4 | 150 MHz
|=======================
.Exemplary Setups
.Exemplary Processor Setups
[TIP]
Check out the `neorv32-setups` repository (on GitHub: https://github.com/stnolting/neorv32-setups),
which provides several demo setups and community projects for various FPGA boards and toolchains.
@ -368,17 +354,13 @@ which provides several demo setups and community projects for various FPGA board
=== CPU Performance
The performance of the NEORV32 was tested and evaluated using the https://www.eembc.org/coremark/[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.
The according sources can be found in the `sw/example/coremark` folder.
The resulting CoreMark score is defined as CoreMark iterations per second per MHz.
.Dhrystone
.Dhrystone Benchmark
[TIP]
A very 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.
.Configuration
[cols="<2,<8"]
[grid="topbot"]
@ -400,13 +382,7 @@ defined as CoreMark score divided by the CPU's clock frequency in MHz.
| _performance_ (`rv32imc_Zicsr` + perf. options) | 95.23 | **0.9523** | **3.54**
|=======================
[NOTE]
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>>.
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. More information regarding the execution
time of each implemented instruction can be found in section <<_instruction_sets_and_extensions>>.

View file

@ -1,7 +1,7 @@
:sectnums:
=== Rationale
:sectnums!:
[discrete]
==== Why did you make this?
Processor and CPU architecture designs are fascinating things: they are the magic frontier where software meets hardware.
@ -17,13 +17,13 @@ This project aims to provide a _simple to understand_ and _easy to use_ yet _pow
that targets FPGA and RISC-V beginners as well as advanced users.
:sectnums!:
[discrete]
==== Why a _soft-core_ processor?
As a matter of fact soft-core processors _cannot_ compete with discrete (like FPGA hard-macro) processors in terms
of performance, energy efficiency 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 (e.g. communication protocol handling) using
software like plain C. This provides high flexibility as software can be easily changed, re-compiled and
of performance, energy efficiency 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 (e.g. 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
@ -31,7 +31,7 @@ _exactly_ the features that are required by the application: additional memories
co-processors and even user-defined instructions.
:sectnums!:
[discrete]
==== Why RISC-V?
image::riscv_logo.png[width=250,align=left]
@ -56,7 +56,7 @@ Finally, I really like the RISC-V ISA itself. It aims to be a clean, orthogonal
resembles with the basic concepts of _RISC_: simple yet effective.
:sectnums!:
[discrete]
==== 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
@ -74,7 +74,7 @@ and even memory accesses that are checked for address space holes and determinis
devices. Precise exceptions allow a defined and fully-synchronized state of the CPU at every time an in every situation.
:sectnums!:
[discrete]
==== A multi-cycle architecture?!?!
Most mainstream CPUs out there are pipelined architectures to increase throughput. In contrast, most CPUs used for
@ -83,29 +83,26 @@ multi-cycle architectures?
In terms of energy, throughput, area and maximal clock frequency multi-cycle architectures are somewhere in between
single-cycle and fully-pipelined designs: they provide higher throughput and clock speed when compared to their
single-cycle counterparts while having less hardware complexity (= area) then a fully-pipelined designs. I decided to use the
multi-cycle approach because of the following reasons:
single-cycle counterparts while having less hardware complexity (= area) then a fully-pipelined designs. I decided to
use the multi-cycle approach because of the following reasons:
* Multi-cycle architecture are quite small! There is no need for pipeline hazard detection and resolution logic
(e.g. forwarding). Furthermore, you can "re-use" parts of the core to do several tasks (e.g. the ALU is used for the actual data
processing, but also for address generation, branch condition check and branch target computation).
(e.g. forwarding). Furthermore, you can "re-use" parts of the core to do several tasks (e.g. the ALU is used for the
actual data processing, but also for address generation, branch condition check and branch target computation).
* Single-cycle architectures require memories that can be read asynchronously - a thing that is not feasible to implement
in real world applications (i.e. FPGA block RAM is entirely synchronous). Furthermore, such design usually have a very (very!!!)
in real world applications (i.e. FPGA block RAM is entirely synchronous). Furthermore, such design usually have a very
long critical path tremendously reducing maximal operating frequency.
* Pipelined designs increase performance by having several instruction "in fly" at the same time. But this also means
there is some kind of "out-of-order" behavior: if an instruction at the end of the pipeline causes an exception
all the instructions in earlier stages have to be invalidated. Potential architecture state changes have to be made _undone_
requiring additional (exception-handling) logic. In a multi-cycle architecture this situation cannot occur because only a
single instruction is "in fly" at a time.
* Having only a single instruction in fly does not only reduce hardware costs, it also simplifies simulation/verification/debugging,
state preservation/restoring during exceptions and extensibility (no need to care about pipeline hazards) - but of course at the
cost of reduced throughput.
* Having only a single instruction in fly does not only reduce hardware costs, it also simplifies
simulation/verification/debugging, state preservation/restoring during exceptions and extensibility (no need to care
about pipeline hazards) - but of course at the cost of reduced throughput.
To counteract the loss of performance implied by a _pure_ multi-cycle architecture, the NEORV32 CPU uses a _mixed_ approach: instruction fetch
(front-end) and instruction execution (back-end) are de-coupled to operate independently of each other. Data is interchanged via a queue
building a simple 2-stage pipeline. Each "pipeline" stage in terms is implemented as multi-cycle architecture to simplify
the hardware and to provide _precise_ state control (e.g. during exceptions).
.CPU Architecture Details
[TIP]
Want to know more? Check out the description in the CPU's <<_architecture>> section.
To counteract the loss of performance implied by a _pure_ multi-cycle architecture, the NEORV32 CPU uses a _mixed_
approach: instruction fetch (front-end) and instruction execution (back-end) are de-coupled to operate independently
of each other. Data is interchanged via a queue building a simple 2-stage pipeline. Each "pipeline" stage in terms is
implemented as multi-cycle architecture to simplify the hardware and to provide _precise_ state control (e.g. during
exceptions).

File diff suppressed because it is too large Load diff

View file

@ -6,44 +6,23 @@
[frame="topbot",grid="none"]
|=======================
| 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 |
| Software driver file(s): | none |
| Top entity port: | none |
| Configuration generics: | `INT_BOOTLOADER_EN` | implement processor-internal bootloader when `true`
| CPU interrupts: | none |
|=======================
This boot ROM module provides a read-only memory that contain the executable image of the default NEORV32
<<_bootloader>>. If the internal bootloader is enabled via the `INT_BOOTLOADER_EN` generic the CPU's boot address
is automatically set to the beginning of the bootloader ROM. See section <<_boot_configuration>> for more
information regarding the processor's different boot scenarios.
.Address Configuration
[NOTE]
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.
.Access Latency
[NOTE]
By default, the bootloader ROM has a fixed access latency of one clock cycle (like all other processor-internal
modules). However, custom versions of this module may also have higher access latency. See section <<_bus_interface>>
for more information.
The bootloader ROM is located at address `0xFFFF0000` and can occupy an address space of up to 32kB. The base
address as well as the maximum address space size are fixed and cannot be modified as this might cause address
collision with other processor modules.
.Read-Only Access
[NOTE]
Any write access to the BOOTROM will raise a _store access fault_ exception.
.Bootloader - Software
[TIP]
See section <<_bootloader>> for more information regarding the actual bootloader software/executable itself.
.Boot Configuration
[TIP]
See section <<_boot_configuration>> for more information regarding the processor's different boot scenarios.

View file

@ -9,7 +9,7 @@
| Software driver file(s): | none |
| Top entity port: | none |
| Configuration generics: | none |
| Package constants: | `max_proc_int_response_time_c` | Access time window (#cycles)
| Package constants: | `max_proc_int_response_time_c` | Access time window (maximum number of cycles)
| CPU interrupts: | none |
|=======================
@ -17,10 +17,10 @@
**Theory of Operation**
The Bus Keeper is a fundamental component of the processor's internal bus system that ensures correct bus operations
to maintain execution safety. The Bus Keeper monitors every single bus transactions that is intimated by the CPU.
If an accessed device responds with an error condition or do not respond within a specific _access time window_,
the according bus access fault exception is raised. The following exceptions can be raised by the Bus Keeper
(see section <<_traps_exceptions_and_interrupts>> for all available CPU traps):
while maintaining execution safety. It monitors every single bus transactions that is initiated by the CPU.
If an accessed device responds with an error condition or do not respond at all within a specific _access time window_,
an according bus access fault exception is raised. The following exceptions can be raised by the Bus Keeper
(see section <<_traps_exceptions_and_interrupts>> for a list of all available bus access-related exceptions):
* `TRAP_CODE_I_ACCESS`: error during instruction fetch bus access
* `TRAP_CODE_S_ACCESS`: error during data store bus access
@ -30,20 +30,15 @@ The **access time window**, in which an accessed device has to respond, is defin
constant from the processor's VHDL package file (`rtl/neorv32_package.vhd`). The default value is **15 clock cycles**.
In case of a bus access fault exception application software can evaluate the Bus Keeper's control register
`NEORV32_BUSKEEPER.CTRL` to retrieve further details of the bus exception. The _BUSKEEPER_ERR_FLAG_ bit indicates
`CTRL` to retrieve further details regarding the bus exception. The `BUSKEEPER_ERR_FLAG` bit indicates
that an actual bus access fault has occurred. The bit is sticky once set and is automatically cleared when reading or
writing the `NEORV32_BUSKEEPER.CTRL` register. The _BUSKEEPER_ERR_TYPE_ bit defines the type of the bus fault:
writing the `NEORV32_BUSKEEPER.CTRL` register. The `BUSKEEPER_ERR_TYPE` bit defines the type of the bus fault:
* `0` - "Device Error": The bus access exception was cause by the memory-mapped device that
has been accessed (the device asserted it's `err_o`).
* `1` - "Timeout Error": The bus access exception was caused by the Bus Keeper because the
accessed memory-mapped device did not respond within the access time window. Note that this error type can also be raised
by the optional timeout feature of the <<_processor_external_memory_interface_wishbone_axi4_lite>>).
[NOTE]
Bus access fault exceptions are also raised if a physical memory protection (PMP) rule is violated. In this case
the _BUSKEEPER_ERR_FLAG_ bit remains zero (since the error signal is not triggered by the BUSKEEPER but by
the CPU's PMP logic).
by the optional timeout feature of the external bus interface.
**Register Map**
@ -53,7 +48,7 @@ the CPU's PMP logic).
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.2+<| `0xffffff78` .2+<| `CTRL` <|`0` _BUSKEEPER_ERR_TYPE_ ^| r/- <| Bus error type, valid if _BUSKEEPER_ERR_FLAG_
<|`31` _BUSKEEPER_ERR_FLAG_ ^| r/c <| Sticky error flag, clears after read or write access
.2+<| `0xffffff78` .2+<| `CTRL` <|`0` `BUSKEEPER_ERR_TYPE` ^| r/- <| Bus error type, valid if _BUSKEEPER_ERR_FLAG_
<|`31` `BUSKEEPER_ERR_FLAG` ^| r/c <| Sticky error flag, clears after read or write access
| `0xffffff7c` | - | _reserved_ | r/c | _reserved_ (mirrored access to `CTRL`)
|=======================

View file

@ -10,25 +10,21 @@
| | neorv32_cfs.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`
| 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 is meant for implementing custom and application-specific logic.
The CFS provides up to 64 32-bit memory-mapped read/write
registers (`REG`, see register map below) that can be accessed by the CPU via normal load/store operations.
The actual functionality of these register has to be defined by the hardware designer. Furthermore, the CFS
provides two IO conduits to implement custom on-chip or off-chip interfaces.
In contrast to connecting custom hardware accelerators via external memory interfaces (like SPI or the processor's
external bus interface), the CFS provide a convenient, low-latency and tightly-coupled extension and
customization option.
The custom functions subsystem is meant for implementing custom tightly-coupled co-processors or interfaces.
IT provides up to 64 32-bit memory-mapped read/write registers (`REG`, see register map below) that can be
accessed by the CPU via normal load/store operations. The actual functionality of these register has to be
defined by the hardware designer. Furthermore, the CFS provides two IO conduits to implement custom on-chip
or off-chip interfaces.
Just like any other externally-connected IP, logic implemented within the custom functions subsystem can operate
_independently_ of the CPU providing true parallel processing capabilities. Potential use cases might include
@ -38,7 +34,7 @@ or real-time data transport (I2S).
[TIP]
If you like to implement _custom instructions_ that are executed right within the CPU's ALU
see the <<_zxcfu_custom_instructions_extension_cfu>> and the according <<_custom_functions_unit_cfu>>.
see the <<_zxcfu_isa_extension>> and the according <<_custom_functions_unit_cfu>>.
[TIP]
Take a look at the template CFS VHDL source file (`rtl/core/neorv32_cfs.vhd`). The file is highly
@ -51,7 +47,7 @@ The CFS can also be used to _replicate_ existing NEORV32 modules - for example t
**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 are declared as 32-bit words of type `uint32_t`.
register map table below). Note that all interface registers are defined as 32-bit words of type `uint32_t`.
.CFS Software Access Example
[source,c]
@ -61,9 +57,6 @@ NEORV32_CFS->REG[0] = (uint32_t)some_data_array(i); // write to CFS register 0
int temp = (int)NEORV32_CFS->REG[20]; // read from CFS register 20
----
[TIP]
A very simple example program that uses the _default_ CFS hardware module can be found in `sw/example/cfs_demo`.
**CFS Interrupt**
@ -74,7 +67,7 @@ writing zero to the according <<_mip>> CSR bit. See section <<_processor_interru
**CFS Configuration Generic**
By default, the CFS provides a single 32-bit `std_(u)logic_vector` configuration generic _IO_CFS_CONFIG_
By default, the CFS provides a single 32-bit `std_ulogic_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 directly down to the CFS. The actual definition of the generic and it's usage inside the
CFS is left to the hardware designer.
@ -87,10 +80,10 @@ These signals are directly propagated to the processor's top entity. These condu
application-specific interfaces like memory or peripheral connections. The actual use case 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
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.
`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.
**Register Map**

View file

@ -9,21 +9,21 @@
| | 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
| Configuration generics: | `MEM_INT_DMEM_EN` | implement processor-internal DMEM when `true`
| | `MEM_INT_DMEM_SIZE` | DMEM size in bytes
| CPU interrupts: | none |
|=======================
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,
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 true RAM.
.Access Latency
[NOTE]
By default, the DMEM has a fixed access latency of one clock cycle (like all other processor-internal
modules). However, custom versions of this module may also have higher access latency. See section <<_bus_interface>>
for more information.
modules). However, custom versions of this module may also have higher access latency. See section
<<_bus_interface>> for more information.
.VHDL Source File
[NOTE]

View file

@ -10,16 +10,18 @@
| | neorv32_gpio.h |
| Top entity port: | `gpio_o` | 64-bit parallel output port
| | `gpio_i` | 64-bit parallel input port
| Configuration generics: | _IO_GPIO_NUM_ | number of input/output pairs (0..64)
| Configuration generics: | `IO_GPIO_NUM` | number of input/output pairs to implement (0..64)
| CPU interrupts: | none |
|=======================
The general purpose parallel IO unit provides a simple parallel input and output port. These ports can be used chip-externally
(for example to drive status LEDs, connect buttons, etc.) or chip-internally to provide control signals for other IP modules.
The actual number of input/output pairs is defined by the _IO_GPIO_NUM_ generic. When set to zero, the GPIO module is excluded
from synthesis and the output port `gpio_o` is tied to all-zero. If _IO_GPIO_NUM_ is less than the maximum value of 64
only the LSB-aligned bits in `gpio_o` and `gpio_i` are actually connected while the remaining bits are unconnected or tied
to zero, respectively.
The general purpose parallel IO unit provides a simple parallel input and output port. These ports can be used
chip-externally (for example to drive status LEDs, connect buttons, etc.) or chip-internally to provide control
signals for other IP modules.
The actual number of input/output pairs is defined by the `IO_GPIO_NUM` generic. When set to zero, the GPIO module
is excluded from synthesis and the output port `gpio_o` is tied to all-zero. If `IO_GPIO_NUM` is less than the
maximum value of 64, only the LSB-aligned bits in `gpio_o` and `gpio_i` are actually connected while the remaining
bits are tied to zero or are left unconnected, respectively.
.Access Atomicity
[NOTE]

View file

@ -9,7 +9,7 @@
| Software driver file(s): | neorv32_gptmr.c |
| | neorv32_gptmr.h |
| Top entity port: | none |
| Configuration generics: | _IO_GPTMR_EN_ | implement general purpose timer when _true_
| Configuration generics: | `IO_GPTMR_EN` | implement general purpose timer when `true`
| CPU interrupts: | fast IRQ channel 12 | timer interrupt (see <<_processor_interrupts>>)
|=======================
@ -17,13 +17,13 @@
**Theory of Operation**
The general purpose timer module provides a simple yet universal 32-bit timer. The timer is implemented if
_IO_GPTMR_EN_ top generic is set _true_. It provides a 32-bit counter register (`COUNT`) and a 32-bit threshold
`IO_GPTMR_EN` top generic is set `true`. It provides a 32-bit counter register (`COUNT`) and a 32-bit threshold
register (`THRES`). An interrupt is generated whenever the value of the counter registers matches the one from
threshold register.
The timer is enabled by setting the _GPTMR_CTRL_EN_ bit in the device's control register `CTRL`. The `COUNT`
The timer is enabled by setting the `GPTMR_CTRL_EN` bit in the device's control register `CTRL`. The `COUNT`
register will start incrementing at a programmable rate, which scales the main processor clock. The
pre-scaler value is configured via the three _GPTMR_CTRL_PRSCx_ control register bits:
pre-scaler value is configured via the three `GPTMR_CTRL_PRSCx` control register bits:
.GPTMR prescaler configuration
[cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"]
@ -33,10 +33,10 @@ pre-scaler value is configured via the three _GPTMR_CTRL_PRSCx_ control register
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096
|=======================
The timer provides two operation modes that are configured by the _GPTMR_CTRL_MODE_ control register bit:
if _GPTMR_CTRL_MODE_ is cleared (`0`) the timer operates in _single-shot mode_. As soon as `COUNT` matches
`THRES` an interrupt request is generated and the timer stops operation (i.e. it stops incrementing). If
_GPTMR_CTRL_MODE_ is set (`1`) the timer operates in _continuous mode_. When `COUNT` matches `THRES` an interrupt
The timer provides two operation modes that are configured via the `GPTMR_CTRL_MODE` control register bit:
.If `GPTMR_CTRL_MODE` is cleared (`0`) the timer operates in _single-shot mode_. As soon as `COUNT` matches
`THRES` an interrupt request is generated and the timer stops operation (i.e. it stops incrementing)
.If `GPTMR_CTRL_MODE` is set (`1`) the timer operates in _continuous mode_. When `COUNT` matches `THRES` an interrupt
request is generated and `COUNT` is automatically reset to all-zero before continuing to increment.
[NOTE]
@ -57,11 +57,10 @@ remains pending inside the CPU until it explicitly cleared by writing zero to th
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.5+<| `0xffffff60` .5+<| `CTRL` <|`0` _GPTMR_CTRL_EN_ ^| r/w <| Timer enable flag
<|`1` _GPTMR_CTRL_PRSC0_ ^| r/w .3+| 3-bit clock prescaler select
<|`2` _GPTMR_CTRL_PRSC1_ ^| r/w
<|`3` _GPTMR_CTRL_PRSC2_ ^| r/w
<|`4` _GPTMR_CTRL_MODE_ ^| r/w <| Counter mode: `0`=single-shot, `1`=continuous
.4+<| `0xffffff60` .4+<| `CTRL` <|`0` `GPTMR_CTRL_EN` ^| r/w <| Timer enable flag
<|`3:1` `GPTMR_CTRL_PRSC2 : GPTMR_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select
<|`4` `GPTMR_CTRL_MODE` ^| r/w <| Counter mode: `0`=single-shot, `1`=continuous
<|`31:5` - ^| r/- <| _reserved_, read as zero
| `0xffffff64` | `THRES` |`31:0` | r/w | Threshold value register
| `0xffffff68` | `COUNT` |`31:0` | r/w | Counter register
|=======================

View file

@ -6,45 +6,34 @@
[frame="topbot",grid="none"]
|=======================
| 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 |
| 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 processor features an optional cache for instructions to improve performance when using memories with high
The processor features an optional instruction cache to improve performance when using memories with high
access latencies. The cache is directly connected to the CPU's instruction fetch interface and provides
full-transparent buffering of instruction fetch accesses to the entire address space.
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 greater than one
the LRU replacement policy (least recently used) is used.
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 greater than or
equal to 4 bytes), `ICACHE_NUM_BLOCKS` (the total amount of cache blocks; has to be a power of two and greater than or
equal to 1) and the actual cache associativity `ICACHE_ASSOCIATIVITY` (number of sets; 1 = direct-mapped, 2 = 2-way set-associative).
If the cache associativity is greater than one the LRU replacement policy (least recently used) is used.
.Cache Memory HDL
[NOTE]
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. Also, 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.
.Caching Internal Memories
[NOTE]
The instruction cache is intended to accelerate instruction fetches from _processor-external_ memories
(via the external bus interface or via the XIP module).
Since all processor-internal memories provide an access latency of one cycle (by default), caching
internal memories does not bring a relevant performance gain. However, it will slightly reduce traffic on the
processor-internal bus.
.Manual Cache Clear/Reload
[NOTE]
By executing the `ifence.i` instruction (`Zifencei` CPU extension) the cache is cleared and a reload from
By executing the `fence.i` instruction (<<_zifencei_isa_extension>>) the cache is cleared and a reload from
main memory is triggered. This also allows to implement self-modifying code.
.Retrieve Cache Configuration from Software
@ -54,7 +43,7 @@ Software can retrieve the cache configuration/layout from the <<_sysinfo_cache_c
**Bus Access Fault Handling**
The cache always loads a complete cache block (_ICACHE_BLOCK_SIZE_ bytes; aligned to the block size) every time a
The cache always loads a complete cache block (aligned to the block size) every time a
cache miss is detected. Each cached word from this block provides a single status bit that indicates if the
according bus access was successful or caused a bus error. Hence, the whole cache block remains valid even
if certain addresses inside caused a bus error. If the CPU accesses any of the faulty cache words, an

View file

@ -8,15 +8,15 @@
| Hardware source file(s): | neorv32_mtime.vhd |
| Software driver file(s): | neorv32_mtime.c |
| | neorv32_mtime.h |
| Top entity port: | `mtime_irq_i` | RISC-V machine timer IRQ if internal MTIME is **not** implemented
| Configuration generics: | _IO_MTIME_EN_ | implement MTIME when _true_
| Top entity port: | `mtime_irq_i` | RISC-V machine timer IRQ if internal one is **not** implemented
| Configuration generics: | `IO_MTIME_EN` | implement machine timer when `true`
| CPU interrupts: | `MTI` | machine timer interrupt (see <<_processor_interrupts>>)
|=======================
The MTIME module implements a memory-mapped MTIME machine system timer that is compatible to the RISC-V
privileged specifications. The 64-bit system time is accessed via the memory-mapped `TIME_LO` and
`TIME_HI`registers. A 64-bit time compare register, which is accessible via the memory-mapped `TIMECMP_LO`
and `TIMECMP_HI` registers, can be used to configure the CPU's MTI (machine timer interrupt). The interrupt
and `TIMECMP_HI` registers, can be used to configure the CPU's `MTI` (machine timer interrupt). The interrupt
is triggered whenever `TIME` (high & low part) is greater than or equal to `TIMECMP` (high & low part).
The interrupt remains active (=pending) until `TIME` becomes less `TIMECMP` again (either by modifying
`TIME` or `TIMECMP`).
@ -27,9 +27,9 @@ After a hardware reset the `TIME` and `TIMECMP` register are reset to all-zero.
.External MTIME Interrupt
[IMPORTANT]
If the internal MTIME module is disabled (`IO_MTIME_EN` = `false`) the machine timer interrupt becomes available as external signal.
The `mtime_irq_i` signal is level-triggered and high-active. Once set the signal has to stay high until
the interrupt request is explicitly acknowledged (e.g. writing to a memory-mapped register). All RISC-V standard interrupts
can **NOT** be acknowledged by writing zero to the according <<_mip>> CSR bit. +
the interrupt request is explicitly acknowledged (e.g. writing to a memory-mapped register).
**Register Map**

View file

@ -9,8 +9,8 @@
| 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, has to be a power of 2, min 1
| Configuration generics: | `IO_NEOLED_EN` | implement NEOLED controller when `true`
| | `IO_NEOLED_TX_FIFO` | TX FIFO depth, has to be a power of 2, min 1
| CPU interrupts: | fast IRQ channel 9 | configurable NEOLED data FIFO interrupt (see <<_processor_interrupts>>)
|=======================
@ -19,17 +19,13 @@
The NEOLED module provides a dedicated interface for "smart RGB LEDs" like WS2812, WS2811 or any other compatible
LEDs. These LEDs provide a single-wire interface 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.
Using the NEOLED module allows CPU-independent operation of an arbitrary number of smart LEDs. A configurable data
data. Using the NEOLED module allows CPU-independent operation of an arbitrary number of smart LEDs. A configurable data
buffer (FIFO) allows to utilize block transfer operation without requiring the CPU.
[NOTE]
The NEOLED interface is compatible to the "Adafruit Industries NeoPixel(TM)" products, which feature
WS2812 (or older WS2811) smart LEDs (see link:https://learn.adafruit.com/adafruit-neopixel-uberguide).
Other LEDs might be compatible as well when adjusting the controller's programmable timing configuration.
WS2812 (or older WS2811) smart LEDs. Other LEDs might be compatible as well when adjusting the controller's programmable
timing configuration.
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)
@ -40,8 +36,8 @@ and RGBW LEDs (32-bit color including a dedicated white LED chip) is possible.
The NEOLED modules provides two accessible interface registers: the control register `CTRL` and the write-only
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
`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 (e.g. implementing the **WS2812** protocol) and transmission
mode are fully programmable via the `CTRL` register to provide maximum flexibility.
@ -52,9 +48,9 @@ four chips providing RGB color plus a dedicated white LED chip (= RGBW). Since t
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 programmed via the _NEOLED_MODE_EN_ control
The data transfer quantity of the NEOLED module can be programmed 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
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 reconfigured before writing a new data word to `DATA` in order to support an arbitrary setup/mixture
@ -86,10 +82,11 @@ image::neopixel.png[align=center]
**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 f~main~ and a 5-bit cycle multiplier _NEOLED_CTRL_T_TOT_x_.
The basic carrier frequency (800kHz for the WS2812 LEDs) is configured via a 3-bit main clock prescaler
(`NEOLED_CTRL_PRSC*`, see table below) that scales the main processor clock f~main~ and a 5-bit cycle
multiplier `NEOLED_CTRL_T_TOT_*`.
.NEOLED prescaler configuration
.NEOLED Prescaler Configuration
[cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"]
[options="header",grid="rows"]
|=======================
@ -98,7 +95,7 @@ that scales the main processor clock f~main~ and a 5-bit cycle multiplier _NEOLE
|=======================
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
defined via the 5-bit `NEOLED_CTRL_T_ONE_H_*` and `NEOLED_CTRL_T_ZERO_H_*` values, respectively. These programmable
timing constants allow to adapt the interface for a wide variety of smart LED protocol (for example WS2812 vs.
WS2811).
@ -108,7 +105,7 @@ WS2811).
Generate the base clock f~TX~ for the NEOLED TX engine:
* processor clock f~main~ = 100 MHz
* _NEOLED_CTRL_PRSCx_ = `0b001` = f~main~ / 4
* `NEOLED_CTRL_PRSCx` = `0b001` = f~main~ / 4
_**f~TX~**_ = _f~main~[Hz]_ / `clock_prescaler` = 100MHz / 4 = 25MHz
@ -116,15 +113,15 @@ _**T~TX~**_ = 1 / _**f~TX~**_ = 40ns
Generate carrier period (T~carrier~) and *high-times* (duty cycle) for sending `0` (T~0H~) and `1` (T~1H~) bits:
* _NEOLED_CTRL_T_TOT_ = `0b11110` (= decimal 30)
* _NEOLED_CTRL_T_ZERO_H_ = `0b01010` (= decimal 10)
* _NEOLED_CTRL_T_ONE_H_ = `0b10100` (= decimal 20)
* `NEOLED_CTRL_T_TOT` = `0b11110` (= decimal 30)
* `NEOLED_CTRL_T_ZERO_H` = `0b01010` (= decimal 10)
* `NEOLED_CTRL_T_ONE_H` = `0b10100` (= decimal 20)
_**T~carrier~**_ = _**T~TX~**_ * _NEOLED_CTRL_T_TOT_ = 40ns * 30 = 1.4µs
_**T~carrier~**_ = _**T~TX~**_ * `NEOLED_CTRL_T_TOT` = 40ns * 30 = 1.4µs
_**T~0H~**_ = _**T~TX~**_ * _NEOLED_CTRL_T_ZERO_H_ = 40ns * 10 = 0.4µs
_**T~0H~**_ = _**T~TX~**_ * `NEOLED_CTRL_T_ZERO_H` = 40ns * 10 = 0.4µs
_**T~1H~**_ = _**T~TX~**_ * _NEOLED_CTRL_T_ONE_H_ = 40ns * 20 = 0.8µs
_**T~1H~**_ = _**T~TX~**_ * `NEOLED_CTRL_T_ONE_H` = 40ns * 20 = 0.8µs
[TIP]
The NEOLED SW driver library (`neorv32_neoled.h`) provides a simplified configuration
@ -135,21 +132,21 @@ clock frequency.
**TX Data FIFO**
The interface features a configurable 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_).
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 and transmit it to the LEDs.
The data transfer size (_NEOLED_MODE_EN_) can be modified at any time since this control register bit is also buffered
The data transfer size (`NEOLED_MODE_EN`) can be modified at any time since this control register bit is also buffered
in the FIFO. This allows an arbitrary mix of 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 serial
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 serial
transmit engine is still busy sending data.
[WARNING]
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
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.
@ -160,11 +157,11 @@ registers when the data line is low for 50μs ("RESET" command, see table above)
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`,
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 (= _**T~carrier~**_).
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 just as another
Since the `NEOLED_CTRL_STROBE` flag is also buffered in the TX buffer, the RESET command is treated just as another
data word being written to the TX buffer making busy wait concepts obsolete and allowing maximum refresh rates.
@ -172,11 +169,11 @@ data word being written to the TX buffer making busy wait concepts obsolete and
The NEOLED modules features a single interrupt that triggers based on the current TX buffer fill level.
The interrupt can only become pending if the NEOLED module is enabled. The specific interrupt condition
is configured via the _NEOLED_CTRL_IRQ_CONF_ bit in the unit's control register.
is configured via the `NEOLED_CTRL_IRQ_CONF` bit in the unit's control register.
If _NEOLED_CTRL_IRQ_CONF_ is set, the module's interrupt is generated whenever the TX FIFO is less than half-full.
In this case software can write up to _IO_NEOLED_TX_FIFO_/2 new data words to `DATA` without checking the FIFO
status flags. If _NEOLED_CTRL_IRQ_CONF_ is cleared, an interrupt is generated when the TX FIFO is empty.
If `NEOLED_CTRL_IRQ_CONF` is set, the module's interrupt is generated whenever the TX FIFO is less than half-full.
In this case software can write up to `IO_NEOLED_TX_FIFO`/2 new data words to `DATA` without checking the FIFO
status flags. If `NEOLED_CTRL_IRQ_CONF` is cleared, an interrupt is generated when the TX FIFO is empty.
One the NEOLED interrupt has been triggered and became pending, it has to explicitly cleared again by
writing zero to according <<_mip>> CSR bit.
@ -189,18 +186,18 @@ writing zero to according <<_mip>> CSR bit.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.13+<| `0xffffffd8` .13+<| `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
<|`5:3` _NEOLED_CTRL_PRSC2_ : _NEOLED_CTRL_PRSC0_ ^| r/w <| 3-bit clock prescaler, bit 0
<|`9:6` _NEOLED_CTRL_BUFS3_ : _NEOLED_CTRL_BUFS0_ ^| r/- <| 4-bit log2(_IO_NEOLED_TX_FIFO_)
<|`14:10` _NEOLED_CTRL_T_TOT_4_ : _NEOLED_CTRL_T_TOT_0_ ^| r/w <| 5-bit pulse clock ticks per total single-bit period (T~total~)
<|`19:15` _NEOLED_CTRL_T_ZERO_H_4_ : _NEOLED_CTRL_T_ZERO_H_0_ ^| r/w <| 5-bit pulse clock ticks per high-time for sending a zero-bit (T~0H~)
<|`24:20` _NEOLED_CTRL_T_ONE_H_4_ : _NEOLED_CTRL_T_ONE_H_0_ ^| r/w <| 5-bit pulse clock ticks per high-time for sending a one-bit (T~1H~)
<|`27` _NEOLED_CTRL_IRQ_CONF_ ^| r/w <| TX FIFO interrupt configuration: `0`=IRQ if FIFO is empty, `1`=IRQ if FIFO is less than half-full
<|`28` _NEOLED_CTRL_TX_EMPTY_ ^| r/- <| TX FIFO is empty
<|`29` _NEOLED_CTRL_TX_HALF_ ^| r/- <| TX FIFO is _at least_ half full
<|`30` _NEOLED_CTRL_TX_FULL_ ^| r/- <| TX FIFO is full
<|`31` _NEOLED_CTRL_TX_BUSY_ ^| r/- <| TX serial engine is busy when set
.13+<| `0xffffffd8` .13+<| `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
<|`5:3` `NEOLED_CTRL_PRSC2 : NEOLED_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler, bit 0
<|`9:6` `NEOLED_CTRL_BUFS3 : NEOLED_CTRL_BUFS0` ^| r/- <| 4-bit log2(_IO_NEOLED_TX_FIFO_)
<|`14:10` `NEOLED_CTRL_T_TOT_4 : NEOLED_CTRL_T_TOT_0` ^| r/w <| 5-bit pulse clock ticks per total single-bit period (T~total~)
<|`19:15` `NEOLED_CTRL_T_ZERO_H_4 : NEOLED_CTRL_T_ZERO_H_0` ^| r/w <| 5-bit pulse clock ticks per high-time for sending a zero-bit (T~0H~)
<|`24:20` `NEOLED_CTRL_T_ONE_H_4 : NEOLED_CTRL_T_ONE_H_0` ^| r/w <| 5-bit pulse clock ticks per high-time for sending a one-bit (T~1H~)
<|`27` `NEOLED_CTRL_IRQ_CONF` ^| r/w <| TX FIFO interrupt configuration: `0`=IRQ if FIFO is empty, `1`=IRQ if FIFO is less than half-full
<|`28` `NEOLED_CTRL_TX_EMPTY` ^| r/- <| TX FIFO is empty
<|`29` `NEOLED_CTRL_TX_HALF` ^| r/- <| TX FIFO is _at least_ half full
<|`30` `NEOLED_CTRL_TX_FULL` ^| r/- <| TX FIFO is full
<|`31` `NEOLED_CTRL_TX_BUSY` ^| r/- <| TX serial engine is busy when set
| `0xffffffdc` | `DATA` <|`31:0` / `23:0` ^| -/w <| TX data (32- or 24-bit, depending on _NEOLED_CTRL_MODE_ bit)
|=======================

View file

@ -10,7 +10,7 @@
| | neorv32_onewire.h |
| Top entity port: | `onewire_i` | 1-bit 1-wire bus sense input
| | `onewire_o` | 1-bit 1-wire bus output (pull low only)
| Configuration generics: | _IO_ONEWIRE_EN_ | implement ONEWIRE interface controller when _true_
| Configuration generics: | `IO_ONEWIRE_EN` | implement ONEWIRE interface controller when `true`
| CPU interrupts: | fast IRQ channel 13 | operation done interrupt (see <<_processor_interrupts>>)
|=======================
@ -20,30 +20,25 @@
The NEORV32 ONEWIRE module implements a single-wire interface controller that is compatible to the
_Dallas/Maxim 1-Wire_ protocol, which is an asynchronous half-duplex bus requiring only a single signal wire
connected to `onewire_io` (plus ground).
The 1-Wire protocol allows an (nearly) arbitrary number of devices but only a single controller that initiates all transfers.
The bus is based on a single tristate signal. The controller and all the devices can only pull-down the bus actively.
The bus is based on a single open-drain signal. The controller and all the devices can only pull-down the bus actively.
Hence, an external pull-up resistor is required. Recommended values are between 1kΩ and 4kΩ depending on the bus
characteristics (wire length, number of devices, etc.). Furthermore, a series resistor (~100Ω) at the controller side
is recommended to control the slew rate and to reduce signal reflections. Also, additional external ESD protection clamp diodes
should be added to the `onewire_io` bus line.
[TIP]
For more information regarding the 1-Wire bus and the device access mechanism
see the Application Notes provided by Maxim Integrated.
should be added to the bus line.
**Tri-State Drivers**
The ONEWIRE module requires a tri-state driver for the 1-wire bus line, which has to be implemented
The ONEWIRE module requires a tri-state driver (actually, open-drain) for the 1-wire bus line, which has to be implemented
in the top module of the setup. A generic VHDL example is given below (`onewire` is the actual 1-wire
bus signal, which is of type `std_logic`).
.ONEWIRE VHDL tri-state driver example
[source,VHDL]
----
onewire <= '0' when (onewire_o = '0') else 'Z';
onewire_i <= std_ulogic(onewire);
onewire <= '0' when (onewire_o = '0') else 'Z'; -- drive
onewire_i <= std_ulogic(onewire); -- sense
----
@ -53,18 +48,18 @@ The ONEWIRE controller provides two interface registers: `CTRL` and `DATA.` The
is used to configure the module, to trigger bus transactions and to monitor the current state of the module.
The `DATA` register is used to read/write data from/to the bus.
The module is enabled by setting the _ONEWIRE_CTRL_EN_ bit in the control register. If this bit is cleared, the
module is automatically reset and the bus is brought to high-impedance (tristate) state.
The basic timing configuration is programmed via the clock prescaler bits _ONEWIRE_CTRL_PRSCx_ and the
clock divider bits _ONEWIRE_CTRL_CLKDIVx_ (see next section).
The module is enabled by setting the `ONEWIRE_CTRL_EN` bit in the control register. If this bit is cleared, the
module is automatically reset and the bus is brought to high-level (due to the external pull-up resistor).
The basic timing configuration is programmed via the clock prescaler bits `ONEWIRE_CTRL_PRSCx` and the
clock divider bits `ONEWIRE_CTRL_CLKDIVx` (see next section).
The controller can execute three basic bus operations, which are triggered by setting one out of three specific
control register bits (the bits auto-clear):
[start=1]
. generate reset pulse and check for device presence; triggered when setting _ONEWIRE_CTRL_TRIG_RST_
. transfer a single-bit (read-while-write); triggered when setting _ONEWIRE_CTRL_TRIG_BIT_
. transfer a full-byte (read-while-write); triggered when setting _ONEWIRE_CTRL_TRIG_BYTE_
. generate reset pulse and check for device presence; triggered when setting `ONEWIRE_CTRL_TRIG_RST`
. transfer a single-bit (read-while-write); triggered when setting `ONEWIRE_CTRL_TRIG_BIT`
. transfer a full-byte (read-while-write); triggered when setting `ONEWIRE_CTRL_TRIG_BYTE`
[IMPORTANT]
Only one trigger bit may be set at once, otherwise undefined behavior might occur.
@ -72,7 +67,7 @@ Only one trigger bit may be set at once, otherwise undefined behavior might occu
When a single-bit operation has been triggered, the data previously written to `DATA[0]` will be send to the bus
and `DATA[7]` will be sampled from the bus. Accordingly, a full-byte transmission will send the previously
byte written to `DATA[7:0]` to the bus and will update `DATA[7:0]` with the data read from the bus (LSB-first).
The triggered operation has completed when the module's busy flag _ONEWIRE_CTRL_BUSY_ has cleared again.
The triggered operation has completed when the module's busy flag `ONEWIRE_CTRL_BUSY` has cleared again.
.Read from Bus
[NOTE]
@ -80,19 +75,17 @@ In order to read a single bit from the bus `DATA[0]` has to set to `1` before tr
operation to allow the accessed device to pull-down the bus. Accordingly, `DATA` has to be set to `0xFF` before
triggering the byte transmission operation when the controller shall read a byte from the bus.
The _ONEWIRE_CTRL_PRESENCE_ bit gets set if at least one device has send a "presence" signal right after the
The `ONEWIRE_CTRL_PRESENCE` bit gets set if at least one device has send a "presence" signal right after the
reset pulse.
**Bus Timing**
The control register provides a 2-bit clock prescaler select (_ONEWIRE_CTRL_PRSCx_) and a 8-bit clock divider
(_ONEWIRE_CTRL_CLKDIVx_) for timing configuration. Both are used to define the elementary **base time T~base~**.
The control register provides a 2-bit clock prescaler select (`ONEWIRE_CTRL_PRSCx`) and a 8-bit clock divider
(`ONEWIRE_CTRL_CLKDIVx`) for timing configuration. Both are used to define the elementary **base time T~base~**.
All bus operations are timed using _multiples_ of this elementary base time.
The following clock prescalers are available:
.ONEWIRE clock prescaler configurations
.ONEWIRE Clock Prescaler Configurations
[cols="<4,^1,^1,^1,^1"]
[options="header",grid="rows"]
|=======================
@ -100,7 +93,7 @@ The following clock prescalers are available:
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64
|=======================
Together with the clock divider value (_ONEWIRE_CTRL_PRSCx_ bits = `clock_divider`) the base time is defined by the
Together with the clock divider value (`ONEWIRE_CTRL_PRSCx` bits = `clock_divider`) the base time is defined by the
following formula:
_**T~base~**_ = (1 / _f~main~[Hz]_) * `clock_prescaler` * (`clock_divider` + 1)
@ -183,15 +176,15 @@ according <<_mip>> CSR FIRQ bit.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.10+<| `0xffffff70` .10+<| `CTRL` <|`0` _ONEWIRE_CTRL_EN_ ^| r/w <| ONEWIRE enable, reset if cleared
<|`2:1` _ONEWIRE_CTRL_PRSC1_ : _ONEWIRE_CTRL_PRSC0_ ^| r/w <| 2-bit clock prescaler select
<|`10:3` _ONEWIRE_CTRL_CLKDIV7_ : _ONEWIRE_CTRL_CLKDIV0_ ^| r/w <| 8-bit clock divider value
<|`11` _ONEWIRE_CTRL_TRIG_RST_ ^| -/w <| trigger reset pulse, auto-clears
<|`12` _ONEWIRE_CTRL_TRIG_BIT_ ^| -/w <| trigger single bit transmission, auto-clears
<|`13` _ONEWIRE_CTRL_TRIG_BYTE_ ^| -/w <| trigger full-byte transmission, auto-clears
<|`28:14` - ^| r/- <| _reserved_, read as zero
<|`29` _ONEWIRE_CTRL_SENSE_ ^| r/- <| current state of the bus line
<|`30` _ONEWIRE_CTRL_PRESENCE_ ^| r/- <| device presence detected after reset pulse
<|`31` _ONEWIRE_CTRL_BUSY_ ^| r/- <| operation in progress when set
| `0xffffff74` | `DATA` |`7:0` _ONEWIRE_DATA_MSB_ : _ONEWIRE_DATA_LSB_ | r/w | receive/transmit data (8-bit)
.10+<| `0xffffff70` .10+<| `CTRL` <|`0` `ONEWIRE_CTRL_EN` ^| r/w <| ONEWIRE enable, reset if cleared
<|`2:1` `ONEWIRE_CTRL_PRSC1 : ONEWIRE_CTRL_PRSC0` ^| r/w <| 2-bit clock prescaler select
<|`10:3` `ONEWIRE_CTRL_CLKDIV7 : ONEWIRE_CTRL_CLKDIV0` ^| r/w <| 8-bit clock divider value
<|`11` `ONEWIRE_CTRL_TRIG_RST` ^| -/w <| trigger reset pulse, auto-clears
<|`12` `ONEWIRE_CTRL_TRIG_BIT` ^| -/w <| trigger single bit transmission, auto-clears
<|`13` `ONEWIRE_CTRL_TRIG_BYTE` ^| -/w <| trigger full-byte transmission, auto-clears
<|`28:14` - ^| r/- <| _reserved_, read as zero
<|`29` `ONEWIRE_CTRL_SENSE` ^| r/- <| current state of the bus line
<|`30` `ONEWIRE_CTRL_PRESENCE` ^| r/- <| device presence detected after reset pulse
<|`31` `ONEWIRE_CTRL_BUSY` ^| r/- <| operation in progress when set
| `0xffffff74` | `DATA` |`7:0` `ONEWIRE_DATA_MSB : ONEWIRE_DATA_LSB` | r/w | receive/transmit data (8-bit)
|=======================

View file

@ -9,35 +9,33 @@
| Software driver file(s): | neorv32_pwm.c |
| | neorv32_pwm.h |
| Top entity port: | `pwm_o` | PWM output channels (12-bit)
| Configuration generics: | _IO_PWM_NUM_CH_ | number of PWM channels to implement (0..12)
| Configuration generics: | `IO_PWM_NUM_CH` | number of PWM channels to implement (0..12)
| CPU interrupts: | none |
|=======================
The PWM controller implements a pulse-width modulation controller with up to 12 independent channels and an
8-bit resolution per channel. The actual number of implemented channels is defined by the _IO_PWM_NUM_CH_ generic.
**Overview**
**Overview**
The PWM module implements a pulse-width modulation controller with up to 12 independent channels providing
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.
[NOTE]
The `pwm_o` has a static size of 12-bit. If less than 12 PWM channels are configured, only the LSB-aligned channel
bits are used while the remaining bits are hardwired to zero.
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 3 duty cycle registers `DC[0]` to `DC[2]` (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 `DC[0]`. The duty cycle of channel 2 is defined via bits 15:0 in `DC[0]` and so on.
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 module
provides three duty cycle registers `DC[0]` to `DC[2]`. Each register contains the duty cycle configuration for four
consecutive channels. For example, the duty cycle of channel 0 is defined via bits 7:0 in `DC[0]`. The duty cycle of
channel 2 is defined via bits 15:0 in `DC[0]` and so on.
[NOTE]
Regardless of the configuration of _IO_PWM_NUM_CH_ all module registers can be accessed without raising an exception.
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.
@ -46,8 +44,8 @@ Based on the configured duty cycle the according intensity of the channel can be
_**Intensity~x~**_ = `DC[y](i*8+7 downto i*8)` / (2^8^)
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 pre-scalers are available:
from the main processor clock and divided by a prescaler via the 3-bit `PWM_CTRL_PRSCx` in the unit's control
register.
.PWM prescaler configuration
[cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"]
@ -69,10 +67,9 @@ _**f~PWM~**_ = _f~main~[Hz]_ / (2^8^ * `clock_prescaler`)
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.4+<| `0xffffff50` .4+<| `CTRL` <|`0` _PWM_CTRL_EN_ ^| r/w | PWM enable
<|`1` _PWM_CTRL_PRSC0_ ^| r/w .3+<| 3-bit clock prescaler select
<|`2` _PWM_CTRL_PRSC1_ ^| r/w
<|`3` _PWM_CTRL_PRSC2_ ^| r/w
.3+<| `0xffffff50` .3+<| `CTRL` <|`0` `PWM_CTRL_EN` ^| r/w <| PWM enable
<|`3:1` `PWM_CTRL_PRSC2 : PWM_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select
<|`31:4` - ^| r/- <| _reserved_, read as zero
.4+<| `0xffffff54` .4+<| `DC[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

View file

@ -12,8 +12,8 @@
| | `sdi_dat_o` | 1-bit serial data output
| | `sdi_dat_i` | 1-bit serial data input
| | `sdi_csn_i` | 1-bit chip-select input (low-active)
| Configuration generics: | _IO_SDI_EN_ | implement SDI controller when _true_
| | _IO_SDI_FIFO_ | data FIFO size, has to be at least 1 or a power of two
| Configuration generics: | `IO_SDI_EN` | implement SDI controller when `true`
| | `IO_SDI_FIFO` | data FIFO size, has to a power of two, min 1
| CPU interrupts: | fast IRQ channel 11 | configurable SDI interrupt (see <<_processor_interrupts>>)
|=======================
@ -29,7 +29,7 @@ transmissions without CPU interaction.
[NOTE]
The NEORV32 SDI module only supports _device mode_. Transmission are initiated by an external host and not by the
the processor itself. If you are looking for a _host-mode_ serial peripheral interface (transactions
initiated by the NEORV32) check out the <<_serial_peripheral_interface_spi>> module.
initiated by the NEORV32) check out the <<_serial_peripheral_interface_controller_spi>>.
The SDI module provides a single control register `CTRL` to configure the module and to check it's status
and a single data register `DATA` for receiving/transmitting data.
@ -37,18 +37,23 @@ and a single data register `DATA` for receiving/transmitting data.
**Theory of Operation**
The SDI module is enabled by setting the _SDI_CTRL_EN_ bit in the `CTRL` control register. Clearing this bit
The SDI module is enabled by setting the `SDI_CTRL_EN` bit in the `CTRL` control register. Clearing this bit
resets the entire module including the RX and TX FIFOs.
The SDI operates on byte-level only. Data written to the `DATA` register will be pushed to the TX FIFO. Received
data can be retrieved by reading the RX FIFO via the `DATA` register. The current state of these FIFOs is available
via the control register's _SDI_CTRL_RX_*_ and _SDI_CTRL_TX_*_ flags. The RX FIFO can be manually cleared at any time
by setting the _SDI_CTRL_CLR_RX_ bit.
via the control register's `SDI_CTRL_RX_*` and `SDI_CTRL_TX_*` flags. The RX FIFO can be manually cleared at any time
by setting the `SDI_CTRL_CLR_RX` bit.
.MSB-first Only
[NOTE]
The NEORV32 SDI module only supports MSB-first mode.
.Transmission Abort
[NOTE]
If the external SPI controller aborts an transmission (by setting the chip-select signal high again) _before_
8 data bits have been transferred, no data is written to the RX FIFO.
**SDI Clocking**
@ -61,11 +66,11 @@ clock domain to simplify timing behavior. However, the clock synchronization req
**SDI Interrupt**
The SDI module provides a set of programmable interrupt conditions based on the level of the RX & TX FIFOs. The different
interrupt sources are enabled by setting the according control register's _SDI_CTRL_IRQ_ bits. All enabled interrupt
interrupt sources are enabled by setting the according control register's `SDI_CTRL_IRQ` bits. All enabled interrupt
conditions are logically OR-ed so any enabled interrupt source will trigger the module's interrupt signal.
Once the SDI interrupt has fired it will remain active until the actual cause of the interrupt is resolved; for
example if just the _SDI_CTRL_IRQ_RX_AVAIL_ bit is set, the interrupt will keep firing until the RX FIFO is empty again.
example if just the `SDI_CTRL_IRQ_RX_AVAIL` bit is set, the interrupt will keep firing until the RX FIFO is empty again.
Furthermore, an active SDI interrupt has to be explicitly cleared again by writing zero to the according
<<_mip>> CSR bit.
@ -77,21 +82,21 @@ Furthermore, an active SDI interrupt has to be explicitly cleared again by writi
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.16+<| `0xfffffff0` .16+<| `CTRL` <|`0` _SDI_CTRL_EN_ ^| r/w <| SDI module enable
<|`1` _SDI_CTRL_CLR_RX_ ^| -/w <| clear RX FIFO when set, bit auto-clears
<|`3:2` _reserved_ ^| r/- <| reserved, read as zero
<|`7:4` _SDI_CTRL_FIFO_MSB_ : _SDI_CTRL_FIFO_LSB_ ^| r/- <| FIFO depth; log2(_IO_SDI_FIFO_)
<|`14:8` _reserved_ ^| r/- <| reserved, read as zero
<|`15` _SDI_CTRL_IRQ_RX_AVAIL_ ^| r/w <| fire interrupt if RX FIFO is not empty
<|`16` _SDI_CTRL_IRQ_RX_HALF_ ^| r/w <| fire interrupt if RX FIFO is at least half full
<|`17` _SDI_CTRL_IRQ_RX_FULL_ ^| r/w <| fire interrupt if if RX FIFO is full
<|`18` _SDI_CTRL_IRQ_TX_EMPTY_ ^| r/w <| fire interrupt if TX FIFO is empty
<|`22:19` _reserved_ ^| r/- <| reserved, read as zero
<|`23` _SDI_CTRL_RX_AVAIL_ ^| r/- <| RX FIFO data available (RX FIFO not empty)
<|`24` _SDI_CTRL_RX_HALF_ ^| r/- <| RX FIFO at least half full
<|`25` _SDI_CTRL_RX_FULL_ ^| r/- <| RX FIFO full
<|`26` _SDI_CTRL_TX_EMPTY_ ^| r/- <| TX FIFO empty
<|`27` _SDI_CTRL_TX_FULL_ ^| r/- <| TX FIFO full
<|`31:28` _reserved_ ^| r/- <| reserved, read as zero
.16+<| `0xfffffff0` .16+<| `CTRL` <|`0` `SDI_CTRL_EN` ^| r/w <| SDI module enable
<|`1` `SDI_CTRL_CLR_RX` ^| -/w <| clear RX FIFO when set, bit auto-clears
<|`3:2` _reserved_ ^| r/- <| reserved, read as zero
<|`7:4` `SDI_CTRL_FIFO_MSB : SDI_CTRL_FIFO_LSB` ^| r/- <| FIFO depth; log2(_IO_SDI_FIFO_)
<|`14:8` _reserved_ ^| r/- <| reserved, read as zero
<|`15` `SDI_CTRL_IRQ_RX_AVAIL` ^| r/w <| fire interrupt if RX FIFO is not empty
<|`16` `SDI_CTRL_IRQ_RX_HALF` ^| r/w <| fire interrupt if RX FIFO is at least half full
<|`17` `SDI_CTRL_IRQ_RX_FULL` ^| r/w <| fire interrupt if if RX FIFO is full
<|`18` `SDI_CTRL_IRQ_TX_EMPTY` ^| r/w <| fire interrupt if TX FIFO is empty
<|`22:19` _reserved_ ^| r/- <| reserved, read as zero
<|`23` `SDI_CTRL_RX_AVAIL` ^| r/- <| RX FIFO data available (RX FIFO not empty)
<|`24` `SDI_CTRL_RX_HALF` ^| r/- <| RX FIFO at least half full
<|`25` `SDI_CTRL_RX_FULL` ^| r/- <| RX FIFO full
<|`26` `SDI_CTRL_TX_EMPTY` ^| r/- <| TX FIFO empty
<|`27` `SDI_CTRL_TX_FULL` ^| r/- <| TX FIFO full
<|`31:28` _reserved_ ^| r/- <| reserved, read as zero
| `0xfffffff4` | `DATA` |`7:0` | r/w | receive/transmit data (FIFO)
|=======================

View file

@ -12,71 +12,62 @@
| | `spi_dat_o` | 1-bit serial data output
| | `spi_dat_i` | 1-bit serial data input
| | `spi_csn_o` | 8-bit dedicated chip select output (low-active)
| Configuration generics: | _IO_SPI_EN_ | implement SPI controller when _true_
| | _IO_SPI_FIFO_ | FIFO depth, has to be a power of two, min 1
| Configuration generics: | `IO_SPI_EN` | implement SPI controller when `true`
| | `IO_SPI_FIFO` | FIFO depth, has to be a power of two, min 1
| CPU interrupts: | fast IRQ channel 6 | configurable SPI interrupt (see <<_processor_interrupts>>)
|=======================
**Overview**
SPI is a common synchronous serial transmission interface for fast on-board communications.
The NEORV32 SPI transceiver module operates on 8-bit base, supports all 4 standard clock modes
and provides up to 8 dedicated chip select signals via the top entity's `spi_csn_o` signal.
An receive/transmit FIFO can be configured via the _IO_SPI_FIFO_ generic to support block-based
An receive/transmit FIFO can be configured via the `IO_SPI_FIFO` generic to support block-based
transmissions without CPU interaction.
The SPI module provides a single control register `CTRL` to configure the module and to check it's status
and a single data register `DATA` for receiving/transmitting data.
.Host-Mode Only
[NOTE]
The NEORV32 SPI module only supports _host mode_. Transmission are initiated only by the processor's SPI module
and not by an external SPI module. If you are looking for a _device-mode_ serial peripheral interface (transactions
initiated by an external host) check out the <<_serial_data_interface_sdi>> module..
initiated by an external host) check out the <<_serial_data_interface_controller_sdi>>.
The SPI module provides a single control register `CTRL` to configure the module and to check it's status
and a single data register `DATA` for receiving/transmitting data.
**Theory of Operation**
The SPI module is enabled by setting the _SPI_CTRL_EN_ bit in the `CTRL` control register. No transfer can be initiated
The SPI module is enabled by setting the `SPI_CTRL_EN` bit in the `CTRL` control register. No transfer can be initiated
and no interrupt request will be triggered if this bit is cleared. Clearing this bit will reset the module, clear
the FIFO and terminate any transfer being in process.
The data quantity to be transferred within a single data transmission is fixed to 8 bits. However, the
total transmission length is left to the user: after asserting chip-select an arbitrary amount of transmission
total transmission length is left to the user: after asserting chip-select an arbitrary amount of 8-bit transmission
can be made before de-asserting chip-select again.
A transmission is started when writing data to the transmitter FIFO via the `DATA` register. Note that data always
transferred MSB-first. The SPI operation is completed as soon as the _SPI_CTRL_BUSY_ flag clears. Received data can
be retrieved by reading the RX FIFO also via the `DATA` register. The control register's SPI_CTRL_RX_AVAIL_,
_SPI_CTRL_TX_EMPTY_, _SPI_CTRL_TX_NHALF_ and _SPI_CTRL_TX_FULL_ flags provide information regarding the FIFO levels.
transferred MSB-first. The SPI operation is completed as soon as the `SPI_CTRL_BUSY` flag clears. Received data can
be retrieved by reading the RX FIFO also via the `DATA` register. The control register's `SPI_CTRL_RX_AVAIL`,
`SPI_CTRL_TX_EMPTY`, `SPI_CTRL_TX_NHALF` and `SPI_CTRL_TX_FULL` flags provide information regarding the RX/TX FIFO levels.
The SPI controller features 8 dedicated chip-select lines. These lines are controlled via the control register's
_SPI_CTRL_CS_SELx_ and _SPI_CTRL_CS_EN_ bits. The 3-bit _SPI_CTRL_CSx_ bits are used to select one out of the eight
dedicated chip select lines. As soon as _SPI_CTRL_CS_EN_ is _set_ the selected chip select line is activated (driven _low_).
`SPI_CTRL_CS_SELx` and `SPI_CTRL_CS_EN` bits. The 3-bit `SPI_CTRL_CS_SELx` bits are used to select one out of the eight
dedicated chip select lines. As soon as `SPI_CTRL_CS_EN` is _set_ the selected chip select line is activated (driven _low_).
Note that disabling the SPI module via the _SPI_CTRL_EN_ bit will also deactivate any currently activated chip select line.
**SPI Clock Configuration**
The SPI module supports all standard SPI clock modes (0, 1, 2, 3), which are configured via the two control register bits
_SPI_CTRL_CPHA_ and _SPI_CTRL_CPOL_. The _SPI_CTRL_CPHA_ bit defines the _clock phase_ and the _SPI_CTRL_CPOL_
`SPI_CTRL_CPHA` and `SPI_CTRL_CPOL`. The `SPI_CTRL_CPHA` bit defines the _clock phase_ and the `SPI_CTRL_CPOL`
bit defines the _clock polarity_.
.SPI clock modes; image from https://en.wikipedia.org/wiki/File:SPI_timing_diagram2.svg (license: (Wikimedia) https://en.wikipedia.org/wiki/Creative_Commons[Creative Commons] https://creativecommons.org/licenses/by-sa/3.0/deed.en[Attribution-Share Alike 3.0 Unported])
image::SPI_timing_diagram2.wikimedia.png[]
.SPI standard clock modes
[cols="<2,^1,^1,^1,^1"]
[options="header",grid="rows"]
|=======================
| | Mode 0 | Mode 1 | Mode 2 | Mode 3
| _SPI_CTRL_CPOL_ | `0` | `0` | `1` | `1`
| _SPI_CTRL_CPHA_ | `0` | `1` | `0` | `1`
|=======================
The SPI clock frequency (`spi_clk_o`) is programmed by the 3-bit _SPI_CTRL_PRSCx_ clock prescaler for a coarse clock selection
and a 4-bit clock divider _SPI_CTRL_CDIVx_ for a fine clock configuration.
The SPI clock frequency (`spi_clk_o`) is programmed by the 3-bit `SPI_CTRL_PRSCx` clock prescaler for a coarse clock selection
and a 4-bit clock divider `SPI_CTRL_CDIVx` for a fine clock configuration.
The following clock prescalers (_SPI_CTRL_PRSCx_) are available:
@ -88,10 +79,10 @@ The following clock prescalers (_SPI_CTRL_PRSCx_) are available:
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096
|=======================
Based on the _SPI_CTRL_PRSCx_ and _SPI_CTRL_CDIVx_ configuration, the actual SPI clock frequency f~SPI~ is derived
Based on the programmen clock configuration, the actual SPI clock frequency f~SPI~ is derived
from the processor's main clock f~main~ according to the following equation:
_**f~SPI~**_ = _f~main~[Hz]_ / (2 * `clock_prescaler` * (1 + _SPI_CTRL_CDIVx_))
_**f~SPI~**_ = _f~main~[Hz]_ / (2 * `clock_prescaler` * (1 + `SPI_CTRL_CDIVx`))
Hence, the maximum SPI clock is f~main~ / 4 and the lowest SPI clock is f~main~ / 131072. The SPI clock is always
symmetric having a duty cycle of 50%.
@ -100,11 +91,11 @@ symmetric having a duty cycle of 50%.
**SPI Interrupt**
The SPI module provides a set of programmable interrupt conditions based on the level of the RX/TX FIFO. The different
interrupt sources are enabled by setting the according control register's _SPI_CTRL_IRQ_ bits. All enabled interrupt
interrupt sources are enabled by setting the according control register's `SPI_CTRL_IRQ_*` bits. All enabled interrupt
conditions are logically OR-ed so any enabled interrupt source will trigger the module's interrupt signal.
Once the SPI interrupt has fired it remains pending until the actual cause of the interrupt is resolved; for
example if just the _SPI_CTRL_IRQ_RX_AVAIL_ bit is set, the interrupt will keep firing until the RX FIFO is empty again.
example if just the `SPI_CTRL_IRQ_RX_AVAIL` bit is set, the interrupt will keep firing until the RX FIFO is empty again.
Furthermore, an active SPI interrupt has to be explicitly cleared again by writing zero to the according
<<_mip>> CSR bit.
@ -116,23 +107,23 @@ Furthermore, an active SPI interrupt has to be explicitly cleared again by writi
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.18+<| `0xffffffa8` .18+<| `CTRL` <|`0` _SPI_CTRL_EN_ ^| r/w <| SPI module enable
<|`1` _SPI_CTRL_CPHA_ ^| r/w <| clock phase
<|`2` _SPI_CTRL_CPOL_ ^| r/w <| clock polarity
<|`5:3` _SPI_CTRL_CS_SEL2_ : _SPI_CTRL_CS_SEL0_ ^| r/w <| Direct chip-select 0..7
<|`6` _SPI_CTRL_CS_EN_ ^| r/w <| Direct chip-select enable: setting `spi_csn_o(SPI_CTRL_CS_SEL)` low when set
<|`9:7` _SPI_CTRL_PRSC2_ : _SPI_CTRL_PRSC0_ ^| r/w <| 3-bit clock prescaler select
<|`13:10` _SPI_CTRL_CDIV2_ : _SPI_CTRL_CDIV0_ ^| r/w <| 4-bit clock divider
<|`15:14` _reserved_ ^| r/- <| reserved, read as zero
<|`16` _SPI_CTRL_RX_AVAIL_ ^| r/- <| RX FIFO data available (RX FIFO not empty)
<|`17` _SPI_CTRL_TX_EMPTY_ ^| r/- <| TX FIFO empty
<|`18` _SPI_CTRL_TX_NHALF_ ^| r/- <| TX FIFO _not_ at least half full
<|`19` _SPI_CTRL_TX_FULL_ ^| r/- <| TX FIFO full
<|`20` _SPI_CTRL_IRQ_RX_AVAIL_ ^| r/w <| Trigger IRQ if RX FIFO not empty
<|`21` _SPI_CTRL_IRQ_TX_EMPTY_ ^| r/w <| Trigger IRQ if TX FIFO empty
<|`22` _SPI_CTRL_IRQ_TX_NHALF_ ^| r/w <| Trigger IRQ if TX FIFO _not_ at least half full
<|`26:23` _SPI_CTRL_FIFO_MSB_ : _SPI_CTRL_FIFO_LSB_ ^| r/- <| FIFO depth; log2(_IO_SPI_FIFO_)
<|`30:27` _reserved_ ^| r/- <| reserved, read as zero
<|`31` _SPI_CTRL_BUSY_ ^| r/- <| SPI module busy when set (serial engine operation in progress and TX FIFO not empty yet)
.18+<| `0xffffffa8` .18+<| `CTRL` <|`0` `SPI_CTRL_EN` ^| r/w <| SPI module enable
<|`1` `SPI_CTRL_CPHA` ^| r/w <| clock phase
<|`2` `SPI_CTRL_CPOL` ^| r/w <| clock polarity
<|`5:3` `SPI_CTRL_CS_SEL2 : SPI_CTRL_CS_SEL0` ^| r/w <| Direct chip-select 0..7
<|`6` `SPI_CTRL_CS_EN` ^| r/w <| Direct chip-select enable: setting `spi_csn_o(SPI_CTRL_CS_SEL)` low when set
<|`9:7` `SPI_CTRL_PRSC2 : SPI_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select
<|`13:10` `SPI_CTRL_CDIV2 : SPI_CTRL_CDIV0` ^| r/w <| 4-bit clock divider
<|`15:14` _reserved_ ^| r/- <| reserved, read as zero
<|`16` `SPI_CTRL_RX_AVAIL` ^| r/- <| RX FIFO data available (RX FIFO not empty)
<|`17` `SPI_CTRL_TX_EMPTY` ^| r/- <| TX FIFO empty
<|`18` `SPI_CTRL_TX_NHALF` ^| r/- <| TX FIFO _not_ at least half full
<|`19` `SPI_CTRL_TX_FULL` ^| r/- <| TX FIFO full
<|`20` `SPI_CTRL_IRQ_RX_AVAIL` ^| r/w <| Trigger IRQ if RX FIFO not empty
<|`21` `SPI_CTRL_IRQ_TX_EMPTY` ^| r/w <| Trigger IRQ if TX FIFO empty
<|`22` `SPI_CTRL_IRQ_TX_NHALF` ^| r/w <| Trigger IRQ if TX FIFO _not_ at least half full
<|`26:23` `SPI_CTRL_FIFO_MSB : SPI_CTRL_FIFO_LSB` ^| r/- <| FIFO depth; log2(_IO_SPI_FIFO_)
<|`30:27` `reserved_ ^| r/- <| reserved, read as zero
<|`31` `SPI_CTRL_BUSY` ^| r/- <| SPI module busy when set (serial engine operation in progress and TX FIFO not empty yet)
| `0xffffffac` | `DATA` |`7:0` | r/w | receive/transmit data (FIFO)
|=======================

View file

@ -12,10 +12,11 @@
| 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.
**Overview**
The SYSINFO allows the application software to determine the setting of most of the <<_processor_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
@ -33,14 +34,14 @@ will signal a "DEVICE ERROR" in this case.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Function
| `0xffffffe0` | `CLK` | clock speed in Hz (via top's <<_clock_frequency>> generic)
| `0xffffffe4` | `CUSTOM_ID | custom user-defined ID (via top's <<_custom_id>> generic)
| `0xffffffe8` | `SOC` | specific SoC configuration (see <<_sysinfo_soc_configuration>>)
| `0xffffffe0` | `CLK` | clock speed in Hz (via top's `CLOCK_FREQUENCY` generic)
| `0xffffffe4` | `CUSTOM_ID | custom user-defined ID (via top's `CUSTOM_ID` generic)
| `0xffffffe8` | `SOC` | specific SoC configuration (see `sysinfo_soc_configuration>>)
| `0xffffffec` | `CACHE` | cache configuration information (see <<_sysinfo_cache_configuration>>)
| `0xfffffff0` | `ISPACE_BASE` | instruction address space base (via package's `ispace_base_c` constant)
| `0xfffffff4` | `IMEM_SIZE` | internal IMEM size in bytes (via top's <<_mem_int_imem_size>> generic)
| `0xfffffff4` | `IMEM_SIZE` | internal IMEM size in bytes (via top's `MEM_INT_IMEM_SIZE` generic)
| `0xfffffff8` | `DSPACE_BASE` | data address space base (via package's `sdspace_base_c` constant)
| `0xfffffffc` | `DMEM_SIZE` | internal DMEM size in bytes (via top's <<_mem_int_dmem_size>> generic)
| `0xfffffffc` | `DMEM_SIZE` | internal DMEM size in bytes (via top's `MEM_INT_DMEM_SIZE` generic)
|=======================
@ -51,32 +52,32 @@ will signal a "DEVICE ERROR" in this case.
[options="header",grid="all"]
|=======================
| Bit | Name [C] | Function
| `0` | _SYSINFO_SOC_BOOTLOADER_ | set if the processor-internal bootloader is implemented (via top's <<_int_bootloader_en>> generic)
| `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)
| `12:6` | - | _reserved_, read as zero
| `13` | _SYSINFO_SOC_IS_SIM_ | set if processor is being **simulated** (⚠️ not guaranteed)
| `14` | _SYSINFO_SOC_OCD_ | set if on-chip debugger implemented (via top's <<_on_chip_debugger_en>> generic)
| `15` | - | _reserved_, read as zero
| `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_num_ch>> 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` | _SYSINFO_SOC_IO_SDI_ | set if the SDI is implemented (via top's <<_io_sdi_en>> generic)
| `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)
| `28` | _SYSINFO_SOC_IO_XIRQ_ | set if the XIRQ is implemented (via top's <<_xirq_num_ch>> generic)
| `29` | _SYSINFO_SOC_IO_GPTMR_ | set if the GPTMR is implemented (via top's <<_io_gptmr_en>> generic)
| `30` | _SYSINFO_SOC_IO_XIP_ | set if the XIP module is implemented (via top's <<_io_xip_en>> generic)
| `31` | _SYSINFO_SOC_IO_ONEWIRE_ | set if the ONEWIRE interface is implemented (via top's <<_io_onewire_en>> generic)
| `0` | `SYSINFO_SOC_BOOTLOADER` | set if the processor-internal bootloader is implemented (via top's `INT_BOOTLOADER_EN` generic)
| `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)
| `12:6` | - | _reserved_, read as zero
| `13` | `SYSINFO_SOC_IS_SIM` | set if processor is being **simulated** (⚠️ not guaranteed)
| `14` | `SYSINFO_SOC_OCD` | set if on-chip debugger implemented (via top's `ON_CHIP_DEBUGGER_EN` generic)
| `15` | - | _reserved_, read as zero
| `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_NUM_CH` 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` | `SYSINFO_SOC_IO_SDI` | set if the SDI is implemented (via top's `IO_SDI_EN` generic)
| `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)
| `28` | `SYSINFO_SOC_IO_XIRQ` | set if the XIRQ is implemented (via top's `XIRQ_NUM_CH` generic)
| `29` | `SYSINFO_SOC_IO_GPTMR` | set if the GPTMR is implemented (via top's `IO_GPTMR_EN` generic)
| `30` | `SYSINFO_SOC_IO_XIP` | set if the XIP module is implemented (via top's `IO_XIP_EN` generic)
| `31` | `SYSINFO_SOC_IO_ONEWIRE` | set if the ONEWIRE interface is implemented (via top's `IO_ONEWIRE_EN` generic)
|=======================
@ -90,9 +91,9 @@ Bit fields in this register are set to all-zero if the according cache is not im
[options="header",grid="all"]
|=======================
| 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: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
|=======================

View file

@ -9,13 +9,13 @@
| Software driver file(s): | neorv32_trng.c |
| | neorv32_trng.h |
| Top entity port: | none |
| Configuration generics: | _IO_TRNG_EN_ | implement TRNG when _true_
| | _IO_TRNG_FIFO_ | data FIFO depth, min 1, has to be a power of two
| Configuration generics: | `IO_TRNG_EN` | implement TRNG when `true`
| | `IO_TRNG_FIFO` | data FIFO depth, min 1, has to be a power of two
| CPU interrupts: | none |
|=======================
**Theory of Operation**
**Overview**
The NEORV32 true random number generator provides _physically_ true random numbers.
Instead of using a pseudo RNG like a LFSR, the TRNG uses a simple, straight-forward ring
@ -23,48 +23,38 @@ oscillator concept as physical entropy source. Hence, voltage, thermal and also
fluctuations are used to provide a true physical entropy source.
The TRNG features a platform independent architecture without FPGA-specific primitives, macros or
attributes so it can be synthesized for _any_ FPGA. Ir is based on the **neoTRNG V2**, which is a "spin-off project" of the
attributes so it can be synthesized for _any_ FPGA. It is based on the **neoTRNG V2**, which is a "spin-off project" of the
NEORV32 processor. More detailed information about the neoTRNG, its architecture and a
detailed evaluation of the random number quality can be found it the neoTRNG repository: https://github.com/stnolting/neoTRNG
.Inferring Latches
[NOTE]
The synthesis tool might emit a warning like _"inferring latches for ... neorv32_trng ..."_. This is no problem
The synthesis tool might emit a warning like "inferring latches for ... neorv32_trng ...". This is no problem
as this is what we actually want: the TRNG is based on latches, which implement the inverters of the ring oscillators.
.Simulation
[IMPORTANT]
When simulating the processor the NEORV32 TRNG is automatically set to "simulation mode". In this mode, the physical entropy
sources (= the ring oscillators) are replaced by a simple **pseudo RNG (LFSR)** providing weak pseudo-random data only.
The _TRNG_CTRL_SIM_MODE_ flag of the control register is set if simulation mode is active.
The `TRNG_CTRL_SIM_MODE` flag of the control register is set if simulation mode is active.
**Using the TRNG**
**Theory of Operation**
The TRNG features a single control register `CTRL` for control, status check and data access. When the _TRNG_CTRL_EN_
bit is set, the TRNG is enabled and starts operation.
The TRNG features a single control register `CTRL` for control, status check and data access. When the `TRNG_CTRL_EN`
bit is set, the TRNG is enabled and starts operation. As soon as the `TRNG_CTRL_VALID` bit is set a new random data byte
is available and can be obtained from the lowest 8 bits of the `CTRL` register. If this bit is cleared, there is no
valid data available and the lowest 8 bit of the `CTRL` register are set to all-zero.
.TRNG Reset
[NOTE]
The TRNG core does not provide a dedicated reset. In order to ensure correct operations, the TRNG should be
disabled (=reset) by clearing the _TRNG_CTRL_EN_ and waiting some 1000s clock cycles before re-enabling it.
As soon as the _TRNG_CTRL_VALID_ bit is set a new random data byte is available and can be obtained from the lowest 8 bits
of the `CTRL` register (_TRNG_CTRL_DATA_MSB_ : _TRNG_CTRL_DATA_LSB_). If this bit is cleared, there is no valid data available
and the lowest 8 bit of the `CTRL` register are set to all-zero.
.Read Access Security
[NOTE]
The random data byte (_TRNG_CTRL_DATA_) in the control register is automatically cleared after each read access
to prevent software from reading the _same_ random data byte more than once.
An optional random data FIFO can be configured using the <<_io_trng_fifo>> generic. This FIFO automatically samples
An internal entropy FIFO can be configured using the `IO_TRNG_FIFO` generic. This FIFO automatically samples
new random data from the TRNG to provide some kind of _random data pool_ for applications, which require a large number
of RND data in a short time. The minimal and default value for <<_io_trng_fifo>> is 1 (implementing a register rather
than a real FIFO); the generic has to be a power of two.
of RND data in a short time. The random data FIFO can be cleared at any time either by disabling the TRNG or by
setting the `TRNG_CTRL_FIFO_CLR` flag.
The random data FIFO can be cleared at any time either by disabling the TRNG via the _TRNG_CTRL_EN_ flag or by
setting the _TRNG_CTRL_FIFO_CLR_ flag. Note that this flag is write-only and auto clears after being set.
.Data Gating
[NOTE]
The TRNG data bits `TRNG_CTRL_DATA_MSB : TRNG_CTRL_DATA_MSB` are set to zero if `TRNG_CTRL_VALID` is low.
This prevents a random byte being read twice.
**Register Map**
@ -74,9 +64,9 @@ setting the _TRNG_CTRL_FIFO_CLR_ flag. Note that this flag is write-only and aut
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.5+<| `0xffffffb8` .5+<| `CTRL` <|`7:0` _TRNG_CTRL_DATA_MSB_ : _TRNG_CTRL_DATA_MSB_ ^| r/- <| 8-bit random data
<|`28` _TRNG_CTRL_FIFO_CLR_ ^| -/w <| flush random data FIFO when set (auto clears)
<|`29` _TRNG_CTRL_SIM_MODE_ ^| r/- <| simulation mode (PRNG!)
<|`30` _TRNG_CTRL_EN_ ^| r/w <| TRNG enable
<|`31` _TRNG_CTRL_VALID_ ^| r/- <| random data is valid when set
.5+<| `0xffffffb8` .5+<| `CTRL` <|`7:0` `TRNG_CTRL_DATA_MSB : TRNG_CTRL_DATA_MSB` ^| r/- <| 8-bit random data
<|`28` `TRNG_CTRL_FIFO_CLR` ^| -/w <| flush random data FIFO when set (auto-clears)
<|`29` `TRNG_CTRL_SIM_MODE` ^| r/- <| simulation mode (PRNG!)
<|`30` `TRNG_CTRL_EN` ^| r/w <| TRNG enable
<|`31` `TRNG_CTRL_VALID` ^| r/- <| random data is valid when set
|=======================

View file

@ -12,20 +12,15 @@
| | `twi_sda_o` | 1-bit serial data line output (pull low only)
| | `twi_scl_i` | 1-bit serial clock line sense input
| | `twi_scl_o` | 1-bit serial clock line output (pull low only)
| Configuration generics: | _IO_TWI_EN_ | implement TWI controller when _true_
| 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**
**Overview**
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 SDA and the serial
clock line SCL) for an arbitrarily number of devices it allows easy interconnections of
several peripheral nodes.
The NEORV32 TWI implements a **TWI controller**. Currently, **no multi-controller
support** is available. Furthermore, the NEORV32 TWI unit cannot operate in peripheral mode.
The NEORV32 TWI implements a **TWI controller**. Currently, **no multi-controller support** is available.
Furthermore, the NEORV32 TWI unit cannot operate in peripheral mode.
[IMPORTANT]
The serial clock (SCL) and the serial data (SDA) lines can only be actively driven low by the
@ -34,26 +29,24 @@ controller. Hence, external pull-up resistors are required for these lines.
**Tri-State Drivers**
The TWI module requires two tri-state drivers for the SDA and SCL lines, which have to be implemented
in the top module of the setup. A generic VHDL example is given below (`sda` and `scl` are the actual TWI
The TWI module requires two tri-state drivers (actually: open-drain) for the SDA and SCL lines, which have to be
implemented in the top module of the setup. A generic VHDL example is given below (`sda` and `scl` are the actual TWI
bus signal, which are of type `std_logic`).
.TWI VHDL tri-state driver example
[source,VHDL]
----
sda <= '0' when (twi_sda_o = '0') else 'Z';
scl <= '0' when (twi_scl_o = '0') else 'Z';
twi_sda_i <= std_ulogic(sda);
twi_scl_i <= std_ulogic(scl);
sda <= '0' when (twi_sda_o = '0') else 'Z'; -- drive
scl <= '0' when (twi_scl_o = '0') else 'Z'; -- drive
twi_sda_i <= std_ulogic(sda); -- sense
twi_scl_i <= std_ulogic(scl); -- sense
----
**TWI Clock Speed**
The TWI clock frequency is programmed by the 3-bit _TWI_CTRL_PRSCx_ clock prescaler for a coarse selection
and a 4-bit clock divider _TWI_CTRL_CDIVx_ for a fine selection.
The following pre-scalers (_TWI_CTRL_PRSCx_) are available:
The TWI clock frequency is programmed by the 3-bit `TWI_CTRL_PRSCx` clock prescaler for a coarse selection
and a 4-bit clock divider `TWI_CTRL_CDIVx` for a fine selection.
.TWI prescaler configuration
[cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"]
@ -63,7 +56,7 @@ The following pre-scalers (_TWI_CTRL_PRSCx_) are available:
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096
|=======================
Based on the _TWI_CTRL_PRSCx_ and _TWI_CTRL_CDIVx_ configuration, the actual TWI clock frequency f~SCL~ is derived
Based on the the clock configuration, the actual TWI clock frequency f~SCL~ is derived
from the processor's main clock f~main~ according to the following equation:
_**f~SCL~**_ = _f~main~[Hz]_ / (4 * `clock_prescaler` * (1 + TWI_CTRL_CDIV))
@ -71,59 +64,20 @@ _**f~SCL~**_ = _f~main~[Hz]_ / (4 * `clock_prescaler` * (1 + TWI_CTRL_CDIV))
Hence, the maximum TWI clock is f~main~ / 8 and the lowest TWI clock is f~main~ / 262144. The generated TWI clock is
always symmetric having a duty cycle of exactly 50%. However, an accessed peripheral can "slow down" the bus clock
by using **clock stretching** (= actively driving the SCL line low). The controller will pause operation in this case
if clock stretching is enabled via the _TWI_CTRL_CSEN_ bit of the unit's control register `CTRL`
if clock stretching is enabled via the `TWI_CTRL_CSEN` bit of the unit's control register `CTRL`
**TWI Transfers**
The TWI is enabled via the _TWI_CTRL_EN_ bit in the `CTRL` control register. The user program can start / stop a
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.
according bits (`TWI_CTRL_START` or `TWI_CTRL_STOP`) in the control register.
Data is transferred via the TWI bus by writing a byte to the `DATA` register. The written byte is send via the TWI bus
and the received byte from the bus is also available in this register after the transmission is completed.
The TWI operation (transmitting data or performing a START or STOP condition) is in progress as long as the
control register's _TWI_CTRL_BUSY_ bit is set.
**TWI ACK/NACK and MACK**
An accessed TWI 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 this bit is cleared after a completed
transmission, the peripheral has send a _not-acknowledge_ (NACK).
The NEORV32 TWI controller can also send an ACK generated by itself ("controller acknowledge _MACK_") right after
transmitting a byte by driving SDA low during the ACK time slot. Some TWI modules require this MACK to acknowledge
certain data movement operations.
The control register's _TWI_CTRL_MACK_ bit has to be set to make the TWI module automatically generate a MACK after
the byte transmission has been completed. If this bit is cleared, the ACK/NACK generated by the peripheral is sampled
in this time slot instead (normal mode).
**TWI Bus Status**
The TWI controller can check if the TWI bus is currently claimed (SCL and SDA both low). The bus can be claimed by the
NEORV32 TWI itself or by any other controller. Bit _TWI_CTRL_CLAIMED_ of the control register will be set if the bus
is currently claimed.
**Summary**
In summary, a complete TWI transfer is based on the following elementary operation:
[start=1]
. generate START condition by setting _TWI_CTRL_START_
. wait until _TWI_CTRL_BUSY_ has cleared (start condition completed)
. transfer one byte while also sampling one byte from the bus (this also samples ACK/NACK or generates a
controller ACK "MACK" if _TWI_CTRL_MACK_ is set) by writing data to `NEORV32_TWI.DATA`; this step can be repeated to
send/receive an arbitrary number of bytes
. wait until _TWI_CTRL_BUSY_ has cleared (data transfer completed)
. optionally generate another START condition (as REPEATED-START condition) by setting _TWI_CTRL_START_ again
. wait until _TWI_CTRL_BUSY_ has cleared (repeated-start condition completed)
. generate STOP condition by setting _TWI_CTRL_STOP_
. wait until _TWI_CTRL_BUSY_ has cleared (stop condition completed)
control register's `TWI_CTRL_BUSY` bit is set.
[TIP]
A transmission can be terminated at any time by disabling the TWI module
@ -134,6 +88,28 @@ When reading data from a device, an all-one byte (`0xFF`) has to be written to T
so the accessed device can actively pull-down SDA when required.
**TWI ACK/NACK and MACK**
An accessed TWI 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 (ACK). If this bit is cleared after a completed
transmission, the peripheral has send a_NOT-ACKNOWLEDGE (NACK).
The NEORV32 TWI controller can also send an ACK generated by itself ("controller acknowledge `MACK`") right after
transmitting a byte by driving SDA low during the ACK time slot. Some TWI modules require this MACK to acknowledge
certain data movement operations.
The control register's `TWI_CTRL_MACK` bit has to be set to make the TWI module automatically generate a MACK after
the byte transmission has been completed. If this bit is cleared, the ACK/NACK generated by the peripheral is sampled
in this time slot instead (normal mode).
**TWI Bus Status**
The TWI controller can check if the TWI bus is currently claimed (SCL and SDA both low). The bus can be claimed by the
NEORV32 TWI itself or by any other controller. Bit `TWI_CTRL_CLAIME` of the control register will be set if the bus
is currently claimed.
**TWI Interrupt**
The TWI module provides a single interrupt to signal "transmission done" to the CPU. Whenever the TWI
@ -149,16 +125,16 @@ explicitly cleared again by writing zero to the according <<_mip>> CSR bit.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.10+<| `0xffffffb0` .10+<| `CTRL` <|`0` _TWI_CTRL_EN_ ^| r/w <| TWI enable, reset if cleared
<|`1` _TWI_CTRL_START_ ^| -/w <| generate START condition, auto-clears
<|`2` _TWI_CTRL_STOP_ ^| -/w <| generate STOP condition, auto-clears
<|`3` _TWI_CTRL_MACK_ ^| r/w <| generate controller-ACK for each transmission ("MACK")
<|`4` _TWI_CTRL_CSEN_ ^| r/w <| allow clock stretching when set
<|`7:5` _TWI_CTRL_PRSC2_ : _TWI_CTRL_PRSC0_ ^| r/w <| 3-bit clock prescaler select
<|`11:8` _TWI_CTRL_CDIV3_ : _TWI_CTRL_CDIV0_ ^| r/w <| 4-bit clock divider
<|`28:12` - ^| r/- <| _reserved_, read as zero
<|`29` _TWI_CTRL_CLAIMED_ ^| r/- <| set if the TWI bus is claimed by any controller
<|`30` _TWI_CTRL_ACK_ ^| r/- <| ACK received when set, NACK received when cleared
<|`31` _TWI_CTRL_BUSY_ ^| r/- <| transfer/START/STOP in progress when set
| `0xffffffb4` | `DATA` |`7:0` _TWI_DATA_MSB_ : _TWI_DATA_LSB_ | r/w | receive/transmit data
.10+<| `0xffffffb0` .10+<| `CTRL` <|`0` `TWI_CTRL_EN` ^| r/w <| TWI enable, reset if cleared
<|`1` `TWI_CTRL_START` ^| -/w <| generate START condition, auto-clears
<|`2` `TWI_CTRL_STOP` ^| -/w <| generate STOP condition, auto-clears
<|`3` `TWI_CTRL_MACK` ^| r/w <| generate controller-ACK for each transmission ("MACK")
<|`4` `TWI_CTRL_CSEN` ^| r/w <| allow clock stretching when set
<|`7:5` `TWI_CTRL_PRSC2 : TWI_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select
<|`11:8` `TWI_CTRL_CDIV3 : TWI_CTRL_CDIV0` ^| r/w <| 4-bit clock divider
<|`28:12` - ^| r/- <| _reserved_, read as zero
<|`29` `TWI_CTRL_CLAIMED` ^| r/- <| set if the TWI bus is claimed by any controller
<|`30` `TWI_CTRL_ACK` ^| r/- <| ACK received when set, NACK received when cleared
<|`31` `TWI_CTRL_BUSY` ^| r/- <| transfer/START/STOP in progress when set
| `0xffffffb4` | `DATA` |`7:0` | r/w | receive/transmit data
|=======================

View file

@ -12,9 +12,9 @@
| | `uart0_rxd_i` | serial receiver input
| | `uart0_rts_o` | flow control: RX ready to receive, low-active
| | `uart0_cts_i` | flow control: RX ready to receive, low-active
| Configuration generics: | _IO_UART0_EN_ | implement UART0 when _true_
| | _UART0_RX_FIFO_ | RX FIFO depth (power of 2, min 1)
| | _UART0_TX_FIFO_ | TX FIFO depth (power of 2, min 1)
| Configuration generics: | `IO_UART0_EN` | implement UART0 when `true`
| | `UART0_RX_FIFO` | RX FIFO depth (power of 2, min 1)
| | `UART0_TX_FIFO` | TX FIFO depth (power of 2, min 1)
| CPU interrupts: | fast IRQ channel 2 | RX interrupt
| | fast IRQ channel 3 | TX interrupt (see <<_processor_interrupts>>)
|=======================
@ -22,8 +22,8 @@
**Overview**
The NEORV32 UART provides a standard serial interface with independent transmitter and receiver channel, each
quipped with a configurable FIFO. The transmission frame is fixed to **8N1**: 8 data bits, no parity bit, 1 stop
The NEORV32 UART provides a standard serial interface with independent transmitter and receiver channels, each
equipped with a configurable FIFO. The transmission frame is fixed to **8N1**: 8 data bits, no parity bit, 1 stop
bit. The actual transmission rate (Baud rate) is programmable via software. The module features two memory-mapped
registers: `CTRL` and `DATA`. These are used for configuration, status check and data transfer.
@ -36,11 +36,11 @@ is used to implement the "standard consoles" (`STDIN`, `STDOUT` and `STDERR`).
**Theory of Operation**
UART0 is enabled by setting the _UART_CTRL_EN_ bit in the UART0 control register `CTRL`. The Baud rate
is configured via a 10-bit _UART_CTRL_BAUDx_ baud divisor (`baud_div`) and a 3-bit _UART_CTRL_PRSCx_
The module is enabled by setting the `UART_CTRL_EN` bit in the UART0 control register `CTRL`. The Baud rate
is configured via a 10-bit `UART_CTRL_BAUDx` baud divisor (`baud_div`) and a 3-bit `UART_CTRL_PRSCx`
clock prescaler (`clock_prescaler`).
.UART0 clock configuration
.UART0 Clock Configuration
[cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"]
[options="header",grid="rows"]
|=======================
@ -50,12 +50,12 @@ clock prescaler (`clock_prescaler`).
_**Baud rate**_ = (_f~main~[Hz]_ / `clock_prescaler`) / (`baud_div` + 1)
The control register's _UART_CTRL_RX_ and _UART_CTRL_TX_ flags provide information about the RX and TX FIFO fill level.
Disabling the module via the _UART_CTRL_EN_ bit will also clear these FIFOs.
The control register's `UART_CTRL_RX_*` and `UART_CTRL_TX_*` flags provide information about the RX and TX FIFO fill level.
Disabling the module via the `UART_CTRL_EN` bit will also clear these FIFOs.
A new TX 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. Rx data is available when
the _UART_CTRL_RX_NEMPTY_ flag becomes set. The _UART_CTRL_RX_OVER_ will be set if the RX FIFO overflows. This flag
A new TX transmission is started by writing data to the lowest byte of the `DATA` register. The
transfer is completed when the `UART_CTRL_TX_BUSY` control register flag returns to zero. RX data is available when
the `UART_CTRL_RX_NEMPTY` flag becomes set. The `UART_CTRL_RX_OVER` will be set if the RX FIFO overflows. This flag
is cleared by reading the `DATA` register or by disabling the module.
@ -63,18 +63,18 @@ is cleared by reading the `DATA` register or by disabling the module.
The UART module provides independent interrupt channels for RX and TX. These interrupts are triggered by certain RX and TX
FIFO levels. The actual configuration is programmed independently for the RX and TX interrupt channel via the control register's
_UART_CTRL_IRQ_RX_ and _UART_CTRL_IRQ_TX_ bits:
`UART_CTRL_IRQ_RX_*` and `UART_CTRL_IRQ_TX_*` bits:
. **RX IRQ** The RX interrupt can be triggered by three different RX FIFO level states: If _UART_CTRL_IRQ_RX_NEMPTY_ is set the
interrupt fires if the RX FIFO is _not_ empty (e.g. when incoming data is available). If _UART_CTRL_IRQ_RX_HALF_ is set the RX IRQ
fires if the RX FIFO is at least half-full. If _UART_CTRL_IRQ_RX_FULL_ the interrupt fires if the RX FIFO is full. Note that all
. **RX IRQ** The RX interrupt can be triggered by three different RX FIFO level states: If `UART_CTRL_IRQ_RX_NEMPTY` is set the
interrupt fires if the RX FIFO is _not_ empty (e.g. when incoming data is available). If `UART_CTRL_IRQ_RX_HALF` is set the RX IRQ
fires if the RX FIFO is at least half-full. If `UART_CTRL_IRQ_RX_FULL` the interrupt fires if the RX FIFO is full. Note that all
these programmable conditions are logically OR-ed (interrupt fires if any enabled conditions is true).
. **TX IRQ** The TX interrupt can be triggered by two different TX FIFO level states: If _UART_CTRL_IRQ_TX_EMPTY_ is set the
interrupt fires if the TX FIFO is empty. If _UART_CTRL_IRQ_TX_NHALF_ is set the interrupt fires if the TX FIFO is _not_ at least
. **TX IRQ** The TX interrupt can be triggered by two different TX FIFO level states: If `UART_CTRL_IRQ_TX_EMPTY` is set the
interrupt fires if the TX FIFO is empty. If `UART_CTRL_IRQ_TX_NHALF` is set the interrupt fires if the TX FIFO is _not_ at least
half full. Note that all these programmable conditions are logically OR-ed (interrupt fires if any enabled conditions is true).
Once an UART interrupt has fired it remains pending until the actual cause of the interrupt is resolved; for
example if just the _UART_CTRL_IRQ_RX_NEMPTY_ bit is set, the RX interrupt will keep firing until the RX FIFO is empty again.
example if just the `UART_CTRL_IRQ_RX_NEMPTY` bit is set, the RX interrupt will keep firing until the RX FIFO is empty again.
Furthermore, a pending UART interrupt has to be explicitly cleared again by writing zero to the according <<_mip>> CSR bit.
@ -82,15 +82,15 @@ Furthermore, a pending UART interrupt has to be explicitly cleared again by writ
The NEORV32 UART supports optional hardware flow control using the standard CTS `uart0_cts_i` ("clear to send") and RTS
`uart0_rts_o` ("ready to send" / "ready to receive (RTR)") signals. Both signals are low-active.
Hardware flow control is enabled by setting the _UART_CTRL_HWFC_EN_ bit in the modules control register `CTRL`.
Hardware flow control is enabled by setting the `UART_CTRL_HWFC_EN` bit in the modules control register `CTRL`.
When hardware flow control is enabled:
. The UART's transmitter will not start a new transmission until the `uart0_cts_i` signal goes low.
During this time, the UART busy flag _UART_CTRL_TX_BUSY_ remains set.
During this time, the UART busy flag `UART_CTRL_TX_BUSY` remains set.
. The UART will set `uart0_rts_o` signal low if the RX FIFO is **less than half full** (to have a wide safety margin).
As long as this signal is low, the connected device can send new data. `uart0_rts_o` is always low if the hardware flow-control
is disabled. Disabling the UART (setting _UART_CTRL_EN_ low) while having hardware flow-control enabled, will set `uart0_rts_o`
is disabled. Disabling the UART (setting `UART_CTRL_EN` low) while having hardware flow-control enabled, will set `uart0_rts_o`
high to signal that the UARt is not capable of receiving new data.
[NOTE]
@ -101,7 +101,7 @@ unconnected. If the CTS handshake is not required it has to be tied to zero.
**Simulation Mode**
The UART provides a _simulation-only_ mode to dump console data as well as raw data directly to a file. When the simulation
mode is enabled (by setting the _UART_CTRL_SIM_MODE_ bit) there will be **no** physical transaction on the `uart0_txd_o` signal.
mode is enabled (by setting the `UART_CTRL_SIM_MODE` bit) there will be **no** physical transaction on the `uart0_txd_o` signal.
Instead, all data written to the `DATA` register is immediately dumped to a file.
. Data written to `DATA[7:0]` will be dumped as ASCII chars to a file named `neorv32.uart0.sim_mode.text.out`. Additionally,
@ -118,24 +118,25 @@ Both file are created in the simulation's home folder.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.18+<| `0xffffffa0` .18+<| `CTRL` <|`0` _UART_CTRL_EN_ ^| r/w <| UART enable
<|`1` _UART_CTRL_SIM_MODE_ ^| r/w <| enable **simulation mode**
<|`2` _UART_CTRL_HWFC_EN_ ^| r/w <| enable RTS/CTS hardware flow-control
<|`5:3` _UART_CTRL_PRSC2_ : _UART_CTRL_PRSC0_ ^| r/w <| Baud rate clock prescaler select
<|`15:6` _UART_CTRL_BAUD9_ : _UART_CTRL_BAUD0_ ^| r/w <| 12-bit Baud value configuration value
<|`16` _UART_CTRL_RX_NEMPTY_ ^| r/- <| RX FIFO not empty
<|`17` _UART_CTRL_RX_HALF_ ^| r/- <| RX FIFO at least half-full
<|`18` _UART_CTRL_RX_FULL_ ^| r/- <| RX FIFO full
<|`19` _UART_CTRL_TX_EMPTY_ ^| r/- <| TX FIFO empty
<|`20` _UART_CTRL_TX_NHALF_ ^| r/- <| TX FIFO not at least half-full
<|`21` _UART_CTRL_TX_FULL_ ^| r/- <| TX FIFO full
<|`22` _UART_CTRL_IRQ_RX_NEMPTY_ ^| r/w <| fire IRQ if RX FIFO not empty
<|`23` _UART_CTRL_IRQ_RX_HALF_ ^| r/w <| fire IRQ if RX FIFO at least half-full
<|`24` _UART_CTRL_IRQ_RX_FULL_ ^| r/w <| fire IRQ if RX FIFO full
<|`25` _UART_CTRL_IRQ_TX_EMPTY_ ^| r/w <| fire IRQ if TX FIFO empty
<|`26` _UART_CTRL_IRQ_TX_NHALF_ ^| r/w <| fire IRQ if TX not at least half full
<|`30` _UART_CTRL_RX_OVER_ ^| r/- <| RX FIFO overflow
<|`31` _UART_CTRL_TX_BUSY_ ^| r/- <| TX busy or TX FIFO not empty
.19+<| `0xffffffa0` .19+<| `CTRL` <|`0` `UART_CTRL_EN` ^| r/w <| UART enable
<|`1` `UART_CTRL_SIM_MODE` ^| r/w <| enable **simulation mode**
<|`2` `UART_CTRL_HWFC_EN` ^| r/w <| enable RTS/CTS hardware flow-control
<|`5:3` `UART_CTRL_PRSC2 : UART_CTRL_PRSC0` ^| r/w <| Baud rate clock prescaler select
<|`15:6` `UART_CTRL_BAUD9 : UART_CTRL_BAUD0` ^| r/w <| 12-bit Baud value configuration value
<|`16` `UART_CTRL_RX_NEMPTY` ^| r/- <| RX FIFO not empty
<|`17` `UART_CTRL_RX_HALF` ^| r/- <| RX FIFO at least half-full
<|`18` `UART_CTRL_RX_FULL` ^| r/- <| RX FIFO full
<|`19` `UART_CTRL_TX_EMPTY` ^| r/- <| TX FIFO empty
<|`20` `UART_CTRL_TX_NHALF` ^| r/- <| TX FIFO not at least half-full
<|`21` `UART_CTRL_TX_FULL` ^| r/- <| TX FIFO full
<|`22` `UART_CTRL_IRQ_RX_NEMPTY` ^| r/w <| fire IRQ if RX FIFO not empty
<|`23` `UART_CTRL_IRQ_RX_HALF` ^| r/w <| fire IRQ if RX FIFO at least half-full
<|`24` `UART_CTRL_IRQ_RX_FULL` ^| r/w <| fire IRQ if RX FIFO full
<|`25` `UART_CTRL_IRQ_TX_EMPTY` ^| r/w <| fire IRQ if TX FIFO empty
<|`26` `UART_CTRL_IRQ_TX_NHALF` ^| r/w <| fire IRQ if TX not at least half full
<|`29:27` - ^| r/- <| _reserved_ read as zero
<|`30` `UART_CTRL_RX_OVER` ^| r/- <| RX FIFO overflow
<|`31` `UART_CTRL_TX_BUSY` ^| r/- <| TX busy or TX FIFO not empty
.3+<| `0xffffffa4` .3+<| `DATA` <|`7:0` ^| r/w <| receive/transmit data
<|`31:8` ^| r/- <| _reserved_, read as zero
<|`31:0` ^| -/w <| **simulation data output**
@ -158,9 +159,9 @@ Both file are created in the simulation's home folder.
| | `uart1_rxd_i` | serial receiver input
| | `uart1_rts_o` | flow control: RX ready to receive, low-active
| | `uart1_cts_i` | flow control: RX ready to receive, low-active
| Configuration generics: | _IO_UART1_EN_ | implement UART1 when _true_
| | _UART1_RX_FIFO_ | RX FIFO depth (power of 2, min 1)
| | _UART1_TX_FIFO_ | TX FIFO depth (power of 2, min 1)
| Configuration generics: | `IO_UART1_EN` | implement UART1 when `true`
| | `UART1_RX_FIFO` | RX FIFO depth (power of 2, min 1)
| | `UART1_TX_FIFO` | TX FIFO depth (power of 2, min 1)
| CPU interrupts: | fast IRQ channel 4 | RX interrupt
| | fast IRQ channel 5 | TX interrupt (see <<_processor_interrupts>>)
|=======================

View file

@ -9,7 +9,7 @@
| Software driver file(s): | neorv32_wdt.c |
| | neorv32_wdt.h |
| Top entity port: | none |
| Configuration generics: | _IO_WDT_EN_ | implement watchdog when _true_
| Configuration generics: | `IO_WDT_EN` | implement watchdog when `true`
| CPU interrupts: | fast IRQ channel 0 | watchdog timeout (see <<_processor_interrupts>>)
|=======================
@ -25,54 +25,55 @@ program every now and then to prevent a timeout.
**Configuration**
The watchdog is enabled by setting the control register's `WDT_CTRL_EN_ bit. When this bit is cleared, the internal
The watchdog is enabled by setting the control register's `WDT_CTRL_EN` bit. When this bit is cleared, the internal
timeout counter is reset to zero and no interrupt and no system reset can be triggered.
The internal 32-bit timeout counter is clocked at 1/4096th of the processor's main clock (f~WDT~[Hz] = f~main~[Hz] / 4096).
Whenever this counter reaches the programmed timeout value (_WDT_CTRL_TIMEOUT_ bits in the control register) a
Whenever this counter reaches the programmed timeout value (`WDT_CTRL_TIMEOUT` bits in the control register) a
hardware reset is triggered. In order to inform the application of an imminent timeout, an optional CPU interrupt is
triggered when the timeout counter reaches **half** of the programmed timeout value.
triggered when the timeout counter reaches _half_ of the programmed timeout value.
The watchdog is "fed" by writing `1` to the _WDT_CTRL_RESET_ control register bit, which
The watchdog is "fed" by writing `1` to the `WDT_CTRL_RESET` control register bit, which
will reset the internal timeout counter back to zero.
.Forced Reset
[NOTE]
Writing all-zero to the _WDT_CTRL_TIMEOUT_ bits will immediately trigger a system-wide reset.
Writing all-zero to the `WDT_CTRL_TIMEOUT` bits will immediately trigger a system-wide reset.
.Watchdog Interrupt
[NOTE]
A watchdog interrupt occurs when the watchdog is enabled and the internal counter reaches _exactly_ half of the programmed
timeout value. Hence, the interrupt only fires once. Howeer, a triggered WDT interrupt has to be explicitly cleared by
timeout value. Hence, the interrupt only fires once. However, a triggered WDT interrupt has to be explicitly cleared by
writing zero to the according <<_mip>> CSR bit.
.Watchdog Operation during Debugging
[IMPORTANT]
By default the watchdog stops operation when the CPU enters debug mode and will resume normal operation after
By default, the watchdog stops operation when the CPU enters debug mode and will resume normal operation after
the CPU has left debug mode again. This will prevent an unintended watchdog timeout during a debug session. However,
the watchdog can also be configured to keep operating even when the CPU is in debug mode by setting the control
register's _WDT_CTRL_DBEN_ bit.
register's `WDT_CTRL_DBEN` bit.
.Watchdog Operation during CPU Sleep
[IMPORTANT]
By default the watchdog stops operating when the CPU enters sleep mode. However, the watchdog can also be configured
to keep operating even when the CPU is in sleep mode by setting the control register's _WDT_CTRL_SEN_ bit.
By default, the watchdog stops operating when the CPU enters sleep mode. However, the watchdog can also be configured
to keep operating even when the CPU is in sleep mode by setting the control register's `WDT_CTRL_SEN` bit.
**Configuration Lock**
The watchdog control register can be locked to protect the current configuration from being modified. The lock is
activated by setting the _WDT_CTRL_LOCK_ bit. In the locked state any write access to the control register is entirely
The watchdog control register can be _locked_ to protect the current configuration from being modified. The lock is
activated by setting the `WDT_CTRL_LOCK` bit. In the locked state any write access to the control register is entirely
ignored (see table below, "writable if locked"). Read accesses to the control register as well as watchdog resets
(by setting the _WDT_CTRL_RESET_ flag) are not affected.
(by setting the `WDT_CTRL_RESET` flag) are not affected.
The lock bit can only be set if the WDT is already enabled (_WDT_CTRL_EN_ is set).
The lock bit can only be cleared again by a system-wide hardware reset.
The lock bit can only be set if the WDT is already enabled (`WDT_CTRL_EN` is set). Furthermore, the lock bit can
only be cleared again by a system-wide hardware reset.
**Cause of last Hardware Reset**
The cause of the last system hardware reset can be determined via the _WDT_CTRL_RCAUSE_ flag. If this flag is
zero, the processor has been reset via the external reset signal (or the on-chip debugger). If this flag is set,
The cause of the last system hardware reset can be determined via the `WDT_CTRL_RCAUSE` flag. If this flag is
cleared, the processor has been reset via the external reset signal (or the on-chip debugger). If this flag is set,
the last system reset was caused by the watchdog itself.
@ -83,12 +84,12 @@ the last system reset was caused by the watchdog itself.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Reset value | Writable if locked | Function
.8+<| `0xffffffbc` .8+<| `CTRL` <|`0` _WDT_CTRL_EN_ ^| r/w ^| `0` ^| no <| watchdog enable
<|`1 _WDT_CTRL_LOCK_ ^| r/w ^| `0` ^| no <| lock configuration when set, clears only on system reset, can only be set if enable bit is set already
<|`2` _WDT_CTRL_DBEN_ ^| r/w ^| `0` ^| no <| set to allow WDT to continue operation even when CPU is in debug mode
<|`3` _WDT_CTRL_SEN_ ^| r/w ^| `0` ^| no <| set to allow WDT to continue operation even when CPU is in sleep mode
<|`4` _WDT_CTRL_RESET_ ^| -/w ^| - ^| yes <| reset watchdog when set, auto-clears
<|`5` _WDT_CTRL_RCAUSE_ ^| r/- ^| `0` ^| - <| cause of last system reset: `0`=caused by external reset signal, `1`=caused by watchdog
.8+<| `0xffffffbc` .8+<| `CTRL` <|`0` `WDT_CTRL_EN` ^| r/w ^| `0` ^| no <| watchdog enable
<|`1 `WDT_CTRL_LOCK` ^| r/w ^| `0` ^| no <| lock configuration when set, clears only on system reset, can only be set if enable bit is set already
<|`2` `WDT_CTRL_DBEN` ^| r/w ^| `0` ^| no <| set to allow WDT to continue operation even when CPU is in debug mode
<|`3` `WDT_CTRL_SEN` ^| r/w ^| `0` ^| no <| set to allow WDT to continue operation even when CPU is in sleep mode
<|`4` `WDT_CTRL_RESET` ^| -/w ^| - ^| yes <| reset watchdog when set, auto-clears
<|`5` `WDT_CTRL_RCAUSE` ^| r/- ^| `0` ^| - <| cause of last system reset: `0`=caused by external reset signal, `1`=caused by watchdog
<|`7:6` - ^| r/- ^| - ^| - <| _reserved_, reads as zero
<|`31:8` _WDT_CTRL_TIMEOUT_MSB_ : _WDT_CTRL_TIMEOUT_LSB_ ^| r/w ^| 0 ^| no <| 24-bit watchdog timeout value
<|`31:8` `WDT_CTRL_TIMEOUT_MSB : WDT_CTRL_TIMEOUT_LSB` ^| r/w ^| 0 ^| no <| 24-bit watchdog timeout value
|=======================

View file

@ -1,6 +1,6 @@
<<<
:sectnums:
==== Processor-External Memory Interface (WISHBONE) (AXI4-Lite)
==== Processor-External Memory Interface (WISHBONE)
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
@ -19,28 +19,28 @@
| | `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_
| | _MEM_EXT_ASYNC_TX_ | use registered TX path when _false_ (default); use async/direct TX path when _true_
| 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`
| | `MEM_EXT_ASYNC_TX_` | use registered TX path when `false` (default); use async/direct TX path when `true`
| CPU interrupts: | none |
|=======================
The external memory interface provides a Wishbone b4-compatible on-chip bus interface. The bus interface is
implemented if the _MEM_EXT_EN_ generic is _true_. This interface can be used to attach external memories,
implemented if 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 to the processor.
The external interface is _not_ mapped to a _specific_ address space region. Instead, all CPU memory accesses that
The external interface is not mapped to a specific address space. Instead, all CPU memory accesses that
do not target a processor-internal module are delegated to the external memory interface. In summary, a CPU load/store
access is delegated via the external bus interface if...
* it does not target the internal instruction memory IMEM (if implemented at all).
* **and** it does not target the internal data memory DMEM (if implemented at all).
* **and** it does not target the internal bootloader ROM or any of the IO devices - regardless if one or more of these components are
actually implemented or not.
* the access does not target the internal instruction memory IMEM (if implemented at all)
* **and** the access does not target the internal data memory DMEM (if implemented at all)
* **and** the access does not target the internal bootloader ROM or any of the internal modules - regardless
if one or more of these components are actually implemented or not.
.Address Space Layout
[TIP]
@ -48,18 +48,18 @@ See section <<_address_space>> for more information.
.Execute-in-Place Module
[NOTE]
If the Execute In Place module (XIP) is implemented accesses targeting the XIP memory-mapped-region will not be forwarded to the
If the Execute In Place module (XIP) is implemented, accesses targeting the XIP memory-mapped-region will not be forwarded to the
external memory interface. See section <<_execute_in_place_module_xip>> for more information.
**Wishbone Bus Protocol**
The external memory interface either uses the **standard** (also called "classic") Wishbone protocol (default) or
**pipelined** Wishbone protocol. The protocol to be used is configured via the <<_mem_ext_pipe_mode>> generic:
**pipelined** Wishbone protocol. The protocol to be used is configured via the `MEM_EXT_PIPE_MODE` generic:
* If _MEM_EXT_PIPE_MODE_ is _false_, all bus control signals including `wb_stb_o` are active and remain stable until the
* If `MEM_EXT_PIPE_MODE` is `false`, all bus control signals including `wb_stb_o` are active and remain stable until the
transfer is acknowledged/terminated.
* If _MEM_EXT_PIPE_MODE_ is _true_, all bus control except `wb_stb_o` are active and remain until the transfer is
* If `MEM_EXT_PIPE_MODE` is `true`, all bus control except `wb_stb_o` are active and remain until the transfer is
acknowledged/terminated. In this case, `wb_stb_o` is asserted only during the very first bus clock cycle.
.Exemplary Wishbone bus accesses using "classic" and "pipelined" protocol
@ -81,33 +81,22 @@ project.
**Bus Access**
The NEORV32 Wishbone gateway does not support burst transfer yet, so there is always just a single transfer in "in fly".
The NEORV32 Wishbone gateway does not support burst transfer yet, so there is always just a single transfer "in fly".
Hence, the Wishbone `STALL` signal is not implemented. An accessed Wishbone device does not have to respond immediately to a bus
request by sending an ACK. Instead, there is a _time window_ where the device has to acknowledge the transfer. This time window
id configured by the _MEM_EXT_TIMEOUT_ top generic that defines the maximum time (in clock cycles) a bus access can be pending
before it is automatically terminated with an error condition. If _MEM_EXT_TIMEOUT_ is set to zero, the timeout disabled
an a bus access can take an arbitrary number of cycles to complete (this is **not recommended**!).
s configured by the `MEM_EXT_TIMEOUT` generic that defines the maximum time (in clock cycles) a bus access can be pending
before it is automatically terminated with an error condition. If `MEM_EXT_TIMEOUT` is set to zero, the timeout is disabled
and a bus access can take an arbitrary number of cycles to complete (this is not recommended!).
When _MEM_EXT_TIMEOUT_ is greater than zero, the Wishbone gateway starts an internal countdown whenever the CPU
When `MEM_EXT_TIMEOUT` is greater than zero, the Wishbone gateway starts an internal countdown whenever the CPU
accesses an address via the external memory interface. If the accessed 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
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 CPU load/store/instruction fetch bus access fault exception is raised.
.External "Address Space Holes"
[IMPORTANT]
Setting _MEM_EXT_TIMEOUT_ to zero will permanently stall the CPU if the targeted Wishbone device never responds. Hence,
_MEM_EXT_TIMEOUT_ should be always set to a value greater than zero. +
+
This feature can be used as **safety guard** if the external memory system does not check for "address space holes". That means
that accessing 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 acknowledge **any** bus accesses
(even if targeting an unimplemented address) the timeout feature can be safely 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.
The 3-bit wishbone `wb_tag_o` signal provides additional information regarding the access type:
* `wb_tag_o(0)` 1: privileged access (CPU is in machine mode); 0: unprivileged access (CPU is not in machine mode)
* `wb_tag_o(1)` always zero (indicating "secure access")
@ -117,8 +106,8 @@ is compatible to the AXI4 `AxPROT` signal.
**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.
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.
Application software can check the Endianness configuration of the external bus interface via the
@ -133,7 +122,7 @@ via Wishbone requires 2 additional clock cycles. This can ease timing closure wh
interconnection networks.
Optionally, the latency of the Wishbone gateway can be reduced by removing the input and output register stages.
Enabling the _MEM_EXT_ASYNC_RX_ option will remove the input register stage; enabling _MEM_EXT_ASYNC_TX_ option will
Enabling the `MEM_EXT_ASYNC_RX` option will remove the input register stage; enabling `MEM_EXT_ASYNC_TX` option will
remove the output register stages. Each enabled option reduces access latency by 1 cycle.
.Output Gating
@ -141,17 +130,4 @@ remove the output register stages. Each enabled option reduces access latency by
All outgoing Wishbone signals use a "gating mechanism" so they only change if there is a actual Wishbone transaction being in
progress. This can reduce dynamic switching activity in the external bus system and also simplifies simulation-based
inspection of the Wishbone transactions. Note that this output gating is only available if the output register buffer is not
disabled (_MEM_EXT_ASYNC_TX_ = _false_).
**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. See The USer Guide for more
information: https://stnolting.github.io/neorv32/ug/#_packaging_the_processor_as_ip_block_for_xilinx_vivado_block_designer
[WARNING]
Using the auto-termination timeout feature (_MEM_EXT_TIMEOUT_ greater than zero) is **not AXI4 compliant** as
the AXI protocol does not support "aborting" bus transactions. Therefore, the NEORV32 top wrapper with AXI4-Lite interface
(`rtl/system_integration/neorv32_SystemTop_axi4lite`) configures _MEM_EXT_TIMEOUT_ = 0 by default.
disabled (`MEM_EXT_ASYNC_TX` = `false`).

View file

@ -12,18 +12,17 @@
| | `xip_clk_o` | 1-bit serial clock output
| | `xip_dat_i` | 1-bit serial data input
| | `xip_dat_o` | 1-bit serial data output
| Configuration generics: | _IO_XIP_EN_ | implement XIP module when _true_
| Configuration generics: | `IO_XIP_EN` | implement XIP module when `true`
| CPU interrupts: | none |
|=======================
**Overview**
The execute in place (XIP) module is probably one of the more complicated modules of the NEORV32. The module
allows to execute code (and read constant data) directly from a SPI flash memory. Hence, it uses the standard
serial peripheral interface (SPI) as transfer protocol under the hood.
The execute in-place (XIP) module allows to execute code (and read constant data) directly from a SPI flash memory.
Hence, it uses the standard serial peripheral interface (SPI) as transfer protocol under the hood.
The XIP flash is not mapped to a specific region of the processor's address space. Instead, the XIP module
The XIP flash is not mapped to a specific region of the processor's address space. Instead, it
provides a programmable mapping scheme to allow a flexible user-defined mapping of the flash to _any section_
of the address space.
@ -34,26 +33,13 @@ Note that this interface is read-only. Any write access will raise a bus error e
The second interface is mapped to the processor's IO space and allows data accesses to the XIP module's
configuration registers.
.Example Program
[TIP]
An example program for the XIP module is available in `sw/example/demo_xip`.
.Compiling a Program for XIP Execution
[NOTE]
If you want to compile a program that shall be executed using the XIP module, the default NEORV32 linker script
(`sw/common/neorv32.ld`) has to be modified: the `ORIGIN` attribute of the `rom` section needs to be adapted to
the XIP page base address and the flash base address. For example if the XIP page is set to `0x20000000` and the
executable is placed in the flash as offset `0x00400000` the `ORIGIN` attribute has to be set to the sum of both
address offsets (`0x20000000 + 0x00400000 = 0x20400000` -> `rom (rx) : ORIGIN = DEFINED(make_bootloader) ? 0xFFFF0000 : 0x20400000`).
See sections <<_linker_script>>, <<_application_makefile>> and <<_executable_image_generator>> for more information.
**SPI Protocol**
The XIP module accesses external flash using the standard SPI protocol. The module always sends data MSB-first and
provides all of the standard four clock modes (0..3), which are configured via the _XIP_CTRL_CPOL_ (clock polarity)
and _XIP_CTRL_CPHA_ (clock phase) control register bits, respectively. The clock speed of the interface (`xip_clk_o`)
is defined by a three-bit clock pre-scaler configured using the _XIP_CTRL_PRSCx_ bits:
provides all of the standard four clock modes (0..3), which are configured via the `XIP_CTRL_CPOL` (clock polarity)
and `XIP_CTRL_CPHA` (clock phase) control register bits, respectively. The clock speed of the interface (`xip_clk_o`)
is defined by a three-bit clock pre-scaler configured using the `XIP_CTRL_PRSCx` bits:
.XIP prescaler configuration
[cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"]
@ -63,7 +49,7 @@ is defined by a three-bit clock pre-scaler configured using the _XIP_CTRL_PRSCx_
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096
|=======================
Based on the _XIP_CTRL_PRSCx_ configuration the actual XIP SPI clock frequency f~XIP~ is derived from the processor's
Based on the clock configuration the actual XIP SPI clock frequency f~XIP~ is derived from the processor's
main clock f~main~ and is determined by:
_**f~XIP~**_ = _f~main~[Hz]_ / (2 * `clock_prescaler`)
@ -72,36 +58,36 @@ Hence, the maximum XIP clock speed is f~main~ / 4.
.High-Speed SPI mode
[TIP]
The module provides a "high-speed" SPI mode. In this mode the clock prescaler configuration (_XIP_CTRL_PRSCx_) is ignored
The module provides a "high-speed" SPI mode. In this mode the clock prescaler configuration (`XIP_CTRL_PRSCx`) is ignored
and the SPI clock operates at f~main~ / 2 (half of the processor's main clock). High speed SPI mode is enabled by setting
the control register's _XIP_CTRL_HIGHSPEED_ bit.
the control register's `XIP_CTRL_HIGHSPEED` bit.
The flash's "read command", which initiates a read access, is defined by the _XIP_CTRL_RD_CMD_ control register bits.
The flash's "read command", which initiates a read access, is defined by the `XIP_CTRL_RD_CMD` control register bits.
For most SPI flash memories this is `0x03` for normal SPI mode.
**Direct SPI Access**
The XIP module allows to initiate _direct_ SPI transactions. This feature can be used to configure the attached SPI
flash or to perform direct read and write accesses to the flash memory. Two data registers `NEORV32_XIP.DATA_LO` and
`NEORV32_XIP.DATA_HI` are provided to send up to 64-bit of SPI data. The `NEORV32_XIP.DATA_HI` register is write-only,
so a total of 32-bit receive data is provided. Note that the module handles the chip-select
flash or to perform direct read and write accesses to the flash memory. Two data registers `DATA_LO` and
`DATA_HI` are provided to send up to 64-bit of SPI data. The `DATA_HI` register is write-only,
so a total of just 32-bits of receive data is provided. Note that the module handles the chip-select
line (`xip_csn_o`) by itself so it is not possible to construct larger consecutive transfers.
The actual data transmission size in bytes is defined by the control register's _XIP_CTRL_SPI_NBYTES_ bits.
The actual data transmission size in bytes is defined by the control register's `XIP_CTRL_SPI_NBYTES` bits.
Any configuration from 1 byte to 8 bytes is valid. Other value will result in unpredictable behavior.
Since data is always transferred MSB-first, the data in `DATA_HI:DATA_LO` also has to be MSB-aligned. Receive data is
available in `DATA_LO` only - `DATA_HI` is write-only. Writing to `DATA_HI` triggers the actual SPI transmission.
The _XIP_CTRL_PHY_BUSY_ control register flag indicates a transmission being in progress.
available in `DATA_LO` only since `DATA_HI` is write-only. Writing to `DATA_HI` triggers the actual SPI transmission.
The `XIP_CTRL_PHY_BUSY` control register flag indicates a transmission being in progress.
The chip-select line of the XIP module (`xip_csn_o`) will only become asserted (enabled, pulled low) if the
_XIP_CTRL_SPI_CSEN_ control register bit is set. If this bit is cleared, `xip_csn_o` is always disabled
`XIP_CTRL_SPI_CSEN` control register bit is set. If this bit is cleared, `xip_csn_o` is always disabled
(pulled high).
[NOTE]
Direct SPI mode is only possible when the module is enabled (setting _XIP_CTRL_EN_) but **before** the actual
XIP mode is enabled via _XIP_CTRL_XIP_EN_.
Direct SPI mode is only possible when the module is enabled (setting `XIP_CTRL_EN`) but **before** the actual
XIP mode is enabled via `XIP_CTRL_XIP_EN`.
[TIP]
When the XIP mode is not enabled, the XIP module can also be used as additional general purpose SPI controller
@ -113,28 +99,16 @@ with a transfer size of up to 64 bits per transmission.
The address mapping of the XIP flash is not fixed by design. It can be mapped to _any section_ within the processor's
address space. A _section_ refers to one out of 16 naturally aligned 256MB wide memory segments. This segment
is defined by the four most significant bits of the address (`31:28`) and the XIP's segment is programmed by the
four _XIP_CTRL_XIP_PAGE_ bits in the unit's control register. All accesses within this page will be mapped to the XIP flash.
four `XIP_CTRL_XIP_PAGE` bits in the unit's control register. All accesses within this page will be mapped to the XIP flash.
[NOTE]
Care must be taken when programming the page mapping to prevent access collisions with other modules (like internal memories
or modules attached to the external memory interface).
Example: to map the XIP flash to the address space starting at `0x20000000` write a "2" (`0b0010`) to the _XIP_CTRL_XIP_PAGE_
control register bits. Any access within `0x20000000 .. 0x2fffffff` will be forwarded to the XIP flash.
Note that the SPI access address might wrap around.
.Using the FPGA Bitstream Flash also for XIP
[TIP]
You can also use the FPGA's bitstream SPI flash for storing XIP programs. To prevent overriding the bitstream,
a certain offset needs to be added to the executable (which might require linker script modifications).
To execute the program stored in the SPI flash simply jump to the according base address. For example
if the executable starts at flash offset `0x8000` and the XIP flash is mapped to the base address `0x20000000`
then add the offset to the base address and use that as jump/call destination (=`0x20008000`).
**Using the XIP Mode**
The XIP module is globally enabled by setting the _XIP_CTRL_EN_ bit in the device's `CTRL` control register.
The XIP module is globally enabled by setting the `XIP_CTRL_EN` bit in the device's `CTRL` control register.
Clearing this bit will reset the whole module and will also terminate any pending SPI transfer.
Since there is a wide variety of SPI flash components with different sizes, the XIP module allows to specify
@ -145,11 +119,11 @@ address bytes (**minus one**). For example for a SPI flash with 24-bit addresses
The transparent XIP accesses are transformed into SPI transmissions with the following format (starting with the MSB):
* 8-bit command: configured by the _XIP_CTRL_RD_CMD_ control register bits ("SPI read command")
* 8 to 32 bits address: defined by the _XIP_CTRL_XIP_ABYTES_ control register bits ("number of address bytes")
* 8-bit command: configured by the `XIP_CTRL_RD_CMD` control register bits ("SPI read command")
* 8 to 32 bits address: defined by the `XIP_CTRL_XIP_ABYTES` control register bits ("number of address bytes")
* 32-bit data: sending zeros and receiving the according flash word (32-bit)
Hence, the maximum XIP transmission size is 72-bit, which has to be configured via the _XIP_CTRL_SPI_NBYTES_
Hence, the maximum XIP transmission size is 72-bit, which has to be configured via the `XIP_CTRL_SPI_NBYTES`
control register bits. Note that the 72-bit transmission size is only available in XIP mode. The transmission
size of the direct SPI accesses is limited to 64-bit.
@ -166,8 +140,8 @@ The XIP mode requires the 4-byte data words in the flash to be ordered in **litt
After the SPI properties (including the amount of address bytes **and** the total amount of SPI transfer bytes)
and XIP address mapping are configured, the actual XIP mode can be enabled by setting
the control register's _XIP_CTRL_XIP_EN_ bit. This will enable the "transparent SPI access port" of the module and thus,
the _transparent_ conversion of access requests into proper SPI flash transmissions. Make sure _XIP_CTRL_SPI_CSEN_
the control register's `XIP_CTRL_XIP_EN` bit. This will enable the "transparent SPI access port" of the module and thus,
the _transparent_ conversion of access requests into proper SPI flash transmissions. Make sure `XIP_CTRL_SPI_CSEN`
is also set so the module can actually select/enable the attached SPI flash.
No more direct SPI accesses via `DATA_HI:DATA_LO` are possible when the XIP mode is enabled. However, the
XIP mode can be disabled at any time.
@ -190,7 +164,7 @@ data bytes. Obviously, this introduces a certain transmission overhead. To reduc
the flash's _incrmental read_ function, which will return consecutive bytes when continuing to send clock cycles after a read command.
Hence, the XIP module provides an optional "burst mode" to accelerate consecutive read accesses.
The XIP burst mode is enabled by setting the _XIP_CTRL_BURST_EN_ bit in the module's control register. The burst mode only affects
The XIP burst mode is enabled by setting the `XIP_CTRL_BURST_EN` bit in the module's control register. The burst mode only affects
the actual XIP mode and _not_ the direct SPI mode. Hence, it should be enabled right before enabling XIP mode only.
By using the XIP burst mode flash read accesses can be accelerated by up to 50%.
@ -202,23 +176,21 @@ By using the XIP burst mode flash read accesses can be accelerated by up to 50%.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.17+<| `0xffffff40` .17+<| `CTRL` <|`0` _XIP_CTRL_EN_ ^| r/w <| XIP module enable
<|`1` _XIP_CTRL_PRSC0_ ^| r/w .3+| 3-bit SPI clock prescaler select
<|`2` _XIP_CTRL_PRSC1_ ^| r/w
<|`3` _XIP_CTRL_PRSC2_ ^| r/w
<|`4` _XIP_CTRL_CPOL_ ^| r/w <| SPI clock polarity
<|`5` _XIP_CTRL_CPHA_ ^| r/w <| SPI clock phase
<|`9:6` _XIP_CTRL_SPI_NBYTES_MSB_ : _XIP_CTRL_SPI_NBYTES_LSB_ ^| r/w <| Number of bytes in SPI transaction (1..9)
<|`10` _XIP_CTRL_XIP_EN_ ^| r/w <| XIP mode enable
<|`12:11` _XIP_CTRL_XIP_ABYTES_MSB_ : _XIP_CTRL_XIP_ABYTES_LSB_ ^| r/w <| Number of address bytes for XIP flash (minus 1)
<|`20:13` _XIP_CTRL_RD_CMD_MSB_ : _XIP_CTRL_RD_CMD_LSB_ ^| r/w <| Flash read command
<|`24:21` _XIP_CTRL_XIP_PAGE_MSB_ : _XIP_CTRL_XIP_PAGE_LSB_ ^| r/w <| XIP memory page
<|`25` _XIP_CTRL_SPI_CSEN_ ^| r/w <| Allow SPI chip-select to be actually asserted when set
<|`26` _XIP_CTRL_HIGHSPEED_ ^| r/w <| enable SPI high-speed mode (ignoring _XIP_CTRL_PRSC_)
<|`27` _XIP_CTRL_BURST_EN_ ^| r/w <| Enable XIP burst mode
<|`29:28` ^| r/- <| _reserved_, read as zero
<|`30` _XIP_CTRL_PHY_BUSY_ ^| r/- <| SPI PHY busy when set
<|`31` _XIP_CTRL_XIP_BUSY_ ^| r/- <| XIP access in progress when set
.15+<| `0xffffff40` .15+<| `CTRL` <|`0` `XIP_CTRL_EN` ^| r/w <| XIP module enable
<|`3:1` `XIP_CTRL_PRSC2 : XIP_CTRL_PRSC0` ^| r/w <| 3-bit SPI clock prescaler select
<|`4` `XIP_CTRL_CPOL` ^| r/w <| SPI clock polarity
<|`5` `XIP_CTRL_CPHA` ^| r/w <| SPI clock phase
<|`9:6` `XIP_CTRL_SPI_NBYTES_MSB : XIP_CTRL_SPI_NBYTES_LSB` ^| r/w <| Number of bytes in SPI transaction (1..9)
<|`10` `XIP_CTRL_XIP_EN` ^| r/w <| XIP mode enable
<|`12:11` `XIP_CTRL_XIP_ABYTES_MSB : XIP_CTRL_XIP_ABYTES_LSB` ^| r/w <| Number of address bytes for XIP flash (minus 1)
<|`20:13` `XIP_CTRL_RD_CMD_MSB : XIP_CTRL_RD_CMD_LSB` ^| r/w <| Flash read command
<|`24:21` `XIP_CTRL_XIP_PAGE_MSB : XIP_CTRL_XIP_PAGE_LSB` ^| r/w <| XIP memory page
<|`25` `XIP_CTRL_SPI_CSEN` ^| r/w <| Allow SPI chip-select to be actually asserted when set
<|`26` `XIP_CTRL_HIGHSPEED` ^| r/w <| enable SPI high-speed mode (ignoring _XIP_CTRL_PRSC_)
<|`27` `XIP_CTRL_BURST_EN` ^| r/w <| Enable XIP burst mode
<|`29:28` - ^| r/- <| _reserved_, read as zero
<|`30` `XIP_CTRL_PHY_BUSY` ^| r/- <| SPI PHY busy when set
<|`31` `XIP_CTRL_XIP_BUSY` ^| r/- <| XIP access in progress when set
| `0xffffff44` | _reserved_ |`31:0` | r/- | _reserved_, read as zero
| `0xffffff48` | `DATA_LO` |`31:0` | r/w | Direct SPI access - data register low
| `0xffffff4C` | `DATA_HI` |`31:0` | -/w | Direct SPI access - data register high; write access triggers SPI transfer

View file

@ -6,31 +6,38 @@
[frame="topbot",grid="none"]
|=======================
| Hardware source file(s): | neorv32_xirq.vhd |
| Software driver file(s): | neorv32_xirq.c |
| | neorv32_xirq.h |
| Top entity port: | `xirq_i` | IRQ input (32-bit, fixed)
| 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>>)
| Software driver file(s): | neorv32_xirq.c |
| | neorv32_xirq.h |
| Top entity port: | `xirq_i` | External interrupts input (32-bit)
| Configuration generics: | `XIRQ_NUM_CH` | Number of external IRQ channels 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
**Overview**
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_.
_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. If less than 32 channels are configure, only the LSB-aligned channels
are used while the remaining bits are left unconnected. An interrupt channel is enabled by setting the according bit in the
interrupt enable register `IER`.
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. If less than 32 channels are configured, only the LSB-aligned channels
are used while the remaining bits are left unconnected.
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`
The actual interrupt trigger (low-level, high-level, rising-edge, falling-edge) can be configured independently for each channel
using the `XIRQ_TRIGGER_TYPE` and `XIRQ_TRIGGER_POLARITY` generics. `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 selected trigger: a `0` defines low-level or falling-edge and a `1` defines high-level or rising-edge trigger polarity.
Each channel can be independently enabled or disabled via the `IER` register. 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.
[NOTE]
@ -39,7 +46,7 @@ A disabled interrupt channel can still be pending if it has been triggered befor
The CPU can determine active external 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.
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`.
@ -53,25 +60,6 @@ An interrupt handler should clear the interrupt pending bit that caused the inte
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.
.Example trigger configuration: channel 0 for rising-edge, IRQ channels 1 to 31 for high-level
[source, vhdl]
----
XIRQ_TRIGGER_TYPE => x"00000001";
XIRQ_TRIGGER_POLARITY => x"ffffffff";
----
**Register Map**
.XIRQ register map (`struct NEORV32_XIRQ`)

View file

@ -1,8 +1,8 @@
:sectnums:
== Software Framework
To make actual use of the NEORV32 processor, the project comes with a complete software ecosystem. This
ecosystem is based on the RISC-V port of the GCC GNU Compiler Collection and consists of the following elementary parts:
The NEORV32 project comes with a complete software ecosystem called the "software framework", which
is based on the C-language RISC-V GCC port and consists of the following parts:
* <<_compiler_toolchain>>
* <<_core_libraries>>
@ -18,7 +18,7 @@ ecosystem is based on the RISC-V port of the GCC GNU Compiler Collection and con
A summarizing list of the most important elements of the software framework and their according
files and folders is shown below:
[cols="<6,<4"]
[cols="<5,<5"]
[grid="none"]
|=======================
| Application start-up code | `sw/common/crt0.S`
@ -26,7 +26,7 @@ files and folders is shown below:
| Core hardware driver libraries ("HAL") | `sw/lib/include/` & `sw/lib/source/`
| Central application makefile | `sw/common/common.mk`
| Tool for generating NEORV32 executables | `sw/image_gen/`
| Default bootloader | `sw/bootloader/bootloader.c`
| Default bootloader | `sw/bootloader`
| Example programs | `sw/example`
|=======================
@ -46,7 +46,7 @@ and peripheral/IO modules, can be found in `sw/example`.
:sectnums:
=== Compiler Toolchain
The toolchain for this project is based on the free RISC-V GCC-port. You can find the compiler sources and
The toolchain for this project is based on the free and open RISC-V GCC-port. You can find the compiler sources and
build instructions on the official RISC-V GNU toolchain GitHub page: https://github.com/riscv/riscv-gnutoolchain.
The NEORV32 implements a 32-bit RISC-V architecture and uses a 32-bit integer and soft-float ABI by default.
@ -56,11 +56,11 @@ Make sure the toolchain / toolchain build is configured accordingly.
* `MABI=ilp32`
* `RISCV_PREFIX=riscv32-unknown-elf-`
These default configurations can be override at any times using <<_application_makefile>> variables.
These default configurations can be overridden at any times using <<_application_makefile>> variables.
[TIP]
More information regarding the toolchain (building from scratch or downloading the prebuilt ones)
can be found in the user guides' section https://stnolting.github.io/neorv32/ug/#_software_toolchain_setup[Software Toolchain Setup].
More information regarding the toolchain (building from scratch or downloading prebuilt ones) can be found in the
user guide section https://stnolting.github.io/neorv32/ug/#_software_toolchain_setup[Software Toolchain Setup].
<<<
@ -77,10 +77,11 @@ The NEORV32 project provides a set of pre-defined C libraries that allow an easy
#include <neorv32.h> // NEORV32 HAL, core and runtime libraries
----
[cols="<3,<4,<8"]
.NEORV32 HAL File List
[cols="<3,<3,<6"]
[options="header",grid="rows"]
|=======================
| C source file | C header file | Description
| C source file | C header file | Description
| - | `neorv32.h` | main NEORV32 definitions and library file
| - | `neorv32_buskeeper` | HW driver functions for the bus keeper
| `neorv32_cfs.c` | `neorv32_cfs.h` | HW driver (stubs) functions for custom functions subsystem
@ -94,7 +95,7 @@ The NEORV32 project provides a set of pre-defined C libraries that allow an easy
| `neorv32_neoled.c` | `neorv32_neoled.h` | HW driver functions for the smart LED interface
| `neorv32_onewire.c` | `neorv32_onewire.h` | HW driver functions for the 1-wire interface
| `neorv32_pwm.c` | `neorv32_pwm.h` | HW driver functions for the pulse-width modulation controller
| `neorv32_rte.c` | `neorv32_rte.h` | NEORV32 runtime environment and helper functions
| `neorv32_rte.c` | `neorv32_rte.h` | <<_neorv32_runtime_environment>>
| `neorv32_sdi.c` | `neorv32_sdi.h` | HW driver functions for the serial data interface
| `neorv32_spi.c` | `neorv32_spi.h` | HW driver functions for the serial peripheral interface
| - | `neorv32_sysinfo.h` | HW driver functions for the system information memory
@ -123,8 +124,8 @@ A CMSIS-SVD-compatible **System View Description (SVD)** file including all peri
:sectnums:
=== Application Makefile
Application compilation is based on a single, centralized **GNU makefile** (`sw/common/common.mk`). Each project in the
`sw/example` folder provides a makefile that just includes this central makefile.
Application compilation is based on a single, centralized GNU makefile (`sw/common/common.mk`). Each project in the
`sw/example` folder provides a makefile that just _includes_ this central makefile.
[TIP]
When creating a new project, copy an existing project folder or at least the makefile to the new project folder.
@ -133,18 +134,12 @@ dependencies can be manually configured via makefile variables if the new projec
[NOTE]
Before the makefile can be used to compile applications, the RISC-V GCC toolchain needs to be installed and
the compiler's `bin` folder has to be added to the system's `PATH` variable. More information can be found in
https://stnolting.github.io/neorv32/ug/#_software_toolchain_setup[User Guide: Software Toolchain Setup].
the compiler's `bin` folder has to be added to the system's `PATH` environment variable. More information can be
found in https://stnolting.github.io/neorv32/ug/#_software_toolchain_setup[User Guide: Software Toolchain Setup].
The makefile is invoked by simply executing `make` in the console. For example:
[source,bash]
----
neorv32/sw/example/demo_blink_led$ make
----
:sectnums:
==== Targets
==== Makefile Targets
Just executing `make` (or executing `make help`) will show the help menu listing all available targets.
@ -185,16 +180,15 @@ Make sure to add the bin folder of RISC-V GCC to your PATH variable.
:sectnums:
==== Configuration
==== Makefile Configuration
The compilation flow is configured via variables right at the beginning of the central
makefile (`sw/common/common.mk`):
.Customizing Makefile Variables
[TIP]
The makefile configuration variables can be overridden or extended directly when invoking the makefile. For
example `$ make MARCH=rv32ic_zicsr clean_all exe` overrides the default `MARCH` variable definitions.
Permanent modifications/definitions can be made in the project-local makefile
(e.g., `sw/example/demo_blink_led/makefile`).
.Default Makefile Configuration
[source,makefile]
@ -224,22 +218,18 @@ NEORV32_HOME ?= ../../..
----
.Variables Description
[cols="<3,<10"]
[cols="<2,<8"]
[grid="none"]
|=======================
| `APP_SRC` | The source files of the application (`*.c`, `*.cpp`, `*.S` and `*.s` files are allowed;
files of these types in the project folder are automatically added via wild cards). Additional files can be added separated by white spaces
| `APP_SRC` | The source files of the application (`*.c`, `*.cpp`, `*.S` and `*.s` files are allowed; files of these types in the project folder are automatically added via wild cards). 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`, `-Ofast`, ...
| `RISCV_PREFIX` | The toolchain prefix to be used; follows the triplet naming convention `[architecture]-[host_system]-[output]-...`
| `MARCH` | The targeted RISC-V architecture/ISA; enable compiler support of optional CPU extension by adding the according extension
name (e.g. `rv32im_zicsr` for `M` CPU extension; see https://stnolting.github.io/neorv32/ug/#_enabling_risc_v_cpu_extensions[User Guide: Enabling RISC-V CPU Extensions]
for more information
| `MARCH` | The targeted RISC-V architecture/ISA
| `MABI` | Application binary interface (default: 32-bit integer ABI `ilp32`)
| `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
default `sw/example` folder
| `NEORV32_HOME` | Relative or absolute path to the NEORV32 project home folder; adapt this if the makefile/project is not in the project's default `sw/example` folder
|=======================
:sectnums:
@ -266,12 +256,11 @@ The following default compiler flags are used for compiling an application. Thes
==== Custom (Compiler) Flags
Custom flags can be _appended_ to the `USER_FLAGS` variable. This allows to customize the entire software framework while
calling `make` without the need to change the makefile(s) or the linker script.
calling `make` without the need to change the makefile(s) or the linker script. The following example will add debug symbols
to the executable (`-g`) and will also re-define the linker script's `__neorv32_heap_size` variable setting the maximal heap
size to 4096 bytes (see sections <<_linker_script>> and <<_ram_layout>>):
The following example will add debug symbols to the executable (`-g`) and will also define the linker script's
`__neorv32_heap_size` setting the maximal heap size to 4096 bytes:
.Example: using the `USER_FLAGS` variable for customization
.Using the `USER_FLAGS` Variable for Customization
[source,bash]
----
$ make USER_FLAGS+="-g -Wl,--__neorv32_heap_size,__heap_size=4096" clean_all exe
@ -283,8 +272,8 @@ $ make USER_FLAGS+="-g -Wl,--__neorv32_heap_size,__heap_size=4096" clean_all exe
:sectnums:
=== Executable Image Format
In order to generate an executable for th processors all source files have to be compiled, linked
and packed into a _final executable_.
In order to generate an executable for the processors all source files have to be compiled, linked
and packed into a final executable.
:sectnums:
==== Linker Script
@ -326,8 +315,7 @@ __neorv32_rom_base = DEFINED(__neorv32_rom_base) ? __neorv32_rom_base : 0x000000
__neorv32_ram_base = DEFINED(__neorv32_ram_base) ? __neorv32_ram_base : 0x80000000; /* = VHDL package's "dspace_base_c" */
----
Only the region **sizes** should be modified by the user. The base addresses are defined by the processor's hardware (see section
<<_address_space>>) and should not be altered at all. The size (and base) configuration can be edited by the user - either by explicitly
The region size and base address configuration can be edited by the user - either by explicitly
changing the default values in the linker script or by overriding them when invoking `make`:
.Overriding default rom size configuration (setting 4096 bytes)
@ -337,18 +325,19 @@ $ make USER_FLAGS+="-Wl,--defsym,__neorv32_rom_size=4096" clean_all exe
----
[IMPORTANT]
`neorv32_rom_base` (= `ORIGIN` of the `ram` section) has to be always identical to the processor's `dspace_base_c` hardware configuration.
Also, `neorv32_ram_base` (= `ORIGIN` of the `rom` section) has to be always identical to the processor's `ispace_base_c` hardware configuration.
`neorv32_rom_base` (= `ORIGIN` of the `ram` section) has to be always identical to the processor's `dspace_base_c` hardware
configuration. Also, `neorv32_ram_base` (= `ORIGIN` of the `rom` section) has to be always identical to the processor's
`ispace_base_c` hardware configuration.
[NOTE]
The default configuration for the `rom` section assumes a maximum of 2GB _logical_ 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 a 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 default configuration for the `rom` section assumes a maximum of 2GB _logical_ memory address space. This size does not
have to reflect the _actual_ physical size of the entire instruction memory. It just provides a maximum limit. When uploading
a 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.
The linker maps all the regions from the compiled object files into five final sections: `.text`, `.rodata`, `.data`, `.bss` and `.heap`.
These regions contain everything required for the application to run:
The linker maps all the regions from the compiled object files into five final sections: `.text`,
`.rodata`, `.data`, `.bss` and `.heap`:
.Linker script - memory regions
[cols="<1,<9"]
@ -368,7 +357,7 @@ sections are extracted and concatenated into a single file `main.bin`.
.Section Alignment
[NOTE]
The default NEORV32 linker script aligns _all_ regions so they start and end on a 32-bit (word) boundary. The default
The default NEORV32 linker script aligns _all_ regions so they start and end on a 32-bit (word) boundaries. The default
NEORV32 start-up code (crt0) makes use of this alignment by using word-level memory instructions to initialize the `.data`
section and to clear the `.bss` section (faster!).
@ -411,8 +400,7 @@ using dynamic memory allocation.
:sectnums:
==== C Standard Library
The NEORV32 is a processor for _embedded_ applications, which is not capable of running desktop OSs like Linux
(at least not without emulation). Hence, the default software framework relies on **newlib** as default C standard library.
The default software framework relies on **newlib** as default C standard library.
.RTOS Support
[NOTE]
@ -424,9 +412,9 @@ for more information.
Newlib provides stubs for common "system calls" (like file handling and standard input/output) that are used by other
C libraries like `stdio`. These stubs are available in `sw/source/source/syscalls.c` and were adapted for the NEORV32 processor.
.Standard Console(s)
.Standard Consoles
[NOTE]
<<_primary_universal_asynchronous_receiver_and_transmitter_uart0, UART0>>
The <<_primary_universal_asynchronous_receiver_and_transmitter_uart0, UART0>>
is used to implement all the standard input, output and error consoles (`STDIN`, `STDOUT` and `STDERR`).
.Constructors and Destructors
@ -446,7 +434,7 @@ is available in `sw/example/demo_newlib`
The `main.bin` file is packed by the NEORV32 image generator (`sw/image_gen`) to generate the final executable file.
The image generator can generate several types of executables selected by a flag when calling the generator:
[cols="<1,<9"]
[cols="<2,<8"]
[grid="none"]
|=======================
| `-app_bin` | Generates an executable binary file `neorv32_exe.bin` (including header) for UART uploading via the bootloader.
@ -456,8 +444,8 @@ The image generator can generate several types of executables selected by a flag
| `-bld_img` | Generates an executable VHDL memory initialization image (no header) 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 application2 compilation flow will generate the `neorv32_exe.bin`
executable for uploading via UART to the default NEORV32 bootloader.
All these options are managed by the makefile. The normal application compilation flow will generate the `neorv32_exe.bin`
executable designated for uploading via the default NEORV32 bootloader.
.Image Generator Compilation
[NOTE]
@ -465,12 +453,12 @@ The sources of the image generator are automatically compiled when invoking the
.Executable Header
[NOTE]
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. **Note that this executable format cannot be used for _direct_
execution (e.g. via XIP or direct memory access).**
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. **Note that this executable format cannot be used for _direct_ execution (e.g. via
XIP or direct memory access).**
:sectnums:

View file

@ -2,37 +2,34 @@
=== Bootloader
[NOTE]
This section refers to the **default** bootloader from the repository. The bootloader can be customized
to target application-specific scenarios using pre-defined options (see User Guide section
https://stnolting.github.io/neorv32/ug/#_customizing_the_internal_bootloader[Customizing the Internal Bootloader]
) or it can be completely rewritten/replaced for custom purpose.
This section refers to the **default** NEORV32 bootloader.
The NEORV32 bootloader (source code `sw/bootloader/bootloader.c`) provides an optional build-in firmware that
The NEORV32 bootloader (`sw/bootloader/bootloader.c`) provides an optional built-in firmware that
allows to upload new application executables at _any time_ without the need to re-synthesize the FPGA's bitstream.
A UART connection is used to provide a simple text-based user interface that allows to upload executables.
Furthermore, the bootloader provides options to store an executable to a processor-external SPI flash.
An "auto boot" feature can optionally fetch this executable right after reset if there is no user interaction
via UART. This allows to build processor setups with _non-volatile application storage_, which can still be updated at any time.
via UART. This allows to build processor setups with _non-volatile application storage_ while maintaining the option
to update the application software at any timer.
:sectnums:
==== Bootloader SoC/CPU Requirements
The bootloader relies on certain CPU and SoC extensions and modules to be enabled to allow full functionality.
The bootloader requires certain CPU and SoC extensions and modules to be enabled in order to operate correctly.
[cols="<3,<12"]
[cols="^2,<8"]
[grid="none"]
|=======================
| **REQUIRED** | The bootloader is implemented only if the <<_int_bootloader_en>> is _true_ (default). This will automatically select the CPU's <<_indirect_boot>> boot configuration.
| **REQUIRED** | The bootloader requires the privileged architecture CPU extension (<<_zicsr_control_and_status_register_access_privileged_architecture>>) to be enabled.
| **REQUIRED** | At least 512 bytes of data memory (processor-internal DMEM or processor-external DMEM) are required for the bootloader's stack.
| _RECOMMENDED_ | For user interaction via UART (like uploading executables) the primary UART (<<_primary_universal_asynchronous_receiver_and_transmitter_uart0>>) has to be implemented.
Without UART0 the auto-boot via SPI is still supported but the bootloader should be customized (see User Guide).
| **REQUIRED** | The bootloader is implemented only if the `INT_BOOTLOADER_EN` top generic is `true`. This will automatically select the CPU's <<_indirect_boot>> boot configuration.
| **REQUIRED** | The bootloader requires the privileged architecture CPU extension (<<_zicsr_isa_extension>>) to be enabled.
| **REQUIRED** | At least 512 bytes of data memory (processor-internal DMEM or processor-external DMEM) are required for the bootloader's stack and global variables.
| _RECOMMENDED_ | For user interaction via the <<_bootloader_console>> (like uploading executables) the primary UART (<<_primary_universal_asynchronous_receiver_and_transmitter_uart0>>) is required.
| _RECOMMENDED_ | The default bootloader uses bit 0 of the <<_general_purpose_input_and_output_port_gpio>> output port to drive a high-active "heart beat" status LED.
| _RECOMMENDED_ | The <<_machine_system_timer_mtime>> is used to control blinking of the status LED and also to automatically trigger the auto-boot sequence.
| OPTIONAL | The SPI controller (<<_serial_peripheral_interface_controller_spi>>) is needed to store/load executable from external flash (for the auto boot feature).
| OPTIONAL | The XIP controller (<<_execute_in_place_module_xip>>) is needed to execute code directly from a pre-programmed SPI flash.
| _RECOMMENDED_ | The <<_machine_system_timer_mtime>> is used to control blinking of the status LED and also to automatically trigger the <<_auto_boot_sequence>>.
| OPTIONAL | The SPI controller (<<_serial_peripheral_interface_controller_spi>>) is needed to store/load executable from external flash using the <<_auto_boot_sequence>>.
| OPTIONAL | The XIP controller (<<_execute_in_place_module_xip>>) is needed to boot/execute code directly from a pre-programmed SPI flash.
|=======================
@ -53,17 +50,16 @@ The SPI flash has to support single-byte read and write operations, 24-bit addre
.Custom Configuration
[TIP]
Most properties (like chip select line, flash address width, SPI clock frequency, ...) of the default bootloader can be reconfigured
without the need to change the source code. Custom configuration can be made using command line switches when recompiling the bootloader.
See the User Guide https://stnolting.github.io/neorv32/ug/#_customizing_the_internal_bootloader for more information.
without the need to change the source code. Custom configuration can be made using command line switches (defines) when recompiling
the bootloader. See the User Guide https://stnolting.github.io/neorv32/ug/#_customizing_the_internal_bootloader for more information.
:sectnums:
==== Bootloader Console
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.
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`):
@ -74,8 +70,9 @@ Terminal console settings (`19200-8-N-1`):
* newline on `\r\n` (carriage return, newline)
* no transfer protocol / control flow protocol - just raw bytes
.Terminal Program
[IMPORTANT]
_Any_ terminal program that can connect to a serial port should work. However, make sure the program
Any terminal program that can connect to a serial port should work. However, make sure the program
can transfer data in _raw_ byte mode without any protocol overhead (e.g. XMODEM). Some terminal programs struggle with
transmitting files larger than 4kB (see https://github.com/stnolting/neorv32/pull/215). Try a different terminal program
if uploading of a binary does not work.
@ -108,13 +105,13 @@ The start-up screen gives some brief information about the bootloader and severa
|=======================
| `BLDV` | Bootloader version (built date).
| `HWV` | Processor hardware version (the <<_mimpid>> CSR); in BCD format; example: `0x01040606` = v1.4.6.6).
| `CID` | Custom user-defined ID (via the `CUSTOM_ID` register from <<_system_configuration_information_memory_sysinfo>>; defined by the <<_custom_id>> generic).
| `CLK` | Processor clock speed in Hz (via the `CLK` register from <<_system_configuration_information_memory_sysinfo>>; defined by the <<_clock_frequency>> generic).
| `CID` | Custom user-defined ID (via the `CUSTOM_ID` register from <<_system_configuration_information_memory_sysinfo>>.
| `CLK` | Processor clock speed in Hz (via the `CLK` register from <<_system_configuration_information_memory_sysinfo>>.
| `MISA` | RISC-V CPU extensions (<<_misa>> CSR).
| `XISA` | NEORV32-specific CPU extensions (<<_mxisa>> CSR).
| `SOC` | Processor configuration (via the `SOC` register from the <<_system_configuration_information_memory_sysinfo>>; mainly defined by the `IO_*` and `MEM_*` configuration generics).
| `IMEM` | IMEM memory base address and size in byte (via the `IMEM_SIZE` and `ISPACE_BASE` registers from the <<_system_configuration_information_memory_sysinfo>>; defined by the <<_mem_int_imem_size>> generic).
| `DMEM` | DMEM memory base address and size in byte (via the `DMEM_SIZE` and `DSPACE_BASE` registers from the <<_system_configuration_information_memory_sysinfo>>; defined by the <<_mem_int_dmem_size>> generic).
| `SOC` | Processor configuration (via the `SOC` register from the <<_system_configuration_information_memory_sysinfo>>.
| `IMEM` | IMEM memory base address and size in byte (via the `IMEM_SIZE` and `ISPACE_BASE` registers from the <<_system_configuration_information_memory_sysinfo>>.
| `DMEM` | DMEM memory base address and size in byte (via the `DMEM_SIZE` and `DSPACE_BASE` registers from the <<_system_configuration_information_memory_sysinfo>>.
|=======================
Now you have 8 seconds to press _any_ key. Otherwise, the bootloader starts the <<_auto_boot_sequence>>. When
@ -177,7 +174,7 @@ section https://stnolting.github.io/neorv32/ug/#_customizing_the_internal_bootlo
.SPI Flash Programming
[TIP]
For detailed information on using an SPI flash for application storage see User Guide section
https://stnolting.github.io/neorv32/ug/#_programming_an_external_spi_flash_via_the_bootloader[Programming an External SPI Flash via the Bootloader].
https://stnolting.github.io/neorv32/ug/#_programming_an_external_spi_flash_via_the_bootloader[Programming an ExternalSPI Flash via the Bootloader].
:sectnums:

View file

@ -1,11 +1,9 @@
:sectnums:
=== NEORV32 Runtime Environment
The NEORV32 software framework provides a minimal runtime environment (**RTE**) that takes care of a stable
and _safe_ execution environment by handling _all_ traps (= exceptions & interrupts). The RTE simplifies trap handling
by wrapping the CPU's _privileged architecture_ (i.e. trap-related CSRs) into a unified software API.
The NEORV32 RTE is a software library (`sw/lib/source/neorv32_rte.c`) that is part of the default processor library set.
It provides public functions via `sw/lib/include/neorv32_rte.h` for application interaction.
The NEORV32 software framework provides a minimal **runtime environment** (abbreviated "RTE") that takes care of a stable
and _safe_ execution environment by handling _all_ traps (exceptions & interrupts). The RTE simplifies trap handling
by wrapping the CPU's privileged architecture (i.e. trap-related CSRs) into a unified software API.
Once initialized, the RTE provides <<_default_rte_trap_handlers>> that catch all possible traps. These
default handlers just output a message via UART to inform the user when a certain trap has been triggered. The
@ -14,36 +12,34 @@ default handlers can be overridden by the application code to install applicatio
[IMPORTANT]
Using the RTE is **optional but highly recommended**. The RTE provides a simple and comfortable way of delegating
traps to application-specific handlers 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.
[NOTE]
For the **C standard runtime library** see section <<c_standard_library>>.
by the application) are handled correctly. Performance-optimized applications or embedded operating systems may
not use the RTE at allin order to increase response time.
==== RTE Operation
The RTE handles the trap-related CSRs of the CPU's privileged architecture (<<_machine_trap_handling_csrs>>).
It initializes the <<_mtvec>> CSR, which provides the base entry point for all trap
handlers. The address stored to this register reflects the **first-level trap handler**, which is provided by the
NEORV32 RTE. Whenever an exception or interrupt is triggered this first-level handler is executed.
The RTE manages the trap-related CSRs of the CPU's privileged architecture (<<_machine_trap_handling_csrs>>).
It initializes the <<_mtvec>> CSR, which provides the base entry point for all trap handlers. The address
stored to this register defines the address of the **first-level trap handler**, which is provided by the
NEORV32 RTE. Whenever an exception or interrupt is triggered this first-level trap handler is executed.
The first-level handler performs a complete context save, analyzes the source of the trap and
calls the according **second-level trap handler**, which takes care of the actual exception/interrupt
handling. For this, the RTE manages a private look-up table to store the addresses of the according trap
handlers.
handling. The RTE manages a private look-up table to store the addresses of the according second-level trap handlers.
After the initial RTE setup, each entry in the RTE's trap handler look-up table is initialized with a
<<_default_rte_trap_handlers>>. These default handler do not execute any trap-related operations - they
just output a message via the *primary UART (UART0)* to inform the user that a trap has occurred, that is not
handled by the actual application. After sending this message, the RTE tries to continue executing the user program.
just output a message via the *primary UART (UART0)* to inform the user that a trap has occurred, which is not (yet)
handled by the actual application. After sending this message, the RTE tries to continue executing the actual program
by resolving the trap cause.
==== Using the RTE
The NEORV32 is enabled by calling the RTE's setup function:
The NEORV32 is part of the default NEORV32 software framework. However, it has to explicitly enabled by calling
the RTE's setup function:
.Function Prototype: RTE Setup
.RTE Setup (Function Prototype)
[source,c]
----
void neorv32_rte_setup(void);
@ -52,24 +48,45 @@ void neorv32_rte_setup(void);
[NOTE]
The RTE should be enabled right at the beginning of the application's `main` function.
As mentioned above, _all_ traps will only trigger execution of the RTE's <<_default_rte_trap_handlers>>.
To use application-specific handlers, which actually _handle_ a trap, the default handlers can be overridden
As mentioned above, all traps will just trigger execution of the RTE's <<_default_rte_trap_handlers>> at first.
To use application-specific handlers, which actually "handle" a trap, the default handlers can be overridden
by installing user-defined ones:
.Function Prototype: Installing an Application-Specific Trap Handler
.Installing an Application-Specific Trap Handler (Function Prototype)
[source,c]
----
int neorv32_rte_handler_install(uint8_t id, void (*handler)(void));
----
The first argument `id` defines the "trap ID" (for example a certain interrupt request) that shall be handled
by the user-defined handler. The second argument `*handler` is the actual function that implements the trap
handler. The function return zero on success and a non-zero value if an error occurred (invalid `id`). In this
case no modifications to the RTE's trap look-up-table will be made.
by the user-defined handler. These IDs are defined in `sw/lib/include/neorv32_rte.h`:
The custom handler functions need to have a specific format without any arguments an with no return value:
.RTE Trap Identifiers (cut-out)
[source,c]
----
enum NEORV32_RTE_TRAP_enum {
RTE_TRAP_I_MISALIGNED = 0, /**< Instruction address misaligned */
RTE_TRAP_I_ACCESS = 1, /**< Instruction (bus) access fault */
RTE_TRAP_I_ILLEGAL = 2, /**< Illegal instruction */
RTE_TRAP_BREAKPOINT = 3, /**< Breakpoint (EBREAK instruction) */
RTE_TRAP_L_MISALIGNED = 4, /**< Load address misaligned */
RTE_TRAP_L_ACCESS = 5, /**< Load (bus) access fault */
RTE_TRAP_S_MISALIGNED = 6, /**< Store address misaligned */
RTE_TRAP_S_ACCESS = 7, /**< Store (bus) access fault */
RTE_TRAP_UENV_CALL = 8, /**< Environment call from user mode (ECALL instruction) */
RTE_TRAP_MENV_CALL = 9, /**< Environment call from machine mode (ECALL instruction) */
RTE_TRAP_MSI = 10, /**< Machine software interrupt */
RTE_TRAP_MTI = 11, /**< Machine timer interrupt */
RTE_TRAP_MEI = 12, /**< Machine external interrupt */
RTE_TRAP_FIRQ_0 = 13, /**< Fast interrupt channel 0 */
RTE_TRAP_FIRQ_1 = 14, /**< Fast interrupt channel 1 */
...
----
.Function Prototype: Custom Trap Handler
The second argument `*handler` is the actual function that implements the user-defined trap handler.
The custom handler functions need to have a specific format without any arguments and with no return value:
.Custom Trap Handler (Function Prototype)
[source,c]
----
void custom_trap_handler_xyz(void) {
@ -80,56 +97,14 @@ void custom_trap_handler_xyz(void) {
.Custom Trap Handler Attributes
[WARNING]
Do NOT use the `((interrupt))` attribute for the application trap handler functions! This
Do **NOT** use the `((interrupt))` attribute for the application trap handler functions! This
will place a `mret` instruction to the end of it making it impossible to return to the first-level
trap handler of the RTE core, which will cause stack corruption.
The trap identifier `id` specifies the according trap cause. These can be an _asynchronous trap_ like
an interrupt from one of the processor modules or a _synchronous trap_ triggered by software-caused events
like an illegal instruction or an environment call instruction. The `sw/lib/include/neorv32_rte.h` library files
provides aliases for trap events supported by the CPU (see <<_neorv32_trap_listing>>) that can be used when
installing custom trap handler functions:
.RTE Trap ID List
[cols="<5,<12"]
[options="header",grid="rows"]
|=======================
| ID alias [C] | Description / trap causing event
| `RTE_TRAP_I_MISALIGNED` | instruction address 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` | load address misaligned
| `RTE_TRAP_L_ACCESS` | load (bus) access fault
| `RTE_TRAP_S_MISALIGNED` | store address 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` | fast interrupt channel 0
| `RTE_TRAP_FIRQ_1` | fast interrupt channel 1
| `RTE_TRAP_FIRQ_2` | fast interrupt channel 2
| `RTE_TRAP_FIRQ_3` | fast interrupt channel 3
| `RTE_TRAP_FIRQ_4` | fast interrupt channel 4
| `RTE_TRAP_FIRQ_5` | fast interrupt channel 5
| `RTE_TRAP_FIRQ_6` | fast interrupt channel 6
| `RTE_TRAP_FIRQ_7` | fast interrupt channel 7
| `RTE_TRAP_FIRQ_8` | fast interrupt channel 8
| `RTE_TRAP_FIRQ_9` | fast interrupt channel 9
| `RTE_TRAP_FIRQ_10` | fast interrupt channel 10
| `RTE_TRAP_FIRQ_11` | fast interrupt channel 11
| `RTE_TRAP_FIRQ_12` | fast interrupt channel 12
| `RTE_TRAP_FIRQ_13` | fast interrupt channel 13
| `RTE_TRAP_FIRQ_14` | fast interrupt channel 14
| `RTE_TRAP_FIRQ_15` | fast interrupt channel 15
|=======================
The following example shows how to install a custom handler (`custom_mtime_irq_handler`) for handling
the RISC-V machine timer (MTIME) interrupt:
.Example: Installing the MTIME IRQ Handler
.Installing a MTIME IRQ Handler
[source,c]
----
neorv32_rte_handler_install(RTE_TRAP_MTI, custom_mtime_irq_handler);
@ -144,10 +119,7 @@ and will re-install the <<_default_rte_trap_handlers>> for the specific trap.
int neorv32_rte_handler_uninstall(uint8_t id);
----
The argument `id` defines the identifier of the according trap that shall be un-installed. The function return zero
on success and a non-zero value if an error occurred (invalid `id`). In this case no modifications to the RTE's trap
look-up-table will be made.
The argument `id` defines the identifier of the according trap that shall be un-installed.
The following example shows how to un-install the custom handler `custom_mtime_irq_handler` from the
RISC-V machine timer (MTIME) interrupt:
@ -160,19 +132,16 @@ neorv32_rte_handler_uninstall(RTE_TRAP_MTI);
==== Default RTE Trap Handlers
The default RTE trap handlers are executed when a certain trap is triggered that is not (yet) handled by a user-defined
application-specific trap handler. The default handler will output a message giving additional debug information
via UART0 to inform the user and will try to resume normal program execution.
.Fast Interrupts (FIRQs)
[NOTE]
The RTE default trap handlers will also clear the according <<_mip>> bit if an un-handled fast interrupt occurs.
The default RTE trap handlers are executed when a certain trap is triggered that is not (yet) handled by an
application-defined trap handler. The default handler will output a message giving additional debug information
via the <<_primary_universal_asynchronous_receiver_and_transmitter_uart0>> to inform the user and it will also
try to resume normal program execution. Some exemplary RTE outputs are shown below.
.Continuing Execution
[WARNING]
In most cases the RTE can successfully continue operation - for example if it catches an **interrupt** request that is not handled
by the actual application program. However, if the RTE catches an un-handled **trap** like a bus access fault
continuing execution will most likely fail making the CPU crash.
In most cases the RTE can successfully continue operation - for example if it catches an **interrupt** request
that is not handled by the actual application program. However, if the RTE catches an un-handled **trap** like
a bus access fault exception continuing execution will most likely fail making the CPU crash.
.RTE Default Trap Handler Output Examples
[source]
@ -182,13 +151,14 @@ continuing execution will most likely fail making the CPU crash.
<RTE> Load address misaligned @ PC=0x00000440, ADDR=0x80000101 </RTE> <3>
<RTE> Fast IRQ 0x00000003 @ PC=0x00000820 </RTE> <4>
----
<1> Illegal 32-bit instruction at address 0x000002d6.
<2> Illegal 16-bit instruction at address 0x00000302.
<3> Misaligned load access at address 0x00000440 (trying to load a full word from 0x80000101).
<4> Fast interrupt request from channel 3 before executing instruction at address 0x00000820.
<1> Illegal 32-bit instruction at address `0x000002d6`.
<2> Illegal 16-bit instruction at address `0x00000302`.
<3> Misaligned load access at address `0x00000440` (trying to load a full 32-bit word from address `0x80000101`).
<4> Fast interrupt request from channel 3 before executing instruction at address `0x00000820`.
The specific _message_ right at the beginning of the debug trap handler message corresponds to the trap code from the
<<_mcause>> CSR (see <<_neorv32_trap_listing>>). A full list of all messages and the according `mcause` trap codes is shown below.
The specific message right at the beginning of the debug trap handler message corresponds to the trap code
obtained from the <<_mcause>> CSR (see <<_neorv32_trap_listing>>). A full list of all messages and the according
`mcause` trap codes is shown below.
.RTE Default Trap Handler Messages and According `mcause` Values
[cols="<5,^5"]
@ -224,12 +194,12 @@ The specific _message_ right at the beginning of the debug trap handler message
| "Fast IRQ 0x0000000d" | `0x8000001d`
| "Fast IRQ 0x0000000e" | `0x8000001e`
| "Fast IRQ 0x0000000f" | `0x8000001f`
| "Unknown trap cause" | _unknown_
| "Unknown trap cause" | undefined
|=======================
===== Bus Access Faults
For bus access faults the RTE default trap handlers also outputs an error code from the
For bus access faults the RTE default trap handlers also outputs the error code obtained from the
<<_internal_bus_monitor_buskeeper>> to show the cause of the bus fault. One example is shown below.
.RTE Default Trap Handler Output Example (Load Access Bus Fault)
@ -243,8 +213,7 @@ Three different messages are possible here:
* `[TIMEOUT_ERR]`: The accessed memory-mapped module did not respond within the valid access time window.
In Most cases this is caused by accessing a module that has not been implemented or when accessing
"address space holes" (unused/unmapped addresses).
* `[DEVICE_ERR]`: The accesses memory-mapped module asserted it's error signal to indicate an invalid access.
For example this can be caused by trying to write to read-only registers or by writing data quantities (like a byte)
to devices that do not support sub-word write accesses.
* `[PMP_ERR]`: This indicates an access right violation caused by the <<_pmp_physical_memory_protection>>.
"address space holes" via unused/unmapped addresses (see section <<_bus_interface_protocol>>).
* `[DEVICE_ERR]`: The accesses memory-mapped module asserted its error signal to indicate an invalid access.
For example this can be caused by trying to write to read-only registers.
* `[PMP_ERR]`: This indicates an access right violation caused by the <<_pmp_isa_extension>>.