From c1c612b5752d0f9dc511b490228d5ce27c7581b6 Mon Sep 17 00:00:00 2001 From: Unai Martinez-Corral Date: Sat, 6 Aug 2022 00:23:59 +0200 Subject: [PATCH] f4pga: yosys TCL wrappers moved from f4pga-arch-defs Signed-off-by: Unai Martinez-Corral --- f4pga/__init__.py | 2 + f4pga/common_modules/synth.py | 18 +- f4pga/setup.py | 9 +- f4pga/wrappers/sh/__init__.py | 6 +- f4pga/wrappers/sh/quicklogic/synth.f4pga.sh | 4 +- f4pga/wrappers/sh/xc7/synth.f4pga.sh | 4 +- f4pga/wrappers/tcl/__init__.py | 59 +++++ f4pga/wrappers/tcl/__main__.py | 27 +++ f4pga/wrappers/tcl/eos-s3/conv.f4pga.tcl | 11 + f4pga/wrappers/tcl/eos-s3/synth.f4pga.tcl | 183 +++++++++++++++ f4pga/wrappers/tcl/ice40/conv.f4pga.tcl | 7 + f4pga/wrappers/tcl/ice40/synth.f4pga.tcl | 29 +++ f4pga/wrappers/tcl/qlf_k4n8/conv.f4pga.tcl | 7 + f4pga/wrappers/tcl/qlf_k4n8/synth.f4pga.tcl | 32 +++ f4pga/wrappers/tcl/xc7/conv.f4pga.tcl | 20 ++ f4pga/wrappers/tcl/xc7/synth.f4pga.tcl | 245 ++++++++++++++++++++ 16 files changed, 650 insertions(+), 13 deletions(-) create mode 100644 f4pga/wrappers/tcl/__init__.py create mode 100644 f4pga/wrappers/tcl/__main__.py create mode 100644 f4pga/wrappers/tcl/eos-s3/conv.f4pga.tcl create mode 100644 f4pga/wrappers/tcl/eos-s3/synth.f4pga.tcl create mode 100644 f4pga/wrappers/tcl/ice40/conv.f4pga.tcl create mode 100644 f4pga/wrappers/tcl/ice40/synth.f4pga.tcl create mode 100644 f4pga/wrappers/tcl/qlf_k4n8/conv.f4pga.tcl create mode 100644 f4pga/wrappers/tcl/qlf_k4n8/synth.f4pga.tcl create mode 100644 f4pga/wrappers/tcl/xc7/conv.f4pga.tcl create mode 100644 f4pga/wrappers/tcl/xc7/synth.f4pga.tcl diff --git a/f4pga/__init__.py b/f4pga/__init__.py index 1ac42db44..62375f28d 100755 --- a/f4pga/__init__.py +++ b/f4pga/__init__.py @@ -75,6 +75,8 @@ ROOT = Path(__file__).resolve().parent FPGA_FAM = environ.get('FPGA_FAM', 'xc7') +if FPGA_FAM not in ['xc7', 'eos-s3', 'qlf_k4n8']: + raise(Exception(f"Unsupported FPGA_FAM <{FPGA_FAM}>!")) bin_dir_path = str(Path(sys_argv[0]).resolve().parent.parent) share_dir_path = \ diff --git a/f4pga/common_modules/synth.py b/f4pga/common_modules/synth.py index fcf12da2c..4a57e5b2d 100755 --- a/f4pga/common_modules/synth.py +++ b/f4pga/common_modules/synth.py @@ -22,6 +22,7 @@ from f4pga.common import decompose_depname, get_verbosity_level, sub as common_sub from f4pga.module import Module, ModuleContext +from f4pga.wrappers.tcl import get_script_path as get_tcl_wrapper_path def yosys_setup_tcl_env(tcl_env_def): @@ -100,16 +101,19 @@ def execute(self, ctx: ModuleContext): tcl_env = yosys_setup_tcl_env(ctx.values.yosys_tcl_env) \ if ctx.values.yosys_tcl_env else {} split_inouts = Path(tcl_env["UTILS_PATH"]) / 'split_inouts.py' - synth_tcl = Path(ctx.values.tcl_scripts) / 'synth.tcl' - conv_tcl = Path(ctx.values.tcl_scripts) / 'conv.tcl' if get_verbosity_level() >= 2: yield f'Synthesizing sources: {ctx.takes.sources}...' else: yield f'Synthesizing sources...' - yosys_synth(str(synth_tcl), tcl_env, ctx.takes.sources, - ctx.values.read_verilog_args, ctx.outputs.synth_log) + yosys_synth( + str(get_tcl_wrapper_path('synth')), + tcl_env, + ctx.takes.sources, + ctx.values.read_verilog_args, + ctx.outputs.synth_log + ) yield f'Splitting in/outs...' common_sub('python3', str(split_inouts), '-i', ctx.outputs.json, '-o', @@ -120,7 +124,11 @@ def execute(self, ctx: ModuleContext): wfptr.write('') yield f'Converting...' - yosys_conv(str(conv_tcl), tcl_env, ctx.outputs.synth_json) + yosys_conv( + str(get_tcl_wrapper_path('conv')), + tcl_env, + ctx.outputs.synth_json + ) def __init__(self, params): self.name = 'synthesize' diff --git a/f4pga/setup.py b/f4pga/setup.py index 0b1e9c832..82ade2043 100644 --- a/f4pga/setup.py +++ b/f4pga/setup.py @@ -83,7 +83,8 @@ def get_requirements(file: Path) -> List[str]: packages=[ "f4pga", "f4pga.common_modules", - "f4pga.wrappers.sh" + "f4pga.wrappers.sh", + "f4pga.wrappers.tcl" ], package_dir={"f4pga": "."}, package_data={ @@ -93,6 +94,12 @@ def get_requirements(file: Path) -> List[str]: 'f4pga.wrappers.sh': [ 'xc7/*.f4pga.sh', 'quicklogic/*.f4pga.sh' + ], + 'f4pga.wrappers.tcl': [ + 'xc7/*.f4pga.tcl', + 'eos-s3/*.f4pga.tcl', + 'qlf_k4n8/*.f4pga.tcl', + 'ice40/*.f4pga.tcl', ] }, classifiers=[], diff --git a/f4pga/wrappers/sh/__init__.py b/f4pga/wrappers/sh/__init__.py index 8ed200d48..03e3eb29e 100644 --- a/f4pga/wrappers/sh/__init__.py +++ b/f4pga/wrappers/sh/__init__.py @@ -25,6 +25,7 @@ from shutil import which from subprocess import check_call +from f4pga import FPGA_FAM python3 = which('python3') @@ -32,9 +33,7 @@ ROOT = Path(__file__).resolve().parent -FPGA_FAM = f4pga_environ.get('FPGA_FAM', 'xc7') -isQuickLogic = FPGA_FAM == 'eos-s3' - +isQuickLogic = FPGA_FAM != 'xc7' SH_SUBDIR = 'quicklogic' if isQuickLogic else FPGA_FAM F4PGA_INSTALL_DIR = f4pga_environ.get('F4PGA_INSTALL_DIR') @@ -433,6 +432,7 @@ def generate_bitstream(): done if [ -z $DEVICE ]; then echo "Please provide device name"; exit 1; fi if [ -z $FASM ]; then echo "Please provide an input FASM file name"; exit 1; fi +if [ ! -f "$FASM" ]; then echo "File <$FASM> does not exist!"; exit 1; fi if [ -z $BIT ]; then echo "Please provide an output bistream file name"; exit 1; fi """ + f""" if [[ "$DEVICE" =~ ^(qlf_k4n8.*)$ ]]; then diff --git a/f4pga/wrappers/sh/quicklogic/synth.f4pga.sh b/f4pga/wrappers/sh/quicklogic/synth.f4pga.sh index 575ac3255..a050c41f4 100755 --- a/f4pga/wrappers/sh/quicklogic/synth.f4pga.sh +++ b/f4pga/wrappers/sh/quicklogic/synth.f4pga.sh @@ -123,8 +123,8 @@ PINMAPCSV="pinmap_${PART}.csv" export TECHMAP_PATH="${F4PGA_SHARE_DIR}/techmaps/${FAMILY}" -SYNTH_TCL_PATH="${F4PGA_SHARE_DIR}/scripts/${FAMILY}/synth.tcl" -CONV_TCL_PATH="${F4PGA_SHARE_DIR}/scripts/${FAMILY}/conv.tcl" +SYNTH_TCL_PATH="$(python3 -m f4pga.wrappers.tcl synth "${FAMILY}")" +CONV_TCL_PATH="$(python3 -m f4pga.wrappers.tcl conv "${FAMILY}")" export USE_ROI="FALSE" export OUT_JSON=$TOP.json diff --git a/f4pga/wrappers/sh/xc7/synth.f4pga.sh b/f4pga/wrappers/sh/xc7/synth.f4pga.sh index c1e87b80f..0b818daeb 100755 --- a/f4pga/wrappers/sh/xc7/synth.f4pga.sh +++ b/f4pga/wrappers/sh/xc7/synth.f4pga.sh @@ -21,7 +21,7 @@ set -e export TECHMAP_PATH="${F4PGA_SHARE_DIR}"/techmaps/xc7_vpr/techmap export UTILS_PATH="${F4PGA_SHARE_DIR}"/scripts -SYNTH_TCL_PATH=${UTILS_PATH}/xc7/synth.tcl +SYNTH_TCL_PATH="$(python3 -m f4pga.wrappers.tcl synth)" VERILOG_FILES=() XDC_FILES=() @@ -140,4 +140,4 @@ else fi python3 ${UTILS_PATH}/split_inouts.py -i ${OUT_JSON} -o ${SYNTH_JSON} -yosys -p "read_json $SYNTH_JSON; tcl ${UTILS_PATH}/xc7/conv.tcl" +yosys -p "read_json $SYNTH_JSON; tcl $(python3 -m f4pga.wrappers.tcl conv)" diff --git a/f4pga/wrappers/tcl/__init__.py b/f4pga/wrappers/tcl/__init__.py new file mode 100644 index 000000000..8058c8324 --- /dev/null +++ b/f4pga/wrappers/tcl/__init__.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (C) 2020-2022 F4PGA Authors. +# +# 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 +# +# https://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. +# +# SPDX-License-Identifier: Apache-2.0 +# +# TCL scripts moved from f4pga-arch-defs + +from pathlib import Path +from f4pga import FPGA_FAM + +ROOT = Path(__file__).resolve().parent + +ARCHS = { + 'xc7': [ + 'artix7', + 'artix7_100t', + 'artix7_200t', + 'zynq7', + 'zynq7_z020', + 'spartan7' + ], + 'eos-s3': [ + 'ql-s3', + 'pp3' + ] +} + + +def get_script_path(arg, arch = None): + if arch is None: + arch = FPGA_FAM + for key, val in ARCHS.items(): + if arch in val: + arch = key + break + if arch not in [ + 'xc7', + 'eos-s3', + 'qlf_k4n8', + 'ice40' + ]: + raise(Exception(f"Unsupported arch <{arch}>!")) + if arg not in ['synth', 'conv']: + raise Exception(f'Unknown tcl wrapper <{arg}>!') + return ROOT / arch / f'{arg}.f4pga.tcl' diff --git a/f4pga/wrappers/tcl/__main__.py b/f4pga/wrappers/tcl/__main__.py new file mode 100644 index 000000000..727b8f2ab --- /dev/null +++ b/f4pga/wrappers/tcl/__main__.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (C) 2020-2022 F4PGA Authors. +# +# 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 +# +# https://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. +# +# SPDX-License-Identifier: Apache-2.0 + + +from sys import argv as sys_argv + +from f4pga.wrappers.tcl import get_script_path + + +if __name__ == '__main__': + print(get_script_path(sys_argv[1], sys_argv[2]) if len(sys_argv)>2 else get_script_path(sys_argv[1])) diff --git a/f4pga/wrappers/tcl/eos-s3/conv.f4pga.tcl b/f4pga/wrappers/tcl/eos-s3/conv.f4pga.tcl new file mode 100644 index 000000000..60587b001 --- /dev/null +++ b/f4pga/wrappers/tcl/eos-s3/conv.f4pga.tcl @@ -0,0 +1,11 @@ +yosys -import + +# Clean +opt_clean + +# Write EBLIF +write_blif -attr -cname -param \ + -true VCC VCC \ + -false GND GND \ + -undef VCC VCC \ + $::env(OUT_EBLIF) diff --git a/f4pga/wrappers/tcl/eos-s3/synth.f4pga.tcl b/f4pga/wrappers/tcl/eos-s3/synth.f4pga.tcl new file mode 100644 index 000000000..7c1a84f79 --- /dev/null +++ b/f4pga/wrappers/tcl/eos-s3/synth.f4pga.tcl @@ -0,0 +1,183 @@ +yosys -import + +plugin -i ql-iob +plugin -i ql-qlf + +yosys -import + +# Read VPR cells library +read_verilog -lib -specify $::env(TECHMAP_PATH)/cells_sim.v +# Read device specific cells library +read_verilog -lib -specify $::env(DEVICE_CELLS_SIM) + +# Synthesize +synth_quicklogic -family pp3 + +# Optimize the netlist by adaptively splitting cells that fit into C_FRAG into +# smaller that can fit into F_FRAG. + +proc max {a b} { + if {$a > $b} { + return $a + } else { + return $b + } +} + +proc min {a b} { + if {$a < $b} { + return $a + } else { + return $b + } +} + +# Returns the required number of C_FRAGs to fit the design +proc get_used_c_frag {} { + set used_c_frag [get_count -cells t:mux8x0 t:LUT4 t:logic_cell_macro] + set used_t_frag [get_count -cells t:mux4x0 t:LUT2 t:LUT3] + + set used_c_frag_for_t_frag [expr int(ceil($used_t_frag / 2.0))] + return [expr $used_c_frag + $used_c_frag_for_t_frag] +} + +# Returns the required number of F_FRAGs to fit the design +proc get_used_f_frag {} { + return [get_count -cells t:inv t:mux2x0 t:LUT1 t:logic_cell_macro] +} + + +# Load the plugin that allows to retrieve cell count +yosys plugin -i design_introspection +yosys -import + +# Maximum number of LOGIC cells in the device +set max_logic 891 + +# Target number of LOGIC cells. This is less than max to allow the VPR +# packet to have more freedom. +set target_logic [expr int($max_logic * 0.90)] +puts "PACK: Optimizing for target of $target_logic/$max_logic LOGIC cells" + +# LUT3 -> mux2x0 (replace) +set used_c_frag [get_used_c_frag] +if {$used_c_frag > $target_logic} { + puts "PACK: Device overfitted $used_c_frag / $target_logic" + + # Update + set required_frags [expr 2 * ($used_c_frag - $target_logic)] + set used_f_frag [get_used_f_frag] + set free_f_frag [expr $target_logic - $used_f_frag] + + # Try converting LUT3 to mux2x0 + if {$free_f_frag > 0} { + puts "PACK: Replacing at most $free_f_frag LUT3 with mux2x0" + set sel_count [min $required_frags $free_f_frag] + yosys techmap -map $::env(TECHMAP_PATH)/lut3tomux2.v t:LUT3 %R$sel_count + } +} + +# LUT2 -> mux2x0 (replace) +set used_c_frag [get_used_c_frag] +if {$used_c_frag > $target_logic} { + puts "PACK: Device overfitted $used_c_frag / $target_logic" + # Update + set required_frags [expr 2 * ($used_c_frag - $target_logic)] + set used_f_frag [get_used_f_frag] + set free_f_frag [expr $target_logic - $used_f_frag] + # Try converting LUT2 to mux2x0 + if {$free_f_frag > 0} { + puts "PACK: Replacing at most $free_f_frag LUT2 with mux2x0" + set sel_count [min $required_frags $free_f_frag] + yosys techmap -map $::env(TECHMAP_PATH)/lut2tomux2.v t:LUT2 %R$sel_count + } +} + +# Split mux4x0 +set used_c_frag [get_used_c_frag] +if {$used_c_frag > $target_logic} { + puts "PACK: Device overfitted $used_c_frag / $target_logic" + + # Update + set required_frags [expr 2 * ($used_c_frag - $target_logic)] + set used_f_frag [get_used_f_frag] + set free_f_frag [expr $target_logic - $used_f_frag] + + # Try converting mux4x0 to 3x mux2x0 + if {$free_f_frag >= 3} { + puts "PACK: Splitting at most $free_f_frag mux4x0 to 3x mux2x0" + + set sel_count [min $required_frags [expr int(floor($free_f_frag / 3.0))]] + + # If there are not enough mux4x0 then map some LUT2 to them (these are + # actually equivalent) + set mux4_count [get_count -cells t:mux4x0] + if {$mux4_count < $sel_count} { + set map_count [expr $sel_count - $mux4_count] + puts "PACK: Replacing at most $map_count LUT2 with mux4x0" + yosys techmap -map $::env(TECHMAP_PATH)/lut2tomux4.v t:LUT2 %R$map_count + } + + yosys techmap -map $::env(TECHMAP_PATH)/mux4tomux2.v t:mux4x0 %R$sel_count + } +} + +# Split mux8x0 +set used_c_frag [get_used_c_frag] +if {$used_c_frag > $target_logic} { + puts "PACK: Device overfitted $used_c_frag / $target_logic" + + # Update + set required_frags [expr 2 * ($used_c_frag - $target_logic)] + set used_f_frag [get_used_f_frag] + set free_f_frag [expr $target_logic - $used_f_frag] + + # Try converting mux8x0 to 7x mux2x0 + if {$free_f_frag >= 7} { + puts "PACK: Splitting at most $free_f_frag mux8x0 to 7x mux2x0" + set sel_count [min $required_frags [expr int(floor($free_f_frag / 7.0))]] + yosys techmap -map $::env(TECHMAP_PATH)/mux8tomux2.v t:mux8x0 %R$sel_count + } +} + +# Final check +set used_c_frag [get_used_c_frag] +if {$used_c_frag > $target_logic} { + puts "PACK: Device overfitted $used_c_frag / $target_logic. No more optimization possible!" +} + +stat + +# Assing parameters to IO cells basing on constraints and package pinmap +if { $::env(PCF_FILE) != "" && $::env(PINMAP_FILE) != ""} { + quicklogic_iob $::env(PCF_FILE) $::env(PINMAP_FILE) +} + +# Write a pre-mapped design +write_verilog $::env(OUT_SYNTH_V).premap.v + +# Select all logic_0 and logic_1 and apply the techmap to them first. This is +# necessary for constant connection detection in the subsequent techmaps. +select -set consts t:logic_0 t:logic_1 +techmap -map $::env(TECHMAP_PATH)/cells_map.v @consts + +# Map to the VPR cell library +techmap -map $::env(TECHMAP_PATH)/cells_map.v +# Map to the device specific VPR cell library +techmap -map $::env(DEVICE_CELLS_MAP) + +# opt_expr -undriven makes sure all nets are driven, if only by the $undef +# net. +opt_expr -undriven +opt_clean +setundef -zero -params +stat + +# Write output JSON, fixup cell names using an external Python script +write_json $::env(OUT_JSON).org.json +exec $::env(PYTHON3) $::env(UTILS_PATH)/yosys_fixup_cell_names.py $::env(OUT_JSON).org.json $::env(OUT_JSON) + +# Read the fixed JSON back and write verilog +design -reset +read_json $::env(OUT_JSON) +write_verilog $::env(OUT_SYNTH_V) diff --git a/f4pga/wrappers/tcl/ice40/conv.f4pga.tcl b/f4pga/wrappers/tcl/ice40/conv.f4pga.tcl new file mode 100644 index 000000000..12d355fb8 --- /dev/null +++ b/f4pga/wrappers/tcl/ice40/conv.f4pga.tcl @@ -0,0 +1,7 @@ +yosys -import + +# Clean +opt_clean + +# Write EBLIF +write_blif -attr -cname -param $::env(OUT_EBLIF) diff --git a/f4pga/wrappers/tcl/ice40/synth.f4pga.tcl b/f4pga/wrappers/tcl/ice40/synth.f4pga.tcl new file mode 100644 index 000000000..3024c89fc --- /dev/null +++ b/f4pga/wrappers/tcl/ice40/synth.f4pga.tcl @@ -0,0 +1,29 @@ +yosys -import + +proc clean_processes {} { + proc_clean + proc_rmdead + proc_prune + proc_init + proc_arst + proc_mux + proc_dlatch + proc_dff + proc_memwr + proc_clean +} + +synth_ice40 -nocarry + +# opt_expr -undriven makes sure all nets are driven, if only by the $undef +# net. +opt_expr -undriven +opt_clean + +# TODO: remove this as soon as new VTR master+wip is pushed: https://github.com/SymbiFlow/vtr-verilog-to-routing/pull/525 +attrmap -remove hdlname + +setundef -zero -params +clean_processes +write_json $::env(OUT_JSON) +write_verilog $::env(OUT_SYNTH_V) diff --git a/f4pga/wrappers/tcl/qlf_k4n8/conv.f4pga.tcl b/f4pga/wrappers/tcl/qlf_k4n8/conv.f4pga.tcl new file mode 100644 index 000000000..12d355fb8 --- /dev/null +++ b/f4pga/wrappers/tcl/qlf_k4n8/conv.f4pga.tcl @@ -0,0 +1,7 @@ +yosys -import + +# Clean +opt_clean + +# Write EBLIF +write_blif -attr -cname -param $::env(OUT_EBLIF) diff --git a/f4pga/wrappers/tcl/qlf_k4n8/synth.f4pga.tcl b/f4pga/wrappers/tcl/qlf_k4n8/synth.f4pga.tcl new file mode 100644 index 000000000..06c5bfac7 --- /dev/null +++ b/f4pga/wrappers/tcl/qlf_k4n8/synth.f4pga.tcl @@ -0,0 +1,32 @@ +yosys -import + +# Load the QuickLogic qlf_k4n8 support plugin. Note that this is only temporary +# until support for the device is merged into the upstream Yosys +plugin -i ql-qlf +yosys -import + +# Read VPR cells library +read_verilog -lib $::env(TECHMAP_PATH)/cells_sim.v + +# Synthesize +if {[info exists ::env(SYNTH_OPTS)]} { + synth_quicklogic -family qlf_k4n8 $::env(SYNTH_OPTS) +} else { + synth_quicklogic -family qlf_k4n8 +} + +# Write a pre-mapped design +write_verilog $::env(OUT_SYNTH_V).premap.v + +# Map to the VPR cell library +techmap -map $::env(TECHMAP_PATH)/cells_map.v + +# opt_expr -undriven makes sure all nets are driven, if only by the $undef +# net. +opt_expr -undriven +opt_clean + +stat + +write_json $::env(OUT_JSON) +write_verilog $::env(OUT_SYNTH_V) diff --git a/f4pga/wrappers/tcl/xc7/conv.f4pga.tcl b/f4pga/wrappers/tcl/xc7/conv.f4pga.tcl new file mode 100644 index 000000000..e2515e9b9 --- /dev/null +++ b/f4pga/wrappers/tcl/xc7/conv.f4pga.tcl @@ -0,0 +1,20 @@ +yosys -import + +# Clean +opt_clean + +# Designs that directly tie OPAD's to constants cannot use the dedicate +# constant network as an artifact of the way the ROI is configured. +# Until the ROI is removed, enable designs to selectively disable the dedicated +# constant network. +if { [info exists ::env(USE_LUT_CONSTANTS)] } { + write_blif -attr -cname -param \ + $::env(OUT_EBLIF) +} else { + write_blif -attr -cname -param \ + -true VCC VCC \ + -false GND GND \ + -undef VCC VCC \ + $::env(OUT_EBLIF) +} + diff --git a/f4pga/wrappers/tcl/xc7/synth.f4pga.tcl b/f4pga/wrappers/tcl/xc7/synth.f4pga.tcl new file mode 100644 index 000000000..7aa41c651 --- /dev/null +++ b/f4pga/wrappers/tcl/xc7/synth.f4pga.tcl @@ -0,0 +1,245 @@ +yosys -import + +plugin -i xdc +plugin -i fasm +plugin -i params +plugin -i sdc +plugin -i design_introspection + + +# Import the commands from the plugins to the tcl interpreter +yosys -import + + +# Update the CLKOUT[0-5]_PHASE and CLKOUT[0-5]_DUTY_CYCLE parameter values. +# Due to the fact that Yosys doesn't support floating parameter values +# i.e. treats them as strings, the parameter values need to be multiplied by 1000 +# for the PLL registers to have correct values calculated during techmapping. +proc multiply_param { cell param_name multiplier } { + set param_value [getparam $param_name $cell] + if {$param_value ne ""} { + set new_param_value [expr int(round([expr $param_value * $multiplier]))] + setparam -set $param_name $new_param_value $cell + puts "Updated parameter $param_name of cell $cell from $param_value to $new_param_value" + } +} + +proc update_pll_and_mmcm_params {} { + foreach cell [selection_to_tcl_list "t:PLLE2_ADV"] { + multiply_param $cell "CLKFBOUT_PHASE" 1000 + for {set output 0} {$output < 6} {incr output} { + multiply_param $cell "CLKOUT${output}_PHASE" 1000 + multiply_param $cell "CLKOUT${output}_DUTY_CYCLE" 100000 + } + } + + foreach cell [selection_to_tcl_list "t:MMCME2_ADV"] { + multiply_param $cell "CLKFBOUT_PHASE" 1000 + for {set output 0} {$output < 7} {incr output} { + multiply_param $cell "CLKOUT${output}_PHASE" 1000 + multiply_param $cell "CLKOUT${output}_DUTY_CYCLE" 100000 + } + multiply_param $cell "CLKFBOUT_MULT_F" 1000 + multiply_param $cell "CLKOUT0_DIVIDE_F" 1000 + } +} + +proc clean_processes {} { + proc_clean + proc_rmdead + proc_prune + proc_init + proc_arst + proc_mux + proc_dlatch + proc_dff + proc_memwr + proc_clean +} + + +# -flatten is used to ensure that the output eblif has only one module. +# Some of symbiflow expects eblifs with only one module. +# +# To solve the carry chain congestion at the output, the synthesis step +# needs to be executed two times. +# abc9 seems to cause troubles if called multiple times in the flow, therefore +# it gets called only at the last synthesis step +# +# Do not infer IOBs for targets that use a ROI. +if { $::env(USE_ROI) == "TRUE" } { + synth_xilinx -flatten -nosrl -noclkbuf -nodsp -noiopad -nowidelut +} else { + # Read Yosys baseline library first. + read_verilog -lib -specify +/xilinx/cells_sim.v + read_verilog -lib +/xilinx/cells_xtra.v + + # Overwrite some models (e.g. IBUF with more parameters) + read_verilog -lib $::env(TECHMAP_PATH)/iobs.v + + # TODO: This should eventually end up in upstream Yosys + # as models such as FD are not currently supported + # as being used in old FPGAs (e.g. Spartan6) + # Read in unsupported models + read_verilog -lib $::env(TECHMAP_PATH)/retarget.v + + if { [info exists ::env(TOP)] && $::env(TOP) != "" } { + hierarchy -check -top $::env(TOP) + } else { + hierarchy -check -auto-top + } + + # Start flow after library reading + synth_xilinx -flatten -nosrl -noclkbuf -nodsp -iopad -nowidelut -run prepare:check +} + +# Check that post-synthesis cells match libraries. +hierarchy -check + +if { [info exists ::env(INPUT_XDC_FILES)] && $::env(INPUT_XDC_FILES) != "" } { + read_xdc -part_json $::env(PART_JSON) {*}$::env(INPUT_XDC_FILES) + write_fasm -part_json $::env(PART_JSON) $::env(OUT_FASM_EXTRA) + + # Perform clock propagation based on the information from the XDC commands + propagate_clocks +} + +update_pll_and_mmcm_params + +# Write the SDC file +# +# Note that write_sdc and the SDC plugin holds live pointers to RTLIL objects. +# If Yosys mutates those objects (e.g. destroys them), the SDC plugin will +# segfault. +write_sdc -include_propagated_clocks $::env(OUT_SDC) + +write_verilog $::env(OUT_SYNTH_V).premap.v + +# Look for connections OSERDESE2.OQ -> OBUFDS.I. Annotate OBUFDS with a parameter +# indicating that it is connected to an OSERDESE2 +select -set obufds t:OSERDESE2 %co2:+\[OQ,I\] t:OBUFDS t:OBUFTDS %u %i +setparam -set HAS_OSERDES 1 @obufds + +# Map Xilinx tech library to 7-series VPR tech library. +read_verilog -specify -lib $::env(TECHMAP_PATH)/cells_sim.v + +# Convert congested CARRY4 outputs to LUTs. +# +# This is required because VPR cannot reliably resolve SLICE[LM] output +# congestion when both O and CO outputs are used. For this reason if both O +# and CO outputs are used, the CO output is computed using a LUT. +# +# Ideally VPR would resolve the congestion in one of the following ways: +# +# - If either O or CO are registered in a FF, then no output +# congestion exists if the O or CO FF is packed into the same cluster. +# The register output will used the [ABCD]Q output, and the unregistered +# output will used the [ABCD]MUX. +# +# - If neither the O or CO are registered in a FF, then the [ABCD]Q output +# can still be used if the FF is placed into "transparent latch" mode. +# VPR can express this edge, but because using a FF in "transparent latch" +# mode requires running specific CE and SR signals connected to constants, +# VPR cannot easily (or at all) express this packing situation. +# +# VPR's packer in theory could be expanded to express this kind of +# situation. +# +# CLE Row +# +# +--------------------------------------------------------------------------+ +# | | +# | | +# | +---+ | +# | | + | +# | | + | +# | +-------->+ O + | +# | CO CHAIN | | + | +# | | | +---------------------> xMUX +# | ^ | +---->+ CO + | +# | | | | | + | +# | | | | | + | +# | +---------+----------+ | | | + | +# | | | | | +---+ | +# | | CARRY ROW | | | | +# | +--->+ S O +--------+ | xOUTMUX | +# | | | | | | +# | | | + | | +# | +--->+ DI CO +-------+o+--+ | +# | | CI CHAIN | + | | +# | | | | | | +# | +---------+----------+ | | xFFMUX | +# | ^ | | | +# | | | | +---+ | +# | + | | | + | +# | | + | + +-----------+ | +# | +--+o+--->+ O + | | | +# | + | + | xFF | | +# | | | +->--D---- Q +------> xQ +# | | | + | | | +# | +---->+ CO + | | | +# | | + +-----------+ | +# | | + | +# | +---+ | +# | | +# | | +# +--------------------------------------------------------------------------+ +# + +techmap -map $::env(TECHMAP_PATH)/carry_map.v + +clean_processes +write_json $::env(OUT_JSON).carry_fixup.json + +exec $::env(PYTHON3) $::env(UTILS_PATH)/fix_xc7_carry.py < $::env(OUT_JSON).carry_fixup.json > $::env(OUT_JSON).carry_fixup_out.json +design -push +read_json $::env(OUT_JSON).carry_fixup_out.json + +techmap -map $::env(TECHMAP_PATH)/clean_carry_map.v + +# Re-read baseline libraries +read_verilog -lib -specify +/xilinx/cells_sim.v +read_verilog -lib +/xilinx/cells_xtra.v +read_verilog -specify -lib $::env(TECHMAP_PATH)/cells_sim.v +if { $::env(USE_ROI) != "TRUE" } { + read_verilog -lib $::env(TECHMAP_PATH)/iobs.v +} + +# Re-run optimization flow to absorb carry modifications +hierarchy -check + +write_ilang $::env(OUT_JSON).pre_abc9.ilang +if { $::env(USE_ROI) == "TRUE" } { + synth_xilinx -flatten -abc9 -nosrl -noclkbuf -nodsp -noiopad -nowidelut -run map_ffs:check +} else { + synth_xilinx -flatten -abc9 -nosrl -noclkbuf -nodsp -iopad -nowidelut -run map_ffs:check +} + +write_ilang $::env(OUT_JSON).post_abc9.ilang + +# Either the JSON bounce or ABC9 pass causes the CARRY4_VPR CIN/CYINIT pins +# to have 0's when unused. As a result VPR will attempt to route a 0 to those +# ports. However this is not generally possible or desirable. +# +# $::env(TECHMAP_PATH)/cells_map.v has a simple techmap pass where these +# unused ports are removed. In theory yosys's "rmports" would work here, but +# it does not. +chtype -map CARRY4_VPR CARRY4_FIX +techmap -map $::env(TECHMAP_PATH)/cells_map.v + +# opt_expr -undriven makes sure all nets are driven, if only by the $undef +# net. +opt_expr -undriven +opt_clean + +setundef -zero -params +stat + +# TODO: remove this as soon as new VTR master+wip is pushed: https://github.com/SymbiFlow/vtr-verilog-to-routing/pull/525 +attrmap -remove hdlname + +# Write the design in JSON format. +clean_processes +write_json $::env(OUT_JSON) +# Write the design in Verilog format. +write_verilog $::env(OUT_SYNTH_V)