From 04b7425598479442c2fba575701243be7a5acd78 Mon Sep 17 00:00:00 2001 From: Movitz Aaron Date: Sat, 24 Oct 2026 17:13:18 +0100 Subject: [PATCH] Add local HLS dev setup: clangd, pragma highlighting + linting, local C-sim --- .gitignore | 7 + Practicals/README.md | 3 + Practicals/hlsdev/README.md | 178 + Practicals/hlsdev/clangd/compile_flags.txt | 5 + Practicals/hlsdev/fetch-headers.sh | 53 + Practicals/hlsdev/hls_pragma_lint.py | 322 + Practicals/hlsdev/include-stub/ap_int.h | 75 + Practicals/hlsdev/lint.sh | 39 + Practicals/hlsdev/nvim/after/syntax/cpp.vim | 43 + .../hlsdev/nvim/plugin/hls_pragma_lint.lua | 92 + Practicals/hlsdev/sim.sh | 75 + .../index/gameoflife.cpp.24C7FF41FD0C8DAA.idx | Bin 0 -> 350 bytes .../index/testbench.cpp.D2F0A5C7CF73988D.idx | Bin 0 -> 458 bytes .../index/toplevel.cpp.B52AE2AA83ACE367.idx | Bin 0 -> 352 bytes .../game_of_life_practical3b/.clangd | 5 + .../game_of_life_practical3b/.gitignore | 21 + .../compile_commands.json | 27 + ...ame_of_life_practical3b.hlscompile_summary | 246 + ...me_of_life_practical3b.hlsrun_csim_summary | 157 + .../hls/.autopilot/db/a.g | Bin 0 -> 131496 bytes .../hls/.autopilot/db/a.g.0.bc | Bin 0 -> 131496 bytes .../hls/.autopilot/db/a.g.1.bc | Bin 0 -> 97216 bytes .../hls/.autopilot/db/a.g.2.bc | Bin 0 -> 69136 bytes .../hls/.autopilot/db/a.g.2.prechk.bc | Bin 0 -> 69136 bytes .../hls/.autopilot/db/a.g.bc | Bin 0 -> 131496 bytes .../hls/.autopilot/db/a.g.ld.0.bc | Bin 0 -> 29036 bytes .../db/a.g.ld.0.bc.clang.reflow.diag.xml | 0 .../db/a.g.ld.0.bc.clang.reflow.diag.yml | 6735 + .../hls/.autopilot/db/a.g.ld.1.lower.bc | Bin 0 -> 29036 bytes .../db/a.g.ld.1.lower.bc.opt.diag.yml | 0 .../hls/.autopilot/db/a.g.ld.2.m1.bc | Bin 0 -> 11393960 bytes .../hls/.autopilot/db/a.g.ld.3.fpc.bc | Bin 0 -> 29984 bytes .../db/a.g.ld.3.fpc.bc.opt.diag.yml | 0 .../hls/.autopilot/db/a.g.ld.4.m2.bc | Bin 0 -> 54428 bytes .../hls/.autopilot/db/a.g.ld.5.gdce.bc | Bin 0 -> 29988 bytes .../db/a.g.ld.5.gdce.bc.opt.diag.yml | 0 .../hls/.autopilot/db/a.g.lto.bc | Bin 0 -> 131496 bytes .../hls/.autopilot/db/a.o.1.bc | Bin 0 -> 97216 bytes .../hls/.autopilot/db/a.o.1.tmp.bc | Bin 0 -> 69212 bytes .../hls/.autopilot/db/a.o.2.bc | Bin 0 -> 64716 bytes .../hls/.autopilot/db/a.o.3.bc | Bin 0 -> 71448 bytes .../hls/.autopilot/db/a.pp.bc | Bin 0 -> 131496 bytes .../hls/.autopilot/db/all.directive.json | 23 + .../.autopilot/db/apatb_game_of_life_step.cpp | 1386 + .../db/apatb_game_of_life_step_ir.bc | Bin 0 -> 7360 bytes .../db/apatb_game_of_life_step_ir.ll | 280 + .../.autopilot/db/autopilot.rtl.models.tcl | 7 + .../.autopilot/db/autopilot.rtl.models.txt | 2 + .../hls/.autopilot/db/clang.diag.xml | 0 .../.autopilot/db/cmplx.3.000.fe.link.json | 22 + .../db/cmplx.3.001.fe.unroll-raw.json | 22 + .../db/cmplx.3.002.fe.unroll-simplified.json | 22 + .../db/cmplx.3.003.fe.inline-raw.json | 22 + .../db/cmplx.3.004.fe.inline-simplified.json | 22 + .../cmplx.3.005.fe.array-partition-raw.json | 22 + ...x.3.006.fe.array-partition-simplified.json | 20 + .../db/cmplx.3.007.fe.aggregate.json | 20 + .../db/cmplx.3.008.fe.array-reshape.json | 20 + .../cmplx.3.009.fe.infer-access-pattern.json | 20 + .../db/cmplx.3.010.fe.performance-prep.json | 20 + .../cmplx.3.011.fe.performance-parallel.json | 20 + ...12.fe.performance-array-partition-raw.json | 20 + .../db/cmplx.3.013.fe.performance-final.json | 20 + .../db/cmplx.3.014.fe.simplify.json | 20 + .../db/cmplx.3.015.fe.lowering.json | 20 + .../hls/.autopilot/db/cmplx.3.016.fe.opt.json | 20 + .../hls/.autopilot/db/cmplx.5.0.fe.end.json | 14 + .../hls/.autopilot/db/coregen/AXILiteS.h | 77 + .../.autopilot/db/coregen/AXILiteS.slave.tcl | 4 + .../game_of_life_step_AXILiteS_s_axi.v | 612 + .../game_of_life_step_AXILiteS_s_axi.vhd | 688 + .../hls/.autopilot/db/cosimDB.tcl | 27 + .../hls/.autopilot/db/design.bindinfo.xml | 961 + .../db/driver/data/game_of_life_step.mdd | 19 + .../db/driver/data/game_of_life_step.tcl | 24 + .../db/driver/data/game_of_life_step.yaml | 24 + .../.autopilot/db/driver/src/CMakeLists.txt | 25 + .../hls/.autopilot/db/driver/src/Makefile | 60 + .../db/driver/src/xgame_of_life_step.c | 281 + .../db/driver/src/xgame_of_life_step.h | 131 + .../db/driver/src/xgame_of_life_step_hw.h | 86 + .../db/driver/src/xgame_of_life_step_linux.c | 150 + .../db/driver/src/xgame_of_life_step_sinit.c | 79 + .../hls/.autopilot/db/dsp_style | 1 + .../hls/.autopilot/db/dut.hcp | Bin 0 -> 45693 bytes .../hls/.autopilot/db/fe_messages.xml | 90 + .../.autopilot/db/fe_pragma_dump.reflow.0.xml | 44 + .../db/game_of_life_step.DependenceCheck.tcl | 6 + .../hls/.autopilot/db/game_of_life_step.adb | 211851 +++++++++++++++ .../.autopilot/db/game_of_life_step.adb.xml | 32459 +++ .../.autopilot/db/game_of_life_step.bind.adb | 176970 ++++++++++++ .../db/game_of_life_step.bind.adb.xml | 32459 +++ .../db/game_of_life_step.compgen.dataonly.tcl | 78 + .../db/game_of_life_step.compgen.tcl | 160 + .../db/game_of_life_step.constraint.tcl | 12 + .../db/game_of_life_step.design.xml | 282 + .../.autopilot/db/game_of_life_step.protoinst | 19 + .../db/game_of_life_step.rtl_wrap.cfg.tcl | 114 + .../.autopilot/db/game_of_life_step.sched.adb | 130967 +++++++++ .../db/game_of_life_step.sched.adb.xml | 25743 ++ .../.autopilot/db/game_of_life_step.tbgen.tcl | 129 + .../db/game_of_life_step.verbose.bind.rpt | 35743 +++ .../db/game_of_life_step.verbose.bind.rpt.xml | 45 + .../db/game_of_life_step.verbose.rpt | 37043 +++ .../db/game_of_life_step.verbose.rpt.xml | 1323 + .../db/game_of_life_step.verbose.sched.rpt | 1833 + .../game_of_life_step.verbose.sched.rpt.xml | 45 + .../db/game_of_life_step_dataflow_ana.wcfg | 66 + ...fe_stepap_header.systemc.txt.ap_header.txt | 6 + ...fe_stepap_header.verilog.txt.ap_header.txt | 6 + ..._life_stepap_header.vhdl.txt.ap_header.txt | 6 + .../hls/.autopilot/db/gameoflife.bc | Bin 0 -> 29008 bytes .../db/gameoflife.cpp.clang.diag.xml | 0 .../hls/.autopilot/db/gameoflife.g.bc | Bin 0 -> 29008 bytes .../hls/.autopilot/db/gameoflife.pp.0.cpp | 8943 + ...fe.pp.0.cpp.clang-tidy.loop-label.diag.yml | 58 + .../db/gameoflife.pp.0.cpp.clang.diag.xml | 279 + .../db/gameoflife.pp.0.cpp.clang.diag.yml | 0 .../hls/.autopilot/db/global.setting.tcl | 66 + .../.autopilot/db/gui_cache/designCache.json | 1 + .../hls/.autopilot/db/hls_design_meta.cpp | 24 + .../hls/.autopilot/db/hls_design_meta.h | 13 + .../hls/.autopilot/db/hls_design_meta.tcl | 2 + .../.autopilot/db/kernel_module_hierarchy.tcl | 3 + .../db/mapper_game_of_life_step.cpp | 251 + .../hls/.autopilot/db/top-io-be.tcl | 0 .../hls/.autopilot/db/top-io-fe.xml | 37 + .../vhdl/game_of_life_step_AXILiteS_s_axi.vhd | 688 + .../vlog/game_of_life_step_AXILiteS_s_axi.v | 612 + .../hls/.debug/game_of_life_step.protoinst | 19 + .../hls/config.cmdline | 3 + .../hls/csim/.lst_opt.tcl | 5 + .../hls/csim/build/Makefile.rules | 469 + .../hls/csim/build/csim.exe | Bin 0 -> 145296 bytes .../hls/csim/build/csim.mk | 96 + .../hls/csim/build/env.json | 9 + .../hls/csim/build/obj/.dir | 0 .../hls/csim/build/obj/gameoflife.d | 19 + .../hls/csim/build/obj/gameoflife.o | Bin 0 -> 246320 bytes .../hls/csim/build/obj/testbench.d | 1 + .../hls/csim/build/obj/testbench.o | Bin 0 -> 16200 bytes .../hls/csim/build/run_sim.tcl | 44 + .../hls/csim/build/sim.sh | 10 + .../hls/hls_data.json | 771 + .../data/game_of_life_step.mdd | 19 + .../data/game_of_life_step.tcl | 24 + .../data/game_of_life_step.yaml | 24 + .../game_of_life_step_v1_0/src/CMakeLists.txt | 25 + .../game_of_life_step_v1_0/src/Makefile | 60 + .../src/xgame_of_life_step.c | 281 + .../src/xgame_of_life_step.h | 131 + .../src/xgame_of_life_step_hw.h | 86 + .../src/xgame_of_life_step_linux.c | 150 + .../src/xgame_of_life_step_sinit.c | 79 + .../hls/impl/misc/logo.png | Bin 0 -> 2297 bytes .../hls/impl/verilog/game_of_life_step.v | 5684 + .../game_of_life_step_AXILiteS_s_axi.v | 612 + .../hls/impl/vhdl/game_of_life_step.vhd | 4093 + .../vhdl/game_of_life_step_AXILiteS_s_axi.vhd | 688 + .../hls/syn/report/csynth.rpt | 1099 + .../hls/syn/report/csynth.xml | 1559 + .../hls/syn/report/csynth_design_size.rpt | 49 + .../hls/syn/report/csynth_design_size.xml | 47 + .../syn/report/game_of_life_step_csynth.rpt | 1341 + .../syn/report/game_of_life_step_csynth.xml | 282 + .../hls/syn/verilog/game_of_life_step.v | 5684 + .../game_of_life_step_AXILiteS_s_axi.v | 612 + .../hls/syn/vhdl/game_of_life_step.vhd | 4093 + .../vhdl/game_of_life_step_AXILiteS_s_axi.vhd | 688 + ...pile_game_of_life_practical3b_guidance.pb3 | Bin 0 -> 973 bytes .../reports/hls_compile.rpt | 1099 + ...ile_game_of_life_practical3b_guidance.html | 3449 + .../game_of_life_practical3b/vitis-comp.json | 9 + .../game_of_life_practical3b/gameoflife.cpp | 118 + .../game_of_life_practical3b/gameoflife.h | 14 + .../game_of_life_practical3b/hls_config.cfg | 10 + .../game_of_life_practical3b/testbench.cpp | 204 + .../game_of_life_practical3b/toplevel.cpp | 0 .../game_of_life_practical3b/toplevel.h | 0 .../game_of_life_practical3b/vitis-comp.json | 13 + 180 files changed, 745644 insertions(+) create mode 100644 Practicals/README.md create mode 100644 Practicals/hlsdev/README.md create mode 100644 Practicals/hlsdev/clangd/compile_flags.txt create mode 100755 Practicals/hlsdev/fetch-headers.sh create mode 100755 Practicals/hlsdev/hls_pragma_lint.py create mode 100644 Practicals/hlsdev/include-stub/ap_int.h create mode 100755 Practicals/hlsdev/lint.sh create mode 100644 Practicals/hlsdev/nvim/after/syntax/cpp.vim create mode 100644 Practicals/hlsdev/nvim/plugin/hls_pragma_lint.lua create mode 100755 Practicals/hlsdev/sim.sh create mode 100644 Practicals/practical-3b/game_of_life_practical3b/.cache/clangd/index/gameoflife.cpp.24C7FF41FD0C8DAA.idx create mode 100644 Practicals/practical-3b/game_of_life_practical3b/.cache/clangd/index/testbench.cpp.D2F0A5C7CF73988D.idx create mode 100644 Practicals/practical-3b/game_of_life_practical3b/.cache/clangd/index/toplevel.cpp.B52AE2AA83ACE367.idx create mode 100644 Practicals/practical-3b/game_of_life_practical3b/.clangd create mode 100644 Practicals/practical-3b/game_of_life_practical3b/.gitignore create mode 100644 Practicals/practical-3b/game_of_life_practical3b/compile_commands.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/game_of_life_practical3b.hlscompile_summary create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/game_of_life_practical3b.hlsrun_csim_summary create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.0.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.1.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.2.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.2.prechk.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.ld.0.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.ld.0.bc.clang.reflow.diag.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.ld.0.bc.clang.reflow.diag.yml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.ld.1.lower.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.ld.1.lower.bc.opt.diag.yml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.ld.2.m1.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.ld.3.fpc.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.ld.3.fpc.bc.opt.diag.yml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.ld.4.m2.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.ld.5.gdce.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.ld.5.gdce.bc.opt.diag.yml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g.lto.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.o.1.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.o.1.tmp.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.o.2.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.o.3.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.pp.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/all.directive.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/apatb_game_of_life_step.cpp create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/apatb_game_of_life_step_ir.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/apatb_game_of_life_step_ir.ll create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/autopilot.rtl.models.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/autopilot.rtl.models.txt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/clang.diag.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.000.fe.link.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.001.fe.unroll-raw.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.002.fe.unroll-simplified.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.003.fe.inline-raw.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.004.fe.inline-simplified.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.005.fe.array-partition-raw.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.006.fe.array-partition-simplified.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.007.fe.aggregate.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.008.fe.array-reshape.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.009.fe.infer-access-pattern.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.010.fe.performance-prep.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.011.fe.performance-parallel.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.012.fe.performance-array-partition-raw.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.013.fe.performance-final.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.014.fe.simplify.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.015.fe.lowering.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.3.016.fe.opt.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cmplx.5.0.fe.end.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/coregen/AXILiteS.h create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/coregen/AXILiteS.slave.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/coregen/game_of_life_step_AXILiteS_s_axi.v create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/coregen/game_of_life_step_AXILiteS_s_axi.vhd create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/cosimDB.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/design.bindinfo.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/driver/data/game_of_life_step.mdd create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/driver/data/game_of_life_step.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/driver/data/game_of_life_step.yaml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/driver/src/CMakeLists.txt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/driver/src/Makefile create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/driver/src/xgame_of_life_step.c create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/driver/src/xgame_of_life_step.h create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/driver/src/xgame_of_life_step_hw.h create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/driver/src/xgame_of_life_step_linux.c create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/driver/src/xgame_of_life_step_sinit.c create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/dsp_style create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/dut.hcp create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/fe_messages.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/fe_pragma_dump.reflow.0.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.DependenceCheck.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.adb create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.adb.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.bind.adb create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.bind.adb.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.compgen.dataonly.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.compgen.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.constraint.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.design.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.protoinst create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.rtl_wrap.cfg.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.sched.adb create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.sched.adb.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.tbgen.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.verbose.bind.rpt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.verbose.bind.rpt.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.verbose.rpt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.verbose.rpt.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.verbose.sched.rpt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step.verbose.sched.rpt.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_step_dataflow_ana.wcfg create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_stepap_header.systemc.txt.ap_header.txt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_stepap_header.verilog.txt.ap_header.txt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/game_of_life_stepap_header.vhdl.txt.ap_header.txt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/gameoflife.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/gameoflife.cpp.clang.diag.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/gameoflife.g.bc create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/gameoflife.pp.0.cpp create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/gameoflife.pp.0.cpp.clang-tidy.loop-label.diag.yml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/gameoflife.pp.0.cpp.clang.diag.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/gameoflife.pp.0.cpp.clang.diag.yml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/global.setting.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/gui_cache/designCache.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/hls_design_meta.cpp create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/hls_design_meta.h create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/hls_design_meta.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/kernel_module_hierarchy.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/mapper_game_of_life_step.cpp create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/top-io-be.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/top-io-fe.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/vhdl/game_of_life_step_AXILiteS_s_axi.vhd create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/vlog/game_of_life_step_AXILiteS_s_axi.v create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.debug/game_of_life_step.protoinst create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/config.cmdline create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/csim/.lst_opt.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/csim/build/Makefile.rules create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/csim/build/csim.exe create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/csim/build/csim.mk create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/csim/build/env.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/csim/build/obj/.dir create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/csim/build/obj/gameoflife.d create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/csim/build/obj/gameoflife.o create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/csim/build/obj/testbench.d create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/csim/build/obj/testbench.o create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/csim/build/run_sim.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/csim/build/sim.sh create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/hls_data.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/misc/drivers/game_of_life_step_v1_0/data/game_of_life_step.mdd create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/misc/drivers/game_of_life_step_v1_0/data/game_of_life_step.tcl create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/misc/drivers/game_of_life_step_v1_0/data/game_of_life_step.yaml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/misc/drivers/game_of_life_step_v1_0/src/CMakeLists.txt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/misc/drivers/game_of_life_step_v1_0/src/Makefile create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/misc/drivers/game_of_life_step_v1_0/src/xgame_of_life_step.c create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/misc/drivers/game_of_life_step_v1_0/src/xgame_of_life_step.h create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/misc/drivers/game_of_life_step_v1_0/src/xgame_of_life_step_hw.h create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/misc/drivers/game_of_life_step_v1_0/src/xgame_of_life_step_linux.c create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/misc/drivers/game_of_life_step_v1_0/src/xgame_of_life_step_sinit.c create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/misc/logo.png create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/verilog/game_of_life_step.v create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/verilog/game_of_life_step_AXILiteS_s_axi.v create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/vhdl/game_of_life_step.vhd create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/impl/vhdl/game_of_life_step_AXILiteS_s_axi.vhd create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/syn/report/csynth.rpt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/syn/report/csynth.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/syn/report/csynth_design_size.rpt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/syn/report/csynth_design_size.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/syn/report/game_of_life_step_csynth.rpt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/syn/report/game_of_life_step_csynth.xml create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/syn/verilog/game_of_life_step.v create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/syn/verilog/game_of_life_step_AXILiteS_s_axi.v create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/syn/vhdl/game_of_life_step.vhd create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/syn/vhdl/game_of_life_step_AXILiteS_s_axi.vhd create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/reports/.db/v++_compile_game_of_life_practical3b_guidance.pb3 create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/reports/hls_compile.rpt create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/reports/v++_compile_game_of_life_practical3b_guidance.html create mode 100644 Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/vitis-comp.json create mode 100644 Practicals/practical-3b/game_of_life_practical3b/gameoflife.cpp create mode 100644 Practicals/practical-3b/game_of_life_practical3b/gameoflife.h create mode 100644 Practicals/practical-3b/game_of_life_practical3b/hls_config.cfg create mode 100644 Practicals/practical-3b/game_of_life_practical3b/testbench.cpp create mode 100644 Practicals/practical-3b/game_of_life_practical3b/toplevel.cpp create mode 100644 Practicals/practical-3b/game_of_life_practical3b/toplevel.h create mode 100644 Practicals/practical-3b/game_of_life_practical3b/vitis-comp.json diff --git a/.gitignore b/.gitignore index e5ee812..958c8ff 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,10 @@ capture_*.jpg *.jou *.log *.str + +# HLS local dev: headers fetched from the server (each machine fetches its own) +Practicals/hlsdev/include/ +# HLS local dev: compiled simulation binaries +Practicals/hlsdev/build/ +# scratch space +.tmp/ diff --git a/Practicals/README.md b/Practicals/README.md new file mode 100644 index 0000000..14bc719 --- /dev/null +++ b/Practicals/README.md @@ -0,0 +1,3 @@ +These files live within the directory /home/userfs/b/brs544/Courses/EMBS/. +They must be created in that directory on the remote machine and can then be copied over to this local directory. +The file names must not be changed, not the directory name more importantly because if the files are then copied back, Vitis + Vivado won't realise the change as they hardcode paths in projects. diff --git a/Practicals/hlsdev/README.md b/Practicals/hlsdev/README.md new file mode 100644 index 0000000..a31adfb --- /dev/null +++ b/Practicals/hlsdev/README.md @@ -0,0 +1,178 @@ +# Local HLS development (`hlsdev`) + +Vitis is slow over X11, so this folder lets you **edit, check and test** the +practical code on your Mac, and only go to the server when you want a real +synthesis run. + +It gives you four things: + +| | What | Why | +| --- | --- | --- | +| 1 | **clangd diagnostics** for the C++ itself | Real errors and warnings as you type, with no Xilinx install | +| 2 | **`#pragma HLS` highlighting** | Stock syntax files treat pragmas as anonymous preprocessor text | +| 3 | **`#pragma HLS` linting** | Compilers silently ignore misspelled pragmas - this catches them | +| 4 | **A local C-simulation** (`./sim.sh`) | Runs the real testbench in about a second instead of minutes | + +--- + +## The one thing worth understanding first + +`#pragma HLS ...` is **not C++**. clang and gcc have never heard of it and just +ignore it: + +```c +#pragma HLS PIPELINE II=2 +#pragma HLS TOTALLY_MADE_UP nonsense=1 // compiles identically fine +``` + +That has two consequences, and they are the reason this folder exists: + +* **Good news:** you get correct C++ diagnostics with any normal compiler. The + pragmas never produce false errors. +* **Bad news:** nothing but the HLS compiler can tell a valid pragma from a + typo, and even Vitis HLS is often happy to ignore one silently. So a mistyped + `PIPLINE` can quietly cost you the optimisation you asked for. + +Hence items 1-2 are for editing comfort, and item 3 is the one that actually +catches bugs. + +--- + +## Setup + +### 1. Point Neovim at the highlighting and linter (one line) + +Add this to your `init.lua`: + +```lua +vim.opt.runtimepath:append(vim.fn.expand('~/Projects/EMBS/Practicals/hlsdev/nvim')) +vim.opt.runtimepath:append(vim.fn.expand('~/Projects/EMBS/Practicals/hlsdev/nvim/after')) +``` + +Both lines are needed: the first picks up `plugin/`, the second picks up +`after/syntax/`. Nothing else to install - no plugin manager involved. + +### 2. Get the real HLS headers (optional but recommended) + +```bash +cd ~/Projects/EMBS/Practicals/hlsdev +./fetch-headers.sh --dry-run # see what is there and how big it is +./fetch-headers.sh # copy them (asks for your gateway password) +``` + +This copies just the header files (a few MB) from +`/opt/york/cs/net/xilinx_vivado-2025.1-x86_64-1/2025.1/Vitis` - not the 60 GB +toolchain. `clangd` and `./sim.sh` then use the genuine `ap_int.h`. + +If you skip this, everything still works using the stand-in header in +`include-stub/`. That stub is **not** the real header - it only implements the +handful of `ap_uint` operations the practicals use, and `./sim.sh` prints a +warning when it falls back to it. + +--- + +## Daily use + +```bash +cd ~/Projects/EMBS/Practicals/hlsdev + +./sim.sh # compile + run the C simulation locally (fast) +./lint.sh # check every #pragma HLS line +./sim.sh practical-4 # any other practical folder +./lint.sh ../practical-3b/game_of_life_practical3b/gameoflife.cpp # one file +``` + +In Neovim: + +* Diagnostics from the pragma linter appear alongside clangd's, automatically + on open and on save. +* `:HlsPragmaLint` re-runs it on the current buffer. + +--- + +## What the linter catches + +``` +$ ./lint.sh +gameoflife.cpp:2:9: error: unknown HLS pragma 'PIPLINE' - did you mean 'PIPELINE'? +gameoflife.cpp:3:9: error: UNROLL: factor must be an integer, got 'two' +gameoflife.cpp:4:9: warning: ARRAY_PARTITION: variable='missing_array' is not declared anywhere in this file +gameoflife.cpp:5:9: error: ARRAY_PARTITION: type='complet' is invalid (expected one of: block, complete, cyclic) +gameoflife.cpp:6:9: error: INTERFACE: 's_axillite' is not a valid mode (expected one of: ... s_axilite ...) +gameoflife.cpp:8:9: error: unknown HLS pragma 'TOTALLY_MADE_UP' +gameoflife.cpp:9:9: error: LOOP_TRIPCOUNT: missing required option 'min=' +``` + +It checks pragma names, option names, option values, required options, and +whether the `variable=` / `port=` you named actually exists in the file - +with "did you mean" suggestions throughout. + +--- + +## How clangd is wired up + +Vitis writes a `compile_commands.json` next to the sources, but its paths point +at `/home/userfs/b/...` on the server and its flags (`-fhls`, `-hls-syn-headers-dir=`, +`--gcc-toolchain=`) are Xilinx-only, so clangd cannot use it. + +Rather than overwrite it (Vitis regenerates and hard-codes paths), the file +`game_of_life_practical3b/.clangd` redirects clangd to a local flag set: + +```yaml +CompileFlags: + CompilationDatabase: ../../hlsdev/clangd +``` + +and `hlsdev/clangd/compile_flags.txt` holds the actual flags: + +``` +-xc++ +-std=c++14 +-Wno-unknown-pragmas +-I../include +-I../include-stub +``` + +Relative paths are resolved from the `compile_flags.txt` directory, so this +stays portable - no absolute paths are committed. + +--- + +## Files + +| File | Purpose | +| --- | --- | +| `sim.sh` | Compile + run the C simulation locally | +| `lint.sh` | Run the pragma linter over the practicals | +| `hls_pragma_lint.py` | The linter itself (also usable in CI) | +| `fetch-headers.sh` | Copy the real HLS headers down from csteach0 | +| `clangd/compile_flags.txt` | Flags clangd uses for these files | +| `include-stub/ap_int.h` | Editor-only stand-in for the real `ap_int.h` | +| `include/` | Where `fetch-headers.sh` puts the real headers (gitignored) | +| `nvim/after/syntax/cpp.vim` | `#pragma HLS` highlighting | +| `nvim/plugin/hls_pragma_lint.lua` | Neovim diagnostics integration | + +--- + +## Two caveats + +**Treesitter will override the highlighting if the `cpp` parser is installed.** +Your config runs `vim.treesitter.start()` on every filetype, and Neovim has no +`cpp` parser installed, so it currently falls back to Vim syntax (where our +rules live). If you ever run `:TSInstall cpp`, the pragma highlighting will +stop working. Fix it by not starting treesitter for cpp, e.g.: + +```lua +vim.api.nvim_create_autocmd('FileType', { + callback = function() + if vim.bo.filetype == 'cpp' then return end -- keep Vim syntax for cpp + pcall(vim.treesitter.start) + end, +}) +``` + +**The local simulation is not synthesis.** It checks your logic against the +testbench, which is the bulk of the work - but it does not tell you about +timing, `II`, resource usage or whether the pragmas did what you wanted. For +that you still need a real HLS run on the server. `./lint.sh` covers the pragma +typos that the compiler would otherwise swallow. diff --git a/Practicals/hlsdev/clangd/compile_flags.txt b/Practicals/hlsdev/clangd/compile_flags.txt new file mode 100644 index 0000000..5e07de5 --- /dev/null +++ b/Practicals/hlsdev/clangd/compile_flags.txt @@ -0,0 +1,5 @@ +-xc++ +-std=c++14 +-Wno-unknown-pragmas +-I../include +-I../include-stub diff --git a/Practicals/hlsdev/fetch-headers.sh b/Practicals/hlsdev/fetch-headers.sh new file mode 100755 index 0000000..8979a12 --- /dev/null +++ b/Practicals/hlsdev/fetch-headers.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# +# Copy the real Vitis HLS headers down from the CS server. +# +# Only header files are copied - a few MB, not the 60 GB toolchain - so clangd +# and ./sim.sh work against the genuine ap_int.h / hls_stream.h instead of the +# stand-in in hlsdev/include-stub. +# +# You will be asked for your gateway password / Duo approval, because the +# server is behind the IT Services SSH gateway. +# +# Usage: +# ./fetch-headers.sh # copy +# ./fetch-headers.sh --dry-run # just report what is there and how big it is +# +set -euo pipefail + +here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +inc="$here/include" +host="${EMBS_HOST:-csteach0.york.ac.uk}" +vitis="${VITIS_ROOT:-/opt/york/cs/net/xilinx_vivado-2025.1-x86_64-1/2025.1/Vitis}" + +sim_inc="$vitis/include" +syn_inc="$vitis/common/technology/autopilot" + +echo "host : $host" +echo "sim inc : $sim_inc" +echo "syn inc : $syn_inc" +echo + +echo "Asking the server what is there ..." +ssh "$host" " + du -sh '$sim_inc' '$syn_inc' 2>/dev/null || true + echo + echo 'ap_int.h / hls_stream.h found at:' + find '$sim_inc' '$syn_inc' -maxdepth 2 \\( -name ap_int.h -o -name hls_stream.h -o -name ap_fixed.h \\) 2>/dev/null +" || { echo "error: could not reach $host" >&2; exit 1; } + +if [ "${1:-}" = "--dry-run" ]; then + echo + echo "--dry-run: nothing copied." + exit 0 +fi + +mkdir -p "$inc" +echo +echo "Copying headers (watch for the password prompt) ..." +rsync -a --info=progress2 "$host:$sim_inc/" "$inc/" +rsync -a --info=progress2 "$host:$syn_inc/" "$inc/" + +echo +echo "Done. $(find "$inc" -name '*.h' | wc -l | tr -d ' ') headers now in $inc" +echo "clangd and ./sim.sh will use these in preference to the stub." diff --git a/Practicals/hlsdev/hls_pragma_lint.py b/Practicals/hlsdev/hls_pragma_lint.py new file mode 100755 index 0000000..4e2ce14 --- /dev/null +++ b/Practicals/hlsdev/hls_pragma_lint.py @@ -0,0 +1,322 @@ +#!/usr/bin/env python3 +""" +hls_pragma_lint.py - check Xilinx Vitis HLS pragmas. + +Why this exists +--------------- +`#pragma HLS ...` is not C++. Stock clang and gcc do not know these pragmas +and simply ignore them, so a typo like + + #pragma HLS PIPLINE II=2 + +compiles perfectly happily. Vitis HLS may also accept it silently, meaning the +optimisation you asked for just never happens - which is a genuinely nasty way +to lose an afternoon. + +This linter knows the pragma names, the options each one takes, and the values +those options allow. It reports problems in the standard + + file:line:col: error: message + +form, so it works with nvim's errorformat, nvim-lint, efm-langserver, `:make`, +or any CI. + +Usage +----- + ./hls_pragma_lint.py file.cpp [more.cpp ...] + ./hls_pragma_lint.py ../practical-3b/game_of_life_practical3b/gameoflife.cpp + +Exit status is 0 when nothing is wrong, 1 when there is at least one error. +""" + +import difflib +import re +import sys + +# --------------------------------------------------------------------------- +# The pragma reference +# --------------------------------------------------------------------------- +# "FLAG" -> a bare keyword with no value, e.g. `rewind` +# "NUM" -> an integer +# set -> a value that must be one of these +# "ANY" -> anything (identifier, number, ...) +# --------------------------------------------------------------------------- +FLAG, NUM, ANY = "FLAG", "NUM", "ANY" + +INTERFACE_MODES = { + "ap_none", "ap_vld", "ap_ack", "ap_hs", "ap_ovld", "ap_fifo", + "ap_memory", "ap_bram", "bram", "ap_ctrl_none", "ap_ctrl_hs", + "ap_ctrl_chain", "m_axi", "s_axilite", "axis", "s_axis", "m_axis", + "ap_stable", "ap_bus", "ap_array", "ap_array_rtl", +} + +PRAGMAS = { + "PIPELINE": { + "options": { + "II": NUM, "rewind": FLAG, "off": FLAG, "enable_flush": FLAG, + "style": {"stp", "frp", "flp"}, + }, + }, + "UNROLL": { + "options": { + "factor": NUM, "region": FLAG, "skip_exit_check": FLAG, "off": FLAG, + }, + }, + "ARRAY_PARTITION": { + "options": { + "variable": ANY, "type": {"block", "cyclic", "complete"}, + "factor": NUM, "dim": NUM, "recursive": FLAG, "off": FLAG, + }, + "required": ["variable"], + }, + "ARRAY_RESHAPE": { + "options": { + "variable": ANY, "type": {"block", "cyclic", "complete"}, + "factor": NUM, "dim": NUM, "off": FLAG, + }, + "required": ["variable"], + }, + "ARRAY_MAP": { + "options": { + "variable": ANY, "instance": ANY, + "mode": {"horizontal", "vertical"}, "offset": NUM, "off": FLAG, + }, + "required": ["variable"], + }, + "DATA_PACK": { + "options": { + "variable": ANY, "instance": ANY, "offset": NUM, "off": FLAG, + }, + }, + "AGGREGATE": { + "options": { + "variable": ANY, "compact": {"bit", "byte", "none", "auto"}, + "dim": NUM, "off": FLAG, + }, + "required": ["variable"], + }, + "STREAM": { + "options": { + "variable": ANY, "depth": NUM, "dim": NUM, "off": FLAG, + }, + "required": ["variable"], + }, + "INTERFACE": { + "positional": INTERFACE_MODES, + "options": { + "mode": INTERFACE_MODES, "port": ANY, "bundle": ANY, + "register": FLAG, "depth": NUM, "offset": ANY, "clock": ANY, + "name": ANY, "num_read_outstanding": NUM, + "num_write_outstanding": NUM, "max_read_burst_length": NUM, + "max_write_burst_length": NUM, "latency": NUM, + "m_axi_offset": ANY, "storage_type": ANY, "storage_impl": ANY, + "sync": FLAG, "max_widen_bitwidth": NUM, + }, + "required": ["port"], + }, + "DATAFLOW": { + "options": {"off": FLAG, "disable_start_propagation": FLAG}, + }, + "INLINE": { + "options": {"off": FLAG, "recursive": FLAG, "region": FLAG}, + }, + "LATENCY": { + "options": {"min": NUM, "max": NUM, "off": FLAG}, + }, + "DEPENDENCE": { + "options": { + "variable": ANY, "type": {"inter", "intra"}, + "direction": {"RAW", "WAR", "WAW"}, "distance": NUM, "off": FLAG, + }, + "required": ["variable"], + }, + "ALLOCATION": { + "options": { + "limit": NUM, "instances": ANY, "operation": ANY, "off": FLAG, + }, + }, + "OCCURRENCE": { + "options": {"cycle": NUM, "operation": ANY, "off": FLAG}, + }, + "LOOP_FLATTEN": {"options": {"off": FLAG}}, + "LOOP_MERGE": {"options": {"force": FLAG, "off": FLAG}}, + "LOOP_TRIPCOUNT": { + "options": {"min": NUM, "max": NUM, "avg": NUM}, + "required": ["min"], + }, + "FUNCTION_INSTANTIATE": { + "options": {"variable": ANY}, "required": ["variable"], + }, + "RESET": {"options": {"off": FLAG}}, + "PROTOCOL": {"positional": {"fixed", "float"}}, + "EXPRESSION_BALANCE": {"options": {"off": FLAG}}, + "REQUIREMENTS": {"options": {"min": NUM, "max": NUM}}, + "DISAGGREGATE": { + "options": {"variable": ANY, "dim": NUM, "type": ANY}, + "required": ["variable"], + }, + "BIND_OP": { + "options": { + "op": ANY, "impl": ANY, "variable": ANY, "latency": NUM, + }, + "required": ["op", "impl"], + }, + "BIND_STORAGE": { + "options": { + "type": ANY, "impl": ANY, "variable": ANY, "latency": NUM, + }, + "required": ["type", "impl"], + }, + "SHARED": {"options": {"off": FLAG}}, +} + +# Options whose value is a name declared in the code. +NAME_OPTIONS = ("variable", "port", "op") + +PRAGMA_RE = re.compile(r"#\s*pragma\s+HLS\b(?P.*)$") +TOKEN_RE = re.compile(r"(?P[A-Za-z_]\w*)\s*=\s*(?P[^\s=]+)|(?P[^\s=]+)") + + +def find_pragma_name(rest): + """Pull the pragma name off the front of a pragma's argument text.""" + m = re.match(r"\s*([A-Za-z_]\w*)", rest) + if not m: + return None, rest + return m.group(1), rest[m.end():] + + +def parse_tokens(text): + """Return (options, bare_words) where options is [(key, val, col), ...].""" + options, bare = [], [] + for m in TOKEN_RE.finditer(text): + if m.group("key"): + options.append((m.group("key"), m.group("val"), m.start("key"))) + else: + bare.append((m.group("bare"), m.start("bare"))) + return options, bare + + +def lint_file(path): + """Return a list of (line, col, severity, message).""" + out = [] + try: + with open(path, "r", errors="replace") as fh: + lines = fh.readlines() + except OSError as exc: + return [(1, 1, "error", "cannot read file: %s" % exc)] + + whole_file = "".join(lines) + + for lineno, line in enumerate(lines, start=1): + m = PRAGMA_RE.search(line) + if not m: + continue + rest = m.group("rest") + + name, args = find_pragma_name(rest) + col = m.start() + m.group(0).index("HLS") + 1 + + if name is None: + out.append((lineno, col, "error", + "HLS pragma has no name (expected e.g. PIPELINE, UNROLL)")) + continue + + if name not in PRAGMAS: + hint = difflib.get_close_matches(name, PRAGMAS.keys(), n=1, cutoff=0.6) + msg = "unknown HLS pragma '%s'" % name + if hint: + msg += " - did you mean '%s'?" % hint[0] + else: + msg += " (see the Vitis HLS pragma reference)" + out.append((lineno, col, "error", msg)) + continue + + spec = PRAGMAS[name] + known = spec.get("options", {}) + positional = spec.get("positional") + + options, bare = parse_tokens(args) + seen = {k for k, _, _ in options} + + # Bare words: either a positional value or a flag. + for word, off in bare: + if word in known and known[word] is FLAG: + seen.add(word) + continue + if positional and word in positional: + continue + if positional and word not in positional: + out.append((lineno, col, "error", + "%s: '%s' is not a valid mode (expected one of: %s)" + % (name, word, ", ".join(sorted(positional))))) + continue + if word in known: + out.append((lineno, col, "error", + "%s: '%s' needs a value, e.g. %s=..." % (name, word, word))) + continue + hint = difflib.get_close_matches(word, list(known) + list(positional or ()), + n=1, cutoff=0.6) + msg = "%s: unknown option '%s'" % (name, word) + if hint: + msg += " - did you mean '%s'?" % hint[0] + out.append((lineno, col, "error", msg)) + + # key=value options + for key, val, off in options: + if key not in known: + hint = difflib.get_close_matches(key, known.keys(), n=1, cutoff=0.6) + msg = "%s: unknown option '%s'" % (name, key) + if hint: + msg += " - did you mean '%s'?" % hint[0] + out.append((lineno, col, "error", msg)) + continue + + expect = known[key] + if expect is FLAG: + out.append((lineno, col, "warning", + "%s: '%s' is a flag and takes no value" % (name, key))) + elif expect is NUM: + if not re.fullmatch(r"\d+", val): + out.append((lineno, col, "error", + "%s: %s must be an integer, got '%s'" + % (name, key, val))) + elif isinstance(expect, set): + if val not in expect: + out.append((lineno, col, "error", + "%s: %s='%s' is invalid (expected one of: %s)" + % (name, key, val, ", ".join(sorted(expect))))) + if key in NAME_OPTIONS and val != "return": + # Count occurrences across the file. A real object is declared + # at least once and named in at least one pragma, so a single + # hit means the only mention is this pragma itself. + hits = len(re.findall(r"\b%s\b" % re.escape(val), whole_file)) + if hits <= 1: + out.append((lineno, col, "warning", + "%s: %s='%s' is not declared anywhere in this file" + % (name, key, val))) + + # Required options + for req in spec.get("required", []): + if req not in seen and not (positional and req in positional): + out.append((lineno, col, "error", + "%s: missing required option '%s='" % (name, req))) + + return out + + +def main(argv): + if len(argv) < 2: + print(__doc__.strip(), file=sys.stderr) + return 2 + + errors = 0 + for path in argv[1:]: + for line, col, sev, msg in lint_file(path): + print("%s:%d:%d: %s: %s" % (path, line, col, sev, msg)) + if sev == "error": + errors += 1 + return 1 if errors else 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/Practicals/hlsdev/include-stub/ap_int.h b/Practicals/hlsdev/include-stub/ap_int.h new file mode 100644 index 0000000..00bfa45 --- /dev/null +++ b/Practicals/hlsdev/include-stub/ap_int.h @@ -0,0 +1,75 @@ +#ifndef HLSDEV_AP_INT_STUB_H +#define HLSDEV_AP_INT_STUB_H + +// =========================================================================== +// EDITOR-ONLY STAND-IN for Xilinx's +// --------------------------------------------------------------------------- +// This is NOT the real header. It exists so clangd can resolve ap_uint +// (and so a simple C-simulation can be built) on a machine with no Xilinx +// tools installed. +// +// It implements only what the EMBS practicals use: unsigned fixed-width +// values, bit indexing, += / -=, and conversion to/from integers. +// It supports widths 1..64 only, and has none of the real header's +// arbitrary-precision, signed, overflow or simulation semantics. +// +// Run ./fetch-headers.sh to download the REAL headers from csteach0. +// They take precedence over this file when present. +// =========================================================================== + +#include + +template +class ap_uint { + static_assert(W > 0 && W <= 64, "hlsdev stub ap_uint supports widths 1..64"); + +public: + unsigned long long v; + + static constexpr unsigned long long width_mask() { + return W >= 64 ? ~0ULL : ((1ULL << W) - 1ULL); + } + + ap_uint() : v(0) {} + ap_uint(int x) : v((unsigned long long)x & width_mask()) {} + ap_uint(unsigned int x) : v((unsigned long long)x & width_mask()) {} + ap_uint(unsigned long long x) : v(x & width_mask()) {} + + // Cross-width conversion, so e.g. ap_uint<4> += ap_uint<1> works. + // The implicit copy constructor is still preferred for the same width. + template + ap_uint(const ap_uint& o) : v((unsigned long long)o & width_mask()) {} + + operator unsigned long long() const { return v & width_mask(); } + + ap_uint& operator=(unsigned long long x) { v = x & width_mask(); return *this; } + + ap_uint& operator+=(const ap_uint& o) { v = (v + o.v) & width_mask(); return *this; } + ap_uint& operator-=(const ap_uint& o) { v = (v - o.v) & width_mask(); return *this; } + + // Writing a single bit needs a proxy object, just like the real ap_uint. + class bit_ref { + ap_uint& parent; + int bit; + + public: + bit_ref(ap_uint& p, int b) : parent(p), bit(b) {} + + bit_ref& operator=(bool x) { + if (x) parent.v |= (1ULL << bit); + else parent.v &= ~(1ULL << bit); + return *this; + } + operator bool() const { return ((parent.v >> bit) & 1ULL) != 0; } + + // Convert straight to any ap_uint in a single user-defined + // conversion, so things like acc += grid[r][c]; resolve cleanly. + template + operator ap_uint() const { return ap_uint((parent.v >> bit) & 1ULL); } + }; + + bit_ref operator[](int b) { return bit_ref(*this, b); } + bool operator[](int b) const { return ((v >> b) & 1ULL) != 0; } +}; + +#endif // HLSDEV_AP_INT_STUB_H diff --git a/Practicals/hlsdev/lint.sh b/Practicals/hlsdev/lint.sh new file mode 100755 index 0000000..5bbf971 --- /dev/null +++ b/Practicals/hlsdev/lint.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# +# Run the HLS pragma linter over the practicals. +# +# Usage: +# ./lint.sh # every source under Practicals/ +# ./lint.sh path/to/file.cpp [more.cpp ...] # just these +# +# Exits non-zero if anything is wrong, so it also works as a pre-commit or CI +# check. For inline diagnostics inside nvim, see nvim/plugin/hls_pragma_lint.lua +# +set -euo pipefail + +here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +if [ "$#" -gt 0 ]; then + exec python3 "$here/hls_pragma_lint.py" "$@" +fi + +# Resolve the parent properly: find returns the path as given, and +# ".../hlsdev/.." would contain "/hlsdev/" and defeat the exclusions below. +root="$(cd "$here/.." && pwd)" + +# Every hand-written source, skipping generated HLS build output and our own +# tooling. Paths in the practicals never contain spaces (Vitis requires it). +files="$(find "$root" -type f \( \ + -name '*.cpp' -o -name '*.cc' -o -name '*.cxx' -o -name '*.c' \ + -o -name '*.h' -o -name '*.hpp' \) \ + -not -path '*/hls/*' -not -path '*/hlsdev/*' | sort)" + +if [ -z "$files" ]; then + echo "no source files found" >&2 + exit 0 +fi + +echo "$files" | sed "s|^$root/| |" +echo +# shellcheck disable=SC2086 +exec python3 "$here/hls_pragma_lint.py" $files diff --git a/Practicals/hlsdev/nvim/after/syntax/cpp.vim b/Practicals/hlsdev/nvim/after/syntax/cpp.vim new file mode 100644 index 0000000..ee64465 --- /dev/null +++ b/Practicals/hlsdev/nvim/after/syntax/cpp.vim @@ -0,0 +1,43 @@ +" =========================================================================== +" HLS pragma highlighting for Vitis HLS (EMBS) +" --------------------------------------------------------------------------- +" Vim's stock C/C++ syntax files treat the whole "#pragma HLS ..." line as one +" anonymous preprocessor directive, so the pragma name and its options are +" invisible. These rules give them their own highlight groups. +" +" Loaded automatically when this directory is on 'runtimepath' (see the +" hlsdev README). Safe to source twice. +" =========================================================================== + +if !exists('b:current_syntax') + finish +endif +if b:current_syntax !=# 'cpp' && b:current_syntax !=# 'c' + finish +endif + +" The pragma line as a whole, so options inside it can be picked out. +syntax region hlsPragma + \ start=/#\s*pragma\s\+HLS\>/ + \ end="$" + \ keepend + \ contains=hlsPragmaName,hlsPragmaOption,hlsPragmaValue,hlsPragmaMode + +" The pragma name: PIPELINE, UNROLL, ARRAY_PARTITION, INTERFACE, ... +syntax match hlsPragmaName /\\s\+\zs[A-Z][A-Z_]*/ contained + +" Option keys: variable=, II=, type=, port=, bundle= +syntax match hlsPragmaOption /\<\w\+\ze\s*=/ contained + +" Option values: complete, 2, in_words, AXILiteS +syntax match hlsPragmaValue /=\s*\zs[^ ]\+/ contained + +" INTERFACE modes are positional, so they have no "mode=" in front of them: +" #pragma HLS INTERFACE s_axilite port=return bundle=ctrl +syntax match hlsPragmaMode /\<\%(ap_none\|ap_vld\|ap_ack\|ap_hs\|ap_ovld\|ap_fifo\|ap_memory\|ap_bram\|bram\|ap_ctrl_none\|ap_ctrl_hs\|ap_ctrl_chain\|m_axi\|s_axilite\|axis\|s_axis\|m_axis\|ap_stable\|ap_bus\)\>/ contained + +highlight default link hlsPragma PreProc +highlight default link hlsPragmaName Statement +highlight default link hlsPragmaOption Identifier +highlight default link hlsPragmaValue Constant +highlight default link hlsPragmaMode Constant diff --git a/Practicals/hlsdev/nvim/plugin/hls_pragma_lint.lua b/Practicals/hlsdev/nvim/plugin/hls_pragma_lint.lua new file mode 100644 index 0000000..25fe540 --- /dev/null +++ b/Practicals/hlsdev/nvim/plugin/hls_pragma_lint.lua @@ -0,0 +1,92 @@ +-- =========================================================================== +-- HLS pragma diagnostics for Neovim (EMBS) +-- --------------------------------------------------------------------------- +-- Runs hls_pragma_lint.py over C/C++ buffers and reports the results through +-- vim.diagnostic, so they show up exactly like clangd's errors. +-- +-- No plugin manager involved: this file lives in the hlsdev nvim runtimepath +-- entry, and Neovim sources plugin/*.lua automatically. +-- +-- Commands: +-- :HlsPragmaLint re-run on the current buffer +-- :lua vim.diagnostic.reset(...) (see :h vim.diagnostic) +-- =========================================================================== + +if vim.g.loaded_hls_pragma_lint then + return +end +vim.g.loaded_hls_pragma_lint = true + +local ns = vim.api.nvim_create_namespace('hls_pragma_lint') + +-- Locate hls_pragma_lint.py next to this runtimepath entry. +local plugin_dir = vim.fn.fnamemodify(debug.getinfo(1, 'S').source:sub(2), ':p:h') +local linter = vim.fn.fnamemodify(plugin_dir, ':h:h') .. '/hls_pragma_lint.py' + +local function python() + if vim.fn.executable('python3') == 1 then + return 'python3' + end + return nil +end + +local SEVERITY = { + error = vim.diagnostic.severity.ERROR, + warning = vim.diagnostic.severity.WARN, + info = vim.diagnostic.severity.INFO, +} + +local function parse(output, bufnr) + local diags = {} + for line in vim.gsplit(output, '\n', { plain = true }) do + local _, l, c, sev, msg = line:match('^(.-):(%d+):(%d+): (%a+): (.*)$') + if l then + table.insert(diags, { + bufnr = bufnr, + lnum = tonumber(l) - 1, + col = tonumber(c) - 1, + severity = SEVERITY[sev] or vim.diagnostic.severity.INFO, + message = msg, + source = 'hls-pragma', + }) + end + end + return diags +end + +local function lint(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + if not vim.api.nvim_buf_is_valid(bufnr) then + return + end + local ft = vim.bo[bufnr].filetype + if ft ~= 'c' and ft ~= 'cpp' then + return + end + if vim.fn.filereadable(linter) ~= 1 then + return + end + local py = python() + if not py then + return + end + local path = vim.api.nvim_buf_get_name(bufnr) + if path == '' then + return + end + + local out = vim.fn.system({ py, linter, path }) + vim.diagnostic.set(ns, bufnr, parse(out, bufnr)) +end + +vim.api.nvim_create_autocmd({ 'BufWritePost', 'BufReadPost' }, { + group = vim.api.nvim_create_augroup('HlsPragmaLint', { clear = true }), + pattern = { '*.c', '*.cpp', '*.cc', '*.h', '*.hpp' }, + callback = function(ev) + lint(ev.buf) + end, +}) + +vim.api.nvim_create_user_command('HlsPragmaLint', function() + lint(0) +end, { desc = 'Check #pragma HLS lines in this buffer' }) diff --git a/Practicals/hlsdev/sim.sh b/Practicals/hlsdev/sim.sh new file mode 100755 index 0000000..794eeef --- /dev/null +++ b/Practicals/hlsdev/sim.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# +# Build and run the Vitis HLS C-simulation for a practical, locally. +# +# This is a plain C++ compile of the testbench + DUT, which is what "Run C +# Simulation" does on the server (minus the HLS compiler's extra checking). +# It is the fast way to know your logic is right before spending time on the +# remote machine. +# +# Usage: +# ./sim.sh # practical-3b +# ./sim.sh practical-4 # any other practical directory +# +set -euo pipefail + +here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +practical="${1:-practical-3b}" + +# Find the HLS component: the directory holding hls_config.cfg. +cfg="$(find "$here/../$practical" -name hls_config.cfg -not -path '*/hls/*' 2>/dev/null | head -1)" +if [ -z "$cfg" ]; then + echo "error: no hls_config.cfg found under $here/../$practical" >&2 + exit 1 +fi +proj="$(cd "$(dirname "$cfg")" && pwd)" + +# hls_config.cfg names the sources, so we never have to guess. +tb_name="$(grep -E '^tb\.file=' "$cfg" | head -1 | cut -d= -f2- | tr -d '\r')" +dut_name="$(grep -E '^syn\.file=' "$cfg" | grep -E '\.(cpp|cc|cxx|c)$' | head -1 | cut -d= -f2- | tr -d '\r')" + +if [ -z "$tb_name" ] || [ -z "$dut_name" ]; then + echo "error: could not read tb.file / syn.file from $cfg" >&2 + exit 1 +fi + +# Prefer the real Xilinx headers; fall back to the stub so this still works +# before ./fetch-headers.sh has been run. +if [ -f "$here/include/ap_int.h" ]; then + inc="$here/include" + inc_extra="$here/include" +else + inc="$here/include-stub" + inc_extra="$here/include-stub" + echo "==============================================================" + echo "NOTE: using the FALLBACK stub headers in hlsdev/include-stub." + echo " They are good enough for the Game of Life practical, but" + echo " they are NOT the real Xilinx headers. Run ./fetch-headers.sh" + echo " for a simulation you can fully trust." + echo "==============================================================" + echo +fi + +CXX="${CXX:-clang++}" +out="$here/build" +mkdir -p "$out" + +dut="$proj/$dut_name" +tb="$proj/$tb_name" +for f in "$dut" "$tb"; do + [ -f "$f" ] || { echo "error: missing source $f" >&2; exit 1; } +done + +echo "component : $proj" +echo "testbench : $tb_name" +echo "dut : $dut_name" +echo "compiling with $CXX ..." + +"$CXX" -std=c++14 -O2 -Wall -Wextra -Wno-unknown-pragmas -Wno-unused-label \ + -I"$inc" -I"$inc_extra" -I"$proj" \ + "$tb" "$dut" -o "$out/csim" + +echo "running ..." +echo "------------------------------------------------------------------" +"$out/csim" +echo "------------------------------------------------------------------" diff --git a/Practicals/practical-3b/game_of_life_practical3b/.cache/clangd/index/gameoflife.cpp.24C7FF41FD0C8DAA.idx b/Practicals/practical-3b/game_of_life_practical3b/.cache/clangd/index/gameoflife.cpp.24C7FF41FD0C8DAA.idx new file mode 100644 index 0000000000000000000000000000000000000000..0da9c64d7f37bf3a045b7225c10cd527bfe7bc48 GIT binary patch literal 350 zcmV-k0ipgTm271!KTkh@&L1^zR3&_66+4l8dif)njIwygD6UXqT)(oMr=&lP;0?0L$CSo=n1( zN1}cQj6ocKApWn)AC=kL#&daXVgLXD0CHt!a{vGU0CHt) wVE_OC0CRFDW;>+eYsI0Ap=rYzqJY00;>R0s{pG1P1{L0P~BNtN;K2 literal 0 HcmV?d00001 diff --git a/Practicals/practical-3b/game_of_life_practical3b/.cache/clangd/index/testbench.cpp.D2F0A5C7CF73988D.idx b/Practicals/practical-3b/game_of_life_practical3b/.cache/clangd/index/testbench.cpp.D2F0A5C7CF73988D.idx new file mode 100644 index 0000000000000000000000000000000000000000..995c6c1c5b796eae7ac2f126e5f47adbb28b01b8 GIT binary patch literal 458 zcmWIYbaOkz$iU#7;#rZKT9U}Zz`!5~#Kk2=nSX(FI}-y##hkT4*;$7Tc)rzkEwZ~5 zz_$C?KQ{L;A;aTl(fle&0Z_ToWEXO-1zIouXU~5`_leQQtDFG{1vcUto)37arQa3 z69+7+Ta6@ctUkqL9DO|Upr@USM&qAXHyqM>v(@YxehBSjU$`T1?Tb{0Pi8(9nX`*8 zOU`+GXouGGz`_HHulME_-k$o!-rvf1nuPl90{P?jPM@efHLYvH*X<7HbH}vu-|S&C@2E2rSLPCfqAs z?iU*`0}lfOHv-~~$av+(nQ37B+IYFe=#kOMP?kqfJC zpfE@)LRU^Ah+SNiT+9um8M%2Ux5bD~&ilo{ker*60}|ulGw%L{5l#bor~XmK5s;rw)7Mts@b2fxIdE&e(E@$cPl^zT+(ab<22FgO^BQqzhd ubWS3OE-p$g<_6M?+%sR^2sFR#_MU+uIX5K-B*wwX#l+0Y#=_3X$p8RL0E +{ + "thisFile": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/game_of_life_practical3b.hlscompile_summary", + "connectId": "", + "serverToken": "", + "timestamp": "0" +} + + +{ + "type": "ET_CmdStep", + "dateTimestamp": "Tue Oct 20 17:50:47 2026", + "timestampMillis": "1792515047583", + "buildStep": { + "cmdId": "26212046-3e3b-461a-b5bb-933a3659c9f1", + "name": "v++", + "logFile": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/logs/game_of_life_practical3b.steps.log", + "commandLine": "/opt/york/cs/net/xilinx_vivado-2025.1-x86_64-1/2025.1/Vitis/bin/unwrapped/lnx64.o/v++ -c --mode hls --config /home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/hls_config.cfg --work_dir game_of_life_practical3b ", + "args": [ + "-c", + "--mode", + "hls", + "--config", + "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/hls_config.cfg", + "--work_dir", + "game_of_life_practical3b" + ], + "iniFiles": [ + { + "path": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/hls_config.cfg", + "content": "part=xc7z010clg400-1\n\n[hls]\nflow_target=vivado\npackage.output.format=ip_catalog\npackage.output.syn=false\ntb.file=testbench.cpp\nsyn.file=gameoflife.cpp\nsyn.file=gameoflife.h\nsyn.top=game_of_life_step" + } + ], + "cwd": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b" + } +} + + +{ + "type": "ET_Status", + "dateTimestamp": "Tue Oct 20 17:50:47 2026", + "timestampMillis": "1792515047583", + "status": { + "cmdId": "26212046-3e3b-461a-b5bb-933a3659c9f1", + "state": "CS_RUNNING" + } +} + + +{ + "type": "ET_FlowMetaData", + "dateTimestamp": "Tue Oct 20 17:50:57 2026", + "timestampMillis": "1792515057957", + "buildSummary": { + "hardwarePlatform": "", + "hardwareDsa": "", + "platformDirectory": "", + "runtime": "", + "systemConfig": "", + "flow": "BF_COMPILE", + "target": "TT_HW", + "kernels": [ + { + "base": { + "type": "KERNEL", + "name": "game_of_life_step", + "file": "", + "reports": [], + "uuid": "" + }, + "sources": [ + "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/gameoflife.cpp", + "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/gameoflife.h" + ], + "psSources": [], + "cuNames": [], + "type": "HLS", + "frequency": 0, + "freqUnits": "" + } + ], + "toolVersion": "2025.1" + } +} + + +{ + "type": "ET_SubCmdStep", + "dateTimestamp": "Tue Oct 20 17:50:57 2026", + "timestampMillis": "1792515057958", + "buildStep": { + "cmdId": "af5e477a-dfc6-4635-ba72-f96514ab12ad", + "name": "", + "logFile": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/hls/hls.log", + "commandLine": "", + "args": [], + "iniFiles": [], + "cwd": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b" + } +} + + +{ + "type": "ET_Status", + "dateTimestamp": "Tue Oct 20 17:50:57 2026", + "timestampMillis": "1792515057958", + "status": { + "cmdId": "af5e477a-dfc6-4635-ba72-f96514ab12ad", + "state": "CS_RUNNING" + } +} + + +{ + "type": "ET_Report", + "dateTimestamp": "Tue Oct 20 17:50:57 2026", + "timestampMillis": "1792515057958", + "report": { + "path": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/hls/hls_data.json", + "name": "", + "fileType": "JSON", + "reportType": "HLS_SYNTHESIS", + "cmdId": "" + } +} + + +{ + "type": "ET_Report", + "dateTimestamp": "Tue Oct 20 17:50:57 2026", + "timestampMillis": "1792515057958", + "report": { + "path": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/hls/syn/report/csynth.rpt", + "name": "", + "fileType": "TEXT", + "reportType": "HLS_SYNTHESIS", + "cmdId": "" + } +} + + +{ + "type": "ET_Report", + "dateTimestamp": "Tue Oct 20 17:50:57 2026", + "timestampMillis": "1792515057958", + "report": { + "path": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/hls/syn/report/csynth.xml", + "name": "", + "fileType": "XML", + "reportType": "HLS_SYNTHESIS", + "cmdId": "" + } +} + + +{ + "type": "ET_Report", + "dateTimestamp": "Tue Oct 20 17:51:22 2026", + "timestampMillis": "1792515082964", + "report": { + "path": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/hls", + "name": "", + "fileType": "DIR", + "reportType": "HLS_DATAFLOW", + "cmdId": "" + } +} + + +{ + "type": "ET_Report", + "dateTimestamp": "Tue Oct 20 17:51:22 2026", + "timestampMillis": "1792515082965", + "report": { + "path": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/hls", + "name": "", + "fileType": "DIR", + "reportType": "HLS_FUNCTION_CALL_GRAPH", + "cmdId": "" + } +} + + +{ + "type": "ET_Report", + "dateTimestamp": "Tue Oct 20 17:51:22 2026", + "timestampMillis": "1792515082965", + "report": { + "path": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/hls", + "name": "", + "fileType": "DIR", + "reportType": "HLS_SCHEDULE", + "cmdId": "" + } +} + + +{ + "type": "ET_Status", + "dateTimestamp": "Tue Oct 20 17:51:22 2026", + "timestampMillis": "1792515082965", + "status": { + "cmdId": "af5e477a-dfc6-4635-ba72-f96514ab12ad", + "state": "CS_PASSED" + } +} + + +{ + "type": "ET_Status", + "dateTimestamp": "Tue Oct 20 17:51:23 2026", + "timestampMillis": "1792515083151", + "status": { + "cmdId": "26212046-3e3b-461a-b5bb-933a3659c9f1", + "state": "CS_PASSED" + } +} + + +{ + "type": "ET_Report", + "dateTimestamp": "Tue Oct 20 17:51:23 2026", + "timestampMillis": "1792515083335", + "report": { + "path": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/reports/v++_compile_game_of_life_practical3b_guidance.html", + "name": "", + "fileType": "HTML", + "reportType": "GLOBAL_RULECHECK_GUIDANCE", + "cmdId": "" + } +} + + +{ + "type": "ET_Report", + "dateTimestamp": "Tue Oct 20 17:51:23 2026", + "timestampMillis": "1792515083336", + "report": { + "path": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/reports/.db/v++_compile_game_of_life_practical3b_guidance.pb3", + "name": "", + "fileType": "BINARY_PROTOBUF", + "reportType": "GLOBAL_RULECHECK_GUIDANCE", + "cmdId": "" + } +} + diff --git a/Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/game_of_life_practical3b.hlsrun_csim_summary b/Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/game_of_life_practical3b.hlsrun_csim_summary new file mode 100644 index 0000000..824adef --- /dev/null +++ b/Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/game_of_life_practical3b.hlsrun_csim_summary @@ -0,0 +1,157 @@ + +{ + "thisFile": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/game_of_life_practical3b.hlsrun_csim_summary", + "connectId": "", + "serverToken": "", + "timestamp": "0" +} + + +{ + "type": "ET_CmdStep", + "dateTimestamp": "Tue Oct 20 17:49:54 2026", + "timestampMillis": "1792514994026", + "buildStep": { + "cmdId": "611563b7-dc4a-4ed6-b71c-5c087f2ef0ef", + "name": "vitis-run", + "logFile": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/logs/game_of_life_practical3b.steps.log", + "commandLine": "/opt/york/cs/net/xilinx_vivado-2025.1-x86_64-1/2025.1/Vitis/bin/unwrapped/lnx64.o/vitis-run --mode hls --csim --config /home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/hls_config.cfg --work_dir game_of_life_practical3b ", + "args": [ + "--mode", + "hls", + "--csim", + "--config", + "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/hls_config.cfg", + "--work_dir", + "game_of_life_practical3b" + ], + "iniFiles": [], + "cwd": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b" + } +} + + +{ + "type": "ET_Status", + "dateTimestamp": "Tue Oct 20 17:49:54 2026", + "timestampMillis": "1792514994026", + "status": { + "cmdId": "611563b7-dc4a-4ed6-b71c-5c087f2ef0ef", + "state": "CS_RUNNING" + } +} + + +{ + "type": "ET_FlowMetaData", + "dateTimestamp": "Tue Oct 20 17:50:08 2026", + "timestampMillis": "1792515008064", + "buildSummary": { + "hardwarePlatform": "", + "hardwareDsa": "", + "platformDirectory": "", + "runtime": "", + "systemConfig": "", + "flow": "BF_RUN", + "target": "TT_HLS_C_SIM", + "kernels": [ + { + "base": { + "type": "KERNEL", + "name": "game_of_life_step", + "file": "", + "reports": [], + "uuid": "" + }, + "sources": [ + "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/gameoflife.cpp", + "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/gameoflife.h" + ], + "psSources": [], + "cuNames": [], + "type": "HLS", + "frequency": 0, + "freqUnits": "" + } + ], + "toolVersion": "2025.1" + } +} + + +{ + "type": "ET_SubCmdStep", + "dateTimestamp": "Tue Oct 20 17:50:08 2026", + "timestampMillis": "1792515008064", + "buildStep": { + "cmdId": "52087256-5096-452a-a117-2dd93b94b79f", + "name": "", + "logFile": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b/game_of_life_practical3b/hls/hls.log", + "commandLine": "", + "args": [], + "iniFiles": [], + "cwd": "/home/userfs/b/brs544/vitis_workspace/game_of_life_practical3b" + } +} + + +{ + "type": "ET_Status", + "dateTimestamp": "Tue Oct 20 17:50:08 2026", + "timestampMillis": "1792515008067", + "status": { + "cmdId": "52087256-5096-452a-a117-2dd93b94b79f", + "state": "CS_RUNNING" + } +} + + +{ + "type": "ET_Status", + "dateTimestamp": "Tue Oct 20 17:50:09 2026", + "timestampMillis": "1792515009671", + "status": { + "cmdId": "52087256-5096-452a-a117-2dd93b94b79f", + "state": "CS_PASSED" + } +} + + +{ + "type": "ET_Status", + "dateTimestamp": "Tue Oct 20 17:50:09 2026", + "timestampMillis": "1792515009780", + "status": { + "cmdId": "611563b7-dc4a-4ed6-b71c-5c087f2ef0ef", + "state": "CS_PASSED" + } +} + + +{ + "type": "ET_Report", + "dateTimestamp": "Tue Oct 20 17:50:09 2026", + "timestampMillis": "1792515009781", + "report": { + "path": "", + "name": "", + "fileType": "HTML", + "reportType": "GLOBAL_RULECHECK_GUIDANCE", + "cmdId": "" + } +} + + +{ + "type": "ET_Report", + "dateTimestamp": "Tue Oct 20 17:50:09 2026", + "timestampMillis": "1792515009782", + "report": { + "path": "", + "name": "", + "fileType": "BINARY_PROTOBUF", + "reportType": "GLOBAL_RULECHECK_GUIDANCE", + "cmdId": "" + } +} + diff --git a/Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g b/Practicals/practical-3b/game_of_life_practical3b/game_of_life_practical3b/hls/.autopilot/db/a.g new file mode 100644 index 0000000000000000000000000000000000000000..86c809db03c4ed4433eca85814e9a1ca0638579d GIT binary patch literal 131496 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