avr-oxide 0.4.0

An extremely simple Rusty operating system for AVR microcontrollers
/* serial.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! Generic AVR serial driver (USARTs, SPI, TWI, etc.) support

// Imports ===================================================================

// Declarations ==============================================================

use core::any::Any;
use ufmt::derive::uDebug;
use avr_oxide::concurrency::Isolated;
use oxide_macros::Persist;
use avr_oxide::hal::generic::callback::IsrCallback;

/**
 * Identifies a particular source of serial port events, i.e. a particular
 * hardware USART, at runtime.
 */
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum SerialPortIdentity {
  /// Universial Synchronous/Asynchronous Receiver/Transmitter 0
  Usart0,
  /// Universial Synchronous/Asynchronous Receiver/Transmitter 1
  Usart1,
  /// Universial Synchronous/Asynchronous Receiver/Transmitter 2
  Usart2,
  /// Universial Synchronous/Asynchronous Receiver/Transmitter 3
  Usart3
}

/**
 * Possible baud rates we can configure a serial port clock to use.
 */
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum BaudRate {
  /// 300 baud
  Baud300,
  /// 600 baud
  Baud600,
  /// 1200 baud
  Baud1200,
  /// 2400 baud
  Baud2400,
  /// 4800 baud
  Baud4800,
  /// 9600 baud
  Baud9600,
  /// 14400 baud
  Baud14400,
  /// 19200 baud
  Baud19200,
  /// 28800 baud
  Baud28800,
  /// 38400 baud
  Baud38400,
  /// 57600 baud
  Baud57600,
  // 76800 baud.  Generating this baud rate with reasonable accuracy depends
  // on a CPU clockrate of 16MHz or above.
  Baud76800,
  // 117200 baud.  Generating this baud rate with reasonable accuracy
  // depends on a CPU clockrate of 16MHz or above.
  Baud115200,
  // Not going higher than this because the chances of generating accurate
  // higher rates from the peripheral clock on our controllers at their typical
  // clockrates is basically zero.

  /// Automatic baud rate detection.  See the AVR manual, but don't expect
  /// me to test whether or not this actually works ;-)
  Auto
}
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum SynchronousMode {
  /// Master mode - I will generate the clock for everyone
  Master(BaudRate),
  /// Slave, I will - use the clock generated by the master
  Slave
}

/**
 * The number of data bits in each character frame
 */
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum DataBits {
  /// 5 bit data frame
  Bits5,
  /// 6 bit data frame
  Bits6,
  /// 7 bit data frame
  Bits7,
  /// 8 bit data frame
  Bits8,
  /// 9 bit data frame, little-endian (low byte sent first)
  Bits9LE,
  /// 9 bit data frame, big-endian (high byte sent first)
  Bits9HE
}

/**
 * Parity for each transmitted character
 */
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum Parity {
  None,
  Even,
  Odd
}

/**
 * The number of stop bits (wait states effectively) to transmit after each
 * character frame
 */
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum StopBits {
  Bits1,
  Bits2
}

/**
 * The serial communications mode for a serial port peripheral.
 */
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum SerialPortMode {
  /// Asynchronous communication
  Asynch(BaudRate,DataBits,Parity,StopBits),
  /// Synchronous communication
  Synch(SynchronousMode)
}

/**
 * Serial communication errors
 */
#[derive(Clone,Copy,PartialEq,Eq,uDebug,Persist)]
pub enum SerialError {
  /// The receive or transmit buffer overflowed
  BufferOverflow,

  /// We received something other than a stop bit when we were expecting one
  FrameError,

  /// The received data was framed correctly, but the calculated parity is
  /// wrong.
  ParityError,

  /// In automatic baud-rate detection, we cannot determine an accurate rate
  AutoBaudDetectFail,

  /// A Break was detected
  Break
}

/**
 * Response from the read handler callback.  A read handler can either return
 * a byte to be added to the input buffer, or it can return a Discard response
 * that will cause this byte to be discarded.
 */
#[derive(Copy,Clone,Debug)]
pub enum ReadHandlerResult<T> {
  /// Add the given byte to the serial receive buffer
  Buffer(T),
  /// Discard the byte
  Discard
}

/**
 * Callback called when a byte has been read
 */
pub type SerialReadEventHandlerFunction = fn(Isolated, SerialPortIdentity, u8, Option<*const dyn Any>) ->ReadHandlerResult<u8>;

pub type SerialReadEventCallback = IsrCallback<SerialReadEventHandlerFunction,ReadHandlerResult<u8>>;

/**
 * Callback called when there is a serial error
 */
pub type SerialErrorEventHandlerFunction = fn(Isolated, SerialPortIdentity, SerialError, Option<*const dyn Any>) ->();

pub type SerialErrorEventCallback = IsrCallback<SerialErrorEventHandlerFunction,()>;

/**
 * Callback called when a byte has been written
 */
pub type SerialWriteEventHandlerFunction = fn(Isolated, SerialPortIdentity, Option<*const dyn Any>) ->();

pub type SerialWriteEventCallback = IsrCallback<SerialWriteEventHandlerFunction,()>;

pub trait SerialRxTx {
  /// Configure the serial port with the given mode
  fn set_mode(&mut self, mode: SerialPortMode);

  /// Enable or disable the receiver & transmitter
  fn set_enable_rxtx(&mut self, enable: bool);

  /// Set the callback handler for write notifications
  fn set_write_complete_callback(&self, handler: SerialWriteEventCallback);

  /// Set the callback handler for error
  fn set_error_callback(&self, handler: SerialErrorEventCallback);

  /// Set the callback handler for read notifications
  fn set_read_callback(&self, handler: SerialReadEventCallback);

  // Write a byte to the serial port.  May block if the transmit buffer is full.
  fn write_u8(&mut self, byte: u8);

  // Flush the transmit buffer (i.e. block until it is empty)
  fn flush(&mut self);

  // Read a byte from the serial port, blocking until a byte is available
  // if necessary.
  fn read_u8(&mut self) -> u8;

  /**
   * Read a byte from the serial port.  If no byte is available, return None
   * instead.
   */
  fn try_read_u8(&mut self) -> Option<u8>;
}

