# Copyright 2020 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.

# inputs expected as env vars
#set opt $::env(SYNTH_OPT)
set buffering $::env(SYNTH_BUFFERING)
set sizing $::env(SYNTH_SIZING)

yosys -import

set vtop $::env(DESIGN_NAME)
#set sdc_file $::env(SDC_FILE)
set sclib $::env(LIB_SYNTH)

if { [info exists ::env(SYNTH_DEFINES) ] } {
	foreach define $::env(SYNTH_DEFINES) {
		log "Defining $define"
		verilog_defines -D$define
	}
}

if { $::env(SYNTH_READ_BLACKBOX_LIB) } {
	log "Reading $::env(LIB_SYNTH_COMPLETE_NO_PG) as a blackbox"
	foreach lib $::env(LIB_SYNTH_COMPLETE_NO_PG) {
		read_liberty -lib -ignore_miss_dir -setattr blackbox $lib
	}
}

if { [info exists ::env(EXTRA_LIBS) ] } {
	foreach lib $::env(EXTRA_LIBS) {
		read_liberty -lib -ignore_miss_dir -setattr blackbox $lib
	}
}

if { [info exists ::env(VERILOG_FILES_BLACKBOX)] } {
	foreach verilog_file $::env(VERILOG_FILES_BLACKBOX) {
		read_verilog -sv -lib $verilog_file
	}
}


# ns expected (in sdc as well)
set clock_period [expr {$::env(CLOCK_PERIOD)*1000}]

set driver  $::env(SYNTH_DRIVING_CELL)
set cload   $::env(SYNTH_CAP_LOAD)
# input pin cap of IN_3VX8
set max_FO $::env(SYNTH_MAX_FANOUT)
if {![info exist ::env(SYNTH_MAX_TRAN)]} {
	set ::env(SYNTH_MAX_TRAN) [expr {0.1*$clock_period}]
} else {
	set ::env(SYNTH_MAX_TRAN) [expr {$::env(SYNTH_MAX_TRAN) * 1000}]
}
set max_Tran $::env(SYNTH_MAX_TRAN)


# Mapping parameters
set A_factor  0.00
set B_factor  0.88
set F_factor  0.00

# Don't change these unless you know what you are doing
set stat_ext    ".stat.rpt"
set chk_ext    ".chk.rpt"
set gl_ext      ".gl.v"
set constr_ext  ".$clock_period.constr"
set timing_ext  ".timing.txt"
set abc_ext     ".abc"


# get old sdc, add library specific stuff for abc scripts
set sdc_file $::env(yosys_tmp_file_tag).sdc
set outfile [open ${sdc_file} w]
#puts $outfile $sdc_data
puts $outfile "set_driving_cell ${driver}"
puts $outfile "set_load ${cload}"
close $outfile


# ABC Scrips
set abc_rs_K    "resub,-K,"
set abc_rs      "resub"
set abc_rsz     "resub,-z"
set abc_rw_K    "rewrite,-K,"
set abc_rw      "rewrite"
set abc_rwz     "rewrite,-z"
set abc_rf      "refactor"
set abc_rfz     "refactor,-z"
set abc_b       "balance"

set abc_resyn2        "${abc_b}; ${abc_rw}; ${abc_rf}; ${abc_b}; ${abc_rw}; ${abc_rwz}; ${abc_b}; ${abc_rfz}; ${abc_rwz}; ${abc_b}"
set abc_share         "strash; multi,-m; ${abc_resyn2}"
set abc_resyn2a       "${abc_b};${abc_rw};${abc_b};${abc_rw};${abc_rwz};${abc_b};${abc_rwz};${abc_b}"
set abc_resyn3        "balance;resub;resub,-K,6;balance;resub,-z;resub,-z,-K,6;balance;resub,-z,-K,5;balance"
set abc_resyn2rs      "${abc_b};${abc_rs_K},6;${abc_rw};${abc_rs_K},6,-N,2;${abc_rf};${abc_rs_K},8;${abc_rw};${abc_rs_K},10;${abc_rwz};${abc_rs_K},10,-N,2;${abc_b},${abc_rs_K},12;${abc_rfz};${abc_rs_K},12,-N,2;${abc_rwz};${abc_b}"

