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

Subversion Repositories nocmodel

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 dargor
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
 
4
#
5
# NoC Base Objects
6
#
7
# Author:  Oscar Diaz
8 4 dargor
# Version: 0.2
9
# Date:    05-07-2012
10 2 dargor
 
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
# 03-03-2011 : (OD) initial release
32 4 dargor
# 05-07-2012 : (OD) intercon class, major changes, various bugfixes
33 2 dargor
#
34
 
35
"""
36 4 dargor
=====================
37 2 dargor
NoCmodel Base Objects
38 4 dargor
=====================
39 2 dargor
 
40
This module declares classes used on a Network-on-chip representation:
41
 
42
* NoC container class
43
    * Router base class
44
    * Channel base class
45
    * IPCore base class
46 4 dargor
    * Intercon base class
47 2 dargor
    * Protocol base class
48
    * Packet class
49
"""
50
 
51
import networkx as nx
52 4 dargor
from myhdl import Signal, SignalType, intbv, bin
53
from collections import OrderedDict
54
from math import ceil as mathceil
55 2 dargor
 
56
class noc(nx.Graph):
57
    """
58
    Base class for NoC modeling.
59
    Based on a Graph object that hold the NoC structure
60
 
61
    Arguments
62
    * kwargs: optional parameters to put as object attributes
63
    """
64
    def __init__(self, **kwargs):
65
        """
66
        NoCmodel constructor
67
        """
68
        nx.Graph.__init__(self, **kwargs)
69 4 dargor
        if not hasattr(self, "name"):
70
            self.name = ""
71
        if not hasattr(self, "description"):
72
            self.description = ""
73
 
74
    def __repr__(self):
75
        if self.name != "":
76
            return "<%s '%s'>" % (self.__class__.__name__, self.name)
77
        else:
78
            return "<%s at '%d'>" % (self.__class__.__name__, id(self))
79 2 dargor
 
80
    # objects management functions
81
    def add_router(self, name="", with_ipcore=False, **kwargs):
82
        """
83
        Create a base router object and add it to NoC model.
84
 
85
        Arguments
86
        * name: optional name for this router. By default has the form of
87
          "R_<index>"
88
        * with_ipcore: If True, add an ipcore to the created router.
89
        * kwargs: optional parameters to put as object attributes
90
 
91
        Return: reference to the created router object
92
        """
93
        #nodeidx = self._get_next_nodeidx()
94
        routernode = router(index=None, name=name, graph_ref=self, **kwargs)
95
        retval = self.add_router_from_object(routernode)
96
        if name == "":
97
            retval.name = "R_%d" % retval.index
98
        if with_ipcore:
99
            # kwargs are reserved for router creation, not for ipcore.
100
            self.add_ipcore(retval)
101
        return retval
102
 
103
    def add_router_from_object(self, router_ref):
104
        """
105
        Add an existing router object to NoC model.
106
 
107
        Use this function to add an object based on a derived class of
108
        base router defined in this module.
109
 
110
        Arguments
111
        * router_ref: reference to the router object
112
 
113
        Return: the same reference passed in router_ref
114
 
115
        Notes:
116
        * This function will change router_ref.index and router_ref.graph_ref
117
          attributes when inserted in the NoC model.
118
        """
119
        if not isinstance(router_ref, router):
120
            raise ValueError("Argument 'router_ref' is not a router object.")
121
 
122
        router_ref.index = self._get_next_nodeidx()
123
        router_ref.graph_ref = self
124
        # don't forget that index is used for address
125
        router_ref.address = router_ref.index
126
        self.add_node(router_ref.index, router_ref=router_ref)
127
        return router_ref
128
 
129
    def add_channel(self, router1, router2, name="", **kwargs):
130
        """
131
        Create a base channel object to link two objects and add it
132
        to NoC model.
133
 
134
        Arguments:
135
        * router1: reference to a router, router index or ipcore
136
        * router2: -idem-
137
        * name: optional argument for channel name
138
        * kwargs: optional parameters to put as object attributes
139
 
140
        Notes:
141
        * If router1 or router2 is an ipcore reference, this method creates
142
          the special channel object and update ipcore references. Additionally,
143
          if both arguments are ipcores, throw an error exception
144
        """
145
        if isinstance(router1, ipcore) and isinstance(router2, ipcore):
146
            raise ValueError("Both object references cannot be ipcore objects.")
147
 
148
        rhash = [None, None]
149
        rrefs = [None, None]
150
        for targetid, routertarget in enumerate((router1, router2)):
151
            if isinstance(routertarget, router):
152
                if routertarget.index in self.node:
153
                    rhash[targetid] = routertarget.index
154
                    rrefs[targetid] = self.node[routertarget.index]["router_ref"]
155
            elif isinstance(routertarget, ipcore):
156
                # special channel
157
                rhash[targetid] = None
158
                rrefs[targetid] = routertarget
159
            elif isinstance(routertarget, int):
160
                if routertarget in self.node:
161
                    rhash[targetid] = routertarget
162
                    rrefs[targetid] = self.node[routertarget]["router_ref"]
163
 
164
        if rrefs[0] is None:
165
            raise ValueError("Object not found for argument 'router1'")
166
        if rrefs[1] is None:
167
            raise ValueError("Object not found for argument 'router2'")
168
 
169
        if None in rhash:
170
            ipcore_idx = rhash.index(None)
171
            # ipcore channel
172
            if name == "":
173
                # channel default name format 
174
                name = "CH_IP_%s" % rrefs[ipcore_idx].name
175
            channelnode = channel(index=None, name=name, graph_ref=self, **kwargs)
176
        else:
177
            # inter-routers channel
178
            if name == "":
179
                # channel default name format 
180
                name = "CH_%s:%s" % (rrefs[0].name, rrefs[1].name)
181
            channelnode = channel(index=self._get_next_edgeidx(), name=name, graph_ref=self, **kwargs)
182
            self.add_edge(rhash[0], rhash[1], channel_ref = channelnode)
183
        channelnode.endpoints = rrefs
184
        return channelnode
185
 
186
    def add_from_channel(self, channel_ref, router1=None, router2=None):
187
        """
188
        Add a channel object to NoC model.
189
 
190
        Use this function to add an object based on a derived class of
191
        base channel defined in this module.
192
 
193
        Arguments
194
        * channel_ref: reference to the channel object
195
        * router1: optional reference to a router, router index or ipcore
196
        * router2: -idem-
197
 
198
        Return: the same reference passed in channel_ref
199
 
200
        Notes:
201
        * If router1 or router2 are not used as arguments, will assume that
202
          channel object has defined its attribute "endpoints" with the
203
          objects to connect. If the objects don't exist in the NoC model,
204
          throw an error exception.
205
 
206
        * If router1 or router2 is an ipcore reference, this method creates
207
          the special channel object and update ipcore references. Additionally,
208
          if both arguments are ipcores, throw an error exception.
209
 
210
        * This function will change channel_ref.index and channel_ref.graph_ref
211
          attributes when inserted in the NoC model. Also it may change
212
          channel_ref.endpoints with router1 and router2 references.
213
        """
214
        if not isinstance(channel_ref, channel):
215
            raise ValueError("Argument 'channel_ref' is not a channel object.")
216
 
217
        if isinstance(router1, ipcore) and isinstance(router2, ipcore):
218
            raise ValueError("Both object references cannot be ipcore objects.")
219
 
220
        rhash = [None, None]
221
        rrefs = [None, None]
222
        for targetid, routertarget in enumerate((router1, router2)):
223
            if isinstance(routertarget, router):
224
                if routertarget.index in self.node:
225
                    rhash[targetid] = routertarget.index
226
                    rrefs[targetid] = self.node[routertarget.index]["router_ref"]
227
            elif isinstance(routertarget, ipcore):
228
                # special channel
229
                rhash[targetid] = None
