总线从端(Factory)实现

简介

本页将记录 BusSlaveFactory 工具及其变体之一的实现。您可以在此处获取有关该工具功能的更多信息 here

规范

类图如下:

../../_images/bus_slave_factory_classes.svg

BusSlaveFactory 抽象类定义了每个实现应提供的最低要求:

名称

描述

busDataWidth

返回总线的数据宽度

read(that,address,bitOffset)

当通过总线读取地址 address 时,用 thatbitOffset 位置的数据填充响应

write(that,address,bitOffset)

当通过总线写入地址 address 时,将总线上 bitOffset 位置的数据赋值 that

onWrite(address)(doThat)

address 地址上发生写操作(出现写事务)时调用 doThat

onRead(address)(doThat)

address 上发生读操作(出现读事务)时调用 doThat

nonStopWrite(that,bitOffset)

将通过总线写入的 bitOffset 的数据赋值到 that

通过使用它们, BusSlaveFactory 还能够提供许多实用工具:

名称

返回类型

描述

readAndWrite(that,address,bitOffset)

使 that 信号可通过 address 地址读写,并且该信号放置在数据的 bitOffset 位置

readMultiWord(that,address)

创建内存映射以从 ‘address’ 地址读取 that 信号。 :
如果 that 的位宽大于一个字(32位),它将在以下地址上扩展寄存器

writeMultiWord(that,address)

创建内存映射以通过总线 ‘address’ 地址写入 that 信号。 :
如果 that 的位宽大于一个字(32位),它将在以下地址上扩展寄存器

createWriteOnly(dataType,address,bitOffset)

T

address 地址处创建一个 dataType 类型的只写寄存器,并将其放置在字中的 bitOffset 位置

createReadWrite(dataType,address,bitOffset)

T

address 处创建一个 dataType 类型的读写寄存器,并将其放置在字中的 bitOffset 位置

createAndDriveFlow(dataType,address,bitOffset)

Flow[T]

address 地址处创建一个 dataType 类型的可写流(Flow)寄存器,并将其放置在字中的 bitOffset 位置

drive(that,address,bitOffset)

使用位于 address 地址的可写寄存器中 bitOffset 位置的信号驱动 that

driveAndRead(that,address,bitOffset)

使用位于 address 地址的可读写寄存器中 bitOffset 位置的信号驱动 that

driveFlow(that,address,bitOffset)

当对 address 地址写入时,通过使用位于 bitOffset 位的数据,在 that 流(Flow)上发出事务

readStreamNonBlocking(that,address,
validBitOffset,payloadBitOffset)
读取 that 信号并在读取 address 地址时消耗事务。
valid <= validBitOffset bit
payload <= payloadBitOffset+widthOf(payload) downto payloadBitOffset
doBitsAccumulationAndClearOnRead
(that,address,bitOffset)
实例化一个内部寄存器,该寄存器在每个周期执行以下操作:
reg := reg | that
然后,当发生读取时,寄存器被清除。该寄存器可通过 address 地址读取,并放置在字中的 bitOffset 位置

关于 BusSlaveFactoryDelayed,它仍然是一个抽象类,但它捕获每个原语(BusSlaveFactoryElement)对数据模型的调用。该数据模型是一个包含所有原语的列表,也是一个 HashMap,它将使用的每个地址链接到正在使用它的原语列表。然后,当它们全部被收集时(在当前组件的末尾),它会执行一个回调,该回调应该由扩展它的类实现。该回调函数中实现了这个原语列表中每个原语相对应的硬件。

实现

BusSlaveFactory

让我们来描述一下原语抽象函数:

trait BusSlaveFactory  extends Area {

  def busDataWidth : Int

  def read(that : Data,
           address : BigInt,
           bitOffset : Int = 0) : Unit

  def write(that : Data,
            address : BigInt,
            bitOffset : Int = 0) : Unit

  def onWrite(address : BigInt)(doThat : => Unit) : Unit
  def onRead (address : BigInt)(doThat : => Unit) : Unit

  def nonStopWrite( that : Data,
                    bitOffset : Int = 0) : Unit

  // ...
}

然后让我们利用这些来实现一些有用的工具:

trait BusSlaveFactory  extends Are {
  // ...
  def readAndWrite(that : Data,
                   address: BigInt,
                   bitOffset : Int = 0): Unit = {
    write(that,address,bitOffset)
    read(that,address,bitOffset)
  }

  def drive(that : Data,
            address : BigInt,
            bitOffset : Int = 0) : Unit = {
    val reg = Reg(that)
    write(reg,address,bitOffset)
    that := reg
  }

