From 2a506c1476c55d9ecf990bd4878169278038f6ff Mon Sep 17 00:00:00 2001 From: Nathan Ringo Date: Sat, 2 Nov 2024 00:08:29 -0500 Subject: ... --- fpga/src/I2C.bs | 105 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 72 insertions(+), 33 deletions(-) (limited to 'fpga/src/I2C.bs') 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. @@ -93,6 +102,8 @@ data I2CBusState WriteAckRaiseSCL | -- | TODO WriteAckReadSDA + | -- | TODO + WriteAckLowerSCL2 | -- | TODO TODO | -- | We're about to either lower SCL or keep it low as part of reading a @@ -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 : -- cgit v1.2.3