| #pragma once |
| #pragma push_macro("CHERIOT_PLATFORM_CUSTOM_UART") |
| #define CHERIOT_PLATFORM_CUSTOM_UART |
| #include_next <platform-uart.hh> |
| #pragma pop_macro("CHERIOT_PLATFORM_CUSTOM_UART") |
| |
| /** |
| * The Synopsis extended 16550 UART has two additional registers that are not |
| * part of a standard 16550, one of which allows for fractional multipliers. |
| * |
| * This is always instantiated with 32-bit device registers. The default baud |
| * rate is set as a template parameter and can be overridden in `init`. |
| */ |
| template<unsigned DefaultBaudRate = 115'200> |
| struct SynopsisExtended16550 : public Uart16550<uint32_t> |
| { |
| /** |
| * Loopback register. Unused. |
| */ |
| uint32_t loopback; |
| |
| /** |
| * Divisor latch fraction. When the divisor latch is set (for writing the |
| * divisor), this is used for the fraction. RS-232 requires a 3% tolerance |
| * for the baud rate and an integer divisor over the clock frequency of the |
| * host may not get within this range. This register is used to provide a |
| * 4-bit correction that allows baud rates to stay within the required |
| * range. |
| */ |
| uint32_t divisorLatchFraction; |
| |
| /** |
| * The divisor is calculated from 16 times the baud rate. The baud rate is |
| * in the range of KHz to hundreds of KHz, whereas the clock frequency is |
| * typically tens to hundreds of MHz. The divisor must fit in a 16-bit |
| * integer (written across two 8-bit device registers) and so is scaled to |
| * ensure that it fits (by a power of two, which makes it trivial to |
| * reverse the multiplication in the hardware). |
| */ |
| static constexpr uint32_t BaudScaleFactorShift = 4; |
| |
| /** |
| * The value in `divisorLatchFraction` is truncated to this many bits. |
| */ |
| static constexpr uint32_t BaudFractionBits = 4; |
| |
| /** |
| * Initialise the UART. |
| */ |
| void init(unsigned baudRate = DefaultBaudRate, |
| unsigned timerFrequency = CPU_TIMER_HZ) volatile |
| { |
| // The multiplier that we will scale the baud rate by. |
| constexpr uint32_t Multiplier = (1 << BaudScaleFactorShift); |
| // The scaled baud rate is used in all of the remaining calculations. |
| // See `BaudScaleFactorShift` for an explanation. |
| uint32_t scaledBaudRate = baudRate * Multiplier; |
| // The divisor to be programmed into the device. |
| uint32_t divisor = timerFrequency / scaledBaudRate; |
| // The remainder of the divisor. |
| uint32_t remainder = timerFrequency % scaledBaudRate; |
| // The remainder is scaled relative to the baud rate to give a |
| // correction factor. |
| remainder = (remainder << (BaudScaleFactorShift + 1)) / scaledBaudRate; |
| // The remainder is rounded up to give the value that can be programmed |
| // into the device register. |
| remainder = (remainder >> 1) + (remainder & 0x1); |
| // Set up the base UART |
| Uart16550<uint32_t>::init(divisor, |
| [&]() { divisorLatchFraction = remainder; }); |
| // Write a character with the low bit set. Any value is fine here, but |
| // if we use 0x7 (ASCII bell character) then we should get a beep if it |
| // goes through to the receiver and so can tell whether this worked. |
| blocking_write(0x7); |
| } |
| }; |
| |
| #ifndef CHERIOT_PLATFORM_CUSTOM_UART |
| using Uart = SynopsisExtended16550<>; |
| #endif |