Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 fix DMA fence flag, ⚠️ rework CPU FIRQs #864

Merged
merged 14 commits into from
Mar 24, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12

| Date | Version | Comment | Link |
|:----:|:-------:|:--------|:----:|
| 24.03.2024 | 1.9.7.5 | :warning: **interrupt system rework**: rework CPU's FIRQ system; `mip` CSR is now read-only ; :bug: fix DMA fence configuration flag | [#864](https://github.com/stnolting/neorv32/pull/864) |
| 23.03.2024 | 1.9.7.4 | :warning: **interrupt system rework**: rework TWI and XIRQ interrupts | [#860](https://github.com/stnolting/neorv32/pull/860) |
| 23.03.2024 | 1.9.7.3 | :warning: **interrupt system rework**: rework ONEWIRE and GPTMR interrupts | [#859](https://github.com/stnolting/neorv32/pull/859) |
| 23.03.2024 | 1.9.7.2 | :warning: **interrupt system rework**: removed WDT and TRNG interrupts; :bug: fix core complex clocking during sleep mode | [#858](https://github.com/stnolting/neorv32/pull/858) |
Expand Down
15 changes: 4 additions & 11 deletions docs/datasheet/cpu.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -871,17 +871,13 @@ even if <<_mstatus>>.mie is cleared.

.Interrupt Signal Requirements - Standard RISC-V Interrupts
[IMPORTANT]
All standard RISC-V interrupt request signals are **high-active**. A request has to stay at high-level
until it is explicitly acknowledged by the CPU software (for example by writing to a specific memory-mapped register).
All interrupt request signals are **high-active**. Once triggered, a interrupt request line should stay high
until it is explicitly acknowledged by a source-specific mechanism (for example by writing to a specific memory-mapped register).

.Interrupt Signal Requirements - NEORV32-Specific Fast Interrupt Requests
[IMPORTANT]
The NEORV32-specific FIRQ request lines are triggered (= becoming pending) by a one-shot high-level.

.Instruction Atomicity
.Instruction Atomicity and Forward-Progress
[NOTE]
All instructions execute as atomic operations - interrupts can only trigger _between_ consecutive instructions.
Even if there is a permanent interrupt request, exactly one instruction from the interrupted program will be executed before
Additionally, if there is a permanent interrupt request, exactly one instruction from the interrupted program will be executed before
another interrupt handler can start. This allows program progress even if there are permanent interrupt requests.


Expand All @@ -903,9 +899,6 @@ As a custom extension, the NEORV32 CPU features 16 fast interrupt request (FIRQ)
entity signals. These interrupts have custom configuration and status flags in the <<_mie>> and <<_mip>> CSRs and also
provide custom trap codes in <<_mcause>>. These FIRQs are reserved for NEORV32 processor-internal usage only.

Once triggered, the according FIRQ bits in <<_mip>> has to be cleared _manually_ by the FIRQ interrupt handler
to clear/acknowledge the fast interrupt request.


:sectnums:
===== NEORV32 Trap Listing
Expand Down
4 changes: 2 additions & 2 deletions docs/datasheet/cpu_csr.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ However, any write-access will be ignored and will not cause an exception to mai
| Address | `0x344`
| Reset value | `0x00000000`
| ISA | `Zicsr`
| Description | The `mip` CSR shows currently _pending_ machine-mode interrupt requests.
| Description | The `mip` CSR shows currently _pending_ machine-mode interrupt requests. Any write access to this register is ignored.
|=======================

.`mip` CSR bits
Expand All @@ -443,7 +443,7 @@ However, any write-access will be ignored and will not cause an exception to mai
| 3 | `CSR_MIP_MSIP` | r/- | **MSIP**: Machine _software_ interrupt pending; _cleared by platform-defined mechanism_
| 7 | `CSR_MIP_MTIP` | r/- | **MTIP**: Machine _timer_ interrupt pending; _cleared by platform-defined mechanism_
| 11 | `CSR_MIP_MEIP` | r/- | **MEIP**: Machine _external_ interrupt pending; _cleared by platform-defined mechanism_
| 31:16 | `CSR_MIP_FIRQ15P` : `CSR_MIP_FIRQ0P` | r/c | **FIRQxP**: Fast interrupt channel 15..0 pending; has to be cleared manually by writing zero; writing `1` has no effect
| 31:16 | `CSR_MIP_FIRQ15P` : `CSR_MIP_FIRQ0P` | r/- | **FIRQxP**: Fast interrupt channel 15..0 pending; _cleared by platform-defined mechanism_
|=======================

.FIRQ Channel Mapping
Expand Down
18 changes: 9 additions & 9 deletions docs/datasheet/soc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -441,18 +441,18 @@ table (the channel number also corresponds to the according FIRQ priority: 0 = h
| Channel | Source | Description
| 0 | - | _reserved_
| 1 | <<_custom_functions_subsystem_cfs,CFS>> | custom functions subsystem (CFS) interrupt (user-defined)
| 2 | <<_primary_universal_asynchronous_receiver_and_transmitter_uart0,UART0>> | UART0 RX interrupt
| 3 | <<_primary_universal_asynchronous_receiver_and_transmitter_uart0,UART0>> | UART0 TX interrupt
| 4 | <<_secondary_universal_asynchronous_receiver_and_transmitter_uart1,UART1>> | UART1 RX interrupt
| 5 | <<_secondary_universal_asynchronous_receiver_and_transmitter_uart1,UART1>> | UART1 TX interrupt
| 6 | <<_serial_peripheral_interface_controller_spi,SPI>> | SPI interrupt
| 7 | <<_two_wire_serial_interface_controller_twi,TWI>> | TWI transmission done interrupt
| 2 | <<_primary_universal_asynchronous_receiver_and_transmitter_uart0,UART0>> | UART0 RX FIFO level interrupt
| 3 | <<_primary_universal_asynchronous_receiver_and_transmitter_uart0,UART0>> | UART0 TX FIFO level interrupt
| 4 | <<_secondary_universal_asynchronous_receiver_and_transmitter_uart1,UART1>> | UART1 RX FIFO level interrupt
| 5 | <<_secondary_universal_asynchronous_receiver_and_transmitter_uart1,UART1>> | UART1 TX FIFO level interrupt
| 6 | <<_serial_peripheral_interface_controller_spi,SPI>> | SPI FIFO level interrupt
| 7 | <<_two_wire_serial_interface_controller_twi,TWI>> | TWI idle interrupt
| 8 | <<_external_interrupt_controller_xirq,XIRQ>> | External interrupt controller interrupt
| 9 | <<_smart_led_interface_neoled,NEOLED>> | NEOLED TX buffer interrupt
| 9 | <<_smart_led_interface_neoled,NEOLED>> | NEOLED TX FIFO level interrupt
| 10 | <<_direct_memory_access_controller_dma,DMA>> | DMA transfer done interrupt
| 11 | <<_serial_data_interface_controller_sdi,SDI>> | SDI interrupt
| 11 | <<_serial_data_interface_controller_sdi,SDI>> | SDI FIFO level interrupt
| 12 | <<_general_purpose_timer_gptmr,GPTMR>> | General purpose timer interrupt
| 13 | <<_one_wire_serial_interface_controller_onewire,ONEWIRE>> | 1-wire operation done interrupt
| 13 | <<_one_wire_serial_interface_controller_onewire,ONEWIRE>> | 1-wire idle interrupt
| 14 | <<_stream_link_interface_slink,SLINK>> | SLINK FIFO level interrupt
| 15 | - | _reserved_
|=======================
Expand Down
26 changes: 7 additions & 19 deletions rtl/core/neorv32_cpu_control.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,6 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
mie_mei : std_ulogic; -- machine external interrupt enable
mie_mti : std_ulogic; -- machine timer interrupt enable
mie_firq : std_ulogic_vector(15 downto 0); -- fast interrupt enable
mip_firq_we : std_ulogic; -- fast interrupt pending write enable
--
privilege : std_ulogic; -- current privilege mode
privilege_eff : std_ulogic; -- current *effective* privilege mode
Expand Down Expand Up @@ -1434,9 +1433,8 @@ begin
elsif rising_edge(clk_aux_i) then

-- Interrupt-Pending Buffer ---------------------------------------------
-- Once triggered the fast interrupt requests stay active until
-- explicitly cleared via the MIP CSR. The RISC-V standard interrupts
-- have to stay high until cleared by a platform-specific mechanism.
-- Once triggered the interrupt line should stay active until explicitly
-- cleared by a mechanism specific to the interrupt-causing module.
-- ----------------------------------------------------------------------

-- RISC-V machine interrupts --
Expand All @@ -1445,22 +1443,16 @@ begin
trap_ctrl.irq_pnd(irq_mti_irq_c) <= mti_i;

-- NEORV32-specific fast interrupts --
for i in 0 to 15 loop
if (firq_i(i) = '1') then -- new incoming FIRQs have highest priority
trap_ctrl.irq_pnd(irq_firq_0_c+i) <= '1';
elsif (csr.mip_firq_we = '1') and (csr.wmask(16+i) = '0') then -- clear-only mip access
trap_ctrl.irq_pnd(irq_firq_0_c+i) <= '0';
end if;
end loop;
trap_ctrl.irq_pnd(irq_firq_15_c downto irq_firq_0_c) <= firq_i(15 downto 0);

-- debug-mode entry --
trap_ctrl.irq_pnd(irq_db_halt_c) <= '0'; -- unused
trap_ctrl.irq_pnd(irq_db_step_c) <= '0'; -- unused

-- Interrupt (Masking) Buffer -------------------------------------------
-- Masking of interrupt request lines. Furthermore, this buffer ensures
-- that an *active* interrupt request line *stays* active (even if
-- disabled via MIE) if the trap environment is *currently* starting.
-- Interrupt-Masking Buffer ---------------------------------------------
-- Masking of interrupt request lines. Additionally, this buffer ensures
-- that an active interrupt request line stays active (even when
-- disabled via MIE) if the trap environment is already starting.
-- ----------------------------------------------------------------------

-- RISC-V machine interrupts --
Expand Down Expand Up @@ -1717,9 +1709,6 @@ begin
when csr_mcause_c => -- machine trap cause
csr.mcause <= csr.wdata(31) & csr.wdata(4 downto 0); -- type (exception/interrupt) & identifier

-- when csr_mip_c => -- machine interrupt pending
-- only the FIRQs are writable (clear-only); the actual mip.firq write logic is in <interrupt_buffer>

-- --------------------------------------------------------------------
-- machine counter setup --
-- --------------------------------------------------------------------
Expand Down Expand Up @@ -1912,7 +1901,6 @@ begin
end process csr_write_access;

-- out-of-process CSR write access --
csr.mip_firq_we <= '1' when (csr.we = '1') and (csr.cmd(0) = '1') and (csr.addr = csr_mip_c) else '0'; -- mip.firq: write/clear only
csr.tdata1_hit_we <= '1' when (csr.we = '1') and (csr.cmd(0) = '1') and (csr.addr = csr_tdata1_c) else '0'; -- tdata1.hit: write/clear only

-- effective privilege mode is MACHINE when in debug mode --
Expand Down
13 changes: 4 additions & 9 deletions rtl/core/neorv32_dma.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ architecture neorv32_dma_rtl of neorv32_dma is
-- control and status register bits --
constant ctrl_en_c : natural := 0; -- r/w: DMA enable
constant ctrl_auto_c : natural := 1; -- r/w: enable FIRQ-triggered transfer
constant ctrl_fence_c : natural := 3; -- r/w: issue FENCE operation when DMA is done
constant ctrl_fence_c : natural := 2; -- r/w: issue FENCE operation when DMA is done
--
constant ctrl_error_rd_c : natural := 8; -- r/-: error during read transfer
constant ctrl_error_wr_c : natural := 9; -- r/-: error during write transfer
Expand Down Expand Up @@ -163,9 +163,7 @@ begin
config.done <= config.enable and (config.done or engine.done); -- set if enabled and transfer done

if (bus_req_i.stb = '1') then

-- write access --
if (bus_req_i.rw = '1') then
if (bus_req_i.rw = '1') then -- write access
if (bus_req_i.addr(3 downto 2) = "00") then -- control and status register
config.enable <= bus_req_i.data(ctrl_en_c);
config.auto <= bus_req_i.data(ctrl_auto_c);
Expand All @@ -187,9 +185,7 @@ begin
config.endian <= bus_req_i.data(type_endian_c);
config.start <= '1'; -- trigger DMA operation
end if;

-- read access --
else
else -- read access
case bus_req_i.addr(3 downto 2) is
when "00" => -- control and status register
bus_rsp_o.data(ctrl_en_c) <= config.enable;
Expand All @@ -212,13 +208,12 @@ begin
bus_rsp_o.data(type_endian_c) <= config.endian;
end case;
end if;

end if;
end if;
end process bus_access;

-- transfer-done interrupt --
irq_o <= engine.done and config.enable; -- no interrupt if transfer was aborted by clearing config.enable
irq_o <= config.done;


-- Automatic Trigger ----------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion rtl/core/neorv32_package.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ package neorv32_package is

-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090704"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090705"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width

Expand Down
2 changes: 1 addition & 1 deletion sim/neorv32_tb.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ begin
if ci_mode then
-- No need to send the full expectation in one big chunk
check_uart(net, uart1_rx_handle, nul & nul);
check_uart(net, uart1_rx_handle, "0/55" & cr & lf);
check_uart(net, uart1_rx_handle, "0/54" & cr & lf);
end if;

-- Wait until all expected data has been received
Expand Down
49 changes: 40 additions & 9 deletions sw/example/demo_dma/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ int main() {
neorv32_dma_enable();

// issue a FENCE operation when the DMA transfer completes (without errors); this
// will re-sync /flush and reload) all downstream caches
// will re-sync / flush and reload) all **DOWNSTREAM** caches
neorv32_dma_fence_enable();

// initialize and data arrays
Expand Down Expand Up @@ -144,6 +144,17 @@ int main() {
break;
}
}
NEORV32_DMA->CTRL &= ~(1<<DMA_CTRL_DONE); // clear DMA-done flag

asm volatile ("fence"); // synchronize caches

// check if transfer was successful
if ((dma_dst[0] != 0x99887766) ||
(dma_dst[1] != 0x55443322) ||
(dma_dst[2] != 0xddccbbaa) ||
(dma_dst[3] != 0xffee1100)) {
neorv32_uart0_printf("Incorrect DST data!\n");
}

show_arrays();

Expand Down Expand Up @@ -177,6 +188,17 @@ int main() {
break;
}
}
NEORV32_DMA->CTRL &= ~(1<<DMA_CTRL_DONE); // clear DMA-done flag

asm volatile ("fence"); // synchronize caches

// check if transfer was successful
if ((dma_dst[0] != 0x66778899) ||
(dma_dst[1] != 0x66778899) ||
(dma_dst[2] != 0x66778899) ||
(dma_dst[3] != 0x66778899)) {
neorv32_uart0_printf("Incorrect DST data!\n");
}

show_arrays();

Expand All @@ -187,7 +209,6 @@ int main() {
neorv32_uart0_printf("\nExample 3: Manual byte-to-signed-word block transfer using transfer-done interrupt.\n");

// configure DMA interrupt
neorv32_cpu_csr_clr(CSR_MIP, 1 << DMA_FIRQ_PENDING); // clear any pending DMA FIRQ
neorv32_cpu_csr_set(CSR_MIE, 1 << DMA_FIRQ_ENABLE); // enable DMA interrupt source
neorv32_cpu_csr_set(CSR_MSTATUS, 1 << CSR_MSTATUS_MIE); // enable machine-mode interrupts

Expand All @@ -205,8 +226,14 @@ int main() {
// go to sleep mode, wakeup on DMA transfer-done interrupt
neorv32_cpu_sleep();

asm volatile ("fence"); // synchronize caches

// check if transfer was successful
if (neorv32_dma_status() != DMA_STATUS_IDLE) {
if ((neorv32_dma_status() != DMA_STATUS_IDLE) || // DMA is in idle mode without errors
(dma_dst[0] != 0xffffff99) ||
(dma_dst[1] != 0xffffff88) ||
(dma_dst[2] != 0x00000077) ||
(dma_dst[3] != 0x00000066)) {
neorv32_uart0_printf("Transfer failed!\n");
}

Expand All @@ -221,7 +248,6 @@ int main() {
if (neorv32_gptmr_available()) { // only execute if GPTMR is available

// configure DMA interrupt
neorv32_cpu_csr_clr(CSR_MIP, 1 << DMA_FIRQ_PENDING); // clear any pending DMA FIRQ
neorv32_cpu_csr_set(CSR_MIE, 1 << DMA_FIRQ_ENABLE); // enable DMA interrupt source
neorv32_cpu_csr_set(CSR_MSTATUS, 1 << CSR_MSTATUS_MIE); // enable machine-mode interrupts

Expand All @@ -245,9 +271,14 @@ int main() {
// sleep until interrupt (from DMA)
neorv32_cpu_sleep();

// transfer successful (and actually executed)?
if ((neorv32_dma_done() == 0) || // check if the DMA has actually completed a transfer
(neorv32_dma_status() != DMA_STATUS_IDLE)) { // DMA is in idle mode without errors
asm volatile ("fence"); // synchronize caches

// transfer successful?
if ((neorv32_dma_status() != DMA_STATUS_IDLE) || // DMA is in idle mode without errors
(dma_dst[0] != 0xffffffff) ||
(dma_dst[1] != 0xffffffff) ||
(dma_dst[2] != 0xffffffff) ||
(dma_dst[3] != 0xffffffff)) {
neorv32_uart0_printf("Transfer failed!\n");
}

Expand All @@ -268,7 +299,6 @@ int main() {
**************************************************************************/
void show_arrays(void) {

asm volatile ("fence"); // re-sync caches
neorv32_uart0_printf("---------------------------\n");
neorv32_uart0_printf(" SRC DST\n");
neorv32_uart0_printf("[0] 0x%x 0x%x\n", dma_src[0], dma_dst[0]);
Expand All @@ -286,7 +316,8 @@ void show_arrays(void) {
**************************************************************************/
void dma_firq_handler(void) {

neorv32_cpu_csr_clr(CSR_MIP, 1 << DMA_FIRQ_PENDING); // clear/ack pending FIRQ
neorv32_gptmr_trigger_matched(); // clear GPTMR timer-match interrupt
NEORV32_DMA->CTRL &= ~(1<<DMA_CTRL_DONE); // clear DMA-done interrupt
neorv32_gptmr_disable(); // disable GPTMR
neorv32_uart0_printf("<<DMA interrupt>>\n");
}
3 changes: 1 addition & 2 deletions sw/example/demo_gptmr/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ int main() {
neorv32_gptmr_setup(CLK_PRSC_8, NEORV32_SYSINFO->CLK / (8 * 2), 1);

// enable interrupt
neorv32_cpu_csr_clr(CSR_MIP, 1 << GPTMR_FIRQ_PENDING); // make sure there is no GPTMR IRQ pending already
neorv32_cpu_csr_set(CSR_MIE, 1 << GPTMR_FIRQ_ENABLE); // enable GPTMR FIRQ channel
neorv32_cpu_csr_set(CSR_MSTATUS, 1 << CSR_MSTATUS_MIE); // enable machine-mode interrupts

Expand All @@ -114,7 +113,7 @@ int main() {
**************************************************************************/
void gptmr_firq_handler(void) {

neorv32_cpu_csr_write(CSR_MIP, ~(1<<GPTMR_FIRQ_PENDING)); // clear/ack pending FIRQ
neorv32_gptmr_trigger_matched(); // clear timer-match interrupt

neorv32_uart0_putc('.'); // send tick symbol via UART0
neorv32_gpio_pin_toggle(0); // toggle output port bit 0
Expand Down
2 changes: 0 additions & 2 deletions sw/example/demo_slink/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,6 @@ int main() {
void slink_firq_handler(void) {

neorv32_uart0_printf(" <<RX data: 0x%x>> ", neorv32_slink_get());

neorv32_cpu_csr_clr(CSR_MIP, 1 << SLINK_FIRQ_PENDING); // ack/clear FIRQ *after* reading RX data
}


Expand Down
2 changes: 0 additions & 2 deletions sw/example/demo_spi_irq/drv/neorv32_spi_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ void neorv32_spi_isr(t_neorv32_spi *self) {
neorv32_spi_cs_dis(); // deselect slave
self->uint32Total = 0;
self->uint8IsBusy = 0;
neorv32_cpu_csr_clr(CSR_MIP, 1 << SPI_FIRQ_PENDING); // ack/clear pending FIRQ
return;
}

Expand All @@ -91,7 +90,6 @@ void neorv32_spi_isr(t_neorv32_spi *self) {
for ( ; self->uint32Write<uint32Lim; (self->uint32Write)++ ) {
NEORV32_SPI->DATA = (uint32_t) (self->ptrSpiBuf)[self->uint32Write]; // next transfer
}
neorv32_cpu_csr_clr(CSR_MIP, 1 << SPI_FIRQ_PENDING); // ack/clear pending FIRQ
return;
}

Expand Down
Loading