Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
2 changes: 1 addition & 1 deletion docs/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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::

Expand Down
25 changes: 25 additions & 0 deletions docs/tools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ Tools
- Lattice
- LFXP2-5E-5TN144C
- device-speed-package
* - Gowin
- Gowin Semiconductor
- GW2AR-LV18QN88C8/I7
- part
* - ISE
- Xilinx
- XC7K160T-3-FBG484
Expand Down Expand Up @@ -48,6 +52,27 @@ Example:

prj = Diamond()

Gowin
-----

`Gowin downloads <https://www.gowinsemi.com/en/support/download_eda/>`_

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
---

Expand Down
18 changes: 18 additions & 0 deletions examples/helpers/gowin.sh
Original file line number Diff line number Diff line change
@@ -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
50 changes: 50 additions & 0 deletions examples/projects/gowin.py
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 2 additions & 0 deletions examples/sources/cons/tangnano20k/clk.cst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
IO_LOC "clk_i" 4;
IO_PORT "clk_i" IO_TYPE=LVCMOS33 PULL_MODE=UP BANK_VCCIO=3.3;
2 changes: 2 additions & 0 deletions examples/sources/cons/tangnano20k/led.cst
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions examples/sources/cons/tangnano20k/timing.sdc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
create_clock -name clk_i -period 37.037 -waveform {0 18.518} [get_ports {clk_i}]
2 changes: 2 additions & 0 deletions pyfpga/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -20,6 +21,7 @@

TOOLS = {
'diamond': Diamond,
'gowin': Gowin,
'ise': Ise,
'libero': Libero,
'openflow': Openflow,
Expand Down
44 changes: 44 additions & 0 deletions pyfpga/gowin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#
# 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'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 _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}'
)
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'])
1 change: 1 addition & 0 deletions pyfpga/helpers/prj2bit.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def main():
# Detecting a Project file

tool_per_ext = {
'.gprj': 'gowin',
'.ldf': 'diamond',
'.xise': 'ise',
'.prjx': 'libero',
Expand Down
16 changes: 16 additions & 0 deletions pyfpga/templates/gowin-prog.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{#
#
# Copyright (C) 2015-2024 PyFPGA Project
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
#}

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
95 changes: 95 additions & 0 deletions pyfpga/templates/gowin.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
{#
#
# 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

{% 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 }}
{% endfor %}
{% endif %}

{% if top %}# Top-level specification
set_option -top_module {{ top }}
{% endif %}

set_option -verilog_std sysv2017

{% if includes %}# Verilog Includes
set_option -include_path { {{- includes | join(';') -}} }
{% 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 %}

{% 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 -----------------------------------------------------------------

{% 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 %}

run syn

{% 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

{% 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 %}
47 changes: 47 additions & 0 deletions tests/mocks/gw_sh
Original file line number Diff line number Diff line change
@@ -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')
9 changes: 9 additions & 0 deletions tests/mocks/openFPGALoader
Original file line number Diff line number Diff line change
@@ -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')
9 changes: 9 additions & 0 deletions tests/mocks/programmer_cli
Original file line number Diff line number Diff line change
@@ -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')
1 change: 1 addition & 0 deletions tests/regress.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ echo "##########################################################################

declare -A TOOLS

TOOLS["gowin"]="tangnano20k"
TOOLS["diamond"]="brevia2"
TOOLS["ise"]="s6micro nexys3"
TOOLS["libero"]="maker"
Expand Down
Loading