set abc_choice        "fraig_store; ${abc_resyn2}; fraig_store; ${abc_resyn2}; fraig_store; fraig_restore"
set abc_choice2      "fraig_store; balance; fraig_store; ${abc_resyn2}; fraig_store; ${abc_resyn2}; fraig_store; ${abc_resyn2}; fraig_store; fraig_restore"

set abc_map_old_cnt			"map,-p,-a,-B,0.2,-A,0.9,-M,0"
set abc_map_old_dly         "map,-p,-B,0.2,-A,0.9,-M,0"
set abc_retime_area         "retime,-D,{D},-M,5"
set abc_retime_dly          "retime,-D,{D},-M,6"
set abc_map_new_area        "amap,-m,-Q,0.1,-F,20,-A,20,-C,5000"

set abc_area_recovery_1       "${abc_choice}; map;"
set abc_area_recovery_2       "${abc_choice2}; map;"

set map_old_cnt			    "map,-p,-a,-B,0.2,-A,0.9,-M,0"
set map_old_dly			    "map,-p,-B,0.2,-A,0.9,-M,0"
set abc_retime_area   	"retime,-D,{D},-M,5"
set abc_retime_dly    	"retime,-D,{D},-M,6"
set abc_map_new_area  	"amap,-m,-Q,0.1,-F,20,-A,20,-C,5000"

if {$buffering==1} {
	set abc_fine_tune		"buffer,-N,${max_FO},-S,${max_Tran};upsize,{D};dnsize,{D}"
} elseif {$sizing} {
	set abc_fine_tune       "upsize,{D};dnsize,{D}"
} else {
	set abc_fine_tune       ""
}


set delay_scripts [list \
	"+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_resyn2};${abc_retime_dly}; scleanup;${abc_map_old_dly};retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
	\
	"+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_resyn2};${abc_retime_dly}; scleanup;${abc_choice2};${abc_map_old_dly};${abc_area_recovery_2}; retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
	\
	"+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_resyn2};${abc_retime_dly}; scleanup;${abc_choice};${abc_map_old_dly};${abc_area_recovery_1}; retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
	\
	"+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_resyn2};${abc_retime_area};scleanup;${abc_choice2};${abc_map_new_area};${abc_choice2};${abc_map_old_dly};retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
	]

set area_scripts [list \
	"+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_resyn2};${abc_retime_area};scleanup;${abc_choice2};${abc_map_new_area};retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
	\
	"+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_resyn2};${abc_retime_area};scleanup;${abc_choice2};${abc_map_new_area};${abc_choice2};${abc_map_new_area};retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
	\
	"+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_choice2};${abc_retime_area};scleanup;${abc_choice2};${abc_map_new_area};${abc_choice2};${abc_map_new_area};retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
	]

set all_scripts [list {*}$delay_scripts {*}$area_scripts]

set strategy_parts [split $::env(SYNTH_STRATEGY)]

proc synth_strategy_format_err { } {
	upvar area_scripts area_scripts
	upvar delay_scripts delay_scripts
	log -stderr "\[ERROR] Misformatted SYNTH_STRATEGY (\"$::env(SYNTH_STRATEGY)\")."
	log -stderr "\[ERROR] Correct format is \"DELAY|AREA 0-[expr [llength $delay_scripts]-1]|0-[expr [llength $area_scripts]-1]\"."
	exit 1
}

if { [llength $strategy_parts] != 2 } {
	synth_strategy_format_err
}

set strategy_type [lindex $strategy_parts 0]
set strategy_type_idx [lindex $strategy_parts 1]

if { $strategy_type != "AREA" && $strategy_type != "DELAY" } {
	log -stderr "\[ERROR] AREA|DELAY tokens not found. ($strategy_type)"
	synth_strategy_format_err
}

