From da1f80cb2fbb9d09bdc7fc27623830c7833df3a0 Mon Sep 17 00:00:00 2001 From: Jaehyun Kim Date: Tue, 10 Nov 2026 11:57:44 -0800 Subject: [PATCH] flow: Add the lowest-VT-only flow and repair_power knobs What: - LOWEST_VT_ONLY=1 synthesizes, places and repairs timing with the lowest VT only: the cells in NON_LOWEST_VT_CELLS join the dont use cells from synthesis to the global route timing repair. - REPAIR_POWER=1 runs repair_power after the global route timing repair, and REPAIR_POWER_VT_BUDGET is passed as its -vt_budget. With LOWEST_VT_ONLY the power recovery may use every VT. - NON_LOWEST_VT_CELLS is a platform default: asap7 and gt2n derive it from the loaded VTs; a platform with a single VT leaves it empty and LOWEST_VT_ONLY changes nothing there. Why: a lowest-VT-only flow closes timing with the fastest cells and then recovers power with the higher VTs. It took a hand-written dont use list and a Tcl hook that lifts and restores it around the power recovery. How: - util.tcl: dont_use_cells returns DONT_USE_CELLS plus NON_LOWEST_VT_CELLS when LOWEST_VT_ONLY is set; the seven set_dont_use and ABC -dont_use sites use it. repair_power_helper lifts the higher VTs, runs repair_power and restores the list. - global_route.tcl calls repair_power_helper inside an incremental global route when REPAIR_POWER is set. - variables.yaml and FlowVariables.md list the four variables. - repair_power and the VT swap fixes it relies on are OpenROAD changes that are not in tools/OpenROAD yet; the knobs are off by default. Signed-off-by: Jaehyun Kim --- docs/user/FlowVariables.md | 16 ++++++++++++++ flow/platforms/asap7/config.mk | 4 ++++ flow/platforms/gt2n/config.mk | 5 +++++ flow/scripts/cts.tcl | 2 +- flow/scripts/floorplan.tcl | 2 +- flow/scripts/global_place.tcl | 2 +- flow/scripts/global_route.tcl | 13 ++++++++--- flow/scripts/resize.tcl | 2 +- flow/scripts/synth_preamble.tcl | 6 ++--- flow/scripts/synth_syn.tcl | 4 +--- flow/scripts/util.tcl | 35 +++++++++++++++++++++++++++++ flow/scripts/variables.yaml | 39 +++++++++++++++++++++++++++++++++ 12 files changed, 116 insertions(+), 14 deletions(-) diff --git a/docs/user/FlowVariables.md b/docs/user/FlowVariables.md index 0df148d71d..0f5899b40d 100644 --- a/docs/user/FlowVariables.md +++ b/docs/user/FlowVariables.md @@ -183,6 +183,7 @@ configuration file. | LEC_CHECK| Perform formal equivalence checks between before and after netlists. This checks CTS repair timing and the initial synthesis netlist against the final netlist. If this fails, report an issue to OpenROAD.| $(if $(wildcard $(KEPLER_FORMAL_EXE)),1,0)| | LIB_FILES| A Liberty file of the standard cell library with PVT characterization, input and output characteristics, timing and power definitions for each cell.| | | LIB_MODEL| Selects between NLDM and CCS timing models for the ASAP7 platform. Valid values: NLDM (default), CCS. Used in flow/platforms/asap7/config.mk to pick the LIB_DIR subdirectory and accumulate the corresponding $(CORNER)_$(LIB_MODEL)_LIB_FILES list, and in flow/scripts/load.tcl to gate CCS-specific Tcl branches.| NLDM| +| LOWEST_VT_ONLY| Synthesize, place and repair timing with the lowest (fastest) VT only: the cells in NON_LOWEST_VT_CELLS are added to the dont use cells. Use REPAIR_POWER to move cells with slack back to the higher VTs after the global route timing repair. No effect on a platform with a single VT.| 0| | MACRO_PLACEMENT_TCL| Specifies the path of a TCL file on how to place macros manually. The user may choose to place just some of the macros in the design. The macro placer will handle the remaining unplaced macros. Note that using the macro placement file generated by the macro placer will not result in the same global placement as actually running the macro placer: the macro placement file does not contain the locations that the macro placer generates for the standard cells, and those locations impact global placement.| | | MACRO_ROWS_HALO_X| Horizontal distance between the edge of the macro and the beginning of the rows created by tapcell. Only available for ASAP7 PDK and GF180/uart-blocks design.| | | MACRO_ROWS_HALO_Y| Vertical distance between the edge of the macro and the beginning of the rows created by tapcell. Only available for ASAP7 PDK and GF180/uart-blocks design.| | @@ -198,6 +199,7 @@ configuration file. | MIN_PLACE_STEP_COEF| Sets the minimum phi coefficient (pcof_min / ยต_k Lower Bound) for global placement optimization. This parameter controls the step size lower bound in the RePlAce Nesterov optimization algorithm. Lower values may improve convergence but can increase runtime. Valid range: 0.95-1.05| 0.95| | MIN_ROUTING_LAYER| The lowest metal layer name to be used in routing.| | | NEG_CLKGATE_AND_PORTS| Specifies the active low enable clock gating cell and port names when used in conjunction with the INFER_CLKGATES variable. Format is "cell_name enable_port:master_clock:gated_clock"| | +| NON_LOWEST_VT_CELLS| Library cell patterns of every VT but the lowest, set by the platform. Used by LOWEST_VT_ONLY. Empty on a platform with a single VT.| | | NUM_CORES| Passed to `openroad -threads $(NUM_CORES)`, defaults to numbers of cores in system as determined by system specific code in Makefile, `nproc` is tried first. OpenROAD does not limit itself to this number of cores across OpenROAD running instances, which can lead to overprovisioning in contexts such as bazel-orfs where there could be many routing, or place jobs running at the same time.| | | OPENROAD_HIERARCHICAL| Feature toggle to enable to run OpenROAD in hierarchical mode, otherwise considered flat. Will eventually be the default and this option will be retired.| 0| | OPT_POST_GRT_WNS| Optimize WNS after global routing by additional repair_timing that uses VT swap and wire rerouting only to minimize placement and routing disturbance| 1| @@ -250,6 +252,8 @@ configuration file. | RECOVER_POWER| Specifies how many percent of paths with positive slacks can be slowed for power savings [0-100].| 0| | REMOVE_CELLS_FOR_LEC| String patterns directly passed to write_verilog -remove_cells <> for lec checks.| | | REPAIR_PDN_VIA_LAYER| Remove power grid vias which generate DRC violations after detailed routing.| | +| REPAIR_POWER| Run repair_power after the global route timing repair: cells with setup slack to spare are swapped to a higher VT of the same footprint to cut power. With LOWEST_VT_ONLY, the power recovery may use every VT.| 0| +| REPAIR_POWER_VT_BUDGET| Passed as -vt_budget to repair_power: pairs of library cell patterns and the largest percent of the logic cells their instances may be, e.g. `*_SL 5 {*_SL *_L} 20`.| | | REPORT_CLOCK_SKEW| Report clock skew as part of reporting metrics, starting at CTS, before which there is no clock skew. This metric can be quite time-consuming, so it can be useful to disable.| 1| | REPORT_ENDPOINT_SLACK| If set to 1, write the setup and hold slack of every timing endpoint at the end of every flow step (e.g. 3_3_place_gp) to $REPORTS_DIR/_endpoint_slack.json. These snapshots can be compared across steps with flow/util/endpointSlack.py. See docs/contrib/EndpointSlackTracking.md.| 0| | ROUTING_LAYER_ADJUSTMENT| Adjusts routing layer capacities to manage congestion and improve detailed routing. High values ease detailed routing but risk excessive detours and long global routing times, while low values reduce global routing failure but can complicate detailed routing. The global routing running time normally reduces dramatically (entirely design specific, but going from hours to minutes has been observed) when the value is low (such as 0.10). Sometimes, global routing will succeed with lower values and fail with higher values. Exploring results with different values can help shed light on the problem. Start with a too low value, such as 0.10, and bisect to value that works by doing multiple global routing runs. As a last resort, `make global_route_issue` and using the tools/OpenROAD/etc/whittle.py can be useful to debug global routing errors. If there is something specific that is impossible to route, such as a clock line over a macro, global routing will terminate with DRC errors routes that could have been routed were it not for the specific impossible routes. whittle.py should weed out the possible routes and leave a minimal failing case that pinpoints the problem.| 0.5| @@ -365,8 +369,10 @@ configuration file. - [LATCH_MAP_FILE](#LATCH_MAP_FILE) - [LEC_AUX_VERILOG_FILES](#LEC_AUX_VERILOG_FILES) - [LEC_CHECK](#LEC_CHECK) +- [LOWEST_VT_ONLY](#LOWEST_VT_ONLY) - [MIN_BUF_CELL_AND_PORTS](#MIN_BUF_CELL_AND_PORTS) - [NEG_CLKGATE_AND_PORTS](#NEG_CLKGATE_AND_PORTS) +- [NON_LOWEST_VT_CELLS](#NON_LOWEST_VT_CELLS) - [POST_SYNTH_TCL](#POST_SYNTH_TCL) - [POS_CLKGATE_AND_PORTS](#POS_CLKGATE_AND_PORTS) - [PRE_SYNTH_TCL](#PRE_SYNTH_TCL) @@ -426,6 +432,7 @@ configuration file. - [GLOBAL_CONNECT_TCL](#GLOBAL_CONNECT_TCL) - [HOLD_SLACK_MARGIN](#HOLD_SLACK_MARGIN) - [IO_CONSTRAINTS](#IO_CONSTRAINTS) +- [LOWEST_VT_ONLY](#LOWEST_VT_ONLY) - [MACRO_PLACEMENT_TCL](#MACRO_PLACEMENT_TCL) - [MACRO_ROWS_HALO_X](#MACRO_ROWS_HALO_X) - [MACRO_ROWS_HALO_Y](#MACRO_ROWS_HALO_Y) @@ -434,6 +441,7 @@ configuration file. - [MATCH_CELL_FOOTPRINT](#MATCH_CELL_FOOTPRINT) - [MAX_ROUTING_LAYER](#MAX_ROUTING_LAYER) - [MIN_ROUTING_LAYER](#MIN_ROUTING_LAYER) +- [NON_LOWEST_VT_CELLS](#NON_LOWEST_VT_CELLS) - [PDN_TCL](#PDN_TCL) - [PLACE_DENSITY](#PLACE_DENSITY) - [PLACE_DENSITY_LB_ADDON](#PLACE_DENSITY_LB_ADDON) @@ -509,11 +517,13 @@ configuration file. - [GPL_TIMING_DRIVEN](#GPL_TIMING_DRIVEN) - [IO_PLACER_H](#IO_PLACER_H) - [IO_PLACER_V](#IO_PLACER_V) +- [LOWEST_VT_ONLY](#LOWEST_VT_ONLY) - [MATCH_CELL_FOOTPRINT](#MATCH_CELL_FOOTPRINT) - [MAX_PLACE_STEP_COEF](#MAX_PLACE_STEP_COEF) - [MAX_ROUTING_LAYER](#MAX_ROUTING_LAYER) - [MIN_PLACE_STEP_COEF](#MIN_PLACE_STEP_COEF) - [MIN_ROUTING_LAYER](#MIN_ROUTING_LAYER) +- [NON_LOWEST_VT_CELLS](#NON_LOWEST_VT_CELLS) - [PLACE_DENSITY](#PLACE_DENSITY) - [PLACE_DENSITY_LB_ADDON](#PLACE_DENSITY_LB_ADDON) - [PLACE_PINS_ARGS](#PLACE_PINS_ARGS) @@ -548,7 +558,9 @@ configuration file. - [HOLD_SLACK_MARGIN](#HOLD_SLACK_MARGIN) - [LEC_AUX_VERILOG_FILES](#LEC_AUX_VERILOG_FILES) - [LEC_CHECK](#LEC_CHECK) +- [LOWEST_VT_ONLY](#LOWEST_VT_ONLY) - [MATCH_CELL_FOOTPRINT](#MATCH_CELL_FOOTPRINT) +- [NON_LOWEST_VT_CELLS](#NON_LOWEST_VT_CELLS) - [POST_CTS_TCL](#POST_CTS_TCL) - [PRE_CTS_TCL](#PRE_CTS_TCL) - [REPORT_CLOCK_SKEW](#REPORT_CLOCK_SKEW) @@ -576,12 +588,16 @@ configuration file. - [GLOBAL_ROUTE_USE_CUGR](#GLOBAL_ROUTE_USE_CUGR) - [GRT_SEED](#GRT_SEED) - [HOLD_SLACK_MARGIN](#HOLD_SLACK_MARGIN) +- [LOWEST_VT_ONLY](#LOWEST_VT_ONLY) - [MAX_REPAIR_ANTENNAS_ITER_GRT](#MAX_REPAIR_ANTENNAS_ITER_GRT) - [MAX_ROUTING_LAYER](#MAX_ROUTING_LAYER) - [MIN_ROUTING_LAYER](#MIN_ROUTING_LAYER) +- [NON_LOWEST_VT_CELLS](#NON_LOWEST_VT_CELLS) - [OPT_POST_GRT_WNS](#OPT_POST_GRT_WNS) - [POST_GLOBAL_ROUTE_TCL](#POST_GLOBAL_ROUTE_TCL) - [PRE_GLOBAL_ROUTE_TCL](#PRE_GLOBAL_ROUTE_TCL) +- [REPAIR_POWER](#REPAIR_POWER) +- [REPAIR_POWER_VT_BUDGET](#REPAIR_POWER_VT_BUDGET) - [REPORT_CLOCK_SKEW](#REPORT_CLOCK_SKEW) - [REPORT_ENDPOINT_SLACK](#REPORT_ENDPOINT_SLACK) - [ROUTING_LAYER_ADJUSTMENT](#ROUTING_LAYER_ADJUSTMENT) diff --git a/flow/platforms/asap7/config.mk b/flow/platforms/asap7/config.mk index 286310146f..301c06faff 100644 --- a/flow/platforms/asap7/config.mk +++ b/flow/platforms/asap7/config.mk @@ -119,6 +119,10 @@ export VT_LIST = $(if $(strip $(ASAP7_USE_VT)), $(ASAP7_USE_VT), RVT) # # The first VT in the ASAP7_USE_VT list is the primary VT. The others get added to OTHER_VT export PRIMARY_VT = $(word 1, $(VT_LIST)) +# Cells of every loaded VT but the lowest one (SLVT < LVT < RVT), for +# LOWEST_VT_ONLY +.LOWEST_VT = $(firstword $(foreach vt,SLVT LVT RVT,$(filter $(vt),$(VT_LIST)))) +export NON_LOWEST_VT_CELLS ?= $(patsubst %VT,*_ASAP7_75t_%,$(filter-out $(.LOWEST_VT),$(VT_LIST))) export PRIMARY_VT_TAG = $(strip $(patsubst %VT, %, $(PRIMARY_VT))) export OTHER_VT = $(wordlist 2, $(words $(VT_LIST)), $(VT_LIST)) diff --git a/flow/platforms/gt2n/config.mk b/flow/platforms/gt2n/config.mk index 50e24068be..dfd77aa16f 100644 --- a/flow/platforms/gt2n/config.mk +++ b/flow/platforms/gt2n/config.mk @@ -31,6 +31,11 @@ export OTHER_W = $(wordlist 2, $(words $(GT2N_USE_W)), $(GT2N_USE_W)) export PRIMARY_VT = $(word 1, $(GT2N_USE_VT)) export OTHER_VT = $(wordlist 2, $(words $(GT2N_USE_VT)), $(GT2N_USE_VT)) +# Cells of every loaded Vt but the lowest one (elvt < ulvt < lvt < svt < hvt), +# for LOWEST_VT_ONLY +.LOWEST_VT = $(firstword $(foreach vt,elvt ulvt lvt svt hvt,$(filter $(vt),$(GT2N_USE_VT)))) +export NON_LOWEST_VT_CELLS ?= $(addprefix *_,$(filter-out $(.LOWEST_VT),$(GT2N_USE_VT))) + # Cartesian product of (W, Vt) used by every cell-name template below. ALL_WVT = $(foreach w,$(GT2N_USE_W), $(foreach vt,$(GT2N_USE_VT),$(w)_$(vt))) PRIMARY_WVT = $(PRIMARY_W)_$(PRIMARY_VT) diff --git a/flow/scripts/cts.tcl b/flow/scripts/cts.tcl index 7439569d12..5963bb4b40 100644 --- a/flow/scripts/cts.tcl +++ b/flow/scripts/cts.tcl @@ -31,7 +31,7 @@ if { [env_var_exists_and_non_empty CTS_ARGS] } { set cts_args $::env(CTS_ARGS) } -set_dont_use $::env(DONT_USE_CELLS) +set_dont_use [dont_use_cells] log_cmd clock_tree_synthesis {*}$cts_args diff --git a/flow/scripts/floorplan.tcl b/flow/scripts/floorplan.tcl index 3190eeb612..a8274c40e3 100644 --- a/flow/scripts/floorplan.tcl +++ b/flow/scripts/floorplan.tcl @@ -120,7 +120,7 @@ if { [env_var_exists_and_non_empty FASTROUTE_TCL] } { } source_env_var_if_exists FOOTPRINT_TCL -log_cmd set_dont_use $::env(DONT_USE_CELLS) +log_cmd set_dont_use [dont_use_cells] # The transforms below (repair_tie_fanout, replace_arith_modules, # repair_timing_helper) look like synthesis-stage diff --git a/flow/scripts/global_place.tcl b/flow/scripts/global_place.tcl index 4a125954e2..3b0e2265a4 100644 --- a/flow/scripts/global_place.tcl +++ b/flow/scripts/global_place.tcl @@ -4,7 +4,7 @@ erase_non_stage_variables place load_design 3_2_place_iop.odb 2_floorplan.sdc source_step_tcl PRE GLOBAL_PLACE -set_dont_use $::env(DONT_USE_CELLS) +set_dont_use [dont_use_cells] if { $::env(GPL_TIMING_DRIVEN) } { remove_buffers diff --git a/flow/scripts/global_route.tcl b/flow/scripts/global_route.tcl index 01ad91ba30..daaec7c1a7 100644 --- a/flow/scripts/global_route.tcl +++ b/flow/scripts/global_route.tcl @@ -63,9 +63,7 @@ set_placement_padding -global \ set_propagated_clock [all_clocks] log_cmd estimate_parasitics -if { [env_var_exists_and_non_empty DONT_USE_CELLS] } { - set_dont_use $::env(DONT_USE_CELLS) -} +set_dont_use [dont_use_cells] if { !$::env(SKIP_INCREMENTAL_REPAIR) } { if { $::env(DETAILED_METRICS) } { @@ -133,6 +131,15 @@ if { !$::env(OPT_POST_GRT_WNS) } { -congestion_report_file $::env(REPORTS_DIR)/congestion_post_recover_power.rpt } +if { $::env(REPAIR_POWER) } { + log_cmd global_route -start_incremental {*}$allow_congestion + repair_power_helper + # Route the nets of the swapped cells + run_global_route_and_catch_failures -end_incremental {*}$res_aware \ + {*}$allow_congestion \ + -congestion_report_file $::env(REPORTS_DIR)/congestion_post_repair_power.rpt +} + if { !$::env(SKIP_ANTENNA_REPAIR) && [env_var_exists_and_non_empty MAX_REPAIR_ANTENNAS_ITER_GRT] diff --git a/flow/scripts/resize.tcl b/flow/scripts/resize.tcl index fa8a26da3d..0a3e1838ee 100644 --- a/flow/scripts/resize.tcl +++ b/flow/scripts/resize.tcl @@ -9,7 +9,7 @@ log_cmd estimate_parasitics set instance_count_before [sta::network_leaf_instance_count] set pin_count_before [sta::network_leaf_pin_count] -set_dont_use $::env(DONT_USE_CELLS) +set_dont_use [dont_use_cells] if { [env_var_exists_and_non_empty EARLY_SIZING_CAP_RATIO] } { log_cmd set_opt_config -set_early_sizing_cap_ratio $env(EARLY_SIZING_CAP_RATIO) diff --git a/flow/scripts/synth_preamble.tcl b/flow/scripts/synth_preamble.tcl index f024281f80..021eb4b1c3 100644 --- a/flow/scripts/synth_preamble.tcl +++ b/flow/scripts/synth_preamble.tcl @@ -297,10 +297,8 @@ foreach lib $::env(LIB_FILES) { # Exclude dont_use cells. This includes macros that are specified via # LIB_FILES and ADDITIONAL_LIBS that are included in LIB_FILES. set lib_dont_use_args "" -if { [env_var_exists_and_non_empty DONT_USE_CELLS] } { - foreach cell $::env(DONT_USE_CELLS) { - lappend lib_dont_use_args -dont_use $cell - } +foreach cell [dont_use_cells] { + lappend lib_dont_use_args -dont_use $cell } # Never map logic onto a hardened block. A block's timing model gives a # constant output a function, and ABC would take the block for a gate. diff --git a/flow/scripts/synth_syn.tcl b/flow/scripts/synth_syn.tcl index 52617bd5ae..1912677fea 100644 --- a/flow/scripts/synth_syn.tcl +++ b/flow/scripts/synth_syn.tcl @@ -13,9 +13,7 @@ if { [env_var_exists_and_non_empty ADDITIONAL_LEFS] } { log_cmd read_lef $lef } } -if { [env_var_exists_and_non_empty DONT_USE_CELLS] } { - log_cmd set_dont_use $::env(DONT_USE_CELLS) -} +log_cmd set_dont_use [dont_use_cells] # Setup verilog include directories set vIdirsArgs "" diff --git a/flow/scripts/util.tcl b/flow/scripts/util.tcl index 09ab88033a..234ede6fed 100644 --- a/flow/scripts/util.tcl +++ b/flow/scripts/util.tcl @@ -81,6 +81,41 @@ proc recover_power_helper { } { report_power } +# Cells the flow must not use: DONT_USE_CELLS, plus the cells of every VT but +# the lowest in a lowest-VT-only flow +proc dont_use_cells { } { + set cells [env_var_or_empty DONT_USE_CELLS] + if { $::env(LOWEST_VT_ONLY) } { + lappend cells {*}$::env(NON_LOWEST_VT_CELLS) + } + return $cells +} + +proc repair_power_helper { } { + puts "Switching non critical gates to higher VT for power recovery" + report_tns + report_wns + report_power + # The power recovery of a lowest-VT-only flow may use every VT + if { $::env(LOWEST_VT_ONLY) } { + log_cmd reset_dont_use + log_cmd set_dont_use [env_var_or_empty DONT_USE_CELLS] + } + set additional_args "-verbose" + if { $::env(SETUP_SLACK_MARGIN) != 0 } { + lappend additional_args -setup_margin $::env(SETUP_SLACK_MARGIN) + } + append_env_var additional_args REPAIR_POWER_VT_BUDGET -vt_budget 1 + log_cmd repair_power {*}$additional_args + if { $::env(LOWEST_VT_ONLY) } { + log_cmd reset_dont_use + log_cmd set_dont_use [dont_use_cells] + } + report_tns + report_wns + report_power +} + proc extract_stage { input_file } { # Match the stage prefix on the basename, not the full path: an ancestor # dir like ".../4_something/3_place.odb" would otherwise match "4_" -> stage 4. diff --git a/flow/scripts/variables.yaml b/flow/scripts/variables.yaml index ac9d582494..3ab244baef 100644 --- a/flow/scripts/variables.yaml +++ b/flow/scripts/variables.yaml @@ -47,6 +47,21 @@ RECOVER_POWER: Specifies how many percent of paths with positive slacks can be slowed for power savings [0-100]. default: 0 +REPAIR_POWER: + description: > + Run repair_power after the global route timing repair: cells with setup + slack to spare are swapped to a higher VT of the same footprint to cut + power. With LOWEST_VT_ONLY, the power recovery may use every VT. + default: 0 + stages: + - grt +REPAIR_POWER_VT_BUDGET: + description: > + Passed as -vt_budget to repair_power: pairs of library cell patterns and + the largest percent of the logic cells their instances may be, e.g. + `*_SL 5 {*_SL *_L} 20`. + stages: + - grt SKIP_INCREMENTAL_REPAIR: default: 0 description: > @@ -159,6 +174,30 @@ PLATFORM_TCL: DONT_USE_CELLS: description: | Dont use cells eases pin access in detailed routing. +LOWEST_VT_ONLY: + description: > + Synthesize, place and repair timing with the lowest (fastest) VT only: + the cells in NON_LOWEST_VT_CELLS are added to the dont use cells. Use + REPAIR_POWER to move cells with slack back to the higher VTs after the + global route timing repair. No effect on a platform with a single VT. + default: 0 + stages: + - synth + - floorplan + - place + - cts + - grt +NON_LOWEST_VT_CELLS: + description: > + Library cell patterns of every VT but the lowest, set by the platform. + Used by LOWEST_VT_ONLY. Empty on a platform with a single VT. + default: "" + stages: + - synth + - floorplan + - place + - cts + - grt SYNTH_GUT: description: > Load design and remove all internal logic before doing synthesis. This is