"""
=================
verilog_testbench
=================
Verilog testbench generator utility module for TheSyDeKick. Documentation provided in 'testbench' class
Extends `testbench_common`.
Initially written by Marko Kosunen 20190108, marko.kosunen@aalto.fi
Refactored from 'testbench' by Marko Kosunen 20221119, marko.kosunen@aalto.fi
"""
import os
import sys
import pdb
from rtl import indent
from rtl.connector import rtl_connector
from rtl.testbench_common import testbench_common
[docs]
class verilog_testbench(testbench_common):
""" Verilog testbench class.
"""
def __init__(self, parent=None, **kwargs):
""" Executes init of testbench_common, thus having the same attributes and
parameters.
Parameters
----------
**kwargs :
See module module_common
"""
super().__init__(parent,**kwargs)
if self.parent.add_tb_timescale:
self.header='`timescale ' + self.rtl_timescale + ' / '+ self.rtl_timeprecision+'\n'
else:
self.header=''
# For some reason unknown to me, overloading he property here is not effective
#@property
#def header(self):
# """ str : Header line of the verilog testbench file.
# Default: If self.add_tb_timescale = True, adds the timescale directive to the testbench top.
# according ot self.rtl_timescale attribute.
# """
#
# if not hasattr (self, '_header'):
# if self.parent.add_tb_timescale:
# self._defheader='`timescale ' + self.rtl_timescale + ' / '+ self.rtl_timescale+'\n'
# else:
# self._defheader=''
# return self._defheader
# else:
# return self._header
#@header.setter
#def header(self,val):
# self._header = val
@property
def parameter_definitions(self):
"""Parameter and variable definition strings of the testbench
"""
definitions='//Parameter definitions\n'
for name, val in self.content_parameters.items():
definitions+='parameter '+ val[0]+' '+name+'='+ val[1]+';\n'
return definitions
@property
def content_parameters(self):
""" Parameters used inside the testbench
Dict : {name: (type,value)}
Example
-------
::
{'c_Ts': ('integer','1/(g_Rs*1e-12)')}
"""
if not hasattr(self,'_content_parameters'):
self._content_parameters={'c_Ts': ('integer','1/(g_Rs*1e-12)')}
return self._content_parameters
@content_parameters.setter
def content_parameters(self,val):
self._content_parameters=val
@property
def connector_definitions(self):
"""Verilog register and wire definition strings
"""
#Update the language formatting. We are operating in verilog
for name, val in self.connectors.Members.items():
val.lang='sv'
# Registers first
definitions='//Register definitions\n'
for name, val in self.connectors.Members.items():
if val.cls=='reg':
definitions=definitions+val.definition
definitions=definitions+'\n//Wire definitions\n'
for name, val in self.connectors.Members.items():
if val.cls=='wire':
definitions=definitions+val.definition
return definitions
[docs]
def assignments(self,**kwargs):
"""Wire assingment strings
"""
matchlist=kwargs.get('matchlist',self.assignment_matchlist)
assigns='\n//Assignments\n'
for match in matchlist:
assigns=assigns+self.connectors.assign(match=match)
return indent(text=assigns,level=kwargs.get('level',0))
@property
def iofile_definitions(self):
"""IOfile definition strings
"""
iofile_defs='//Variables for the io_files\n'
for name, val in self.iofiles.Members.items():
iofile_defs=iofile_defs+val.rtl_statdef
iofile_defs=iofile_defs+val.rtl_fopen
iofile_defs=iofile_defs+'\n'
return iofile_defs
@property
def clock_definition(self):
"""Clock definition string
Todo
Create append mechanism to add more clocks.
"""
clockdef='//Master clock is omnipresent\nalways #(c_Ts/2.0) clock = !clock;'
return clockdef
@property
def iofile_close(self):
"""File close procedure for all IO files.
"""
iofile_close='\n//Close the io_files\n'
for name, val in self.iofiles.Members.items():
iofile_close=iofile_close+val.rtl_fclose
iofile_close=iofile_close+'\n'
return iofile_close
@property
def end_condition(self):
""" Verilog structure for custom finish of the simulation.
Default: ''
"""
if not hasattr(self,'_end_condition'):
if hasattr(self.parent, 'end_condition'):
self._end_condition = self.parent.end_condition
else:
self._end_condition ='\n'
return self._end_condition
@end_condition.setter
def end_condition(self,value):
self._end_condition = value
@property
def misccmd(self):
"""String
Miscellaneous command string corresponding to self.rtlmisc -list in
the parent entity.
"""
if not hasattr(self,'_misccmd'):
self._misccmd="// Manual commands\n"
mcmd = self.parent.rtlmisc
for cmd in mcmd:
self._misccmd += cmd + "\n"
return self._misccmd
@misccmd.setter
def misccmd(self,value):
self._misccmd=value
@misccmd.deleter
def misccmd(self,value):
self._misccmd=None
[docs]
def define_testbench(self):
"""Defines the tb connectivity, creates reset and clock, and initializes them to zero
"""
# Dut is creted automaticaly, if verilog file for it exists
self.connectors.update(bundle=self.dut_instance.io_signals.Members)
#Assign verilog simulation parameters to testbench
self.parameters=self.parent.rtlparameters
# Create clock if nonexistent and reset it
if 'clock' not in self.dut_instance.ios.Members:
self.connectors.Members['clock']=rtl_connector(lang='sv',
name='clock',cls='reg', init='\'b0')
elif self.connectors.Members['clock'].init=='':
self.connectors.Members['clock'].init='\'b0'
# Create reset if nonexistent and reset it
if 'reset' not in self.dut_instance.ios.Members:
self.connectors.Members['reset']=rtl_connector(lang='sv',
name='reset',cls='reg', init='\'b0')
elif self.connectors.Members['reset'].init=='':
self.connectors.Members['reset'].init='\'b0'
## Start initializations
#Init the signals connected to the dut input to zero
for name, val in self.dut_instance.ios.Members.items():
if val.cls=='input':
val.connect.init='\'b0'
# Automate this based in dir
[docs]
def generate_contents(self):
""" This is the method to generate testbench contents. Override if needed
Contents of the testbench is constructed from attributes in the
following order ::
self.parameter_definitions
self.connector_definitions
self.assignments()
self.iofile_definitions
self.misccmd
self.dumpfile
self.dut_instance.verilog_instance
self.verilog_instance_members.items().instance (for all members)
self.connectors.verilog_inits()
self.iofiles.Members.items().rtl_io (for all members)
self.iofile.close (for all members)
Addtional code may be currently injected by appending desired
strings (Verilog sytax) to the relevant string attributes.
"""
# Start the testbench contents
contents="""
//timescale 1ps this should probably be a global model parameter
"""+self.parameter_definitions+\
self.connector_definitions+\
self.assignments()+\
self.iofile_definitions+\
self.misccmd+\
self.end_condition+\
self.dumpfile+\
"""
//DUT definition
"""+\
self.dut_instance.verilog_instance
for inst, module in self.verilog_instances.Members.items():
contents+=module.instance
contents+=self.clock_definition
contents+="""
//io_out
"""
for key, member in self.iofiles.Members.items():
if member.dir=='out':
contents+=member.rtl_io
contents+="""
//Execution with parallel fork-join and sequential begin-end sections
initial #0 begin
fork
""" + \
self.connectors.rtl_inits(level=1)+\
"""
// Sequences enabled by initdone
$display("Ready to read");
"""
for key, member in self.iofiles.Members.items():
if member.dir=='in':
contents+=indent(text=member.rtl_io, level=1)
contents+='\njoin\n'+self.iofile_close+'\n'
contents+='$finish;\n'
contents+='end\n'
self.contents=contents
if __name__=="__main__":
pass