Add constructors, converters, and serdes for AXI tunneled over SERDES (AtoS)
This commit is contained in:
parent
fbd66ac87b
commit
db3b2c264c
286
junctions/src/main/scala/atos.scala
Normal file
286
junctions/src/main/scala/atos.scala
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
package junctions
|
||||||
|
|
||||||
|
import Chisel._
|
||||||
|
import scala.math.max
|
||||||
|
import cde.{Parameters, Field}
|
||||||
|
|
||||||
|
trait HasAtosParameters extends HasNastiParameters {
|
||||||
|
// round up to a multiple of 32
|
||||||
|
def roundup(n: Int) = 32 * ((n - 1) / 32 + 1)
|
||||||
|
|
||||||
|
val atosUnionBits = max(nastiXDataBits + nastiWStrobeBits + 1,
|
||||||
|
nastiXIdBits + nastiXBurstBits +
|
||||||
|
nastiXSizeBits + nastiXLenBits + nastiXAddrBits)
|
||||||
|
val atosIdBits = nastiXIdBits
|
||||||
|
val atosTypBits = 2
|
||||||
|
val atosRespBits = nastiXRespBits
|
||||||
|
val atosDataBits = nastiXDataBits
|
||||||
|
|
||||||
|
val atosLenOffset = nastiXAddrBits
|
||||||
|
val atosSizeOffset = atosLenOffset + nastiXLenBits
|
||||||
|
val atosBurstOffset = atosSizeOffset + nastiXSizeBits
|
||||||
|
val atosIdOffset = atosBurstOffset + nastiXBurstBits
|
||||||
|
val atosStrobeOffset = nastiXDataBits
|
||||||
|
val atosLastOffset = atosStrobeOffset + nastiWStrobeBits
|
||||||
|
|
||||||
|
val atosRequestBits = roundup(atosTypBits + atosUnionBits)
|
||||||
|
val atosResponseBits = roundup(atosTypBits + atosIdBits + atosRespBits + atosDataBits + 1)
|
||||||
|
val atosRequestBytes = atosRequestBits / 8
|
||||||
|
val atosResponseBytes = atosResponseBits / 8
|
||||||
|
val atosRequestWords = atosRequestBytes / 4
|
||||||
|
val atosResponseWords = atosResponseBytes / 4
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AtosModule(implicit val p: Parameters)
|
||||||
|
extends Module with HasAtosParameters
|
||||||
|
abstract class AtosBundle(implicit val p: Parameters)
|
||||||
|
extends ParameterizedBundle()(p) with HasAtosParameters
|
||||||
|
|
||||||
|
object AtosRequest {
|
||||||
|
def arType = UInt("b00")
|
||||||
|
def awType = UInt("b01")
|
||||||
|
def wType = UInt("b10")
|
||||||
|
|
||||||
|
def apply(typ: UInt, union: UInt)(implicit p: Parameters): AtosRequest = {
|
||||||
|
val areq = Wire(new AtosRequest)
|
||||||
|
areq.typ := typ
|
||||||
|
areq.union := union
|
||||||
|
areq
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(ar: NastiReadAddressChannel)(implicit p: Parameters): AtosRequest =
|
||||||
|
apply(arType, Cat(ar.id, ar.burst, ar.size, ar.len, ar.addr))
|
||||||
|
|
||||||
|
def apply(aw: NastiWriteAddressChannel)(implicit p: Parameters): AtosRequest =
|
||||||
|
apply(awType, Cat(aw.id, aw.burst, aw.size, aw.len, aw.addr))
|
||||||
|
|
||||||
|
def apply(w: NastiWriteDataChannel)(implicit p: Parameters): AtosRequest =
|
||||||
|
apply(wType, Cat(w.last, w.strb, w.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtosRequest(implicit p: Parameters)
|
||||||
|
extends AtosBundle()(p) with Serializable {
|
||||||
|
val typ = UInt(width = atosTypBits)
|
||||||
|
val union = UInt(width = atosUnionBits)
|
||||||
|
|
||||||
|
def id(dummy: Int = 0) =
|
||||||
|
union(atosIdOffset + nastiXIdBits - 1, atosIdOffset)
|
||||||
|
|
||||||
|
def burst(dummy: Int = 0) =
|
||||||
|
union(atosIdOffset - 1, atosBurstOffset)
|
||||||
|
|
||||||
|
def size(dummy: Int = 0) =
|
||||||
|
union(atosBurstOffset - 1, atosSizeOffset)
|
||||||
|
|
||||||
|
def len(dummy: Int = 0) =
|
||||||
|
union(atosSizeOffset - 1, atosLenOffset)
|
||||||
|
|
||||||
|
def addr(dummy: Int = 0) =
|
||||||
|
union(atosLenOffset - 1, 0)
|
||||||
|
|
||||||
|
def data(dummy: Int = 0) =
|
||||||
|
union(nastiXDataBits - 1, 0)
|
||||||
|
|
||||||
|
def strb(dummy: Int = 0) =
|
||||||
|
union(atosLastOffset - 1, atosStrobeOffset)
|
||||||
|
|
||||||
|
def last(dummy: Int = 0) =
|
||||||
|
union(atosLastOffset)
|
||||||
|
|
||||||
|
def has_addr(dummy: Int = 0) =
|
||||||
|
typ === AtosRequest.arType || typ === AtosRequest.awType
|
||||||
|
|
||||||
|
def has_data(dummy: Int = 0) =
|
||||||
|
typ === AtosRequest.wType
|
||||||
|
|
||||||
|
def is_last(dummy: Int = 0) =
|
||||||
|
typ === AtosRequest.arType || (typ === AtosRequest.wType && last())
|
||||||
|
|
||||||
|
def nbytes: Int = atosRequestBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
object AtosResponse {
|
||||||
|
def rType = UInt("b00")
|
||||||
|
def bType = UInt("b01")
|
||||||
|
|
||||||
|
def apply(typ: UInt, id: UInt, resp: UInt, data: UInt, last: Bool)
|
||||||
|
(implicit p: Parameters): AtosResponse = {
|
||||||
|
val aresp = Wire(new AtosResponse)
|
||||||
|
aresp.typ := typ
|
||||||
|
aresp.id := id
|
||||||
|
aresp.resp := resp
|
||||||
|
aresp.data := data
|
||||||
|
aresp.last := last
|
||||||
|
aresp
|
||||||
|
}
|
||||||
|
|
||||||
|
def apply(r: NastiReadDataChannel)(implicit p: Parameters): AtosResponse =
|
||||||
|
apply(rType, r.id, r.resp, r.data, r.last)
|
||||||
|
|
||||||
|
def apply(b: NastiWriteResponseChannel)(implicit p: Parameters): AtosResponse =
|
||||||
|
apply(bType, b.id, b.resp, UInt(0), Bool(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtosResponse(implicit p: Parameters)
|
||||||
|
extends AtosBundle()(p) with Serializable {
|
||||||
|
val typ = UInt(width = atosTypBits)
|
||||||
|
val id = UInt(width = atosIdBits)
|
||||||
|
val resp = UInt(width = atosRespBits)
|
||||||
|
val last = Bool()
|
||||||
|
val data = UInt(width = atosDataBits)
|
||||||
|
|
||||||
|
def has_data(dummy: Int = 0) = typ === AtosResponse.rType
|
||||||
|
|
||||||
|
def is_last(dummy: Int = 0) = !has_data() || last
|
||||||
|
|
||||||
|
def nbytes: Int = atosResponseBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtosIO(implicit p: Parameters) extends AtosBundle()(p) {
|
||||||
|
val req = Decoupled(new AtosRequest)
|
||||||
|
val resp = Decoupled(new AtosResponse).flip
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtosRequestEncoder(implicit p: Parameters) extends AtosModule()(p) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val ar = Decoupled(new NastiReadAddressChannel).flip
|
||||||
|
val aw = Decoupled(new NastiWriteAddressChannel).flip
|
||||||
|
val w = Decoupled(new NastiWriteDataChannel).flip
|
||||||
|
val req = Decoupled(new AtosRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
val writing = Reg(init = Bool(false))
|
||||||
|
|
||||||
|
io.ar.ready := !writing && io.req.ready
|
||||||
|
io.aw.ready := !writing && !io.ar.valid && io.req.ready
|
||||||
|
io.w.ready := writing && io.req.ready
|
||||||
|
|
||||||
|
io.req.valid := Mux(writing, io.w.valid, io.ar.valid || io.aw.valid)
|
||||||
|
io.req.bits := Mux(writing, AtosRequest(io.w.bits),
|
||||||
|
Mux(io.ar.valid, AtosRequest(io.ar.bits), AtosRequest(io.aw.bits)))
|
||||||
|
|
||||||
|
when (io.aw.fire()) { writing := Bool(true) }
|
||||||
|
when (io.w.fire() && io.w.bits.last) { writing := Bool(false) }
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtosResponseDecoder(implicit p: Parameters) extends AtosModule()(p) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val resp = Decoupled(new AtosResponse).flip
|
||||||
|
val b = Decoupled(new NastiWriteResponseChannel)
|
||||||
|
val r = Decoupled(new NastiReadDataChannel)
|
||||||
|
}
|
||||||
|
|
||||||
|
val is_b = io.resp.bits.typ === AtosResponse.bType
|
||||||
|
val is_r = io.resp.bits.typ === AtosResponse.rType
|
||||||
|
|
||||||
|
io.b.valid := io.resp.valid && is_b
|
||||||
|
io.b.bits := NastiWriteResponseChannel(
|
||||||
|
id = io.resp.bits.id,
|
||||||
|
resp = io.resp.bits.resp)
|
||||||
|
|
||||||
|
io.r.valid := io.resp.valid && is_r
|
||||||
|
io.r.bits := NastiReadDataChannel(
|
||||||
|
id = io.resp.bits.id,
|
||||||
|
data = io.resp.bits.data,
|
||||||
|
last = io.resp.bits.last,
|
||||||
|
resp = io.resp.bits.resp)
|
||||||
|
|
||||||
|
io.resp.ready := (is_b && io.b.ready) || (is_r && io.r.ready)
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtosClientConverter(implicit p: Parameters) extends AtosModule()(p) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val nasti = (new NastiIO).flip
|
||||||
|
val atos = new AtosIO
|
||||||
|
}
|
||||||
|
|
||||||
|
val req_enc = Module(new AtosRequestEncoder)
|
||||||
|
req_enc.io.ar <> io.nasti.ar
|
||||||
|
req_enc.io.aw <> io.nasti.aw
|
||||||
|
req_enc.io.w <> io.nasti.w
|
||||||
|
io.atos.req <> req_enc.io.req
|
||||||
|
|
||||||
|
val resp_dec = Module(new AtosResponseDecoder)
|
||||||
|
resp_dec.io.resp <> io.atos.resp
|
||||||
|
io.nasti.b <> resp_dec.io.b
|
||||||
|
io.nasti.r <> resp_dec.io.r
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtosRequestDecoder(implicit p: Parameters) extends AtosModule()(p) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val req = Decoupled(new AtosRequest).flip
|
||||||
|
val ar = Decoupled(new NastiReadAddressChannel)
|
||||||
|
val aw = Decoupled(new NastiWriteAddressChannel)
|
||||||
|
val w = Decoupled(new NastiWriteDataChannel)
|
||||||
|
}
|
||||||
|
|
||||||
|
val is_ar = io.req.bits.typ === AtosRequest.arType
|
||||||
|
val is_aw = io.req.bits.typ === AtosRequest.awType
|
||||||
|
val is_w = io.req.bits.typ === AtosRequest.wType
|
||||||
|
|
||||||
|
io.ar.valid := io.req.valid && is_ar
|
||||||
|
io.ar.bits := NastiReadAddressChannel(
|
||||||
|
id = io.req.bits.id(),
|
||||||
|
addr = io.req.bits.addr(),
|
||||||
|
size = io.req.bits.size(),
|
||||||
|
len = io.req.bits.len(),
|
||||||
|
burst = io.req.bits.burst())
|
||||||
|
|
||||||
|
io.aw.valid := io.req.valid && is_aw
|
||||||
|
io.aw.bits := NastiWriteAddressChannel(
|
||||||
|
id = io.req.bits.id(),
|
||||||
|
addr = io.req.bits.addr(),
|
||||||
|
size = io.req.bits.size(),
|
||||||
|
len = io.req.bits.len(),
|
||||||
|
burst = io.req.bits.burst())
|
||||||
|
|
||||||
|
io.w.valid := io.req.valid && is_w
|
||||||
|
io.w.bits := NastiWriteDataChannel(
|
||||||
|
data = io.req.bits.data(),
|
||||||
|
strb = io.req.bits.strb(),
|
||||||
|
last = io.req.bits.last())
|
||||||
|
|
||||||
|
io.req.ready := (io.ar.ready && is_ar) ||
|
||||||
|
(io.aw.ready && is_aw) ||
|
||||||
|
(io.w.ready && is_w)
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtosResponseEncoder(implicit p: Parameters) extends AtosModule()(p) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val b = Decoupled(new NastiWriteResponseChannel).flip
|
||||||
|
val r = Decoupled(new NastiReadDataChannel).flip
|
||||||
|
val resp = Decoupled(new AtosResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
val locked = Reg(init = Bool(false))
|
||||||
|
|
||||||
|
io.resp.valid := (io.b.valid && !locked) || io.r.valid
|
||||||
|
io.resp.bits := Mux(io.r.valid,
|
||||||
|
AtosResponse(io.r.bits), AtosResponse(io.b.bits))
|
||||||
|
|
||||||
|
io.b.ready := !locked && !io.r.valid && io.resp.ready
|
||||||
|
io.r.ready := io.resp.ready
|
||||||
|
|
||||||
|
when (io.r.fire() && !io.r.bits.last) { locked := Bool(true) }
|
||||||
|
when (io.r.fire() && io.r.bits.last) { locked := Bool(false) }
|
||||||
|
}
|
||||||
|
|
||||||
|
class AtosManagerConverter(implicit p: Parameters) extends AtosModule()(p) {
|
||||||
|
val io = new Bundle {
|
||||||
|
val atos = (new AtosIO).flip
|
||||||
|
val nasti = new NastiIO
|
||||||
|
}
|
||||||
|
|
||||||
|
val req_dec = Module(new AtosRequestDecoder)
|
||||||
|
val resp_enc = Module(new AtosResponseEncoder)
|
||||||
|
|
||||||
|
req_dec.io.req <> io.atos.req
|
||||||
|
io.atos.resp <> resp_enc.io.resp
|
||||||
|
|
||||||
|
io.nasti.ar <> req_dec.io.ar
|
||||||
|
io.nasti.aw <> req_dec.io.aw
|
||||||
|
io.nasti.w <> req_dec.io.w
|
||||||
|
|
||||||
|
resp_enc.io.b <> io.nasti.b
|
||||||
|
resp_enc.io.r <> io.nasti.r
|
||||||
|
}
|
@ -87,3 +87,96 @@ class NastiIOStreamIOConverter(w: Int)(implicit p: Parameters) extends Module {
|
|||||||
|
|
||||||
when (io.nasti.b.fire()) { write_resp := Bool(false) }
|
when (io.nasti.b.fire()) { write_resp := Bool(false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class StreamNarrower(win: Int, wout: Int) extends Module {
|
||||||
|
require(win > wout, "Stream narrower input width must be larger than input width")
|
||||||
|
require(win % wout == 0, "Stream narrower input width must be multiple of output width")
|
||||||
|
|
||||||
|
val io = new Bundle {
|
||||||
|
val in = Decoupled(new StreamChannel(win)).flip
|
||||||
|
val out = Decoupled(new StreamChannel(wout))
|
||||||
|
}
|
||||||
|
|
||||||
|
val n_pieces = win / wout
|
||||||
|
val buffer = Reg(Bits(width = win))
|
||||||
|
val (piece_idx, pkt_done) = Counter(io.out.fire(), n_pieces)
|
||||||
|
val pieces = Vec.tabulate(n_pieces) { i => buffer(wout * (i + 1) - 1, wout * i) }
|
||||||
|
val last_piece = (piece_idx === UInt(n_pieces - 1))
|
||||||
|
val sending = Reg(init = Bool(false))
|
||||||
|
val in_last = Reg(Bool())
|
||||||
|
|
||||||
|
when (io.in.fire()) {
|
||||||
|
buffer := io.in.bits.data
|
||||||
|
in_last := io.in.bits.last
|
||||||
|
sending := Bool(true)
|
||||||
|
}
|
||||||
|
when (pkt_done) { sending := Bool(false) }
|
||||||
|
|
||||||
|
io.out.valid := sending
|
||||||
|
io.out.bits.data := pieces(piece_idx)
|
||||||
|
io.out.bits.last := in_last && last_piece
|
||||||
|
io.in.ready := !sending
|
||||||
|
}
|
||||||
|
|
||||||
|
class StreamExpander(win: Int, wout: Int) extends Module {
|
||||||
|
require(win < wout, "Stream expander input width must be smaller than input width")
|
||||||
|
require(wout % win == 0, "Stream narrower output width must be multiple of input width")
|
||||||
|
|
||||||
|
val io = new Bundle {
|
||||||
|
val in = Decoupled(new StreamChannel(win)).flip
|
||||||
|
val out = Decoupled(new StreamChannel(wout))
|
||||||
|
}
|
||||||
|
|
||||||
|
val n_pieces = wout / win
|
||||||
|
val buffer = Reg(Vec(n_pieces, UInt(width = win)))
|
||||||
|
val last = Reg(Bool())
|
||||||
|
val collecting = Reg(init = Bool(true))
|
||||||
|
val (piece_idx, pkt_done) = Counter(io.in.fire(), n_pieces)
|
||||||
|
|
||||||
|
when (io.in.fire()) { buffer(piece_idx) := io.in.bits.data }
|
||||||
|
when (pkt_done) { last := io.in.bits.last; collecting := Bool(false) }
|
||||||
|
when (io.out.fire()) { collecting := Bool(true) }
|
||||||
|
|
||||||
|
io.in.ready := collecting
|
||||||
|
io.out.valid := !collecting
|
||||||
|
io.out.bits.data := buffer.toBits
|
||||||
|
io.out.bits.last := last
|
||||||
|
}
|
||||||
|
|
||||||
|
object StreamUtils {
|
||||||
|
def connectStreams(a: StreamIO, b: StreamIO) {
|
||||||
|
a.in <> b.out
|
||||||
|
b.in <> a.out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Serializable {
|
||||||
|
def nbytes: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class Serializer[T <: Data with Serializable](typ: T) extends Module {
|
||||||
|
val io = new Bundle {
|
||||||
|
val in = Decoupled(typ).flip
|
||||||
|
val out = Decoupled(new StreamChannel(8))
|
||||||
|
}
|
||||||
|
|
||||||
|
val narrower = Module(new StreamNarrower(typ.nbytes * 8, 8))
|
||||||
|
narrower.io.in.bits.data := io.in.bits.toBits
|
||||||
|
narrower.io.in.bits.last := Bool(true)
|
||||||
|
narrower.io.in.valid := io.in.valid
|
||||||
|
io.in.ready := narrower.io.in.ready
|
||||||
|
io.out <> narrower.io.out
|
||||||
|
}
|
||||||
|
|
||||||
|
class Deserializer[T <: Data with Serializable](typ: T) extends Module {
|
||||||
|
val io = new Bundle {
|
||||||
|
val in = Decoupled(new StreamChannel(8)).flip
|
||||||
|
val out = Decoupled(typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
val expander = Module(new StreamExpander(8, 8 * typ.nbytes))
|
||||||
|
expander.io.in <> io.in
|
||||||
|
io.out.valid := expander.io.out.valid
|
||||||
|
io.out.bits := typ.cloneType.fromBits(expander.io.out.bits.data)
|
||||||
|
expander.io.out.ready := io.out.ready
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user