230
                rrefs[targetid] = routertarget
231
            elif isinstance(routertarget, int):
232
                if routertarget in self.node:
233
                    rhash[targetid] = routertarget
234
                    rrefs[targetid] = self.node[routertarget]["router_ref"]
235
 
236
        if (router1 is None) and (router2 is None):
237
            # extract from endpoints attribute
238
            if not hasattr(channel_ref, "endpoints"):
239
                raise ValueError("Channel object has not attribute 'endpoints'")
240
            for i in range(2):
241
                if not isinstance(channel_ref.endpoints[i], [router, ipcore]):
242
                    raise ValueError("Channel object: attribute 'endpoints'[%d] is not a router or an ipcore" % i)
243
                if isinstance(channel_ref.endpoints[i], router):
244
                    if channel_ref.endpoints[i].index in self.node:
245
                        rhash[i] = channel_ref.endpoints[i].index
246
                        rrefs[i] = channel_ref.endpoints[i]
247
                if isinstance(channel_ref.endpoints[i], ipcore):
248
                    rhash[i] = None
249
                    rrefs[i] = channel_ref.endpoints[i]
250
 
251
        if rrefs[0] is None:
252
            raise ValueError("Object not found for argument 'router1'")
253
        if rrefs[1] is None:
254
            raise ValueError("Object not found for argument 'router2'")
255
 
256
        if None in rhash:
257
            ipcore_idx = rhash.index(None)
258
            channel_ref.index = None
259
            # ipcore channel: adjust the references
260
            rrefs[ipcore_idx].channel_ref = channel_ref
261
            # the other reference must be a router object
262
            rrefs[ipcore_idx - 1].ipcore_ref = rrefs[ipcore_idx]
263
        else:
264
            # inter-routers channel
265
            channel_ref.index = self._get_next_edgeidx()
266
            self.add_edge(rhash[0], rhash[1], channel_ref=channel_ref)
267
        # update common references
268
        channel_ref.graph_ref = self
269
        channel_ref.endpoints = rrefs
270
        return channel_ref
271
 
272
 
273
    def add_ipcore(self, router_ref, name="", **kwargs):
274
        """
275
        Create an ipcore object and connect it to the router reference argument
276
 
277
        Arguments
278
        * router_ref: reference to an existing router
279
        * name: optional name for this router. By default has the form of
280
          "IP_<router_index>"
281
        * kwargs: optional parameters to put as object attributes
282
 
283
        Return: reference to the created ipcore object
284
 
285
        Notes:
286
        * This function automatically adds a special channel object
287
          (router-to-ipcore) and adjust its relations in all involved objects.
288
        """
289
        if router_ref not in self.router_list():
290
            raise ValueError("Argument 'router_ref' must be an existing router.")
291
        if name == "":
292
            # channel default name format 
293
            name = "IP_%d" % router_ref.index
294
        # fix channel name, based on ipcore name
295
        chname = "CH_%s" % name
296
        newip = ipcore(name=name, router_ref=router_ref, graph_ref=self, **kwargs)
297
        channelnode = channel(index=None, name=chname, graph_ref=self, endpoints=[router_ref, newip])
298
        # fix references
299
        newip.channel_ref = channelnode
300
        router_ref.ipcore_ref = newip
301
        return newip
302
 
303
    def add_from_ipcore(self, ipcore_ref, router_ref, channel_ref=None):
304
        """
305
        Add a ipcore object to NoC model.
306
 
307
        Arguments
308
        * ipcore_ref: reference to ipcore object
309
        * router_ref: reference to an existing router to connect
310
        * channel_ref: optional channel object that connect the router and
311
          the ipcore. If not used, this function will create a new channel object.
312
 
313
        Return: the same reference passed in ipcore_ref
314
 
315
        Notes:
316
        * This function automatically adds a special channel object
317
          (router-to-ipcore) and adjust its relations in all involved objects.
318
        """
319
        if not isinstance(ipcore_ref, ipcore):
320
            raise ValueError("Argument 'ipcore_ref' is not an ipcore object.")
321
        if router_ref not in self.router_list():
322
            raise ValueError("Argument 'router_ref' must be an existing router.")
323
 
324
        if channel_ref != None:
325
            if not isinstance(channel_ref, channel):
326
                raise ValueError("Argument 'channel_ref' is not a channel object.")
327
            else:
328
                channel_ref.index = None
329
                channel_ref.graph_ref = self
330
                channel_ref.endpoints = [router_ref, ipcore_ref]
331
        else:
332
            # channel default name format 
333
            chname = "CH_IP_%d" % router_ref.index
334
            channel_ref = channel(index=None, name=chname, graph_ref=self, endpoints=[router_ref, ipcore_ref])
335
 
336
        # fix references
337
        ipcore_ref.router_ref = router_ref
338
        ipcore_ref.channel_ref = channel_ref
339
        ipcore_ref.graph_ref = self
340
        router_ref.ipcore_ref = ipcore_ref
341
 
342
        return ipcore_ref
343
 
344
    def del_router(self, router_ref):
345
        """
346
        Remove router_ref from the NoC model
347
 
348
        TODO: not implemented
349
        """
350
        pass
351
 
352
    def del_channel(self, channel_ref):
353
        """
354
        Remove channel_ref from the NoC model
355
 
356
        TODO: not implemented
357
        """
358
        pass
359
 
360
    def del_ipcore(self, ipcore_ref):
361
        """
362
        Remove ipcore_ref from the NoC model
363
 
364
        TODO: not implemented
365
        """
366
        pass
367
 
368
    # list generation functions
369
    def router_list(self):
370
        l = []
371
        for i in self.nodes_iter(data=True):
372 4 dargor
            r = i[1].get("router_ref", None)
373
            if r is not None:
374
                l.append(r)
375 2 dargor
        return l
376
 
377
    def ipcore_list(self):
378
        l = []
379 4 dargor
        for i in self.router_list():
380
            ip = getattr(i, "ipcore_ref", None)
381
            if ip is not None:
382
                l.append(ip)
383 2 dargor
        return l
384
 
385 4 dargor
    def channel_list(self, with_ipcore_channel=False):
386 2 dargor
        l = []
387
        for i in self.edges_iter(data=True):
388 4 dargor
            ch = i[2].get("channel_ref", None)
389
            if ch is not None:
390
                l.append(ch)
391
        if with_ipcore_channel:
392
            for i in self.ipcore_list():
393
                ch = getattr(i, "channel_ref", None)
394
                if ch is not None:
395
                    l.append(ch)
396 2 dargor
        return l
397 4 dargor
 
398
    def all_list(self, with_ipcore_channel=False):
399 2 dargor
        l = self.router_list()
400
        l.extend(self.ipcore_list())
401 4 dargor
        l.extend(self.channel_list(with_ipcore_channel))
402 2 dargor
        return l
403
 
404
    # query functions
405
    def get_router_by_address(self, address):
406
        for r in self.router_list():
407
            if r.address == address:
408
                return r
409
        return False
410 4 dargor
 
411
    # update functions
412
    def update_nocdata(self):
413
        for r in self.router_list():
414
            r.update_ports_info()
415
            r.update_routes_info()
416
        for ch in self.channel_list(True):
417
            ch.update_ports_info()
418 2 dargor
 
419
    # hidden functions
420
    def _add_router_from_node(self, node, name="", router_ref=None, **kwargs):
421
        """
422
        Create a router object (or use an existing router reference) based on
423
        an existing empty node on graph object.
424
        """
425
        if router_ref is None:
426
            # index comes from node
427
            if name == "":
428 4 dargor
                name = "R_%s" % repr(node)
429 2 dargor
            routernode = router(index=node, name=name, graph_ref=self, **kwargs)
430
        else:
431
            if not isinstance(router_ref, router):
432
                raise ValueError("Argument 'router_ref' is not a router object.")
433
            routernode = router_ref
