Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul Linter Handling of Black-boxed Verilog Models #1929

Merged
merged 18 commits into from
Aug 31, 2023
Merged
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
3 changes: 3 additions & 0 deletions .github/scripts/variables_documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@
VCHECK_OUTPUT
VDD_NET
WRITE_VIEWS_NO_GLOBAL_CONNECT
YOSYS_IN
YOSYS_OUT
YOSYS_DEFINES
TECH_METAL_LAYERS
LIB_SYNTH_COMPLETE
LIB_SYNTH_COMPLETE_NO_PG
Expand Down
2 changes: 1 addition & 1 deletion configuration/checkers.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ set ::env(QUIT_ON_UNMAPPED_CELLS) 1
set ::env(QUIT_ON_SYNTH_CHECKS) 1
set ::env(SYNTH_CHECKS_ALLOW_TRISTATE) 1
set ::env(LINTER_RELATIVE_INCLUDES) 1
set ::env(LINTER_INCLUDE_PDK_MODELS) 0
set ::env(LINTER_INCLUDE_PDK_MODELS) 1
set ::env(QUIT_ON_LINTER_WARNINGS) 0
set ::env(QUIT_ON_LINTER_ERRORS) 1

Expand Down
2 changes: 1 addition & 1 deletion designs/ci
49 changes: 49 additions & 0 deletions scripts/clean_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright 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.
import re
import click
from enum import Enum


@click.command()
@click.option("-o", "--output", default=None)
@click.argument(
"file_in",
type=click.Path(exists=True, file_okay=True, dir_okay=False),
required=True,
)
def clean_models(output, file_in):
class State(Enum):
output = 0
primitive = 1

out_file = None
if output is not None:
out_file = open(output, "w", encoding="utf8")
bad_yosys_line = re.compile(r"^\s+\S+\s*\(.*\).*;")
state = State.output
for line in open(file_in, "r", encoding="utf8"):
if state == State.output:
if line.strip().startswith("primitive"):
state = State.primitive
elif bad_yosys_line.search(line) is None:
print(line.strip("\n"), file=out_file)
elif state == State.primitive:
if line.strip().startswith("endprimitive"):
print("/* removed primitive */", file=out_file)
state = State.output


