blob: c5ab3d36490b0e6a29f4155bc477ed444696a42d [file] [log] [blame]
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bus
import chisel3._
import chisel3.util._
import common.KelvinRRArbiter
import kelvin.Parameters
/**
* Axi2TLUL: A Chisel module that serves as a bridge between an AXI4 master
* and a TileLink-UL slave.
*
* This module translates AXI read and write transactions into TileLink Get and Put
* operations, respectively. It uses a dataflow approach with queues to manage
* the protocol conversion.
*
* Note: This implementation handles single-beat AXI transactions (len=0). AXI
* bursting would require more complex logic to be added.
*
* @param p The Kelvin parameters.
*/
class Axi2TLUL[A_USER <: Data, D_USER <: Data](p: Parameters, userAGen: () => A_USER, userDGen: () => D_USER) extends Module {
val tlul_p = new TLULParameters(p)
val io = IO(new Bundle {
val axi = Flipped(new AxiMasterIO(p.axi2AddrBits, p.axi2DataBits, p.axi2IdBits))
val tl_a = Decoupled(new TileLink_A_ChannelBase(tlul_p, userAGen)) // TileLink Output
val tl_d = Flipped(Decoupled(new TileLink_D_ChannelBase(tlul_p, userDGen))) // TileLink Input
})
assert(io.axi.read.addr.bits.len === 0.U || !io.axi.read.addr.valid, "Axi2TLUL: AXI read bursts not supported")
assert(io.axi.write.addr.bits.len === 0.U || !io.axi.write.addr.valid, "Axi2TLUL: AXI write bursts not supported")
val read_addr_q = Queue(io.axi.read.addr, entries = 2)
val write_addr_q = Queue(io.axi.write.addr, entries = 2)
val write_data_q = Queue(io.axi.write.data, entries = 2)
private def axiToTl(addr: AxiAddress, data: Option[AxiWriteData]): TileLink_A_ChannelBase[A_USER] = {
val tl_a = Wire(new TileLink_A_ChannelBase(tlul_p, userAGen))
tl_a.opcode := data.map(_ => TLULOpcodesA.PutFullData.asUInt).getOrElse(TLULOpcodesA.Get.asUInt)
tl_a.param := 0.U
tl_a.address := addr.addr
tl_a.source := addr.id
tl_a.size := addr.size
tl_a.mask := data.map(_.strb).getOrElse(0.U(tlul_p.w.W))
tl_a.data := data.map(_.data).getOrElse(0.U((8 * p.axi2DataBits).W))
tl_a.user := 0.U.asTypeOf(io.tl_a.bits.user)
tl_a
}
val read_stream = read_addr_q.map(axiToTl(_, None))
val write_stream = Wire(Decoupled(new TileLink_A_ChannelBase(tlul_p, userAGen)))
write_stream.valid := write_addr_q.valid && write_data_q.valid
write_stream.bits := axiToTl(write_addr_q.bits, Some(write_data_q.bits))
// Reads are given higher priority.
val arb = Module(new KelvinRRArbiter(new TileLink_A_ChannelBase(tlul_p, userAGen), 2))
arb.io.in(0) <> read_stream
arb.io.in(1) <> write_stream
io.tl_a <> arb.io.out
write_addr_q.ready := write_stream.fire
write_data_q.ready := write_stream.fire
val d_is_write = io.tl_d.bits.opcode === TLULOpcodesD.AccessAck.asUInt
val d_is_read = io.tl_d.bits.opcode === TLULOpcodesD.AccessAckData.asUInt
io.axi.write.resp.valid := io.tl_d.valid && d_is_write
io.axi.write.resp.bits.id := io.tl_d.bits.source
io.axi.write.resp.bits.resp := Mux(io.tl_d.bits.error, AxiResponseType.SLVERR.asUInt, AxiResponseType.OKAY.asUInt)
io.axi.read.data.valid := io.tl_d.valid && d_is_read
io.axi.read.data.bits.id := io.tl_d.bits.source
io.axi.read.data.bits.data := io.tl_d.bits.data
io.axi.read.data.bits.resp := Mux(io.tl_d.bits.error, AxiResponseType.SLVERR.asUInt, AxiResponseType.OKAY.asUInt)
io.axi.read.data.bits.last := true.B
io.tl_d.ready := Mux(d_is_read, io.axi.read.data.ready, io.axi.write.resp.ready)
}
import _root_.circt.stage.{ChiselStage,FirtoolOption}
import chisel3.stage.ChiselGeneratorAnnotation
import scala.annotation.nowarn
@nowarn
object EmitAxi2TLUL extends App {
val p = Parameters()
(new ChiselStage).execute(
Array("--target", "systemverilog") ++ args,
Seq(ChiselGeneratorAnnotation(() => new Axi2TLUL(p, () => new NoUser, () => new NoUser))) ++ Seq(FirtoolOption("-enable-layers=Verification"))
)
}