434
            routernode.index = node
435
            routernode.graph_ref = self
436
        self.node[node]["router_ref"] = routernode
437
        return routernode
438
 
439
    def _add_channel_from_edge(self, edge, name="", channel_ref=None, **kwargs):
440
        """
441
        Create a channel object (or use an existing channel reference) based
442
        on an existing edge on graph object.
443
        """
444
        # inter-routers channels only
445
        rrefs = [self.node[edge[0]]["router_ref"], self.node[edge[1]]["router_ref"]]
446
        chindex = self.edges().index(edge)
447
        if channel_ref is None:
448
            if name == "":
449
                # channel default name format 
450
                name = "CH_%s:%s" % (rrefs[0].name, rrefs[1].name)
451
            channelnode = channel(index=chindex, name=name, graph_ref=self, **kwargs)
452
        else:
453
            if not isinstance(channel_ref, channel):
454
                raise ValueError("Argument 'channel_ref' is not a channel object.")
455
            channelnode = channel_ref
456
            channelnode.index = chindex
457
            channelnode.graph_ref = self
458
        channelnode.endpoints = rrefs
459
        self.get_edge_data(edge[0], edge[1])["channel_ref"] = channelnode
460
 
461
        return channelnode
462
 
463
    def _get_next_nodeidx(self):
464
        # get the next node index number
465
        # don't use intermediate available indexes
466
        return len(self.nodes())
467
 
468
    def _get_next_edgeidx(self):
469
        # get the next edge index number
470
        # don't use intermediate available indexes
471
        return len(self.edges())
472
 
473
# *******************************
474
# Generic models for NoC elements
475
# *******************************
476
 
477
class nocobject():
478
    """
479
    NoC base object
480
 
481
    This base class is used to implement common methods for NoC objects.
482
    Don't use directly.
483
    """
484 4 dargor
    name = ""
485
    description = ""
486
 
487
    def __repr__(self):
488
        if self.name != "":
489
            return "<%s '%s'>" % (self.__class__.__name__, self.name)
490
        else:
491
            return "<%s at '%d'>" % (self.__class__.__name__, id(self))
492
 
493 2 dargor
    def get_protocol_ref(self):
494
        """
495
        Get protocol object for this instance
496
        """
497
        if hasattr(self, "protocol_ref"):
498
            if isinstance(self.protocol_ref, protocol):
499
                return self.protocol_ref
500
        if isinstance(self.graph_ref.protocol_ref, protocol):
501
            return self.graph_ref.protocol_ref
502
        # nothing?
503
        return None
504
 
505 4 dargor
    def get_address(self):
506
        """
507
        Get address related to this object. If it is a router or a ipcore,
508
        returns the address in the router. If it is a channel, return a list
509
        or pair addresses from its endpoints. If it is another object without
510
        address, return None.
511
        """
512
        if hasattr(self, "address"):
513
            return self.address
514
        else:
515
            # try ipcore
516
            if isinstance(self, ipcore):
517
                return self.router_ref.address
518
            # try channel
519
            if isinstance(self, channel):
520
                return [x.get_address() for x in self.endpoints]
521
            # nothing?
522
            return None
523
 
524 2 dargor
class ipcore(nocobject):
525
    """
526
    IP core base object
527
 
528
    This object represents a IP Core object and its properties. This base class
529
    is meant to either be inherited or extended by adding other attributes.
530
 
531
    Relations with other objects:
532
    * It should be related to one NoC model object (self.graph_ref)
533
    * It should have one reference to a router object (self.router_ref), even if
534
      the ipcore has a direct connection to the NoC.
535
    * It should have one reference to a channel object (self.channel_ref). This
536
      channel exclusively link this ipcore and its router.
537
 
538
    Attributes:
539
    * name
540
    * router_ref: optional reference to its related router.
541
    * channel_ref: optional reference to its related channel
542
    * graph_ref: optional reference to its graph model
543
    """
544
    def __init__(self, name, **kwargs):
545
        # Basic properties
546
        self.name = name
547
        # default values
548
        self.router_ref = None
549
        self.channel_ref = None
550
        self.graph_ref = None
551
        for key in kwargs.keys():
552
            setattr(self, key, kwargs[key])
553 4 dargor
        # ports structure
554
        self.ports = {}
555
 
556
    # update functions: call them when the underlying NoC structure
557
    # has changed
558
    def update_ports_info(self):
559
        """
560
        Update the dictionary "ports". For an ipcore, it only has one element
561
        to its router.
562
 
563
        Ports dictionary has the following structure:
564
        * key: address of its related router
565
        * value: dictionary with the following keys:
566
            * "peer" (required): reference to its router
567
            * "channel" (required): reference to the channel that connects this
568
              ipcore and its router.
569
            * Optional keys can be added to this dictionary with the same
570
              meaning as other ports.
571
        """
572
        myaddr = self.get_address()
573
        self.ports = {myaddr: {}}
574
        self.ports[myaddr]["peer"] = self.router_ref
575
        self.ports[myaddr]["channel"] = self.channel_ref
576 2 dargor
 
577
class router(nocobject):
578
    """
579
    Router base object
580
 
581
    This object represents a router object and its properties. This base class
582
    is meant to either be inherited or extended by adding other attributes.
583
 
584
    Relations with other objects:
585
    * It should be related to one NoC model object (self.graph_ref)
586
    * It should be one of the node attributes in the graph model
587
      (node["router_ref"])
588
    * It may have one reference to an ipcore object (self.ipcore_ref)
589
    * It has a port list with relations to other routers through channel objects,
590
      and only one relation to its ipcore object through one channel.
591
      (self.ports). Note that this attribute must be updated after changing
592
      the NoC model.
593
 
594
    Attributes:
595
    * index: index on a noc object. Essential to search for a router object
596
    * name
597
    * ipcore_ref: optional reference to its related ipcore
598
    * graph_ref: optional reference to its graph model
599
    """
600
    def __init__(self, index, name, **kwargs):
601
        # Basic properties
602
        self.index = index
603
        self.name = name
604
        # default values
605
        self.ipcore_ref = None
606
        self.graph_ref = None
607
        # address can be anything, but let use index by default
608
        # note that address can be overriden with optional arguments in kwargs
609
        self.address = index
610
        for key in kwargs.keys():
611
            setattr(self, key, kwargs[key])
612
        # ports structure
613
        self.ports = {}
614
        # available routes info
615
        self.routes_info = {}
616
 
617
    # update functions: call them when the underlying NoC structure
618
    # has changed
619
    def update_ports_info(self):
620
        """
621
        Update the dictionary "ports": information about router neighbors,
622
        the channels that connect them and its references.
623
 
624
        Ports dictionary has the following structure:
625
        * key: address of the neighbor router that this port connects.
626
        * value: dictionary with the following keys:
627
            * "peer" (required): reference to the neighbor router
628
            * "channel" (required): reference to the channel that connects this
629
              router and its neighbor router.
630
            * Optional keys can be added to this dictionary.
631
        * Also, the special key "local address" holds the port to
632
          router's ipcore. Its values are:
633
            * "peer" (required): reference to its ipcore
634
            * "channel" (required): reference to the channel that connects this
635
              router and its ipcore.
636
            * Optional keys can be added to this dictionary with the same
637
              meaning as other ports.
638
        """
639
        # port definitions
640
        localhash = self.address
641
        updated_addr = [self.address]
642
        for neighborhash in self.graph_ref.neighbors(localhash):
643
            neighbor = self.graph_ref.node[neighborhash]["router_ref"]
644
            #check if already defined in ports dictionary
645
            if neighbor.address not in self.ports:
646
                self.ports[neighbor.address] = {}
647
            # update relevant data
648
            self.ports[neighbor.address]["peer"] = neighbor
649
            ch_ref = self.graph_ref.edge[localhash][neighborhash]["channel_ref"]
