mbv: i really though async would work this time

There's still a glitch/race somewhere.
This commit is contained in:
Paul Mathieu 2025-06-29 13:56:58 -07:00
parent 724f8db1b1
commit f0f26af791
6 changed files with 121 additions and 109 deletions

View File

@ -86,7 +86,7 @@ void step() {
if (stuff->h.done()) { if (stuff->h.done()) {
stuff->h.destroy(); stuff->h.destroy();
} } // XXX: else??
{ {
InterruptLock lock; InterruptLock lock;

View File

@ -27,38 +27,6 @@ struct task_final_suspend {
std::coroutine_handle<> parent; std::coroutine_handle<> parent;
}; };
template <typename T>
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 <typename T = void> template <typename T = void>
struct task; struct task;
@ -205,4 +173,58 @@ inline auto delay(int ms) {
return awaitable{ms}; return awaitable{ms};
} }
template <typename T>
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<T>* 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 } // namespace async

View File

@ -11,11 +11,9 @@
namespace { namespace {
using async::AwaitableType;
Timer* timer0; Timer* timer0;
void Uart0Isr() { void Uart0Isr() {
ToggleLed(7);
HandleUartIsr(); HandleUartIsr();
} }
@ -48,16 +46,23 @@ void SetupInterrupts() {
} }
async::task<> echo() { async::task<> echo() {
async::task<uint8_t> reader = UartReadLoop(); async::task<std::byte> reader = UartReadLoop();
async::gimme<std::span<const std::byte>> feeder; async::gimme<std::span<const std::byte>> feeder;
async::task<> writer = UartWriteLoop(feeder); async::task<> writer = UartWriteLoop(feeder);
writer.h.resume(); // advance to first yield writer.h.resume(); // advance to first yield
while (1) { while (1) {
SetLed(1); SetLed(1);
uint8_t c = co_await reader; std::byte c = co_await reader;
ClearLed(1); ClearLed(1);
ToggleLed(2); co_await feeder.feed(std::span{&c, 1});
feeder.feed(std::as_bytes(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() { int main() {
SetupUart(); SetupUart();
UartWriteCrash("uart setup done\r\n"); UartWriteBlocking("uart setup done\r\n");
SetupTimer(); SetupTimer();
UartWriteCrash("timer setup done\r\n"); UartWriteBlocking("timer setup done\r\n");
gpio0->data = 0; gpio0->data = 0;
SetupInterrupts(); SetupInterrupts();
async::schedule(echo().h); 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([]() { async::main_loop(nullptr);
static int cnt = 0;
timer0->Pet();
if ((cnt++ % 100000) == 0) {
ToggleLed(0);
}
return false;
});
// should never get here // should never get here
} }

View File

@ -22,13 +22,43 @@ XUartLite_Config uart0_config = {
.DataBits = 8, .DataBits = 8,
}; };
constexpr size_t kUartRxBufferSize = 256;
std::array<std::byte, kUartRxBufferSize> rx_buffer = {};
RingBuffer rx_ring_buffer{.buffer = rx_buffer};
constexpr size_t kUartTxBufferSize = 256; constexpr size_t kUartTxBufferSize = 256;
std::array<std::byte, kUartTxBufferSize> tx_buffer = {}; std::array<std::byte, kUartTxBufferSize> tx_buffer = {};
RingBuffer tx_ring_buffer{.buffer = tx_buffer}; RingBuffer tx_ring_buffer{.buffer = tx_buffer};
XUartLite* uart0 = &uart0_inst; 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 } // namespace
void InitUarts() { void InitUarts() {
@ -36,6 +66,7 @@ void InitUarts() {
XUartLite_SetSendHandler(uart0, HandleUartTxFromIsr, nullptr); XUartLite_SetSendHandler(uart0, HandleUartTxFromIsr, nullptr);
XUartLite_SetRecvHandler(uart0, HandleUartRxFromIsr, nullptr); XUartLite_SetRecvHandler(uart0, HandleUartRxFromIsr, nullptr);
StartReceiving();
XUartLite_EnableInterrupt(uart0); XUartLite_EnableInterrupt(uart0);
sending = false; sending = false;
@ -53,23 +84,18 @@ void UartWriteCrash(std::span<const std::byte> data) {
} }
while (XUartLite_IsSending(uart0)) { while (XUartLite_IsSending(uart0)) {
} }
XUartLite_Send(uart0, nullptr, 0); // reset buffer before enabling interrupts
XUartLite_EnableInterrupt(uart0); XUartLite_EnableInterrupt(uart0);
} }
async::task<> UartWrite(std::span<const std::byte> data) { async::task<> UartWrite(std::span<const std::byte> data) {
while (!tx_ring_buffer.Store(data)) { while (!tx_ring_buffer.Store(data)) {
tracing::trace(tracing::TraceEvent::kUartTxBufferFull);
co_await async::await(AwaitableType::kUartTx); co_await async::await(AwaitableType::kUartTx);
tracing::trace(tracing::TraceEvent::kUartTxBufferNotFull);
} }
{ {
InterruptLock lock; InterruptLock lock;
if (!XUartLite_IsSending(uart0)) { StartSending();
tracing::trace(tracing::TraceEvent::kUartSend);
XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(),
tx_ring_buffer.ContiguousAvailableData());
}
} }
} }
@ -78,30 +104,19 @@ async::task<> UartWriteLoop(
while (1) { while (1) {
auto data = co_await data_gen; auto data = co_await data_gen;
while (!tx_ring_buffer.Store(data)) { while (!tx_ring_buffer.Store(data)) {
tracing::trace(tracing::TraceEvent::kUartTxBufferFull);
co_await async::await(AwaitableType::kUartTx); co_await async::await(AwaitableType::kUartTx);
tracing::trace(tracing::TraceEvent::kUartTxBufferNotFull);
} }
{ {
InterruptLock lock; InterruptLock lock;
if (!sending) { StartSending();
tracing::trace(tracing::TraceEvent::kUartSend);
XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(),
tx_ring_buffer.ContiguousAvailableData());
sending = true;
}
} }
} }
} }
// TODO: use chunks to allow receiving more than 256 bytes at once
void UartReadBlocking(std::span<std::byte> data) { void UartReadBlocking(std::span<std::byte> data) {
size_t bytes_received = 0; while (!rx_ring_buffer.Load(data)) {
while (bytes_received < data.size()) {
auto* buffer = reinterpret_cast<uint8_t*>(data.data() + bytes_received);
tracing::trace(tracing::TraceEvent::kUartRecv);
bytes_received +=
XUartLite_Recv(uart0, buffer, data.size() - bytes_received);
} }
} }
@ -111,21 +126,14 @@ void UartWriteBlocking(std::span<const std::byte> data) {
{ {
InterruptLock lock; InterruptLock lock;
if (!XUartLite_IsSending(uart0)) { StartSending();
tracing::trace(tracing::TraceEvent::kUartSend);
XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(),
tx_ring_buffer.ContiguousAvailableData());
}
} }
} }
async::task<uint8_t> UartReadLoop() { async::task<std::byte> UartReadLoop() {
uint8_t c; std::byte c;
while (1) { while (1) {
tracing::trace(tracing::TraceEvent::kUartRecv); while (!rx_ring_buffer.Load(std::span{&c, 1})) {
size_t received = XUartLite_Recv(uart0, &c, 1);
// some data may already be in the fifo, but if not, wait:
if (received < 1) {
co_await async::await(AwaitableType::kUartRx); co_await async::await(AwaitableType::kUartRx);
} }
@ -135,11 +143,7 @@ async::task<uint8_t> UartReadLoop() {
async::task<buffer> UartRead(int size) { async::task<buffer> UartRead(int size) {
auto buff = buffer::make(size); auto buff = buffer::make(size);
auto* data = reinterpret_cast<uint8_t*>(buff.data.data()); while (!rx_ring_buffer.Load(buff.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()) {
co_await async::await(AwaitableType::kUartRx); co_await async::await(AwaitableType::kUartRx);
} }
co_return buff; co_return buff;
@ -148,27 +152,14 @@ async::task<buffer> UartRead(int size) {
void HandleUartTxFromIsr(void*, unsigned int transmitted) { void HandleUartTxFromIsr(void*, unsigned int transmitted) {
sending = false; sending = false;
tx_ring_buffer.Pop(transmitted); tx_ring_buffer.Pop(transmitted);
if (tx_ring_buffer.AvailableData() > 0) { StartSending();
tracing::trace(tracing::TraceEvent::kUartSend);
XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(),
tx_ring_buffer.ContiguousAvailableData());
sending = true;
}
async::resume(AwaitableType::kUartTx); 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); async::resume(AwaitableType::kUartRx);
} }
void HandleUartIsr() { XUartLite_InterruptHandler(uart0); } 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;
}

View File

@ -7,7 +7,7 @@
#include "buffer.h" #include "buffer.h"
async::task<buffer> UartRead(int size); async::task<buffer> UartRead(int size);
async::task<uint8_t> UartReadLoop(); async::task<std::byte> UartReadLoop();
async::task<> UartWrite(std::span<const std::byte> data); async::task<> UartWrite(std::span<const std::byte> data);
inline async::task<> UartWrite(std::string_view s) { inline async::task<> UartWrite(std::string_view s) {
co_await UartWrite(std::as_bytes(std::span{s.data(), s.size()})); co_await UartWrite(std::as_bytes(std::span{s.data(), s.size()}));

4
mbv/configure vendored
View File

@ -304,9 +304,9 @@ all = [
app_image("async", sources=[ app_image("async", sources=[
"apps/async/async.cc", "apps/async/async.cc",
"apps/async/lock.cc", "apps/async/lock.cc",
"apps/async/main2.cc", "apps/async/main.cc",
"apps/async/trace.cc", "apps/async/trace.cc",
"apps/async/uart2.cc", "apps/async/uart.cc",
]), ]),
] ]