URL
https://opencores.org/ocsvn/neorv32/neorv32/trunk
Subversion Repositories neorv32
Compare Revisions
- This comparison shows the changes necessary to convert path
/neorv32/trunk
- from Rev 67 to Rev 68
- ↔ Reverse comparison
Rev 67 → Rev 68
/docs/datasheet/cpu.adoc
611,28 → 611,31
|
The CSR access instructions as well as the exception and interrupt system (= the privileged architecture) |
is implemented when the `CPU_EXTENSION_RISCV_Zicsr` configuration generic is _true_. |
|
[IMPORTANT] |
If the `Zicsr` extension is disabled the CPU does not provide any _privileged architecture_ features at all! |
In order to provide the full set of privileged functions that are required to run more complex tasks like |
operating system and to allow a secure execution environment the `Zicsr` extension should always be enabled. |
|
In this case the following instructions are available: |
|
* CSR access: `csrrw`, `csrrs`, `csrrc`, `csrrwi`, `csrrsi`, `csrrci` |
* environment: `mret`, `wfi` |
|
[WARNING] |
If the `Zicsr` extension is disabled the CPU does not provide any _privileged architecture_ features at all! |
In order to provide the full set of functions and to allow a secure execution |
environment the `Zicsr` extension should always be enabled. |
[NOTE] |
If `rd=x0` for the `csrrw[i]` instructions there will be no actual read access to the according CSR. |
However, access privileges are still enforced so these instruction variants _do_ cause side-effects |
(the RISC-V spec. state that these combinations "_shall_ not cause any side-effects"). |
|
[NOTE] |
The "wait for interrupt instruction" `wfi` works like a sleep command. When executed, the CPU is |
The "wait for interrupt instruction" `wfi` acts like a sleep command. When executed, the CPU is |
halted until a valid interrupt request occurs. To wake up again, the according interrupt source has to |
be enabled via the `mie` CSR and the global interrupt enable flag in `mstatus` has to be set. |
|
[NOTE] |
The `wfi` instruction may also be executed in user-mode without causing an exception as <<_mstatus>> bit |
`TW` (timeout wait) is hardwired to zero. |
`TW` (timeout wait) is _hardwired_ to zero. |
|
|
|
|
==== **`Zicntr`** CPU Base Counters |
|
The `Zicntr` ISA extension adds the basic cycle `[m]cycle[h]`), instruction-retired (`[m]instret[h]`) and time (`time[h]`) |
/docs/datasheet/on_chip_debugger.adoc
87,13 → 87,11
| `jtag_tms_i` | 1 | in | mode select |
|======================= |
|
.JTAG Clock |
.Maximum JTAG Clock |
[IMPORTANT] |
The actual JTAG clock signal is **not** used as primary clock. Instead it is used to synchronize |
JTGA accesses, while all internal operations trigger on the system clock. Hence, no additional clock domain is required |
for integration of this module. |
However, this constraints the maximal JTAG clock (`jtag_tck_i`) frequency to be less than or equal to |
1/4 of the system clock (`clk_i`) frequency. |
All JTAG signals are synchronized to the processor clock domain by oversampling them in DTM. Hence, no additional |
clock domain is required for the DTM. However, this constraints the maximal JTAG clock frequency (`jtag_tck_i`) to be less |
than or equal to **1/5** of the processor clock frequency (`clk_i`). |
|
[NOTE] |
If the on-chip debugger is disabled (_ON_CHIP_DEBUGGER_EN_ = false) the JTAG serial input `jtag_tdi_i` is directly |
127,10 → 125,24
| others | `BYPASS` | 1 | default JTAG bypass register |
|======================= |
|
.`DTMCS` - DTM Control and Status Register |
[cols="^2,^3,^1,<8"] |
[options="header",grid="rows"] |
|======================= |
| Bit(s) | Name | r/w | Description |
| 31:18 | - | r/- | _reserved_, hardwired to zero |
| 17 | `dmihardreset` | r/w | setting this bit will reset the DM interface; this bit auto-clears |
| 16 | `dmireset` | r/w | setting this bit will clear ste sticky error state; this bit auto-clears |
| 15 | - | r/- | _reserved_, hardwired to zero |
| 14:12 | `idle` | r/- | recommended idle states (= 0, no idle states required) |
| 11:10 | `dmistat` | r/- | DMI statu: `00` = no error, `01` = reserved, `10` = operation failed, `11` = failed operation during pending DMI operation |
| 9:4 | `abits` | r/- | number of DMI address bits (= 7) |
| 3:0 | `version` | r/- | `0001` = spec version 0.13 |
|======================= |
|
[INFO] |
See the https://github.com/riscv/riscv-debug-spec[RISC-V debug specification] for more information regarding the data |
registers and operations. |
A local copy can be found in `docs/references`. |
registers and operations. A local copy can be found in `docs/references`. |
|
|
|
/docs/datasheet/overview.adoc
107,7 → 107,13
vs. size trade-off and a different focus: _embrace_ concepts like documentation, platform-independence / portability, |
RISC-V compatibility, _customization_ and _ease of use_. See the <<_project_key_features>> below. |
|
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 from memory-mapped |
devices. Precise exceptions allow a defined and fully-synchronized state of the CPU at every time. |
|
|
|
// #################################################################################################################### |
:sectnums: |
=== Project Key Features |
193,7 → 199,7
:sectnums: |
=== VHDL File Hierarchy |
|
All necessary VHDL hardware description files are located in the project's `rtl/core folder`. The top entity |
All necessary VHDL hardware description files are located in the project's `rtl/core` folder. The top entity |
of the entire processor including all the required configuration generics is **`neorv32_top.vhd`**. |
|
[IMPORTANT] |
243,14 → 249,14
├neorv32_wishbone.vhd - External (Wishbone) bus interface |
├neorv32_xirq.vhd - External interrupt controller |
│ |
├mem/neorv32_dmem.default.vhd - _Default_ data memory (architecture-only!) |
â””mem/neorv32_imem.default.vhd - _Default_ instruction memory (architecture-only!) |
├mem/neorv32_dmem.default.vhd - _Default_ data memory (architecture-only) |
â””mem/neorv32_imem.default.vhd - _Default_ instruction memory (architecture-only) |
................................... |
|
[NOTE] |
The processor-internal instruction and data memories (IMEM and DMEM) are split into two design files each: |
a plain entity definition (`neorv32_*mem.entity.vhd`) and the actual architecture definition |
(`mem/neorv32_*mem.default.vhd`). The **default** architecture definitions from `rtl/core/mem` provide a _generic_ and |
(`mem/neorv32_*mem.default.vhd`). The `*.default.vhd` architecture definitions from `rtl/core/mem` provide a _generic_ and |
_platform independent_ memory design that (should) infers embedded memory blocks. You can replace/modify the architecture |
source file in order to use platform-specific features (like advanced memory resources) or to improve technology mapping |
and/or timing. |
/docs/datasheet/soc.adoc
1321,32 → 1321,35
:sectnums!: |
===== Indirect Boot |
|
The _indirect_ boot scenarios **1a** and **1b** use the processor-internal <<_bootloader>>. This general setup is enabled |
by setting the <<_int_bootloader_en>> generic to true, which will implement the processor-internal <<_bootloader_rom_bootrom>>. |
The _indirect_ boot scenarios **1a** and **1b** use 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 (via UART or from external SPI flash) and copies it to |
the beginning of the _instruction address space_ so the CPU can execute it. |
|
The bootloader provides several options to upload an executable (via UART or from external SPI flash) and store it to |
the _instruction address space_ so the CPU can execute it. Boot scenario **1a** uses the processor-internal IMEM |
Boot scenario **1a** uses the processor-internal IMEM |
(<<_mem_int_imem_en>> = _true_). This scenario implements the internal <<_instruction_memory_imem>> as non-initialized |
RAM so the bootloader can write the actual executable to it. |
RAM so the bootloader can copy the actual executable to it. |
|
Boot scenario **1b** uses a processor-external IMEM (<<_mem_int_imem_en>> = _false_) that is connected via the processor's |
bus interface. In this scenario the internal <<_instruction_memory_imem>> is not implemented at all and the bootloader will |
write the executable to the processor-external memory. |
copy the executable to the processor-external memory. Hence, the external memory has to be implemented as RAM. |
|
:sectnums!: |
===== Direct Boot |
|
The _direct_ boot scenarios **2a** and **2b** do not use the processor-internal bootloader. Hence, the <<_int_bootloader_en>> |
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 instruction address space after reset. A "pre-initialization mechanism is required |
in order to provide an executable _in_ memory. |
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 _in_ memory. |
|
Boot scenario **2a** uses the processor-internal IMEM (<<_mem_int_imem_en>> = _true_) that is implemented as _read-only memory_ |
in this scenario. It is pre-initialized (by the bitstream) with the actual application executable. |
in this scenario. It is pre-initialized (by the bitstream) with the actual application executable during synthesis. |
|
In contrast, boot scenario **2b** uses a processor-external IMEM (<<_mem_int_imem_en>> = _false_). In this scenario the |
system designer is responsible for providing a initialized external memory that contains the actual application to be executed. |
system designer is responsible for providing an initialized external memory that contains the actual application to be executed. |
If the external is not already initialized after reset, a simple ROM containing a "polling loop" can be implemented that is |
exited as soon as the application logic has finished initializing the memory with the acutal application code. |
|
|
|
/docs/datasheet/soc_buskeeper.adoc
13,6 → 13,7
| CPU interrupts: | none | |
|======================= |
|
|
**Theory of Operation** |
|
The Bus Keeper is a fundamental component of the processor's internal bus system that ensures correct bus operations |
30,35 → 31,25
|
In case of a bus access fault exception application software can evaluate the Bus Keeper's control register |
`NEORV32_BUSKEEPER.CTRL` to retrieve further details of the bus exception. The _BUSKEEPER_ERR_FLAG_ bit indicates |
that an actual bus access fault has occurred. The bit is sticky once set is automatically cleared when reading the |
`NEORV32_BUSKEEPER.CTRL` register. The _BUSKEEPER_ERR_TYPE_ indicated the tape or bus fault: |
that an actual bus access fault has occurred. The bit is sticky once set and is automatically cleared when reading or |
writing the `NEORV32_BUSKEEPER.CTRL` register. The _BUSKEEPER_ERR_TYPE_ indicated the tape of the bus fault: |
|
* _BUSKEEPER_ERR_TYPE_ = `0` - "Device Error": The bus access exception was cause by the memory-mapped device that |
has been accessed (the device asserted it's `err_o`). |
* _BUSKEEPER_ERR_TYPE_ = `1` - "Timeout Error": The bus access exception was caused by the Bus Keeper because the |
accessed memory-mapped device did not respond within the access time window. |
accessed memory-mapped device did not respond within the access time window. Note that this error type can also be raised |
by the optional timeout feature of the <<_processor_external_memory_interface_wishbone_axi4_lite>>). |
|
[NOTE] |
Bus access fault exceptions are also raised if a physical memory protection rule is violated. In this case |
the _BUSKEEPER_ERR_FLAG_ bit remains zero. |
|
Furthermore, application software can determine the source of the bus access fault via the _BUSKEEPER_ERR_SRC_ bit: |
|
* _BUSKEEPER_ERR_SRC_ = `0`: The error was cause during access via the <<_processor_external_memory_interface_wishbone_axi4_lite>>). |
* _BUSKEEPER_ERR_SRC_ = `1`: The error was cause during access to an processor-internal module. |
|
[NOTE] |
The Bus Keeper does not track **timeout errors** of processor-external accesses via the external memory bus interface. |
However, the external memory bus interface also provides an _optional_ and independent bus timeout feature |
(see section <<_processor_external_memory_interface_wishbone_axi4_lite>>). |
|
|
.BUSKEEPER register map (`struct NEORV32_BUSKEEPER`) |
[cols="<2,<2,<4,^1,<4"] |
[options="header",grid="all"] |
|======================= |
| Address | Name [C] | Bit(s), Name [C] | R/W | Function |
.3+<| `0xffffff7C` .3+<| `NEORV32_BUSKEEPER.CTRL` <|`0` _BUSKEEPER_ERR_TYPE_ ^| r/- <| Bus error type, valid if _BUSKEEPER_ERR_FLAG_ is set: `0`=device error, `1`=access timeout |
<|`1` _BUSKEEPER_ERR_SRC_ ^| r/- <| Error source: `0`=processor-internal, `1`=processor-external (via Wishbone bus interface) |
<|`31` _BUSKEEPER_ERR_FLAG_ ^| r/- <| Sticky error flag, clears after read |
.2+<| `0xffffff7C` .2+<| `NEORV32_BUSKEEPER.CTRL` <|`0` _BUSKEEPER_ERR_TYPE_ ^| r/- <| Bus error type, valid if _BUSKEEPER_ERR_FLAG_ is set: `0`=device error, `1`=access timeout |
<|`31` _BUSKEEPER_ERR_FLAG_ ^| r/- <| Sticky error flag, clears after read or write access |
|======================= |
/docs/datasheet/soc_neoled.adoc
163,19 → 163,20
data word being written to the TX buffer making busy wait concepts obsolete and allowing maximum refresh rates. |
|
|
**Interrupt** |
**NEOLED Interrupt** |
|
The NEOLED modules features a single interrupt that becomes pending based on the current TX buffer fill level. |
The interrupt can only become pending if the NEOLED module is enabled. The specific interrupt condition |
is configured via the _NEOLED_CTRL_IRQ_CONF_ in the control register `NEORV32_NEOLED.CTRL`. |
is configured via the _NEOLED_CTRL_IRQ_CONF_ bit in the unit's control register. |
|
If _NEOLED_CTRL_IRQ_CONF_ is cleared, an interrupt is generated whenever the TX FIFO is _less than half-full_. |
If _NEOLED_CTRL_IRQ_CONF_ is cleared, an interrupt is generated whenever the TX FIFO _becomes_ less than half-full. |
In this case software can write up to _IO_NEOLED_TX_FIFO_/2 new data words to `DATA` without checking the FIFO |
status flags. The interrupt request is cleared whenever the FIFO fill level is above _half-full_ level or if |
the NEOLED module is disabled. |
status flags. If _NEOLED_CTRL_IRQ_CONF_ is set, an interrupt is generated whenever the TX FIFO _becomes_ empty. |
|
If _NEOLED_CTRL_IRQ_CONF_ is set, an interrupt is generated whenever the TX FIFO is _empty_. The interrupt |
request is cleared again when the FIFO contains at least one data word. |
A pending interrupt request is cleared is cleared by any of the following operations: |
* write access to `NEORV32_NEOLED.DATA` (for example to send more LED data) |
* write access to `NEORV32_NEOLED.CTRL` |
* disabling the NEOLED module |
|
[NOTE] |
The _NEOLED_CTRL_IRQ_CONF_ is hardwired to one if _IO_NEOLED_TX_FIFO_ = 1 (-> IRQ if FIFO is empty). |
/docs/datasheet/soc_slink.adoc
79,11 → 79,14
The NEORV32 processor ensures that _any_ CPU access to memory-mapped devices (including the SLINK module) |
will **time out** after a certain number of cycles (see section <<_bus_interface>>). |
Hence, blocking access to a stream link that does not complete within a certain amount of cycles will |
raise a _store bus access exception_ when writing a _full_ TX link or a _load bus access exception_ when reading |
from an _empty_ RX link. Hence, this concept should only be used when evaluating the half-full FIFO condition |
(for example via the SLINK interrupts) before actual accessing links. |
raise a _store bus access exception_ when writing to a _full_ TX link's FIFO or a _load bus access exception_ |
when reading from an _empty_ RX 's FIFO. Hence, this concept should only be used when evaluating the half-full |
FIFO condition (for example via the SLINK interrupts) before actual accessing links. |
|
[NOTE] |
There is no RX FIFO overflow mechanism available yet. |
|
|
**Non-Blocking Link Access** |
|
For a non-blocking link access concept, the FIFO status flags in `STATUS` need to be checked _before_ |
118,43 → 121,52
The SLINK handshake protocol is compatible with the https://developer.arm.com/documentation/ihi0051/a/Introduction/About-the-AXI4-Stream-protocol[AXI4-Stream] base protocol. |
|
|
**Interrupts** |
**SLINK Interrupts** |
|
The stream interface provides two independent interrupts that are _globally_ driven by the RX and TX link's |
FIFO fill level status. Each RX and TX link provides an individual interrupt enable flag and an individual |
interrupt type flag that allows to configure interrupts only for certain (or all) links and for application- |
specific interrupt conditions. The interrupt configuration is done using the `NEORV32_SLINK.IRQ` register. |
specific FIFO conditions. The interrupt configuration is done using the `NEORV32_SLINK.IRQ` register. |
Any interrupt can only become pending if the SLINK module is enabled at all. |
|
[NOTE] |
There is no RX FIFO overflow mechanism available yet. |
|
The current FIFO fill-level of a specific **RX link** can only raise an interrupt request if it's interrupt enable flag |
_SLINK_IRQ_RX_EN_ is set. Vice versa, the current FIFO fill-level of a specific **TX link** can only raise an interrupt |
request if it's interrupt enable flag _SLINK_IRQ_TX_EN_ is set. |
|
The **RX link's** _SLINK_IRQ_RX_MODE_ flags define the FIFO fill-level condition for raising an RX interrupt request: |
* If a link's interrupt mode flag is `1` an IRQ is generated when the link's FIFO is _not empty_ ("RX data available"). |
* If a link's interrupt mode flag is `0` an IRQ is generated when the link's FIFO is _at least half-full_ ("time to get data from RX FIFO to prevent overflow"). |
* If a link's interrupt mode flag is `1` an IRQ is generated when the link's FIFO _becomes_ not empty ("RX data available"). |
* If a link's interrupt mode flag is `0` an IRQ is generated when the link's FIFO _becomes_ at least half-full ("time to get data from RX FIFO to prevent overflow"). |
|
The **TX link's** _SLINK_IRQ_TX_MODE_ flags define the FIFO fill-level condition for raising an TX interrupt request: |
* If a link's interrupt mode flag is `1` an IRQ is generated when the link's FIFO is _not full_ ("space left in FIFO for new TX data"). |
* If a link's interrupt mode flag is `0` an IRQ is generated when the link's FIFO is _less than half-full_ ("SW can send _SLINK_TX_FIFO_/2 data words without checking any flags"). |
* If a link's interrupt mode flag is `1` an IRQ is generated when the link's FIFO _becomes_ not full ("space left in FIFO for new TX data"). |
* If a link's interrupt mode flag is `0` an IRQ is generated when the link's FIFO _becomes_ less than half-full ("SW can send _SLINK_TX_FIFO_/2 data words without checking any flags"). |
|
[NOTE] |
If _SLINK_RX_FIFO_ is 1 the _SLINK_IRQ_RX_MODE_ bits are hardwired to one. |
If _SLINK_TX_FIFO_ is 1 the _SLINK_IRQ_TX_MODE_ bits are hardwired to one. |
[IMPORTANT] |
The interrupt configuration register `NEORV32_SLINK.IRQ` should we written _before_ the SLINK |
module is actually enabled. |
|
[NOTE] |
There is no RX FIFO overflow mechanism available yet. |
If _SLINK_RX_FIFO_ is 1 all _SLINK_IRQ_RX_MODE_ bits are hardwired to one. |
If _SLINK_TX_FIFO_ is 1 all _SLINK_IRQ_TX_MODE_ bits are hardwired to one. |
|
If _any_ configured interrupt condition is fulfilled, the according global SLINK RX / SLINK TX CPU |
interrupt becomes pending. |
If the interrupt enable flags of several links are set, the interrupt service handler has to evaluate the SLINK |
status register is order to detect which link(s) caused the interrupt. |
A **pending RX interrupt** request is cleared by any of the following operations: |
* read access to any `NEORV32_SLINK.DATA` (for example to read incoming data) |
* write access to `NEORV32_SLINK.CTRL` |
* disabling the SLINK module |
|
[NOTE] |
If the programmed interrupt condition is fulfilled, the corresponding IRQ will become _pending_ until |
the causing interrupt conditions is resolved (for example by reading data from the according RX FIFO). |
A **pending TX interrupt** request is cleared by any of the following operations: |
* write access any `NEORV32_SLINK.DATA` (for example to send more data) |
* write access to `NEORV32_SLINK.CTRL` |
* disabling the SLINK module |
|
[TIP] |
A dummy write to to the control register (i.e. `NEORV32_SLINK.DATA = NEORV32_SLINK.DATA`) |
can be executed to acknowledge any interrupt. |
|
|
.SLINK register map (`struct NEORV32_SLINK`) |
[cols="^4,<5,^2,^2,<14"] |
[options="header",grid="all"] |
168,9 → 180,9
<| `3:0` _SLINK_CTRL_RX_NUM3_ : _SLINK_CTRL_RX_NUM0_ ^| r/- <| Number of implemented RX links |
| `0xfffffec4` | - |`31:0` | r/- | _reserved_ |
.4+<| `0xfffffec8` .4+<| `NEORV32_SLINK.IRQ` <|`31:24` _SLINK_IRQ_RX_EN_MSB_ : _SLINK_IRQ_RX_EN_LSB_ ^| r/w <| RX interrupt enable for link 7..0 |
<|`23:16` _SLINK_IRQ_RX_MODE_MSB_ : _SLINK_IRQ_RX_MODE_LSB_ ^| r/w <| RX IRQ mode for link 7..0: `0` = FIFO at least half-full; `1` = FIFO not empty |
<|`23:16` _SLINK_IRQ_RX_MODE_MSB_ : _SLINK_IRQ_RX_MODE_LSB_ ^| r/w <| RX IRQ mode for link 7..0: `0` = FIFO rises above half-full; `1` = FIFO not empty |
<|`15:8` _SLINK_IRQ_TX_EN_MSB_ : _SLINK_IRQ_TX_EN_LSB_ ^| r/w <| TX interrupt enable for link 7..0 |
<|`7:0` _SLINK_IRQ_TX_MODE_MSB_ : _SLINK_IRQ_TX_MODE_LSB_ ^| r/w <| TX IRQ mode for link 7..0: `0` = FIFO less than half-full; `1` = FIFO not full |
<|`7:0` _SLINK_IRQ_TX_MODE_MSB_ : _SLINK_IRQ_TX_MODE_LSB_ ^| r/w <| TX IRQ mode for link 7..0: `0` = FIFO falls below half-full; `1` = FIFO not full |
| `0xfffffeec` | - |`31:0` | r/- | _reserved_ |
.4+<| `0xfffffed0` .4+<| `NEORV32_SLINK.STATUS` <| `31:24` _SLINK_STATUS_TX7_HALF_ : _SLINK_STATUS_TX0_HALF_ ^| r/- <| TX link 7..0 FIFO fill level is >= half-full |
<| `23:16` _SLINK_STATUS_RX7_HALF_ : _SLINK_STATUS_RX0_HALF_ ^| r/- <| RX link 7..0 FIFO fill level is >= half-full |
/docs/datasheet/soc_spi.adoc
107,11 → 107,18
|
**SPI Interrupt** |
|
The SPI module provides a single interrupt to signal "ready for new transmission" to the CPU. Whenever the SPI |
module is currently idle (and enabled), the interrupt request is active. A pending interrupt request is cleared |
by triggering a new SPI transmission or by disabling the SPI module. |
The SPI module provides a single interrupt to signal "transmission done" to the CPU. Whenever the SPI |
module completes the current transfer operation, the interrupt request is set. A pending interrupt request |
is cleared by any of the following operations: |
* read or write access to `NEORV32_SPI.DATA` (for example to trigger a new transmission) |
* write access to `NEORV32_SPI.CTRL` |
* disabling the SPI module |
|
[TIP] |
A dummy read from `NEORV32_SPI.DATA` can be executed to acknowledge the interrupt without affecting data |
or the state of the SPI module. |
|
|
.SPI register map (`struct NEORV32_SPI`) |
[cols="<2,<2,<4,^1,<7"] |
[options="header",grid="all"] |
/docs/datasheet/soc_trng.adoc
13,65 → 13,36
| CPU interrupts: | none | |
|======================= |
|
|
**Theory of Operation** |
|
The NEORV32 true random number generator provides _physical true random numbers_ for your application. |
Instead of using a pseudo RNG like a LFSR, the TRNG of the processor uses a simple, straight-forward ring |
oscillator as physical entropy source. Hence, voltage and thermal fluctuations are used to provide true |
physical random data. |
The NEORV32 true random number generator provides _physical_ true random numbers. |
Instead of using a pseudo RNG like a LFSR, the TRNG uses a simple, straight-forward ring |
oscillator concept as physical entropy source. Hence, voltage, thermal and also semiconductor manufacturing |
fluctuations are used to provide a true physical entropy source. |
|
The TRNG is based on the neoTRNG[https://github.com/stnolting/neoTRNG], which is a "spin-off project" of the |
NEORV32 processor. The TRNG uses the default neoTRNG configuration, which showed very good results in the |
`dieharder` battery of random number tests. More detailed information about the neoTRNG, it's architecture and a |
detailed evaluation of the random number quality can be found it it's repository: https://github.com/stnolting/neoTRNG |
|
[NOTE] |
The TRNG features a platform independent architecture without FPGA-specific primitives, macros or |
attributes. |
attributes so it can be synthesized for _any_ FPGA. |
|
**Architecture** |
|
The NEORV32 TRNG is based on simple ring oscillators, which are implemented as an inverter chain with |
an odd number of inverters. A **latch** is used to decouple each individual inverter. Basically, this architecture |
is some king of asynchronous LFSR. |
|
The output of several ring oscillators are synchronized using two registers and are XORed together. The |
resulting output is de-biased using a von-Neumann randomness extractor. This de-biased output is further |
processed by a simple 8-bit Fibonacci LFSR to improve whitening. After at least 8 clock cycles the state of |
the LFSR is sampled and provided as final data output. |
|
To prevent the synthesis tool from doing logic optimization and thus, removing all but one inverter, the |
TRNG uses simple latches to decouple an inverter and its actual output. The latches are reset when the |
TRNG is disabled and are enabled one by one by a "real" shift register when the TRNG is activated. This |
construct can be synthesized for any FPGA platform. Thus, the NEORV32 TRNG provides a platform |
independent architecture. |
|
**TRNG Configuration** |
|
The TRNG uses several ring-oscillators, where the next oscillator provides a slightly longer chain (more |
inverters) than the one before. This increment is constant for all implemented oscillators. This setup can be |
customized by modifying the "Advanced Configuration" constants in the TRNG's VHDL file: |
|
* The `num_roscs_c` constant defines the total number of ring oscillators in the system. num_inv_start_c |
defines the number of inverters used by the first ring oscillators (has to be an odd number). Each additional |
ring oscillator provides `num_inv_inc_c` more inverters that the one before (has to be an even number). |
* The LFSR-based post-processing can be deactivated using the `lfsr_en_c` constant. The polynomial tap |
mask of the LFSR can be customized using `lfsr_taps_c`. |
|
**Using the TRNG** |
|
The TRNG features a single register for status and data access. When the _TRNG_CTRL_EN_ control register (`CTRL`) |
bit is set, the TRNG is enabled and starts operation. As soon as the _TRNG_CTRL_VALID_ bit is set, the currently |
sampled 8-bit random data byte can be obtained from the lowest 8 bits of the `CTRL` register |
(_TRNG_CTRL_DATA_MSB_ : _TRNG_CTRL_DATA_LSB_). The _TRNG_CTRL_VALID_ bit is automatically cleared |
when reading the control register. |
(_TRNG_CTRL_DATA_MSB_ : _TRNG_CTRL_DATA_LSB_). These bits always keep the latest valid data obtained from the TRNG |
entropy source. The _TRNG_CTRL_VALID_ bit is automatically cleared when reading the control register. |
|
[IMPORTANT] |
The TRNG needs at least 8 clock cycles to generate a new random byte. During this sampling time |
the current output random data is kept stable in the output register until a valid sampling of the new byte has |
completed. |
[NOTE] |
The TRNG core does not provide a dedicated reset. In order to ensure correct operations, the TRNG should be |
disabled (=reset) by clearing the _TRNG_CTRL_EN_ and waiting some milliseconds before re-enabling it. |
|
Randomness "Quality" |
I have not verified the quality of the generated random numbers (for example using NIST test suites). The |
quality is highly effected by the actual configuration of the TRNG and the resulting FPGA mapping/routing. |
However, generating larger histograms of the generated random number shows an equal distribution (binary |
average of the random numbers = 127). A simple evaluation test/demo program can be found in |
`sw/example/demo_trng`. |
|
.TRNG register map (`struct NEORV32_TRNG`) |
[cols="<2,<2,<4,^1,<7"] |
78,7 → 49,7
[options="header",grid="all"] |
|======================= |
| Address | Name [C] | Bit(s), Name [C] | R/W | Function |
.3+<| `0xffffffb8` .3+<| `NEORV32_TRNG.CTRL` <|`7:0` _TRNG_CTRL_DATA_MSB_ : _TRNG_CTRL_DATA_MSB_ ^| r/- <| 8-bit random data output |
<|`30` _TRNG_CTRL_EN_ ^| r/w <| TRNG enable |
<|`31` _TRNG_CTRL_VALID_ ^| r/- <| random data output is valid when set |
.3+<| `0xffffffb8` .3+<| `NEORV32_TRNG.CTRL` <|`7:0` _TRNG_CTRL_DATA_MSB_ : _TRNG_CTRL_DATA_MSB_ ^| r/- <| 8-bit random data |
<|`30` _TRNG_CTRL_EN_ ^| r/w <| TRNG enable |
<|`31` _TRNG_CTRL_VALID_ ^| r/- <| random data is valid when set |
|======================= |
/docs/datasheet/soc_twi.adoc
21,8 → 21,8
clock line `twi_scl_io`) - despite of the number of connected devices - it allows easy interconnections of |
several peripheral nodes. |
|
The NEORV32 TWI implements a **TWI controller**. It features "clock stretching" (if enabled via the control |
register), so a slow peripheral can halt the transmission by pulling the SCL line low. Currently, **no multi-controller |
The NEORV32 TWI implements a **TWI controller**. It supports "clock so a slow peripheral can halt |
the transmission by pulling the SCL line low. Currently, **no multi-controller |
support** is available. Also, the NEORV32 TWI unit cannot operate in peripheral mode. |
|
The TWI is enabled via the _TWI_CTRL_EN_ bit in the `CTRL` control register. The user program can start / stop a |
70,27 → 70,34
_**f~SCL~**_ = _f~main~[Hz]_ / (4 * `clock_prescaler`) |
|
|
**Interrupt** |
**TWI Interrupt** |
|
The TWI module provides a single interrupt to signal _idle state_ (= read for new transmission) to the CPU. Whenever TWI SPI module |
is currently idle (and enabled), the interrupt request is active. A pending interrupt request is cleared |
by triggering a new TWI transmission or by disabling the device. |
The SPI module provides a single interrupt to signal "operation done" to the CPU. Whenever the TWI |
module completes the current operation (generate stop condition, generate start conditions or transfer byte), |
the interrupt request is set. A pending interrupt request is cleared is cleared by any of |
the following operations: |
* read or write access to `NEORV32_TWI.DATA` (for example to trigger a new transmission) |
* write access to `NEORV32_TWI.CTRL` |
* disabling the TWI module |
|
[TIP] |
A dummy read from `NEORV32_TWI.DATA` can be executed to acknowledge the interrupt without affecting data |
or the state of the TWI module. |
|
|
.TWI register map (`struct NEORV32_TWI`) |
[cols="<2,<2,<4,^1,<7"] |
[options="header",grid="all"] |
|======================= |
| Address | Name [C] | Bit(s), Name [C] | R/W | Function |
.10+<| `0xffffffb0` .10+<| `NEORV32_TWI.CTRL` <|`0` _TWI_CTRL_EN_ ^| r/w <| TWI enable |
<|`1` _TWI_CTRL_START_ ^| r/w <| generate START condition |
<|`2` _TWI_CTRL_STOP_ ^| r/w <| generate STOP condition |
<|`3` _TWI_CTRL_PRSC0_ ^| r/w .3+<| 3-bit clock prescaler select |
<|`4` _TWI_CTRL_PRSC1_ ^| r/w |
<|`5` _TWI_CTRL_PRSC2_ ^| r/w |
<|`6` _TWI_CTRL_MACK_ ^| r/w <| generate controller ACK for each transmission ("MACK") |
<|`7` _TWI_CTRL_CKSTEN_ ^| r/w <| allow clock-stretching by peripherals when set |
<|`30` _TWI_CTRL_ACK_ ^| r/- <| ACK received when set |
<|`31` _TWI_CTRL_BUSY_ ^| r/- <| transfer/START/STOP in progress when set |
.9+<| `0xffffffb0` .9+<| `NEORV32_TWI.CTRL` <|`0` _TWI_CTRL_EN_ ^| r/w <| TWI enable |
<|`1` _TWI_CTRL_START_ ^| r/w <| generate START condition |
<|`2` _TWI_CTRL_STOP_ ^| r/w <| generate STOP condition |
<|`3` _TWI_CTRL_PRSC0_ ^| r/w .3+<| 3-bit clock prescaler select |
<|`4` _TWI_CTRL_PRSC1_ ^| r/w |
<|`5` _TWI_CTRL_PRSC2_ ^| r/w |
<|`6` _TWI_CTRL_MACK_ ^| r/w <| generate controller ACK for each transmission ("MACK") |
<|`30` _TWI_CTRL_ACK_ ^| r/- <| ACK received when set |
<|`31` _TWI_CTRL_BUSY_ ^| r/- <| transfer/START/STOP in progress when set |
| `0xffffffb4` | `NEORV32_TWI.DATA` |`7:0` _TWI_DATA_MSB_ : TWI_DATA_LSB_ | r/w | receive/transmit data |
|======================= |
/docs/datasheet/soc_uart.adoc
108,29 → 108,40
received character and is cleared by reading the `DATA` register. |
|
|
**Interrupts** |
**UART Interrupts** |
|
UART0 features two independent interrupt for signaling certain RX and TX conditions. The behavior of these interrupts differ |
based on the configured FIFO size. If the according FIFO size is greater than 1, the _UART_CTRL_RX_IRQ_ and _UART_CTRL_TX_IRQ_ |
`CTRL` flags allow a more fine-grained IRQ configuration. |
UART0 features two independent interrupt for signaling certain RX and TX conditions. The behavior of these conditions differs |
based on the configured FIFO sizes. If the according FIFO size is greater than 1, the _UART_CTRL_RX_IRQ_ and _UART_CTRL_TX_IRQ_ |
`CTRL` flags allow a more fine-grained IRQ configuration. An interrupt can only become pending if the according interrupt |
condition is fulfilled and the UART is enabled at all. |
|
* If _UART0_RX_FIFO_ is exactly 1, the RX interrupt becomes pending as soon as there is data available in the RX FIFO |
(-> _UART_CTRL_RX_EMPTY_ clears). This flag is hardwired to `0` if _UART0_RX_FIFO_ = 1. |
* If _UART0_TX_FIFO_ is exactly 1, the TX interrupt becomes pending as soon as there is a free entry left in the TX FIFO |
(-> _UART_CTRL_TX_FULL_ clears). This flag is hardwired to `0` if _UART0_RX_FIFO_ = 1. |
* If _UART0_RX_FIFO_ is exactly 1, the RX interrupt goes pending when data _becomes_ available in the RX FIFO |
(-> _UART_CTRL_RX_EMPTY_ clears). _UART_CTRL_RX_IRQ_ is hardwired to `0` in this case. |
* If _UART0_TX_FIFO_ is exactly 1, the TX interrupt goes pending when at least one entry in the TX FIFO _becomes_ free |
(-> _UART_CTRL_TX_FULL_ clears). _UART_CTRL_TX_IRQ_ is hardwired to `0` in this case. |
|
* If _UART0_RX_FIFO_ is greater than 1: If _UART_CTRL_RX_IRQ_ is `0` the RX interrupt becomes pending as soon as there is data |
available in the RX FIFO (-> _UART_CTRL_RX_EMPTY_ clears). If _UART_CTRL_RX_IRQ_ is `1` the RX interrupt becomes pending as soon as |
the RX FIFO is at least half-full (-> _UART_CTRL_RX_HALF_ sets). |
* If _UART0_TX_FIFO_ is greater than 1: If _UART_CTRL_TX_IRQ_ is `0` the TX interrupt becomes pending as soon as there is a free |
entry left in the TX FIFO (-> _UART_CTRL_TX_FULL_ clears). If _UART_CTRL_TX_IRQ_ is `1` the TX interrupt becomes pending as soon as |
the RX FIFO is less than half-full (-> _UART_CTRL_TX_HALF_ clears). |
* If _UART0_RX_FIFO_ is greater than 1: If _UART_CTRL_RX_IRQ_ is `0` the RX interrupt goes pending when data _becomes_ |
available in the RX FIFO (-> _UART_CTRL_RX_EMPTY_ clears). If _UART_CTRL_RX_IRQ_ is `1` the RX interrupt becomes pending |
the RX FIFO _becomes_ at least half-full (-> _UART_CTRL_RX_HALF_ sets). |
* If _UART0_TX_FIFO_ is greater than 1: If _UART_CTRL_TX_IRQ_ is `0` the TX interrupt goes pending when at least one entry |
in the TX FIFO _becomes_ free (-> _UART_CTRL_TX_FULL_ clears). If _UART_CTRL_TX_IRQ_ is `1` the TX interrupt goes pending |
when the RX FIFO _becomes_ less than half-full (-> _UART_CTRL_TX_HALF_ clears). |
|
An interrupt can only become pending if the according interrupt condition is fulfilled and the UART is enabled at all. |
A pending interrupt is removed by resolving the interrupt-triggering conditions (for example by reading data from the |
more-than-half-full RX FIFO). |
A **pending RX interrupt** request is cleared by any of the following operations: |
* read access to `NEORV32_UART0.DATA` (for example to read incoming data) |
* write access to `NEORV32_UART0.CTRL` |
* disabling the UART module |
|
A **pending TX interrupt** request is cleared by any of the following operations: |
* write access to `NEORV32_UART0.DATA` (for example to send more data) |
* write access to `NEORV32_UART0.CTRL` |
* disabling the UART module |
|
[TIP] |
A dummy write to to the control register (i.e. `NEORV32_UART0.DATA = NEORV32_UART0.DATA`) |
can be executed to acknowledge any interrupt. |
|
|
**Simulation Mode** |
|
The default UART0 operation will transmit any data written to the `DATA` register via the serial TX line at |
/docs/datasheet/software.adoc
588,9 → 588,18
|
[source] |
---- |
<RTE> Illegal instruction @0x000002d6, MTVAL=0x00001537 </RTE> |
<RTE> Illegal instruction @ PC=0x000002d6, MTVAL=0x00001537 </RTE> |
---- |
|
For bus access faults the RTE also outputs the error code from the <<_internal_bus_monitor_buskeeper>> |
to show the cause of the access fault. Two example are shown below. |
|
[source] |
---- |
<RTE> Load access fault [TIMEOUT_ERR] @ PC=0x00000150, MTVAL=0xFFFFFF70 </RTE> |
<RTE> Store access fault [DEVICE_ERR] @ PC=0x00000162, MTVAL=0xF0000000 </RTE> |
---- |
|
To install the **actual application's trap handlers** the NEORV32 RTE provides functions for installing and |
un-installing trap handler for each implemented exception/interrupt source. |
|
/docs/figures/riscv_logo_small.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
docs/figures/riscv_logo_small.png
Property changes :
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: docs/userguide/content.adoc
===================================================================
--- docs/userguide/content.adoc (revision 67)
+++ docs/userguide/content.adoc (revision 68)
@@ -147,8 +147,8 @@
.Internal Memories
[IMPORTANT]
-For a _general_ first setup (technology-independent) use the _default_ memory architectures for the internal memories
-(IMEM and DMEM). These are located in `rtl/core/mem`, so **make sure to add the files from `rtl/core/mem` to your project, too**. +
+For a _general_ first setup (technology-independent) use the `*.default.vhd` memory architectures for the internal memories
+(IMEM and DMEM). These are located in `rtl/core/mem` so make sure to add the files to your project, too. +
+
If synthesis cannot efficiently map those default memory descriptions to the available memory resources, you can later replace the
default memory architectures by optimized platform-specific memory architectures. **Example:** The `setups/radiant/UPduino_v3`
@@ -380,9 +380,11 @@
[start=1]
. Connect the primary UART (UART0) interface of your FPGA board to a serial port of your host computer.
-. Start a terminal program. In this tutorial, I am using TeraTerm for Windows. You can download it fore free
-from https://ttssh2.osdn.jp/index.html.en
+. Start a terminal program. In this tutorial, I am using TeraTerm for Windows. You can download it for free
+from https://ttssh2.osdn.jp/index.html.en . On Linux you could use GTKTerm, which you can get here
+https://github.com/Jeija/gtkterm.git (or install via your package manager).
+
[NOTE]
_Any_ terminal program that can connect to a serial port should work. However, make sure the program
can transfer data in _raw_ byte mode without any protocol overhead around it.
/docs/attrs.adoc
2,7 → 2,7
:email: stnolting@gmail.com |
:keywords: neorv32, risc-v, riscv, fpga, soft-core, vhdl, microcontroller, cpu, soc, processor, gcc, openocd, gdb |
:description: A size-optimized, customizable and open-source full-scale 32-bit RISC-V soft-core CPU and SoC written in platform-independent VHDL. |
:revnumber: v1.6.3 |
:revnumber: v1.6.4 |
:doctype: book |
:sectnums: |
:stem: |
/rtl/core/mem/README.md
0,0 → 1,14
# Processor Memory Source Files |
|
This folder provides the architecture-only VHDL sources for the processor-internal memories |
(instruction memory "IMEM", data memory "DMEM"). Different implementations are available - but |
only **one** version of each (IMEM and DMEM) has to be added as actual source files. |
|
For the first implementation the `*.default.vhd` files should be selected. The HDL style for describing |
memories used by these files has proven **platform-independence** across several FPGA architectures and toolchains. |
|
If synthesis fails to infer actual block RAM resources from these default files, try the legacy `*.cyclone2.vhd` files, which |
provide a different HDL style. These files are intended for legacy support of older Intel/Altera Quartus versions (13.0 and older). However, |
these files do **not** use platform-specific macros or primitives - so they might also work for other FPGAs and toolchains. |
|
:warning: Make sure to add the selected files from this folder also to the `neorv32` design library. |
/rtl/core/mem/neorv32_dmem.cyclone2.vhd
0,0 → 1,130
-- ################################################################################################# |
-- # << NEORV32 - Processor-internal data memory (DMEM) >> # |
-- # ********************************************************************************************* # |
-- # BSD 3-Clause License # |
-- # # |
-- # Copyright (c) 2021, Stephan Nolting. All rights reserved. # |
-- # # |
-- # Redistribution and use in source and binary forms, with or without modification, are # |
-- # permitted provided that the following conditions are met: # |
-- # # |
-- # 1. Redistributions of source code must retain the above copyright notice, this list of # |
-- # conditions and the following disclaimer. # |
-- # # |
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of # |
-- # conditions and the following disclaimer in the documentation and/or other materials # |
-- # provided with the distribution. # |
-- # # |
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to # |
-- # endorse or promote products derived from this software without specific prior written # |
-- # permission. # |
-- # # |
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS # |
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # |
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # |
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # |
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # |
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # |
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # |
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # |
-- # OF THE POSSIBILITY OF SUCH DAMAGE. # |
-- # ********************************************************************************************* # |
-- # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
-- ################################################################################################# |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
library neorv32; |
use neorv32.neorv32_package.all; |
|
architecture neorv32_dmem_rtl of neorv32_dmem is |
|
-- IO space: module base address -- |
constant hi_abb_c : natural := 31; -- high address boundary bit |
constant lo_abb_c : natural := index_size_f(DMEM_SIZE); -- low address boundary bit |
|
-- local signals -- |
signal acc_en : std_ulogic; |
signal rdata : std_ulogic_vector(31 downto 0); |
signal rden : std_ulogic; |
signal addr : std_ulogic_vector(index_size_f(DMEM_SIZE/4)-1 downto 0); |
signal addr_ff : std_ulogic_vector(index_size_f(DMEM_SIZE/4)-1 downto 0); |
|
-- -------------------------------------------------------------------------------------------------------------- -- |
-- The memory (RAM) is built from 4 individual byte-wide memories b0..b3, since some synthesis tools have -- |
-- problems with 32-bit memories that provide dedicated byte-enable signals AND/OR with multi-dimensional arrays. -- |
-- -------------------------------------------------------------------------------------------------------------- -- |
|
-- RAM - not initialized at all -- |
signal mem_ram_b0 : mem8_t(0 to DMEM_SIZE/4-1); |
signal mem_ram_b1 : mem8_t(0 to DMEM_SIZE/4-1); |
signal mem_ram_b2 : mem8_t(0 to DMEM_SIZE/4-1); |
signal mem_ram_b3 : mem8_t(0 to DMEM_SIZE/4-1); |
|
-- read data -- |
signal mem_ram_b0_rd, mem_ram_b1_rd, mem_ram_b2_rd, mem_ram_b3_rd : std_ulogic_vector(7 downto 0); |
|
begin |
|
-- Sanity Checks -------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
assert false report "NEORV32 PROCESSOR CONFIG NOTE: Using CYCLONE-2-optimized HDL style DMEM." severity note; |
assert false report "NEORV32 PROCESSOR CONFIG NOTE: Implementing processor-internal DMEM (RAM, " & natural'image(DMEM_SIZE) & " bytes)." severity note; |
|
|
-- Access Control ------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
acc_en <= '1' when (addr_i(hi_abb_c downto lo_abb_c) = DMEM_BASE(hi_abb_c downto lo_abb_c)) else '0'; |
addr <= addr_i(index_size_f(DMEM_SIZE/4)+1 downto 2); -- word aligned |
|
|
-- Memory Access -------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
mem_access: process(clk_i) |
begin |
if rising_edge(clk_i) then |
addr_ff <= addr; |
if (acc_en = '1') then -- reduce switching activity when not accessed |
if (wren_i = '1') and (ben_i(0) = '1') then -- byte 0 |
mem_ram_b0(to_integer(unsigned(addr))) <= data_i(07 downto 00); |
end if; |
if (wren_i = '1') and (ben_i(1) = '1') then -- byte 1 |
mem_ram_b1(to_integer(unsigned(addr))) <= data_i(15 downto 08); |
end if; |
if (wren_i = '1') and (ben_i(2) = '1') then -- byte 2 |
mem_ram_b2(to_integer(unsigned(addr))) <= data_i(23 downto 16); |
end if; |
if (wren_i = '1') and (ben_i(3) = '1') then -- byte 3 |
mem_ram_b3(to_integer(unsigned(addr))) <= data_i(31 downto 24); |
end if; |
end if; |
end if; |
end process mem_access; |
|
-- sync(!) read - alternative HDL style -- |
mem_ram_b0_rd <= mem_ram_b0(to_integer(unsigned(addr_ff))); |
mem_ram_b1_rd <= mem_ram_b1(to_integer(unsigned(addr_ff))); |
mem_ram_b2_rd <= mem_ram_b2(to_integer(unsigned(addr_ff))); |
mem_ram_b3_rd <= mem_ram_b3(to_integer(unsigned(addr_ff))); |
|
|
-- Bus Feedback --------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
bus_feedback: process(clk_i) |
begin |
if rising_edge(clk_i) then |
rden <= acc_en and rden_i; |
ack_o <= acc_en and (rden_i or wren_i); |
end if; |
end process bus_feedback; |
|
-- pack -- |
rdata <= mem_ram_b3_rd & mem_ram_b2_rd & mem_ram_b1_rd & mem_ram_b0_rd; |
|
-- output gate -- |
data_o <= rdata when (rden = '1') else (others => '0'); |
|
|
end neorv32_dmem_rtl; |
/rtl/core/mem/neorv32_dmem.default.vhd
69,7 → 69,7
|
-- Sanity Checks -------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
assert false report "NEORV32 PROCESSOR CONFIG NOTE: Using default platform-agnostic DMEM." severity note; |
assert false report "NEORV32 PROCESSOR CONFIG NOTE: Using DEFAULT platform-agnostic DMEM." severity note; |
assert false report "NEORV32 PROCESSOR CONFIG NOTE: Implementing processor-internal DMEM (RAM, " & natural'image(DMEM_SIZE) & " bytes)." severity note; |
|
|
/rtl/core/mem/neorv32_imem.cyclone2.vhd
0,0 → 1,176
-- ################################################################################################# |
-- # << NEORV32 - Processor-internal instruction memory (IMEM) >> # |
-- # ********************************************************************************************* # |
-- # This memory optionally includes the in-place executable image of the application. See the # |
-- # processor's documentary to get more information. # |
-- # ********************************************************************************************* # |
-- # BSD 3-Clause License # |
-- # # |
-- # Copyright (c) 2021, Stephan Nolting. All rights reserved. # |
-- # # |
-- # Redistribution and use in source and binary forms, with or without modification, are # |
-- # permitted provided that the following conditions are met: # |
-- # # |
-- # 1. Redistributions of source code must retain the above copyright notice, this list of # |
-- # conditions and the following disclaimer. # |
-- # # |
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of # |
-- # conditions and the following disclaimer in the documentation and/or other materials # |
-- # provided with the distribution. # |
-- # # |
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to # |
-- # endorse or promote products derived from this software without specific prior written # |
-- # permission. # |
-- # # |
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS # |
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # |
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # |
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # |
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # |
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # |
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # |
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # |
-- # OF THE POSSIBILITY OF SUCH DAMAGE. # |
-- # ********************************************************************************************* # |
-- # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
-- ################################################################################################# |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
library neorv32; |
use neorv32.neorv32_package.all; |
use neorv32.neorv32_application_image.all; -- this file is generated by the image generator |
|
architecture neorv32_imem_rtl of neorv32_imem is |
|
-- IO space: module base address -- |
constant hi_abb_c : natural := 31; -- high address boundary bit |
constant lo_abb_c : natural := index_size_f(IMEM_SIZE); -- low address boundary bit |
|
-- local signals -- |
signal acc_en : std_ulogic; |
signal rdata : std_ulogic_vector(31 downto 0); |
signal rden : std_ulogic; |
signal addr : std_ulogic_vector(index_size_f(IMEM_SIZE/4)-1 downto 0); |
signal addr_ff : std_ulogic_vector(index_size_f(IMEM_SIZE/4)-1 downto 0); |
|
-- --------------------------- -- |
-- IMEM as pre-initialized ROM -- |
-- --------------------------- -- |
|
-- application (image) size in bytes -- |
constant imem_app_size_c : natural := (application_init_image'length)*4; |
|
-- ROM - initialized with executable code -- |
constant mem_rom : mem32_t(0 to IMEM_SIZE/4-1) := mem32_init_f(application_init_image, IMEM_SIZE/4); |
|
-- read data -- |
signal mem_rom_rd : std_ulogic_vector(31 downto 0); |
|
-- -------------------------------------------------------------------------------------------------------------- -- |
-- The memory (RAM) is built from 4 individual byte-wide memories b0..b3, since some synthesis tools have -- |
-- problems with 32-bit memories that provide dedicated byte-enable signals AND/OR with multi-dimensional arrays. -- |
-- -------------------------------------------------------------------------------------------------------------- -- |
|
-- RAM - not initialized at all -- |
signal mem_ram_b0 : mem8_t(0 to IMEM_SIZE/4-1); |
signal mem_ram_b1 : mem8_t(0 to IMEM_SIZE/4-1); |
signal mem_ram_b2 : mem8_t(0 to IMEM_SIZE/4-1); |
signal mem_ram_b3 : mem8_t(0 to IMEM_SIZE/4-1); |
|
-- read data -- |
signal mem_b0_rd, mem_b1_rd, mem_b2_rd, mem_b3_rd : std_ulogic_vector(7 downto 0); |
|
begin |
|
-- Sanity Checks -------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
assert false report "NEORV32 PROCESSOR CONFIG NOTE: Using CYCLONE-2-optimized HDL style IMEM." severity note; |
assert not (IMEM_AS_IROM = true) report "NEORV32 PROCESSOR CONFIG NOTE: Implementing processor-internal IMEM as ROM (" & natural'image(IMEM_SIZE) & |
" bytes), pre-initialized with application (" & natural'image(imem_app_size_c) & " bytes)." severity note; |
-- |
assert not (IMEM_AS_IROM = false) report "NEORV32 PROCESSOR CONFIG NOTE: Implementing processor-internal IMEM as blank RAM (" & natural'image(IMEM_SIZE) & |
" bytes)." severity note; |
-- |
assert not ((IMEM_AS_IROM = true) and (imem_app_size_c > IMEM_SIZE)) report "NEORV32 PROCESSOR CONFIG ERROR: Application (image = " & natural'image(imem_app_size_c) & |
" bytes) does not fit into processor-internal IMEM (ROM = " & natural'image(IMEM_SIZE) & " bytes)!" severity error; |
|
|
-- Access Control ------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
acc_en <= '1' when (addr_i(hi_abb_c downto lo_abb_c) = IMEM_BASE(hi_abb_c downto lo_abb_c)) else '0'; |
addr <= addr_i(index_size_f(IMEM_SIZE/4)+1 downto 2); -- word aligned |
|
|
-- Implement IMEM as pre-initialized ROM -------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
imem_rom: |
if (IMEM_AS_IROM = true) generate |
mem_access: process(clk_i) |
begin |
if rising_edge(clk_i) then |
if (acc_en = '1') then -- reduce switching activity when not accessed |
mem_rom_rd <= mem_rom(to_integer(unsigned(addr))); |
end if; |
end if; |
end process mem_access; |
-- read data -- |
rdata <= mem_rom_rd; |
end generate; |
|
|
-- Implement IMEM as not-initialized RAM -------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
imem_ram: |
if (IMEM_AS_IROM = false) generate |
mem_access: process(clk_i) |
begin |
if rising_edge(clk_i) then |
addr_ff <= addr; |
if (acc_en = '1') then -- reduce switching activity when not accessed |
if (wren_i = '1') and (ben_i(0) = '1') then -- byte 0 |
mem_ram_b0(to_integer(unsigned(addr))) <= data_i(07 downto 00); |
end if; |
if (wren_i = '1') and (ben_i(1) = '1') then -- byte 1 |
mem_ram_b1(to_integer(unsigned(addr))) <= data_i(15 downto 08); |
end if; |
if (wren_i = '1') and (ben_i(2) = '1') then -- byte 2 |
mem_ram_b2(to_integer(unsigned(addr))) <= data_i(23 downto 16); |
end if; |
if (wren_i = '1') and (ben_i(3) = '1') then -- byte 3 |
mem_ram_b3(to_integer(unsigned(addr))) <= data_i(31 downto 24); |
end if; |
end if; |
end if; |
end process mem_access; |
-- sync(!) read - alternative HDL style -- |
mem_b0_rd <= mem_ram_b0(to_integer(unsigned(addr_ff))); |
mem_b1_rd <= mem_ram_b1(to_integer(unsigned(addr_ff))); |
mem_b2_rd <= mem_ram_b2(to_integer(unsigned(addr_ff))); |
mem_b3_rd <= mem_ram_b3(to_integer(unsigned(addr_ff))); |
-- pack -- |
rdata <= mem_b3_rd & mem_b2_rd & mem_b1_rd & mem_b0_rd; |
end generate; |
|
|
-- Bus Feedback --------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
bus_feedback: process(clk_i) |
begin |
if rising_edge(clk_i) then |
rden <= acc_en and rden_i; |
if (IMEM_AS_IROM = true) then |
ack_o <= acc_en and rden_i; |
else |
ack_o <= acc_en and (rden_i or wren_i); |
end if; |
end if; |
end process bus_feedback; |
|
-- output gate -- |
data_o <= rdata when (rden = '1') else (others => '0'); |
|
|
end neorv32_imem_rtl; |
/rtl/core/mem/neorv32_imem.default.vhd
86,7 → 86,7
|
-- Sanity Checks -------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
assert false report "NEORV32 PROCESSOR CONFIG NOTE: Using default platform-agnostic IMEM." severity note; |
assert false report "NEORV32 PROCESSOR CONFIG NOTE: Using DEFAULT platform-agnostic IMEM." severity note; |
assert not (IMEM_AS_IROM = true) report "NEORV32 PROCESSOR CONFIG NOTE: Implementing processor-internal IMEM as ROM (" & natural'image(IMEM_SIZE) & |
" bytes), pre-initialized with application (" & natural'image(imem_app_size_c) & " bytes)." severity note; |
-- |
/rtl/core/neorv32_bus_keeper.vhd
1,15 → 1,9
-- ################################################################################################# |
-- # << NEORV32 - Bus Keeper (BUSKEEPER) >> # |
-- # ********************************************************************************************* # |
-- # This unit monitors the processor-internal bus. If the accessed INTERNAL (IMEM if enabled, # |
-- # DMEM if enabled, BOOTROM + IO region) module does not respond within the defined number of # |
-- # cycles (VHDL package: max_proc_int_response_time_c) the BUS KEEPER asserts the error signal # |
-- # to inform the CPU / bus driver. # |
-- # # |
-- # WARNING: The bus keeper timeout does not track accesses via the processor-external bus # |
-- # interface! If the timeout-function of the Wishbone interface is not used, the CPU # |
-- # might be permanently stalled by an an unacknowledged transfer! If the external bus # |
-- # interface is disabled, ALL accesses by the CPU are internal. # |
-- # This unit monitors the processor-internal bus. If the accessed module does not respond within # |
-- # the defined number of cycles (VHDL package: max_proc_int_response_time_c) or issues an ERROR # |
-- # conditions the BUS KEEPER asserts the error signal to inform the CPU. # |
-- # ********************************************************************************************* # |
-- # BSD 3-Clause License # |
-- # # |
50,16 → 44,6
use neorv32.neorv32_package.all; |
|
entity neorv32_bus_keeper is |
generic ( |
-- External memory interface -- |
MEM_EXT_EN : boolean; -- implement external memory bus interface? |
-- Internal instruction memory -- |
MEM_INT_IMEM_EN : boolean; -- implement processor-internal instruction memory |
MEM_INT_IMEM_SIZE : natural; -- size of processor-internal instruction memory in bytes |
-- Internal data memory -- |
MEM_INT_DMEM_EN : boolean; -- implement processor-internal data memory |
MEM_INT_DMEM_SIZE : natural -- size of processor-internal data memory in bytes |
); |
port ( |
-- host access -- |
clk_i : in std_ulogic; -- global clock line |
75,7 → 59,9
bus_rden_i : in std_ulogic; -- read enable |
bus_wren_i : in std_ulogic; -- write enable |
bus_ack_i : in std_ulogic; -- transfer acknowledge from bus system |
bus_err_i : in std_ulogic -- transfer error from bus system |
bus_err_i : in std_ulogic; -- transfer error from bus system |
bus_tmo_i : in std_ulogic; -- transfer timeout (external interface) |
bus_ext_i : in std_ulogic -- external bus access |
); |
end neorv32_bus_keeper; |
|
87,11 → 73,11
|
-- Control register -- |
constant ctrl_err_type_c : natural := 0; -- r/-: error type: 0=device error, 1=access timeout |
constant ctrl_err_src_c : natural := 1; -- r/-: error source: 0=processor-external, 1=processor-internal |
constant ctrl_err_flag_c : natural := 31; -- r/c: bus error encountered, sticky; cleared by writing zero |
|
-- sticky error flag -- |
-- sticky error flags -- |
signal err_flag : std_ulogic; |
signal err_type : std_ulogic; |
|
-- access control -- |
signal acc_en : std_ulogic; -- module access enable |
98,21 → 84,11
signal wren : std_ulogic; -- word write enable |
signal rden : std_ulogic; -- read enable |
|
-- bus access check -- |
type access_check_t is record |
int_imem : std_ulogic; |
int_dmem : std_ulogic; |
int_bootrom_io : std_ulogic; |
valid : std_ulogic; |
end record; |
signal access_check : access_check_t; |
|
-- controller -- |
type control_t is record |
pending : std_ulogic; |
timeout : std_ulogic_vector(index_size_f(max_proc_int_response_time_c)-1 downto 0); |
err_type : std_ulogic; |
int_ext : std_ulogic; |
bus_err : std_ulogic; |
end record; |
signal control : control_t; |
131,17 → 107,6
rden <= acc_en and rden_i; |
|
|
-- Bus Access Check ----------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
-- access to processor-internal IMEM or DMEM? -- |
access_check.int_imem <= '1' when (bus_addr_i(31 downto index_size_f(MEM_INT_IMEM_SIZE)) = imem_base_c(31 downto index_size_f(MEM_INT_IMEM_SIZE))) and (MEM_INT_IMEM_EN = true) else '0'; |
access_check.int_dmem <= '1' when (bus_addr_i(31 downto index_size_f(MEM_INT_DMEM_SIZE)) = dmem_base_c(31 downto index_size_f(MEM_INT_DMEM_SIZE))) and (MEM_INT_DMEM_EN = true) else '0'; |
-- access to processor-internal BOOTROM or IO devices? -- |
access_check.int_bootrom_io <= '1' when (bus_addr_i(31 downto 16) = boot_rom_base_c(31 downto 16)) else '0'; -- hacky! |
-- actual internal bus access? -- |
access_check.valid <= access_check.int_imem or access_check.int_dmem or access_check.int_bootrom_io; |
|
|
-- Read/Write Access ---------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
rw_access: process(clk_i) |
153,15 → 118,16
-- read access -- |
data_o <= (others => '0'); |
if (rden = '1') then |
data_o(ctrl_err_type_c) <= control.err_type; |
data_o(ctrl_err_src_c) <= control.int_ext; |
data_o(ctrl_err_type_c) <= err_type; |
data_o(ctrl_err_flag_c) <= err_flag; |
end if; |
-- |
if (control.bus_err = '1') then |
err_flag <= '1'; -- sticky error flag |
elsif (rden = '1') then -- clear on read |
if (control.bus_err = '1') then -- sticky error flag |
err_flag <= '1'; |
err_type <= control.err_type; |
elsif ((wren or rden) = '1') then -- clear on or read or write |
err_flag <= '0'; |
err_type <= '0'; |
end if; |
end if; |
end process rw_access; |
175,9 → 141,7
control.pending <= '0'; |
control.bus_err <= '0'; |
control.err_type <= def_rst_val_c; |
control.int_ext <= def_rst_val_c; |
control.timeout <= (others => def_rst_val_c); |
err_o <= '0'; |
elsif rising_edge(clk_i) then |
-- defaults -- |
control.bus_err <= '0'; |
186,36 → 150,31
if (control.pending = '0') then |
control.timeout <= std_ulogic_vector(to_unsigned(max_proc_int_response_time_c, index_size_f(max_proc_int_response_time_c))); |
if (bus_rden_i = '1') or (bus_wren_i = '1') then |
if (access_check.valid = '1') or (MEM_EXT_EN = false) then |
control.int_ext <= '1'; -- processor-internal access |
else |
control.int_ext <= '0'; -- processor-external access |
end if; |
control.pending <= '1'; |
end if; |
|
-- access monitor: PENDING -- |
else |
control.timeout <= std_ulogic_vector(unsigned(control.timeout) - 1); -- countdown timer |
if (bus_ack_i = '1') then -- normal termination by bus system |
control.err_type <= '0'; -- don't care |
control.bus_err <= '0'; |
control.pending <= '0'; |
elsif (bus_err_i = '1') then -- error termination by bus system |
if (bus_err_i = '1') then -- error termination by bus system |
control.err_type <= '0'; -- device error |
control.bus_err <= '1'; |
control.pending <= '0'; |
elsif (or_reduce_f(control.timeout) = '0') and (control.int_ext = '1') then -- timeout! terminate bus transfer (internal accesses only!) |
elsif ((or_reduce_f(control.timeout) = '0') and (bus_ext_i = '0')) or -- internal access timeout |
(bus_tmo_i = '1') then -- external access timeout |
control.err_type <= '1'; -- timeout error |
control.bus_err <= '1'; |
control.pending <= '0'; |
elsif (bus_ack_i = '1') then -- normal termination by bus system |
control.err_type <= '0'; -- don't care |
control.bus_err <= '0'; |
control.pending <= '0'; |
end if; |
end if; |
|
-- only output timeout errors here - device errors are already propagated by the bus system -- |
err_o <= control.bus_err and control.err_type; |
end if; |
end process keeper_control; |
|
-- signal bus error to CPU -- |
err_o <= control.bus_err; |
|
|
end neorv32_bus_keeper_rtl; |
/rtl/core/neorv32_cfs.vhd
59,6 → 59,7
data_i : in std_ulogic_vector(31 downto 0); -- data in |
data_o : out std_ulogic_vector(31 downto 0); -- data out |
ack_o : out std_ulogic; -- transfer acknowledge |
err_o : out std_ulogic; -- transfer error |
-- clock generator -- |
clkgen_en_o : out std_ulogic; -- enable clock generator |
clkgen_i : in std_ulogic_vector(07 downto 0); -- "clock" inputs |
101,7 → 102,14
-- NOTE: Do not modify the CFS base address or the CFS' occupied address space as this might cause access |
-- collisions with other modules. |
|
-- This module provides an ERROR signal to signal a faulty access operation to the CPU. |
-- It can be used to indicate an invalid access (for example to an unused CFS register address) or to signal |
-- a faulty state (like "not operational yet"). The error signal can be checked be checked by the applications |
-- "bus access fault" exception handler (provided by the system's BUSKEEPER module). |
-- This signal may only be set when the module is actually accessed! Tie to zero if not explicitly used. |
err_o <= '0'; |
|
|
-- CFS Generics --------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
-- In its default version, the CFS provides the configuration generics. single generic: |
/rtl/core/neorv32_cpu.vhd
156,6 → 156,7
signal be_store : std_ulogic; -- bus error on store data access |
signal fetch_pc : std_ulogic_vector(data_width_c-1 downto 0); -- pc for instruction fetch |
signal curr_pc : std_ulogic_vector(data_width_c-1 downto 0); -- current pc (for current executed instruction) |
signal next_pc : std_ulogic_vector(data_width_c-1 downto 0); -- next pc (for next executed instruction) |
signal fpu_flags : std_ulogic_vector(4 downto 0); -- FPU exception flags |
|
-- pmp interface -- |
285,6 → 286,7
imm_o => imm, -- immediate |
fetch_pc_o => fetch_pc, -- PC for instruction fetch |
curr_pc_o => curr_pc, -- current PC (corresponding to current instruction) |
next_pc_o => next_pc, -- next PC (corresponding to next instruction) |
csr_rdata_o => csr_rdata, -- CSR read data |
-- FPU interface -- |
fpu_flags_i => fpu_flags, -- exception flags |
355,7 → 357,8
-- data input -- |
rs1_i => rs1, -- rf source 1 |
rs2_i => rs2, -- rf source 2 |
pc2_i => curr_pc, -- delayed PC |
pc_i => curr_pc, -- current PC |
pc2_i => next_pc, -- next PC |
imm_i => imm, -- immediate |
csr_i => csr_rdata, -- CSR read data |
-- data output -- |
/rtl/core/neorv32_cpu_alu.vhd
60,7 → 60,8
-- data input -- |
rs1_i : in std_ulogic_vector(data_width_c-1 downto 0); -- rf source 1 |
rs2_i : in std_ulogic_vector(data_width_c-1 downto 0); -- rf source 2 |
pc2_i : in std_ulogic_vector(data_width_c-1 downto 0); -- delayed PC |
pc_i : in std_ulogic_vector(data_width_c-1 downto 0); -- current PC |
pc2_i : in std_ulogic_vector(data_width_c-1 downto 0); -- next PC |
imm_i : in std_ulogic_vector(data_width_c-1 downto 0); -- immediate |
csr_i : in std_ulogic_vector(data_width_c-1 downto 0); -- CSR read data |
-- data output -- |
85,10 → 86,8
|
-- results -- |
signal addsub_res : std_ulogic_vector(data_width_c downto 0); |
-- |
signal alu_res : std_ulogic_vector(data_width_c-1 downto 0); |
signal cp_res : std_ulogic_vector(data_width_c-1 downto 0); |
signal arith_res : std_ulogic_vector(data_width_c-1 downto 0); |
signal logic_res : std_ulogic_vector(data_width_c-1 downto 0); |
|
-- co-processor arbiter and interface -- |
type cp_ctrl_t is record |
119,7 → 118,7
|
-- ALU Input Operand Mux ------------------------------------------------------------------ |
-- ------------------------------------------------------------------------------------------- |
opa <= pc2_i when (ctrl_i(ctrl_alu_opa_mux_c) = '1') else rs1_i; -- operand a (first ALU input operand), only required for arithmetic ops |
opa <= pc_i when (ctrl_i(ctrl_alu_opa_mux_c) = '1') else rs1_i; -- operand a (first ALU input operand), only required for arithmetic ops |
opb <= imm_i when (ctrl_i(ctrl_alu_opb_mux_c) = '1') else rs2_i; -- operand b (second ALU input operand) |
|
|
136,7 → 135,7
op_a_v := (opa(opa'left) and (not ctrl_i(ctrl_alu_unsigned_c))) & opa; |
op_b_v := (opb(opb'left) and (not ctrl_i(ctrl_alu_unsigned_c))) & opb; |
-- add/sub(slt) select -- |
if (ctrl_i(ctrl_alu_addsub_c) = '1') then -- subtraction |
if (ctrl_i(ctrl_alu_op0_c) = '1') then -- subtraction |
op_y_v := not op_b_v; |
cin_v(0) := '1'; |
else -- addition |
143,25 → 142,49
op_y_v := op_b_v; |
cin_v(0) := '0'; |
end if; |
-- adder core (result + carry/borrow) -- |
-- adder core -- |
addsub_res <= std_ulogic_vector(unsigned(op_a_v) + unsigned(op_y_v) + unsigned(cin_v(0 downto 0))); |
end process binary_arithmetic_core; |
|
-- direct output of address result -- |
-- direct output of adder result -- |
add_o <= addsub_res(data_width_c-1 downto 0); |
|
-- ALU arithmetic logic core -- |
arithmetic_core: process(ctrl_i, addsub_res) |
|
-- ALU Operation Select ------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
alu_core: process(ctrl_i, addsub_res, rs1_i, opb) |
begin |
if (ctrl_i(ctrl_alu_arith_c) = alu_arith_cmd_addsub_c) then -- ADD/SUB |
arith_res <= addsub_res(data_width_c-1 downto 0); |
else -- SLT |
arith_res <= (others => '0'); |
arith_res(0) <= addsub_res(addsub_res'left); -- => carry/borrow |
end if; |
end process arithmetic_core; |
case ctrl_i(ctrl_alu_op2_c downto ctrl_alu_op0_c) is |
when alu_op_add_c => alu_res <= addsub_res(data_width_c-1 downto 0); -- (default) |
when alu_op_sub_c => alu_res <= addsub_res(data_width_c-1 downto 0); |
-- when alu_op_mova_c => alu_res <= rs1_i; -- FIXME |
when alu_op_slt_c => alu_res <= (others => '0'); alu_res(0) <= addsub_res(addsub_res'left); -- => carry/borrow |
when alu_op_movb_c => alu_res <= opb; |
when alu_op_xor_c => alu_res <= rs1_i xor opb; -- only rs1 required for logic ops (opa would also contain pc) |
when alu_op_or_c => alu_res <= rs1_i or opb; |
when alu_op_and_c => alu_res <= rs1_i and opb; |
when others => alu_res <= addsub_res(data_width_c-1 downto 0); |
end case; |
end process alu_core; |
|
-- ALU Function Select -------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
alu_function_mux: process(ctrl_i, alu_res, pc2_i, csr_i, cp_res) |
begin |
case ctrl_i(ctrl_alu_func1_c downto ctrl_alu_func0_c) is |
when alu_func_core_c => res_o <= alu_res; -- (default) |
when alu_func_nxpc_c => res_o <= pc2_i; |
when alu_func_csrr_c => res_o <= csr_i; |
when alu_func_copro_c => res_o <= cp_res; |
when others => res_o <= alu_res; -- undefined |
end case; |
end process alu_function_mux; |
|
|
-- ************************************************************************************************************************** |
-- Co-Processors |
-- ************************************************************************************************************************** |
|
-- Co-Processor Arbiter ------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
-- Interface: |
193,7 → 216,7
end process cp_arbiter; |
|
-- is co-processor operation? -- |
cp_ctrl.cmd <= '1' when (ctrl_i(ctrl_alu_func1_c downto ctrl_alu_func0_c) = alu_func_cmd_copro_c) else '0'; |
cp_ctrl.cmd <= '1' when (ctrl_i(ctrl_alu_func1_c downto ctrl_alu_func0_c) = alu_func_copro_c) else '0'; |
cp_ctrl.start <= '1' when (cp_ctrl.cmd = '1') and (cp_ctrl.cmd_ff = '0') else '0'; |
|
-- co-processor select / star trigger -- |
209,38 → 232,6
cp_res <= cp_result(0) or cp_result(1) or cp_result(2) or cp_result(3); |
|
|
-- ALU Logic Core ------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
alu_logic_core: process(ctrl_i, rs1_i, opb) |
begin |
case ctrl_i(ctrl_alu_logic1_c downto ctrl_alu_logic0_c) is |
when alu_logic_cmd_movb_c => logic_res <= opb; -- (default) |
when alu_logic_cmd_xor_c => logic_res <= rs1_i xor opb; -- only rs1 required for logic ops (opa would also contain pc) |
when alu_logic_cmd_or_c => logic_res <= rs1_i or opb; |
when alu_logic_cmd_and_c => logic_res <= rs1_i and opb; |
when others => logic_res <= opb; -- undefined |
end case; |
end process alu_logic_core; |
|
|
-- ALU Function Select -------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
alu_function_mux: process(ctrl_i, arith_res, logic_res, csr_i, cp_res) |
begin |
case ctrl_i(ctrl_alu_func1_c downto ctrl_alu_func0_c) is |
when alu_func_cmd_arith_c => res_o <= arith_res; -- (default) |
when alu_func_cmd_logic_c => res_o <= logic_res; |
when alu_func_cmd_csrr_c => res_o <= csr_i; |
when alu_func_cmd_copro_c => res_o <= cp_res; |
when others => res_o <= arith_res; -- undefined |
end case; |
end process alu_function_mux; |
|
|
-- ************************************************************************************************************************** |
-- Co-Processors |
-- ************************************************************************************************************************** |
|
-- Co-Processor 0: Shifter (CPU Core ISA) -------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
neorv32_cpu_cp_shifter_inst: neorv32_cpu_cp_shifter |
/rtl/core/neorv32_cpu_control.vhd
94,6 → 94,7
imm_o : out std_ulogic_vector(data_width_c-1 downto 0); -- immediate |
fetch_pc_o : out std_ulogic_vector(data_width_c-1 downto 0); -- PC for instruction fetch |
curr_pc_o : out std_ulogic_vector(data_width_c-1 downto 0); -- current PC (corresponding to current instruction) |
next_pc_o : out std_ulogic_vector(data_width_c-1 downto 0); -- next PC (corresponding to next instruction) |
csr_rdata_o : out std_ulogic_vector(data_width_c-1 downto 0); -- CSR read data |
-- FPU interface -- |
fpu_flags_i : in std_ulogic_vector(04 downto 0); -- exception flags |
185,7 → 186,6
|
-- instruction decoding helper logic -- |
type decode_aux_t is record |
alu_immediate : std_ulogic; |
is_atomic_lr : std_ulogic; |
is_atomic_sc : std_ulogic; |
is_float_op : std_ulogic; |
194,6 → 194,9
is_m_div : std_ulogic; |
is_bitmanip_imm : std_ulogic; |
is_bitmanip_reg : std_ulogic; |
rs1_zero : std_ulogic; |
rs2_zero : std_ulogic; |
rd_zero : std_ulogic; |
end record; |
signal decode_aux : decode_aux_t; |
|
359,8 → 362,8
signal debug_ctrl : debug_ctrl_t; |
|
-- (hpm) counter events -- |
signal cnt_event, cnt_event_nxt : std_ulogic_vector(hpmcnt_event_size_c-1 downto 0); |
signal hpmcnt_trigger : std_ulogic_vector(HPM_NUM_CNTS-1 downto 0); |
signal cnt_event : std_ulogic_vector(hpmcnt_event_size_c-1 downto 0); |
signal hpmcnt_trigger : std_ulogic_vector(HPM_NUM_CNTS-1 downto 0); |
|
-- illegal instruction check -- |
signal illegal_opcode_lsbs : std_ulogic; -- opcode != rv32 |
496,14 → 499,9
issue_engine.buf <= (others => '0'); |
elsif rising_edge(clk_i) then |
if (ipb.clear = '1') then |
if (CPU_EXTENSION_RISCV_C = true) then |
if (execute_engine.pc(1) = '1') then -- branch to unaligned address? |
issue_engine.state <= ISSUE_REALIGN; |
issue_engine.align <= '1'; -- aligned on 16-bit boundary |
else |
issue_engine.state <= issue_engine.state_nxt; |
issue_engine.align <= '0'; -- aligned on 32-bit boundary |
end if; |
if (CPU_EXTENSION_RISCV_C = true) and (execute_engine.pc(1) = '1') then -- branch to unaligned address? |
issue_engine.state <= ISSUE_REALIGN; |
issue_engine.align <= '1'; -- aligned on 16-bit boundary |
else |
issue_engine.state <= issue_engine.state_nxt; |
issue_engine.align <= '0'; -- always aligned on 32-bit boundaries |
623,42 → 621,45
if (rstn_i = '0') then |
imm_o <= (others => def_rst_val_c); |
elsif rising_edge(clk_i) then |
if (execute_engine.state = BRANCH) then -- next_PC as immediate for jump-and-link operations (=return address) via ALU.MOV_B |
imm_o <= execute_engine.next_pc; |
else -- "normal" immediate from instruction word |
opcode_v := execute_engine.i_reg(instr_opcode_msb_c downto instr_opcode_lsb_c+2) & "11"; |
case opcode_v is -- save some bits here, the two LSBs are always "11" for rv32 |
when opcode_store_c => -- S-immediate |
imm_o(31 downto 11) <= (others => execute_engine.i_reg(31)); -- sign extension |
imm_o(10 downto 05) <= execute_engine.i_reg(30 downto 25); |
imm_o(04 downto 01) <= execute_engine.i_reg(11 downto 08); |
imm_o(00) <= execute_engine.i_reg(07); |
when opcode_branch_c => -- B-immediate |
imm_o(31 downto 12) <= (others => execute_engine.i_reg(31)); -- sign extension |
imm_o(11) <= execute_engine.i_reg(07); |
imm_o(10 downto 05) <= execute_engine.i_reg(30 downto 25); |
imm_o(04 downto 01) <= execute_engine.i_reg(11 downto 08); |
imm_o(00) <= '0'; |
when opcode_lui_c | opcode_auipc_c => -- U-immediate |
imm_o(31 downto 20) <= execute_engine.i_reg(31 downto 20); |
imm_o(19 downto 12) <= execute_engine.i_reg(19 downto 12); |
imm_o(11 downto 00) <= (others => '0'); |
when opcode_jal_c => -- J-immediate |
imm_o(31 downto 20) <= (others => execute_engine.i_reg(31)); -- sign extension |
imm_o(19 downto 12) <= execute_engine.i_reg(19 downto 12); |
imm_o(11) <= execute_engine.i_reg(20); |
imm_o(10 downto 05) <= execute_engine.i_reg(30 downto 25); |
imm_o(04 downto 01) <= execute_engine.i_reg(24 downto 21); |
imm_o(00) <= '0'; |
when opcode_atomic_c => -- atomic memory access |
imm_o <= (others => '0'); -- effective address is addr = reg + 0 = reg |
when others => -- I-immediate |
imm_o(31 downto 11) <= (others => execute_engine.i_reg(31)); -- sign extension |
imm_o(10 downto 05) <= execute_engine.i_reg(30 downto 25); |
imm_o(04 downto 01) <= execute_engine.i_reg(24 downto 21); |
imm_o(00) <= execute_engine.i_reg(20); |
end case; |
end if; |
-- default: I-immediate: ALU-immediate, loads, jump-and-link with registers |
imm_o(31 downto 11) <= (others => execute_engine.i_reg(31)); -- sign extension |
imm_o(10 downto 05) <= execute_engine.i_reg(30 downto 25); |
imm_o(04 downto 01) <= execute_engine.i_reg(24 downto 21); |
imm_o(00) <= execute_engine.i_reg(20); |
|
opcode_v := execute_engine.i_reg(instr_opcode_msb_c downto instr_opcode_lsb_c+2) & "11"; |
case opcode_v is -- save some bits here, the two LSBs are always "11" for rv32 |
when opcode_store_c => -- S-immediate: store |
imm_o(31 downto 11) <= (others => execute_engine.i_reg(31)); -- sign extension |
imm_o(10 downto 05) <= execute_engine.i_reg(30 downto 25); |
imm_o(04 downto 01) <= execute_engine.i_reg(11 downto 08); |
imm_o(00) <= execute_engine.i_reg(07); |
when opcode_branch_c => -- B-immediate: conditional branches |
imm_o(31 downto 12) <= (others => execute_engine.i_reg(31)); -- sign extension |
imm_o(11) <= execute_engine.i_reg(07); |
imm_o(10 downto 05) <= execute_engine.i_reg(30 downto 25); |
imm_o(04 downto 01) <= execute_engine.i_reg(11 downto 08); |
imm_o(00) <= '0'; |
when opcode_lui_c | opcode_auipc_c => -- U-immediate: lui, auipc |
imm_o(31 downto 20) <= execute_engine.i_reg(31 downto 20); |
imm_o(19 downto 12) <= execute_engine.i_reg(19 downto 12); |
imm_o(11 downto 00) <= (others => '0'); |
when opcode_jal_c => -- J-immediate: unconditional jumps |
imm_o(31 downto 20) <= (others => execute_engine.i_reg(31)); -- sign extension |
imm_o(19 downto 12) <= execute_engine.i_reg(19 downto 12); |
imm_o(11) <= execute_engine.i_reg(20); |
imm_o(10 downto 05) <= execute_engine.i_reg(30 downto 25); |
imm_o(04 downto 01) <= execute_engine.i_reg(24 downto 21); |
imm_o(00) <= '0'; |
when opcode_atomic_c => -- atomic memory access and everything else |
if (CPU_EXTENSION_RISCV_A = true) then |
imm_o <= (others => '0'); -- effective address is addr = reg + 0 = reg |
else |
NULL; -- use default |
end if; |
when others => -- I-immediate |
NULL; -- use default |
end case; |
end if; |
end process imm_gen; |
|
688,10 → 689,10
begin |
if (rstn_i = '0') then |
-- registers that DO require a specific reset state -- |
execute_engine.pc <= CPU_BOOT_ADDR(data_width_c-1 downto 2) & "00"; -- 32-bit aligned! |
execute_engine.state <= SYS_WAIT; |
execute_engine.sleep <= '0'; |
execute_engine.branched <= '1'; -- reset is a branch from "somewhere" |
execute_engine.pc <= CPU_BOOT_ADDR(data_width_c-1 downto 2) & "00"; -- 32-bit aligned! |
execute_engine.state <= SYS_WAIT; |
execute_engine.sleep <= '0'; |
execute_engine.branched <= '1'; -- reset is a branch from "somewhere" |
-- no dedicated RESET required -- |
execute_engine.state_prev <= SYS_WAIT; -- actual reset value is not relevant |
execute_engine.i_reg <= (others => def_rst_val_c); |
701,7 → 702,6
execute_engine.i_reg_last <= (others => def_rst_val_c); |
execute_engine.next_pc <= (others => def_rst_val_c); |
ctrl <= (others => def_rst_val_c); |
-- |
ctrl(ctrl_bus_rd_c) <= '0'; |
ctrl(ctrl_bus_wr_c) <= '0'; |
elsif rising_edge(clk_i) then |
713,12 → 713,11
execute_engine.pc <= alu_add_i(data_width_c-1 downto 1) & '0'; -- jump/taken_branch |
end if; |
end if; |
-- |
execute_engine.state <= execute_engine.state_nxt; |
execute_engine.sleep <= execute_engine.sleep_nxt; |
execute_engine.branched <= execute_engine.branched_nxt; |
-- |
|
execute_engine.state <= execute_engine.state_nxt; |
execute_engine.state_prev <= execute_engine.state; |
execute_engine.sleep <= execute_engine.sleep_nxt; |
execute_engine.branched <= execute_engine.branched_nxt; |
execute_engine.i_reg <= execute_engine.i_reg_nxt; |
execute_engine.is_ci <= execute_engine.is_ci_nxt; |
execute_engine.is_ici <= execute_engine.is_ici_nxt; |
731,7 → 730,7
|
-- next PC -- |
case execute_engine.state is |
when TRAP_ENTER => |
when TRAP_ENTER => -- ENTERING trap environment |
if (CPU_EXTENSION_RISCV_DEBUG = false) then -- normal trapping |
execute_engine.next_pc <= csr.mtvec(data_width_c-1 downto 1) & '0'; -- trap enter |
else -- DEBUG MODE enabled |
743,13 → 742,13
execute_engine.next_pc <= csr.mtvec(data_width_c-1 downto 1) & '0'; -- trap enter |
end if; |
end if; |
when TRAP_EXIT => |
when TRAP_EXIT => -- LEAVING trap environment |
if (CPU_EXTENSION_RISCV_DEBUG = false) or (debug_ctrl.running = '0') then -- normal end of trap |
execute_engine.next_pc <= csr.mepc(data_width_c-1 downto 1) & '0'; -- trap exit |
else -- DEBUG MODE exiting |
execute_engine.next_pc <= csr.dpc(data_width_c-1 downto 1) & '0'; -- debug mode exit |
end if; |
when EXECUTE => |
when EXECUTE => -- NORMAL pc increment |
execute_engine.next_pc <= std_ulogic_vector(unsigned(execute_engine.pc) + unsigned(execute_engine.next_pc_inc)); -- next linear PC |
when others => |
NULL; |
765,7 → 764,8
execute_engine.next_pc_inc <= x"00000004" when ((execute_engine.is_ci = '0') or (CPU_EXTENSION_RISCV_C = false)) else x"00000002"; |
|
-- PC output -- |
curr_pc_o <= execute_engine.pc(data_width_c-1 downto 1) & '0'; -- PC for ALU ops |
curr_pc_o <= execute_engine.pc(data_width_c-1 downto 1) & '0'; -- current PC for ALU ops |
next_pc_o <= execute_engine.next_pc(data_width_c-1 downto 1) & '0'; -- next PC for ALU ops |
|
-- CSR access address -- |
csr.addr <= execute_engine.i_reg(instr_csr_id_msb_c downto instr_csr_id_lsb_c); |
817,7 → 817,6
variable sys_env_cmd_mask_v : std_ulogic_vector(11 downto 0); |
begin |
-- defaults -- |
decode_aux.alu_immediate <= '0'; |
decode_aux.is_atomic_lr <= '0'; |
decode_aux.is_atomic_sc <= '0'; |
decode_aux.is_float_op <= '0'; |
825,12 → 824,12
decode_aux.is_m_div <= '0'; |
decode_aux.is_bitmanip_imm <= '0'; |
decode_aux.is_bitmanip_reg <= '0'; |
decode_aux.rs1_zero <= '0'; |
decode_aux.rs2_zero <= '0'; |
decode_aux.rd_zero <= '0'; |
|
-- is immediate ALU operation? -- |
decode_aux.alu_immediate <= not execute_engine.i_reg(instr_opcode_msb_c-1); |
|
-- is atomic load-reservate/store-conditional? -- |
if (CPU_EXTENSION_RISCV_A = true) and (execute_engine.i_reg(instr_opcode_lsb_c+3 downto instr_opcode_lsb_c+2) = "11") then -- valid atomic sub-opcode |
if (CPU_EXTENSION_RISCV_A = true) and (execute_engine.i_reg(instr_opcode_lsb_c+2) = '1') then -- valid atomic sub-opcode |
decode_aux.is_atomic_lr <= not execute_engine.i_reg(instr_funct5_lsb_c); |
decode_aux.is_atomic_sc <= execute_engine.i_reg(instr_funct5_lsb_c); |
end if; |
850,7 → 849,7
((execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0110000") and (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = "101")) or -- RORI |
((execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0010100") and (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = "101") and (execute_engine.i_reg(instr_funct12_lsb_c+4 downto instr_funct12_lsb_c) = "00111")) or -- ORCB |
((execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0110100") and (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = "101") and (execute_engine.i_reg(instr_funct12_lsb_c+4 downto instr_funct12_lsb_c) = "11000")) then -- REV8 |
decode_aux.is_bitmanip_imm <= '1'; |
decode_aux.is_bitmanip_imm <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_B); -- BITMANIP implemented at all? |
end if; |
-- register operation -- |
if ((execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0110000") and (execute_engine.i_reg(instr_funct3_msb_c-1 downto instr_funct3_lsb_c) = "01")) or -- ROR / ROL |
870,7 → 869,7
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = "110") -- SH3ADD |
) |
) then |
decode_aux.is_bitmanip_reg <= '1'; |
decode_aux.is_bitmanip_reg <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_B); -- BITMANIP implemented at all? |
end if; |
|
-- floating-point operations (Zfinx) -- |
882,7 → 881,7
((execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c+2) = "10100") and (execute_engine.i_reg(instr_funct3_msb_c) = '0')) or -- FEQ.S / FLT.S / FLE.S |
((execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c+2) = "11010") and (execute_engine.i_reg(instr_funct12_lsb_c+4 downto instr_funct12_lsb_c+1) = "0000")) or -- FCVT.S.W* |
((execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c+2) = "11000") and (execute_engine.i_reg(instr_funct12_lsb_c+4 downto instr_funct12_lsb_c+1) = "0000")) then -- FCVT.W*.S |
decode_aux.is_float_op <= '1'; |
decode_aux.is_float_op <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zfinx); -- FPU implemented at all? |
end if; |
|
-- system/environment instructions -- |
892,9 → 891,14
-- integer MUL (M/Zmmul) / DIV (M) operation -- |
if (execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_alu_c(5)) and |
(execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0000001") then |
decode_aux.is_m_mul <= not execute_engine.i_reg(instr_funct3_msb_c); |
decode_aux.is_m_div <= execute_engine.i_reg(instr_funct3_msb_c); |
decode_aux.is_m_mul <= (not execute_engine.i_reg(instr_funct3_msb_c)) and (bool_to_ulogic_f(CPU_EXTENSION_RISCV_M) or bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zmmul)); |
decode_aux.is_m_div <= execute_engine.i_reg(instr_funct3_msb_c) and bool_to_ulogic_f(CPU_EXTENSION_RISCV_M); |
end if; |
|
-- register address checks -- |
decode_aux.rs1_zero <= not or_reduce_f(execute_engine.i_reg(instr_rs1_msb_c downto instr_rs1_lsb_c)); |
decode_aux.rs2_zero <= not or_reduce_f(execute_engine.i_reg(instr_rs2_msb_c downto instr_rs2_lsb_c)); |
decode_aux.rd_zero <= not or_reduce_f(execute_engine.i_reg(instr_rd_msb_c downto instr_rd_lsb_c)); |
end process decode_helper; |
|
|
937,9 → 941,8
-- CONTROL DEFAULTS -- |
ctrl_nxt <= (others => '0'); -- default: all off |
-- ALU main control -- |
ctrl_nxt(ctrl_alu_addsub_c) <= '0'; -- ADD(I) |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_cmd_arith_c; -- default ALU function select: arithmetic |
ctrl_nxt(ctrl_alu_arith_c) <= alu_arith_cmd_addsub_c; -- default ALU arithmetic operation: ADDSUB |
ctrl_nxt(ctrl_alu_op2_c downto ctrl_alu_op0_c) <= alu_op_add_c; -- default ALU operation: ADD |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_core_c; -- default ALU operation: ADD |
-- ALU sign control -- |
if (execute_engine.i_reg(instr_opcode_lsb_c+4) = '1') then -- ALU ops |
ctrl_nxt(ctrl_alu_unsigned_c) <= execute_engine.i_reg(instr_funct3_lsb_c+0); -- unsigned ALU operation? (SLTIU, SLTU) |
946,12 → 949,8
else -- branches |
ctrl_nxt(ctrl_alu_unsigned_c) <= execute_engine.i_reg(instr_funct3_lsb_c+1); -- unsigned branches? (BLTU, BGEU) |
end if; |
-- Atomic store-conditional instruction (evaluate lock status) -- |
if (CPU_EXTENSION_RISCV_A = true) then |
ctrl_nxt(ctrl_bus_ch_lock_c) <= decode_aux.is_atomic_sc; |
else |
ctrl_nxt(ctrl_bus_ch_lock_c) <= '0'; |
end if; |
-- atomic store-conditional instruction (evaluate lock status) -- |
ctrl_nxt(ctrl_bus_ch_lock_c) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_A) and decode_aux.is_atomic_sc; |
|
|
-- state machine -- |
975,14 → 974,15
execute_engine.branched_nxt <= '0'; |
execute_engine.pc_we <= not execute_engine.branched; -- update PC with linear next_pc if there was no actual branch |
-- IR update - exceptions -- |
trap_ctrl.instr_ma <= cmd_issue.data(33); -- misaligned instruction fetch address |
trap_ctrl.instr_ma <= cmd_issue.data(33) and (not bool_to_ulogic_f(CPU_EXTENSION_RISCV_C)); -- misaligned instruction fetch address, if C disabled |
trap_ctrl.instr_be <= cmd_issue.data(34); -- bus access fault during instruction fetch |
execute_engine.is_ici_nxt <= cmd_issue.data(35); -- invalid decompressed instruction |
-- any reason to go to trap state? -- |
if (execute_engine.sleep = '1') or -- WFI instruction - this will enter sleep state |
if (execute_engine.sleep = '1') or -- enter sleep state |
(trap_ctrl.exc_fire = '1') or -- exception during LAST instruction (illegal instruction) |
(trap_ctrl.env_start = '1') or -- pending trap (IRQ or exception) |
((cmd_issue.data(33) or cmd_issue.data(34)) = '1') then -- exception during instruction fetch of the NEW instruction |
((cmd_issue.data(33) = '1') and (CPU_EXTENSION_RISCV_C = false)) or -- misaligned instruction fetch address, if C disabled |
(cmd_issue.data(34) = '1') then -- bus access fault during instruction fetch |
execute_engine.state_nxt <= TRAP_ENTER; |
else |
execute_engine.state_nxt <= EXECUTE; |
997,12 → 997,14
execute_engine.state_nxt <= TRAP_EXECUTE; |
end if; |
|
|
when TRAP_EXIT => -- Return from trap environment - get xEPC |
-- ------------------------------------------------------------ |
trap_ctrl.env_end <= '1'; |
execute_engine.state_nxt <= TRAP_EXECUTE; |
|
when TRAP_EXECUTE => -- Start trap environment -> jump to xTVEC / return from trap environment -> jump to xEPC |
|
when TRAP_EXECUTE => -- Process trap environment -> jump to xTVEC / return from trap environment -> jump to xEPC |
-- ------------------------------------------------------------ |
execute_engine.pc_mux_sel <= '0'; -- next_PC |
fetch_engine.reset <= '1'; |
1018,109 → 1020,76
|
when opcode_alu_c | opcode_alui_c => -- (register/immediate) ALU operation |
-- ------------------------------------------------------------ |
ctrl_nxt(ctrl_alu_opa_mux_c) <= '0'; -- use RS1 as ALU.OPA |
ctrl_nxt(ctrl_alu_opb_mux_c) <= decode_aux.alu_immediate; -- use IMM as ALU.OPB for immediate operations |
ctrl_nxt(ctrl_rf_in_mux_c) <= '0'; -- RF input = ALU result |
ctrl_nxt(ctrl_alu_opb_mux_c) <= not execute_engine.i_reg(instr_opcode_msb_c-1); -- use IMM as ALU.OPB for immediate operations |
|
-- ALU arithmetic operation type -- |
if (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_slt_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sltu_c) then |
ctrl_nxt(ctrl_alu_arith_c) <= alu_arith_cmd_slt_c; |
else |
ctrl_nxt(ctrl_alu_arith_c) <= alu_arith_cmd_addsub_c; |
end if; |
|
-- ADD/SUB -- |
if ((decode_aux.alu_immediate = '0') and (execute_engine.i_reg(instr_funct7_msb_c-1) = '1')) or -- not an immediate op and funct7.6 set => SUB |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_slt_c) or -- SLT operation |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sltu_c) then -- SLTU operation |
ctrl_nxt(ctrl_alu_addsub_c) <= '1'; -- SUB/SLT |
else |
ctrl_nxt(ctrl_alu_addsub_c) <= '0'; -- ADD(I) |
end if; |
|
-- ALU logic operation -- |
-- ALU core operation -- |
case execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) is -- actual ALU.logic operation (re-coding) |
when funct3_xor_c => ctrl_nxt(ctrl_alu_logic1_c downto ctrl_alu_logic0_c) <= alu_logic_cmd_xor_c; -- XOR(I) |
when funct3_or_c => ctrl_nxt(ctrl_alu_logic1_c downto ctrl_alu_logic0_c) <= alu_logic_cmd_or_c; -- OR(I) |
when others => ctrl_nxt(ctrl_alu_logic1_c downto ctrl_alu_logic0_c) <= alu_logic_cmd_and_c; -- AND(I) |
when funct3_subadd_c => -- ADD(I)/SUB |
if ((execute_engine.i_reg(instr_opcode_msb_c-1) = '1') and (execute_engine.i_reg(instr_funct7_msb_c-1) = '1')) then -- not an immediate op and funct7.6 set => SUB |
ctrl_nxt(ctrl_alu_op2_c downto ctrl_alu_op0_c) <= alu_op_sub_c; |
else |
ctrl_nxt(ctrl_alu_op2_c downto ctrl_alu_op0_c) <= alu_op_add_c; |
end if; |
when funct3_slt_c | funct3_sltu_c => -- SLT(I), SLTU(I) |
ctrl_nxt(ctrl_alu_op2_c downto ctrl_alu_op0_c) <= alu_op_slt_c; |
when funct3_xor_c => -- XOR(I) |
ctrl_nxt(ctrl_alu_op2_c downto ctrl_alu_op0_c) <= alu_op_xor_c; |
when funct3_or_c => -- OR(I) |
ctrl_nxt(ctrl_alu_op2_c downto ctrl_alu_op0_c) <= alu_op_or_c; |
when others => -- AND(I), multi-cycle / co-processor operations |
ctrl_nxt(ctrl_alu_op2_c downto ctrl_alu_op0_c) <= alu_op_and_c; |
end case; |
|
-- Check if single-cycle or multi-cycle (co-processor) operation -- |
-- co-processor MULDIV operation? -- |
if ((CPU_EXTENSION_RISCV_M = true) and ((decode_aux.is_m_mul = '1') or (decode_aux.is_m_div = '1'))) or -- MUL/DIV |
((CPU_EXTENSION_RISCV_Zmmul = true) and (decode_aux.is_m_mul = '1')) then -- MUL |
ctrl_nxt(ctrl_cp_id_msb_c downto ctrl_cp_id_lsb_c) <= cp_sel_muldiv_c; -- use MULDIV CP |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_cmd_copro_c; |
-- co-processor bit manipulation operation? -- |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_copro_c; |
execute_engine.state_nxt <= ALU_WAIT; |
-- co-processor BIT-MANIPULATION operation? -- |
elsif (CPU_EXTENSION_RISCV_B = true) and |
(((execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_alu_c(5)) and (decode_aux.is_bitmanip_reg = '1')) or -- register operation |
((execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_alui_c(5)) and (decode_aux.is_bitmanip_imm = '1'))) then -- immediate operation |
(((execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_alu_c(5)) and (decode_aux.is_bitmanip_reg = '1')) or -- register operation |
((execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_alui_c(5)) and (decode_aux.is_bitmanip_imm = '1'))) then -- immediate operation |
ctrl_nxt(ctrl_cp_id_msb_c downto ctrl_cp_id_lsb_c) <= cp_sel_bitmanip_c; -- use BITMANIP CP |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_cmd_copro_c; |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_copro_c; |
execute_engine.state_nxt <= ALU_WAIT; |
-- co-processor SHIFT operation? -- |
elsif (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sll_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sr_c) then |
ctrl_nxt(ctrl_cp_id_msb_c downto ctrl_cp_id_lsb_c) <= cp_sel_shifter_c; -- use SHIFTER CP (only relevant for shift operations) |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_copro_c; |
execute_engine.state_nxt <= ALU_WAIT; |
-- ALU core operations (single-cycle) -- |
else |
-- ALU operation, function select -- |
ctrl_nxt(ctrl_cp_id_msb_c downto ctrl_cp_id_lsb_c) <= cp_sel_shifter_c; -- use SHIFTER CP (only relevant for shift operations) |
case execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) is |
when funct3_sll_c | funct3_sr_c => -- SHIFT operation |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_cmd_copro_c; |
when funct3_xor_c | funct3_or_c | funct3_and_c => -- LOGIC operation |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_cmd_logic_c; |
when others => -- ARITHMETIC operation |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_cmd_arith_c; |
end case; |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_core_c; |
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back |
execute_engine.state_nxt <= DISPATCH; |
end if; |
|
-- multi cycle ALU operation? -- |
if (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sll_c) or -- SLL shift operation? |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sr_c) or -- SR shift operation? |
((CPU_EXTENSION_RISCV_M = true) and ((decode_aux.is_m_mul = '1') or (decode_aux.is_m_div = '1'))) or -- MUL/DIV |
((CPU_EXTENSION_RISCV_Zmmul = true) and (decode_aux.is_m_mul = '1')) or -- MUL |
((CPU_EXTENSION_RISCV_B = true) and ( |
((execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_alu_c(5)) and (decode_aux.is_bitmanip_reg = '1')) or -- BITMANIP CP register operation? |
((execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_alui_c(5)) and (decode_aux.is_bitmanip_imm = '1'))) -- BITMANIP CP immediate operation? |
) then |
execute_engine.state_nxt <= ALU_WAIT; |
else -- single cycle ALU operation |
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back |
execute_engine.state_nxt <= DISPATCH; |
end if; |
|
when opcode_lui_c | opcode_auipc_c => -- load upper immediate / add upper immediate to PC |
-- ------------------------------------------------------------ |
ctrl_nxt(ctrl_alu_opa_mux_c) <= '1'; -- ALU.OPA = PC (for AUIPC only) |
ctrl_nxt(ctrl_alu_opb_mux_c) <= '1'; -- use IMM as ALU.OPB |
ctrl_nxt(ctrl_alu_arith_c) <= alu_arith_cmd_addsub_c; -- ADD |
ctrl_nxt(ctrl_alu_logic1_c downto ctrl_alu_logic0_c) <= alu_logic_cmd_movb_c; -- MOVB |
if (execute_engine.i_reg(instr_opcode_lsb_c+5) = opcode_lui_c(5)) then -- LUI |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_cmd_logic_c; -- actual ALU operation = MOVB |
ctrl_nxt(ctrl_alu_op2_c downto ctrl_alu_op0_c) <= alu_op_movb_c; -- actual ALU operation = MOVB |
else -- AUIPC |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_cmd_arith_c; -- actual ALU operation = ADD |
ctrl_nxt(ctrl_alu_op2_c downto ctrl_alu_op0_c) <= alu_op_add_c; -- actual ALU operation = ADD |
end if; |
ctrl_nxt(ctrl_rf_in_mux_c) <= '0'; -- RF input = ALU result |
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back |
execute_engine.state_nxt <= DISPATCH; |
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back |
execute_engine.state_nxt <= DISPATCH; |
|
|
when opcode_load_c | opcode_store_c | opcode_atomic_c => -- load/store / atomic memory access |
-- ------------------------------------------------------------ |
ctrl_nxt(ctrl_alu_opa_mux_c) <= '0'; -- use RS1 as ALU.OPA |
ctrl_nxt(ctrl_alu_opb_mux_c) <= '1'; -- use IMM as ALU.OPB |
ctrl_nxt(ctrl_bus_mo_we_c) <= '1'; -- write to MAR and MDO (MDO only relevant for store) |
-- |
if (CPU_EXTENSION_RISCV_A = false) or -- atomic extension disabled |
(execute_engine.i_reg(instr_opcode_lsb_c+3 downto instr_opcode_lsb_c+2) = "00") then -- normal integer load/store |
execute_engine.state_nxt <= LOADSTORE_0; |
else -- atomic operation |
if (execute_engine.i_reg(instr_funct5_msb_c downto instr_funct5_lsb_c) = funct5_a_sc_c) or -- store-conditional |
(execute_engine.i_reg(instr_funct5_msb_c downto instr_funct5_lsb_c) = funct5_a_lr_c) then -- load-reservate |
execute_engine.state_nxt <= LOADSTORE_0; |
else -- unimplemented (atomic) instruction |
execute_engine.state_nxt <= SYS_WAIT; |
end if; |
end if; |
execute_engine.state_nxt <= LOADSTORE_0; |
|
|
when opcode_branch_c | opcode_jal_c | opcode_jalr_c => -- branch / jump and link (with register) |
-- ------------------------------------------------------------ |
-- target address (ALU.ADD) operands -- |
if (execute_engine.i_reg(instr_opcode_lsb_c+3 downto instr_opcode_lsb_c+2) = opcode_jalr_c(3 downto 2)) then -- JALR |
ctrl_nxt(ctrl_alu_opa_mux_c) <= '0'; -- use RS1 as ALU.OPA (branch target address base) |
else -- JAL |
1129,21 → 1098,21
ctrl_nxt(ctrl_alu_opb_mux_c) <= '1'; -- use IMM as ALU.OPB (branch target address offset) |
execute_engine.state_nxt <= BRANCH; |
|
|
when opcode_fence_c => -- fence operations |
-- ------------------------------------------------------------ |
if (execute_engine.i_reg(instr_funct3_lsb_c) = funct3_fence_c(0)) then -- FENCE |
ctrl_nxt(ctrl_bus_fence_c) <= '1'; |
execute_engine.state_nxt <= SYS_WAIT; |
else -- FENCE.I |
if (CPU_EXTENSION_RISCV_Zifencei = true) then |
ctrl_nxt(ctrl_bus_fencei_c) <= '1'; |
execute_engine.branched_nxt <= '1'; -- this is an actual branch |
execute_engine.state_nxt <= TRAP_EXECUTE; -- use TRAP_EXECUTE to "modify" PC (PC <= PC) |
else -- fence.i not implemented |
execute_engine.state_nxt <= SYS_WAIT; |
end if; |
if (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_fence_c) then -- FENCE |
ctrl_nxt(ctrl_bus_fence_c) <= '1'; |
execute_engine.state_nxt <= SYS_WAIT; |
elsif (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_fencei_c) and (CPU_EXTENSION_RISCV_Zifencei = true) then -- FENCE.I |
ctrl_nxt(ctrl_bus_fencei_c) <= '1'; |
execute_engine.branched_nxt <= '1'; -- this is an actual branch |
execute_engine.state_nxt <= TRAP_EXECUTE; -- use TRAP_EXECUTE to "modify" PC (PC <= PC) |
else -- illegal fence instruction |
execute_engine.state_nxt <= SYS_WAIT; |
end if; |
|
|
when opcode_syscsr_c => -- system/csr access |
-- ------------------------------------------------------------ |
if (CPU_EXTENSION_RISCV_Zicsr = true) then |
1156,17 → 1125,19
execute_engine.state_nxt <= SYS_WAIT; |
end if; |
|
|
when opcode_fop_c => -- floating-point operations |
-- ------------------------------------------------------------ |
if (CPU_EXTENSION_RISCV_Zfinx = true) and (decode_aux.is_float_op = '1') then |
if (CPU_EXTENSION_RISCV_Zfinx = true) then |
ctrl_nxt(ctrl_cp_id_msb_c downto ctrl_cp_id_lsb_c) <= cp_sel_fpu_c; -- trigger FPU CP |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_cmd_copro_c; |
execute_engine.state_nxt <= ALU_WAIT; |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_copro_c; |
execute_engine.state_nxt <= ALU_WAIT; |
else |
execute_engine.state_nxt <= SYS_WAIT; |
end if; |
|
when others => -- undefined |
|
when others => -- illegal opcode |
-- ------------------------------------------------------------ |
execute_engine.state_nxt <= SYS_WAIT; |
|
1176,57 → 1147,49
when SYS_ENV => -- system environment operation - execution |
-- ------------------------------------------------------------ |
execute_engine.state_nxt <= SYS_WAIT; -- default |
case decode_aux.sys_env_cmd is -- use a simplified input here (with permanent zeros) |
when funct12_ecall_c => trap_ctrl.env_call <= '1'; -- ECALL |
when funct12_ebreak_c => trap_ctrl.break_point <= '1'; -- EBREAK |
when funct12_wfi_c => -- WFI |
if (CPU_EXTENSION_RISCV_DEBUG = true) and |
((debug_ctrl.running = '1') or (csr.dcsr_step = '1')) then -- act as NOP when in debug-mode or during single-stepping |
NULL; -- executed as NOP |
else |
execute_engine.sleep_nxt <= '1'; -- go to sleep mode |
end if; |
when funct12_mret_c => -- MRET |
if (csr.priv_m_mode = '1') then -- only allowed in M-mode |
execute_engine.state_nxt <= TRAP_EXIT; |
else |
NULL; -- executed as NOP |
end if; |
when funct12_dret_c => -- DRET |
if (CPU_EXTENSION_RISCV_DEBUG = true) and (debug_ctrl.running = '1') then -- only allowed in debug-mode |
execute_engine.state_nxt <= TRAP_EXIT; |
debug_ctrl.dret <= '1'; |
else |
NULL; -- executed as NOP |
end if; |
when others => NULL; -- undefined / execute as NOP |
end case; |
if (trap_ctrl.exc_buf(exception_iillegal_c) = '0') then -- no illegal instruction |
case decode_aux.sys_env_cmd is -- use a simplified input here (with hardwired zeros) |
when funct12_ecall_c => trap_ctrl.env_call <= '1'; -- ECALL |
when funct12_ebreak_c => trap_ctrl.break_point <= '1'; -- EBREAK |
when funct12_mret_c => execute_engine.state_nxt <= TRAP_EXIT; -- MRET |
when funct12_dret_c => -- DRET |
if (CPU_EXTENSION_RISCV_DEBUG = true) then |
execute_engine.state_nxt <= TRAP_EXIT; |
debug_ctrl.dret <= '1'; |
else |
NULL; -- executed as NOP (and raise illegal instruction exception) |
end if; |
when funct12_wfi_c => -- WFI |
if (CPU_EXTENSION_RISCV_DEBUG = true) and |
((debug_ctrl.running = '1') or (csr.dcsr_step = '1')) then -- act as NOP when in debug-mode or during single-stepping |
NULL; -- executed as NOP |
else |
execute_engine.sleep_nxt <= '1'; -- go to sleep mode |
end if; |
when others => NULL; -- undefined / execute as NOP |
end case; |
end if; |
|
|
when CSR_ACCESS => -- read & write status and control register (CSR) |
-- ------------------------------------------------------------ |
-- CSR write access -- |
case execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) is |
when funct3_csrrw_c | funct3_csrrwi_c => -- CSRRW(I) |
csr.we_nxt <= '1'; -- always write CSR |
when funct3_csrrs_c | funct3_csrrsi_c | funct3_csrrc_c | funct3_csrrci_c => -- CSRRS(I) / CSRRC(I) |
csr.we_nxt <= or_reduce_f(execute_engine.i_reg(instr_rs1_msb_c downto instr_rs1_lsb_c)); -- write CSR if rs1/imm is not zero |
when others => -- invalid |
csr.we_nxt <= '0'; |
end case; |
if (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrw_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrwi_c) then -- CSRRW(I) |
csr.we_nxt <= '1'; -- always write CSR |
else -- CSRRS(I) / CSRRC(I) [invalid CSR instruction are already checked by the illegal instruction logic] |
csr.we_nxt <= not decode_aux.rs1_zero; -- write CSR if rs1/imm is not zero |
end if; |
-- register file write back -- |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_cmd_csrr_c; |
ctrl_nxt(ctrl_rf_in_mux_c) <= '0'; -- RF input = ALU result |
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back |
execute_engine.state_nxt <= DISPATCH; |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_csrr_c; |
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back |
execute_engine.state_nxt <= DISPATCH; |
|
|
when ALU_WAIT => -- wait for multi-cycle ALU operation (co-processor) to finish |
-- ------------------------------------------------------------ |
ctrl_nxt(ctrl_rf_in_mux_c) <= '0'; -- RF input = ALU result |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_cmd_copro_c; |
-- wait for result -- |
if (alu_idone_i = '1') then -- done |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_copro_c; |
if (alu_idone_i = '1') or (trap_ctrl.exc_buf(exception_iillegal_c) = '1') then -- completed or exception |
ctrl_nxt(ctrl_rf_wb_en_c) <= '1'; -- valid RF write-back |
execute_engine.state_nxt <= DISPATCH; |
end if; |
1235,13 → 1198,10
when BRANCH => -- update PC for taken branches and jumps |
-- ------------------------------------------------------------ |
-- get and store return address (only relevant for jump-and-link operations) -- |
ctrl_nxt(ctrl_alu_opb_mux_c) <= '1'; -- use IMM as ALU.OPB (next_pc from immediate generator = return address) |
ctrl_nxt(ctrl_alu_logic1_c downto ctrl_alu_logic0_c) <= alu_logic_cmd_movb_c; -- MOVB |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_cmd_logic_c; -- actual ALU operation = MOVB |
ctrl_nxt(ctrl_rf_in_mux_c) <= '0'; -- RF input = ALU result |
ctrl_nxt(ctrl_rf_wb_en_c) <= execute_engine.i_reg(instr_opcode_lsb_c+2); -- valid RF write-back? (is jump-and-link?) |
ctrl_nxt(ctrl_alu_func1_c downto ctrl_alu_func0_c) <= alu_func_nxpc_c; -- next PC |
ctrl_nxt(ctrl_rf_wb_en_c) <= execute_engine.i_reg(instr_opcode_lsb_c+2); -- valid RF write-back? (is jump-and-link?) |
-- destination address -- |
execute_engine.pc_mux_sel <= '1'; -- alu.add = branch/jump destination |
execute_engine.pc_mux_sel <= '1'; -- PC <= alu.add = branch/jump destination |
if (execute_engine.i_reg(instr_opcode_lsb_c+2) = '1') or (execute_engine.branch_taken = '1') then -- JAL/JALR or taken branch |
-- no need to check for illegal instructions here; the branch condition evaluation circuit will not set "branch_taken" if funct3 is invalid |
execute_engine.pc_we <= '1'; -- update PC |
1259,12 → 1219,10
if (execute_engine.i_reg(instr_opcode_msb_c-1) = '0') or (decode_aux.is_atomic_lr = '1') then -- normal load or atomic load-reservate |
ctrl_nxt(ctrl_bus_rd_c) <= '1'; -- read request |
else -- store |
if (decode_aux.is_atomic_sc = '1') then -- evaluate lock state |
if (excl_state_i = '1') then -- lock is still ok - perform write access |
ctrl_nxt(ctrl_bus_wr_c) <= '1'; -- write request |
end if; |
else |
ctrl_nxt(ctrl_bus_wr_c) <= '1'; -- (normal) write request |
if (decode_aux.is_atomic_sc = '0') or (CPU_EXTENSION_RISCV_A = false) then -- (normal) write request |
ctrl_nxt(ctrl_bus_wr_c) <= '1'; |
else -- evaluate lock state |
ctrl_nxt(ctrl_bus_wr_c) <= excl_state_i; -- write request if lock is still ok |
end if; |
end if; |
execute_engine.state_nxt <= LOADSTORE_1; |
1280,9 → 1238,9
-- ------------------------------------------------------------ |
ctrl_nxt(ctrl_bus_mi_we_c) <= '1'; -- keep writing input data to MDI (only relevant for load (and SC.W) operations) |
ctrl_nxt(ctrl_rf_in_mux_c) <= '1'; -- RF input = memory input (only relevant for LOADs) |
-- wait for memory response / exception -- |
if (trap_ctrl.env_start = '1') and (trap_ctrl.cause(6 downto 5) = "00") then -- only abort if SYNC EXCEPTION (from bus) / no IRQs and NOT DEBUG-MODE-related |
execute_engine.state_nxt <= SYS_WAIT; |
-- wait for memory response -- |
if (trap_ctrl.env_start = '1') and (trap_ctrl.cause(6 downto 5) = "00") then -- abort if SYNC EXCEPTION (from bus or illegal cmd) / no IRQs and NOT DEBUG-MODE-related |
execute_engine.state_nxt <= DISPATCH; |
elsif (bus_d_wait_i = '0') then -- wait for bus to finish transaction |
-- data write-back -- |
if (execute_engine.i_reg(instr_opcode_msb_c-1) = '0') or -- normal load |
1312,15 → 1270,18
|
-- CSR Access Check ----------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
csr_access_check: process(execute_engine.i_reg, csr, debug_ctrl) |
variable csr_wacc_v : std_ulogic; -- to check access to read-only CSRs |
csr_access_check: process(execute_engine.i_reg, decode_aux, csr, debug_ctrl) |
variable csr_wacc_v : std_ulogic; -- actual CSR write |
-- variable csr_racc_v : std_ulogic; -- actual CSR read |
begin |
-- is this CSR instruction really going to write to a CSR? -- |
if (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrw_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrwi_c) then |
csr_wacc_v := '1'; -- always write CSR |
-- csr_racc_v := or_reduce_f(execute_engine.i_reg(instr_rd_msb_c downto instr_rd_lsb_c)); -- read if rd != 0 |
else -- clear/set |
csr_wacc_v := or_reduce_f(execute_engine.i_reg(instr_rs1_msb_c downto instr_rs1_lsb_c)); -- write allowed if rs1/uimm5 != 0 |
csr_wacc_v := not decode_aux.rs1_zero; -- write if rs1/uimm5 != 0 |
-- csr_racc_v := '1'; -- always read CSR |
end if; |
|
-- check CSR access -- |
1330,7 → 1291,7
when csr_fflags_c | csr_frm_c | csr_fcsr_c => |
csr_acc_valid <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zfinx); -- full access for everyone if FPU implemented |
|
-- machine trap setup & handling -- |
-- machine trap setup/handling & counters -- |
when csr_mstatus_c | csr_mstatush_c | csr_misa_c | csr_mie_c | csr_mtvec_c | csr_mscratch_c | csr_mepc_c | csr_mcause_c | csr_mip_c | csr_mtval_c | |
csr_mcycle_c | csr_mcycleh_c | csr_minstret_c | csr_minstreth_c | csr_mcountinhibit_c => |
-- NOTE: MISA, MIP and MTVAL are read-only in the NEORV32 but we do not cause an exception here for compatibility. |
1341,7 → 1302,8
when csr_mvendorid_c | csr_marchid_c | csr_mimpid_c | csr_mhartid_c | csr_mconfigptr_c => |
csr_acc_valid <= (not csr_wacc_v) and csr.priv_m_mode; -- M-mode only, read-only |
|
when csr_mcounteren_c | csr_menvcfg_c | csr_menvcfgh_c => -- only available if U mode is implemented |
-- user-mode registers -- |
when csr_mcounteren_c | csr_menvcfg_c | csr_menvcfgh_c => |
csr_acc_valid <= csr.priv_m_mode and bool_to_ulogic_f(CPU_EXTENSION_RISCV_U); |
|
-- physical memory protection (PMP) -- |
1375,7 → 1337,7
csr_mhpmevent27_c | csr_mhpmevent28_c | csr_mhpmevent29_c | csr_mhpmevent30_c | csr_mhpmevent31_c => |
csr_acc_valid <= csr.priv_m_mode and bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zihpm); -- M-mode only |
|
-- user-level counters/timers -- |
-- user-level counters/timers (read-only) -- |
when csr_cycle_c | csr_cycleh_c | csr_instret_c | csr_instreth_c | csr_time_c | csr_timeh_c => |
case csr.addr(1 downto 0) is |
when "00" => csr_acc_valid <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_Zicntr) and (not csr_wacc_v) and (csr.priv_m_mode or csr.mcounteren_cy); -- cyle[h]: M-mode, U-mode if authorized, implemented at all, read-only |
1429,57 → 1391,52
-- ------------------------------------------------------------ |
illegal_instruction <= '0'; |
-- illegal E-CPU register? -- |
if (CPU_EXTENSION_RISCV_E = true) and (execute_engine.i_reg(instr_rd_msb_c) = '1') then |
illegal_register <= '1'; |
end if; |
illegal_register <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_E) and execute_engine.i_reg(instr_rd_msb_c); |
|
when opcode_alu_c => -- check ALU.funct3 & ALU.funct7 |
-- ------------------------------------------------------------ |
if (decode_aux.is_m_mul = '1') then -- MUL |
if (CPU_EXTENSION_RISCV_M = false) and (CPU_EXTENSION_RISCV_Zmmul = false) then -- not implemented |
illegal_instruction <= '1'; |
end if; |
elsif (decode_aux.is_m_div = '1') then -- DIV |
if (CPU_EXTENSION_RISCV_M = false) then -- not implemented |
illegal_instruction <= '1'; |
end if; |
elsif (decode_aux.is_bitmanip_reg = '1') then -- bit manipulation |
if (CPU_EXTENSION_RISCV_B = false) then -- not implemented |
illegal_instruction <= '1'; |
end if; |
elsif ((execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_subadd_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sr_c)) and -- ADD/SUB or SRA/SRL check |
((execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c) /= "0000000") and |
(execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c) /= "0100000")) then -- ADD/SUB or SRA/SRL select |
if (((execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_subadd_c) or (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sr_c)) and |
(execute_engine.i_reg(instr_funct7_msb_c-2 downto instr_funct7_lsb_c) = "00000") and (execute_engine.i_reg(instr_funct7_msb_c) = '0')) or |
(((execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sll_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_slt_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sltu_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_xor_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_or_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_and_c)) and |
(execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0000000")) then -- valid base ALUI instruction? |
illegal_instruction <= '0'; |
elsif ((CPU_EXTENSION_RISCV_M = true) or (CPU_EXTENSION_RISCV_Zmmul = false)) and (decode_aux.is_m_mul = '1') then -- valid MUL instruction? |
illegal_instruction <= '0'; |
elsif (CPU_EXTENSION_RISCV_M = true) and (decode_aux.is_m_div = '1') then -- valid DIV instruction? |
illegal_instruction <= '0'; |
elsif (CPU_EXTENSION_RISCV_B = true) and (decode_aux.is_bitmanip_reg = '1') then -- valid BITMANIP instruction? |
illegal_instruction <= '0'; |
else |
illegal_instruction <= '1'; |
else |
illegal_instruction <= '0'; |
end if; |
-- illegal E-CPU register? -- |
if (CPU_EXTENSION_RISCV_E = true) and |
((execute_engine.i_reg(instr_rs2_msb_c) = '1') or (execute_engine.i_reg(instr_rs1_msb_c) = '1') or (execute_engine.i_reg(instr_rd_msb_c) = '1')) then |
illegal_register <= '1'; |
end if; |
illegal_register <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_E) and (execute_engine.i_reg(instr_rd_msb_c) or execute_engine.i_reg(instr_rs1_msb_c) or execute_engine.i_reg(instr_rs2_msb_c)); |
|
when opcode_alui_c => -- check ALUI.funct7 |
-- ------------------------------------------------------------ |
if (decode_aux.is_bitmanip_imm = '1') then -- bit manipulation |
if (CPU_EXTENSION_RISCV_B = false) then -- not implemented |
illegal_instruction <= '1'; |
end if; |
elsif ((execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sll_c) and |
(execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c) /= "0000000")) or -- shift logical left |
((execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sr_c) and |
((execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c) /= "0000000") and |
(execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c) /= "0100000"))) then -- shift right |
if (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_subadd_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_slt_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sltu_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_xor_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_or_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_and_c) or |
((execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sll_c) and -- shift logical left |
(execute_engine.i_reg(instr_funct7_msb_c downto instr_funct7_lsb_c) = "0000000")) or |
((execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_sr_c) and -- shift right |
((execute_engine.i_reg(instr_funct7_msb_c-2 downto instr_funct7_lsb_c) = "00000") and (execute_engine.i_reg(instr_funct7_msb_c) = '0'))) then -- valid base ALUI instruction? |
illegal_instruction <= '0'; |
elsif (CPU_EXTENSION_RISCV_B = true) and (decode_aux.is_bitmanip_imm = '1') then -- valid BITMANIP immediate instruction? |
illegal_instruction <= '0'; |
else |
illegal_instruction <= '1'; |
else |
illegal_instruction <= '0'; |
end if; |
-- illegal E-CPU register? -- |
if (CPU_EXTENSION_RISCV_E = true) and ((execute_engine.i_reg(instr_rs1_msb_c) = '1') or (execute_engine.i_reg(instr_rd_msb_c) = '1')) then |
illegal_register <= '1'; |
end if; |
illegal_register <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_E) and (execute_engine.i_reg(instr_rs1_msb_c) or execute_engine.i_reg(instr_rd_msb_c)); |
|
when opcode_load_c => -- check LOAD.funct3 |
-- ------------------------------------------------------------ |
1493,9 → 1450,7
illegal_instruction <= '1'; |
end if; |
-- illegal E-CPU register? -- |
if (CPU_EXTENSION_RISCV_E = true) and ((execute_engine.i_reg(instr_rs1_msb_c) = '1') or (execute_engine.i_reg(instr_rd_msb_c) = '1')) then |
illegal_register <= '1'; |
end if; |
illegal_register <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_E) and (execute_engine.i_reg(instr_rs1_msb_c) or execute_engine.i_reg(instr_rd_msb_c)); |
|
when opcode_store_c => -- check STORE.funct3 |
-- ------------------------------------------------------------ |
1507,8 → 1462,24
illegal_instruction <= '1'; |
end if; |
-- illegal E-CPU register? -- |
if (CPU_EXTENSION_RISCV_E = true) and ((execute_engine.i_reg(instr_rs2_msb_c) = '1') or (execute_engine.i_reg(instr_rs1_msb_c) = '1')) then |
illegal_register <= '1'; |
illegal_register <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_E) and (execute_engine.i_reg(instr_rs2_msb_c) or execute_engine.i_reg(instr_rs1_msb_c)); |
|
when opcode_atomic_c => -- atomic instructions |
-- ------------------------------------------------------------ |
if (CPU_EXTENSION_RISCV_A = true) then |
if (execute_engine.i_reg(instr_funct5_msb_c downto instr_funct5_lsb_c) = "00010") then -- LR |
illegal_instruction <= '0'; |
-- illegal E-CPU register? -- |
illegal_register <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_E) and (execute_engine.i_reg(instr_rs1_msb_c) or execute_engine.i_reg(instr_rd_msb_c)); |
elsif (execute_engine.i_reg(instr_funct5_msb_c downto instr_funct5_lsb_c) = "00011") then -- SC |
illegal_instruction <= '0'; |
-- illegal E-CPU register? -- |
illegal_register <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_E) and (execute_engine.i_reg(instr_rs1_msb_c) or execute_engine.i_reg(instr_rs2_msb_c) or execute_engine.i_reg(instr_rd_msb_c)); |
else |
illegal_instruction <= '1'; |
end if; |
else |
illegal_instruction <= '1'; |
end if; |
|
when opcode_branch_c => -- check BRANCH.funct3 |
1524,9 → 1495,7
illegal_instruction <= '1'; |
end if; |
-- illegal E-CPU register? -- |
if (CPU_EXTENSION_RISCV_E = true) and ((execute_engine.i_reg(instr_rs2_msb_c) = '1') or (execute_engine.i_reg(instr_rs1_msb_c) = '1')) then |
illegal_register <= '1'; |
end if; |
illegal_register <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_E) and (execute_engine.i_reg(instr_rs2_msb_c) or execute_engine.i_reg(instr_rs1_msb_c)); |
|
when opcode_jalr_c => -- check JALR.funct3 |
-- ------------------------------------------------------------ |
1536,11 → 1505,9
illegal_instruction <= '1'; |
end if; |
-- illegal E-CPU register? -- |
if (CPU_EXTENSION_RISCV_E = true) and ((execute_engine.i_reg(instr_rs1_msb_c) = '1') or (execute_engine.i_reg(instr_rd_msb_c) = '1')) then |
illegal_register <= '1'; |
end if; |
illegal_register <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_E) and (execute_engine.i_reg(instr_rs1_msb_c) or execute_engine.i_reg(instr_rd_msb_c)); |
|
when opcode_fence_c => -- fence instructions |
when opcode_fence_c => -- check FENCE.funct3 |
-- ------------------------------------------------------------ |
if ((execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_fencei_c) and (CPU_EXTENSION_RISCV_Zifencei = true)) or -- FENCE.I |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_fence_c) then -- FENCE |
1548,22 → 1515,20
else |
illegal_instruction <= '1'; |
end if; |
-- illegal E-CPU register? -- |
illegal_register <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_E) and (execute_engine.i_reg(instr_rs1_msb_c) or execute_engine.i_reg(instr_rd_msb_c)); |
|
when opcode_syscsr_c => -- check system instructions |
-- ------------------------------------------------------------ |
-- CSR access -- |
if (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrw_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrs_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrc_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrwi_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrsi_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrci_c) then |
-- valid CSR access? -- |
if (csr_acc_valid = '1') then |
illegal_instruction <= '0'; |
else |
illegal_instruction <= '1'; |
end if; |
if ((execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrw_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrs_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrc_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrwi_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrsi_c) or |
(execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = funct3_csrrci_c)) and |
(csr_acc_valid = '1') then -- valid CSR access? |
illegal_instruction <= '0'; |
-- illegal E-CPU register? -- |
if (CPU_EXTENSION_RISCV_E = true) then |
if (execute_engine.i_reg(instr_funct3_msb_c) = '0') then -- reg-reg CSR |
1572,28 → 1537,14
illegal_register <= execute_engine.i_reg(instr_rd_msb_c); |
end if; |
end if; |
|
-- ecall, ebreak, mret, wfi, dret -- |
elsif (execute_engine.i_reg(instr_rd_msb_c downto instr_rd_lsb_c) = "00000") and |
(execute_engine.i_reg(instr_rs1_msb_c downto instr_rs1_lsb_c) = "00000") then |
if (execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) = funct12_ecall_c) or -- ECALL |
(execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) = funct12_ebreak_c) or -- EBREAK |
((execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) = funct12_mret_c) and (csr.priv_m_mode = '1')) or -- MRET (only allowed in M-mode) |
((execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) = funct12_dret_c) and (CPU_EXTENSION_RISCV_DEBUG = true) and (debug_ctrl.running = '1')) or -- DRET (only allowed in D-mode) |
(execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) = funct12_wfi_c) then -- WFI (always allowed to execute) |
illegal_instruction <= '0'; |
else |
illegal_instruction <= '1'; |
end if; |
else |
illegal_instruction <= '1'; |
end if; |
|
when opcode_atomic_c => -- atomic instructions |
-- ------------------------------------------------------------ |
if (CPU_EXTENSION_RISCV_A = true) and -- atomic memory operations (A extension) enabled |
((execute_engine.i_reg(instr_funct5_msb_c downto instr_funct5_lsb_c) = funct5_a_lr_c) or -- LR |
(execute_engine.i_reg(instr_funct5_msb_c downto instr_funct5_lsb_c) = funct5_a_sc_c)) then -- SC |
elsif (execute_engine.i_reg(instr_funct3_msb_c downto instr_funct3_lsb_c) = "000") and |
(decode_aux.rs1_zero = '1') and (decode_aux.rd_zero = '1') and |
((execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) = funct12_ecall_c) or -- ECALL |
(execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) = funct12_ebreak_c) or -- EBREAK |
((execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) = funct12_mret_c) and (csr.priv_m_mode = '1')) or -- MRET (only allowed in M-mode) |
((execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) = funct12_dret_c) and (CPU_EXTENSION_RISCV_DEBUG = true) and (debug_ctrl.running = '1')) or -- DRET (only allowed in D-mode) |
(execute_engine.i_reg(instr_funct12_msb_c downto instr_funct12_lsb_c) = funct12_wfi_c)) then -- WFI (always allowed to execute) |
illegal_instruction <= '0'; |
else |
illegal_instruction <= '1'; |
1608,6 → 1559,9
else |
illegal_instruction <= '1'; |
end if; |
-- illegal E-CPU register? -- |
-- FIXME: rs2 is not checked! |
illegal_register <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_E) and (execute_engine.i_reg(instr_rs1_msb_c) or execute_engine.i_reg(instr_rd_msb_c)); |
|
when others => -- undefined instruction -> illegal! |
-- ------------------------------------------------------------ |
1644,54 → 1598,46
elsif rising_edge(clk_i) then |
if (CPU_EXTENSION_RISCV_Zicsr = true) then |
|
-- exception queue: misaligned load/store/instruction address |
-- exception queue: misaligned load/store/instruction address -- |
trap_ctrl.exc_buf(exception_lalign_c) <= (trap_ctrl.exc_buf(exception_lalign_c) or ma_load_i) and (not trap_ctrl.exc_ack); |
trap_ctrl.exc_buf(exception_salign_c) <= (trap_ctrl.exc_buf(exception_salign_c) or ma_store_i) and (not trap_ctrl.exc_ack); |
trap_ctrl.exc_buf(exception_ialign_c) <= (trap_ctrl.exc_buf(exception_ialign_c) or trap_ctrl.instr_ma) and (not trap_ctrl.exc_ack); |
|
-- exception queue: load/store/instruction bus access error |
-- exception queue: load/store/instruction bus access error -- |
trap_ctrl.exc_buf(exception_laccess_c) <= (trap_ctrl.exc_buf(exception_laccess_c) or be_load_i) and (not trap_ctrl.exc_ack); |
trap_ctrl.exc_buf(exception_saccess_c) <= (trap_ctrl.exc_buf(exception_saccess_c) or be_store_i) and (not trap_ctrl.exc_ack); |
trap_ctrl.exc_buf(exception_iaccess_c) <= (trap_ctrl.exc_buf(exception_iaccess_c) or trap_ctrl.instr_be) and (not trap_ctrl.exc_ack); |
|
-- exception queue: illegal instruction / environment call / break point |
-- exception queue: illegal instruction / environment calls -- |
trap_ctrl.exc_buf(exception_m_envcall_c) <= (trap_ctrl.exc_buf(exception_m_envcall_c) or (trap_ctrl.env_call and csr.priv_m_mode)) and (not trap_ctrl.exc_ack); |
trap_ctrl.exc_buf(exception_u_envcall_c) <= (trap_ctrl.exc_buf(exception_u_envcall_c) or (trap_ctrl.env_call and csr.priv_u_mode)) and (not trap_ctrl.exc_ack); |
trap_ctrl.exc_buf(exception_iillegal_c) <= (trap_ctrl.exc_buf(exception_iillegal_c) or trap_ctrl.instr_il) and (not trap_ctrl.exc_ack); |
|
-- exception queue: break point -- |
if (CPU_EXTENSION_RISCV_DEBUG = true) then |
trap_ctrl.exc_buf(exception_break_c) <= (trap_ctrl.exc_buf(exception_break_c) or |
( |
(trap_ctrl.break_point and csr.priv_m_mode and (not csr.dcsr_ebreakm) and (not debug_ctrl.running)) or -- enable break to machine-trap-handler when in machine mode on "ebreak" |
(trap_ctrl.break_point and csr.priv_u_mode and (not csr.dcsr_ebreaku) and (not debug_ctrl.running)) -- enable break to machine-trap-handler when in user mode on "ebreak" |
) |
) and (not trap_ctrl.exc_ack); |
trap_ctrl.exc_buf(exception_break_c) <= (not trap_ctrl.exc_ack) and (trap_ctrl.exc_buf(exception_break_c) or |
((trap_ctrl.break_point and csr.priv_m_mode and (not csr.dcsr_ebreakm) and (not debug_ctrl.running)) or -- enable break to machine-trap-handler when in machine mode on "ebreak" |
(trap_ctrl.break_point and csr.priv_u_mode and (not csr.dcsr_ebreaku) and (not debug_ctrl.running)))); -- enable break to machine-trap-handler when in user mode on "ebreak" |
else |
trap_ctrl.exc_buf(exception_break_c) <= (trap_ctrl.exc_buf(exception_break_c) or trap_ctrl.break_point) and (not trap_ctrl.exc_ack); |
end if; |
|
-- enter debug mode -- |
if (CPU_EXTENSION_RISCV_DEBUG = true) then |
trap_ctrl.exc_buf(exception_db_break_c) <= (trap_ctrl.exc_buf(exception_db_break_c) or debug_ctrl.trig_break) and (not trap_ctrl.exc_ack); |
trap_ctrl.irq_buf(interrupt_db_halt_c) <= debug_ctrl.trig_halt; |
trap_ctrl.irq_buf(interrupt_db_step_c) <= debug_ctrl.trig_step; |
else |
trap_ctrl.exc_buf(exception_db_break_c) <= '0'; |
trap_ctrl.irq_buf(interrupt_db_halt_c) <= '0'; |
trap_ctrl.irq_buf(interrupt_db_step_c) <= '0'; |
end if; |
-- exception buffer: enter debug mode -- |
trap_ctrl.exc_buf(exception_db_break_c) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_DEBUG) and (trap_ctrl.exc_buf(exception_db_break_c) or debug_ctrl.trig_break) and (not trap_ctrl.exc_ack); |
trap_ctrl.irq_buf(interrupt_db_halt_c) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_DEBUG) and debug_ctrl.trig_halt; |
trap_ctrl.irq_buf(interrupt_db_step_c) <= bool_to_ulogic_f(CPU_EXTENSION_RISCV_DEBUG) and debug_ctrl.trig_step; |
|
-- interrupt buffer: machine software/external/timer interrupt |
-- interrupt buffer: machine software/external/timer interrupt -- |
trap_ctrl.irq_buf(interrupt_msw_irq_c) <= csr.mie_msie and msw_irq_i; |
trap_ctrl.irq_buf(interrupt_mext_irq_c) <= csr.mie_meie and mext_irq_i; |
trap_ctrl.irq_buf(interrupt_mtime_irq_c) <= csr.mie_mtie and mtime_irq_i; |
-- interrupt queue: NEORV32-specific fast interrupts |
for i in 0 to 15 loop |
trap_ctrl.irq_buf(interrupt_firq_0_c+i) <= csr.mie_firqe(i) and firq_i(i); |
end loop; |
|
-- trap control -- |
-- interrupt buffer: NEORV32-specific fast interrupts (FIRQ) -- |
trap_ctrl.irq_buf(interrupt_firq_15_c downto interrupt_firq_0_c) <= csr.mie_firqe(15 downto 0) and firq_i(15 downto 0); |
|
-- trap environment control -- |
if (trap_ctrl.env_start = '0') then -- no started trap handler |
if (trap_ctrl.exc_fire = '1') or ((trap_ctrl.irq_fire = '1') and -- trap triggered! |
if (trap_ctrl.exc_fire = '1') or ((trap_ctrl.irq_fire = '1') and -- exception triggered! |
((execute_engine.state = EXECUTE) or (execute_engine.state = TRAP_ENTER))) then -- fire IRQs in EXECUTE or TRAP state only to continue execution even on permanent IRQ |
trap_ctrl.cause <= trap_ctrl.cause_nxt; -- capture source ID for program (for mcause csr) |
trap_ctrl.exc_ack <= '1'; -- clear exceptions (no ack mask: these have highest priority and are always evaluated first!) |
1727,7 → 1673,7
-- exceptions (from trap_ctrl.irq_buf). |
|
-- ---------------------------------------------------------------------------------------- |
-- the following traps are caused by *synchronous* exceptions; here we do not need a |
-- the following traps are caused by *synchronous* exceptions; we do not need a |
-- specific acknowledge mask since only _one_ exception (the one with highest priority) |
-- is allowed to kick in at once |
-- ---------------------------------------------------------------------------------------- |
1917,45 → 1863,45
-- NOT actually have a real reset by default (def_rst_val_c = '-') and have to be explicitly initialized by software! |
-- see: https://forums.xilinx.com/t5/General-Technical-Discussion/quot-Don-t-care-quot-reset-value/td-p/412845 |
if (rstn_i = '0') then |
csr.we <= '0'; |
csr.we <= '0'; |
-- |
csr.mstatus_mie <= '0'; |
csr.mstatus_mpie <= '0'; |
csr.mstatus_mpp <= (others => '0'); |
csr.privilege <= priv_mode_m_c; -- start in MACHINE mode |
csr.mie_msie <= def_rst_val_c; |
csr.mie_meie <= def_rst_val_c; |
csr.mie_mtie <= def_rst_val_c; |
csr.mie_firqe <= (others => def_rst_val_c); |
csr.mtvec <= (others => def_rst_val_c); |
csr.mscratch <= x"19880704"; |
csr.mepc <= (others => def_rst_val_c); |
csr.mcause <= (others => def_rst_val_c); |
csr.mtval <= (others => def_rst_val_c); |
csr.mstatus_mie <= '0'; |
csr.mstatus_mpie <= '0'; |
csr.mstatus_mpp <= (others => '0'); |
csr.privilege <= priv_mode_m_c; -- start in MACHINE mode |
csr.mie_msie <= def_rst_val_c; |
csr.mie_meie <= def_rst_val_c; |
csr.mie_mtie <= def_rst_val_c; |
csr.mie_firqe <= (others => def_rst_val_c); |
csr.mtvec <= (others => def_rst_val_c); |
csr.mscratch <= x"19880704"; |
csr.mepc <= (others => def_rst_val_c); |
csr.mcause <= (others => def_rst_val_c); |
csr.mtval <= (others => def_rst_val_c); |
-- |
csr.pmpcfg <= (others => (others => '0')); |
csr.pmpaddr <= (others => (others => def_rst_val_c)); |
csr.pmpcfg <= (others => (others => '0')); |
csr.pmpaddr <= (others => (others => def_rst_val_c)); |
-- |
csr.mhpmevent <= (others => (others => def_rst_val_c)); |
csr.mhpmevent <= (others => (others => def_rst_val_c)); |
-- |
csr.mcounteren_cy <= def_rst_val_c; |
csr.mcounteren_tm <= def_rst_val_c; |
csr.mcounteren_ir <= def_rst_val_c; |
csr.mcounteren_cy <= def_rst_val_c; |
csr.mcounteren_tm <= def_rst_val_c; |
csr.mcounteren_ir <= def_rst_val_c; |
-- |
csr.mcountinhibit_cy <= def_rst_val_c; |
csr.mcountinhibit_ir <= def_rst_val_c; |
csr.mcountinhibit_hpm <= (others => def_rst_val_c); |
-- |
csr.fflags <= (others => def_rst_val_c); |
csr.frm <= (others => def_rst_val_c); |
csr.fflags <= (others => def_rst_val_c); |
csr.frm <= (others => def_rst_val_c); |
-- |
csr.dcsr_ebreakm <= '0'; |
csr.dcsr_ebreaku <= '0'; |
csr.dcsr_step <= '0'; |
csr.dcsr_prv <= (others => def_rst_val_c); |
csr.dcsr_cause <= (others => def_rst_val_c); |
csr.dpc <= (others => def_rst_val_c); |
csr.dscratch0 <= (others => def_rst_val_c); |
csr.dcsr_ebreakm <= '0'; |
csr.dcsr_ebreaku <= '0'; |
csr.dcsr_step <= '0'; |
csr.dcsr_prv <= (others => def_rst_val_c); |
csr.dcsr_cause <= (others => def_rst_val_c); |
csr.dpc <= (others => def_rst_val_c); |
csr.dscratch0 <= (others => def_rst_val_c); |
|
elsif rising_edge(clk_i) then |
-- write access? -- |
1965,22 → 1911,20
-- -------------------------------------------------------------------------------- |
-- CSR access by application software |
-- -------------------------------------------------------------------------------- |
if (csr.we = '1') and (trap_ctrl.exc_buf(exception_iillegal_c) = '0') then -- manual update if not illegal instruction |
if (csr.we = '1') and (trap_ctrl.exc_buf(exception_iillegal_c) = '0') then -- manual write access and not illegal instruction |
|
-- user floating-point CSRs -- |
-- -------------------------------------------------------------------- |
if (CPU_EXTENSION_RISCV_Zfinx = true) then -- floating point CSR class |
if (csr.addr(11 downto 4) = csr_class_float_c) and (csr.addr(3 downto 2) = csr_fcsr_c(3 downto 2)) then |
case csr.addr(1 downto 0) is |
when "01" => -- R/W: fflags - floating-point (FPU) exception flags |
csr.fflags <= csr.wdata(4 downto 0); |
when "10" => -- R/W: frm - floating-point (FPU) rounding mode |
csr.frm <= csr.wdata(2 downto 0); |
when "11" => -- R/W: fcsr - floating-point (FPU) control/status (frm + fflags) |
csr.frm <= csr.wdata(7 downto 5); |
csr.fflags <= csr.wdata(4 downto 0); |
when others => NULL; |
end case; |
if (csr.addr(11 downto 2) = csr_class_float_c) then |
if (csr.addr(1 downto 0) = "01") then -- R/W: fflags - floating-point (FPU) exception flags |
csr.fflags <= csr.wdata(4 downto 0); |
elsif (csr.addr(1 downto 0) = "10") then -- R/W: frm - floating-point (FPU) rounding mode |
csr.frm <= csr.wdata(2 downto 0); |
elsif (csr.addr(1 downto 0) = "11") then -- R/W: fcsr - floating-point (FPU) control/status (frm + fflags) |
csr.frm <= csr.wdata(7 downto 5); |
csr.fflags <= csr.wdata(4 downto 0); |
end if; |
end if; |
end if; |
|
2021,18 → 1965,18
|
-- machine trap handling -- |
-- -------------------------------------------------------------------- |
if (csr.addr(11 downto 4) = csr_class_trap_c) then -- machine trap handling CSR class |
if (csr.addr(11 downto 3) = csr_class_trap_c) then -- machine trap handling CSR class |
-- R/W: mscratch - machine scratch register -- |
if (csr.addr(3 downto 0) = csr_mscratch_c(3 downto 0)) then |
if (csr.addr(2 downto 0) = csr_mscratch_c(2 downto 0)) then |
csr.mscratch <= csr.wdata; |
end if; |
-- R/W: mepc - machine exception program counter -- |
if (csr.addr(3 downto 0) = csr_mepc_c(3 downto 0)) then |
if (csr.addr(2 downto 0) = csr_mepc_c(2 downto 0)) then |
csr.mepc <= csr.wdata; |
end if; |
-- R/W: mcause - machine trap cause -- |
if (csr.addr(3 downto 0) = csr_mcause_c(3 downto 0)) then |
csr.mcause(csr.mcause'left) <= csr.wdata(31); -- 1: interrupt, 0: exception |
if (csr.addr(2 downto 0) = csr_mcause_c(2 downto 0)) then |
csr.mcause(csr.mcause'left) <= csr.wdata(31); -- 1: async/interrupt, 0: sync/exception |
csr.mcause(4 downto 0) <= csr.wdata(4 downto 0); -- identifier |
end if; |
end if; |
2129,7 → 2073,7
|
-- floating-point (FPU) exception flags -- |
-- -------------------------------------------------------------------- |
if (CPU_EXTENSION_RISCV_Zfinx = true) then |
if (CPU_EXTENSION_RISCV_Zfinx = true) and (trap_ctrl.exc_buf(exception_iillegal_c) = '0') then -- no illegal instruction |
csr.fflags <= csr.fflags or fpu_flags_i; -- accumulate flags ("accrued exception flags") |
end if; |
|
2312,7 → 2256,7
-- ------------------------------------------------------------------------------------------- |
csr_counters: process(rstn_i, clk_i) |
begin |
-- Counter CSRs (each counter is split into two 32-bit counters - coupled via an MSB overflow detector) |
-- Counter CSRs (each counter is split into two 32-bit counters - coupled via an MSB overflow FF) |
if (rstn_i = '0') then |
csr.mcycle <= (others => def_rst_val_c); |
csr.mcycle_ovfl <= (others => def_rst_val_c); |
2327,7 → 2271,7
|
-- [m]cycle -- |
if (cpu_cnt_lo_width_c > 0) and (CPU_EXTENSION_RISCV_Zicntr = true) then |
csr.mcycle_ovfl(0) <= csr.mcycle_nxt(csr.mcycle_nxt'left); |
csr.mcycle_ovfl(0) <= csr.mcycle_nxt(csr.mcycle_nxt'left) and (not csr.mcountinhibit_cy); |
if (csr.we = '1') and (csr.addr = csr_mcycle_c) then -- write access |
csr.mcycle(cpu_cnt_lo_width_c-1 downto 0) <= csr.wdata(cpu_cnt_lo_width_c-1 downto 0); |
elsif (csr.mcountinhibit_cy = '0') and (cnt_event(hpmcnt_event_cy_c) = '1') then -- non-inhibited automatic update |
2342,7 → 2286,7
if (cpu_cnt_hi_width_c > 0) and (CPU_EXTENSION_RISCV_Zicntr = true) then |
if (csr.we = '1') and (csr.addr = csr_mcycleh_c) then -- write access |
csr.mcycleh(cpu_cnt_hi_width_c-1 downto 0) <= csr.wdata(cpu_cnt_hi_width_c-1 downto 0); |
elsif (csr.mcountinhibit_cy = '0') and (cnt_event(hpmcnt_event_cy_c) = '1') then -- non-inhibited automatic update |
else -- automatic update |
csr.mcycleh(cpu_cnt_hi_width_c-1 downto 0) <= std_ulogic_vector(unsigned(csr.mcycleh(cpu_cnt_hi_width_c-1 downto 0)) + unsigned(csr.mcycle_ovfl)); |
end if; |
else |
2352,7 → 2296,7
|
-- [m]instret -- |
if (cpu_cnt_lo_width_c > 0) and (CPU_EXTENSION_RISCV_Zicntr = true) then |
csr.minstret_ovfl(0) <= csr.minstret_nxt(csr.minstret_nxt'left); |
csr.minstret_ovfl(0) <= csr.minstret_nxt(csr.minstret_nxt'left) and (not csr.mcountinhibit_ir); |
if (csr.we = '1') and (csr.addr = csr_minstret_c) then -- write access |
csr.minstret(cpu_cnt_lo_width_c-1 downto 0) <= csr.wdata(cpu_cnt_lo_width_c-1 downto 0); |
elsif (csr.mcountinhibit_ir = '0') and (cnt_event(hpmcnt_event_ir_c) = '1') then -- non-inhibited automatic update |
2367,7 → 2311,7
if (cpu_cnt_hi_width_c > 0) and (CPU_EXTENSION_RISCV_Zicntr = true) then |
if (csr.we = '1') and (csr.addr = csr_minstreth_c) then -- write access |
csr.minstreth(cpu_cnt_hi_width_c-1 downto 0) <= csr.wdata(cpu_cnt_hi_width_c-1 downto 0); |
elsif (csr.mcountinhibit_ir = '0') and (cnt_event(hpmcnt_event_ir_c) = '1') then -- non-inhibited automatic update |
else -- automatic update |
csr.minstreth(cpu_cnt_hi_width_c-1 downto 0) <= std_ulogic_vector(unsigned(csr.minstreth(cpu_cnt_hi_width_c-1 downto 0)) + unsigned(csr.minstret_ovfl)); |
end if; |
else |
2380,7 → 2324,7
|
-- [m]hpmcounter* -- |
if (hpm_cnt_lo_width_c > 0) and (CPU_EXTENSION_RISCV_Zihpm = true) then |
csr.mhpmcounter_ovfl(i)(0) <= csr.mhpmcounter_nxt(i)(csr.mhpmcounter_nxt(i)'left); |
csr.mhpmcounter_ovfl(i)(0) <= csr.mhpmcounter_nxt(i)(csr.mhpmcounter_nxt(i)'left) and (not csr.mcountinhibit_hpm(i)); |
if (csr.we = '1') and (csr.addr = std_ulogic_vector(unsigned(csr_mhpmcounter3_c) + i)) then -- write access |
csr.mhpmcounter(i)(hpm_cnt_lo_width_c-1 downto 0) <= csr.wdata(hpm_cnt_lo_width_c-1 downto 0); |
elsif (csr.mcountinhibit_hpm(i) = '0') and (hpmcnt_trigger(i) = '1') then -- non-inhibited automatic update |
2395,7 → 2339,7
if (hpm_cnt_hi_width_c > 0) and (CPU_EXTENSION_RISCV_Zihpm = true) then |
if (csr.we = '1') and (csr.addr = std_ulogic_vector(unsigned(csr_mhpmcounter3h_c) + i)) then -- write access |
csr.mhpmcounterh(i)(hpm_cnt_hi_width_c-1 downto 0) <= csr.wdata(hpm_cnt_hi_width_c-1 downto 0); |
elsif (csr.mcountinhibit_hpm(i) = '0') and (hpmcnt_trigger(i) = '1') then -- non-inhibited automatic update |
else -- automatic update |
csr.mhpmcounterh(i)(hpm_cnt_hi_width_c-1 downto 0) <= std_ulogic_vector(unsigned(csr.mhpmcounterh(i)(hpm_cnt_hi_width_c-1 downto 0)) + unsigned(csr.mhpmcounter_ovfl(i))); |
end if; |
else |
2409,7 → 2353,7
|
|
-- mcycle & minstret increment LOW -- |
csr.mcycle_nxt <= std_ulogic_vector(unsigned('0' & csr.mcycle) + 1); |
csr.mcycle_nxt <= std_ulogic_vector(unsigned('0' & csr.mcycle) + 1); |
csr.minstret_nxt <= std_ulogic_vector(unsigned('0' & csr.minstret) + 1); |
|
-- hpm counter increment LOW -- |
2442,11 → 2386,8
hpmcnt_ctrl: process(rstn_i, clk_i) |
begin |
if (rstn_i = '0') then |
cnt_event <= (others => def_rst_val_c); |
hpmcnt_trigger <= (others => def_rst_val_c); |
elsif rising_edge(clk_i) then |
-- buffer event sources -- |
cnt_event <= cnt_event_nxt; |
-- enable selected triggers by ANDing actual events and according CSR configuration bits -- |
-- OR everything to see if counter should increment -- |
hpmcnt_trigger <= (others => '0'); -- default |
2459,26 → 2400,26
end process hpmcnt_ctrl; |
|
-- counter event trigger - RISC-V-specific -- |
cnt_event_nxt(hpmcnt_event_cy_c) <= not execute_engine.sleep; -- active cycle |
cnt_event_nxt(hpmcnt_event_never_c) <= '0'; -- undefined (never) |
cnt_event_nxt(hpmcnt_event_ir_c) <= '1' when (execute_engine.state = EXECUTE) else '0'; -- retired instruction |
cnt_event(hpmcnt_event_cy_c) <= not execute_engine.sleep; -- active cycle |
cnt_event(hpmcnt_event_never_c) <= '0'; -- undefined (never) |
cnt_event(hpmcnt_event_ir_c) <= '1' when (execute_engine.state = EXECUTE) else '0'; -- retired instruction |
|
-- counter event trigger - custom / NEORV32-specific -- |
cnt_event_nxt(hpmcnt_event_cir_c) <= '1' when (execute_engine.state = EXECUTE) and (execute_engine.is_ci = '1') else '0'; -- retired compressed instruction |
cnt_event_nxt(hpmcnt_event_wait_if_c) <= '1' when (fetch_engine.state = IFETCH_ISSUE) and (fetch_engine.state_prev = IFETCH_ISSUE) else '0'; -- instruction fetch memory wait cycle |
cnt_event_nxt(hpmcnt_event_wait_ii_c) <= '1' when (execute_engine.state = DISPATCH) and (execute_engine.state_prev = DISPATCH) else '0'; -- instruction issue wait cycle |
cnt_event_nxt(hpmcnt_event_wait_mc_c) <= '1' when (execute_engine.state = ALU_WAIT) else '0'; -- multi-cycle alu-operation wait cycle |
cnt_event(hpmcnt_event_cir_c) <= '1' when (execute_engine.state = EXECUTE) and (execute_engine.is_ci = '1') else '0'; -- retired compressed instruction |
cnt_event(hpmcnt_event_wait_if_c) <= '1' when (fetch_engine.state = IFETCH_ISSUE) and (fetch_engine.state_prev = IFETCH_ISSUE) else '0'; -- instruction fetch memory wait cycle |
cnt_event(hpmcnt_event_wait_ii_c) <= '1' when (execute_engine.state = DISPATCH) and (execute_engine.state_prev = DISPATCH) else '0'; -- instruction issue wait cycle |
cnt_event(hpmcnt_event_wait_mc_c) <= '1' when (execute_engine.state = ALU_WAIT) else '0'; -- multi-cycle alu-operation wait cycle |
|
cnt_event_nxt(hpmcnt_event_load_c) <= '1' when (execute_engine.state = LOADSTORE_1) and (ctrl(ctrl_bus_rd_c) = '1') else '0'; -- load operation |
cnt_event_nxt(hpmcnt_event_store_c) <= '1' when (execute_engine.state = LOADSTORE_1) and (ctrl(ctrl_bus_wr_c) = '1') else '0'; -- store operation |
cnt_event_nxt(hpmcnt_event_wait_ls_c) <= '1' when (execute_engine.state = LOADSTORE_2) and (execute_engine.state_prev = LOADSTORE_2) else '0'; -- load/store memory wait cycle |
cnt_event(hpmcnt_event_load_c) <= '1' when (ctrl(ctrl_bus_rd_c) = '1') else '0'; -- load operation |
cnt_event(hpmcnt_event_store_c) <= '1' when (ctrl(ctrl_bus_wr_c) = '1') else '0'; -- store operation |
cnt_event(hpmcnt_event_wait_ls_c) <= '1' when (execute_engine.state = LOADSTORE_2) and (execute_engine.state_prev = LOADSTORE_2) else '0'; -- load/store memory wait cycle |
|
cnt_event_nxt(hpmcnt_event_jump_c) <= '1' when (execute_engine.state = BRANCH) and (execute_engine.i_reg(instr_opcode_lsb_c+2) = '1') else '0'; -- jump (unconditional) |
cnt_event_nxt(hpmcnt_event_branch_c) <= '1' when (execute_engine.state = BRANCH) and (execute_engine.i_reg(instr_opcode_lsb_c+2) = '0') else '0'; -- branch (conditional, taken or not taken) |
cnt_event_nxt(hpmcnt_event_tbranch_c) <= '1' when (execute_engine.state = BRANCH) and (execute_engine.i_reg(instr_opcode_lsb_c+2) = '0') and (execute_engine.branch_taken = '1') else '0'; -- taken branch (conditional) |
cnt_event(hpmcnt_event_jump_c) <= '1' when (execute_engine.state = BRANCH) and (execute_engine.i_reg(instr_opcode_lsb_c+2) = '1') else '0'; -- jump (unconditional) |
cnt_event(hpmcnt_event_branch_c) <= '1' when (execute_engine.state = BRANCH) and (execute_engine.i_reg(instr_opcode_lsb_c+2) = '0') else '0'; -- branch (conditional, taken or not taken) |
cnt_event(hpmcnt_event_tbranch_c) <= '1' when (execute_engine.state = BRANCH) and (execute_engine.i_reg(instr_opcode_lsb_c+2) = '0') and (execute_engine.branch_taken = '1') else '0'; -- taken branch (conditional) |
|
cnt_event_nxt(hpmcnt_event_trap_c) <= '1' when (trap_ctrl.env_start_ack = '1') else '0'; -- entered trap |
cnt_event_nxt(hpmcnt_event_illegal_c) <= '1' when (trap_ctrl.env_start_ack = '1') and (trap_ctrl.cause = trap_iil_c) else '0'; -- illegal operation |
cnt_event(hpmcnt_event_trap_c) <= '1' when (trap_ctrl.env_start_ack = '1') else '0'; -- entered trap |
cnt_event(hpmcnt_event_illegal_c) <= '1' when (trap_ctrl.env_start_ack = '1') and (trap_ctrl.cause = trap_iil_c) else '0'; -- illegal operation |
|
|
-- Control and Status Registers - Read Access --------------------------------------------- |
2798,12 → 2739,12
debug_control: process(rstn_i, clk_i) |
begin |
if (rstn_i = '0') then |
debug_ctrl.state <= DEBUG_OFFLINE; |
debug_ctrl.state <= DEBUG_OFFLINE; |
debug_ctrl.ext_halt_req <= '0'; |
elsif rising_edge(clk_i) then |
if (CPU_EXTENSION_RISCV_DEBUG = true) then |
|
-- rising edge detector -- |
-- external halt request (from Debug Module) -- |
debug_ctrl.ext_halt_req <= db_halt_req_i; |
|
-- state machine -- |
2836,7 → 2777,7
|
end case; |
else -- debug mode NOT implemented |
debug_ctrl.state <= DEBUG_OFFLINE; |
debug_ctrl.state <= DEBUG_OFFLINE; |
debug_ctrl.ext_halt_req <= '0'; |
end if; |
end if; |
2866,8 → 2807,8
csr.dcsr_rd(31 downto 28) <= "0100"; -- xdebugver: external debug support compatible to spec |
csr.dcsr_rd(27 downto 16) <= (others => '0'); -- reserved |
csr.dcsr_rd(15) <= csr.dcsr_ebreakm; -- ebreakm: what happens on ebreak in m-mode? (normal trap OR debug-enter) |
csr.dcsr_rd(14) <= '0'; -- ebreakh: not available |
csr.dcsr_rd(13) <= '0'; -- ebreaks: not available |
csr.dcsr_rd(14) <= '0'; -- ebreakh: hypervisor mode not implemented |
csr.dcsr_rd(13) <= '0'; -- ebreaks: supervisor mode not implemented |
csr.dcsr_rd(12) <= csr.dcsr_ebreaku when (CPU_EXTENSION_RISCV_U = true) else '0'; -- ebreaku: what happens on ebreak in u-mode? (normal trap OR debug-enter) |
csr.dcsr_rd(11) <= '0'; -- stepie: interrupts are disabled during single-stepping |
csr.dcsr_rd(10) <= '0'; -- stopcount: counters increment as usual FIXME ??? |
/rtl/core/neorv32_debug_dtm.vhd
37,7 → 37,6
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity neorv32_debug_dtm is |
generic ( |
72,10 → 71,27
architecture neorv32_debug_dtm_rtl of neorv32_debug_dtm is |
|
-- DMI Configuration (fixed!) -- |
constant dmi_idle_c : std_ulogic_vector(02 downto 0) := "010"; -- minimum number if idle cycles |
constant dmi_idle_c : std_ulogic_vector(02 downto 0) := "000"; -- no idle cycles required |
constant dmi_version_c : std_ulogic_vector(03 downto 0) := "0001"; -- version (0.13) |
constant dmi_abits_c : std_ulogic_vector(05 downto 0) := "000111"; -- number of DMI address bits (7) |
|
-- tap JTAG signal synchronizer -- |
type tap_sync_t is record |
-- internal -- |
trst_ff : std_ulogic_vector(2 downto 0); |
tck_ff : std_ulogic_vector(2 downto 0); |
tdi_ff : std_ulogic_vector(2 downto 0); |
tms_ff : std_ulogic_vector(2 downto 0); |
-- external -- |
trst : std_ulogic; |
tck_rising : std_ulogic; |
tck_falling : std_ulogic; |
tdi : std_ulogic; |
tdo : std_ulogic; |
tms : std_ulogic; |
end record; |
signal tap_sync : tap_sync_t; |
|
-- tap controller - fsm -- |
type tap_ctrl_state_t is (LOGIC_RESET, DR_SCAN, DR_CAPTURE, DR_SHIFT, DR_EXIT1, DR_PAUSE, DR_EXIT2, DR_UPDATE, |
RUN_IDLE, IR_SCAN, IR_CAPTURE, IR_SHIFT, IR_EXIT1, IR_PAUSE, IR_EXIT2, IR_UPDATE); |
82,11 → 98,6
type tap_ctrl_t is record |
state : tap_ctrl_state_t; |
state_prev : tap_ctrl_state_t; |
trst_sync : std_ulogic_vector(01 downto 0); |
tck_sync : std_ulogic_vector(02 downto 0); |
tdi_sync : std_ulogic_vector(01 downto 0); |
tdo_sync : std_ulogic; |
tms_sync : std_ulogic_vector(01 downto 0); |
end record; |
signal tap_ctrl : tap_ctrl_t; |
|
118,49 → 129,64
|
begin |
|
-- JTAG Signal Synchronizer --------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
tap_synchronizer: process(rstn_i, clk_i) |
begin |
if rising_edge(clk_i) then |
tap_sync.trst_ff <= tap_sync.trst_ff(1 downto 0) & jtag_trst_i; |
tap_sync.tck_ff <= tap_sync.tck_ff( 1 downto 0) & jtag_tck_i; |
tap_sync.tdi_ff <= tap_sync.tdi_ff( 1 downto 0) & jtag_tdi_i; |
tap_sync.tms_ff <= tap_sync.tms_ff( 1 downto 0) & jtag_tms_i; |
if (tap_sync.tck_falling = '1') then -- update output data TDO on falling edge of TCK |
jtag_tdo_o <= tap_sync.tdo; |
end if; |
end if; |
end process tap_synchronizer; |
|
-- JTAG reset -- |
tap_sync.trst <= '0' when (tap_sync.trst_ff(2 downto 1) = "00") else '1'; |
|
-- JTAG clock edge -- |
tap_sync.tck_rising <= '1' when (tap_sync.tck_ff(2 downto 1) = "01") else '0'; |
tap_sync.tck_falling <= '1' when (tap_sync.tck_ff(2 downto 1) = "10") else '0'; |
|
-- JTAG test mode select -- |
tap_sync.tms <= tap_sync.tms_ff(2); |
|
-- JTAG serial data input -- |
tap_sync.tdi <= tap_sync.tdi_ff(2); |
|
|
-- Tap Control FSM ------------------------------------------------------------------------ |
-- ------------------------------------------------------------------------------------------- |
tap_control: process(rstn_i, clk_i) |
begin |
if (rstn_i = '0') then |
tap_ctrl.trst_sync <= (others => '0'); |
tap_ctrl.tck_sync <= (others => '0'); |
tap_ctrl.tdi_sync <= (others => '0'); |
tap_ctrl.tms_sync <= (others => '0'); |
jtag_tdo_o <= '0'; |
-- |
tap_ctrl.state <= LOGIC_RESET; |
tap_ctrl.state_prev <= LOGIC_RESET; |
elsif rising_edge(clk_i) then |
-- synchronizer -- |
tap_ctrl.trst_sync <= tap_ctrl.trst_sync(0) & jtag_trst_i; |
tap_ctrl.tck_sync <= tap_ctrl.tck_sync(1 downto 0) & jtag_tck_i; |
tap_ctrl.tdi_sync <= tap_ctrl.tdi_sync(0) & jtag_tdi_i; |
tap_ctrl.tms_sync <= tap_ctrl.tms_sync(0) & jtag_tms_i; |
jtag_tdo_o <= tap_ctrl.tdo_sync; |
|
-- state machine -- |
tap_ctrl.state_prev <= tap_ctrl.state; |
if (tap_ctrl.trst_sync(1) = '0') then -- reset |
if (tap_sync.trst = '0') then -- reset |
tap_ctrl.state <= LOGIC_RESET; |
elsif (tap_ctrl.tck_sync(2) = '0') and (tap_ctrl.tck_sync(1) = '1') then -- clock pulse (trigger on rising edge) |
elsif (tap_sync.tck_rising = '1') then -- clock pulse (evaluate TMS on the rising edge of TCK) |
case tap_ctrl.state is -- JTAG state machine |
when LOGIC_RESET => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= RUN_IDLE; else tap_ctrl.state <= LOGIC_RESET; end if; |
when RUN_IDLE => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= RUN_IDLE; else tap_ctrl.state <= DR_SCAN; end if; |
when DR_SCAN => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= DR_CAPTURE; else tap_ctrl.state <= IR_SCAN; end if; |
when DR_CAPTURE => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= DR_SHIFT; else tap_ctrl.state <= DR_EXIT1; end if; |
when DR_SHIFT => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= DR_SHIFT; else tap_ctrl.state <= DR_EXIT1; end if; |
when DR_EXIT1 => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= DR_PAUSE; else tap_ctrl.state <= DR_UPDATE; end if; |
when DR_PAUSE => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= DR_PAUSE; else tap_ctrl.state <= DR_EXIT2; end if; |
when DR_EXIT2 => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= DR_SHIFT; else tap_ctrl.state <= DR_UPDATE; end if; |
when DR_UPDATE => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= RUN_IDLE; else tap_ctrl.state <= DR_SCAN; end if; |
when IR_SCAN => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= IR_CAPTURE; else tap_ctrl.state <= LOGIC_RESET; end if; |
when IR_CAPTURE => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= IR_SHIFT; else tap_ctrl.state <= IR_EXIT1; end if; |
when IR_SHIFT => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= IR_SHIFT; else tap_ctrl.state <= IR_EXIT1; end if; |
when IR_EXIT1 => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= IR_PAUSE; else tap_ctrl.state <= IR_UPDATE; end if; |
when IR_PAUSE => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= IR_PAUSE; else tap_ctrl.state <= IR_EXIT2; end if; |
when IR_EXIT2 => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= IR_SHIFT; else tap_ctrl.state <= IR_UPDATE; end if; |
when IR_UPDATE => if (tap_ctrl.tms_sync(1) = '0') then tap_ctrl.state <= RUN_IDLE; else tap_ctrl.state <= DR_SCAN; end if; |
when LOGIC_RESET => if (tap_sync.tms = '0') then tap_ctrl.state <= RUN_IDLE; else tap_ctrl.state <= LOGIC_RESET; end if; |
when RUN_IDLE => if (tap_sync.tms = '0') then tap_ctrl.state <= RUN_IDLE; else tap_ctrl.state <= DR_SCAN; end if; |
when DR_SCAN => if (tap_sync.tms = '0') then tap_ctrl.state <= DR_CAPTURE; else tap_ctrl.state <= IR_SCAN; end if; |
when DR_CAPTURE => if (tap_sync.tms = '0') then tap_ctrl.state <= DR_SHIFT; else tap_ctrl.state <= DR_EXIT1; end if; |
when DR_SHIFT => if (tap_sync.tms = '0') then tap_ctrl.state <= DR_SHIFT; else tap_ctrl.state <= DR_EXIT1; end if; |
when DR_EXIT1 => if (tap_sync.tms = '0') then tap_ctrl.state <= DR_PAUSE; else tap_ctrl.state <= DR_UPDATE; end if; |
when DR_PAUSE => if (tap_sync.tms = '0') then tap_ctrl.state <= DR_PAUSE; else tap_ctrl.state <= DR_EXIT2; end if; |
when DR_EXIT2 => if (tap_sync.tms = '0') then tap_ctrl.state <= DR_SHIFT; else tap_ctrl.state <= DR_UPDATE; end if; |
when DR_UPDATE => if (tap_sync.tms = '0') then tap_ctrl.state <= RUN_IDLE; else tap_ctrl.state <= DR_SCAN; end if; |
when IR_SCAN => if (tap_sync.tms = '0') then tap_ctrl.state <= IR_CAPTURE; else tap_ctrl.state <= LOGIC_RESET; end if; |
when IR_CAPTURE => if (tap_sync.tms = '0') then tap_ctrl.state <= IR_SHIFT; else tap_ctrl.state <= IR_EXIT1; end if; |
when IR_SHIFT => if (tap_sync.tms = '0') then tap_ctrl.state <= IR_SHIFT; else tap_ctrl.state <= IR_EXIT1; end if; |
when IR_EXIT1 => if (tap_sync.tms = '0') then tap_ctrl.state <= IR_PAUSE; else tap_ctrl.state <= IR_UPDATE; end if; |
when IR_PAUSE => if (tap_sync.tms = '0') then tap_ctrl.state <= IR_PAUSE; else tap_ctrl.state <= IR_EXIT2; end if; |
when IR_EXIT2 => if (tap_sync.tms = '0') then tap_ctrl.state <= IR_SHIFT; else tap_ctrl.state <= IR_UPDATE; end if; |
when IR_UPDATE => if (tap_sync.tms = '0') then tap_ctrl.state <= RUN_IDLE; else tap_ctrl.state <= DR_SCAN; end if; |
when others => tap_ctrl.state <= LOGIC_RESET; |
end case; |
end if; |
173,23 → 199,20
reg_access: process(clk_i) |
begin |
if rising_edge(clk_i) then |
if (tap_ctrl.trst_sync(1) = '0') then -- reset |
tap_reg.ireg <= "00001"; -- IDCODE |
elsif (tap_ctrl.tck_sync(2) = '0') and (tap_ctrl.tck_sync(1) = '1') then -- clock pulse (trigger on rising edge) |
-- serial data input -- |
if (tap_sync.tck_rising = '1') then -- clock pulse (evaluate TDI on rising edge of TCK) |
|
-- instruction register -- |
if (tap_ctrl.state = LOGIC_RESET) then -- reset |
if (tap_ctrl.state = LOGIC_RESET) or (tap_ctrl.state = IR_CAPTURE) then -- reset or preload phase |
tap_reg.ireg <= "00001"; -- IDCODE |
elsif (tap_ctrl.state = IR_CAPTURE) then -- preload phase |
tap_reg.ireg <= "00001"; -- IDCODE |
elsif (tap_ctrl.state = IR_SHIFT) then -- access phase |
tap_reg.ireg <= tap_ctrl.tdi_sync(1) & tap_reg.ireg(tap_reg.ireg'left downto 1); |
tap_reg.ireg <= tap_sync.tdi & tap_reg.ireg(tap_reg.ireg'left downto 1); |
end if; |
|
-- data register -- |
if (tap_ctrl.state = DR_CAPTURE) then -- preload phase |
case tap_reg.ireg is |
when "00001" => tap_reg.idcode <= IDCODE_VERSION & IDCODE_PARTID & IDCODE_MANID & '1'; -- IDCODE (LBS has to be always set!) |
when "00001" => tap_reg.idcode <= IDCODE_VERSION & IDCODE_PARTID & IDCODE_MANID & '1'; -- IDCODE (LSB has to be always set!) |
when "10000" => tap_reg.dtmcs <= tap_reg.dtmcs_nxt;-- dtmcs |
when "10001" => tap_reg.dmi <= tap_reg.dmi_nxt; -- dmi |
when others => tap_reg.bypass <= '0'; -- BYPASS |
196,10 → 219,10
end case; |
elsif (tap_ctrl.state = DR_SHIFT) then -- access phase |
case tap_reg.ireg is |
when "00001" => tap_reg.idcode <= tap_ctrl.tdi_sync(1) & tap_reg.idcode(tap_reg.idcode'left downto 1); -- IDCODE |
when "10000" => tap_reg.dtmcs <= tap_ctrl.tdi_sync(1) & tap_reg.dtmcs(tap_reg.dtmcs'left downto 1); -- dtmcs |
when "10001" => tap_reg.dmi <= tap_ctrl.tdi_sync(1) & tap_reg.dmi(tap_reg.dmi'left downto 1); -- dmi |
when others => tap_reg.bypass <= tap_ctrl.tdi_sync(1); -- BYPASS |
when "00001" => tap_reg.idcode <= tap_sync.tdi & tap_reg.idcode(tap_reg.idcode'left downto 1); -- IDCODE |
when "10000" => tap_reg.dtmcs <= tap_sync.tdi & tap_reg.dtmcs(tap_reg.dtmcs'left downto 1); -- dtmcs |
when "10001" => tap_reg.dmi <= tap_sync.tdi & tap_reg.dmi(tap_reg.dmi'left downto 1); -- dmi |
when others => tap_reg.bypass <= tap_sync.tdi; -- BYPASS |
end case; |
end if; |
end if; |
206,13 → 229,13
|
-- serial data output -- |
if (tap_ctrl.state = IR_SHIFT) then |
tap_ctrl.tdo_sync <= tap_reg.ireg(0); |
tap_sync.tdo <= tap_reg.ireg(0); |
else |
case tap_reg.ireg is |
when "00001" => tap_ctrl.tdo_sync <= tap_reg.idcode(0); -- IDCODE |
when "10000" => tap_ctrl.tdo_sync <= tap_reg.dtmcs(0); -- dtmcs |
when "10001" => tap_ctrl.tdo_sync <= tap_reg.dmi(0); -- dmi |
when others => tap_ctrl.tdo_sync <= tap_reg.bypass; -- BYPASS |
when "00001" => tap_sync.tdo <= tap_reg.idcode(0); -- IDCODE |
when "10000" => tap_sync.tdo <= tap_reg.dtmcs(0); -- dtmcs |
when "10001" => tap_sync.tdo <= tap_reg.dmi(0); -- dmi |
when others => tap_sync.tdo <= tap_reg.bypass; -- BYPASS |
end case; |
end if; |
end if; |
221,19 → 244,6
|
-- Debug Module Interface ----------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
|
-- DTM Control and Status Register (dtmcs) -- |
tap_reg.dtmcs_nxt(31 downto 18) <= (others => '0'); -- unused |
tap_reg.dtmcs_nxt(17) <= '0'; -- dmihardreset, always reads as zero |
tap_reg.dtmcs_nxt(16) <= '0'; -- dmireset, always reads as zero |
tap_reg.dtmcs_nxt(15) <= '0'; -- unused |
tap_reg.dtmcs_nxt(14 downto 12) <= dmi_idle_c; -- minimum number if idle cycles |
tap_reg.dtmcs_nxt(11 downto 10) <= tap_reg.dmi_nxt(1 downto 0); -- dmistat |
tap_reg.dtmcs_nxt(09 downto 04) <= dmi_abits_c; -- number of DMI address bits |
tap_reg.dtmcs_nxt(03 downto 00) <= dmi_version_c; -- version |
|
|
-- Debug Module Interface Access Register (dmi) -- |
dmi_controller: process(rstn_i, clk_i) |
begin |
if (rstn_i = '0') then |
263,15 → 273,16
|
when DMI_IDLE => -- waiting for new request |
if (tap_ctrl.state = DR_UPDATE) and (tap_ctrl.state_prev /= DR_UPDATE) and (tap_reg.ireg = "10001") then -- update <dmi> |
case tap_reg.dmi(1 downto 0) is -- op field |
when "01" => dmi_ctrl.state <= DMI_READ_WAIT; -- read |
when "10" => dmi_ctrl.state <= DMI_WRITE_WAIT; -- write |
when others => NULL; |
end case; |
dmi_ctrl.addr <= tap_reg.dmi(40 downto 34); |
dmi_ctrl.wdata <= tap_reg.dmi(33 downto 02); |
if (tap_reg.dmi(1 downto 0) = "01") then -- read |
dmi_ctrl.state <= DMI_READ_WAIT; |
elsif (tap_reg.dmi(1 downto 0) = "10") then -- write |
dmi_ctrl.state <= DMI_WRITE_WAIT; |
end if; |
dmi_ctrl.addr <= tap_reg.dmi(40 downto 34); |
dmi_ctrl.wdata <= tap_reg.dmi(33 downto 02); |
end if; |
|
|
when DMI_READ_WAIT => -- wait for DMI to become ready |
if (dmi_req_ready_i = '1') then |
dmi_ctrl.state <= DMI_READ; |
287,6 → 298,7
dmi_ctrl.state <= DMI_IDLE; |
end if; |
|
|
when DMI_WRITE_WAIT => -- wait for DMI to become ready |
if (dmi_req_ready_i = '1') then |
dmi_ctrl.state <= DMI_WRITE; |
301,11 → 313,12
dmi_ctrl.state <= DMI_IDLE; |
end if; |
|
|
when others => -- undefined |
dmi_ctrl.state <= DMI_IDLE; |
|
end case; |
-- override sticky error flag -- |
-- clear sticky error flag -- |
if (dmi_ctrl.dmireset = '1') then |
dmi_ctrl.err <= '0'; |
end if; |
313,6 → 326,16
end if; |
end process dmi_controller; |
|
-- DTM Control and Status Register (dtmcs) -- |
tap_reg.dtmcs_nxt(31 downto 18) <= (others => '0'); -- unused |
tap_reg.dtmcs_nxt(17) <= '0'; -- dmihardreset, always reads as zero |
tap_reg.dtmcs_nxt(16) <= '0'; -- dmireset, always reads as zero |
tap_reg.dtmcs_nxt(15) <= '0'; -- unused |
tap_reg.dtmcs_nxt(14 downto 12) <= dmi_idle_c; -- minimum number of idle cycles |
tap_reg.dtmcs_nxt(11 downto 10) <= tap_reg.dmi_nxt(1 downto 0); -- dmistat |
tap_reg.dtmcs_nxt(09 downto 04) <= dmi_abits_c; -- number of DMI address bits |
tap_reg.dtmcs_nxt(03 downto 00) <= dmi_version_c; -- version |
|
-- DMI register read access -- |
tap_reg.dmi_nxt(40 downto 34) <= dmi_ctrl.addr; -- address |
tap_reg.dmi_nxt(33 downto 02) <= dmi_ctrl.rdata; -- read data |
/rtl/core/neorv32_gptmr.vhd
176,7 → 176,7
if rising_edge(clk_i) then |
if (timer.cnt_we = '1') then -- write access |
timer.count <= data_i; |
elsif (ctrl(ctrl_en_c) = '1') then -- enabled |
elsif (ctrl(ctrl_en_c) = '1') and (gptmr_clk_en = '1') then -- enabled and clock tick |
if (timer.match = '1') then |
if (ctrl(ctrl_mode_c) = '1') then -- reset counter if continuous mode |
timer.count <= (others => '0'); |
/rtl/core/neorv32_neoled.vhd
89,7 → 89,7
signal rden : std_ulogic; -- read enable |
|
-- Control register bits -- |
constant ctrl_enable_c : natural := 0; -- r/w: module enable |
constant ctrl_en_c : natural := 0; -- r/w: module enable |
constant ctrl_mode_c : natural := 1; -- r/w: 0 = 24-bit RGB mode, 1 = 32-bit RGBW mode |
constant ctrl_strobe_c : natural := 2; -- r/w: 0 = send normal data, 1 = send LED strobe command (RESET) on data write |
-- |
153,6 → 153,14
end record; |
signal tx_buffer : tx_buffer_t; |
|
-- interrupt generator -- |
type irq_t is record |
pending : std_ulogic; -- pending interrupt request |
set : std_ulogic; |
clr : std_ulogic; |
end record; |
signal irq : irq_t; |
|
-- serial transmission engine -- |
type serial_state_t is (S_IDLE, S_INIT, S_GETBIT, S_PULSE, S_STROBE); |
type serial_t is record |
159,6 → 167,7
-- state control -- |
state : serial_state_t; |
mode : std_ulogic; |
done : std_ulogic; |
busy : std_ulogic; |
bit_cnt : std_ulogic_vector(5 downto 0); |
-- shift register -- |
199,7 → 208,7
|
-- write access: control register -- |
if (wren = '1') and (addr = neoled_ctrl_addr_c) then |
ctrl.enable <= data_i(ctrl_enable_c); |
ctrl.enable <= data_i(ctrl_en_c); |
ctrl.mode <= data_i(ctrl_mode_c); |
ctrl.strobe <= data_i(ctrl_strobe_c); |
ctrl.clk_prsc <= data_i(ctrl_clksel2_c downto ctrl_clksel0_c); |
212,7 → 221,7
-- read access: control register -- |
data_o <= (others => '0'); |
if (rden = '1') then -- and (addr = neoled_ctrl_addr_c) then |
data_o(ctrl_enable_c) <= ctrl.enable; |
data_o(ctrl_en_c) <= ctrl.enable; |
data_o(ctrl_mode_c) <= ctrl.mode; |
data_o(ctrl_strobe_c) <= ctrl.strobe; |
data_o(ctrl_clksel2_c downto ctrl_clksel0_c) <= ctrl.clk_prsc; |
241,26 → 250,42
|
-- IRQ Generator -------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
irq_select: process(ctrl, tx_buffer) |
begin |
if (FIFO_DEPTH = 1) then |
irq.set <= tx_buffer.free; -- fire IRQ if FIFO is empty |
else |
if (ctrl.irq_conf = '0') then -- fire IRQ if FIFO is less than half-full |
irq.set <= not tx_buffer.half; |
else -- fire IRQ if FIFO is empty |
irq.set <= tx_buffer.free; |
end if; |
end if; |
end process irq_select; |
|
-- Interrupt Arbiter -- |
irq_generator: process(clk_i) |
begin |
if rising_edge(clk_i) then |
if (ctrl.enable = '0') then |
irq_o <= '0'; -- no interrupt if unit is disabled |
irq.pending <= '0'; |
else |
if (FIFO_DEPTH = 1) then |
irq_o <= tx_buffer.free; -- fire IRQ if FIFO is empty |
else |
if (ctrl.irq_conf = '0') then -- fire IRQ if FIFO is less than half-full |
irq_o <= not tx_buffer.half; |
else -- fire IRQ if FIFO is empty |
irq_o <= tx_buffer.free; |
end if; |
if (irq.set = '1') and (serial.done = '1') then -- evaluate IRQ condition when transmitter is done again |
irq.pending <= '1'; |
elsif (irq.clr = '1') then |
irq.pending <= '0'; |
end if; |
end if; |
end if; |
end process irq_generator; |
|
-- IRQ request to CPU -- |
irq_o <= irq.pending; |
|
-- IRQ acknowledge -- |
irq.clr <= '1' when (wren = '1') else '0'; -- write data or control register |
|
|
-- TX Buffer (FIFO) ----------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
tx_data_fifo: neorv32_fifo |
299,6 → 324,9
-- clock generator -- |
serial.pulse_clk <= clkgen_i(to_integer(unsigned(ctrl.clk_prsc))); |
|
-- defaults -- |
serial.done <= '0'; |
|
-- FSM -- |
if (ctrl.enable = '0') then -- disabled |
serial.state <= S_IDLE; |
342,6 → 370,7
end if; |
if (serial.bit_cnt = "000000") then -- all done? |
serial.tx_out <= '0'; |
serial.done <= '1'; -- done sending data |
serial.state <= S_IDLE; |
else -- send current data MSB |
serial.tx_out <= '1'; |
378,6 → 407,7
end if; |
-- number of LOW periods reached for RESET? -- |
if (and_reduce_f(serial.strobe_cnt) = '1') then |
serial.done <= '1'; -- done sending RESET |
serial.state <= S_IDLE; |
end if; |
|
/rtl/core/neorv32_package.vhd
64,7 → 64,7
-- Architecture Constants (do not modify!) ------------------------------------------------ |
-- ------------------------------------------------------------------------------------------- |
constant data_width_c : natural := 32; -- native data path width - do not change! |
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01060301"; -- no touchy! |
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01060400"; -- no touchy! |
constant archid_c : natural := 19; -- official NEORV32 architecture ID - hands off! |
|
-- External Interface Types --------------------------------------------------------------- |
316,70 → 316,69
constant ctrl_rf_rd_adr4_c : natural := 15; -- destination register address bit 4 |
constant ctrl_rf_wb_en_c : natural := 16; -- write back enable |
-- alu -- |
constant ctrl_alu_arith_c : natural := 17; -- ALU arithmetic command |
constant ctrl_alu_logic0_c : natural := 18; -- ALU logic command bit 0 |
constant ctrl_alu_logic1_c : natural := 19; -- ALU logic command bit 1 |
constant ctrl_alu_op0_c : natural := 17; -- ALU operation select bit 0 |
constant ctrl_alu_op1_c : natural := 18; -- ALU operation select bit 1 |
constant ctrl_alu_op2_c : natural := 19; -- ALU operation select bit 2 |
constant ctrl_alu_func0_c : natural := 20; -- ALU function select command bit 0 |
constant ctrl_alu_func1_c : natural := 21; -- ALU function select command bit 1 |
constant ctrl_alu_addsub_c : natural := 22; -- 0=ADD, 1=SUB |
constant ctrl_alu_opa_mux_c : natural := 23; -- operand A select (0=rs1, 1=PC) |
constant ctrl_alu_opb_mux_c : natural := 24; -- operand B select (0=rs2, 1=IMM) |
constant ctrl_alu_unsigned_c : natural := 25; -- is unsigned ALU operation |
constant ctrl_alu_shift_dir_c : natural := 26; -- shift direction (0=left, 1=right) |
constant ctrl_alu_shift_ar_c : natural := 27; -- is arithmetic shift |
constant ctrl_alu_frm0_c : natural := 28; -- FPU rounding mode bit 0 |
constant ctrl_alu_frm1_c : natural := 29; -- FPU rounding mode bit 1 |
constant ctrl_alu_frm2_c : natural := 30; -- FPU rounding mode bit 2 |
constant ctrl_alu_opa_mux_c : natural := 22; -- operand A select (0=rs1, 1=PC) |
constant ctrl_alu_opb_mux_c : natural := 23; -- operand B select (0=rs2, 1=IMM) |
constant ctrl_alu_unsigned_c : natural := 24; -- is unsigned ALU operation |
constant ctrl_alu_shift_dir_c : natural := 25; -- shift direction (0=left, 1=right) |
constant ctrl_alu_shift_ar_c : natural := 26; -- is arithmetic shift |
constant ctrl_alu_frm0_c : natural := 27; -- FPU rounding mode bit 0 |
constant ctrl_alu_frm1_c : natural := 28; -- FPU rounding mode bit 1 |
constant ctrl_alu_frm2_c : natural := 29; -- FPU rounding mode bit 2 |
-- bus interface -- |
constant ctrl_bus_size_lsb_c : natural := 31; -- transfer size lsb (00=byte, 01=half-word) |
constant ctrl_bus_size_msb_c : natural := 32; -- transfer size msb (10=word, 11=?) |
constant ctrl_bus_rd_c : natural := 33; -- read data request |
constant ctrl_bus_wr_c : natural := 34; -- write data request |
constant ctrl_bus_if_c : natural := 35; -- instruction fetch request |
constant ctrl_bus_mo_we_c : natural := 36; -- memory address and data output register write enable |
constant ctrl_bus_mi_we_c : natural := 37; -- memory data input register write enable |
constant ctrl_bus_unsigned_c : natural := 38; -- is unsigned load |
constant ctrl_bus_ierr_ack_c : natural := 39; -- acknowledge instruction fetch bus exceptions |
constant ctrl_bus_derr_ack_c : natural := 40; -- acknowledge data access bus exceptions |
constant ctrl_bus_fence_c : natural := 41; -- executed fence operation |
constant ctrl_bus_fencei_c : natural := 42; -- executed fencei operation |
constant ctrl_bus_lock_c : natural := 43; -- make atomic/exclusive access lock |
constant ctrl_bus_de_lock_c : natural := 44; -- remove atomic/exclusive access |
constant ctrl_bus_ch_lock_c : natural := 45; -- evaluate atomic/exclusive lock (SC operation) |
constant ctrl_bus_size_lsb_c : natural := 30; -- transfer size lsb (00=byte, 01=half-word) |
constant ctrl_bus_size_msb_c : natural := 31; -- transfer size msb (10=word, 11=?) |
constant ctrl_bus_rd_c : natural := 32; -- read data request |
constant ctrl_bus_wr_c : natural := 33; -- write data request |
constant ctrl_bus_if_c : natural := 34; -- instruction fetch request |
constant ctrl_bus_mo_we_c : natural := 35; -- memory address and data output register write enable |
constant ctrl_bus_mi_we_c : natural := 36; -- memory data input register write enable |
constant ctrl_bus_unsigned_c : natural := 37; -- is unsigned load |
constant ctrl_bus_ierr_ack_c : natural := 38; -- acknowledge instruction fetch bus exceptions |
constant ctrl_bus_derr_ack_c : natural := 39; -- acknowledge data access bus exceptions |
constant ctrl_bus_fence_c : natural := 40; -- executed fence operation |
constant ctrl_bus_fencei_c : natural := 41; -- executed fencei operation |
constant ctrl_bus_lock_c : natural := 42; -- make atomic/exclusive access lock |
constant ctrl_bus_de_lock_c : natural := 43; -- remove atomic/exclusive access |
constant ctrl_bus_ch_lock_c : natural := 44; -- evaluate atomic/exclusive lock (SC operation) |
-- co-processors -- |
constant ctrl_cp_id_lsb_c : natural := 46; -- cp select ID lsb |
constant ctrl_cp_id_msb_c : natural := 47; -- cp select ID msb |
constant ctrl_cp_id_lsb_c : natural := 45; -- cp select ID lsb |
constant ctrl_cp_id_msb_c : natural := 46; -- cp select ID msb |
-- instruction's control blocks (used by cpu co-processors) -- |
constant ctrl_ir_funct3_0_c : natural := 48; -- funct3 bit 0 |
constant ctrl_ir_funct3_1_c : natural := 49; -- funct3 bit 1 |
constant ctrl_ir_funct3_2_c : natural := 50; -- funct3 bit 2 |
constant ctrl_ir_funct12_0_c : natural := 51; -- funct12 bit 0 |
constant ctrl_ir_funct12_1_c : natural := 52; -- funct12 bit 1 |
constant ctrl_ir_funct12_2_c : natural := 53; -- funct12 bit 2 |
constant ctrl_ir_funct12_3_c : natural := 54; -- funct12 bit 3 |
constant ctrl_ir_funct12_4_c : natural := 55; -- funct12 bit 4 |
constant ctrl_ir_funct12_5_c : natural := 56; -- funct12 bit 5 |
constant ctrl_ir_funct12_6_c : natural := 57; -- funct12 bit 6 |
constant ctrl_ir_funct12_7_c : natural := 58; -- funct12 bit 7 |
constant ctrl_ir_funct12_8_c : natural := 59; -- funct12 bit 8 |
constant ctrl_ir_funct12_9_c : natural := 60; -- funct12 bit 9 |
constant ctrl_ir_funct12_10_c : natural := 61; -- funct12 bit 10 |
constant ctrl_ir_funct12_11_c : natural := 62; -- funct12 bit 11 |
constant ctrl_ir_opcode7_0_c : natural := 63; -- opcode7 bit 0 |
constant ctrl_ir_opcode7_1_c : natural := 64; -- opcode7 bit 1 |
constant ctrl_ir_opcode7_2_c : natural := 65; -- opcode7 bit 2 |
constant ctrl_ir_opcode7_3_c : natural := 66; -- opcode7 bit 3 |
constant ctrl_ir_opcode7_4_c : natural := 67; -- opcode7 bit 4 |
constant ctrl_ir_opcode7_5_c : natural := 68; -- opcode7 bit 5 |
constant ctrl_ir_opcode7_6_c : natural := 69; -- opcode7 bit 6 |
constant ctrl_ir_funct3_0_c : natural := 47; -- funct3 bit 0 |
constant ctrl_ir_funct3_1_c : natural := 48; -- funct3 bit 1 |
constant ctrl_ir_funct3_2_c : natural := 49; -- funct3 bit 2 |
constant ctrl_ir_funct12_0_c : natural := 50; -- funct12 bit 0 |
constant ctrl_ir_funct12_1_c : natural := 51; -- funct12 bit 1 |
constant ctrl_ir_funct12_2_c : natural := 52; -- funct12 bit 2 |
constant ctrl_ir_funct12_3_c : natural := 53; -- funct12 bit 3 |
constant ctrl_ir_funct12_4_c : natural := 54; -- funct12 bit 4 |
constant ctrl_ir_funct12_5_c : natural := 55; -- funct12 bit 5 |
constant ctrl_ir_funct12_6_c : natural := 56; -- funct12 bit 6 |
constant ctrl_ir_funct12_7_c : natural := 57; -- funct12 bit 7 |
constant ctrl_ir_funct12_8_c : natural := 58; -- funct12 bit 8 |
constant ctrl_ir_funct12_9_c : natural := 59; -- funct12 bit 9 |
constant ctrl_ir_funct12_10_c : natural := 60; -- funct12 bit 10 |
constant ctrl_ir_funct12_11_c : natural := 61; -- funct12 bit 11 |
constant ctrl_ir_opcode7_0_c : natural := 62; -- opcode7 bit 0 |
constant ctrl_ir_opcode7_1_c : natural := 63; -- opcode7 bit 1 |
constant ctrl_ir_opcode7_2_c : natural := 64; -- opcode7 bit 2 |
constant ctrl_ir_opcode7_3_c : natural := 65; -- opcode7 bit 3 |
constant ctrl_ir_opcode7_4_c : natural := 66; -- opcode7 bit 4 |
constant ctrl_ir_opcode7_5_c : natural := 67; -- opcode7 bit 5 |
constant ctrl_ir_opcode7_6_c : natural := 68; -- opcode7 bit 6 |
-- CPU status -- |
constant ctrl_priv_lvl_lsb_c : natural := 70; -- privilege level lsb |
constant ctrl_priv_lvl_msb_c : natural := 71; -- privilege level msb |
constant ctrl_sleep_c : natural := 72; -- set when CPU is in sleep mode |
constant ctrl_trap_c : natural := 73; -- set when CPU is entering trap execution |
constant ctrl_debug_running_c : natural := 74; -- CPU is in debug mode when set |
constant ctrl_priv_lvl_lsb_c : natural := 69; -- privilege level lsb |
constant ctrl_priv_lvl_msb_c : natural := 70; -- privilege level msb |
constant ctrl_sleep_c : natural := 71; -- set when CPU is in sleep mode |
constant ctrl_trap_c : natural := 72; -- set when CPU is entering trap execution |
constant ctrl_debug_running_c : natural := 73; -- CPU is in debug mode when set |
-- control bus size -- |
constant ctrl_width_c : natural := 75; -- control bus size |
constant ctrl_width_c : natural := 74; -- control bus size |
|
-- Comparator Bus ------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
526,7 → 525,7
-- ------------------------------------------------------------------------------------------- |
-- <<< standard read/write CSRs >>> -- |
-- user floating-point CSRs -- |
constant csr_class_float_c : std_ulogic_vector(07 downto 0) := x"00"; -- floating point |
constant csr_class_float_c : std_ulogic_vector(09 downto 0) := x"00" & "00"; -- floating point |
constant csr_fflags_c : std_ulogic_vector(11 downto 0) := x"001"; |
constant csr_frm_c : std_ulogic_vector(11 downto 0) := x"002"; |
constant csr_fcsr_c : std_ulogic_vector(11 downto 0) := x"003"; |
576,7 → 575,7
constant csr_mhpmevent30_c : std_ulogic_vector(11 downto 0) := x"33e"; |
constant csr_mhpmevent31_c : std_ulogic_vector(11 downto 0) := x"33f"; |
-- machine trap handling -- |
constant csr_class_trap_c : std_ulogic_vector(07 downto 0) := x"34"; -- machine trap handling |
constant csr_class_trap_c : std_ulogic_vector(08 downto 0) := x"34" & '0'; -- machine trap handling |
constant csr_mscratch_c : std_ulogic_vector(11 downto 0) := x"340"; |
constant csr_mepc_c : std_ulogic_vector(11 downto 0) := x"341"; |
constant csr_mcause_c : std_ulogic_vector(11 downto 0) := x"342"; |
761,19 → 760,20
|
-- ALU Function Codes --------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
-- arithmetic core -- |
constant alu_arith_cmd_addsub_c : std_ulogic := '0'; -- r.arith <= A +/- B |
constant alu_arith_cmd_slt_c : std_ulogic := '1'; -- r.arith <= A < B |
-- logic core -- |
constant alu_logic_cmd_movb_c : std_ulogic_vector(1 downto 0) := "00"; -- r.logic <= B |
constant alu_logic_cmd_xor_c : std_ulogic_vector(1 downto 0) := "01"; -- r.logic <= A xor B |
constant alu_logic_cmd_or_c : std_ulogic_vector(1 downto 0) := "10"; -- r.logic <= A or B |
constant alu_logic_cmd_and_c : std_ulogic_vector(1 downto 0) := "11"; -- r.logic <= A and B |
-- function select (actual alu result) -- |
constant alu_func_cmd_arith_c : std_ulogic_vector(1 downto 0) := "00"; -- r <= r.arith |
constant alu_func_cmd_logic_c : std_ulogic_vector(1 downto 0) := "01"; -- r <= r.logic |
constant alu_func_cmd_csrr_c : std_ulogic_vector(1 downto 0) := "10"; -- r <= CSR read |
constant alu_func_cmd_copro_c : std_ulogic_vector(1 downto 0) := "11"; -- r <= CP result (multi-cycle) |
-- ALU core [DO NOT CHANGE ENCODING!] -- |
constant alu_op_add_c : std_ulogic_vector(2 downto 0) := "000"; -- alu_result <= A + B |
constant alu_op_sub_c : std_ulogic_vector(2 downto 0) := "001"; -- alu_result <= A - B |
--constant alu_op_mova_c : std_ulogic_vector(2 downto 0) := "010"; -- alu_result <= A (rs1) |
constant alu_op_slt_c : std_ulogic_vector(2 downto 0) := "011"; -- alu_result <= A < B |
constant alu_op_movb_c : std_ulogic_vector(2 downto 0) := "100"; -- alu_result <= B |
constant alu_op_xor_c : std_ulogic_vector(2 downto 0) := "101"; -- alu_result <= A xor B |
constant alu_op_or_c : std_ulogic_vector(2 downto 0) := "110"; -- alu_result <= A or B |
constant alu_op_and_c : std_ulogic_vector(2 downto 0) := "111"; -- alu_result <= A and B |
-- function select (actual ALU result) -- |
constant alu_func_core_c : std_ulogic_vector(1 downto 0) := "00"; -- r <= alu_result |
constant alu_func_nxpc_c : std_ulogic_vector(1 downto 0) := "01"; -- r <= next_PC |
constant alu_func_csrr_c : std_ulogic_vector(1 downto 0) := "10"; -- r <= CSR read |
constant alu_func_copro_c : std_ulogic_vector(1 downto 0) := "11"; -- r <= CP result (multi-cycle) |
|
-- Trap ID Codes -------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
1177,6 → 1177,7
imm_o : out std_ulogic_vector(data_width_c-1 downto 0); -- immediate |
fetch_pc_o : out std_ulogic_vector(data_width_c-1 downto 0); -- PC for instruction fetch |
curr_pc_o : out std_ulogic_vector(data_width_c-1 downto 0); -- current PC (corresponding to current instruction) |
next_pc_o : out std_ulogic_vector(data_width_c-1 downto 0); -- next PC (corresponding to next instruction) |
csr_rdata_o : out std_ulogic_vector(data_width_c-1 downto 0); -- CSR read data |
-- FPU interface -- |
fpu_flags_i : in std_ulogic_vector(04 downto 0); -- exception flags |
1244,7 → 1245,8
-- data input -- |
rs1_i : in std_ulogic_vector(data_width_c-1 downto 0); -- rf source 1 |
rs2_i : in std_ulogic_vector(data_width_c-1 downto 0); -- rf source 2 |
pc2_i : in std_ulogic_vector(data_width_c-1 downto 0); -- delayed PC |
pc_i : in std_ulogic_vector(data_width_c-1 downto 0); -- current PC |
pc2_i : in std_ulogic_vector(data_width_c-1 downto 0); -- next PC |
imm_i : in std_ulogic_vector(data_width_c-1 downto 0); -- immediate |
csr_i : in std_ulogic_vector(data_width_c-1 downto 0); -- CSR read data |
-- data output -- |
1408,16 → 1410,6
-- Component: Bus Keeper ------------------------------------------------------------------ |
-- ------------------------------------------------------------------------------------------- |
component neorv32_bus_keeper is |
generic ( |
-- External memory interface -- |
MEM_EXT_EN : boolean; -- implement external memory bus interface? |
-- Internal instruction memory -- |
MEM_INT_IMEM_EN : boolean; -- implement processor-internal instruction memory |
MEM_INT_IMEM_SIZE : natural; -- size of processor-internal instruction memory in bytes |
-- Internal data memory -- |
MEM_INT_DMEM_EN : boolean; -- implement processor-internal data memory |
MEM_INT_DMEM_SIZE : natural -- size of processor-internal data memory in bytes |
); |
port ( |
-- host access -- |
clk_i : in std_ulogic; -- global clock line |
1433,7 → 1425,9
bus_rden_i : in std_ulogic; -- read enable |
bus_wren_i : in std_ulogic; -- write enable |
bus_ack_i : in std_ulogic; -- transfer acknowledge from bus system |
bus_err_i : in std_ulogic -- transfer error from bus system |
bus_err_i : in std_ulogic; -- transfer error from bus system |
bus_tmo_i : in std_ulogic; -- transfer timeout (external interface) |
bus_ext_i : in std_ulogic -- external bus access |
); |
end component; |
|
1790,7 → 1784,9
lock_i : in std_ulogic; -- exclusive access request |
ack_o : out std_ulogic; -- transfer acknowledge |
err_o : out std_ulogic; -- transfer error |
tmo_o : out std_ulogic; -- transfer timeout |
priv_i : in std_ulogic_vector(01 downto 0); -- current CPU privilege level |
ext_o : out std_ulogic; -- active external access |
-- wishbone interface -- |
wb_tag_o : out std_ulogic_vector(02 downto 0); -- request tag |
wb_adr_o : out std_ulogic_vector(31 downto 0); -- address |
1824,6 → 1820,7
data_i : in std_ulogic_vector(31 downto 0); -- data in |
data_o : out std_ulogic_vector(31 downto 0); -- data out |
ack_o : out std_ulogic; -- transfer acknowledge |
err_o : out std_ulogic; -- transfer error |
-- clock generator -- |
clkgen_en_o : out std_ulogic; -- enable clock generator |
clkgen_i : in std_ulogic_vector(07 downto 0); -- "clock" inputs |
/rtl/core/neorv32_slink.vhd
126,6 → 126,8
signal ack_write : std_ulogic; |
signal acc_en : std_ulogic; |
signal addr : std_ulogic_vector(31 downto 0); |
signal wren : std_ulogic; -- word write enable |
signal rden : std_ulogic; -- read enable |
|
-- control register -- |
signal enable : std_ulogic; -- global enable |
148,6 → 150,17
signal rx_fifo_half : std_ulogic_vector(7 downto 0); |
signal tx_fifo_half : std_ulogic_vector(7 downto 0); |
|
-- interrupt generator -- |
type detect_t is array (0 to 7) of std_ulogic_vector(1 downto 0); |
type irq_t is record |
pending : std_ulogic; -- pending interrupt request |
detect : detect_t; -- rising-edge detector |
trigger : std_ulogic_vector(7 downto 0); |
set : std_ulogic_vector(7 downto 0); |
clr : std_ulogic; |
end record; |
signal rx_irq, tx_irq : irq_t; |
|
begin |
|
-- Sanity Checks -------------------------------------------------------------------------- |
169,6 → 182,8
-- ------------------------------------------------------------------------------------------- |
acc_en <= '1' when (addr_i(hi_abb_c downto lo_abb_c) = slink_base_c(hi_abb_c downto lo_abb_c)) else '0'; |
addr <= slink_base_c(31 downto lo_abb_c) & addr_i(lo_abb_c-1 downto 2) & "00"; -- word aligned |
wren <= acc_en and wren_i; |
rden <= acc_en and rden_i; |
|
|
-- Read/Write Access ---------------------------------------------------------------------- |
178,7 → 193,7
if rising_edge(clk_i) then |
-- write access -- |
ack_write <= '0'; |
if (acc_en = '1') and (wren_i = '1') then |
if (wren = '1') then |
if (addr(5) = '0') then -- control/status/irq |
if (addr(4 downto 3) = "00") then -- control register |
enable <= data_i(ctrl_en_c); |
202,7 → 217,7
-- read access -- |
data_o <= (others => '0'); |
ack_read <= '0'; |
if (acc_en = '1') and (rden_i = '1') then |
if (rden = '1') then |
if (addr(5) = '0') then -- control/status registers |
ack_read <= '1'; |
case addr(4 downto 3) is |
246,50 → 261,110
|
-- Interrupt Generator -------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
irq_arbiter: process(clk_i) |
variable rx_tmp_v : std_ulogic_vector(SLINK_NUM_RX-1 downto 0); |
variable tx_tmp_v : std_ulogic_vector(SLINK_NUM_TX-1 downto 0); |
irq_type: process(irq_rx_mode, rx_fifo_avail, rx_fifo_half, |
irq_tx_mode, tx_fifo_free, tx_fifo_half) |
begin |
if rising_edge(clk_i) then |
if (enable = '0') then -- no interrupts if unit is disabled |
irq_rx_o <= '0'; |
irq_tx_o <= '0'; |
-- RX interrupt -- |
rx_irq.trigger <= (others => '0'); |
for i in 0 to SLINK_NUM_RX-1 loop |
if (SLINK_RX_FIFO = 1) then |
rx_irq.trigger(i) <= rx_fifo_avail(i); -- fire if any RX_FIFO is not empty |
else |
|
-- RX interrupt -- |
if (SLINK_RX_FIFO = 1) then |
irq_rx_o <= or_reduce_f(irq_rx_en and rx_fifo_avail); -- fire if any RX_FIFO is not empty |
else |
rx_tmp_v := (others => '0'); |
for i in 0 to SLINK_NUM_RX-1 loop |
if (irq_rx_mode(i) = '0') then -- fire if any RX_FIFO is at least half-full |
rx_tmp_v(i) := rx_fifo_half(i); |
else -- fire if any RX_FIFO is not empty (= data available) |
rx_tmp_v(i) := rx_fifo_avail(i); |
end if; |
end loop; |
irq_rx_o <= or_reduce_f(irq_rx_en and rx_tmp_v); |
if (irq_rx_mode(i) = '0') then -- fire if any RX_FIFO is at least half-full |
rx_irq.trigger(i) <= rx_fifo_half(i); |
else -- fire if any RX_FIFO is not empty (= data available) |
rx_irq.trigger(i) <= rx_fifo_avail(i); |
end if; |
end if; |
end loop; |
-- TX interrupt -- |
tx_irq.trigger <= (others => '0'); |
for i in 0 to SLINK_NUM_TX-1 loop |
if (SLINK_TX_FIFO = 1) then |
tx_irq.trigger(i) <= tx_fifo_free(i); -- fire if any TX_FIFO is not full |
else |
if (irq_tx_mode(i) = '0') then -- fire if any TX_FIFO is less than half-full |
tx_irq.trigger(i) <= not tx_fifo_half(i); |
else -- fire if any TX_FIFO is not full (= free buffer space available) |
tx_irq.trigger(i) <= tx_fifo_free(i); |
end if; |
end if; |
end loop; |
end process irq_type; |
|
-- TX interrupt -- |
if (SLINK_TX_FIFO = 1) then |
irq_tx_o <= or_reduce_f(irq_tx_en and tx_fifo_free); -- fire if any TX_FIFO is not full |
else |
tx_tmp_v := (others => '0'); |
for i in 0 to SLINK_NUM_TX-1 loop |
if (irq_tx_mode(i) = '0') then -- fire if any RX_FIFO is less than half-full |
tx_tmp_v(i) := not rx_fifo_half(i); |
else -- fire if any RX_FIFO is not full (= free buffer space available) |
tx_tmp_v(i) := tx_fifo_free(i); |
end if; |
end loop; |
irq_tx_o <= or_reduce_f(irq_tx_en and tx_tmp_v); |
-- interrupt trigger -- |
irq_trigger_sync: process(clk_i) |
begin |
if rising_edge(clk_i) then |
-- RX -- |
rx_irq.detect <= (others => (others => '0')); -- default |
if (enable = '1') then |
for i in 0 to SLINK_NUM_RX-1 loop |
rx_irq.detect(i) <= rx_irq.detect(i)(0) & rx_irq.trigger(i); |
end loop; |
end if; |
-- TX -- |
tx_irq.detect <= (others => (others => '0')); -- default |
if (enable = '1') then |
for i in 0 to SLINK_NUM_TX-1 loop |
tx_irq.detect(i) <= tx_irq.detect(i)(0) & tx_irq.trigger(i); |
end loop; |
end if; |
end if; |
end process irq_trigger_sync; |
|
-- interrupt trigger -- |
irq_trigger_comb: process(rx_irq, irq_rx_en, tx_irq, irq_tx_en) |
begin |
-- RX -- |
rx_irq.set <= (others => '0'); |
for i in 0 to SLINK_NUM_RX-1 loop |
if (rx_irq.detect(i) = "01") and (irq_rx_en(i) = '1') then -- rising-edge |
rx_irq.set(i) <= '1'; |
end if; |
end loop; |
-- TX -- |
tx_irq.set <= (others => '0'); |
for i in 0 to SLINK_NUM_TX-1 loop |
if (tx_irq.detect(i) = "01") and (irq_tx_en(i) = '1') then -- rising-edge |
tx_irq.set(i) <= '1'; |
end if; |
end loop; |
end process irq_trigger_comb; |
|
-- interrupt arbiter -- |
irq_generator: process(clk_i) |
begin |
if rising_edge(clk_i) then |
if (enable = '0') then |
rx_irq.pending <= '0'; |
tx_irq.pending <= '0'; |
else |
-- RX -- |
if (or_reduce_f(rx_irq.set) = '1') then |
rx_irq.pending <= '1'; |
elsif (rx_irq.clr = '1') then |
rx_irq.pending <= '0'; |
end if; |
-- TX -- |
if (or_reduce_f(tx_irq.set) = '1') then |
tx_irq.pending <= '1'; |
elsif (tx_irq.clr = '1') then |
tx_irq.pending <= '0'; |
end if; |
end if; |
end if; |
end process irq_arbiter; |
end process irq_generator; |
|
-- IRQ requests to CPU -- |
irq_rx_o <= rx_irq.pending; |
irq_tx_o <= tx_irq.pending; |
|
-- IRQ acknowledge -- |
rx_irq.clr <= '1' when ((rden = '1') and (addr(5) = '1')) or ((wren = '1') and (addr(5 downto 3) = "000")) else '0'; -- read from data FIFO OR write to control register |
tx_irq.clr <= '1' when ((wren = '1') and (addr(5) = '1')) or ((wren = '1') and (addr(5 downto 3) = "000")) else '0'; -- write to data FIFO OR write to control register |
|
|
-- Link Select ---------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
link_select: process(addr) |
309,8 → 384,8
|
fifo_access_gen: |
for i in 0 to 7 generate |
tx_fifo_we(i) <= link_sel(i) and acc_en and wren_i; |
rx_fifo_re(i) <= link_sel(i) and acc_en and rden_i; |
tx_fifo_we(i) <= link_sel(i) and wren; |
rx_fifo_re(i) <= link_sel(i) and rden; |
end generate; |
|
|
/rtl/core/neorv32_spi.vhd
3,7 → 3,7
-- # ********************************************************************************************* # |
-- # Frame format: 8/16/24/32-bit receive/transmit data, always MSB first, 2 clock modes, # |
-- # 8 pre-scaled clocks (derived from system clock), 8 dedicated chip-select lines (low-active). # |
-- # Interrupt: SPI_transfer_done # |
-- # Interrupt: "transfer done" # |
-- # ********************************************************************************************* # |
-- # BSD 3-Clause License # |
-- # # |
116,6 → 116,14
end record; |
signal rtx_engine : rtx_engine_t; |
|
-- interrupt generator -- |
type irq_t is record |
pending : std_ulogic; -- pending interrupt request |
set : std_ulogic; |
clr : std_ulogic; |
end record; |
signal irq : irq_t; |
|
begin |
|
-- Access Control ------------------------------------------------------------------------- |
231,6 → 239,7
|
-- defaults -- |
spi_sck_o <= ctrl(ctrl_cpol_c); |
irq.set <= '0'; |
|
-- serial engine -- |
rtx_engine.state(2) <= ctrl(ctrl_en_c); |
264,7 → 273,8
if (spi_clk_en = '1') then |
rtx_engine.sreg <= rtx_engine.sreg(30 downto 0) & rtx_engine.sdi_sync(rtx_engine.sdi_sync'left); |
if (rtx_engine.bitcnt(5 downto 3) = rtx_engine.bytecnt) then -- all bits transferred? |
rtx_engine.state(1 downto 0) <= "00"; |
irq.set <= '1'; -- interrupt! |
rtx_engine.state(1 downto 0) <= "00"; -- transmission done |
else |
rtx_engine.state(1 downto 0) <= "10"; |
end if; |
282,9 → 292,28
rtx_engine.busy <= '0' when (rtx_engine.state(1 downto 0) = "00") else '1'; |
|
|
-- Interrupt ------------------------------------------------------------------------------ |
-- Interrupt Generator -------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
irq_o <= '1' when (rtx_engine.state = "100") else '0'; -- fire IRQ if transceiver idle and enabled |
irq_generator: process(clk_i) |
begin |
if rising_edge(clk_i) then |
if (ctrl(ctrl_en_c) = '0') then |
irq.pending <= '0'; |
else |
if (irq.set = '1') then |
irq.pending <= '1'; |
elsif (irq.clr = '1') then |
irq.pending <= '0'; |
end if; |
end if; |
end if; |
end process irq_generator; |
|
-- IRQ request to CPU -- |
irq_o <= irq.pending; |
|
-- IRQ acknowledge -- |
irq.clr <= '1' when ((rden = '1') and (addr = spi_rtx_addr_c)) or (wren = '1') else '0'; -- read data register OR write data/control register |
|
|
end neorv32_spi_rtl; |
/rtl/core/neorv32_sysinfo.vhd
158,8 → 158,8
-- Memory -- |
sysinfo_mem(2)(00) <= bool_to_ulogic_f(INT_BOOTLOADER_EN); -- processor-internal bootloader implemented? |
sysinfo_mem(2)(01) <= bool_to_ulogic_f(MEM_EXT_EN); -- external memory bus interface implemented? |
sysinfo_mem(2)(02) <= bool_to_ulogic_f(MEM_INT_IMEM_EN); -- processor-internal instruction memory implemented? |
sysinfo_mem(2)(03) <= bool_to_ulogic_f(MEM_INT_DMEM_EN); -- processor-internal data memory implemented? |
sysinfo_mem(2)(02) <= bool_to_ulogic_f(MEM_INT_IMEM_EN) and bool_to_ulogic_f(boolean(MEM_INT_IMEM_SIZE > 0)); -- processor-internal instruction memory implemented? |
sysinfo_mem(2)(03) <= bool_to_ulogic_f(MEM_INT_DMEM_EN) and bool_to_ulogic_f(boolean(MEM_INT_DMEM_SIZE > 0)); -- processor-internal data memory implemented? |
sysinfo_mem(2)(04) <= bool_to_ulogic_f(MEM_EXT_BIG_ENDIAN); -- is external memory bus interface using BIG-endian byte-order? |
sysinfo_mem(2)(05) <= bool_to_ulogic_f(ICACHE_EN); -- processor-internal instruction cache implemented? |
-- |
/rtl/core/neorv32_top.vhd
276,6 → 276,9
end record; |
signal cpu_i, i_cache, cpu_d, p_bus : bus_interface_t; |
|
-- bus access error (from BUSKEEPER) -- |
signal bus_error : std_ulogic; |
|
-- debug core interface (DCI) -- |
signal dci_ndmrstn : std_ulogic; |
signal dci_halt_req : std_ulogic; |
334,7 → 337,9
signal gptmr_irq : std_ulogic; |
|
-- misc -- |
signal mtime_time : std_ulogic_vector(63 downto 0); -- current system time from MTIME |
signal mtime_time : std_ulogic_vector(63 downto 0); -- current system time from MTIME |
signal ext_timeout : std_ulogic; |
signal ext_access : std_ulogic; |
|
begin |
|
531,19 → 536,20
fence_o <= cpu_d.fence; -- indicates an executed FENCE operation |
fencei_o <= cpu_i.fence; -- indicates an executed FENCEI operation |
|
-- fast interrupts -- |
fast_irq(00) <= wdt_irq; -- HIGHEST PRIORITY - watchdog timeout |
-- fast interrupt requests (FIRQs) -- |
-- these stay asserted until explicitly acknowledged -- |
fast_irq(00) <= wdt_irq; -- HIGHEST PRIORITY - watchdog |
fast_irq(01) <= cfs_irq; -- custom functions subsystem |
fast_irq(02) <= uart0_rxd_irq; -- primary UART (UART0) RX interrupt |
fast_irq(03) <= uart0_txd_irq; -- primary UART (UART0) TX interrupt |
fast_irq(04) <= uart1_rxd_irq; -- secondary UART (UART1) RX interrupt |
fast_irq(05) <= uart1_txd_irq; -- secondary UART (UART1) TX interrupt |
fast_irq(06) <= spi_irq; -- SPI idle |
fast_irq(07) <= twi_irq; -- TWI idle |
fast_irq(02) <= uart0_rxd_irq; -- primary UART (UART0) RX |
fast_irq(03) <= uart0_txd_irq; -- primary UART (UART0) TX |
fast_irq(04) <= uart1_rxd_irq; -- secondary UART (UART1) RX |
fast_irq(05) <= uart1_txd_irq; -- secondary UART (UART1) TX |
fast_irq(06) <= spi_irq; -- SPI |
fast_irq(07) <= twi_irq; -- TWI |
fast_irq(08) <= xirq_irq; -- external interrupt controller |
fast_irq(09) <= neoled_irq; -- NEOLED buffer free |
fast_irq(10) <= slink_rx_irq; -- SLINK RX interrupt |
fast_irq(11) <= slink_tx_irq; -- SLINK TX interrupt |
fast_irq(10) <= slink_rx_irq; -- SLINK RX |
fast_irq(11) <= slink_tx_irq; -- SLINK TX |
fast_irq(12) <= gptmr_irq; -- general purpose timer |
-- |
fast_irq(13) <= '0'; -- reserved |
644,7 → 650,7
p_bus_re_o => p_bus.re, -- read enable |
p_bus_lock_o => p_bus.lock, -- exclusive access request |
p_bus_ack_i => p_bus.ack, -- bus transfer acknowledge |
p_bus_err_i => p_bus.err -- bus transfer error |
p_bus_err_i => bus_error -- bus transfer error |
); |
|
-- current CPU privilege level -- |
676,16 → 682,6
-- Bus Keeper (BUSKEEPER) ----------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
neorv32_bus_keeper_inst: neorv32_bus_keeper |
generic map ( |
-- External memory interface -- |
MEM_EXT_EN => MEM_EXT_EN, -- implement external memory bus interface? |
-- 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 |
-- Internal data memory -- |
MEM_INT_DMEM_EN => MEM_INT_DMEM_EN, -- implement processor-internal data memory |
MEM_INT_DMEM_SIZE => MEM_INT_DMEM_SIZE -- size of processor-internal data memory in bytes |
) |
port map ( |
-- host access -- |
clk_i => clk_i, -- global clock line |
695,20 → 691,25
wren_i => io_wren, -- byte write enable |
data_o => resp_bus(RESP_BUSKEEPER).rdata, -- data out |
ack_o => resp_bus(RESP_BUSKEEPER).ack, -- transfer acknowledge |
err_o => resp_bus(RESP_BUSKEEPER).err, -- transfer error |
err_o => bus_error, -- transfer error |
-- bus monitoring -- |
bus_addr_i => p_bus.addr, -- address |
bus_rden_i => p_bus.re, -- read enable |
bus_wren_i => p_bus.we, -- write enable |
bus_ack_i => p_bus.ack, -- transfer acknowledge from bus system |
bus_err_i => p_bus.err -- transfer error from bus system |
bus_err_i => p_bus.err, -- transfer error from bus system |
bus_tmo_i => ext_timeout, -- transfer timeout (external interface) |
bus_ext_i => ext_access -- external bus access |
); |
|
-- unused, BUSKEEPER **directly** issues error to the CPU -- |
resp_bus(RESP_BUSKEEPER).err <= '0'; |
|
|
-- Processor-Internal Instruction Memory (IMEM) ------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
neorv32_int_imem_inst_true: |
if (MEM_INT_IMEM_EN = true) generate |
if (MEM_INT_IMEM_EN = true) and (MEM_INT_IMEM_SIZE > 0) generate |
neorv32_int_imem_inst: neorv32_imem |
generic map ( |
IMEM_BASE => imem_base_c, -- memory base address |
729,7 → 730,7
end generate; |
|
neorv32_int_imem_inst_false: |
if (MEM_INT_IMEM_EN = false) generate |
if (MEM_INT_IMEM_EN = false) or (MEM_INT_IMEM_SIZE = 0) generate |
resp_bus(RESP_IMEM) <= resp_bus_entry_terminate_c; |
end generate; |
|
737,7 → 738,7
-- Processor-Internal Data Memory (DMEM) -------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
neorv32_int_dmem_inst_true: |
if (MEM_INT_DMEM_EN = true) generate |
if (MEM_INT_DMEM_EN = true) and (MEM_INT_DMEM_SIZE > 0) generate |
neorv32_int_dmem_inst: neorv32_dmem |
generic map ( |
DMEM_BASE => dmem_base_c, -- memory base address |
757,7 → 758,7
end generate; |
|
neorv32_int_dmem_inst_false: |
if (MEM_INT_DMEM_EN = false) generate |
if (MEM_INT_DMEM_EN = false) or (MEM_INT_DMEM_SIZE = 0) generate |
resp_bus(RESP_DMEM) <= resp_bus_entry_terminate_c; |
end generate; |
|
819,7 → 820,9
lock_i => p_bus.lock, -- exclusive access request |
ack_o => resp_bus(RESP_WISHBONE).ack, -- transfer acknowledge |
err_o => resp_bus(RESP_WISHBONE).err, -- transfer error |
tmo_o => ext_timeout, -- transfer timeout |
priv_i => p_bus.priv, -- current CPU privilege level |
ext_o => ext_access, -- active external access |
-- wishbone interface -- |
wb_tag_o => wb_tag_o, -- request tag |
wb_adr_o => wb_adr_o, -- address |
838,6 → 841,8
neorv32_wishbone_inst_false: |
if (MEM_EXT_EN = false) generate |
resp_bus(RESP_WISHBONE) <= resp_bus_entry_terminate_c; |
ext_timeout <= '0'; |
ext_access <= '0'; |
-- |
wb_adr_o <= (others => '0'); |
wb_dat_o <= (others => '0'); |
878,6 → 883,7
data_i => p_bus.wdata, -- data in |
data_o => resp_bus(RESP_CFS).rdata, -- data out |
ack_o => resp_bus(RESP_CFS).ack, -- transfer acknowledge |
err_o => resp_bus(RESP_CFS).err, -- access error |
-- clock generator -- |
clkgen_en_o => cfs_cg_en, -- enable clock generator |
clkgen_i => clk_gen, -- "clock" inputs |
887,7 → 893,6
cfs_in_i => cfs_in_i, -- custom inputs |
cfs_out_o => cfs_out_o -- custom outputs |
); |
resp_bus(RESP_CFS).err <= '0'; -- no access error possible |
end generate; |
|
neorv32_cfs_inst_false: |
/rtl/core/neorv32_trng.vhd
1,10 → 1,8
-- ################################################################################################# |
-- # << NEORV32 - True Random Number Generator (TRNG) >> # |
-- # ********************************************************************************************* # |
-- # This unit implements a *true* random number generator which uses several ring oscillators as # |
-- # entropy source. The outputs of all chains are XORed and de-biased using a John von Neumann # |
-- # randomness extractor. The de-biased signal is further processed by a simple LFSR for improved # |
-- # whitening. # |
-- # This processor module instantiates the "neoTRNG" true random number generator. # |
-- # See the neoTRNG's documentation for more information: https://github.com/stnolting/neoTRNG # |
-- # ********************************************************************************************* # |
-- # BSD 3-Clause License # |
-- # # |
59,18 → 57,16
|
architecture neorv32_trng_rtl of neorv32_trng is |
|
-- Advanced Configuration -------------------------------------------------------------------------------- |
constant num_roscs_c : natural := 4; -- total number of ring oscillators |
constant num_inv_start_c : natural := 5; -- number of inverters in FIRST ring oscillator (has to be odd) |
constant num_inv_inc_c : natural := 2; -- number of inverters increment for each next ring oscillator (has to be even) |
constant lfsr_en_c : boolean := true; -- use LFSR-based post-processing |
constant lfsr_taps_c : std_ulogic_vector(7 downto 0) := "10111000"; -- Fibonacci post-processing LFSR feedback taps |
-- ------------------------------------------------------------------------------------------------------- |
-- neoTRNG Configuration ------------------------------------------------------------------------------------------- |
constant num_cells_c : natural := 3; -- total number of ring-oscillator cells |
constant num_inv_start_c : natural := 3; -- number of inverters in first cell (short path), has to be odd |
constant num_inv_inc_c : natural := 2; -- number of additional inverters in next cell (short path), has to be even |
constant num_inv_delay_c : natural := 2; -- additional inverters to form cell's long path, has to be even |
-- ----------------------------------------------------------------------------------------------------------------- |
|
-- control register bits -- |
constant ctrl_data_lsb_c : natural := 0; -- r/-: Random data byte LSB |
constant ctrl_data_msb_c : natural := 7; -- r/-: Random data byte MSB |
-- |
constant ctrl_en_c : natural := 30; -- r/w: TRNG enable |
constant ctrl_valid_c : natural := 31; -- r/-: Output data valid |
|
78,57 → 74,38
constant hi_abb_c : natural := index_size_f(io_size_c)-1; -- high address boundary bit |
constant lo_abb_c : natural := index_size_f(trng_size_c); -- low address boundary bit |
|
-- Component: Ring-Oscillator -- |
component neorv32_trng_ring_osc |
-- access control -- |
signal acc_en : std_ulogic; -- module access enable |
signal wren : std_ulogic; -- full word write enable |
signal rden : std_ulogic; -- read enable |
|
-- Component: neoTRNG true random number generator -- |
component neoTRNG |
generic ( |
NUM_INV : natural := 16 -- number of inverters in chain |
NUM_CELLS : natural; -- total number of ring-oscillator cells |
NUM_INV_START : natural; -- number of inverters in first cell (short path), has to be odd |
NUM_INV_INC : natural; -- number of additional inverters in next cell (short path), has to be even |
NUM_INV_DELAY : natural -- additional inverters to form cell's long path, has to be even |
); |
port ( |
clk_i : in std_ulogic; |
enable_i : in std_ulogic; -- enable chain input |
enable_o : out std_ulogic; -- enable chain output |
data_o : out std_ulogic -- sync random bit |
clk_i : in std_ulogic; -- global clock line |
enable_i : in std_ulogic; -- unit enable (high-active), reset unit when low |
data_o : out std_ulogic_vector(7 downto 0); -- random data byte output |
valid_o : out std_ulogic -- data_o is valid when set |
); |
end component; |
|
-- access control -- |
signal acc_en : std_ulogic; -- module access enable |
signal wren : std_ulogic; -- full word write enable |
signal rden : std_ulogic; -- read enable |
-- TRNG interface -- |
signal trng_data : std_ulogic_vector(7 downto 0); |
signal trng_valid : std_ulogic; |
|
-- ring-oscillator array -- |
signal osc_array_en_in : std_ulogic_vector(num_roscs_c-1 downto 0); |
signal osc_array_en_out : std_ulogic_vector(num_roscs_c-1 downto 0); |
signal osc_array_data : std_ulogic_vector(num_roscs_c-1 downto 0); |
-- arbiter -- |
signal enable : std_ulogic; |
signal valid : std_ulogic; |
signal rnd_reg : std_ulogic_vector(7 downto 0); |
|
-- von-Neumann de-biasing -- |
type debiasing_t is record |
sreg : std_ulogic_vector(1 downto 0); |
state : std_ulogic; -- process de-biasing every second cycle |
valid : std_ulogic; -- de-biased data |
data : std_ulogic; -- de-biased data valid |
end record; |
signal debiasing : debiasing_t; |
|
-- (post-)processing core -- |
type processing_t is record |
enable : std_ulogic; -- TRNG enable flag |
cnt : std_ulogic_vector(3 downto 0); -- bit counter |
sreg : std_ulogic_vector(7 downto 0); -- data shift register |
output : std_ulogic_vector(7 downto 0); -- output register |
valid : std_ulogic; -- data output valid flag |
end record; |
signal processing : processing_t; |
|
begin |
|
-- Sanity Checks -------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
assert not (num_roscs_c = 0) report "NEORV32 PROCESSOR CONFIG ERROR: TRNG - Total number of ring-oscillators has to be >0." severity error; |
assert not ((num_inv_start_c mod 2) = 0) report "NEORV32 PROCESSOR CONFIG ERROR: TRNG - Number of inverters in first ring has to be odd." severity error; |
assert not ((num_inv_inc_c mod 2) /= 0) report "NEORV32 PROCESSOR CONFIG ERROR: TRNG - Number of inverters increment for each next ring has to be even." severity error; |
|
|
-- Access Control ------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
acc_en <= '1' when (addr_i(hi_abb_c downto lo_abb_c) = trng_base_c(hi_abb_c downto lo_abb_c)) else '0'; |
141,127 → 118,309
rw_access: process(clk_i) |
begin |
if rising_edge(clk_i) then |
-- host bus acknowledge -- |
ack_o <= wren or rden; |
|
-- write access -- |
if (wren = '1') then |
processing.enable <= data_i(ctrl_en_c); |
enable <= data_i(ctrl_en_c); |
end if; |
|
-- read access -- |
data_o <= (others => '0'); |
if (rden = '1') then |
data_o(ctrl_data_msb_c downto ctrl_data_lsb_c) <= processing.output; |
data_o(ctrl_en_c) <= processing.enable; |
data_o(ctrl_valid_c) <= processing.valid; |
data_o(ctrl_data_msb_c downto ctrl_data_lsb_c) <= rnd_reg; |
data_o(ctrl_en_c) <= enable; |
data_o(ctrl_valid_c) <= valid; |
end if; |
|
-- sample -- |
if (trng_valid = '1') then |
rnd_reg <= trng_data; |
end if; |
|
-- data valid? -- |
if (enable = '0') then -- disabled |
valid <= '0'; |
else |
if (trng_valid = '1') then |
valid <= '1'; |
elsif (rden = '1') then |
valid <= '0'; |
end if; |
end if; |
end if; |
end process rw_access; |
|
|
-- neoTRNG -------------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
neoTRNG_inst: neoTRNG |
generic map ( |
NUM_CELLS => num_cells_c, |
NUM_INV_START => num_inv_start_c, |
NUM_INV_INC => num_inv_inc_c, |
NUM_INV_DELAY => num_inv_delay_c |
) |
port map ( |
clk_i => clk_i, |
enable_i => enable, |
data_o => trng_data, |
valid_o => trng_valid |
); |
|
|
end neorv32_trng_rtl; |
|
|
-- ############################################################################################################################ |
-- ############################################################################################################################ |
|
|
-- ################################################################################################# |
-- # << neoTRNG - A Tiny and Platform-Independent True Random Number Generator for any FPGA >> # |
-- # ********************************************************************************************* # |
-- # This generator is based on entropy cells, which implement simple ring-oscillators. Each ring- # |
-- # oscillator features a short and a long delay path that is dynamically selected defining the # |
-- # primary oscillation frequency. The cells are cascaded so that the random data output of a # |
-- # cell controls the delay path of the next cell (which has the next-larger inverter chain). # |
-- # # |
-- # The random data outputs of all cells are XOR-ed and de-biased using a von Neumann randomness # |
-- # extractor (converting edges into bits). The resulting bit is sampled in chunks of 8 bits to # |
-- # provide the final random data output. No further internal post-processing is applied. Hence, # |
-- # the TRNG produces simple de-biased *RAW* data. # |
-- # # |
-- # The entropy cell architecture uses individually-controlled latches and inverters to create # |
-- # the inverter chain in a platform-agnostic style that can be implemented for any FPGA without # |
-- # requiring primitive instantiation or technology-specific attributes. # |
-- # # |
-- # See the neoTRNG's documentation for more information: https://github.com/stnolting/neoTRNG # |
-- # ********************************************************************************************* # |
-- # BSD 3-Clause License # |
-- # # |
-- # Copyright (c) 2021, Stephan Nolting. All rights reserved. # |
-- # # |
-- # Redistribution and use in source and binary forms, with or without modification, are # |
-- # permitted provided that the following conditions are met: # |
-- # # |
-- # 1. Redistributions of source code must retain the above copyright notice, this list of # |
-- # conditions and the following disclaimer. # |
-- # # |
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of # |
-- # conditions and the following disclaimer in the documentation and/or other materials # |
-- # provided with the distribution. # |
-- # # |
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to # |
-- # endorse or promote products derived from this software without specific prior written # |
-- # permission. # |
-- # # |
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS # |
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # |
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # |
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # |
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # |
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # |
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # |
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # |
-- # OF THE POSSIBILITY OF SUCH DAMAGE. # |
-- # ********************************************************************************************* # |
-- # neoTRNG - https://github.com/stnolting/neoTRNG (c) Stephan Nolting # |
-- ################################################################################################# |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity neoTRNG is |
generic ( |
NUM_CELLS : natural; -- total number of ring-oscillator cells |
NUM_INV_START : natural; -- number of inverters in first cell (short path), has to be odd |
NUM_INV_INC : natural; -- number of additional inverters in next cell (short path), has to be even |
NUM_INV_DELAY : natural -- additional inverters to form cell's long path, has to be even |
); |
port ( |
clk_i : in std_ulogic; -- global clock line |
enable_i : in std_ulogic; -- unit enable (high-active), reset unit when low |
data_o : out std_ulogic_vector(7 downto 0); -- random data byte output |
valid_o : out std_ulogic -- data_o is valid when set |
); |
end neoTRNG; |
|
architecture neoTRNG_rtl of neoTRNG is |
|
-- Component: neoTRNG entropy cell -- |
component neoTRNG_cell |
generic ( |
NUM_INV_S : natural; -- number of inverters in short path |
NUM_INV_L : natural -- number of inverters in long path |
); |
port ( |
clk_i : in std_ulogic; -- system clock |
select_i : in std_ulogic; -- delay select |
enable_i : in std_ulogic; -- enable chain input |
enable_o : out std_ulogic; -- enable chain output |
data_o : out std_ulogic -- sync random bit |
); |
end component; |
|
-- ring-oscillator array interconnect -- |
type cell_array_t is record |
en_in : std_ulogic_vector(NUM_CELLS-1 downto 0); |
en_out : std_ulogic_vector(NUM_CELLS-1 downto 0); |
rnd : std_ulogic_vector(NUM_CELLS-1 downto 0); |
sel : std_ulogic_vector(NUM_CELLS-1 downto 0); |
end record; |
signal cell_array : cell_array_t; |
|
-- global cell-XOR -- |
signal rnd_bit : std_ulogic; |
|
-- von-Neumann de-biasing -- |
type debiasing_t is record |
sreg : std_ulogic_vector(1 downto 0); |
state : std_ulogic; -- process de-biasing every second cycle |
valid : std_ulogic; -- de-biased data |
data : std_ulogic; -- de-biased data valid |
end record; |
signal deb : debiasing_t; |
|
-- control unit -- |
type ctrl_t is record |
enable : std_ulogic; |
run : std_ulogic; |
cnt : std_ulogic_vector(2 downto 0); -- bit counter |
sreg : std_ulogic_vector(7 downto 0); -- data shift register |
end record; |
signal ctrl : ctrl_t; |
|
begin |
|
-- Sanity Checks -------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
assert not (NUM_CELLS < 2) report "neoTRNG config ERROR: Total number of ring-oscillator cells <NUM_CELLS> has to be >= 2." severity error; |
assert not ((NUM_INV_START mod 2) = 0) report "neoTRNG config ERROR: Number of inverters in first cell <NUM_INV_START> has to be odd." severity error; |
assert not ((NUM_INV_INC mod 2) /= 0) report "neoTRNG config ERROR: Inverter increment for each next cell <NUM_INV_INC> has to be even." severity error; |
assert not ((NUM_INV_DELAY mod 2) /= 0) report "neoTRNG config ERROR: Inverter increment to form long path <NUM_INV_DELAY> has to be even." severity error; |
|
|
-- Entropy Source ------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
neorv32_trng_ring_osc_inst: |
for i in 0 to num_roscs_c-1 generate |
neorv32_trng_ring_osc_inst_i: neorv32_trng_ring_osc |
neoTRNG_cell_inst: |
for i in 0 to NUM_CELLS-1 generate |
neoTRNG_cell_inst_i: neoTRNG_cell |
generic map ( |
NUM_INV => num_inv_start_c + (i*num_inv_inc_c) -- number of inverters in chain |
NUM_INV_S => NUM_INV_START + (i*NUM_INV_INC), -- number of inverters in short chain |
NUM_INV_L => NUM_INV_START + (i*NUM_INV_INC) + NUM_INV_DELAY -- number of inverters in long chain |
) |
port map ( |
clk_i => clk_i, |
enable_i => osc_array_en_in(i), |
enable_o => osc_array_en_out(i), |
data_o => osc_array_data(i) |
select_i => cell_array.sel(i), |
enable_i => cell_array.en_in(i), |
enable_o => cell_array.en_out(i), |
data_o => cell_array.rnd(i) -- SYNC data output |
); |
end generate; |
|
-- RO enable chain -- |
array_intercon: process(processing.enable, osc_array_en_out) |
-- path select chain -- |
cell_array.sel(0) <= cell_array.rnd(NUM_CELLS-1); -- use output of last cell to select path of first cell |
cell_array.sel(NUM_CELLS-1 downto 1) <= cell_array.rnd(NUM_CELLS-2 downto 0); -- i+1 <= i |
|
-- enable chain -- |
cell_array.en_in(0) <= ctrl.enable; -- start of chain |
cell_array.en_in(NUM_CELLS-1 downto 1) <=cell_array.en_out(NUM_CELLS-2 downto 0); -- i+1 <= i |
|
|
-- XOR All Cell's Outputs ----------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
cell_xor: process(cell_array.rnd) |
variable tmp_v : std_ulogic; |
begin |
for i in 0 to num_roscs_c-1 loop |
if (i = 0) then -- start of enable chain |
osc_array_en_in(i) <= processing.enable; |
else |
osc_array_en_in(i) <= osc_array_en_out(i-1); |
end if; |
tmp_v := '0'; |
for i in 0 to NUM_CELLS-1 loop |
tmp_v := tmp_v xor cell_array.rnd(i); |
end loop; -- i |
end process array_intercon; |
rnd_bit <= tmp_v; |
end process cell_xor; |
|
|
-- John von Neumann De-Biasing ------------------------------------------------------------ |
-- John von Neumann Randomness Extractor -------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
neumann_debiasing_sync: process(clk_i) |
debiasing_sync: process(clk_i) |
begin |
if rising_edge(clk_i) then |
debiasing.sreg <= debiasing.sreg(debiasing.sreg'left-1 downto 0) & xor_reduce_f(osc_array_data); |
debiasing.state <= (not debiasing.state) and osc_array_en_out(num_roscs_c-1); -- start toggling when last RO is enabled -> process in every second cycle |
deb.sreg <= deb.sreg(0) & rnd_bit; |
-- start operation when last cell is enabled and process in every second cycle -- |
deb.state <= (not deb.state) and cell_array.en_out(NUM_CELLS-1); |
end if; |
end process neumann_debiasing_sync; |
end process debiasing_sync; |
|
-- Edge detector -- |
neumann_debiasing_comb: process(debiasing) |
-- edge detector -- |
debiasing_comb: process(deb) |
variable tmp_v : std_ulogic_vector(2 downto 0); |
begin |
-- check groups of two non-overlapping bits from the input stream |
tmp_v := debiasing.state & debiasing.sreg; |
tmp_v := deb.state & deb.sreg(1 downto 0); -- check groups of two non-overlapping bits from the input stream |
case tmp_v is |
when "101" => debiasing.valid <= '1'; debiasing.data <= '1'; -- rising edge -> '1' |
when "110" => debiasing.valid <= '1'; debiasing.data <= '0'; -- falling edge -> '0' |
when others => debiasing.valid <= '0'; debiasing.data <= '0'; -- no valid data |
when "101" => deb.valid <= '1'; deb.data <= '0'; -- rising edge = '0' |
when "110" => deb.valid <= '1'; deb.data <= '1'; -- falling edge = '1' |
when others => deb.valid <= '0'; deb.data <= '-'; -- no valid data |
end case; |
end process neumann_debiasing_comb; |
end process debiasing_comb; |
|
|
-- Processing Core ------------------------------------------------------------------------ |
-- Control Unit --------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
processing_core: process(clk_i) |
control_unit: process(clk_i) |
begin |
if rising_edge(clk_i) then |
-- sample random data bit and apply post-processing -- |
if (processing.enable = '0') then |
processing.cnt <= (others => '0'); |
processing.sreg <= (others => '0'); |
elsif (debiasing.valid = '1') then -- valid random sample? |
if (processing.cnt = "1000") then |
processing.cnt <= (others => '0'); |
else |
processing.cnt <= std_ulogic_vector(unsigned(processing.cnt) + 1); |
end if; |
if (lfsr_en_c = true) then -- LFSR post-processing |
processing.sreg <= processing.sreg(processing.sreg'left-1 downto 0) & ((not xor_reduce_f(processing.sreg and lfsr_taps_c)) xnor debiasing.data); |
else -- NO post-processing |
processing.sreg <= processing.sreg(processing.sreg'left-1 downto 0) & debiasing.data; |
end if; |
-- make sure enable is sync -- |
ctrl.enable <= enable_i; |
|
-- sample chunks of 8 bit -- |
if (ctrl.enable = '0') then |
ctrl.cnt <= (others => '0'); |
ctrl.run <= '0'; |
elsif (deb.valid = '1') then -- valid random sample? |
ctrl.cnt <= std_ulogic_vector(unsigned(ctrl.cnt) + 1); |
ctrl.run <= '1'; |
end if; |
|
-- data output register -- |
if (processing.cnt = "1000") then |
processing.output <= processing.sreg; |
-- sample shift register -- |
if (deb.valid = '1') then |
ctrl.sreg <= ctrl.sreg(ctrl.sreg'left-1 downto 0) & deb.data; |
end if; |
|
-- data ready/valid flag -- |
if (processing.cnt = "1000") then -- new sample ready? |
processing.valid <= '1'; |
elsif (processing.enable = '0') or (rden = '1') then -- clear when deactivated or on data read |
processing.valid <= '0'; |
end if; |
end if; |
end process processing_core; |
end process control_unit; |
|
-- random byte output -- |
data_o <= ctrl.sreg; |
|
end neorv32_trng_rtl; |
-- data valid? -- |
valid_o <= '1' when (ctrl.cnt = "000") and (ctrl.run = '1') else '0'; |
|
|
end neoTRNG_rtl; |
|
|
-- ############################################################################################################################ |
-- ############################################################################################################################ |
|
|
-- ################################################################################################# |
-- # << NEORV32 - True Random Number Generator (TRNG) - Ring-Oscillator-Based Entropy Source >> # |
-- # << neoTRNG - A Tiny and Platform-Independent True Random Number Generator for any FPGA >> # |
-- # ********************************************************************************************* # |
-- # An inverter chain (ring oscillator) is used as entropy source. # |
-- # The inverter chain is constructed as an "asynchronous" LFSR. The single inverters are # |
-- # connected via latches that are used to enable/disable the TRNG. Also, these latches are used # |
-- # as additional delay element. By using unique enable signals for each latch, the synthesis # |
-- # tool cannot "optimize" (=remove) any of the inverters out of the design. # |
-- # neoTRNG Entropy Cell # |
-- # # |
-- # The cell consists of two ring-oscillators build from inverter chains. The short chain uses # |
-- # NUM_INV_S inverters and oscillates at a "high" frequency and the long chain uses NUM_INV_L # |
-- # inverters and oscillates at a "low" frequency. The select_i input selects which chain is # |
-- # actually used. # |
-- # # |
-- # Each inverter chain is constructed as an "asynchronous" shift register. The single inverters # |
-- # are connected via latches that are used to enable/disable the TRNG. Also, these latches are # |
-- # used as additional delay element. By using unique enable signals for each latch, the # |
-- # synthesis tool cannot "optimize" (=remove) any of the inverters out of the design making the # |
-- # design platform-agnostic. # |
-- # ********************************************************************************************* # |
-- # BSD 3-Clause License # |
-- # # |
291,73 → 450,101
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # |
-- # OF THE POSSIBILITY OF SUCH DAMAGE. # |
-- # ********************************************************************************************* # |
-- # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
-- # neoTRNG - https://github.com/stnolting/neoTRNG (c) Stephan Nolting # |
-- ################################################################################################# |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
library neorv32; |
use neorv32.neorv32_package.all; |
|
entity neorv32_trng_ring_osc is |
entity neoTRNG_cell is |
generic ( |
NUM_INV : natural := 15 -- number of inverters in chain |
NUM_INV_S : natural; -- number of inverters in short path |
NUM_INV_L : natural -- number of inverters in long path |
); |
port ( |
clk_i : in std_ulogic; |
clk_i : in std_ulogic; -- system clock |
select_i : in std_ulogic; -- delay select |
enable_i : in std_ulogic; -- enable chain input |
enable_o : out std_ulogic; -- enable chain output |
data_o : out std_ulogic -- sync random bit |
); |
end neorv32_trng_ring_osc; |
end neoTRNG_cell; |
|
architecture neorv32_trng_ring_osc_rtl of neorv32_trng_ring_osc is |
architecture neoTRNG_cell_rtl of neoTRNG_cell is |
|
signal inv_chain : std_ulogic_vector(NUM_INV-1 downto 0); -- oscillator chain |
signal enable_sreg : std_ulogic_vector(NUM_INV-1 downto 0); -- enable shift register |
signal sync_ff : std_ulogic_vector(1 downto 0); -- output signal synchronizer |
signal inv_chain_s : std_ulogic_vector(NUM_INV_S-1 downto 0); -- short oscillator chain |
signal inv_chain_l : std_ulogic_vector(NUM_INV_L-1 downto 0); -- long oscillator chain |
signal feedback : std_ulogic; -- cell feedback/output |
signal enable_sreg_s : std_ulogic_vector(NUM_INV_S-1 downto 0); -- enable shift register for short chain |
signal enable_sreg_l : std_ulogic_vector(NUM_INV_L-1 downto 0); -- enable shift register for long chain |
signal sync_ff : std_ulogic_vector(1 downto 0); -- output signal synchronizer |
|
begin |
|
-- Ring Oscillator ------------------------------------------------------------------------ |
-- Ring Oscillators ----------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
ring_osc: process(enable_i, enable_sreg, inv_chain) |
-- Each cell provides a short inverter chain (high frequency) and a long oscillator chain (low frequency). |
-- The select_i signals defines which chain is enabled. |
-- NOTE: All signals that control a inverter-latch element have to be registered to ensure a single element |
-- is mapped to a single LUT (or LUT + FF(latch-mode)). |
|
-- short oscillator chain -- |
ring_osc_short: process(enable_i, enable_sreg_s, feedback, inv_chain_s) |
begin |
-- Using individual enable signals for each inverter - derived from a shift register - to prevent the synthesis tool |
-- from removing all but one inverter (since they implement "logical identical functions"). |
-- This also allows to make the TRNG platform independent. |
for i in 0 to NUM_INV-1 loop -- inverters in chain |
for i in 0 to NUM_INV_S-1 loop -- inverters in short chain |
if (enable_i = '0') then -- start with a defined state (latch reset) |
inv_chain(i) <= '0'; |
elsif (enable_sreg(i) = '1') then |
-- here we have the inverter chain -- |
if (i = NUM_INV-1) then -- left-most inverter? |
inv_chain(i) <= not inv_chain(0); |
inv_chain_s(i) <= '0'; |
elsif (enable_sreg_s(i) = '1') then |
if (i = NUM_INV_S-1) then -- left-most inverter? |
inv_chain_s(i) <= not feedback; |
else |
inv_chain(i) <= not inv_chain(i+1); |
inv_chain_s(i) <= not inv_chain_s(i+1); |
end if; |
end if; |
end loop; -- i |
end process ring_osc; |
end process ring_osc_short; |
|
-- long oscillator chain -- |
ring_osc_long: process(enable_i, enable_sreg_l, feedback, inv_chain_l) |
begin |
for i in 0 to NUM_INV_L-1 loop -- inverters in long chain |
if (enable_i = '0') then -- start with a defined state (latch reset) |
inv_chain_l(i) <= '0'; |
elsif (enable_sreg_l(i) = '1') then |
if (i = NUM_INV_L-1) then -- left-most inverter? |
inv_chain_l(i) <= not feedback; |
else |
inv_chain_l(i) <= not inv_chain_l(i+1); |
end if; |
end if; |
end loop; -- i |
end process ring_osc_long; |
|
-- length select -- |
feedback <= inv_chain_l(0) when (select_i = '0') else inv_chain_s(0); |
|
|
-- Control -------------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
-- Using individual enable signals for each inverter from a shift register to prevent the synthesis tool |
-- from removing all but one inverter (since they implement "logical identical functions" (='toggle')). |
-- This makes the TRNG platform independent (since we do not need to use primitives to ensure a correct architecture). |
ctrl_unit: process(clk_i) |
begin |
if rising_edge(clk_i) then |
enable_sreg <= enable_sreg(enable_sreg'left-1 downto 0) & enable_i; -- activate right-most inverter first |
sync_ff <= sync_ff(0) & inv_chain(0); -- synchronize to prevent metastability |
-- enable sreg -- |
enable_sreg_s <= enable_sreg_s(enable_sreg_s'left-1 downto 0) & enable_i; |
enable_sreg_l <= enable_sreg_l(enable_sreg_l'left-1 downto 0) & enable_sreg_s(enable_sreg_s'left); |
-- data output sync - no metastability beyond this point -- |
sync_ff <= sync_ff(0) & feedback; |
end if; |
end process ctrl_unit; |
|
-- output for "enable chain" -- |
enable_o <= enable_sreg(enable_sreg'left); |
enable_o <= enable_sreg_l(enable_sreg_l'left); |
|
-- rnd output -- |
-- random data output -- |
data_o <= sync_ff(1); |
|
|
end neorv32_trng_ring_osc_rtl; |
end neoTRNG_cell_rtl; |
/rtl/core/neorv32_twi.vhd
3,7 → 3,7
-- # ********************************************************************************************* # |
-- # Supports START and STOP conditions, 8 bit data + ACK/NACK transfers and clock stretching. # |
-- # Supports ACKs by the controller. No multi-controller support and no peripheral mode support # |
-- # yet. Interrupt: TWI_idle # |
-- # yet. Interrupt: "operation done" # |
-- # ********************************************************************************************* # |
-- # BSD 3-Clause License # |
-- # # |
70,45 → 70,55
constant hi_abb_c : natural := index_size_f(io_size_c)-1; -- high address boundary bit |
constant lo_abb_c : natural := index_size_f(twi_size_c); -- low address boundary bit |
|
-- control reg bits -- |
constant ctrl_twi_en_c : natural := 0; -- r/w: TWI enable |
constant ctrl_twi_start_c : natural := 1; -- -/w: Generate START condition |
constant ctrl_twi_stop_c : natural := 2; -- -/w: Generate STOP condition |
constant ctrl_twi_prsc0_c : natural := 3; -- r/w: CLK prsc bit 0 |
constant ctrl_twi_prsc1_c : natural := 4; -- r/w: CLK prsc bit 1 |
constant ctrl_twi_prsc2_c : natural := 5; -- r/w: CLK prsc bit 2 |
constant ctrl_twi_mack_c : natural := 6; -- r/w: generate ACK by controller for transmission |
constant ctrl_twi_cksten_c : natural := 7; -- r/w: enable clock stretching by peripheral |
-- control register -- |
constant ctrl_en_c : natural := 0; -- r/w: TWI enable |
constant ctrl_start_c : natural := 1; -- -/w: Generate START condition |
constant ctrl_stop_c : natural := 2; -- -/w: Generate STOP condition |
constant ctrl_prsc0_c : natural := 3; -- r/w: CLK prsc bit 0 |
constant ctrl_prsc1_c : natural := 4; -- r/w: CLK prsc bit 1 |
constant ctrl_prsc2_c : natural := 5; -- r/w: CLK prsc bit 2 |
constant ctrl_mack_c : natural := 6; -- r/w: generate ACK by controller for transmission |
-- |
constant ctrl_twi_ack_c : natural := 30; -- r/-: Set if ACK received |
constant ctrl_twi_busy_c : natural := 31; -- r/-: Set if TWI unit is busy |
constant ctrl_ack_c : natural := 30; -- r/-: Set if ACK received |
constant ctrl_busy_c : natural := 31; -- r/-: Set if TWI unit is busy |
-- |
signal ctrl : std_ulogic_vector(6 downto 0); -- unit's control register |
|
-- access control -- |
signal acc_en : std_ulogic; -- module access enable |
signal addr : std_ulogic_vector(31 downto 0); -- access address |
signal wr_en : std_ulogic; -- word write enable |
signal rd_en : std_ulogic; -- read enable |
signal wren : std_ulogic; -- word write enable |
signal rden : std_ulogic; -- read enable |
|
-- twi clocking -- |
signal twi_clk : std_ulogic; |
signal twi_phase_gen : std_ulogic_vector(3 downto 0); |
signal twi_clk_phase : std_ulogic_vector(3 downto 0); |
signal twi_clk : std_ulogic; |
signal twi_phase_gen : std_ulogic_vector(3 downto 0); |
signal twi_clk_phase : std_ulogic_vector(3 downto 0); |
|
-- twi clock stretching -- |
signal twi_clk_halt : std_ulogic; |
|
-- twi transceiver core -- |
signal ctrl : std_ulogic_vector(7 downto 0); -- unit's control register |
signal arbiter : std_ulogic_vector(2 downto 0); |
signal bitcnt : std_ulogic_vector(3 downto 0); |
signal rtx_sreg : std_ulogic_vector(8 downto 0); -- main rx/tx shift reg |
|
-- tri-state I/O -- |
signal twi_sda_i_ff0, twi_sda_i_ff1 : std_ulogic; -- sda input sync |
signal twi_scl_i_ff0, twi_scl_i_ff1 : std_ulogic; -- sda input sync |
signal twi_sda_i, twi_sda_o : std_ulogic; |
signal twi_scl_i, twi_scl_o : std_ulogic; |
signal twi_sda_in_ff : std_ulogic_vector(1 downto 0); -- SDA input sync |
signal twi_scl_in_ff : std_ulogic_vector(1 downto 0); -- SCL input sync |
signal twi_sda_in : std_ulogic; |
signal twi_scl_in : std_ulogic; |
signal twi_sda_out : std_ulogic; |
signal twi_scl_out : std_ulogic; |
|
-- interrupt generator -- |
type irq_t is record |
pending : std_ulogic; -- pending interrupt request |
set : std_ulogic; |
clr : std_ulogic; |
end record; |
signal irq : irq_t; |
|
begin |
|
-- Access Control ------------------------------------------------------------------------- |
115,8 → 125,8
-- ------------------------------------------------------------------------------------------- |
acc_en <= '1' when (addr_i(hi_abb_c downto lo_abb_c) = twi_base_c(hi_abb_c downto lo_abb_c)) else '0'; |
addr <= twi_base_c(31 downto lo_abb_c) & addr_i(lo_abb_c-1 downto 2) & "00"; -- word aligned |
wr_en <= acc_en and wren_i; |
rd_en <= acc_en and rden_i; |
wren <= acc_en and wren_i; |
rden <= acc_en and rden_i; |
|
|
-- Read/Write Access ---------------------------------------------------------------------- |
124,9 → 134,9
rw_access: process(clk_i) |
begin |
if rising_edge(clk_i) then |
ack_o <= acc_en and (rden_i or wren_i); |
ack_o <= rden or wren; |
-- write access -- |
if (wr_en = '1') then |
if (wren = '1') then |
if (addr = twi_ctrl_addr_c) then |
ctrl <= data_i(ctrl'left downto 0); |
end if; |
133,20 → 143,18
end if; |
-- read access -- |
data_o <= (others => '0'); |
if (rd_en = '1') then |
if (rden = '1') then |
if (addr = twi_ctrl_addr_c) then |
data_o(ctrl_twi_en_c) <= ctrl(ctrl_twi_en_c); |
data_o(ctrl_twi_prsc0_c) <= ctrl(ctrl_twi_prsc0_c); |
data_o(ctrl_twi_prsc1_c) <= ctrl(ctrl_twi_prsc1_c); |
data_o(ctrl_twi_prsc2_c) <= ctrl(ctrl_twi_prsc2_c); |
data_o(ctrl_twi_mack_c) <= ctrl(ctrl_twi_mack_c); |
data_o(ctrl_twi_cksten_c) <= ctrl(ctrl_twi_cksten_c); |
data_o(ctrl_en_c) <= ctrl(ctrl_en_c); |
data_o(ctrl_prsc0_c) <= ctrl(ctrl_prsc0_c); |
data_o(ctrl_prsc1_c) <= ctrl(ctrl_prsc1_c); |
data_o(ctrl_prsc2_c) <= ctrl(ctrl_prsc2_c); |
data_o(ctrl_mack_c) <= ctrl(ctrl_mack_c); |
-- |
data_o(ctrl_twi_ack_c) <= not rtx_sreg(0); |
data_o(ctrl_twi_busy_c) <= arbiter(1) or arbiter(0); |
data_o(ctrl_ack_c) <= not rtx_sreg(0); |
data_o(ctrl_busy_c) <= arbiter(1) or arbiter(0); |
else -- twi_rtx_addr_c => |
data_o(7 downto 0) <= rtx_sreg(8 downto 1); |
|
data_o(7 downto 0) <= rtx_sreg(8 downto 1); |
end if; |
end if; |
end if; |
156,16 → 164,16
-- Clock Generation ----------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
-- clock generator enable -- |
clkgen_en_o <= ctrl(ctrl_twi_en_c); |
clkgen_en_o <= ctrl(ctrl_en_c); |
|
-- twi clock select -- |
twi_clk <= clkgen_i(to_integer(unsigned(ctrl(ctrl_twi_prsc2_c downto ctrl_twi_prsc0_c)))); |
twi_clk <= clkgen_i(to_integer(unsigned(ctrl(ctrl_prsc2_c downto ctrl_prsc0_c)))); |
|
-- generate four non-overlapping clock ticks at twi_clk/4 -- |
clock_phase_gen: process(clk_i) |
begin |
if rising_edge(clk_i) then |
if (arbiter(2) = '0') or (arbiter = "100") then -- offline or idle |
if (arbiter(2) = '0') or (arbiter(1 downto 0) = "00") then -- offline or idle |
twi_phase_gen <= "0001"; -- make sure to start with a new phase, bit 0,1,2,3 stepping |
elsif (twi_clk = '1') and (twi_clk_halt = '0') then -- enabled and no clock stretching detected |
twi_phase_gen <= twi_phase_gen(2 downto 0) & twi_phase_gen(3); -- rotate left |
186,35 → 194,29
begin |
if rising_edge(clk_i) then |
-- input synchronizer & sampler -- |
twi_sda_i_ff0 <= twi_sda_i; |
twi_sda_i_ff1 <= twi_sda_i_ff0; |
twi_scl_i_ff0 <= twi_scl_i; |
twi_scl_i_ff1 <= twi_scl_i_ff0; |
twi_sda_in_ff <= twi_sda_in_ff(0) & twi_sda_in; |
twi_scl_in_ff <= twi_scl_in_ff(0) & twi_scl_in; |
|
-- interrupt -- |
if (arbiter = "100") then -- fire IRQ if enabled transceiver is idle |
irq_o <= '1'; |
else |
irq_o <= '0'; |
end if; |
-- defaults -- |
irq.set <= '0'; |
|
-- serial engine -- |
arbiter(2) <= ctrl(ctrl_twi_en_c); -- still activated? |
arbiter(2) <= ctrl(ctrl_en_c); -- still activated? |
case arbiter is |
|
when "100" => -- IDLE: waiting for requests, bus might be still claimed by this controller if no STOP condition was generated |
bitcnt <= (others => '0'); |
if (wr_en = '1') then |
if (wren = '1') then |
if (addr = twi_ctrl_addr_c) then |
if (data_i(ctrl_twi_start_c) = '1') then -- issue START condition |
if (data_i(ctrl_start_c) = '1') then -- issue START condition |
arbiter(1 downto 0) <= "01"; |
elsif (data_i(ctrl_twi_stop_c) = '1') then -- issue STOP condition |
elsif (data_i(ctrl_stop_c) = '1') then -- issue STOP condition |
arbiter(1 downto 0) <= "10"; |
end if; |
elsif (addr = twi_rtx_addr_c) then -- start a data transmission |
-- one bit extra for ack, issued by controller if ctrl_twi_mack_c is set, |
-- sampled from peripheral if ctrl_twi_mack_c is cleared |
rtx_sreg <= data_i(7 downto 0) & (not ctrl(ctrl_twi_mack_c)); |
-- one bit extra for ack, issued by controller if ctrl_mack_c is set, |
-- sampled from peripheral if ctrl_mack_c is cleared |
rtx_sreg <= data_i(7 downto 0) & (not ctrl(ctrl_mack_c)); |
arbiter(1 downto 0) <= "11"; |
end if; |
end if; |
221,51 → 223,54
|
when "101" => -- START: generate START condition |
if (twi_clk_phase(0) = '1') then |
twi_sda_o <= '1'; |
twi_sda_out <= '1'; |
elsif (twi_clk_phase(1) = '1') then |
twi_sda_o <= '0'; |
twi_sda_out <= '0'; |
end if; |
-- |
if (twi_clk_phase(0) = '1') then |
twi_scl_o <= '1'; |
twi_scl_out <= '1'; |
elsif (twi_clk_phase(3) = '1') then |
twi_scl_o <= '0'; |
twi_scl_out <= '0'; |
irq.set <= '1'; -- Interrupt! |
arbiter(1 downto 0) <= "00"; -- go back to IDLE |
end if; |
|
when "110" => -- STOP: generate STOP condition |
if (twi_clk_phase(0) = '1') then |
twi_sda_o <= '0'; |
twi_sda_out <= '0'; |
elsif (twi_clk_phase(3) = '1') then |
twi_sda_o <= '1'; |
twi_sda_out <= '1'; |
irq.set <= '1'; -- Interrupt! |
arbiter(1 downto 0) <= "00"; -- go back to IDLE |
end if; |
-- |
if (twi_clk_phase(0) = '1') then |
twi_scl_o <= '0'; |
twi_scl_out <= '0'; |
elsif (twi_clk_phase(1) = '1') then |
twi_scl_o <= '1'; |
twi_scl_out <= '1'; |
end if; |
|
when "111" => -- TRANSMISSION: transmission in progress |
if (twi_clk_phase(0) = '1') then |
bitcnt <= std_ulogic_vector(unsigned(bitcnt) + 1); |
twi_scl_o <= '0'; |
twi_sda_o <= rtx_sreg(8); -- MSB first |
twi_scl_out <= '0'; |
twi_sda_out <= rtx_sreg(8); -- MSB first |
elsif (twi_clk_phase(1) = '1') then -- first half + second half of valid data strobe |
twi_scl_o <= '1'; |
twi_scl_out <= '1'; |
elsif (twi_clk_phase(3) = '1') then |
rtx_sreg <= rtx_sreg(7 downto 0) & twi_sda_i_ff1; -- sample and shift left |
twi_scl_o <= '0'; |
rtx_sreg <= rtx_sreg(7 downto 0) & twi_sda_in_ff(twi_sda_in_ff'left); -- sample and shift left |
twi_scl_out <= '0'; |
end if; |
-- |
if (bitcnt = "1010") then -- 8 data bits + 1 bit for ACK + 1 tick delay |
irq.set <= '1'; -- Interrupt! |
arbiter(1 downto 0) <= "00"; -- go back to IDLE |
end if; |
|
when others => -- "0--" OFFLINE: TWI deactivated |
twi_sda_o <= '1'; |
twi_scl_o <= '1'; |
twi_sda_out <= '1'; |
twi_scl_out <= '1'; |
arbiter(1 downto 0) <= "00"; -- stay here, go to idle when activated |
|
end case; |
275,29 → 280,43
|
-- Clock Stretching Detector -------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
clock_stretching: process(ctrl, arbiter, twi_scl_o, twi_scl_i_ff1) |
begin |
-- clock stretching by the peripheral can happen at "any time" |
if (arbiter(2) = '1') and -- module enabled |
(ctrl(ctrl_twi_cksten_c) = '1') and -- clock stretching enabled |
(twi_scl_o = '1') and -- controller wants to pull scl high |
(twi_scl_i_ff1 = '0') then -- but scl is pulled low by peripheral |
twi_clk_halt <= '1'; |
else |
twi_clk_halt <= '0'; |
end if; |
end process clock_stretching; |
-- controller wants to pull SCL high, but SCL is pulled low by peripheral -- |
twi_clk_halt <= '1' when (twi_scl_out = '1') and (twi_scl_in_ff(twi_scl_in_ff'left) = '0') else '0'; |
|
|
-- Tri-State Driver ----------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
-- SDA and SCL need to be of type std_logic to be correctly resolved in simulation |
twi_sda_io <= '0' when (twi_sda_o = '0') else 'Z'; |
twi_scl_io <= '0' when (twi_scl_o = '0') else 'Z'; |
twi_sda_io <= '0' when (twi_sda_out = '0') else 'Z'; |
twi_scl_io <= '0' when (twi_scl_out = '0') else 'Z'; |
|
-- read-back -- |
twi_sda_i <= std_ulogic(twi_sda_io); |
twi_scl_i <= std_ulogic(twi_scl_io); |
twi_sda_in <= std_ulogic(twi_sda_io); |
twi_scl_in <= std_ulogic(twi_scl_io); |
|
|
-- Interrupt Generator -------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
irq_generator: process(clk_i) |
begin |
if rising_edge(clk_i) then |
if (ctrl(ctrl_en_c) = '0') then |
irq.pending <= '0'; |
else |
if (irq.set = '1') then |
irq.pending <= '1'; |
elsif (irq.clr = '1') then |
irq.pending <= '0'; |
end if; |
end if; |
end if; |
end process irq_generator; |
|
-- IRQ request to CPU -- |
irq_o <= irq.pending; |
|
-- IRQ acknowledge -- |
irq.clr <= '1' when ((rden = '1') and (addr = twi_rtx_addr_c)) or (wren = '1') else '0'; -- read data register OR write data/control register |
|
|
end neorv32_twi_rtl; |
/rtl/core/neorv32_uart.vhd
163,8 → 163,8
-- access control -- |
signal acc_en : std_ulogic; -- module access enable |
signal addr : std_ulogic_vector(31 downto 0); -- access address |
signal wr_en : std_ulogic; -- word write enable |
signal rd_en : std_ulogic; -- read enable |
signal wren : std_ulogic; -- word write enable |
signal rden : std_ulogic; -- read enable |
|
-- clock generator -- |
signal uart_clk : std_ulogic; |
181,6 → 181,7
type tx_engine_t is record |
state : tx_state_t; |
busy : std_ulogic; |
done : std_ulogic; |
bitcnt : std_ulogic_vector(03 downto 0); |
sreg : std_ulogic_vector(10 downto 0); |
baud_cnt : std_ulogic_vector(11 downto 0); |
192,6 → 193,7
type rx_state_t is (S_RX_IDLE, S_RX_RECEIVE); |
type rx_engine_t is record |
state : rx_state_t; |
done : std_ulogic; |
sync : std_ulogic_vector(04 downto 0); |
bitcnt : std_ulogic_vector(03 downto 0); |
sreg : std_ulogic_vector(09 downto 0); |
227,6 → 229,14
end record; |
signal rx_buffer : rx_buffer_t; |
|
-- interrupt generator -- |
type irq_t is record |
pending : std_ulogic; -- pending interrupt request |
set : std_ulogic; |
clr : std_ulogic; |
end record; |
signal rx_irq, tx_irq : irq_t; |
|
begin |
|
-- Sanity Checks -------------------------------------------------------------------------- |
241,8 → 251,8
-- ------------------------------------------------------------------------------------------- |
acc_en <= '1' when (addr_i(hi_abb_c downto lo_abb_c) = uart_id_base_c(hi_abb_c downto lo_abb_c)) else '0'; |
addr <= uart_id_base_c(31 downto lo_abb_c) & addr_i(lo_abb_c-1 downto 2) & "00"; -- word aligned |
wr_en <= acc_en and wren_i; |
rd_en <= acc_en and rden_i; |
wren <= acc_en and wren_i; |
rden <= acc_en and rden_i; |
|
|
-- Read/Write Access ---------------------------------------------------------------------- |
251,10 → 261,10
begin |
if rising_edge(clk_i) then |
-- bus access acknowledge -- |
ack_o <= wr_en or rd_en; |
ack_o <= wren or rden; |
|
-- write access -- |
if (wr_en = '1') then |
if (wren = '1') then |
if (addr = uart_id_ctrl_addr_c) then |
ctrl <= (others => '0'); |
ctrl(ctrl_baud11_c downto ctrl_baud00_c) <= data_i(ctrl_baud11_c downto ctrl_baud00_c); |
271,7 → 281,7
|
-- read access -- |
data_o <= (others => '0'); |
if (rd_en = '1') then |
if (rden = '1') then |
if (addr = uart_id_ctrl_addr_c) then |
data_o(ctrl_baud11_c downto ctrl_baud00_c) <= ctrl(ctrl_baud11_c downto ctrl_baud00_c); |
data_o(ctrl_sim_en_c) <= ctrl(ctrl_sim_en_c); |
346,7 → 356,7
tx_buffer.clear <= not ctrl(ctrl_en_c); |
|
-- write access -- |
tx_buffer.we <= '1' when (wr_en = '1') and (addr = uart_id_rtx_addr_c) else '0'; |
tx_buffer.we <= '1' when (wren = '1') and (addr = uart_id_rtx_addr_c) else '0'; |
tx_buffer.wdata <= data_i; |
|
|
356,8 → 366,9
begin |
if rising_edge(clk_i) then |
-- defaults -- |
uart_txd_o <= '1'; -- keep TX line idle (=high) if waiting for permission to start sending (->CTS) |
tx_buffer.re <= '0'; |
uart_txd_o <= '1'; -- keep TX line idle (=high) if waiting for permission to start sending (->CTS) |
tx_buffer.re <= '0'; |
tx_engine.done <= '0'; |
|
-- FSM -- |
if (ctrl(ctrl_en_c) = '0') then -- disabled |
408,6 → 419,7
end if; |
uart_txd_o <= tx_engine.sreg(0); |
if (or_reduce_f(tx_engine.bitcnt) = '0') then -- all bits send? |
tx_engine.done <= '1'; -- sending done |
tx_engine.state <= S_TX_IDLE; |
end if; |
|
436,6 → 448,9
-- input synchronizer -- |
rx_engine.sync <= uart_rxd_i & rx_engine.sync(rx_engine.sync'left downto 1); |
|
-- default -- |
rx_engine.done <= '0'; |
|
-- FSM -- |
if (ctrl(ctrl_en_c) = '0') then -- disabled |
rx_engine.overr <= '0'; |
463,6 → 478,7
end if; |
end if; |
if (or_reduce_f(rx_engine.bitcnt) = '0') then -- all bits received? |
rx_engine.done <= '1'; -- receiving done |
rx_engine.state <= S_RX_IDLE; |
end if; |
|
473,7 → 489,7
end case; |
|
-- overrun flag -- |
if (rd_en = '1') and (addr = uart_id_rtx_addr_c) then -- clear when reading data register |
if (rden = '1') and (addr = uart_id_rtx_addr_c) then -- clear when reading data register |
rx_engine.overr <= '1'; |
elsif (rx_buffer.we = '1') and (rx_buffer.free = '0') then -- write to full FIFO |
rx_engine.overr <= '0'; |
520,7 → 536,7
rx_buffer.wdata(8) <= ctrl(ctrl_pmode1_c) and (xor_reduce_f(rx_engine.sreg(8 downto 0)) xor ctrl(ctrl_pmode0_c)); -- parity error flag |
rx_buffer.wdata(9) <= not rx_engine.sreg(9); -- frame error flag: check stop bit (error if not set) |
rx_buffer.we <= '1' when (rx_engine.bitcnt = "0000") and (rx_engine.state = S_RX_RECEIVE) else '0'; -- RX complete |
rx_buffer.re <= '1' when (rd_en = '1') and (addr = uart_id_rtx_addr_c) else '0'; |
rx_buffer.re <= '1' when (rden = '1') and (addr = uart_id_rtx_addr_c) else '0'; |
|
|
-- Hardware Flow Control ------------------------------------------------------------------ |
538,41 → 554,66
end process flow_control_buffer; |
|
|
-- Interrupts ----------------------------------------------------------------------------- |
-- Interrupt Generator -------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
irq_type: process(ctrl, tx_buffer, rx_buffer) |
begin |
-- TX interrupt -- |
if (UART_TX_FIFO = 1) then |
tx_irq.set <= tx_buffer.free; -- fire IRQ if FIFO is not full |
else |
if (ctrl(ctrl_tx_irq_c) = '1') then |
tx_irq.set <= not tx_buffer.half; -- fire IRQ if FIFO is less than half-full |
else |
tx_irq.set <= tx_buffer.free; -- fire IRQ if FIFO is not full |
end if; |
end if; |
|
-- RX interrupt -- |
if (UART_RX_FIFO = 1) then |
rx_irq.set <= rx_buffer.avail; -- fire IRQ if FIFO is not empty |
else |
if (ctrl(ctrl_rx_irq_c) = '1') then |
rx_irq.set <= rx_buffer.half; -- fire IRQ if FIFO is at least half-full |
else |
rx_irq.set <= rx_buffer.avail; -- fire IRQ is FIFO is not empty |
end if; |
end if; |
end process irq_type; |
|
-- interrupt arbiter -- |
irq_generator: process(clk_i) |
begin |
if rising_edge(clk_i) then |
if (ctrl(ctrl_en_c) = '0') then -- no interrupts when disabled |
irq_txd_o <= '0'; |
irq_rxd_o <= '0'; |
if (ctrl(ctrl_en_c) = '0') then |
rx_irq.pending <= '0'; |
tx_irq.pending <= '0'; |
else |
-- TX interrupt -- |
if (UART_TX_FIFO = 1) then |
irq_txd_o <= tx_buffer.free; -- fire IRQ if FIFO is not full |
else |
if (ctrl(ctrl_tx_irq_c) = '1') then |
irq_txd_o <= not tx_buffer.half; -- fire IRQ if FIFO is less than half-full |
else |
irq_txd_o <= tx_buffer.free; -- fire IRQ if FIFO is not full |
end if; |
-- TX -- |
if (tx_irq.set = '1') and (tx_engine.done = '1') then -- evaluate IRQ condition when TX is done with current sending |
tx_irq.pending <= '1'; |
elsif (tx_irq.clr = '1') then |
tx_irq.pending <= '0'; |
end if; |
|
-- RX interrupt -- |
if (UART_RX_FIFO = 1) then |
irq_rxd_o <= rx_buffer.avail; -- fire IRQ if FIFO is not empty |
else |
if (ctrl(ctrl_rx_irq_c) = '1') then |
irq_rxd_o <= rx_buffer.half; -- fire IRQ if FIFO is at least half-full |
else |
irq_rxd_o <= rx_buffer.avail; -- fire IRQ is FIFO is not empty |
end if; |
-- RX -- |
if (rx_irq.set = '1') and (rx_engine.done = '1') then -- evaluate IRQ condition when RX is done with current receiving |
rx_irq.pending <= '1'; |
elsif (rx_irq.clr = '1') then |
rx_irq.pending <= '0'; |
end if; |
end if; |
end if; |
end process irq_generator; |
|
-- IRQ requests to CPU -- |
irq_txd_o <= tx_irq.pending; |
irq_rxd_o <= rx_irq.pending; |
|
-- IRQ acknowledge -- |
tx_irq.clr <= '1' when (tx_buffer.we = '1') or ((wren = '1') and (addr = uart_id_ctrl_addr_c)) else '0'; -- write to data reg OR write to control reg |
rx_irq.clr <= '1' when (rx_buffer.re = '1') or ((wren = '1') and (addr = uart_id_ctrl_addr_c)) else '0'; -- read from data reg OR write to control reg |
|
|
-- SIMULATION Transmitter ----------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
-- pragma translate_off |
/rtl/core/neorv32_wdt.vhd
140,10 → 140,8
ctrl_reg.mode <= '0'; -- trigger interrupt on WDT overflow |
ctrl_reg.clk_sel <= (others => '1'); -- slowest clock source |
ctrl_reg.lock <= '0'; |
cpu_irq.clr <= '-'; |
elsif rising_edge(clk_i) then |
-- acknowledge interrupt when resetting WDT -- |
cpu_irq.clr <= ctrl_reg.reset; |
if (rstn_sync = '0') then -- internal reset |
ctrl_reg.reset <= '0'; |
ctrl_reg.enforce <= '0'; |
190,6 → 188,7
|
-- action trigger -- |
cpu_irq.set <= ctrl_reg.enable and (wdt_cnt(wdt_cnt'left) or ctrl_reg.enforce) and (not ctrl_reg.mode); -- mode 0: IRQ |
cpu_irq.clr <= ctrl_reg.reset; -- ack IRQ on WDT reset |
hw_rst <= ctrl_reg.enable and (wdt_cnt(wdt_cnt'left) or ctrl_reg.enforce) and ( ctrl_reg.mode); -- mode 1: RESET |
|
|
/rtl/core/neorv32_wishbone.vhd
76,7 → 76,9
lock_i : in std_ulogic; -- exclusive access request |
ack_o : out std_ulogic; -- transfer acknowledge |
err_o : out std_ulogic; -- transfer error |
tmo_o : out std_ulogic; -- transfer timeout |
priv_i : in std_ulogic_vector(01 downto 0); -- current CPU privilege level |
ext_o : out std_ulogic; -- active external access |
-- wishbone interface -- |
wb_tag_o : out std_ulogic_vector(02 downto 0); -- request tag |
wb_adr_o : out std_ulogic_vector(31 downto 0); -- address |
106,18 → 108,20
-- bus arbiter |
type ctrl_state_t is (IDLE, BUSY); |
type ctrl_t is record |
state : ctrl_state_t; |
we : std_ulogic; |
adr : std_ulogic_vector(31 downto 0); |
wdat : std_ulogic_vector(31 downto 0); |
rdat : std_ulogic_vector(31 downto 0); |
sel : std_ulogic_vector(03 downto 0); |
ack : std_ulogic; |
err : std_ulogic; |
timeout : std_ulogic_vector(index_size_f(BUS_TIMEOUT)-1 downto 0); |
src : std_ulogic; |
lock : std_ulogic; |
priv : std_ulogic_vector(01 downto 0); |
state : ctrl_state_t; |
state_ff : ctrl_state_t; |
we : std_ulogic; |
adr : std_ulogic_vector(31 downto 0); |
wdat : std_ulogic_vector(31 downto 0); |
rdat : std_ulogic_vector(31 downto 0); |
sel : std_ulogic_vector(03 downto 0); |
ack : std_ulogic; |
err : std_ulogic; |
tmo : std_ulogic; |
timeout : std_ulogic_vector(index_size_f(BUS_TIMEOUT)-1 downto 0); |
src : std_ulogic; |
lock : std_ulogic; |
priv : std_ulogic_vector(01 downto 0); |
end record; |
signal ctrl : ctrl_t; |
signal stb_int : std_ulogic; |
165,24 → 169,28
bus_arbiter: process(rstn_i, clk_i) |
begin |
if (rstn_i = '0') then |
ctrl.state <= IDLE; |
ctrl.we <= def_rst_val_c; |
ctrl.adr <= (others => def_rst_val_c); |
ctrl.wdat <= (others => def_rst_val_c); |
ctrl.rdat <= (others => def_rst_val_c); |
ctrl.sel <= (others => def_rst_val_c); |
ctrl.timeout <= (others => def_rst_val_c); |
ctrl.ack <= def_rst_val_c; |
ctrl.err <= def_rst_val_c; |
ctrl.src <= def_rst_val_c; |
ctrl.lock <= def_rst_val_c; |
ctrl.priv <= (others => def_rst_val_c); |
ctrl.state <= IDLE; |
ctrl.state_ff <= IDLE; |
ctrl.we <= def_rst_val_c; |
ctrl.adr <= (others => def_rst_val_c); |
ctrl.wdat <= (others => def_rst_val_c); |
ctrl.rdat <= (others => def_rst_val_c); |
ctrl.sel <= (others => def_rst_val_c); |
ctrl.timeout <= (others => def_rst_val_c); |
ctrl.ack <= def_rst_val_c; |
ctrl.err <= def_rst_val_c; |
ctrl.tmo <= def_rst_val_c; |
ctrl.src <= def_rst_val_c; |
ctrl.lock <= def_rst_val_c; |
ctrl.priv <= (others => def_rst_val_c); |
elsif rising_edge(clk_i) then |
-- defaults -- |
ctrl.rdat <= (others => '0'); -- required for internal output gating |
ctrl.ack <= '0'; |
ctrl.err <= '0'; |
ctrl.timeout <= std_ulogic_vector(to_unsigned(BUS_TIMEOUT, index_size_f(BUS_TIMEOUT))); |
ctrl.state_ff <= ctrl.state; |
ctrl.rdat <= (others => '0'); -- required for internal output gating |
ctrl.ack <= '0'; |
ctrl.err <= '0'; |
ctrl.tmo <= '0'; |
ctrl.timeout <= std_ulogic_vector(to_unsigned(BUS_TIMEOUT, index_size_f(BUS_TIMEOUT))); |
|
-- state machine -- |
case ctrl.state is |
210,10 → 218,12
when BUSY => -- transfer in progress |
-- ------------------------------------------------------------ |
ctrl.rdat <= wb_dat_i; |
if (wb_err_i = '1') or -- abnormal bus termination |
((timeout_en_c = true) and (or_reduce_f(ctrl.timeout) = '0')) then -- valid timeout |
if (wb_err_i = '1') then -- abnormal bus termination |
ctrl.err <= '1'; |
ctrl.state <= IDLE; |
elsif (timeout_en_c = true) and (or_reduce_f(ctrl.timeout) = '0') then -- enabled timeout |
ctrl.tmo <= '1'; |
ctrl.state <= IDLE; |
elsif (wb_ack_i = '1') then -- normal bus termination |
ctrl.ack <= '1'; |
ctrl.state <= IDLE; |
236,9 → 246,12
rdata_gated <= wb_dat_i when (ctrl.state = BUSY) else (others => '0'); -- CPU read data gate for "async" RX |
rdata <= ctrl.rdat when (ASYNC_RX = false) else rdata_gated; |
|
ext_o <= '1' when (ctrl.state = BUSY) else '0'; -- active external access |
|
data_o <= rdata when (BIG_ENDIAN = false) else bswap32_f(rdata); -- endianness conversion |
ack_o <= ctrl.ack when (ASYNC_RX = false) else ack_gated; |
err_o <= ctrl.err; |
tmo_o <= ctrl.tmo; |
|
-- wishbone interface -- |
wb_tag_o(0) <= '0' when (ctrl.priv = priv_mode_u_c) else '1'; -- unprivileged access when in user mode |
254,7 → 267,7
wb_stb_o <= stb_int when (PIPE_MODE = true) else cyc_int; |
wb_cyc_o <= cyc_int; |
|
stb_int <= '1' when (ctrl.state = BUSY) else '0'; |
stb_int <= '1' when (ctrl.state = BUSY) and (ctrl.state_ff /= BUSY) else '0'; |
cyc_int <= '1' when (ctrl.state = BUSY) else '0'; |
|
|
/setups/osflow/board_tops/neorv32_iCEBreaker_BoardTop_MinimalBoot.vhd
0,0 → 1,163
-- ################################################################################################# |
-- # << NEORV32 - Example setup for the tinyVision.ai Inc. "UPduino v3" (c) Board >> # |
-- # ********************************************************************************************* # |
-- # BSD 3-Clause License # |
-- # # |
-- # Copyright (c) 2021, Stephan Nolting. All rights reserved. # |
-- # # |
-- # Redistribution and use in source and binary forms, with or without modification, are # |
-- # permitted provided that the following conditions are met: # |
-- # # |
-- # 1. Redistributions of source code must retain the above copyright notice, this list of # |
-- # conditions and the following disclaimer. # |
-- # # |
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of # |
-- # conditions and the following disclaimer in the documentation and/or other materials # |
-- # provided with the distribution. # |
-- # # |
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to # |
-- # endorse or promote products derived from this software without specific prior written # |
-- # permission. # |
-- # # |
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS # |
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # |
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # |
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # |
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # |
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # |
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # |
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # |
-- # OF THE POSSIBILITY OF SUCH DAMAGE. # |
-- # ********************************************************************************************* # |
-- # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
-- ################################################################################################# |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
library iCE40; |
use iCE40.components.all; -- for device primitives and macros |
|
entity neorv32_iCEBreaker_BoardTop_MinimalBoot is |
port ( |
-- UART (uart0) -- |
uart_txd_o : out std_ulogic; |
uart_rxd_i : in std_ulogic; |
-- GPIO -- |
gpio_o : out std_ulogic_vector(3 downto 0); |
-- PWM (to on-board RGB power LED) -- |
pwm_o : out std_logic_vector(2 downto 0) |
); |
end entity; |
|
architecture neorv32_iCEBreaker_BoardTop_MinimalBoot_rtl of neorv32_iCEBreaker_BoardTop_MinimalBoot is |
|
-- configuration -- |
constant f_clock_c : natural := 18000000; -- PLL output clock frequency in Hz |
|
-- On-chip oscillator -- |
signal hf_osc_clk : std_logic; |
|
-- Globals |
signal pll_rstn : std_logic; |
signal pll_clk : std_logic; |
|
-- internal IO connection -- |
signal con_pwm : std_logic_vector(2 downto 0); |
|
begin |
|
-- On-Chip HF Oscillator ------------------------------------------------------------------ |
-- ------------------------------------------------------------------------------------------- |
HSOSC_inst : SB_HFOSC |
generic map ( |
CLKHF_DIV => "0b10" -- 12 MHz |
) |
port map ( |
CLKHFPU => '1', |
CLKHFEN => '1', |
CLKHF => hf_osc_clk |
); |
|
-- System PLL ----------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
-- Settings generated by icepll -i 12 -o 18: |
-- F_PLLIN: 12.000 MHz (given) |
-- F_PLLOUT: 18.000 MHz (requested) |
-- F_PLLOUT: 18.000 MHz (achieved) |
-- FEEDBACK: SIMPLE |
-- F_PFD: 12.000 MHz |
-- F_VCO: 576.000 MHz |
-- DIVR: 0 (4'b0000) |
-- DIVF: 47 (7'b0101111) |
-- DIVQ: 5 (3'b101) |
-- FILTER_RANGE: 1 (3'b001) |
Pll_inst : SB_PLL40_CORE |
generic map ( |
FEEDBACK_PATH => "SIMPLE", |
DIVR => x"0", |
DIVF => 7x"2F", |
DIVQ => 3x"5", |
FILTER_RANGE => 3x"1" |
) |
port map ( |
REFERENCECLK => hf_osc_clk, |
PLLOUTCORE => open, |
PLLOUTGLOBAL => pll_clk, |
EXTFEEDBACK => '0', |
DYNAMICDELAY => x"00", |
LOCK => pll_rstn, |
BYPASS => '0', |
RESETB => '1', |
LATCHINPUTVALUE => '0', |
SDO => open, |
SDI => '0', |
SCLK => '0' |
); |
|
-- The core of the problem ---------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
neorv32_inst: entity work.neorv32_ProcessorTop_MinimalBoot |
generic map ( |
CLOCK_FREQUENCY => f_clock_c -- clock frequency of clk_i in Hz |
) |
port map ( |
-- Global control -- |
clk_i => std_ulogic(pll_clk), |
rstn_i => std_ulogic(pll_rstn), |
|
-- GPIO -- |
gpio_o => gpio_o, |
|
-- primary UART -- |
uart_txd_o => uart_txd_o, -- UART0 send data |
uart_rxd_i => uart_rxd_i, -- UART0 receive data |
uart_rts_o => open, -- hw flow control: UART0.RX ready to receive ("RTR"), low-active, optional |
uart_cts_i => '0', -- hw flow control: UART0.TX allowed to transmit, low-active, optional |
|
-- PWM (to on-board RGB LED) -- |
pwm_o => con_pwm |
); |
|
-- IO Connection -------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
RGB_inst: SB_RGBA_DRV |
generic map ( |
CURRENT_MODE => "0b1", |
RGB0_CURRENT => "0b000011", |
RGB1_CURRENT => "0b000011", |
RGB2_CURRENT => "0b000011" |
) |
port map ( |
CURREN => '1', -- I |
RGBLEDEN => '1', -- I |
RGB0PWM => con_pwm(1), -- I - green - pwm channel 1 |
RGB1PWM => con_pwm(2), -- I - blue - pwm channel 2 |
RGB2PWM => con_pwm(0), -- I - red - pwm channel 0 |
RGB2 => pwm_o(2), -- O - red |
RGB1 => pwm_o(1), -- O - blue |
RGB0 => pwm_o(0) -- O - green |
); |
|
end architecture; |
/setups/osflow/board_tops/neorv32_iCEBreaker_BoardTop_UP5KDemo.vhd
0,0 → 1,205
-- ################################################################################################# |
-- # << NEORV32 - Example setup for the tinyVision.ai Inc. "UPduino v3" (c) Board >> # |
-- # ********************************************************************************************* # |
-- # BSD 3-Clause License # |
-- # # |
-- # Copyright (c) 2021, Stephan Nolting. All rights reserved. # |
-- # # |
-- # Redistribution and use in source and binary forms, with or without modification, are # |
-- # permitted provided that the following conditions are met: # |
-- # # |
-- # 1. Redistributions of source code must retain the above copyright notice, this list of # |
-- # conditions and the following disclaimer. # |
-- # # |
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of # |
-- # conditions and the following disclaimer in the documentation and/or other materials # |
-- # provided with the distribution. # |
-- # # |
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to # |
-- # endorse or promote products derived from this software without specific prior written # |
-- # permission. # |
-- # # |
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS # |
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # |
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # |
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # |
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # |
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # |
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # |
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # |
-- # OF THE POSSIBILITY OF SUCH DAMAGE. # |
-- # ********************************************************************************************* # |
-- # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
-- ################################################################################################# |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
library iCE40; |
use iCE40.components.all; -- for device primitives and macros |
|
entity neorv32_iCEBreaker_BoardTop_UP5KDemo is |
port ( |
user_reset_btn : in std_ulogic; |
-- UART (uart0) -- |
uart_txd_o : out std_ulogic; |
uart_rxd_i : in std_ulogic; |
-- SPI to on-board flash -- |
flash_sck_o : out std_ulogic; |
flash_sdo_o : out std_ulogic; |
flash_sdi_i : in std_ulogic; |
flash_csn_o : out std_ulogic; -- NEORV32.SPI_CS(0) |
-- SPI to IO pins -- |
spi_sck_o : out std_ulogic; |
spi_sdo_o : out std_ulogic; |
spi_sdi_i : in std_ulogic; |
spi_csn_o : out std_ulogic; -- NEORV32.SPI_CS(1) |
-- TWI -- |
twi_sda_io : inout std_logic; |
twi_scl_io : inout std_logic; |
-- GPIO -- |
gpio_i : in std_ulogic_vector(3 downto 0); |
gpio_o : out std_ulogic_vector(3 downto 0); |
-- PWM (to on-board RGB power LED) -- |
pwm_o : out std_ulogic_vector(2 downto 0) |
); |
end entity; |
|
architecture neorv32_iCEBreaker_BoardTop_UP5KDemo_rtl of neorv32_iCEBreaker_BoardTop_UP5KDemo is |
|
-- configuration -- |
constant f_clock_c : natural := 18000000; -- PLL output clock frequency in Hz |
|
-- On-chip oscillator -- |
signal hf_osc_clk : std_logic; |
|
-- Globals |
signal pll_rstn : std_logic; |
signal pll_clk : std_logic; |
|
-- internal IO connection -- |
signal con_pwm : std_ulogic_vector(2 downto 0); |
signal con_spi_sdi : std_ulogic; |
signal con_spi_csn : std_ulogic; |
|
begin |
|
-- On-Chip HF Oscillator ------------------------------------------------------------------ |
-- ------------------------------------------------------------------------------------------- |
HSOSC_inst : SB_HFOSC |
generic map ( |
CLKHF_DIV => "0b10" -- 12 MHz |
) |
port map ( |
CLKHFPU => '1', |
CLKHFEN => '1', |
CLKHF => hf_osc_clk |
); |
|
|
-- System PLL ----------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
-- Settings generated by icepll -i 12 -o 18: |
-- F_PLLIN: 12.000 MHz (given) |
-- F_PLLOUT: 18.000 MHz (requested) |
-- F_PLLOUT: 18.000 MHz (achieved) |
-- FEEDBACK: SIMPLE |
-- F_PFD: 12.000 MHz |
-- F_VCO: 576.000 MHz |
-- DIVR: 0 (4'b0000) |
-- DIVF: 47 (7'b0101111) |
-- DIVQ: 5 (3'b101) |
-- FILTER_RANGE: 1 (3'b001) |
Pll_inst : SB_PLL40_CORE |
generic map ( |
FEEDBACK_PATH => "SIMPLE", |
DIVR => x"0", |
DIVF => 7x"2F", |
DIVQ => 3x"5", |
FILTER_RANGE => 3x"1" |
) |
port map ( |
REFERENCECLK => hf_osc_clk, |
PLLOUTCORE => open, |
PLLOUTGLOBAL => pll_clk, |
EXTFEEDBACK => '0', |
DYNAMICDELAY => x"00", |
LOCK => pll_rstn, |
BYPASS => '0', |
RESETB => user_reset_btn, |
LATCHINPUTVALUE => '0', |
SDO => open, |
SDI => '0', |
SCLK => '0' |
); |
|
-- The core of the problem ---------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
|
neorv32_inst: entity work.neorv32_ProcessorTop_UP5KDemo |
generic map ( |
CLOCK_FREQUENCY => f_clock_c -- clock frequency of clk_i in Hz |
) |
port map ( |
-- Global control -- |
clk_i => std_ulogic(pll_clk), |
rstn_i => std_ulogic(pll_rstn), |
|
-- primary UART -- |
uart_txd_o => uart_txd_o, |
uart_rxd_i => uart_rxd_i, |
uart_rts_o => open, |
uart_cts_i => '0', |
|
-- SPI to on-board flash -- |
flash_sck_o => flash_sck_o, |
flash_sdo_o => flash_sdo_o, |
flash_sdi_i => flash_sdi_i, |
flash_csn_o => flash_csn_o, |
|
-- SPI to IO pins -- |
spi_sck_o => spi_sck_o, |
spi_sdo_o => spi_sdo_o, |
spi_sdi_i => con_spi_sdi, |
spi_csn_o => con_spi_csn, |
|
-- TWI -- |
twi_sda_io => twi_sda_io, |
twi_scl_io => twi_scl_io, |
|
-- GPIO -- |
gpio_i => gpio_i, |
gpio_o => gpio_o, |
|
-- PWM (to on-board RGB power LED) -- |
pwm_o => con_pwm |
); |
|
-- IO Connection -------------------------------------------------------------------------- |
-- ------------------------------------------------------------------------------------------- |
|
-- SPI sdi read-back -- |
spi_csn_o <= con_spi_csn; |
con_spi_sdi <= flash_sdi_i when (con_spi_csn = '0') else spi_sdi_i; |
|
-- RGB -- |
RGB_inst: SB_RGBA_DRV |
generic map ( |
CURRENT_MODE => "0b1", |
RGB0_CURRENT => "0b000001", |
RGB1_CURRENT => "0b000001", |
RGB2_CURRENT => "0b000001" |
) |
port map ( |
CURREN => '1', -- I |
RGBLEDEN => '1', -- I |
RGB0PWM => con_pwm(1), -- I - green - pwm channel 1 |
RGB1PWM => con_pwm(2), -- I - bluee - pwm channel 2 |
RGB2PWM => con_pwm(0), -- I - red - pwm channel 0 |
RGB2 => pwm_o(2), -- O - red |
RGB1 => pwm_o(1), -- O - blue |
RGB0 => pwm_o(0) -- O - green |
); |
|
end architecture; |
/setups/osflow/boards/iCEBreaker.mk
0,0 → 1,4
.PHONY: all |
|
all: bit |
echo "! Built $(IMPL) for $(BOARD)" |
/setups/osflow/boards/index.mk
61,7 → 61,17
|
endif |
|
ifeq ($(BOARD),iCEBreaker) |
$(info Setting constraints and implementation args for BOARD iCEBreaker) |
|
CONSTRAINTS ?= $(PCF_PATH)/$(BOARD).pcf |
PNRFLAGS ?= --up5k --package sg48 --ignore-loops --timing-allow-fail |
IMPL ?= neorv32_$(BOARD)_$(ID) |
|
endif |
|
|
|
ifeq ($(BOARD),OrangeCrab) |
$(info Setting constraints and implementation args for BOARD OrangeCrab) |
|
/setups/osflow/constraints/iCEBreaker.pcf
0,0 → 1,39
## UART (uart0) |
set_io uart_txd_o 9 |
set_io uart_rxd_i 6 |
|
## SPI - on-board flash |
set_io flash_sdo_o 14 |
set_io flash_sck_o 15 |
set_io flash_csn_o 16 |
set_io flash_sdi_i 17 |
|
## SPI - user port |
set_io spi_sdo_o 43 |
set_io spi_sck_o 38 |
set_io spi_csn_o 34 |
set_io spi_sdi_i 31 |
|
## TWI |
set_io twi_sda_io 2 |
set_io twi_scl_io 4 |
|
## GPIO - input |
set_io gpio_i[0] 18 |
set_io gpio_i[1] 19 |
set_io gpio_i[2] 20 |
set_io gpio_i[3] 28 |
|
## GPIO - output |
set_io gpio_o[0] 25 |
set_io gpio_o[1] 26 |
set_io gpio_o[2] 27 |
set_io gpio_o[3] 23 |
|
## RGB power LED |
set_io pwm_o[0] 39 |
set_io pwm_o[1] 40 |
set_io pwm_o[2] 41 |
|
#User Reset Btn |
set_io user_reset_btn 10 |
/setups/osflow/Makefile
10,7 → 10,7
UPduino_REV ?= v3 |
|
#ifndef BOARD |
#$(error BOARD needs to be set to 'Fomu', 'iCESugar', 'UPDuino' or 'OrangeCrab' !) |
#$(error BOARD needs to be set to 'Fomu', 'iCESugar', 'UPDuino', 'iCEBreaker' or 'OrangeCrab' !) |
#endif |
|
run: |
29,71 → 29,100
# Boards |
|
Fomu: |
$(eval BITSTREAM ?= neorv32_$(BOARD)_$(FOMU_REV)_$(DESIGN).bit) |
ifeq ($(DESIGN),Minimal) |
$(eval IMEM_SRC := ../../rtl/core/mem/neorv32_imem.default.vhd) |
else |
$(eval IMEM_SRC := devices/ice40/neorv32_imem.ice40up_spram.vhd) |
endif |
$(eval NEORV32_MEM_SRC ?= ${IMEM_SRC} devices/ice40/neorv32_dmem.ice40up_spram.vhd) |
$(MAKE) \ |
BITSTREAM=neorv32_$(BOARD)_$(FOMU_REV)_$(DESIGN).bit \ |
NEORV32_MEM_SRC="${IMEM_SRC} devices/ice40/neorv32_dmem.ice40up_spram.vhd" \ |
BITSTREAM="$(BITSTREAM)" \ |
NEORV32_MEM_SRC="$(NEORV32_MEM_SRC)" \ |
run |
|
iCESugar: |
$(eval BITSTREAM ?= neorv32_$(BOARD)_$(DESIGN).bit) |
$(eval NEORV32_MEM_SRC ?= devices/ice40/neorv32_imem.ice40up_spram.vhd devices/ice40/neorv32_dmem.ice40up_spram.vhd) |
$(MAKE) \ |
BITSTREAM=neorv32_$(BOARD)_$(DESIGN).bit \ |
NEORV32_MEM_SRC="devices/ice40/neorv32_imem.ice40up_spram.vhd devices/ice40/neorv32_dmem.ice40up_spram.vhd" \ |
BITSTREAM="$(BITSTREAM)" \ |
NEORV32_MEM_SRC="$(NEORV32_MEM_SRC)" \ |
run |
|
UPduino: |
$(eval BITSTREAM ?= neorv32_$(BOARD)_$(UPduino_REV)_$(DESIGN).bit) |
$(eval NEORV32_MEM_SRC ?= devices/ice40/neorv32_imem.ice40up_spram.vhd devices/ice40/neorv32_dmem.ice40up_spram.vhd) |
$(MAKE) \ |
BITSTREAM=neorv32_$(BOARD)_$(UPduino_REV)_$(DESIGN).bit \ |
NEORV32_MEM_SRC="devices/ice40/neorv32_imem.ice40up_spram.vhd devices/ice40/neorv32_dmem.ice40up_spram.vhd" \ |
BITSTREAM="$(BITSTREAM)" \ |
NEORV32_MEM_SRC="$(NEORV32_MEM_SRC)" \ |
run |
|
OrangeCrab: |
$(eval BITSTREAM ?= neorv32_$(BOARD)_$(OrangeCrab_REV)_$(DESIGN).bit) |
$(eval NEORV32_MEM_SRC ?= ../../rtl/core/mem/neorv32_imem.default.vhd ../../rtl/core/mem/neorv32_dmem.default.vhd) |
$(MAKE) \ |
BITSTREAM=neorv32_$(BOARD)_$(OrangeCrab_REV)_$(DESIGN).bit \ |
NEORV32_MEM_SRC="../../rtl/core/mem/neorv32_imem.default.vhd ../../rtl/core/mem/neorv32_dmem.default.vhd" \ |
BITSTREAM="$(BITSTREAM)" \ |
NEORV32_MEM_SRC="$(NEORV32_MEM_SRC)" \ |
run |
|
AlhambraII: |
$(eval BITSTREAM ?= neorv32_$(BOARD)_$(DESIGN).bit) |
$(eval NEORV32_MEM_SRC ?= ../../rtl/core/mem/neorv32_imem.default.vhd ../../rtl/core/mem/neorv32_dmem.default.vhd) |
$(MAKE) \ |
BITSTREAM=neorv32_$(BOARD)_$(DESIGN).bit \ |
NEORV32_MEM_SRC="../../rtl/core/mem/neorv32_imem.default.vhd ../../rtl/core/mem/neorv32_dmem.default.vhd" \ |
BITSTREAM="$(BITSTREAM)" \ |
NEORV32_MEM_SRC="$(NEORV32_MEM_SRC)" \ |
run |
|
ULX3S: |
$(eval BITSTREAM ?= neorv32_$(BOARD)_$(DESIGN).bit) |
$(eval NEORV32_MEM_SRC ?= ../../rtl/core/mem/neorv32_imem.default.vhd ../../rtl/core/mem/neorv32_dmem.default.vhd) |
$(MAKE) \ |
BITSTREAM=neorv32_$(BOARD)_$(DESIGN).bit \ |
NEORV32_MEM_SRC="../../rtl/core/mem/neorv32_imem.default.vhd ../../rtl/core/mem/neorv32_dmem.default.vhd" \ |
BITSTREAM="$(BITSTREAM)" \ |
NEORV32_MEM_SRC="$(NEORV32_MEM_SRC)" \ |
run |
|
iCEBreaker: |
$(eval BITSTREAM ?= neorv32_$(BOARD)_$(DESIGN).bit) |
$(eval NEORV32_MEM_SRC ?= devices/ice40/neorv32_imem.ice40up_spram.vhd devices/ice40/neorv32_dmem.ice40up_spram.vhd) |
$(MAKE) \ |
BITSTREAM="$(BITSTREAM)" \ |
NEORV32_MEM_SRC="$(NEORV32_MEM_SRC)" \ |
run |
|
# Designs |
|
Minimal: |
$(eval DESIGN ?= $@) |
$(eval DESIGN_SRC ?= $(TEMPLATES)/neorv32_ProcessorTop_Minimal*.vhd) |
$(MAKE) \ |
DESIGN=$@ \ |
DESIGN_SRC=$(TEMPLATES)/neorv32_ProcessorTop_Minimal*.vhd \ |
DESIGN="$(DESIGN)" \ |
DESIGN_SRC="$(DESIGN_SRC)" \ |
$(BOARD) |
|
MinimalBoot: |
$(eval DESIGN ?= $@) |
$(eval DESIGN_SRC ?= $(TEMPLATES)/neorv32_ProcessorTop_MinimalBoot.vhd) |
$(MAKE) \ |
DESIGN=$@ \ |
DESIGN_SRC=$(TEMPLATES)/neorv32_ProcessorTop_MinimalBoot.vhd \ |
DESIGN="$(DESIGN)" \ |
DESIGN_SRC="$(DESIGN_SRC)" \ |
$(BOARD) |
|
UP5KDemo: |
$(eval DESIGN ?= $@) |
$(eval DESIGN_SRC ?= $(TEMPLATES)/neorv32_ProcessorTop_UP5KDemo.vhd) |
$(MAKE) \ |
DESIGN=$@ \ |
DESIGN_SRC=$(TEMPLATES)/neorv32_ProcessorTop_UP5KDemo.vhd \ |
DESIGN="$(DESIGN)" \ |
DESIGN_SRC="$(DESIGN_SRC)" \ |
$(BOARD) |
|
MixedLanguage: |
$(eval DESIGN ?= $@) |
$(eval DESIGN_SRC ?= $(TEMPLATES)/neorv32_ProcessorTop_Minimal*.vhd) |
$(eval NEORV32_VERILOG_SRC ?= devices/ice40/sb_ice40_components.v board_tops/neorv32_Fomu_MixedLanguage_ClkGen.v) |
$(MAKE) \ |
DESIGN=$@ \ |
DESIGN_SRC=$(TEMPLATES)/neorv32_ProcessorTop_Minimal*.vhd \ |
NEORV32_VERILOG_SRC='devices/ice40/sb_ice40_components.v board_tops/neorv32_Fomu_MixedLanguage_ClkGen.v' \ |
DESIGN="$(DESIGN)" \ |
DESIGN_SRC="$(DESIGN_SRC)" \ |
NEORV32_VERILOG_SRC="$(NEORV32_VERILOG_SRC)" \ |
$(BOARD) |
|
# Help |
102,3 → 131,4
@echo "Open-Source Synthesis, P&R, Routing and Bitstream Generation" |
@echo "Usage: make BOARD=<fpga board> <board top>" |
@echo "Example: make BOARD=Fomu Minimal" |
|
/setups/osflow/filesets.mk
48,7 → 48,8
# Before including this partial makefile, NEORV32_MEM_SRC needs to be set |
# (containing two VHDL sources: one for IMEM and one for DMEM) |
|
NEORV32_SRC := ${NEORV32_PKG} ${NEORV32_APP_SRC} ${NEORV32_MEM_ENTITIES} ${NEORV32_MEM_SRC} ${NEORV32_CORE_SRC} |
NEORV32_SRC := ${NEORV32_PKG} ${NEORV32_APP_SRC} ${NEORV32_MEM_ENTITIES} ${NEORV32_MEM_SRC} ${NEORV32_MEM_SRC_EXTRA} ${NEORV32_CORE_SRC} ${NEORV32_CORE_SRC_EXTRA} |
NEORV32_VERILOG_ALL := ${NEORV32_VERILOG_SRC} ${NEORV32_VERILOG_SRC_EXTRA} |
|
ICE40_SRC := \ |
devices/ice40/sb_ice40_components.vhd |
/setups/osflow/synthesis.mk
7,10 → 7,10
work-obj08.cf: neorv32-obj08.cf ${DESIGN_SRC} ${BOARD_SRC} |
ghdl -a $(GHDL_FLAGS) --work=work ${DESIGN_SRC} ${BOARD_SRC} |
|
${IMPL}.json: work-obj08.cf $(NEORV32_VERILOG_SRC) |
${IMPL}.json: work-obj08.cf $(NEORV32_VERILOG_ALL) |
$(YOSYS) $(YOSYSFLAGS) \ |
-p \ |
"$(GHDLSYNTH) $(GHDL_FLAGS) --no-formal $(TOP); \ |
synth_${YOSYSSYNTH} \ |
-top $(TOP) $(YOSYSPIPE) \ |
-json $@" $(NEORV32_VERILOG_SRC) 2>&1 | tee yosys-report.txt |
-json $@" $(NEORV32_VERILOG_ALL) 2>&1 | tee yosys-report.txt |
/setups/radiant/UPduino_v3/source/impl_1.xcf
1,6 → 1,6
<?xml version='1.0' encoding='utf-8' ?> |
<!DOCTYPE ispXCF SYSTEM "IspXCF.dtd" > |
<ispXCF version="R2.1"> |
<ispXCF version="R3.0"> |
<Comment></Comment> |
<Chain> |
<Comm>SPI</Comm> |
18,11 → 18,12
<BScanVal>0</BScanVal> |
</Bypass> |
<File>../../impl_1/neorv32_upduino_v3_impl_1.bin</File> |
<FileTime>05/12/21 23:45:06</FileTime> |
<FileTime>11/04/21 21:44:40</FileTime> |
<MemoryType>External SPI Flash Memory (SPI FLASH)</MemoryType> |
<Operation>Erase,Program,Verify</Operation> |
<Option> |
<SVFVendor>JTAG STANDARD</SVFVendor> |
<SVFProcessor>SVF Processor</SVFProcessor> |
<AccessMode>Direct Programming</AccessMode> |
</Option> |
<FPGALoader> |
47,6 → 48,7
<SVFVendor>JTAG STANDARD</SVFVendor> |
<IOState>HighZ</IOState> |
<IOVectorData></IOVectorData> |
<SVFProcessor>SVF Processor</SVFProcessor> |
<AccessMode>Direct Programming</AccessMode> |
</Option> |
</Device> |
81,6 → 83,7
</LocalChainList> |
<Option> |
<SVFVendor>JTAG STANDARD</SVFVendor> |
<SVFProcessor>SVF Processor</SVFProcessor> |
</Option> |
</Device> |
</FPGADevice> |
99,7 → 102,7
</ProjectOptions> |
<CableOptions> |
<CableName>USB2</CableName> |
<PortAdd>FTUSB-0</PortAdd> |
<USBID>UPduino v3.0 Location 0001 Serial </USBID> |
<PortAdd>FTUSB-1</PortAdd> |
<USBID>UPduino v3.0 Location 0002 Serial </USBID> |
</CableOptions> |
</ispXCF> |
/setups/radiant/UPduino_v3/README.md
15,7 → 15,7
|
### Processor Configuration |
|
- [x] CPU: `rv32imac_Zicsr` (reduced CPU `[m]instret` & `[m]cycle` counter width!) |
- [x] CPU: `rv32imacu_Zicsr_Zicntr` (reduced CPU `[m]instret` & `[m]cycle` counter width!) |
- [x] Memory: 64 kB instruction memory (internal IMEM), 64 kB data memory (internal DMEM), 4 kB bootloader ROM |
- [x] Peripherals: `GPIO`, `MTIME`, `UART0`, `SPI`, `TWI`, `PWM`, `WDT`, `TRNG` |
- [x] Clock: 24 MHz from on-chip HF oscillator (via PLL) |
70,22 → 70,21
### FPGA Utilization |
|
``` |
Number of slice registers: 1768 out of 5280 (33%) |
Number of I/O registers: 7 out of 117 (6%) |
Number of LUT4s: 4850 out of 5280 (92%) |
Number of IO sites used: 23 out of 39 (59%) |
Number of DSPs: 0 out of 8 (0%) |
Number of I2Cs: 0 out of 2 (0%) |
Number of slice registers: 1754 out of 5280 (33%) |
Number of I/O registers: 11 out of 117 (9%) |
Number of LUT4s: 4882 out of 5280 (92%) |
Number of DSPs: 0 out of 8 (0%) |
Number of I2Cs: 0 out of 2 (0%) |
Number of High Speed OSCs: 1 out of 1 (100%) |
Number of Low Speed OSCs: 0 out of 1 (0%) |
Number of RGB PWM: 0 out of 1 (0%) |
Number of Low Speed OSCs: 0 out of 1 (0%) |
Number of RGB PWM: 0 out of 1 (0%) |
Number of RGB Drivers: 1 out of 1 (100%) |
Number of SCL FILTERs: 0 out of 2 (0%) |
Number of SCL FILTERs: 0 out of 2 (0%) |
Number of SRAMs: 4 out of 4 (100%) |
Number of WARMBOOTs: 0 out of 1 (0%) |
Number of SPIs: 0 out of 2 (0%) |
Number of EBRs: 15 out of 30 (50%) |
Number of PLLs: 1 out of 1 (100% |
Number of WARMBOOTs: 0 out of 1 (0%) |
Number of SPIs: 0 out of 2 (0%) |
Number of EBRs: 15 out of 30 (50%) |
Number of PLLs: 1 out of 1 (100%) |
``` |
|
### FPGA Setup |
/setups/radiant/UPduino_v3/neorv32_upduino_v3_top.vhd
149,33 → 149,18
HW_THREAD_ID => 0, -- hardware thread id (32-bit) |
INT_BOOTLOADER_EN => true, -- boot configuration: true = boot explicit bootloader; false = boot from int/ext (I)MEM |
|
-- On-Chip Debugger (OCD) -- |
ON_CHIP_DEBUGGER_EN => false, -- implement on-chip debugger? |
|
-- RISC-V CPU Extensions -- |
CPU_EXTENSION_RISCV_A => true, -- implement atomic extension? |
CPU_EXTENSION_RISCV_C => true, -- implement compressed extension? |
CPU_EXTENSION_RISCV_E => false, -- implement embedded RF extension? |
CPU_EXTENSION_RISCV_M => true, -- implement mul/div extension? |
CPU_EXTENSION_RISCV_U => false, -- implement user mode extension? |
CPU_EXTENSION_RISCV_Zfinx => false, -- implement 32-bit floating-point extension (using INT regs!) |
CPU_EXTENSION_RISCV_U => true, -- implement user mode extension? |
CPU_EXTENSION_RISCV_Zicsr => true, -- implement CSR system? |
CPU_EXTENSION_RISCV_Zicntr => true, -- implement base counters? |
CPU_EXTENSION_RISCV_Zifencei => true, -- implement instruction stream sync.? |
|
-- Extension Options -- |
FAST_MUL_EN => false, -- use DSPs for M extension's multiplier |
FAST_SHIFT_EN => false, -- use barrel shifter for shift operations |
CPU_CNT_WIDTH => 34, -- total width of CPU cycle and instret counters (0..64) |
|
-- Physical Memory Protection (PMP) -- |
PMP_NUM_REGIONS => 0, -- number of regions (0..64) |
PMP_MIN_GRANULARITY => 64*1024, -- minimal region granularity in bytes, has to be a power of 2, min 8 bytes |
|
-- Hardware Performance Monitors (HPM) -- |
HPM_NUM_CNTS => 0, -- number of implemented HPM counters (0..29) |
HPM_CNT_WIDTH => 40, -- total size of HPM counters (1..64) |
|
-- Internal Instruction memory -- |
MEM_INT_IMEM_EN => true, -- implement processor-internal instruction memory |
MEM_INT_IMEM_SIZE => 64*1024, -- size of processor-internal instruction memory in bytes |
/setups/README.md
23,6 → 23,7
| :file_folder: [`nexys-a7-test-setup`](https://github.com/stnolting/neorv32/tree/master/setups/vivado/nexys-a7-test-setup) | Xilinx Vivado | [Digilent Nexys A7](https://reference.digilentinc.com/reference/programmable-logic/nexys-a7/start) | Xilinx Artix-7 `XC7A50TCSG324-1` | [AWenzel83](https://github.com/AWenzel83) | |
| :file_folder: [`nexys-a7-test-setup`](https://github.com/stnolting/neorv32/tree/master/setups/vivado/nexys-a7-test-setup) | Xilinx Vivado | [Digilent Nexys 4 DDR](https://reference.digilentinc.com/reference/programmable-logic/nexys-4-ddr/start) | Xilinx Artix-7 `XC7A100TCSG324-1` | [AWenzel83](https://github.com/AWenzel83) | |
| :earth_africa: [custom CRC32 processor module for the nexys-a7 boards (**tutorial**)](https://github.com/motius/neorv32/tree/add-custom-crc32-module) | Xilinx Vivado | [Digilent Nexys A7](https://reference.digilentinc.com/reference/programmable-logic/nexys-a7/start) | Xilinx Artix-7 `XC7A50TCSG324-1` | [motius](https://github.com/motius) ([ikstvn](https://github.com/ikstvn), [turbinenreiter](https://github.com/turbinenreiter)) | |
| :earth_africa: [neorv32-examples](https://github.com/emb4fun/neorv32-examples) | Intel Quartus Prime | Different Terasic boards | Different Intel FPGAs | [emb4fun](https://github.com/emb4fun) | |
|
|
## Setups using Open-Source Toolchains |
35,6 → 36,7
| :file_folder: [`AlhambraII`](https://github.com/stnolting/neorv32/tree/master/setups/osflow) | GHDL, Yosys, nextPNR | [AlhambraII](https://alhambrabits.com/alhambra/) | Lattice iCE40HX4K | [zipotron](https://github.com/zipotron) | |
| :file_folder: [`Orange Crab`](https://github.com/stnolting/neorv32/tree/master/setups/osflow) | GHDL, Yosys, nextPNR | [Orange Crab](https://github.com/gregdavill/OrangeCrab) | Lattice ECP5-25F | [umarcor](https://github.com/umarcor), [jeremyherbert](https://github.com/jeremyherbert) | |
| :file_folder: [`ULX3S`](https://github.com/stnolting/neorv32/tree/master/setups/osflow) | GHDL, Yosys, nextPNR | [ULX3S](https://radiona.org/ulx3s/) | Lattice ECP5 `LFE5U-85F-6BG381C` | [zipotron](https://github.com/zipotron) | |
| :earth_africa: [`ULX3S-SDRAM`](https://github.com/zipotron/neorv32-complex-setups) | GHDL, Yosys, nextPNR | [ULX3S](https://radiona.org/ulx3s/) | Lattice ECP5 `LFE5U-85F-6BG381C` | [zipotron](https://github.com/zipotron) | |
|
:information_source: All setups using open-source toolchains are located in the |
[`osflow`](https://github.com/stnolting/neorv32/tree/master/setups/osflow) folder. |
/sim/simple/neorv32_tb.simple.vhd
351,6 → 351,7
wb_mem_a.sel <= wb_cpu.sel; |
wb_mem_a.tag <= wb_cpu.tag; |
wb_mem_a.cyc <= wb_cpu.cyc; |
wb_mem_a.lock <= wb_cpu.lock; |
|
wb_mem_b.addr <= wb_cpu.addr; |
wb_mem_b.wdata <= wb_cpu.wdata; |
358,6 → 359,7
wb_mem_b.sel <= wb_cpu.sel; |
wb_mem_b.tag <= wb_cpu.tag; |
wb_mem_b.cyc <= wb_cpu.cyc; |
wb_mem_b.lock <= wb_cpu.lock; |
|
wb_mem_c.addr <= wb_cpu.addr; |
wb_mem_c.wdata <= wb_cpu.wdata; |
365,6 → 367,7
wb_mem_c.sel <= wb_cpu.sel; |
wb_mem_c.tag <= wb_cpu.tag; |
wb_mem_c.cyc <= wb_cpu.cyc; |
wb_mem_c.lock <= wb_cpu.lock; |
|
wb_irq.addr <= wb_cpu.addr; |
wb_irq.wdata <= wb_cpu.wdata; |
/sim/neorv32_tb.vhd
482,6 → 482,7
wb_mem_a.sel <= wb_cpu.sel; |
wb_mem_a.tag <= wb_cpu.tag; |
wb_mem_a.cyc <= wb_cpu.cyc; |
wb_mem_a.lock <= wb_cpu.lock; |
|
wb_mem_b.addr <= wb_cpu.addr; |
wb_mem_b.wdata <= wb_cpu.wdata; |
489,6 → 490,7
wb_mem_b.sel <= wb_cpu.sel; |
wb_mem_b.tag <= wb_cpu.tag; |
wb_mem_b.cyc <= wb_cpu.cyc; |
wb_mem_b.lock <= wb_cpu.lock; |
|
wb_mem_c.addr <= wb_cpu.addr; |
wb_mem_c.wdata <= wb_cpu.wdata; |
496,6 → 498,7
wb_mem_c.sel <= wb_cpu.sel; |
wb_mem_c.tag <= wb_cpu.tag; |
wb_mem_c.cyc <= wb_cpu.cyc; |
wb_mem_c.lock <= wb_cpu.lock; |
|
wb_irq.addr <= wb_cpu.addr; |
wb_irq.wdata <= wb_cpu.wdata; |
/sw/bootloader/makefile
34,7 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Relative or absolute path to the NEORV32 home folder |
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../.. |
|
include ../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/hex_viewer/main.c
File deleted
/sw/example/hex_viewer/makefile
File deleted
/sw/example/bitmanip_test/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/blink_led/main.c
93,7 → 93,7
// use ASM version of LED blinking (file: blink_led_in_asm.S) |
#ifdef USE_ASM_VERSION |
|
blink_led_asm((uint32_t)(&GPIO_OUTPUT)); |
blink_led_asm((uint32_t)(&NEORV32_GPIO.OUTPUT_LO)); |
|
// use C version of LED blinking |
#else |
/sw/example/blink_led/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/bus_explorer/main.c
0,0 → 1,436
// ################################################################################################# |
// # << NEORV32 - Bus Explorer - Processor Memory Space Inspector >> # |
// # ********************************************************************************************* # |
// # BSD 3-Clause License # |
// # # |
// # Copyright (c) 2021, Stephan Nolting. All rights reserved. # |
// # # |
// # Redistribution and use in source and binary forms, with or without modification, are # |
// # permitted provided that the following conditions are met: # |
// # # |
// # 1. Redistributions of source code must retain the above copyright notice, this list of # |
// # conditions and the following disclaimer. # |
// # # |
// # 2. Redistributions in binary form must reproduce the above copyright notice, this list of # |
// # conditions and the following disclaimer in the documentation and/or other materials # |
// # provided with the distribution. # |
// # # |
// # 3. Neither the name of the copyright holder nor the names of its contributors may be used to # |
// # endorse or promote products derived from this software without specific prior written # |
// # permission. # |
// # # |
// # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS # |
// # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # |
// # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # |
// # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # |
// # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # |
// # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # |
// # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # |
// # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # |
// # OF THE POSSIBILITY OF SUCH DAMAGE. # |
// # ********************************************************************************************* # |
// # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
// ################################################################################################# |
|
|
/**********************************************************************//** |
* @file bus_explorer/main.c |
* @author Stephan Nolting |
* @brief Interactive memory inspector. |
**************************************************************************/ |
|
#include <neorv32.h> |
#include <string.h> |
|
|
/**********************************************************************//** |
* @name User configuration |
**************************************************************************/ |
/**@{*/ |
/** UART BAUD rate */ |
#define BAUD_RATE 19200 |
/**@}*/ |
|
// Global variables |
char access_size; |
|
// Prototypes |
void read_memory(void); |
void setup_access(void); |
void write_memory(void); |
void atomic_cas(void); |
void dump_memory(void); |
uint32_t hexstr_to_uint(char *buffer, uint8_t length); |
void aux_print_hex_byte(uint8_t byte); |
|
|
/**********************************************************************//** |
* This program provides an interactive console to read/write memory. |
* |
* @note This program requires the UART to be synthesized. |
* |
* @return 0 if execution was successful |
**************************************************************************/ |
int main() { |
|
char buffer[8]; |
int length = 0; |
|
access_size = 0; |
|
// check if UART unit is implemented at all |
if (neorv32_uart0_available() == 0) { |
return 1; |
} |
|
|
// capture all exceptions and give debug info via UART |
neorv32_rte_setup(); |
|
// disable global interrupts |
neorv32_cpu_dint(); |
|
// init UART at default baud rate, no parity bits, ho hw flow control |
neorv32_uart0_setup(BAUD_RATE, PARITY_NONE, FLOW_CONTROL_NONE); |
|
// check available hardware extensions and compare with compiler flags |
neorv32_rte_check_isa(0); // silent = 0 -> show message if isa mismatch |
|
// intro |
neorv32_uart0_printf("\n<<< NEORV32 Bus Explorer >>>\n\n"); |
|
// info |
neorv32_uart0_printf("This program allows to read/write/dump memory space by hand.\n" |
"Type 'help' to see the help menu.\n\n"); |
|
// Main menu |
for (;;) { |
neorv32_uart0_printf("BUS_EXPLORER:> "); |
length = neorv32_uart0_scan(buffer, 8, 1); |
neorv32_uart0_printf("\n"); |
|
if (!length) // nothing to be done |
continue; |
|
// decode input and execute command |
if (!strcmp(buffer, "help")) { |
neorv32_uart0_printf("Available commands:\n" |
" help - show this text\n" |
" setup - configure memory access width (byte,half,word)\n" |
" read - read from address (byte,half,word)\n" |
" write - write to address (byte,half,word)\n" |
" atomic - perform atomic LR/SC access (word-only)\n" |
" dump - dump several bytes/halfs/words from base address\n"); |
} |
|
else if (!strcmp(buffer, "setup")) { |
setup_access(); |
} |
|
else if (!strcmp(buffer, "read")) { |
read_memory(); |
} |
|
else if (!strcmp(buffer, "atomic")) { |
atomic_cas(); |
} |
|
else if (!strcmp(buffer, "write")) { |
write_memory(); |
} |
|
else if (!strcmp(buffer, "dump")) { |
dump_memory(); |
} |
|
else { |
neorv32_uart0_printf("Invalid command. Type 'help' to see all commands.\n"); |
} |
} |
|
return 0; |
} |
|
|
/**********************************************************************//** |
* Configure memory access size |
**************************************************************************/ |
void setup_access(void) { |
|
neorv32_uart0_printf("Select data size (press 'x' to abort):\n" |
" 'b' - byte, 8-bit, unsigned\n" |
" 'h' - half-word, 16-bit, unsigned\n" |
" 'w' - word, 32-bit, unsigned\n"); |
|
while(1) { |
neorv32_uart0_printf("selection: "); |
char tmp = neorv32_uart0_getc(); |
neorv32_uart0_putc(tmp); |
if ((tmp == 'b') || (tmp == 'h') || (tmp == 'w')) { |
access_size = tmp; |
neorv32_uart0_printf("\n"); |
return; |
} |
else if (tmp == 'x') { |
neorv32_uart0_printf("\n"); |
return; |
} |
else { |
neorv32_uart0_printf("Invalid selection!\n"); |
} |
} |
} |
|
|
/**********************************************************************//** |
* Read from memory address |
**************************************************************************/ |
void read_memory(void) { |
|
char terminal_buffer[16]; |
|
if (access_size == 0) { |
neorv32_uart0_printf("Configure data size using 'setup' first.\n"); |
return; |
} |
|
// enter address |
neorv32_uart0_printf("Enter address (8 hex chars): 0x"); |
neorv32_uart0_scan(terminal_buffer, 8+1, 1); // 8 hex chars for address plus '\0' |
register uint32_t mem_address = (uint32_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
|
// perform read access |
neorv32_uart0_printf("\n[0x%x] = ", mem_address); |
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0); |
|
uint8_t mem_data_b = 0; |
uint16_t mem_data_h = 0; |
uint32_t mem_data_w = 0; |
if (access_size == 'b') { mem_data_b = (uint32_t)neorv32_cpu_load_unsigned_byte(mem_address); } |
if (access_size == 'h') { mem_data_h = (uint32_t)neorv32_cpu_load_unsigned_half(mem_address); } |
if (access_size == 'w') { mem_data_w = (uint32_t)neorv32_cpu_load_unsigned_word(mem_address); } |
|
// show memory content if there was no exception |
if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) { |
neorv32_uart0_printf("0x"); |
if (access_size == 'b') { |
aux_print_hex_byte(mem_data_b); |
} |
if (access_size == 'h') { |
aux_print_hex_byte((uint8_t)(mem_data_h >> 8)); |
aux_print_hex_byte((uint8_t)(mem_data_h >> 0)); |
} |
if (access_size == 'w') { |
aux_print_hex_byte((uint8_t)(mem_data_w >> 24)); |
aux_print_hex_byte((uint8_t)(mem_data_w >> 16)); |
aux_print_hex_byte((uint8_t)(mem_data_w >> 8)); |
aux_print_hex_byte((uint8_t)(mem_data_w >> 0)); |
} |
} |
|
neorv32_uart0_printf("\n"); |
} |
|
|
/**********************************************************************//** |
* Write to memory address |
**************************************************************************/ |
void write_memory(void) { |
|
char terminal_buffer[16]; |
|
if (access_size == 0) { |
neorv32_uart0_printf("Configure data size using 'setup' first.\n"); |
return; |
} |
|
// enter address |
neorv32_uart0_printf("Enter address (8 hex chars): 0x"); |
neorv32_uart0_scan(terminal_buffer, 8+1, 1); // 8 hex chars for address plus '\0' |
uint32_t mem_address = (uint32_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
|
// enter data |
uint8_t mem_data_b = 0; |
uint16_t mem_data_h = 0; |
uint32_t mem_data_w = 0; |
if (access_size == 'b') { |
neorv32_uart0_printf("\nEnter data (2 hex chars): 0x"); |
neorv32_uart0_scan(terminal_buffer, 2+1, 1); // 2 hex chars for address plus '\0' |
mem_data_b = (uint8_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
} |
if (access_size == 'h') { |
neorv32_uart0_printf("\nEnter data (4 hex chars): 0x"); |
neorv32_uart0_scan(terminal_buffer, 4+1, 1); // 4 hex chars for address plus '\0' |
mem_data_h = (uint16_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
} |
if (access_size == 'w') { |
neorv32_uart0_printf("\nEnter data (8 hex chars): 0x"); |
neorv32_uart0_scan(terminal_buffer, 8+1, 1); // 8 hex chars for address plus '\0' |
mem_data_w = (uint32_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
} |
|
// perform write access |
if (access_size == 'b') { neorv32_cpu_store_unsigned_byte(mem_address, mem_data_b); } |
if (access_size == 'h') { neorv32_cpu_store_unsigned_half(mem_address, mem_data_h); } |
if (access_size == 'w') { neorv32_cpu_store_unsigned_word(mem_address, mem_data_w); } |
|
neorv32_uart0_printf("\n"); |
} |
|
|
/**********************************************************************//** |
* Perform atomic compare-and-swap operation, always 32-bit |
**************************************************************************/ |
void atomic_cas(void) { |
|
char terminal_buffer[16]; |
uint32_t mem_address, rdata, wdata, status; |
|
if ((neorv32_cpu_csr_read(CSR_MISA) & (1<<CSR_MISA_A)) != 0) { |
|
// enter memory address |
neorv32_uart0_printf("Enter memory address (8 hex chars): 0x"); |
neorv32_uart0_scan(terminal_buffer, 8+1, 1); // 8 hex chars for address plus '\0' |
mem_address = (uint32_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
|
// enter desired value |
neorv32_uart0_printf("\nEnter new value @0x%x (8 hex chars): 0x", mem_address); |
neorv32_uart0_scan(terminal_buffer, 8+1, 1); // 8 hex chars for address plus '\0' |
wdata = (uint32_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
|
rdata = neorv32_cpu_load_reservate_word(mem_address); // make reservation |
status = neorv32_cpu_store_conditional(mem_address, wdata); |
|
// status |
neorv32_uart0_printf("\nOld data: 0x%x\n", rdata); |
if (status == 0) { |
neorv32_uart0_printf("Atomic access successful!\n"); |
neorv32_uart0_printf("New data: 0x%x\n", neorv32_cpu_load_unsigned_word(mem_address)); |
} |
else { |
neorv32_uart0_printf("Atomic access failed!\n"); |
} |
} |
else { |
neorv32_uart0_printf("Atomic operations not implemented/enabled!\n"); |
} |
} |
|
|
/**********************************************************************//** |
* Read several bytes/halfs/word from memory base address |
**************************************************************************/ |
void dump_memory(void) { |
|
char terminal_buffer[16]; |
|
if (access_size == 0) { |
neorv32_uart0_printf("Configure data size using 'setup' first.\n"); |
return; |
} |
|
// enter base address |
neorv32_uart0_printf("Enter base address (8 hex chars): 0x"); |
neorv32_uart0_scan(terminal_buffer, 8+1, 1); // 8 hex chars for address plus '\0' |
uint32_t mem_address = (uint32_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
|
neorv32_uart0_printf("\nPress key to start dumping. Press any key to abort.\n"); |
|
neorv32_uart0_getc(); // wait for key |
|
// perform read accesses |
while(neorv32_uart0_char_received() == 0) { |
|
neorv32_uart0_printf("[0x%x] = ", mem_address); |
|
neorv32_cpu_csr_write(CSR_MCAUSE, 0); |
|
uint8_t mem_data_b = 0; |
uint16_t mem_data_h = 0; |
uint32_t mem_data_w = 0; |
if (access_size == 'b') { mem_data_b = (uint32_t)neorv32_cpu_load_unsigned_byte(mem_address); } |
if (access_size == 'h') { mem_data_h = (uint32_t)neorv32_cpu_load_unsigned_half(mem_address); } |
if (access_size == 'w') { mem_data_w = (uint32_t)neorv32_cpu_load_unsigned_word(mem_address); } |
|
// show memory content if there was no exception |
if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) { |
neorv32_uart0_printf("0x"); |
if (access_size == 'b') { |
aux_print_hex_byte(mem_data_b); |
} |
if (access_size == 'h') { |
aux_print_hex_byte((uint8_t)(mem_data_h >> 8)); |
aux_print_hex_byte((uint8_t)(mem_data_h >> 0)); |
} |
if (access_size == 'w') { |
aux_print_hex_byte((uint8_t)(mem_data_w >> 24)); |
aux_print_hex_byte((uint8_t)(mem_data_w >> 16)); |
aux_print_hex_byte((uint8_t)(mem_data_w >> 8)); |
aux_print_hex_byte((uint8_t)(mem_data_w >> 0)); |
} |
neorv32_uart0_printf("\n"); |
} |
else { |
break; |
} |
|
if (access_size == 'b') { |
mem_address += 1; |
} |
else if (access_size == 'h') { |
mem_address += 2; |
} |
else if (access_size == 'w') { |
mem_address += 4; |
} |
|
} |
neorv32_uart0_char_received_get(); // clear UART rx buffer |
neorv32_uart0_printf("\n"); |
} |
|
|
/**********************************************************************//** |
* Helper function to convert N hex chars string into uint32_T |
* |
* @param[in,out] buffer Pointer to array of chars to convert into number. |
* @param[in,out] length Length of the conversion string. |
* @return Converted number. |
**************************************************************************/ |
uint32_t hexstr_to_uint(char *buffer, uint8_t length) { |
|
uint32_t res = 0, d = 0; |
char c = 0; |
|
while (length--) { |
c = *buffer++; |
|
if ((c >= '0') && (c <= '9')) |
d = (uint32_t)(c - '0'); |
else if ((c >= 'a') && (c <= 'f')) |
d = (uint32_t)((c - 'a') + 10); |
else if ((c >= 'A') && (c <= 'F')) |
d = (uint32_t)((c - 'A') + 10); |
else |
d = 0; |
|
res = res + (d << (length*4)); |
} |
|
return res; |
} |
|
|
/**********************************************************************//** |
* Print HEX byte. |
* |
* @param[in] byte Byte to be printed as 2-cahr hex value. |
**************************************************************************/ |
void aux_print_hex_byte(uint8_t byte) { |
|
static const char symbols[] = "0123456789abcdef"; |
|
neorv32_uart0_putc(symbols[(byte >> 4) & 0x0f]); |
neorv32_uart0_putc(symbols[(byte >> 0) & 0x0f]); |
} |
/sw/example/bus_explorer/makefile
0,0 → 1,40
################################################################################################# |
# << NEORV32 - Application Makefile >> # |
# ********************************************************************************************* # |
# Make sure to add the RISC-V GCC compiler's bin folder to your PATH environment variable. # |
# ********************************************************************************************* # |
# BSD 3-Clause License # |
# # |
# Copyright (c) 2021, Stephan Nolting. All rights reserved. # |
# # |
# Redistribution and use in source and binary forms, with or without modification, are # |
# permitted provided that the following conditions are met: # |
# # |
# 1. Redistributions of source code must retain the above copyright notice, this list of # |
# conditions and the following disclaimer. # |
# # |
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of # |
# conditions and the following disclaimer in the documentation and/or other materials # |
# provided with the distribution. # |
# # |
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to # |
# endorse or promote products derived from this software without specific prior written # |
# permission. # |
# # |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS # |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # |
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # |
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # |
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # |
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # |
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # |
# OF THE POSSIBILITY OF SUCH DAMAGE. # |
# ********************************************************************************************* # |
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/coremark/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/demo_freeRTOS/makefile
138,7 → 138,7
|
|
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
# Include central makefile |
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/demo_gptmr/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/demo_neopixel/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/demo_pwm/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/demo_spi/main.c
0,0 → 1,366
// ################################################################################################# |
// # << NEORV32 - SPI Bus Explorer Demo Program >> # |
// # ********************************************************************************************* # |
// # BSD 3-Clause License # |
// # # |
// # Copyright (c) 2021, Stephan Nolting. All rights reserved. # |
// # # |
// # Redistribution and use in source and binary forms, with or without modification, are # |
// # permitted provided that the following conditions are met: # |
// # # |
// # 1. Redistributions of source code must retain the above copyright notice, this list of # |
// # conditions and the following disclaimer. # |
// # # |
// # 2. Redistributions in binary form must reproduce the above copyright notice, this list of # |
// # conditions and the following disclaimer in the documentation and/or other materials # |
// # provided with the distribution. # |
// # # |
// # 3. Neither the name of the copyright holder nor the names of its contributors may be used to # |
// # endorse or promote products derived from this software without specific prior written # |
// # permission. # |
// # # |
// # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS # |
// # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # |
// # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # |
// # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # |
// # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # |
// # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # |
// # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # |
// # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # |
// # OF THE POSSIBILITY OF SUCH DAMAGE. # |
// # ********************************************************************************************* # |
// # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
// ################################################################################################# |
|
|
/**********************************************************************//** |
* @file demo_spi/main.c |
* @author Stephan Nolting |
* @brief SPI bus explorer (execute SPI transactions by hand). |
**************************************************************************/ |
|
#include <neorv32.h> |
#include <string.h> |
|
|
/**********************************************************************//** |
* @name User configuration |
**************************************************************************/ |
/**@{*/ |
/** UART BAUD rate */ |
#define BAUD_RATE 19200 |
/**@}*/ |
|
|
// Global variables |
uint32_t spi_configured; |
uint32_t spi_size; // data quantity in bytes |
|
// Prototypes |
void spi_cs(uint32_t type); |
void spi_trans(void); |
void spi_setup(void); |
uint32_t hexstr_to_uint(char *buffer, uint8_t length); |
void aux_print_hex_byte(uint8_t byte); |
|
|
/**********************************************************************//** |
* This program provides an interactive console to communicate with SPI devices. |
* |
* @note This program requires the UART and the SPI to be synthesized. |
* |
* @return Irrelevant. |
**************************************************************************/ |
int main() { |
|
char buffer[8]; |
int length = 0; |
|
|
// capture all exceptions and give debug info via UART |
// this is not required, but keeps us safe |
neorv32_rte_setup(); |
|
// init UART0 at default baud rate, no parity bits, ho hw flow control |
neorv32_uart0_setup(BAUD_RATE, PARITY_NONE, FLOW_CONTROL_NONE); |
|
|
// check if UART0 unit is implemented at all |
if (neorv32_uart0_available() == 0) { |
return 1; |
} |
|
// intro |
neorv32_uart0_printf("\n<<< SPI Bus Explorer >>>\n\n"); |
|
// check if SPI unit is implemented at all |
if (neorv32_spi_available() == 0) { |
neorv32_uart0_printf("No SPI unit implemented."); |
return 1; |
} |
|
|
// info |
neorv32_uart0_printf("This program allows to create SPI transfers by hand.\n" |
"Type 'help' to see the help menu.\n\n"); |
|
// disable and reset SPI module |
NEORV32_SPI.CTRL = 0; |
spi_configured = 0; // SPI not configured yet |
spi_size = 0; |
|
|
// Main menu |
for (;;) { |
neorv32_uart0_printf("SPI_EXPLORER:> "); |
length = neorv32_uart0_scan(buffer, 8, 1); |
neorv32_uart0_printf("\n"); |
|
if (!length) // nothing to be done |
continue; |
|
// decode input and execute command |
if (!strcmp(buffer, "help")) { |
neorv32_uart0_printf("Available commands:\n" |
" help - show this text\n" |
" setup - configure SPI module\n" |
" cs-en - enable CS line (set low)\n" |
" cs-dis - disable CS line (set high)\n" |
" trans - execute a transmission (write & read to/from SPI)\n" |
"\n" |
"Configure the SPI module using 'setup'. Enable a certain module using 'cs-en',\n" |
"then transfer data using 'trans' and disable the module again using 'cs-dis'.\n\n"); |
} |
else if (!strcmp(buffer, "setup")) { |
spi_setup(); |
} |
else if (!strcmp(buffer, "cs-en")) { |
spi_cs(1); |
} |
else if (!strcmp(buffer, "cs-dis")) { |
spi_cs(0); |
} |
else if (!strcmp(buffer, "trans")) { |
spi_trans(); |
} |
else { |
neorv32_uart0_printf("Invalid command. Type 'help' to see all commands.\n"); |
} |
} |
|
return 0; |
} |
|
|
/**********************************************************************//** |
* Enable or disable chip-select line |
* |
* @param[in] type 0=disable, 1=enable |
**************************************************************************/ |
void spi_cs(uint32_t type) { |
|
char terminal_buffer[2]; |
uint8_t channel; |
|
if (type) { |
neorv32_uart0_printf("Select chip-select line to enable (set low) [0..7]: "); |
} |
else { |
neorv32_uart0_printf("Select chip-select line to disable (set high) [0..7]: "); |
} |
|
while (1) { |
neorv32_uart0_scan(terminal_buffer, 2, 1); // 1 hex char plus '\0' |
channel = (uint8_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
if (channel > 7) { |
neorv32_uart0_printf("\nInvalid channel selection!\n"); |
return; |
} |
else { |
neorv32_uart0_printf("\n"); |
break; |
} |
} |
|
if (type) { |
neorv32_spi_cs_en(channel); |
} |
else { |
neorv32_spi_cs_dis(channel); |
} |
} |
|
|
/**********************************************************************//** |
* SPI data transfer |
**************************************************************************/ |
void spi_trans(void) { |
|
char terminal_buffer[9]; |
|
if (spi_configured == 0) { |
neorv32_uart0_printf("SPI module not configured yet! Use 'setup' to configure SPI module.\n"); |
return; |
} |
|
neorv32_uart0_printf("Enter TX data (%u hex chars): 0x", spi_size); |
neorv32_uart0_scan(terminal_buffer, spi_size*2+1, 1); |
uint32_t tx_data = (uint32_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
|
uint32_t rx_data = neorv32_spi_trans(tx_data); |
|
if (spi_size == 1) { |
neorv32_uart0_printf("\nTX data: 0x"); |
aux_print_hex_byte((uint8_t)(tx_data)); |
neorv32_uart0_printf("\nRX data: 0x"); |
aux_print_hex_byte((uint8_t)(rx_data)); |
neorv32_uart0_printf("\n"); |
} |
else if (spi_size == 2) { |
neorv32_uart0_printf("\nTX data: 0x"); |
aux_print_hex_byte((uint8_t)(tx_data >> 8)); |
aux_print_hex_byte((uint8_t)(tx_data)); |
neorv32_uart0_printf("\nRX data: 0x"); |
aux_print_hex_byte((uint8_t)(rx_data >> 8)); |
aux_print_hex_byte((uint8_t)(rx_data)); |
neorv32_uart0_printf("\n"); |
} |
else if (spi_size == 3) { |
neorv32_uart0_printf("\nTX data: 0x"); |
aux_print_hex_byte((uint8_t)(tx_data >> 16)); |
aux_print_hex_byte((uint8_t)(tx_data >> 8)); |
aux_print_hex_byte((uint8_t)(tx_data)); |
neorv32_uart0_printf("\nRX data: 0x"); |
aux_print_hex_byte((uint8_t)(rx_data >> 16)); |
aux_print_hex_byte((uint8_t)(rx_data >> 8)); |
aux_print_hex_byte((uint8_t)(rx_data)); |
neorv32_uart0_printf("\n"); |
} |
else { |
neorv32_uart0_printf("\nTX data: 0x%x\n", tx_data); |
neorv32_uart0_printf("RX data: 0x%x\n", rx_data); |
} |
} |
|
|
/**********************************************************************//** |
* Configure SPI module |
**************************************************************************/ |
void spi_setup(void) { |
|
char terminal_buffer[9]; |
uint8_t spi_prsc, clk_phase, clk_pol, data_size; |
uint32_t tmp; |
|
// ---- SPI clock ---- |
|
while (1) { |
neorv32_uart0_printf("Select SPI clock prescaler (0..7): "); |
neorv32_uart0_scan(terminal_buffer, 2, 1); |
tmp = (uint32_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
if (tmp > 8) { |
neorv32_uart0_printf("\nInvalid selection!\n"); |
} |
else { |
spi_prsc = (uint8_t)tmp; |
break; |
} |
} |
|
uint32_t div = 0; |
switch (spi_prsc) { |
case 0: div = 2 * 2; break; |
case 1: div = 2 * 4; break; |
case 2: div = 2 * 8; break; |
case 3: div = 2 * 64; break; |
case 4: div = 2 * 128; break; |
case 5: div = 2 * 1024; break; |
case 6: div = 2 * 2048; break; |
case 7: div = 2 * 4096; break; |
default: div = 0; break; |
} |
uint32_t clock = NEORV32_SYSINFO.CLK / div; |
neorv32_uart0_printf("\n+ New SPI clock speed = %u Hz\n", clock); |
|
// ---- SPI clock mode ---- |
|
while (1) { |
neorv32_uart0_printf("Select SPI clock mode (0..3): "); |
neorv32_uart0_scan(terminal_buffer, 2, 1); |
tmp = (uint32_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
if (tmp > 4) { |
neorv32_uart0_printf("\nInvalid selection!\n"); |
} |
else { |
clk_pol = (uint8_t)((tmp >> 1) & 1); |
clk_phase = (uint8_t)(tmp & 1); |
break; |
} |
} |
neorv32_uart0_printf("\n+ New SPI clock mode = %u\n", tmp); |
|
// ---- SPI transfer data quantity ---- |
|
while (1) { |
neorv32_uart0_printf("Select SPI data transfer size in bytes (1,2,3,4): "); |
neorv32_uart0_scan(terminal_buffer, 2, 1); |
tmp = (uint32_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
if ( (tmp < 1) || (tmp > 4)) { |
neorv32_uart0_printf("\nInvalid selection!\n"); |
} |
else { |
data_size = (uint8_t)(tmp - 1); |
break; |
} |
} |
neorv32_uart0_printf("\n+ New SPI data size = %u-byte(s)\n\n", tmp); |
|
neorv32_spi_setup(spi_prsc, clk_phase, clk_pol, data_size); |
spi_configured = 1; // SPI is configured now |
spi_size = tmp; |
} |
|
|
/**********************************************************************//** |
* Helper function to convert N hex chars string into uint32_T |
* |
* @param[in,out] buffer Pointer to array of chars to convert into number. |
* @param[in,out] length Length of the conversion string. |
* @return Converted number. |
**************************************************************************/ |
uint32_t hexstr_to_uint(char *buffer, uint8_t length) { |
|
uint32_t res = 0, d = 0; |
char c = 0; |
|
while (length--) { |
c = *buffer++; |
|
if ((c >= '0') && (c <= '9')) |
d = (uint32_t)(c - '0'); |
else if ((c >= 'a') && (c <= 'f')) |
d = (uint32_t)((c - 'a') + 10); |
else if ((c >= 'A') && (c <= 'F')) |
d = (uint32_t)((c - 'A') + 10); |
else |
d = 0; |
|
res = res + (d << (length*4)); |
} |
|
return res; |
} |
|
|
/**********************************************************************//** |
* Print HEX byte. |
* |
* @param[in] byte Byte to be printed as 2-cahr hex value. |
**************************************************************************/ |
void aux_print_hex_byte(uint8_t byte) { |
|
static const char symbols[] = "0123456789abcdef"; |
|
neorv32_uart0_putc(symbols[(byte >> 4) & 0x0f]); |
neorv32_uart0_putc(symbols[(byte >> 0) & 0x0f]); |
} |
/sw/example/demo_spi/makefile
0,0 → 1,40
################################################################################################# |
# << NEORV32 - Application Makefile >> # |
# ********************************************************************************************* # |
# Make sure to add the RISC-V GCC compiler's bin folder to your PATH environment variable. # |
# ********************************************************************************************* # |
# BSD 3-Clause License # |
# # |
# Copyright (c) 2021, Stephan Nolting. All rights reserved. # |
# # |
# Redistribution and use in source and binary forms, with or without modification, are # |
# permitted provided that the following conditions are met: # |
# # |
# 1. Redistributions of source code must retain the above copyright notice, this list of # |
# conditions and the following disclaimer. # |
# # |
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of # |
# conditions and the following disclaimer in the documentation and/or other materials # |
# provided with the distribution. # |
# # |
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to # |
# endorse or promote products derived from this software without specific prior written # |
# permission. # |
# # |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS # |
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # |
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # |
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # |
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE # |
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED # |
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # |
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # |
# OF THE POSSIBILITY OF SUCH DAMAGE. # |
# ********************************************************************************************* # |
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/demo_trng/main.c
82,7 → 82,7
neorv32_rte_check_isa(0); // silent = 0 -> show message if isa mismatch |
|
// intro |
neorv32_uart0_printf("\n--- TRNG Demo ---\n\n"); |
neorv32_uart0_printf("\n<<< NEORV32 TRNG Demo >>>\n"); |
|
// check if TRNG unit is implemented at all |
if (neorv32_trng_available() == 0) { |
92,13 → 92,14
|
// enable TRNG |
neorv32_trng_enable(); |
neorv32_cpu_delay_ms(100); // TRNG "warm up" |
|
while(1) { |
|
// main menu |
neorv32_uart0_printf("\nCommands:\n" |
" n: Print 8-bit random numbers (abort by pressing any key)\n" |
" h: Generate and print histogram\n"); |
" n: Print 8-bit random numbers (abort by pressing any key)\n" |
" h: Generate and print histogram\n"); |
|
neorv32_uart0_printf("CMD:> "); |
char cmd = neorv32_uart0_getc(); |
/sw/example/demo_trng/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/demo_twi/main.c
57,6 → 57,7
void set_speed(void); |
void send_twi(void); |
uint32_t hexstr_to_uint(char *buffer, uint8_t length); |
void print_hex_byte(uint8_t data); |
|
|
/**********************************************************************//** |
104,8 → 105,8
neorv32_uart0_printf("This program allows to create TWI transfers by hand.\n" |
"Type 'help' to see the help menu.\n\n"); |
|
// configure TWI, second slowest clock, no clock-stretching |
neorv32_twi_setup(CLK_PRSC_2048, 0); |
// configure TWI, second slowest clock |
neorv32_twi_setup(CLK_PRSC_2048); |
|
// no active bus session yet |
bus_claimed = 0; |
219,7 → 220,9
neorv32_twi_generate_stop(); |
|
if (twi_ack == 0) { |
neorv32_uart0_printf("+ Found device at write-address 0x%x\n", (uint32_t)(2*i)); |
neorv32_uart0_printf(" + Found device at write-address 0x"); |
print_hex_byte(2*i); |
neorv32_uart0_printf("\n"); |
num_devices++; |
} |
} |
242,8 → 245,9
neorv32_uart0_scan(terminal_buffer, 3, 1); // 2 hex chars for address plus '\0' |
uint8_t tmp = (uint8_t)hexstr_to_uint(terminal_buffer, strlen(terminal_buffer)); |
uint8_t res = neorv32_twi_trans(tmp); |
neorv32_uart0_printf("\nRX data: 0x%x\n", (uint32_t)neorv32_twi_get_data()); |
neorv32_uart0_printf("Response: "); |
neorv32_uart0_printf("\n RX data: 0x"); |
print_hex_byte((uint8_t)neorv32_twi_get_data()); |
neorv32_uart0_printf("\n Response: "); |
if (res == 0) |
neorv32_uart0_printf("ACK\n"); |
else |
253,7 → 257,7
|
|
/**********************************************************************//** |
* Helper function to convert N hex chars string into uint32_T |
* Helper function to convert N hex chars string into uint32_t |
* |
* @param[in,out] buffer Pointer to array of chars to convert into number. |
* @param[in,out] length Length of the conversion string. |
280,4 → 284,19
} |
|
return res; |
} |
} |
|
|
/**********************************************************************//** |
* Print byte as hex chars via UART0. |
* |
* @param data 8-bit data to be printed as two hex chars. |
**************************************************************************/ |
void print_hex_byte(uint8_t data) { |
|
static const char symbols[] = "0123456789abcdef"; |
|
neorv32_uart0_putc(symbols[(data >> 4) & 15]); |
neorv32_uart0_putc(symbols[(data >> 0) & 15]); |
} |
|
/sw/example/demo_twi/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/demo_wdt/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/demo_xirq/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/dhrystone/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/floating_point_test/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/game_of_life/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/hello_world/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/example/processor_check/main.c
483,26 → 483,26
} |
|
|
// ---------------------------------------------------------- |
// No "real" CSR write access (because rs1 = r0) |
// ---------------------------------------------------------- |
neorv32_cpu_csr_write(CSR_MCAUSE, 0); |
PRINT_STANDARD("[%i] Read-only CSR 'no-write' (rs1=0) access: ", cnt_test); |
//// ---------------------------------------------------------- |
//// No "real" CSR write access (because rs1 = r0) |
//// ---------------------------------------------------------- |
//neorv32_cpu_csr_write(CSR_MCAUSE, 0); |
//PRINT_STANDARD("[%i] Read-only CSR 'no-write' (rs1=0) access: ", cnt_test); |
// |
//cnt_test++; |
// |
//// time CSR is read-only, but no actual write is performed because rs1=r0 |
//// -> should cause no exception |
//asm volatile("csrrs zero, time, zero"); |
// |
//if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) { |
// test_ok(); |
//} |
//else { |
// test_fail(); |
//} |
|
cnt_test++; |
|
// time CSR is read-only, but no actual write is performed because rs1=r0 |
// -> should cause no exception |
asm volatile("csrrs zero, time, zero"); |
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == 0) { |
test_ok(); |
} |
else { |
test_fail(); |
} |
|
|
// ---------------------------------------------------------- |
// Unaligned instruction address |
// ---------------------------------------------------------- |
1124,10 → 1124,11
// configure SPI |
neorv32_spi_setup(CLK_PRSC_2, 0, 0, 0); |
|
// enable fast interrupt |
neorv32_cpu_irq_enable(CSR_MIE_FIRQ6E); |
|
// trigger SPI IRQ |
neorv32_spi_trans(0); |
// enable fast interrupt |
neorv32_cpu_irq_enable(CSR_MIE_FIRQ6E); |
while(neorv32_spi_busy()); // wait for current transfer to finish |
|
// wait some time for the IRQ to arrive the CPU |
1155,14 → 1156,14
|
cnt_test++; |
|
// configure TWI, fastest clock, no peripheral clock stretching |
neorv32_twi_setup(CLK_PRSC_2, 0); |
// configure TWI, fastest clock |
neorv32_twi_setup(CLK_PRSC_2); |
|
// enable TWI FIRQ |
neorv32_cpu_irq_enable(CSR_MIE_FIRQ7E); |
|
// trigger TWI IRQ |
neorv32_twi_generate_start(); |
neorv32_twi_trans(0); |
neorv32_twi_generate_stop(); |
neorv32_cpu_irq_enable(CSR_MIE_FIRQ7E); |
|
// wait some time for the IRQ to arrive the CPU |
asm volatile("nop"); |
1224,9 → 1225,37
// ---------------------------------------------------------- |
// Fast interrupt channel 9 (NEOLED) |
// ---------------------------------------------------------- |
PRINT_STANDARD("[%i] FIRQ9 (NEOLED): skipped\n", cnt_test); |
if (neorv32_neoled_available()) { |
neorv32_cpu_csr_write(CSR_MCAUSE, 0); |
PRINT_STANDARD("[%i] FIRQ9 (NEOLED): ", cnt_test); |
|
cnt_test++; |
|
// enable fast interrupt |
neorv32_cpu_irq_enable(CSR_MIE_FIRQ9E); |
|
// configure NEOLED |
neorv32_neoled_setup(CLK_PRSC_2, 0, 0, 0); |
|
// send dummy data |
neorv32_neoled_write_nonblocking(0); |
|
// wait some time for the IRQ to arrive the CPU |
asm volatile("nop"); |
neorv32_cpu_irq_disable(CSR_MIE_FIRQ9E); |
|
if (neorv32_cpu_csr_read(CSR_MCAUSE) == TRAP_CODE_FIRQ_9) { |
test_ok(); |
} |
else { |
test_fail(); |
} |
|
// no more NEOLED interrupts |
neorv32_neoled_disable(); |
} |
|
|
// ---------------------------------------------------------- |
// Fast interrupt channel 10 & 11 (SLINK) |
// ---------------------------------------------------------- |
1236,13 → 1265,13
|
cnt_test++; |
|
// enable SLINK |
neorv32_slink_enable(); |
|
// configure SLINK IRQs |
neorv32_slink_tx_irq_config(0, SLINK_IRQ_ENABLE, SLINK_IRQ_TX_NOT_FULL); |
neorv32_slink_rx_irq_config(0, SLINK_IRQ_ENABLE, SLINK_IRQ_RX_NOT_EMPTY); |
|
// enable SLINK |
neorv32_slink_enable(); |
|
// enable SLINK FIRQs |
neorv32_cpu_irq_enable(CSR_MIE_FIRQ10E); |
neorv32_cpu_irq_enable(CSR_MIE_FIRQ11E); |
1306,8 → 1335,8
// enable GPTMR FIRQ |
neorv32_cpu_irq_enable(CSR_MIE_FIRQ12E); |
|
// configure timer IRQ for one-shot mode after 2*4 clock cycles |
neorv32_gptmr_setup(CLK_PRSC_2, 0, 4); |
// configure timer IRQ for one-shot mode after 2*3 clock cycles |
neorv32_gptmr_setup(CLK_PRSC_2, 0, 3); |
|
// wait some time for the IRQ to arrive the CPU |
asm volatile("nop"); |
/sw/example/processor_check/makefile
34,6 → 34,7
# The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting # |
################################################################################################# |
|
# Modify this variable to fit your NEORV32 setup (neorv32 home folder) |
NEORV32_HOME ?= ../../.. |
|
include ../../common/common.mk |
include $(NEORV32_HOME)/sw/common/common.mk |
/sw/lib/include/neorv32.h
740,13 → 740,13
enum NEORV32_SLINK_IRQ_enum { |
SLINK_IRQ_RX_EN_LSB = 0, /**< SLINK IRQ configuration register( 0) (r/w): RX IRQ enable LSB (link 0) (#NEORV32_SLINK_IRQ_EN_enum) */ |
SLINK_IRQ_RX_EN_MSB = 7, /**< SLINK IRQ configuration register( 7) (r/w): RX IRQ enable MSB (link 7) (#NEORV32_SLINK_IRQ_EN_enum) */ |
SLINK_IRQ_RX_MODE_LSB = 8, /**< SLINK IRQ configuration register( 8) (r/w): RX IRQ mode LSB (link 0) */ |
SLINK_IRQ_RX_MODE_MSB = 15, /**< SLINK IRQ configuration register(15) (r/w): RX IRQ mode MSB (link 7) */ |
SLINK_IRQ_RX_MODE_LSB = 8, /**< SLINK IRQ configuration register( 8) (r/w): RX IRQ mode LSB (link 0) (#NEORV32_SLINK_IRQ_RX_TYPE_enum) */ |
SLINK_IRQ_RX_MODE_MSB = 15, /**< SLINK IRQ configuration register(15) (r/w): RX IRQ mode MSB (link 7) (#NEORV32_SLINK_IRQ_RX_TYPE_enum) */ |
|
SLINK_IRQ_TX_EN_LSB = 16, /**< SLINK IRQ configuration register(16) (r/w): TX IRQ enable LSB (link 0) (#NEORV32_SLINK_IRQ_EN_enum) */ |
SLINK_IRQ_TX_EN_MSB = 23, /**< SLINK IRQ configuration register(23) (r/w): TX IRQ enable MSB (link 7) (#NEORV32_SLINK_IRQ_EN_enum) */ |
SLINK_IRQ_TX_MODE_LSB = 24, /**< SLINK IRQ configuration register(24) (r/w): TX IRQ mode LSB (link 0) */ |
SLINK_IRQ_TX_MODE_MSB = 31 /**< SLINK IRQ configuration register(31) (r/w): TX IRQ mode MSB (link 7) */ |
SLINK_IRQ_TX_MODE_LSB = 24, /**< SLINK IRQ configuration register(24) (r/w): TX IRQ mode LSB (link 0) (#NEORV32_SLINK_IRQ_TX_TYPE_enum) */ |
SLINK_IRQ_TX_MODE_MSB = 31 /**< SLINK IRQ configuration register(31) (r/w): TX IRQ mode MSB (link 7) (#NEORV32_SLINK_IRQ_TX_TYPE_enum) */ |
}; |
|
/** SLINK interrupt configuration enable (per link) */ |
757,13 → 757,13
|
/** SLINK RX interrupt configuration type (per link) */ |
enum NEORV32_SLINK_IRQ_RX_TYPE_enum { |
SLINK_IRQ_RX_FIFO_HALF = 0, /**< '0': RX FIFO is at least half-full */ |
SLINK_IRQ_RX_FIFO_HALF = 0, /**< '0': RX FIFO fill-level rises above half-full */ |
SLINK_IRQ_RX_NOT_EMPTY = 1 /**< '1': RX FIFO is not empty */ |
}; |
|
/** SLINK TX interrupt configuration type (per link) */ |
enum NEORV32_SLINK_IRQ_TX_TYPE_enum { |
SLINK_IRQ_TX_FIFO_HALF = 0, /**< '0': TX FIFO is less than half-full */ |
SLINK_IRQ_TX_FIFO_HALF = 0, /**< '0': TX FIFO fill-level falls below half-full */ |
SLINK_IRQ_TX_NOT_FULL = 1 /**< '1': TX FIFO is not FULL */ |
}; |
|
850,8 → 850,7
/** BUSKEEPER control/data register bits */ |
enum NEORV32_BUSKEEPER_CTRL_enum { |
BUSKEEPER_ERR_TYPE = 0, /**< BUSKEEPER control register(0) (r/-): Bus error type: 0=device error, 1=access timeout */ |
BUSKEEPER_ERR_SRC = 1, /**< BUSKEEPER control register(1) (r/-): Bus error source: 0=processor-external, 1=processor-internal */ |
BUSKEEPER_ERR_FLAG = 31 /**< BUSKEEPER control register(31) (r/c): Sticky error flag, clears after read */ |
BUSKEEPER_ERR_FLAG = 31 /**< BUSKEEPER control register(31) (r/c): Sticky error flag, clears after read or write access */ |
}; |
/**@}*/ |
|
1028,17 → 1027,16
|
/** TWI control register bits */ |
enum NEORV32_TWI_CTRL_enum { |
TWI_CTRL_EN = 0, /**< TWI control register(0) (r/w): TWI enable */ |
TWI_CTRL_START = 1, /**< TWI control register(1) (-/w): Generate START condition, auto-clears */ |
TWI_CTRL_STOP = 2, /**< TWI control register(2) (-/w): Generate STOP condition, auto-clears */ |
TWI_CTRL_PRSC0 = 3, /**< TWI control register(3) (r/w): Clock prescaler select bit 0 */ |
TWI_CTRL_PRSC1 = 4, /**< TWI control register(4) (r/w): Clock prescaler select bit 1 */ |
TWI_CTRL_PRSC2 = 5, /**< TWI control register(5) (r/w): Clock prescaler select bit 2 */ |
TWI_CTRL_MACK = 6, /**< TWI control register(6) (r/w): Generate controller ACK for each transmission */ |
TWI_CTRL_CKSTEN = 7, /**< TWI control register(7) (r/w): Enable clock stretching (by peripheral) */ |
TWI_CTRL_EN = 0, /**< TWI control register(0) (r/w): TWI enable */ |
TWI_CTRL_START = 1, /**< TWI control register(1) (-/w): Generate START condition, auto-clears */ |
TWI_CTRL_STOP = 2, /**< TWI control register(2) (-/w): Generate STOP condition, auto-clears */ |
TWI_CTRL_PRSC0 = 3, /**< TWI control register(3) (r/w): Clock prescaler select bit 0 */ |
TWI_CTRL_PRSC1 = 4, /**< TWI control register(4) (r/w): Clock prescaler select bit 1 */ |
TWI_CTRL_PRSC2 = 5, /**< TWI control register(5) (r/w): Clock prescaler select bit 2 */ |
TWI_CTRL_MACK = 6, /**< TWI control register(6) (r/w): Generate ACK by controller for each transmission */ |
|
TWI_CTRL_ACK = 30, /**< TWI control register(30) (r/-): ACK received when set */ |
TWI_CTRL_BUSY = 31 /**< TWI control register(31) (r/-): Transfer in progress, busy flag */ |
TWI_CTRL_ACK = 30, /**< TWI control register(30) (r/-): ACK received when set */ |
TWI_CTRL_BUSY = 31 /**< TWI control register(31) (r/-): Transfer in progress, busy flag */ |
}; |
|
/** WTD receive/transmit data register bits */ |
/sw/lib/include/neorv32_cpu.h
273,8 → 273,6
inline void __attribute__ ((always_inline)) neorv32_cpu_eint(void) { |
|
asm volatile ("csrrsi zero, mstatus, %0" : : "i" (1 << CSR_MSTATUS_MIE)); |
asm volatile ("nop"); |
asm volatile ("nop"); |
} |
|
|
284,8 → 282,6
inline void __attribute__ ((always_inline)) neorv32_cpu_dint(void) { |
|
asm volatile ("csrrci zero, mstatus, %0" : : "i" (1 << CSR_MSTATUS_MIE)); |
asm volatile ("nop"); |
asm volatile ("nop"); |
} |
|
|
/sw/lib/include/neorv32_twi.h
46,7 → 46,7
|
// prototypes |
int neorv32_twi_available(void); |
void neorv32_twi_setup(uint8_t prsc, uint8_t ckst_en); |
void neorv32_twi_setup(uint8_t prsc); |
void neorv32_twi_disable(void); |
void neorv32_twi_enable(void); |
void neorv32_twi_mack_enable(void); |
/sw/lib/source/neorv32_rte.c
48,7 → 48,7
static uint32_t __neorv32_rte_vector_lut[NEORV32_RTE_NUM_TRAPS] __attribute__((unused)); // trap handler vector table |
|
// private functions |
static void __attribute__((__interrupt__)) __neorv32_rte_core(void) __attribute__((aligned(16))); |
static void __attribute__((__interrupt__)) __neorv32_rte_core(void) __attribute__((aligned(4))); |
static void __neorv32_rte_debug_exc_handler(void); |
static void __neorv32_rte_print_true_false(int state); |
static void __neorv32_rte_print_checkbox(int state); |
65,8 → 65,7
void neorv32_rte_setup(void) { |
|
// configure trap handler base address |
uint32_t mtvec_base = (uint32_t)(&__neorv32_rte_core); |
neorv32_cpu_csr_write(CSR_MTVEC, mtvec_base); |
neorv32_cpu_csr_write(CSR_MTVEC, (uint32_t)(&__neorv32_rte_core)); |
|
// install debug handler for all sources |
uint8_t id; |
73,6 → 72,9
for (id = 0; id < (sizeof(__neorv32_rte_vector_lut)/sizeof(__neorv32_rte_vector_lut[0])); id++) { |
neorv32_rte_exception_uninstall(id); // this will configure the debug handler |
} |
|
// clear BUSKEEPER error flags |
NEORV32_BUSKEEPER.CTRL = 0; |
} |
|
|
111,7 → 113,7
|
// id valid? |
if ((id >= RTE_TRAP_I_MISALIGNED) && (id <= CSR_MIE_FIRQ15E)) { |
__neorv32_rte_vector_lut[id] = (uint32_t)(&__neorv32_rte_debug_exc_handler); // use dummy handler in case the exception is accidently triggered |
__neorv32_rte_vector_lut[id] = (uint32_t)(&__neorv32_rte_debug_exc_handler); // use dummy handler in case the exception is accidentally triggered |
return 0; |
} |
return 1; |
126,7 → 128,7
* |
* @warning When using the the RTE, this function is the ONLY function that can use the 'interrupt' attribute! |
**************************************************************************/ |
static void __attribute__((__interrupt__)) __attribute__((aligned(16))) __neorv32_rte_core(void) { |
static void __attribute__((__interrupt__)) __attribute__((aligned(4))) __neorv32_rte_core(void) { |
|
register uint32_t rte_mepc = neorv32_cpu_csr_read(CSR_MEPC); |
neorv32_cpu_csr_write(CSR_MSCRATCH, rte_mepc); // store for later |
133,7 → 135,7
register uint32_t rte_mcause = neorv32_cpu_csr_read(CSR_MCAUSE); |
|
// compute return address |
if (((int32_t)rte_mcause) >= 0) { // modify pc only if exception (MSB cleared) |
if (((int32_t)rte_mcause) >= 0) { // modify pc only if not interrupt (MSB cleared) |
|
// get low half word of faulting instruction |
register uint32_t rte_trap_inst; |
151,7 → 153,7
} |
|
// find according trap handler |
register uint32_t rte_handler = (uint32_t)(&__neorv32_rte_debug_exc_handler); |
register uint32_t rte_handler; |
switch (rte_mcause) { |
case TRAP_CODE_I_MISALIGNED: rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_I_MISALIGNED]; break; |
case TRAP_CODE_I_ACCESS: rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_I_ACCESS]; break; |
182,7 → 184,7
case TRAP_CODE_FIRQ_13: rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_13]; break; |
case TRAP_CODE_FIRQ_14: rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_14]; break; |
case TRAP_CODE_FIRQ_15: rte_handler = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_15]; break; |
default: break; |
default: rte_handler = (uint32_t)(&__neorv32_rte_debug_exc_handler); break; |
} |
|
// execute handler |
730,7 → 732,7
/**********************************************************************//** |
* NEORV32 runtime environment: Check required ISA extensions (via compiler flags) against available ISA extensions (via MISA csr). |
* |
* @param[in] silent Show error message (via neorv32.uart) if isa_sw > isa_hw when != 0. |
* @param[in] silent Show error message (via neorv32.uart) if isa_sw > isa_hw when = 0. |
* @return MISA content according to compiler configuration. |
**************************************************************************/ |
int neorv32_rte_check_isa(int silent) { |
746,7 → 748,7
return 0; |
} |
else { |
if ((silent == 0) || (neorv32_uart0_available() == 0)) { |
if ((silent == 0) && (neorv32_uart0_available() != 0)) { |
neorv32_uart0_printf("\nWARNING! SW_ISA (features required) vs HW_ISA (features available) mismatch!\n" |
"SW_ISA = 0x%x (compiler flags)\n" |
"HW_ISA = 0x%x (misa csr)\n\n", misa_sw, misa_hw); |
/sw/lib/source/neorv32_trng.c
99,20 → 99,15
**************************************************************************/ |
int neorv32_trng_get(uint8_t *data) { |
|
const int retries = 3; |
int i; |
uint32_t ct_reg; |
|
for (i=0; i<retries; i++) { |
ct_reg = NEORV32_TRNG.CTRL; |
ct_reg = NEORV32_TRNG.CTRL; |
|
if ((ct_reg & (1<<TRNG_CTRL_VALID)) == 0) { // output data valid? |
continue; |
} |
|
if (ct_reg & (1<<TRNG_CTRL_VALID)) { // output data valid? |
*data = (uint8_t)(ct_reg >> TRNG_CTRL_DATA_LSB); |
return 0; // valid data |
} |
|
return -1; // no valid data available |
else { |
return -1; |
} |
} |
/sw/lib/source/neorv32_twi.c
65,9 → 65,8
* Enable and configure TWI controller. The TWI control register bits are listed in #NEORV32_TWI_CTRL_enum. |
* |
* @param[in] prsc Clock prescaler select (0..7). See #NEORV32_CLOCK_PRSC_enum. |
* @param[in] ckst_en Enable clock-stretching by peripherals when 1. |
**************************************************************************/ |
void neorv32_twi_setup(uint8_t prsc, uint8_t ckst_en) { |
void neorv32_twi_setup(uint8_t prsc) { |
|
NEORV32_TWI.CTRL = 0; // reset |
|
77,10 → 76,7
uint32_t ct_prsc = (uint32_t)(prsc & 0x07); |
ct_prsc = ct_prsc << TWI_CTRL_PRSC0; |
|
uint32_t ct_cksten = (uint32_t)(ckst_en & 0x01); |
ct_cksten = ct_cksten << TWI_CTRL_CKSTEN; |
|
NEORV32_TWI.CTRL = ct_enable | ct_prsc | ct_cksten; |
NEORV32_TWI.CTRL = ct_enable | ct_prsc; |
} |
|
|
/CHANGELOG.md
26,7 → 26,18
|
| Date (*dd.mm.yyyy*) | Version | Comment | |
|:----------:|:-------:|:--------| |
| 03.11.2021 | 1.6.3.1 | :sparkles: added new peripheral module - general purpose 32-bit timer `GPTMR` ([see PR #195](https://github.com/stnolting/neorv32/pull/195)) | |
| 26.11.2021 |[**:rocket:1.6.4**](https://github.com/stnolting/neorv32/releases/tag/v1.6.4) | **New release** | |
| 22.11.2021 | 1.6.3.11 | on-chip debugger: reworked JTAG signal input/output synchronization logic (see [PR #216](https://github.com/stnolting/neorv32/pull/216)) | |
| 22.11.2021 | 1.6.3.10 | reworked **TRNG** (less hardware requirements, improved quality), see [PR #212](https://github.com/stnolting/neorv32/pull/212) and [stnolting/neoTRNG](https://github.com/stnolting/neoTRNG) | |
| 21.11.2021 | 1.6.3.9 | minor rtl edits: configuring an IMEM or DMEM size (`MEM_INT_IMEM_SIZE` / `MEM_INT_DMEM_SIZE` generic) of 0 will now exclude the according memory from synthesis (and also clears the according `NEORV32_SYSINFO.SOC` flags) | |
| 18.11.2021 | 1.6.3.8 | TWI: removed TWI_CTRL_CKSTEN flag (enable clock stretching) from control registers, clock-stretching is now _always_ enabled | |
| 14.11.2021 | 1.6.3.7 | major control unit and ALU logic optimizations, reduced hardware footprint; :lock: closed further illegal instruction encoding holes (system environment instructions, ALU and ALU-immediate instructions, FENCE instructions); [PR #204](https://github.com/stnolting/neorv32/pull/204) | |
| 10.11.2021 | 1.6.3.6 | optimized BUSKEEPER: removed redundant logic - bus keeper now also shows an external interface access timeout (if implemented) as "timeout error"; removed _BUSKEEPER_ERR_SRC_ status flag; :warning: added `err_o` (fault access operation) to the custom functions subsystem (CFS) | |
| 09.11.2021 | 1.6.3.5 | :warning: reworked IRQ trigger logic of SPI, TWI, UART0, UART1, NELOED and SLINK; FIRQs now only trigger **once** when the programmed interrupt condition is met instead of triggering **all the time** (see [PR #202](https://github.com/stnolting/neorv32/pull/202)) | |
| 06.11.2021 | 1.6.3.4 | :bug: fixed bug in **WISHBONE** interface: _pipelined_ Wishbone mode did not clear STB after first transfer cycle | |
| 05.11.2021 | 1.6.3.3 | :bug: fixed bug in general purpose timer **GPTMR** - clock prescaler had no effect, the timer was always counting at full processor clock speed; minor watchdog (WDT) code edits | |
| 04.11.2021 | 1.6.3.2 | added optional _alternative_ IMEM and DMEM architecture-only design files (in `rtl/core/mem`); these are not device-specific ("cyclone 2") as they do not use any FPGA-specific primitives or macros - just a different HDL style for describing memories is used (see [PR #192](https://github.com/stnolting/neorv32/pull/198) and [Issue #197](https://github.com/stnolting/neorv32/issues/197)) | |
| 03.11.2021 | 1.6.3.1 | :sparkles: added new peripheral module - **General Purpose 32-bit Timer `GPTMR`** ([see PR #195](https://github.com/stnolting/neorv32/pull/195)) | |
| 02.11.2021 |[**:rocket:1.6.3**](https://github.com/stnolting/neorv32/releases/tag/v1.6.3) | **New release** | |
| 01.11.2021 | 1.6.2.13 | added new top generics to explicitly control implementation of `Zicntr` (CPU base counters) and `Zihpm` (hardware performance monitors, see [PR #192](https://github.com/stnolting/neorv32/pull/192) | |
| 30.10.2021 | 1.6.2.12 | :sparkles: :lock: added memory-mapped register to BUSKEEPER module - software can now retrieve the actual cause of an instruction / data-load / data-store bus access fault exception (access timeout or device error); see [PR #191](https://github.com/stnolting/neorv32/pull/191) | |
/README.md
13,7 → 13,7
[![license](https://img.shields.io/github/license/stnolting/neorv32?longCache=true&style=flat-square)](https://github.com/stnolting/neorv32/blob/master/LICENSE) |
[![release](https://img.shields.io/github/v/release/stnolting/neorv32?longCache=true&style=flat-square&logo=GitHub)](https://github.com/stnolting/neorv32/releases) |
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5018888.svg)](https://doi.org/10.5281/zenodo.5018888) |
|
\ |
[![datasheet (pdf)](https://img.shields.io/badge/data%20sheet-PDF-ffbd00?longCache=true&style=flat-square&logo=asciidoctor)](https://github.com/stnolting/neorv32/releases/tag/nightly) |
[![datasheet (html)](https://img.shields.io/badge/-HTML-ffbd00?longCache=true&style=flat-square)](https://stnolting.github.io/neorv32) |
[![userguide (pdf)](https://img.shields.io/badge/user%20guide-PDF-ffbd00?longCache=true&style=flat-square&logo=asciidoctor)](https://github.com/stnolting/neorv32/releases/tag/nightly) |
46,7 → 46,7
Therefore, the CPU ensures that all memory access are acknowledged and no invalid/malformed instructions |
are executed. Whenever an unexpected situation occurs, the application code is informed via hardware exceptions. |
|
:information_source: Want to know more? Check out the [project's rationale](https://stnolting.github.io/neorv32/#_rationale). |
:thinking: Want to know more? Check out the [project's rationale](https://stnolting.github.io/neorv32/#_rationale). |
|
:books: For detailed information take a look at the [NEORV32 documentation (online at GitHub-pages)](https://stnolting.github.io/neorv32/). |
The *doxygen*-based documentation of the *software framework* is also available online |
59,16 → 59,13
various FPGA boards and toolchains to get you started. Several example programs (including a FreeRTOS port) to be run on your setup |
can be found in [`sw/example`](https://github.com/stnolting/neorv32/tree/master/sw/example). |
|
:kite: Upstream [**Zephyr RTOS**](https://docs.zephyrproject.org/latest/boards/riscv/neorv32/doc/index.html) support. |
:kite: Supported by upstream [Zephyr OS](https://docs.zephyrproject.org/latest/boards/riscv/neorv32/doc/index.html). |
|
:spiral_notepad: Check out the [project boards](https://github.com/stnolting/neorv32/projects) for a list of current **ideas**, |
**TODOs**, features being **planned** and **work-in-progress**. |
|
:bulb: Feel free to open a [new issue](https://github.com/stnolting/neorv32/issues) or start a |
[new discussion](https://github.com/stnolting/neorv32/discussions) if you have questions, comments, ideas or if something is |
not working as expected. Or have a chat on our [gitter channel](https://gitter.im/neorv32/community). |
|
:rocket: Check out the [quick links below](#Getting-Started) or directly jump to the |
:rocket: Check out the [quick links below](#5-Getting-Started) or directly jump to the |
[*User Guide*](https://stnolting.github.io/neorv32/ug/) to get started |
setting up your NEORV32 setup! |
|
75,7 → 72,7
|
### Project Key Features |
|
- [x] all-in-one: [CPU](#NEORV32-CPU-Features) plus [Processor/SoC](#NEORV32-Processor-Features) plus [Software Framework & Tooling](#Software-Framework-and-Tooling) |
- [x] all-in-one: [CPU](#3-NEORV32-CPU-Features) plus [SoC](#2-NEORV32-Processor-Features) plus [Software Framework & Tooling](#4-Software-Framework-and-Tooling) |
- [x] completely described in behavioral, platform-independent VHDL - no primitives, macros, etc. |
- [x] fully synchronous design, no latches, no gated clocks |
- [x] be as small as possible while being as RISC-V-compliant as possible – but with a reasonable size-performance trade-off: |
92,8 → 89,6
The NEORV32 Processor (top entity: [`rtl/core/neorv32_top.vhd`](https://github.com/stnolting/neorv32/blob/master/rtl/core/neorv32_top.vhd)) |
provides a full-featured SoC build around the NEORV32 CPU. It is highly configurable via generics |
to allow a flexible customization according to your needs. Note that all modules listed below are _optional_. |
In-depth detailed information regarding the processor/SoC can be found in the :books: |
[online documentation - _"NEORV32 Processors (SoC)"_](https://stnolting.github.io/neorv32/#_neorv32_processor_soc). |
|
**Memory** |
|
101,7 → 96,7
[IMEM](https://stnolting.github.io/neorv32/#_instruction_memory_imem)) & |
cache ([iCACHE](https://stnolting.github.io/neorv32/#_processor_internal_instruction_cache_icache)) |
* bootloader ([BOOTLDROM](https://stnolting.github.io/neorv32/#_bootloader_rom_bootrom)) with serial user interface |
* supports boot via UART or from external SPI flash |
* allows booting application code via UART or from external SPI flash |
|
**Timers** |
|
136,24 → 131,19
**Advanced** |
|
* _true random_ number generator ([TRNG](https://stnolting.github.io/neorv32/#_true_random_number_generator_trng)) |
* on-chip debugger ([OCD](https://stnolting.github.io/neorv32/#_on_chip_debugger_ocd)) via JTGA - implementing |
* on-chip debugger ([OCD](https://stnolting.github.io/neorv32/#_on_chip_debugger_ocd)) accessible via JTAG interface - implementing |
the [*Minimal RISC-V Debug Specification Version 0.13.2*](https://github.com/riscv/riscv-debug-spec) |
and compatible with *OpenOCD* and *gdb* |
* bus keeper to monitor processor-internal bus transactions ([BUSKEEPER](https://stnolting.github.io/neorv32/#_internal_bus_monitor_buskeeper)) |
* bus keeper to monitor the CPU's bus transactions ([BUSKEEPER](https://stnolting.github.io/neorv32/#_internal_bus_monitor_buskeeper)) |
|
:information_source: It is recommended to use the processor setup even if you want to **use the CPU in stand-alone mode**. |
Just disable all optional processor-internal modules via the according generics and you will get a "CPU wrapper" that |
provides a minimal CPU environment and an external memory interface (like AXI4). This minimal setup allows to further use |
the default bootloader and software framework. From this base you can start building your own processor system. |
|
[[back to top](#The-NEORV32-RISC-V-Processor)] |
|
|
### FPGA Implementation Results - Processor |
|
The hardware resources used by a specific processor setup is defined by the implemented CPU extensions |
([see below](#FPGA-Implementation-Results---CPU)), the configuration of the peripheral modules and some "glue logic". |
Section [_"FPGA Implementation Results - Processor Modules"_](https://stnolting.github.io/neorv32/#_processor_modules) |
The hardware resources used by a specific processor setup is defined by the implemented CPU extensions, |
the configuration of the peripheral modules and some "glue logic". |
Section [_FPGA Implementation Results - Processor Modules_](https://stnolting.github.io/neorv32/#_processor_modules) |
of the online datasheet shows the resource utilization of each optional processor module to allow an |
estimation of the actual setup's hardware requirements. |
|
167,9 → 157,6
|
## 3. NEORV32 CPU Features |
|
:books: In-depth detailed information regarding the CPU can be found in the |
[online documentation - _"NEORV32 Central Processing Unit"_](https://stnolting.github.io/neorv32/#_neorv32_central_processing_unit_cpu). |
|
The CPU (top entity: [`rtl/core/neorv32_cpu.vhd`](https://github.com/stnolting/neorv32/blob/master/rtl/core/neorv32_cpu.vhd)) |
implements the RISC-V 32-bit `rv32` ISA with optional extensions (see below). It is compatible to subsets of the |
*Unprivileged ISA Specification* [(Version 2.2)](https://github.com/stnolting/neorv32/blob/master/docs/references/riscv-spec.pdf) |
184,7 → 171,10
It also supports **all** standard RISC-V exceptions (instruction/load/store misaligned address & bus access fault, illegal |
instruction, breakpoint, environment calls). |
|
:books: In-depth detailed information regarding the CPU can be found in the |
[_Data Sheet: NEORV32 Central Processing Unit_](https://stnolting.github.io/neorv32/#_neorv32_central_processing_unit_cpu). |
|
|
### Available ISA Extensions |
|
Currently, the following _optional_ RISC-V-compatible ISA extensions are implemented (linked to the according |
228,11 → 218,12
| `rv32i_Zicsr_Zicntr` | 1729 | 813 | 1024 | 0 | 124 MHz | |
| `rv32imac_Zicsr_Zicntr` | 2511 | 1074 | 1024 | 0 | 124 MHz | |
|
:information_source: An incremental list of CPU extension's hardware utilization can found in |
[online documentation - _"FPGA Implementation Results - CPU"_](https://stnolting.github.io/neorv32/#_cpu). |
:information_source: An incremental list of CPU extension's hardware utilization can found in the |
[_Data Sheet: FPGA Implementation Results - CPU_](https://stnolting.github.io/neorv32/#_cpu). |
|
:information_source: The CPU provides options to further reduce the footprint (for example by constraining |
the CPU-internal counters). See the [online data](https://stnolting.github.io/neorv32) sheet for more information. |
:information_source: The CPU (and also the SoC) provides advanced options to optimize for performance, area or energy. |
See [_User Guide: Application-Specific Processor Configuration_](https://stnolting.github.io/neorv32/ug/#_application_specific_processor_configuration) |
for more information. |
|
[[back to top](#The-NEORV32-RISC-V-Processor)] |
|
245,20 → 236,11
available CPU extensions. |
|
The following table shows the performance results (relative CoreMark score and average cycles per instruction) for |
_exemplary_ CPU configuration running 2000 iterations of the [CoreMark CPU benchmark](https://www.eembc.org/coremark). |
_exemplary_ CPU configuration running 2000 iterations of the CoreMark CPU benchmark. |
The source files are available in [`sw/example/coremark`](https://github.com/stnolting/neorv32/blob/master/sw/example/coremark). |
A simple(!) port of the **Dhrystone** benchmark is also available in |
[`sw/example/dhrystone`](https://github.com/stnolting/neorv32/blob/master/sw/example/dhrystone). |
|
:information_source: A _simple_ port of the **Dhrystone** benchmark is also available: |
[`sw/example/dhrystone`](https://github.com/stnolting/neorv32/blob/master/sw/example/dhrystone) |
|
~~~ |
**CoreMark Setup** |
Hardware: 32kB IMEM, 8kB DMEM, no caches, 100MHz clock |
CoreMark: 2000 iterations, MEM_METHOD is MEM_STACK |
Compiler: RISCV32-GCC 10.1.0 (rv32i toolchain) |
Compiler flags: default, see makefile; optimization -O3 |
~~~ |
|
Results generated for hardware version [`1.5.7.10`](https://github.com/stnolting/neorv32/blob/master/CHANGELOG.md). |
|
| CPU Configuration | CoreMark Score | CoreMarks/MHz | Average CPI | |
268,7 → 250,7
| _performance_ (`rv32imc_Zicsr` + perf. options) | 95.23 | **0.9523** | **3.54** | |
|
:information_source: More information regarding the CPU performance can be found in the |
[online documentation - _"CPU Performance"_](https://stnolting.github.io/neorv32/#_cpu_performance). |
[_Data Sheet: CPU Performance_](https://stnolting.github.io/neorv32/#_cpu_performance). |
|
[[back to top](#The-NEORV32-RISC-V-Processor)] |
|
277,7 → 259,7
## 4. Software Framework and Tooling |
|
:books: In-depth detailed information regarding the software framework can be found in the |
[online documentation - _"Software Framework"_](https://stnolting.github.io/neorv32/#_software_framework). |
[_Data Sheet: Software Framework_](https://stnolting.github.io/neorv32/#_software_framework). |
|
* [core libraries](https://github.com/stnolting/neorv32/tree/master/sw/lib) for high-level usage of the provided functions and peripherals |
* application compilation based on GNU makefiles |
284,10 → 266,7
* gcc-based toolchain ([pre-compiled toolchains available](https://github.com/stnolting/riscv-gcc-prebuilt)) |
* bootloader with UART interface console |
* runtime environment for handling traps |
* several [example programs](https://github.com/stnolting/neorv32/tree/master/sw/example) to get started including |
[CoreMark](https://github.com/stnolting/neorv32/tree/master/sw/example/coremark), |
[FreeRTOS](https://github.com/stnolting/neorv32/tree/master/sw/example/demo_freeRTOS) and |
[Conway's Game of Life](https://github.com/stnolting/neorv32/tree/master/sw/example/game_of_life) |
* several [example programs](https://github.com/stnolting/neorv32/tree/master/sw/example) to get started including CoreMark, FreeRTOS and Conway's Game of Life |
* `doxygen`-based documentation, available on :books: [GitHub pages](https://stnolting.github.io/neorv32/sw/files.html) |
* supports implementation using open source tooling (GHDL, Yosys and nextpnr; in the future Verilog-to-Routing); both, software and hardware can be |
developed and debugged with open source tooling |
332,7 → 311,7
* [Application Makefiles](https://stnolting.github.io/neorv32/#_application_makefile) - turning your application into an executable |
* [Bootloader](https://stnolting.github.io/neorv32/#_bootloader) - the build-in NEORV32 bootloader |
|
### :rocket: User Guides (see full [User Guide](https://stnolting.github.io/neorv32/ug/)) |
### :rocket: User Guide |
|
* [Toolchain Setup](https://stnolting.github.io/neorv32/ug/#_toolchain_setup) - install and setup RISC-V gcc |
* [General Hardware Setup](https://stnolting.github.io/neorv32/ug/#_general_hardware_setup) - setup a new NEORV32 EDA project |
357,12 → 336,8
|
## Acknowledgements |
|
**A big shoutout to all [contributors](https://github.com/stnolting/neorv32/graphs/contributors), who helped improving this project! :heart:** |
**A big shout-out to the community and all [contributors](https://github.com/stnolting/neorv32/graphs/contributors), who helped improving this project! :heart:** |
|
[RISC-V](https://riscv.org/) - Instruction Sets Want To Be Free! |
|
Continous integration provided by [:octocat: GitHub Actions](https://github.com/features/actions) and powered by [GHDL](https://github.com/ghdl/ghdl). |
|
-------- |
|
Made with :coffee: in Hanover, Germany :eu: |
Continuous integration provided by [:octocat: GitHub Actions](https://github.com/features/actions) and powered by [GHDL](https://github.com/ghdl/ghdl). |