650
            self.ports[neighbor.address]["channel"] = ch_ref
651
            updated_addr.append(neighbor.address)
652
 
653
        # special port: ipcore
654
        if self.address not in self.ports:
655
            self.ports[self.address] = {}
656
        self.ports[self.address]["peer"] = self.ipcore_ref
657
        # take channel reference from ipcore. Other channels are related to
658
        # an edge on the graph model. Channels in an ipcore are special because
659
        # they don't have a related edge, just link an ipcore and this router
660
        self.ports[self.address]["channel"] = self.ipcore_ref.channel_ref
661
 
662
        # clean 'deleted' ports
663
        keys = self.ports.iterkeys()
664
        for deleted in keys:
665
            if deleted not in updated_addr:
666
                del self.ports[deleted]
667
 
668
    def update_routes_info(self):
669
        """
670
        Update the dictionary "routes_info": it is a table with information
671
        about how a package, starting from this router, can reach another one.
672
 
673
        routes_info dictionary has the following structure:
674
        * keys : the address of all the routers in NoC
675
        * values : an ordered list of dictionaries with
676
            * "next" : address of the next router
677
            * "paths" : list of possible paths for key destination
678
 
679
        """
680
        # this function will calculate a new table!
681
        self.routes_info.clear()
682
 
683
        #mynodehash = (self.coord_x, self.coord_y)
684
        mynodehash = self.index
685
 
686
        for destrouter in self.graph_ref.router_list():
687
            # discard route to myself
688
            if destrouter == self:
689
                continue
690
            #desthash = (destrouter.coord_x, destrouter.coord_y)
691
            desthash = destrouter.index
692
 
693
            # entry for destrouter
694
            self.routes_info[destrouter.index] = []
695
 
696
            # first: take all shortest paths (function not available on NetworkX)
697
            shortest_routes = all_shortest_paths(self.graph_ref, mynodehash, desthash)
698
            # convert nodehashes to router addresses
699
            shortest_r_addr = [map(lambda x : self.graph_ref.node[x]["router_ref"].address, i) for i in shortest_routes]
700
 
701
            # NOTE about routing tables: need to think about which routes based on 
702
            # shortest paths are better in general with other routers, so the links
703
            # are well balanced. A possible problem could be that some links will carry
704
            # more data flow than others.
705
            # A possible workaround lies in the generation of routing tables at
706
            # NoC level, taking account of neighbors tables and others parameters.
707
 
708
            # extract the next neighbor in each path
709
            for route in shortest_r_addr:
710
                # first element is myself, last element is its destination. 
711
                # for this routing table, we only need the next router to
712
                # send the package.
713
                newroute = True
714
                for route_entry in self.routes_info[destrouter.index]:
715
                    if route[1] == route_entry["next"]:
716
                        # another route which next element was taken account
717
                        route_entry["paths"].append(route)
718
                        newroute = False
719
                if newroute:
720
                    self.routes_info[destrouter.index].append({"next": route[1], "paths": [route]})
721
            # last option: send through another node not in the shortest paths 
722
            # NOTE: decide if this is needed or make sense
723
 
724
class channel(nocobject):
725
    """
726
    Channel base object
727
 
728
    This object represents a channel object and its properties. This base class
729
    is meant to either be inherited or extended by adding other attributes.
730
 
731
    Relations with other objects:
732
    * It should be related to one NoC model object (self.graph_ref)
733
    * It may be one of the edge attributes in the graph model
734
      (edge["channel_ref"]). In this case, this channel connects two routers
735
      in the NoC model.
736
    * It should have two references to NoC objects (self.endpoints). Two options
737
      exists: two routers references (channel has an edge object) or, one
738
      router and one ipcore (channel don't have any edge object).
739
 
740
    Attributes:
741 4 dargor
    * name :
742
    * index : optional index on a noc object. Must have an index when it has
743 2 dargor
      a related edge in the graph model (and allowing it to be able to do
744
      channel searching). None means it is an ipcore related channel
745 4 dargor
    * graph_ref : optional reference to its graph model
746
    * endpoints : optional two-item list with references to the connected objects
747
    * intercon_class : optional reference to a intercon class used in this channel.
748 2 dargor
    """
749
    def __init__(self, name, index=None, **kwargs):
750
        # Basic properties
751
        self.index = index
752
        self.name = name
753
        # Default values
754
        self.graph_ref = None
755
        self.endpoints = [None, None]
756 4 dargor
        self.intercon_class = intercon
757
        self.intercon_class_defargs = {}
758 2 dargor
        for key in kwargs.keys():
759
            setattr(self, key, kwargs[key])
760 4 dargor
        # ports structure
761
        self.ports = {}
762
 
763
    # update functions: call them when the underlying NoC structure
764
    # has changed
765
    def update_ports_info(self, intercon_class=None):
766
        """
767
        Update the dictionary "ports": For a channel, there is two
768
        elements referencing both connected objects
769
 
770
        Ports dictionary has the following structure:
771
        * key: address of the router related with this channel, or None
772
            for a ipcore object.
773
        * value: dictionary with the following keys:
774
            * "peer" (required): reference to the router or ipcore
775
            * "channel" (required): reference to self
776
            * "intercon" (required for RTL sim and codegen)
777
            * Optional keys can be added to this dictionary.
778 2 dargor
 
779 4 dargor
        Arguments :
780
        * intercon_class : class reference to generate intercon instances
781
        NOTE:
782
        * This update will change and DESTROY existing intercon objects.
783
        """
784
        if intercon_class is None:
785
            intercon_class = self.intercon_class
786
        if not issubclass(intercon_class,  intercon):
787
            raise TypeError("intercon_class must be subclass of intercon.")
788
        self.ports = {}
789
        for endp in self.endpoints:
790
            if isinstance(endp, ipcore):
791
                idx = None
792
            elif isinstance(endp, router):
793
                idx = endp.get_address()
794
            else:
795
                raise ValueError("endpoints has an inconsistent state (%s)." % repr(endp))
796
            self.ports[idx] = {}
797
            self.ports[idx]["peer"] = endp
798
            self.ports[idx]["channel"] = self
799
            # TEMPORAL WORKAROUND: use intercon_class.complement on ipcore side
800
            # ONLY on case of ipcore channels.
801
            if idx == None and hasattr(intercon_class, "complement"):
802
                self.ports[idx]["intercon"] = intercon_class.complement(name=endp.name, **self.intercon_class_defargs)
803
            else:
804
                self.ports[idx]["intercon"] = intercon_class(name=endp.name, **self.intercon_class_defargs)
805
 
806 2 dargor
    def is_ipcore_link(self):
807
        """
808
        Checks if this channel is a special link for an ipcore.
809
 
810
        Return: True if the channel is related to an ipcore
811
        """
812
        # Search for ipcore in endpoints. Don't check None on self.index.
813
        for ref in self.endpoints:
814
            if isinstance(ref, ipcore):
815
                return True
816
        return False
817
 
818 4 dargor
# ****************************************
819
# Generic models for abstract NoC elements
820
# ****************************************
821
 
822
# physical and data-link layers
823
class intercon():
824 2 dargor
    """
825 4 dargor
    Interconnection base object
826
 
827
    This object models the interconnection that use a port in a NoC object.
828
    It defines at physical and data-link level how is the connection between
829
    ports of the NoC object.
830
 
831
    Relations with other objects:
832
    * Each port of a NoC object (routers, channels and ipcores) must have
833
        an instance of a intercon object (object.ports[index]["intercon"])
834
    * A NoC model may have one or various references to intercon classes, in
835
        order to provide object constructors for different ports in the NoC
836
        objects.
837
 
838
    Attributes:
839
    * name :
840
    * intercon_type : string to identify intercon type
841
    * complement : reference to a class that provides its reciprocal intercon
842
    * description : a string with a brief description
843
    * long_desc : a long string with a detailed description, usually formatted
844
      in reST (as any Python help string).
845
    * signals : dictionary with the list of signals. The structure is:
846
        * keys : signal's name
847
        * values :
848
            * "width" : bit length of this signal
849
            * "direction" : "in" or "out"
850
            * "signal_obj" : (only simulation) MyHDL signal reference
851
            * "description" :
852
            * Optional keys can be added to this dictionary.
853
 
854
    Notes:
855
    * To avoid excessive duplication of intercon objects, we assume the following
856
      convention for symmetrical intercons: only channels will create new
857
      instances; routers and ipcores only can hold references to this instances.
858
      In case of asymmetrical intercons (master/slave schemes), routes and
859
      ipcores can create new instances, based on intercon type on channel.
860
    """
