aboutsummaryrefslogtreecommitdiff
path: root/src/Uart.bs
blob: 2cfe14a982a18025dd44974dd921d84ccd5243a5 (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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
    -- 'Stop' by sending the last data bit.
    Data (Bit 8) (Bit 3)
  | -- | The UART is about to send the stop bit. Transitions to 'Idle'.
    Stop
  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 ==> 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 := Stop
            else
              state := Data (b >> 1) (n - 1)
          "uart_tx_stop": when Stop <- state ==> do
            pin := 1
            state := Idle
    interface TxUart
      pin = pin
      send = toPut fifo

-- | The state of the RX side of the UART.
data RxState
  = -- | The UART is not currently receiving anything. 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 'Idle' by receving the last data bit.
    Data (Bit 8) (Bit 3)
  deriving (Bits)

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

  debugBit :: Bit 1

mkRxUart :: Clock -> Integer -> Module RxUart
mkRxUart baudClock bufferSize =
  module
    fifo :: FIFOF (Bit 8) <- mkSizedFIFOF bufferSize
    state :: Reg RxState <- mkReg Idle
    pin :: Wire (Bit 1) <- mkWire
    debugBit :: Reg (Bit 1) <- mkReg 1

    rules
      "uart_rx": when baudClock.clk
        rules
          "uart_rx_idle": when Idle <- state ==>
            if pin == 0 then
              state := Data 0 0
            else
              state := Idle
          "uart_rx_data": when Data hi n <- state ==> do
            debugBit := pin
            let b :: Bit 8 = hi[7:1] ++ pin
            if n == 7 then do
              fifo.enq b
              state := Idle
            else
              state := Data b (n + 1)

    interface RxUart
      pin bit = pin := bit
      recv = toGet fifo
      debugBit = debugBit

-- | 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)
  debugBit :: Bit 1

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
      debugBit = rx.debugBit

-- vim: set ft=haskell :