/**
 * Transmission in a no-interrupt context, i.e. write bytes even though
 * everything else is broken.  Used in internal contexts like the panic
 * handler.
 */
pub(crate) trait SerialNoInterruptTx {

  /**
   * Return true iff we can (or at least, should) write to the serial device.
   * A response of true means that the blocking_write methods *should* not
   * block forever (but no guarantee).
   */
  fn can_write(&self) -> bool;

  /**
   * Write a single byte synchronously, waiting until the hardware tells us it
   * is completed.
   */
  fn blocking_write_u8(&mut self, byte: u8);

  /**
   * Write a slice to the serial port.
   */
  fn blocking_write_slice(&mut self, bytes: &[u8]){
    for byte in bytes {
      self.blocking_write_u8(*byte);
    }
  }
}

// Code ======================================================================
#[allow(dead_code)]
impl BaudRate {
  /**
   * Convert a baud rate into a number of bits-per-second
   */
  pub fn to_bps(&self) -> u32 {
    match self {
      BaudRate::Baud300 => 300,
      BaudRate::Baud600 => 600,
      BaudRate::Baud1200 => 1200,
      BaudRate::Baud2400 => 2400,
      BaudRate::Baud4800 => 4800,
      BaudRate::Baud9600 => 9600,
      BaudRate::Baud14400 => 14400,
      BaudRate::Baud19200 => 19200,
      BaudRate::Baud28800 => 28800,
      BaudRate::Baud38400 => 38400,
      BaudRate::Baud57600 => 57600,
      BaudRate::Baud76800 => 76800,
      BaudRate::Baud115200 => 115200,
      BaudRate::Auto => 1
    }
  }
}

#[cfg(target_arch="avr")]
pub mod base {
  /**
   * Implementation of USART for the device in megaAVR 0-Series devices like
   * the ATmega4809
   */
  pub mod zeroseries {
    #![allow(dead_code)]
    use core::any::Any;
    use core::cell::Cell;
    use core::arch::asm;
    use avr_oxide::sleepctrl;
    use avr_oxide::hal::generic::serial::{SerialPortMode, SerialPortIdentity, SynchronousMode, BaudRate, Parity, StopBits, DataBits, SerialRxTx, ReadHandlerResult, SerialNoInterruptTx, SerialReadEventCallback, SerialWriteEventCallback, SerialErrorEventCallback};
    use avr_oxide::deviceconsts::clock::{MASTER_CLOCK_HZ, MASTER_CLOCK_PRESCALER};
    use core::marker::PhantomData;
    use avr_oxide::concurrency::Isolated;
    use avr_oxide::hal::generic::cpu::private::PermitStandbyToken;
    use avr_oxide::hal::generic::cpu::SleepControl;
    use avr_oxide::hal::generic::MuxControl;
    use avr_oxide::private::ringq::RingQ;
    use avr_oxide::util::datatypes::{BitFieldAccess, BitIndex, Volatile, VolatileBitField};
    use avr_oxide::OxideResult::{Ok,Err};

    /**
     * The AVR USART control register block in 0-series devices like 4809
     */
    #[repr(C)]
    pub struct Atmel0SeriesUsartControl {
      pub(crate) rxdatal: Volatile<u8>,
      pub(crate) rxdatah: Volatile<u8>,
      pub(crate) txdatal: Volatile<u8>,
      pub(crate) txdatah: Volatile<u8>,
      pub(crate) status: VolatileBitField,
      pub(crate) ctrla: VolatileBitField,
      pub(crate) ctrlb: VolatileBitField,
      pub(crate) ctrlc: Volatile<u8>,
      pub(crate) baud: Volatile<u16>,
      pub(crate) ctrld: Volatile<u8>,
      pub(crate) dbgctrl: Volatile<u8>,
      pub(crate) evctrl: Volatile<u8>,
      pub(crate) txplctrl: Volatile<u8>,
      pub(crate) rxplctrl: Volatile<u8>
    }

    pub(crate) const CTRLA_DREIE: BitIndex = BitIndex::bit_c(5);

    pub(crate) const CTRLB_RXEN : BitIndex = BitIndex::bit_c(7);
    pub(crate) const CTRLB_TXEN : BitIndex = BitIndex::bit_c(6);
    pub(crate) const CTRLB_SFDEN : BitIndex = BitIndex::bit_c(4);
    pub(crate) const CTRLB_ODME : BitIndex = BitIndex::bit_c(3);
    pub(crate) const CTRLB_MPCM : BitIndex = BitIndex::bit_c(0);

    pub(crate) const STATUS_RXCIF : BitIndex = BitIndex::bit_c(7);
    pub(crate) const STATUS_TXCIF : BitIndex = BitIndex::bit_c(6);
    pub(crate) const STATUS_DREIF : BitIndex = BitIndex::bit_c(5);
    pub(crate) const STATUS_RXSIF : BitIndex = BitIndex::bit_c(4);
    pub(crate) const STATUS_ISFIF : BitIndex = BitIndex::bit_c(3);
    pub(crate) const STATUS_BDF   : BitIndex = BitIndex::bit_c(1);
    pub(crate) const STATUS_WFB   : BitIndex = BitIndex::bit_c(0);


    pub struct AtmelUsart<M,const BUF_SIZE: usize>
    where
      M: MuxControl
    {
      pub(crate) control: &'static mut Atmel0SeriesUsartControl,
      pub(crate) read_handler: Cell<SerialReadEventCallback>,
      pub(crate) write_complete_handler: Cell<SerialWriteEventCallback>,
      pub(crate) error_handler: Cell<SerialErrorEventCallback>,
      pub(crate) phantom: PhantomData<M>,

      pub(crate) rx_buf: RingQ<u8,BUF_SIZE>,
      pub(crate) tx_buf: RingQ<u8,BUF_SIZE>,

      pub(crate) sleep_inhibitor: Option<PermitStandbyToken>
    }

