Bug fix: arbLost should be asserted when bitState =/= s_bit_idle (#49)
[sifive-blocks.git] / src / main / scala / devices / i2c / I2C.scala
index 7b9fad8897e28521702c017bb29a69e9067d7fde..b3e2db5f7cb0d88556bcf027ed7f1aee5034b0ee 100644 (file)
 package sifive.blocks.devices.i2c
 
 import Chisel._
-import config._
-import regmapper._
-import uncore.tilelink2._
-import util.{AsyncResetRegVec, Majority}
-import sifive.blocks.devices.gpio.{GPIOPinCtrl}
+import chisel3.experimental.MultiIOModule
+import freechips.rocketchip.config._
+import freechips.rocketchip.regmapper._
+import freechips.rocketchip.tilelink._
+import freechips.rocketchip.util.{AsyncResetRegVec, Majority}
 
 case class I2CParams(address: BigInt)
 
@@ -65,7 +65,7 @@ trait HasI2CBundleContents extends Bundle {
   val port = new I2CPort
 }
 
-trait HasI2CModuleContents extends Module with HasRegMap {
+trait HasI2CModuleContents extends MultiIOModule with HasRegMap {
   val io: HasI2CBundleContents
   val params: I2CParams
 
@@ -202,7 +202,7 @@ trait HasI2CModuleContents extends Module with HasRegMap {
        s_bit_wr_a    :: s_bit_wr_b    :: s_bit_wr_c    :: s_bit_wr_d    :: Nil) = Enum(UInt(), 18)
   val bitState    = Reg(init = s_bit_idle)
 
-  val arbLost     = Reg(init = false.B, next = (sdaChk && !sSDA && sdaOen) | ((bitState === s_bit_idle) && stopCond && !bitCmdStop))
+  val arbLost     = Reg(init = false.B, next = (sdaChk && !sSDA && sdaOen) | ((bitState =/= s_bit_idle) && stopCond && !bitCmdStop))
 
   // bit FSM
   when (arbLost) {
@@ -488,16 +488,16 @@ trait HasI2CModuleContents extends Module with HasRegMap {
 
   // hack: b/c the same register offset is used to write cmd and read status
   val nextCmd = Wire(UInt(8.W))
-  nextCmd := cmd.asUInt
   cmd := (new CommandBundle).fromBits(nextCmd)
+  nextCmd := cmd.asUInt & 0xFE.U  // clear IRQ_ACK bit (essentially 1 cycle pulse b/c it is overwritten by regmap below)
 
+  // Note: This wins over the regmap update of nextCmd (even if something tries to write them to 1, these values take priority).
   when (cmdAck || arbLost) {
     cmd.start := false.B    // clear command bits when done
     cmd.stop  := false.B    // or when aribitration lost
     cmd.read  := false.B
     cmd.write := false.B
   }
-  cmd.irqAck  := false.B    // clear IRQ_ACK bit (essentially 1 cycle pulse b/c it is overwritten by regmap below)
 
   status.receivedAck := receivedAck
   when (stopCond) {
@@ -514,15 +514,34 @@ trait HasI2CModuleContents extends Module with HasRegMap {
     status.arbLost          := false.B
   }
   status.transferInProgress := cmd.read || cmd.write
-  status.irqFlag            := (cmdAck || arbLost || status.irqFlag) && !cmd.irqAck
+  status.irqFlag            := (cmdAck || arbLost || status.irqFlag) && !cmd.irqAck // interrupt request flag is always generated
 
 
+  val statusReadReady = Reg(init = true.B)
+  when (cmdAck || arbLost) {    // => cmd.read or cmd.write deassert 1 cycle later => transferInProgress deassert 2 cycles later
+    statusReadReady := false.B  // do not allow status read if status.transferInProgress is going to change
+  }
+  .elsewhen (!statusReadReady) {
+    statusReadReady := true.B
+  }
+
+  // statusReadReady,
   regmap(
     I2CCtrlRegs.prescaler_lo -> Seq(RegField(8, prescaler.lo)),
     I2CCtrlRegs.prescaler_hi -> Seq(RegField(8, prescaler.hi)),
     I2CCtrlRegs.control      -> control.elements.map{ case(name, e) => RegField(e.getWidth, e.asInstanceOf[UInt]) }.toSeq,
     I2CCtrlRegs.data         -> Seq(RegField(8, r = RegReadFn(receivedData),  w = RegWriteFn(transmitData))),
-    I2CCtrlRegs.cmd_status   -> Seq(RegField(8, r = RegReadFn(status.asUInt), w = RegWriteFn(nextCmd)))
+    I2CCtrlRegs.cmd_status   -> Seq(RegField(8, r = RegReadFn{ ready =>
+                                                               (statusReadReady, status.asUInt)
+                                                             },
+                                                w = RegWriteFn((valid, data) => {
+                                                               when (valid) {
+                                                                 statusReadReady := false.B
+                                                                 nextCmd := data
+                                                             }
+                                                             true.B
+                                                }
+                                                )))
   )
 
   // tie off unused bits