diff options
Diffstat (limited to 'src/test/scala/chiselTests')
| -rw-r--r-- | src/test/scala/chiselTests/experimental/TraceSpec.scala | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/src/test/scala/chiselTests/experimental/TraceSpec.scala b/src/test/scala/chiselTests/experimental/TraceSpec.scala new file mode 100644 index 00000000..59548921 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/TraceSpec.scala @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3._ +import chisel3.experimental.ChiselEnum +import chisel3.experimental.Trace._ +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, DesignAnnotation} +import chisel3.util.experimental.InlineInstance +import firrtl.AnnotationSeq +import firrtl.annotations.TargetToken.{Instance, OfModule, Ref} +import firrtl.annotations.{CompleteTarget, InstanceTarget, ReferenceTarget} +import org.scalatest.matchers.should.Matchers + +class TraceSpec extends ChiselFlatSpec with Matchers { + + def refTarget(topName: String, ref: String, path: Seq[(Instance, OfModule)] = Seq()) = + ReferenceTarget(topName, topName, path, ref, Seq()) + + def instTarget(topName: String, instance: String, ofModule: String, path: Seq[(Instance, OfModule)] = Seq()) = + InstanceTarget(topName, topName, path, instance, ofModule) + + def compile(testName: String, gen: () => Module): (os.Path, AnnotationSeq) = { + val testDir = os.Path(createTestDirectory(testName).getAbsolutePath) + val annos = (new ChiselStage).execute( + Array("--target-dir", s"$testDir"), + Seq( + ChiselGeneratorAnnotation(gen) + ) + ) + (testDir, annos) + } + + "TraceFromAnnotations" should "be able to get nested name." in { + class Bundle0 extends Bundle { + val a = UInt(8.W) + val b = Bool() + val c = Enum0.Type + } + + class Bundle1 extends Bundle { + val a = new Bundle0 + val b = Vec(4, Vec(4, Bool())) + } + + class Module0 extends Module { + val i = IO(Input(new Bundle1)) + val o = IO(Output(new Bundle1)) + val r = Reg(new Bundle1) + o := r + r := i + + traceName(r) + traceName(i) + traceName(o) + } + + class Module1 extends Module { + val i = IO(Input(new Bundle1)) + val m0 = Module(new Module0) + m0.i := i + m0.o := DontCare + } + + object Enum0 extends ChiselEnum { + val s0, s1, s2 = Value + } + + val (testDir, annos) = compile("TraceFromAnnotaions", () => new Module1) + val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[Module1] + // out of Builder. + + val oneTarget = finalTarget(annos)(dut.m0.r.a.a).head + val ioTarget = finalTarget(annos)(dut.m0.i.b(1)(2)).head + + val topName = "Module1" + oneTarget should be(refTarget(topName, "r_a_a", Seq(Instance("m0") -> OfModule("Module0")))) + + ioTarget should be(refTarget(topName, "i_b_1_2", Seq(Instance("m0") -> OfModule("Module0")))) + + // Below codes doesn't needs to be a FIRRTL Transform. + def generateVerilatorConfigFile(data: Seq[Data], annos: AnnotationSeq): String = + """`verilator_config + |lint_off -rule unused + |lint_off -rule declfilename + |""".stripMargin + + data + .flatMap(finalTarget(annos)) + .toSet + .map { target: CompleteTarget => + s"""public_flat_rd -module "${target.tokens.collectFirst { case OfModule(m) => m }.get}" -var "${target.tokens.collectFirst { case Ref(r) => r }.get}"""" + } + .mkString("\n") + "\n" + + def verilatorTemplate(data: Seq[Data], annos: AnnotationSeq): String = { + val vpiNames = data.flatMap(finalTarget(annos)).map { ct => + s"""TOP.${ct.circuit}.${ct.path.map { case (Instance(i), _) => i }.mkString(".")}.${ct.tokens.collectFirst { case Ref(r) => r }.get}""" + } + s""" + |#include "V${topName}.h" + |#include "verilated_vpi.h" + |#include <memory> + |#include <verilated.h> + | + |int vpiGetInt(const char name[]) { + | vpiHandle vh1 = vpi_handle_by_name((PLI_BYTE8 *)name, NULL); + | if (!vh1) + | vl_fatal(__FILE__, __LINE__, "sim_main", "No handle found"); + | s_vpi_value v; + | v.format = vpiIntVal; + | vpi_get_value(vh1, &v); + | return v.value.integer; + |} + | + |int main(int argc, char **argv) { + | const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext}; + | contextp->commandArgs(argc, argv); + | const std::unique_ptr<V$topName> top{new V$topName{contextp.get(), "TOP"}}; + | top->reset = 0; + | top->clock = 0; + | int a_b = 1; + | top->i_a_b = a_b; + | bool started = false; + | int ticks = 20; + | while (ticks--) { + | contextp->timeInc(1); + | top->clock = !top->clock; + | if (!top->clock) { + | if (contextp->time() > 1 && contextp->time() < 10) { + | top->reset = 1; + | } else { + | top->reset = 0; + | started = true; + | } + | a_b = a_b ? 0 : 1; + | top->i_a_b = a_b; + | } + | top->eval(); + | VerilatedVpi::callValueCbs(); + | if (started && !top->clock) { + | const int i = top->i_a_b; + | const int o = vpiGetInt("${vpiNames.head}"); + | if (i == o) + | vl_fatal(__FILE__, __LINE__, "sim_main", "${vpiNames.head} should be the old value of Module1.i_a_b"); + | printf("${vpiNames.head}=%d Module1.m0.o_a_b=%d\\n", i, o); + | } + | } + | top->final(); + | return 0; + |} + |""".stripMargin + } + + val config = os.temp(dir = testDir, contents = generateVerilatorConfigFile(Seq(dut.m0.o.a.b), annos)) + val verilog = testDir / s"$topName.v" + val cpp = os.temp(dir = testDir, suffix = ".cpp", contents = verilatorTemplate(Seq(dut.m0.o.a.b), annos)) + val exe = testDir / "obj_dir" / s"V$topName" + os.proc("verilator", "-Wall", "--cc", "--exe", "--build", "--vpi", s"$cpp", s"$verilog", s"$config").call(stdout = os.Inherit, stderr = os.Inherit, cwd = testDir) + assert(os.proc(s"$exe").call(stdout = os.Inherit, stderr = os.Inherit).exitCode == 0, "verilator should exit peacefully") + } + + "TraceFromCollideBundle" should "work" in { + class CollideModule extends Module { + val a = IO(Input(Vec(2, new Bundle { + val b = Flipped(Bool()) + val c = Vec(2, new Bundle { + val d = UInt(2.W) + val e = Flipped(UInt(3.W)) + }) + val c_1_e = UInt(4.W) + }))) + val a_0_c = IO(Output(UInt(5.W))) + val a__0 = IO(Output(UInt(5.W))) + a_0_c := DontCare + a__0 := DontCare + + traceName(a) + traceName(a_0_c) + traceName(a__0) + } + + val (_, annos) = compile("TraceFromCollideBundle", () => new CollideModule) + val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[CollideModule] + + val topName = "CollideModule" + + val a0 = finalTarget(annos)(dut.a(0)) + val a__0 = finalTarget(annos)(dut.a__0).head + val a__0_ref = refTarget(topName, "a__0") + a0.foreach(_ shouldNot be(a__0_ref)) + a__0 should be(a__0_ref) + + val a0_c = finalTarget(annos)(dut.a(0).c) + val a_0_c = finalTarget(annos)(dut.a_0_c).head + val a_0_c_ref = refTarget(topName, "a_0_c") + a0_c.foreach(_ shouldNot be(a_0_c_ref)) + a_0_c should be(a_0_c_ref) + + val a0_c1_e = finalTarget(annos)(dut.a(0).c(1).e).head + val a0_c_1_e = finalTarget(annos)(dut.a(0).c_1_e).head + a0_c1_e should be(refTarget(topName, "a_0_c__1_e")) + a0_c_1_e should be(refTarget(topName, "a_0_c_1_e")) + } + + "Inline should work" should "work" in { + class Module0 extends Module { + val i = IO(Input(Bool())) + val o = IO(Output(Bool())) + traceName(i) + o := !i + } + + class Module1 extends Module { + val i = IO(Input(Bool())) + val o = IO(Output(Bool())) + val m0 = Module(new Module0 with InlineInstance) + m0.i := i + o := m0.o + } + + val (_, annos) = compile("Inline", () => new Module1) + val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[Module1] + + val m0_i = finalTarget(annos)(dut.m0.i).head + m0_i should be(refTarget("Module1", "m0_i")) + } + + "Constant Propagation" should "be turned off by traceName" in { + class Module0 extends Module { + val i = WireDefault(1.U) + val i0 = i + 1.U + val o = IO(Output(UInt(2.W))) + traceName(i0) + o := i0 + } + + val (_, annos) = compile("ConstantProp", () => new Module0) + val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[Module0] + + val i0 = finalTarget(annos)(dut.i0).head + i0 should be(refTarget("Module0", "i0")) + } + + "Nested Module" should "work" in { + class Io extends Bundle { + val i = Input(Bool()) + val o = Output(Bool()) + } + + class Not extends Module { + val io = IO(new Io) + io.o := !io.i + } + + class M1 extends Module { + val io = IO(new Io) + val not = Module(new Not) + not.io <> io + } + + class M2 extends Module { + val io = IO(new Io) + val m1 = Module(new M1 with InlineInstance) + val not = Module(new Not) + + m1.io.i := io.i + not.io.i := io.i + + io.o := m1.io.o && not.io.o + } + + class M3 extends Module { + val io = IO(new Io) + val m2 = Module(new M2) + io <> m2.io + traceName(m2.not) + traceName(m2.m1.not) + } + + val (_, annos) = compile("NestedModule", () => new M3) + val m3 = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[M3] + + val m2_m1_not = finalTarget(annos)(m3.m2.m1.not).head + val m2_not = finalTarget(annos)(m3.m2.not).head + + m2_m1_not should be(instTarget("M3", "m1_not", "Not", Seq(Instance("m2") -> OfModule("M2")))) + m2_not should be(instTarget("M3", "not", "Not", Seq(Instance("m2") -> OfModule("M2")))) + } + + "All traced signal" should "generate" in { + class M extends Module { + val a = Wire(Bool()) + val b = Wire(Vec(2, Bool())) + a := DontCare + b := DontCare + Seq(a, b).foreach(traceName) + } + val (_, annos) = compile("NestedModule", () => new M) + val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[M] + val allTargets = finalTargetMap(annos) + allTargets(dut.a.toAbsoluteTarget) should be (Seq(refTarget("M", "a"))) + allTargets(dut.b.toAbsoluteTarget) should be (Seq( + refTarget("M", "b_0"), + refTarget("M", "b_1"), + )) + allTargets(dut.b(0).toAbsoluteTarget) should be (Seq(refTarget("M", "b_0"))) + allTargets(dut.b(1).toAbsoluteTarget) should be (Seq(refTarget("M", "b_1"))) + } +} |
