aboutsummaryrefslogtreecommitdiff
path: root/fpga/src/Uart.bs
blob: 729808364471fb91e3302a46fcda15d20279e597 (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
package Uart where

import Clock
import FIFOF
import GetPut

-- | 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 clock bufferSize =
  module
    fifo :: FIFOF (Bit 8) <- mkSizedFIFOF bufferSize
    state :: Reg TxState <- mkReg Idle
    pin :: Reg (Bit 1) <- mkReg 1

    rules
      "uart_tx": when clock.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 =
  -- | Reads a byte from the UART's receive buffer.
  recv :: Get (Bit 8)

mkRxUart :: Wire (Bit 1) -> Clock -> Integer -> Module RxUart
mkRxUart rx clock bufferSize =
  module
    fifo :: FIFOF (Bit 8) <- mkGSizedFIFOF True False bufferSize
    state :: Reg RxState <- mkReg Idle

    rules
      "uart_rx": when clock.clk
        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

    interface RxUart
      recv = 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)

mkUart :: Wire (Bit 1) -> Clock -> Module Uart
mkUart rx clock = module
  uart_rx <- mkRxUart rx clock 8
  uart_tx <- mkTxUart clock 8
  interface Uart
    tx = uart_tx.pin
    recv = uart_rx.recv
    send = uart_tx.send

-- vim: set ft=haskell :