Completed Chisel RTL (not tested yet)
authorAlex Solomatnikov <sols@gamma06.internal.sifive.com>
Wed, 1 Feb 2017 01:20:53 +0000 (17:20 -0800)
committerAlex Solomatnikov <sols@gamma06.internal.sifive.com>
Wed, 1 Feb 2017 01:20:53 +0000 (17:20 -0800)
src/main/scala/devices/i2c/I2C.scala

index e5959c551c2634a39b30d5d03b8fe283cbd7da79..40102ba9e0fe68ff2e99648fd3159889e8dd6c5a 100644 (file)
@@ -3,6 +3,7 @@ package sifive.blocks.devices.i2c
 
 import Chisel._
 import config._
+import util._
 import regmapper._
 import uncore.tilelink2._
 import rocketchip.PeripheryBusConfig
@@ -35,22 +36,477 @@ trait I2CBundle extends Bundle with HasI2CParameters {
 trait I2CModule extends Module with HasI2CParameters with HasRegMap {
   val io: I2CBundle
 
-  val prescaler_lo = Reg(UInt(8.W))  // low byte clock prescaler register
-  val prescaler_hi = Reg(UInt(8.W))  // high byte clock prescaler register
-  val control      = Reg(UInt(8.W))  // control register
-  val data         = Reg(UInt(8.W))  // write: transmit byte, read: receive byte
-  val cmd_status   = Reg(UInt(8.W))  // write: command, read: status
+  val I2C_CMD_NOP   = UInt(0x00)
+  val I2C_CMD_START = UInt(0x01)
+  val I2C_CMD_STOP  = UInt(0x02)
+  val I2C_CMD_WRITE = UInt(0x04)
+  val I2C_CMD_READ  = UInt(0x08)
+
+  class PrescalerBundle extends Bundle{
+    val hi = UInt(8.W)
+    val lo = UInt(8.W)
+  }
+
+  class ControlBundle extends Bundle{
+    val coreEn             = Bool()
+    val intEn              = Bool()
+    val reserved           = UInt(6.W)
+  }
+
+  class CommandBundle extends Bundle{
+    val start              = Bool()
+    val stop               = Bool()
+    val read               = Bool()
+    val write              = Bool()
+    val ack                = Bool()
+    val reserved           = UInt(2.W)
+    val irqAck             = Bool()
+  }
+
+  class StatusBundle extends Bundle{
+    val receivedAck        = Bool()    // received aknowledge from slave
+    val busy               = Bool()
+    val arbLost            = Bool()
+    val reserved           = UInt(3.W)
+    val transferInProgress = Bool()
+    val irqFlag            = Bool()
+  }
+
+  // control state visible to SW/driver
+  val prescaler    = Reg(init = (new PrescalerBundle).fromBits(0xFFFF.U))
+  val control      = Reg(init = (new ControlBundle).fromBits(0.U))
+  val transmitData = Reg(init = UInt(0, 8.W))
+  val receivedData = Reg(init = UInt(0, 8.W))
+  val cmd          = Reg(init = (new CommandBundle).fromBits(0.U))
+  val status       = Reg(init = (new StatusBundle).fromBits(0.U))
+
+
+  //////// Bit level ////////
+
+  io.port.scl.out := false.B                           // i2c clock line output
+  io.port.sda.out := false.B                           // i2c data line output
+
+  // filter SCL and SDA signals; (attempt to) remove glitches
+  val filterCnt = Reg(init = UInt(0, 14.W))
+  when ( !control.coreEn ) {
+    filterCnt := 0.U
+  } .elsewhen (~(filterCnt.orR)) {
+    filterCnt := Cat(prescaler.hi, prescaler.lo) >> 2  //16x I2C bus frequency
+  } .otherwise {
+    filterCnt := filterCnt - 1.U
+  }
+
+  val fSCL      = Reg(init = UInt(0x7, 3.W))
+  val fSDA      = Reg(init = UInt(0x7, 3.W))
+  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 dSCL      = Reg(init = Bool(true), next = sSCL)
+  val dSDA      = Reg(init = Bool(true), 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)
+
+  // master drives SCL high, but another master pulls it low
+  // master start counting down its low cycle now (clock synchronization)
+  val sclSync   = dSCL && !sSCL && io.port.scl.oe
+
+  // 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))
+  slaveWait := (io.port.scl.oe && !dSCLOen && !sSCL) || (slaveWait && !sSCL)
+
+  val clkEn     = Reg(init = Bool(true))     // 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 ) {
+    cnt   := Cat(prescaler.hi, prescaler.lo)
+    clkEn := true.B
+  }
+  .elsewhen (slaveWait) {
+    clkEn := false.B
+  }
+  .otherwise {
+    cnt   := cnt - 1.U
+    clkEn := false.B
+  }
+
+  val sclOen     = Reg(init = Bool(true))
+  io.port.scl.oe := sclOen
+
+  val sdaOen     = Reg(init = Bool(true))
+  io.port.sda.oe := sdaOen
+
+  val sdaChk     = Reg(init = Bool(false))    // check SDA output (Multi-master arbitration)
+
+  val transmitBit = Reg(init = Bool(false))
+  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))
+  when (clkEn) {
+    bitCmdStop := bitCmd === I2C_CMD_STOP
+  }
+  val bitCmdAck   = Reg(init = Bool(false))
+
+  val (s_bit_idle ::
+       s_bit_start_a :: s_bit_start_b :: s_bit_start_c :: s_bit_start_d :: s_bit_start_e ::
+       s_bit_stop_a  :: s_bit_stop_b  :: s_bit_stop_c  :: s_bit_stop_d  ::
+       s_bit_rd_a    :: s_bit_rd_b    :: s_bit_rd_c    :: s_bit_rd_d    ::
+       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))
+
+  // bit FSM
+  when (arbLost) {
+    bitState  := s_bit_idle
+    bitCmdAck := false.B
+    sclOen    := true.B
+    sdaOen    := true.B
+    sdaChk    := false.B
+  }
+  .otherwise {
+    bitCmdAck := false.B
+
+    when (clkEn) {
+      switch (bitState) {
+        is (s_bit_idle) {
+          switch (bitCmd) {
+            is (I2C_CMD_START) { bitState := s_bit_start_a }
+            is (I2C_CMD_STOP)  { bitState := s_bit_stop_a  }
+            is (I2C_CMD_WRITE) { bitState := s_bit_wr_a    }
+            is (I2C_CMD_READ)  { bitState := s_bit_rd_a    }
+          }
+          sdaChk := false.B
+        }
+
+        is (s_bit_start_a) {
+          bitState  := s_bit_start_b
+          sclOen    := sclOen
+          sdaOen    := true.B
+          sdaChk    := false.B
+        }
+        is (s_bit_start_b) {
+          bitState  := s_bit_start_c
+          sclOen    := true.B
+          sdaOen    := true.B
+          sdaChk    := false.B
+        }
+        is (s_bit_start_c) {
+          bitState  := s_bit_start_d
+          sclOen    := true.B
+          sdaOen    := false.B
+          sdaChk    := false.B
+        }
+        is (s_bit_start_d) {
+          bitState  := s_bit_start_e
+          sclOen    := true.B
+          sdaOen    := false.B
+          sdaChk    := false.B
+        }
+        is (s_bit_start_e) {
+          bitState  := s_bit_idle
+          bitCmdAck := true.B
+          sclOen    := false.B
+          sdaOen    := false.B
+          sdaChk    := false.B
+        }
+
+        is (s_bit_stop_a) {
+          bitState  := s_bit_stop_b
+          sclOen    := false.B
+          sdaOen    := false.B
+          sdaChk    := false.B
+        }
+        is (s_bit_stop_b) {
+          bitState  := s_bit_stop_c
+          sclOen    := true.B
+          sdaOen    := false.B
+          sdaChk    := false.B
+        }
+        is (s_bit_stop_c) {
+          bitState  := s_bit_stop_d
+          sclOen    := true.B
+          sdaOen    := false.B
+          sdaChk    := false.B
+        }
+        is (s_bit_stop_d) {
+          bitState  := s_bit_idle
+          bitCmdAck := true.B
+          sclOen    := true.B
+          sdaOen    := true.B
+          sdaChk    := false.B
+        }
+
+        is (s_bit_rd_a) {
+          bitState  := s_bit_rd_b
+          sclOen    := false.B
+          sdaOen    := true.B
+          sdaChk    := false.B
+        }
+        is (s_bit_rd_b) {
+          bitState  := s_bit_rd_c
+          sclOen    := true.B
+          sdaOen    := true.B
+          sdaChk    := false.B
+        }
+        is (s_bit_rd_c) {
+          bitState  := s_bit_rd_d
+          sclOen    := true.B
+          sdaOen    := true.B
+          sdaChk    := false.B
+        }
+        is (s_bit_rd_d) {
+          bitState  := s_bit_idle
+          bitCmdAck := true.B
+          sclOen    := false.B
+          sdaOen    := true.B
+          sdaChk    := false.B
+        }
+
+        is (s_bit_wr_a) {
+          bitState  := s_bit_wr_b
+          sclOen    := false.B
+          sdaOen    := transmitBit
+          sdaChk    := false.B
+        }
+        is (s_bit_wr_b) {
+          bitState  := s_bit_wr_c
+          sclOen    := true.B
+          sdaOen    := transmitBit
+          sdaChk    := false.B
+        }
+        is (s_bit_wr_c) {
+          bitState  := s_bit_wr_d
+          sclOen    := true.B
+          sdaOen    := transmitBit
+          sdaChk    := true.B
+        }
+        is (s_bit_wr_d) {
+          bitState  := s_bit_idle
+          bitCmdAck := true.B
+          sclOen    := false.B
+          sdaOen    := transmitBit
+          sdaChk    := false.B
+        }
+      }
+    }
+  }
+
+
+  //////// 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 bitCnt      = Reg(init = UInt(0, 3.W))
+  when (load) {
+    bitCnt := 0x7.U
+  }
+  .elsewhen (shift) {
+    bitCnt := bitCnt - 1.U
+  }
+  val bitCntDone  = !(bitCnt.orR)
+
+  // receivedData is used as shift register directly
+  when (load) {
+    receivedData := transmitData
+  }
+  .otherwise {
+    receivedData := Cat(receivedData, receivedBit)
+  }
+
+  val (s_byte_idle :: s_byte_start :: s_byte_read :: s_byte_write :: s_byte_ack :: s_byte_stop :: Nil) = Enum(UInt(), 6)
+  val byteState   = Reg(init = s_byte_idle)
+
+  when (arbLost) {
+    bitCmd      := I2C_CMD_NOP
+    transmitBit := false.B
+    shift       := false.B
+    load        := false.B
+    cmdAck      := false.B
+    byteState   := s_byte_idle
+    receivedAck := false.B
+  }
+  .otherwise {
+    transmitBit := receivedData(7)
+    shift       := false.B
+    load        := false.B
+    cmdAck      := false.B
+
+    switch (byteState) {
+      is (s_byte_idle) {
+        when (go) {
+          when (cmd.start) {
+            byteState := s_byte_start
+            bitCmd    := I2C_CMD_START
+          }
+          .elsewhen (cmd.read) {
+            byteState := s_byte_read
+            bitCmd    := I2C_CMD_READ
+          }
+          .elsewhen (cmd.write) {
+            byteState := s_byte_write
+            bitCmd    := I2C_CMD_WRITE
+          }
+          .otherwise { // stop
+            byteState := s_byte_stop
+            bitCmd    := I2C_CMD_STOP
+          }
+
+          load        := true.B
+        }
+      }
+      is (s_byte_start) {
+        when (bitCmdAck) {
+          when (cmd.read) {
+            byteState := s_byte_read
+            bitCmd    := I2C_CMD_READ
+          }
+          .otherwise {
+            byteState := s_byte_write
+            bitCmd    := I2C_CMD_WRITE
+          }
+
+          load        := true.B
+        }
+      }
+      is (s_byte_write) {
+        when (bitCmdAck) {
+          when (bitCntDone) {
+            byteState := s_byte_ack
+            bitCmd    := I2C_CMD_READ
+          }
+          .otherwise {
+            byteState := s_byte_write
+            bitCmd    := I2C_CMD_WRITE
+            shift     := true.B
+          }
+        }
+      }
+      is (s_byte_read) {
+        when (bitCmdAck) {
+          when (bitCntDone) {
+            byteState := s_byte_ack
+            bitCmd    := I2C_CMD_WRITE
+          }
+          .otherwise {
+            byteState := s_byte_read
+            bitCmd    := I2C_CMD_READ
+          }
+
+          shift       := true.B
+          transmitBit := cmd.ack
+        }
+      }
+      is (s_byte_ack) {
+        when (bitCmdAck) {
+          when (cmd.stop) {
+            byteState := s_byte_stop
+            bitCmd    := I2C_CMD_STOP
+          }
+          .otherwise {
+            byteState := s_byte_idle
+            bitCmd    := I2C_CMD_NOP
+
+           // generate command acknowledge signal
+            cmdAck    := true.B
+          }
+
+         // assign ack_out output to bit_controller_rxd (contains last received bit)
+          receivedAck := receivedBit
+
+          transmitBit := true.B
+        }
+        .otherwise {
+          transmitBit := cmd.ack
+        }
+      }
+      is (s_byte_stop) {
+        when (bitCmdAck) {
+          byteState := s_byte_idle
+          bitCmd    := I2C_CMD_NOP
+
+         // assign ack_out output to bit_controller_rxd (contains last received bit)
+          cmdAck    := true.B
+        }
+      }
+    }
+  }
+
+
+  //////// Top level ////////
+
+  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)
+
+  when (stopCond) {
+    status.busy             := false.B
+  }
+  .elsewhen (startCond) {
+    status.busy             := true.B
+  }
+
+  when (arbLost) {
+    status.arbLost          := true.B
+  }
+  .elsewhen (cmd.start) {
+    status.arbLost          := false.B
+  }
+  status.transferInProgress := cmd.read || cmd.write
+  status.irqFlag            := (cmdAck || arbLost || status.irqFlag) && !cmd.irqAck
+
+  // hack
+  val nextCmd = Wire(UInt(8.W))
+  nextCmd := cmd.asUInt
+  cmd := (new CommandBundle).fromBits(nextCmd)
 
   // Note that these are out of order.
   regmap(
-    I2CCtrlRegs.prescaler_lo -> Seq(RegField(8, prescaler_lo)),
-    I2CCtrlRegs.prescaler_hi -> Seq(RegField(8, prescaler_hi)),
-    I2CCtrlRegs.control      -> Seq(RegField(8, control)),
-    I2CCtrlRegs.data         -> Seq(RegField(8, data)),
-    I2CCtrlRegs.cmd_status   -> Seq(RegField(8, cmd_status))
+    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)))
   )
+
+  // tie off unused bits
+  control.reserved := 0.U
+  cmd.reserved     := 0.U
+  status.reserved  := 0.U
+
+  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)(