aboutsummaryrefslogtreecommitdiff
path: root/fpga/src/I2C.bs
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/src/I2C.bs')
-rw-r--r--fpga/src/I2C.bs93
1 files changed, 93 insertions, 0 deletions
diff --git a/fpga/src/I2C.bs b/fpga/src/I2C.bs
index f3cd760..a8553a8 100644
--- a/fpga/src/I2C.bs
+++ b/fpga/src/I2C.bs
@@ -1,3 +1,96 @@
+-- | 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
+ -- | The TX side of the SDA pin. High corresponds to high-impedance.
+ txSDA :: Bit 1
+ -- | Reads a byte from the I²C controller's receive buffer.
+ recv :: Get (Maybe (Bit 8))
+ -- | 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)
+
+data State
+ = -- | The initial state.
+ Idle
+ | -- | TODO
+ TODO
+ deriving (Bits)
+
+-- | Returns an I²C interface that does _not_ have FIFOs.
+mkI2C' :: Bit 1 -> Bit 1 -> Module I2C
+mkI2C' rxSCL rxSDA = module
+ recvFIFO :: Wire (Maybe (Bit 8)) <- mkWire
+ sendFIFO :: Wire I2CCommand <- mkWire
+ state :: Reg State <- mkReg Idle
+ txSCL :: Wire (Bit 1) <- mkWire
+ txSDA :: Wire (Bit 1) <- mkWire
+
+ rules
+ when Idle <- state ==> do
+ txSCL._write 1
+ txSDA._write 1
+
+ interface I2C
+ txSCL = txSCL
+ txSDA = txSDA
+ recv = toGet recvFIFO._read
+ send = toPut sendFIFO._write
+
+-- | 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 (Maybe (Bit 8)) <- 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
+ i2c <- mkI2C' rxSCLSync.read rxSDASync.read
+ rules
+ when True ==> do
+ txSCLSync._write i2c.txSCL
+ txSDASync._write i2c.txSDA
+ when True ==> do
+ byte <- i2c.recv.get
+ recvFIFO.enq byte
+ when True ==> do
+ i2c.send.put sendFIFO.first
+ sendFIFO.deq
+ return ()
+
+ interface I2C
+ txSCL = txSCLSync
+ txSDA = txSDASync
+ recv = toGet recvFIFO
+ send = toPut sendFIFO
+
-- vim: set ft=haskell :