blob: 1e7ba275a00d569301571611e13f6f0dfd88d119 (
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
|
-- | An I²C controller with support for 7-bit addresses, with a 16-element FIFO
-- buffer.
package I2C where
import Clocks
import GetPut
-- | An I²C controller.
interface I2C =
-- | The TX side of the SCL pin. High corresponds to high-impedance.
txSCL :: Bit 1 {-# always_ready #-}
-- | The TX side of the SDA pin. High corresponds to high-impedance.
txSDA :: Bit 1 {-# always_ready #-}
-- | Reads a result from the I²C controller's receive buffer.
recv :: Get I2CResult
-- | Writes a command to the I²C controller's command buffer.
send :: Put I2CCommand
data I2CCommand
= -- | A read from the given address. If the read succeeds with the byte `b`,
-- `Just b` is written to the receive buffer. If the read fails, `Nothing`
-- is written to the receive buffer.
Read (Bit 7)
| -- | A write to the given address. If the write succeeds, `Just 0` is
-- written to the receive buffer. If the write fails, `Nothing` is written
-- to the receive buffer.
Write (Bit 7) (Bit 8)
deriving (Bits, FShow)
getAddrAndRW :: I2CCommand -> Bit 8
getAddrAndRW (Read addr) = addr ++ 0b0
getAddrAndRW (Write addr _) = addr ++ 0b1
data I2CResult
= -- | A byte was successfully read.
ReadOK (Bit 8)
| -- | Arbitration was lost.
ArbitrationLost
| -- | A NACK bit was received.
NACK
deriving (Bits, FShow)
data State
= -- | The initial state.
Idle
| -- | We're about to lower the clock as part of writing the address bit with
-- the given index. Also used as part of the START sequence.
LowerSCLBeforeAddrAndRWBit (Bit 3) I2CCommand
| -- | We're about to write the address bit with the given index.
WriteAddrAndRWBit (Bit 3) I2CCommand
| -- | We're about to raise the clock as part of writing the address.
RaiseSCLAfterAddrAndRWBit (Bit 3) I2CCommand
| -- | We're about to wait for the target to read the address bit.
WaitAfterAddrAndRWBit (Bit 3) I2CCommand
| -- | TODO
TODO
deriving (Bits, FShow)
-- | Runs an I²C interface on the current clock.
mkI2C' :: Bit 1 -> Bit 1 -> Reg (Bit 1) -> Reg (Bit 1) ->
SyncFIFOIfc I2CCommand -> SyncFIFOIfc I2CResult -> Module I2C
mkI2C' rxSCL rxSDA txSCL txSDA sendFIFO recvFIFO = module
state :: Reg State <- mkReg Idle
rules
when True ==> do
$display "state: " (fshow state)
when Idle <- state
rules
when not sendFIFO.notEmpty ==> do
$display "Staying idle"
-- Keep idling.
txSCL := 1
txSDA := 1
when sendFIFO.notEmpty ==> do
let cmd = sendFIFO.first
sendFIFO.deq
$display "Got a command: " (fshow cmd)
-- See if we can get the bus.
case rxSDA of
0 -> do
-- We lost arbitration -- someone else is driving the bus low.
recvFIFO.enq ArbitrationLost
1 -> do
-- Drive SDA low and wait.
txSDA := 0
state := LowerSCLBeforeAddrAndRWBit 7 cmd
when LowerSCLBeforeAddrAndRWBit i cmd <- state ==> do
-- Drive SCL low.
txSCL := 0
-- Prepare to start writing the address.
state := WriteAddrAndRWBit i cmd
when WriteAddrAndRWBit i cmd <- state ==> do
-- Keep SCL low while writing the data bit.
txSDA := (getAddrAndRW cmd)[i:i]
-- Next, we'll raise the clock for a cycle.
state := RaiseSCLAfterAddrAndRWBit i cmd
when RaiseSCLAfterAddrAndRWBit i cmd <- state ==> do
-- Keep SDA the same while raising SCL.
txSCL := 1
state := WaitAfterAddrAndRWBit i cmd
when WaitAfterAddrAndRWBit i cmd <- state ==> do
if i == 0 then
state := TODO
else
state := LowerSCLBeforeAddrAndRWBit (i - 1) cmd
when TODO <- state ==> do
state := TODO
interface I2C
txSCL = txSCL
txSDA = txSDA
recv = toGet recvFIFO
send = toPut sendFIFO
-- | Returns an I²C interface with FIFOs that runs on a divided clock. Note
-- that the clock rate should be twice the bus speed -- to run the bus at
-- 100kbit/s (normal mode), the clock should run at 200kHz.
mkDividedI2C :: Integer -> Bit 1 -> Bit 1 -> Module I2C
mkDividedI2C divisor rxSCL rxSDA = module
clock <- mkClockDivider divisor
reset <- mkAsyncResetFromCR divisor clock.slowClock
rxSCLSync :: SyncBitIfc (Bit 1) <- mkSyncBitFromCC clock.slowClock
rxSDASync :: SyncBitIfc (Bit 1) <- mkSyncBitFromCC clock.slowClock
txSCLSync :: Reg (Bit 1) <- mkSyncRegToCC 1 clock.slowClock reset
txSDASync :: Reg (Bit 1) <- mkSyncRegToCC 1 clock.slowClock reset
recvFIFO :: SyncFIFOIfc I2CResult <- mkSyncFIFOToCC 16 clock.slowClock reset
sendFIFO :: SyncFIFOIfc I2CCommand <- mkSyncFIFOFromCC 16 clock.slowClock
rules
when True ==> do
rxSCLSync.send rxSCL
rxSDASync.send rxSDA
changeSpecialWires (Just clock.slowClock) (Just reset) Nothing $ module
mkI2C' rxSCLSync.read rxSDASync.read txSCLSync txSDASync sendFIFO recvFIFO
interface I2C
txSCL = txSCLSync
txSDA = txSDASync
recv = toGet recvFIFO
send = toPut sendFIFO
-- vim: set ft=haskell :
|