OpenCores
URL https://opencores.org/ocsvn/nocmodel/nocmodel/trunk

Subversion Repositories nocmodel

[/] [nocmodel/] [trunk/] [nocmodel/] [noc_codegen_base.py] - Blame information for rev 4

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 4 dargor
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
 
4
#
5
# NoC Code generator
6
#   This module defines the basic structure for code generation
7
#
8
# Author:  Oscar Diaz
9
# Version: 0.2
10
# Date:    11-11-2011
11
 
12
#
13
# This code is free software; you can redistribute it and/or
14
# modify it under the terms of the GNU Lesser General Public
15
# License as published by the Free Software Foundation; either
16
# version 2.1 of the License, or (at your option) any later version.
17
#
18
# This code is distributed in the hope that it will be useful,
19
# but WITHOUT ANY WARRANTY; without even the implied warranty of
20
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21
# Lesser General Public License for more details.
22
#
23
# You should have received a copy of the GNU Lesser General Public
24
# License along with this library; if not, write to the
25
# Free Software  Foundation, Inc., 59 Temple Place, Suite 330,
26
# Boston, MA  02111-1307  USA
27
#
28
 
29
#
30
# Changelog:
31
#
32
# 11-11-2011 : (OD) initial release
33
#
34
 
35
"""
36
Base for code generation support
37
 
38
This module defines:
39
* Class 'noc_codegen_base'
40
* Class 'noc_codegen_ext'
41
"""
42
 
43
from myhdl import intbv, SignalType
44
from nocmodel import *
45
 
46
#import scipy
47
#import networkx as nx
48
from copy import deepcopy
49
from sys import exc_info
50
import warnings
51
 
52
class noc_codegen_base():
53
    """
54
    Base class for code generator
55
 
56
    This class holds a basic model of a hardware description. Although inspired
57
    in VHDL, it also holds for Verilog.
58
 
59
    Note that its necessary a defined noc model to generate this code model.
60
 
61
    Note that this class is meant to be extended by a particular code generator:
62
    VHDL generator, Verilog generator, etc.
63
 
64
    This object will assume a module has the following sections:
65
    --------------------
66
    <docheader>  : File header and documentation about the module
67
    <libraries>  : Declarations at the beginning (VHDL: libraries declarations)
68
    <modulename> : Module name (VHDL: entity name)
69
    <generics>   : Parameters for the module (VHDL: generics inside an entity)
70
    <ports>      : Declaration of inputs and outputs (VHDL: ports inside an entity)
71
    <implementation> : Code implementation (pure VHDL or Verilog code)
72
    --------------------
73
 
74
    Objects type:
75
    * docheader, libraries, modulename and implementation are simple strings.
76
    * generics are list of dictionaries with this required keys:
77
        * "class" : always "generic"
78
        * "name"
79
        * "type" : string formatted data type
80
        * "type_array" : 2-element list of strings that declares array boundaries
81
            either using numbers or generics names. None for simple data types
82
        * "default_value"
83
        * "current_value"
84
        * "description"
85
    * ports are list of dictionaries with this required keys:
86
        * "class" : always "port"
87
        * "name"
88
        * "type" : port type, if applicable
89
        * "nocport" : index to related port in the nocobject' ports dictionary or
90
            others signal containers in the nocobject.
91
        * "signal_list" : list of signals that compose the port
92
        * "description"
93
    * external_signals are list of signals that aren't related to any port.
94
    * signals are dictionaries with this required keys:
95
        * "class" : always "signal"
96
        * "name"
97
        * "type" : string formatted data type
98
        * "type_array" : 2-element list of strings that declares array boundaries
99
            either using numbers or generics names. None for simple data types
100
        * "direction"
101
        * "default_value"
102
        * "related_generics" : when the data type is an array, this list has
103
            references to generics to be used to build array declaration.
104
        * "description"
105
 
106
    Attributes:
107
    * modulename
108
    * nocobject_ref
109
    * docheader
110
    * libraries
111
    * generics
112
    * ports
113
    * external_signals
114
    * implementation
115
    * interface_hash
116
 
117
    Notes:
118
    * Code generation methods are meant to return strings. The user is
119
      responsible to write it in files.
120
    * This object maintains the code model and use it to generate code. Any
121
      particular changes or adjustments to the model should be done by a
122
      'noc_codegen_ext' object.
123
    * This code generation model are restricted to noc models, because
124
      routers and ipcores are generated by MyHDL code converter (this
125
      could change if the noc objects needs to keep hierarchy in its
126
      internal implementation).
127
      This converter keeps routers and ipcores hierarchy.
128
    """
