mirror of
https://github.com/stnolting/neorv32.git
synced 2025-04-24 06:07:52 -04:00
⚠️ rework DMA and GPTMR (#1194)
This commit is contained in:
commit
449dd87766
18 changed files with 235 additions and 460 deletions
|
@ -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) |
|
||||
|
|
|
@ -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
|
||||
|=======================
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
/**@}*/
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue