#include "uart.h" #include "async.h" #include "gpio.h" #include "lock.h" #include "ring_buffer.h" #include "trace.h" #include "uart_async.h" #include "xuartlite.h" namespace { using async::AwaitableType; constexpr uintptr_t kUart0BaseAddress = 0x40001000; XUartLite uart0_inst; XUartLite_Config uart0_config = { .DeviceId = 0, .RegBaseAddr = kUart0BaseAddress, .BaudRate = 115200, .UseParity = false, .DataBits = 8, }; constexpr size_t kUartTxBufferSize = 256; std::array tx_buffer = {}; RingBuffer tx_ring_buffer{.buffer = tx_buffer}; 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 UartWriteCrash(std::span data) { while (data.size() > 0) { while ((XUartLite_GetSR(uart0) & XUL_SR_TX_FIFO_EMPTY) == 0) { } auto* dat = reinterpret_cast(const_cast(data.data())); uint8_t sent = XUartLite_Send(uart0, dat, data.size()); data = data.subspan(sent); } while ((XUartLite_GetSR(uart0) & XUL_SR_TX_FIFO_EMPTY) == 0) { } } 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()); } } } #define GCC_HAS_BUG_101133 0 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101133 #if !GCC_HAS_BUG_101133 async::task<> UartWriteLoop(async::gimme>& data_gen) { 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 (!XUartLite_IsSending(uart0)) { tracing::trace(tracing::TraceEvent::kUartSend); XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), tx_ring_buffer.ContiguousAvailableData()); } } } } #endif // !GCC_HAS_BUG_101133 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); } } void UartWriteBlocking(std::span data) { if (__get_PRIMASK() != 0) { UartWriteCrash("\r\nUartWriteBlocking called with interupts disabled!\r\n"); __builtin_trap(); } while (!tx_ring_buffer.Store(data)) { } { InterruptLock lock; if (!XUartLite_IsSending(uart0)) { tracing::trace(tracing::TraceEvent::kUartSend); XUartLite_Send(uart0, tx_ring_buffer.RawReadPointer(), tx_ring_buffer.ContiguousAvailableData()); } } } async::task UartReadLoop() { uint8_t 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) { co_await async::await(AwaitableType::kUartRx); } co_yield c; } } 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()) { co_await async::await(AwaitableType::kUartRx); } co_return buff; } void HandleUartTxFromIsr(void*, unsigned int transmitted) { 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()); } async::resume(AwaitableType::kUartTx); } void HandleUartRxFromIsr(void*, unsigned int) { 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; }