861
    def __init__(self, name="", **kwargs):
862
        self.name = name
863
        self.intercon_type = ""
864
        self.description = ""
865
        self.long_desc = ""
866
        for key in kwargs.keys():
867
            setattr(self, key, kwargs[key])
868
        # complementary class: None means myself
869
        self.complement = None
870
        # signals info (ordered dict)
871
        self.signals = OrderedDict()
872
 
873
    def __repr__(self):
874
        if self.name != "":
875
            return "<%s '%s'>" % (self.__class__.__name__, self.name)
876
        else:
877
            return "<%s at '%d'>" % (self.__class__.__name__, id(self))
878
 
879
    def add_signal(self, signalname, direction, bitwidth, signalref=None, description=""):
880
        """
881
        Add a signal entry to the intercon
882
 
883
        Arguments:
884
        * signalname: signal name on this intercon
885
        * direction: "in" or "out"
886
        * bitwidth: must be >= 1
887
        * signalref: optional MyHDL signal
888
        * description:
889
 
890
        Returns:
891
        * The contents of signalref
892
        """
893
        if not isinstance(signalname, str):
894
            raise ValueError("Signalname must be an string (not %s)" % repr(signalname))
895
        if direction not in ("in", "out"):
896
            raise ValueError("Direction must be either 'in' or 'out' (not %s)" % repr(direction))
897
        if bitwidth <= 0:
898
            raise ValueError("Bitwidth must be greater than 0 (not %s)" % repr(bitwidth))
899
 
900
        if signalref is not None:
901
            if not isinstance(signalref, SignalType):
902
                raise ValueError("Signalref %s must be a MyHDL Signal (not %s, type %s)" % (signalname, repr(signalref), type(signalref)))
903
 
904
        self.signals[signalname] = {"width": bitwidth, "direction": direction, "signal_obj": signalref, "description": description}
905
 
906
        return signalref
907
 
908
    def get_signal_info(self, signalname, field=None):
909
        """
910
        Search the signal in the intercon and return signal information.
911
        Raise exceptions if the signal is not found, or if the field doesn't
912
        exist for this signal.
913
 
914
        Arguments:
915
        * signalname: signal name on this intercon
916
        * field: particular key of this signal info. None to return
917
                all fields in a dict.
918
 
919
        Returns:
920
        * A dict with the signal information, if field is None.
921
        * The particular object with the key "field" in the signal
922
            info dict.
923
        """
924
        if signalname not in self.signals:
925
            raise KeyError("Signal '%s' not found" % signalname)
926
        if field is None:
927
            return self.signals[signalname]
928
        else:
929
            if field not in self.signals[signalname]:
930
                raise KeyError("Signal '%s': field %s not found" % (signalname, repr(field)))
931
            else:
932
                return self.signals[signalname][field]
933
 
934
    def get_signal_ref(self, signalname=None):
935
        """
936
        Return the MyHDL signal object
937
 
938
        Arguments:
939
        * signalname: name to search, or None
940
 
941
        Returns:
942
        * The signal reference
943
        * If signalname is None, returns a dict with all available
944
            signal objects.
945
        """
946
        if signalname is not None:
947
            return self.get_signal_info(signalname, "signal_obj")
948
        else:
949
            # dict with all available MyHDL signal references
950
            retval = OrderedDict()
951
            for key, val in self.signals.iteritems():
952
                if isinstance(val["signal_obj"], SignalType):
953
                    retval[key] = val["signal_obj"]
954
            return retval
955
 
956
    def get_signal_allnames(self):
957
        """
958
        Return a list of signal names
959
        """
960
        return self.signals.keys()
961
 
962
    def create_myhdl_signals(self):
963
        """
964
        Make MyHDL Signal objects for each signal entry.
965
 
966
        Returns: A dict with all the created signal objects
967
        Note: Previous signal objects will be *unreferenced*.
968
        """
969
        retval = OrderedDict()
970
        for key, sig in self.signals.iteritems():
971
            # use bool for 1-bit signals
972
            if sig["width"] == 1:
973
                sig["signal_obj"] = Signal(bool(0))
974
            else:
975
                sig["signal_obj"] = Signal(intbv(0)[sig["width"]:])
976
            retval[key] = sig["signal_obj"]
977
        return retval
978
 
979
    def get_complement_signal(self, signalname):
980
        """
981
        Get the signal name that should be connected to this signal when
982
        connecting two intercon.
983
 
984
        Arguments:
985
        * signalname: signal name of this intercon
986
 
987
        Return: a string with the name of a signal from a complementary intercon.
988
        """
989
        return None
990
 
991
    def create_complementary(self, newname="", **kwargs):
992
        """
993
        Create a instance of a complementary type
994
 
995
        Arguments:
996
        * newname : optional new name for the created object. By default use
997
          the same name as self.
998
        * optional list of arguments to use for new object. By default the
999
          created object copy its attributes from this object.
1000
        """
1001
        # prepare list of arguments
1002
        if newname == "":
1003
            newname = self.name
1004
        if "name" not in kwargs:
1005
            kwargs["name"] = newname
1006
        # extract list of attributes, excluding some...
1007
        names = filter(lambda s: s[1] != "_", dir(self))
1008
        names = filter(lambda s: s not in ("name", "intercon_type", "complement", "signals", "sigmapping"), names)
1009
        # and, if not defined in kwargs, use self attributes
1010
        for s in names:
1011
            if s not in kwargs:
1012
                kwargs[s] = getattr(self, s)
1013
 
1014
        if self.complement is None:
1015
            # I'm my complement? like a object clone
1016
            # use my class reference, so the constructor is correctly called.
1017
            return self.__class__(**kwargs)
1018
        else:
1019
            return self.complement(**kwargs)
1020
 
1021
# Special intercon to implement a signal container
1022
class signalset(intercon):
1023
    """
1024
    Signal container based on an intercon.
1025
 
1026
    Attributes:
1027
    * name :
1028
    * intercon_type : "signalset"
1029
    * complement : Without complement intercon by default
1030
    * description :
1031
    * signals : dictionary with the list of signals. The structure is:
1032
        * keys : signal's name
1033
        * values :
1034
            * "width" : bit length of this signal
1035
            * "direction" : "in" or "out"
1036
            * "signal_obj" : MyHDL signal reference
1037
            * "description" :
1038
            * Optional keys can be added to this dictionary.
1039
 
1040
    Notes:
1041
    * This object makes a MyHDL signal when method "add_signal" is used.
1042
    * This object implements a custom complement signal mechanism.
1043
    """
1044
 
1045
    def __init__(self, name, **kwargs):
1046
        intercon.__init__(self, name, **kwargs)
1047
        self.complement_mapping = {}
1048
 
1049
        self.intercon_type = "signalset"
1050
        self.complement = None
1051
        self.sideinfo = ""
1052
 
1053
    def add_signal(self, signalname, direction, bitwidth, signalref=None, complname=None, description=""):
