veriforge 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (573) hide show
  1. benchmarks/benchmark.py +1020 -0
  2. examples/axi/axi_crossbar_4x4/bench/axi_crossbar_4x4_bench.py +153 -0
  3. examples/axi/axi_crossbar_4x4/debug/debug_axi.py +113 -0
  4. examples/axi/axi_crossbar_4x4/debug/debug_width.py +108 -0
  5. examples/axi/axi_lite_example.py +90 -0
  6. examples/axi/axi_stream_example.py +74 -0
  7. examples/basics/alu.py +102 -0
  8. examples/basics/counter.py +54 -0
  9. examples/basics/fsm.py +103 -0
  10. examples/basics/shift_register.py +71 -0
  11. examples/basics/testbench.py +94 -0
  12. examples/composability/design_explorer.py +122 -0
  13. examples/composability/pipeline_generator.py +152 -0
  14. examples/composability/register_bank.py +191 -0
  15. examples/darkriscv/diag_compare.py +55 -0
  16. examples/darkriscv/profile_compiled.py +52 -0
  17. examples/darkriscv/run_fast.py +122 -0
  18. examples/darkriscv/run_sim.py +146 -0
  19. examples/debug/gen_partselect/test_gen_partselect.py +103 -0
  20. examples/debug/gen_port_partsel/test_gen_port_partsel.py +40 -0
  21. examples/debug/gen_unpacked_arr/test_gen_unpacked_arr.py +69 -0
  22. examples/femtorv/cosim_validate.py +57 -0
  23. examples/femtorv/gen_firmware.py +693 -0
  24. examples/femtorv/run_fast.py +137 -0
  25. examples/femtorv/run_sim.py +144 -0
  26. examples/ibex/gen_firmware.py +491 -0
  27. examples/ibex/model_test.py +51 -0
  28. examples/ibex/parse_test.py +47 -0
  29. examples/ibex/run_sim.py +174 -0
  30. examples/ibex/run_vcd.py +193 -0
  31. examples/ibex/vcd_compare_verilator.py +539 -0
  32. examples/library/cdc_example.py +31 -0
  33. examples/library/codec_example.py +25 -0
  34. examples/library/dsp_example.py +33 -0
  35. examples/library/fifo_example.py +28 -0
  36. examples/library/xilinx_example.py +34 -0
  37. examples/multi_iface_project/tb/multi_iface_tb.py +449 -0
  38. examples/pause_demo/pause_demo.py +856 -0
  39. examples/picorv32/cosim_validate.py +56 -0
  40. examples/picorv32/gen_firmware.py +735 -0
  41. examples/picorv32/run_fast.py +137 -0
  42. examples/picorv32/run_sim.py +133 -0
  43. examples/pulp/axi/axi_cdc/bench/axi_cdc_bench.py +295 -0
  44. examples/pulp/axi/axi_cdc/run_sim.py +313 -0
  45. examples/pulp/axi/axi_fifo/bench/axi_fifo_bench.py +300 -0
  46. examples/pulp/axi/axi_fifo/run_sim.py +342 -0
  47. examples/pulp/axi/axi_lite_dw_converter/bench/axi_lite_dw_bench.py +432 -0
  48. examples/pulp/axi/axi_lite_dw_converter/run_sim.py +480 -0
  49. examples/pulp/axi/axi_lite_mailbox/bench/axi_lite_mailbox_bench.py +143 -0
  50. examples/pulp/axi/axi_lite_mailbox/run_sim.py +160 -0
  51. examples/pulp/axi/axi_lite_regs/bench/axi_lite_regs_bench.py +93 -0
  52. examples/pulp/axi/axi_lite_regs/run_sim.py +310 -0
  53. examples/pulp/axi/axi_lite_to_axi/bench/axi_lite_to_axi_bench.py +118 -0
  54. examples/pulp/axi/axi_lite_to_axi/run_sim.py +230 -0
  55. examples/pulp/axi/axi_lite_xbar/bench/axi_lite_xbar_bench.py +116 -0
  56. examples/pulp/axi/axi_lite_xbar/run_sim.py +226 -0
  57. examples/pulp/axi/axi_mem_flat/bench/axi_mem_flat_bench.py +102 -0
  58. examples/pulp/axi/axi_to_axi_lite/bench/axi_to_axi_lite_bench.py +160 -0
  59. examples/pulp/axi/axi_to_axi_lite/run_sim.py +244 -0
  60. examples/pulp/axi/axi_xbar/bench/axi_xbar_bench.py +358 -0
  61. examples/pulp/axi/axi_xbar/run_sim.py +408 -0
  62. examples/pulp/common_cells/binary_to_gray/run_sim.py +91 -0
  63. examples/pulp/common_cells/cc_onehot/run_sim.py +91 -0
  64. examples/pulp/common_cells/cdc_2phase/run_sim.py +219 -0
  65. examples/pulp/common_cells/cdc_2phase_clearable/run_sim.py +475 -0
  66. examples/pulp/common_cells/cdc_4phase/run_sim.py +273 -0
  67. examples/pulp/common_cells/cdc_fifo/run_sim.py +237 -0
  68. examples/pulp/common_cells/cdc_fifo_gray/run_sim.py +211 -0
  69. examples/pulp/common_cells/cdc_reset_ctrlr/run_sim.py +339 -0
  70. examples/pulp/common_cells/counter/run_sim.py +94 -0
  71. examples/pulp/common_cells/credit_counter/run_sim.py +93 -0
  72. examples/pulp/common_cells/delta_counter/run_sim.py +91 -0
  73. examples/pulp/common_cells/edge_detect/run_sim.py +95 -0
  74. examples/pulp/common_cells/edge_propagator_ack/run_sim.py +98 -0
  75. examples/pulp/common_cells/edge_propagator_rx/run_sim.py +94 -0
  76. examples/pulp/common_cells/edge_propagator_tx/run_sim.py +92 -0
  77. examples/pulp/common_cells/exp_backoff/run_sim.py +92 -0
  78. examples/pulp/common_cells/fall_through_register/run_sim.py +217 -0
  79. examples/pulp/common_cells/fifo_v3/run_sim.py +276 -0
  80. examples/pulp/common_cells/gray_to_binary/run_sim.py +91 -0
  81. examples/pulp/common_cells/heaviside/run_sim.py +91 -0
  82. examples/pulp/common_cells/isochronous_4phase_handshake/run_sim.py +199 -0
  83. examples/pulp/common_cells/isochronous_spill_register/run_sim.py +258 -0
  84. examples/pulp/common_cells/lfsr_8bit/run_sim.py +92 -0
  85. examples/pulp/common_cells/lossy_valid_to_stream/run_sim.py +206 -0
  86. examples/pulp/common_cells/lzc/run_sim.py +91 -0
  87. examples/pulp/common_cells/max_counter/run_sim.py +94 -0
  88. examples/pulp/common_cells/onehot_to_bin/run_sim.py +91 -0
  89. examples/pulp/common_cells/passthrough_stream_fifo/run_sim.py +253 -0
  90. examples/pulp/common_cells/plru_tree/run_sim.py +92 -0
  91. examples/pulp/common_cells/popcount/run_sim.py +109 -0
  92. examples/pulp/common_cells/read/run_sim.py +91 -0
  93. examples/pulp/common_cells/ring_buffer/run_sim.py +92 -0
  94. examples/pulp/common_cells/rr_arb_tree/run_sim.py +192 -0
  95. examples/pulp/common_cells/rstgen/run_sim.py +182 -0
  96. examples/pulp/common_cells/rstgen_bypass/run_sim.py +182 -0
  97. examples/pulp/common_cells/serial_deglitch/run_sim.py +92 -0
  98. examples/pulp/common_cells/shift_reg/run_sim.py +93 -0
  99. examples/pulp/common_cells/spill_register/bench/spill_register_bench.py +94 -0
  100. examples/pulp/common_cells/spill_register/run_sim.py +231 -0
  101. examples/pulp/common_cells/spill_register_flushable/run_sim.py +212 -0
  102. examples/pulp/common_cells/stream_arbiter/run_sim.py +211 -0
  103. examples/pulp/common_cells/stream_arbiter_flushable/run_sim.py +228 -0
  104. examples/pulp/common_cells/stream_delay/run_sim.py +193 -0
  105. examples/pulp/common_cells/stream_demux/run_sim.py +147 -0
  106. examples/pulp/common_cells/stream_fifo/bench/stream_fifo_bench.py +98 -0
  107. examples/pulp/common_cells/stream_fifo/run_sim.py +240 -0
  108. examples/pulp/common_cells/stream_fifo_optimal_wrap/run_sim.py +245 -0
  109. examples/pulp/common_cells/stream_filter/run_sim.py +146 -0
  110. examples/pulp/common_cells/stream_fork/run_sim.py +197 -0
  111. examples/pulp/common_cells/stream_fork_dynamic/run_sim.py +205 -0
  112. examples/pulp/common_cells/stream_join/run_sim.py +144 -0
  113. examples/pulp/common_cells/stream_mux/run_sim.py +156 -0
  114. examples/pulp/common_cells/stream_omega_net/run_sim.py +289 -0
  115. examples/pulp/common_cells/stream_register/bench/stream_register_bench.py +91 -0
  116. examples/pulp/common_cells/stream_register/run_sim.py +213 -0
  117. examples/pulp/common_cells/stream_throttle/run_sim.py +235 -0
  118. examples/pulp/common_cells/stream_to_mem/run_sim.py +277 -0
  119. examples/pulp/common_cells/stream_xbar/run_sim.py +220 -0
  120. examples/pulp/common_cells/stream_xbar_typed/run_sim.py +190 -0
  121. examples/pulp/common_cells/sub_per_hash/oracle_vectors.py +168 -0
  122. examples/pulp/common_cells/sub_per_hash/run_sim.py +92 -0
  123. examples/pulp/common_cells/sync/run_sim.py +208 -0
  124. examples/pulp/common_cells/sync_wedge/run_sim.py +210 -0
  125. examples/pulp/common_cells/trip_counter/run_sim.py +93 -0
  126. examples/pulp/common_cells/unread/run_sim.py +91 -0
  127. examples/python_testbench/axi_stream_loopback.py +152 -0
  128. examples/python_testbench/multi_domain_axis.py +171 -0
  129. examples/serv/cosim_validate.py +76 -0
  130. examples/serv/gen_firmware.py +693 -0
  131. examples/serv/run_fast.py +174 -0
  132. examples/serv/run_sim.py +165 -0
  133. examples/taxi/tb/test_axil_ram.py +170 -0
  134. examples/taxi/tb/test_axis_adapter.py +233 -0
  135. examples/taxi/tb/test_axis_arb_mux.py +183 -0
  136. examples/taxi/tb/test_axis_async_fifo.py +210 -0
  137. examples/taxi/tb/test_axis_broadcast.py +157 -0
  138. examples/taxi/tb/test_axis_register.py +211 -0
  139. src/veriforge/__init__.py +39 -0
  140. src/veriforge/__main__.py +1427 -0
  141. src/veriforge/_version.py +1 -0
  142. src/veriforge/analysis/__init__.py +64 -0
  143. src/veriforge/analysis/clock_reset.py +438 -0
  144. src/veriforge/analysis/const_fold.py +351 -0
  145. src/veriforge/analysis/lint.py +502 -0
  146. src/veriforge/analysis/resolver.py +494 -0
  147. src/veriforge/analysis/width_inference.py +341 -0
  148. src/veriforge/codegen/__init__.py +17 -0
  149. src/veriforge/codegen/format_style.py +63 -0
  150. src/veriforge/codegen/verilog_emitter.py +1354 -0
  151. src/veriforge/codegen/verilog_formatter.py +1014 -0
  152. src/veriforge/convert/__init__.py +5 -0
  153. src/veriforge/convert/to_dsl.py +1078 -0
  154. src/veriforge/dsl/__init__.py +81 -0
  155. src/veriforge/dsl/builder.py +1960 -0
  156. src/veriforge/dsl/interface.py +293 -0
  157. src/veriforge/dsl/lib/__init__.py +55 -0
  158. src/veriforge/dsl/lib/axi.py +76 -0
  159. src/veriforge/dsl/lib/axi_stream.py +119 -0
  160. src/veriforge/dsl/lib/cdc.py +114 -0
  161. src/veriforge/dsl/lib/codec.py +113 -0
  162. src/veriforge/dsl/lib/dsp.py +291 -0
  163. src/veriforge/dsl/lib/fifo.py +96 -0
  164. src/veriforge/dsl/lib/xilinx.py +152 -0
  165. src/veriforge/dsl/ram.py +265 -0
  166. src/veriforge/dsl/testbench.py +1732 -0
  167. src/veriforge/dsl/testbench_deps.py +147 -0
  168. src/veriforge/lark_file/__init__.py +0 -0
  169. src/veriforge/lark_file/gen_tree.py +256 -0
  170. src/veriforge/lark_file/parse_metadata.py +656 -0
  171. src/veriforge/model/__init__.py +189 -0
  172. src/veriforge/model/assignments.py +40 -0
  173. src/veriforge/model/base.py +185 -0
  174. src/veriforge/model/behavioral.py +79 -0
  175. src/veriforge/model/design.py +282 -0
  176. src/veriforge/model/expressions.py +427 -0
  177. src/veriforge/model/functions.py +146 -0
  178. src/veriforge/model/generate.py +270 -0
  179. src/veriforge/model/instances.py +168 -0
  180. src/veriforge/model/interface.py +175 -0
  181. src/veriforge/model/nets.py +88 -0
  182. src/veriforge/model/package.py +128 -0
  183. src/veriforge/model/parameters.py +59 -0
  184. src/veriforge/model/ports.py +100 -0
  185. src/veriforge/model/specify.py +63 -0
  186. src/veriforge/model/statements.py +658 -0
  187. src/veriforge/model/sv_types.py +330 -0
  188. src/veriforge/model/variables.py +89 -0
  189. src/veriforge/preprocessor.py +719 -0
  190. src/veriforge/project.py +360 -0
  191. src/veriforge/refactor/__init__.py +79 -0
  192. src/veriforge/refactor/_boundary_models.py +189 -0
  193. src/veriforge/refactor/_boundary_pull_push.py +81 -0
  194. src/veriforge/refactor/_boundary_selection.py +472 -0
  195. src/veriforge/refactor/_boundary_validation.py +161 -0
  196. src/veriforge/refactor/_extract_classify.py +641 -0
  197. src/veriforge/refactor/_extract_models.py +230 -0
  198. src/veriforge/refactor/_pull_up_engine.py +3323 -0
  199. src/veriforge/refactor/_push_down_engine.py +267 -0
  200. src/veriforge/refactor/_refactor_utils.py +244 -0
  201. src/veriforge/refactor/diagnostics.py +21 -0
  202. src/veriforge/refactor/hierarchy_boundary.py +320 -0
  203. src/veriforge/refactor/hierarchy_collapse.py +295 -0
  204. src/veriforge/refactor/hierarchy_extract.py +2689 -0
  205. src/veriforge/refactor/hierarchy_graph.py +390 -0
  206. src/veriforge/refactor/visualization.py +118 -0
  207. src/veriforge/scaffold.py +361 -0
  208. src/veriforge/sim/__init__.py +38 -0
  209. src/veriforge/sim/bench/__init__.py +119 -0
  210. src/veriforge/sim/bench/interfaces.py +798 -0
  211. src/veriforge/sim/bench/lowering.py +2339 -0
  212. src/veriforge/sim/bench/plan.py +381 -0
  213. src/veriforge/sim/bench/planner.py +596 -0
  214. src/veriforge/sim/bench/runtime.py +471 -0
  215. src/veriforge/sim/compiled/__init__.py +19 -0
  216. src/veriforge/sim/compiled/_codegen_utils.py +118 -0
  217. src/veriforge/sim/compiled/_expr_emitter.py +1986 -0
  218. src/veriforge/sim/compiled/_gen_narrow_accessors.py +12 -0
  219. src/veriforge/sim/compiled/_gen_narrow_assign.py +12 -0
  220. src/veriforge/sim/compiled/_gen_narrow_stage.py +12 -0
  221. src/veriforge/sim/compiled/_gen_narrow_tail.py +12 -0
  222. src/veriforge/sim/compiled/_gen_sections.py +1168 -0
  223. src/veriforge/sim/compiled/_gen_wide_section.py +1092 -0
  224. src/veriforge/sim/compiled/_process_compiler.py +1439 -0
  225. src/veriforge/sim/compiled/_stmt_emitters.py +2366 -0
  226. src/veriforge/sim/compiled/_wide_emitter.py +4008 -0
  227. src/veriforge/sim/compiled/codegen.py +1718 -0
  228. src/veriforge/sim/compiled/compiled_scheduler.py +1172 -0
  229. src/veriforge/sim/compiled/compiler.py +406 -0
  230. src/veriforge/sim/cosim.py +802 -0
  231. src/veriforge/sim/elaborate.py +2460 -0
  232. src/veriforge/sim/endpoints/__init__.py +73 -0
  233. src/veriforge/sim/endpoints/_generator.py +96 -0
  234. src/veriforge/sim/endpoints/axi4_master.py +348 -0
  235. src/veriforge/sim/endpoints/axi4_responder.py +365 -0
  236. src/veriforge/sim/endpoints/axi_lite_common.py +65 -0
  237. src/veriforge/sim/endpoints/axi_lite_master.py +169 -0
  238. src/veriforge/sim/endpoints/axi_lite_request_driver.py +69 -0
  239. src/veriforge/sim/endpoints/axi_lite_responder.py +323 -0
  240. src/veriforge/sim/endpoints/axi_lite_response_driver.py +58 -0
  241. src/veriforge/sim/endpoints/axis_sink.py +203 -0
  242. src/veriforge/sim/endpoints/axis_source.py +145 -0
  243. src/veriforge/sim/endpoints/detect.py +737 -0
  244. src/veriforge/sim/endpoints/frame.py +353 -0
  245. src/veriforge/sim/endpoints/helpers.py +388 -0
  246. src/veriforge/sim/endpoints/membus_master.py +122 -0
  247. src/veriforge/sim/endpoints/membus_responder.py +151 -0
  248. src/veriforge/sim/endpoints/pause.py +114 -0
  249. src/veriforge/sim/endpoints/stream_sink.py +86 -0
  250. src/veriforge/sim/endpoints/stream_source.py +105 -0
  251. src/veriforge/sim/evaluator.py +888 -0
  252. src/veriforge/sim/event_queue.py +266 -0
  253. src/veriforge/sim/example_runner.py +30 -0
  254. src/veriforge/sim/executor.py +1342 -0
  255. src/veriforge/sim/scheduler.py +1483 -0
  256. src/veriforge/sim/step_harness.py +33 -0
  257. src/veriforge/sim/testbench.py +489 -0
  258. src/veriforge/sim/trace.py +140 -0
  259. src/veriforge/sim/value.py +605 -0
  260. src/veriforge/sim/vcd.py +266 -0
  261. src/veriforge/sim/vcd_compare.py +342 -0
  262. src/veriforge/sim/vm/__init__.py +22 -0
  263. src/veriforge/sim/vm/compiler.py +2601 -0
  264. src/veriforge/sim/vm/interpreter.py +1022 -0
  265. src/veriforge/sim/vm/opcodes.py +151 -0
  266. src/veriforge/sim/vm/vm_scheduler.py +1569 -0
  267. src/veriforge/transforms/__init__.py +6 -0
  268. src/veriforge/transforms/_assignments.py +176 -0
  269. src/veriforge/transforms/_declarations.py +943 -0
  270. src/veriforge/transforms/_design_builder.py +700 -0
  271. src/veriforge/transforms/_expressions.py +1348 -0
  272. src/veriforge/transforms/_functions_tasks.py +474 -0
  273. src/veriforge/transforms/_generate.py +362 -0
  274. src/veriforge/transforms/_instances.py +401 -0
  275. src/veriforge/transforms/_statements.py +1089 -0
  276. src/veriforge/transforms/_tree_utils.py +48 -0
  277. src/veriforge/transforms/comment_extractor.py +164 -0
  278. src/veriforge/transforms/tree_to_model.py +1278 -0
  279. src/veriforge/verilog_parser.py +63 -0
  280. tests/__init__.py +0 -0
  281. tests/conftest.py +340 -0
  282. tests/test_analysis/test_block_locals.py +58 -0
  283. tests/test_analysis/test_clock_reset.py +403 -0
  284. tests/test_analysis/test_clock_reset_hier.py +99 -0
  285. tests/test_analysis/test_const_fold.py +628 -0
  286. tests/test_analysis/test_generate_improvements.py +546 -0
  287. tests/test_analysis/test_interface.py +389 -0
  288. tests/test_analysis/test_lint.py +504 -0
  289. tests/test_analysis/test_package.py +570 -0
  290. tests/test_analysis/test_struct_union.py +755 -0
  291. tests/test_analysis/test_typedef_enum.py +366 -0
  292. tests/test_analysis/test_width_inference.py +522 -0
  293. tests/test_dsl/__init__.py +0 -0
  294. tests/test_dsl/test_axi4_mem_example.py +92 -0
  295. tests/test_dsl/test_axi_cdc_pulp_example.py +53 -0
  296. tests/test_dsl/test_axi_fifo_pulp_example.py +51 -0
  297. tests/test_dsl/test_axi_lite_dw_pulp_example.py +57 -0
  298. tests/test_dsl/test_axi_lite_mailbox_pulp_example.py +88 -0
  299. tests/test_dsl/test_axi_lite_regs_example.py +92 -0
  300. tests/test_dsl/test_axi_lite_to_axi_pulp_example.py +50 -0
  301. tests/test_dsl/test_axi_lite_xbar_pulp_example.py +53 -0
  302. tests/test_dsl/test_axi_to_axi_lite_pulp_example.py +53 -0
  303. tests/test_dsl/test_axi_xbar_pulp_example.py +53 -0
  304. tests/test_dsl/test_builder.py +2919 -0
  305. tests/test_dsl/test_builder_errors_m24.py +478 -0
  306. tests/test_dsl/test_builder_errors_m9.py +314 -0
  307. tests/test_dsl/test_convert_to_dsl.py +1289 -0
  308. tests/test_dsl/test_dsl_boundary.py +210 -0
  309. tests/test_dsl/test_examples.py +351 -0
  310. tests/test_dsl/test_lib_axi.py +324 -0
  311. tests/test_dsl/test_lib_cdc.py +191 -0
  312. tests/test_dsl/test_lib_codec.py +186 -0
  313. tests/test_dsl/test_lib_dsp.py +251 -0
  314. tests/test_dsl/test_lib_fifo.py +156 -0
  315. tests/test_dsl/test_lib_xilinx.py +171 -0
  316. tests/test_dsl/test_ram.py +276 -0
  317. tests/test_dsl/test_roundtrip_dsl.py +1174 -0
  318. tests/test_dsl/test_sv_dsl.py +645 -0
  319. tests/test_dsl/test_sv_interface_emit.py +233 -0
  320. tests/test_dsl/test_taxi_axil_ram.py +98 -0
  321. tests/test_dsl/test_taxi_axis_adapter.py +208 -0
  322. tests/test_dsl/test_taxi_axis_arb_mux.py +116 -0
  323. tests/test_dsl/test_taxi_axis_async_fifo.py +136 -0
  324. tests/test_dsl/test_taxi_axis_async_fifo_dualclk.py +221 -0
  325. tests/test_dsl/test_taxi_axis_broadcast.py +97 -0
  326. tests/test_dsl/test_taxi_axis_register.py +116 -0
  327. tests/test_dsl/test_testbench.py +484 -0
  328. tests/test_dsl/test_testbench_bench_style.py +350 -0
  329. tests/test_dsl/test_testbench_deps.py +96 -0
  330. tests/test_dsl/test_testbench_enhanced.py +182 -0
  331. tests/test_formatter/__init__.py +0 -0
  332. tests/test_formatter/test_formatter.py +716 -0
  333. tests/test_lsp/__init__.py +0 -0
  334. tests/test_lsp/test_lark_fallback.py +137 -0
  335. tests/test_lsp/test_navigation.py +120 -0
  336. tests/test_lsp/test_symbols.py +85 -0
  337. tests/test_lsp/test_trace.py +2641 -0
  338. tests/test_model/__init__.py +0 -0
  339. tests/test_model/conftest.py +17 -0
  340. tests/test_model/test_analysis.py +930 -0
  341. tests/test_model/test_behavioral.py +780 -0
  342. tests/test_model/test_comment_roundtrip.py +172 -0
  343. tests/test_model/test_comments.py +257 -0
  344. tests/test_model/test_corpus.py +495 -0
  345. tests/test_model/test_functions_generate.py +911 -0
  346. tests/test_model/test_instances.py +461 -0
  347. tests/test_model/test_module.py +475 -0
  348. tests/test_model/test_roundtrip.py +140 -0
  349. tests/test_model/test_specify.py +360 -0
  350. tests/test_model/test_tree_to_model_characterization.py +129 -0
  351. tests/test_model/test_typed_sv_regressions.py +156 -0
  352. tests/test_partial_assign.py +170 -0
  353. tests/test_preprocessor/__init__.py +0 -0
  354. tests/test_preprocessor/test_preprocessor.py +774 -0
  355. tests/test_project/__init__.py +0 -0
  356. tests/test_project/test_darkriscv.py +216 -0
  357. tests/test_project/test_project.py +1271 -0
  358. tests/test_refactor/test_hierarchy_graph.py +5926 -0
  359. tests/test_sim/__init__.py +590 -0
  360. tests/test_sim/test_axi_lite_master.py +438 -0
  361. tests/test_sim/test_axis_endpoints.py +258 -0
  362. tests/test_sim/test_axis_frame.py +119 -0
  363. tests/test_sim/test_bench_native.py +1695 -0
  364. tests/test_sim/test_bench_plan.py +372 -0
  365. tests/test_sim/test_bench_planner.py +367 -0
  366. tests/test_sim/test_bench_runtime.py +266 -0
  367. tests/test_sim/test_combinational_coordinator.py +193 -0
  368. tests/test_sim/test_compiled.py +60771 -0
  369. tests/test_sim/test_compiled_batch_run_propagation.py +142 -0
  370. tests/test_sim/test_compiled_latent_risks.py +174 -0
  371. tests/test_sim/test_coordinator_strict.py +349 -0
  372. tests/test_sim/test_darkriscv_constructs.py +2339 -0
  373. tests/test_sim/test_evaluator.py +492 -0
  374. tests/test_sim/test_executor.py +699 -0
  375. tests/test_sim/test_function_task.py +499 -0
  376. tests/test_sim/test_generate.py +1014 -0
  377. tests/test_sim/test_generator_endpoint.py +147 -0
  378. tests/test_sim/test_hierarchy.py +1140 -0
  379. tests/test_sim/test_ibex_examples.py +7763 -0
  380. tests/test_sim/test_interface_detection.py +433 -0
  381. tests/test_sim/test_membus_endpoints.py +599 -0
  382. tests/test_sim/test_memory.py +1937 -0
  383. tests/test_sim/test_multi_domain_runner.py +213 -0
  384. tests/test_sim/test_param_width.py +576 -0
  385. tests/test_sim/test_planner_naming_fallback.py +198 -0
  386. tests/test_sim/test_precedence_and_fixes.py +1063 -0
  387. tests/test_sim/test_pulp_axi_examples.py +3261 -0
  388. tests/test_sim/test_pulp_common_cells_examples.py +1216 -0
  389. tests/test_sim/test_pulp_ready_valid_examples.py +3716 -0
  390. tests/test_sim/test_scheduler.py +470 -0
  391. tests/test_sim/test_sim_sv.py +1030 -0
  392. tests/test_sim/test_stream_protocol.py +146 -0
  393. tests/test_sim/test_structural_patterns.py +1722 -0
  394. tests/test_sim/test_testbench.py +714 -0
  395. tests/test_sim/test_value.py +475 -0
  396. tests/test_sim/test_value_widths.py +545 -0
  397. tests/test_sim/test_vcd.py +269 -0
  398. tests/test_sim/test_vm.py +3591 -0
  399. tests/test_sim/test_wide_signal_catchall.py +271 -0
  400. tests/test_validation/__init__.py +0 -0
  401. tests/test_validation/test_iverilog_validation.py +2226 -0
  402. tests/test_validation/test_vm_vs_reference.py +1279 -0
  403. tests/test_verilog_parser/test_all.py +106 -0
  404. tests/test_verilog_parser/test_rule_examples.py +118 -0
  405. tests/test_verilog_parser/test_section_a1.py +143 -0
  406. tests/test_verilog_parser/test_section_a2.py +247 -0
  407. tests/test_verilog_parser/test_section_a6.py +245 -0
  408. tests/test_verilog_parser/test_section_a8.py +299 -0
  409. tests/test_verilog_parser/test_sv_features.py +590 -0
  410. tools/check_overview.py +214 -0
  411. tools/validate_compiled_pytest.py +245 -0
  412. veriforge/__init__.py +39 -0
  413. veriforge/__main__.py +1427 -0
  414. veriforge/_version.py +1 -0
  415. veriforge/analysis/__init__.py +64 -0
  416. veriforge/analysis/clock_reset.py +438 -0
  417. veriforge/analysis/const_fold.py +351 -0
  418. veriforge/analysis/lint.py +502 -0
  419. veriforge/analysis/resolver.py +494 -0
  420. veriforge/analysis/width_inference.py +341 -0
  421. veriforge/codegen/__init__.py +17 -0
  422. veriforge/codegen/format_style.py +63 -0
  423. veriforge/codegen/verilog_emitter.py +1354 -0
  424. veriforge/codegen/verilog_formatter.py +1014 -0
  425. veriforge/convert/__init__.py +5 -0
  426. veriforge/convert/to_dsl.py +1078 -0
  427. veriforge/dsl/__init__.py +81 -0
  428. veriforge/dsl/builder.py +1960 -0
  429. veriforge/dsl/interface.py +293 -0
  430. veriforge/dsl/lib/__init__.py +55 -0
  431. veriforge/dsl/lib/axi.py +76 -0
  432. veriforge/dsl/lib/axi_stream.py +119 -0
  433. veriforge/dsl/lib/cdc.py +114 -0
  434. veriforge/dsl/lib/codec.py +113 -0
  435. veriforge/dsl/lib/dsp.py +291 -0
  436. veriforge/dsl/lib/fifo.py +96 -0
  437. veriforge/dsl/lib/xilinx.py +152 -0
  438. veriforge/dsl/ram.py +265 -0
  439. veriforge/dsl/testbench.py +1732 -0
  440. veriforge/dsl/testbench_deps.py +147 -0
  441. veriforge/lark_file/__init__.py +0 -0
  442. veriforge/lark_file/gen_tree.py +256 -0
  443. veriforge/lark_file/parse_metadata.py +656 -0
  444. veriforge/model/__init__.py +189 -0
  445. veriforge/model/assignments.py +40 -0
  446. veriforge/model/base.py +185 -0
  447. veriforge/model/behavioral.py +79 -0
  448. veriforge/model/design.py +282 -0
  449. veriforge/model/expressions.py +427 -0
  450. veriforge/model/functions.py +146 -0
  451. veriforge/model/generate.py +270 -0
  452. veriforge/model/instances.py +168 -0
  453. veriforge/model/interface.py +175 -0
  454. veriforge/model/nets.py +88 -0
  455. veriforge/model/package.py +128 -0
  456. veriforge/model/parameters.py +59 -0
  457. veriforge/model/ports.py +100 -0
  458. veriforge/model/specify.py +63 -0
  459. veriforge/model/statements.py +658 -0
  460. veriforge/model/sv_types.py +330 -0
  461. veriforge/model/variables.py +89 -0
  462. veriforge/preprocessor.py +719 -0
  463. veriforge/project.py +360 -0
  464. veriforge/refactor/__init__.py +79 -0
  465. veriforge/refactor/_boundary_models.py +189 -0
  466. veriforge/refactor/_boundary_pull_push.py +81 -0
  467. veriforge/refactor/_boundary_selection.py +472 -0
  468. veriforge/refactor/_boundary_validation.py +161 -0
  469. veriforge/refactor/_extract_classify.py +641 -0
  470. veriforge/refactor/_extract_models.py +230 -0
  471. veriforge/refactor/_pull_up_engine.py +3323 -0
  472. veriforge/refactor/_push_down_engine.py +267 -0
  473. veriforge/refactor/_refactor_utils.py +244 -0
  474. veriforge/refactor/diagnostics.py +21 -0
  475. veriforge/refactor/hierarchy_boundary.py +320 -0
  476. veriforge/refactor/hierarchy_collapse.py +295 -0
  477. veriforge/refactor/hierarchy_extract.py +2689 -0
  478. veriforge/refactor/hierarchy_graph.py +390 -0
  479. veriforge/refactor/visualization.py +118 -0
  480. veriforge/scaffold.py +361 -0
  481. veriforge/sim/__init__.py +38 -0
  482. veriforge/sim/bench/__init__.py +119 -0
  483. veriforge/sim/bench/interfaces.py +798 -0
  484. veriforge/sim/bench/lowering.py +2339 -0
  485. veriforge/sim/bench/plan.py +381 -0
  486. veriforge/sim/bench/planner.py +596 -0
  487. veriforge/sim/bench/runtime.py +471 -0
  488. veriforge/sim/compiled/__init__.py +19 -0
  489. veriforge/sim/compiled/_codegen_utils.py +118 -0
  490. veriforge/sim/compiled/_expr_emitter.py +1986 -0
  491. veriforge/sim/compiled/_gen_narrow_accessors.py +12 -0
  492. veriforge/sim/compiled/_gen_narrow_assign.py +12 -0
  493. veriforge/sim/compiled/_gen_narrow_stage.py +12 -0
  494. veriforge/sim/compiled/_gen_narrow_tail.py +12 -0
  495. veriforge/sim/compiled/_gen_sections.py +1168 -0
  496. veriforge/sim/compiled/_gen_wide_section.py +1092 -0
  497. veriforge/sim/compiled/_process_compiler.py +1439 -0
  498. veriforge/sim/compiled/_stmt_emitters.py +2366 -0
  499. veriforge/sim/compiled/_wide_emitter.py +4008 -0
  500. veriforge/sim/compiled/codegen.py +1718 -0
  501. veriforge/sim/compiled/compiled_scheduler.py +1172 -0
  502. veriforge/sim/compiled/compiler.py +406 -0
  503. veriforge/sim/compiled/templates/narrow_accessors.pxi +81 -0
  504. veriforge/sim/compiled/templates/narrow_assign.pxi +6379 -0
  505. veriforge/sim/compiled/templates/narrow_stage.pxi +5165 -0
  506. veriforge/sim/compiled/templates/narrow_tail.pxi +144 -0
  507. veriforge/sim/cosim.py +802 -0
  508. veriforge/sim/elaborate.py +2460 -0
  509. veriforge/sim/endpoints/__init__.py +73 -0
  510. veriforge/sim/endpoints/_generator.py +96 -0
  511. veriforge/sim/endpoints/axi4_master.py +348 -0
  512. veriforge/sim/endpoints/axi4_responder.py +365 -0
  513. veriforge/sim/endpoints/axi_lite_common.py +65 -0
  514. veriforge/sim/endpoints/axi_lite_master.py +169 -0
  515. veriforge/sim/endpoints/axi_lite_request_driver.py +69 -0
  516. veriforge/sim/endpoints/axi_lite_responder.py +323 -0
  517. veriforge/sim/endpoints/axi_lite_response_driver.py +58 -0
  518. veriforge/sim/endpoints/axis_sink.py +203 -0
  519. veriforge/sim/endpoints/axis_source.py +145 -0
  520. veriforge/sim/endpoints/detect.py +737 -0
  521. veriforge/sim/endpoints/frame.py +353 -0
  522. veriforge/sim/endpoints/helpers.py +388 -0
  523. veriforge/sim/endpoints/membus_master.py +122 -0
  524. veriforge/sim/endpoints/membus_responder.py +151 -0
  525. veriforge/sim/endpoints/pause.py +114 -0
  526. veriforge/sim/endpoints/stream_sink.py +86 -0
  527. veriforge/sim/endpoints/stream_source.py +105 -0
  528. veriforge/sim/evaluator.py +888 -0
  529. veriforge/sim/event_queue.py +266 -0
  530. veriforge/sim/example_runner.py +30 -0
  531. veriforge/sim/executor.py +1342 -0
  532. veriforge/sim/scheduler.py +1483 -0
  533. veriforge/sim/step_harness.py +33 -0
  534. veriforge/sim/testbench.py +489 -0
  535. veriforge/sim/trace.py +140 -0
  536. veriforge/sim/value.py +605 -0
  537. veriforge/sim/vcd.py +266 -0
  538. veriforge/sim/vcd_compare.py +342 -0
  539. veriforge/sim/vm/__init__.py +22 -0
  540. veriforge/sim/vm/compiler.py +2601 -0
  541. veriforge/sim/vm/interpreter.py +1022 -0
  542. veriforge/sim/vm/opcodes.py +151 -0
  543. veriforge/sim/vm/vm_scheduler.py +1569 -0
  544. veriforge/transforms/__init__.py +6 -0
  545. veriforge/transforms/_assignments.py +176 -0
  546. veriforge/transforms/_declarations.py +943 -0
  547. veriforge/transforms/_design_builder.py +700 -0
  548. veriforge/transforms/_expressions.py +1348 -0
  549. veriforge/transforms/_functions_tasks.py +474 -0
  550. veriforge/transforms/_generate.py +362 -0
  551. veriforge/transforms/_instances.py +401 -0
  552. veriforge/transforms/_statements.py +1089 -0
  553. veriforge/transforms/_tree_utils.py +48 -0
  554. veriforge/transforms/comment_extractor.py +164 -0
  555. veriforge/transforms/tree_to_model.py +1278 -0
  556. veriforge/verilog_parser.py +63 -0
  557. veriforge-0.0.1.dist-info/METADATA +271 -0
  558. veriforge-0.0.1.dist-info/RECORD +573 -0
  559. veriforge-0.0.1.dist-info/WHEEL +5 -0
  560. veriforge-0.0.1.dist-info/entry_points.txt +3 -0
  561. veriforge-0.0.1.dist-info/licenses/LICENSE +21 -0
  562. veriforge-0.0.1.dist-info/top_level.txt +7 -0
  563. veriforge_lsp/__init__.py +0 -0
  564. veriforge_lsp/__main__.py +6 -0
  565. veriforge_lsp/handlers/__init__.py +0 -0
  566. veriforge_lsp/handlers/extended.py +1807 -0
  567. veriforge_lsp/handlers/navigation.py +172 -0
  568. veriforge_lsp/handlers/symbols.py +210 -0
  569. veriforge_lsp/handlers/text_sync.py +58 -0
  570. veriforge_lsp/index.py +111 -0
  571. veriforge_lsp/protocol.py +64 -0
  572. veriforge_lsp/server.py +184 -0
  573. veriforge_lsp/workspace.py +664 -0