    /**
     * A no-op function that is the default handler for the serial
     * read interrupt callbacks.
     */
    #[inline(never)]
    pub(crate) fn read_nop_handler(_isotoken: Isolated, _src: SerialPortIdentity, byte: u8, _udata: Option<*const dyn Any>) -> ReadHandlerResult<u8> {
      ReadHandlerResult::Buffer(byte)
    }

    impl<M,const BUF_SIZE:usize> AtmelUsart<M,BUF_SIZE>
    where
      M: MuxControl
    {
      pub fn mux(&mut self, mux: M) -> &mut Self {
        mux.route();

        self
      }

      /**
       * Enable or disable the Data Register Empty interrupt.  This will
       * send interrupts continuously as long as there is no data to send,
       * so we need to turn if off when we have nothing left and turn it on
       * where there might be more.  try_send_u8_from_buffer more or less
       * handles this for us.
       */
      pub(crate) fn dre_int_enable(&mut self, isotoken: Isolated, enabled: bool) {
        match enabled {
          true  => {
            self.control.ctrla.set_isolated(isotoken, CTRLA_DREIE);
          },
          false => {
            self.control.ctrla.clr_isolated(isotoken, CTRLA_DREIE);
          }
        };
      }

      /**
       * Try to send a byte from our buffer.  It will only do so if our USART's
       * transmit buffer is free, and also only if we have something to send :).
       *
       * Returns false if there are no more bytes to send, true otherwise.
       */
      pub(crate) fn isr_try_send_u8_from_buffer(&mut self, isotoken: Isolated) -> bool {
        if self.control.status.is_set(STATUS_DREIF) {
          let byte = self.tx_buf.consume(isotoken);

          match byte {
            Some(byte) => {
              self.control.txdatah.write(0x00);
              self.control.txdatal.write(byte);
              true
            },
            None => {
              // Nothing to send, so disable the DRE interrupt

              false
            }
          }
        } else {
          true
        }
      }

      /**
       * Called by the ISR when transmission is complete.  At this point
       * if we have no more bytes to send, we can re-enable sleep mode.
       */
      pub(crate) unsafe fn isr_transmission_complete(&mut self, _isotoken: Isolated) {
        self.control.status.exc_set(STATUS_TXCIF);
      }

      /**
       * To to receive a byte into our buffer.  Returns true if we succeeded,
       * false if there was a buffer overflow.
       */
      pub(crate) unsafe fn isr_try_receive_u8_to_buffer(&mut self, isotoken: Isolated, byte: u8) -> bool {
        self.rx_buf.append(isotoken, byte).is_ok()
      }

      fn mode(&mut self, mode: SerialPortMode) -> &mut Self {
        unsafe {
          // The datasheet says all USART configuration should be done with
          // global interrupts disabled, so...
          avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
            self.control.ctrla.clr_all();

            // Disable received/transmitter (and any other ctrlb flags)
            self.control.ctrlb.clr_all();


            // Wait for any pending writes to finish
            while self.control.status.is_clr(STATUS_DREIF) {
              asm!("nop");
            }

            // Clear the read buffer
            while self.control.status.is_set(STATUS_RXCIF) {
              let _discard = self.control.rxdatah.read();
              let _discard = self.control.rxdatal.read();
            }

            // OK, now we are clean and ready to configure

            // Set the baud rate register
            self.control.baud.write(mode.avr_0series_baud_register_value());

            // Set the data comms mode
            self.control.ctrlc.write(mode.avr_0series_ctrlc());

            // Set (harcode) auto-baud window size to the most permissive
            self.control.ctrld.write(0b11000000);

            // Enable receive & transmit interrupts.
            //
            // Data Register Empty is disabled, we'll enable it when we actually
            // send something.
            self.control.ctrla.write_byte(0b11000000);
          });
        }
        self
      }

