diff --git a/fpga/Makefile b/fpga/Makefile index b53e4fdf73..df460449c8 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -14,6 +14,9 @@ sim_name := none ######################################################################################### # include shared variables ######################################################################################### + +VIVADO_VERSION := $(shell vivado -version | head -1 | grep -o -P '(?<=\s).*(?=\s)') + SUB_PROJECT ?= vcu118 ifeq ($(SUB_PROJECT),vcu118) @@ -56,7 +59,25 @@ ifeq ($(SUB_PROJECT),arty) TB ?= none # unused TOP ?= ChipTop BOARD ?= arty + FPGA_FAMILY ?= kintex7 + FPGA_BRAND ?= xilinx +endif + +ifeq ($(SUB_PROJECT),artysim) + # TODO: Fix with Arty + SBT_PROJECT ?= fpga_platforms + MODEL ?= ArtyFPGATestHarness + VLOG_MODEL ?= ArtyFPGATestHarness + MODEL_PACKAGE ?= chipyard.fpga.arty + CONFIG ?= TinyRocketArtySimConfig + CONFIG_PACKAGE ?= chipyard.fpga.arty + GENERATOR_PACKAGE ?= chipyard + TB ?= ArtyTestDriver + TOP ?= ChipTop + BOARD ?= arty + FPGA_FAMILY ?= kintex7 FPGA_BRAND ?= xilinx + sim_name = arty endif include $(base_dir)/variables.mk @@ -77,7 +98,8 @@ fpga_common_script_dir := $(fpga_dir)/common/tcl # setup misc. sim files ######################################################################################### SIM_FILE_REQS += \ - $(ROCKETCHIP_RSRCS_DIR)/vsrc/EICG_wrapper.v + $(ROCKETCHIP_RSRCS_DIR)/vsrc/EICG_wrapper.v \ + $(base_dir)/generators/chipyard/src/main/resources/vsrc/ArtyTestDriver.v # copy files but ignore *.h files in *.f (match vcs) $(sim_files): $(SIM_FILE_REQS) | $(build_dir) @@ -118,7 +140,7 @@ $(BIT_FILE): $(synth_list_f) -tclargs \ -top-module "$(MODEL)" \ -F "$(synth_list_f)" \ - -board "$(BOARD)" \ + -board "$(BOARD)" \ -ip-vivado-tcls "$(shell find '$(build_dir)' -name '*.vivado.tcl')" .PHONY: bitstream @@ -135,6 +157,80 @@ debug-bitstream: $(build_dir)/obj/post_synth.dcp $(build_dir)/debug_obj \ $(fpga_common_script_dir) +######################################################################################### +# sim rules +######################################################################################### + +project-creation-script := $(sim_dir)/scripts/create_project.tcl + +# generate the vivado project and add all sources +vivado-project := $(build_dir)/$(MODEL).xpr +$(vivado-project): $(synth_list_f) $(project-creation-script) + cd $(build_dir); vivado \ + -nojournal -mode batch \ + -source $(project-creation-script) \ + -tclargs \ + -top-module "$(MODEL)" \ + -F "$(synth_list_f)" \ + -ip-vivado-tcls "$(shell find '$(build_dir)' -name '*.vivado.tcl')" \ + -board "$(BOARD)" + +# generate vcs collateral +vivado-vcs-collateral := $(build_dir)/$(MODEL).cache/compile_simlib/synopsys_sim.setup +$(vivado-vcs-collateral): $(sim_dir)/scripts/generate_vcs_collateral.tcl $(vivado-project) + cd $(build_dir); vivado $(vivado-project) \ + -nojournal -mode batch \ + -source $(sim_dir)/scripts/generate_vcs_collateral.tcl \ + -tclargs \ + $(FPGA_FAMILY) \ + $(build_dir) \ + $(MODEL) \ + $(TB) + +include $(base_dir)/vcs.mk + +sim_prefix = simv +sim = $(build_dir)/$(sim_prefix)-$(MODEL_PACKAGE)-$(CONFIG) + +fpga_sim_verilog_sources = $(build_dir)/fpga_sim_verilog_sources.f +fpga_sim_vhdl_sources = $(build_dir)/fpga_sim_vhdl_sources.f +fpga_sim_cc_sources = $(build_dir)/fpga_sim_cc_sources.f + +xilinx_lib = xil_defaultlib + +VCS = vcs -full64 +VLOGAN = vlogan -full64 +VHDLAN = vhdlan -full64 + +# todo: deduplication between this and other VCS sim makefile +VLOGAN_OPTS = \ + -notice \ + -line \ + +lint=all,noVCDE,noONGS,noUI \ + -error=PCWM-L \ + -error=noZMMCM \ + -timescale=1ns/10ps \ + -quiet \ + -q \ + +rad \ + +vcs+lic+wait \ + +vc+list \ + -sverilog +systemverilogext+.sv+.svi+.svh+.svt -assert svaext +libext+.sv \ + +v2k +verilog2001ext+.v95+.vt+.vp +libext+.v \ + -debug_pp \ + +incdir+$(build_dir) \ + $(PREPROC_DEFINES) + +# vcs-mx flow +$(sim): $(vivado-vcs-collateral) + mkdir -p $(build_dir)/vcs_lib/xil_defaultlib + cd $(build_dir); $(VHDLAN) -work $(xilinx_lib) -f $(fpga_sim_vhdl_sources) -l vhdlan.log + cd $(build_dir); $(VLOGAN) $(VLOGAN_OPTS) +define+DEBUG -work $(xilinx_lib) -f $(fpga_sim_verilog_sources) -l vlogan.log + cd $(build_dir); $(VCS) $(VCS_CC_OPTS) $(xilinx_lib).$(TB) $(xilinx_lib).glbl -file $(fpga_sim_cc_sources) -o $@ -Mdir=$(build_dir)/$(long_name) -l vcs.log -debug_access+all + +.PHONY: sim-fpga +sim-fpga: $(sim) + ######################################################################################### # general cleanup rules ######################################################################################### diff --git a/fpga/scripts/create_project.tcl b/fpga/scripts/create_project.tcl new file mode 100644 index 0000000000..cadccb39b2 --- /dev/null +++ b/fpga/scripts/create_project.tcl @@ -0,0 +1,9 @@ +# Set the variable for the directory that includes all scripts + +set scriptdir [file join [file dirname [info script]] "../fpga-shells/xilinx/common/tcl"] + +# Set up variables and Vivado objects +source [file join $scriptdir "prologue.tcl"] + +# Initialize Vivado project files +source [file join $scriptdir "init.tcl"] \ No newline at end of file diff --git a/fpga/scripts/generate_vcs_collateral.tcl b/fpga/scripts/generate_vcs_collateral.tcl new file mode 100644 index 0000000000..cc93138cac --- /dev/null +++ b/fpga/scripts/generate_vcs_collateral.tcl @@ -0,0 +1,34 @@ +#### Command line arguments to this script +# argv[0] = fpga family +# argv[1] = build directory +# argv[2] = chipyard model definition +# argv[3] = test driver + +set family [lindex $argv 0] +set build_dir [lindex $argv 1] +set model [lindex $argv 2] +set tb [lindex $argv 3] + +# compile the simulation libraries for vcs +compile_simlib -directory $build_dir/$model.cache/compile_simlib -family $family -simulator vcs_mx -library all +# set the top level of the design +set_property top $tb [current_fileset -simset] +# generate other vcs simulation collateral +export_simulation -force -simulator vcs -ip_user_files_dir $build_dir/$model.ip_user_files -lib_map_path $build_dir/$model.cache/compile_simlib -use_ip_compiled_libs -directory $build_dir/export_sim +# Add vivado library mapping to synopsys_sim.setup file +set synopsys_libraries [open $build_dir/synopsys_sim.setup a] +puts $synopsys_libraries "xil_defaultlib : vcs_lib/xil_defaultlib" +close $synopsys_libraries +# generate separate lists of verilog, vhdl, and cc sim sources +set fpga_sim_verilog_sources [open $build_dir/fpga_sim_verilog_sources.f w] +foreach source [get_files -compile_order sources -used_in simulation -filter {FILE_TYPE == Verilog}] {puts $fpga_sim_verilog_sources $source} +foreach source [get_files -compile_order sources -used_in simulation -filter {FILE_TYPE == SystemVerilog}] {puts $fpga_sim_verilog_sources $source} +# add vivado's glbl.v +puts $fpga_sim_verilog_sources $build_dir/export_sim/vcs/glbl.v +close $fpga_sim_verilog_sources +set fpga_sim_vhdl_sources [open $build_dir/fpga_sim_vhdl_sources.f w] +foreach source [get_files -compile_order sources -used_in simulation -filter {FILE_TYPE == VHDL}] {puts $fpga_sim_vhdl_sources $source} +close $fpga_sim_vhdl_sources +set fpga_sim_cc_sources [open $build_dir/fpga_sim_cc_sources.f w] +foreach source [get_files *.cc] {puts $fpga_sim_cc_sources $source} +close $fpga_sim_cc_sources diff --git a/fpga/src/main/scala/arty/Configs.scala b/fpga/src/main/scala/arty/Configs.scala index 66391b4130..b24de5143e 100644 --- a/fpga/src/main/scala/arty/Configs.scala +++ b/fpga/src/main/scala/arty/Configs.scala @@ -38,4 +38,11 @@ class WithArtyTweaks extends Config( class TinyRocketArtyConfig extends Config( new WithArtyTweaks ++ new chipyard.TinyRocketConfig) + +class TinyRocketArtySimConfig extends Config( + new WithFPGASimSerial ++ + new testchipip.WithDefaultSerialTL ++ + new chipyard.harness.WithSimSerial ++ + new chipyard.harness.WithTiedOffDebug ++ + new TinyRocketArtyConfig) // DOC include end: AbstractArty and Rocket diff --git a/fpga/src/main/scala/arty/HarnessBinders.scala b/fpga/src/main/scala/arty/HarnessBinders.scala index aaec1a8600..ef4bb06c00 100644 --- a/fpga/src/main/scala/arty/HarnessBinders.scala +++ b/fpga/src/main/scala/arty/HarnessBinders.scala @@ -15,11 +15,24 @@ import sifive.fpgashells.ip.xilinx.{IBUFG, IOBUF, PULLUP, PowerOnResetFPGAOnly} import chipyard.harness.{ComposeHarnessBinder, OverrideHarnessBinder} import chipyard.iobinders.JTAGChipIO +import testchipip._ + +import chisel3.util._ +import chisel3.experimental.IO +import freechips.rocketchip.config.{Parameters, Field} +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.devices.debug.HasPeripheryDebug +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.util._ +import freechips.rocketchip.prci.{ClockSinkDomain} +import scala.math.min + class WithArtyResetHarnessBinder extends ComposeHarnessBinder({ (system: HasPeripheryDebugModuleImp, th: ArtyFPGATestHarness, ports: Seq[Bool]) => { require(ports.size == 2) - withClockAndReset(th.clock_32MHz, th.ck_rst) { + withClockAndReset(th.clock_32MHz, th.hReset) { // Debug module reset th.dut_ndreset := ports(0) @@ -29,6 +42,32 @@ class WithArtyResetHarnessBinder extends ComposeHarnessBinder({ } }) +class WithFPGASimSerial extends OverrideHarnessBinder({ + (system: CanHavePeripheryTLSerial, th: ArtyFPGATestHarness, ports: Seq[ClockedIO[SerialIO]]) => { + // This binder is the main difference between FPGA and Chipyard simulation. + // For FPGA sim, we want to wire the output of the Xilinx reset IP to + // the sim ram and serial modules, rather than connect the harness reset + // directly. + implicit val p = chipyard.iobinders.GetSystemParameters(system) + ports.map({ port => + val bits = SerialAdapter.asyncQueue(port, th.buildtopClock, th.buildtopReset) + withClockAndReset(th.buildtopClock, th.buildtopReset.asBool) { + val ram = SerialAdapter.connectHarnessRAM(system.serdesser.get, bits, th.buildtopReset.asBool) + + val success = { + val sim = Module(new SimSerial(ram.module.io.tsi_ser.w)) + sim.io.clock := port.clock + sim.io.reset := th.buildtopReset.asBool + sim.io.serial <> ram.module.io.tsi_ser + sim.io.exit + } + + when (success) { th.success := true.B } + } + }) + } +}) + class WithArtyJTAGHarnessBinder extends OverrideHarnessBinder({ (system: HasPeripheryDebug, th: ArtyFPGATestHarness, ports: Seq[Data]) => { ports.map { @@ -71,7 +110,7 @@ class WithArtyJTAGHarnessBinder extends OverrideHarnessBinder({ class WithArtyUARTHarnessBinder extends OverrideHarnessBinder({ (system: HasPeripheryUARTModuleImp, th: ArtyFPGATestHarness, ports: Seq[UARTPortIO]) => { - withClockAndReset(th.clock_32MHz, th.ck_rst) { + withClockAndReset(th.clock_32MHz, th.hReset) { IOBUF(th.uart_rxd_out, ports.head.txd) ports.head.rxd := IOBUF(th.uart_txd_in) } diff --git a/fpga/src/main/scala/arty/TestHarness.scala b/fpga/src/main/scala/arty/TestHarness.scala index fdb91a4771..2584c2a6d8 100644 --- a/fpga/src/main/scala/arty/TestHarness.scala +++ b/fpga/src/main/scala/arty/TestHarness.scala @@ -11,6 +11,8 @@ import chipyard.{BuildTop, HasHarnessSignalReferences} import chipyard.harness.{ApplyHarnessBinders} import chipyard.iobinders.{HasIOBinders} +import testchipip.{SerialTLKey} + class ArtyFPGATestHarness(override implicit val p: Parameters) extends ArtyShell with HasHarnessSignalReferences { val lazyDut = LazyModule(p(BuildTop)(p)).suggestName("chiptop") @@ -22,16 +24,24 @@ class ArtyFPGATestHarness(override implicit val p: Parameters) extends ArtyShell val dReset = Wire(AsyncReset()) dReset := reset_core.asAsyncReset - // default to 32MHz clock + // Default to 32MHz clock withClockAndReset(clock_32MHz, hReset) { val dut = Module(lazyDut.module) } - val buildtopClock = clock_32MHz - val buildtopReset = hReset - val success = false.B + // Set SRST_n (JTAG reset, active-low) to true unless overridden in the JTAG + // harness binder. This is necessary because the Xilinx reset IP depends on it + // in fpga-shells, and the simulation config does not include JTAG. + SRST_n := true.B + val buildtopClock = clock_32MHz + val buildtopReset = dReset val dutReset = dReset + val success = IO(Output(Bool())) + + // This will be overridden by the WithFPGASimSerial harness binder to set + // success to the output of the sim serial module. + success := false.B // must be after HasHarnessSignalReferences assignments lazyDut match { case d: HasIOBinders => diff --git a/generators/chipyard/src/main/resources/vsrc/ArtyTestDriver.v b/generators/chipyard/src/main/resources/vsrc/ArtyTestDriver.v new file mode 100644 index 0000000000..eb4a6ec871 --- /dev/null +++ b/generators/chipyard/src/main/resources/vsrc/ArtyTestDriver.v @@ -0,0 +1,177 @@ +// See LICENSE.SiFive for license details. +//VCS coverage exclude_file +`ifndef RESET_DELAY + `define RESET_DELAY 777.7 +`endif +`ifndef CLOCK_PERIOD + `define CLOCK_PERIOD 1.0 +`endif +`ifndef MODEL + `define MODEL ArtyFPGATestHarness +`endif + +module ArtyTestDriver; + + reg clock = 1'b0; + reg reset = 1'b1; + // the ArtyFPGATestHarness expects an active low reset, because the reset + // switch on the dev board is also active low. the rest of the TestDriver + // logic depends on active high reset. just give resetn to the test harness. + wire resetn = !reset; + + always #(`CLOCK_PERIOD/2.0) clock = ~clock; + initial #(`RESET_DELAY) reset = 0; + + // Read input arguments and initialize + reg verbose = 1'b0; + wire printf_cond = verbose && !reset; + reg [63:0] max_cycles = 0; + reg [63:0] dump_start = 0; + reg [63:0] trace_count = 0; + reg [2047:0] fsdbfile = 0; + reg [2047:0] vcdplusfile = 0; + reg [2047:0] vcdfile = 0; + int unsigned rand_value; + initial + begin + void'($value$plusargs("max-cycles=%d", max_cycles)); + void'($value$plusargs("dump-start=%d", dump_start)); + verbose = $test$plusargs("verbose"); + + // do not delete the lines below. + // $random function needs to be called with the seed once to affect all + // the downstream $random functions within the Chisel-generated Verilog + // code. + // $urandom is seeded via cmdline (+ntb_random_seed in VCS) but that + // doesn't seed $random. + rand_value = $urandom; + rand_value = $random(rand_value); + if (verbose) begin +`ifdef VCS + $fdisplay(stderr, "testing $random %0x seed %d", rand_value, unsigned'($get_initial_random_seed)); +`else + $fdisplay(stderr, "testing $random %0x", rand_value); +`endif + end + +`ifdef DEBUG + + if ($value$plusargs("vcdplusfile=%s", vcdplusfile)) + begin +`ifdef VCS + $vcdplusfile(vcdplusfile); +`else + $fdisplay(stderr, "Error: +vcdplusfile is VCS-only; use +vcdfile instead or recompile with VCS=1"); + $fatal; +`endif + end + + if ($value$plusargs("fsdbfile=%s", fsdbfile)) + begin +`ifdef FSDB + $fsdbDumpfile(fsdbfile); + $fsdbDumpvars("+all"); + //$fsdbDumpSVA; +`else + $fdisplay(stderr, "Error: +fsdbfile is FSDB-only; use +vcdfile/+vcdplus instead or recompile with FSDB=1"); + $fatal; +`endif + end + + if ($value$plusargs("vcdfile=%s", vcdfile)) + begin + $dumpfile(vcdfile); + $dumpvars(0, testHarness); + end + +`ifdef FSDB +`define VCDPLUSON $fsdbDumpon; +`define VCDPLUSCLOSE $fsdbDumpoff; +`elsif VCS +`define VCDPLUSON $vcdpluson(0); $vcdplusmemon(0); +`define VCDPLUSCLOSE $vcdplusclose; $dumpoff; +`else +`define VCDPLUSON $dumpon; +`define VCDPLUSCLOSE $dumpoff; +`endif +`else + // No +define+DEBUG +`define VCDPLUSON +`define VCDPLUSCLOSE + + if ($test$plusargs("vcdplusfile=") || $test$plusargs("vcdfile=") || $test$plusargs("fsdbfile=")) + begin + $fdisplay(stderr, "Error: +vcdfile, +vcdplusfile, or +fsdbfile requested but compile did not have +define+DEBUG enabled"); + $fatal; + end + +`endif + + if (dump_start == 0) + begin + // Start dumping before first clock edge to capture reset sequence in waveform + `VCDPLUSON + end + end + +`ifdef TESTBENCH_IN_UVM + // UVM library has its own way to manage end-of-simulation. + // A UVM-based testbench will raise an objection, watch this signal until this goes 1, then drop the objection. + reg finish_request = 1'b0; +`endif + reg [255:0] reason = ""; + reg failure = 1'b0; + wire success; + integer stderr = 32'h80000002; + always @(posedge clock) + begin +`ifdef GATE_LEVEL + if (verbose) + begin + $fdisplay(stderr, "C: %10d", trace_count); + end +`endif + + trace_count = trace_count + 1; + + if (trace_count == dump_start) + begin + `VCDPLUSON + end + + if (!reset) + begin + if (max_cycles > 0 && trace_count > max_cycles) + begin + reason = " (timeout)"; + failure = 1'b1; + end + + if (failure) + begin + $fdisplay(stderr, "*** FAILED ***%s after %d simulation cycles", reason, trace_count); + `VCDPLUSCLOSE + $fatal; + end + + if (success) + begin + if (verbose) + $fdisplay(stderr, "*** PASSED *** Completed after %d simulation cycles", trace_count); + `VCDPLUSCLOSE +`ifdef TESTBENCH_IN_UVM + finish_request = 1; +`else + $finish; +`endif + end + end + end + + `MODEL testHarness( + .CLK100MHZ(clock), + .ck_rst(resetn), + .success(success) + ); + +endmodule diff --git a/sims/common-sim-flags.mk b/sims/common-sim-flags.mk index 7c6c580d6e..3a24e29a60 100644 --- a/sims/common-sim-flags.mk +++ b/sims/common-sim-flags.mk @@ -15,7 +15,7 @@ SIM_CXXFLAGS = \ SIM_LDFLAGS = \ $(LDFLAGS) \ -L$(RISCV)/lib \ - -Wl,-rpath,$(RISCV)/lib \ + -Wl,--no-as-needed,-rpath,$(RISCV)/lib \ -L$(sim_dir) \ -L$(dramsim_dir) \ -lfesvr \