"""
========
Inverter
========
Inverter model template The System Development Kit
Used as a template for all TheSyDeKick Entities.
Current docstring documentation style is Numpy
https://numpydoc.readthedocs.io/en/latest/format.html
For reference of the markup syntax
https://docutils.sourceforge.io/docs/user/rst/quickref.html
This text here is to remind you that documentation is important.
However, youu may find it out the even the documentation of this
entity may be outdated and incomplete. Regardless of that, every day
and in every way we are getting better and better :).
Initially written by Marko Kosunen, marko.kosunen@aalto.fi, 2017.
Role of section 'if __name__=="__main__"'
--------------------------------------------
This section is for self testing and interfacing of this class. The content of
it is fully up to designer. You may use it for example to test the
functionality of the class by calling it as ``pyhon3 __init__.py`` or you may
define how it handles the arguments passed during the invocation. In this
example it is used as a complete self test script for all the simulation models
defined for the inverter.
"""
import os
import sys
if not (os.path.abspath('../../thesdk') in sys.path):
sys.path.append(os.path.abspath('../../thesdk'))
from thesdk import *
from rtl import *
from spice import *
import numpy as np
[docs]
class inverter(rtl,spice,thesdk):
[docs]
def __init__(self,*arg):
""" Inverter parameters and attributes
Parameters
----------
*arg :
If any arguments are defined, the first one should be the parent instance
Attributes
----------
proplist : array_like
List of strings containing the names of attributes whose values are to be copied
from the parent
Rs : float
Sampling rate [Hz] of which the input values are assumed to change. Default: 100.0e6
vdd : float
Supply voltage [V] for inverter analog simulation. Default 1.0.
IOS : Bundle
Members of this bundle are the IO's of the entity. See documentation of thsdk package.
Default members defined as
self.IOS.Members['A']=IO() # Pointer for input data
self.IOS.Members['Z']= IO() # pointer for oputput data
self.IOS.Members['control_write']= IO() # Piter for control IO for rtl simulations
model : string
Default 'py' for Python. See documentation of thsdk package for more details.
"""
self.print_log(type='I', msg='Initializing %s' %(__name__))
self.proplist = ['Rs', 'vdd'] # Properties that can be propagated from parent
self.Rs = 100e6 # Sampling frequency
self.vdd = 1.0
self.IOS.Members['A'] = IO() # Pointer for input data
self.IOS.Members['Z'] = IO()
self.IOS.Members['CLK'] = IO() # Test clock for spice simulations
self.IOS.Members['A_OUT'] = IO() # Test output for the input A
##Analog output for inverter for analog simulation
self.IOS.Members['Z_ANA'] = IO()
## For Extracting rising edges from the output waveform
self.IOS.Members['Z_RISE'] = IO()
## Extracting values of A and Z at falling edges of CLK in decimal format (integer, in this case 0 or 1)
## The clock signal can be any node voltage in the simulation
self.IOS.Members['A_DIG'] = IO()
self.IOS.Members['control_write'] = IO() # File for control is created in controller
self.model = 'py' # Can be set externally, but is not propagated
# this copies the parameter values from the parent based on self.proplist
if len(arg)>=1:
parent=arg[0]
self.copy_propval(parent,self.proplist)
self.parent=parent
self.init()
[docs]
def init(self):
""" Method to re-initialize the structure if the attribute values are changed after creation.
"""
pass #Currently nothing to add
[docs]
def main(self):
''' The main python description of the operation. Contents fully up to designer, however, the
IO's should be handled bu following this guideline:
To isolate the internal processing from IO connection assigments,
The procedure to follow is
1) Assign input data from input to local variable
2) Do the processing
3) Assign local variable to output
'''
inval=self.IOS.Members['A'].Data
out=np.array(1-inval)
if self.par:
self.queue.put(out)
self.IOS.Members['Z'].Data=out
[docs]
def run(self,*arg):
''' The default name of the method to be executed. This means: parameters and attributes
control what is executed if run method is executed. By this we aim to avoid the need of
documenting what is the execution method. It is always self.run.
Parameters
----------
*arg :
The first argument is assumed to be the queue for the parallel processing defined in the parent,
and it is assigned to self.queue and self.par is set to True.
'''
if self.model=='py':
self.main()
else:
# This defines contents of modelsim control file executed when interactive_rtl = True
# Interactive control files
if self.model == 'icarus' or self.model == 'ghdl':
self.interactive_control_contents="""
set io_facs [list]
lappend io_facs "tb_inverter.A"
lappend io_facs "tb_inverter.Z"
lappend io_facs "tb_inverter.clock"
gtkwave::addSignalsFromList $io_facs
gtkwave::/Time/Zoom/Zoom_Full
"""
else:
self.interactive_control_contents="""
add wave \\
sim/:tb_inverter:A \\
sim/:tb_inverter:initdone \\
sim/:tb_inverter:clock \\
sim/:tb_inverter:Z
run -all
wave zoom full
"""
if self.model == 'ghdl':
# With this structure you can control the signals to be dumped to VCD
#pass
self.simulator_control_contents=("version = 1.1 # Optional\n"
+ "/tb_inverter/A\n"
+ "/tb_inverter/Z\n"
+ "/tb_inverter/clock\n"
)
if self.model == 'sv':
self.simulator_control_contents = ("vcd file %s/inverter_dump.vcd\n" %(self.rtlsimpath)
+ "vcd add -r *\n"
+ "vcd on\n"
+ "run -all\n"
+ "quit\n"
)
if self.model in ['sv', 'icarus']:
# Verilog simulation options here
_=rtl_iofile(self, name='A', dir='in', iotype='sample', ionames=['A'], datatype='sint') # IO file for input A
f=rtl_iofile(self, name='Z', dir='out', iotype='sample', ionames=['Z'], datatype='sint')
# This is to avoid sampling time confusion with Icarus
if self.lang == 'sv':
f.rtl_io_sync='@(negedge clock)'
elif self.lang == 'vhdl':
f.rtl_io_sync='falling_edge(clock)'
self.rtlparameters=dict([ ('g_Rs',('real',self.Rs)),]) # Defines the sample rate
self.run_rtl()
self.IOS.Members['Z'].Data=self.IOS.Members['Z'].Data[:,0].astype(int).reshape(-1,1)
elif self.model=='vhdl' or self.model == 'ghdl':
# VHDL simulation options here
_=rtl_iofile(self, name='A', dir='in', iotype='sample', ionames=['A']) # IO file for input A
f=rtl_iofile(self, name='Z', dir='out', iotype='sample', ionames=['Z'], datatype='int')
if self.lang == 'sv':
f.rtl_io_sync='@(negedge clock)'
elif self.lang == 'vhdl':
f.rtl_io_sync='falling_edge(clock)'
self.rtlparameters=dict([ ('g_Rs',('real',self.Rs)),]) # Defines the sample rate
self.run_rtl()
self.IOS.Members['Z'].Data=self.IOS.Members['Z'].Data.astype(int).reshape(-1,1)
elif self.model in ['eldo','spectre','ngspice']:
# Creating a clock signal, which is used for testing the sample output features
_=spice_iofile(self, name='CLK', dir='in', iotype='sample', ionames='CLK', rs=2*self.Rs, \
vhi=self.vdd, trise=1/(self.Rs*8), tfall=1/(self.Rs*8))
# Sample type input
_=spice_iofile(self, name='A', dir='in', iotype='sample', ionames='A', rs=self.Rs, \
vhi=self.vdd, trise=1/(self.Rs*4), tfall=1/(self.Rs*4))
# These are helper IOS for analog simulation
_=spice_iofile(self, name='Z_ANA', dir='out', iotype='event', sourcetype='V', ionames='Z')
# Sample type output
# Clock is used to sample the waveform in analog simulation
_=spice_iofile(self, name='Z', dir='out', iotype='sample', ionames='Z', trigger='CLK', \
vth=self.vdd/2,edgetype='rising',ioformat='dec')
# Saving the analog waveform of the input as well
_=spice_iofile(self, name='A_OUT', dir='out', iotype='event', sourcetype='V', ionames='A')
# For Extracting rising edges from the output waveform
_=spice_iofile(self, name='Z_RISE', dir='out', iotype='time', sourcetype='V', ionames='Z', \
edgetype='rising',vth=self.vdd/2)
## Extracting values of A and Z at falling edges of CLK in decimal format (integer, in this case 0 or 1)
## The clock signal can be any node voltage in the simulation
_=spice_iofile(self, name='A_DIG', dir='out', iotype='sample', ionames='A', trigger='CLK', \
vth=self.vdd/2,edgetype='rising',ioformat='dec')
# Multithreading, options and parameters
self.nproc = 2
self.spiceoptions = {
'eps': '1e-6'
}
self.spiceparameters = {
'exampleparam': '0'
}
# Defining library options
# Path to model libraries needs to be defined in TheSDK.config as
# either ELDOLIBFILE or SPECTRELIBFILE. In this case, no model libraries
# will be included (assuming these variables are not defined). The
# temperature will be set regardless.
self.spicecorner = {
'corner': 'top_tt',
'temp': 27,
}
# Example of defining supplies (not used here because the example inverter has no supplies)
_=spice_dcsource(self,name='supply',value=self.vdd,pos='VDD',neg='VSS',extract=True)
_=spice_dcsource(self,name='ground',value=0,pos='VSS',neg='0')
# Adding a resistor between VDD and VSS to demonstrate power consumption extraction
# This also demonstrates how to inject manual commands in to the testbench
if self.model=='spectre':
self.spicemisc.append('simulator lang=spice')
self.spicemisc.append('Rtest VDD VSS 2000')
if self.model=='spectre':
self.spicemisc.append('simulator lang=spectre')
# Plotting nodes for interactive waveform viewing.
# Spectre also supported, but without 'v()' specifiers.
# i.e. plotlist = ['A','Z']
if self.model == 'eldo':
plotlist = ['v(A)','v(Z)']
elif self.model == 'spectre':
plotlist = ['A','Z']
else:
plotlist = []
# Simulation command
_=spice_simcmd(self,sim='tran',plotlist=plotlist)
self.run_spice()
if self.par:
self.queue.put(self.IOS.Members)
[docs]
def define_io_conditions(self):
'''This overloads the method called by run_rtl method. It defines the read/write conditions for the files
'''
if self.lang == 'sv':
# Input A is read to verilog simulation after 'initdone' is set to 1 by controller
self.iofile_bundle.Members['A'].rtl_io_condition='initdone'
# Output is read to verilog simulation when all of the outputs are valid,
# and after 'initdone' is set to 1 by controller
self.iofile_bundle.Members['Z'].rtl_io_condition_append(cond='&& initdone')
elif self.lang == 'vhdl':
self.iofile_bundle.Members['A'].rtl_io_condition='(initdone = \'1\')'
# Output is read to verilog simulation when all of the outputs are valid,
# and after 'initdone' is set to 1 by controller
self.iofile_bundle.Members['Z'].rtl_io_condition_append(cond='and initdone = \'1\'')
if __name__=="__main__":
import argparse
import matplotlib.pyplot as plt
from inverter import *
from inverter.controller import controller as inverter_controller
from inverter.signal_source import signal_source
from inverter.signal_plotter import signal_plotter
import pdb
# Implement argument parser
parser = argparse.ArgumentParser(description='Parse selectors')
parser.add_argument('--show', dest='show', type=bool, nargs='?', const = True,
default=False,help='Show figures on screen')
args=parser.parse_args()
length=2**8
rs=100e6
lang='sv'
#Testbench vhdl
#lang='vhdl'
controller=inverter_controller(lang=lang)
controller.Rs=rs
#controller.reset()
#controller.step_time()
controller.start_datafeed()
#models=['py','sv','icarus', 'ghdl', 'vhdl','eldo','spectre', 'ngspice']
#By default, we set only open souce simulators
models=['py', 'icarus', 'ghdl', 'ngspice']
# Here we instantiate the signal source
duts=[]
plotters=[]
#Here we construct the 'testbench'
s_source=signal_source()
for model in models:
# Create an inverter
d=inverter()
duts.append(d)
d.model=model
if model == 'ghdl':
d.lang='vhdl'
else:
d.lang=lang
d.Rs=rs
#d.preserve_rtlfiles = True
# Enable debug messages
#d.DEBUG = True
# Run simulations in interactive modes to monitor progress/results
#d.interactive_spice=True
#d.interactive_rtl=True
# Preserve the IO files or simulator files for debugging purposes
#d.preserve_iofiles = True
#d.preserve_spicefiles = True
# Save the entity state after simulation
#d.save_state = True
#d.save_database = True
# Optionally load the state of the most recent simulation
#d.load_state = 'latest'
# This connects the input to the output of the signal source
d.IOS.Members['A']=s_source.IOS.Members['data']
# This connects the clock to the output of the signal source
d.IOS.Members['CLK']=s_source.IOS.Members['clk']
d.IOS.Members['control_write']=controller.IOS.Members['control_write']
## Add plotters
p=signal_plotter()
plotters.append(p)
p.plotmodel=d.model
p.plotvdd=d.vdd
p.Rs = rs
p.IOS.Members['A']=d.IOS.Members['A']
p.IOS.Members['Z']=d.IOS.Members['Z']
p.IOS.Members['A_OUT']=d.IOS.Members['A_OUT']
p.IOS.Members['A_DIG']=d.IOS.Members['A_DIG']
p.IOS.Members['Z_ANA']=d.IOS.Members['Z_ANA']
p.IOS.Members['Z_RISE']=d.IOS.Members['Z_RISE']
# Here we run the instances
s_source.run() # Creates the data to the output
for d in duts:
d.init()
d.run()
for p in plotters:
p.init()
p.run()
#This is here to keep the images visible
#For batch execution, you should comment the following line
if args.show:
input()
#This is to have exit status for succesfuulexecution
sys.exit(0)