Merge branch 'main' of github.com:stnolting/neorv32 into fixed-docs-makefile

This commit is contained in:
Lukas Pajak 2024-11-26 10:00:00 +01:00
commit 37a029f24e
80 changed files with 1838 additions and 2747 deletions

View file

@ -17,25 +17,9 @@ on:
jobs:
Software:
sim_default_tb:
runs-on: ubuntu-latest
container: ghcr.io/stnolting/neorv32/sim
steps:
- name: '🧰 Repository Checkout'
uses: actions/checkout@v4
- name: '⚙️ Build Software Framework Tests'
run: |
make -C sw/example/processor_check check
make -C sw/example clean_all exe
make -C sw/bootloader clean_all info bootloader
Simple:
runs-on: ubuntu-latest
name: 'Simple testbench'
name: 'processor simulation'
strategy:
fail-fast: false
matrix:
@ -48,50 +32,33 @@ jobs:
- name: '🧰 Repository Checkout'
uses: actions/checkout@v4
- name: '🚧 Build and install software; then simulate with shell script'
uses: docker://ghcr.io/stnolting/neorv32/sim
# Redirect UART0 TX to text.io simulation output via <UARTx_SIM_MODE> user flags
with:
args: >-
make -C sw/example/${{ matrix.example }}
clean_all
USER_FLAGS+="-DUART0_SIM_MODE -DUART1_SIM_MODE -flto"
EFFORT=-Os
MARCH=rv32ima_zicsr_zifencei
info
all
sim-check
- name: '📦 Install xPack RISC-V GCC'
run: |
wget -q https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v14.2.0-2/xpack-riscv-none-elf-gcc-14.2.0-2-linux-x64.tar.gz
mkdir $GITHUB_WORKSPACE/riscv-gcc
tar -xzf xpack-riscv-none-elf-gcc-14.2.0-2-linux-x64.tar.gz -C $GITHUB_WORKSPACE/riscv-gcc
echo $GITHUB_WORKSPACE/riscv-gcc/xpack-riscv-none-elf-gcc-14.2.0-2/bin >> $GITHUB_PATH
- name: '📦 Install GHDL'
uses: ghdl/setup-ghdl-ci@nightly
VUnit:
runs-on: ubuntu-latest
- name: '🔍 Check tools'
run: |
riscv-none-elf-gcc -v
ghdl -v
steps:
- name: '⚙️ Build Software Framework Tests'
run: |
make RISCV_PREFIX=riscv-none-elf- -C sw/example/processor_check check
make RISCV_PREFIX=riscv-none-elf- -C sw/example clean_all exe
make RISCV_PREFIX=riscv-none-elf- -C sw/bootloader clean_all info bootloader
- name: '🧰 Repository Checkout'
uses: actions/checkout@v4
- name: '⚙️ Build and install Processor Check software'
uses: docker://ghcr.io/stnolting/neorv32/sim
with:
args: >-
make -C sw/example/processor_check
clean_all
USER_FLAGS+=-DUART0_SIM_MODE
USER_FLAGS+=-DSUPPRESS_OPTIONAL_UART_PRINT
USER_FLAGS+=-flto
MARCH=rv32imac_zicsr_zifencei
info
all
- name: '📤 Archive Processor Check application image'
uses: actions/upload-artifact@v4
with:
name: application
path: rtl/core/neorv32_application_image.vhd
- name: '🚧 Run Processor Hardware Tests with VUnit'
uses: VUnit/vunit_action@master
with:
image: ghcr.io/stnolting/neorv32/sim
cmd: ./sim/run.py --ci-mode -v
- name: '🚧 Compile executable and run simulation'
run: |
make -C sw/example/${{ matrix.example }} \
RISCV_PREFIX=riscv-none-elf- \
USER_FLAGS+="-DUART0_SIM_MODE -DUART1_SIM_MODE" \
clean_all \
info \
all \
sim-check

View file

