Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions flow/scripts/synth_canonicalize.tcl
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
source $::env(SCRIPTS_DIR)/synth_preamble.tcl
read_design_sources
# Fingerprint the design state right after the HDL frontend
# (slang / builtin Verilog) returns, before any other pass runs.
# An unstable hash here means the frontend itself is non-idempotent
# -- distinct from drift introduced later by hierarchy / opt_clean
# / etc. Surfaced as a literal warning-level rule via genRuleFile.py.
write_state_hash synth__post_read_sources__hash

hierarchy -check -top $::env(DESIGN_NAME)

Expand Down
33 changes: 33 additions & 0 deletions flow/scripts/synth_preamble.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,39 @@ yosys -import
source $::env(SCRIPTS_DIR)/util.tcl
erase_non_stage_variables synth

# Fingerprint the current yosys design state to a `<metric>: <sha>`
# line in the surrounding yosys log so `genMetrics.py` can pick it up.
#
# `setattr -unset src` strips file:line attribute lines from the
# RTLIL before hashing so the hash is path-independent (bazel sandbox
# paths differ from the classic-make build's relative paths; without
# stripping, hashes always differ trivially).
#
# The strip is wrapped in `design -push` / `design -pop` so it does
# not leak into the rest of the synth run -- src attributes are
# preserved for back-annotation / debugging downstream. This matters
# specifically for builds without `SYNTH_REPEATABLE_BUILD=1`, where
# synth_canonicalize.tcl deliberately leaves src attrs alone after
# the canonical-RTLIL write.
proc write_state_hash { metric } {
design -push
# `*/*` strips src attrs across objects in *all* modules. Bare `*`
# only targets the current module's objects, which is fine after
# `hierarchy -check -top` collapses things to one design, but
# `write_state_hash` is called before `hierarchy` here, so the
# post-frontend state may have many separate modules whose src
# attrs would otherwise survive into the hashed RTLIL and break
# path-independence.
setattr -unset src */*
setattr -mod -unset src *
set tmp $::env(OBJECTS_DIR)/.${metric}.tmp.rtlil
write_rtlil $tmp
design -pop
set sha [lindex [split [exec sha1sum $tmp]] 0]
file delete $tmp
puts "${metric}: $sha"
Comment on lines +21 to +36

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The write_state_hash procedure lacks error handling, which could leave the Yosys design stack in an inconsistent state (due to a missing design -pop) if an error occurs during attribute stripping, file writing, or hashing (e.g., if sha1sum is missing or the disk is full). Wrapping the core logic in a catch block ensures that the design state is restored and the temporary file is cleaned up. Additionally, stripping the orig_src and syn_file attributes is recommended to ensure path-independence, as some frontends (like Verific) use them to store absolute paths.

  design -push
  set tmp $::env(OBJECTS_DIR)/.${metric}.tmp.rtlil
  if {[catch {
    foreach attr {src orig_src syn_file} {
      setattr -unset $attr */*
      setattr -mod -unset $attr *
    }
    write_rtlil $tmp
    set sha [lindex [exec sha1sum $tmp] 0]
    puts "${metric}: $sha"
  } err]} {
    puts "Warning: ${metric} calculation failed: $err"
  }
  design -pop
  file delete -force $tmp

}
Comment thread
oharboe marked this conversation as resolved.

# If using a cached, gate level netlist, then copy over to the results dir with
# preserve timestamps flag set. If you don't, subsequent runs will cause the
# floorplan step to be re-executed.
Expand Down
27 changes: 23 additions & 4 deletions flow/util/genMetrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,29 @@ def extract_metrics(
rptPath + "/synth_stat.txt",
)

# Netlist hashes: fingerprints of the canonical RTLIL (pre-ABC) and
# the final post-synthesis Verilog so the rules-base.json check
# (level=warning) flags when bazel-built vs make-built yosys
# disagree for the same RTL.
# Netlist hashes: fingerprints at three points in the yosys
# pipeline so the rules-base.json check (level=warning) can
# isolate frontend drift (yosys-slang isn't idempotent) from
# mid-synth drift from ABC drift.
#
# post_read_sources = state right after `read_design_sources`
# (HDL frontend output only)
# canonical_netlist = state after `opt_clean -purge`
# (= `1_1_yosys_canonicalize.rtlil`)
# netlist = `1_2_yosys.v`, post-ABC
#
# `post_read_sources` is emitted by synth_canonicalize.tcl via
# `write_state_hash` (synth_preamble.tcl) as a `<metric>: <sha>`
# line in 1_1_yosys_canonicalize.log; the other two come straight
# from `file_sha1` of the already-emitted RTLIL / Verilog.
extractTagFromFile(
"synth__post_read_sources__hash",
metrics_dict,
r"^\s*synth__post_read_sources__hash:\s+([0-9a-f]{40})\s*$",
logPath + "/1_1_yosys_canonicalize.log",
t=str,
required=False,
)
metrics_dict["synth__canonical_netlist__hash"] = file_sha1(
resultPath + "/1_1_yosys_canonicalize.rtlil"
)
Expand Down
5 changes: 5 additions & 0 deletions flow/util/genRuleFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ def gen_rule_file(
# surfaces as a [WARN] diagnostic in checkMetadata.py without
# failing the build, matching how rules-base.json already
# treats warning counts.
"synth__post_read_sources__hash": {
"mode": "literal",
"compare": "==",
"level": "warning",
},
"synth__canonical_netlist__hash": {
"mode": "literal",
"compare": "==",
Expand Down