Skip to content

Commit

Permalink
[DMA] use FIRQ select instead of FIRQ mask (#877)
Browse files Browse the repository at this point in the history
  • Loading branch information
stnolting authored Apr 17, 2024
2 parents 229b8f1 + 5dc4638 commit 183fa56
Show file tree
Hide file tree
Showing 13 changed files with 73 additions and 61 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12

| Date | Version | Comment | Link |
|:----:|:-------:|:--------|:----:|
| 16.04.2024 | 1.9.8.4 | :warning: use a 4-bit FIRQ select instead of a 16-bit FIRQ mask for DMA auto-trigger configuration | [#877](https://github.com/stnolting/neorv32/pull/877) |
| 15.04.2024 | 1.9.8.3 | :warning: simplify XBUS gateway logic and configuration generics; only "pipelined Wishbone" protocol is supported now | [#876](https://github.com/stnolting/neorv32/pull/876) |
| 14.04.2024 | 1.9.8.2 | :warning: rename SLINK data interface registers; minor CPU control logic/area optimizations | [#874](https://github.com/stnolting/neorv32/pull/874) |
| 13.04.2024 | 1.9.8.1 | minor rtl code cleanups and optimizations | [#872](https://github.com/stnolting/neorv32/pull/872) |
Expand Down
2 changes: 1 addition & 1 deletion docs/datasheet/soc.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ Some interfaces (like the TWI and the 1-Wire bus) require tri-state drivers in t
| `jtag_tms_i` | 1 | in | `'L'` | mode select
5+^| **<<_processor_external_bus_interface_xbus>>**
| `xbus_adr_o` | 32 | out | - | destination address
| `xbus_dat_i` | 32 | in | `'L'` | write data
| `xbus_dat_o` | 32 | out | - | read data
| `xbus_we_o` | 1 | out | - | write enable ('0' = read transfer)
| `xbus_sel_o` | 4 | out | - | byte enable
| `xbus_stb_o` | 1 | out | - | strobe
| `xbus_cyc_o` | 1 | out | - | valid cycle
| `xbus_dat_i` | 32 | in | `'L'` | write data
| `xbus_ack_i` | 1 | in | `'L'` | transfer acknowledge
| `xbus_err_i` | 1 | in | `'L'` | transfer error
5+^| **<<_stream_link_interface_slink>>**
Expand Down
46 changes: 23 additions & 23 deletions docs/datasheet/soc_dma.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
[cols="<3,<3,<4"]
[frame="topbot",grid="none"]
|=======================
| Hardware source file(s): | neorv32_dma.vhd |
| Software driver file(s): | neorv32_dma.c |
| | neorv32_dma.h |
| Top entity port: | none |
| Configuration generics: | `IO_DMA_EN` | implement DMA when `true`
| CPU interrupts: | fast IRQ channel 10 | DMA transfer done (see <<_processor_interrupts>>)
| Hardware source files: | neorv32_dma.vhd |
| Software driver files: | neorv32_dma.c |
| | neorv32_dma.h |
| Top entity ports: | none |
| Configuration generics: | `IO_DMA_EN` | implement DMA when `true`
| CPU interrupts: | fast IRQ channel 10 | DMA transfer done (see <<_processor_interrupts>>)
|=======================


Expand Down Expand Up @@ -98,19 +98,18 @@ data quantity has to be set to **word** (32-bit) since all IO registers can only
**Automatic Trigger**

As an alternative to the manual trigger mode, the DMA can be configured to **automatic trigger mode** starting a pre-configured
transfer if a specific processor-internal peripheral issues an interrupt request. The automatic trigger mode is enabled by
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 actual trigger is configured via the control register `DMA_CTRL_FIRQ_MASK`. These bits reflect the state of the CPU's
<<_mip>> CSR showing any pending fast interrupt requests (for a full list see <<_neorv32_specific_fast_interrupt_requests>>).
The same bit definitions/locations as for the <<_mip>> and <<_mie>> CPU CSRs are used.
If any of the enabled sources issues an interrupt the DMA will start the pre-configured transfer (note that all enabled
sources are logically OR-ed).
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.

.FIRQ Trigger
[NOTE]
The DMA transfer will start if a **rising edge** is detected on _any_ of the enabled FIRQ source channels.
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.


**Memory Barrier / Fence Operation**
Expand All @@ -135,16 +134,17 @@ register).
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.10+<| `0xffffed00` .10+<| `CTRL` <|`0` `DMA_CTRL_EN` ^| r/w <| DMA module enable
<|`1` `DMA_CTRL_AUTO` ^| r/w <| Enable automatic mode (FIRQ-triggered)
<|`2` `DMA_CTRL_FENCE` ^| r/w <| Issue a downstream FENCE operation when DMA transfer completes (without errors)
<|`7:3` _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
<|`15:12` _reserved_ ^| r/- <| reserved, read as zero
<|`31:16` `DMA_CTRL_FIRQ_MASK_MSB : DMA_CTRL_FIRQ_MASK_LSB` ^| r/w <| FIRQ trigger mask (same bits as in <<_mip>>)
.11+<| `0xffffed00` .11+<| `CTRL` <|`0` `DMA_CTRL_EN` ^| r/w <| DMA module enable
<|`1` `DMA_CTRL_AUTO` ^| r/w <| Enable automatic mode (FIRQ-triggered)
<|`2` `DMA_CTRL_FENCE` ^| r/w <| Issue a downstream FENCE operation when DMA transfer completes (without errors)
<|`7:3` _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
<|`15:12` _reserved_ ^| r/- <| reserved, read as zero
<|`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
| `0xffffed04` | `SRC_BASE` |`31:0` | r/w | Source base address (shows the last-accessed source address when read)
| `0xffffed08` | `DST_BASE` |`31:0` | r/w | Destination base address (shows the last-accessed destination address when read)
.6+<| `0xffffed0c` .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)
Expand Down
16 changes: 8 additions & 8 deletions rtl/core/neorv32_dma.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ architecture neorv32_dma_rtl of neorv32_dma is
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_mask_lsb_c : natural := 16; -- r/w: FIRQ trigger mask LSB
constant ctrl_firq_mask_msb_c : natural := 31; -- r/w: FIRQ trigger mask MSB
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

-- transfer quantities --
constant qsel_b2b_c : std_ulogic_vector(1 downto 0) := "00"; -- byte to byte
Expand All @@ -64,7 +64,7 @@ architecture neorv32_dma_rtl of neorv32_dma is
enable : std_ulogic; -- DMA enabled when set
auto : std_ulogic; -- FIRQ-driven auto transfer
fence : std_ulogic; -- issue FENCE operation when DMA is done
firq_mask : std_ulogic_vector(15 downto 0); -- FIRQ trigger mask
firq_sel : std_ulogic_vector(03 downto 0); -- FIRQ trigger select
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
Expand Down Expand Up @@ -116,7 +116,7 @@ begin
config.enable <= '0';
config.auto <= '0';
config.fence <= '0';
config.firq_mask <= (others => '0');
config.firq_sel <= (others => '0');
config.src_base <= (others => '0');
config.dst_base <= (others => '0');
config.num <= (others => '0');
Expand All @@ -143,7 +143,7 @@ begin
config.auto <= bus_req_i.data(ctrl_auto_c);
config.fence <= bus_req_i.data(ctrl_fence_c);
config.done <= '0'; -- clear on write access
config.firq_mask <= bus_req_i.data(ctrl_firq_mask_msb_c downto ctrl_firq_mask_lsb_c);
config.firq_sel <= bus_req_i.data(ctrl_firq_sel_msb_c downto ctrl_firq_sel_lsb_c);
end if;
if (bus_req_i.addr(3 downto 2) = "01") then -- source base address
config.src_base <= bus_req_i.data;
Expand All @@ -169,7 +169,7 @@ begin
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) <= config.done;
bus_rsp_o.data(ctrl_firq_mask_msb_c downto ctrl_firq_mask_lsb_c) <= config.firq_mask;
bus_rsp_o.data(ctrl_firq_sel_msb_c downto ctrl_firq_sel_lsb_c) <= config.firq_sel;
when "01" => -- address of last read access
bus_rsp_o.data <= engine.src_addr;
when "10" => -- address of last write access
Expand Down Expand Up @@ -205,8 +205,8 @@ begin
end if;
end process automatic_trigger;

-- logical OR of all enabled trigger FIRQs --
match <= or_reduce_f(firq_buf and config.firq_mask);
-- select a single FIRQ --
match <= firq_buf(to_integer(unsigned(config.firq_sel)));


-- Bus Access Engine ----------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions rtl/core/neorv32_package.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ package neorv32_package is

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

Expand Down Expand Up @@ -816,12 +816,12 @@ package neorv32_package is
jtag_tms_i : in std_ulogic := 'L';
-- External bus interface (available if XBUS_EN = true) --
xbus_adr_o : out std_ulogic_vector(31 downto 0);
xbus_dat_i : in std_ulogic_vector(31 downto 0) := (others => 'L');
xbus_dat_o : out std_ulogic_vector(31 downto 0);
xbus_we_o : out std_ulogic;
xbus_sel_o : out std_ulogic_vector(03 downto 0);
xbus_stb_o : out std_ulogic;
xbus_cyc_o : out std_ulogic;
xbus_dat_i : in std_ulogic_vector(31 downto 0) := (others => 'L');
xbus_ack_i : in std_ulogic := 'L';
xbus_err_i : in std_ulogic := 'L';
-- Stream Link Interface (available if IO_SLINK_EN = true) --
Expand Down
2 changes: 1 addition & 1 deletion rtl/core/neorv32_top.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,12 @@ entity neorv32_top is

-- External bus interface (available if XBUS_EN = true) --
xbus_adr_o : out std_ulogic_vector(31 downto 0); -- address
xbus_dat_i : in std_ulogic_vector(31 downto 0) := (others => 'L'); -- read data
xbus_dat_o : out std_ulogic_vector(31 downto 0); -- write data
xbus_we_o : out std_ulogic; -- read/write
xbus_sel_o : out std_ulogic_vector(03 downto 0); -- byte enable
xbus_stb_o : out std_ulogic; -- strobe
xbus_cyc_o : out std_ulogic; -- valid cycle
xbus_dat_i : in std_ulogic_vector(31 downto 0) := (others => 'L'); -- read data
xbus_ack_i : in std_ulogic := 'L'; -- transfer acknowledge
xbus_err_i : in std_ulogic := 'L'; -- transfer error

Expand Down
2 changes: 1 addition & 1 deletion rtl/system_integration/neorv32_litex_core_complex.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,12 @@ begin
jtag_tms_i => jtag_tms_i, -- mode select
-- External bus interface --
xbus_adr_o => wb_adr_o, -- address
xbus_dat_i => wb_dat_i, -- read data
xbus_dat_o => wb_dat_o, -- write data
xbus_we_o => wb_we_o, -- read/write
xbus_sel_o => wb_sel_o, -- byte enable
xbus_stb_o => open, -- strobe
xbus_cyc_o => wb_cyc, -- valid cycle
xbus_dat_i => wb_dat_i, -- read data
xbus_ack_i => wb_ack_i, -- transfer acknowledge
xbus_err_i => wb_err_i, -- transfer error
-- CPU Interrupts --
Expand Down
2 changes: 1 addition & 1 deletion sim/neorv32_tb.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -295,12 +295,12 @@ begin
jtag_tms_i => '0', -- mode select
-- External bus interface (available if XBUS_EN = true) --
xbus_adr_o => wb_cpu.addr, -- address
xbus_dat_i => wb_cpu.rdata, -- read data
xbus_dat_o => wb_cpu.wdata, -- write data
xbus_we_o => wb_cpu.we, -- read/write
xbus_sel_o => wb_cpu.sel, -- byte enable
xbus_stb_o => wb_cpu.stb, -- strobe
xbus_cyc_o => wb_cpu.cyc, -- valid cycle
xbus_dat_i => wb_cpu.rdata, -- read data
xbus_ack_i => wb_cpu.ack, -- transfer acknowledge
xbus_err_i => wb_cpu.err, -- transfer error
-- Stream Link Interface (available if IO_SLINK_EN = true) --
Expand Down
2 changes: 1 addition & 1 deletion sim/simple/neorv32_tb.simple.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -271,12 +271,12 @@ begin
jtag_tms_i => '0', -- mode select
-- External bus interface (available if XBUS_EN = true) --
xbus_adr_o => wb_cpu.addr, -- address
xbus_dat_i => wb_cpu.rdata, -- read data
xbus_dat_o => wb_cpu.wdata, -- write data
xbus_we_o => wb_cpu.we, -- read/write
xbus_sel_o => wb_cpu.sel, -- byte enable
xbus_stb_o => wb_cpu.stb, -- strobe
xbus_cyc_o => wb_cpu.cyc, -- valid cycle
xbus_dat_i => wb_cpu.rdata, -- read data
xbus_ack_i => wb_cpu.ack, -- transfer acknowledge
xbus_err_i => wb_cpu.err, -- transfer error
-- Stream Link Interface (available if IO_SLINK_EN = true) --
Expand Down
22 changes: 17 additions & 5 deletions sw/example/demo_dma/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,9 @@ int main() {
(dma_dst[3] != 0xffee1100)) {
neorv32_uart0_printf("Incorrect DST data!\n");
}
else {
neorv32_uart0_printf("Transfer succeeded!\n");
}

show_arrays();

Expand Down Expand Up @@ -199,6 +202,9 @@ int main() {
(dma_dst[3] != 0x66778899)) {
neorv32_uart0_printf("Incorrect DST data!\n");
}
else {
neorv32_uart0_printf("Transfer succeeded!\n");
}

show_arrays();

Expand Down Expand Up @@ -236,6 +242,9 @@ int main() {
(dma_dst[3] != 0x00000066)) {
neorv32_uart0_printf("Transfer failed!\n");
}
else {
neorv32_uart0_printf("Transfer succeeded!\n");
}

show_arrays();

Expand All @@ -262,11 +271,11 @@ int main() {
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
1 << GPTMR_FIRQ_PENDING); // trigger transfer on pending GPTMR interrupt
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

// sleep until interrupt (from DMA)
neorv32_cpu_sleep();
Expand All @@ -281,6 +290,9 @@ int main() {
(dma_dst[3] != 0xffffffff)) {
neorv32_uart0_printf("Transfer failed!\n");
}
else {
neorv32_uart0_printf("Transfer succeeded!\n");
}

show_arrays();
}
Expand Down
20 changes: 10 additions & 10 deletions sw/lib/include/neorv32_dma.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,17 @@ 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_FENCE = 2, /**< DMA control register(2) (r/w): Issue FENCE downstream operation when DMA transfer is completed */
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_FENCE = 2, /**< DMA control register(2) (r/w): Issue FENCE downstream operation when DMA transfer is completed */

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_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_MASK_LSB = 16, /**< DMA control register(16) (r/w): FIRQ trigger mask LSB */
DMA_CTRL_FIRQ_MASK_MSB = 31 /**< DMA control register(31) (r/w): FIRQ trigger mask MSB */
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 transfer type bits */
Expand Down Expand Up @@ -127,7 +127,7 @@ void neorv32_dma_disable(void);
void neorv32_dma_fence_enable(void);
void neorv32_dma_fence_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, uint32_t firq_mask);
void neorv32_dma_transfer_auto(uint32_t base_src, uint32_t base_dst, uint32_t num, uint32_t config, int firq_sel);
int neorv32_dma_status(void);
int neorv32_dma_done(void);
/**@}*/
Expand Down
Loading

0 comments on commit 183fa56

Please sign in to comment.