Skip to content

Commit edd0bd7

Browse files
authored
Merge pull request #43 from m42uko/feature/diamond
Implement support for Lattice Diamond
2 parents e552a69 + fe6937a commit edd0bd7

File tree

11 files changed

+312
-0
lines changed

11 files changed

+312
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# PyFPGA [![License](https://img.shields.io/badge/License-GPL--3.0-darkgreen?style=flat-square)](LICENSE)
22

3+
![Diamond](https://img.shields.io/badge/Diamond-3.13-blue.svg?style=flat-square)
34
![ISE](https://img.shields.io/badge/ISE-14.7-blue.svg?style=flat-square)
45
![Libero](https://img.shields.io/badge/Libero--Soc-2024.1-blue.svg?style=flat-square)
56
![Quartus](https://img.shields.io/badge/Quartus--Prime-23.1-blue.svg?style=flat-square)

examples/hooks/diamond.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""Diamond example hooks."""
2+
3+
from pyfpga.diamond import Diamond
4+
5+
prj = Diamond(odir='../build/diamond')
6+
7+
hooks = {
8+
"reports": """
9+
prj_run Map -task MapTrace -forceOne
10+
prj_run PAR -task PARTrace -forceOne
11+
prj_run PAR -task IOTiming -forceOne
12+
""",
13+
14+
"netlist_simulation": """
15+
prj_run Map -task MapVerilogSimFile
16+
prj_run Map -task MapVHDLSimFile -forceOne
17+
prj_run Export -task TimingSimFileVHD -forceOne
18+
prj_run Export -task TimingSimFileVlg -forceOne
19+
prj_run Export -task IBIS -forceOne
20+
""",
21+
22+
"progfile_ecp5u": """
23+
prj_run Export -task Promgen -forceOne
24+
""",
25+
26+
"progfile_machxo2": """
27+
prj_run Export -task Jedecgen -forceOne
28+
"""
29+
}
30+
31+
prj.set_part('LFXP2-5E-5TN144C')
32+
33+
prj.add_param('FREQ', '50000000')
34+
prj.add_param('SECS', '1')
35+
36+
prj.add_cons('../sources/cons/brevia2/clk.lpf', 'syn')
37+
prj.add_cons('../sources/cons/brevia2/clk.lpf', 'par')
38+
prj.add_cons('../sources/cons/brevia2/io.lpf', 'par')
39+
40+
prj.add_include('../sources/vlog/include1')
41+
prj.add_include('../sources/vlog/include2')
42+
prj.add_vlog('../sources/vlog/*.v')
43+
44+
prj.add_define('DEFINE1', '1')
45+
prj.add_define('DEFINE2', '1')
46+
47+
prj.set_top('Top')
48+
49+
for hook_name, hook in hooks.items():
50+
prj.add_hook('postpar', hook)
51+
52+
prj.make()

examples/projects/diamond.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""Diamond examples."""
2+
3+
import argparse
4+
5+
from pyfpga.diamond import Diamond
6+
7+
8+
parser = argparse.ArgumentParser()
9+
parser.add_argument(
10+
'--board', choices=['brevia2'], default='brevia2'
11+
)
12+
parser.add_argument(
13+
'--source', choices=['vlog', 'vhdl', 'slog'], default='vlog'
14+
)
15+
parser.add_argument(
16+
'--action', choices=['make', 'prog', 'all'], default='make'
17+
)
18+
args = parser.parse_args()
19+
20+
prj = Diamond(odir='../build/diamond')
21+
22+
if args.board == 'brevia2':
23+
prj.set_part('LFXP2-5E-5TN144C')
24+
prj.add_param('FREQ', '50000000')
25+
prj.add_cons('../sources/cons/brevia2/clk.lpf', 'syn')
26+
prj.add_cons('../sources/cons/brevia2/clk.lpf', 'par')
27+
prj.add_cons('../sources/cons/brevia2/io.lpf', 'par')
28+
prj.add_param('SECS', '1')
29+
30+
if args.source == 'vhdl':
31+
prj.add_vhdl('../sources/vhdl/*.vhdl', 'blink_lib')
32+
prj.add_vhdl('../sources/vhdl/top.vhdl')
33+
if args.source == 'vlog':
34+
prj.add_include('../sources/vlog/include1')
35+
prj.add_include('../sources/vlog/include2')
36+
prj.add_vlog('../sources/vlog/*.v')
37+
if args.source == 'slog':
38+
prj.add_include('../sources/slog/include1')
39+
prj.add_include('../sources/slog/include2')
40+
prj.add_slog('../sources/slog/*.sv')
41+
if args.source in ['vlog', 'slog']:
42+
prj.add_define('DEFINE1', '1')
43+
prj.add_define('DEFINE2', '1')
44+
45+
prj.set_top('Top')
46+
47+
if args.action in ['make', 'all']:
48+
prj.make()
49+
50+
if args.action in ['prog', 'all']:
51+
prj.prog()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
BLOCK RESETPATHS ;
2+
BLOCK ASYNCPATHS ;
3+
FREQUENCY NET "clk_i_c" 50.000000 MHz ;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
LOCATE COMP "clk_i" SITE "21" ;
2+
IOBUF PORT "clk_i" IO_TYPE=LVCMOS33 ;
3+
LOCATE COMP "led_o" SITE "37" ;
4+
IOBUF PORT "led_o" IO_TYPE=LVCMOS33 ;

pyfpga/diamond.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#
2+
# Copyright (C) 2024 PyFPGA Project
3+
#
4+
# SPDX-License-Identifier: GPL-3.0-or-later
5+
#
6+
7+
"""
8+
Implements support for Diamond.
9+
"""
10+
11+
import os
12+
from pyfpga.project import Project
13+
14+
15+
class Diamond(Project):
16+
"""Class to support Diamond projects."""
17+
18+
def _configure(self):
19+
tool = 'diamond'
20+
executable = 'pnmainc' if os.name == 'nt' else 'diamondc'
21+
self.conf['tool'] = tool
22+
self.conf['make_cmd'] = f'{executable} {tool}.tcl'
23+
self.conf['make_ext'] = 'tcl'
24+
self.conf['prog_bit'] = 'bit'
25+
self.conf['prog_cmd'] = f'sh {tool}-prog.sh'
26+
self.conf['prog_ext'] = 'sh'
27+
28+
def _make_custom(self):
29+
if 'part' not in self.data:
30+
self.data['part'] = 'LFXP2-5E-5TN144C'

pyfpga/factory.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

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

13+
from pyfpga.diamond import Diamond
1314
from pyfpga.ise import Ise
1415
from pyfpga.libero import Libero
1516
from pyfpga.openflow import Openflow
@@ -18,6 +19,7 @@
1819

1920

2021
TOOLS = {
22+
'diamond': Diamond,
2123
'ise': Ise,
2224
'libero': Libero,
2325
'openflow': Openflow,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{#
2+
# Copyright (C) 2024 PyFPGA Project
3+
#
4+
# SPDX-License-Identifier: GPL-3.0-or-later
5+
#
6+
#}
7+
8+
if [ "$DIAMOND_XCF" == "" ]; then
9+
DIAMOND_XCF=impl1/impl1.xcf
10+
fi
11+
12+
if [ -f "$DIAMOND_XCF" ]; then
13+
pgrcmd -infile $DIAMOND_XCF
14+
else
15+
echo "ERROR: Automatic programming with Diamond is not yet supported."
16+
echo " Please create the `realpath $DIAMOND_XCF` file manually and rerun the prog command."
17+
echo " Hint: You can change the location of the XCF file by setting the DIAMOND_XCF environment variable."
18+
exit 1
19+
fi

pyfpga/templates/diamond.jinja

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
{#
2+
#
3+
# Copyright (C) 2015-2024 PyFPGA Project
4+
#
5+
# SPDX-License-Identifier: GPL-3.0-or-later
6+
#
7+
#}
8+
9+
{% if 'cfg' in steps %}# Project configuration -------------------------------------------------------
10+
11+
prj_project new -name {{ project }} -dev {{ part }}
12+
13+
# For now, let's enforce Synplify as LSE (the default) has broken top level generic handling
14+
prj_syn set synplify
15+
16+
{% if hooks %}{{ hooks.precfg | join('\n') }}{% endif %}
17+
18+
{% if files %}# Files inclusion
19+
{% for name, attr in files.items() %}
20+
prj_src add {% if 'lib' in attr %}-work {{ attr.lib }}{% else %}{% endif %} {{ name }}
21+
{% endfor %}
22+
{% endif %}
23+
24+
{% if constraints %}
25+
# Constraints inclusion
26+
# Diamond only supports one constraints file, so we need to combine them into the default diamond.lpf.
27+
# We can't just do `prj_src add <constraints-file>` multiple times.
28+
set fileId [open diamond.lpf "w"]
29+
{% for name, attr in constraints.items() %}
30+
set fp [open "{{ name }}" r]
31+
set file_data [read $fp]
32+
close $fp
33+
puts -nonewline $fileId $file_data
34+
{% endfor %}
35+
close $fileId
36+
{% endif %}
37+
38+
{% if top %}# Top-level specification
39+
prj_impl option top "{{ top }}"
40+
{% endif %}
41+
42+
{% if includes %}# Verilog Includes
43+
{% for include in includes %}
44+
prj_impl option -append {include path} {{ "{"+include+"}" }}
45+
{% endfor %}
46+
{% endif %}
47+
48+
{% if defines %}# Verilog Defines
49+
{% for key, value in defines.items() %}
50+
prj_impl option -append VERILOG_DIRECTIVES {{ key }}={{ value }}
51+
{% endfor %}
52+
{% endif %}
53+
54+
{% if params %}# Verilog Parameters / VHDL Generics
55+
{% for key, value in params.items() %}
56+
prj_impl option -append HDL_PARAM {{ key }}={{ value }}
57+
{% endfor %}
58+
{% endif %}
59+
60+
{% if hooks %}{{ hooks.postcfg | join('\n') }}{% endif %}
61+
62+
prj_project save
63+
prj_project close
64+
65+
{% endif %}
66+
67+
{% if 'syn' in steps or 'par' in steps or 'bit' in steps %}# Design flow -----------------------------------------------------------------
68+
69+
prj_project open {{ project }}.ldf
70+
71+
{% if 'syn' in steps %}# Synthesis
72+
73+
{% if hooks %}{{ hooks.presyn | join('\n') }}{% endif %}
74+
75+
prj_run Synthesis -forceOne
76+
77+
{% if hooks %}{{ hooks.postsyn | join('\n') }}{% endif %}
78+
79+
{% endif %}
80+
81+
{% if 'par' in steps %} # Translate, Map, and Place and Route
82+
{% if hooks %}{{ hooks.prepar | join('\n') }}{% endif %}
83+
84+
prj_run Translate -forceOne
85+
prj_run Map -forceOne
86+
prj_run PAR -forceOne
87+
88+
{% if hooks %}{{ hooks.postpar | join('\n') }}{% endif %}
89+
90+
{% endif %}
91+
92+
{% if 'bit' in steps %}# Bitstream generation
93+
94+
{% if hooks %}{{ hooks.prebit | join('\n') }}{% endif %}
95+
96+
prj_run Export -task Bitgen -forceOne
97+
98+
{% if hooks %}{{ hooks.postbit | join('\n') }}{% endif %}
99+
100+
{% endif %}
101+
102+
prj_project save
103+
prj_project close
104+
105+
{% endif %}

tests/mocks/diamondc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env python3
2+
3+
#
4+
# Copyright (C) 2024 PyFPGA Project
5+
#
6+
# SPDX-License-Identifier: GPL-3.0-or-later
7+
#
8+
9+
import argparse
10+
import subprocess
11+
import sys
12+
13+
14+
parser = argparse.ArgumentParser()
15+
16+
parser.add_argument('source')
17+
18+
args = parser.parse_args()
19+
20+
tool = parser.prog
21+
22+
tcl = f'''
23+
proc unknown args {{ }}
24+
25+
source {args.source}
26+
'''
27+
28+
with open(f'{tool}-mock.tcl', 'w', encoding='utf-8') as file:
29+
file.write(tcl)
30+
31+
subprocess.run(
32+
f'tclsh {tool}-mock.tcl',
33+
shell=True,
34+
check=True,
35+
universal_newlines=True
36+
)
37+
38+
print(f'INFO:the {tool.upper()} mock has been executed')

0 commit comments

Comments
 (0)