arm: async uart writes & fixes

This commit is contained in:
Paul Mathieu 2022-05-17 10:17:56 -07:00
parent e9f623e754
commit e39cdc5709
13 changed files with 232 additions and 190 deletions

View File

@ -6,27 +6,32 @@
namespace async { namespace async {
template<typename T = void> struct task_final_suspend {
struct task { bool await_ready() noexcept(true) { return false; }
struct promise_type; void await_suspend(std::coroutine_handle<> h) noexcept(true) {
using handle_type = std::coroutine_handle<promise_type>;
struct maybe_suspend {
bool await_ready() noexcept(true) { return !parent; }
void await_suspend(std::coroutine_handle<>) noexcept(true) {
if (parent) { if (parent) {
parent(); parent();
} }
h.destroy();
} }
void await_resume() noexcept(true) {} void await_resume() noexcept(true) {}
std::coroutine_handle<> parent; std::coroutine_handle<> parent;
}; };
template <typename T = void>
struct task {
struct promise_type;
using handle_type = std::coroutine_handle<promise_type>;
struct promise_type { struct promise_type {
task get_return_object() { return {.h = handle_type::from_promise(*this)}; } task get_return_object() {
return {.h = handle_type::from_promise(*this)};
}
std::suspend_always initial_suspend() noexcept { return {}; } std::suspend_always initial_suspend() noexcept { return {}; }
maybe_suspend final_suspend() noexcept { return { .parent = parent }; } task_final_suspend final_suspend() noexcept {
return {.parent = parent};
}
void return_value(T&& value) { ret_value = std::move(value); } void return_value(T&& value) { ret_value = std::move(value); }
void unhandled_exception() {} void unhandled_exception() {}
@ -50,22 +55,14 @@ struct task<void> {
struct promise_type; struct promise_type;
using handle_type = std::coroutine_handle<promise_type>; using handle_type = std::coroutine_handle<promise_type>;
struct maybe_suspend {
bool await_ready() noexcept(true) { return !parent; }
void await_suspend(std::coroutine_handle<>) noexcept(true) {
if (parent) {
parent();
}
}
void await_resume() noexcept(true) { }
std::coroutine_handle<> parent;
};
struct promise_type { struct promise_type {
task get_return_object() { return {.h = handle_type::from_promise(*this)}; } task get_return_object() {
return {.h = handle_type::from_promise(*this)};
}
std::suspend_always initial_suspend() noexcept { return {}; } std::suspend_always initial_suspend() noexcept { return {}; }
maybe_suspend final_suspend() noexcept { return { .parent = parent }; } task_final_suspend final_suspend() noexcept {
return {.parent = parent};
}
void return_void() {} void return_void() {}
void unhandled_exception() {} void unhandled_exception() {}
@ -75,8 +72,8 @@ struct task<void> {
// awaitable // awaitable
bool await_ready() { return h.done(); } bool await_ready() { return h.done(); }
void await_suspend(std::coroutine_handle<> ha) { void await_suspend(std::coroutine_handle<> ha) {
h();
h.promise().parent = ha; h.promise().parent = ha;
h();
} }
void await_resume() {} void await_resume() {}
@ -86,6 +83,7 @@ struct task<void> {
enum class AwaitableType { enum class AwaitableType {
kUnknown = 0, kUnknown = 0,
kUartRx = 1, kUartRx = 1,
kUartTx = 2,
kNumTypes kNumTypes
}; };
@ -101,9 +99,7 @@ inline auto await(AwaitableType type) {
AwaitableType type; AwaitableType type;
bool await_ready() { return false; }; bool await_ready() { return false; };
void await_suspend(std::coroutine_handle<> h) { void await_suspend(std::coroutine_handle<> h) { enqueue(h, type); }
enqueue(h, type);
}
void await_resume() {} void await_resume() {}
}; };
@ -115,9 +111,7 @@ inline auto delay(int ms) {
int ms; int ms;
bool await_ready() { return false; }; bool await_ready() { return false; };
void await_suspend(std::coroutine_handle<> h) { void await_suspend(std::coroutine_handle<> h) { schedule(h, ms); }
schedule(h, ms);
}
void await_resume() {} void await_resume() {}
}; };

View File

@ -17,7 +17,14 @@ struct buffer {
buffer& operator=(buffer& other) = delete; buffer& operator=(buffer& other) = delete;
buffer(buffer&& other) : data(std::exchange(other.data, {})) {} buffer(buffer&& other) : data(std::exchange(other.data, {})) {}
buffer& operator=(buffer&& other) { data = std::exchange(other.data, {}); return *this; } buffer& operator=(buffer&& other) {
data = std::exchange(other.data, {});
return *this;
}
~buffer() { if (data.data()) { delete[] data.data(); }; } ~buffer() {
if (data.data()) {
delete[] data.data();
};
}
}; };

View File

@ -15,19 +15,19 @@ namespace {
char addr[] = "00000000: "; char addr[] = "00000000: ";
int i = 0; int i = 0;
itoa(reinterpret_cast<uint32_t>(begin), addr); itoa(reinterpret_cast<uint32_t>(begin), addr);
UartSendCrash(addr); UartWriteCrash(addr);
for (uint32_t* ptr = begin; direction > 0 ? ptr < end : ptr > end; for (uint32_t* ptr = begin; direction > 0 ? ptr < end : ptr > end;
ptr += direction, i++) { ptr += direction, i++) {
itoa(*ptr, number); itoa(*ptr, number);
UartSendCrash(number); UartWriteCrash(number);
if (i % 4 == 3) { if (i % 4 == 3) {
UartSendCrash("\r\n"); UartWriteCrash("\r\n");
itoa(reinterpret_cast<uint32_t>(ptr + 1), addr); itoa(reinterpret_cast<uint32_t>(ptr + 1), addr);
UartSendCrash(addr); UartWriteCrash(addr);
} }
} }
if (i % 4 != 3) { if (i % 4 != 3) {
UartSendCrash("\r\n"); UartWriteCrash("\r\n");
} }
} }
@ -39,7 +39,7 @@ void StackTrace(uint32_t* sp) {
continue; continue;
} }
itoa(*ptr, number); itoa(*ptr, number);
UartSendCrash(number); UartWriteCrash(number);
} }
} }
@ -68,16 +68,16 @@ struct Armv6mRegs {
void CrashHandler(Armv6mRegs* regs) { void CrashHandler(Armv6mRegs* regs) {
char number[] = "00000000\r\n"; char number[] = "00000000\r\n";
UartSendCrash("\r\n\r\nCra$h!!\r\n- xpsr: 0x"); UartWriteCrash("\r\n\r\nCra$h!!\r\n- xpsr: 0x");
itoa(regs->xpsr, number); itoa(regs->xpsr, number);
UartSendCrash(number); UartWriteCrash(number);
UartSendCrash("- pc: 0x"); UartWriteCrash("- pc: 0x");
itoa(regs->pc, number); itoa(regs->pc, number);
UartSendCrash(number); UartWriteCrash(number);
UartSendCrash("- lr: 0x"); UartWriteCrash("- lr: 0x");
itoa(regs->lr, number); itoa(regs->lr, number);
UartSendCrash(number); UartWriteCrash(number);
UartSendCrash("- Stack trace:\r\n"); UartWriteCrash("- Stack trace:\r\n");
StackTrace(reinterpret_cast<uint32_t*>(regs->sp)); StackTrace(reinterpret_cast<uint32_t*>(regs->sp));
while (1) { while (1) {
@ -103,10 +103,13 @@ __attribute__((naked)) void HardFaultHandler() {
"mrs lr, msp \n" "mrs lr, msp \n"
"push {r0-r3, lr} \n" "push {r0-r3, lr} \n"
"push {r4-r7} \n" "push {r4-r7} \n"
:
: "r"(gpio0));
asm volatile(
"mrs r0, msp \n" "mrs r0, msp \n"
"mov r1, %1 \n" "mov r1, %0 \n"
"blx r1 \n" "blx r1 \n"
: :
: "r"(gpio0), "r"(CrashHandler)); : "r"(CrashHandler));
} }
} // namespace crash } // namespace crash

View File

@ -5,10 +5,7 @@
struct InterruptLock { struct InterruptLock {
bool was_locked; bool was_locked;
InterruptLock() InterruptLock() : was_locked(__get_PRIMASK() != 0) { __disable_irq(); }
: was_locked(__get_PRIMASK() != 0) {
__disable_irq();
}
~InterruptLock() { ~InterruptLock() {
if (!was_locked) { if (!was_locked) {

View File

@ -10,14 +10,9 @@
namespace { namespace {
using async::AwaitableType; using async::AwaitableType;
void HandleUartRxFromIsr(void*, unsigned int) {
tracing::trace(tracing::TraceEvent::kUartRxCb);
async::resume(AwaitableType::kUartRx);
}
void Uart0Isr() { void Uart0Isr() {
tracing::trace(tracing::TraceEvent::kUartIsr); tracing::trace(tracing::TraceEvent::kUartIsr);
XUartLite_InterruptHandler(uart0); HandleUartIsr();
} }
void Timer0Isr() { void Timer0Isr() {
@ -36,10 +31,6 @@ void SetupUart() {
vector_table[16 + Timer0_IRQn] = reinterpret_cast<uint32_t>(Timer0Isr); vector_table[16 + Timer0_IRQn] = reinterpret_cast<uint32_t>(Timer0Isr);
NVIC_SetPriority(Uart0_IRQn, 3); NVIC_SetPriority(Uart0_IRQn, 3);
NVIC_EnableIRQ(Uart0_IRQn); NVIC_EnableIRQ(Uart0_IRQn);
XUartLite_SetSendHandler(uart0, HandleUartTxFromIsr, nullptr);
XUartLite_SetRecvHandler(uart0, HandleUartRxFromIsr, nullptr);
XUartLite_EnableInterrupt(uart0);
} }
void SetupTimer() { void SetupTimer() {
@ -49,20 +40,10 @@ void SetupTimer() {
} }
} // namespace } // namespace
async::task<buffer> UartRead(int size) {
auto buff = buffer::make(size);
auto* data = reinterpret_cast<uint8_t*>(buff.data.data());
size_t received = XUartLite_Recv(uart0, data, buff.data.size());
if (received < buff.data.size()) {
co_await async::await(AwaitableType::kUartRx);
}
co_return buff;
}
async::task<> echo() { async::task<> echo() {
while (1) { while (1) {
buffer buff = co_await UartRead(1); buffer buff = co_await UartRead(1);
UartSend(buff.data); co_await UartWrite(buff.data);
} }
} }

View File

@ -23,7 +23,7 @@ includes += -Ihal/lib/common
sources += hal/uart/xuartlite.c hal/uart/xuartlite_stats.c hal/uart/xuartlite_intr.c sources += hal/uart/xuartlite.c hal/uart/xuartlite_stats.c hal/uart/xuartlite_intr.c
includes += -Ihal/uart includes += -Ihal/uart
bootloader_objects = uart.o bootloader.o vector_table.o $(sources:.c=.o) bootloader_objects = bootloader.o vector_table.o $(sources:.c=.o)
app_objects = app_init.o crash.o main.o uart.o stdlib.o async.o trace.o $(sources:.c=.o) app_objects = app_init.o crash.o main.o uart.o stdlib.o async.o trace.o $(sources:.c=.o)
CPPFLAGS += $(includes) CPPFLAGS += $(includes)

View File

@ -15,7 +15,8 @@ struct RingBuffer {
return false; return false;
} }
const size_t to_copy = std::min(buffer.size() - write_ptr, data.size()); const size_t to_copy = std::min(buffer.size() - write_ptr, data.size());
std::copy(data.begin(), data.begin() + to_copy, buffer.begin() + write_ptr); std::copy(data.begin(), data.begin() + to_copy,
buffer.begin() + write_ptr);
if (to_copy < data.size()) { if (to_copy < data.size()) {
std::copy(data.begin() + to_copy, data.end(), buffer.begin()); std::copy(data.begin() + to_copy, data.end(), buffer.begin());
} }
@ -29,9 +30,11 @@ struct RingBuffer {
return false; return false;
} }
const size_t to_copy = std::min(buffer.size() - read_ptr, out.size()); const size_t to_copy = std::min(buffer.size() - read_ptr, out.size());
std::copy(buffer.begin() + read_ptr, buffer.begin() + read_ptr + to_copy, out.begin()); std::copy(buffer.begin() + read_ptr,
buffer.begin() + read_ptr + to_copy, out.begin());
if (to_copy < out.size()) { if (to_copy < out.size()) {
std::copy(buffer.begin(), buffer.begin() + out.size() - to_copy, out.begin() + to_copy); std::copy(buffer.begin(), buffer.begin() + out.size() - to_copy,
out.begin() + to_copy);
} }
Pop(out.size()); Pop(out.size());
return true; return true;
@ -59,9 +62,7 @@ struct RingBuffer {
return true; return true;
} }
size_t FreeSpace() const { size_t FreeSpace() const { return buffer.size() - AvailableData(); }
return buffer.size() - AvailableData();
}
size_t AvailableData() const { size_t AvailableData() const {
if (read_ptr == write_ptr) { if (read_ptr == write_ptr) {

View File

@ -12,9 +12,9 @@ extern unsigned char _heap_begin, _heap_end;
namespace { namespace {
void LogStats(unsigned char* heap) { void LogStats(unsigned char* heap) {
char number[] = "00000000\r\n"; char number[] = "00000000\r\n";
UartSend("Program break now at 0x"); UartWriteBlocking("Program break now at 0x");
itoa(reinterpret_cast<int>(heap), number); itoa(reinterpret_cast<int>(heap), number);
UartSend(number); UartWriteBlocking(number);
} }
} // namespace } // namespace
@ -22,7 +22,7 @@ extern "C" void* _sbrk(int increment) {
static unsigned char* heap = &_heap_begin; static unsigned char* heap = &_heap_begin;
unsigned char* prev_heap = heap; unsigned char* prev_heap = heap;
if (heap + increment >= &_heap_end) { if (heap + increment >= &_heap_end) {
UartSend("Heap overflow!\r\n"); UartWriteBlocking("Heap overflow!\r\n");
return reinterpret_cast<void*>(-1); return reinterpret_cast<void*>(-1);
} }
heap += increment; heap += increment;

View File

@ -38,9 +38,7 @@ struct Timer {
TCSR1.ENT0 = 1; TCSR1.ENT0 = 1;
} }
uint32_t GetT1Ticks() { uint32_t GetT1Ticks() { return TCR1; }
return TCR1;
}
void SetupAsWdt(uint32_t timeout_ticks) { void SetupAsWdt(uint32_t timeout_ticks) {
TLR0 = timeout_ticks; TLR0 = timeout_ticks;

View File

@ -32,7 +32,7 @@ void dump() {
for (TraceEvent event : buffer) { for (TraceEvent event : buffer) {
itoa(static_cast<int>(event), number); itoa(static_cast<int>(event), number);
UartSendCrash(number); UartWriteCrash(number);
} }
} }
} // namespace tracing } // namespace tracing

View File

@ -1,10 +1,13 @@
#include "uart.h" #include "uart.h"
#include "async.h"
#include "gpio.h" #include "gpio.h"
#include "ring_buffer.h" #include "ring_buffer.h"
#include "trace.h" #include "trace.h"
#include "xuartlite.h"
namespace { namespace {
using async::AwaitableType;
constexpr uintptr_t kUart0BaseAddress = 0x40001000; constexpr uintptr_t kUart0BaseAddress = 0x40001000;
XUartLite uart0_inst; XUartLite uart0_inst;
@ -20,18 +23,22 @@ 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};
} // namespace
XUartLite* uart0 = &uart0_inst; XUartLite* uart0 = &uart0_inst;
} // namespace
void InitUarts() { void InitUarts() {
XUartLite_CfgInitialize(uart0, &uart0_config, uart0_config.RegBaseAddr); XUartLite_CfgInitialize(uart0, &uart0_config, uart0_config.RegBaseAddr);
XUartLite_SetSendHandler(uart0, HandleUartTxFromIsr, nullptr);
XUartLite_SetRecvHandler(uart0, HandleUartRxFromIsr, nullptr);
XUartLite_EnableInterrupt(uart0);
} }
// xuartlite private header stuff
extern "C" uint8_t XUartLite_GetSR(XUartLite* InstancePtr); extern "C" uint8_t XUartLite_GetSR(XUartLite* InstancePtr);
#define XUL_SR_TX_FIFO_EMPTY 0x04 /* transmit FIFO empty */ #define XUL_SR_TX_FIFO_EMPTY 0x04 /* transmit FIFO empty */
void UartSendCrash(std::span<const std::byte> data) { void UartWriteCrash(std::span<const std::byte> data) {
while (data.size() > 0) { while (data.size() > 0) {
while ((XUartLite_GetSR(uart0) & XUL_SR_TX_FIFO_EMPTY) == 0) { while ((XUartLite_GetSR(uart0) & XUL_SR_TX_FIFO_EMPTY) == 0) {
} }
@ -44,8 +51,27 @@ void UartSendCrash(std::span<const std::byte> data) {
} }
} }
// blocking async::task<> UartWrite(std::span<const std::byte> data) {
void UartSend(std::span<const std::byte> data) { while (!tx_ring_buffer.Store(data)) {
co_await async::await(AwaitableType::kUartTx);
}
if (!XUartLite_IsSending(uart0)) {
XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(),
tx_ring_buffer.ContiguousAvailableData());
}
}
void UartReadBlocking(std::span<std::byte> data) {
size_t bytes_received = 0;
while (bytes_received < data.size()) {
auto* buffer = reinterpret_cast<uint8_t*>(data.data() + bytes_received);
bytes_received +=
XUartLite_Recv(uart0, buffer, data.size() - bytes_received);
}
}
void UartWriteBlocking(std::span<const std::byte> data) {
while (!tx_ring_buffer.Store(data)) { while (!tx_ring_buffer.Store(data)) {
} }
@ -55,6 +81,16 @@ void UartSend(std::span<const std::byte> data) {
} }
} }
async::task<buffer> UartRead(int size) {
auto buff = buffer::make(size);
auto* data = reinterpret_cast<uint8_t*>(buff.data.data());
size_t received = XUartLite_Recv(uart0, data, buff.data.size());
if (received < buff.data.size()) {
co_await async::await(AwaitableType::kUartRx);
}
co_return buff;
}
void HandleUartTxFromIsr(void*, unsigned int transmitted) { void HandleUartTxFromIsr(void*, unsigned int transmitted) {
tracing::trace(tracing::TraceEvent::kUartTxCb); tracing::trace(tracing::TraceEvent::kUartTxCb);
tx_ring_buffer.Pop(transmitted); tx_ring_buffer.Pop(transmitted);
@ -62,4 +98,12 @@ void HandleUartTxFromIsr(void*, unsigned int transmitted) {
XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(),
tx_ring_buffer.ContiguousAvailableData()); tx_ring_buffer.ContiguousAvailableData());
} }
async::resume(AwaitableType::kUartTx);
} }
void HandleUartRxFromIsr(void*, unsigned int) {
tracing::trace(tracing::TraceEvent::kUartRxCb);
async::resume(AwaitableType::kUartRx);
}
void HandleUartIsr() { XUartLite_InterruptHandler(uart0); }

View File

@ -3,20 +3,37 @@
#include <span> #include <span>
#include <string_view> #include <string_view>
#include "xuartlite.h" #include "async.h"
#include "buffer.h"
extern XUartLite* uart0;
void InitUarts(); void InitUarts();
// blocking async::task<buffer> UartRead(int size);
void UartSend(std::span<const std::byte> data); async::task<> UartWrite(std::span<const std::byte> data);
inline void UartSend(std::string_view s) { inline async::task<> UartWrite(std::string_view s) {
return UartSend(std::as_bytes(std::span{s.data(), s.size()})); co_await UartWrite(std::as_bytes(std::span{s.data(), s.size()}));
} }
void UartSendCrash(std::span<const std::byte> data);
inline void UartSendCrash(std::string_view s) { // block until the provided buffer is full
return UartSendCrash(std::as_bytes(std::span{s.data(), s.size()})); void UartReadBlocking(std::span<std::byte> data);
inline uint8_t UartReadByteBlocking() {
std::byte byte;
UartReadBlocking(std::span{&byte, 1});
return static_cast<uint8_t>(byte);
}
// 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()}));
}
// block until room is available in tx fifo, then send
void UartWriteBlocking(std::span<const std::byte> data);
inline void UartWriteBlocking(std::string_view s) {
return UartWriteBlocking(std::as_bytes(std::span{s.data(), s.size()}));
} }
void HandleUartTxFromIsr(void*, unsigned int transmitted); void HandleUartTxFromIsr(void*, unsigned int transmitted);
void HandleUartRxFromIsr(void*, unsigned int);
void HandleUartIsr();