if __name__ == "__main__":
clean_models()
4 changes: 2 additions & 2 deletions scripts/tcl_commands/checkers.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ proc check_out_of_bound {log} {
$log || true"]

if { $checker ne "" } {
puts_err "Synthesis failed. Range select out of bounds on some signals. Search for '$match' in $log"
puts_err "Synthesis failed. Range select out of bounds on some signals. Search for '$match' in '[relpath . $log]'"
throw_error
}
}
Expand All @@ -60,7 +60,7 @@ proc check_resizing_cell_port {log} {
$log || true"]

if { $checker ne "" } {
puts_err "Synthesis failed. Signal not matching port size. Search for '$match' in $log"
puts_err "Synthesis failed. Signal not matching port size. Search for '$match' in '[relpath . $log]'"
throw_error
}
}
Expand Down
80 changes: 60 additions & 20 deletions scripts/tcl_commands/synthesis.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -243,30 +243,71 @@ proc logic_equiv_check {args} {
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 verilator_verified_pdks "sky130A sky130B"
set verilator_verified_scl "sky130_fd_sc_hd"
set includes ""
if { [string match *$::env(PDK)* $verilator_verified_pdks] == 0 || \
[string match *$::env(STD_CELL_LIBRARY)* $verilator_verified_scl] == 0} {
puts_warn "PDK '$::env(PDK)', SCL '$::env(STD_CELL_LIBRARY)' will generate errors with instantiated stdcells in the design."
puts_warn "Either disable QUIT_ON_LINTER_ERRORS or remove the instantiated cells."
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 {
set pdk_verilog_models [glob $::env(PDK_ROOT)/$::env(PDK)/libs.ref/$::env(STD_CELL_LIBRARY_OPT)/verilog/*.v]
foreach model $pdk_verilog_models {
set includes "$includes $model"
# 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
puts_info "Running linter (Verilator) (log: [relpath . $log])..."
set arg_list [list]
if { $::env(LINTER_INCLUDE_PDK_MODELS) } {
lappend arg_list {*}$includes
lappend arg_list {*}$pdk_model_blackbox
}
lappend arg_list {*}$::env(VERILOG_FILES)
if { [info exists ::env(VERILOG_FILES_BLACKBOX)] } {
lappend arg_list {*}$::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)
lappend arg_list -Wno-fatal
if { $::env(LINTER_RELATIVE_INCLUDES) } {
lappend arg_list "--relative-includes"
Expand All @@ -284,18 +325,17 @@ proc run_verilator {} {
}
lappend arg_list {*}$defines

set arg "|& tee $log $::env(TERMINAL_OUTPUT)"
lappend arg_list {*}$arg
try_exec bash -c "verilator \
--lint-only \
puts_info "Running linter (Verilator) (log: [relpath . $log])..."
catch_exec verilator \
-Wall \
--lint-only \
--Wno-DECLFILENAME \
--top-module $::env(DESIGN_NAME) \
$arg_list"
{*}$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 wrap them around an ifdef. It heavily unrecommended to rely on timing constructs for synthesis."
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
Expand Down
50 changes: 33 additions & 17 deletions scripts/utils/utils.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,28 @@ proc set_and_log {var val} {
close $global_cfg_file
}

proc try_exec {args} {
# puts_info "Executing \"$args\"\n"
proc log_exec {args} {
if { ! [catch { set cmd_log_file [open $::env(RUN_DIR)/cmds.log a+] } ]} {
set timestamp [clock format [clock seconds]]
puts $cmd_log_file "$timestamp - Executing \"$args\"\n"
close $cmd_log_file
}
}

proc catch_exec {args} {
# Logs invocation to command log.
# Emplaces an array, "exec_result", in the calling context.
# The array has two keys, exit_code and out.
upvar exec_result exec_result
log_exec $args
set exec_result(exit_code) [catch {eval exec $args} exec_result(out)]
}

proc try_exec {args} {
# Logs invocation to command log.
# Fails on non-zero exit codes: prints the last 10 lines and throws an error.
# Returns nothing on success.
log_exec $args
set exit_code [catch {eval exec $args} error_msg]
if { $exit_code } {
set tool [string range $args 0 [string first " " $args]]
Expand Down Expand Up @@ -306,9 +321,10 @@ proc calc_total_runtime {args} {
set_if_unset arg_values(-report) $::env(REPORTS_DIR)/total_runtime.txt
set_if_unset arg_values(-status) "flow completed"

if {[catch {exec python3 $::env(SCRIPTS_DIR)/write_runtime.py --conclude --seconds --time-in $::env(timer_end) $arg_values(-status)} err]} {
catch_exec python3 $::env(SCRIPTS_DIR)/write_runtime.py --conclude --seconds --time-in $::env(timer_end) $arg_values(-status)
if { $exec_result(exit_code) } {
puts_err "Failed to calculate total runtime:"
puts_err "$err"
puts_err "$exec_result(out)"
}
}
}
Expand Down Expand Up @@ -402,16 +418,17 @@ proc generate_final_summary_report {args} {
set_if_unset arg_values(-output) $::env(REPORTS_DIR)/metrics.csv
set_if_unset arg_values(-man_report) $::env(REPORTS_DIR)/manufacturability.rpt

if {
[catch {exec python3 $::env(OPENLANE_ROOT)/scripts/generate_reports.py -d $::env(DESIGN_DIR) \
--design_name $::env(DESIGN_NAME) \
--tag $::env(RUN_TAG) \
--output_file $arg_values(-output) \
--man_report $arg_values(-man_report) \
--run_path $::env(RUN_DIR)} err]
} {
catch_exec python3 $::env(OPENLANE_ROOT)/scripts/generate_reports.py \
-d $::env(DESIGN_DIR) \
--design_name $::env(DESIGN_NAME) \
--tag $::env(RUN_TAG) \
--output_file $arg_values(-output) \
--man_report $arg_values(-man_report) \
--run_path $::env(RUN_DIR)

if { $exec_result(exit_code) } {
puts_err "Failed to create manufacturability and metric reports:"
puts_err "$err"
puts_err "$exec_result(out)"
} else {

set man_report_rel [relpath . $arg_values(-man_report)]
Expand Down Expand Up @@ -667,15 +684,14 @@ proc run_tcl_script {args} {
} else {
puts_verbose "Executing $tool with Tcl script '$script_relative'..."

set exit_code [catch {exec {*}$args} error_msg]

if { $exit_code } {
catch_exec {*}$args
if { $exec_result(exit_code) } {
set print_error_msg "during executing $tool script $script"
set log_relpath [relpath $::env(PWD) $arg_values(-indexed_log)]

puts_err "$print_error_msg"
puts_err "Log: $log_relpath"
puts_err "Last 10 lines:\n[exec tail -10 << $error_msg]\n"
puts_err "Last 10 lines:\n[exec tail -10 << $exec_result(out)]\n"

set create_reproducible 1
puts_err "Creating issue reproducible..."
Expand Down
24 changes: 24 additions & 0 deletions scripts/yosys/blackbox.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 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.

yosys -import
foreach input $::env(YOSYS_IN) {
if { [info exists ::env(YOSYS_DEFINES)] } {
read_verilog -lib -D$::env(YOSYS_DEFINES) $input
} else {
read_verilog -lib $input
}
}
blackbox *
write_verilog -noattr -noexpr -nohex -nodec -defparam -blackboxes $::env(YOSYS_OUT)
Loading