std1.0.0-beta-7 SafeC docs

Source hal/uart.h

View in the repository

// SafeC Standard Library — UART HAL
// Polling-based UART serial driver. Freestanding-safe.
//
// Uart<C> is generic over the UART block, a compile-time chip type:
//   UartGeneric    reference layout: DATA +0x00, STATUS +0x04 (bit 0 RX
//                  ready, bit 1 TX ready), BAUD +0x08 (written with 'baud')
//   Pl011          ARM PrimeCell PL011 (QEMU virt, Raspberry Pi, many SoCs)
//   Ns16550        16550-compatible, byte-wide registers at consecutive
//                  addresses (QEMU RISC-V virt, many SoCs)
//   Stm32F4Usart   STM32F1/F2/F4 USART (SR/DR/BRR/CR1)
//
//   struct Uart<Pl011> u = std::Uart::new(uart0, 115200u, 24000000u);
//   u.write_str("hello\n");
#pragma once

namespace std {

// What a UART block type provides; Uart<C> requires it.
trait UartChip {
    void          init(unsigned long base, unsigned int baud, unsigned int clock_hz) const;
    int           tx_ready(unsigned long base) const;
    int           rx_ready(unsigned long base) const;
    void          put(unsigned long base, unsigned char c) const;
    unsigned char get(unsigned long base) const;
}

// Each chip: init (set the baud rate from the input clock and enable TX/RX;
// clock_hz == 0 keeps whatever the boot firmware configured), the two
// ready flags, and one-byte data access. The driver does the waiting.
struct UartGeneric {
    int           unused_;
    void          init(unsigned long base, unsigned int baud, unsigned int clock_hz) const;
    int           tx_ready(unsigned long base) const;
    int           rx_ready(unsigned long base) const;
    void          put(unsigned long base, unsigned char c) const;
    unsigned char get(unsigned long base) const;
};
struct Pl011 {
    int           unused_;
    void          init(unsigned long base, unsigned int baud, unsigned int clock_hz) const;
    int           tx_ready(unsigned long base) const;
    int           rx_ready(unsigned long base) const;
    void          put(unsigned long base, unsigned char c) const;
    unsigned char get(unsigned long base) const;
};
struct Ns16550 {
    int           unused_;
    void          init(unsigned long base, unsigned int baud, unsigned int clock_hz) const;
    int           tx_ready(unsigned long base) const;
    int           rx_ready(unsigned long base) const;
    void          put(unsigned long base, unsigned char c) const;
    unsigned char get(unsigned long base) const;
};
struct Stm32F4Usart {
    int           unused_;
    void          init(unsigned long base, unsigned int baud, unsigned int clock_hz) const;
    int           tx_ready(unsigned long base) const;
    int           rx_ready(unsigned long base) const;
    void          put(unsigned long base, unsigned char c) const;
    unsigned char get(unsigned long base) const;
};

generic<C: UartChip>
struct Uart {
    &volatile unsigned int base; // MMIO base address
    unsigned int baud;  // configured baud rate
    C            chip;

    // Initialize the UART at `base`: 8N1 at `baud` from a `clock_hz` input clock
    // (0 = leave the rate the firmware set). The chip type comes from the target.
    static struct Uart<C> new(&volatile unsigned int base, unsigned int baud, unsigned int clock_hz);

    // Send a single byte (blocks until TX ready).
    void          write_byte(unsigned char c);
    // Read a single byte (blocks until RX available).
    unsigned char read_byte();
    // Send a null-terminated string.
    void          write_str(const &char s);
    // Check if a byte is available to read. Returns 1 if ready.
    int           rx_ready() const;
    // Check if transmitter is ready. Returns 1 if ready.
    int           tx_ready() const;
    unsigned long addr_() const;
};

generic<C: UartChip>
struct Uart<C> Uart::new(&volatile unsigned int base, unsigned int baud, unsigned int clock_hz) {
    struct Uart<C> u;
    u.base = base;
    u.baud = baud;
    u.chip.init(u.addr_(), baud, clock_hz);
    return u;
}

generic<C: UartChip>
unsigned long Uart::addr_() const { unsafe { return (unsigned long)self.base; } }
generic<C: UartChip>
int Uart::tx_ready() const { return self.chip.tx_ready(self.addr_()); }
generic<C: UartChip>
int Uart::rx_ready() const { return self.chip.rx_ready(self.addr_()); }
generic<C: UartChip>
void Uart::write_byte(unsigned char c) {
    while (self.tx_ready() == 0) { }
    self.chip.put(self.addr_(), c);
}
generic<C: UartChip>
unsigned char Uart::read_byte() {
    while (self.rx_ready() == 0) { }
    return self.chip.get(self.addr_());
}
generic<C: UartChip>
void Uart::write_str(const &char s) {
    unsigned long i = 0UL;
    unsafe {
        while (s[i] != (char)0) { self.write_byte((unsigned char)s[i]); i = i + 1UL; }
    }
}

} // namespace std