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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
|
-- | A simple I²C controller.
package I2C where
import Clocks
import GetPut
struct I2CStatus =
{ -- | This bit is low when a message is being sent, and gets raised after:
--
-- - We finished sending an address, but a NACK was received.
-- - We finished reading a byte, including sending an ACK or NACK bit.
-- - We finished writing a byte, including reading an ACK or NACK bit.
--
-- When this bit is `True`, writing to it will cause a new read or write,
-- either in the current transaction (if this happened before `busIdle` was
-- lowered) or in a new one.
ready :: Bool
; -- | Whether the bus is idle. Usually, software will want to enable
-- interrupts on the `ready` bit instead to determine when to send new
-- commands; the `busIdle` bit is only set to `True` after the STOP
-- condition (i.e., after the transaction ends). Writing to this bit does
-- not affect the internal state of the I²C controller.
busIdle :: Bool
; -- | The last ACK bit we received from sending an address. Writing to this
-- bit does not affect the internal state of the I²C controller.
addrAck :: Bool
; -- | The last ACK bit we received during a write, or the next ACK bit we'll
-- send during a read.
dataAck :: Bool
; -- | A flag set if we detect during a START condition that we've lost
-- arbitration. If this occurs, no read or write will occur, and the read
-- or write should be retried later. Writing to this bit does not affect
-- the internal state of the I²C controller.
arbitrationLost :: Bool
; -- | Not-yet-allocated bits.
rsvd :: Bit 3
}
deriving (Bits)
-- | An I²C controller.
interface I2C =
-- | The TX side of the SCL pin. High corresponds to high-impedance.
txSCL :: Bit 1
-- | The TX side of the SDA pin. High corresponds to high-impedance.
txSDA :: Bit 1
-- | The address register, which contains the address in the high 7 bits and
-- the R/!W bit in the low bit.
addrReg :: Reg (Bit 8)
-- | The data register, which can be read into by a read command or written
-- from by a write command, depending on which the address register's low bit
-- says to do.
dataReg :: Reg (Bit 8)
-- | The status register.
statusReg :: Reg I2CStatus
data I2CBusState
= -- | (We believe) the bus is idle.
Idle
| -- | We've driven SDA low as part of the START condition.
StartSDA
| -- | We're sending an address, and about to write the numbered bit to SDA.
-- SCL should be low.
SendAddrBitSDA (Bit 3)
| -- | We're sending an address, and about to raise SCL.
SendAddrBitRaiseSCL (Bit 3) (Bit 1)
| -- | We're waiting for the device to read our address bit. SCL should be
-- high.
SendAddrBitWait (Bit 3) (Bit 1)
| -- | We're sending an address, and about to lower SCL.
SendAddrBitLowerSCL (Bit 3) (Bit 1)
| -- | We're lowering SDA as part of getting the ACK bit after sending an
-- address.
GetAddrAckSDA
| -- | We're raising SCL to start the clock cycle in which we'll get the ACK
-- bit after sending an address.
GetAddrAckRaiseSCL
| -- | We're reading SDA, which should be the ACK bit after sending an
-- address.
GetAddrAckReadSDA
| -- | We're about to either lower SCL or keep it low as part of reading a
-- byte from a device.
ReadLowerSCL (Bit 3) (Bit 1)
| -- | We're about to raise SCL as part of reading a byte from a device.
ReadRaiseSCL (Bit 3)
| -- | We're about to read SDA while keeping SCL high as part of reading a
-- byte from a device.
ReadReadSDA (Bit 3)
| -- | We're about to lower SCL as part of writing an ACK bit after reading a
-- byte from a device.
ReadAckLowerSCL
| -- | We're about to write SDA while keeping SCL low as part of writing an
-- ACK bit in response to reading a byte from a device.
ReadAckWriteSDA
| -- | We're about to raise SCL or keep it raised as part of writing an ACK
-- bit after reading a byte from a device. The first bit is the bit to
-- hold, the second is the number of cycles to hold it for.
ReadAckRaiseSCL (Bit 1) (Bit 1)
| -- | TODO
TODO
deriving (Bits, FShow)
-- | Runs an I²C interface on the current clock.
mkI2C' :: Bit 1 -> Bit 1 -> Wire (Bit 1) -> Wire (Bit 1) -> Reg (Bit 8) ->
Reg (Bit 8) -> Reg I2CStatus -> Module ()
mkI2C' rxSCL rxSDA txSCL txSDA addrReg dataReg statusReg = module
state :: Reg I2CBusState <- mkReg Idle
rules
"debug": when True ==> $display (fshow state)
"remain Idle": when Idle <- state, statusReg.notBusy ==> do
state := Idle
txSCL := 1
txSDA := 1
"triggered from Idle": when Idle <- state, not statusReg.notBusy ==>
if rxSDA == 0 then do
statusReg := statusReg { ready = True; busIdle = True; arbitrationLost = True }
state := Idle
txSCL := 1
txSDA := 1
else do
state := StartSDA
txSCL := 1
txSDA := 0
"StartSDA": when StartSDA <- state ==> do
state := SendAddrBitSDA 7
txSCL := 0
txSDA := 0
"SendAddrBitSDA": when SendAddrBitSDA n <- state ==> do
let bit = addrReg[n:n]
state := SendAddrBitRaiseSCL n bit
txSCL := 0
txSDA := bit
"SendAddrBitRaiseSCL": when SendAddrBitRaiseSCL n bit <- state ==> do
state := SendAddrBitWait n bit
txSCL := 1
txSDA := bit
"SendAddrBitWait": when SendAddrBitWait n bit <- state ==> do
state := SendAddrBitLowerSCL n bit
txSCL := 1
txSDA := bit
"SendAddrBitLowerSCL": when SendAddrBitLowerSCL n bit <- state ==> do
state := if n == 0
then GetAddrAckSDA
else SendAddrBitSDA (n - 1)
txSCL := 0
txSDA := bit
"GetAddrAckSDA": when GetAddrAckSDA <- state ==> do
state := GetAddrAckRaiseSCL
txSCL := 0
txSDA := 0
"GetAddrAckRaiseSCL": when GetAddrAckRaiseSCL <- state ==> do
state := GetAddrAckReadSDA
txSCL := 1
txSDA := 1
"GetAddrAckReadSDA": when GetAddrAckReadSDA <- state ==> do
statusReg := statusReg { addrAck = unpack rxSDA }
state := if rxSDA == 1 -- NACK
then TODO
else if addrReg[0:0] == 0 -- R/!W
then TODO -- !W
else ReadLowerSCL 7 1
txSCL := 1
txSDA := 1
"ReadLowerSCL": when ReadLowerSCL n time <- state ==> do
state := if time == 1
then ReadLowerSCL n (time - 1)
else ReadRaiseSCL n
txSCL := 0
txSDA := 1
"ReadRaiseSCL": when ReadRaiseSCL n <- state ==> do
state := ReadReadSDA n
txSCL := 1
txSDA := 1
"ReadReadSDA": when ReadReadSDA n <- state ==> do
dataReg := dataReg [6:0] ++ rxSDA
state := if n == 0
then ReadAckLowerSCL
else ReadLowerSCL (n - 1) 1
txSCL := 1
txSDA := 1
"ReadAckLowerSCL": when ReadAckLowerSCL <- state ==> do
state := ReadAckWriteSDA
txSCL := 0
txSDA := 1
"ReadAckWriteSDA": when ReadAckWriteSDA <- state ==> do
let bit = pack statusReg.dataAck
state := ReadAckRaiseSCL bit 1
txSCL := 0
txSDA := bit
"ReadAckRaiseSCL": when ReadAckRaiseSCL bit time <- state ==> do
state := if time == 1
then ReadAckRaiseSCL bit (time - 1)
else TODO
txSCL := 1
txSDA := bit
return ()
-- | Returns an I²C interface that runs on the current clock. This is not what
-- you want, most likely.
mkI2C :: Bit 1 -> Bit 1 -> Module I2C
mkI2C rxSCL rxSDA = module
txSCL <- mkWire
txSDA <- mkWire
addrReg <- mkReg 0
dataReg <- mkReg 0
statusReg <- mkReg (I2CStatus
{ notBusy = False
; busIdle = False
; addrAck = False
; dataAck = False
; arbitrationLost = False
; rsvd = 0
})
mkI2C' rxSCL rxSDA txSCL txSDA addrReg dataReg statusReg
interface I2C
txSCL = txSCL
txSDA = txSDA
addrReg = addrReg
dataReg = dataReg
statusReg = statusReg
{-
-- | 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
"copy rxSCL and rxSDA": 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 :
|