All Downloads are FREE. Search and download functionalities are using the official Maven repository.

spinal.lib.com.i2c.I2cCtrl.scala Maven / Gradle / Ivy

package spinal.lib.com.i2c

import spinal.core._
import spinal.lib._
import spinal.lib.bus.misc.{BusSlaveFactory, BusSlaveFactoryAddressWrapper}
import spinal.lib.fsm.{State, StateMachine}

object I2cCtrl{
  case class I2cAddress() extends Bundle{
    val enable  = Bool
    val value = Bits(10 bits)
    val is10Bit = Bool
  }

  /*
   * To create an I2c controller which is master/slave ready, you basicaly only need an low level I2C bus driver (I2cSlave) and add on the top
   * of it some clock generation logic/status/buffers. This is what driveI2cSlaveIo do.
   *
   * This controller logic can be instanciated as an I2C slave controller only, or as an I2C master/Slave controller.
   * There are a list of features :
   * - Slave only / Master Slave
   * - Slave address filtering (7/10 bits)
   * - Multimaster
   * - Timeout
   *
   * Master initialisation :
   * 1) Configure samplingClockDivider/timeout/tsuDat at x28/x2C/x30
   * 2) Configure tLow/tHigh/tBuf at address x50/x54/x58 (WO).   tBuf is the idle time after a STOP transmition
   *
   * Master write:
   * 1) Do a START by setting the start bit of the masterStatus register
   *   Multimaster => You have to check that the master had catch the bus via the busy flag of the masterStatus register
   * 2) Provide the address by feeding the txData/txAck register, then wait the txAck emptiness
   *   Multimaster => Don't forget the txData disableOnDataConflict flag, then to know of the arbitration had pass, check the txData enable flag
   *   Conflict => If you address negotiation had conflicted with another master, you have to set the drop flag of the masterStatus register
   *   ack check => Should had enable the listen flag of the rxAck register
   * 3) Provide the data by feeding the txData/txAck register, then wait the txAck emptiness
   * 4) Do a STOP by setting the stop bit of the masterStatus register
   *
   * Master read :
   *   Note : (multimaster operations and ack checking are the thame than the onf of 'master write')
   * 1) Do a START by setting the start bit of the masterStatus register
   * 2) Provide the address by feeding the txData/txAck register, then wait the txAck emptiness
   *   Multimaster => Same as master write
   * 3) Enable rxData then provide the data by feeding the txData/txAck register, then wait the txAck emptiness to read the rxData
   * 4) Do a STOP by setting the stop bit of the masterStatus register
   *
   *
   * Slave initialisation :
   * 1) Configure samplingClockDivider/timeout/tsuDat at x28/x2C/x30
   *
   * Slave write without hardware address filtering :
   * 1) Wait the start interrupt, clear start flag
   * 2) Disable the rxAck register (! real time !)
   * 3) Wait the rxData interrupt and check the rxData value.
   *    - Address KO, put the rxAck in an NACK repeat mode and disable the rxData listen, then return to 1)
   *    - Address OK, set the rxAck to ACK
   * 4) wait for rxData interrupt, then set rxAck to NACK to end the transfer
   *
   *
   * Slave read without hardware address filtering :
   * 1) Wait the start interrupt, clear start flag
   * 2) Disable the rxAck register (! real time !)
   * 3) Wait the rxData interrupt and check the rxData value.
   *    - Address KO, put the rxAck in an NACK repeat mode and disable the rxData listen, then return to 1)
   *    - Address OK, disable the txData, set the rxAck to ACK
   * 4) set the txData, set the rxAck to disable
   *
   * Slave address filtering
   * 1) Set the address filtring registers
   * 2) wait for a txAck interrupt, then check for the hits flags into the filteringStatus register to identify which filter had hit
   * 3) Continue has the 3) address OK of Slave read/write without hardware address filtering. You can get the RW i2c frame bit in the hitContext register
   *
   *
   * Register mapping :
   *
   * txData -> 0x00
   * - value -> W[7:0]
   * - valid -> RW[8]
   * - enable -> RW[9]
   * - repeat -> W[10]
   * - disableOnDataConflict -> W[11]
   *
   * txAck -> 0x04
   * - value -> W[0]
   * - valid -> RW[8]
   * - enable -> RW[9]
   * - repeat -> W[10]
   * - disableOnDataConflict -> W[11]
   *
   * rxData -> 0x08
   * - value -> R[7:0]
   * - valid -> R[8]
   * - listen -> W[9], cleared when read
   *
   * rxAck -> 0x0C
   * - value -> R[0]
   * - valid -> R[8]
   * - listen -> W[9], cleared when read
   * 
   * interrupt -> 0x20
   * - rxDataEnable -> RW[0]
   * - rxAckEnable -> RW[1]
   * - txDataEnable -> RW[2]
   * - txAckEnable -> RW[3]
   *
   * - startEnable -> RW[4]
   * - restartEnable -> RW[5]
   * - endEnable -> RW[6]
   * - dropEnable -> RW[7]
   *
   * - startFlag -> RW[8] cleared when set
   * - restartFlag -> RW[9] cleared when set
   * - endFlag -> RW[10] cleared when set
   * - dropFlag -> RW[11] cleared when set
   *
   * - clockGenBusyEnable -> RW[16]
   *
   * samplingClockDivider -> W 0x28
   * timeout -> W 0x2C
   * tsuDat -> W 0x30
   *
   * masterStatus -> 0x40
   * - isBusy -> R[0]
   * - start -> W[4]
   * - stop -> W[5]
   * - drop -> W[6]
   *
   * tLow -> W 0x50
   * tHigh -> W 0x54
   * tBuf -> W 0x58
   *
   * filteringStatus -> 0x80
   * - hit_0 -> R[0]
   * - hit_1 -> R[1]
   * - hit_N -> R[N]
   *
   * hitContext -> 0x84
   * - rw -> R[0]
   *
   * filteringConfig_0 -> 0x88
   * - value -> W[9:0]
   * - is10Bit -> W[14]
   * - enable -> W[15]
   *
   * filteringConfig_N -> 0x88 + 4*N
   *
  **/

