| # Copyright (c) Efabless Corporation. All rights reserved. |
| # See LICENSE file in the project root for full license information. |
| proc set_def {def} { |
| set ::env(CURRENT_DEF) $def |
| set replace [string map {/ \\/} $def] |
| exec sed -i -e "s/\\(set ::env(CURRENT_DEF)\\).*/\\1 $replace/" "$::env(GLB_CFG_FILE)" |
| } |
| |
| proc prep {args} { |
| set ::env(timer_start) [clock seconds] |
| source $::env(OPENLANE_ROOT)/scripts/utils/utils.tcl |
| set ::env(SCRIPTS_DIR) "$::env(OPENLANE_ROOT)/scripts/" |
| set ::env(PATH) "$::env(PATH):$::env(SCRIPTS_DIR)/pdn/src/scripts" |
| set ::env(TCLLIBPATH) "$::env(SCRIPTS_DIR)/pdn/src/scripts" |
| foreach tcl_file [glob $::env(SCRIPTS_DIR)/utils/*.tcl] { |
| source $tcl_file |
| } |
| foreach tcl_file [glob $::env(SCRIPTS_DIR)/tcl_commands/*.tcl] { |
| if {[string first "pkgIndex.tcl" $tcl_file 0] == -1} { |
| source $tcl_file |
| } |
| } |
| |
| set options {{-design required} \ |
| {-tag optional} \ |
| {-config optional} \ |
| } |
| |
| set flags {-init_design_config -disable_output -overwrite} |
| |
| set args_copy $args |
| parse_key_args "flow.tcl" args arg_values $options flags_map $flags |
| |
| |
| if { [info exists arg_values(-config)] } { |
| set config_tag $arg_values(-config) |
| } else { |
| set config_tag "config" |
| } |
| |
| if { [info exist flags_map(-init_design_config)] } { |
| set config_tag "config" |
| if { [info exist arg_values(-tag) ] } { |
| set config_tag $arg_values(-tag) |
| } |
| init_design $arg_values(-design) $config_tag |
| exit |
| } |
| |
| if { ! [info exist flags_map(-disable_output)] } { |
| set ::env(TERMINAL_OUTPUT) "/dev/tty" |
| } else { |
| set ::env(TERMINAL_OUTPUT) "/dev/null" |
| } |
| |
| set ::env(datetime) [clock format [clock seconds] -format %d-%m_%H-%M] |
| if { [lsearch -exact $args_copy -tag ] >= 0} { |
| set tag "$arg_values(-tag)" |
| } else { |
| set tag $::env(datetime) |
| } |
| |
| |
| |
| #set flow_config "./configuration/flow_default.tcl" |
| set ::env(CONFIGS) [glob $::env(OPENLANE_ROOT)/configuration/*.tcl] |
| set ::env(DESIGN_DIR) [file normalize $arg_values(-design)] |
| if { ![file exists $::env(DESIGN_DIR)] } { |
| set ::env(DESIGN_DIR) [file normalize $::env(OPENLANE_ROOT)/designs/$arg_values(-design)/] |
| } |
| set ::env(DESIGN_CONFIG) $::env(DESIGN_DIR)/$config_tag.tcl |
| |
| foreach config $::env(CONFIGS) { |
| source $config |
| } |
| |
| source $::env(DESIGN_CONFIG) |
| set pdk_config $::env(PDK_ROOT)/$::env(PDK)/libs.tech/openlane/config.tcl |
| set pdk_variant_config $::env(PDK_ROOT)/$::env(PDK)/libs.tech/openlane/$::env(PDK_VARIANT)/config.tcl |
| source $pdk_config |
| source $pdk_variant_config |
| source $::env(DESIGN_CONFIG) |
| |
| # |
| ############################ |
| # Prep directories and files |
| ############################ |
| # |
| set ::env(RUN_TAG) "$tag" |
| set ::env(RUN_DIR) "$::env(DESIGN_DIR)/runs/$tag/" |
| set ::env(RESULTS_DIR) "$::env(DESIGN_DIR)/runs/$tag/results/" |
| set ::env(TMP_DIR) "$::env(DESIGN_DIR)/runs/$tag/tmp/" |
| set ::env(LOG_DIR) "$::env(DESIGN_DIR)/runs/$tag/logs/" |
| set ::env(REPORTS_DIR) "$::env(DESIGN_DIR)/runs/$tag/reports/" |
| set ::env(GLB_CFG_FILE) "$::env(DESIGN_DIR)/runs/$tag/config.tcl" |
| |
| if { [file exists $::env(RUN_DIR)] } { |
| if { [info exists flags_map(-overwrite)] } { |
| puts "Removing exisiting run $::env(RUN_DIR)" |
| exec rm -r $::env(RUN_DIR) |
| } else { |
| puts "A run for $::env(DESIGN_NAME) with tag: $tag already exists pass -overwrite option to overwrite it" |
| } |
| } |
| |
| if { [file exists $::env(GLB_CFG_FILE)] } { |
| if { [info exist flags_map(-overwrite)] } { |
| puts "Removing $::env(GLB_CFG_FILE)" |
| exec rm $::env(GLB_CFG_FILE) |
| } else { |
| puts "sourcing $::env(GLB_CFG_FILE)" |
| source $::env(GLB_CFG_FILE) |
| } |
| } |
| set tmp_output { |
| {yosys "synthesis/yosys"} |
| {opensta synthesis/opensta} |
| {verilog2def floorplan/verilog2def} |
| {ioPlacer floorplan/ioPlacer} |
| {pdn floorplan/pdn} |
| {tapcell floorplan/tapcell} |
| {replaceio placement/replace} |
| {psn placement/psn} |
| {opendp placement/opendp} |
| {addspacers routing/addspacers} |
| {fastroute routing/fastroute} |
| {tritonRoute routing/tritonRoute} |
| {magic magic/magic} |
| {cts cts/cts} |
| {lvs lvs/lvs} |
| } |
| |
| set final_output \ |
| [list \ |
| [list yosys synthesis/$::env(DESIGN_NAME).synthesis] \ |
| [list tapcell floorplan/$::env(DESIGN_NAME).floorplan] \ |
| [list opendp placement/$::env(DESIGN_NAME).placement] \ |
| [list tritonRoute routing/$::env(DESIGN_NAME).routing] \ |
| [list cts cts/$::env(DESIGN_NAME).cts] \ |
| [list magic magic/$::env(DESIGN_NAME).magic] \ |
| [list lvs lvs/$::env(DESIGN_NAME).lvs] \ |
| ] |
| |
| array set results_file_name [make_array $final_output $::env(RESULTS_DIR)] |
| array set reports_file_name [make_array $tmp_output $::env(REPORTS_DIR)] |
| array set logs_file_name [make_array $tmp_output $::env(LOG_DIR)] |
| array set tmp_file_name [make_array $tmp_output $::env(TMP_DIR)] |
| |
| foreach {key value} [array get results_file_name] { |
| set ::env(${key}_result_file_tag) $value |
| } |
| foreach {key value} [array get reports_file_name] { |
| set ::env(${key}_report_file_tag) $value |
| } |
| foreach {key value} [array get logs_file_name] { |
| set ::env(${key}_log_file_tag) $value |
| } |
| foreach {key value} [array get tmp_file_name] { |
| set ::env(${key}_tmp_file_tag) $value |
| } |
| |
| |
| exec mkdir -p $::env(RESULTS_DIR) $::env(TMP_DIR) $::env(LOG_DIR) $::env(REPORTS_DIR) |
| |
| set stages {synthesis floorplan placement cts routing magic lvs} |
| foreach stage $stages { |
| exec mkdir -p $::env(RESULTS_DIR)/$stage \ |
| $::env(TMP_DIR)/$stage \ |
| $::env(LOG_DIR)/$stage \ |
| $::env(REPORTS_DIR)/$stage |
| } |
| |
| # merge cells |
| exec $::env(SCRIPTS_DIR)/mergeLef.py -i $::env(TECH_LEF) $::env(CELLS_LEF) -o $::env(TMP_DIR)/merged_unpadded.lef |& tee $::env(TERMINAL_OUTPUT) |
| set ::env(MERGED_LEF_UNPADDED) $::env(TMP_DIR)/merged_unpadded.lef |
| |
| # pad lef |
| set ::env(CELLS_LEF_UNPADDED) $::env(TMP_DIR)/merged_unpadded.lef |
| try_catch $::env(SCRIPTS_DIR)/padLefMacro.py -s $::env(PLACE_SITE) -r $::env(CELL_PAD) -i $::env(CELLS_LEF_UNPADDED) -o $::env(TMP_DIR)/merged.lef -e "$::env(CELL_PAD_EXECLUDE)" |& tee $::env(TERMINAL_OUTPUT) |
| |
| set ::env(CELLS_LEF) $::env(TMP_DIR)/merged.lef |
| if { $::env(USE_GPIO_PADS) } { |
| exec cp $::env(CELLS_LEF) $::env(CELLS_LEF).old |
| exec $::env(SCRIPTS_DIR)/mergeLef.py -i $::env(CELLS_LEF).old {*}$::env(GPIO_PADS_LEF) -o $::env(CELLS_LEF) |
| } |
| |
| set ::env(MERGED_LEF) $::env(CELLS_LEF) |
| |
| # trim libs |
| set trimmed_lib $::env(TMP_DIR)/trimmed.lib |
| if { ![info exists $trimmed_lib] } { |
| puts $::env(LIB_SYNTH) |
| exec $::env(SCRIPTS_DIR)/libtrim.pl $::env(PDK_VARIANT) $::env(LIB_SYNTH) > $trimmed_lib |
| set ::env(LIB_SYNTH) $trimmed_lib |
| } |
| |
| set tracks_copy $::env(TMP_DIR)/tracks_copy.info |
| exec cp $::env(TRACKS_INFO_FILE) $tracks_copy |
| |
| # change to system verilog |
| #if {$::env(SYSTEM_VERILOG)} { |
| # foreach verilogfile $::env(VERILOG_FILES) { |
| # system_verilog_2_verilog \ |
| # -include $::env(VERILOG_FILES) \ |
| # -input $verilogfile \ |
| # -output [file rootname $verilogfile].v \ |
| # -define SYNTHESIS |
| # } |
| #} |
| |
| set util $::env(FP_CORE_UTIL) |
| set density $::env(PL_TARGET_DENSITY) |
| |
| # Fill config file |
| #General |
| exec echo "# General config" > $::env(GLB_CFG_FILE) |
| set_log ::env(PDK) $::env(PDK) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(PDK_VARIANT) $::env(PDK_VARIANT) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(PDK_ROOT) $::env(PDK_ROOT) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(CELL_PAD) $::env(CELL_PAD) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(MERGED_LEF) $::env(MERGED_LEF) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(TRACKS_INFO_FILE) $::env(TRACKS_INFO_FILE) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(TECH_LEF) $::env(TECH_LEF) $::env(GLB_CFG_FILE) 1 |
| # Design |
| exec echo "# Design config" >> $::env(GLB_CFG_FILE) |
| set_log ::env(CLOCK_PERIOD) $::env(CLOCK_PERIOD) $::env(GLB_CFG_FILE) 1 |
| # Synthesis |
| exec echo "# Synthesis config" >> $::env(GLB_CFG_FILE) |
| set_log ::env(LIB_SYNTH) $::env(LIB_SYNTH) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(SYNTH_DRIVING_CELL) $::env(SYNTH_DRIVING_CELL) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(SYNTH_CAP_LOAD) $::env(SYNTH_CAP_LOAD) $::env(GLB_CFG_FILE) 1; # femtofarad |
| set_log ::env(SYNTH_MAX_FANOUT) $::env(SYNTH_MAX_FANOUT) $::env(GLB_CFG_FILE) 1 |
| if { [info exist ::env(SYNTH_MAX_TRAN)] } { |
| set_log ::env(SYNTH_MAX_TRAN) $::env(SYNTH_MAX_TRAN) $::env(GLB_CFG_FILE) 1 |
| } else { |
| set_log ::env(SYNTH_MAX_TRAN) "\[\expr {0.1*$::env(CLOCK_PERIOD)}\]" $::env(GLB_CFG_FILE) 1 |
| } |
| set_log ::env(LIB_MIN) $::env(LIB_MIN) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(LIB_MAX) $::env(LIB_MAX) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(LIB_TYPICAL) $::env(LIB_TYPICAL) $::env(GLB_CFG_FILE) 1 |
| if { $::env(SYNTH_TOP_LEVEL) } { |
| set_log ::env(SYNTH_SCRIPT) "$::env(OPENLANE_ROOT)/scripts/synth_top.tcl" $::env(GLB_CFG_FILE) 0 |
| } else { |
| set_log ::env(SYNTH_SCRIPT) "$::env(OPENLANE_ROOT)/scripts/synth.tcl" $::env(GLB_CFG_FILE) 0 |
| } |
| set_log ::env(SYNTH_STRATEGY) $::env(SYNTH_STRATEGY) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(CLOCK_BUFFER_FANOUT) $::env(CLOCK_BUFFER_FANOUT) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(SYNTH_OPT) 0 $::env(GLB_CFG_FILE) 0 |
| |
| # Floorplan |
| exec echo "# Floorplan config" >> $::env(GLB_CFG_FILE) |
| set_log ::env(FP_SIZING) $::env(FP_SIZING) $::env(GLB_CFG_FILE) 0; # absolute, relative |
| set_log ::env(FP_CORE_UTIL) $util $::env(GLB_CFG_FILE) 1 |
| set_log ::env(FP_ASPECT_RATIO) $::env(FP_ASPECT_RATIO) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(FP_CORE_MARGIN) $::env(FP_CORE_MARGIN) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(FP_IO_HMETAL) $::env(FP_IO_HMETAL) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(FP_IO_VMETAL) $::env(FP_IO_VMETAL) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(FP_IO_RANDOM) 2 $::env(GLB_CFG_FILE) 0; #0 (default, disabled) 1 fully random, 2 evenly distributed, 3 group on the middle of core edge |
| set_log ::env(FP_WELLTAP_CELL) $::env(FP_WELLTAP_CELL) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(FP_ENDCAP_CELL) $::env(FP_ENDCAP_CELL) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(FP_PDN_VOFFSET) $::env(FP_PDN_VOFFSET) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(FP_PDN_VPITCH) $::env(FP_PDN_VPITCH) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(FP_PDN_HOFFSET) $::env(FP_PDN_HOFFSET) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(FP_PDN_HPITCH) $::env(FP_PDN_HPITCH) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(FP_TAPCELL_DIST) $::env(FP_TAPCELL_DIST) $::env(GLB_CFG_FILE) 1 |
| |
| # Placement |
| exec echo "# Placement config" >> $::env(GLB_CFG_FILE) |
| set_log ::env(PL_TARGET_DENSITY) $density $::env(GLB_CFG_FILE) 1 |
| set_log ::env(PL_INIT_COEFF) 0.00002 $::env(GLB_CFG_FILE) 0 |
| set_log ::env(PL_TIME_DRIVEN) $::env(PL_TIME_DRIVEN) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(PL_LIB) $::env(PL_LIB) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(PL_IO_ITER) 5 $::env(GLB_CFG_FILE) 0 |
| |
| # CTS |
| exec echo "# CTS config" >> $::env(GLB_CFG_FILE) |
| set_log ::env(CTS_TARGET_SKEW) $::env(CTS_TARGET_SKEW) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(CTS_ROOT_BUFFER) $::env(CTS_ROOT_BUFFER) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(CTS_TECH_DIR) $::env(CTS_TECH_DIR) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(CTS_TOLERANCE) $::env(CTS_TOLERANCE) $::env(GLB_CFG_FILE) 1 |
| |
| # ROUTING |
| exec echo "# Routing config" >> $::env(GLB_CFG_FILE) |
| set_log ::env(GLB_RT_MAXLAYER) $::env(GLB_RT_MAXLAYER) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(GLB_RT_ADJUSTMENT) $::env(GLB_RT_ADJUSTMENT) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(GLB_RT_LI1_ADJUSTMENT) $::env(GLB_RT_LI1_ADJUSTMENT) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(GLB_RT_MET1_ADJUSTMENT) $::env(GLB_RT_MET1_ADJUSTMENT) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(GLB_RT_MINLAYER) $::env(GLB_RT_MINLAYER) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(GLB_RT_MAXLAYER) $::env(GLB_RT_MAXLAYER) $::env(GLB_CFG_FILE) 1 |
| |
| # Flow control |
| exec echo "# Flow control config" >> $::env(GLB_CFG_FILE) |
| set_log ::env(RUN_MAGIC) $::env(RUN_MAGIC) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(RUN_SIMPLE_CTS) $::env(RUN_SIMPLE_CTS) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(RUN_ROUTING_DETAILED) $::env(RUN_ROUTING_DETAILED) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(CLOCK_TREE_SYNTH) $::env(CLOCK_TREE_SYNTH) $::env(GLB_CFG_FILE) 1 |
| set_log ::env(FILL_INSERTION) $::env(FILL_INSERTION) $::env(GLB_CFG_FILE) 1 |
| |
| if { [info exists ::env(CURRENT_DEF)] } { |
| set_log ::env(CURRENT_DEF) $::env(CURRENT_DEF) $::env(GLB_CFG_FILE) 1 |
| } else { |
| set ::env(CURRENT_DEF) 0 |
| set_log ::env(CURRENT_DEF) $::env(CURRENT_DEF) $::env(GLB_CFG_FILE) 1 |
| } |
| |
| puts "Done" |
| return 0 |
| } |
| |
| proc run_cts {args} { |
| |
| # |----------------------------------------------------| |
| # |---------------- 4. CTS --------------------------| |
| # |----------------------------------------------------| |
| set ::env(CURRENT_STAGE) cts |
| if {![info exists ::env(OPENROAD)]} { |
| set ::env(OPENROAD) ~/.local/bin |
| } |
| |
| TIMER::timer_start |
| if {$::env(CLOCK_TREE_SYNTH)} { |
| exec openroad < $::env(OPENLANE_ROOT)/scripts/cts.tcl |& tee $::env(TERMINAL_OUTPUT) |
| exit |
| # set_core_dims \ |
| # -log_path $::env(verilog2def_log_file_tag).log \ |
| # |
| # gen_cts_config \ |
| # -verilog $::env(yosys_result_file_tag).v \ |
| # -def $::env(opendp_result_file_tag).def \ |
| # -root_buffer $::env(CTS_ROOT_BUFFER) \ |
| # -target_skew $::env(CTS_TARGET_SKEW) \ |
| # -output $::env(cts_tmp_file_tag).config \ |
| # -toler $::env(CTS_TOLERANCE) |
| # |
| # gen_clock_tree \ |
| # -config $::env(cts_tmp_file_tag).config |
| # eval exec mv [glob $::env(RESULTS_DIR)/cts/*] $::env(TMP_DIR)/cts/ |
| # exec mv $::env(TMP_DIR)/cts/$::env(DESIGN_NAME).cts.v $::env(RESULTS_DIR)/cts/$::env(DESIGN_NAME).cts.v |
| # exec mv $::env(TMP_DIR)/cts/$::env(DESIGN_NAME).cts.def $::env(RESULTS_DIR)/cts/$::env(DESIGN_NAME).cts.def |
| } else { |
| exec echo "SKIPPED!" >> $::env(cts_log_file_tag).log |
| try_catch cp $::env(opendp_result_file_tag).def $::env(cts_result_file_tag).def |
| } |
| TIMER::timer_stop |
| exec echo "[TIMER::get_runtime]" >> $::env(cts_log_file_tag)_runtime.txt |
| } |
| |
| proc run_magic {args} { |
| # |----------------------------------------------------| |
| # |---------------- 6. TAPE-OUT ---------------------| |
| # |----------------------------------------------------| |
| if {$::env(RUN_MAGIC)} { |
| puts "Streaming out GDS II..." |
| set ::env(CURRENT_STAGE) finishing |
| if {[catch {exec tclsh $::env(SCRIPTS_DIR)/run_magic.tcl} issue]} { } |
| # set PDKPATH $::env(PDK_ROOT)/$::env(PDK)/ |
| # set tech $PDKPATH/libs.tech/magic/current/EFS8A.tech |
| # cd $::env(TMP_DIR) |
| # exec /ef/apps/bin/magicGdrc -T $tech $::env(magic_result_file_tag).gds $::env(DESIGN_NAME) \ |
| |& tee $::env(TERMINAL_OUTPUT) $::env(magic_log_file_tag).drc |
| } |
| } |
| |
| proc run_magic_drc {args} { |
| set magicrc $::env(TMP_DIR)/magic.magicrc |
| set ::env(PDKPATH) "$::env(PDK_ROOT)/ef-skywater-s8/EFS8A" |
| set ::env(MAGPATH) "$::env(PDKPATH)/libs.ref/maglef" |
| exec envsubst < $::env(MAGIC_MAGICRC) > $magicrc |
| exec magic \ |
| -noconsole \ |
| -dnull \ |
| -rcfile $magicrc \ |
| $::env(SCRIPTS_DIR)/magic_drc.tcl \ |
| </dev/null \ |
| |& tee $::env(TERMINAL_OUTPUT) $::env(magic_log_file_tag)_drc.log |
| } |
| |
| proc padframe_gen {args} { |
| set pfg_exec $::env(SCRIPTS_DIR)/pfg.py |
| set pf_src $::env(DESIGN_DIR)/src |
| set pf_src_tmp $::env(TMP_DIR)/src |
| file copy $pf_src $pf_src_tmp |
| |
| fake_display_buffer |
| |
| exec $pfg_exec -nogui -tech-path=$::env(PDK_ROOT)/$::env(PDK) \ |
| -project-path=$pf_src_tmp \ |
| |& tee $::env(TERMINAL_OUTPUT) $pf_src_tmp/pfg.log |
| |
| kill_display_buffer |
| } |
| |
| package provide openlane 0.9 |
| |