Bug fix: arbLost should be asserted when bitState =/= s_bit_idle (#49)
[sifive-blocks.git] / src / main / scala / devices / i2c / I2C.scala
index 40102ba9e0fe68ff2e99648fd3159889e8dd6c5a..b3e2db5f7cb0d88556bcf027ed7f1aee5034b0ee 100644 (file)
@@ -1,22 +1,54 @@
+/////////////////////////////////////////////////////////////////////
+////                                                             ////
+////  WISHBONE revB.2 compliant I2C Master controller Top-level  ////
+////                                                             ////
+////                                                             ////
+////  Author: Richard Herveille                                  ////
+////          richard@asics.ws                                   ////
+////          www.asics.ws                                       ////
+////                                                             ////
+////  Downloaded from: http://www.opencores.org/projects/i2c/    ////
+////                                                             ////
+/////////////////////////////////////////////////////////////////////
+////                                                             ////
+//// Copyright (C) 2001 Richard Herveille                        ////
+////                    richard@asics.ws                         ////
+////                                                             ////
+//// This source file may be used and distributed without        ////
+//// restriction provided that this copyright statement is not   ////
+//// removed from the file and that any derivative work contains ////
+//// the original copyright notice and the associated disclaimer.////
+////                                                             ////
+////     THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY     ////
+//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   ////
+//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS   ////
+//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR      ////
+//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,         ////
+//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    ////
+//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE   ////
+//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR        ////
+//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  ////
+//// LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT  ////
+//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  ////
+//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE         ////
+//// POSSIBILITY OF SUCH DAMAGE.                                 ////
+////                                                             ////
+/////////////////////////////////////////////////////////////////////
+
+// This code was re-written in Chisel by SiFive, Inc.
 // See LICENSE for license details.
+// WISHBONE interface replaced by Tilelink2
+
 package sifive.blocks.devices.i2c
 
 import Chisel._
-import config._
-import util._
-import regmapper._
-import uncore.tilelink2._
-import rocketchip.PeripheryBusConfig
-import util.AsyncResetRegVec
-import sifive.blocks.devices.gpio.{GPIOPinCtrl}
-
-case class I2CConfig(address: BigInt)
-
-trait HasI2CParameters {
-  implicit val p: Parameters
-  val params: I2CConfig
-  val c = params
-}
+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)
 
 class I2CPin extends Bundle {
   val in  = Bool(INPUT)
@@ -29,12 +61,13 @@ class I2CPort extends Bundle {
   val sda = new I2CPin
 }
 
-trait I2CBundle extends Bundle with HasI2CParameters {
+trait HasI2CBundleContents extends Bundle {
   val port = new I2CPort
 }
 
-trait I2CModule extends Module with HasI2CParameters with HasRegMap {
-  val io: I2CBundle
+trait HasI2CModuleContents extends MultiIOModule with HasRegMap {
+  val io: HasI2CBundleContents
+  val params: I2CParams
 
   val I2C_CMD_NOP   = UInt(0x00)
   val I2C_CMD_START = UInt(0x01)
@@ -90,7 +123,7 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap {
   val filterCnt = Reg(init = UInt(0, 14.W))
   when ( !control.coreEn ) {
     filterCnt := 0.U
-  } .elsewhen (~(filterCnt.orR)) {
+  } .elsewhen (!(filterCnt.orR)) {
     filterCnt := Cat(prescaler.hi, prescaler.lo) >> 2  //16x I2C bus frequency
   } .otherwise {
     filterCnt := filterCnt - 1.U
@@ -98,23 +131,23 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap {
 
   val fSCL      = Reg(init = UInt(0x7, 3.W))
   val fSDA      = Reg(init = UInt(0x7, 3.W))
-  when (~(filterCnt.orR)) {
+  when (!(filterCnt.orR)) {
     fSCL := Cat(fSCL, io.port.scl.in)
     fSDA := Cat(fSDA, io.port.sda.in)
   }
 
-  val sSCL      = Reg(init = Bool(true), next = (new Majority(fSCL.toBools.toSet)).out)
-  val sSDA      = Reg(init = Bool(true), next = (new Majority(fSDA.toBools.toSet)).out)
+  val sSCL      = Reg(init = true.B, next = Majority(fSCL))
+  val sSDA      = Reg(init = true.B, next = Majority(fSDA))
 
-  val dSCL      = Reg(init = Bool(true), next = sSCL)
-  val dSDA      = Reg(init = Bool(true), next = sSDA)
+  val dSCL      = Reg(init = true.B, next = sSCL)
+  val dSDA      = Reg(init = true.B, next = sSDA)
 
   val dSCLOen   = Reg(next = io.port.scl.oe) // delayed scl_oen
 
   // detect start condition => detect falling edge on SDA while SCL is high
   // detect stop  condition => detect rising  edge on SDA while SCL is high
-  val startCond = Reg(init = Bool(false), next = !sSDA &&  dSDA && sSCL)
-  val stopCond  = Reg(init = Bool(false), next =  sSDA && !dSDA && sSCL)
+  val startCond = Reg(init = false.B, next = !sSDA &&  dSDA && sSCL)
+  val stopCond  = Reg(init = false.B, next =  sSDA && !dSDA && sSCL)
 
   // master drives SCL high, but another master pulls it low
   // master start counting down its low cycle now (clock synchronization)
@@ -122,14 +155,14 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap {
 
   // slave_wait is asserted when master wants to drive SCL high, but the slave pulls it low
   // slave_wait remains asserted until the slave releases SCL
-  val slaveWait = Reg(init = Bool(false))
+  val slaveWait = Reg(init = false.B)
   slaveWait := (io.port.scl.oe && !dSCLOen && !sSCL) || (slaveWait && !sSCL)
 
-  val clkEn     = Reg(init = Bool(true))     // clock generation signals
+  val clkEn     = Reg(init = true.B)     // clock generation signals
   val cnt       = Reg(init = UInt(0, 16.W))  // clock divider counter (synthesis)
 
   // generate clk enable signal
-  when (~(cnt.orR) || !control.coreEn || sclSync ) {
+  when (!(cnt.orR) || !control.coreEn || sclSync ) {
     cnt   := Cat(prescaler.hi, prescaler.lo)
     clkEn := true.B
   }
@@ -141,26 +174,26 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap {
     clkEn := false.B
   }
 
-  val sclOen     = Reg(init = Bool(true))
-  io.port.scl.oe := sclOen
+  val sclOen     = Reg(init = true.B)
+  io.port.scl.oe := !sclOen
 
-  val sdaOen     = Reg(init = Bool(true))
-  io.port.sda.oe := sdaOen
+  val sdaOen     = Reg(init = true.B)
+  io.port.sda.oe := !sdaOen
 
-  val sdaChk     = Reg(init = Bool(false))    // check SDA output (Multi-master arbitration)
+  val sdaChk     = Reg(init = false.B)       // check SDA output (Multi-master arbitration)
 
-  val transmitBit = Reg(init = Bool(false))
+  val transmitBit = Reg(init = false.B)
   val receivedBit = Reg(Bool())
   when (sSCL && !dSCL) {
     receivedBit := sSDA
   }
 
   val bitCmd      = Reg(init = UInt(0, 4.W)) // command (from byte controller)
-  val bitCmdStop  = Reg(init = Bool(false))
+  val bitCmdStop  = Reg(init = false.B)
   when (clkEn) {
     bitCmdStop := bitCmd === I2C_CMD_STOP
   }
-  val bitCmdAck   = Reg(init = Bool(false))
+  val bitCmdAck   = Reg(init = false.B)
 
   val (s_bit_idle ::
        s_bit_start_a :: s_bit_start_b :: s_bit_start_c :: s_bit_start_d :: s_bit_start_e ::
@@ -169,7 +202,7 @@ trait I2CModule extends Module with HasI2CParameters 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 = Bool(false), 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) {
@@ -309,11 +342,11 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap {
 
 
   //////// Byte level ///////
-  val load        = Reg(init = Bool(false))                      // load shift register
-  val shift       = Reg(init = Bool(false))                      // shift shift register
-  val cmdAck      = Reg(init = Bool(false))                      // also done
-  val receivedAck = Reg(init = Bool(false))                      // from I2C slave
-  val go          = (cmd.read | cmd.write | cmd.stop) & ~cmdAck  // CHECK: why stop instead of start?
+  val load        = Reg(init = false.B)                         // load shift register
+  val shift       = Reg(init = false.B)                         // shift shift register
+  val cmdAck      = Reg(init = false.B)                         // also done
+  val receivedAck = Reg(init = false.B)                         // from I2C slave
+  val go          = (cmd.read | cmd.write | cmd.stop) & !cmdAck
 
   val bitCnt      = Reg(init = UInt(0, 3.W))
   when (load) {
@@ -328,7 +361,7 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap {
   when (load) {
     receivedData := transmitData
   }
-  .otherwise {
+  .elsewhen (shift) {
     receivedData := Cat(receivedData, receivedBit)
   }
 
@@ -453,14 +486,20 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap {
 
   //////// Top level ////////
 
+  // hack: b/c the same register offset is used to write cmd and read status
+  val nextCmd = Wire(UInt(8.W))
+  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) {
     status.busy             := false.B
   }
@@ -475,20 +514,34 @@ trait I2CModule extends Module with HasI2CParameters 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
 
-  // hack
-  val nextCmd = Wire(UInt(8.W))
-  nextCmd := cmd.asUInt
-  cmd := (new CommandBundle).fromBits(nextCmd)
 
-  // Note that these are out of order.
+  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
@@ -499,16 +552,8 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap {
   interrupts(0) := status.irqFlag & control.intEn
 }
 
-// Copied from UART.scala
-class Majority(in: Set[Bool]) {
-  private val n = (in.size >> 1) + 1
-  private val clauses = in.subsets(n).map(_.reduce(_ && _))
-  val out = clauses.reduce(_ || _)
-}
-
-
 // Magic TL2 Incantation to create a TL2 Slave
-class TLI2C(c: I2CConfig)(implicit p: Parameters)
-  extends TLRegisterRouter(c.address, interrupts = 1, beatBytes = p(PeripheryBusConfig).beatBytes)(
-  new TLRegBundle(c, _)    with I2CBundle)(
-  new TLRegModule(c, _, _) with I2CModule)
+class TLI2C(w: Int, c: I2CParams)(implicit p: Parameters)
+  extends TLRegisterRouter(c.address, "i2c", Seq("sifive,i2c0"), interrupts = 1, beatBytes = w)(
+  new TLRegBundle(c, _)    with HasI2CBundleContents)(
+  new TLRegModule(c, _, _) with HasI2CModuleContents)