RiscV-Formal/hs/Peripherals/Uart.hs

78 lines
2.3 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 (
getCharFromTerminal,
writeCharToTerminal,
isCharAvailable,
)
import BusTypes (
TransactionSize(..),
BusVal(..),
)
import Util((|>))
-- 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) <- fmap 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 = fmap (busValFromByte size) buildRBR
| addr == lsrAddr = fmap (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 ()