总线从端(Factory)实现
简介
本页将记录 BusSlaveFactory 工具及其变体之一的实现。您可以在此处获取有关该工具功能的更多信息 here。
规范
类图如下:
BusSlaveFactory
抽象类定义了每个实现应提供的最低要求:
名称 |
描述 |
---|---|
busDataWidth |
返回总线的数据宽度 |
read(that,address,bitOffset) |
当通过总线读取地址 |
write(that,address,bitOffset) |
当通过总线写入地址 |
onWrite(address)(doThat) |
当 |
onRead(address)(doThat) |
当 |
nonStopWrite(that,bitOffset) |
将通过总线写入的 |
通过使用它们, BusSlaveFactory
还能够提供许多实用工具:
名称 |
返回类型 |
描述 |
---|---|---|
readAndWrite(that,address,bitOffset) |
使 |
|
readMultiWord(that,address) |
创建内存映射以从 ‘address’ 地址读取
that 信号。 :如果
that 的位宽大于一个字(32位),它将在以下地址上扩展寄存器 |
|
writeMultiWord(that,address) |
创建内存映射以通过总线 ‘address’ 地址写入
that 信号。 :如果
that 的位宽大于一个字(32位),它将在以下地址上扩展寄存器 |
|
createWriteOnly(dataType,address,bitOffset) |
T |
在 |
createReadWrite(dataType,address,bitOffset) |
T |
在 |
createAndDriveFlow(dataType,address,bitOffset) |
Flow[T] |
在 |
drive(that,address,bitOffset) |
使用位于 |
|
driveAndRead(that,address,bitOffset) |
使用位于 |
|
driveFlow(that,address,bitOffset) |
当对 |
|
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