quartus.py 10.9 KB
Newer Older
1 2 3
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
4
# Copyright (c) 2013 - 2016 CERN
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
# Author: Pawel Szostek (pawel.szostek@cern.ch)
# Multi-tool support by Javier D. Garcia-Lasheras (javier@garcialasheras.com)
#
# This file is part of Hdlmake.
#
# Hdlmake is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Hdlmake is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Hdlmake.  If not, see <http://www.gnu.org/licenses/>.
#

24 25
"""Module providing support for Altera Quartus synthesis"""

26
from __future__ import absolute_import
27
import os
28
import sys
29
import logging
30

31
from .make_syn import ToolSyn
32
from hdlmake.util import path as path_mod
33
from hdlmake.util import shell
34 35
from hdlmake.srcfile import (VHDLFile, VerilogFile, SVFile, DPFFile,
                             SignalTapFile, SDCFile, QIPFile, QSYSFile,
36
                             QSFFile, BSFFile, BDFFile, TDFFile, GDFFile)
37 38


39
class ToolQuartus(ToolSyn):
40

41
    """Class providing the interface for Altera Quartus synthesis"""
42

43 44 45
    TOOL_INFO = {
        'name': 'Quartus',
        'id': 'quartus',
46
        'windows_bin': 'quartus_sh.exe -t',
47
        'linux_bin': 'quartus_sh -t',
48
        'project_ext': 'qpf'}
49

50 51
    STANDARD_LIBS = ['altera', 'altera_mf', 'lpm', 'ieee', 'std']

52
    _QUARTUS_SOURCE = 'set_global_assignment -name {0} $(sourcefile)'
53 54 55 56

    SUPPORTED_FILES = {
        SignalTapFile: _QUARTUS_SOURCE.format('SIGNALTAP_FILE'),
        SDCFile: _QUARTUS_SOURCE.format('SDC_FILE'),
57
        QIPFile: _QUARTUS_SOURCE.format('QIP_FILE'),
58 59 60 61 62 63 64 65 66 67 68
        QSYSFile: _QUARTUS_SOURCE.format('QSYS_FILE'),
        DPFFile: _QUARTUS_SOURCE.format('MISC_FILE'),
        QSFFile: _QUARTUS_SOURCE.format('SOURCE_TCL_SCRIPT_FILE'),
        BSFFile: _QUARTUS_SOURCE.format('BSF_FILE'),
        BDFFile: _QUARTUS_SOURCE.format('BDF_FILE'),
        TDFFile: _QUARTUS_SOURCE.format('AHDL_FILE'),
        GDFFile: _QUARTUS_SOURCE.format('GDF_FILE')}

    _QUARTUS_LIBRARY = " -library {0}".format('work')

    HDL_FILES = {
69 70
        VHDLFile: _QUARTUS_SOURCE.format('VHDL_FILE') +
                  _QUARTUS_LIBRARY,
71
        VerilogFile: _QUARTUS_SOURCE.format('VERILOG_FILE') +
72
                     _QUARTUS_LIBRARY,
73
        SVFile: _QUARTUS_SOURCE.format('SYSTEMVERILOG_FILE') +
74
                 _QUARTUS_LIBRARY}
75

76
    CLEAN_TARGETS = {'clean': ["*.rpt", "*.smsg", "*.summary",
77
                               "*.done", "*.jdi", "*.pin", "*.qws",
78 79
                               "db", "incremental_db", "$(PROJECT).qsf",
                               "*.qpf"],
80
                     'mrproper': ["*.sof", "*.pof", "*.jam", "*.jbc",
81
                                  "*.ekp", "*.jic"]}
82

83 84 85 86
    TCL_CONTROLS = {'create': 'project_new $(PROJECT)',
                    'open': 'project_open $(PROJECT)',
                    'project': 'load_package flow\n'
                               '$(TCL_CREATE)\n'
87
                               'remove_all_global_assignments -name *_FILE\n'
88
                               'source files.tcl',
89 90
                    'bitstream': 'load_package flow\n'
                                 '$(TCL_OPEN)\n'
91
                                 'execute_flow -compile',
92 93
                    'install_source': ''}

