refactor(bus): Clean up AXI/TL-UL bridges and tests
This commit refactors the AXI-to-TileLink and TileLink-to-AXI bridge
logic and cleans up the associated test infrastructure.
Key changes:
- Simplified the ready signal logic in `Axi2TLUL.scala` for better
clarity and correctness.
- Moved the cocotb test files for the AXI/TL-UL bridges from
`hdl/chisel/src/bus` to a dedicated `tests/cocotb/tlul` directory.
- Reorganized the Bazel BUILD files to reflect the new test locations
and improve dependency management.
- Added minor delays at the end of the `tlul2axi` tests to facilitate
easier waveform debugging.
Change-Id: I9a2a3c6510d34b010e0ecc2ba1da1db3e1462f2b
diff --git a/hdl/chisel/src/bus/Axi2TLUL.scala b/hdl/chisel/src/bus/Axi2TLUL.scala
index f0a8e57..c5ab3d3 100644
--- a/hdl/chisel/src/bus/Axi2TLUL.scala
+++ b/hdl/chisel/src/bus/Axi2TLUL.scala
@@ -16,6 +16,7 @@
import chisel3._
import chisel3.util._
+import common.KelvinRRArbiter
import kelvin.Parameters
@@ -47,39 +48,45 @@
val write_addr_q = Queue(io.axi.write.addr, entries = 2)
val write_data_q = Queue(io.axi.write.data, entries = 2)
- // Prioritize reads over writes.
- val is_write = write_addr_q.valid && write_data_q.valid
- val is_read = read_addr_q.valid
+ 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
+ }
- io.tl_a.valid := is_read || is_write
- read_addr_q.ready := false.B
- write_addr_q.ready := false.B
- write_data_q.ready := false.B
+ val read_stream = read_addr_q.map(axiToTl(_, None))
- read_addr_q.ready := Mux(is_read, io.tl_a.ready, false.B)
- write_addr_q.ready := !is_read && io.tl_a.ready
- write_data_q.ready := !is_read && io.tl_a.ready
+ 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))
- io.tl_a.bits.opcode := Mux(is_read, TLULOpcodesA.Get.asUInt, TLULOpcodesA.PutFullData.asUInt)
- io.tl_a.bits.param := 0.U
- io.tl_a.bits.address := Mux(is_read, read_addr_q.bits.addr, write_addr_q.bits.addr)
- io.tl_a.bits.source := Mux(is_read, read_addr_q.bits.id, write_addr_q.bits.id)
- io.tl_a.bits.size := Mux(is_read, read_addr_q.bits.size, write_addr_q.bits.size)
- io.tl_a.bits.mask := Mux(is_read, 0.U, write_data_q.bits.strb)
- io.tl_a.bits.data := Mux(is_read, 0.U, write_data_q.bits.data)
- io.tl_a.bits.user := 0.U.asTypeOf(io.tl_a.bits.user)
+ // 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 := 0.U
+ 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, "b10".U, "b00".U)
+ 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)
diff --git a/hdl/chisel/src/bus/BUILD b/hdl/chisel/src/bus/BUILD
index 48737ef..3e2be71 100644
--- a/hdl/chisel/src/bus/BUILD
+++ b/hdl/chisel/src/bus/BUILD
@@ -47,6 +47,13 @@
)
chisel_cc_library(
+ name = "axi2tlul_cc_library",
+ chisel_lib = ":bus",
+ emit_class = "bus.EmitAxi2TLUL",
+ module_name = "Axi2TLUL",
+)
+
+chisel_cc_library(
name = "tlul2axi_cc_library",
chisel_lib = ":bus",
emit_class = "bus.EmitTLUL2Axi",
@@ -54,81 +61,19 @@
)
verilator_cocotb_model(
- name = "tlul2axi_model",
- hdl_toplevel = "TLUL2Axi",
- verilog_source = "//hdl/chisel/src/bus:TLUL2Axi.sv",
- cflags = [],
+ name = "axi2tlul_model",
+ cflags = VERILATOR_BUILD_ARGS,
+ hdl_toplevel = "Axi2TLUL",
trace = True,
-)
-
-# BEGIN_TESTCASES_FOR_tlul2axi_cocotb_test
-TLUL2AXI_TESTCASES = [
- "test_put_request",
- "test_get_request",
- "test_backpressure",
- "test_put_then_get",
-]
-# END_TESTCASES_FOR_tlul2axi_cocotb_test
-
-cocotb_test_suite(
- name = "tlul2axi_cocotb_test",
- simulators = ["verilator", "vcs"],
- testcases = TLUL2AXI_TESTCASES,
- testcases_vname = "TLUL2AXI_TESTCASES",
- tests_kwargs = {
- "hdl_toplevel": "TLUL2Axi",
- "test_module": ["tlul2axi_cocotb_test.py"],
- "size": "large",
- "deps": [
- "@bazel_tools//tools/python/runfiles",
- requirement("tqdm"),
- ],
- "waves": True,
- },
- vcs_verilog_sources = ["//hdl/chisel/src/bus:tlul2axi_cc_library_verilog"],
- verilator_model = ":tlul2axi_model",
-)
-
-chisel_cc_library(
- name = "axi2tlul_cc_library",
- chisel_lib = ":bus",
- emit_class = "bus.EmitAxi2TLUL",
- module_name = "Axi2TLUL",
+ verilog_source = "//hdl/chisel/src/bus:Axi2TLUL.sv",
)
verilator_cocotb_model(
- name = "axi2tlul_model",
- hdl_toplevel = "Axi2TLUL",
- verilog_source = "//hdl/chisel/src/bus:Axi2TLUL.sv",
- cflags = [],
+ name = "tlul2axi_model",
+ cflags = VERILATOR_BUILD_ARGS,
+ hdl_toplevel = "TLUL2Axi",
trace = True,
-)
-
-# BEGIN_TESTCASES_FOR_axi2tlul_cocotb_test
-AXI2TLUL_TESTCASES = [
- "test_write_request",
- "test_read_request",
- "test_read_error",
-]
-# END_TESTCASES_FOR_axi2tlul_cocotb_test
-
-cocotb_test_suite(
- name = "axi2tlul_cocotb_test",
- simulators = ["verilator", "vcs"],
- testcases = AXI2TLUL_TESTCASES,
- testcases_vname = "AXI2TLUL_TESTCASES",
- tests_kwargs = {
- "hdl_toplevel": "Axi2TLUL",
- "test_module": ["axi2tlul_cocotb_test.py"],
- "size": "large",
- "deps": [
- "@bazel_tools//tools/python/runfiles",
- requirement("tqdm"),
- ],
- "waves": True,
- },
- vcs_verilog_sources = ["//hdl/chisel/src/bus:axi2tlul_cc_library_verilog"],
- verilator_model = ":axi2tlul_model",
+ verilog_source = "//hdl/chisel/src/bus:TLUL2Axi.sv",
)
chisel_cc_library(
@@ -189,4 +134,4 @@
hdl_toplevel = "SecdedEncoderTestbench57",
trace = True,
verilog_source = "//hdl/chisel/src/bus:SecdedEncoderTestbench57.sv",
-)
\ No newline at end of file
+)
diff --git a/tests/cocotb/tlul/BUILD b/tests/cocotb/tlul/BUILD
index 9b16b9c..4740777 100644
--- a/tests/cocotb/tlul/BUILD
+++ b/tests/cocotb/tlul/BUILD
@@ -24,6 +24,73 @@
"VCS_TEST_ARGS",
)
+# BEGIN_TESTCASES_FOR_tlul2axi_cocotb_test
+TLUL2AXI_TESTCASES = [
+ "test_put_request",
+ "test_get_request",
+ "test_backpressure",
+ "test_put_then_get",
+]
+# END_TESTCASES_FOR_tlul2axi_cocotb_test
+
+cocotb_test_suite(
+ name = "tlul2axi_cocotb_test",
+ simulators = [
+ "verilator",
+ "vcs",
+ ],
+ testcases = TLUL2AXI_TESTCASES,
+ testcases_vname = "TLUL2AXI_TESTCASES",
+ tests_kwargs = {
+ "hdl_toplevel": "TLUL2Axi",
+ "test_module": ["tlul2axi_cocotb_test.py"],
+ "size": "large",
+ "deps": [
+ "@bazel_tools//tools/python/runfiles",
+ requirement("tqdm"),
+ ],
+ "waves": True,
+ },
+ vcs_verilog_sources = ["//hdl/chisel/src/bus:tlul2axi_cc_library_verilog"],
+ verilator_model = "//hdl/chisel/src/bus:tlul2axi_model",
+ vcs_build_args = VCS_BUILD_ARGS,
+ vcs_test_args = VCS_TEST_ARGS,
+ vcs_defines = VCS_DEFINES,
+)
+
+# BEGIN_TESTCASES_FOR_axi2tlul_cocotb_test
+AXI2TLUL_TESTCASES = [
+ "test_write_request",
+ "test_read_request",
+ "test_read_error",
+]
+# END_TESTCASES_FOR_axi2tlul_cocotb_test
+
+cocotb_test_suite(
+ name = "axi2tlul_cocotb_test",
+ simulators = [
+ "verilator",
+ "vcs",
+ ],
+ testcases = AXI2TLUL_TESTCASES,
+ testcases_vname = "AXI2TLUL_TESTCASES",
+ tests_kwargs = {
+ "hdl_toplevel": "Axi2TLUL",
+ "test_module": ["axi2tlul_cocotb_test.py"],
+ "size": "large",
+ "deps": [
+ "@bazel_tools//tools/python/runfiles",
+ requirement("tqdm"),
+ ],
+ "waves": True,
+ },
+ vcs_verilog_sources = ["//hdl/chisel/src/bus:axi2tlul_cc_library_verilog"],
+ verilator_model = "//hdl/chisel/src/bus:axi2tlul_model",
+ vcs_build_args = VCS_BUILD_ARGS,
+ vcs_test_args = VCS_TEST_ARGS,
+ vcs_defines = VCS_DEFINES,
+)
+
# BEGIN_TESTCASES_FOR_tlul_integrity_cocotb_test
TLUL_INTEGRITY_TESTCASES = [
"test_request_integrity_gen",
diff --git a/hdl/chisel/src/bus/axi2tlul_cocotb_test.py b/tests/cocotb/tlul/axi2tlul_cocotb_test.py
similarity index 99%
rename from hdl/chisel/src/bus/axi2tlul_cocotb_test.py
rename to tests/cocotb/tlul/axi2tlul_cocotb_test.py
index e6b721c..2abe7f4 100644
--- a/hdl/chisel/src/bus/axi2tlul_cocotb_test.py
+++ b/tests/cocotb/tlul/axi2tlul_cocotb_test.py
@@ -179,6 +179,7 @@
dut.io_tl_d_bits_opcode.value = TLUL_OpcodeD.AccessAckData
dut.io_tl_d_bits_source.value = test_source
dut.io_tl_d_bits_data.value = test_data
+ dut.io_tl_d_bits_error.value = 0
dut.io_axi_read_data_ready.value = 1
diff --git a/hdl/chisel/src/bus/tlul2axi_cocotb_test.py b/tests/cocotb/tlul/tlul2axi_cocotb_test.py
similarity index 98%
rename from hdl/chisel/src/bus/tlul2axi_cocotb_test.py
rename to tests/cocotb/tlul/tlul2axi_cocotb_test.py
index e50b25b..e62a48f 100644
--- a/hdl/chisel/src/bus/tlul2axi_cocotb_test.py
+++ b/tests/cocotb/tlul/tlul2axi_cocotb_test.py
@@ -165,6 +165,7 @@
assert not dut.io_tl_d_bits_error.value, "TL D_ERROR should be low"
dut.io_tl_d_ready.value = 0
+ # Allow a few extra cycles for waveform viewing.
await ClockCycles(dut.clock, 5)
@cocotb.test()
@@ -240,6 +241,7 @@
assert not dut.io_tl_d_bits_error.value, "TL D_ERROR should be low"
dut.io_tl_d_ready.value = 0
+ # Allow a few extra cycles for waveform viewing.
await ClockCycles(dut.clock, 5)
@@ -326,6 +328,7 @@
assert not dut.io_tl_d_bits_error.value, "TL D_ERROR should be low"
dut.io_tl_d_ready.value = 0
+ # Allow a few extra cycles for waveform viewing.
await ClockCycles(dut.clock, 5)
@@ -417,4 +420,5 @@
assert dut.io_tl_d_bits_opcode.value == TLUL_OpcodeD.AccessAckData, "TL D_OPCODE should be AccessAckData for Get"
dut.io_tl_d_ready.value = 0
+ # Allow a few extra cycles for waveform viewing.
await ClockCycles(dut.clock, 5)
\ No newline at end of file