open-standards/wai/wai-quantum-landscape-fpga/landscape.tcl
Transaction Science d4867b1b83 wai-quantum-landscape-fpga: the review of #677 answered
The lock and reloads. load takes the board lock before it reconfigures, and the session check sees
other runs of this tool and its scripts (their own parents excepted), so a reload waits for the job
that holds the board. The scripts do not hold the lock across the steps they run. A reload of the
same design, which passes the signature and the ID, is seen by the engine's uptime counter and its
reset BOUND register: the engine checks both before it takes a workload or a table as loaded and
after every job, and refuses a result read across a reload. A batch that comes back short, and a
sampled chain whose sweep count is wrong, are refused.

The AXI port. It checks the port's write width as well as its read width; it fills a result page
with the illegal cell pair before a write-back and refuses a result still holding it; it refuses a
transfer whose page moved. It is written against two traits, so a software link tests it off the
board: the guard, the DMA, the sequencer's count, the write-back's layout, dropped accesses.

Tests. lowered_settle's five cases and 300 random workloads with fields to 2^40 against the
reference; the joint test counts against the chains requested and checks the control's cell family.
CI gates on the change, requires tests to run, denies clippy warnings, certifies the model member,
runs a job on it, builds on linux-arm64, and replays both testbenches in Icarus Verilog
(scripts/run_tb.sh). The names check reads .v, .tcl, .xdc and .dat files.

The build. The tool version and each block's version are enforced; the multi-core build refuses the
AXI link. The documents. The latency table says how each row was timed; the engine's share of a
chain is its own clock count; the sampling energy carries its error and its floor; the known answer
is placed in time; the step response is reported; derived figures are marked; the scope of the
meter, the guard's window and the DMA's locked pages are stated; provenance says what the private
code declared; old names are gone; the build and simulator results have a raw record.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-07 03:22:22 -05:00

121 lines
7 KiB
Tcl

