CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
platform-uart.hh
1// Copyright Microsoft and CHERIoT Contributors.
2// SPDX-License-Identifier: MIT
3
4#pragma once
5
6#include <platform/concepts/uart.hh>
7#include <stdint.h>
8
9/**
10 * Generic 16550A memory-mapped register layout.
11 *
12 * The registers are 8 bits wide, but typically the bus supports only 4-byte
13 * (or larger) transactions and so they are padded to a 32-bit word. The
14 * template parameter allows this to be controlled.
15 */
16template<typename RegisterType = uint32_t>
18{
19 static void no_custom_init() {}
20
21 public:
22 /**
23 * The interface to the read/write FIFOs for this UART.
24 *
25 * This is also the low byte of the divisor when the divisor latch (bit 7 of
26 * the `lineControl`) is set.
27 */
28 RegisterType data;
29 /**
30 * Interrupt-enabled control / status. Write 1 to enabled, 0 to disable, to
31 * each of the low four bits:
32 *
33 * 0: Data-receive interrupt
34 * 1: Transmit holding register empty interrupt
35 * 2: Receive line status interrupts
36 * 3: Modem status interrupts.
37 *
38 * When bit 7 of `lineControl` is set, this is instead the
39 * divisor-latch-high register and stores the high 8 bits of the divisor.
40 */
41 RegisterType intrEnable;
42 /**
43 * Interrupt identification and FIFO enable/disable.
44 *
45 * We only care about the low bit here, which enables the FIFO.
46 */
47 RegisterType intrIDandFifo;
48 /**
49 * Specifies properties of the line (stop bit, parity, and so on). The
50 * only bit that we use is bit 7, the divisor latch, which enables writing
51 * the speed.
52 */
53 RegisterType lineControl;
54 /**
55 * Modem control.
56 */
57 RegisterType modemControl;
58 /**
59 * The line status word. The bits that we care about are:
60 *
61 * 0: Receive ready
62 * 5: Transmit buffer empty
63 */
64 const RegisterType LineStatus;
65 /**
66 * Modem status.
67 */
68 const RegisterType ModemStatus;
69 /**
70 * Scratch register. Unused.
71 */
72 RegisterType scratch;
73
74 /**
75 * Returns true if the transmit buffer is empty.
76 */
77 __always_inline bool can_write() volatile
78 {
79#ifdef SIMULATION
80 // If we're in a simulator, we're running *much* slower than the thing
81 // handling the UART, so assume that you can always write to the UART.
82 return true;
83#else
84 return LineStatus & (1 << 5);
85#endif
86 }
87
88 /**
89 * Returns true if the receive buffer is not.
90 */
91 __always_inline bool can_read() volatile
92 {
93 return LineStatus & (1 << 0);
94 }
95
96 /**
97 * Read one byte, blocking until a byte is available.
98 */
99 uint8_t blocking_read() volatile
100 {
101 while (!can_read())
102 {
103 }
104 return data;
105 }
106
107 /**
108 * Write one byte, blocking until the byte is written.
109 */
110 void blocking_write(uint8_t byte) volatile
111 {
112 while (!can_write())
113 {
114 }
115 data = byte;
116 }
117
118 /**
119 * Initialise the UART.
120 */
121 template<typename T = decltype(no_custom_init)>
122 void init(int divisor = 1, T &&otherSetup = no_custom_init) volatile
123 {
124 // Disable interrupts
125 intrEnable = 0x00;
126 // Set the divisor latch (we're going to write the divisor) and set the
127 // character width to 8 bits, one stop bit, no parity.
128 lineControl = 0x83;
129 // Set the divisor
130 data = divisor & 0xff;
131 intrEnable = (divisor >> 8) & 0xff;
132 // Run any other setup that we were asked to do.
133 otherSetup();
134 // Clear the divisor latch
135 lineControl = 0x03;
136 // Enable the FIFO and reset
137 // Enabled bits:
138 // 0 - Enable FIFOs
139 // 1 - Clear receive FIFO
140 // 2 - Clear send FIFO
141 // 5 - 64-byte FIFO (if available)
142 intrIDandFifo = 0x1;
143 }
144};
145
146// A platform can provide a custom version of this.
147#ifndef CHERIOT_PLATFORM_CUSTOM_UART
148/// The default UART type.
149using Uart = Uart16550<uint32_t>;
150// Check that our UART matches the concept.
151static_assert(IsUart<Uart>);
152#endif
Generic 16550A memory-mapped register layout.
const uint32_t ModemStatus
uint8_t blocking_read() volatile
Read one byte, blocking until a byte is available.
bool can_write() volatile
Returns true if the transmit buffer is empty.
void init(int divisor=1, T &&otherSetup=no_custom_init) volatile
Initialise the UART.
void blocking_write(uint8_t byte) volatile
Write one byte, blocking until the byte is written.
const uint32_t LineStatus
bool can_read() volatile
Returns true if the receive buffer is not.
Concept for checking that a UART driver exposes the right interface.
Definition uart.hh:13