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
|