-
Kareem Farid authoredKareem Farid authored
synthesis.tcl 14.11 KiB
# Copyright 2020-2023 Efabless Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
proc convert_pg_pins {lib_in lib_out} {
try_exec sed -E {s/^([[:space:]]+)pg_pin(.*)/\1pin\2\n\1 direction : "inout";/g} $lib_in > $lib_out
}
proc run_yosys {args} {
set options {
{-output optional}
{-log optional}
{-indexed_log optional}
}
set flags {
-no_set_netlist
}
parse_key_args "run_yosys" args arg_values $options flags_map $flags
if { [info exists arg_values(-log)] } {
puts_warn "run_yosys -log is deprecated: replace -log with -indexed_log."
set arg_values(-indexed_log) $arg_values(-log)
}
set_if_unset arg_values(-output) $::env(synthesis_results)/$::env(DESIGN_NAME).v
set_if_unset arg_values(-indexed_log) /dev/null
if { [ info exists ::env(SYNTH_ADDER_TYPE)] && ($::env(SYNTH_ADDER_TYPE) in [list "RCA" "CSA"]) } {
set ::env(SYNTH_READ_BLACKBOX_LIB) 1
}
set ::env(synth_report_prefix) [index_file $::env(synthesis_reports)/synthesis]
set ::env(LIB_SYNTH_COMPLETE_NO_PG) [list]
foreach lib $::env(LIB_SYNTH_COMPLETE) {
set fbasename [file rootname [file tail $lib]]
set lib_path [index_file $::env(synthesis_tmpfiles)/$fbasename.no_pg.lib]
convert_pg_pins $lib $lib_path
lappend ::env(LIB_SYNTH_COMPLETE_NO_PG) $lib_path
}
set ::env(LIB_SYNTH_NO_PG) [list]
foreach lib $::env(LIB_SYNTH) {
set fbasename [file rootname [file tail $lib]]
set lib_path [index_file $::env(synthesis_tmpfiles)/$fbasename.no_pg.lib]
convert_pg_pins $lib $lib_path
lappend ::env(LIB_SYNTH_NO_PG) $lib_path
}
set ::env(SAVE_NETLIST) $arg_values(-output)
run_yosys_script $::env(SYNTH_SCRIPT) -indexed_log $arg_values(-indexed_log)
if { ! [info exists flags_map(-no_set_netlist)] } {
set_netlist -lec $::env(SAVE_NETLIST)
}
# The following is a naive workaround to OpenROAD not accepting defparams.
# It *should* be handled with a fix to the OpenROAD Verilog parser.
if { [info exists ::env(SYNTH_EXPLORE)] && $::env(SYNTH_EXPLORE) } {
puts_info "This is a Synthesis Exploration and so no need to remove the defparam lines."
} else {
try_exec sed -i.bak {/defparam/d} $arg_values(-output)
exec rm -f $arg_values(-output).bak
}
unset ::env(SAVE_NETLIST)
}
proc run_synth_exploration {args} {
if { $::env(SYNTH_NO_FLAT) } {
puts_err "Cannot run synthesis exploration with SYNTH_NO_FLAT."
throw_error
}
puts_info "Running Synthesis Exploration..."
set ::env(SYNTH_EXPLORE) 1
set log [index_file $::env(synthesis_logs)/synthesis.log]
run_yosys -indexed_log $log
set exploration_report [index_file $::env(synthesis_reports)/exploration_analysis.html]
puts_info "Generating exploration report..."
try_exec python3 $::env(SCRIPTS_DIR)/synth_exp/analyze.py\
--output $exploration_report\
[index_file $::env(synthesis_logs)/synthesis.log]
set exploration_report_relative [relpath . $exploration_report]
puts_success "Done with synthesis exploration: See report at '$exploration_report_relative'."
# Following two cannot be indexed- referenced by path in the HTML file.
file copy $::env(SCRIPTS_DIR)/synth_exp/table.css $::env(synthesis_reports)
file copy $::env(SCRIPTS_DIR)/synth_exp/utils.js $::env(synthesis_reports)
}
proc run_synthesis {args} {
increment_index
TIMER::timer_start
set log [index_file $::env(synthesis_logs)/synthesis.log]
puts_info "Running Synthesis (log: [relpath . $log])..."
set ::env(CURRENT_SDC) $::env(BASE_SDC_FILE)
# in-place insertion
if { [file exists $::env(synthesis_results)/$::env(DESIGN_NAME).v] } {
puts_warn "A netlist at $::env(synthesis_results)/$::env(DESIGN_NAME).v already exists. Synthesis will be skipped."
set_netlist $::env(synthesis_results)/$::env(DESIGN_NAME).v
} else {
run_yosys -indexed_log $log
if { $::env(QUIT_ON_SYNTH_CHECKS) } {
set pre_synth_report $::env(synth_report_prefix)_pre_synth.chk.rpt
if { [info exists ::env(SYNTH_ELABORATE_ONLY)] \
&& $::env(SYNTH_ELABORATE_ONLY) == 1 } {
set pre_synth_report $::env(synth_report_prefix).chk.rpt
}
run_synthesis_checkers $log $pre_synth_report
}
}
TIMER::timer_stop
exec echo "[TIMER::get_runtime]" | python3 $::env(SCRIPTS_DIR)/write_runtime.py "synthesis - yosys"
if { [info exists ::env(CLOCK_PORT)] && $::env(CLOCK_PORT) != "" } {
set missing_clock_ports [exec\
python3 $::env(SCRIPTS_DIR)/check_clock_ports.py\
--top $::env(DESIGN_NAME)\
--netlist-in $::env(synthesis_tmpfiles)/$::env(DESIGN_NAME).json\
{*}$::env(CLOCK_PORT)]
set ports_not_found 0
foreach {clock_port} $missing_clock_ports {
puts_err "The specified clock port '$clock_port' does not exist in the top-level module."
set ports_not_found 1
}
if { $ports_not_found } {
throw_error
}
}
if { $::env(QUIT_ON_ASSIGN_STATEMENTS) == 1 } {
check_assign_statements $::env(CURRENT_NETLIST)
}
if { $::env(QUIT_ON_UNMAPPED_CELLS) == 1 } {
set strategy_escaped [string map {" " _} $::env(SYNTH_STRATEGY)]
set final_stat_file $::env(synth_report_prefix).$strategy_escaped.stat.rpt
if { [info exists ::env(SYNTH_ELABORATE_ONLY)] \
&& $::env(SYNTH_ELABORATE_ONLY) == 1 } {
set final_stat_file $::env(synth_report_prefix).stat
}
check_unmapped_cells $final_stat_file
}
run_sta\
-log $::env(synthesis_logs)/sta.log \
-netlist_in \
-pre_cts \
-save_to $::env(synthesis_results) \
-tool sta
set ::env(LAST_TIMING_REPORT_TAG) [index_file $::env(synthesis_reports)/syn_sta]
if { [info exists ::env(SYNTH_USE_PG_PINS_DEFINES)] } {
puts_info "Creating a netlist with power/ground pins."
if { ! [info exists ::env(SYNTH_DEFINES)] } {
set ::env(SYNTH_DEFINES) [list]
}
lappend ::env(SYNTH_DEFINES) {*}$::env(SYNTH_USE_PG_PINS_DEFINES)
run_yosys -output $::env(synthesis_tmpfiles)/pg_define.v -no_set_netlist
}
}
proc verilog_elaborate {args} {
# usually run on structural verilog (top-level netlists)
set synth_script_old $::env(SYNTH_SCRIPT)
set ::env(SYNTH_SCRIPT) $::env(SCRIPTS_DIR)/yosys/elaborate.tcl
run_yosys {*}$args
set ::env(SYNTH_SCRIPT) $synth_script_old
}
proc yosys_rewrite_verilog {filename} {
if { !$::env(LEC_ENABLE) } {
puts_verbose "Skipping Verilog rewrite (logic equivalency checks are disabled)..."
return
}
if { !$::env(YOSYS_REWRITE_VERILOG) } {
puts_verbose "Skipping Verilog rewrite..."
return
}
increment_index
TIMER::timer_start
set log [index_file $::env(synthesis_logs)/rewrite_verilog.log]
puts_info "Rewriting $filename to $::env(SAVE_NETLIST) using Yosys (log: [relpath . $log])..."
assert_files_exist $filename
set ::env(SAVE_NETLIST) $filename
run_yosys_script $::env(SCRIPTS_DIR)/yosys/rewrite_verilog.tcl -indexed_log $log
unset ::env(SAVE_NETLIST)
TIMER::timer_stop
exec echo "[TIMER::get_runtime]" | python3 $::env(SCRIPTS_DIR)/write_runtime.py "verilog rewrite - yosys"
}
proc logic_equiv_check {args} {
set options {
{-lhs required}
{-rhs required}
}
set flags {}
set args_copy $args
parse_key_args "logic_equiv_check" args arg_values $options flags_map $flags
if { [file exists $arg_values(-lhs).without_power_pins.v] } {
set ::env(LEC_LHS_NETLIST) $arg_values(-lhs).without_power_pins.v
} else {
set ::env(LEC_LHS_NETLIST) $arg_values(-lhs)
}
if { [file exists $arg_values(-rhs).without_power_pins.v] } {
set ::env(LEC_RHS_NETLIST) $arg_values(-rhs).without_power_pins.v
} else {
set ::env(LEC_RHS_NETLIST) $arg_values(-rhs)
}
increment_index
TIMER::timer_start
set log [index_file $::env(synthesis_logs).equiv.log]
set lhs_rel [relpath . $::env(LEC_LHS_NETLIST)]
set rhs_rel [relpath . $::env(LEC_RHS_NETLIST)]
puts_info "Running LEC: '$lhs_rel' vs. '$rhs_rel' (log: [relpath . $log])..."
if { [catch {run_yosys_script $::env(SCRIPTS_DIR)/yosys/logic_equiv_check.tcl -indexed_log $log}] } {
puts_err "$::env(LEC_LHS_NETLIST) is not logically equivalent to $::env(LEC_RHS_NETLIST)."
TIMER::timer_stop
exec echo "[TIMER::get_runtime]" | python3 $::env(SCRIPTS_DIR)/write_runtime.py "logic equivalence check - yosys"
throw_error
}
puts_info "$::env(LEC_LHS_NETLIST) and $::env(LEC_RHS_NETLIST) are proven equivalent"
TIMER::timer_stop
exec echo "[TIMER::get_runtime]" | python3 $::env(SCRIPTS_DIR)/write_runtime.py "logic equivalence check - yosys"
}
proc generate_blackbox_verilog {inputs output {defines ""}} {
set defines_flag ""
set ::env(YOSYS_IN) $inputs
set ::env(YOSYS_OUT) $output
if { $defines != "" } {
set ::env(YOSYS_DEFINES) $defines
}
try_exec yosys -c $::env(SCRIPTS_DIR)/yosys/blackbox.tcl
set out_str [cat $output]
set f [open $output w]
puts $f "/* verilator lint_off UNDRIVEN */\n/* verilator lint_off UNUSEDSIGNAL */\n$out_str\n/* verilator lint_on UNUSEDSIGNAL */\n/* verilator lint_on UNDRIVEN */\n"
close $f
set inputs_rel [list]
foreach input $inputs {
lappend inputs_rel [relpath . $input]
}
puts_verbose "Generated black-box model ([relpath . $output]) from ($inputs_rel)."
}
proc run_verilator {} {
set bb_dir $::env(synthesis_tmpfiles)/blackbox
file mkdir $bb_dir
set pdk_model_blackbox [list]
set included_blackbox_models [glob -nocomplain "$::env(PDK_ROOT)/$::env(PDK)/libs.ref/$::env(STD_CELL_LIBRARY)/verilog/*__blackbox.v"]
if { [llength $included_blackbox_models]} {
foreach model $included_blackbox_models {
set output_file "$bb_dir/[file rootname [file tail $model]].v"
generate_blackbox_verilog $model $output_file
lappend pdk_model_blackbox $output_file
}
} else {
# No black-box model in PDK: gotta try our best here
set pdk_models [glob -nocomplain "$::env(PDK_ROOT)/$::env(PDK)/libs.ref/$::env(STD_CELL_LIBRARY)/verilog/*.v"]
foreach model $pdk_models {
set output_file "$bb_dir/[file rootname [file tail $model]].v"
set patched_file "$bb_dir/[file rootname [file tail $model]].patched.v"
try_exec python3 $::env(SCRIPTS_DIR)/clean_models.py\
--output $patched_file\
$model
generate_blackbox_verilog $patched_file $output_file FUNCTIONAL
lappend pdk_model_blackbox $output_file
}
}
set log $::env(synthesis_logs)/linter.log
set arg_list [list]
if { $::env(LINTER_INCLUDE_PDK_MODELS) } {
lappend arg_list {*}$pdk_model_blackbox
}
if { [info exists ::env(VERILOG_FILES_BLACKBOX)] } {
set output_file "$bb_dir/extra.v"
if { [info exists ::env(LINTER_DEFINES)] } {
generate_blackbox_verilog $::env(VERILOG_FILES_BLACKBOX) $output_file "$::env(LINTER_DEFINES)"
} else {
generate_blackbox_verilog $::env(VERILOG_FILES_BLACKBOX) $output_file
}
lappend arg_list {*}$output_file
}
lappend arg_list {*}$::env(VERILOG_FILES)
set incdirs ""
if { [info exists ::env(VERILOG_INCLUDE_DIRS)] } {
foreach incdir $::env(VERILOG_INCLUDE_DIRS) {
set incdirs "$incdirs +incdir+$incdir"
}
}
lappend arg_list {*}$incdirs
lappend arg_list -Wno-fatal
if { $::env(LINTER_RELATIVE_INCLUDES) } {
lappend arg_list "--relative-includes"
}
set defines ""
if { [info exists ::env(LINTER_DEFINES)] } {
foreach define $::env(LINTER_DEFINES) {
set defines "$defines +define+$define"
}
} elseif { [info exists ::env(SYNTH_DEFINES)] } {
foreach define $::env(SYNTH_DEFINES) {
set defines "$defines +define+$define"
}
}
lappend arg_list {*}$defines
puts_info "Running linter (Verilator) (log: [relpath . $log])..."
catch_exec verilator \
-Wall \
--lint-only \
--Wno-DECLFILENAME \
--top-module $::env(DESIGN_NAME) \
{*}$arg_list |& tee $log $::env(TERMINAL_OUTPUT)
set timing_errors [exec bash -c "grep -i 'Error-NEEDTIMINGOPT' $log || true"]
if { $timing_errors ne "" } {
set msg "Timing constructs found in the RTL. Please remove them or add a preprocessor guard. It is heavily discouraged to rely on timing constructs in synthesis."
if { $::env(QUIT_ON_LINTER_ERRORS) } {
puts_err $msg
throw_error
} else {
puts_warn $msg
}
}
set errors_count [exec bash -c "grep -i '%Error' $log | wc -l"]
if { [expr $errors_count > 0] } {
if { $::env(QUIT_ON_LINTER_ERRORS) } {
puts_err "$errors_count errors found by linter"
throw_error
}
puts_warn "$errors_count errors found by linter"
} else {
puts_info "$errors_count errors found by linter"
}
set warnings_count [exec bash -c "grep -i '%Warning' $log | wc -l"]
if { [expr $warnings_count > 0] } {
if { $::env(QUIT_ON_LINTER_WARNINGS) } {
puts_err "$warnings_count warnings found by linter"
throw_error
}
puts_warn "$warnings_count warnings found by linter"
} else {
puts_info "$warnings_count warnings found by linter"
}
}
package provide openlane 0.9