aboutsummaryrefslogtreecommitdiff
path: root/fpga/src/Uart.bs
blob: 954f96e0717415ebe5c69b8b7db39b48447c6c6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package Uart where

import Clocks
import GetPut

-- | The state of the TX side of the UART.
data TxState
  = -- | The UART is not currently sending anything. Transitions to 'Data b 0'
    -- after sending the start bit once.
    Idle
  | -- | The UART is about to send a data bit. 'Data b n' transitions to
    -- 'Data b (n + 1)' by sending a data bit. 'Data b 7' 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)

-- | Runs the TX side of the UART, receiving bytes from the 'Get' and writing
-- output bits to the 'Put'.
mkTxUart :: RWire (Bit 8) -> Wire (Bit 1) -> Module ()
mkTxUart fifo tx = module
  state :: Reg TxState <- mkReg Idle
  rules
    "uart_tx_idle": when Idle <- state ==> do
      case fifo.wget of
        Just b -> do
          state := Data b 7
          tx._write 0
        Nothing ->
          tx._write 1
    "uart_tx_data": when Data b n <- state ==> do
      tx._write b[n:n]
      if n == 7 then
        state := Idle
      else
        state := Data b (n + 1)

-- | 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)

-- | Runs the RX side of the UART, receiving input bits from the 'Get' and
-- writing bytes to the 'Put'.
mkRxUart :: Wire (Bit 8) -> Bit 1 -> Module ()
mkRxUart fifo rxBit = module
  rules

{-
mkRxUart :: Wire (Bit 1) -> Integer -> Module (Get (Bit 8))
mkRxUart rx bufferSize = module
  fifo :: FIFOF (Bit 8) <- mkGSizedFIFOF True False bufferSize
  state :: Reg RxState <- mkReg Idle
  rules
    "uart_rx_idle_to_start": when Idle <- state, rx == 0 ==> do
      state := Data 0 0
    "uart_rx_data_to_data": when Data bits n <- state, n < 7 ==> do
      state := Data (rx ++ bits[7:1]) (n + 1)
    "uart_rx_data_to_stop": when Data bits 7 <- state ==> do
      state := Stop (rx ++ bits[7:1])
    "uart_rx_stop_to_idle": when Stop bits <- state, rx == 1 ==> do
      fifo.enq bits
      state := Idle
  return (toGet fifo)
-}

-- | An 8n1 UART.
interface Uart =
  -- | The TX pin.
  tx :: 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)

-- | Returns a UART that does _not_ have FIFOs.
mkUart' :: Bit 1 -> Module Uart
mkUart' rx = module
  recvFIFO :: Wire (Bit 8) <- mkWire
  sendFIFO :: RWire (Bit 8) <- mkRWireSBR
  tx :: Wire (Bit 1) <- mkWire

  mkRxUart recvFIFO rx
  mkTxUart sendFIFO tx

  interface Uart
    tx = tx._read
    recv = toGet recvFIFO
    send = toPut sendFIFO

mkDividedUart :: Integer -> Bit 1 -> Module Uart
mkDividedUart divisor rx = module
  clock <- mkClockDivider divisor
  reset <- mkAsyncResetFromCR divisor clock.slowClock

  rxSync :: SyncBitIfc (Bit 1) <- mkSyncBitFromCC clock.slowClock
  txSync :: Reg (Bit 1) <- mkSyncRegToCC 1 clock.slowClock reset
  recvFIFO :: SyncFIFOIfc (Bit 8) <- mkSyncFIFOToCC 16 clock.slowClock reset
  sendFIFO :: SyncFIFOIfc (Bit 8) <- mkSyncFIFOFromCC 16 clock.slowClock
  rules
    when True ==> rxSync.send rx

  changeSpecialWires (Just clock.slowClock) (Just reset) Nothing $ module
    uart <- mkUart' rxSync.read
    rules
      when True ==> txSync._write uart.tx
      when True ==> do
        byte <- uart.recv.get
        recvFIFO.enq byte
      when True ==> do
        uart.send.put sendFIFO.first
        sendFIFO.deq
    return ()

  interface Uart
    tx = txSync._read
    recv = toGet recvFIFO
    send = toPut sendFIFO

-- vim: set ft=haskell :