From 2cc1012fa2940840e071359215746cddb14c7aa2 Mon Sep 17 00:00:00 2001 From: Alex Solomatnikov Date: Tue, 31 Jan 2017 17:20:53 -0800 Subject: [PATCH] Completed Chisel RTL (not tested yet) --- src/main/scala/devices/i2c/I2C.scala | 476 ++++++++++++++++++++++++++- 1 file changed, 466 insertions(+), 10 deletions(-) diff --git a/src/main/scala/devices/i2c/I2C.scala b/src/main/scala/devices/i2c/I2C.scala index e5959c5..40102ba 100644 --- a/src/main/scala/devices/i2c/I2C.scala +++ b/src/main/scala/devices/i2c/I2C.scala @@ -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)(