Skip to content

Commit

Permalink
Implement support for Lattice Diamond
Browse files Browse the repository at this point in the history
  • Loading branch information
m42uko committed Aug 12, 2024
1 parent cc89caf commit fe6937a
Show file tree
Hide file tree
Showing 11 changed files with 312 additions and 0 deletions.
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)

![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)
![Quartus](https://img.shields.io/badge/Quartus--Prime-23.1-blue.svg?style=flat-square)
Expand Down
52 changes: 52 additions & 0 deletions examples/hooks/diamond.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Diamond example hooks."""

from pyfpga.diamond import Diamond

prj = Diamond(odir='../build/diamond')

hooks = {
"reports": """
prj_run Map -task MapTrace -forceOne
prj_run PAR -task PARTrace -forceOne
prj_run PAR -task IOTiming -forceOne
""",

"netlist_simulation": """
prj_run Map -task MapVerilogSimFile
prj_run Map -task MapVHDLSimFile -forceOne
prj_run Export -task TimingSimFileVHD -forceOne
prj_run Export -task TimingSimFileVlg -forceOne
prj_run Export -task IBIS -forceOne
""",

"progfile_ecp5u": """
prj_run Export -task Promgen -forceOne
""",

"progfile_machxo2": """
prj_run Export -task Jedecgen -forceOne
"""
}

prj.set_part('LFXP2-5E-5TN144C')

prj.add_param('FREQ', '50000000')
prj.add_param('SECS', '1')

prj.add_cons('../sources/cons/brevia2/clk.lpf', 'syn')
prj.add_cons('../sources/cons/brevia2/clk.lpf', 'par')
prj.add_cons('../sources/cons/brevia2/io.lpf', 'par')

prj.add_include('../sources/vlog/include1')
prj.add_include('../sources/vlog/include2')
prj.add_vlog('../sources/vlog/*.v')

prj.add_define('DEFINE1', '1')
prj.add_define('DEFINE2', '1')

prj.set_top('Top')

for hook_name, hook in hooks.items():
prj.add_hook('postpar', hook)

prj.make()
51 changes: 51 additions & 0 deletions examples/projects/diamond.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Diamond examples."""

import argparse

from pyfpga.diamond import Diamond


parser = argparse.ArgumentParser()
parser.add_argument(
'--board', choices=['brevia2'], default='brevia2'
)
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 = Diamond(odir='../build/diamond')

if args.board == 'brevia2':
prj.set_part('LFXP2-5E-5TN144C')
prj.add_param('FREQ', '50000000')
prj.add_cons('../sources/cons/brevia2/clk.lpf', 'syn')
prj.add_cons('../sources/cons/brevia2/clk.lpf', 'par')
prj.add_cons('../sources/cons/brevia2/io.lpf', 'par')
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()
3 changes: 3 additions & 0 deletions examples/sources/cons/brevia2/clk.lpf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
BLOCK RESETPATHS ;
BLOCK ASYNCPATHS ;
FREQUENCY NET "clk_i_c" 50.000000 MHz ;
4 changes: 4 additions & 0 deletions examples/sources/cons/brevia2/io.lpf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
LOCATE COMP "clk_i" SITE "21" ;
IOBUF PORT "clk_i" IO_TYPE=LVCMOS33 ;
LOCATE COMP "led_o" SITE "37" ;
IOBUF PORT "led_o" IO_TYPE=LVCMOS33 ;
30 changes: 30 additions & 0 deletions pyfpga/diamond.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#
# Copyright (C) 2024 PyFPGA Project
#
# SPDX-License-Identifier: GPL-3.0-or-later
#

"""
Implements support for Diamond.
"""

import os
from pyfpga.project import Project


class Diamond(Project):
"""Class to support Diamond projects."""

def _configure(self):
tool = 'diamond'
executable = 'pnmainc' if os.name == 'nt' else 'diamondc'
self.conf['tool'] = tool
self.conf['make_cmd'] = f'{executable} {tool}.tcl'
self.conf['make_ext'] = 'tcl'
self.conf['prog_bit'] = 'bit'
self.conf['prog_cmd'] = f'sh {tool}-prog.sh'
self.conf['prog_ext'] = 'sh'

def _make_custom(self):
if 'part' not in self.data:
self.data['part'] = 'LFXP2-5E-5TN144C'
2 changes: 2 additions & 0 deletions pyfpga/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

# pylint: disable=too-few-public-methods

from pyfpga.diamond import Diamond
from pyfpga.ise import Ise
from pyfpga.libero import Libero
from pyfpga.openflow import Openflow
Expand All @@ -18,6 +19,7 @@


TOOLS = {
'diamond': Diamond,
'ise': Ise,
'libero': Libero,
'openflow': Openflow,
Expand Down
19 changes: 19 additions & 0 deletions pyfpga/templates/diamond-prog.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{#
# Copyright (C) 2024 PyFPGA Project
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
#}

if [ "$DIAMOND_XCF" == "" ]; then
DIAMOND_XCF=impl1/impl1.xcf
fi

if [ -f "$DIAMOND_XCF" ]; then
pgrcmd -infile $DIAMOND_XCF
else
echo "ERROR: Automatic programming with Diamond is not yet supported."
echo " Please create the `realpath $DIAMOND_XCF` file manually and rerun the prog command."
echo " Hint: You can change the location of the XCF file by setting the DIAMOND_XCF environment variable."
exit 1
fi
105 changes: 105 additions & 0 deletions pyfpga/templates/diamond.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{#
#
# Copyright (C) 2015-2024 PyFPGA Project
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
#}

{% if 'cfg' in steps %}# Project configuration -------------------------------------------------------

prj_project new -name {{ project }} -dev {{ part }}

# For now, let's enforce Synplify as LSE (the default) has broken top level generic handling
prj_syn set synplify

{% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %}

{% if files %}# Files inclusion
{% for name, attr in files.items() %}
prj_src add {% if 'lib' in attr %}-work {{ attr.lib }}{% else %}{% endif %} {{ name }}
{% endfor %}
{% endif %}

{% if constraints %}
# Constraints inclusion
# Diamond only supports one constraints file, so we need to combine them into the default diamond.lpf.
# We can't just do `prj_src add <constraints-file>` multiple times.
set fileId [open diamond.lpf "w"]
{% for name, attr in constraints.items() %}
set fp [open "{{ name }}" r]
set file_data [read $fp]
close $fp
puts -nonewline $fileId $file_data
{% endfor %}
close $fileId
{% endif %}

{% if top %}# Top-level specification
prj_impl option top "{{ top }}"
{% endif %}

{% if includes %}# Verilog Includes
{% for include in includes %}
prj_impl option -append {include path} {{ "{"+include+"}" }}
{% endfor %}
{% endif %}

{% if defines %}# Verilog Defines
{% for key, value in defines.items() %}
prj_impl option -append VERILOG_DIRECTIVES {{ key }}={{ value }}
{% endfor %}
{% endif %}

{% if params %}# Verilog Parameters / VHDL Generics
{% for key, value in params.items() %}
prj_impl option -append HDL_PARAM {{ key }}={{ value }}
{% endfor %}
{% endif %}

{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %}

prj_project save
prj_project close

{% endif %}

{% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow -----------------------------------------------------------------

prj_project open {{ project }}.ldf

{% if 'syn' in steps %}# Synthesis

{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %}

prj_run Synthesis -forceOne

{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %}

{% endif %}

{% if 'par' in steps %} # Translate, Map, and Place and Route
{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %}

prj_run Translate -forceOne
prj_run Map -forceOne
prj_run PAR -forceOne

{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %}

{% endif %}

{% if 'bit' in steps %}# Bitstream generation

{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %}

prj_run Export -task Bitgen -forceOne

{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %}

{% endif %}

prj_project save
prj_project close

{% endif %}
38 changes: 38 additions & 0 deletions tests/mocks/diamondc
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env python3

#
# Copyright (C) 2024 PyFPGA Project
#
# SPDX-License-Identifier: GPL-3.0-or-later
#

import argparse
import subprocess
import sys


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
)

print(f'INFO:the {tool.upper()} mock has been executed')
7 changes: 7 additions & 0 deletions tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
tdir = Path(__file__).parent.resolve()


def test_diamond():
tool = 'diamond'
generate(tool, 'PARTNAME')
base = f'results/{tool}/{tool}'
assert Path(f'{base}.tcl').exists(), 'file not found'


def test_ise():
tool = 'ise'
generate(tool, 'DEVICE-PACKAGE-SPEED')
Expand Down

0 comments on commit fe6937a

Please sign in to comment.