129
 
130
    def __init__(self, nocobject_ref, **kwargs):
131
        # nocobject reference
132
        if not isinstance(nocobject_ref, (nocobject, noc)):
133
            raise TypeError("Argument must be an instance of nocobject or noc class")
134
        self.nocobject_ref = nocobject_ref
135
 
136
        # External conversion flag
137
        self.external_conversion = False
138
 
139
        # string type objects
140
        self.docheader = ""
141
        self.libraries = ""
142
        self.modulename = ""
143
 
144
        # list of dictionaries (generic and port)
145
        self.generics = []
146
        self.ports = []
147
        self.external_signals = []
148
 
149
        # implementation 
150
        self.implementation = ""
151
 
152
        # module hash
153
        self.interface_hash = ""
154
 
155
        # optional arguments
156
        for key in kwargs.keys():
157
            setattr(self, key, kwargs[key])
158
 
159
    # main methods
160
    def generate_file(self):
161
        """
162
        Generate the entire file that implements this object.
163
        """
164
        raise NotImplementedError("What language for code generation?")
165
 
166
    def generate_component(self):
167
        """
168
        Generate a component definition for this object.
169
        """
170
        raise NotImplementedError("What language for code generation?")
171
 
172
    def generate_generic_declaration(self, generic=None, with_default=False):
173
        """
174
        Generate a generic declaration for this object.
175
 
176
        Arguments:
177
        * generic : either a name or a list index for a particular generic
178
        * with_default : True to add the default value
179
 
180
        Returns:
181
        * A string when generic argument is used
182
        * A list of strings with all generics
183
        """
184
        raise NotImplementedError("What language for code generation?")
185
 
186
    def generate_port_declaration(self, port=None, with_default=False):
187
        """
188
        Generate a port declaration for this object.
189
 
190
        Arguments:
191
        * port : either a name or a list index for a particular port
192
        * with_default : True to add the default value
193
 
194
        Returns:
195
        * A list of strings with all signals in port when port argument is used
196
        * A list of strings with all signals in all ports
197
        """
198
        raise NotImplementedError("What language for code generation?")
199
 
200
    def generate_signal_declaration(self, inport=None, signal=None, with_default=False):
201
        """
202
        Generate a signal declaration for this object.
203
 
204
        Arguments:
205
        * inport : either a name or a list index for a particular port. None
206
            means use the external_signals list
207
        * signal : either a name or a list index for a particular signal
208
        * with_default : True to add the default value
209
 
210
        Returns:
211
        * A string when signal argument is used
212
        * A list of strings with all signals
213
        """
214
        raise NotImplementedError("What language for code generation?")
215
 
216
    def make_comment(self, data):
217
        """
218
        Convert string data to language comment
219
 
220
        Argument:
221
        * data: string or list of strings to convert
222
 
223
        Returns: string or list of strings with comments added.
224
        """
225
        raise NotImplementedError("What language for code generation?")
226
 
227
    def add_tab(self, data, level=1):
228
        """
229
        Add an indentation level to the string
230
 
231
        Argument:
232
        * data: string or list of strings
233
        * level: how many indentation levels to add. Default 1
234
 
235
        Returns: string or list of strings with <level> indentation levels.
236
        """
237
        raise NotImplementedError("What language for code generation?")
238
 
239
    def to_valid_str(self, str_in):
240
        """
241
        Convert an input string, changing special characters used on
242
        the HDL language. Useful for set names .
243
 
244
        Argument:
245
        * str_in: string to convert
246
 
247
        Returns: the converted string.
248
        """
249
        raise NotImplementedError("What language for code generation?")
250
 
251
    # codegen model management
252
    def add_generic(self, name, value, description="", **kwargs):
253
        """
254
        Add a generic to the model.
255
 
256
        Arguments:
257
        * name : must be a string
258
        * value : default value for this generic
259
        * description :
260
        * Optional arguments (just for method call format)
261
 
262
        Returns:
263
        * Reference to added generic dictionary.
264
 
265
        Note:
266
        * This method can override an existing generic entry.
267
        * This basic method just add name, default value and description.
268
          Extended class methods must fill correct type arguments.
269
        """
270
        if not isinstance(name, str):
271
            raise TypeError("Name must be string, not '%s'." % repr(name))
272
 