  def driveAndRead(that : Data,
                   address : BigInt,
                   bitOffset : Int = 0) : Unit = {
    val reg = Reg(that)
    write(reg,address,bitOffset)
    read(reg,address,bitOffset)
    that := reg
  }

  def driveFlow[T <: Data](that : Flow[T],
                           address: BigInt,
                           bitOffset : Int = 0) : Unit = {
    that.valid := False
    onWrite(address) {
      that.valid := True
    }
    nonStopWrite(that.payload,bitOffset)
  }

  def createReadWrite[T <: Data](dataType: T,
                                 address: BigInt,
                                 bitOffset : Int = 0): T = {
    val reg = Reg(dataType)
    write(reg,address,bitOffset)
    read(reg,address,bitOffset)
    reg
  }

  def createAndDriveFlow[T <: Data](dataType : T,
                                 address: BigInt,
                                 bitOffset : Int = 0) : Flow[T] = {
    val flow = Flow(dataType)
    driveFlow(flow,address,bitOffset)
    flow
  }

  def doBitsAccumulationAndClearOnRead(   that : Bits,
                                          address : BigInt,
                                          bitOffset : Int = 0): Unit = {
    assert(that.getWidth <= busDataWidth)
    val reg = Reg(that)
    reg := reg | that
    read(reg,address,bitOffset)
    onRead(address) {
      reg := that
    }
  }

  def readStreamNonBlocking[T <: Data] (that : Stream[T],
                                        address: BigInt,
                                        validBitOffset : Int,
                                        payloadBitOffset : Int) : Unit = {
    that.ready := False
    onRead(address) {
      that.ready := True
    }
    read(that.valid  ,address,validBitOffset)
    read(that.payload,address,payloadBitOffset)
  }

  def readMultiWord(that : Data,
                address : BigInt) : Unit  = {
    val wordCount = (widthOf(that) - 1) / busDataWidth + 1
    val valueBits = that.asBits.resize(wordCount*busDataWidth)
    val words = (0 until wordCount).map(id => valueBits(id * busDataWidth , busDataWidth bits))
    for (wordId <- (0 until wordCount)) {
      read(words(wordId), address + wordId*busDataWidth/8)
    }
  }

  def writeMultiWord(that : Data,
                 address : BigInt) : Unit  = {
    val wordCount = (widthOf(that) - 1) / busDataWidth + 1
    for (wordId <- (0 until wordCount)) {
      write(
        that = new DataWrapper {
          override def getBitsWidth: Int =
            Math.min(busDataWidth, widthOf(that) - wordId * busDataWidth)

          override def assignFromBits(value : Bits): Unit = {
            that.assignFromBits(
              bits     = value.resized,
              offset   = wordId * busDataWidth,
              bitCount = getBitsWidth bits)
          }
        },address = address + wordId * busDataWidth / 8,0
      )
    }
  }
}

BusSlaveFactoryDelayed

让我们实现用于存储原语的类:

trait BusSlaveFactoryElement

// Ask to make `that` readable when a access is done on `address`.
// bitOffset specify where `that` is placed on the answer
case class BusSlaveFactoryRead(that : Data,
                               address : BigInt,
                               bitOffset : Int) extends BusSlaveFactoryElement

// Ask to make `that` writable when a access is done on `address`.
// bitOffset specify where `that` get bits from the request
case class BusSlaveFactoryWrite(that : Data,
                                address : BigInt,
                                bitOffset : Int) extends BusSlaveFactoryElement

// Ask to execute `doThat` when a write access is done on `address`
case class BusSlaveFactoryOnWrite(address : BigInt,
                                  doThat : () => Unit) extends BusSlaveFactoryElement

// Ask to execute `doThat` when a read access is done on `address`
case class BusSlaveFactoryOnRead( address : BigInt,
                                  doThat : () => Unit) extends BusSlaveFactoryElement

// Ask to constantly drive `that` with the data bus
// bitOffset specify where `that` get bits from the request
case class BusSlaveFactoryNonStopWrite(that : Data,
                                       bitOffset : Int) extends BusSlaveFactoryElement

然后让我们实现 BusSlaveFactoryDelayed 本身:

trait BusSlaveFactoryDelayed extends BusSlaveFactory {
  // elements is an array of all BusSlaveFactoryElement requested
  val elements = ArrayBuffer[BusSlaveFactoryElement]()


  // elementsPerAddress is more structured than elements, it group all BusSlaveFactoryElement per requested addresses
  val elementsPerAddress = collection.mutable.HashMap[BigInt,ArrayBuffer[BusSlaveFactoryElement]]()

