module Peripherals.Uart (read, write, UartAddr) where import Clash.Prelude hiding (read) import Types (Byte) import Data.Char (ord, chr) import Peripherals.UartCFFI ( getCharFromTerminal, writeCharToTerminal, isCharAvailable, ) import BusTypes ( TransactionSize(..), BusVal(..), ) -- based on a 16550 UART which has an address space of 8 bytes type UartAddr = Unsigned 3 -- Receiver Buffer Register address (commonly 0x0 for 16550 UART) rbrAddr :: UartAddr rbrAddr = 0x0 thrAddr :: UartAddr thrAddr = 0x0 -- Line Status Register address lsrAddr :: UartAddr lsrAddr = 0x5 -- Helper function to convert Byte to BusVal based on TransactionSize busValFromByte :: TransactionSize -> Byte -> BusVal busValFromByte size val = case size of SizeByte -> BusByte val SizeHalfWord -> BusHalfWord (resize val) SizeFullWord -> BusFullWord (resize val) SizeDoubleWord -> BusDoubleWord (resize val) SizeQuadWord -> BusQuadWord (resize val) -- Reads a character from the terminal (RBR equivalent) buildRBR :: IO Byte buildRBR = do c <- getCharFromTerminal return $ fromIntegral (ord c) -- Convert Char to Byte -- Reads the Line Status Register (LSR) to check character availability buildLSR :: IO Byte buildLSR = do (char_available :: Byte) <- fromIntegral <$> isCharAvailable -- highly unlikely that we overflow stdout buffer, so we set -- transmit to always ready let (transmit_ready :: Byte) = 0b0010_0000 return (char_available .|. transmit_ready) -- Updated 'read' function to handle RBR and LSR reads read :: TransactionSize -> UartAddr -> IO BusVal read size addr | addr == rbrAddr = busValFromByte size <$> buildRBR | addr == lsrAddr = busValFromByte size <$> buildLSR | otherwise = return $ busValFromByte size 0x00 extractLowestByte :: BusVal -> Byte extractLowestByte (BusByte b) = b extractLowestByte (BusHalfWord hw) = resize hw extractLowestByte (BusFullWord fw) = resize fw extractLowestByte (BusDoubleWord dw) = resize dw extractLowestByte (BusQuadWord qw) = resize qw byteToChar :: Byte -> Char byteToChar = chr . fromIntegral write :: BusVal -> UartAddr -> IO () write val addr | addr == thrAddr = writeCharToTerminal $ byteToChar $ extractLowestByte val | otherwise = return ()