Automatic Simulation with Ocean Script

This articles covers:

  • The SKILL language basics
  • Use of Ocean script for complicated parameter sweep
  • Use of Python for parallel simulation

Basic

Running Ocean Script

To use Ocean interactively

  1. In a UNIX Shell, type in ocean
  2. In the CIW, type in Ocean commands directly

To run a written Ocean script

  1. In the interactive mode, type in load( "oceanScript.ocn" )
  2. In a UNIX Shell, type in ocean < oceanScript.ocn >> oceanLog.log

Ocean Use Model

It would be the best to copy the auto-generated "netlist" folder to avoid file missing. For successful Ocean simulation, the following netlist files are indispensable:

  1. netlist

    1
    2
    3
    4
    5
    6
    // attenuator
    PORT0 (INPUT 0) port r=50 num=1 type=sine
    PORT1 (OUTPUT 0) port r=50 num=2 type=sine
    R0 (INPUT OUTPUT) resistor r=rt
    R1 (INPUT 0) resistor r=rb
    R2 (OUTPUT 0) resistor r=rb
  2. netlistHeader

    1
    2
    simulator lang=spectre
    global 0
  3. netlistFooter (empty file)

Besides, it is highly likely that map files are required for result browsing. Otherwise, a error "In Save statement: None of the nets/terminals got successfully mapped." may occur.

