All host tests currently pass Some async refactors may not work well on device, will try later
		
			
				
	
	
		
			176 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #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<std::byte, kUartTxBufferSize> 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<const std::byte> data) {
 | |
|     while (data.size() > 0) {
 | |
|         while ((XUartLite_GetSR(uart0) & XUL_SR_TX_FIFO_EMPTY) == 0) {
 | |
|         }
 | |
|         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_GetSR(uart0) & XUL_SR_TX_FIFO_EMPTY) == 0) {
 | |
|     }
 | |
| }
 | |
| 
 | |
| async::task<> UartWrite(std::span<const std::byte> 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<std::span<const std::byte>>& 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<std::byte> data) {
 | |
|     size_t bytes_received = 0;
 | |
|     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);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void UartWriteBlocking(std::span<const std::byte> 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<uint8_t> 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<buffer> UartRead(int size) {
 | |
|     auto buff = buffer::make(size);
 | |
|     auto* data = reinterpret_cast<uint8_t*>(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;
 | |
| }
 |