UART(E): Add Loopback backend

Add a new minimal overhead backend which connects
a UART in loopback.

Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
This commit is contained in:
Alberto Escolar Piedras 2024-01-10 15:14:50 +01:00
parent bc648f255d
commit 8b0819f405
5 changed files with 134 additions and 4 deletions

View File

@ -33,8 +33,14 @@ bytes into the UART peripheral like if they arrived thru the Rx line.
### Backends:
Today there is only 1 backend to choose from:
* The FIFO backend.
Today there are 2 backends to choose from:
* The loopback backend
* The FIFO backend
#### The Loopback backend
This backend just connects an instance Tx to its Rx, including the RTS and CTS signals.
While having minimal overhead.
#### The FIFO backend
@ -73,10 +79,12 @@ It is possible to connect a UART instance Tx directly to its Rx (or to another i
and have the RTR propagated to the CTS.
To do this, just configure the same FIFO file name for both the Rx and Tx, for example like:
`-uart0_fifob_rxfile=looped_back -uart0_fifob_txfile=looped_back`
Note that you can also use the loopback backend when connecting a single instance in loopback,
and have the same result with lower overhead, and no files created on disk.
**IMPOTANT**:
Do not connect both devices which are connected thru the UART to the Physical layer
simulation. Connect only the one which has the BLE/15.4 controller.
Do not connect both devices which are connected thru the UART FIFO backend to the Physical layer
simulation simultaneously. Connect only the one which has the BLE/15.4 controller.
Otherwise, with the current implementation the simulation will deadlock with very high
likelihood, and if it does not deadlock it will slow down the simulation considerably.
You can still provide the sim_id and an unused device number to the other device, but

View File

@ -25,6 +25,7 @@ src/HW_models/NHW_TEMP.c
src/HW_models/NHW_TIMER.c
src/HW_models/NHW_UART.c
src/HW_models/NHW_UART_backend_fifo.c
src/HW_models/NHW_UART_be_loopb.c
src/HW_models/bs_compat.c
src/HW_models/NHW_52_FICR.c
src/HW_models/NRF_GPIOTE.c

View File

@ -30,5 +30,6 @@ src/HW_models/NHW_TIMER.c
src/HW_models/NHW_TEMP.c
src/HW_models/NHW_UART.c
src/HW_models/NHW_UART_backend_fifo.c
src/HW_models/NHW_UART_be_loopb.c
src/HW_models/NHW_VREQCTRL.c
src/HW_models/weak_stubs.c

View File

@ -185,6 +185,9 @@ uart_rtxb_cb_f nhw_uarte_register_rx_cb(int inst, uart_rtxb_cb_f cb, bool Rx_Not
void nhw_UARTE_backend_register(uint inst, struct backend_if *backend) {
struct uarte_status *u_el = &nhw_uarte_st[inst];
if (u_el->backend.tx_byte_f != NULL) {
bs_trace_warning_line("UART%i backend selection overwritten\n", inst);
}
memcpy(&u_el->backend, backend, sizeof(struct backend_if));
}

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Backend for the UART(E) which connects the Tx and Rx of the same UART instance in loopback
*/
#include <stdint.h>
#include "bs_tracing.h"
#include "bs_cmd_line.h"
#include "bs_utils.h"
#include "bs_dynargs.h"
#include "NHW_config.h"
#include "NHW_peri_types.h"
#include "NHW_UART_backend_if.h"
#include "nsi_tasks.h"
#include "nsi_hw_scheduler.h"
#include "nsi_hws_models_if.h"
static bs_time_t Timer_ULoopback = TIME_NEVER;
struct ublb_st_t {
bool enabled;
bs_time_t Timer;
char rx_byte;
} ublb_st[NHW_UARTE_TOTAL_INST];
static void nhw_ublb_tx_byte(uint inst, uint8_t data);
static void nhw_ublb_RTS_pin_toggle(uint inst, bool new_level);
static void nhw_ublb_init(void) {
struct backend_if st;
st.tx_byte_f = nhw_ublb_tx_byte;
st.RTS_pin_toggle_f = nhw_ublb_RTS_pin_toggle;
st.uart_enable_notify_f = NULL;
for (int i = 0; i < NHW_UARTE_TOTAL_INST; i++) {
ublb_st[i].Timer = TIME_NEVER;
if (!ublb_st[i].enabled) {
continue;
}
nhw_UARTE_backend_register(i, &st);
nhw_UARTE_CTS_raised(i);
}
}
NSI_TASK(nhw_ublb_init, HW_INIT, 100); /* this must be before the uart itself */
static void nhw_ublb_register_cmdline(void) {
static bs_args_struct_t args[NHW_UARTE_TOTAL_INST + 1 /* End marker */];
static char descr[] = "Connect this UART instance in loopback (Tx->Rx, RTS->CTS)";
#define OPTION_LEN (4 + 2 + 9 + 1)
static char options[NHW_UARTE_TOTAL_INST][OPTION_LEN];
for (int i = 0 ; i < NHW_UARTE_TOTAL_INST; i++) {
snprintf(options[i], OPTION_LEN, "uart%i_loopback", i);
args[i].is_switch = true;
args[i].option = options[i];
args[i].type = 'b';
args[i].dest = &ublb_st[i].enabled;
args[i].descript = descr;
}
bs_add_extra_dynargs(args);
}
NSI_TASK(nhw_ublb_register_cmdline, PRE_BOOT_1, 200);
static void nhw_ublb_update_timer(void) {
Timer_ULoopback = TIME_NEVER;
for (int i = 0; i < NHW_UARTE_TOTAL_INST; i++) {
if (!ublb_st[i].enabled) {
continue;
}
Timer_ULoopback = BS_MIN(ublb_st[i].Timer, Timer_ULoopback);
}
nsi_hws_find_next_event();
}
static void nhw_ublb_tx_byte(uint inst, uint8_t data) {
if (ublb_st[inst].Timer != TIME_NEVER) {
bs_trace_error_time_line("%s: Unexpected error\n", __func__);
}
ublb_st[inst].rx_byte = data;
ublb_st[inst].Timer = nsi_hws_get_time() + nhw_uarte_one_byte_time(inst);
nhw_ublb_update_timer();
}
static void nhw_ublb_RTS_pin_toggle(uint inst, bool new_level) {
if (new_level){
nhw_UARTE_CTS_raised(inst);
} else {
nhw_UARTE_CTS_lowered(inst);
}
}
static void nhw_ublb_timer_triggered(void) {
bs_time_t current_time = Timer_ULoopback;
for (int i = 0; i < NHW_UARTE_TOTAL_INST; i++) {
if (ublb_st[i].Timer == current_time) {
nhw_UARTE_digest_Rx_byte(i, ublb_st[i].rx_byte);
ublb_st[i].Timer = TIME_NEVER;
}
}
nhw_ublb_update_timer();
}
NSI_HW_EVENT(Timer_ULoopback, nhw_ublb_timer_triggered, 40); /* Before the UART itself */