      /**
       * Enable (or disable) receiver/transmitter.  Call this after you have set
       * up the mode, but also after you have configured the receive/transmit
       * pins to input/output as required.
       */
      fn enable_rxtx(&mut self, enable: bool) -> &mut Self {
        avr_oxide::concurrency::interrupt::isolated(|isotoken|{
          match enable {
            true  => {
              if self.sleep_inhibitor.is_none() {
                self.sleep_inhibitor = Some(sleepctrl!(isotoken).inhibit_standby());
              }
              self.control.ctrlb.set_isolated(isotoken, CTRLB_RXEN);
              self.control.ctrlb.set_isolated(isotoken, CTRLB_TXEN);
            },
            false => {
              match self.sleep_inhibitor.take() {
                None => {
                  // Do nothing, standby wasn't inhibited by us
                },
                Some(token) => {
                  sleepctrl!(isotoken).permit_standby(token);
                }
              }
              self.control.ctrlb.clr_isolated(isotoken, CTRLB_RXEN);
              self.control.ctrlb.clr_isolated(isotoken, CTRLB_TXEN);

              // The transmitter is not actually disabled until transmit is
              // completed
              while self.control.status.is_clr(STATUS_DREIF) {
                unsafe {
                  asm!("nop");
                }
              }
            }
          }
        });
        self
      }
    }

    impl SerialPortMode {
      /**
       * Return the value to write to the USART's `baud` register to configure
       * this speed.
       */
      pub fn avr_0series_baud_register_value(&self) -> u16 {
        // Some would have us do these calcs using floating point arithmetic.
        // We do not for three reasons -
        // 1. It involves linking in a raft of floating point library code,
        // 2. Floating point maths is irretrievably broken with LLVM-avr at
        //    the moment.
        // 3. It's entirely unnecessary


        let integer_clk_per = MASTER_CLOCK_HZ / MASTER_CLOCK_PRESCALER as u32;

        match self {
          SerialPortMode::Asynch(baud, _, _, _) => {
            let integer_baud_clock = (integer_clk_per * 64u32) / (16u32 * baud.to_bps() as u32)+1;
            integer_baud_clock as u16
          },
          SerialPortMode::Synch(mode) => {
              match mode {
              SynchronousMode::Master(baud) => {
                let integer_baud_clock = (integer_clk_per / (2u32 * baud.to_bps() as u32)) + 1;

                (integer_baud_clock as u16) << 6
              }
              SynchronousMode::Slave => {
                0
              }
            }
          }
        }
      }

      /**
       * Return the value to write to the `rxmode` bits of the USART's `ctrlb`
       * register to configure this mode.
       */
      pub fn avr_0series_ctrlb_rxmode(&self) -> u8 {
        match self {
          SerialPortMode::Asynch(baud, _, _, _) => {
            match baud {
              BaudRate::Auto => 0x02 << 1,
              _ => 0x00
            }
          },
          SerialPortMode::Synch(_) => 0x00
        }
      }

      /**
       * Return the value to write the `ctrlc` register of the USART to
       * configure this mode
       */
      #[allow(clippy::unusual_byte_groupings)]
      pub fn avr_0series_ctrlc(&self) -> u8 {
        match self {
          SerialPortMode::Asynch(_baud, dbits, parity, sbits) => {
            let mut ctrlc: u8 = 0b00_000000; // Asynchronous USART mode
            ctrlc |= match parity {
              Parity::None => 0b00_00_0000,
              Parity::Even => 0b00_10_0000,
              Parity::Odd =>  0b00_11_0000
            };
            ctrlc |= match sbits {
              StopBits::Bits1 => 0b0000_0_000,
              StopBits::Bits2 => 0b0000_1_000
            };
            ctrlc |= match dbits {
              DataBits::Bits5 => 0x00,
              DataBits::Bits6 => 0x01,
              DataBits::Bits7 => 0x02,
              DataBits::Bits8 => 0x03,
              DataBits::Bits9LE => 0x06,
              DataBits::Bits9HE => 0x07
            };
            ctrlc
          },
          SerialPortMode::Synch(_) => {
            let ctrlc: u8 = 0b01_000000; // Synchronous USART mode
            ctrlc
          }
        }
      }
    }
    impl<M,const BUF_SIZE:usize> SerialRxTx for AtmelUsart<M,BUF_SIZE>
    where
      M: MuxControl
    {
      fn set_mode(&mut self, mode: SerialPortMode) {
        self.mode(mode);
      }

      /**
       * Enable (or disable) receiver/transmitter.  Call this after you have set
       * up the mode, but also after you have configured the receive/transmit
       * pins to input/output as required.
       */
      fn set_enable_rxtx(&mut self, enable: bool) {
        self.enable_rxtx(enable);
      }

      fn set_write_complete_callback(&self, callback: SerialWriteEventCallback) {
        avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
          self.write_complete_handler.replace(callback);
        });
      }

      fn set_read_callback(&self, callback: SerialReadEventCallback) {
        avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
          self.read_handler.replace(callback);
        });
      }

      fn set_error_callback(&self, callback: SerialErrorEventCallback) {
        avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
          self.error_handler.replace(callback);
        });
      }


      /**
       * Attempt to write a byte.  May block if the tx buffer is full.
       */
      fn write_u8(&mut self, byte: u8) {
        match avr_oxide::concurrency::interrupt::isolated(|isotoken|{
          if self.sleep_inhibitor.is_none() {
            self.sleep_inhibitor = Some(sleepctrl!(isotoken).inhibit_standby());
          }
          self.tx_buf.append(isotoken, byte)
        }) {
          Ok(()) => {
            // Byte added; nothing more for us to do!
          }
          Err(_e) => {
            // Hmm, byte couldn't be added.  Maybe our buffer is full - let's
            // try flushing and then block until we can add it.
            self.flush();
            self.tx_buf.append_blocking(byte);
          }
        }
      }

      /**
       * Flush the buffer (that is, start the interrupt handler which will
       * empty the buffer.)
       */
      fn flush(&mut self) {
        avr_oxide::concurrency::interrupt::isolated(|isotoken|{
          // Enable the Data Register Empty interrupt, which will consume
          // any bytes in the buffer.
          self.dre_int_enable(isotoken, true);
        })
      }

      /**
       * Read a byte from the input buffer, blocking until a byte is
       * available if the buffer is empty.
       */
      fn read_u8(&mut self) -> u8 {
        self.rx_buf.consume_blocking()
      }

      /**
       * Try to read a byte from the input buffer, returns Some(byte) if
       * there is a byte available, or None if not.
       */
      fn try_read_u8(&mut self) -> Option<u8> {
        avr_oxide::concurrency::interrupt::isolated(|isotoken|{self.rx_buf.consume(isotoken)})
      }
    }

    impl<M,const BUF_SIZE:usize> SerialNoInterruptTx for AtmelUsart<M,BUF_SIZE>
    where
      M: MuxControl
    {
      fn can_write(&self) -> bool {
        self.control.ctrlb.is_set(CTRLB_TXEN)
      }

      fn blocking_write_u8(&mut self, byte: u8) {
        unsafe {
          avr_oxide::concurrency::interrupt::isolated(|isotoken|{
            // Disable Standby mode for as long as we are transmitting
            let stdbytoken = sleepctrl!(isotoken).inhibit_standby();

            // Wait until the data register is ready to accept a byte
            while self.control.status.is_clr(STATUS_DREIF) {
              asm!("nop")
            }
            // Now write the byte
            self.control.txdatah.write(0x00);
            self.control.txdatal.write(byte);

            // Wait for transmission complete flag
            while self.control.status.is_clr(STATUS_TXCIF) {
              asm!("nop")
            }
            // Now clear the transmission complete flag, and re-enable
            // sleep mode.
            self.control.status.exc_set(STATUS_TXCIF);
            sleepctrl!(isotoken).permit_standby(stdbytoken);
          })
        }
      }
    }

    #[doc(hidden)]
    #[macro_export]
    macro_rules! atmel_0series_usart_tpl {
      ($usartref:expr, $srcref:expr, $implementation:ty, $isr_rx:expr, $isr_tx:expr, $isr_dre:expr) => {
        use avr_oxide::hal::generic::serial::{SerialError, ReadHandlerResult,SerialReadEventCallback,SerialWriteEventCallback,SerialErrorEventCallback};
        use avr_oxide::hal::generic::serial::base::zeroseries::{ AtmelUsart, read_nop_handler };
        use avr_oxide::{mut_singleton_explicit_init,isr_cb_invoke};
        use avr_oxide::private::ringq::RingQ;
        use avr_oxide::hal::generic::serial::base::zeroseries::{STATUS_RXCIF,STATUS_ISFIF,STATUS_BDF};
        use avr_oxide::util::datatypes::BitFieldAccess;
        use core::marker::PhantomData;
        use core::cell::Cell;

        pub type SerialImpl = $implementation;

        mut_singleton_explicit_init!(
          $implementation,
          __INSTANCE,
          initialise, instance, instance_isolated,
          AtmelUsart {
            control: core::mem::transmute($usartref),
            read_handler: Cell::new(SerialReadEventCallback::Function(read_nop_handler)),
            write_complete_handler: Cell::new(SerialWriteEventCallback::Nop(())),
            error_handler: Cell::new(SerialErrorEventCallback::Nop(())),
            phantom: PhantomData::default(),
            rx_buf: RingQ::new(),
            tx_buf: RingQ::new(),
            sleep_inhibitor: None
          });

        /**
         * Receive interrupt handler. Checks the type of interrupt, if it's
         * an error type we'll call the read handler with a suitable Err
         * instance.  If it's receive complete, we will read the byte and
         * send that to the callback.  In all cases, you need to have
         * set a `set_read_callback` of course.
         */
        #[oxide_macros::interrupt(isr=$isr_rx)]
        fn usart_rx_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
          let atmelusart = instance_isolated(isotoken);

          let trigger = atmelusart.control.status.snapshot();

          if trigger.is_set(STATUS_RXCIF) { // Receive complete
            // We MUST read the byte to clear the interrupt
            let byte = atmelusart.control.rxdatal.read();

            // Pass it to the user handler, and then buffer it if needed
            match isr_cb_invoke!(isotoken, atmelusart.read_handler.get(), $srcref, byte) {
              ReadHandlerResult::Buffer(byte) => {
                if !atmelusart.isr_try_receive_u8_to_buffer(isotoken, byte) {
                  isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::BufferOverflow);
                }
              },
              ReadHandlerResult::Discard => {}
            }
          }
          if trigger.is_set(STATUS_ISFIF) { // Baud sync fail
            isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::AutoBaudDetectFail);
          }
          if trigger.is_set(STATUS_BDF) { // Break detected
            isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::Break);
          }

          // Clear all the other flags we don't care about
          //
          // @todo Note, in future we should care about them ;).
          atmelusart.control.status.write_byte(0b00011010);
        }
        /**
         * Transmit interrupt handler.  Our only job here is to check if
         * there is nothing left in the transmit buffer - if so, we can
         * permit the processor to enter Standby mode again.
         */
        #[oxide_macros::interrupt(isr=$isr_tx)]
        fn usart_tx_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
          let atmelusart = instance_isolated(isotoken);

          atmelusart.isr_transmission_complete(isotoken);
        }

        /**
         * Data Register Empty interrupt handler.  We will try to send a
         * byte from our transmit buffer, if that fails we will turn off
         * the interrupt.
         */
        #[oxide_macros::interrupt(isr=$isr_dre)]
        fn usart_dre_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
          let atmelusart = instance_isolated(isotoken);

          match atmelusart.isr_try_send_u8_from_buffer(isotoken) {
            true => {
              // Success, leave the interrupt enabled
            },
            false => {
              // Nothing to send, so let's disable the interrupt
              // and also send a notification
              atmelusart.dre_int_enable(isotoken, false);

              isr_cb_invoke!(isotoken, atmelusart.write_complete_handler.get(), $srcref);
            }
          }
        }
      }
    }
  }

  /**
   * Implementation of USART for the device in simple AVR devices like the
   * ATmega328P
   */
  pub mod simple {
    #![allow(dead_code)]
    use core::any::Any;
    use core::cell::Cell;
    use core::arch::asm;
    use avr_oxide::hal::generic::serial::{SerialPortMode, SerialPortIdentity, SynchronousMode, Parity, StopBits, DataBits, SerialRxTx, ReadHandlerResult, SerialNoInterruptTx, SerialReadEventCallback, SerialWriteEventCallback, SerialErrorEventCallback};
    use avr_oxide::deviceconsts::clock::{MASTER_CLOCK_HZ, MASTER_CLOCK_PRESCALER};
    use avr_oxide::concurrency::Isolated;
    use avr_oxide::private::ringq::RingQ;
    use avr_oxide::util::datatypes::Volatile;
    use avr_oxide::OxideResult::{Ok,Err};


    const CTRLB_ENABLE_ALL          : u8 = 0b1111_1000;
    const CTRLB_ENABLE_ALL_XCEPT_DRE: u8 = 0b1101_1000;

    /**
     * The AVR USART control register block in basic devices like 328P
     */
    #[repr(C)]
    pub struct AtmelSimpleUsartControl {
      pub(crate) ctrla: Volatile<u8>,
      pub(crate) ctrlb: Volatile<u8>,
      pub(crate) ctrlc: Volatile<u8>,
      pub(crate) _reserved: u8,
      pub(crate) baud: Volatile<u16>,
      pub(crate) data: Volatile<u8>
    }

    pub struct AtmelUsart<const BUF_SIZE: usize>
    {
      pub(crate) control: &'static mut AtmelSimpleUsartControl,
      pub(crate) read_handler: Cell<SerialReadEventCallback>,
      pub(crate) write_complete_handler: Cell<SerialWriteEventCallback>,
      pub(crate) error_handler: Cell<SerialErrorEventCallback>,

      pub(crate) rx_buf: RingQ<u8,BUF_SIZE>,
      pub(crate) tx_buf: RingQ<u8,BUF_SIZE>
    }

    /**
     * A no-op function that is the default handler for the serial
     * read interrupt callbacks.
     */
    #[inline(never)]
    pub(crate) fn read_nop_handler(_isotoken: Isolated, _src: SerialPortIdentity, byte: u8, _udata: Option<*const dyn Any>) -> ReadHandlerResult<u8> {
      ReadHandlerResult::Buffer(byte)
    }

    impl<const BUF_SIZE:usize> AtmelUsart<BUF_SIZE> {
      /**
       * Enable or disable the Data Register Empty interrupt.  This will
       * send interrupts continuously as long as there is no data to send,
       * so we need to turn if off when we have nothing left and turn it on
       * where there might be more.  try_send_u8_from_buffer more or less
       * handles this for us.
       */
      pub(crate) unsafe fn dre_int_enable(&mut self, enabled: bool) {
        self.control.ctrlb.write(match enabled {
          true  => CTRLB_ENABLE_ALL,
          false => CTRLB_ENABLE_ALL_XCEPT_DRE
        })
      }

      /**
       * Try to send a byte from our buffer.  It will only do so if our USART's
       * transmit buffer is free, and also only if we have something to send :).
       *
       * Returns false if there are no bytes to send, true otherwise.
       */
      pub(crate) unsafe fn isr_try_send_u8_from_buffer(&mut self, isotoken: Isolated) -> bool {
        if (self.control.ctrla.read() & 0b0010_0000) > 0 { // DRE
          let byte = self.tx_buf.consume(isotoken);

          match byte {
            Some(byte) => {
              self.control.data.write(byte);
              true
            },
            None => {
              // Nothing to send, so disable the DRE interrupt
              false
            }
          }
        } else {
          true
        }
      }

      /**
       * Called by the ISR when transmission is complete.  At this point
       * if we have no more bytes to send, we can re-enable sleep mode.
       */
      pub(crate) unsafe fn isr_transmission_complete(&mut self, _isotoken: Isolated) {
        // Clear the TXC flag.  I no longer trust the datasheet that
        // claims this is done automatically on interrupt complete, given
        // it lied about that in the ATmega4809 datasheet...
        self.control.ctrla.write(0b01000000);
      }

      /**
       * To to receive a byte into our buffer.  Returns true if we succeeded,
       * false if there was a buffer overflow.
       */
      pub(crate) unsafe fn isr_try_receive_u8_to_buffer(&mut self, isotoken: Isolated, byte: u8) -> bool {
        self.rx_buf.append(isotoken, byte).is_ok()
      }

      fn mode(&mut self, mode: SerialPortMode) -> &mut Self {
        // The datasheet says all USART configuration should be done with
        // global interrupts disabled, so...
        avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
          // Disable all interrupts and the receiver/transmitter
          self.control.ctrlb.write(0x00);

          // Clear the read buffer
          let _discard = self.control.data.read();

          // OK, now we are clean and ready to configure

          // Set the baud rate register
          self.control.baud.write(mode.avr_simple_baud_register_value());

          // Set the data comms mode
          self.control.ctrlc.write(mode.avr_simple_ctrlc());

          // Clear transmit complete flag and also make sure double-speed
          // and MCM modes are disabled.
          self.control.ctrla.write(0b01000000);

          // Enable receive & transmit interrupts, but not yet the
          // receiver/transmitter
          //
          // Data Register Empty is disabled, we'll enable it when we actually
          // send something.
          // Note we hardcode the UCSZn2 bit to zero also (no 9-bit rx/tx, sorry)
          self.control.ctrlb.write(0b110_00_000);
        });

        self
      }

      /**
       * Enable (or disable) receiver/transmitter.  Call this after you have set
       * up the mode, but also after you have configured the receive/transmit
       * pins to input/output as required.
       */
      fn enable_rxtx(&mut self, enable: bool) -> &mut Self {
        match enable {
          true  => self.control.ctrlb |= 0b000_11_000,
          false => self.control.ctrlb &= 0b111_00_111
        };
        self
      }

    }

    impl SerialPortMode {
      /**
       * Return the value to write to the USART's `baud` register to configure
       * this speed.
       */
      pub fn avr_simple_baud_register_value(&self) -> u16 {
        // Some would have us do these calcs using floating point arithmetic.
        // We do not for three reasons -
        // 1. It involves linking in a raft of floating point library code,
        // 2. Floating point maths is irretrievably broken with LLVM-avr at
        //    the moment.
        // 3. It's entirely unnecessary
        let integer_clk_per = MASTER_CLOCK_HZ / MASTER_CLOCK_PRESCALER as u32;

        match self {
          SerialPortMode::Asynch(baud, _, _, _) => {
            let integer_baud_clock = (integer_clk_per / (16u32 * baud.to_bps() as u32))-1;
            integer_baud_clock as u16
          },
          SerialPortMode::Synch(mode) => {
            match mode {
              SynchronousMode::Master(baud) => {
                let integer_baud_clock = (integer_clk_per / (2u32 * baud.to_bps() as u32)) - 1;
                integer_baud_clock as u16
              }
              SynchronousMode::Slave => {
                0
              }
            }
          }
        }
      }

      /**
       * Return the value to write the `ctrlc` register of the USART to
       * configure this mode
       */
      #[allow(clippy::unusual_byte_groupings)]
      pub fn avr_simple_ctrlc(&self) -> u8 {
        match self {
          SerialPortMode::Asynch(_baud, dbits, parity, sbits) => {
            let mut ctrlc: u8 = 0b00_000000; // Asynchronous USART mode
            ctrlc |= match parity {
              Parity::None => 0b00_00_0000,
              Parity::Even => 0b00_10_0000,
              Parity::Odd =>  0b00_11_0000
            };
            ctrlc |= match sbits {
              StopBits::Bits1 => 0b0000_0_000,
              StopBits::Bits2 => 0b0000_1_000
            };
            ctrlc |= match dbits {
              DataBits::Bits5 => 0b00000_00_0,
              DataBits::Bits6 => 0b00000_01_0,
              DataBits::Bits7 => 0b00000_10_0,
              DataBits::Bits8 => 0b00000_11_0,
              DataBits::Bits9LE => avr_oxide::oserror::halt(avr_oxide::oserror::OsError::BadParams),
              DataBits::Bits9HE => avr_oxide::oserror::halt(avr_oxide::oserror::OsError::BadParams)
            };
            ctrlc
          },
          SerialPortMode::Synch(_) => {
            let ctrlc: u8 = 0b01_000000; // Synchronous USART mode
            ctrlc
          }
        }
      }
    }
    impl<const BUF_SIZE:usize> SerialRxTx for AtmelUsart<BUF_SIZE> {
      fn set_mode(&mut self, mode: SerialPortMode) {
        self.mode(mode);
      }
      fn set_enable_rxtx(&mut self, enable: bool) {
        self.enable_rxtx(enable);
      }

      fn set_write_complete_callback(&self, callback: SerialWriteEventCallback) {
        avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
          self.write_complete_handler.replace(callback);
        });
      }

      fn set_read_callback(&self, callback: SerialReadEventCallback) {
        avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
          self.read_handler.replace(callback);
        });
      }

      fn set_error_callback(&self, callback: SerialErrorEventCallback) {
        avr_oxide::concurrency::interrupt::isolated(|_isotoken|{
          self.error_handler.replace(callback);
        });
      }


      /**
       * Attempt to write a byte.  May block if the tx buffer is full.
       */
      fn write_u8(&mut self, byte: u8) {
        match avr_oxide::concurrency::interrupt::isolated(|isotoken|{self.tx_buf.append(isotoken, byte)}) {
          Ok(()) => {
            // Byte added; nothing more for us to do!
          }
          Err(_e) => {
            // Hmm, byte couldn't be added.  Maybe our buffer is full - let's
            // try flushing and then block until we can add it.
            self.flush();
            self.tx_buf.append_blocking(byte);
          }
        }
      }

      /**
       * Flush the buffer (that is, start the interrupt handler which will
       * empty the buffer.)
       */
      fn flush(&mut self) {
        unsafe {
          // Enable the Data Register Empty interrupt, which will consume
          // any bytes in the buffer.
          self.dre_int_enable(true);
        }
      }

      /**
       * Read a byte from the input buffer, blocking until a byte is
       * available if the buffer is empty.
       */
      fn read_u8(&mut self) -> u8 {
        self.rx_buf.consume_blocking()
      }

      /**
       * Try to read a byte from the input buffer, returns Some(byte) if
       * there is a byte available, or None if not.
       */
      fn try_read_u8(&mut self) -> Option<u8> {
        avr_oxide::concurrency::interrupt::isolated(|isotoken|{self.rx_buf.consume(isotoken)})
      }
    }

    impl<const BUF_SIZE:usize> SerialNoInterruptTx for AtmelUsart<BUF_SIZE> {
      fn can_write(&self) -> bool {
        (self.control.ctrlb.read() & 0b000_10_000) > 0
      }

      fn blocking_write_u8(&mut self, byte: u8) {
        unsafe {
          avr_oxide::concurrency::interrupt::isolated(|_isotoken|{

            // Wait until the data register is ready to accept a byte
            while (self.control.ctrla.read() & 0b00100000) == 0 {
              asm!("nop")
            }
            // Now write the byte
            self.control.data.write(byte);

            // Wait for transmission complete flag
            while (self.control.ctrla.read() & 0b01000000) == 0 {
              asm!("nop")
            }
            // Now clear the transmission complete flag
            self.control.ctrla.write(0b01000000);
          })
        }
      }
    }

    #[doc(hidden)]
    #[macro_export]
    macro_rules! atmel_simple_usart_tpl {
      ($usartref:expr, $srcref:expr, $implementation:ty, $isr_rx:expr, $isr_tx:expr, $isr_dre:expr) => {
        use avr_oxide::hal::generic::serial::{SerialError, ReadHandlerResult,SerialReadEventCallback,SerialWriteEventCallback,SerialErrorEventCallback};
        use avr_oxide::hal::generic::serial::base::simple::{ AtmelUsart, read_nop_handler };
        use avr_oxide::{mut_singleton_explicit_init,isr_cb_invoke};
        use avr_oxide::private::ringq::RingQ;
        use core::marker::PhantomData;
        use core::cell::Cell;

        pub type SerialImpl = $implementation;

        mut_singleton_explicit_init!(
          $implementation,
          __INSTANCE,
          initialise, instance, instance_isolated,
          AtmelUsart {
            control: core::mem::transmute($usartref),
            read_handler: Cell::new(SerialReadEventCallback::Function(read_nop_handler)),
            write_complete_handler: Cell::new(SerialWriteEventCallback::Nop(())),
            error_handler: Cell::new(SerialErrorEventCallback::Nop(())),
            rx_buf: RingQ::new(),
            tx_buf: RingQ::new(),
          });

        /**
         * Receive interrupt handler. Checks the type of interrupt, if it's
         * an error type we'll call the read handler with a suitable Err
         * instance.  If it's receive complete, we will read the byte and
         * send that to the callback.  In all cases, you need to have
         * set a `set_read_callback` of course.
         */
        #[oxide_macros::interrupt(isr=$isr_rx)]
        fn usart_rx_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
          let atmelusart = instance_isolated(isotoken);
          let trigger = atmelusart.control.ctrla.read();

          if (trigger & 0b10000000) > 0 { // Receive complete
            // We MUST read the byte to clear the interrupt
            let byte = atmelusart.control.data.read();
            // Pass it to the user handler, and then buffer it if needed
            match isr_cb_invoke!(isotoken, atmelusart.read_handler.get(), $srcref, byte) {
              ReadHandlerResult::Buffer(byte) => {
                if !atmelusart.isr_try_receive_u8_to_buffer(isotoken, byte) {
                  isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::BufferOverflow);
                }
              },
              ReadHandlerResult::Discard => {}
            }
          }
          if (trigger & 0b00010000) > 0 { // Frame error
            // @todo Strictly speaking, this is only a Break if the received
            //       byte is zero.
            isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::Break);
          }
          if (trigger & 0b00001000) > 0 { // Data overrun error
            isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::BufferOverflow);
          }
          if (trigger & 0b00000100) > 0 { // Parity error
            isr_cb_invoke!(isotoken, atmelusart.error_handler.get(), $srcref, SerialError::ParityError);
          }
        }
        /**
         * Transmit interrupt handler.  Our only job here is to check if
         * there is nothing left in the transmit buffer - if so, we can
         * permit the processor to enter Standby mode again.
         */
        #[oxide_macros::interrupt(isr=$isr_tx)]
        fn usart_tx_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
          let atmelusart = instance_isolated(isotoken);

          atmelusart.isr_transmission_complete(isotoken);
        }

        /**
         * Data Register Empty interrupt handler.  We will try to send a
         * byte from our transmit buffer, if that fails we will turn off
         * the interrupt.
         */
        #[oxide_macros::interrupt(isr=$isr_dre)]
        fn usart_dre_interrupt(isotoken: avr_oxide::concurrency::Isolated) {
          let atmelusart = instance_isolated(isotoken);

          match atmelusart.isr_try_send_u8_from_buffer(isotoken) {
            true => {
              // Success, leave the interrupt enabled
            },
            false => {
              // Nothing to send, so let's disable the interrupt
              // and also send a notification
              atmelusart.dre_int_enable(false);

              isr_cb_invoke!(isotoken, atmelusart.write_complete_handler.get(), $srcref);
            }
          }
        }
      }
    }
  }
}

