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

Subversion Repositories nocmodel

[/] [nocmodel/] [trunk/] [nocmodel/] [noc_codegen_base.py] - Rev 4

Compare with Previous | Blame | View Log

#!/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
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

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