Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12

| Date | Version | Comment | Ticket |
|:----:|:-------:|:--------|:------:|
| 18.06.2025 | 1.11.6.8 | minor rtl edits and optimizations | [#1291](https://github.com/stnolting/neorv32/pull/1291) |
| 08.06.2025 | 1.11.6.7 | :warning: combine individual UART RX/TX interrupt requests into a single (programmable) interrupt request | [#1289](https://github.com/stnolting/neorv32/pull/1289) |
| 08.06.2025 | 1.11.6.6 | :warning: invert "TX FIFO full" status flag; add interrupt option for "TX FIFO not full" status | [#1288](https://github.com/stnolting/neorv32/pull/1288) |
| 07.06.2025 | 1.11.6.5 | :sparkles: add TRNG interrupt | [#1287](https://github.com/stnolting/neorv32/pull/1287) |
| 07.06.2025 | 1.11.6.4 | :warning: combine individual SLINK RX/TX interrupt requests into a single (programmable) interrupt request | [#1286](https://github.com/stnolting/neorv32/pull/1286) |
| 06.06.2025 | 1.11.6.3 | :sparkles: :test_tube: add semihosting support for the on-chip debugger | [#]1285(https://github.com/stnolting/neorv32/pull/1285) |
| 06.06.2025 | 1.11.6.3 | :sparkles: :test_tube: add semihosting support for the on-chip debugger | [#1285](https://github.com/stnolting/neorv32/pull/1285) |
| 06.06.2025 | 1.11.6.2 | upgrade neoTRNG to version 3.3 | [#1284](https://github.com/stnolting/neorv32/pull/1284) |
| 06.06.2025 | 1.11.6.1 | minor rtl optimizations and cleanups | [#1283](https://github.com/stnolting/neorv32/pull/1283) |
| 02.06.2025 | [**:rocket:1.11.6**](https://github.com/stnolting/neorv32/releases/tag/v1.11.6) | **New release** | |
Expand Down
22 changes: 11 additions & 11 deletions docs/datasheet/soc_dma.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ register's `DMA_CTRL_START` bit. Setting this bit while the descriptor FIFO is e
of the FIFO can be checked via the `DMA_CTRL_D*` flags.

The DMA uses an atomic read-modify-write transfer process. Data is read from the current source address, modified/aligned
internally and then written back to the current destination address. If the DMA controller detects a bus error during operation,
it will set the `DMA_CTRL_ERROR` and terminate the current transfer waiting in idle state for the user to acknowledge/clear
`DMA_CTRL_ERROR`. This also happens if there are further descriptor in the FIFO that have not yet been executed.
internally and then written back to the current destination address. If the DMA controller encounters a bus error during
operation, it will set the `DMA_CTRL_ERROR` flag and will terminate the current transfer. An new transfer can only start
if the `DMA_CTRL_ERROR` flag is cleared manually.

When the `DMA_CTRL_DONE` flag is set the DMA has completed all programmed transfers, i.e. all descriptors from the FIFO
were executed. This flag also triggers the DMA controller's interrupt request signal. The application software has to
Expand Down Expand Up @@ -82,8 +82,7 @@ The descriptor FIFO has a minimal size of 4 entries. This can be extended by the
.Incomplete Descriptors
[NOTE]
The DMA controller consumes 3 entries from the FIFO for each transfer. If the FIFO does not provide a complete
DMA descriptor, the controller will remove the incomplete descriptor and will set the `DMA_CTRL_ERROR` status flag
without executing the incomplete transfer descriptor.
DMA descriptor, the controller will wait until a complete descriptor is available.

The source and destination data addresses can target any memory location in the entire 32-bit address space including
memory-mapped peripherals. The number of elements to transfer as well as incrementing or constant byte- or word-level
Expand All @@ -108,7 +107,7 @@ Source and destination data accesses are configured by a 2-bit selector individu
* `10`: Incrementing byte - transfer data as byte (8-bit); increment the source address by 1
* `11`: Incrementing word - transfer data as word (32-bit); increment the source address by 4

optionally, the controller automatically swaps the logical byte order ("Endianness") of the transferred data
Optionally, the controller can automatically swap the logical byte order ("Endianness") of the transferred data
when the `DMA_CONF_BSWAP` bit is set.


Expand All @@ -119,15 +118,16 @@ when the `DMA_CONF_BSWAP` bit is set.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.10+<| `0xffed0000` .10+<| `CTRL` <|`0` `DMA_CTRL_EN` ^| r/w <| DMA module enable; reset module when cleared
.11+<| `0xffed0000` .11+<| `CTRL` <|`0` `DMA_CTRL_EN` ^| r/w <| DMA module enable; reset module when cleared
<|`1` `DMA_CTRL_START` ^| -/w <| Start programmed DMA transfer(s)
<|`15:2` _reserved_ ^| r/- <| _reserved_, read as zero
<|`19:16` `DMA_CTRL_DFIFO_MSB : DMA_CTRL_DFIFO_LSB` ^| r/- <|
<|`19:16` `DMA_CTRL_DFIFO_MSB : DMA_CTRL_DFIFO_LSB` ^| r/- <| Descriptor FIFO depth, log2(`IO_DMA_DSC_FIFO`)
<|`26:20` _reserved_ ^| r/- <| _reserved_, read as zero
<|`27` `DMA_CTRL_DEMPTY`` ^| r/- <| Descriptor FIFO is empty
<|`27` `DMA_CTRL_ACK` ^| -/w <| Write `1` to clear DMA interrupt (also clears `DMA_CTRL_ERROR` and `DMA_CTRL_DONE`)
<|`27` `DMA_CTRL_DEMPTY` ^| r/- <| Descriptor FIFO is empty
<|`28` `DMA_CTRL_DFULL` ^| r/- <| Descriptor FIFO is full
<|`29` `DMA_CTRL_ERROR` ^| r/c <| Bus access error during transfer or incomplete descriptor data; clear by writing `1`
<|`30` `DMA_CTRL_DONE` ^| r/c <| All transfers executed; clear by writing `1`
<|`29` `DMA_CTRL_ERROR` ^| r/- <| Bus access error during transfer or incomplete descriptor data
<|`30` `DMA_CTRL_DONE` ^| r/1 <| All transfers executed
<|`31` `DMA_CTRL_BUSY` ^| r/- <| DMA transfer(s) in progress
| `0xffed0004` | `DESC` |`31:0` | -/w | Descriptor FIFO write access
|=======================
7 changes: 4 additions & 3 deletions rtl/core/neorv32_cpu_frontend.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ end neorv32_cpu_frontend;
architecture neorv32_cpu_frontend_rtl of neorv32_cpu_frontend is

-- instruction fetch engine --
type fetch_state_t is (S_RESTART, S_REQUEST, S_PENDING);
type state_t is (S_RESTART, S_REQUEST, S_PENDING);
type fetch_t is record
state : fetch_state_t;
state : state_t;
restart : std_ulogic; -- buffered restart request (after branch)
pc : std_ulogic_vector(XLEN-1 downto 0);
priv : std_ulogic; -- fetch privilege level
Expand Down Expand Up @@ -148,7 +148,8 @@ begin
FIFO_WIDTH => 17, -- error status & instruction half-word data
FIFO_RSYNC => false, -- we NEED to read data asynchronously
FIFO_SAFE => false, -- no safe access required (ensured by FIFO-external logic)
FULL_RESET => false -- no need for a full hardware reset
FULL_RESET => false, -- no need for a full hardware reset,
OUT_GATE => false -- no output gate required
)
port map (
-- control and status --
Expand Down
154 changes: 83 additions & 71 deletions rtl/core/neorv32_dma.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@ architecture neorv32_dma_rtl of neorv32_dma is
constant ctrl_start_c : natural := 1; -- -/w: start DMA transfer(s)
constant ctrl_fifo0_c : natural := 16; -- r/-: log2(FIFO descriptor depth), LSB
constant ctrl_fifo3_c : natural := 19; -- r/-: log2(FIFO descriptor depth), MSB
constant ctrl_ack_c : natural := 26; -- -/w: set 1 to clean ERROR and DONE flags
constant ctrl_dempty_c : natural := 27; -- r/-: descriptor buffer is empty
constant ctrl_dfull_c : natural := 28; -- r/-: descriptor buffer is full
constant ctrl_error_c : natural := 29; -- r/-: bus access error during transfer
constant ctrl_done_c : natural := 30; -- r/c: transfer has completed
constant ctrl_done_c : natural := 30; -- r/-: transfer has completed
constant ctrl_busy_c : natural := 31; -- r/-: DMA transfer in progress

-- control and status register --
Expand All @@ -74,24 +75,25 @@ architecture neorv32_dma_rtl of neorv32_dma is
signal fifo : fifo_t;

-- bus access engine --
type state_t is (S_IDLE, S_GET_0, S_GET_1, S_READ_REQ, S_READ_RSP, S_WRITE_REQ, S_WRITE_RSP, S_NEXT);
type state_t is (S_CHECK, S_GET_0, S_GET_1, S_GET_2, S_READ_REQ, S_READ_RSP, S_WRITE_REQ, S_WRITE_RSP);
type engine_t is record
state : state_t;
run : std_ulogic;
run_ff : std_ulogic;
done : std_ulogic;
err : std_ulogic;
src_add : unsigned(31 downto 0);
dst_add : unsigned(31 downto 0);
src_addr : std_ulogic_vector(31 downto 0);
dst_addr : std_ulogic_vector(31 downto 0);
num : std_ulogic_vector(23 downto 0);
num_or : std_ulogic;
bswap : std_ulogic; -- swap byte order
src_type : std_ulogic_vector(1 downto 0);
dst_type : std_ulogic_vector(1 downto 0);
end record;
signal engine : engine_t;

-- address increment --
signal src_add, dst_add : unsigned(31 downto 0);

-- data buffer --
signal rdata, data_buf : std_ulogic_vector(31 downto 0);

Expand Down Expand Up @@ -121,8 +123,10 @@ begin
if (bus_req_i.rw = '1') then -- write access
ctrl.enable <= bus_req_i.data(ctrl_en_c);
ctrl.start <= bus_req_i.data(ctrl_start_c);
ctrl.err <= ctrl.err and (not bus_req_i.data(ctrl_error_c)); -- write 1 to clear
ctrl.done <= ctrl.done and (not bus_req_i.data(ctrl_done_c)); -- write 1 to clear
if (bus_req_i.data(ctrl_start_c) = '1') or (bus_req_i.data(ctrl_ack_c) = '1') then -- write 1 to clear
ctrl.err <= '0';
ctrl.done <= '0';
end if;
else -- read access
bus_rsp_o.data(ctrl_en_c) <= ctrl.enable;
bus_rsp_o.data(ctrl_fifo3_c downto ctrl_fifo0_c) <= std_ulogic_vector(to_unsigned(log2_fifo_size_c, 4));
Expand All @@ -148,7 +152,8 @@ begin
FIFO_WIDTH => 32,
FIFO_RSYNC => false,
FIFO_SAFE => true,
FULL_RESET => false
FULL_RESET => false,
OUT_GATE => false
)
port map (
-- control and status --
Expand All @@ -170,108 +175,134 @@ begin
-- FIFO control --
fifo.clr <= '1' when (ctrl.enable = '0') else '0';
fifo.we <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '1') and (bus_req_i.addr(2) = '1') else '0';
fifo.re <= engine.run when (engine.state = S_IDLE) or (engine.state = S_GET_0) or (engine.state = S_GET_1) else '0';
fifo.re <= '1' when (engine.state = S_GET_0) or (engine.state = S_GET_1) or (engine.state = S_GET_2) else '0';


-- Bus Access Engine ----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
bus_engine: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
engine.state <= S_IDLE;
engine.state <= S_CHECK;
engine.run <= '0';
engine.run_ff <= '0';
engine.done <= '0';
engine.err <= '0';
engine.src_addr <= (others => '0');
engine.dst_addr <= (others => '0');
engine.num <= (others => '0');
engine.num_or <= '0';
engine.bswap <= '0';
engine.src_type <= (others => '0');
engine.dst_type <= (others => '0');
elsif rising_edge(clk_i) then
engine.run_ff <= engine.run;
case engine.state is

when S_IDLE => -- idle, waiting for trigger
when S_CHECK => -- waiting for trigger
-- ------------------------------------------------------------
engine.done <= '0';
engine.err <= '0';
engine.num_or <= '0';
if (engine.run = '0') then -- start new transfer if descriptor available and no pending error
if (fifo.avail = '1') and (ctrl.start = '1') and (ctrl.err = '0') then
engine.run <= '1';
engine.state <= S_GET_0;
end if;
else -- transfer in progress
if (fifo.avail = '1') and (engine.err = '0') and (ctrl.err = '0') then -- execute next descriptor if there was no error
engine.run <= '1';
engine.state <= S_GET_0;
else
engine.run <= '0';
engine.done <= '1'; -- all transfers completed
end if;
end if;

when S_GET_0 => -- get descriptor: source base address
-- ------------------------------------------------------------
engine.err <= '0';
if (engine.run = '1') and (fifo.avail = '1') and (engine.err = '0') then
engine.src_addr <= fifo.rdata; -- get descriptor: source base address
engine.state <= S_GET_0;
else
engine.run <= ctrl.enable and ctrl.start and fifo.avail and (not ctrl.err);
engine.src_addr <= fifo.rdata;
if (fifo.avail = '1') then
engine.state <= S_GET_1;
end if;

when S_GET_0 => -- get descriptor: destination base address
when S_GET_1 => -- get descriptor: destination base address
-- ------------------------------------------------------------
engine.dst_addr <= fifo.rdata;
if (fifo.avail = '1') then
engine.dst_addr <= fifo.rdata;
engine.state <= S_GET_1;
else
engine.err <= '1'; -- error - no descriptor data available
engine.state <= S_NEXT;
engine.state <= S_GET_2;
end if;

when S_GET_1 => -- get descriptor: transfer configuration
when S_GET_2 => -- get descriptor: transfer configuration
-- ------------------------------------------------------------
engine.num <= fifo.rdata(conf_num_hi_c downto conf_num_lo_c);
engine.bswap <= fifo.rdata(conf_bswap_c);
engine.src_type <= fifo.rdata(conf_src_hi_c downto conf_src_lo_c);
engine.dst_type <= fifo.rdata(conf_dst_hi_c downto conf_dst_lo_c);
if (fifo.avail = '1') then
engine.num <= fifo.rdata(conf_num_hi_c downto conf_num_lo_c);
engine.bswap <= fifo.rdata(conf_bswap_c);
engine.src_type <= fifo.rdata(conf_src_hi_c downto conf_src_lo_c);
engine.dst_type <= fifo.rdata(conf_dst_hi_c downto conf_dst_lo_c);
engine.state <= S_READ_REQ;
else
engine.err <= '1'; -- error - no descriptor data available
engine.state <= S_NEXT;
engine.state <= S_READ_REQ;
end if;

when S_READ_REQ => -- read request
-- ------------------------------------------------------------
if (engine.num_or = '1') then -- hacky! do not increment in first iteration
engine.dst_addr <= std_ulogic_vector(unsigned(engine.dst_addr) + dst_add);
end if;
engine.num <= std_ulogic_vector(unsigned(engine.num) - 1);
engine.state <= S_READ_RSP;

when S_READ_RSP => -- read response
-- ------------------------------------------------------------
if (dma_rsp_i.ack = '1') then
engine.err <= dma_rsp_i.err;
if (dma_rsp_i.err = '1') then
engine.state <= S_NEXT;
engine.state <= S_CHECK;
else
engine.state <= S_WRITE_REQ;
end if;
end if;

when S_WRITE_REQ => -- write request
-- ------------------------------------------------------------
engine.num <= std_ulogic_vector(unsigned(engine.num) - 1);
engine.state <= S_WRITE_RSP;
engine.src_addr <= std_ulogic_vector(unsigned(engine.src_addr) + src_add);
engine.num_or <= or_reduce_f(engine.num);
engine.state <= S_WRITE_RSP;

when S_WRITE_RSP => -- write response
-- ------------------------------------------------------------
if (dma_rsp_i.ack = '1') then
engine.err <= dma_rsp_i.err;
engine.state <= S_NEXT;
end if;

when S_NEXT => -- check if done; prepare next access
-- ------------------------------------------------------------
engine.src_addr <= std_ulogic_vector(unsigned(engine.src_addr) + engine.src_add);
engine.dst_addr <= std_ulogic_vector(unsigned(engine.dst_addr) + engine.dst_add);
if (or_reduce_f(engine.num) = '0') or (ctrl.enable = '0') or (engine.err = '1') then -- all elements done? abort?
engine.state <= S_IDLE;
else
engine.state <= S_READ_REQ;
engine.err <= dma_rsp_i.err;
if (engine.num_or = '0') or (ctrl.enable = '0') or (dma_rsp_i.err = '1') then -- done/abort/error?
engine.state <= S_CHECK;
else
engine.state <= S_READ_REQ;
end if;
end if;

when others => -- undefined
-- ------------------------------------------------------------
engine.state <= S_IDLE;
engine.state <= S_CHECK;

end case;
end if;
end process bus_engine;

-- transfer(s) done --
engine.done <= '1' when (engine.run = '0') and (engine.run_ff = '1') else '0';

-- Address Increment ----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
address_inc: process(engine)
begin
-- source --
case engine.src_type is
when "10" => src_add <= to_unsigned(1, 32); -- incrementing byte
when "11" => src_add <= to_unsigned(4, 32); -- incrementing word
when others => src_add <= to_unsigned(0, 32); -- constant byte/word
end case;
-- destination --
case engine.dst_type is
when "10" => dst_add <= to_unsigned(1, 32); -- incrementing byte
when "11" => dst_add <= to_unsigned(4, 32); -- incrementing word
when others => dst_add <= to_unsigned(0, 32); -- constant byte/word
end case;
end process address_inc;


-- Bus Output Control ---------------------------------------------------------------------
Expand Down Expand Up @@ -316,25 +347,6 @@ begin
end process bus_control;


-- Address Increment ----------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
address_inc: process(engine)
begin
-- source --
case engine.src_type is
when "10" => engine.src_add <= to_unsigned(1, 32); -- incrementing byte
when "11" => engine.src_add <= to_unsigned(4, 32); -- incrementing word
when others => engine.src_add <= to_unsigned(0, 32); -- constant byte/word
end case;
-- destination --
case engine.dst_type is
when "10" => engine.dst_add <= to_unsigned(1, 32); -- incrementing byte
when "11" => engine.dst_add <= to_unsigned(4, 32); -- incrementing word
when others => engine.dst_add <= to_unsigned(0, 32); -- constant byte/word
end case;
end process address_inc;


-- Input Data Alignment -------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
src_align: process(rstn_i, clk_i)
Expand Down
Loading