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

Subversion Repositories nocmodel

[/] [nocmodel/] [trunk/] [nocmodel/] [noc_codegen_vhdl.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 - VHDL generator
6
#
7
# Author:  Oscar Diaz
8
# Version: 0.1
9
# Date:    11-11-2011
10
 
11
#
12
# This code is free software; you can redistribute it and/or
13
# modify it under the terms of the GNU Lesser General Public
14
# License as published by the Free Software Foundation; either
15
# version 2.1 of the License, or (at your option) any later version.
16
#
17
# This code is distributed in the hope that it will be useful,
18
# but WITHOUT ANY WARRANTY; without even the implied warranty of
19
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20
# Lesser General Public License for more details.
21
#
22
# You should have received a copy of the GNU Lesser General Public
23
# License along with this library; if not, write to the
24
# Free Software  Foundation, Inc., 59 Temple Place, Suite 330,
25
# Boston, MA  02111-1307  USA
26
#
27
 
28
#
29
# Changelog:
30
#
31
# 11-11-2011 : (OD) initial release
32
#
33
 
34
"""
35
Code generation support for VHDL
36
 
37
This module defines:
38
* Class 'noc_codegen_vhdl'
39
* Function 'add_codegen_vhdl_support'
40
"""
41
 
42
from nocmodel import *
43
from noc_codegen_base import *
44
from myhdl import intbv, bin
45
 
46
class noc_codegen_vhdl(noc_codegen_base):
47
    """
48
    VHDL generator class
49
 
50
    This class defines methods used to generate VHDL code from any noc object.
51
 
52
    Like base class, this object has this base attributes:
53
    * docheader
54
    * libraries
55
    * modulename
56
    * generics
57
    * ports
58
    * implementation
59
    """
60
    def __init__(self, nocobject_ref, **kwargs):
61
        noc_codegen_base.__init__(self, nocobject_ref, **kwargs)
62
 
63
        # note: the noc object must be complete, in order to 
64
        # add code generation support
65
        #self.nocobject_ref = nocobject_ref
66
        self.modulename = self.to_valid_str(self.nocobject_ref.name)
67
 
68
        # default header and libraries
69
        self.docheader = "-- Document header"
70
        self.libraries = "library ieee;\nuse ieee.std_logic_1164.all;\nuse ieee.numeric_std.all;"
71
 
72
        # code generation attributes
73
        self.usetab = "    " # 4-space tabs
74
        self.full_comments = True
75
 
76
     # main methods
77
    def generate_file(self):
78
        """
79
        Generate the entire file that implements this object.
80
        """
81
        # Unless code generation is externally generated.
82
        if self.external_conversion:
83
            # use "codemodel" object on nocobject_ref
84
            if hasattr(self.nocobject_ref, "codemodel"):
85
                return self.nocobject_ref.codemodel.generate_file()
86
            else:
87
                raise AttributeError("External conversion flag is enabled, but there's no 'codemodel' object available in nocobject %s" % self.nocobject_ref)
88
        else:
89
            # first try to call build_implementation() method
90
            self.build_implementation()
91
            sret = self.docheader + "\n\n" + self.libraries + "\n\n"
92
            if self.full_comments:
93
                sret += self.make_comment("Object '%s' name '%s' description '%s'\n" % (repr(self.nocobject_ref), self.nocobject_ref.name, self.nocobject_ref.description))
94
            sret += self.generate_entity_section() + "\n\n"
95
            sret += self.generate_architecture_section() + "\n"
96
            return sret
97
 
98
    def generate_component(self):
99
        """
100
        Generate a component definition for this object.
101
        """
102
        sret = "component %s\n" % self.modulename
103
        stmp = self.generate_generics_section()
104
        if stmp != "":
105
            sret += self.add_tab(stmp) + ";\n"
106
        stmp = self.generate_ports_section()
107
        if stmp != "":
108
            sret += self.add_tab(stmp) + ";\n"
109
        sret += "end component;\n"
110
        return sret
111
 
112
    def generate_generic_declaration(self, generic=None, with_default=False):
113
        """
114
        Generate a generic declaration for this object.
115
 
116
        Arguments:
117
        * generic : either a name or a list index for a particular generic
118
        * with_default : True to add the default value
119
 
120
        Returns:
121
        * A string when generic argument is used
122
        * A list of strings with all generics
123
        """
124
        if generic is None:
125
            # all generics
126
            l = []
127
            for i in range(len(self.generics)):
128
                l.append(self.generate_generic_declaration(i, with_default))
129
            return l
130
        else:
131
            # search for correct index
132
            if isinstance(generic, int):
133
                g = self.generics[generic]
134
            elif isinstance(generic, string):
135
                g = filter(lambda s: s["name"] == generic, self.generics)[0]
136
            else:
137
                raise TypeError("Don't know how to search with '%s'." % repr(generic))
138
            sret = "%s : %s" % (self.to_valid_str(g["name"]), g["type"])
139
            if with_default:
140
                sret += ' := %s' % convert_value(g["default_value"], g["type"], g["type_array"])
141
                # add string quotes
142
#                if g["type"] == "string":
143
#                    sret += ' := "%s"' % g["default_value"]
144
#                else:
145
#                    sret += ' := %s' % g["default_value"]
146
            return sret
147
 
148
    def generate_port_declaration(self, port=None, with_default=False):
149
        """
150
        Generate a port declaration for this object.
151
 
152
        Arguments:
153
        * port : either a name or a list index for a particular port
154
        * with_default : True to add the default value
155
 
156
        Returns:
157
        * A list of strings with all signals in port when port argument is used
158
        * A list of strings with all signals in all ports
159
        """
160
        if port is None:
161
            # all ports
162
            l = []
163
            for i in range(len(self.ports)):
164
                l.append(self.make_comment("Port '%s' : '%s'" % (self.to_valid_str(self.ports[i]["name"]), self.ports[i]["type"])))
165
                pl = self.generate_port_declaration(i, with_default)
166
                pl.sort()
167
                l.extend(pl)
168
            return l
169
        else:
170
            # search for correct index
171
            if isinstance(port, int):
172
                idx = port
173
            elif isinstance(port, string):
174
                p = filter(lambda s: s["name"] == port, self.ports)[0]
175
                idx = self.ports.index(p)
176
            else:
177
                raise TypeError("Don't know how to search with '%s'." % repr(port))
178
            return self.generate_signal_declaration(idx, None, with_default)
179
 
180
    def generate_signal_declaration(self, inport=None, signal=None, with_default=False):
181
        """
182
        Generate a signal declaration for this object.
183
 
184
        Arguments:
185
        * inport : either a name or a list index for a particular port. None
186
            means use the external_signals list
187
        * signal : either a name or a list index for a particular signal
188
        * with_default : True to add the default value
189
 
190
        Returns:
191
        * A string when signal argument is used
192
        * A list of strings with all signals
193
        """
194
        if signal is None:
195
            # all signals
196
            l = []
197
            if inport is None:
198
                r = range(len(self.external_signals))
199
            else:
200
                # what port?
201
                if isinstance(inport, int):
202
                    g = self.ports[inport]
203
                elif isinstance(inport, string):
204
                    g = filter(lambda s: s["name"] == inport, self.ports)[0]
205
                else:
206
                    raise TypeError("Don't know how to search with '%s'." % repr(inport))
207
                r = range(len(g["signal_list"]))
208
            for i in r:
209
                l.append(self.generate_signal_declaration(inport, i, with_default))
210
            return l
211
        else:
212
            # locate either port or external_signals
213
            if inport is None:
214
                if isinstance(signal, int):
215
                    sig = self.external_signals[signal]
216
                elif isinstance(signal, string):
217
                    sig = filter(lambda s: s["name"] == signal, self.external_signals)[0]
218
                else:
219
                    raise TypeError("Don't know how to search with '%s'." % repr(signal))
220
                # put nothing as signal prefix
221
                sprefix = ""
222
            else:
223
                # what port?
224
                if isinstance(inport, int):
225
                    p = self.ports[inport]
226
                elif isinstance(inport, string):
227
                    p = filter(lambda s: s["name"] == inport, self.ports)[0]
228
                else:
229
                    raise TypeError("Don't know how to search with '%s'." % repr(inport))
230
                # what signal?
231
                if isinstance(signal, int):
232
                    sig = p["signal_list"][signal]
233
                elif isinstance(signal, string):
234
                    sig = filter(lambda s: s["name"] == signal, p["signal_list"])[0]
235
                else:
236
                    raise TypeError("Don't know how to search with '%s'." % repr(signal))
237
                # put port name as signal prefix
238
                #sprefix = self.to_valid_str(p["name"]) + "_"
239
                sprefix = ""
240
            sret = "%s%s : %s %s" % (sprefix, self.to_valid_str(sig["name"]), sig["direction"], sig["type"])
241
            if with_default:
242
                sret += ' := %s' % convert_value(sig["default_value"], sig["type"])
243
#                if sig["type"] == "string":
244
#                    sret += ' := "%s"' % sig["default_value"]
245
#                else:
246
#                    sret += ' := %s' % sig["default_value"]
247
            return sret
248
 
249
    def make_comment(self, data):
250
        """
251
        Convert string data to language comment
252
 
253
        Argument:
254
        * data: string or list of strings to convert
255
 
256
        Returns: a new string or list of strings with comments added.
257
        """
258
        if isinstance(data, str):
259
            return "-- %s%s" % (data[:-1].replace("\n", "\n-- "), data[-1])
260
        else:
261
            # don't put exception catch. It is an error if data is not
262
            # iterable.
263
            it = iter(data)
264
            retval = []
265
            for s in data:
266
                retval.append("-- %s%s" % (s[:-1].replace("\n", "\n-- "), s[-1]))
267
            return retval
268
 
269
    def add_tab(self, data, level=1):
270
        """
271
        Add an indentation level to the string
272
 
273
        Argument:
274
        * data: string or list of strings
275
        * level: how many indentation levels to add. Default 1
276
 
277
        Returns: string or list of strings with <level> indentation levels.
278
        """
279
        leveltabs = self.usetab*level
280
        if isinstance(data, str):
281
            return "%s%s%s" % (leveltabs, data[:-1].replace("\n", "\n%s" % leveltabs), data[-1])
282
        else:
283
            # don't put exception catch. It is an error if data is not
284
            # iterable.
285
            it = iter(data)
286
            retval = []
287
            for s in data:
288
                retval.append("%s%s%s" % (leveltabs, s[:-1].replace("\n", "\n%s" % leveltabs), s[-1]))
289
            return retval
290
 
291
    def to_valid_str(self, str_in):
292
        """
293
        Convert an input string, changing special characters used on
294
        the HDL language. Useful for set names .
295
 
296
        Argument:
297
        * str_in: string to convert
298
 
299
        Returns: the converted string.
300
        """
301
        # list of transformations:
302
        # * strip spaces
303
        # * space,":" with "_"
304
        s = str_in.strip()
305
        s = s.replace(" ", "_")
306
        s = s.replace(":", "_")
307
        return s
308
 
309
    # codegen model management
310
    def add_generic(self, name, value, description="", **kwargs):
311
        """
312
        Add a generic to the model - with VHDL type formatting.
313
        """
314
        g = noc_codegen_base.add_generic(self, name, value, description, **kwargs)
315
        # Set correct type. String by default
316
        gadd = {"type": "string"}
317
        if "type" in kwargs:
318
            # Optional type must be a string with a custom type name.
319
            if isinstance(kwargs["type"], str):
320
                # custom type name. 
321
                gadd.update({"type": kwargs["type"]})
322
            #else:
323
                # default to use string
324
        else:
325
            gadd["type"] = self.calculate_type(value)
326
            ## use type in value
327
            #gadd["type"] = _pytovhdltype[type(value)]
328
            ## defined range
329
            #if "type_array" in kwargs:
330
                #if isinstance(value, int):
331
                    ## for an integer it means range
332
                    #gadd["type"] += " range %d to %d" % (kwargs["type_array"][0], kwargs["type_array"][1])
333
                #elif isinstance(value, intbv):
334
                    ## for a intbv it means bit boundaries
335
                    #gadd["type"] += "_vector(%d to %d)" % (kwargs["type_array"][0], kwargs["type_array"][1])
336
                ##else:
337
                    ## ignore array range
338
            #else:
339
                ## special case
340
                #if isinstance(value, intbv):
341
                    ## put vector only if necessary
342
                    #if value._nrbits > 1:
343
                        ## extract vector information
344
                        #gadd["type_array"] = [0, value._nrbits - 1]
345
                        #gadd["type"] += "_vector(%d to %d)" % (0, value._nrbits - 1)
346
 
347
        g.update(gadd)
348
        return g
349
 
350
    def add_port(self, name, signal_desc=None, description="", **kwargs):
351
        """
352
        Add a port to the model - with VHDL type formatting.
353
        """
354
        p = noc_codegen_base.add_port(self, name, signal_desc, description, **kwargs)
355
        if signal_desc is None:
356
            # nothing else to do
357
            return p
358
        # set correct type for the particular signal_desc
359
        sig = filter(lambda x: x["name"] == signal_desc["name"], p["signal_list"])
360
        if len(sig) == 0:
361
            # strange error
362
            raise ValueError("Strange error: recently added signal '%s' to port '%s', but signal cannot be found." % (signal_desc["name"], p["name"]))
363
        else:
364
            sig = sig[0]
365
        # type inferring: only apply if signal_desc has empty type
366
        if sig["type"] != "":
367
            return p
368
 
369
        # integer by default
370
        sadd = {"type": "integer"}
371
        if "type" in kwargs:
372
            # Optional type must be a string with a custom type name.
373
            if isinstance(kwargs["type"], str):
374
                # custom type name. 
375
                sadd["type"] = kwargs["type"]
376
                # also add if it has default_value
377
                if "default_value" in kwargs:
378
                    sadd["default_value"] = kwargs["default_value"]
379
            #else:
380
                # default to use integer
381
        else:
382
            sadd["type"] = self.calculate_type(sig["default_value"])
383
            ## use type in value
384
            #sadd["type"] = _pytovhdltype[type(sig["default_value"])]
385
            ## defined range
386
            #if "type_array" in kwargs:
387
                #if isinstance(sig["default_value"], int):
388
                    #sadd["type"] += " range(%d to %d)" % (kwargs["type_array"][0], kwargs["type_array"][1])
389
                #elif isinstance(sig["default_value"], intbv):
390
                    #sadd["type"] += "_vector(%d downto %d)" % (kwargs["type_array"][1], kwargs["type_array"][0])
391
                ##else:
392
                    ## ignore array range
393
            #else:
394
                ## special case
395
                #if isinstance(sig["default_value"], intbv):
396
                    ## put vector only if necessary
397
                    #if sig["default_value"]._nrbits > 1:
398
                        ## extract vector information
399
                        #sadd["type_array"] = [0, sig["default_value"]._nrbits - 1]
400
                        #sadd["type"] += "_vector(%d downto 0)" % (sig["default_value"]._nrbits - 1)
401
        sig.update(sadd)
402
        return p
403
 
404
    def add_external_signal(self, name, direction, value, description="", **kwargs):
405
        """
406
        Add a external signal to the model - with VHDL type formatting.
407
        """
408
        sig = noc_codegen_base.add_external_signal(self, name, direction, value, description, **kwargs)
409
        # Set correct type. integer by default
410
        sadd = {"type": "integer"}
411
        if "type" in kwargs:
412
            # Optional type must be a string with a custom type name.
413
            if isinstance(kwargs["type"], str):
414
                # custom type name. 
415
                sadd.update({"type": kwargs["type"]})
416
            #else:
417
                # default to use integer
418
        else:
419
            sadd["type"] = self.calculate_type(value)
420
            ## use type in value
421
            #sadd["type"] = _pytovhdltype[type(value)]
422
            ## defined range
423
            #if "type_array" in kwargs:
424
                #if isinstance(value, int):
425
                    #sadd["type"] += " range(%d to %d)" % (kwargs["type_array"][0], kwargs["type_array"][1])
426
                #elif isinstance(value, intbv):
427
                    #sadd["type"] += "_vector(%d to %d)" % (kwargs["type_array"][0], kwargs["type_array"][1])
428
                ##else:
429
                    ## ignore array range
430
            #else:
431
                ## special case
432
                #if isinstance(value, intbv):
433
                    ## extract vector information
434
                    #sadd["type_array"] = [0, sig["default_value"]._nrbits - 1]
435
                    #if None not in sadd["type_array"]:
436
                        #sadd["type"] += "_vector(%d to %d)" % (0, sig["default_value"]._nrbits - 1)
437
        sig.update(sadd)
438
        return sig
439
 
440
    # particular VHDL methods
441
    def generate_entity_section(self):
442
        """
443
        Generate entity section
444
        """
445
        sret = "entity %s is\n" % self.modulename
446
        stmp = self.generate_generics_section()
447
        if stmp != "":
448
            sret += self.add_tab(stmp) + ";\n"
449
        stmp = self.generate_ports_section()
450
        if stmp != "":
451
            sret += self.add_tab(stmp) + ";\n"
452
        sret += "end %s;\n" % self.modulename
453
        return sret
454
 
455
    def generate_architecture_section(self, archname="rtl"):
456
        """
457
        Generate architecture section
458
        """
459
        sret = "architecture %s of %s is\n" % (archname, self.modulename)
460
        if self.implementation != "":
461
            sret += self.add_tab(self.implementation)
462
        sret += "\nend %s;\n" % archname
463
        return sret
464
 
465
    def generate_generics_section(self):
466
        """
467
        Generate generics section used on entity and component
468
        """
469
        sret = "generic (\n"
470
        l = self.generate_generic_declaration(None, True)
471
        if len(l) > 0:
472
            l = self.add_tab(l)
473
            sret += ";\n".join(l)
474
            sret += "\n)"
475
        else:
476
            # empty generics section
477
            sret = ""
478
        return sret
479
 
480
    def generate_ports_section(self):
481
        """
482
        Generate ports section used on entity and component
483
        """
484
        # first ports and then external signals
485
        sret = "port (\n"
486
        l = self.generate_port_declaration(None, False)
487
        l.extend(self.generate_signal_declaration(None, None, False))
488
        if len(l) > 0:
489
            l = self.add_tab(l)
490
            sret += ";\n".join(l)
491
            sret += "\n)"
492
        else:
493
            # empty ports section
494
            sret = ""
495
        return sret
496
 
497
    def calculate_type(self, object, with_default=False):
498
        """
499
        Calculate the correct VHDL type for a particular object
500
 
501
        Arguments:
502
        * object:
503
        * with_default: True to add a default value at the end
504
        Returns:
505
        A string with the VHDL equivalent type
506
        """
507
        if isinstance(object, bool):
508
            return "std_logic"
509
        elif isinstance(object, (int, long)):
510
            # CHECK if this is a correct type translation
511
            return "integer"
512
        elif isinstance(object, str):
513
            return "string"
514
        elif isinstance(object, myhdl.intbv):
515
            width = object._nrbits
516
            initval = object._val
517
        elif isinstance(object, myhdl.SignalType):
518
            width = object._nrbits
519
            initval = object._init
520
        else:
521
            raise ValueError("Type conversion for object %s not found (type: %s)" % (repr(object), type(object)))
522
        if width <= 0:
523
            raise ValueError("Object %s don't have a valid bit width." % repr(object))
524
        if width == 1:
525
            retval = "std_logic"
526
            defval = "'%d'" % int(initval)
527
        else:
528
            retval = "unsigned(%s downto 0)" % (width - 1)
529
            defval = '"%s"' % myhdl.bin(initval, width)
530
        if with_default:
531
            return "%s := %s" % (retval, defval)
532
        else:
533
            return retval
534
 
535
# helper functions
536
def add_codegen_vhdl_support(instance, **kwargs):
537
    """
538
    This function will add for every object in instance a noc_codegen object
539
    """
540
    if isinstance(instance, noc):
541
        # add code generation object
542
        instance.codegen = noc_codegen_vhdl(instance, **kwargs)
543
        # and add cg objects recursively
544
        for obj in instance.all_list(True):
545
            add_codegen_vhdl_support(obj, **kwargs)
546
    elif isinstance(instance, (ipcore, router, channel)):
547
        instance.codegen = noc_codegen_vhdl(instance, **kwargs)
548
    else:
549
        raise TypeError("Unsupported object: type %s" % type(instance))
550
 
551
def convert_value(data, custom_type=None, type_array=[None, None]):
552
    """
553
    This function converts data to a string valid in VHDL syntax.
554
    """
555
    retval = ""
556
    numbits = 0
557
    usedata = data
558
    if custom_type is not None:
559
        # custom type
560
        if custom_type == int:
561
            usedata = int(data)
562
        elif custom_type == bool:
563
            usedata = data == True
564
        elif custom_type == intbv:
565
            # need fixed boundaries. Use type_array
566
            if type_array == [None, None]:
567
                # default: one bit width
568
                usedata = intbv(data)[1:]
569
            else:
570
                # intbv boundaries refer to bit width
571
                usedata = intbv(data)[type_array[1]+1:type_array[0]]
572
        elif custom_type == str:
573
            usedata = str(data)
574
        elif isinstance(custom_type, str):
575
            # type in a string format
576
            if custom_type == "integer":
577
                usedata = int(data)
578
            elif custom_type == "std_logic":
579
                usedata = intbv(data)[1:]
580
            elif custom_type == "string":
581
                usedata = str(data)
582
            elif custom_type.find("std_logic_vector") == 0:
583
                # strip values
584
                vecinfo = custom_type.replace("std_logic_vector", "").strip("()").split()
585
                if vecinfo[1] == "to":
586
                    vecmin = int(vecinfo[0])
587
                    vecmax = int(vecinfo[2])
588
                elif vecinfo[1] == "downto":
589
                    vecmin = int(vecinfo[2])
590
                    vecmax = int(vecinfo[0])
591
                usedata = intbv(data)[vecmax+1:vecmin]
592
            else:
593
                raise TypeError("Unsupported custom type string '%s' for VHDL conversion." % custom_type)
594
        else:
595
            raise TypeError("Unsupported custom type '%s' for VHDL conversion." % type(custom_type))
596
    # convert
597
    if isinstance(usedata, int):
598
        retval = "%d" % data
599
    elif isinstance(usedata, bool):
600
        retval = "'%d'" % int(data)
601
    elif isinstance(usedata, intbv):
602
        # check boundaries
603
        if usedata._nrbits > 1:
604
            # print vector in bits
605
            retval = '"%s"' % bin(usedata, usedata._nrbits)
606
        else:
607
            retval = "'%d'" % usedata
608
    elif isinstance(usedata, str):
609
        retval = '"%s"' % usedata
610
    else:
611
        raise TypeError("Unsupported type '%s' for VHDL conversion." % type(usedata))
612
    return retval
613
 
614
# helper structures:
615
# type conversions
616
_pytovhdltype = {
617
    "bool": "std_logic",
618
    "int": "integer",
619
    "intbv" : "unsigned",
620
    "str" : "string",
621
    bool: "std_logic",
622
    int: "integer",
623
    intbv : "unsigned",
624
    str : "string"
625
    }
626
_pytovhdlzeroval = {
627
    "bool": "False",
628
    "int": "0",
629
    "intbv" : '"0"',
630
    "str" : '""',
631
    bool: "False",
632
    int: "0",
633
    intbv : '"0"',
634
    str : '""'
635
    }
636
 

powered by: WebSVN 2.1.0

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