From 9d2a173b15ff2f9c546d95261fd171cf6da51328 Mon Sep 17 00:00:00 2001 From: Alex Solomatnikov Date: Tue, 24 Jan 2017 14:58:01 -0800 Subject: [PATCH 1/9] Initial (compilable) version of I2C (no actual logic yet) --- src/main/scala/devices/i2c/I2C.scala | 58 +++++++++++++++++++ src/main/scala/devices/i2c/I2CCtrlRegs.scala | 13 +++++ src/main/scala/devices/i2c/I2CPeriphery.scala | 34 +++++++++++ src/main/scala/devices/i2c/I2CPins.scala | 27 +++++++++ 4 files changed, 132 insertions(+) create mode 100644 src/main/scala/devices/i2c/I2C.scala create mode 100644 src/main/scala/devices/i2c/I2CCtrlRegs.scala create mode 100644 src/main/scala/devices/i2c/I2CPeriphery.scala create mode 100644 src/main/scala/devices/i2c/I2CPins.scala diff --git a/src/main/scala/devices/i2c/I2C.scala b/src/main/scala/devices/i2c/I2C.scala new file mode 100644 index 0000000..e5959c5 --- /dev/null +++ b/src/main/scala/devices/i2c/I2C.scala @@ -0,0 +1,58 @@ +// See LICENSE for license details. +package sifive.blocks.devices.i2c + +import Chisel._ +import config._ +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 +} + +class I2CPin extends Bundle { + val in = Bool(INPUT) + val out = Bool(OUTPUT) + val oe = Bool(OUTPUT) +} + +class I2CPort extends Bundle { + val scl = new I2CPin + val sda = new I2CPin +} + +trait I2CBundle extends Bundle with HasI2CParameters { + val port = new I2CPort +} + +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 + + // 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)) + ) +} + +// 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) diff --git a/src/main/scala/devices/i2c/I2CCtrlRegs.scala b/src/main/scala/devices/i2c/I2CCtrlRegs.scala new file mode 100644 index 0000000..1a69783 --- /dev/null +++ b/src/main/scala/devices/i2c/I2CCtrlRegs.scala @@ -0,0 +1,13 @@ +// See LICENSE for license details. +package sifive.blocks.devices.i2c + +// matching Open Cores I2C to re-use Linux driver +// http://lxr.free-electrons.com/source/drivers/i2c/busses/i2c-ocores.c?v=4.6 + +object I2CCtrlRegs { + val prescaler_lo = 0x00 // low byte clock prescaler register + val prescaler_hi = 0x01 // high byte clock prescaler register + val control = 0x02 // control register + val data = 0x03 // write: transmit byte, read: receive byte + val cmd_status = 0x04 // write: command, read: status +} diff --git a/src/main/scala/devices/i2c/I2CPeriphery.scala b/src/main/scala/devices/i2c/I2CPeriphery.scala new file mode 100644 index 0000000..8eb7418 --- /dev/null +++ b/src/main/scala/devices/i2c/I2CPeriphery.scala @@ -0,0 +1,34 @@ +// See LICENSE for license details. +package sifive.blocks.devices.i2c + +import Chisel._ +import diplomacy.LazyModule +import rocketchip.{TopNetwork,TopNetworkModule} +import uncore.tilelink2.TLFragmenter + +trait PeripheryI2C { + this: TopNetwork { val i2cConfigs: Seq[I2CConfig] } => + val i2cDevices = i2cConfigs.zipWithIndex.map { case (c, i) => + val i2c = LazyModule(new TLI2C(c)) + i2c.suggestName(s"i2c$i") + i2c.node := TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node) + intBus.intnode := i2c.intnode + i2c + } +} + +trait PeripheryI2CBundle { + this: { val i2cConfigs: Seq[I2CConfig] } => + val i2cs = Vec(i2cConfigs.size, new I2CPort) +} + +trait PeripheryI2CModule { + this: TopNetworkModule { + val i2cConfigs: Seq[I2CConfig] + val outer: PeripheryI2C + val io: PeripheryI2CBundle + } => + (io.i2cs zip outer.i2cDevices).foreach { case (io, device) => + io <> device.module.io.port + } +} diff --git a/src/main/scala/devices/i2c/I2CPins.scala b/src/main/scala/devices/i2c/I2CPins.scala new file mode 100644 index 0000000..d7017bc --- /dev/null +++ b/src/main/scala/devices/i2c/I2CPins.scala @@ -0,0 +1,27 @@ +// See LICENSE for license details. +package sifive.blocks.devices.i2c + +import Chisel._ +import sifive.blocks.devices.gpio.{GPIOPin, GPIOOutputPinCtrl} +import sifive.blocks.util.ShiftRegisterInit + + +class I2CPinsIO extends Bundle { + val scl = new GPIOPin + val sda = new GPIOPin +} + +class I2CGPIOPort(syncStages: Int = 0) extends Module { + val io = new Bundle{ + val i2c = new I2CPort().flip() + val pins = new I2CPinsIO + } + + GPIOOutputPinCtrl(io.pins.scl, io.i2c.scl.out, pue=true.B, ie = true.B) + io.pins.scl.o.oe := io.i2c.scl.oe + io.i2c.scl.in := ShiftRegisterInit(io.pins.scl.i.ival, syncStages, Bool(true)) + + GPIOOutputPinCtrl(io.pins.sda, io.i2c.sda.out, pue=true.B, ie = true.B) + io.pins.sda.o.oe := io.i2c.sda.oe + io.i2c.sda.in := ShiftRegisterInit(io.pins.sda.i.ival, syncStages, Bool(true)) +} From 2cc1012fa2940840e071359215746cddb14c7aa2 Mon Sep 17 00:00:00 2001 From: Alex Solomatnikov Date: Tue, 31 Jan 2017 17:20:53 -0800 Subject: [PATCH 2/9] 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)( From 3781d1fb1aa2c878ea7931a7bcf346c0ea95a619 Mon Sep 17 00:00:00 2001 From: Alex Solomatnikov Date: Fri, 3 Feb 2017 16:41:59 -0800 Subject: [PATCH 3/9] Bug fixes: passing OC WB test --- src/main/scala/devices/i2c/I2C.scala | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/scala/devices/i2c/I2C.scala b/src/main/scala/devices/i2c/I2C.scala index 40102ba..ac4e58a 100644 --- a/src/main/scala/devices/i2c/I2C.scala +++ b/src/main/scala/devices/i2c/I2C.scala @@ -328,7 +328,7 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap { when (load) { receivedData := transmitData } - .otherwise { + .elsewhen (shift) { receivedData := Cat(receivedData, receivedBit) } @@ -453,6 +453,11 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap { //////// Top level //////// + // hack + val nextCmd = Wire(UInt(8.W)) + nextCmd := cmd.asUInt + cmd := (new CommandBundle).fromBits(nextCmd) + when (cmdAck || arbLost) { cmd.start := false.B // clear command bits when done cmd.stop := false.B // or when aribitration lost @@ -461,6 +466,7 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap { } 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 } @@ -477,11 +483,6 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap { 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)), From d474b5ceb25e76501fb8a9bb77f541e563f589be Mon Sep 17 00:00:00 2001 From: Alex Solomatnikov Date: Fri, 3 Feb 2017 18:10:03 -0800 Subject: [PATCH 4/9] Addressing comments: bool style, comments, removed suggestName --- src/main/scala/devices/i2c/I2C.scala | 50 +++++++++---------- src/main/scala/devices/i2c/I2CPeriphery.scala | 1 - 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/main/scala/devices/i2c/I2C.scala b/src/main/scala/devices/i2c/I2C.scala index ac4e58a..0fcf640 100644 --- a/src/main/scala/devices/i2c/I2C.scala +++ b/src/main/scala/devices/i2c/I2C.scala @@ -90,7 +90,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 +98,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 = (new Majority(fSCL.toBools.toSet)).out) + val sSDA = Reg(init = true.B, next = (new Majority(fSDA.toBools.toSet)).out) - 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 +122,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 +141,26 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap { clkEn := false.B } - val sclOen = Reg(init = Bool(true)) + val sclOen = Reg(init = true.B) io.port.scl.oe := sclOen - val sdaOen = Reg(init = Bool(true)) + 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 +169,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 +309,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) { @@ -453,7 +453,7 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap { //////// Top level //////// - // hack + // 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) @@ -483,7 +483,7 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap { status.transferInProgress := cmd.read || cmd.write status.irqFlag := (cmdAck || arbLost || status.irqFlag) && !cmd.irqAck - // Note that these are out of order. + regmap( I2CCtrlRegs.prescaler_lo -> Seq(RegField(8, prescaler.lo)), I2CCtrlRegs.prescaler_hi -> Seq(RegField(8, prescaler.hi)), diff --git a/src/main/scala/devices/i2c/I2CPeriphery.scala b/src/main/scala/devices/i2c/I2CPeriphery.scala index 8eb7418..7ee5015 100644 --- a/src/main/scala/devices/i2c/I2CPeriphery.scala +++ b/src/main/scala/devices/i2c/I2CPeriphery.scala @@ -10,7 +10,6 @@ trait PeripheryI2C { this: TopNetwork { val i2cConfigs: Seq[I2CConfig] } => val i2cDevices = i2cConfigs.zipWithIndex.map { case (c, i) => val i2c = LazyModule(new TLI2C(c)) - i2c.suggestName(s"i2c$i") i2c.node := TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node) intBus.intnode := i2c.intnode i2c From 5a0d084b3867dd6494974bf12a139eb98810c2e4 Mon Sep 17 00:00:00 2001 From: Alex Solomatnikov Date: Mon, 6 Feb 2017 10:39:47 -0800 Subject: [PATCH 5/9] Renamed i2cDevices to i2c --- src/main/scala/devices/i2c/I2CPeriphery.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/devices/i2c/I2CPeriphery.scala b/src/main/scala/devices/i2c/I2CPeriphery.scala index 7ee5015..09bb9cc 100644 --- a/src/main/scala/devices/i2c/I2CPeriphery.scala +++ b/src/main/scala/devices/i2c/I2CPeriphery.scala @@ -8,7 +8,7 @@ import uncore.tilelink2.TLFragmenter trait PeripheryI2C { this: TopNetwork { val i2cConfigs: Seq[I2CConfig] } => - val i2cDevices = i2cConfigs.zipWithIndex.map { case (c, i) => + val i2c = i2cConfigs.zipWithIndex.map { case (c, i) => val i2c = LazyModule(new TLI2C(c)) i2c.node := TLFragmenter(peripheryBusConfig.beatBytes, cacheBlockBytes)(peripheryBus.node) intBus.intnode := i2c.intnode @@ -27,7 +27,7 @@ trait PeripheryI2CModule { val outer: PeripheryI2C val io: PeripheryI2CBundle } => - (io.i2cs zip outer.i2cDevices).foreach { case (io, device) => + (io.i2cs zip outer.i2c).foreach { case (io, device) => io <> device.module.io.port } } From c311b6ec6348e469e6f7b26854928b1a30242dd4 Mon Sep 17 00:00:00 2001 From: Alex Solomatnikov Date: Tue, 7 Feb 2017 15:58:04 -0800 Subject: [PATCH 6/9] Added license --- src/main/scala/devices/i2c/I2C.scala | 39 ++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/main/scala/devices/i2c/I2C.scala b/src/main/scala/devices/i2c/I2C.scala index 0fcf640..543b591 100644 --- a/src/main/scala/devices/i2c/I2C.scala +++ b/src/main/scala/devices/i2c/I2C.scala @@ -1,4 +1,43 @@ +///////////////////////////////////////////////////////////////////// +//// //// +//// 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. + package sifive.blocks.devices.i2c import Chisel._ From 9ca71c0cf222bd50c78434aaaa8f2ddcc4f7a269 Mon Sep 17 00:00:00 2001 From: Alex Solomatnikov Date: Tue, 7 Feb 2017 16:14:28 -0800 Subject: [PATCH 7/9] Added note: WISHBONE interface replaced by Tilelink2 --- src/main/scala/devices/i2c/I2C.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/scala/devices/i2c/I2C.scala b/src/main/scala/devices/i2c/I2C.scala index 543b591..da55549 100644 --- a/src/main/scala/devices/i2c/I2C.scala +++ b/src/main/scala/devices/i2c/I2C.scala @@ -37,6 +37,7 @@ // 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 From 72e4b60d81e5ae1cbc518c9a3bda486a56dfca8c Mon Sep 17 00:00:00 2001 From: Alex Solomatnikov Date: Thu, 9 Feb 2017 11:36:19 -0800 Subject: [PATCH 8/9] Made regs 32-bit word aligned to match the rest of the system --- src/main/scala/devices/i2c/I2CCtrlRegs.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/scala/devices/i2c/I2CCtrlRegs.scala b/src/main/scala/devices/i2c/I2CCtrlRegs.scala index 1a69783..aaa6197 100644 --- a/src/main/scala/devices/i2c/I2CCtrlRegs.scala +++ b/src/main/scala/devices/i2c/I2CCtrlRegs.scala @@ -6,8 +6,8 @@ package sifive.blocks.devices.i2c object I2CCtrlRegs { val prescaler_lo = 0x00 // low byte clock prescaler register - val prescaler_hi = 0x01 // high byte clock prescaler register - val control = 0x02 // control register - val data = 0x03 // write: transmit byte, read: receive byte - val cmd_status = 0x04 // write: command, read: status + val prescaler_hi = 0x04 // high byte clock prescaler register + val control = 0x08 // control register + val data = 0x0c // write: transmit byte, read: receive byte + val cmd_status = 0x10 // write: command, read: status } From 095cb158dd0e7fe87b3cf5afa30b6db3ec4dc266 Mon Sep 17 00:00:00 2001 From: Alex Solomatnikov Date: Thu, 9 Feb 2017 11:37:40 -0800 Subject: [PATCH 9/9] Flipped polarity of output enables to match Guava pins logic --- src/main/scala/devices/i2c/I2C.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/devices/i2c/I2C.scala b/src/main/scala/devices/i2c/I2C.scala index da55549..0c48764 100644 --- a/src/main/scala/devices/i2c/I2C.scala +++ b/src/main/scala/devices/i2c/I2C.scala @@ -182,10 +182,10 @@ trait I2CModule extends Module with HasI2CParameters with HasRegMap { } val sclOen = Reg(init = true.B) - io.port.scl.oe := sclOen + io.port.scl.oe := !sclOen val sdaOen = Reg(init = true.B) - io.port.sda.oe := sdaOen + io.port.sda.oe := !sdaOen val sdaChk = Reg(init = false.B) // check SDA output (Multi-master arbitration)