diff --git a/arm/async.h b/arm/async.h index a813c16..219d7df 100644 --- a/arm/async.h +++ b/arm/async.h @@ -5,87 +5,85 @@ #include namespace async { - -template -struct task { - struct promise_type; - using handle_type = std::coroutine_handle; - - struct maybe_suspend { - bool await_ready() noexcept(true) { return !parent; } - void await_suspend(std::coroutine_handle<>) noexcept(true) { - if (parent) { - parent(); - } + +struct task_final_suspend { + bool await_ready() noexcept(true) { return false; } + void await_suspend(std::coroutine_handle<> h) noexcept(true) { + if (parent) { + parent(); + } + h.destroy(); } - void await_resume() noexcept(true) { } - + void await_resume() noexcept(true) {} + std::coroutine_handle<> parent; - }; - - struct promise_type { - task get_return_object() { return {.h = handle_type::from_promise(*this)}; } - std::suspend_always initial_suspend() noexcept { return {}; } - maybe_suspend final_suspend() noexcept { return { .parent = parent }; } - void return_value(T&& value) { ret_value = std::move(value); } - void unhandled_exception() {} - - T ret_value; - std::coroutine_handle<> parent; - }; - - // awaitable - bool await_ready() { return h.done(); } - void await_suspend(std::coroutine_handle<> ha) { - h(); - h.promise().parent = ha; - } - T await_resume() { return std::move(h.promise().ret_value); } - - std::coroutine_handle h; }; -template<> -struct task { - struct promise_type; - using handle_type = std::coroutine_handle; - - struct maybe_suspend { - bool await_ready() noexcept(true) { return !parent; } - void await_suspend(std::coroutine_handle<>) noexcept(true) { - if (parent) { - parent(); - } +template +struct task { + struct promise_type; + using handle_type = std::coroutine_handle; + + struct promise_type { + task get_return_object() { + return {.h = handle_type::from_promise(*this)}; + } + std::suspend_always initial_suspend() noexcept { return {}; } + task_final_suspend final_suspend() noexcept { + return {.parent = parent}; + } + void return_value(T&& value) { ret_value = std::move(value); } + void unhandled_exception() {} + + T ret_value; + std::coroutine_handle<> parent; + }; + + // awaitable + bool await_ready() { return h.done(); } + void await_suspend(std::coroutine_handle<> ha) { + h(); + h.promise().parent = ha; } - void await_resume() noexcept(true) { } - - std::coroutine_handle<> parent; - }; - - struct promise_type { - task get_return_object() { return {.h = handle_type::from_promise(*this)}; } - std::suspend_always initial_suspend() noexcept { return {}; } - maybe_suspend final_suspend() noexcept { return { .parent = parent }; } - void return_void() {} - void unhandled_exception() {} - - std::coroutine_handle<> parent; - }; - - // awaitable - bool await_ready() { return h.done(); } - void await_suspend(std::coroutine_handle<> ha) { - h(); - h.promise().parent = ha; - } - void await_resume() {} - - std::coroutine_handle h; + T await_resume() { return std::move(h.promise().ret_value); } + + std::coroutine_handle h; +}; + +template <> +struct task { + struct promise_type; + using handle_type = std::coroutine_handle; + + struct promise_type { + task get_return_object() { + return {.h = handle_type::from_promise(*this)}; + } + std::suspend_always initial_suspend() noexcept { return {}; } + task_final_suspend final_suspend() noexcept { + return {.parent = parent}; + } + void return_void() {} + void unhandled_exception() {} + + std::coroutine_handle<> parent; + }; + + // awaitable + bool await_ready() { return h.done(); } + void await_suspend(std::coroutine_handle<> ha) { + h.promise().parent = ha; + h(); + } + void await_resume() {} + + std::coroutine_handle h; }; enum class AwaitableType { kUnknown = 0, kUartRx = 1, + kUartTx = 2, kNumTypes }; @@ -97,31 +95,27 @@ void resume(AwaitableType type); // typically called from an ISR void main_loop(void (*idle_function)()); inline auto await(AwaitableType type) { - struct awaitable { - AwaitableType type; - - bool await_ready() { return false; }; - void await_suspend(std::coroutine_handle<> h) { - enqueue(h, type); - } - void await_resume() {} - }; - - return awaitable{type}; + struct awaitable { + AwaitableType type; + + bool await_ready() { return false; }; + void await_suspend(std::coroutine_handle<> h) { enqueue(h, type); } + void await_resume() {} + }; + + return awaitable{type}; } inline auto delay(int ms) { - struct awaitable { - int ms; - - bool await_ready() { return false; }; - void await_suspend(std::coroutine_handle<> h) { - schedule(h, ms); - } - void await_resume() {} - }; - - return awaitable{ms}; + struct awaitable { + int ms; + + bool await_ready() { return false; }; + void await_suspend(std::coroutine_handle<> h) { schedule(h, ms); } + void await_resume() {} + }; + + return awaitable{ms}; } } // namespace async diff --git a/arm/buffer.h b/arm/buffer.h index 9cbcc8b..3cb4971 100644 --- a/arm/buffer.h +++ b/arm/buffer.h @@ -2,22 +2,29 @@ #include #include - + struct buffer { - std::span data; - - buffer() = default; - buffer(std::span d) : data(d) {} - - static buffer make(size_t size) { - return buffer({new std::byte[size], size}); - } - - buffer(buffer& other) = delete; - buffer& operator=(buffer& other) = delete; - - buffer(buffer&& other) : data(std::exchange(other.data, {})) {} - buffer& operator=(buffer&& other) { data = std::exchange(other.data, {}); return *this; } - - ~buffer() { if (data.data()) { delete[] data.data(); }; } + std::span data; + + buffer() = default; + buffer(std::span d) : data(d) {} + + static buffer make(size_t size) { + return buffer({new std::byte[size], size}); + } + + buffer(buffer& other) = delete; + buffer& operator=(buffer& other) = delete; + + buffer(buffer&& other) : data(std::exchange(other.data, {})) {} + buffer& operator=(buffer&& other) { + data = std::exchange(other.data, {}); + return *this; + } + + ~buffer() { + if (data.data()) { + delete[] data.data(); + }; + } }; diff --git a/arm/crash.cc b/arm/crash.cc index b3ca8e3..daeda06 100644 --- a/arm/crash.cc +++ b/arm/crash.cc @@ -15,19 +15,19 @@ namespace { char addr[] = "00000000: "; int i = 0; itoa(reinterpret_cast(begin), addr); - UartSendCrash(addr); + UartWriteCrash(addr); for (uint32_t* ptr = begin; direction > 0 ? ptr < end : ptr > end; ptr += direction, i++) { itoa(*ptr, number); - UartSendCrash(number); + UartWriteCrash(number); if (i % 4 == 3) { - UartSendCrash("\r\n"); + UartWriteCrash("\r\n"); itoa(reinterpret_cast(ptr + 1), addr); - UartSendCrash(addr); + UartWriteCrash(addr); } } if (i % 4 != 3) { - UartSendCrash("\r\n"); + UartWriteCrash("\r\n"); } } @@ -39,7 +39,7 @@ void StackTrace(uint32_t* sp) { continue; } itoa(*ptr, number); - UartSendCrash(number); + UartWriteCrash(number); } } @@ -68,16 +68,16 @@ struct Armv6mRegs { void CrashHandler(Armv6mRegs* regs) { 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); - UartSendCrash(number); - UartSendCrash("- pc: 0x"); + UartWriteCrash(number); + UartWriteCrash("- pc: 0x"); itoa(regs->pc, number); - UartSendCrash(number); - UartSendCrash("- lr: 0x"); + UartWriteCrash(number); + UartWriteCrash("- lr: 0x"); itoa(regs->lr, number); - UartSendCrash(number); - UartSendCrash("- Stack trace:\r\n"); + UartWriteCrash(number); + UartWriteCrash("- Stack trace:\r\n"); StackTrace(reinterpret_cast(regs->sp)); while (1) { @@ -103,10 +103,13 @@ __attribute__((naked)) void HardFaultHandler() { "mrs lr, msp \n" "push {r0-r3, lr} \n" "push {r4-r7} \n" + : + : "r"(gpio0)); + asm volatile( "mrs r0, msp \n" - "mov r1, %1 \n" + "mov r1, %0 \n" "blx r1 \n" : - : "r"(gpio0), "r"(CrashHandler)); + : "r"(CrashHandler)); } } // namespace crash diff --git a/arm/gpio.h b/arm/gpio.h index 59051da..23e4a22 100644 --- a/arm/gpio.h +++ b/arm/gpio.h @@ -6,7 +6,7 @@ struct Gpio { volatile uint32_t data; }; -#define gpio0 ((Gpio*) 0x40000000) +#define gpio0 ((Gpio*)0x40000000) inline void ToggleLed(int which) { uint8_t data = gpio0->data; diff --git a/arm/lock.h b/arm/lock.h index dc97a49..bd6c6b6 100644 --- a/arm/lock.h +++ b/arm/lock.h @@ -5,10 +5,7 @@ struct InterruptLock { bool was_locked; - InterruptLock() - : was_locked(__get_PRIMASK() != 0) { - __disable_irq(); - } + InterruptLock() : was_locked(__get_PRIMASK() != 0) { __disable_irq(); } ~InterruptLock() { if (!was_locked) { diff --git a/arm/main.cc b/arm/main.cc index 8c6cc2a..abeadd0 100644 --- a/arm/main.cc +++ b/arm/main.cc @@ -10,14 +10,9 @@ namespace { using async::AwaitableType; -void HandleUartRxFromIsr(void*, unsigned int) { - tracing::trace(tracing::TraceEvent::kUartRxCb); - async::resume(AwaitableType::kUartRx); -} - void Uart0Isr() { tracing::trace(tracing::TraceEvent::kUartIsr); - XUartLite_InterruptHandler(uart0); + HandleUartIsr(); } void Timer0Isr() { @@ -36,10 +31,6 @@ void SetupUart() { vector_table[16 + Timer0_IRQn] = reinterpret_cast(Timer0Isr); NVIC_SetPriority(Uart0_IRQn, 3); NVIC_EnableIRQ(Uart0_IRQn); - - XUartLite_SetSendHandler(uart0, HandleUartTxFromIsr, nullptr); - XUartLite_SetRecvHandler(uart0, HandleUartRxFromIsr, nullptr); - XUartLite_EnableInterrupt(uart0); } void SetupTimer() { @@ -49,20 +40,10 @@ void SetupTimer() { } } // namespace -async::task UartRead(int size) { - auto buff = buffer::make(size); - auto* data = reinterpret_cast(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() { while (1) { buffer buff = co_await UartRead(1); - UartSend(buff.data); + co_await UartWrite(buff.data); } } diff --git a/arm/makefile b/arm/makefile index 5366cbc..0836039 100644 --- a/arm/makefile +++ b/arm/makefile @@ -23,7 +23,7 @@ includes += -Ihal/lib/common sources += hal/uart/xuartlite.c hal/uart/xuartlite_stats.c hal/uart/xuartlite_intr.c 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) CPPFLAGS += $(includes) diff --git a/arm/ring_buffer.h b/arm/ring_buffer.h index 56e8d37..ca6e368 100644 --- a/arm/ring_buffer.h +++ b/arm/ring_buffer.h @@ -15,7 +15,8 @@ struct RingBuffer { return false; } 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()) { std::copy(data.begin() + to_copy, data.end(), buffer.begin()); } @@ -29,9 +30,11 @@ struct RingBuffer { return false; } 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()) { - 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()); return true; @@ -59,9 +62,7 @@ struct RingBuffer { return true; } - size_t FreeSpace() const { - return buffer.size() - AvailableData(); - } + size_t FreeSpace() const { return buffer.size() - AvailableData(); } size_t AvailableData() const { if (read_ptr == write_ptr) { diff --git a/arm/stdlib.cc b/arm/stdlib.cc index b3fe6c9..1c2c44f 100644 --- a/arm/stdlib.cc +++ b/arm/stdlib.cc @@ -12,9 +12,9 @@ extern unsigned char _heap_begin, _heap_end; namespace { void LogStats(unsigned char* heap) { char number[] = "00000000\r\n"; - UartSend("Program break now at 0x"); + UartWriteBlocking("Program break now at 0x"); itoa(reinterpret_cast(heap), number); - UartSend(number); + UartWriteBlocking(number); } } // namespace @@ -22,7 +22,7 @@ extern "C" void* _sbrk(int increment) { static unsigned char* heap = &_heap_begin; unsigned char* prev_heap = heap; if (heap + increment >= &_heap_end) { - UartSend("Heap overflow!\r\n"); + UartWriteBlocking("Heap overflow!\r\n"); return reinterpret_cast(-1); } heap += increment; diff --git a/arm/timer.h b/arm/timer.h index c746eee..c379fa2 100644 --- a/arm/timer.h +++ b/arm/timer.h @@ -3,18 +3,18 @@ struct TimerControl { union { struct { - uint32_t MDT0 : 1; - uint32_t UDT0 : 1; + uint32_t MDT0 : 1; + uint32_t UDT0 : 1; uint32_t GENT0 : 1; uint32_t CAPT0 : 1; uint32_t ARHT0 : 1; uint32_t LOAD0 : 1; uint32_t ENIT0 : 1; - uint32_t ENT0 : 1; + uint32_t ENT0 : 1; uint32_t T0INT : 1; uint32_t PWMA0 : 1; uint32_t ENALL : 1; - uint32_t CASC : 1; + uint32_t CASC : 1; uint32_t reserved : 20; }; @@ -38,18 +38,16 @@ struct Timer { TCSR1.ENT0 = 1; } - uint32_t GetT1Ticks() { - return TCR1; - } + uint32_t GetT1Ticks() { return TCR1; } void SetupAsWdt(uint32_t timeout_ticks) { TLR0 = timeout_ticks; - TCSR0.LOAD0 = 1; // reset counter - TCSR0.UDT0 = 1; // count backwards from the load value - TCSR0.ENIT0 = 1; // enable interrupt + TCSR0.LOAD0 = 1; // reset counter + TCSR0.UDT0 = 1; // count backwards from the load value + TCSR0.ENIT0 = 1; // enable interrupt - TCSR0.LOAD0 = 0; // allow counter to run - TCSR0.ENT0 = 1; // enable timer + TCSR0.LOAD0 = 0; // allow counter to run + TCSR0.ENT0 = 1; // enable timer } void Pet() { @@ -60,4 +58,4 @@ struct Timer { } }; -#define timer0 ((Timer*) 0x40002000) +#define timer0 ((Timer*)0x40002000) diff --git a/arm/trace.cc b/arm/trace.cc index a47ee09..8daad23 100644 --- a/arm/trace.cc +++ b/arm/trace.cc @@ -32,7 +32,7 @@ void dump() { for (TraceEvent event : buffer) { itoa(static_cast(event), number); - UartSendCrash(number); + UartWriteCrash(number); } } } // namespace tracing diff --git a/arm/uart.cc b/arm/uart.cc index f8c4eea..cab5a07 100644 --- a/arm/uart.cc +++ b/arm/uart.cc @@ -1,10 +1,13 @@ #include "uart.h" +#include "async.h" #include "gpio.h" #include "ring_buffer.h" #include "trace.h" +#include "xuartlite.h" namespace { +using async::AwaitableType; constexpr uintptr_t kUart0BaseAddress = 0x40001000; XUartLite uart0_inst; @@ -20,18 +23,22 @@ constexpr size_t kUartTxBufferSize = 256; std::array tx_buffer = {}; RingBuffer tx_ring_buffer{.buffer = tx_buffer}; -} // namespace - XUartLite* uart0 = &uart0_inst; +} // namespace void InitUarts() { 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); #define XUL_SR_TX_FIFO_EMPTY 0x04 /* transmit FIFO empty */ -void UartSendCrash(std::span data) { +void UartWriteCrash(std::span data) { while (data.size() > 0) { while ((XUartLite_GetSR(uart0) & XUL_SR_TX_FIFO_EMPTY) == 0) { } @@ -44,8 +51,27 @@ void UartSendCrash(std::span data) { } } -// blocking -void UartSend(std::span data) { +async::task<> UartWrite(std::span 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 data) { + size_t bytes_received = 0; + while (bytes_received < data.size()) { + auto* buffer = reinterpret_cast(data.data() + bytes_received); + bytes_received += + XUartLite_Recv(uart0, buffer, data.size() - bytes_received); + } +} + +void UartWriteBlocking(std::span data) { while (!tx_ring_buffer.Store(data)) { } @@ -55,6 +81,16 @@ void UartSend(std::span data) { } } +async::task UartRead(int size) { + auto buff = buffer::make(size); + auto* data = reinterpret_cast(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) { tracing::trace(tracing::TraceEvent::kUartTxCb); tx_ring_buffer.Pop(transmitted); @@ -62,4 +98,12 @@ void HandleUartTxFromIsr(void*, unsigned int transmitted) { XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), 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); } diff --git a/arm/uart.h b/arm/uart.h index d7b0a28..7021b71 100644 --- a/arm/uart.h +++ b/arm/uart.h @@ -3,20 +3,37 @@ #include #include -#include "xuartlite.h" - -extern XUartLite* uart0; +#include "async.h" +#include "buffer.h" void InitUarts(); -// blocking -void UartSend(std::span data); -inline void UartSend(std::string_view s) { - return UartSend(std::as_bytes(std::span{s.data(), s.size()})); +async::task UartRead(int size); +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()})); } -void UartSendCrash(std::span data); -inline void UartSendCrash(std::string_view s) { - return UartSendCrash(std::as_bytes(std::span{s.data(), s.size()})); + +// block until the provided buffer is full +void UartReadBlocking(std::span data); +inline uint8_t UartReadByteBlocking() { + std::byte byte; + UartReadBlocking(std::span{&byte, 1}); + return static_cast(byte); +} + +// 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()})); +} + +// block until room is available in tx fifo, then send +void UartWriteBlocking(std::span 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 HandleUartRxFromIsr(void*, unsigned int); +void HandleUartIsr();