URL
https://opencores.org/ocsvn/nocmodel/nocmodel/trunk
Subversion Repositories nocmodel
Compare Revisions
- This comparison shows the changes necessary to convert path
/nocmodel/trunk/nocmodel
- from Rev 3 to Rev 4
- ↔ Reverse comparison
Rev 3 → Rev 4
/noc_tlm_base.py
File deleted
/noc_tlm_utils.py
File deleted
/noc_base.py
5,8 → 5,8
# NoC Base Objects |
# |
# Author: Oscar Diaz |
# Version: 0.1 |
# Date: 03-03-2011 |
# Version: 0.2 |
# Date: 05-07-2012 |
|
# |
# This code is free software; you can redistribute it and/or |
29,12 → 29,13
# Changelog: |
# |
# 03-03-2011 : (OD) initial release |
# 05-07-2012 : (OD) intercon class, major changes, various bugfixes |
# |
|
""" |
================ |
===================== |
NoCmodel Base Objects |
================ |
===================== |
|
This module declares classes used on a Network-on-chip representation: |
|
42,11 → 43,15
* Router base class |
* Channel base class |
* IPCore base class |
* Intercon base class |
* Protocol base class |
* Packet class |
""" |
|
import networkx as nx |
from myhdl import Signal, SignalType, intbv, bin |
from collections import OrderedDict |
from math import ceil as mathceil |
|
class noc(nx.Graph): |
""" |
61,6 → 66,16
NoCmodel constructor |
""" |
nx.Graph.__init__(self, **kwargs) |
if not hasattr(self, "name"): |
self.name = "" |
if not hasattr(self, "description"): |
self.description = "" |
|
def __repr__(self): |
if self.name != "": |
return "<%s '%s'>" % (self.__class__.__name__, self.name) |
else: |
return "<%s at '%d'>" % (self.__class__.__name__, id(self)) |
|
# objects management functions |
def add_router(self, name="", with_ipcore=False, **kwargs): |
354,27 → 369,36
def router_list(self): |
l = [] |
for i in self.nodes_iter(data=True): |
l.append(i[1]["router_ref"]) |
r = i[1].get("router_ref", None) |
if r is not None: |
l.append(r) |
return l |
|
def ipcore_list(self): |
l = [] |
for i in self.nodes_iter(data=True): |
if i[1]["router_ref"].ipcore_ref != None: |
l.append(i[1]["router_ref"].ipcore_ref) |
for i in self.router_list(): |
ip = getattr(i, "ipcore_ref", None) |
if ip is not None: |
l.append(ip) |
return l |
|
def channel_list(self): |
# this function does not list ipcore channels |
def channel_list(self, with_ipcore_channel=False): |
l = [] |
for i in self.edges_iter(data=True): |
l.append(i[2]["channel_ref"]) |
ch = i[2].get("channel_ref", None) |
if ch is not None: |
l.append(ch) |
if with_ipcore_channel: |
for i in self.ipcore_list(): |
ch = getattr(i, "channel_ref", None) |
if ch is not None: |
l.append(ch) |
return l |
|
def all_list(self): |
|
def all_list(self, with_ipcore_channel=False): |
l = self.router_list() |
l.extend(self.ipcore_list()) |
l.extend(self.channel_list()) |
l.extend(self.channel_list(with_ipcore_channel)) |
return l |
|
# query functions |
383,6 → 407,14
if r.address == address: |
return r |
return False |
|
# update functions |
def update_nocdata(self): |
for r in self.router_list(): |
r.update_ports_info() |
r.update_routes_info() |
for ch in self.channel_list(True): |
ch.update_ports_info() |
|
# hidden functions |
def _add_router_from_node(self, node, name="", router_ref=None, **kwargs): |
393,7 → 425,7
if router_ref is None: |
# index comes from node |
if name == "": |
name = "R_%d" % node |
name = "R_%s" % repr(node) |
routernode = router(index=node, name=name, graph_ref=self, **kwargs) |
else: |
if not isinstance(router_ref, router): |
449,6 → 481,15
This base class is used to implement common methods for NoC objects. |
Don't use directly. |
""" |
name = "" |
description = "" |
|
def __repr__(self): |
if self.name != "": |
return "<%s '%s'>" % (self.__class__.__name__, self.name) |
else: |
return "<%s at '%d'>" % (self.__class__.__name__, id(self)) |
|
def get_protocol_ref(self): |
""" |
Get protocol object for this instance |
461,6 → 502,25
# nothing? |
return None |
|
def get_address(self): |
""" |
Get address related to this object. If it is a router or a ipcore, |
returns the address in the router. If it is a channel, return a list |
or pair addresses from its endpoints. If it is another object without |
address, return None. |
""" |
if hasattr(self, "address"): |
return self.address |
else: |
# try ipcore |
if isinstance(self, ipcore): |
return self.router_ref.address |
# try channel |
if isinstance(self, channel): |
return [x.get_address() for x in self.endpoints] |
# nothing? |
return None |
|
class ipcore(nocobject): |
""" |
IP core base object |
490,6 → 550,29
self.graph_ref = None |
for key in kwargs.keys(): |
setattr(self, key, kwargs[key]) |
# ports structure |
self.ports = {} |
|
# update functions: call them when the underlying NoC structure |
# has changed |
def update_ports_info(self): |
""" |
Update the dictionary "ports". For an ipcore, it only has one element |
to its router. |
|
Ports dictionary has the following structure: |
* key: address of its related router |
* value: dictionary with the following keys: |
* "peer" (required): reference to its router |
* "channel" (required): reference to the channel that connects this |
ipcore and its router. |
* Optional keys can be added to this dictionary with the same |
meaning as other ports. |
""" |
myaddr = self.get_address() |
self.ports = {myaddr: {}} |
self.ports[myaddr]["peer"] = self.router_ref |
self.ports[myaddr]["channel"] = self.channel_ref |
|
class router(nocobject): |
""" |
655,12 → 738,13
router and one ipcore (channel don't have any edge object). |
|
Attributes: |
* name |
* index: optional index on a noc object. Must have an index when it has |
* name : |
* index : optional index on a noc object. Must have an index when it has |
a related edge in the graph model (and allowing it to be able to do |
channel searching). None means it is an ipcore related channel |
* graph_ref optional reference to its graph model |
* endpoints optional two-item list with references to the connected objects |
* graph_ref : optional reference to its graph model |
* endpoints : optional two-item list with references to the connected objects |
* intercon_class : optional reference to a intercon class used in this channel. |
""" |
def __init__(self, name, index=None, **kwargs): |
# Basic properties |
669,9 → 753,56
# Default values |
self.graph_ref = None |
self.endpoints = [None, None] |
self.intercon_class = intercon |
self.intercon_class_defargs = {} |
for key in kwargs.keys(): |
setattr(self, key, kwargs[key]) |
# ports structure |
self.ports = {} |
|
# update functions: call them when the underlying NoC structure |
# has changed |
def update_ports_info(self, intercon_class=None): |
""" |
Update the dictionary "ports": For a channel, there is two |
elements referencing both connected objects |
|
Ports dictionary has the following structure: |
* key: address of the router related with this channel, or None |
for a ipcore object. |
* value: dictionary with the following keys: |
* "peer" (required): reference to the router or ipcore |
* "channel" (required): reference to self |
* "intercon" (required for RTL sim and codegen) |
* Optional keys can be added to this dictionary. |
|
Arguments : |
* intercon_class : class reference to generate intercon instances |
NOTE: |
* This update will change and DESTROY existing intercon objects. |
""" |
if intercon_class is None: |
intercon_class = self.intercon_class |
if not issubclass(intercon_class, intercon): |
raise TypeError("intercon_class must be subclass of intercon.") |
self.ports = {} |
for endp in self.endpoints: |
if isinstance(endp, ipcore): |
idx = None |
elif isinstance(endp, router): |
idx = endp.get_address() |
else: |
raise ValueError("endpoints has an inconsistent state (%s)." % repr(endp)) |
self.ports[idx] = {} |
self.ports[idx]["peer"] = endp |
self.ports[idx]["channel"] = self |
# TEMPORAL WORKAROUND: use intercon_class.complement on ipcore side |
# ONLY on case of ipcore channels. |
if idx == None and hasattr(intercon_class, "complement"): |
self.ports[idx]["intercon"] = intercon_class.complement(name=endp.name, **self.intercon_class_defargs) |
else: |
self.ports[idx]["intercon"] = intercon_class(name=endp.name, **self.intercon_class_defargs) |
|
def is_ipcore_link(self): |
""" |
Checks if this channel is a special link for an ipcore. |
684,8 → 815,295
return True |
return False |
|
class protocol(nocobject): |
# **************************************** |
# Generic models for abstract NoC elements |
# **************************************** |
|
# physical and data-link layers |
class intercon(): |
""" |
Interconnection base object |
|
This object models the interconnection that use a port in a NoC object. |
It defines at physical and data-link level how is the connection between |
ports of the NoC object. |
|
Relations with other objects: |
* Each port of a NoC object (routers, channels and ipcores) must have |
an instance of a intercon object (object.ports[index]["intercon"]) |
* A NoC model may have one or various references to intercon classes, in |
order to provide object constructors for different ports in the NoC |
objects. |
|
Attributes: |
* name : |
* intercon_type : string to identify intercon type |
* complement : reference to a class that provides its reciprocal intercon |
* description : a string with a brief description |
* long_desc : a long string with a detailed description, usually formatted |
in reST (as any Python help string). |
* signals : dictionary with the list of signals. The structure is: |
* keys : signal's name |
* values : |
* "width" : bit length of this signal |
* "direction" : "in" or "out" |
* "signal_obj" : (only simulation) MyHDL signal reference |
* "description" : |
* Optional keys can be added to this dictionary. |
|
Notes: |
* To avoid excessive duplication of intercon objects, we assume the following |
convention for symmetrical intercons: only channels will create new |
instances; routers and ipcores only can hold references to this instances. |
In case of asymmetrical intercons (master/slave schemes), routes and |
ipcores can create new instances, based on intercon type on channel. |
""" |
def __init__(self, name="", **kwargs): |
self.name = name |
self.intercon_type = "" |
self.description = "" |
self.long_desc = "" |
for key in kwargs.keys(): |
setattr(self, key, kwargs[key]) |
# complementary class: None means myself |
self.complement = None |
# signals info (ordered dict) |
self.signals = OrderedDict() |
|
def __repr__(self): |
if self.name != "": |
return "<%s '%s'>" % (self.__class__.__name__, self.name) |
else: |
return "<%s at '%d'>" % (self.__class__.__name__, id(self)) |
|
def add_signal(self, signalname, direction, bitwidth, signalref=None, description=""): |
""" |
Add a signal entry to the intercon |
|
Arguments: |
* signalname: signal name on this intercon |
* direction: "in" or "out" |
* bitwidth: must be >= 1 |
* signalref: optional MyHDL signal |
* description: |
|
Returns: |
* The contents of signalref |
""" |
if not isinstance(signalname, str): |
raise ValueError("Signalname must be an string (not %s)" % repr(signalname)) |
if direction not in ("in", "out"): |
raise ValueError("Direction must be either 'in' or 'out' (not %s)" % repr(direction)) |
if bitwidth <= 0: |
raise ValueError("Bitwidth must be greater than 0 (not %s)" % repr(bitwidth)) |
|
if signalref is not None: |
if not isinstance(signalref, SignalType): |
raise ValueError("Signalref %s must be a MyHDL Signal (not %s, type %s)" % (signalname, repr(signalref), type(signalref))) |
|
self.signals[signalname] = {"width": bitwidth, "direction": direction, "signal_obj": signalref, "description": description} |
|
return signalref |
|
def get_signal_info(self, signalname, field=None): |
""" |
Search the signal in the intercon and return signal information. |
Raise exceptions if the signal is not found, or if the field doesn't |
exist for this signal. |
|
Arguments: |
* signalname: signal name on this intercon |
* field: particular key of this signal info. None to return |
all fields in a dict. |
|
Returns: |
* A dict with the signal information, if field is None. |
* The particular object with the key "field" in the signal |
info dict. |
""" |
if signalname not in self.signals: |
raise KeyError("Signal '%s' not found" % signalname) |
if field is None: |
return self.signals[signalname] |
else: |
if field not in self.signals[signalname]: |
raise KeyError("Signal '%s': field %s not found" % (signalname, repr(field))) |
else: |
return self.signals[signalname][field] |
|
def get_signal_ref(self, signalname=None): |
""" |
Return the MyHDL signal object |
|
Arguments: |
* signalname: name to search, or None |
|
Returns: |
* The signal reference |
* If signalname is None, returns a dict with all available |
signal objects. |
""" |
if signalname is not None: |
return self.get_signal_info(signalname, "signal_obj") |
else: |
# dict with all available MyHDL signal references |
retval = OrderedDict() |
for key, val in self.signals.iteritems(): |
if isinstance(val["signal_obj"], SignalType): |
retval[key] = val["signal_obj"] |
return retval |
|
def get_signal_allnames(self): |
""" |
Return a list of signal names |
""" |
return self.signals.keys() |
|
def create_myhdl_signals(self): |
""" |
Make MyHDL Signal objects for each signal entry. |
|
Returns: A dict with all the created signal objects |
Note: Previous signal objects will be *unreferenced*. |
""" |
retval = OrderedDict() |
for key, sig in self.signals.iteritems(): |
# use bool for 1-bit signals |
if sig["width"] == 1: |
sig["signal_obj"] = Signal(bool(0)) |
else: |
sig["signal_obj"] = Signal(intbv(0)[sig["width"]:]) |
retval[key] = sig["signal_obj"] |
return retval |
|
def get_complement_signal(self, signalname): |
""" |
Get the signal name that should be connected to this signal when |
connecting two intercon. |
|
Arguments: |
* signalname: signal name of this intercon |
|
Return: a string with the name of a signal from a complementary intercon. |
""" |
return None |
|
def create_complementary(self, newname="", **kwargs): |
""" |
Create a instance of a complementary type |
|
Arguments: |
* newname : optional new name for the created object. By default use |
the same name as self. |
* optional list of arguments to use for new object. By default the |
created object copy its attributes from this object. |
""" |
# prepare list of arguments |
if newname == "": |
newname = self.name |
if "name" not in kwargs: |
kwargs["name"] = newname |
# extract list of attributes, excluding some... |
names = filter(lambda s: s[1] != "_", dir(self)) |
names = filter(lambda s: s not in ("name", "intercon_type", "complement", "signals", "sigmapping"), names) |
# and, if not defined in kwargs, use self attributes |
for s in names: |
if s not in kwargs: |
kwargs[s] = getattr(self, s) |
|
if self.complement is None: |
# I'm my complement? like a object clone |
# use my class reference, so the constructor is correctly called. |
return self.__class__(**kwargs) |
else: |
return self.complement(**kwargs) |
|
# Special intercon to implement a signal container |
class signalset(intercon): |
""" |
Signal container based on an intercon. |
|
Attributes: |
* name : |
* intercon_type : "signalset" |
* complement : Without complement intercon by default |
* description : |
* signals : dictionary with the list of signals. The structure is: |
* keys : signal's name |
* values : |
* "width" : bit length of this signal |
* "direction" : "in" or "out" |
* "signal_obj" : MyHDL signal reference |
* "description" : |
* Optional keys can be added to this dictionary. |
|
Notes: |
* This object makes a MyHDL signal when method "add_signal" is used. |
* This object implements a custom complement signal mechanism. |
""" |
|
def __init__(self, name, **kwargs): |
intercon.__init__(self, name, **kwargs) |
self.complement_mapping = {} |
|
self.intercon_type = "signalset" |
self.complement = None |
self.sideinfo = "" |
|
def add_signal(self, signalname, direction, bitwidth, signalref=None, complname=None, description=""): |
""" |
Add a signal entry to signalset |
|
Arguments: |
* signalname: |
* direction: "in" or "out" |
* bitwidth: must be >= 1 |
* signalref: optional MyHDL signal. If None, it will create a new signal. |
* complname: optional complement signal name. This string will be |
returned on "get_complement_signal" method. |
* description: |
|
Returns: |
* The contents of signalref |
""" |
intercon.add_signal(self, signalname, direction, bitwidth, signalref, description) |
|
# need signal creation at this point |
if signalref is None: |
if bitwidth == 1: |
signalref = Signal(bool(0)) |
else: |
signalref = Signal(intbv(0)[bitwidth:]) |
self.signals[signalname]["signal_obj"] = signalref |
|
# complement signal mapping |
if isinstance(complname, str): |
self.complement_mapping[signalname] = complname |
|
return signalref |
|
def get_complement_signal(self, signalname): |
""" |
Get the signal name that should be connected to this signal when |
connecting two signalref (or intercon). Return value depends on |
signal creation arguments (see "add_signal" method) |
|
Arguments: |
* signalname: signal name of this intercon |
|
Return: a string with complementary signal name, or None if not found. |
""" |
if signalname not in self.signals: |
raise KeyError("Signal '%s' not found" % signalname) |
|
if signalname in self.complement_mapping: |
return self.complement_mapping[signalname] |
else: |
return None |
|
# network and transport layers |
class protocol(): |
""" |
Protocol base object |
|
This object represents the protocol that the NoC objects use. This |
703,9 → 1121,12
|
Attributes: |
* name |
* description : a string with a brief description |
* long_desc : a long string with a detailed description, usually formatted |
in reST (as any Python help string). |
|
Notes: |
* Optional arguments "packet_format" and "packet_order" can be |
* Optional arguments "packet_format" can be |
added at object construction, but will not check its data consistency. |
At the moment, we recommend using update_packet_field() method to |
fill this data structures. |
717,20 → 1138,25
Notes: |
* Optional arguments will be added as object attributes. |
""" |
# NOTE: to avoid python version requirements (2.7), implement ordered |
# dict with an additional list. When we are sure of using |
# Python > 2.7 , change to collections.OrderedDict |
self.name = name |
self.packet_format = {} |
self.packet_order = [] |
self.packet_format = OrderedDict() |
self.packet_class = packet |
self.packet_bitlen = 0 |
self.flit_bitlen = 0 |
self.flit_fixcount = 0 |
self.flit_padbits = 0 |
self.variable_packet = False |
self.description = "" |
self.long_desc = "" |
for key in kwargs.keys(): |
setattr(self, key, kwargs[key]) |
|
def get_protocol_ref(self): |
# override to use myself |
return self |
|
def __repr__(self): |
if self.name != "": |
return "<%s '%s'>" % (self.__class__.__name__, self.name) |
else: |
return "<%s at '%d'>" % (self.__class__.__name__, id(self)) |
|
def update_packet_field(self, name, type, bitlen, description=""): |
""" |
Add or update a packet field. |
737,24 → 1163,29
|
Arguments |
* name |
* type: string that can be "int", "fixed" or "float" |
* type: string that can be "int", "uint", "fixed" or "float" |
* bitlen: bit length of this field |
* description: optional description of this field |
|
Notes: |
* Each new field will be added at the end. |
* Fields are configured to have a bit range inside the packet, |
* starting at 0. Also, it refers to the fixed part of the packet. |
* Fields "msb" and "lsb" are indexes relative to MSB bit of the first |
field (Big endian scheme), completely different to a |
bit vector indexing. |
""" |
if (type != "int") and (type != "fixed") and (type != "float"): |
raise ValueError("Argument 'type' must be 'int', 'fixed' or 'float'.") |
if type not in ("int", "uint", "fixed", "float"): |
raise ValueError("Argument 'type' must be 'int', 'uint', 'fixed' or 'float'.") |
|
if name in self.packet_format: |
# update field |
previdx = self.packet_order.index(name) - 1 |
previdx = self.packet_format.keys().index(name) - 1 |
if previdx < 0: |
# first field |
lastbitpos = 0 |
else: |
lastbitpos = self.packet_format[self.packet_order[previdx]][lsb] |
lastbitpos = self.packet_format[self.packet_format.keys()[previdx]]["lsb"] |
nextbitpos = lastbitpos + bitlen |
self.packet_format[name]["type"] = type |
self.packet_format[name]["position"] = previdx + 1 |
764,23 → 1195,74
self.packet_format[name]["lsb"] = nextbitpos |
self.packet_format[name]["msb"] = lastbitpos |
# iterate through the rest of the fields adjusting lsb and msb |
for idx in range(previdx+2, len(self.packet_order)): |
curname = self.packet_order[idx] |
for idx in range(previdx+2, len(self.packet_format.keys())): |
curname = self.packet_format.keys()[idx] |
curbitlen = self.packet_format[curname]["bitlen"] |
self.packet_format[curname]["lsb"] = nextbitpos + curbitlen |
self.packet_format[curname]["msb"] = nextbitpos |
nextbitpos += curbitlen |
self.packet_bitlen = nextbitpos |
else: |
# append |
if len(self.packet_format) == 0: |
lastbitpos = 0 |
else: |
lastbitpos = self.packet_format[self.packet_order[-1]]["lsb"] |
lastbitpos = self.packet_format[self.packet_format.keys()[-1]]["lsb"] + 1 |
nextbitpos = lastbitpos + bitlen |
fieldpos = len(self.packet_order) |
self.packet_format[name] = {"type": type, "position": fieldpos, "bitlen": bitlen, "lsb": nextbitpos, "msb": lastbitpos} |
self.packet_order.append(name) |
fieldpos = len(self.packet_format) |
self.packet_format[name] = {"type": type, "position": fieldpos, "bitlen": bitlen, "lsb": nextbitpos - 1, "msb": lastbitpos} |
self.packet_bitlen = nextbitpos |
|
def get_field_info(self, name): |
""" |
Get information about a existing packet field. |
|
Arguments: |
* name |
Returns a dict with the following information: |
* "type" |
* "pkt_bitpos": absolute bit position in the packet (msb) |
* "bitlen": bit size |
* "flit_num": which flit use this field |
* "flit_bitpos": bit position inside the flit |
""" |
if name not in self.packet_format: |
raise ValueError("Packet field '%s' not found." % name) |
retinfo = {} |
retinfo["type"] = self.packet_format[name]["type"] |
retinfo["bitlen"] = self.packet_format[name]["bitlen"] |
retinfo["pkt_bitpos"] = self.packet_format[name]["msb"] |
if getattr(self, "flit_bitlen", 0) == 0: |
# not using flits |
retinfo["flit_num"] = 0 |
retinfo["flit_position"] = self.packet_format[name]["msb"] |
else: |
# CHECK THIS |
retinfo["flit_num"] = int(self.packet_format[name]["msb"] / self.flit_bitlen) |
retinfo["flit_position"] = self.packet_format[name]["msb"] - retinfo["flit_num"] |
|
return retinfo |
|
def configure_flits(self, flit_size, variable_packet=False): |
""" |
Configure the flit split in a packet. |
|
Arguments: |
* flit_size: size in bits of a flit. Based on this size a packet |
is split, and a zero padding added if necessary. |
* variable_packet: If true, allows the packet to have a variable |
packet size by adding additional flits at the end. |
|
NOTE: variable packet mechanism is still under development. |
""" |
if flit_size <= 0: |
raise ValueError("Argument 'flit_size' must be greater than zero.") |
self.flit_bitlen = flit_size |
# calculate the number of flits of the fixed part of the packet |
self.flit_fixcount = int(mathceil(float(self.packet_bitlen) / float(self.flit_bitlen))) |
self.flit_padbits = (self.flit_fixcount*self.flit_bitlen) - self.packet_bitlen |
self.variable_packet = variable_packet |
|
def newpacket(self, zerodefault=True, *args, **kwargs): |
""" |
Return a new packet with all required fields. |
798,7 → 1280,7
nameless arguments. |
""" |
retpacket = self.packet_class(protocol_ref=self) |
fieldlist = self.packet_order[:] |
fieldlist = self.packet_format.keys() |
# first named arguments |
for fkey, fvalue in kwargs.iteritems(): |
if fkey in fieldlist: |
806,7 → 1288,7
fieldlist.remove(fkey) |
# then nameless |
for fidx, fvalue in enumerate(args): |
fkey = self.packet_order[fidx] |
fkey = self.packet_format.keys()[fidx] |
if fkey in fieldlist: |
retpacket[fkey] = fvalue |
fieldlist.remove(fkey) |
818,6 → 1300,100
else: |
raise ValueError("Missing fields in argument list: %s" % repr(fieldlist)) |
return retpacket |
|
def newpacket_frombinary(self, binaryinput): |
""" |
Return a new packet based on a binary representation |
|
Arguments: |
* binaryinput: integer or intbv with the binary representation |
of the packet. |
""" |
if isinstance(binaryinput, (int, long)): |
theinput = intbv(binaryinput)[self.packet_bitlen:] |
elif isinstance(binaryinput, intbv): |
theinput = binaryinput |
else: |
raise ValueError("Unsupported type for binaryinput: '%s'" % repr(type(binaryinput))) |
retpacket = self.packet_class(protocol_ref=self) |
for field, field_info in self.packet_format.iteritems(): |
# NOTE: msb and lsb indexes are referred as 0 as the MSB bit |
# recalculate to have the LSB bit at 0 |
msb = self.packet_bitlen - field_info["msb"] |
lsb = self.packet_bitlen - field_info["lsb"] - 1 |
if field_info["type"] == "int": |
#retpacket[field] = theinput[msb:lsb].signed() |
retpacket[field] = theinput[msb:lsb] |
elif field_info["type"] == "uint": |
retpacket[field] = theinput[msb:lsb] |
else: |
raise NotImplementedError("Field %s type %s not supported yet." % (field, field_info["type"])) |
retpacket.prev_repr = binaryinput |
return retpacket |
|
def newpacket_fromflits(self, flits_list): |
""" |
Return a new packet based on a list of flits |
|
Arguments: |
* flits_list: list of integers or intbv with the binary |
representation of each flit. |
""" |
if not isinstance(flits_list, (list, tuple)): |
raise ValueError("Unsupported type for flits_list: '%s'" % repr(type(flits_list))) |
extracted = [] |
flit_curbit = self.flit_bitlen |
flit_idx = 0 |
for field, field_info in self.packet_format.iteritems(): |
flit_val = intbv(0)[field_info["bitlen"]:] |
|
msb = flit_curbit |
lsb = flit_curbit - field_info["bitlen"] |
|
if lsb < 0: |
|
# split packet field into several flits |
lsb_pend = -lsb |
|
# first part |
flit_val[:lsb_pend] = flits_list[flit_idx][flit_curbit:] |
|
flit_idx += 1 |
flit_curbit = self.flit_bitlen |
|
while lsb_pend > 0: |
if lsb_pend >= self.flit_bitlen: |
flit_val[lsb_pend:lsb_pend-self.flit_bitlen] = flits_list[flit_idx] |
flit_idx += 1 |
flit_curbit = self.flit_bitlen |
lsb_pend -= field_info["bitlen"] |
else: |
# last flit |
flit_val[lsb_pend:] = flits_list[flit_idx][:self.flit_bitlen-lsb_pend] |
flit_curbit -= lsb_pend |
lsb_pend = 0 |
else: |
flit_val = flits_list[flit_idx][msb:lsb] |
flit_curbit -= field_info["bitlen"] |
|
extracted.append(flit_val) |
if lsb == 0: |
# next flit |
flit_idx += 1 |
flit_curbit = self.flit_bitlen |
|
retpacket = self.packet_class(protocol_ref=self) |
for field, content in zip(self.packet_format.keys(), extracted): |
if self.packet_format[field]["type"] == "int": |
#retpacket[field] = theinput[msb:lsb].signed() |
retpacket[field] = content |
elif self.packet_format[field]["type"] == "uint": |
retpacket[field] = content |
else: |
raise NotImplementedError("Field %s type %s not supported yet." % (field, self.packet_format[field]["type"])) |
retpacket.prev_repr = flits_list |
return retpacket |
|
def register_packet_generator(self, packet_class): |
""" |
Register a special packet generator class. |
842,13 → 1418,120
|
Attributes: |
* protocol_ref: protocol object that created this object. |
* prev_repr: previous representation of this packet. Can be: |
- None: this packet was created by each field data |
- <type long> : was created with a numeric representation. |
- <type list> : was created with a list of flits |
This attribute should only be changed by its protocol object. |
""" |
|
# TODO: add support for flit construction: temporal storage for flits, |
# and package construction after final flit |
|
# the constructor |
def __init__(self, *args, **kwargs): |
# look for a protocol_ref key |
self.protocol_ref = kwargs.pop("protocol_ref", None) |
self.prev_repr = None |
dict.__init__(self, *args, **kwargs) |
|
|
# override copy method |
def copy(self): |
# Warning: take account of each element inside, specially if it's |
# a intbv |
basedict = dict(**self) |
for k, v in self.iteritems(): |
if isinstance(v, intbv): |
# make a copy |
basedict[k] = intbv(v) |
# use same keys to build the new packet |
pktobj = packet(**basedict) |
pktobj.protocol_ref = self.protocol_ref |
pktobj.prev_repr = self.prev_repr |
return pktobj |
|
def get_flit_repr(self): |
""" |
Returns a list of integers with the binary representation of the |
full packet contents, separated in flits. |
""" |
protocol_ref = self.protocol_ref |
binary_flits = [intbv(0)[protocol_ref.flit_bitlen:] for i in range(protocol_ref.flit_fixcount)] |
flit_curbit = protocol_ref.flit_bitlen |
flit_idx = 0 |
for field, field_info in protocol_ref.packet_format.iteritems(): |
if field_info["type"] == "int" or field_info["type"] == "uint": |
bitvalue = intbv(self[field])[field_info["bitlen"]:] |
elif field_info["type"] == "fixed": |
raise NotImplementedError("Don't know how to put a fixed point in a binary representation.") |
elif field_info["type"] == "float": |
raise NotImplementedError("Don't know how to put a float in a binary representation.") |
|
#{"type": type, "position": fieldpos, "bitlen": bitlen, "lsb": nextbitpos, "msb": lastbitpos} |
msb_flit = flit_curbit |
lsb_flit = flit_curbit - field_info["bitlen"] |
|
if lsb_flit < 0: |
# split packet field into several flits |
lsb_flit_pend = -lsb_flit |
|
# first flit |
binary_flits[flit_idx][msb_flit:] = bitvalue[field_info["bitlen"]:flit_curbit] |
flit_idx += 1 |
flit_curbit = protocol_ref.flit_bitlen |
while lsb_flit_pend > 0: |
if lsb_flit_pend >= protocol_ref.flit_bitlen: |
binary_flits[flit_idx] = bitvalue |
flit_idx += 1 |
flit_curbit = protocol_ref.flit_bitlen |
lsb_flit_pend -= field_info["bitlen"] |
else: |
# last flit |
binary_flits[flit_idx][:protocol_ref.flit_bitlen - lsb_flit_pend] = bitvalue[lsb_flit_pend:] |
flit_curbit -= lsb_flit_pend |
|
else: |
binary_flits[flit_idx][msb_flit:lsb_flit] = bitvalue |
flit_curbit -= field_info["bitlen"] |
|
if lsb_flit == 0: |
# next flit |
flit_idx += 1 |
flit_curbit = protocol_ref.flit_bitlen |
#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) |
|
#print "integer repr: TEMP BIN %d b%s" % (binaryout, bin(binaryout)) |
#print "integer repr: FINAL BIN %d b%s" % (binaryout, bin(binaryout)) |
return binary_flits |
|
def get_integer_repr(self): |
""" |
Returns an integer with the binary representation of the |
full packet contents. |
""" |
protocol_ref = self.protocol_ref |
#binaryout = 0 |
binaryout = intbv(0)[protocol_ref.packet_bitlen:] |
for field, field_info in protocol_ref.packet_format.iteritems(): |
bitvalue = self[field] |
#{"type": type, "position": fieldpos, "bitlen": bitlen, "lsb": nextbitpos, "msb": lastbitpos} |
# NOTE: msb and lsb indexes are referred as 0 as the MSB bit |
# recalculate to have the LSB bit at 0 |
msb = protocol_ref.packet_bitlen - field_info["msb"] |
lsb = protocol_ref.packet_bitlen - field_info["lsb"] - 1 |
#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) |
if field_info["type"] == "int" or field_info["type"] == "uint": |
#binaryout |= (bitvalue << lsb) |
binaryout[msb:lsb] = intbv(bitvalue)[field_info["bitlen"]:] |
elif field_info["type"] == "fixed": |
raise NotImplementedError("Don't know how to put a fixed point in a binary representation.") |
elif field_info["type"] == "float": |
raise NotImplementedError("Don't know how to put a float in a binary representation.") |
#print "integer repr: TEMP BIN %d b%s" % (binaryout, bin(binaryout)) |
#print "integer repr: FINAL BIN %d b%s" % (binaryout, bin(binaryout)) |
return binaryout |
|
|
# ******************************* |
# Additional functions |
/noc_guilib.py
48,6 → 48,7
has_matplotlib = False |
|
import networkx as nx |
import warnings |
|
from noc_base import * |
|
64,7 → 65,7
are tuples with x and y positions. |
""" |
if not has_matplotlib: |
print("Function not available") |
warnings.warn("Function not available") |
return None |
|
# node positions |
115,4 → 116,5
# box with ipcore labels |
ax.text(thepos[0]+ip_relpos[0], thepos[1]+ip_relpos[1], i.name, horizontalalignment="center", bbox=dict(facecolor='green', alpha=0.2)) |
|
# adjust axis TODO! |
plt.show() |
/noc_tbm_base.py
0,0 → 1,308
#!/usr/bin/env python |
# -*- coding: utf-8 -*- |
|
# |
# NoC TBM simulation support |
# This module declares classes for Transaction Based Model simulation |
# |
# Author: Oscar Diaz |
# Version: 0.2 |
# Date: 17-03-2011 |
|
# |
# This code is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This code is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the |
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
# Boston, MA 02111-1307 USA |
# |
|
# |
# Changelog: |
# |
# 03-03-2011 : (OD) initial release |
# |
|
""" |
=============================== |
NoCmodel TBM simulation support |
=============================== |
|
This module declares classes for Transaction Based Model simulation |
|
* Class 'noc_tbm_base' |
* Class 'noc_tbm_simulation' |
* Class 'noc_tbm_errcodes' |
""" |
|
import networkx as nx |
import myhdl |
import logging |
|
from noc_base import * |
|
import inspect |
|
class noc_tbm_base(): |
""" |
Base class for NoC TBM simulator. |
|
This class add methods to a NoC object, required for the TBM model. Each |
derived class must override the methods: |
|
* __init__() : its constructor contains the object TBM model (data |
structures, generators, etc). |
* send() |
* recv() |
|
Other methods are related to simulation configuration and logging support. |
""" |
def __init__(self): |
self.log = logging.getLogger() |
self.logname = "BASECLASS" |
self.generators = [] |
self.tracesend = [] |
self.tracerecv = [] |
|
def __repr__(self): |
if self.logname != "": |
return "<%s '%s'>" % (self.__class__.__name__, self.logname) |
else: |
return "<%s at '%d'>" % (self.__class__.__name__, id(self)) |
|
def get_generators(self): |
return self.generators |
|
def _do_build_generators(self): |
self.generators.extend(self.build_generators()) |
|
# TBM main: every object must define this functions |
def send(self, src, dest, data, addattrs=None): |
""" |
SEND method: this method MUST be called only by the local |
object who wants to start a transaction. |
|
This function will call the recv method in the right object. |
|
Arguments: |
* src: source object (or router address) that call this method, i.e. the |
object that starts the transaction. |
* dest: destination object (or router address) that receive the data in |
the transaction. This method will call dest' recv method. |
* data: data to be sent. Can be anything, but normally is an object of |
type packet. |
* addattrs: optional dictionary with additional arguments |
|
Return: Must return a number: 0 for everything OK, != 0 to show an error |
relevant to the caller, an exception in case of attribute error |
""" |
self.debug("-> send( %s , %s , %s , %s )" % (repr(src), repr(dest), repr(packet), repr(addattrs))) |
return noc_tbm_errcodes.not_implemented |
|
def recv(self, src, dest, data, addattrs=None): |
""" |
RECV method: this method MUST be called only by the send |
method of the object who started the transaction. |
|
Arguments: |
* src: source object (or router address) that call this method, i.e. the |
object that starts the transaction. |
* dest: destination object (or router address) that receive the data in |
the transaction. This method will call dest' recv method. |
* data: data to be sent. Can be anything, but normally is an object of |
type packet. |
* addattrs: optional dictionary with additional arguments |
|
@return Must return a number: 0 for everything OK, != 0 to show an error |
relevant to the caller, an exception in case of attribute error |
""" |
self.debug("-> recv( %s , %s , %s , %s )" % (repr(src), repr(dest), repr(packet), repr(addattrs))) |
return noc_tbm_errcodes.not_implemented |
|
# logging methods (only use 4 levels) |
def debug(self, msg, *args, **kwargs): |
self.log.debug(msg, extra={"objname": self.logname}, *args, **kwargs) |
def info(self, msg, *args, **kwargs): |
self.log.info(msg, extra={"objname": self.logname}, *args, **kwargs) |
def warning(self, msg, *args, **kwargs): |
self.log.warning(msg, extra={"objname": self.logname}, *args, **kwargs) |
def error(self, msg, *args, **kwargs): |
self.log.error(msg, extra={"objname": self.logname}, *args, **kwargs) |
|
# special log |
def debugstate(self): |
self.debug(" '%s' object state: " % repr(self)) |
for i in dir(self): |
# exclude hidden attributes |
if i[0] == "_": |
continue |
self.debug(" ['%s'] = %s " % (i, repr(getattr(self, i)))) |
def generators_info(self): |
self.debug(" Registered generators for '%s': " % repr(self)) |
for g in self.generators: |
self.debug(" '%s': %s" % (getattr(g, "name", "-GEN-"), repr(g))) |
|
class noc_tbm_simulation(): |
""" |
NoC TBM simulator object |
|
This class manages the MyHDL simulation on a NoC object and its logging |
support. |
|
Attributes: |
* noc_ref: reference to NoC model to simulate |
* log_file: optional file to save the simulation log |
* log_level: optional logging level for the previous file |
* kwargs: optional attributes to add to this object |
""" |
def __init__(self, noc_ref, log_file=None, log_level=logging.INFO, **kwargs): |
if isinstance(noc_ref, noc): |
self.noc_ref = noc_ref |
else: |
raise TypeError("This class needs a noc object as constructor argument.") |
# configure logging system |
# log errors to console, custom log to log_file if specified |
addmsg = "" |
self.log = logging.getLogger() |
self.log.setLevel(log_level) |
console_hdl = logging.StreamHandler() |
console_hdl.setLevel(logging.WARNING) |
class SimTimeFilter(logging.Filter): |
def filter(self, record): |
record.myhdltime = myhdl.now() |
return True |
self.log.addFilter(SimTimeFilter()) |
self.noc_formatter = logging.Formatter("%(myhdltime)4d:%(levelname)-5s:%(objname)-16s - %(message)s") |
console_hdl.setFormatter(self.noc_formatter) |
self.log.addHandler(console_hdl) |
if log_file != None: |
file_hdl = logging.FileHandler(log_file, 'w') |
file_hdl.setLevel(log_level) |
file_hdl.setFormatter(self.noc_formatter) |
self.log.addHandler(file_hdl) |
addmsg = "and on file (%s) level %s" % (log_file, logging._levelNames[log_level]) |
# ready to roll |
self.debug("Logging enabled! Running log on console level WARNING %s" % addmsg) |
|
def configure_simulation(self, max_time=None, add_generators=[]): |
""" |
Configure MyHDL simulation. |
|
Arguments: |
* max_time: optional max time to simulate. None means simulation |
without time limit. |
* add_generators: external MyHDL generators to add to the simulation |
""" |
# myhdl simulation: extract all generators and prepare |
# arguments |
for obj in self.noc_ref.all_list(): |
prevcount = len(add_generators) |
add_generators.extend(obj.tbm.get_generators()) |
#self.debug("configure_simulation: adding %d generators from object %s" % (len(add_generators)-prevcount, repr(obj))) |
if isinstance(obj, ipcore): |
add_generators.extend(obj.channel_ref.tbm.get_generators()) |
#self.debug("configure_simulation: plus ipcore channel: adding %d generators from object %s" % (len(add_generators)-prevcount, repr(obj.channel_ref))) |
# -------------------------------- |
# debug info |
# TODO: try to get info about generators, particularly obtain origin |
# info about @always and @always_comb generators |
#self.debug("configure_simulation: list of generators: (count = %d)" % len(add_generators)) |
#for genl in add_generators: |
#if not isinstance(genl, list): |
#gen2 = [genl] |
#else: |
#gen2 = genl |
#self.debug("configure_simulation: generator list '%s'" % repr(genl)) |
#for gen in gen2: |
#self.debug("configure_simulation: generator '%s'" % repr(gen)) |
#try: |
#self.debug("configure_simulation: inspect info name '%s'" % gen.gen.__name__) |
#self.debug("configure_simulation: inspect info locals '%s'" % repr(gen.gen.gi_frame.f_locals.keys())) |
#for k, v in gen.gen.gi_frame.f_locals.iteritems(): |
#self.debug("configure_simulation: inspect info locals[%s] '%s'" % (k, repr(v))) |
#if gen.gen.__name__ == "genfunc": |
#self.debug("configure_simulation: inspect info deep name '%s'" % gen.func.__name__) |
#except: |
#pass |
# -------------------------------- |
self.sim_object = myhdl.Simulation(*add_generators) |
self.sim_duration = max_time |
self.debug("configure_simulation: will run until simulation time '%d'" % max_time) |
|
def run(self): |
""" |
Run MyHDL simulation |
""" |
self.debug("Start simulation") |
self.sim_object.run(self.sim_duration) |
self.debug("End simulation") |
|
# custom logging methods (only use 4 levels) |
def debug(self, msg, *args, **kwargs): |
self.log.debug(msg, extra={"objname": "TopNoC"}, *args, **kwargs) |
def info(self, msg, *args, **kwargs): |
self.log.info(msg, extra={"objname": "TopNoC"}, *args, **kwargs) |
def warning(self, msg, *args, **kwargs): |
self.log.warning(msg, extra={"objname": "TopNoC"}, *args, **kwargs) |
def error(self, msg, *args, **kwargs): |
self.log.error(msg, extra={"objname": "TopNoC"}, *args, **kwargs) |
|
# special log filter: log individually by object name |
def configure_byobject_logging(self, basefilename="", log_level=logging.INFO): |
""" |
Special log filter: log individually by object name |
|
Arguments: |
* basefilename: generated filenames will start with this string |
* log_level: optional logging level for previous files |
""" |
# base filter |
class ObjFilter(logging.Filter): |
def __init__(self, basename): |
self.basename = basename |
def filter(self, record): |
if record.objname == self.basename: |
return True |
return False |
# need a handler for each object |
for obj in self.noc_ref.all_list(): |
newfilter = ObjFilter(obj.tbm.logname) |
newhandler = logging.FileHandler("%s_%s.log" % (basefilename, obj.tbm.logname), "w") |
newhandler.setLevel(log_level) |
newhandler.addFilter(newfilter) |
newhandler.setFormatter(self.noc_formatter) |
self.log.addHandler(newhandler) |
# Transactions logger |
class TransFilter(logging.Filter): |
def filter(self, record): |
if record.message.find("->") == 0: |
return True |
return False |
newhandler = logging.FileHandler("%s_transactions.log" % basefilename, "w") |
newhandler.setLevel(log_level) |
newhandler.addFilter(TransFilter()) |
newhandler.setFormatter(self.noc_formatter) |
self.log.addHandler(newhandler) |
# TopNoC will not be added to this set |
self.debug("Special logging enabled. basefilename=%s level %s" % (basefilename, logging._levelNames[log_level])) |
|
class noc_tbm_errcodes(): |
""" |
Common error codes definition |
""" |
no_error = 0 |
full_fifo = -1 |
packet_bad_data = -2 |
tbm_badcall_recv = -3 |
tbm_badcall_send = -4 |
tbm_busy_channel = -5 |
not_implemented = -15 |
/noc_codegen_base.py
0,0 → 1,520
#!/usr/bin/env python |
# -*- coding: utf-8 -*- |
|
# |
# NoC Code generator |
# This module defines the basic structure for code generation |
# |
# Author: Oscar Diaz |
# Version: 0.2 |
# Date: 11-11-2011 |
|
# |
# This code is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This code is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the |
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
# Boston, MA 02111-1307 USA |
# |
|
# |
# Changelog: |
# |
# 11-11-2011 : (OD) initial release |
# |
|
""" |
Base for code generation support |
|
This module defines: |
* Class 'noc_codegen_base' |
* Class 'noc_codegen_ext' |
""" |
|
from myhdl import intbv, SignalType |
from nocmodel import * |
|
#import scipy |
#import networkx as nx |
from copy import deepcopy |
from sys import exc_info |
import warnings |
|
class noc_codegen_base(): |
""" |
Base class for code generator |
|
This class holds a basic model of a hardware description. Although inspired |
in VHDL, it also holds for Verilog. |
|
Note that its necessary a defined noc model to generate this code model. |
|
Note that this class is meant to be extended by a particular code generator: |
VHDL generator, Verilog generator, etc. |
|
This object will assume a module has the following sections: |
-------------------- |
<docheader> : File header and documentation about the module |
<libraries> : Declarations at the beginning (VHDL: libraries declarations) |
<modulename> : Module name (VHDL: entity name) |
<generics> : Parameters for the module (VHDL: generics inside an entity) |
<ports> : Declaration of inputs and outputs (VHDL: ports inside an entity) |
<implementation> : Code implementation (pure VHDL or Verilog code) |
-------------------- |
|
Objects type: |
* docheader, libraries, modulename and implementation are simple strings. |
* generics are list of dictionaries with this required keys: |
* "class" : always "generic" |
* "name" |
* "type" : string formatted data type |
* "type_array" : 2-element list of strings that declares array boundaries |
either using numbers or generics names. None for simple data types |
* "default_value" |
* "current_value" |
* "description" |
* ports are list of dictionaries with this required keys: |
* "class" : always "port" |
* "name" |
* "type" : port type, if applicable |
* "nocport" : index to related port in the nocobject' ports dictionary or |
others signal containers in the nocobject. |
* "signal_list" : list of signals that compose the port |
* "description" |
* external_signals are list of signals that aren't related to any port. |
* signals are dictionaries with this required keys: |
* "class" : always "signal" |
* "name" |
* "type" : string formatted data type |
* "type_array" : 2-element list of strings that declares array boundaries |
either using numbers or generics names. None for simple data types |
* "direction" |
* "default_value" |
* "related_generics" : when the data type is an array, this list has |
references to generics to be used to build array declaration. |
* "description" |
|
Attributes: |
* modulename |
* nocobject_ref |
* docheader |
* libraries |
* generics |
* ports |
* external_signals |
* implementation |
* interface_hash |
|
Notes: |
* Code generation methods are meant to return strings. The user is |
responsible to write it in files. |
* This object maintains the code model and use it to generate code. Any |
particular changes or adjustments to the model should be done by a |
'noc_codegen_ext' object. |
* This code generation model are restricted to noc models, because |
routers and ipcores are generated by MyHDL code converter (this |
could change if the noc objects needs to keep hierarchy in its |
internal implementation). |
This converter keeps routers and ipcores hierarchy. |
""" |
|
def __init__(self, nocobject_ref, **kwargs): |
# nocobject reference |
if not isinstance(nocobject_ref, (nocobject, noc)): |
raise TypeError("Argument must be an instance of nocobject or noc class") |
self.nocobject_ref = nocobject_ref |
|
# External conversion flag |
self.external_conversion = False |
|
# string type objects |
self.docheader = "" |
self.libraries = "" |
self.modulename = "" |
|
# list of dictionaries (generic and port) |
self.generics = [] |
self.ports = [] |
self.external_signals = [] |
|
# implementation |
self.implementation = "" |
|
# module hash |
self.interface_hash = "" |
|
# optional arguments |
for key in kwargs.keys(): |
setattr(self, key, kwargs[key]) |
|
# main methods |
def generate_file(self): |
""" |
Generate the entire file that implements this object. |
""" |
raise NotImplementedError("What language for code generation?") |
|
def generate_component(self): |
""" |
Generate a component definition for this object. |
""" |
raise NotImplementedError("What language for code generation?") |
|
def generate_generic_declaration(self, generic=None, with_default=False): |
""" |
Generate a generic declaration for this object. |
|
Arguments: |
* generic : either a name or a list index for a particular generic |
* with_default : True to add the default value |
|
Returns: |
* A string when generic argument is used |
* A list of strings with all generics |
""" |
raise NotImplementedError("What language for code generation?") |
|
def generate_port_declaration(self, port=None, with_default=False): |
""" |
Generate a port declaration for this object. |
|
Arguments: |
* port : either a name or a list index for a particular port |
* with_default : True to add the default value |
|
Returns: |
* A list of strings with all signals in port when port argument is used |
* A list of strings with all signals in all ports |
""" |
raise NotImplementedError("What language for code generation?") |
|
def generate_signal_declaration(self, inport=None, signal=None, with_default=False): |
""" |
Generate a signal declaration for this object. |
|
Arguments: |
* inport : either a name or a list index for a particular port. None |
means use the external_signals list |
* signal : either a name or a list index for a particular signal |
* with_default : True to add the default value |
|
Returns: |
* A string when signal argument is used |
* A list of strings with all signals |
""" |
raise NotImplementedError("What language for code generation?") |
|
def make_comment(self, data): |
""" |
Convert string data to language comment |
|
Argument: |
* data: string or list of strings to convert |
|
Returns: string or list of strings with comments added. |
""" |
raise NotImplementedError("What language for code generation?") |
|
def add_tab(self, data, level=1): |
""" |
Add an indentation level to the string |
|
Argument: |
* data: string or list of strings |
* level: how many indentation levels to add. Default 1 |
|
Returns: string or list of strings with <level> indentation levels. |
""" |
raise NotImplementedError("What language for code generation?") |
|
def to_valid_str(self, str_in): |
""" |
Convert an input string, changing special characters used on |
the HDL language. Useful for set names . |
|
Argument: |
* str_in: string to convert |
|
Returns: the converted string. |
""" |
raise NotImplementedError("What language for code generation?") |
|
# codegen model management |
def add_generic(self, name, value, description="", **kwargs): |
""" |
Add a generic to the model. |
|
Arguments: |
* name : must be a string |
* value : default value for this generic |
* description : |
* Optional arguments (just for method call format) |
|
Returns: |
* Reference to added generic dictionary. |
|
Note: |
* This method can override an existing generic entry. |
* This basic method just add name, default value and description. |
Extended class methods must fill correct type arguments. |
""" |
if not isinstance(name, str): |
raise TypeError("Name must be string, not '%s'." % repr(name)) |
|
# supported types: |
if not isinstance(value, (bool, int, intbv, str)): |
raise TypeError("Unsupported type '%s'." % repr(type(value))) |
|
# check if new entry |
g = filter(lambda x: x["name"] == name, self.generics) |
if len(g) == 0: |
g = get_new_generic(name = name) |
self.generics.append(g) |
else: |
g = g[0] |
g.update(default_value = value, description = description) |
# optional kwargs |
g.update(kwargs) |
# return reference to added generic dict |
return g |
|
def add_port(self, name, signal_desc=None, description="", **kwargs): |
""" |
Add a port to the model. |
|
Arguments: |
* name : must be a string |
* signal_desc : optional dictionary with signal information included. |
None to just add/update port without changing its signals. |
* description : |
* Optional arguments (just for method call format) |
|
Returns: |
* Reference to added port dictionary. |
|
Note: |
* This method can add a new port or update an existing port with new |
signals. |
* This basic method just add name and description, and update a signal |
using signal_desc information. Extended class methods must fill |
correct type arguments in the updated signal. |
""" |
if not isinstance(name, str): |
raise TypeError("Name must be string, not '%s'." % repr(name)) |
|
# check signal dict keys: must have at least class and name |
if signal_desc is not None: |
if not all([x in signal_desc for x in ("class", "name")]): |
raise TypeError("Argument signal_desc must be a signal dict ('%s')." % repr(signal_desc)) |
if signal_desc["class"] != "signal": |
raise TypeError("Argument signal_desc must be a signal dict ('%s')." % repr(signal_desc)) |
|
# check if new entry |
p = filter(lambda x: x["name"] == name, self.ports) |
if len(p) == 0: |
p = get_new_port(name = name) |
self.ports.append(p) |
else: |
p = p[0] |
|
# only update description when string is non-empty |
if description != "": |
p.update(description = description) |
|
# check existing signal |
if signal_desc is not None: |
sig = filter(lambda x: x["name"] == signal_desc["name"], p["signal_list"]) |
if len(sig) == 0: |
p["signal_list"].append(signal_desc) |
else: |
sig[0].update(signal_desc) |
|
# optional kwargs |
p.update(kwargs) |
# return reference to added/updated port dict |
return p |
|
def add_external_signal(self, name, direction, value, description="", **kwargs): |
""" |
Add a external signal to the model. |
|
Arguments: |
* name : must be a string |
* direction : "in" or "out" |
* value : initial value for this port |
* description |
* Optional arguments (just for method call format) |
|
Returns: |
* Reference to added external_signals dictionary. |
|
Note: |
* This method can override an existing signal entry. |
* This basic method just add name, direction, intial value and |
description. Extended class methods must fill correct type arguments. |
""" |
if not isinstance(name, str): |
raise TypeError("Name must be string, not '%s'." % repr(name)) |
|
# supported types: |
if isinstance(value, SignalType): |
value = value._init |
if not isinstance(value, (bool, int, intbv)): |
raise TypeError("Unsupported type '%s'." % repr(type(value))) |
if direction not in ["in", "out"]: |
raise ValueError("Direction must be 'in' or 'out', not '%s'." % repr(direction)) |
# check if new entry |
sig = filter(lambda x: x["name"] == name, self.external_signals) |
if len(sig) == 0: |
sig = get_new_signal(name = name) |
self.external_signals.append(sig) |
else: |
sig = sig[0] |
sig.update(direction = direction, default_value = value, description = description) |
# optional kwargs |
sig.update(kwargs) |
# return reference to added generic dict |
return sig |
|
def build_implementation(self): |
""" |
Try to generate the implementation section. Check if a "codemodel" |
object exists and try to call "generate_implementation" method. |
|
If the call is successful, self.implementation will store its return |
string. Do nothing on failure (report a warning). |
""" |
if hasattr(self.nocobject_ref, "codemodel"): |
implementation = "" |
if not hasattr(self.nocobject_ref.codemodel, "generate_implementation"): |
warnings.warn("Method 'generate_implementation()' not found.") |
elif not callable(self.nocobject_ref.codemodel.generate_implementation): |
warnings.warn("Attribute 'generate_implementation()' not callable") |
else: |
implementation = self.nocobject_ref.codemodel.generate_implementation() |
##except: |
### Report a warning instead of the exception |
##exctype, value = exc_info()[:2] |
##warnings.warn("%s: %s" % (exctype, value)) |
if isinstance(implementation, str): |
self.implementation = implementation |
|
def model_hash(self): |
""" |
Calculate a hash string based on the nocobject interface: Generics, |
ports and external signals. |
|
Two nocobject with the same model hash can be instantiated by the same |
module code (with different generics' values). |
|
Returns: a string with a predefined format. |
""" |
hash_str = "" |
|
# 1. Generics information |
hash_gen = [] |
for g in self.generics: |
hash_gen.append([g["name"], g["type"]]) |
# sort generics by name |
hash_gen.sort(key = lambda x: x[0]) |
# add a stream of "<name><type>" |
for g in hash_gen: |
hash_str += "%s%s" % (g[0], g[1]) |
|
# 2. Ports information |
hash_port = [] |
for g in self.ports: |
hash_port.append([g["name"], g["type"], len(g["signal_list"])]) |
# sort ports by name |
hash_port.sort(key = lambda x: x[0]) |
# add a stream of "<name><type><num-of-signals>" |
for g in hash_port: |
hash_str += "%s%s" % (g[0], g[1]) |
|
# 3. External ports information |
hash_ext = [] |
for g in self.external_signals: |
hash_ext.append([g["name"], g["type"], g["direction"]]) |
# sort external_signals by name |
hash_ext.sort(key = lambda x: x[0]) |
# add a stream of "<name><type><direction>" |
for g in hash_ext: |
hash_str += "%s%s" % (g[0], g[1]) |
|
self.interface_hash = hash_str |
return hash_str |
|
class noc_codegen_ext(): |
""" |
Extension class for code generator |
|
This class supports an access point to do transformations on a |
'noc_codegen_base' code model. |
|
This transformation methods should be declared on extended classes of this |
class, and are close related to the particular noc object. |
|
Notes: |
* This object maintains the code model and use it to generate code. Any |
particular changes or adjustments to the model should be done by a |
'noc_codegen_ext' object. Usually this object is related to a particular |
nocobject (router, ipcore or channel). |
""" |
|
def __init__(self, codegen_ref): |
# must be related to a noc_codegen_base object |
if not isinstance(codegen_ref, noc_codegen_base): |
raise TypeError("Argument must be an instance of 'noc_codegen_base' class") |
|
self.codegen_ref = codegen_ref |
self.nocobject_ref = codegen_ref.nocobject_ref |
|
# helper structures: |
# base objects for generics, ports and signals |
_empty_generic = { |
"class": "generic", |
"name": "", |
"type": "", |
"type_array": [None, None], |
"default_value": "", |
"current_value": "", |
"description": ""} |
_empty_port = { |
"class": "port", |
"name": "", |
"type": "", |
"nocport": None, |
"signal_list": [], |
"description": ""} |
_empty_signal = { |
"class": "signal", |
"name": "", |
"type": "", |
"type_array": [None, None], |
"direction": "", |
"default_value": "", |
"related_generics": [], |
"description": ""} |
|
# new structures generation |
def get_new_generic(**kwargs): |
s = deepcopy(_empty_generic) |
s.update(kwargs) |
return s |
def get_new_port(**kwargs): |
s = deepcopy(_empty_port) |
s.update(kwargs) |
return s |
def get_new_signal(**kwargs): |
s = deepcopy(_empty_signal) |
s.update(kwargs) |
return s |
/noc_rtl_myhdl.py
0,0 → 1,259
#!/usr/bin/env python |
# -*- coding: utf-8 -*- |
|
# |
# NoC RTL support in MyHDL |
# This module adds support for mixed model and RTL descriptions in MyHDL |
# |
# Author: Oscar Diaz |
# Version: 0.2 |
# Date: 01-06-2012 |
|
# |
# This code is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This code is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the |
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
# Boston, MA 02111-1307 USA |
# |
|
# |
# Changelog: |
# |
# 03-03-2011 : (OD) initial release |
# 01-06-2012 : (OD) big refactorization |
# |
|
""" |
=============================== |
NoCmodel RTL MyHDL support |
=============================== |
|
This module extends 'noc_tbm_base' class for RTL descriptions in MyHDL |
|
* Class 'noc_rtl_myhdl_base' |
""" |
|
import myhdl |
|
from nocmodel import * |
from noc_tbm_base import * |
|
class noc_rtl_myhdl_base(noc_tbm_base): |
""" |
Extended class for a MyHDL description of a NoC router. |
|
This class adds support for behavioral - RTL mixed modeling and |
full RTL modeling. It is meant to add in NoC objects through |
multiple inheritance mechanism |
|
Features: |
* Keep a list of (Intercon type) interface objects, each object is |
indexed by a port index and each object stores a list of signals |
dict: "interface_objects" |
* Keep a list of external interface objects. Each object is a set of |
signals (use "signalset" class), and are accessed by any python |
object (a string is recommended). |
dict: "external_objects" |
* Keep a list of internal MyHDL signals, and provides correct references |
dict: "internal_signals" |
""" |
# Interface objects methods |
def add_interface(self, port_idx, object_ref=None): |
""" |
Add a new interface |
Arguments: |
""" |
if not hasattr(self, "interface_objects"): |
self._new_interface_objects() |
|
self.interface_objects[port_idx] = object_ref |
return object_ref |
|
def get_interface_signal(self, port_idx, signalname): |
""" |
Try to get a signal reference from the interface object indexed by |
"port_idx". Raise an Exception if not found. |
Arguments: |
* port_idx: |
* signalname: |
Returns: |
the MyHDL signal reference |
""" |
if not hasattr(self, "interface_objects"): |
self._new_interface_objects() |
|
if port_idx not in self.interface_objects: |
raise ValueError("%s : Interface '%s' not found." % (repr(self), repr(port_idx))) |
|
# check if object is list-dict type or a class with signal attributes |
if isinstance(self.interface_objects[port_idx], (tuple, list, dict)): |
# indexing lookup |
if signalname not in self.interface_objects[port_idx]: |
raise AttributeError("%s : Signal '%s' from Interface %s not found." % (repr(self), signalname, repr(port_idx))) |
|
return self.interface_objects[port_idx][signalname] |
else: |
# attribute lookup |
try: |
return getattr(self.interface_objects[port_idx], signalname) |
except: |
raise AttributeError("%s : Signal '%s' from Interface %s not found." % (repr(self), signalname, repr(port_idx))) |
|
def get_interface_all_signals(self, port_idx): |
""" |
Return a dict with all signals references from interface |
Arguments: |
* port_idx: |
Returns: |
dict with references to all signals in interface |
""" |
if not hasattr(self, "interface_objects"): |
self._new_interface_objects() |
return {} |
|
if port_idx not in self.interface_objects: |
raise ValueError("%s : Interface '%s' not found." % (repr(self), repr(port_idx))) |
|
if isinstance(self.interface_objects[port_idx], dict): |
return self.interface_objects[port_idx] |
elif isinstance(self.interface_objects[port_idx], (tuple, list)): |
return dict([(k, v) for k, v in enumerate(self.interface_objects[port_idx])]) |
else: |
# search through all attributes |
retval = {} |
for attrname in dir(self.interface_objects[port_idx]): |
attr = getattr(self.interface_objects[port_idx], attrname) |
if isinstance(attr, myhdl.SignalType): |
retval[attrname] = attr |
return retval |
|
# External interface objects methods |
def add_external_interface(self, index, object_ref=None): |
if not hasattr(self, "external_objects"): |
self._new_external_objects() |
|
if index is None: |
raise ValueError("Index cannot be None.") |
|
# Use an intercon object to model the external |
# interface objects. |
if object_ref is not None: |
if not isinstance(object_ref, signalset): |
raise ValueError("Object_ref must be of signalset type (not %s, type %s)" % (repr(object_ref), type(object_ref))) |
else: |
object_ref = signalset(index) |
|
self.external_objects[index] = object_ref |
return object_ref |
|
def get_external_signal(self, index, signalname): |
""" |
Try to get a signal reference from the external interface object |
indexed by "index". Raise an Exception if not found. |
Arguments: |
* index: |
* signalname: |
Returns: |
the MyHDL signal reference |
""" |
if not hasattr(self, "external_objects"): |
self._new_external_objects() |
|
if index is None: |
# special case: search through all external interfaces |
siglist = [] |
for sigset in self.external_objects.itervalues(): |
try: |
siglist.append(sigset.get_signal_ref(signalname)) |
except: |
pass |
if len(siglist) == 0: |
raise ValueError("%s : signal '%s' not found in any of the external objects." % (repr(self), repr(signalname))) |
elif len(siglist) > 1: |
raise ValueError("%s : signal '%s' found on multiple external objects." % (repr(self), repr(signalname))) |
else: |
return siglist[0] |
|
if index not in self.external_objects: |
raise ValueError("%s : External interface '%s' not found." % (repr(self), repr(index))) |
|
return self.external_objects[index].get_signal_ref(signalname) |
|
def get_external_all_signals(self, index): |
""" |
Return a dict with all signals references from interface |
Arguments: |
* index: |
Returns: |
dict with references to all signals in interface |
""" |
if not hasattr(self, "external_objects"): |
self._new_external_objects() |
|
if index not in self.external_objects: |
raise ValueError("%s : External interface '%s' not found." % (repr(self), repr(index))) |
|
retval = {} |
for signalname in self.external_objects[index].get_signal_allnames(): |
retval[signalname] = self.external_objects[index].get_signal_ref(signalname) |
return retval |
|
# Internal signals methods |
def getset_internal_signal(self, signalname, newsignal_ref=None): |
""" |
Try to get a reference to a internal signal with name "signalname". |
If not found, optionally add a new signal with its reference |
in "newsignal_ref". |
Arguments: |
* signalname: |
* newsignal_ref: |
Returns: |
the MyHDL signal reference, or the contents of "newsignal_ref" |
""" |
if not hasattr(self, "internal_signals"): |
self._new_internal_signals() |
|
if signalname not in self.internal_signals: |
if newsignal_ref is not None: |
if isinstance(newsignal_ref, myhdl.SignalType): |
self.internal_signals[signalname] = newsignal_ref |
else: |
# build a new signal based on newsignal_ref |
self.internal_signals[signalname] = myhdl.Signal(newsignal_ref) |
else: |
raise ValueError("%s : Signal '%s' not found." % (repr(self), signalname)) |
return self.internal_signals[signalname] |
|
def internal_signal_iter(self): |
""" |
Return an iterator object over all the internal signals. |
This iterator works with tuples (signame, sigref). |
""" |
return self.internal_signals.iteritems() |
|
# Methods to override |
def build_generators_codegen(self, withbaseclass=True): |
""" |
Separate generator builder, for hw generators only |
NOTES: |
* Generators in this method must be convertible |
* Use overridden functions |
""" |
raise NotImplementedError |
|
def _new_internal_signals(self): |
self.internal_signals = {} |
def _new_external_objects(self): |
self.external_objects = {} |
def _new_interface_objects(self): |
self.interface_objects = {} |
/basicmodels/basic_channel.py
3,7 → 3,7
|
# |
# Basic Channel model |
# * TLM model |
# * TBM model |
# |
# Author: Oscar Diaz |
# Version: 0.1 |
33,17 → 33,17
# |
|
""" |
Basic channel TLM model |
Basic channel TBM model |
""" |
|
from nocmodel.noc_tlm_base import * |
from nocmodel.noc_tbm_base import * |
|
# --------------------------- |
# Channel TLM model |
# Channel TBM model |
|
class basic_channel_tlm(noc_tlm_base): |
class basic_channel_tbm(noc_tbm_base): |
""" |
TLM model of a NoC channel. It models a simple FIFO channel with |
TBM model of a NoC channel. It models a simple FIFO channel with |
adjustable delay. This channel will move any kind of data as a whole, but |
ideally will move packet objects. |
|
53,8 → 53,8
Notes: |
*This model is completely behavioral |
""" |
def __init__(self, channel_ref, channel_delay=1): |
noc_tlm_base.__init__(self) |
def __init__(self, channel_ref, channel_delay=2): |
noc_tbm_base.__init__(self) |
if isinstance(channel_ref, channel): |
self.channel_ref = channel_ref |
self.graph_ref = channel_ref.graph_ref |
77,7 → 77,7
if self.channel_delay > 0: |
self.has_delay = True |
self.delay_fifo = [] |
self.delay_fifo_max = 10 # error-catch parameter, avoid fifo excessive growing |
self.delay_fifo_max = 4 # error-catch parameter, avoid fifo excessive growing |
self.delay_event = myhdl.Signal(False) |
# implement delay generator |
@myhdl.instance |
88,18 → 88,20
timed_packet = self.delay_fifo.pop(0) |
# calculate the exact delay value |
next_delay = self.channel_delay - (myhdl.now() - timed_packet[0]) |
if next_delay <= 0: |
# time could be 0, when the packets arrive from both endpoints |
# at the same time. In that case don't yield |
if next_delay < 0: |
self.debug("delay_generator CATCH next_delay is '%d'" % next_delay) |
else: |
elif next_delay > 0: |
yield myhdl.delay(next_delay) |
self.debug("delay_generator sending delayed packet (by %d), timed_packet format %s" % (next_delay, repr(timed_packet)) ) |
# use send() |
retval = self.send(*timed_packet[1:]) |
# what to do in error case? report and continue |
if retval != noc_tlm_errcodes.no_error: |
if retval != noc_tbm_errcodes.no_error: |
self.error("delay_generator send returns code '%d'?" % retval ) |
self.delay_event.next = False |
yield self.delay_event |
yield self.delay_event.posedge |
self.generators.append(delay_generator) |
|
self.debugstate() |
118,20 → 120,26
thedest = self.graph_ref.get_router_by_address(dest) |
if thedest == False: |
self.error("-> send: dest %s not found" % repr(dest) ) |
return noc_tlm_errcodes.tlm_badcall_send |
return noc_tbm_errcodes.tbm_badcall_send |
elif isinstance(dest, (router, ipcore)): |
thedest = dest |
else: |
self.error("-> send: what is dest '%s'?" % repr(dest) ) |
return noc_tlm_errcodes.tlm_badcall_send |
return noc_tbm_errcodes.tbm_badcall_send |
|
# check dest as one of the channel endpoints |
if thedest not in self.endpoints: |
self.error("-> send: object %s is NOT one of the channel endpoints [%s,%s]" % (repr(thedest), repr(self.endpoints[0]), repr(self.endpoints[1])) ) |
return noc_tlm_errcodes.tlm_badcall_send |
return noc_tbm_errcodes.tbm_badcall_send |
|
# call trace functions |
traceargs = {"self": self, "src": src, "dest": dest, "packet": packet, "addattrs": addattrs} |
for f in self.tracesend: |
if callable(f): |
f(traceargs) |
|
# call recv on the dest object |
retval = thedest.tlm.recv(self.channel_ref, dest, packet, addattrs) |
retval = thedest.tbm.recv(self.channel_ref, dest, packet, addattrs) |
|
# Something to do with the retval? Only report it. |
self.debug("-> send returns code '%s'" % repr(retval)) |
150,17 → 158,23
thesrc = self.graph_ref.get_router_by_address(src) |
if thesrc == False: |
self.error("-> recv: src %s not found" % repr(src) ) |
return noc_tlm_errcodes.tlm_badcall_recv |
return noc_tbm_errcodes.tbm_badcall_recv |
elif isinstance(src, (router, ipcore)): |
thesrc = src |
else: |
self.error("-> recv: what is src '%s'?" % repr(src) ) |
return noc_tlm_errcodes.tlm_badcall_recv |
return noc_tbm_errcodes.tbm_badcall_recv |
|
# check src as one of the channel endpoints |
if thesrc not in self.endpoints: |
self.error("-> recv: object %s is NOT one of the channel endpoints [%s,%s]" % (repr(thesrc), repr(self.endpoints[0]), repr(self.endpoints[1])) ) |
return noc_tlm_errcodes.tlm_badcall_recv |
return noc_tbm_errcodes.tbm_badcall_recv |
|
# call trace functions |
for f in self.tracerecv: |
if callable(f): |
traceargs = {"self": self, "src": src, "dest": dest, "packet": packet, "addattrs": addattrs} |
f(traceargs) |
|
# calculate the other endpoint |
end_index = self.endpoints.index(thesrc) - 1 |
174,7 → 188,7
self.warning("-> recv: delay_fifo is getting bigger! current size is %d" % len(self.delay_fifo) ) |
# trigger event |
self.delay_event.next = True |
retval = noc_tlm_errcodes.no_error |
retval = noc_tbm_errcodes.no_error |
else: |
# use send() call directly |
router_dest = self.endpoints[end_index] |
/basicmodels/__init__.py
42,6 → 42,8
* Module basic_ipcore |
* Module basic_protocol |
* Module basic_router |
* Module intercon_model |
* Module basic_noc_codegen |
""" |
|
# provided modules |
49,5 → 51,7
from basic_ipcore import * |
from basic_protocol import * |
from basic_router import * |
from intercon_model import * |
from basic_noc_codegen import * |
|
__version__ = "0.1" |
/basicmodels/basic_router.py
3,11 → 3,12
|
# |
# Basic Router model |
# * TLM model |
# * TBM model |
# * Code generation model |
# |
# Author: Oscar Diaz |
# Version: 0.1 |
# Date: 03-03-2011 |
# Version: 0.2 |
# Date: 14-03-2011 |
|
# |
# This code is free software; you can redistribute it and/or |
30,20 → 31,24
# Changelog: |
# |
# 03-03-2011 : (OD) initial release |
# 14-03-2011 : (OD) adding code generation model |
# |
|
""" |
Basic router TLM model |
* Basic router TBM model |
* Router code generation model |
""" |
|
from nocmodel.noc_tlm_base import * |
from nocmodel.noc_tbm_base import * |
from nocmodel.noc_codegen_base import * |
from intercon_model import * |
|
# --------------------------- |
# Router TLM model |
# Router TBM model |
|
class basic_router_tlm(noc_tlm_base): |
class basic_router_tbm(noc_tbm_base): |
""" |
TLM model of a NoC router. This router uses store-and-forward technique, |
TBM model of a NoC router. This router uses store-and-forward technique, |
using the routing information from the router object. This model just |
forward the packet, and if the packet is in its router destination, send it |
to its ipcore. Each package that the ipcore generates is delivered |
57,8 → 62,8
* This model is completely behavioral. |
* See code comments to better understanding. |
""" |
def __init__(self, router_ref, fifo_len=4): |
noc_tlm_base.__init__(self) |
def __init__(self, router_ref, fifo_len=5): |
noc_tbm_base.__init__(self) |
if isinstance(router_ref, router): |
self.router_ref = router_ref |
self.graph_ref = router_ref.graph_ref |
91,6 → 96,7
p["fifo_out"] = [] |
p["fifo_in_event"] = myhdl.Signal(False) |
p["fifo_out_event"] = myhdl.Signal(False) |
self.idlesignal = myhdl.Signal(True) |
|
# extract a list of all fifo event signals |
self.list_fifo_in_events = [i["fifo_in_event"] for i in self.ports_info.itervalues()] |
123,6 → 129,7
while True: |
for port, data in self.ports_info.iteritems(): |
if len(data["fifo_out"]) > 0: |
self.idlesignal.next = False |
if not data["fifo_out_event"].val: |
self.debug("flush_fifo_out CATCH fifo not empty and NO trigger! fifo has %s" % repr(data["fifo_out"])) |
self.info("flush_fifo_out event in port %d" % port) |
130,11 → 137,13
self.debug("flush_fifo_out port %d packet is %s (delay %d)" % (port, repr(packet), self.delay_outfromfifo)) |
# DELAY model: time to move from fifo to external port in destination object |
yield myhdl.delay(self.delay_outfromfifo) |
self.idlesignal.next = False |
# try to send it |
retval = self.send(self.router_ref, data["channel"], packet) |
if retval == noc_tlm_errcodes.no_error: |
if retval == noc_tbm_errcodes.no_error: |
# clean trigger |
data["fifo_out_event"].next = False |
self.debug("flush_fifo_out clean trigger. list %s" % repr(self.list_fifo_out_events)) |
#continue |
else: |
self.error("flush_fifo_out FAILED in port %d (code %d)" % (port, retval)) |
142,6 → 151,11
#TODO: temporally put back to fifo |
self.info("flush_fifo_out packet went back to fifo.") |
data["fifo_out"].append(packet) |
else: |
if data["fifo_out_event"].val: |
self.debug("flush_fifo_out CATCH fifo_out empty and trigger ON! Cleaning trigger") |
data["fifo_out_event"].next = False |
self.idlesignal.next = True |
yield self.list_fifo_out_events |
self.debug("flush_fifo_out event hit. list %s" % repr(self.list_fifo_out_events)) |
|
152,6 → 166,7
# routing update: check all fifos |
for port, data in self.ports_info.iteritems(): |
while len(data["fifo_in"]) > 0: |
self.idlesignal.next = False |
if not data["fifo_in_event"].val: |
self.debug("routing_loop CATCH fifo not empty and NO trigger! fifo has %s" % repr(data["fifo_in"])) |
self.info("routing_loop fifo_in event in port %d" % port) |
166,15 → 181,24
self.debug("routing_loop port %d to port %s (dest %d)" % (port, nextaddr, destaddr)) |
# DELAY model: time spent to make a route decisition |
yield myhdl.delay(self.delay_route) |
self.idlesignal.next = False |
self.ports_info[nextaddr]["fifo_out"].append(packet) |
# fifo trigger |
if self.ports_info[nextaddr]["fifo_out_event"]: |
self.debug("routing_loop CATCH possible miss event because port %d fifo_out_event=True", self.myaddress) |
self.ports_info[nextaddr]["fifo_out_event"].next = True |
# assuming empty fifo_in |
if data["fifo_in_event"].val: |
self.debug("routing_loop CATCH fifo_in empty and trigger ON! Cleaning trigger") |
data["fifo_in_event"].next = False |
self.idlesignal.next = True |
self.debug("routing_loop idle. fifo_in_events list %s" % repr(self.list_fifo_in_events)) |
if not any(self.list_fifo_in_events): |
yield self.list_fifo_in_events |
else: |
self.debug("routing_loop pending fifo_in_events list %s" % repr(self.list_fifo_in_events)) |
self.debug("routing_loop fifo_in event hit. list %s" % repr(self.list_fifo_in_events)) |
|
yield self.list_fifo_in_events |
self.debug("routing_loop event hit. list %s" % repr(self.list_fifo_in_events)) |
|
# list of all generators |
self.generators.extend([flush_fifo_out, routing_loop]) |
self.debugstate() |
195,7 → 219,7
therouter = self.graph_ref.get_router_by_address(dest) |
if therouter == False: |
self.error("-> send: dest %s not found" % repr(dest) ) |
return noc_tlm_errcodes.tlm_badcall_send |
return noc_tbm_errcodes.tbm_badcall_send |
# extract channel ref from ports_info |
thedest = self.ports_info[therouter.address]["channel"] |
elif isinstance(dest, router): |
206,10 → 230,10
thedest = dest |
else: |
self.error("-> send: what is dest '%s'?" % repr(dest) ) |
return noc_tlm_errcodes.tlm_badcall_send |
return noc_tbm_errcodes.tbm_badcall_send |
|
# call recv on the dest channel object |
retval = thedest.tlm.recv(self.router_ref, thedest, packet, addattrs) |
retval = thedest.tbm.recv(self.router_ref, thedest, packet, addattrs) |
|
# TODO: something to do with the retval? |
self.debug("-> send returns code '%s'" % repr(retval)) |
242,10 → 266,10
thesrc = theend.router_ref.address |
else: |
self.error("-> recv: what is endpoint '%s' in channel '%s'?" % (repr(theend), repr(src)) ) |
return noc_tlm_errcodes.tlm_badcall_recv |
return noc_tbm_errcodes.tbm_badcall_recv |
else: |
self.error("-> recv: what is src '%s'?" % repr(src) ) |
return noc_tlm_errcodes.tlm_badcall_recv |
return noc_tbm_errcodes.tbm_badcall_recv |
|
# thesrc becomes the port number |
# check if there is enough space on the FIFO |
252,13 → 276,108
if len(self.ports_info[thesrc]["fifo_in"]) == self.fifo_len: |
# full FIFO |
self.error("-> recv: full fifo. Try later.") |
return noc_tlm_errcodes.full_fifo |
self.debug("-> recv: port %s fifo_in contents: %s" % (thesrc, repr(self.ports_info[thesrc]["fifo_in"]))) |
return noc_tbm_errcodes.full_fifo |
# get into fifo |
self.ports_info[thesrc]["fifo_in"].append(packet) |
# trigger a new routing event |
if self.ports_info[thesrc]["fifo_in_event"].val: |
self.debug("-> recv: CATCH possible miss event because in port %d fifo_in_event=True", thesrc) |
self.debug("-> recv: CATCH fifo_in_event list %s" % repr(self.list_fifo_in_events)) |
self.ports_info[thesrc]["fifo_in_event"].next = True |
|
self.debug("-> recv returns 'noc_tlm_errcodes.no_error'") |
return noc_tlm_errcodes.no_error |
self.debug("-> recv returns 'noc_tbm_errcodes.no_error'") |
return noc_tbm_errcodes.no_error |
|
# --------------------------- |
# Router code generation model |
|
class basic_router_codegen(noc_codegen_ext): |
""" |
Code generation extension for Router objects. |
""" |
|
def __init__(self, codegen_ref): |
noc_codegen_ext.__init__(self, codegen_ref) |
self.router_ref = codegen_ref.nocobject_ref |
if not isinstance(self.router_ref, router): |
raise TypeError("Argument must be a 'noc_codegen_base' instance defined for a router object.") |
|
# router model: This basic router has some parameters put in the |
# generics list, and a fixed number of NoC ports: 4. |
|
# assumptions: use 4 ports 'dualwb_intercon' plus 1 port 'slavewb_intercon' |
|
codegen_ref.modulename = "basic_4P_router" |
|
# 1. convert basic attributes to generics |
codegen_ref.add_generic("name", self.router_ref.name, "Router Name") |
codegen_ref.add_generic("address", self.router_ref.address, "Router Address") |
# assuming rectangular layout |
codegen_ref.add_generic("coord_x", self.router_ref.coord_x, "Router X-axis Coord") |
codegen_ref.add_generic("coord_y", self.router_ref.coord_y, "Router Y-axis Coord") |
|
# 2. Calculate which of 4 ports is used by who |
portinfo = {"N": None, "E": None, "S": None, "W": None} |
for pname, pvalues in self.router_ref.ports.iteritems(): |
if pname != self.router_ref.address: |
# check correct intercon |
if not isinstance(pvalues["channel"].ports[self.router_ref.address]["intercon"], dualwb_intercon): |
raise UserWarning("Port '%d' on router '%s' does not use intercon 'dualwb_intercon'." % (pname, self.router_ref.name)) |
# calculate which port |
dx = self.router_ref.coord_x - pvalues["peer"].coord_x |
dy = self.router_ref.coord_y - pvalues["peer"].coord_y |
if dx == 0: |
if dy > 0: |
portinfo["S"] = pname |
else: |
portinfo["N"] = pname |
if dy == 0: |
if dx > 0: |
portinfo["W"] = pname |
else: |
portinfo["E"] = pname |
else: |
# check correct intercon |
if not isinstance(pvalues["channel"].ports[self.router_ref.address]["intercon"], slavewb_intercon): |
raise UserWarning("Port 'Local' on router '%s' does not use intercon 'slavewb_intercon'." % self.router_ref.name) |
|
icon_ref = dualwb_intercon() |
# 3. Add ports and info through generics |
for pname, pvalue in portinfo.iteritems(): |
# add new port |
pstr = "Port%s" % pname |
codegen_ref.add_port(pstr, None, "Port %s" % pname, type=icon_ref.intercon_type, nocport=pvalue) |
for signame, sigval in icon_ref.signals.iteritems(): |
stmp = get_new_signal( |
name=signame, |
direction=sigval["direction"], |
default_value=intbv(0)[sigval["width"]:], |
description=sigval["description"]) |
codegen_ref.add_port(pstr, stmp) |
# determine if the port is used |
if pvalue is None: |
penable = 0 |
paddr = 0 |
else: |
penable = 1 |
paddr = pvalue |
codegen_ref.add_generic("Use_Port%s" % pname, penable, "Is Port%s being used?" % pname) |
codegen_ref.add_generic("Dest_Port%s" % pname, paddr, "Dest address in Port%s" % pname) |
# 4. Local port |
icon_ref = slavewb_intercon() |
codegen_ref.add_port("PortLocal", None, "Port Local", type=icon_ref.intercon_type) |
for signame, sigval in icon_ref.signals.iteritems(): |
stmp = get_new_signal( |
name=signame, |
direction=sigval["direction"], |
default_value=intbv(0)[sigval["width"]:], |
description=sigval["description"]) |
codegen_ref.add_port("PortLocal", stmp) |
|
# 5. Calculate a hash with generics and ports info. This hash will help |
# codegen to establish equivalent router implementations. |
codegen_ref.model_hash() |
|
# 6. Implementation comment |
codegen_ref.implementation += "-- Add here implementation code for Router %s" % self.router_ref.name |
/basicmodels/intercon_model.py
0,0 → 1,231
#!/usr/bin/env python |
# -*- coding: utf-8 -*- |
|
# |
# Intercon models |
# * Dual P2P Wishbone model |
# * Single Bus Wishbone model |
# |
# Author: Oscar Diaz |
# Version: 0.1 |
# Date: 11-03-2011 |
|
# |
# This code is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This code is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the |
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
# Boston, MA 02111-1307 USA |
# |
|
# |
# Changelog: |
# |
# 11-03-2011 : (OD) initial release |
# |
|
""" |
Basic Wishbone intercon models |
""" |
|
from nocmodel import * |
|
class dualwb_intercon(intercon): |
""" |
Wishbone dual P2P intercon model |
|
This intercon defines two bidirectional Wishbone P2P ports. |
""" |
intercon_type = "dualwb" |
complement = None |
sideinfo = "" |
|
def __init__(self, **kwargs): |
intercon.__init__(self, **kwargs) |
|
# attributes: data_width (default 32 bits) |
if not hasattr(self, "data_width"): |
setattr(self, "data_width", 32) |
# addr_width (default 0, don't use address signal) |
if not hasattr(self, "addr_width"): |
setattr(self, "addr_width", 0) |
|
self.intercon_type = "dualwb" |
# self.complement = None |
# self.sideinfo = "" |
|
# build the intercon structure |
# Common signals |
#self.signals["rst_i"] |
#self.signals["clk_i"] |
# Master part |
# discard m_dat_i, m_we_o, m_sel_o, m_rty_i, m_lock_o |
# optional m_adr_o |
self.signals["m_dat_o"] = {"width": self.data_width, "direction": "out", "signal_obj": None, "description": "Master data output"} |
if self.addr_width > 0: |
self.signals["m_adr_o"] = {"width": self.addr_width, "direction": "out", "signal_obj": None, "description": "Master address output"} |
self.signals["m_stb_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Master strobe"} |
self.signals["m_cyc_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Master cycle"} |
self.signals["m_lflit_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Master last flit flag"} |
self.signals["m_ack_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Master acknowledge"} |
self.signals["m_stall_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Master stall"} |
self.signals["m_err_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Master error"} |
# Slave part |
# discard s_adr_i, s_dat_o, s_we_i, s_sel_i, s_rty_o, s_lock_i |
self.signals["s_dat_i"] = {"width": self.data_width, "direction": "in", "signal_obj": None, "description": "Slave data input"} |
if self.addr_width > 0: |
self.signals["s_adr_i"] = {"width": self.addr_width, "direction": "in", "signal_obj": None, "description": "Slave address input"} |
self.signals["s_stb_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Slave strobe"} |
self.signals["s_cyc_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Slave cycle"} |
self.signals["s_lflit_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Slave last flit flag"} |
self.signals["s_ack_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Slave acknowledge"} |
self.signals["s_stall_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Slave stall"} |
self.signals["s_err_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Slave error"} |
|
def get_complement_signal(self, signalname): |
""" |
Get the signal name that should be connected to this signal when |
connecting two intercon. |
|
Arguments: |
* signalname: signal name of this intercon |
|
Return: a string with the name of a signal from a complementary intercon. |
""" |
if signalname not in self.signals: |
raise KeyError("Signal '%s' not found" % signalname) |
mchange = {"m": "s", "s": "m"} |
dchange = {"i": "o", "o": "i"} |
return mchange[signalname[0]] + signalname[1:-1] + dchange[signalname[-1]] |
|
class slavewb_intercon(): |
pass |
|
class masterwb_intercon(intercon): |
""" |
Wishbone single bus master intercon model |
|
This intercon defines a simple master Wishbone bus. |
""" |
intercon_type = "masterwb" |
complement = slavewb_intercon |
sideinfo = "master" |
|
def __init__(self, **kwargs): |
intercon.__init__(self, **kwargs) |
|
# attributes: data_width (default 32 bits) |
if not hasattr(self, "data_width"): |
setattr(self, "data_width", 32) |
# addr_width (default 16 bits) |
if not hasattr(self, "addr_width"): |
setattr(self, "addr_width", 16) |
|
self.intercon_type = "masterwb" |
# self.complement = slavewb_intercon |
# self.sideinfo = "master" |
|
# build the intercon structure |
self.signals["rst_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Reset input"} |
self.signals["clk_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Clock input"} |
|
# discard m_sel_o, m_rty_i, m_lock_o |
self.signals["m_adr_o"] = {"width": self.addr_width, "direction": "out", "signal_obj": None, "description": "Master address output"} |
self.signals["m_dat_i"] = {"width": self.data_width, "direction": "in", "signal_obj": None, "description": "Master data input"} |
self.signals["m_dat_o"] = {"width": self.data_width, "direction": "out", "signal_obj": None, "description": "Master data output"} |
self.signals["m_we_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Master write enable"} |
self.signals["m_stb_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Master strobe"} |
self.signals["m_cyc_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Master cycle"} |
self.signals["m_ack_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Master acknowledge"} |
self.signals["m_stall_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Master stall"} |
self.signals["m_err_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Master error"} |
self.signals["m_irq_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Master IRQ"} |
|
def get_complement_signal(self, signalname): |
""" |
Get the signal name that should be connected to this signal when |
connecting two intercon. |
|
Arguments: |
* signalname: signal name of this intercon |
|
Return: a string with the name of a signal from a complementary intercon. |
""" |
if signalname not in self.signals: |
raise KeyError("Signal '%s' not found" % signalname) |
mchange = {"m": "s", "s": "m"} |
dchange = {"i": "o", "o": "i"} |
if signalname == "rst_i" or signalname == "clk_i": |
# special signals. Return None |
return None |
else: |
return mchange[signalname[0]] + signalname[1:-1] + dchange[signalname[-1]] |
|
class slavewb_intercon(intercon): |
""" |
Wishbone single bus slave intercon model |
|
This intercon defines a simple slave Wishbone bus. |
""" |
intercon_type = "slavewb" |
complement = masterwb_intercon |
sideinfo = "slave" |
|
def __init__(self, **kwargs): |
intercon.__init__(self, **kwargs) |
|
# attributes: data_width (default 32 bits) |
if not hasattr(self, "data_width"): |
setattr(self, "data_width", 32) |
# addr_width (default 16 bits) |
if not hasattr(self, "addr_width"): |
setattr(self, "addr_width", 16) |
|
self.intercon_type = "slavewb" |
# self.complement = masterwb_intercon |
# self.sideinfo = "slave" |
|
# build the intercon structure |
self.signals["rst_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Reset input"} |
self.signals["clk_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Clock input"} |
|
# discard s_sel_o, s_rty_i, s_lock_o |
self.signals["s_adr_i"] = {"width": self.addr_width, "direction": "in", "signal_obj": None, "description": "Slave address output"} |
self.signals["s_dat_o"] = {"width": self.data_width, "direction": "out", "signal_obj": None, "description": "Slave data input"} |
self.signals["s_dat_i"] = {"width": self.data_width, "direction": "in", "signal_obj": None, "description": "Slave data output"} |
self.signals["s_we_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Slave write enable"} |
self.signals["s_stb_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Slave strobe"} |
self.signals["s_cyc_i"] = {"width": 1, "direction": "in", "signal_obj": None, "description": "Slave cycle"} |
self.signals["s_ack_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Slave acknowledge"} |
self.signals["s_stall_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Slave stall"} |
self.signals["s_err_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Slave error"} |
self.signals["s_irq_o"] = {"width": 1, "direction": "out", "signal_obj": None, "description": "Slave IRQ"} |
|
def get_complement_signal(self, signalname): |
""" |
Get the signal name that should be connected to this signal when |
connecting two intercon. |
|
Arguments: |
* signalname: signal name of this intercon |
|
Return: a string with the name of a signal from a complementary intercon. |
""" |
if signalname not in self.signals: |
raise KeyError("Signal '%s' not found" % signalname) |
mchange = {"m": "s", "s": "m"} |
dchange = {"i": "o", "o": "i"} |
if signalname == "rst_i" or signalname == "clk_i": |
# special signals. Return None |
return None |
else: |
return mchange[signalname[0]] + signalname[1:-1] + dchange[signalname[-1]] |
/basicmodels/basic_ipcore.py
3,11 → 3,12
|
# |
# Basic IPcore model |
# * TLM model |
# * TBM model |
# * Code generation model |
# |
# Author: Oscar Diaz |
# Version: 0.1 |
# Date: 03-03-2011 |
# Version: 0.2 |
# Date: 14-03-2011 |
|
# |
# This code is free software; you can redistribute it and/or |
30,20 → 31,24
# Changelog: |
# |
# 03-03-2011 : (OD) initial release |
# 14-03-2011 : (OD) adding code generation model |
# |
|
""" |
Basic ipcore TLM model |
* Basic ipcore TBM model |
* Ipcore code generation model |
""" |
|
from nocmodel.noc_tlm_base import * |
from nocmodel.noc_tbm_base import * |
from nocmodel.noc_codegen_base import * |
from intercon_model import * |
|
# --------------------------- |
# Basic IPCore TLM model |
# Basic IPCore TBM model |
|
class basic_ipcore_tlm(noc_tlm_base): |
class basic_ipcore_tbm(noc_tbm_base): |
""" |
TLM model of a NoC ipcore. Its based on sending and receiving packets |
TBM model of a NoC ipcore. Its based on sending and receiving packets |
to a custom-based MyHDL generators. This class does not define any |
functionality. |
|
55,7 → 60,7
* See code comments to better understanding. |
""" |
def __init__(self, ipcore_ref): |
noc_tlm_base.__init__(self) |
noc_tbm_base.__init__(self) |
if isinstance(ipcore_ref, ipcore): |
self.ipcore_ref = ipcore_ref |
self.graph_ref = ipcore_ref.graph_ref |
67,6 → 72,8
|
self.debug("constructor") |
# generic parameters |
self.retrytimes = 3 |
self.retrydelay = 2 |
|
# one-port support: get a reference to the related channel |
self.localch = self.ipcore_ref.channel_ref |
87,7 → 94,12
def outgoing_process(): |
while True: |
yield self.outgoing_packet |
retval = self.send(self.ipcore_ref, self.localch, self.outgoing_packet.val) |
# multiple tries |
for i in range(self.retrytimes): |
retval = self.send(self.ipcore_ref, self.localch, self.outgoing_packet.val) |
if retval == noc_tbm_errcodes.no_error: |
break; |
yield myhdl.delay(self.retrydelay) |
|
self.generators = [outgoing_process] |
self.debugstate() |
102,7 → 114,7
|
Notes: |
* This method requires that genfunction has the following prototype: |
* my_function(din, dout, tlm_ref, <other_arguments>) |
* my_function(din, dout, tbm_ref, <other_arguments>) |
* din is a MyHDL Signal of type packet, and is the input signal |
to the ipcore. Use this signal to react to input events and |
receive input packets. |
109,12 → 121,12
* dout is a MyHDL Signal of type packet, and is the output |
signal to the ipcore. Use this signal to send out packets to |
local channel (and then insert into the NoC). |
* tlm_ref is a reference to this object. Normal use is to access |
logging methods (e.g. tlm_ref.info("message") ). |
* tbm_ref is a reference to an object with logging methods. |
(e.g. tbm_ref.info("message") ). |
* <other_arguments> may be defined, this method use kwargs |
argument to pass them. |
""" |
makegen = genfunction(din=self.incoming_packet, dout=self.outgoing_packet, tlm_ref=self, **kwargs) |
makegen = genfunction(din=self.incoming_packet, dout=self.outgoing_packet, tbm_ref=self, **kwargs) |
self.debug("register_generator( %s ) generator is %s args %s" % (repr(genfunction), repr(makegen), repr(kwargs))) |
self.generators.append(makegen) |
|
131,7 → 143,7
self.debug("-> send( %s , %s , %s , %s )" % (repr(src), repr(dest), repr(packet), repr(addattrs))) |
|
# call recv on the local channel object |
retval = self.localch.tlm.recv(self.ipcore_ref, self.localch, packet, addattrs) |
retval = self.localch.tbm.recv(self.ipcore_ref, self.localch, packet, addattrs) |
|
# something to do with the retval? Only report it. |
self.debug("-> send returns code '%s'" % repr(retval)) |
150,5 → 162,63
# update signal |
self.incoming_packet.next = packet |
|
self.debug("-> recv returns 'noc_tlm_errcodes.no_error'") |
return noc_tlm_errcodes.no_error |
self.debug("-> recv returns 'noc_tbm_errcodes.no_error'") |
return noc_tbm_errcodes.no_error |
|
# --------------------------- |
# Ipcore code generation model |
|
class basic_ipcore_codegen(noc_codegen_ext): |
""" |
Code generation extension for Ipcore objects. |
""" |
|
def __init__(self, codegen_ref): |
noc_codegen_ext.__init__(self, codegen_ref) |
#self.codegen_ref = codegen_ref |
self.ipcore_ref = codegen_ref.nocobject_ref |
if not isinstance(self.ipcore_ref, ipcore): |
raise TypeError("Argument must be a 'noc_codegen_base' instance defined for a ipcore object.") |
|
# ipcore model: This basic ipcore has some parameters put in the |
# generics list, and only one port of type masterwb. |
|
codegen_ref.modulename = "basic_ipcore" |
|
# 1. convert some attributes to generics |
codegen_ref.add_generic("name", self.ipcore_ref.name, "Ipcore Name") |
codegen_ref.add_generic("address", self.ipcore_ref.router_ref.address, "Ipcore router Address") |
|
# 2. check intercon on ipcore port |
|
ch = self.ipcore_ref.channel_ref |
icon = ch.ports[None]["intercon"] |
if not isinstance(icon, masterwb_intercon): |
raise UserWarning("Port Local on ipcore '%s' does not use intercon 'masterwb_intercon'." % self.ipcore_ref.name) |
|
# 2. convert ipcore port to codegen port |
codegen_ref.add_port("PortLocal", None, "Port Local", type=icon.intercon_type) |
|
for signame, sigval in icon.signals.iteritems(): |
stmp = get_new_signal( |
name=signame, |
direction=sigval["direction"], |
default_value=intbv(0)[sigval["width"]:], |
description=sigval["description"]) |
codegen_ref.add_port("PortLocal", stmp) |
|
# 3. Calculate a Hash with generics and ports info. |
# WARNING: You must recalculate the hash when you change the model! |
codegen_ref.model_hash() |
|
# 4. Implementation comment |
codegen_ref.implementation += "-- Add here implementation code for Ipcore %s" % self.ipcore_ref.name |
|
def add_external_signal(self, name, direction, value, description="", **kwargs): |
""" |
Wrapper to noc_codegen_base.add_external_signal method, to support hash |
updating. |
""" |
retval = self.codegen_ref.add_external_signal(name, direction, value, description, **kwargs) |
self.codegen_ref.model_hash() |
return retval |
/basicmodels/basic_noc_codegen.py
0,0 → 1,229
#!/usr/bin/env python |
# -*- coding: utf-8 -*- |
|
# |
# Basic NoC Code generation model |
# * VHDL code support |
# |
# Author: Oscar Diaz |
# Version: 0.2 |
# Date: 14-03-2011 |
|
# |
# This code is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This code is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the |
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
# Boston, MA 02111-1307 USA |
# |
|
# |
# Changelog: |
# |
# 14-03-2011 : (OD) initial release |
# |
|
""" |
* Basic NoC VHDL code generation model |
""" |
|
from nocmodel import * |
from nocmodel.noc_codegen_base import * |
from nocmodel.noc_codegen_vhdl import * |
from intercon_model import * |
|
from myhdl import intbv |
|
class basic_noc_arch_vhdl(noc_codegen_ext): |
""" |
Code generation extension for NoC objects in VHDL. |
|
This extension object generates the architecture needed to connect all |
objects into a top VHDL file. |
""" |
def __init__(self, codegen_ref): |
noc_codegen_ext.__init__(self, codegen_ref) |
self.noc_ref = codegen_ref.nocobject_ref |
if not isinstance(self.noc_ref, noc): |
raise TypeError("Argument must be a 'noc_codegen_base' instance defined for a NoC object.") |
|
codegen_ref.modulename = self.noc_ref.name |
|
# NoC model: must generate all needed components and join them together |
# in this top module. |
|
# 1. convert some attributes to generics |
codegen_ref.add_generic("name", self.noc_ref.name, "NoC Name") |
|
# 2. define a basic port signals: |
codegen_ref.add_external_signal("rst_i", "in", intbv(0)[1:], "System reset in") |
codegen_ref.add_external_signal("clk_i", "in", intbv(0)[1:], "System clock in") |
|
# 2. keep a list of needed components |
hash_list = {} |
|
# 2. search for defined external_ports in codegen objects |
for obj in self.noc_ref.all_list(True): |
for sig in obj.codegen.external_signals: |
# make a copy of the signal dict, adding a reference to the |
# nocobject which belongs |
csig = sig.copy() |
csig["back_ref"] = obj |
codegen_ref.external_signals.append(csig) |
|
# 3. Build implementation string |
codegen_ref.implementation += "-- Implementation for '%s' name '%s' generated by '%s'\n" % (repr(self.noc_ref), self.noc_ref.name, repr(self)) |
# 3.1 add components |
# TODO: check the components name |
for obj in self.noc_ref.all_list(): |
# add only for routers and ipcores (WARNING) |
if isinstance(obj, (router, ipcore)): |
if obj.codegen.interface_hash == "": |
# update hash |
obj.codegen.model_hash() |
# check if the component is already declared |
if hash(obj.codegen.interface_hash) in hash_list: |
# already declared component |
hash_list[hash(obj.codegen.interface_hash)]["instance_list"].append(obj) |
else: |
# new component |
codegen_ref.implementation += "\n-- Component for '%s' name '%s'\n" % (repr(obj), obj.name) |
codegen_ref.implementation += obj.codegen.generate_component() |
hash_list[hash(obj.codegen.interface_hash)] = { |
"str_hash": obj.codegen.interface_hash, |
"component_name": obj.codegen.modulename, |
"instance_list": [obj] |
} |
|
# 3.2 intermediate signals |
codegen_ref.implementation += "\n-- Intermediate signals\n\n" |
# signals here has the format: "sig_<obj.name>_<signal_name>" |
for obj in self.noc_ref.all_list(): |
# add only for routers and ipcores (WARNING) |
if isinstance(obj, (router, ipcore)): |
for s in obj.codegen.generate_port_declaration(None, True): |
if s[0:2] != "--": |
# note that port declaration will put "in" or "out". Filter it |
sf = s.replace(": in", ":").replace(": out", ":") |
codegen_ref.implementation += "signal sig_%s_%s;\n" % (obj.name, sf) |
else: |
codegen_ref.implementation += "-- Object %s : %s\n" % (obj.name, s[2:]) |
|
codegen_ref.implementation += "begin\n" |
codegen_ref.implementation += codegen_ref.add_tab("-- component instantiation\n\n") |
|
# 3.3 instances and port mapping |
# hash_list has the declared components and its related objects |
for hcomp in hash_list.itervalues(): |
for obj in hcomp["instance_list"]: |
codegen_ref.implementation += codegen_ref.add_tab("-- Instance for '%s' name '%s'\n" % (repr(obj), obj.name)) |
codegen_ref.implementation += codegen_ref.add_tab("i_%s : %s" % (obj.name, hcomp["component_name"])) |
# generic map |
if len(obj.codegen.generics) > 0: |
codegen_ref.implementation += " generic map (\n" |
strl = [] |
for g in obj.codegen.generics: |
strl.append("%s => %s" % (g["name"], convert_value(g["default_value"], g["type"]))) |
codegen_ref.implementation += ",\n".join(codegen_ref.add_tab(strl, 2)) |
codegen_ref.implementation += codegen_ref.add_tab("\n)") |
# port map |
if (len(obj.codegen.ports) + len(obj.codegen.external_signals)) > 0: |
codegen_ref.implementation += " port map (\n" |
strl = [] |
for p in obj.codegen.ports: |
# search for each signal in a port |
for sig in p["signal_list"]: |
# format: <portname>_<signame> => sig_<obj.name>_<portname>_<signal_name> |
strl.append("%s_%s => sig_%s_%s_%s" % (p["name"], sig["name"], obj.name, p["name"], sig["name"])) |
# and then in external_signals |
for sig in obj.codegen.external_signals: |
# format: <signame> => <obj.name>_<signal_name> |
strl.append("%s => %s_%s" % (sig["name"], obj.name, sig["name"])) |
codegen_ref.implementation += ",\n".join(codegen_ref.add_tab(strl, 2)) |
codegen_ref.implementation += codegen_ref.add_tab("\n)") |
codegen_ref.implementation += ";\n\n" |
|
# 3.4 signal connections |
# connect routers first |
for obj in self.noc_ref.router_list(): |
# go through all ports and check its use |
codegen_ref.implementation += codegen_ref.add_tab("-- Signal mapping for '%s' name '%s'\n" % (repr(obj), obj.name)) |
for p in obj.codegen.ports: |
if p["type"] == "dualwb": |
if p["nocport"] is None: |
codegen_ref.implementation += codegen_ref.add_tab("-- port '%s' unused\n" % p["name"]) |
# Unused port. Go through all signals and put input |
# values tied to 0 |
for sig in p["signal_list"]: |
if sig["direction"] == "in": |
# format: sig_<obj.name>_<portname>_<signal_name> |
codegen_ref.implementation += codegen_ref.add_tab("sig_%s_%s_%s <= %s;\n" % (obj.name, p["name"], sig["name"], convert_value(sig["default_value"], sig["type"]))) |
else: |
# connected with a port. Assign only input signals. |
peerobj = obj.ports[p["nocport"]]["peer"] |
# search for peer object codegen ports |
peerpname = None |
for peerp in peerobj.codegen.ports: |
if peerp["nocport"] is not None: |
findobj = peerobj.ports[peerp["nocport"]]["peer"] |
if findobj == obj: |
peerpname = peerp["name"] |
break |
if peerpname is None: |
raise ValueError("Connected object '%s'->'%s': port not found" % (obj.name, peerobj.name)) |
codegen_ref.implementation += codegen_ref.add_tab("-- port '%s' connected to object '%s' port '%s'\n" % (p["name"], peerobj.name, peerpname)) |
for sig in p["signal_list"]: |
if sig["direction"] == "in": |
peersig = obj.ports[p["nocport"]]["channel"].ports[obj.address]["intercon"].get_complement_signal(sig["name"]) |
if peersig is not None: |
# format: sig_<obj.name>_<portname>_<signal_name> |
codegen_ref.implementation += codegen_ref.add_tab("sig_%s_%s_%s <= sig_%s_%s_%s;\n" % (obj.name, p["name"], sig["name"], peerobj.name, peerpname, peersig)) |
else: |
codegen_ref.implementation += codegen_ref.add_tab("-- Special signal sig_%s_%s_%s not assigned yet.\n" % (obj.name, p["name"], sig["name"])) |
|
# then ipcores |
for obj in self.noc_ref.ipcore_list(): |
codegen_ref.implementation += codegen_ref.add_tab("-- Signal mapping for '%s' name '%s'\n" % (repr(obj), obj.name)) |
# connect signals from and to ipcore |
# get local and remote intercon |
obj_icon = obj.ports[obj.get_address()]["channel"].ports[None]["intercon"] |
dest_icon = obj.ports[obj.get_address()]["channel"].ports[obj.get_address()]["intercon"] |
obj_port = obj.codegen.ports[0] |
dest_port = None |
for p in obj.router_ref.codegen.ports: |
if p["type"] == dest_icon.intercon_type: |
dest_port = p |
if dest_port is None: |
raise ValueError("Router object '%s': local port not found" % obj.router_ref.name) |
codegen_ref.implementation += codegen_ref.add_tab("-- port '%s' connected to object '%s' port '%s'\n" % (p["name"], obj.router_ref.name, dest_port["name"])) |
# first ipcore <= router |
for sig in obj_port["signal_list"]: |
if sig["direction"] == "in": |
peersig = obj_icon.get_complement_signal(sig["name"]) |
if peersig is not None: |
# format: sig_<obj.name>_<portname>_<signal_name> |
codegen_ref.implementation += codegen_ref.add_tab("sig_%s_%s_%s <= sig_%s_%s_%s;\n" % (obj.name, obj_port["name"], sig["name"], obj.router_ref.name, dest_port["name"], peersig)) |
else: |
# special port (TODO!) |
codegen_ref.implementation += codegen_ref.add_tab("-- Special signal sig_%s_%s_%s not assigned yet.\n" % (obj.name, obj_port["name"], sig["name"])) |
# then router <= ipcore |
for sig in dest_port["signal_list"]: |
if sig["direction"] == "in": |
peersig = dest_icon.get_complement_signal(sig["name"]) |
if peersig is not None: |
# format: sig_<obj.name>_<portname>_<signal_name> |
codegen_ref.implementation += codegen_ref.add_tab("sig_%s_%s_%s <= sig_%s_%s_%s;\n" % (obj.router_ref.name, dest_port["name"], sig["name"], obj.name, obj_port["name"], peersig)) |
else: |
codegen_ref.implementation += codegen_ref.add_tab("-- Special signal sig_%s_%s_%s not assigned yet.\n" % (obj.router_ref.name, dest_port["name"], sig["name"])) |
|
codegen_ref.implementation += codegen_ref.add_tab("-- Implementation code for Noc --") |
|
/__init__.py
5,8 → 5,8
# NoCmodel package |
# |
# Author: Oscar Diaz |
# Version: 0.1 |
# Date: 03-03-2011 |
# Version: 0.2 |
# Date: 05-07-2012 |
|
# |
# This code is free software; you can redistribute it and/or |
29,6 → 29,7
# Changelog: |
# |
# 03-03-2011 : (OD) initial release |
# 14-03-2011 : (OD) support for code generation |
# |
|
""" |
40,8 → 41,11
|
* Module noc_base: NoCmodel Base Objects |
* Module noc_guilib: NoCmodel Graphic utilities |
* Module noc_tlm_base: NoCmodel TLM simulation support |
* Module noc_tlm_utils: helper functions for TLM simulation |
* Module noc_tbm_base: NoCmodel TBM simulation support |
* Module noc_tbm_utils: helper functions for TBM simulation |
* Module noc_codegen_base: NoCmodel base for code generation support |
* Module noc_codegen_vhdl: VHDL support for code generation |
* Module noc_helpers: Utility functions |
* Package basicmodels: basic examples of NoC objects (not imported by default) |
""" |
|
51,7 → 55,11
# provided modules |
from noc_base import * |
from noc_guilib import * |
from noc_tlm_base import * |
from noc_tlm_utils import * |
from noc_tbm_base import * |
from noc_tbm_utils import * |
from noc_rtl_myhdl import * |
from noc_codegen_base import * |
from noc_codegen_vhdl import * |
from noc_helpers import * |
|
__version__ = "0.1" |
__version__ = "0.2" |
/noc_codegen_vhdl.py
0,0 → 1,636
#!/usr/bin/env python |
# -*- coding: utf-8 -*- |
|
# |
# NoC Code generator - VHDL generator |
# |
# Author: Oscar Diaz |
# Version: 0.1 |
# Date: 11-11-2011 |
|
# |
# This code is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This code is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the |
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
# Boston, MA 02111-1307 USA |
# |
|
# |
# Changelog: |
# |
# 11-11-2011 : (OD) initial release |
# |
|
""" |
Code generation support for VHDL |
|
This module defines: |
* Class 'noc_codegen_vhdl' |
* Function 'add_codegen_vhdl_support' |
""" |
|
from nocmodel import * |
from noc_codegen_base import * |
from myhdl import intbv, bin |
|
class noc_codegen_vhdl(noc_codegen_base): |
""" |
VHDL generator class |
|
This class defines methods used to generate VHDL code from any noc object. |
|
Like base class, this object has this base attributes: |
* docheader |
* libraries |
* modulename |
* generics |
* ports |
* implementation |
""" |
def __init__(self, nocobject_ref, **kwargs): |
noc_codegen_base.__init__(self, nocobject_ref, **kwargs) |
|
# note: the noc object must be complete, in order to |
# add code generation support |
#self.nocobject_ref = nocobject_ref |
self.modulename = self.to_valid_str(self.nocobject_ref.name) |
|
# default header and libraries |
self.docheader = "-- Document header" |
self.libraries = "library ieee;\nuse ieee.std_logic_1164.all;\nuse ieee.numeric_std.all;" |
|
# code generation attributes |
self.usetab = " " # 4-space tabs |
self.full_comments = True |
|
# main methods |
def generate_file(self): |
""" |
Generate the entire file that implements this object. |
""" |
# Unless code generation is externally generated. |
if self.external_conversion: |
# use "codemodel" object on nocobject_ref |
if hasattr(self.nocobject_ref, "codemodel"): |
return self.nocobject_ref.codemodel.generate_file() |
else: |
raise AttributeError("External conversion flag is enabled, but there's no 'codemodel' object available in nocobject %s" % self.nocobject_ref) |
else: |
# first try to call build_implementation() method |
self.build_implementation() |
sret = self.docheader + "\n\n" + self.libraries + "\n\n" |
if self.full_comments: |
sret += self.make_comment("Object '%s' name '%s' description '%s'\n" % (repr(self.nocobject_ref), self.nocobject_ref.name, self.nocobject_ref.description)) |
sret += self.generate_entity_section() + "\n\n" |
sret += self.generate_architecture_section() + "\n" |
return sret |
|
def generate_component(self): |
""" |
Generate a component definition for this object. |
""" |
sret = "component %s\n" % self.modulename |
stmp = self.generate_generics_section() |
if stmp != "": |
sret += self.add_tab(stmp) + ";\n" |
stmp = self.generate_ports_section() |
if stmp != "": |
sret += self.add_tab(stmp) + ";\n" |
sret += "end component;\n" |
return sret |
|
def generate_generic_declaration(self, generic=None, with_default=False): |
""" |
Generate a generic declaration for this object. |
|
Arguments: |
* generic : either a name or a list index for a particular generic |
* with_default : True to add the default value |
|
Returns: |
* A string when generic argument is used |
* A list of strings with all generics |
""" |
if generic is None: |
# all generics |
l = [] |
for i in range(len(self.generics)): |
l.append(self.generate_generic_declaration(i, with_default)) |
return l |
else: |
# search for correct index |
if isinstance(generic, int): |
g = self.generics[generic] |
elif isinstance(generic, string): |
g = filter(lambda s: s["name"] == generic, self.generics)[0] |
else: |
raise TypeError("Don't know how to search with '%s'." % repr(generic)) |
sret = "%s : %s" % (self.to_valid_str(g["name"]), g["type"]) |
if with_default: |
sret += ' := %s' % convert_value(g["default_value"], g["type"], g["type_array"]) |
# add string quotes |
# if g["type"] == "string": |
# sret += ' := "%s"' % g["default_value"] |
# else: |
# sret += ' := %s' % g["default_value"] |
return sret |
|
def generate_port_declaration(self, port=None, with_default=False): |
""" |
Generate a port declaration for this object. |
|
Arguments: |
* port : either a name or a list index for a particular port |
* with_default : True to add the default value |
|
Returns: |
* A list of strings with all signals in port when port argument is used |
* A list of strings with all signals in all ports |
""" |
if port is None: |
# all ports |
l = [] |
for i in range(len(self.ports)): |
l.append(self.make_comment("Port '%s' : '%s'" % (self.to_valid_str(self.ports[i]["name"]), self.ports[i]["type"]))) |
pl = self.generate_port_declaration(i, with_default) |
pl.sort() |
l.extend(pl) |
return l |
else: |
# search for correct index |
if isinstance(port, int): |
idx = port |
elif isinstance(port, string): |
p = filter(lambda s: s["name"] == port, self.ports)[0] |
idx = self.ports.index(p) |
else: |
raise TypeError("Don't know how to search with '%s'." % repr(port)) |
return self.generate_signal_declaration(idx, None, with_default) |
|
def generate_signal_declaration(self, inport=None, signal=None, with_default=False): |
""" |
Generate a signal declaration for this object. |
|
Arguments: |
* inport : either a name or a list index for a particular port. None |
means use the external_signals list |
* signal : either a name or a list index for a particular signal |
* with_default : True to add the default value |
|
Returns: |
* A string when signal argument is used |
* A list of strings with all signals |
""" |
if signal is None: |
# all signals |
l = [] |
if inport is None: |
r = range(len(self.external_signals)) |
else: |
# what port? |
if isinstance(inport, int): |
g = self.ports[inport] |
elif isinstance(inport, string): |
g = filter(lambda s: s["name"] == inport, self.ports)[0] |
else: |
raise TypeError("Don't know how to search with '%s'." % repr(inport)) |
r = range(len(g["signal_list"])) |
for i in r: |
l.append(self.generate_signal_declaration(inport, i, with_default)) |
return l |
else: |
# locate either port or external_signals |
if inport is None: |
if isinstance(signal, int): |
sig = self.external_signals[signal] |
elif isinstance(signal, string): |
sig = filter(lambda s: s["name"] == signal, self.external_signals)[0] |
else: |
raise TypeError("Don't know how to search with '%s'." % repr(signal)) |
# put nothing as signal prefix |
sprefix = "" |
else: |
# what port? |
if isinstance(inport, int): |
p = self.ports[inport] |
elif isinstance(inport, string): |
p = filter(lambda s: s["name"] == inport, self.ports)[0] |
else: |
raise TypeError("Don't know how to search with '%s'." % repr(inport)) |
# what signal? |
if isinstance(signal, int): |
sig = p["signal_list"][signal] |
elif isinstance(signal, string): |
sig = filter(lambda s: s["name"] == signal, p["signal_list"])[0] |
else: |
raise TypeError("Don't know how to search with '%s'." % repr(signal)) |
# put port name as signal prefix |
#sprefix = self.to_valid_str(p["name"]) + "_" |
sprefix = "" |
sret = "%s%s : %s %s" % (sprefix, self.to_valid_str(sig["name"]), sig["direction"], sig["type"]) |
if with_default: |
sret += ' := %s' % convert_value(sig["default_value"], sig["type"]) |
# if sig["type"] == "string": |
# sret += ' := "%s"' % sig["default_value"] |
# else: |
# sret += ' := %s' % sig["default_value"] |
return sret |
|
def make_comment(self, data): |
""" |
Convert string data to language comment |
|
Argument: |
* data: string or list of strings to convert |
|
Returns: a new string or list of strings with comments added. |
""" |
if isinstance(data, str): |
return "-- %s%s" % (data[:-1].replace("\n", "\n-- "), data[-1]) |
else: |
# don't put exception catch. It is an error if data is not |
# iterable. |
it = iter(data) |
retval = [] |
for s in data: |
retval.append("-- %s%s" % (s[:-1].replace("\n", "\n-- "), s[-1])) |
return retval |
|
def add_tab(self, data, level=1): |
""" |
Add an indentation level to the string |
|
Argument: |
* data: string or list of strings |
* level: how many indentation levels to add. Default 1 |
|
Returns: string or list of strings with <level> indentation levels. |
""" |
leveltabs = self.usetab*level |
if isinstance(data, str): |
return "%s%s%s" % (leveltabs, data[:-1].replace("\n", "\n%s" % leveltabs), data[-1]) |
else: |
# don't put exception catch. It is an error if data is not |
# iterable. |
it = iter(data) |
retval = [] |
for s in data: |
retval.append("%s%s%s" % (leveltabs, s[:-1].replace("\n", "\n%s" % leveltabs), s[-1])) |
return retval |
|
def to_valid_str(self, str_in): |
""" |
Convert an input string, changing special characters used on |
the HDL language. Useful for set names . |
|
Argument: |
* str_in: string to convert |
|
Returns: the converted string. |
""" |
# list of transformations: |
# * strip spaces |
# * space,":" with "_" |
s = str_in.strip() |
s = s.replace(" ", "_") |
s = s.replace(":", "_") |
return s |
|
# codegen model management |
def add_generic(self, name, value, description="", **kwargs): |
""" |
Add a generic to the model - with VHDL type formatting. |
""" |
g = noc_codegen_base.add_generic(self, name, value, description, **kwargs) |
# Set correct type. String by default |
gadd = {"type": "string"} |
if "type" in kwargs: |
# Optional type must be a string with a custom type name. |
if isinstance(kwargs["type"], str): |
# custom type name. |
gadd.update({"type": kwargs["type"]}) |
#else: |
# default to use string |
else: |
gadd["type"] = self.calculate_type(value) |
## use type in value |
#gadd["type"] = _pytovhdltype[type(value)] |
## defined range |
#if "type_array" in kwargs: |
#if isinstance(value, int): |
## for an integer it means range |
#gadd["type"] += " range %d to %d" % (kwargs["type_array"][0], kwargs["type_array"][1]) |
#elif isinstance(value, intbv): |
## for a intbv it means bit boundaries |
#gadd["type"] += "_vector(%d to %d)" % (kwargs["type_array"][0], kwargs["type_array"][1]) |
##else: |
## ignore array range |
#else: |
## special case |
#if isinstance(value, intbv): |
## put vector only if necessary |
#if value._nrbits > 1: |
## extract vector information |
#gadd["type_array"] = [0, value._nrbits - 1] |
#gadd["type"] += "_vector(%d to %d)" % (0, value._nrbits - 1) |
|
g.update(gadd) |
return g |
|
def add_port(self, name, signal_desc=None, description="", **kwargs): |
""" |
Add a port to the model - with VHDL type formatting. |
""" |
p = noc_codegen_base.add_port(self, name, signal_desc, description, **kwargs) |
if signal_desc is None: |
# nothing else to do |
return p |
# set correct type for the particular signal_desc |
sig = filter(lambda x: x["name"] == signal_desc["name"], p["signal_list"]) |
if len(sig) == 0: |
# strange error |
raise ValueError("Strange error: recently added signal '%s' to port '%s', but signal cannot be found." % (signal_desc["name"], p["name"])) |
else: |
sig = sig[0] |
# type inferring: only apply if signal_desc has empty type |
if sig["type"] != "": |
return p |
|
# integer by default |
sadd = {"type": "integer"} |
if "type" in kwargs: |
# Optional type must be a string with a custom type name. |
if isinstance(kwargs["type"], str): |
# custom type name. |
sadd["type"] = kwargs["type"] |
# also add if it has default_value |
if "default_value" in kwargs: |
sadd["default_value"] = kwargs["default_value"] |
#else: |
# default to use integer |
else: |
sadd["type"] = self.calculate_type(sig["default_value"]) |
## use type in value |
#sadd["type"] = _pytovhdltype[type(sig["default_value"])] |
## defined range |
#if "type_array" in kwargs: |
#if isinstance(sig["default_value"], int): |
#sadd["type"] += " range(%d to %d)" % (kwargs["type_array"][0], kwargs["type_array"][1]) |
#elif isinstance(sig["default_value"], intbv): |
#sadd["type"] += "_vector(%d downto %d)" % (kwargs["type_array"][1], kwargs["type_array"][0]) |
##else: |
## ignore array range |
#else: |
## special case |
#if isinstance(sig["default_value"], intbv): |
## put vector only if necessary |
#if sig["default_value"]._nrbits > 1: |
## extract vector information |
#sadd["type_array"] = [0, sig["default_value"]._nrbits - 1] |
#sadd["type"] += "_vector(%d downto 0)" % (sig["default_value"]._nrbits - 1) |
sig.update(sadd) |
return p |
|
def add_external_signal(self, name, direction, value, description="", **kwargs): |
""" |
Add a external signal to the model - with VHDL type formatting. |
""" |
sig = noc_codegen_base.add_external_signal(self, name, direction, value, description, **kwargs) |
# Set correct type. integer by default |
sadd = {"type": "integer"} |
if "type" in kwargs: |
# Optional type must be a string with a custom type name. |
if isinstance(kwargs["type"], str): |
# custom type name. |
sadd.update({"type": kwargs["type"]}) |
#else: |
# default to use integer |
else: |
sadd["type"] = self.calculate_type(value) |
## use type in value |
#sadd["type"] = _pytovhdltype[type(value)] |
## defined range |
#if "type_array" in kwargs: |
#if isinstance(value, int): |
#sadd["type"] += " range(%d to %d)" % (kwargs["type_array"][0], kwargs["type_array"][1]) |
#elif isinstance(value, intbv): |
#sadd["type"] += "_vector(%d to %d)" % (kwargs["type_array"][0], kwargs["type_array"][1]) |
##else: |
## ignore array range |
#else: |
## special case |
#if isinstance(value, intbv): |
## extract vector information |
#sadd["type_array"] = [0, sig["default_value"]._nrbits - 1] |
#if None not in sadd["type_array"]: |
#sadd["type"] += "_vector(%d to %d)" % (0, sig["default_value"]._nrbits - 1) |
sig.update(sadd) |
return sig |
|
# particular VHDL methods |
def generate_entity_section(self): |
""" |
Generate entity section |
""" |
sret = "entity %s is\n" % self.modulename |
stmp = self.generate_generics_section() |
if stmp != "": |
sret += self.add_tab(stmp) + ";\n" |
stmp = self.generate_ports_section() |
if stmp != "": |
sret += self.add_tab(stmp) + ";\n" |
sret += "end %s;\n" % self.modulename |
return sret |
|
def generate_architecture_section(self, archname="rtl"): |
""" |
Generate architecture section |
""" |
sret = "architecture %s of %s is\n" % (archname, self.modulename) |
if self.implementation != "": |
sret += self.add_tab(self.implementation) |
sret += "\nend %s;\n" % archname |
return sret |
|
def generate_generics_section(self): |
""" |
Generate generics section used on entity and component |
""" |
sret = "generic (\n" |
l = self.generate_generic_declaration(None, True) |
if len(l) > 0: |
l = self.add_tab(l) |
sret += ";\n".join(l) |
sret += "\n)" |
else: |
# empty generics section |
sret = "" |
return sret |
|
def generate_ports_section(self): |
""" |
Generate ports section used on entity and component |
""" |
# first ports and then external signals |
sret = "port (\n" |
l = self.generate_port_declaration(None, False) |
l.extend(self.generate_signal_declaration(None, None, False)) |
if len(l) > 0: |
l = self.add_tab(l) |
sret += ";\n".join(l) |
sret += "\n)" |
else: |
# empty ports section |
sret = "" |
return sret |
|
def calculate_type(self, object, with_default=False): |
""" |
Calculate the correct VHDL type for a particular object |
|
Arguments: |
* object: |
* with_default: True to add a default value at the end |
Returns: |
A string with the VHDL equivalent type |
""" |
if isinstance(object, bool): |
return "std_logic" |
elif isinstance(object, (int, long)): |
# CHECK if this is a correct type translation |
return "integer" |
elif isinstance(object, str): |
return "string" |
elif isinstance(object, myhdl.intbv): |
width = object._nrbits |
initval = object._val |
elif isinstance(object, myhdl.SignalType): |
width = object._nrbits |
initval = object._init |
else: |
raise ValueError("Type conversion for object %s not found (type: %s)" % (repr(object), type(object))) |
if width <= 0: |
raise ValueError("Object %s don't have a valid bit width." % repr(object)) |
if width == 1: |
retval = "std_logic" |
defval = "'%d'" % int(initval) |
else: |
retval = "unsigned(%s downto 0)" % (width - 1) |
defval = '"%s"' % myhdl.bin(initval, width) |
if with_default: |
return "%s := %s" % (retval, defval) |
else: |
return retval |
|
# helper functions |
def add_codegen_vhdl_support(instance, **kwargs): |
""" |
This function will add for every object in instance a noc_codegen object |
""" |
if isinstance(instance, noc): |
# add code generation object |
instance.codegen = noc_codegen_vhdl(instance, **kwargs) |
# and add cg objects recursively |
for obj in instance.all_list(True): |
add_codegen_vhdl_support(obj, **kwargs) |
elif isinstance(instance, (ipcore, router, channel)): |
instance.codegen = noc_codegen_vhdl(instance, **kwargs) |
else: |
raise TypeError("Unsupported object: type %s" % type(instance)) |
|
def convert_value(data, custom_type=None, type_array=[None, None]): |
""" |
This function converts data to a string valid in VHDL syntax. |
""" |
retval = "" |
numbits = 0 |
usedata = data |
if custom_type is not None: |
# custom type |
if custom_type == int: |
usedata = int(data) |
elif custom_type == bool: |
usedata = data == True |
elif custom_type == intbv: |
# need fixed boundaries. Use type_array |
if type_array == [None, None]: |
# default: one bit width |
usedata = intbv(data)[1:] |
else: |
# intbv boundaries refer to bit width |
usedata = intbv(data)[type_array[1]+1:type_array[0]] |
elif custom_type == str: |
usedata = str(data) |
elif isinstance(custom_type, str): |
# type in a string format |
if custom_type == "integer": |
usedata = int(data) |
elif custom_type == "std_logic": |
usedata = intbv(data)[1:] |
elif custom_type == "string": |
usedata = str(data) |
elif custom_type.find("std_logic_vector") == 0: |
# strip values |
vecinfo = custom_type.replace("std_logic_vector", "").strip("()").split() |
if vecinfo[1] == "to": |
vecmin = int(vecinfo[0]) |
vecmax = int(vecinfo[2]) |
elif vecinfo[1] == "downto": |
vecmin = int(vecinfo[2]) |
vecmax = int(vecinfo[0]) |
usedata = intbv(data)[vecmax+1:vecmin] |
else: |
raise TypeError("Unsupported custom type string '%s' for VHDL conversion." % custom_type) |
else: |
raise TypeError("Unsupported custom type '%s' for VHDL conversion." % type(custom_type)) |
# convert |
if isinstance(usedata, int): |
retval = "%d" % data |
elif isinstance(usedata, bool): |
retval = "'%d'" % int(data) |
elif isinstance(usedata, intbv): |
# check boundaries |
if usedata._nrbits > 1: |
# print vector in bits |
retval = '"%s"' % bin(usedata, usedata._nrbits) |
else: |
retval = "'%d'" % usedata |
elif isinstance(usedata, str): |
retval = '"%s"' % usedata |
else: |
raise TypeError("Unsupported type '%s' for VHDL conversion." % type(usedata)) |
return retval |
|
# helper structures: |
# type conversions |
_pytovhdltype = { |
"bool": "std_logic", |
"int": "integer", |
"intbv" : "unsigned", |
"str" : "string", |
bool: "std_logic", |
int: "integer", |
intbv : "unsigned", |
str : "string" |
} |
_pytovhdlzeroval = { |
"bool": "False", |
"int": "0", |
"intbv" : '"0"', |
"str" : '""', |
bool: "False", |
int: "0", |
intbv : '"0"', |
str : '""' |
} |
|
/noc_helpers.py
0,0 → 1,78
#!/usr/bin/env python |
# -*- coding: utf-8 -*- |
|
# |
# Helper functions for NoC Objects |
# Functions that easily construct predefined NoC structures and objects |
# |
# Author: Oscar Diaz |
# Version: 0.2 |
# Date: 14-03-2011 |
# |
# This code is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This code is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the |
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
# Boston, MA 02111-1307 USA |
# |
|
# |
# Changelog: |
# |
# 14-03-2011 : (OD) initial release |
# |
|
import networkx as nx |
from noc_base import * |
|
def generate_squarenoc(orderx=3, ordery=None, with_ipcore=False): |
""" |
NoC generator helper: generates a 2D grid NoC object |
|
Arguments |
* orderx: optional X-axis length of the grid. By default build a 3x3 square |
grid. |
* ordery: optional Y-axis length of the grid. By default build a square grid |
of order "orderx". This argument is used to build rectangular grids. |
* with_ipcore: If True, add ipcores to routers automatically. |
""" |
if ordery == None: |
ordery = orderx |
|
# 1. generate a 2d grid |
basegrid = nx.grid_2d_graph(orderx, ordery) |
|
# 2. convert to a graph with ints as nodes |
convgrid = nx.Graph() |
for n in basegrid.nodes_iter(): |
n2 = n[0] + n[1]*orderx |
convgrid.add_node(n2, coord_x=n[0], coord_y=n[1]) |
for e in basegrid.edges_iter(): |
e1 = e[0][0] + e[0][1]*orderx |
e2 = e[1][0] + e[1][1]*orderx |
convgrid.add_edge(e1, e2) |
|
nocbase = noc(name="NoC grid %dx%d" % (orderx, ordery), data=convgrid) |
|
# 2. for each node add router object |
for n in nocbase.nodes_iter(): |
cx = nocbase.node[n]["coord_x"] |
cy = nocbase.node[n]["coord_y"] |
r = nocbase._add_router_from_node(n, coord_x=cx, coord_y=cy) |
if with_ipcore: |
nocbase.add_ipcore(r) |
|
# 3. for each edge add channel object |
for e in nocbase.edges_iter(): |
nocbase._add_channel_from_edge(e) |
|
return nocbase |
/noc_tbm_utils.py
0,0 → 1,71
#!/usr/bin/env python |
# -*- coding: utf-8 -*- |
|
# |
# NoC TBM simulation support - Utilities |
# This module declares additional helper functions |
# |
# Author: Oscar Diaz |
# Version: 0.2 |
# Date: 17-03-2011 |
|
# |
# This code is free software; you can redistribute it and/or |
# modify it under the terms of the GNU Lesser General Public |
# License as published by the Free Software Foundation; either |
# version 2.1 of the License, or (at your option) any later version. |
# |
# This code is distributed in the hope that it will be useful, |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
# Lesser General Public License for more details. |
# |
# You should have received a copy of the GNU Lesser General Public |
# License along with this library; if not, write to the |
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
# Boston, MA 02111-1307 USA |
# |
|
# |
# Changelog: |
# |
# 03-03-2011 : (OD) initial release |
# |
|
""" |
============================= |
NoCmodel TBM simulation utils |
============================= |
|
This module declares additional helper functions. |
|
* Function 'add_tbm_basic_support' |
""" |
|
from noc_tbm_base import * |
from nocmodel.basicmodels import * |
|
# helper functions |
def add_tbm_basic_support(instance, **kwargs): |
""" |
This function will add for every object in noc_instance a noc_tbm object |
""" |
if isinstance(instance, noc): |
# add simulation object |
instance.tbmsim = noc_tbm_simulation(instance, **kwargs) |
# and add tbm objects recursively |
for obj in instance.all_list(): |
altkwargs = kwargs |
altkwargs.pop("log_file", None) |
altkwargs.pop("log_level", None) |
add_tbm_basic_support(obj, **kwargs) |
elif isinstance(instance, ipcore): |
instance.tbm = basic_ipcore_tbm(instance, **kwargs) |
# don't forget internal channel |
instance.channel_ref.tbm = basic_channel_tbm(instance.channel_ref, **kwargs) |
elif isinstance(instance, router): |
instance.tbm = basic_router_tbm(instance, **kwargs) |
elif isinstance(instance, channel): |
instance.tbm = basic_channel_tbm(instance, **kwargs) |
else: |
raise TypeError("Unsupported object: type %s" % type(instance)) |