From f0f26af79184dfa11ff40c9d0eda1c06123d242f Mon Sep 17 00:00:00 2001 From: Paul Mathieu Date: Sun, 29 Jun 2025 13:56:58 -0700 Subject: [PATCH] mbv: i really though async would work this time There's still a glitch/race somewhere. --- mbv/apps/async/async.cc | 4 +- mbv/apps/async/async.h | 86 ++++++++++++++++++------------ mbv/apps/async/main.cc | 33 ++++++------ mbv/apps/async/uart.cc | 101 ++++++++++++++++-------------------- mbv/apps/async/uart_async.h | 2 +- mbv/configure | 4 +- 6 files changed, 121 insertions(+), 109 deletions(-) diff --git a/mbv/apps/async/async.cc b/mbv/apps/async/async.cc index 647891d..9bc5056 100644 --- a/mbv/apps/async/async.cc +++ b/mbv/apps/async/async.cc @@ -86,7 +86,7 @@ void step() { if (stuff->h.done()) { stuff->h.destroy(); - } + } // XXX: else?? { InterruptLock lock; @@ -165,4 +165,4 @@ void resume(AwaitableType type) { delete stuff; } -} // namespace async \ No newline at end of file +} // namespace async diff --git a/mbv/apps/async/async.h b/mbv/apps/async/async.h index aa68ac3..975c45d 100644 --- a/mbv/apps/async/async.h +++ b/mbv/apps/async/async.h @@ -27,38 +27,6 @@ struct task_final_suspend { std::coroutine_handle<> parent; }; -template -struct gimme { - // child interface - bool await_ready() { return false; } - void await_suspend(std::coroutine_handle<> h) { - ha = h; - waiting = true; - TRACE(tracing::TraceEvent::kAsyncGimmeWaiting); - } - T await_resume() { - waiting = false; - TRACE(tracing::TraceEvent::kAsyncGimmeResume); - return std::move(stuff); - } - - // parent interface - void feed(T&& s) { - if (!waiting) { - __builtin_trap(); - } - if (!ha) { - __builtin_trap(); - } - stuff = s; - ha.resume(); - } - - bool waiting = false; - std::coroutine_handle<> ha; - T stuff; -}; - template struct task; @@ -205,4 +173,58 @@ inline auto delay(int ms) { return awaitable{ms}; } +template +struct gimme { + // child interface + bool await_ready() { return false; } + void await_suspend(std::coroutine_handle<> h) { + ha = h; + waiting = true; + TRACE(tracing::TraceEvent::kAsyncGimmeWaiting); + if (suspended) { + schedule(parent); + } + } + T await_resume() { + suspended = false; + waiting = false; + TRACE(tracing::TraceEvent::kAsyncGimmeResume); + return std::move(stuff); + } + + // parent interface + auto feed(T&& s) { + struct awaitable { + bool await_ready() { + g->ha.resume(); + if (!g->waiting) { + g->suspended = true; + } + return !g->suspended; + } + void await_suspend(std::coroutine_handle<> h) { + g->parent = h; + } + void await_resume() { } + + gimme* g; + }; + + if (!waiting) { + __builtin_trap(); + } + if (!ha) { + __builtin_trap(); + } + stuff = s; + return awaitable{.g = this}; + } + + bool suspended = false; + bool waiting = false; + std::coroutine_handle<> ha; + std::coroutine_handle<> parent; + T stuff; +}; + } // namespace async diff --git a/mbv/apps/async/main.cc b/mbv/apps/async/main.cc index c52c14d..5397d40 100644 --- a/mbv/apps/async/main.cc +++ b/mbv/apps/async/main.cc @@ -11,11 +11,9 @@ namespace { -using async::AwaitableType; Timer* timer0; void Uart0Isr() { - ToggleLed(7); HandleUartIsr(); } @@ -48,16 +46,23 @@ void SetupInterrupts() { } async::task<> echo() { - async::task reader = UartReadLoop(); + async::task reader = UartReadLoop(); async::gimme> feeder; async::task<> writer = UartWriteLoop(feeder); writer.h.resume(); // advance to first yield while (1) { SetLed(1); - uint8_t c = co_await reader; + std::byte c = co_await reader; ClearLed(1); - ToggleLed(2); - feeder.feed(std::as_bytes(std::span{&c, 1})); + co_await feeder.feed(std::span{&c, 1}); + } +} + +async::task<> blink() { + while (1) { + co_await async::delay(500); + ToggleLed(0); + timer0->Pet(); } } @@ -65,25 +70,19 @@ async::task<> echo() { int main() { SetupUart(); - UartWriteCrash("uart setup done\r\n"); + UartWriteBlocking("uart setup done\r\n"); SetupTimer(); - UartWriteCrash("timer setup done\r\n"); + UartWriteBlocking("timer setup done\r\n"); gpio0->data = 0; SetupInterrupts(); async::schedule(echo().h); + async::schedule(blink().h); - UartWriteCrash("init done. starting main loop\r\n"); + UartWriteBlocking("init done. starting main loop\r\n"); - async::main_loop([]() { - static int cnt = 0; - timer0->Pet(); - if ((cnt++ % 100000) == 0) { - ToggleLed(0); - } - return false; - }); + async::main_loop(nullptr); // should never get here } diff --git a/mbv/apps/async/uart.cc b/mbv/apps/async/uart.cc index 7472d83..e3168db 100644 --- a/mbv/apps/async/uart.cc +++ b/mbv/apps/async/uart.cc @@ -22,13 +22,43 @@ XUartLite_Config uart0_config = { .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; -bool sending; +volatile bool sending; + +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 (sending) { + return; + } + size_t tosend = tx_ring_buffer.ContiguousAvailableData(); + if (tosend < 1) { + return; + } + sending = true; + XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), tosend); +} } // namespace void InitUarts() { @@ -36,6 +66,7 @@ void InitUarts() { XUartLite_SetSendHandler(uart0, HandleUartTxFromIsr, nullptr); XUartLite_SetRecvHandler(uart0, HandleUartRxFromIsr, nullptr); + StartReceiving(); XUartLite_EnableInterrupt(uart0); sending = false; @@ -53,23 +84,18 @@ void UartWriteCrash(std::span data) { } while (XUartLite_IsSending(uart0)) { } + XUartLite_Send(uart0, nullptr, 0); // reset buffer before enabling interrupts XUartLite_EnableInterrupt(uart0); } async::task<> UartWrite(std::span data) { while (!tx_ring_buffer.Store(data)) { - tracing::trace(tracing::TraceEvent::kUartTxBufferFull); co_await async::await(AwaitableType::kUartTx); - tracing::trace(tracing::TraceEvent::kUartTxBufferNotFull); } { InterruptLock lock; - if (!XUartLite_IsSending(uart0)) { - tracing::trace(tracing::TraceEvent::kUartSend); - XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), - tx_ring_buffer.ContiguousAvailableData()); - } + StartSending(); } } @@ -78,30 +104,19 @@ async::task<> UartWriteLoop( while (1) { auto data = co_await data_gen; while (!tx_ring_buffer.Store(data)) { - tracing::trace(tracing::TraceEvent::kUartTxBufferFull); co_await async::await(AwaitableType::kUartTx); - tracing::trace(tracing::TraceEvent::kUartTxBufferNotFull); } { InterruptLock lock; - if (!sending) { - tracing::trace(tracing::TraceEvent::kUartSend); - XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), - tx_ring_buffer.ContiguousAvailableData()); - sending = true; - } + StartSending(); } } } +// TODO: use chunks to allow receiving more than 256 bytes at once void UartReadBlocking(std::span data) { - size_t bytes_received = 0; - while (bytes_received < data.size()) { - auto* buffer = reinterpret_cast(data.data() + bytes_received); - tracing::trace(tracing::TraceEvent::kUartRecv); - bytes_received += - XUartLite_Recv(uart0, buffer, data.size() - bytes_received); + while (!rx_ring_buffer.Load(data)) { } } @@ -111,21 +126,14 @@ void UartWriteBlocking(std::span data) { { InterruptLock lock; - if (!XUartLite_IsSending(uart0)) { - tracing::trace(tracing::TraceEvent::kUartSend); - XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), - tx_ring_buffer.ContiguousAvailableData()); - } + StartSending(); } } -async::task UartReadLoop() { - uint8_t c; +async::task UartReadLoop() { + std::byte c; while (1) { - tracing::trace(tracing::TraceEvent::kUartRecv); - size_t received = XUartLite_Recv(uart0, &c, 1); - // some data may already be in the fifo, but if not, wait: - if (received < 1) { + while (!rx_ring_buffer.Load(std::span{&c, 1})) { co_await async::await(AwaitableType::kUartRx); } @@ -135,11 +143,7 @@ async::task UartReadLoop() { async::task UartRead(int size) { auto buff = buffer::make(size); - auto* data = reinterpret_cast(buff.data.data()); - tracing::trace(tracing::TraceEvent::kUartRecv); - size_t received = XUartLite_Recv(uart0, data, buff.data.size()); - // some data may already be in the fifo, but if not, wait: - if (received < buff.data.size()) { + while (!rx_ring_buffer.Load(buff.data)) { co_await async::await(AwaitableType::kUartRx); } co_return buff; @@ -148,27 +152,14 @@ 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; - } + StartSending(); async::resume(AwaitableType::kUartTx); } -void HandleUartRxFromIsr(void*, unsigned int) { +void HandleUartRxFromIsr(void*, unsigned int transmitted) { + rx_ring_buffer.Push(transmitted); + StartReceiving(); async::resume(AwaitableType::kUartRx); } void HandleUartIsr() { XUartLite_InterruptHandler(uart0); } - -extern "C" uint8_t XUartLite_GetSR(XUartLite*); - -uint8_t UartStatus() { return XUartLite_GetSR(uart0); } - -void LogStuff() { - uint8_t data = gpio0->data; - data |= (uart0->ReceiveBuffer.RemainingBytes & 0xf) << 4; - gpio0->data = data; -} diff --git a/mbv/apps/async/uart_async.h b/mbv/apps/async/uart_async.h index e6912aa..d9a8134 100644 --- a/mbv/apps/async/uart_async.h +++ b/mbv/apps/async/uart_async.h @@ -7,7 +7,7 @@ #include "buffer.h" async::task UartRead(int size); -async::task UartReadLoop(); +async::task UartReadLoop(); async::task<> UartWrite(std::span data); inline async::task<> UartWrite(std::string_view s) { co_await UartWrite(std::as_bytes(std::span{s.data(), s.size()})); diff --git a/mbv/configure b/mbv/configure index 754f332..d32f559 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/main2.cc", + "apps/async/main.cc", "apps/async/trace.cc", - "apps/async/uart2.cc", + "apps/async/uart.cc", ]), ]