Merge branch 'main' of github.com:LukasP46/neorv32 into i2c-bootloader
6
.github/workflows/Documentation.yml
vendored
|
@ -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:
|
||||
|
|
2
.github/workflows/Processor.yml
vendored
|
@ -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
|
||||
|
|
17
CHANGELOG.md
|
@ -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) |
|
||||
|
|
2
LICENSE
|
@ -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:
|
||||
|
|
|
@ -96,7 +96,7 @@ setup according to your needs. Note that all of the following SoC modules are en
|
|||
**CPU Core**
|
||||
|
||||
* [](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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
<<<
|
||||
// ####################################################################################################################
|
||||
|
||||
<<<
|
||||
include::overview.adoc[]
|
||||
|
||||
<<<
|
||||
include::soc.adoc[]
|
||||
|
||||
<<<
|
||||
include::cpu.adoc[]
|
||||
|
||||
<<<
|
||||
include::software.adoc[]
|
||||
|
||||
<<<
|
||||
include::on_chip_debugger.adoc[]
|
||||
|
||||
<<<
|
||||
include::../legal.adoc[]
|
||||
|
|
|
@ -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[]
|
||||
|
|
|
@ -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)
|
||||
|=======================
|
||||
|
|
82
docs/datasheet/cpu_dual_core.adoc
Normal 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.
|
|
@ -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
|
||||
|=======================
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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[]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|=======================
|
||||
|
|
62
docs/datasheet/soc_clint.adoc
Normal 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
|
||||
|=======================
|
|
@ -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)
|
||||
|=======================
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|=======================
|
||||
|
|
|
@ -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
|
||||
|=======================
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|=======================
|
|
@ -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)
|
||||
|=======================
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|=======================
|
||||
|
|
|
@ -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
|
||||
|=======================
|
||||
|
|
|
@ -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
|
||||
|=======================
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|=======================
|
|
@ -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 a I²C-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)
|
||||
|=======================
|
||||
|
|
|
@ -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
|
||||
|=======================
|
||||
|
|
|
@ -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
|
||||
|=======================
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|=======================
|
||||
|
|
|
@ -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
|
||||
|=======================
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 294 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 355 KiB |
BIN
docs/figures/smp_system.png
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
docs/figures/twd_sequences.png
Normal file
After Width: | Height: | Size: 50 KiB |
|
@ -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:
|
||||
|
|
BIN
docs/references/riscv-aclint-1.0-rc4.pdf
Normal file
BIN
docs/references/riscv-debug-specification.pdf
Normal 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....|.....|..'},
|
||||
],
|
||||
{},
|
||||
[
|
||||
|
|
|
@ -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....|.....|.....'},
|
||||
],
|
||||
{},
|
||||
[
|
||||
|
|
34
docs/sources/twd_sequences.json
Normal 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'
|
||||
]
|
||||
}
|
|
@ -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[]
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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
|
@ -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;
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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 --------------------------------------------------------------------------
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ------------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 --
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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
|
@ -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;
|
|
@ -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
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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)?
|
||||
|
|
|
@ -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 --
|
||||
|
|
|
@ -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} }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_*`).
|
||||
|
|
|
@ -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 --
|
||||
|
|