From e7b38fb5603ee7cced66bd26d9a941215e64d536 Mon Sep 17 00:00:00 2001 From: Paul Mathieu Date: Fri, 27 Jun 2025 12:58:43 -0700 Subject: [PATCH] mbv: debugging the async app --- mbv/apps/async/main.cc | 7 --- mbv/apps/async/main2.cc | 108 +++++++++++++++++++++++++++++++ mbv/apps/async/ring_buffer.h | 43 ++++++------- mbv/apps/async/uart.cc | 11 +++- mbv/apps/async/uart2.cc | 119 +++++++++++++++++++++++++++++++++++ mbv/apps/async/uart2.h | 18 ++++++ mbv/configure | 4 +- mbv/testecho.py | 62 ++++++++++++++++++ 8 files changed, 339 insertions(+), 33 deletions(-) create mode 100644 mbv/apps/async/main2.cc create mode 100644 mbv/apps/async/uart2.cc create mode 100644 mbv/apps/async/uart2.h create mode 100644 mbv/testecho.py diff --git a/mbv/apps/async/main.cc b/mbv/apps/async/main.cc index a531eaf..c52c14d 100644 --- a/mbv/apps/async/main.cc +++ b/mbv/apps/async/main.cc @@ -63,9 +63,6 @@ async::task<> echo() { } // namespace -#define XUL_SR_RX_FIFO_FULL 0x02 /* receive FIFO full */ -#define XUL_SR_RX_FIFO_VALID_DATA 0x01 /* data in receive FIFO */ - int main() { SetupUart(); UartWriteCrash("uart setup done\r\n"); @@ -99,10 +96,6 @@ int main() { #include "itoa.h" #include "lock.h" -#ifndef SBRK_STATS -#define SBRK_STATS 0 -#endif - extern unsigned char _heap_begin, _heap_end; extern "C" void* _sbrk(int increment) { diff --git a/mbv/apps/async/main2.cc b/mbv/apps/async/main2.cc new file mode 100644 index 0000000..73a2828 --- /dev/null +++ b/mbv/apps/async/main2.cc @@ -0,0 +1,108 @@ +#include "buffer.h" +#include "gpio.h" +#include "intc.h" +#include "interrupts.h" +#include "pol0.h" +#include "timer.h" +#include "trace.h" +#include "uart2.h" + +namespace { + +Timer* timer0; + +void Uart0Isr() { +// ToggleLed(7); + HandleUartIsr(); +} + +void Timer0Isr() { + SetLed(6); + __builtin_trap(); +} + +void SetupUart() { + InitUarts(); + + intc::SetIsr(UART0_IRQN, Uart0Isr); + intc::SetIrqEnabled(UART0_IRQN, true); +} + +void SetupTimer() { + timer0 = Timer::Instance(TIMER0_BASE); +// timer0->SetupAsWdt(100'000 * 1000); + timer0->EnableT1(); + + intc::SetIsr(TIMER0_IRQN, Timer0Isr); + intc::SetIrqEnabled(TIMER0_IRQN, true); +} + +void SetupInterrupts() { + intc::EnableInterrupts(); + SetExternalInterruptHandler(intc::InterruptHandler); + EnableExternalInterrupts(); + EnableInterrupts(true); +} + +} // namespace + +int main() { + SetupUart(); + UartWriteCrash("uart setup done\r\n"); + SetupTimer(); + UartWriteCrash("timer setup done\r\n"); + + SetupInterrupts(); + + UartWriteCrash("init done. starting main loop\r\n"); + + UartEcho(); + + // should never get here +} + +/// stdlib stuff + +#include + +#include + +#include "itoa.h" +#include "lock.h" + +extern unsigned char _heap_begin, _heap_end; + +extern "C" void* _sbrk(int increment) { + static unsigned char* heap = &_heap_begin; + unsigned char* prev_heap = heap; + if (heap + increment >= &_heap_end) { + UartWriteCrash("Heap overflow!\r\n"); + return reinterpret_cast(-1); + } + heap += increment; + return prev_heap; +} + +extern "C" int _gettimeofday(struct timeval* tv, void* tzvp) { + (void)tzvp; + uint32_t ticks = timer0->GetT1Ticks(); + tv->tv_sec = ticks / 100000000; + tv->tv_usec = (ticks % 100000000) / 100; + + return 0; +} + +extern "C" uint8_t __atomic_exchange_1(volatile void* ptr, uint8_t val, + int memorder) { + (void)memorder; + auto* dest = reinterpret_cast(ptr); + bool ret; + + { + InterruptLock lock; + ret = *dest; + *dest = val; + } + + return ret; +} diff --git a/mbv/apps/async/ring_buffer.h b/mbv/apps/async/ring_buffer.h index 56a496e..d24b233 100644 --- a/mbv/apps/async/ring_buffer.h +++ b/mbv/apps/async/ring_buffer.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include "lock.h" @@ -8,9 +7,9 @@ struct RingBuffer { std::span buffer; - std::atomic read_ptr = 0; - std::atomic write_ptr = 0; - std::atomic full = 0; + size_t read_ptr = 0; + size_t write_ptr = 0; + size_t used = 0; bool Store(std::span data) { InterruptLock lock; @@ -53,9 +52,7 @@ struct RingBuffer { return false; } write_ptr = (write_ptr + amount) % buffer.size(); - if (read_ptr == write_ptr) { - full = true; - } + used = used + amount; return true; } @@ -66,25 +63,20 @@ struct RingBuffer { return false; } read_ptr = (read_ptr + amount) % buffer.size(); - if (amount > 0) { - full = false; - } + used = used - amount; return true; } size_t FreeSpace() const { InterruptLock lock; - return buffer.size() - AvailableData(); + return buffer.size() - used; } size_t AvailableData() const { InterruptLock lock; - if (read_ptr == write_ptr) { - return full ? buffer.size() : 0; - } - return (buffer.size() + write_ptr - read_ptr) % buffer.size(); + return used; } uint8_t* RawReadPointer() const { @@ -93,16 +85,21 @@ struct RingBuffer { return reinterpret_cast(buffer.data() + read_ptr); } + uint8_t* RawWritePointer() const { + InterruptLock lock; + + return reinterpret_cast(buffer.data() + write_ptr); + } + + size_t ContiguousFreeSpace() const { + InterruptLock lock; + + return std::min(FreeSpace(), buffer.size() - write_ptr); + } + size_t ContiguousAvailableData() const { InterruptLock lock; - if (read_ptr < write_ptr) { - return AvailableData(); - } - if (full) { - return 0; - } - - return buffer.size() - read_ptr; + return std::min(AvailableData(), buffer.size() - read_ptr); } }; diff --git a/mbv/apps/async/uart.cc b/mbv/apps/async/uart.cc index fcfcf97..7472d83 100644 --- a/mbv/apps/async/uart.cc +++ b/mbv/apps/async/uart.cc @@ -27,6 +27,8 @@ std::array tx_buffer = {}; RingBuffer tx_ring_buffer{.buffer = tx_buffer}; XUartLite* uart0 = &uart0_inst; + +bool sending; } // namespace void InitUarts() { @@ -35,9 +37,12 @@ void InitUarts() { XUartLite_SetSendHandler(uart0, HandleUartTxFromIsr, nullptr); XUartLite_SetRecvHandler(uart0, HandleUartRxFromIsr, nullptr); XUartLite_EnableInterrupt(uart0); + + sending = false; } void UartWriteCrash(std::span data) { + XUartLite_DisableInterrupt(uart0); while (data.size() > 0) { while (XUartLite_IsSending(uart0)) { } @@ -48,6 +53,7 @@ void UartWriteCrash(std::span data) { } while (XUartLite_IsSending(uart0)) { } + XUartLite_EnableInterrupt(uart0); } async::task<> UartWrite(std::span data) { @@ -79,10 +85,11 @@ async::task<> UartWriteLoop( { InterruptLock lock; - if (!XUartLite_IsSending(uart0)) { + if (!sending) { tracing::trace(tracing::TraceEvent::kUartSend); XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), tx_ring_buffer.ContiguousAvailableData()); + sending = true; } } } @@ -139,11 +146,13 @@ async::task UartRead(int size) { } void HandleUartTxFromIsr(void*, unsigned int transmitted) { + sending = false; tx_ring_buffer.Pop(transmitted); if (tx_ring_buffer.AvailableData() > 0) { tracing::trace(tracing::TraceEvent::kUartSend); XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), tx_ring_buffer.ContiguousAvailableData()); + sending = true; } async::resume(AwaitableType::kUartTx); } diff --git a/mbv/apps/async/uart2.cc b/mbv/apps/async/uart2.cc new file mode 100644 index 0000000..f6c9fbb --- /dev/null +++ b/mbv/apps/async/uart2.cc @@ -0,0 +1,119 @@ +#include "uart2.h" + +#include "gpio.h" +#include "lock.h" +#include "pol0.h" +#include "ring_buffer.h" +#include "xuartlite.h" + +namespace { + +constexpr uintptr_t kUart0BaseAddress = UART0_BASE; +XUartLite uart0_inst; +XUartLite_Config uart0_config = { + .DeviceId = 0, + .RegBaseAddr = kUart0BaseAddress, + .BaudRate = 115200, + .UseParity = false, + .DataBits = 8, +}; + +constexpr size_t kUartRxBufferSize = 256; +std::array rx_buffer = {}; +RingBuffer rx_ring_buffer{.buffer = rx_buffer}; + +constexpr size_t kUartTxBufferSize = 256; +std::array tx_buffer = {}; +RingBuffer tx_ring_buffer{.buffer = tx_buffer}; + +XUartLite* uart0 = &uart0_inst; + +volatile int sending = 0; + +void StartReceiving() { + while (1) { + if (rx_ring_buffer.FreeSpace() < 1) { + // woops, full. discard some data + // TODO: keep track of overrun stats + rx_ring_buffer.Pop(1); + } + if (XUartLite_Recv(uart0, rx_ring_buffer.RawWritePointer(), 1) < 1) { + break; + } + rx_ring_buffer.Push(1); + } +} + +void StartSending() { + if (tx_ring_buffer.AvailableData() > 0 && sending < 1) { + sending += 1; + XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), 1); + } +} + +std::byte UartReadByte() { + std::byte c; + while (!rx_ring_buffer.Load(std::span{&c, 1})) { + } + + return c; +} + +void UartWriteByte(std::byte c) { + while (!tx_ring_buffer.Store(std::span{&c, 1})) { + } + { + InterruptLock lock; + StartSending(); + } +} + +} // namespace + +void InitUarts() { + XUartLite_CfgInitialize(uart0, &uart0_config, uart0_config.RegBaseAddr); + + XUartLite_SetSendHandler(uart0, HandleUartTxFromIsr, nullptr); + XUartLite_SetRecvHandler(uart0, HandleUartRxFromIsr, nullptr); + StartReceiving(); + XUartLite_EnableInterrupt(uart0); +} + +void UartWriteCrash(std::span data) { + XUartLite_DisableInterrupt(uart0); + while (data.size() > 0) { + while (XUartLite_IsSending(uart0)) { + } + auto* dat = + reinterpret_cast(const_cast(data.data())); + uint8_t sent = XUartLite_Send(uart0, dat, data.size()); + data = data.subspan(sent); + } + while (XUartLite_IsSending(uart0)) { + } + XUartLite_EnableInterrupt(uart0); +} + +void UartEcho() { + while (1) { + gpio0->data = tx_ring_buffer.AvailableData(); + + std::byte c = UartReadByte(); + UartWriteByte(c); + + //gpio0->data = uart0->Stats.ReceiveOverrunErrors; + } +} + +void HandleUartTxFromIsr(void*, unsigned int transmitted) { + sending -= 1; + tx_ring_buffer.Pop(transmitted); + StartSending(); +} + +void HandleUartRxFromIsr(void*, unsigned int transmitted) { + rx_ring_buffer.Push(transmitted); + StartReceiving(); +} + +void HandleUartIsr() { XUartLite_InterruptHandler(uart0); } diff --git a/mbv/apps/async/uart2.h b/mbv/apps/async/uart2.h new file mode 100644 index 0000000..54fa55f --- /dev/null +++ b/mbv/apps/async/uart2.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +void InitUarts(); + +// send and poll the uart until transmitted +void UartWriteCrash(std::span data); +inline void UartWriteCrash(std::string_view s) { + return UartWriteCrash(std::as_bytes(std::span{s.data(), s.size()})); +} + +void UartEcho(); + +void HandleUartTxFromIsr(void*, unsigned int transmitted); +void HandleUartRxFromIsr(void*, unsigned int); +void HandleUartIsr(); diff --git a/mbv/configure b/mbv/configure index d32f559..754f332 100755 --- a/mbv/configure +++ b/mbv/configure @@ -304,9 +304,9 @@ all = [ app_image("async", sources=[ "apps/async/async.cc", "apps/async/lock.cc", - "apps/async/main.cc", + "apps/async/main2.cc", "apps/async/trace.cc", - "apps/async/uart.cc", + "apps/async/uart2.cc", ]), ] diff --git a/mbv/testecho.py b/mbv/testecho.py new file mode 100644 index 0000000..107cb95 --- /dev/null +++ b/mbv/testecho.py @@ -0,0 +1,62 @@ +import argparse +import random +import serial +import sys +import threading +import time + +tty = "/dev/ttyUSB1" +baud = 115200 + + +def sendrecv(s, size, timeout=5): + """Send some data and recv until the same data was received back.""" + data = random.randbytes(size) + return sendcheck(s, data, timeout) + + +def sendcheck(s, data, timeout=5): + s.write(data) + received = 0 + start = time.time() + got = bytearray() + while received < len(data): + r = s.read() + got += r + try: + assert(r == data[received: received + len(r)]) + assert(time.time() < start + timeout) + except: + fr = received - 10 + to = received + len(r) + 1 + sdat = data[fr: to] + rdat = bytes(got[fr: to]) + print(f"failed after receiving {received} correct bytes") + print(f"expected {sdat}") + print(f" got {rdat}") + sys.exit(-1) + + received += len(r) + + +def parse_args(): + parser = argparse.ArgumentParser() + return parser.parse_args() + + +def main(): + args = parse_args() + + s = serial.Serial(tty, baud, timeout=1) + for i in range(12): + size = 2**i + print(f"Trying with {size} bytes") + sendrecv(s, size) + data = b'ze%s\x96:M#\xd8\x98\x9d\x96\xf5?\x80c\xc6\xa7\x03\xe0i\x04V\xcb\xa3\x95#GC\xabf\x98' + #sendcheck(s, data) + + print("All sizes passed.") + + +if __name__ == "__main__": + main()