Merge branch 'main' of github.com:LukasP46/neorv32 into i2c-bootloader

This commit is contained in:
Lukas Pajak 2025-01-03 17:01:20 +01:00
commit 1ea1965f24
153 changed files with 6959 additions and 4221 deletions

View file

@ -14,7 +14,7 @@ on:
jobs:
doxygen:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
name: 'SW Framework'
steps:
@ -41,7 +41,7 @@ jobs:
asciidoctor:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
name: 'Datasheet'
steps:
@ -66,7 +66,7 @@ jobs:
needs:
- doxygen
- asciidoctor
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
name: 'Deploy to Releases and Pages'
steps:

View file

@ -18,7 +18,7 @@ on:
jobs:
sim_default_tb:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
name: 'processor simulation'
strategy:
fail-fast: false

View file

@ -29,6 +29,23 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12
| Date | Version | Comment | Ticket |
|:----:|:-------:|:--------|:------:|
| 01.01.2025 | 1.10.8.6 | :sparkles: :test_tube: add smp dual-core option | [#1135](https://github.com/stnolting/neorv32/pull/1135) |
| 29.12.2024 | 1.10.8.5 | :test_tube: add multi-hart support to debug module | [#1132](https://github.com/stnolting/neorv32/pull/1132) |
| 29.12.2024 | 1.10.8.4 | :warning: rename `SYSINFO.MEM -> SYSINFO.MISC`; add new `SYSINFO.MISC` entry for number of CPU cores (hardwired to one) | [#1134](https://github.com/stnolting/neorv32/pull/1134) |
| 29.12.2024 | 1.10.8.3 | :bug: fix incorrect HPM counter sizes if `HPM_CNT_WIDTH = 64` | [#1128](https://github.com/stnolting/neorv32/pull/1128) |
| 27.12.2024 | 1.10.8.2 | add out-of-band signals to internal request bus | [#1131](https://github.com/stnolting/neorv32/pull/1131) |
| 27.12.2024 | 1.10.8.1 | :warning: replace MTIME by CLINT; :warning: remove `HART_ID` generic | [#1130](https://github.com/stnolting/neorv32/pull/1130) |
| 26.12.2024 | [**:rocket:1.10.8**](https://github.com/stnolting/neorv32/releases/tag/v1.10.8) | **New release** | |
| 23.12.2024 | 1.10.7.9 | :warning: rework IO/peripheral address space; :sparkles: increase device size from 256 bytes to 64kB | [#1126](https://github.com/stnolting/neorv32/pull/1126) |
| 22.12.2024 | 1.10.7.8 | :warning: rename CPU tuning options / generics | [#1125](https://github.com/stnolting/neorv32/pull/1125) |
| 22.12.2024 | 1.10.7.7 | :warning: move clock gating switch from processor top to CPU clock; `CLOCK_GATING_EN` is now a CPU tuning option | [#1124](https://github.com/stnolting/neorv32/pull/1124) |
| 21.12.2024 | 1.10.7.6 | minor rtl cleanups and optimizations | [#1123](https://github.com/stnolting/neorv32/pull/1123) |
| 19.12.2024 | 1.10.7.5 | :test_tube: use time-multiplex PMP architecture (reducing area footprint) | [#1105](https://github.com/stnolting/neorv32/pull/1105) |
| 14.12.2024 | 1.10.7.4 | :sparkles: add new module: I2C-compatible **Two-Wire Device Controller (TWD)** | [#1121](https://github.com/stnolting/neorv32/pull/1121) |
| 14.12.2024 | 1.10.7.3 | :warning: rework TRNG (change HAL; remove interrupt) | [#1120](https://github.com/stnolting/neorv32/pull/1120) |
| 12.12.2024 | 1.10.7.2 | add external memory configuration/initialization options to testbench | [#1119](https://github.com/stnolting/neorv32/pull/1119) |
| 11.12.2024 | 1.10.7.1 | :test_tube: shrink bootloader's minimal ISA (`rv32e`) and RAM (256 bytes) requirements | [#1118](https://github.com/stnolting/neorv32/pull/1118) |
| 10.12.2024 | [**:rocket:1.10.7**](https://github.com/stnolting/neorv32/releases/tag/v1.10.7) | **New release** | |
| 03.12.2024 | 1.10.6.9 | :sparkles: add ONEWIRE command and data FIFO; :warning: rework ONEWIRE interface register layout; :bug: fix regression: busy flag was stuck at zero | [#1113](https://github.com/stnolting/neorv32/pull/1113) |
| 01.12.2024 | 1.10.6.8 | add TWI bus sensing logic | [#1111](https://github.com/stnolting/neorv32/pull/1111) |
| 26.11.2024 | 1.10.6.7 | :bug: fix some HDL issues that caused problems when auto-converting to Verilog | [#1103](https://github.com/stnolting/neorv32/pull/1103) |

View file

@ -1,7 +1,7 @@
BSD 3-Clause License
Copyright (c) NEORV32 contributors.
Copyright (c) 2020-2024, Stephan Nolting. All rights reserved.
Copyright (c) 2020-2025, Stephan Nolting. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

View file

@ -96,7 +96,7 @@ setup according to your needs. Note that all of the following SoC modules are en
**CPU Core**
* [![RISCV-ARCHID](https://img.shields.io/badge/RISC--V%20Architecture%20ID-19-000000.svg?longCache=true&style=flat-square&logo=riscv&colorA=273274&colorB=fbb517)](https://github.com/riscv/riscv-isa-manual/blob/master/marchid.md)
* RISC-V 32-bit little-endian single-core pipelined/multi-cycle modified Harvard architecture
* RISC-V 32-bit little-endian single- or SMP-dual-core pipelined/multi-cycle modified Harvard architecture
* configurable [instruction sets and extensions](https://stnolting.github.io/neorv32/#_instruction_sets_and_extensions):
\
`RV32`
@ -152,7 +152,7 @@ allows booting application code via UART, TWI or from external SPI flash
**Timers and Counters**
* 64-bit machine timer ([MTIME](https://stnolting.github.io/neorv32/#_machine_system_timer_mtime)), RISC-V spec. compatible
* core local interruptor ([CLINT](https://stnolting.github.io/neorv32/#_core_local_interruptor_clint)), RISC-V-compatible
* 32-bit general purpose timer ([GPTMR](https://stnolting.github.io/neorv32/#_general_purpose_timer_gptmr))
* watchdog timer ([WDT](https://stnolting.github.io/neorv32/#_watchdog_timer_wdt))
@ -162,7 +162,8 @@ allows booting application code via UART, TWI or from external SPI flash
([UART](https://stnolting.github.io/neorv32/#_primary_universal_asynchronous_receiver_and_transmitter_uart0),
[SPI](https://stnolting.github.io/neorv32/#_serial_peripheral_interface_controller_spi) (SPI host),
[SDI](https://stnolting.github.io/neorv32/#_serial_data_interface_controller_sdi) (SPI device),
[TWI/I²C](https://stnolting.github.io/neorv32/#_two_wire_serial_interface_controller_twi),
[TWI](https://stnolting.github.io/neorv32/#_two_wire_serial_interface_controller_twi) (I²C host),
[TWD](https://stnolting.github.io/neorv32/#_two_wire_serial_device_controller_twd) (I²C device),
[ONEWIRE/1-Wire](https://stnolting.github.io/neorv32/#_one_wire_serial_interface_controller_onewire))
* general purpose IOs ([GPIO](https://stnolting.github.io/neorv32/#_general_purpose_input_and_output_port_gpio)) and
[PWM](https://stnolting.github.io/neorv32/#_pulse_width_modulation_controller_pwm)

View file

@ -88,5 +88,5 @@ help:
@echo " ug-html - build user guide as HTML page (public/ug/index.html)"
@echo " doxygen - build software documentation as HTML page (doxygen_build/html/index.html)"
@echo " revnumber - for overriding the revnumber attribute in 'pdf' and/or 'html'"
@echo " container - Build 'pdf' and 'html' in an 'asciidoctor-wavedrom' container
@echo " container - Build 'pdf' and 'html' in an 'asciidoctor-wavedrom' container"
@echo " clean - delete output files and directories"

View file

@ -2,7 +2,7 @@
:email: stnolting@gmail.com
:keywords: neorv32, risc-v, riscv, rv32, fpga, soft-core, vhdl, microcontroller, cpu, soc, processor, gcc, openocd, gdb, verilog, rtl, asip, asic
: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.10.6
:revnumber: v1.10.8
:icons: font
:imagesdir: ../figures
:toc: macro

View file

@ -1,14 +1,20 @@
<<<
// ####################################################################################################################
<<<
include::overview.adoc[]
<<<
include::soc.adoc[]
<<<
include::cpu.adoc[]
<<<
include::software.adoc[]
<<<
include::on_chip_debugger.adoc[]
<<<
include::../legal.adoc[]

View file

@ -3,8 +3,10 @@
The NEORV32 CPU is an area-optimized RISC-V core implementing the `rv32i_zicsr_zifencei` base (privileged) ISA and
supporting several additional/optional ISA extensions. The CPU's micro architecture is based on a von-Neumann
machine build upon a mixture of multi-cycle and pipelined execution schemes.
machine build upon a mixture of multi-cycle and pipelined execution schemes. Optionally, the core can be implemented
as SMP <<_dual_core_configuration>>.
.RISC-V Specifications
[NOTE]
This chapter assumes that the reader is familiar with the official
RISC-V _User_ and _Privileged Architecture_ specifications.
@ -33,13 +35,13 @@ test framework is available in a separate repository at GitHub: https://github.c
Executing instructions or accessing CSRs from yet unsupported ISA extensions will raise an illegal
instruction exception (see section <<_full_virtualization>>).
**Incompatibility Issues and Limitations**
.`time[h]` CSRs (Wall Clock Time)
[IMPORTANT]
The NEORV32 does not implement the `time[h]` registers. Any access to these registers will trap. It is
recommended that the trap handler software provides a means of accessing the platform-defined <<_machine_system_timer_mtime>>.
recommended that the trap handler software provides a means of accessing the the machine timer of the
<<_core_local_interruptor_clint>>.
.No Hardware Support of Misaligned Memory Accesses
[IMPORTANT]
@ -65,23 +67,20 @@ direction as seen from the CPU.
|=======================
| Signal | Width/Type | Dir | Description
4+^| **Global Signals**
| `clk_i` | 1 | in | Global clock line, all registers triggering on rising edge, this clock can be switched off during <<_sleep_mode>>
| `clk_aux_i` | 1 | in | Always-on clock, used to keep the the sleep control active when `clk_i` is switched off
| `rstn_i` | 1 | in | Global reset, low-active
| `sleep_o` | 1 | out | CPU is in <<_sleep_mode>> when set
| `debug_o` | 1 | out | CPU is in <<_cpu_debug_mode,debug mode>> when set
| `clk_i` | 1 | in | Global clock line, all registers triggering on rising edge.
| `rstn_i` | 1 | in | Global reset, low-active.
4+^| **Interrupts (<<_traps_exceptions_and_interrupts>>)**
| `msi_i` | 1 | in | RISC-V machine software interrupt
| `mei_i` | 1 | in | RISC-V machine external interrupt
| `mti_i` | 1 | in | RISC-V machine timer interrupt
| `firq_i` | 16 | in | Custom fast interrupt request signals
| `dbi_i` | 1 | in | Request CPU to halt and enter debug mode (RISC-V <<_on_chip_debugger_ocd>>)
| `msi_i` | 1 | in | RISC-V machine software interrupt.
| `mei_i` | 1 | in | RISC-V machine external interrupt.
| `mti_i` | 1 | in | RISC-V machine timer interrupt.
| `firq_i` | 16 | in | Custom fast interrupt request signals.
| `dbi_i` | 1 | in | Request CPU to halt and enter debug mode (RISC-V <<_on_chip_debugger_ocd>>).
4+^| **Instruction <<_bus_interface>>**
| `ibus_req_o` | `bus_req_t` | out | Instruction fetch bus request
| `ibus_rsp_i` | `bus_rsp_t` | in | Instruction fetch bus response
| `ibus_req_o` | `bus_req_t` | out | Instruction fetch bus request.
| `ibus_rsp_i` | `bus_rsp_t` | in | Instruction fetch bus response.
4+^| **Data <<_bus_interface>>**
| `dbus_req_o` | `bus_req_t` | out | Data access (load/store) bus request
| `dbus_rsp_i` | `bus_rsp_t` | in | Data access (load/store) bus response
| `dbus_req_o` | `bus_req_t` | out | Data access (load/store) bus request.
| `dbus_rsp_i` | `bus_rsp_t` | in | Data access (load/store) bus response.
|=======================
.Bus Interface Protocol
@ -110,6 +109,7 @@ The generic type "suv(x:y)" represents a `std_ulogic_vector(x downto y)`.
[options="header",grid="rows"]
|=======================
| Name | Type | Description
| `HART_ID` | natural | Value for the <<_mhartid>> CSR.
| `VENDOR_ID` | suv(31:0) | Value for the <<_mvendorid>> CSR.
| `BOOT_ADDR` | suv(31:0) | CPU reset address. See section <<_address_space>>.
| `DEBUG_PARK_ADDR` | suv(31:0) | "Park loop" entry address for the <<_on_chip_debugger_ocd>>, has to be 4-byte aligned.
@ -119,6 +119,10 @@ The generic type "suv(x:y)" represents a `std_ulogic_vector(x downto y)`.
| `RISCV_ISA_Smpmp` | boolean | Implement RISC-V-compatible physical memory protection (PMP). See section <<_smpmp_isa_extension>>.
|=======================
.Tuning Option Generics
[TIP]
Additional generics that are related to certain _tuning options_ are listed in section <<_cpu_tuning_options>>.
<<<
// ####################################################################################################################
@ -158,23 +162,10 @@ Up to four individual synchronous read ports allow to fetch up to 4 register ope
are mutually exclusive as they happen in separate cycles. Hence, there is no need to consider things like "read-during-write"
behavior.
The register file provides two different implementation options configured via the top's `REGFILE_HW_RST` generic.
* `REGFILE_HW_RST = false` (default): In this configuration the register file is implemented as plain memory array without a
dictated hardware reset. This architecture allows to infer FPGA block RAM for the entire register file resulting in minimal
general logic utilization.
* `REGFILE_HW_RST = true`: This configuration is based on individual FFs that do provide a dedicated hardware reset.
Hence, the register cannot be mapped to FPGA block RAM. This optional can be selected if the application requires a
reset of the register file (e.g. for security reasons) or if the design shall be synthesized for an **ASIC** implementation.
Using individual FFs for th register file might also improve timing as no long routing lines are required to connect to
block RAM primitives.
The state of this configuration generic can be checked by software via the <<_mxisa>> CSR.
.FPGA Implementation
[WARNING]
Enabling the `REGFILE_HW_RST` option for FPGA implementation is not recommended as this will massively increase the amount
of required logic resources.
.Memory Tuning Options
[TIP]
The physical implementation of the register file's memory core can be tuned for certain design goals like area or throughput.
See section <<_cpu_tuning_options>> for more information.
.Implementation of the `zero` Register within FPGA Block RAM
[NOTE]
@ -208,12 +199,6 @@ and <<_b_isa_extension>>).
The CPU control will raise an illegal instruction exception if a multi-cycle functional unit (like the <<_custom_functions_unit_cfu>>)
does not complete processing in a bound amount of time (configured via the package's `monitor_mc_tmo_c` constant; default = 512 clock cycles).
.Tuning Options
[TIP]
The ALU architecture can be tuned for an application-specific area-vs-performance trade-off. The `FAST_MUL_EN` and `FAST_SHIFT_EN`
generics can be used to implement performance-optimized barrel shifters and DSP blocks, respectively. See sections <<_i_isa_extension>>,
<<_b_isa_extension>> and <<_m_isa_extension>> for specific examples.
:sectnums:
==== CPU Bus Unit
@ -261,10 +246,94 @@ CPU back-end for actual execution. Execution is conducted by a state-machine tha
includes the <<_control_and_status_registers_csrs>> as well as the trap controller.
:sectnums:
==== CPU Tuning Options
The top module provides several tuning options to optimize the CPU for a specific goal.
Note that these configuration options have no impact on the actual functionality (e.g. ISA compatibility).
.Software Tuning Options Discovery
[TIP]
Software can check for configured tuning options via specific flags in the <<_mxisa>> CSR.
{empty} +
[discrete]
===== **`CPU_CLOCK_GATING_EN`**
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | Clock gating
| Type | `boolean`
| Default | `false`, disabled
| Description | When **enabled** the CPU's primary clock is switched off when the CPU enters <<_sleep_mode>>. See <<_cpu_clock_gating>>.
| | When **disabled** the CPU clock system is implemented as single always-on clock domain.
|=======================
{empty} +
[discrete]
===== **`CPU_FAST_MUL_EN`**
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | Fast multiplication
| Type | `boolean`
| Default | `false`, disabled
| Description | When **enabled** the `M`/`Zmmul` extension's multiplier is implemented as "plain multiplication" allowing the
synthesis tool to infer DSP blocks / multiplication primitives. Multiplication operations only require a few cycles due to the
DSP-internal register stages. The execution time is time-independent of the provided operands.
| | When **disabled** the `M`/`Zmmul` extension's multiplier is implemented as bit-serial multiplier that computes one
result bit in every cycle. Multiplication operations only requires at least 32 cycles but the entire execution time is still
time-independent of the provided operands.
|=======================
{empty} +
[discrete]
===== **`CPU_FAST_SHIFT_EN`**
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | Fast bit shifting
| Type | `boolean`
| Default | `false`, disabled
| Description | When **enabled** the ALU's shifter unit is implemented as full-parallel barrel shifter that is capable
of shifting a data word by an arbitrary number of positions within a single cycle. Hence, the execution time of any base-ISA
shift operation is independent of the provided operands. Note that the barrel shifter requires a lot of hardware resources and
might also increase the core's critical path.
| | When **disabled** the ALU's shifter unit is implemented as bit-serial shifter that can shift the input data
only by one position per cycle. Hence, several cycles might be required to complete any base-ISA shift-related operations.
Therefore, the execution time of the serial approach is **not** time-independent of the provided operands. However, the serial
approach requires only a few hardware resources and does not impact the critical path.
|=======================
{empty} +
[discrete]
===== **`CPU_RF_HW_RST_EN`**
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | Register file hardware reset
| Type | `boolean`
| Default | `false`, disabled
| Description | When **enabled** the CPU register file is implemented using single flip flops that provide a full hardware reset.
The register file is reset to all-zero after each hardware reset. Note that this options requires a lot of flip flops and LUTs to
build the register file. However, timing might be optimized as there is no need to route to far blockRAM resources.
| | When **disabled** the CPU register file is implemented in a way to allow synthesis to infer memory primitives
like blockRAM. Note that these primitives do not provide any kind of hardware reset. Hence, the data content is undefined after reset.
|=======================
==== Sleep Mode
The NEORV32 CPU provides a single sleep mode that can be entered to power-down the core reducing
dynamic power consumption. Sleep mode is entered by executing the `wfi` ("wait for interrupt") instruction.
dynamic power consumption. Sleep mode is entered by executing the RISC-V `wfi` ("wait for interrupt") instruction.
.Execution Details
[NOTE]
@ -272,8 +341,8 @@ The `wfi` instruction will raise an illegal instruction exception when executed
if `TW` in <<_mstatus>> is set. When executed in debug-mode or during single-stepping `wfi` will behave as
simple `nop` without entering sleep mode.
After executing the `wfi` instruction the CPU's `sleep_o` signal (<<_cpu_top_entity_signals>>) will become set
as soon as the CPU has fully halted ("CPU is sleeping"):
After executing the `wfi` instruction the `sleep` signal of the CPU's request buses (<<_bus_interface>> will become set
as soon as the CPU has fully halted:
[start=1]
.The front-end (instruction fetch) has stopped. There is no pending instruction fetch bus access.
@ -281,14 +350,25 @@ as soon as the CPU has fully halted ("CPU is sleeping"):
.There is no enabled interrupt being pending.
CPU-external modules like memories, timers and peripheral interfaces are not affected by this. Furthermore, the CPU will
continue to buffer/enqueue incoming interrupt. The CPU will leave sleep mode as soon as any _enabled_ interrupt (via <<_mie>>)
continue to buffer/enqueue incoming interrupts. The CPU will leave sleep mode as soon as any _enabled_ interrupt (via <<_mie>>)
source becomes _pending_ or if a debug session is started.
===== Power-Down Mode
Optionally, the sleep mode can also be used to shut down the CPU's main clock to further reduce power consumption
by halting the core's clock tree. This clock gating mode is enabled by the `CLOCK_GATING_EN` generic
(<<_processor_top_entity_generics>>). See section <<_processor_clocking>> for more information.
==== CPU Clock Gating
The single clock domain of the CPU core can be split into an always-on clock domain and a switchable clock domain.
The switchable clock domain can be deactivated to further reduce reduce dynamic power consumption. CPU-external modules
like timers, interfaces and memories are not affected by the clock gating.
The splitting into two clock domain is enabled by the `CPU_CLOCK_GATING_EN` generic (<<_processor_top_entity_generics>> /
<<_cpu_tuning_options>>). When enabled, a generic clock switching gate is added to decouple the switchable clock from
the always-on clock domain. Whenever the CPU enters <<_sleep_mode>> the switchable clock domain is shut down.
.Clock Switch Hardware
[NOTE]
By default, a generic clock switch is used (`rtl/core/neorv32_clockgate.vhd`). Especially for FPGA setups it is highly
recommended to replace this default module by a technology-specific primitive or macro wrapper to improve synthesis results
(clock skew, global clock tree usage, etc.).
==== Full Virtualization
@ -312,15 +392,22 @@ the instruction fetch interface (`i_bus_*` signals) is used for fetching instruc
(`d_bus_*` signals) is used to access data via load and store operations. Each of these interfaces can access an address
space of up to 2^32^ bytes (4GB).
The bus interface uses two custom interface types: `bus_req_t` is used to propagate the bus access **requests**. These
signals are driven by the _accessing_ device (i.e. the CPU core). `bus_rsp_t` is used to return the bus **response** and
is driven by the _accessed_ device or bus system (i.e. a processor-internal memory or IO device).
The bus interface uses two custom interface types: `bus_req_t` is used to propagate the bus access requests downstream
from a host to a device. These signals are driven by the request-issuing device (i.e. the CPU core). Vice versa, `bus_rsp_t`
is used to return the bus response upstream from a device back to the host and is driven by the accessed device or bus system
(i.e. a processor-internal memory or IO device).
The signals of the request bus are split in to two categories: _in-band_ signals and _out-of-band_ signals. In-band
signals always belong to a certain bus transaction and are only valid between `stb` being set and the according response
(`err` or `ack`). being set. In contrast, the out-of-band signals are not associated with any bus transaction and are
always valid when set.
.Bus Interface - Request Bus (`bus_req_t`)
[cols="^1,^1,<6"]
[options="header",grid="rows"]
|=======================
| Signal | Width | Description
3+^| **In-Band Signals**
| `addr` | 32 | Access address (byte addressing)
| `data` | 32 | Write data
| `ben` | 4 | Byte-enable for each byte in `data`
@ -329,7 +416,10 @@ is driven by the _accessed_ device or bus system (i.e. a processor-internal memo
| `src` | 1 | Access source (`0` = instruction fetch, `1` = load/store)
| `priv` | 1 | Set if privileged (M-mode) access
| `rvso` | 1 | Set if current access is a reservation-set operation (`lr` or `sc` instruction, <<_zalrsc_isa_extension>>)
| `fence` | 1 | Data/instruction fence operation; valid without `stb` being set
3+^| **Out-Of-Band Signals**
| `fence` | 1 | Data/instruction fence request; single-shot
| `sleep` | 1 | Set if ALL upstream devices are in <<_sleep_mode>>
| `debug` | 1 | Set if the upstream device is in debug-mode
|=======================
.Bus Interface - Response Bus (`bus_rsp_t`)
@ -363,7 +453,7 @@ The figure below shows three exemplary bus accesses:
. A write access to address `B_addr` writing `wdata` (fastest response; `ACK` arrives right in the next cycle).
. A failing read access to address `C_addr` (slow response; `ERR` arrives after several cycles).
.Three Exemplary Bus Transactions
.Three Exemplary Bus Transactions (showing only in-band signals)
image::bus_interface.png[700]
.Adding Register Stages
@ -397,7 +487,7 @@ and also registers a reservation for the address `addr` (`rvs_valid` becomes set
invalidated (`rvs_valid` is `0`) the store access fails, so `wdata2` is **not** written to address `addr` at all. The failed
operation is indicated by a **1** being returned via `rsp.data` together with `ack`.
.Three Exemplary LR/SC Bus Transactions
.Three Exemplary LR/SC Bus Transactions (showing only in-band signals)
image::bus_interface_atomic.png[700]
.Store-Conditional Status
@ -503,7 +593,7 @@ The "compressed" ISA extension provides 16-bit encodings of commonly used instru
|=======================
| Class | Instructions | Execution cycles
| ALU | `c.addi4spn` `c.nop` `c.add[i]` `c.li` `c.addi16sp` `c.lui` `c.and[i]` `c.sub` `c.xor` `c.or` `c.mv` | 2
| ALU | `c.srli` `c.srai` `c.slli` | 3 + 1..32; FAST_SHIFT: 4
| ALU | `c.srli` `c.srai` `c.slli` | 3 + 1..32; `CPU_FAST_SHIFT_EN`: 4
| Branches | `c.beqz` `c.bnez` | taken: 6; not taken: 3
| Jumps / calls | `c.jal[r]` `c.j` `c.jr` | 6
| Memory access | `c.lw` `c.sw` `c.lwsp` `c.swsp` | 4
@ -532,7 +622,7 @@ The `I` ISA extensions is the base RISC-V integer ISA that is always enabled.
| Class | Instructions | Execution cycles
| ALU | `add[i]` `slt[i]` `slt[i]u` `xor[i]` `or[i]` `and[i]` `sub` `lui` `auipc` | 2
| No-operation | "`nop`" | 2
| ALU shifts | `sll[i]` `srl[i]` `sra[i]` | 3 + 1..32; FAST_SHIFT: 4
| ALU shifts | `sll[i]` `srl[i]` `sra[i]` | 3 + 1..32; `CPU_FAST_SHIFT_EN`: 4
| Branches | `beq` `bne` `blt` `bge` `bltu` `bgeu` | taken: 6; not taken: 3
| Jump/call | `jal[r]` | 6
| Load/store | `lb` `lh` `lw` `lbu` `lhu` `sb` `sh` `sw` | 5
@ -555,11 +645,10 @@ platform-compatibility and to indicate the actual intention of the according fen
The `wfi` instruction is used to enter <<_sleep_mode>>. Executing the `wfi` instruction in user-mode
will raise an illegal instruction exception if the `TW` bit of <<_mstatus>> is set.
.Barrel Shifter
.Shifter Tuning Options
[TIP]
The shift operations are implemented as multi-cycle ALU co-process (`rtl/core/neorv32_cpu_cp_shifter.vhd`).
These operations can be accelerated (at the cost of additional logic resources) by enabling the `FAST_SHIFT_EN`
configuration option that will replace the (time-variant) bit-serial shifter by a (time-constant) barrel shifter.
The physical implementation of the bit-shifter can be tuned for certain design goals like area or throughput.
See section <<_cpu_tuning_options>> for more information.
==== `M` ISA Extension
@ -572,14 +661,14 @@ This ISA extension is implemented as multi-cycle ALU co-process (`rtl/core/neorv
[options="header", grid="rows"]
|=======================
| Class | Instructions | Execution cycles
| Multiplication | `mul` `mulh` `mulhsu` `mulhu` | 36; FAST_MUL: 4
| Multiplication | `mul` `mulh` `mulhsu` `mulhu` | 36; `CPU_FAST_MUL_EN`: 4
| Division | `div` `divu` `rem` `remu` | 36
|=======================
.DSP Blocks
.Multiplication Tuning Options
[TIP]
Multiplication operations can be accelerated (at the cost of additional logic resources) by enabling the `FAST_MUL_EN`
configuration option that will replace the (time-variant) bit-serial multiplier by (time-constant) FPGA DSP blocks.
The physical implementation of the multiplier can be tuned for certain design goals like area or throughput.
See section <<_cpu_tuning_options>> for more information.
==== `U` ISA Extension
@ -698,7 +787,7 @@ counter CSRs. Section <<_machine_counter_and_timer_csrs>> shows a list of all `Z
.Time CSRs
[NOTE]
The user-mode `time[h]` CSRs are **not implemented**. Any access will trap allowing the trap handler to
retrieve system time from the <<_machine_system_timer_mtime>>.
retrieve system time from the <<_core_local_interruptor_clint>>.
.Mandatory Extension
[NOTE]
@ -794,19 +883,19 @@ generic. This ISA extension is implemented as multi-cycle ALU co-processor (`rtl
|=======================
| Class | Instructions | Execution cycles
| Logic with negate | `andn` `orn` `xnor` | 4
| Count leading/trailing zeros | `clz` `ctz` | 6 + 1..32; FAST_SHIFT: 4
| Count population | `cpop` | 6 + 32; FAST_SHIFT: 4
| Count leading/trailing zeros | `clz` `ctz` | 6 + 1..32; `CPU_FAST_SHIFT_EN`: 4
| Count population | `cpop` | 6 + 32; `CPU_FAST_SHIFT_EN`: 4
| Integer maximum/minimum | `min[u]` `max[u]` | 4
| Sign/zero extension | `sext.b` `sext.h` `zext` | 4
| Bitwise rotation | `rol` `ror[i]` | 6 + _shift_amount_; FAST_SHIFT: 4
| Bitwise rotation | `rol` `ror[i]` | 6 + _shift_amount_; `CPU_FAST_SHIFT_EN`: 4
| OR-combine | `orc.b` | 4
| Byte-reverse | `rev8` | 4
|=======================
.Shift Operations
.shifter Tuning Options
[TIP]
Shift operations can be accelerated (at the cost of additional logic resources) by enabling the `FAST_SHIFT_EN`
configuration option that will replace the (time-variant) bit-serial shifter by a (time-constant) barrel shifter.
The physical implementation of the bit-shifter can be tuned for certain design goals like area or throughput.
See section <<_cpu_tuning_options>> for more information.
==== `Zbs` ISA Extension
@ -999,13 +1088,13 @@ core does implement must adhere to the requirements of `Zkt`.
|=======================
| Parent extension | Instructions | Data independent execution time?
.2+<| `RVI` <| `lui` `auipc` `add[i]` `slt[i][u]` `xor[i]` `or[i]` `and[i]` `sub` <| yes
<| `sll[i]` `srl[i]` `sra[i]` <| yes if `FAST_SHIFT_EN` enabled
<| `sll[i]` `srl[i]` `sra[i]` <| yes if `CPU_FAST_SHIFT_EN` enabled
| `RVM` | `mul[h]` `mulh[s]u` | yes
.2+<| `RVC` <| `c.nop` `c.addi` `c.lui` `c.andi` `c.sub` `c.xor` `c.and` `c.mv` `c.add` <| yes
<| `c.srli` `c.srai` `c.slli` <| yes if `FAST_SHIFT_EN` enabled
<| `c.srli` `c.srai` `c.slli` <| yes if `CPU_FAST_SHIFT_EN` enabled
| `RVK` | `aes32ds[m]i` `aes32es[m]i` `sha256sig*` `sha512sig*` `sha512sum*` `sm3p0` `sm3p1` `sm4ed` `sm4ks` | yes
.2+<| `RVB` <| `xperm4` `xperm8` `andn` `orn` `xnor` `pack[h]` `brev8` `rev8` <| yes
<| `ror[i]` `rol` <| yes if `FAST_SHIFT_EN` enabled
<| `ror[i]` `rol` <| yes if `CPU_FAST_SHIFT_EN` enabled
|=======================
@ -1044,28 +1133,40 @@ does not complete operation within this time window.
==== `Smpmp` ISA Extension
The NEORV32 physical memory protection (PMP) provides an elementary memory
protection mechanism that can be used to constrain read, write and execute rights of arbitrary memory regions.
The NEORV32 PMP is fully compatible to the RISC-V Privileged Architecture Specifications. In general, the PMP can
**grant permissions to user mode**, which by default has none, and can **revoke permissions from M-mode**, which
by default has full permissions. The PMP is configured via the <<_machine_physical_memory_protection_csrs>>.
The NEORV32 physical memory protection (PMP) provides an elementary memory protection mechanism that can be used
to configure read/write(execute permission of arbitrary memory regions. In general, the PMP can **grant permissions
to user mode**, which by default has none, and can **revoke permissions from M-mode**, which by default has full
permissions. The NEORV32 PMP is fully compatible to the RISC-V Privileged Architecture Specifications and is
configured via several CSRs (<<_machine_physical_memory_protection_csrs>>). Several <<_processor_top_entity_generics>>
are provided to adjust the CPU's PMP capabilities according to the application requirements (pre-synthesis):
Several <<_processor_top_entity_generics>> are provided to fine-tune the CPU's PMP capabilities:
. `PMP_NUM_REGIONS` defines the number of implemented PMP regions (0..16); setting this generic to zero will
result in absolutely no PMP logic being implemented
. `PMP_MIN_GRANULARITY` defines the minimal granularity of each region (has to be a power of 2, minimal
granularity = 4 bytes); note that a smaller granularity will lead to wider comparators and thus, to higher area footprint
and longer critical path
. `PMP_TOR_MODE_EN` controls the implementation of the top-of-region (TOR) mode (default = true); disabling this mode
will reduce area footprint
. `PMP_NAP_MODE_EN` controls the implementation of the naturally-aligned-power-of-two (NA4 and NAPOT) modes (default =
true); disabling this mode will reduce area footprint and critical path length
* `PMP_NUM_REGIONS` defines the number of implemented PMP region
* `PMP_MIN_GRANULARITY` defines the minimal granularity of each region
* `PMP_TOR_MODE_EN` controls the implementation of the top-of-region (TOR) mode
* `PMP_NAP_MODE_EN` controls the implementation of the naturally-aligned-power-of-two (NA4 and NAPOT) modes
.PMP Rules when in Debug Mode
.PMP Permissions when in Debug Mode
[NOTE]
When in debug-mode all PMP rules are ignored making the debugger have maximum access rights.
When in debug-mode all PMP rules are bypassed/ignored granting the debugger maximum access permissions.
.Protected Instruction Fetches
.PMP Time-Multiplex
[NOTE]
Instructions are executed in a multi-cycle manner. Hence, data access (load/store) and instruction fetch cannot occur
at the same time. Therefore, the PMP hardware uses only a single set of comparators for memory access permissions checks
that are switched in an iterative, time-multiplex style reducing hardware footprint by approx. 50% while maintaining
full security features and RISC-V compatibility.
.PMP Memory Accesses
[IMPORTANT]
New instruction fetches are **always triggered even when denied** by a certain PMP rule. However, the fetched instruction(s)
will not be executed and will not change CPU core state. Instead, they will raise a bus exception when reaching the CPU's
executions stage.
Load/store accesses for which there are insufficient access permission do not trigger any memory/bus accesses at all.
In contrast, instruction accesses for which there are insufficient access permission nevertheless lead to a memory/bus
access (causing potential side effects on the memory side=. However, the fetched instruction will be discarded and the
corresponding exception will still be triggered precisely.
==== `Sdext` ISA Extension
@ -1164,6 +1265,10 @@ provide custom trap codes in <<_mcause>>. These FIRQs are reserved for NEORV32 p
The following tables show all traps that are currently supported by the NEORV32 CPU. It also shows the prioritization
and the CSR side-effects.
.FIRQ Mapping
[TIP]
See section <<_neorv32_specific_fast_interrupt_requests>> for the mapping of the FIRQ channels to the according hardware modules.
**Table Annotations**
The "Prio." column shows the priority of each trap with the highest priority being 1. The "RTE Trap ID" aliases are
@ -1232,8 +1337,8 @@ written to the according CSRs when a trap is triggered:
| `TRAP_CODE_S_ACCESS` | bus timeout, bus access error or <<_smpmp_isa_extension,PMP>> rule violation during store data operation
| `TRAP_CODE_FIRQ_*` | caused by interrupt-condition of **processor-internal modules**, see <<_neorv32_specific_fast_interrupt_requests>>
| `TRAP_CODE_MEI` | machine external interrupt (via dedicated <<_processor_top_entity_signals>>)
| `TRAP_CODE_MSI` | machine software interrupt (via dedicated <<_processor_top_entity_signals>>)
| `TRAP_CODE_MTI` | machine timer interrupt (internal <<_machine_system_timer_mtime>> or via dedicated <<_processor_top_entity_signals>>)
| `TRAP_CODE_MSI` | machine software interrupt (internal <<_core_local_interruptor_clint>> or via dedicated <<_processor_top_entity_signals>>)
| `TRAP_CODE_MTI` | machine timer interrupt (internal <<_core_local_interruptor_clint>> or via dedicated <<_processor_top_entity_signals>>)
|=======================
.Resumable Exceptions
@ -1241,3 +1346,8 @@ written to the according CSRs when a trap is triggered:
Note that not all exceptions are resumable. For example, the "instruction access fault" exception or the "instruction
address misaligned" exception are not resumable in most cases. These exception might indicate a fatal memory hardware failure.
<<<
// ####################################################################################################################
include::cpu_dual_core.adoc[]

View file

@ -254,8 +254,8 @@ Machine-mode software can discover available `Z*` _sub-extensions_ (like `Zicsr`
[options="header",grid="rows"]
|=======================
| Bit | Name [C] | R/W | Function
| 3 | `CSR_MIE_MSIE` | r/w | **MSIE**: Machine _software_ interrupt enable
| 7 | `CSR_MIE_MTIE` | r/w | **MTIE**: Machine _timer_ interrupt enable (from <<_machine_system_timer_mtime>>)
| 3 | `CSR_MIE_MSIE` | r/w | **MSIE**: Machine _software_ interrupt enable (from <<_core_local_interruptor_clint>>)
| 7 | `CSR_MIE_MTIE` | r/w | **MTIE**: Machine _timer_ interrupt enable (from <<_core_local_interruptor_clint>>)
| 11 | `CSR_MIE_MEIE` | r/w | **MEIE**: Machine _external_ interrupt enable
| 31:16 | `CSR_MIE_FIRQ15E` : `CSR_MIE_FIRQ0E` | r/w | Fast interrupt channel 15..0 enable
|=======================
@ -436,8 +436,8 @@ However, any write-access will be ignored and will not cause an exception to mai
|=======================
| Bit | Name [C] | R/W | Function
| 3 | `CSR_MIP_MSIP` | r/- | **MSIP**: Machine _software_ interrupt pending, triggered by `msi_i` top port (see <<_cpu_top_entity_signals>>); _cleared by source-specific mechanism_
| 7 | `CSR_MIP_MTIP` | r/- | **MTIP**: Machine _timer_ interrupt pending, triggered by `mei_i` top port (see <<_cpu_top_entity_signals>>); _cleared by source-specific mechanism_
| 11 | `CSR_MIP_MEIP` | r/- | **MEIP**: Machine _external_ interrupt pending, triggered by `mti_i` top port (see <<_cpu_top_entity_signals>>) or by the processor-internal <<_machine_system_timer_mtime>>; _cleared by source-specific mechanism_
| 7 | `CSR_MIP_MTIP` | r/- | **MTIP**: Machine _timer_ interrupt pending, triggered by `mei_i` top port (see <<_cpu_top_entity_signals>>)or by the processor-internal <<(from <<_core_local_interruptor_clint>>)>>; _cleared by source-specific mechanism_
| 11 | `CSR_MIP_MEIP` | r/- | **MEIP**: Machine _external_ interrupt pending, triggered by `mti_i` top port (see <<_cpu_top_entity_signals>>) or by the processor-internal <<(from <<_core_local_interruptor_clint>>)>>; _cleared by source-specific mechanism_
| 31:16 | `CSR_MIP_FIRQ15P` : `CSR_MIP_FIRQ0P` | r/- | **FIRQxP**: Fast interrupt channel 15..0 pending, see <<_neorv32_specific_fast_interrupt_requests>>; _cleared by source-specific mechanism_
|=======================
@ -625,7 +625,8 @@ implementation of the according modes.
.`time[h]` CSRs (Wall Clock Time)
[IMPORTANT]
The NEORV32 does not implement the user-mode `time[h]` registers. Any access to these registers will trap.
It is recommended that the trap handler software provides a means of accessing the platform-defined <<_machine_system_timer_mtime>>.
It is recommended that the trap handler software provides a means of accessing the machine timer oft the
<<_core_local_interruptor_clint>>.
.Instruction Retired Counter Increment
[NOTE]
@ -903,8 +904,8 @@ NEORV32 as BCD-coded number (example: `mimpid = 0x01020312` → 01.02.03.12 →
| Address | `0xf14`
| Reset value | `DEFINED`
| ISA | `Zicsr`
| Description | The `mhartid` CSR is read-only and provides the core's hart ID,
which is assigned via the `HW_THREAD_ID` top generic (<<_processor_top_entity_generics>>).
| Description | The `mhartid` CSR is read-only and provides the core's hart ID. For a multi-core system each
core's hart ID is unique starting at 0 for the first core.
|=======================
@ -979,9 +980,10 @@ discover ISA sub-extensions and CPU configuration options
| 23 | `CSR_MXISA_ZBB` | r/- | <<_zbb_isa_extension>> available
| 24 | `CSR_MXISA_ZBS` | r/- | <<_zbs_isa_extension>> available
| 25 | `CSR_MXISA_ZALRSC` | r/- | <<_zalrsc_isa_extension>> available
| 27:26 | - | r/- | _reserved_, hardwired to zero
| 28 | `CSR_MXISA_RFHWRST` | r/- | full hardware reset of register file available when set (`REGFILE_HW_RST`)
| 29 | `CSR_MXISA_FASTMUL` | r/- | fast multiplication available when set (`FAST_MUL_EN`)
| 30 | `CSR_MXISA_FASTSHIFT` | r/- | fast shifts available when set (`FAST_SHIFT_EN`)
| 28:26 | - | r/- | _reserved_, hardwired to zero
| 27 | `CSR_MXISA_CLKGATE` | r/- | sleep-mode clock gating implemented when set (`CPU_CLOCK_GATING_EN`), see <<_cpu_tuning_options>>
| 28 | `CSR_MXISA_RFHWRST` | r/- | full hardware reset of register file available when set (`CPU_RF_HW_RST_EN`), see <<_cpu_tuning_options>>
| 29 | `CSR_MXISA_FASTMUL` | r/- | fast multiplication available when set (`CPU_FAST_MUL_EN`), see <<_cpu_tuning_options>>
| 30 | `CSR_MXISA_FASTSHIFT` | r/- | fast shifts available when set (`CPU_FAST_SHIFT_EN`), see <<_cpu_tuning_options>>
| 31 | `CSR_MXISA_IS_SIM` | r/- | set if CPU is being **simulated** (⚠️ not guaranteed)
|=======================

View file

@ -0,0 +1,82 @@
:sectnums:
=== Dual-Core Configuration
.Hardware Requirements
[IMPORTANT]
The SMP dual-core configuration requires the <<_core_local_interruptor_clint>> to be implemented.
Optionally, the CPU core can be implemented as **symmetric multiprocessing (SMP) dual-core** system.
This dual-core configuration is enabled by the `DUAL_CORE_EN` <<_processor_top_entity_generics, top generic>>.
When enabled, two _core complexes_ are implemented. Each core complex consists of a CPU core and optional
instruction (`I$`) and data (`D$`) caches. Similar to the single-core <<_bus_system>>, the instruction and
data interfaces are switched into a single bus interface by a prioritizing bus switch. The bus interfaces
of both core complexes are further switched into a single system bus using a round-robin arbiter.
image::smp_system.png[align=center]
Both CPU cores are fully identical and use the same configuration provided by the according
<<_processor_top_entity_generics, top generics>>. However, each core can be identified by the according
"hart ID" that can be retrieved from the <<_mhartid>> CSR. CPU core 0 (the _primary_ core) has `mhartid = 0`
while core 1 (the _secondary_ core) has `mhartid = 1`.
The following table summarizes the most important aspects when using the dual-core configuration.
[cols="<2,<10"]
[grid="rows"]
|=======================
| **Debugging** | A special SMP openOCD script (`sw/openocd/openocd_neorv32.dual_core.cfg`) is required to
debug both cores at one. SMP-debugging is fully supported by RISC-V gdb port.
| **Clock and reset** | Both cores use the same global processor clock and reset. If <<_cpu_clock_gating>>
is enabled the clock of each core can be individually halted by putting it into <<_sleep_mode>>.
| **Address space** | Both cores have access to the same <<_address_space>>.
| **Interrupts** | All <<_processor_interrupts>> are routed to both cores. Hence, each core has access to
all <<_neorv32_specific_fast_interrupt_requests>> (FIRQs). Additionally, the RISC-V machine-level _external
interrupt_ (via the top `mext_irq_i` port) is also send to both cores. In contrast, the RISC-V machine level
_software_ and _timer_ interrupts are exclusive for each core (provided by the <<_core_local_interruptor_clint>>).
| **RTE** | The <<_neorv32_runtime_environment>> also supports the dual-core configuration. However, it needs
to be explicitly initialized on each core individually. The RTE trap handling provides a individual handler
tables for each core.
| **Memory** | Each core has its own stack. The top of stack of core 0 is defined by the <<_linker_script>>
while the top of stack of core 1 has to be explicitly defined by core 0 (see <<_dual_core_boot>>). Both
cores share the same heap, `.data` and `.bss` sections.
| **Constructors and destructors** | Constructors and destructors are executed on core 0 only.
(see )
| **Bootloader** | Only core 0 will boot and execute the bootloader while core 1 is held in standby.
| **Booting** | See next section <<_dual_core_boot>>.
|=======================
.Dual-Core Example
[TIP]
A simple dual-core example setup / test program can be found in `sw/example/demo_dual_core`.
==== Dual-Core Boot
After reset both cores start booting. However, core 1 will always (regardless of the boot configuration) enter
sleep mode inside the default <<_start_up_code_crt0>> that is linked with any compiled application. The primary
core (core 0) will continue booting executing either the <<_bootloader>> or the pre-installed image in the
internal instruction memory (depending on the <<_boot_configuration>>).
To boot-up core 1 the primary core has to use a special library function provided by the NEORV32 runtime
environment (RTE):
.CPU Core 1 launch function prototype (note that this function can only be executed on core 0)
[source,c]
----
int neorv32_rte_smp_launch(void (*entry_point)(void), uint8_t* stack_memory, size_t stack_size_bytes);
----
When executed, core 0 will populate a configuration structure in main memory that contain the entry point
for core 1 (via `entry_point`) and the actual stack configuration (via `stack_memory` and `stack_size_bytes`).
.Core 1 Stack Memory
[NOTE]
The memory for the stack of core 1 (`stack_memory`) can be either statically allocated (i.e. a global
volatile memory array; placed in the `.data` or `.bss` section of core 0) or dynamically allocated
(using `malloc`; placed on the heap of core 0). In any case the memory should be aligned to a 16-byte
boundary.´
After that, the primary core triggers the _machine software interrupt_ of core 1 using the
<<_core_local_interruptor_clint>>. Core 1 wakes up from sleep mode, consumes the configuration structure and
finally starts executing at the provided entry point. When `neorv32_rte_smp_launch()` returns (with no error
code) the secondary core is online and running.

View file

@ -2,15 +2,18 @@
:sectnums:
== On-Chip Debugger (OCD)
The NEORV32 Processor features an _on-chip debugger_ (OCD) implementing the **execution-based debugging** scheme
compatible to the **Minimal RISC-V Debug Specification**. A copy of the specification is available in `docs/references`.
The on-chip debugger is implemented via the <<_processor_top_entity_generics, `OCD_EN`>> processor top generic.
The NEORV32 Processor features an _on-chip debugger_ (OCD) compatible to the **Minimal RISC-V Debug Specification**
implementing the **execution-based debugging** scheme. A copy of the specification is available in `docs/references`.
The on-chip debugger is implemented if the <<_processor_top_entity_generics, `OCD_EN`>> processor top generic is set
to `true`.
**Key Features**
* standard 4-wire JTAG access port
* debugging of up to 4 CPU cores ("harts")
* full control of the CPU: halting, single-stepping and resuming
* indirect access to all core registers and the entire processor address space (via program buffer)
* execution of arbitrary programs via the program buffer
* compatible with upstream OpenOCD and GDB
* optional trigger module for hardware breakpoints
* optional authentication for increased security
@ -21,46 +24,38 @@ A simple example on how to use NEORV32 on-chip debugger in combination with Open
section https://stnolting.github.io/neorv32/ug/#_debugging_using_the_on_chip_debugger[Debugging using the On-Chip Debugger]
of the User Guide.
**Section Structure**
* <<_debug_transport_module_dtm>>
* <<_debug_module_dm>>
* <<_debug_authentication>>
* <<_cpu_debug_mode>>
* <<_trigger_module>>
The NEORV32 on-chip debugger is based on five hardware modules:
**Overview**
.NEORV32 on-chip debugger complex
image::neorv32_ocd_complex.png[align=center]
The NEORV32 on-chip debugger is based on five hardware modules:
[start=1]
. <<_debug_transport_module_dtm>>: JTAG access tap to allow an external adapter to interface with the _debug module (DM)_.
. <<_debug_module_dm>>: RISC-V debug module that is configured by the DTM. From the CPU's perspective 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.
. <<_debug_authentication>>: Authenticator module to secure on-chip debugger access. This module implements a very simple
authentication mechanism as example. Users can modify/replace this default logic to implement arbitrary authentication mechanism.
. <<_cpu_debug_mode>> ISA extension: This ISA extension provides the "debug execution mode" as another operation mode that is
used to execute the park loop code from the DM. This mode also provides additional CSRs and instructions.
. CPU <<_trigger_module>>: This module provides a single _hardware_ breakpoint.
. <<_debug_transport_module_dtm>>: JTAG access tap to allow an external adapter to interface with the _debug module (DM)_.
. <<_debug_module_dm>>: The RISC-V debug module is the main bridge between the external debugger and the processor being
debugged. It provides a _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 between the debugger and the CPU.
. <<_debug_authentication>>: Authenticator module to secure on-chip debugger access. By default this module implements a
very simple authentication mechanism as example. Users can modify/replace this default logic to implement arbitrary
authentication mechanism.
. <<_cpu_debug_mode>> ISA extension: This ISA extension provides the "debug execution mode" as another CPU operation mode
that is used to execute the park loop code from the DM. This mode also provides additional CSRs and instructions.
. CPU <<_trigger_module>>: This module provides a single _hardware breakpoint_.
**Theory of Operation**
When debugging the system using the OCD, the debugger (like GDB) issues a halt request to the CPU to make the it enter
_debug mode_. In this mode the application-defined architectural state of the system/CPU is "frozen" so the debugger
can monitor it without interfering with the actual application. However, the OCD can also modify the entire architectural
state at any time. While in debug mode, the debugger has full control over the entire CPU and processor operating at
highest-privileged mode.
When debugging the system using the OCD, the external debugger (e.g. GDB) issues a halt request to the CPU to make it
enter so-called _debug mode_. In this mode the application-defined architectural state of the system/CPU is "frozen" so
the debugger can monitor it without interfering with the actual application. However, the OCD can also modify the entire
architectural state at any time. While in debug mode, the debugger has full control over the entire CPU core.
While in debug mode, the CPU executes the "park loop" code from the code ROM of the debug module (DM).
This park loop implements an endless loop, where the CPU polls a memory-mapped <<_status_register>> that is
controlled by the 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 or to signal that an exception has been
raised while being in debug mode.
After halting, the CPU executes the "park loop" code from the code ROM of the debug module (DM). This park loop implements
an endless loop that is used to poll a memory-mapped <<_status_register>> of the DM. The flags in this register are used to
communicate requests from the DM and to acknowledge their processing 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 or to signal that an exception has been raised while being in debug mode.
<<<
@ -68,10 +63,10 @@ raised while being in debug mode.
:sectnums:
=== Debug Transport Module (DTM)
The debug transport module "DTM" (VHDL module: `rtl/core/neorv32_debug_dtm.vhd`) provides a standard 4-wire JTAG test
access port ("tap") via the following top-level ports:
The debug transport module "DTM" (VHDL module: `rtl/core/neorv32_debug_dtm.vhd`) provides a bridge between a standard 4-wire
JTAG test access port ("tap") and the internal debug module interface.
.JTAG top level signals
.JTAG Top Level Signals of the DTM
[cols="^2,^2,^2,<8"]
[options="header",grid="rows"]
|=======================
@ -84,35 +79,46 @@ access port ("tap") via the following top-level ports:
.Maximum JTAG Clock
[IMPORTANT]
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`).
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`).
.JTAG TAP Reset
[NOTE]
The NEORV32 JTAG TAP does not provide a dedicated reset signal ("TRST"). However, the missing TRST is not a problem,
since JTAG-level resets can be triggered using with TMS signaling.
The NEORV32 JTAG TAP does not provide a dedicated reset signal ("TRST").
However, JTAG-level resets can be triggered using TMS signaling.
.Maintaining JTAG Chain
.Maintaining the JTAG Chain
[NOTE]
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.
JTAG accesses are based on a single 5-bit _instruction register_ `IR` 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:
The DTM implement a single 5-bit _instruction register_ `IR` 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 all available data registers and their addresses:
.JTAG TAP registers
[cols="^2,^2,^2,<8"]
[options="header",grid="rows"]
|=======================
| Address (via `IR`) | Name | Size (bits) | Description
| `00001` | `IDCODE` | 32 | identifier, version and part ID fields are hardwired to zero, manufacturer ID is assigned via the <<_processor_top_entity_generics, `JEDEC_ID`>> generic
| `00001` | `IDCODE` | 32 | identification code (see below)
| `10000` | `DTMCS` | 32 | debug transport module control and status register (see below)
| `10001` | `DMI` | 41 | debug module interface: 7-bit address, 32-bit read/write data, 2-bit operation (`00` = NOP; `10` = write; `01` = read)
| `10001` | `DMI` | 41 | debug module interface (see below)
| others | `BYPASS` | 1 | default JTAG bypass register
|=======================
.`IDCODE` - DTM Identification Code Register
[cols="^2,^3,^1,<8"]
[options="header",grid="rows"]
|=======================
| Bit(s) | Name | R/W | Description
| 31:28 | `version` | r/- | version ID, hardwired to zero
| 27:12 | `partid` | r/- | part ID, hardwired to zero
| 11:1 | `manid` | r/- | JEDEDC manufacturer ID, assigned via the <<_processor_top_entity_generics, `JEDEC_ID`>> generic
| 0 | - | r/- | hardwired to `1`
|=======================
.`DTMCS` - DTM Control and Status Register
[cols="^2,^3,^1,<8"]
[options="header",grid="rows"]
@ -128,6 +134,16 @@ register. The following table shows the available data registers and their addre
| 3:0 | `version` | r/- | `0001` = DTM is compatible to RISC-V debug spec. versions v0.13 and v1.0
|=======================
.`DMI` - DTM Debug Module Interface Register
[cols="^2,^3,^1,<8"]
[options="header",grid="rows"]
|=======================
| Bit(s) | Name | R/W | Description
| 40:34 | `address` | r/w | 7-bit address, see <<_dm_registers>>
| 33:2 | `data` | r/w | 32-bit to write/read to/from the addresses DM register
| 1:0 | `command` | r/w | 2-bit operation (`00` = NOP; `10` = write; `01` = read)
|=======================
<<<
// ####################################################################################################################
@ -143,14 +159,14 @@ It supports the following features:
* Provides abstract read and write access to the halted hart's general purpose registers.
* Provides access to a reset signal that allows debugging from the very first instruction after reset.
* Provides a _program buffer_ to force the hart to execute arbitrary instructions.
* Allows memory access from a hart's point of view.
* Allows memory accesses (to the entire address space) from a hart's point of view.
* Optionally implements an authentication mechanism to secure on-chip debugger access.
The NEORV32 DM follows the "Minimal RISC-V External Debug Specification" to provide full debugging capabilities while
keeping resource/area requirements at a minimum. It implements the **execution based debugging scheme** for a
single hart and provides the following architectural core features:
keeping resource/area requirements at a minimum. It implements the **execution based debugging scheme** for up to
four individual CPU cores ("harts") and provides the following architectural core features:
* program buffer with 2 entries and an implicit `ebreak` instruction
* program buffer with 2 entries and an implicit `ebreak` instruction at the end
* indirect bus access via the CPU using the program buffer
* abstract commands: "access register" plus auto-execution
* halt-on-reset capability
@ -162,7 +178,7 @@ The NEORV32 DM complies to the RISC-V DM spec version 1.0.
From the DTM's point of view, the DM implements a set of <<_dm_registers>> that are used to control and monitor the
debugging session. From the CPU's point of view, the DM implements several memory-mapped registers that are used for
communicating debugging control and status (<<_dm_cpu_access>>).
communicating data, instructions, debugging control and status (<<_dm_cpu_access>>).
:sectnums:
@ -172,15 +188,15 @@ The DM is controlled via a set of registers that are accessed via the DTM. The f
.Unimplemented Registers
[NOTE]
Write accesses to registers that are not implemented are simply ignored and read accesses
to these registers will always return zero.
Write accesses to registers that are not implemented are simply ignored and read accesses to these
registers will always return zero. In both cases no error condition is signaled to the DTM.
.Available DM registers
[cols="^2,^3,<7"]
[options="header",grid="rows"]
|=======================
| Address | Name | Description
| 0x04 | <<_data0>> | Abstract data 0, used for data transfer between debugger and processor
| 0x04 | <<_data0>> | Abstract data register 0
| 0x10 | <<_dmcontrol>> | Debug module control
| 0x11 | <<_dmstatus>> | Debug module status
| 0x12 | <<_hartinfo>> | Hart information
@ -192,6 +208,7 @@ to these registers will always return zero.
| 0x21 | <<_progbuf, `progbuf1`>> | Program buffer 1
| 0x30 | <<_authdata>> | Data to/from the authentication module
| 0x38 | `sbcs` | System bus access control and status; reads as zero to indicate there is **no** system bus access
| 0x40 | <<_haltsum0>> | Hart halt summary
|=======================
@ -223,12 +240,19 @@ are configured as "zero" and are read-only. Writing '1' to these bits/fields wil
[cols="^1,^2,^1,<8"]
[options="header",grid="rows"]
|=======================
| Bit | Name [RISC-V] | R/W | Description
| 31 | `haltreq` | -/w | set/clear hart halt request
| 30 | `resumereq` | -/w | request hart to resume
| 28 | `ackhavereset` | -/w | write `1` to clear `*havereset` flags
| 1 | `ndmreset` | r/w | put whole system (except OCD) into reset state when `1`
| 0 | `dmactive` | r/w | DM enable; writing `0`-`1` will reset the DM
| Bit | Name [RISC-V] | R/W | Description
| 31 | `haltreq` | -/w | set/clear hart halt request
| 30 | `resumereq` | -/w | request hart to resume
| 28 | `ackhavereset` | -/w | write `1` to clear `*havereset` flags
| 27 | - | r/- | reserved, hardwired to zero
| 26 | `hasel` | r/- | `0`: only a single hart can be selected at once
| 25:16 | `hartsello` | r/w | hart select; only the lowest 3 bits are implemented
| 15:6 | `hartselhi` | r/- | hardwired to zero
| 5:4 | - | r/- | reserved, hardwired to zero
| 3 | `setresethaltreq` | r/- | `0`: halt-on-reset not implemented
| 2 | `clrresethaltreq` | r/- | `0`: halt-on-reset not implemented
| 1 | `ndmreset` | r/w | put whole system (except OCD) into reset state when `1`
| 0 | `dmactive` | r/w | DM enable; writing `0`-`1` will reset the DM
|=======================
@ -251,17 +275,17 @@ are configured as "zero" and are read-only. Writing '1' to these bits/fields wil
| 31:23 | _reserved_ | reserved; zero
| 22 | `impebreak` | `1`: indicates an implicit `ebreak` instruction after the last program buffer entry
| 21:20 | _reserved_ | reserved; zero
| 19 | `allhavereset` .2+| `1` when the hart is in reset
| 19 | `allhavereset` .2+| `1` when the selected hart is in reset state
| 18 | `anyhavereset`
| 17 | `allresumeack` .2+| `1` when the hart has acknowledged a resume request
| 17 | `allresumeack` .2+| `1` when the selected hart has acknowledged a resume request
| 16 | `anyresumeack`
| 15 | `allnonexistent` .2+| zero to indicate the hart is always existent
| 15 | `allnonexistent` .2+| `1` when the selected hart is not available
| 14 | `anynonexistent`
| 13 | `allunavail` .2+| `1` when the DM is disabled to indicate the hart is unavailable
| 13 | `allunavail` .2+| `1` when the DM is disabled to indicate the selected hart is unavailable
| 12 | `anyunavail`
| 11 | `allrunning` .2+| `1` when the hart is running
| 11 | `allrunning` .2+| `1` when the selected hart is running
| 10 | `anyrunning`
| 9 | `allhalted` .2+| `1` when the hart is halted
| 9 | `allhalted` .2+| `1` when the selected hart is halted
| 8 | `anyhalted`
| 7 | `authenticated` | set if authentication passed; see <<_debug_authentication>>
| 6 | `authbusy` | set if authentication is busy, see <<_debug_authentication>>
@ -410,58 +434,72 @@ hart's GPRs x0 - x15/31 (abstract command register index `0x1000` - `0x101f`).
|======
:sectnums!:
===== **`haltsum0`**
[cols="4,27,>7"]
[frame="topbot",grid="none"]
|======
| 0x30 | **Halt summary 0** | `haltsum0`
3+| Reset value: `0x00000000`
3+| Each bit corresponds to a hart being halted. Only the lowest four bits are implemented.
|======
:sectnums:
==== DM CPU Access
From the CPU's perspective the DM acts like another memory-mapped peripheral. It occupies 256 bytes of the CPU's address
space starting at address `base_io_dm_c`. 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 and are mirrored several times to fill
the entire section.
From the CPU's perspective the DM acts like another memory-mapped peripheral. It occupies 512 bytes of the CPU's
address space starting at address `base_io_dm_c` (`0xffff0000`). This address space is divided into four sections
128 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 128-byte-wide
sections and are mirrored several times across the entire section.
.DM CPU Access - Address Map
[cols="^2,^2,<4"]
[options="header",grid="rows"]
|=======================
| Base address | Physical size | Description
| `0xffffff00` | 64 bytes | ROM for the "park loop" code
| `0xffffff40` | 16 bytes | Program buffer (<<_progbuf>>)
| `0xffffff80` | 4 bytes | Data buffer (<<_data0>>)
| `0xffffffc0` | 4 bytes | Control and <<_status_register>>
| `0xfffffe00` | 128 bytes | ROM for the "park loop" code (<<_code_rom>>)
| `0xfffffe80` | 16 bytes | Program buffer (<<_progbuf>>)
| `0xffffff00` | 4 bytes | Data buffer (<<_data0>>)
| `0xffffff80` | 16 bytes | Control and <<_status_register>>
|=======================
.DM Register Access
[IMPORTANT]
All memory-mapped registers of the DM can only be accessed by the CPU if it is in debug mode. Hence, the DM registers are not
visible nor accessible for normal CPU operations. Any CPU access outside of debug mode will raise a bus access fault exception.
All memory-mapped registers of the DM can only be accessed by the CPU when in debug mode. Hence, the DM registers are
not accessible for normal CPU operations. Any CPU access outside of debug mode will raise a bus access fault exception.
:sectnums:
===== Code ROM
The code ROM contain the minimal OCD firmware that implements the debuggers part loop.
.Park Loop Code Sources ("OCD Firmware")
[NOTE]
The assembly sources of the park loop code are available in `sw/ocd-firmware/park_loop.S`.
:sectnums:
===== Code ROM Entry Points
The park loop code provides two entry points where code execution can start. These are used to enter the park loop either when
an explicit debug-entry/halt request has been issued (for example a halt request) or when an exception has occurred while executing
code in debug mode.
The park loop code provides two entry points where code execution can start. These are used to enter the park loop
either when an explicit debug-entry/halt request has been issued (for example a halt request) or when an exception
has occurred while executing code in debug mode (from the profram buffer).
.Park Loop Entry Points
[cols="^6,<4"]
[options="header",grid="rows"]
|=======================
| Address | Description
| `dm_exc_entry_c` (`base_io_dm_c` + 0) | Exception entry address
| `dm_park_entry_c` (`base_io_dm_c` + 8) | Normal entry address (halt request)
| Address | Description
| `dm_exc_entry_c` (`base_io_dm_c` + 0) | Exception entry address
| `dm_park_entry_c` (`base_io_dm_c` + 16) | Normal entry address (halt request)
|=======================
When the CPU enters (via an explicit halt request from the dubber) or re-enters debug mode (for example via an `ebreak` in the
DM's program buffer), it jumps to the _normal entry point_ that is configured via the <<_cpu_top_entity_generics, `CPU_DEBUG_PARK_ADDR`>>
CPU generic. By default, this address is set to `dm_park_entry_c`, which is defined in the main
package file. If an exception is encountered during debug mode, the CPU jumps to the address of the _exception entry point_
configured via the <<_cpu_top_entity_generics, `CPU_DEBUG_EXC_ADDR`>> CPU generic. By default, this address
is set to `dm_exc_entry_c`, which is also defined in the main package file.
When the CPU enters (via an explicit halt request from the debugger) or re-enters debug mode (for example via an
`ebreak` in the DM's program buffer), it jumps to the **normal entry point** that is configured via the
<<_cpu_top_entity_generics, `CPU_DEBUG_PARK_ADDR`>> CPU generic. By default, this address is set to `dm_park_entry_c`,
which is defined in the main package file. If an exception is encountered during debug mode, the CPU jumps to the
address of the **exception entry point** configured via the <<_cpu_top_entity_generics, `CPU_DEBUG_EXC_ADDR`>> CPU
generic. By default, this address is set to `dm_exc_entry_c`, which is also defined in the main package file.
:sectnums:
@ -469,24 +507,46 @@ is set to `dm_exc_entry_c`, which is also defined in the main package file.
The status register provides a direct communication channel between the CPU's debug-mode executing the park loop
and the debugger-controlled DM. This register is used to communicate requests, which are issued by the
DM, and the according acknowledges, which are generated by the CPU.
DM, and the according acknowledges, which are generated by the CPU. The status register is sub-divided into four
consecutive memory-mapped registers.
There are only 4 bits in this register that are used to implement requests/acknowledges. Each bit is left-aligned
in one sub-byte of the entire 32-bit register. Thus, the CPU can access each bit individually using store-byte (`sb`) and
load-byte (`lb`) instructions. This eliminates the need to perform bit-masking in the park loop code resulting in less code
size and faster execution.
Starting at `0xffffff80` the status register provides a set of memory-mapped interface register whose functionality
depends on whether the CPU accesses the register in read or write mode. **Read** accesses return the **requests**
for each individual hart generated by the DM. **Write** accesses are used to **acknowledge** these requests by the
individual harts back to the DM.
.DM Status Register - CPU Access
[cols="^1,^3,^3,<8"]
For read accesses, the hart ID is used as byte offset to read the hart-specific request flags. The flags for hart 0
are located at `0xffffff80 + 0`, the flags for hart 1 are located at `0xffffff80 + 1` and so on. Hence, each hart
can use load-unsigned-byte instructions to isolate the hart specific flags.
.DM Status Register - Read Access (byte-wise access)
[cols="^2,^1,^1,^1,<6"]
[options="header",grid="rows"]
|=======================
| Bit | Name | CPU/DM access <| Description
| 0 | `sreg_halt_ack` | CPU write, DM read <| Set by the CPU when halting.
.2+| 8 | `sreg_resume_req` | DM write, CPU read <| Set by the DM to request the CPU to resume normal operation.
| `sreg_resume_ack` | CPU write, DM read <| Set by the CPU before it starts resuming.
.2+| 16 | `sreg_execute_req` | DM write, CPU read <| Set by the DM to request execution of the program buffer.
| `sreg_execute_ack` | CPU write, DM read <| Set by the CPU before it starts executing the program buffer.
| 24 | `sreg_execute_ack` | CPU write, DM read <| Set by the CPU if an exception occurs while being in debug mode.
| Address | Hart | R/W | Bits | Description
.2+| `0xffffff80` .2+| 0 .2+| r/- ^| 0 <| Resume request
^| 1 <| Execute request
.2+| `0xffffff81` .2+| 1 .2+| r/- ^| 0 <| Resume request
^| 1 <| Execute request
.2+| `0xffffff82` .2+| 2 .2+| r/- ^| 0 <| Resume request
^| 1 <| Execute request
.2+| `0xffffff83` .2+| 3 .2+| r/- ^| 0 <| Resume request
^| 1 <| Execute request
|=======================
For write accesses, four consecutive memory-mapped registers are implemented. Each individual register is used
to acknowledge a specific condition: halt, resume, execute and exception. Each hart can acknowledge the according
condition by writing its hart ID to the according register.
.DM Status Register - Write Access (word-wise access)
[cols="^2,^1,^1,<6"]
[options="header",grid="rows"]
|=======================
| Address | R/W | Bits | Description
| `0xffffff80` | r/w | 1:0 | write hart ID to send hart's HALT acknowledge
| `0xffffff84` | r/w | 1:0 | write hart ID to send hart's RESUME acknowledge
| `0xffffff88` | r/w | 1:0 | write hart ID to send hart's EXECUTE acknowledge
| `0xffffff8c` | r/w | 1:0 | write any value to send hart's EXCEPTION acknowledge
|=======================

View file

@ -203,6 +203,7 @@ neorv32_top.vhd - NEORV32 PROCESSOR/SOC TOP ENTITY
├neorv32_bus.vhd - SoC bus infrastructure modules
├neorv32_cache.vhd - Generic cache module
├neorv32_cfs.vhd - Custom functions subsystem
├neorv32_clint.vhd - Core local interruptor
├neorv32_clockgate.vhd - Generic clock gating switch
├neorv32_crc.vhd - Cyclic redundancy check unit
├neorv32_debug_dm.vhd - on-chip debugger: debug module
@ -215,7 +216,6 @@ neorv32_top.vhd - NEORV32 PROCESSOR/SOC TOP ENTITY
├neorv32_gptmr.vhd - General purpose 32-bit timer
├neorv32_imem.vhd - Generic processor-internal instruction memory
│└neorv32_application_image.vhd - IMEM application initialization image (package)
├neorv32_mtime.vhd - Machine system timer
├neorv32_neoled.vhd - NeoPixel (TM) compatible smart LED interface
├neorv32_onewire.vhd - One-Wire serial interface controller
├neorv32_package.vhd - Main VHDL package file
@ -267,7 +267,7 @@ These placeholders need to be replaced by the actual path before being used. Exa
----
NEORV32_HOME = path/to/neorv32 <1>
NEORV32_SOC_FILE = $(shell cat $(NEORV32_HOME)/rtl/file_list_soc.f) <2>
NEORV32_SOC_SRCS = $(subst NEORV32_RTL_PATH_PLACEHOLDER, $(NEORV32_HOME)/rtl, $(NEORV32_SOC_FILE))) <3>
NEORV32_SOC_SRCS = $(subst NEORV32_RTL_PATH_PLACEHOLDER, $(NEORV32_HOME)/rtl, $(NEORV32_SOC_FILE)) <3>
----
<1> Path to the NEORV32 home folder (i.e. the root folder of the GitHub repository).
<2> Load the content of the `file_list_soc.f` file-list into a new variable `NEORV32_SOC_FILE`.
@ -282,11 +282,6 @@ puts "NEORV32 source files:"
puts $file_list
----
.File-List Usage Examples
[TIP]
The provided file-list files are used by the GHDL-based simple simulation setup (`sim/ghdl.setup.sh`) as
well as by the Vivado IP packager script (`rtl/system_integration/neorv32_vivado_ip.tcl`).
<<<
// ####################################################################################################################
@ -389,7 +384,7 @@ https://stnolting.github.io/neorv32/ug/#_application_specific_processor_configur
| IO Switch | _SoC bus infrastructure_ | 217 | 0 | 0 | 0
| iCACHE | Instruction cache (2x4 blocks, 64 bytes per block) | 458 | 296 | 4096 | 0
| IMEM | Processor-internal instruction memory (16kB) | 7 | 2 | 131072 | 0
| MTIME | Machine system timer | 307 | 166 | 0 | 0
| CLINT | Core local interruptor | 307 | 166 | 0 | 0
| NEOLED | Smart LED Interface (NeoPixel/WS28128) (FIFO_depth=1) | 171 | 129 | 0 | 0
| ONEWIRE | 1-wire interface | 105 | 77 | 0 | 0
| PWM | Pulse_width modulation controller (4 channels) | 91 | 81 | 0 | 0

View file

@ -3,7 +3,7 @@
:sectnums:
== NEORV32 Processor (SoC)
The NEORV32 Processor is based on the NEORV32 CPU. Together with common peripheral
The NEORV32 Processor is build around the <<_neorv32_central_processing_unit_cpu>>. Together with common peripheral
interfaces and embedded memories it provides a RISC-V-based full-scale microcontroller-like SoC platform.
.The NEORV32 Processor (Block Diagram)
@ -19,16 +19,18 @@ image::neorv32_processor.png[align=center]
**Key Features**
* _optional_ SMP <<_dual_core_configuration>>
* _optional_ processor-internal data and instruction memories (<<_data_memory_dmem,**DMEM**>>/<<_instruction_memory_imem,**IMEM**>>)
* _optional_ caches (<<_processor_internal_instruction_cache_icache,**I-CACHE**>>, <<_processor_internal_data_cache_dcache,**D-CACHE**>>,
<<_execute_in_place_module_xip,**XIP-CACHE**>>, <<_processor_external_bus_interface_xbus,**XBUS-CACHE**>>)
* _optional_ internal bootloader (<<_bootloader_rom_bootrom,**BOOTROM**>>) with UART console & SPI flash boot option
* _optional_ machine system timer (<<_machine_system_timer_mtime,**MTIME**>>), RISC-V-compatible
* _optional_ RISC-V-compatible core local interruptor (<<_core_local_interruptor_clint,**CLINT**>>)
* _optional_ two independent universal asynchronous receivers and transmitters (<<_primary_universal_asynchronous_receiver_and_transmitter_uart0,**UART0**>>,
<<_secondary_universal_asynchronous_receiver_and_transmitter_uart1,**UART1**>>) with optional hardware flow control (RTS/CTS)
* _optional_ serial peripheral interface host controller (<<_serial_peripheral_interface_controller_spi,**SPI**>>) with 8 dedicated CS lines
* _optional_ 8-bit serial data device interface (<<_serial_data_interface_controller_spi,**SDI**>>)
* _optional_ two wire serial interface controller (<<_two_wire_serial_interface_controller_twi,**TWI**>>), compatible to the I²C standard
* _optional_ two-wire serial interface controller (<<_two_wire_serial_interface_controller_twi,**TWI**>>), compatible to the I²C standard
* _optional_ two-wire serial device controller (<<_two_wire_serial_device_controller_twd,**TWD**>>), compatible to the I²C standard
* _optional_ general purpose parallel IO port (<<_general_purpose_input_and_output_port_gpio,**GPIO**>>), 64xOut, 64xIn
* _optional_ 32-bit external bus interface, Wishbone b4 / AXI4-Lite compatible (<<_processor_external_bus_interface_xbus,**XBUS**>>)
* _optional_ watchdog timer (<<_watchdog_timer_wdt,**WDT**>>)
@ -70,7 +72,7 @@ bits/channels are hardwired to zero.
.Tri-State Interfaces
[NOTE]
Some interfaces (like the TWI and the 1-Wire bus) require explicit tri-state drivers in the final top module.
Some interfaces (like the TWI, the TWD and the 1-Wire bus) require explicit tri-state drivers in the final top module.
.Input/Output Registers
[NOTE]
@ -146,6 +148,11 @@ to all inputs and output so the synthesis tool can insert an explicit IO (bounda
| `twi_sda_o` | 1 | out | - | serial data line output (pull low only)
| `twi_scl_i` | 1 | in | `'H'` | serial clock line sense input
| `twi_scl_o` | 1 | out | - | serial clock line output (pull low only)
5+^| **<<_two_wire_serial_device_controller_twd>>**
| `twd_sda_i` | 1 | in | `'H'` | serial data line sense input
| `twd_sda_o` | 1 | out | - | serial data line output (pull low only)
| `twd_scl_i` | 1 | in | `'H'` | serial clock line sense input
| `twd_scl_o` | 1 | out | - | serial clock line output (pull low only)
5+^| **<<_one_wire_serial_interface_controller_onewire>>**
| `onewire_i` | 1 | in | `'H'` | 1-wire bus sense input
| `onewire_o` | 1 | out | - | 1-wire bus output (pull low only)
@ -156,8 +163,8 @@ to all inputs and output so the synthesis tool can insert an explicit IO (bounda
| `cfs_out_o` | 32 | out | - | custom CFS output signal conduit
5+^| **<<_smart_led_interface_neoled>>**
| `neoled_o` | 1 | out | - | asynchronous serial data output
5+^| **<<_machine_system_timer_mtime>>**
| `mtime_time_o` | 64 | out | - | MTIME system time output
5+^| **<<_core_local_interruptor_clint>>**
| `mtime_time_o` | 64 | out | - | CLINT.MTIMER system time output
5+^| **<<_external_interrupt_controller_xirq>>**
| `xirq_i` | 32 | in | `'L'` | external interrupt requests
5+^| **RISC-V Machine-Mode <<_processor_interrupts>>**
@ -204,9 +211,9 @@ The generic type "`suv(x:y)`" is an abbreviation for "`std_ulogic_vector(x downt
| Name | Type | Default | Description
4+^| **<<_processor_clocking>>**
| `CLOCK_FREQUENCY` | natural | 0 | The clock frequency of the processor's `clk_i` input port in Hertz (Hz).
| `CLOCK_GATING_EN` | boolean | false | Enable clock gating when CPU is in sleep mode (see sections <<_sleep_mode>> and <<_processor_clocking>>).
4+^| **<<_dual_core_configuration>>**
| `DUAL_CORE_EN` | boolean | false | Enable the SMP dual-core configuration.
4+^| **Core Identification**
| `HART_ID` | suv(31:0) | x"00000000" | The hart thread ID of the CPU (passed to <<_mhartid>> CSR).
| `JEDEC_ID` | suv(10:0) | "00000000000" | JEDEC ID; continuation codes plus vendor ID (passed to <<_mvendorid>> CSR and to the <<_debug_transport_module_dtm>>).
4+^| **<<_boot_configuration>>**
| `BOOT_MODE_SELECT` | natural | 0 | Boot mode select; see <<_boot_configuration>>.
@ -237,10 +244,11 @@ The generic type "`suv(x:y)`" is an abbreviation for "`std_ulogic_vector(x downt
| `RISCV_ISA_Zksh` | boolean | false | Enable <<_zksh_isa_extension>> (scalar cryptography ShangMi hash functions).
| `RISCV_ISA_Zmmul` | boolean | false | Enable <<_zmmul_isa_extension>> (hardware-based integer multiplication).
| `RISCV_ISA_Zxcfu` | boolean | false | Enable NEORV32-specific <<_zxcfu_isa_extension>> (custom RISC-V instructions).
4+^| **CPU <<_architecture>> Tuning Options**
| `FAST_MUL_EN` | boolean | false | Implement fast but large full-parallel multipliers (trying to infer DSP blocks); see section <<_cpu_arithmetic_logic_unit>>.
| `FAST_SHIFT_EN` | boolean | false | Implement fast but large full-parallel barrel shifters; see section <<_cpu_arithmetic_logic_unit>>.
| `REGFILE_HW_RST` | boolean | false | Implement full hardware reset for register file (use individual FFs instead of BRAM); see section <<_cpu_register_file>>.
4+^| **<<_cpu_tuning_options>>**
| `CPU_CLOCK_GATING_EN` | boolean | false | Implement sleep-mode clock gating; see sections <<_sleep_mode>> and <<_cpu_clock_gating>>.
| `CPU_FAST_MUL_EN` | boolean | false | Implement fast but large full-parallel multipliers (trying to infer DSP blocks); see section <<_cpu_arithmetic_logic_unit>>.
| `CPU_FAST_SHIFT_EN` | boolean | false | Implement fast but large full-parallel barrel shifters; see section <<_cpu_arithmetic_logic_unit>>.
| `CPU_RF_HW_RST_EN` | boolean | false | Implement full hardware reset for register file (use individual FFs instead of BRAM); see section <<_cpu_register_file>>.
4+^| **Physical Memory Protection (<<_smpmp_isa_extension>>)**
| `PMP_NUM_REGIONS` | natural | 0 | Number of implemented PMP regions (0..16).
| `PMP_MIN_GRANULARITY` | natural | 4 | Minimal region granularity in bytes. Has to be a power of two, min 4.
@ -280,7 +288,7 @@ The generic type "`suv(x:y)`" is an abbreviation for "`std_ulogic_vector(x downt
4+^| **Peripheral/IO Modules**
| `IO_DISABLE_SYSINFO` | boolean | false | Disable <<_system_configuration_information_memory_sysinfo>> module; ⚠️ not recommended - for advanced users only!
| `IO_GPIO_NUM` | natural | 0 | Number of general purpose input/output pairs of the <<_general_purpose_input_and_output_port_gpio>>.
| `IO_MTIME_EN` | boolean | false | Implement the <<_machine_system_timer_mtime>>.
| `IO_CLINT_EN` | boolean | false | Implement the <<_core_local_interruptor_clint>>.
| `IO_UART0_EN` | boolean | false | Implement the <<_primary_universal_asynchronous_receiver_and_transmitter_uart0>>.
| `IO_UART0_RX_FIFO` | natural | 1 | UART0 RX FIFO depth, has to be a power of two, minimum value is 1, max 32768.
| `IO_UART0_TX_FIFO` | natural | 1 | UART0 TX FIFO depth, has to be a power of two, minimum value is 1, max 32768.
@ -293,6 +301,8 @@ The generic type "`suv(x:y)`" is an abbreviation for "`std_ulogic_vector(x downt
| `IO_SDI_FIFO` | natural | 1 | Depth of the <<_serial_data_interface_controller_sdi>> FIFO. Has to be a power of two, min 1, max 32768.
| `IO_TWI_EN` | boolean | false | Implement the <<_two_wire_serial_interface_controller_twi>>.
| `IO_TWI_FIFO` | natural | 1 | Depth of the <<_two_wire_serial_interface_controller_twi>> FIFO. Has to be a power of two, min 1, max 32768.
| `IO_TWD_EN` | boolean | false | Implement the <<_two_wire_serial_device_controller_twd>>.
| `IO_TWD_FIFO` | natural | 1 | Depth of the <<_two_wire_serial_device_controller_twd>> FIFO. Has to be a power of two, min 1, max 32768.
| `IO_PWM_NUM_CH` | natural | 0 | Number of channels of the <<_pulse_width_modulation_controller_pwm>> to implement (0..16).
| `IO_WDT_EN` | boolean | false | Implement the <<_watchdog_timer_wdt>>.
| `IO_TRNG_EN` | boolean | false | Implement the <<_true_random_number_generator_trng>>.
@ -321,27 +331,13 @@ The generic type "`suv(x:y)`" is an abbreviation for "`std_ulogic_vector(x downt
The processor is implemented as fully-synchronous logic design using a single clock domain that is driven entirely
by the top's `clk_i` signal. This clock signal is used by all internal registers and memories. All of them trigger
on the **rising edge** of this clock signal - the only exception it the default <<_clock_gating>> module. External
"clocks" like the OCD's JTAG clock or the SDI's serial clock are synchronized into the processor's clock domain
before being used as "general logic signal" (and not as a dedicated clock).
on the **rising edge** of this clock signal. External "clocks" like the OCD's JTAG clock or the SDI's serial clock
are synchronized into the processor's clock domain before being used as "general logic signal" (and not as a dedicated clock).
==== Clock Gating
The single clock domain of the processor can be split into an always-on clock domain and a switchable clock domain.
The switchable clock domain is used to clock the CPU core, the CPU's bus switch and - if implemented - the caches.
This domain can be deactivated to reduce power consumption. The always-on clock domain is used to clock all other
processor modules like peripherals, memories and IO devices. Hence, these modules can continue operation (e.g. a
timer keeps running) even if the CPU is shut down.
The splitting into two clock domain is enabled by the `CLOCK_GATING_EN` generic (<<_processor_top_entity_generics>>).
When enabled, a generic clock switching gate is added to decouple the switchable clock from the always-on clock domain
(VHDL file `neorv32_clockgate.vhd`). Whenever the CPU enters <<_sleep_mode>> the CPU clock domain ist shut down.
.Clock Switch Hardware
.CPU Clock Gating
[NOTE]
By default, a generic clock gate is used (`rtl/core/neorv32_clockgate.vhd`) to shut down the CPU clock.
Especially for FPGA setups it is highly recommended to replace this default version by a technology-specific primitive
or macro wrapper to improve efficiency (clock skew, global clock tree usage, etc.).
The CPU core provides an optional clock-gating feature to switch off large parts of the core when sleep mode is entered.
See section <<_cpu_clock_gating>> for more information.
==== Peripheral Clocks
@ -427,8 +423,8 @@ specifications. However, bare-metal system can also repurpose these interrupts.
[options="header",grid="rows"]
|=======================
| Top signal | Description
| `mtime_irq_i` | Machine timer interrupt from _processor-external_ MTIME unit (`MTI`). This IRQ is only available if the processor-internal <<_machine_system_timer_mtime>> unit is not implemented.
| `msw_irq_i` | Machine software interrupt (`MSI`). This interrupt is used for inter-processor interrupts in multi-core systems. However, it can also be used for any custom purpose.
| `mtime_irq_i` | Machine timer interrupt from _processor-external_ CLINT (`MTI`). This IRQ is only available if the processor-internal <<_core_local_interruptor_clint>> unit is not implemented.
| `msw_irq_i` | Machine software interrupt from _processor-external_ CLINT (`MSI`). This IRQ is only available if the processor-internal <<_core_local_interruptor_clint>> unit is not implemented.
| `mext_irq_i` | Machine external interrupt (`MEI`). This interrupt is used for any processor-external interrupt source (like a platform interrupt controller).
|=======================
@ -449,7 +445,7 @@ table (the channel number also corresponds to the according FIRQ priority: 0 = h
[options="header",grid="rows"]
|=======================
| Channel | Source | Description
| 0 | <<_true_random_number_generator_trng,TRNG>> | TRNG data available interrupt
| 0 | <<_two_wire_serial_device_controller_twd,TWD>> | TWD FIFO level interrupt
| 1 | <<_custom_functions_subsystem_cfs,CFS>> | Custom functions subsystem (CFS) interrupt (user-defined)
| 2 | <<_primary_universal_asynchronous_receiver_and_transmitter_uart0,UART0>> | UART0 RX FIFO level interrupt
| 3 | <<_primary_universal_asynchronous_receiver_and_transmitter_uart0,UART0>> | UART0 TX FIFO level interrupt
@ -474,55 +470,47 @@ table (the channel number also corresponds to the according FIRQ priority: 0 = h
=== Address Space
As a 32-bit architecture the NEORV32 can access a 4GB physical address space. By default, this address space is
split into six main regions. Each region provides specific _physical memory attributes_ ("PMAs") that define
the access capabilities (`rwxac`; `r` = read permission, `w` = write permission, `x` - execute permission,
`a` = atomic access support, `c` = cached CPU access, `p` = privileged access only).
split into four main regions. All accesses to "unmapped" addresses (a.k.a. "the void") are redirected to the
<<_processor_external_bus_interface_xbus>>. For example, if the internal IMEM is disabled, the accesses to the
_entire_ address space between `0x00000000` and `0x7FFFFFFF` are converted into XBUS requests. If the XBUS interface
is not enabled any access to the void will raise a bus error exception.
.NEORV32 Processor Address Space (Default Configuration)
image::address_space.png[900]
.The "Void" (Unmapped Addresses)
[NOTE]
All accesses to "unmapped" addresses (= "void") are redirected to the <<_processor_external_bus_interface_xbus>>.
For example, if the internal IMEM is disabled, the accesses to the _entire_ address space between `0x00000000` and
`0x7FFFFFFF` are converted into XBUS requests. If the XBUS interface is not enabled any access to the void will
raise a bus error exception.
.Main Address Regions
[cols="<1,^4,^2,<7"]
[options="header",grid="rows"]
|=======================
| # | Region | PMAs | Description
| 1 | Internal IMEM address space | `rwxac-` | For instructions (=code) and constants; mapped to the internal <<_instruction_memory_imem>>.
| 2 | Internal DMEM address space | `rwxac-` | For application runtime data (heap, stack, etc.); mapped to the internal <<_data_memory_dmem>>).
| 3 | Memory-mapped XIP flash | `r-xac-` | Memory-mapped access to the <<_execute_in_place_module_xip>> SPI flash.
| 4 | Bootloader address space | `r-xa-p` | Read-only memory for the internal <<_bootloader_rom_bootrom>> containing the default <<_bootloader>>.
| 5 | IO/peripheral address space | `rwxa-p` | Processor-internal peripherals / IO devices.
| 6 | The "**void**" | `rwxac-` | Unmapped address space. All accesses to this region(s) are redirected to the <<_processor_external_bus_interface_xbus>> (if implemented).
|=======================
.Privileged IO and BOOTROM Access Only
[IMPORTANT]
Only privileged accesses (M-mode) to the IO/peripheral and bootloader address spaces are allowed.
If an unprivileged application tries to access this address space a bus access error exception is raised.
Each region provides specific _physical memory attributes_ ("PMAs") that define the access capabilities (`rwxac`;
`r` = read access, `w` = write access, `x` - execute access, `a` = atomic access, `c` = cached CPU access).
.Custom PMAs
[TIP]
Custom physical memory attributes enforced by the CPU's _physcial memory protection_ (<<_smpmp_isa_extension>>)
can be used to further constrain the physical memory attributes.
.Main Address Regions
[cols="<1,^4,^2,<7"]
[options="header",grid="rows"]
|=======================
| # | Region | PMAs | Description
| 1 | Internal IMEM address space | `rwxac` | For instructions / code and constants; mapped to the internal <<_instruction_memory_imem>> if implemented.
| 2 | Internal DMEM address space | `rwxac` | For application runtime data (heap, stack, etc.); mapped to the internal <<_data_memory_dmem>>) if implemented.
| 3 | Memory-mapped XIP flash | `r-xac` | Transparent memory-mapped access to an external <<_execute_in_place_module_xip>> SPI flash.
| 4 | IO/peripheral address space | `rwxa-` | Processor-internal peripherals / IO devices including the <<_bootloader_rom_bootrom>>.
| - | The "**void**" | `rwxa[c]` | Unmapped address space. All accesses to this region(s) are redirected to the <<_processor_external_bus_interface_xbus>> if implemented.
|=======================
:sectnums:
==== Bus System
The CPU can access all of the 32-bit address space from the instruction fetch interface and also from the data access
interface. Both CPU interfaces can be equipped with optional caches (<<_processor_internal_data_cache_dcache>> and
<<_processor_internal_instruction_cache_icache>>). The two CPU interfaces are multiplexed by a simple bus switch into
a _single processor-internal bus_. Optionally, this bus is further switched by another instance of the bus switch so the
<<_direct_memory_access_controller_dma>> controller can also access the entire address space. Accesses via the
resulting SoC bus are split by the <<_bus_gateway>> that redirects accesses to the according main address regions
(see table above). Accesses to the processor-internal IO/peripheral devices are further redirected via a
dedicated <<_io_switch>>.
The CPU provides individual interfaces for instruction fetch and data access. It can can access all of the 32-bit
address space from each of the interface. Both of them can be equipped with optional caches (<<_processor_internal_data_cache_dcache>>
and <<_processor_internal_instruction_cache_icache>>).
The two CPU interfaces are multiplexed by a simple bus switch into a _single processor-internal bus_. Optionally,
this bus is further multiplexed by another instance of the bus switch so the <<_direct_memory_access_controller_dma>>
controller can also access the entire address space. Accesses via the resulting SoC bus are split by the <<_bus_gateway>>
that redirects accesses to the according main address regions (see table above). Accesses to the processor-internal
IO/peripheral devices are further redirected via a dedicated <<_io_switch>>.
.Processor-Internal Bus Architecture
image::neorv32_bus.png[1300]
@ -535,35 +523,36 @@ The components of the processor's bus system infrastructure are located in `rtl/
[TIP]
See sections CPU <<_architecture>> and <<_bus_interface>> for more information regarding the CPU bus accesses.
.SMP Dual-Core Configuration
[TIP]
The dual-core configuration adds a second CPU core complex in parallel to the first one.
See section <<_dual_core_configuration>> for more information.
:sectnums:
==== Bus Gateway
The central bus gateway serves two purposes: **redirect** core accesses to the according modules (e.g. memory accesses
vs. memory-mapped IO accesses) and **monitor** all bus transactions. The redirection of access request is based on a
The central bus gateway serves two purposes: it **redirects** accesses to the according modules (e.g. memory accesses
vs. memory-mapped IO accesses) and also **monitors** all bus transactions. The redirection of access request is based on a
customizable memory map implemented via VHDL constants in the main package file (`rtl/core/neorv323_package.vhd`):
.Main Address Regions Configuration in the VHDL Package File
[source,vhdl]
----
-- Main Address Regions ---
constant mem_imem_base_c : std_ulogic_vector(31 downto 0) := x"00000000";
constant mem_dmem_base_c : std_ulogic_vector(31 downto 0) := x"80000000";
constant mem_xip_base_c : std_ulogic_vector(31 downto 0) := x"e0000000";
constant mem_imem_base_c : std_ulogic_vector(31 downto 0) := x"00000000"; -- IMEM size via generic
constant mem_dmem_base_c : std_ulogic_vector(31 downto 0) := x"80000000"; -- DMEM size via generic
constant mem_xip_base_c : std_ulogic_vector(31 downto 0) := x"e0000000"; -- page (4 MSBs) only!
constant mem_xip_size_c : natural := 256*1024*1024;
constant mem_boot_base_c : std_ulogic_vector(31 downto 0) := x"ffffc000";
constant mem_boot_size_c : natural := 8*1024;
constant mem_io_base_c : std_ulogic_vector(31 downto 0) := x"ffffe000";
constant mem_io_size_c : natural := 8*1024;
constant mem_io_base_c : std_ulogic_vector(31 downto 0) := x"ffe00000";
constant mem_io_size_c : natural := 32*64*1024; -- = 32 * iodev_size_c
----
Besides the delegation of bus requests the gateway also implements a bus monitor (aka "the bus keeper") that tracks all
active bus transactions to ensure _safe_ and _deterministic_ operations.
Whenever a memory-mapped device is accessed (a real memory, a memory-mapped IO or some processor-external module) the bus
monitor starts an internal timer. The accessed module has to respond ("ACK") to the bus request within a specific
**time window**. This time window is defined by a global constant in the processor's VHDL package file
(`rtl/core/neorv323_package.vhd`).
Besides the redirecting of bus requests the gateway also implements a bus monitor (aka "the bus keeper") that tracks all
active bus transactions to ensure _safe_ and _deterministic_ operations. Whenever a memory-mapped device is accessed (a
real memory, a memory-mapped IO or some processor-external module) the bus monitor starts an internal countdown. The
accessed module has to respond ("ACK") to the bus request within a bound **time window**. This time window is defined
by a global constant in the processor's VHDL package file (`rtl/core/neorv323_package.vhd`).
.Internal Bus Timeout Configuration
[source,vhdl]
@ -668,12 +657,6 @@ constant base_io_slink_c : std_ulogic_vector(31 downto 0) := x"ffffec00";
constant base_io_dma_c : std_ulogic_vector(31 downto 0) := x"ffffed00";
----
.IO Access Latency
[IMPORTANT]
In order to shorten the critical path of the IO system, the IO switch contain a partial register stage that
buffers the address bus. Hence, accesses to the processor-internal IO region requires an additional clock cycle
to complete.
<<<
// ####################################################################################################################
@ -694,6 +677,10 @@ generic (see <<_processor_top_entity_generics>>).
| 2 | IMEM Image | Base of internal IMEM | Implement the processor-internal <<_instruction_memory_imem>> as pre-initialized ROM and boot from there.
|=======================
.Dual-Core Boot
[TIP]
For the SMPA dual-core CPU configuration boot procedure see section <<_dual_core_boot>>.
:sectnums:
==== Booting via Bootloader
@ -743,31 +730,32 @@ need for an explicit initialization / executable upload.
:sectnums:
=== Processor-Internal Modules
.Privileged IO Access Only
[IMPORTANT]
Only privileged accesses (M-mode) to the IO/peripheral modules are allowed. If an unprivileged application
tries to access this address space a bus access error exception is raised.
.Full-Word Write Accesses Only
[NOTE]
All peripheral/IO devices should only be written in full-word mode (i.e. 32-bit). Byte or half-word (8/16-bit) write accesses
might cause undefined behavior.
[IMPORTANT]
All peripheral/IO devices should only be accessed in full-word mode (i.e. 32-bit).
Byte or half-word (8/16-bit) write accesses might cause undefined behavior.
.Writing to Read-Only Registers
[NOTE]
Unless otherwise specified, writing to registers that are listed as read-only does not trigger an exception.
The write access is simply ignored by the corresponding hardware module.
.IO Module's Address Space
[NOTE]
Each peripheral/IO module occupies an address space of 256 bytes (64 words). Most devices do not fully utilize this address
space and will simply _mirror_ the available interface registers across the entire 256 bytes of address space.
.IO Module Address Space
[IMPORTAN]
Each peripheral/IO module occupies an address space of 64kB bytes. Most devices do not fully utilize this
address space and will _mirror_ the available memory-mapped registers across the entire 64kB address space.
However, accessing memory-mapped registers other than the specified ones should be avoided.
.Unimplemented Modules / Address Holes
[NOTE]
When accessing an IO device that hast not been implemented (disabled via the according generic)
or when accessing an address that is actually unused, a load/store access fault exception is raised.
.Writing to Read-Only Registers
[NOTE]
Unless otherwise specified, writing to registers that are listed as read-only does not trigger an exception
as the write access is simply ignored by the corresponding hardware module.
.IO Access Latency
[NOTE]
In order to shorten the critical path of the IO system, the IO switch provides register stages for the request and
response buses.Hence, accesses to the processor-internal IO region require two additional clock cycles to complete.
.Module Interrupts
[NOTE]
Several peripheral/IO devices provide some kind of interrupt. These interrupts are mapped to the CPU's
@ -800,7 +788,7 @@ include::soc_crc.adoc[]
include::soc_wdt.adoc[]
include::soc_mtime.adoc[]
include::soc_clint.adoc[]
include::soc_uart.adoc[]
@ -810,6 +798,8 @@ include::soc_sdi.adoc[]
include::soc_twi.adoc[]
include::soc_twd.adoc[]
include::soc_onewire.adoc[]
include::soc_pwm.adoc[]

View file

@ -11,7 +11,7 @@
| Top entity ports: | none |
| Configuration generics: | `BOOT_MODE_SELECT` | implement BOOTROM when `BOOT_MODE_SELECT` = 0; see <<_boot_configuration>>
| CPU interrupts: | none |
| Access restrictions: 2+| privileged access only, read-only
| Access restrictions: 2+| read-only
|=======================
@ -19,10 +19,11 @@
The boot ROM contains the executable image of the default NEORV32 <<_bootloader>>. When the
<<_boot_configuration>> is set to _bootloader_ mode (0) via the `BOOT_MODE_SELECT` generic, the
boot ROM is automatically enabled and the CPU boot address is automatically adjusted to the
base address of the boot ROM.
boot ROM is automatically enabled and the CPU boot address is adjusted to the base address of the boot ROM.
.Bootloader Image
[IMPORTANT]
The boot ROM is initialized during synthesis with the default bootloader image
(`rtl/core/neorv32_bootloader_image.vhd`). Note that the BOOTROM size is constrained to 4kB.
The bootloader ROM is initialized during synthesis with the default bootloader image
(`rtl/core/neorv32_bootloader_image.vhd`). The physical size of the ROM is automatically
adjusted to the next power of two of the image size. However, note that the BOOTROM is
constrained to a maximum size of 64kB.

View file

@ -15,14 +15,14 @@
| | `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>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
**Overview**
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
IT provides up to 16384 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.
@ -94,9 +94,9 @@ If the CFU output signals are to be used outside the chip, it is recommended to
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s) | R/W | Function
| `0xffffeb00` | `REG[0]` |`31:0` | (r)/(w) | custom CFS register 0
| `0xffffeb04` | `REG[1]` |`31:0` | (r)/(w) | custom CFS register 1
| ... | ... |`31:0` | (r)/(w) | ...
| `0xffffebf8` | `REG[62]` |`31:0` | (r)/(w) | custom CFS register 62
| `0xffffebfc` | `REG[63]` |`31:0` | (r)/(w) | custom CFS register 63
| `0xffeb0000` | `REG[0]` |`31:0` | (r)/(w) | custom CFS register 0
| `0xffeb0004` | `REG[1]` |`31:0` | (r)/(w) | custom CFS register 1
| ... | ... |`31:0` | (r)/(w) | ...
| `0xffebfff8` | `REG[16382]` |`31:0` | (r)/(w) | custom CFS register 16382
| `0xffebfffc` | `REG[16383]` |`31:0` | (r)/(w) | custom CFS register 16383
|=======================

View file

@ -0,0 +1,62 @@
<<<
:sectnums:
==== Core Local Interruptor (CLINT)
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
|=======================
| Hardware source files: | neorv32_clint.vhd |
| Software driver files: | neorv32_clint.c |
| | neorv32_clint.h |
| Top entity ports: | `mtime_irq_i` | RISC-V machine timer IRQ if CLINT is **not** implemented
| | `msw_irq_i` | RISC-V software IRQ if CLINT is **not** implemented
| | `mtime_time_o` | Current system time (from CLINT's MTIMER)
| Configuration generics: | `IO_CLINT_EN` | implement core local interruptor when `true`
| CPU interrupts: | `MTI` | machine timer interrupt (see <<_processor_interrupts>>)
| | `MSI` | machine software interrupt (see <<_processor_interrupts>>)
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
**Overview**
The core local interruptor provides machine-level timer and software interrupts for a set of CPU cores (also called _harts).
It is compatible to the original SiFive(R) CLINT specifications and it is also backwards-compatible to the upcoming RISC-V
_Advanced Core Local Interruptor (ACLINT)_ specifications. In terms of the ACLINT spec the NEORV32 CLINT implements three
_devices_ that are placed next to each other in the address space: an MTIMER and an MSWI device.
The CLINT can support up to 4095 harts. However, the NEORV32 CLINT is configured for a single hart only (yet).
Hence, only the according registers are implemented while the remaining ones are hardwired to zero.
**MTIMER Device**
The MTIMER device provides a global 64-bit machine timer (`NEORV32_CLINT->MTIME`) that increments with every main processor
clock tick. Upon reset the timer is reset to all zero. Each hart provides an individual 64-bit timer-compare register
(`NEORV32_CLINT->MTIMECMP[0]` for hart 0). Whenever `MTIMECMP >= MTIME` the according machine timer interrupt is pending.
**MSIW Device**
The MSIV provides software interrupts for each hart via hart-individual memory-mapped registers (`NEORV32_CLINT->MSWI[0]` for
hart 0). Setting bit 0 of this register will bring the machine software interrupt into pending state.
.External Machine Timer and Software Interrupts
[NOTE]
If the NEORV32 CLINT module is disabled (`IO_CLINT_EN` = `false`) the core's machine timer interrupt and
machine software interrupt become available as processor-external signals (`mtime_irq_i` and `msw_irq_i`, respectively).
**Register Map**
.CLINT register map (`struct NEORV32_CLINT`)
[cols="<3,<3,^1,^1,<6"]
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bits | R/W | Function
.2+<| `0xfff40000` .2+<| `MSWI[0]` ^| 0 ^| r/w <| trigger machine software interrupt for hart 0 when set
^| 31:1 ^| r/- <| hardwired to zero
| `0xfff44000` | `MTIMECMP[0]` | 63:0 | r/w | 64-bit time compare for hart 0
| `0xfff4bff8` | `MTIME` | 63:0 | r/w | 64-bit global machine timer
|=======================

View file

@ -11,7 +11,7 @@
| Top entity ports: | none |
| Configuration generics: | `IO_CRC_EN` | implement CRC module when `true`
| CPU interrupts: | none |
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -63,10 +63,10 @@ and for CRC32-mode the entire 32-bit of `POLY` and `SREG` are used.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.2+<| `0xffffee00` .2+<| `CTRL` <|`1:0` ^| r/w <| CRC mode select (`00` CRC8, `01`: CRC16, `10`: CRC32)
.2+<| `0xffee0000` .2+<| `CTRL` <|`1:0` ^| r/w <| CRC mode select (`00` CRC8, `01`: CRC16, `10`: CRC32)
<|`31:2` ^| r/- <| _reserved_, read as zero
| `0xffffee04` | `POLY` |`31:0` | r/w | CRC polynomial
.2+<| `0xffffee08` .2+<| `DATA` <|`7:0` ^| r/w <| data input (single byte)
| `0xffee0004` | `POLY` |`31:0` | r/w | CRC polynomial
.2+<| `0xffee0008` .2+<| `DATA` <|`7:0` ^| r/w <| data input (single byte)
<|`31:8` ^| r/- <| _reserved_, read as zero, writes are ignored
| `0xffffee0c` | `SREG` |`32:0` | r/w | current CRC shift register value (set start value on write)
| `0xffee000c` | `SREG` |`32:0` | r/w | current CRC shift register value (set start value on write)
|=======================

View file

@ -11,7 +11,7 @@
| Top entity ports: | none |
| Configuration generics: | `IO_DMA_EN` | implement DMA when `true`
| CPU interrupts: | fast IRQ channel 10 | DMA transfer done (see <<_processor_interrupts>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -142,7 +142,7 @@ register).
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.12+<| `0xffffed00` .12+<| `CTRL` <|`0` `DMA_CTRL_EN` ^| r/w <| DMA module enable
.12+<| `0xffed0000` .12+<| `CTRL` <|`0` `DMA_CTRL_EN` ^| r/w <| DMA module enable
<|`1` `DMA_CTRL_AUTO` ^| r/w <| Enable automatic mode (FIRQ-triggered)
<|`2` `DMA_CTRL_FENCE` ^| r/w <| Issue a downstream FENCE operation when DMA transfer completes (without errors)
<|`7:3` _reserved_ ^| r/- <| reserved, read as zero
@ -154,9 +154,9 @@ register).
<|`15` `DMA_CTRL_FIRQ_TYPE` ^| r/w <| Trigger on rising-edge (`0`) or high-level (`1`) or selected FIRQ channel
<|`19:16` `DMA_CTRL_FIRQ_SEL_MSB : DMA_CTRL_FIRQ_SEL_LSB` ^| r/w <| FIRQ trigger select (FIRQ0=0 ... FIRQ15=15)
<|`31:20` _reserved_ ^| r/- <| reserved, read as zero
| `0xffffed04` | `SRC_BASE` |`31:0` | r/w | Source base address (shows the last-accessed source address when read)
| `0xffffed08` | `DST_BASE` |`31:0` | r/w | Destination base address (shows the last-accessed destination address when read)
.6+<| `0xffffed0c` .6+<| `TTYPE` <|`23:0` `DMA_TTYPE_NUM_MSB : DMA_TTYPE_NUM_LSB` ^| r/w <| Number of elements to transfer (shows the last-transferred element index when read)
| `0xffed0004` | `SRC_BASE` |`31:0` | r/w | Source base address (shows the last-accessed source address when read)
| `0xffed0008` | `DST_BASE` |`31:0` | r/w | Destination base address (shows the last-accessed destination address when read)
.6+<| `0xffed000c` .6+<| `TTYPE` <|`23:0` `DMA_TTYPE_NUM_MSB : DMA_TTYPE_NUM_LSB` ^| r/w <| Number of elements to transfer (shows the last-transferred element index when read)
<|`26:24` _reserved_ ^| r/- <| reserved, read as zero
<|`28:27` `DMA_TTYPE_QSEL_MSB : DMA_TTYPE_QSEL_LSB` ^| r/w <| Quantity select (`00` = byte -> byte, `01` = byte -> zero-extended-word, `10` = byte -> sign-extended-word, `11` = word -> word)
<|`29` `DMA_TTYPE_SRC_INC` ^| r/w <| Constant (`0`) or incrementing (`1`) source address

View file

@ -12,7 +12,7 @@
| | `gpio_i` | 64-bit parallel input port
| Configuration generics: | `IO_GPIO_NUM` | number of input/output pairs to implement (0..64)
| CPU interrupts: | none |
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -41,8 +41,8 @@ be performed within a single clock cycle.
[options="header",grid="rows"]
|=======================
| Address | Name [C] | Bit(s) | R/W | Function
| `0xfffffc00` | `INPUT[0]` | 31:0 | r/- | parallel input port pins 31:0
| `0xfffffc04` | `INPUT[1]` | 31:0 | r/- | parallel input port pins 63:32
| `0xfffffc08` | `OUTPUT[0]` | 31:0 | r/w | parallel output port pins 31:0
| `0xfffffc0c` | `OUTPUT[1]` | 31:0 | r/w | parallel output port pins 63:32
| `0xfffc0000` | `INPUT[0]` | 31:0 | r/- | parallel input port pins 31:0
| `0xfffc0004` | `INPUT[1]` | 31:0 | r/- | parallel input port pins 63:32
| `0xfffc0008` | `OUTPUT[0]` | 31:0 | r/w | parallel output port pins 31:0
| `0xfffc000c` | `OUTPUT[1]` | 31:0 | r/w | parallel output port pins 63:32
|=======================

View file

@ -11,7 +11,7 @@
| Top entity ports: | none |
| Configuration generics: | `IO_GPTMR_EN` | implement general purpose timer when `true`
| CPU interrupts: | fast IRQ channel 12 | timer interrupt (see <<_processor_interrupts>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -62,12 +62,12 @@ stay pending until explicitly cleared by writing a 1 to `GPTMR_CTRL_IRQ_CLR`.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.6+<| `0xfffff100` .6+<| `CTRL` <|`0` `GPTMR_CTRL_EN` ^| r/w <| Timer enable flag
.6+<| `0xfff10000` .6+<| `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 <| Operation mode (0=single-shot, 1=continuous)
<|`29:5` - ^| r/- <| _reserved_, read as zero
<|`30` `GPTMR_CTRL_IRQ_CLR` ^| -/w <| Write `1` to clear timer-match interrupt; auto-clears
<|`31` `GPTMR_CTRL_IRQ_PND` ^| r/- <| Timer-match interrupt pending
| `0xfffff104` | `THRES` |`31:0` | r/w | Threshold value register
| `0xfffff108` | `COUNT` |`31:0` | r/- | Counter register
| `0xfff10004` | `THRES` |`31:0` | r/w | Threshold value register
| `0xfff10008` | `COUNT` |`31:0` | r/- | Counter register
|=======================

View file

@ -25,7 +25,7 @@ Note that this size should be a power of two to optimize physical implementation
the IMEM is mapped to base address `0x00000000` (see section <<_address_space>>).
By default the IMEM is implemented as true RAM so the content can be modified during run time. This is
required when using the <<_bootloader>> (or the <<_on_chip_debugger>>) so it can update the content of the IMEM at
required when using the <<_bootloader>> (or the <<_on_chip_debugger_ocd>>) so it can update the content of the IMEM at
any time.
Alternatively, the IMEM can be implemented as **pre-initialized read-only memory (ROM)**, so the processor can

View file

@ -1,52 +0,0 @@
<<<
:sectnums:
==== Machine System Timer (MTIME)
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
|=======================
| Hardware source files: | neorv32_mtime.vhd |
| Software driver files: | neorv32_mtime.c |
| | neorv32_mtime.h |
| Top entity ports: | `mtime_irq_i` | RISC-V machine timer IRQ if internal one is **not** implemented
| | `mtime_time_o` | Current system time (`TIME` register)
| Configuration generics: | `IO_MTIME_EN` | implement machine timer when `true`
| CPU interrupts: | `MTI` | machine timer interrupt (see <<_processor_interrupts>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
|=======================
**Overview**
The MTIME module implements a memory-mapped machine system timer that is compatible to the RISC-V
privileged specifications. The 64-bit system time is accessed via individual `TIME_LO` and
`TIME_HI` registers. A 64-bit time compare register, which is accessible via individual `TIMECMP_LO`
and `TIMECMP_HI` registers, can be used to configure the CPU's machine timer interrupt (`MTI`)).
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 than `TIMECMP` again (either by modifying
`TIME` or `TIMECMP`). The current system time is available for other SoC modules via the top's `mtime_time_o` signal.
.Hardware Reset
[NOTE]
After a hardware reset the `TIME` and `TIMECMP` register are reset to all-zero.
.External MTIME Interrupt
[NOTE]
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 user-defined memory-mapped register).
**Register Map**
.MTIME register map (`struct NEORV32_MTIME`)
[cols="<3,<3,^1,^1,<6"]
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bits | R/W | Function
| `0xfffff400` | `TIME_LO` | 31:0 | r/w | system time, low word
| `0xfffff404` | `TIME_HI` | 31:0 | r/w | system time, high word
| `0xfffff408` | `TIMECMP_LO` | 31:0 | r/w | time compare, low word
| `0xfffff40c` | `TIMECMP_HI` | 31:0 | r/w | time compare, high word
|=======================

View file

@ -12,7 +12,7 @@
| 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>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -186,7 +186,7 @@ Once the NEOLED interrupt has fired it remains pending until the actual cause of
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.13+<| `0xfffffd00` .13+<| `CTRL` <|`0` `NEOLED_CTRL_EN` ^| r/w <| NEOLED enable
.13+<| `0xfffd0000` .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
@ -199,5 +199,5 @@ Once the NEOLED interrupt has fired it remains pending until the actual cause of
<|`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
| `0xfffffd04` | `DATA` <|`31:0` / `23:0` ^| -/w <| TX data (32- or 24-bit, depending on _NEOLED_CTRL_MODE_ bit)
| `0xfffd0004` | `DATA` <|`31:0` / `23:0` ^| -/w <| TX data (32- or 24-bit, depending on _NEOLED_CTRL_MODE_ bit)
|=======================

View file

@ -13,7 +13,7 @@
| Configuration generics: | `IO_ONEWIRE_EN` | implement ONEWIRE interface controller when `true`
| | `IO_ONEWIRE_FIFO` | RTX fifo depth, has to be zero or a power of two, min 1
| CPU interrupts: | fast IRQ channel 13 | operation done interrupt (see <<_processor_interrupts>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -183,7 +183,7 @@ controller is idle (again) and the data/command FIFO is empty, the interrupt bec
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.11+<| `0xfffff200` .11+<| `CTRL` <|`0` `ONEWIRE_CTRL_EN` ^| r/w <| ONEWIRE enable, reset if cleared
.11+<| `0xfff20000` .11+<| `CTRL` <|`0` `ONEWIRE_CTRL_EN` ^| r/w <| ONEWIRE enable, reset if cleared
<|`1` `ONEWIRE_CTRL_CLEAR` ^| -/w <| clear RXT FIFO, auto-clears
<|`3:2` `ONEWIRE_CTRL_PRSC1 : ONEWIRE_CTRL_PRSC0` ^| r/w <| 2-bit clock prescaler select
<|`11:4` `ONEWIRE_CTRL_CLKDIV7 : ONEWIRE_CTRL_CLKDIV0` ^| r/w <| 8-bit clock divider value
@ -194,7 +194,7 @@ controller is idle (again) and the data/command FIFO is empty, the interrupt bec
<|`29` `ONEWIRE_CTRL_RX_AVAIL` ^| r/- <| RX FIFO data available
<|`30` `ONEWIRE_CTRL_SENSE` ^| r/- <| current state of the bus line
<|`31` `ONEWIRE_CTRL_BUSY` ^| r/- <| operation in progress when set or TX FIFO not empty
.4+<| `0xfffff204` .4+<| `DCMD` <|`7:0` `ONEWIRE_DCMD_DATA_MSB : ONEWIRE_DCMD_DATA_LSB` ^| r/w <| receive/transmit data
.4+<| `0xfff20004` .4+<| `DCMD` <|`7:0` `ONEWIRE_DCMD_DATA_MSB : ONEWIRE_DCMD_DATA_LSB` ^| r/w <| receive/transmit data
<|`9:8` `ONEWIRE_DCMD_CMD_HI : ONEWIRE_DCMD_CMD_LO` ^| -/w <| operation command LSBs
<|`10` `ONEWIRE_DCMD_PRESENCE` ^| -/w <| bus presence detected
<|`31:11` - ^| r/- <| _reserved_, read as zero

View file

@ -11,7 +11,7 @@
| Top entity ports: | `pwm_o` | PWM output channels (16-bit)
| Configuration generics: | `IO_PWM_NUM_CH` | number of PWM channels to implement (0..16)
| CPU interrupts: | none |
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -60,18 +60,18 @@ _f~PWM~_[Hz] = _f~main~_[Hz] / (2^8^ * `clock_prescaler` * (1 + `PWM_CFG_CDIV`))
**Register Map**
.PWM register map (`struct neorv32_pwm_t`)
.PWM register map (`struct NEORV32_PWM`)
[cols="<4,<2,<6,^2,<8"]
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.5+<| `0xfffff000` .5+<| `CHANNEL_CFG[0]` <|`31` - `PWM_CFG_EN` ^| r/w <| Channel 0: channel enabled when set
.5+<| `0xfff00000` .5+<| `CHANNEL_CFG[0]` <|`31` - `PWM_CFG_EN` ^| r/w <| Channel 0: channel enabled when set
<|`30:28` - `PWM_CFG_PRSC_MSB:PWM_CFG_PRSC_LSB` ^| r/w <| Channel 0: 3-bit clock prescaler select
<|`27:18` ^| r/- <| Channel 0: _reserved_, hardwired to zero
<|`17:8` - `PWM_CFG_CDIV_MSB:PWM_CFG_CDIV_LSB` ^| r/w <| Channel 0: 10-bit clock divider
<|`7:0` - `PWM_CFG_DUTY_MSB:PWM_CFG_DUTY_LSB` ^| r/w <| Channel 0: 8-bit duty cycle
| `0xfffff004` ... `0xfffff038` | `CHANNEL_CFG[1]` ... `CHANNEL_CFG[14]` | ... | r/w <| Channels 1 to 14
.5+<| `0xfffff03C` .5+<| `CHANNEL_CFG[15]` <|`31` - `PWM_CFG_EN` ^| r/w <| Channel 15: channel enabled when set
| `0xfff00004` ... `0xfff00038` | `CHANNEL_CFG[1]` ... `CHANNEL_CFG[14]` | ... | r/w <| Channels 1 to 14
.5+<| `0xfff0003C` .5+<| `CHANNEL_CFG[15]` <|`31` - `PWM_CFG_EN` ^| r/w <| Channel 15: channel enabled when set
<|`30:28` - `PWM_CFG_PRSC_MSB:PWM_CFG_PRSC_LSB` ^| r/w <| Channel 15: 3-bit clock prescaler select
<|`27:18` ^| r/- <| Channel 15: _reserved_, hardwired to zero
<|`17:8` - `PWM_CFG_CDIV_MSB:PWM_CFG_CDIV_LSB` ^| r/w <| Channel 15: 10-bit clock divider

View file

@ -15,7 +15,7 @@
| 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>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -87,7 +87,7 @@ example if just the `SDI_CTRL_IRQ_RX_AVAIL` bit is set, the interrupt will keep
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.18+<| `0xfffff700` .18+<| `CTRL` <|`0` `SDI_CTRL_EN` ^| r/w <| SDI module enable
.18+<| `0xfff70000` .18+<| `CTRL` <|`0` `SDI_CTRL_EN` ^| r/w <| SDI module enable
<|`3:1` _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
@ -105,6 +105,6 @@ example if just the `SDI_CTRL_IRQ_RX_AVAIL` bit is set, the interrupt will keep
<|`28` `SDI_CTRL_TX_FULL` ^| r/- <| TX FIFO full
<|`30:29` _reserved_ ^| r/- <| reserved, read as zero
<|`31` `SDI_CTRL_CS_ACTIVE` ^| r/- <| Chip-select is active when set
.2+<| `0xfffff704` .2+<| `DATA` <|`7:0` ^| r/w <| receive/transmit data (FIFO)
.2+<| `0xfff70004` .2+<| `DATA` <|`7:0` ^| r/w <| receive/transmit data (FIFO)
<|`31:8` _reserved_ ^| r/- <| reserved, read as zero
|=======================

View file

@ -23,7 +23,7 @@
| | `IO_SLINK_TX_FIFO` | TX FIFO depth (1..32k), has to be a power of two, min 1
| CPU interrupts: | fast IRQ channel 14 | RX SLINK IRQ (see <<_processor_interrupts>>)
| | fast IRQ channel 15 | TX SLINK IRQ (see <<_processor_interrupts>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -116,31 +116,31 @@ interrupt-causing condition is resolved (e.g. by reading from the RX FIFO).
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s) | R/W | Function
.22+<| `0xffffec00` .22+<| `NEORV32_SLINK.CTRL` <| `0` `SLINK_CTRL_EN` ^| r/w <| SLINK global enable
<| `1` `SLINK_CTRL_RX_CLR` ^| -/w <| Clear RX FIFO when set (bit auto-clears)
<| `2` `SLINK_CTRL_TX_CLR` ^| -/w <| Clear TX FIFO when set (bit auto-clears)
<| `3` _reserved_ ^| r/- <| _reserved_, read as zero
<| `4` `SLINK_CTRL_RX_LAST` ^| r/- <| Last word read from `RX_DATA` is marked as "end of stream"
<| `7:5` _reserved_ ^| r/- <| _reserved_, read as zero
<| `8` `SLINK_CTRL_RX_EMPTY` ^| r/- <| RX FIFO empty
<| `9` `SLINK_CTRL_RX_HALF` ^| r/- <| RX FIFO at least half full
<| `10` `SLINK_CTRL_RX_FULL` ^| r/- <| RX FIFO full
<| `11` `SLINK_CTRL_TX_EMPTY` ^| r/- <| TX FIFO empty
<| `12` `SLINK_CTRL_TX_HALF` ^| r/- <| TX FIFO at least half full
<| `13` `SLINK_CTRL_TX_FULL` ^| r/- <| TX FIFO full
<| `15:14` _reserved_ ^| r/- <| _reserved_, read as zero
<| `16` `SLINK_CTRL_IRQ_RX_NEMPTY` ^| r/w <| RX interrupt if RX FIFO not empty
<| `17` `SLINK_CTRL_IRQ_RX_HALF` ^| r/w <| RX interrupt if RX FIFO at least half full
<| `18` `SLINK_CTRL_IRQ_RX_FULL` ^| r/w <| RX interrupt if RX FIFO full
<| `19` `SLINK_CTRL_IRQ_TX_EMPTY` ^| r/w <| TX interrupt if TX FIFO empty
<| `20` `SLINK_CTRL_IRQ_TX_NHALF` ^| r/w <| TX interrupt if TX FIFO not at least half full
<| `21` `SLINK_CTRL_IRQ_TX_NFULL` ^| r/w <| TX interrupt if TX FIFO not full
<| `23:22` _reserved_ ^| r/- <| _reserved_, read as zero
<| `27:24` `SLINK_CTRL_RX_FIFO_MSB : SLINK_CTRL_RX_FIFO_LSB` ^| r/- <| log2(RX FIFO size)
<| `31:28` `SLINK_CTRL_TX_FIFO_MSB : SLINK_CTRL_TX_FIFO_LSB` ^| r/- <| log2(TX FIFO size)
.3+<| `0xffffec04` .3+<| `NEORV32_SLINK.ROUTE` <| `3:0` | r/w | TX destination routing information (`slink_tx_dst_o`)
<| `7:4` | r/- | RX source routing information (`slink_rx_src_i`)
<| `31:8` | -/- | _reserved_
| `0xffffec08` | `NEORV32_SLINK.DATA` | `31:0` | r/w | Write data to TX FIFO; read data from RX FIFO
| `0xffffec0c` | `NEORV32_SLINK.DATA_LAST` | `31:0` | r/w | Write data to TX FIFO (and also set "last" signal); read data from RX FIFO
.22+<| `0xffec0000` .22+<| `CTRL` <| `0` `SLINK_CTRL_EN` ^| r/w <| SLINK global enable
<| `1` `SLINK_CTRL_RX_CLR` ^| -/w <| Clear RX FIFO when set (bit auto-clears)
<| `2` `SLINK_CTRL_TX_CLR` ^| -/w <| Clear TX FIFO when set (bit auto-clears)
<| `3` _reserved_ ^| r/- <| _reserved_, read as zero
<| `4` `SLINK_CTRL_RX_LAST` ^| r/- <| Last word read from `RX_DATA` is marked as "end of stream"
<| `7:5` _reserved_ ^| r/- <| _reserved_, read as zero
<| `8` `SLINK_CTRL_RX_EMPTY` ^| r/- <| RX FIFO empty
<| `9` `SLINK_CTRL_RX_HALF` ^| r/- <| RX FIFO at least half full
<| `10` `SLINK_CTRL_RX_FULL` ^| r/- <| RX FIFO full
<| `11` `SLINK_CTRL_TX_EMPTY` ^| r/- <| TX FIFO empty
<| `12` `SLINK_CTRL_TX_HALF` ^| r/- <| TX FIFO at least half full
<| `13` `SLINK_CTRL_TX_FULL` ^| r/- <| TX FIFO full
<| `15:14` _reserved_ ^| r/- <| _reserved_, read as zero
<| `16` `SLINK_CTRL_IRQ_RX_NEMPTY` ^| r/w <| RX interrupt if RX FIFO not empty
<| `17` `SLINK_CTRL_IRQ_RX_HALF` ^| r/w <| RX interrupt if RX FIFO at least half full
<| `18` `SLINK_CTRL_IRQ_RX_FULL` ^| r/w <| RX interrupt if RX FIFO full
<| `19` `SLINK_CTRL_IRQ_TX_EMPTY` ^| r/w <| TX interrupt if TX FIFO empty
<| `20` `SLINK_CTRL_IRQ_TX_NHALF` ^| r/w <| TX interrupt if TX FIFO not at least half full
<| `21` `SLINK_CTRL_IRQ_TX_NFULL` ^| r/w <| TX interrupt if TX FIFO not full
<| `23:22` _reserved_ ^| r/- <| _reserved_, read as zero
<| `27:24` `SLINK_CTRL_RX_FIFO_MSB : SLINK_CTRL_RX_FIFO_LSB` ^| r/- <| log2(RX FIFO size)
<| `31:28` `SLINK_CTRL_TX_FIFO_MSB : SLINK_CTRL_TX_FIFO_LSB` ^| r/- <| log2(TX FIFO size)
.3+<| `0xffec0004` .3+<| `ROUTE` <| `3:0` | r/w | TX destination routing information (`slink_tx_dst_o`)
<| `7:4` | r/- | RX source routing information (`slink_rx_src_i`)
<| `31:8` | -/- | _reserved_
| `0xffec0008` | `DATA` | `31:0` | r/w | Write data to TX FIFO; read data from RX FIFO
| `0xffec000c` | `DATA_LAST` | `31:0` | r/w | Write data to TX FIFO (and also set "last" signal); read data from RX FIFO
|=======================

View file

@ -15,7 +15,7 @@
| 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>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -126,7 +126,7 @@ example if just the `SPI_CTRL_IRQ_RX_AVAIL` bit is set, the interrupt will keep
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.19+<| `0xfffff800` .19+<| `CTRL` <|`0` `SPI_CTRL_EN` ^| r/w <| SPI module enable
.19+<| `0xfff80000` .19+<| `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_PRSC2 : SPI_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select
@ -145,7 +145,7 @@ example if just the `SPI_CTRL_IRQ_RX_AVAIL` bit is set, the interrupt will keep
<|`30:28` _reserved_ ^| r/- <| reserved, read as zero
<|`30` `SPI_CS_ACTIVE` ^| r/- <| Set if any chip-select line is active
<|`31` `SPI_CTRL_BUSY` ^| r/- <| SPI module busy when set (serial engine operation in progress and TX FIFO not empty yet)
.3+<| `0xfffff804` .3+<| `DATA` <|`7:0` `SPI_DATA_MSB : SPI_DATA_LSB` ^| r/w <| receive/transmit data (FIFO)
.3+<| `0xfff80004` .3+<| `DATA` <|`7:0` `SPI_DATA_MSB : SPI_DATA_LSB` ^| r/w <| receive/transmit data (FIFO)
<|`30:8` _reserved_ ^| r/- <| reserved, read as zero
<|`31` `SPI_DATA_CMD` ^| -/w <| data (`0`) / chip-select-command (`1`) select
|=======================

View file

@ -10,7 +10,7 @@
| Top entity ports: | none |
| Configuration generics: | * | most of the top's configuration generics
| CPU interrupts: | none |
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -42,14 +42,14 @@ to take into account a dynamic frequency scaling of the processor.
[options="header",grid="all"]
|=======================
| Address | Name [C] | R/W | Description
| `0xfffffe00` | `CLK` | r/w | clock frequency in Hz (initialized from top's `CLOCK_FREQUENCY` generic)
| `0xfffffe04` | `MEM[4]` | r/- | internal memory configuration (see <<_sysinfo_memory_configuration>>)
| `0xfffffe08` | `SOC` | r/- | specific SoC configuration (see <<_sysinfo_soc_configuration>>)
| `0xfffffe0c` | `CACHE` | r/- | cache configuration information (see <<_sysinfo_cache_configuration>>)
| `0xfffe0000` | `CLK` | r/w | clock frequency in Hz (initialized from top's `CLOCK_FREQUENCY` generic)
| `0xfffe0004` | `MISC[4]` | r/- | miscellaneous system configurations (see <<_sysinfo_miscellaneous_configuration>>)
| `0xfffe0008` | `SOC` | r/- | specific SoC configuration (see <<_sysinfo_soc_configuration>>)
| `0xfffe000c` | `CACHE` | r/- | cache configuration information (see <<_sysinfo_cache_configuration>>)
|=======================
===== SYSINFO - Memory Configuration
===== SYSINFO - Miscellaneous Configuration
[NOTE]
Bit fields in this register are set to all-zero if the according memory system is not implemented.
@ -59,10 +59,10 @@ Bit fields in this register are set to all-zero if the according memory system i
[options="header",grid="all"]
|=======================
| Byte | Name [C] | Description
| `0` | `SYSINFO_MEM_IMEM` | _log2_(internal IMEM size in bytes), via top's `MEM_INT_IMEM_SIZE` generic
| `1` | `SYSINFO_MEM_DMEM` | _log2_(internal DMEM size in bytes), via top's `MEM_INT_DMEM_SIZE` generic
| `2` | - | _reserved_, read as zero
| `3` | `SYSINFO_MEM_BOOT` | boot mode configuration, via top's `BOOT_MODE_SELECT` generic (see <<_boot_configuration>>))
| `0` | `SYSINFO_MISC_IMEM` | _log2_(internal IMEM size in bytes), via top's `MEM_INT_IMEM_SIZE` generic
| `1` | `SYSINFO_MISC_DMEM` | _log2_(internal DMEM size in bytes), via top's `MEM_INT_DMEM_SIZE` generic
| `2` | `SYSINFO_MISC_HART` | number of physical CPU cores ("harts")
| `3` | `SYSINFO_MISC_BOOT` | boot mode configuration, via top's `BOOT_MODE_SELECT` generic (see <<_boot_configuration>>))
|=======================
@ -80,16 +80,16 @@ Bit fields in this register are set to all-zero if the according memory system i
| `4` | `SYSINFO_SOC_OCD` | set if on-chip debugger is implemented (via top's `OCD_EN` generic)
| `5` | `SYSINFO_SOC_ICACHE` | set if processor-internal instruction cache is implemented (via top's `ICACHE_EN` generic)
| `6` | `SYSINFO_SOC_DCACHE` | set if processor-internal data cache is implemented (via top's `DCACHE_EN` generic)
| `7` | `SYSINFO_SOC_CLOCK_GATING` | set if CPU clock gating is implemented (via top's `CLOCK_GATING_EN` generic)
| `7` | - |_reserved_, read as zero
| `8` | `SYSINFO_SOC_XBUS_CACHE` | set if external bus interface cache is implemented (via top's `XBUS_CACHE_EN` generic)
| `9` | `SYSINFO_SOC_XIP` | set if XIP module is implemented (via top's `XIP_EN` generic)
| `10` | `SYSINFO_SOC_XIP_CACHE` | set if XIP cache is implemented (via top's `XIP_CACHE_EN` generic)
| `11` | `SYSINFO_SOC_OCD_AUTH` | set if on-chip debugger authentication is implemented (via top's `OCD_AUTHENTICATION` generic)
| `12` | `SYSINFO_SOC_IMEM_ROM` | set if processor-internal IMEM is implemented as pre-initialized ROM (via top's `BOOT_MODE_SELECT` generic; see <<_boot_configuration>>)
| `13` | - | _reserved_, read as zero
| `13` | `SYSINFO_SOC_IO_TWD` | set if TWD is implemented (via top's `IO_TWD_EN` generic)
| `14` | `SYSINFO_SOC_IO_DMA` | set if direct memory access controller is implemented (via top's `IO_DMA_EN` generic)
| `15` | `SYSINFO_SOC_IO_GPIO` | set if GPIO is implemented (via top's `IO_GPIO_EN` generic)
| `16` | `SYSINFO_SOC_IO_MTIME` | set if MTIME is implemented (via top's `IO_MTIME_EN` generic)
| `16` | `SYSINFO_SOC_IO_CLINT` | set if CLINT is implemented (via top's `IO_CLINT_EN` generic)
| `17` | `SYSINFO_SOC_IO_UART0` | set if primary UART0 is implemented (via top's `IO_UART0_EN` generic)
| `18` | `SYSINFO_SOC_IO_SPI` | set if SPI is implemented (via top's `IO_SPI_EN` generic)
| `19` | `SYSINFO_SOC_IO_TWI` | set if TWI is implemented (via top's `IO_TWI_EN` generic)
@ -110,8 +110,12 @@ Bit fields in this register are set to all-zero if the according memory system i
===== SYSINFO - Cache Configuration
[NOTE]
Bit fields in this register are set to all-zero if the according cache is not implemented.
The SYSINFO cache register provides information about the configuration of the processor caches:
* <<_processor_internal_instruction_cache_icache>>
* <<_processor_internal_data_cache_dcache>>
* <<_execute_in_place_module_xip>> cache (XIP-CACHE)
* <<_processor_external_bus_interface_xbus>> cache (XBUS-CACHE)
.SYSINFO `CACHE` Bits
[cols="^1,<10,<10"]

View file

@ -11,8 +11,8 @@
| Top entity ports: | 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
| CPU interrupts: | fast IRQ channel 0 | TRNG data available interrupt (see <<_processor_interrupts>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| CPU interrupts: | none
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -33,34 +33,27 @@ neoTRNG repository: https://github.com/stnolting/neoTRNG
The synthesis tool might emit warnings regarding **inferred latches** or **combinatorial loops**. However, this
is not design flaw as this is exactly what we want. ;)
.Simulation
[IMPORTANT]
When simulating the processor the TRNG is automatically set to "simulation mode". In this mode the physical entropy
sources (the ring oscillators) are replaced by a simple **pseudo RNG** based on a LFSR providing only
**deterministic pseudo-random** data. The `TRNG_CTRL_SIM_MODE` flag of the control register is set if simulation
mode is active.
**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. 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.
The TRNG provides two memory mapped interface register. One control register (`CTRL`) for configuration and
status check and one data register (`DATA`) for obtaining the random data. The TRNG is enabled by setting the
control register's `TRNG_CTRL_EN`. As soon as the `TRNG_CTRL_AVAIL` bit is set a new random data byte is
available and can be obtained from the lowest 8 bits of the `DATA` register. If this bit is cleared, there
is no valid data available and the reading `DATA` will return all-zero.
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 random 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 FIFO depth can be retrieved by software via the `TRNG_CTRL_FIFO_*` bits.
new random data from the TRNG to provide some kind of _random data pool_ for applications which require a
larger number of random 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 FIFO depth can be retrieved by software
via the `TRNG_CTRL_FIFO_*` bits.
**TRNG Interrupt**
As the neoTRNG is a rather slow entropy source, a "data available" interrupt is provided to inform the application
software that new random data is available. This interrupt can be trigger by either of two conditions: trigger the
interrupt if _any_ random data is available (i.e. the data FIFO is not empty; `TRNG_CTRL_IRQ_SEL = 0`) or trigger
the interrupt if the random pool is full (i.e. the data FIFO is full; `TRNG_CTRL_IRQ_SEL = 1`).
Once the TRNG interrupt has fired it remains pending until the actual cause of the interrupt is resolved.
.Simulation
[IMPORTANT]
When simulating the processor the TRNG is automatically set to "simulation mode". In this mode the physical
entropy sources (the ring oscillators) are replaced by a simple **pseudo RNG** based on a LFSR providing only
**deterministic pseudo-random** data. The `TRNG_CTRL_SIM_MODE` flag of the control register is set if simulation
mode is active.
**Register Map**
@ -70,13 +63,11 @@ Once the TRNG interrupt has fired it remains pending until the actual cause of t
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.9+<| `0xfffffa00` .9+<| `CTRL` <|`7:0` `TRNG_CTRL_DATA_MSB : TRNG_CTRL_DATA_MSB` ^| r/- <| 8-bit random data
<|`15:8` - ^| r/- <| reserved, read as zero
<|`19:16` `TRNG_CTRL_FIFO_MSB : TRNG_CTRL_FIFO_MSB` ^| r/- <| FIFO depth, log2(`IO_TRNG_FIFO`)
<|`27:20` - ^| r/- <| reserved, read as zero
<|`27` `TRNG_CTRL_IRQ_SEL` ^| r/w <| interrupt trigger select (0 = data available, 1 = FIFO full)
<|`28` `TRNG_CTRL_FIFO_CLR` ^| -/w <| flush random data FIFO when set; flag 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+<| `0xfffa0000` .5+<| `CTRL` <|`0` `TRNG_CTRL_EN` ^| r/w <| TRNG enable
<|`1` `TRNG_CTRL_FIFO_CLR` ^| -/w <| flush random data FIFO when set; flag auto-clears
<|`5:2` `TRNG_CTRL_FIFO_MSB : TRNG_CTRL_FIFO_LSB` ^| r/- <| FIFO depth, log2(`IO_TRNG_FIFO`)
<|`6` `TRNG_CTRL_SIM_MODE` ^| r/- <| simulation mode (PRNG!)
<|`7` `TRNG_CTRL_AVAIL` ^| r/- <| random data available when set
.2+<| `0xfffa0004` .2+<| `DATA` <|`7:0` `TRNG_DATA_MSB : TRNG_DATA_LSB` ^| r/- <| random data byte
<|`31:8` _reserved_ ^| r/- <| reserved, read as zero
|=======================

167
docs/datasheet/soc_twd.adoc Normal file
View file

@ -0,0 +1,167 @@
<<<
:sectnums:
==== Two-Wire Serial Device Controller (TWD)
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
|=======================
| Hardware source files: | neorv32_twd.vhd |
| Software driver files: | neorv32_twd.c |
| | neorv32_twd.h |
| Top entity ports: | `twd_sda_i` | 1-bit serial data line sense input
| | `twd_sda_o` | 1-bit serial data line output (pull low only)
| | `twd_scl_i` | 1-bit serial clock line sense input
| | `twd_scl_o` | 1-bit serial clock line output (pull low only)
| Configuration generics: | `IO_TWD_EN` | implement TWD controller when `true`
| | `IO_TWD_FIFO` | RX/TX FIFO depth, has to be a power of two, min 1
| CPU interrupts: | fast IRQ channel 0 | FIFO status interrupt (see <<_processor_interrupts>>)
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
**Overview**
The NEORV32 TWD implements a I2C-compatible **device-mode** controller. Processor-external hosts can communicate
with this module by issuing I2C transactions. The TWD is entirely passive an only reacts on those transmissions.
Key features:
* Programmable 7-bit device address
* Programmable interrupt conditions
* Configurable RX/TX data FIFO to "program" large TWD sequences without further involvement of the CPU
.Device-Mode Only
[NOTE]
The NEORV32 TWD controller only supports **device mode**. Transmission are initiated by processor-external modules
and not by an external TWD. If you are looking for a _host-mode_ module (transactions initiated by the processor)
check out the <<_two_wire_serial_interface_controller_twi>>.
**Theory of Operation**
The TWD module provides two memory-mapped registers that are used for configuration & status check (`CTRL`) and
for accessing transmission data (`DATA`). The `DATA` register is transparently buffered by separate RX and TX FIFOs.
The size of those FIFOs can be configured by the `IO_TWD_FIFO` generic. Software can determine the FIFO size via the
`TWD_CTRL_FIFO_*` bits.
The module is globally enabled by setting the control register's `TWD_CTRL_EN` bit. Clearing this bit will disable
and reset the entire module also clearing the internal RX and TX FIFOs. Each FIFO can also be cleared individually at
any time by setting `TWD_CTRL_CLR_RX` or `TWD_CTRL_CLR_TX`, respectively.
The external two wire bus is sampled sampled and synchronized to processor's clock domain with a sampling frequency
of 1/8 of the processor's main clock. To increase the resistance to glitches the sampling frequency can be lowered
to 1/64 of the processor clock by setting the `TWD_CTRL_FSEL` bit.
.Current Bus State
[TIP]
The current state of the I²C bus lines (SCL and SDA) can be checked by software via the `TWD_CTRL_SENSE_*` control
register bits. Note that the TWD module needs to be enabled in order to sample the bus state.
The actual 7-bit device address of the TWD is programmed by the `TWD_CTRL_DEV_ADDR` bits. Note that the TWD will
only response to a host transactions if the host issues the according address. Specific general-call or broadcast
addresses are not supported.
Depending on the transaction type, data is either read from the RX FIFO and transferred to the host ("read operation")
or data is received from the host and written to the TX FIFO ("write operation"). Hence, data sequences can be
programmed to the TX FIFO to be fetched from the host. If the TX FIFO is empty and the host keeps performing read
transaction, the transferred data byte is automatically set to all-one.
The current status of the RX and TX FIFO can be polled by software via the `TWD_CTRL_RX_*` and `TWD_CTRL_TX_*`
flags.
**TWD Interrupt**
The TWD module provides a single interrupt to signal certain FIFO conditions to the CPU. The control register's
`TWD_CTRL_IRQ_*` bits are used to enabled individual interrupt conditions. Note that all enabled conditions are
logically OR-ed.
* `TWD_CTRL_IRQ_RX_AVAIL`: trigger interrupt if at least one data byte is available in the RX FIFO
* `TWD_CTRL_IRQ_RX_FULL`: trigger interrupt if the RX FIFO is completely full
* `TWD_CTRL_IRQ_TX_EMPTY`: trigger interrupt if the TX FIFO is empty
The interrupt remains active until all enabled interrupt-causing conditions are resolved.
The interrupt can only trigger if the module is actually enabled (`TWD_CTRL_EN` is set).
**TWD Transmissions**
Two standard I²C-compatible transaction types are supported: **read** operations and **write** operations. These
two operation types are illustrated in the following figure (note that the transactions are split across two lines
to improve readability).
.TWD single-byte read and write transaction timing (not to scale)
image::twd_sequences.png[]
Any new transaction starts with a **START** condition. Then, the host transmits the 7 bit device address MSB-first
(green signals `A6` to `A0`) plus a command bit. The command bit can be either **write** (pulling the SDA line low)
or **read** (leaving the SDA line high). If the transferred address matches the one programmed to to `TWD_CTRL_DEV_ADDR`
control register bits the TWD module will response with an **ACK** (acknowledge) by pulling the SDA bus line actively
low during the 9th SCL clock pulse. If there is no address match the TWD will not interfere with the bus and move back
to idle state.
For a **write transaction** (upper timing diagram) the host can now transfer an arbitrary number of bytes (blue signals
`D7` to `D0`, MSB-first) to the TWD module. Each byte is acknowledged by the TWD by pulling SDA low during the 9th SCL
clock pules (**ACK**). Each received data byte is pushed to the internal RX FIFO. Data will be lost if the FIFO overflows.
The transaction is terminated when the host issues a **STOP** condition.
For a **read transaction** (lower timing diagram) the cost keeps the SDA line at high state while sending the clock
pulse. The TWD will read a byte from the internal TX FIFO and will transmit it MSB-first to the host (blue signals `D7`
to `D0)`. During the 9th clock pulse the host has to acknowledged the transfer (**ACK**). If no ACK is received by the
TWD no data is taken from the TX FIFO and the same byte can be transmitted in the next data phase. If the TX FIFO becomes
empty while the host keeps reading data, all-one bytes are transmitted. The transaction is terminated when the host
issues a **STOP** condition.
A **repeated-START** condition can be issued at any time bringing the TWD back to the start of the address/command
transmission phase. The control register's `TWD_CTRL_BUSY` flag remains high while a bus transaction is in progress.
.Abort / Termination
[TIP]
An active or even stuck transmission can be terminated at any time by disabling the TWD module.
This will also clear the RX/TX FIFOs.
**Tristate Drivers**
The TWD module requires two tristate drivers (actually: open-drain drivers - signals can only be actively driven low) for
the SDA and SCL lines, which have to be implemented by the user in the setup's top module / IO ring. A generic VHDL example
is shown below (here, `sda_io` and `scl_io` are the actual TWD bus lines, which are of type `std_logic`).
.TWD VHDL Tristate Driver Example
[source,VHDL]
----
sda_io <= '0' when (twd_sda_o = '0') else 'Z'; -- drive
scl_io <= '0' when (twd_scl_o = '0') else 'Z'; -- drive
twd_sda_i <= std_ulogic(sda_io); -- sense
twd_scl_i <= std_ulogic(scl_io); -- sense
----
**Register Map**
.TWD register map (`struct NEORV32_TWD`)
[cols="<2,<1,<4,^1,<7"]
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.18+<| `0xffea0000` .18+<| `CTRL` <|`0` `TWD_CTRL_EN` ^| r/w <| TWD enable, reset if cleared
<|`1` `TWD_CTRL_CLR_RX` ^| -/w <| Clear RX FIFO, flag auto-clears
<|`2` `TWD_CTRL_CLR_TX` ^| -/w <| Clear TX FIFO, flag auto-clears
<|`3` `TWD_CTRL_FSEL` ^| r/w <| Bus sample clock / filter select
<|`10:4` `TWD_CTRL_DEV_ADDR6 : TWD_CTRL_DEV_ADDR0` ^| r/w <| Device address (7-bit)
<|`11` `TWD_CTRL_IRQ_RX_AVAIL` ^| r/w <| IRQ if RX FIFO data available
<|`12` `TWD_CTRL_IRQ_RX_FULL` ^| r/w <| IRQ if RX FIFO full
<|`13` `TWD_CTRL_IRQ_TX_EMPTY` ^| r/w <| IRQ if TX FIFO empty
<|`14:9` - ^| r/- <| _reserved_, read as zero
<|`18:15` `TWD_CTRL_FIFO_MSB : TWD_CTRL_FIFO_LSB` ^| r/- <| FIFO depth; log2(`IO_TWD_FIFO`)
<|`24:12` - ^| r/- <| _reserved_, read as zero
<|`25` `TWD_CTRL_RX_AVAIL` ^| r/- <| RX FIFO data available
<|`26` `TWD_CTRL_RX_FULL` ^| r/- <| RX FIFO full
<|`27` `TWD_CTRL_TX_EMPTY` ^| r/- <| TX FIFO empty
<|`28` `TWD_CTRL_TX_FULL` ^| r/- <| TX FIFO full
<|`29` `TWD_CTRL_SENSE_SCL` ^| r/- <| current state of the SCL bus line
<|`30` `TWD_CTRL_SENSE_SDA` ^| r/- <| current state of the SDA bus line
<|`31` `TWD_CTRL_BUSY` ^| r/- <| bus engine is busy (transaction in progress)
.2+<| `0xffea0004` .2+<| `DATA` <|`7:0` `TWD_DATA_MSB : TWD_DATA_LSB` ^| r/w <| RX/TX data FIFO access
<|`31:8` - ^| r/- <| _reserved_, read as zero
|=======================

View file

@ -15,15 +15,20 @@
| Configuration generics: | `IO_TWI_EN` | implement TWI controller when `true`
| | `IO_TWI_FIFO` | FIFO depth, has to be a power of two, min 1
| CPU interrupts: | fast IRQ channel 7 | FIFO empty and module idle interrupt (see <<_processor_interrupts>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
**Overview**
The NEORV32 TWI implements an I2C-compatible host controller to communicate with arbitrary I2C-devices.
The NEORV32 TWI implements aC-compatible host controller to communicate with arbitrary I2C-devices.
Note that peripheral-mode (controller acts as a device) and multi-controller mode are not supported yet.
.Host-Mode Only
[NOTE]
The NEORV32 TWI controller only supports **host mode**. Transmission are initiated by the processor's TWI controller
and not by an external I²C module. If you are looking for a _device-mode_ module (transactions
initiated by an external host) check out the <<_two_wire_serial_device_controller_twd>>.
Key features:
@ -32,7 +37,7 @@ Key features:
* Generate START / repeated-START and STOP conditions
* Sending & receiving 8 data bits including ACK/NACK
* Generating a host-ACK (ACK send by the TWI controller)
* Configurable data/command FIFO to "program" large TWI sequences without further involvement of the CPU
* Configurable data/command FIFO to "program" large I²C sequences without further involvement of the CPU
The TWI controller provides two memory-mapped registers that are used for configuring the module and
for triggering operations: the control and status register `CTRL` and the command and data register `DCMD`.
@ -42,7 +47,7 @@ for triggering operations: the control and status register `CTRL` and the comman
The TWI module requires two tristate drivers (actually: open-drain drivers - signals can only be actively driven low) for
the SDA and SCL lines, which have to be implemented by the user in the setup's top module / IO ring. A generic VHDL example
is shown below (here, `sda_io` and `scl_io` are the actual TWI bus lines, which are of type `std_logic`).
is shown below (here, `sda_io` and `scl_io` are the actual I²C bus lines, which are of type `std_logic`).
.TWI VHDL Tristate Driver Example
[source,VHDL]
@ -111,7 +116,7 @@ that have not been executed yet) or of the TWI bus engine is still processing an
An active transmission can be terminated at any time by disabling the TWI module. This will also clear the data/command FIFO.
[TIP]
The current state of the TWI bus lines (SCL and SDA) can be checked by software via the `TWI_CTRL_SENSE_*` control register bits.
The current state of the I²C bus lines (SCL and SDA) can be checked by software via the `TWI_CTRL_SENSE_*` control register bits.
[NOTE]
When reading data from a device, an all-one byte (`0xFF`) has to be written to TWI data register `NEORV32_TWI.DATA`
@ -131,7 +136,7 @@ TWI module is enabled (`TWI_CTRL_EN` = `1`) and the TX FIFO is empty and the TWI
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.12+<| `0xfffff900` .12+<| `CTRL` <|`0` `TWI_CTRL_EN` ^| r/w <| TWI enable, reset if cleared
.12+<| `0xfff90000` .12+<| `CTRL` <|`0` `TWI_CTRL_EN` ^| r/w <| TWI enable, reset if cleared
<|`3:1` `TWI_CTRL_PRSC2 : TWI_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select
<|`7:4` `TWI_CTRL_CDIV3 : TWI_CTRL_CDIV0` ^| r/w <| 4-bit clock divider
<|`8` `TWI_CTRL_CLKSTR` ^| r/w <| Enable (allow) clock stretching
@ -143,7 +148,7 @@ TWI module is enabled (`TWI_CTRL_EN` = `1`) and the TX FIFO is empty and the TWI
<|`29` `TWI_CTRL_TX_FULL` ^| r/- <| set if the TWI bus is claimed by any controller
<|`30` `TWI_CTRL_RX_AVAIL` ^| r/- <| RX FIFO data available
<|`31` `TWI_CTRL_BUSY` ^| r/- <| TWI bus engine busy or TX FIFO not empty
.3+<| `0xfffff904` .3+<| `DCMD` <|`7:0` `TWI_DCMD_MSB : TWI_DCMD_LSB` ^| r/w <| RX/TX data byte
.3+<| `0xfff90004` .3+<| `DCMD` <|`7:0` `TWI_DCMD_MSB : TWI_DCMD_LSB` ^| r/w <| RX/TX data byte
<|`8` `TWI_DCMD_ACK` ^| r/w <| write: ACK bit sent by controller; read: `1` = device NACK, `0` = device ACK
<|`10:9` `TWI_DCMD_CMD_HI : TWI_DCMD_CMD_LO` ^| r/w <| TWI operation (`00` = NOP, `01` = START conditions, `10` = STOP condition, `11` = data transmission)
|=======================

View file

@ -17,7 +17,7 @@
| | `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>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -128,7 +128,7 @@ Both file are created in the simulation's home folder.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.20+<| `0xfffff500` .20+<| `CTRL` <|`0` `UART_CTRL_EN` ^| r/w <| UART enable
.21+<| `0xfff50000` .21+<| `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
@ -149,7 +149,7 @@ Both file are created in the simulation's home folder.
<|`29` `UART_CTRL_TX_CLR` ^| r/w <| Clear TX FIFO, flag auto-clears
<|`30` `UART_CTRL_RX_OVER` ^| r/- <| RX FIFO overflow; cleared by disabling the module
<|`31` `UART_CTRL_TX_BUSY` ^| r/- <| TX busy or TX FIFO not empty
.4+<| `0xfffff504` .4+<| `DATA` <|`7:0` `UART_DATA_RTX_MSB : UART_DATA_RTX_LSB` ^| r/w <| receive/transmit data
.4+<| `0xfff50004` .4+<| `DATA` <|`7:0` `UART_DATA_RTX_MSB : UART_DATA_RTX_LSB` ^| r/w <| receive/transmit data
<|`11:8` `UART_DATA_RX_FIFO_SIZE_MSB : UART_DATA_RX_FIFO_SIZE_LSB` ^| r/- <| log2(RX FIFO size)
<|`15:12` `UART_DATA_TX_FIFO_SIZE_MSB : UART_DATA_TX_FIFO_SIZE_LSB` ^| r/- <| log2(TX FIFO size)
<|`31:16` ^| r/- <| _reserved_, read as zero
@ -202,6 +202,6 @@ written to UART1-specific file `neorv32.uart1_sim_mode.out`. This data is also p
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
| `0xfffff600` | `CTRL` | ... | ... | Same as UART0
| `0xfffff604` | `DATA` | ... | ... | Same as UART0
| `0xfff60000` | `CTRL` | ... | ... | Same as UART0
| `0xfff60004` | `DATA` | ... | ... | Same as UART0
|=======================

View file

@ -11,7 +11,7 @@
| Top entity ports: | none |
| Configuration generics: | `IO_WDT_EN` | implement watchdog when `true`
| CPU interrupts: | none |
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -84,7 +84,7 @@ The cause of the last system hardware reset can be determined via the `WDT_CTRL_
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Reset value | Writable if locked | Function
.8+<| `0xfffffb00` .8+<| `CTRL` <|`0` `WDT_CTRL_EN` ^| r/w ^| `0` ^| no <| watchdog enable
.8+<| `0xfffb0000` .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
@ -92,5 +92,5 @@ The cause of the last system hardware reset can be determined via the `WDT_CTRL_
<|`6:5` `WDT_CTRL_RCAUSE_HI : WDT_CTRL_RCAUSE_LO` ^| r/- ^| `0` ^| - <| cause of last system reset; 0=external reset, 1=ocd-reset, 2=watchdog reset
<|`7` - ^| r/- ^| - ^| - <| _reserved_, reads as zero
<|`31:8` `WDT_CTRL_TIMEOUT_MSB : WDT_CTRL_TIMEOUT_LSB` ^| r/w ^| 0 ^| no <| 24-bit watchdog timeout value
| `0xfffffb04` | `RESET` |`31:0` | -/w | - | yes | Write _PASSWORD_ to reset WDT timeout counter
| `0xfffb0004` | `RESET` |`31:0` | -/w | - | yes | Write _PASSWORD_ to reset WDT timeout counter
|=======================

View file

@ -110,7 +110,7 @@ It compatible to the the AXI4 `ARPROT` and `AWPROT` signals.
* `xbus_tag_o(2)` **I**: access is an **instruction** fetch when set; access is a data access when cleared
**External Bus Cache (X-CACHE)**
**External Bus Cache (XBUS-CACHE)**
The XBUS interface provides an optional internal cache that can be used to buffer processor-external accesses.
The x-cache is enabled via the `XBUS_CACHE_EN` generic. The total size of the cache is split into the number of

View file

@ -18,7 +18,7 @@
| | `XIP_CACHE_NUM_BLOCKS` | number of blocks in XIP cache; has to be a power of two
| | `XIP_CACHE_BLOCK_SIZE` | number of bytes per XIP cache block; has to be a power of two, min 4
| CPU interrupts: | none |
| Access restrictions: 2+| control registers: privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| control registers: non-32-bit write accesses are ignored
| 2+| XIP data access: read-only
|=======================
@ -193,7 +193,7 @@ The XIP cache is cleared when the XIP module is disabled (`XIP_CTRL_EN = 0`), wh
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.14+<| `0xffffff40` .14+<| `CTRL` <|`0` `XIP_CTRL_EN` ^| r/w <| XIP module enable
.14+<| `0xffff4000` .14+<| `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
@ -207,7 +207,7 @@ The XIP cache is cleared when the XIP module is disabled (`XIP_CTRL_EN = 0`), wh
<|`29:27` - ^| 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
| `0xffff4004` | _reserved_ |`31:0` | r/- | _reserved_, read as zero
| `0xffff4008` | `DATA_LO` |`31:0` | r/w | Direct SPI access - data register low
| `0xffff400C` | `DATA_HI` |`31:0` | -/w | Direct SPI access - data register high; write access triggers SPI transfer
|=======================

View file

@ -11,7 +11,7 @@
| Top entity ports: | `xirq_i` | External interrupts input (32-bit)
| Configuration generics: | `XIRQ_NUM_CH` | Number of external IRQ channels to implement (0..32)
| CPU interrupts: | fast IRQ channel 8 | XIRQ (see <<_processor_interrupts>>)
| Access restrictions: 2+| privileged access only, non-32-bit write accesses are ignored
| Access restrictions: 2+| non-32-bit write accesses are ignored
|=======================
@ -74,10 +74,10 @@ can issue a new CPU interrupt).
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s) | R/W | Description
| `0xfffff300` | `EIE` | `31:0` | r/w | External interrupt enable register (one bit per channel, LSB-aligned)
.3+^| `0xfffff304` .3+<| `ESC` ^| `31` ^| r/c <| XIRQ interrupt when set; write any value to this register to acknowledge the current XIRQ interrupt
| `0xfff30000` | `EIE` | `31:0` | r/w | External interrupt enable register (one bit per channel, LSB-aligned)
.3+^| `0xfff30004` .3+<| `ESC` ^| `31` ^| r/c <| XIRQ interrupt when set; write any value to this register to acknowledge the current XIRQ interrupt
^| `30:5` ^| r/- <| _reserved_, read as zero
^| `4:0` ^| r/c <| Interrupt source ID (0..31) of firing IRQ (prioritized!)
| `0xfffff308` | `TTYP` | `31:0` | r/w | Trigger type select (`0` = level trigger, `1` = edge trigger); each bit corresponds to the according channel number
| `0xfffff30c` | `TPOL` | `31:0` | r/w | Trigger polarity select (`0` = low-level/falling-edge, `1` = high-level/rising-edge); each bit corresponds to the according channel number
| `0xfff30008` | `TTYP` | `31:0` | r/w | Trigger type select (`0` = level trigger, `1` = edge trigger); each bit corresponds to the according channel number
| `0xfff3000c` | `TPOL` | `31:0` | r/w | Trigger polarity select (`0` = low-level/falling-edge, `1` = high-level/rising-edge); each bit corresponds to the according channel number
|=======================

View file

@ -65,17 +65,16 @@ The NEORV32 HAL consists of the following files.
| - | `neorv32.h` | Main NEORV32 library file
| `neorv32_aux.c` | `neorv32_aux.h` | General auxiliary/helper function
| `neorv32_cfs.c` | `neorv32_cfs.h` | <<_custom_functions_subsystem_cfs>> HAL
| `neorv32_crc.c` | `neorv32_crc.h` | <<_cyclic_redundancy_check_crc>> HAL
| `neorv32_clint.c` | `neorv32_clint.h` | <<_core_local_interruptor_clint>> HAL
| `neorv32_cpu.c` | `neorv32_cpu.h` | <<_neorv32_central_processing_unit_cpu>> HAL
| `neorv32_cpu_amo.c` | `neorv32_cpu_amo.h` | Emulation functions for the read-modify-write <<_zalrsc_isa_extension>> / `A` instructions
| | `neorv32_cpu_csr.h` | <<_control_and_status_registers_csrs>> definitions
| `neorv32_cpu_cfu.c` | `neorv32_cpu_cfu.h` | <<_custom_functions_unit_cfu>> HAL
| `neorv32_crc.c` | `neorv32_crc.h` | <<_cyclic_redundancy_check_crc>> HAL
| `neorv32_dma.c` | `neorv32_dma.h` | <<_direct_memory_access_controller_dma>> HAL
| `neorv32_gpio.c` | `neorv32_gpio.h` | <<_general_purpose_input_and_output_port_gpio>> HAL
| `neorv32_gptmr.c` | `neorv32_gptmr.h` | <<_general_purpose_timer_gptmr>> HAL
| - | `neorv32_intrinsics.h` | Macros for intrinsics and custom instructions
| - | `neorv32_legacy.h` | Legacy compatibility layer / wrappers (**do not use for new designs**)
| `neorv32_mtime.c` | `neorv32_mtime.h` | <<_machine_system_timer_mtime>> HAL
| `neorv32_neoled.c` | `neorv32_neoled.h` | <<_smart_led_interface_neoled>> HAL
| `neorv32_onewire.c` | `neorv32_onewire.h` | <<_one_wire_serial_interface_controller_onewire>> HAL
| `neorv32_pwm.c` | `neorv32_pwm.h` | <<_pulse_width_modulation_controller_pwm>> HAL
@ -85,6 +84,7 @@ The NEORV32 HAL consists of the following files.
| `neorv32_spi.c` | `neorv32_spi.h` | <<_serial_peripheral_interface_controller_spi>> HAL
| `neorv32_sysinfo.c` | `neorv32_sysinfo.h` | <<_system_configuration_information_memory_sysinfo>> HAL
| `neorv32_trng.c` | `neorv32_trng.h` | <<_true_random_number_generator_trng>> HAL
| `neorv32_twd.c` | `neorv32_twd.h` | <<_two_wire_serial_device_controller_twd>> HAL
| `neorv32_twi.c` | `neorv32_twi.h` | <<_two_wire_serial_interface_controller_twi>> HAL
| `neorv32_uart.c` | `neorv32_uart.h` | <<_primary_universal_asynchronous_receiver_and_transmitter_uart0>> and UART1 HAL
| `neorv32_wdt.c` | `neorv32_wdt.h` | <<_watchdog_timer_wdt>> HAL
@ -401,11 +401,12 @@ program and gets mapped before the actual application code so it gets executed r
The `crt0.S` start-up performs the following operations:
[start=1]
. Clear <<_mstatus>> CSR.
. Clear <<_mie>> CSR disabling all interrupt sources.
. Setup the stack pointer and the global pointer according to the <<_ram_layout>> provided by the <<_linker_script>> symbols.
. Initialize <<_mstatus>> CSR disabling machine-level interrupts.
. Install an <<_early_trap_handler>> to <<_mtvec>> CSR.
. Initialize the global pointer `gp` and the stack pointer `sp` according to the <<_ram_layout>> provided by the linker script.
. Clear <<_mie>> CSR disabling all interrupt sources.
. Initialize all integer register `x1` - `x31` (only `x1` - `x15` if the `E` CPU extension is enabled).
. If the executing CPU core is not core 0 an SMP-specific code is executed and the CPU is halted in sleep mode. See section <<_dual_core_boot>> for more information.
. Setup `.data` section to configure initialized variables.
. Clear the `.bss` section.
. Call all _constructors_ (if there are any).
@ -413,8 +414,9 @@ The `crt0.S` start-up performs the following operations:
. If `main()` returns:
** All interrupt sources are disabled by clearing <<_mie>> CSR.
** The return value of `main()` is copied to the <<_mscratch>> CSR to allow inspection by the debugger.
** The <<_early_trap_handler>> is re-installed to <<_mtvec>> CSR.
** Call all _destructors_ (if there are any).
** Re-install an <<_early_trap_handler>> to <<_mtvec>> CSR. If any destructor causes an exception the <<_early_trap_handler>> is used for handling.
** Execute an `ebreak` instruction to enter debug mode if an external debugger is connected.
** The CPU enters sleep mode executing the `wfi` instruction in an endless loop.

View file

@ -6,7 +6,19 @@
This section refers to the **default** NEORV32 bootloader. A pre-compiled memory image for the processor-internal
<<_bootloader_rom_bootrom>> is available in the project's +rtl+ folder: `rtl/core/neorv32_bootloader_image.vhd`.
This image is automatically inserted into the boot ROM when synthesizing the processor with the bootloader being
enabled. **Note that the default bootloader image was compiled for a minimal `rv32i` + priv. ISA!**
enabled.
.Minimal RISC-V ISA and Memory Configuration
[NOTE]
The default bootloader image was compiled for a minimal `rv32e_zicsr_zifencei` ISA configuration and only requires a
RAM size of at least 256 bytes. Both constraints ensure that the bootloader can be executed by any actual CPU/processor
configuration. However, the bootloader can recompiled with different capabilities. See the User Guide
https://stnolting.github.io/neorv32/ug/#_customizing_the_internal_bootloader for more information.
.SMP Dual-Core Configuration
[NOTE]
For the SMP <<_dual_core_configuration>> only the primary core (core 0) will boot and execute the bootloader
while the secondary core (core 1) will be halted in sleep mode.
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.
@ -31,7 +43,7 @@ The bootloader requires certain CPU and SoC extensions and modules 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>>.
| _RECOMMENDED_ | The machine timer of the <<_core_local_interruptor_clint>> 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.
| OPTIONAL | The TWI controller (<<_two_wire_serial_interface_controller_twi>>) is needed to boot/execute code directly from pre-programmed TWI memory.

View file

@ -3,7 +3,8 @@
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.
by wrapping the CPU's privileged architecture (i.e. trap-related CSRs and the actual execution of trap handlers)
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
@ -33,9 +34,14 @@ just output a message via the *primary UART (UART0)* to inform the user that a t
handled by the actual application. After sending this message, the RTE tries to continue executing the actual program
by resolving the trap cause.
.Dual-Core Configuration
[NOTE]
The RTE also supports the SMP <<_dual_core_configuration>> as it provides core-individual internal trap management.
==== Using the RTE
.Machine-Mode Only
[IMPORTANT]
All provided RTE functions can be called only from machine-mode code.
@ -48,8 +54,10 @@ the RTE's setup function:
void neorv32_rte_setup(void);
----
.RTE Setup
[NOTE]
The RTE should be enabled right at the beginning of the application's `main` function.
The RTE should be enabled right at the beginning of the application's `main` function. For the SMP
<<_dual_core_configuration>> the RTE setup functions has to be called on each core individually.
[IMPORTANT]
It is recommended to not use the <<_mscratch>> CSR when using the RTE as this register is used to provide services
@ -121,13 +129,13 @@ Do **NOT** use the `((interrupt))` attribute for the application trap handler fu
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 following example shows how to install a custom handler (`custom_mtime_irq_handler`) for handling
the RISC-V machine timer (MTIME) interrupt:
The following example shows how to install a custom handler (`custom_timer_irq_handler`) for handling
the RISC-V CLINT timer interrupt:
.Installing a MTIME IRQ Handler
.Installing a CLINT Timer IRQ Handler
[source,c]
----
neorv32_rte_handler_install(RTE_TRAP_MTI, custom_mtime_irq_handler);
neorv32_rte_handler_install(RTE_TRAP_MTI, custom_timer_irq_handler);
----
User-defined trap handlers can also be un-installed. This will remove the users trap handler from the RTE core
@ -140,17 +148,19 @@ int neorv32_rte_handler_uninstall(uint8_t id);
----
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:
The following example shows how to un-install the custom handler `custom_timer_irq_handler` from the
RISC-V CLINT timer interrupt:
.Example: Removing the Custom MTIME IRQ Handler
.Example: Removing the Custom CLINT Timer IRQ Handler
[source,c]
----
neorv32_rte_handler_uninstall(RTE_TRAP_MTI);
----
[TIP]
The current RTE configuration can be printed via UART0 via the `neorv32_rte_info` function.
.Dual-Core Configuration
[NOTE]
The RTE handler install/uninstall functions can be called on any core in the SMP <<_dual_core_configuration>>.
Internally, the functions will only access the core-specific management entries.
==== Default RTE Trap Handlers

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

After

Width:  |  Height:  |  Size: 294 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 145 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 355 KiB

Before After
Before After

BIN
docs/figures/smp_system.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View file

@ -22,7 +22,7 @@ stnolting@gmail.com
**BSD 3-Clause License**
Copyright (c) NEORV32 contributors.
Copyright (c) 2020 - 2024, Stephan Nolting. All rights reserved.
Copyright (c) 2020 - 2025, Stephan Nolting. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that
the following conditions are met:

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -10,7 +10,6 @@
{name: 'src', wave: 'x0.|.x0.x..|..'},
{name: 'priv', wave: 'x0.|.x0.x..|..'},
{name: 'rvso', wave: 'x0.|.x0.x..|..'},
{name: 'fence', wave: '0....|.....|..'},
],
{},
[

View file

@ -10,7 +10,6 @@
{name: 'src', wave: '0....|.....|.....'},
{name: 'priv', wave: '0....|.....|.....'},
{name: 'rvso', wave: '01..0|.1..0|.1..0', node: '.b.......e....'},
{name: 'fence', wave: '0....|.....|.....'},
],
{},
[

View file

@ -0,0 +1,34 @@
{signal: [
[
"write byte",
{name: 'SDA', wave: '10.7..7..7..7..7..7..7..0..0..x|.', node: 'a.b.....................c..d..e', data: ['A6', 'A5', 'A4', 'A3', 'A2', 'A1', 'A0']},
{name: 'SCL', wave: '1.0.10.10.10.10.10.10.10.10.10.|.'},
{},
{name: 'SDA', wave: 'x|.5..5..5..5..5..5..5..5..0..0.1', node: '...........................f..gh.i', data: ['D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'D0']},
{name: 'SCL', wave: '0|..10.10.10.10.10.10.10.10.10.1.'}
],
{},
{},
[
"read byte",
{name: 'SDA', wave: '10.7..7..7..7..7..7..7..1..0..x|.', node: 'j.k.....................l..m..n', data: ['A6', 'A5', 'A4', 'A3', 'A2', 'A1', 'A0']},
{name: 'SCL', wave: '1.0.10.10.10.10.10.10.10.10.10.|.'},
{},
{name: 'SDA', wave: 'x|.9..9..9..9..9..9..9..9..0..0.1', node: '...........................o..pq.r', data: ['D7', 'D6', 'D5', 'D4', 'D3', 'D2', 'D1', 'D0']},
{name: 'SCL', wave: '0|..10.10.10.10.10.10.10.10.10.1.'}
]
],
edge: [
'a-b START',
'c-d WRITE',
'd-e ACK by TWD',
'f-g ACK by TWD',
'h-i STOP',
'j-k START',
'l-m READ',
'm-n ACK by TWD',
'o-p ACK by HOST',
'q-r STOP'
]
}

View file

@ -57,8 +57,6 @@ include::packaging_vivado.adoc[]
include::simulating_the_processor.adoc[]
include::vhdl_development_environment.adoc[]
include::building_the_documentation.adoc[]
include::zephyr_support.adoc[]

View file

@ -12,7 +12,7 @@ bootloader ROM) and the processor has to be re-synthesized.
[NOTE]
Keep in mind that the maximum size for the bootloader is limited to 8kB and it should be compiled using the
minimal base & privileged ISA `rv32i_zicsr_zifencei` only to ensure it can work independently of the actual CPU configuration.
minimal base & privileged ISA `rv32e_zicsr_zifencei` only to ensure it can work with any actual CPU configuration.
.Bootloader configuration parameters
[cols="<2,^1,^2,<6"]

View file

@ -3,16 +3,16 @@
== Eclipse IDE
Eclipse (https://www.eclipse.org/) is an interactive development environment that can be used to develop, debug and profile
application code for the NEORV32 RISC-V Processor. This chapter shows how to import the provided example setup
application code for the NEORV32 RISC-V Processor. This chapter shows how to import the provided **example setup**
from the NEORV32 project repository. Additionally, all the required steps to create a compatible project from
scratch are illustrated in this chapter.
.This is a Makefile-Based Project!
[IMPORTANT]
Note that the provided Eclipse project as well as the setup tutorial in this section implement a **makefile-based project**.
Hence, the makefile in the example folder (that includes the main NEORV32 makefile) is used for building the application
instead of the Eclipse-managed build. Therefore, **all compiler options, include folder, source files, etc. have to be
defined within the project's makefile**.
Note that the provided Eclipse example project (as well as the setup tutorial in this section) implements a
**makefile-based project**. Hence, the makefile in the example folder is used for building the application
instead of the Eclipse-managed build system. Therefore, **all compiler options, include folder, source files,
etc. have to be defined within this makefile**.
.Developing and debugging code for the NEORV32 using the Eclipse IDE
image::eclipse.png[align=center]
@ -26,18 +26,18 @@ The following tools are required:
* Eclipse IDE (**Eclipse IDE for Embedded C/C++ Developers**): https://www.eclipse.org/downloads/
* Precompiled RISC-V GCC toolchain: e.g. https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack
* Precompiled OpenOCD binaries: e.g. https://github.com/xpack-dev-tools/openocd-xpack
* Build tools like make: e.g. https://github.com/xpack-dev-tools/windows-build-tools-xpack
* Build tools like make and busybox: e.g. https://github.com/xpack-dev-tools/windows-build-tools-xpack
.XPack Windows Build Tools
[IMPORTANT]
The NEORV32 makefile relies on the `basename` command which is not part of the default XPack
Windows Build Tools. However, you can just open the `bin` folder, copy `busybox.exe` and rename
that copy to `basename.exe`.
[TIP]
The NEORV32 makefile relies on the `basename` command which might not be part of the default XPack Windows Build Tools.
However, you can just open the according `bin` folder, copy `busybox.exe` and rename that copy to `basename.exe`.
:sectnums:
=== Import The Provided Eclipse Example Project
A pre-configured Eclipse project is available in `neorv32/sw/example/eclipse`.
A preconfigured Eclipse project is available in `neorv32/sw/example/eclipse`.
To import it:
[start=1]
@ -50,30 +50,26 @@ To import it:
.NEORV32 Folder and File Paths
[IMPORTANT]
The provided example project uses **relative paths** for including all the NEORV32-specific files and folders
(in the Eclipse configuration files). Note that these paths need to be adjusted when moving this example setup
to a different location (makefile, NEORV32 sources, etc.).
(in the Eclipse configuration files). Note that these paths need to be adjusted when moving the example setup
to a different location.
.Executables Configuration
.Tool Configuration
[IMPORTANT]
Make sure to adjust the binaries / installation folders of the RISC-V GCC toolchain
and OpenOCD according to your installation. See the following chapter for more information.
Make sure to adjust the binaries / installation folders of the RISC-V GCC toolchain, openOCD and Windows build tools
according to your installation. See the section <<_configure_build_tools>> for more information.
.Makefile Adjustment
[IMPORTANT]
Make sure to adjust the variables inside the project's makefile to match your processor
configuration (memory sizes, CPU ISA configuration, etc.).
configuration (memory sizes, CPU ISA configuration, etc.):
https://stnolting.github.io/neorv32/#_application_makefile
:sectnums:
=== Setup a new Eclipse Project from Scratch
This chapter shows all the steps required to create an Eclipse project for the NEORV32 entirely from scratch.
.This is an early version! ;)
[NOTE]
The provided Eclipse project as well as the tutorial from this chapter are in a very early stage.
This setup was build and tested on Windows.
Feel free to open a new issue or pull request to improve this setup.
==== Create a new Project
[start=1]
@ -99,7 +95,6 @@ If you need to reconfigure the RISC-V GCC binaries and/or paths:
. adjust the **Toolchain folder** and the **Toolchain name** if required
. Click **Apply**.
==== Add Initial Files
Start a simple project by adding two initial files. Further files can be added later. Only the makefile is really
@ -129,7 +124,7 @@ that there are no (incompatible) artifacts left from previous builds.
See the NEORV32 data sheet for a list and description of all available makefile targets:
https://stnolting.github.io/neorv32/#_makefile_targets
=== Configure Build Tools
==== Configure Build Tools
This step is only required if your system does not provide any build tools (like `make`) by default.
@ -140,7 +135,7 @@ This step is only required if your system does not provide any build tools (like
* Example: `Build tools folder: C:/xpack/xpack-windows-build-tools-4.4.1-2/bin`
. Click **Apply and Close**.
=== Adjust Default Build Configuration (optional)
==== Adjust Default Build Configuration (optional)
This will simplify the auto-build by replacing the default `make all` command by `make elf`. Thus, only
the required `main.elf` file gets generated instead of _all_ executable files (like HDL and memory image files).
@ -231,6 +226,7 @@ Installing TM Terminal from the Eclipse market place:
. Select **TM Terminal** from the list and install it.
. After installation restart Eclipse.
=== Eclipse Setup References
* Eclipse help: https://help.eclipse.org/latest/index.jsp

View file

@ -2,12 +2,12 @@
:sectnums:
== Simulating the Processor
The NEORV32 project includes a core CPU, built-in peripherals in the Processor Subsystem, and additional
peripherals in the templates and examples. Therefore, there is a wide range of possible testing and
verification strategies.
Due to the complexity of the NEORV32 processor and all the different configuration options,
there is a wide range of possible testing and verification strategies.
On the one hand, a simple smoke testbench allows ensuring that functionality is correct from a software point of view.
That is used for running the RISC-V architecture tests, in order to guarantee compliance with the ISA specification(s).
All required simulation sources are located in `sim`.
On the other hand, http://vunit.github.io/[VUnit] and http://vunit.github.io/verification_components/user_guide.html[Verification Components]
are used for verifying the functionality of the various peripherals from a hardware point of view.
@ -26,68 +26,58 @@ ensure that all application images (`*_image.vhd`) are reanalyzed when recompili
A more sophisticated testbench using **VUnit** is available in a separate repository:
https://github.com/stnolting/neorv32-vunit
A plain-VHDL (no third-party libraries) testbench (`sim/neorv32_tb.vhd`) can be used for simulating and
testing the processor. This testbench features a 100MHz clock and enables all optional peripheral and CPU
extensions.
.True Random Number Generator
[NOTE]
The NEORV32 TRNG will be set to "simulation mode" when enabled for simulation (replacing the ring-oscillators
by pseudo-random LFSRs). See the neoTRNG documentation for more information.
A plain-VHDL testbench without any third-party libraries / dependencies (`sim/neorv32_tb.vhd`) can be used for simulating
and testing the processor and all its configurations. This testbench features clock and reset generators and enables all
optional peripheral and CPU extensions. The processor check program (`sw/example/processor_check`) is develop in close
relation to the default testbench in order to test all primary processor functions.
The simulation setup is configured via the "User Configuration" section located right at the beginning of
the testbench architecture. Each configuration constant provides comments to explain the functionality.
[IMPORTANT]
The simulated NEORV32 does not use the bootloader and _directly boots_ the current application image (from
the `rtl/core/neorv32_application_image.vhd` image file).
the testbench architecture. Each configuration generic provides a default value and a comments to explain the functionality.
Basically, these configuration generics represent most of the processor's **top generics**.
.UART output during simulation
[IMPORTANT]
Data written to the NEORV32 UART0 / UART1 transmitter is send to a virtual UART receiver implemented
as part of the testbench. Received chars are send to the simulator console and are also stored to a log file
(`neorv32.testbench_uart0.out` for UART0, `neorv32.testbench_uart1.out` for UART1) inside the simulation's home folder.
**Please note that printing via the native UART receiver takes a lot of time.** For faster simulation console output
see section <<_faster_simulation_console_output>>.
Data written to the NEORV32 UART0 / UART1 transmitter is send to a virtual UART receiver implemented as part of the default
testbench. The received chars are send to the simulator console and are also stored to a log file (`neorv32_tb.uart0_rx.out`
for UART0, `neorv32_tb.uart1_rx.out` for UART1) inside the simulator's home folder. **Please note that printing via the
native UART receiver takes a lot of time.** For faster simulation console output see section <<_faster_simulation_console_output>>.
:sectnums:
=== Faster Simulation Console Output
When printing data via the physical UART the communication speed will always be based on the configured BAUD
rate. For a simulation this might take some time. To have faster output you can enable the **simulation mode**
for UART0/UART1 (see section https://stnolting.github.io/neorv32/#_primary_universal_asynchronous_receiver_and_transmitter_uart0[Documentation: Primary Universal Asynchronous Receiver and Transmitter (UART0)]).
When printing data via the physical UART the communication speed will always be based on the configured BAUD rate. For a
simulation this might take some time. To have faster output you can enable the **simulation mode** for UART0/UART1 (see
section https://stnolting.github.io/neorv32/#_primary_universal_asynchronous_receiver_and_transmitter_uart0).
ASCII data sent to UART0|UART1 will be immediately printed to the simulator console and logged to files in the simulator
execution directory:
ASCII data sent to UART0 / UART1 will be immediately printed to the simulator console and logged to files in the
simulator's home directory.
* `neorv32.uart*.sim_mode.text.out`: ASCII data.
* `neorv32.uart0_sim_mode.out`: ASCII data send via UART0
* `neorv32.uart1_sim_mode.out`: ASCII data send via UART1
You can "automatically" enable the simulation mode of UART0/UART1 when compiling an application.
In this case, the "real" UART0/UART1 transmitter unit is permanently disabled.
To enable the simulation mode just compile and install your application and add _UART?_SIM_MODE_ to the compiler's
_USER_FLAGS_ variable (do not forget the `-D` suffix flag):
.Automatic Simulation Mode
[TIP]
You can "automatically" enable the simulation mode of UART0/UART1 when compiling an application. In this case, the "real"
UART0/UART1 transmitter unit is permanently disabled by setting the UART's "sim-mode" bit.
To enable the simulation mode just compile and install the application and add `-DUART0_SIM_MODE` `-DUART0_SIM_MODE` /
`-DUART1_SIM_MODE` to the compiler's `USER_FLAGS` variable (do not forget the `-D` suffix flag):
.Auto-Enable UART0 Simulation-Mode while Compiling
[source, bash]
----
sw/example/demo_blink_led$ make USER_FLAGS+=-DUART0_SIM_MODE clean_all all
----
The provided define will change the default UART0/UART1 setup function in order to set the simulation
mode flag in the according UART's control register.
[NOTE]
The UART simulation output (to file and to screen) outputs "complete lines" at once. A line is
completed with a line feed (newline, ASCII `\n` = 10).
:sectnums:
=== Simulation using a shell script (with GHDL)
=== GHDL Simulation
To simulate the processor using _GHDL_ navigate to the `sim` folder and run the provided shell script.
Any arguments that are provided while executing this script are passed to GHDL.
For example the simulation time can be set to 20ms using `--stop-time=20ms` as argument.
The default simulation setup that is also used by the project's CI pipeline is based on the free and open-source VHDL
simulator **GHDL**. The `sim` folder also contains a simple script that evaluates and simulates all core files.
This script can be called right from the command. Optionally, additional GHDL flags can be passes.
.Invoking the default GHDL simulation script
[source, bash]
----
neorv32/sim$ sh ghdl.sh --stop-time=20ms
@ -95,73 +85,76 @@ neorv32/sim$ sh ghdl.sh --stop-time=20ms
:sectnums:
=== Simulation using Application Makefiles (In-Console with GHDL)
=== Simulation using Application Makefiles
To directly compile and run a program in the console (using the default testbench and GHDL
as simulator) you can use the `sim` makefile target. Make sure to use the UART simulation mode
(`USER_FLAGS+=-DUART0_SIM_MODE` and/or `USER_FLAGS+=-DUART1_SIM_MODE`) to get
faster / direct-to-console UART output.
The <<_ghdl_simulation>> can also be started by the main application makefile (i.e. from each SW project folder).
.Starting the GHDL simulation from the application makefile
[source, bash]
----
sw/example/demo_blink_led$ make USER_FLAGS+=-DUART0_SIM_MODE clean_all sim
sw/example/demo_blink_led$ make USER_FLAGS+=-DUART0_SIM_MODE clean_all install sim
[...]
Blinking LED demo program
----
Makefile targets:
* `clean_all`: delete all artifacts and rebuild everything
* `install`: install executable
* `sim`: run GHDL simulation
.Adjusting the Testbench Configuration
[TIP]
The testbench provides several generics for customization. These can be adjusted in-console using the
makefile's `GHDL_RUN_FLAGS` variable. E.g.: `make GHDL_RUN_FLAGS+="-gBOOT_MODE_SELECT=1" sim`
:sectnums:
==== Hello World!
To do a quick test of the NEORV32 make sure to have https://github.com/ghdl/ghdl[GHDL] and a
https://github.com/stnolting/riscv-gcc-prebuilt[RISC-V gcc toolchain] installed.
Navigate to the project's `sw/example/hello_world` folder and run `make USER_FLAGS+=-DUART0_SIM_MODE clean_all sim`:
To do a quick test of the NEORV32 make and the required tools navigate to the project's `sw/example/hello_world`
folder and run `make USER_FLAGS+=-DUART0_SIM_MODE clean_all install sim`:
[source, bash]
----
neorv32/sw/example/hello_world$ make USER_FLAGS+=-DUART0_SIM_MODE clean_all sim
neorv32/sw/example/hello_world$ make USER_FLAGS+=-DUART0_SIM_MODE clean_all install sim
../../../sw/lib/source/neorv32_uart.c: In function 'neorv32_uart_setup':
../../../sw/lib/source/neorv32_uart.c:116:2: warning: #warning UART0_SIM_MODE (primary UART) enabled! Sending all UART0.TX data to text.io simulation output instead of real UART0 transmitter. Use this for simulations only! [-Wcpp]
116 | #warning UART0_SIM_MODE (primary UART) enabled! Sending all UART0.TX data to text.io simulation output instead of real UART0 transmitter. Use this for simulations only! <1>
../../../sw/lib/source/neorv32_uart.c:109:2: warning: #warning UART0_SIM_MODE (primary UART) enabled! Sending all UART0.TX data to text.io simulation output instead of real UART0 transmitter. Use this for simulation only! [-Wcpp]
109 | #warning UART0_SIM_MODE (primary UART) enabled! Sending all UART0.TX data to text.io simulation output instead of real UART0 transmitter. Use this for simulation only! <1>
| ^~~~~~~
Memory utilization:
text data bss dec hex filename
4664 0 116 4780 12ac main.elf <2>
Compiling ../../../sw/image_gen/image_gen
5540 0 116 5656 1618 main.elf <2>
Compiling image generator...
Generating neorv32_application_image.vhd
Installing application image to ../../../rtl/core/neorv32_application_image.vhd <3>
Simulating neorv32_application_image.vhd...
Tip: Compile application with USER_FLAGS+=-DUART[0/1]_SIM_MODE to auto-enable UART[0/1]'s simulation mode (redirect UART output to simulator console). <4>
Using simulation run arguments: --stop-time=10ms <5>
../../rtl/core/neorv32_top.vhd:355:5:@0ms:(assertion note): [NEORV32] The NEORV32 RISC-V Processor (version 0x01090504), github.com/stnolting/neorv32 <6>
../../rtl/core/neorv32_top.vhd:361:5:@0ms:(assertion note): [NEORV32] Processor Configuration: IMEM DMEM I-CACHE D-CACHE WISHBONE GPIO MTIME UART0 UART1 SPI SDI TWI PWM WDT TRNG CFS NEOLED XIRQ GPTMR XIP ONEWIRE DMA SLINK CRC SYSINFO OCD
../../rtl/core/neorv32_clockgate.vhd:60:3:@0ms:(assertion warning): [NEORV32] Clock gating enabled (using generic clock switch).
../../rtl/core/neorv32_cpu.vhd:142:3:@0ms:(assertion note): [NEORV32] CPU ISA: rv32imabu_zicsr_zicntr_zicond_zifencei_zfinx_zihpm_zxcfu_sdext_sdtrig_smpmp
../../rtl/core/neorv32_cpu.vhd:163:3:@0ms:(assertion note): [NEORV32] CPU tuning options: fast_mul fast_shift
../../rtl/core/neorv32_cpu.vhd:170:3:@0ms:(assertion warning): [NEORV32] Assuming this is a simulation.
../../rtl/core/neorv32_cpu_cp_fpu.vhd:292:3:@0ms:(assertion warning): [NEORV32] The floating-point unit (Zfinx) is still in experimental state.
../../rtl/core/mem/neorv32_imem.legacy.vhd:72:3:@0ms:(assertion note): [NEORV32] Implementing LEGACY processor-internal IMEM as pre-initialized ROM.
../../rtl/core/neorv32_wishbone.vhd:117:3:@0ms:(assertion note): [NEORV32] Ext. Bus Interface (WISHBONE) - PIPELINED Wishbone protocol, auto-timeout, LITTLE-endian byte order, registered RX, registered TX
../../rtl/core/neorv32_trng.vhd:343:3:@0ms:(assertion note): [neoTRNG NOTE] << neoTRNG V3 - A Tiny and Platform-Independent True Random Number Generator >>
../../rtl/core/neorv32_trng.vhd:545:5:@0ms:(assertion warning): [neoTRNG WARNING] Implementing non-physical pseudo-RNG!
../../rtl/core/neorv32_trng.vhd:545:5:@0ms:(assertion warning): [neoTRNG WARNING] Implementing non-physical pseudo-RNG!
../../rtl/core/neorv32_trng.vhd:545:5:@0ms:(assertion warning): [neoTRNG WARNING] Implementing non-physical pseudo-RNG!
../../rtl/core/neorv32_debug_dm.vhd:227:3:@0ms:(assertion note): [NEORV32] OCD DM compatible to debug spec. version 1.0
<7>
Simulating processor using default testbench...
GHDL simulation run parameters: --stop-time=10ms <4>
../rtl/core/neorv32_top.vhd:351:5:@0ms:(assertion note): [NEORV32] The NEORV32 RISC-V Processor (v1.10.7.6), github.com/stnolting/neorv32
../rtl/core/neorv32_top.vhd:357:5:@0ms:(assertion note): [NEORV32] Processor Configuration: CPU IMEM-ROM DMEM I-CACHE D-CACHE XBUS XBUS-CACHE XIP XIP-CACHE CLINT GPIO UART0 UART1 SPI SDI TWI TWD PWM WDT TRNG CFS NEOLED XIRQ GPTMR ONEWIRE DMA SLINK CRC SYSINFO OCD-AUTH
../rtl/core/neorv32_top.vhd:411:5:@0ms:(assertion note): [NEORV32] BOOT_MODE_SELECT = 2: booting IMEM image
../rtl/core/neorv32_clockgate.vhd:38:3:@0ms:(assertion warning): [NEORV32] Clock gating enabled (using default/generic clock switch).
../rtl/core/neorv32_cpu.vhd:135:3:@0ms:(assertion note): [NEORV32] CPU ISA: rv32ibmux_zalrsc_zba_zbb_zbkb_zbkc_zbkx_zbs_zicntr_zicond_zicsr_zifencei_zihpm_zfinx_zkn_zknd_zkne_zknh_zks_zksed_zksh_zkt_zmmul_zxcfu_sdext_sdtrig_smpmp
../rtl/core/neorv32_cpu.vhd:171:3:@0ms:(assertion note): [NEORV32] CPU tuning options: fast_mul fast_shift rf_hw_rst
../rtl/core/neorv32_cpu.vhd:178:3:@0ms:(assertion warning): [NEORV32] Assuming this is a simulation.
../rtl/core/neorv32_imem.vhd:59:3:@0ms:(assertion note): [NEORV32] Implementing processor-internal IMEM as pre-initialized ROM.
../rtl/core/neorv32_trng.vhd:277:3:@0ms:(assertion note): [neoTRNG] The neoTRNG (v3.2) - A Tiny and Platform-Independent True Random Number Generator, https://github.com/stnolting/neoTRNG
../rtl/core/neorv32_trng.vhd:284:3:@0ms:(assertion warning): [neoTRNG] Simulation-mode enabled (NO TRUE/PHYSICAL RANDOM)!
../rtl/core/neorv32_debug_auth.vhd:48:3:@0ms:(assertion warning): [NEORV32] using DEFAULT on-chip debugger authenticator. Replace by custom module.
<5>
## ## ## ##
## ## ######### ######## ######## ## ## ######## ######## ## ################
#### ## ## ## ## ## ## ## ## ## ## ## ## ## #### ####
## ## ## ## ## ## ## ## ## ## ## ## ## ## ###### ##
## ## ## ######### ## ## ######### ## ## ##### ## ## #### ###### ####
## ## ## ## ## ## ## ## ## ## ## ## ## ## ###### ##
## #### ## ## ## ## ## ## ## ## ## ## ## #### ####
## ## ######### ######## ## ## ## ######## ########## ## ################
## ## ## ## ## ## ## ## ## ## ## ## ## ## ###### ##
## #### ## ## ## ## ## ## ## ## ## ## ## #### ####
## ## ######### ######## ## ## ## ######## ########## ## ################
## ## ## ##
Hello world! :)
----
<1> Notifier that "simulation mode" of UART0 is enabled (by the `USER_FLAGS+=-DUART0_SIM_MODE` makefile flag). All UART0 output is send to the simulator console.
<2> Final executable size (`text`) and _static_ data memory requirements (`data`, `bss`).
<3> The application code is _installed_ as pre-initialized IMEM. This is the default approach for simulation.
<4> A note regarding UART "simulation mode", but we have already enabled that.
<5> List of (default) arguments that were send to the simulator. Here: maximum simulation time (10ms).
<6> "Sanity checks" from the core's VHDL files. These reports give some brief information about the SoC/CPU configuration (-> generics). If there are problems with the current configuration, an ERROR will appear.
<7> Execution of the actual program starts.
<4> List of (default) arguments that were send to the simulator. Here: maximum simulation time (10ms).
<5> Execution of the actual program starts. UART0 TX data is printed right to the console.

View file

@ -1,12 +0,0 @@
<<<
:sectnums:
== VHDL Development Environment
To navigate and develop the NEORV32 processor VHDL code you can use the free and open source VHDL-LS language server.
The easiest way to get started is to install the https://marketplace.visualstudio.com/items?itemName=hbohlin.vhdl-ls[VHDL-LS VSCode extension].
The VHDL-LS server requires a `vhdl_ls.toml` file which is automatically generated by the `sim/run.py` script. See <<_simulating_the_processor, Simulate the processor>> for further information.
1. Run `sim/run.py` to create the library mapping file
2. Install the VHDL-LS VSCode extension
3. Open the root folder of the NEORV32 repository in VSCode
4. Open any VHDL file

View file

@ -1,8 +1,7 @@
-- The NEORV32 RISC-V Processor - github.com/stnolting/neorv32
-- Auto-generated memory initialization package (for internal IMEM)
-- Source: demo_blink_led/main.bin
-- Size: 1100 bytes
-- Built: 31.10.2024 22:32:21
-- Auto-generated memory initialization image (for internal IMEM)
-- Source: demo_blink_led/build/main.bin
-- Built: 02.01.2025 14:38:10
library ieee;
use ieee.std_logic_1164.all;
@ -12,24 +11,36 @@ use neorv32.neorv32_package.all;
package neorv32_application_image is
constant application_init_image : mem32_t := (
x"000020b7",
x"80008093",
x"30009073",
x"00000097",
x"13808093",
x"30509073",
x"30401073",
constant application_init_size_c : natural := 1260; -- bytes
constant application_init_image_c : mem32_t := (
x"f14020f3",
x"80002217",
x"fe320213",
x"ffb20213",
x"ff027113",
x"80000197",
x"7d818193",
x"00000293",
x"00000313",
x"00000393",
x"00000413",
x"00000493",
x"7f018193",
x"000022b7",
x"80028293",
x"30029073",
x"00000317",
x"1b030313",
x"30531073",
x"30401073",
x"00000397",
x"4b838393",
x"80000417",
x"fc440413",
x"80000497",
x"fbc48493",
x"80000517",
x"fb450513",
x"80000597",
x"fac58593",
x"00000617",
x"1b860613",
x"00000693",
x"00000713",
x"00000793",
x"00000813",
x"00000893",
x"00000913",
@ -46,52 +57,77 @@ x"00000e13",
x"00000e93",
x"00000f13",
x"00000f93",
x"44c00593",
x"80000617",
x"f7860613",
x"80000697",
x"f7068693",
x"00c58e63",
x"00d65c63",
x"0005a703",
x"00e62023",
x"00458593",
x"00460613",
x"06008463",
x"00000797",
x"01878793",
x"30579073",
x"30446073",
x"30046073",
x"1040006f",
x"34202773",
x"800007b7",
x"00378793",
x"00f70463",
x"30200073",
x"0ff0000f",
x"fff446b7",
x"0086a683",
x"1337d737",
x"afe70713",
x"0006a783",
x"00f70463",
x"30200073",
x"0086a103",
x"00c6a603",
x"0006a023",
x"fff40737",
x"00072223",
x"0540006f",
x"00838e63",
x"00945c63",
x"0003a783",
x"00f42023",
x"00438393",
x"00440413",
x"fedff06f",
x"80000717",
x"f4c70713",
x"80000797",
x"f4478793",
x"00f75863",
x"00072023",
x"00470713",
x"00b55863",
x"00052023",
x"00450513",
x"ff5ff06f",
x"00000417",
x"37840413",
x"3a840413",
x"00000497",
x"37048493",
x"3a048493",
x"00945a63",
x"00042083",
x"000080e7",
x"00440413",
x"ff1ff06f",
x"0ff0000f",
x"0000100f",
x"30029073",
x"34201073",
x"34101073",
x"00000513",
x"00000593",
x"084000ef",
x"000600e7",
x"30401073",
x"34051073",
x"00000517",
x"04450513",
x"30551073",
x"f1402473",
x"02041463",
x"00000417",
x"03840413",
x"30541073",
x"00000417",
x"33440413",
x"34840413",
x"00000497",
x"32c48493",
x"34048493",
x"00945a63",
x"00042083",
x"000080e7",
x"00440413",
x"ff1ff06f",
x"00100073",
x"10500073",
x"ffdff06f",
x"34041073",
@ -115,16 +151,22 @@ x"00000513",
x"00000593",
x"00112623",
x"00812423",
x"11c000ef",
x"138000ef",
x"00000513",
x"00150413",
x"00000593",
x"0ff57513",
x"108000ef",
x"124000ef",
x"0fa00513",
x"020000ef",
x"038000ef",
x"00040513",
x"fe5ff06f",
x"fff4c7b7",
x"ffc7a583",
x"ff87a503",
x"ffc7a703",
x"fee59ae3",
x"00008067",
x"c80027f3",
x"c0002573",
x"c80025f3",
@ -135,13 +177,13 @@ x"00112e23",
x"00812c23",
x"00912a23",
x"00a12623",
x"0f8000ef",
x"0e0000ef",
x"3e800593",
x"1b0000ef",
x"1a8000ef",
x"00c12603",
x"00000693",
x"00000593",
x"10c000ef",
x"0f8000ef",
x"00050413",
x"00058493",
x"fc0027f3",
@ -164,19 +206,20 @@ x"01812403",
x"01412483",
x"02010113",
x"00008067",
x"e0802783",
x"fffe07b7",
x"0087a783",
x"00f79713",
x"02075663",
x"05c000ef",
x"f3dff0ef",
x"00850433",
x"00a43533",
x"009585b3",
x"00b504b3",
x"048000ef",
x"f29ff0ef",
x"fe95eee3",
x"fcb492e3",
x"fcb490e3",
x"fe856ae3",
x"fbdff06f",
x"fb9ff06f",
x"01c49493",
x"00445413",
x"0084e433",
@ -185,19 +228,13 @@ x"00040863",
x"fff40413",
x"00000013",
x"ff1ff06f",
x"f99ff06f",
x"c0000793",
x"f95ff06f",
x"fffc07b7",
x"00a7a423",
x"00b7a623",
x"00008067",
x"fffff7b7",
x"40078793",
x"0047a583",
x"fffe07b7",
x"0007a503",
x"0047a703",
x"fee59ae3",
x"00008067",
x"e0002503",
x"00008067",
x"00050613",
x"00000513",
@ -208,39 +245,42 @@ x"0015d593",
x"00161613",
x"fe0596e3",
x"00008067",
x"00050e13",
x"ff010113",
x"00068313",
x"00812423",
x"00050413",
x"00112623",
x"00060513",
x"000e0893",
x"00060693",
x"00000713",
x"00068393",
x"00040713",
x"00060813",
x"00000793",
x"00000813",
x"0016fe93",
x"00171613",
x"000e8a63",
x"01088833",
x"00e787b3",
x"01183733",
x"00f707b3",
x"01f8d713",
x"0016d693",
x"00e66733",
x"00189893",
x"fc069ae3",
x"00000313",
x"00000f13",
x"01e708b3",
x"00187e93",
x"00f30fb3",
x"01f75e13",
x"00185813",
x"00e8b2b3",
x"00179793",
x"000e8663",
x"00088f13",
x"01f28333",
x"00171713",
x"01c7e7b3",
x"fc0818e3",
x"00058663",
x"f81ff0ef",
x"00a787b3",
x"00030a63",
x"000e0513",
x"00030593",
x"f6dff0ef",
x"00f507b3",
x"f79ff0ef",
x"00650333",
x"00038a63",
x"00040513",
x"00038593",
x"f65ff0ef",
x"00650333",
x"00c12083",
x"00080513",
x"00078593",
x"00812403",
x"000f0513",
x"00030593",
x"01010113",
x"00008067",
x"06054063",

View file

@ -28,11 +28,11 @@ end neorv32_boot_rom;
architecture neorv32_boot_rom_rtl of neorv32_boot_rom is
-- determine physical ROM size in bytes (expand to next power of two) --
constant boot_rom_size_index_c : natural := index_size_f((bootloader_init_image'length)); -- address with (32-bit entries)
constant boot_rom_size_c : natural range 0 to mem_boot_size_c := (2**boot_rom_size_index_c)*4; -- physical size in bytes
constant boot_rom_size_index_c : natural := index_size_f((bootloader_init_size_c)); -- address with (bytes)
constant boot_rom_size_c : natural range 0 to iodev_size_c := (2**boot_rom_size_index_c); -- physical size in bytes
-- ROM initialized with executable code --
constant mem_rom_c : mem32_t(0 to boot_rom_size_c/4-1) := mem32_init_f(bootloader_init_image, boot_rom_size_c/4);
constant mem_rom_c : mem32_t(0 to boot_rom_size_c/4-1) := mem32_init_f(bootloader_init_image_c, boot_rom_size_c/4);
-- local signals --
signal rden : std_ulogic;

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,5 @@
-- ================================================================================ --
-- NEORV32 SoC - Processor Bus Infrastructure: Prioritizing 2-to-1 Bus Switch --
-- -------------------------------------------------------------------------------- --
-- Allows to access a single device bus X by two controller ports A and B. --
-- Controller port A has priority over controller port B. --
-- NEORV32 SoC - Processor Bus Infrastructure: 2-to-1 Bus Switch --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
@ -19,14 +16,15 @@ use neorv32.neorv32_package.all;
entity neorv32_bus_switch is
generic (
PORT_A_READ_ONLY : boolean; -- set if port A is read-only
PORT_B_READ_ONLY : boolean -- set if port B is read-only
ROUND_ROBIN_EN : boolean := false; -- enable round-robing scheduling
PORT_A_READ_ONLY : boolean := false; -- set if port A is read-only
PORT_B_READ_ONLY : boolean := false -- set if port B is read-only
);
port (
clk_i : in std_ulogic; -- global clock, rising edge
rstn_i : in std_ulogic; -- global reset, low-active, async
a_lock_i : in std_ulogic; -- exclusive access for port A while set
a_req_i : in bus_req_t; -- host port A request bus (PRIORITIZED)
a_req_i : in bus_req_t; -- host port A request bus
a_rsp_o : out bus_rsp_t; -- host port A response bus
b_req_i : in bus_req_t; -- host port B request bus
b_rsp_o : out bus_rsp_t; -- host port B response bus
@ -38,17 +36,10 @@ end neorv32_bus_switch;
architecture neorv32_bus_switch_rtl of neorv32_bus_switch is
-- access arbiter --
type arbiter_t is record
state, state_nxt : std_ulogic_vector(1 downto 0);
a_req, b_req : std_ulogic;
sel, stb : std_ulogic;
end record;
signal arbiter : arbiter_t;
-- FSM states --
constant IDLE : std_ulogic_vector(1 downto 0) := "00";
constant BUSY_A : std_ulogic_vector(1 downto 0) := "01";
constant BUSY_B : std_ulogic_vector(1 downto 0) := "10";
type state_t is (S_CHECK_A, S_BUSY_A, S_CHECK_B, S_BUSY_B);
signal state, state_nxt : state_t;
signal a_req, b_req : std_ulogic;
signal sel, stb : std_ulogic;
begin
@ -57,96 +48,251 @@ begin
arbiter_sync: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
arbiter.state <= IDLE;
arbiter.a_req <= '0';
arbiter.b_req <= '0';
state <= S_CHECK_A;
a_req <= '0';
b_req <= '0';
elsif rising_edge(clk_i) then
arbiter.state <= arbiter.state_nxt;
arbiter.a_req <= (arbiter.a_req or a_req_i.stb) and (not arbiter.state(0)); -- clear STB buffer in BUSY_A
arbiter.b_req <= (arbiter.b_req or b_req_i.stb) and (not arbiter.state(1)); -- clear STB buffer in BUSY_B
state <= state_nxt;
if (state = S_BUSY_A) then -- clear request
a_req <= '0';
else -- buffer request
a_req <= a_req or a_req_i.stb;
end if;
if (state = S_BUSY_B) then -- clear request
b_req <= '0';
else -- buffer request
b_req <= b_req or b_req_i.stb;
end if;
end if;
end process arbiter_sync;
-- fsm --
arbiter_comb: process(arbiter, a_lock_i, a_req_i, b_req_i, x_rsp_i)
begin
-- defaults --
arbiter.state_nxt <= arbiter.state;
arbiter.sel <= '0';
arbiter.stb <= '0';
-- state machine --
case arbiter.state is
-- Prioritizing Bus Switch ----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
arbiter_prioritized:
if not ROUND_ROBIN_EN generate
arbiter_fsm: process(state, a_req, b_req, a_lock_i, a_req_i, b_req_i, x_rsp_i)
begin
-- defaults --
state_nxt <= state;
sel <= '0';
stb <= '0';
when BUSY_A => -- port A access in progress
-- ------------------------------------------------------------
arbiter.sel <= '0';
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
arbiter.state_nxt <= IDLE;
end if;
-- state machine --
case state is
when BUSY_B => -- port B access in progress
-- ------------------------------------------------------------
arbiter.sel <= '1';
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
arbiter.state_nxt <= IDLE;
end if;
when S_BUSY_A => -- port A access in progress
-- ------------------------------------------------------------
sel <= '0';
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
state_nxt <= S_CHECK_A;
end if;
when others => -- IDLE: wait for requests
-- ------------------------------------------------------------
if (a_req_i.stb = '1') or (arbiter.a_req = '1') then -- request from port A (prioritized)?
arbiter.sel <= '0';
arbiter.stb <= '1';
arbiter.state_nxt <= BUSY_A;
elsif ((b_req_i.stb = '1') or (arbiter.b_req = '1')) and (a_lock_i = '0') then -- request from port B?
arbiter.sel <= '1';
arbiter.stb <= '1';
arbiter.state_nxt <= BUSY_B;
end if;
when S_BUSY_B => -- port B access in progress
-- ------------------------------------------------------------
sel <= '1';
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
state_nxt <= S_CHECK_A;
end if;
end case;
end process arbiter_comb;
when others => -- wait for requests
-- ------------------------------------------------------------
if (a_req_i.stb = '1') or (a_req = '1') then -- request from port A (prioritized)?
sel <= '0';
stb <= '1';
state_nxt <= S_BUSY_A;
elsif ((b_req_i.stb = '1') or (b_req = '1')) and (a_lock_i = '0') then -- request from port B?
sel <= '1';
stb <= '1';
state_nxt <= S_BUSY_B;
end if;
end case;
end process arbiter_fsm;
end generate;
-- Round-Robin Arbiter --------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
arbiter_round_robin:
if ROUND_ROBIN_EN generate
arbiter_fsm: process(state, a_req, b_req, a_req_i, b_req_i, x_rsp_i)
begin
-- defaults --
state_nxt <= state;
sel <= '0';
stb <= '0';
-- state machine --
case state is
when S_CHECK_A => -- check if access from port A
-- ------------------------------------------------------------
sel <= '0';
if (a_req_i.stb = '1') or (a_req = '1') then
stb <= '1';
state_nxt <= S_BUSY_A;
else
state_nxt <= S_CHECK_B;
end if;
when S_BUSY_A => -- port B access in progress
-- ------------------------------------------------------------
sel <= '0';
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
state_nxt <= S_CHECK_B;
end if;
when S_CHECK_B => -- check if access from port B
-- ------------------------------------------------------------
sel <= '1';
if (b_req_i.stb = '1') or (b_req = '1') then
stb <= '1';
state_nxt <= S_BUSY_B;
else
state_nxt <= S_CHECK_A;
end if;
when S_BUSY_B => -- port B access in progress
-- ------------------------------------------------------------
sel <= '1';
if (x_rsp_i.err = '1') or (x_rsp_i.ack = '1') then
state_nxt <= S_CHECK_A;
end if;
when others => -- undefined
-- ------------------------------------------------------------
state_nxt <= S_CHECK_A;
end case;
end process arbiter_fsm;
end generate;
-- Request Switch -------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
x_req_o.addr <= a_req_i.addr when (arbiter.sel = '0') else b_req_i.addr;
x_req_o.rvso <= a_req_i.rvso when (arbiter.sel = '0') else b_req_i.rvso;
x_req_o.priv <= a_req_i.priv when (arbiter.sel = '0') else b_req_i.priv;
x_req_o.src <= a_req_i.src when (arbiter.sel = '0') else b_req_i.src;
x_req_o.rw <= a_req_i.rw when (arbiter.sel = '0') else b_req_i.rw;
x_req_o.fence <= a_req_i.fence or b_req_i.fence; -- propagate any fence operations
x_req_o.addr <= a_req_i.addr when (sel = '0') else b_req_i.addr;
x_req_o.rvso <= a_req_i.rvso when (sel = '0') else b_req_i.rvso;
x_req_o.priv <= a_req_i.priv when (sel = '0') else b_req_i.priv;
x_req_o.src <= a_req_i.src when (sel = '0') else b_req_i.src;
x_req_o.rw <= a_req_i.rw when (sel = '0') else b_req_i.rw;
x_req_o.fence <= a_req_i.fence or b_req_i.fence; -- propagate any fence request
x_req_o.sleep <= a_req_i.sleep and b_req_i.sleep; -- set if ALL upstream devices are in sleep mode
x_req_o.debug <= a_req_i.debug when (sel = '0') else b_req_i.debug;
x_req_o.data <= b_req_i.data when PORT_A_READ_ONLY else
a_req_i.data when PORT_B_READ_ONLY else
a_req_i.data when (arbiter.sel = '0') else b_req_i.data;
x_req_o.data <= b_req_i.data when PORT_A_READ_ONLY else
a_req_i.data when PORT_B_READ_ONLY else
a_req_i.data when (sel = '0') else b_req_i.data;
x_req_o.ben <= b_req_i.ben when PORT_A_READ_ONLY else
a_req_i.ben when PORT_B_READ_ONLY else
a_req_i.ben when (arbiter.sel = '0') else b_req_i.ben;
x_req_o.ben <= b_req_i.ben when PORT_A_READ_ONLY else
a_req_i.ben when PORT_B_READ_ONLY else
a_req_i.ben when (sel = '0') else b_req_i.ben;
x_req_o.stb <= arbiter.stb;
x_req_o.stb <= stb;
-- Response Switch ------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
a_rsp_o.data <= x_rsp_i.data;
a_rsp_o.ack <= x_rsp_i.ack when (arbiter.sel = '0') else '0';
a_rsp_o.err <= x_rsp_i.err when (arbiter.sel = '0') else '0';
a_rsp_o.ack <= x_rsp_i.ack when (sel = '0') else '0';
a_rsp_o.err <= x_rsp_i.err when (sel = '0') else '0';
b_rsp_o.data <= x_rsp_i.data;
b_rsp_o.ack <= x_rsp_i.ack when (arbiter.sel = '1') else '0';
b_rsp_o.err <= x_rsp_i.err when (arbiter.sel = '1') else '0';
b_rsp_o.ack <= x_rsp_i.ack when (sel = '1') else '0';
b_rsp_o.err <= x_rsp_i.err when (sel = '1') else '0';
end neorv32_bus_switch_rtl;
-- ================================================================================ --
-- NEORV32 SoC - Processor Bus Infrastructure: Bus Register Stage --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
library ieee;
use ieee.std_logic_1164.all;
library neorv32;
use neorv32.neorv32_package.all;
entity neorv32_bus_reg is
generic (
REQ_REG_EN : boolean := false; -- enable request bus register stage
RSP_REG_EN : boolean := false -- enable response bus register stage
);
port (
-- global control --
clk_i : in std_ulogic; -- global clock, rising edge
rstn_i : in std_ulogic; -- global reset, low-active, async
-- bus ports --
host_req_i : in bus_req_t; -- host request
host_rsp_o : out bus_rsp_t; -- host response
device_req_o : out bus_req_t; -- device request
device_rsp_i : in bus_rsp_t -- device response
);
end neorv32_bus_reg;
architecture neorv32_bus_reg_rtl of neorv32_bus_reg is
begin
-- Request Register -----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
request_reg_enabled:
if REQ_REG_EN generate
request_reg: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
device_req_o <= req_terminate_c;
elsif rising_edge(clk_i) then
if (host_req_i.stb = '1') then -- reduce switching activity on device bus system
device_req_o <= host_req_i;
end if;
device_req_o.stb <= host_req_i.stb;
end if;
end process request_reg;
end generate;
request_reg_disabled:
if not REQ_REG_EN generate
device_req_o <= host_req_i;
end generate;
-- Response Register ----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
response_reg_enabled:
if RSP_REG_EN generate
response_reg: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
host_rsp_o <= rsp_terminate_c;
elsif rising_edge(clk_i) then
host_rsp_o <= device_rsp_i;
end if;
end process response_reg;
end generate;
response_reg_disabled:
if not RSP_REG_EN generate
host_rsp_o <= device_rsp_i;
end generate;
end neorv32_bus_reg_rtl;
-- ================================================================================ --
-- NEORV32 SoC - Processor Bus Infrastructure: Section Gateway --
-- -------------------------------------------------------------------------------- --
-- Bus gateway to distribute accesses to 5 non-overlapping address sub-spaces --
-- (A to E). Note that the sub-spaces have to be aligned to their individual sizes. --
-- Bus gateway to distribute accesses to 4 non-overlapping address sub-spaces --
-- (A to D). Note that the sub-spaces have to be aligned to their individual sizes. --
-- All accesses that do not match any of these sections are redirected to the "X" --
-- port. The gateway-internal bus monitor ensures that all accesses are completed --
-- within a bound time window (if port's *_TMO_EN is true). Otherwise, a bus error --
@ -174,35 +320,24 @@ entity neorv32_bus_gateway is
A_BASE : std_ulogic_vector(31 downto 0); -- port address space base address
A_SIZE : natural; -- port address space size in bytes (power of two), aligned to size
A_TMO_EN : boolean; -- port access timeout enable
A_PRIV : boolean; -- privileged (M-mode) access only
-- port B --
B_ENABLE : boolean;
B_BASE : std_ulogic_vector(31 downto 0);
B_SIZE : natural;
B_TMO_EN : boolean;
B_PRIV : boolean;
-- port C --
C_ENABLE : boolean;
C_BASE : std_ulogic_vector(31 downto 0);
C_SIZE : natural;
C_TMO_EN : boolean;
C_PRIV : boolean;
-- port D --
D_ENABLE : boolean;
D_BASE : std_ulogic_vector(31 downto 0);
D_SIZE : natural;
D_TMO_EN : boolean;
D_PRIV : boolean;
-- port E --
E_ENABLE : boolean;
E_BASE : std_ulogic_vector(31 downto 0);
E_SIZE : natural;
E_TMO_EN : boolean;
E_PRIV : boolean;
-- port X (the void) --
X_ENABLE : boolean;
X_TMO_EN : boolean;
X_PRIV : boolean
X_TMO_EN : boolean
);
port (
-- global control --
@ -220,8 +355,6 @@ entity neorv32_bus_gateway is
c_rsp_i : in bus_rsp_t;
d_req_o : out bus_req_t;
d_rsp_i : in bus_rsp_t;
e_req_o : out bus_req_t;
e_rsp_i : in bus_rsp_t;
x_req_o : out bus_req_t;
x_rsp_i : in bus_rsp_t
);
@ -230,22 +363,20 @@ end neorv32_bus_gateway;
architecture neorv32_bus_gateway_rtl of neorv32_bus_gateway is
-- port select --
signal port_sel : std_ulogic_vector(5 downto 0);
signal port_sel : std_ulogic_vector(4 downto 0);
-- port enable and privileged access lists --
type port_bool_list_t is array (0 to 5) of boolean;
constant port_en_list_c : port_bool_list_t := (A_ENABLE, B_ENABLE, C_ENABLE, D_ENABLE, E_ENABLE, X_ENABLE);
constant priv_acc_list_c : port_bool_list_t := (A_PRIV, B_PRIV, C_PRIV, D_PRIV, E_PRIV, X_PRIV);
-- port enable list --
type port_bool_list_t is array (0 to 4) of boolean;
constant port_en_list_c : port_bool_list_t := (A_ENABLE, B_ENABLE, C_ENABLE, D_ENABLE, X_ENABLE);
-- port timeout enable list --
constant tmo_en_list_c : std_ulogic_vector(5 downto 0) := (
bool_to_ulogic_f(X_TMO_EN), bool_to_ulogic_f(E_TMO_EN), bool_to_ulogic_f(D_TMO_EN),
bool_to_ulogic_f(C_TMO_EN), bool_to_ulogic_f(B_TMO_EN), bool_to_ulogic_f(A_TMO_EN)
constant tmo_en_list_c : std_ulogic_vector(4 downto 0) := (
bool_to_ulogic_f(X_TMO_EN), bool_to_ulogic_f(D_TMO_EN), bool_to_ulogic_f(C_TMO_EN), bool_to_ulogic_f(B_TMO_EN), bool_to_ulogic_f(A_TMO_EN)
);
-- gateway ports combined as arrays --
type port_req_t is array (0 to 5) of bus_req_t;
type port_rsp_t is array (0 to 5) of bus_rsp_t;
type port_req_t is array (0 to 4) of bus_req_t;
type port_rsp_t is array (0 to 4) of bus_rsp_t;
signal port_req : port_req_t;
signal port_rsp : port_rsp_t;
@ -269,10 +400,9 @@ begin
port_sel(1) <= '1' when B_ENABLE and (req_i.addr(31 downto index_size_f(B_SIZE)) = B_BASE(31 downto index_size_f(B_SIZE))) else '0';
port_sel(2) <= '1' when C_ENABLE and (req_i.addr(31 downto index_size_f(C_SIZE)) = C_BASE(31 downto index_size_f(C_SIZE))) else '0';
port_sel(3) <= '1' when D_ENABLE and (req_i.addr(31 downto index_size_f(D_SIZE)) = D_BASE(31 downto index_size_f(D_SIZE))) else '0';
port_sel(4) <= '1' when E_ENABLE and (req_i.addr(31 downto index_size_f(E_SIZE)) = E_BASE(31 downto index_size_f(E_SIZE))) else '0';
-- accesses to the "void" are redirected to the X port --
port_sel(5) <= '1' when X_ENABLE and (port_sel(4 downto 0) = "00000") else '0';
port_sel(4) <= '1' when X_ENABLE and (port_sel(3 downto 0) = "0000") else '0';
-- Gateway Ports --------------------------------------------------------------------------
@ -281,21 +411,16 @@ begin
b_req_o <= port_req(1); port_rsp(1) <= b_rsp_i;
c_req_o <= port_req(2); port_rsp(2) <= c_rsp_i;
d_req_o <= port_req(3); port_rsp(3) <= d_rsp_i;
e_req_o <= port_req(4); port_rsp(4) <= e_rsp_i;
x_req_o <= port_req(5); port_rsp(5) <= x_rsp_i;
x_req_o <= port_req(4); port_rsp(4) <= x_rsp_i;
-- bus request --
request: process(req_i, port_sel)
begin
for i in 0 to 5 loop
for i in 0 to 4 loop
port_req(i) <= req_terminate_c;
if port_en_list_c(i) then -- port enabled
port_req(i) <= req_i;
if priv_acc_list_c(i) then -- privileged-access only
port_req(i).stb <= port_sel(i) and req_i.stb and req_i.priv;
else
port_req(i).stb <= port_sel(i) and req_i.stb;
end if;
port_req(i).stb <= port_sel(i) and req_i.stb;
end if;
end loop;
end process request;
@ -305,7 +430,7 @@ begin
variable tmp_v : bus_rsp_t;
begin
tmp_v := rsp_terminate_c; -- start with all-zero
for i in 0 to 5 loop -- OR all response signals
for i in 0 to 4 loop -- OR all response signals
if port_en_list_c(i) then -- port enabled
tmp_v.data := tmp_v.data or port_rsp(i).data;
tmp_v.ack := tmp_v.ack or port_rsp(i).ack;
@ -374,7 +499,9 @@ use neorv32.neorv32_package.all;
entity neorv32_bus_io_switch is
generic (
DEV_SIZE : natural; -- size of each single IO device, has to be a power of two
INREG_EN : boolean := false; -- enable main_req_i register stage
OUTREG_EN : boolean := false; -- enable main_rsp_o register stage
DEV_SIZE : natural := 256; -- size of each single IO device, has to be a power of two
-- device port enable and base address; enabled ports do not have to be contiguous --
DEV_00_EN : boolean := false; DEV_00_BASE : std_ulogic_vector(31 downto 0) := (others => '0');
DEV_01_EN : boolean := false; DEV_01_BASE : std_ulogic_vector(31 downto 0) := (others => '0');
@ -454,6 +581,24 @@ end neorv32_bus_io_switch;
architecture neorv32_bus_io_switch_rtl of neorv32_bus_io_switch is
-- bus register --
component neorv32_bus_reg
generic (
REQ_REG_EN : boolean := false;
RSP_REG_EN : boolean := false
);
port (
-- global control --
clk_i : in std_ulogic;
rstn_i : in std_ulogic;
-- bus ports --
host_req_i : in bus_req_t;
host_rsp_o : out bus_rsp_t;
device_req_o : out bus_req_t;
device_rsp_i : in bus_rsp_t
);
end component;
-- module configuration --
constant num_devs_c : natural := 32; -- number of device ports
@ -485,11 +630,31 @@ architecture neorv32_bus_io_switch_rtl of neorv32_bus_io_switch is
signal dev_req : dev_req_t;
signal dev_rsp : dev_rsp_t;
-- (partial) register stage --
-- register stages --
signal main_req : bus_req_t;
signal main_rsp : bus_rsp_t;
begin
-- Register Stages ------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_bus_reg_inst: neorv32_bus_reg
generic map (
REQ_REG_EN => INREG_EN,
RSP_REG_EN => OUTREG_EN
)
port map (
-- global control --
clk_i => clk_i,
rstn_i => rstn_i,
-- bus ports --
host_req_i => main_req_i,
host_rsp_o => main_rsp_o,
device_req_o => main_req,
device_rsp_i => main_rsp
);
-- Combine Device Ports -------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
dev_00_req_o <= dev_req(0); dev_rsp(0) <= dev_00_rsp_i;
@ -526,31 +691,6 @@ begin
dev_31_req_o <= dev_req(31); dev_rsp(31) <= dev_31_rsp_i;
-- Input Buffer ---------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
request_reg: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
main_req.addr <= (others => '0');
main_req.stb <= '0';
elsif rising_edge(clk_i) then
if (main_req_i.stb = '1') then -- reduce switching activity on IO bus system
main_req.addr <= main_req_i.addr;
end if;
main_req.stb <= main_req_i.stb;
end if;
end process request_reg;
-- no need to register these signals; they are stable for the entire transfer and do not impact the critical path --
main_req.data <= main_req_i.data;
main_req.ben <= main_req_i.ben;
main_req.rw <= main_req_i.rw;
main_req.src <= main_req_i.src;
main_req.priv <= main_req_i.priv;
main_req.rvso <= main_req_i.rvso;
main_req.fence <= main_req_i.fence;
-- Request --------------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
bus_request_gen:
@ -590,7 +730,7 @@ begin
tmp_v.err := tmp_v.err or dev_rsp(i).err;
end if;
end loop;
main_rsp_o <= tmp_v;
main_rsp <= tmp_v;
end process;
@ -626,10 +766,10 @@ entity neorv32_bus_reservation_set is
rvs_addr_o : out std_ulogic_vector(31 downto 0);
rvs_valid_o : out std_ulogic;
rvs_clear_i : in std_ulogic;
-- core/cpu port --
-- core port --
core_req_i : in bus_req_t;
core_rsp_o : out bus_rsp_t;
-- system ports --
-- system port --
sys_req_o : out bus_req_t;
sys_rsp_i : in bus_rsp_t
);

View file

@ -40,8 +40,8 @@ use neorv32.neorv32_package.all;
entity neorv32_cache is
generic (
NUM_BLOCKS : natural range 2 to 4096; -- number of cache blocks (min 2), has to be a power of 2
BLOCK_SIZE : natural range 4 to 4096; -- cache block size in bytes (min 4), has to be a power of 2
NUM_BLOCKS : natural range 2 to 1024; -- number of cache blocks (min 2), has to be a power of 2
BLOCK_SIZE : natural range 4 to 32768; -- cache block size in bytes (min 4), has to be a power of 2
UC_BEGIN : std_ulogic_vector(3 downto 0); -- begin of uncached address space (page number / 4 MSBs of address)
UC_ENABLE : boolean; -- enable direct/uncached accesses
READ_ONLY : boolean -- read-only accesses for host
@ -183,7 +183,7 @@ begin
-- request splitter: cached or direct access --
req_splitter: process(host_req_i, dir_acc_d)
begin
-- default: pass-through of all bus signals --
-- default: pass-through all bus signals --
cache_req <= host_req_i;
dir_req_d <= host_req_i;
-- direct access --
@ -826,7 +826,7 @@ begin
-- Control Engine FSM Comb ----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
ctrl_engine_comb: process(state, upret, addr, haddr, baddr, bus_rsp_i, cmd_sync_i, cmd_miss_i, rdata_i, dirty_i)
ctrl_engine_comb: process(state, upret, addr, haddr, baddr, host_req_i, bus_rsp_i, cmd_sync_i, cmd_miss_i, rdata_i, dirty_i)
begin
-- control engine defaults --
state_nxt <= state;
@ -845,13 +845,19 @@ begin
new_o <= '0';
-- bus interface defaults --
bus_req_o <= req_terminate_c; -- all-zero
bus_req_o.addr <= addr.tag & addr.idx & addr.ofs & "00"; -- always word-aligned
bus_req_o.data <= rdata_i;
bus_req_o.ben <= (others => '1'); -- full-word writes only
bus_req_o.src <= '0'; -- cache accesses are always "data" accesses
bus_req_o.priv <= '0'; -- cache accesses are always "unprivileged" accesses
bus_req_o.rvso <= '0'; -- cache accesses can never be a reservation set operation
bus_req_o <= req_terminate_c; -- all-zero
bus_req_o.addr <= addr.tag & addr.idx & addr.ofs & "00"; -- always word-aligned
bus_req_o.data <= rdata_i;
bus_req_o.ben <= (others => '1'); -- full-word writes only
bus_req_o.src <= '0'; -- cache accesses are always data accesses
bus_req_o.priv <= '0'; -- cache accesses are always "unprivileged" accesses
bus_req_o.rvso <= '0'; -- cache accesses can never be a reservation set operation
bus_req_o.debug <= host_req_i.debug;
if (state = S_IDLE) then
bus_req_o.sleep <= host_req_i.sleep;
else
bus_req_o.sleep <= '0';
end if;
-- fsm --
case state is

454
rtl/core/neorv32_clint.vhd Normal file
View file

@ -0,0 +1,454 @@
-- ================================================================================ --
-- NEORV32 SoC - RISC-V Core Local Interruptor (CLINT) --
-- -------------------------------------------------------------------------------- --
-- Compatible to the RISC-V & SiFive CLINT specification. Supports machine software --
-- interrupts and machine timer interrupts for up to 4095 harts. --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library neorv32;
use neorv32.neorv32_package.all;
entity neorv32_clint is
generic (
NUM_HARTS : natural range 1 to 4095 -- number of physical CPU cores
);
port (
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active, async
bus_req_i : in bus_req_t; -- bus request
bus_rsp_o : out bus_rsp_t; -- bus response
time_o : out std_ulogic_vector(63 downto 0); -- current system time
mti_o : out std_ulogic_vector(NUM_HARTS-1 downto 0); -- machine timer interrupt
msi_o : out std_ulogic_vector(NUM_HARTS-1 downto 0) -- machine software interrupt
);
end neorv32_clint;
architecture neorv32_clint_rtl of neorv32_clint is
-- global machine timer --
component neorv32_clint_mtime
port (
clk_i : in std_ulogic;
rstn_i : in std_ulogic;
en_i : in std_ulogic;
rw_i : in std_ulogic;
addr_i : in std_ulogic;
wdata_i : in std_ulogic_vector(31 downto 0);
rdata_o : out std_ulogic_vector(31 downto 0);
mtime_o : out std_ulogic_vector(63 downto 0)
);
end component;
-- time interrupt generator --
component neorv32_clint_mtimecmp
port (
clk_i : in std_ulogic;
rstn_i : in std_ulogic;
en_i : in std_ulogic;
rw_i : in std_ulogic;
addr_i : in std_ulogic;
wdata_i : in std_ulogic_vector(31 downto 0);
rdata_o : out std_ulogic_vector(31 downto 0);
mtime_i : in std_ulogic_vector(63 downto 0);
mti_o : out std_ulogic
);
end component;
-- software interrupt trigger --
component neorv32_clint_swi
port (
clk_i : in std_ulogic;
rstn_i : in std_ulogic;
en_i : in std_ulogic;
rw_i : in std_ulogic;
wdata_i : in std_ulogic_vector(31 downto 0);
rdata_o : out std_ulogic_vector(31 downto 0);
swi_o : out std_ulogic
);
end component;
-- device offsets --
constant clic_mswi_base_c : unsigned(15 downto 0) := x"0000";
constant clic_mtimecmp_base_c : unsigned(15 downto 0) := x"4000";
constant clic_mtime_base_c : unsigned(15 downto 0) := x"bff8";
-- device access --
signal mtime_en : std_ulogic;
signal mtimecmp_en : std_ulogic_vector(NUM_HARTS-1 downto 0);
signal mswi_en : std_ulogic_vector(NUM_HARTS-1 downto 0);
-- read-back --
type rb32_t is array (0 to NUM_HARTS-1) of std_ulogic_vector(31 downto 0);
signal mtime_rd : std_ulogic_vector(31 downto 0);
signal mtimecmp_rd : rb32_t;
signal mswi_rd : rb32_t;
-- misc --
signal mtime : std_ulogic_vector(63 downto 0);
signal ack_q : std_ulogic;
signal rdata : std_ulogic_vector(31 downto 0);
begin
-- MTIME - Machine Timer ------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_clint_mtime_inst: neorv32_clint_mtime
port map (
clk_i => clk_i,
rstn_i => rstn_i,
en_i => mtime_en,
rw_i => bus_req_i.rw,
addr_i => bus_req_i.addr(2),
wdata_i => bus_req_i.data,
rdata_o => mtime_rd,
mtime_o => mtime
);
-- device access --
mtime_en <= '1' when (bus_req_i.stb = '1') and (unsigned(bus_req_i.addr(15 downto 3)) = clic_mtime_base_c(15 downto 3)) else '0';
-- system time output: synchronize low and high words --
time_output: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
time_o(31 downto 0) <= (others => '0');
elsif rising_edge(clk_i) then
time_o(31 downto 0) <= mtime(31 downto 0);
end if;
end process time_output;
time_o(63 downto 32) <= mtime(63 downto 32);
-- MTIMECMP - Per-Hart Time Comparator / Interrupt Generator ------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_clint_mtimecmp_gen:
for i in 0 to NUM_HARTS-1 generate
neorv32_clint_mtimecmp_inst: neorv32_clint_mtimecmp
port map (
clk_i => clk_i,
rstn_i => rstn_i,
en_i => mtimecmp_en(i),
rw_i => bus_req_i.rw,
addr_i => bus_req_i.addr(2),
wdata_i => bus_req_i.data,
rdata_o => mtimecmp_rd(i),
mtime_i => mtime,
mti_o => mti_o(i)
);
-- device access --
mtimecmp_en(i) <= '1' when (bus_req_i.stb = '1') and (unsigned(bus_req_i.addr(15 downto 3)) = (clic_mtimecmp_base_c(15 downto 3) + i)) else '0';
end generate;
-- MSWI - Per-Hart Machine Software Interrupt Trigger -------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_clint_swi_gen:
for i in 0 to NUM_HARTS-1 generate
neorv32_clint_swi_inst: neorv32_clint_swi
port map (
clk_i => clk_i,
rstn_i => rstn_i,
en_i => mswi_en(i),
rw_i => bus_req_i.rw,
wdata_i => bus_req_i.data,
rdata_o => mswi_rd(i),
swi_o => msi_o(i)
);
-- device access --
mswi_en(i) <= '1' when (bus_req_i.stb = '1') and (unsigned(bus_req_i.addr(15 downto 2)) = (clic_mswi_base_c(15 downto 2) + i)) else '0';
end generate;
-- Data Read-Back -------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
read_back: process(mtime_rd, mtimecmp_rd, mswi_rd)
variable tmp_v : std_ulogic_vector(31 downto 0);
begin
tmp_v := (others => '0');
-- mtime --
tmp_v := tmp_v or mtime_rd;
-- mtimecmp --
for i in 0 to NUM_HARTS-1 loop
tmp_v := tmp_v or mtimecmp_rd(i);
end loop;
-- mswi --
for i in 0 to NUM_HARTS-1 loop
tmp_v := tmp_v or mswi_rd(i);
end loop;
-- output --
rdata <= tmp_v;
end process read_back;
-- Bus Handshake --------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
bus_access: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
ack_q <= '0';
elsif rising_edge(clk_i) then
ack_q <= bus_req_i.stb;
end if;
end process bus_access;
bus_rsp_o.ack <= ack_q;
bus_rsp_o.err <= '0';
bus_rsp_o.data <= rdata;
end neorv32_clint_rtl;
-- ================================================================================ --
-- NEORV32 SoC - CLINT MTIME (global machine timer) --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library neorv32;
use neorv32.neorv32_package.all;
entity neorv32_clint_mtime is
port (
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active, async
en_i : in std_ulogic; -- access-enable
rw_i : in std_ulogic; -- read (0) / write (1)
addr_i : in std_ulogic; -- low/high word select
wdata_i : in std_ulogic_vector(31 downto 0); -- write data
rdata_o : out std_ulogic_vector(31 downto 0); -- read data
mtime_o : out std_ulogic_vector(63 downto 0) -- global mtime.time (async words!)
);
end neorv32_clint_mtime;
architecture neorv32_clint_mtime_rtl of neorv32_clint_mtime is
signal re_q : std_ulogic_vector(1 downto 0);
signal we_q : std_ulogic_vector(1 downto 0);
signal mtime_lo_q : std_ulogic_vector(31 downto 0);
signal mtime_hi_q : std_ulogic_vector(31 downto 0);
signal carry_q : std_ulogic_vector(0 downto 0);
signal inc_lo : std_ulogic_vector(32 downto 0);
signal inc_hi : std_ulogic_vector(32 downto 0);
begin
-- 64-Bit Timer Core (split into two 32-bit registers) ------------------------------------
-- -------------------------------------------------------------------------------------------
mtime_core: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
we_q <= (others => '0');
re_q <= (others => '0');
mtime_lo_q <= (others => '0');
carry_q <= (others => '0');
mtime_hi_q <= (others => '0');
elsif rising_edge(clk_i) then
we_q(0) <= en_i and rw_i and (not addr_i);
we_q(1) <= en_i and rw_i and ( addr_i);
re_q(0) <= en_i and (not addr_i);
re_q(1) <= en_i and ( addr_i);
-- low-word --
if (we_q(0) = '1') then
mtime_lo_q <= wdata_i;
else
mtime_lo_q <= inc_lo(31 downto 0);
end if;
carry_q(0) <= inc_lo(32); -- low-to-high carry
-- high-word --
if (we_q(1) = '1') then
mtime_hi_q <= wdata_i;
else
mtime_hi_q <= inc_hi(31 downto 0);
end if;
end if;
end process mtime_core;
-- increments --
inc_lo <= std_ulogic_vector(unsigned('0' & mtime_lo_q) + 1);
inc_hi <= std_ulogic_vector(unsigned('0' & mtime_hi_q) + unsigned(carry_q));
-- global time output; low and high words are off-sync by one cycle! --
mtime_o <= mtime_hi_q & mtime_lo_q;
-- read access --
rdata_o <= mtime_hi_q when (re_q(1) = '1') else
mtime_lo_q when (re_q(0) = '1') else
(others => '0');
end neorv32_clint_mtime_rtl;
-- ================================================================================ --
-- NEORV32 SoC - CLINT MTIMECMP (per-hart time comparator) --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library neorv32;
use neorv32.neorv32_package.all;
entity neorv32_clint_mtimecmp is
port (
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active, async
en_i : in std_ulogic; -- access-enable
rw_i : in std_ulogic; -- read (0) / write (1)
addr_i : in std_ulogic; -- low/high word select
wdata_i : in std_ulogic_vector(31 downto 0); -- write data
rdata_o : out std_ulogic_vector(31 downto 0); -- read data
mtime_i : in std_ulogic_vector(63 downto 0); -- global mtime.time (async words!)
mti_o : out std_ulogic -- interrupt
);
end neorv32_clint_mtimecmp;
architecture neorv32_clint_mtimecmp_rtl of neorv32_clint_mtimecmp is
signal rden_q : std_ulogic_vector(1 downto 0);
signal mtimecmp_q : std_ulogic_vector(63 downto 0);
signal cmp_lo_eq, cmp_lo_gt, cmp_lo_ge, cmp_hi_eq, cmp_hi_gt : std_ulogic;
begin
-- MTIMECMP Access ------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
write_access: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
rden_q <= (others => '0');
mtimecmp_q <= (others => '0');
elsif rising_edge(clk_i) then
rden_q(0) <= en_i and (not addr_i);
rden_q(1) <= en_i and ( addr_i);
if (en_i = '1') and (rw_i = '1') then
if (addr_i = '0') then
mtimecmp_q(31 downto 0) <= wdata_i;
else
mtimecmp_q(63 downto 32) <= wdata_i;
end if;
end if;
end if;
end process write_access;
-- read access --
rdata_o <= mtimecmp_q(63 downto 32) when (rden_q(1) = '1') else
mtimecmp_q(31 downto 00) when (rden_q(0) = '1') else
(others => '0');
-- Interrupt Generator (comparator is split across two cycles) ----------------------------
-- -------------------------------------------------------------------------------------------
irq_gen: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
cmp_lo_ge <= '0';
mti_o <= '0';
elsif rising_edge(clk_i) then
cmp_lo_ge <= cmp_lo_gt or cmp_lo_eq; -- low word greater-than or equal
mti_o <= cmp_hi_gt or (cmp_hi_eq and cmp_lo_ge);
end if;
end process irq_gen;
-- sub-word comparators; there is one cycle delay between low (earlier) and high (later) word --
cmp_lo_eq <= '1' when (unsigned(mtime_i(31 downto 0)) = unsigned(mtimecmp_q(31 downto 0))) else '0';
cmp_lo_gt <= '1' when (unsigned(mtime_i(31 downto 0)) > unsigned(mtimecmp_q(31 downto 0))) else '0';
cmp_hi_eq <= '1' when (unsigned(mtime_i(63 downto 32)) = unsigned(mtimecmp_q(63 downto 32))) else '0';
cmp_hi_gt <= '1' when (unsigned(mtime_i(63 downto 32)) > unsigned(mtimecmp_q(63 downto 32))) else '0';
end neorv32_clint_mtimecmp_rtl;
-- ================================================================================ --
-- NEORV32 SoC - CLINT SWI (software interrupt trigger) --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
library ieee;
use ieee.std_logic_1164.all;
library neorv32;
use neorv32.neorv32_package.all;
entity neorv32_clint_swi is
port (
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active, async
en_i : in std_ulogic; -- access-enable
rw_i : in std_ulogic; -- read (0) / write (1)
wdata_i : in std_ulogic_vector(31 downto 0); -- write data
rdata_o : out std_ulogic_vector(31 downto 0); -- read data
swi_o : out std_ulogic -- interrupt
);
end neorv32_clint_swi;
architecture neorv32_clint_swi_rtl of neorv32_clint_swi is
signal rden_q, sip_q : std_ulogic;
begin
-- MSI Access -----------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
write_access: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
rden_q <= '0';
sip_q <= '0';
elsif rising_edge(clk_i) then
rden_q <= en_i;
if (en_i = '1') and (rw_i = '1') then
sip_q <= wdata_i(0);
end if;
end if;
end process write_access;
-- read access --
rdata_o(31 downto 1) <= (others => '0');
rdata_o(0) <= sip_q when (rden_q = '1') else '0';
-- interrupt --
swi_o <= sip_q;
end neorv32_clint_swi_rtl;

View file

@ -22,7 +22,7 @@ use neorv32.neorv32_package.all;
entity neorv32_cpu is
generic (
-- General --
HART_ID : std_ulogic_vector(31 downto 0); -- hardware thread ID
HART_ID : natural; -- hardware thread ID
VENDOR_ID : std_ulogic_vector(31 downto 0); -- vendor's JEDEC ID
BOOT_ADDR : std_ulogic_vector(31 downto 0); -- cpu boot address
DEBUG_PARK_ADDR : std_ulogic_vector(31 downto 0); -- cpu debug mode parking loop entry address
@ -54,9 +54,10 @@ entity neorv32_cpu is
RISCV_ISA_Sdtrig : boolean; -- implement trigger module extension
RISCV_ISA_Smpmp : boolean; -- implement physical memory protection
-- Tuning Options --
FAST_MUL_EN : boolean; -- use DSPs for M extension's multiplier
FAST_SHIFT_EN : boolean; -- use barrel shifter for shift operations
REGFILE_HW_RST : boolean; -- implement full hardware reset for register file
CPU_CLOCK_GATING_EN : boolean; -- enable clock gating when in sleep mode
CPU_FAST_MUL_EN : boolean; -- use DSPs for M extension's multiplier
CPU_FAST_SHIFT_EN : boolean; -- use barrel shifter for shift operations
CPU_RF_HW_RST_EN : boolean; -- implement full hardware reset for register file
-- Physical Memory Protection (PMP) --
PMP_NUM_REGIONS : natural range 0 to 16; -- number of regions (0..16)
PMP_MIN_GRANULARITY : natural; -- minimal region granularity in bytes, has to be a power of 2, min 4 bytes
@ -69,10 +70,7 @@ entity neorv32_cpu is
port (
-- global control --
clk_i : in std_ulogic; -- switchable global clock, rising edge
clk_aux_i : in std_ulogic; -- always-on clock, rising edge
rstn_i : in std_ulogic; -- global reset, low-active, async
sleep_o : out std_ulogic; -- cpu is in sleep mode when set
debug_o : out std_ulogic; -- cpu is in debug mode when set
-- interrupts --
msi_i : in std_ulogic; -- risc-v machine software interrupt
mei_i : in std_ulogic; -- risc-v machine external interrupt
@ -93,7 +91,7 @@ architecture neorv32_cpu_rtl of neorv32_cpu is
-- auto-configuration --
constant rf_rs3_en_c : boolean := RISCV_ISA_Zxcfu or RISCV_ISA_Zfinx; -- 3rd register file read port
constant riscv_b_c : boolean := RISCV_ISA_Zba and RISCV_ISA_Zbb and RISCV_ISA_Zbs; -- B: bit manipulation
constant riscv_zkt_c : boolean := FAST_SHIFT_EN; -- Zkt: data-independent execution time for cryptographic operations
constant riscv_zkt_c : boolean := CPU_FAST_SHIFT_EN; -- Zkt: data-independent execution time for cryptographic operations
constant riscv_zkn_c : boolean := RISCV_ISA_Zbkb and RISCV_ISA_Zbkc and RISCV_ISA_Zbkx and
RISCV_ISA_Zkne and RISCV_ISA_Zknd and RISCV_ISA_Zknh; -- Zkn: NIST suite
constant riscv_zks_c : boolean := RISCV_ISA_Zbkb and RISCV_ISA_Zbkc and RISCV_ISA_Zbkx and
@ -108,6 +106,7 @@ architecture neorv32_cpu_rtl of neorv32_cpu is
signal xcsr_rdata_res : std_ulogic_vector(XLEN-1 downto 0);
-- local signals --
signal clk_gated : std_ulogic; -- switchable clock (clock gating)
signal ctrl : ctrl_bus_t; -- main control bus
signal alu_imm : std_ulogic_vector(XLEN-1 downto 0); -- immediate
signal rf_wdata : std_ulogic_vector(XLEN-1 downto 0); -- register file write data
@ -121,11 +120,10 @@ architecture neorv32_cpu_rtl of neorv32_cpu is
signal csr_rdata : std_ulogic_vector(XLEN-1 downto 0); -- csr read data
signal lsu_mar : std_ulogic_vector(XLEN-1 downto 0); -- lsu memory address register
signal lsu_err : std_ulogic_vector(3 downto 0); -- lsu alignment/access errors
signal pc_fetch : std_ulogic_vector(XLEN-1 downto 0); -- pc for instruction fetch
signal pc_curr : std_ulogic_vector(XLEN-1 downto 0); -- current pc (for currently executed instruction)
signal pc_next : std_ulogic_vector(XLEN-1 downto 0); -- next PC (corresponding to next instruction)
signal pc_ret : std_ulogic_vector(XLEN-1 downto 0); -- return address
signal pmp_ex_fault : std_ulogic; -- pmp instruction fetch fault
signal pmp_rw_fault : std_ulogic; -- pmp read/write access fault
signal pmp_fault : std_ulogic; -- pmp permission violation
signal irq_machine : std_ulogic_vector(2 downto 0); -- risc-v standard machine-level interrupts
begin
@ -170,81 +168,103 @@ begin
-- CPU tuning options --
assert false report "[NEORV32] CPU tuning options: " &
cond_sel_string_f(FAST_MUL_EN, "fast_mul ", "") &
cond_sel_string_f(FAST_SHIFT_EN, "fast_shift ", "") &
cond_sel_string_f(REGFILE_HW_RST, "rf_hw_rst ", "")
cond_sel_string_f(CPU_CLOCK_GATING_EN, "clock_gating ", "") &
cond_sel_string_f(CPU_FAST_MUL_EN, "fast_mul ", "") &
cond_sel_string_f(CPU_FAST_SHIFT_EN, "fast_shift ", "") &
cond_sel_string_f(CPU_RF_HW_RST_EN, "rf_hw_rst ", "")
severity note;
-- simulation notifier --
assert not is_simulation_c report "[NEORV32] Assuming this is a simulation." severity warning;
-- Clock Gating ---------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_cpu_clockgate_enabled:
if CPU_CLOCK_GATING_EN generate
neorv32_cpu_clockgate_inst: entity neorv32.neorv32_clockgate
port map (
clk_i => clk_i,
rstn_i => rstn_i,
halt_i => ctrl.cpu_sleep,
clk_o => clk_gated
);
end generate;
neorv32_cpu_clockgate_disabled:
if not CPU_CLOCK_GATING_EN generate
clk_gated <= clk_i;
end generate;
-- Control Unit ---------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_cpu_control_inst: entity neorv32.neorv32_cpu_control
generic map (
-- General --
HART_ID => HART_ID, -- hardware thread ID
VENDOR_ID => VENDOR_ID, -- vendor's JEDEC ID
BOOT_ADDR => BOOT_ADDR, -- cpu boot address
DEBUG_PARK_ADDR => DEBUG_PARK_ADDR, -- cpu debug mode parking loop entry address
DEBUG_EXC_ADDR => DEBUG_EXC_ADDR, -- cpu debug mode exception entry address
HART_ID => HART_ID, -- hardware thread ID
VENDOR_ID => VENDOR_ID, -- vendor's JEDEC ID
BOOT_ADDR => BOOT_ADDR, -- cpu boot address
DEBUG_PARK_ADDR => DEBUG_PARK_ADDR, -- cpu debug mode parking loop entry address
DEBUG_EXC_ADDR => DEBUG_EXC_ADDR, -- cpu debug mode exception entry address
-- RISC-V ISA Extensions --
RISCV_ISA_B => riscv_b_c, -- implement bit-manipulation extension
RISCV_ISA_C => RISCV_ISA_C, -- implement compressed extension
RISCV_ISA_E => RISCV_ISA_E, -- implement embedded RF extension
RISCV_ISA_M => RISCV_ISA_M, -- implement mul/div extension
RISCV_ISA_U => RISCV_ISA_U, -- implement user mode extension
RISCV_ISA_Zalrsc => RISCV_ISA_Zalrsc, -- implement atomic reservation-set extension
RISCV_ISA_Zba => RISCV_ISA_Zba, -- implement shifted-add bit-manipulation extension
RISCV_ISA_Zbb => RISCV_ISA_Zbb, -- implement basic bit-manipulation extension
RISCV_ISA_Zbkb => RISCV_ISA_Zbkb, -- implement bit-manipulation instructions for cryptography
RISCV_ISA_Zbkc => RISCV_ISA_Zbkc, -- implement carry-less multiplication instructions
RISCV_ISA_Zbkx => RISCV_ISA_Zbkx, -- implement cryptography crossbar permutation extension
RISCV_ISA_Zbs => RISCV_ISA_Zbs, -- implement single-bit bit-manipulation extension
RISCV_ISA_Zfinx => RISCV_ISA_Zfinx, -- implement 32-bit floating-point extension
RISCV_ISA_Zicntr => RISCV_ISA_Zicntr, -- implement base counters
RISCV_ISA_Zicond => RISCV_ISA_Zicond, -- implement integer conditional operations
RISCV_ISA_Zihpm => RISCV_ISA_Zihpm, -- implement hardware performance monitors
RISCV_ISA_Zkn => riscv_zkn_c, -- NIST algorithm suite available
RISCV_ISA_Zknd => RISCV_ISA_Zknd, -- implement cryptography NIST AES decryption extension
RISCV_ISA_Zkne => RISCV_ISA_Zkne, -- implement cryptography NIST AES encryption extension
RISCV_ISA_Zknh => RISCV_ISA_Zknh, -- implement cryptography NIST hash extension
RISCV_ISA_Zks => riscv_zks_c, -- ShangMi algorithm suite available
RISCV_ISA_Zksed => RISCV_ISA_Zksed, -- implement ShangMi block cypher extension
RISCV_ISA_Zksh => RISCV_ISA_Zksh, -- implement ShangMi hash extension
RISCV_ISA_Zkt => riscv_zkt_c, -- data-independent execution time available (for cryptographic operations)
RISCV_ISA_Zmmul => RISCV_ISA_Zmmul, -- implement multiply-only M sub-extension
RISCV_ISA_Zxcfu => RISCV_ISA_Zxcfu, -- implement custom (instr.) functions unit
RISCV_ISA_Sdext => RISCV_ISA_Sdext, -- implement external debug mode extension
RISCV_ISA_Sdtrig => RISCV_ISA_Sdtrig, -- implement trigger module extension
RISCV_ISA_Smpmp => RISCV_ISA_Smpmp, -- implement physical memory protection
RISCV_ISA_B => riscv_b_c, -- implement bit-manipulation extension
RISCV_ISA_C => RISCV_ISA_C, -- implement compressed extension
RISCV_ISA_E => RISCV_ISA_E, -- implement embedded RF extension
RISCV_ISA_M => RISCV_ISA_M, -- implement mul/div extension
RISCV_ISA_U => RISCV_ISA_U, -- implement user mode extension
RISCV_ISA_Zalrsc => RISCV_ISA_Zalrsc, -- implement atomic reservation-set extension
RISCV_ISA_Zba => RISCV_ISA_Zba, -- implement shifted-add bit-manipulation extension
RISCV_ISA_Zbb => RISCV_ISA_Zbb, -- implement basic bit-manipulation extension
RISCV_ISA_Zbkb => RISCV_ISA_Zbkb, -- implement bit-manipulation instructions for cryptography
RISCV_ISA_Zbkc => RISCV_ISA_Zbkc, -- implement carry-less multiplication instructions
RISCV_ISA_Zbkx => RISCV_ISA_Zbkx, -- implement cryptography crossbar permutation extension
RISCV_ISA_Zbs => RISCV_ISA_Zbs, -- implement single-bit bit-manipulation extension
RISCV_ISA_Zfinx => RISCV_ISA_Zfinx, -- implement 32-bit floating-point extension
RISCV_ISA_Zicntr => RISCV_ISA_Zicntr, -- implement base counters
RISCV_ISA_Zicond => RISCV_ISA_Zicond, -- implement integer conditional operations
RISCV_ISA_Zihpm => RISCV_ISA_Zihpm, -- implement hardware performance monitors
RISCV_ISA_Zkn => riscv_zkn_c, -- NIST algorithm suite available
RISCV_ISA_Zknd => RISCV_ISA_Zknd, -- implement cryptography NIST AES decryption extension
RISCV_ISA_Zkne => RISCV_ISA_Zkne, -- implement cryptography NIST AES encryption extension
RISCV_ISA_Zknh => RISCV_ISA_Zknh, -- implement cryptography NIST hash extension
RISCV_ISA_Zks => riscv_zks_c, -- ShangMi algorithm suite available
RISCV_ISA_Zksed => RISCV_ISA_Zksed, -- implement ShangMi block cypher extension
RISCV_ISA_Zksh => RISCV_ISA_Zksh, -- implement ShangMi hash extension
RISCV_ISA_Zkt => riscv_zkt_c, -- data-independent execution time available (for cryptographic operations)
RISCV_ISA_Zmmul => RISCV_ISA_Zmmul, -- implement multiply-only M sub-extension
RISCV_ISA_Zxcfu => RISCV_ISA_Zxcfu, -- implement custom (instr.) functions unit
RISCV_ISA_Sdext => RISCV_ISA_Sdext, -- implement external debug mode extension
RISCV_ISA_Sdtrig => RISCV_ISA_Sdtrig, -- implement trigger module extension
RISCV_ISA_Smpmp => RISCV_ISA_Smpmp, -- implement physical memory protection
-- Tuning Options --
FAST_MUL_EN => FAST_MUL_EN, -- use DSPs for M extension's multiplier
FAST_SHIFT_EN => FAST_SHIFT_EN, -- use barrel shifter for shift operations
REGFILE_HW_RST => REGFILE_HW_RST, -- implement full hardware reset for register file
CPU_CLOCK_GATING_EN => CPU_CLOCK_GATING_EN, -- enable clock gating when in sleep mode
CPU_FAST_MUL_EN => CPU_FAST_MUL_EN, -- use DSPs for M extension's multiplier
CPU_FAST_SHIFT_EN => CPU_FAST_SHIFT_EN, -- use barrel shifter for shift operations
CPU_RF_HW_RST_EN => CPU_RF_HW_RST_EN, -- implement full hardware reset for register file
-- Hardware Performance Monitors (HPM) --
HPM_NUM_CNTS => HPM_NUM_CNTS, -- number of implemented HPM counters (0..13)
HPM_CNT_WIDTH => HPM_CNT_WIDTH -- total size of HPM counters
HPM_NUM_CNTS => HPM_NUM_CNTS, -- number of implemented HPM counters (0..13)
HPM_CNT_WIDTH => HPM_CNT_WIDTH -- total size of HPM counters
)
port map (
-- global control --
clk_i => clk_i, -- global clock, rising edge
clk_aux_i => clk_aux_i, -- always-on clock, rising edge
clk_i => clk_gated, -- global clock, rising edge
clk_aux_i => clk_i, -- always-on clock, rising edge
rstn_i => rstn_i, -- global reset, low-active, async
ctrl_o => ctrl, -- main control bus
-- instruction fetch interface --
ibus_pmperr_i => pmp_ex_fault, -- instruction fetch pmp fault
ibus_req_o => ibus_req_o, -- request
ibus_rsp_i => ibus_rsp_i, -- response
-- pmp fault --
pmp_fault_i => pmp_fault, -- instruction fetch / execute pmp fault
-- data path interface --
alu_cp_done_i => alu_cp_done, -- ALU iterative operation done
alu_cmp_i => alu_cmp, -- comparator status
alu_add_i => alu_add, -- ALU address result
alu_imm_o => alu_imm, -- immediate
rf_rs1_i => rs1, -- rf source 1
pc_fetch_o => pc_fetch, -- instruction fetch address
pc_curr_o => pc_curr, -- current PC (corresponding to current instruction)
pc_next_o => pc_next, -- next PC (corresponding to next instruction)
pc_ret_o => pc_ret, -- return address
csr_rdata_o => csr_rdata, -- CSR read data
-- external CSR interface --
@ -268,29 +288,25 @@ begin
-- external CSR read-back --
xcsr_rdata_res <= xcsr_rdata_pmp or xcsr_rdata_alu;
-- CPU state --
sleep_o <= ctrl.cpu_sleep; -- set when CPU is sleeping (after WFI)
debug_o <= ctrl.cpu_debug; -- set when CPU is in debug mode
-- Register File --------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_cpu_regfile_inst: entity neorv32.neorv32_cpu_regfile
generic map (
RST_EN => REGFILE_HW_RST, -- enable dedicated hardware reset ("ASIC style")
RVE_EN => RISCV_ISA_E, -- implement embedded RF extension
RS3_EN => rf_rs3_en_c -- enable 3rd read port
RST_EN => CPU_RF_HW_RST_EN, -- enable dedicated hardware reset ("ASIC style")
RVE_EN => RISCV_ISA_E, -- implement embedded RF extension
RS3_EN => rf_rs3_en_c -- enable 3rd read port
)
port map (
-- global control --
clk_i => clk_i, -- global clock, rising edge
rstn_i => rstn_i, -- global reset, low-active, async
ctrl_i => ctrl, -- main control bus
clk_i => clk_gated, -- global clock, rising edge
rstn_i => rstn_i, -- global reset, low-active, async
ctrl_i => ctrl, -- main control bus
-- operands --
rd_i => rf_wdata, -- destination operand rd
rs1_o => rs1, -- source operand rs1
rs2_o => rs2, -- source operand rs2
rs3_o => rs3 -- source operand rs3
rd_i => rf_wdata, -- destination operand rd
rs1_o => rs1, -- source operand rs1
rs2_o => rs2, -- source operand rs2
rs3_o => rs3 -- source operand rs3
);
-- all buses are zero unless there is an according operation --
@ -319,12 +335,12 @@ begin
RISCV_ISA_Zmmul => RISCV_ISA_Zmmul, -- implement multiply-only M sub-extension
RISCV_ISA_Zxcfu => RISCV_ISA_Zxcfu, -- implement custom (instr.) functions unit
-- Tuning Options --
FAST_MUL_EN => FAST_MUL_EN, -- use DSPs for M extension's multiplier
FAST_SHIFT_EN => FAST_SHIFT_EN -- use barrel shifter for shift operations
FAST_MUL_EN => CPU_FAST_MUL_EN, -- use DSPs for M extension's multiplier
FAST_SHIFT_EN => CPU_FAST_SHIFT_EN -- use barrel shifter for shift operations
)
port map (
-- global control --
clk_i => clk_i, -- global clock, rising edge
clk_i => clk_gated, -- global clock, rising edge
rstn_i => rstn_i, -- global reset, low-active, async
ctrl_i => ctrl, -- main control bus
-- CSR interface --
@ -355,7 +371,7 @@ begin
)
port map (
-- global control --
clk_i => clk_i, -- global clock, rising edge
clk_i => clk_gated, -- global clock, rising edge
rstn_i => rstn_i, -- global reset, low-active, async
ctrl_i => ctrl, -- main control bus
-- cpu data access interface --
@ -365,7 +381,7 @@ begin
mar_o => lsu_mar, -- memory address register
wait_o => lsu_wait, -- wait for access to complete
err_o => lsu_err, -- alignment/access errors
pmp_fault_i => pmp_rw_fault, -- PMP read/write access fault
pmp_fault_i => pmp_fault, -- PMP read/write access fault
-- data bus --
dbus_req_o => dbus_req_o, -- request
dbus_rsp_i => dbus_rsp_i -- response
@ -385,7 +401,7 @@ begin
)
port map (
-- global control --
clk_i => clk_i, -- global clock, rising edge
clk_i => clk_gated, -- global clock, rising edge
rstn_i => rstn_i, -- global reset, low-active, async
ctrl_i => ctrl, -- main control bus
-- CSR interface --
@ -394,19 +410,17 @@ begin
csr_wdata_i => xcsr_wdata, -- write data
csr_rdata_o => xcsr_rdata_pmp, -- read data
-- address input --
addr_if_i => pc_fetch, -- instruction fetch address
addr_if_i => pc_next, -- instruction fetch address
addr_ls_i => alu_add, -- load/store address
-- faults --
fault_ex_o => pmp_ex_fault, -- instruction fetch fault
fault_rw_o => pmp_rw_fault -- read/write access fault
-- access error --
fault_o => pmp_fault -- permission violation
);
end generate;
pmp_inst_false:
if not RISCV_ISA_Smpmp generate
xcsr_rdata_pmp <= (others => '0');
pmp_ex_fault <= '0';
pmp_rw_fault <= '0';
pmp_fault <= '0';
end generate;

View file

@ -29,48 +29,49 @@ use neorv32.neorv32_package.all;
entity neorv32_cpu_control is
generic (
-- General --
HART_ID : std_ulogic_vector(31 downto 0); -- hardware thread ID
VENDOR_ID : std_ulogic_vector(31 downto 0); -- vendor's JEDEC ID
BOOT_ADDR : std_ulogic_vector(31 downto 0); -- cpu boot address
DEBUG_PARK_ADDR : std_ulogic_vector(31 downto 0); -- cpu debug-mode parking loop entry address, 4-byte aligned
DEBUG_EXC_ADDR : std_ulogic_vector(31 downto 0); -- cpu debug-mode exception entry address, 4-byte aligned
HART_ID : natural; -- hardware thread ID
VENDOR_ID : std_ulogic_vector(31 downto 0); -- vendor's JEDEC ID
BOOT_ADDR : std_ulogic_vector(31 downto 0); -- cpu boot address
DEBUG_PARK_ADDR : std_ulogic_vector(31 downto 0); -- cpu debug-mode parking loop entry address, 4-byte aligned
DEBUG_EXC_ADDR : std_ulogic_vector(31 downto 0); -- cpu debug-mode exception entry address, 4-byte aligned
-- RISC-V ISA Extensions --
RISCV_ISA_B : boolean; -- implement bit-manipulation extension
RISCV_ISA_C : boolean; -- implement compressed extension
RISCV_ISA_E : boolean; -- implement embedded-class register file extension
RISCV_ISA_M : boolean; -- implement mul/div extension
RISCV_ISA_U : boolean; -- implement user mode extension
RISCV_ISA_Zalrsc : boolean; -- implement atomic reservation-set extension
RISCV_ISA_Zba : boolean; -- implement shifted-add bit-manipulation extension
RISCV_ISA_Zbb : boolean; -- implement basic bit-manipulation extension
RISCV_ISA_Zbkb : boolean; -- implement bit-manipulation instructions for cryptography
RISCV_ISA_Zbkc : boolean; -- implement carry-less multiplication instructions
RISCV_ISA_Zbkx : boolean; -- implement cryptography crossbar permutation extension
RISCV_ISA_Zbs : boolean; -- implement single-bit bit-manipulation extension
RISCV_ISA_Zfinx : boolean; -- implement 32-bit floating-point extension
RISCV_ISA_Zicntr : boolean; -- implement base counters
RISCV_ISA_Zicond : boolean; -- implement integer conditional operations
RISCV_ISA_Zihpm : boolean; -- implement hardware performance monitors
RISCV_ISA_Zkn : boolean; -- NIST algorithm suite available
RISCV_ISA_Zknd : boolean; -- implement cryptography NIST AES decryption extension
RISCV_ISA_Zkne : boolean; -- implement cryptography NIST AES encryption extension
RISCV_ISA_Zknh : boolean; -- implement cryptography NIST hash extension
RISCV_ISA_Zks : boolean; -- ShangMi algorithm suite available
RISCV_ISA_Zksed : boolean; -- implement ShangMi block cypher extension
RISCV_ISA_Zksh : boolean; -- implement ShangMi hash extension
RISCV_ISA_Zkt : boolean; -- data-independent execution time available (for cryptographic operations)
RISCV_ISA_Zmmul : boolean; -- implement multiply-only M sub-extension
RISCV_ISA_Zxcfu : boolean; -- implement custom (instr.) functions unit
RISCV_ISA_Sdext : boolean; -- implement external debug mode extension
RISCV_ISA_Sdtrig : boolean; -- implement trigger module extension
RISCV_ISA_Smpmp : boolean; -- implement physical memory protection
RISCV_ISA_B : boolean; -- implement bit-manipulation extension
RISCV_ISA_C : boolean; -- implement compressed extension
RISCV_ISA_E : boolean; -- implement embedded-class register file extension
RISCV_ISA_M : boolean; -- implement mul/div extension
RISCV_ISA_U : boolean; -- implement user mode extension
RISCV_ISA_Zalrsc : boolean; -- implement atomic reservation-set extension
RISCV_ISA_Zba : boolean; -- implement shifted-add bit-manipulation extension
RISCV_ISA_Zbb : boolean; -- implement basic bit-manipulation extension
RISCV_ISA_Zbkb : boolean; -- implement bit-manipulation instructions for cryptography
RISCV_ISA_Zbkc : boolean; -- implement carry-less multiplication instructions
RISCV_ISA_Zbkx : boolean; -- implement cryptography crossbar permutation extension
RISCV_ISA_Zbs : boolean; -- implement single-bit bit-manipulation extension
RISCV_ISA_Zfinx : boolean; -- implement 32-bit floating-point extension
RISCV_ISA_Zicntr : boolean; -- implement base counters
RISCV_ISA_Zicond : boolean; -- implement integer conditional operations
RISCV_ISA_Zihpm : boolean; -- implement hardware performance monitors
RISCV_ISA_Zkn : boolean; -- NIST algorithm suite available
RISCV_ISA_Zknd : boolean; -- implement cryptography NIST AES decryption extension
RISCV_ISA_Zkne : boolean; -- implement cryptography NIST AES encryption extension
RISCV_ISA_Zknh : boolean; -- implement cryptography NIST hash extension
RISCV_ISA_Zks : boolean; -- ShangMi algorithm suite available
RISCV_ISA_Zksed : boolean; -- implement ShangMi block cypher extension
RISCV_ISA_Zksh : boolean; -- implement ShangMi hash extension
RISCV_ISA_Zkt : boolean; -- data-independent execution time available (for cryptographic operations)
RISCV_ISA_Zmmul : boolean; -- implement multiply-only M sub-extension
RISCV_ISA_Zxcfu : boolean; -- implement custom (instr.) functions unit
RISCV_ISA_Sdext : boolean; -- implement external debug mode extension
RISCV_ISA_Sdtrig : boolean; -- implement trigger module extension
RISCV_ISA_Smpmp : boolean; -- implement physical memory protection
-- Tuning Options --
FAST_MUL_EN : boolean; -- use DSPs for M extension's multiplier
FAST_SHIFT_EN : boolean; -- use barrel shifter for shift operations
REGFILE_HW_RST : boolean; -- implement full hardware reset for register file
CPU_CLOCK_GATING_EN : boolean; -- enable clock gating when in sleep mode
CPU_FAST_MUL_EN : boolean; -- use DSPs for M extension's multiplier
CPU_FAST_SHIFT_EN : boolean; -- use barrel shifter for shift operations
CPU_RF_HW_RST_EN : boolean; -- implement full hardware reset for register file
-- Hardware Performance Monitors (HPM) --
HPM_NUM_CNTS : natural range 0 to 13; -- number of implemented HPM counters (0..13)
HPM_CNT_WIDTH : natural range 0 to 64 -- total size of HPM counters (0..64)
HPM_NUM_CNTS : natural range 0 to 13; -- number of implemented HPM counters (0..13)
HPM_CNT_WIDTH : natural range 0 to 64 -- total size of HPM counters (0..64)
);
port (
-- global control --
@ -79,17 +80,18 @@ entity neorv32_cpu_control is
rstn_i : in std_ulogic; -- global reset, low-active, async
ctrl_o : out ctrl_bus_t; -- main control bus
-- instruction fetch interface --
ibus_pmperr_i : in std_ulogic; -- instruction fetch pmp fault
ibus_req_o : out bus_req_t; -- request
ibus_rsp_i : in bus_rsp_t; -- response
-- pmp fault --
pmp_fault_i : in std_ulogic; -- instruction fetch / execute pmp fault
-- data path interface --
alu_cp_done_i : in std_ulogic; -- ALU iterative operation done
alu_cmp_i : in std_ulogic_vector(1 downto 0); -- comparator status
alu_add_i : in std_ulogic_vector(XLEN-1 downto 0); -- ALU address result
alu_imm_o : out std_ulogic_vector(XLEN-1 downto 0); -- immediate
rf_rs1_i : in std_ulogic_vector(XLEN-1 downto 0); -- rf source 1
pc_fetch_o : out std_ulogic_vector(XLEN-1 downto 0); -- instruction fetch address
pc_curr_o : out std_ulogic_vector(XLEN-1 downto 0); -- current PC (corresponding to current instruction)
pc_next_o : out std_ulogic_vector(XLEN-1 downto 0); -- next PC (corresponding to next instruction)
pc_ret_o : out std_ulogic_vector(XLEN-1 downto 0); -- return address
csr_rdata_o : out std_ulogic_vector(XLEN-1 downto 0); -- CSR read data
-- external CSR interface --
@ -113,7 +115,7 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
-- HPM counter auto-configuration --
constant hpm_num_c : natural := cond_sel_natural_f(RISCV_ISA_Zihpm, HPM_NUM_CNTS, 0);
constant hpm_cnt_lo_width_c : natural := min_natural_f(HPM_CNT_WIDTH, 32); -- size low word
constant hpm_cnt_hi_width_c : natural := (HPM_CNT_WIDTH / 32) * (HPM_CNT_WIDTH rem 32); -- size high word
constant hpm_cnt_hi_width_c : natural := HPM_CNT_WIDTH - hpm_cnt_lo_width_c; -- size high word
-- instruction fetch engine --
type fetch_engine_state_t is (IF_RESTART, IF_REQUEST, IF_PENDING);
@ -150,7 +152,8 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
signal issue_engine : issue_engine_t;
-- instruction execution engine --
type exe_engine_state_t is (DISPATCH, TRAP_ENTER, TRAP_EXIT, RESTART, SLEEP, EXECUTE, ALU_WAIT, BRANCH, BRANCHED, SYSTEM, MEM_REQ, MEM_RSP);
type exe_engine_state_t is (EX_DISPATCH, EX_TRAP_ENTER, EX_TRAP_EXIT, EX_RESTART, EX_SLEEP, EX_EXECUTE,
EX_ALU_WAIT, EX_BRANCH, EX_BRANCHED, EX_SYSTEM, EX_MEM_REQ, EX_MEM_RSP);
type exe_engine_t is record
state : exe_engine_state_t;
ir : std_ulogic_vector(31 downto 0); -- instruction word being executed right now
@ -168,8 +171,8 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
signal opcode : std_ulogic_vector(6 downto 0);
-- execution monitor --
signal monitor_cnt, monitor_add : std_ulogic_vector(monitor_mc_tmo_c downto 0);
signal monitor_exc : std_ulogic;
signal monitor_cnt : std_ulogic_vector(monitor_mc_tmo_c downto 0);
signal monitor_exc : std_ulogic;
-- CPU sleep-mode --
signal sleep_mode : std_ulogic;
@ -206,6 +209,7 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
type csr_t is record
addr : std_ulogic_vector(11 downto 0); -- physical access address
we, we_nxt : std_ulogic; -- write enable
operand : std_ulogic_vector(XLEN-1 downto 0); -- write operand
wdata : std_ulogic_vector(XLEN-1 downto 0); -- write data
rdata : std_ulogic_vector(XLEN-1 downto 0); -- read data
--
@ -250,12 +254,11 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
end record;
signal csr : csr_t;
-- hpm event configuration CSRs --
-- HPM event configuration CSRs --
type hpmevent_cfg_t is array (3 to 15) of std_ulogic_vector(hpmcnt_event_width_c-1 downto 0);
type hpmevent_rd_t is array (3 to 15) of std_ulogic_vector(XLEN-1 downto 0);
signal hpmevent_cfg : hpmevent_cfg_t;
signal hpmevent_rd : hpmevent_rd_t;
signal hpmevent_we : std_ulogic_vector(15 downto 0);
-- counter CSRs --
type cnt_dat_t is array (0 to 2+hpm_num_c) of std_ulogic_vector(XLEN-1 downto 0);
@ -263,13 +266,11 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
type cnt_ovf_t is array (0 to 2+hpm_num_c) of std_ulogic_vector(0 downto 0);
type cnt_inc_t is array (15 downto 0) of std_ulogic_vector(0 downto 0);
type cnt_t is record
we_lo : std_ulogic_vector(15 downto 0);
we_hi : std_ulogic_vector(15 downto 0);
inc : cnt_inc_t;
lo : cnt_dat_t; -- counter word low
hi : cnt_dat_t; -- counter word high
nxt : cnt_nxt_t; -- low-word increment including carry bit
ovf : cnt_ovf_t; -- counter low-to-high-word overflow
inc : cnt_inc_t;
lo : cnt_dat_t; -- counter word low
hi : cnt_dat_t; -- counter word high
nxt : cnt_nxt_t; -- low-word increment including carry bit
ovf : cnt_ovf_t; -- counter low-to-high-word overflow
end record;
signal cnt : cnt_t;
signal cnt_hi_rd, cnt_lo_rd : cnt_dat_t;
@ -308,18 +309,11 @@ begin
fetch_engine.pc <= (others => '0');
fetch_engine.priv <= '0';
elsif rising_edge(clk_i) then
-- restart request --
if (fetch_engine.state = IF_RESTART) then -- restart done
fetch_engine.restart <= '0';
else -- buffer request
fetch_engine.restart <= fetch_engine.restart or fetch_engine.reset;
end if;
-- fsm --
case fetch_engine.state is
when IF_REQUEST => -- request next 32-bit-aligned instruction word
-- ------------------------------------------------------------
fetch_engine.restart <= fetch_engine.restart or fetch_engine.reset; -- buffer restart request
if (ipb.free = "11") then -- free IPB space?
fetch_engine.state <= IF_PENDING;
elsif (fetch_engine.restart = '1') or (fetch_engine.reset = '1') then -- restart because of branch
@ -328,6 +322,7 @@ begin
when IF_PENDING => -- wait for bus response and write instruction data to prefetch buffer
-- ------------------------------------------------------------
fetch_engine.restart <= fetch_engine.restart or fetch_engine.reset; -- buffer restart request
if (fetch_engine.resp = '1') then -- wait for bus response
fetch_engine.pc <= std_ulogic_vector(unsigned(fetch_engine.pc) + 4); -- next word
fetch_engine.pc(1) <= '0'; -- (re-)align to 32-bit
@ -340,9 +335,10 @@ begin
when others => -- IF_RESTART: set new start address
-- ------------------------------------------------------------
fetch_engine.pc <= exe_engine.pc2(XLEN-1 downto 1) & '0'; -- initialize from PC incl. 16-bit-alignment bit
fetch_engine.priv <= csr.privilege_eff; -- set new privilege level
fetch_engine.state <= IF_REQUEST;
fetch_engine.restart <= '0'; -- restart done
fetch_engine.pc <= exe_engine.pc2(XLEN-1 downto 1) & '0'; -- initialize from PC incl. 16-bit-alignment bit
fetch_engine.priv <= csr.privilege_eff; -- set new privilege level
fetch_engine.state <= IF_REQUEST;
end case;
end if;
@ -350,7 +346,6 @@ begin
-- PC output for instruction fetch --
ibus_req_o.addr <= fetch_engine.pc(XLEN-1 downto 2) & "00"; -- word aligned
pc_fetch_o <= fetch_engine.pc(XLEN-1 downto 2) & "00"; -- word aligned
-- instruction fetch (read) request if IPB not full --
ibus_req_o.stb <= '1' when (fetch_engine.state = IF_REQUEST) and (ipb.free = "11") else '0';
@ -359,15 +354,15 @@ begin
fetch_engine.resp <= ibus_rsp_i.ack or ibus_rsp_i.err;
-- IPB instruction data and status --
ipb.wdata(0) <= (ibus_rsp_i.err or ibus_pmperr_i) & ibus_rsp_i.data(15 downto 0);
ipb.wdata(1) <= (ibus_rsp_i.err or ibus_pmperr_i) & ibus_rsp_i.data(31 downto 16);
ipb.wdata(0) <= ibus_rsp_i.err & ibus_rsp_i.data(15 downto 0);
ipb.wdata(1) <= ibus_rsp_i.err & ibus_rsp_i.data(31 downto 16);
-- IPB write enable --
ipb.we(0) <= '1' when (fetch_engine.state = IF_PENDING) and (fetch_engine.resp = '1') and
((fetch_engine.pc(1) = '0') or (not RISCV_ISA_C)) else '0';
ipb.we(1) <= '1' when (fetch_engine.state = IF_PENDING) and (fetch_engine.resp = '1') else '0';
-- bus access type --
-- bus access meta data --
ibus_req_o.priv <= fetch_engine.priv; -- current effective privilege level
ibus_req_o.data <= (others => '0'); -- read-only
ibus_req_o.ben <= (others => '0'); -- read-only
@ -375,6 +370,8 @@ begin
ibus_req_o.src <= '1'; -- source = instruction fetch
ibus_req_o.rvso <= '0'; -- cannot be a reservation set operation
ibus_req_o.fence <= ctrl.lsu_fence; -- fence operation, valid without STB being set
ibus_req_o.sleep <= sleep_mode; -- sleep mode, valid without STB being set
ibus_req_o.debug <= debug_ctrl.run; -- debug mode, valid without STB being set
-- Instruction Prefetch Buffer (FIFO) -----------------------------------------------------
@ -384,7 +381,7 @@ begin
prefetch_buffer_inst: entity neorv32.neorv32_fifo
generic map (
FIFO_DEPTH => 2, -- number of IPB entries; has to be a power of two, min 2
FIFO_WIDTH => ipb.wdata(i)'length, -- size of data elements in fifo
FIFO_WIDTH => ipb.wdata(i)'length, -- size of data elements in FIFO
FIFO_RSYNC => false, -- we NEED to read data asynchronously
FIFO_SAFE => false, -- no safe access required (ensured by FIFO-external logic)
FULL_RESET => true -- map to FFs and add a dedicated reset
@ -501,7 +498,7 @@ begin
if (rstn_i = '0') then
alu_imm_o <= (others => '0');
elsif rising_edge(clk_i) then
if (exe_engine.state = DISPATCH) then -- prepare update of next PC (using ALU's PC + IMM in EXECUTE state)
if (exe_engine.state = EX_DISPATCH) then -- prepare update of next PC (using ALU's PC + IMM in EX_EXECUTE state)
alu_imm_o <= (others => '0');
if RISCV_ISA_C and (issue_engine.data(33) = '1') then -- is de-compressed C instruction?
alu_imm_o(3 downto 0) <= x"2";
@ -550,7 +547,7 @@ begin
begin
if (rstn_i = '0') then
ctrl <= ctrl_bus_zero_c;
exe_engine.state <= RESTART;
exe_engine.state <= EX_RESTART;
exe_engine.ir <= (others => '0');
exe_engine.ci <= '0';
exe_engine.pc <= BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit-aligned boot address
@ -564,6 +561,7 @@ begin
-- PC output --
pc_curr_o <= exe_engine.pc(XLEN-1 downto 1) & '0'; -- address of current instruction
pc_next_o <= exe_engine.pc2(XLEN-1 downto 1) & '0'; -- address of next instruction
pc_ret_o <= exe_engine.ra(XLEN-1 downto 1) & '0'; -- return address
-- simplified rv32 opcode --
@ -572,7 +570,8 @@ begin
-- Execute Engine FSM Comb ----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
execute_engine_fsm_comb: process(exe_engine, debug_ctrl, trap_ctrl, hw_trigger_match, opcode, issue_engine, csr, alu_cp_done_i, lsu_wait_i, alu_add_i, branch_taken)
execute_engine_fsm_comb: process(exe_engine, debug_ctrl, trap_ctrl, hw_trigger_match, opcode, issue_engine, csr,
alu_cp_done_i, lsu_wait_i, alu_add_i, branch_taken, pmp_fault_i)
variable funct3_v : std_ulogic_vector(2 downto 0);
variable funct7_v : std_ulogic_vector(6 downto 0);
begin
@ -632,27 +631,27 @@ begin
-- state machine --
case exe_engine.state is
when DISPATCH => -- wait for ISSUE ENGINE to emit a valid instruction word
when EX_DISPATCH => -- wait for ISSUE ENGINE to emit a valid instruction word
-- ------------------------------------------------------------
ctrl_nxt.alu_opa_mux <= '1'; -- prepare update of next PC in EXECUTE (opa = current PC)
ctrl_nxt.alu_opb_mux <= '1'; -- prepare update of next PC in EXECUTE (opb = imm = +2/4)
ctrl_nxt.alu_opa_mux <= '1'; -- prepare update of next PC in EX_EXECUTE (opa = current PC)
ctrl_nxt.alu_opb_mux <= '1'; -- prepare update of next PC in EX_EXECUTE (opb = imm = +2/4)
--
if (trap_ctrl.env_pending = '1') or (trap_ctrl.exc_fire = '1') then -- pending trap or pending exception (fast)
exe_engine_nxt.state <= TRAP_ENTER;
exe_engine_nxt.state <= EX_TRAP_ENTER;
elsif RISCV_ISA_Sdtrig and (hw_trigger_match = '1') then -- hardware breakpoint
exe_engine_nxt.pc <= exe_engine.pc2(XLEN-1 downto 1) & '0'; -- PC <= next PC; intercept BEFORE executing the instruction
trap_ctrl.hwtrig <= '1';
exe_engine_nxt.state <= DISPATCH; -- stay here another round until trap_ctrl.hwtrig arrives in trap_ctrl.env_pending
exe_engine_nxt.state <= EX_DISPATCH; -- stay here another round until trap_ctrl.hwtrig arrives in trap_ctrl.env_pending
elsif (issue_engine.valid(0) = '1') or (issue_engine.valid(1) = '1') then -- new instruction word available
issue_engine.ack <= '1';
trap_ctrl.instr_be <= issue_engine.data(32); -- access fault during instruction fetch
exe_engine_nxt.ci <= issue_engine.data(33); -- this is a de-compressed instruction
exe_engine_nxt.ir <= issue_engine.data(31 downto 0); -- instruction word
exe_engine_nxt.pc <= exe_engine.pc2(XLEN-1 downto 1) & '0'; -- PC <= next PC
exe_engine_nxt.state <= EXECUTE; -- start executing new instruction
exe_engine_nxt.state <= EX_EXECUTE; -- start executing new instruction
end if;
when TRAP_ENTER => -- enter trap environment and jump to trap vector
when EX_TRAP_ENTER => -- enter trap environment and jump to trap vector
-- ------------------------------------------------------------
if (trap_ctrl.cause(5) = '1') and RISCV_ISA_Sdext then -- debug mode (re-)entry
exe_engine_nxt.pc2 <= DEBUG_PARK_ADDR(XLEN-1 downto 2) & "00"; -- debug mode enter; start at "parking loop" <normal_entry>
@ -668,10 +667,10 @@ begin
--
if (trap_ctrl.env_pending = '1') then -- wait for sync. exceptions to become pending
trap_ctrl.env_enter <= '1';
exe_engine_nxt.state <= RESTART; -- restart instruction fetch
exe_engine_nxt.state <= EX_RESTART; -- restart instruction fetch
end if;
when TRAP_EXIT => -- return from trap environment and jump to trap PC
when EX_TRAP_EXIT => -- return from trap environment and jump to trap PC
-- ------------------------------------------------------------
if (debug_ctrl.run = '1') and RISCV_ISA_Sdext then -- debug mode exit
exe_engine_nxt.pc2 <= csr.dpc(XLEN-1 downto 1) & '0';
@ -679,20 +678,20 @@ begin
exe_engine_nxt.pc2 <= csr.mepc(XLEN-1 downto 1) & '0';
end if;
trap_ctrl.env_exit <= '1';
exe_engine_nxt.state <= RESTART; -- restart instruction fetch
exe_engine_nxt.state <= EX_RESTART; -- restart instruction fetch
when RESTART => -- reset and restart instruction fetch at next PC
when EX_RESTART => -- reset and restart instruction fetch at next PC
-- ------------------------------------------------------------
ctrl_nxt.rf_zero_we <= not bool_to_ulogic_f(REGFILE_HW_RST); -- house keeping: force writing zero to x0 if it's a phys. register
ctrl_nxt.rf_zero_we <= not bool_to_ulogic_f(CPU_RF_HW_RST_EN); -- house keeping: force writing zero to x0 if it's a phys. register
fetch_engine.reset <= '1';
exe_engine_nxt.state <= BRANCHED; -- delay cycle to restart front-end
exe_engine_nxt.state <= EX_BRANCHED; -- delay cycle to restart front-end
when EXECUTE => -- decode and execute instruction (control will be here for exactly 1 cycle in any case)
-- [NOTE] register file is read in this stage; due to the sync read, data will be available in the _next_ state
when EX_EXECUTE => -- decode and execute instruction (control will be here for exactly 1 cycle in any case)
-- ------------------------------------------------------------
exe_engine_nxt.pc2 <= alu_add_i(XLEN-1 downto 1) & '0'; -- next PC (= PC + immediate)
trap_ctrl.instr_be <= pmp_fault_i; -- did this instruction cause a PMP-execute violation?
-- decode instruction class/type --
-- decode instruction class/type; [NOTE] register file is read in THIS stage; due to the sync read data will be available in the NEXT state --
case opcode is
-- register/immediate ALU operation --
@ -722,62 +721,62 @@ begin
((funct3_v = funct3_xor_c) and (funct7_v = "0000000")) or ((funct3_v = funct3_or_c) and (funct7_v = "0000000")) or
((funct3_v = funct3_and_c) and (funct7_v = "0000000")))) then -- base ALU instruction (excluding SLL, SRL, SRA)?
ctrl_nxt.rf_wb_en <= '1'; -- valid RF write-back (won't happen if exception)
exe_engine_nxt.state <= DISPATCH;
exe_engine_nxt.state <= EX_DISPATCH;
else -- [NOTE] potential illegal ALU[I] instruction are handled as multi-cycle operations that will time-out if no co-processor responds
ctrl_nxt.alu_cp_alu <= '1'; -- trigger ALU[I] opcode-space co-processor
exe_engine_nxt.state <= ALU_WAIT;
exe_engine_nxt.state <= EX_ALU_WAIT;
end if;
-- load upper immediate --
when opcode_lui_c =>
ctrl_nxt.alu_op <= alu_op_movb_c; -- pass immediate
ctrl_nxt.rf_wb_en <= '1'; -- valid RF write-back (won't happen if exception)
exe_engine_nxt.state <= DISPATCH;
exe_engine_nxt.state <= EX_DISPATCH;
-- add upper immediate to PC --
when opcode_auipc_c =>
ctrl_nxt.alu_op <= alu_op_add_c; -- add PC and immediate
ctrl_nxt.rf_wb_en <= '1'; -- valid RF write-back (won't happen if exception)
exe_engine_nxt.state <= DISPATCH;
exe_engine_nxt.state <= EX_DISPATCH;
-- memory access --
when opcode_load_c | opcode_store_c | opcode_amo_c =>
exe_engine_nxt.state <= MEM_REQ;
exe_engine_nxt.state <= EX_MEM_REQ;
-- branch / jump-and-link (with register) --
when opcode_branch_c | opcode_jal_c | opcode_jalr_c =>
exe_engine_nxt.state <= BRANCH;
exe_engine_nxt.state <= EX_BRANCH;
-- memory fence operations (execute even if illegal funct3) --
when opcode_fence_c =>
ctrl_nxt.lsu_fence <= '1'; -- [NOTE] fence == fence.i; ignore all ordering bits
exe_engine_nxt.state <= RESTART; -- reset instruction fetch + IPB (actually only required for fence.i)
exe_engine_nxt.state <= EX_RESTART; -- reset instruction fetch + IPB (actually only required for fence.i)
-- FPU: floating-point operations --
when opcode_fop_c =>
ctrl_nxt.alu_cp_fpu <= '1'; -- trigger FPU co-processor
exe_engine_nxt.state <= ALU_WAIT; -- will be aborted via monitor timeout if FPU is not implemented
exe_engine_nxt.state <= EX_ALU_WAIT; -- will be aborted via monitor timeout if FPU is not implemented
-- CFU: custom RISC-V instructions --
when opcode_cust0_c | opcode_cust1_c =>
ctrl_nxt.alu_cp_cfu <= '1'; -- trigger CFU co-processor
exe_engine_nxt.state <= ALU_WAIT; -- will be aborted via monitor timeout if CFU is not implemented
exe_engine_nxt.state <= EX_ALU_WAIT; -- will be aborted via monitor timeout if CFU is not implemented
-- environment/CSR operation or ILLEGAL opcode --
when others =>
exe_engine_nxt.state <= SYSTEM;
exe_engine_nxt.state <= EX_SYSTEM;
end case; -- /EXECUTE
end case; -- /EX_EXECUTE
when ALU_WAIT => -- wait for multi-cycle ALU co-processor operation to finish or trap
when EX_ALU_WAIT => -- wait for multi-cycle ALU co-processor operation to finish or trap
-- ------------------------------------------------------------
ctrl_nxt.alu_op <= alu_op_cp_c;
if (alu_cp_done_i = '1') or (trap_ctrl.exc_buf(exc_illegal_c) = '1') then
ctrl_nxt.rf_wb_en <= '1'; -- valid RF write-back (won't happen if exception)
exe_engine_nxt.state <= DISPATCH;
exe_engine_nxt.state <= EX_DISPATCH;
end if;
when BRANCH => -- update next PC on taken branches and jumps
when EX_BRANCH => -- update next PC on taken branches and jumps
-- ------------------------------------------------------------
exe_engine_nxt.ra <= exe_engine.pc2(XLEN-1 downto 1) & '0'; -- output return address
ctrl_nxt.rf_wb_en <= opcode(2); -- save return address if link operation (won't happen if exception)
@ -785,23 +784,23 @@ begin
trap_ctrl.instr_ma <= alu_add_i(1) and bool_to_ulogic_f(not RISCV_ISA_C); -- branch destination is misaligned?
fetch_engine.reset <= '1'; -- reset instruction fetch to restart at modified PC
exe_engine_nxt.pc2 <= alu_add_i(XLEN-1 downto 1) & '0';
exe_engine_nxt.state <= BRANCHED; -- shortcut (faster than going to RESTART)
exe_engine_nxt.state <= EX_BRANCHED; -- shortcut (faster than going to EX_RESTART)
else
exe_engine_nxt.state <= DISPATCH;
exe_engine_nxt.state <= EX_DISPATCH;
end if;
when BRANCHED => -- delay cycle to wait for reset of pipeline front-end (instruction fetch)
when EX_BRANCHED => -- delay cycle to wait for reset of pipeline front-end (instruction fetch)
-- ------------------------------------------------------------
exe_engine_nxt.state <= DISPATCH;
exe_engine_nxt.state <= EX_DISPATCH;
when MEM_REQ => -- trigger memory request
when EX_MEM_REQ => -- trigger memory request
-- ------------------------------------------------------------
if (trap_ctrl.exc_buf(exc_illegal_c) = '0') then -- memory request only if not an illegal instruction
ctrl_nxt.lsu_req <= '1'; -- memory access request
end if;
exe_engine_nxt.state <= MEM_RSP;
exe_engine_nxt.state <= EX_MEM_RSP;
when MEM_RSP => -- wait for memory response
when EX_MEM_RSP => -- wait for memory response
-- ------------------------------------------------------------
if (lsu_wait_i = '0') or -- bus system has completed the transaction (if there was any)
(trap_ctrl.exc_buf(exc_saccess_c) = '1') or (trap_ctrl.exc_buf(exc_laccess_c) = '1') or -- access exception
@ -810,25 +809,25 @@ begin
if (RISCV_ISA_Zalrsc and (opcode(2) = opcode_amo_c(2))) or (opcode(5) = '0') then -- atomic operation / normal load
ctrl_nxt.rf_wb_en <= '1'; -- allow write-back to register file (won't happen in case of exception)
end if;
exe_engine_nxt.state <= DISPATCH;
exe_engine_nxt.state <= EX_DISPATCH;
end if;
when SLEEP => -- sleep mode
when EX_SLEEP => -- sleep mode
-- ------------------------------------------------------------
if (trap_ctrl.wakeup = '1') then
exe_engine_nxt.state <= DISPATCH;
exe_engine_nxt.state <= EX_DISPATCH;
end if;
when others => -- SYSTEM - CSR/ENVIRONMENT operation; no effect if illegal instruction
when others => -- EX_SYSTEM - CSR/ENVIRONMENT operation; no effect if illegal instruction
-- ------------------------------------------------------------
exe_engine_nxt.state <= DISPATCH; -- default
exe_engine_nxt.state <= EX_DISPATCH; -- default
if (funct3_v = funct3_env_c) and (trap_ctrl.exc_buf(exc_illegal_c) = '0') then -- non-illegal ENVIRONMENT
case exe_engine.ir(instr_funct12_lsb_c+2 downto instr_funct12_lsb_c) is -- three LSBs are sufficient here
when "000" => trap_ctrl.ecall <= '1'; -- ecall
when "001" => trap_ctrl.ebreak <= '1'; -- ebreak
when "010" => exe_engine_nxt.state <= TRAP_EXIT; -- xret
when "101" => exe_engine_nxt.state <= SLEEP; -- wfi
when others => exe_engine_nxt.state <= DISPATCH; -- illegal or CSR operation
when "010" => exe_engine_nxt.state <= EX_TRAP_EXIT; -- xret
when "101" => exe_engine_nxt.state <= EX_SLEEP; -- wfi
when others => exe_engine_nxt.state <= EX_DISPATCH; -- illegal or CSR operation
end case;
end if;
-- always write to CSR (if CSR instruction); ENVIRONMENT operations have rs1/imm5 = zero so this won't happen then --
@ -863,7 +862,7 @@ begin
-- load/store unit --
ctrl_o.lsu_req <= ctrl.lsu_req;
ctrl_o.lsu_rw <= ctrl.lsu_rw;
ctrl_o.lsu_mo_we <= '1' when (exe_engine.state = MEM_REQ) else '0'; -- write memory output registers (data & address)
ctrl_o.lsu_mo_we <= '1' when (exe_engine.state = EX_MEM_REQ) else '0'; -- write memory output registers (data & address)
ctrl_o.lsu_fence <= ctrl.lsu_fence;
ctrl_o.lsu_priv <= csr.mstatus_mpp when (csr.mstatus_mprv = '1') else csr.privilege_eff; -- effective privilege level for loads/stores in M-mode
-- instruction word bit fields --
@ -888,13 +887,14 @@ begin
if (rstn_i = '0') then
monitor_cnt <= (others => '0');
elsif rising_edge(clk_i) then
monitor_cnt <= std_ulogic_vector(unsigned(monitor_add) + 1);
if (exe_engine.state = EX_ALU_WAIT) then
monitor_cnt <= std_ulogic_vector(unsigned(monitor_cnt) + 1);
else
monitor_cnt <= (others => '0');
end if;
end if;
end process multi_cycle_monitor;
-- timeout counter (allow mapping of entire logic into the LUTs in front of the carry-chain) --
monitor_add <= monitor_cnt when (exe_engine.state = ALU_WAIT) else (others => '0');
-- raise illegal instruction exception if a multi-cycle instruction takes longer than a bound amount of time --
monitor_exc <= monitor_cnt(monitor_cnt'left);
@ -983,7 +983,7 @@ begin
-- ------------------------------------------------------------
-- Privilege level
-- ------------------------------------------------------------
if (csr_addr_v(11 downto 2) = csr_dcsr_c(11 downto 2)) and -- debug-mode-only CSR (dcsr, dpc, dscratch)?
if (csr_addr_v(11 downto 4) = csr_dcsr_c(11 downto 4)) and -- debug-mode-only CSR?
RISCV_ISA_Sdext and (debug_ctrl.run = '0') then -- debug-mode implemented and not running?
csr_valid(0) <= '0'; -- invalid access
elsif RISCV_ISA_Zicntr and RISCV_ISA_U and (csr.privilege_eff = '0') and -- any user-mode counters available and in user-mode?
@ -1004,16 +1004,16 @@ begin
-- -------------------------------------------------------------------------------------------
illegal_check: process(exe_engine, csr, csr_valid, debug_ctrl)
begin
illegal_cmd <= '1'; -- default: illegal
case exe_engine.ir(instr_opcode_msb_c downto instr_opcode_lsb_c) is -- check entire opcode
when opcode_lui_c | opcode_auipc_c | opcode_jal_c => -- U-instruction type
illegal_cmd <= '0'; -- all encodings are valid
when opcode_jalr_c => -- unconditional jump-and-link
case exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) is
when "000" => illegal_cmd <= '0';
when others => illegal_cmd <= '1';
end case;
if (exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "000") then
illegal_cmd <= '0';
end if;
when opcode_branch_c => -- conditional branch
case exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) is
@ -1034,21 +1034,18 @@ begin
end case;
when opcode_amo_c => -- atomic memory operation (LR/SC)
if RISCV_ISA_Zalrsc and (exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "010") and
(exe_engine.ir(instr_funct7_lsb_c+6 downto instr_funct7_lsb_c+3) = "0001") then -- LR.W/SC.W
if (exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "010") and RISCV_ISA_Zalrsc and
(exe_engine.ir(instr_funct7_lsb_c+6 downto instr_funct7_lsb_c+3) = "0001") then -- LR.W/SC.W
illegal_cmd <= '0';
else
illegal_cmd <= '1';
end if;
when opcode_alu_c | opcode_alui_c | opcode_fop_c | opcode_cust0_c | opcode_cust1_c => -- ALU[I] / FPU / CFU operation
illegal_cmd <= '0'; -- [NOTE] valid if not terminated by the "instruction execution monitor"
when opcode_alu_c | opcode_alui_c | opcode_fop_c | opcode_cust0_c | opcode_cust1_c => -- ALU[I] / FPU / custom operations
illegal_cmd <= '0'; -- [NOTE] valid if not terminated/invalidated by the "instruction execution monitor"
when opcode_fence_c => -- memory ordering
case exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) is
when funct3_fence_c | funct3_fencei_c => illegal_cmd <= '0';
when others => illegal_cmd <= '1';
end case;
if (exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c+1) = funct3_fence_c(2 downto 1)) then
illegal_cmd <= '0';
end if;
when opcode_system_c => -- CSR / system instruction
if (exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_env_c) then -- system environment
@ -1061,12 +1058,8 @@ begin
when funct12_wfi_c => illegal_cmd <= (not csr.privilege) and csr.mstatus_tw; -- wfi allowed in M-mode or if TW is zero
when others => illegal_cmd <= '1'; -- undefined
end case;
else
illegal_cmd <= '1';
end if;
elsif (csr_valid /= "111") or (exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csril_c) then -- invalid CSR operation
illegal_cmd <= '1';
else
elsif (csr_valid = "111") and (exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) /= funct3_csril_c) then -- valid CSR operation
illegal_cmd <= '0';
end if;
@ -1079,7 +1072,7 @@ begin
-- Illegal Operation Check ----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
trap_ctrl.instr_il <= '1' when ((exe_engine.state = EXECUTE) or (exe_engine.state = ALU_WAIT)) and -- check in execution states only
trap_ctrl.instr_il <= '1' when ((exe_engine.state = EX_EXECUTE) or (exe_engine.state = EX_ALU_WAIT)) and -- check in execution states only
((monitor_exc = '1') or (illegal_cmd = '1')) else '0'; -- instruction timeout or illegal instruction
@ -1253,7 +1246,7 @@ begin
end if;
end if;
-- trap environment has just been entered --
if (exe_engine.state = EXECUTE) then -- first instruction of trap environment is executing
if (exe_engine.state = EX_EXECUTE) then -- first instruction of trap environment is executing
trap_ctrl.env_entered <= '0';
elsif (trap_ctrl.env_enter = '1') then
trap_ctrl.env_entered <= '1';
@ -1266,20 +1259,20 @@ begin
-- system interrupt? --
trap_ctrl.irq_fire(0) <= '1' when
(exe_engine.state = EXECUTE) and -- trigger system IRQ only in EXECUTE state
(exe_engine.state = EX_EXECUTE) and -- trigger system IRQ only in EX_EXECUTE state
(or_reduce_f(trap_ctrl.irq_buf(irq_firq_15_c downto irq_msi_irq_c)) = '1') and -- pending system IRQ
((csr.mstatus_mie = '1') or (csr.privilege = priv_mode_u_c)) and -- IRQ only when in M-mode and MIE=1 OR when in U-mode
(debug_ctrl.run = '0') and (csr.dcsr_step = '0') else '0'; -- no system IRQs when in debug-mode / during single-stepping
-- debug-entry halt interrupt? --
trap_ctrl.irq_fire(1) <= '1' when
((exe_engine.state = EXECUTE) or (exe_engine.state = BRANCHED)) and -- allow halt also after "reset" (#879)
((exe_engine.state = EX_EXECUTE) or (exe_engine.state = EX_BRANCHED)) and -- allow halt also after "reset" (#879)
(trap_ctrl.irq_buf(irq_db_halt_c) = '1') else '0'; -- pending external halt
-- debug-entry single-step interrupt? --
trap_ctrl.irq_fire(2) <= '1' when
((exe_engine.state = EXECUTE) or -- trigger single-step in EXECUTE state
((trap_ctrl.env_entered = '1') and (exe_engine.state = BRANCHED))) and -- also allow triggering when entering a system trap (#887)
((exe_engine.state = EX_EXECUTE) or -- trigger single-step in EX_EXECUTE state
((trap_ctrl.env_entered = '1') and (exe_engine.state = EX_BRANCHED))) and -- also allow triggering when entering a system trap (#887)
(trap_ctrl.irq_buf(irq_db_step_c) = '1') else '0'; -- pending single-step halt
@ -1290,7 +1283,7 @@ begin
if (rstn_i = '0') then
sleep_mode <= '0';
elsif rising_edge(clk_aux_i) then
if (exe_engine.state = SLEEP) and -- instruction execution has halted
if (exe_engine.state = EX_SLEEP) and -- instruction execution has halted
(ipb.free /= "11") and -- instruction fetch has halted
(trap_ctrl.wakeup = '0') then -- no wake-up request
sleep_mode <= '1';
@ -1315,11 +1308,8 @@ begin
if (rstn_i = '0') then
csr.addr <= (others => '0');
elsif rising_edge(clk_i) then
-- update only for actual CSR operations to reduce switching activity on the CSR address net --
if (opcode = opcode_system_c) then
csr.addr(11 downto 10) <= exe_engine.ir(instr_imm12_lsb_c+11 downto instr_imm12_lsb_c+10);
csr.addr(9 downto 8) <= replicate_f(exe_engine.ir(instr_imm12_lsb_c+8), 2); -- M-mode (11) and U-mode (00) CSRs only
csr.addr(7 downto 0) <= exe_engine.ir(instr_imm12_lsb_c+7 downto instr_imm12_lsb_c);
if (opcode = opcode_system_c) then -- update only for actual CSR operations to reduce switching activity on csr.addr net
csr.addr <= exe_engine.ir(instr_imm12_msb_c downto instr_imm12_lsb_c);
end if;
end if;
end process csr_addr_reg;
@ -1327,23 +1317,13 @@ begin
-- CSR Write-Data ALU ---------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
csr_write_data: process(exe_engine.ir, csr.rdata, rf_rs1_i)
variable tmp_v : std_ulogic_vector(XLEN-1 downto 0);
begin
-- immediate/register operand --
if (exe_engine.ir(instr_funct3_msb_c) = '1') then
tmp_v := (others => '0');
tmp_v(4 downto 0) := exe_engine.ir(19 downto 15); -- uimm5
else
tmp_v := rf_rs1_i;
end if;
-- tiny ALU to compute CSR write data --
case exe_engine.ir(instr_funct3_msb_c-1 downto instr_funct3_lsb_c) is
when "10" => csr.wdata <= csr.rdata or tmp_v; -- set
when "11" => csr.wdata <= csr.rdata and (not tmp_v); -- clear
when others => csr.wdata <= tmp_v; -- write
end case;
end process csr_write_data;
csr.operand <= rf_rs1_i when (exe_engine.ir(instr_funct3_msb_c) = '0') else (x"000000" & "000" & exe_engine.ir(19 downto 15));
-- tiny ALU to compute CSR write data --
with exe_engine.ir(instr_funct3_msb_c-1 downto instr_funct3_lsb_c) select csr.wdata <=
csr.rdata or csr.operand when "10", -- set
csr.rdata and (not csr.operand) when "11", -- clear
csr.operand when others; -- write
-- External CSR Interface -----------------------------------------------------------------
@ -1586,7 +1566,7 @@ begin
end if;
-- ********************************************************************************
-- Override - hardwire/terminate unavailable registers/bits
-- Override - terminate unavailable registers and bits
-- ********************************************************************************
-- hardwired bits --
@ -1654,7 +1634,7 @@ begin
csr.rdata <= (others => '0');
elsif rising_edge(clk_i) then
csr.rdata <= (others => '0'); -- default; output all-zero if there is no explicit CSR read operation
if (exe_engine.state = SYSTEM) then -- always read from CSR file in SYSTEM state
if (exe_engine.state = EX_SYSTEM) then -- always read from CSR file in EX_SYSTEM state
case csr.addr is -- address is zero if there is no CSR operation
-- --------------------------------------------------------------------
@ -1825,7 +1805,7 @@ begin
when csr_mvendorid_c => csr.rdata <= VENDOR_ID; -- vendor's JEDEC ID
when csr_marchid_c => csr.rdata(4 downto 0) <= "10011"; -- architecture ID - official RISC-V open-source arch ID
when csr_mimpid_c => csr.rdata <= hw_version_c; -- implementation ID -- NEORV32 hardware version
when csr_mhartid_c => csr.rdata <= HART_ID; -- hardware thread ID
when csr_mhartid_c => csr.rdata(9 downto 0) <= std_ulogic_vector(to_unsigned(HART_ID, 10)); -- hardware thread ID
-- when csr_mconfigptr_c => csr.rdata <= (others => '0'); -- machine configuration pointer register - hardwired to zero
-- --------------------------------------------------------------------
@ -1849,40 +1829,41 @@ begin
-- machine extended ISA extensions information --
when csr_mxisa_c =>
-- extended ISA (sub-)extensions --
csr.rdata(0) <= '1'; -- Zicsr: CSR access (always enabled)
csr.rdata(1) <= '1'; -- Zifencei: instruction stream sync. (always enabled)
csr.rdata(2) <= bool_to_ulogic_f(RISCV_ISA_Zmmul); -- Zmmul: mul/div
csr.rdata(3) <= bool_to_ulogic_f(RISCV_ISA_Zxcfu); -- Zxcfu: custom RISC-V instructions
csr.rdata(4) <= bool_to_ulogic_f(RISCV_ISA_Zkt); -- Zkt: data independent execution latency
csr.rdata(5) <= bool_to_ulogic_f(RISCV_ISA_Zfinx); -- Zfinx: FPU using x registers
csr.rdata(6) <= bool_to_ulogic_f(RISCV_ISA_Zicond); -- Zicond: integer conditional operations
csr.rdata(7) <= bool_to_ulogic_f(RISCV_ISA_Zicntr); -- Zicntr: base counters
csr.rdata(8) <= bool_to_ulogic_f(RISCV_ISA_Smpmp); -- Smpmp: physical memory protection
csr.rdata(9) <= bool_to_ulogic_f(RISCV_ISA_Zihpm); -- Zihpm: hardware performance monitors
csr.rdata(10) <= bool_to_ulogic_f(RISCV_ISA_Sdext); -- Sdext: RISC-V external debug
csr.rdata(11) <= bool_to_ulogic_f(RISCV_ISA_Sdtrig); -- Sdtrig: trigger module
csr.rdata(12) <= bool_to_ulogic_f(RISCV_ISA_Zbkx); -- Zbkx: cryptography crossbar permutation
csr.rdata(13) <= bool_to_ulogic_f(RISCV_ISA_Zknd); -- Zknd: cryptography NIST AES decryption
csr.rdata(14) <= bool_to_ulogic_f(RISCV_ISA_Zkne); -- Zkne: cryptography NIST AES encryption
csr.rdata(15) <= bool_to_ulogic_f(RISCV_ISA_Zknh); -- Zknh: cryptography NIST hash functions
csr.rdata(16) <= bool_to_ulogic_f(RISCV_ISA_Zbkb); -- Zbkb: bit manipulation instructions for cryptography
csr.rdata(17) <= bool_to_ulogic_f(RISCV_ISA_Zbkc); -- Zbkc: carry-less multiplication for cryptography
csr.rdata(18) <= bool_to_ulogic_f(RISCV_ISA_Zkn); -- Zkn: NIST algorithm suite
csr.rdata(19) <= bool_to_ulogic_f(RISCV_ISA_Zksh); -- Zksh: ShangMi hash functions
csr.rdata(20) <= bool_to_ulogic_f(RISCV_ISA_Zksed); -- Zksed: ShangMi block cyphers
csr.rdata(21) <= bool_to_ulogic_f(RISCV_ISA_Zks); -- Zks: ShangMi algorithm suite
csr.rdata(22) <= bool_to_ulogic_f(RISCV_ISA_Zba); -- Zba: shifted-add bit-manipulation
csr.rdata(23) <= bool_to_ulogic_f(RISCV_ISA_Zbb); -- Zbb: basic bit-manipulation extension
csr.rdata(24) <= bool_to_ulogic_f(RISCV_ISA_Zbs); -- Zbs: single-bit bit-manipulation extension
csr.rdata(25) <= bool_to_ulogic_f(RISCV_ISA_Zalrsc); -- Zalrsc: reservation set extension
csr.rdata(0) <= '1'; -- Zicsr: CSR access (always enabled)
csr.rdata(1) <= '1'; -- Zifencei: instruction stream sync. (always enabled)
csr.rdata(2) <= bool_to_ulogic_f(RISCV_ISA_Zmmul); -- Zmmul: mul/div
csr.rdata(3) <= bool_to_ulogic_f(RISCV_ISA_Zxcfu); -- Zxcfu: custom RISC-V instructions
csr.rdata(4) <= bool_to_ulogic_f(RISCV_ISA_Zkt); -- Zkt: data independent execution latency
csr.rdata(5) <= bool_to_ulogic_f(RISCV_ISA_Zfinx); -- Zfinx: FPU using x registers
csr.rdata(6) <= bool_to_ulogic_f(RISCV_ISA_Zicond); -- Zicond: integer conditional operations
csr.rdata(7) <= bool_to_ulogic_f(RISCV_ISA_Zicntr); -- Zicntr: base counters
csr.rdata(8) <= bool_to_ulogic_f(RISCV_ISA_Smpmp); -- Smpmp: physical memory protection
csr.rdata(9) <= bool_to_ulogic_f(RISCV_ISA_Zihpm); -- Zihpm: hardware performance monitors
csr.rdata(10) <= bool_to_ulogic_f(RISCV_ISA_Sdext); -- Sdext: RISC-V external debug
csr.rdata(11) <= bool_to_ulogic_f(RISCV_ISA_Sdtrig); -- Sdtrig: trigger module
csr.rdata(12) <= bool_to_ulogic_f(RISCV_ISA_Zbkx); -- Zbkx: cryptography crossbar permutation
csr.rdata(13) <= bool_to_ulogic_f(RISCV_ISA_Zknd); -- Zknd: cryptography NIST AES decryption
csr.rdata(14) <= bool_to_ulogic_f(RISCV_ISA_Zkne); -- Zkne: cryptography NIST AES encryption
csr.rdata(15) <= bool_to_ulogic_f(RISCV_ISA_Zknh); -- Zknh: cryptography NIST hash functions
csr.rdata(16) <= bool_to_ulogic_f(RISCV_ISA_Zbkb); -- Zbkb: bit manipulation instructions for cryptography
csr.rdata(17) <= bool_to_ulogic_f(RISCV_ISA_Zbkc); -- Zbkc: carry-less multiplication for cryptography
csr.rdata(18) <= bool_to_ulogic_f(RISCV_ISA_Zkn); -- Zkn: NIST algorithm suite
csr.rdata(19) <= bool_to_ulogic_f(RISCV_ISA_Zksh); -- Zksh: ShangMi hash functions
csr.rdata(20) <= bool_to_ulogic_f(RISCV_ISA_Zksed); -- Zksed: ShangMi block cyphers
csr.rdata(21) <= bool_to_ulogic_f(RISCV_ISA_Zks); -- Zks: ShangMi algorithm suite
csr.rdata(22) <= bool_to_ulogic_f(RISCV_ISA_Zba); -- Zba: shifted-add bit-manipulation
csr.rdata(23) <= bool_to_ulogic_f(RISCV_ISA_Zbb); -- Zbb: basic bit-manipulation extension
csr.rdata(24) <= bool_to_ulogic_f(RISCV_ISA_Zbs); -- Zbs: single-bit bit-manipulation extension
csr.rdata(25) <= bool_to_ulogic_f(RISCV_ISA_Zalrsc); -- Zalrsc: reservation set extension
csr.rdata(26) <= '0'; -- reserved
csr.rdata(27) <= '0'; -- reserved
-- tuning options --
csr.rdata(28) <= bool_to_ulogic_f(REGFILE_HW_RST); -- full hardware reset of register file
csr.rdata(29) <= bool_to_ulogic_f(FAST_MUL_EN); -- DSP-based multiplication (M extensions only)
csr.rdata(30) <= bool_to_ulogic_f(FAST_SHIFT_EN); -- parallel logic for shifts (barrel shifters)
csr.rdata(27) <= bool_to_ulogic_f(CPU_CLOCK_GATING_EN); -- enable clock gating when in sleep mode
csr.rdata(28) <= bool_to_ulogic_f(CPU_RF_HW_RST_EN); -- full hardware reset of register file
csr.rdata(29) <= bool_to_ulogic_f(CPU_FAST_MUL_EN); -- DSP-based multiplication (M extensions only)
csr.rdata(30) <= bool_to_ulogic_f(CPU_FAST_SHIFT_EN); -- parallel logic for shifts (barrel shifters)
-- misc --
csr.rdata(31) <= bool_to_ulogic_f(is_simulation_c); -- is this a simulation?
csr.rdata(31) <= bool_to_ulogic_f(is_simulation_c); -- is this a simulation?
-- --------------------------------------------------------------------
-- undefined/unavailable
@ -1904,22 +1885,6 @@ begin
-- Counter CSRs ---------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
-- write enable decoder --
cnt_we: process(csr)
begin
cnt.we_lo <= (others => '0');
cnt.we_hi <= (others => '0');
-- [NOTE] no need to check bits 6:4 of the address as they're always zero (checked by illegal CSR logic)
if (csr.we = '1') and (csr.addr(11 downto 8) = csr_mcycle_c(11 downto 8)) then
if (csr.addr(7) = '0') then -- low word
cnt.we_lo(to_integer(unsigned(csr.addr(3 downto 0)))) <= '1';
else -- high word
cnt.we_hi(to_integer(unsigned(csr.addr(3 downto 0)))) <= '1';
end if;
end if;
end process cnt_we;
-- hardware counters --
cnt_gen:
for i in 0 to 2+hpm_num_c generate
@ -1932,14 +1897,18 @@ begin
cnt.hi(i) <= (others => '0');
elsif rising_edge(clk_i) then
-- low word --
if (cnt.we_lo(i) = '1') then
-- [NOTE] no need to check bits 6:4 of the address as they're always zero (checked by illegal CSR logic)
if (csr.we = '1') and (csr.addr(11 downto 7) = csr_mcycle_c(11 downto 7)) and
(csr.addr(3 downto 0) = std_ulogic_vector(to_unsigned(i, 4))) then
cnt.lo(i) <= csr.wdata;
else
cnt.lo(i) <= cnt.nxt(i)(XLEN-1 downto 0);
end if;
cnt.ovf(i)(0) <= cnt.nxt(i)(XLEN);
-- high word --
if (cnt.we_hi(i) = '1') then
-- [NOTE] no need to check bits 6:4 of the address as they're always zero (checked by illegal CSR logic)
if (csr.we = '1') and (csr.addr(11 downto 7) = csr_mcycleh_c(11 downto 7)) and
(csr.addr(3 downto 0) = std_ulogic_vector(to_unsigned(i, 4))) then
cnt.hi(i) <= csr.wdata;
else
cnt.hi(i) <= std_ulogic_vector(unsigned(cnt.hi(i)) + unsigned(cnt.ovf(i)));
@ -1964,7 +1933,7 @@ begin
cnt_lo_rd(2) <= cnt.lo(2); -- instret
cnt_hi_rd(2) <= cnt.hi(2); -- instreth
end if;
-- hpm counters --
-- HPM counters --
if RISCV_ISA_Zihpm and (hpm_num_c > 0) then
for i in 3 to (hpm_num_c+3)-1 loop
if (hpm_cnt_lo_width_c > 0) then -- constrain low word size
@ -1983,16 +1952,6 @@ begin
hpmevent_gen_enable:
if RISCV_ISA_Zihpm and (hpm_num_c > 0) generate
-- write enable decoder --
hpmevent_write: process(csr)
begin
hpmevent_we <= (others => '0');
-- [NOTE] no need to check bit 4 of the address as it is always zero (checked by illegal CSR logic)
if (csr.addr(11 downto 5) = csr_mhpmevent3_c(11 downto 5)) and (csr.we = '1') then
hpmevent_we(to_integer(unsigned(csr.addr(3 downto 0)))) <= '1';
end if;
end process hpmevent_write;
-- event registers --
hpmevent_reg_gen:
for i in 3 to (hpm_num_c+3)-1 generate
@ -2001,7 +1960,9 @@ begin
if (rstn_i = '0') then
hpmevent_cfg(i) <= (others => '0');
elsif rising_edge(clk_i) then
if (hpmevent_we(i) = '1') then
-- [NOTE] no need to check bit 4 of the address as it is always zero (checked by illegal CSR logic)
if (csr.addr(11 downto 5) = csr_mhpmevent3_c(11 downto 5)) and
(csr.we = '1') and (csr.addr(3 downto 0) = std_ulogic_vector(to_unsigned(i, 4))) then
hpmevent_cfg(i) <= csr.wdata(hpmcnt_event_width_c-1 downto 0);
end if;
hpmevent_cfg(i)(hpmcnt_event_tm_c) <= '0'; -- time: not available
@ -2024,7 +1985,6 @@ begin
-- no HPMs implemented --
hpmevent_gen_disable:
if (not RISCV_ISA_Zihpm) or (hpm_num_c = 0) generate
hpmevent_we <= (others => '0');
hpmevent_cfg <= (others => (others => '0'));
hpmevent_rd <= (others => (others => '0'));
end generate;
@ -2041,7 +2001,7 @@ begin
cnt.inc(0) <= (others => (cnt_event(hpmcnt_event_cy_c) and (not csr.mcountinhibit(0)) and (not debug_ctrl.run)));
cnt.inc(1) <= (others => '0'); -- time: not available
cnt.inc(2) <= (others => (cnt_event(hpmcnt_event_ir_c) and (not csr.mcountinhibit(2)) and (not debug_ctrl.run)));
-- hpm counters --
-- HPM counters --
for i in 3 to 15 loop
cnt.inc(i) <= (others => (or_reduce_f(cnt_event and hpmevent_cfg(i)) and (not csr.mcountinhibit(i)) and (not debug_ctrl.run)));
end loop;
@ -2051,22 +2011,22 @@ begin
-- RISC-V-compliant counter events --
cnt_event(hpmcnt_event_cy_c) <= '1' when (sleep_mode = '0') else '0'; -- cycle: active cycle
cnt_event(hpmcnt_event_tm_c) <= '0'; -- time: not available
cnt_event(hpmcnt_event_ir_c) <= '1' when (exe_engine.state = EXECUTE) else '0'; -- instret: retired (==executed!) instruction
cnt_event(hpmcnt_event_ir_c) <= '1' when (exe_engine.state = EX_EXECUTE) else '0'; -- instret: retired (==executed!) instruction
-- NEORV32-specific counter events --
cnt_event(hpmcnt_event_compr_c) <= '1' when (exe_engine.state = EXECUTE) and (exe_engine.ci = '1') else '0'; -- executed compressed instruction
cnt_event(hpmcnt_event_wait_dis_c) <= '1' when (exe_engine.state = DISPATCH) and (issue_engine.valid = "00") else '0'; -- instruction dispatch wait cycle
cnt_event(hpmcnt_event_wait_alu_c) <= '1' when (exe_engine.state = ALU_WAIT) else '0'; -- multi-cycle ALU wait cycle
cnt_event(hpmcnt_event_branch_c) <= '1' when (exe_engine.state = BRANCH) else '0'; -- executed branch instruction
cnt_event(hpmcnt_event_branched_c) <= '1' when (exe_engine.state = BRANCHED) else '0'; -- control flow transfer
cnt_event(hpmcnt_event_load_c) <= '1' when (ctrl.lsu_req = '1') and (ctrl.lsu_rw = '0') else '0'; -- executed load operation
cnt_event(hpmcnt_event_store_c) <= '1' when (ctrl.lsu_req = '1') and (ctrl.lsu_rw = '1') else '0'; -- executed store operation
cnt_event(hpmcnt_event_wait_lsu_c) <= '1' when (ctrl.lsu_req = '0') and (exe_engine.state = MEM_RSP) else '0'; -- load/store memory wait cycle
cnt_event(hpmcnt_event_trap_c) <= '1' when (trap_ctrl.env_enter = '1') else '0'; -- entered trap
cnt_event(hpmcnt_event_compr_c) <= '1' when (exe_engine.state = EX_EXECUTE) and (exe_engine.ci = '1') else '0'; -- executed compressed instruction
cnt_event(hpmcnt_event_wait_dis_c) <= '1' when (exe_engine.state = EX_DISPATCH) and (issue_engine.valid = "00") else '0'; -- instruction dispatch wait cycle
cnt_event(hpmcnt_event_wait_alu_c) <= '1' when (exe_engine.state = EX_ALU_WAIT) else '0'; -- multi-cycle ALU wait cycle
cnt_event(hpmcnt_event_branch_c) <= '1' when (exe_engine.state = EX_BRANCH) else '0'; -- executed branch instruction
cnt_event(hpmcnt_event_branched_c) <= '1' when (exe_engine.state = EX_BRANCHED) else '0'; -- control flow transfer
cnt_event(hpmcnt_event_load_c) <= '1' when (ctrl.lsu_req = '1') and (ctrl.lsu_rw = '0') else '0'; -- executed load operation
cnt_event(hpmcnt_event_store_c) <= '1' when (ctrl.lsu_req = '1') and (ctrl.lsu_rw = '1') else '0'; -- executed store operation
cnt_event(hpmcnt_event_wait_lsu_c) <= '1' when (ctrl.lsu_req = '0') and (exe_engine.state = EX_MEM_RSP) else '0'; -- load/store memory wait cycle
cnt_event(hpmcnt_event_trap_c) <= '1' when (trap_ctrl.env_enter = '1') else '0'; -- entered trap
-- ****************************************************************************************************************************
-- CPU Debug Mode (Part of the On-Chip Debugger)
-- CPU Debug Mode
-- ****************************************************************************************************************************
-- Debug Control --------------------------------------------------------------------------

View file

@ -354,8 +354,10 @@ begin
res_o <= (others => '0'); -- default
if (done = '1') then
case out_sel is
when "100" => res_o <= xperm_res;
when "101" | "110" => res_o <= blk_res;
when "100" =>
res_o <= xperm_res;
when "101" | "110" =>
res_o <= blk_res;
when others =>
if EN_ZKSH and (ctrl_i.ir_opcode(5) = '0') and (funct12(3) = '1') then
res_o <= sm3_res;

View file

@ -110,7 +110,7 @@ begin
elsif rising_edge(clk_i) then
-- defaults --
ctrl.out_en <= '0';
ctrl.cnt <= std_ulogic_vector(to_unsigned(XLEN-2, index_size_f(XLEN))); -- cycle counter initialization
ctrl.cnt <= std_ulogic_vector(to_unsigned(XLEN-2, ctrl.cnt'length)); -- cycle counter initialization
-- fsm --
case ctrl.state is

View file

@ -106,7 +106,9 @@ begin
end process mem_do_reg;
dbus_req_o.src <= '0'; -- 0 = data access
dbus_req_o.fence <= ctrl_i.lsu_fence; -- this is valid without STB being set
dbus_req_o.fence <= ctrl_i.lsu_fence; -- out-of-band: this is valid without STB being set
dbus_req_o.sleep <= ctrl_i.cpu_sleep; -- out-of-band: this is valid without STB being set
dbus_req_o.debug <= ctrl_i.cpu_debug; -- out-of-band: this is valid without STB being set
-- Data Input: Alignment and Sign-Extension -----------------------------------------------
@ -121,14 +123,10 @@ begin
case ctrl_i.ir_funct3(1 downto 0) is
when "00" => -- byte
case mar(1 downto 0) is
when "00" => -- byte 0
rdata_o <= replicate_f((not ctrl_i.ir_funct3(2)) and dbus_rsp_i.data(7), 24) & dbus_rsp_i.data(7 downto 0);
when "01" => -- byte 1
rdata_o <= replicate_f((not ctrl_i.ir_funct3(2)) and dbus_rsp_i.data(15), 24) & dbus_rsp_i.data(15 downto 8);
when "10" => -- byte 2
rdata_o <= replicate_f((not ctrl_i.ir_funct3(2)) and dbus_rsp_i.data(23), 24) & dbus_rsp_i.data(23 downto 16);
when others => -- byte 3
rdata_o <= replicate_f((not ctrl_i.ir_funct3(2)) and dbus_rsp_i.data(31), 24) & dbus_rsp_i.data(31 downto 24);
when "00" => rdata_o <= replicate_f((not ctrl_i.ir_funct3(2)) and dbus_rsp_i.data(7), 24) & dbus_rsp_i.data(7 downto 0);
when "01" => rdata_o <= replicate_f((not ctrl_i.ir_funct3(2)) and dbus_rsp_i.data(15), 24) & dbus_rsp_i.data(15 downto 8);
when "10" => rdata_o <= replicate_f((not ctrl_i.ir_funct3(2)) and dbus_rsp_i.data(23), 24) & dbus_rsp_i.data(23 downto 16);
when others => rdata_o <= replicate_f((not ctrl_i.ir_funct3(2)) and dbus_rsp_i.data(31), 24) & dbus_rsp_i.data(31 downto 24);
end case;
when "01" => -- half-word
if (mar(1) = '0') then -- low half-word

View file

@ -3,6 +3,8 @@
-- -------------------------------------------------------------------------------- --
-- Compatible to the RISC-V PMP privilege architecture specifications. Granularity --
-- and supported modes can be constrained via generics to reduce area requirements. --
-- This PMP module uses a "time multiplex" architecture to check instruction fetch --
-- and load/store requests in a serial way to minimize area requirements. --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
@ -38,16 +40,15 @@ entity neorv32_cpu_pmp is
-- address input --
addr_if_i : in std_ulogic_vector(XLEN-1 downto 0); -- instruction fetch address
addr_ls_i : in std_ulogic_vector(XLEN-1 downto 0); -- load/store address
-- faults --
fault_ex_o : out std_ulogic; -- instruction fetch fault
fault_rw_o : out std_ulogic -- read/write access fault
-- access error --
fault_o : out std_ulogic -- permission violation
);
end neorv32_cpu_pmp;
architecture neorv32_cpu_pmp_rtl of neorv32_cpu_pmp is
-- auto-configuration --
constant granularity_c : natural := cond_sel_natural_f(boolean(GRANULARITY < 4), 4, 2**index_size_f(GRANULARITY));
constant g_c : natural := cond_sel_natural_f(boolean(GRANULARITY < 4), 4, 2**index_size_f(GRANULARITY));
-- configuration register bits --
constant cfg_r_c : natural := 0; -- read permit
@ -55,124 +56,132 @@ architecture neorv32_cpu_pmp_rtl of neorv32_cpu_pmp is
constant cfg_x_c : natural := 2; -- execute permit
constant cfg_al_c : natural := 3; -- mode bit low
constant cfg_ah_c : natural := 4; -- mode bit high
constant cfg_rl_c : natural := 5; -- reserved
constant cfg_rh_c : natural := 6; -- reserved
constant cfg_l_c : natural := 7; -- locked entry
-- operation modes --
constant mode_off_c : std_ulogic_vector(1 downto 0) := "00"; -- null region (disabled)
constant mode_tor_c : std_ulogic_vector(1 downto 0) := "01"; -- top of range
constant mode_na4_c : std_ulogic_vector(1 downto 0) := "10"; -- naturally aligned four-byte region
constant mode_napot_c : std_ulogic_vector(1 downto 0) := "11"; -- naturally aligned power-of-two region (>= 8 bytes)
constant mode_napot_c : std_ulogic_vector(1 downto 0) := "11"; -- naturally aligned power-of-two region (> 4 bytes)
-- address LSB according to granularity --
constant pmp_lsb_c : natural := index_size_f(granularity_c); -- min = 2
constant pmp_lsb_c : natural := index_size_f(g_c); -- min = 2
-- CSRs --
type csr_cfg_t is array (0 to NUM_REGIONS-1) of std_ulogic_vector(7 downto 0);
type csr_addr_t is array (0 to NUM_REGIONS-1) of std_ulogic_vector(XLEN-1 downto 0);
-- configuration CSRs --
type pmpcfg_t is array (0 to NUM_REGIONS-1) of std_ulogic_vector(7 downto 0);
signal pmpcfg : pmpcfg_t;
signal pmpcfg_we : std_ulogic_vector(3 downto 0);
-- address CSRs --
type pmpaddr_t is array (0 to NUM_REGIONS-1) of std_ulogic_vector(XLEN-1 downto 0);
signal pmpaddr : pmpaddr_t;
signal pmpaddr_we : std_ulogic_vector(15 downto 0);
-- CSR read-back --
type csr_cfg_rd_t is array (0 to 15) of std_ulogic_vector(7 downto 0);
type csr_cfg_rd32_t is array (0 to 03) of std_ulogic_vector(XLEN-1 downto 0);
type csr_addr_rd_t is array (0 to 15) of std_ulogic_vector(XLEN-1 downto 0);
type csr_t is record
we_cfg : std_ulogic_vector(3 downto 0);
we_addr : std_ulogic_vector(15 downto 0);
cfg : csr_cfg_t;
addr : csr_addr_t;
end record;
signal csr : csr_t;
signal cfg_rd : csr_cfg_rd_t;
signal cfg_rd32 : csr_cfg_rd32_t;
signal addr_rd : csr_addr_rd_t;
-- extended address (34-bit) --
type xaddr_t is array (0 to NUM_REGIONS-1) of std_ulogic_vector(XLEN+1 downto 0);
signal xaddr : xaddr_t;
-- CPU access --
signal acc_addr : std_ulogic_vector(XLEN-1 downto 0);
signal acc_priv : std_ulogic;
-- region access logic --
-- address mask (NA$/NAPOT) --
type addr_mask_t is array (0 to NUM_REGIONS-1) of std_ulogic_vector(XLEN-1 downto pmp_lsb_c);
signal addr_mask_napot, addr_mask : addr_mask_t;
type region_t is record
i_cmp_mm, d_cmp_mm : std_ulogic_vector(NUM_REGIONS-1 downto 0); -- masked match
i_cmp_ge, d_cmp_ge : std_ulogic_vector(NUM_REGIONS-1 downto 0); -- greater or equal
i_cmp_lt, d_cmp_lt : std_ulogic_vector(NUM_REGIONS-1 downto 0); -- less than
i_match, d_match : std_ulogic_vector(NUM_REGIONS-1 downto 0); -- region address match
perm_ex, perm_rw : std_ulogic_vector(NUM_REGIONS-1 downto 0); -- region's permission
end record;
signal region : region_t;
-- permission check violation --
signal fail_ex, fail_rw : std_ulogic_vector(NUM_REGIONS downto 0);
-- comparators --
signal cmp_na, cmp_ge, cmp_lt : std_ulogic_vector(NUM_REGIONS-1 downto 0);
-- region access logic --
signal match : std_ulogic_vector(NUM_REGIONS-1 downto 0); -- region address match
signal allow : std_ulogic_vector(NUM_REGIONS-1 downto 0); -- access allowed (permission OK)
signal fail : std_ulogic_vector(NUM_REGIONS downto 0); -- access failed (prioritized)
begin
-- Sanity Checks --------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
assert (GRANULARITY = granularity_c) report
assert (GRANULARITY = g_c) report
"[NEORV32] Auto-adjusting invalid PMP granularity configuration." severity warning;
-- CSR Write Access -----------------------------------------------------------------------
-- CSR Write Access: Configuration (PMPCFG) -----------------------------------------------
-- -------------------------------------------------------------------------------------------
csr_we: process(csr_we_i, csr_addr_i) -- write enable decoder
csr_we_cfg: process(csr_we_i, csr_addr_i) -- write enable decoder
begin
-- Configuration registers --
csr.we_cfg <= (others => '0');
pmpcfg_we <= (others => '0');
if (csr_addr_i(11 downto 2) = csr_pmpcfg0_c(11 downto 2)) and (csr_we_i = '1') then
csr.we_cfg(to_integer(unsigned(csr_addr_i(1 downto 0)))) <= '1';
pmpcfg_we(to_integer(unsigned(csr_addr_i(1 downto 0)))) <= '1';
end if;
-- Address registers --
csr.we_addr <= (others => '0');
if (csr_addr_i(11 downto 4) = csr_pmpaddr0_c(11 downto 4)) and (csr_we_i = '1') then
csr.we_addr(to_integer(unsigned(csr_addr_i(3 downto 0)))) <= '1';
end if;
end process csr_we;
end process csr_we_cfg;
-- PMP CSR registers --
csr_reg_gen:
-- CSRs --
csr_pmpcfg_gen:
for i in 0 to NUM_REGIONS-1 generate
csr_reg: process(rstn_i, clk_i)
csr_pmpcfg: process(rstn_i, clk_i)
variable mode_v : std_ulogic_vector(1 downto 0);
begin
if (rstn_i = '0') then
csr.cfg(i) <= (others => '0');
csr.addr(i) <= (others => '0');
pmpcfg(i) <= (others => '0');
elsif rising_edge(clk_i) then
-- configuration --
if (csr.we_cfg(i/4) = '1') and (csr.cfg(i)(cfg_l_c) = '0') then -- unlocked write access
csr.cfg(i)(cfg_r_c) <= csr_wdata_i((i mod 4)*8+cfg_r_c); -- R (read)
csr.cfg(i)(cfg_w_c) <= csr_wdata_i((i mod 4)*8+cfg_w_c); -- W (write)
csr.cfg(i)(cfg_x_c) <= csr_wdata_i((i mod 4)*8+cfg_x_c); -- X (execute)
-- A (mode) --
if (pmpcfg_we(i/4) = '1') and (pmpcfg(i)(cfg_l_c) = '0') then -- unlocked write access
-- permissions --
pmpcfg(i)(cfg_r_c) <= csr_wdata_i((i mod 4)*8+cfg_r_c); -- R (read)
pmpcfg(i)(cfg_w_c) <= csr_wdata_i((i mod 4)*8+cfg_w_c); -- W (write)
pmpcfg(i)(cfg_x_c) <= csr_wdata_i((i mod 4)*8+cfg_x_c); -- X (execute)
-- mode --
mode_v := csr_wdata_i((i mod 4)*8+cfg_ah_c downto (i mod 4)*8+cfg_al_c);
if ((mode_v = mode_tor_c) and (not TOR_EN)) or -- TOR mode not implemented
((mode_v = mode_na4_c) and (not NAP_EN)) or -- NA4 mode not implemented
((mode_v = mode_napot_c) and (not NAP_EN)) or -- NAPOT mode not implemented
((mode_v = mode_na4_c) and (granularity_c > 4)) then -- NA4 not available
csr.cfg(i)(cfg_ah_c downto cfg_al_c) <= mode_off_c;
((mode_v = mode_na4_c) and (g_c > 4)) then -- NA4 not available
pmpcfg(i)(cfg_ah_c downto cfg_al_c) <= mode_off_c;
else -- valid configuration
csr.cfg(i)(cfg_ah_c downto cfg_al_c) <= mode_v;
pmpcfg(i)(cfg_ah_c downto cfg_al_c) <= mode_v;
end if;
--
csr.cfg(i)(cfg_rl_c) <= '0'; -- reserved
csr.cfg(i)(cfg_rh_c) <= '0'; -- reserved
csr.cfg(i)(cfg_l_c) <= csr_wdata_i((i mod 4)*8+cfg_l_c); -- L (locked)
-- reserved --
pmpcfg(i)(6 downto 5) <= (others => '0');
-- locked --
pmpcfg(i)(cfg_l_c) <= csr_wdata_i((i mod 4)*8+cfg_l_c);
end if;
end if;
end process csr_pmpcfg;
end generate;
-- address --
if (csr.we_addr(i) = '1') and (csr.cfg(i)(cfg_l_c) = '0') then -- unlocked write access
-- CSR Write Access: Address (PMPADDR) ----------------------------------------------------
-- -------------------------------------------------------------------------------------------
csr_we_addr: process(csr_we_i, csr_addr_i) -- write enable decoder
begin
pmpaddr_we <= (others => '0');
if (csr_addr_i(11 downto 4) = csr_pmpaddr0_c(11 downto 4)) and (csr_we_i = '1') then
pmpaddr_we(to_integer(unsigned(csr_addr_i(3 downto 0)))) <= '1';
end if;
end process csr_we_addr;
-- CSRs --
csr_pmpaddr_gen:
for i in 0 to NUM_REGIONS-1 generate
csr_pmpaddr: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
pmpaddr(i) <= (others => '0');
elsif rising_edge(clk_i) then
if (pmpaddr_we(i) = '1') and (pmpcfg(i)(cfg_l_c) = '0') then -- unlocked write access
if (i < NUM_REGIONS-1) then
if (csr.cfg(i+1)(cfg_l_c) = '0') or (csr.cfg(i+1)(cfg_ah_c downto cfg_al_c) /= mode_tor_c) then -- cfg(i+1) not "LOCKED TOR"
csr.addr(i) <= "00" & csr_wdata_i(XLEN-3 downto 0);
if (pmpcfg(i+1)(cfg_l_c) = '0') or (pmpcfg(i+1)(cfg_ah_c downto cfg_al_c) /= mode_tor_c) then -- pmpcfg(i+1) not "LOCKED TOR"
pmpaddr(i) <= "00" & csr_wdata_i(XLEN-3 downto 0);
end if;
else -- very last entry
csr.addr(i) <= "00" & csr_wdata_i(XLEN-3 downto 0);
pmpaddr(i) <= "00" & csr_wdata_i(XLEN-3 downto 0);
end if;
end if;
end if;
end process csr_reg;
end process csr_pmpaddr;
end generate;
@ -195,22 +204,22 @@ begin
csr_read_back_gen:
for i in 0 to NUM_REGIONS-1 generate
-- configuration --
cfg_rd(i) <= csr.cfg(i);
cfg_rd(i) <= pmpcfg(i);
-- address --
address_read_back: process(csr)
address_read_back: process(pmpaddr, pmpcfg)
begin
addr_rd(i) <= (others => '0');
addr_rd(i)(XLEN-1 downto pmp_lsb_c-2) <= csr.addr(i)(XLEN-1 downto pmp_lsb_c-2);
if (granularity_c = 8) and TOR_EN then -- bit G-1 reads as zero in TOR or OFF mode
if (csr.cfg(i)(cfg_ah_c) = '0') then -- TOR/OFF mode
addr_rd(i)(XLEN-3 downto pmp_lsb_c-2) <= pmpaddr(i)(XLEN-3 downto pmp_lsb_c-2);
if (g_c = 8) and TOR_EN then -- bit G-1 reads as zero in TOR or OFF mode
if (pmpcfg(i)(cfg_ah_c) = '0') then -- TOR/OFF mode
addr_rd(i)(pmp_lsb_c) <= '0';
end if;
elsif (granularity_c > 8) then
elsif (g_c > 8) then
if NAP_EN then
addr_rd(i)(pmp_lsb_c-2 downto 0) <= (others => '1'); -- in NAPOT mode bits G-2:0 must read as one
end if;
if TOR_EN then
if (csr.cfg(i)(cfg_ah_c) = '0') then -- TOR/OFF mode
if (pmpcfg(i)(cfg_ah_c) = '0') then -- TOR/OFF mode
addr_rd(i)(pmp_lsb_c-1 downto 0) <= (others => '0'); -- in TOR or OFF mode bits G-1:0 must read as zero
end if;
end if;
@ -232,23 +241,24 @@ begin
end generate;
-- Region Access Logic --------------------------------------------------------------------
-- Region Access and Permission Check Logic -----------------------------------------------
-- -------------------------------------------------------------------------------------------
-- access switch (check I/D in time-multiplex) --
acc_addr <= addr_if_i when (ctrl_i.lsu_mo_we = '0') else addr_ls_i;
acc_priv <= ctrl_i.cpu_priv when (ctrl_i.lsu_mo_we = '0') else ctrl_i.lsu_priv;
region_gen:
for r in 0 to NUM_REGIONS-1 generate
-- extend region addresses to 34-bit --
xaddr(r) <= csr.addr(r) & "00"; -- mask byte offset
-- naturally-aligned address mask --
-- NAPOT address mask --
nap_mode_enable:
if NAP_EN generate
-- compute address masks for NAPOT mode --
addr_mask_napot(r)(pmp_lsb_c) <= '0';
addr_mask_napot_gen:
for i in pmp_lsb_c+1 to XLEN-1 generate
addr_mask_napot(r)(i) <= addr_mask_napot(r)(i-1) or (not xaddr(r)(i-1));
addr_mask_napot(r)(i) <= addr_mask_napot(r)(i-1) or (not pmpaddr(r)(i-3));
end generate;
-- address mask select --
@ -257,95 +267,80 @@ begin
if (rstn_i = '0') then
addr_mask(r) <= (others => '0');
elsif rising_edge(clk_i) then
if (csr.cfg(r)(cfg_al_c) = '1') then -- NAPOT
if (pmpcfg(r)(cfg_al_c) = '1') then -- NAPOT
addr_mask(r) <= addr_mask_napot(r);
else -- NA4
addr_mask(r) <= (others => '1');
end if;
end if;
end process addr_masking;
end generate; -- /nap_mode_enable
-- NAPOT disabled --
nap_mode_disable:
if not NAP_EN generate
addr_mask_napot <= (others => (others => '0'));
addr_mask <= (others => (others => '0'));
end generate;
-- check region address match --
-- NA4 and NAPOT --
region.i_cmp_mm(r) <= '1' when ((addr_if_i(XLEN-1 downto pmp_lsb_c) and addr_mask(r)) = (xaddr(r)(XLEN-1 downto pmp_lsb_c) and addr_mask(r))) and NAP_EN else '0';
region.d_cmp_mm(r) <= '1' when ((addr_ls_i(XLEN-1 downto pmp_lsb_c) and addr_mask(r)) = (xaddr(r)(XLEN-1 downto pmp_lsb_c) and addr_mask(r))) and NAP_EN else '0';
cmp_na(r) <= '1' when ((acc_addr(XLEN-1 downto pmp_lsb_c) and addr_mask(r)) = (pmpaddr(r)(XLEN-3 downto pmp_lsb_c-2) and addr_mask(r))) and NAP_EN else '0';
-- TOR region 0 --
addr_match_r0_gen:
if (r = 0) generate -- first entry: use ZERO as base and current entry as bound
region.i_cmp_ge(r) <= '1' when TOR_EN else '0'; -- address is always greater than or equal to zero (and TOR mode enabled)
region.i_cmp_lt(r) <= '0'; -- unused
region.d_cmp_ge(r) <= '1' when TOR_EN else '0'; -- address is always greater than or equal to zero (and TOR mode enabled)
region.d_cmp_lt(r) <= '0'; -- unused
cmp_ge(r) <= '1' when TOR_EN else '0'; -- address is always greater than or equal to zero (and TOR mode enabled)
cmp_lt(r) <= '0'; -- unused
end generate;
-- TOR region any --
addr_match_rx_gen:
addr_match_rn_gen:
if (r > 0) generate -- use previous entry as base and current entry as bound
region.i_cmp_ge(r) <= '1' when (unsigned(addr_if_i(XLEN-1 downto pmp_lsb_c)) >= unsigned(xaddr(r-1)(XLEN-1 downto pmp_lsb_c))) and TOR_EN else '0';
region.i_cmp_lt(r) <= '1' when (unsigned(addr_if_i(XLEN-1 downto pmp_lsb_c)) < unsigned(xaddr(r )(XLEN-1 downto pmp_lsb_c))) and TOR_EN else '0';
region.d_cmp_ge(r) <= '1' when (unsigned(addr_ls_i(XLEN-1 downto pmp_lsb_c)) >= unsigned(xaddr(r-1)(XLEN-1 downto pmp_lsb_c))) and TOR_EN else '0';
region.d_cmp_lt(r) <= '1' when (unsigned(addr_ls_i(XLEN-1 downto pmp_lsb_c)) < unsigned(xaddr(r )(XLEN-1 downto pmp_lsb_c))) and TOR_EN else '0';
cmp_ge(r) <= '1' when (unsigned(acc_addr(XLEN-1 downto pmp_lsb_c)) >= unsigned(pmpaddr(r-1)(XLEN-3 downto pmp_lsb_c-2))) and TOR_EN else '0';
cmp_lt(r) <= '1' when (unsigned(acc_addr(XLEN-1 downto pmp_lsb_c)) < unsigned(pmpaddr(r )(XLEN-3 downto pmp_lsb_c-2))) and TOR_EN else '0';
end generate;
-- check region match according to configured mode --
match_gen: process(csr, region)
variable tmp_v : std_ulogic_vector(1 downto 0);
match_gen: process(pmpcfg, cmp_ge, cmp_lt, cmp_na)
begin
tmp_v := csr.cfg(r)(cfg_ah_c downto cfg_al_c);
case tmp_v is -- VHDL/GHDL issue: "object type is not locally static"
when mode_off_c => -- entry disabled
region.i_match(r) <= '0';
region.d_match(r) <= '0';
when mode_tor_c => -- top of region
if TOR_EN then -- TOR mode implemented?
if (r = (NUM_REGIONS-1)) then -- very last entry
region.i_match(r) <= region.i_cmp_ge(r) and region.i_cmp_lt(r);
region.d_match(r) <= region.d_cmp_ge(r) and region.d_cmp_lt(r);
else -- this saves a LOT of comparators
region.i_match(r) <= region.i_cmp_ge(r) and (not region.i_cmp_ge(r+1));
region.d_match(r) <= region.d_cmp_ge(r) and (not region.d_cmp_ge(r+1));
end if;
else
region.i_match(r) <= '0';
region.d_match(r) <= '0';
end if;
when others => -- naturally-aligned region
if NAP_EN then -- NAPOT/NA4 modes implemented?
region.i_match(r) <= region.i_cmp_mm(r);
region.d_match(r) <= region.d_cmp_mm(r);
else
region.i_match(r) <= '0';
region.d_match(r) <= '0';
end if;
end case;
if (pmpcfg(r)(cfg_ah_c downto cfg_al_c) = mode_tor_c) and TOR_EN then -- TOR
if (r = (NUM_REGIONS-1)) then -- very last region
match(r) <= cmp_ge(r) and cmp_lt(r);
else -- any other region
match(r) <= cmp_ge(r) and (not cmp_ge(r+1)); -- this saves a LOT of comparators
end if;
elsif (pmpcfg(r)(cfg_ah_c) = mode_napot_c(1)) and NAP_EN then -- NA4/NAPOT
match(r) <= cmp_na(r);
else -- OFF / mode not supported
match(r) <= '0';
end if;
end process match_gen;
-- compute region permissions --
perm_gen: process(csr.cfg, ctrl_i)
-- select region permission --
perm_gen: process(ctrl_i, acc_priv, pmpcfg)
begin
-- execute (X) --
if (ctrl_i.cpu_priv = priv_mode_m_c) then
region.perm_ex(r) <= csr.cfg(r)(cfg_x_c) or (not csr.cfg(r)(cfg_l_c)); -- M mode: always allow if not locked
else
region.perm_ex(r) <= csr.cfg(r)(cfg_x_c);
end if;
-- read (R) --
if (ctrl_i.lsu_rw = '0') then
if (ctrl_i.lsu_priv = priv_mode_m_c) then
region.perm_rw(r) <= csr.cfg(r)(cfg_r_c) or (not csr.cfg(r)(cfg_l_c)); -- M mode: always allow if not locked
if (ctrl_i.lsu_mo_we = '0') then
if (acc_priv = priv_mode_m_c) then
allow(r) <= pmpcfg(r)(cfg_x_c) or (not pmpcfg(r)(cfg_l_c)); -- M mode: always allow if not locked
else
region.perm_rw(r) <= csr.cfg(r)(cfg_r_c);
allow(r) <= pmpcfg(r)(cfg_x_c);
end if;
-- read (R) --
elsif (ctrl_i.lsu_rw = '0') then
if (acc_priv = priv_mode_m_c) then
allow(r) <= pmpcfg(r)(cfg_r_c) or (not pmpcfg(r)(cfg_l_c)); -- M mode: always allow if not locked
else
allow(r) <= pmpcfg(r)(cfg_r_c);
end if;
-- write (W) --
else
if (ctrl_i.lsu_priv = priv_mode_m_c) then
region.perm_rw(r) <= csr.cfg(r)(cfg_w_c) or (not csr.cfg(r)(cfg_l_c)); -- M mode: always allow if not locked
if (acc_priv = priv_mode_m_c) then
allow(r) <= pmpcfg(r)(cfg_w_c) or (not pmpcfg(r)(cfg_l_c)); -- M mode: always allow if not locked
else
region.perm_rw(r) <= csr.cfg(r)(cfg_w_c);
allow(r) <= pmpcfg(r)(cfg_w_c);
end if;
end if;
end process perm_gen;
@ -353,30 +348,25 @@ begin
end generate;
-- Access Permission Check ----------------------------------------------------------------
-- Access Permission Check (using static prioritization) ----------------------------------
-- -------------------------------------------------------------------------------------------
-- check for access fault (using static prioritization) --
fail_ex(NUM_REGIONS) <= '1' when (ctrl_i.cpu_priv /= priv_mode_m_c) else '0'; -- default (if not match): fault if not M-mode
fail_rw(NUM_REGIONS) <= '1' when (ctrl_i.lsu_priv /= priv_mode_m_c) else '0'; -- default (if not match): fault if not M-mode
-- this is a *structural* description of a prioritization logic implemented as a multiplexer chain --
fail(NUM_REGIONS) <= '1' when (acc_priv /= priv_mode_m_c) else '0'; -- default (if not match): fault if not M-mode
fault_check_gen:
for r in NUM_REGIONS-1 downto 0 generate -- start with lowest priority
fail_ex(r) <= not region.perm_ex(r) when (region.i_match(r) = '1') else fail_ex(r+1);
fail_rw(r) <= not region.perm_rw(r) when (region.d_match(r) = '1') else fail_rw(r+1);
fail(r) <= not allow(r) when (match(r) = '1') else fail(r+1);
end generate;
-- final access check --
access_check: process(rstn_i, clk_i)
-- output buffer --
fault_check: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
fault_ex_o <= '0';
fault_rw_o <= '0';
fault_o <= '0';
elsif rising_edge(clk_i) then
fault_ex_o <= (not ctrl_i.cpu_debug) and fail_ex(0); -- ignore PMP rules when in debug mode
fault_rw_o <= (not ctrl_i.cpu_debug) and fail_rw(0);
fault_o <= (not ctrl_i.cpu_debug) and fail(0); -- ignore PMP rules when in debug-mode
end if;
end process access_check;
end process fault_check;
end neorv32_cpu_pmp_rtl;

View file

@ -1,5 +1,5 @@
-- ================================================================================ --
-- NEORV32 SoC - RISC-V-Compatible Authentication Module for the On-Chip Debugger --
-- NEORV32 OCD - RISC-V-Compatible Authentication Module for the On-Chip Debugger --
-- -------------------------------------------------------------------------------- --
-- Note that this module (in its default state) just provides a very simple and --
-- UNSECURE authentication mechanism that is meant as an example to showcase the --
@ -39,7 +39,7 @@ end neorv32_debug_auth;
architecture neorv32_debug_auth_rtl of neorv32_debug_auth is
signal authenticated : std_ulogic;
signal authenticated_q : std_ulogic;
begin
@ -53,12 +53,12 @@ begin
dm_controller: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
authenticated <= '0';
authenticated_q <= '0';
elsif rising_edge(clk_i) then
if (enable_i = '0') then
authenticated <= '0'; -- clear authentication when disabled
authenticated_q <= '0'; -- clear authentication when disabled
elsif (we_i = '1') then
authenticated <= wdata_i(0); -- just write a 1 to authenticate
authenticated_q <= wdata_i(0); -- just write a "1" to authenticate
end if;
end if;
end process dm_controller;
@ -67,7 +67,7 @@ begin
busy_o <= '0'; -- this simple authenticator is always ready
-- authentication passed --
valid_o <= authenticated;
valid_o <= authenticated_q;
-- read data --
rdata_o <= (others => '0'); -- there is nothing to read here

View file

@ -1,7 +1,8 @@
-- ================================================================================ --
-- NEORV32 SoC - RISC-V-Compatible Debug Module (DM) --
-- NEORV32 OCD - RISC-V-Compatible Debug Module (DM) --
-- -------------------------------------------------------------------------------- --
-- Execution-based debugger compatible to the "Minimal RISC-V Debug Specification". --
-- The DM can support up to 4 harts in parallel. --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
@ -19,33 +20,32 @@ use neorv32.neorv32_package.all;
entity neorv32_debug_dm is
generic (
CPU_BASE_ADDR : std_ulogic_vector(31 downto 0); -- base address for the memory-mapped CPU interface registers
AUTHENTICATOR : boolean -- implement authentication module when true
NUM_HARTS : natural range 1 to 4 := 1; -- number of physical CPU cores
AUTHENTICATOR : boolean := false -- implement authentication module when true
);
port (
-- global control --
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active
cpu_debug_i : in std_ulogic; -- CPU is in debug mode
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active
-- debug module interface (DMI) --
dmi_req_i : in dmi_req_t; -- request
dmi_rsp_o : out dmi_rsp_t; -- response
dmi_req_i : in dmi_req_t; -- request
dmi_rsp_o : out dmi_rsp_t; -- response
-- CPU bus access --
bus_req_i : in bus_req_t; -- bus request
bus_rsp_o : out bus_rsp_t; -- bus response
bus_req_i : in bus_req_t; -- bus request
bus_rsp_o : out bus_rsp_t; -- bus response
-- CPU control --
cpu_ndmrstn_o : out std_ulogic; -- soc reset
cpu_halt_req_o : out std_ulogic -- request hart to halt (enter debug mode)
ndmrstn_o : out std_ulogic; -- soc reset
halt_req_o : out std_ulogic_vector(NUM_HARTS-1 downto 0) -- request hart to halt (enter debug mode)
);
end neorv32_debug_dm;
architecture neorv32_debug_dm_rtl of neorv32_debug_dm is
-- memory map --
constant dm_code_base_c : std_ulogic_vector(31 downto 0) := std_ulogic_vector(unsigned(CPU_BASE_ADDR) + x"00"); -- code ROM (park loop)
constant dm_pbuf_base_c : std_ulogic_vector(31 downto 0) := std_ulogic_vector(unsigned(CPU_BASE_ADDR) + x"40"); -- program buffer (PBUF)
constant dm_data_base_c : std_ulogic_vector(31 downto 0) := std_ulogic_vector(unsigned(CPU_BASE_ADDR) + x"80"); -- abstract data buffer (DATA)
constant dm_sreg_base_c : std_ulogic_vector(31 downto 0) := std_ulogic_vector(unsigned(CPU_BASE_ADDR) + x"C0"); -- status register (SREG)
-- memory map, 128 bytes per device; replicated throughout the entire device address space --
constant dm_code_base_c : std_ulogic_vector(31 downto 0) := x"fffffe00"; -- code ROM (park loop)
constant dm_pbuf_base_c : std_ulogic_vector(31 downto 0) := x"fffffe80"; -- program buffer (PBUF)
constant dm_data_base_c : std_ulogic_vector(31 downto 0) := x"ffffff00"; -- abstract data buffer (DATA)
constant dm_sreg_base_c : std_ulogic_vector(31 downto 0) := x"ffffff80"; -- status register(s) (SREG)
-- rv32i instruction prototypes --
constant instr_nop_c : std_ulogic_vector(31 downto 0) := x"00000013"; -- nop
@ -70,6 +70,7 @@ architecture neorv32_debug_dm_rtl of neorv32_debug_dm is
constant addr_progbuf1_c : std_ulogic_vector(6 downto 0) := "0100001";
constant addr_authdata_c : std_ulogic_vector(6 downto 0) := "0110000";
--constant addr_sbcs_c : std_ulogic_vector(6 downto 0) := "0111000"; -- hardwired to zero
constant addr_haltsum0_c : std_ulogic_vector(6 downto 0) := "1000000";
-- DMI access --
signal dmi_wren, dmi_wren_auth, dmi_rden, dmi_rden_auth : std_ulogic;
@ -85,8 +86,11 @@ architecture neorv32_debug_dm_rtl of neorv32_debug_dm is
command : std_ulogic_vector(31 downto 0);
--
halt_req : std_ulogic;
resume_req : std_ulogic;
req_res : std_ulogic;
reset_ack : std_ulogic;
hartsel : std_ulogic_vector(1+1 downto 0); -- plus one bit to detect "unavailable hart"
hartsel_dec : std_ulogic_vector(NUM_HARTS-1 downto 0);
hartsel_inv : std_ulogic; -- invalid/unavailable hart selection
wr_acc_err : std_ulogic;
rd_acc_err : std_ulogic;
clr_acc_err : std_ulogic;
@ -119,10 +123,10 @@ architecture neorv32_debug_dm_rtl of neorv32_debug_dm is
illegal_cmd : std_ulogic;
cmderr : std_ulogic_vector(2 downto 0);
-- hart status --
hart_halted : std_ulogic;
hart_resume_req : std_ulogic;
hart_resume_ack : std_ulogic;
hart_reset : std_ulogic;
hart_halted : std_ulogic_vector(NUM_HARTS-1 downto 0);
hart_resume_req : std_ulogic_vector(NUM_HARTS-1 downto 0);
hart_resume_ack : std_ulogic_vector(NUM_HARTS-1 downto 0);
hart_reset : std_ulogic_vector(NUM_HARTS-1 downto 0);
end record;
signal dm_ctrl : dm_ctrl_t;
@ -140,50 +144,60 @@ architecture neorv32_debug_dm_rtl of neorv32_debug_dm is
-- CPU Bus and Debug Interfaces
-- ----------------------------------------------------------
-- status and control register - bits --
-- for write access we only care about the actual BYTE WRITE ACCESSES! --
constant sreg_halt_ack_c : natural := 0; -- -/w: CPU is halted in debug mode and waits in park loop
constant sreg_resume_req_c : natural := 8; -- r/-: DM requests CPU to resume
constant sreg_resume_ack_c : natural := 8; -- -/w: CPU starts resuming
constant sreg_execute_req_c : natural := 16; -- r/-: DM requests to execute program buffer
constant sreg_execute_ack_c : natural := 16; -- -/w: CPU starts to execute program buffer
constant sreg_exception_ack_c : natural := 24; -- -/w: CPU has detected an exception
-- code ROM containing "park loop" --
-- copied manually from 'sw/ocd-firmware/neorv32_debug_mem_code.vhd' --
type code_rom_t is array (0 to 15) of std_ulogic_vector(31 downto 0);
constant code_rom : code_rom_t := (
00 => x"fc0001a3",
01 => x"00100073",
02 => x"7b241073",
03 => x"fc000023",
04 => x"fc204403",
05 => x"00041c63",
06 => x"fc104403",
07 => x"fe0408e3",
08 => x"fc0000a3",
09 => x"7b202473",
10 => x"7b200073",
11 => x"fc000123",
12 => x"7b202473",
13 => x"0000100f",
14 => x"f4000067",
15 => x"00000073"
-- copied manually from 'sw/ocd-firmware/neorv32_application_image.vhd' --
type code_rom_t is array (0 to 31) of std_ulogic_vector(31 downto 0);
constant code_rom_c : code_rom_t := (
00 => x"f8002623",
01 => x"7b202473",
02 => x"00100073",
03 => x"00000013",
04 => x"7b241073",
05 => x"f1402473",
06 => x"f8802023",
07 => x"f1402473",
08 => x"f8044403",
09 => x"00247413",
10 => x"02041263",
11 => x"f1402473",
12 => x"f8044403",
13 => x"00147413",
14 => x"fe0402e3",
15 => x"f1402473",
16 => x"f8802223",
17 => x"7b202473",
18 => x"7b200073",
19 => x"f1402473",
20 => x"f8802423",
21 => x"7b202473",
22 => x"0000100f",
23 => x"e8000067",
24 => x"00000073",
25 => x"00000073",
26 => x"00000073",
27 => x"00000073",
28 => x"00000073",
29 => x"00000073",
30 => x"00000073",
31 => x"00000073"
);
-- CPU access helpers --
signal accen, rden, wren : std_ulogic;
-- CPU response (hart ID) decoder --
signal cpu_rsp_dec : std_ulogic_vector(NUM_HARTS-1 downto 0);
-- Debug Core Interface --
type dci_t is record
halt_ack : std_ulogic; -- CPU (re-)entered HALT state (single-shot)
resume_req : std_ulogic; -- DM wants the CPU to resume when set
resume_ack : std_ulogic; -- CPU starts resuming when set (single-shot)
execute_req : std_ulogic; -- DM wants CPU to execute program buffer when set
execute_ack : std_ulogic; -- CPU starts executing program buffer when set (single-shot)
exception_ack : std_ulogic; -- CPU has detected an exception (single-shot)
data_we : std_ulogic; -- write abstract data
data_reg : std_ulogic_vector(31 downto 0); -- memory-mapped data exchange register
ack_hlt : std_ulogic_vector(NUM_HARTS-1 downto 0); -- CPU (re-)entered HALT state (single-shot)
req_res : std_ulogic_vector(NUM_HARTS-1 downto 0); -- DM wants the CPU to resume when set
ack_res : std_ulogic_vector(NUM_HARTS-1 downto 0); -- CPU starts resuming when set (single-shot)
req_exe : std_ulogic_vector(NUM_HARTS-1 downto 0); -- DM wants CPU to execute program buffer when set
ack_exe : std_ulogic_vector(NUM_HARTS-1 downto 0); -- CPU starts executing program buffer when set (single-shot)
ack_exc : std_ulogic; -- CPU has detected an exception (single-shot)
data_reg_we : std_ulogic; -- write abstract data
data_reg : std_ulogic_vector(31 downto 0); -- memory-mapped data exchange register
end record;
signal dci : dci_t;
@ -206,7 +220,7 @@ begin
if (rstn_i = '0') then
dm_ctrl.state <= CMD_IDLE;
dm_ctrl.ldsw_progbuf <= (others => '0');
dci.execute_req <= '0';
dci.req_exe <= (others => '0');
dm_ctrl.pbuf_en <= '0';
dm_ctrl.illegal_cmd <= '0';
dm_ctrl.illegal_state <= '0';
@ -215,7 +229,7 @@ begin
if (dm_reg.dmcontrol_dmactive = '0') then -- DM reset / DM disabled
dm_ctrl.state <= CMD_IDLE;
dm_ctrl.ldsw_progbuf <= instr_sw_c;
dci.execute_req <= '0';
dci.req_exe <= (others => '0');
dm_ctrl.pbuf_en <= '0';
dm_ctrl.illegal_cmd <= '0';
dm_ctrl.illegal_state <= '0';
@ -223,7 +237,7 @@ begin
else -- DM active
-- defaults --
dci.execute_req <= '0';
dci.req_exe <= (others => '0');
dm_ctrl.illegal_cmd <= '0';
dm_ctrl.illegal_state <= '0';
@ -249,7 +263,7 @@ begin
(dm_reg.command(22 downto 20) = "010") and -- aarsize: has to be 32-bit
(dm_reg.command(19) = '0') and -- aarpostincrement: not supported
((dm_reg.command(17) = '0') or (dm_reg.command(15 downto 5) = "00010000000")) then -- regno: only GPRs are supported: 0x1000..0x101f if transfer is set
if (dm_ctrl.hart_halted = '1') then -- CPU is halted
if (or_reduce_f(dm_ctrl.hart_halted and dm_reg.hartsel_dec) = '1') then -- selected CPU is halted
dm_ctrl.state <= CMD_PREPARE;
else -- error! CPU is still running
dm_ctrl.illegal_state <= '1';
@ -281,14 +295,14 @@ begin
when CMD_TRIGGER => -- request CPU to execute command
-- ------------------------------------------------------------
dci.execute_req <= '1'; -- request execution
if (dci.execute_ack = '1') then -- CPU starts execution
dci.req_exe <= dm_reg.hartsel_dec; -- request execution
if (or_reduce_f(dci.ack_exe and dm_reg.hartsel_dec) = '1') then -- selected CPU starts execution
dm_ctrl.state <= CMD_BUSY;
end if;
when CMD_BUSY => -- wait for CPU to finish
-- ------------------------------------------------------------
if (dci.halt_ack = '1') then -- CPU is parked (halted) again -> execution done
if (or_reduce_f(dci.ack_hlt and dm_reg.hartsel_dec) = '1') then -- selected CPU is parked (halted) again -> execution done
dm_ctrl.state <= CMD_IDLE;
end if;
@ -303,11 +317,10 @@ begin
end case;
-- error code --
-- ------------------------------------------------------------
if (dm_ctrl.cmderr = "000") then -- ready to set new error
if (dm_ctrl.illegal_state = '1') then -- cannot execute since hart is not in expected state
dm_ctrl.cmderr <= "100";
elsif (dci.exception_ack = '1') then -- exception during execution
elsif (dci.ack_exc = '1') then -- exception during execution (can only be caused by the currently selected hart)
dm_ctrl.cmderr <= "011";
elsif (dm_ctrl.illegal_cmd = '1') then -- unsupported command
dm_ctrl.cmderr <= "010";
@ -331,41 +344,48 @@ begin
hart_status: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
dm_ctrl.hart_halted <= '0';
dm_ctrl.hart_resume_req <= '0';
dm_ctrl.hart_resume_ack <= '0';
dm_ctrl.hart_reset <= '0';
dm_ctrl.hart_halted <= (others => '0');
dm_ctrl.hart_resume_req <= (others => '0');
dm_ctrl.hart_resume_ack <= (others => '0');
dm_ctrl.hart_reset <= (others => '0');
elsif rising_edge(clk_i) then
-- halted ACK --
if (dm_reg.dmcontrol_ndmreset = '1') then
dm_ctrl.hart_halted <= '0';
elsif (dci.halt_ack = '1') then
dm_ctrl.hart_halted <= '1';
elsif (dci.resume_ack = '1') then
dm_ctrl.hart_halted <= '0';
end if;
-- resume REQ --
if (dm_reg.dmcontrol_ndmreset = '1') then
dm_ctrl.hart_resume_req <= '0';
elsif (dm_reg.resume_req = '1') then
dm_ctrl.hart_resume_req <= '1';
elsif (dci.resume_ack = '1') then
dm_ctrl.hart_resume_req <= '0';
end if;
-- resume ACK --
if (dm_reg.dmcontrol_ndmreset = '1') then
dm_ctrl.hart_resume_ack <= '0';
elsif (dci.resume_ack = '1') then
dm_ctrl.hart_resume_ack <= '1';
elsif (dm_reg.resume_req = '1') then
dm_ctrl.hart_resume_ack <= '0';
end if;
-- reset ACK --
if (dm_reg.dmcontrol_ndmreset = '1') then -- explicit RESET triggered by DM
dm_ctrl.hart_reset <= '1';
elsif (dm_reg.reset_ack = '1') then
dm_ctrl.hart_reset <= '0';
end if;
for i in 0 to NUM_HARTS-1 loop
-- halted ACK --
if (dm_reg.dmcontrol_ndmreset = '1') then -- DM reset
dm_ctrl.hart_halted(i) <= '0';
elsif (dci.ack_hlt(i) = '1') then
dm_ctrl.hart_halted(i) <= '1';
elsif (dci.ack_res(i) = '1') then
dm_ctrl.hart_halted(i) <= '0';
end if;
-- resume REQ --
if (dm_reg.dmcontrol_ndmreset = '1') then -- DM reset
dm_ctrl.hart_resume_req(i) <= '0';
elsif (dm_reg.req_res = '1') and (dm_reg.hartsel_dec(i) = '1') then
dm_ctrl.hart_resume_req(i) <= '1';
elsif (dci.ack_res(i) = '1') then
dm_ctrl.hart_resume_req(i) <= '0';
end if;
-- resume ACK --
if (dm_reg.dmcontrol_ndmreset = '1') then -- DM reset
dm_ctrl.hart_resume_ack(i) <= '0';
elsif (dci.ack_res(i) = '1') then
dm_ctrl.hart_resume_ack(i) <= '1';
elsif (dm_reg.req_res = '1') and (dm_reg.hartsel_dec(i) = '1') then
dm_ctrl.hart_resume_ack(i) <= '0';
end if;
-- reset ACK --
if (dm_reg.dmcontrol_ndmreset = '1') then -- DM reset
dm_ctrl.hart_reset(i) <= '1';
elsif (dm_reg.reset_ack = '1') and (dm_reg.hartsel_dec(i) = '1') then
dm_ctrl.hart_reset(i) <= '0';
end if;
end loop;
end if;
end process hart_status;
@ -385,15 +405,16 @@ begin
dm_reg.progbuf <= (others => instr_nop_c);
--
dm_reg.halt_req <= '0';
dm_reg.resume_req <= '0';
dm_reg.req_res <= '0';
dm_reg.reset_ack <= '0';
dm_reg.hartsel <= (others => '0');
dm_reg.wr_acc_err <= '0';
dm_reg.clr_acc_err <= '0';
dm_reg.autoexec_wr <= '0';
elsif rising_edge(clk_i) then
-- default --
dm_reg.resume_req <= '0';
dm_reg.req_res <= '0';
dm_reg.reset_ack <= '0';
dm_reg.wr_acc_err <= '0';
dm_reg.clr_acc_err <= '0';
@ -403,8 +424,9 @@ begin
if (dmi_req_i.addr = addr_dmcontrol_c) then
if (dmi_wren_auth = '1') then -- valid and authenticated DM write access
dm_reg.halt_req <= dmi_req_i.data(31); -- haltreq (-/w): write 1 to request halt; has to be cleared again by debugger
dm_reg.resume_req <= dmi_req_i.data(30); -- resumereq (-/w1): write 1 to request resume; auto-clears
dm_reg.req_res <= dmi_req_i.data(30); -- resumereq (-/w1): write 1 to request resume; auto-clears
dm_reg.reset_ack <= dmi_req_i.data(28); -- ackhavereset (-/w1): write 1 to ACK reset; auto-clears
dm_reg.hartsel <= dmi_req_i.data(18 downto 16); -- hartsello (r/w): up to 4 harts are supported (plus 1 bit to detect unavailable)
dm_reg.dmcontrol_ndmreset <= dmi_req_i.data(1); -- ndmreset (r/w): SoC reset when high
end if;
if (dmi_wren = '1') then -- valid DM write access (may be unauthenticated)
@ -459,18 +481,28 @@ begin
end if;
end process dmi_write_access;
-- hat select decoder (one-hot) --
hartsel_decode:
for i in 0 to NUM_HARTS-1 generate
dm_reg.hartsel_dec(i) <= '1' when (dm_reg.hartsel(2) = '0') and (dm_reg.hartsel(1 downto 0) = std_ulogic_vector(to_unsigned(i, 2))) else '0';
end generate;
dm_reg.hartsel_inv <= '0' when (unsigned(dm_reg.hartsel) < NUM_HARTS) else '1'; -- invalid/unavailable hart selection
-- Direct Control -------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
-- write to abstract data register --
dci.data_we <= '1' when (dmi_wren_auth = '1') and (dmi_req_i.addr = addr_data0_c) and (dm_ctrl.busy = '0') else '0';
dci.data_reg_we <= '1' when (dmi_wren_auth = '1') and (dmi_req_i.addr = addr_data0_c) and (dm_ctrl.busy = '0') else '0';
-- CPU halt/resume request --
cpu_halt_req_o <= dm_reg.halt_req and dm_reg.dmcontrol_dmactive when ((not AUTHENTICATOR) or (auth.valid = '1')) else '0';
dci.resume_req <= dm_ctrl.hart_resume_req; -- active until explicitly cleared
-- CPU halt/resume requests --
request_gen:
for i in 0 to NUM_HARTS-1 generate
halt_req_o(i) <= dm_reg.halt_req and dm_reg.hartsel_dec(i) and dm_reg.dmcontrol_dmactive when ((not AUTHENTICATOR) or (auth.valid = '1')) else '0';
dci.req_res(i) <= dm_ctrl.hart_resume_req(i); -- active until explicitly cleared
end generate;
-- SoC reset --
cpu_ndmrstn_o <= '0' when (dm_reg.dmcontrol_ndmreset = '1') and (dm_reg.dmcontrol_dmactive = '1') and ((not AUTHENTICATOR) or (auth.valid = '1')) else '1';
ndmrstn_o <= '0' when (dm_reg.dmcontrol_ndmreset = '1') and (dm_reg.dmcontrol_dmactive = '1') and ((not AUTHENTICATOR) or (auth.valid = '1')) else '1';
-- construct program buffer array for CPU access --
cpu_progbuf(0) <= dm_ctrl.ldsw_progbuf; -- pseudo program buffer for GPR<->DM.data0 transfer
@ -496,23 +528,23 @@ begin
-- debug module status register --
when addr_dmstatus_c =>
if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
dmi_rsp_o.data(31 downto 23) <= (others => '0'); -- reserved (r/-)
dmi_rsp_o.data(22) <= '1'; -- impebreak (r/-): there is an implicit ebreak instruction after the visible program buffer
dmi_rsp_o.data(21 downto 20) <= (others => '0'); -- reserved (r/-)
dmi_rsp_o.data(19) <= dm_ctrl.hart_reset; -- allhavereset (r/-): there is only one hart that can be reset
dmi_rsp_o.data(18) <= dm_ctrl.hart_reset; -- anyhavereset (r/-): there is only one hart that can be reset
dmi_rsp_o.data(17) <= dm_ctrl.hart_resume_ack; -- allresumeack (r/-): there is only one hart that can acknowledge resume request
dmi_rsp_o.data(16) <= dm_ctrl.hart_resume_ack; -- anyresumeack (r/-): there is only one hart that can acknowledge resume request
dmi_rsp_o.data(15) <= '0'; -- allnonexistent (r/-): there is only one hart that is always existent
dmi_rsp_o.data(14) <= '0'; -- anynonexistent (r/-): there is only one hart that is always existent
dmi_rsp_o.data(13) <= dm_reg.dmcontrol_ndmreset; -- allunavail (r/-): there is only one hart that is unavailable during reset
dmi_rsp_o.data(12) <= dm_reg.dmcontrol_ndmreset; -- anyunavail (r/-): there is only one hart that is unavailable during reset
dmi_rsp_o.data(11) <= not dm_ctrl.hart_halted; -- allrunning (r/-): there is only one hart that can be RUNNING or HALTED
dmi_rsp_o.data(10) <= not dm_ctrl.hart_halted; -- anyrunning (r/-): there is only one hart that can be RUNNING or HALTED
dmi_rsp_o.data(9) <= dm_ctrl.hart_halted; -- allhalted (r/-): there is only one hart that can be RUNNING or HALTED
dmi_rsp_o.data(8) <= dm_ctrl.hart_halted; -- anyhalted (r/-): there is only one hart that can be RUNNING or HALTED
dmi_rsp_o.data(5) <= '0'; -- hasresethaltreq (r/-): halt-on-reset not implemented
dmi_rsp_o.data(4) <= '0'; -- confstrptrvalid (r/-): no configuration string available
dmi_rsp_o.data(31 downto 23) <= (others => '0'); -- reserved (r/-)
dmi_rsp_o.data(22) <= '1'; -- impebreak (r/-): there is an implicit ebreak instruction after the visible program buffer
dmi_rsp_o.data(21 downto 20) <= (others => '0'); -- reserved (r/-)
dmi_rsp_o.data(19) <= or_reduce_f(dm_ctrl.hart_reset and dm_reg.hartsel_dec); -- allhavereset (r/-): selected hart in reset
dmi_rsp_o.data(18) <= or_reduce_f(dm_ctrl.hart_reset and dm_reg.hartsel_dec); -- anyhavereset (r/-): selected hart in reset
dmi_rsp_o.data(17) <= or_reduce_f(dm_ctrl.hart_resume_ack and dm_reg.hartsel_dec); -- allresumeack (r/-): selected hart is resuming
dmi_rsp_o.data(16) <= or_reduce_f(dm_ctrl.hart_resume_ack and dm_reg.hartsel_dec); -- anyresumeack (r/-): selected hart is resuming
dmi_rsp_o.data(15) <= dm_reg.hartsel_inv; -- allnonexistent (r/-): invalid hart selection
dmi_rsp_o.data(14) <= dm_reg.hartsel_inv; -- anynonexistent (r/-): invalid hart selection
dmi_rsp_o.data(13) <= dm_reg.dmcontrol_ndmreset; -- allunavail (r/-): DM in reset
dmi_rsp_o.data(12) <= dm_reg.dmcontrol_ndmreset; -- anyunavail (r/-): DM in reset
dmi_rsp_o.data(11) <= not or_reduce_f(dm_ctrl.hart_halted and dm_reg.hartsel_dec); -- allrunning (r/-): selected hart not halted
dmi_rsp_o.data(10) <= not or_reduce_f(dm_ctrl.hart_halted and dm_reg.hartsel_dec); -- anyrunning (r/-): selected hart not halted
dmi_rsp_o.data(9) <= or_reduce_f(dm_ctrl.hart_halted and dm_reg.hartsel_dec); -- allhalted (r/-): selected hart halted
dmi_rsp_o.data(8) <= or_reduce_f(dm_ctrl.hart_halted and dm_reg.hartsel_dec); -- anyhalted (r/-): selected hart halted
dmi_rsp_o.data(5) <= '0'; -- hasresethaltreq (r/-): halt-on-reset not implemented
dmi_rsp_o.data(4) <= '0'; -- confstrptrvalid (r/-): no configuration string available
end if;
dmi_rsp_o.data(7) <= auth.valid; -- authenticated (r/-): authentication successful when set
dmi_rsp_o.data(6) <= auth.busy; -- authbusy (r/-): wait for authenticator operation when set
@ -521,18 +553,18 @@ begin
-- debug module control --
when addr_dmcontrol_c =>
if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
dmi_rsp_o.data(31) <= '0'; -- haltreq (-/w): write-only
dmi_rsp_o.data(30) <= '0'; -- resumereq (-/w1): write-only
dmi_rsp_o.data(29) <= '0'; -- hartreset (r/w): not supported
dmi_rsp_o.data(28) <= '0'; -- ackhavereset (-/w1): write-only
dmi_rsp_o.data(27) <= '0'; -- reserved (r/-)
dmi_rsp_o.data(26) <= '0'; -- hasel (r/-) - only a single hart can be selected at once
dmi_rsp_o.data(25 downto 16) <= (others => '0'); -- hartsello (r/-) - there is only one hart
dmi_rsp_o.data(15 downto 6) <= (others => '0'); -- hartselhi (r/-) - there is only one hart
dmi_rsp_o.data(5 downto 4) <= (others => '0'); -- reserved (r/-)
dmi_rsp_o.data(3) <= '0'; -- setresethaltreq (-/w1): halt-on-reset request - halt-on-reset not implemented
dmi_rsp_o.data(2) <= '0'; -- clrresethaltreq (-/w1): halt-on-reset ack - halt-on-reset not implemented
dmi_rsp_o.data(1) <= dm_reg.dmcontrol_ndmreset; -- ndmreset (r/w): soc reset
dmi_rsp_o.data(31) <= '0'; -- haltreq (-/w): write-only
dmi_rsp_o.data(30) <= '0'; -- resumereq (-/w1): write-only
dmi_rsp_o.data(29) <= '0'; -- hartreset (r/w): not supported
dmi_rsp_o.data(28) <= '0'; -- ackhavereset (-/w1): write-only
dmi_rsp_o.data(27) <= '0'; -- reserved (r/-)
dmi_rsp_o.data(26) <= '0'; -- hasel (r/-) - only a single hart can be selected at once
dmi_rsp_o.data(25 downto 16) <= "0000000" & dm_reg.hartsel; -- hartsello (r/w) - only up to 4 harts are supported (plus 1 bit to detect unavailable)
dmi_rsp_o.data(15 downto 6) <= (others => '0'); -- hartselhi (r/-) - hardwired to zero; hartsello is sufficient
dmi_rsp_o.data(5 downto 4) <= (others => '0'); -- reserved (r/-)
dmi_rsp_o.data(3) <= '0'; -- setresethaltreq (-/w1): halt-on-reset request - halt-on-reset not implemented
dmi_rsp_o.data(2) <= '0'; -- clrresethaltreq (-/w1): halt-on-reset ack - halt-on-reset not implemented
dmi_rsp_o.data(1) <= dm_reg.dmcontrol_ndmreset; -- ndmreset (r/w): soc reset
end if;
dmi_rsp_o.data(0) <= dm_reg.dmcontrol_dmactive; -- dmactive (r/w): DM reset
@ -577,11 +609,11 @@ begin
when addr_authdata_c =>
dmi_rsp_o.data <= auth.rdata;
-- -- halt summary 0 (not required for DM spec. v1.0 if there is only a single hart) --
-- when "1000000" => -- haltsum0
-- if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
-- dmi_rsp_o.data(0) <= dm_ctrl.hart_halted; -- hart 0 is halted
-- end if;
-- halt summary 0 --
when addr_haltsum0_c => -- haltsum0
if (not AUTHENTICATOR) or (auth.valid = '1') then -- authenticated?
dmi_rsp_o.data(NUM_HARTS-1 downto 0) <= dm_ctrl.hart_halted(NUM_HARTS-1 downto 0); -- hart i is halted
end if;
-- not implemented or read-only-zero --
when others => -- addr_sbcs_c, addr_progbuf0_c, addr_progbuf1_c, addr_nextdm_c, addr_command_c
@ -618,58 +650,79 @@ begin
bus_access: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
bus_rsp_o <= rsp_terminate_c;
dci.data_reg <= (others => '0');
dci.halt_ack <= '0';
dci.resume_ack <= '0';
dci.execute_ack <= '0';
dci.exception_ack <= '0';
bus_rsp_o <= rsp_terminate_c;
dci.data_reg <= (others => '0');
dci.ack_hlt <= (others => '0');
dci.ack_res <= (others => '0');
dci.ack_exe <= (others => '0');
dci.ack_exc <= '0';
elsif rising_edge(clk_i) then
-- bus handshake --
bus_rsp_o.ack <= accen;
bus_rsp_o.err <= '0';
bus_rsp_o.data <= (others => '0');
bus_rsp_o.ack <= accen;
bus_rsp_o.err <= '0';
-- data buffer --
if (dci.data_we = '1') then -- DM write access
-- data buffer access --
if (dci.data_reg_we = '1') then -- DM write access
dci.data_reg <= dmi_req_i.data;
elsif (bus_req_i.addr(7 downto 6) = dm_data_base_c(7 downto 6)) and (wren = '1') then -- CPU write access
elsif (wren = '1') and (bus_req_i.addr(8 downto 7) = dm_data_base_c(8 downto 7)) then -- CPU write access
dci.data_reg <= bus_req_i.data;
end if;
-- control and status register CPU write access --
dci.halt_ack <= '0'; -- all writable flags auto-clear
dci.resume_ack <= '0';
dci.execute_ack <= '0';
dci.exception_ack <= '0';
if (bus_req_i.addr(7 downto 6) = dm_sreg_base_c(7 downto 6)) and (wren = '1') then
dci.halt_ack <= bus_req_i.ben(sreg_halt_ack_c/8); -- [NOTE] use individual BYTE ENABLES and not the actual write data
dci.resume_ack <= bus_req_i.ben(sreg_resume_ack_c/8);
dci.execute_ack <= bus_req_i.ben(sreg_execute_ack_c/8);
dci.exception_ack <= bus_req_i.ben(sreg_exception_ack_c/8);
-- CPU status register write access --
dci.ack_hlt <= (others => '0'); -- all writable flags auto-clear
dci.ack_res <= (others => '0');
dci.ack_exe <= (others => '0');
dci.ack_exc <= '0';
if (wren = '1') and (bus_req_i.addr(8 downto 7) = dm_sreg_base_c(8 downto 7)) then
for i in 0 to NUM_HARTS-1 loop
case bus_req_i.addr(3 downto 2) is
when "00" => dci.ack_hlt(i) <= cpu_rsp_dec(i); -- CPU is HALTED in debug mode and waits in park loop
when "01" => dci.ack_res(i) <= cpu_rsp_dec(i); -- CPU starts RESUMING
when "10" => dci.ack_exe(i) <= cpu_rsp_dec(i); -- CPU starts to EXECUTE program buffer
when others => dci.ack_exc <= '1'; -- CPU has detected an EXCEPTION (can only be caused by the currently selected hart)
end case;
end loop;
end if;
-- control and status register CPU read access --
-- CPU read access --
bus_rsp_o.data <= (others => '0'); -- default
if (rden = '1') then -- output enable
case bus_req_i.addr(7 downto 6) is -- module select
case bus_req_i.addr(8 downto 7) is -- module select
when "00" => -- dm_code_base_c: code ROM
bus_rsp_o.data <= code_rom(to_integer(unsigned(bus_req_i.addr(5 downto 2))));
bus_rsp_o.data <= code_rom_c(to_integer(unsigned(bus_req_i.addr(6 downto 2))));
when "01" => -- dm_pbuf_base_c: program buffer
bus_rsp_o.data <= cpu_progbuf(to_integer(unsigned(bus_req_i.addr(3 downto 2))));
when "10" => -- dm_data_base_c: data buffer
bus_rsp_o.data <= dci.data_reg;
when others => -- dm_sreg_base_c: control and status register
bus_rsp_o.data(sreg_resume_req_c) <= dci.resume_req;
bus_rsp_o.data(sreg_execute_req_c) <= dci.execute_req;
when others => -- dm_sreg_base_c: request register
for i in 0 to NUM_HARTS-1 loop
bus_rsp_o.data(i*8+0) <= dci.req_res(i); -- DM requests CPU to resume
bus_rsp_o.data(i*8+1) <= dci.req_exe(i); -- DM requests CPU to execute program buffer
end loop;
end case;
end if;
end if;
end process bus_access;
-- access helpers --
accen <= cpu_debug_i and bus_req_i.stb; -- allow access only when in debug-mode
accen <= bus_req_i.debug and bus_req_i.stb; -- access only when in debug-mode
rden <= accen and (not bus_req_i.rw);
wren <= accen and ( bus_req_i.rw);
wren <= accen and ( bus_req_i.rw) and and_reduce_f(bus_req_i.ben);
-- CPU response (hart ID) decoder for a single hart --
hart_id_decode_single:
if NUM_HARTS = 1 generate
cpu_rsp_dec <= (others => '1');
end generate;
-- CPU response (hart ID) decoder for multiple harts (max 4) --
hart_id_decode_multiple:
if NUM_HARTS > 1 generate
hart_id_decode_gen:
for i in 0 to NUM_HARTS-1 generate
cpu_rsp_dec(i) <= '1' when (bus_req_i.data(1 downto 0) = std_ulogic_vector(to_unsigned(i, 2))) else '0';
end generate;
end generate;
-- Authentication Module ------------------------------------------------------------------

View file

@ -1,5 +1,5 @@
-- ================================================================================ --
-- NEORV32 SoC - RISC-V-Compatible Debug Transport Module (DTM) --
-- NEORV32 OCD - RISC-V-Compatible Debug Transport Module (DTM) --
-- -------------------------------------------------------------------------------- --
-- Compatible to RISC-V debug spec. versions 0.13 and 1.0. --
-- -------------------------------------------------------------------------------- --
@ -111,7 +111,7 @@ begin
tap_sync.tdi <= tap_sync.tdi_ff(2);
-- Tap Control FSM ------------------------------------------------------------------------
-- JTAG Tap Control FSM -------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
tap_control: process(rstn_i, clk_i)
begin

View file

@ -310,6 +310,8 @@ begin
dma_req_o.addr <= engine.src_addr when (engine.state = S_READ) else engine.dst_addr;
dma_req_o.rvso <= '0'; -- no reservation set operation possible
dma_req_o.fence <= cfg.enable and cfg.fence and engine.done; -- issue FENCE operation when transfer is done
dma_req_o.sleep <= '1' when (engine.state = S_IDLE) else '0'; -- idle = sleep mode
dma_req_o.debug <= '0'; -- can never ever be in debug mode
-- address increment --
address_inc: process(cfg.qsel)

View file

@ -17,8 +17,8 @@ use neorv32.neorv32_package.all;
entity neorv32_fifo is
generic (
FIFO_DEPTH : natural := 4; -- number of fifo entries; has to be a power of two; min 1
FIFO_WIDTH : natural := 32; -- size of data elements in fifo
FIFO_DEPTH : natural := 4; -- number of FIFO entries; has to be a power of two; min 1
FIFO_WIDTH : natural := 32; -- size of data elements in FIFO
FIFO_RSYNC : boolean := false; -- false = async read; true = sync read
FIFO_SAFE : boolean := false; -- true = allow read/write only if data available
FULL_RESET : boolean := false -- true = reset all memory cells (cannot be mapped to BRAM)
@ -50,7 +50,7 @@ architecture neorv32_fifo_rtl of neorv32_fifo is
signal fifo_mem : fifo_mem_t; -- for fifo_depth_c > 1
signal fifo_reg : std_ulogic_vector(FIFO_WIDTH-1 downto 0); -- for fifo_depth_c = 1
-- Fifo control and status --
-- FIFO control and status --
signal we, re, match, empty, full, half, free, avail : std_ulogic;
-- write/read pointer --

View file

@ -38,8 +38,7 @@ architecture neorv32_imem_rtl of neorv32_imem is
constant alt_style_c : boolean := false; -- [TIP] enable this if synthesis fails to infer block RAM
-- ROM - initialized with executable code --
constant imem_app_size_c : natural := (application_init_image'length)*4; -- application (image) size in bytes
constant mem_rom_c : mem32_t(0 to IMEM_SIZE/4-1) := mem32_init_f(application_init_image, IMEM_SIZE/4);
constant mem_rom_c : mem32_t(0 to IMEM_SIZE/4-1) := mem32_init_f(application_init_image_c, IMEM_SIZE/4);
-- local signals --
signal rdata : std_ulogic_vector(31 downto 0);
@ -60,8 +59,8 @@ begin
"[NEORV32] Implementing processor-internal IMEM as " &
cond_sel_string_f(IMEM_INIT, "pre-initialized ROM.", "blank RAM.") severity note;
assert not ((IMEM_INIT = true) and (imem_app_size_c > IMEM_SIZE)) report
"[NEORV32] Application image (" & natural'image(imem_app_size_c) &
assert not ((IMEM_INIT = true) and (application_init_size_c > IMEM_SIZE)) report
"[NEORV32] Application image (" & natural'image(application_init_size_c) &
" bytes) does not fit into processor-internal IMEM (" &
natural'image(IMEM_SIZE) & " bytes)!" severity error;

View file

@ -1,138 +0,0 @@
-- ================================================================================ --
-- NEORV32 SoC - RISC-V-Compatible Machine Timer (MTIME) --
-- -------------------------------------------------------------------------------- --
-- The 64-bit counter core is split into two decoupled 32-bit registers to provide --
-- a shorter carry chain (improving timing). --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library neorv32;
use neorv32.neorv32_package.all;
entity neorv32_mtime is
port (
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active, async
bus_req_i : in bus_req_t; -- bus request
bus_rsp_o : out bus_rsp_t; -- bus response
time_o : out std_ulogic_vector(63 downto 0); -- current system time
irq_o : out std_ulogic -- interrupt request
);
end neorv32_mtime;
architecture neorv32_mtime_rtl of neorv32_mtime is
-- time write access buffer --
signal mtime_we : std_ulogic_vector(1 downto 0);
-- counter core --
signal mtimecmp, mtime : std_ulogic_vector(63 downto 0);
signal buf_lo : std_ulogic_vector(31 downto 0);
signal inc_lo, inc_hi : std_ulogic_vector(32 downto 0);
signal carry : std_ulogic_vector(0 downto 0);
-- comparator --
signal cmp_lo_eq, cmp_lo_gt, cmp_lo_ge, cmp_hi_eq, cmp_hi_gt : std_ulogic;
begin
-- Bus Access -----------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
bus_access: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
mtimecmp <= (others => '0');
mtime_we <= (others => '0');
bus_rsp_o <= rsp_terminate_c;
elsif rising_edge(clk_i) then
-- mtimecmp write access --
if (bus_req_i.stb = '1') and (bus_req_i.rw = '1') and (bus_req_i.addr(3) = '1') then
if (bus_req_i.addr(2) = '0') then
mtimecmp(31 downto 0) <= bus_req_i.data;
else
mtimecmp(63 downto 32) <= bus_req_i.data;
end if;
end if;
-- mtime write access buffer --
mtime_we(0) <= bus_req_i.stb and bus_req_i.rw and (not bus_req_i.addr(3)) and (not bus_req_i.addr(2));
mtime_we(1) <= bus_req_i.stb and bus_req_i.rw and (not bus_req_i.addr(3)) and ( bus_req_i.addr(2));
-- read access --
bus_rsp_o.ack <= bus_req_i.stb; -- bus handshake
bus_rsp_o.err <= '0'; -- no access errors
bus_rsp_o.data <= (others => '0'); -- default
if (bus_req_i.stb = '1') and (bus_req_i.rw = '0') then
case bus_req_i.addr(3 downto 2) is
when "00" => bus_rsp_o.data <= mtime(31 downto 0);
when "01" => bus_rsp_o.data <= mtime(63 downto 32);
when "10" => bus_rsp_o.data <= mtimecmp(31 downto 0);
when others => bus_rsp_o.data <= mtimecmp(63 downto 32);
end case;
end if;
end if;
end process bus_access;
-- 64-Bit Counter Core (split into two 32-bit registers) ----------------------------------
-- -------------------------------------------------------------------------------------------
counter: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
mtime <= (others => '0');
buf_lo <= (others => '0');
carry <= (others => '0');
elsif rising_edge(clk_i) then
-- low-word --
if (mtime_we(0) = '1') then -- write access (data is stable for at least one cycle after STB becomes low)
mtime(31 downto 0) <= bus_req_i.data;
else -- auto increment
mtime(31 downto 0) <= inc_lo(31 downto 0);
end if;
buf_lo <= mtime(31 downto 0); -- delay low-word by one cycle for system time output
carry(0) <= inc_lo(32); -- low-to-high carry
-- high-word --
if (mtime_we(1) = '1') then -- write access (data is stable for at least one cycle after STB becomes low)
mtime(63 downto 32) <= bus_req_i.data;
else -- auto increment
mtime(63 downto 32) <= inc_hi(31 downto 0);
end if;
end if;
end process counter;
-- increments --
inc_lo <= std_ulogic_vector(unsigned('0' & mtime(31 downto 0)) + 1);
inc_hi <= std_ulogic_vector(unsigned('0' & mtime(63 downto 32)) + unsigned(carry));
-- system time output --
time_o <= mtime(63 downto 32) & buf_lo;
-- Interrupt Generator (comparator) -------------------------------------------------------
-- -------------------------------------------------------------------------------------------
irq_gen: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
cmp_lo_ge <= '0';
irq_o <= '0';
elsif rising_edge(clk_i) then
cmp_lo_ge <= cmp_lo_gt or cmp_lo_eq; -- low word greater-than or equal
irq_o <= cmp_hi_gt or (cmp_hi_eq and cmp_lo_ge);
end if;
end process irq_gen;
-- sub-word comparators; there is one cycle delay between low (earlier) and high (later) word --
cmp_lo_eq <= '1' when (unsigned(mtime(31 downto 0)) = unsigned(mtimecmp(31 downto 0))) else '0';
cmp_lo_gt <= '1' when (unsigned(mtime(31 downto 0)) > unsigned(mtimecmp(31 downto 0))) else '0';
cmp_hi_eq <= '1' when (unsigned(mtime(63 downto 32)) = unsigned(mtimecmp(63 downto 32))) else '0';
cmp_hi_gt <= '1' when (unsigned(mtime(63 downto 32)) > unsigned(mtimecmp(63 downto 32))) else '0';
end neorv32_mtime_rtl;

View file

@ -3,7 +3,7 @@
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
@ -29,7 +29,7 @@ package neorv32_package is
-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100609"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100806"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width
@ -50,54 +50,52 @@ package neorv32_package is
-- Main Address Regions (base address must be aligned to the region's size) ---
constant mem_imem_base_c : std_ulogic_vector(31 downto 0) := x"00000000"; -- IMEM size via generic
constant mem_dmem_base_c : std_ulogic_vector(31 downto 0) := x"80000000"; -- DMEM size via generic
constant mem_xip_base_c : std_ulogic_vector(31 downto 0) := x"e0000000"; -- page (4MSBs) only!
constant mem_xip_base_c : std_ulogic_vector(31 downto 0) := x"e0000000"; -- page (4 MSBs) only!
constant mem_xip_size_c : natural := 256*1024*1024;
constant mem_boot_base_c : std_ulogic_vector(31 downto 0) := x"ffffc000";
constant mem_boot_size_c : natural := 8*1024;
constant mem_io_base_c : std_ulogic_vector(31 downto 0) := x"ffffe000";
constant mem_io_size_c : natural := 8*1024; -- = 32 * iodev_size_c
constant mem_io_base_c : std_ulogic_vector(31 downto 0) := x"ffe00000";
constant mem_io_size_c : natural := 32*64*1024; -- = 32 * iodev_size_c
-- Start of uncached memory access (256MB page / 4MSBs only) --
-- Start of uncached memory access (256MB page / 4 MSBs only) --
constant mem_uncached_begin_c : std_ulogic_vector(31 downto 0) := x"f0000000";
-- IO Address Map (base address must be aligned to the region's size) --
constant iodev_size_c : natural := 256; -- size of a single IO device (bytes)
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe000"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe100"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe200"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe300"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe400"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe500"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe600"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe700"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe800"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffe900"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffffea00"; -- reserved
constant base_io_cfs_c : std_ulogic_vector(31 downto 0) := x"ffffeb00";
constant base_io_slink_c : std_ulogic_vector(31 downto 0) := x"ffffec00";
constant base_io_dma_c : std_ulogic_vector(31 downto 0) := x"ffffed00";
constant base_io_crc_c : std_ulogic_vector(31 downto 0) := x"ffffee00";
constant base_io_xip_c : std_ulogic_vector(31 downto 0) := x"ffffef00";
constant base_io_pwm_c : std_ulogic_vector(31 downto 0) := x"fffff000";
constant base_io_gptmr_c : std_ulogic_vector(31 downto 0) := x"fffff100";
constant base_io_onewire_c : std_ulogic_vector(31 downto 0) := x"fffff200";
constant base_io_xirq_c : std_ulogic_vector(31 downto 0) := x"fffff300";
constant base_io_mtime_c : std_ulogic_vector(31 downto 0) := x"fffff400";
constant base_io_uart0_c : std_ulogic_vector(31 downto 0) := x"fffff500";
constant base_io_uart1_c : std_ulogic_vector(31 downto 0) := x"fffff600";
constant base_io_sdi_c : std_ulogic_vector(31 downto 0) := x"fffff700";
constant base_io_spi_c : std_ulogic_vector(31 downto 0) := x"fffff800";
constant base_io_twi_c : std_ulogic_vector(31 downto 0) := x"fffff900";
constant base_io_trng_c : std_ulogic_vector(31 downto 0) := x"fffffa00";
constant base_io_wdt_c : std_ulogic_vector(31 downto 0) := x"fffffb00";
constant base_io_gpio_c : std_ulogic_vector(31 downto 0) := x"fffffc00";
constant base_io_neoled_c : std_ulogic_vector(31 downto 0) := x"fffffd00";
constant base_io_sysinfo_c : std_ulogic_vector(31 downto 0) := x"fffffe00";
constant base_io_dm_c : std_ulogic_vector(31 downto 0) := x"ffffff00";
constant iodev_size_c : natural := 64*1024; -- size of a single IO device (bytes)
constant base_io_bootrom_c : std_ulogic_vector(31 downto 0) := x"ffe00000";
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffe10000"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffe20000"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffe30000"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffe40000"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffe50000"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffe60000"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffe70000"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffe80000"; -- reserved
--constant base_io_???_c : std_ulogic_vector(31 downto 0) := x"ffe90000"; -- reserved
constant base_io_twd_c : std_ulogic_vector(31 downto 0) := x"ffea0000";
constant base_io_cfs_c : std_ulogic_vector(31 downto 0) := x"ffeb0000";
constant base_io_slink_c : std_ulogic_vector(31 downto 0) := x"ffec0000";
constant base_io_dma_c : std_ulogic_vector(31 downto 0) := x"ffed0000";
constant base_io_crc_c : std_ulogic_vector(31 downto 0) := x"ffee0000";
constant base_io_xip_c : std_ulogic_vector(31 downto 0) := x"ffef0000";
constant base_io_pwm_c : std_ulogic_vector(31 downto 0) := x"fff00000";
constant base_io_gptmr_c : std_ulogic_vector(31 downto 0) := x"fff10000";
constant base_io_onewire_c : std_ulogic_vector(31 downto 0) := x"fff20000";
constant base_io_xirq_c : std_ulogic_vector(31 downto 0) := x"fff30000";
constant base_io_clint_c : std_ulogic_vector(31 downto 0) := x"fff40000";
constant base_io_uart0_c : std_ulogic_vector(31 downto 0) := x"fff50000";
constant base_io_uart1_c : std_ulogic_vector(31 downto 0) := x"fff60000";
constant base_io_sdi_c : std_ulogic_vector(31 downto 0) := x"fff70000";
constant base_io_spi_c : std_ulogic_vector(31 downto 0) := x"fff80000";
constant base_io_twi_c : std_ulogic_vector(31 downto 0) := x"fff90000";
constant base_io_trng_c : std_ulogic_vector(31 downto 0) := x"fffa0000";
constant base_io_wdt_c : std_ulogic_vector(31 downto 0) := x"fffb0000";
constant base_io_gpio_c : std_ulogic_vector(31 downto 0) := x"fffc0000";
constant base_io_neoled_c : std_ulogic_vector(31 downto 0) := x"fffd0000";
constant base_io_sysinfo_c : std_ulogic_vector(31 downto 0) := x"fffe0000";
constant base_io_ocd_c : std_ulogic_vector(31 downto 0) := x"ffff0000";
-- On-Chip Debugger - Debug Module Entry Points (Code ROM) --
constant dm_exc_entry_c : std_ulogic_vector(31 downto 0) := x"ffffff00"; -- = base_io_dm_c + 0, exceptions entry point
constant dm_park_entry_c : std_ulogic_vector(31 downto 0) := x"ffffff08"; -- = base_io_dm_c + 8, normal entry point
constant dm_exc_entry_c : std_ulogic_vector(31 downto 0) := x"fffffe00"; -- = base_io_ocd_c + code_rom_base + 0
constant dm_park_entry_c : std_ulogic_vector(31 downto 0) := x"fffffe10"; -- = base_io_ocd_c + code_rom_base + 16
-- **********************************************************************************************************
-- SoC Definitions
@ -131,7 +129,10 @@ package neorv32_package is
src : std_ulogic; -- access source (1=instruction fetch, 0=data access)
priv : std_ulogic; -- set if privileged (machine-mode) access
rvso : std_ulogic; -- set if reservation set operation (atomic LR/SC)
fence : std_ulogic; -- set if fence(.i) operation, single-shot (out-of-band)
-- out-of-band signals --
fence : std_ulogic; -- set if fence(.i) request by upstream device, single-shot
sleep : std_ulogic; -- set if ALL upstream sources are in sleep mode
debug : std_ulogic; -- set if upstream device is in debug mode
end record;
-- bus response --
@ -151,7 +152,9 @@ package neorv32_package is
src => '0',
priv => '0',
rvso => '0',
fence => '0'
fence => '0',
sleep => '1',
debug => '0'
);
-- endpoint (response) termination --
@ -194,6 +197,17 @@ package neorv32_package is
cyc : std_ulogic; -- valid cycle
end record;
-- source (request) termination --
constant xbus_req_terminate_c : xbus_req_t := (
addr => (others => '0'),
data => (others => '0'),
tag => (others => '0'),
we => '0',
sel => (others => '0'),
stb => '0',
cyc => '0'
);
-- xbus response --
type xbus_rsp_t is record
data : std_ulogic_vector(31 downto 0); -- read data, valid if ack=1
@ -697,6 +711,7 @@ package neorv32_package is
function replicate_f(input : std_ulogic; num : natural) return std_ulogic_vector;
impure function mem32_init_f(init : mem32_t; depth : natural) return mem32_t;
function print_version_f(version : std_ulogic_vector(31 downto 0)) return string;
function match_f(input : std_ulogic_vector; pattern : std_ulogic_vector) return boolean;
-- **********************************************************************************************************
-- NEORV32 Processor Top Entity (component prototype)
@ -706,9 +721,9 @@ package neorv32_package is
generic (
-- Processor Clocking --
CLOCK_FREQUENCY : natural := 0;
CLOCK_GATING_EN : boolean := false;
-- Identification --
HART_ID : std_ulogic_vector(31 downto 0) := x"00000000";
-- Dual-Core Configuration --
DUAL_CORE_EN : boolean := false;
-- Core Identification --
JEDEC_ID : std_ulogic_vector(10 downto 0) := "00000000000";
-- Boot Configuration --
BOOT_MODE_SELECT : natural range 0 to 2 := 0;
@ -740,9 +755,10 @@ package neorv32_package is
RISCV_ISA_Zksh : boolean := false;
RISCV_ISA_Zxcfu : boolean := false;
-- Tuning Options --
FAST_MUL_EN : boolean := false;
FAST_SHIFT_EN : boolean := false;
REGFILE_HW_RST : boolean := false;
CPU_CLOCK_GATING_EN : boolean := false;
CPU_FAST_MUL_EN : boolean := false;
CPU_FAST_SHIFT_EN : boolean := false;
CPU_RF_HW_RST_EN : boolean := false;
-- Physical Memory Protection (PMP) --
PMP_NUM_REGIONS : natural range 0 to 16 := 0;
PMP_MIN_GRANULARITY : natural := 4;
@ -782,7 +798,7 @@ package neorv32_package is
-- Processor peripherals --
IO_DISABLE_SYSINFO : boolean := false;
IO_GPIO_NUM : natural range 0 to 64 := 0;
IO_MTIME_EN : boolean := false;
IO_CLINT_EN : boolean := false;
IO_UART0_EN : boolean := false;
IO_UART0_RX_FIFO : natural range 1 to 2**15 := 1;
IO_UART0_TX_FIFO : natural range 1 to 2**15 := 1;
@ -795,6 +811,8 @@ package neorv32_package is
IO_SDI_FIFO : natural range 1 to 2**15 := 1;
IO_TWI_EN : boolean := false;
IO_TWI_FIFO : natural range 1 to 2**15 := 1;
IO_TWD_EN : boolean := false;
IO_TWD_FIFO : natural range 1 to 2**15 := 1;
IO_PWM_NUM_CH : natural range 0 to 16 := 0;
IO_WDT_EN : boolean := false;
IO_TRNG_EN : boolean := false;
@ -878,6 +896,11 @@ package neorv32_package is
twi_sda_o : out std_ulogic;
twi_scl_i : in std_ulogic := 'H';
twi_scl_o : out std_ulogic;
-- TWD (available if IO_TWD_EN = true) --
twd_sda_i : in std_ulogic := 'H';
twd_sda_o : out std_ulogic;
twd_scl_i : in std_ulogic := 'H';
twd_scl_o : out std_ulogic;
-- 1-Wire Interface (available if IO_ONEWIRE_EN = true) --
onewire_i : in std_ulogic := 'H';
onewire_o : out std_ulogic;
@ -888,7 +911,7 @@ package neorv32_package is
cfs_out_o : out std_ulogic_vector(IO_CFS_OUT_SIZE-1 downto 0);
-- NeoPixel-compatible smart LED interface (available if IO_NEOLED_EN = true) --
neoled_o : out std_ulogic;
-- Machine timer system time (available if IO_MTIME_EN = true) --
-- Machine timer system time (available if IO_CLINT_EN = true) --
mtime_time_o : out std_ulogic_vector(63 downto 0);
-- External platform interrupts (available if XIRQ_NUM_CH > 0) --
xirq_i : in std_ulogic_vector(31 downto 0) := (others => 'L');
@ -1156,4 +1179,23 @@ package body neorv32_package is
return res_v;
end function print_version_f;
-- Check if signal matches binary pattern (skip elements compared with '-') ---------------
-- -------------------------------------------------------------------------------------------
function match_f(input : std_ulogic_vector; pattern : std_ulogic_vector) return boolean is
variable match_v : boolean;
begin
if (input'length /= pattern'length) then
report "[NEORV32] match_f: input and pattern have different sizes!" severity error;
return false;
else
match_v := true;
for i in input'length-1 downto 0 loop
if (pattern(i) = '1') or (pattern(i) = '0') then -- valid pattern value, skip everything else
match_v := match_v and boolean(pattern(i) = input(i));
end if;
end loop;
return match_v;
end if;
end function match_f;
end neorv32_package;

View file

@ -17,8 +17,8 @@ use neorv32.neorv32_package.all;
entity neorv32_sysinfo is
generic (
NUM_HARTS : natural; -- number of physical CPU cores
CLOCK_FREQUENCY : natural; -- clock frequency of clk_i in Hz
CLOCK_GATING_EN : boolean; -- enable clock gating when in sleep mode
BOOT_MODE_SELECT : natural; -- boot configuration select (default = 0 = bootloader)
INT_BOOTLOADER_EN : boolean; -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
MEM_INT_IMEM_EN : boolean; -- implement processor-internal instruction memory
@ -43,12 +43,13 @@ entity neorv32_sysinfo is
OCD_EN : boolean; -- implement OCD?
OCD_AUTHENTICATION : boolean; -- implement OCD authenticator?
IO_GPIO_EN : boolean; -- implement general purpose IO port (GPIO)?
IO_MTIME_EN : boolean; -- implement machine system timer (MTIME)?
IO_CLINT_EN : boolean; -- implement machine local interruptor (CLINT)?
IO_UART0_EN : boolean; -- implement primary universal asynchronous receiver/transmitter (UART0)?
IO_UART1_EN : boolean; -- implement secondary universal asynchronous receiver/transmitter (UART1)?
IO_SPI_EN : boolean; -- implement serial peripheral interface (SPI)?
IO_SDI_EN : boolean; -- implement serial data interface (SDI)?
IO_TWI_EN : boolean; -- implement two-wire interface (TWI)?
IO_TWD_EN : boolean; -- implement two-wire device (TWD)?
IO_PWM_EN : boolean; -- implement pulse-width modulation controller (PWM)?
IO_WDT_EN : boolean; -- implement watch dog timer (WDT)?
IO_TRNG_EN : boolean; -- implement true random number generator (TRNG)?
@ -103,10 +104,10 @@ begin
end if;
end process sysinfo_0_write;
-- SYSINFO(1): Internal Memory Configuration (sizes)
-- SYSINFO(1): Misc --
sysinfo(1)(7 downto 0) <= std_ulogic_vector(to_unsigned(index_size_f(MEM_INT_IMEM_SIZE), 8)); -- log2(IMEM size)
sysinfo(1)(15 downto 8) <= std_ulogic_vector(to_unsigned(index_size_f(MEM_INT_DMEM_SIZE), 8)); -- log2(DMEM size)
sysinfo(1)(23 downto 16) <= (others => '0'); -- reserved
sysinfo(1)(23 downto 16) <= std_ulogic_vector(to_unsigned(NUM_HARTS, 8)); -- number of physical CPU cores
sysinfo(1)(31 downto 24) <= std_ulogic_vector(to_unsigned(BOOT_MODE_SELECT, 8)); -- boot configuration
-- SYSINFO(2): SoC Configuration --
@ -117,16 +118,16 @@ begin
sysinfo(2)(4) <= '1' when OCD_EN else '0'; -- on-chip debugger implemented?
sysinfo(2)(5) <= '1' when ICACHE_EN else '0'; -- processor-internal instruction cache implemented?
sysinfo(2)(6) <= '1' when DCACHE_EN else '0'; -- processor-internal data cache implemented?
sysinfo(2)(7) <= '1' when CLOCK_GATING_EN else '0'; -- enable clock gating when in sleep mode
sysinfo(2)(7) <= '0'; -- reserved
sysinfo(2)(8) <= '1' when xcache_en_c else '0'; -- external bus interface cache implemented?
sysinfo(2)(9) <= '1' when XIP_EN else '0'; -- execute in-place module implemented?
sysinfo(2)(10) <= '1' when xip_cache_en_c else '0'; -- execute in-place cache implemented?
sysinfo(2)(11) <= '1' when ocd_auth_en_c else '0'; -- on-chip debugger authentication implemented?
sysinfo(2)(12) <= '1' when int_imem_rom_c else '0'; -- processor-internal instruction memory implemented as pre-initialized ROM?
sysinfo(2)(13) <= '0'; -- reserved
sysinfo(2)(13) <= '1' when IO_TWD_EN else '0'; -- two-wire device (TWD) implemented?
sysinfo(2)(14) <= '1' when IO_DMA_EN else '0'; -- direct memory access controller (DMA) implemented?
sysinfo(2)(15) <= '1' when IO_GPIO_EN else '0'; -- general purpose input/output port unit (GPIO) implemented?
sysinfo(2)(16) <= '1' when IO_MTIME_EN else '0'; -- machine system timer (MTIME) implemented?
sysinfo(2)(16) <= '1' when IO_CLINT_EN else '0'; -- core local interruptor (CLINT) implemented?
sysinfo(2)(17) <= '1' when IO_UART0_EN else '0'; -- primary universal asynchronous receiver/transmitter (UART0) implemented?
sysinfo(2)(18) <= '1' when IO_SPI_EN else '0'; -- serial peripheral interface (SPI) implemented?
sysinfo(2)(19) <= '1' when IO_TWI_EN else '0'; -- two-wire interface (TWI) implemented?

View file

@ -8,7 +8,7 @@
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
@ -23,10 +23,11 @@ entity neorv32_top is
generic (
-- Processor Clocking --
CLOCK_FREQUENCY : natural := 0; -- clock frequency of clk_i in Hz
CLOCK_GATING_EN : boolean := false; -- enable clock gating when in sleep mode
-- Dual-Core Configuration --
DUAL_CORE_EN : boolean := false; -- enable dual-core homogeneous SMP
-- Core Identification --
HART_ID : std_ulogic_vector(31 downto 0) := x"00000000"; -- hardware thread ID
JEDEC_ID : std_ulogic_vector(10 downto 0) := "00000000000"; -- JEDEC ID: continuation codes + vendor ID
-- Boot Configuration --
@ -56,15 +57,16 @@ entity neorv32_top is
RISCV_ISA_Zknd : boolean := false; -- implement cryptography NIST AES decryption extension
RISCV_ISA_Zkne : boolean := false; -- implement cryptography NIST AES encryption extension
RISCV_ISA_Zknh : boolean := false; -- implement cryptography NIST hash extension
RISCV_ISA_Zksed : boolean := false; -- implement ShangMi block cypher extension
RISCV_ISA_Zksed : boolean := false; -- implement ShangMi block cipher extension
RISCV_ISA_Zksh : boolean := false; -- implement ShangMi hash extension
RISCV_ISA_Zmmul : boolean := false; -- implement multiply-only M sub-extension
RISCV_ISA_Zxcfu : boolean := false; -- implement custom (instr.) functions unit
-- Tuning Options --
FAST_MUL_EN : boolean := false; -- use DSPs for M extension's multiplier
FAST_SHIFT_EN : boolean := false; -- use barrel shifter for shift operations
REGFILE_HW_RST : boolean := false; -- implement full hardware reset for register file
CPU_CLOCK_GATING_EN : boolean := false; -- enable clock gating when in sleep mode
CPU_FAST_MUL_EN : boolean := false; -- use DSPs for M extension's multiplier
CPU_FAST_SHIFT_EN : boolean := false; -- use barrel shifter for shift operations
CPU_RF_HW_RST_EN : boolean := false; -- implement full hardware reset for register file
-- Physical Memory Protection (PMP) --
PMP_NUM_REGIONS : natural range 0 to 16 := 0; -- number of regions (0..16)
@ -114,23 +116,25 @@ entity neorv32_top is
-- Processor peripherals --
IO_DISABLE_SYSINFO : boolean := false; -- disable the SYSINFO module (for advanced users only)
IO_GPIO_NUM : natural range 0 to 64 := 0; -- number of GPIO input/output pairs (0..64)
IO_MTIME_EN : boolean := false; -- implement machine system timer (MTIME)?
IO_CLINT_EN : boolean := false; -- implement core local interruptor (CLINT)?
IO_UART0_EN : boolean := false; -- implement primary universal asynchronous receiver/transmitter (UART0)?
IO_UART0_RX_FIFO : natural range 1 to 2**15 := 1; -- RX fifo depth, has to be a power of two, min 1
IO_UART0_TX_FIFO : natural range 1 to 2**15 := 1; -- TX fifo depth, has to be a power of two, min 1
IO_UART0_RX_FIFO : natural range 1 to 2**15 := 1; -- RX FIFO depth, has to be a power of two, min 1
IO_UART0_TX_FIFO : natural range 1 to 2**15 := 1; -- TX FIFO depth, has to be a power of two, min 1
IO_UART1_EN : boolean := false; -- implement secondary universal asynchronous receiver/transmitter (UART1)?
IO_UART1_RX_FIFO : natural range 1 to 2**15 := 1; -- RX fifo depth, has to be a power of two, min 1
IO_UART1_TX_FIFO : natural range 1 to 2**15 := 1; -- TX fifo depth, has to be a power of two, min 1
IO_UART1_RX_FIFO : natural range 1 to 2**15 := 1; -- RX FIFO depth, has to be a power of two, min 1
IO_UART1_TX_FIFO : natural range 1 to 2**15 := 1; -- TX FIFO depth, has to be a power of two, min 1
IO_SPI_EN : boolean := false; -- implement serial peripheral interface (SPI)?
IO_SPI_FIFO : natural range 1 to 2**15 := 1; -- RTX fifo depth, has to be a power of two, min 1
IO_SPI_FIFO : natural range 1 to 2**15 := 1; -- RTX FIFO depth, has to be a power of two, min 1
IO_SDI_EN : boolean := false; -- implement serial data interface (SDI)?
IO_SDI_FIFO : natural range 1 to 2**15 := 1; -- RTX fifo depth, has to be zero or a power of two, min 1
IO_SDI_FIFO : natural range 1 to 2**15 := 1; -- RTX FIFO depth, has to be zero or a power of two, min 1
IO_TWI_EN : boolean := false; -- implement two-wire interface (TWI)?
IO_TWI_FIFO : natural range 1 to 2**15 := 1; -- RTX fifo depth, has to be zero or a power of two, min 1
IO_TWI_FIFO : natural range 1 to 2**15 := 1; -- RTX FIFO depth, has to be zero or a power of two, min 1
IO_TWD_EN : boolean := false; -- implement two-wire device (TWD)?
IO_TWD_FIFO : natural range 1 to 2**15 := 1; -- RTX FIFO depth, has to be zero or a power of two, min 1
IO_PWM_NUM_CH : natural range 0 to 16 := 0; -- number of PWM channels to implement (0..16)
IO_WDT_EN : boolean := false; -- implement watch dog timer (WDT)?
IO_TRNG_EN : boolean := false; -- implement true random number generator (TRNG)?
IO_TRNG_FIFO : natural range 1 to 2**15 := 1; -- data fifo depth, has to be a power of two, min 1
IO_TRNG_FIFO : natural range 1 to 2**15 := 1; -- data FIFO depth, has to be a power of two, min 1
IO_CFS_EN : boolean := false; -- implement custom functions subsystem (CFS)?
IO_CFS_CONFIG : std_ulogic_vector(31 downto 0) := x"00000000"; -- custom CFS configuration generic
IO_CFS_IN_SIZE : natural := 32; -- size of CFS input conduit in bits
@ -139,11 +143,11 @@ entity neorv32_top is
IO_NEOLED_TX_FIFO : natural range 1 to 2**15 := 1; -- NEOLED FIFO depth, has to be a power of two, min 1
IO_GPTMR_EN : boolean := false; -- implement general purpose timer (GPTMR)?
IO_ONEWIRE_EN : boolean := false; -- implement 1-wire interface (ONEWIRE)?
IO_ONEWIRE_FIFO : natural range 1 to 2**15 := 1; -- RTX fifo depth, has to be zero or a power of two, min 1
IO_ONEWIRE_FIFO : natural range 1 to 2**15 := 1; -- RTX FIFO depth, has to be zero or a power of two, min 1
IO_DMA_EN : boolean := false; -- implement direct memory access controller (DMA)?
IO_SLINK_EN : boolean := false; -- implement stream link interface (SLINK)?
IO_SLINK_RX_FIFO : natural range 1 to 2**15 := 1; -- RX fifo depth, has to be a power of two, min 1
IO_SLINK_TX_FIFO : natural range 1 to 2**15 := 1; -- TX fifo depth, has to be a power of two, min 1
IO_SLINK_RX_FIFO : natural range 1 to 2**15 := 1; -- RX FIFO depth, has to be a power of two, min 1
IO_SLINK_TX_FIFO : natural range 1 to 2**15 := 1; -- TX FIFO depth, has to be a power of two, min 1
IO_CRC_EN : boolean := false -- implement cyclic redundancy check unit (CRC)?
);
port (
@ -221,6 +225,12 @@ entity neorv32_top is
twi_scl_i : in std_ulogic := 'H'; -- serial clock line sense input
twi_scl_o : out std_ulogic; -- serial clock line output (pull low only)
-- TWD (available if IO_TWD_EN = true) --
twd_sda_i : in std_ulogic := 'H'; -- serial data line sense input
twd_sda_o : out std_ulogic; -- serial data line output (pull low only)
twd_scl_i : in std_ulogic := 'H'; -- serial clock line sense input
twd_scl_o : out std_ulogic; -- serial clock line output (pull low only)
-- 1-Wire Interface (available if IO_ONEWIRE_EN = true) --
onewire_i : in std_ulogic := 'H'; -- 1-wire bus sense input
onewire_o : out std_ulogic; -- 1-wire bus output (pull low only)
@ -235,15 +245,15 @@ entity neorv32_top is
-- NeoPixel-compatible smart LED interface (available if IO_NEOLED_EN = true) --
neoled_o : out std_ulogic; -- async serial data line
-- Machine timer system time (available if IO_MTIME_EN = true) --
-- Machine timer system time (available if IO_CLINT_EN = true) --
mtime_time_o : out std_ulogic_vector(63 downto 0); -- current system time
-- External platform interrupts (available if XIRQ_NUM_CH > 0) --
xirq_i : in std_ulogic_vector(31 downto 0) := (others => 'L'); -- IRQ channels
-- CPU interrupts (for chip-internal usage only) --
mtime_irq_i : in std_ulogic := 'L'; -- machine timer interrupt, available if IO_MTIME_EN = false
msw_irq_i : in std_ulogic := 'L'; -- machine software interrupt
mtime_irq_i : in std_ulogic := 'L'; -- machine timer interrupt, available if IO_CLINT_EN = false
msw_irq_i : in std_ulogic := 'L'; -- machine software interrupt, available if IO_CLINT_EN = false
mext_irq_i : in std_ulogic := 'L' -- machine external interrupt
);
end neorv32_top;
@ -260,11 +270,12 @@ architecture neorv32_top_rtl of neorv32_top is
constant bootrom_en_c : boolean := boolean(BOOT_MODE_SELECT = 0);
constant imem_as_rom_c : boolean := boolean(BOOT_MODE_SELECT = 2);
constant cpu_boot_addr_c : std_ulogic_vector(31 downto 0) :=
cond_sel_suv_f(boolean(BOOT_MODE_SELECT = 0), mem_boot_base_c,
cond_sel_suv_f(boolean(BOOT_MODE_SELECT = 0), base_io_bootrom_c,
cond_sel_suv_f(boolean(BOOT_MODE_SELECT = 1), BOOT_ADDR_CUSTOM,
cond_sel_suv_f(boolean(BOOT_MODE_SELECT = 2), mem_imem_base_c, x"00000000")));
-- auto-configuration --
constant num_cores_c : natural := cond_sel_natural_f(DUAL_CORE_EN, 2, 1);
constant io_gpio_en_c : boolean := boolean(IO_GPIO_NUM > 0);
constant io_xirq_en_c : boolean := boolean(XIRQ_NUM_CH > 0);
constant io_pwm_en_c : boolean := boolean(IO_PWM_NUM_CH > 0);
@ -282,39 +293,42 @@ architecture neorv32_top_rtl of neorv32_top is
signal rstn_wdt, rstn_sys, rstn_ext : std_ulogic;
-- clock system --
signal clk_cpu : std_ulogic; -- CPU core clock, can be switched off
signal clk_gen : std_ulogic_vector(7 downto 0); -- scaled clock-enables
--
type clk_gen_en_enum_t is (
CG_CFS, CG_UART0, CG_UART1, CG_SPI, CG_TWI, CG_PWM, CG_WDT, CG_NEOLED, CG_GPTMR, CG_XIP, CG_ONEWIRE
CG_CFS, CG_UART0, CG_UART1, CG_SPI, CG_TWI, CG_TWD, CG_PWM, CG_WDT, CG_NEOLED, CG_GPTMR, CG_XIP, CG_ONEWIRE
);
type clk_gen_en_t is array (clk_gen_en_enum_t) of std_ulogic;
signal clk_gen_en : clk_gen_en_t;
signal clk_gen_en2 : std_ulogic_vector(10 downto 0);
-- CPU status --
signal cpu_debug, cpu_sleep : std_ulogic;
signal clk_gen_en2 : std_ulogic_vector(11 downto 0);
-- debug module interface (DMI) --
signal dmi_req : dmi_req_t;
signal dmi_rsp : dmi_rsp_t;
-- debug core interface (DCI) --
signal dci_ndmrstn, dci_haltreq : std_ulogic;
signal dci_ndmrstn : std_ulogic;
signal dci_haltreq : std_ulogic_vector(num_cores_c-1 downto 0);
-- bus: core complex (CPU + caches) and DMA --
signal cpu_i_req, cpu_d_req, icache_req, dcache_req, core_req, main_req, main2_req, dma_req : bus_req_t;
signal cpu_i_rsp, cpu_d_rsp, icache_rsp, dcache_rsp, core_rsp, main_rsp, main2_rsp, dma_rsp : bus_rsp_t;
-- bus: CPU core(s) + L1 caches --
type multicore_req_t is array (0 to num_cores_c-1) of bus_req_t;
type multicore_rsp_t is array (0 to num_cores_c-1) of bus_rsp_t;
signal cpu_i_req, cpu_d_req, icache_req, dcache_req, core_req : multicore_req_t;
signal cpu_i_rsp, cpu_d_rsp, icache_rsp, dcache_rsp, core_rsp : multicore_rsp_t;
-- bus: core complex and DMA --
signal complex_req, main_req, main2_req, dma_req : bus_req_t;
signal complex_rsp, main_rsp, main2_rsp, dma_rsp : bus_rsp_t;
-- bus: main sections --
signal imem_req, dmem_req, xipcache_req, xip_req, boot_req, io_req, xcache_req, xbus_req : bus_req_t;
signal imem_rsp, dmem_rsp, xipcache_rsp, xip_rsp, boot_rsp, io_rsp, xcache_rsp, xbus_rsp : bus_rsp_t;
signal imem_req, dmem_req, xipcache_req, xip_req, io_req, xcache_req, xbus_req : bus_req_t;
signal imem_rsp, dmem_rsp, xipcache_rsp, xip_rsp, io_rsp, xcache_rsp, xbus_rsp : bus_rsp_t;
-- bus: IO devices --
type io_devices_enum_t is (
IODEV_OCD, IODEV_SYSINFO, IODEV_NEOLED, IODEV_GPIO, IODEV_WDT, IODEV_TRNG, IODEV_TWI,
IODEV_SPI, IODEV_SDI, IODEV_UART1, IODEV_UART0, IODEV_MTIME, IODEV_XIRQ, IODEV_ONEWIRE,
IODEV_GPTMR, IODEV_PWM, IODEV_XIP, IODEV_CRC, IODEV_DMA, IODEV_SLINK, IODEV_CFS
IODEV_BOOTROM, IODEV_OCD, IODEV_SYSINFO, IODEV_NEOLED, IODEV_GPIO, IODEV_WDT, IODEV_TRNG, IODEV_TWI,
IODEV_SPI, IODEV_SDI, IODEV_UART1, IODEV_UART0, IODEV_CLINT, IODEV_XIRQ, IODEV_ONEWIRE,
IODEV_GPTMR, IODEV_PWM, IODEV_XIP, IODEV_CRC, IODEV_DMA, IODEV_SLINK, IODEV_CFS, IODEV_TWD
);
type iodev_req_t is array (io_devices_enum_t) of bus_req_t;
type iodev_rsp_t is array (io_devices_enum_t) of bus_rsp_t;
@ -323,19 +337,21 @@ architecture neorv32_top_rtl of neorv32_top is
-- IRQs --
type firq_enum_t is (
FIRQ_TRNG, FIRQ_UART0_RX, FIRQ_UART0_TX, FIRQ_UART1_RX, FIRQ_UART1_TX, FIRQ_SPI, FIRQ_SDI, FIRQ_TWI,
FIRQ_TWD, FIRQ_UART0_RX, FIRQ_UART0_TX, FIRQ_UART1_RX, FIRQ_UART1_TX, FIRQ_SPI, FIRQ_SDI, FIRQ_TWI,
FIRQ_CFS, FIRQ_NEOLED, FIRQ_XIRQ, FIRQ_GPTMR, FIRQ_ONEWIRE, FIRQ_DMA, FIRQ_SLINK_RX, FIRQ_SLINK_TX
);
type firq_t is array (firq_enum_t) of std_ulogic;
signal firq : firq_t;
signal cpu_firq : std_ulogic_vector(15 downto 0);
signal mtime_irq : std_ulogic;
signal mtime_irq : std_ulogic_vector(num_cores_c-1 downto 0);
signal msw_irq : std_ulogic_vector(num_cores_c-1 downto 0);
begin
-- **************************************************************************************************************************
-- Sanity Checks
-- **************************************************************************************************************************
sanity_checks:
if true generate
@ -348,6 +364,7 @@ begin
-- show SoC configuration --
assert false report
"[NEORV32] Processor Configuration: CPU " & -- cpu core is always enabled
cond_sel_string_f(DUAL_CORE_EN, "(dual-core-smp) ", "(single-core) ") &
cond_sel_string_f(MEM_INT_IMEM_EN, cond_sel_string_f(imem_as_rom_c, "IMEM-ROM ", "IMEM "), "") &
cond_sel_string_f(MEM_INT_DMEM_EN, "DMEM ", "") &
cond_sel_string_f(bootrom_en_c, "BOOTROM ", "") &
@ -357,13 +374,14 @@ begin
cond_sel_string_f(XBUS_EN and XBUS_CACHE_EN, "XBUS-CACHE ", "") &
cond_sel_string_f(XIP_EN, "XIP ", "") &
cond_sel_string_f(XIP_EN and XIP_CACHE_EN, "XIP-CACHE ", "") &
cond_sel_string_f(IO_CLINT_EN, "CLINT ", "") &
cond_sel_string_f(io_gpio_en_c, "GPIO ", "") &
cond_sel_string_f(IO_MTIME_EN, "MTIME ", "") &
cond_sel_string_f(IO_UART0_EN, "UART0 ", "") &
cond_sel_string_f(IO_UART1_EN, "UART1 ", "") &
cond_sel_string_f(IO_SPI_EN, "SPI ", "") &
cond_sel_string_f(IO_SDI_EN, "SDI ", "") &
cond_sel_string_f(IO_TWI_EN, "TWI ", "") &
cond_sel_string_f(IO_TWD_EN, "TWD ", "") &
cond_sel_string_f(io_pwm_en_c, "PWM ", "") &
cond_sel_string_f(IO_WDT_EN, "WDT ", "") &
cond_sel_string_f(IO_TRNG_EN, "TRNG ", "") &
@ -411,6 +429,7 @@ begin
-- **************************************************************************************************************************
-- Clock and Reset Generators
-- **************************************************************************************************************************
generators:
if true generate
@ -431,7 +450,7 @@ begin
-- -------------------------------------------------------------------------------------------
neorv32_sys_clock_inst: entity neorv32.neorv32_sys_clock
generic map (
NUM_EN => 11
NUM_EN => clk_gen_en2'length
)
port map (
clk_i => clk_i,
@ -441,9 +460,9 @@ begin
);
-- fresh clocks anyone? --
clk_gen_en2 <= clk_gen_en(CG_WDT) & clk_gen_en(CG_UART0) & clk_gen_en(CG_UART1) & clk_gen_en(CG_SPI) &
clk_gen_en(CG_TWI) & clk_gen_en(CG_PWM) & clk_gen_en(CG_WDT) & clk_gen_en(CG_NEOLED) &
clk_gen_en(CG_GPTMR) & clk_gen_en(CG_XIP) & clk_gen_en(CG_ONEWIRE);
clk_gen_en2 <= clk_gen_en(CG_WDT) & clk_gen_en(CG_UART0) & clk_gen_en(CG_UART1) & clk_gen_en(CG_SPI) &
clk_gen_en(CG_TWI) & clk_gen_en(CG_TWD) & clk_gen_en(CG_PWM) & clk_gen_en(CG_WDT) &
clk_gen_en(CG_NEOLED) & clk_gen_en(CG_GPTMR) & clk_gen_en(CG_XIP) & clk_gen_en(CG_ONEWIRE);
end generate; -- /generators
@ -451,34 +470,35 @@ begin
-- **************************************************************************************************************************
-- Core Complex
-- **************************************************************************************************************************
core_complex:
if true generate
-- CPU Clock Gating -----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_cpu_clockgate_inst_true:
if CLOCK_GATING_EN generate
neorv32_cpu_clockgate_inst: entity neorv32.neorv32_clockgate
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
halt_i => cpu_sleep,
clk_o => clk_cpu
);
end generate;
neorv32_cpu_clockgate_inst_false:
if not CLOCK_GATING_EN generate
clk_cpu <= clk_i;
end generate;
-- fast interrupt requests (FIRQs) --
cpu_firq(0) <= firq(FIRQ_TWD);
cpu_firq(1) <= firq(FIRQ_CFS);
cpu_firq(2) <= firq(FIRQ_UART0_RX);
cpu_firq(3) <= firq(FIRQ_UART0_TX);
cpu_firq(4) <= firq(FIRQ_UART1_RX);
cpu_firq(5) <= firq(FIRQ_UART1_TX);
cpu_firq(6) <= firq(FIRQ_SPI);
cpu_firq(7) <= firq(FIRQ_TWI);
cpu_firq(8) <= firq(FIRQ_XIRQ);
cpu_firq(9) <= firq(FIRQ_NEOLED);
cpu_firq(10) <= firq(FIRQ_DMA);
cpu_firq(11) <= firq(FIRQ_SDI);
cpu_firq(12) <= firq(FIRQ_GPTMR);
cpu_firq(13) <= firq(FIRQ_ONEWIRE);
cpu_firq(14) <= firq(FIRQ_SLINK_RX);
cpu_firq(15) <= firq(FIRQ_SLINK_TX);
-- CPU core(s) + optional L1 caches --
core_complex_gen:
for i in 0 to num_cores_c-1 generate
-- CPU Core -------------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_cpu_inst: entity neorv32.neorv32_cpu
generic map (
-- General --
HART_ID => HART_ID,
HART_ID => i,
VENDOR_ID => vendorid_c,
BOOT_ADDR => cpu_boot_addr_c,
DEBUG_PARK_ADDR => dm_park_entry_c,
@ -510,9 +530,10 @@ begin
RISCV_ISA_Sdtrig => OCD_EN,
RISCV_ISA_Smpmp => cpu_smpmp_c,
-- Tuning Options --
FAST_MUL_EN => FAST_MUL_EN,
FAST_SHIFT_EN => FAST_SHIFT_EN,
REGFILE_HW_RST => REGFILE_HW_RST,
CPU_CLOCK_GATING_EN => CPU_CLOCK_GATING_EN,
CPU_FAST_MUL_EN => CPU_FAST_MUL_EN,
CPU_FAST_SHIFT_EN => CPU_FAST_SHIFT_EN,
CPU_RF_HW_RST_EN => CPU_RF_HW_RST_EN,
-- Physical Memory Protection (PMP) --
PMP_NUM_REGIONS => PMP_NUM_REGIONS,
PMP_MIN_GRANULARITY => PMP_MIN_GRANULARITY,
@ -524,45 +545,24 @@ begin
)
port map (
-- global control --
clk_i => clk_cpu, -- switchable clock
clk_aux_i => clk_i, -- always-on clock
clk_i => clk_i,
rstn_i => rstn_sys,
sleep_o => cpu_sleep,
debug_o => cpu_debug,
-- interrupts --
msi_i => msw_irq_i,
msi_i => msw_irq(i),
mei_i => mext_irq_i,
mti_i => mtime_irq,
mti_i => mtime_irq(i),
firq_i => cpu_firq,
dbi_i => dci_haltreq,
dbi_i => dci_haltreq(i),
-- instruction bus interface --
ibus_req_o => cpu_i_req,
ibus_rsp_i => cpu_i_rsp,
ibus_req_o => cpu_i_req(i),
ibus_rsp_i => cpu_i_rsp(i),
-- data bus interface --
dbus_req_o => cpu_d_req,
dbus_rsp_i => cpu_d_rsp
dbus_req_o => cpu_d_req(i),
dbus_rsp_i => cpu_d_rsp(i)
);
-- fast interrupt requests (FIRQs) --
cpu_firq(0) <= firq(FIRQ_TRNG);
cpu_firq(1) <= firq(FIRQ_CFS);
cpu_firq(2) <= firq(FIRQ_UART0_RX);
cpu_firq(3) <= firq(FIRQ_UART0_TX);
cpu_firq(4) <= firq(FIRQ_UART1_RX);
cpu_firq(5) <= firq(FIRQ_UART1_TX);
cpu_firq(6) <= firq(FIRQ_SPI);
cpu_firq(7) <= firq(FIRQ_TWI);
cpu_firq(8) <= firq(FIRQ_XIRQ);
cpu_firq(9) <= firq(FIRQ_NEOLED);
cpu_firq(10) <= firq(FIRQ_DMA);
cpu_firq(11) <= firq(FIRQ_SDI);
cpu_firq(12) <= firq(FIRQ_GPTMR);
cpu_firq(13) <= firq(FIRQ_ONEWIRE);
cpu_firq(14) <= firq(FIRQ_SLINK_RX);
cpu_firq(15) <= firq(FIRQ_SLINK_TX);
-- CPU Instruction Cache (I-Cache) --------------------------------------------------------
-- CPU L1 Instruction Cache (I-Cache) -----------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_icache_inst_true:
if ICACHE_EN generate
@ -577,21 +577,21 @@ begin
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
host_req_i => cpu_i_req,
host_rsp_o => cpu_i_rsp,
bus_req_o => icache_req,
bus_rsp_i => icache_rsp
host_req_i => cpu_i_req(i),
host_rsp_o => cpu_i_rsp(i),
bus_req_o => icache_req(i),
bus_rsp_i => icache_rsp(i)
);
end generate;
neorv32_icache_inst_false:
if not ICACHE_EN generate
icache_req <= cpu_i_req;
cpu_i_rsp <= icache_rsp;
icache_req(i) <= cpu_i_req(i);
cpu_i_rsp(i) <= icache_rsp(i);
end generate;
-- CPU Data Cache (D-Cache) ---------------------------------------------------------------
-- CPU L1 Data Cache (D-Cache) ------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_dcache_inst_true:
if DCACHE_EN generate
@ -606,45 +606,77 @@ begin
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
host_req_i => cpu_d_req,
host_rsp_o => cpu_d_rsp,
bus_req_o => dcache_req,
bus_rsp_i => dcache_rsp
host_req_i => cpu_d_req(i),
host_rsp_o => cpu_d_rsp(i),
bus_req_o => dcache_req(i),
bus_rsp_i => dcache_rsp(i)
);
end generate;
neorv32_dcache_inst_false:
if not DCACHE_EN generate
dcache_req <= cpu_d_req;
cpu_d_rsp <= dcache_rsp;
dcache_req(i) <= cpu_d_req(i);
cpu_d_rsp(i) <= dcache_rsp(i);
end generate;
-- Core Complex Bus Switch ----------------------------------------------------------------
-- Core Instruction/Data Bus Switch -------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_core_bus_switch_inst: entity neorv32.neorv32_bus_switch
generic map (
ROUND_ROBIN_EN => false, -- use prioritizing arbitration
PORT_A_READ_ONLY => false,
PORT_B_READ_ONLY => true -- i-fetch is read-only
)
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
a_lock_i => '0', -- no exclusive accesses for port A
a_req_i => dcache_req, -- prioritized
a_rsp_o => dcache_rsp,
b_req_i => icache_req,
b_rsp_o => icache_rsp,
x_req_o => core_req,
x_rsp_i => core_rsp
a_lock_i => '0', -- no exclusive accesses
a_req_i => dcache_req(i), -- prioritized
a_rsp_o => dcache_rsp(i),
b_req_i => icache_req(i),
b_rsp_o => icache_rsp(i),
x_req_o => core_req(i),
x_rsp_i => core_rsp(i)
);
end generate; -- /core_complex
-- Core Complex Bus Switch ----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
core_complex_dual:
if num_cores_c > 1 generate
neorv32_complex_mux_inst: entity neorv32.neorv32_bus_switch
generic map (
ROUND_ROBIN_EN => true,
PORT_A_READ_ONLY => false,
PORT_B_READ_ONLY => false
)
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
a_lock_i => '0',
a_req_i => core_req(core_req'left),
a_rsp_o => core_rsp(core_req'left),
b_req_i => core_req(core_req'right), -- [hack] core_req(1) does not exist if single core
b_rsp_o => core_rsp(core_req'right),
x_req_o => complex_req,
x_rsp_i => complex_rsp
);
end generate;
core_complex_single:
if num_cores_c = 1 generate
complex_req <= core_req(0);
core_rsp(0) <= complex_rsp;
end generate;
-- **************************************************************************************************************************
-- Direct Memory Access Controller (DMA) Complex
-- **************************************************************************************************************************
neorv32_dma_complex_true:
if IO_DMA_EN generate
@ -667,15 +699,16 @@ begin
-- -------------------------------------------------------------------------------------------
neorv32_dma_bus_switch_inst: entity neorv32.neorv32_bus_switch
generic map (
ROUND_ROBIN_EN => false, -- use prioritizing arbitration
PORT_A_READ_ONLY => false,
PORT_B_READ_ONLY => false
)
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
a_lock_i => '0', -- no exclusive accesses for port A
a_req_i => core_req, -- prioritized
a_rsp_o => core_rsp,
a_lock_i => '0', -- no exclusive accesses
a_req_i => complex_req, -- prioritized
a_rsp_o => complex_rsp,
b_req_i => dma_req,
b_rsp_o => dma_rsp,
x_req_o => main_req,
@ -687,8 +720,8 @@ begin
neorv32_dma_complex_false:
if not IO_DMA_EN generate
iodev_rsp(IODEV_DMA) <= rsp_terminate_c;
main_req <= core_req;
core_rsp <= main_rsp;
main_req <= complex_req;
complex_rsp <= main_rsp;
firq(FIRQ_DMA) <= '0';
end generate;
@ -696,6 +729,7 @@ begin
-- **************************************************************************************************************************
-- Reservation Set Controller (for atomic LR/SC accesses)
-- **************************************************************************************************************************
neorv32_bus_reservation_set_true:
if RISCV_ISA_Zalrsc generate
neorv32_bus_reservation_set_inst: entity neorv32.neorv32_bus_reservation_set
@ -722,6 +756,7 @@ begin
-- **************************************************************************************************************************
-- Address Region Gateway
-- **************************************************************************************************************************
neorv32_bus_gateway_inst: entity neorv32.neorv32_bus_gateway
generic map (
TIMEOUT => bus_timeout_c,
@ -730,35 +765,24 @@ begin
A_BASE => mem_imem_base_c,
A_SIZE => imem_size_c,
A_TMO_EN => true,
A_PRIV => false,
-- port B: internal DMEM --
B_ENABLE => MEM_INT_DMEM_EN,
B_BASE => mem_dmem_base_c,
B_SIZE => dmem_size_c,
B_TMO_EN => true,
B_PRIV => false,
-- port C: XIP --
C_ENABLE => XIP_EN,
C_BASE => mem_xip_base_c,
C_SIZE => mem_xip_size_c,
C_TMO_EN => false, -- no timeout for XIP accesses
C_PRIV => false,
-- port D: BOOT ROM --
D_ENABLE => bootrom_en_c,
D_BASE => mem_boot_base_c,
D_SIZE => mem_boot_size_c,
-- port D: IO --
D_ENABLE => true, -- always enabled (but will be trimmed if no IO devices are implemented)
D_BASE => mem_io_base_c,
D_SIZE => mem_io_size_c,
D_TMO_EN => true,
D_PRIV => true, -- only privileged (M-mode) accesses are allowed
-- port E: IO --
E_ENABLE => true, -- always enabled (but will be trimmed if no IO devices are implemented)
E_BASE => mem_io_base_c,
E_SIZE => mem_io_size_c,
E_TMO_EN => true,
E_PRIV => true, -- only privileged (M-mode) accesses are allowed
-- port X (the void): XBUS --
X_ENABLE => XBUS_EN,
X_TMO_EN => false, -- timeout handled by XBUS gateway
X_PRIV => false
X_TMO_EN => false -- timeout handled by XBUS gateway
)
port map (
-- global control --
@ -774,10 +798,8 @@ begin
b_rsp_i => dmem_rsp,
c_req_o => xip_req,
c_rsp_i => xip_rsp,
d_req_o => boot_req,
d_rsp_i => boot_rsp,
e_req_o => io_req,
e_rsp_i => io_rsp,
d_req_o => io_req,
d_rsp_i => io_rsp,
x_req_o => xbus_req,
x_rsp_i => xbus_rsp
);
@ -786,6 +808,7 @@ begin
-- **************************************************************************************************************************
-- Memory System
-- **************************************************************************************************************************
memory_system:
if true generate
@ -834,25 +857,6 @@ begin
end generate;
-- Processor-Internal Bootloader ROM (BOOTROM) --------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_boot_rom_inst_true:
if bootrom_en_c generate
neorv32_boot_rom_inst: entity neorv32.neorv32_boot_rom
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
bus_req_i => boot_req,
bus_rsp_o => boot_rsp
);
end generate;
neorv32_boot_rom_inst_false:
if not bootrom_en_c generate
boot_rsp <= rsp_terminate_c;
end generate;
-- Execute In-Place Module (XIP) ----------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_xip_inst_true:
@ -994,6 +998,7 @@ begin
-- **************************************************************************************************************************
-- IO/Peripheral Modules
-- **************************************************************************************************************************
io_system:
if true generate
@ -1001,80 +1006,101 @@ begin
-- -------------------------------------------------------------------------------------------
neorv32_bus_io_switch_inst: entity neorv32.neorv32_bus_io_switch
generic map (
INREG_EN => true,
OUTREG_EN => true,
DEV_SIZE => iodev_size_c, -- size of a single IO device
DEV_00_EN => OCD_EN, DEV_00_BASE => base_io_dm_c,
DEV_01_EN => io_sysinfo_en_c, DEV_01_BASE => base_io_sysinfo_c,
DEV_02_EN => IO_NEOLED_EN, DEV_02_BASE => base_io_neoled_c,
DEV_03_EN => io_gpio_en_c, DEV_03_BASE => base_io_gpio_c,
DEV_04_EN => IO_WDT_EN, DEV_04_BASE => base_io_wdt_c,
DEV_05_EN => IO_TRNG_EN, DEV_05_BASE => base_io_trng_c,
DEV_06_EN => IO_TWI_EN, DEV_06_BASE => base_io_twi_c,
DEV_07_EN => IO_SPI_EN, DEV_07_BASE => base_io_spi_c,
DEV_08_EN => IO_SDI_EN, DEV_08_BASE => base_io_sdi_c,
DEV_09_EN => IO_UART1_EN, DEV_09_BASE => base_io_uart1_c,
DEV_10_EN => IO_UART0_EN, DEV_10_BASE => base_io_uart0_c,
DEV_11_EN => IO_MTIME_EN, DEV_11_BASE => base_io_mtime_c,
DEV_12_EN => io_xirq_en_c, DEV_12_BASE => base_io_xirq_c,
DEV_13_EN => IO_ONEWIRE_EN, DEV_13_BASE => base_io_onewire_c,
DEV_14_EN => IO_GPTMR_EN, DEV_14_BASE => base_io_gptmr_c,
DEV_15_EN => io_pwm_en_c, DEV_15_BASE => base_io_pwm_c,
DEV_16_EN => XIP_EN, DEV_16_BASE => base_io_xip_c,
DEV_17_EN => IO_CRC_EN, DEV_17_BASE => base_io_crc_c,
DEV_18_EN => IO_DMA_EN, DEV_18_BASE => base_io_dma_c,
DEV_19_EN => IO_SLINK_EN, DEV_19_BASE => base_io_slink_c,
DEV_20_EN => IO_CFS_EN, DEV_20_BASE => base_io_cfs_c,
DEV_21_EN => false, DEV_31_BASE => (others => '0'), -- reserved
DEV_22_EN => false, DEV_30_BASE => (others => '0'), -- reserved
DEV_23_EN => false, DEV_29_BASE => (others => '0'), -- reserved
DEV_24_EN => false, DEV_28_BASE => (others => '0'), -- reserved
DEV_25_EN => false, DEV_27_BASE => (others => '0'), -- reserved
DEV_26_EN => false, DEV_26_BASE => (others => '0'), -- reserved
DEV_27_EN => false, DEV_25_BASE => (others => '0'), -- reserved
DEV_28_EN => false, DEV_24_BASE => (others => '0'), -- reserved
DEV_29_EN => false, DEV_23_BASE => (others => '0'), -- reserved
DEV_30_EN => false, DEV_22_BASE => (others => '0'), -- reserved
DEV_31_EN => false, DEV_21_BASE => (others => '0') -- reserved
DEV_00_EN => bootrom_en_c, DEV_00_BASE => base_io_bootrom_c,
DEV_01_EN => false, DEV_01_BASE => (others => '0'), -- reserved
DEV_02_EN => false, DEV_02_BASE => (others => '0'), -- reserved
DEV_03_EN => false, DEV_03_BASE => (others => '0'), -- reserved
DEV_04_EN => false, DEV_04_BASE => (others => '0'), -- reserved
DEV_05_EN => false, DEV_05_BASE => (others => '0'), -- reserved
DEV_06_EN => false, DEV_06_BASE => (others => '0'), -- reserved
DEV_07_EN => false, DEV_07_BASE => (others => '0'), -- reserved
DEV_08_EN => false, DEV_08_BASE => (others => '0'), -- reserved
DEV_09_EN => false, DEV_09_BASE => (others => '0'), -- reserved
DEV_10_EN => IO_TWD_EN, DEV_10_BASE => base_io_twd_c,
DEV_11_EN => IO_CFS_EN, DEV_11_BASE => base_io_cfs_c,
DEV_12_EN => IO_SLINK_EN, DEV_12_BASE => base_io_slink_c,
DEV_13_EN => IO_DMA_EN, DEV_13_BASE => base_io_dma_c,
DEV_14_EN => IO_CRC_EN, DEV_14_BASE => base_io_crc_c,
DEV_15_EN => XIP_EN, DEV_15_BASE => base_io_xip_c,
DEV_16_EN => io_pwm_en_c, DEV_16_BASE => base_io_pwm_c,
DEV_17_EN => IO_GPTMR_EN, DEV_17_BASE => base_io_gptmr_c,
DEV_18_EN => IO_ONEWIRE_EN, DEV_18_BASE => base_io_onewire_c,
DEV_19_EN => io_xirq_en_c, DEV_19_BASE => base_io_xirq_c,
DEV_20_EN => IO_CLINT_EN, DEV_20_BASE => base_io_clint_c,
DEV_21_EN => IO_UART0_EN, DEV_21_BASE => base_io_uart0_c,
DEV_22_EN => IO_UART1_EN, DEV_22_BASE => base_io_uart1_c,
DEV_23_EN => IO_SDI_EN, DEV_23_BASE => base_io_sdi_c,
DEV_24_EN => IO_SPI_EN, DEV_24_BASE => base_io_spi_c,
DEV_25_EN => IO_TWI_EN, DEV_25_BASE => base_io_twi_c,
DEV_26_EN => IO_TRNG_EN, DEV_26_BASE => base_io_trng_c,
DEV_27_EN => IO_WDT_EN, DEV_27_BASE => base_io_wdt_c,
DEV_28_EN => io_gpio_en_c, DEV_28_BASE => base_io_gpio_c,
DEV_29_EN => IO_NEOLED_EN, DEV_29_BASE => base_io_neoled_c,
DEV_30_EN => io_sysinfo_en_c, DEV_30_BASE => base_io_sysinfo_c,
DEV_31_EN => OCD_EN, DEV_31_BASE => base_io_ocd_c
)
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
main_req_i => io_req,
main_rsp_o => io_rsp,
dev_00_req_o => iodev_req(IODEV_OCD), dev_00_rsp_i => iodev_rsp(IODEV_OCD),
dev_01_req_o => iodev_req(IODEV_SYSINFO), dev_01_rsp_i => iodev_rsp(IODEV_SYSINFO),
dev_02_req_o => iodev_req(IODEV_NEOLED), dev_02_rsp_i => iodev_rsp(IODEV_NEOLED),
dev_03_req_o => iodev_req(IODEV_GPIO), dev_03_rsp_i => iodev_rsp(IODEV_GPIO),
dev_04_req_o => iodev_req(IODEV_WDT), dev_04_rsp_i => iodev_rsp(IODEV_WDT),
dev_05_req_o => iodev_req(IODEV_TRNG), dev_05_rsp_i => iodev_rsp(IODEV_TRNG),
dev_06_req_o => iodev_req(IODEV_TWI), dev_06_rsp_i => iodev_rsp(IODEV_TWI),
dev_07_req_o => iodev_req(IODEV_SPI), dev_07_rsp_i => iodev_rsp(IODEV_SPI),
dev_08_req_o => iodev_req(IODEV_SDI), dev_08_rsp_i => iodev_rsp(IODEV_SDI),
dev_09_req_o => iodev_req(IODEV_UART1), dev_09_rsp_i => iodev_rsp(IODEV_UART1),
dev_10_req_o => iodev_req(IODEV_UART0), dev_10_rsp_i => iodev_rsp(IODEV_UART0),
dev_11_req_o => iodev_req(IODEV_MTIME), dev_11_rsp_i => iodev_rsp(IODEV_MTIME),
dev_12_req_o => iodev_req(IODEV_XIRQ), dev_12_rsp_i => iodev_rsp(IODEV_XIRQ),
dev_13_req_o => iodev_req(IODEV_ONEWIRE), dev_13_rsp_i => iodev_rsp(IODEV_ONEWIRE),
dev_14_req_o => iodev_req(IODEV_GPTMR), dev_14_rsp_i => iodev_rsp(IODEV_GPTMR),
dev_15_req_o => iodev_req(IODEV_PWM), dev_15_rsp_i => iodev_rsp(IODEV_PWM),
dev_16_req_o => iodev_req(IODEV_XIP), dev_16_rsp_i => iodev_rsp(IODEV_XIP),
dev_17_req_o => iodev_req(IODEV_CRC), dev_17_rsp_i => iodev_rsp(IODEV_CRC),
dev_18_req_o => iodev_req(IODEV_DMA), dev_18_rsp_i => iodev_rsp(IODEV_DMA),
dev_19_req_o => iodev_req(IODEV_SLINK), dev_19_rsp_i => iodev_rsp(IODEV_SLINK),
dev_20_req_o => iodev_req(IODEV_CFS), dev_20_rsp_i => iodev_rsp(IODEV_CFS),
dev_21_req_o => open, dev_21_rsp_i => rsp_terminate_c, -- reserved
dev_22_req_o => open, dev_22_rsp_i => rsp_terminate_c, -- reserved
dev_23_req_o => open, dev_23_rsp_i => rsp_terminate_c, -- reserved
dev_24_req_o => open, dev_24_rsp_i => rsp_terminate_c, -- reserved
dev_25_req_o => open, dev_25_rsp_i => rsp_terminate_c, -- reserved
dev_26_req_o => open, dev_26_rsp_i => rsp_terminate_c, -- reserved
dev_27_req_o => open, dev_27_rsp_i => rsp_terminate_c, -- reserved
dev_28_req_o => open, dev_28_rsp_i => rsp_terminate_c, -- reserved
dev_29_req_o => open, dev_29_rsp_i => rsp_terminate_c, -- reserved
dev_30_req_o => open, dev_30_rsp_i => rsp_terminate_c, -- reserved
dev_31_req_o => open, dev_31_rsp_i => rsp_terminate_c -- reserved
dev_00_req_o => iodev_req(IODEV_BOOTROM), dev_00_rsp_i => iodev_rsp(IODEV_BOOTROM),
dev_01_req_o => open, dev_01_rsp_i => rsp_terminate_c, -- reserved
dev_02_req_o => open, dev_02_rsp_i => rsp_terminate_c, -- reserved
dev_03_req_o => open, dev_03_rsp_i => rsp_terminate_c, -- reserved
dev_04_req_o => open, dev_04_rsp_i => rsp_terminate_c, -- reserved
dev_05_req_o => open, dev_05_rsp_i => rsp_terminate_c, -- reserved
dev_06_req_o => open, dev_06_rsp_i => rsp_terminate_c, -- reserved
dev_07_req_o => open, dev_07_rsp_i => rsp_terminate_c, -- reserved
dev_08_req_o => open, dev_08_rsp_i => rsp_terminate_c, -- reserved
dev_09_req_o => open, dev_09_rsp_i => rsp_terminate_c, -- reserved
dev_10_req_o => iodev_req(IODEV_TWD), dev_10_rsp_i => iodev_rsp(IODEV_TWD),
dev_11_req_o => iodev_req(IODEV_CFS), dev_11_rsp_i => iodev_rsp(IODEV_CFS),
dev_12_req_o => iodev_req(IODEV_SLINK), dev_12_rsp_i => iodev_rsp(IODEV_SLINK),
dev_13_req_o => iodev_req(IODEV_DMA), dev_13_rsp_i => iodev_rsp(IODEV_DMA),
dev_14_req_o => iodev_req(IODEV_CRC), dev_14_rsp_i => iodev_rsp(IODEV_CRC),
dev_15_req_o => iodev_req(IODEV_XIP), dev_15_rsp_i => iodev_rsp(IODEV_XIP),
dev_16_req_o => iodev_req(IODEV_PWM), dev_16_rsp_i => iodev_rsp(IODEV_PWM),
dev_17_req_o => iodev_req(IODEV_GPTMR), dev_17_rsp_i => iodev_rsp(IODEV_GPTMR),
dev_18_req_o => iodev_req(IODEV_ONEWIRE), dev_18_rsp_i => iodev_rsp(IODEV_ONEWIRE),
dev_19_req_o => iodev_req(IODEV_XIRQ), dev_19_rsp_i => iodev_rsp(IODEV_XIRQ),
dev_20_req_o => iodev_req(IODEV_CLINT), dev_20_rsp_i => iodev_rsp(IODEV_CLINT),
dev_21_req_o => iodev_req(IODEV_UART0), dev_21_rsp_i => iodev_rsp(IODEV_UART0),
dev_22_req_o => iodev_req(IODEV_UART1), dev_22_rsp_i => iodev_rsp(IODEV_UART1),
dev_23_req_o => iodev_req(IODEV_SDI), dev_23_rsp_i => iodev_rsp(IODEV_SDI),
dev_24_req_o => iodev_req(IODEV_SPI), dev_24_rsp_i => iodev_rsp(IODEV_SPI),
dev_25_req_o => iodev_req(IODEV_TWI), dev_25_rsp_i => iodev_rsp(IODEV_TWI),
dev_26_req_o => iodev_req(IODEV_TRNG), dev_26_rsp_i => iodev_rsp(IODEV_TRNG),
dev_27_req_o => iodev_req(IODEV_WDT), dev_27_rsp_i => iodev_rsp(IODEV_WDT),
dev_28_req_o => iodev_req(IODEV_GPIO), dev_28_rsp_i => iodev_rsp(IODEV_GPIO),
dev_29_req_o => iodev_req(IODEV_NEOLED), dev_29_rsp_i => iodev_rsp(IODEV_NEOLED),
dev_30_req_o => iodev_req(IODEV_SYSINFO), dev_30_rsp_i => iodev_rsp(IODEV_SYSINFO),
dev_31_req_o => iodev_req(IODEV_OCD), dev_31_rsp_i => iodev_rsp(IODEV_OCD)
);
-- Processor-Internal Bootloader ROM (BOOTROM) --------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_boot_rom_inst_true:
if bootrom_en_c generate
neorv32_boot_rom_inst: entity neorv32.neorv32_boot_rom
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
bus_req_i => iodev_req(IODEV_BOOTROM),
bus_rsp_o => iodev_rsp(IODEV_BOOTROM)
);
end generate;
neorv32_boot_rom_inst_false:
if not bootrom_en_c generate
iodev_rsp(IODEV_BOOTROM) <= rsp_terminate_c;
end generate;
-- Custom Functions Subsystem (CFS) -------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_cfs_inst_true:
@ -1173,8 +1199,6 @@ begin
rstn_sys_i => rstn_sys,
bus_req_i => iodev_req(IODEV_WDT),
bus_rsp_o => iodev_rsp(IODEV_WDT),
cpu_debug_i => cpu_debug,
cpu_sleep_i => cpu_sleep,
clkgen_en_o => clk_gen_en(CG_WDT),
clkgen_i => clk_gen,
rstn_o => rstn_wdt
@ -1189,26 +1213,31 @@ begin
end generate;
-- Machine System Timer (MTIME) -----------------------------------------------------------
-- Core Local Interruptor (CLINT) ---------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_mtime_inst_true:
if IO_MTIME_EN generate
neorv32_mtime_inst: entity neorv32.neorv32_mtime
neorv32_clint_inst_true:
if IO_CLINT_EN generate
neorv32_clint_inst: entity neorv32.neorv32_clint
generic map (
NUM_HARTS => num_cores_c
)
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
bus_req_i => iodev_req(IODEV_MTIME),
bus_rsp_o => iodev_rsp(IODEV_MTIME),
bus_req_i => iodev_req(IODEV_CLINT),
bus_rsp_o => iodev_rsp(IODEV_CLINT),
time_o => mtime_time_o,
irq_o => mtime_irq
mti_o => mtime_irq,
msi_o => msw_irq
);
end generate;
neorv32_mtime_inst_false:
if not IO_MTIME_EN generate
iodev_rsp(IODEV_MTIME) <= rsp_terminate_c;
neorv32_clint_inst_false:
if not IO_CLINT_EN generate
iodev_rsp(IODEV_CLINT) <= rsp_terminate_c;
mtime_time_o <= (others => '0');
mtime_irq <= mtime_irq_i;
mtime_irq <= (others => mtime_irq_i);
msw_irq <= (others => msw_irq_i);
end generate;
@ -1355,6 +1384,39 @@ begin
end generate;
-- Two-Wire Device (TWD) ------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_twd_inst_true:
if IO_TWD_EN generate
neorv32_twd_inst: entity neorv32.neorv32_twd
generic map (
TWD_FIFO => IO_TWD_FIFO
)
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
bus_req_i => iodev_req(IODEV_TWD),
bus_rsp_o => iodev_rsp(IODEV_TWD),
clkgen_en_o => clk_gen_en(CG_TWD),
clkgen_i => clk_gen,
twd_sda_i => twd_sda_i,
twd_sda_o => twd_sda_o,
twd_scl_i => twd_scl_i,
twd_scl_o => twd_scl_o,
irq_o => firq(FIRQ_TWD)
);
end generate;
neorv32_twd_inst_false:
if not IO_TWD_EN generate
iodev_rsp(IODEV_TWD) <= rsp_terminate_c;
twd_sda_o <= '1';
twd_scl_o <= '1';
clk_gen_en(CG_TWD) <= '0';
firq(FIRQ_TWD) <= '0';
end generate;
-- Pulse-Width Modulation Controller (PWM) ------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_pwm_inst_true:
@ -1388,21 +1450,19 @@ begin
if IO_TRNG_EN generate
neorv32_trng_inst: entity neorv32.neorv32_trng
generic map (
IO_TRNG_FIFO => IO_TRNG_FIFO
TRNG_FIFO => IO_TRNG_FIFO
)
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
bus_req_i => iodev_req(IODEV_TRNG),
bus_rsp_o => iodev_rsp(IODEV_TRNG),
irq_o => firq(FIRQ_TRNG)
bus_rsp_o => iodev_rsp(IODEV_TRNG)
);
end generate;
neorv32_trng_inst_false:
if not IO_TRNG_EN generate
iodev_rsp(IODEV_TRNG) <= rsp_terminate_c;
firq(FIRQ_TRNG) <= '0';
end generate;
@ -1441,14 +1501,14 @@ begin
if io_xirq_en_c generate
neorv32_xirq_inst: entity neorv32.neorv32_xirq
generic map (
XIRQ_NUM_CH => XIRQ_NUM_CH
NUM_CH => XIRQ_NUM_CH
)
port map (
clk_i => clk_i,
rstn_i => rstn_sys,
bus_req_i => iodev_req(IODEV_XIRQ),
bus_rsp_o => iodev_rsp(IODEV_XIRQ),
xirq_i => xirq_i,
xirq_i => xirq_i(XIRQ_NUM_CH-1 downto 0),
cpu_irq_o => firq(FIRQ_XIRQ)
);
end generate;
@ -1581,8 +1641,8 @@ begin
if io_sysinfo_en_c generate
neorv32_sysinfo_inst: entity neorv32.neorv32_sysinfo
generic map (
NUM_HARTS => num_cores_c,
CLOCK_FREQUENCY => CLOCK_FREQUENCY,
CLOCK_GATING_EN => CLOCK_GATING_EN,
BOOT_MODE_SELECT => BOOT_MODE_SELECT,
INT_BOOTLOADER_EN => bootrom_en_c,
MEM_INT_IMEM_EN => MEM_INT_IMEM_EN,
@ -1607,12 +1667,13 @@ begin
OCD_EN => OCD_EN,
OCD_AUTHENTICATION => OCD_AUTHENTICATION,
IO_GPIO_EN => io_gpio_en_c,
IO_MTIME_EN => IO_MTIME_EN,
IO_CLINT_EN => IO_CLINT_EN,
IO_UART0_EN => IO_UART0_EN,
IO_UART1_EN => IO_UART1_EN,
IO_SPI_EN => IO_SPI_EN,
IO_SDI_EN => IO_SDI_EN,
IO_TWI_EN => IO_TWI_EN,
IO_TWD_EN => IO_TWD_EN,
IO_PWM_EN => io_pwm_en_c,
IO_WDT_EN => IO_WDT_EN,
IO_TRNG_EN => IO_TRNG_EN,
@ -1645,6 +1706,7 @@ begin
-- **************************************************************************************************************************
-- On-Chip Debugger Complex
-- **************************************************************************************************************************
neorv32_ocd_inst_true:
if OCD_EN generate
@ -1671,19 +1733,18 @@ begin
-- -------------------------------------------------------------------------------------------
neorv32_debug_dm_inst: entity neorv32.neorv32_debug_dm
generic map (
CPU_BASE_ADDR => base_io_dm_c,
NUM_HARTS => num_cores_c,
AUTHENTICATOR => OCD_AUTHENTICATION
)
port map (
clk_i => clk_i,
rstn_i => rstn_ext,
cpu_debug_i => cpu_debug,
dmi_req_i => dmi_req,
dmi_rsp_o => dmi_rsp,
bus_req_i => iodev_req(IODEV_OCD),
bus_rsp_o => iodev_rsp(IODEV_OCD),
cpu_ndmrstn_o => dci_ndmrstn,
cpu_halt_req_o => dci_haltreq
clk_i => clk_i,
rstn_i => rstn_ext,
dmi_req_i => dmi_req,
dmi_rsp_o => dmi_rsp,
bus_req_i => iodev_req(IODEV_OCD),
bus_rsp_o => iodev_rsp(IODEV_OCD),
ndmrstn_o => dci_ndmrstn,
halt_req_o => dci_haltreq
);
end generate;
@ -1693,7 +1754,7 @@ begin
iodev_rsp(IODEV_OCD) <= rsp_terminate_c;
jtag_tdo_o <= jtag_tdi_i; -- JTAG pass-through
dci_ndmrstn <= '1';
dci_haltreq <= '0';
dci_haltreq <= (others => '0');
end generate;

View file

@ -19,14 +19,13 @@ use neorv32.neorv32_package.all;
entity neorv32_trng is
generic (
IO_TRNG_FIFO : natural range 1 to 2**15 -- RND fifo depth, has to be a power of two, min 1
TRNG_FIFO : natural range 1 to 2**15 -- FIFO depth, has to be a power of two, min 1
);
port (
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active, async
bus_req_i : in bus_req_t; -- bus request
bus_rsp_o : out bus_rsp_t; -- bus response
irq_o : out std_ulogic -- data-available interrupt
bus_rsp_o : out bus_rsp_t -- bus response
);
end neorv32_trng;
@ -38,17 +37,16 @@ architecture neorv32_trng_rtl of neorv32_trng is
-- ------------------------------------------------------------------------------------------------
-- control register bits --
constant ctrl_data_lsb_c : natural := 0; -- r/-: Random data byte LSB
constant ctrl_data_msb_c : natural := 7; -- r/-: Random data byte MSB
--
constant ctrl_fifo_size0_c : natural := 16; -- r/-: log2(FIFO size) bit 0
constant ctrl_fifo_size3_c : natural := 19; -- r/-: log2(FIFO size) bit 3
--
constant ctrl_irq_sel_c : natural := 27; -- r/w: interrupt select (0 = data available, 1 = FIFO full)
constant ctrl_fifo_clr_c : natural := 28; -- -/w: Clear data FIFO (auto clears)
constant ctrl_sim_mode_c : natural := 29; -- r/-: TRNG implemented in pseudo-RNG simulation mode
constant ctrl_en_c : natural := 30; -- r/w: TRNG enable
constant ctrl_valid_c : natural := 31; -- r/-: Output data valid
constant ctrl_en_c : natural := 0; -- r/w: TRNG enable
constant ctrl_fifo_clr_c : natural := 1; -- -/w: Clear data FIFO (auto clears)
constant ctrl_fifo_size0_c : natural := 2; -- r/-: log2(FIFO size) bit 0, LSB
constant ctrl_fifo_size3_c : natural := 5; -- r/-: log2(FIFO size) bit 3, MSB
constant ctrl_sim_mode_c : natural := 6; -- r/-: TRNG implemented in pseudo-RNG simulation mode
constant ctrl_avail_c : natural := 7; -- r/-: Random data available
-- data register bits --
constant ctrl_data_lsb_c : natural := 0; -- r/-: random data bit 0, LSB
constant ctrl_data_msb_c : natural := 7; -- r/-: random data bit 7, MSB
-- neoTRNG true random number generator --
component neoTRNG
@ -66,10 +64,10 @@ architecture neorv32_trng_rtl of neorv32_trng is
);
end component;
-- control --
signal enable, irq_sel, fifo_clr : std_ulogic;
-- control register --
signal enable, fifo_clr : std_ulogic;
-- data FIFO --
-- data FIFO interface --
type fifo_t is record
we : std_ulogic; -- write enable
re : std_ulogic; -- read enable
@ -91,7 +89,6 @@ begin
if (rstn_i = '0') then
bus_rsp_o <= rsp_terminate_c;
fifo_clr <= '0';
irq_sel <= '0';
enable <= '0';
elsif rising_edge(clk_i) then
-- defaults --
@ -101,19 +98,22 @@ begin
fifo_clr <= '0'; -- auto-clear
-- host access --
if (bus_req_i.stb = '1') then
if (bus_req_i.rw = '1') then -- write access
irq_sel <= bus_req_i.data(ctrl_irq_sel_c);
fifo_clr <= bus_req_i.data(ctrl_fifo_clr_c);
if (bus_req_i.rw = '1') then -- write access (control register)
enable <= bus_req_i.data(ctrl_en_c);
fifo_clr <= bus_req_i.data(ctrl_fifo_clr_c);
else -- read access
bus_rsp_o.data(ctrl_data_msb_c downto ctrl_data_lsb_c) <= fifo.rdata;
--
bus_rsp_o.data(ctrl_fifo_size3_c downto ctrl_fifo_size0_c) <= std_ulogic_vector(to_unsigned(index_size_f(IO_TRNG_FIFO), 4));
--
bus_rsp_o.data(ctrl_irq_sel_c) <= irq_sel;
bus_rsp_o.data(ctrl_sim_mode_c) <= bool_to_ulogic_f(is_simulation_c);
bus_rsp_o.data(ctrl_en_c) <= enable;
bus_rsp_o.data(ctrl_valid_c) <= fifo.avail;
if (bus_req_i.addr(2) = '0') then -- control register
bus_rsp_o.data(ctrl_en_c) <= enable;
bus_rsp_o.data(ctrl_fifo_size3_c downto ctrl_fifo_size0_c) <= std_ulogic_vector(to_unsigned(index_size_f(TRNG_FIFO), 4));
bus_rsp_o.data(ctrl_sim_mode_c) <= bool_to_ulogic_f(is_simulation_c);
bus_rsp_o.data(ctrl_avail_c) <= fifo.avail;
else -- data register
if (fifo.avail = '0') then -- output zero if no data available
bus_rsp_o.data(ctrl_data_msb_c downto ctrl_data_lsb_c) <= (others => '0');
else
bus_rsp_o.data(ctrl_data_msb_c downto ctrl_data_lsb_c) <= fifo.rdata;
end if;
end if;
end if;
end if;
end if;
@ -141,11 +141,11 @@ begin
-- -------------------------------------------------------------------------------------------
rnd_pool_fifo_inst: entity neorv32.neorv32_fifo
generic map (
FIFO_DEPTH => IO_TRNG_FIFO, -- number of fifo entries; has to be a power of two; min 1
FIFO_WIDTH => 8, -- size of data elements in fifo
FIFO_RSYNC => true, -- sync read
FIFO_SAFE => true, -- safe access
FULL_RESET => false -- no HW reset, try to infer BRAM
FIFO_DEPTH => TRNG_FIFO, -- number of FIFO entries; has to be a power of two; min 1
FIFO_WIDTH => 8, -- size of data elements in FIFO
FIFO_RSYNC => true, -- sync read
FIFO_SAFE => true, -- safe access
FULL_RESET => false -- no HW reset, try to infer BRAM
)
port map (
-- control --
@ -164,25 +164,7 @@ begin
);
fifo.clear <= '1' when (enable = '0') or (fifo_clr = '1') else '0';
fifo.re <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '0') else '0';
-- IRQ generator --
irq_generator: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
irq_o <= '0';
elsif rising_edge(clk_i) then
if (enable = '1') then
if (irq_sel = '0') then -- fire IRQ if any data is available
irq_o <= fifo.avail;
else -- fire IRQ if data FIFO is full
irq_o <= not fifo.free;
end if;
else
irq_o <= '0';
end if;
end if;
end process irq_generator;
fifo.re <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '0') and (bus_req_i.addr(2) = '1') else '0';
end neorv32_trng_rtl;

441
rtl/core/neorv32_twd.vhd Normal file
View file

@ -0,0 +1,441 @@
-- ================================================================================ --
-- NEORV32 SoC - Two-Wire Device (TWD) --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library neorv32;
use neorv32.neorv32_package.all;
entity neorv32_twd is
generic (
TWD_FIFO : natural range 1 to 2**15 -- RTX FIFO depth, has to be a power of two, min 1
);
port (
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active, async
bus_req_i : in bus_req_t; -- bus request
bus_rsp_o : out bus_rsp_t; -- bus response
clkgen_en_o : out std_ulogic; -- enable clock generator
clkgen_i : in std_ulogic_vector(7 downto 0);
twd_sda_i : in std_ulogic; -- serial data line input
twd_sda_o : out std_ulogic; -- serial data line output
twd_scl_i : in std_ulogic; -- serial clock line input
twd_scl_o : out std_ulogic; -- serial clock line output
irq_o : out std_ulogic -- interrupt
);
end neorv32_twd;
architecture neorv32_twd_rtl of neorv32_twd is
-- control register --
constant ctrl_en_c : natural := 0; -- r/w: module enable (reset when zero)
constant ctrl_clr_rx_c : natural := 1; -- -/w: clear RX FIFO (flag auto-clears)
constant ctrl_clr_tx_c : natural := 2; -- -/w: clear TX FIFO (flag auto-clears)
constant ctrl_fsel_c : natural := 3; -- r/w: input filter / sample clock select
constant ctrl_dev_addr0_c : natural := 4; -- r/w: device address, bit 0 (LSB)
constant ctrl_dev_addr6_c : natural := 10; -- r/w: device address, bit 6 (MSB)
constant ctrl_irq_rx_avail_c : natural := 11; -- r/w: IRQ if RX FIFO data available
constant ctrl_irq_rx_full_c : natural := 12; -- r/w: IRQ if RX FIFO full
constant ctrl_irq_tx_empty_c : natural := 13; -- r/w: IRQ if TX FIFO empty
--
constant ctrl_fifo_size0_c : natural := 15; -- r/-: log2(FIFO size), bit 0 (LSB)
constant ctrl_fifo_size3_c : natural := 18; -- r/-: log2(FIFO size), bit 3 (MSB)
--
constant ctrl_rx_avail_c : natural := 25; -- r/-: RX FIFO data available
constant ctrl_rx_full_c : natural := 26; -- r/-: RX FIFO full
constant ctrl_tx_empty_c : natural := 27; -- r/-: TX FIFO empty
constant ctrl_tx_full_c : natural := 28; -- r/-: TX FIFO full
constant ctrl_sense_scl_c : natural := 29; -- r/-: current state of the SCL bus line
constant ctrl_sense_sda_c : natural := 30; -- r/-: current state of the SDA bus line
constant ctrl_busy_c : natural := 31; -- r/-: bus engine is busy (transaction in progress)
-- control register --
type ctrl_t is record
enable : std_ulogic;
clr_rx : std_ulogic;
clr_tx : std_ulogic;
fsel : std_ulogic;
device_addr : std_ulogic_vector(6 downto 0);
irq_rx_avail : std_ulogic;
irq_rx_full : std_ulogic;
irq_tx_empty : std_ulogic;
end record;
signal ctrl : ctrl_t;
-- bus sample logic --
type smp_t is record
clk_en : std_ulogic; -- sample clock
valid : std_ulogic; -- valid sample
sda_sreg : std_ulogic_vector(2 downto 0); -- synchronizer
scl_sreg : std_ulogic_vector(2 downto 0); -- synchronizer
sda : std_ulogic; -- current SDA state
scl : std_ulogic; -- current SCL state
scl_rise : std_ulogic; -- SCL rising edge
scl_fall : std_ulogic; -- SCL falling edge
start : std_ulogic; -- start condition
stop : std_ulogic; -- stop condition
end record;
signal smp : smp_t;
-- FIFO interface --
type fifo_t is record
clr : std_ulogic; -- sync reset, high-active
we : std_ulogic; -- write enable
re : std_ulogic; -- read enable
wdata : std_ulogic_vector(7 downto 0); -- write data
rdata : std_ulogic_vector(7 downto 0); -- read data
avail : std_ulogic; -- data available?
free : std_ulogic; -- free entry available?
end record;
signal rx_fifo, tx_fifo : fifo_t;
-- bus engine --
type state_t is (S_IDLE, S_INIT, S_ADDR, S_RESP, S_RTX, S_ACK);
type engine_t is record
state : state_t; -- FSM state
cnt : unsigned(3 downto 0); -- bit counter
sreg : std_ulogic_vector(7 downto 0); -- shift register
cmd : std_ulogic; -- 0 = write, 1 = read
rdata : std_ulogic_vector(7 downto 0); -- read-access data
dout : std_ulogic; -- output bit
busy : std_ulogic; -- bus operation in progress
wr_we : std_ulogic; -- write write-enable
rd_re : std_ulogic; -- read read-enable
end record;
signal engine : engine_t;
begin
-- Bus Access -----------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
bus_access: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
bus_rsp_o <= rsp_terminate_c;
ctrl.enable <= '0';
ctrl.clr_rx <= '0';
ctrl.clr_tx <= '0';
ctrl.fsel <= '0';
ctrl.device_addr <= (others => '0');
ctrl.irq_rx_avail <= '0';
ctrl.irq_rx_full <= '0';
ctrl.irq_tx_empty <= '0';
elsif rising_edge(clk_i) then
-- bus handshake defaults --
bus_rsp_o.ack <= bus_req_i.stb;
bus_rsp_o.err <= '0';
bus_rsp_o.data <= (others => '0');
-- read/write access --
ctrl.clr_rx <= '0'; -- auto-clear
ctrl.clr_tx <= '0'; -- auto-clear
if (bus_req_i.stb = '1') then
if (bus_req_i.rw = '1') then -- write access
if (bus_req_i.addr(2) = '0') then -- control register
ctrl.enable <= bus_req_i.data(ctrl_en_c);
ctrl.clr_rx <= bus_req_i.data(ctrl_clr_rx_c);
ctrl.clr_tx <= bus_req_i.data(ctrl_clr_tx_c);
ctrl.fsel <= bus_req_i.data(ctrl_fsel_c);
ctrl.device_addr <= bus_req_i.data(ctrl_dev_addr6_c downto ctrl_dev_addr0_c);
ctrl.irq_rx_avail <= bus_req_i.data(ctrl_irq_rx_avail_c);
ctrl.irq_rx_full <= bus_req_i.data(ctrl_irq_rx_full_c);
ctrl.irq_tx_empty <= bus_req_i.data(ctrl_irq_tx_empty_c);
end if;
else -- read access
if (bus_req_i.addr(2) = '0') then -- control register
bus_rsp_o.data(ctrl_en_c) <= ctrl.enable;
bus_rsp_o.data(ctrl_fsel_c) <= ctrl.fsel;
bus_rsp_o.data(ctrl_dev_addr6_c downto ctrl_dev_addr0_c) <= ctrl.device_addr;
bus_rsp_o.data(ctrl_irq_rx_avail_c) <= ctrl.irq_rx_avail;
bus_rsp_o.data(ctrl_irq_rx_full_c) <= ctrl.irq_rx_full;
bus_rsp_o.data(ctrl_irq_tx_empty_c) <= ctrl.irq_tx_empty;
--
bus_rsp_o.data(ctrl_fifo_size3_c downto ctrl_fifo_size0_c) <= std_ulogic_vector(to_unsigned(index_size_f(TWD_FIFO), 4));
bus_rsp_o.data(ctrl_rx_avail_c) <= rx_fifo.avail;
bus_rsp_o.data(ctrl_rx_full_c) <= not rx_fifo.free;
bus_rsp_o.data(ctrl_tx_empty_c) <= not tx_fifo.avail;
bus_rsp_o.data(ctrl_tx_full_c) <= not tx_fifo.free;
bus_rsp_o.data(ctrl_sense_scl_c) <= smp.scl;
bus_rsp_o.data(ctrl_sense_sda_c) <= smp.sda;
bus_rsp_o.data(ctrl_busy_c) <= engine.busy;
else -- RX FIFO
bus_rsp_o.data(7 downto 0) <= rx_fifo.rdata;
end if;
end if;
end if;
end if;
end process bus_access;
-- enable SoC clock generator --
clkgen_en_o <= ctrl.enable;
-- Data FIFO ("Ring Buffer") --------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
-- TX FIFO --
tx_fifo_inst: entity neorv32.neorv32_fifo
generic map (
FIFO_DEPTH => TWD_FIFO,
FIFO_WIDTH => 8,
FIFO_RSYNC => true,
FIFO_SAFE => true,
FULL_RESET => false
)
port map (
-- control --
clk_i => clk_i,
rstn_i => rstn_i,
clear_i => tx_fifo.clr,
half_o => open,
-- write port --
wdata_i => tx_fifo.wdata,
we_i => tx_fifo.we,
free_o => tx_fifo.free,
-- read port --
re_i => tx_fifo.re,
rdata_o => tx_fifo.rdata,
avail_o => tx_fifo.avail
);
tx_fifo.clr <= '1' when (ctrl.enable = '0') or (ctrl.clr_tx = '1') else '0';
tx_fifo.we <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '1') and (bus_req_i.addr(2) = '1') else '0';
tx_fifo.wdata <= bus_req_i.data(7 downto 0);
tx_fifo.re <= engine.rd_re;
engine.rdata <= tx_fifo.rdata when (tx_fifo.avail = '1') else (others => '1'); -- read ones when TX FIFO is drained
-- RX FIFO --
rx_fifo_inst: entity neorv32.neorv32_fifo
generic map (
FIFO_DEPTH => TWD_FIFO,
FIFO_WIDTH => 8,
FIFO_RSYNC => true,
FIFO_SAFE => true,
FULL_RESET => false
)
port map (
-- control --
clk_i => clk_i,
rstn_i => rstn_i,
clear_i => rx_fifo.clr,
half_o => open,
-- write port --
wdata_i => rx_fifo.wdata,
we_i => rx_fifo.we,
free_o => rx_fifo.free,
-- read port --
re_i => rx_fifo.re,
rdata_o => rx_fifo.rdata,
avail_o => rx_fifo.avail
);
rx_fifo.clr <= '1' when (ctrl.enable = '0') or (ctrl.clr_rx = '1') else '0';
rx_fifo.wdata <= engine.sreg;
rx_fifo.we <= engine.wr_we;
rx_fifo.re <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '0') and (bus_req_i.addr(2) = '1') else '0';
-- Interrupt Generator --
irq_trigger: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
irq_o <= '0';
elsif rising_edge(clk_i) then
irq_o <= ctrl.enable and (
(ctrl.irq_rx_avail and rx_fifo.avail) or -- RX FIFO data available
(ctrl.irq_rx_full and (not rx_fifo.free)) or -- RX FIFO full
(ctrl.irq_tx_empty and (not tx_fifo.avail))); -- TX FIFO empty
end if;
end process irq_trigger;
-- Bus Sample Logic -----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
synchronizer: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
smp.sda_sreg <= (others => '0');
smp.scl_sreg <= (others => '0');
smp.valid <= '0';
elsif rising_edge(clk_i) then
-- input register --
smp.sda_sreg(0) <= to_stdulogic(to_bit(twd_sda_i)); -- "to_bit" to avoid hardware-vs-simulation mismatch
smp.scl_sreg(0) <= to_stdulogic(to_bit(twd_scl_i));
-- sample register --
smp.valid <= '0';
if (ctrl.enable = '1') then
if (smp.clk_en = '1') then
smp.valid <= '1'; -- valid sample
smp.sda_sreg(2 downto 1) <= smp.sda_sreg(1 downto 0);
smp.scl_sreg(2 downto 1) <= smp.scl_sreg(1 downto 0);
end if;
else
smp.sda_sreg(2 downto 1) <= (others => '1');
smp.scl_sreg(2 downto 1) <= (others => '1');
end if;
end if;
end process synchronizer;
-- sample clock for input "filtering" --
smp.clk_en <= clkgen_i(clk_div64_c) when (ctrl.fsel = '1') else clkgen_i(clk_div8_c);
-- bus event detector (event signals are "single-shot") --
bus_event: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
smp.sda <= '0';
smp.scl <= '0';
smp.scl_rise <= '0';
smp.scl_fall <= '0';
smp.start <= '0';
smp.stop <= '0';
elsif rising_edge(clk_i) then
smp.sda <= smp.sda_sreg(2) or smp.sda_sreg(1);
smp.scl <= smp.sda_sreg(2) or smp.sda_sreg(1);
smp.scl_rise <= smp.valid and (not smp.scl_sreg(2)) and ( smp.scl_sreg(1)); -- rising edge
smp.scl_fall <= smp.valid and ( smp.scl_sreg(2)) and (not smp.scl_sreg(1)); -- falling edge
smp.start <= smp.valid and smp.scl_sreg(2) and smp.scl_sreg(1) and ( smp.sda_sreg(2)) and (not smp.sda_sreg(1));
smp.stop <= smp.valid and smp.scl_sreg(2) and smp.scl_sreg(1) and (not smp.sda_sreg(2)) and ( smp.sda_sreg(1));
end if;
end process bus_event;
-- Bus Engine -----------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
bus_engine: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
twd_sda_o <= '1';
twd_scl_o <= '1';
engine.state <= S_IDLE;
engine.cnt <= (others => '0');
engine.sreg <= (others => '1');
engine.cmd <= '0';
engine.dout <= '0';
engine.wr_we <= '0';
engine.rd_re <= '0';
elsif rising_edge(clk_i) then
-- keep bus idle by default --
twd_sda_o <= '1';
twd_scl_o <= '1';
-- defaults --
engine.wr_we <= '0';
engine.rd_re <= '0';
-- fsm --
case engine.state is
when S_IDLE => -- idle, wait for start condition
-- ------------------------------------------------------------
if (ctrl.enable = '1') and (smp.start = '1') then
engine.state <= S_INIT;
end if;
when S_INIT => -- (re-)initialize new transaction
-- ------------------------------------------------------------
engine.cnt <= (others => '0');
engine.sreg <= (others => '0');
if (ctrl.enable = '0') or (smp.stop = '1') then -- disabled or stop-condition received?
engine.state <= S_IDLE;
else
engine.state <= S_ADDR;
end if;
when S_ADDR => -- sample address + R/W bit and check if address match
-- ------------------------------------------------------------
if (ctrl.enable = '0') or (smp.stop = '1') then -- disabled or stop-condition received?
engine.state <= S_IDLE;
elsif (smp.start = '1') then -- start-condition received?
engine.state <= S_INIT;
elsif (engine.cnt(3) = '1') and (smp.scl_fall = '1') then -- 8 bits received?
if (ctrl.device_addr = engine.sreg(7 downto 1)) then -- address match?
engine.state <= S_RESP; -- access device
else
engine.state <= S_IDLE; -- no match, go back to idle
end if;
end if;
-- sample bus on rising edge --
if (smp.scl_rise = '1') then
engine.sreg <= engine.sreg(6 downto 0) & smp.sda;
engine.cnt <= engine.cnt + 1;
end if;
when S_RESP => -- send device address match ACK
-- ------------------------------------------------------------
twd_sda_o <= '0'; -- ACK
engine.cnt <= (others => '0');
engine.cmd <= engine.sreg(0);
if (ctrl.enable = '0') then -- disabled?
engine.state <= S_IDLE;
elsif (smp.scl_fall = '1') then
engine.state <= S_RTX;
end if;
-- get FIFO TX data (required for read access only) --
if (smp.scl_fall = '1') then
engine.sreg <= engine.rdata; -- FIFO TX data
engine.dout <= engine.rdata(7); -- FIFO TX data (first bit)
end if;
when S_RTX => -- receive/transmit 8 data bits
-- ------------------------------------------------------------
if (ctrl.enable = '0') or (smp.stop = '1') then -- disabled or stop-condition
engine.state <= S_IDLE;
elsif (smp.start = '1') then -- start-condition
engine.state <= S_INIT; -- restart transaction
elsif (engine.cnt(3) = '1') and (smp.scl_fall = '1') then -- 8 bits received?
engine.wr_we <= '1'; -- write byte to RX FIFO
engine.state <= S_ACK;
end if;
-- sample bus on rising edge --
if (smp.scl_rise = '1') then
engine.sreg <= engine.sreg(6 downto 0) & smp.sda;
engine.cnt <= engine.cnt + 1;
end if;
-- update bus on falling edge --
twd_sda_o <= engine.dout;
if (smp.scl_fall = '1') and (engine.cmd = '1') then
engine.dout <= engine.sreg(7);
end if;
when S_ACK => -- receive/transmit ACK/NACK
-- ------------------------------------------------------------
engine.cnt <= (others => '0');
engine.sreg <= engine.rdata; -- FIFO TX data
engine.dout <= engine.rdata(7); -- FIFO TX data (first bit)
if (ctrl.enable = '0') then -- disabled?
engine.state <= S_IDLE;
elsif (smp.scl_fall = '1') then
engine.state <= S_RTX;
end if;
-- [READ] advance to next data byte if ACK is send by host --
if (engine.cmd = '1') and (smp.scl_rise = '1') and (smp.sda = '0') then
engine.rd_re <= '1'; -- get next TX data byte
end if;
-- [WRITE] transmit ACK --
if (engine.cmd = '0') then
twd_sda_o <= '0';
end if;
when others => -- undefined
-- ------------------------------------------------------------
engine.state <= S_IDLE;
end case;
end if;
end process bus_engine;
-- transaction in progress --
engine.busy <= '0' when (engine.state = S_IDLE) else '1';
end neorv32_twd_rtl;

View file

@ -1,5 +1,5 @@
-- ================================================================================ --
-- NEORV32 SoC - Two-Wire Interface Controller (TWI) --
-- NEORV32 SoC - Two-Wire Interface Host Controller (TWI) --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
@ -38,14 +38,14 @@ architecture neorv32_twi_rtl of neorv32_twi is
-- control register --
constant ctrl_en_c : natural := 0; -- r/w: module enable (reset when zero)
constant ctrl_prsc0_c : natural := 1; -- r/w: CLK prsc bit 0
constant ctrl_prsc2_c : natural := 3; -- r/w: CLK prsc bit 2
constant ctrl_prsc0_c : natural := 1; -- r/w: clock prescaler bit 0
constant ctrl_prsc2_c : natural := 3; -- r/w: clock prescaler bit 2
constant ctrl_cdiv0_c : natural := 4; -- r/w: clock divider bit 0
constant ctrl_cdiv3_c : natural := 7; -- r/w: clock divider bit 3
constant ctrl_clkstr_en_c : natural := 8; -- r/w: enable clock stretching
--
constant ctrl_fifo_size0_c : natural := 15; -- r/-: log2(fifo size), bit 0 (lsb)
constant ctrl_fifo_size3_c : natural := 18; -- r/-: log2(fifo size), bit 3 (msb)
constant ctrl_fifo_size0_c : natural := 15; -- r/-: log2(FIFO size), bit 0 (LSB)
constant ctrl_fifo_size3_c : natural := 18; -- r/-: log2(FIFO size), bit 3 (MSB)
--
constant ctrl_sense_scl_c : natural := 27; -- r/-: current state of the SCL bus line
constant ctrl_sense_sda_c : natural := 28; -- r/-: current state of the SDA bus line

View file

@ -23,8 +23,6 @@ entity neorv32_wdt is
rstn_sys_i : in std_ulogic; -- system reset, low-active
bus_req_i : in bus_req_t; -- bus request
bus_rsp_o : out bus_rsp_t; -- bus response
cpu_debug_i : in std_ulogic; -- CPU is in debug mode
cpu_sleep_i : in std_ulogic; -- CPU is in sleep mode
clkgen_en_o : out std_ulogic; -- enable clock generator
clkgen_i : in std_ulogic_vector(7 downto 0);
rstn_o : out std_ulogic -- timeout reset, low_active, sync
@ -155,8 +153,8 @@ begin
-- valid counter increment? --
cnt_inc <= '1' when ((prsc_tick = '1') and (cnt_started = '1')) and -- clock tick and started
((cpu_debug_i = '0') or (ctrl.dben = '1')) and -- not in debug mode or allowed to run in debug mode
((cpu_sleep_i = '0') or (ctrl.sen = '1')) else '0'; -- not in sleep mode or allowed to run in sleep mode
((bus_req_i.debug = '0') or (ctrl.dben = '1')) and -- not in debug mode or allowed to run in debug mode
((bus_req_i.sleep = '0') or (ctrl.sen = '1')) else '0'; -- not in sleep mode or allowed to run in sleep mode
-- timeout detector --
cnt_timeout <= '1' when (cnt_started = '1') and (cnt = ctrl.timeout) else '0';

View file

@ -99,7 +99,7 @@ begin
bus_rw <= '0';
elsif rising_edge(clk_i) then
if (pending = '0') then -- idle, waiting for request
timeout_cnt <= std_ulogic_vector(to_unsigned(TIMEOUT_VAL, index_size_f(TIMEOUT_VAL)+1));
timeout_cnt <= std_ulogic_vector(to_unsigned(TIMEOUT_VAL, timeout_cnt'length));
pending <= bus_req.stb;
else -- busy, transfer in progress
timeout_cnt <= std_ulogic_vector(unsigned(timeout_cnt) - 1);

View file

@ -21,14 +21,14 @@ use neorv32.neorv32_package.all;
entity neorv32_xirq is
generic (
XIRQ_NUM_CH : natural range 0 to 32 -- number of IRQ channels
NUM_CH : natural range 0 to 32 -- number of IRQ channels
);
port (
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active
bus_req_i : in bus_req_t; -- bus request
bus_rsp_o : out bus_rsp_t; -- bus response
xirq_i : in std_ulogic_vector(31 downto 0); -- external IRQ channels
xirq_i : in std_ulogic_vector(NUM_CH-1 downto 0); -- external IRQ channels
cpu_irq_o : out std_ulogic -- CPU interrupt
);
end neorv32_xirq;
@ -42,16 +42,16 @@ architecture neorv32_xirq_rtl of neorv32_xirq is
constant addr_tpol_c : std_ulogic_vector(1 downto 0) := "11"; -- r/w: trigger polarity (high/low or rising/falling)
-- configuration registers --
signal irq_enable, irq_type, irq_polarity : std_ulogic_vector(XIRQ_NUM_CH-1 downto 0);
signal irq_enable, irq_type, irq_polarity : std_ulogic_vector(NUM_CH-1 downto 0);
-- interrupt trigger --
signal irq_sync1, irq_sync2, irq_trig : std_ulogic_vector(XIRQ_NUM_CH-1 downto 0);
signal irq_sync1, irq_sync2, irq_trig : std_ulogic_vector(NUM_CH-1 downto 0);
-- pending interrupt(s) --
signal irq_pending : std_ulogic_vector(XIRQ_NUM_CH-1 downto 0);
signal irq_pending : std_ulogic_vector(NUM_CH-1 downto 0);
-- priority encoder --
type prio_enc_t is array (0 to XIRQ_NUM_CH-1) of std_ulogic_vector(4 downto 0);
type prio_enc_t is array (0 to NUM_CH-1) of std_ulogic_vector(4 downto 0);
signal prio_enc : prio_enc_t;
-- interrupt arbiter --
@ -79,25 +79,25 @@ begin
if (bus_req_i.stb = '1') then
if (bus_req_i.rw = '1') then -- write access
if (bus_req_i.addr(3 downto 2) = addr_eie_c) then -- channel-enable
irq_enable <= bus_req_i.data(XIRQ_NUM_CH-1 downto 0);
irq_enable <= bus_req_i.data(NUM_CH-1 downto 0);
end if;
if (bus_req_i.addr(3 downto 2) = addr_ttyp_c) then -- trigger type
irq_type <= bus_req_i.data(XIRQ_NUM_CH-1 downto 0);
irq_type <= bus_req_i.data(NUM_CH-1 downto 0);
end if;
if (bus_req_i.addr(3 downto 2) = addr_tpol_c) then -- trigger polarity
irq_polarity <= bus_req_i.data(XIRQ_NUM_CH-1 downto 0);
irq_polarity <= bus_req_i.data(NUM_CH-1 downto 0);
end if;
else -- read access
case bus_req_i.addr(3 downto 2) is
when addr_eie_c => -- channel-enable
bus_rsp_o.data(XIRQ_NUM_CH-1 downto 0) <= irq_enable;
bus_rsp_o.data(NUM_CH-1 downto 0) <= irq_enable;
when addr_esc_c =>
bus_rsp_o.data(31) <= irq_state(1); -- active interrupt waiting for ACK
bus_rsp_o.data(4 downto 0) <= irq_source; -- interrupt source (channel number)
when addr_ttyp_c => -- trigger type
bus_rsp_o.data(XIRQ_NUM_CH-1 downto 0) <= irq_type;
bus_rsp_o.data(NUM_CH-1 downto 0) <= irq_type;
when others => -- trigger polarity
bus_rsp_o.data(XIRQ_NUM_CH-1 downto 0) <= irq_polarity;
bus_rsp_o.data(NUM_CH-1 downto 0) <= irq_polarity;
end case;
end if;
end if;
@ -113,14 +113,14 @@ begin
irq_sync1 <= (others => '0');
irq_sync2 <= (others => '0');
elsif rising_edge(clk_i) then
irq_sync1 <= xirq_i(XIRQ_NUM_CH-1 downto 0);
irq_sync1 <= xirq_i(NUM_CH-1 downto 0);
irq_sync2 <= irq_sync1;
end if;
end process synchronizer;
-- trigger type select --
irq_trigger_gen:
for i in 0 to XIRQ_NUM_CH-1 generate
for i in 0 to NUM_CH-1 generate
irq_trigger: process(irq_sync1, irq_sync2, irq_type, irq_polarity)
variable sel_v : std_ulogic_vector(1 downto 0);
begin
@ -143,7 +143,7 @@ begin
if (rstn_i = '0') then
irq_pending <= (others => '0');
elsif rising_edge(clk_i) then
irq_pending <= irq_enable and ((irq_pending and (not irq_clear(XIRQ_NUM_CH-1 downto 0))) or irq_trig);
irq_pending <= irq_enable and ((irq_pending and (not irq_clear(NUM_CH-1 downto 0))) or irq_trig);
end if;
end process irq_buffer;
@ -151,14 +151,14 @@ begin
-- Priority Encoder (structural code: mux-chain) ----------------------------
-- -----------------------------------------------------------------------------
priority_encoder_gen:
for i in 0 to XIRQ_NUM_CH-1 generate -- start with highest priority (=0)
for i in 0 to NUM_CH-1 generate -- start with highest priority (=0)
priority_encoder_gen_chain: -- inside chain
if i < XIRQ_NUM_CH-1 generate
if i < NUM_CH-1 generate
prio_enc(i) <= std_ulogic_vector(to_unsigned(i, 5)) when (irq_pending(i) = '1') else prio_enc(i+1);
end generate;
priority_encoder_gen_last: -- end of chain
if i = XIRQ_NUM_CH-1 generate
prio_enc(XIRQ_NUM_CH-1) <= std_ulogic_vector(to_unsigned(XIRQ_NUM_CH-1, 5)); -- lowest priority
if i = NUM_CH-1 generate
prio_enc(NUM_CH-1) <= std_ulogic_vector(to_unsigned(NUM_CH-1, 5)); -- lowest priority
end generate;
end generate;

View file

@ -1,4 +1,5 @@
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_package.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_clockgate.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_fifo.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_cpu_decompressor.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_cpu_control.vhd

View file

@ -22,18 +22,19 @@ NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_dma.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_application_image.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_imem.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_dmem.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_bootloader_image.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_boot_rom.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_xip.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_xbus.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_bootloader_image.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_boot_rom.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_cfs.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_sdi.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_gpio.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_wdt.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_mtime.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_clint.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_uart.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_spi.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_twi.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_twd.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_pwm.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_trng.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_neoled.vhd

View file

@ -58,7 +58,7 @@ begin
MEM_INT_DMEM_EN => MEM_INT_DMEM_EN, -- implement processor-internal data memory
MEM_INT_DMEM_SIZE => MEM_INT_DMEM_SIZE, -- size of processor-internal data memory in bytes
-- Processor peripherals --
IO_MTIME_EN => true, -- implement machine system timer (MTIME)?
IO_CLINT_EN => true, -- implement core local interruptor (CLINT)?
IO_PWM_NUM_CH => IO_PWM_NUM_CH -- number of PWM channels to implement (0..12); 0 = disabled
)
port map (

View file

@ -66,7 +66,7 @@ begin
MEM_INT_DMEM_SIZE => MEM_INT_DMEM_SIZE, -- size of processor-internal data memory in bytes
-- Processor peripherals --
IO_GPIO_NUM => IO_GPIO_NUM, -- number of GPIO input/output pairs (0..64)
IO_MTIME_EN => true, -- implement machine system timer (MTIME)?
IO_CLINT_EN => true, -- implement core local interruptor (CLINT)?
IO_UART0_EN => true, -- implement primary universal asynchronous receiver/transmitter (UART0)?
IO_PWM_NUM_CH => IO_PWM_NUM_CH -- number of PWM channels to implement (0..12); 0 = disabled
)

View file

@ -93,7 +93,7 @@ begin
MEM_INT_DMEM_SIZE => MEM_INT_DMEM_SIZE, -- size of processor-internal data memory in bytes
-- Processor peripherals --
IO_GPIO_NUM => IO_GPIO_NUM, -- number of GPIO input/output pairs (0..64)
IO_MTIME_EN => true, -- implement machine system timer (MTIME)?
IO_CLINT_EN => true, -- implement core local interruptor (CLINT)?
IO_UART0_EN => true, -- implement primary universal asynchronous receiver/transmitter (UART0)?
IO_SPI_EN => true, -- implement serial peripheral interface (SPI)?
IO_TWI_EN => true, -- implement two-wire interface (TWI)?

View file

@ -68,8 +68,7 @@ end neorv32_litex_core_complex;
architecture neorv32_litex_core_complex_rtl of neorv32_litex_core_complex is
-- identifiers --
constant hart_id_c : std_ulogic_vector(31 downto 0) := x"00000000"; -- hardware thread ID ("core ID")
-- core identification --
constant jedec_id_c : std_ulogic_vector(10 downto 0) := "00000000000"; -- vendor's JEDEC manufacturer ID
-- configuration helpers --
@ -88,7 +87,7 @@ architecture neorv32_litex_core_complex_rtl of neorv32_litex_core_complex is
xcache_en : bool_t;
xcache_nb : natural_t;
xcache_bs : natural_t;
mtime : bool_t;
clint : bool_t;
end record;
-- core complex configurations --
@ -105,7 +104,7 @@ architecture neorv32_litex_core_complex_rtl of neorv32_litex_core_complex is
xcache_en => ( false, false, true, true ), -- external bus cache enabled
xcache_nb => ( 0, 0, 32, 64 ), -- number of cache blocks (lines), power of two
xcache_bs => ( 0, 0, 32, 32 ), -- size of cache clock (lines) in bytes, power of two
mtime => ( false, true, true, true ) -- RISC-V machine system timer
clint => ( false, true, true, true ) -- RISC-V core local interruptor
);
-- misc --
@ -119,7 +118,6 @@ begin
generic map (
-- General --
CLOCK_FREQUENCY => 0, -- clock frequency of clk_i in Hz [not required by the core complex]
HART_ID => hart_id_c, -- hardware thread ID
JEDEC_ID => jedec_id_c, -- vendor's JEDEC ID
-- On-Chip Debugger (OCD) --
OCD_EN => DEBUG, -- implement on-chip debugger
@ -130,8 +128,8 @@ begin
RISCV_ISA_Zicntr => configs_c.riscv_zicntr(CONFIG), -- implement base counters?
RISCV_ISA_Zihpm => configs_c.riscv_zihpm(CONFIG), -- implement hardware performance monitors?
-- Tuning Options --
FAST_MUL_EN => configs_c.fast_ops(CONFIG), -- use DSPs for M extension's multiplier
FAST_SHIFT_EN => configs_c.fast_ops(CONFIG), -- use barrel shifter for shift operations
CPU_FAST_MUL_EN => configs_c.fast_ops(CONFIG), -- use DSPs for M extension's multiplier
CPU_FAST_SHIFT_EN => configs_c.fast_ops(CONFIG), -- use barrel shifter for shift operations
-- Physical Memory Protection (PMP) --
PMP_NUM_REGIONS => configs_c.pmp_num(CONFIG), -- number of regions (0..16)
PMP_MIN_GRANULARITY => 4, -- minimal region granularity in bytes, has to be a power of 2, min 4 bytes
@ -146,7 +144,7 @@ begin
XBUS_CACHE_NUM_BLOCKS => configs_c.xcache_nb(CONFIG), -- x-cache: number of blocks (min 1), has to be a power of 2
XBUS_CACHE_BLOCK_SIZE => configs_c.xcache_bs(CONFIG), -- x-cache: block size in bytes (min 4), has to be a power of 2
-- Processor peripherals --
IO_MTIME_EN => configs_c.mtime(CONFIG) -- implement machine system timer (MTIME)?
IO_CLINT_EN => configs_c.clint(CONFIG) -- implement core local interruptor (CLINT)?
)
port map (
-- Global control --

View file

@ -129,13 +129,15 @@ proc setup_ip_gui {} {
set_property enablement_dependency {$IO_SPI_EN} [ipx::get_ports spi_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_SDI_EN} [ipx::get_ports sdi_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_TWI_EN} [ipx::get_ports twi_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_TWD_EN} [ipx::get_ports twd_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_ONEWIRE_EN} [ipx::get_ports onewire_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_PWM_EN} [ipx::get_ports pwm_o -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_CFS_EN} [ipx::get_ports cfs_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_NEOLED_EN} [ipx::get_ports neoled_o -of_objects [ipx::current_core]]
set_property enablement_dependency {$XIRQ_EN} [ipx::get_ports xirq_i -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_MTIME_EN} [ipx::get_ports mtime_time_o -of_objects [ipx::current_core]]
set_property enablement_dependency {!$IO_MTIME_EN} [ipx::get_ports mtime_irq_i -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_CLINT_EN} [ipx::get_ports mtime_time_o -of_objects [ipx::current_core]]
set_property enablement_dependency {!$IO_CLINT_EN} [ipx::get_ports mtime_irq_i -of_objects [ipx::current_core]]
set_property enablement_dependency {!$IO_CLINT_EN} [ipx::get_ports msw_irq_i -of_objects [ipx::current_core]]
# **************************************************************
@ -170,7 +172,6 @@ proc setup_ip_gui {} {
set group [add_group $page {Core Identification}]
add_params $group {
{ HART_ID {HART ID} {The hart thread ID of the CPU (passed to mhartid CSR)} }
{ JEDEC_ID {JEDEC ID} {For JTAG tap identification and mvendorid CSR} }
}
@ -246,9 +247,9 @@ proc setup_ip_gui {} {
set group [add_group $page {Tuning Options}]
add_params $group {
{ FAST_MUL_EN {DSP-Based Multiplier} }
{ FAST_SHIFT_EN {Barrel Shifter} }
{ REGFILE_HW_RST {Allow Full HW Reset for Register File} {Implement register file with FFs instead of BRAM to allow full hardware reset} }
{ CPU_FAST_MUL_EN {DSP-Based Multiplier} }
{ CPU_FAST_SHIFT_EN {Barrel Shifter} }
{ CPU_RF_HW_RST_EN {Allow Full HW Reset for Register File} {Implement register file with FFs instead of BRAM to allow full hardware reset} }
}
@ -310,9 +311,9 @@ proc setup_ip_gui {} {
{ IO_GPIO_OUT_NUM {Number of Outputs} {} {$IO_GPIO_EN} }
}
set group [add_group $page {Machine Timer (MTIME)}]
set group [add_group $page {Core Local Interruptor (CLINT)}]
add_params $group {
{ IO_MTIME_EN {Enable Machine Timer} }
{ IO_CLINT_EN {Enable Core Local Interruptor} }
}
set group [add_group $page {Primary UART (UART0)}]
@ -341,12 +342,18 @@ proc setup_ip_gui {} {
{ IO_SDI_FIFO {FIFO Depth} {Number of entries (use a power of two)} {$IO_SDI_EN} }
}
set group [add_group $page {Two-Wire/I2C Interface (TWI)}]
set group [add_group $page {Two-Wire/I2C Host (TWI)}]
add_params $group {
{ IO_TWI_EN {Enable TWI} }
{ IO_TWI_FIFO {FIFO Depth} {Number of entries (use a power of two)} {$IO_TWI_EN} }
}
set group [add_group $page {Two-Wire/I2C Device (TWD)}]
add_params $group {
{ IO_TWD_EN {Enable TWD} }
{ IO_TWD_FIFO {FIFO Depth} {Number of entries (use a power of two)} {$IO_TWD_EN} }
}
set group [add_group $page {Pulse-Width Modulation Controller (PWM)}]
add_params $group {
{ IO_PWM_EN {Enable PWM} }

View file

@ -28,7 +28,6 @@ entity neorv32_vivado_ip is
-- Clocking --
CLOCK_FREQUENCY : natural := 100_000_000;
-- Identification --
HART_ID : std_logic_vector(31 downto 0) := x"00000000";
JEDEC_ID : std_logic_vector(10 downto 0) := "00000000000";
-- Boot Configuration --
BOOT_MODE_SELECT : natural range 0 to 2 := 0;
@ -60,9 +59,9 @@ entity neorv32_vivado_ip is
RISCV_ISA_Zksh : boolean := false;
RISCV_ISA_Zxcfu : boolean := false;
-- Tuning Options --
FAST_MUL_EN : boolean := false;
FAST_SHIFT_EN : boolean := false;
REGFILE_HW_RST : boolean := false;
CPU_FAST_MUL_EN : boolean := false;
CPU_FAST_SHIFT_EN : boolean := false;
CPU_RF_HW_RST_EN : boolean := false;
-- Physical Memory Protection (PMP) --
PMP_NUM_REGIONS : natural range 0 to 16 := 0;
PMP_MIN_GRANULARITY : natural := 4;
@ -104,7 +103,7 @@ entity neorv32_vivado_ip is
IO_GPIO_EN : boolean := false;
IO_GPIO_IN_NUM : natural range 1 to 64 := 1; -- variable-sized ports must be at least 0 downto 0; #974
IO_GPIO_OUT_NUM : natural range 1 to 64 := 1;
IO_MTIME_EN : boolean := false;
IO_CLINT_EN : boolean := false;
IO_UART0_EN : boolean := false;
IO_UART0_RX_FIFO : natural range 1 to 2**15 := 1;
IO_UART0_TX_FIFO : natural range 1 to 2**15 := 1;
@ -117,6 +116,8 @@ entity neorv32_vivado_ip is
IO_SDI_FIFO : natural range 1 to 2**15 := 1;
IO_TWI_EN : boolean := false;
IO_TWI_FIFO : natural range 1 to 2**15 := 1;
IO_TWD_EN : boolean := false;
IO_TWD_FIFO : natural range 1 to 2**15 := 1;
IO_PWM_EN : boolean := false;
IO_PWM_NUM_CH : natural range 1 to 16 := 1; -- variable-sized ports must be at least 0 downto 0; #974
IO_WDT_EN : boolean := false;
@ -232,6 +233,11 @@ entity neorv32_vivado_ip is
twi_sda_o : out std_logic;
twi_scl_i : in std_logic := '0';
twi_scl_o : out std_logic;
-- TWD (available if IO_TWD_EN = true) --
twd_sda_i : in std_logic := '0';
twd_sda_o : out std_logic;
twd_scl_i : in std_logic := '0';
twd_scl_o : out std_logic;
-- 1-Wire Interface (available if IO_ONEWIRE_EN = true) --
onewire_i : in std_logic := '0';
onewire_o : out std_logic;
@ -242,7 +248,7 @@ entity neorv32_vivado_ip is
cfs_out_o : out std_logic_vector(IO_CFS_OUT_SIZE-1 downto 0); -- variable-sized ports must be at least 0 downto 0; #974
-- NeoPixel-compatible smart LED interface (available if IO_NEOLED_EN = true) --
neoled_o : out std_logic;
-- Machine timer system time (available if IO_MTIME_EN = true) --
-- Machine timer system time (available if IO_CLINT_EN = true) --
mtime_time_o : out std_logic_vector(63 downto 0);
-- External platform interrupts (available if XIRQ_NUM_CH > 0) --
xirq_i : in std_logic_vector(XIRQ_NUM_CH-1 downto 0) := (others => '0'); -- variable-sized ports must be at least 0 downto 0; #974
@ -263,7 +269,7 @@ architecture neorv32_vivado_ip_rtl of neorv32_vivado_ip is
-- AXI4-Lite bridge --
component xbus2axi4lite_bridge
port (
-- Global control
-- Global control
clk : in std_logic;
resetn : in std_logic;
-- XBUS device interface --
@ -315,6 +321,7 @@ architecture neorv32_vivado_ip_rtl of neorv32_vivado_ip is
signal spi_csn_aux : std_ulogic_vector(7 downto 0);
signal sdi_do_aux : std_ulogic;
signal twi_sda_o_aux, twi_scl_o_aux : std_ulogic;
signal twd_sda_o_aux, twd_scl_o_aux : std_ulogic;
signal onewire_o_aux : std_ulogic;
signal cfs_out_aux : std_ulogic_vector(IO_CFS_OUT_SIZE-1 downto 0);
signal neoled_aux : std_ulogic;
@ -346,9 +353,7 @@ begin
generic map (
-- Clocking --
CLOCK_FREQUENCY => CLOCK_FREQUENCY,
CLOCK_GATING_EN => false, -- clock gating is not supported here
-- Identification --
HART_ID => std_ulogic_vector(HART_ID),
JEDEC_ID => std_ulogic_vector(JEDEC_ID),
-- Boot Configuration --
BOOT_MODE_SELECT => BOOT_MODE_SELECT,
@ -380,9 +385,10 @@ begin
RISCV_ISA_Zksh => RISCV_ISA_Zksh,
RISCV_ISA_Zxcfu => RISCV_ISA_Zxcfu,
-- Extension Options --
FAST_MUL_EN => FAST_MUL_EN,
FAST_SHIFT_EN => FAST_SHIFT_EN,
REGFILE_HW_RST => REGFILE_HW_RST,
CPU_CLOCK_GATING_EN => false, -- clock gating is not supported here
CPU_FAST_MUL_EN => CPU_FAST_MUL_EN,
CPU_FAST_SHIFT_EN => CPU_FAST_SHIFT_EN,
CPU_RF_HW_RST_EN => CPU_RF_HW_RST_EN,
-- Physical Memory Protection --
PMP_NUM_REGIONS => PMP_NUM_REGIONS,
PMP_MIN_GRANULARITY => PMP_MIN_GRANULARITY,
@ -422,7 +428,7 @@ begin
-- Processor peripherals --
IO_DISABLE_SYSINFO => false,
IO_GPIO_NUM => num_gpio_c,
IO_MTIME_EN => IO_MTIME_EN,
IO_CLINT_EN => IO_CLINT_EN,
IO_UART0_EN => IO_UART0_EN,
IO_UART0_RX_FIFO => IO_UART0_RX_FIFO,
IO_UART0_TX_FIFO => IO_UART0_TX_FIFO,
@ -435,6 +441,8 @@ begin
IO_SDI_FIFO => IO_SDI_FIFO,
IO_TWI_EN => IO_TWI_EN,
IO_TWI_FIFO => IO_TWI_FIFO,
IO_TWD_EN => IO_TWD_EN,
IO_TWD_FIFO => IO_TWD_FIFO,
IO_PWM_NUM_CH => num_pwm_c,
IO_WDT_EN => IO_WDT_EN,
IO_TRNG_EN => IO_TRNG_EN,
@ -517,6 +525,11 @@ begin
twi_sda_o => twi_sda_o_aux,
twi_scl_i => std_ulogic(twi_scl_i),
twi_scl_o => twi_scl_o_aux,
-- TWD (available if IO_TWD_EN = true) --
twd_sda_i => std_ulogic(twd_sda_i),
twd_sda_o => twd_sda_o_aux,
twd_scl_i => std_ulogic(twd_scl_i),
twd_scl_o => twd_scl_o_aux,
-- 1-Wire Interface (available if IO_ONEWIRE_EN = true) --
onewire_i => std_ulogic(onewire_i),
onewire_o => onewire_o_aux,
@ -566,6 +579,9 @@ begin
twi_sda_o <= std_logic(twi_sda_o_aux);
twi_scl_o <= std_logic(twi_scl_o_aux);
twd_sda_o <= std_logic(twd_sda_o_aux);
twd_scl_o <= std_logic(twd_scl_o_aux);
onewire_o <= std_logic(onewire_o_aux);
cfs_out_o <= std_logic_vector(cfs_out_aux);

View file

@ -33,7 +33,7 @@ your FPGA/board.
This setup configures a `rv32imc_Zicsr_Zicntr` CPU with 16kB IMEM (as pre-initialized ROM),
8kB DMEM and includes the **GPIO** module to drive 8 external signals (`gpio_o`)
and the **MTIME** module for generating timer interrupts.
and the **CLINT** module for generating timer interrupts.
The setup uses the ["direct boot"](https://stnolting.github.io/neorv32/#_direct_boot)
configuration, so software applications are "installed" directly into the
processor-internal IMEM (via the bitstream) during synthesis.
@ -45,7 +45,7 @@ processor-internal IMEM (via the bitstream) during synthesis.
### > [`neorv32_test_setup_bootloader.vhd`](https://github.com/stnolting/neorv32/blob/main/rtl/test_setups/neorv32_test_setup_bootloader.vhd)
This setup configures a `rv32imc_Zicsr_Zicntr` CPU with 16kB IMEM (as RAM), 8kB DMEM
and includes the **GPIO** module to drive 8 external signals (`gpio_o`), the **MTIME**
and includes the **GPIO** module to drive 8 external signals (`gpio_o`), the **CLINT**
module for generating timer interrupts and **UART0** to interface with the bootloader or application
(via `uart0_txd_o` and `uart0_rxd_i`) via a serial terminal.
The setup uses the ["indirect boot"](https://stnolting.github.io/neorv32/#_indirect_boot)
@ -59,7 +59,7 @@ and a serial terminal.
### > [`neorv32_test_setup_on_chip_debugger.vhd`](https://github.com/stnolting/neorv32/blob/main/rtl/test_setups/neorv32_test_setup_on_chip_debugger.vhd)
This setup configures a `rv32imc_Zicsr_Zicntr_Zifencei` CPU with 16kB IMEM (as RAM), 8kB DMEM
and includes the **GPIO** module to drive 8 external signals (`gpio_o`), the **MTIME**
and includes the **GPIO** module to drive 8 external signals (`gpio_o`), the **CLINT**
module for generating timer interrupts, **UART0** to interface with the bootloader or application
(via `uart0_txd_o` and `uart0_rxd_i`) via a serial terminal and also the RISC-V-compatible
on-chip debugger (**OCD**), which is accessible via a standard JTAG interface (`jtag_*`).

View file

@ -57,7 +57,7 @@ begin
MEM_INT_DMEM_SIZE => MEM_INT_DMEM_SIZE, -- size of processor-internal data memory in bytes
-- Processor peripherals --
IO_GPIO_NUM => 8, -- number of GPIO input/output pairs (0..64)
IO_MTIME_EN => true -- implement machine system timer (MTIME)?
IO_CLINT_EN => true -- implement core local interruptor (CLINT)?
)
port map (
-- Global control --

Some files were not shown because too many files have changed in this diff Show more