CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
platform-uart.hh
1#pragma once
2#pragma push_macro("CHERIOT_PLATFORM_CUSTOM_UART")
3#define CHERIOT_PLATFORM_CUSTOM_UART
4#include_next <platform-uart.hh>
5#pragma pop_macro("CHERIOT_PLATFORM_CUSTOM_UART")
6
7/**
8 * The Synopsis extended 16550 UART has two additional registers that are not
9 * part of a standard 16550, one of which allows for fractional multipliers.
10 *
11 * This is always instantiated with 32-bit device registers. The default baud
12 * rate is set as a template parameter and can be overridden in `init`.
13 */
14template<unsigned DefaultBaudRate = 115'200>
15struct SynopsisExtended16550 : public Uart16550<uint32_t>
16{
17 /**
18 * Loopback register. Unused.
19 */
20 uint32_t loopback;
21
22 /**
23 * Divisor latch fraction. When the divisor latch is set (for writing the
24 * divisor), this is used for the fraction. RS-232 requires a 3% tolerance
25 * for the baud rate and an integer divisor over the clock frequency of the
26 * host may not get within this range. This register is used to provide a
27 * 4-bit correction that allows baud rates to stay within the required
28 * range.
29 */
31
32 /**
33 * The divisor is calculated from 16 times the baud rate. The baud rate is
34 * in the range of KHz to hundreds of KHz, whereas the clock frequency is
35 * typically tens to hundreds of MHz. The divisor must fit in a 16-bit
36 * integer (written across two 8-bit device registers) and so is scaled to
37 * ensure that it fits (by a power of two, which makes it trivial to
38 * reverse the multiplication in the hardware).
39 */
40 static constexpr uint32_t BaudScaleFactorShift = 4;
41
42 /**
43 * The value in `divisorLatchFraction` is truncated to this many bits.
44 */
45 static constexpr uint32_t BaudFractionBits = 4;
46
47 /**
48 * Initialise the UART.
49 */
50 void init(unsigned baudRate = DefaultBaudRate,
51 unsigned timerFrequency = CPU_TIMER_HZ) volatile
52 {
53 // The multiplier that we will scale the baud rate by.
54 constexpr uint32_t Multiplier = (1 << BaudScaleFactorShift);
55 // The scaled baud rate is used in all of the remaining calculations.
56 // See `BaudScaleFactorShift` for an explanation.
57 uint32_t scaledBaudRate = baudRate * Multiplier;
58 // The divisor to be programmed into the device.
59 uint32_t divisor = timerFrequency / scaledBaudRate;
60 // The remainder of the divisor.
61 uint32_t remainder = timerFrequency % scaledBaudRate;
62 // The remainder is scaled relative to the baud rate to give a
63 // correction factor.
64 remainder = (remainder << (BaudScaleFactorShift + 1)) / scaledBaudRate;
65 // The remainder is rounded up to give the value that can be programmed
66 // into the device register.
67 remainder = (remainder >> 1) + (remainder & 0x1);
68 // Set up the base UART
70 [&]() { divisorLatchFraction = remainder; });
71 // Write a character with the low bit set. Any value is fine here, but
72 // if we use 0x7 (ASCII bell character) then we should get a beep if it
73 // goes through to the receiver and so can tell whether this worked.
74 blocking_write(0x7);
75 }
76};
77
78#ifndef CHERIOT_PLATFORM_CUSTOM_UART
79using Uart = SynopsisExtended16550<>;
80#endif
Generic 16550A memory-mapped register layout.
void init(int divisor=1, T &&otherSetup=no_custom_init) volatile
Initialise the UART.
void blocking_write(uint8_t byte) volatile
The Synopsis extended 16550 UART has two additional registers that are not part of a standard 16550,...
static constexpr uint32_t BaudFractionBits
The value in divisorLatchFraction is truncated to this many bits.
uint32_t loopback
Loopback register.
uint32_t divisorLatchFraction
Divisor latch fraction.
void init(unsigned baudRate=DefaultBaudRate, unsigned timerFrequency=CPU_TIMER_HZ) volatile
Initialise the UART.
static constexpr uint32_t BaudScaleFactorShift
The divisor is calculated from 16 times the baud rate.