// See LICENSE.SiFive for license details. package uncore.tilelink2 import Chisel._ import chisel3.internal.sourceinfo.SourceInfo import config._ import diplomacy._ import util.PositionalMultiQueue import uncore.ahb._ import scala.math.{min, max} import AHBParameters._ case class TLToAHBNode() extends MixedNode(TLImp, AHBImp)( dFn = { case (1, Seq(TLClientPortParameters(clients, unsafeAtomics, minLatency))) => val masters = clients.map { case c => AHBMasterParameters(nodePath = c.nodePath) } Seq(AHBMasterPortParameters(masters)) }, uFn = { case (1, Seq(AHBSlavePortParameters(slaves, beatBytes))) => val managers = slaves.map { case s => TLManagerParameters( address = s.address, regionType = s.regionType, executable = s.executable, nodePath = s.nodePath, supportsGet = s.supportsRead, supportsPutFull = s.supportsWrite, // but not PutPartial fifoId = Some(0)) // a common FIFO domain } Seq(TLManagerPortParameters(managers, beatBytes, 1, 1)) }, numPO = 1 to 1, numPI = 1 to 1) class TLToAHB(combinational: Boolean = true)(implicit p: Parameters) extends LazyModule { val node = TLToAHBNode() lazy val module = new LazyModuleImp(this) { val io = new Bundle { val in = node.bundleIn val out = node.bundleOut } val in = io.in(0) val out = io.out(0) val edgeIn = node.edgesIn(0) val edgeOut = node.edgesOut(0) val beatBytes = edgeOut.slave.beatBytes val maxTransfer = edgeOut.slave.maxTransfer val lgMax = log2Ceil(maxTransfer) val lgBytes = log2Ceil(beatBytes) // AHB has no cache coherence in.b.valid := Bool(false) in.c.ready := Bool(true) in.e.ready := Bool(true) // We need a skidpad to capture D output: // We cannot know if the D response will be accepted until we have // presented it on D as valid. We also can't back-pressure AHB in the // data phase. Therefore, we must have enough space to save the data // phase result. Whenever we have a queued response, we can not allow // AHB to present new responses, so we must quash the address phase. val d = Wire(in.d) in.d <> Queue(d, 1, flow = true) val a_quash = in.d.valid && !in.d.ready // Record what is coming out in d_phase val d_valid = RegInit(Bool(false)) val d_hasData = Reg(Bool()) val d_error = Reg(Bool()) val d_addr_lo = Reg(UInt(width = lgBytes)) val d_source = Reg(UInt()) val d_size = Reg(UInt()) when (out.hreadyout) { d_error := d_error || out.hresp } when (d.fire()) { d_valid := Bool(false) } d.valid := d_valid && out.hreadyout d.bits := edgeIn.AccessAck(d_addr_lo, UInt(0), d_source, d_size, out.hrdata, out.hresp || d_error) d.bits.opcode := Mux(d_hasData, TLMessages.AccessAckData, TLMessages.AccessAck) // We need an irrevocable input for AHB to stall on read bursts // We also need the values to NOT change when valid goes low => 1 entry only val a = Queue(in.a, 1, flow = combinational, pipe = !combinational) val a_valid = a.valid && !a_quash // This is lot like TLEdge.firstlast, but counts beats also for single-beat TL types val a_size = edgeIn.size(a.bits) val a_beats1 = UIntToOH1(a_size, lgMax) >> lgBytes val a_counter = RegInit(UInt(0, width = log2Up(maxTransfer/beatBytes))) val a_counter1 = a_counter - UInt(1) val a_first = a_counter === UInt(0) val a_last = a_counter === UInt(1) || a_beats1 === UInt(0) val a_offset = (a_beats1 & ~a_counter1) << lgBytes val a_hasData = edgeIn.hasData(a.bits) // Expand no-data A-channel requests into multiple beats a.ready := (a_hasData || a_last) && out.hreadyout && !a_quash when (a_valid && out.hreadyout) { a_counter := Mux(a_first, a_beats1, a_counter1) d_valid := !a_hasData || a_last // Record what will be in the data phase when (a_first) { d_hasData := !a_hasData d_error := Bool(false) d_addr_lo := a.bits.address d_source := a.bits.source d_size := a.bits.size } } // Transform TL size into AHB hsize+hburst val a_size_bits = a_size.getWidth val a_sizeDelta = Cat(UInt(0, width = 1), a_size) - UInt(lgBytes+1) val a_singleBeat = a_sizeDelta(a_size_bits) val a_logBeats1 = a_sizeDelta(a_size_bits-1, 0) out.hmastlock := Bool(false) // for now out.htrans := Mux(a_valid, Mux(a_first, TRANS_NONSEQ, TRANS_SEQ), Mux(a_first, TRANS_IDLE, TRANS_BUSY)) out.hsel := Bool(true) out.hready := out.hreadyout out.hwrite := a_hasData out.haddr := a.bits.address | a_offset out.hsize := Mux(a_singleBeat, a.bits.size, UInt(lgBytes)) out.hburst := Mux(a_singleBeat, BURST_SINGLE, (a_logBeats1<<1) | UInt(1)) out.hprot := PROT_DEFAULT out.hwdata := RegEnable(a.bits.data, a.fire()) } } object TLToAHB { // applied to the TL source node; y.node := TLToAHB()(x.node) def apply(combinational: Boolean = true)(x: TLOutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): AHBOutwardNode = { val axi4 = LazyModule(new TLToAHB(combinational)) axi4.node := x axi4.node } }