The Ocean script is used to control the simulation. To do this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
; set the simulation engine
simulator( 'spectre )

; set the netlist path
design( "./netlist" )

; set the model files according to the technology
;modelFile(
; '("./model.scs" "tt")
; '("./model.scs" "tt_tf")
;)

; set the analysis
analysis(
'sp
?ports list("/PORT0" "/PORT1")
?start "0G" ?stop "100G" ?step "0.1G"
)

; set the design variable
desVar( "rb" 869.5 )
desVar( "rt" 5.76 )

; set the options
envOption(
'analysisOrder list("sp")
)

; set the parameters to be saved
save( 'v "/OUT_N" "/OUT_P" ) ; voltage
save( 'i "/PORT0/MINUS" "/PORT0/PLUS" "/PORT1/MINUS" "/PORT1/PLUS" ) ; current

; set the simulation temperature
temp( 27 )

; set the result directory
resultsDir( "./resultDir" )

; run the simulation
run()

; plot the graph
S21 = db(spm('sp 2 1))
plot( S21 ?expr '( "S21" ) )

; save the data
ocnPrint( S21 ?output "S21.csv" )

Browse Results

After the simulation, the results can be browsed independently.

1
2
3
4
openResults( "./result" )
selectResults( 'sp )
S21 = db(spm('sp 2 1))
ocnPrint( S21 ?output "S21.csv" )

Types of Simulation

DC

In the Ocean script

1
2
3
4
5
6
; specify the analysis
analysis('dc ?saveOppoint t )

; browse the result
IDC("/V0/PLUS")
VDC("/OUT_P")

TRAN

In the netlist, specify the ports

1
2
3
4
5
6
// piecewise-linear voltage source
// for the wave parameter, write down time-value pair sequentially
V0 (INPUT 0) vsource type=pwl wave=[ 0 0 10p 1 ]

// sin voltage source
V0 (net032 net02) vsource mag=1 type=sine

In the Ocean script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
; specify the analysis
analysis('tran ?stop "1n" ?errpreset "moderate" ?maxstep "1p" )

; browse the result
vtime('tran "/OUT_P")
itime('tran "/PORT1/PLUS")

; clip part of the X axis
clip( vtime('tran "/OUT_P") 0.5n 1n )

; perform Fourier transformation to get the spectrum
; signal, start time, end time, number of points
; freqMax = 1 / timeInterval / 2 (Nyquist sampling theorem)
; freqResolution = 1 / timeDuration
dft( vtime('tran "/OUT_P") 0.5n 1n 1000 )

AC

In the netlist, specify the ports

1
2
PORT0 (INPUT 0) port r=50 num=1 mag=1
PORT1 (OUTPUT 0) port r=50 num=2 type=sine

In the Ocean script

1
2
3
4
5
6
; specify the analysis
analysis('ac ?start "0G" ?stop "50G" ?step "1G" )

; browse the result
vfreq('ac "/OUT_P")
ifreq('ac "/PORT1/PLUS")

SP

In the netlist, specify the ports

1
2
PORT0 (INPUT 0) port r=50 num=1 type=sine
PORT1 (OUTPUT 0) port r=50 num=2 type=sine

In the Ocean script

1
2
3
4
5
6
7
8
9
10
11
; specify the analysis
analysis(
'sp
?ports list("/PORT0" "/PORT1")
?start "0G" ?stop "100G" ?step "0.1G"
)

; browse the result
; or S21 = db(sp(2 2 ?result "sp"))
S21 = db(spm('sp 2 1))
plot( S21 ?expr '( "S21" ) )

HB

In the netlist, specify the ports

1
2
3
PORT0 (INPUT 0) port r=50 num=1 type=sine freq=fin dbm=pin \
fundname="RFin"
PORT1 (OUTPUT 0) port r=50 num=2 type=sine type=dc

In the Ocean Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
; specify the analysis
analysis('hb ?HBTranMode "No" ?tstab "0" ?saveinit ""
?autoharms "" ?autotstab "" ?oversample list("1") ?funds list("RFin")
?maxharms list("3") ?param "pin" ?start "-30" ?stop "4"
?sweepStep "2" )

; save the current
save( 'i "/PORT1/MINUS" "/PORT1/PLUS" )
; save the voltage
save( 'v "/OUT_N" "/OUT_P" )

; browse the result

; obtain current at a specified harmonic
; analysis name, terminal, harmonic list
ih('hb "/V0/PLUS" '(0))
; for rms value, use
; note that for 0 harmonic, only the peak value is correct
rms( ih('hb "/V0/PLUS" '(0)) )

; similarly, for voltage harmonics, use vh
vh('hb "/OUT_P" '(0))

; obtain current waveform at all power levels
itime('hb "/V0/PLUS")
; use value() to filter results at specified levels
; waveform, sweep variable, value
value( itime('hb "/V0/PLUS") "pin" '(-30 -10) )
; similarly, for voltage waveform, use vtime

; obtain current spectrum
ih('hb "/V0/PLUS")
; use value() to filter results at specified levels
value(ih('hb "/V0/PLUS") '"pin" '-30)
; similarly, for voltage spectrum, use vh\

; obtain power at specified harmonic
; analysis name, voltage +, voltage -, branch name 1, branch name 2, harmonic list
; usually, branch name 2 is set to be 0
pvi('hb "/OUT_P" "/OUT_N" "/PORT1/PLUS" 0 '(1))
; use dbm() to obtain power in dbm
dbm( pvi('hb "/OUT_P" "/OUT_N" "/PORT1/PLUS" 0 '(1)) )

; to calculate power gain
; it is simply output power divided by input power, turned into db
db10((pvi('hb "/OUT_P" "/OUT_N" "/PORT1/PLUS" 0 '(1)) / (- pvi('hb "/IN_P" "/IN_N" "/PORT0/PLUS" 0 '1))))

; to calculate compression point
compressionVRI((v("/OUT_P" ?result "hb_fd") - v("/OUT_N" ?result "hb_fd")) '1 ?rport resultParam("PORT1:r" ?result "hb_fd") ?gcomp 1 ?measure "Output")

The SKILL® Language

Skill is a programming language integrated into the Virtuoso environment to extend its capabilities. Its grammar is similar to Lisp, with supplemented useful features.

Operators

1
2
3
4
5
6
7
8
9
10
+	;plus
- ;minus
* ;multiply
/ ;divide
** ;exponentiation
== ;equal
!= ;nequal
= ;assignment
<, <=, >, >=, ==, != ;relation operator
!, &&, || ;logic operators

Syntax

1
2
3
4
5
6
7
8
\	;Escape for special characters
( ) ;Grouping of list elements, function calls
[ ] ;Array index, super right bracket
' ;Specifies a symbol (quoting the expression prevents its evaluation)
"" ;String delimiter
, ;Optional delimiter between list elements (different from Lisp)
; ;Line-style comment character
? ;If first character, implies keyword parameter

Calling a function

1
2
3
4
plus(2,3)		;C style
(plus 2 3) ;Lisp style
plus(2 3) ;hybrid style
(plus,2,3) ;hybrid style

Defining a variable

1
2
a = 2		;C style
(setf a 2) ;Lisp style

Defining a list

1
2
3
4
5
6
7
8
9
a = '(3 3.14 "abc")			;use a ' to avoid evaluation
a = (list 3 3.14 "abc")
(cons a a) ;concatenate lists
(car a) ;obtain the first element
(cdr a) ;obtain the rest elements (without the first)
(nth 2 a) ;access data at the specified index, index starts at 1
(length a) ;count the elements
aCoordinate = 300:400 ;equal to '( 300 400 )
bBox = list( 300:400 500:450 ) ;equal to '(( 300 400 ) ( 500 450 ))

Declaring a function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
procedure(	add( x y )
x+y
)

; define local variable with let() function
procedure( square( x )
let(( n )
n=2
x**2
)
)

; use defun, note that defun accept 3 arguments
; there is a space between the function name and the argument list
defun( add ( x y )
x+y
)

Loop and branch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
;if
if(-1<0 print("a<0"))
if(-1<0 print("a<0") print("a>=0"))
if(-1<0
then
print("a<0")
else
print("a>=0")
)

;case
case( "c"
( "a" print("apple") )
( "b" print("banana") )
( "c" print("car") )
)

;for
for( i 1 5 printf( "%d" i ) ) ;including 1 and 5

a = list(1,2,3,4,5)
foreach( i a printf( "%d" i ) )

;while
i = 1
while(i<6
printf( "%d" i )
i++
)

Datatype

1
2
3
4
5
nil		;atom, ogical false or empty list
t ;stom, logical true
5 ;integer
5.3 ;floating point number
"abc" ;string

Useful Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
; calculate stability factor mu
((1 - abs((aaSP(1 1)**2))) / (abs((aaSP(1 2) * aaSP(2 1))) + abs((aaSP(2 2) - (conjugate(aaSP(1 1)) * ((aaSP(1 1) * aaSP(2 2)) - (aaSP(1 2) * aaSP(2 1))))))))

; calculate Vout vs. Pout
waveVsWave(?y vh('hb "/PDET_OUT" '(0)) ?x dbm(pvr('hb "/OUT_P" "/OUT_N" 50.0 '(1))) ?xName "Pout" ?xUnits "dBm" ?yName "Vout" ?yUnits "V")

; calculate equivalent capacitance
let( ( (k1 1) (k2 1) ) imag(ypm('sp k1 k2)) / 2 / pi / xval(imag(ypm('sp k1 k2))) )

; calculate equivalent inductance
let( ( (k1 1) (k2 1) ) imag(zpm('sp k1 k2)) / 2 / pi / xval(imag(zpm('sp k1 k2))) )

; calculate Gmax
gmax(spm('sp 1 1) spm('sp 1 2) spm('sp 2 1) spm('sp 2 2))

Multi-Thread Simulation

Spectre® engine itself does not support multi-thread simulation. To leverage the potential of multi-core processors, we can perform several Spectre simulation at the same time manually or automatically. One method to implement automatic parallel simulation is to use Threading module in Python to open up several threads. To achieve this

Preparation

The files should be arranged as follows

1
2
3
4
5
6
7
- autoOceanSim.py		# Python multi-thread control
- result # A folder for the simulation results
- ocean-script
- oceanScript.ocn # modified Ocean script
- sample-netlist
- netlist # netlist folder generated by spectre
- working-directory # working directory

In the Ocean script, several places have to be modified

1
2
3
4
5
6
7
8
9
10
11
; <!--DIR--> represents the working directory for specific project
design( "<!--DIR-->/netlist/netlist" ) ; specify the netlist folder
resultsDir( "<!--DIR-->/result" ) ; specify the raw results

; specify the parameters to be swept
desVar( "fin" <!--FREQ-->*1e9 )
desVar( "pin" <!--PIN--> )

; specify the folder for selected results
; <!--RNAME--> represents the "result" folder in the top level
ocnPrint( ?output "<!--RNAME-->" dbm(pvi('hb "/OUT_P" "/OUT_N" "/PORT1/PLUS" 0 '(1))) )

Source Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import os
import datetime
import subprocess
import shutil
import threading
import time
import signal

oceanDirectory = os.path.join(os.getcwd(), 'ocean-script')
workingDirectory = os.path.join(os.getcwd(), 'working-directory')
sampleNetlist = os.path.join(os.getcwd(), 'sample-netlist')
resultDirectory = os.path.join(os.getcwd(), 'result')

class commandWrapper(threading.Thread):
def __init__(self, name, command, timeOut, maxTry):
threading.Thread.__init__(self)
self.name = name
self.command = command
self.timeOut = timeOut
self.maxTry = maxTry
def run(self):
run = 0
while run < self.maxTry:
try:
print "Start(%d) %s" % (run+1, self.name)
p = subprocess.Popen(self.command, preexec_fn=os.setsid, shell=True)
p.wait(self.timeOut)
except subprocess.TimeoutExpired:
print 'Timeout(%d) %s' % (run+1, self.name)
p.terminate()
p.wait()
try:
os.killpg(p.pid, signal.SIGKILL)
except Exception as error:
print error
run += 1
continue
else:
break
print 'Finish %s' % self.name

class simuController(object):
def __init__(self, oceanScriptName, maxThread=3, timeOut=1800, maxTry=3):
self.oceanScriptName = oceanScriptName
self.__readOriginalOcean()
self.task = []
self.maxThread = maxThread
self.timeOut = timeOut
self.maxTry = maxTry
self.threads = []
self.closedThread = []

# place cds.log file in the current directory to avoid collision
os.environ['CDS_LOG_PATH'] = os.getcwd()
# name cds.log file with pid value
os.environ['CDS_LOG_VERSION'] = 'pid'

def __readOriginalOcean(self):
with open(os.path.join(oceanDirectory, self.oceanScriptName), 'r') as f:
self.rawData = f.read()

def __preProcessOcean(self, conversionTable, currentDir, resultName):
tmpData = self.rawData
for [x, y] in conversionTable:
tmpData = tmpData.replace(x, y)
tmpData = tmpData.replace('<!--DIR-->', currentDir)
tmpData = tmpData.replace('<!--RNAME-->', os.path.join(resultDirectory, resultName+'.csv'))
return tmpData

def prepareSimulation(self, conversionTable, resultName):
folderName = datetime.datetime.now().strftime('%Y%m%d-%H%M%S') + '_' + resultName
currentDir = os.path.join(workingDirectory, folderName)
os.mkdir(currentDir)
tmpData = self.__preProcessOcean(conversionTable, currentDir, resultName)
# write the ocean script
with open(os.path.join(currentDir, 'oceanScript.ocn'), 'w') as f:
f.write(tmpData)
# copy the netlist folder
shutil.copytree(os.path.join(sampleNetlist, 'netlist'),\
os.path.join(currentDir, 'netlist'))
command = 'ocean < %s >> %s' % (os.path.join(currentDir, 'oceanScript.ocn'),\
os.path.join(currentDir, 'ocean.log'))
self.task.append([resultName, command])

def runSimulation(self):
for [name, command] in self.task:
# check if alive thread number exceeds the maximum
while (threading.activeCount() - 1) >= self.maxThread:
# if everything's ok, wait for 10 seconds and check again
time.sleep(10)
newSimulation = commandWrapper(name, command, self.timeOut, self.maxTry)
newSimulation.daemon = True
newSimulation.start()
self.threads.append(newSimulation)

# wait for all the threads to terminate
for t in self.threads:
t.join()

print "All simulations have been finished!"

if __name__ == '__main__':
sim = simuController(oceanScriptName='oceanScript.ocn', maxThread=5, timeOut=2400, maxTry=3)
# add the points to be simulated
for pin in [0]:
for freq in [29]:
sim.prepareSimulation([['<!--PIN-->', str(pin)],\
['<!--FREQ-->', str(freq)]],\
'Loadpull_freq_%.1fGHz_pin_%.1fdBm' % (freq, pin))
# start the simulation
sim.runSimulation()

Reference

  1. ANSI Common Lisp 中文版

SKILL® and Spectre® are the trademarks of the Cadence Design Systems, Inc.

0%