File size: 7,291 Bytes
0ba037b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | # PCIe accelerator: parallel engine, PCIe-facing core, LiteX SoC for the
# LiteFury, and the host program.
#
# make lint verilator -Wall on the engine and the PCIe core
# make sim engine vs the single-op reference core: several
# geometries and seeds (Icarus); core with 128-bit streams.
# The runs are independent targets: use make -j.
# make sim-verilator the engine testbench under Verilator
# make soc generate the LiteX SoC sources and Linux driver (no Vivado)
# make cosim host program (host/bitacc_pcie.c) against the Verilated core
# through a liblitepcie stand-in (needs `make soc` first)
# make perf engine throughput in clock cycles at the LiteFury geometry
# (8 lanes x 2048 words): host bench against the Verilated core
# make timing open-source place and route for the XC7A100T (nextpnr-xilinx;
# set NEXTPNR and CHIPDB, see timing/run.sh): Fmax estimate
# make bitstream full build with Vivado (not available in CI)
#
# The engine testbench fails unless results and final memory equal the
# reference, and unless out-of-order completion, a full reorder buffer, both
# directions of the range barrier and every operation class (single-bit and
# range; in range, out of range, wrapping, undefined) occurred.
#
# A testbench passes only if vvp exits 0 and prints PASS. (Piping vvp into
# grep would report the grep's status, so a failing run would pass.)
IVERILOG ?= iverilog
VVP ?= vvp
VERILATOR ?= verilator
PYTHON ?= python3
BUILD ?= build
OPS ?= 4000
REF := ../rtl/bit_accelerator.sv
ENGINE := rtl/bitacc_engine.sv
CORE := rtl/bitacc_pcie_core.sv
SOC_DIR := $(abspath $(BUILD))/litefury
DRIVER := $(SOC_DIR)/driver
GEOMETRIES := 2x4 4x16 8x16 8x512
SEEDS := 1 7
TB_INC := tb/gold_ops.svh tb/range_stim.svh
# plus 16 lanes, a build without the MATCH unit (opcode 15 must be an error), and
# matchers with fewer units than lanes (run-ml-<lanes>x<words>-<units>)
ENGINE_RUNS := $(foreach g,$(GEOMETRIES),$(foreach s,$(SEEDS),run-$(g)-$(s))) run-16x8-1
# $(call vvp_check,command,log): run, show the summary, fail unless exit 0 and PASS.
define vvp_check
$(1) > $(2) 2>&1; rc=$$?; grep -E '^(stimulus|range coverage|match coverage|checks|PASS|FAIL)' $(2); \
test $$rc -eq 0 && grep -q '^PASS' $(2)
endef
.PHONY: all lint sim sim-engine sim-core sim-verilator soc cosim perf timing bitstream clean $(ENGINE_RUNS) run-nomatch $(ML_RUNS)
.DEFAULT_GOAL := all
all: lint sim sim-verilator
lint:
$(VERILATOR) --lint-only -Wall $(ENGINE)
$(VERILATOR) --lint-only -Wall $(ENGINE) $(CORE) --top-module bitacc_pcie_core
sim: sim-engine sim-core
ML_RUNS := run-ml-4x16-1 run-ml-8x16-2
sim-engine: $(ENGINE_RUNS) run-nomatch $(ML_RUNS)
$(ML_RUNS): run-ml-%: $(BUILD)/tb_ml_%.vvp
@echo "engine $* (lanes x words - MATCH units)"
@$(call vvp_check,$(VVP) -n $< +seed=3 +ops=$(OPS),$(BUILD)/run-ml-$*.log)
$(BUILD)/tb_ml_%.vvp: $(REF) $(ENGINE) tb/tb_engine.sv $(TB_INC)
@mkdir -p $(BUILD)
c=$*; g=$${c%-*}; m=$${c#*-}; \
$(IVERILOG) -g2012 -Itb -DLANES=$${g%x*} -DWPL=$${g#*x} -DMLANES=$$m -o $@ $(REF) $(ENGINE) tb/tb_engine.sv
# run-<lanes>x<words per lane>-<seed>
.SECONDEXPANSION:
$(ENGINE_RUNS): run-%: $(BUILD)/tb_$$(word 1,$$(subst -, ,$$*)).vvp
@echo "engine $*"
@$(call vvp_check,$(VVP) -n $< +seed=$(word 2,$(subst -, ,$*)) +ops=$(OPS),$(BUILD)/run-$*.log)
run-nomatch: $(BUILD)/tb_nomatch.vvp
@echo "engine 4x16 without MATCH"
@$(call vvp_check,$(VVP) -n $< +seed=1 +ops=$(OPS),$(BUILD)/run-nomatch.log)
$(BUILD)/tb_nomatch.vvp: $(REF) $(ENGINE) tb/tb_engine.sv $(TB_INC)
@mkdir -p $(BUILD)
$(IVERILOG) -g2012 -Itb -DLANES=4 -DWPL=16 -DMATCH=0 -o $@ $(REF) $(ENGINE) tb/tb_engine.sv
$(BUILD)/tb_%.vvp: $(REF) $(ENGINE) tb/tb_engine.sv $(TB_INC)
@mkdir -p $(BUILD)
g=$*; $(IVERILOG) -g2012 -Itb -DLANES=$${g%x*} -DWPL=$${g#*x} -o $@ $(REF) $(ENGINE) tb/tb_engine.sv
sim-core:
@mkdir -p $(BUILD)
$(IVERILOG) -g2012 -Itb -o $(BUILD)/tb_core.vvp $(REF) $(ENGINE) $(CORE) tb/tb_pcie_core.sv
@$(call vvp_check,$(VVP) -n $(BUILD)/tb_core.vvp,$(BUILD)/core.log)
# --output-split-cfuncs 0: Verilator 5.020 splits the testbench's main initial
# block (a coroutine with a fork) across C++ functions and drops the code that
# waits for the join; the binary then crashes (illegal instruction).
sim-verilator:
@mkdir -p $(BUILD)
$(VERILATOR) --binary --timing --timescale 1ns/1ps -Wno-fatal -Wno-lint -Wno-style -Itb --output-split-cfuncs 0 \
--top-module tb_engine -Mdir $(BUILD)/verilator $(REF) $(ENGINE) tb/tb_engine.sv >/dev/null
@$(call vvp_check,$(BUILD)/verilator/Vtb_engine +ops=$(OPS),$(BUILD)/verilator.log)
soc:
cd litex && $(PYTHON) bitacc_litefury.py --driver --output-dir $(SOC_DIR)
# $(call cosim_build,dir,lanes,words per lane[,MATCH units]): host program + Verilated core + mock.
define cosim_build
@test -d $(DRIVER) || { echo "run 'make soc' first"; exit 1; }
@mkdir -p $(1)
cc -O2 -Wall -Wextra -Werror -I$(DRIVER)/user/liblitepcie -I$(DRIVER)/kernel \
-c -o $(abspath $(1))/bitacc_pcie.o host/bitacc_pcie.c
$(VERILATOR) --cc --exe --build -O2 --timescale 1ns/1ps -Wno-fatal -Wno-lint \
-GLANES=$(2) -GWORDS_PER_LANE=$(3) $(if $(4),-GMATCH_LANES=$(4)) --top-module bitacc_pcie_core -Mdir $(1)/obj \
-CFLAGS "-I$(DRIVER)/user/liblitepcie -I$(DRIVER)/kernel -DMOCK_LANES=$(2) -DMOCK_WPL=$(3) $(if $(4),-DMOCK_MATCH_LANES=$(4))" \
-LDFLAGS "$(abspath $(1))/bitacc_pcie.o" \
$(ENGINE) $(CORE) $(abspath host/sim/mock_litepcie.cpp) -o bitacc_pcie_cosim >/dev/null
endef
# Small geometry keeps the co-simulation fast; the host reads it from CSRs.
# Arguments: operations, seed, percentage of range operations.
cosim:
$(call cosim_build,$(BUILD)/cosim,4,64)
$(BUILD)/cosim/obj/bitacc_pcie_cosim selftest 200000 1 5
$(BUILD)/cosim/obj/bitacc_pcie_cosim selftest 50000 9 50
# The LiteFury configuration with MATCH: 8 lanes x 2048 words, 2 MATCH units.
# Fails if a bench needs more cycles per descriptor than the design intends:
# single-bit 1 (plus 2%), COUNT/FIND over 16384 words 16384/8 steps of 1
# cycle, BULK over 8192 words 8192/8 steps of 2 cycles, MATCH over 16384
# words 16384/2 sub-steps of 1 cycle (plus 1% and a fixed allowance for
# pipeline fill and the MATCH pattern load).
perf:
$(call cosim_build,$(BUILD)/perf,8,2048,2)
$(BUILD)/perf/obj/bitacc_pcie_cosim selftest 20000 3 20
@for kl in get:1.02 count:2084 find:2084 xor:2084 match:8300; do \
k=$${kl%:*}; limit=$${kl#*:}; n=$$( [ $$k = get ] && echo 200000 || echo 2048 ); \
out=$$($(BUILD)/perf/obj/bitacc_pcie_cosim bench $$k $$n 2>&1) || { echo "$$out"; exit 1; }; \
echo "$$out" | grep -E '^(bench|mock)'; \
echo "$$out" | awk -v l=$$limit '/^mock:/ { c = $$(NF-1) } END { if (c == "" || c + 0 > l) { print "cycles/descriptor " c " above " l; exit 1 } }' || exit 1; \
done
TIMING_CONFIGS ?= 8:2048:1:2 8:2048:0
timing:
@set -e; cd timing; for c in $(TIMING_CONFIGS); do \
echo "== $$c (lanes:words per lane:MATCH)"; ./run.sh $$(echo $$c | tr ':' ' '); done
bitstream:
cd litex && $(PYTHON) bitacc_litefury.py --build --driver --output-dir $(SOC_DIR)
clean:
rm -rf $(BUILD) host/build_cosim
|