Skip to content

Commit

Permalink
Add UART FIFO clear flags; add DMA FIRQ interrupt configuration (#930)
Browse files Browse the repository at this point in the history
  • Loading branch information
stnolting committed Jun 23, 2024
2 parents a09c6d9 + 3d7ffa1 commit f4bcc2f
Show file tree
Hide file tree
Showing 13 changed files with 158 additions and 27 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 | Ticket |
|:----:|:-------:|:--------|:------:|
| 22.06.2024 | 1.10.0.3 | UARTs: add flags to clear RX/TX FIFOs; DMA: add FIRQ trigger type configuration flag | [#930](https://github.com/stnolting/neorv32/pull/930) |
| 21.06.2024 | 1.10.0.2 | minor code rtl clean-ups; fix some missing TOP defaults | [#929](https://github.com/stnolting/neorv32/pull/929) |
| 17.05.2024 | 1.10.0.1 | :warning: remove (optional and redundant) JTAG reset signal `jtag_trst_i` | [#928](https://github.com/stnolting/neorv32/pull/928) |
| 16.05.2024 | [**:rocket:1.10.0**](https://github.com/stnolting/neorv32/releases/tag/v1.10.0) | **New release** | |
Expand Down
14 changes: 11 additions & 3 deletions docs/datasheet/soc_dma.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ 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
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.
Expand All @@ -106,6 +106,13 @@ The actually triggering FIRQ channel is configured via the control register's `D
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
Expand Down Expand Up @@ -134,15 +141,16 @@ register).
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.11+<| `0xffffed00` .11+<| `CTRL` <|`0` `DMA_CTRL_EN` ^| r/w <| DMA module enable
.12+<| `0xffffed00` .12+<| `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
<|`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
| `0xffffed04` | `SRC_BASE` |`31:0` | r/w | Source base address (shows the last-accessed source address when read)
Expand Down
14 changes: 12 additions & 2 deletions docs/datasheet/soc_uart.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ and the runtime environment) use the primary UART (_UART0_) as default user cons
is used to implement the "standard consoles" (`STDIN`, `STDOUT` and `STDERR`).


**RX and TX FIFOs**

The UART provides individual data FIFOs for RX and TX to allow data transmission without CPU intervention.
The sizes of these FIFOs can be configured via the according configuration generics (`UART0_RX_FIFO` and `UART0_TX_FIFO`).
Both FIFOs a re automatically cleared when disabling the module via the `UART_CTRL_EN` flag. However, the FIFOs can
also be cleared individually by setting the `UART_CTRL_RX_CLR` / `UART_CTRL_TX_CLR` flags.


**Theory of Operation**

The module is enabled by setting the `UART_CTRL_EN` bit in the UART0 control register `CTRL`. The Baud rate
Expand Down Expand Up @@ -119,7 +127,7 @@ Both file are created in the simulation's home folder.
[options="header",grid="all"]
|=======================
| Address | Name [C] | Bit(s), Name [C] | R/W | Function
.19+<| `0xfffff500` .19+<| `CTRL` <|`0` `UART_CTRL_EN` ^| r/w <| UART enable
.20+<| `0xfffff500` .20+<| `CTRL` <|`0` `UART_CTRL_EN` ^| r/w <| UART enable
<|`1` `UART_CTRL_SIM_MODE` ^| r/w <| enable **simulation mode**
<|`2` `UART_CTRL_HWFC_EN` ^| r/w <| enable RTS/CTS hardware flow-control
<|`5:3` `UART_CTRL_PRSC2 : UART_CTRL_PRSC0` ^| r/w <| Baud rate clock prescaler select
Expand All @@ -135,7 +143,9 @@ Both file are created in the simulation's home folder.
<|`24` `UART_CTRL_IRQ_RX_FULL` ^| r/w <| fire IRQ if RX FIFO full
<|`25` `UART_CTRL_IRQ_TX_EMPTY` ^| r/w <| fire IRQ if TX FIFO empty
<|`26` `UART_CTRL_IRQ_TX_NHALF` ^| r/w <| fire IRQ if TX not at least half full
<|`29:27` - ^| r/- <| _reserved_ read as zero
<|`27` - ^| r/- <| _reserved_ read as zero
<|`28` `UART_CTRL_RX_CLR` ^| r/w <| Clear RX FIFO, flag auto-clears
<|`29` `UART_CTRL_TX_CLR` ^| r/w <| Clear TX FIFO, flag auto-clears
<|`30` `UART_CTRL_RX_OVER` ^| r/- <| RX FIFO overflow; cleared by disabling the module
<|`31` `UART_CTRL_TX_BUSY` ^| r/- <| TX busy or TX FIFO not empty
.4+<| `0xfffff504` .4+<| `DATA` <|`7:0` `UART_DATA_RTX_MSB : UART_DATA_RTX_LSB` ^| r/w <| receive/transmit data
Expand Down
27 changes: 18 additions & 9 deletions rtl/core/neorv32_dma.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ 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_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

Expand All @@ -65,6 +66,7 @@ architecture neorv32_dma_rtl of neorv32_dma is
auto : std_ulogic; -- FIRQ-driven auto transfer
fence : std_ulogic; -- issue FENCE operation when DMA is done
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
Expand Down Expand Up @@ -117,6 +119,7 @@ begin
config.auto <= '0';
config.fence <= '0';
config.firq_sel <= (others => '0');
config.firq_type <= '0';
config.src_base <= (others => '0');
config.dst_base <= (others => '0');
config.num <= (others => '0');
Expand All @@ -143,7 +146,8 @@ 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_sel <= bus_req_i.data(ctrl_firq_sel_msb_c downto ctrl_firq_sel_lsb_c);
config.firq_type <= bus_req_i.data(ctrl_firq_type_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 @@ -162,13 +166,14 @@ begin
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) <= 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;
bus_rsp_o.data(ctrl_done_c) <= config.done;
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;
bus_rsp_o.data(ctrl_done_c) <= config.done;
bus_rsp_o.data(ctrl_firq_type_c) <= config.firq_type;
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;
Expand Down Expand Up @@ -201,7 +206,11 @@ begin
elsif rising_edge(clk_i) then
firq_buf <= firq_i;
match_ff <= match;
atrigger <= match and (not match_ff); -- trigger on rising edge of FIRQ
if (config.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;

Expand Down
2 changes: 1 addition & 1 deletion 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"01100002"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100003"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width

Expand Down
25 changes: 16 additions & 9 deletions rtl/core/neorv32_uart.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ architecture neorv32_uart_rtl of neorv32_uart is
constant ctrl_irq_tx_empty_c : natural := 25; -- r/w: TX FIFO empty
constant ctrl_irq_tx_nhalf_c : natural := 26; -- r/w: TX FIFO not at least half-full
--
constant ctrl_rx_clr_c : natural := 28; -- r/w: Clear RX FIFO, flag auto-clears
constant ctrl_tx_clr_c : natural := 29; -- r/w: Clear TX FIFO, flag auto-clears
constant ctrl_rx_over_c : natural := 30; -- r/-: RX FIFO overflow
constant ctrl_tx_busy_c : natural := 31; -- r/-: UART transmitter is busy and TX FIFO not empty

Expand All @@ -96,6 +98,8 @@ architecture neorv32_uart_rtl of neorv32_uart is
irq_rx_full : std_ulogic;
irq_tx_empty : std_ulogic;
irq_tx_nhalf : std_ulogic;
clr_rx : std_ulogic;
clr_tx : std_ulogic;
end record;
signal ctrl : ctrl_t;

Expand Down Expand Up @@ -157,15 +161,18 @@ begin
ctrl.irq_rx_full <= '0';
ctrl.irq_tx_empty <= '0';
ctrl.irq_tx_nhalf <= '0';
ctrl.clr_rx <= '0';
ctrl.clr_tx <= '0';
elsif rising_edge(clk_i) then
-- bus handshake --
-- defaults --
bus_rsp_o.ack <= bus_req_i.stb;
bus_rsp_o.err <= '0';
bus_rsp_o.data <= (others => '0');
ctrl.clr_rx <= '0'; -- auto-clear
ctrl.clr_tx <= '0'; -- auto-clear
-- bus access --
if (bus_req_i.stb = '1') then

-- write access --
if (bus_req_i.rw = '1') then
if (bus_req_i.rw = '1') then -- write access
if (bus_req_i.addr(2) = '0') then -- control register
ctrl.enable <= bus_req_i.data(ctrl_en_c);
ctrl.sim_mode <= bus_req_i.data(ctrl_sim_en_c);
Expand All @@ -178,10 +185,10 @@ begin
ctrl.irq_rx_full <= bus_req_i.data(ctrl_irq_rx_full_c);
ctrl.irq_tx_empty <= bus_req_i.data(ctrl_irq_tx_empty_c);
ctrl.irq_tx_nhalf <= bus_req_i.data(ctrl_irq_tx_nhalf_c);
ctrl.clr_rx <= bus_req_i.data(ctrl_rx_clr_c);
ctrl.clr_tx <= bus_req_i.data(ctrl_tx_clr_c);
end if;

-- read access --
else
else -- read access
if (bus_req_i.addr(2) = '0') then -- control register
bus_rsp_o.data(ctrl_en_c) <= ctrl.enable;
bus_rsp_o.data(ctrl_sim_en_c) <= ctrl.sim_mode;
Expand Down Expand Up @@ -244,7 +251,7 @@ begin
avail_o => tx_fifo.avail
);

tx_fifo.clear <= '1' when (ctrl.enable = '0') or (ctrl.sim_mode = '1') else '0';
tx_fifo.clear <= '1' when (ctrl.enable = '0') or (ctrl.sim_mode = '1') or (ctrl.clr_tx = '1') else '0';
tx_fifo.wdata <= bus_req_i.data(data_rtx_msb_c downto data_rtx_lsb_c);
tx_fifo.we <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '1') and (bus_req_i.addr(2) = '1') else '0';
tx_fifo.re <= '1' when (tx_engine.state = "100") else '0';
Expand Down Expand Up @@ -285,7 +292,7 @@ begin
avail_o => rx_fifo.avail
);

rx_fifo.clear <= '1' when (ctrl.enable = '0') or (ctrl.sim_mode = '1') else '0';
rx_fifo.clear <= '1' when (ctrl.enable = '0') or (ctrl.sim_mode = '1') or (ctrl.clr_rx = '1') else '0';
rx_fifo.wdata <= rx_engine.sreg(7 downto 0);
rx_fifo.we <= rx_engine.done;
rx_fifo.re <= '1' when (bus_req_i.stb = '1') and (bus_req_i.rw = '0') and (bus_req_i.addr(2) = '1') else '0';
Expand Down
38 changes: 37 additions & 1 deletion sw/example/demo_dma/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ int main() {
// setup UART at default baud rate, no interrupts
neorv32_uart0_setup(BAUD_RATE, 0);


// intro
neorv32_uart0_printf("\n<<< DMA Controller Demo Program >>>\n\n");

Expand Down Expand Up @@ -249,7 +250,8 @@ int main() {
(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
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();
Expand All @@ -275,6 +277,40 @@ int main() {
}


// ----------------------------------------------------------
// 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;
}
Expand Down
12 changes: 12 additions & 0 deletions sw/example/processor_check/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,9 @@ int main() {
neorv32_uart0_setup(BAUD_RATE, 1 << UART_CTRL_IRQ_RX_NEMPTY);
// make sure sim mode is disabled
NEORV32_UART0->CTRL &= ~(1 << UART_CTRL_SIM_MODE);
// clear FIFOs
neorv32_uart0_rx_clear();
neorv32_uart0_tx_clear();

// enable fast interrupt
neorv32_cpu_csr_write(CSR_MIE, 1 << UART0_RX_FIRQ_ENABLE);
Expand Down Expand Up @@ -1116,6 +1119,9 @@ int main() {
neorv32_uart0_setup(BAUD_RATE, 1 << UART_CTRL_IRQ_TX_EMPTY);
// make sure sim mode is disabled
NEORV32_UART0->CTRL &= ~(1 << UART_CTRL_SIM_MODE);
// clear FIFOs
neorv32_uart0_rx_clear();
neorv32_uart0_tx_clear();

neorv32_uart0_putc(0);
while(neorv32_uart0_tx_busy());
Expand Down Expand Up @@ -1159,6 +1165,9 @@ int main() {
neorv32_uart1_setup(BAUD_RATE, 1 << UART_CTRL_IRQ_RX_NEMPTY);
// make sure sim mode is disabled
NEORV32_UART1->CTRL &= ~(1 << UART_CTRL_SIM_MODE);
// clear FIFOs
neorv32_uart1_rx_clear();
neorv32_uart1_tx_clear();

// UART1 RX interrupt enable
neorv32_cpu_csr_write(CSR_MIE, 1 << UART1_RX_FIRQ_ENABLE);
Expand Down Expand Up @@ -1202,6 +1211,9 @@ int main() {
neorv32_uart1_setup(BAUD_RATE, 1 << UART_CTRL_IRQ_TX_EMPTY);
// make sure sim mode is disabled
NEORV32_UART1->CTRL &= ~(1 << UART_CTRL_SIM_MODE);
// clear FIFOs
neorv32_uart1_rx_clear();
neorv32_uart1_tx_clear();

neorv32_uart1_putc(0);
while(neorv32_uart1_tx_busy());
Expand Down
3 changes: 2 additions & 1 deletion sw/lib/include/neorv32_dma.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum NEORV32_DMA_CTRL_enum {
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 */
};
Expand Down Expand Up @@ -102,7 +103,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, int firq_sel);
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);
int neorv32_dma_status(void);
int neorv32_dma_done(void);
/**@}*/
Expand Down
Loading

0 comments on commit f4bcc2f

Please sign in to comment.