1054
        """
1055
        Add a signal entry to signalset
1056
 
1057
        Arguments:
1058
        * signalname:
1059
        * direction: "in" or "out"
1060
        * bitwidth: must be >= 1
1061
        * signalref: optional MyHDL signal. If None, it will create a new signal.
1062
        * complname: optional complement signal name. This string will be
1063
            returned on "get_complement_signal" method.
1064
        * description:
1065
 
1066
        Returns:
1067
        * The contents of signalref
1068
        """
1069
        intercon.add_signal(self, signalname, direction, bitwidth, signalref, description)
1070
 
1071
        # need signal creation at this point
1072
        if signalref is None:
1073
            if bitwidth == 1:
1074
                signalref = Signal(bool(0))
1075
            else:
1076
                signalref = Signal(intbv(0)[bitwidth:])
1077
            self.signals[signalname]["signal_obj"] = signalref
1078
 
1079
        # complement signal mapping
1080
        if isinstance(complname, str):
1081
            self.complement_mapping[signalname] = complname
1082
 
1083
        return signalref
1084
 
1085
    def get_complement_signal(self, signalname):
1086
        """
1087
        Get the signal name that should be connected to this signal when
1088
        connecting two signalref (or intercon). Return value depends on
1089
        signal creation arguments (see "add_signal" method)
1090
 
1091
        Arguments:
1092
        * signalname: signal name of this intercon
1093
 
1094
        Return: a string with complementary signal name, or None if not found.
1095
        """
1096
        if signalname not in self.signals:
1097
            raise KeyError("Signal '%s' not found" % signalname)
1098
 
1099
        if signalname in self.complement_mapping:
1100
            return self.complement_mapping[signalname]
1101
        else:
1102
            return None
1103
 
1104
# network and transport layers
1105
class protocol():
1106
    """
1107 2 dargor
    Protocol base object
1108
 
1109
    This object represents the protocol that the NoC objects use. This
1110
    object has attributes that define the protocol used, mainly it can be
1111
    used to generate, encode, decode or interpret packets on the NoC.
1112
    This base class can be either be inherited or extended by adding
1113
    other attributes.
1114
 
1115
    Relations with other objects:
1116
    * Each object on the NoC (routers, channels and ipcores) may have
1117
      one reference to a protocol object (object.protocol_ref)
1118
    * A NoC model may have a protocol object: in this case, all objects in
1119
      the model will use this protocol (nocmodel.protocol_ref)
1120
    * A protocol object is a generator of packet objects
1121
 
1122
    Attributes:
1123
    * name
1124 4 dargor
    * description : a string with a brief description
1125
    * long_desc : a long string with a detailed description, usually formatted
1126
      in reST (as any Python help string).
1127 2 dargor
 
1128
    Notes:
1129 4 dargor
    * Optional arguments "packet_format" can be
1130 2 dargor
      added at object construction, but will not check its data consistency.
1131
      At the moment, we recommend using update_packet_field() method to
1132
      fill this data structures.
1133
    """
1134
    def __init__(self, name="", **kwargs):
1135
        """
1136
        Constructor
1137
 
1138
        Notes:
1139
        * Optional arguments will be added as object attributes.
1140
        """
1141
        self.name = name
1142 4 dargor
        self.packet_format = OrderedDict()
1143 2 dargor
        self.packet_class = packet
1144 4 dargor
        self.packet_bitlen = 0
1145
        self.flit_bitlen = 0
1146
        self.flit_fixcount = 0
1147
        self.flit_padbits = 0
1148
        self.variable_packet = False
1149
        self.description = ""
1150
        self.long_desc = ""
1151 2 dargor
        for key in kwargs.keys():
1152
            setattr(self, key, kwargs[key])
1153
 
1154 4 dargor
    def __repr__(self):
1155
        if self.name != "":
1156
            return "<%s '%s'>" % (self.__class__.__name__, self.name)
1157
        else:
1158
            return "<%s at '%d'>" % (self.__class__.__name__, id(self))
1159
 
1160 2 dargor
    def update_packet_field(self, name, type, bitlen, description=""):
1161
        """
1162
        Add or update a packet field.
1163
 
1164
        Arguments
1165
        * name
1166 4 dargor
        * type: string that can be "int", "uint", "fixed" or "float"
1167 2 dargor
        * bitlen: bit length of this field
1168
        * description: optional description of this field
1169
 
1170
        Notes:
1171
        * Each new field will be added at the end.
1172 4 dargor
        * Fields are configured to have a bit range inside the packet,
1173
        *  starting at 0. Also, it refers to the fixed part of the packet.
1174
        * Fields "msb" and "lsb" are indexes relative to MSB bit of the first
1175
           field (Big endian scheme), completely different to a
1176
           bit vector indexing.
1177 2 dargor
        """
1178 4 dargor
        if type not in ("int", "uint", "fixed", "float"):
1179
            raise ValueError("Argument 'type' must be 'int', 'uint', 'fixed' or 'float'.")
1180 2 dargor
 
1181
        if name in self.packet_format:
1182
            # update field
1183 4 dargor
            previdx = self.packet_format.keys().index(name) - 1
1184 2 dargor
            if previdx < 0:
1185
                # first field
1186
                lastbitpos = 0
1187
            else:
1188 4 dargor
                lastbitpos = self.packet_format[self.packet_format.keys()[previdx]]["lsb"]
1189 2 dargor
            nextbitpos = lastbitpos + bitlen
1190
            self.packet_format[name]["type"] = type
1191
            self.packet_format[name]["position"] = previdx + 1
1192
            # check if the packet format needs to adjust the bit positions
1193
            if self.packet_format[name]["bitlen"] != bitlen:
1194
                self.packet_format[name]["bitlen"] = bitlen
1195
                self.packet_format[name]["lsb"] = nextbitpos
1196
                self.packet_format[name]["msb"] = lastbitpos
1197
                # iterate through the rest of the fields adjusting lsb and msb
1198 4 dargor
                for idx in range(previdx+2, len(self.packet_format.keys())):
1199
                    curname = self.packet_format.keys()[idx]
1200 2 dargor
                    curbitlen = self.packet_format[curname]["bitlen"]
1201
                    self.packet_format[curname]["lsb"] = nextbitpos + curbitlen
1202
                    self.packet_format[curname]["msb"] = nextbitpos
1203
                    nextbitpos += curbitlen
1204 4 dargor
            self.packet_bitlen = nextbitpos
1205 2 dargor
        else:
1206
            # append
1207
            if len(self.packet_format) == 0:
1208
                lastbitpos = 0
1209
            else:
1210 4 dargor
                lastbitpos = self.packet_format[self.packet_format.keys()[-1]]["lsb"] + 1
1211 2 dargor
            nextbitpos = lastbitpos + bitlen
1212 4 dargor
            fieldpos = len(self.packet_format)
1213
            self.packet_format[name] = {"type": type, "position": fieldpos, "bitlen": bitlen, "lsb": nextbitpos - 1, "msb": lastbitpos}
1214
            self.packet_bitlen = nextbitpos
1215
 
1216
    def get_field_info(self, name):
1217
        """
1218
        Get information about a existing packet field.
1219 2 dargor
 
1220 4 dargor
        Arguments:
1221
        * name
1222
        Returns a dict with the following information:
1223
        * "type"
1224
        * "pkt_bitpos": absolute bit position in the packet (msb)
1225
        * "bitlen": bit size
1226
        * "flit_num": which flit use this field
1227
        * "flit_bitpos": bit position inside the flit
1228
        """
1229
        if name not in self.packet_format:
1230
            raise ValueError("Packet field '%s' not found." % name)
1231
        retinfo = {}
1232
        retinfo["type"] = self.packet_format[name]["type"]
1233
        retinfo["bitlen"] = self.packet_format[name]["bitlen"]
1234
        retinfo["pkt_bitpos"] = self.packet_format[name]["msb"]
1235
        if getattr(self, "flit_bitlen", 0) == 0:
1236
            # not using flits
1237
            retinfo["flit_num"] = 0
1238
            retinfo["flit_position"] = self.packet_format[name]["msb"]
