RiscV-Formal/hs/Peripherals/Uart.hs

82 lines
2.4 KiB
Haskell

module Peripherals.Uart (read, write, UartAddr) where
import Clash.Prelude hiding (read)
import Types (Byte)
import Data.Char (ord, chr)
import Peripherals.UartCFFI (
initTerminal,
restoreTerminal,
getCharFromTerminal,
writeCharToTerminal,
isCharAvailable,
setupSigintHandler,
wasCtrlCReceived
)
import BusTypes (
TransactionSize(..),
BusVal(..),
)
import GHC.Generics (URec(UAddr), Generic (from))
-- 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 ()