Replicating existing modules - further extend UART ports #944
Replies: 2 comments 5 replies
-
Hey @Manoj-Kumar-MMU!
That's great! 👍
Sure, you can do that. The CFS provides up to 256 bytes of memory mapped IO - that's 64x 32-bit registers. The default UART only requires 2x 32-bit registers. Hence, you could add up to 32 additional UARTs there. The easiest way would be to add an instance of the HardwareFirst, remove all the default logic from the CFS ( Let's say you want to add 3 CFS-internal UARTs. Add the bus signals for them: signal uart_a_req, uart_b_req, uart_c_req : bus_req_t;
signal uart_a_rsp, uart_b_rsp, uart_c_rsp : bus_rsp_t; Then add the IO switch. We just use the first three ports here. Aach UART requires 8 bytes of memory-mapped IO. The first UART (A) is mapped to the base address of the CFS ( cfu_switch_inst: entity neorv32.neorv32_bus_io_switch
generic map (
DEV_SIZE => 8, -- physical address size of one UART instance
DEV_00_EN => true, DEV_00_BASE => std_ulogic_vector(unsigned(base_io_cfs_c) + 0*8),
DEV_01_EN => true, DEV_01_BASE => std_ulogic_vector(unsigned(base_io_cfs_c) + 1*8),
DEV_02_EN => true, DEV_02_BASE => std_ulogic_vector(unsigned(base_io_cfs_c) + 2*8),
DEV_03_EN => false, DEV_03_BASE => (others => '-'),
...
DEV_31_EN => false, DEV_21_BASE => (others => '-')
)
port map (
main_req_i => bus_req_i,
main_rsp_o => bus_rsp_o,
dev_00_req_o => uart_a_req, dev_00_rsp_i => uart_a_rsp,
dev_01_req_o => uart_b_req, dev_01_rsp_i => uart_b_rsp,
dev_02_req_o => uart_c_req, dev_02_rsp_i => uart_c_rsp,
dev_03_req_o => open, dev_03_rsp_i => rsp_terminate_c,
...
dev_31_req_o => open, dev_31_rsp_i => rsp_terminate_c
); Then add instantiate the UARTs and connect them to the IO switch: cfs_uart_a_inst: entity neorv32.neorv32_uart
generic map (
SIM_LOG_FILE => "neorv32.cfs_uart_a.sim_mode.text.out",
UART_RX_FIFO => 64,
UART_TX_FIFO => 64
)
port map (
clk_i => clk_i,
rstn_i => rstn_i,
bus_req_i => uart_a_req,
bus_rsp_o => uart_a_rsp,
clkgen_en_o => open,
clkgen_i => clkgen_i,
uart_txd_o => cfs_out_o(0),
uart_rxd_i => cfs_in_i(0),
uart_rts_o => open,
uart_cts_i => '0',
irq_rx_o => open,
irq_tx_o => open
); Use different names for the simulation log file ( Finally, make sure to enable the CFS clock input so that all the UARTs get their clocks. Additionally, tie the (yet) unused IRQ output to low: clkgen_en_o <= '1';
irq_o <= '0'; SoftwareLuckily, the UART software library already allows to pass a "hardware handle" to each function in order to select a specific UART instance. Btw, maybe we should add this capability to all other modules/HALs/functions?! 🤔 Here is a simple code that shows how to integrate the new UARTs and how to use them conveniently: int main() {
neorv32_rte_setup();
neorv32_uart0_setup(BAUD_RATE, 0);
// define hardware handles for the CFS UARTs
neorv32_uart_t* NEORV32_CFS_UART_A = (neorv32_uart_t*)(NEORV32_CFS_BASE + 0*8); // first CFS UART; offset +0 bytes
neorv32_uart_t* NEORV32_CFS_UART_B = (neorv32_uart_t*)(NEORV32_CFS_BASE + 1*8); // second CFS UART; offset +8 bytes
neorv32_uart_t* NEORV32_CFS_UART_C = (neorv32_uart_t*)(NEORV32_CFS_BASE + 2*8); // third CFS UART; offset +16 bytes
// copy the initialization from the system UART (UART0)
// this will also include the "simulation-mode flag"
NEORV32_CFS_UART_A->CTRL = NEORV32_UART0->CTRL;
NEORV32_CFS_UART_B->CTRL = NEORV32_UART0->CTRL;
NEORV32_CFS_UART_C->CTRL = NEORV32_UART0->CTRL;
neorv32_uart_puts(NEORV32_UART0, "hello from the system UART (UART0)!\n");
while(neorv32_uart_tx_busy(NEORV32_UART0));
neorv32_uart_puts(NEORV32_CFS_UART_A, "hello from CFS_UART_A!\n");
while(neorv32_uart_tx_busy(NEORV32_CFS_UART_A));
neorv32_uart_puts(NEORV32_CFS_UART_B, "hello from CFS_UART_B!\n");
while(neorv32_uart_tx_busy(NEORV32_CFS_UART_B));
neorv32_uart_puts(NEORV32_CFS_UART_C, "hello from CFS_UART_C!\n");
while(neorv32_uart_tx_busy(NEORV32_CFS_UART_C));
return 0;
} I've tested that in simulation using the default testbench and it seems to work. 😉 Be careful with the simulation mode - the software framework only supports that for the default UARTs (that's why I am copying the initialization from UART0 in the example above).
|
Beta Was this translation helpful? Give feedback.
-
Currently I have this implementation in my CFS (neorv32_cfs.vhd) for my quadrature encoder. -- default CFS interface registers --
type cfs_regs_t is array (0 to 3) of std_ulogic_vector(31 downto 0); -- just implement 4 registers for this example
signal cfs_reg_wr : cfs_regs_t; -- interface registers for WRITE accesses
signal cfs_reg_rd : cfs_regs_t; -- interface registers for READ accesses
signal CounterM1: integer range -2000000000 to 2000000000 := 0;
signal CounterM2: integer range -2000000000 to 2000000000 := 0;
signal CounterM3: integer range -2000000000 to 2000000000 := 0;
signal CounterM4: integer range -2000000000 to 2000000000 := 0; functionality implemented in CFS Function Core: Encoder: process(rstn_i)
begin
if rising_edge(cfs_in_i(0)) then
if (cfs_in_i(1) = '1') then
CounterM1 <= CounterM1 + 1;
else
CounterM1 <= CounterM1 - 1;
end if;
end if;
if rising_edge(cfs_in_i(2)) then
if (cfs_in_i(3) = '1') then
CounterM2 <= CounterM2 + 1;
else
CounterM2 <= CounterM2 - 1;
end if;
end if;
if rising_edge(cfs_in_i(4)) then
if (cfs_in_i(5) = '1') then
CounterM3 <= CounterM3 + 1;
else
CounterM3 <= CounterM3 - 1;
end if;
end if;
if rising_edge(cfs_in_i(6)) then
if (cfs_in_i(7) = '1') then
CounterM4 <= CounterM4 + 1;
else
CounterM4 <= CounterM4 - 1;
end if;
end if;
end process Encoder;
cfs_reg_rd(0) <= std_ulogic_vector(to_unsigned(CounterM1, cfs_reg_rd(0)'length));
cfs_reg_rd(1) <= std_ulogic_vector(to_unsigned(CounterM2, cfs_reg_rd(1)'length));
cfs_reg_rd(2) <= std_ulogic_vector(to_unsigned(CounterM3, cfs_reg_rd(2)'length));
cfs_reg_rd(3) <= std_ulogic_vector(to_unsigned(CounterM4, cfs_reg_rd(3)'length)); How do I offset base_io_cfs_c in the case of my implementation? Sorry as I am still relatively new in the FPGA and VHDL environment, learning as I go. |
Beta Was this translation helpful? Give feedback.
-
I have used the Custom Function Subsystem (CFS) to get the readings and process the data of a quadrature encoder from my DC motor. It worked flawlessly.
In the NEORV32 datasheet, I saw it was mentioned that the neorv32 submodules can be replicated using the CFS. I'm wondering how is it done to extend the number of UARTs using this approach (or other possible approach)?
I have read the thread #9 but couldn't work around how to do so.
Beta Was this translation helpful? Give feedback.
All reactions