From Python to silicon
 
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)
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