tilelink2: move general-purpose code out of tilelink2 package
This commit is contained in:
115
src/main/scala/regmapper/RegField.scala
Normal file
115
src/main/scala/regmapper/RegField.scala
Normal file
@ -0,0 +1,115 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package regmapper
|
||||
|
||||
import Chisel._
|
||||
import chisel3.util.{ReadyValidIO}
|
||||
import util.{SimpleRegIO}
|
||||
|
||||
case class RegReadFn private(combinational: Boolean, fn: (Bool, Bool) => (Bool, Bool, UInt))
|
||||
object RegReadFn
|
||||
{
|
||||
// (ivalid: Bool, oready: Bool) => (iready: Bool, ovalid: Bool, data: UInt)
|
||||
// iready may combinationally depend on oready
|
||||
// all other combinational dependencies forbidden (e.g. ovalid <= ivalid)
|
||||
// effects must become visible on the cycle after ovalid && oready
|
||||
// data is only inspected when ovalid && oready
|
||||
implicit def apply(x: (Bool, Bool) => (Bool, Bool, UInt)) =
|
||||
new RegReadFn(false, x)
|
||||
implicit def apply(x: RegisterReadIO[UInt]): RegReadFn =
|
||||
RegReadFn((ivalid, oready) => {
|
||||
x.request.valid := ivalid
|
||||
x.response.ready := oready
|
||||
(x.request.ready, x.response.valid, x.response.bits)
|
||||
})
|
||||
// (ready: Bool) => (valid: Bool, data: UInt)
|
||||
// valid must not combinationally depend on ready
|
||||
// effects must become visible on the cycle after valid && ready
|
||||
implicit def apply(x: Bool => (Bool, UInt)) =
|
||||
new RegReadFn(true, { case (_, oready) =>
|
||||
val (ovalid, data) = x(oready)
|
||||
(Bool(true), ovalid, data)
|
||||
})
|
||||
// read from a ReadyValidIO (only safe if there is a consistent source of data)
|
||||
implicit def apply(x: ReadyValidIO[UInt]):RegReadFn = RegReadFn(ready => { x.ready := ready; (x.valid, x.bits) })
|
||||
// read from a register
|
||||
implicit def apply(x: UInt):RegReadFn = RegReadFn(ready => (Bool(true), x))
|
||||
// noop
|
||||
implicit def apply(x: Unit):RegReadFn = RegReadFn(UInt(0))
|
||||
}
|
||||
|
||||
case class RegWriteFn private(combinational: Boolean, fn: (Bool, Bool, UInt) => (Bool, Bool))
|
||||
object RegWriteFn
|
||||
{
|
||||
// (ivalid: Bool, oready: Bool, data: UInt) => (iready: Bool, ovalid: Bool)
|
||||
// iready may combinationally depend on both oready and data
|
||||
// all other combinational dependencies forbidden (e.g. ovalid <= ivalid)
|
||||
// effects must become visible on the cycle after ovalid && oready
|
||||
// data should only be used for an effect when ivalid && iready
|
||||
implicit def apply(x: (Bool, Bool, UInt) => (Bool, Bool)) =
|
||||
new RegWriteFn(false, x)
|
||||
implicit def apply(x: RegisterWriteIO[UInt]): RegWriteFn =
|
||||
RegWriteFn((ivalid, oready, data) => {
|
||||
x.request.valid := ivalid
|
||||
x.request.bits := data
|
||||
x.response.ready := oready
|
||||
(x.request.ready, x.response.valid)
|
||||
})
|
||||
// (valid: Bool, data: UInt) => (ready: Bool)
|
||||
// ready may combinationally depend on data (but not valid)
|
||||
// effects must become visible on the cycle after valid && ready
|
||||
implicit def apply(x: (Bool, UInt) => Bool) =
|
||||
// combinational => data valid on oready
|
||||
new RegWriteFn(true, { case (_, oready, data) =>
|
||||
(Bool(true), x(oready, data))
|
||||
})
|
||||
// write to a DecoupledIO (only safe if there is a consistent sink draining data)
|
||||
// NOTE: this is not an IrrevocableIO (even on TL2) because other fields could cause a lowered valid
|
||||
implicit def apply(x: DecoupledIO[UInt]): RegWriteFn = RegWriteFn((valid, data) => { x.valid := valid; x.bits := data; x.ready })
|
||||
// updates a register (or adds a mux to a wire)
|
||||
implicit def apply(x: UInt): RegWriteFn = RegWriteFn((valid, data) => { when (valid) { x := data }; Bool(true) })
|
||||
// noop
|
||||
implicit def apply(x: Unit): RegWriteFn = RegWriteFn((valid, data) => { Bool(true) })
|
||||
}
|
||||
|
||||
case class RegField(width: Int, read: RegReadFn, write: RegWriteFn, name: String, description: String)
|
||||
{
|
||||
require (width > 0)
|
||||
def pipelined = !read.combinational || !write.combinational
|
||||
}
|
||||
|
||||
object RegField
|
||||
{
|
||||
// Byte address => sequence of bitfields, lowest index => lowest address
|
||||
type Map = (Int, Seq[RegField])
|
||||
|
||||
def apply(n: Int) : RegField = apply(n, (), (), "", "")
|
||||
def apply(n: Int, r: RegReadFn, w: RegWriteFn) : RegField = apply(n, r, w, "", "")
|
||||
def apply(n: Int, rw: UInt) : RegField = apply(n, rw, rw, "", "")
|
||||
def apply(n: Int, rw: UInt, name: String, description: String) : RegField = apply(n, rw, rw, name, description)
|
||||
def r(n: Int, r: RegReadFn, name: String = "", description: String = "") : RegField = apply(n, r, (), name, description)
|
||||
def w(n: Int, w: RegWriteFn, name: String = "", description: String = "") : RegField = apply(n, (), w, name, description)
|
||||
|
||||
// This RegField allows 'set' to set bits in 'reg'.
|
||||
// and to clear bits when the bus writes bits of value 1.
|
||||
// Setting takes priority over clearing.
|
||||
def w1ToClear(n: Int, reg: UInt, set: UInt): RegField =
|
||||
RegField(n, reg, RegWriteFn((valid, data) => { reg := ~(~reg | Mux(valid, data, UInt(0))) | set; Bool(true) }))
|
||||
|
||||
// This RegField wraps an explicit register
|
||||
// (e.g. Black-Boxed Register) to create a R/W register.
|
||||
def rwReg(n: Int, bb: SimpleRegIO) : RegField =
|
||||
RegField(n, bb.q, RegWriteFn((valid, data) => {
|
||||
bb.en := valid
|
||||
bb.d := data
|
||||
Bool(true)
|
||||
}))
|
||||
}
|
||||
|
||||
trait HasRegMap
|
||||
{
|
||||
def regmap(mapping: RegField.Map*): Unit
|
||||
val interrupts: Vec[Bool]
|
||||
}
|
||||
|
||||
// See GPIO.scala for an example of how to use regmap
|
196
src/main/scala/regmapper/RegMapper.scala
Normal file
196
src/main/scala/regmapper/RegMapper.scala
Normal file
@ -0,0 +1,196 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package regmapper
|
||||
|
||||
import Chisel._
|
||||
import diplomacy._
|
||||
import util.{GenericParameterizedBundle}
|
||||
|
||||
// A bus agnostic register interface to a register-based device
|
||||
|
||||
case class RegMapperParams(indexBits: Int, maskBits: Int, extraBits: Int)
|
||||
|
||||
class RegMapperInput(params: RegMapperParams) extends GenericParameterizedBundle(params)
|
||||
{
|
||||
val read = Bool()
|
||||
val index = UInt(width = params.indexBits)
|
||||
val data = UInt(width = params.maskBits*8)
|
||||
val mask = UInt(width = params.maskBits)
|
||||
val extra = UInt(width = params.extraBits)
|
||||
}
|
||||
|
||||
class RegMapperOutput(params: RegMapperParams) extends GenericParameterizedBundle(params)
|
||||
{
|
||||
val read = Bool()
|
||||
val data = UInt(width = params.maskBits*8)
|
||||
val extra = UInt(width = params.extraBits)
|
||||
}
|
||||
|
||||
object RegMapper
|
||||
{
|
||||
// Create a generic register-based device
|
||||
def apply(bytes: Int, concurrency: Int, undefZero: Boolean, in: DecoupledIO[RegMapperInput], mapping: RegField.Map*) = {
|
||||
val bytemap = mapping.toList
|
||||
// Don't be an asshole...
|
||||
bytemap.foreach { byte => require (byte._1 >= 0) }
|
||||
|
||||
// Transform all fields into bit offsets Seq[(bit, field)]
|
||||
val bitmap = bytemap.map { case (byte, fields) =>
|
||||
val bits = fields.scanLeft(byte * 8)(_ + _.width).init
|
||||
bits zip fields
|
||||
}.flatten.sortBy(_._1)
|
||||
|
||||
// Detect overlaps
|
||||
(bitmap.init zip bitmap.tail) foreach { case ((lbit, lfield), (rbit, rfield)) =>
|
||||
require (lbit + lfield.width <= rbit, s"Register map overlaps at bit ${rbit}.")
|
||||
}
|
||||
|
||||
// Group those fields into bus words Map[word, List[(bit, field)]]
|
||||
val wordmap = bitmap.groupBy(_._1 / (8*bytes))
|
||||
|
||||
// Make sure registers fit
|
||||
val inParams = in.bits.params
|
||||
val inBits = inParams.indexBits
|
||||
assert (wordmap.keySet.max < (1 << inBits), "Register map does not fit in device")
|
||||
|
||||
val out = Wire(Decoupled(new RegMapperOutput(inParams)))
|
||||
val front = Wire(Decoupled(new RegMapperInput(inParams)))
|
||||
front.bits := in.bits
|
||||
|
||||
// Must this device pipeline the control channel?
|
||||
val pipelined = wordmap.values.map(_.map(_._2.pipelined)).flatten.reduce(_ || _)
|
||||
val depth = concurrency
|
||||
require (depth >= 0)
|
||||
require (!pipelined || depth > 0, "Register-based device with request/response handshaking needs concurrency > 0")
|
||||
val back = if (depth > 0) Queue(front, depth, pipe = depth == 1) else front
|
||||
|
||||
// Convert to and from Bits
|
||||
def toBits(x: Int, tail: List[Boolean] = List.empty): List[Boolean] =
|
||||
if (x == 0) tail.reverse else toBits(x >> 1, ((x & 1) == 1) :: tail)
|
||||
def ofBits(bits: List[Boolean]) = bits.foldRight(0){ case (x,y) => (if (x) 1 else 0) | y << 1 }
|
||||
|
||||
// Find the minimal mask that can decide the register map
|
||||
val mask = AddressDecoder(wordmap.keySet.toList)
|
||||
val maskMatch = ~UInt(mask, width = inBits)
|
||||
val maskFilter = toBits(mask)
|
||||
val maskBits = maskFilter.filter(x => x).size
|
||||
|
||||
// Calculate size and indexes into the register map
|
||||
val regSize = 1 << maskBits
|
||||
def regIndexI(x: Int) = ofBits((maskFilter zip toBits(x)).filter(_._1).map(_._2))
|
||||
def regIndexU(x: UInt) = if (maskBits == 0) UInt(0) else
|
||||
Cat((maskFilter zip x.toBools).filter(_._1).map(_._2).reverse)
|
||||
|
||||
// Protection flag for undefined registers
|
||||
val iRightReg = Array.fill(regSize) { Bool(true) }
|
||||
val oRightReg = Array.fill(regSize) { Bool(true) }
|
||||
|
||||
// Transform the wordmap into minimal decoded indexes, Seq[(index, bit, field)]
|
||||
val flat = wordmap.toList.map { case (word, fields) =>
|
||||
val index = regIndexI(word)
|
||||
val uint = UInt(word, width = inBits)
|
||||
if (undefZero) {
|
||||
iRightReg(index) = ((front.bits.index ^ uint) & maskMatch) === UInt(0)
|
||||
oRightReg(index) = ((back .bits.index ^ uint) & maskMatch) === UInt(0)
|
||||
}
|
||||
// Confirm that no field spans a word boundary
|
||||
fields foreach { case (bit, field) =>
|
||||
val off = bit - 8*bytes*word
|
||||
// println(s"Reg ${word}: [${off}, ${off+field.width})")
|
||||
require (off + field.width <= bytes * 8, s"Field at word ${word}*(${bytes}B) has bits [${off}, ${off+field.width}), which exceeds word limit.")
|
||||
}
|
||||
// println("mapping 0x%x -> 0x%x for 0x%x/%d".format(word, index, mask, maskBits))
|
||||
fields.map { case (bit, field) => (index, bit-8*bytes*word, field) }
|
||||
}.flatten
|
||||
|
||||
// Forward declaration of all flow control signals
|
||||
val rivalid = Wire(Vec(flat.size, Bool()))
|
||||
val wivalid = Wire(Vec(flat.size, Bool()))
|
||||
val riready = Wire(Vec(flat.size, Bool()))
|
||||
val wiready = Wire(Vec(flat.size, Bool()))
|
||||
val rovalid = Wire(Vec(flat.size, Bool()))
|
||||
val wovalid = Wire(Vec(flat.size, Bool()))
|
||||
val roready = Wire(Vec(flat.size, Bool()))
|
||||
val woready = Wire(Vec(flat.size, Bool()))
|
||||
|
||||
// Per-register list of all control signals needed for data to flow
|
||||
val rifire = Array.tabulate(regSize) { i => Seq(Bool(true)) }
|
||||
val wifire = Array.tabulate(regSize) { i => Seq(Bool(true)) }
|
||||
val rofire = Array.tabulate(regSize) { i => Seq(Bool(true)) }
|
||||
val wofire = Array.tabulate(regSize) { i => Seq(Bool(true)) }
|
||||
|
||||
// The output values for each register
|
||||
val dataOut = Array.tabulate(regSize) { _ => UInt(0) }
|
||||
|
||||
// Which bits are touched?
|
||||
val frontMask = FillInterleaved(8, front.bits.mask)
|
||||
val backMask = FillInterleaved(8, back .bits.mask)
|
||||
|
||||
// Connect the fields
|
||||
for (i <- 0 until flat.size) {
|
||||
val (reg, low, field) = flat(i)
|
||||
val high = low + field.width - 1
|
||||
// Confirm that no register is too big
|
||||
require (high < 8*bytes)
|
||||
val rimask = frontMask(high, low).orR()
|
||||
val wimask = frontMask(high, low).andR()
|
||||
val romask = backMask(high, low).orR()
|
||||
val womask = backMask(high, low).andR()
|
||||
val data = if (field.write.combinational) back.bits.data else front.bits.data
|
||||
val (f_riready, f_rovalid, f_data) = field.read.fn(rivalid(i) && rimask, roready(i) && romask)
|
||||
val (f_wiready, f_wovalid) = field.write.fn(wivalid(i) && wimask, woready(i) && womask, data(high, low))
|
||||
riready(i) := f_riready || !rimask
|
||||
wiready(i) := f_wiready || !wimask
|
||||
rovalid(i) := f_rovalid || !romask
|
||||
wovalid(i) := f_wovalid || !womask
|
||||
rifire(reg) = riready(i) +: rifire(reg)
|
||||
wifire(reg) = wiready(i) +: wifire(reg)
|
||||
rofire(reg) = rovalid(i) +: rofire(reg)
|
||||
wofire(reg) = wovalid(i) +: wofire(reg)
|
||||
dataOut(reg) = dataOut(reg) | ((f_data << low) & (~UInt(0, width = high+1)))
|
||||
}
|
||||
|
||||
// Is the selected register ready?
|
||||
val rifireMux = Vec(rifire.zipWithIndex.map { case (seq, i) => !iRightReg(i) || seq.reduce(_ && _)})
|
||||
val wifireMux = Vec(wifire.zipWithIndex.map { case (seq, i) => !iRightReg(i) || seq.reduce(_ && _)})
|
||||
val rofireMux = Vec(rofire.zipWithIndex.map { case (seq, i) => !oRightReg(i) || seq.reduce(_ && _)})
|
||||
val wofireMux = Vec(wofire.zipWithIndex.map { case (seq, i) => !oRightReg(i) || seq.reduce(_ && _)})
|
||||
val iindex = regIndexU(front.bits.index)
|
||||
val oindex = regIndexU(back .bits.index)
|
||||
val iready = Mux(front.bits.read, rifireMux(iindex), wifireMux(iindex))
|
||||
val oready = Mux(back .bits.read, rofireMux(oindex), wofireMux(oindex))
|
||||
|
||||
// Connect the pipeline
|
||||
in.ready := front.ready && iready
|
||||
front.valid := in.valid && iready
|
||||
back.ready := out.ready && oready
|
||||
out.valid := back.valid && oready
|
||||
|
||||
// Which register is touched?
|
||||
val frontSel = UIntToOH(iindex) & Cat(iRightReg.reverse)
|
||||
val backSel = UIntToOH(oindex) & Cat(oRightReg.reverse)
|
||||
|
||||
// Include the per-register one-hot selected criteria
|
||||
for (reg <- 0 until regSize) {
|
||||
rifire(reg) = (in.valid && front.ready && front.bits.read && frontSel(reg)) +: rifire(reg)
|
||||
wifire(reg) = (in.valid && front.ready && !front.bits.read && frontSel(reg)) +: wifire(reg)
|
||||
rofire(reg) = (back.valid && out.ready && back .bits.read && backSel (reg)) +: rofire(reg)
|
||||
wofire(reg) = (back.valid && out.ready && !back .bits.read && backSel (reg)) +: wofire(reg)
|
||||
}
|
||||
|
||||
// Connect the field's ivalid and oready
|
||||
for (i <- 0 until flat.size) {
|
||||
val (reg, _, _ ) = flat(i)
|
||||
rivalid(i) := rifire(reg).filter(_ ne riready(i)).reduce(_ && _)
|
||||
wivalid(i) := wifire(reg).filter(_ ne wiready(i)).reduce(_ && _)
|
||||
roready(i) := rofire(reg).filter(_ ne rovalid(i)).reduce(_ && _)
|
||||
woready(i) := wofire(reg).filter(_ ne wovalid(i)).reduce(_ && _)
|
||||
}
|
||||
|
||||
out.bits.read := back.bits.read
|
||||
out.bits.data := Mux(Vec(oRightReg)(oindex), Vec(dataOut)(oindex), UInt(0))
|
||||
out.bits.extra := back.bits.extra
|
||||
|
||||
out
|
||||
}
|
||||
}
|
189
src/main/scala/regmapper/RegisterCrossing.scala
Normal file
189
src/main/scala/regmapper/RegisterCrossing.scala
Normal file
@ -0,0 +1,189 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package regmapper
|
||||
|
||||
import Chisel._
|
||||
import chisel3.util.{Irrevocable}
|
||||
import util.{AsyncQueue,AsyncScope,AsyncResetRegVec}
|
||||
|
||||
// A very simple flow control state machine, run in the specified clock domain
|
||||
class BusyRegisterCrossing(clock: Clock, reset: Bool)
|
||||
extends Module(_clock = clock, _reset = reset) {
|
||||
val io = new Bundle {
|
||||
val progress = Bool(INPUT)
|
||||
val request_valid = Bool(INPUT)
|
||||
val response_ready = Bool(INPUT)
|
||||
val busy = Bool(OUTPUT)
|
||||
}
|
||||
|
||||
val busy = RegInit(Bool(false))
|
||||
when (io.progress) {
|
||||
busy := Mux(busy, !io.response_ready, io.request_valid)
|
||||
}
|
||||
io.busy := busy
|
||||
}
|
||||
|
||||
// RegField should support connecting to one of these
|
||||
class RegisterWriteIO[T <: Data](gen: T) extends Bundle {
|
||||
val request = Decoupled(gen).flip()
|
||||
val response = Irrevocable(Bool()) // ignore .bits
|
||||
}
|
||||
|
||||
// To turn on/off a domain:
|
||||
// 1. lower allow on the other side
|
||||
// 2. wait for inflight traffic to resolve
|
||||
// 3. assert reset in the domain
|
||||
// 4. turn off the domain
|
||||
// 5. turn on the domain
|
||||
// 6. deassert reset in the domain
|
||||
// 7. raise allow on the other side
|
||||
|
||||
class RegisterWriteCrossingIO[T <: Data](gen: T) extends Bundle {
|
||||
// Master clock domain
|
||||
val master_clock = Clock(INPUT)
|
||||
val master_reset = Bool(INPUT)
|
||||
val master_allow = Bool(INPUT) // actually wait for the slave
|
||||
val master_port = new RegisterWriteIO(gen)
|
||||
// Slave clock domain
|
||||
val slave_clock = Clock(INPUT)
|
||||
val slave_reset = Bool(INPUT)
|
||||
val slave_allow = Bool(INPUT) // honour requests from the master
|
||||
val slave_register = gen.asOutput
|
||||
val slave_valid = Bool(OUTPUT) // is high on 1st cycle slave_register has a new value
|
||||
}
|
||||
|
||||
class RegisterWriteCrossing[T <: Data](gen: T, sync: Int = 3) extends Module {
|
||||
val io = new RegisterWriteCrossingIO(gen)
|
||||
// The crossing must only allow one item inflight at a time
|
||||
val crossing = Module(new AsyncQueue(gen, 1, sync))
|
||||
|
||||
// We can just randomly reset one-side of a single entry AsyncQueue.
|
||||
// If the enq side is reset, at worst deq.bits is reassigned from mem(0), which stays fixed.
|
||||
// If the deq side is reset, at worst the master rewrites mem(0) once, deq.bits stays fixed.
|
||||
crossing.io.enq_clock := io.master_clock
|
||||
crossing.io.enq_reset := io.master_reset
|
||||
crossing.io.deq_clock := io.slave_clock
|
||||
crossing.io.deq_reset := io.slave_reset
|
||||
|
||||
crossing.io.enq.bits := io.master_port.request.bits
|
||||
io.slave_register := crossing.io.deq.bits
|
||||
io.slave_valid := crossing.io.deq.valid
|
||||
|
||||
// If the slave is not operational, just drop the write.
|
||||
val progress = crossing.io.enq.ready || !io.master_allow
|
||||
|
||||
val reg = Module(new BusyRegisterCrossing(io.master_clock, io.master_reset))
|
||||
reg.io.progress := progress
|
||||
reg.io.request_valid := io.master_port.request.valid
|
||||
reg.io.response_ready := io.master_port.response.ready
|
||||
|
||||
crossing.io.deq.ready := Bool(true)
|
||||
crossing.io.enq.valid := io.master_port.request.valid && !reg.io.busy
|
||||
io.master_port.request.ready := progress && !reg.io.busy
|
||||
io.master_port.response.valid := progress && reg.io.busy
|
||||
}
|
||||
|
||||
// RegField should support connecting to one of these
|
||||
class RegisterReadIO[T <: Data](gen: T) extends Bundle {
|
||||
val request = Decoupled(Bool()).flip() // ignore .bits
|
||||
val response = Irrevocable(gen)
|
||||
}
|
||||
|
||||
class RegisterReadCrossingIO[T <: Data](gen: T) extends Bundle {
|
||||
// Master clock domain
|
||||
val master_clock = Clock(INPUT)
|
||||
val master_reset = Bool(INPUT)
|
||||
val master_allow = Bool(INPUT) // actually wait for the slave
|
||||
val master_port = new RegisterReadIO(gen)
|
||||
// Slave clock domain
|
||||
val slave_clock = Clock(INPUT)
|
||||
val slave_reset = Bool(INPUT)
|
||||
val slave_allow = Bool(INPUT) // honour requests from the master
|
||||
val slave_register = gen.asInput
|
||||
}
|
||||
|
||||
class RegisterReadCrossing[T <: Data](gen: T, sync: Int = 3) extends Module {
|
||||
val io = new RegisterReadCrossingIO(gen)
|
||||
// The crossing must only allow one item inflight at a time
|
||||
val crossing = Module(new AsyncQueue(gen, 1, sync))
|
||||
|
||||
// We can just randomly reset one-side of a single entry AsyncQueue.
|
||||
// If the enq side is reset, at worst deq.bits is reassigned from mem(0), which stays fixed.
|
||||
// If the deq side is reset, at worst the slave rewrites mem(0) once, deq.bits stays fixed.
|
||||
crossing.io.enq_clock := io.slave_clock
|
||||
crossing.io.enq_reset := io.slave_reset
|
||||
crossing.io.deq_clock := io.master_clock
|
||||
crossing.io.deq_reset := io.master_reset
|
||||
|
||||
crossing.io.enq.bits := io.slave_register
|
||||
io.master_port.response.bits := crossing.io.deq.bits
|
||||
|
||||
// If the slave is not operational, just repeat the last value we saw.
|
||||
val progress = crossing.io.deq.valid || !io.master_allow
|
||||
|
||||
val reg = Module(new BusyRegisterCrossing(io.master_clock, io.master_reset))
|
||||
reg.io.progress := progress
|
||||
reg.io.request_valid := io.master_port.request.valid
|
||||
reg.io.response_ready := io.master_port.response.ready
|
||||
|
||||
io.master_port.response.valid := progress && reg.io.busy
|
||||
io.master_port.request.ready := progress && !reg.io.busy
|
||||
crossing.io.deq.ready := io.master_port.request.valid && !reg.io.busy
|
||||
crossing.io.enq.valid := Bool(true)
|
||||
}
|
||||
|
||||
/** Wrapper to create an
|
||||
* asynchronously reset slave register which can be
|
||||
* both read and written
|
||||
* using crossing FIFOs.
|
||||
* The reset and allow assertion & de-assertion
|
||||
* should be synchronous to their respective
|
||||
* domains.
|
||||
*/
|
||||
|
||||
object AsyncRWSlaveRegField {
|
||||
|
||||
def apply(slave_clock: Clock,
|
||||
slave_reset: Bool,
|
||||
width: Int,
|
||||
init: Int,
|
||||
name: Option[String] = None,
|
||||
master_allow: Bool = Bool(true),
|
||||
slave_allow: Bool = Bool(true)
|
||||
): (UInt, RegField) = {
|
||||
|
||||
val async_slave_reg = Module(new AsyncResetRegVec(width, init))
|
||||
name.foreach(async_slave_reg.suggestName(_))
|
||||
async_slave_reg.reset := slave_reset
|
||||
async_slave_reg.clock := slave_clock
|
||||
|
||||
val wr_crossing = Module (new RegisterWriteCrossing(UInt(width = width)))
|
||||
name.foreach(n => wr_crossing.suggestName(s"${n}_wcrossing"))
|
||||
|
||||
val scope = Module (new AsyncScope())
|
||||
|
||||
wr_crossing.io.master_clock := scope.clock
|
||||
wr_crossing.io.master_reset := scope.reset
|
||||
wr_crossing.io.master_allow := master_allow
|
||||
wr_crossing.io.slave_clock := slave_clock
|
||||
wr_crossing.io.slave_reset := slave_reset
|
||||
wr_crossing.io.slave_allow := slave_allow
|
||||
|
||||
async_slave_reg.io.en := wr_crossing.io.slave_valid
|
||||
async_slave_reg.io.d := wr_crossing.io.slave_register
|
||||
|
||||
val rd_crossing = Module (new RegisterReadCrossing(UInt(width = width )))
|
||||
name.foreach(n => rd_crossing.suggestName(s"${n}_rcrossing"))
|
||||
|
||||
rd_crossing.io.master_clock := scope.clock
|
||||
rd_crossing.io.master_reset := scope.reset
|
||||
rd_crossing.io.master_allow := master_allow
|
||||
rd_crossing.io.slave_clock := slave_clock
|
||||
rd_crossing.io.slave_reset := slave_reset
|
||||
rd_crossing.io.slave_allow := slave_allow
|
||||
|
||||
rd_crossing.io.slave_register := async_slave_reg.io.q
|
||||
|
||||
(async_slave_reg.io.q, RegField(width, rd_crossing.io.master_port, wr_crossing.io.master_port))
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user