mbv: debugging the async app

This commit is contained in:
Paul Mathieu 2025-06-27 12:58:43 -07:00
parent e4936abb53
commit e7b38fb560
8 changed files with 339 additions and 33 deletions

View File

@ -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) {

108
mbv/apps/async/main2.cc Normal file
View File

@ -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 <sys/time.h>
#include <cstdint>
#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<void*>(-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<volatile uint8_t*>(ptr);
bool ret;
{
InterruptLock lock;
ret = *dest;
*dest = val;
}
return ret;
}

View File

@ -1,6 +1,5 @@
#pragma once
#include <atomic>
#include <span>
#include "lock.h"
@ -8,9 +7,9 @@
struct RingBuffer {
std::span<std::byte> buffer;
std::atomic<size_t> read_ptr = 0;
std::atomic<size_t> write_ptr = 0;
std::atomic<bool> full = 0;
size_t read_ptr = 0;
size_t write_ptr = 0;
size_t used = 0;
bool Store(std::span<const std::byte> 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<uint8_t*>(buffer.data() + read_ptr);
}
uint8_t* RawWritePointer() const {
InterruptLock lock;
return reinterpret_cast<uint8_t*>(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);
}
};

View File

@ -27,6 +27,8 @@ std::array<std::byte, kUartTxBufferSize> 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<const std::byte> data) {
XUartLite_DisableInterrupt(uart0);
while (data.size() > 0) {
while (XUartLite_IsSending(uart0)) {
}
@ -48,6 +53,7 @@ void UartWriteCrash(std::span<const std::byte> data) {
}
while (XUartLite_IsSending(uart0)) {
}
XUartLite_EnableInterrupt(uart0);
}
async::task<> UartWrite(std::span<const std::byte> 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<buffer> 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);
}

119
mbv/apps/async/uart2.cc Normal file
View File

@ -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<std::byte, kUartRxBufferSize> rx_buffer = {};
RingBuffer rx_ring_buffer{.buffer = rx_buffer};
constexpr size_t kUartTxBufferSize = 256;
std::array<std::byte, kUartTxBufferSize> 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<const std::byte> data) {
XUartLite_DisableInterrupt(uart0);
while (data.size() > 0) {
while (XUartLite_IsSending(uart0)) {
}
auto* dat =
reinterpret_cast<uint8_t*>(const_cast<std::byte*>(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); }

18
mbv/apps/async/uart2.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <span>
#include <string_view>
void InitUarts();
// send and poll the uart until transmitted
void UartWriteCrash(std::span<const std::byte> 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();

4
mbv/configure vendored
View File

@ -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",
]),
]

62
mbv/testecho.py Normal file
View File

@ -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()