if { $strategy_type == "DELAY" && $strategy_type_idx >= [llength $delay_scripts] } {
	log -stderr "\[ERROR] strategy index ($strategy_type_idx) is too high."
	synth_strategy_format_err
}

if { $strategy_type == "AREA" && $strategy_type_idx >= [llength $area_scripts] } {
	log -stderr "\[ERROR] strategy index ($strategy_type_idx) is too high."
	synth_strategy_format_err
}

if { $strategy_type == "DELAY" } {
	set strategy $strategy_type_idx
} else {
	set strategy [expr {[llength $delay_scripts]+$strategy_type_idx}]
}

set adder_type $::env(SYNTH_ADDER_TYPE)
if { !($adder_type in [list "YOSYS" "FA" "RCA" "CSA"]) } {
	log -stderr "\[ERROR] Misformatted SYNTH_ADDER_TYPE (\"$::env(SYNTH_ADDER_TYPE)\")."
	log -stderr "\[ERROR] Correct format is \"YOSYS|FA|RCA|CSA\"."
	exit 1
}

set vIdirsArgs ""
if {[info exist ::env(VERILOG_INCLUDE_DIRS)]} {
	foreach dir $::env(VERILOG_INCLUDE_DIRS) {
		lappend vIdirsArgs "-I$dir"
	}
	set vIdirsArgs [join $vIdirsArgs]
}

for { set i 0 } { $i < [llength $::env(VERILOG_FILES)] } { incr i } {
	read_verilog -sv {*}$vIdirsArgs [lindex $::env(VERILOG_FILES) $i]
}

select -module $vtop
show -format dot -prefix $::env(TMP_DIR)/synthesis/hierarchy
select -clear

hierarchy -check -top $vtop

# Infer tri-state buffers.
set tbuf_map false
if { [info exists ::env(TRISTATE_BUFFER_MAP)] } {
        if { [file exists $::env(TRISTATE_BUFFER_MAP)] } {
                set tbuf_map true
                tribuf
        } else {
          log "WARNING: TRISTATE_BUFFER_MAP is defined but could not be found: $::env(TRISTATE_BUFFER_MAP)"
        }
}

# handle technology mapping of rca and csa adders
if { $adder_type == "RCA"} {
	if { [info exists ::env(RIPPLE_CARRY_ADDER_MAP)] && [file exists $::env(RIPPLE_CARRY_ADDER_MAP)] } {
		techmap -map $::env(RIPPLE_CARRY_ADDER_MAP)
	}
} elseif { $adder_type == "CSA"} {
	if { [info exists ::env(CARRY_SELECT_ADDER_MAP)] && [file exists $::env(CARRY_SELECT_ADDER_MAP)] } {
		techmap -map $::env(CARRY_SELECT_ADDER_MAP)
	}
}

if { $::env(SYNTH_NO_FLAT) } {
	synth -top $vtop
} else {
	synth -top $vtop -flatten
}

if { $::env(SYNTH_SHARE_RESOURCES) } {
	share -aggressive
}

set fa_map false
if { $adder_type == "FA" } {
	if { [info exists ::env(FULL_ADDER_MAP)] && [file exists $::env(FULL_ADDER_MAP)] } {
		extract_fa -fa -v
		extract_fa -ha -v
		set fa_map true
	}
}

opt
opt_clean -purge

tee -o "$::env(yosys_report_file_tag)_pre.stat" stat

# Map tri-state buffers.
if { $tbuf_map } {
        log {mapping tbuf}
        techmap -map $::env(TRISTATE_BUFFER_MAP)
        simplemap
}

# Map Full Adders.
if { $fa_map } {
	techmap -map $::env(FULL_ADDER_MAP)
}

# handle technology mapping of 4-MUX, and tell Yosys to infer 4-muxes
if { [info exists ::env(SYNTH_MUX4_MAP)] && [file exists $::env(SYNTH_MUX4_MAP)] } {
  muxcover -mux4 
  techmap -map $::env(SYNTH_MUX4_MAP)
  simplemap
}