  private def addAddressableElement(e : BusSlaveFactoryElement,address : BigInt) = {
    elements += e
    elementsPerAddress.getOrElseUpdate(address, ArrayBuffer[BusSlaveFactoryElement]()) += e
  }

  override def read(that : Data,
           address : BigInt,
           bitOffset : Int = 0) : Unit  = {
    assert(bitOffset + that.getBitsWidth <= busDataWidth)
    addAddressableElement(BusSlaveFactoryRead(that,address,bitOffset),address)
  }

  override def write(that : Data,
            address : BigInt,
            bitOffset : Int = 0) : Unit  = {
    assert(bitOffset + that.getBitsWidth <= busDataWidth)
    addAddressableElement(BusSlaveFactoryWrite(that,address,bitOffset),address)
  }

  def onWrite(address : BigInt)(doThat : => Unit) : Unit = {
    addAddressableElement(BusSlaveFactoryOnWrite(address,() => doThat),address)
  }
  def onRead (address : BigInt)(doThat : => Unit) : Unit = {
    addAddressableElement(BusSlaveFactoryOnRead(address,() => doThat),address)
  }

  def nonStopWrite( that : Data,
                    bitOffset : Int = 0) : Unit = {
    assert(bitOffset + that.getBitsWidth <= busDataWidth)
    elements += BusSlaveFactoryNonStopWrite(that,bitOffset)
  }

  // This is the only thing that should be implement by class that extends BusSlaveFactoryDelayed
  def build() : Unit

  component.addPrePopTask(() => build())
}

AvalonMMSlaveFactory

首先,让我们实现提供兼容 AvalonMM 配置对象的伴随对象,对应于下表:

信号名称

类型

描述

read

Bool

保持一个周期高电平来产生一个读请求

write

Bool

保持一个周期高电平来产生一个写请求

address

UInt(addressWidth bits)

字节为粒度但是字对齐的

writeData

Bits(dataWidth bits)

readDataValid

Bool

保持高电平来响应读命令

readData

Bits(dataWidth bits)

readDataValid 为高时有效

object AvalonMMSlaveFactory {
  def getAvalonConfig( addressWidth : Int,
                       dataWidth : Int) = {
    AvalonMMConfig.pipelined(   // Create a simple pipelined configuration of the Avalon Bus
      addressWidth = addressWidth,
      dataWidth = dataWidth
    ).copy(                     // Change some parameters of the configuration
      useByteEnable = false,
      useWaitRequestn = false
    )
  }

  def apply(bus : AvalonMM) = new AvalonMMSlaveFactory(bus)
}

然后,让我们实现 AvalonMMSlaveFactory 本身。

class AvalonMMSlaveFactory(bus : AvalonMM) extends BusSlaveFactoryDelayed {
  assert(bus.c == AvalonMMSlaveFactory.getAvalonConfig(bus.c.addressWidth,bus.c.dataWidth))

  val readAtCmd = Flow(Bits(bus.c.dataWidth bits))
  val readAtRsp = readAtCmd.stage()

  bus.readDataValid := readAtRsp.valid
  bus.readData := readAtRsp.payload

  readAtCmd.valid := bus.read
  readAtCmd.payload := 0

  override def build(): Unit = {
    for(element <- elements) element match {
      case element : BusSlaveFactoryNonStopWrite =>
        element.that.assignFromBits(bus.writeData(element.bitOffset, element.that.getBitsWidth bits))
      case _ =>
    }

    for((address,jobs) <- elementsPerAddress) {
      when(bus.address === address) {
        when(bus.write) {
          for(element <- jobs) element match {
            case element : BusSlaveFactoryWrite => {
              element.that.assignFromBits(bus.writeData(element.bitOffset, element.that.getBitsWidth bits))
            }
            case element : BusSlaveFactoryOnWrite => element.doThat()
            case _ =>
          }
        }
        when(bus.read) {
          for(element <- jobs) element match {
            case element : BusSlaveFactoryRead => {
              readAtCmd.payload(element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits
            }
            case element : BusSlaveFactoryOnRead => element.doThat()
            case _ =>
          }
        }
      }
    }
  }

  override def busDataWidth: Int = bus.c.dataWidth
}

结论

就这些了,您可以在此处查看一个使用 Apb3SlaveFactory 来创建 Apb3UartCtrl 的 示例

如果您想添加对新内存总线的支持,非常简单,您只需继承、实现 BusSlaveFactoryDelayed 特征(trait)的另一个变体即可。 Apb3SlaveFactory 可能是一个很好的参考:D