diff --git a/.github/eos-s3_test.json b/.github/eos-s3_test.json index 0967ef4..8838c7d 100644 --- a/.github/eos-s3_test.json +++ b/.github/eos-s3_test.json @@ -1 +1,26 @@ -{} +{ + "default_part": "EOS3FF512-PDN64", + "values": { + "top": "top" + }, + "dependencies": { + "sources": [ + "eos-s3/btn_counter/btn_counter.v" + ], + "synth_log": "synth.log", + "pack_log": "pack.log", + "analysis_log": "analysis.log" + }, + "EOS3FF512-PDN64": { + "default_target": "bitstream", + "dependencies": { + "build_dir": "build/eos-s3", + "pcf": "eos-s3/btn_counter/chandalar.pcf", + "sdc-in": "eos-s3/btn_counter/dummy.sdc" + }, + "values": { + "part": "ql-eos-s3", + "package": "PD64" + } + } +} diff --git a/.github/workflows/Pipeline.yml b/.github/workflows/Pipeline.yml index 9e7cd64..a2c3401 100644 --- a/.github/workflows/Pipeline.yml +++ b/.github/workflows/Pipeline.yml @@ -115,26 +115,15 @@ jobs: if: matrix.flow == 'F4PGA' run: | . ./.github/scripts/activate.sh - cd f4pga-examples + f4pga build --flow ../.github/${{ matrix.fam }}_test.json - # FIXME - # Temporarily allow QL example to fail, until https://github.com/chipsalliance/f4pga/pull/577 is merged. - case '${{ matrix.fam }}' in - eos-s3) - f4pga build --flow ../.github/${{ matrix.fam }}_test.json | echo 'See #577' - ;; - *) - f4pga build --flow ../.github/${{ matrix.fam }}_test.json - ;; - esac - - - name: '📤 Upload artifact: Arty 35 bitstream' - if: matrix.flow == 'F4PGA' && matrix.fam == 'xc7' + - name: '📤 Upload artifact: ${{ matrix.fam }} bitstream' + if: matrix.flow == 'F4PGA' uses: actions/upload-artifact@v3 with: - name: ${{ matrix.flow }}-arty_35-Bitstream-pyF4PGA - path: f4pga-examples/build/arty_35/top.bit + name: ${{ matrix.flow }}-${{ matrix.fam }}-Bitstream + path: f4pga-examples/build/${{ matrix.fam }}/top.bit if-no-files-found: error # SymbiFlow @@ -163,19 +152,19 @@ jobs: esac - name: '📤 Upload artifact: Arty 35 bitstream' - if: matrix.flow == 'SymbiFLow' && matrix.fam == 'xc7' + if: matrix.flow == 'SymbiFlow' && matrix.fam == 'xc7' uses: actions/upload-artifact@v3 with: - name: ${{ matrix.flow }}-arty_35-Bitstream - path: f4pga-examples/xc7/counter_test/build/arty_35/top.bit + name: ${{ matrix.flow }}-${{ matrix.fam }}-Bitstream + path: f4pga-examples/${{ matrix.fam }}/counter_test/build/arty_35/top.bit if-no-files-found: error - name: '📤 Upload artifact: QuickLogic bitstream' - if: matrix.flow == 'SymbiFLow' && matrix.fam == 'eos-s3' + if: matrix.flow == 'SymbiFlow' && matrix.fam == 'eos-s3' uses: actions/upload-artifact@v3 with: - name: ${{ matrix.flow }}-eos-s3-Bitstream - path: f4pga-examples/eos-s3/btn_counter/build/top.bit + name: ${{ matrix.flow }}-${{ matrix.fam }}-Bitstream + path: f4pga-examples/${{ matrix.fam }}/btn_counter/build/top.bit if-no-files-found: error @@ -225,4 +214,4 @@ jobs: - name: 🚧 Test pyF4PGA (PYTHONPATH) run: | PYTHONPATH=$(pwd) python3 f4pga/__init__.py - PYTHONPATH=$(pwd) python3 f4pga/__init__.py -h \ No newline at end of file + PYTHONPATH=$(pwd) python3 f4pga/__init__.py -h diff --git a/.github/xc7_test.json b/.github/xc7_test.json index e1bf212..5d0fa4b 100644 --- a/.github/xc7_test.json +++ b/.github/xc7_test.json @@ -13,7 +13,7 @@ "XC7A35TCSG324-1": { "default_target": "bitstream", "dependencies": { - "build_dir": "build/arty_35", + "build_dir": "build/xc7", "xdc": [ "xc7/counter_test/arty.xdc" ] diff --git a/f4pga/__init__.py b/f4pga/__init__.py index 49eb36c..ee26f47 100755 --- a/f4pga/__init__.py +++ b/f4pga/__init__.py @@ -74,14 +74,13 @@ install_dir = environ.get("F4PGA_INSTALL_DIR", "/usr/local") mypath = str(Path(__file__).resolve().parent) +FPGA_FAM = environ.get('FPGA_FAM', 'xc7') bin_dir_path = \ environ.get('F4PGA_BIN_DIR', str(Path(sys_argv[0]).resolve().parent.parent)) share_dir_path = \ environ.get('F4PGA_SHARE_DIR', - str(Path(f'{install_dir}/xc7/install/share/f4pga').resolve())) -if share_dir_path is None: - share_dir_path = str(Path(f'{install_dir}/xc7/install/share/f4pga').resolve()) + str(Path(f'{install_dir}/{FPGA_FAM}/install/share/f4pga').resolve())) class DependencyNotProducedException(F4PGAException): dep_name: str @@ -532,11 +531,14 @@ def setup_resolution_env(): """ Generate initial values, available in configs. """ - return { - 'prjxray_db': common_sub('prjxray-config').decode().replace('\n', ''), + conf = { 'python3': common_sub('which', 'python3').decode().replace('\n', ''), 'noisyWarnings': _noisy_warnings() } + if (FPGA_FAM == 'xc7'): + conf['prjxray_db'] = common_sub('prjxray-config').decode().replace('\n', '') + + return conf r_env.add_values(_generate_values()) return r_env diff --git a/f4pga/common.py b/f4pga/common.py index 8da06cf..4225a96 100644 --- a/f4pga/common.py +++ b/f4pga/common.py @@ -174,6 +174,8 @@ def vpr(mode: str, vprargs: VprArgs, cwd=None): modeargs = ['--place'] elif mode == 'route': modeargs = ['--route'] + elif mode == 'analysis': + modeargs = ['--analysis'] return sub(*([ 'vpr', diff --git a/f4pga/common_modules/analysis.py b/f4pga/common_modules/analysis.py new file mode 100644 index 0000000..1a324a7 --- /dev/null +++ b/f4pga/common_modules/analysis.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# Copyright (C) 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 +# +# 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. +# +# SPDX-License-Identifier: Apache-2.0 + +from pathlib import Path +from shutil import move as sh_mv + +from f4pga.common import * +from f4pga.module import Module, ModuleContext + + +def analysis_merged_post_implementation_file(ctx: ModuleContext): + return str(Path(ctx.takes.eblif).with_suffix('')) + '_merged_post_implementation.v' + + +def analysis_post_implementation_file(ctx: ModuleContext): + return str(Path(ctx.takes.eblif).with_suffix('')) + '_post_synthesis.v' + + +class analysisModule(Module): + def map_io(self, ctx: ModuleContext): + return { + 'merged_post_implementation_v': analysis_merged_post_implementation_file(ctx), + 'post_implementation_v': analysis_post_implementation_file(ctx) + } + + def execute(self, ctx: ModuleContext): + build_dir = str(Path(ctx.takes.eblif).parent) + + vpr_options = [] + if ctx.values.vpr_options: + vpr_options = options_dict_to_list(ctx.values.vpr_options) + + yield 'Analysis with VPR...' + vpr( + 'analysis', + VprArgs( + ctx.share, + ctx.takes.eblif, + ctx.values, + sdc_file=ctx.takes.sdc + ), + cwd=build_dir + ) + + if ctx.is_output_explicit('merged_post_implementation_v'): + sh_mv(analysis_merged_post_implementation_file(ctx), ctx.outputs.merged_post_implementation_v) + + if ctx.is_output_explicit('post_implementation_v'): + sh_mv(analysis_post_implementation_file(ctx), ctx.outputs.post_implementation_v) + + yield 'Saving log...' + save_vpr_log('analysis.log', build_dir=build_dir) + + def __init__(self, _): + self.name = 'analysis' + self.no_of_phases = 2 + self.takes = [ + 'eblif', + 'route', + 'sdc?' + ] + self.produces = [ + 'merged_post_implementation_v', + 'post_implementation_v', + 'analysis_log' + ] + self.values = [ + 'device', + 'vpr_options?' + ] + vpr_specific_values() + +ModuleClass = analysisModule diff --git a/f4pga/common_modules/synth.py b/f4pga/common_modules/synth.py index b3d1943..0c44995 100755 --- a/f4pga/common_modules/synth.py +++ b/f4pga/common_modules/synth.py @@ -18,6 +18,7 @@ # SPDX-License-Identifier: Apache-2.0 import os +from pathlib import Path from f4pga.common import * from f4pga.module import Module, ModuleContext @@ -97,23 +98,22 @@ class SynthModule(Module): return mapping def execute(self, ctx: ModuleContext): - split_inouts = os.path.join(ctx.share, 'scripts/split_inouts.py') - synth_tcl = os.path.join(ctx.values.tcl_scripts, 'synth.tcl') - conv_tcl = os.path.join(ctx.values.tcl_scripts, 'conv.tcl') - 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(synth_tcl, tcl_env, ctx.takes.sources, + yosys_synth(str(synth_tcl), tcl_env, ctx.takes.sources, ctx.values.read_verilog_args, ctx.outputs.synth_log) yield f'Splitting in/outs...' - sub('python3', split_inouts, '-i', ctx.outputs.json, '-o', + sub('python3', str(split_inouts), '-i', ctx.outputs.json, '-o', ctx.outputs.synth_json) if not os.path.isfile(ctx.produces.fasm_extra): @@ -121,7 +121,7 @@ class SynthModule(Module): f.write('') yield f'Converting...' - yosys_conv(conv_tcl, tcl_env, ctx.outputs.synth_json) + yosys_conv(str(conv_tcl), tcl_env, ctx.outputs.synth_json) def __init__(self, params): self.name = 'synthesize' diff --git a/f4pga/platforms/ql-eos-s3.json b/f4pga/platforms/ql-eos-s3.json index 598003a..78deaf1 100644 --- a/f4pga/platforms/ql-eos-s3.json +++ b/f4pga/platforms/ql-eos-s3.json @@ -2,18 +2,29 @@ "stages": { "mk_build_dir": "common:mkdirs", "synth": "common:synth", + "prepare_sdc": "common:generic_script_wrapper", "pack": "common:pack", "ioplace": "common:generic_script_wrapper", + "place_constraints": "common:generic_script_wrapper", + "iomux_jlink": "common:generic_script_wrapper", + "iomux_openocd": "common:generic_script_wrapper", + "iomux_binary": "common:generic_script_wrapper", "place": "common:place", "route": "common:route", + "analysis": "common:analysis", "fasm": "common:fasm", - "bitstream": "common:generic_script_wrapper" + "bitstream": "common:generic_script_wrapper", + "bitstream_bitheader": "common:generic_script_wrapper", + "bitstream_binary": "common:generic_script_wrapper", + "bitstream_jlink": "common:generic_script_wrapper", + "bitstream_openocd": "common:generic_script_wrapper", + "fasm2bels": "common:generic_script_wrapper" }, "values": { "device": "ql-eos-s3", "device_alt": "ql-eos-s3_wlcsp", - "pinmap": "${shareDir}/arch/ql-eos-s3_wlcsp/pinmap_PD64.csv", + "pinmap": "${shareDir}/arch/ql-eos-s3_wlcsp/pinmap_${package}.csv", "arch_def": "${shareDir}/arch/ql-eos-s3_wlcsp/arch.timing.xml", "rr_graph_lookahead_bin": "${shareDir}/arch/ql-eos-s3_wlcsp/rr_graph_ql-eos-s3_wlcsp.lookahead.bin", "rr_graph_real_bin": "${shareDir}/arch/ql-eos-s3_wlcsp/rr_graph_ql-eos-s3_wlcsp.rr_graph.real.bin", @@ -52,7 +63,9 @@ "synth": { "params": { "takes": [ "pcf?" ], - "produces": [ "synth_v" ] + "produces": [ + "synth_v" + ] }, "values": { "tcl_scripts": "${shareDir}/scripts/pp3", @@ -65,8 +78,67 @@ "TECHMAP_PATH": "${shareDir}/techmaps/pp3", "DEVICE_CELLS_SIM": "${shareDir}/arch/ql-eos-s3_wlcsp/cells/ram_sim.v", "DEVICE_CELLS_MAP": "${shareDir}/arch/ql-eos-s3_wlcsp/cells/ram_map.v", - "PINMAP_FILE": "${shareDir}/arch/ql-eos-s3_wlcsp/pinmap_PD64.csv", - "PCF_FILE": "${:pcf}" + "PINMAP_FILE": "${shareDir}/arch/ql-eos-s3_wlcsp/pinmap_${package}.csv", + "PCF_FILE": "${:pcf}", + "PYTHON3": "${python3}", + "UTILS_PATH": "${shareDir}/scripts" + } + } + }, + "prepare_sdc": { + "params": { + "stage_name": "prepare_sdc", + "interpreter": "${python3}", + "script": "${shareDir}/scripts/process_sdc_constraints.py", + "outputs": { + "sdc": { + "mode": "file", + "file": "${:eblif[noext]}.sdc", + "target": "${:eblif[noext]}.sdc" + } + }, + "inputs": { + "eblif": "${:eblif}", + "sdc-in": "${:sdc-in}", + "sdc-out": "${:eblif[noext]}.sdc", + "pcf": "${:pcf}", + "pin-map": "", + "$PYTHONPATH": "${shareDir}/scripts/" + } + } + }, + "pack": { + "values": { + "device": "ql-eos-s3", + "device_alt": "ql-eos-s3_wlcsp", + "pinmap": "${shareDir}/arch/ql-eos-s3_wlcsp/pinmap_${package}.csv", + "arch_def": "${shareDir}/arch/ql-eos-s3_wlcsp/arch.timing.xml", + "rr_graph_lookahead_bin": "${shareDir}/arch/ql-eos-s3_wlcsp/rr_graph_ql-eos-s3_wlcsp.lookahead.bin", + "rr_graph_real_bin": "${shareDir}/arch/ql-eos-s3_wlcsp/rr_graph_ql-eos-s3_wlcsp.rr_graph.real.bin", + "vpr_place_delay": "${shareDir}/arch/ql-eos-s3_wlcsp/rr_graph_ql-eos-s3_wlcsp.place_delay.bin", + "vpr_grid_layout_name": "ql-eos-s3", + "vpr_options": { + "write_block_usage": "block_usage.json", + "max_router_iterations": 500, + "routing_failure_predictor": "off", + "router_high_fanout_threshold": -1, + "constant_net_method": "route", + "route_chan_width": 100, + "clock_modeling": "route", + "place_delay_model": "delta_override", + "router_lookahead": "extended_map", + "check_route": "quick", + "strict_checks": "off", + "allow_dangling_combinational_nodes": "on", + "disable_errors": "check_unbuffered_edges:check_route", + "congested_routing_iteration_threshold": "0.8", + "incremental_reroute_delay_ripup": "off", + "base_cost_type": "delay_normalized_length_bounded", + "bb_factor": "10", + "initial_pres_fac": "4.0", + "check_rr_graph": "off", + "pack_high_fanout_threshold": "PB-lOGIC:18", + "suppress_warnings": "${noisyWarnings},sum_pin_class:check_unbuffered_edges:load_rr_indexed_data_T_values:check_rr_node:trans_per_R:check_route:set_rr_graph_tool_comment " } } }, @@ -74,7 +146,7 @@ "params": { "stage_name": "ioplace", "interpreter": "${python3}", - "script": "${binDir}/python/ql_pp3_create_ioplace.py", + "script": "${shareDir}/scripts/pp3_create_ioplace.py", "outputs": { "io_place": { "mode": "stdout", @@ -85,8 +157,161 @@ "blif": "${:eblif}", "net": "${:net}", "pcf": "${:pcf}", - "map": "${shareDir}/arch/ql-eos-s3_wlcsp/pinmap_PD64.csv", - "$PYTHONPATH": "${binDir}/python/" + "map": "${shareDir}/arch/ql-eos-s3_wlcsp/pinmap_${package}.csv", + "$PYTHONPATH": "${shareDir}/scripts/" + } + } + }, + "place_constraints": { + "params": { + "stage_name": "place_constraints", + "interpreter": "${python3}", + "script": "${shareDir}/scripts/pp3_create_place_constraints.py", + "outputs": { + "place_constraints": { + "mode": "stdout", + "target": "${:eblif[noext]}_constraints.place" + } + }, + "inputs": { + "blif": "${:eblif}", + "map": "${shareDir}/arch/ql-eos-s3_wlcsp/clkmap_${package}.csv", + "i": "${:io_place}", + "$PYTHONPATH": "${shareDir}/scripts/" + } + } + }, + "iomux_jlink": { + "params": { + "stage_name": "iomux_jlink", + "interpreter": "${python3}", + "script": "${shareDir}/scripts/pp3_eos_s3_iomux_config.py", + "outputs": { + "iomux_jlink": { + "mode": "stdout", + "target": "${:eblif[noext]}_iomux.jlink" + } + }, + "inputs": { + "eblif": "${:eblif}", + "pcf": "${:pcf}", + "map": "${shareDir}/arch/ql-eos-s3_wlcsp/pinmap_${package}.csv", + "output-format": "jlink", + "$PYTHONPATH": "${shareDir}/scripts/" + } + } + }, + "iomux_openocd": { + "params": { + "stage_name": "iomux_openocd", + "interpreter": "${python3}", + "script": "${shareDir}/scripts/pp3_eos_s3_iomux_config.py", + "outputs": { + "iomux_openocd": { + "mode": "stdout", + "target": "${:eblif[noext]}_iomux.openocd" + } + }, + "inputs": { + "eblif": "${:eblif}", + "pcf": "${:pcf}", + "map": "${shareDir}/arch/ql-eos-s3_wlcsp/pinmap_${package}.csv", + "output-format": "openocd", + "$PYTHONPATH": "${shareDir}/scripts/" + } + } + }, + "iomux_binary": { + "params": { + "stage_name": "iomux_binary", + "interpreter": "${python3}", + "script": "${shareDir}/scripts/pp3_eos_s3_iomux_config.py", + "outputs": { + "iomux_binary": { + "mode": "stdout", + "target": "${:eblif[noext]}_iomux.bin" + } + }, + "inputs": { + "eblif": "${:eblif}", + "pcf": "${:pcf}", + "map": "${shareDir}/arch/ql-eos-s3_wlcsp/pinmap_${package}.csv", + "output-format": "binary", + "$PYTHONPATH": "${shareDir}/scripts/" + } + } + }, + "route": { + "values": { + "device": "ql-eos-s3", + "device_alt": "ql-eos-s3_wlcsp", + "pinmap": "${shareDir}/arch/ql-eos-s3_wlcsp/pinmap_${package}.csv", + "arch_def": "${shareDir}/arch/ql-eos-s3_wlcsp/arch.timing.xml", + "rr_graph_lookahead_bin": "${shareDir}/arch/ql-eos-s3_wlcsp/rr_graph_ql-eos-s3_wlcsp.lookahead.bin", + "rr_graph_real_bin": "${shareDir}/arch/ql-eos-s3_wlcsp/rr_graph_ql-eos-s3_wlcsp.rr_graph.real.bin", + "vpr_place_delay": "${shareDir}/arch/ql-eos-s3_wlcsp/rr_graph_ql-eos-s3_wlcsp.place_delay.bin", + "vpr_grid_layout_name": "ql-eos-s3", + "vpr_options": { + "write_timing_summary": "timing_summary.json", + "max_router_iterations": 500, + "routing_failure_predictor": "off", + "router_high_fanout_threshold": -1, + "constant_net_method": "route", + "route_chan_width": 100, + "clock_modeling": "route", + "place_delay_model": "delta_override", + "router_lookahead": "extended_map", + "check_route": "quick", + "strict_checks": "off", + "allow_dangling_combinational_nodes": "on", + "disable_errors": "check_unbuffered_edges:check_route", + "congested_routing_iteration_threshold": "0.8", + "incremental_reroute_delay_ripup": "off", + "base_cost_type": "delay_normalized_length_bounded", + "bb_factor": "10", + "initial_pres_fac": "4.0", + "check_rr_graph": "off", + "pack_high_fanout_threshold": "PB-lOGIC:18", + "suppress_warnings": "${noisyWarnings},sum_pin_class:check_unbuffered_edges:load_rr_indexed_data_T_values:check_rr_node:trans_per_R:check_route:set_rr_graph_tool_comment " + } + } + }, + "analysis": { + "values": { + "device": "ql-eos-s3", + "device_alt": "ql-eos-s3_wlcsp", + "pinmap": "${shareDir}/arch/ql-eos-s3_wlcsp/pinmap_${package}.csv", + "arch_def": "${shareDir}/arch/ql-eos-s3_wlcsp/arch.timing.xml", + "rr_graph_lookahead_bin": "${shareDir}/arch/ql-eos-s3_wlcsp/rr_graph_ql-eos-s3_wlcsp.lookahead.bin", + "rr_graph_real_bin": "${shareDir}/arch/ql-eos-s3_wlcsp/rr_graph_ql-eos-s3_wlcsp.rr_graph.real.bin", + "vpr_place_delay": "${shareDir}/arch/ql-eos-s3_wlcsp/rr_graph_ql-eos-s3_wlcsp.place_delay.bin", + "vpr_grid_layout_name": "ql-eos-s3", + "vpr_options": { + "gen_post_synthesis_netlist": "on", + "gen_post_implementation_merged_netlist": "on", + "post_synth_netlist_unconn_inputs": "nets", + "post_synth_netlist_unconn_outputs": "nets", + "verify_file_digests": "off", + "max_router_iterations": 500, + "routing_failure_predictor": "off", + "router_high_fanout_threshold": -1, + "constant_net_method": "route", + "route_chan_width": 100, + "clock_modeling": "route", + "place_delay_model": "delta_override", + "router_lookahead": "extended_map", + "check_route": "quick", + "strict_checks": "off", + "allow_dangling_combinational_nodes": "on", + "disable_errors": "check_unbuffered_edges:check_route", + "congested_routing_iteration_threshold": "0.8", + "incremental_reroute_delay_ripup": "off", + "base_cost_type": "delay_normalized_length_bounded", + "bb_factor": "10", + "initial_pres_fac": "4.0", + "check_rr_graph": "off", + "pack_high_fanout_threshold": "PB-lOGIC:18", + "suppress_warnings": "${noisyWarnings},sum_pin_class:check_unbuffered_edges:load_rr_indexed_data_T_values:check_rr_node:trans_per_R:check_route:set_rr_graph_tool_comment " } } }, @@ -97,24 +322,152 @@ "outputs": { "bitstream": { "mode": "file", - "file": "bitstream-${device}.bit", - "target": "${build_dir?}/bitstream-${device}.bit" + "file": "${:eblif[noext]}.bit", + "target": "${:eblif[noext]}.bit" }, "bitstream_log": { "mode": "stdout", - "target": "${build_dir?}/bitstream-${device}.log" + "target": "${:eblif[noext]}.bit.log" } }, "inputs": { "#1": "${:fasm}", - "#2": "bitstream-${device}.bit", - "dev-type": "ql-eos-s3", - "db-root": "${shareDir}/fasm_database/pp3" + "#2": "${:eblif[noext]}.bit", + "dev-type": "ql-eos-s3" } }, "values": { - "build_dir?": "." + "build_dir": "." + } + }, + "bitstream_bitheader": { + "params": { + "stage_name": "bitstream_bitheader", + "script": "symbiflow_write_bitheader", + "outputs": { + "bitstream_bitheader": { + "mode": "file", + "file": "${:bitstream}.h", + "target": "${:bitstream}.h" + }, + "bitstream_bitheader_log": { + "mode": "stdout", + "target": "${:bitstream}.h.log" + } + }, + "inputs": { + "#1": "${:bitstream}", + "#2": "${:bitstream}.h", + "#3": "${:iomux_binary}" + } + } + }, + "bitstream_jlink": { + "params": { + "stage_name": "bitstream_jlink", + "script": "symbiflow_write_jlink", + "outputs": { + "bitstream_jlink": { + "mode": "file", + "file": "${:bitstream}.jlink", + "target": "${:bitstream}.jlink" + }, + "bitstream_jlink_log": { + "mode": "stdout", + "target": "${:bitstream}.jlink.log" + } + }, + "inputs": { + "#1": "${:bitstream}", + "#2": "${:bitstream}.jlink", + "#3": "${:iomux_jlink}" + } + } + }, + "bitstream_openocd": { + "params": { + "stage_name": "bitstream_openocd", + "script": "symbiflow_write_openocd", + "outputs": { + "bitstream_openocd": { + "mode": "file", + "file": "${:bitstream}.openocd", + "target": "${:bitstream}.openocd" + }, + "bitstream_openocd_log": { + "mode": "stdout", + "target": "${:bitstream}.openocd.log" + } + }, + "inputs": { + "#1": "${:bitstream}", + "#2": "${:bitstream}.openocd", + "#3": "${:iomux_openocd}" + } + } + }, + "bitstream_binary": { + "params": { + "stage_name": "bitstream_binary", + "script": "symbiflow_write_binary", + "outputs": { + "bitstream_binary": { + "mode": "file", + "file": "${:bitstream}.bin", + "target": "${:bitstream}.bin" + }, + "bitstream_binary_log": { + "mode": "stdout", + "target": "${:bitstream}.bin.log" + } + }, + "inputs": { + "#1": "${:bitstream}", + "#2": "${:bitstream}.bin", + "#3": "${:iomux_binary}" + } + } + }, + "fasm2bels": { + "params": { + "stage_name": "fasm2bels", + "script": "symbiflow_fasm2bels", + "outputs": { + "fasm2bels_verilog": { + "mode": "file", + "file": "${:bitstream}.v", + "target": "${:bitstream}.v" + }, + "fasm2bels_pcf": { + "mode": "file", + "file": "${:bitstream}.pcf", + "target": "${:bitstream}.pcf" + }, + "fasm2bels_qcf": { + "mode": "file", + "file": "${:bitstream}.qcf", + "target": "${:bitstream}.qcf" + }, + "fasm2bels_log": { + "mode": "stdout", + "target": "${:bitstream}.log" + } + }, + "inputs": { + "device": "${device}", + "part": "${package}", + "pcf": "${:pcf}", + "bit": "${:bitstream}", + "out-verilog": "${:bitstream}.v", + "out-pcf": "${:bitstream}.pcf", + "out-qcf": "${:bitstream}.qcf", + "$F4PGA_INSTALL_DIR": "${shareDir}/../../../../", + "$FPGA_FAM": "eos-s3", + "$PATH": "${shareDir}/../../../conda/envs/eos-s3/bin/:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "$SHARE_DIR_PATH": "${shareDir}", + "$BIN_DIR_PATH": "${binDir}" + } } } } -} \ No newline at end of file +} diff --git a/f4pga/setup.py b/f4pga/setup.py index 21d20d8..450eb2c 100644 --- a/f4pga/setup.py +++ b/f4pga/setup.py @@ -72,6 +72,10 @@ wrapper_entrypoints = [ f"{sf}_generate_constraints = {shwrappers}:generate_constraints", f"{sf}_analysis = {shwrappers}:analysis", f"{sf}_fasm2bels = {shwrappers}:fasm2bels", + f"{sf}_write_binary = {shwrappers}:write_binary", + f"{sf}_write_bitheader = {shwrappers}:write_bitheader", + f"{sf}_write_jlink = {shwrappers}:write_jlink", + f"{sf}_write_openocd = {shwrappers}:write_openocd", f"ql_{sf} = {shwrappers}:ql", f"vpr_common = {shwrappers}:vpr_common", ] diff --git a/f4pga/wrappers/sh/__init__.py b/f4pga/wrappers/sh/__init__.py index 016c7b3..e6d6e35 100644 --- a/f4pga/wrappers/sh/__init__.py +++ b/f4pga/wrappers/sh/__init__.py @@ -120,3 +120,19 @@ def ql(): def fasm2bels(): print("[F4PGA] Running (deprecated) fasm2bels") run_sh(ROOT / "quicklogic/fasm2bels.f4pga.sh") + +def write_bitheader(): + print("[F4PGA] Running (deprecated) write bitheader") + run_sh(ROOT / "quicklogic/write_bitheader.f4pga.sh") + +def write_binary(): + print("[F4PGA] Running (deprecated) write binary") + run_sh(ROOT / "quicklogic/write_binary.f4pga.sh") + +def write_jlink(): + print("[F4PGA] Running (deprecated) write jlink") + run_sh(ROOT / "quicklogic/write_jlink.f4pga.sh") + +def write_openocd(): + print("[F4PGA] Running (deprecated) write openocd") + run_sh(ROOT / "quicklogic/write_openocd.f4pga.sh") diff --git a/f4pga/wrappers/sh/quicklogic/fasm2bels.f4pga.sh b/f4pga/wrappers/sh/quicklogic/fasm2bels.f4pga.sh index 60767e4..c48707c 100755 --- a/f4pga/wrappers/sh/quicklogic/fasm2bels.f4pga.sh +++ b/f4pga/wrappers/sh/quicklogic/fasm2bels.f4pga.sh @@ -21,8 +21,8 @@ set -e SHARE_DIR_PATH=${SHARE_DIR_PATH:="$F4PGA_SHARE_DIR"} BIN_DIR_PATH=${BIN_DIR_PATH:="$F4PGA_BIN_DIR"} -OPTS=d:P:p:b: -LONGOPTS=device:,part:,pcf:,bit:, +OPTS=d:P:p:b:v:o:q +LONGOPTS=device:,part:,pcf:,bit:,out-verilog:,out-pcf:,out-qcf:, PARSED_OPTS=`getopt --options=${OPTS} --longoptions=${LONGOPTS} --name $0 -- "$@"` eval set -- "${PARSED_OPTS}" @@ -31,6 +31,9 @@ DEVICE="" PART="" PCF="" BIT="" +OUT_VERILOG="" +OUT_PCF="" +OUT_QCF="" while true; do case "$1" in @@ -50,6 +53,18 @@ while true; do BIT=$2 shift 2 ;; + -v|--out-verilog) + OUT_VERILOG=$2 + shift 2 + ;; + -o|--out-pcf) + OUT_PCF=$2 + shift 2 + ;; + -q|--out-qcf) + OUT_QCF=$2 + shift 2 + ;; --) break ;; @@ -67,27 +82,26 @@ if [ -z $BIT ]; then fi -# Run fasm2bels -if [[ "$DEVICE" =~ ^(ql-eos-s3|ql-pp3e)$ ]]; then - - VPR_DB=`readlink -f ${SHARE_DIR_PATH}/arch/${DEVICE}_wlcsp/db_phy.pickle` - FASM2BELS=`readlink -f ${SHARE_DIR_PATH}/scripts/fasm2bels.py` - FASM2BELS_DEVICE=${DEVICE/ql-/} - VERILOG_FILE="${BIT}.v" - PCF_FILE="${BIT}.v.pcf" - QCF_FILE="${BIT}.v.qcf" - - if [ ! -z "{PCF}" ]; then - PCF_ARGS="--input-pcf ${PCF}" - else - PCF_ARGS="" - fi - - echo "Running fasm2bels" - `which python3` ${FASM2BELS} ${BIT} --phy-db ${VPR_DB} --device-name ${FASM2BELS_DEVICE} --package-name ${PART} --input-type bitstream --output-verilog ${VERILOG_FILE} ${PCF_ARGS} --output-pcf ${PCF_FILE} --output-qcf ${QCF_FILE} - -else - +# $DEVICE is not ql-eos-s3 or ql-pp3e +if ! [[ "$DEVICE" =~ ^(ql-eos-s3|ql-pp3e)$ ]]; then echo "ERROR: Unsupported device '${DEVICE}' for fasm2bels" exit -1 fi + +# Run fasm2bels +VPR_DB=`readlink -f ${SHARE_DIR_PATH}/arch/${DEVICE}_wlcsp/db_phy.pickle` +FASM2BELS=`readlink -f ${SHARE_DIR_PATH}/scripts/fasm2bels.py` +FASM2BELS_DEVICE=${DEVICE/ql-/} + +VERILOG_FILE="${OUT_VERILOG:-$BIT.v}" +PCF_FILE="${OUT_PCF:-$BIT.v.pcf}" +QCF_FILE="${OUT_QCF:-$BIT.v.qcf}" + +if [ ! -z "{PCF}" ]; then + PCF_ARGS="--input-pcf ${PCF}" +else + PCF_ARGS="" +fi + +echo "Running fasm2bels" +`which python3` ${FASM2BELS} ${BIT} --phy-db ${VPR_DB} --device-name ${FASM2BELS_DEVICE} --package-name ${PART} --input-type bitstream --output-verilog ${VERILOG_FILE} ${PCF_ARGS} --output-pcf ${PCF_FILE} --output-qcf ${QCF_FILE} diff --git a/f4pga/wrappers/sh/quicklogic/write_binary.f4pga.sh b/f4pga/wrappers/sh/quicklogic/write_binary.f4pga.sh old mode 100644 new mode 100755 index 28bdfcf..d5ebeb3 --- a/f4pga/wrappers/sh/quicklogic/write_binary.f4pga.sh +++ b/f4pga/wrappers/sh/quicklogic/write_binary.f4pga.sh @@ -18,7 +18,5 @@ set -e -source $(dirname "$(readlink -f "$BASH_SOURCE")")/env - echo "Converting bitstream to flashable binary format" `which python3` -m quicklogic_fasm.bitstream_to_binary $@ diff --git a/f4pga/wrappers/sh/quicklogic/write_bitheader.f4pga.sh b/f4pga/wrappers/sh/quicklogic/write_bitheader.f4pga.sh old mode 100644 new mode 100755 index 2fa8a01..f0d6eef --- a/f4pga/wrappers/sh/quicklogic/write_bitheader.f4pga.sh +++ b/f4pga/wrappers/sh/quicklogic/write_bitheader.f4pga.sh @@ -18,7 +18,5 @@ set -e -source $(dirname "$(readlink -f "$BASH_SOURCE")")/env - echo "Converting bitstream to C Header" `which python3` -m quicklogic_fasm.bitstream_to_header $@ diff --git a/f4pga/wrappers/sh/quicklogic/write_jlink.f4pga.sh b/f4pga/wrappers/sh/quicklogic/write_jlink.f4pga.sh old mode 100644 new mode 100755 index 2e0c425..6c85fb5 --- a/f4pga/wrappers/sh/quicklogic/write_jlink.f4pga.sh +++ b/f4pga/wrappers/sh/quicklogic/write_jlink.f4pga.sh @@ -18,7 +18,5 @@ set -e -source $(dirname "$(readlink -f "$BASH_SOURCE")")/env - echo "Converting bitstream to JLink script" `which python3` -m quicklogic_fasm.bitstream_to_jlink $@ diff --git a/f4pga/wrappers/sh/quicklogic/write_openocd.f4pga.sh b/f4pga/wrappers/sh/quicklogic/write_openocd.f4pga.sh old mode 100644 new mode 100755 index 52bec54..345fb6b --- a/f4pga/wrappers/sh/quicklogic/write_openocd.f4pga.sh +++ b/f4pga/wrappers/sh/quicklogic/write_openocd.f4pga.sh @@ -18,7 +18,5 @@ set -e -source $(dirname "$(readlink -f "$BASH_SOURCE")")/env - echo "Converting bitstream to OpenOCD script" `which python3` -m quicklogic_fasm.bitstream_to_openocd $@