112 lines
4.3 KiB
C++
112 lines
4.3 KiB
C++
#include <cassert>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <stdexcept>
|
|
#include <helpers/bridges/RS232UartUtils.h>
|
|
|
|
using IRQn_Type = int;
|
|
struct NRF_UARTE_Type {
|
|
uint32_t INTENCLR = 0, EVENTS_RXTO = 0, EVENTS_TXSTOPPED = 0;
|
|
uint32_t TASKS_STOPRX = 0, TASKS_STOPTX = 0, ENABLE = 0;
|
|
struct { uint32_t TXD = 0, RXD = 0, RTS = 0, CTS = 0; } PSEL;
|
|
};
|
|
constexpr uint32_t UARTE_INTENSET_ENDRX_Msk = 1, UARTE_INTENSET_ENDTX_Msk = 2;
|
|
constexpr uint32_t UARTE_ENABLE_ENABLE_Disabled = 0;
|
|
const uint32_t g_ADigitalPinMap[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
|
|
static NRF_UARTE_Type* peripheral = nullptr;
|
|
static bool hardware_completes = false;
|
|
static unsigned stops = 0, yields = 0, deleted = 0, null_deletes = 0;
|
|
void NVIC_DisableIRQ(IRQn_Type) { ++stops; }
|
|
void yield() {
|
|
// Disabled hardware does not promise stop events. Bound the regression
|
|
// instead of hanging a test process inside the real core's stop loop.
|
|
if (hardware_completes) {
|
|
peripheral->EVENTS_RXTO = peripheral->EVENTS_TXSTOPPED = 1;
|
|
} else if (++yields == 3) {
|
|
throw std::runtime_error("unstarted UART stop wait");
|
|
}
|
|
}
|
|
void vSemaphoreDelete(void* semaphore) {
|
|
// FreeRTOS vQueueDelete asserts a non-null queue. Even an optimistic
|
|
// disabled-peripheral completion cannot make end() valid before begin().
|
|
if (!semaphore) {
|
|
++null_deletes;
|
|
throw std::runtime_error("FreeRTOS null semaphore assertion");
|
|
}
|
|
++deleted;
|
|
}
|
|
struct Uart {
|
|
NRF_UARTE_Type* nrfUart = nullptr;
|
|
IRQn_Type IRQn = 0;
|
|
uint8_t uc_pinRX = 0, uc_pinTX = 0, uc_hwFlow = 0;
|
|
void* _end_tx_sem = nullptr;
|
|
bool _begun = false;
|
|
struct { unsigned clears = 0; void clear() { ++clears; } } rxBuffer;
|
|
Uart(NRF_UARTE_Type*, IRQn_Type, uint8_t, uint8_t);
|
|
void end();
|
|
void setPins(uint8_t, uint8_t);
|
|
// The public accessor's body is the real Uart.h contract.
|
|
operator bool ()
|
|
{
|
|
return _begun;
|
|
}
|
|
// UART start is an explicit I/O boundary, not an emulation of baud/DMA.
|
|
void startForTest() {
|
|
assert(!_begun && !_end_tx_sem);
|
|
nrfUart->ENABLE = 8;
|
|
nrfUart->PSEL.RXD = uc_pinRX;
|
|
nrfUart->PSEL.TXD = uc_pinTX;
|
|
_end_tx_sem = this;
|
|
_begun = true;
|
|
}
|
|
};
|
|
#include "core_uart.inc"
|
|
|
|
int main() try {
|
|
NRF_UARTE_Type registers;
|
|
peripheral = ®isters;
|
|
Uart uart(®isters, 1, 0, 1);
|
|
assert(!uart && !uart._end_tx_sem && !registers.ENABLE);
|
|
|
|
// Negative controls exercise the frozen end() precondition itself. They
|
|
// prove why a harmless-looking unconditional stop is not valid cleanup.
|
|
bool failed = false;
|
|
try { uart.end(); }
|
|
catch (const std::runtime_error&) { failed = true; }
|
|
assert(failed && yields == 3 && stops == 1 && !deleted && !null_deletes);
|
|
hardware_completes = true;
|
|
failed = false;
|
|
try { uart.end(); }
|
|
catch (const std::runtime_error&) { failed = true; }
|
|
assert(failed && stops == 2 && !deleted && null_deletes == 1);
|
|
|
|
// Fresh first enable and an already-stopped standby resume do not call
|
|
// end(), touch the stop IRQ, or delete a semaphore that was never created.
|
|
for (unsigned retry = 0; retry < 2; ++retry) {
|
|
mesh::bridge::prepareNrfUart(uart, uint8_t(2), uint8_t(3));
|
|
assert(!uart && !uart._end_tx_sem && stops == 2 && !deleted);
|
|
assert(null_deletes == 1 && uart.uc_pinRX == 2 && uart.uc_pinTX == 3);
|
|
}
|
|
|
|
// A begun UART still must finish stop before pin remapping. All four PSEL
|
|
// values and the semaphore reflect the actual core's complete shutdown.
|
|
for (unsigned cycle = 0; cycle < 100; ++cycle) {
|
|
uart.startForTest();
|
|
assert(uart && registers.ENABLE == 8 && uart._end_tx_sem);
|
|
const uint8_t rx = uint8_t(cycle % 4 + 2), tx = uint8_t(cycle % 4 + 6);
|
|
mesh::bridge::prepareNrfUart(uart, rx, tx);
|
|
assert(!uart && !uart._end_tx_sem && !registers.ENABLE);
|
|
assert(deleted == cycle + 1 && stops == cycle + 3 && null_deletes == 1);
|
|
assert(registers.PSEL.RXD == UINT32_MAX && registers.PSEL.TXD == UINT32_MAX);
|
|
assert(registers.PSEL.RTS == UINT32_MAX && registers.PSEL.CTS == UINT32_MAX);
|
|
assert(uart.uc_pinRX == rx && uart.uc_pinTX == tx);
|
|
const unsigned stopped = stops, freed = deleted;
|
|
mesh::bridge::prepareNrfUart(uart, rx, tx);
|
|
assert(!uart && stops == stopped && deleted == freed);
|
|
}
|
|
puts("PASS: nRF UART lifecycle; fresh/stopped guards and 100 running remaps");
|
|
} catch (const std::exception& error) {
|
|
std::fprintf(stderr, "%s\n", error.what());
|
|
return 2;
|
|
}
|