From Python to silicon
 

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

users:george_pantazopoulos:demo_1 [2006/10/10 12:02] (current)
themaxx created
Line 1: Line 1:
 +<code python>
 +from myhdl import *
 +
 +# myhdl_signal_and_port_groups_with_wishbone.py
 +# George Pantazopoulos
 +# 10 Oct 2006
 +
 +# Key concepts:
 +#
 +
 +# Hardware Module: aka "module". Different meaning than a Python module.
 +
 +# Signal: A connection between one or more modules.
 +#         A Signal may be driven by only one module at a time
 +
 +# Signal Group: A connection of related signals
 +
 +
 +# Port:   A module's connection to the world outside it.
 +#         To connect a module, connect signals to its ports
 +
 +
 +# Port Group: A collection of related ports
 +
 +
 +# TODO: Break out ASCII Port Spec parser into own class
 +
 +class Port(object):
 +
 + def __init__(self,name, direction, descr=""):
 +
 +     self.name = name
 +     self.signal = None
 +
 +     directions = ['in', 'out', 'inout']
 +
 +     if direction not in directions:
 +    raise Exception, \
 +    "Direction needs to be one of " + str(directions)
 +
 +     self.dir = direction
 +     self.descr = descr
 +
 + def connect_to(self, signal):
 +     """
 +     signal should be of type <class 'myhdl_.Signal.Signal'>
 +     """
 +     self.signal = signal
 +
 + def get_name(self):
 +     return self.name
 +    
 + def get_descr(self):
 + return self.descr
 +
 + def sig(self):
 +     """
 +     """
 +     # It's helpful to error here if the 'signal'
 +     # is not set. Otherwise the errors in MyHDL are confusing
 +
 +     # TODO: is this the right place for this?
 +     # Maybe if we just return the None value it can cause an
 +     # exception elsewhere
 +     if self.signal == None:
 +    raise Exception, \
 +    "port '" + str(self.name) + "' has no signal connected to it"
 +
 +
 +     return self.signal
 +
 +
 +# generic record-keeping class
 +class rec:
 +
 + def __getattr__(self, attrname):
 + return self.__dict__[attrname]
 +
 + def __setattr__(self, attr, value):
 + self.__dict__[attr] = value
 +
 + def __repr__(self):
 + s = ""
 + for key in self.__dict__:
 + s += key + ": " + repr(self.__dict__[key])
 + s += '\n'
 + return s
 +
 +
 +
 +class HardwareModule:
 +
 + def __init__(self):
 +
 + # A collection of port groups
 + self.__dict__['port_groups'] = rec()
 +
 +
 + def __getattr__(self, attrname):
 +
 + if attrname == 'ports':
 + return self.__dict__['port_groups']
 +
 + else:
 + raise AttributeError, attrname
 +
 +
 + def __setattr__(self, attr, value):
 +
 + if attr == 'ports':
 + self.__dict__[attr] = value
 +
 + else:
 + self.__dict__[attr] = value
 +
 +
 + def add_port_group(self, name):
 + """
 + Add an empty port group
 + """
 +
 + self.port_groups.__setattr__(name, rec())
 +
 +
 + def connect(self, port, sig):
 + """
 + Connect a port in the design to an external signal
 +
 + Must qualify port name within a port group with a '.'
 +
 + Eg.
 +
 + clk = Signal(bool(0))
 +
 + uc = UpCounter()
 + uc.connect('wishbone.clk_i', clk)
 +
 + """
 +
 + # The port name will be qualified with the port group
 + fqpn = port.split('.')
 +
 + self.ports.__getattr__(fqpn[0]).__getattr__(fqpn[1]).connect_to(sig)
 +
 + def parse_portspec(self, s):
 +     """
 +     Automatically parses an Ascii Port Specification
 +     and generates port groups that contain the correct ports.
 +     
 +     Example portspec:
 +     -----------------
 +     
 +     Begin portspec
 +     ----------------------------------------------------------
 +     Name    Dir   Width         Group           Descr         
 +     ----------------------------------------------------------
 +     clk_i   in    1             wishbone        clock   input
 +     rst_i   in    1             wishbone        reset   input
 +     count_o out   count_width   non_wishbone    counter output
 +
 +     End portspec
 +     
 +     In the port attribute line there must be at least one char whitespace
 +     between port attributes. Currently only spaces are supported, not tabs.
 +     """
 +     
 +     __version__ = "0.2.0"
 +     
 +     beginstr = "Begin portspec"
 +     endstr   = "End portspec"
 +
 +     # It's a assumed that a docstring will be inputted.
 +     
 +     # Break up the string into seperate lines
 +     lines = s.splitlines()
 +
 +     def find_in_str_list(str_list, substr):
 +    """
 +    Given a list of strings, return the list index
 +    of the string containing the first occurence
 +    of substr.
 +
 +    If not found, returns -1
 +    """
 +
 +    index = -1
 +
 +    # For each string in the list
 +    for i in range(len(str_list)):
 +
 +   # look for substr
 +   hindex = str_list[i].find(substr)
 +
 +   # if found, break.
 +   # We dont care about the horizontal position of the substring
 +   if hindex != -1:
 + index = i
 + break
 +   
 +    return index
 +
 +     # Search for the begin string
 +     beginstr_line = find_in_str_list(lines, beginstr)
 +
 +     # If no begin string found, then error
 +     if beginstr_line == -1:
 +    raise Exception, \
 +    "port specification must begin with: " + beginstr + "on its own line"
 +
 +     # Search for the end string
 +     endstr_line   = find_in_str_list(lines, endstr)
 +
 +     # If no begin string found, then error
 +     if endstr_line == -1:
 +    raise Exception, \
 +    "port specification must end with: " + endstr + "on its own line"
 +
 +     min_num_lines = 4
 +     if endstr_line - beginstr_line < min_num_lines:
 +    raise Exception, "\n" + \
 +   "There must be at least " + str(min_num_lines) + \
 +   "lines between the begin and end strings"     
 +
 +     # ignore the next line
 +
 +     # The following line should contain the port attribute types
 +     # Name,Dir, Width, Class, Descr
 +     pat_str_line = beginstr_line + 2
 +     pat_str = lines[pat_str_line]    
 +
 +     pats = dict( _name  = 'Name',
 +   _dir   = 'Dir',
 +   _width = 'Width',
 +   _group = 'Group',
 +   _descr = 'Descr')
 +
 +     pat_indices =    dict( _name  = -1,
 +   _dir   = -1,
 +   _width = -1,
 +   _group = -1,
 +   _descr = -1)
 +     
 +     # Find all of these and record their horizonal indices
 +
 +
 +     for pat in pats:
 +    ps = pats[pat]
 +    idx = pat_str.find(ps)
 +
 +    # If even one of them is not found, it's an error
 +    if idx == -1:
 +   raise Exception, \
 +   "Could not find " + ps + " in Port Attribute Types line"
 +     
 +    # Record its horizontal index
 +    pat_indices[pat] = idx
 +
 +
 +     import pprint
 +     #pprint.pprint(pat_indices)
 +
 +     # Enforce a particular order for the port attribute types
 +     pat_order = \
 +     [pats['_name'], pats['_dir'], pats['_width'], pats['_group'], pats['_descr']]
 +
 +     # Split the given port attribute string into a list of seperate strings
 +     # and compare the it against the reference order list
 +
 +     if pat_str.split() != pat_order:
 +
 +    # Build an example string to show the user
 +    example_str = ""
 +    for s in pat_order:
 +   example_str += s
 +   example_str += " "
 +
 +    # Note, this exception may also be raised if an extra column was added
 +    raise Exception, \
 +    "\nInvalid port attribute types line: (line #" + \
 +    str(pat_str_line) + " of docstring)\n" +\
 +    "'" + pat_str + "'" + \
 +    "\nOnly these port attribute types must be present " + \
 +    "and be in the following order:\n" + \
 +    "'" + example_str + "'"
 +    
 +     
 +     # ignore the next line
 +     first_port_attr_line = pat_str_line + 2
 +     last_port_attr_line  = endstr_line - 1
 +
 +     pa_dicts = dict()
 +     for line in lines[first_port_attr_line:last_port_attr_line]:
 +
 +    # Skip this line if it consists entirely of whitespace
 +    if len(line.split()) <= 0:
 +   continue
 +     
 +    # Each port attribute is supposed to be lined up horizontally
 +    # with each port attribute type up top.
 +    
 +    # For each user port attribute type, grab
 +    # the user port attribute string that is lined up beneath it
 +    
 +    pa_dict = dict()
 +    for pat in pat_indices:
 +   pat_index = pat_indices[pat]
 +
 +   # from the user port attribute line, extract a substring
 +   # starting at the column of the port attribute type above it
 +   # and going to the end of the line
 +   pa_plus_rest_of_line = line[pat_index-1:]
 +
 +   # Split this portion into tokens and take the first one
 +   pa = pa_plus_rest_of_line.split()[0]
 +   
 +
 +   # For each port attribute line
 +   # Create a dictionary containing all the user-supplied
 +   # port attributes
 +   # eg. foo = dict('_name'='clk_i', '_dir'='inout')
 +
 +   pa_dict[pat] = pa
 +   
 +    # To associate all the port attributes with a port name
 +    # stuff that dictionary inside another dictionary with the
 +    # key being the port's given name.
 +    # eg
 +    # bar = dict(foo['name']=foo)
 +
 +    port_name = pa_dict['_name']
 +    pa_dicts[port_name]=pa_dict
 +
 +
 +
 +     # For each port attribute 'group' found, create a port group
 +     # They are attributes of a simple record-keeping class
 +     port_groups = rec()
 +     for port_name in pa_dicts:
 +
 +    port_groupname = pa_dicts[port_name]['_group']
 +    port_groups.__setattr__(port_groupname, rec())
 +     
 +
 +     # Now that we have our port groups, put each port in the correct
 +     # port group
 +     
 +     for port_name in pa_dicts:
 +    
 +    port_dir = pa_dicts[port_name]['_dir']
 +    port_groupname = pa_dicts[port_name]['_group']
 +    port_descr = pa_dicts[port_name]['_descr']
 +
 +    # Create a port
 +    # TODO: support more attributes or use dict directly
 +    p = Port(name=port_name, direction=port_dir, descr=port_descr)
 +
 +    # Add it to the correct port group
 +    port_groups.__getattr__(port_groupname).__setattr__(port_name, p)
 +
 +     return port_groups
 +
 +
 +class UpCounter(HardwareModule):
 + """
 + Begin portspec
 + ----------------------------------------------------------
 + Name    Dir   Width         Group           Descr         
 + ----------------------------------------------------------
 + clk_i   in    1             wishbone        clock   input
 + rst_i   in    1             wishbone        reset   input
 + count_o out   count_width   non_wishbone    counter output
 +
 + End portspec
 + """
 +
 + # TODO: Have portspec parser handle tabs
 + # TODO: Also, spaces and tabs should be allowed for Descr
 +
 + def __init__(self):
 +
 + # Need to initialize superclass
 + HardwareModule.__init__(self)
 +
 + # Auto-generated ports and port groups! 
 + # TODO: make error messages more friendly
 + # (eg. "Could not parse port spec")
 + self.ports = self.parse_portspec(self.__doc__)
 +
 + def up_counter(self, clk_i, rst_i, count_o):
 + """
 + up-counter MyHDL implementation
 + """
 +
 + @always(clk_i.posedge)
 + def proc():
 +   if rst_i:
 + count_o.next = 0
 +
 +   else:
 + count_o.next = count_o + 1
 +
 + return instances()
 +  
 + def hw_inst(self):
 + """
 + Connects the ports of the implementation to the Signals
 + in each class-level port and returns the connected unit.
 + """
 +
 + return self.up_counter(clk_i   = self.ports.wishbone.clk_i.sig(), 
 +    rst_i   = self.ports.wishbone.rst_i.sig(), 
 +    count_o = self.ports.non_wishbone.count_o.sig())
 +
 +def top(clk, rst):
 +
 + count = Signal(intbv(0)[3:])
 +
 + uc = UpCounter()
 +
 + uc.connect('wishbone.clk_i', clk)
 + uc.connect('wishbone.rst_i', rst)
 + uc.connect('non_wishbone.count_o', count)
 +
 + UC_INST = uc.hw_inst()
 +
 + return instances()
 +
 +clk = Signal(bool(0))
 +rst = Signal(bool(0))
 +
 +
 +toVerilog(top, clk, rst)
 +</code>
  
users/george_pantazopoulos/demo_1.txt ยท Last modified: 2006/10/10 12:02 by themaxx
 
Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki