diff options
Diffstat (limited to 'fpga/src')
-rw-r--r-- | fpga/src/I2C.bs | 105 | ||||
-rw-r--r-- | fpga/src/Numini.bs | 2 | ||||
-rw-r--r-- | fpga/src/Top.bs | 155 | ||||
-rw-r--r-- | fpga/src/TopSim.bs | 75 |
4 files changed, 282 insertions, 55 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 : diff --git a/fpga/src/Numini.bs b/fpga/src/Numini.bs index cb26d5e..b5e0223 100644 --- a/fpga/src/Numini.bs +++ b/fpga/src/Numini.bs @@ -46,7 +46,7 @@ mkNumini ch559_uart_rx inkplate_uart_rx usb_uart_rx hyperbus_rwds_in inkplate_uart <- mkDividedUart (clockFreqHz / 2_000_000) inkplate_uart_rx usb_uart <- mkDividedUart (clockFreqHz / 9_600) usb_uart_rx hyperbus <- mkDividedHyperBus (clockFreqHz / 3_000_000) hyperbus_rwds_in hyperbus_dq_in - i2c <- mkDividedI2C (clockFreqHz / 200_000) i2c_scl_in i2c_sda_in + i2c <- mkDividedI2C (clockFreqHz / 400_000) i2c_scl_in i2c_sda_in mkConnection usb_uart.send usb_uart.recv diff --git a/fpga/src/Top.bs b/fpga/src/Top.bs index 65baf8a..df5098f 100644 --- a/fpga/src/Top.bs +++ b/fpga/src/Top.bs @@ -1,7 +1,8 @@ -- | The top-level module, for the iCEBreaker. package Top where -import Numini +import I2C +-- import Numini import TriState -- | The interface to the iCEBreaker. @@ -81,6 +82,157 @@ mkTop = module i2c_scl_in :: Wire (Bit 1) <- mkWire i2c_sda_in :: Wire (Bit 1) <- mkWire + -- i2c <- mkDividedI2C (clockFreqHz / 400_000) i2c_scl_in i2c_sda_in + i2c <- mkI2C i2c_scl_in i2c_sda_in + + -- Provide commands. + state :: Reg (Bit 4) <- mkReg 0 + rules + when state == 0, i2c.statusRegGet.ready ==> do + i2c.addrRegPut (0x20 ++ (0 :: Bit 1)) + i2c.dataRegPut 0x00 + i2c.statusRegPut (i2c.statusRegGet { ready = False }) + state := 1 + when state == 1 ==> do + i2c.addrRegPut (0x20 ++ (0 :: Bit 1)) + i2c.dataRegPut 0x00 + state := 2 + when state == 2, i2c.statusRegGet.ready ==> do + i2c.statusRegPut (i2c.statusRegGet { ready = False }) + state := 3 + when state == 3 ==> do + i2c.addrRegPut (0x20 ++ (0 :: Bit 1)) + i2c.dataRegPut 0x00 + state := 4 + when state == 4, i2c.statusRegGet.ready ==> do + i2c.statusRegPut (i2c.statusRegGet { ready = False }) + state := 5 + when state == 5 ==> do + i2c.addrRegPut (0x20 ++ (0 :: Bit 1)) + i2c.dataRegPut 0x12 + state := 6 + when state == 6, i2c.statusRegGet.busIdle ==> do + i2c.statusRegPut (i2c.statusRegGet { ready = False }) + state := 7 + when state == 7 ==> do + i2c.addrRegPut (0x20 ++ (0 :: Bit 1)) + i2c.dataRegPut 0xaa + state := 8 + when state == 8, i2c.statusRegGet.ready ==> do + i2c.statusRegPut (i2c.statusRegGet { ready = False }) + state := 9 + when state == 9 ==> do + i2c.addrRegPut (0x20 ++ (0 :: Bit 1)) + i2c.dataRegPut 0xaa + state := 10 + when state == 10, i2c.statusRegGet.ready ==> do + i2c.statusRegPut (i2c.statusRegGet { ready = False }) + state := 11 + + -- Wire up the tristates. + rules + "update_hyperbus_rwds_out": when True ==> do + hyperbus_rwds_enable := False + hyperbus_rwds_out := 0 + "update_hyperbus_rwds_in": when True ==> do + hyperbus_rwds_in := hyperbus_rwds._read + "update_hyperbus_dq_out": when True ==> do + hyperbus_dq_enable := False + hyperbus_dq_out := 0 + "update_hyperbus_dq_in": when True ==> do + hyperbus_dq_in := hyperbus_dq7._read + ++ hyperbus_dq6._read + ++ hyperbus_dq5._read + ++ hyperbus_dq4._read + ++ hyperbus_dq3._read + ++ hyperbus_dq2._read + ++ hyperbus_dq1._read + ++ hyperbus_dq0._read + "update_i2c_scl_out": when True ==> do + i2c_scl_enable := unpack i2c.txSCL + "update_i2c_scl_in": when True ==> do + i2c_scl_in := i2c_scl._read + "update_i2c_sda_out": when True ==> do + i2c_sda_enable := unpack i2c.txSDA + "update_i2c_sda_in": when True ==> do + i2c_sda_in := i2c_sda._read + + interface Top + -- RS232 + usb_uart_rx bit = usb_uart_rx := bit + usb_uart_tx = 0 + -- Onboard LEDs + led_r_n = 1 + led_g_n = 1 + -- RGB LED driver + rgb_r_n = 1 + rgb_g_n = 1 + rgb_b_n = 1 + -- HyperBus 1 (PMOD 1A) + hyperbus_cs2_n = 1 + hyperbus_cs0_n = 1 + hyperbus_ck = 0 + hyperbus_ck_n = 1 + hyperbus_cs3_n = 1 + hyperbus_cs1_n = 1 + hyperbus_reset_n = 1 + hyperbus_rwds = hyperbus_rwds.io + -- HyperBus 2 (PMOD 1B) + hyperbus_dq0 = hyperbus_dq0.io + hyperbus_dq1 = hyperbus_dq1.io + hyperbus_dq2 = hyperbus_dq2.io + hyperbus_dq3 = hyperbus_dq3.io + hyperbus_dq4 = hyperbus_dq4.io + hyperbus_dq5 = hyperbus_dq5.io + hyperbus_dq6 = hyperbus_dq6.io + hyperbus_dq7 = hyperbus_dq7.io + -- LEDs and buttons (PMOD 2) + ch559_uart_rx bit = ch559_uart_rx := bit + ch559_uart_tx = 0 + inkplate_uart_rx bit = inkplate_uart_rx := bit + inkplate_uart_tx = 0 + todo_btn _ = noAction + todo_led = 0 + i2c_scl = i2c_scl.io + i2c_sda = i2c_sda.io +{-# verilog mkTop #-} +{-# properties mkTop = { RSTN = BTN_N } #-} + +{- +mkTop :: Module Top +mkTop = module + ch559_uart_rx <- mkWire + inkplate_uart_rx <- mkWire + usb_uart_rx <- mkWire + + -- Make tristates for the HyperBus inouts. + hyperbus_rwds_enable :: Reg Bool <- mkReg False + hyperbus_rwds_out :: Reg (Bit 1) <- mkReg 0 + hyperbus_dq_enable :: Reg Bool <- mkReg False + hyperbus_dq_out :: Reg (Bit 8) <- mkReg 0 + hyperbus_rwds <- mkTriState hyperbus_rwds_enable hyperbus_rwds_out + hyperbus_dq0 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[0:0] + hyperbus_dq1 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[1:1] + hyperbus_dq2 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[2:2] + hyperbus_dq3 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[3:3] + hyperbus_dq4 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[4:4] + hyperbus_dq5 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[5:5] + hyperbus_dq6 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[6:6] + hyperbus_dq7 <- mkTriState hyperbus_dq_enable hyperbus_dq_out[7:7] + + -- Make a tristate for the I2C inouts. Note that we never drive the line + -- high -- I2C uses a high-impedance state instead of high. + i2c_scl_enable <- mkReg False + i2c_scl <- mkTriState i2c_scl_enable 0 + i2c_sda_enable <- mkReg False + i2c_sda <- mkTriState i2c_sda_enable 0 + + -- Make wires for all the inout inputs. + hyperbus_rwds_in :: Wire (Bit 1) <- mkWire + hyperbus_dq_in :: Wire (Bit 8) <- mkWire + i2c_scl_in :: Wire (Bit 1) <- mkWire + i2c_sda_in :: Wire (Bit 1) <- mkWire + numini <- mkNumini ch559_uart_rx inkplate_uart_rx usb_uart_rx hyperbus_rwds_in hyperbus_dq_in i2c_scl_in i2c_sda_in @@ -162,5 +314,6 @@ mkTop = module i2c_sda = i2c_sda.io {-# verilog mkTop #-} {-# properties mkTop = { RSTN = BTN_N } #-} +-} -- vim: set ft=haskell : diff --git a/fpga/src/TopSim.bs b/fpga/src/TopSim.bs index e4e65c9..2a58a83 100644 --- a/fpga/src/TopSim.bs +++ b/fpga/src/TopSim.bs @@ -29,28 +29,63 @@ mkTopSim = module when True ==> txSCL := i2c.txSCL when True ==> txSDA := i2c.txSDA - timer :: Reg (Bit 16) <- mkReg 0 + -- Provide commands. + state :: Reg (Bit 4) <- mkReg 0 rules - "t0000": when (timer == 0x0000) ==> do - i2c.addrReg := 0x20 ++ (0 :: Bit 1) - i2c.dataReg := 0x12 - "t0001": when (timer == 0x0001) ==> do - i2c.statusReg := i2c.statusReg { ready = False; dataAckBit = True } - "t0002": when (timer == 0x0002) ==> do - i2c.addrReg := 0x20 ++ (0 :: Bit 1) - i2c.dataReg := 0xaa - "t0022": when (timer == 0x0023) ==> rxSDA := 0 - "t0025": when (timer == 0x0027) ==> rxSDA := 1 - -- "t0047": when (timer == 0x0047) ==> rxSDA := 0 - -- "t0051": when (timer == 0x004b) ==> rxSDA := 1 - -- "t004b": when (timer == 0x004b) ==> do - -- i2c.statusReg := i2c.statusReg { ready = False } - "advance timer": when True ==> timer := timer + 1 - "finish": when (timer == 0x00ff) ==> $finish + when state == 0, i2c.statusRegGet.ready ==> do + i2c.addrRegPut (0x20 ++ (0 :: Bit 1)) + i2c.dataRegPut 0x01 + i2c.statusRegPut (i2c.statusRegGet { ready = False }) + state := 1 + when state == 1 ==> do + i2c.addrRegPut (0x20 ++ (0 :: Bit 1)) + i2c.dataRegPut 0x02 + state := 2 + when state == 2, i2c.statusRegGet.ready ==> do + i2c.statusRegPut (i2c.statusRegGet { ready = False }) + state := 3 {- - "log received values": when True ==> do - result <- i2c.recv.get - $display "recv: " (fshow result) + when state == 3 ==> do + i2c.addrRegPut (0x20 ++ (0 :: Bit 1)) + i2c.dataRegPut 0x03 + state := 4 + when state == 4, i2c.statusRegGet.ready ==> do + i2c.statusRegPut (i2c.statusRegGet { ready = False }) + state := 5 + when state == 5 ==> do + i2c.addrRegPut (0x20 ++ (0 :: Bit 1)) + i2c.dataRegPut 0x12 + state := 6 + when state == 6, i2c.statusRegGet.busIdle ==> do + i2c.statusRegPut (i2c.statusRegGet { ready = False }) + state := 7 + when state == 7 ==> do + i2c.addrRegPut (0x20 ++ (0 :: Bit 1)) + i2c.dataRegPut 0xaa + state := 8 + when state == 8, i2c.statusRegGet.ready ==> do + i2c.statusRegPut (i2c.statusRegGet { ready = False }) + state := 9 + when state == 9 ==> do + i2c.addrRegPut (0x20 ++ (0 :: Bit 1)) + i2c.dataRegPut 0xaa + state := 10 + when state == 10, i2c.statusRegGet.ready ==> do + i2c.statusRegPut (i2c.statusRegGet { ready = False }) + state := 11 -} + timer :: Reg (Bit 16) <- mkReg 0 + rules + "t0022": when (timer == 0x0022) ==> rxSDA := 0 + "t0026": when (timer == 0x0026) ==> rxSDA := 1 + "t0046": when (timer == 0x0046) ==> rxSDA := 0 + "t004a": when (timer == 0x004a) ==> rxSDA := 1 + "t006a": when (timer == 0x006a) ==> rxSDA := 0 + "t006e": when (timer == 0x006e) ==> rxSDA := 1 + "t008e": when (timer == 0x008e) ==> rxSDA := 0 + "t0092": when (timer == 0x0092) ==> rxSDA := 1 + "advance timer": when True ==> timer := timer + 1 + "finish": when (timer == 0x01ff) ==> $finish + -- vim: set ft=haskell : |