Source code for rtl.sv.verilog_module

"""
==============
Verilog Module
==============
Verilog module import features for RTL simulation package of 
The System Development Kit.

Provides utilities to import Verilog modules to 
python environment.

Initially written by Marko Kosunen, 2017

"""
import os
from thesdk import *
from rtl import *
from copy import deepcopy
from rtl.connector import *
from rtl.module_common import module_common

[docs] class verilog_module(module_common,thesdk): """Objective: 1) a) Collect IO's to database b) collect parameters to dict 2) Reconstruct the module definition 3) a) Implement methods provide sinal connections b) Implement methods to provide parameter assingments 4) Create a method to create assigned module \ definition, where signals are \ a) assigned by name b) to arbitrary name vector. 5) Add contents, if required, and include that to definition """ def __init__(self, **kwargs): ''' Executes init of module_common, thus having the same attributes and parameters. Parameters ---------- **kwargs : See module module_common ''' super().__init__(**kwargs) @property def ios(self): '''Verilog connector bundle containing connectors for all module IOS. All the IOs are connected to signal connectors that have the same name than the IOs. This is due to fact the we have decided that all signals are connectors. ''' if not hasattr(self,'_ios'): # multiline regex for parsing relevant sections of a module definition # capture groups: # 0. (full match) # 1. module parameter port list # 2. list of ports # 3. module items # # playground: https://regex101.com/r/n2Zmgn/1 module_pattern = r"module\s*"+self.name+r"\s*(\s*#\([\s\S]*?\))?\s*\(([\s\S]*?)\);([\s\S]*?)endmodule" module_regex = re.compile(module_pattern) # regex pattern for parsing port declarations # capture groups: # 0. (full match) # 1. port direction (input|output|inout) # 2. port type (wire|reg) # 3. port size [msb:lsb] # 4. msb index # 5. lsb index # 6. port name (identifier) # # playground: https://regex101.com/r/eiZDS0/1 # TODO: add support for escaped identifiers def port_pattern(identifier): return fr"\s*(input|output|inout)?\s*(wire|reg)?\s*(\[\s*([0-9]+)\s*:\s*([0-9]+)\s*\])?\s*({identifier})" port_identifier_pattern = r"[a-zA-Z_]+[a-zA-Z0-9_$]*" port_regex = re.compile(port_pattern(port_identifier_pattern)) self._ios=rtl_connector_bundle() self.print_log(type='I', msg="{}".format(self.file)) if not os.path.isfile(self.file): self.print_log(type='F', msg='File does not exist: %s' % self.file) # parse module IO definitions with open(self.file) as infile: file_str = infile.read() module_match = re.search(module_regex, file_str) if module_match is not None: signals = [] port_list = module_match.group(2).split(',') for port in port_list: port_match = re.search(port_regex, port) signal=rtl_connector(lang='sv') signal.cls=port_match.group(1) if port_match.group(3) is not None: signal.ll=port_match.group(4) signal.rl=port_match.group(5) signal.name=port_match.group(6) signals.append(signal) # look for port information in module items since # some tools output verilog in a format where the port declarations are # split between the module port list and the module body for signal in signals: # fill in IO information defined in module body signal_regex = re.compile(port_pattern(signal.name)) match = re.search(signal_regex, module_match.group(3)) if match is not None: if match.group(1) is not None: signal.cls = match.group(1) if match.group(3) is not None: signal.ll = match.group(4) signal.rl = match.group(5) # By default, we create a connector that is cross connected to the input signal.connect=deepcopy(signal) if signal.cls=='input': signal.connect.cls='reg' if signal.cls=='output': signal.connect.cls='wire' signal.connect.connect=signal self._ios.Members[signal.name]=signal return self._ios # Setting principle, assign a dict # individual parameters can be set externally @ios.setter def ios(self,value): self._ios=deepcopy(value) @property def parameters(self): '''Parameters of the verilog module. Bundle of values of type string. ''' if not hasattr(self,'_parameters'): startmatch=re.compile(r"module *(?="+self.name+r"\s*\()\s*"+r".*.+$") iomatch=re.compile(r".*(?<!#)\(.*$") parammatch=re.compile(r".*(?<=#)\(.*$") paramstopmatch=re.compile(r".*\).*$") parablock='' self._parameters=Bundle() # Extract the module definition if os.path.isfile(self.file): with open(self.file) as infile: wholefile = infile.readlines() modfind = False iofind = False parafind = False for line in wholefile: if (not modfind and startmatch.match(line)): modfind = True if modfind and (not iofind) and iomatch.match(line): iofind = True if modfind and (not iofind) and parammatch.match(line): parafind = True if ( modfind and (not iofind) and parafind and paramstopmatch.match(line)): modfind = False parafind = False line = re.sub(r"\).*$","",line) line = re.sub(r"//.*$","",line) #Inclusive parablock = parablock+line +'\n' elif modfind and parafind: line = re.sub(r"//.*$","",line) parablock = parablock+line if parablock: #Generate lambda functions for pattern filtering parablock.replace("\n","") fils = [ re.compile(r"module\s*"+self.name+r"\s*"), re.compile(r"#"), re.compile(r"\(*"), re.compile(r"\)*"), re.compile(r"\s*"), re.compile(r";*") ] func_list= [lambda s,fil=x: re.sub(fil,"",s) for x in fils] parablock=reduce(lambda s, func: func(s), func_list, parablock) parablock=parablock.split(',') for param in parablock: extr=param.split('=') self._parameters.Members[extr[0]]=extr[1] return self._parameters # Setting principle, assign a dict # individual parameters can be set externally @parameters.setter def parameters(self,value): self._parameters.Members=deepcopy(value) @property def contents(self): '''Contents of the module. String containing the Verilog code after the module definition. ''' if not hasattr(self,'_contents'): startmatch=re.compile(r"module *(?="+self.name+r")\s*"+r".*.+$") headerstopmatch=re.compile(r".*\);.*$") modulestopmatch=re.compile(r"\s*endmodule\s*$") self._contents='\n' # Extract the module definition if os.path.isfile(self.file): with open(self.file) as infile: wholefile=infile.readlines() modfind=False headers=False for line in wholefile: if (not modfind and startmatch.match(line)): modfind=True if modfind and headerstopmatch.match(line): headers=True elif ( modfind and headers and modulestopmatch.match(line)): modfind=False headers=False #exclusive elif modfind and headers: self._contents=self._contents+line return self._contents @contents.setter def contents(self,value): self._contents=value @contents.deleter def contents(self,value): self._contents=None @property def io_signals(self): '''Bundle containing the signal connectors for IO connections. ''' if not hasattr(self,'_io_signals'): self._io_signals=rtl_connector_bundle() for ioname, io in self.ios.Members.items(): # Connectior is created already in io definitio # just point to it self._io_signals.Members[ioname]=io.connect return self._io_signals @io_signals.setter def io_signals(self,value): for conn in value.Members : self._io_signals.Members[conn.name].connect=conn return self._io_signals @property def header(self): """Header configuring the e.g. libraries if needed""" if not hasattr(self,'_header'): self._header='' return self._header @header.setter def header(self,value): if not hasattr(self,'_header'): self._header=value @property def definition(self): '''Module definition part extracted for the file. Contains parameters and IO definitions. ''' if not hasattr(self,'_definition'): #First we print the parameter section if self.parameters.Members: parameters='' first=True for name, val in self.parameters.Members.items(): if type(val) is not tuple: self.print_log(type='F', msg='Parameter %s must be defined as {\'<name>\': (\'<type>\',value)}' %(name)) if first: parameters='#(\n parameter %s = %s' %(name,val[1]) first=False else: parameters=parameters+',\n parameter %s = %s' %(name,val[1]) parameters=parameters+'\n)' self._definition='module %s %s' %(self.name, parameters) else: self._definition='module %s ' %(self.name) first=True if self.ios.Members: for ioname, io in self.ios.Members.items(): if first: self._definition=self._definition+'(\n' first=False else: self._definition=self._definition+',\n' if io.cls in [ 'input', 'output', 'inout' ]: if io.width==1: self._definition=(self._definition+ (' %s %s' %(io.cls, io.name))) else: self._definition=(self._definition+ (' %s [%s:%s] %s' %(io.cls, io.ll, io.rl, io.name))) else: self.print_log(type='F', msg='Assigning signal direction %s to verilog module IO.' %(io.cls)) self._definition=self._definition+'\n)' self._definition=self._definition+';' if self.contents: self._definition=self._definition+self.contents+'\nendmodule' return self._definition # Instance is defined through the io_signals # Therefore it is always regenerated @property def verilog_instance(self): '''Instantioation string of the module. Can be used inside of the other modules. ''' #First we write the parameter section if self.parameters.Members: parameters='' first=True for name, val in self.parameters.Members.items(): if first: parameters='#(\n .%s(%s)' %(name,name) first=False else: parameters=parameters+',\n .%s(%s)' %(name,name) parameters=parameters+'\n)' self._instance='%s %s %s' %(self.name, parameters, self.instname) else: self._instance='%s %s ' %(self.name, self.instname) first=True # Then we write the IOs if self.ios.Members: for ioname, io in self.ios.Members.items(): if first: self._instance=self._instance+'(\n' first=False else: self._instance=self._instance+',\n' if io.cls in [ 'input', 'output', 'inout' ]: self._instance=(self._instance+ (' .%s(%s)' %(io.name, io.connect.name))) else: self.print_log(type='F', msg='Assigning signal direction %s to verilog module IO.' %(io.cls)) self._instance=self._instance+('\n)') self._instance=self._instance+(';\n') return self._instance #Methods
[docs] def export(self,**kwargs): '''Method to export the module. Exports self.headers+self.definition to a given file. Parameters ---------- **kwargs : force: Bool ''' if not os.path.isfile(self.file): self.print_log(msg='Exporting verilog_module to %s.' %(self.file)) with open(self.file, "w") as module_file: module_file.write(self.header+self.definition) elif os.path.isfile(self.file) and not kwargs.get('force'): self.print_log(type='F', msg=('Export target file %s exists.\n Force overwrite with force=True.' %(self.file))) elif kwargs.get('force'): self.print_log(msg='Forcing overwrite of verilog_module to %s.' %(self.file)) with open(self.file, "w") as module_file: module_file.write(self.header+self.definition)
if __name__=="__main__": pass