diff --git a/CHANGELOG.md b/CHANGELOG.md index a48785498..22be90b62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12 | Date | Version | Comment | Link | |:----:|:-------:|:--------|:----:| +| 15.01.2023 | 1.9.4.13 | allow the DMA to issue a FENCE operation | [#807](https://github.com/stnolting/neorv32/pull/807) | | 14.02.2024 | 1.9.4.12 | :bug: close another illegal compressed instruction encoding loophole | [#806](https://github.com/stnolting/neorv32/pull/806) | | 11.02.2024 | 1.9.4.11 | :bug: fix several FPU bugs and design flaws | [#794](https://github.com/stnolting/neorv32/pull/794) | | 11.02.2024 | 1.9.4.10 | minor additions to previous version (1.9.4.9): fix HPM configuration read-back | [#804](https://github.com/stnolting/neorv32/pull/804) | diff --git a/docs/datasheet/soc_dma.adoc b/docs/datasheet/soc_dma.adoc index 2cf6559f5..da608484d 100644 --- a/docs/datasheet/soc_dma.adoc +++ b/docs/datasheet/soc_dma.adoc @@ -113,6 +113,13 @@ sources are logically OR-ed). The DMA transfer will start if a **rising edge** is detected on _any_ of the enabled FIRQ source channels. +**Memory Barrier / Fence Operation** + +Optionally, the DMA can issue a FENCE request to the downstream memory system when a transfer has been completed +without errors. This can be used to re-sync caches (flush and reload) and buffers to maintain data coherency. +This automatic fencing is enabled by the setting the control register's `DMA_CTRL_FENCE` bit. + + **DMA Interrupt** The DMA features a single CPU interrupt that is triggered when the programmed transfer has completed. This @@ -127,15 +134,16 @@ explicitly cleared again by writing zero to the according <<_mip>> CSR bit. [options="header",grid="all"] |======================= | Address | Name [C] | Bit(s), Name [C] | R/W | Function -.9+<| `0xffffed00` .9+<| `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 - <|`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>>) +.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>>) | `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) diff --git a/rtl/core/neorv32_dma.vhd b/rtl/core/neorv32_dma.vhd index b96217ee7..70a26bd28 100644 --- a/rtl/core/neorv32_dma.vhd +++ b/rtl/core/neorv32_dma.vhd @@ -69,6 +69,7 @@ architecture neorv32_dma_rtl of neorv32_dma is -- control and status register bits -- constant ctrl_en_c : natural := 0; -- r/w: DMA enable constant ctrl_auto_c : natural := 1; -- r/w: enable FIRQ-triggered transfer + constant ctrl_fence_c : natural := 3; -- r/w: issue FENCE operation when DMA is done -- constant ctrl_error_rd_c : natural := 8; -- r/-: error during read transfer constant ctrl_error_wr_c : natural := 9; -- r/-: error during write transfer @@ -88,6 +89,7 @@ architecture neorv32_dma_rtl of neorv32_dma is type config_t is record 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 src_base : std_ulogic_vector(31 downto 0); -- source base address dst_base : std_ulogic_vector(31 downto 0); -- destination base address @@ -139,6 +141,7 @@ begin bus_rsp_o.data <= (others => '0'); config.enable <= '0'; config.auto <= '0'; + config.fence <= '0'; config.firq_mask <= (others => '0'); config.src_base <= (others => '0'); config.dst_base <= (others => '0'); @@ -166,6 +169,7 @@ begin if (bus_req_i.addr(3 downto 2) = "00") then -- control and status register config.enable <= bus_req_i.data(ctrl_en_c); config.auto <= bus_req_i.data(ctrl_auto_c); + 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); end if; @@ -190,6 +194,7 @@ begin when "00" => -- control and status register bus_rsp_o.data(ctrl_en_c) <= config.enable; bus_rsp_o.data(ctrl_auto_c) <= config.auto; + bus_rsp_o.data(ctrl_fence_c) <= config.fence; 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; @@ -328,7 +333,7 @@ begin dma_req_o.src <= '0'; -- source = data access dma_req_o.addr <= engine.src_addr when (engine.state = S_READ) else engine.dst_addr; dma_req_o.rvso <= '0'; -- no reservation set operation possible - dma_req_o.fence <= '0'; -- DMA cannot trigger a fence + dma_req_o.fence <= config.enable and config.fence and engine.done; -- issue FENCE operation when transfer is done -- address increment -- address_inc: process(config.qsel) diff --git a/rtl/core/neorv32_package.vhd b/rtl/core/neorv32_package.vhd index 5a90692e7..961963d62 100644 --- a/rtl/core/neorv32_package.vhd +++ b/rtl/core/neorv32_package.vhd @@ -53,7 +53,7 @@ package neorv32_package is -- Architecture Constants ----------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- - constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090412"; -- hardware version + constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090413"; -- hardware version constant archid_c : natural := 19; -- official RISC-V architecture ID constant XLEN : natural := 32; -- native data path width diff --git a/sw/example/demo_dma/main.c b/sw/example/demo_dma/main.c index a8f8b017d..f6376cc80 100644 --- a/sw/example/demo_dma/main.c +++ b/sw/example/demo_dma/main.c @@ -96,6 +96,10 @@ int main() { // enable DMA neorv32_dma_enable(); + // issue a FENCE operation when the DMA transfer completes (without errors); this + // will re-sync /flush and reload) all downstream caches + neorv32_dma_fence_enable(); + // initialize and data arrays dma_src[0] = 0x66778899UL; dma_src[1] = 0x22334455UL; @@ -107,7 +111,7 @@ int main() { dma_dst[2] = 0; dma_dst[3] = 0; - asm volatile ("fence"); // make sure main memory is sync with d-cache + asm volatile ("fence"); // re-sync caches // ---------------------------------------------------------- @@ -264,7 +268,7 @@ int main() { **************************************************************************/ void show_arrays(void) { - asm volatile ("fence"); // make sure main memory is sync with d-cache + asm volatile ("fence"); // re-sync caches neorv32_uart0_printf("---------------------------\n"); neorv32_uart0_printf(" SRC DST\n"); neorv32_uart0_printf("[0] 0x%x 0x%x\n", dma_src[0], dma_dst[0]); diff --git a/sw/example/processor_check/main.c b/sw/example/processor_check/main.c index 682057018..038a8ebe5 100644 --- a/sw/example/processor_check/main.c +++ b/sw/example/processor_check/main.c @@ -1470,8 +1470,9 @@ int main() { if (NEORV32_SYSINFO->SOC & (1 << SYSINFO_SOC_IO_DMA)) { cnt_test++; - // enable DMA and according FIRQ channel + // enable DMA, auto-fencing and according FIRQ channel neorv32_dma_enable(); + neorv32_dma_fence_enable(); neorv32_cpu_irq_enable(DMA_FIRQ_ENABLE); // setup source data diff --git a/sw/lib/include/neorv32_dma.h b/sw/lib/include/neorv32_dma.h index c35f428e3..cdb4bfa6d 100644 --- a/sw/lib/include/neorv32_dma.h +++ b/sw/lib/include/neorv32_dma.h @@ -3,7 +3,7 @@ // # ********************************************************************************************* # // # BSD 3-Clause License # // # # -// # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +// # Copyright (c) 2024, Stephan Nolting. All rights reserved. # // # # // # Redistribution and use in source and binary forms, with or without modification, are # // # permitted provided that the following conditions are met: # @@ -62,6 +62,7 @@ typedef volatile struct __attribute__((packed,aligned(4))) { 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_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 */ @@ -123,6 +124,8 @@ enum NEORV32_DMA_STATUS_enum { int neorv32_dma_available(void); void neorv32_dma_enable(void); 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); int neorv32_dma_status(void); diff --git a/sw/lib/source/neorv32_dma.c b/sw/lib/source/neorv32_dma.c index 84b3ed2a6..67e900483 100644 --- a/sw/lib/source/neorv32_dma.c +++ b/sw/lib/source/neorv32_dma.c @@ -3,7 +3,7 @@ // # ********************************************************************************************* # // # BSD 3-Clause License # // # # -// # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +// # Copyright (c) 2024, Stephan Nolting. All rights reserved. # // # # // # Redistribution and use in source and binary forms, with or without modification, are # // # permitted provided that the following conditions are met: # @@ -78,6 +78,25 @@ void neorv32_dma_disable(void) { } +/**********************************************************************//** + * Enable memory barrier (fence): issue a FENCE operation when DMA transfer + * completes without errors. + **************************************************************************/ +void neorv32_dma_fence_enable(void) { + + NEORV32_DMA->CTRL |= (uint32_t)(1 << DMA_CTRL_FENCE); +} + + +/**********************************************************************//** + * Disable memory barrier (fence). + **************************************************************************/ +void neorv32_dma_fence_disable(void) { + + NEORV32_DMA->CTRL &= ~((uint32_t)(1 << DMA_CTRL_FENCE)); +} + + /**********************************************************************//** * Trigger manual DMA transfer. * diff --git a/sw/svd/neorv32.svd b/sw/svd/neorv32.svd index 1f6f5d928..8260dd808 100644 --- a/sw/svd/neorv32.svd +++ b/sw/svd/neorv32.svd @@ -371,6 +371,11 @@ [1:1] Enable automatic transfer trigger (FIRQ-triggered) + + DMA_CTRL_FENCE + [2:2] + Issue a downstream FENCE operation when DMA transfer completes (without errors) + DMA_CTRL_ERROR_RD [8:8]