@ -29,6 +29,12 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12
| Date | Version | Comment | Ticket |
|:----:|:-------:|:--------|:------:|
| 23.11.2024 | 1.10.6.6 | CPU control: large code edits and cleanups | [#1099](https://github.com/stnolting/neorv32/pull/1099) |
| 10.11.2024 | 1.10.6.5 | :warning: switch to [xPack](https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack) as default prebuilt RISC-V GCC toolchain (now using `riscv-none-elf-` as default gcc prefix) | [#1091](https://github.com/stnolting/neorv32/pull/1091) |
| 10.11.2024 | 1.10.6.4 | rework default processor testbench | [#1093](https://github.com/stnolting/neorv32/pull/1093) |
| 06.11.2024 | 1.10.6.3 | minor rtl edits and cleanups | [#1090](https://github.com/stnolting/neorv32/pull/1090) |
| 02.11.2024 | 1.10.6.2 | :warning: rework processor boot configuration; add new boot-configuration generics | [#1086](https://github.com/stnolting/neorv32/pull/1086) |
| 01.11.2024 | 1.10.6.1 | :test_tube: convert VHDL memory images into full-scale VHDL packages | [#1084](https://github.com/stnolting/neorv32/pull/1084) |
| 26.10.2024 | [**:rocket:1.10.6**](https://github.com/stnolting/neorv32/releases/tag/v1.10.6) | **New release** | |
| 26.10.2024 | 1.10.5.11 | cleanup central makefile and linker script | [#1077](https://github.com/stnolting/neorv32/pull/1077) |
| 21.10.2024 | 1.10.5.10 | :test_tube: rework linker script's ROM/IMEM default size (=16kB); add customization variable to all makefiles in `sw/example` | [#1072](https://github.com/stnolting/neorv32/pull/1072) |
@ -149,7 +155,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12
| 16.02.2024 | 1.9.5.2 | :warning: **revert** support for page faults (keep that in mmu branch for now) | [#809](https://github.com/stnolting/neorv32/pull/809) |
| 16.02.2024 | 1.9.5.1 | :sparkles: add two new generics to exclude certain PMP modes from synthesis | [#808](https://github.com/stnolting/neorv32/pull/808) |
| 16.02.2024 | [**:rocket:1.9.5**](https://github.com/stnolting/neorv32/releases/tag/v1.9.5) | **New release** | |
| 15.02.2023 | 1.9.4.13 | allow the DMA to issue a FENCE operation | [#807](https://github.com/stnolting/neorv32/pull/807) |
| 15.02.2024 | 1.9.4.13 | allow the DMA to issue a FENCE operation | [#807](https://github.com/stnolting/neorv32/pull/807) |
| 14.02.2024 | 1.9.4.12 | :lock: close another illegal compressed instruction encoding loophole | [#806](https://github.com/stnolting/neorv32/pull/806) |
| 11.02.2024 | 1.9.4.11 | :bug: fix several FPU bugs and design flaws | [#794](https://github.com/stnolting/neorv32/pull/794) |
| 11.02.2024 | 1.9.4.10 | minor additions to previous version (1.9.4.9): fix HPM configuration read-back | [#804](https://github.com/stnolting/neorv32/pull/804) |

View file

@ -39,7 +39,7 @@ informed via _precise_ and resumable hardware exceptions.
targeting various FPGA boards and toolchains to get started.
* :package: The entire processor is also available as [Vivado IP Block](https://stnolting.github.io/neorv32/ug/#_packaging_the_processor_as_vivado_ip_block).
* :kite: Support for [FreeRTOS](https://github.com/stnolting/neorv32-freertos),
[Zephyr OS](https://docs.zephyrproject.org/latest/boards/riscv/neorv32/doc/index.html) and
[Zephyr OS](https://docs.zephyrproject.org/latest/boards/others/neorv32/doc/index.html) and
[LiteX](https://github.com/enjoy-digital/litex/wiki/CPUs#risc-v---neorv32) SoC Builder Framework.
* :desktop_computer: Pre-configured [Eclipse project](https://stnolting.github.io/neorv32/ug/#_eclipse_ide) for developing and debugging code using an IDE.
* :label: The project's change log is available in [CHANGELOG.md](https://github.com/stnolting/neorv32/blob/main/CHANGELOG.md).
@ -250,7 +250,8 @@ This overview provides some *quick links* to the most important sections of the
* [Top Entity - Signals](https://stnolting.github.io/neorv32/#_processor_top_entity_signals) - how to connect to the processor
* [Top Entity - Generics](https://stnolting.github.io/neorv32/#_processor_top_entity_generics) - processor/CPU configuration options
* [Address Space](https://stnolting.github.io/neorv32/#_address_space) - memory layout and boot configurations
* [Address Space](https://stnolting.github.io/neorv32/#_address_space) - memory layout and address mapping
* [Boot Configuration](https://stnolting.github.io/neorv32/#_boot_configuration) - how to make the processor start executing
* [SoC Modules](https://stnolting.github.io/neorv32/#_processor_internal_modules) - IO/peripheral modules and memories
* [On-Chip Debugger](https://stnolting.github.io/neorv32/#_on_chip_debugger_ocd) - in-system debugging of the processor via JTAG

View file

@ -1051,6 +1051,7 @@ The NEORV32 PMP is fully compatible to the RISC-V Privileged Architecture Specif
by default has full permissions. The PMP is configured via the <<_machine_physical_memory_protection_csrs>>.
Several <<_processor_top_entity_generics>> are provided to fine-tune the CPU's PMP capabilities:
* `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

View file

@ -360,7 +360,7 @@ As software does not need to determine the interrupt cause the reduction in late
|=======================
| Name | Machine exception program counter
| Address | `0x341`
| Reset value | `0x00000000`
| Reset value | `BOOT_ADDR` (CPU boot address, see <<_cpu_top_entity_generics>>)
| ISA | `Zicsr`
| Description | The `mepc` CSR provides the instruction address where execution has stopped/failed when
an interrupt is triggered / an exception is raised. See section <<_traps_exceptions_and_interrupts>> for a list of all legal values.
@ -435,10 +435,10 @@ However, any write-access will be ignored and will not cause an exception to mai
[options="header",grid="rows"]
|=======================
| Bit | Name [C] | R/W | Function
| 3 | `CSR_MIP_MSIP` | r/- | **MSIP**: Machine _software_ interrupt pending; _cleared by platform-defined mechanism_
| 7 | `CSR_MIP_MTIP` | r/- | **MTIP**: Machine _timer_ interrupt pending; _cleared by platform-defined mechanism_
| 11 | `CSR_MIP_MEIP` | r/- | **MEIP**: Machine _external_ interrupt pending; _cleared by platform-defined mechanism_
| 31:16 | `CSR_MIP_FIRQ15P` : `CSR_MIP_FIRQ0P` | r/- | **FIRQxP**: Fast interrupt channel 15..0 pending; _cleared by platform-defined mechanism_
| 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_
| 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_
|=======================
.FIRQ Channel Mapping
@ -528,7 +528,7 @@ See section <<_smpmp_isa_extension>> for more information.
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | PMP region configuration registers
| Name | Physical memory protection region configuration registers
| Address | `0x3a0` (`pmpcfg0`)
| | `0x3a1` (`pmpcfg1`)
| | `0x3a2` (`pmpcfg2`)
@ -538,7 +538,7 @@ See section <<_smpmp_isa_extension>> for more information.
| Description | Configuration of physical memory protection regions. Each region provides an individual 8-bit array in these CSRs.
|=======================
.`pmpcfg0` CSR Bits
.`pmpcfg*` CSR Bits
[cols="^1,^2,^1,<11"]
[options="header",grid="rows"]
|=======================
@ -560,12 +560,10 @@ implementation of the according modes.
[discrete]
===== **`pmpaddr`**
The `pmpaddr*` CSRs are used to configure the region's address boundaries.
[cols="<1,<8"]
[frame="topbot",grid="none"]
|=======================
| Name | Physical memory protection address registers
| Name | Physical memory protection region address registers
| Address | `0x3b0` (`pmpaddr1`)
| | `0x3b1` (`pmpaddr2`)
| | `0x3b2` (`pmpaddr3`)
@ -584,7 +582,16 @@ The `pmpaddr*` CSRs are used to configure the region's address boundaries.
| | `0x3bf` (`pmpaddr15`)
| Reset value | `0x00000000`
| ISA | `Zicsr` & `Smpmp`
| Description | Region address configuration. The two MSBs of each CSR are hardwired to zero (= bits 33:32 of the physical address).
| Description | Region address/boundaries configuration.
|=======================
.`pmpaddr*` CSR Bits
[cols="^1,^2,^1,<11"]
[options="header",grid="rows"]
|=======================
| Bit | R/W | Description
| 31:30 | r-w | address bits `33 downto 32`´, hardwired to zero
| 29:0 | r/w | address bits `31 downto 2`
|=======================

View file

@ -179,7 +179,7 @@ All core VHDL files from the list below have to be assigned to a **new library**
[NOTE]
See section <<_file_list_files>> for more information.
.RTL File List and Hierarchy
.RTL File List and Hierarchy (in alphabetical order)
...................................
neorv32_top.vhd - NEORV32 PROCESSOR/SOC TOP ENTITY
@ -199,7 +199,7 @@ neorv32_top.vhd - NEORV32 PROCESSOR/SOC TOP ENTITY
│└neorv32_cpu_regfile.vhd - Data register file
├neorv32_boot_rom.vhd - Bootloader ROM
│└neorv32_bootloader_image.vhd - Bootloader ROM memory image
│└neorv32_bootloader_image.vhd - Bootloader ROM memory image (package)
├neorv32_bus.vhd - SoC bus infrastructure modules
├neorv32_cache.vhd - Generic cache module
├neorv32_cfs.vhd - Custom functions subsystem
@ -214,7 +214,7 @@ neorv32_top.vhd - NEORV32 PROCESSOR/SOC TOP ENTITY
├neorv32_gpio.vhd - General purpose input/output port unit
├neorv32_gptmr.vhd - General purpose 32-bit timer
├neorv32_imem.vhd - Generic processor-internal instruction memory
│└neorv32_application_image.vhd - IMEM application initialization image
│└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
@ -284,7 +284,7 @@ puts $file_list
.File-List Usage Examples
[TIP]
The provided file-list files are used by the GHDL-based simple simulation setup (`sim/tb_simple/ghdl.setup.sh`) as
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`).

View file

@ -4,14 +4,14 @@
[discrete]
==== Why did you make this?
Processor and CPU architecture designs are fascinating things: they are the magic frontier where software meets hardware.
This project started as something like a _journey_ into this magic realm to understand how things actually work
down on this very low level and evolved over time to a capable system on chip.
For me, processor and CPU architecture designs are fascinating things: they are the magic frontier where software meets hardware.
This project started as something like a _journey_ into this realm to understand how things actually work
down on the very low level and evolved over time to a quite capable system-on-chip.
But there is more: when I started to dive into the emerging RISC-V ecosystem I felt overwhelmed by the complexity.
When I started to dive into the emerging RISC-V ecosystem I felt overwhelmed by the complexity.
As a beginner it is hard to get an overview - especially when you want to setup a minimal platform to tinker with...
Which core to use? How to get the right toolchain? What features do I need? How does booting work? How do I
create an actual executable? How to get that into the hardware? How to customize things? **_Where to start???_**
create an actual executable? How to get that into the hardware? How to customize things? _Where to start???_
This project aims to provide a _simple to understand_ and _easy to use_ yet _powerful_ and _flexible_ platform
that targets FPGA and RISC-V beginners as well as advanced users.
@ -20,15 +20,16 @@ that targets FPGA and RISC-V beginners as well as advanced users.
[discrete]
==== Why a _soft-core_ processor?
As a matter of fact soft-core processors _cannot_ compete with discrete (like FPGA hard-macro) processors in terms
of performance, energy efficiency and size. But they do fill a niche in FPGA design space: for example, soft-core
processors allow to implement the _control flow part_ of certain applications (e.g. communication protocol handling)
As a matter of fact soft-core processors _cannot_ compete with discrete (ASIC) processors in terms
of performance, energy efficiency and size. But they do fill a niche in the design space: for example, soft-core
processors allow to implement the _control flow part_ of certain applications (like communication protocol handling)
using software like plain C. This provides high flexibility as software can be easily changed, re-compiled and
re-uploaded again.
Furthermore, the concept of flexibility applies to all aspects of a soft-core processor. The user can add
_exactly_ the features that are required by the application: additional memories, custom interfaces, specialized
co-processors and even user-defined instructions.
co-processors and even user-defined instructions. These application-specific optimization capabilities compensate
for many of the limitations of soft-core processors.
[discrete]
@ -43,48 +44,58 @@ ____
Open-source is a great thing!
While open-source has already become quite popular in _software_, hardware-focused projects still need to catch up.
Admittedly, there has been quite a development, but mainly in terms of _platforms_ and _applications_ (so
schematics, PCBs, etc.). Although processors and CPUs are the heart of almost every digital system, having a true
open-source silicon is still a rarity. RISC-V aims to change that - and even it is _just one approach_, it helps paving
the road for future development.
Although processors and CPUs are the heart of almost every digital system, having a true open-source platform is still
a rarity. RISC-V aims to change that - and even it is _just one approach_, it helps paving the road for future development.
Furthermore, I highly appreciate the community aspect of RISC-V. The ISA and everything beyond is developed in direct
contact with the community: this includes businesses and professionals but also hobbyist, amateurs and people
that are just curious. Everyone can join discussions and contribute to RISC-V in their very own way.
contact with the community: this includes businesses and professionals but also hobbyist, amateurs and enthusiasts.
Everyone can join discussions and contribute to RISC-V in their very own way.
Finally, I really like the RISC-V ISA itself. It aims to be a clean, orthogonal and "intuitive" ISA that
resembles with the basic concepts of _RISC_: simple yet effective.
resembles with the basic concepts of RISC: _simple yet effective_.
[discrete]
==== Yet another RISC-V core? What makes it special?
The NEORV32 is not based on another RISC-V core. It was build entirely from ground up (just following the official
ISA specs). The project does not intend to replace certain RISC-V cores or
just beat existing ones like https://github.com/SpinalHDL/VexRiscv[VexRISC] in terms of performance or
https://github.com/olofk/serv[SERV] in terms of size. It was build having a different design goal in mind.
The NEORV32 is not based on another (RISC-V) core. It was build entirely from ground up just following the official
ISA specs. The project does not intend to replace certain RISC-V cores or beat existing ones in terms of _performance_
or _size_. It was build having a different design goal in mind.
The project aims to provide _another option_ in the RISC-V / soft-core design space with a different performance
vs. size trade-off and a different focus: _embrace_ concepts like documentation, platform-independence / portability,
RISC-V compatibility, _extensibility & customization_ and _ease of use_ (see the <<_project_key_features>> below).
vs. size trade-off and a different focus: embrace concepts like documentation, platform-independence / portability,
RISC-V compatibility, extensibility & customization and - last but not least - ease of use.
Furthermore, the NEORV32 pays special focus on _execution safety_ using <<_full_virtualization>>. The CPU aims to
provide fall-backs for _everything that could go wrong_. This includes malformed instruction words, privilege escalations
and even memory accesses that are checked for address space holes and deterministic response times of memory-mapped
devices. Precise exceptions allow a defined and fully-synchronized state of the CPU at every time an in every situation.
To summarize, this project pursues the following objectives (in rough order of importance):
[start=1]
. RISC-V-compliance and -compatibility
. Functionality and features
. Extensibility
. Safety and security
. Minimal area
. Short critical paths, high operating clock
. Simplicity / easy to understand
. Low-power design
. High overall performance
[discrete]
==== A multi-cycle architecture?!
Most mainstream CPUs out there are pipelined architectures to increase throughput. In contrast, most CPUs used for
teaching are single-cycle designs since they are probably the most easiest to understand. But what about the
multi-cycle architectures?
The primary goal of many mainstream CPUs is pure performance. Deep pipelines and out-of-order execution are some concepts
to boost performance, while also increasing complexity. In contrast, most CPUs used for teaching are single-cycle designs
since they are probably the most easiest to understand. But what about something in-between?
In terms of energy, throughput, area and maximal clock frequency multi-cycle architectures are somewhere in between
In terms of energy, throughput, area and maximal clock frequency, multi-cycle architectures are somewhere in between
single-cycle and fully-pipelined designs: they provide higher throughput and clock speed when compared to their
single-cycle counterparts while having less hardware complexity (= area) then a fully-pipelined designs. I decided to
use the multi-cycle approach because of the following reasons:
single-cycle counterparts while having less hardware complexity (= area) and thus, less performance, then a fully-pipelined
designs. So I decided to use the multi-cycle-approach because of the following reasons:
* Multi-cycle architectures are quite small! There is no need for pipeline hazard detection/resolution logic
(e.g. forwarding). Furthermore, you can "re-use" parts of the core to do several tasks (e.g. the ALU is used for
@ -94,29 +105,15 @@ in real-world applications (i.e. FPGA block RAM is entirely synchronous). Furthe
long critical path tremendously reducing maximal operating frequency.
* Pipelined designs increase performance by having several instruction "in fly" at the same time. But this also means
there is some kind of "out-of-order" behavior: if an instruction at the end of the pipeline causes an exception
all the instructions in earlier stages have to be invalidated. Potential architectureral state changes have to be made _undone_
requiring additional (exception-handling) logic. In a multi-cycle architecture this situation cannot occur since only a
all the instructions in earlier stages have to be invalidated. Potential architectural state changes have to be made _undone_
requiring additional logic (Spectre and Meltdown...). In a multi-cycle architecture this situation cannot occur since only a
single instruction is being processed ("in-fly") at a time.
* Having only a single instruction in fly does not only reduce hardware costs, it also simplifies
simulation/verification/debugging, state preservation/restoring during exceptions and extensibility (no need to care
about pipeline hazards) - but of course at the cost of reduced throughput.
To counteract the loss of performance implied by a _pure_ multi-cycle architecture, the NEORV32 CPU uses a _mixed_
approach: instruction fetch (front-end) and instruction execution (back-end) are de-coupled to operate independently
approach: instruction-fetch (front-end) and instruction-execution (back-end) are de-coupled to operate independently
of each other. Data is interchanged via a queue building a simple 2-stage pipeline. Each "pipeline" stage in terms is
implemented as multi-cycle architecture to simplify the hardware and to provide _precise_ state control (e.g. during
exceptions).
[discrete]
==== Objective
[start=1]
. RISC-V-compliance and -compatibility
. Functionality and features
. Safety and security
. Minimal area
. Short critical paths, high operating clock
. Low-power design
. High overall performance
. Simplicity / easy to understand
implemented as multi-cycle architecture to simplify the hardware and to provide _precise_ state control (for example
during exceptions).

View file

@ -202,12 +202,15 @@ The generic type "`suv(x:y)`" is an abbreviation for "`std_ulogic_vector(x downt
[options="header",grid="rows"]
|=======================
| Name | Type | Default | Description
4+^| **General**
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>>).
| `INT_BOOTLOADER_EN` | boolean | false | Implement the processor-internal <<_bootloader_rom_bootrom>>, pre-initialized with the default <<_bootloader>> image.
| `HART_ID` | suv(31:0) | 0x00000000 | The hart thread ID of the CPU (passed to <<_mhartid>> CSR).
| `JEDEC_ID` | suv(10:0) | 0b00000000000 | JEDEC ID; continuation codes plus vendor ID (passed to <<_mvendorid>> CSR and to the <<_debug_transport_module_dtm>>).
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>>.
| `BOOT_ADDR_CUSTOM` | suv(31:0) | x"00000000" | Custom CPU boot address (available if `BOOT_MODE_SELECT` = 1).
4+^| **<<_on_chip_debugger_ocd>>**
| `OCD_EN` | boolean | false | Implement the on-chip debugger and the CPU debug mode.
| `OCD_AUTHENTICATION` | boolean | false | Implement <<_debug_authentication>> module.
@ -295,7 +298,7 @@ The generic type "`suv(x:y)`" is an abbreviation for "`std_ulogic_vector(x downt
| `IO_TRNG_EN` | boolean | false | Implement the <<_true_random_number_generator_trng>>.
| `IO_TRNG_FIFO` | natural | 1 | Depth of the TRNG data FIFO. Has to be a power of two, min 1, max 32768.
| `IO_CFS_EN` | boolean | false | Implement the <<_custom_functions_subsystem_cfs>>.
| `IO_CFS_CONFIG` | suv(31:0) | 0x00000000 | "Conduit" generic to pass user-defined flags to the <<_custom_functions_subsystem_cfs>>.
| `IO_CFS_CONFIG` | suv(31:0) | x"00000000" | "Conduit" generic to pass user-defined flags to the <<_custom_functions_subsystem_cfs>>.
| `IO_CFS_IN_SIZE` | natural | 32 | Size of the <<_custom_functions_subsystem_cfs>> input signal conduit (`cfs_in_i`).
| `IO_CFS_OUT_SIZE` | natural | 32 | Size of the <<_custom_functions_subsystem_cfs>> output signal conduit (`cfs_out_o`).
| `IO_NEOLED_EN` | boolean | false | Implement the <<_smart_led_interface_neoled>>.
@ -564,7 +567,7 @@ monitor starts an internal timer. The accessed module has to respond ("ACK") to
.Internal Bus Timeout Configuration
[source,vhdl]
----
constant bus_timeout_c : natural := 15;
constant bus_timeout_c : natural := 15;
----
This constant defines the _maximum_ number of cycles after which a non-responding bus request (i.e. no `ack`
@ -657,11 +660,11 @@ package file (`rtl/core/neorv323_package.vhd`).
.Exemplary Cut-Out from the IO Address Map
[source,vhdl]
----
-- IO Address Map --
constant iodev_size_c : natural := 256; -- size of a single IO device (bytes)
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";
-- IO Address Map --
constant iodev_size_c : natural := 256; -- size of a single IO device (bytes)
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";
----
.IO Access Latency
@ -671,47 +674,67 @@ buffers the address bus. Hence, accesses to the processor-internal IO region req
to complete.
<<<
// ####################################################################################################################
:sectnums:
==== Boot Configuration
=== Boot Configuration
Due to the flexible memory configuration, the NEORV32 Processor provides several different boot scenarios.
The following section illustrates the two most common boot scenarios.
The NEORV32 processor provides some pre-defined boot configurations to adjust system start-up to
the requirements of the application. The actual boot configuration is defined by the `BOOT_MODE_SELECT`
generic (see <<_processor_top_entity_generics>>).
.NEORV32 Boot Configurations
image::neorv32_boot_configurations.png[800]
[cols="^2,^2,^2,<6"]
[options="header",grid="rows"]
|=======================
| `BOOT_MODE_SELECT` | Name | Boot address | Description
| 0 (default) | Bootloader | Base of internal BOOTROM | Implement the processor-internal <<_bootloader_rom_bootrom>> as pre-initialized ROM and boot from there.
| 1 | Custom Address | `BOOT_ADDR_CUSTOM` generic | Start booting at user-defined address (`BOOT_ADDR_CUSTOM` top generic).
| 2 | IMEM Image | Base of internal IMEM | Implement the processor-internal <<_instruction_memory_imem>> as pre-initialized ROM and boot from there.
|=======================
There are two general boot scenarios: _Indirect Boot_ (1a and 1b) and _Direct Boot_ (2a and 2b) configured via the
`INT_BOOTLOADER_EN` generic. If this generic is `true` the _indirect boot scenario_ is used. This is also the
default boot configuration of the processor. If `INT_BOOTLOADER_EN` is `*false` the _direct boot scenario_ is used.
:sectnums!:
===== Indirect Boot
:sectnums:
==== Booting via Bootloader
The indirect_boot scenarios **1a** and **1b** are based on the processor-internal <<_bootloader>>. This boot setup is enabled
by setting the `INT_BOOTLOADER_EN` generic to `true`, which will implement the processor-internal <<_bootloader_rom_bootrom>>.
This read-only memory is pre-initialized during synthesis with the default bootloader firmware. The bootloader provides several
options to upload an executable copying it to the beginning of the _instruction address space_ so the CPU can execute it.
This is the most common and thus, the default boot configuration. When selected, the processor-internal
<<_bootloader_rom_bootrom>> is enabled. This ROM contains the executable image (`rtl/core/neorv32_bootloader_image.vhd`)
of the default NEORV32 <<_bootloader>> that will be executed right after reset. The bootloader provides an interactive
user console for executable upload as well as an automatic boot-configuration targeting external (SPI) memories.
Boot scenario **1a** uses the processor-internal IMEM. This scenario implements the internal <<_instruction_memory_imem>>
as non-initialized RAM so the bootloader can copy the actual executable to it.
If the processor-internal <<_instruction_memory_imem>> is enabled it will be implemented as _blank_ RAM.
Boot scenario **1b** uses a processor-external IMEM that is connected via the processor's bus interface. In this scenario
the internal <<_instruction_memory_imem>> is not implemented at all and the bootloader will copy the executable to the
processor-external memory. Hence, the external memory has to be implemented as RAM.
:sectnums!:
===== Direct Boot
:sectnums:
==== Boot from Custom Address
The direct boot scenarios **2a** and **2b** do not use the processor-internal bootloader since the `INT_BOOTLOADER_EN`
generic is set `false`. In this configuration the <<_bootloader_rom_bootrom>> is not implemented at all and the CPU will
directly begin executing code from the beginning of the instruction address space after reset. An application-specific
"pre-initialization" mechanism is required in order to provide an executable inside the memory.
This is the most flexible boot configuration as it allows the user to specify a custom boot address via the
`BOOT_ADDR_CUSTOM` generic. Note that this address has to be aligned to 4-byte boundary. The processor will
start executing from the defined address right after reset. For example, this boot configuration ca be used to
execute a _custom bootloader_ from a memory that is attached via the <<_processor_external_bus_interface_xbus>>.
Boot scenario **2a** uses the processor-internal IMEM implemented as _read-only memory_ in this scenario.
It is pre-initialized (by the bitstream) with the actual application executable during synthesis.
The <<_bootloader_rom_bootrom>> is not enabled / implement at all.
If the processor-internal <<_instruction_memory_imem>> is enabled it will be implemented as _blank_ RAM.
In contrast, boot scenario **2b** uses a processor-external IMEM. In this scenario the system designer is responsible for
providing an initialized external memory that contains the actual application to be executed.
:sectnums:
==== Boot IMEM Image
This configuration will implement the <<_instruction_memory_imem>> as _pre-initialized read-only memory_ (ROM).
The ROM is initialized during synthesis with the according application image file (`rtl/core/neorv32_application_image.vhd`).
After reset, the CPU will directly start executing this image. Since the IMEM is implemented as ROM, the executable cannot
be altered at runtime at all.
The <<_bootloader_rom_bootrom>> is not enabled / implement at all.
.Internal IMEM is Required
[IMPORTANT]
This boot configuration requires the IMEM to be enabled (`MEM_INT_IMEM_EN` = true).
.Simulation Setup
[TIP]
This boot configuration is handy for simulations as the application software is executed right away without the
need for an explicit initialization / executable upload.
<<<

View file

@ -5,28 +5,24 @@
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
|=======================
| Hardware source files: | neorv32_boot_rom.vhd |
| Software driver files: | none |
| Top entity ports: | none |
| Configuration generics: | `INT_BOOTLOADER_EN` | implement processor-internal bootloader when `true`
| CPU interrupts: | none |
| Hardware source files: | neorv32_boot_rom.vhd | default platform-agnostic bootloader ROM
| | neorv32_bootloader_image.vhd | initialization image (a VHDL package)
| Software driver files: | none | _implicitly used_
| 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
|=======================
**Overview**
This boot ROM module provides a read-only memory that contain the executable image of the default NEORV32
<<_bootloader>>. If the internal bootloader is enabled via the `INT_BOOTLOADER_EN` generic the CPU's boot address
is automatically set to the beginning of the bootloader ROM. See sections <<_address_space>> and
<<_boot_configuration>> for more information regarding the processor's different boot scenarios.
.Memory Size
[IMPORTANT]
If the configured boot ROM size is **not** a power of two the actual memory size will be auto-adjusted to
the next power of two (e.g. configuring a memory size of 6kB will result in a physical memory size of 8kB).
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.
.Bootloader Image
[IMPORTANT]
The boot ROM is initialized during synthesis with the default bootloader image
(`rtl/core/neorv32_bootloader_image.vhd`).
(`rtl/core/neorv32_bootloader_image.vhd`). Note that the BOOTROM size is constrained to 4kB.

View file

@ -36,7 +36,7 @@ physical memory size of 64kB).
.Legacy HDL Style
[TIP]
If synthesis fails to infer block RAM for the DMEM, turn on the `alternaitve_style_en_c` option inside
If synthesis fails to infer block RAM for the DMEM, turn on the `alt_style_c` option inside
the memory's VHDL source file. When enabled, a different HDL style is used to describe the memory core.
.Execute from RAM

View file

@ -5,13 +5,14 @@
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
|=======================
| Hardware source files: | neorv32_imem.vhd | default platform-agnostic instruction memory
| Software driver files: | none | _implicitly used_
| Top entity ports: | none |
| Configuration generics: | `MEM_INT_IMEM_EN` | implement processor-internal IMEM when `true`
| | `MEM_INT_IMEM_SIZE` | IMEM size in bytes (use a power of 2)
| | `INT_BOOTLOADER_EN` | use internal bootloader when `true` (implements IMEM as _uninitialized_ RAM, otherwise the IMEM is implemented an _pre-intialized_ ROM)
| CPU interrupts: | none |
| Hardware source files: | neorv32_imem.vhd | default platform-agnostic instruction memory (RAM or ROM)
| | neorv32_application_image.vhd | initialization image (a VHDL package)
| Software driver files: | none | _implicitly used_
| Top entity ports: | none |
| Configuration generics: | `MEM_INT_IMEM_EN` | implement processor-internal IMEM when `true`
| | `MEM_INT_IMEM_SIZE` | IMEM size in bytes (use a power of 2)
| | `BOOT_MODE_SELECT` | implement IMEM as ROM when `BOOT_MODE_SELECT` = 2; see <<_boot_configuration>>
| CPU interrupts: | none |
| Access restrictions: 2+| none / read-only if `INT_BOOTLOADER_EN = true`
|=======================
@ -20,19 +21,19 @@
Implementation of the processor-internal instruction memory is enabled by the processor's
`MEM_INT_IMEM_EN` generic. The total memory size in bytes is defined via the `MEM_INT_IMEM_SIZE` generic.
Note that this size should be a power of two to optimize physical implementation. If the IMEM is implemented,
it is mapped to base address `0x00000000` by default (see section <<_address_space>>).
Note that this size should be a power of two to optimize physical implementation. If enabled,
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
any time. If this feature is not required the IMEM can be implemented as _pre-intialized_ ROM so that the
application code permanently resides in memory. This is automatically implemented when the
processor-internal bootloader is disabled (`INT_BOOTLOADER_EN` = `false`).
required when using the <<_bootloader>> (or the <<_on_chip_debugger>>) so it can update the content of the IMEM at
any time.
When the IMEM is implemented as ROM, it will be initialized during synthesis with the actual application program
image. The compiler toolchain provides an option to generate and override the default VHDL initialization file
`rtl/core/neorv32_application_image.vhd`, which is automatically inserted into the IMEM. If the IMEM is implemented
as RAM (default), the memory block will **not be initialized at all**.
Alternatively, the IMEM can be implemented as **pre-initialized read-only memory (ROM)**, so the processor can
directly boot from it after reset. This option is configured via the `BOOT_MODE_SELECT` generic. See section
<<_boot_configuration>> for more information. The initialization image is embedded into the bitstream during synthesis.
The software framework provides an option to generate and override the default VHDL initialization file
`rtl/core/neorv32_application_image.vhd`, which is automatically inserted into the IMEM (see <<_makefile_targets>>.
If the IMEM is implemented as RAM (default), the memory block will not be initialized at all.
.Platform-Specific Memory Primitives
[NOTE]
@ -47,9 +48,9 @@ physical memory size of 64kB).
.Legacy HDL Style
[TIP]
If synthesis fails to infer block RAM for the IMEM, turn on the `alternaitve_style_en_c` option inside
If synthesis fails to infer block RAM for the IMEM, turn on the `alt_style_c` option inside
the memory's VHDL source file. When enabled, a different HDL style is used to describe the memory core.
.Read-Only Access
[NOTE]
If the IMEM is implemented as true ROM any write attempt to it will raise a _store access fault_ exception.
If the IMEM is implemented as ROM any write attempt to it will raise a _store access fault_ exception.

View file

@ -55,7 +55,7 @@ The basic timing configuration is programmed via the clock prescaler bits `ONEWI
clock divider bits `ONEWIRE_CTRL_CLKDIVx` (see next section).
The controller can execute three basic bus operations, which are triggered by setting one out of three specific
control register bits (the bits auto-clear):
control register bits (all those bits auto-clear):
[start=1]
. generate reset pulse and check for device presence; triggered when setting `ONEWIRE_CTRL_TRIG_RST`
@ -158,8 +158,8 @@ constant t_presence_end_c : unsigned(6 downto 0) := to_unsigned(96, 7); -- t6
.Overdrive
[IMPORTANT]
The ONEWIRE controller does not support the _overdrive_ mode. However, it can be implemented by reducing the base
time **T~base~** (and by eventually changing the hardwired timing configuration in the VHDL source file).
The ONEWIRE controller does not support the overdrive mode natively. However, it can be implemented by reducing
the base time **T~base~** (and by eventually changing the hardwired timing configuration in the VHDL source file).
**Interrupt**

View file

@ -62,7 +62,7 @@ Bit fields in this register are set to all-zero if the according memory system i
| `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` | - | _reserved_, read as zero
| `3` | `SYSINFO_MEM_BOOT` | boot mode configuration, via top's `BOOT_MODE_SELECT` generic (see <<_boot_configuration>>))
|=======================
@ -73,37 +73,38 @@ Bit fields in this register are set to all-zero if the according memory system i
[options="header",grid="all"]
|=======================
| Bit | Name [C] | Description
| `0` | `SYSINFO_SOC_BOOTLOADER` | set if processor-internal bootloader is implemented (via top's `INT_BOOTLOADER_EN` generic)
| `1` | `SYSINFO_SOC_XBUS` | set if external Wishbone bus interface is implemented (via top's `XBUS_EN` generic)
| `2` | `SYSINFO_SOC_MEM_INT_IMEM` | set if processor-internal DMEM implemented (via top's `MEM_INT_DMEM_EN` generic)
| `3` | `SYSINFO_SOC_MEM_INT_DMEM` | set if processor-internal IMEM is implemented (via top's `MEM_INT_IMEM_EN` generic)
| `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)
| `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)
| `13:12` | - | _reserved_, read as zero
| `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)
| `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)
| `20` | `SYSINFO_SOC_IO_PWM` | set if PWM is implemented (via top's `IO_PWM_NUM_CH` generic)
| `21` | `SYSINFO_SOC_IO_WDT` | set if WDT is implemented (via top's `IO_WDT_EN` generic)
| `22` | `SYSINFO_SOC_IO_CFS` | set if custom functions subsystem is implemented (via top's `IO_CFS_EN` generic)
| `23` | `SYSINFO_SOC_IO_TRNG` | set if TRNG is implemented (via top's `IO_TRNG_EN` generic)
| `24` | `SYSINFO_SOC_IO_SDI` | set if SDI is implemented (via top's `IO_SDI_EN` generic)
| `25` | `SYSINFO_SOC_IO_UART1` | set if secondary UART1 is implemented (via top's `IO_UART1_EN` generic)
| `26` | `SYSINFO_SOC_IO_NEOLED` | set if NEOLED is implemented (via top's `IO_NEOLED_EN` generic)
| `27` | `SYSINFO_SOC_IO_XIRQ` | set if XIRQ is implemented (via top's `XIRQ_NUM_CH` generic)
| `28` | `SYSINFO_SOC_IO_GPTMR` | set if GPTMR is implemented (via top's `IO_GPTMR_EN` generic)
| `29` | `SYSINFO_SOC_IO_SLINK` | set if stream link interface is implemented (via top's `IO_SLINK_EN` generic)
| `30` | `SYSINFO_SOC_IO_ONEWIRE` | set if ONEWIRE interface is implemented (via top's `IO_ONEWIRE_EN` generic)
| `31` | `SYSINFO_SOC_IO_CRC` | set if cyclic redundancy check unit is implemented (via top's `IO_CRC_EN` generic)
| `0` | `SYSINFO_SOC_BOOTLOADER` | set if processor-internal bootloader is implemented (via top's `BOOT_MODE_SELECT` generic; see <<_boot_configuration>>)
| `1` | `SYSINFO_SOC_XBUS` | set if external Wishbone bus interface is implemented (via top's `XBUS_EN` generic)
| `2` | `SYSINFO_SOC_MEM_INT_IMEM` | set if processor-internal DMEM is implemented (via top's `MEM_INT_IMEM_EN` generic)
| `3` | `SYSINFO_SOC_MEM_INT_DMEM` | set if processor-internal IMEM is implemented (via top's `MEM_INT_DMEM_EN` generic)
| `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)
| `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
| `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)
| `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)
| `20` | `SYSINFO_SOC_IO_PWM` | set if PWM is implemented (via top's `IO_PWM_NUM_CH` generic)
| `21` | `SYSINFO_SOC_IO_WDT` | set if WDT is implemented (via top's `IO_WDT_EN` generic)
| `22` | `SYSINFO_SOC_IO_CFS` | set if custom functions subsystem is implemented (via top's `IO_CFS_EN` generic)
| `23` | `SYSINFO_SOC_IO_TRNG` | set if TRNG is implemented (via top's `IO_TRNG_EN` generic)
| `24` | `SYSINFO_SOC_IO_SDI` | set if SDI is implemented (via top's `IO_SDI_EN` generic)
| `25` | `SYSINFO_SOC_IO_UART1` | set if secondary UART1 is implemented (via top's `IO_UART1_EN` generic)
| `26` | `SYSINFO_SOC_IO_NEOLED` | set if NEOLED is implemented (via top's `IO_NEOLED_EN` generic)
| `27` | `SYSINFO_SOC_IO_XIRQ` | set if XIRQ is implemented (via top's `XIRQ_NUM_CH` generic)
| `28` | `SYSINFO_SOC_IO_GPTMR` | set if GPTMR is implemented (via top's `IO_GPTMR_EN` generic)
| `29` | `SYSINFO_SOC_IO_SLINK` | set if stream link interface is implemented (via top's `IO_SLINK_EN` generic)
| `30` | `SYSINFO_SOC_IO_ONEWIRE` | set if ONEWIRE interface is implemented (via top's `IO_ONEWIRE_EN` generic)
| `31` | `SYSINFO_SOC_IO_CRC` | set if cyclic redundancy check unit is implemented (via top's `IO_CRC_EN` generic)
|=======================

View file

@ -116,7 +116,7 @@ unconnected. If the CTS handshake is not required it has to be tied to zero.
The UART provides a _simulation-only_ mode to dump console data as well as raw data directly to a file. When the simulation
mode is enabled (by setting the `UART_CTRL_SIM_MODE` bit) there will be **no** physical transaction on the `uart0_txd_o` signal.
Instead, all data written to the `DATA` register is immediately dumped to a file. Data written to `DATA[7:0]` will be dumped as
ASCII chars to a file named `neorv32.uart0.sim_mode.text.out`. Additionally, the ASCII data is printed to the simulator console.
ASCII chars to a file named `neorv32.uart0_sim_mode.out`. Additionally, the ASCII data is printed to the simulator console.
Both file are created in the simulation's home folder.
@ -151,7 +151,7 @@ Both file are created in the simulation's home folder.
<|`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
<|`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(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
|=======================
@ -192,7 +192,7 @@ as for the primary UART. The RX and TX interrupts of UART1 are mapped to differe
**Simulation Mode**
The secondary UART (UART1) provides the same simulation options as the primary UART (UART0). However, output data is
written to UART1-specific file `neorv32.uart1.sim_mode.text.out`. This data is also printed to the simulator console.
written to UART1-specific file `neorv32.uart1_sim_mode.out`. This data is also printed to the simulator console.
**Register Map**

View file

@ -220,7 +220,7 @@ Variables:
APP_INC - C include folder(s) [append only]: "-I ."
APP_SRC - C source folder(s) [append only]: "./main.c "
ASM_INC - ASM include folder(s) [append only]: "-I ."
RISCV_PREFIX - Toolchain prefix: "riscv32-unknown-elf-"
RISCV_PREFIX - Toolchain prefix: "riscv-none-elf-"
NEORV32_HOME - NEORV32 home folder: "../../.."
GDB_ARGS - GDB (connection) arguments: "-ex target extended-remote localhost:3333"
GHDL_RUN_FLAGS - GHDL simulation run arguments: ""
@ -407,7 +407,7 @@ The `crt0.S` start-up performs the following operations:
:sectnums:
===== Early Trap Handler
==== Early Trap Handler
The start-up code provides a very basic trap handler for the early boot phase. This handler does nothing but
trying to move on to the next linear instruction whenever an interrupt or synchronous exception is encountered.
@ -429,8 +429,8 @@ by a flag when calling the generator.
[grid="none"]
|=======================
| `-app_bin` | Generates an executable binary file (including a bootloader header) for upload via the bootloader.
| `-app_img` | Generates an executable VHDL memory initialization image for the processor-internal IMEM.
| `-bld_img` | Generates an executable VHDL memory initialization image for the processor-internal BOOT ROM.
| `-app_vhd` | Generates an executable VHDL memory initialization image for the processor-internal IMEM.
| `-bld_vhd` | Generates an executable VHDL memory initialization image for the processor-internal BOOT ROM.
| `-raw_hex` | Generates a raw 8x ASCII hex-char file for custom purpose.
| `-raw_bin` | Generates a raw binary file `for custom purpose.
| `-raw_coe` | Generates a raw COE file for FPGA memory initialization.

View file

@ -26,7 +26,7 @@ The bootloader requires certain CPU and SoC extensions and modules to be enabled
[cols="^2,<8"]
[grid="none"]
|=======================
| **REQUIRED** | The bootloader is implemented only if the `INT_BOOTLOADER_EN` top generic is `true`. This will automatically select the CPU's <<_indirect_boot>> boot configuration.
| **REQUIRED** | The <<_boot_configuration>> (`BOOT_MODE_SELECT` generic) has to be set to "bootloader" mode.
| **REQUIRED** | The bootloader requires the privileged architecture CPU extension (<<_zicsr_isa_extension>>) to be enabled.
| **REQUIRED** | At least 512 bytes of data memory (processor-internal DMEM or processor-external DMEM) are required for the bootloader's stack and global variables.
| _RECOMMENDED_ | For user interaction via the <<_bootloader_console>> (like uploading executables) the primary UART (<<_primary_universal_asynchronous_receiver_and_transmitter_uart0>>) is required.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

View file

@ -46,8 +46,9 @@ Vivado project.
.Combinatorial Loops DRC Errors
[WARNING]
If the TRNG is enabled it is recommended to add the following commands to the project's constraints file in order
to prevent DRC errors during bitstream generation:
to prevent DRC errors during bitstream generation.
.Allow Combinatorial Loops
[source,xdc]
----
set_property SEVERITY {warning} [get_drc_checks LUTLP-1]
@ -65,3 +66,9 @@ shall be updated. It is **not** not possible to replace the IMEM image (`neorv32
in the packaged_ip folder. For the Vivado design suite, the new program to be executed must be compiled and installed using the
`install` makefile target. Next, the `neorv32_vivado_ip.tcl` script has to be executed again. Finally, Vivado will prompt to upgrade
the NEORV32 IP.
.AMD Vivado / ISIM - Incremental Compilation of Simulation Sources
[IMPORTANT]
When using AMD Vivado (ISIM for simulation) make sure to **TURN OFF** "incremental compilation" (_Project Setting_
-> _Simulation_ -> _Advanced_ -> _Enable incremental compilation). This will slow down simulation relaunch but will
ensure that all application images (`*_image.vhd`) are reanalyzed when recompiling the NEORV32 application or bootloader.

View file

@ -2,9 +2,9 @@
: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.
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.
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).
@ -12,28 +12,23 @@ That is used for running the RISC-V architecture tests, in order to guarantee co
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.
.AMD Vivado / ISIM
.AMD Vivado / ISIM - Incremental Compilation
[IMPORTANT]
When using AMD Vivado (ISIM for simulation) make sure to **turn of** "incremental compilation" (_Project Setting_
When using AMD Vivado (ISIM for simulation) make sure to **TURN OFF** "incremental compilation" (_Project Setting_
-> _Simulation_ -> _Advanced_ -> _Enable incremental compilation). This will slow down simulation relaunch but will
ensure that all application images (`*_image.vhd`) are reanalyzed when recompiling the NEORV32 application or bootloader
[TIP]
The processor can check if it is being _simulated_ by checking the SYSINFO _SYSINFO_SOC_IS_SIM_ flag
(see https://stnolting.github.io/neorv32/#_system_configuration_information_memory_sysinfo).
Note that this flag is not guaranteed to be set correctly (depending on the HDL toolchain's pragma support).
ensure that all application images (`*_image.vhd`) are reanalyzed when recompiling the NEORV32 application or bootloader.
:sectnums:
=== Testbench
A plain-VHDL (no third-party libraries) testbench (`sim/simple/neorv32_tb.simple.vhd`) can be used for simulating and
testing the processor.
This testbench features a 100MHz clock and enables all optional peripheral and CPU extensions except for the `E`.
.VUnit Testbench
[TIP]
A more sophisticated testbench using **VUnit** is available in a separate repository:
https://github.com/stnolting/neorv32-vunit
[IMPORTANT]
In the simple testbench several optional extensions are disabled, such as C or E.
If software is compiled using instructions corresponding to disabled extensions, the whole processor will hang in an eternal exception loop and, therefore, the simulation will timeout.
The `MARCH` must be a subset of the extensions enabled in the testbench.
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]
@ -41,41 +36,7 @@ The NEORV32 TRNG will be set to "simulation mode" when enabled for simulation (r
by pseudo-random LFSRs). See the neoTRNG documentation for more information.
The simulation setup is configured via the "User Configuration" section located right at the beginning of
the testbench's architecture. Each configuration constant provides comments to explain the functionality.
Besides the actual NEORV32 Processor, the testbench also simulates "external" components that are connected
to the processor's external bus/memory interface. These components are:
* an external instruction memory (that also allows booting from it)
* an external data memory
* an external memory to simulate "external IO devices"
* a memory-mapped registers to trigger the processor's interrupt signals
The following table shows the base addresses of these four components and their default configuration and
properties:
[NOTE]
====
Attributes:
* `r` = read
* `w` = write
* `e` = execute
* `8` = byte-accessible
* `16` = half-word-accessible
* `32` = word-accessible
====
.Testbench: processor-external memories
[cols="^4,>3,^5,<11"]
[options="header",grid="rows"]
|=======================
| Base address | Size | Attributes | Description
| `0x00000000` | `imem_size_c` | `r/w/e 8/16/32` | external IMEM (initialized with application image)
| `0x80000000` | `dmem_size_c` | `r/w/e 8/16/32` | external DMEM
| `0xf0000000` | 64 bytes | `r/w/e 8/16/32` | external "IO" memory
| `0xff000000` | 4 bytes | `-/w/- -/-/32` | memory-mapped register to trigger "machine external", "machine software" and "SoC Fast Interrupt" interrupts
|=======================
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
@ -100,7 +61,7 @@ for UART0/UART1 (see section https://stnolting.github.io/neorv32/#_primary_unive
ASCII data sent to UART0|UART1 will be immediately printed to the simulator console and logged to files in the simulator
execution directory:
* `neorv32.uart?.sim_mode.text.out`: ASCII data.
* `neorv32.uart*.sim_mode.text.out`: ASCII data.
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.
@ -123,13 +84,13 @@ completed with a line feed (newline, ASCII `\n` = 10).
:sectnums:
=== Simulation using a shell script (with GHDL)
To simulate the processor using _GHDL_ navigate to the `sim/simple/` folder and run the provided shell script.
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.
[source, bash]
----
neorv32/sim/simple$ sh ghdl.sh --stop-time=20ms
neorv32/sim$ sh ghdl.sh --stop-time=20ms
----
@ -156,10 +117,6 @@ To do a quick test of the NEORV32 make sure to have https://github.com/ghdl/ghdl
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`:
[TIP]
The simulator will output some _sanity check_ notes (and warnings or even errors if something is ill-configured)
right at the beginning of the simulation to give a brief overview of the actual NEORV32 SoC and CPU configurations.
[source, bash]
----
neorv32/sw/example/hello_world$ make USER_FLAGS+=-DUART0_SIM_MODE clean_all sim
@ -208,39 +165,3 @@ Hello world! :)
<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.
:sectnums:
=== Advanced Simulation using VUnit
https://vunit.github.io/[VUnit] is an open source unit testing framework for VHDL/SystemVerilog.
It allows continuous and automated testing of HDL code by complementing traditional testing methodologies.
The motto of VUnit is _"testing early and often"_ through automation.
VUnit is composed by a http://vunit.github.io/py/ui.html[Python interface] and multiple optional
http://vunit.github.io/vhdl_libraries.html[VHDL libraries].
The Python interface allows declaring sources and simulation options, and it handles the compilation, execution and
gathering of the results regardless of the simulator used.
That allows having a single `run.py` script to be used with GHDL, ModelSim/QuestaSim, Riviera PRO, etc.
On the other hand, the VUnit's VHDL libraries provide utilities for assertions, logging, having virtual queues, handling CSV files, etc.
The http://vunit.github.io/verification_components/user_guide.html[Verification Component Library] uses those features
for abstracting away bit-toggling when verifying standard interfaces such as Wishbone, AXI, Avalon, UARTs, etc.
Testbench sources in `sim` (such as `sim/neorv32_tb.vhd` and `sim/uart_rx*.vhd`) use VUnit's VHDL libraries for testing
NEORV32 and peripherals.
The entry-point for executing the tests is `sim/run.py`.
[source, bash]
----
# ./sim/run.py -l
neorv32.neorv32_tb.all
Listed 1 tests
# ./sim/run.py -v
Compiling into neorv32: rtl/core/neorv32_uart.vhd passed
Compiling into neorv32: rtl/core/neorv32_twi.vhd passed
Compiling into neorv32: rtl/core/neorv32_trng.vhd passed
...
----
See http://vunit.github.io/user_guide.html[VUnit: User Guide] and http://vunit.github.io/cli.html[VUnit: Command Line Interface] for further info about VUnit's features.

View file

@ -2,85 +2,71 @@
:sectnums:
== Software Toolchain Setup
To compile (and debug) executables for the NEORV32 a RISC-V toolchain is required.
There are two possibilities to get this:
To compile (and debug) executables for the NEORV32 a RISC-V-compatible toolchain is required.
By default, the project's software framework uses the GNU C Compiler RISC-V port "RISC-V GCC".
Basically, there are two options to obtain such a toolchain:
1. Download and _build_ the official RISC-V GNU toolchain yourself.
2. Download and install a prebuilt version of the toolchain; this might also done via the package manager / app store of your OS
1. Download and _build_ the RISC-V GNU toolchain by yourself.
2. Download and _install_ a **prebuilt** version of the toolchain.
.Default GCC Prefix
[NOTE]
The default toolchain prefix (`RISCV_PREFIX` variable) for this project is **`riscv32-unknown-elf-`**. Of course you can use any other RISC-V
toolchain (like `riscv64-unknown-elf-`) that is capable to emit code for a `rv32` architecture. Just change `RISCV_PREFIX`
according to your needs.
The default toolchain prefix for this project is **`riscv-none-elf-`** (`RISCV_PREFIX` variable).
:sectnums:
=== Building the Toolchain from Scratch
**Toolchain Requirements**
To build the toolchain by yourself you can follow the guide from the official https://github.com/riscv-collab/riscv-gnu-toolchain GitHub page.
You need to make sure the generated toolchain fits the architecture of the NEORV32 core. To get a toolchain that even supports minimal
ISA extension configurations, it is recommend to compile for `rv32i` only. Please note that this minimal ISA also provides further ISA
extensions like `m` or `c`. Of course you can use a _multilib_ approach to generate toolchains for several target ISAs at once.
[start=1]
.The toolchain must be able to emit code for a 32-bit architecture (i.e. `mabi=rv32`).
.A "bare metal" / stand-alone C standard library should be used (i.e. the no-Linux version; for example "Newlib").
.Preparing GCC build for `rv32i` (minimal ISA)
.Library/ISA Considerations
[IMPORTANT]
Note that a toolchain build with `--with-arch=rv32imc` provides library code (like the C standard library)
compiled entirely using compressed (`C`) and `mul`/`div` instructions (`M`). Hence, this pre-compiled library
code CANNOT be executed (without emulation) on an architecture that does not support these ISA extensions.
**Building the Toolchain from Scratch**
The official RISC-V GCC GitHub repository (https://github.com/riscv-collab/riscv-gnu-toolchain) provides instructions
for building the toolchain from scratch:
.Preparing GCC build for `rv32i` (minimal ISA only in this example)
[source,bash]
----
$ git clone https://github.com/riscv/riscv-gnu-toolchain
$ cd riscv-gnu-toolchain
$ riscv-gnu-toolchain$ ./configure --prefix=/opt/riscv --with-arch=rv32i --with-abi=ilp32
$ riscv-gnu-toolchain$ make
----
[IMPORTANT]
Keep in mind that - for instance - a toolchain build with `--with-arch=rv32imc` provides library code (like the C
standard library) compiled entirely utilizing compressed (`C`) and `mul`/`div` instructions (`M`). Hence, this
code CANNOT be executed (without emulation) on an architecture that does not support these ISA extensions.
**Downloading and Installing a Prebuilt Toolchain**
Alternatively, a prebuilt toolchain can be used. Some OS package managers provide embedded RISC-V GCC toolchain.
However, I can highly recommend the toolchain provided by the X-Pack project (MIT license):
https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack
:sectnums:
=== Downloading and Installing a Prebuilt Toolchain
**Toolchain Installation**
Alternatively, you can download a prebuilt toolchain.
:sectnums:
==== Use The Pre-Built Toolchains
We have compiled several GCC toolchains on a 64-bit x86 Ubuntu (Ubuntu on Windows, actually) and uploaded it to
GitHub. You can directly download the according toolchain archive as single _zip-file_ within a packed
release from https://github.com/stnolting/riscv-gcc-prebuilt. More information about downloading and installing
these prebuilt toolchains can be found in the repository's README.
:sectnums:
==== Use a Third Party Toolchain
Of course you can also use any other prebuilt version of the toolchain. There are a lot RISC-V GCC packages out there -
even for Windows. On Linux system you might even be able to fetch a toolchain via your distribution's package manager.
[IMPORTANT]
Make sure the toolchain can (also) emit code for a `rv32i` architecture, uses the `ilp32` or `ilp32e` ABI and **was not build** using
CPU extensions that are not supported by the NEORV32 (like `D`).
:sectnums:
=== Installation
Now you have the toolchain binaries. The last step is to add them to your `PATH` environment variable (if you have not
already done so): make sure to add the _binaries_ folder (`bin`) of your toolchain.
To integrate the toolchain of choice into the NEORV32 software framework, the toolchain's binaries need
to be added to the system path (e.g. `PATH` environment variable) so they can be used by a shell. Therefore,
the absolute path to the toolchain's `bin` folder has to be appended to the `PATH` variable:
[source,bash]
----
$ export PATH=$PATH:/opt/riscv/bin
----
You should add this command to your `.bashrc` (if you are using bash) to automatically add the RISC-V
.bashrc
[TIP]
This command can be added to `.bashrc` (or similar) to automatically add the RISC-V
toolchain at every console start.
:sectnums:
=== Testing the Installation
To make sure everything works fine, navigate to an example project in the NEORV32 example folder and
To make sure everything works fine, navigate to an example project in the NEORV32 `sw/example` folder and
execute the following command:
[source,bash]
@ -89,4 +75,4 @@ neorv32/sw/example/demo_blink_led$ make check
----
This will test all the tools required for generating NEORV32 executables.
Everything is working fine if `Toolchain check OK` appears at the end.
Everything is working fine if "Toolchain check OK" appears at the end of the log output.

View file

@ -2,7 +2,7 @@
:sectnums:
== Zephyr RTOS Support
The NEORV32 processor is supported by upstream Zephyr RTOS: https://docs.zephyrproject.org/latest/boards/riscv/neorv32/doc/index.html
The NEORV32 processor is supported by upstream Zephyr RTOS: https://docs.zephyrproject.org/latest/boards/others/neorv32/doc/index.html
[IMPORTANT]
The absolute path to the NEORV32 executable image generator binary (`.../neorv32/sw/image_gen`) has to be added to the `PATH` variable

View file

@ -1,11 +1,16 @@
-- The NEORV32 RISC-V Processor: https://github.com/stnolting/neorv32
-- Auto-generated memory initialization file (for APPLICATION) from source file <demo_blink_led/main.bin>
-- 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
-- MARCH: default
-- Built: 15.09.2024 21:48:24 (dd.mm.yyyy hh:mm:ss)
-- Built: 31.10.2024 22:32:21
-- prototype defined in 'neorv32_package.vhd'
package body neorv32_application_image is
library ieee;
use ieee.std_logic_1164.all;
library neorv32;
use neorv32.neorv32_package.all;
package neorv32_application_image is
constant application_init_image : mem32_t := (
x"000020b7",

View file

@ -1,11 +1,16 @@
-- The NEORV32 RISC-V Processor: https://github.com/stnolting/neorv32
-- Auto-generated memory initialization file (for BOOTLOADER) from source file <bootloader/main.bin>
-- The NEORV32 RISC-V Processor - github.com/stnolting/neorv32
-- Auto-generated memory initialization package (for internal BOOTROM)
-- Source: bootloader/main.bin
-- Size: 4072 bytes
-- MARCH: default
-- Built: 27.09.2024 05:54:10 (dd.mm.yyyy hh:mm:ss)
-- Built: 31.10.2024 22:31:51
-- prototype defined in 'neorv32_package.vhd'
package body neorv32_bootloader_image is
library ieee;
use ieee.std_logic_1164.all;
library neorv32;
use neorv32.neorv32_package.all;
package neorv32_bootloader_image is
constant bootloader_init_image : mem32_t := (
x"000020b7",
@ -894,9 +899,9 @@ x"6f6c746f",
x"72656461",
x"0a3e3e20",
x"444c420a",
x"53203a56",
x"32207065",
x"30322037",
x"4f203a56",
x"33207463",
x"30322031",
x"480a3432",
x"203a5657",
x"00000020",

View file

@ -374,7 +374,7 @@ use neorv32.neorv32_package.all;
entity neorv32_bus_io_switch is
generic (
DEV_SIZE : natural; -- size of a single IO device, has to be a power of two
DEV_SIZE : natural; -- 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');
@ -417,38 +417,38 @@ entity neorv32_bus_io_switch is
main_req_i : in bus_req_t; -- host request
main_rsp_o : out bus_rsp_t; -- host response
-- device ports --
dev_00_req_o : out bus_req_t; dev_00_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_01_req_o : out bus_req_t; dev_01_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_02_req_o : out bus_req_t; dev_02_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_03_req_o : out bus_req_t; dev_03_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_04_req_o : out bus_req_t; dev_04_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_05_req_o : out bus_req_t; dev_05_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_06_req_o : out bus_req_t; dev_06_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_07_req_o : out bus_req_t; dev_07_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_08_req_o : out bus_req_t; dev_08_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_09_req_o : out bus_req_t; dev_09_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_10_req_o : out bus_req_t; dev_10_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_11_req_o : out bus_req_t; dev_11_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_12_req_o : out bus_req_t; dev_12_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_13_req_o : out bus_req_t; dev_13_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_14_req_o : out bus_req_t; dev_14_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_15_req_o : out bus_req_t; dev_15_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_16_req_o : out bus_req_t; dev_16_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_17_req_o : out bus_req_t; dev_17_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_18_req_o : out bus_req_t; dev_18_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_19_req_o : out bus_req_t; dev_19_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_20_req_o : out bus_req_t; dev_20_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_21_req_o : out bus_req_t; dev_21_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_22_req_o : out bus_req_t; dev_22_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_23_req_o : out bus_req_t; dev_23_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_24_req_o : out bus_req_t; dev_24_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_25_req_o : out bus_req_t; dev_25_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_26_req_o : out bus_req_t; dev_26_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_27_req_o : out bus_req_t; dev_27_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_28_req_o : out bus_req_t; dev_28_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_29_req_o : out bus_req_t; dev_29_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_30_req_o : out bus_req_t; dev_30_rsp_i : in bus_rsp_t := rsp_terminate_c;
dev_31_req_o : out bus_req_t; dev_31_rsp_i : in bus_rsp_t := rsp_terminate_c
dev_00_req_o : out bus_req_t; dev_00_rsp_i : in bus_rsp_t;
dev_01_req_o : out bus_req_t; dev_01_rsp_i : in bus_rsp_t;
dev_02_req_o : out bus_req_t; dev_02_rsp_i : in bus_rsp_t;
dev_03_req_o : out bus_req_t; dev_03_rsp_i : in bus_rsp_t;
dev_04_req_o : out bus_req_t; dev_04_rsp_i : in bus_rsp_t;
dev_05_req_o : out bus_req_t; dev_05_rsp_i : in bus_rsp_t;
dev_06_req_o : out bus_req_t; dev_06_rsp_i : in bus_rsp_t;
dev_07_req_o : out bus_req_t; dev_07_rsp_i : in bus_rsp_t;
dev_08_req_o : out bus_req_t; dev_08_rsp_i : in bus_rsp_t;
dev_09_req_o : out bus_req_t; dev_09_rsp_i : in bus_rsp_t;
dev_10_req_o : out bus_req_t; dev_10_rsp_i : in bus_rsp_t;
dev_11_req_o : out bus_req_t; dev_11_rsp_i : in bus_rsp_t;
dev_12_req_o : out bus_req_t; dev_12_rsp_i : in bus_rsp_t;
dev_13_req_o : out bus_req_t; dev_13_rsp_i : in bus_rsp_t;
dev_14_req_o : out bus_req_t; dev_14_rsp_i : in bus_rsp_t;
dev_15_req_o : out bus_req_t; dev_15_rsp_i : in bus_rsp_t;
dev_16_req_o : out bus_req_t; dev_16_rsp_i : in bus_rsp_t;
dev_17_req_o : out bus_req_t; dev_17_rsp_i : in bus_rsp_t;
dev_18_req_o : out bus_req_t; dev_18_rsp_i : in bus_rsp_t;
dev_19_req_o : out bus_req_t; dev_19_rsp_i : in bus_rsp_t;
dev_20_req_o : out bus_req_t; dev_20_rsp_i : in bus_rsp_t;
dev_21_req_o : out bus_req_t; dev_21_rsp_i : in bus_rsp_t;
dev_22_req_o : out bus_req_t; dev_22_rsp_i : in bus_rsp_t;
dev_23_req_o : out bus_req_t; dev_23_rsp_i : in bus_rsp_t;
dev_24_req_o : out bus_req_t; dev_24_rsp_i : in bus_rsp_t;
dev_25_req_o : out bus_req_t; dev_25_rsp_i : in bus_rsp_t;
dev_26_req_o : out bus_req_t; dev_26_rsp_i : in bus_rsp_t;
dev_27_req_o : out bus_req_t; dev_27_rsp_i : in bus_rsp_t;
dev_28_req_o : out bus_req_t; dev_28_rsp_i : in bus_rsp_t;
dev_29_req_o : out bus_req_t; dev_29_rsp_i : in bus_rsp_t;
dev_30_req_o : out bus_req_t; dev_30_rsp_i : in bus_rsp_t;
dev_31_req_o : out bus_req_t; dev_31_rsp_i : in bus_rsp_t
);
end neorv32_bus_io_switch;

View file

@ -30,12 +30,12 @@ entity neorv32_cfs is
clk_i : in std_ulogic; -- global clock line
rstn_i : in std_ulogic; -- global reset line, low-active, use as async
bus_req_i : in bus_req_t; -- bus request
bus_rsp_o : out bus_rsp_t := rsp_terminate_c; -- bus response
clkgen_en_o : out std_ulogic := '0'; -- enable clock generator
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); -- "clock" inputs
irq_o : out std_ulogic := '0'; -- interrupt request
irq_o : out std_ulogic; -- interrupt request
cfs_in_i : in std_ulogic_vector(CFS_IN_SIZE-1 downto 0); -- custom inputs
cfs_out_o : out std_ulogic_vector(CFS_OUT_SIZE-1 downto 0) := (others => '0') -- custom outputs
cfs_out_o : out std_ulogic_vector(CFS_OUT_SIZE-1 downto 0) -- custom outputs
);
end neorv32_cfs;

View file

@ -123,7 +123,7 @@ architecture neorv32_cpu_rtl of neorv32_cpu is
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 (return address)
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 irq_machine : std_ulogic_vector(2 downto 0); -- risc-v standard machine-level interrupts
@ -245,7 +245,7 @@ begin
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 (return address)
pc_ret_o => pc_ret, -- return address
csr_rdata_o => csr_rdata, -- CSR read data
-- external CSR interface --
xcsr_we_o => xcsr_we, -- global write enable
@ -294,7 +294,7 @@ begin
);
-- all buses are zero unless there is an according operation --
rf_wdata <= alu_res or lsu_rdata or csr_rdata or pc_next;
rf_wdata <= alu_res or lsu_rdata or csr_rdata or pc_ret;
-- ALU (Arithmetic/Logic Unit) and ALU Co-Processors --------------------------------------

View file

@ -90,7 +90,7 @@ entity neorv32_cpu_control is
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 (return address)
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 --
xcsr_we_o : out std_ulogic; -- global write enable
@ -150,31 +150,26 @@ 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_WAIT);
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_t is record
state : exe_engine_state_t;
state_nxt : exe_engine_state_t;
ir : std_ulogic_vector(31 downto 0); -- instruction word being executed right now
ir_nxt : std_ulogic_vector(31 downto 0);
is_ci : std_ulogic; -- current instruction is de-compressed instruction
is_ci_nxt : std_ulogic;
branch_taken : std_ulogic; -- branch condition fulfilled
pc : std_ulogic_vector(XLEN-1 downto 0); -- actual PC, corresponding to current executed instruction
pc_we : std_ulogic; -- PC update enable
next_pc : std_ulogic_vector(XLEN-1 downto 0); -- next PC, corresponding to next instruction to be executed
state : exe_engine_state_t;
ir : std_ulogic_vector(31 downto 0); -- instruction word being executed right now
ci : std_ulogic; -- current instruction is de-compressed instruction
pc : std_ulogic_vector(XLEN-1 downto 0); -- current PC (current instruction)
pc2 : std_ulogic_vector(XLEN-1 downto 0); -- next PC (next linear instruction)
ra : std_ulogic_vector(XLEN-1 downto 0); -- return address
end record;
signal exe_engine : exe_engine_t;
signal exe_engine, exe_engine_nxt : exe_engine_t;
-- taken branch --
signal branch_taken : std_ulogic;
-- simplified opcode (2 LSBs hardwired to "11" to indicate rv32) --
signal opcode : std_ulogic_vector(6 downto 0);
-- execution monitor --
type monitor_t is record
cnt, cnt_add : std_ulogic_vector(monitor_mc_tmo_c downto 0);
exc : std_ulogic;
end record;
signal monitor : monitor_t;
signal monitor_cnt, monitor_add : std_ulogic_vector(monitor_mc_tmo_c downto 0);
signal monitor_exc : std_ulogic;
-- CPU sleep-mode --
signal sleep_mode : std_ulogic;
@ -211,8 +206,8 @@ 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
re, re_nxt : std_ulogic; -- read enable
wdata, rdata : std_ulogic_vector(XLEN-1 downto 0); -- read/write data
wdata : std_ulogic_vector(XLEN-1 downto 0); -- write data
rdata : std_ulogic_vector(XLEN-1 downto 0); -- read data
--
mstatus_mie : std_ulogic; -- machine-mode IRQ enable
mstatus_mpie : std_ulogic; -- previous machine-mode IRQ enable
@ -292,7 +287,7 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
signal illegal_cmd : std_ulogic;
-- CSR access existence/read-write/privilege check --
signal csr_valid : std_ulogic_vector(2 downto 0); -- 2: implemented, 1: r/w access, 0: privilege
signal csr_valid : std_ulogic_vector(2 downto 0); -- [2]: implemented, [1]: r/w access, [0]: privilege
-- hardware trigger module --
signal hw_trigger_match, hw_trigger_fired : std_ulogic;
@ -345,7 +340,7 @@ begin
when others => -- IF_RESTART: set new start address
-- ------------------------------------------------------------
fetch_engine.pc <= exe_engine.next_pc(XLEN-1 downto 1) & '0'; -- initialize from PC incl. 16-bit-alignment bit
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;
@ -413,7 +408,7 @@ begin
-- ****************************************************************************************************************************
-- Instruction Issue (decompress 16-bit instructions and assemble a 32-bit instruction word)
-- Instruction Issue (decompress 16-bit instruction and/or assemble a 32-bit instruction word)
-- ****************************************************************************************************************************
issue_engine_enabled:
@ -427,7 +422,7 @@ begin
issue_engine.align <= '0'; -- start aligned after reset
elsif rising_edge(clk_i) then
if (fetch_engine.restart = '1') then
issue_engine.align <= exe_engine.next_pc(1); -- branch to unaligned address?
issue_engine.align <= exe_engine.pc2(1); -- branch to unaligned address?
elsif (issue_engine.ack = '1') then
issue_engine.align <= (issue_engine.align and (not issue_engine.align_clr)) or issue_engine.align_set; -- "rs flip-flop"
end if;
@ -506,7 +501,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 = DISPATCH) then -- prepare update of next PC (using ALU's PC + IMM in 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";
@ -514,7 +509,6 @@ begin
alu_imm_o(3 downto 0) <= x"4";
end if;
else
alu_imm_o <= replicate_f(exe_engine.ir(31), 21) & exe_engine.ir(30 downto 21) & exe_engine.ir(20); -- default: I-immediate
case opcode is
when opcode_store_c => -- S-immediate
alu_imm_o <= replicate_f(exe_engine.ir(31), 21) & exe_engine.ir(30 downto 25) & exe_engine.ir(11 downto 7);
@ -525,9 +519,9 @@ begin
when opcode_jal_c => -- J-immediate
alu_imm_o <= replicate_f(exe_engine.ir(31), 12) & exe_engine.ir(19 downto 12) & exe_engine.ir(20) & exe_engine.ir(30 downto 21) & '0';
when opcode_amo_c => -- atomic memory access
if RISCV_ISA_Zalrsc then alu_imm_o <= (others => '0'); end if;
when others =>
NULL; -- use default
alu_imm_o <= (others => '0');
when others => -- I-immediate
alu_imm_o <= replicate_f(exe_engine.ir(31), 21) & exe_engine.ir(30 downto 21) & exe_engine.ir(20);
end case;
end if;
end if;
@ -540,12 +534,12 @@ begin
begin
if (exe_engine.ir(instr_opcode_lsb_c+2) = '0') then -- conditional branch
if (exe_engine.ir(instr_funct3_msb_c) = '0') then -- beq / bne
exe_engine.branch_taken <= alu_cmp_i(cmp_equal_c) xor exe_engine.ir(instr_funct3_lsb_c);
branch_taken <= alu_cmp_i(cmp_equal_c) xor exe_engine.ir(instr_funct3_lsb_c);
else -- blt(u) / bge(u)
exe_engine.branch_taken <= alu_cmp_i(cmp_less_c) xor exe_engine.ir(instr_funct3_lsb_c);
branch_taken <= alu_cmp_i(cmp_less_c) xor exe_engine.ir(instr_funct3_lsb_c);
end if;
else -- unconditional branch
exe_engine.branch_taken <= '1';
branch_taken <= '1';
end if;
end process branch_check;
@ -555,79 +549,22 @@ begin
execute_engine_fsm_sync: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
ctrl <= ctrl_bus_zero_c;
exe_engine.state <= RESTART;
exe_engine.ir <= (others => '0');
exe_engine.is_ci <= '0';
exe_engine.pc <= BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit-aligned boot address
exe_engine.next_pc <= BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit-aligned boot address
pc_next_o <= (others => '0');
ctrl <= ctrl_bus_zero_c;
exe_engine.state <= 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
exe_engine.pc2 <= BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit-aligned boot address
exe_engine.ra <= (others => '0');
elsif rising_edge(clk_i) then
-- control bus --
ctrl <= ctrl_nxt;
-- execute engine arbiter --
exe_engine.state <= exe_engine.state_nxt;
exe_engine.ir <= exe_engine.ir_nxt;
exe_engine.is_ci <= exe_engine.is_ci_nxt;
-- current PC: address of instruction being executed --
if (exe_engine.pc_we = '1') then
exe_engine.pc <= exe_engine.next_pc(XLEN-1 downto 1) & '0';
end if;
-- next PC: address of next instruction --
pc_next_o <= (others => '0'); -- output zero if not a branch instruction
case exe_engine.state is
when TRAP_ENTER => -- starting trap environment
-- ------------------------------------------------------------
if (trap_ctrl.cause(5) = '1') and RISCV_ISA_Sdext then -- debug mode (re-)entry
exe_engine.next_pc <= DEBUG_PARK_ADDR(XLEN-1 downto 2) & "00"; -- debug mode enter; start at "parking loop" <normal_entry>
elsif (debug_ctrl.run = '1') and RISCV_ISA_Sdext then -- any other trap INSIDE debug mode
exe_engine.next_pc <= DEBUG_EXC_ADDR(XLEN-1 downto 2) & "00"; -- debug mode enter: start at "parking loop" <exception_entry>
else -- normal start of trap
if (csr.mtvec(0) = '1') and (trap_ctrl.cause(6) = '1') then -- vectored mode + interrupt
exe_engine.next_pc <= csr.mtvec(XLEN-1 downto 7) & trap_ctrl.cause(4 downto 0) & "00"; -- pc = mtvec + 4 * mcause
else
exe_engine.next_pc <= csr.mtvec(XLEN-1 downto 2) & "00"; -- pc = mtvec
end if;
end if;
when TRAP_EXIT => -- leaving trap environment
-- ------------------------------------------------------------
if (debug_ctrl.run = '1') and RISCV_ISA_Sdext then -- debug mode exit
exe_engine.next_pc <= csr.dpc(XLEN-1 downto 1) & '0';
else -- normal end of trap
exe_engine.next_pc <= csr.mepc(XLEN-1 downto 1) & '0';
end if;
when BRANCH => -- branch instruction
-- ------------------------------------------------------------
pc_next_o <= exe_engine.next_pc(XLEN-1 downto 1) & '0'; -- output as link/return address
if (trap_ctrl.exc_buf(exc_illegal_c) = '0') and (exe_engine.branch_taken = '1') then -- valid taken branch
exe_engine.next_pc <= alu_add_i(XLEN-1 downto 1) & '0';
end if;
when EXECUTE => -- linear increment (use ALU's adder to compute next_pc = current_pc + imm (2/4))
-- ------------------------------------------------------------
exe_engine.next_pc <= alu_add_i(XLEN-1 downto 1) & '0';
when others => -- no update
-- ------------------------------------------------------------
NULL;
end case;
ctrl <= ctrl_nxt;
exe_engine <= exe_engine_nxt;
end if;
end process execute_engine_fsm_sync;
-- check if branch destination is misaligned --
trap_ctrl.instr_ma <= '1' when (exe_engine.state = BRANCH) and -- branch instruction (can also be INVALID as exc_illegal_c has higher priority)
(exe_engine.branch_taken = '1') and -- branch is taken
(alu_add_i(1) = '1') and (not RISCV_ISA_C) else '0'; -- misaligned destination
-- current PC output --
pc_curr_o <= exe_engine.pc(XLEN-1 downto 1) & '0';
-- PC output --
pc_curr_o <= exe_engine.pc(XLEN-1 downto 1) & '0'; -- address of current instruction
pc_ret_o <= exe_engine.ra(XLEN-1 downto 1) & '0'; -- return address
-- simplified rv32 opcode --
opcode <= exe_engine.ir(instr_opcode_msb_c downto instr_opcode_lsb_c+2) & "11";
@ -635,7 +572,7 @@ 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)
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)
variable funct3_v : std_ulogic_vector(2 downto 0);
variable funct7_v : std_ulogic_vector(6 downto 0);
begin
@ -644,20 +581,22 @@ begin
funct7_v := exe_engine.ir(instr_funct7_msb_c downto instr_funct7_lsb_c);
-- arbiter defaults --
exe_engine.state_nxt <= exe_engine.state;
exe_engine.ir_nxt <= exe_engine.ir;
exe_engine.is_ci_nxt <= exe_engine.is_ci;
exe_engine.pc_we <= '0';
exe_engine_nxt.state <= exe_engine.state;
exe_engine_nxt.ir <= exe_engine.ir;
exe_engine_nxt.ci <= exe_engine.ci;
exe_engine_nxt.pc <= exe_engine.pc;
exe_engine_nxt.pc2 <= exe_engine.pc2;
exe_engine_nxt.ra <= (others => '0'); -- output zero if not a branch instruction
issue_engine.ack <= '0';
fetch_engine.reset <= '0';
trap_ctrl.env_enter <= '0';
trap_ctrl.env_exit <= '0';
trap_ctrl.instr_be <= '0';
trap_ctrl.instr_ma <= '0';
trap_ctrl.ecall <= '0';
trap_ctrl.ebreak <= '0';
trap_ctrl.hwtrig <= '0';
csr.we_nxt <= '0';
csr.re_nxt <= '0';
ctrl_nxt <= ctrl_bus_zero_c; -- all zero/off by default (ALU operation = ZERO, ALU.adder_out = ADD)
-- ALU sign control --
@ -695,45 +634,65 @@ begin
when 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 EXECUTE (opa = current PC)
ctrl_nxt.alu_opb_mux <= '1'; -- prepare update of next PC in 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.state_nxt <= TRAP_ENTER;
exe_engine_nxt.state <= TRAP_ENTER;
elsif RISCV_ISA_Sdtrig and (hw_trigger_match = '1') then -- hardware breakpoint
exe_engine.pc_we <= '1'; -- pc <= next_pc; intercept BEFORE executing the instruction
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.state_nxt <= DISPATCH; -- stay here another round until trap_ctrl.hwtrig arrives in trap_ctrl.env_pending
exe_engine_nxt.state <= 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.is_ci_nxt <= issue_engine.data(33); -- this is a de-compressed instruction
exe_engine.ir_nxt <= issue_engine.data(31 downto 0); -- instruction word
exe_engine.pc_we <= '1'; -- pc <= next_pc
exe_engine.state_nxt <= EXECUTE;
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
end if;
when 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>
elsif (debug_ctrl.run = '1') and RISCV_ISA_Sdext then -- any other trap INSIDE debug mode
exe_engine_nxt.pc2 <= DEBUG_EXC_ADDR(XLEN-1 downto 2) & "00"; -- debug mode enter: start at "parking loop" <exception_entry>
else -- normal start of trap
if (csr.mtvec(0) = '1') and (trap_ctrl.cause(6) = '1') then -- vectored mode + interrupt
exe_engine_nxt.pc2 <= csr.mtvec(XLEN-1 downto 7) & trap_ctrl.cause(4 downto 0) & "00"; -- PC = mtvec + 4 * mcause
else
exe_engine_nxt.pc2 <= csr.mtvec(XLEN-1 downto 2) & "00"; -- PC = mtvec
end if;
end if;
--
if (trap_ctrl.env_pending = '1') then -- wait for sync. exceptions to become pending
trap_ctrl.env_enter <= '1';
exe_engine.state_nxt <= RESTART;
exe_engine_nxt.state <= RESTART; -- restart instruction fetch
end if;
when 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';
else -- normal end of trap
exe_engine_nxt.pc2 <= csr.mepc(XLEN-1 downto 1) & '0';
end if;
trap_ctrl.env_exit <= '1';
exe_engine.state_nxt <= RESTART;
exe_engine_nxt.state <= RESTART; -- restart instruction fetch
when RESTART => -- reset and restart instruction fetch at <next_pc>
when 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
fetch_engine.reset <= '1';
exe_engine.state_nxt <= BRANCHED;
exe_engine_nxt.state <= 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
-- ------------------------------------------------------------
exe_engine_nxt.pc2 <= alu_add_i(XLEN-1 downto 1) & '0'; -- next PC (= PC + immediate)
-- decode instruction class/type --
case opcode is
-- register/immediate ALU operation --
@ -763,51 +722,50 @@ 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.state_nxt <= DISPATCH;
exe_engine_nxt.state <= 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.state_nxt <= ALU_WAIT;
exe_engine_nxt.state <= 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.state_nxt <= DISPATCH;
exe_engine_nxt.state <= 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.state_nxt <= DISPATCH;
exe_engine_nxt.state <= DISPATCH;
-- memory access --
when opcode_load_c | opcode_store_c | opcode_amo_c =>
exe_engine.state_nxt <= MEM_REQ;
exe_engine_nxt.state <= MEM_REQ;
-- branch / jump-and-link (with register) --
when opcode_branch_c | opcode_jal_c | opcode_jalr_c =>
exe_engine.state_nxt <= BRANCH;
exe_engine_nxt.state <= 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.state_nxt <= RESTART; -- reset instruction fetch + IPB (actually only required for fence.i)
exe_engine_nxt.state <= 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.state_nxt <= ALU_WAIT; -- will be aborted via monitor timeout if FPU is not implemented
exe_engine_nxt.state <= 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.state_nxt <= ALU_WAIT; -- will be aborted via monitor timeout if CFU is not implemented
exe_engine_nxt.state <= ALU_WAIT; -- will be aborted via monitor timeout if CFU is not implemented
-- environment/CSR operation or ILLEGAL opcode --
when others =>
csr.re_nxt <= '1';
exe_engine.state_nxt <= SYSTEM;
exe_engine_nxt.state <= SYSTEM;
end case; -- /EXECUTE
@ -816,31 +774,34 @@ begin
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.state_nxt <= DISPATCH;
exe_engine_nxt.state <= DISPATCH;
end if;
when BRANCH => -- update next_pc on taken branches and jumps
when 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)
if (trap_ctrl.exc_buf(exc_illegal_c) = '0') and (exe_engine.branch_taken = '1') then -- valid taken branch
if (trap_ctrl.exc_buf(exc_illegal_c) = '0') and (branch_taken = '1') then -- valid taken branch
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.state_nxt <= BRANCHED; -- shortcut (faster than going to RESTART)
exe_engine_nxt.pc2 <= alu_add_i(XLEN-1 downto 1) & '0';
exe_engine_nxt.state <= BRANCHED; -- shortcut (faster than going to RESTART)
else
exe_engine.state_nxt <= DISPATCH;
exe_engine_nxt.state <= DISPATCH;
end if;
when BRANCHED => -- delay cycle to wait for reset of pipeline front-end (instruction fetch)
-- ------------------------------------------------------------
exe_engine.state_nxt <= DISPATCH;
exe_engine_nxt.state <= DISPATCH;
when 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.state_nxt <= MEM_WAIT;
exe_engine_nxt.state <= MEM_RSP;
when MEM_WAIT => -- wait for bus transaction to finish
when 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
@ -849,25 +810,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.state_nxt <= DISPATCH;
exe_engine_nxt.state <= DISPATCH;
end if;
when SLEEP => -- sleep mode
-- ------------------------------------------------------------
if (trap_ctrl.wakeup = '1') then
exe_engine.state_nxt <= DISPATCH;
exe_engine_nxt.state <= DISPATCH;
end if;
when others => -- SYSTEM - CSR/ENVIRONMENT operation; no effect if illegal instruction
-- ------------------------------------------------------------
exe_engine.state_nxt <= DISPATCH; -- default
exe_engine_nxt.state <= 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.state_nxt <= TRAP_EXIT; -- xret
when "101" => exe_engine.state_nxt <= SLEEP; -- wfi
when others => exe_engine.state_nxt <= DISPATCH; -- illegal or CSR operation
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
end case;
end if;
-- always write to CSR (if CSR instruction); ENVIRONMENT operations have rs1/imm5 = zero so this won't happen then --
@ -925,17 +886,17 @@ begin
multi_cycle_monitor: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
monitor.cnt <= (others => '0');
monitor_cnt <= (others => '0');
elsif rising_edge(clk_i) then
monitor.cnt <= std_ulogic_vector(unsigned(monitor.cnt_add) + 1);
monitor_cnt <= std_ulogic_vector(unsigned(monitor_add) + 1);
end if;
end process multi_cycle_monitor;
-- timeout counter (allow mapping of entire logic into the LUTs in front of the carry-chain) --
monitor.cnt_add <= monitor.cnt when (exe_engine.state = ALU_WAIT) else (others => '0');
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);
monitor_exc <= monitor_cnt(monitor_cnt'left);
-- CSR Access Check -----------------------------------------------------------------------
@ -1119,7 +1080,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
((monitor.exc = '1') or (illegal_cmd = '1')) else '0'; -- instruction timeout or illegal instruction
((monitor_exc = '1') or (illegal_cmd = '1')) else '0'; -- instruction timeout or illegal instruction
-- ****************************************************************************************************************************
@ -1233,7 +1194,7 @@ begin
if (trap_ctrl.exc_buf(exc_iaccess_c) = '1') then trap_ctrl.cause <= trap_iaf_c; -- instruction access fault
elsif (trap_ctrl.exc_buf(exc_illegal_c) = '1') then trap_ctrl.cause <= trap_iil_c; -- illegal instruction
elsif (trap_ctrl.exc_buf(exc_ialign_c) = '1') then trap_ctrl.cause <= trap_ima_c; -- instruction address misaligned
elsif (trap_ctrl.exc_buf(exc_ecall_c) = '1') then trap_ctrl.cause <= trap_env_c(6 downto 2) & csr.privilege & csr.privilege; -- environment call (U/M)
elsif (trap_ctrl.exc_buf(exc_ecall_c) = '1') then trap_ctrl.cause <= trap_env_c(6 downto 2) & replicate_f(csr.privilege, 2); -- environment call (U/M)
elsif (trap_ctrl.exc_buf(exc_ebreak_c) = '1') then trap_ctrl.cause <= trap_brk_c; -- environment breakpoint
elsif (trap_ctrl.exc_buf(exc_salign_c) = '1') then trap_ctrl.cause <= trap_sma_c; -- store address misaligned
elsif (trap_ctrl.exc_buf(exc_lalign_c) = '1') then trap_ctrl.cause <= trap_lma_c; -- load address misaligned
@ -1270,7 +1231,7 @@ begin
end process trap_priority;
-- exception program counter: async. interrupt or sync. exception? --
trap_ctrl.epc <= exe_engine.next_pc when (trap_ctrl.cause(trap_ctrl.cause'left) = '1') else exe_engine.pc;
trap_ctrl.epc <= exe_engine.pc2 when (trap_ctrl.cause(trap_ctrl.cause'left) = '1') else exe_engine.pc;
-- Trap Controller ------------------------------------------------------------------------
@ -1410,7 +1371,7 @@ begin
csr.mie_firq <= (others => '0');
csr.mtvec <= (others => '0');
csr.mscratch <= x"19880704";
csr.mepc <= (others => '0');
csr.mepc <= BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit-aligned boot address
csr.mcause <= (others => '0');
csr.mtval <= (others => '0');
csr.mtinst <= (others => '0');
@ -1437,115 +1398,126 @@ begin
-- Software CSR access
-- ********************************************************************************
if (csr.we = '1') then
case csr.addr is
-- --------------------------------------------------------------------
-- machine trap setup
-- --------------------------------------------------------------------
when csr_mstatus_c => -- machine status register
csr.mstatus_mie <= csr.wdata(3);
csr.mstatus_mpie <= csr.wdata(7);
if RISCV_ISA_U then
csr.mstatus_mpp <= csr.wdata(11) or csr.wdata(12); -- everything /= U will fall back to M
csr.mstatus_mprv <= csr.wdata(17);
csr.mstatus_tw <= csr.wdata(21);
end if;
-- --------------------------------------------------------------------
-- machine trap setup
-- --------------------------------------------------------------------
when csr_mie_c => -- machine interrupt enable register
csr.mie_msi <= csr.wdata(3);
csr.mie_mti <= csr.wdata(7);
csr.mie_mei <= csr.wdata(11);
csr.mie_firq <= csr.wdata(31 downto 16);
-- machine status register --
if (csr.addr = csr_mstatus_c) then
csr.mstatus_mie <= csr.wdata(3);
csr.mstatus_mpie <= csr.wdata(7);
if RISCV_ISA_U then
csr.mstatus_mpp <= csr.wdata(11) or csr.wdata(12); -- everything /= U will fall back to M
csr.mstatus_mprv <= csr.wdata(17);
csr.mstatus_tw <= csr.wdata(21);
end if;
end if;
when csr_mtvec_c => -- machine trap-handler base address
csr.mtvec <= csr.wdata(XLEN-1 downto 2) & '0' & csr.wdata(0); -- base + mode (vectored/direct)
-- machine interrupt enable register --
if (csr.addr = csr_mie_c) then
csr.mie_msi <= csr.wdata(3);
csr.mie_mti <= csr.wdata(7);
csr.mie_mei <= csr.wdata(11);
csr.mie_firq <= csr.wdata(31 downto 16);
end if;
when csr_mcounteren_c => -- machine counter access enable
if RISCV_ISA_U and RISCV_ISA_Zicntr then
csr.mcounteren_cy <= csr.wdata(0);
csr.mcounteren_ir <= csr.wdata(2);
end if;
-- machine trap-handler base address --
if (csr.addr = csr_mtvec_c) then
csr.mtvec <= csr.wdata(XLEN-1 downto 2) & '0' & csr.wdata(0); -- base + mode (vectored/direct)
end if;
-- --------------------------------------------------------------------
-- machine trap handling
-- --------------------------------------------------------------------
when csr_mscratch_c => -- machine scratch register
csr.mscratch <= csr.wdata;
-- machine counter access enable --
if (csr.addr = csr_mcounteren_c) and RISCV_ISA_U and RISCV_ISA_Zicntr then
csr.mcounteren_cy <= csr.wdata(0);
csr.mcounteren_ir <= csr.wdata(2);
end if;
when csr_mepc_c => -- machine exception program counter
csr.mepc <= csr.wdata(XLEN-1 downto 1) & '0';
if not RISCV_ISA_C then -- RISC-V priv. spec.: MEPC[1] is masked when IALIGN = 32
csr.mepc(1) <= '0';
end if;
-- --------------------------------------------------------------------
-- machine trap handling
-- --------------------------------------------------------------------
when csr_mcause_c => -- machine trap cause
csr.mcause <= csr.wdata(31) & csr.wdata(4 downto 0); -- type (exception/interrupt) & identifier
-- machine scratch register --
if (csr.addr = csr_mscratch_c) then
csr.mscratch <= csr.wdata;
end if;
-- --------------------------------------------------------------------
-- machine counter setup
-- --------------------------------------------------------------------
when csr_mcountinhibit_c => -- machine counter-inhibit register
if RISCV_ISA_Zicntr then
csr.mcountinhibit(0) <= csr.wdata(0);
csr.mcountinhibit(2) <= csr.wdata(2);
end if;
if RISCV_ISA_Zihpm then
csr.mcountinhibit(15 downto 3) <= csr.wdata(15 downto 3);
end if;
-- machine exception program counter --
if (csr.addr = csr_mepc_c) then
csr.mepc <= csr.wdata(XLEN-1 downto 1) & '0';
if not RISCV_ISA_C then -- RISC-V priv. spec.: MEPC[1] is masked when IALIGN = 32
csr.mepc(1) <= '0';
end if;
end if;
-- --------------------------------------------------------------------
-- debug mode CSRs
-- --------------------------------------------------------------------
when csr_dcsr_c => -- debug mode control and status register
if RISCV_ISA_Sdext then
csr.dcsr_ebreakm <= csr.wdata(15);
csr.dcsr_step <= csr.wdata(2);
if RISCV_ISA_U then
csr.dcsr_ebreaku <= csr.wdata(12);
csr.dcsr_prv <= csr.wdata(1) or csr.wdata(0); -- everything /= U will fall back to M
end if;
end if;
-- machine trap cause --
if (csr.addr = csr_mcause_c) then
csr.mcause <= csr.wdata(31) & csr.wdata(4 downto 0); -- type (exception/interrupt) & identifier
end if;
when csr_dpc_c => -- debug mode program counter
if RISCV_ISA_Sdext then
csr.dpc <= csr.wdata(XLEN-1 downto 1) & '0';
if not RISCV_ISA_C then -- RISC-V priv. spec.: DPC[1] is masked when IALIGN = 32
csr.dpc(1) <= '0';
end if;
end if;
-- --------------------------------------------------------------------
-- machine counter setup
-- --------------------------------------------------------------------
when csr_dscratch0_c => -- debug mode scratch register 0
if RISCV_ISA_Sdext then
csr.dscratch0 <= csr.wdata;
end if;
-- machine counter-inhibit register --
if (csr.addr = csr_mcountinhibit_c) then
if RISCV_ISA_Zicntr then
csr.mcountinhibit(0) <= csr.wdata(0);
csr.mcountinhibit(2) <= csr.wdata(2);
end if;
if RISCV_ISA_Zihpm then
csr.mcountinhibit(15 downto 3) <= csr.wdata(15 downto 3);
end if;
end if;
-- --------------------------------------------------------------------
-- trigger module CSRs
-- --------------------------------------------------------------------
when csr_tdata1_c => -- match control
if RISCV_ISA_Sdtrig then
if (csr.tdata1_dmode = '0') or (debug_ctrl.run = '1') then -- write access from debug-mode only?
csr.tdata1_execute <= csr.wdata(2);
csr.tdata1_action <= csr.wdata(12);
end if;
if (debug_ctrl.run = '1') then -- writable from debug-mode only
csr.tdata1_dmode <= csr.wdata(27);
end if;
end if;
-- --------------------------------------------------------------------
-- debug mode CSRs
-- --------------------------------------------------------------------
when csr_tdata2_c => -- address compare
if RISCV_ISA_Sdtrig then
if (csr.tdata1_dmode = '0') or (debug_ctrl.run = '1') then -- write access from debug-mode only?
csr.tdata2 <= csr.wdata(XLEN-1 downto 1) & '0';
end if;
end if;
-- debug mode control and status register --
if (csr.addr = csr_dcsr_c) and RISCV_ISA_Sdext then
csr.dcsr_ebreakm <= csr.wdata(15);
csr.dcsr_step <= csr.wdata(2);
if RISCV_ISA_U then
csr.dcsr_ebreaku <= csr.wdata(12);
csr.dcsr_prv <= csr.wdata(1) or csr.wdata(0); -- everything /= U will fall back to M
end if;
end if;
-- --------------------------------------------------------------------
-- not implemented (or implemented externally)
-- --------------------------------------------------------------------
when others => NULL;
-- debug mode program counter --
if (csr.addr = csr_dpc_c) and RISCV_ISA_Sdext then
csr.dpc <= csr.wdata(XLEN-1 downto 1) & '0';
if not RISCV_ISA_C then -- RISC-V priv. spec.: DPC[1] is masked when IALIGN = 32
csr.dpc(1) <= '0';
end if;
end if;
end case;
-- debug mode scratch register 0 --
if (csr.addr = csr_dscratch0_c) and RISCV_ISA_Sdext then
csr.dscratch0 <= csr.wdata;
end if;
-- --------------------------------------------------------------------
-- trigger module CSRs
-- --------------------------------------------------------------------
-- match control --
if (csr.addr = csr_tdata1_c) and RISCV_ISA_Sdtrig then
if (csr.tdata1_dmode = '0') or (debug_ctrl.run = '1') then -- write access from debug-mode only?
csr.tdata1_execute <= csr.wdata(2);
csr.tdata1_action <= csr.wdata(12);
end if;
if (debug_ctrl.run = '1') then -- writable from debug-mode only
csr.tdata1_dmode <= csr.wdata(27);
end if;
end if;
-- address compare --
if (csr.addr = csr_tdata2_c) and RISCV_ISA_Sdtrig then
if (csr.tdata1_dmode = '0') or (debug_ctrl.run = '1') then -- write access from debug-mode only?
csr.tdata2 <= csr.wdata(XLEN-1 downto 1) & '0';
end if;
end if;
-- ********************************************************************************
-- Hardware CSR access: TRAP ENTER
@ -1565,7 +1537,7 @@ begin
-- trap instruction --
if (trap_ctrl.cause(6) = '0') then -- exception
csr.mtinst <= exe_engine.ir;
if (exe_engine.is_ci = '1') and RISCV_ISA_C then
if (exe_engine.ci = '1') and RISCV_ISA_C then
csr.mtinst(1) <= '0'; -- RISC-V priv. spec: clear bit 1 if compressed instruction
end if;
else -- interrupt
@ -1679,12 +1651,10 @@ begin
csr_read_access: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
csr.re <= '0';
csr.rdata <= (others => '0');
elsif rising_edge(clk_i) then
csr.re <= csr.re_nxt;
csr.rdata <= (others => '0'); -- default; output all-zero if there is no explicit CSR read operation
if (csr.re = '1') then
if (exe_engine.state = SYSTEM) then -- always read from CSR file in SYSTEM state
case csr.addr is -- address is zero if there is no CSR operation
-- --------------------------------------------------------------------
@ -2084,14 +2054,14 @@ begin
cnt_event(hpmcnt_event_ir_c) <= '1' when (exe_engine.state = 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.is_ci = '1') else '0'; -- executed compressed instruction
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 co-processor 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_WAIT) else '0'; -- load/store unit memory wait cycle
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
@ -2170,7 +2140,7 @@ begin
-- trigger on instruction address match (trigger right BEFORE execution) --
hw_trigger_match <= '1' when (csr.tdata1_execute = '1') and -- trigger enabled to match on instruction address
(hw_trigger_fired = '0') and -- trigger has not fired yet
(csr.tdata2(XLEN-1 downto 1) = exe_engine.next_pc(XLEN-1 downto 1)) -- address match
(csr.tdata2(XLEN-1 downto 1) = exe_engine.pc2(XLEN-1 downto 1)) -- address match
else '0';
-- status flag - set when trigger has fired --

View file

@ -65,6 +65,7 @@ begin
end if;
end process mem_addr_reg;
-- address output --
dbus_req_o.addr <= mar; -- bus address
mar_o <= mar; -- for MTVAL CSR

View file

@ -49,7 +49,7 @@ 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));
-- PMP configuration register bits --
-- configuration register bits --
constant cfg_r_c : natural := 0; -- read permit
constant cfg_w_c : natural := 1; -- write permit
constant cfg_x_c : natural := 2; -- execute permit
@ -59,16 +59,16 @@ architecture neorv32_cpu_pmp_rtl of neorv32_cpu_pmp is
constant cfg_rh_c : natural := 6; -- reserved
constant cfg_l_c : natural := 7; -- locked entry
-- PMP modes --
-- 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)
-- PMP helpers --
-- address LSB according to granularity --
constant pmp_lsb_c : natural := index_size_f(granularity_c); -- min = 2
-- PMP CSRs --
-- 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);
type csr_cfg_rd_t is array (0 to 15) of std_ulogic_vector(7 downto 0);
@ -85,7 +85,7 @@ architecture neorv32_cpu_pmp_rtl of neorv32_cpu_pmp is
signal cfg_rd32 : csr_cfg_rd32_t;
signal addr_rd : csr_addr_rd_t;
-- PMP address extension to 34 bit --
-- 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;
@ -240,7 +240,7 @@ begin
-- extend region addresses to 34-bit --
xaddr(r) <= csr.addr(r) & "00"; -- mask byte offset
-- naturally-aligned address mask --
nap_mode_enable:
if NAP_EN generate
@ -366,7 +366,6 @@ begin
fail_rw(r) <= not region.perm_rw(r) when (region.d_match(r) = '1') else fail_rw(r+1);
end generate;
-- final access check --
access_check: process(rstn_i, clk_i)
begin

View file

@ -128,11 +128,11 @@ architecture neorv32_debug_dm_rtl of neorv32_debug_dm is
-- authentication --
type auth_t is record
busy : std_ulogic; -- authenticator is busy when set
valid : std_ulogic; -- authentication successful
reset : std_ulogic; -- reset authentication (sync, high-active)
re, we : std_ulogic; -- data interface read/write enable
rdata : std_ulogic_vector(31 downto 0); -- read data
busy : std_ulogic; -- authenticator is busy when set
valid : std_ulogic; -- authentication successful
re : std_ulogic; -- data interface read enable
we : std_ulogic; -- data interface write enable
rdata : std_ulogic_vector(31 downto 0); -- read data
end record;
signal auth : auth_t;

View file

@ -20,7 +20,7 @@ use neorv32.neorv32_package.all;
entity neorv32_dmem is
generic (
DMEM_SIZE : natural -- processor-internal instruction memory size in bytes, has to be a power of 2
DMEM_SIZE : natural -- memory size in bytes, has to be a power of 2, min 4
);
port (
clk_i : in std_ulogic; -- global clock line
@ -38,7 +38,7 @@ architecture neorv32_dmem_rtl of neorv32_dmem is
-- local signals --
signal rdata : std_ulogic_vector(31 downto 0);
signal rden : std_ulogic;
signal addr, addr_ff : std_ulogic_vector(index_size_f(DMEM_SIZE/4)-1 downto 0);
signal addr, addr_ff : unsigned(index_size_f(DMEM_SIZE/4)-1 downto 0);
-- [NOTE] The memory (RAM) is built from 4 individual byte-wide memories as some synthesis tools
-- have issues inferring 32-bit memories with individual byte-enable signals.
@ -57,22 +57,22 @@ begin
if rising_edge(clk_i) then
if (bus_req_i.stb = '1') and (bus_req_i.rw = '1') then
if (bus_req_i.ben(0) = '1') then -- byte 0
mem_ram_b0(to_integer(unsigned(addr))) <= bus_req_i.data(7 downto 0);
mem_ram_b0(to_integer(addr)) <= bus_req_i.data(7 downto 0);
end if;
if (bus_req_i.ben(1) = '1') then -- byte 1
mem_ram_b1(to_integer(unsigned(addr))) <= bus_req_i.data(15 downto 8);
mem_ram_b1(to_integer(addr)) <= bus_req_i.data(15 downto 8);
end if;
if (bus_req_i.ben(2) = '1') then -- byte 2
mem_ram_b2(to_integer(unsigned(addr))) <= bus_req_i.data(23 downto 16);
mem_ram_b2(to_integer(addr)) <= bus_req_i.data(23 downto 16);
end if;
if (bus_req_i.ben(3) = '1') then -- byte 3
mem_ram_b3(to_integer(unsigned(addr))) <= bus_req_i.data(31 downto 24);
mem_ram_b3(to_integer(addr)) <= bus_req_i.data(31 downto 24);
end if;
end if;
rdata(7 downto 0) <= mem_ram_b0(to_integer(unsigned(addr)));
rdata(15 downto 8) <= mem_ram_b1(to_integer(unsigned(addr)));
rdata(23 downto 16) <= mem_ram_b2(to_integer(unsigned(addr)));
rdata(31 downto 24) <= mem_ram_b3(to_integer(unsigned(addr)));
rdata(7 downto 0) <= mem_ram_b0(to_integer(addr));
rdata(15 downto 8) <= mem_ram_b1(to_integer(addr));
rdata(23 downto 16) <= mem_ram_b2(to_integer(addr));
rdata(31 downto 24) <= mem_ram_b3(to_integer(addr));
end if;
end process mem_access;
addr_ff <= (others => '0'); -- unused
@ -86,28 +86,28 @@ begin
addr_ff <= addr;
if (bus_req_i.stb = '1') and (bus_req_i.rw = '1') then
if (bus_req_i.ben(0) = '1') then -- byte 0
mem_ram_b0(to_integer(unsigned(addr))) <= bus_req_i.data(7 downto 0);
mem_ram_b0(to_integer(addr)) <= bus_req_i.data(7 downto 0);
end if;
if (bus_req_i.ben(1) = '1') then -- byte 1
mem_ram_b1(to_integer(unsigned(addr))) <= bus_req_i.data(15 downto 8);
mem_ram_b1(to_integer(addr)) <= bus_req_i.data(15 downto 8);
end if;
if (bus_req_i.ben(2) = '1') then -- byte 2
mem_ram_b2(to_integer(unsigned(addr))) <= bus_req_i.data(23 downto 16);
mem_ram_b2(to_integer(addr)) <= bus_req_i.data(23 downto 16);
end if;
if (bus_req_i.ben(3) = '1') then -- byte 3
mem_ram_b3(to_integer(unsigned(addr))) <= bus_req_i.data(31 downto 24);
mem_ram_b3(to_integer(addr)) <= bus_req_i.data(31 downto 24);
end if;
end if;
end if;
end process mem_access;
rdata(7 downto 0) <= mem_ram_b0(to_integer(unsigned(addr_ff)));
rdata(15 downto 8) <= mem_ram_b1(to_integer(unsigned(addr_ff)));
rdata(23 downto 16) <= mem_ram_b2(to_integer(unsigned(addr_ff)));
rdata(31 downto 24) <= mem_ram_b3(to_integer(unsigned(addr_ff)));
rdata(7 downto 0) <= mem_ram_b0(to_integer(addr_ff));
rdata(15 downto 8) <= mem_ram_b1(to_integer(addr_ff));
rdata(23 downto 16) <= mem_ram_b2(to_integer(addr_ff));
rdata(31 downto 24) <= mem_ram_b3(to_integer(addr_ff));
end generate;
-- word aligned access address --
addr <= bus_req_i.addr(index_size_f(DMEM_SIZE/4)+1 downto 2);
addr <= unsigned(bus_req_i.addr(index_size_f(DMEM_SIZE/4)+1 downto 2));
-- Bus Response ---------------------------------------------------------------------------

View file

@ -21,8 +21,8 @@ use neorv32.neorv32_application_image.all; -- generated by the image generator
entity neorv32_imem is
generic (
IMEM_SIZE : natural; -- processor-internal instruction memory size in bytes, has to be a power of 2
IMEM_AS_IROM : boolean -- implement IMEM as pre-initialized read-only memory?
IMEM_SIZE : natural; -- memory size in bytes, has to be a power of 2, min 4
IMEM_INIT : boolean -- implement IMEM as pre-initialized read-only memory?
);
port (
clk_i : in std_ulogic; -- global clock line
@ -37,16 +37,14 @@ architecture neorv32_imem_rtl of neorv32_imem is
-- alternative memory description style --
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);
-- local signals --
signal rdata : std_ulogic_vector(31 downto 0);
signal rden : std_ulogic;
signal addr, addr_ff : std_ulogic_vector(index_size_f(IMEM_SIZE/4)-1 downto 0);
-- application (image) size in bytes --
constant imem_app_size_c : natural := (application_init_image'length)*4;
-- ROM - initialized with executable code --
constant mem_rom_c : mem32_t(0 to IMEM_SIZE/4-1) := mem32_init_f(application_init_image, IMEM_SIZE/4);
signal addr, addr_ff : unsigned(index_size_f(IMEM_SIZE/4)-1 downto 0);
-- [NOTE] The memory (RAM) is built from 4 individual byte-wide memories as some synthesis tools
-- have issues inferring 32-bit memories with individual byte-enable signals.
@ -60,9 +58,9 @@ begin
-- -------------------------------------------------------------------------------------------
assert false report
"[NEORV32] Implementing processor-internal IMEM as " &
cond_sel_string_f(IMEM_AS_IROM, "pre-initialized ROM.", "blank RAM.") severity note;
cond_sel_string_f(IMEM_INIT, "pre-initialized ROM.", "blank RAM.") severity note;
assert not ((IMEM_AS_IROM = true) and (imem_app_size_c > IMEM_SIZE)) report
assert not ((IMEM_INIT = true) and (imem_app_size_c > IMEM_SIZE)) report
"[NEORV32] Application image (" & natural'image(imem_app_size_c) &
" bytes) does not fit into processor-internal IMEM (" &
natural'image(IMEM_SIZE) & " bytes)!" severity error;
@ -71,14 +69,14 @@ begin
-- Implement IMEM as pre-initialized ROM --------------------------------------------------
-- -------------------------------------------------------------------------------------------
imem_rom:
if IMEM_AS_IROM generate
if IMEM_INIT generate
imem_rom_default: -- default memory HDL style
if not alt_style_c generate
mem_access: process(clk_i)
begin
if rising_edge(clk_i) then
rdata <= mem_rom_c(to_integer(unsigned(addr)));
rdata <= mem_rom_c(to_integer(addr));
end if;
end process mem_access;
addr_ff <= (others => '0'); -- unused
@ -92,19 +90,19 @@ begin
addr_ff <= addr;
end if;
end process mem_access;
rdata <= mem_rom_c(to_integer(unsigned(addr_ff)));
rdata <= mem_rom_c(to_integer(addr_ff));
end generate;
end generate;
-- word aligned access address --
addr <= bus_req_i.addr(index_size_f(IMEM_SIZE/4)+1 downto 2);
addr <= unsigned(bus_req_i.addr(index_size_f(IMEM_SIZE/4)+1 downto 2));
-- Implement IMEM as non-initialized RAM --------------------------------------------------
-- -------------------------------------------------------------------------------------------
imem_ram:
if not IMEM_AS_IROM generate
if not IMEM_INIT generate
imem_ram_default: -- default memory HDL style
if not alt_style_c generate
@ -113,22 +111,22 @@ begin
if rising_edge(clk_i) then
if (bus_req_i.stb = '1') and (bus_req_i.rw = '1') then
if (bus_req_i.ben(0) = '1') then -- byte 0
mem_ram_b0(to_integer(unsigned(addr))) <= bus_req_i.data(7 downto 0);
mem_ram_b0(to_integer(addr)) <= bus_req_i.data(7 downto 0);
end if;
if (bus_req_i.ben(1) = '1') then -- byte 1
mem_ram_b1(to_integer(unsigned(addr))) <= bus_req_i.data(15 downto 8);
mem_ram_b1(to_integer(addr)) <= bus_req_i.data(15 downto 8);
end if;
if (bus_req_i.ben(2) = '1') then -- byte 2
mem_ram_b2(to_integer(unsigned(addr))) <= bus_req_i.data(23 downto 16);
mem_ram_b2(to_integer(addr)) <= bus_req_i.data(23 downto 16);
end if;
if (bus_req_i.ben(3) = '1') then -- byte 3
mem_ram_b3(to_integer(unsigned(addr))) <= bus_req_i.data(31 downto 24);
mem_ram_b3(to_integer(addr)) <= bus_req_i.data(31 downto 24);
end if;
end if;
rdata(7 downto 0) <= mem_ram_b0(to_integer(unsigned(addr)));
rdata(15 downto 8) <= mem_ram_b1(to_integer(unsigned(addr)));
rdata(23 downto 16) <= mem_ram_b2(to_integer(unsigned(addr)));
rdata(31 downto 24) <= mem_ram_b3(to_integer(unsigned(addr)));
rdata(7 downto 0) <= mem_ram_b0(to_integer(addr));
rdata(15 downto 8) <= mem_ram_b1(to_integer(addr));
rdata(23 downto 16) <= mem_ram_b2(to_integer(addr));
rdata(31 downto 24) <= mem_ram_b3(to_integer(addr));
end if;
end process mem_access;
addr_ff <= (others => '0'); -- unused
@ -142,24 +140,24 @@ begin
addr_ff <= addr;
if (bus_req_i.stb = '1') and (bus_req_i.rw = '1') then
if (bus_req_i.ben(0) = '1') then -- byte 0
mem_ram_b0(to_integer(unsigned(addr))) <= bus_req_i.data(7 downto 0);
mem_ram_b0(to_integer(addr)) <= bus_req_i.data(7 downto 0);
end if;
if (bus_req_i.ben(1) = '1') then -- byte 1
mem_ram_b1(to_integer(unsigned(addr))) <= bus_req_i.data(15 downto 8);
mem_ram_b1(to_integer(addr)) <= bus_req_i.data(15 downto 8);
end if;
if (bus_req_i.ben(2) = '1') then -- byte 2
mem_ram_b2(to_integer(unsigned(addr))) <= bus_req_i.data(23 downto 16);
mem_ram_b2(to_integer(addr)) <= bus_req_i.data(23 downto 16);
end if;
if (bus_req_i.ben(3) = '1') then -- byte 3
mem_ram_b3(to_integer(unsigned(addr))) <= bus_req_i.data(31 downto 24);
mem_ram_b3(to_integer(addr)) <= bus_req_i.data(31 downto 24);
end if;
end if;
end if;
end process mem_access;
rdata(7 downto 0) <= mem_ram_b0(to_integer(unsigned(addr_ff)));
rdata(15 downto 8) <= mem_ram_b1(to_integer(unsigned(addr_ff)));
rdata(23 downto 16) <= mem_ram_b2(to_integer(unsigned(addr_ff)));
rdata(31 downto 24) <= mem_ram_b3(to_integer(unsigned(addr_ff)));
rdata(7 downto 0) <= mem_ram_b0(to_integer(addr_ff));
rdata(15 downto 8) <= mem_ram_b1(to_integer(addr_ff));
rdata(23 downto 16) <= mem_ram_b2(to_integer(addr_ff));
rdata(31 downto 24) <= mem_ram_b3(to_integer(addr_ff));
end generate;
end generate;
@ -174,7 +172,7 @@ begin
bus_rsp_o.ack <= '0';
elsif rising_edge(clk_i) then
rden <= bus_req_i.stb and (not bus_req_i.rw);
if (IMEM_AS_IROM = true) then
if (IMEM_INIT = true) then
bus_rsp_o.ack <= bus_req_i.stb and (not bus_req_i.rw); -- read-only!
else
bus_rsp_o.ack <= bus_req_i.stb;

View file

@ -29,7 +29,7 @@ package neorv32_package is
-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100600"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100606"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width
@ -181,6 +181,43 @@ package neorv32_package is
ack : std_ulogic;
end record;
-- External Bus Interface (XBUS / Wishbone) -----------------------------------------------
-- -------------------------------------------------------------------------------------------
-- xbus request --
type xbus_req_t is record
addr : std_ulogic_vector(31 downto 0); -- access address
data : std_ulogic_vector(31 downto 0); -- write data
tag : std_ulogic_vector(2 downto 0); -- access tag
we : std_ulogic; -- read/write
sel : std_ulogic_vector(3 downto 0); -- byte enable
stb : std_ulogic; -- strobe
cyc : std_ulogic; -- valid cycle
end record;
-- xbus response --
type xbus_rsp_t is record
data : std_ulogic_vector(31 downto 0); -- read data, valid if ack=1
ack : std_ulogic; -- access acknowledge
err : std_ulogic; -- access error
end record;
-- endpoint (response) termination --
constant xbus_rsp_terminate_c : xbus_rsp_t := (
data => (others => '0'),
ack => '0',
err => '0'
);
-- External Stream-Link Interface (SLINK / AXI4-Stream) -----------------------------------
-- -------------------------------------------------------------------------------------------
type slink_t is record
data : std_ulogic_vector(31 downto 0); -- data
addr : std_ulogic_vector(3 downto 0); -- source/destination ID
valid : std_ulogic; -- source valid
last : std_ulogic; -- last element of packet
ready : std_ulogic; -- sink ready
end record;
-- **********************************************************************************************************
-- RISC-V ISA Definitions
-- **********************************************************************************************************
@ -667,12 +704,15 @@ package neorv32_package is
component neorv32_top
generic (
-- General --
-- Processor Clocking --
CLOCK_FREQUENCY : natural := 0;
CLOCK_GATING_EN : boolean := false;
-- Identification --
HART_ID : std_ulogic_vector(31 downto 0) := x"00000000";
JEDEC_ID : std_ulogic_vector(10 downto 0) := "00000000000";
INT_BOOTLOADER_EN : boolean := false;
-- Boot Configuration --
BOOT_MODE_SELECT : natural range 0 to 2 := 0;
BOOT_ADDR_CUSTOM : std_ulogic_vector(31 downto 0) := x"00000000";
-- On-Chip Debugger (OCD) --
OCD_EN : boolean := false;
OCD_AUTHENTICATION : boolean := false;
@ -1116,40 +1156,3 @@ package body neorv32_package is
end function print_version_f;
end neorv32_package;
-- **********************************************************************************************************
-- Additional Packages
-- **********************************************************************************************************
-- Prototype Definition: bootloader_init_image --------------------------------------------
-- -------------------------------------------------------------------------------------------
-- > memory content in 'neorv32_bootloader_image.vhd', auto-generated by 'image_gen'
-- > used by 'neorv32_boot_rom.vhd'
-- > enables body-only recompile in case of firmware change (NEORV32 PR #338)
library ieee;
use ieee.std_logic_1164.all;
library neorv32;
use neorv32.neorv32_package.all;
package neorv32_bootloader_image is
constant bootloader_init_image : mem32_t;
end neorv32_bootloader_image;
-- Prototype Definition: neorv32_application_image ----------------------------------------
-- -------------------------------------------------------------------------------------------
-- > memory content in 'neorv32_application_image.vhd', auto-generated by 'image_gen'
-- > used by 'mem/neorv32_imem.*.vhd'
-- > enables body-only recompile in case of firmware change (NEORV32 PR #338)
library ieee;
use ieee.std_logic_1164.all;
library neorv32;
use neorv32.neorv32_package.all;
package neorv32_application_image is
constant application_init_image : mem32_t;
end neorv32_application_image;

View file

@ -19,8 +19,10 @@ entity neorv32_sysinfo is
generic (
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
MEM_INT_IMEM_ROM : boolean; -- implement processor-internal instruction memory as pre-initialized ROM
MEM_INT_IMEM_SIZE : natural; -- size of processor-internal instruction memory in bytes
MEM_INT_DMEM_EN : boolean; -- implement processor-internal data memory
MEM_INT_DMEM_SIZE : natural; -- size of processor-internal data memory in bytes
@ -75,6 +77,7 @@ architecture neorv32_sysinfo_rtl of neorv32_sysinfo is
constant xcache_en_c : boolean := XBUS_EN and XBUS_CACHE_EN;
constant xip_cache_en_c : boolean := XIP_EN and XIP_CACHE_EN;
constant ocd_auth_en_c : boolean := OCD_EN and OCD_AUTHENTICATION;
constant int_imem_rom_c : boolean := int_imem_en_c and MEM_INT_IMEM_ROM;
-- system information memory --
type sysinfo_t is array (0 to 3) of std_ulogic_vector(31 downto 0);
@ -104,7 +107,7 @@ begin
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)(31 downto 24) <= (others => '0'); -- reserved
sysinfo(1)(31 downto 24) <= std_ulogic_vector(to_unsigned(BOOT_MODE_SELECT, 8)); -- boot configuration
-- SYSINFO(2): SoC Configuration --
sysinfo(2)(0) <= '1' when INT_BOOTLOADER_EN else '0'; -- processor-internal bootloader implemented?
@ -119,7 +122,7 @@ begin
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) <= '0'; -- reserved
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)(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?

View file

@ -21,12 +21,17 @@ use neorv32.neorv32_package.all;
entity neorv32_top is
generic (
-- General --
-- 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
-- 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
INT_BOOTLOADER_EN : boolean := false; -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- Boot Configuration --
BOOT_MODE_SELECT : natural range 0 to 2 := 0; -- boot configuration select (default = 0 = bootloader)
BOOT_ADDR_CUSTOM : std_ulogic_vector(31 downto 0) := x"00000000"; -- custom CPU boot address (if boot_config = 1)
-- On-Chip Debugger (OCD) --
OCD_EN : boolean := false; -- implement on-chip debugger
@ -244,16 +249,28 @@ end neorv32_top;
architecture neorv32_top_rtl of neorv32_top is
-- ----------------------------------------------------------
-- Boot Configuration (BOOT_MODE_SELECT)
-- ----------------------------------------------------------
-- 0: Internal bootloader ROM
-- 1: Custom (use BOOT_ADDR_CUSTOM)
-- 2: Internal IMEM initialized with application image
-- ----------------------------------------------------------
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 = 1), BOOT_ADDR_CUSTOM,
cond_sel_suv_f(boolean(BOOT_MODE_SELECT = 2), mem_imem_base_c, x"00000000")));
-- auto-configuration --
constant cpu_boot_addr_c : std_ulogic_vector(31 downto 0) := cond_sel_suv_f(INT_BOOTLOADER_EN, mem_boot_base_c, mem_imem_base_c);
constant imem_as_rom_c : boolean := not INT_BOOTLOADER_EN;
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);
constant cpu_smpmp_c : boolean := boolean(PMP_NUM_REGIONS > 0);
constant io_sysinfo_en_c : boolean := not IO_DISABLE_SYSINFO;
-- convert JEDEC ID to mvendorid CSR --
-- convert JEDEC ID to MVENDORID CSR --
constant vendorid_c : std_ulogic_vector(31 downto 0) := x"00000" & "0" & JEDEC_ID;
-- make sure physical memory sizes are a power of two --
@ -282,7 +299,7 @@ architecture neorv32_top_rtl of neorv32_top is
signal dmi_rsp : dmi_rsp_t;
-- debug core interface (DCI) --
signal dci_ndmrstn, dci_halt_req : std_ulogic;
signal dci_ndmrstn, dci_haltreq : std_ulogic;
-- 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;
@ -330,9 +347,9 @@ begin
-- show SoC configuration --
assert false report
"[NEORV32] Processor Configuration: CPU " & -- cpu core is always enabled
cond_sel_string_f(MEM_INT_IMEM_EN, "IMEM ", "") &
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(INT_BOOTLOADER_EN, "BOOTROM ", "") &
cond_sel_string_f(bootrom_en_c, "BOOTROM ", "") &
cond_sel_string_f(ICACHE_EN, "I-CACHE ", "") &
cond_sel_string_f(DCACHE_EN, "D-CACHE ", "") &
cond_sel_string_f(XBUS_EN, "XBUS ", "") &
@ -358,7 +375,7 @@ begin
cond_sel_string_f(IO_SLINK_EN, "SLINK ", "") &
cond_sel_string_f(IO_CRC_EN, "CRC ", "") &
cond_sel_string_f(io_sysinfo_en_c, "SYSINFO ", "") &
cond_sel_string_f(OCD_EN, cond_sel_string_f(OCD_AUTHENTICATION, "OCD-AUTH ", "OCD "), "") &
cond_sel_string_f(OCD_EN, cond_sel_string_f(OCD_AUTHENTICATION, "OCD-AUTH ", "OCD "), "") &
""
severity note;
@ -378,6 +395,15 @@ begin
assert not (CLOCK_FREQUENCY = 0) report
"[NEORV32] CLOCK_FREQUENCY must be configured according to the frequency of clk_i port!" severity warning;
-- Boot configuration notifier --
assert not (BOOT_MODE_SELECT = 0) report "[NEORV32] BOOT_MODE_SELECT = 0: booting via bootloader" severity note;
assert not (BOOT_MODE_SELECT = 1) report "[NEORV32] BOOT_MODE_SELECT = 1: booting from custom address" severity note;
assert not (BOOT_MODE_SELECT = 2) report "[NEORV32] BOOT_MODE_SELECT = 2: booting IMEM image" severity note;
-- Boot configuration: boot from initialized IMEM requires the IMEM to be enabled --
assert not ((BOOT_MODE_SELECT = 2) and (MEM_INT_IMEM_EN = false)) report
"[NEORV32] BOOT_MODE_SELECT = 2 (boot IMEM image) requires the internal instruction memory (IMEM) to be enabled!" severity error;
end generate; -- /sanity_checks
@ -507,7 +533,7 @@ begin
mei_i => mext_irq_i,
mti_i => mtime_irq,
firq_i => cpu_firq,
dbi_i => dci_halt_req,
dbi_i => dci_haltreq,
-- instruction bus interface --
ibus_req_o => cpu_i_req,
ibus_rsp_i => cpu_i_rsp,
@ -717,7 +743,7 @@ begin
C_TMO_EN => false, -- no timeout for XIP accesses
C_PRIV => false,
-- port D: BOOT ROM --
D_ENABLE => INT_BOOTLOADER_EN,
D_ENABLE => bootrom_en_c,
D_BASE => mem_boot_base_c,
D_SIZE => mem_boot_size_c,
D_TMO_EN => true,
@ -768,8 +794,8 @@ begin
if MEM_INT_IMEM_EN generate
neorv32_int_imem_inst: entity neorv32.neorv32_imem
generic map (
IMEM_SIZE => imem_size_c,
IMEM_AS_IROM => imem_as_rom_c
IMEM_SIZE => imem_size_c,
IMEM_INIT => imem_as_rom_c
)
port map (
clk_i => clk_i,
@ -810,7 +836,7 @@ begin
-- Processor-Internal Bootloader ROM (BOOTROM) --------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_boot_rom_inst_true:
if INT_BOOTLOADER_EN generate
if bootrom_en_c generate
neorv32_boot_rom_inst: entity neorv32.neorv32_boot_rom
port map (
clk_i => clk_i,
@ -821,7 +847,7 @@ begin
end generate;
neorv32_boot_rom_inst_false:
if not INT_BOOTLOADER_EN generate
if not bootrom_en_c generate
boot_rsp <= rsp_terminate_c;
end generate;
@ -1191,7 +1217,8 @@ begin
if IO_UART0_EN generate
neorv32_uart0_inst: entity neorv32.neorv32_uart
generic map (
SIM_LOG_FILE => "neorv32.uart0.sim_mode.text.out",
SIM_MODE_EN => true,
SIM_LOG_FILE => "neorv32.uart0_sim_mode.out",
UART_RX_FIFO => IO_UART0_RX_FIFO,
UART_TX_FIFO => IO_UART0_TX_FIFO
)
@ -1228,7 +1255,8 @@ begin
if IO_UART1_EN generate
neorv32_uart1_inst: entity neorv32.neorv32_uart
generic map (
SIM_LOG_FILE => "neorv32.uart1.sim_mode.text.out",
SIM_MODE_EN => true,
SIM_LOG_FILE => "neorv32.uart1_sim_mode.out",
UART_RX_FIFO => IO_UART1_RX_FIFO,
UART_TX_FIFO => IO_UART1_TX_FIFO
)
@ -1551,8 +1579,10 @@ begin
generic map (
CLOCK_FREQUENCY => CLOCK_FREQUENCY,
CLOCK_GATING_EN => CLOCK_GATING_EN,
INT_BOOTLOADER_EN => INT_BOOTLOADER_EN,
BOOT_MODE_SELECT => BOOT_MODE_SELECT,
INT_BOOTLOADER_EN => bootrom_en_c,
MEM_INT_IMEM_EN => MEM_INT_IMEM_EN,
MEM_INT_IMEM_ROM => imem_as_rom_c,
MEM_INT_IMEM_SIZE => imem_size_c,
MEM_INT_DMEM_EN => MEM_INT_DMEM_EN,
MEM_INT_DMEM_SIZE => dmem_size_c,
@ -1649,7 +1679,7 @@ begin
bus_req_i => iodev_req(IODEV_OCD),
bus_rsp_o => iodev_rsp(IODEV_OCD),
cpu_ndmrstn_o => dci_ndmrstn,
cpu_halt_req_o => dci_halt_req
cpu_halt_req_o => dci_haltreq
);
end generate;
@ -1659,7 +1689,7 @@ begin
iodev_rsp(IODEV_OCD) <= rsp_terminate_c;
jtag_tdo_o <= jtag_tdi_i; -- JTAG pass-through
dci_ndmrstn <= '1';
dci_halt_req <= '0';
dci_haltreq <= '0';
end generate;

View file

@ -448,16 +448,21 @@ begin
-- latch with global reset and individual enable --
latch(i) <= '0' when (en_i = '0') else latch(i) when (sreg(i) = '0') else inv_out(i);
-- inverter with "propagation delay" --
inverter_sim:
if SIM_MODE generate
inv_out(i) <= not inv_in(i) when rising_edge(clk_i); -- for SIMULATION ONLY
end generate;
-- inverter for actual synthesis --
inverter_phy:
if not SIM_MODE generate
inv_out(i) <= not inv_in(i);
inv_out(i) <= not inv_in(i); -- this is one part of the ring oscillator's physical propagation delay
end generate;
-- inverter with "propagation delay (as a simple FF)" --
inverter_sim:
if SIM_MODE generate -- for SIMULATION ONLY
inverter_sim_ff: process(clk_i) -- this will NOT generate true random numbers
begin
if rising_edge(clk_i) then
inv_out(i) <= not inv_in(i);
end if;
end process inverter_sim_ff;
end generate;
end generate;

View file

@ -23,7 +23,8 @@ use neorv32.neorv32_package.all;
entity neorv32_uart is
generic (
SIM_LOG_FILE : string; -- name of SIM mode log file
SIM_MODE_EN : boolean; -- enable simulation-mode option
SIM_LOG_FILE : string; -- name of SIM mode log file
UART_RX_FIFO : natural range 1 to 2**15; -- RX fifo depth, has to be a power of two, min 1
UART_TX_FIFO : natural range 1 to 2**15 -- TX fifo depth, has to be a power of two, min 1
);
@ -45,6 +46,9 @@ end neorv32_uart;
architecture neorv32_uart_rtl of neorv32_uart is
-- simulation mode available? --
constant sim_mode_en_c : boolean := SIM_MODE_EN and is_simulation_c;
-- control register bits --
constant ctrl_en_c : natural := 0; -- r/w: UART enable
constant ctrl_sim_en_c : natural := 1; -- r/w: simulation-mode enable
@ -169,7 +173,7 @@ begin
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.sim_mode <= bus_req_i.data(ctrl_sim_en_c);
ctrl.sim_mode <= bus_req_i.data(ctrl_sim_en_c) and bool_to_ulogic_f(sim_mode_en_c);
ctrl.hwfc_en <= bus_req_i.data(ctrl_hwfc_en_c);
ctrl.prsc <= bus_req_i.data(ctrl_prsc2_c downto ctrl_prsc0_c);
ctrl.baud <= bus_req_i.data(ctrl_baud9_c downto ctrl_baud0_c);
@ -185,7 +189,7 @@ begin
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_sim_en_c) <= ctrl.sim_mode;
bus_rsp_o.data(ctrl_sim_en_c) <= ctrl.sim_mode and bool_to_ulogic_f(sim_mode_en_c);
bus_rsp_o.data(ctrl_hwfc_en_c) <= ctrl.hwfc_en;
bus_rsp_o.data(ctrl_prsc2_c downto ctrl_prsc0_c) <= ctrl.prsc;
bus_rsp_o.data(ctrl_baud9_c downto ctrl_baud0_c) <= ctrl.baud;
@ -473,7 +477,7 @@ begin
-- pragma translate_off
-- RTL_SYNTHESIS OFF
simulation_transmitter:
if is_simulation_c generate -- for simulation only!
if sim_mode_en_c generate -- for simulation only!
sim_tx: process(clk_i)
file file_out : text open write_mode is SIM_LOG_FILE;
variable char_v : integer;

View file

@ -126,7 +126,7 @@ begin
xbus_tag_o <= bus_req.src & '0' & bus_req.priv; -- instr/data, secure, privileged/unprivileged
-- response gating --
bus_rsp.data <= xbus_dat_i when (pending = '1') and (bus_rw = '0') else (others => '0'); -- no read-back if READ operation
bus_rsp.data <= xbus_dat_i when (pending = '1') and (bus_rw = '0') else (others => '0'); -- no read-back if WRITE operation
bus_rsp.ack <= xbus_ack_i when (pending = '1') else '0';
bus_rsp.err <= (xbus_err_i or timeout) when (pending = '1') else '0';

View file

@ -14,5 +14,3 @@ NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_cpu_alu.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_cpu_lsu.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_cpu_pmp.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_cpu.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_bootloader_image.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_application_image.vhd

View file

@ -19,8 +19,10 @@ NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_cpu.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_bus.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_cache.vhd
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
@ -45,5 +47,3 @@ NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_debug_dtm.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_debug_auth.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_debug_dm.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_top.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_application_image.vhd
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_bootloader_image.vhd

View file

@ -16,7 +16,7 @@ library neorv32;
entity neorv32_ProcessorTop_Minimal is
generic (
-- General --
-- Clocking --
CLOCK_FREQUENCY : natural := 0; -- clock frequency of clk_i in Hz
-- Internal Instruction memory --
MEM_INT_IMEM_EN : boolean := true; -- implement processor-internal instruction memory
@ -47,9 +47,10 @@ begin
-- -------------------------------------------------------------------------------------------
neorv32_inst: entity neorv32.neorv32_top
generic map (
-- General --
-- Clocking --
CLOCK_FREQUENCY => CLOCK_FREQUENCY, -- clock frequency of clk_i in Hz
INT_BOOTLOADER_EN => false, -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- Boot Configuration --
BOOT_MODE_SELECT => 2, -- boot from pre-initialized interal IMEM
-- Internal Instruction memory --
MEM_INT_IMEM_EN => MEM_INT_IMEM_EN, -- implement processor-internal instruction memory
MEM_INT_IMEM_SIZE => MEM_INT_IMEM_SIZE, -- size of processor-internal instruction memory in bytes

View file

@ -16,7 +16,7 @@ library neorv32;
entity neorv32_ProcessorTop_MinimalBoot is
generic (
-- General --
-- Clocking --
CLOCK_FREQUENCY : natural := 0; -- clock frequency of clk_i in Hz
-- Internal Instruction memory --
MEM_INT_IMEM_EN : boolean := true; -- implement processor-internal instruction memory
@ -54,9 +54,10 @@ begin
-- -------------------------------------------------------------------------------------------
neorv32_inst: entity neorv32.neorv32_top
generic map (
-- General --
-- Clocking --
CLOCK_FREQUENCY => CLOCK_FREQUENCY, -- clock frequency of clk_i in Hz
INT_BOOTLOADER_EN => true, -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- Boot Configuration --
BOOT_MODE_SELECT => 0, -- boot via internal bootloader
-- Internal Instruction memory --
MEM_INT_IMEM_EN => MEM_INT_IMEM_EN, -- implement processor-internal instruction memory
MEM_INT_IMEM_SIZE => MEM_INT_IMEM_SIZE, -- size of processor-internal instruction memory in bytes

View file

@ -16,7 +16,7 @@ library neorv32;
entity neorv32_ProcessorTop_UP5KDemo is
generic (
-- General --
-- Clocking --
CLOCK_FREQUENCY : natural := 0; -- clock frequency of clk_i in Hz
-- Internal Instruction memory --
MEM_INT_IMEM_EN : boolean := true; -- implement processor-internal instruction memory
@ -77,9 +77,10 @@ begin
-- -------------------------------------------------------------------------------------------
neorv32_inst: entity neorv32.neorv32_top
generic map (
-- General --
-- Clocking --
CLOCK_FREQUENCY => CLOCK_FREQUENCY, -- clock frequency of clk_i in Hz
INT_BOOTLOADER_EN => true, -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- Boot Configuration --
BOOT_MODE_SELECT => 0, -- boot via internal bootloader
-- RISC-V CPU Extensions --
RISCV_ISA_M => true, -- implement mul/div extension?
RISCV_ISA_U => true, -- implement user mode extension?

View file

@ -11,6 +11,7 @@
# -- SPDX-License-Identifier: BSD-3-Clause --
# -- ================================================================================ --
# **************************************************************
# Global configuration
# **************************************************************
@ -20,6 +21,7 @@ set ip_logo docs/figures/neorv32_logo_riscv_small.png
set outputdir neorv32_vivado_ip_work
set cur_dir [file normalize .]
# **************************************************************
# Create empty (!) output/working directory
# **************************************************************
@ -30,11 +32,13 @@ if {[llength $files] != 0} {
file delete -force {*}[glob -directory $outputdir *];
}
# **************************************************************
# Create Vivado project
# **************************************************************
create_project "neorv32-ip" $outputdir
#set_property target_language VHDL [current_project]
set_property INCREMENTAL false [get_filesets sim_1]
# **************************************************************
# Import HDL source files
@ -55,6 +59,7 @@ set_property top $ip_top [current_fileset]
update_compile_order -fileset sources_1
# **************************************************************
# Package as IP block
# **************************************************************
@ -64,6 +69,10 @@ set_property vendor_display_name "Stephan Nolting" [ipx::current_core]
set_property company_url https://github.com/stnolting/neorv32 [ipx::current_core]
set_property description "The NEORV32 RISC-V Processor" [ipx::current_core]
# **************************************************************
# Setup configuration GUI
# **************************************************************
proc setup_ip_gui {} {
proc set_param_properties {name {display_name ""} {tooltip ""} {enablement_expr ""} {value_expr ""}} {
set param_spec [ipgui::get_guiparamspec -name $name -component [ipx::current_core]]
@ -105,28 +114,33 @@ proc setup_ip_gui {} {
}
}
# **************************************************************
# Interfaces: Configuration Dependencies
# **************************************************************
set_property enablement_dependency {$AXI4_STREAM_EN} [ipx::get_bus_interfaces s0_axis -of_objects [ipx::current_core]]
set_property enablement_dependency {$AXI4_STREAM_EN} [ipx::get_bus_interfaces s1_axis -of_objects [ipx::current_core]]
set_property enablement_dependency {$XBUS_EN} [ipx::get_bus_interfaces m_axi -of_objects [ipx::current_core]]
set_property enablement_dependency {$OCD_EN} [ipx::get_ports jtag_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$XIP_EN} [ipx::get_ports xip_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_GPIO_EN} [ipx::get_ports gpio_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_UART0_EN} [ipx::get_ports uart0_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_UART1_EN} [ipx::get_ports uart1_* -of_objects [ipx::current_core]]
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_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_SLINK_EN} [ipx::get_bus_interfaces s0_axis -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_SLINK_EN} [ipx::get_bus_interfaces s1_axis -of_objects [ipx::current_core]]
set_property enablement_dependency {$XBUS_EN} [ipx::get_bus_interfaces m_axi -of_objects [ipx::current_core]]
set_property enablement_dependency {$OCD_EN} [ipx::get_ports jtag_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$XIP_EN} [ipx::get_ports xip_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_GPIO_EN} [ipx::get_ports gpio_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_UART0_EN} [ipx::get_ports uart0_* -of_objects [ipx::current_core]]
set_property enablement_dependency {$IO_UART1_EN} [ipx::get_ports uart1_* -of_objects [ipx::current_core]]
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_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]]
# **************************************************************
# Configuration pages
# **************************************************************
# Remove default page
set page [ipgui::get_pagespec -name "Page 0" -component [ipx::current_core]]
if {$page ne ""} {
@ -139,8 +153,23 @@ proc setup_ip_gui {} {
set page [add_page {General}]
# { param_name {display_name} {tooltip} {enablement_expr} {value_expr} }
set group [add_group $page {Clocking}]
add_params $page {
{ CLOCK_FREQUENCY {Clock Frequency (Hz)} {Frequency of the clk signal in Hz} }
{ CLOCK_FREQUENCY {Clock Frequency (Hz)} {Frequency of the clk input signal in Hz} }
}
set group [add_group $page {Boot Configuration}]
add_params $group {
{ BOOT_MODE_SELECT {Boot mode select} {Processor boot configuration} }
{ BOOT_ADDR_CUSTOM {Custom boot address} {Available if BOOT_MODE_SELECT = 1; has to be 4-byte aligned} {$BOOT_MODE_SELECT == 1}}
}
set_property widget {comboBox} [ipgui::get_guiparamspec -name "BOOT_MODE_SELECT" -component [ipx::current_core] ]
set_property value_validation_type pairs [ipx::get_user_parameters BOOT_MODE_SELECT -of_objects [ipx::current_core]]
set_property value_validation_pairs {{Internal bootloader} 0 {Custom address} 1 {Internal IMEM image} 2} [ipx::get_user_parameters BOOT_MODE_SELECT -of_objects [ipx::current_core]]
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} }
}
@ -159,6 +188,7 @@ proc setup_ip_gui {} {
set sub_group [add_group $group {XBUS Cache}]
add_params $sub_group {
{ XBUS_REGSTAGE_EN {Add register stages} {Relaxes timing, but will increase latency} {$XBUS_EN} }
{ XBUS_CACHE_EN {Enable XBUS Cache} {} {$XBUS_EN} {$XBUS_EN ? $XBUS_CACHE_EN : false}}
{ XBUS_CACHE_NUM_BLOCKS {Number of Blocks} {} {$XBUS_CACHE_EN} }
{ XBUS_CACHE_BLOCK_SIZE {Block Size} {In bytes (use a power of two)} {$XBUS_CACHE_EN} }
@ -166,9 +196,9 @@ proc setup_ip_gui {} {
set group [add_group $page {Stream Link Interface (SLINK / AXI4-Stream Source & Sink)}]
add_params $group {
{ AXI4_STREAM_EN {Enable SLINK} {} }
{ IO_SLINK_RX_FIFO {RX FIFO Depth} {Number of entries (use a power of two)} {$AXI4_STREAM_EN} }
{ IO_SLINK_TX_FIFO {TX FIFO Depth} {Number of entries (use a power of two)} {$AXI4_STREAM_EN} }
{ IO_SLINK_EN {Enable SLINK} {} }
{ IO_SLINK_RX_FIFO {RX FIFO Depth} {Number of entries (use a power of two)} {$IO_SLINK_EN} }
{ IO_SLINK_TX_FIFO {TX FIFO Depth} {Number of entries (use a power of two)} {$IO_SLINK_EN} }
}
@ -214,7 +244,7 @@ proc setup_ip_gui {} {
}
set_property value_validation_range_minimum 4 [ipx::get_user_parameters PMP_MIN_GRANULARITY -of_objects [ipx::current_core]]
set group [add_group $page {Architecture Tuning Options}]
set group [add_group $page {Tuning Options}]
add_params $group {
{ FAST_MUL_EN {DSP-Based Multiplier} }
{ FAST_SHIFT_EN {Barrel Shifter} }
@ -261,11 +291,6 @@ proc setup_ip_gui {} {
{ XIP_CACHE_BLOCK_SIZE {Cache Block Size} {In bytes (use a power of two)} {$XIP_CACHE_EN} }
}
set group [add_group $page {Internal Bootloader}]
add_params $group {
{ INT_BOOTLOADER_EN {Enable Bootloader} {Start interactive bootloader console after reset} }
}
# **************************************************************
# GUI Page: Peripherals
@ -376,6 +401,7 @@ proc setup_ip_gui {} {
setup_ip_gui
# **************************************************************
# Configuration GUI: IP logo
# **************************************************************
@ -392,6 +418,7 @@ set_property type LOGO [ipx::get_files ../../$neorv32_home/$ip_logo -of_objects
ipx::add_file_group -type product_guide {} [ipx::current_core]
ipx::add_file {https://stnolting.github.io/neorv32/} [ipx::get_file_groups xilinx_productguide -of_objects [ipx::current_core]]
# **************************************************************
# Finalize and add to IP repository
# **************************************************************

View file

@ -25,13 +25,14 @@ entity neorv32_vivado_ip is
-- ------------------------------------------------------------
-- Configuration Generics
-- ------------------------------------------------------------
-- AXI-Stream Interfaces --
AXI4_STREAM_EN : boolean := false;
-- General --
-- 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";
INT_BOOTLOADER_EN : boolean := false;
-- Boot Configuration --
BOOT_MODE_SELECT : natural range 0 to 2 := 0;
BOOT_ADDR_CUSTOM : std_ulogic_vector(31 downto 0) := x"00000000";
-- On-Chip Debugger (OCD) --
OCD_EN : boolean := false;
OCD_AUTHENTICATION : boolean := false;
@ -87,6 +88,7 @@ entity neorv32_vivado_ip is
-- External Bus Interface --
XBUS_EN : boolean := true;
XBUS_TIMEOUT : natural range 8 to 65536 := 64;
XBUS_REGSTAGE_EN : boolean := false;
XBUS_CACHE_EN : boolean := false;
XBUS_CACHE_NUM_BLOCKS : natural range 1 to 256 := 8;
XBUS_CACHE_BLOCK_SIZE : natural range 1 to 2**16 := 256;
@ -129,6 +131,7 @@ entity neorv32_vivado_ip is
IO_GPTMR_EN : boolean := false;
IO_ONEWIRE_EN : boolean := false;
IO_DMA_EN : boolean := false;
IO_SLINK_EN : boolean := false;
IO_SLINK_RX_FIFO : natural range 1 to 2**15 := 1;
IO_SLINK_TX_FIFO : natural range 1 to 2**15 := 1;
IO_CRC_EN : boolean := false
@ -170,7 +173,7 @@ entity neorv32_vivado_ip is
m_axi_bvalid : in std_logic := '0';
m_axi_bready : out std_logic;
-- ------------------------------------------------------------
-- AXI4-Stream Interfaces (available if AXI4_STREAM_EN = true)
-- AXI4-Stream Interfaces (available if IO_SLINK_EN = true)
-- ------------------------------------------------------------
-- Source --
-- s0_axis_aclk : in std_logic := '0'; -- just to satisfy Vivado, but not actually used
@ -260,54 +263,44 @@ architecture neorv32_vivado_ip_rtl of neorv32_vivado_ip is
-- AXI4-Lite bridge --
component xbus2axi4lite_bridge
port (
-- ------------------------------------------------------------
-- Global Control
-- ------------------------------------------------------------
clk : in std_logic;
resetn : in std_logic; -- low-active
-- ------------------------------------------------------------
-- XBUS Device Interface
-- ------------------------------------------------------------
xbus_adr_i : in std_ulogic_vector(31 downto 0); -- address
xbus_dat_i : in std_ulogic_vector(31 downto 0); -- write data
xbus_tag_i : in std_ulogic_vector(2 downto 0); -- access tag
xbus_we_i : in std_ulogic; -- read/write
xbus_sel_i : in std_ulogic_vector(3 downto 0); -- byte enable
xbus_stb_i : in std_ulogic; -- strobe
xbus_cyc_i : in std_ulogic; -- valid cycle
xbus_ack_o : out std_ulogic; -- transfer acknowledge
xbus_err_o : out std_ulogic; -- transfer error
xbus_dat_o : out std_ulogic_vector(31 downto 0); -- read data
-- ------------------------------------------------------------
-- AXI4-Lite Host Interface
-- ------------------------------------------------------------
-- Clock and Reset --
-- m_axi_aclk : in std_logic; -- just to satisfy Vivado, but not actually used
-- m_axi_aresetn : in std_logic; -- just to satisfy Vivado, but not actually used
-- Write Address Channel --
m_axi_awaddr : out std_logic_vector(31 downto 0);
m_axi_awprot : out std_logic_vector(2 downto 0);
m_axi_awvalid : out std_logic;
m_axi_awready : in std_logic;
-- Write Data Channel --
m_axi_wdata : out std_logic_vector(31 downto 0);
m_axi_wstrb : out std_logic_vector(3 downto 0);
m_axi_wvalid : out std_logic;
m_axi_wready : in std_logic;
-- Read Address Channel --
m_axi_araddr : out std_logic_vector(31 downto 0);
m_axi_arprot : out std_logic_vector(2 downto 0);
m_axi_arvalid : out std_logic;
m_axi_arready : in std_logic;
-- Read Data Channel --
m_axi_rdata : in std_logic_vector(31 downto 0);
m_axi_rresp : in std_logic_vector(1 downto 0);
m_axi_rvalid : in std_logic;
m_axi_rready : out std_logic;
-- Write Response Channel --
m_axi_bresp : in std_logic_vector(1 downto 0);
m_axi_bvalid : in std_logic;
m_axi_bready : out std_logic
-- Global control
clk : in std_logic;
resetn : in std_logic;
-- XBUS device interface --
xbus_adr_i : in std_ulogic_vector(31 downto 0);
xbus_dat_i : in std_ulogic_vector(31 downto 0);
xbus_tag_i : in std_ulogic_vector(2 downto 0);
xbus_we_i : in std_ulogic;
xbus_sel_i : in std_ulogic_vector(3 downto 0);
xbus_stb_i : in std_ulogic;
xbus_cyc_i : in std_ulogic;
xbus_ack_o : out std_ulogic;
xbus_err_o : out std_ulogic;
xbus_dat_o : out std_ulogic_vector(31 downto 0);
-- AXI4-Lite host write address channel --
m_axi_awaddr : out std_logic_vector(31 downto 0);
m_axi_awprot : out std_logic_vector(2 downto 0);
m_axi_awvalid : out std_logic;
m_axi_awready : in std_logic;
-- AXI4-Lite host write data channel --
m_axi_wdata : out std_logic_vector(31 downto 0);
m_axi_wstrb : out std_logic_vector(3 downto 0);
m_axi_wvalid : out std_logic;
m_axi_wready : in std_logic;
-- AXI4-Lite host read address channel --
m_axi_araddr : out std_logic_vector(31 downto 0);
m_axi_arprot : out std_logic_vector(2 downto 0);
m_axi_arvalid : out std_logic;
m_axi_arready : in std_logic;
-- AXI4-Lite host read data channel --
m_axi_rdata : in std_logic_vector(31 downto 0);
m_axi_rresp : in std_logic_vector(1 downto 0);
m_axi_rvalid : in std_logic;
m_axi_rready : out std_logic;
-- AXI4-Lite host write response channel --
m_axi_bresp : in std_logic_vector(1 downto 0);
m_axi_bvalid : in std_logic;
m_axi_bready : out std_logic
);
end component;
@ -334,16 +327,16 @@ architecture neorv32_vivado_ip_rtl of neorv32_vivado_ip is
signal xirq_i_aux : std_ulogic_vector(31 downto 0);
-- internal wishbone bus --
signal xbus_adr : std_ulogic_vector(31 downto 0); -- address
signal xbus_do : std_ulogic_vector(31 downto 0); -- write data
signal xbus_tag : std_ulogic_vector(2 downto 0); -- access tag
signal xbus_we : std_ulogic; -- read/write
signal xbus_sel : std_ulogic_vector(3 downto 0); -- byte enable
signal xbus_stb : std_ulogic; -- strobe
signal xbus_cyc : std_ulogic; -- valid cycle
signal xbus_di : std_ulogic_vector(31 downto 0); -- read data
signal xbus_ack : std_ulogic; -- transfer acknowledge
signal xbus_err : std_ulogic; -- transfer error
signal xbus_adr : std_ulogic_vector(31 downto 0); -- address
signal xbus_do : std_ulogic_vector(31 downto 0); -- write data
signal xbus_tag : std_ulogic_vector(2 downto 0); -- access tag
signal xbus_we : std_ulogic; -- read/write
signal xbus_sel : std_ulogic_vector(3 downto 0); -- byte enable
signal xbus_stb : std_ulogic; -- strobe
signal xbus_cyc : std_ulogic; -- valid cycle
signal xbus_di : std_ulogic_vector(31 downto 0); -- read data
signal xbus_ack : std_ulogic; -- transfer acknowledge
signal xbus_err : std_ulogic; -- transfer error
begin
@ -351,12 +344,15 @@ begin
-- -------------------------------------------------------------------------------------------
neorv32_top_inst: neorv32_top
generic map (
-- General --
-- 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),
INT_BOOTLOADER_EN => INT_BOOTLOADER_EN,
-- Boot Configuration --
BOOT_MODE_SELECT => BOOT_MODE_SELECT,
BOOT_ADDR_CUSTOM => BOOT_ADDR_CUSTOM,
-- On-Chip Debugger --
OCD_EN => OCD_EN,
OCD_AUTHENTICATION => OCD_AUTHENTICATION,
@ -412,7 +408,7 @@ begin
-- External bus interface --
XBUS_EN => XBUS_EN,
XBUS_TIMEOUT => XBUS_TIMEOUT,
XBUS_REGSTAGE_EN => false,
XBUS_REGSTAGE_EN => XBUS_REGSTAGE_EN,
XBUS_CACHE_EN => XBUS_CACHE_EN,
XBUS_CACHE_NUM_BLOCKS => XBUS_CACHE_NUM_BLOCKS,
XBUS_CACHE_BLOCK_SIZE => XBUS_CACHE_BLOCK_SIZE,
@ -424,6 +420,7 @@ begin
-- External Interrupts Controller --
XIRQ_NUM_CH => num_xirq_c,
-- Processor peripherals --
IO_DISABLE_SYSINFO => false,
IO_GPIO_NUM => num_gpio_c,
IO_MTIME_EN => IO_MTIME_EN,
IO_UART0_EN => IO_UART0_EN,
@ -451,7 +448,7 @@ begin
IO_GPTMR_EN => IO_GPTMR_EN,
IO_ONEWIRE_EN => IO_ONEWIRE_EN,
IO_DMA_EN => IO_DMA_EN,
IO_SLINK_EN => AXI4_STREAM_EN,
IO_SLINK_EN => IO_SLINK_EN,
IO_SLINK_RX_FIFO => IO_SLINK_RX_FIFO,
IO_SLINK_TX_FIFO => IO_SLINK_TX_FIFO,
IO_CRC_EN => IO_CRC_EN
@ -618,14 +615,10 @@ begin
if XBUS_EN generate
axi4_bridge_inst: xbus2axi4lite_bridge
port map (
-- ------------------------------------------------------------
-- Global Control
-- ------------------------------------------------------------
-- Global control --
clk => clk,
resetn => resetn,
-- ------------------------------------------------------------
-- XBUS Device Interface
-- ------------------------------------------------------------
-- XBUS device interface --
xbus_adr_i => xbus_adr,
xbus_dat_i => xbus_do,
xbus_tag_i => xbus_tag,
@ -636,30 +629,27 @@ begin
xbus_ack_o => xbus_ack,
xbus_err_o => xbus_err,
xbus_dat_o => xbus_di,
-- ------------------------------------------------------------
-- AXI4-Lite Host Interface
-- ------------------------------------------------------------
-- Write Address Channel --
-- AXI4-Lite host write address channel --
m_axi_awaddr => m_axi_awaddr,
m_axi_awprot => m_axi_awprot,
m_axi_awvalid => m_axi_awvalid,
m_axi_awready => m_axi_awready,
-- Write Data Channel --
-- AXI4-Lite host write data channel --
m_axi_wdata => m_axi_wdata,
m_axi_wstrb => m_axi_wstrb,
m_axi_wvalid => m_axi_wvalid,
m_axi_wready => m_axi_wready,
-- Read Address Channel --
-- AXI4-Lite host read address channel --
m_axi_araddr => m_axi_araddr,
m_axi_arprot => m_axi_arprot,
m_axi_arvalid => m_axi_arvalid,
m_axi_arready => m_axi_arready,
-- Read Data Channel --
-- AXI4-Lite host read data channel --
m_axi_rdata => m_axi_rdata,
m_axi_rresp => m_axi_rresp,
m_axi_rvalid => m_axi_rvalid,
m_axi_rready => m_axi_rready,
-- Write Response Channel --
-- AXI4-Lite host write response channel --
m_axi_bresp => m_axi_bresp,
m_axi_bvalid => m_axi_bvalid,
m_axi_bready => m_axi_bready

View file

@ -1,5 +1,5 @@
-- ================================================================================ --
-- NEORV32 SoC - XBUS to AHB3-Lite Bridge (non-overlapping single transfers only) --
-- NEORV32 SoC - XBUS to AHB3-Lite Bridge (single non-overlapping transfers only) --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --

View file

@ -1,5 +1,5 @@
-- ================================================================================ --
-- NEORV32 SoC - XBUS to AXI4-Lite Bridge (non-overlapping single transfers only) --
-- NEORV32 SoC - XBUS to AXI4-Lite Bridge (single non-overlapping transfers only) --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
@ -14,139 +14,94 @@ use ieee.numeric_std.all;
entity xbus2axi4lite_bridge is
port (
-- ------------------------------------------------------------
-- Global Control
-- ------------------------------------------------------------
clk : in std_logic;
resetn : in std_logic; -- low-active
-- ------------------------------------------------------------
-- XBUS Device Interface
-- ------------------------------------------------------------
xbus_adr_i : in std_ulogic_vector(31 downto 0); -- address
xbus_dat_i : in std_ulogic_vector(31 downto 0); -- write data
xbus_tag_i : in std_ulogic_vector(2 downto 0); -- access tag
xbus_we_i : in std_ulogic; -- read/write
xbus_sel_i : in std_ulogic_vector(3 downto 0); -- byte enable
xbus_stb_i : in std_ulogic; -- strobe
xbus_cyc_i : in std_ulogic; -- valid cycle
xbus_ack_o : out std_ulogic; -- transfer acknowledge
xbus_err_o : out std_ulogic; -- transfer error
xbus_dat_o : out std_ulogic_vector(31 downto 0); -- read data
-- ------------------------------------------------------------
-- AXI4-Lite Host Interface
-- ------------------------------------------------------------
-- Clock and Reset --
-- m_axi_aclk : in std_logic; -- just to satisfy Vivado, but not actually used
-- m_axi_aresetn : in std_logic; -- just to satisfy Vivado, but not actually used
-- Write Address Channel --
m_axi_awaddr : out std_logic_vector(31 downto 0);
m_axi_awprot : out std_logic_vector(2 downto 0);
m_axi_awvalid : out std_logic;
m_axi_awready : in std_logic;
-- Write Data Channel --
m_axi_wdata : out std_logic_vector(31 downto 0);
m_axi_wstrb : out std_logic_vector(3 downto 0);
m_axi_wvalid : out std_logic;
m_axi_wready : in std_logic;
-- Read Address Channel --
m_axi_araddr : out std_logic_vector(31 downto 0);
m_axi_arprot : out std_logic_vector(2 downto 0);
m_axi_arvalid : out std_logic;
m_axi_arready : in std_logic;
-- Read Data Channel --
m_axi_rdata : in std_logic_vector(31 downto 0);
m_axi_rresp : in std_logic_vector(1 downto 0);
m_axi_rvalid : in std_logic;
m_axi_rready : out std_logic;
-- Write Response Channel --
m_axi_bresp : in std_logic_vector(1 downto 0);
m_axi_bvalid : in std_logic;
m_axi_bready : out std_logic
-- Global control --
clk : in std_logic;
resetn : in std_logic;
-- XBUS device interface --
xbus_adr_i : in std_ulogic_vector(31 downto 0);
xbus_dat_i : in std_ulogic_vector(31 downto 0);
xbus_tag_i : in std_ulogic_vector(2 downto 0);
xbus_we_i : in std_ulogic;
xbus_sel_i : in std_ulogic_vector(3 downto 0);
xbus_stb_i : in std_ulogic;
xbus_cyc_i : in std_ulogic;
xbus_ack_o : out std_ulogic;
xbus_err_o : out std_ulogic;
xbus_dat_o : out std_ulogic_vector(31 downto 0);
-- AXI4-Lite host write address channel --
m_axi_awaddr : out std_logic_vector(31 downto 0);
m_axi_awprot : out std_logic_vector(2 downto 0);
m_axi_awvalid : out std_logic;
m_axi_awready : in std_logic;
-- AXI4-Lite host write data channel --
m_axi_wdata : out std_logic_vector(31 downto 0);
m_axi_wstrb : out std_logic_vector(3 downto 0);
m_axi_wvalid : out std_logic;
m_axi_wready : in std_logic;
-- AXI4-Lite host read address channel --
m_axi_araddr : out std_logic_vector(31 downto 0);
m_axi_arprot : out std_logic_vector(2 downto 0);
m_axi_arvalid : out std_logic;
m_axi_arready : in std_logic;
-- AXI4-Lite host read data channel --
m_axi_rdata : in std_logic_vector(31 downto 0);
m_axi_rresp : in std_logic_vector(1 downto 0);
m_axi_rvalid : in std_logic;
m_axi_rready : out std_logic;
-- AXI4-Lite host write response channel --
m_axi_bresp : in std_logic_vector(1 downto 0);
m_axi_bvalid : in std_logic;
m_axi_bready : out std_logic
);
end entity;
architecture xbus2axi4lite_bridge_rtl of xbus2axi4lite_bridge is
-- AXI bridge control --
signal axi_radr_received, axi_wadr_received, axi_wdat_received : std_ulogic;
signal ready : std_ulogic_vector(2 downto 0);
signal xbus_rd_ack, xbus_rd_err, xbus_wr_ack, xbus_wr_err : std_ulogic;
begin
-- channel arbiter --
axi_arbiter: process(resetn, clk)
-- channel handshake arbiter --
axi_handshake: process(resetn, clk)
begin
if (resetn = '0') then
axi_radr_received <= '0';
axi_wadr_received <= '0';
axi_wdat_received <= '0';
ready <= (others => '0');
elsif rising_edge(clk) then
if (xbus_cyc_i = '0') then
axi_radr_received <= '0';
axi_wadr_received <= '0';
axi_wdat_received <= '0';
else -- pending access
if (xbus_we_i = '0') then -- read
if (m_axi_arready = '1') then -- read address received by interconnect?
axi_radr_received <= '1';
end if;
else -- write
if (m_axi_awready = '1') then -- write address received by interconnect?
axi_wadr_received <= '1';
end if;
if (m_axi_wready = '1') then -- write data received by interconnect?
axi_wdat_received <= '1';
end if;
end if;
end if;
ready(0) <= xbus_cyc_i and (ready(0) or std_ulogic(m_axi_arready));
ready(1) <= xbus_cyc_i and (ready(1) or std_ulogic(m_axi_awready));
ready(2) <= xbus_cyc_i and (ready(2) or std_ulogic(m_axi_wready));
end if;
end process axi_arbiter;
end process axi_handshake;
-- read address channel --
-- AXI read address channel --
m_axi_araddr <= std_logic_vector(xbus_adr_i);
m_axi_arprot <= std_logic_vector(xbus_tag_i);
m_axi_arvalid <= std_logic(xbus_cyc_i and (not xbus_we_i) and (not axi_radr_received));
m_axi_arvalid <= std_logic(xbus_cyc_i and (not xbus_we_i) and (not ready(0)));
-- read data channel --
-- AXI read data channel --
m_axi_rready <= std_logic(xbus_cyc_i and (not xbus_we_i));
xbus_dat_o <= std_ulogic_vector(m_axi_rdata);
xbus_rd_ack <= '1' when (m_axi_rvalid = '1') and (m_axi_rresp = "00") else '0';
xbus_rd_err <= '1' when (m_axi_rvalid = '1') and (m_axi_rresp /= "00") else '0';
-- write address channel --
-- AXI write address channel --
m_axi_awaddr <= std_logic_vector(xbus_adr_i);
m_axi_awprot <= std_logic_vector(xbus_tag_i);
m_axi_awvalid <= std_logic(xbus_cyc_i and xbus_we_i and (not axi_wadr_received));
m_axi_awvalid <= std_logic(xbus_cyc_i and xbus_we_i and (not ready(1)));
-- write data channel --
-- AXI write data channel --
m_axi_wdata <= std_logic_vector(xbus_dat_i);
m_axi_wstrb <= std_logic_vector(xbus_sel_i);
m_axi_wvalid <= std_logic(xbus_cyc_i and xbus_we_i and (not axi_wdat_received));
m_axi_wvalid <= std_logic(xbus_cyc_i and xbus_we_i and (not ready(2)));
-- write response channel --
-- AXI write response channel --
m_axi_bready <= std_logic(xbus_cyc_i and xbus_we_i);
xbus_wr_ack <= '1' when (m_axi_bvalid = '1') and (m_axi_bresp = "00") else '0';
xbus_wr_err <= '1' when (m_axi_bvalid = '1') and (m_axi_bresp /= "00") else '0';
-- read/write response --
axi_response: process(xbus_we_i, m_axi_bvalid, m_axi_bresp, m_axi_rvalid, m_axi_rresp)
begin
xbus_ack_o <= '0'; -- default
xbus_err_o <= '0'; -- default
if (xbus_we_i = '1') then -- write operation
if (m_axi_bvalid = '1') then -- valid write response
if (m_axi_bresp = "00") then -- status check
xbus_ack_o <= '1'; -- OK
else
xbus_err_o <= '1'; -- ERROR
end if;
end if;
else -- read operation
if (m_axi_rvalid = '1') then -- valid read response
if (m_axi_rresp = "00") then -- status check
xbus_ack_o <= '1'; -- OK
else
xbus_err_o <= '1'; -- ERROR
end if;
end if;
end if;
end process axi_response;
-- XBUS response --
xbus_ack_o <= xbus_rd_ack when (xbus_we_i = '0') else xbus_wr_ack;
xbus_err_o <= xbus_rd_err when (xbus_we_i = '0') else xbus_wr_err;
end architecture xbus2axi4lite_bridge_rtl;

View file

@ -41,9 +41,10 @@ begin
-- -------------------------------------------------------------------------------------------
neorv32_top_inst: neorv32_top
generic map (
-- General --
-- Clocking --
CLOCK_FREQUENCY => CLOCK_FREQUENCY, -- clock frequency of clk_i in Hz
INT_BOOTLOADER_EN => false, -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- Boot Configuration --
BOOT_MODE_SELECT => 2, -- boot from pre-initialized IMEM
-- RISC-V CPU Extensions --
RISCV_ISA_C => true, -- implement compressed extension?
RISCV_ISA_M => true, -- implement mul/div extension?

View file

@ -44,9 +44,10 @@ begin
-- -------------------------------------------------------------------------------------------
neorv32_top_inst: neorv32_top
generic map (
-- General --
-- Clocking --
CLOCK_FREQUENCY => CLOCK_FREQUENCY, -- clock frequency of clk_i in Hz
INT_BOOTLOADER_EN => true, -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- Boot Configuration --
BOOT_MODE_SELECT => 0, -- boot via internal bootloader
-- RISC-V CPU Extensions --
RISCV_ISA_C => true, -- implement compressed extension?
RISCV_ISA_M => true, -- implement mul/div extension?

View file

@ -49,9 +49,10 @@ begin
-- -------------------------------------------------------------------------------------------
neorv32_top_inst: neorv32_top
generic map (
-- General --
-- Clocking --
CLOCK_FREQUENCY => CLOCK_FREQUENCY, -- clock frequency of clk_i in Hz
INT_BOOTLOADER_EN => true, -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- Boot Configuration --
BOOT_MODE_SELECT => 0, -- boot via internal bootloader
-- On-Chip Debugger (OCD) --
OCD_EN => true, -- implement on-chip debugger
-- RISC-V CPU Extensions --

View file

@ -1,24 +0,0 @@
## Simulation Sources
### > [`simple`](simple) testbench
"Simple" testbench for the NEORV32 Processor and script for simulation using GHDL.
- [`ghdl.setup.sh`](simple/ghdl.setup.sh)
- [`ghdl.run.sh`](simple/ghdl.run.sh)
- [`ghdl.sh`](simple/ghdl.sh)
- [`neorv32_tb.simple.vhd`](simple/neorv32_tb.simple.vhd)
- [`uart_rx.simple.vhd`](simple/uart_rx.simple.vhd)
### > VUnit testbench (this folder)
VUnit testbench for the NEORV32 Processor.
> [!WARNING]
> This testbench requires VHDL-2008 (or newer) as standard!
- [`run.py`](run.py)
- [`neorv32_tb.vhd`](neorv32_tb.vhd)
- [`uart_rx_pkg.vhd`](uart_rx_pkg.vhd)
- [`uart_rx.vhd`](uart_rx.vhd)

57
sim/ghdl.run.sh Normal file
View file

@ -0,0 +1,57 @@
#!/usr/bin/env bash
set -e
cd $(dirname "$0")
NEORV32_RTL=${NEORV32_RTL:-../rtl}
FILE_LIST=`cat $NEORV32_RTL/file_list_soc.f`
CORE_SRCS="${FILE_LIST//NEORV32_RTL_PATH_PLACEHOLDER/"$NEORV32_RTL"}"
GHDL="${GHDL:-ghdl}"
# Prepare UART SIM_MODE output files
touch neorv32.uart0_sim_mode.out neorv32.uart1_sim_mode.out
chmod 777 neorv32.uart0_sim_mode.out neorv32.uart1_sim_mode.out
# Prepare testbench UART log files
touch neorv32_tb.uart0_rx.out neorv32_tb.uart1_rx.out
chmod 777 neorv32_tb.uart0_rx.out neorv32_tb.uart1_rx.out
# GHDL build directory
mkdir -p build
# GHDL import
$GHDL -i --work=neorv32 --workdir=build \
$CORE_SRCS \
"$NEORV32_RTL"/processor_templates/*.vhd \
"$NEORV32_RTL"/system_integration/*.vhd \
"$NEORV32_RTL"/test_setups/*.vhd \
neorv32_tb.vhd \
sim_uart_rx.vhd \
xbus_gateway.vhd \
xbus_memory.vhd
# GHDL analyze
$GHDL -m --work=neorv32 --workdir=build neorv32_tb
# GHDL run parameters
if [ -z "$1" ]
then
GHDL_RUN_ARGS="${@:---stop-time=10ms}"
else
# Let's pass down all the parameters to GHDL
GHDL_RUN_ARGS=$@
fi
echo "GHDL simulation run parameters: $GHDL_RUN_ARGS";
# GHDL run
runcmd="$GHDL -r --work=neorv32 --workdir=build neorv32_tb \
--max-stack-alloc=0 \
--ieee-asserts=disable \
--assert-level=error $GHDL_RUN_ARGS"
if [ -n "$GHDL_DEVNULL" ]; then
$runcmd >> /dev/null
else
$runcmd
fi

11
sim/ghdl.sh Normal file
View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
# Abort if any command returns != 0
set -e
cd $(dirname "$0")
echo "[TIP] Compile application with USER_FLAGS+=-DUART[0/1]_SIM_MODE to enable UART[0/1]'s simulation mode (redirect UART output to simulator console)."
# Run simulation (pass down more than 1 parameter to GHDL)
/bin/bash ghdl.run.sh $@

View file

@ -1,5 +1,5 @@
-- ================================================================================ --
-- NEORV32 - VUnit Processor Testbench --
-- NEORV32 - Default Processor Testbench --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
@ -8,11 +8,6 @@
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
library vunit_lib;
context vunit_lib.vunit_context;
context vunit_lib.com_context;
context vunit_lib.vc_context;
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
@ -20,599 +15,452 @@ use ieee.math_real.all;
library neorv32;
use neorv32.neorv32_package.all;
use neorv32.neorv32_application_image.all; -- this file is generated by the image generator
use std.textio.all;
library osvvm;
use osvvm.RandomPkg.all;
use work.uart_rx_pkg.all;
entity neorv32_tb is
generic (runner_cfg : string := runner_cfg_default;
ci_mode : boolean := false);
generic (
CLOCK_FREQUENCY : natural := 100_000_000; -- clock frequency of clk_i in Hz
BOOT_MODE_SELECT : natural range 0 to 2 := 2; -- boot from pre-initialized IMEM
RISCV_ISA_C : boolean := false; -- implement compressed extension
RISCV_ISA_E : boolean := false; -- implement embedded RF extension
RISCV_ISA_M : boolean := true; -- implement mul/div extension
RISCV_ISA_U : boolean := true; -- implement user mode extension
RISCV_ISA_Zalrsc : boolean := true; -- implement atomic reservation-set extension
RISCV_ISA_Zba : boolean := true; -- implement shifted-add bit-manipulation extension
RISCV_ISA_Zbb : boolean := true; -- implement basic bit-manipulation extension
RISCV_ISA_Zbkb : boolean := true; -- implement bit-manipulation instructions for cryptography
RISCV_ISA_Zbkc : boolean := true; -- implement carry-less multiplication instructions
RISCV_ISA_Zbkx : boolean := true; -- implement cryptography crossbar permutation extension
RISCV_ISA_Zbs : boolean := true; -- implement single-bit bit-manipulation extension
RISCV_ISA_Zfinx : boolean := true; -- implement 32-bit floating-point extension
RISCV_ISA_Zicntr : boolean := true; -- implement base counters
RISCV_ISA_Zicond : boolean := true; -- implement integer conditional operations
RISCV_ISA_Zihpm : boolean := true; -- implement hardware performance monitors
RISCV_ISA_Zknd : boolean := true; -- implement cryptography NIST AES decryption extension
RISCV_ISA_Zkne : boolean := true; -- implement cryptography NIST AES encryption extension
RISCV_ISA_Zknh : boolean := true; -- implement cryptography NIST hash extension
RISCV_ISA_Zksed : boolean := true; -- implement ShangMi block cypher extension
RISCV_ISA_Zksh : boolean := true; -- implement ShangMi hash extension
RISCV_ISA_Zmmul : boolean := true; -- implement multiply-only M sub-extension
RISCV_ISA_Zxcfu : boolean := true; -- implement custom (instr.) functions unit
FAST_MUL_EN : boolean := true; -- use DSPs for M extension's multiplier
FAST_SHIFT_EN : boolean := true; -- use barrel shifter for shift operations
REGFILE_HW_RST : boolean := true; -- implement full hardware reset for register file
MEM_INT_IMEM_EN : boolean := true; -- implement processor-internal instruction memory
MEM_INT_IMEM_SIZE : natural := 32*1024; -- size of processor-internal instruction memory in bytes (use a power of 2)
MEM_INT_DMEM_EN : boolean := true; -- implement processor-internal data memory
MEM_INT_DMEM_SIZE : natural := 8*1024; -- size of processor-internal data memory in bytes (use a power of 2)
ICACHE_EN : boolean := true; -- implement instruction cache
ICACHE_NUM_BLOCKS : natural range 1 to 256 := 64; -- i-cache: number of blocks (min 1), has to be a power of 2
ICACHE_BLOCK_SIZE : natural range 4 to 2**16 := 32; -- i-cache: block size in bytes (min 4), has to be a power of 2
DCACHE_EN : boolean := true; -- implement data cache
DCACHE_NUM_BLOCKS : natural range 1 to 256 := 32; -- d-cache: number of blocks (min 1), has to be a power of 2
DCACHE_BLOCK_SIZE : natural range 4 to 2**16 := 32 -- d-cache: block size in bytes (min 4), has to be a power of 2
);
end neorv32_tb;
architecture neorv32_tb_rtl of neorv32_tb is
-- User Configuration ---------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
-- general --
constant int_imem_c : boolean := false; -- true: use proc-internal IMEM, false: use external simulated IMEM (ext. mem A)
constant int_dmem_c : boolean := false; -- true: use proc-internal DMEM, false: use external simulated DMEM (ext. mem B)
constant imem_size_c : natural := 32*1024; -- size in bytes of processor-internal IMEM / external mem A
constant dmem_size_c : natural := 8*1024; -- size in bytes of processor-internal DMEM / external mem B
constant f_clock_c : natural := 100000000; -- main clock in Hz
constant baud0_rate_c : natural := 19200; -- simulation UART0 (primary UART) baud rate
constant baud1_rate_c : natural := 19200; -- simulation UART1 (secondary UART) baud rate
constant icache_en_c : boolean := false; -- implement i-cache
constant icache_block_size_c : natural := 64; -- i-cache block size in bytes
-- simulated external Wishbone memory A (can be used as external IMEM) --
constant ext_mem_a_base_addr_c : std_ulogic_vector(31 downto 0) := x"00000000"; -- wishbone memory base address (external IMEM base)
constant ext_mem_a_size_c : natural := imem_size_c; -- wishbone memory size in bytes
constant ext_mem_a_latency_c : natural := 8; -- latency in clock cycles (min 1, max 255), plus 1 cycle initial delay
-- simulated external Wishbone memory B (can be used as external DMEM) --
constant ext_mem_b_base_addr_c : std_ulogic_vector(31 downto 0) := x"80000000"; -- wishbone memory base address (external DMEM base)
constant ext_mem_b_size_c : natural := dmem_size_c; -- wishbone memory size in bytes
constant ext_mem_b_latency_c : natural := 8; -- latency in clock cycles (min 1, max 255), plus 1 cycle initial delay
-- simulated external Wishbone memory C (can be used to simulate external IO access) --
constant ext_mem_c_base_addr_c : std_ulogic_vector(31 downto 0) := x"F0000000"; -- wishbone memory base address (default begin of EXTERNAL IO area)
constant ext_mem_c_size_c : natural := icache_block_size_c/2; -- wishbone memory size in bytes, should be smaller than an iCACHE block
constant ext_mem_c_latency_c : natural := 128; -- latency in clock cycles (min 1, max 255), plus 1 cycle initial delay
-- simulation interrupt trigger --
constant irq_trigger_base_addr_c : std_ulogic_vector(31 downto 0) := x"FF000000";
-- -------------------------------------------------------------------------------------------
-- internals - hands off! --
constant uart0_baud_val_c : real := real(f_clock_c) / real(baud0_rate_c);
constant uart1_baud_val_c : real := real(f_clock_c) / real(baud1_rate_c);
constant t_clock_c : time := (1 sec) / f_clock_c;
-- generators --
signal clk_gen, rst_gen : std_ulogic := '0';
-- uart --
signal uart0_txd, uart1_txd : std_ulogic;
signal uart0_cts, uart1_cts : std_ulogic;
-- gpio --
-- IO connection --
signal uart0_txd, uart0_cts, uart1_txd, uart1_cts : std_ulogic;
signal gpio : std_ulogic_vector(63 downto 0);
-- twi --
signal twi_scl, twi_sda : std_logic;
signal twi_scl_i, twi_scl_o, twi_sda_i, twi_sda_o : std_ulogic;
-- 1-wire --
signal onewire : std_logic;
signal onewire_i, onewire_o : std_ulogic;
-- spi & sdi --
signal spi_csn: std_ulogic_vector(7 downto 0);
signal spi_csn : std_ulogic_vector(7 downto 0);
signal spi_di, spi_do, spi_clk : std_ulogic;
signal sdi_di, sdi_do, sdi_clk, sdi_csn : std_ulogic;
signal msi, mei, mti : std_ulogic;
-- irq --
signal msi_ring, mei_ring : std_ulogic;
-- slink --
signal slink_tx, slink_rx : slink_t;
-- SLINK echo --
signal slink_dat : std_ulogic_vector(31 downto 0);
signal slink_val : std_ulogic;
signal slink_lst : std_ulogic;
signal slink_rdy : std_ulogic;
signal slink_id : std_ulogic_vector(3 downto 0);
-- Wishbone bus --
type wishbone_t is record
addr : std_ulogic_vector(31 downto 0); -- address
wdata : std_ulogic_vector(31 downto 0); -- master write data
rdata : std_ulogic_vector(31 downto 0); -- master read data
tag : std_ulogic_vector(2 downto 0); -- access tag
we : std_ulogic; -- write enable
sel : std_ulogic_vector(3 downto 0); -- byte enable
stb : std_ulogic; -- strobe
cyc : std_ulogic; -- valid cycle
ack : std_ulogic; -- transfer acknowledge
err : std_ulogic; -- transfer error
end record;
signal wb_cpu, wb_mem_a, wb_mem_b, wb_mem_c, wb_irq : wishbone_t;
-- Wishbone access latency type --
type ext_mem_read_latency_t is array (0 to 255) of std_ulogic_vector(31 downto 0);
-- simulated external memory c (IO) --
signal ext_ram_c : mem32_t(0 to ext_mem_c_size_c/4-1); -- uninitialized, used to simulate external IO
-- simulated external memory bus feedback type --
type ext_mem_t is record
rdata : ext_mem_read_latency_t;
acc_en : std_ulogic;
ack : std_ulogic_vector(255 downto 0);
end record;
signal ext_mem_a, ext_mem_b, ext_mem_c : ext_mem_t;
constant uart0_rx_logger : logger_t := get_logger("UART0.RX");
constant uart1_rx_logger : logger_t := get_logger("UART1.RX");
constant uart0_rx_handle : uart_rx_t := new_uart_rx(uart0_baud_val_c, uart0_rx_logger);
constant uart1_rx_handle : uart_rx_t := new_uart_rx(uart1_baud_val_c, uart1_rx_logger);
-- XBUS (Wishbone b4) bus --
signal xbus_core_req, xbus_imem_req, xbus_dmem_req, xbus_mmio_req, xbus_trig_req : xbus_req_t;
signal xbus_core_rsp, xbus_imem_rsp, xbus_dmem_rsp, xbus_mmio_rsp, xbus_trig_rsp : xbus_rsp_t;
begin
test_runner : process
variable msg : msg_t;
variable rnd : RandomPType;
begin
test_runner_setup(runner, runner_cfg);
rnd.InitSeed(test_runner'path_name);
-- Show passing checks for UART0 on the display (stdout)
show(uart0_rx_logger, display_handler, pass);
show(uart1_rx_logger, display_handler, pass);
if ci_mode then
check_uart(net, uart0_rx_handle, nul & nul);
else
check_uart(net, uart0_rx_handle, "Blinking LED demo program" & cr & lf);
end if;
if ci_mode then
-- No need to send the full expectation in one big chunk
check_uart(net, uart1_rx_handle, nul & nul);
check_uart(net, uart1_rx_handle, "0/55" & cr & lf);
end if;
-- Wait until all expected data has been received
--
-- wait_until_idle can take the VC actor as argument but
-- the more abstract view is that wait_until_idle is part
-- of the sync VCI and to use it a VC must be cast
-- to a sync VC
wait_until_idle(net, as_sync(uart0_rx_handle));
wait_until_idle(net, as_sync(uart1_rx_handle));
-- Wait a bit more if some extra unexpected data is produced. If so,
-- uart_rx will fail
wait for (20 * (1e9 / baud0_rate_c)) * ns;
test_runner_cleanup(runner);
end process;
-- In case we get stuck waiting there is a watchdog timeout to terminate and fail the
-- testbench
test_runner_watchdog(runner, 50 ms);
-- Clock/Reset Generator ------------------------------------------------------------------
-- Clock & Reset Generators ---------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
clk_gen <= not clk_gen after (t_clock_c/2);
rst_gen <= '0', '1' after 60*(t_clock_c/2);
clk_gen <= not clk_gen after (((1 sec) / CLOCK_FREQUENCY) / 2);
rst_gen <= '0', '1' after 60*(((1 sec) / CLOCK_FREQUENCY) / 2);
-- The Core of the Problem ----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_top_inst: neorv32_top
generic map (
-- General --
CLOCK_FREQUENCY => f_clock_c, -- clock frequency of clk_i in Hz
CLOCK_GATING_EN => true, -- enable clock gating when in sleep mode
HART_ID => x"00000000", -- hardware thread ID
JEDEC_ID => "00000000000", -- vendor's JEDEC ID
INT_BOOTLOADER_EN => false, -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- Clocking --
CLOCK_FREQUENCY => CLOCK_FREQUENCY,
CLOCK_GATING_EN => true,
-- Identification --
HART_ID => x"00000000",
JEDEC_ID => "00000000000",
-- Boot Configuration --
BOOT_MODE_SELECT => BOOT_MODE_SELECT,
BOOT_ADDR_CUSTOM => x"00000000",
-- On-Chip Debugger (OCD) --
OCD_EN => true, -- implement on-chip debugger
OCD_AUTHENTICATION => true, -- implement on-chip debugger authentication
OCD_EN => true,
OCD_AUTHENTICATION => true,
-- RISC-V CPU Extensions --
RISCV_ISA_C => true, -- implement compressed extension?
RISCV_ISA_E => false, -- implement embedded RF extension?
RISCV_ISA_M => true, -- implement mul/div extension?
RISCV_ISA_U => true, -- implement user mode extension?
RISCV_ISA_Zalrsc => true, -- implement atomic reservation-set extension
RISCV_ISA_Zba => true, -- implement shifted-add bit-manipulation extension
RISCV_ISA_Zbb => true, -- implement basic bit-manipulation extension
RISCV_ISA_Zbkb => true, -- implement bit-manipulation instructions for cryptography
RISCV_ISA_Zbkc => true, -- implement carry-less multiplication instructions?
RISCV_ISA_Zbkx => true, -- implement cryptography crossbar permutation extension?
RISCV_ISA_Zbs => true, -- implement single-bit bit-manipulation extension
RISCV_ISA_Zfinx => true, -- implement 32-bit floating-point extension (using INT reg!)
RISCV_ISA_Zicntr => true, -- implement base counters?
RISCV_ISA_Zicond => true, -- implement integer conditional operations?
RISCV_ISA_Zihpm => true, -- implement hardware performance monitors?
RISCV_ISA_Zknd => true, -- implement cryptography NIST AES decryption extension?
RISCV_ISA_Zkne => true, -- implement cryptography NIST AES encryption extension?
RISCV_ISA_Zknh => true, -- implement cryptography NIST hash extension?
RISCV_ISA_Zksed => true, -- implement ShangMi block cypher extension?
RISCV_ISA_Zksh => true, -- implement ShangMi hash extension?
RISCV_ISA_Zmmul => false, -- implement multiply-only M sub-extension?
RISCV_ISA_Zxcfu => true, -- implement custom (instr.) functions unit?
RISCV_ISA_C => RISCV_ISA_C,
RISCV_ISA_E => RISCV_ISA_E,
RISCV_ISA_M => RISCV_ISA_M,
RISCV_ISA_U => RISCV_ISA_U,
RISCV_ISA_Zalrsc => RISCV_ISA_Zalrsc,
RISCV_ISA_Zba => RISCV_ISA_Zba,
RISCV_ISA_Zbb => RISCV_ISA_Zbb,
RISCV_ISA_Zbkb => RISCV_ISA_Zbkb,
RISCV_ISA_Zbkc => RISCV_ISA_Zbkc,
RISCV_ISA_Zbkx => RISCV_ISA_Zbkx,
RISCV_ISA_Zbs => RISCV_ISA_Zbs,
RISCV_ISA_Zfinx => RISCV_ISA_Zfinx,
RISCV_ISA_Zicntr => RISCV_ISA_Zicntr,
RISCV_ISA_Zicond => RISCV_ISA_Zicond,
RISCV_ISA_Zihpm => RISCV_ISA_Zihpm,
RISCV_ISA_Zknd => RISCV_ISA_Zknd,
RISCV_ISA_Zkne => RISCV_ISA_Zkne,
RISCV_ISA_Zknh => RISCV_ISA_Zknh,
RISCV_ISA_Zksed => RISCV_ISA_Zksed,
RISCV_ISA_Zksh => RISCV_ISA_Zksh,
RISCV_ISA_Zmmul => RISCV_ISA_Zmmul,
RISCV_ISA_Zxcfu => RISCV_ISA_Zxcfu,
-- Extension Options --
FAST_MUL_EN => false, -- use DSPs for M extension's multiplier
FAST_SHIFT_EN => false, -- use barrel shifter for shift operations
REGFILE_HW_RST => true, -- full hardware reset
FAST_MUL_EN => FAST_MUL_EN,
FAST_SHIFT_EN => FAST_SHIFT_EN,
REGFILE_HW_RST => REGFILE_HW_RST,
-- Physical Memory Protection (PMP) --
PMP_NUM_REGIONS => 5, -- number of regions (0..16)
PMP_MIN_GRANULARITY => 4, -- minimal region granularity in bytes, has to be a power of 2, min 4 bytes
PMP_TOR_MODE_EN => true, -- implement TOR mode
PMP_NAP_MODE_EN => true, -- implement NAPOT/NA4 mode
PMP_NUM_REGIONS => 5,
PMP_MIN_GRANULARITY => 4,
PMP_TOR_MODE_EN => true,
PMP_NAP_MODE_EN => true,
-- Hardware Performance Monitors (HPM) --
HPM_NUM_CNTS => 12, -- number of implemented HPM counters (0..29)
HPM_CNT_WIDTH => 40, -- total size of HPM counters (0..64)
HPM_NUM_CNTS => 12,
HPM_CNT_WIDTH => 40,
-- Internal Instruction memory --
MEM_INT_IMEM_EN => int_imem_c , -- implement processor-internal instruction memory
MEM_INT_IMEM_SIZE => imem_size_c, -- size of processor-internal instruction memory in bytes
MEM_INT_IMEM_EN => MEM_INT_IMEM_EN ,
MEM_INT_IMEM_SIZE => MEM_INT_IMEM_SIZE,
-- Internal Data memory --
MEM_INT_DMEM_EN => int_dmem_c, -- implement processor-internal data memory
MEM_INT_DMEM_SIZE => dmem_size_c, -- size of processor-internal data memory in bytes
MEM_INT_DMEM_EN => MEM_INT_DMEM_EN,
MEM_INT_DMEM_SIZE => MEM_INT_DMEM_SIZE,
-- Internal Cache memory --
ICACHE_EN => false, -- implement instruction cache
ICACHE_EN => ICACHE_EN,
ICACHE_NUM_BLOCKS => ICACHE_NUM_BLOCKS,
ICACHE_BLOCK_SIZE => ICACHE_BLOCK_SIZE,
-- Internal Data Cache (dCACHE) --
DCACHE_EN => false, -- implement data cache
DCACHE_EN => DCACHE_EN,
DCACHE_NUM_BLOCKS => DCACHE_NUM_BLOCKS,
DCACHE_BLOCK_SIZE => DCACHE_BLOCK_SIZE,
-- External bus interface --
XBUS_EN => true, -- implement external memory bus interface?
XBUS_TIMEOUT => 256, -- cycles after a pending bus access auto-terminates (0 = disabled)
XBUS_REGSTAGE_EN => false, -- add register stage
XBUS_CACHE_EN => true, -- enable external bus cache (x-cache)
XBUS_CACHE_NUM_BLOCKS => 64, -- x-cache: number of blocks (min 1), has to be a power of 2
XBUS_CACHE_BLOCK_SIZE => 32, -- x-cache: block size in bytes (min 4), has to be a power of 2
XBUS_EN => true,
XBUS_TIMEOUT => 256,
XBUS_REGSTAGE_EN => true,
XBUS_CACHE_EN => true,
XBUS_CACHE_NUM_BLOCKS => 4,
XBUS_CACHE_BLOCK_SIZE => 32,
-- Execute in-place module (XIP) --
XIP_EN => true, -- implement execute in place module (XIP)?
XIP_CACHE_EN => true, -- implement XIP cache?
XIP_CACHE_NUM_BLOCKS => 4, -- number of blocks (min 1), has to be a power of 2
XIP_CACHE_BLOCK_SIZE => 256, -- block size in bytes (min 4), has to be a power of 2
XIP_EN => true,
XIP_CACHE_EN => true,
XIP_CACHE_NUM_BLOCKS => 4,
XIP_CACHE_BLOCK_SIZE => 256,
-- External Interrupts Controller (XIRQ) --
XIRQ_NUM_CH => 32, -- number of external IRQ channels (0..32)
XIRQ_NUM_CH => 32,
-- Processor peripherals --
IO_GPIO_NUM => 64, -- number of GPIO input/output pairs (0..64)
IO_MTIME_EN => true, -- implement machine system timer (MTIME)?
IO_UART0_EN => true, -- implement primary universal asynchronous receiver/transmitter (UART0)?
IO_UART0_RX_FIFO => 32, -- RX fifo depth, has to be a power of two, min 1
IO_UART0_TX_FIFO => 32, -- TX fifo depth, has to be a power of two, min 1
IO_UART1_EN => true, -- implement secondary universal asynchronous receiver/transmitter (UART1)?
IO_UART1_RX_FIFO => 1, -- RX fifo depth, has to be a power of two, min 1
IO_UART1_TX_FIFO => 1, -- TX fifo depth, has to be a power of two, min 1
IO_SPI_EN => true, -- implement serial peripheral interface (SPI)?
IO_SPI_FIFO => 4, -- SPI RTX fifo depth, has to be zero or a power of two
IO_SDI_EN => true, -- implement serial data interface (SDI)?
IO_SDI_FIFO => 4, -- SDI RTX fifo depth, has to be zero or a power of two
IO_TWI_EN => true, -- implement two-wire interface (TWI)?
IO_TWI_FIFO => 4, -- RTX fifo depth, has to be zero or a power of two, min 1
IO_PWM_NUM_CH => 8, -- number of PWM channels to implement (0..16)
IO_WDT_EN => true, -- implement watch dog timer (WDT)?
IO_TRNG_EN => true, -- implement true random number generator (TRNG)?
IO_TRNG_FIFO => 4, -- TRNG fifo depth, has to be a power of two, min 1
IO_CFS_EN => true, -- implement custom functions subsystem (CFS)?
IO_CFS_CONFIG => (others => '0'), -- custom CFS configuration generic
IO_CFS_IN_SIZE => 32, -- size of CFS input conduit in bits
IO_CFS_OUT_SIZE => 32, -- size of CFS output conduit in bits
IO_NEOLED_EN => true, -- implement NeoPixel-compatible smart LED interface (NEOLED)?
IO_NEOLED_TX_FIFO => 8, -- NEOLED TX FIFO depth, 1..32k, has to be a power of two
IO_GPTMR_EN => true, -- implement general purpose timer (GPTMR)?
IO_ONEWIRE_EN => true, -- implement 1-wire interface (ONEWIRE)?
IO_DMA_EN => true, -- implement direct memory access controller (DMA)?
IO_SLINK_EN => true, -- implement stream link interface (SLINK)?
IO_SLINK_RX_FIFO => 4, -- RX fifo depth, has to be a power of two, min 1
IO_SLINK_TX_FIFO => 4, -- TX fifo depth, has to be a power of two, min 1
IO_CRC_EN => true -- implement cyclic redundancy check unit (CRC)?
IO_GPIO_NUM => 64,
IO_MTIME_EN => true,
IO_UART0_EN => true,
IO_UART0_RX_FIFO => 32,
IO_UART0_TX_FIFO => 32,
IO_UART1_EN => true,
IO_UART1_RX_FIFO => 1,
IO_UART1_TX_FIFO => 1,
IO_SPI_EN => true,
IO_SPI_FIFO => 4,
IO_SDI_EN => true,
IO_SDI_FIFO => 4,
IO_TWI_EN => true,
IO_TWI_FIFO => 4,
IO_PWM_NUM_CH => 8,
IO_WDT_EN => true,
IO_TRNG_EN => true,
IO_TRNG_FIFO => 4,
IO_CFS_EN => true,
IO_CFS_CONFIG => (others => '0'),
IO_CFS_IN_SIZE => 32,
IO_CFS_OUT_SIZE => 32,
IO_NEOLED_EN => true,
IO_NEOLED_TX_FIFO => 8,
IO_GPTMR_EN => true,
IO_ONEWIRE_EN => true,
IO_DMA_EN => true,
IO_SLINK_EN => true,
IO_SLINK_RX_FIFO => 4,
IO_SLINK_TX_FIFO => 4,
IO_CRC_EN => true
)
port map (
-- Global control --
clk_i => clk_gen, -- global clock, rising edge
rstn_i => rst_gen, -- global reset, low-active, async
-- JTAG on-chip debugger interface (available if OCD_EN = true) --
jtag_tck_i => '0', -- serial clock
jtag_tdi_i => '0', -- serial data input
jtag_tdo_o => open, -- serial data output
jtag_tms_i => '0', -- mode select
-- External bus interface (available if XBUS_EN = true) --
xbus_adr_o => wb_cpu.addr, -- address
xbus_dat_o => wb_cpu.wdata, -- write data
xbus_tag_o => wb_cpu.tag, -- access tag
xbus_we_o => wb_cpu.we, -- read/write
xbus_sel_o => wb_cpu.sel, -- byte enable
xbus_stb_o => wb_cpu.stb, -- strobe
xbus_cyc_o => wb_cpu.cyc, -- valid cycle
xbus_dat_i => wb_cpu.rdata, -- read data
xbus_ack_i => wb_cpu.ack, -- transfer acknowledge
xbus_err_i => wb_cpu.err, -- transfer error
-- Stream Link Interface (available if IO_SLINK_EN = true) --
slink_rx_dat_i => slink_dat, -- RX input data
slink_rx_src_i => slink_id, -- RX source routing information
slink_rx_val_i => slink_val, -- RX valid input
slink_rx_lst_i => slink_lst, -- RX last element of stream
slink_rx_rdy_o => slink_rdy, -- RX ready to receive
slink_tx_dat_o => slink_dat, -- TX output data
slink_tx_dst_o => slink_id, -- TX destination routing information
slink_tx_val_o => slink_val, -- TX valid output
slink_tx_lst_o => slink_lst, -- TX last element of stream
slink_tx_rdy_i => slink_rdy, -- TX ready to send
-- XIP (execute in place via SPI) signals (available if XIP_EN = true) --
xip_csn_o => open, -- chip-select, low-active
xip_clk_o => open, -- serial clock
xip_dat_i => '1', -- device data input
xip_dat_o => open, -- controller data output
-- GPIO (available if IO_GPIO_NUM > 0) --
gpio_o => gpio, -- parallel output
gpio_i => gpio, -- parallel input
-- primary UART0 (available if IO_UART0_EN = true) --
uart0_txd_o => uart0_txd, -- UART0 send data
uart0_rxd_i => uart0_txd, -- UART0 receive data
uart0_rts_o => uart1_cts, -- HW flow control: UART0.RX ready to receive ("RTR"), low-active, optional
uart0_cts_i => uart0_cts, -- HW flow control: UART0.TX allowed to transmit, low-active, optional
-- secondary UART1 (available if IO_UART1_EN = true) --
uart1_txd_o => uart1_txd, -- UART1 send data
uart1_rxd_i => uart1_txd, -- UART1 receive data
uart1_rts_o => uart0_cts, -- HW flow control: UART0.RX ready to receive ("RTR"), low-active, optional
uart1_cts_i => uart1_cts, -- HW flow control: UART0.TX allowed to transmit, low-active, optional
-- SPI (available if IO_SPI_EN = true) --
spi_clk_o => spi_clk, -- SPI serial clock
spi_dat_o => spi_do, -- controller data out, peripheral data in
spi_dat_i => spi_di, -- controller data in, peripheral data out
spi_csn_o => spi_csn, -- SPI CS
-- SDI (available if IO_SDI_EN = true) --
sdi_clk_i => sdi_clk, -- SDI serial clock
sdi_dat_o => sdi_do, -- controller data out, peripheral data in
sdi_dat_i => sdi_di, -- controller data in, peripheral data out
sdi_csn_i => sdi_csn, -- chip-select
-- TWI (available if IO_TWI_EN = true) --
twi_sda_i => twi_sda_i, -- serial data line sense input
twi_sda_o => twi_sda_o, -- serial data line output (pull low only)
twi_scl_i => twi_scl_i, -- serial clock line sense input
twi_scl_o => twi_scl_o, -- serial clock line output (pull low only)
-- 1-Wire Interface (available if IO_ONEWIRE_EN = true) --
onewire_i => onewire_i, -- 1-wire bus sense input
onewire_o => onewire_o, -- 1-wire bus output (pull low only)
-- PWM (available if IO_PWM_NUM_CH > 0) --
pwm_o => open, -- pwm channels
clk_i => clk_gen,
rstn_i => rst_gen,
-- JTAG on-chip debugger interface --
jtag_tck_i => '0',
jtag_tdi_i => '0',
jtag_tdo_o => open,
jtag_tms_i => '0',
-- External bus interface --
xbus_adr_o => xbus_core_req.addr,
xbus_dat_o => xbus_core_req.data,
xbus_tag_o => xbus_core_req.tag,
xbus_we_o => xbus_core_req.we,
xbus_sel_o => xbus_core_req.sel,
xbus_stb_o => xbus_core_req.stb,
xbus_cyc_o => xbus_core_req.cyc,
xbus_dat_i => xbus_core_rsp.data,
xbus_ack_i => xbus_core_rsp.ack,
xbus_err_i => xbus_core_rsp.err,
-- Stream Link Interface --
slink_rx_dat_i => slink_rx.data,
slink_rx_src_i => slink_rx.addr,
slink_rx_val_i => slink_rx.valid,
slink_rx_lst_i => slink_rx.last,
slink_rx_rdy_o => slink_rx.ready,
slink_tx_dat_o => slink_tx.data,
slink_tx_dst_o => slink_tx.addr,
slink_tx_val_o => slink_tx.valid,
slink_tx_lst_o => slink_tx.last,
slink_tx_rdy_i => slink_tx.ready,
-- XIP --
xip_csn_o => open,
xip_clk_o => open,
xip_dat_i => '0',
xip_dat_o => open,
-- GPIO --
gpio_o => gpio,
gpio_i => gpio,
-- primary UART0 --
uart0_txd_o => uart0_txd,
uart0_rxd_i => uart0_txd,
uart0_rts_o => uart0_cts,
uart0_cts_i => uart0_cts,
-- secondary UART1 --
uart1_txd_o => uart1_txd,
uart1_rxd_i => uart1_txd,
uart1_rts_o => uart1_cts,
uart1_cts_i => uart1_cts,
-- SPI --
spi_clk_o => spi_clk,
spi_dat_o => spi_do,
spi_dat_i => spi_di,
spi_csn_o => spi_csn,
-- SDI --
sdi_clk_i => sdi_clk,
sdi_dat_o => sdi_do,
sdi_dat_i => sdi_di,
sdi_csn_i => sdi_csn,
-- TWI --
twi_sda_i => twi_sda_i,
twi_sda_o => twi_sda_o,
twi_scl_i => twi_scl_i,
twi_scl_o => twi_scl_o,
-- 1-Wire Interface --
onewire_i => onewire_i,
onewire_o => onewire_o,
-- PWM --
pwm_o => open,
-- Custom Functions Subsystem IO --
cfs_in_i => (others => '0'), -- custom CFS inputs
cfs_out_o => open, -- custom CFS outputs
-- NeoPixel-compatible smart LED interface (available if IO_NEOLED_EN = true) --
neoled_o => open, -- async serial data line
-- Machine timer system time (available if IO_MTIME_EN = true) --
cfs_in_i => (others => '0'),
cfs_out_o => open,
-- NeoPixel-compatible smart LED interface --
neoled_o => open,
-- Machine timer system time --
mtime_time_o => open,
-- External platform interrupts (available if XIRQ_NUM_CH > 0) --
xirq_i => gpio(31 downto 0), -- IRQ channels
-- External platform interrupts --
xirq_i => gpio(31 downto 0),
-- CPU Interrupts --
mtime_irq_i => '0', -- machine software interrupt, available if IO_MTIME_EN = false
msw_irq_i => msi_ring, -- machine software interrupt
mext_irq_i => mei_ring -- machine external interrupt
mtime_irq_i => mti,
msw_irq_i => msi,
mext_irq_i => mei
);
-- TWI tri-state driver --
twi_sda <= '0' when (twi_sda_o = '0') else 'Z'; -- module can only pull the line low actively
twi_scl <= '0' when (twi_scl_o = '0') else 'Z';
twi_sda_i <= std_ulogic(twi_sda);
twi_scl_i <= std_ulogic(twi_scl);
-- 1-Wire tri-state driver --
onewire <= '0' when (onewire_o = '0') else 'Z'; -- module can only pull the line low actively
onewire_i <= std_ulogic(onewire);
-- Two-Wire Bus ---------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
twi_sda <= '0' when (twi_sda_o = '0') else 'Z'; -- tristate driver: module can only pull the line low actively
twi_scl <= '0' when (twi_scl_o = '0') else 'Z'; -- tristate driver: module can only pull the line low actively
twi_sda_i <= std_ulogic(twi_sda); -- sense input
twi_scl_i <= std_ulogic(twi_scl); -- sense input
-- TWI termination (pull-ups) --
twi_scl <= 'H';
twi_sda <= 'H';
-- TWI bus termination --
twi_scl <= 'H'; -- weak pull-up "resistor"
twi_sda <= 'H'; -- weak pull-up "resistor"
-- 1-Wire termination (pull-up) --
onewire <= 'H';
-- SPI/SDI echo with propagation delay --
sdi_clk <= spi_clk after 40 ns;
-- One-Wire Bus ---------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
onewire <= '0' when (onewire_o = '0') else 'Z'; -- tristate driver: module can only pull the line low actively
onewire_i <= std_ulogic(onewire); -- sense input
-- 1-Wire bus termination --
onewire <= 'H'; -- weak pull-up "resistor"
-- SP/SDI ---------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
sdi_clk <= spi_clk after 40 ns; -- echo with propagation delay
sdi_csn <= spi_csn(7) after 40 ns;
sdi_di <= spi_do after 40 ns;
spi_di <= sdi_do when (spi_csn(7) = '0') else spi_do after 40 ns;
uart0_checker: entity work.uart_rx
generic map (uart0_rx_handle)
port map (
clk => clk_gen,
uart_txd => uart0_txd);
uart1_checker: entity work.uart_rx
generic map (uart1_rx_handle)
port map (
clk => clk_gen,
uart_txd => uart1_txd);
-- Wishbone Fabric ------------------------------------------------------------------------
-- Stream-Link FIFO Buffer ----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
-- CPU broadcast signals --
wb_mem_a.addr <= wb_cpu.addr;
wb_mem_a.wdata <= wb_cpu.wdata;
wb_mem_a.we <= wb_cpu.we;
wb_mem_a.sel <= wb_cpu.sel;
wb_mem_a.cyc <= wb_cpu.cyc;
wb_mem_b.addr <= wb_cpu.addr;
wb_mem_b.wdata <= wb_cpu.wdata;
wb_mem_b.we <= wb_cpu.we;
wb_mem_b.sel <= wb_cpu.sel;
wb_mem_b.cyc <= wb_cpu.cyc;
wb_mem_c.addr <= wb_cpu.addr;
wb_mem_c.wdata <= wb_cpu.wdata;
wb_mem_c.we <= wb_cpu.we;
wb_mem_c.sel <= wb_cpu.sel;
wb_mem_c.cyc <= wb_cpu.cyc;
wb_irq.addr <= wb_cpu.addr;
wb_irq.wdata <= wb_cpu.wdata;
wb_irq.we <= wb_cpu.we;
wb_irq.sel <= wb_cpu.sel;
wb_irq.cyc <= wb_cpu.cyc;
-- CPU read-back signals (no mux here since peripherals have "output gates") --
wb_cpu.rdata <= wb_mem_a.rdata or wb_mem_b.rdata or wb_mem_c.rdata or wb_irq.rdata;
wb_cpu.ack <= wb_mem_a.ack or wb_mem_b.ack or wb_mem_c.ack or wb_irq.ack;
wb_cpu.err <= wb_mem_a.err or wb_mem_b.err or wb_mem_c.err or wb_irq.err;
-- peripheral select via STROBE signal --
wb_mem_a.stb <= wb_cpu.stb when (wb_cpu.addr >= ext_mem_a_base_addr_c) and (wb_cpu.addr < std_ulogic_vector(unsigned(ext_mem_a_base_addr_c) + ext_mem_a_size_c)) else '0';
wb_mem_b.stb <= wb_cpu.stb when (wb_cpu.addr >= ext_mem_b_base_addr_c) and (wb_cpu.addr < std_ulogic_vector(unsigned(ext_mem_b_base_addr_c) + ext_mem_b_size_c)) else '0';
wb_mem_c.stb <= wb_cpu.stb when (wb_cpu.addr >= ext_mem_c_base_addr_c) and (wb_cpu.addr < std_ulogic_vector(unsigned(ext_mem_c_base_addr_c) + ext_mem_c_size_c)) else '0';
wb_irq.stb <= wb_cpu.stb when (wb_cpu.addr = irq_trigger_base_addr_c) else '0';
slink_buffer: entity neorv32.neorv32_fifo
generic map (
FIFO_DEPTH => 4,
FIFO_WIDTH => 32+4+1,
FIFO_RSYNC => false,
FIFO_SAFE => true,
FULL_RESET => true
)
port map (
-- control --
clk_i => clk_gen,
rstn_i => rst_gen,
clear_i => '0',
half_o => open,
-- write port --
wdata_i(31 downto 0) => slink_tx.data,
wdata_i(35 downto 32) => slink_tx.addr,
wdata_i(36) => slink_tx.last,
we_i => slink_tx.valid,
free_o => slink_tx.ready,
-- read port --
re_i => slink_rx.ready,
rdata_o(31 downto 0) => slink_rx.data,
rdata_o(35 downto 32) => slink_rx.addr,
rdata_o(36) => slink_rx.last,
avail_o => slink_rx.valid
);
-- Wishbone Memory A (simulated external IMEM) --------------------------------------------
-- UART Simulation Receivers --------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
generate_ext_imem:
if (int_imem_c = false) generate
ext_mem_a_access: process(clk_gen)
variable ext_ram_a : mem32_t(0 to ext_mem_a_size_c/4-1) := mem32_init_f(application_init_image, ext_mem_a_size_c/4); -- initialized, used to simulate external IMEM
begin
if rising_edge(clk_gen) then
-- control --
ext_mem_a.ack(0) <= wb_mem_a.cyc and wb_mem_a.stb; -- wishbone acknowledge
sim_rx_uart0: entity work.sim_uart_rx
generic map (
name => "uart0",
fclk => real(CLOCK_FREQUENCY),
baud => real(19200)
)
port map (
clk => clk_gen,
rxd => uart0_txd
);
-- write access --
if ((wb_mem_a.cyc and wb_mem_a.stb and wb_mem_a.we) = '1') then -- valid write access
for i in 0 to 3 loop
if (wb_mem_a.sel(i) = '1') then
ext_ram_a(to_integer(unsigned(wb_mem_a.addr(index_size_f(ext_mem_a_size_c/4)+1 downto 2))))(7+i*8 downto 0+i*8) := wb_mem_a.wdata(7+i*8 downto 0+i*8);
end if;
end loop; -- i
end if;
-- read access --
ext_mem_a.rdata(0) <= ext_ram_a(to_integer(unsigned(wb_mem_a.addr(index_size_f(ext_mem_a_size_c/4)+1 downto 2)))); -- word aligned
-- virtual read and ack latency --
if (ext_mem_a_latency_c > 1) then
for i in 1 to ext_mem_a_latency_c-1 loop
ext_mem_a.rdata(i) <= ext_mem_a.rdata(i-1);
ext_mem_a.ack(i) <= ext_mem_a.ack(i-1) and wb_mem_a.cyc;
end loop;
end if;
-- bus output register --
wb_mem_a.err <= '0';
if (ext_mem_a.ack(ext_mem_a_latency_c-1) = '1') and (wb_mem_a.cyc = '1') then
wb_mem_a.rdata <= ext_mem_a.rdata(ext_mem_a_latency_c-1);
wb_mem_a.ack <= '1';
else
wb_mem_a.rdata <= (others => '0');
wb_mem_a.ack <= '0';
end if;
end if;
end process ext_mem_a_access;
end generate;
generate_ext_imem_false:
if (int_imem_c = true) generate
wb_mem_a.rdata <= (others => '0');
wb_mem_a.ack <= '0';
wb_mem_a.err <= '0';
end generate;
sim_rx_uart1: entity work.sim_uart_rx
generic map (
name => "uart1",
fclk => real(CLOCK_FREQUENCY),
baud => real(19200)
)
port map (
clk => clk_gen,
rxd => uart1_txd
);
-- Wishbone Memory B (simulated external DMEM) --------------------------------------------
-- XBUS / Wishbone Interconnect -----------------------------------------------------------
-- -------------------------------------------------------------------------------------------
generate_ext_dmem:
if (int_dmem_c = false) generate
ext_mem_b_access: process(clk_gen)
variable ext_ram_b : mem32_t(0 to ext_mem_b_size_c/4-1) := (others => (others => '0')); -- zero, used to simulate external DMEM
begin
if rising_edge(clk_gen) then
-- control --
ext_mem_b.ack(0) <= wb_mem_b.cyc and wb_mem_b.stb; -- wishbone acknowledge
-- write access --
if ((wb_mem_b.cyc and wb_mem_b.stb and wb_mem_b.we) = '1') then -- valid write access
for i in 0 to 3 loop
if (wb_mem_b.sel(i) = '1') then
ext_ram_b(to_integer(unsigned(wb_mem_b.addr(index_size_f(ext_mem_b_size_c/4)+1 downto 2))))(7+i*8 downto 0+i*8) := wb_mem_b.wdata(7+i*8 downto 0+i*8);
end if;
end loop; -- i
end if;
-- read access --
ext_mem_b.rdata(0) <= ext_ram_b(to_integer(unsigned(wb_mem_b.addr(index_size_f(ext_mem_b_size_c/4)+1 downto 2)))); -- word aligned
-- virtual read and ack latency --
if (ext_mem_b_latency_c > 1) then
for i in 1 to ext_mem_b_latency_c-1 loop
ext_mem_b.rdata(i) <= ext_mem_b.rdata(i-1);
ext_mem_b.ack(i) <= ext_mem_b.ack(i-1) and wb_mem_b.cyc;
end loop;
end if;
-- bus output register --
wb_mem_b.err <= '0';
if (ext_mem_b.ack(ext_mem_b_latency_c-1) = '1') and (wb_mem_b.cyc = '1') then
wb_mem_b.rdata <= ext_mem_b.rdata(ext_mem_b_latency_c-1);
wb_mem_b.ack <= '1';
else
wb_mem_b.rdata <= (others => '0');
wb_mem_b.ack <= '0';
end if;
end if;
end process ext_mem_b_access;
end generate;
generate_ext_dmem_false:
if (int_dmem_c = true) generate
wb_mem_b.rdata <= (others => '0');
wb_mem_b.ack <= '0';
wb_mem_b.err <= '0';
end generate;
xbus_interconnect: entity work.xbus_gateway
generic map (
-- device address size in bytes and base address --
DEV_0_SIZE => MEM_INT_IMEM_SIZE, DEV_0_BASE => mem_imem_base_c,
DEV_1_SIZE => MEM_INT_DMEM_SIZE, DEV_1_BASE => mem_dmem_base_c,
DEV_2_SIZE => 64, DEV_2_BASE => x"F0000000",
DEV_3_SIZE => 4, DEV_3_BASE => x"FF000000"
)
port map (
-- host port --
host_req_i => xbus_core_req,
host_rsp_o => xbus_core_rsp,
-- device ports --
dev_0_req_o => xbus_imem_req, dev_0_rsp_i => xbus_imem_rsp,
dev_1_req_o => xbus_dmem_req, dev_1_rsp_i => xbus_dmem_rsp,
dev_2_req_o => xbus_mmio_req, dev_2_rsp_i => xbus_mmio_rsp,
dev_3_req_o => xbus_trig_req, dev_3_rsp_i => xbus_trig_rsp
);
-- Wishbone Memory C (simulated external IO) ----------------------------------------------
-- XBUS: Instruction Memory ---------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
ext_mem_c_access: process(clk_gen)
begin
if rising_edge(clk_gen) then
-- control --
ext_mem_c.ack(0) <= wb_mem_c.cyc and wb_mem_c.stb; -- wishbone acknowledge
-- write access --
if ((wb_mem_c.cyc and wb_mem_c.stb and wb_mem_c.we) = '1') then -- valid write access
for i in 0 to 3 loop
if (wb_mem_c.sel(i) = '1') then
ext_ram_c(to_integer(unsigned(wb_mem_c.addr(index_size_f(ext_mem_c_size_c/4)+1 downto 2))))(7+i*8 downto 0+i*8) <= wb_mem_c.wdata(7+i*8 downto 0+i*8);
end if;
end loop; -- i
end if;
-- read access --
ext_mem_c.rdata(0) <= ext_ram_c(to_integer(unsigned(wb_mem_c.addr(index_size_f(ext_mem_c_size_c/4)+1 downto 2)))); -- word aligned
-- virtual read and ack latency --
if (ext_mem_c_latency_c > 1) then
for i in 1 to ext_mem_c_latency_c-1 loop
ext_mem_c.rdata(i) <= ext_mem_c.rdata(i-1);
ext_mem_c.ack(i) <= ext_mem_c.ack(i-1) and wb_mem_c.cyc;
end loop;
end if;
-- bus output register --
if (ext_mem_c.ack(ext_mem_c_latency_c-1) = '1') and (wb_mem_c.cyc = '1') then
wb_mem_c.rdata <= ext_mem_c.rdata(ext_mem_c_latency_c-1);
wb_mem_c.ack <= '1';
wb_mem_c.err <= '0';
else
wb_mem_c.rdata <= (others => '0');
wb_mem_c.ack <= '0';
wb_mem_c.err <= '0';
end if;
end if;
end process ext_mem_c_access;
xbus_imem: entity work.xbus_memory
generic map (
MEM_SIZE => MEM_INT_IMEM_SIZE,
MEM_LATE => 1
)
port map (
clk_i => clk_gen,
rstn_i => rst_gen,
xbus_req_i => xbus_imem_req,
xbus_rsp_o => xbus_imem_rsp
);
-- Wishbone IRQ Triggers ------------------------------------------------------------------
-- XBUS: Data Memory ----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
irq_trigger: process(rst_gen, clk_gen)
xbus_dmem: entity work.xbus_memory
generic map (
MEM_SIZE => MEM_INT_DMEM_SIZE,
MEM_LATE => 1
)
port map (
clk_i => clk_gen,
rstn_i => rst_gen,
xbus_req_i => xbus_dmem_req,
xbus_rsp_o => xbus_dmem_rsp
);
-- XBUS: Memory-Mapped IO -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
xbus_mmio: entity work.xbus_memory
generic map (
MEM_SIZE => 64,
MEM_LATE => 32
)
port map (
clk_i => clk_gen,
rstn_i => rst_gen,
xbus_req_i => xbus_mmio_req,
xbus_rsp_o => xbus_mmio_rsp
);
-- XBUS: IRQ Triggers ---------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
xbus_irq_trigger: process(rst_gen, clk_gen)
begin
if (rst_gen = '0') then
msi_ring <= '0';
mei_ring <= '0';
msi <= '0';
mti <= '0';
mei <= '0';
elsif rising_edge(clk_gen) then
-- bus interface --
wb_irq.rdata <= (others => '0');
wb_irq.ack <= wb_irq.cyc and wb_irq.stb and wb_irq.we and and_reduce_f(wb_irq.sel);
wb_irq.err <= '0';
-- bus response defaults --
xbus_trig_rsp.data <= (others => '0');
xbus_trig_rsp.ack <= '0';
xbus_trig_rsp.err <= '0';
-- trigger RISC-V platform IRQs --
if ((wb_irq.cyc and wb_irq.stb and wb_irq.we and and_reduce_f(wb_irq.sel)) = '1') then
msi_ring <= wb_irq.wdata(03); -- machine software interrupt
mei_ring <= wb_irq.wdata(11); -- machine software interrupt
if ((xbus_trig_req.cyc and xbus_trig_req.stb and xbus_trig_req.we and and_reduce_f(xbus_trig_req.sel)) = '1') then
xbus_trig_rsp.ack <= '1';
msi <= xbus_trig_req.data(03); -- machine software interrupt
mti <= xbus_trig_req.data(07); -- machine timer interrupt
mei <= xbus_trig_req.data(11); -- machine external interrupt
end if;
end if;
end process irq_trigger;
end process xbus_irq_trigger;
end neorv32_tb_rtl;

View file

@ -1,56 +0,0 @@
#!/usr/bin/env python3
import json
from pathlib import Path
from vunit import VUnit, VUnitCLI
cli = VUnitCLI()
cli.parser.add_argument(
"--ci-mode",
action="store_true",
default=False,
help="Enable special settings used by the CI",
)
args = cli.parse_args()
PRJ = VUnit.from_args(args=args)
PRJ.add_vhdl_builtins()
PRJ.add_com()
PRJ.add_verification_components()
PRJ.add_osvvm()
ROOT = Path(__file__).parent
NEORV32 = PRJ.add_library("neorv32")
NEORV32.add_source_files([
ROOT / "*.vhd",
ROOT / ".." / "rtl" / "**" / "*.vhd",
])
NEORV32.test_bench("neorv32_tb").set_generic("ci_mode", args.ci_mode)
PRJ.set_sim_option("disable_ieee_warnings", True)
PRJ.set_sim_option("ghdl.sim_flags", ["--max-stack-alloc=256"])
def _gen_vhdl_ls(vu):
"""
Generate the vhdl_ls.toml file required by VHDL-LS language server.
"""
# Repo root
parent = Path(__file__).parent.parent
proj = vu._project
libs = proj.get_libraries()
with open(parent / 'vhdl_ls.toml', "w") as f:
for lib in libs:
f.write(f"[libraries.{lib.name}]\n")
files = [str(file).replace('\\', '/') for file in lib._source_files
# Conflicts with *.default.vhd
if not any(exclude in file for exclude in ('neorv32_imem.simple.vhd', 'neorv32_imem.legacy.vhd', 'neorv32_dmem.legacy.vhd'))
]
f.write(f"files = {json.dumps(files, indent=4)}\n")
_gen_vhdl_ls(PRJ)
PRJ.main()

93
sim/sim_uart_rx.vhd Normal file
View file

@ -0,0 +1,93 @@
-- ================================================================================ --
-- NEORV32 - Simulation UART Receiver (print to simulator console) --
-- -------------------------------------------------------------------------------- --
-- 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;
use ieee.math_real.all;
use std.textio.all;
entity sim_uart_rx is
generic (
name : string; -- receiver name (for log file)
fclk : real; -- clock speed of clk_i in Hz
baud : real -- baud rate
);
port (
clk : in std_ulogic; -- global clock
rxd : in std_ulogic -- serial UART RX data
);
end entity sim_uart_rx;
architecture sim_uart_rx_rtl of sim_uart_rx is
signal sync : std_ulogic_vector(4 downto 0) := (others => '1');
signal busy : std_ulogic := '0';
signal sreg : std_ulogic_vector(8 downto 0) := (others => '0');
signal baudcnt : real;
signal bitcnt : natural;
constant baud_val_c : real := fclk / baud;
file file_out : text open write_mode is "neorv32_tb." & name & "_rx.out";
begin
sim_receiver: process(clk)
variable c : integer;
variable l : line;
begin
if rising_edge(clk) then
-- synchronizer --
sync <= sync(3 downto 0) & rxd;
-- arbiter --
if (busy = '0') then -- idle
busy <= '0';
baudcnt <= round(0.5 * baud_val_c);
bitcnt <= 9;
if (sync(4 downto 1) = "1100") then -- start bit (falling edge)
busy <= '1';
end if;
else -- receiving
if (baudcnt <= 0.0) then
if (bitcnt = 1) then
baudcnt <= round(0.5 * baud_val_c);
else
baudcnt <= round(baud_val_c);
end if;
if (bitcnt = 0) then
busy <= '0'; -- done
c := to_integer(unsigned(sreg(8 downto 1)));
if (c < 32) or (c > 32+95) then -- non-printable character?
report name & ".rx: (" & integer'image(c) & ")";
else
report name & ".rx: " & character'val(c);
end if;
if (c = 10) then -- LF line break
writeline(file_out, l);
elsif (c /= 13) then -- no additional CR
write(l, character'val(c));
end if;
else
sreg <= sync(4) & sreg(8 downto 1);
bitcnt <= bitcnt - 1;
end if;
else
baudcnt <= baudcnt - 1.0;
end if;
end if;
end if;
end process sim_receiver;
end architecture sim_uart_rx_rtl;

View file

@ -1,44 +0,0 @@
#!/usr/bin/env bash
set -e
cd $(dirname "$0")
echo "[TIP] Compile application with USER_FLAGS+=-DUART[0/1]_SIM_MODE to enable UART[0/1]'s simulation mode (redirect UART output to simulator console)."
# Prepare simulation output files for UART0 and UART 1
# - Testbench receiver log file (neorv32.testbench_uart?.out)
# - Direct simulation output (neorv32.uart?.sim_mode.text.out)
for uart in 0 1; do
for item in \
testbench_uart"$uart" \
uart"$uart".sim_mode.text; do
touch neorv32."$item".out
chmod 777 neorv32."$item".out
done
done
GHDL="${GHDL:-ghdl}"
$GHDL -m --work=neorv32 --workdir=build neorv32_tb_simple
if [ -z "$1" ]
then
GHDL_RUN_ARGS="${@:---stop-time=10ms}"
else
# Lets pass down all the parameters to GHDL instead of just 1
GHDL_RUN_ARGS=$@
fi
echo "Using simulation run arguments: $GHDL_RUN_ARGS";
runcmd="$GHDL -r --work=neorv32 --workdir=build neorv32_tb_simple \
--max-stack-alloc=0 \
--ieee-asserts=disable \
--assert-level=error $GHDL_RUN_ARGS"
if [ -n "$GHDL_DEVNULL" ]; then
$runcmd >> /dev/null
else
$runcmd
fi

View file

@ -1,20 +0,0 @@
#!/usr/bin/env bash
set -e
cd $(dirname "$0")
NEORV32_LOCAL_RTL=${NEORV32_LOCAL_RTL:-../../rtl}
FILE_LIST=`cat $NEORV32_LOCAL_RTL/file_list_soc.f`
CORE_SRCS="${FILE_LIST//NEORV32_RTL_PATH_PLACEHOLDER/"$NEORV32_LOCAL_RTL"}"
mkdir -p build
ghdl -i --work=neorv32 --workdir=build \
$CORE_SRCS \
"$NEORV32_LOCAL_RTL"/processor_templates/*.vhd \
"$NEORV32_LOCAL_RTL"/system_integration/*.vhd \
"$NEORV32_LOCAL_RTL"/test_setups/*.vhd \
neorv32_tb.simple.vhd \
uart_rx.simple.vhd

View file

@ -1,10 +0,0 @@
#!/usr/bin/env bash
# Abort if any command returns != 0
set -e
cd $(dirname "$0")
./ghdl.setup.sh
# We want to be able to pass down more than 1 parameter to GHDL
./ghdl.run.sh $@

View file

@ -1,605 +0,0 @@
-- ================================================================================ --
-- NEORV32 - Default Processor Testbench --
-- -------------------------------------------------------------------------------- --
-- 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;
use ieee.math_real.all;
library neorv32;
use neorv32.neorv32_package.all;
use neorv32.neorv32_application_image.all; -- this file is generated by the image generator
use std.textio.all;
entity neorv32_tb_simple is
generic (
PERFORMANCE_OPTION : natural := 0 -- Set core options for performance measurements
);
end neorv32_tb_simple;
architecture neorv32_tb_simple_rtl of neorv32_tb_simple is
-- advanced configuration --
constant num_configs_c : natural := 3; -- number of pre-defined configurations
-- helpers --
type bool_t is array (0 to num_configs_c-1) of boolean;
type natural_t is array (0 to num_configs_c-1) of natural;
type performance_options_type_t is record
fast_mul_en_c : bool_t;
fast_shift_en_c : bool_t;
imem_size_c : natural_t;
icache_en_c : bool_t;
icache_block_size_c : natural_t;
dcache_en_c : bool_t;
dcache_block_size_c : natural_t;
end record;
-- User Configuration ---------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
-- core performance options --
constant performance_options_c : performance_options_type_t := (
-- default fast core area core
fast_mul_en_c => ( true, true, false), -- Fast multiplication, more area
fast_shift_en_c => ( true, true, false), -- Fast shifting, more area
imem_size_c => ( 32*1024, 128*1024, 128*1024), -- Instruction memory size min. 128kB for performance tests
icache_en_c => ( true, false, false), -- I$ disabled for performance tests
icache_block_size_c => ( 32, 32, 32), -- I$ block size
dcache_en_c => ( true, false, false), -- D$ disabled for performance tests
dcache_block_size_c => ( 32, 32, 32) -- D$ block size
);
-- general --
constant int_imem_c : boolean := true; -- true: use proc-internal IMEM, false: use external simulated IMEM (ext. mem A)
constant int_dmem_c : boolean := true; -- true: use proc-internal DMEM, false: use external simulated DMEM (ext. mem B)
constant dmem_size_c : natural := 8*1024; -- size in bytes of processor-internal DMEM / external mem B
constant f_clock_c : natural := 100000000; -- main clock in Hz
constant baud0_rate_c : natural := 19200; -- simulation UART0 (primary UART) baud rate
constant baud1_rate_c : natural := 19200; -- simulation UART1 (secondary UART) baud rate
-- simulated external Wishbone memory A (can be used as external IMEM) --
constant ext_mem_a_base_addr_c : std_ulogic_vector(31 downto 0) := x"00000000"; -- wishbone memory base address (external IMEM base)
constant ext_mem_a_size_c : natural := performance_options_c.imem_size_c(PERFORMANCE_OPTION); -- wishbone memory size in bytes
constant ext_mem_a_latency_c : natural := 8; -- latency in clock cycles (min 1, max 255), plus 1 cycle initial delay
-- simulated external Wishbone memory B (can be used as external DMEM) --
constant ext_mem_b_base_addr_c : std_ulogic_vector(31 downto 0) := x"80000000"; -- wishbone memory base address (external DMEM base)
constant ext_mem_b_size_c : natural := dmem_size_c; -- wishbone memory size in bytes
constant ext_mem_b_latency_c : natural := 8; -- latency in clock cycles (min 1, max 255), plus 1 cycle initial delay
-- simulated external Wishbone memory C (can be used to simulate external IO access) --
constant ext_mem_c_base_addr_c : std_ulogic_vector(31 downto 0) := x"F0000000"; -- wishbone memory base address (default begin of EXTERNAL IO area)
constant ext_mem_c_size_c : natural := performance_options_c.icache_block_size_c(PERFORMANCE_OPTION)/2; -- wishbone memory size in bytes, should be smaller than an iCACHE block
constant ext_mem_c_latency_c : natural := 128; -- latency in clock cycles (min 1, max 255), plus 1 cycle initial delay
-- simulation interrupt trigger --
constant irq_trigger_base_addr_c : std_ulogic_vector(31 downto 0) := x"FF000000";
-- -------------------------------------------------------------------------------------------
-- internals - hands off! --
constant uart0_baud_val_c : real := real(f_clock_c) / real(baud0_rate_c);
constant uart1_baud_val_c : real := real(f_clock_c) / real(baud1_rate_c);
constant t_clock_c : time := (1 sec) / f_clock_c;
-- generators --
signal clk_gen, rst_gen : std_ulogic := '0';
-- text.io --
file file_uart0_tx_out : text open write_mode is "neorv32.testbench_uart0.out";
-- uart --
signal uart0_txd, uart1_txd : std_ulogic;
signal uart0_cts, uart1_cts : std_ulogic;
-- gpio --
signal gpio : std_ulogic_vector(63 downto 0);
-- twi --
signal twi_scl, twi_sda : std_logic;
signal twi_scl_i, twi_scl_o, twi_sda_i, twi_sda_o : std_ulogic;
-- 1-wire --
signal onewire : std_logic;
signal onewire_i, onewire_o : std_ulogic;
-- spi & sdi --
signal spi_csn: std_ulogic_vector(7 downto 0);
signal spi_di, spi_do, spi_clk : std_ulogic;
signal sdi_di, sdi_do, sdi_clk, sdi_csn : std_ulogic;
-- irq --
signal msi_ring, mei_ring : std_ulogic;
-- SLINK echo --
signal slink_dat : std_ulogic_vector(31 downto 0);
signal slink_val : std_ulogic;
signal slink_lst : std_ulogic;
signal slink_rdy : std_ulogic;
signal slink_id : std_ulogic_vector(3 downto 0);
-- Wishbone bus --
type wishbone_t is record
addr : std_ulogic_vector(31 downto 0); -- address
wdata : std_ulogic_vector(31 downto 0); -- master write data
rdata : std_ulogic_vector(31 downto 0); -- master read data
tag : std_ulogic_vector(2 downto 0); -- access tag
we : std_ulogic; -- write enable
sel : std_ulogic_vector(3 downto 0); -- byte enable
stb : std_ulogic; -- strobe
cyc : std_ulogic; -- valid cycle
ack : std_ulogic; -- transfer acknowledge
err : std_ulogic; -- transfer error
end record;
signal wb_cpu, wb_mem_a, wb_mem_b, wb_mem_c, wb_irq : wishbone_t;
-- Wishbone access latency type --
type ext_mem_read_latency_t is array (0 to 255) of std_ulogic_vector(31 downto 0);
-- simulated external memory c (IO) --
signal ext_ram_c : mem32_t(0 to ext_mem_c_size_c/4-1); -- uninitialized, used to simulate external IO
-- simulated external memory bus feedback type --
type ext_mem_t is record
rdata : ext_mem_read_latency_t;
acc_en : std_ulogic;
ack : std_ulogic_vector(255 downto 0);
end record;
signal ext_mem_a, ext_mem_b, ext_mem_c : ext_mem_t;
begin
-- Clock/Reset Generator ------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
clk_gen <= not clk_gen after (t_clock_c/2);
rst_gen <= '0', '1' after 60*(t_clock_c/2);
-- The Core of the Problem ----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_top_inst: neorv32_top
generic map (
-- General --
CLOCK_FREQUENCY => f_clock_c, -- clock frequency of clk_i in Hz
CLOCK_GATING_EN => true, -- enable clock gating when in sleep mode
HART_ID => x"00000000", -- hardware thread ID
JEDEC_ID => "00000000000", -- vendor's JEDEC ID
INT_BOOTLOADER_EN => false, -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM
-- On-Chip Debugger (OCD) --
OCD_EN => true, -- implement on-chip debugger
OCD_AUTHENTICATION => true, -- implement on-chip debugger authentication
-- RISC-V CPU Extensions --
RISCV_ISA_C => false, -- implement compressed extension?
RISCV_ISA_E => false, -- implement embedded RF extension?
RISCV_ISA_M => true, -- implement mul/div extension?
RISCV_ISA_U => true, -- implement user mode extension?
RISCV_ISA_Zalrsc => true, -- implement atomic reservation-set extension
RISCV_ISA_Zba => true, -- implement shifted-add bit-manipulation extension
RISCV_ISA_Zbb => true, -- implement basic bit-manipulation extension
RISCV_ISA_Zbkb => true, -- implement bit-manipulation instructions for cryptography
RISCV_ISA_Zbkc => true, -- implement carry-less multiplication instructions?
RISCV_ISA_Zbkx => true, -- implement cryptography crossbar permutation extension?
RISCV_ISA_Zbs => true, -- implement single-bit bit-manipulation extension
RISCV_ISA_Zfinx => true, -- implement 32-bit floating-point extension (using INT reg!)
RISCV_ISA_Zicntr => true, -- implement base counters?
RISCV_ISA_Zicond => true, -- implement integer conditional operations?
RISCV_ISA_Zihpm => true, -- implement hardware performance monitors?
RISCV_ISA_Zknd => true, -- implement cryptography NIST AES decryption extension?
RISCV_ISA_Zkne => true, -- implement cryptography NIST AES encryption extension?
RISCV_ISA_Zknh => true, -- implement cryptography NIST hash extension?
RISCV_ISA_Zksed => true, -- implement ShangMi block cypher extension?
RISCV_ISA_Zksh => true, -- implement ShangMi hash extension?
RISCV_ISA_Zmmul => false, -- implement multiply-only M sub-extension?
RISCV_ISA_Zxcfu => true, -- implement custom (instr.) functions unit?
-- Extension Options --
FAST_MUL_EN => performance_options_c.fast_mul_en_c(PERFORMANCE_OPTION), -- use DSPs for M extension's multiplier
FAST_SHIFT_EN => performance_options_c.fast_shift_en_c(PERFORMANCE_OPTION), -- use barrel shifter for shift operations
REGFILE_HW_RST => false, -- no hardware reset
-- Physical Memory Protection (PMP) --
PMP_NUM_REGIONS => 5, -- number of regions (0..16)
PMP_MIN_GRANULARITY => 4, -- minimal region granularity in bytes, has to be a power of 2, min 4 bytes
PMP_TOR_MODE_EN => true, -- implement TOR mode
PMP_NAP_MODE_EN => true, -- implement NAPOT/NA4 mode
-- Hardware Performance Monitors (HPM) --
HPM_NUM_CNTS => 12, -- number of implemented HPM counters (0..29)
HPM_CNT_WIDTH => 40, -- total size of HPM counters (0..64)
-- Internal Instruction memory --
MEM_INT_IMEM_EN => int_imem_c , -- implement processor-internal instruction memory
MEM_INT_IMEM_SIZE => performance_options_c.imem_size_c(PERFORMANCE_OPTION), -- size of processor-internal instruction memory in bytes
-- Internal Data memory --
MEM_INT_DMEM_EN => int_dmem_c, -- implement processor-internal data memory
MEM_INT_DMEM_SIZE => dmem_size_c, -- size of processor-internal data memory in bytes
-- Internal Cache memory --
ICACHE_EN => performance_options_c.icache_en_c(PERFORMANCE_OPTION), -- implement instruction cache
ICACHE_NUM_BLOCKS => 64, -- i-cache: number of blocks (min 2), has to be a power of 2
ICACHE_BLOCK_SIZE => performance_options_c.icache_block_size_c(PERFORMANCE_OPTION), -- i-cache: block size in bytes (min 4), has to be a power of 2
-- Internal Data Cache (dCACHE) --
DCACHE_EN => performance_options_c.dcache_en_c(PERFORMANCE_OPTION), -- implement data cache
DCACHE_NUM_BLOCKS => 32, -- d-cache: number of blocks (min 1), has to be a power of 2
DCACHE_BLOCK_SIZE => performance_options_c.dcache_block_size_c(PERFORMANCE_OPTION), -- d-cache: block size in bytes (min 4), has to be a power of 2
-- External bus interface --
XBUS_EN => true, -- implement external memory bus interface?
XBUS_TIMEOUT => 256, -- cycles after a pending bus access auto-terminates (0 = disabled)
XBUS_REGSTAGE_EN => true, -- add register stage
XBUS_CACHE_EN => true, -- enable external bus cache (x-cache)
XBUS_CACHE_NUM_BLOCKS => 4, -- x-cache: number of blocks (min 1), has to be a power of 2
XBUS_CACHE_BLOCK_SIZE => 32, -- x-cache: block size in bytes (min 4), has to be a power of 2
-- Execute in-place module (XIP) --
XIP_EN => true, -- implement execute in place module (XIP)?
XIP_CACHE_EN => true, -- implement XIP cache?
XIP_CACHE_NUM_BLOCKS => 4, -- number of blocks (min 1), has to be a power of 2
XIP_CACHE_BLOCK_SIZE => 256, -- block size in bytes (min 4), has to be a power of 2
-- External Interrupts Controller (XIRQ) --
XIRQ_NUM_CH => 32, -- number of external IRQ channels (0..32)
-- Processor peripherals --
IO_GPIO_NUM => 64, -- number of GPIO input/output pairs (0..64)
IO_MTIME_EN => true, -- implement machine system timer (MTIME)?
IO_UART0_EN => true, -- implement primary universal asynchronous receiver/transmitter (UART0)?
IO_UART0_RX_FIFO => 32, -- RX fifo depth, has to be a power of two, min 1
IO_UART0_TX_FIFO => 32, -- TX fifo depth, has to be a power of two, min 1
IO_UART1_EN => true, -- implement secondary universal asynchronous receiver/transmitter (UART1)?
IO_UART1_RX_FIFO => 1, -- RX fifo depth, has to be a power of two, min 1
IO_UART1_TX_FIFO => 1, -- TX fifo depth, has to be a power of two, min 1
IO_SPI_EN => true, -- implement serial peripheral interface (SPI)?
IO_SPI_FIFO => 4, -- SPI RTX fifo depth, has to be zero or a power of two
IO_SDI_EN => true, -- implement serial data interface (SDI)?
IO_SDI_FIFO => 4, -- SDI RTX fifo depth, has to be zero or a power of two
IO_TWI_EN => true, -- implement two-wire interface (TWI)?
IO_TWI_FIFO => 4, -- RTX fifo depth, has to be zero or a power of two, min 1
IO_PWM_NUM_CH => 8, -- number of PWM channels to implement (0..16)
IO_WDT_EN => true, -- implement watch dog timer (WDT)?
IO_TRNG_EN => true, -- implement true random number generator (TRNG)?
IO_TRNG_FIFO => 4, -- TRNG fifo depth, has to be a power of two, min 1
IO_CFS_EN => true, -- implement custom functions subsystem (CFS)?
IO_CFS_CONFIG => (others => '0'), -- custom CFS configuration generic
IO_CFS_IN_SIZE => 32, -- size of CFS input conduit in bits
IO_CFS_OUT_SIZE => 32, -- size of CFS output conduit in bits
IO_NEOLED_EN => true, -- implement NeoPixel-compatible smart LED interface (NEOLED)?
IO_NEOLED_TX_FIFO => 8, -- NEOLED TX FIFO depth, 1..32k, has to be a power of two
IO_GPTMR_EN => true, -- implement general purpose timer (GPTMR)?
IO_ONEWIRE_EN => true, -- implement 1-wire interface (ONEWIRE)?
IO_DMA_EN => true, -- implement direct memory access controller (DMA)?
IO_SLINK_EN => true, -- implement stream link interface (SLINK)?
IO_SLINK_RX_FIFO => 4, -- RX fifo depth, has to be a power of two, min 1
IO_SLINK_TX_FIFO => 4, -- TX fifo depth, has to be a power of two, min 1
IO_CRC_EN => true -- implement cyclic redundancy check unit (CRC)?
)
port map (
-- Global control --
clk_i => clk_gen, -- global clock, rising edge
rstn_i => rst_gen, -- global reset, low-active, async
-- JTAG on-chip debugger interface (available if OCD_EN = true) --
jtag_tck_i => '0', -- serial clock
jtag_tdi_i => '0', -- serial data input
jtag_tdo_o => open, -- serial data output
jtag_tms_i => '0', -- mode select
-- External bus interface (available if XBUS_EN = true) --
xbus_adr_o => wb_cpu.addr, -- address
xbus_dat_o => wb_cpu.wdata, -- write data
xbus_tag_o => wb_cpu.tag, -- access tag
xbus_we_o => wb_cpu.we, -- read/write
xbus_sel_o => wb_cpu.sel, -- byte enable
xbus_stb_o => wb_cpu.stb, -- strobe
xbus_cyc_o => wb_cpu.cyc, -- valid cycle
xbus_dat_i => wb_cpu.rdata, -- read data
xbus_ack_i => wb_cpu.ack, -- transfer acknowledge
xbus_err_i => wb_cpu.err, -- transfer error
-- Stream Link Interface (available if IO_SLINK_EN = true) --
slink_rx_dat_i => slink_dat, -- RX input data
slink_rx_src_i => slink_id, -- RX source routing information
slink_rx_val_i => slink_val, -- RX valid input
slink_rx_lst_i => slink_lst, -- RX last element of stream
slink_rx_rdy_o => slink_rdy, -- RX ready to receive
slink_tx_dat_o => slink_dat, -- TX output data
slink_tx_dst_o => slink_id, -- TX destination routing information
slink_tx_val_o => slink_val, -- TX valid output
slink_tx_lst_o => slink_lst, -- TX last element of stream
slink_tx_rdy_i => slink_rdy, -- TX ready to send
-- XIP (execute in place via SPI) signals (available if XIP_EN = true) --
xip_csn_o => open, -- chip-select, low-active
xip_clk_o => open, -- serial clock
xip_dat_i => '0', -- device data input
xip_dat_o => open, -- controller data output
-- GPIO (available if IO_GPIO_NUM > true) --
gpio_o => gpio, -- parallel output
gpio_i => gpio, -- parallel input
-- primary UART0 (available if IO_UART0_EN = true) --
uart0_txd_o => uart0_txd, -- UART0 send data
uart0_rxd_i => uart0_txd, -- UART0 receive data
uart0_rts_o => uart1_cts, -- HW flow control: UART0.RX ready to receive ("RTR"), low-active, optional
uart0_cts_i => uart0_cts, -- HW flow control: UART0.TX allowed to transmit, low-active, optional
-- secondary UART1 (available if IO_UART1_EN = true) --
uart1_txd_o => uart1_txd, -- UART1 send data
uart1_rxd_i => uart1_txd, -- UART1 receive data
uart1_rts_o => uart0_cts, -- HW flow control: UART0.RX ready to receive ("RTR"), low-active, optional
uart1_cts_i => uart1_cts, -- HW flow control: UART0.TX allowed to transmit, low-active, optional
-- SPI (available if IO_SPI_EN = true) --
spi_clk_o => spi_clk, -- SPI serial clock
spi_dat_o => spi_do, -- controller data out, peripheral data in
spi_dat_i => spi_di, -- controller data in, peripheral data out
spi_csn_o => spi_csn, -- SPI CS
-- SDI (available if IO_SDI_EN = true) --
sdi_clk_i => sdi_clk, -- SDI serial clock
sdi_dat_o => sdi_do, -- controller data out, peripheral data in
sdi_dat_i => sdi_di, -- controller data in, peripheral data out
sdi_csn_i => sdi_csn, -- chip-select
-- TWI (available if IO_TWI_EN = true) --
twi_sda_i => twi_sda_i, -- serial data line sense input
twi_sda_o => twi_sda_o, -- serial data line output (pull low only)
twi_scl_i => twi_scl_i, -- serial clock line sense input
twi_scl_o => twi_scl_o, -- serial clock line output (pull low only)
-- 1-Wire Interface (available if IO_ONEWIRE_EN = true) --
onewire_i => onewire_i, -- 1-wire bus sense input
onewire_o => onewire_o, -- 1-wire bus output (pull low only)
-- PWM (available if IO_PWM_NUM_CH > 0) --
pwm_o => open, -- pwm channels
-- Custom Functions Subsystem IO --
cfs_in_i => (others => '0'), -- custom CFS inputs
cfs_out_o => open, -- custom CFS outputs
-- NeoPixel-compatible smart LED interface (available if IO_NEOLED_EN = true) --
neoled_o => open, -- async serial data line
-- Machine timer system time (available if IO_MTIME_EN = true) --
mtime_time_o => open,
-- External platform interrupts (available if XIRQ_NUM_CH > 0) --
xirq_i => gpio(31 downto 0), -- IRQ channels
-- CPU Interrupts --
mtime_irq_i => '0', -- machine software interrupt, available if IO_MTIME_EN = false
msw_irq_i => msi_ring, -- machine software interrupt
mext_irq_i => mei_ring -- machine external interrupt
);
-- TWI tri-state driver --
twi_sda <= '0' when (twi_sda_o = '0') else 'Z'; -- module can only pull the line low actively
twi_scl <= '0' when (twi_scl_o = '0') else 'Z';
twi_sda_i <= std_ulogic(twi_sda);
twi_scl_i <= std_ulogic(twi_scl);
-- 1-Wire tri-state driver --
onewire <= '0' when (onewire_o = '0') else 'Z'; -- module can only pull the line low actively
onewire_i <= std_ulogic(onewire);
-- TWI termination (pull-ups) --
twi_scl <= 'H';
twi_sda <= 'H';
-- 1-Wire termination (pull-up) --
onewire <= 'H';
-- SPI/SDI echo with propagation delay --
sdi_clk <= spi_clk after 40 ns;
sdi_csn <= spi_csn(7) after 40 ns;
sdi_di <= spi_do after 40 ns;
spi_di <= sdi_do when (spi_csn(7) = '0') else spi_do after 40 ns;
-- UART Simulation Receiver ---------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
uart0_checker: entity work.uart_rx_simple
generic map (
name => "uart0",
uart_baud_val_c => uart0_baud_val_c
)
port map (
clk => clk_gen,
uart_txd => uart0_txd
);
uart1_checker: entity work.uart_rx_simple
generic map (
name => "uart1",
uart_baud_val_c => uart1_baud_val_c
)
port map (
clk => clk_gen,
uart_txd => uart1_txd
);
-- Wishbone Fabric ------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
-- CPU broadcast signals --
wb_mem_a.addr <= wb_cpu.addr;
wb_mem_a.wdata <= wb_cpu.wdata;
wb_mem_a.we <= wb_cpu.we;
wb_mem_a.sel <= wb_cpu.sel;
wb_mem_a.cyc <= wb_cpu.cyc;
wb_mem_b.addr <= wb_cpu.addr;
wb_mem_b.wdata <= wb_cpu.wdata;
wb_mem_b.we <= wb_cpu.we;
wb_mem_b.sel <= wb_cpu.sel;
wb_mem_b.cyc <= wb_cpu.cyc;
wb_mem_c.addr <= wb_cpu.addr;
wb_mem_c.wdata <= wb_cpu.wdata;
wb_mem_c.we <= wb_cpu.we;
wb_mem_c.sel <= wb_cpu.sel;
wb_mem_c.cyc <= wb_cpu.cyc;
wb_irq.addr <= wb_cpu.addr;
wb_irq.wdata <= wb_cpu.wdata;
wb_irq.we <= wb_cpu.we;
wb_irq.sel <= wb_cpu.sel;
wb_irq.cyc <= wb_cpu.cyc;
-- CPU read-back signals (no mux here since peripherals have "output gates") --
wb_cpu.rdata <= wb_mem_a.rdata or wb_mem_b.rdata or wb_mem_c.rdata or wb_irq.rdata;
wb_cpu.ack <= wb_mem_a.ack or wb_mem_b.ack or wb_mem_c.ack or wb_irq.ack;
wb_cpu.err <= wb_mem_a.err or wb_mem_b.err or wb_mem_c.err or wb_irq.err;
-- peripheral select via STROBE signal --
wb_mem_a.stb <= wb_cpu.stb when (wb_cpu.addr >= ext_mem_a_base_addr_c) and (wb_cpu.addr < std_ulogic_vector(unsigned(ext_mem_a_base_addr_c) + ext_mem_a_size_c)) else '0';
wb_mem_b.stb <= wb_cpu.stb when (wb_cpu.addr >= ext_mem_b_base_addr_c) and (wb_cpu.addr < std_ulogic_vector(unsigned(ext_mem_b_base_addr_c) + ext_mem_b_size_c)) else '0';
wb_mem_c.stb <= wb_cpu.stb when (wb_cpu.addr >= ext_mem_c_base_addr_c) and (wb_cpu.addr < std_ulogic_vector(unsigned(ext_mem_c_base_addr_c) + ext_mem_c_size_c)) else '0';
wb_irq.stb <= wb_cpu.stb when (wb_cpu.addr = irq_trigger_base_addr_c) else '0';
-- Wishbone Memory A (simulated external IMEM) --------------------------------------------
-- -------------------------------------------------------------------------------------------
generate_ext_imem:
if (int_imem_c = false) generate
ext_mem_a_access: process(clk_gen)
variable ext_ram_a : mem32_t(0 to ext_mem_a_size_c/4-1) := mem32_init_f(application_init_image, ext_mem_a_size_c/4); -- initialized, used to simulate external IMEM
begin
if rising_edge(clk_gen) then
-- control --
ext_mem_a.ack(0) <= wb_mem_a.cyc and wb_mem_a.stb; -- wishbone acknowledge
-- write access --
if ((wb_mem_a.cyc and wb_mem_a.stb and wb_mem_a.we) = '1') then -- valid write access
for i in 0 to 3 loop
if (wb_mem_a.sel(i) = '1') then
ext_ram_a(to_integer(unsigned(wb_mem_a.addr(index_size_f(ext_mem_a_size_c/4)+1 downto 2))))(7+i*8 downto 0+i*8) := wb_mem_a.wdata(7+i*8 downto 0+i*8);
end if;
end loop; -- i
end if;
-- read access --
ext_mem_a.rdata(0) <= ext_ram_a(to_integer(unsigned(wb_mem_a.addr(index_size_f(ext_mem_a_size_c/4)+1 downto 2)))); -- word aligned
-- virtual read and ack latency --
if (ext_mem_a_latency_c > 1) then
for i in 1 to ext_mem_a_latency_c-1 loop
ext_mem_a.rdata(i) <= ext_mem_a.rdata(i-1);
ext_mem_a.ack(i) <= ext_mem_a.ack(i-1) and wb_mem_a.cyc;
end loop;
end if;
-- bus output register --
wb_mem_a.err <= '0';
if (ext_mem_a.ack(ext_mem_a_latency_c-1) = '1') and (wb_mem_a.cyc = '1') then
wb_mem_a.rdata <= ext_mem_a.rdata(ext_mem_a_latency_c-1);
wb_mem_a.ack <= '1';
else
wb_mem_a.rdata <= (others => '0');
wb_mem_a.ack <= '0';
end if;
end if;
end process ext_mem_a_access;
end generate;
generate_ext_imem_false:
if (int_imem_c = true) generate
wb_mem_a.rdata <= (others => '0');
wb_mem_a.ack <= '0';
wb_mem_a.err <= '0';
end generate;
-- Wishbone Memory B (simulated external DMEM) --------------------------------------------
-- -------------------------------------------------------------------------------------------
generate_ext_dmem:
if (int_dmem_c = false) generate
ext_mem_b_access: process(clk_gen)
variable ext_ram_b : mem32_t(0 to ext_mem_b_size_c/4-1) := (others => (others => '0')); -- zero, used to simulate external DMEM
begin
if rising_edge(clk_gen) then
-- control --
ext_mem_b.ack(0) <= wb_mem_b.cyc and wb_mem_b.stb; -- wishbone acknowledge
-- write access --
if ((wb_mem_b.cyc and wb_mem_b.stb and wb_mem_b.we) = '1') then -- valid write access
for i in 0 to 3 loop
if (wb_mem_b.sel(i) = '1') then
ext_ram_b(to_integer(unsigned(wb_mem_b.addr(index_size_f(ext_mem_b_size_c/4)+1 downto 2))))(7+i*8 downto 0+i*8) := wb_mem_b.wdata(7+i*8 downto 0+i*8);
end if;
end loop; -- i
end if;
-- read access --
ext_mem_b.rdata(0) <= ext_ram_b(to_integer(unsigned(wb_mem_b.addr(index_size_f(ext_mem_b_size_c/4)+1 downto 2)))); -- word aligned
-- virtual read and ack latency --
if (ext_mem_b_latency_c > 1) then
for i in 1 to ext_mem_b_latency_c-1 loop
ext_mem_b.rdata(i) <= ext_mem_b.rdata(i-1);
ext_mem_b.ack(i) <= ext_mem_b.ack(i-1) and wb_mem_b.cyc;
end loop;
end if;
-- bus output register --
wb_mem_b.err <= '0';
if (ext_mem_b.ack(ext_mem_b_latency_c-1) = '1') and (wb_mem_b.cyc = '1') then
wb_mem_b.rdata <= ext_mem_b.rdata(ext_mem_b_latency_c-1);
wb_mem_b.ack <= '1';
else
wb_mem_b.rdata <= (others => '0');
wb_mem_b.ack <= '0';
end if;
end if;
end process ext_mem_b_access;
end generate;
generate_ext_dmem_false:
if (int_dmem_c = true) generate
wb_mem_b.rdata <= (others => '0');
wb_mem_b.ack <= '0';
wb_mem_b.err <= '0';
end generate;
-- Wishbone Memory C (simulated external IO) ----------------------------------------------
-- -------------------------------------------------------------------------------------------
ext_mem_c_access: process(clk_gen)
begin
if rising_edge(clk_gen) then
-- control --
ext_mem_c.ack(0) <= wb_mem_c.cyc and wb_mem_c.stb; -- wishbone acknowledge
-- write access --
if ((wb_mem_c.cyc and wb_mem_c.stb and wb_mem_c.we) = '1') then -- valid write access
for i in 0 to 3 loop
if (wb_mem_c.sel(i) = '1') then
ext_ram_c(to_integer(unsigned(wb_mem_c.addr(index_size_f(ext_mem_c_size_c/4)+1 downto 2))))(7+i*8 downto 0+i*8) <= wb_mem_c.wdata(7+i*8 downto 0+i*8);
end if;
end loop; -- i
end if;
-- read access --
ext_mem_c.rdata(0) <= ext_ram_c(to_integer(unsigned(wb_mem_c.addr(index_size_f(ext_mem_c_size_c/4)+1 downto 2)))); -- word aligned
-- virtual read and ack latency --
if (ext_mem_c_latency_c > 1) then
for i in 1 to ext_mem_c_latency_c-1 loop
ext_mem_c.rdata(i) <= ext_mem_c.rdata(i-1);
ext_mem_c.ack(i) <= ext_mem_c.ack(i-1) and wb_mem_c.cyc;
end loop;
end if;
-- bus output register --
if (ext_mem_c.ack(ext_mem_c_latency_c-1) = '1') and (wb_mem_c.cyc = '1') then
wb_mem_c.rdata <= ext_mem_c.rdata(ext_mem_c_latency_c-1);
wb_mem_c.ack <= '1';
wb_mem_c.err <= '0';
else
wb_mem_c.rdata <= (others => '0');
wb_mem_c.ack <= '0';
wb_mem_c.err <= '0';
end if;
end if;
end process ext_mem_c_access;
-- Wishbone IRQ Triggers ------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
irq_trigger: process(rst_gen, clk_gen)
begin
if (rst_gen = '0') then
msi_ring <= '0';
mei_ring <= '0';
elsif rising_edge(clk_gen) then
-- bus interface --
wb_irq.rdata <= (others => '0');
wb_irq.ack <= wb_irq.cyc and wb_irq.stb and wb_irq.we and and_reduce_f(wb_irq.sel);
wb_irq.err <= '0';
-- trigger RISC-V platform IRQs --
if ((wb_irq.cyc and wb_irq.stb and wb_irq.we and and_reduce_f(wb_irq.sel)) = '1') then
msi_ring <= wb_irq.wdata(03); -- machine software interrupt
mei_ring <= wb_irq.wdata(11); -- machine software interrupt
end if;
end if;
end process irq_trigger;
end neorv32_tb_simple_rtl;

View file

@ -1,77 +0,0 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
use std.textio.all;
entity uart_rx_simple is
generic (
name : string;
uart_baud_val_c : real);
port (
clk : in std_ulogic;
uart_txd : in std_ulogic
);
end entity;
architecture a of uart_rx_simple is
signal uart_rx_sync : std_ulogic_vector(04 downto 0) := (others => '1');
signal uart_rx_busy : std_ulogic := '0';
signal uart_rx_sreg : std_ulogic_vector(08 downto 0) := (others => '0');
signal uart_rx_baud_cnt : real;
signal uart_rx_bitcnt : natural;
file file_uart_tx_out : text open write_mode is "neorv32.testbench_" & name & ".out";
begin
uart_rx_console : process(clk)
variable i : integer;
variable l : line;
begin
-- "UART" --
if rising_edge(clk) then
-- synchronizer --
uart_rx_sync <= uart_rx_sync(3 downto 0) & uart_txd;
-- arbiter --
if (uart_rx_busy = '0') then -- idle
uart_rx_busy <= '0';
uart_rx_baud_cnt <= round(0.5 * uart_baud_val_c);
uart_rx_bitcnt <= 9;
if (uart_rx_sync(4 downto 1) = "1100") then -- start bit? (falling edge)
uart_rx_busy <= '1';
end if;
else
if (uart_rx_baud_cnt <= 0.0) then
if (uart_rx_bitcnt = 1) then
uart_rx_baud_cnt <= round(0.5 * uart_baud_val_c);
else
uart_rx_baud_cnt <= round(uart_baud_val_c);
end if;
if (uart_rx_bitcnt = 0) then
uart_rx_busy <= '0'; -- done
i := to_integer(unsigned(uart_rx_sreg(8 downto 1)));
if (i < 32) or (i > 32+95) then -- printable char?
report name & ".tx: (" & integer'image(i) & ")"; -- print code
else
report name & ".tx: " & character'val(i); -- print ASCII
end if;
if (i = 10) then -- Linux line break
writeline(file_uart_tx_out, l);
elsif (i /= 13) then -- Remove additional carriage return
write(l, character'val(i));
end if;
else
uart_rx_sreg <= uart_rx_sync(4) & uart_rx_sreg(8 downto 1);
uart_rx_bitcnt <= uart_rx_bitcnt - 1;
end if;
else
uart_rx_baud_cnt <= uart_rx_baud_cnt - 1.0;
end if;
end if;
end if;
end process uart_rx_console;
end architecture;

View file

@ -1,119 +0,0 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
use std.textio.all;
library vunit_lib;
context vunit_lib.vunit_context;
context vunit_lib.com_context;
context vunit_lib.vc_context;
use work.uart_rx_pkg.all;
entity uart_rx is
generic (handle : uart_rx_t);
port (
clk : in std_ulogic;
uart_txd : in std_ulogic
);
end entity;
architecture a of uart_rx is
signal uart_rx_sync : std_ulogic_vector(04 downto 0) := (others => '1');
signal uart_rx_busy : std_ulogic := '0';
signal uart_rx_sreg : std_ulogic_vector(08 downto 0) := (others => '0');
signal uart_rx_baud_cnt : real;
signal uart_rx_bitcnt : natural;
file file_uart_tx_out : text open write_mode is "neorv32.testbench_" & get_name(handle.p_logger) & ".out";
constant checker : checker_t := new_checker(handle.p_logger);
constant character_queue : queue_t := new_queue;
begin
control : process
variable request_msg, reply_msg : msg_t;
variable msg_type : msg_type_t;
procedure put_characters_in_queue(s : string) is
begin
for idx in s'range loop
push(character_queue, s(idx));
end loop;
end procedure put_characters_in_queue;
begin
receive(net, handle.p_actor, request_msg);
msg_type := message_type(request_msg);
-- Standard handling of standard wait_for_time messages = wait for the given time
-- before proceeeding
handle_wait_for_time(net, msg_type, request_msg);
if msg_type = check_uart_msg then
put_characters_in_queue(pop(request_msg));
-- Custom handling of standard wait_until_idle message
elsif msg_type = wait_until_idle_msg then
while not is_empty(character_queue) loop
wait until rising_edge(clk);
end loop;
reply_msg := new_msg(wait_until_idle_reply_msg);
reply(net, request_msg, reply_msg);
else
unexpected_msg_type(msg_type);
end if;
end process;
uart_rx_console : process(clk)
variable i : integer;
variable l : line;
variable expected_character : character;
begin
-- "UART" --
if rising_edge(clk) then
-- synchronizer --
uart_rx_sync <= uart_rx_sync(3 downto 0) & uart_txd;
-- arbiter --
if (uart_rx_busy = '0') then -- idle
uart_rx_busy <= '0';
uart_rx_baud_cnt <= round(0.5 * handle.p_baud_val);
uart_rx_bitcnt <= 9;
if (uart_rx_sync(4 downto 1) = "1100") then -- start bit? (falling edge)
uart_rx_busy <= '1';
end if;
else
if (uart_rx_baud_cnt <= 0.0) then
if (uart_rx_bitcnt = 1) then
uart_rx_baud_cnt <= round(0.5 * handle.p_baud_val);
else
uart_rx_baud_cnt <= round(handle.p_baud_val);
end if;
if (uart_rx_bitcnt = 0) then
uart_rx_busy <= '0'; -- done
i := to_integer(unsigned(uart_rx_sreg(8 downto 1)));
if is_empty(character_queue) then
check_failed(checker, "Extra characters received");
else
expected_character := pop(character_queue);
check_equal(checker, character'val(i), expected_character);
end if;
if (i = 10) then -- Linux line break
writeline(file_uart_tx_out, l);
elsif (i /= 13) then -- Remove additional carriage return
write(l, character'val(i));
end if;
else
uart_rx_sreg <= uart_rx_sync(4) & uart_rx_sreg(8 downto 1);
uart_rx_bitcnt <= uart_rx_bitcnt - 1;
end if;
else
uart_rx_baud_cnt <= uart_rx_baud_cnt - 1.0;
end if;
end if;
end if;
end process uart_rx_console;
end architecture;

View file

@ -1,60 +0,0 @@
library vunit_lib;
context vunit_lib.vunit_context;
context vunit_lib.com_context;
use vunit_lib.sync_pkg.all;
package uart_rx_pkg is
constant check_uart_msg : msg_type_t := new_msg_type("check_uart");
type uart_rx_t is record
p_baud_val : real;
p_logger : logger_t;
p_actor : actor_t;
end record;
impure function new_uart_rx(
baud_val : real;
logger : logger_t := null_logger;
actor : actor_t := null_actor) return uart_rx_t;
function as_sync(handle : uart_rx_t) return sync_handle_t;
procedure check_uart(
signal net : inout network_t;
constant handle : in uart_rx_t;
constant data : in string);
end package uart_rx_pkg;
package body uart_rx_pkg is
constant uart_rx_logger : logger_t := get_logger("neorv32_lib:uart_rx_pkg");
impure function new_uart_rx(
baud_val : real;
logger : logger_t := null_logger;
actor : actor_t := null_actor) return uart_rx_t is
variable result : uart_rx_t;
begin
result.p_baud_val := baud_val;
result.p_logger := logger when logger /= null_logger else uart_rx_logger;
result.p_actor := actor when actor /= null_actor else new_actor;
return result;
end;
function as_sync(handle : uart_rx_t) return sync_handle_t is
begin
return handle.p_actor;
end;
procedure check_uart(
signal net : inout network_t;
constant handle : in uart_rx_t;
constant data : in string) is
variable msg : msg_t;
begin
msg := new_msg(check_uart_msg);
push(msg, data);
send(net, handle.p_actor, msg);
end;
end package body uart_rx_pkg;

102
sim/xbus_gateway.vhd Normal file
View file

@ -0,0 +1,102 @@
-- ================================================================================ --
-- NEORV32 - Simple Combinatorial XBUS / Wishbone Gateway --
-- -------------------------------------------------------------------------------- --
-- 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 xbus_gateway is
generic (
-- device address size in bytes and base address --
DEV_0_SIZE : natural := 0; DEV_0_BASE : std_ulogic_vector(31 downto 0) := (others => '0');
DEV_1_SIZE : natural := 0; DEV_1_BASE : std_ulogic_vector(31 downto 0) := (others => '0');
DEV_2_SIZE : natural := 0; DEV_2_BASE : std_ulogic_vector(31 downto 0) := (others => '0');
DEV_3_SIZE : natural := 0; DEV_3_BASE : std_ulogic_vector(31 downto 0) := (others => '0')
);
port (
-- host port --
host_req_i : in xbus_req_t;
host_rsp_o : out xbus_rsp_t;
-- device ports --
dev_0_req_o : out xbus_req_t; dev_0_rsp_i : in xbus_rsp_t;
dev_1_req_o : out xbus_req_t; dev_1_rsp_i : in xbus_rsp_t;
dev_2_req_o : out xbus_req_t; dev_2_rsp_i : in xbus_rsp_t;
dev_3_req_o : out xbus_req_t; dev_3_rsp_i : in xbus_rsp_t
);
end xbus_gateway;
architecture xbus_gateway_rtl of xbus_gateway is
-- module configuration --
constant num_devs_c : natural := 4; -- number of device ports
-- list of device base address and address size --
type dev_base_list_t is array (0 to num_devs_c-1) of std_ulogic_vector(31 downto 0);
type dev_size_list_t is array (0 to num_devs_c-1) of natural;
constant dev_base_list_c : dev_base_list_t := (DEV_0_BASE, DEV_1_BASE, DEV_2_BASE, DEV_3_BASE);
constant dev_size_list_c : dev_size_list_t := (DEV_0_SIZE, DEV_1_SIZE, DEV_2_SIZE, DEV_3_SIZE);
-- device ports combined as arrays --
type dev_req_t is array (0 to num_devs_c-1) of xbus_req_t;
type dev_rsp_t is array (0 to num_devs_c-1) of xbus_rsp_t;
signal dev_req : dev_req_t;
signal dev_rsp : dev_rsp_t;
-- access enable --
signal acc_en : std_ulogic_vector(num_devs_c-1 downto 0);
begin
-- combine device ports --
dev_0_req_o <= dev_req(0); dev_rsp(0) <= dev_0_rsp_i;
dev_1_req_o <= dev_req(1); dev_rsp(1) <= dev_1_rsp_i;
dev_2_req_o <= dev_req(2); dev_rsp(2) <= dev_2_rsp_i;
dev_3_req_o <= dev_req(3); dev_rsp(3) <= dev_3_rsp_i;
-- device select --
acc_en_gen:
for i in 0 to num_devs_c-1 generate
acc_en(i) <= '1' when (dev_size_list_c(i) > 0) and
(unsigned(host_req_i.addr) >= unsigned(dev_base_list_c(i))) and
(unsigned(host_req_i.addr) < (unsigned(dev_base_list_c(i)) + dev_size_list_c(i))) else '0';
end generate;
-- request --
bus_request_gen:
for i in 0 to num_devs_c-1 generate
bus_request: process(host_req_i, acc_en)
begin
dev_req(i) <= host_req_i;
dev_req(i).cyc <= host_req_i.cyc and acc_en(i);
dev_req(i).stb <= host_req_i.stb and acc_en(i);
end process bus_request;
end generate;
-- response --
bus_response: process(dev_rsp, acc_en)
variable tmp_v : xbus_rsp_t;
begin
tmp_v.data := (others => '0');
tmp_v.ack := '0';
tmp_v.err := '0';
for i in 0 to num_devs_c-1 loop -- OR all enabled response buses
if (acc_en(i) = '1') then
tmp_v.data := tmp_v.data or dev_rsp(i).data;
tmp_v.ack := tmp_v.ack or dev_rsp(i).ack;
tmp_v.err := tmp_v.err or dev_rsp(i).err;
end if;
end loop;
host_rsp_o <= tmp_v;
end process bus_response;
end xbus_gateway_rtl;

100
sim/xbus_memory.vhd Normal file
View file

@ -0,0 +1,100 @@
-- ================================================================================ --
-- NEORV32 - Simple XBUS / Wishbone Memory (meant for simulation only) --
-- -------------------------------------------------------------------------------- --
-- 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 xbus_memory is
generic (
MEM_SIZE : natural := 1024; -- memory size in bytes; should be a power of two
MEM_LATE : natural := 1 -- number of latency cycles (min 1)
);
port (
clk_i : in std_ulogic;
rstn_i : in std_ulogic;
xbus_req_i : in xbus_req_t;
xbus_rsp_o : out xbus_rsp_t
);
end xbus_memory;
architecture xbus_memory_rtl of xbus_memory is
-- memory type --
type mem8_bv_t is array (natural range <>) of bit_vector(7 downto 0); -- bit_vector type for optimized system storage
-- memory access --
signal addr : integer range 0 to (MEM_SIZE/4)-1;
signal rdata : std_ulogic_vector(31 downto 0);
signal ack : std_ulogic;
-- latency generator --
type late_data_t is array (MEM_LATE downto 0) of std_ulogic_vector(31 downto 0);
signal late_data : late_data_t;
signal late_ack : std_ulogic_vector(MEM_LATE downto 0);
begin
-- word-aligned read/write address --
addr <= to_integer(unsigned(xbus_req_i.addr(index_size_f(MEM_SIZE/4)+1 downto 2)));
-- Non-Initialized Memory Core ------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
memory_core: process(clk_i)
variable mem8_bv_b0_v, mem8_bv_b1_v, mem8_bv_b2_v, mem8_bv_b3_v : mem8_bv_t(0 to (MEM_SIZE/4)-1);
begin
if rising_edge(clk_i) then
ack <= '0';
if (xbus_req_i.cyc = '1') and (xbus_req_i.stb = '1') then
ack <= '1';
if (xbus_req_i.we = '1') then
if (xbus_req_i.sel(0) = '1') then mem8_bv_b0_v(addr) := to_bitvector(xbus_req_i.data(07 downto 00)); end if;
if (xbus_req_i.sel(1) = '1') then mem8_bv_b1_v(addr) := to_bitvector(xbus_req_i.data(15 downto 08)); end if;
if (xbus_req_i.sel(2) = '1') then mem8_bv_b2_v(addr) := to_bitvector(xbus_req_i.data(23 downto 16)); end if;
if (xbus_req_i.sel(3) = '1') then mem8_bv_b3_v(addr) := to_bitvector(xbus_req_i.data(31 downto 24)); end if;
else
rdata(07 downto 00) <= to_stdulogicvector(mem8_bv_b0_v(addr));
rdata(15 downto 08) <= to_stdulogicvector(mem8_bv_b1_v(addr));
rdata(23 downto 16) <= to_stdulogicvector(mem8_bv_b2_v(addr));
rdata(31 downto 24) <= to_stdulogicvector(mem8_bv_b3_v(addr));
end if;
end if;
end if;
end process memory_core;
-- Latency Generator ----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
latency_gen: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
late_data <= (others => (others => '0'));
late_ack <= (others => '0');
elsif rising_edge(clk_i) then
late_data(0) <= rdata;
late_ack(0) <= ack;
for i in 0 to MEM_LATE-1 loop
late_data(i+1) <= late_data(i);
late_ack(i+1) <= late_ack(i);
end loop;
end if;
end process latency_gen;
-- bus response --
xbus_rsp_o.data <= rdata when (MEM_LATE = 1) else late_data(MEM_LATE-1);
xbus_rsp_o.ack <= ack when (MEM_LATE = 1) else late_ack(MEM_LATE-1);
xbus_rsp_o.err <= '0';
end xbus_memory_rtl;

View file

@ -2,11 +2,13 @@
# Minimal RISC-V ISA only
MARCH = rv32i_zicsr_zifencei
MABI = ilp32
# Optimize for minimal size
EFFORT = -Os
# Adjust memory size and base for BOOTROM
# Adjust "rom" memory size and base for BOOTROM
# Just use a minimal "ram" size that should be available on any platform configuration.
# Define MAKE_BOOTLOADER for SW library (reduces footprint)
# Enable link-time-optimization
USER_FLAGS += \

View file

@ -25,8 +25,8 @@ ASM_INC ?= -I .
# Optimization
EFFORT ?= -Os
# Compiler toolchain
RISCV_PREFIX ?= riscv32-unknown-elf-
# Compiler toolchain prefix
RISCV_PREFIX ?= riscv-none-elf-
# CPU architecture and ABI
MARCH ?= rv32i_zicsr_zifencei
@ -59,7 +59,7 @@ NEORV32_EXG_PATH = $(NEORV32_HOME)/sw/image_gen
# Path to NEORV32 rtl folder
NEORV32_RTL_PATH = $(NEORV32_LOCAL_RTL)
# Path to NEORV32 sim folder
NEORV32_SIM_PATH = $(NEORV32_HOME)/sim/simple
NEORV32_SIM_PATH = $(NEORV32_HOME)/sim
# Marker file to check for NEORV32 home folder
NEORV32_HOME_MARKER = $(NEORV32_INC_PATH)/neorv32.h
@ -195,14 +195,16 @@ main.bin: $(APP_ELF)
# Generate NEORV32 executable image for upload via bootloader
$(APP_EXE): main.bin $(IMAGE_GEN)
@set -e
@echo "Generating $(APP_EXE)"
@$(IMAGE_GEN) -app_bin $< $@ $(shell basename $(CURDIR))
@echo "Executable ($(APP_EXE)) size in bytes:"
@echo "Executable size in bytes:"
@wc -c < $(APP_EXE)
# Generate NEORV32 executable VHDL boot image
$(APP_VHD): main.bin $(IMAGE_GEN)
@set -e
@$(IMAGE_GEN) -app_img $< $@ $(shell basename $(CURDIR))
@echo "Generating $(APP_VHD)"
@$(IMAGE_GEN) -app_vhd $< $@ $(shell basename $(CURDIR))
# Install VHDL memory initialization file
install-$(APP_VHD): $(APP_VHD)
@ -213,26 +215,31 @@ install-$(APP_VHD): $(APP_VHD)
# Generate NEORV32 RAW executable image in plain hex format
$(APP_HEX): main.bin $(IMAGE_GEN)
@set -e
@echo "Generating $(APP_HEX)"
@$(IMAGE_GEN) -raw_hex $< $@ $(shell basename $(CURDIR))
# Generate NEORV32 RAW executable image in binary format
$(APP_BIN): main.bin $(IMAGE_GEN)
@set -e
@echo "Generating $(APP_BIN)"
@$(IMAGE_GEN) -raw_bin $< $@ $(shell basename $(CURDIR))
# Generate NEORV32 RAW executable image in COE format
$(APP_COE): main.bin $(IMAGE_GEN)
@set -e
@echo "Generating $(APP_COE)"
@$(IMAGE_GEN) -raw_coe $< $@ $(shell basename $(CURDIR))
# Generate NEORV32 RAW executable image in MIF format
$(APP_MIF): main.bin $(IMAGE_GEN)
@set -e
@echo "Generating $(APP_MIF)"
@$(IMAGE_GEN) -raw_mif $< $@ $(shell basename $(CURDIR))
# Generate NEORV32 RAW executable image in MEM format
$(APP_MEM): main.bin $(IMAGE_GEN)
@set -e
@echo "Generating $(APP_MEM)"
@$(IMAGE_GEN) -raw_mem $< $@ $(shell basename $(CURDIR))
# -----------------------------------------------------------------------------
@ -241,7 +248,8 @@ $(APP_MEM): main.bin $(IMAGE_GEN)
# Create local VHDL BOOTROM image
bl_image: main.bin $(IMAGE_GEN)
@set -e
@$(IMAGE_GEN) -bld_img $< $(BOOT_VHD) $(shell basename $(CURDIR))
@echo "Generating $(BOOT_VHD)"
@$(IMAGE_GEN) -bld_vhd $< $(BOOT_VHD) $(shell basename $(CURDIR))
# Install BOOTROM image to VHDL source directory
bootloader: bl_image
@ -279,10 +287,10 @@ endif
@echo "Toolchain check OK"
# -----------------------------------------------------------------------------
# In-console simulation using default/simple testbench and GHDL
# In-console simulation using default testbench and GHDL
# -----------------------------------------------------------------------------
sim: $(APP_VHD) install
@echo "Simulating processor using simple testbench..."
@echo "Simulating processor using default testbench..."
@sh $(NEORV32_SIM_PATH)/ghdl.sh $(GHDL_RUN_FLAGS)
# -----------------------------------------------------------------------------
@ -375,7 +383,7 @@ help:
@echo " mif - compile and generate <$(APP_MIF)> executable memory image"
@echo " image - compile and generate VHDL IMEM application boot image <$(APP_VHD)> in local folder"
@echo " install - compile, generate and install VHDL IMEM application boot image <$(APP_VHD)>"
@echo " sim - in-console simulation using default/simple testbench and GHDL"
@echo " sim - in-console simulation using default testbench (sim folder) and GHDL"
@echo " hdl_lists - regenerate HDL file-lists (*.f) in NEORV32_HOME/rtl"
@echo " all - exe + install + hex + bin + asm"
@echo " elf_info - show ELF layout info"

View file

@ -1,7 +1,9 @@
## Dhrystone Benchmark
**:warning: The Dhrystone port is outdated. Have a look at the CoreMark port for benchmarking.**
:copyright: Original sources from [https://github.com/sifive/benchmark-dhrystone](https://github.com/sifive/benchmark-dhrystone);
see `LICENSE` file. The origianl source code has been modified for the NEORV32 RISC-V Processor.
see `LICENSE` file. The original source code has been modified for the NEORV32 RISC-V Processor.
To compile the `main.exe` executable:

View file

@ -54,12 +54,10 @@
#define __USE_GNU
#include <fenv.h>
//#pragma STDC FENV_ACCESS ON
#define _GNU_SOURCE
#include <float.h>
#include <fenv.h>
#include <math.h>
@ -130,37 +128,6 @@ uint32_t get_hw_exceptions(void) {
}
/**********************************************************************//**
* Get exception flags from C runtime (floating-point emulation).
*
* @warning WORK-IN-PROGRESS!
*
* @return Floating point exception status word.
**************************************************************************/
uint32_t get_sw_exceptions(void) {
const uint32_t FP_EXC_NV_C = 1 << 0; // invalid operation
const uint32_t FP_EXC_DZ_C = 1 << 1; // divide by zero
const uint32_t FP_EXC_OF_C = 1 << 2; // overflow
const uint32_t FP_EXC_UF_C = 1 << 3; // underflow
const uint32_t FP_EXC_NX_C = 1 << 4; // inexact
int fpeRaised = fetestexcept(FE_ALL_EXCEPT);
uint32_t res = 0;
if (fpeRaised & FE_INVALID) { res |= FP_EXC_NV_C; }
if (fpeRaised & FE_DIVBYZERO) { res |= FP_EXC_DZ_C; }
if (fpeRaised & FE_OVERFLOW) { res |= FP_EXC_OF_C; }
if (fpeRaised & FE_UNDERFLOW) { res |= FP_EXC_UF_C; }
if (fpeRaised & FE_INEXACT) { res |= FP_EXC_NX_C; }
feclearexcept(FE_ALL_EXCEPT);
return res;
}
// ################################################################################################
// "Intrinsics"
// ################################################################################################

View file

@ -33,4 +33,4 @@ NEORV32_HOME ?= ../../..
include $(NEORV32_HOME)/sw/common/common.mk
sim-check: sim
cat $(NEORV32_HOME)/sim/simple/neorv32.uart0.sim_mode.text.out | grep "Hello world! :)"
cat $(NEORV32_HOME)/sim/neorv32.uart0_sim_mode.out | grep "Hello world! :)"

View file

@ -6,8 +6,8 @@ TOPTARGETS := exe clean_all check info all
SUBDIRS := $(wildcard */.)
# ignore dummy folders (starting with '~')
EXCLDIR := $(wildcard ~*/.)
# ignore the Eclipse project
EXCLDIR += eclipse/.
# ignore some of the default projects/examples
EXCLDIR += eclipse/. dhrystone/. performance_tests/. float_corner_test/.
SUBDIRS := $(filter-out $(EXCLDIR), $(SUBDIRS))
$(TOPTARGETS): $(SUBDIRS)

View file

@ -31,6 +31,8 @@
#define ADDR_UNREACHABLE (NEORV32_DM_BASE)
//** External memory base address */
#define EXT_MEM_BASE (0xF0000000UL)
//** External IRQ trigger base address */
#define SIM_TRIG_BASE (0xFF000000UL)
/**@}*/
@ -239,9 +241,9 @@ int main() {
cnt_test++;
// set execute permission for u-mode
// set execute permission for u-mode for the entire address range
// use entry 2 so we can use entries 0 & 1 later on for higher-prioritized configurations
tmp_a = neorv32_cpu_pmp_configure_region(2, -1, (PMP_NAPOT << PMPCFG_A_LSB) | (1 << PMPCFG_X));
tmp_a = neorv32_cpu_pmp_configure_region(2, 0xffffffff, (PMP_NAPOT << PMPCFG_A_LSB) | (1 << PMPCFG_X));
if ((neorv32_cpu_csr_read(CSR_MCAUSE) == mcause_never_c) && (tmp_a == 0)) {
test_ok();
@ -2237,7 +2239,7 @@ int main() {
**************************************************************************/
void sim_irq_trigger(uint32_t sel) {
*((volatile uint32_t*) (0xFF000000)) = sel;
*((volatile uint32_t*)SIM_TRIG_BASE) = sel;
}

View file

@ -23,7 +23,7 @@ override USER_FLAGS += -Wl,--defsym,__neorv32_rom_size=32k
override USER_FLAGS += -Wl,--defsym,__neorv32_ram_size=8k
# Adjust maximum heap size
#SER_FLAGS += -Wl,--defsym,__neorv32_heap_size=3096
override USER_FLAGS += -Wl,--defsym,__neorv32_heap_size=3096
# Simulation arguments
override GHDL_RUN_FLAGS ?= --stop-time=15ms
@ -40,4 +40,4 @@ include $(NEORV32_HOME)/sw/common/common.mk
# Add test-specific makefile target
sim-check: sim
cat $(NEORV32_HOME)/sim/simple/neorv32.uart0.sim_mode.text.out | grep "PROCESSOR TEST COMPLETED SUCCESSFULLY!"
cat $(NEORV32_HOME)/sim/neorv32.uart0_sim_mode.out | grep "PROCESSOR TEST COMPLETED SUCCESSFULLY!"

View file

@ -20,8 +20,8 @@ const uint32_t signature = 0x4788CAFE;
// output file types (operation select)
enum operation_enum {
OP_APP_BIN,
OP_APP_IMG,
OP_BLD_IMG,
OP_APP_VHD,
OP_BLD_VHD,
OP_RAW_HEX,
OP_RAW_BIN,
OP_RAW_COE,
@ -36,8 +36,8 @@ int main(int argc, char *argv[]) {
"Three arguments are required.\n"
"1st: Operation\n"
" -app_bin : Generate application executable binary (binary file, little-endian, with header) \n"
" -app_img : Generate application raw executable memory image (vhdl package body file, no header)\n"
" -bld_img : Generate bootloader raw executable memory image (vhdl package body file, no header)\n"
" -app_vhd : Generate application raw executable memory image (vhdl package body file, no header)\n"
" -bld_vhd : Generate bootloader raw executable memory image (vhdl package body file, no header)\n"
" -raw_hex : Generate application raw executable (ASCII hex file, no header)\n"
" -raw_bin : Generate application raw executable (binary file, no header)\n"
" -raw_coe : Generate application raw executable (COE file, no header)\n"
@ -58,22 +58,22 @@ int main(int argc, char *argv[]) {
unsigned long raw_exe_size = 0;
if (strcmp(argv[1], "-app_bin") == 0) { operation = OP_APP_BIN; }
else if (strcmp(argv[1], "-app_img") == 0) { operation = OP_APP_IMG; }
else if (strcmp(argv[1], "-bld_img") == 0) { operation = OP_BLD_IMG; }
else if (strcmp(argv[1], "-app_vhd") == 0) { operation = OP_APP_VHD; }
else if (strcmp(argv[1], "-bld_vhd") == 0) { operation = OP_BLD_VHD; }
else if (strcmp(argv[1], "-raw_hex") == 0) { operation = OP_RAW_HEX; }
else if (strcmp(argv[1], "-raw_bin") == 0) { operation = OP_RAW_BIN; }
else if (strcmp(argv[1], "-raw_coe") == 0) { operation = OP_RAW_COE; }
else if (strcmp(argv[1], "-raw_mem") == 0) { operation = OP_RAW_MEM; }
else if (strcmp(argv[1], "-raw_mif") == 0) { operation = OP_RAW_MIF; }
else {
printf("Invalid operation!");
printf("Invalid operation '%s'!\n", argv[1]);
return -1;
}
// open input file
input = fopen(argv[2], "rb");
if(input == NULL) {
printf("Input file error!");
printf("Input file error (%s)!\n", argv[2]);
return -2;
}
@ -84,12 +84,12 @@ int main(int argc, char *argv[]) {
rewind(input);
if ((input_size % 4) != 0) {
printf("WARNING - image size is not a multiple of 4 bytes.\n");
printf("WARNING - image size is not a multiple of 4 bytes!\n");
}
// input file empty?
if(input_size == 0) {
printf("Input file is empty!");
printf("Input file is empty (%s)!\n", argv[2]);
fclose(input);
return -3;
}
@ -97,32 +97,21 @@ int main(int argc, char *argv[]) {
// open output file
output = fopen(argv[3], "wb");
if(output == NULL) {
printf("Output file error!");
printf("Output file error (%s)!\n", argv[3]);
fclose(input);
return -4;
}
// --------------------------------------------------------------------------
// Try to find out targeted CPU configuration via MARCH environment variable
// --------------------------------------------------------------------------
char string_march[64] = "default";
char *envvar_march = "MARCH";
if (getenv(envvar_march)) {
if (snprintf(string_march, 64, "%s", getenv(envvar_march)) >= 64){
strcpy(string_march, "default");
}
}
// --------------------------------------------------------------------------
// Get image's compilation date and time
// Image's compilation date and time
// --------------------------------------------------------------------------
time_t time_current;
time(&time_current);
struct tm *time_local = localtime(&time_current);
char compile_time[64];
snprintf(compile_time, 64, "%02d.%02d.%d %02d:%02d:%02d (dd.mm.yyyy hh:mm:ss)",
snprintf(compile_time, 64, "%02d.%02d.%d %02d:%02d:%02d",
time_local->tm_mday,
time_local->tm_mon + 1,
time_local->tm_year + 1900,
@ -133,7 +122,7 @@ int main(int argc, char *argv[]) {
// --------------------------------------------------------------------------
// Get size of application (in bytes)
// Size of application (in bytes)
// --------------------------------------------------------------------------
fseek(input, 0L, SEEK_END);
@ -204,22 +193,26 @@ int main(int argc, char *argv[]) {
// --------------------------------------------------------------------------
// Generate RAW APPLICATION's executable memory initialization file
// -> VHDL package body
// Generate APPLICATION executable memory initialization image package (IMEM)
// --------------------------------------------------------------------------
else if (operation == OP_APP_IMG) {
else if (operation == OP_APP_VHD) {
// header
sprintf(tmp_string, "-- The NEORV32 RISC-V Processor: https://github.com/stnolting/neorv32\n"
"-- Auto-generated memory initialization file (for APPLICATION) from source file <%s/%s>\n"
sprintf(tmp_string, "-- The NEORV32 RISC-V Processor - github.com/stnolting/neorv32\n"
"-- Auto-generated memory initialization package (for internal IMEM)\n"
"-- Source: %s/%s\n"
"-- Size: %lu bytes\n"
"-- MARCH: %s\n"
"-- Built: %s\n"
"\n"
"-- prototype defined in 'neorv32_package.vhd'\n"
"package body neorv32_application_image is\n"
"library ieee;\n"
"use ieee.std_logic_1164.all;\n"
"\n"
"constant application_init_image : mem32_t := (\n", argv[4], argv[2], raw_exe_size, string_march, compile_time);
"library neorv32;\n"
"use neorv32.neorv32_package.all;\n"
"\n"
"package neorv32_application_image is\n"
"\n"
"constant application_init_image : mem32_t := (\n", argv[4], argv[2], raw_exe_size, compile_time);
fputs(tmp_string, output);
i = 0;
@ -261,22 +254,26 @@ int main(int argc, char *argv[]) {
// --------------------------------------------------------------------------
// Generate RAW BOOTLOADER's executable memory initialization file
// -> VHDL package body
// Generate BOOTLOADER executable memory initialization image package (BOOTROM)
// --------------------------------------------------------------------------
else if (operation == OP_BLD_IMG) {
else if (operation == OP_BLD_VHD) {
// header
sprintf(tmp_string, "-- The NEORV32 RISC-V Processor: https://github.com/stnolting/neorv32\n"
"-- Auto-generated memory initialization file (for BOOTLOADER) from source file <%s/%s>\n"
sprintf(tmp_string, "-- The NEORV32 RISC-V Processor - github.com/stnolting/neorv32\n"
"-- Auto-generated memory initialization package (for internal BOOTROM)\n"
"-- Source: %s/%s\n"
"-- Size: %lu bytes\n"
"-- MARCH: %s\n"
"-- Built: %s\n"
"\n"
"-- prototype defined in 'neorv32_package.vhd'\n"
"package body neorv32_bootloader_image is\n"
"library ieee;\n"
"use ieee.std_logic_1164.all;\n"
"\n"
"constant bootloader_init_image : mem32_t := (\n", argv[4], argv[2], raw_exe_size, string_march, compile_time);
"library neorv32;\n"
"use neorv32.neorv32_package.all;\n"
"\n"
"package neorv32_bootloader_image is\n"
"\n"
"constant bootloader_init_image : mem32_t := (\n", argv[4], argv[2], raw_exe_size, compile_time);
fputs(tmp_string, output);
i = 0;
@ -352,7 +349,7 @@ int main(int argc, char *argv[]) {
// header
sprintf(tmp_string, "memory_initialization_radix=16;\n");
fputs(tmp_string, output);
sprintf(tmp_string, "memory_initialization_vector=");
sprintf(tmp_string, "memory_initialization_vector=\n");
fputs(tmp_string, output);
i = 0;
@ -362,18 +359,14 @@ int main(int argc, char *argv[]) {
tmp |= (uint32_t)(buffer[2] << 16);
tmp |= (uint32_t)(buffer[3] << 24);
if (i == (input_words-1)) {
sprintf(tmp_string, "\n%08x", (unsigned int)tmp);
sprintf(tmp_string, "%08x;\n", (unsigned int)tmp);
}
else {
sprintf(tmp_string, "\n%08x,", (unsigned int)tmp);
sprintf(tmp_string, "%08x,\n", (unsigned int)tmp);
}
fputs(tmp_string, output);
i++;
}
// footer
sprintf(tmp_string, ";\n");
fputs(tmp_string, output);
}
@ -435,7 +428,7 @@ int main(int argc, char *argv[]) {
// Invalid operation
// --------------------------------------------------------------------------
else {
printf("Invalid operation!");
printf("Invalid operation!\n");
fclose(input);
fclose(output);
return -1;
@ -443,7 +436,7 @@ int main(int argc, char *argv[]) {
// --------------------------------------------------------------------------
// Done, clean up
// Clean up
// --------------------------------------------------------------------------
fclose(input);
fclose(output);

View file

@ -66,6 +66,7 @@ enum NEORV32_RTE_TRAP_enum {
**************************************************************************/
/**@{*/
void neorv32_rte_setup(void);
void neorv32_rte_core(void);
int neorv32_rte_handler_install(int id, void (*handler)(void));
int neorv32_rte_handler_uninstall(int id);
void neorv32_rte_debug_handler(void);

View file

@ -37,12 +37,14 @@ typedef volatile struct __attribute__((packed,aligned(4))) {
/** NEORV32_SYSINFO.MEM (r/-): Memory configuration (sizes) */
enum NEORV32_SYSINFO_MEM_enum {
SYSINFO_MEM_IMEM = 0, /**< SYSINFO_MEM byte 0 (r/-): log2(internal IMEM size in bytes) (via MEM_INT_IMEM_SIZE generic) */
SYSINFO_MEM_DMEM = 1 /**< SYSINFO_MEM byte 1 (r/-): log2(internal DMEM size in bytes) (via MEM_INT_DMEM_SIZE generic) */
SYSINFO_MEM_DMEM = 1, /**< SYSINFO_MEM byte 1 (r/-): log2(internal DMEM size in bytes) (via MEM_INT_DMEM_SIZE generic) */
SYSINFO_MEM_res = 2, /**< SYSINFO_MEM byte 2 (r/-): reserved, read as zero */
SYSINFO_MEM_BOOT = 3 /**< SYSINFO_MEM byte 3 (r/-): boot mode configuration (via BOOT_MODE_SELECT generic) */
};
/** NEORV32_SYSINFO.SOC (r/-): Implemented processor devices/features */
enum NEORV32_SYSINFO_SOC_enum {
SYSINFO_SOC_BOOTLOADER = 0, /**< SYSINFO_SOC (0) (r/-): Bootloader implemented when 1 (via INT_BOOTLOADER_EN generic) */
SYSINFO_SOC_BOOTLOADER = 0, /**< SYSINFO_SOC (0) (r/-): Bootloader implemented when 1 (via BOOT_MODE_SELECT generic) */
SYSINFO_SOC_XBUS = 1, /**< SYSINFO_SOC (1) (r/-): External bus interface implemented when 1 (via XBUS_EN generic) */
SYSINFO_SOC_MEM_INT_IMEM = 2, /**< SYSINFO_SOC (2) (r/-): Processor-internal instruction memory implemented when 1 (via MEM_INT_IMEM_EN generic) */
SYSINFO_SOC_MEM_INT_DMEM = 3, /**< SYSINFO_SOC (3) (r/-): Processor-internal data memory implemented when 1 (via MEM_INT_DMEM_EN generic) */
@ -54,6 +56,7 @@ enum NEORV32_SYSINFO_SOC_enum {
SYSINFO_SOC_XIP = 9, /**< SYSINFO_SOC (9) (r/-): Execute in-place module implemented when 1 (via XIP_EN generic) */
SYSINFO_SOC_XIP_CACHE = 10, /**< SYSINFO_SOC (10) (r/-): Execute in-place cache implemented when 1 (via XIP_CACHE_EN generic) */
SYSINFO_SOC_OCD_AUTH = 11, /**< SYSINFO_SOC (11) (r/-): On-chip debugger authentication implemented when 1 (via OCD_AUTHENTICATION generic) */
SYSINFO_SOC_IMEM_ROM = 12, /**< SYSINFO_SOC (12) (r/-): Processor-internal instruction memory implemented as pre-initialized ROM when 1 (via BOOT_MODE_SELECT generic) */
SYSINFO_SOC_IO_DMA = 14, /**< SYSINFO_SOC (14) (r/-): Direct memory access controller implemented when 1 (via IO_DMA_EN generic) */
SYSINFO_SOC_IO_GPIO = 15, /**< SYSINFO_SOC (15) (r/-): General purpose input/output port unit implemented when 1 (via IO_GPIO_EN generic) */

View file

@ -301,7 +301,7 @@ uint32_t neorv32_cpu_pmp_get_granularity(void) {
* @warning This function expects a WORD address!
*
* @param[in] index Region number (index, 0..PMP_NUM_REGIONS-1).
* @param[in] addr Region address (word address!).
* @param[in] addr Region address (bits [33:2]).
* @param[in] config Region configuration byte (see #NEORV32_PMPCFG_ATTRIBUTES_enum).
* @return Returns 0 on success, !=0 on failure.
**************************************************************************/

View file

@ -23,7 +23,6 @@
static uint32_t __neorv32_rte_vector_lut[NEORV32_RTE_NUM_TRAPS] __attribute__((unused)); // trap handler vector table
// private functions
static void __attribute__((__naked__,aligned(4))) __neorv32_rte_core(void);
static void __neorv32_rte_print_hex_word(uint32_t num);
@ -41,7 +40,7 @@ void neorv32_rte_setup(void) {
neorv32_cpu_csr_write(CSR_MSTATUS, (1<<CSR_MSTATUS_MPP_H) | (1<<CSR_MSTATUS_MPP_L));
// configure trap handler base address
neorv32_cpu_csr_write(CSR_MTVEC, (uint32_t)(&__neorv32_rte_core));
neorv32_cpu_csr_write(CSR_MTVEC, (uint32_t)(&neorv32_rte_core));
// disable all IRQ channels
neorv32_cpu_csr_write(CSR_MIE, 0);
@ -98,7 +97,7 @@ int neorv32_rte_handler_uninstall(int id) {
* NEORV32 runtime environment (RTE):
* This is the core of the NEORV32 RTE (first-level trap handler, executed in machine mode).
**************************************************************************/
static void __attribute__((__naked__,aligned(4))) __neorv32_rte_core(void) {
void __attribute__((__naked__,aligned(4))) neorv32_rte_core(void) {
// save context
asm volatile (
@ -493,21 +492,24 @@ void neorv32_rte_print_hw_config(void) {
neorv32_uart0_printf("\nPhys. Memory Prot.: ");
uint32_t pmp_num_regions = neorv32_cpu_pmp_get_num_regions();
if (pmp_num_regions != 0) {
neorv32_uart0_printf("%u region(s), %u bytes granularity, modes={OFF", pmp_num_regions, neorv32_cpu_pmp_get_granularity());
neorv32_uart0_printf("%u region(s), %u bytes granularity, modes =", pmp_num_regions, neorv32_cpu_pmp_get_granularity());
// check implemented modes
neorv32_cpu_csr_write(CSR_PMPCFG0, (PMP_OFF << PMPCFG_A_LSB)); // try to set mode "OFF"
if ((neorv32_cpu_csr_read(CSR_PMPCFG0) & 0xff) == (PMP_OFF << PMPCFG_A_LSB)) {
neorv32_uart0_printf(" OFF");
}
neorv32_cpu_csr_write(CSR_PMPCFG0, (PMP_TOR << PMPCFG_A_LSB)); // try to set mode "TOR"
if ((neorv32_cpu_csr_read(CSR_PMPCFG0) & 0xff) == (PMP_TOR << PMPCFG_A_LSB)) {
neorv32_uart0_printf(",TOR");
neorv32_uart0_printf(" TOR");
}
neorv32_cpu_csr_write(CSR_PMPCFG0, (PMP_NA4 << PMPCFG_A_LSB)); // try to set mode "NA4"
if ((neorv32_cpu_csr_read(CSR_PMPCFG0) & 0xff) == (PMP_NA4 << PMPCFG_A_LSB)) {
neorv32_uart0_printf(",NA4");
neorv32_uart0_printf(" NA4");
}
neorv32_cpu_csr_write(CSR_PMPCFG0, (PMP_NAPOT << PMPCFG_A_LSB)); // try to set mode "NAPOT"
if ((neorv32_cpu_csr_read(CSR_PMPCFG0) & 0xff) == (PMP_NAPOT << PMPCFG_A_LSB)) {
neorv32_uart0_printf(",NAPOT");
neorv32_uart0_printf(" NAPOT");
}
neorv32_uart0_putc('}');
neorv32_cpu_csr_write(CSR_PMPCFG0, 0); // disable PMP entry again
}
else {
@ -524,12 +526,13 @@ void neorv32_rte_print_hw_config(void) {
neorv32_uart0_printf("none");
}
neorv32_uart0_printf("\nBoot configuration: Boot ");
if (NEORV32_SYSINFO->SOC & (1 << SYSINFO_SOC_BOOTLOADER)) {
neorv32_uart0_printf("via Bootloader\n");
}
else {
neorv32_uart0_printf("from memory\n");
neorv32_uart0_printf("\nBoot configuration: ");
int boot_config = (int)(NEORV32_SYSINFO->MEM[SYSINFO_MEM_BOOT]);
switch (boot_config) {
case 0: neorv32_uart0_printf("boot via bootloader (0)\n"); break;
case 1: neorv32_uart0_printf("boot from custom address (1)\n"); break;
case 2: neorv32_uart0_printf("boot from pre-initialized IMEM (2)\n"); break;
default: neorv32_uart0_printf("unknown (%u)\n", boot_config); break;
}
// internal IMEM
@ -618,9 +621,9 @@ void neorv32_rte_print_hw_config(void) {
neorv32_uart0_printf("Ext. bus interface: ");
tmp = NEORV32_SYSINFO->SOC;
if (tmp & (1 << SYSINFO_SOC_XBUS)) {
neorv32_uart0_printf("Wishbone-b4 ");
neorv32_uart0_printf("enabled ");
if (tmp & (1 << SYSINFO_SOC_XBUS_CACHE)) {
neorv32_uart0_printf("x-cache\n");
neorv32_uart0_printf("+ xbus-cache\n");
}
else {
neorv32_uart0_printf("\n");

View file

@ -83,14 +83,14 @@ void neorv32_uart_setup(neorv32_uart_t *UARTx, uint32_t baudrate, uint32_t irq_m
tmp |= (uint32_t)(irq_mask & (0x1fU << UART_CTRL_IRQ_RX_NEMPTY));
#ifdef UART0_SIM_MODE
#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!
#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!
if (((uint32_t)UARTx) == NEORV32_UART0_BASE) {
tmp |= 1U << UART_CTRL_SIM_MODE;
}
#endif
#ifdef UART1_SIM_MODE
#warning UART1_SIM_MODE (secondary UART) enabled! Sending all UART1.TX data to text.io simulation output instead of real UART1 transmitter. Use this for simulations only!
#warning UART1_SIM_MODE (secondary UART) enabled! Sending all UART1.TX data to text.io simulation output instead of real UART1 transmitter. Use this for simulation only!
if (((uint32_t)UARTx) == NEORV32_UART1_BASE) {
tmp |= 1U << UART_CTRL_SIM_MODE;
}

View file

@ -1591,10 +1591,10 @@
<addressOffset>0x04</addressOffset>
<access>read-only</access>
<fields>
<field><name>SYSINFO_MEM_0</name><bitRange>[7:0]</bitRange><description>log2(IMEM size in bytes)</description></field>
<field><name>SYSINFO_MEM_1</name><bitRange>[15:8]</bitRange><description>log2(DMEM size in bytes)</description></field>
<field><name>SYSINFO_MEM_2</name><bitRange>[23:16]</bitRange><description>yet unused</description></field>
<field><name>SYSINFO_MEM_3</name><bitRange>[31:24]</bitRange><description>yet unused</description></field>
<field><name>SYSINFO_MEM_IMEM</name><bitRange>[7:0]</bitRange><description>log2(IMEM size in bytes)</description></field>
<field><name>SYSINFO_MEM_DMEM</name><bitRange>[15:8]</bitRange><description>log2(DMEM size in bytes)</description></field>
<field><name>SYSINFO_MEM_res</name><bitRange>[23:16]</bitRange><description>yet unused</description></field>
<field><name>SYSINFO_MEM_BOOT</name><bitRange>[31:24]</bitRange><description>Boot mode configuration select</description></field>
</fields>
</register>
<register>
@ -1615,6 +1615,7 @@
<field><name>SYSINFO_SOC_XIP</name><bitRange>[9:9]</bitRange><description>Execute in place module implemented</description></field>
<field><name>SYSINFO_SOC_XIP_CACHE</name><bitRange>[10:10]</bitRange><description>Execute in place cache implemented</description></field>
<field><name>SYSINFO_SOC_OCD_AUTH</name><bitRange>[11:11]</bitRange><description>On-chip debugger authentication implemented</description></field>
<field><name>SYSINFO_SOC_IMEM_ROM</name><bitRange>[12:12]</bitRange><description>Processor-internal instruction memory implemented as pre-initialized ROM</description></field>
<field><name>SYSINFO_SOC_IO_DMA</name><bitRange>[14:14]</bitRange><description>Direct memory access controller implemented</description></field>
<field><name>SYSINFO_SOC_IO_GPIO</name><bitRange>[15:15]</bitRange><description>General purpose input/output port unit implemented</description></field>
<field><name>SYSINFO_SOC_IO_MTIME</name><bitRange>[16:16]</bitRange><description>Machine system timer implemented</description></field>