Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
spinal.lib.bus.bmb.Bmb.scala Maven / Gradle / Ivy
package spinal.lib.bus.bmb
import spinal.core._
import spinal.lib._
import spinal.lib.bus.misc.AddressMapping
import spinal.lib.bus.wishbone.Wishbone
import scala.collection.mutable
import scala.util.Random
object WeakConnector{
def apply[T <: Data](source : Bundle,sink : Bundle, by : T,to : T,defaultValue : () => T,allowUpSize: Boolean, allowDownSize: Boolean,allowDrop : Boolean) : Unit = {
(to != null,by != null) match {
case (false,false) =>
case (true,false) => if(defaultValue != null) to := defaultValue() else LocatedPendingError(s"$source can't drive $to because this first doesn't has the corresponding pin")
case (false,true) => if(!allowDrop) LocatedPendingError(s"$by can't drive $sink because this last one doesn't has the corresponding pin")
case (true,true) => {
val toWidth = widthOf(to)
val byWidth = widthOf(by)
if(toWidth == byWidth) to := by
else if(toWidth < byWidth) (if(allowDownSize) to := by.resized else LocatedPendingError(s"$by can't drive $to because the width do not match"))
else (if(allowUpSize) to := by.resized else LocatedPendingError(s"$by can't drive $to because the width do not match"))
}
}
}
}
object Bmb{
val boundaryWidth = 12
val boundarySize = 1 << boundaryWidth
object Cmd {
object Opcode {
val READ = 0
val WRITE = 1
def isWrite(opcode : Bits) = opcode === WRITE
}
}
object Rsp {
object Opcode {
val SUCCESS = 0
val ERROR = 1
}
}
object Inv {
object Opcode {
val EXCEPTED_SOURCE = 0
val ALL = 1
}
}
def incr(address : UInt, p : BmbParameter) : UInt = {
val result = UInt(address.getWidth bits)
val highCat = if (address.getWidth > boundaryWidth) address(address.high downto boundaryWidth) else U""
val base = address(Math.min(boundaryWidth, address.getWidth) - 1 downto 0).resize(boundaryWidth)
result := (highCat @@ ((base + p.access.byteCount) & ~U(p.access.byteCount-1, widthOf(base) bits))).resized
result
}
def addToAddress(address : UInt, value : UInt, p : BmbParameter) : UInt = {
val result = UInt(address.getWidth bits)
val highCat = if (address.getWidth > boundaryWidth) address(address.high downto boundaryWidth) else U""
val base = address(Math.min(boundaryWidth, address.getWidth) - 1 downto 0).resize(boundaryWidth)
result := (highCat @@ (base + value)).resized
result
}
def apply(access : BmbAccessParameter, invalidation: BmbInvalidationParameter) : Bmb = Bmb(BmbParameter(access, invalidation))
def apply(access : BmbAccessParameter) : Bmb = Bmb(BmbParameter(access, BmbInvalidationParameter()))
def transferBeatCountMinusOneBytesAligned(address : UInt, length : UInt, p : BmbParameter) : UInt = {
((U"0" @@ length) + address(p.access.wordRange))(length.high + 1 downto log2Up(p.access.byteCount))
}
}
case class BmbMasterParameterIdMapping(range : AddressMapping, maximumPendingTransactionPerId : Int)
case class BmbMasterParameter(idMapping : Seq[BmbMasterParameterIdMapping])
case class BmbSlaveParameter(maximumPendingTransactionPerId : Int)
object BmbParameter{
object BurstAlignement {
trait Kind{
def allowByte : Boolean = false
def allowWord : Boolean = false
}
object BYTE extends Kind{
override def allowByte = true
override def allowWord = true
}
object WORD extends Kind{
override def allowWord = true
}
object LENGTH extends Kind
}
def apply(addressWidth : Int,
dataWidth : Int,
sourceWidth : Int,
contextWidth : Int,
lengthWidth : Int,
alignment : BmbParameter.BurstAlignement.Kind = BmbParameter.BurstAlignement.WORD,
alignmentMin : Int = 0,
accessLatencyMin : Int = 1,
canRead : Boolean = true,
canWrite : Boolean = true,
canExclusive : Boolean = false,
maximumPendingTransaction : Int = Int.MaxValue) : BmbParameter = BmbAccessParameter(
addressWidth = addressWidth,
dataWidth = dataWidth
).addSources(1 << sourceWidth, BmbSourceParameter(
contextWidth = contextWidth,
lengthWidth = lengthWidth,
alignment = alignment,
alignmentMin = alignmentMin,
accessLatencyMin = accessLatencyMin,
canRead = canRead,
canWrite = canWrite,
canExclusive = canExclusive,
maximumPendingTransaction = maximumPendingTransaction
)).toBmbParameter()
def apply(access : BmbAccessParameter) : BmbParameter= BmbParameter(access, BmbInvalidationParameter())
}
case class BmbParameter(access : BmbAccessParameter,
invalidation : BmbInvalidationParameter){
}
case class BmbAccessParameter(addressWidth : Int,
dataWidth : Int,
sources : mutable.LinkedHashMap[Int, BmbSourceParameter] = mutable.LinkedHashMap[Int, BmbSourceParameter]()
){
def toBmbParameter() = BmbParameter(this, BmbInvalidationParameter())
assert(dataWidth % 8 == 0)
assert(isPow2(byteCount))
var _self = this
var _aggregated : BmbSourceParameter = null
def aggregated: BmbSourceParameter ={
if(_aggregated == null || _self != this) {
_aggregated = BmbSourceParameter.aggregate(sources.values.toSeq)
_self = this
}
_aggregated
}
def contextWidth = aggregated.contextWidth
def lengthWidth = aggregated.lengthWidth
def alignment = aggregated.alignment
def alignmentMin = aggregated.alignmentMin
def accessLatencyMin = aggregated.accessLatencyMin
def canRead = aggregated.canRead
def canWrite = aggregated.canWrite
def canExclusive = aggregated.canExclusive
def maximumPendingTransaction = aggregated.maximumPendingTransaction
def sourceWidth = log2Up(sources.keys.max + 1)
def canMask = aggregated.canMask
def canInvalidate = aggregated.canInvalidate
def canSync = aggregated.canSync
def sourcesId = sources.map(_._1)
def byteCount = dataWidth/8
def wordMask = byteCount-1
def wordRange = wordRangeLength -1 downto 0
def maskWidth = byteCount
def allowBurst = lengthWidth > wordRangeLength
def beatCounterWidth = lengthWidth - wordRangeLength + (if(sources.exists(_._2.alignment.allowByte)) 1 else 0)
def wordRangeLength = log2Up(byteCount)
def transferBeatCount = (1 << lengthWidth) / byteCount + (if(sources.exists(_._2.alignment.allowByte)) 1 else 0)
def sourcesTransform(f : BmbSourceParameter => BmbSourceParameter) = {
val p = BmbAccessParameter(
addressWidth = addressWidth,
dataWidth = dataWidth,
sources = mutable.LinkedHashMap[Int, BmbSourceParameter]()
)
for((k,v) <- sources){
p.sources(k) = f(v)
}
p
}
def sourcesRemap(f : Int => Int) = {
val p = BmbAccessParameter(
addressWidth = addressWidth,
dataWidth = dataWidth,
sources = mutable.LinkedHashMap[Int, BmbSourceParameter]()
)
for((k,v) <- sources){
p.sources(f(k)) = v
}
p
}
def withSingleSource(source : BmbSourceParameter) = copy(sources = mutable.LinkedHashMap(0 -> source))
def toAccessCapabilities() = BmbAccessCapabilities(
addressWidth = addressWidth,
dataWidth = dataWidth,
sourceWidthMax = sourceWidth,
contextWidthMax = contextWidth,
lengthWidthMax = lengthWidth,
alignment = alignment,
alignmentMin = alignmentMin,
accessLatencyMin = accessLatencyMin,
canRead = canRead,
canWrite = canWrite,
canMask = canMask,
canExclusive = canExclusive,
canInvalidate = canInvalidate,
canSync = canSync,
maximumPendingTransaction = maximumPendingTransaction
)
def randSource() = sources.keys.toArray.apply(Random.nextInt(sources.size))
def addSources(count : Int, p: BmbSourceParameter): this.type ={
for(i <- 0 until count) sources(i) = p
this
}
}
case class BmbAccessCapabilities(addressWidth : Int,
dataWidth : Int,
sourceWidthMax : Int = Int.MaxValue,
contextWidthMax : Int = Int.MaxValue,
lengthWidthMax : Int = Int.MaxValue,
alignment : BmbParameter.BurstAlignement.Kind = BmbParameter.BurstAlignement.WORD,
alignmentMin : Int = 0,
accessLatencyMin : Int = 1,
canRead : Boolean = true,
canWrite : Boolean = true,
canMask : Boolean = true,
canExclusive : Boolean = false,
maximumPendingTransaction : Int = Int.MaxValue,
canInvalidate : Boolean = false,
canSync : Boolean = false){
def toBmbParameter = BmbParameter(BmbAccessParameter(
addressWidth = addressWidth,
dataWidth = dataWidth
).addSources(1 << sourceWidthMax, BmbSourceParameter(
contextWidth = contextWidthMax,
lengthWidth = lengthWidthMax,
alignment = alignment,
alignmentMin = alignmentMin,
accessLatencyMin = accessLatencyMin,
canRead = canRead,
canWrite = canWrite,
canMask = canMask,
canExclusive = canExclusive,
maximumPendingTransaction = maximumPendingTransaction,
canInvalidate = canInvalidate,
canSync = canSync
)))
}
object BmbSourceParameter{
def aggregate(l : Seq[BmbSourceParameter]) = {
var contextWidth = 0
var lengthWidth = 0
var alignment : BmbParameter.BurstAlignement.Kind = BmbParameter.BurstAlignement.LENGTH
var alignmentMin : Int = Int.MaxValue
var accessLatencyMin : Int = Int.MaxValue
var canRead = false
var canWrite = false
var canMask = false
var canExclusive = false
var maximumPendingTransaction : Int = 0
var withCachedRead = false
var canInvalidate = false
var canSync = false
for(s <- l){
contextWidth = contextWidth.max(s.contextWidth)
lengthWidth = lengthWidth.max(s.lengthWidth)
if(alignment == BmbParameter.BurstAlignement.LENGTH) alignment = s.alignment
else if (alignment == BmbParameter.BurstAlignement.WORD && s.alignment != BmbParameter.BurstAlignement.LENGTH) alignment = s.alignment
alignmentMin = alignmentMin.min(s.alignmentMin)
accessLatencyMin = accessLatencyMin.min(s.accessLatencyMin)
canRead |= s.canRead
canWrite |= s.canWrite
canMask |= s.canWrite && s.canMask
canExclusive |= s.canExclusive
withCachedRead |= s.withCachedRead
canInvalidate |= s.canInvalidate
canSync |= s.canSync
maximumPendingTransaction = maximumPendingTransaction.max(s.maximumPendingTransaction)
}
BmbSourceParameter(
contextWidth = contextWidth,
lengthWidth = lengthWidth,
alignment = alignment,
alignmentMin = alignmentMin,
accessLatencyMin = accessLatencyMin,
canRead = canRead,
canWrite = canWrite,
canMask = canMask,
canExclusive = canExclusive,
withCachedRead = withCachedRead,
maximumPendingTransaction = maximumPendingTransaction,
canInvalidate = canInvalidate,
canSync = canSync
)
}
}
case class BmbSourceParameter(contextWidth : Int,
lengthWidth : Int,
alignment : BmbParameter.BurstAlignement.Kind = BmbParameter.BurstAlignement.WORD,
alignmentMin : Int = 0,
accessLatencyMin : Int = 1,
canRead : Boolean = true,
canWrite : Boolean = true,
canMask : Boolean = true,
canExclusive : Boolean = false,
withCachedRead : Boolean = false,
maximumPendingTransaction : Int = Int.MaxValue,
canInvalidate : Boolean = false,
canSync : Boolean = false)
case class BmbInvalidationParameter(invalidateLength : Int = 0,
invalidateAlignment : BmbParameter.BurstAlignement.Kind = BmbParameter.BurstAlignement.WORD)
//case class BmbParameter(addressWidth : Int,
// dataWidth : Int,
// lengthWidth : Int,
// sourceWidth : Int,
// contextWidth : Int,
// alignment : BmbParameter.BurstAlignement.Kind = BmbParameter.BurstAlignement.WORD,
// alignmentMin : Int = 0,
// accessLatencyMin : Int = 1,
// canRead : Boolean = true,
// canWrite : Boolean = true,
// canExclusive : Boolean = false,
// canInvalidate : Boolean = false,
// canSync : Boolean = false,
// invalidateLength : Int = 0,
// invalidateAlignment : BmbParameter.BurstAlignement.Kind = BmbParameter.BurstAlignement.WORD,
// maximumPendingTransactionPerId : Int = Int.MaxValue){
// assert(dataWidth % 8 == 0)
// assert(isPow2(byteCount))
// def byteCount = dataWidth/8
// def wordMask = byteCount-1
// def wordRange = wordRangeLength -1 downto 0
// def maskWidth = byteCount
// def allowBurst = lengthWidth > wordRangeLength
// def beatCounterWidth = lengthWidth - wordRangeLength + (if(alignment.allowByte) 1 else 0)
// def wordRangeLength = log2Up(byteCount)
// def transferBeatCount = (1 << lengthWidth) / byteCount + (if(alignment.allowByte) 1 else 0)
//
// def toAccessParameter = BmbAccessParameter(
// addressWidth = addressWidth,
// dataWidth = dataWidth,
// lengthWidth = lengthWidth,
// sourceWidth = sourceWidth,
// contextWidth = contextWidth,
// alignment = alignment,
// alignmentMin = alignmentMin,
// canRead = canRead,
// canWrite = canWrite,
// canExclusive = canExclusive
// )
//}
case class BmbCmd(p : BmbParameter) extends Bundle{
val source = UInt(p.access.sourceWidth bits)
val opcode = Bits(1 bits)
val exclusive = p.access.canExclusive generate Bool()
val address = UInt(p.access.addressWidth bits)
val length = UInt(p.access.lengthWidth bits)
val data = p.access.canWrite generate Bits(p.access.dataWidth bits)
val mask = (p.access.canWrite && p.access.canMask) generate Bits(p.access.maskWidth bits)
val context = Bits(p.access.contextWidth bits)
def isWrite = opcode === Bmb.Cmd.Opcode.WRITE
def isRead = opcode === Bmb.Cmd.Opcode.READ
def setWrite() = opcode := Bmb.Cmd.Opcode.WRITE
def setRead() = opcode := Bmb.Cmd.Opcode.READ
def weakAssignFrom(m : BmbCmd): Unit ={
def s = this
WeakConnector(m, s, m.source, s.source, defaultValue = null, allowUpSize = true , allowDownSize = false, allowDrop = false)
WeakConnector(m, s, m.opcode, s.opcode, defaultValue = null, allowUpSize = false, allowDownSize = false, allowDrop = false)
WeakConnector(m, s, m.address, s.address, defaultValue = null, allowUpSize = false, allowDownSize = true , allowDrop = false)
WeakConnector(m, s, m.length, s.length, defaultValue = null, allowUpSize = true, allowDownSize = false , allowDrop = false)
WeakConnector(m, s, m.data, s.data, defaultValue = () => Bits(m.p.access.dataWidth bits).assignDontCare() , allowUpSize = false, allowDownSize = false, allowDrop = true )
WeakConnector(m, s, m.mask, s.mask, defaultValue = () => Bits(m.p.access.maskWidth bits).assignDontCare() , allowUpSize = false, allowDownSize = false, allowDrop = true)
WeakConnector(m, s, m.context, s.context, defaultValue = null, allowUpSize = true, allowDownSize = false, allowDrop = false)
WeakConnector(m, s, m.exclusive, s.exclusive, defaultValue = () => False, allowUpSize = false, allowDownSize = false, allowDrop = false)
}
def transferBeatCountMinusOne : UInt = {
if(!p.access.alignment.allowByte){
if(p.access.lengthWidth < log2Up(p.access.byteCount)){
U""
} else {
length(length.high downto log2Up(p.access.byteCount))
}
} else {
((U"0" @@ length) + address(p.access.wordRange))(length.high + 1 downto log2Up(p.access.byteCount))
}
}
}
case class BmbRsp(p : BmbParameter) extends Bundle{
val source = UInt(p.access.sourceWidth bits)
val opcode = Bits(1 bits)
val exclusive = p.access.canExclusive generate Bool()
val data = p.access.canRead generate Bits(p.access.dataWidth bits)
val context = Bits(p.access.contextWidth bits)
def isSuccess = opcode === Bmb.Rsp.Opcode.SUCCESS
def isError = opcode === Bmb.Rsp.Opcode.ERROR
def setSuccess() = opcode := Bmb.Rsp.Opcode.SUCCESS
def setError() = opcode := Bmb.Rsp.Opcode.ERROR
def weakAssignFrom(m : BmbRsp): Unit ={
def s = this
WeakConnector(m, s, m.source, s.source, defaultValue = null, allowUpSize = false , allowDownSize = true, allowDrop = false)
WeakConnector(m, s, m.opcode, s.opcode, defaultValue = null, allowUpSize = false, allowDownSize = false, allowDrop = false)
WeakConnector(m, s, m.data, s.data, defaultValue = () => Bits(m.p.access.dataWidth bits).assignDontCare(), allowUpSize = false, allowDownSize = false, allowDrop = true )
WeakConnector(m, s, m.context, s.context, defaultValue = null, allowUpSize = false, allowDownSize = true, allowDrop = false)
WeakConnector(m, s, m.exclusive, s.exclusive, defaultValue = null, allowUpSize = false, allowDownSize = false, allowDrop = true)
}
}
case class BmbInv(p: BmbParameter) extends Bundle{
val all = Bool() //If cleared, should not invalidate the source
val address = UInt(p.access.addressWidth bits)
val length = UInt(p.invalidation.invalidateLength bits)
val source = UInt(p.access.sourceWidth bits)
def weakAssignFrom(m : BmbInv): Unit ={
def s = this
WeakConnector(m, s, m.source , s.source , defaultValue = null, allowUpSize = false, allowDownSize = false, allowDrop = false)
WeakConnector(m, s, m.address, s.address, defaultValue = null, allowUpSize = false, allowDownSize = false, allowDrop = false)
WeakConnector(m, s, m.length , s.length , defaultValue = null, allowUpSize = false, allowDownSize = false, allowDrop = false)
WeakConnector(m, s, m.all , s.all , defaultValue = null, allowUpSize = false, allowDownSize = false, allowDrop = false)
}
def transferBeatCountMinusOne(alignSize : Int) : UInt = {
val wordRange = log2Up(alignSize)-1 downto 0
if(!p.invalidation.invalidateAlignment.allowByte){
length(length.high downto log2Up(alignSize))
} else {
((U"0" @@ length) + address(wordRange))(length.high + 1 downto log2Up(alignSize))
}
}
}
case class BmbAck(p: BmbParameter) extends Bundle{
def weakAssignFrom(m : BmbAck): Unit ={
}
}
case class BmbSync(p: BmbParameter) extends Bundle{
val source = UInt(p.access.sourceWidth bits)
def weakAssignFrom(m : BmbSync): Unit ={
def s = this
WeakConnector(m, s, m.source , s.source , defaultValue = null, allowUpSize = false, allowDownSize = false, allowDrop = false)
}
}
case class Bmb(p : BmbParameter) extends Bundle with IMasterSlave {
val cmd = (p.access.canRead || p.access.canWrite) generate Stream(Fragment(BmbCmd(p)))
val rsp = (p.access.canRead || p.access.canWrite) generate Stream(Fragment(BmbRsp(p))) //Out of order across source
val inv = p.access.canInvalidate generate Stream(BmbInv(p))
val ack = p.access.canInvalidate generate Stream(BmbAck(p)) //In order
val sync = p.access.canSync generate Stream(BmbSync(p)) //In order
override def asMaster(): Unit = {
master(cmd)
slave(rsp)
if(p.access.canInvalidate) {
slave(inv)
master(ack)
}
if(p.access.canSync) {
slave(sync)
}
}
def <<(m : Bmb) : Unit = {
val s = this
s.cmd.arbitrationFrom(m.cmd)
m.rsp.arbitrationFrom(s.rsp)
s.cmd.last := m.cmd.last
m.rsp.last := s.rsp.last
s.cmd.weakAssignFrom(m.cmd)
m.rsp.weakAssignFrom(s.rsp)
if(p.access.canInvalidate){
m.inv.arbitrationFrom(s.inv)
s.ack.arbitrationFrom(m.ack)
m.inv.weakAssignFrom(s.inv)
s.ack.weakAssignFrom(m.ack)
}
if(p.access.canSync){
m.sync.arbitrationFrom(s.sync)
m.sync.weakAssignFrom(s.sync)
}
}
def >>(s : Bmb) : Unit = s << this
def cmdM2sPipe(): Bmb = {
val ret = cloneOf(this)
this.cmd.m2sPipe() >> ret.cmd
this.rsp << ret.rsp
ret
}
def cmdS2mPipe(): Bmb = {
val ret = cloneOf(this)
this.cmd.s2mPipe() >> ret.cmd
this.rsp << ret.rsp
ret
}
def rspdS2mPipe(): Bmb = {
val ret = cloneOf(this)
this.cmd >> ret.cmd
this.rsp << ret.rsp.m2sPipe()
ret
}
def rspdM2sPipe(): Bmb = {
val ret = cloneOf(this)
this.cmd >> ret.cmd
this.rsp << ret.rsp.s2mPipe()
ret
}
def resize(dataWidth : Int): Bmb = this match {
case _ if dataWidth == p.access.dataWidth => this
case _ if dataWidth < p.access.dataWidth => {
val bridge = BmbDownSizerBridge(
inputParameter = p,
outputParameter = BmbDownSizerBridge.outputParameterFrom(p.access, dataWidth).toBmbParameter()
).setCompositeName(this, "downSizer", true)
bridge.io.input << this
bridge.io.output
}
case _ if dataWidth > p.access.dataWidth => {
val bridge = BmbUpSizerBridge(
inputParameter = p,
outputParameter = BmbUpSizerBridge.outputParameterFrom(p.access, dataWidth).toBmbParameter()
).setCompositeName(this, "upSizer", true)
bridge.io.input << this
bridge.io.output
}
}
def unburstify() : Bmb = {
val bridge = BmbUnburstify(p).setCompositeName(this, "unburstify", true)
bridge.io.input << this
bridge.io.output
}
def toWishbone() : Wishbone = {
val bridge = BmbToWishbone(p).setCompositeName(this, "toWishbone", true)
bridge.io.input << this
bridge.io.output
}
def pipelined(cmdValid : Boolean = false,
cmdReady : Boolean = false,
cmdHalfRate : Boolean = false,
rspValid : Boolean = false,
rspReady : Boolean = false,
rspHalfRate : Boolean = false,
invValid : Boolean = false,
invReady : Boolean = false,
invHalfRate : Boolean = false,
ackValid : Boolean = false,
ackReady : Boolean = false,
ackHalfRate : Boolean = false,
syncValid : Boolean = false,
syncReady : Boolean = false,
syncHalfRate : Boolean = false
): Bmb = {
val ret = Bmb(p)
ret.cmd << cmd.pipelined(
m2s = cmdValid,
s2m = cmdReady,
halfRate = cmdHalfRate
)
rsp << ret.rsp.pipelined(
m2s = rspValid,
s2m = rspReady,
halfRate = rspHalfRate
)
if(p.access.canInvalidate){
inv << ret.inv.pipelined(
m2s = invValid,
s2m = invReady,
halfRate = invHalfRate
)
ret.ack << ack.pipelined(
m2s = ackValid,
s2m = ackReady,
halfRate = ackHalfRate
)
}
if(p.access.canSync){
sync << ret.sync.pipelined(
m2s = syncValid,
s2m = syncReady,
halfRate = syncHalfRate
)
}
ret
}
}