aboutsummaryrefslogtreecommitdiff
path: root/fpga/src/Uart.bs
diff options
context:
space:
mode:
authorNathan Ringo <nathan@remexre.com>2024-09-23 21:46:34 -0500
committerNathan Ringo <nathan@remexre.com>2024-09-23 21:46:34 -0500
commitfc1959bd9887ecc4d4ceb62a53e87abc6f49ef00 (patch)
tree801fad484692ca226eb2dfdf792338cabe218c72 /fpga/src/Uart.bs
parent777da6874bdbda1c83108024eb37dc901e04838e (diff)
Adds README, moves fpga stuff to a subdirectory.
Diffstat (limited to 'fpga/src/Uart.bs')
-rw-r--r--fpga/src/Uart.bs148
1 files changed, 148 insertions, 0 deletions
diff --git a/fpga/src/Uart.bs b/fpga/src/Uart.bs
new file mode 100644
index 0000000..1059a65
--- /dev/null
+++ b/fpga/src/Uart.bs
@@ -0,0 +1,148 @@
+package Uart where
+
+import FIFOF
+import GetPut
+import Util
+
+interface Clock =
+ clk :: Bool
+
+mkDivider :: Integer -> Module Clock
+mkDivider divisor =
+ module
+ count :: Reg (Bit 32) <- mkReg 0
+
+ rules
+ "increment_divider": when True ==> do
+ if count == fromInteger (divisor - 1) then do
+ count := 0
+ else
+ count := count + 1
+
+ interface Clock
+ clk = count == 0
+
+-- | The state of the TX side of the UART.
+data TxState
+ = -- | The UART is not currently sending anything. May transition to
+ -- 'Start b' when ready to send 'b'.
+ Idle
+ | -- | The UART is about to send the start bit. 'Start b' transitions to
+ -- 'Data b 7' by sending the start bit.
+ Start (Bit 8)
+ | -- | The UART is about to send a data bit. 'Data b n' transitions to
+ -- 'Data (b >> 1) (n - 1)' by sending a data bit. 'Data b 0' transitions to
+ -- 'Idle' by sending the last data bit. Being in the 'Idle' state for a
+ -- clock transmits the stop bit.
+ Data (Bit 8) (Bit 3)
+ deriving (Bits)
+
+-- | The TX side of the UART.
+interface TxUart =
+ -- | The TX pin.
+ pin :: Bit 1
+ -- | Writes a byte to the UART's transmit buffer.
+ send :: Put (Bit 8)
+
+mkTxUart :: Clock -> Integer -> Module TxUart
+mkTxUart baudClock bufferSize =
+ module
+ fifo :: FIFOF (Bit 8) <- mkSizedFIFOF bufferSize
+ state :: Reg TxState <- mkReg Idle
+ pin :: Reg (Bit 1) <- mkReg 1
+
+ rules
+ "uart_tx": when baudClock.clk
+ rules
+ "uart_tx_idle": when Idle <- state, not fifo.notEmpty ==> do
+ pin := 1
+ "uart_tx_idle_to_start": when Idle <- state, fifo.notEmpty ==> do
+ pin := 1
+ b <- (toGet fifo).get
+ state := Start b
+ "uart_tx_start": when Start b <- state ==> do
+ pin := 0
+ state := Data b 7
+ "uart_tx_data": when Data b n <- state ==> do
+ pin := b[0:0]
+ if n == 0 then
+ state := Idle
+ else
+ state := Data (b >> 1) (n - 1)
+ interface TxUart
+ pin = pin
+ send = toPut fifo
+
+-- | The state of the RX side of the UART.
+data RxState
+ = -- | The initial state of the UART, and the state after receiving the stop
+ -- bit. May transition to 'Data 0 0' when the start bit is received.
+ Idle
+ | -- | In the 'Data _ n' state, the UART has received the start bit and 'n'
+ -- data bits, and is about to receive more data bits. 'Data _ n'
+ -- transitions to 'Data _ (n + 1)' by receiving a data bit. 'Data b 7'
+ -- transitions to 'Stop' by receving the last data bit.
+ Data (Bit 8) (Bit 3)
+ | -- | In the 'Stop' state, the UART has received the start and data bits,
+ -- and is waiting for the stop bit (which is ignored). Transitions to
+ -- 'Idle'.
+ Stop (Bit 8)
+ deriving (Bits, FShow)
+
+-- | The RX side of the UART.
+interface RxUart =
+ -- | The RX pin.
+ pin :: Bit 1 -> Action
+ -- | Reads a byte from the UART's receive buffer.
+ recv :: Get (Bit 8)
+
+mkRxUart :: Clock -> Integer -> Module RxUart
+mkRxUart baudClock bufferSize =
+ module
+ fifo :: FIFOF (Bit 8) <- mkGSizedFIFOF True False bufferSize
+ state :: Reg RxState <- mkReg Idle
+ pin :: Wire (Bit 1) <- mkWire
+
+ rules
+ "uart_rx": when baudClock.clk
+ rules
+ "uart_rx_idle_to_start": when Idle <- state, pin == 0 ==> do
+ state := Data 0 0
+ "uart_rx_data_to_data": when Data bits n <- state, n < 7 ==> do
+ state := Data (pin ++ bits[7:1]) (n + 1)
+ "uart_rx_data_to_stop": when Data bits 7 <- state ==> do
+ state := Stop (pin ++ bits[7:1])
+ "uart_rx_stop_to_idle": when Stop bits <- state, pin == 1 ==> do
+ fifo.enq bits
+ state := Idle
+
+ interface RxUart
+ pin bit = pin := bit
+ recv = toGet fifo
+
+-- | An 8n1 UART.
+interface Uart =
+ -- | The RX pin.
+ rxPin :: Bit 1 -> Action
+ -- | The TX pin.
+ txPin :: Bit 1
+
+ -- | Reads a byte from the UART's receive buffer.
+ recv :: Get (Bit 8)
+ -- | Writes a byte to the UART's transmit buffer.
+ send :: Put (Bit 8)
+
+mkUart :: Integer -> Module Uart
+mkUart baudDivisor =
+ module
+ baudClock <- mkDivider baudDivisor
+ rx <- mkRxUart baudClock 8
+ tx <- mkTxUart baudClock 8
+
+ interface Uart
+ rxPin = rx.pin
+ txPin = tx.pin
+ recv = rx.recv
+ send = tx.send
+
+-- vim: set ft=haskell :