273
        # supported types:
274
        if not isinstance(value, (bool, int, intbv, str)):
275
            raise TypeError("Unsupported type '%s'." % repr(type(value)))
276
 
277
        # check if new entry
278
        g = filter(lambda x: x["name"] == name, self.generics)
279
        if len(g) == 0:
280
            g = get_new_generic(name = name)
281
            self.generics.append(g)
282
        else:
283
            g = g[0]
284
        g.update(default_value = value, description = description)
285
        # optional kwargs
286
        g.update(kwargs)
287
        # return reference to added generic dict
288
        return g
289
 
290
    def add_port(self, name, signal_desc=None, description="", **kwargs):
291
        """
292
        Add a port to the model.
293
 
294
        Arguments:
295
        * name : must be a string
296
        * signal_desc : optional dictionary with signal information included.
297
          None to just add/update port without changing its signals.
298
        * description :
299
        * Optional arguments (just for method call format)
300
 
301
        Returns:
302
        * Reference to added port dictionary.
303
 
304
        Note:
305
        * This method can add a new port or update an existing port with new
306
          signals.
307
        * This basic method just add name and description, and update a signal
308
          using signal_desc information. Extended class methods must fill
309
          correct type arguments in the updated signal.
310
        """
311
        if not isinstance(name, str):
312
            raise TypeError("Name must be string, not '%s'." % repr(name))
313
 
314
        # check signal dict keys: must have at least class and name
315
        if signal_desc is not None:
316
            if not all([x in signal_desc for x in ("class", "name")]):
317
                raise TypeError("Argument signal_desc must be a signal dict ('%s')." % repr(signal_desc))
318
            if signal_desc["class"] != "signal":
319
                raise TypeError("Argument signal_desc must be a signal dict ('%s')." % repr(signal_desc))
320
 
321
        # check if new entry
322
        p = filter(lambda x: x["name"] == name, self.ports)
323
        if len(p) == 0:
324
            p = get_new_port(name = name)
325
            self.ports.append(p)
326
        else:
327
            p = p[0]
328
 
329
        # only update description when string is non-empty
330
        if description != "":
331
            p.update(description = description)
332
 
333
        # check existing signal
334
        if signal_desc is not None:
335
            sig = filter(lambda x: x["name"] == signal_desc["name"], p["signal_list"])
336
            if len(sig) == 0:
337
                p["signal_list"].append(signal_desc)
338
            else:
339
                sig[0].update(signal_desc)
340
 
341
        # optional kwargs
342
        p.update(kwargs)
343
        # return reference to added/updated port dict
344
        return p
345
 
346
    def add_external_signal(self, name, direction, value, description="", **kwargs):
347
        """
348
        Add a external signal to the model.
349
 
350
        Arguments:
351
        * name : must be a string
352
        * direction : "in" or "out"
353
        * value : initial value for this port
354
        * description
355
        * Optional arguments (just for method call format)
356
 
357
        Returns:
358
        * Reference to added external_signals dictionary.
359
 
360
        Note:
361
        * This method can override an existing signal entry.
362
        * This basic method just add name, direction, intial value and
363
          description. Extended class methods must fill correct type arguments.
364
        """
365
        if not isinstance(name, str):
366
            raise TypeError("Name must be string, not '%s'." % repr(name))
367
 
368
        # supported types:
369
        if isinstance(value, SignalType):
370
            value = value._init
371
        if not isinstance(value, (bool, int, intbv)):
372
            raise TypeError("Unsupported type '%s'." % repr(type(value)))
373
        if direction not in ["in", "out"]:
374
            raise ValueError("Direction must be 'in' or 'out', not '%s'." % repr(direction))
375
        # check if new entry
376
        sig = filter(lambda x: x["name"] == name, self.external_signals)
377
        if len(sig) == 0:
378
            sig = get_new_signal(name = name)
379
            self.external_signals.append(sig)
380
        else:
381
            sig = sig[0]
382
        sig.update(direction = direction, default_value = value, description = description)
383
        # optional kwargs
384
        sig.update(kwargs)
385
        # return reference to added generic dict
386
        return sig
387
 
388
    def build_implementation(self):
389
        """
390
        Try to generate the implementation section. Check if a "codemodel"
391
        object exists and try to call "generate_implementation" method.
392
 
393
        If the call is successful, self.implementation will store its return
394
        string. Do nothing on failure (report a warning).
395
        """
396
        if hasattr(self.nocobject_ref, "codemodel"):
