tilelink2: add a RegisterCrossing primitive
This commit is contained in:
parent
d75f9d6a34
commit
fe6a67dd0e
@ -17,6 +17,12 @@ object RegReadFn
|
||||
// effects must become visible on the cycle after 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
|
||||
// valid must eventually go high without requiring ready to go high
|
||||
@ -47,6 +53,13 @@ object RegWriteFn
|
||||
// effects must become visible on the cycle after ovalid && oready
|
||||
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)
|
||||
// ready must eventually go high without requiring valid to go high
|
||||
|
130
src/main/scala/uncore/tilelink2/RegisterCrossing.scala
Normal file
130
src/main/scala/uncore/tilelink2/RegisterCrossing.scala
Normal file
@ -0,0 +1,130 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
package uncore.tilelink2
|
||||
|
||||
import Chisel._
|
||||
import junctions._
|
||||
|
||||
// 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 = Decoupled(Bool()) // ignore .bits
|
||||
}
|
||||
|
||||
// To turn on/off a domain:
|
||||
// 1. lower allow on the other side
|
||||
// 2. wait for inflight traffic to resolve
|
||||
// 3. turn off the domain
|
||||
// 4. assert reset in 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
|
||||
}
|
||||
|
||||
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 || !io.master_allow
|
||||
crossing.io.deq_clock := io.slave_clock
|
||||
crossing.io.deq_reset := io.slave_reset || !io.slave_allow
|
||||
|
||||
crossing.io.enq.bits := io.master_port.request.bits
|
||||
io.slave_register := crossing.io.deq.bits
|
||||
|
||||
// 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 = Decoupled(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 || !io.slave_allow
|
||||
crossing.io.deq_clock := io.master_clock
|
||||
crossing.io.deq_reset := io.master_reset || !io.master_allow
|
||||
|
||||
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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user