aboutsummaryrefslogtreecommitdiff
path: root/fpga/src/I2C.bs
diff options
context:
space:
mode:
authorNathan Ringo <nathan@remexre.com>2024-11-02 00:08:29 -0500
committerNathan Ringo <nathan@remexre.com>2024-11-02 00:08:29 -0500
commit2a506c1476c55d9ecf990bd4878169278038f6ff (patch)
tree2d618474d89d0b90fe0166e30ba96268e94a2d1e /fpga/src/I2C.bs
parentd3bcde4eedd2dd3dcf5cbc4302821f38ff553498 (diff)
...
Diffstat (limited to 'fpga/src/I2C.bs')
-rw-r--r--fpga/src/I2C.bs105
1 files changed, 72 insertions, 33 deletions
diff --git a/fpga/src/I2C.bs b/fpga/src/I2C.bs
index 2a0614f..e092853 100644
--- a/fpga/src/I2C.bs
+++ b/fpga/src/I2C.bs
@@ -45,15 +45,24 @@ interface I2C =
-- | 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
+ -- | Reads from the address register, which contains the address in the high
+ -- 7 bits and the R/!W bit in the low bit.
+ addrRegGet :: Bit 8
+ -- | Writes to the address register, which contains the address in the high
+ -- 7 bits and the R/!W bit in the low bit.
+ addrRegPut :: Bit 8 -> Action
+ -- | Reads from 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.
+ dataRegGet :: Bit 8
+ -- | Writes to 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.
+ dataRegPut :: Bit 8 -> Action
+ -- | Reads from the status register.
+ statusRegGet :: I2CStatus
+ -- | Writes to the status register.
+ statusRegPut :: I2CStatus -> Action
data I2CBusState
= -- | (We believe) the bus is idle.
@@ -94,6 +103,8 @@ data I2CBusState
| -- | TODO
WriteAckReadSDA
| -- | TODO
+ WriteAckLowerSCL2
+ | -- | TODO
TODO
| -- | We're about to either lower SCL or keep it low as part of reading a
-- byte from a device.
@@ -144,14 +155,14 @@ mkI2C' rxSCL rxSDA txSCL txSDA addrReg dataReg statusReg = module
txSDA := 1
else do
state := StartSDA
+ savedAddr := addrReg
+ savedData := if savedAddr[0:0] == 1 -- R/!W
+ then 0 -- R
+ else dataReg -- !W
txSCL := 1
txSDA := 0
"StartSDA": when StartSDA <- state ==> do
state := SendAddrBitSDA 7
- savedAddr := addrReg
- savedData := if savedAddr[0:0] == 1 -- R/!W
- then 0 -- R
- else dataReg -- !W
txSCL := 0
txSDA := 0
"SendAddrBitSDA": when SendAddrBitSDA n <- state ==> do
@@ -223,9 +234,20 @@ mkI2C' rxSCL rxSDA txSCL txSDA addrReg dataReg statusReg = module
statusReg := statusReg { ready = True; dataAckBit = unpack rxSDA }
state := if rxSDA == 1 -- NACK
then StopLowerSCL
- else TODO
+ else WriteAckLowerSCL2
txSCL := 1
txSDA := 1
+ "WriteAckLowerSCL2": when WriteAckLowerSCL2 <- state ==> do
+ if not statusReg.ready && addrReg == savedAddr then do
+ state := WriteWriteSDA 7
+ savedAddr := addrReg
+ savedData := if savedAddr[0:0] == 1 -- R/!W
+ then 0 -- R
+ else dataReg -- !W
+ else do
+ state := StopLowerSDA
+ txSCL := 0
+ txSDA := 1
"ReadLowerSCL": when ReadLowerSCL n time <- state ==> do
state := if time == 1
then ReadLowerSCL n (time - 1)
@@ -302,14 +324,17 @@ mkI2C rxSCL rxSDA = module
interface I2C
txSCL = txSCL
txSDA = txSDA
- addrReg = addrReg
- dataReg = dataReg
- statusReg = statusReg
+ addrRegGet = addrReg
+ addrRegPut value = addrReg := value
+ dataRegGet = dataReg
+ dataRegPut value = dataReg := value
+ statusRegGet = statusReg
+ statusRegPut value = statusReg := value
{-
--- | 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.
+-- | Returns an I²C interface that runs on a clock derived from the current
+-- clock divided by `divisor`. This should run at four times the rate of the
+-- I²C bus's clock rate.
mkDividedI2C :: Integer -> Bit 1 -> Bit 1 -> Module I2C
mkDividedI2C divisor rxSCL rxSDA = module
clock <- mkClockDivider divisor
@@ -317,23 +342,37 @@ mkDividedI2C divisor rxSCL rxSDA = module
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
+ txSCLSync :: SyncPulseIfc <- mkSyncPulseToCC clock.slowClock reset
+ txSDASync :: SyncPulseIfc <- mkSyncPulseToCC clock.slowClock reset
+ addrReg <- mkReg 0
+ dataReg <- mkReg 0
+ statusReg <- mkReg (I2CStatus
+ { ready = True
+ ; busIdle = True
+ ; addrAckBit = False
+ ; dataAckBit = False
+ ; arbitrationLost = False
+ ; rsvd = 0
+ })
+
+ txSCL <- mkWire
+ txSDA <- mkWire
changeSpecialWires (Just clock.slowClock) (Just reset) Nothing $ module
- mkI2C' rxSCLSync.read rxSDASync.read txSCLSync txSDASync sendFIFO recvFIFO
+ mkI2C' rxSCL rxSDA txSCL txSDA addrReg dataReg statusReg
+ rules
+ when unpack txSCL ==> txSCLSync.send
+ when unpack txSDA ==> txSDASync.send
interface I2C
- txSCL = txSCLSync
- txSDA = txSDASync
- recv = toGet recvFIFO
- send = toPut sendFIFO
+ txSCL = pack txSCLSync.pulse
+ txSDA = pack txSDASync.pulse
+ addrRegGet = 0
+ addrRegPut value = undefined
+ dataRegGet = 0
+ dataRegPut value = undefined
+ statusRegGet = undefined
+ statusRegPut value = undefined
-}
-- vim: set ft=haskell :