X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmain%2Fscala%2Fdevices%2Fi2c%2FI2C.scala;h=d767a265f05c5add29c63ef0e53e70bc96ba08c7;hb=6c5b80671cab9436195fc3fdde9544764a5ded34;hp=ac4e58aabc3c927fed0e897e55dcf112a108ca6e;hpb=3781d1fb1aa2c878ea7931a7bcf346c0ea95a619;p=sifive-blocks.git diff --git a/src/main/scala/devices/i2c/I2C.scala b/src/main/scala/devices/i2c/I2C.scala index ac4e58a..d767a26 100644 --- a/src/main/scala/devices/i2c/I2C.scala +++ b/src/main/scala/devices/i2c/I2C.scala @@ -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) { @@ -453,18 +486,18 @@ 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) + 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) { @@ -483,13 +516,29 @@ 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. + + val statusReadReady = Reg(init = true.B) + when (!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 @@ -500,16 +549,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)