// Tests =====================================================================
#[cfg(not(target_arch="avr"))]
pub mod base {
  pub mod zeroseries {
    use avr_oxide::hal::generic::serial::{SerialPortMode, SerialPortIdentity, BaudRate, Parity, StopBits, DataBits, SerialRxTx, SerialWriteEventCallback, SerialReadEventCallback, SerialError, ReadHandlerResult, SerialErrorEventCallback};
    use avr_oxide::hal::generic::MuxControl;
    use core::marker::PhantomData;
    use std::fmt::{Display, Formatter};

    /**
     * The dummy USART control register block
     */
    #[repr(C)]
    pub struct DummyUsartControl {
    }

    #[allow(dead_code)]
    pub struct DummyUsart {
      pub(crate) control: DummyUsartControl,
    }

    impl DummyUsart {
      pub fn mux<M: MuxControl>(&mut self, mux: M) -> &mut Self {
        mux.route();

        self
      }
    }

    impl Display for SerialPortMode {
      fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
          SerialPortMode::Asynch(baud, dbits, parity, stopbits) => {
            f.write_str(&format!("{} {}{}{}", baud, dbits, parity, stopbits))
          }
          SerialPortMode::Synch(_) => {
            f.write_str("Synchronous")
          }
        }
      }
    }
    impl Display for BaudRate {
      fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
          BaudRate::Baud300 => "300",
          BaudRate::Baud600 => "600",
          BaudRate::Baud1200 => "1200",
          BaudRate::Baud2400 => "2400",
          BaudRate::Baud4800 => "4800",
          BaudRate::Baud9600 => "9600",
          BaudRate::Baud14400 => "14400",
          BaudRate::Baud19200 => "19200",
          BaudRate::Baud28800 => "28800",
          BaudRate::Baud38400 => "38400",
          BaudRate::Baud57600 => "57600",
          BaudRate::Baud76800 => "76800",
          BaudRate::Baud115200 => "115200",
          BaudRate::Auto => "Auto"
        })
      }
    }
    impl Display for DataBits {
      fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
          DataBits::Bits5 => "5",
          DataBits::Bits6 => "6",
          DataBits::Bits7 => "7",
          DataBits::Bits8 => "8",
          DataBits::Bits9LE => "9le",
          DataBits::Bits9HE => "9he"
        })
      }
    }
    impl Display for Parity {
      fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
          Parity::None => "N",
          Parity::Even => "E",
          Parity::Odd => "O"
        })
      }
    }
    impl Display for StopBits {
      fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
          StopBits::Bits1 => "1",
          StopBits::Bits2 => "2"
        })
      }
    }

    impl SerialRxTx for DummyUsart {
      fn set_mode(&mut self, mode: SerialPortMode) {
        println!("*** USART: Set port mode to {}", mode);
      }

      fn set_enable_rxtx(&mut self, enable: bool) {
        println!("*** USART: Rx/Tx enable set to {}", enable);
      }

      fn set_write_complete_callback(&self, callback: SerialWriteEventCallback) {
        println!("*** USART: Write callback set to {:?}", callback);
      }

      fn set_read_callback(&self, callback: SerialReadEventCallback) {
        println!("*** USART: Write callback set to {:?}", callback);
      }

      fn set_error_callback(&self, callback: SerialErrorEventCallback) {
        println!("*** USART: Write callback set to {:?}", callback);
      }


      /**
       * Attempt to write a byte.  May block if the tx buffer is full.
       */
      fn write_u8(&mut self, byte: u8) {
        println!("*** USART: Wrote '{}'", byte as char);
        eprint!("{}", byte as char);
      }

      fn flush(&mut self) {
        todo!()
      }

      fn read_u8(&mut self) -> u8 {
        todo!()
      }

      fn try_read_u8(&mut self) -> Option<u8> {
        todo!()
      }
    }

    #[doc(hidden)]
    #[macro_export]
    macro_rules! atmel_0series_usart_tpl {
      ($usartref:expr, $srcref:expr, $implementation:ty, $isr_rx:expr, $isr_tx:expr, $isr_dre:expr) => {
        use avr_oxide::hal::generic::serial::{SerialError, ReadHandlerResult,SerialReadEventCallback,SerialWriteEventCallback,SerialErrorEventCallback};
        use avr_oxide::{mut_singleton_explicit_init,isr_cb_invoke};
        use avr_oxide::private::ringq::RingQ;
        use core::marker::PhantomData;
        use core::cell::Cell;
        use avr_oxide::hal::generic::serial::base::zeroseries::{ DummyUsart, DummyUsartControl};

        pub type SerialImpl = DummyUsart;

        static mut INSTANCE : DummyUsart = DummyUsart {
          control: DummyUsartControl {},
        };

        pub fn initialise() {
        }

        pub fn instance_isolated(_isotoken: avr_oxide::concurrency::Isolated) -> &'static mut SerialImpl {
          unsafe {
            &mut INSTANCE
          }
        }

        pub fn instance() -> &'static mut SerialImpl {
          unsafe {
            &mut INSTANCE
          }
        }
      }
    }
  }
}