⚠️ rework DMA and GPTMR (#1194)
Some checks failed
Documentation / SW Framework (push) Has been cancelled
Documentation / Datasheet (push) Has been cancelled
Processor / processor simulation (push) Has been cancelled
Documentation / Deploy to Releases and Pages (push) Has been cancelled

This commit is contained in:
stnolting 2025-03-02 15:31:59 +01:00 committed by GitHub
commit 449dd87766
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 235 additions and 460 deletions

View file

@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12
| Date | Version | Comment | Ticket |
|:----:|:-------:|:--------|:------:|
| 02.03.2025 | 1.11.1.8 | :warning: remove DMA FIRQ-triggered auto mode; :warning: remove GPTMR mode configuration bit | [#1194](https://github.com/stnolting/neorv32/pull/1194) |
| 01.03.2025 | 1.11.1.7 | minor rtl / coding style edits (fixing a Vivado 2024.2 synthesis issue) | [#1193](https://github.com/stnolting/neorv32/pull/1193) |
| 23.02.2025 | 1.11.1.6 | source-out CPU counters into a new rtl file (`neorv32_cpu_counters.vhd`) | [#1192](https://github.com/stnolting/neorv32/pull/1192) |
| 22.02.2025 | 1.11.1.5 | minor rtl edits and cleanups | [#1191](https://github.com/stnolting/neorv32/pull/1191) |

View file

@ -16,17 +16,20 @@
**Overview**
The NEORV32 DMA provides a small-scale scatter/gather direct memory access controller that allows to transfer and
modify data independently of the CPU. A single read/write transfer channel is implemented that is configured via
memory-mapped registers. a configured transfer can either be triggered manually or by a programmable CPU FIRQ interrupt
(see <<_neorv32_specific_fast_interrupt_requests>>).
The NEORV32 DMA provides a lightweight direct memory access controller that allows to transfer and
modify data independently of the CPU. A single read/write channel is implemented that is configured via
memory-mapped registers.
The DMA is connected to the central processor-internal bus system (see section <<_address_space>>) and can access the same
address space as the CPU core. It uses _interleaving mode_ accessing the central processor bus only if the CPU does not
currently request and bus access.
currently request and bus access. The controller can handle different data quantities (e.g. read bytes and write them
back as sign-extend words) and can also change the Endianness of data while transferring.
The controller can handle different data quantities (e.g. read bytes and write them back as sign-extend words) and can
also change the Endianness of data while transferring.
.DMA Access Privilege Level
[WARNING]
Transactions performed by the DMA are executed as bus transactions with elevated **machine-mode** privilege level.
Note that any physical memory protection rules (<<_smpmp_isa_extension>>) are not applied to DMA transfers.
.DMA Demo Program
[TIP]
@ -40,39 +43,32 @@ configuring the actual DMA transfer. The base address of the source data is prog
Vice versa, the base address of the destination data is programmed via the `DST_BASE`. The third configuration register
`TTYPE` is use to configure the actual transfer type and the number of elements to transfer.
The DMA is enabled by setting the `DMA_CTRL_EN` bit of the control register. Manual trigger mode (i.e. the DMA transfer is
triggered by writing to the `TTYPE` register) is selected if `DMA_CTRL_AUTO` is cleared. Alternatively, the DMA transfer can
be triggered by a processor internal FIRQ signal if `DMA_CTRL_AUTO` is set (see section below).
The DMA is enabled by setting the `DMA_CTRL_EN` bit of the control register. A programmed DMA transfer is initiated
by setting the control register's `DMA_CTRL_START` bit.
The DMA uses a load-modify-write data transfer process. Data is read from the bus system, internally modified and then written
back to the bus system. This combination is implemented as an atomic progress, so canceling the current transfer by clearing the
`DMA_CTRL_EN` bit will stop the DMA right after the current load-modify-write operation.
`DMA_CTRL_EN` bit will stop the DMA after the current load-modify-write operation.
If the DMA controller detects a bus error during operation, it will set either the `DMA_CTRL_ERROR_RD` (error during
last read access) or `DMA_CTRL_ERROR_WR` (error during last write access) and will terminate the current transfer.
Software can read the `SRC_BASE` or `DST_BASE` register to retrieve the address that caused the according error.
Alternatively, software can read back the `NUM` bits of the control register to determine the index of the element
that caused the error. The error bits are automatically cleared when starting a new transfer.
The error bits are automatically cleared when starting a new transfer. The error flags auto-clear when starting a new
DMA transfer.
When the `DMA_CTRL_DONE` flag is set the DMA has actually executed a transfer. However, the `DMA_CTRL_ERROR_*` flags
should also be checked to verify that the executed transfer completed without errors. The `DMA_CTRL_DONE` flag is
automatically cleared when writing the `CTRL` register.
.DMA Access Privilege Level
[WARNING]
Transactions performed by the DMA are executed as bus transactions with elevated **machine-mode** privilege level.
Note that any physical memory protection rules (<<_smpmp_isa_extension>>) are not applied to DMA transfers.
**Transfer Configuration**
If the DMA is set to **manual trigger mode** (`DMA_CTRL_AUTO` = 0) writing the `TTRIG` register will start the
programmed DMA transfer. Once started, the DMA will read one data quantity from the source address, processes it internally
Once started, the DMA will read one data quantity from the source address, processes it internally
and then will write it back to the destination address. The `DMA_TTYPE_NUM` bits of the `TTYPE` register define how many
times this process is repeated by specifying the number of elements to transfer.
Optionally, the source and/or destination addresses can be increments according to the data quantities
automatically by setting the according `DMA_TTYPE_SRC_INC` and/or `DMA_TTYPE_DST_INC` bit.
Optionally, the source and/or destination addresses can be automatically increments according to the data quantities
by setting the according `DMA_TTYPE_SRC_INC` and/or `DMA_TTYPE_DST_INC` bit.
Four different transfer quantities are available, which are configured via the `DMA_TTYPE_QSEL` bits:
@ -89,34 +85,11 @@ bit is set.
Make sure to align the source and destination base addresses to the according transfer data quantities. For instance,
word-to-word transfers require that the two LSB of `SRC_BASE` and `DST_BASE` are cleared.
.Writing to IO Device
.Accessing IO Device
[IMPORTANT]
When writing data to IO / peripheral devices (for example to the <<_cyclic_redundancy_check_crc>>) the destination
data quantity has to be set to **word** (32-bit) since all IO registers can only be written in full 32-bit word mode.
**Automatic Trigger**
As an alternative to the manual trigger mode, the DMA can be set to **automatic trigger mode** starting a pre-configured
transfer if a specific processor-internal peripheral issues a FIRQ interrupt request. The automatic trigger mode is enabled by
setting the `CTRL` register's `DMA_CTRL_AUTO` bit. In this configuration _no_ transfer is started when writing to the DMA's
`TTYPE` register.
The actually triggering FIRQ channel is configured via the control register's `DMA_CTRL_FIRQ_SEL` bits. Writing a 0 will
select FIRQ channel 0, writing a 1 will select FIRQ channel 1, and so on. See section <<_processor_interrupts>>
for a list of all FIRQ channels and their according sources.
The FIRQ trigger can operate in two trigger mode configured via the `DMA_CTRL_FIRQ_TYPE` flag:
* `DMA_CTRL_FIRQ_TYPE = 0`: trigger the automatic DMA transfer on a rising-edge of the selected FIRQ channel (e.g. trigger
DMA transfer only once)
* `DMA_CTRL_FIRQ_TYPE = 1`: trigger the automatic DMA transfer when the selected FIRQ channel is active (e.g. trigger
DMA transfer again and again)
.FIRQ Trigger
[NOTE]
The DMA transfer will start if a **rising edge** is detected on the configured FIRQ channel. Hence, the DMA is triggered only
once even if the selected FIRQ channel keeps pending.
In contrast, read accesses to IO / peripheral devices can also be executed on a byte granule.
**DMA Interrupt**
@ -134,23 +107,19 @@ register).
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.11+<| `0xffed0000` .11+<| `CTRL` <|`0` `DMA_CTRL_EN` ^| r/w <| DMA module enable
<|`1` `DMA_CTRL_AUTO` ^| r/w <| Enable automatic mode (FIRQ-triggered)
<|`7:2` _reserved_ ^| r/- <| reserved, read as zero
<|`8` `DMA_CTRL_ERROR_RD` ^| r/- <| Error during read access, clears when starting a new transfer
<|`9` `DMA_CTRL_ERROR_WR` ^| r/- <| Error during write access, clears when starting a new transfer
<|`10` `DMA_CTRL_BUSY` ^| r/- <| DMA transfer in progress
<|`11` `DMA_CTRL_DONE` ^| r/c <| Set if a transfer was executed; auto-clears on write-access
<|`14:12` _reserved_ ^| r/- <| reserved, read as zero
<|`15` `DMA_CTRL_FIRQ_TYPE` ^| r/w <| Trigger on rising-edge (`0`) or high-level (`1`) or selected FIRQ channel
<|`19:16` `DMA_CTRL_FIRQ_SEL_MSB : DMA_CTRL_FIRQ_SEL_LSB` ^| r/w <| FIRQ trigger select (FIRQ0=0 ... FIRQ15=15)
<|`31:20` _reserved_ ^| r/- <| reserved, read as zero
.7+<| `0xffed0000` .7+<| `CTRL` <|`0` `DMA_CTRL_EN` ^| r/w <| DMA module enable
<|`1` `DMA_CTRL_START` ^| r/s <| Start programmed DMA transfer (reads as zero)
<|`7:27` _reserved_ ^| r/- <| reserved, read as zero
<|`28` `DMA_CTRL_ERROR_RD` ^| r/- <| Error during read access, clears when starting a new transfer
<|`29` `DMA_CTRL_ERROR_WR` ^| r/- <| Error during write access, clears when starting a new transfer
<|`30` `DMA_CTRL_DONE` ^| r/c <| Set if a transfer was executed; auto-clears on write-access
<|`31` `DMA_CTRL_BUSY` ^| r/- <| DMA transfer in progress
| `0xffed0004` | `SRC_BASE` |`31:0` | r/w | Source base address (shows the last-accessed source address when read)
| `0xffed0008` | `DST_BASE` |`31:0` | r/w | Destination base address (shows the last-accessed destination address when read)
.6+<| `0xffed000c` .6+<| `TTYPE` <|`23:0` `DMA_TTYPE_NUM_MSB : DMA_TTYPE_NUM_LSB` ^| r/w <| Number of elements to transfer (shows the last-transferred element index when read)
<|`26:24` _reserved_ ^| r/- <| reserved, read as zero
<|`28:27` `DMA_TTYPE_QSEL_MSB : DMA_TTYPE_QSEL_LSB` ^| r/w <| Quantity select (`00` = byte -> byte, `01` = byte -> zero-extended-word, `10` = byte -> sign-extended-word, `11` = word -> word)
<|`28:27` `DMA_TTYPE_QSEL_MSB : DMA_TTYPE_QSEL_LSB` ^| r/w <| Transfer type (`00` = byte -> byte, `01` = byte -> zero-extended-word, `10` = byte -> sign-extended-word, `11` = word -> word)
<|`29` `DMA_TTYPE_SRC_INC` ^| r/w <| Constant (`0`) or incrementing (`1`) source address
<|`30` `DMA_TTYPE_DST_INC` ^| r/w <| Constant (`0`) or incrementing (`1`) destination address
<|`31` `DMA_TTYPE_ENDIAN` ^| r/w <| Swap Endianness when set
<|`31` `DMA_TTYPE_ENDIAN` ^| r/w <| Convert Endianness when set
|=======================

View file

@ -16,13 +16,13 @@
**Overview**
The general purpose timer module implements a simple yet universal 32-bit timer. It is implemented if the processor's
The general purpose timer module implements a simple 32-bit interval timer. It is implemented if the processor's
`IO_GPTMR_EN` top generic is set `true`. The timer provides a pre-scaled counter register that can trigger an interrupt
when reaching a programmable threshold value.
The GPTMR provides three interface registers : a control register (`CTRL`), a 32-bit counter register (`COUNT`) and a
32-bit threshold register (`THRES`). The timer is globally enabled by setting the `GPTMR_CTRL_EN` bit in the module's
control register. When the timer is enable the `COUNT` register will start incrementing from zero at a programmable
control register. When the timer is enabled the `COUNT` register will start incrementing from zero at a programmable
rate that scales the main processor clock. this pre-scaler is configured via the three `GPTMR_CTRL_PRSCx`
control register bits:
@ -34,14 +34,10 @@ control register bits:
| Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096
|=======================
Whenever the counter register `COUNT` equals the programmable threshold value `THRES` the module's interrupt
signal becomes pending (indicated by `GPTMR_CTRL_IRQ_PND` being set). Note that a pending interrupt has to be
cleared manually by writing a `1` to `GPTMR_CTRL_IRQ_CLR`.
Whenever the counter register `COUNT` equals the programmable threshold value `THRES` the module's interrupt signal becomes
pending (indicated by `GPTMR_CTRL_IRQ_PND` being set). In this case the `COUNT` register is automatically reset and restarts
incrementing from zero. Note that a pending interrupt has to be cleared manually by writing a `1` to `GPTMR_CTRL_IRQ_CLR`.
The control register's `GPTMR_CTRL_MODE` bit defines what will happen when `COUNT == THRES`.
* `GPTMR_CTRL_MODE = 0`: **single-shot mode** - the `COUNT` register will stop incrementing
* `GPTMR_CTRL_MODE = 1`: **continuous mode** - the `COUNT` register is automatically reset and restarts incrementing from zero
.Resetting the Counter
[NOTE]
@ -61,10 +57,9 @@ stay pending until explicitly cleared by writing a 1 to `GPTMR_CTRL_IRQ_CLR`.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.6+<| `0xfff10000` .6+<| `CTRL` <|`0` `GPTMR_CTRL_EN` ^| r/w <| Timer enable flag
.5+<| `0xfff10000` .5+<| `CTRL` <|`0` `GPTMR_CTRL_EN` ^| r/w <| Timer enable flag
<|`3:1` `GPTMR_CTRL_PRSC2 : GPTMR_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select
<|`4` `GPTMR_CTRL_MODE` ^| r/w <| Operation mode (0=single-shot, 1=continuous)
<|`29:5` - ^| r/- <| _reserved_, read as zero
<|`29:4` - ^| r/- <| _reserved_, read as zero
<|`30` `GPTMR_CTRL_IRQ_CLR` ^| -/w <| Write `1` to clear timer-match interrupt; auto-clears
<|`31` `GPTMR_CTRL_IRQ_PND` ^| r/- <| Timer-match interrupt pending
| `0xfff10004` | `THRES` |`31:0` | r/w | Threshold value register

View file

@ -3,7 +3,7 @@
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
@ -45,7 +45,7 @@ architecture neorv32_crc_rtl of neorv32_crc is
signal crc : crc_t;
-- delayed ACK on write access --
signal we_ack : std_ulogic_vector(5 downto 0); -- to wait for serial CRC processing
signal we_ack : std_ulogic_vector(4 downto 0); -- to wait for serial CRC processing
begin
@ -68,13 +68,13 @@ begin
-- write access --
if (bus_req_i.stb = '1') and (bus_req_i.rw = '1') then
if (bus_req_i.addr(3 downto 2) = mode_addr_c) then -- mode select
crc.mode <= bus_req_i.data(01 downto 0);
crc.mode <= bus_req_i.data(1 downto 0);
end if;
if (bus_req_i.addr(3 downto 2) = poly_addr_c) then -- polynomial
crc.poly <= bus_req_i.data(31 downto 0);
end if;
if (bus_req_i.addr(3 downto 2) = data_addr_c) then -- data
crc.data <= bus_req_i.data(07 downto 0);
crc.data <= bus_req_i.data(7 downto 0);
end if;
end if;
@ -84,7 +84,7 @@ begin
-- read access --
if (bus_req_i.stb = '1') and (bus_req_i.rw = '0') then
case bus_req_i.addr(3 downto 2) is
when mode_addr_c => bus_rsp_o.data(01 downto 0) <= crc.mode; -- mode select
when mode_addr_c => bus_rsp_o.data(1 downto 0) <= crc.mode; -- mode select
when poly_addr_c => bus_rsp_o.data(31 downto 0) <= crc.poly; -- polynomial
when others => bus_rsp_o.data(31 downto 0) <= crc.sreg; -- CRC result
end case;
@ -123,7 +123,7 @@ begin
-- operation mode --
with crc.mode select crc.msb <=
crc.sreg(07) when "00", -- crc8
crc.sreg(7) when "00", -- crc8
crc.sreg(15) when "01", -- crc16
crc.sreg(31) when others; -- crc32

View file

@ -23,7 +23,6 @@ entity neorv32_dma is
bus_rsp_o : out bus_rsp_t; -- bus response
dma_req_o : out bus_req_t; -- DMA request
dma_rsp_i : in bus_rsp_t; -- DMA response
firq_i : in std_ulogic_vector(15 downto 0); -- CPU FIRQ channels
irq_o : out std_ulogic -- transfer done interrupt
);
end neorv32_dma;
@ -41,17 +40,13 @@ architecture neorv32_dma_rtl of neorv32_dma is
constant type_endian_c : natural := 31; -- r/w: Convert Endianness when set
-- 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_en_c : natural := 0; -- r/w: DMA enable
constant ctrl_start_c : natural := 1; -- -/s: start DMA operation
--
constant ctrl_error_rd_c : natural := 8; -- r/-: error during read transfer
constant ctrl_error_wr_c : natural := 9; -- r/-: error during write transfer
constant ctrl_busy_c : natural := 10; -- r/-: DMA transfer in progress
constant ctrl_done_c : natural := 11; -- r/c: a DMA transfer was executed/attempted
--
constant ctrl_firq_type_c : natural := 15; -- r/w: trigger on FIRQ rising-edge or on high-level
constant ctrl_firq_sel_lsb_c : natural := 16; -- r/w: FIRQ trigger select LSB
constant ctrl_firq_sel_msb_c : natural := 19; -- r/w: FIRQ trigger select MSB
constant ctrl_error_rd_c : natural := 28; -- r/-: error during read transfer
constant ctrl_error_wr_c : natural := 29; -- r/-: error during write transfer
constant ctrl_done_c : natural := 30; -- r/c: transfer has completed
constant ctrl_busy_c : natural := 31; -- r/-: DMA transfer in progress
-- transfer quantities --
constant qsel_b2b_c : std_ulogic_vector(1 downto 0) := "00"; -- byte to byte
@ -61,19 +56,16 @@ architecture neorv32_dma_rtl of neorv32_dma is
-- configuration registers --
type cfg_t is record
enable : std_ulogic; -- DMA enabled when set
auto : std_ulogic; -- FIRQ-driven auto transfer
firq_sel : std_ulogic_vector(3 downto 0); -- FIRQ trigger select
firq_type : std_ulogic; -- trigger on FIRQ rising-edge (0) or high-level (1)
src_base : std_ulogic_vector(31 downto 0); -- source base address
dst_base : std_ulogic_vector(31 downto 0); -- destination base address
num : std_ulogic_vector(23 downto 0); -- number of elements
qsel : std_ulogic_vector(1 downto 0); -- data quantity select
src_inc : std_ulogic; -- constant (0) or incrementing (1) source address
dst_inc : std_ulogic; -- constant (0) or incrementing (1) destination address
endian : std_ulogic; -- convert endianness when set
start : std_ulogic; -- transfer start trigger
done : std_ulogic; -- transfer was executed (but might have failed)
enable : std_ulogic; -- DMA enabled when set
start : std_ulogic; -- transfer start trigger
done : std_ulogic; -- transfer was executed (but might have failed)
src_base : std_ulogic_vector(31 downto 0); -- source base address
dst_base : std_ulogic_vector(31 downto 0); -- destination base address
num : std_ulogic_vector(23 downto 0); -- number of elements
qsel : std_ulogic_vector(1 downto 0); -- data quantity select
src_inc : std_ulogic; -- constant (0) or incrementing (1) source address
dst_inc : std_ulogic; -- constant (0) or incrementing (1) destination address
endian : std_ulogic; -- convert endianness when set
end record;
signal cfg : cfg_t;
@ -81,6 +73,8 @@ architecture neorv32_dma_rtl of neorv32_dma is
type state_t is (S_IDLE, S_READ, S_WRITE, S_NEXT);
type engine_t is record
state : state_t;
stb : std_ulogic;
rw : std_ulogic;
src_addr : std_ulogic_vector(31 downto 0);
dst_addr : std_ulogic_vector(31 downto 0);
num : std_ulogic_vector(23 downto 0);
@ -94,14 +88,7 @@ architecture neorv32_dma_rtl of neorv32_dma is
signal engine : engine_t;
-- data alignment --
signal align_buf : std_ulogic_vector(31 downto 0);
signal align_end : std_ulogic_vector(31 downto 0);
-- FIRQ trigger --
signal firq_buf : std_ulogic_vector(15 downto 0);
signal match : std_ulogic;
signal match_ff : std_ulogic;
signal atrigger : std_ulogic;
signal align_buf, align_end : std_ulogic_vector(31 downto 0);
begin
@ -110,20 +97,17 @@ begin
bus_access: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
bus_rsp_o <= rsp_terminate_c;
cfg.enable <= '0';
cfg.auto <= '0';
cfg.firq_sel <= (others => '0');
cfg.firq_type <= '0';
cfg.src_base <= (others => '0');
cfg.dst_base <= (others => '0');
cfg.num <= (others => '0');
cfg.qsel <= (others => '0');
cfg.src_inc <= '0';
cfg.dst_inc <= '0';
cfg.endian <= '0';
cfg.start <= '0';
cfg.done <= '0';
bus_rsp_o <= rsp_terminate_c;
cfg.enable <= '0';
cfg.start <= '0';
cfg.done <= '0';
cfg.src_base <= (others => '0');
cfg.dst_base <= (others => '0');
cfg.num <= (others => '0');
cfg.qsel <= (others => '0');
cfg.src_inc <= '0';
cfg.dst_inc <= '0';
cfg.endian <= '0';
elsif rising_edge(clk_i) then
-- bus handshake --
bus_rsp_o.ack <= bus_req_i.stb;
@ -134,14 +118,13 @@ begin
cfg.start <= '0'; -- default
cfg.done <= cfg.enable and (cfg.done or engine.done); -- set if enabled and transfer done
-- bus access --
if (bus_req_i.stb = '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
cfg.enable <= bus_req_i.data(ctrl_en_c);
cfg.auto <= bus_req_i.data(ctrl_auto_c);
cfg.done <= '0'; -- clear on write access
cfg.firq_type <= bus_req_i.data(ctrl_firq_type_c);
cfg.firq_sel <= bus_req_i.data(ctrl_firq_sel_msb_c downto ctrl_firq_sel_lsb_c);
cfg.enable <= bus_req_i.data(ctrl_en_c);
cfg.start <= bus_req_i.data(ctrl_start_c); -- start transfer
cfg.done <= '0'; -- clear on write access
end if;
if (bus_req_i.addr(3 downto 2) = "01") then -- source base address
cfg.src_base <= bus_req_i.data;
@ -155,19 +138,15 @@ begin
cfg.src_inc <= bus_req_i.data(type_src_inc_c);
cfg.dst_inc <= bus_req_i.data(type_dst_inc_c);
cfg.endian <= bus_req_i.data(type_endian_c);
cfg.start <= '1'; -- trigger DMA operation
end if;
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) <= cfg.enable;
bus_rsp_o.data(ctrl_auto_c) <= cfg.auto;
bus_rsp_o.data(ctrl_error_rd_c) <= engine.err_rd;
bus_rsp_o.data(ctrl_error_wr_c) <= engine.err_wr;
bus_rsp_o.data(ctrl_busy_c) <= engine.busy;
bus_rsp_o.data(ctrl_done_c) <= cfg.done;
bus_rsp_o.data(ctrl_firq_type_c) <= cfg.firq_type;
bus_rsp_o.data(ctrl_firq_sel_msb_c downto ctrl_firq_sel_lsb_c) <= cfg.firq_sel;
bus_rsp_o.data(ctrl_en_c) <= cfg.enable;
bus_rsp_o.data(ctrl_error_rd_c) <= engine.err_rd;
bus_rsp_o.data(ctrl_error_wr_c) <= engine.err_wr;
bus_rsp_o.data(ctrl_done_c) <= cfg.done;
bus_rsp_o.data(ctrl_busy_c) <= engine.busy;
when "01" => -- address of last read access
bus_rsp_o.data <= engine.src_addr;
when "10" => -- address of last write access
@ -188,63 +167,40 @@ begin
irq_o <= cfg.done;
-- Automatic Trigger ----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
automatic_trigger: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
firq_buf <= (others => '0');
match_ff <= '0';
atrigger <= '0';
elsif rising_edge(clk_i) then
firq_buf <= firq_i;
match_ff <= match;
if (cfg.firq_type = '0') then -- auto-trigger on rising-edge of FIRQ
atrigger <= match and (not match_ff);
else -- auto-trigger on high-level of FIRQ
atrigger <= match;
end if;
end if;
end process automatic_trigger;
-- select a single FIRQ --
match <= firq_buf(to_integer(unsigned(cfg.firq_sel)));
-- Bus Access Engine ----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
bus_engine: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
engine.state <= S_IDLE;
engine.stb <= '0';
engine.rw <= '0';
engine.src_addr <= (others => '0');
engine.dst_addr <= (others => '0');
engine.num <= (others => '0');
engine.err_rd <= '0';
engine.err_wr <= '0';
engine.done <= '0';
dma_req_o.rw <= '0';
dma_req_o.stb <= '0';
elsif rising_edge(clk_i) then
-- defaults --
engine.done <= '0';
dma_req_o.stb <= '0';
engine.done <= '0';
engine.stb <= '0';
-- state machine --
case engine.state is
when S_IDLE => -- idle, waiting for start trigger
when S_IDLE => -- idle, waiting for trigger
-- ------------------------------------------------------------
engine.src_addr <= cfg.src_base;
engine.dst_addr <= cfg.dst_base;
engine.num <= cfg.num;
if (cfg.enable = '1') and
(((cfg.auto = '0') and (cfg.start = '1')) or -- manual trigger
((cfg.auto = '1') and (atrigger = '1'))) then -- automatic trigger
engine.rw <= '0';
if (cfg.enable = '0') and (cfg.start = '1') then -- disabled or start
engine.err_rd <= '0';
engine.err_wr <= '0';
dma_req_o.rw <= '0'; -- read
dma_req_o.stb <= '1'; -- issue read request
end if;
if (cfg.enable = '1') and (cfg.start = '1') then -- start
engine.stb <= '1';
engine.state <= S_READ;
end if;
@ -255,9 +211,9 @@ begin
engine.err_rd <= '1';
engine.state <= S_IDLE;
elsif (dma_rsp_i.ack = '1') then
dma_req_o.rw <= '1'; -- write
dma_req_o.stb <= '1'; -- issue write request
engine.state <= S_WRITE;
engine.rw <= '1'; -- write
engine.stb <= '1'; -- issue write request
engine.state <= S_WRITE;
end if;
when S_WRITE => -- pending write access
@ -283,9 +239,9 @@ begin
if (cfg.dst_inc = '1') then -- incrementing destination address
engine.dst_addr <= std_ulogic_vector(unsigned(engine.dst_addr) + engine.dst_add);
end if;
dma_req_o.rw <= '0'; -- read
dma_req_o.stb <= '1'; -- issue read request
engine.state <= S_READ;
engine.rw <= '0';
engine.stb <= '1'; -- issue read request
engine.state <= S_READ;
end if;
when others => -- undefined
@ -300,7 +256,9 @@ begin
engine.busy <= '0' when (engine.state = S_IDLE) else '1';
-- bus output --
dma_req_o.addr <= engine.src_addr when (engine.state = S_READ) else engine.dst_addr;
dma_req_o.stb <= engine.stb;
dma_req_o.rw <= engine.rw;
dma_req_o.addr <= engine.dst_addr when (engine.state = S_WRITE) else engine.src_addr;
dma_req_o.src <= '0'; -- source = data access
dma_req_o.priv <= priv_mode_m_c; -- DMA accesses are always privileged
dma_req_o.debug <= '0'; -- can never ever be in debug mode
@ -312,9 +270,15 @@ begin
address_inc: process(cfg.qsel)
begin
case cfg.qsel is
when qsel_b2b_c => engine.src_add <= to_unsigned(1, 32); engine.dst_add <= to_unsigned(1, 32); -- byte -> byte
when qsel_w2w_c => engine.src_add <= to_unsigned(4, 32); engine.dst_add <= to_unsigned(4, 32); -- word -> word
when others => engine.src_add <= to_unsigned(1, 32); engine.dst_add <= to_unsigned(4, 32); -- byte -> word
when qsel_b2b_c => -- byte -> byte
engine.src_add <= to_unsigned(1, 32);
engine.dst_add <= to_unsigned(1, 32);
when qsel_w2w_c => -- word -> word
engine.src_add <= to_unsigned(4, 32);
engine.dst_add <= to_unsigned(4, 32);
when others => -- byte -> word
engine.src_add <= to_unsigned(1, 32);
engine.dst_add <= to_unsigned(4, 32);
end case;
end process address_inc;

View file

@ -3,7 +3,7 @@
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
-- Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --
@ -34,12 +34,11 @@ architecture neorv32_gptmr_rtl of neorv32_gptmr is
constant ctrl_prsc0_c : natural := 1; -- r/w: clock prescaler select bit 0
constant ctrl_prsc1_c : natural := 2; -- r/w: clock prescaler select bit 1
constant ctrl_prsc2_c : natural := 3; -- r/w: clock prescaler select bit 2
constant ctrl_mode_c : natural := 4; -- r/w: mode (0=single-shot, 1=continuous)
--
constant ctrl_irq_clr_c : natural := 30; -- -/w: set to clear timer-match interrupt
constant ctrl_irq_c : natural := 31; -- r/-: timer-match interrupt pending
--
signal ctrl : std_ulogic_vector(4 downto 0);
signal ctrl : std_ulogic_vector(3 downto 0);
-- timer core --
type timer_t is record
@ -78,7 +77,6 @@ begin
ctrl(ctrl_prsc0_c) <= bus_req_i.data(ctrl_prsc0_c);
ctrl(ctrl_prsc1_c) <= bus_req_i.data(ctrl_prsc1_c);
ctrl(ctrl_prsc2_c) <= bus_req_i.data(ctrl_prsc2_c);
ctrl(ctrl_mode_c) <= bus_req_i.data(ctrl_mode_c);
--
irq_clr <= bus_req_i.data(ctrl_irq_clr_c);
end if;
@ -92,7 +90,6 @@ begin
bus_rsp_o.data(ctrl_prsc0_c) <= ctrl(ctrl_prsc0_c);
bus_rsp_o.data(ctrl_prsc1_c) <= ctrl(ctrl_prsc1_c);
bus_rsp_o.data(ctrl_prsc2_c) <= ctrl(ctrl_prsc2_c);
bus_rsp_o.data(ctrl_mode_c) <= ctrl(ctrl_mode_c);
--
bus_rsp_o.data(ctrl_irq_c) <= irq_pnd;
when "01" => -- threshold register
@ -122,9 +119,7 @@ begin
timer.count <= (others => '0');
elsif (timer.tick = '1') then -- timer 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');
end if;
timer.count <= (others => '0');
else
timer.count <= std_ulogic_vector(unsigned(timer.count) + 1);
end if;

View file

@ -29,7 +29,7 @@ package neorv32_package is
-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01110107"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01110108"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width

View file

@ -462,7 +462,7 @@ begin
-- **************************************************************************************************************************
-- fast interrupt requests (FIRQs) --
cpu_firq(0) <= firq(FIRQ_TWD);
cpu_firq(0) <= firq(FIRQ_TWD); -- highest priority
cpu_firq(1) <= firq(FIRQ_CFS);
cpu_firq(2) <= firq(FIRQ_UART0_RX);
cpu_firq(3) <= firq(FIRQ_UART0_TX);
@ -477,7 +477,7 @@ begin
cpu_firq(12) <= firq(FIRQ_GPTMR);
cpu_firq(13) <= firq(FIRQ_ONEWIRE);
cpu_firq(14) <= firq(FIRQ_SLINK_RX);
cpu_firq(15) <= firq(FIRQ_SLINK_TX);
cpu_firq(15) <= firq(FIRQ_SLINK_TX); -- lowest priority
-- CPU core(s) + optional L1 caches + bus switch --
core_complex_gen:
@ -561,7 +561,7 @@ begin
mem_sync(i) <= dcache_clean(i) and xcache_clean; -- for this hart's perspective only
-- CPU L1 Instruction Cache (I-Cache) -----------------------------------------------------
-- CPU L1 Instruction Cache ---------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_icache_enabled:
if ICACHE_EN generate
@ -590,7 +590,7 @@ begin
end generate;
-- CPU L1 Data Cache (D-Cache) ------------------------------------------------------------
-- CPU L1 Data Cache ----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
neorv32_dcache_enabled:
if DCACHE_EN generate
@ -697,7 +697,6 @@ begin
bus_rsp_o => iodev_rsp(IODEV_DMA),
dma_req_o => dma_req,
dma_rsp_i => dma_rsp,
firq_i => cpu_firq,
irq_o => firq(FIRQ_DMA)
);

View file

@ -1,7 +1,7 @@
// ================================================================================ //
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
// Copyright (c) NEORV32 contributors. //
// Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. //
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
// SPDX-License-Identifier: BSD-3-Clause //
// ================================================================================ //
@ -9,7 +9,6 @@
/**********************************************************************//**
* @file demo_crc/main.c
* @author Stephan Nolting
* @brief CRC demo program.
**************************************************************************/
@ -53,7 +52,6 @@ int main() {
return 1;
}
uint32_t result, polynomial, reference, seed;
neorv32_uart0_printf("Test string: '%s'\n", test_string);
@ -108,13 +106,9 @@ int main() {
neorv32_uart0_printf("[FAILED]\n");
}
// CRC8 example using the DMA
if (neorv32_dma_available() != 0) {
uint32_t cmd;
int rc;
polynomial = 0x07;
reference = 0x5b; // generated by http://www.sunshine2k.de/coding/javascript/crc/crc_js.html
seed = 0x00;
@ -126,22 +120,24 @@ int main() {
neorv32_dma_enable();
// configure transfer type
cmd = DMA_CMD_B2UW | // read source in byte quantities, write destination in WORD(!!) quantities
DMA_CMD_SRC_INC | // auto-increment source address
DMA_CMD_DST_CONST; // constant destination address
// configure DMA descriptor
neorv32_dma_desc_t dma_desc;
dma_desc.src = (uint32_t)(&test_string[0]); // source array base address (data = 0xff)
dma_desc.dst = (uint32_t)(&NEORV32_CRC->DATA); // destination address = CRC data register (32-bit!)
dma_desc.num = sizeof(test_string); // number of elements to transfer
dma_desc.cmd = DMA_CMD_B2UW | // read source in byte quantities, write destination in WORD quantities
DMA_CMD_SRC_INC | // auto-increment source address
DMA_CMD_DST_CONST; // constant destination address
// configure automatic DMA transfer
neorv32_dma_transfer((uint32_t)(&test_string[0]), // source array base address (data = 0xff)
(uint32_t)(&NEORV32_CRC->DATA), // destination address = CRC data register (32-bit!)
sizeof(test_string), // number of elements to transfer
cmd); // transfer type configuration
// trigger DMA transfer
neorv32_dma_transfer(&dma_desc);
// wait for transfer to complete using polling
neorv32_uart0_printf("Waiting for DMA... ");
int rc;
while (1) {
rc = neorv32_dma_status();
if (rc == DMA_STATUS_IDLE) {
if (rc == DMA_STATUS_DONE) {
neorv32_uart0_printf("Transfer done.\n");
break;
}
@ -168,7 +164,6 @@ int main() {
}
neorv32_uart0_printf("\nProgram completed.\n");
return 0;
}

View file

@ -9,7 +9,6 @@
/**********************************************************************//**
* @file demo_dma/main.c
* @author Stephan Nolting
* @brief DMA demo program.
**************************************************************************/
@ -41,8 +40,8 @@ void dma_firq_handler(void);
**************************************************************************/
int main() {
uint32_t cmd;
int rc;
neorv32_dma_desc_t dma_desc;
int dma_rc;
// setup NEORV32 runtime environment
neorv32_rte_setup();
@ -71,7 +70,7 @@ int main() {
// enable DMA
neorv32_dma_enable();
// initialize and data arrays
// initialize test data arrays
dma_src[0] = 0x66778899UL;
dma_src[1] = 0x22334455UL;
dma_src[2] = 0xaabbccddUL;
@ -82,35 +81,35 @@ int main() {
dma_dst[2] = 0;
dma_dst[3] = 0;
asm volatile ("fence"); // re-sync caches
asm volatile ("fence"); // flush caches
// ----------------------------------------------------------
// example 1
// ----------------------------------------------------------
neorv32_uart0_printf("\nExample 1: Manual byte-to-byte block transfer with Endianness conversion using busy wait.\n");
neorv32_uart0_printf("\nExample 1: byte-to-byte block transfer with Endianness conversion using busy wait\n");
// configure transfer type
cmd = DMA_CMD_B2B | // read source in byte quantities, write destination in byte quantities
DMA_CMD_SRC_INC | // auto-increment source address
DMA_CMD_DST_INC | // auto-increment destination address
DMA_CMD_ENDIAN; // change Endianness
// setup DMA transfer descriptor
dma_desc.src = (uint32_t)(&dma_src[0]); // source array base address - byte-aligned
dma_desc.dst = (uint32_t)(&dma_dst[0]); // destination array base address - byte-aligned
dma_desc.num = 16; // number of elements to transfer: 16
dma_desc.cmd = DMA_CMD_B2B | // read source in byte quantities, write destination in byte quantities
DMA_CMD_SRC_INC | // auto-increment source address
DMA_CMD_DST_INC | // auto-increment destination address
DMA_CMD_ENDIAN; // change Endianness
// trigger manual DMA transfer
neorv32_dma_transfer((uint32_t)(&dma_src[0]), // source array base address - byte-aligned!
(uint32_t)(&dma_dst[0]), // destination array base address - byte-aligned!
16, // number of elements to transfer: 16
cmd); // transfer type configuration
// trigger DMA transfer
neorv32_dma_transfer(&dma_desc);
// wait for transfer to complete using polling
neorv32_uart0_printf("Waiting for DMA... ");
while (1) {
rc = neorv32_dma_status();
if (rc == DMA_STATUS_IDLE) {
neorv32_uart0_printf("Transfer done.\n");
dma_rc = neorv32_dma_status();
if (dma_rc == DMA_STATUS_DONE) {
neorv32_uart0_printf("Transfer succeeded!\n");
break;
}
else if ((rc == DMA_STATUS_ERR_RD) || (rc == DMA_STATUS_ERR_WR)) {
else if ((dma_rc == DMA_STATUS_ERR_RD) || (dma_rc == DMA_STATUS_ERR_WR)) {
neorv32_uart0_printf("Transfer failed!\n");
break;
}
@ -136,28 +135,28 @@ int main() {
// ----------------------------------------------------------
// example 2
// ----------------------------------------------------------
neorv32_uart0_printf("\nExample 2: Manual word-to-word one-to-many transfer using busy wait.\n");
neorv32_uart0_printf("\nExample 2: word-to-word one-to-many transfer using busy wait\n");
// configure transfer type
cmd = DMA_CMD_W2W | // read source in word quantities, write destination in word quantities
DMA_CMD_SRC_CONST | // constant source address
DMA_CMD_DST_INC; // auto-increment destination address
// setup DMA transfer descriptor
dma_desc.src = (uint32_t)(&dma_src[0]); // source array base address - byte-aligned
dma_desc.dst = (uint32_t)(&dma_dst[0]); // destination array base address - word-aligned
dma_desc.num = 4; // number of elements to transfer: 4
dma_desc.cmd = DMA_CMD_W2W | // read source in word quantities, write destination in word quantities
DMA_CMD_SRC_CONST | // constant source address
DMA_CMD_DST_INC; // auto-increment destination address
// trigger manual DMA transfer
neorv32_dma_transfer((uint32_t)(&dma_src[0]), // source array base address - word-aligned!
(uint32_t)(&dma_dst[0]), // destination array base address - word-aligned!
4, // number of elements to transfer: 4
cmd); // transfer type configuration
// trigger DMA transfer
neorv32_dma_transfer(&dma_desc);
// wait for transfer to complete using polling
neorv32_uart0_printf("Waiting for DMA... ");
while (1) {
rc = neorv32_dma_status();
if (rc == DMA_STATUS_IDLE) {
neorv32_uart0_printf("Transfer done.\n");
dma_rc = neorv32_dma_status();
if (dma_rc == DMA_STATUS_DONE) {
neorv32_uart0_printf("Transfer succeeded!\n");
break;
}
else if ((rc == DMA_STATUS_ERR_RD) || (rc == DMA_STATUS_ERR_WR)) {
else if ((dma_rc == DMA_STATUS_ERR_RD) || (dma_rc == DMA_STATUS_ERR_WR)) {
neorv32_uart0_printf("Transfer failed!\n");
break;
}
@ -183,22 +182,22 @@ int main() {
// ----------------------------------------------------------
// example 3
// ----------------------------------------------------------
neorv32_uart0_printf("\nExample 3: Manual byte-to-signed-word block transfer using transfer-done interrupt.\n");
neorv32_uart0_printf("\nExample 3: byte-to-signed-word block transfer using transfer-done interrupt\n");
// configure DMA interrupt
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
// configure transfer type
cmd = DMA_CMD_B2SW | // read source in byte quantities, write destination in sign-extended word quantities
DMA_CMD_SRC_INC | // auto-increment source address
DMA_CMD_DST_INC; // auto-increment destination address
// setup DMA transfer descriptor
dma_desc.src = (uint32_t)(&dma_src[0]); // source array base address - byte-aligned
dma_desc.dst = (uint32_t)(&dma_dst[0]); // destination array base address - byte-aligned
dma_desc.num = 16; // number of elements to transfer: 4
dma_desc.cmd = DMA_CMD_B2SW | // read source in byte quantities, write destination in sign-extended word quantities
DMA_CMD_SRC_INC | // auto-increment source address
DMA_CMD_DST_INC; // auto-increment destination address
// trigger manual DMA transfer
neorv32_dma_transfer((uint32_t)(&dma_src[0]), // source array base address - byte-aligned!
(uint32_t)(&dma_dst[0]), // destination array base address - word-aligned!
4, // number of elements to transfer: 4
cmd); // transfer type configuration
// trigger DMA transfer
neorv32_dma_transfer(&dma_desc);
// go to sleep mode, wakeup on DMA transfer-done interrupt
neorv32_cpu_sleep();
@ -206,7 +205,7 @@ int main() {
asm volatile ("fence"); // synchronize caches
// check if transfer was successful
if ((neorv32_dma_status() != DMA_STATUS_IDLE) || // DMA is in idle mode without errors
if ((neorv32_dma_status() != DMA_STATUS_DONE) || // DMA is in idle mode without errors
(dma_dst[0] != 0xffffff99) ||
(dma_dst[1] != 0xffffff88) ||
(dma_dst[2] != 0x00000077) ||
@ -220,94 +219,6 @@ int main() {
show_arrays();
// ----------------------------------------------------------
// example 4
// ----------------------------------------------------------
neorv32_uart0_printf("\nExample 4: Automatic byte-to-byte one-to-many transfer using transfer-done interrupt.\n");
neorv32_uart0_printf( " The GPTMR FIRQ channel is used to trigger the DMA.\n");
if (neorv32_gptmr_available()) { // only execute if GPTMR is available
// configure DMA interrupt
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
// configure GPTMR
neorv32_gptmr_setup(CLK_PRSC_2, // GPTM clock = 1/2 main clock
4096, // counter threshold for triggering IRQ
0); // single-shot mode
// configure transfer type
cmd = DMA_CMD_B2B | // read source in byte quantities, write destination in byte quantities
DMA_CMD_SRC_CONST | // constant source address
DMA_CMD_DST_INC; // auto-increment destination address
// configure automatic DMA transfer
neorv32_dma_transfer_auto((uint32_t)(&dma_src[3]), // source array base address (data = 0xff)
(uint32_t)(&dma_dst[0]), // destination array base address
16, // number of elements to transfer: 16
cmd, // transfer type configuration
GPTMR_FIRQ_PENDING, // trigger transfer on pending GPTMR interrupt
0); // trigger on rising-edge of selected FIRQ channel
// sleep until interrupt (from DMA)
neorv32_cpu_sleep();
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");
}
else {
neorv32_uart0_printf("Transfer succeeded!\n");
}
neorv32_gptmr_disable(); // disable GPTMR
show_arrays();
}
else {
neorv32_uart0_printf("Example skipped as GPTMR is not implemented.\n");
}
// ----------------------------------------------------------
// example 5
// ----------------------------------------------------------
neorv32_uart0_printf("\nExample 5: Automatic UART0 echo without CPU.\n");
neorv32_uart0_printf( " The UART RX FIRQ channel is used to trigger the DMA.\n\n");
// note that NO CPU interrupts are enabled here
neorv32_cpu_csr_write(CSR_MIE, 0);
neorv32_cpu_csr_clr(CSR_MSTATUS, 1 << CSR_MSTATUS_MIE);
// clear UART0 RX FIFO
neorv32_uart0_rx_clear();
// configure DMA-triggering interrupt: UART0 RX
NEORV32_UART0->CTRL |= (uint32_t)(1 << UART_CTRL_IRQ_RX_NEMPTY); // RX FIFO not empty interrupt
// configure transfer type
cmd = DMA_CMD_W2W | // read source in word quantities, write destination in word quantities
DMA_CMD_SRC_CONST | // constant source address
DMA_CMD_DST_CONST; // constant address source
// configure automatic DMA transfer
neorv32_dma_transfer_auto((uint32_t)(&NEORV32_UART0->DATA), // source: UART0 RX data register
(uint32_t)(&NEORV32_UART0->DATA), // destination: UART0 TX data register
1, // number of elements to transfer: 1
cmd, // transfer type configuration
UART0_RX_FIRQ_PENDING, // trigger transfer on pending UART0 RX interrupt
1); // trigger on hihg-level of selected FIRQ channel
// put CPU into eternal sleep mode
neorv32_cpu_sleep();
// should never be reached
neorv32_uart0_printf("\nProgram completed.\n");
return 0;
}
@ -337,6 +248,5 @@ void dma_firq_handler(void) {
neorv32_gptmr_irq_ack(); // 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");
}

View file

@ -1,7 +1,7 @@
// ================================================================================ //
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
// Copyright (c) NEORV32 contributors. //
// Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. //
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
// SPDX-License-Identifier: BSD-3-Clause //
// ================================================================================ //
@ -9,7 +9,6 @@
/**********************************************************************//**
* @file demo_gptmr/main.c
* @author Stephan Nolting
* @brief Simple GPTMR timer-match interrupt example.
**************************************************************************/
@ -63,8 +62,8 @@ int main() {
// install GPTMR interrupt handler
neorv32_rte_handler_install(GPTMR_RTE_ID, gptmr_firq_handler);
// configure timer for 0.5Hz ticks with clock divisor = 8 and set to run in continuous mode
neorv32_gptmr_setup(CLK_PRSC_8, neorv32_sysinfo_get_clk() / (8 * 2), 1);
// configure timer for 0.5Hz ticks with clock divisor = 8
neorv32_gptmr_setup(CLK_PRSC_8, neorv32_sysinfo_get_clk() / (8 * 2));
// enable interrupt
neorv32_cpu_csr_set(CSR_MIE, 1 << GPTMR_FIRQ_ENABLE); // enable GPTMR FIRQ channel

View file

@ -1486,8 +1486,12 @@ int main() {
neorv32_crc_setup(CRC_MODE32, 0x04C11DB7, 0xFFFFFFFF);
// configure and trigger DMA transfer
tmp_a = DMA_CMD_B2UW | DMA_CMD_SRC_INC | DMA_CMD_DST_CONST | DMA_CMD_ENDIAN;
neorv32_dma_transfer((uint32_t)(&dma_src), (uint32_t)(&NEORV32_CRC->DATA), 4, tmp_a);
neorv32_dma_desc_t dma_desc;
dma_desc.src = (uint32_t)(&dma_src);
dma_desc.dst = (uint32_t)(&NEORV32_CRC->DATA);
dma_desc.num = 4;
dma_desc.cmd = DMA_CMD_B2UW | DMA_CMD_SRC_INC | DMA_CMD_DST_CONST | DMA_CMD_ENDIAN;
neorv32_dma_transfer(&dma_desc);
// sleep until interrupt
neorv32_cpu_sleep();
@ -1499,8 +1503,7 @@ int main() {
if ((neorv32_cpu_csr_read(CSR_MCAUSE) == DMA_TRAP_CODE) && // correct interrupt source
(neorv32_crc_get() == 0x31DC476E) && // correct CRC sum
(neorv32_dma_done() != 0) && // DMA has actually attempted a transfer
(neorv32_dma_status() == DMA_STATUS_IDLE)) { // DMA back in idle mode without errors
(neorv32_dma_status() == DMA_STATUS_DONE)) { // DMA transfer completed without errors
test_ok();
}
else {
@ -1573,8 +1576,8 @@ int main() {
// enable GPTMR FIRQ
neorv32_cpu_csr_write(CSR_MIE, 1 << GPTMR_FIRQ_ENABLE);
// match-interrupt after CLK_PRSC_2*THRESHOLD = 2*2 = 8 clock cycles, single-shot mode
neorv32_gptmr_setup(CLK_PRSC_2, 2, 0);
// match-interrupt after CLK_PRSC_2*THRESHOLD = 2*2 = 8 clock cycles
neorv32_gptmr_setup(CLK_PRSC_2, 2);
// wait for interrupt
asm volatile ("nop");

View file

@ -169,7 +169,7 @@ inline int8_t __attribute__ ((always_inline)) neorv32_cpu_load_signed_byte(uint3
inline uint32_t __attribute__ ((always_inline)) neorv32_cpu_csr_read(const int csr_id) {
uint32_t csr_data;
asm volatile ("csrr %[result], %[input_i]" : [result] "=r" (csr_data) : [input_i] "i" (csr_id));
asm volatile ("csrr %[dst], %[id]" : [dst] "=r" (csr_data) : [id] "i" (csr_id));
return csr_data;
}
@ -183,7 +183,7 @@ inline uint32_t __attribute__ ((always_inline)) neorv32_cpu_csr_read(const int c
inline void __attribute__ ((always_inline)) neorv32_cpu_csr_write(const int csr_id, uint32_t data) {
uint32_t csr_data = data;
asm volatile ("csrw %[input_i], %[input_j]" : : [input_i] "i" (csr_id), [input_j] "r" (csr_data));
asm volatile ("csrw %[id], %[src]" : : [id] "i" (csr_id), [src] "r" (csr_data));
}
@ -196,7 +196,7 @@ inline void __attribute__ ((always_inline)) neorv32_cpu_csr_write(const int csr_
inline void __attribute__ ((always_inline)) neorv32_cpu_csr_set(const int csr_id, uint32_t mask) {
uint32_t csr_data = mask;
asm volatile ("csrs %[input_i], %[input_j]" : : [input_i] "i" (csr_id), [input_j] "r" (csr_data));
asm volatile ("csrs %[id], %[src]" : : [id] "i" (csr_id), [src] "r" (csr_data));
}
@ -209,7 +209,7 @@ inline void __attribute__ ((always_inline)) neorv32_cpu_csr_set(const int csr_id
inline void __attribute__ ((always_inline)) neorv32_cpu_csr_clr(const int csr_id, uint32_t mask) {
uint32_t csr_data = mask;
asm volatile ("csrc %[input_i], %[input_j]" : : [input_i] "i" (csr_id), [input_j] "r" (csr_data));
asm volatile ("csrc %[id], %[src]" : : [id] "i" (csr_id), [src] "r" (csr_data));
}

View file

@ -35,16 +35,12 @@ typedef volatile struct __attribute__((packed,aligned(4))) {
/** DMA control and status register bits */
enum NEORV32_DMA_CTRL_enum {
DMA_CTRL_EN = 0, /**< DMA control register(0) (r/w): DMA enable */
DMA_CTRL_AUTO = 1, /**< DMA control register(1) (r/w): Automatic trigger mode enable */
DMA_CTRL_START = 1, /**< DMA control register(1) (-/s): Start configured DMA transfer */
DMA_CTRL_ERROR_RD = 8, /**< DMA control register(8) (r/-): Error during read access; SRC_BASE shows the faulting address */
DMA_CTRL_ERROR_WR = 9, /**< DMA control register(9) (r/-): Error during write access; DST_BASE shows the faulting address */
DMA_CTRL_BUSY = 10, /**< DMA control register(10) (r/-): DMA busy / transfer in progress */
DMA_CTRL_DONE = 11, /**< DMA control register(11) (r/c): A transfer was executed when set */
DMA_CTRL_FIRQ_TYPE = 15, /**< DMA control register(15) (r/w): Trigger on FIRQ rising-edge (0) or high-level (1) */
DMA_CTRL_FIRQ_SEL_LSB = 16, /**< DMA control register(16) (r/w): FIRQ trigger select LSB */
DMA_CTRL_FIRQ_SEL_MSB = 19 /**< DMA control register(19) (r/w): FIRQ trigger select MSB */
DMA_CTRL_ERROR_RD = 28, /**< DMA control register(28) (r/-): Error during read access; SRC_BASE shows the faulting address */
DMA_CTRL_ERROR_WR = 29, /**< DMA control register(29) (r/-): Error during write access; DST_BASE shows the faulting address */
DMA_CTRL_DONE = 30, /**< DMA control register(30) (r/c): A transfer has been executed when set */
DMA_CTRL_BUSY = 31 /**< DMA control register(32) (r/-): DMA busy / transfer in progress */
};
/** DMA transfer type bits */
@ -87,10 +83,22 @@ enum NEORV32_DMA_STATUS_enum {
DMA_STATUS_ERR_WR = -2, /**< write access error during last transfer (-2) */
DMA_STATUS_ERR_RD = -1, /**< read access error during last transfer (-1) */
DMA_STATUS_IDLE = 0, /**< DMA idle (0) */
DMA_STATUS_BUSY = 1 /**< DMA busy (1) */
DMA_STATUS_BUSY = 1, /**< DMA busy (1) */
DMA_STATUS_DONE = 2 /**< transfer done (2) */
};
/**********************************************************************//**
* DMA transfer descriptor
**************************************************************************/
typedef struct __attribute__((packed,aligned(4))) {
uint32_t src; /**< 32-bit source base address */
uint32_t dst; /**< 32-bit destination base address */
uint32_t num; /**< 24-bit (LSB-aligned) number of elements to transfer */
uint32_t cmd; /**< transfer type */
} neorv32_dma_desc_t;
/**********************************************************************//**
* @name Prototypes
**************************************************************************/
@ -98,10 +106,8 @@ enum NEORV32_DMA_STATUS_enum {
int neorv32_dma_available(void);
void neorv32_dma_enable(void);
void neorv32_dma_disable(void);
void neorv32_dma_transfer(uint32_t base_src, uint32_t base_dst, uint32_t num, uint32_t config);
void neorv32_dma_transfer_auto(uint32_t base_src, uint32_t base_dst, uint32_t num, uint32_t config, int firq_sel, int firq_type);
void neorv32_dma_transfer(neorv32_dma_desc_t *desc);
int neorv32_dma_status(void);
int neorv32_dma_done(void);
/**@}*/

View file

@ -23,8 +23,8 @@
/**@{*/
/** GPTMR module prototype */
typedef volatile struct __attribute__((packed,aligned(4))) {
uint32_t CTRL; /**< offset 0: control register (#NEORV32_GPTMR_CTRL_enum) */
uint32_t THRES; /**< offset 4: threshold register */
uint32_t CTRL; /**< offset 0: control register (#NEORV32_GPTMR_CTRL_enum) */
uint32_t THRES; /**< offset 4: threshold register */
const uint32_t COUNT; /**< offset 8: counter register, read-only */
} neorv32_gptmr_t;
@ -37,7 +37,6 @@ enum NEORV32_GPTMR_CTRL_enum {
GPTMR_CTRL_PRSC0 = 1, /**< GPTMR control register(1) (r/w): Clock prescaler select bit 0 */
GPTMR_CTRL_PRSC1 = 2, /**< GPTMR control register(2) (r/w): Clock prescaler select bit 1 */
GPTMR_CTRL_PRSC2 = 3, /**< GPTMR control register(3) (r/w): Clock prescaler select bit 2 */
GPTMR_CTRL_MODE = 4, /**< GPTMR control register(4) (r/w): Operation mode (0=single-shot, 1=continuous) */
GPTMR_CTRL_IRQ_CLR = 30, /**< GPTMR control register(30) (-/w): Set to clear timer-match interrupt */
GPTMR_CTRL_IRQ_PND = 31, /**< GPTMR control register(31) (r/-): Timer-match interrupt pending */
@ -50,7 +49,7 @@ enum NEORV32_GPTMR_CTRL_enum {
**************************************************************************/
/**@{*/
int neorv32_gptmr_available(void);
void neorv32_gptmr_setup(int prsc, uint32_t threshold, int cont_mode);
void neorv32_gptmr_setup(int prsc, uint32_t threshold);
void neorv32_gptmr_disable(void);
void neorv32_gptmr_enable(void);
void neorv32_gptmr_irq_ack(void);

View file

@ -56,37 +56,12 @@ void neorv32_dma_disable(void) {
* @param[in] num Number of elements to transfer (24-bit).
* @param[in] config Transfer type configuration/commands.
**************************************************************************/
void neorv32_dma_transfer(uint32_t base_src, uint32_t base_dst, uint32_t num, uint32_t config) {
void neorv32_dma_transfer(neorv32_dma_desc_t *desc) {
NEORV32_DMA->CTRL &= ~((uint32_t)(1 << DMA_CTRL_AUTO)); // manual transfer trigger
NEORV32_DMA->SRC_BASE = base_src;
NEORV32_DMA->DST_BASE = base_dst;
NEORV32_DMA->TTYPE = (num & 0x00ffffffUL) | (config & 0xff000000UL); // trigger transfer
}
/**********************************************************************//**
* Configure automatic DMA transfer (triggered by CPU FIRQ).
*
* @param[in] base_src Source base address (has to be aligned to source data type!).
* @param[in] base_dst Destination base address (has to be aligned to destination data type!).
* @param[in] num Number of elements to transfer (24-bit).
* @param[in] config Transfer type configuration/commands.
* @param[in] firq_sel FIRQ trigger select (#NEORV32_CSR_MIP_enum); only FIRQ0..FIRQ15 = 16..31.
* @param[in] firq_type Trigger on rising-edge (0) or high-level (1) of FIRQ channel.
**************************************************************************/
void neorv32_dma_transfer_auto(uint32_t base_src, uint32_t base_dst, uint32_t num, uint32_t config, int firq_sel, int firq_type) {
uint32_t tmp = NEORV32_DMA->CTRL;
tmp |= (uint32_t)(1 << DMA_CTRL_AUTO); // automatic transfer trigger
tmp &= ~(0xf << DMA_CTRL_FIRQ_SEL_LSB); // clear current FIRQ select
tmp |= (uint32_t)((firq_sel & 0xf) << DMA_CTRL_FIRQ_SEL_LSB); // set new FIRQ select
tmp |= (uint32_t)((firq_type & 1) << DMA_CTRL_FIRQ_TYPE); // FIRQ trigger type
NEORV32_DMA->CTRL = tmp;
NEORV32_DMA->SRC_BASE = base_src;
NEORV32_DMA->DST_BASE = base_dst;
NEORV32_DMA->TTYPE = (num & 0x00ffffffUL) | (config & 0xff000000UL);
NEORV32_DMA->SRC_BASE = desc->src;
NEORV32_DMA->DST_BASE = desc->dst;
NEORV32_DMA->TTYPE = (desc->num & 0x00ffffffUL) | (desc->cmd & 0xff000000UL);
NEORV32_DMA->CTRL |= 1<<DMA_CTRL_START;
}
@ -108,24 +83,10 @@ int neorv32_dma_status(void) {
else if (tmp & (1 << DMA_CTRL_BUSY)) {
return DMA_STATUS_BUSY; // transfer in progress
}
else if (tmp & (1 << DMA_CTRL_DONE)) {
return DMA_STATUS_DONE; // transfer done
}
else {
return DMA_STATUS_IDLE; // idle
}
}
/**********************************************************************//**
* Check if a transfer has actually been executed.
*
* @return 0 if no transfer was executed, 1 if a transfer has actually been executed.
* Use neorv32_dma_status(void) to check if there was an error during that transfer.
**************************************************************************/
int neorv32_dma_done(void) {
if (NEORV32_DMA->CTRL & (1 << DMA_CTRL_DONE)) {
return 1; // there was a transfer
}
else {
return 0; // no transfer executed
}
}

View file

@ -1,7 +1,7 @@
// ================================================================================ //
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
// Copyright (c) NEORV32 contributors. //
// Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. //
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
// SPDX-License-Identifier: BSD-3-Clause //
// ================================================================================ //
@ -9,10 +9,6 @@
/**
* @file neorv32_gptmr.c
* @brief General purpose timer (GPTMR) HW driver source file.
*
* @note These functions should only be used if the GPTMR unit was synthesized (IO_GPTMR_EN = true).
*
* @see https://stnolting.github.io/neorv32/sw/files.html
*/
#include <neorv32.h>
@ -39,17 +35,15 @@ int neorv32_gptmr_available(void) {
*
* @param[in] prsc Clock prescaler select (0..7). See #NEORV32_CLOCK_PRSC_enum.
* @param[in] threshold Threshold value, counter will reset to zero when reaching this.
* @param[in] cont_mode Set to operate timer in continuous mode (instead of single-shot mode).
**************************************************************************/
void neorv32_gptmr_setup(int prsc, uint32_t threshold, int cont_mode) {
void neorv32_gptmr_setup(int prsc, uint32_t threshold) {
NEORV32_GPTMR->CTRL = 0; // reset module
NEORV32_GPTMR->THRES = threshold;
uint32_t tmp = 0;
tmp |= (uint32_t)(1 & 0x01) << GPTMR_CTRL_EN;
tmp |= (uint32_t)(prsc & 0x07) << GPTMR_CTRL_PRSC0;
tmp |= (uint32_t)(cont_mode & 0x01) << GPTMR_CTRL_MODE;
tmp |= (uint32_t)(1 & 0x01) << GPTMR_CTRL_EN;
tmp |= (uint32_t)(prsc & 0x07) << GPTMR_CTRL_PRSC0;
NEORV32_GPTMR->CTRL = tmp;
}

View file

@ -408,42 +408,32 @@
<description>DMA enable flag</description>
</field>
<field>
<name>DMA_CTRL_AUTO</name>
<name>DMA_CTRL_START</name>
<bitRange>[1:1]</bitRange>
<description>Enable automatic transfer trigger (FIRQ-triggered)</description>
<description>Start programmed DMA transfer</description>
</field>
<field>
<name>DMA_CTRL_ERROR_RD</name>
<bitRange>[8:8]</bitRange>
<bitRange>[28:28]</bitRange>
<access>read-only</access>
<description>Error during last read access</description>
</field>
<field>
<name>DMA_CTRL_ERROR_WR</name>
<bitRange>[9:9]</bitRange>
<bitRange>[29:29]</bitRange>
<access>read-only</access>
<description>Error during last write access</description>
</field>
<field>
<name>DMA_CTRL_BUSY</name>
<bitRange>[10:10]</bitRange>
<access>read-only</access>
<description>DMA transfer in progress</description>
</field>
<field>
<name>DMA_CTRL_DONE</name>
<bitRange>[11:11]</bitRange>
<description>DMA transfer done; auto-clears on write access</description>
<bitRange>[30:30]</bitRange>
<description>Transfer done; auto-clears on write access</description>
</field>
<field>
<name>DMA_CTRL_FIRQ_TYPE</name>
<bitRange>[15:15]</bitRange>
<description>Trigger on rising-edge (0) or high-level (1) or selected FIRQ channel</description>
</field>
<field>
<name>DMA_CTRL_FIRQ_SEL</name>
<bitRange>[19:16]</bitRange>
<description>FIRQ trigger select</description>
<name>DMA_CTRL_BUSY</name>
<bitRange>[31:31]</bitRange>
<access>read-only</access>
<description>Transfer in progress</description>
</field>
</fields>
</register>
@ -609,11 +599,6 @@
<bitRange>[3:1]</bitRange>
<description>Clock prescaler select</description>
</field>
<field>
<name>GPTMR_CTRL_MODE</name>
<bitRange>[4:4]</bitRange>
<description>Operation mode (0=single-shot, 1=continuous)</description>
</field>
<field>
<name>GPTMR_CTRL_IRQ_CLR</name>
<bitRange>[30:30]</bitRange>