# SPDX-License-Identifier: Apache-2.0
# Bitstream build for the energy-landscape engine. Vivado batch mode:
# vivado -mode batch -source landscape.tcl -tclargs [FREQ <MHz>] [TAG <name>] [JOBS <n>] [CORES <k>] [LINK emio|axi]
# The part and board are data: PART and BOARD below. There is no programmable-logic I/O: the
# processing system provides the clock and the link. LINK axi (the default on one engine) gives an
# AXI-Lite register port, a write-list DMA from DDR and an in-fabric chain sequencer
# (rtl/landscape_axi.v); LINK emio carries register accesses over the EMIO GPIO lines, and is the
# only link of the multi-core build. Every block below is the vendor's no-charge processing-system
# configuration or AXI infrastructure; no licensed IP. The tool and block versions are the ones this
# design was built and measured with, and a different tool stops the build: set ANY_TOOL to go on,
# and expect a different bitstream.
set part xck26-sfvc784-2LV-c
set board xilinx.com:kv260_som:part0:2.0
proc arg {name dflt} {
global argv
set i [lsearch $argv $name]
if {$i >= 0 && [llength $argv] > $i + 1} { return [lindex $argv [expr {$i + 1}]] }
return $dflt
}
set freq [arg FREQ 100]
set tag [arg TAG landscape]
set jobs [arg JOBS 4]
set cores [arg CORES 1]
set link [arg LINK [expr {$cores > 1 ? "emio" : "axi"}]]
if {$cores > 1 && $link ne "emio"} { error "the multi-core build has the EMIO link only" }
set tool 2026.1
if {[version -short] ne $tool && ![info exists ::env(ANY_TOOL)]} {
error "this design was built and measured with Vivado $tool, not [version -short]: set ANY_TOOL to build anyway"
}
# Each block by its exact version.
proc block {name version} {
set d [get_ipdefs -all xilinx.com:ip:$name:$version]
if {$d eq ""} { error "no $name $version in this tool" }
return [lindex $d 0]
}
set here [file dirname [file normalize [info script]]]
puts "BUILD: tag=$tag freq=$freq jobs=$jobs cores=$cores link=$link here=$here"
create_project ev_$tag ./ev_$tag -part $part -force
set_property board_part $board [current_project]
create_bd_design sys
create_bd_cell -type ip -vlnv [block zynq_ultra_ps_e 3.5] ps
apply_bd_automation -rule xilinx.com:bd_rule:zynq_ultra_ps_e -config {apply_board_preset 1} [get_bd_cells ps]
set_property -dict [list \
CONFIG.PSU__FPGA_PL0_ENABLE {1} \
CONFIG.PSU__CRL_APB__PL0_REF_CTRL__FREQMHZ $freq \
CONFIG.PSU__USE__M_AXI_GP0 {0} CONFIG.PSU__USE__M_AXI_GP1 {0} CONFIG.PSU__USE__M_AXI_GP2 {0} \
CONFIG.PSU__GPIO_EMIO__PERIPHERAL__ENABLE {1} \
CONFIG.PSU__GPIO_EMIO__PERIPHERAL__IO {95} \
] [get_bd_cells ps]
create_bd_port -dir O -from 94 -to 0 emio_o
create_bd_port -dir I -from 94 -to 0 emio_i
connect_bd_net [get_bd_pins ps/emio_gpio_o] [get_bd_ports emio_o]
connect_bd_net [get_bd_pins ps/emio_gpio_i] [get_bd_ports emio_i]
create_bd_port -dir O -type clk pl_clk0
connect_bd_net [get_bd_pins ps/pl_clk0] [get_bd_ports pl_clk0]
if {$link eq "axi"} {
# The two port widths are the ones this board's boot leaves set (src/axi.rs, ps_ports): loading
# a bitstream through the FPGA manager does not change them, so the design meets them as they
# are and writes no processor register. M_AXI_HPM0_FPD (128-bit) -> width converter -> protocol
# converter -> LITE (32-bit) at 0xA000_0000, 256 KiB. DMA (AXI4, 64-bit) -> width converter ->
# S_AXI_HP0_FPD (128-bit) -> DDR. The reset is the fabric's own (aresetn).
set_property -dict [list CONFIG.PSU__USE__M_AXI_GP0 {1} CONFIG.PSU__MAXIGP0__DATA_WIDTH {128} \
CONFIG.PSU__USE__S_AXI_GP2 {1} CONFIG.PSU__SAXIGP2__DATA_WIDTH {128}] [get_bd_cells ps]
connect_bd_net [get_bd_pins ps/pl_clk0] [get_bd_pins ps/maxihpm0_fpd_aclk]
connect_bd_net [get_bd_pins ps/pl_clk0] [get_bd_pins ps/saxihp0_fpd_aclk]
create_bd_port -dir I -type rst aresetn
set dw [block axi_dwidth_converter 2.1]
create_bd_cell -type ip -vlnv $dw down
create_bd_cell -type ip -vlnv $dw up
set_property -dict [list CONFIG.SI_DATA_WIDTH {128} CONFIG.MI_DATA_WIDTH {32}] [get_bd_cells down]
set_property -dict [list CONFIG.SI_DATA_WIDTH {64} CONFIG.MI_DATA_WIDTH {128}] [get_bd_cells up]
create_bd_cell -type ip -vlnv [block axi_protocol_converter 2.1] conv
set_property -dict [list CONFIG.SI_PROTOCOL {AXI4} CONFIG.MI_PROTOCOL {AXI4LITE} CONFIG.DATA_WIDTH {32}] [get_bd_cells conv]
foreach c {down up} {
connect_bd_net [get_bd_pins ps/pl_clk0] [get_bd_pins $c/s_axi_aclk]
connect_bd_net [get_bd_ports aresetn] [get_bd_pins $c/s_axi_aresetn]
}
connect_bd_net [get_bd_pins ps/pl_clk0] [get_bd_pins conv/aclk]
connect_bd_net [get_bd_ports aresetn] [get_bd_pins conv/aresetn]
connect_bd_intf_net [get_bd_intf_pins ps/M_AXI_HPM0_FPD] [get_bd_intf_pins down/S_AXI]
connect_bd_intf_net [get_bd_intf_pins down/M_AXI] [get_bd_intf_pins conv/S_AXI]
create_bd_intf_port -mode Master -vlnv xilinx.com:interface:aximm_rtl:1.0 LITE
set_property -dict [list CONFIG.PROTOCOL {AXI4LITE} CONFIG.ADDR_WIDTH {32} CONFIG.DATA_WIDTH {32}] [get_bd_intf_ports LITE]
connect_bd_intf_net [get_bd_intf_pins conv/M_AXI] [get_bd_intf_ports LITE]
create_bd_intf_port -mode Slave -vlnv xilinx.com:interface:aximm_rtl:1.0 DMA
set_property -dict [list CONFIG.PROTOCOL {AXI4} CONFIG.ADDR_WIDTH {49} CONFIG.DATA_WIDTH {64} CONFIG.ID_WIDTH {0} \
CONFIG.HAS_REGION {0} CONFIG.HAS_QOS {0} CONFIG.HAS_LOCK {0}] [get_bd_intf_ports DMA]
connect_bd_intf_net [get_bd_intf_ports DMA] [get_bd_intf_pins up/S_AXI]
connect_bd_intf_net [get_bd_intf_pins up/M_AXI] [get_bd_intf_pins ps/S_AXI_HP0_FPD]
set_property CONFIG.ASSOCIATED_BUSIF {LITE:DMA} [get_bd_ports pl_clk0]
set_property CONFIG.ASSOCIATED_RESET {aresetn} [get_bd_ports pl_clk0]
assign_bd_address
set lite [get_bd_addr_segs -of_objects [get_bd_addr_spaces ps/Data] -filter {NAME =~ *LITE*}]
set_property offset 0xA0000000 $lite
set_property range 256K $lite
foreach seg [get_bd_addr_segs -of_objects [get_bd_addr_spaces ps/Data]] { puts "ADDR $seg [get_property offset $seg] [get_property range $seg]" }
}
validate_bd_design
make_wrapper -files [get_files sys.bd] -top -import
generate_target all [get_files sys.bd]
if {$cores > 1} {
add_files [list $here/rtl/landscape_top_multi.v $here/rtl/landscape_multi.v $here/rtl/landscape_core.v]
set_property verilog_define [list LANDSCAPE_K=$cores] [current_fileset]
} elseif {$link eq "axi"} {
add_files [list $here/rtl/landscape_top_axi.v $here/rtl/landscape_axi.v $here/rtl/landscape_core.v]
} else {
add_files [list $here/rtl/landscape_top.v $here/rtl/landscape_port.v $here/rtl/landscape_core.v]
}
add_files -fileset constrs_1 $here/rtl/landscape.xdc
set_property top design_top [current_fileset]
update_compile_order -fileset sources_1
launch_runs impl_1 -to_step write_bitstream -jobs $jobs
wait_on_run impl_1
puts "IMPL_STATUS: [get_property STATUS [get_runs impl_1]] PROGRESS: [get_property PROGRESS [get_runs impl_1]]"
open_run impl_1
report_utilization -file ev_$tag.util.rpt
report_timing_summary -max_paths 5 -file ev_$tag.timing.rpt
puts "BITSTREAM: [glob -nocomplain ./ev_$tag/ev_$tag.runs/impl_1/*.bit]"