diff --git a/dv/d2d/Makefile b/dv/d2d/Makefile new file mode 100644 index 0000000..81287f5 --- /dev/null +++ b/dv/d2d/Makefile @@ -0,0 +1,80 @@ +UCIE_ROOT ?= $(abspath ../..) +SCALA_DIR ?= $(UCIE_ROOT)/scala +BUILD_DIR ?= $(UCIE_ROOT)/build/d2d-dv +GEN_DIR ?= $(BUILD_DIR)/generated +SIM_DIR ?= $(BUILD_DIR)/sim + +DATA_BYTES ?= 32 +SIDEBAND_WIDTH ?= 32 + +ELAB_MAIN ?= edu.berkeley.cs.uciedigital.d2dadapter.D2DAdapterDvElaborate +ELAB_TOP ?= D2DAdapterDvTop +TB_TOP ?= ucie_d2d_dv_top +TEST ?= smoke +REGRESSION_TESTS ?= smoke + +VCS ?= vcs -full64 +VCS_OPTS ?= \ + -sverilog \ + +v2k \ + -timescale=1ns/1ps \ + +lint=all,noVCDE,noONGS,noUI \ + +vcs+lic+wait \ + -debug_access+all \ + +define+D2D_DV_DATA_BYTES=$(DATA_BYTES) \ + +define+D2D_DV_SIDEBAND_WIDTH=$(SIDEBAND_WIDTH) \ + -Mdir=$(SIM_DIR)/csrc \ + -kdb \ + -lca + +TEST_SRCS ?= $(UCIE_ROOT)/dv/d2d/tests/$(TEST)_test.sv + +DV_SRCS ?= \ + $(UCIE_ROOT)/dv/d2d/common/ucie_d2d_dv_pkg.sv \ + $(UCIE_ROOT)/dv/d2d/if/ucie_d2d_fdi_if.sv \ + $(UCIE_ROOT)/dv/d2d/if/ucie_d2d_rdi_if.sv \ + $(UCIE_ROOT)/dv/d2d/checkers/ucie_d2d_common_checkers.sv \ + $(TEST_SRCS) \ + $(UCIE_ROOT)/dv/d2d/tb/ucie_d2d_dv_top.sv +RTL_FILELIST := $(SIM_DIR)/rtl.f +SIMV := $(SIM_DIR)/simv-$(TEST) + +.PHONY: elab force-elab vcs run regression clean + +elab: + @if [ -f "$(GEN_DIR)/$(ELAB_TOP).sv" ]; then \ + echo "Using existing elaborated RTL in $(GEN_DIR). Run 'make force-elab' to regenerate."; \ + else \ + $(MAKE) force-elab; \ + fi + +force-elab: + cd $(SCALA_DIR) && ./mill -i d2ddv.runMain $(ELAB_MAIN) \ + --target-dir $(GEN_DIR) \ + --data-bytes $(DATA_BYTES) \ + --sideband-width $(SIDEBAND_WIDTH) + +$(RTL_FILELIST): elab + mkdir -p $(SIM_DIR) + find $(GEN_DIR) -type f \( -name '*.sv' -o -name '*.v' \) \ + ! -path '$(GEN_DIR)/verification/*' | sort > $(RTL_FILELIST) + +vcs: $(RTL_FILELIST) + @test -f "$(TEST_SRCS)" || (echo "Missing TEST_SRCS=$(TEST_SRCS)"; exit 1) + $(VCS) $(VCS_OPTS) \ + -f $(RTL_FILELIST) \ + $(DV_SRCS) \ + -top $(TB_TOP) \ + -o $(SIMV) \ + -l $(SIM_DIR)/vcs_compile_$(TEST).log + +run: vcs + $(SIMV) -l $(SIM_DIR)/vcs_run_$(TEST).log + +regression: + @for test in $(REGRESSION_TESTS); do \ + $(MAKE) run TEST=$$test || exit $$?; \ + done + +clean: + rm -rf $(BUILD_DIR) diff --git a/dv/d2d/README.md b/dv/d2d/README.md new file mode 100644 index 0000000..06dfc3f --- /dev/null +++ b/dv/d2d/README.md @@ -0,0 +1,59 @@ +# D2D Adapter DV Flow + +This directory is the entry point for SystemVerilog verification of the +elaborated D2D adapter. + +The flow is intentionally split into two parts: + +1. Elaborate the Chisel D2D adapter DV top with Mill. +2. Compile the emitted SystemVerilog plus DV sources with VCS. + +The D2D adapter uses a narrow Mill module, `d2ddv`, so adapter elaboration does +not require the TileLink/Chipyard dependencies used by the full repo tests. + +## Commands + +From this directory: + +```bash +make elab +``` + +This runs: + +```bash +cd ../../scala +./mill -i d2ddv.runMain \ + edu.berkeley.cs.uciedigital.d2dadapter.D2DAdapterDvElaborate \ + --target-dir ../build/d2d-dv/generated \ + --data-bytes 32 \ + --sideband-width 32 +``` + +Build a VCS simulator from the emitted RTL: + +```bash +make vcs +``` + +Run it: + +```bash +make run +``` + +By default, VCS compiles the emitted `D2DAdapterDvTop` under the stable +SystemVerilog wrapper `ucie_d2d_dv_top`. The wrapper exposes fixed `fdi` and +`rdi` interface instances for drivers, monitors, and SVA checkers. + +Override widths as needed: + +```bash +make elab DATA_BYTES=64 SIDEBAND_WIDTH=32 +``` + +## Next Integration Step + +Add new tests by extending or replacing `dv/d2d/tb/ucie_d2d_dv_top.sv`, or by +adding extra files through `DV_SRCS`. Keep tests and assertions pointed at the +wrapper/interface signals rather than generated internal hierarchy. diff --git a/dv/d2d/checkers/ucie_d2d_common_checkers.sv b/dv/d2d/checkers/ucie_d2d_common_checkers.sv new file mode 100644 index 0000000..5a62c65 --- /dev/null +++ b/dv/d2d/checkers/ucie_d2d_common_checkers.sv @@ -0,0 +1,28 @@ +module ucie_d2d_common_checkers ( + input logic clock, + input logic reset, + ucie_d2d_fdi_if fdi, + ucie_d2d_rdi_if rdi +); + + import ucie_d2d_dv_pkg::*; + + default clocking cb @(posedge clock); + endclocking + + fdi_active_requires_rdi_active: assert property ( + disable iff (reset) + fdi.plStateSts == RDI_STATE_ACTIVE |-> rdi.plStateSts == RDI_STATE_ACTIVE + ) else $error("FDI reached Active while RDI was not Active"); + + fdi_linkerror_requires_rdi_linkerror: assert property ( + disable iff (reset) + fdi.plStateSts == RDI_STATE_LINKERROR |-> rdi.plStateSts == RDI_STATE_LINKERROR + ) else $error("FDI reached LinkError while RDI was not LinkError"); + + rdi_stall_ack_requires_stall_req: assert property ( + disable iff (reset) + rdi.lpStallAck |-> rdi.plStallReq + ) else $error("RDI lpStallAck asserted without plStallReq"); + +endmodule diff --git a/dv/d2d/common/ucie_d2d_dv_pkg.sv b/dv/d2d/common/ucie_d2d_dv_pkg.sv new file mode 100644 index 0000000..5b28b8b --- /dev/null +++ b/dv/d2d/common/ucie_d2d_dv_pkg.sv @@ -0,0 +1,36 @@ +package ucie_d2d_dv_pkg; + +`ifndef D2D_DV_DATA_BYTES +`define D2D_DV_DATA_BYTES 32 +`endif + +`ifndef D2D_DV_SIDEBAND_WIDTH +`define D2D_DV_SIDEBAND_WIDTH 32 +`endif + + localparam int DATA_BYTES = `D2D_DV_DATA_BYTES; + localparam int DATA_BITS = DATA_BYTES * 8; + localparam int SIDEBAND_WIDTH = `D2D_DV_SIDEBAND_WIDTH; + localparam int DEFAULT_TEST_TIMEOUT_CYCLES = 1000; + + localparam logic [3:0] RDI_STATE_RESET = 4'h0; + localparam logic [3:0] RDI_STATE_ACTIVE = 4'h1; + localparam logic [3:0] RDI_STATE_LINKERROR = 4'ha; + localparam logic [3:0] RDI_STATE_RETRAIN = 4'hb; + + localparam logic [3:0] RDI_STATE_REQ_NOP = 4'h0; + localparam logic [3:0] RDI_STATE_REQ_ACTIVE = 4'h1; + localparam logic [3:0] RDI_STATE_REQ_LINKERROR = 4'ha; + localparam logic [3:0] RDI_STATE_REQ_RETRAIN = 4'hb; + + function automatic string rdi_state_name(logic [3:0] state); + case (state) + RDI_STATE_RESET: return "Reset"; + RDI_STATE_ACTIVE: return "Active"; + RDI_STATE_LINKERROR: return "LinkError"; + RDI_STATE_RETRAIN: return "Retrain"; + default: return "Unknown"; + endcase + endfunction + +endpackage diff --git a/dv/d2d/if/ucie_d2d_fdi_if.sv b/dv/d2d/if/ucie_d2d_fdi_if.sv new file mode 100644 index 0000000..8543334 --- /dev/null +++ b/dv/d2d/if/ucie_d2d_fdi_if.sv @@ -0,0 +1,56 @@ +interface ucie_d2d_fdi_if #( + parameter int DATA_BITS = 256, + parameter int SIDEBAND_WIDTH = 32 +); + logic lclk; + logic lpIrdy; + logic lpValid; + logic [DATA_BITS-1:0] lpData; + logic plTrdy; + logic plValid; + logic [DATA_BITS-1:0] plData; + logic [3:0] lpStateReq; + logic lpLinkError; + logic [3:0] plStateSts; + logic plInbandPres; + logic plError; + logic plCError; + logic plNfError; + logic plTrainError; + logic plPhyInRecenter; + logic plStallReq; + logic lpStallAck; + logic [2:0] plSpeedmode; + logic plMaxSpeedmode; + logic [2:0] plLnkCfg; + logic plClkReq; + logic plWakeAck; + logic [SIDEBAND_WIDTH-1:0] plCfg; + logic plCfgVld; + logic plCfgCrd; + logic [SIDEBAND_WIDTH-1:0] lpCfg; + logic lpCfgVld; + logic lpCfgCrd; + + // Drive logical protocol inputs from the FDI partner to a safe idle value + task automatic drive_idle(); + lpIrdy = 1'b0; + lpValid = 1'b0; + lpData = '0; + lpStateReq = 4'h0; + lpLinkError = 1'b0; + lpStallAck = 1'b0; + lpCfg = '0; + lpCfgVld = 1'b0; + plCfgCrd = 1'b1; + endtask + + task automatic request_active(); + lpStateReq = 4'h1; + endtask + + task automatic clear_state_request(); + lpStateReq = 4'h0; + endtask + +endinterface diff --git a/dv/d2d/if/ucie_d2d_rdi_if.sv b/dv/d2d/if/ucie_d2d_rdi_if.sv new file mode 100644 index 0000000..82d61c5 --- /dev/null +++ b/dv/d2d/if/ucie_d2d_rdi_if.sv @@ -0,0 +1,69 @@ +interface ucie_d2d_rdi_if #( + parameter int DATA_BITS = 256, + parameter int SIDEBAND_WIDTH = 32 +); + + logic lclk; + logic lpIrdy; + logic lpValid; + logic [DATA_BITS-1:0] lpData; + logic plTrdy; + logic plValid; + logic [DATA_BITS-1:0] plData; + logic [3:0] lpStateReq; + logic lpLinkError; + logic [3:0] plStateSts; + logic plInbandPres; + logic plError; + logic plCError; + logic plNfError; + logic plTrainError; + logic plPhyInRecenter; + logic plStallReq; + logic lpStallAck; + logic [2:0] plSpeedmode; + logic plMaxSpeedmode; + logic [2:0] plLnkCfg; + logic plClkReq; + logic lpClkAck; + logic lpWakeReq; + logic plWakeAck; + logic [SIDEBAND_WIDTH-1:0] plCfg; + logic plCfgVld; + logic plCfgCrd; + logic [SIDEBAND_WIDTH-1:0] lpCfg; + logic lpCfgVld; + logic lpCfgCrd; + + // Drive logical protocol inputs from the RDI/LogPhy partner to idle + task automatic drive_idle(); + plTrdy = 1'b1; + plValid = 1'b0; + plData = '0; + plStateSts = 4'h0; + plInbandPres = 1'b0; + plError = 1'b0; + plCError = 1'b0; + plNfError = 1'b0; + plTrainError = 1'b0; + plPhyInRecenter = 1'b0; + plStallReq = 1'b0; + plSpeedmode = 3'h0; + plMaxSpeedmode = 1'b0; + plLnkCfg = 3'h0; + plClkReq = 1'b0; + plWakeAck = 1'b0; + plCfg = '0; + plCfgVld = 1'b0; + plCfgCrd = 1'b1; + endtask + + task automatic drive_inband_present(); + plInbandPres = 1'b1; + endtask + + task automatic drive_state(logic [3:0] state); + plStateSts = state; + endtask + +endinterface diff --git a/dv/d2d/tb/ucie_d2d_dv_top.sv b/dv/d2d/tb/ucie_d2d_dv_top.sv new file mode 100644 index 0000000..fffa232 --- /dev/null +++ b/dv/d2d/tb/ucie_d2d_dv_top.sv @@ -0,0 +1,126 @@ +`timescale 1ns/1ps + +module ucie_d2d_dv_top; + import ucie_d2d_dv_pkg::*; + + logic clock; + logic reset; + int timeout_cycles; + + ucie_d2d_fdi_if #( + .DATA_BITS(DATA_BITS), + .SIDEBAND_WIDTH(SIDEBAND_WIDTH) + ) fdi(); + + ucie_d2d_rdi_if #( + .DATA_BITS(DATA_BITS), + .SIDEBAND_WIDTH(SIDEBAND_WIDTH) + ) rdi(); + + D2DAdapterDvTop dut ( + .clock (clock), + .reset (reset), + .io_fdi_lclk (fdi.lclk), + .io_fdi_lpIrdy (fdi.lpIrdy), + .io_fdi_lpValid (fdi.lpValid), + .io_fdi_lpData (fdi.lpData), + .io_fdi_plTrdy (fdi.plTrdy), + .io_fdi_plValid (fdi.plValid), + .io_fdi_plData (fdi.plData), + .io_fdi_lpStateReq (fdi.lpStateReq), + .io_fdi_lpLinkError (fdi.lpLinkError), + .io_fdi_plStateSts (fdi.plStateSts), + .io_fdi_plInbandPres (fdi.plInbandPres), + .io_fdi_plError (fdi.plError), + .io_fdi_plCError (fdi.plCError), + .io_fdi_plNfError (fdi.plNfError), + .io_fdi_plTrainError (fdi.plTrainError), + .io_fdi_plPhyInRecenter (fdi.plPhyInRecenter), + .io_fdi_plStallReq (fdi.plStallReq), + .io_fdi_lpStallAck (fdi.lpStallAck), + .io_fdi_plSpeedmode (fdi.plSpeedmode), + .io_fdi_plMaxSpeedmode (fdi.plMaxSpeedmode), + .io_fdi_plLnkCfg (fdi.plLnkCfg), + .io_fdi_plClkReq (fdi.plClkReq), + .io_fdi_plWakeAck (fdi.plWakeAck), + .io_fdi_plCfg (fdi.plCfg), + .io_fdi_plCfgVld (fdi.plCfgVld), + .io_fdi_plCfgCrd (fdi.plCfgCrd), + .io_fdi_lpCfg (fdi.lpCfg), + .io_fdi_lpCfgVld (fdi.lpCfgVld), + .io_fdi_lpCfgCrd (fdi.lpCfgCrd), + .io_rdi_lclk (rdi.lclk), + .io_rdi_lpIrdy (rdi.lpIrdy), + .io_rdi_lpValid (rdi.lpValid), + .io_rdi_lpData (rdi.lpData), + .io_rdi_plTrdy (rdi.plTrdy), + .io_rdi_plValid (rdi.plValid), + .io_rdi_plData (rdi.plData), + .io_rdi_lpStateReq (rdi.lpStateReq), + .io_rdi_lpLinkError (rdi.lpLinkError), + .io_rdi_plStateSts (rdi.plStateSts), + .io_rdi_plInbandPres (rdi.plInbandPres), + .io_rdi_plError (rdi.plError), + .io_rdi_plCError (rdi.plCError), + .io_rdi_plNfError (rdi.plNfError), + .io_rdi_plTrainError (rdi.plTrainError), + .io_rdi_plPhyInRecenter (rdi.plPhyInRecenter), + .io_rdi_plStallReq (rdi.plStallReq), + .io_rdi_lpStallAck (rdi.lpStallAck), + .io_rdi_plSpeedmode (rdi.plSpeedmode), + .io_rdi_plMaxSpeedmode (rdi.plMaxSpeedmode), + .io_rdi_plLnkCfg (rdi.plLnkCfg), + .io_rdi_plClkReq (rdi.plClkReq), + .io_rdi_lpClkAck (rdi.lpClkAck), + .io_rdi_lpWakeReq (rdi.lpWakeReq), + .io_rdi_plWakeAck (rdi.plWakeAck), + .io_rdi_plCfg (rdi.plCfg), + .io_rdi_plCfgVld (rdi.plCfgVld), + .io_rdi_plCfgCrd (rdi.plCfgCrd), + .io_rdi_lpCfg (rdi.lpCfg), + .io_rdi_lpCfgVld (rdi.lpCfgVld), + .io_rdi_lpCfgCrd (rdi.lpCfgCrd) + ); + + ucie_d2d_common_checkers checkers ( + .clock (clock), + .reset (reset), + .fdi (fdi), + .rdi (rdi) + ); + + ucie_d2d_test test ( + .clock (clock), + .reset (reset), + .fdi (fdi), + .rdi (rdi) + ); + + initial begin + clock = 1'b0; + forever #5 clock = ~clock; + end + + initial begin + fdi.drive_idle(); + rdi.drive_idle(); + + reset = 1'b1; + repeat (5) begin + @(posedge clock); + end + reset = 1'b0; + end + + initial begin + if (!$value$plusargs("D2D_TIMEOUT_CYCLES=%d", timeout_cycles)) begin + timeout_cycles = DEFAULT_TEST_TIMEOUT_CYCLES; + end + + repeat (timeout_cycles) begin + @(posedge clock); + end + $fatal(1, "D2D DV test timeout after %0d cycles", timeout_cycles); + end + +endmodule diff --git a/dv/d2d/tests/smoke_test.sv b/dv/d2d/tests/smoke_test.sv new file mode 100644 index 0000000..71cff1c --- /dev/null +++ b/dv/d2d/tests/smoke_test.sv @@ -0,0 +1,26 @@ +module ucie_d2d_test ( + input logic clock, + input logic reset, + ucie_d2d_fdi_if fdi, + ucie_d2d_rdi_if rdi +); + import ucie_d2d_dv_pkg::*; + + initial begin + @(negedge reset); + + rdi.drive_inband_present(); + repeat (5) begin + @(posedge clock); + end + + rdi.drive_state(RDI_STATE_ACTIVE); + repeat (20) begin + @(posedge clock); + end + + $display("D2D DV smoke completed"); + $finish; + end + +endmodule diff --git a/scala/build.mill b/scala/build.mill index c096443..4ae9f07 100644 --- a/scala/build.mill +++ b/scala/build.mill @@ -40,5 +40,27 @@ object `package` extends ScalaModule, PublishModule { ) def testParallelism = false } -} + object d2ddv extends ScalaModule { + def moduleDir = super.moduleDir / os.up + def scalaVersion = "2.13.18" + def chiselVersion = "7.8.0" + + def mvnDeps = Seq( + mvn"org.chipsalliance::chisel:${chiselVersion}", + ) + + def scalacPluginMvnDeps = Seq( + mvn"org.chipsalliance:::chisel-plugin:${chiselVersion}" + ) + + def sources = Task.Sources( + "src/interfaces", + "src/sideband/utils", + "src/sideband/modules", + "src/sideband/D2DSidebandChannel.scala", + "src/utils/SkidBuffer.scala", + "src/d2dadapter", + ) + } +} diff --git a/scala/src/d2dadapter/D2DAdapter.scala b/scala/src/d2dadapter/D2DAdapter.scala index cbd86b8..ac59d99 100644 --- a/scala/src/d2dadapter/D2DAdapter.scala +++ b/scala/src/d2dadapter/D2DAdapter.scala @@ -34,6 +34,7 @@ class D2DAdapter(val fdiParams: FdiParams, val rdiParams: RdiParams, io.rdi.lclk := DontCare // Default protocol-facing status outputs derived from RDI. + io.fdi.lclk := clock.asBool io.fdi.plSpeedmode := io.rdi.plSpeedmode io.fdi.plMaxSpeedmode := io.rdi.plMaxSpeedmode io.fdi.plLnkCfg := io.rdi.plLnkCfg diff --git a/scala/src/d2dadapter/D2DAdapterDvTop.scala b/scala/src/d2dadapter/D2DAdapterDvTop.scala new file mode 100644 index 0000000..dd2021d --- /dev/null +++ b/scala/src/d2dadapter/D2DAdapterDvTop.scala @@ -0,0 +1,45 @@ +package edu.berkeley.cs.uciedigital.d2dadapter + +import chisel3._ +import circt.stage.ChiselStage +import edu.berkeley.cs.uciedigital.interfaces._ +import edu.berkeley.cs.uciedigital.sideband._ + +class D2DAdapterDvTop(dataBytes: Int, sidebandWidth: Int) extends Module { + val fdiParams = FdiParams(width = dataBytes, sbWidth = sidebandWidth) + val rdiParams = RdiParams(nBytes = dataBytes, ncWidth = sidebandWidth) + val sbParams = SidebandParams() + + val io = IO(new D2DAdapterIO(fdiParams, rdiParams)) + + val dut = Module(new D2DAdapter(fdiParams, rdiParams, sbParams)) + dut.io <> io +} + +object D2DAdapterDvElaborate extends App { + private def argValue(name: String, default: String): String = { + args + .sliding(2) + .collectFirst { case Array(flag, value) if flag == name => value } + .getOrElse(default) + } + + val targetDir = argValue("--target-dir", "build/d2d-dv/generated") + val dataBytes = argValue("--data-bytes", "32").toInt + val sidebandWidth = argValue("--sideband-width", "32").toInt + + ChiselStage.emitSystemVerilogFile( + new D2DAdapterDvTop(dataBytes, sidebandWidth), + args = Array( + "--target-dir", + targetDir + ), + firtoolOpts = Array( + "-O=debug", + "-g", + "--disable-all-randomization", + "--strip-debug-info", + "--lowering-options=disallowLocalVariables" + ) + ) +}