1239
        else:
1240
            # CHECK THIS
1241
            retinfo["flit_num"] = int(self.packet_format[name]["msb"] / self.flit_bitlen)
1242
            retinfo["flit_position"] = self.packet_format[name]["msb"] - retinfo["flit_num"]
1243
 
1244
        return retinfo
1245
 
1246
    def configure_flits(self, flit_size, variable_packet=False):
1247
        """
1248
        Configure the flit split in a packet.
1249
 
1250
        Arguments:
1251
        * flit_size: size in bits of a flit. Based on this size a packet
1252
          is split, and a zero padding added if necessary.
1253
        * variable_packet: If true, allows the packet to have a variable
1254
          packet size by adding additional flits at the end.
1255
 
1256
        NOTE: variable packet mechanism is still under development.
1257
        """
1258
        if flit_size <= 0:
1259
            raise ValueError("Argument 'flit_size' must be greater than zero.")
1260
        self.flit_bitlen = flit_size
1261
        # calculate the number of flits of the fixed part of the packet
1262
        self.flit_fixcount = int(mathceil(float(self.packet_bitlen) / float(self.flit_bitlen)))
1263
        self.flit_padbits = (self.flit_fixcount*self.flit_bitlen) - self.packet_bitlen
1264
        self.variable_packet = variable_packet
1265
 
1266 2 dargor
    def newpacket(self, zerodefault=True, *args, **kwargs):
1267
        """
1268
        Return a new packet with all required fields.
1269
 
1270
        Arguments:
1271
        * zerodefault: If True, all missing fields will be zeroed by default.
1272
          If False, a missing value in arguments will throw an exception.
1273
        * args: Nameless arguments will add field values based on packet field
1274
          order.
1275
        * kwargs: Key-based arguments will add specified field values based in
1276
          its keys
1277
 
1278
        Notes:
1279
        * kwargs takes precedence over args: i.e. named arguments can overwrite
1280
          nameless arguments.
1281
        """
1282
        retpacket = self.packet_class(protocol_ref=self)
1283 4 dargor
        fieldlist = self.packet_format.keys()
1284 2 dargor
        # first named arguments
1285
        for fkey, fvalue in kwargs.iteritems():
1286
            if fkey in fieldlist:
1287
                retpacket[fkey] = fvalue
1288
                fieldlist.remove(fkey)
1289
        # then nameless
1290
        for fidx, fvalue in enumerate(args):
1291 4 dargor
            fkey = self.packet_format.keys()[fidx]
1292 2 dargor
            if fkey in fieldlist:
1293
                retpacket[fkey] = fvalue
1294
                fieldlist.remove(fkey)
1295
        # check for empty fields
1296
        if len(fieldlist) > 0:
1297
            if zerodefault:
1298
                for fkey in fieldlist:
1299
                    retpacket[fkey] = 0
1300
            else:
1301
                raise ValueError("Missing fields in argument list: %s" % repr(fieldlist))
1302
        return retpacket
1303 4 dargor
 
1304
    def newpacket_frombinary(self, binaryinput):
1305
        """
1306
        Return a new packet based on a binary representation
1307
 
1308
        Arguments:
1309
        * binaryinput: integer or intbv with the binary representation
1310
          of the packet.
1311
        """
1312
        if isinstance(binaryinput, (int, long)):
1313
            theinput = intbv(binaryinput)[self.packet_bitlen:]
1314
        elif isinstance(binaryinput, intbv):
1315
            theinput = binaryinput
1316
        else:
1317
            raise ValueError("Unsupported type for binaryinput: '%s'" % repr(type(binaryinput)))
1318
        retpacket = self.packet_class(protocol_ref=self)
1319
        for field, field_info in self.packet_format.iteritems():
1320
            # NOTE: msb and lsb indexes are referred as 0 as the MSB bit
1321
            # recalculate to have the LSB bit at 0
1322
            msb = self.packet_bitlen - field_info["msb"]
1323
            lsb = self.packet_bitlen - field_info["lsb"] - 1
1324
            if field_info["type"] == "int":
1325
                #retpacket[field] = theinput[msb:lsb].signed()
1326
                retpacket[field] = theinput[msb:lsb]
1327
            elif field_info["type"] == "uint":
1328
                retpacket[field] = theinput[msb:lsb]
1329
            else:
1330
                raise NotImplementedError("Field %s type %s not supported yet." % (field, field_info["type"]))
1331
        retpacket.prev_repr = binaryinput
1332
        return retpacket
1333
 
1334
    def newpacket_fromflits(self, flits_list):
1335
        """
1336
        Return a new packet based on a list of flits
1337
 
1338
        Arguments:
1339
        * flits_list: list of integers or intbv with the binary
1340
          representation of each flit.
1341
        """
1342
        if not isinstance(flits_list, (list, tuple)):
1343
            raise ValueError("Unsupported type for flits_list: '%s'" % repr(type(flits_list)))
1344
        extracted = []
1345
        flit_curbit = self.flit_bitlen
1346
        flit_idx = 0
1347
        for field, field_info in self.packet_format.iteritems():
1348
            flit_val = intbv(0)[field_info["bitlen"]:]
1349
 
1350
            msb = flit_curbit
1351
            lsb = flit_curbit - field_info["bitlen"]
1352
 
1353
            if lsb < 0:
1354
 
1355
                # split packet field into several flits
1356
                lsb_pend = -lsb
1357
 
1358
                # first part
1359
                flit_val[:lsb_pend] = flits_list[flit_idx][flit_curbit:]
1360
 
1361
                flit_idx += 1
1362
                flit_curbit = self.flit_bitlen
1363
 
1364
                while lsb_pend > 0:
1365
                    if lsb_pend >= self.flit_bitlen:
1366
                        flit_val[lsb_pend:lsb_pend-self.flit_bitlen] = flits_list[flit_idx]
1367
                        flit_idx += 1
1368
                        flit_curbit = self.flit_bitlen
1369
                        lsb_pend -= field_info["bitlen"]
1370
                    else:
1371
                        # last flit
1372
                        flit_val[lsb_pend:] = flits_list[flit_idx][:self.flit_bitlen-lsb_pend]
1373
                        flit_curbit -= lsb_pend
1374
                        lsb_pend = 0
1375
            else:
1376
                flit_val = flits_list[flit_idx][msb:lsb]
1377
                flit_curbit -= field_info["bitlen"]
1378
 
1379
            extracted.append(flit_val)
1380
            if lsb == 0:
1381
                # next flit
1382
                flit_idx += 1
1383
                flit_curbit = self.flit_bitlen
1384
 
1385
        retpacket = self.packet_class(protocol_ref=self)
1386
        for field, content in zip(self.packet_format.keys(), extracted):
1387
            if self.packet_format[field]["type"] == "int":
1388
                #retpacket[field] = theinput[msb:lsb].signed()
1389
                retpacket[field] = content
1390
            elif self.packet_format[field]["type"] == "uint":
1391
                retpacket[field] = content
1392
            else:
1393
                raise NotImplementedError("Field %s type %s not supported yet." % (field, self.packet_format[field]["type"]))
1394
        retpacket.prev_repr = flits_list
1395
        return retpacket
1396
 
1397 2 dargor
    def register_packet_generator(self, packet_class):
1398
        """
1399
        Register a special packet generator class.
1400
 
1401
        Must be a derived class of packet
1402
        """
1403
        if not issubclass(packet_class, packet):
1404
            raise TypeError("Argument 'packet_class' must derive from 'packet' class.")
1405
        self.packet_class = packet_class
1406
 
