Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UART FIFO clear flags; add DMA FIRQ interrupt configuration #930

Merged
merged 11 commits into from
Jun 23, 2024
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