  def driveI2cSlaveIo(io : I2cSlaveIo,busCtrl: BusSlaveFactory, baseAddress: BigInt)(generics : I2cSlaveMemoryMappedGenerics) = new Area{
    import generics._
    import io._


    val busCtrlWithOffset = new BusSlaveFactoryAddressWrapper(busCtrl, baseAddress)
    val frameReset = False
    val i2cBuffer = I2c()
    i2cBuffer <> i2c



    val rxData = new Area{
      val event = RegNext(False) init(False)
      val listen = RegInit(False)
      val valid = RegInit(False)
      val value = Reg(Bits(8 bits))

      busCtrlWithOffset.write(listen, address = 0x08, bitOffset = 9)
      busCtrlWithOffset.read(valid, address = 0x08, bitOffset = 8)
      busCtrlWithOffset.read(value, address = 0x08, bitOffset = 0)
      valid clearWhen(busCtrlWithOffset.isReading(0x08))
    }

    val rxAck = new Area{
      val listen = RegInit(False)
      val valid = RegInit(False)
      val value = Reg(Bool)

      busCtrlWithOffset.write(listen, address = 0x0C, bitOffset = 9)
      busCtrlWithOffset.read(valid, address = 0x0C, bitOffset = 8)
      busCtrlWithOffset.read(value, address = 0x0C, bitOffset = 0)
      valid clearWhen(busCtrlWithOffset.isReading(0x0C))
    }

    val txData = new Area {
      val valid  = RegInit(True)
      val repeat = RegInit(True)
      val enable = RegInit(False)
      val disableOnDataConflict = Reg(Bool)
      val value  = Reg(Bits(8 bits))
      val forceDisable = False
    }

    val txAck = new Area {
      val valid  = RegInit(True)
      val repeat = RegInit(True)
      val enable = RegInit(False)
      val disableOnDataConflict = Reg(Bool)
      val value  = Reg(Bool)
      val forceAck = False
    }

    val addressFilter = if(genAddressFilter) new Area{
      val addresses = Vec(Reg(I2cAddress()), addressFilterCount)
      for((address, idx) <- addresses.zipWithIndex){
        address.enable init(False)
        busCtrlWithOffset.write(address.value, 0x88 + 4*idx, 0)
        busCtrlWithOffset.write(address.is10Bit, 0x88 + 4*idx, 14)
        busCtrlWithOffset.write(address.enable, 0x88 + 4*idx, 15)
      }

      val state = RegInit(U"00")
      val byte0, byte1 = Reg(Bits(8 bits))
      val byte0Is10Bit = byte0(7 downto 3) === 0x1E
      when(rxData.event){
        switch(state){
          is(0){
            byte0 := rxData.value
            state := 1
          }
          is(1){
            byte1 := rxData.value
            state := 2
          }
        }
      }
      when(frameReset){
        state := 0
      }

      val hits = addresses.map(address => address.enable && Mux(!address.is10Bit,(byte0 >> 1) === address.value(6 downto 0) && state =/= 0, (byte0(2 downto 1) ## byte1) === address.value && state === 2))
      txAck.forceAck setWhen(byte0Is10Bit && state === 1 && addresses.map(address => address.enable && address.is10Bit && byte0(2 downto 1) === address.value(9 downto 8)).orR)
      busCtrlWithOffset.read(hits.asBits, 0x80, 0)
      busCtrlWithOffset.read(byte0.lsb, 0x84, 0)

      when(hits.orR.rise()){
        txAck.valid := False
      }
    } else null




    val masterLogic = if(genMaster) new Area{
      val start = busCtrlWithOffset.createReadAndWrite(Bool, 0x40, 4) init(False)
      val stop = busCtrlWithOffset.createReadAndWrite(Bool, 0x40, 5) init(False)
      val drop = busCtrlWithOffset.createReadAndWrite(Bool, 0x40, 6) init(False)
      val timer = new Area{
        val value = Reg(UInt(masterGenerics.timerWidth bits))
        val tLow = busCtrlWithOffset.createWriteOnly(value, address = 0x50)
        val tHigh = busCtrlWithOffset.createWriteOnly(value, address = 0x54)
        val tBuf = busCtrlWithOffset.createWriteOnly(value, address = 0x58)
        val done = value === 0
        value := value - done.asUInt
      }

      val txReady = Bool //Say if the tx buffer is ready to continue

      val fsm = new StateMachine{
        val IDLE, BUSY, START, LOW, HIGH, RESTART, STOP1, STOP2, TBUF = State()
        setEntry(IDLE)

        val isBusy = !this.isActive(IDLE)
        busCtrlWithOffset.read(isBusy, 0x40, 0)

        IDLE.onEntry{
          start := False
          stop := False
          drop := False
        }
        IDLE.whenIsActive{
          when(start && !internals.inFrame){
            goto(START)
          }
        }

        START.onEntry{
          timer.value := timer.tHigh
        }
        START.whenIsActive{
          i2cBuffer.sda.write := False
          when(timer.done || !internals.sclRead){
            start := False
            goto(LOW)
          }
        }

        LOW.onEntry{
          timer.value := timer.tLow
        }
        LOW.whenIsActive{
          when(timer.done){
//            val couldBeEnd =  !inAckState && dataCounter === 0 && !txData.valid
            when(stop ) {
              txData.forceDisable := True
              goto(STOP1)
            }.elsewhen(start){
              txData.forceDisable := True
              goto(RESTART)
            }.elsewhen(internals.sclRead && txReady) {
              goto(HIGH)
            }
          } otherwise {
            i2cBuffer.scl.write := False
          }
        }

        HIGH.onEntry{
          timer.value := timer.tHigh
        }
        HIGH.whenIsActive{
          when(timer.done || !internals.sclRead){
            goto(LOW)
          }
        }

        RESTART.onEntry{
          timer.value := timer.tHigh
        }
        RESTART.whenIsActive{
          when(!internals.sclRead){ //Check for slave clock stretching
            timer.value := timer.tHigh
          }
          when(timer.done){
            goto(START)
          }
        }

        STOP1.onEntry{
          timer.value := timer.tHigh
        }
        STOP1.whenIsActive{
          i2cBuffer.scl.write := False
          i2cBuffer.sda.write := False
          when(timer.done){
            goto(STOP2)
          }
        }

        STOP2.onEntry{
          timer.value := timer.tHigh
        }
        STOP2.whenIsActive{
          i2cBuffer.sda.write := False
          when(timer.done){
            stop := False
            goto(TBUF)
          }
        }

        TBUF.onEntry{
          timer.value := timer.tBuf
        }
        TBUF.whenIsActive{
          when(timer.done){
            goto(IDLE)
          }
        }
        always{
          when(drop) {
            goto(TBUF)
          }
        }
      }
    } else null


    val dataCounter = RegInit(U"000")
    val inAckState = RegInit(False)
    val wasntAck = RegInit(False)


    if(genMaster) masterLogic.txReady :=  inAckState ? txAck.valid | txData.valid
    when(!inAckState) {
      bus.rsp.valid  := txData.valid && !(rxData.valid && rxData.listen) && bus.cmd.kind === I2cSlaveCmdMode.DRIVE
      bus.rsp.enable := txData.enable
      bus.rsp.data   := txData.value(7 - dataCounter)
      when(txData.forceDisable){
        bus.rsp.valid := True
        bus.rsp.enable := False
      }
    } otherwise {
      bus.rsp.valid  := txAck.valid && !(rxAck.valid && rxAck.listen) && bus.cmd.kind === I2cSlaveCmdMode.DRIVE
      bus.rsp.enable := txAck.enable
      bus.rsp.data   := txAck.value
      when(txAck.forceAck){
        bus.rsp.valid := True
        bus.rsp.enable := True
        bus.rsp.data := False
      }
    }
    when(wasntAck){
      bus.rsp.valid  := bus.cmd.kind === I2cSlaveCmdMode.DRIVE
      bus.rsp.enable := False
    }


    switch(bus.cmd.kind){
      is(I2cSlaveCmdMode.START){
        frameReset := True
      }
      is(I2cSlaveCmdMode.RESTART){
        frameReset := True
      }
      is(I2cSlaveCmdMode.STOP){
        frameReset := True
      }
      is(I2cSlaveCmdMode.DROP){
        rxData.listen := False
        rxAck.listen := False
        frameReset := True
      }
      is(I2cSlaveCmdMode.DRIVE){
        when(!inAckState) {
          when(txData.valid && dataCounter === 7) {
            when(!txData.repeat) {
              txData.valid := False
            }
          }
        } otherwise {
          when(txAck.valid) {
            when(!txAck.repeat) {
              txAck.valid := False
            }
          }
        }
      }
      is(I2cSlaveCmdMode.READ){
        when(!inAckState) {
          rxData.value(7 - dataCounter) := bus.cmd.data
          dataCounter := dataCounter + 1

          when(bus.rsp.data =/= bus.cmd.data){
            txData.enable clearWhen(txData.disableOnDataConflict)
            txAck.enable clearWhen(txAck.disableOnDataConflict)
          }
          when(dataCounter === U"111"){
            rxData.event := True
            rxData.valid setWhen(rxData.listen)
            inAckState := True
          }

        } otherwise {
          rxAck.valid setWhen(rxAck.listen)
          rxAck.value := bus.cmd.data
          inAckState := False
          wasntAck := bus.cmd.data
        }
      }
    }

    when(frameReset){
      inAckState := False
      dataCounter := 0
      wasntAck := False
    }

    when(bus.cmd.kind === I2cSlaveCmdMode.STOP || bus.cmd.kind === I2cSlaveCmdMode.DROP){
      txData.valid := True
      txData.enable := False
      txData.repeat := True

      txAck.valid := True
      txAck.enable := False
      txAck.repeat := True

      rxData.listen := False

      rxAck.listen := False
    }

    val interruptCtrl = new Area{
      val rxDataEnable = busCtrlWithOffset.createReadAndWrite(Bool, address = 0x20, bitOffset = 0) init(False)
      val rxAckEnable = busCtrlWithOffset.createReadAndWrite(Bool, address = 0x20, bitOffset = 1)  init(False)
      val txDataEnable = busCtrlWithOffset.createReadAndWrite(Bool, address = 0x20, bitOffset = 2) init(False)
      val txAckEnable = busCtrlWithOffset.createReadAndWrite(Bool, address = 0x20, bitOffset = 3)  init(False)

      def i2CSlaveEvent(enableBitId : Int, flagBitId : Int, busCmd : I2cSlaveCmdMode.E) = new Area{
        val enable = busCtrlWithOffset.createReadAndWrite(Bool, address = 0x20, bitOffset = enableBitId) init(False)
        val flag = busCtrlWithOffset.readAndClearOnSet(RegInit(False) setWhen(bus.cmd.kind === busCmd) clearWhen(!enable),  address = 0x20, bitOffset = flagBitId)
      }

      val start = i2CSlaveEvent(4,8,I2cSlaveCmdMode.START)
      val restart = i2CSlaveEvent(5,9,I2cSlaveCmdMode.RESTART)
      val end = i2CSlaveEvent(6,10,I2cSlaveCmdMode.STOP)
      val drop = i2CSlaveEvent(7,11,I2cSlaveCmdMode.DROP)

      val interrupt = (rxDataEnable && rxData.valid) || (rxAckEnable && rxAck.valid) ||
        (txDataEnable && !txData.valid) || (txAckEnable && !txAck.valid) ||
        (start.flag || restart.flag || end.flag || drop.flag)

      val clockGen = if(genMaster) new Area{
        val busyEnable = busCtrlWithOffset.createReadAndWrite(Bool, address = 0x20, bitOffset = 16)
        interrupt setWhen((busyEnable && masterLogic.fsm.isBusy))
      } else null
    }



    busCtrlWithOffset.write(0x00, 0 -> txData.value, 10 -> txData.repeat, 11 -> txData.disableOnDataConflict)
    busCtrlWithOffset.readAndWrite(txData.valid, address = 0x00, bitOffset = 8)
    busCtrlWithOffset.readAndWrite(txData.enable, address = 0x00, bitOffset = 9)

    busCtrlWithOffset.write(0x04, 0 -> txAck.value, 10 -> txAck.repeat, 11 -> txAck.disableOnDataConflict)
    busCtrlWithOffset.readAndWrite(txAck.valid, address = 0x04, bitOffset = 8)
    busCtrlWithOffset.readAndWrite(txAck.enable, address = 0x04, bitOffset = 9)

    busCtrlWithOffset.drive(config.samplingClockDivider, 0x28) init(0)
    busCtrlWithOffset.drive(config.timeout, 0x2C) randBoot()
    busCtrlWithOffset.drive(config.tsuDat, 0x30) randBoot()
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy