Skip to content

Commit

Permalink
Merge pull request #35 from sgherbst/readme
Browse files Browse the repository at this point in the history
Add spline implementation of generic transfer function
  • Loading branch information
sgherbst committed Jul 28, 2021
2 parents bbe2b51 + 2466dce commit e38d5ec
Show file tree
Hide file tree
Showing 13 changed files with 163 additions and 34 deletions.
8 changes: 1 addition & 7 deletions msdsl/circuit.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
# warning message
print('######################################################')
print('# WARNING: The msdsl.circuit module is experimental! #')
print('######################################################')
print()

from msdsl.expr.signals import AnalogSignal
from msdsl.eqn.deriv import Deriv
from msdsl.eqn.cases import eqn_case
Expand Down Expand Up @@ -208,4 +202,4 @@ def compile_to_eqn_list(self):
for val in self.kcl.values():
retval.append(val == 0)

return retval
return retval
28 changes: 19 additions & 9 deletions msdsl/templates/lds.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
from tqdm import tqdm
from scipy.linalg import expm
from scipy.interpolate import interp1d
from scipy.signal import tf2ss

from msdsl import MixedSignalModel, RangeOf
from msdsl.interp.interp import calc_interp_w
from msdsl.interp.lds import SplineLDS
from msdsl.interp.ctle import calc_ctle_abcd
from msdsl.interp.ctle import calc_ctle_num_den

# consumes and produces splines for LDS behavior
# implicitly assumes time has been normalized so dtmax=1
Expand Down Expand Up @@ -227,14 +228,23 @@ def calc_ranges(A, B, C, D, in_range, dt, num_terms):
return state_ranges, out_range


class CTLEModel(LDSModel):
def __init__(self, fz, fp1, dtmax, fp2=None, gbw=None, **kwargs):
# calculate the state-space representation
fz = fz*dtmax
fp1 = fp1*dtmax
fp2 = fp2*dtmax if fp2 is not None else None
gbw = gbw*dtmax if gbw is not None else None
A, B, C, D = calc_ctle_abcd(fz=fz, fp1=fp1, fp2=fp2, gbw=gbw)
class TFModel(LDSModel):
def __init__(self, num, den, dtmax, **kwargs):
# rescale numerator and coefficients
num = [num[k]*((1.0/dtmax)**(len(num)-1-k)) for k in range(len(num))]
den = [den[k]*((1.0/dtmax)**(len(den)-1-k)) for k in range(len(den))]

# convert transfer function to ABCD
A, B, C, D = tf2ss(num=num, den=den)

# call the super constructor
super().__init__(A=A, B=B, C=C, D=D, **kwargs)


class CTLEModel(TFModel):
def __init__(self, fz, fp1, fp2=None, gbw=None, **kwargs):
# calculate the transfer function representation
num, den = calc_ctle_num_den(fz=fz, fp1=fp1, fp2=fp2, gbw=gbw)

# call the super constructor
super().__init__(num=num, den=den, **kwargs)
45 changes: 28 additions & 17 deletions msdsl/templates/saturation.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,43 @@
from msdsl import MixedSignalModel
from msdsl.interp.nonlin import calc_tanh_vsat, tanhsat

class SaturationModel(MixedSignalModel):
def __init__(self, compr=-1, units='dB', veval=1.0, in_='in_', out='out', domain=None, order=1,
numel=64, in_range=None, out_range=None, clk=None, rst=None, **kwargs):

class NonlinModel(MixedSignalModel):
def __init__(self, func, in_='in_', out='out', domain=None, order=1, numel=64,
in_range=None, out_range=None, clk=None, rst=None, **kwargs):
# call the super constructor
super().__init__(**kwargs)

# create IOs
self.add_analog_input(in_)
self.add_analog_output(out)

# save settings
self.in_range = in_range
self.out_range = out_range
self.func = func

# create and apply function
real_func = self.make_function(func, domain=domain, order=order, numel=numel, write_tables=False)
self.set_from_func(self.get_signal(out), real_func, self.get_signal(in_), func_mode='async')


class SaturationModel(NonlinModel):
def __init__(self, compr=-1, units='dB', veval=1.0, domain=None, in_range=None, out_range=None, **kwargs):
# set defaults
if domain is None:
domain = [-2*abs(veval), +2*abs(veval)]

# find vsat
self.vsat = calc_tanh_vsat(compr=compr, units=units)
# find and save vsat
vsat = calc_tanh_vsat(compr=compr, units=units)
def func(v):
return tanhsat(v, vsat)
self.vsat = vsat

# calculate the output range if needed
if out_range is None:
if in_range is not None:
out_range = (self.func(in_range[0]), self.func(in_range[1]))
self.out_range = out_range

# create IOs
self.add_analog_input(in_)
self.add_analog_output(out)
out_range = (func(in_range[0]), func(in_range[1]))

# create and apply function
real_func = self.make_function(self.func, domain=domain, order=order, numel=numel, write_tables=False)
self.set_from_func(self.get_signal(out), real_func, self.get_signal(in_), func_mode='async')

def func(self, v):
return tanhsat(v, self.vsat)
# call the super constructor
super().__init__(func=func, domain=domain, in_range=in_range, out_range=out_range, **kwargs)
12 changes: 12 additions & 0 deletions random/adcdac.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from msdsl import *
m = MixedSignalModel('model')
vref = 1.2
# DAC
d_in = m.add_digital_input('d_in', width=8)
a_out = m.add_analog_output('a_out')
m.set_this_cycle(a_out, vref*(d_in/256))
# ADC
a_in = m.add_analog_input('a_in')
d_out = m.add_digital_output('d_out', width=9, signed=True)
m.set_this_cycle(d_out, to_sint((a_in/vref)*256, width=9))
m.compile_and_print(VerilogGenerator())
26 changes: 26 additions & 0 deletions random/buck.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from msdsl import *
# declare I/O
m = MixedSignalModel('buck', dt=0.1e-6)
sw = m.add_digital_input('sw')
v_in = m.add_analog_input('v_in')
v_out = m.add_analog_output('v_out')
# create circuit
c = m.make_circuit()
gnd = c.make_ground()
# input
c.voltage('net_v_in', gnd, v_in)
# transistor + diode
c.switch('net_v_in', 'net_v_sw', sw, r_on=1.0, r_off=10e3)
c.diode(gnd, 'net_v_sw', r_on=1.0, r_off=10e3)
# snubber
c.capacitor('net_v_sw', 'net_v_x', 100e-12, voltage_range=100.0)
c.resistor('net_v_x', gnd, 300)
# inductor + capacitor
c.inductor('net_v_sw', 'net_v_out', 2.2e-6, current_range=20.0)
c.capacitor('net_v_out', gnd, 10e-6, voltage_range=10.0)
# load
c.resistor('net_v_out', gnd, 5.5)
# assign outputs
c.add_eqns(v_out == AnalogSignal('net_v_out'))
# print output
m.compile_and_print(VerilogGenerator())
9 changes: 9 additions & 0 deletions random/func3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import numpy as np
from msdsl import *
m = MixedSignalModel('rc')
dt = m.add_analog_input('dt')
alpha = m.add_analog_output('alpha')
func = lambda dt: np.exp(-dt)
f = m.make_function(func, domain=[0, 10], numel=512, order=1)
m.set_from_sync_func(alpha, f, dt)
m.compile_and_print(VerilogGenerator())
5 changes: 5 additions & 0 deletions random/noise1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from msdsl import *
m = MixedSignalModel('model')
y = m.add_analog_output('y')
m.set_this_cycle(y, m.uniform_signal())
m.compile_and_print(VerilogGenerator())
8 changes: 8 additions & 0 deletions random/noise2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from msdsl import *
from scipy.stats import truncnorm
m = MixedSignalModel('model')
y = m.add_analog_output('y')
inv_cdf = lambda x: truncnorm.ppf(x, -8, +8)
inv_cdf_func = m.make_function(inv_cdf, domain=[0.0, 1.0])
m.set_this_cycle(y, m.arbitrary_noise(inv_cdf_func))
m.compile_and_print(VerilogGenerator())
12 changes: 12 additions & 0 deletions random/rccirc2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from msdsl import *
r, c = 1e3, 1e-9
m = MixedSignalModel('rc', dt=0.1e-6)
x = m.add_analog_input('x')
y = m.add_analog_output('y')
circ = m.make_circuit()
gnd = circ.make_ground()
circ.capacitor('net_y', gnd, c, voltage_range=RangeOf(x))
circ.resistor('net_x', 'net_y', r)
circ.voltage('net_x', gnd, x)
circ.add_eqns(AnalogSignal('net_y') == y)
m.compile_and_print(VerilogGenerator())
18 changes: 18 additions & 0 deletions random/rccirc3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from msdsl import *
r, c = 1e3, 1e-9
m = MixedSignalModel('rc', dt=0.1e-6)
vin = m.add_analog_input('vin')
iin = m.add_analog_output('iin') # note: output
vout = m.add_analog_output('vout')
iout = m.add_analog_input('iout') # note: input
circ = m.make_circuit()
gnd = circ.make_ground()
circ.capacitor('net_vout', gnd, c, voltage_range=RangeOf(vin))
circ.resistor('net_vin', 'net_vout', r)
c_iin = circ.voltage('net_vin', gnd, vin)
circ.current('net_vout', gnd, iout)
circ.add_eqns(
iin == -c_iin,
vout == AnalogSignal('net_vout')
)
m.compile_and_print(VerilogGenerator())
9 changes: 9 additions & 0 deletions random/swrc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from msdsl import *
r0, r1, c = 1234, 2345, 1e-9
m = MixedSignalModel('rc', dt=0.1e-6)
u = m.add_analog_input('u')
k = m.add_digital_input('k')
x = m.add_analog_output('x')
g = eqn_case([1/r0, 1/r1], [k])
m.add_eqn_sys([c*Deriv(x) == (u-x)*g])
m.compile_and_print(VerilogGenerator())
15 changes: 15 additions & 0 deletions random/swrc2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from msdsl import *
r00, r01, r10, r11, c = 123, 234, 345, 456, 1e-9
m = MixedSignalModel('rc', dt=0.1e-6)
u = m.add_analog_input('u')
s0 = m.add_digital_input('s0')
s1 = m.add_digital_input('s1')
x = m.add_analog_output('x')
g0 = eqn_case([1/r00, 1/r01], [s0])
g1 = eqn_case([1/r10, 1/r11], [s1])
v = AnalogSignal('v')
m.add_eqn_sys([
(u - v) * g0 == (v - x) * g1,
(v - x) * g1 == c * Deriv(x)
])
m.compile_and_print(VerilogGenerator())
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from setuptools import setup, find_packages

name = 'msdsl'
version = '0.3.7'
version = '0.3.8'

DESCRIPTION = '''\
Library for generating synthesizable mixed-signal models for FPGA emulation\
Expand Down

0 comments on commit e38d5ec

Please sign in to comment.