# handle technology mapping of 2-MUX
if { [info exists ::env(SYNTH_MUX_MAP)] && [file exists $::env(SYNTH_MUX_MAP)] } {
  techmap -map $::env(SYNTH_MUX_MAP)
  simplemap
}

# handle technology mapping of latches
if { [info exists ::env(SYNTH_LATCH_MAP)] && [file exists $::env(SYNTH_LATCH_MAP)] } {
	techmap -map $::env(SYNTH_LATCH_MAP)
	simplemap
}

dfflibmap -liberty $sclib
tee -o "$::env(yosys_report_file_tag)_dff.stat" stat

if { [info exists ::env(SYNTH_EXPLORE)] && $::env(SYNTH_EXPLORE) } {
	design -save myDesign

	for { set index 0 }  { $index < [llength $all_scripts] }  { incr index } {
		log "\[INFO\]: ABC: WireLoad : S_$index"
		design -load myDesign

		abc -D $clock_period \
			-constr "$sdc_file" \
			-liberty $sclib  \
			-script [lindex $all_scripts $index]

		setundef -zero

		hilomap -hicell {*}$::env(SYNTH_TIEHI_PORT) -locell {*}$::env(SYNTH_TIELO_PORT)

		# get rid of the assignments that make verilog2def fail
		splitnets
		opt_clean -purge
		insbuf -buf {*}$::env(SYNTH_MIN_BUF_PORT)

		tee -o "$::env(yosys_report_file_tag)_$index$chk_ext" check
		tee -o "$::env(yosys_report_file_tag)$index$stat_ext" stat -top $vtop -liberty [lindex $::env(LIB_SYNTH_COMPLETE_NO_PG) 0]
		write_verilog -noattr -noexpr -nohex -nodec -defparam "$::env(yosys_result_file_tag)_$index.v"
		design -reset
	}
} else {

	log "\[INFO\]: ABC: WireLoad : S_$strategy"

	abc -D $clock_period \
		-constr "$sdc_file" \
		-liberty $sclib  \
		-script [lindex $all_scripts $strategy] \
		-showtmp;

	setundef -zero

	hilomap -hicell {*}$::env(SYNTH_TIEHI_PORT) -locell {*}$::env(SYNTH_TIELO_PORT)

	# get rid of the assignments that make verilog2def fail
	splitnets
	opt_clean -purge
	insbuf -buf {*}$::env(SYNTH_MIN_BUF_PORT)

	tee -o "$::env(yosys_report_file_tag)_$strategy$chk_ext" check
	tee -o "$::env(yosys_report_file_tag)_$strategy$stat_ext" stat -top $vtop -liberty [lindex $::env(LIB_SYNTH_COMPLETE_NO_PG) 0]
	write_verilog -noattr -noexpr -nohex -nodec -defparam "$::env(SAVE_NETLIST)"
}

if { $::env(SYNTH_NO_FLAT) } {
	design -reset
	read_liberty -lib -ignore_miss_dir -setattr blackbox $::env(LIB_SYNTH_COMPLETE_NO_PG)
	file copy -force $::env(SAVE_NETLIST) $::env(yosys_tmp_file_tag)_unflat.v
	read_verilog -sv $::env(SAVE_NETLIST)
	synth -top $vtop -flatten
	splitnets
	opt_clean -purge
	insbuf -buf {*}$::env(SYNTH_MIN_BUF_PORT)
	write_verilog -noattr -noexpr -nohex -nodec -defparam "$::env(SAVE_NETLIST)"
	tee -o "$::env(yosys_report_file_tag)_$strategy$chk_ext" check
	tee -o "$::env(yosys_report_file_tag)_$strategy$stat_ext" stat -top $vtop -liberty [lindex $::env(LIB_SYNTH_COMPLETE_NO_PG) 0]
}