397
            implementation = ""
398
            if not hasattr(self.nocobject_ref.codemodel, "generate_implementation"):
399
                warnings.warn("Method 'generate_implementation()' not found.")
400
            elif not callable(self.nocobject_ref.codemodel.generate_implementation):
401
                warnings.warn("Attribute 'generate_implementation()' not callable")
402
            else:
403
                implementation = self.nocobject_ref.codemodel.generate_implementation()
404
            ##except:
405
                ### Report a warning instead of the exception
406
                ##exctype, value = exc_info()[:2]
407
                ##warnings.warn("%s: %s" % (exctype, value))
408
            if isinstance(implementation, str):
409
                self.implementation = implementation
410
 
411
    def model_hash(self):
412
        """
413
        Calculate a hash string based on the nocobject interface: Generics,
414
        ports and external signals.
415
 
416
        Two nocobject with the same model hash can be instantiated by the same
417
        module code (with different generics' values).
418
 
419
        Returns: a string with a predefined format.
420
        """
421
        hash_str = ""
422
 
423
        # 1. Generics information
424
        hash_gen = []
425
        for g in self.generics:
426
            hash_gen.append([g["name"], g["type"]])
427
        # sort generics by name
428
        hash_gen.sort(key = lambda x: x[0])
429
        # add a stream of "<name><type>"
430
        for g in hash_gen:
431
            hash_str += "%s%s" % (g[0], g[1])
432
 
433
        # 2. Ports information
434
        hash_port = []
435
        for g in self.ports:
436
            hash_port.append([g["name"], g["type"], len(g["signal_list"])])
437
        # sort ports by name
438
        hash_port.sort(key = lambda x: x[0])
439
        # add a stream of "<name><type><num-of-signals>"
440
        for g in hash_port:
441
            hash_str += "%s%s" % (g[0], g[1])
442
 
443
        # 3. External ports information
444
        hash_ext = []
445
        for g in self.external_signals:
446
            hash_ext.append([g["name"], g["type"], g["direction"]])
447
        # sort external_signals by name
448
        hash_ext.sort(key = lambda x: x[0])
449
        # add a stream of "<name><type><direction>"
450
        for g in hash_ext:
451
            hash_str += "%s%s" % (g[0], g[1])
452
 
453
        self.interface_hash = hash_str
454
        return hash_str
455
 
456
class noc_codegen_ext():
457
    """
458
    Extension class for code generator
459
 
460
    This class supports an access point to do transformations on a
461
    'noc_codegen_base' code model.
462
 
463
    This transformation methods should be declared on extended classes of this
464
    class, and are close related to the particular noc object.
465
 
466
    Notes:
467
    * This object maintains the code model and use it to generate code. Any
468
      particular changes or adjustments to the model should be done by a
469
      'noc_codegen_ext' object. Usually this object is related to a particular
470
      nocobject (router, ipcore or channel).
471
    """
472
 
473
    def __init__(self, codegen_ref):
474
        # must be related to a noc_codegen_base object
475
        if not isinstance(codegen_ref, noc_codegen_base):
476
            raise TypeError("Argument must be an instance of 'noc_codegen_base' class")
477
 
478
        self.codegen_ref = codegen_ref
479
        self.nocobject_ref = codegen_ref.nocobject_ref
480
 
481
# helper structures:
482
# base objects for generics, ports and signals
483
_empty_generic = {
484
    "class": "generic",
485
    "name": "",
486
    "type": "",
487
    "type_array": [None, None],
488
    "default_value": "",
489
    "current_value": "",
490
    "description": ""}
491
_empty_port = {
492
    "class": "port",
493
    "name": "",
494
    "type": "",
495
    "nocport": None,
496
    "signal_list": [],
497
    "description": ""}
498
_empty_signal = {
499
    "class": "signal",
500
    "name": "",
501
    "type": "",
502
    "type_array": [None, None],
503
    "direction": "",
504
    "default_value": "",
505
    "related_generics": [],
506
    "description": ""}
507
 
508
# new structures generation
509
def get_new_generic(**kwargs):
510
    s = deepcopy(_empty_generic)
511
    s.update(kwargs)
512
    return s
513
def get_new_port(**kwargs):
514
    s = deepcopy(_empty_port)
515
    s.update(kwargs)
516
    return s
517
def get_new_signal(**kwargs):
518
    s = deepcopy(_empty_signal)
519
    s.update(kwargs)
520
    return s

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.