1
0
rocket-chip/src/main/scala/uncore/tilelink2/RegisterCrossing.scala

186 lines
6.7 KiB
Scala
Raw Normal View History

// See LICENSE for license details.
package uncore.tilelink2
import Chisel._
import chisel3.util.{Irrevocable, IrrevocableIO}
import util.{AsyncResetRegVec, AsyncQueue, AsyncScope}
// 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 = Irrevocable(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 = Irrevocable(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)
}
2016-09-15 01:30:59 +02:00
/** 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.
2016-09-15 01:30:59 +02:00
*/
object AsyncRWSlaveRegField {
def apply(slave_clock: Clock,
slave_reset: Bool,
width: Int,
init: Int,
master_allow: Bool = Bool(true),
slave_allow: Bool = Bool(true)
): (UInt, RegField) = {
val async_slave_reg = Module(new AsyncResetRegVec(width, init))
async_slave_reg.reset := slave_reset
async_slave_reg.clock := slave_clock
val wr_crossing = Module (new RegisterWriteCrossing(UInt(width = width)))
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 )))
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))
}
}