1407
class packet(dict):
1408
    """
1409
    Packet base object
1410
 
1411
    This object represents a packet data, related to a protocol object. It
1412
    behaves exactly like a Python dictionary, but adds methods to simplify
1413
    packet transformations.
1414
 
1415
    Relations with other objects:
1416
    * It should be generated by a protocol object (and will have a reference
1417
      in self.protocol_ref)
1418
 
1419
    Attributes:
1420
    * protocol_ref: protocol object that created this object.
1421 4 dargor
    * prev_repr: previous representation of this packet. Can be:
1422
      - None: this packet was created by each field data
1423
      - <type long> : was created with a numeric representation.
1424
      - <type list> : was created with a list of flits
1425
      This attribute should only be changed by its protocol object.
1426 2 dargor
    """
1427
 
1428 4 dargor
    # TODO: add support for flit construction: temporal storage for flits,
1429
    # and package construction after final flit
1430
 
1431 2 dargor
    # the constructor 
1432
    def __init__(self, *args, **kwargs):
1433
        # look for a protocol_ref key
1434
        self.protocol_ref = kwargs.pop("protocol_ref", None)
1435 4 dargor
        self.prev_repr = None
1436 2 dargor
        dict.__init__(self, *args, **kwargs)
1437 4 dargor
 
1438
 
1439
    # override copy method
1440
    def copy(self):
1441
        # Warning: take account of each element inside, specially if it's 
1442
        # a intbv
1443
        basedict = dict(**self)
1444
        for k, v in self.iteritems():
1445
            if isinstance(v, intbv):
1446
                # make a copy
1447
                basedict[k] = intbv(v)
1448
        # use same keys to build the new packet
1449
        pktobj = packet(**basedict)
1450
        pktobj.protocol_ref = self.protocol_ref
1451
        pktobj.prev_repr = self.prev_repr
1452
        return pktobj
1453
 
1454
    def get_flit_repr(self):
1455
        """
1456
        Returns a list of integers with the binary representation of the
1457
        full packet contents, separated in flits.
1458
        """
1459
        protocol_ref = self.protocol_ref
1460
        binary_flits = [intbv(0)[protocol_ref.flit_bitlen:] for i in range(protocol_ref.flit_fixcount)]
1461
        flit_curbit = protocol_ref.flit_bitlen
1462
        flit_idx = 0
1463
        for field, field_info in protocol_ref.packet_format.iteritems():
1464
            if field_info["type"] == "int" or field_info["type"] == "uint":
1465
                bitvalue = intbv(self[field])[field_info["bitlen"]:]
1466
            elif field_info["type"] == "fixed":
1467
                raise NotImplementedError("Don't know how to put a fixed point in a binary representation.")
1468
            elif field_info["type"] == "float":
1469
                raise NotImplementedError("Don't know how to put a float in a binary representation.")
1470
 
1471
            #{"type": type, "position": fieldpos, "bitlen": bitlen, "lsb": nextbitpos, "msb": lastbitpos}
1472
            msb_flit = flit_curbit
1473
            lsb_flit = flit_curbit - field_info["bitlen"]
1474
 
1475
            if lsb_flit < 0:
1476
                # split packet field into several flits
1477
                lsb_flit_pend = -lsb_flit
1478
 
1479
                # first flit
1480
                binary_flits[flit_idx][msb_flit:] = bitvalue[field_info["bitlen"]:flit_curbit]
1481
                flit_idx += 1
1482
                flit_curbit = protocol_ref.flit_bitlen
1483
                while lsb_flit_pend > 0:
1484
                    if lsb_flit_pend >= protocol_ref.flit_bitlen:
1485
                        binary_flits[flit_idx] = bitvalue
1486
                        flit_idx += 1
1487
                        flit_curbit = protocol_ref.flit_bitlen
1488
                        lsb_flit_pend -= field_info["bitlen"]
1489
                    else:
1490
                        # last flit
1491
                        binary_flits[flit_idx][:protocol_ref.flit_bitlen - lsb_flit_pend] = bitvalue[lsb_flit_pend:]
1492
                        flit_curbit -= lsb_flit_pend
1493
 
1494
            else:
1495
                binary_flits[flit_idx][msb_flit:lsb_flit] = bitvalue
1496
                flit_curbit -= field_info["bitlen"]
1497
 
1498
            if lsb_flit == 0:
1499
                # next flit
1500
                flit_idx += 1
1501
                flit_curbit = protocol_ref.flit_bitlen
1502
            #print "integer repr: FIELD %s bitval %d b%s\n field info %s recalc msb %d lsb %d" % (field, bitvalue, bin(bitvalue), repr(field_info), msb, lsb)
1503
 
1504
            #print "integer repr: TEMP BIN %d b%s" % (binaryout, bin(binaryout))
1505
        #print "integer repr: FINAL BIN %d b%s" % (binaryout, bin(binaryout))
1506
        return binary_flits
1507
 
1508
    def get_integer_repr(self):
1509
        """
1510
        Returns an integer with the binary representation of the
1511
        full packet contents.
1512
        """
1513
        protocol_ref = self.protocol_ref
1514
        #binaryout = 0
1515
        binaryout = intbv(0)[protocol_ref.packet_bitlen:]
1516
        for field, field_info in protocol_ref.packet_format.iteritems():
1517
            bitvalue = self[field]
1518
            #{"type": type, "position": fieldpos, "bitlen": bitlen, "lsb": nextbitpos, "msb": lastbitpos}
1519
            # NOTE: msb and lsb indexes are referred as 0 as the MSB bit
1520
            # recalculate to have the LSB bit at 0
1521
            msb = protocol_ref.packet_bitlen - field_info["msb"]
1522
            lsb = protocol_ref.packet_bitlen - field_info["lsb"] - 1
1523
            #print "integer repr: FIELD %s bitval %d b%s\n field info %s recalc msb %d lsb %d" % (field, bitvalue, bin(bitvalue), repr(field_info), msb, lsb)
1524
            if field_info["type"] == "int" or field_info["type"] == "uint":
1525
                #binaryout |= (bitvalue << lsb)
1526
                binaryout[msb:lsb] = intbv(bitvalue)[field_info["bitlen"]:]
1527
            elif field_info["type"] == "fixed":
1528
                raise NotImplementedError("Don't know how to put a fixed point in a binary representation.")
1529
            elif field_info["type"] == "float":
1530
                raise NotImplementedError("Don't know how to put a float in a binary representation.")
1531
            #print "integer repr: TEMP BIN %d b%s" % (binaryout, bin(binaryout))
1532
        #print "integer repr: FINAL BIN %d b%s" % (binaryout, bin(binaryout))
1533
        return binaryout
1534
 
1535 2 dargor
 
1536
# *******************************
1537
# Additional functions
1538
# *******************************
1539
 
1540
# Missing function in NetworkX
1541
def all_shortest_paths(G,a,b):
1542
    """
1543
    Return a list of all shortest paths in graph G between nodes a and b
1544
    This is a function not available in NetworkX (checked at 22-02-2011)
1545
 
1546
    Taken from:
1547
    http://groups.google.com/group/networkx-discuss/browse_thread/thread/55465e6bb9bae12e
1548
    """
1549
    ret = []
1550
    pred = nx.predecessor(G,b)
1551
    if not pred.has_key(a):  # b is not reachable from a
1552
        return []
1553
    pth = [[a,0]]
1554
    pthlength = 1  # instead of array shortening and appending, which are relatively
1555
    ind = 0        # slow operations, we will just overwrite array elements at position ind
1556
    while ind >= 0:
1557
        n,i = pth[ind]
1558
        if n == b:
1559
            ret.append(map(lambda x:x[0],pth[:ind+1]))
1560
        if len(pred[n]) > i:
1561
            ind += 1
1562
            if ind == pthlength:
1563
                pth.append([pred[n][i],0])
1564
                pthlength += 1
1565
            else:
1566
                pth[ind] = [pred[n][i],0]
1567
        else:
1568
            ind -= 1
1569
            if ind >= 0:
1570
                pth[ind][1] += 1
1571
    return ret

powered by: WebSVN 2.1.0

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