@@ -0,0 +1,1020 @@
1
+ #!/usr/bin/env python3
2
+ """Simulation benchmark — all engines and external simulators.
3
+
4
+ Benchmarks Reference, VM (Python), VM (Cython), Compiled (step), and
5
+ Compiled (batch) engines against Icarus Verilog and Verilator (if found).
6
+
7
+ Usage:
8
+ uv run python benchmarks/benchmark.py # 50K cycles, console output
9
+ uv run python benchmarks/benchmark.py --cycles 100000 # more cycles
10
+ uv run python benchmarks/benchmark.py --update # also write notes/benchmarks.md
11
+ uv run python benchmarks/benchmark.py --profile # cProfile on reference engine
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import argparse
17
+ import cProfile
18
+ import io
19
+ import os
20
+ import platform
21
+ import pstats
22
+ import shutil
23
+ import subprocess
24
+ import sys
25
+ import tempfile
26
+ import time
27
+ from datetime import datetime, timezone
28
+
29
+ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
30
+ sys.path.insert(0, os.path.join(ROOT, "src"))
31
+
32
+ from veriforge.sim.testbench import Clock, Simulator
33
+ from veriforge.sim.value import Value
34
+ from veriforge.transforms import tree_to_design
35
+ from veriforge.verilog_parser import verilog_parser
36
+
37
+ # ──────────────────────────────────────────────────────────────────────────────
38
+ # DUT
39
+ # ──────────────────────────────────────────────────────────────────────────────
40
+ # Medium-high complexity designed to exercise the major simulation paths:
41
+ # - 8-bit ALU: 12 operations including carry via concatenation LHS
42
+ # - 8-entry register file: memory array with synchronous write
43
+ # - 4-state FSM: IDLE → LOAD → EXEC → STORE cycle
44
+ # - Free-running counters: 16-bit cycle counter + 8-bit phase counter
45
+ # - 16-bit LFSR: XOR-feedback shift register
46
+ # - 16-bit accumulator: conditional on FSM state
47
+ # - Continuous-assign chain: 4 assigns with mux and reduction-OR
48
+ # - Counter-driven stimulus: inputs change every cycle without a testbench driver
49
+ #
50
+ # Ports: input clk, rst; output [15:0] accum_out.
51
+ # Clock and reset are driven by testbench wrappers (not internal initial blocks)
52
+ # for the VM / compiled engine runners.
53
+
54
+ BENCH_DUT = """\
55
+ module bench(
56
+ input wire clk,
57
+ input wire rst,
58
+ output wire [15:0] accum_out
59
+ );
60
+
61
+ // ── Free-running counters ──
62
+ reg [15:0] cycle_count;
63
+ reg [7:0] phase;
64
+
65
+ always @(posedge clk or posedge rst) begin
66
+ if (rst) begin
67
+ cycle_count <= 16'd0;
68
+ phase <= 8'd0;
69
+ end else begin
70
+ cycle_count <= cycle_count + 16'd1;
71
+ phase <= phase + 8'd1;
72
+ end
73
+ end
74
+
75
+ // ── 8-entry register file ──
76
+ reg [7:0] regfile [0:7];
77
+ reg [2:0] wr_addr, rd_addr_a, rd_addr_b;
78
+ reg [7:0] wr_data;
79
+ reg wr_en;
80
+ wire [7:0] rd_data_a, rd_data_b;
81
+
82
+ assign rd_data_a = regfile[rd_addr_a];
83
+ assign rd_data_b = regfile[rd_addr_b];
84
+
85
+ always @(posedge clk) begin
86
+ if (wr_en)
87
+ regfile[wr_addr] <= wr_data;
88
+ end
89
+
90
+ // ── ALU ──
91
+ reg [3:0] alu_op;
92
+ reg [7:0] alu_a, alu_b;
93
+ reg [7:0] alu_result;
94
+ reg alu_zero, alu_carry;
95
+
96
+ always @(*) begin
97
+ alu_carry = 1'b0;
98
+ case (alu_op)
99
+ 4'd0: alu_result = alu_a + alu_b;
100
+ 4'd1: alu_result = alu_a - alu_b;
101
+ 4'd2: alu_result = alu_a & alu_b;
102
+ 4'd3: alu_result = alu_a | alu_b;
103
+ 4'd4: alu_result = alu_a ^ alu_b;
104
+ 4'd5: alu_result = ~alu_a;
105
+ 4'd6: alu_result = alu_a << alu_b[2:0];
106
+ 4'd7: alu_result = alu_a >> alu_b[2:0];
107
+ 4'd8: begin
108
+ {alu_carry, alu_result} = alu_a + alu_b;
109
+ end
110
+ 4'd9: alu_result = alu_a * alu_b;
111
+ 4'd10: alu_result = (alu_a > alu_b) ? alu_a : alu_b;
112
+ 4'd11: alu_result = (alu_a < alu_b) ? alu_a : alu_b;
113
+ default: alu_result = 8'd0;
114
+ endcase
115
+ alu_zero = (alu_result == 8'd0);
116
+ end
117
+
118
+ // ── FSM ──
119
+ reg [1:0] state, next_state;
120
+ parameter S_IDLE = 2'd0;
121
+ parameter S_LOAD = 2'd1;
122
+ parameter S_EXEC = 2'd2;
123
+ parameter S_STORE = 2'd3;
124
+
125
+ always @(posedge clk or posedge rst) begin
126
+ if (rst)
127
+ state <= S_IDLE;
128
+ else
129
+ state <= next_state;
130
+ end
131
+
132
+ always @(*) begin
133
+ next_state = state;
134
+ wr_en = 1'b0;
135
+ case (state)
136
+ S_IDLE: next_state = S_LOAD;
137
+ S_LOAD: next_state = S_EXEC;
138
+ S_EXEC: next_state = S_STORE;
139
+ S_STORE: begin
140
+ wr_en = 1'b1;
141
+ next_state = S_IDLE;
142
+ end
143
+ endcase
144
+ end
145
+
146
+ // ── Stimulus generation (driven by counters) ──
147
+ always @(posedge clk) begin
148
+ if (!rst) begin
149
+ alu_op <= phase[3:0];
150
+ alu_a <= cycle_count[7:0];
151
+ alu_b <= cycle_count[15:8] ^ phase;
152
+ rd_addr_a <= phase[2:0];
153
+ rd_addr_b <= phase[2:0] + 3'd1;
154
+ wr_addr <= phase[5:3];
155
+ wr_data <= alu_result;
156
+ end
157
+ end
158
+
159
+ // ── Continuous assign chain ──
160
+ wire [7:0] sum_ab;
161
+ wire [7:0] diff_ab;
162
+ wire [7:0] combined;
163
+ wire flag;
164
+
165
+ assign sum_ab = rd_data_a + rd_data_b;
166
+ assign diff_ab = rd_data_a - rd_data_b;
167
+ assign combined = (phase[0]) ? sum_ab : diff_ab;
168
+ assign flag = |combined;
169
+
170
+ // ── LFSR ──
171
+ reg [15:0] shift_reg;
172
+ always @(posedge clk or posedge rst) begin
173
+ if (rst)
174
+ shift_reg <= 16'd1;
175
+ else
176
+ shift_reg <= {shift_reg[14:0], shift_reg[15] ^ shift_reg[13]};
177
+ end
178
+
179
+ // ── Accumulator ──
180
+ reg [15:0] accum;
181
+ always @(posedge clk or posedge rst) begin
182
+ if (rst)
183
+ accum <= 16'd0;
184
+ else if (state == S_STORE)
185
+ accum <= accum + {8'd0, alu_result};
186
+ end
187
+
188
+ assign accum_out = accum;
189
+
190
+ endmodule
191
+ """
192
+
193
+ DUT_DESCRIPTION = "ALU + RegFile + FSM + Counter + LFSR + Accumulator + Continuous Assigns"
194
+
195
+
196
+ # ──────────────────────────────────────────────────────────────────────────────
197
+ # Tool discovery
198
+ # ──────────────────────────────────────────────────────────────────────────────
199
+
200
+ _WINDOWS_SEARCH_DIRS = [
201
+ r"C:\iverilog\bin",
202
+ r"C:\Program Files\Icarus Verilog\bin",
203
+ r"C:\Program Files (x86)\Icarus Verilog\bin",
204
+ ]
205
+
206
+ _VERILATOR_WINDOWS_DIRS = [
207
+ r"C:\msys64\mingw64\bin",
208
+ r"C:\verilator\bin",
209
+ ]
210
+
211
+
212
+ def _find_tool(name: str, extra_dirs: list[str] | None = None) -> str | None:
213
+ env_val = os.environ.get(name.upper())
214
+ if env_val and os.path.isfile(env_val):
215
+ return env_val
216
+ found = shutil.which(name)
217
+ if found:
218
+ return found
219
+ search = extra_dirs or []
220
+ if sys.platform == "win32":
221
+ search = _WINDOWS_SEARCH_DIRS + search
222
+ for d in search:
223
+ for suffix in ("", ".exe"):
224
+ candidate = os.path.join(d, name + suffix)
225
+ if os.path.isfile(candidate):
226
+ return candidate
227
+ return None
228
+
229
+
230
+ IVERILOG = _find_tool("iverilog")
231
+ VVP = _find_tool("vvp")
232
+
233
+
234
+ # ──────────────────────────────────────────────────────────────────────────────
235
+ # Machine info
236
+ # ──────────────────────────────────────────────────────────────────────────────
237
+
238
+
239
+ def _get_cpu_name() -> str:
240
+ if sys.platform.startswith("linux"):
241
+ try:
242
+ with open("/proc/cpuinfo", encoding="utf-8") as f:
243
+ for line in f:
244
+ if line.startswith("model name"):
245
+ return line.split(":", 1)[1].strip()
246
+ except OSError:
247
+ pass
248
+ name = platform.processor()
249
+ if name:
250
+ return name
251
+ return platform.machine() or "unknown"
252
+
253
+
254
+ def _get_ram_gb() -> float | None:
255
+ try:
256
+ import psutil # type: ignore[import]
257
+
258
+ return psutil.virtual_memory().total / 1024**3
259
+ except ImportError:
260
+ pass
261
+ if sys.platform.startswith("linux"):
262
+ try:
263
+ with open("/proc/meminfo", encoding="utf-8") as f:
264
+ for line in f:
265
+ if line.startswith("MemTotal:"):
266
+ kb = int(line.split()[1])
267
+ return kb / 1024**2
268
+ except OSError:
269
+ pass
270
+ if sys.platform == "win32":
271
+ try:
272
+ import ctypes
273
+
274
+ class MEMORYSTATUSEX(ctypes.Structure):
275
+ _fields_ = [
276
+ ("dwLength", ctypes.c_ulong),
277
+ ("dwMemoryLoad", ctypes.c_ulong),
278
+ ("ullTotalPhys", ctypes.c_ulonglong),
279
+ ("ullAvailPhys", ctypes.c_ulonglong),
280
+ ("ullTotalPageFile", ctypes.c_ulonglong),
281
+ ("ullAvailPageFile", ctypes.c_ulonglong),
282
+ ("ullTotalVirtual", ctypes.c_ulonglong),
283
+ ("ullAvailVirtual", ctypes.c_ulonglong),
284
+ ("ullAvailExtendedVirtual", ctypes.c_ulonglong),
285
+ ]
286
+
287
+ stat = MEMORYSTATUSEX()
288
+ stat.dwLength = ctypes.sizeof(MEMORYSTATUSEX)
289
+ ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
290
+ return stat.ullTotalPhys / 1024**3
291
+ except Exception:
292
+ pass
293
+ return None
294
+
295
+
296
+ def collect_machine_info() -> dict:
297
+ uname = platform.uname()
298
+ info: dict = {
299
+ "os": f"{uname.system} {uname.release}".strip(),
300
+ "cpu": _get_cpu_name(),
301
+ "cores": os.cpu_count() or 1,
302
+ "python": f"{platform.python_implementation()} {platform.python_version()}",
303
+ "iverilog": _iverilog_version(),
304
+ "verilator": _verilator_version(),
305
+ }
306
+ ram = _get_ram_gb()
307
+ if ram is not None:
308
+ info["ram_gb"] = ram
309
+ return info
310
+
311
+
312
+ def _iverilog_version() -> str | None:
313
+ if not IVERILOG:
314
+ return None
315
+ try:
316
+ r = subprocess.run(
317
+ [IVERILOG, "-V"],
318
+ capture_output=True,
319
+ text=True,
320
+ timeout=5,
321
+ check=False,
322
+ )
323
+ for line in (r.stdout + r.stderr).splitlines():
324
+ if "Icarus Verilog" in line:
325
+ return line.strip()
326
+ except Exception:
327
+ pass
328
+ return None
329
+
330
+
331
+ def _verilator_version() -> str | None:
332
+ verilator = _find_tool("verilator", extra_dirs=_VERILATOR_WINDOWS_DIRS)
333
+ if not verilator:
334
+ return None
335
+ try:
336
+ r = subprocess.run(
337
+ [verilator, "--version"],
338
+ capture_output=True,
339
+ text=True,
340
+ timeout=5,
341
+ check=False,
342
+ )
343
+ line = (r.stdout + r.stderr).splitlines()[0].strip() if (r.stdout + r.stderr) else None
344
+ return line
345
+ except Exception:
346
+ return None
347
+
348
+
349
+ # ──────────────────────────────────────────────────────────────────────────────
350
+ # Capability detection
351
+ # ──────────────────────────────────────────────────────────────────────────────
352
+
353
+
354
+ def _has_vm_cython() -> bool:
355
+ try:
356
+ from veriforge.sim.vm.vm_scheduler import _HAS_CYTHON # noqa: PLC0415
357
+
358
+ return bool(_HAS_CYTHON)
359
+ except Exception:
360
+ return False
361
+
362
+
363
+ # ──────────────────────────────────────────────────────────────────────────────
364
+ # Testbench source helpers
365
+ # ──────────────────────────────────────────────────────────────────────────────
366
+
367
+
368
+ def _make_ref_src(sim_time: int) -> str:
369
+ """Append self-contained clock + reset initial blocks to BENCH_DUT."""
370
+ return BENCH_DUT.replace(
371
+ "endmodule",
372
+ f"""\
373
+ initial begin
374
+ clk = 0;
375
+ rst = 1;
376
+ #20 rst = 0;
377
+ #{sim_time};
378
+ $finish;
379
+ end
380
+
381
+ initial forever #5 clk = ~clk;
382
+
383
+ endmodule
384
+ """,
385
+ )
386
+
387
+
388
+ def _make_icarus_src(sim_time: int) -> str:
389
+ tb = f"""\
390
+ module bench_tb;
391
+ reg clk, rst;
392
+ wire [15:0] accum_out;
393
+
394
+ bench dut(.clk(clk), .rst(rst), .accum_out(accum_out));
395
+
396
+ initial begin
397
+ clk = 0;
398
+ rst = 1;
399
+ #20 rst = 0;
400
+ #{sim_time};
401
+ $finish;
402
+ end
403
+
404
+ always #5 clk = ~clk;
405
+ endmodule
406
+ """
407
+ return BENCH_DUT + "\n" + tb
408
+
409
+
410
+ def _make_verilator_cpp(cycles: int) -> str:
411
+ return f"""\
412
+ #include "Vbench.h"
413
+ #include "verilated.h"
414
+ #include <cstdio>
415
+ #include <chrono>
416
+
417
+ int main(int argc, char** argv) {{
418
+ Verilated::commandArgs(argc, argv);
419
+ Vbench* dut = new Vbench;
420
+
421
+ dut->rst = 1;
422
+ dut->clk = 0;
423
+ for (int i = 0; i < 4; i++) {{
424
+ dut->clk = !dut->clk;
425
+ dut->eval();
426
+ }}
427
+ dut->rst = 0;
428
+
429
+ auto t0 = std::chrono::high_resolution_clock::now();
430
+
431
+ for (int i = 0; i < {cycles} * 2; i++) {{
432
+ dut->clk = !dut->clk;
433
+ dut->eval();
434
+ }}
435
+
436
+ auto t1 = std::chrono::high_resolution_clock::now();
437
+ double elapsed = std::chrono::duration<double>(t1 - t0).count();
438
+
439
+ printf("%.6f\\n", elapsed);
440
+
441
+ dut->final();
442
+ delete dut;
443
+ return 0;
444
+ }}
445
+ """
446
+
447
+
448
+ def _parse_design():
449
+ p = verilog_parser(start="source_text")
450
+ tree = p.build_tree(BENCH_DUT)
451
+ return tree_to_design(tree)
452
+
453
+
454
+ # ──────────────────────────────────────────────────────────────────────────────
455
+ # Engine runners — each returns {"time": float, "throughput": float} or {"error": str}
456
+ # ──────────────────────────────────────────────────────────────────────────────
457
+
458
+
459
+ def run_reference(cycles: int, max_time: int, sim_time: int) -> dict:
460
+ ref_src = _make_ref_src(sim_time)
461
+ p = verilog_parser(start="source_text")
462
+ tree = p.build_tree(ref_src)
463
+ design = tree_to_design(tree)
464
+ module = design.modules[0]
465
+
466
+ sim = Simulator(module)
467
+ sim._sched.executor.loop_limit = max(100_000, max_time * 4) # type: ignore[attr-defined]
468
+
469
+ t0 = time.perf_counter()
470
+ sim.run(max_time=max_time)
471
+ elapsed = time.perf_counter() - t0
472
+ return {"time": elapsed, "throughput": cycles / elapsed if elapsed > 0 else 0}
473
+
474
+
475
+ def run_vm_python(cycles: int, max_time: int) -> dict:
476
+ """VM engine in pure-Python bytecode mode (no Cython extension)."""
477
+ design = _parse_design()
478
+ module = design.modules[0]
479
+
480
+ sim = Simulator(module, engine="vm") # force_python=True
481
+ clk = sim.signal("clk")
482
+ sim.fork(Clock(clk, period=10))
483
+
484
+ def test(s):
485
+ s.drive("rst", Value(1, width=1))
486
+ s._sched.schedule_at(20, ("clock_toggle", "rst", Value(0, width=1)))
487
+
488
+ t0 = time.perf_counter()
489
+ sim.run(test, max_time=max_time)
490
+ elapsed = time.perf_counter() - t0
491
+ return {"time": elapsed, "throughput": cycles / elapsed if elapsed > 0 else 0}
492
+
493
+
494
+ def run_vm_cython(cycles: int, max_time: int) -> dict:
495
+ """VM engine with Cython C delta loop."""
496
+ if not _has_vm_cython():
497
+ return {"error": "Cython VM extension not built (run: uv run python setup_cython.py build_ext --inplace)"}
498
+
499
+ design = _parse_design()
500
+ module = design.modules[0]
501
+
502
+ sim = Simulator(module, engine="vm-fast") # force_python=False, uses Cython
503
+ clk = sim.signal("clk")
504
+ sim.fork(Clock(clk, period=10))
505
+
506
+ def test(s):
507
+ s.drive("rst", Value(1, width=1))
508
+ s._sched.schedule_at(20, ("clock_toggle", "rst", Value(0, width=1)))
509
+
510
+ t0 = time.perf_counter()
511
+ sim.run(test, max_time=max_time)
512
+ elapsed = time.perf_counter() - t0
513
+ return {"time": elapsed, "throughput": cycles / elapsed if elapsed > 0 else 0}
514
+
515
+
516
+ def run_compiled_step(cycles: int, max_time: int) -> dict:
517
+ design = _parse_design()
518
+ module = design.modules[0]
519
+
520
+ sim = Simulator(module, engine="compiled")
521
+ clk = sim.signal("clk")
522
+ sim.fork(Clock(clk, period=10))
523
+
524
+ def test(s):
525
+ s.drive("rst", Value(1, width=1))
526
+ s._sched.schedule_at(20, ("clock_toggle", "rst", Value(0, width=1)))
527
+
528
+ t0 = time.perf_counter()
529
+ sim.run(test, max_time=max_time)
530
+ elapsed = time.perf_counter() - t0
531
+ return {"time": elapsed, "throughput": cycles / elapsed if elapsed > 0 else 0}
532
+
533
+
534
+ def run_compiled_batch(cycles: int) -> dict:
535
+ design = _parse_design()
536
+ module = design.modules[0]
537
+
538
+ sim = Simulator(module, engine="compiled")
539
+ from typing import Any # noqa: PLC0415
540
+
541
+ csim: Any = sim._sched._sim # type: ignore[attr-defined]
542
+
543
+ sim.drive("rst", Value(1, width=1))
544
+ sim.drive("clk", Value(0, width=1))
545
+ csim.snapshot()
546
+ sim.drive("clk", Value(1, width=1))
547
+ csim.step()
548
+ csim.snapshot()
549
+ sim.drive("clk", Value(0, width=1))
550
+ csim.step()
551
+ sim.drive("rst", Value(0, width=1))
552
+ csim.snapshot()
553
+ sim.drive("clk", Value(1, width=1))
554
+ csim.step()
555
+ csim.snapshot()
556
+ sim.drive("clk", Value(0, width=1))
557
+ csim.step()
558
+
559
+ t0 = time.perf_counter()
560
+ sim.batch_run(cycles, "clk", clock_period=10)
561
+ elapsed = time.perf_counter() - t0
562
+ return {"time": elapsed, "throughput": cycles / elapsed if elapsed > 0 else 0}
563
+
564
+
565
+ def run_icarus(cycles: int, sim_time: int) -> dict:
566
+ if not IVERILOG or not VVP:
567
+ return {"error": "iverilog/vvp not found"}
568
+
569
+ src = _make_icarus_src(sim_time)
570
+ with tempfile.TemporaryDirectory() as tmpdir:
571
+ src_path = os.path.join(tmpdir, "bench.v")
572
+ out_path = os.path.join(tmpdir, "bench.out")
573
+ with open(src_path, "w", encoding="utf-8") as f:
574
+ f.write(src)
575
+
576
+ t0 = time.perf_counter()
577
+ r = subprocess.run(
578
+ [IVERILOG, "-o", out_path, src_path],
579
+ capture_output=True,
580
+ text=True,
581
+ timeout=60,
582
+ check=False,
583
+ )
584
+ compile_time = time.perf_counter() - t0
585
+ if r.returncode != 0:
586
+ return {"error": f"compile failed: {r.stderr.strip()[:200]}"}
587
+
588
+ t0 = time.perf_counter()
589
+ subprocess.run(
590
+ [VVP, out_path],
591
+ capture_output=True,
592
+ text=True,
593
+ timeout=600,
594
+ cwd=tmpdir,
595
+ check=False,
596
+ )
597
+ run_time = time.perf_counter() - t0
598
+
599
+ return {
600
+ "compile_time": compile_time,
601
+ "time": run_time,
602
+ "throughput": cycles / run_time if run_time > 0 else 0,
603
+ }
604
+
605
+
606
+ def run_verilator(cycles: int) -> dict:
607
+ verilator = _find_tool("verilator", extra_dirs=_VERILATOR_WINDOWS_DIRS)
608
+ if not verilator:
609
+ return {"error": "verilator not found"}
610
+
611
+ with tempfile.TemporaryDirectory() as tmpdir:
612
+ dut_path = os.path.join(tmpdir, "bench.v")
613
+ tb_path = os.path.join(tmpdir, "sim_main.cpp")
614
+ with open(dut_path, "w", encoding="utf-8") as f:
615
+ f.write(BENCH_DUT)
616
+ with open(tb_path, "w", encoding="utf-8") as f:
617
+ f.write(_make_verilator_cpp(cycles))
618
+
619
+ t0 = time.perf_counter()
620
+ r = subprocess.run(
621
+ [verilator, "--cc", "--exe", "--build", "-Wno-fatal", "-o", "bench_sim", dut_path, tb_path],
622
+ capture_output=True,
623
+ text=True,
624
+ timeout=120,
625
+ cwd=tmpdir,
626
+ check=False,
627
+ )
628
+ compile_time = time.perf_counter() - t0
629
+ if r.returncode != 0:
630
+ return {"error": f"verilator build failed: {r.stderr.strip()[:200]}"}
631
+
632
+ exe_path = os.path.join(tmpdir, "obj_dir", "bench_sim")
633
+ if not os.path.isfile(exe_path):
634
+ for cand in [
635
+ os.path.join(tmpdir, "bench_sim"),
636
+ os.path.join(tmpdir, "obj_dir", "Vbench"),
637
+ ]:
638
+ if os.path.isfile(cand):
639
+ exe_path = cand
640
+ break
641
+ else:
642
+ return {"error": "verilator: compiled binary not found"}
643
+
644
+ t0 = time.perf_counter()
645
+ r = subprocess.run(
646
+ [exe_path],
647
+ capture_output=True,
648
+ text=True,
649
+ timeout=300,
650
+ cwd=tmpdir,
651
+ check=False,
652
+ )
653
+ run_time = time.perf_counter() - t0
654
+
655
+ # Prefer the elapsed time printed by the binary (avoids subprocess overhead)
656
+ stdout = r.stdout.strip()
657
+ try:
658
+ run_time = float(stdout.splitlines()[-1])
659
+ except (ValueError, IndexError):
660
+ pass
661
+
662
+ return {
663
+ "compile_time": compile_time,
664
+ "time": run_time,
665
+ "throughput": cycles / run_time if run_time > 0 else 0,
666
+ }
667
+
668
+
669
+ # ──────────────────────────────────────────────────────────────────────────────
670
+ # Profiling
671
+ # ──────────────────────────────────────────────────────────────────────────────
672
+
673
+
674
+ def profile_reference(_cycles: int, max_time: int, sim_time: int, top_n: int = 30) -> None:
675
+ ref_src = _make_ref_src(sim_time)
676
+ p = verilog_parser(start="source_text")
677
+ tree = p.build_tree(ref_src)
678
+ design = tree_to_design(tree)
679
+ module = design.modules[0]
680
+
681
+ sim = Simulator(module)
682
+ sim._sched.executor.loop_limit = max(100_000, max_time * 4) # type: ignore[attr-defined]
683
+
684
+ pr = cProfile.Profile()
685
+ pr.enable()
686
+ sim.run(max_time=max_time)
687
+ pr.disable()
688
+
689
+ for sort_key, label in [("cumulative", "cumulative"), ("tottime", "total (self)")]:
690
+ s = io.StringIO()
691
+ ps = pstats.Stats(pr, stream=s)
692
+ ps.sort_stats(sort_key)
693
+ print(f"\n{'=' * 80}")
694
+ print(f"PROFILE — top {top_n} functions by {label} time")
695
+ print("=" * 80)
696
+ ps.print_stats(top_n)
697
+ print(s.getvalue())
698
+
699
+
700
+ # ──────────────────────────────────────────────────────────────────────────────
701
+ # Markdown generation
702
+ # ──────────────────────────────────────────────────────────────────────────────
703
+
704
+ _ENGINE_ORDER = [
705
+ "Compiled (batch)",
706
+ "Verilator",
707
+ "Compiled (step)",
708
+ "VM (Cython)",
709
+ "Icarus Verilog",
710
+ "VM (Python)",
711
+ "Reference",
712
+ ]
713
+
714
+
715
+ def _sort_key(name: str, tp: float) -> tuple:
716
+ try:
717
+ order = _ENGINE_ORDER.index(name)
718
+ except ValueError:
719
+ order = len(_ENGINE_ORDER)
720
+ return (-tp, order)
721
+
722
+
723
+ def _fmt_tp(tp: float) -> str:
724
+ if tp >= 1_000_000:
725
+ return f"{tp / 1_000_000:.2f}M cyc/s"
726
+ if tp >= 1_000:
727
+ return f"{tp / 1_000:.1f}K cyc/s"
728
+ return f"{tp:.0f} cyc/s"
729
+
730
+
731
+ def generate_markdown(
732
+ results: dict[str, dict],
733
+ cycles: int,
734
+ machine_info: dict,
735
+ ) -> str:
736
+ now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
737
+
738
+ lines: list[str] = []
739
+
740
+ # ── Header ───────────────────────────────────────────────────────────────
741
+ lines += [
742
+ "# Simulation Benchmarks",
743
+ "",
744
+ "> Auto-generated by `benchmarks/benchmark.py`. Edit the script, not this file.",
745
+ f"> Last updated: {now}",
746
+ "",
747
+ "## How to Update",
748
+ "",
749
+ "```bash",
750
+ "# Quick run (50K cycles, console only)",
751
+ "uv run python benchmarks/benchmark.py",
752
+ "",
753
+ "# Write results to this file",
754
+ "uv run python benchmarks/benchmark.py --update",
755
+ "",
756
+ "# Longer run for more stable numbers",
757
+ "uv run python benchmarks/benchmark.py --cycles 200000 --update",
758
+ "```",
759
+ "",
760
+ "Icarus Verilog and Verilator are optional — the script detects them automatically on both Windows and Linux.",
761
+ "The Cython-accelerated VM and compiled engines require:",
762
+ "",
763
+ "```bash",
764
+ "uv run python setup_cython.py build_ext --inplace",
765
+ "```",
766
+ "",
767
+ "## Benchmark DUT",
768
+ "",
769
+ f"**{DUT_DESCRIPTION}** — a single self-contained Verilog module that exercises:",
770
+ "",
771
+ "- **ALU** — 12 operations (add, sub, and, or, xor, not, shifts, multiply, "
772
+ "min, max, add-with-carry via concatenation LHS)",
773
+ "- **Register file** — 8-entry memory array, synchronous write, async read via `assign`",
774
+ "- **FSM** — 4-state IDLE→LOAD→EXEC→STORE cycle, drives register write-enable",
775
+ "- **Counters** — 16-bit cycle counter + 8-bit phase counter",
776
+ "- **LFSR** — 16-bit XOR-feedback shift register",
777
+ "- **Accumulator** — 16-bit, conditional on FSM STORE state",
778
+ "- **Continuous-assign chain** — 4 assigns with mux and reduction-OR",
779
+ "- **Counter-driven stimulus** — inputs change every cycle with no external driver",
780
+ "",
781
+ "This combination exercises: expression evaluation, sensitivity analysis, "
782
+ "NBA scheduling, delta cycles, `always @(*)` re-triggering, and memory array reads.",
783
+ "",
784
+ "## Engines",
785
+ "",
786
+ "| Engine | Description |",
787
+ "|--------|-------------|",
788
+ "| Reference | Pure-Python tree-walking interpreter — baseline for correctness |",
789
+ "| VM (Python) | Stack-based bytecode interpreter, pure Python dispatch |",
790
+ "| VM (Cython) | Same bytecode, C delta loop via Cython extension (`_interp_fast.pyx`) |",
791
+ "| Compiled (step) | Design-specific Cython `.pyx`, event-driven step mode |",
792
+ "| Compiled (batch) | Same compiled code, `nogil` C loop — no Python per cycle |",
793
+ "| Icarus Verilog | Industry-standard interpreted simulator (external process) |",
794
+ "| Verilator | Compiled C++ simulator (external process) |",
795
+ "",
796
+ ]
797
+
798
+ # ── Machine info ─────────────────────────────────────────────────────────
799
+ lines += [
800
+ "## Test Machine",
801
+ "",
802
+ "| | |",
803
+ "|---|---|",
804
+ f"| OS | {machine_info.get('os', 'unknown')} |",
805
+ f"| CPU | {machine_info.get('cpu', 'unknown')} |",
806
+ f"| Cores | {machine_info.get('cores', '?')} |",
807
+ ]
808
+ if "ram_gb" in machine_info:
809
+ lines.append(f"| RAM | {machine_info['ram_gb']:.0f} GB |")
810
+ lines.append(f"| Python | {machine_info.get('python', 'unknown')} |")
811
+ if machine_info.get("iverilog"):
812
+ lines.append(f"| Icarus | {machine_info['iverilog']} |")
813
+ if machine_info.get("verilator"):
814
+ lines.append(f"| Verilator | {machine_info['verilator']} |")
815
+ lines.append("")
816
+
817
+ # ── Results table ────────────────────────────────────────────────────────
818
+ lines += [
819
+ "## Results",
820
+ "",
821
+ f"DUT: {DUT_DESCRIPTION} — **{cycles:,} cycles**",
822
+ "",
823
+ ]
824
+
825
+ if not results:
826
+ lines.append("*No results — run the benchmark script.*")
827
+ lines.append("")
828
+ return "\n".join(lines)
829
+
830
+ icarus_tp = results.get("Icarus Verilog", {}).get("throughput", 0.0)
831
+ ref_tp = results.get("Reference", {}).get("throughput", 0.0)
832
+ baseline_tp = icarus_tp or ref_tp
833
+
834
+ sorted_results = sorted(
835
+ results.items(),
836
+ key=lambda kv: _sort_key(kv[0], kv[1].get("throughput", 0.0)),
837
+ )
838
+
839
+ lines.append("| Engine | Time | Throughput | vs Icarus |")
840
+ lines.append("|--------|------|-----------|-----------|")
841
+
842
+ for name, r in sorted_results:
843
+ if "error" in r and r.get("throughput", 0) == 0:
844
+ lines.append(f"| {name} | — | *skipped* | — |")
845
+ continue
846
+
847
+ t = r.get("time", 0.0)
848
+ tp = r.get("throughput", 0.0)
849
+ time_str = f"{t:.3f}s"
850
+ tp_str = _fmt_tp(tp)
851
+
852
+ if name == "Icarus Verilog":
853
+ vs = "baseline"
854
+ elif baseline_tp > 0 and tp > 0:
855
+ ratio = tp / baseline_tp
856
+ if ratio >= 1.05:
857
+ vs = f"{ratio:.1f}x faster"
858
+ elif ratio <= 0.95:
859
+ vs = f"{1.0 / ratio:.1f}x slower"
860
+ else:
861
+ vs = "~parity"
862
+ else:
863
+ vs = "—"
864
+
865
+ lines.append(f"| {name} | {time_str} | {tp_str} | {vs} |")
866
+
867
+ lines.append("")
868
+
869
+ # ── Notes on skipped engines ─────────────────────────────────────────────
870
+ skipped = [(name, r["error"]) for name, r in results.items() if "error" in r and r.get("throughput", 0) == 0]
871
+ if skipped:
872
+ lines += ["**Skipped:**", ""]
873
+ for name, err in skipped:
874
+ lines.append(f"- **{name}**: {err}")
875
+ lines.append("")
876
+
877
+ return "\n".join(lines)
878
+
879
+
880
+ # ──────────────────────────────────────────────────────────────────────────────
881
+ # Main
882
+ # ──────────────────────────────────────────────────────────────────────────────
883
+
884
+
885
+ def main() -> None: # noqa: PLR0912, PLR0915
886
+ ap = argparse.ArgumentParser(description="Simulation benchmark — all engines and simulators")
887
+ ap.add_argument(
888
+ "--cycles",
889
+ type=int,
890
+ default=50_000,
891
+ help="Clock cycles to simulate (default: 50000)",
892
+ )
893
+ ap.add_argument(
894
+ "--update",
895
+ action="store_true",
896
+ help="Write results to notes/benchmarks.md",
897
+ )
898
+ ap.add_argument(
899
+ "--profile",
900
+ action="store_true",
901
+ help="Run cProfile on reference engine and exit",
902
+ )
903
+ args = ap.parse_args()
904
+
905
+ cycles = args.cycles
906
+ sim_time = cycles * 10 + 20
907
+ max_time = sim_time + 20
908
+
909
+ machine_info = collect_machine_info()
910
+
911
+ print(f"{'=' * 70}")
912
+ print("Simulation Benchmark")
913
+ print(f" DUT: {DUT_DESCRIPTION}")
914
+ print(f" Python: {machine_info['python']}")
915
+ print(f" OS: {machine_info['os']}")
916
+ print(f" CPU: {machine_info['cpu']}")
917
+ print(f" Cores: {machine_info['cores']}")
918
+ if "ram_gb" in machine_info:
919
+ print(f" RAM: {machine_info['ram_gb']:.0f} GB")
920
+ print(f" Cycles: {cycles:,}")
921
+ print(f" iverilog:{' ' + IVERILOG if IVERILOG else ' not found'}")
922
+ verilator = _find_tool("verilator", extra_dirs=_VERILATOR_WINDOWS_DIRS)
923
+ print(f" verilator:{' ' + verilator if verilator else ' not found'}")
924
+ print(f" VM Cython: {'yes' if _has_vm_cython() else 'no (run setup_cython.py)'}")
925
+ print(f"{'=' * 70}")
926
+
927
+ if args.profile:
928
+ profile_reference(cycles, max_time, sim_time)
929
+ return
930
+
931
+ results: dict[str, dict] = {}
932
+ runners = [
933
+ ("Reference", lambda: run_reference(cycles, max_time, sim_time)),
934
+ ("VM (Python)", lambda: run_vm_python(cycles, max_time)),
935
+ ("VM (Cython)", lambda: run_vm_cython(cycles, max_time)),
936
+ ("Compiled (step)", lambda: run_compiled_step(cycles, max_time)),
937
+ ("Compiled (batch)", lambda: run_compiled_batch(cycles)),
938
+ ("Icarus Verilog", lambda: run_icarus(cycles, sim_time)),
939
+ ("Verilator", lambda: run_verilator(cycles)),
940
+ ]
941
+ warmup_runners = [
942
+ ("Reference", lambda: run_reference(200, 2200, 2020)),
943
+ ("VM (Python)", lambda: run_vm_python(200, 2200)),
944
+ ("VM (Cython)", lambda: run_vm_cython(200, 2200)),
945
+ ("Compiled (step)", lambda: run_compiled_step(200, 2200)),
946
+ ("Compiled (batch)", lambda: run_compiled_batch(200)),
947
+ ]
948
+ warmup_set = {name for name, _ in warmup_runners}
949
+ warmup_map = dict(warmup_runners)
950
+
951
+ total = len(runners)
952
+ for step, (name, run_fn) in enumerate(runners, 1):
953
+ print(f"\n[{step}/{total}] {name}...")
954
+ try:
955
+ if name in warmup_set:
956
+ print(" warming up...")
957
+ warmup_map[name]()
958
+ result = run_fn()
959
+ except Exception as e:
960
+ print(f" ERROR: {e}")
961
+ results[name] = {"error": str(e), "throughput": 0.0, "time": 0.0}
962
+ continue
963
+
964
+ if "error" in result and result.get("throughput", 0) == 0:
965
+ print(f" SKIPPED: {result['error']}")
966
+ results[name] = result
967
+ continue
968
+
969
+ t = result["time"]
970
+ tp = result["throughput"]
971
+ if result.get("compile_time"):
972
+ print(f" compile: {result['compile_time']:.3f}s")
973
+ print(f" run: {t:.3f}s")
974
+ print(f" throughput: {_fmt_tp(tp)}")
975
+ results[name] = result
976
+
977
+ # ── Summary comparison ────────────────────────────────────────────────
978
+ def _tp(name: str) -> float:
979
+ return results.get(name, {}).get("throughput", 0.0)
980
+
981
+ ref_tp = _tp("Reference")
982
+ vm_py_tp = _tp("VM (Python)")
983
+ vm_cy_tp = _tp("VM (Cython)")
984
+ compiled_tp = _tp("Compiled (batch)")
985
+ icarus_tp = _tp("Icarus Verilog")
986
+
987
+ print(f"\n{'─' * 60}")
988
+ print("Key ratios:")
989
+ if ref_tp > 0 and vm_py_tp > 0:
990
+ print(f" VM (Python) vs Reference: {vm_py_tp / ref_tp:.1f}x")
991
+ if ref_tp > 0 and vm_cy_tp > 0:
992
+ print(f" VM (Cython) vs Reference: {vm_cy_tp / ref_tp:.1f}x")
993
+ if vm_py_tp > 0 and vm_cy_tp > 0:
994
+ print(f" VM (Cython) vs VM (Python): {vm_cy_tp / vm_py_tp:.1f}x")
995
+ if ref_tp > 0 and compiled_tp > 0:
996
+ print(f" Compiled (batch) vs Reference: {compiled_tp / ref_tp:.0f}x")
997
+ if icarus_tp > 0 and vm_cy_tp > 0:
998
+ ratio = vm_cy_tp / icarus_tp
999
+ label = f"{ratio:.1f}x faster" if ratio > 1 else f"{1 / ratio:.1f}x slower"
1000
+ print(f" VM (Cython) vs Icarus: {label}")
1001
+ if icarus_tp > 0 and compiled_tp > 0:
1002
+ print(f" Compiled (batch) vs Icarus: {compiled_tp / icarus_tp:.1f}x faster")
1003
+ print(f"{'─' * 60}")
1004
+
1005
+ # ── Markdown output ───────────────────────────────────────────────────
1006
+ md = generate_markdown(results, cycles, machine_info)
1007
+ print(f"\n{'=' * 70}")
1008
+ print("MARKDOWN (notes/benchmarks.md)")
1009
+ print("=" * 70)
1010
+ print(md)
1011
+
1012
+ if args.update:
1013
+ dest = os.path.join(ROOT, "notes", "benchmarks.md")
1014
+ with open(dest, "w", encoding="utf-8") as f:
1015
+ f.write(md)
1016
+ print(f"Written to {dest}")
1017
+
1018
+
1019
+ if __name__ == "__main__":
1020
+ main()