From 051529fe6961a8716e7e779c376442b0cbba6794 Mon Sep 17 00:00:00 2001 From: Dave Keeshan Date: Thu, 19 Jun 2025 13:26:31 +0100 Subject: [PATCH 1/2] Add initial support for gowin --- pyfpga/factory.py | 1 + pyfpga/gowin.py | 66 ++++++++++++++++++++++ pyfpga/templates/gowin-prog.jinja | 15 +++++ pyfpga/templates/gowin.jinja | 94 +++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+) create mode 100644 pyfpga/gowin.py create mode 100644 pyfpga/templates/gowin-prog.jinja create mode 100644 pyfpga/templates/gowin.jinja diff --git a/pyfpga/factory.py b/pyfpga/factory.py index 6da4029a..61111958 100644 --- a/pyfpga/factory.py +++ b/pyfpga/factory.py @@ -25,6 +25,7 @@ 'openflow': Openflow, 'quartus': Quartus, 'vivado': Vivado + 'gowin': Gowin } diff --git a/pyfpga/gowin.py b/pyfpga/gowin.py new file mode 100644 index 00000000..55751f5f --- /dev/null +++ b/pyfpga/gowin.py @@ -0,0 +1,66 @@ +# +# Copyright (C) 2019-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +""" +Implements support for Gowin. +""" + +from pathlib import Path +from pyfpga.project import Project + + +class Gowin(Project): + """Class to support Gowin projects.""" + + def _configure(self): + tool = 'gowin' + command = 'gw_sh' + self.conf['tool'] = tool + self.conf['make_cmd'] = f'{command} {tool}.tcl' + self.conf['make_ext'] = 'tcl' + self.conf['prog_bit'] = ['fs', 'bin'] + self.conf['prog_cmd'] = f'{command} {tool}-prog.tcl' + self.conf['prog_ext'] = 'tcl' + + def _make_custom(self): + if 'part' not in self.data: + self.data['part'] = 'GW2AR-LV18QN88C8/I7' + + def add_param(self, name, value): + """Add a Parameter/Generic Value. + + :param name: parameter/generic name + :type name: str + :param value: parameter/generic value + :type name: str + """ + self.logger.debug('Executing add_param: %s : %s', name, value) + self.data.setdefault('params', {})[name] = value + raise NotImplementedError("Gowin has not implemented Params yet") + + def add_define(self, name, value): + """Add a Verilog Defile Value. + + :param name: define name + :type name: str + :param value: define value + :type name: str + """ + self.logger.debug('Executing add_define: %s : %s', name, value) + self.data.setdefault('defines', {})[name] = value + raise NotImplementedError("Gowin has not implemented defines yet") + + def _get_bitstream(self, bitstream=None): + if not bitstream: + for ext in self.conf['prog_bit']: + candidate = Path(self.odir) / f'{self.data["project"]}/impl/pnr/{self.data["project"]}.{ext}' + print(candidate) + if candidate.is_file(): + bitstream = candidate + break + if not bitstream or not Path(bitstream).is_file(): + raise FileNotFoundError(bitstream) + return self._get_absolute(bitstream, self.conf['prog_ext']) diff --git a/pyfpga/templates/gowin-prog.jinja b/pyfpga/templates/gowin-prog.jinja new file mode 100644 index 00000000..a0da59fe --- /dev/null +++ b/pyfpga/templates/gowin-prog.jinja @@ -0,0 +1,15 @@ +{# +# +# Copyright (C) 2015-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +# if { [ catch { open_hw_manager } ] } { open_hw } +# connect_hw_server +# open_hw_target +# puts [get_hw_devices] +# set obj [lindex [get_hw_devices [current_hw_device]] 0] +# set_property PROGRAM.FILE {{ bitstream }} $obj +# program_hw_devices $obj diff --git a/pyfpga/templates/gowin.jinja b/pyfpga/templates/gowin.jinja new file mode 100644 index 00000000..1244dd5c --- /dev/null +++ b/pyfpga/templates/gowin.jinja @@ -0,0 +1,94 @@ +{# +# +# Copyright (C) 2015-2024 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +#} + +{% if 'cfg' in steps %}# Project configuration ------------------------------------------------------- + +create_project -force -name {{ project }} -dir . -pn {{ part }} -device_version C +# create_project -force -name {{ project }} -dir . +# set_device -device_version C {{ part }} + +{% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} + +{% if files %}# Files inclusion +{% for name, attr in files.items() %} +add_file {{ name }} +{% if 'lib' in attr %}set_property library {{ attr.lib }} [get_files {{ name }}]{% endif %} +{% endfor %} +{% endif %} + +{% if constraints %}# Constraints inclusion +{% for name, attr in constraints.items() %} +add_file {{ name }} +# {% if loop.first %}set_property TARGET_CONSTRS_FILE {{ name }} [current_fileset -constrset]{% endif %} +{% endfor %} +{% endif %} + +{% if top %}# Top-level specification +set_option -top_module {{ top }} +{% endif %} + +{% if includes %}# Verilog Includes +set_option -include_path { {{ includes | join(' ') }} } +{% endif %} +# +# {% if defines %}# Verilog Defines +# set_property VERILOG_DEFINE { {{ defines.items() | map('join', '=') | join(' ') }} } [current_fileset] +# {% endif %} +# +# {% if params %}# Verilog Parameters / VHDL Generics +# set_property GENERIC { {{ params.items() | map('join', '=') | join(' ') }} } -objects [get_filesets sources_1] +# {% endif %} + +{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %} + +run close + +{% endif %} + +{% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow ----------------------------------------------------------------- + +open_project {{ project }}/{{ project }}.gprj + +{% if 'syn' in steps %}# Synthesis + +{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} + +# PRESYNTH +run syn +#report_property [get_runs synth_1] +# if { [get_property STATUS [get_runs synth_1]] ne "synth_design Complete!" } { exit 1 } + +{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} + +{% endif %} + +{% if 'par' in steps %}# Place and Route + +{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %} + +set_option -bit_format txt +run pnr +# #report_property [get_runs impl_1] +# if { [get_property STATUS [get_runs impl_1]] ne "route_design Complete!" } { exit 1 } + +{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} + +{% endif %} + +{% if 'bit' in steps %}# Bitstream generation + +{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %} + +# There is no bit phase in gowin, the bitstream is created at the end of the 'par' phase +{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %} + +{% endif %} + +run close + +{% endif %} From e834d1915354a499fbebd72c84adbedbe908933a Mon Sep 17 00:00:00 2001 From: Dave Keeshan Date: Tue, 8 Sep 2026 15:24:37 +0100 Subject: [PATCH 2/2] Make native Gowin EDA (gw_sh) usable on 1.9.9 and add Tang Nano examples. Wrap -ifdef/-verilog_param/open_project so 1.9.9 still builds, program with programmer_cli or openFPGALoader, and fix the factory TOOLS dict so Gowin is importable. --- README.md | 1 + docs/intro.rst | 2 +- docs/tools.rst | 25 ++++++++++ examples/helpers/gowin.sh | 18 +++++++ examples/projects/gowin.py | 50 ++++++++++++++++++++ examples/sources/cons/tangnano20k/clk.cst | 2 + examples/sources/cons/tangnano20k/led.cst | 2 + examples/sources/cons/tangnano20k/timing.sdc | 1 + pyfpga/factory.py | 3 +- pyfpga/gowin.py | 34 +++---------- pyfpga/helpers/prj2bit.py | 1 + pyfpga/templates/gowin-prog.jinja | 15 +++--- pyfpga/templates/gowin.jinja | 41 ++++++++-------- tests/mocks/gw_sh | 47 ++++++++++++++++++ tests/mocks/openFPGALoader | 9 ++++ tests/mocks/programmer_cli | 9 ++++ tests/regress.sh | 1 + tests/test_tools.py | 8 ++++ 18 files changed, 212 insertions(+), 57 deletions(-) create mode 100755 examples/helpers/gowin.sh create mode 100644 examples/projects/gowin.py create mode 100644 examples/sources/cons/tangnano20k/clk.cst create mode 100644 examples/sources/cons/tangnano20k/led.cst create mode 100644 examples/sources/cons/tangnano20k/timing.sdc create mode 100755 tests/mocks/gw_sh create mode 100755 tests/mocks/openFPGALoader create mode 100755 tests/mocks/programmer_cli diff --git a/README.md b/README.md index 0c7d5f3a..2ab37cf7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # PyFPGA [![License](https://img.shields.io/badge/License-GPL--3.0-darkgreen?style=flat-square)](LICENSE) +![Gowin](https://img.shields.io/badge/Gowin-1.9.11-blue.svg?style=flat-square) ![Diamond](https://img.shields.io/badge/Diamond-3.13-blue.svg?style=flat-square) ![ISE](https://img.shields.io/badge/ISE-14.7-blue.svg?style=flat-square) ![Libero](https://img.shields.io/badge/Libero--Soc-2024.1-blue.svg?style=flat-square) diff --git a/docs/intro.rst b/docs/intro.rst index abc7e945..6180daa4 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -13,7 +13,7 @@ With PyFPGA, you can create your own FPGA development workflow tailored to your * Ensured reproducibility and repeatability. * Lower resource consumption compared to GUI-based workflows. -It currently supports vendor tools such as ``Diamond``, ``Ise``, ``Quartus``, ``Libero``, and ``Vivado``, as well as ``Openflow``, a solution based on *Free/Libre and Open Source Software* (**FLOSS**). +It currently supports vendor tools such as ``Diamond``, ``Gowin``, ``Ise``, ``Quartus``, ``Libero``, and ``Vivado``, as well as ``Openflow``, a solution based on *Free/Libre and Open Source Software* (**FLOSS**). .. ATTENTION:: diff --git a/docs/tools.rst b/docs/tools.rst index 38da3ef3..806f5245 100644 --- a/docs/tools.rst +++ b/docs/tools.rst @@ -12,6 +12,10 @@ Tools - Lattice - LFXP2-5E-5TN144C - device-speed-package + * - Gowin + - Gowin Semiconductor + - GW2AR-LV18QN88C8/I7 + - part * - ISE - Xilinx - XC7K160T-3-FBG484 @@ -48,6 +52,27 @@ Example: prj = Diamond() +Gowin +----- + +`Gowin downloads `_ + +Gowin EDA (``gw_sh``) supports Gowin FPGA families. The default part is +``GW2AR-LV18QN88C8/I7`` (Sipeed Tang Nano 20K). Bitstreams are ``.fs`` files +produced at the end of place-and-route. + +Gowin 1.9.9 has no ``open_project`` / ``-ifdef`` / ``-verilog_param``; pyfpga +runs create+syn+pnr in one ``gw_sh`` invocation and wraps the newer options in +``catch``. Prefer 1.9.11+ when licensed. + +Example: + +.. code:: + + from pyfpga.gowin import Gowin + + prj = Gowin() + ISE --- diff --git a/examples/helpers/gowin.sh b/examples/helpers/gowin.sh new file mode 100755 index 00000000..7d8642a1 --- /dev/null +++ b/examples/helpers/gowin.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e + +HDIR=../../pyfpga/helpers + +python3 $HDIR/hdl2bit.py -t gowin -o results/gowin-vlog -p GW2AR-LV18QN88C8/I7 \ + -i ../sources/vlog/include1 -i ../sources/vlog/include2 \ + -f ../sources/vlog/blink.v -f ../sources/vlog/top.v \ + -f ../sources/cons/tangnano20k/timing.sdc -f ../sources/cons/tangnano20k/clk.cst \ + -f ../sources/cons/tangnano20k/led.cst \ + --define DEFINE1 1 --define DEFINE2 1 --param FREQ 27000000 --param SECS 1 Top + +python3 $HDIR/hdl2bit.py -t gowin -o results/gowin-vhdl -p GW2AR-LV18QN88C8/I7 --project example \ + -f ../sources/vhdl/blink.vhdl,blink_lib -f ../sources/vhdl/blink_pkg.vhdl,blink_lib -f ../sources/vhdl/top.vhdl \ + -f ../sources/cons/tangnano20k/timing.sdc -f ../sources/cons/tangnano20k/clk.cst \ + -f ../sources/cons/tangnano20k/led.cst \ + --param FREQ 27000000 --param SECS 1 --last cfg Top diff --git a/examples/projects/gowin.py b/examples/projects/gowin.py new file mode 100644 index 00000000..db515ba1 --- /dev/null +++ b/examples/projects/gowin.py @@ -0,0 +1,50 @@ +"""Gowin examples.""" + +import argparse + +from pyfpga.gowin import Gowin + + +parser = argparse.ArgumentParser() +parser.add_argument( + '--board', choices=['tangnano20k'], default='tangnano20k' +) +parser.add_argument( + '--source', choices=['vlog', 'vhdl', 'slog'], default='vlog' +) +parser.add_argument( + '--action', choices=['make', 'prog', 'all'], default='make' +) +args = parser.parse_args() + +prj = Gowin(odir=f'results/gowin/{args.source}/{args.board}') + +if args.board == 'tangnano20k': + prj.set_part('GW2AR-LV18QN88C8/I7') + prj.add_param('FREQ', '27000000') + prj.add_cons('../sources/cons/tangnano20k/timing.sdc') + prj.add_cons('../sources/cons/tangnano20k/clk.cst') + prj.add_cons('../sources/cons/tangnano20k/led.cst') +prj.add_param('SECS', '1') + +if args.source == 'vhdl': + prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib') + prj.add_vhdl('../sources/vhdl/top.vhdl') +if args.source == 'vlog': + prj.add_include('../sources/vlog/include1') + prj.add_include('../sources/vlog/include2') + prj.add_vlog('../sources/vlog/*.v') +if args.source == 'slog': + prj.add_include('../sources/slog/include1') + prj.add_include('../sources/slog/include2') + prj.add_slog('../sources/slog/*.sv') +if args.source in ['vlog', 'slog']: + prj.add_define('DEFINE1', '1') + prj.add_define('DEFINE2', '1') + +prj.set_top('Top') + +if args.action in ['make', 'all']: + prj.make() +if args.action in ['prog', 'all']: + prj.prog() diff --git a/examples/sources/cons/tangnano20k/clk.cst b/examples/sources/cons/tangnano20k/clk.cst new file mode 100644 index 00000000..b6c9602b --- /dev/null +++ b/examples/sources/cons/tangnano20k/clk.cst @@ -0,0 +1,2 @@ +IO_LOC "clk_i" 4; +IO_PORT "clk_i" IO_TYPE=LVCMOS33 PULL_MODE=UP BANK_VCCIO=3.3; diff --git a/examples/sources/cons/tangnano20k/led.cst b/examples/sources/cons/tangnano20k/led.cst new file mode 100644 index 00000000..35cea4c4 --- /dev/null +++ b/examples/sources/cons/tangnano20k/led.cst @@ -0,0 +1,2 @@ +IO_LOC "led_o" 15; +IO_PORT "led_o" IO_TYPE=LVCMOS33 PULL_MODE=UP DRIVE=8 BANK_VCCIO=3.3; diff --git a/examples/sources/cons/tangnano20k/timing.sdc b/examples/sources/cons/tangnano20k/timing.sdc new file mode 100644 index 00000000..cb0476f2 --- /dev/null +++ b/examples/sources/cons/tangnano20k/timing.sdc @@ -0,0 +1 @@ +create_clock -name clk_i -period 37.037 -waveform {0 18.518} [get_ports {clk_i}] diff --git a/pyfpga/factory.py b/pyfpga/factory.py index 61111958..5743874f 100644 --- a/pyfpga/factory.py +++ b/pyfpga/factory.py @@ -11,6 +11,7 @@ # pylint: disable=too-few-public-methods from pyfpga.diamond import Diamond +from pyfpga.gowin import Gowin from pyfpga.ise import Ise from pyfpga.libero import Libero from pyfpga.openflow import Openflow @@ -20,12 +21,12 @@ TOOLS = { 'diamond': Diamond, + 'gowin': Gowin, 'ise': Ise, 'libero': Libero, 'openflow': Openflow, 'quartus': Quartus, 'vivado': Vivado - 'gowin': Gowin } diff --git a/pyfpga/gowin.py b/pyfpga/gowin.py index 55751f5f..09097673 100644 --- a/pyfpga/gowin.py +++ b/pyfpga/gowin.py @@ -22,42 +22,20 @@ def _configure(self): self.conf['make_cmd'] = f'{command} {tool}.tcl' self.conf['make_ext'] = 'tcl' self.conf['prog_bit'] = ['fs', 'bin'] - self.conf['prog_cmd'] = f'{command} {tool}-prog.tcl' - self.conf['prog_ext'] = 'tcl' + self.conf['prog_cmd'] = f'bash {tool}-prog.sh' + self.conf['prog_ext'] = 'sh' def _make_custom(self): if 'part' not in self.data: self.data['part'] = 'GW2AR-LV18QN88C8/I7' - def add_param(self, name, value): - """Add a Parameter/Generic Value. - - :param name: parameter/generic name - :type name: str - :param value: parameter/generic value - :type name: str - """ - self.logger.debug('Executing add_param: %s : %s', name, value) - self.data.setdefault('params', {})[name] = value - raise NotImplementedError("Gowin has not implemented Params yet") - - def add_define(self, name, value): - """Add a Verilog Defile Value. - - :param name: define name - :type name: str - :param value: define value - :type name: str - """ - self.logger.debug('Executing add_define: %s : %s', name, value) - self.data.setdefault('defines', {})[name] = value - raise NotImplementedError("Gowin has not implemented defines yet") - def _get_bitstream(self, bitstream=None): if not bitstream: for ext in self.conf['prog_bit']: - candidate = Path(self.odir) / f'{self.data["project"]}/impl/pnr/{self.data["project"]}.{ext}' - print(candidate) + candidate = ( + Path(self.odir) / + f'{self.data["project"]}/impl/pnr/{self.data["project"]}.{ext}' + ) if candidate.is_file(): bitstream = candidate break diff --git a/pyfpga/helpers/prj2bit.py b/pyfpga/helpers/prj2bit.py index 9e4188dc..d8dc64ad 100644 --- a/pyfpga/helpers/prj2bit.py +++ b/pyfpga/helpers/prj2bit.py @@ -54,6 +54,7 @@ def main(): # Detecting a Project file tool_per_ext = { + '.gprj': 'gowin', '.ldf': 'diamond', '.xise': 'ise', '.prjx': 'libero', diff --git a/pyfpga/templates/gowin-prog.jinja b/pyfpga/templates/gowin-prog.jinja index a0da59fe..8f5ac956 100644 --- a/pyfpga/templates/gowin-prog.jinja +++ b/pyfpga/templates/gowin-prog.jinja @@ -6,10 +6,11 @@ # #} -# if { [ catch { open_hw_manager } ] } { open_hw } -# connect_hw_server -# open_hw_target -# puts [get_hw_devices] -# set obj [lindex [get_hw_devices [current_hw_device]] 0] -# set_property PROGRAM.FILE {{ bitstream }} $obj -# program_hw_devices $obj +set -e + +# Prefer Gowin Programmer CLI when loaded; otherwise openFPGALoader. +if command -v programmer_cli >/dev/null 2>&1; then + programmer_cli --fsFile {{ bitstream }} +else + openFPGALoader {{ bitstream }} +fi diff --git a/pyfpga/templates/gowin.jinja b/pyfpga/templates/gowin.jinja index 1244dd5c..4aaf832b 100644 --- a/pyfpga/templates/gowin.jinja +++ b/pyfpga/templates/gowin.jinja @@ -9,8 +9,6 @@ {% if 'cfg' in steps %}# Project configuration ------------------------------------------------------- create_project -force -name {{ project }} -dir . -pn {{ part }} -device_version C -# create_project -force -name {{ project }} -dir . -# set_device -device_version C {{ part }} {% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %} @@ -24,7 +22,6 @@ add_file {{ name }} {% if constraints %}# Constraints inclusion {% for name, attr in constraints.items() %} add_file {{ name }} -# {% if loop.first %}set_property TARGET_CONSTRS_FILE {{ name }} [current_fileset -constrset]{% endif %} {% endfor %} {% endif %} @@ -32,36 +29,42 @@ add_file {{ name }} set_option -top_module {{ top }} {% endif %} +set_option -verilog_std sysv2017 + {% if includes %}# Verilog Includes -set_option -include_path { {{ includes | join(' ') }} } +set_option -include_path { {{- includes | join(';') -}} } {% endif %} -# -# {% if defines %}# Verilog Defines -# set_property VERILOG_DEFINE { {{ defines.items() | map('join', '=') | join(' ') }} } [current_fileset] -# {% endif %} -# -# {% if params %}# Verilog Parameters / VHDL Generics -# set_property GENERIC { {{ params.items() | map('join', '=') | join(' ') }} } -objects [get_filesets sources_1] -# {% endif %} -{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %} +{% if defines %}# Verilog Defines (Gowin 1.9.11+; 1.9.9 has no -ifdef) +if {[catch {set_option -ifdef { {{ defines.items() | map('join', '=') | join(' ') }} }} err]} { + puts "WARNING: set_option -ifdef unsupported: $err" +} +{% endif %} -run close +{% if params %}# Verilog Parameters (Gowin 1.9.11+; 1.9.9 has no -verilog_param) +if {[catch {set_option -verilog_param { {{ params.items() | map('join', '=') | join(' ') }} }} err]} { + puts "WARNING: set_option -verilog_param unsupported: $err" +} +{% endif %} + +{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %} {% endif %} {% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow ----------------------------------------------------------------- -open_project {{ project }}/{{ project }}.gprj +{% if 'cfg' not in steps %} +# 1.9.11+ only. 1.9.9 has no open_project — run cfg+syn+pnr in one gw_sh. +if {[catch {open_project {{ project }}/{{ project }}.gprj} err]} { + puts "WARNING: open_project failed: $err" +} +{% endif %} {% if 'syn' in steps %}# Synthesis {% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %} -# PRESYNTH run syn -#report_property [get_runs synth_1] -# if { [get_property STATUS [get_runs synth_1]] ne "synth_design Complete!" } { exit 1 } {% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %} @@ -73,8 +76,6 @@ run syn set_option -bit_format txt run pnr -# #report_property [get_runs impl_1] -# if { [get_property STATUS [get_runs impl_1]] ne "route_design Complete!" } { exit 1 } {% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %} diff --git a/tests/mocks/gw_sh b/tests/mocks/gw_sh new file mode 100755 index 00000000..fb23dee3 --- /dev/null +++ b/tests/mocks/gw_sh @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2022-2025 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import argparse +import os +import re +import subprocess + + +parser = argparse.ArgumentParser() + +parser.add_argument('source') + +args = parser.parse_args() + +tool = parser.prog + +tcl = f''' +proc unknown args {{ }} + +source {args.source} +''' + +with open(f'{tool}-mock.tcl', 'w', encoding='utf-8') as file: + file.write(tcl) + +subprocess.run( + f'tclsh {tool}-mock.tcl', + shell=True, + check=True, + universal_newlines=True +) + +pattern = r'create_project\s+-force\s+-name\s+(\S+)\s' +with open(args.source, 'r', encoding='utf-8') as file: + match = re.search(pattern, file.read()) + if match: + project = match.group(1) + os.makedirs(f'{project}/impl/pnr', exist_ok=True) + open(f'{project}/impl/pnr/{project}.fs', 'w', encoding='utf-8').close() + +print(f'INFO:the {tool.upper()} mock has been executed') diff --git a/tests/mocks/openFPGALoader b/tests/mocks/openFPGALoader new file mode 100755 index 00000000..ab69f58d --- /dev/null +++ b/tests/mocks/openFPGALoader @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2022-2025 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +print('INFO:the OPENFPGALOADER mock has been executed') diff --git a/tests/mocks/programmer_cli b/tests/mocks/programmer_cli new file mode 100755 index 00000000..c7d334d9 --- /dev/null +++ b/tests/mocks/programmer_cli @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2022-2025 PyFPGA Project +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +print('INFO:the PROGRAMMER_CLI mock has been executed') diff --git a/tests/regress.sh b/tests/regress.sh index 6e4c1186..ec3c3ab1 100644 --- a/tests/regress.sh +++ b/tests/regress.sh @@ -16,6 +16,7 @@ echo "########################################################################## declare -A TOOLS +TOOLS["gowin"]="tangnano20k" TOOLS["diamond"]="brevia2" TOOLS["ise"]="s6micro nexys3" TOOLS["libero"]="maker" diff --git a/tests/test_tools.py b/tests/test_tools.py index a023ea32..e8d31a2f 100644 --- a/tests/test_tools.py +++ b/tests/test_tools.py @@ -14,6 +14,14 @@ def test_diamond(): assert Path(f'{base}-prog.sh').exists(), 'file not found' +def test_gowin(): + tool = 'gowin' + generate(tool, 'GW2AR-LV18QN88C8/I7') + base = f'results/{tool}/{tool}' + assert Path(f'{base}.tcl').exists(), 'file not found' + assert Path(f'{base}-prog.sh').exists(), 'file not found' + + def test_ise(): tool = 'ise' generate(tool, 'DEVICE-PACKAGE-SPEED')