94 95 96 97 98 99 100 101 102 103
    SET_GLOBAL_INSTANCE = 0
    SET_INSTANCE_ASSIGNMENT = 1
    SET_LOCATION_ASSIGNMENT = 2
    SET_GLOBAL_ASSIGNMENT = 3

    PROP_TYPE = {"set_global_instance": SET_GLOBAL_INSTANCE,
                 "set_instance_assignment": SET_INSTANCE_ASSIGNMENT,
                 "set_location_assignment": SET_LOCATION_ASSIGNMENT,
                 "set_global_assignment": SET_GLOBAL_ASSIGNMENT}

104 105
    def __init__(self):
        super(ToolQuartus, self).__init__()
106
        self._tool_info.update(ToolQuartus.TOOL_INFO)
107 108
        self._hdl_files.update(ToolQuartus.HDL_FILES)
        self._supported_files.update(ToolQuartus.SUPPORTED_FILES)
109
        self._standard_libs.extend(ToolQuartus.STANDARD_LIBS)
110 111
        self._clean_targets.update(ToolQuartus.CLEAN_TARGETS)
        self._tcl_controls.update(ToolQuartus.TCL_CONTROLS)
112

113
    def _makefile_syn_top(self):
114
        """Update project synthesis variables for Quartus"""
115
        import re
116

117 118 119 120 121
        def __get_family_string(family=None, device=None):
            """Function that looks for a existing device family name and
            try to guess the value from the device string if not defined"""
            family_names = {
                "^EP2AGX.*$": "Arria II GX",
122 123
                "^EP1C.*$": "Cyclone",
                "^EP2C.*$": "Cyclone II",
124
                "^EP3C.*$": "Cyclone III",
125
                "^EP4C.*$": "Cyclone IV",
126 127
                "^EP4CE.*$": "Cyclone IV E",
                "^EP4CGX.*$": "Cyclone IV GX",
128
                "^5A.*$": "Arria V",
129 130 131 132 133 134 135 136 137
                "^5S.*$": "Stratix V"}
            if family is None:
                for key in family_names:
                    if re.match(key, device.upper()):
                        family = family_names[key]
                        logging.debug(
                            "Auto-guessed syn_family to be %s (%s => %s)",
                            family, device, key)
            if family is None:
138 139
                raise Exception("Could not auto-guess device family, please "
                                "specify in Manifest.py using syn_family!")
140
            return family
141

142
        family_string = __get_family_string(
143 144
            family=self.manifest_dict.get("syn_family", None),
            device=self.manifest_dict.get("syn_device", ''))
145 146 147 148 149
        device_string = (self.manifest_dict["syn_device"] +
                         self.manifest_dict["syn_package"] +
                         self.manifest_dict["syn_grade"])
        self.manifest_dict["syn_family"] = family_string
        self.manifest_dict["syn_device"] = device_string
150
        super(ToolQuartus, self)._makefile_syn_top()
151 152 153 154 155 156

    def _emit_property(self, command, new_property):
        """Emit a formated property for Altera Quartus TCL"""
        property_dict = {
            'what': None,
            'name': None,
157 158 159
            'type': None,
            'from': None,
            'to': None,
160
            'section_id': None,
161
            'tag': None}
162 163 164 165 166 167 168 169 170
        property_dict.update(new_property)
        words = []
        words.append(dict([(b, a) for a, b in
                     self.PROP_TYPE.items()])[command])
        if property_dict['what'] is not None:
            words.append(property_dict['what'])
        if property_dict['name'] is not None:
            words.append("-name")
            words.append(property_dict['name'])
171 172
            words.append('\\"%s\\"' % property_dict['value'])
        if property_dict['from'] is not None:
173
            words.append("-from")
174 175
            words.append(property_dict['from'])
        if property_dict['tag'] is not None:
176
            words.append("-tag")
177 178
            words.append(property_dict['tag'])
        if property_dict['to'] is not None:
179
            words.append("-to")
180
            words.append(property_dict['to'])
181 182 183 184 185
        if property_dict['section_id'] is not None:
            words.append("-section_id")
            words.append(property_dict['section_id'])
        return ' '.join(words)

186
    def _makefile_syn_tcl(self):
187
        """Add initial properties to the Altera Quartus project"""
188
        command_list = []
189
        command_list.append(self._tcl_controls["project"])
190
        command_list.append(self._emit_property(
191
            self.SET_GLOBAL_ASSIGNMENT,
192 193
            {'name': 'FAMILY',
            'value': '$(SYN_FAMILY)'}))
194
        command_list.append(self._emit_property(
195
            self.SET_GLOBAL_ASSIGNMENT,
196 197
            {'name': 'DEVICE',
            'value':'$(SYN_DEVICE)'}))
198
        command_list.append(self._emit_property(
199
            self.SET_GLOBAL_ASSIGNMENT,
200 201
            {'name': 'TOP_LEVEL_ENTITY',
            'value': '$(TOP_MODULE)'}))
202
        for user_property in self.manifest_dict.get("syn_properties", []):
203
            if not isinstance(user_property, dict):
204 205
                raise Exception("Quartus property should be defined as dict: "
                                + str(user_property))
206
            command_list.append(self._emit_property(self.SET_GLOBAL_ASSIGNMENT,
207
                                user_property))
208 209
        for inc in self.manifest_dict.get("include_dirs", []):
            command_list.append(self._emit_property(self.SET_GLOBAL_ASSIGNMENT,
210 211
                                {'name': 'SEARCH_PATH',
                                'value': inc}))
212
        self._tcl_controls["project"] = '\n'.join(command_list)
213
        super(ToolQuartus, self)._makefile_syn_tcl()
214

215
    def _makefile_syn_files(self):
216
        # Insert the Quartus standard control TCL files
217
        command_list = []
218
        if "quartus_preflow" in self.manifest_dict:
219
            path = shell.tclpath(path_mod.compose(
220
                self.manifest_dict["quartus_preflow"], os.getcwd()))
221
            if not os.path.exists(path):
222 223 224
                raise Exception("quartus_preflow file listed in "
                                + os.getcwd() + " doesn't exist: "
                                + path + ".\nExiting.")
225
            preflow = '"' + 'quartus_sh:' + path + '"'
226
            command_list.append(self._emit_property(self.SET_GLOBAL_ASSIGNMENT,
227 228
                                {'name': 'PRE_FLOW_SCRIPT_FILE',
                                'value': preflow}))
229
        if "quartus_postmodule" in self.manifest_dict:
230
            path = shell.tclpath(path_mod.compose(
231
                self.manifest_dict["quartus_postmodule"],
232
                os.getcwd()))
233
            if not os.path.exists(path):
234 235 236
                raise Exception("quartus_postmodule file listed in "
                                + os.getcwd() + " doesn't exist: "
                                + path + ".\nExiting.")
237
            postmodule = '"' + 'quartus_sh:' + path + '"'
238
            command_list.append(self._emit_property(self.SET_GLOBAL_ASSIGNMENT,
239 240
                                {'name': 'POST_MODULE_SCRIPT_FILE',
                                'value': postmodule}))
241
        if "quartus_postflow" in self.manifest_dict:
242
            path = shell.tclpath(path_mod.compose(
243
                self.manifest_dict["quartus_postflow"], os.getcwd()))
244
            if not os.path.exists(path):
245 246 247
                raise Exception("quartus_postflow file listed in "
                                + os.getcwd() + " doesn't exist: "
                                + path + ".\nExiting.")
248
            postflow = '"' + 'quartus_sh:' + path + '"'
249
            command_list.append(self._emit_property(self.SET_GLOBAL_ASSIGNMENT,
250 251
                                {'name': 'POST_FLOW_SCRIPT_FILE',
                                'value': postflow}))
252
        self._tcl_controls["files"] = '\n'.join(command_list)
253
        super(ToolQuartus, self)._makefile_syn_files()