├── .gitattributes ├── src ├── qtest │ ├── ns_init.ils │ ├── init.ils │ ├── sym_init.ils │ ├── core.ils │ ├── test_assertions.ils │ └── assertions.ils ├── ns_init.ils ├── pcell │ ├── init.ils │ ├── generic.ils │ ├── sym_init.ils │ └── PcellParam.ils ├── circuits │ ├── init.ils │ ├── unit_conversion.ils │ ├── test_unit_conversion.ils │ ├── sym_init.ils │ └── passives.ils ├── geometry │ ├── init.ils │ ├── grid.ils │ ├── trig.ils │ ├── line2d.ils │ ├── sym_init.ils │ ├── point2d.ils │ └── shapes.ils ├── design_environment │ ├── ade.ils │ ├── init.ils │ ├── layout_gui.ils │ ├── bbox.ils │ ├── sym_init.ils │ ├── layout.ils │ ├── schematic_gui.ils │ ├── schematic_settings.ils │ └── schematic.ils ├── std │ ├── init.ils │ ├── test_string.ils │ ├── string.ils │ ├── test_classes.ils │ ├── test_lists.ils │ ├── classes.ils │ ├── sym_init.ils │ ├── test_functional.ils │ ├── test_math.ils │ ├── math.ils │ ├── functional.ils │ ├── fileio.ils │ └── list.ils ├── ocean │ ├── init.ils │ ├── shoot_through.ils │ ├── stability_calculations.ils │ ├── tNetwork.ils │ ├── transistor.ils │ ├── sym_init.ils │ ├── oceanHelpers.ils │ ├── waveforms.ils │ ├── ampHB.ils │ ├── loadPullResults.ils │ ├── passiveOnePort.ils │ ├── ampTran.ils │ ├── outphasingSweep.ils │ ├── piNetwork.ils │ └── coupledCoil.ils ├── inductor_generator │ ├── oct_ind_pcell.ils │ ├── sym_init.ils │ ├── transformerB.ils │ ├── spiral_inductor.ils │ ├── ind_pcell.ils │ ├── init.ils │ ├── transformerA.ils │ ├── symm_ind_pcell.ils │ ├── shibata.ils │ ├── symmetric_inductor.ils │ ├── frlan_segment.ils │ └── frlan.ils └── init.ils ├── LICENSE └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ils linguist-language=lisp 2 | *.il linguist-language=lisp -------------------------------------------------------------------------------- /src/qtest/ns_init.ils: -------------------------------------------------------------------------------- 1 | (unless (findNamespace "qtest") 2 | (makeNamespace "qtest")) -------------------------------------------------------------------------------- /src/ns_init.ils: -------------------------------------------------------------------------------- 1 | ; Namespace initialisation 2 | (unless (findNamespace "qub") 3 | (makeNamespace "qub")) -------------------------------------------------------------------------------- /src/pcell/init.ils: -------------------------------------------------------------------------------- 1 | (qubCreateModule "pcell/" 2 | (modload "generic.ils") 3 | (modload "PcellParam.ils")) -------------------------------------------------------------------------------- /src/circuits/init.ils: -------------------------------------------------------------------------------- 1 | (qubCreateModule "circuits/" 2 | (modload "passives.ils") 3 | (modload "unit_conversion.ils")) -------------------------------------------------------------------------------- /src/qtest/init.ils: -------------------------------------------------------------------------------- 1 | ; Initialises the qtest module 2 | (qubCreateModule "qtest/" 3 | (modload "core.ils") 4 | (modload "assertions.ils")) 5 | -------------------------------------------------------------------------------- /src/geometry/init.ils: -------------------------------------------------------------------------------- 1 | (qubCreateModule "geometry/" 2 | (modload "point2d.ils") 3 | (modload "trig.ils") 4 | (modload "shapes.ils") 5 | (modload "line2d.ils") 6 | (modload "grid.ils")) -------------------------------------------------------------------------------- /src/design_environment/ade.ils: -------------------------------------------------------------------------------- 1 | ; Loads the ADE Direct Plot window for the current active window 2 | (defun qub::adeDirectPlot () 3 | (sevDirectPlot (sevSession (getCurrentWindow)) 'asiDirectPlotResultsMenuCB)) -------------------------------------------------------------------------------- /src/std/init.ils: -------------------------------------------------------------------------------- 1 | (qubCreateModule "std/" 2 | (modload "list.ils") 3 | (modload "functional.ils") 4 | (modload "classes.ils") 5 | (modload "math.ils") 6 | (modload "fileio.ils") 7 | (modload "string.ils")) 8 | -------------------------------------------------------------------------------- /src/pcell/generic.ils: -------------------------------------------------------------------------------- 1 | ; Defines the generic function for draw. 2 | ; This is placed in its own file as this should be loaded 3 | ; once as redefining a generic function will wipe the existing methods 4 | (defgeneric qub::draw (device @rest _args) t) -------------------------------------------------------------------------------- /src/std/test_string.ils: -------------------------------------------------------------------------------- 1 | (qtest::runSuites 2 | (qtest::TestSuite ((f qub::startsWith)) 3 | (qtest::TestCase normal_prefix 4 | (qtest::assertTrue (f "test_jjjj" "test"))) 5 | (qtest::TestCase no_prefix 6 | (qtest::assertNil (f "test_jjjj" "kmmm"))))) 7 | 8 | -------------------------------------------------------------------------------- /src/design_environment/init.ils: -------------------------------------------------------------------------------- 1 | (qubCreateModule "design_environment/" 2 | (modload "bbox.ils") 3 | (modload "schematic.ils") 4 | (modload "schematic_settings.ils") 5 | (modload "ade.ils") 6 | (modload "layout.ils") 7 | (modload "schematic_gui.ils") 8 | (modload "layout_gui.ils")) -------------------------------------------------------------------------------- /src/circuits/unit_conversion.ils: -------------------------------------------------------------------------------- 1 | ; Convert power in dBm to Watts 2 | (defun qub::dBmToWatts (x) 3 | (quotient (pow 10.0 (quotient x 10.0)) 1e3)) 4 | 5 | ; Sinusoidal Vpeak to Watts 6 | ; Default arg assumes 50 ohms 7 | (defun qub::vpeakToWatts (vp @key (r 50.0)) 8 | (quotient (pow vp 2) (times 2.0 r))) 9 | 10 | (defun qub::wattsToVpeak (p @key (r 50.0)) 11 | (sqrt (times 2.0 p r))) -------------------------------------------------------------------------------- /src/pcell/sym_init.ils: -------------------------------------------------------------------------------- 1 | ; generic.ils 2 | (addToExportList '(qub:::draw)) 3 | 4 | ; PcellParam.ils 5 | (addToExportList '(qub:::PcellParam 6 | qub:::setPcellParams 7 | qub:::defineParam 8 | qub:::getParamType 9 | qub:::getParamValue 10 | qub:::setParamValue 11 | qub:::isParam 12 | qub:::getPcellParams 13 | qub:::getPcellBoolParamVal)) -------------------------------------------------------------------------------- /src/circuits/test_unit_conversion.ils: -------------------------------------------------------------------------------- 1 | (qtest::runSuites 2 | (qtest::TestSuite ((fn qub::dBmToWatts)) 3 | (qtest::TestCase check_multiples_of_ten 4 | (qtest::assertEqual 5 | (qub::lcmp (fn x) for x in (list 30 20 10 0)) 6 | (list 1 0.1 1e-2 1e-3)))) 7 | (qtest::TestSuite ((fn qub::vpeakToWatts)) 8 | (qtest::TestCase check_1V 9 | (qtest::assertEqual (fn 1) 0.01))) 10 | (qtest::TestSuite ((fn qub::wattsToVpeak)) 11 | (qtest::TestCase check_0p01W 12 | (qtest::assertEqual (fn 0.01) 1.0)))) -------------------------------------------------------------------------------- /src/ocean/init.ils: -------------------------------------------------------------------------------- 1 | (qubCreateModule "ocean/" 2 | (modload "oceanHelpers.ils") 3 | (modload "waveforms.ils") 4 | (modload "transistor.ils") 5 | (modload "passiveOnePort.ils") 6 | (modload "coupledCoil.ils") 7 | (modload "loadPullResults.ils") 8 | (modload "piNetwork.ils") 9 | (modload "tNetwork.ils") 10 | (modload "ampHB.ils") 11 | (modload "outphasingSweep.ils") 12 | (modload "stability_calculations.ils") 13 | (modload "passiveOnePort.ils") 14 | (modload "ampTran.ils") 15 | (modload "shoot_through.ils")) 16 | -------------------------------------------------------------------------------- /src/std/string.ils: -------------------------------------------------------------------------------- 1 | ; Checks if string starts with a particular substring 2 | ; Returns: 3 | ; bool 4 | ; Args: 5 | ; str: string 6 | ; The string to check 7 | ; prefix: string 8 | ; The substring we're checking for within str 9 | (defun qub::startsWith (str prefix) 10 | (equal 0 (strncmp str prefix (strlen prefix)))) 11 | 12 | ; Converts an s-expression to a string 13 | ; Used for when some functions want a callback as a string but I 14 | ; don't want to lose syntax highlighting and other features 15 | ; in my editor 16 | (defmacro qub::sexpToStr (sexp) 17 | `(sprintf nil "%A" ',sexp)) -------------------------------------------------------------------------------- /src/circuits/sym_init.ils: -------------------------------------------------------------------------------- 1 | ; passives.ils 2 | (addToExportList '(qub:::seriesToParallel 3 | qub:::parallelResistors 4 | qub:::parallelToSeries 5 | qub:::XToC 6 | qub:::XToL 7 | qub:::BToC 8 | qub:::BToL 9 | qub:::GToR 10 | qub:::ZToY 11 | qub:::ZToSeriesRLC 12 | qub:::YToParallelRLC)) 13 | 14 | ; unit_conversion.ils 15 | (addToExportList '(qub:::dBmToWatts 16 | qub:::vpeakToWatts 17 | qub:::wattsToVpeak)) -------------------------------------------------------------------------------- /src/geometry/grid.ils: -------------------------------------------------------------------------------- 1 | ; Rounds values to the nearest 5nm assuming um coords used 2 | ; 0.124 will become 0.125, 0.901 to 0.900 etc 3 | (defun qub::roundValue5nm (x) 4 | (let ((x_round (round (times 1000.0 x))) 5 | (x_floor (times 10.0 (floor (times 100.0 x))))) 6 | (cond ((x_round < x_floor + 3) (quotient x_floor 1000.0)) 7 | ((x_round > x_floor + 6) (quotient (plus x_floor 10.0) 1000.0)) 8 | (t (quotient (plus x_floor 5.0) 1000.0))))) 9 | 10 | ; Apply qub::roundValue5nm to a two-element list 11 | (defun qub::roundCoord5nm (x) 12 | (list (qub::roundValue5nm (car x)) (qub::roundValue5nm (cadr x)))) -------------------------------------------------------------------------------- /src/std/test_classes.ils: -------------------------------------------------------------------------------- 1 | (qtest::runSuites 2 | (qtest::TestSuite ((f qub::checkType)) 3 | (qtest::TestCase wrong_type 4 | (qtest::assertRaises (f 3 'string)))) 5 | (qtest::TestSuite ((f qub::equal)) 6 | ; Integers 7 | (qtest::TestCase integers_true 8 | (qtest::assertTrue (f 2 2))) 9 | (qtest::TestCase integers_false 10 | (qtest::assertNil (f 2 3))) 11 | ; Strings 12 | (qtest::TestCase equal_string 13 | (qtest::assertTrue (f "hello" "hello"))) 14 | (qtest::TestCase nequal_string 15 | (qtest::assertNil (f "hello" "world")))) 16 | (qtest::TestSuite ((f qub::allEqual)) 17 | ; Integers 18 | (qtest::TestCase pos_ints 19 | (qtest::assertTrue (f 1 1 1 1 1 1))))) 20 | -------------------------------------------------------------------------------- /src/ocean/shoot_through.ils: -------------------------------------------------------------------------------- 1 | ; Calculates the shoot-through current in a CMOS inverter 2 | ; May not be totally correct as my method was arrived through intuition 3 | ; but it gives me a rough answer that's enough to guage how bad 4 | ; the shoot-through current really is. 5 | ; 6 | ; Args: 7 | ; p: waveform object 8 | ; The waveform of the current flowing through the PMOS(s) 9 | ; n: waveform object 10 | ; The waveform of the current flowing through the NMOS(s) 11 | ; Returns: 12 | ; A mutated version of I_N with the shoot-through current 13 | (defun qub::ocnShootThoughCurrent (I_P I_N) 14 | ; Take the max value after taking the min in the cases where the NMOS current 15 | ; is negative 16 | (max 0 (min I_P I_N))) -------------------------------------------------------------------------------- /src/ocean/stability_calculations.ils: -------------------------------------------------------------------------------- 1 | ; Get the delta term used to calculate the Rollett stability factor 2 | ; Assumes two-port S-parameters 3 | ; Args: 4 | ; s11: waveform 5 | ; s12: waveform 6 | ; s21: waveform 7 | ; s22: waveform 8 | (defun qub::rollett_delta (s11 s12 s21 s22) 9 | (difference (times s11 s22) (times s12 s21))) 10 | 11 | ; Return the mu stability factor 12 | ; Args: 13 | ; s11: waveform 14 | ; s12: waveform 15 | ; s21: waveform 16 | ; s22: waveform 17 | (defun qub::mu_stability_factor (s11 s12 s21 s22) 18 | (let ((D (qub::rollett_delta s11 s12 s21 s22)) 19 | (s11_c (conjugate s11)) 20 | (abs_s12_s21 (abs s12*s21)) 21 | (abs_s11 (abs s11))) 22 | (quotient (1 - abs_s11**2) 23 | ((abs s22 - D*s11_c) + abs_s12_s21)))) -------------------------------------------------------------------------------- /src/std/test_lists.ils: -------------------------------------------------------------------------------- 1 | (qtest::runSuites 2 | (qtest::TestSuite ((f qub::joinLists)) 3 | (qtest::TestCase join_three_lists 4 | (qtest::assertEqual (list 1 2 3 4 5 6) 5 | (f (list (list 1 2) (list 3 4) (list 5 6)))))) 6 | (qtest::TestSuite ((f qub::flattenList)) 7 | (qtest::TestCase normal_use 8 | (qtest::assertEqual (list 1 2 3 4 5 6 7) 9 | (f (list 1 2 (list 3 (list 4 (list 5 6) 7))))))) 10 | (qtest::TestSuite ((f qub::range)) 11 | (qtest::TestCase no_step 12 | (qtest::assertEqual (list 1 2 3) (f ?start 1 ?stop 4))) 13 | (qtest::TestCase step_two 14 | (qtest::assertEqual (list 1 3 5 7) (f ?start 1 ?stop 8 ?step 2))) 15 | (qtest::TestCase bas_input 16 | (qtest::assertRaises (f ?start 1 ?stop 0 ?step 1))))) 17 | 18 | -------------------------------------------------------------------------------- /src/design_environment/layout_gui.ils: -------------------------------------------------------------------------------- 1 | (defun qub::createLayoutMenu (args) 2 | (letseq ((actions 3 | (list (hiCreateMenuItem 4 | ?name 'QubSubtractShapesButton1 5 | ?itemText "Subtract Shapes" 6 | ?callback "(qub::subtractSmallerShapes (geGetEditCellView) (geGetSelSet))") 7 | (hiCreateMenuItem 8 | ?name 'QubSubtractShapesButton2 9 | ?itemText "Subtract Shapes (Remove old shapes)" 10 | ?callback "(qub::subtractSmallerShapes (geGetEditCellView) (geGetSelSet) ?delete_large_shape t ?delete_small_shapes t)"))) 11 | (pulldownMenu (hiCreatePulldownMenu 'qub_layout_menu 12 | "QUB" 13 | actions))) 14 | (list pulldownMenu))) -------------------------------------------------------------------------------- /src/geometry/trig.ils: -------------------------------------------------------------------------------- 1 | ;;;; All coordinates assume polar plot 2 | ;;;; e.g. 0 degrees is east, 90 degrees is north etc 3 | 4 | ; Restrict a number between 0 and 360 5 | ; Args: 6 | ; angle: float or int 7 | (defgeneric qub::fixAngle (angle)) 8 | 9 | (defmethod qub::fixAngle ((angle fixnum)) 10 | (modulo angle 360)) 11 | 12 | (defmethod qub::fixAngle ((angle flonum)) 13 | (if (negativep angle) 14 | (plus 360.0 (modf angle 360.0)) 15 | (modf angle 360.0))) 16 | 17 | ; Convert angle in degrees to radians 18 | ; Args: 19 | ; angle: float 20 | (defun qub::degToRad (angle) 21 | (times 22 | qub::m.PI 23 | (quotient 24 | angle 25 | 180.0))) 26 | 27 | ; Convert an angle from radians to degrees 28 | ; Args: 29 | ; angle: float 30 | (defun qub::radToDeg (angle) 31 | (times 180.0 (quotient angle qub::m.PI))) -------------------------------------------------------------------------------- /src/ocean/tNetwork.ils: -------------------------------------------------------------------------------- 1 | ; Plot the resistances and reactances of a passive network's T model 2 | ; Args: 3 | ; f0: float 4 | ; The frequency for the plot markers 5 | (defun qub::ocnTNetwork (f0) 6 | ; T Network representation using Z-Parameters. 7 | ; T network is assumed to be in the following form. 8 | ; 9 | ; O---Za-----Zb---O 10 | ; | 11 | ; Zc 12 | ; | 13 | ; O---------------O 14 | 15 | Za = (zpm "sp" 1 1) - (zpm "sp" 1 2) 16 | Zb = (zpm "sp" 2 2) - (zpm "sp" 1 2) 17 | Zc = (zpm "sp" 1 2) 18 | 19 | Ra = (real Za) 20 | Rb = (real Zb) 21 | Rc = (real Zc) 22 | 23 | Xa = (imag Za) 24 | Xb = (imag Zb) 25 | Xc = (imag Zc) 26 | 27 | winId = (newWindow) 28 | (plot Xa Xb Xc Ra Rb Rc ?expr (list "Xa" "Xb" "Xc" "Ra" "Rb" "Rc") ?yNumber (list 1 1 1 2 2 2)) 29 | (awvPlaceXMarker winId f0 ?subwindow 1)) -------------------------------------------------------------------------------- /src/qtest/sym_init.ils: -------------------------------------------------------------------------------- 1 | ; assertions.ils 2 | (addToExportList '(qtest:::assertEqual 3 | qtest:::assertNotEqual 4 | qtest:::assertTrue 5 | qtest:::assertNil 6 | qtest:::assertEq 7 | qtest:::assertNotEq 8 | qtest:::assertMember 9 | qtest:::assertNotMember 10 | qtest:::assertIsInstance 11 | qtest:::assertNotIsInstance 12 | qtest:::assertRaises 13 | qtest:::assertAlmostEqual 14 | qtest:::assertNotAlmostEqual)) 15 | 16 | ; core.ils 17 | (addToExportList '(qtest:::TestCase 18 | qtest:::runTests 19 | qtest:::TestSuite 20 | qtest:::runSuites 21 | qtest:::runAllTests)) -------------------------------------------------------------------------------- /src/std/classes.ils: -------------------------------------------------------------------------------- 1 | ; Throws an error if it doesn't receive the expected type 2 | (defun qub::checkType (instance expected_type) 3 | (unless (classp instance expected_type) 4 | (error "Expected a %s\n" (get_pname expected_type)))) 5 | 6 | ; Used to compare objects 7 | (defgeneric qub::equal (a b)) 8 | 9 | (defun qub::nequal (a b) 10 | (not (qub::equal a b))) 11 | 12 | ; Should only be in the format (defun fn (a b)) 13 | (defmacro qub::eqfn (class @rest body) 14 | `(defmethod qub::equal ((a ,class) (b ,class)) 15 | ,@body)) 16 | 17 | (qub::eqfn fixnum (equal a b)) 18 | (qub::eqfn flonum (equal a b)) 19 | 20 | (defmethod qub::equal ((a fixnum) (b flonum)) 21 | (equal a b)) 22 | 23 | (defmethod qub::equal ((a flonum) (b fixnum)) 24 | (equal a b)) 25 | 26 | (qub::eqfn list (equal a b)) 27 | (qub::eqfn string (equal a b)) 28 | 29 | (defun qub::allEqual (@rest values) 30 | (forall x (cdr values) (qub::equal (car values) x))) -------------------------------------------------------------------------------- /src/ocean/transistor.ils: -------------------------------------------------------------------------------- 1 | ; For plotting transistory things 2 | 3 | ; Return 0 if the net can't be plotted 4 | ; it's usually gnd 5 | ; s_ana = analysis type ('hb, 'pss, 'tran) 6 | (defun qub::getNetVoltageSafe (s_ana net_name) 7 | (let ((sig (vtime s_ana net_name))) 8 | (if (null sig) 0 sig))) 9 | 10 | (defun qub::getMosVx (s_ana inst term_name) 11 | (qub::getNetVoltageSafe s_ana (qub::getInstTermNet inst term_name))) 12 | 13 | (defun qub::getMosVxx (s_ana inst term1 term2) 14 | (difference 15 | (qub::getMosVx s_ana inst term1) 16 | (qub::getMosVx s_ana inst term2))) 17 | 18 | (defun qub::getMosVds (s_ana inst) 19 | (qub::getMosVxx s_ana inst "d" "s")) 20 | 21 | (defun qub::getMosVsd (s_ana inst) 22 | (qub::getMosVxx s_ana inst "s" "d")) 23 | 24 | (defun qub::getMosVgs (s_ana inst) 25 | (qub::getMosVxx s_ana inst "g" "s")) 26 | 27 | (defun qub::getMosVsg (s_ana inst) 28 | (qub::getMosVxx s_ana inst "s" "g")) -------------------------------------------------------------------------------- /src/design_environment/bbox.ils: -------------------------------------------------------------------------------- 1 | ; Get the width of a bounding box 2 | (defun qub::bboxWidth (bbox) 3 | (difference (rightEdge bbox) (leftEdge bbox))) 4 | 5 | ; Get the height of a bounding box 6 | (defun qub::bboxHeight (bbox) 7 | (difference (topEdge bbox) (bottomEdge bbox))) 8 | 9 | (defun qub::bboxArea (bbox) 10 | (times (qub::bboxHeight bbox) (qub::bboxWidth bbox))) 11 | 12 | ; Find what side a point is relative to a bbox 13 | (defun qub::bboxFindSide (bbox pt) 14 | (let ((x (xCoord pt)) 15 | (y (yCoord pt)) 16 | (top (topEdge bbox)) 17 | (left (leftEdge bbox)) 18 | (right (rightEdge bbox)) 19 | (bottom (bottomEdge bbox))) 20 | (if (and (lessp y top) 21 | (greaterp y bottom)) 22 | ; Has to be on left or right side 23 | (if (leqp x left) 24 | 'left 25 | 'right) 26 | ; Has to be above or below 27 | (if (leqp y bottom) 28 | 'bottom 29 | 'top)))) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 MatthewLoveQUB 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/geometry/line2d.ils: -------------------------------------------------------------------------------- 1 | ; Find the intersection point between two lines 2 | ; equation taken from here the Line-Line intersection wiki page 3 | (defun qub::findIntersectPoint (a1 a2 b1 b2) 4 | (let ((x1 (xCoord a1)) 5 | (x2 (xCoord a2)) 6 | (x3 (xCoord b1)) 7 | (x4 (xCoord b2)) 8 | (y1 (yCoord a1)) 9 | (y2 (yCoord a2)) 10 | (y3 (yCoord b1)) 11 | (y4 (yCoord b2))) 12 | (let ((x1y2 (x1 * y2)) 13 | (y1x2 (y1 * x2)) 14 | (x3y4 (x3 * y4)) 15 | (y3x4 (y3 * x4))) 16 | (let ((denominator (difference 17 | ((x1 - x2) * (y3 - y4)) 18 | ((y1 - y2) * (x3 - x4))))) 19 | (let ((px (quotient 20 | (difference 21 | ((x1y2 - y1x2) * (x3 - x4)) 22 | ((x1 - x2) * (x3y4 - y3x4))) 23 | denominator)) 24 | (py (quotient 25 | (difference 26 | ((x1y2 - y1x2) * (y3 - y4)) 27 | ((y1 - y2) * (x3y4 - y3x4))) 28 | denominator))) 29 | px:py))))) -------------------------------------------------------------------------------- /src/inductor_generator/oct_ind_pcell.ils: -------------------------------------------------------------------------------- 1 | (defclass qub::octInductorPCell (qub::inductorPCell) ()) 2 | 3 | (defmethod qub::draw ((device qub::octInductorPCell)) 4 | (qub::setParamValue device 'nsides 8) 5 | (callNextMethod)) 6 | 7 | (defmacro qub::createOctInductorPCell (@key (library nil) 8 | (defaultMetalLayer nil)) 9 | (letseq ((cellName "octSpiralInd") 10 | (cellType "layout") 11 | (cellObj (ddGetObj library cellName))) 12 | ; Delete the cell if it doesn't exist 13 | ; As re-running the macro doesn't seem to refresh the PCell instance 14 | (if cellObj 15 | (ddDeleteObj cellObj)) 16 | `(pcDefinePCell 17 | (list (ddGetObj ,library) ,cellName ,cellType) 18 | ((completeTurns "int" 2) 19 | (additionalSegments "int" 0) 20 | (width "float" 10.0) 21 | (spacing "float" 5.0) 22 | (apothem "float" 50.0) 23 | (metalLayer "string" ,defaultMetalLayer)) 24 | (let ((pcell (makeInstance 'qub::octInductorPCell))) 25 | (qub::setPcellParams pcell pcCellView) 26 | (qub::draw pcell))))) 27 | -------------------------------------------------------------------------------- /src/init.ils: -------------------------------------------------------------------------------- 1 | ; Loading some stdlib files to access file io functions 2 | (load "std/sym_init.ils") 3 | (load "std/fileio.ils") 4 | (load "std/list.ils") 5 | (load "std/string.ils") 6 | 7 | ; Run this file twice to fix all "unknown object" errors 8 | (defmacro qubCreateModule (folder @rest body) 9 | `(let ((modload (lambda (p) (load (strcat ,folder p))))) 10 | ,@body)) 11 | 12 | ; Have to load the symbols first to avoid namespace errors 13 | ; Make sure to set $QUB_CODE_REPO_DIR to this file's parent directory 14 | (qubCreateModule "" 15 | (if (null (getShellEnvVar "QUB_CODE_REPO_DIR")) 16 | (error "Env var QUB_CODE_REPO_DIR must be set to the repo parent dir")) 17 | (letseq ((dirFiles (qub::listFileRec (getShellEnvVar "QUB_CODE_REPO_DIR"))) 18 | (nsInitFiles (qub::findFiles dirFiles "ns_init.ils")) 19 | (symInitFiles (qub::findFiles dirFiles "sym_init.ils"))) 20 | (mapc load nsInitFiles) 21 | (mapc load symInitFiles)) 22 | (modload "std/init.ils") 23 | (modload "qtest/init.ils") 24 | (modload "pcell/init.ils") 25 | (modload "design_environment/init.ils") 26 | (modload "geometry/init.ils") 27 | (modload "inductor_generator/init.ils") 28 | (modload "circuits/init.ils") 29 | (modload "ocean/init.ils")) -------------------------------------------------------------------------------- /src/geometry/sym_init.ils: -------------------------------------------------------------------------------- 1 | ; point2d.ils 2 | (addToExportList '(qub:::topPoint 3 | qub:::bottomPoint 4 | qub:::leftPoint 5 | qub:::rightPoint 6 | qub:::movePoint 7 | qub:::mvPtX 8 | qub:::mvPtY 9 | qub:::higherp 10 | qub:::getPtAngle 11 | qub:::midPoint 12 | qub:::getAngleToPt 13 | qub:::moveAtAngle 14 | qub:::getDist 15 | qub:::getPoint 16 | qub:::rotatePoint)) 17 | 18 | ; trig.ils 19 | (addToExportList '(qub:::degToRad 20 | qub:::fixAngle 21 | qub:::radToDeg)) 22 | 23 | ; shapes.ils 24 | (addToExportList '(qub:::apothemToCircumradius 25 | qub:::internalAngle 26 | qub:::genOctagon 27 | qub:::genOctagons 28 | qub:::octagonsToCoil 29 | qub:::getPolyPoint)) 30 | 31 | ; line.ils 32 | (addToExportList '(qub:::findIntersectPoint)) 33 | 34 | ; grid.ils 35 | (addToExportList '(qub:::roundCoord5nm 36 | qub:::roundValue5nm)) -------------------------------------------------------------------------------- /src/std/sym_init.ils: -------------------------------------------------------------------------------- 1 | (unless (findNamespace "qub") 2 | (makeNamespace "qub")) 3 | 4 | ; classes.ils 5 | (addToExportList '(qub:::checkType 6 | qub:::equal 7 | qub:::nequal 8 | qub:::eqfn 9 | qub:::allEqual)) 10 | 11 | ; fileio.ils 12 | (addToExportList '(qub:::listFileRec 13 | qub:::filterUnwanted 14 | qub:::getFileName 15 | qub:::findFilesWithPrefix 16 | qub:::findFiles)) 17 | 18 | ; functional.ils 19 | (addToExportList '(qub:::foldl 20 | qub:::foldr 21 | qub:::sum 22 | qub:::lcmp 23 | qub:::takeN 24 | qub:::dropN)) 25 | 26 | ; list.ils 27 | (addToExportList '(qub:::joinLists 28 | qub:::flattenList 29 | qub:::range 30 | qub:::pushEnd 31 | qub:::lastAtom 32 | qub:::addListElems 33 | qub:::copyObjProperties)) 34 | 35 | ; math.ils 36 | (addToExportList '(qub:::m 37 | qub:::almostEqual 38 | qub:::notAlmostEqual 39 | qub:::mean)) 40 | 41 | ; string.ils 42 | (addToExportList '(qub:::startsWith 43 | qub:::sexpToStr)) -------------------------------------------------------------------------------- /src/std/test_functional.ils: -------------------------------------------------------------------------------- 1 | (qtest::runSuites 2 | ; qub::sum 3 | (qtest::TestSuite ((f qub::sum)) 4 | (qtest::TestCase integers 5 | (qtest::assertEqual 9 (f (list 4 5)))) 6 | (qtest::TestCase letters_fail 7 | (qtest::assertRaises (qub::sum (list 1 "s"))))) 8 | ; qub::foldl 9 | (qtest::TestSuite ((f qub::foldl)) 10 | (qtest::TestCase basic_sum_foldl 11 | (qtest::assertEqual 9 (f plus 0 (list 5 4)))) 12 | (qtest::TestCase join_letters_foldl 13 | (qtest::assertEqual "abc" (f strcat "" (list "a" "b" "c"))))) 14 | ; qub::foldl 15 | (qtest::TestSuite ((f qub::foldr)) 16 | (qtest::TestCase basic_sum_foldr 17 | (qtest::assertEqual 9 (f plus 0 (list 5 4)))) 18 | (qtest::TestCase join_letters_foldr 19 | (qtest::assertEqual "cba" (f strcat "" (list "a" "b" "c")))) 20 | (qtest::TestCase doesnt_mutate_arguments 21 | (let ((initval 0)) 22 | (qub::foldl plus initval (list 1 2 3 4)) 23 | (qtest::assertEqual 0 initval)))) 24 | ; Testing the list comprehension macro 25 | (qtest::TestSuite () 26 | (qtest::TestCase double_numbers 27 | (qtest::assertEqual (list 2 3) 28 | (qub::lcmp (add1 x) for x in (list 1 2)))) 29 | (qtest::TestCase filter_evens 30 | (qtest::assertEqual (list 1 3 5) 31 | (qub::lcmp x for x in (list 1 2 3 4 5) 32 | if (oddp x)))))) 33 | -------------------------------------------------------------------------------- /src/ocean/sym_init.ils: -------------------------------------------------------------------------------- 1 | ; oceanHelpers.ils 2 | (addToExportList '(qub:::simPreamble 3 | qub:::createSmith 4 | qub:::sToF)) 5 | 6 | ; coupledCoil.ocn 7 | (addToExportList '(qub:::ocnCoupledCoil)) 8 | 9 | ; loadPullResults.ils 10 | (addToExportList '(qub:::ocnLoadPull)) 11 | 12 | ; piNetwork.ils 13 | (addToExportList '(qub:::ocnPiNetwork)) 14 | 15 | ; tNetwork.ils 16 | (addToExportList '(qub:::ocnTNetwork)) 17 | 18 | ; ampHB.ils 19 | (addToExportList '(qub:::ocnAmpHb)) 20 | 21 | ; outphasingSweep.ils 22 | (addToExportList '(qub:::ocnOutphasingSweep)) 23 | 24 | ; transistor.ils 25 | (addToExportList '(qub:::getMosVds 26 | qub:::getMosVsd 27 | qub:::getMosVgs 28 | qub:::getMosVsg 29 | qub:::getNetVoltage 30 | qub:::getMosVxx 31 | qub:::getNetVoltageSafe 32 | qub:::getMosVx)) 33 | 34 | ;stability_calculations 35 | (addToExportList '(qub:::rollett_delta 36 | qub:::mu_stability_factor)) 37 | 38 | ; passiveOnePort.ils 39 | (addToExportList '(qub:::ocnPassiveOnePort)) 40 | 41 | ; ampTran.ils 42 | (addToExportList '(qub:::ocnAmpTran)) 43 | 44 | ; shoot_through.ils 45 | (addToExportList '(qub:::ocnShootThoughCurrent)) 46 | 47 | ; waveforms.ils 48 | (addToExportList '(qub:::copyWaveform 49 | qub:::mapWaveform)) -------------------------------------------------------------------------------- /src/ocean/oceanHelpers.ils: -------------------------------------------------------------------------------- 1 | (defun qub:::getSpectreSimFiles () 2 | (letseq ((spectreObj (asiGetTool 'spectre)) 3 | (rawFiles (asiGetEnvOptionVal spectreObj 'allDefinitionFiles))) 4 | (mapcar cadr rawFiles))) 5 | 6 | (defun qub:::getModelFiles () 7 | (letseq ((spectreObj (asiGetTool 'spectre)) 8 | (rawFiles (asiGetEnvOptionVal spectreObj 'modelFiles))) 9 | (setof x rawFiles (or (equal "top_tt" (cadr x)) 10 | (equal "pre_simu" (cadr x)))))) 11 | 12 | (defmacro qub::simPreamble (lib schem @key (view "schematic")) 13 | `(progn 14 | (simulator 'spectre) 15 | ; Turn off Spectre logging to speed up simulation time 16 | (envSetVal "asimenv.misc" "includeSimLogInOCEAN" 'boolean nil) 17 | (design ,lib ,schem ,view) 18 | ; Look for the preloaded model and definition files 19 | (apply 'modelFile (qub:::getModelFiles)) 20 | (apply 'definitionFile (qub:::getSpectreSimFiles)) 21 | (createNetlist ?recreateAll t) 22 | (resultsDir (sprintf nil "/user/p_outpa/mlove/%s/%s/" ,lib ,schem)))) 23 | 24 | ; Create a new window in the smith chart mode 25 | ; Args: 26 | ; smithTypeStr: string 27 | ; Either "impedance", "admittance", or "polar" 28 | (defun qub::createSmith (@optional (smithTypeStr "impedance")) 29 | (newWindow) 30 | (displayMode "smith") 31 | (smithType smithTypeStr)) 32 | 33 | ; Gets the frequency data from the results of an S-Parameter simulation 34 | (defun qub::sToF () 35 | (xval (getData "/s11" ?result "sp"))) -------------------------------------------------------------------------------- /src/std/test_math.ils: -------------------------------------------------------------------------------- 1 | (qtest::runSuites 2 | (qtest::TestSuite ((f qub::almostEqual)) 3 | ; Checking integers first 4 | (qtest::TestCase compare_zero_int 5 | (qtest::assertTrue (f 0 0))) 6 | (qtest::TestCase pos_int_eq 7 | (qtest::assertTrue (f 1 1))) 8 | (qtest::TestCase neg_int_eq 9 | (qtest::assertTrue (f -1 -1))) 10 | (qtest::TestCase pos_int_neq 11 | (qtest::assertNil (f 1 2))) 12 | (qtest::TestCase neg_int_neq 13 | (qtest::assertNil (f -1 -2))) 14 | ; Checking floats 15 | ; Checking absolute difference 16 | (qtest::TestCase cmp_zero_float 17 | (qtest::assertTrue (f 0.0 0.0))) 18 | (qtest::TestCase positive_floats_true 19 | (qtest::assertTrue (f 1 1.1 ?abs_tol 0.2))) 20 | (qtest::TestCase positive_floats_nil 21 | (qtest::assertNil (f 1 1.1 ?abs_tol 1u))) 22 | (qtest::TestCase small_difference_true 23 | (qtest::assertTrue (f 1 (plus 1 1n) ?abs_tol 1u))) 24 | (qtest::TestCase small_difference_nil 25 | (qtest::assertNil (f 1 (plus 1 1n) ?abs_tol 1p))) 26 | ; Checking relative difference 27 | (qtest::TestCase small_rel_diff_true 28 | (qtest::assertTrue (f 100 104 ?rel_tol 0.05))) 29 | (qtest::TestCase small_ref_diff_false 30 | (qtest::assertNil (f 100 106 ?rel_tol 0.05))) 31 | ; Checking for bad inputs 32 | (qtest::TestCase neg_abs_tol 33 | (qtest::assertRaises (f 0 0 ?abs_tol -1))) 34 | (qtest::TestCase neg_rel_tol 35 | (qtest::assertRaises (f 0 0 ?rel_tol -1))) 36 | (qtest::TestCase difference_types 37 | (qtest::assertRaises (f 0 "a"))))) 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/design_environment/sym_init.ils: -------------------------------------------------------------------------------- 1 | ; bbox.ils 2 | (addToExportList '(qub:::bboxWidth 3 | qub:::bboxHeight 4 | qub:::bboxArea 5 | qub:::bboxFindSide)) 6 | 7 | ; schematic.ils 8 | (addToExportList '(qub:::getInstTermNet 9 | qub:::getHierNetName 10 | qub:::getSchematicInstanceBBox 11 | qub:::makeTerminalStub 12 | qub:::addStubsToInstance 13 | qub:::addStubsToInstances 14 | qub:::createPinsFromInstance 15 | qub:::createInstancesPinsAndStubs 16 | qub:::createSchematicPinsAndStubs 17 | qub:::createSelectedInstancesPinsAndStubs 18 | qub:::createSelectedInstancesStubs 19 | qub:::generateSchPinName 20 | qub:::createInstancePins)) 21 | 22 | ; ade.ils 23 | (addToExportList '(qub:::adeDirectPlot)) 24 | 25 | ; schematic_gui.ils 26 | (addToExportList '(qub:::createLayoutMenu)) 27 | 28 | ; schematic_settings.ils 29 | (addToExportList '(qub:::findSettingsTextBlocks 30 | qub:::makeEmptySchematicSettings 31 | qub:::parseSchematicSettings 32 | qub:::parseSchematicSettingsLine 33 | qub:::addSettingsEntry)) 34 | 35 | ; layout_gui.ils 36 | (addToExportList '(qub:::createSchematicMenu)) 37 | 38 | ; layout.ils 39 | (addToExportList '(qub:::hidePaletteDuplicates 40 | qub:::limitPathWidth 41 | qub:::subtractSmallerShapes)) 42 | -------------------------------------------------------------------------------- /src/ocean/waveforms.ils: -------------------------------------------------------------------------------- 1 | ; Creates a new copy of a waveform object 2 | ; Args: 3 | ; wf: A waveform object 4 | ; Returns: 5 | ; A copy of the waveform object 6 | ; X and Y values are preserved, as are the units 7 | ; Nothing else is guaranteed 8 | (defun qub::copyWaveform (wf) 9 | (letseq ((oldXVals (drGetWaveformXVec wf)) 10 | (oldXType (drGetWaveformXType wf)) 11 | (oldXLength (drVectorLength oldXVals)) 12 | (oldYVals (drGetWaveformYVec wf)) 13 | (oldYType (drGetWaveformYType wf)) 14 | (oldYLength (drVectorLength oldYVals)) 15 | (newX (drCreateVec oldXType oldXLength)) 16 | (newY (drCreateVec oldYType oldYLength)) 17 | (cpVals (lambda (oldLen oldVec newVec) 18 | (for i 0 (sub1 oldLen) 19 | (drSetElem newVec i (drGetElem oldVec i)))))) 20 | ; Populate the new waveform 21 | (cpVals oldXLength oldXVals newX) 22 | (cpVals oldYLength oldYVals newY) 23 | (qub::copyObjProperties oldXVals newX) 24 | (qub::copyObjProperties oldYVals newY) 25 | (drCreateWaveform newX newY))) 26 | 27 | ; Map function fn to waveform wfm 28 | ; This is destructive, the original waveform will be modified 29 | ; Wrap wfm in qub::copyWaveform if you want to preserve the original waveform 30 | (defun qub::mapWaveform (fn wfm) 31 | (letseq ((y_vec (drGetWaveformYVec wfm)) 32 | (n_elements (drVectorLength y_vec)) 33 | (stop_idx (sub1 n_elements))) 34 | (for idx 0 stop_idx 35 | (let ((element (drGetElem y_vec idx))) 36 | (drSetElem y_vec idx (fn element)))) 37 | wfm)) -------------------------------------------------------------------------------- /src/inductor_generator/sym_init.ils: -------------------------------------------------------------------------------- 1 | ; oct_ind_pcell 2 | (addToExportList '(qub:::createOctInductorPCell 3 | qub:::octInductorPCell)) 4 | 5 | ; spiral_inductor.ils 6 | (addToExportList '(qub:::getSpiralPoints 7 | qub:::drawInductor)) 8 | 9 | 10 | ; symmetric_inductor.ils 11 | (addToExportList '(qub:::extendMiddle 12 | qub:::getBridgePoints 13 | qub:::invertSide 14 | qub:::getInterconnects 15 | qub:::sortTopPoint 16 | qub:::sortBottomPoint 17 | qub:::createSymInd 18 | qub:::getSymmetricSpiralPoints 19 | qub:::drawSymmetricInductor 20 | qub:::addPorts)) 21 | 22 | ; ind_pcell.ils 23 | (addToExportList '(qub:::createInductorPCell 24 | qub:::inductorPCell)) 25 | 26 | ; symm_ind_pcell.ils 27 | (addToExportList '(qub:::createSymmInductorPCell 28 | qub:::symmInductorPCell)) 29 | 30 | ; transformerA.ils 31 | (addToExportList '(qub:::createTransformerA 32 | qub:::transformerA)) 33 | 34 | ; transformerB.ils 35 | (addToExportList '(qub:::createTransformerB 36 | qub:::transformerB)) 37 | 38 | ; init.ils 39 | (addToExportList '(qub:::createInductorPCells)) 40 | 41 | ; frlan_segment.ils 42 | (addToExportList '(qub:::octagonsToFrlan 43 | qub:::frlanSegmentPCell 44 | qub:::createFrlanSegmentPCell)) 45 | 46 | ; frlan.ils 47 | (addToExportList '(qub:::frlanPCell 48 | qub:::createFrlanPCell 49 | qub:::createFrlanCdf 50 | qub:::createFrlanInnerBridge 51 | qub:::createFrlanInputConnector)) -------------------------------------------------------------------------------- /src/design_environment/layout.ils: -------------------------------------------------------------------------------- 1 | ; Hides palettes in the layout that end with a number in brackets e.g. (3) 2 | ; My Virtuoso instance has a bug where every palette member is duplicated 3 | ; when Virtuoso is loaded and this function removes them all 4 | (defun qub::hidePaletteDuplicates () 5 | (foreach setname (pteGetAllLayerSets) 6 | (if (rexMatchp ".*([0-9])$" setname) (pteSetLSEnable setname nil)))) 7 | 8 | ; Limit the path width to the maximum given in the techfile 9 | ; Args: 10 | ; techfile: database object 11 | ; The library techfile object 12 | ; Often retrieved using techGetTechFile 13 | ; width: float 14 | ; The width of the path 15 | ; layer: string 16 | ; The layer the path is being drawn on 17 | (defun qub::limitPathWidth (techfile width layer) 18 | (when techfile 19 | (let ((maxWidth (techGetSpacingRule techfile "maxWidth" layer))) 20 | (if maxWidth 21 | (min width maxWidth) 22 | width)))) 23 | 24 | (defun qub::subtractSmallerShapes (cv 25 | shapes 26 | @key 27 | (delete_large_shape nil) 28 | (delete_small_shapes nil)) 29 | (letseq ((sortedShapes (sort shapes 30 | (lambda (x y) (geqp (qub::bboxArea x->bBox) 31 | (qub::bboxArea y->bBox))))) 32 | (largestShape (car sortedShapes)) 33 | (subtractors (cdr sortedShapes)) 34 | (outputLayer (car largestShape->lpp))) 35 | (dbLayerAndNot cv outputLayer (list largestShape) subtractors) 36 | (when delete_small_shapes 37 | (foreach shape subtractors 38 | (dbDeleteObject shape))) 39 | (when delete_large_shape 40 | (dbDeleteObject largestShape)))) -------------------------------------------------------------------------------- /src/std/math.ils: -------------------------------------------------------------------------------- 1 | ; For mathematics 2 | ; Assigns a set of math constants to the symbol 3 | (defMathConstants 'qub::m) 4 | 5 | ; Check if two floats are almost equal 6 | ; Returns: 7 | ; bool 8 | ; Args: 9 | ; a: float 10 | ; b: float 11 | ; rel_tol: float 12 | ; The relative tolerance of the comparison 13 | ; 0.0 for 0% tolerance, 1.0 for 100% tolerance etc 14 | ; abs_tol: float 15 | ; The absolute numerical tolerance of the comparison 16 | (defgeneric qub::almostEqual (a b @key (rel_tol 0) (abs_tol 0))) 17 | (defmethod qub::almostEqual ((a number) 18 | (b number) 19 | @key (rel_tol 0) (abs_tol 0)) 20 | (when (or (negativep rel_tol) 21 | (negativep abs_tol)) 22 | (error "Can't have negative tolerance")) 23 | (cond ((equal a b) t) 24 | ((isInfinity (float a)) nil) 25 | ((isInfinity (float b)) nil) 26 | (t (let ((diff (abs (difference a b)))) 27 | (cond ((leqp diff (abs (times rel_tol b))) t) 28 | ((leqp diff (abs (times rel_tol a))) t) 29 | ((leqp diff abs_tol)) 30 | (t nil)))))) 31 | 32 | (defmethod qub::almostEqual ((a list) (b list) @key (rel_tol 0) (abs_tol 0)) 33 | ; check if each value is equal 34 | (if (nequal (length a) (length b)) 35 | nil 36 | (let ((ae (lambda (x y) 37 | (qub::almostEqual x y ?rel_tol rel_tol ?abs_tol abs_tol)))) 38 | (qub::foldl (lambda (acc x) (and acc x)) t (mapcar ae a b))))) 39 | 40 | ; Checks if two floats are not almost equal 41 | ; It just inverts the return value of qub::almostEqual 42 | (defun qub::notAlmostEqual (a b @key (rel_tol 0) (abs_tol 0)) 43 | (not (qub::almostEqual a b ?rel_tol rel_tol ?abs_tol abs_tol))) 44 | 45 | ; Find the mean of some numbers 46 | (defun qub::mean (@rest x) 47 | (quotient (qub::sum x) (length x))) -------------------------------------------------------------------------------- /src/inductor_generator/transformerB.ils: -------------------------------------------------------------------------------- 1 | ; Two single turn octagonal inductors on top of each other 2 | 3 | (defclass qub::transformerB (qub::transformerA) ()) 4 | 5 | (defmethod qub::draw ((device qub::transformerB)) 6 | (qub::setParamValue device 'coilSpacing 0) 7 | (qub::setParamValue device 'secondCoilDirection "up") 8 | (qub::setParamValue device 'allowOverlap t) 9 | (qub::setParamValue device 'coilSpacing 10 | (qub::getParamValue device 'portLength)) 11 | (qub::setParamValue device 'turns 1) 12 | (qub::setParamValue device 'nsides 8) 13 | (letseq ((m (parseString (qub::getParamValue device 'metalStack))) 14 | (m1 (car m)) 15 | (m2 (cadr m)) 16 | (s (sprintf nil "%s %s %s %s" m1 m1 m2 m2))) 17 | (qub::setParamValue device 'metalStack s)) 18 | (callNextMethod)) 19 | 20 | (defmacro qub::createTransformerB (@key (library nil) 21 | (defaultTopMetalLayer nil) 22 | (defaultTopMinusOneMetalLayer nil)) 23 | (letseq ((cellName "transformerB") 24 | (cellType "layout") 25 | (cellObj (ddGetObj library cellName))) 26 | ; Delete the cell if it doesn't exist 27 | ; As re-running the macro doesn't seem to refresh the PCell instance 28 | (if cellObj 29 | (ddDeleteObj cellObj)) 30 | `(pcDefinePCell 31 | (list (ddGetObj ,library) ,cellName ,cellType) 32 | ((width "float" 10.0) 33 | (spacing "float" 0.0) 34 | (apothem "float" 50.0) 35 | (coilRotation "string" "R0 R180") 36 | (metalStack "string" 37 | (strcat ,defaultTopMetalLayer 38 | " " 39 | ,defaultTopMinusOneMetalLayer)) 40 | (portLength "float" 25.0)) 41 | (let ((pcell (makeInstance 'qub::transformerB))) 42 | (qub::setPcellParams pcell pcCellView) 43 | (qub::draw pcell))))) 44 | 45 | -------------------------------------------------------------------------------- /src/circuits/passives.ils: -------------------------------------------------------------------------------- 1 | ; General functions for designing passive networks 2 | 3 | ; Convert series RL/RC to parallel 4 | (defun qub::seriesToParallel (@key Rs Xs) 5 | Xp = (Rs**2 + Xs**2)/Xs 6 | Rp = (Rs**2 + Xs**2)/Rs 7 | (list nil 'Rp Rp 'Xp Xp)) 8 | 9 | ; Convert parallel RL/RC to series 10 | (defun qub::parallelToSeries (@key Rp Xp) 11 | ; Eqn shared by both 12 | core = (Rp*Xp)/(Rp**2 + Xp**2) 13 | Rs = core * Xp 14 | Xs = core * Rp 15 | (list nil 'Rs Rs 'Xs Xs)) 16 | 17 | ; Calculate value of parallel resistors 18 | (defun qub::parallelResistors (@rest args) 19 | (quotient 1.0 (qub::sum (mapcar (lambda (x) 1.0/x) args)))) 20 | 21 | ; Reactance to capacitance 22 | (defun qub::XToC (reactance angular_frequency) 23 | (quotient -1.0 (times reactance angular_frequency))) 24 | 25 | ; Reactance to inductance 26 | (defun qub::XToL (reactance angular_frequency) 27 | (quotient reactance angular_frequency)) 28 | 29 | ; Susceptance to capacitance 30 | (defun qub::BToC (susceptance angular_frequency) 31 | (quotient susceptance angular_frequency)) 32 | 33 | ; Susceptance to inductance 34 | (defun qub::BToL (susceptance angular_frequency) 35 | (quotient -1.0 (times susceptance angular_frequency))) 36 | 37 | (defun qub::GToR (conductance) 38 | (quotient 1.0 conductance)) 39 | 40 | (defun qub::ZToY (impedance) 41 | (quotient 1.0 impedance)) 42 | 43 | ; Return plist of fields for R, L and C 44 | (defun qub::ZToSeriesRLC (impedance angular_frequency) 45 | (list nil 46 | 'R (real impedance) 47 | 'L (qub::XToL (imag impedance) angular_frequency) 48 | 'C (qub::XToC (imag impedance) angular_frequency))) 49 | 50 | ; Return plist of fields for R, L and C 51 | (defun qub::YToParallelRLC (admittance angular_frequency) 52 | (list nil 53 | 'R (qub::GToR (real admittance)) 54 | 'L (qub::BToL (imag admittance) angular_frequency) 55 | 'C (qub::BToC (imag admittance) angular_frequency))) -------------------------------------------------------------------------------- /src/design_environment/schematic_gui.ils: -------------------------------------------------------------------------------- 1 | ; If registered with schematic cell views using deRegUserTriggers, it 2 | ; creates a drop-down menu with a few buttons to trigger helper functions 3 | (defun qub::createSchematicMenu (args) 4 | (letseq ((actions (list 5 | (hiCreateMenuItem 6 | ?name 'QubCreateSelectionPinsStubs 7 | ?itemText "Create Instance(s) Pins/Stubs)" 8 | ?callback 9 | (qub::sexpToStr 10 | (qub::createSelectedInstancesPinsAndStubs nil))) 11 | (hiCreateMenuItem 12 | ?name 'QubCreateSelectionPinsStubsPrefixed 13 | ?itemText "Create Instance(s) Pins/Stubs (Prefixed)" 14 | ?callback 15 | (qub::sexpToStr 16 | (qub::createSelectedInstancesPinsAndStubs t))) 17 | (hiCreateMenuItem 18 | ?name 'QubCreateSchematicPinsStubs 19 | ?itemText "Create Schematic Pins/Stubs" 20 | ?callback 21 | (qub::sexpToStr 22 | (qub::createSchematicPinsAndStubs nil))) 23 | (hiCreateMenuItem 24 | ?name 'QubCreateSchematicPinsStubsPrefixed 25 | ?itemText "Create Schematic Pins/Stubs (Prefixed)" 26 | ?callback 27 | (qub::sexpToStr 28 | (qub::createSchematicPinsAndStubs t))) 29 | (hiCreateMenuItem 30 | ?name 'QubCreateSelectionStubs 31 | ?itemText "Create Instance(s) Stubs" 32 | ?callback 33 | (qub::sexpToStr 34 | (qub::createSelectedInstancesStubs nil))) 35 | (hiCreateMenuItem 36 | ?name 'QubCreateSelectionStubsPrefixes 37 | ?itemText "Create Instance(s) Stubs (Prefixed)" 38 | ?callback 39 | (qub::sexpToStr 40 | (qub::createSelectedInstancesStubs t))))) 41 | (pulldownMenu (hiCreatePulldownMenu 'qub_schematic_menu 42 | "QUB" 43 | actions))) 44 | (list pulldownMenu))) -------------------------------------------------------------------------------- /src/inductor_generator/spiral_inductor.ils: -------------------------------------------------------------------------------- 1 | (defun qub::getSpiralPoints (@key 2 | (completeTurns 2) 3 | (additionalSegments 0) 4 | (width 10.0) 5 | (spacing 5.0) 6 | (apothem 50.0) 7 | (nsides 8) 8 | (rotation_anticlockwise 0.0) 9 | (ref 0.0:0.0)) 10 | (when (greaterp additionalSegments nsides) 11 | (error "More additional segments then sides")) 12 | (let ((a2c qub::apothemToCircumradius) 13 | (fip qub:::findIntersectPoint) 14 | (maxTurnIdx (if (zerop additionalSegments) 15 | (sub1 completeTurns) 16 | completeTurns)) 17 | (maxPointIdx (sub1 nsides))) 18 | (let ((points nil) 19 | (getPoint 20 | (lambda (polyIdx pointIdx) 21 | (qub::getPolyPoint 22 | polyIdx 23 | pointIdx 24 | nsides 25 | (a2c ((width / 2.0) + apothem) nsides) 26 | spacing 27 | width 28 | ?rotation_anticlockwise rotation_anticlockwise 29 | ?ref ref))) 30 | (getMaxPtIdx 31 | (lambda (trnIdx) 32 | (if (and (equal trnIdx maxTurnIdx) (plusp additionalSegments)) 33 | (sub1 additionalSegments) 34 | maxPointIdx)))) 35 | (for turnIdx 0 maxTurnIdx 36 | ; drawing each segment 37 | (for pointIdx 0 (getMaxPtIdx turnIdx) 38 | (pushf (getPoint turnIdx pointIdx) points)) 39 | (unless (equal turnIdx maxTurnIdx) 40 | ; Get the last two points of the current spiral 41 | ; and the first two points of the next spiral 42 | ; and calculate an intersection point to bridge 43 | ; the two spirals together 44 | (let ((cur1 (getPoint turnIdx (sub1 maxPointIdx))) 45 | (cur2 (getPoint turnIdx maxPointIdx)) 46 | (nxt1 (getPoint (add1 turnIdx) 0)) 47 | (nxt2 (getPoint (add1 turnIdx) maxPointIdx))) 48 | (pushf (fip cur1 cur2 nxt1 nxt2) points)))) 49 | points))) -------------------------------------------------------------------------------- /src/std/functional.ils: -------------------------------------------------------------------------------- 1 | ; A rough version of haskell's fold left function 2 | ; Args: 3 | ; fn: The function to apply 4 | ; It should be in the form (accumulator list-value) => accumulator 5 | ; accumulator: object 6 | ; The accumulator value 7 | ; lst: The list to be folded 8 | ; Example: 9 | ; >>> (qub::foldl (lambda (acc val) (plus acc val)) 0 (list 1 2 3)) 10 | ; 6 11 | ; >>> (qub::foldl (lambda (acc val) (strcat acc val)) "" (list "a" "b" "c")) 12 | ; "abc" 13 | (defun qub::foldl (fn accumulator lst) 14 | (mapc (lambda (x) (setq accumulator (fn accumulator x))) lst) 15 | accumulator) 16 | 17 | ; See qub::foldl 18 | ; It folds from right to left 19 | ; Example: 20 | ; >>> (qub::foldr (lambda (acc val) (strcat acc val)) "" (list "a" "b" "c")) 21 | ; "cba" 22 | (defun qub::foldr (fn init lst) 23 | (qub::foldl fn init (reverse lst))) 24 | 25 | ; Sums a list 26 | ; Returns: 27 | ; The value of the summed list values 28 | ; Args: 29 | ; lst: list 30 | ; The list being summed. 31 | ; Example: 32 | ; >>> (qub::sum (list 1 2 4)) 33 | ; 7 34 | (defun qub::sum (lst) 35 | (qub::foldl plus 0 lst)) 36 | 37 | ; List comprehension modelled after python's 38 | ; Example: 39 | ; >>> (qub::lcmp x**2 for x in (list 1 2 3)) 40 | ; (1 4 9) 41 | (define_syntax qub::lcmp 42 | (syntax_rules (for in if) 43 | ((lcmp expression for element in list) 44 | (mapcar (lambda (element) expression) list)) 45 | ((lcmp expression for element in list if predicate) 46 | (mapcar (lambda (element) expression) (setof element list predicate))))) 47 | 48 | ; Take the first N values of a list 49 | ; Returns: 50 | ; list 51 | ; Args: 52 | ; n: int 53 | ; The number of elements to take 54 | ; lst: list 55 | ; The list to take from 56 | ; Example: 57 | ; >>> (qub::takeN 2 (list 1 2 3 4 5)) 58 | ; (1 2) 59 | (defun qub::takeN (n lst) 60 | (mapcar (lambda (x) (nth x lst)) (qub::range ?start 0 ?stop n))) 61 | 62 | ; Returns: 63 | ; The input list with the first n elements removed 64 | ; Args: 65 | ; n: int 66 | ; The number of elements to drop 67 | ; lst: The list we're extracting elements from 68 | ; Example: 69 | ; >>> (qub::dropN 2 (list 1 2 3 4 5)) 70 | ; (3 4 5) 71 | (defun qub::dropN (n lst) 72 | (mapcar (lambda (x) (nth x lst)) (qub::range ?start n ?stop (length lst)))) -------------------------------------------------------------------------------- /src/std/fileio.ils: -------------------------------------------------------------------------------- 1 | ; Recursively get all files within a directory with their full paths 2 | ; Returns: 3 | ; A list of strings of paths 4 | ; Args: 5 | ; rel_path: string 6 | ; The parent directory 7 | (defun qub::listFileRec (rel_path) 8 | (labels ((helper (pth) 9 | (if (isFile pth) 10 | pth 11 | (mapcar (lambda (x) (helper (strcat pth "/" x))) 12 | (qub::filterUnwanted (getDirFiles pth)))))) 13 | (qub::flattenList (helper rel_path)))) 14 | 15 | ; Removes . .. and dotted files from list 16 | ; Returns: 17 | ; List of paths with only "undotted" files 18 | ; Args: 19 | ; files: (list string) 20 | ; A list of path strings to filter 21 | ; Example: 22 | ; >>> (qub::filterUnwanted (list ".." "." "foo.ils" "bar.ils" ".baz")) 23 | ; ("foo.ils" "bar.ils") 24 | (defun qub::filterUnwanted (files) 25 | (setof x files (and (nequal x ".") 26 | (nequal x "..") 27 | (nequal (getchar x 1) '\.)))) 28 | 29 | ; Strips the directory information from a file's full path 30 | ; Return: 31 | ; String of the file name 32 | ; Args: 33 | ; path: The path to be processed 34 | ; Example: 35 | ; >>> (qub::getFileName "x/y/z/foo.ils") 36 | ; "foo.ils" 37 | (defun qub::getFileName (path) 38 | (car (last (parseString path "/")))) 39 | 40 | ; Finds all paths in a list where the files begin with a certain prefix 41 | ; Returns: 42 | ; A list of paths 43 | ; Args: 44 | ; files: (list string) 45 | ; The paths to filter 46 | ; prefix: string 47 | ; The prefix to search for 48 | ; Example: 49 | ; >>> paths = (list "x/x_1" "x/x_2" "x/y_1" "y/x_1") 50 | ; ("x/x_1" "x/x_2" "x/y_1" "y/x_1") 51 | ; >>> (qub::findFilesWithPrefix paths "x_") 52 | ; ("x/x_1" "x/x_2" "y/x_1") 53 | (defun qub::findFilesWithPrefix (files prefix) 54 | (setof x files (qub::startsWith (qub::getFileName x) prefix))) 55 | 56 | ; Get the paths of files with a certain name 57 | ; Return: 58 | ; List of paths 59 | ; Args: 60 | ; files: (list string) 61 | ; List of paths 62 | ; name: string 63 | ; The filename to search for 64 | ; Example: 65 | ; >>> (qub::findFiles (list "x/foo.ils" "y/foo.ils" "z/bar.ils") "foo.ils") 66 | ; ("x/foo.ils" "y/foo.ils") 67 | (defun qub::findFiles (files name) 68 | (setof x files (equal name (qub::getFileName x)))) -------------------------------------------------------------------------------- /src/inductor_generator/ind_pcell.ils: -------------------------------------------------------------------------------- 1 | (defclass qub::inductorPCell (qub::PcellParam) 2 | ((completeTurns @initform (qub::defineParam "int" 2)) 3 | (additionalSegments @initform (qub::defineParam "int" 0)) 4 | (width @initform (qub::defineParam "float" 10.0)) 5 | (spacing @initform (qub::defineParam "float" 2.0)) 6 | (apothem @initform (qub::defineParam "float" 50.0)) 7 | (nsides @initform (qub::defineParam "int" 8)) 8 | (metalLayer @initform (qub::defineParam "string" "M1")))) 9 | 10 | (defmethod qub::draw ((device qub::inductorPCell)) 11 | (let ((ct (qub::getParamValue device 'completeTurns)) 12 | (as (qub::getParamValue device 'additionalSegments)) 13 | (w (qub::getParamValue device 'width)) 14 | (s (qub::getParamValue device 'spacing)) 15 | (a (qub::getParamValue device 'apothem)) 16 | (n (qub::getParamValue device 'nsides)) 17 | (ml (qub::getParamValue device 'metalLayer)) 18 | (cv (slotValue device 'cvId))) 19 | (let ((points (qub::getSpiralPoints ?completeTurns ct 20 | ?additionalSegments as 21 | ?width w 22 | ?spacing s 23 | ?apothem a 24 | ?nsides n 25 | ?rotation_anticlockwise 0.0))) 26 | (dbCreatePath cv (list ml) points w "truncateExtend")) 27 | (callNextMethod))) 28 | 29 | (defmacro qub::createInductorPCell (@key (library nil) 30 | (defaultMetalLayer nil)) 31 | (letseq ((cellName "spiralInd") 32 | (cellType "layout") 33 | (cellObj (ddGetObj library cellName))) 34 | ; Delete the cell if it doesn't exist 35 | ; As re-running the macro doesn't seem to refresh the PCell instance 36 | (if cellObj 37 | (ddDeleteObj cellObj)) 38 | `(pcDefinePCell 39 | (list (ddGetObj ,library) ,cellName ,cellType) 40 | ((completeTurns "int" 2) 41 | (additionalSegments "int" 0) 42 | (width "float" 10.0) 43 | (spacing "float" 5.0) 44 | (apothem "float" 50.0) 45 | (nsides "int" 8) 46 | (metalLayer "string" ,defaultMetalLayer)) 47 | (let ((pcell (makeInstance 'qub::inductorPCell))) 48 | (qub::setPcellParams pcell pcCellView) 49 | (qub::draw pcell))))) 50 | -------------------------------------------------------------------------------- /src/inductor_generator/init.ils: -------------------------------------------------------------------------------- 1 | (qubCreateModule "inductor_generator/" 2 | (modload "spiral_inductor.ils") 3 | (modload "ind_pcell.ils") 4 | (modload "oct_ind_pcell.ils") 5 | (modload "symmetric_inductor.ils") 6 | (modload "symm_ind_pcell.ils") 7 | (modload "transformerA.ils") 8 | (modload "transformerB.ils") 9 | (modload "frlan_segment.ils") 10 | (modload "frlan.ils")) 11 | 12 | ; Create inductor PCells for a library. 13 | ; If this macro is too restrictive then you can always define your own macro 14 | ; or look to see what the individual macros are doing to create a more custom 15 | ; version. 16 | ; 17 | ; The libInit.il file for the library should re-load this library's code 18 | ; or else streaming out to GDSII will fail. 19 | ; 20 | ; Return: 21 | ; Nothing, it only generates side effects. 22 | ; Args: 23 | ; libname: string 24 | ; The name of the library to create the PCells in 25 | ; metalLayers: list(string) 26 | ; The metal layers used for the coils. 27 | ; It's assumed that the first value is the top layer and so on. 28 | (defmacro qub::createInductorPCells (libname metalLayers) 29 | `(progn (qub::createInductorPCell ?library ,libname 30 | ?defaultMetalLayer (car ,metalLayers)) 31 | (qub::createOctInductorPCell ?library ,libname 32 | ?defaultMetalLayer (car ,metalLayers)) 33 | (qub::createSymmInductorPCell 34 | ?library ,libname 35 | ?defaultTopMetalLayer (car ,metalLayers) 36 | ?defaultTopMinusOneMetalLayer (cadr ,metalLayers)) 37 | (qub::createTransformerA 38 | ?library ,libname 39 | ?defaultTopMetalLayer (car ,metalLayers) 40 | ?defaultTopMinusOneMetalLayer (cadr ,metalLayers)) 41 | (qub::createTransformerB 42 | ?library ,libname 43 | ?defaultTopMetalLayer (car ,metalLayers) 44 | ?defaultTopMinusOneMetalLayer (cadr ,metalLayers)) 45 | (qub::createFrlanSegmentPCell ?library ,libname 46 | ?defaultMetalLayer (car ,metalLayers)) 47 | (qub::createFrlanPCell 48 | ?library ,libname 49 | ?defaultTopMetalLayer (car ,metalLayers) 50 | ?defaultBridgeMetalLayer (cadr ,metalLayers)) 51 | (printf "Created PCells in %s.\n" ,libname) 52 | (printf 53 | (strcat 54 | "Note: Don't forget to re-load this library's code in " 55 | "libInit.il to prevent errors when " 56 | "streaming out the layout.\n")))) -------------------------------------------------------------------------------- /src/pcell/PcellParam.ils: -------------------------------------------------------------------------------- 1 | ; Base class for all Pcells. 2 | ; cvId holds the pcCellView value 3 | (defclass qub::PcellParam () 4 | ((cvId @initarg cvId))) 5 | 6 | ; Populate the Pcell parameters from the super master to a Pcell device's slots 7 | ; Args: 8 | ; device: qub::PcellParam 9 | ; The instance that we're mutating 10 | ; cv: cellView instance 11 | (defmethod qub::setPcellParams ((device qub::PcellParam) cv) 12 | (when (and cv (dbIsId cv)) 13 | (setSlotValue device 'cvId cv) 14 | (foreach param cv~>parameters~>value 15 | (qub::setParamValue device (concat param~>name) param~>value)))) 16 | 17 | ; Define a Pcell parameter stored as a Pcell class' slot. 18 | ; Args: 19 | ; g_type: string 20 | ; The type of the parameter 21 | ; g_value: The type of g_value should be the same as that stated by g_type 22 | ; e.g. if g_type = "float" then g_value should be a float 23 | ; _isParam: bool 24 | ; Indicates the parameter is an active Pcell parameter 25 | ; Can be used to disable parameters 26 | (defun qub::defineParam (g_type g_value @optional (_isParam t)) 27 | (list nil 'type g_type 'value g_value 'isParam _isParam)) 28 | 29 | ; Get the Pcell parameter's type 30 | (defmethod qub::getParamType ((device qub::PcellParam) 31 | (propertyName symbol)) 32 | ((slotValue device propertyName)->type)) 33 | 34 | ; Get the Pcell parameter's value 35 | (defmethod qub::getParamValue ((device qub::PcellParam) 36 | (propertyName symbol)) 37 | (slotValue device propertyName)->value) 38 | 39 | ; Set the Pcell parameter's value 40 | (defmethod qub::setParamValue ((device qub::PcellParam) 41 | (propertyName symbol) val) 42 | ((slotValue device propertyName)->value = val)) 43 | 44 | ; Check is the given name a Pcell parameter or not. 45 | ; Based on the setting of the isParam attribute 46 | (defmethod qub::isParam ((device qub::PcellParam) 47 | (propertyName symbol)) 48 | (slotValue device propertyName)->isParam) 49 | 50 | ; Get a list of Pcell parameters with their names, types and values 51 | (defmethod qub::getPcellParams ((device qub::PcellParam)) 52 | (let ((params nil)) 53 | (setq params (setof p device->? (isParam device p))) 54 | (setq params (foreach mapcar p params 55 | (list p 56 | (qub::getParamType device p) 57 | (qub::getParamValue device p)))))) 58 | 59 | (defun qub::getPcellBoolParamVal (device param) 60 | (let ((outStr (qub::getParamValue device param))) 61 | (cond ((equal outStr "FALSE") nil) 62 | ((equal outStr "TRUE") t) 63 | (t (error "drawConnectingBridge is neither 'TRUE' nor 'FALSE'"))))) -------------------------------------------------------------------------------- /src/design_environment/schematic_settings.ils: -------------------------------------------------------------------------------- 1 | ; Get the text of every label object where the first line is "qub_settings" 2 | ; Args: 3 | ; instances: a list of schematic instance objects 4 | (defun qub::findSettingsTextBlocks (instances) 5 | (defun pred (x) 6 | (and (equal x->objType "label") 7 | (equal (car (parseString x->theLabel "\n")) "qub_settings"))) 8 | (qub::lcmp x->theLabel for x in instances if (pred x))) 9 | 10 | (defun qub::makeEmptySchematicSettings () 11 | (list nil 'merge nil 'prefix_instance_name nil 'wire_length 0.75)) 12 | 13 | ; Parses a string of settings text lines into a settings plist and returns the plist 14 | ; Args 15 | ; Settings: A string of the user settings 16 | (defun qub::parseSchematicSettings (settings_text) 17 | (letseq ((lines (parseString settings_text "\n")) 18 | (first_line (car lines))) 19 | (when (equal first_line "qub_settings") 20 | (letseq ((parsed_lines (mapcar qub::parseSchematicSettingsLine lines)) 21 | (valid_lines (setof line parsed_lines (nequal line->cmd "INVALID"))) 22 | (invalid_lines (setof line parsed_lines (equal line->cmd "INVALID"))) 23 | (settings (qub::makeEmptySchematicSettings))) 24 | (mapc (lambda (x) (qub::addSettingsEntry x settings)) valid_lines) 25 | settings)))) 26 | 27 | ; Parse a settings line into a property list with fields for "cmd" and "arg" 28 | ; These represent the command, e.g. "wire_length", and the argument, e.g. 0.75 29 | ; Some of the fields like wire_length and prefix_instance_name are parsed into 30 | ; their appropriate data types instead of being left as strings 31 | (defun qub::parseSchematicSettingsLine (line) 32 | (defun makeCmd (cmd arg mod) 33 | (list nil 'command cmd 'argument arg 'mode mod)) 34 | (defun makeInvalidCmd () 35 | (makeCmd "INVALID" line nil)) 36 | (letseq ((vals (parseString line "=")) 37 | (cmd (car vals)) 38 | (arg (cadr vals))) 39 | (cond ((equal cmd "merge") (makeCmd cmd (parseString arg ",") "add")) 40 | ((equal cmd "prefix_instance_name") 41 | (cond ((equal arg "t") (makeCmd cmd t "set")) 42 | ((equal arg "nil") (makeCmd cmd nil "set")) 43 | (t (makeInvalidCmd)))) 44 | ((equal cmd "wire_length") 45 | (let ((parsed_wire_length (atof arg))) 46 | (if (floatp parsed_wire_length) 47 | (makeCmd cmd parsed_wire_length "set") 48 | (makeInvalidCmd)))) 49 | (t (makeInvalidCmd))))) 50 | 51 | ; Add the new entry to the settings object 52 | ; It mutates the settings plist passed in 53 | ; Args: 54 | ; Entry: Property list with fields for "command" and "argument" 55 | (defun qub::addSettingsEntry (entry settings) 56 | (let ((settings_field (get settings entry->command))) 57 | (cond ((equal entry->mode "set") 58 | (putprop settings entry->argument entry->command)) 59 | ((equal entry->mode "add") 60 | (putprop settings 61 | (qub::joinLists (list settings_field entry->argument)) 62 | entry->command)) 63 | (t nil)))) 64 | -------------------------------------------------------------------------------- /src/ocean/ampHB.ils: -------------------------------------------------------------------------------- 1 | ; For a power amplifier circuit, plot the following: 2 | ; output power 3 | ; gain 4 | ; drain efficiency 5 | ; power added efficiency 6 | ; 7 | ; Args: 8 | ; out: (list net:string node:string) 9 | ; The net and node of the output port. 10 | ; in: (list net:string node:string) 11 | ; The net and node of the input port. 12 | ; supplies: (list (list net:string node:string) ...) 13 | ; The nets and nodes of the power supplies. 14 | ; Unlike out and in, this is a list 15 | ; of lists to support multiple supplies. 16 | ; opSupply: (list net:string node:string) 17 | ; The net and node of the supply voltage of the output stage. 18 | ; Used to check the drain efficiency of the output stage only. 19 | ; Set it to nil if you don't want to use it 20 | ; nHarmonics: int 21 | ; The number of harmonics to plot. 22 | ; testType: symbol 23 | ; 'hb or 'pss for Harmonic Balance and PSS analyses. 24 | ; pwrInWatts (list of floats or a float): Used for gain and PAE. 25 | (defun qub::ocnAmpHb (@key (out nil) 26 | (in nil) 27 | (supplies nil) 28 | (opSupply nil) 29 | (testType 'hb) 30 | (pwrInWatts nil) 31 | (nHarmonics 3)) 32 | (letseq ((getPwr (lambda (x) (pvi testType (car x) 0 (cadr x) 0))) 33 | (getPwrDc (lambda (x) (abs (harmonic (getPwr x) 0)))) 34 | (pwrOutWatts (getPwr out)) 35 | (harmIdxs (qub::range ?start 1 ?stop nHarmonics+1 ?step 1)) 36 | (pwrOutWattsHarms 37 | (qub::lcmp (harmonic pwrOutWatts x) for x in harmIdxs)) 38 | (pwrOutWattsF0 (car pwrOutWattsHarms)) 39 | (pwrDc (qub::sum (qub::lcmp (getPwrDc src) for src in supplies))) 40 | (opPwrDc (if opSupply 41 | (getPwrDc opSupply) 42 | nil)) 43 | (gain (db10 (pwrOutWattsF0 / pwrInWatts))) 44 | (pae (times 100.0 (quotient (pwrOutWattsF0 - pwrInWatts) pwrDc))) 45 | (getDe (lambda (pOut pDc) (times 100.0 (quotient pOut pDc)))) 46 | (drain_efficiency (getDe pwrOutWattsF0 pwrDc)) 47 | (op_drain_efficiency (if opPwrDc 48 | (getDe pwrOutWattsF0 opPwrDc) 49 | nil)) 50 | (winId (newWindow))) 51 | (if (or (drIsWaveform pae) (drIsParamWave pae)) 52 | (progn (plot gain drain_efficiency pae 53 | ?expr (list "Gain (dB)" "DE (%)" "PAE (%)")) 54 | (when op_drain_efficiency 55 | (plot op_drain_efficiency ?expr (list "DE (OP) (%)"))) 56 | (foreach idx (qub::range ?start 0 ?stop nHarmonics ?step 1) 57 | (plot (dbm (nth idx pwrOutWattsHarms)) 58 | ?expr (list (sprintf nil "Pout (dBm, %df0)" idx+1))))) 59 | (progn (printf "Pout (f0): %.2f dBm\n" (dBm pwrOutWattsF0)) 60 | (printf "PAE: %.2f%%\n" pae) 61 | (printf "DE: %.2f%%\n" drain_efficiency) 62 | (when op_drain_efficiency 63 | (printf "DE (OP): %.2f%%\n" op_drain_efficiency)) 64 | (printf "Gain: %.2f dB\n" gain) 65 | (plot (dbm pwrOutWatts) ?expr (list "Pout")))))) -------------------------------------------------------------------------------- /src/ocean/loadPullResults.ils: -------------------------------------------------------------------------------- 1 | ; Plot the results of a load-pull simulation on a smith chart 2 | ; Args: 3 | ; minPout: float 4 | ; The minimum output power to display. Value in dBm. 5 | ; minPae: float 6 | ; The minimum power-added efficiency to display. Value in %. 7 | ; minDrainEff: float 8 | ; The minimum drain efficiency to display. Value in %. 9 | ; outName: string 10 | ; Instance name of the output port 11 | ; inName: string 12 | ; Instance name of the input port 13 | ; vddName: string 14 | ; Instance name of the supply voltage source 15 | ; testType: string 16 | ; The type of simulation, either "pss" or "hb" for periodic steady 17 | ; state or harmonic balance. 18 | ; nContours: int 19 | ; The number of load-pull contours to display for each measurement 20 | ; supplyNet: string 21 | ; The net connected to the supply voltage source 22 | ; outNet: string 23 | ; The net connected to the output port 24 | ; inNet: string 25 | ; The net connected to the input port 26 | ; closeContours: bool 27 | ; Contours are closed if true, can be incomplete if nil 28 | (defun qub::ocnLoadPull (@key (minPout 0) 29 | (minPae 0) 30 | (minDrainEff 0) 31 | (outName nil) 32 | (inName nil) 33 | (vddName nil) 34 | (testType nil) 35 | (nContours 6) 36 | (supplyNet nil) 37 | (outNet nil) 38 | (inNet nil) 39 | (closeContours nil)) 40 | (cond ((not testType) 41 | (error "testType not \"hb\" or \"pss\"")) 42 | ((not outName) 43 | (error "Provide a name for the output port \"outName\".")) 44 | ((not inName) 45 | (error "Provide a name for the in port \"inName\".")) 46 | ((not vddName) 47 | (error "Provide a name for the supply component \"vddName\"."))) 48 | 49 | (qub::createSmith) 50 | 51 | simType = (if (equal testType "hb") 'hb 'pss) 52 | (printf "Assuming sim type: '%s'\n" simType) 53 | testType = (strcat testType "_fd") 54 | 55 | outNode = (sprintf nil "/%s/PLUS" outName) 56 | inNode = (sprintf nil "/%s/PLUS" inName) 57 | supplyNode = (sprintf nil "/%s/PLUS" vddName) 58 | 59 | outCurrent = (i outNode ?result testType) 60 | outVoltage = (v outNet ?result testType) 61 | outPower = (spectralPower outCurrent outVoltage) 62 | 63 | inCurrent = (i inNode ?result testType) 64 | inVoltage = (v inNet ?result testType) 65 | inPower = (- (spectralPower inCurrent inVoltage)) 66 | 67 | dcCurrent = (i supplyNode ?result testType) 68 | dcVoltage = (v supplyNet ?result testType) 69 | dcPower = (- (harmonic (spectralPower dcCurrent dcVoltage) 0)) 70 | 71 | rfPower = (harmonic outPower 1) - (harmonic inPower 1) 72 | 73 | PAE = (awvRfLoadPull (100.0 * (rfPower/dcPower)) 74 | ?maxValue 100.0 75 | ?minValue minPae 76 | ?numCont nContours 77 | ?closeCont closeContours) 78 | 79 | drainEff = (awvRfLoadPull (100.0 * ((harmonic outPower 1)/dcPower)) 80 | ?maxValue 100.0 81 | ?minValue minDrainEff 82 | ?numCont nContours 83 | ?closeCont closeContours) 84 | 85 | powerContours = (cPwrContour outCurrent 86 | outVoltage 87 | '1 88 | ?refImp 50.0 89 | ?numCont nContours 90 | ?minPower minPout 91 | ?closeCont closeContours 92 | ?modifier "dBm") 93 | 94 | (plot powerContours ?expr (list "Po")) 95 | (plot drainEff ?expr (list "DE")) 96 | (plot PAE ?expr (list "PAE"))) 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/inductor_generator/transformerA.ils: -------------------------------------------------------------------------------- 1 | ; Two coils some space apart 2 | 3 | (defclass qub::transformerA (qub::symmInductorPCell) 4 | ((coilSpacing @initform (qub::defineParam "float" 5.0)) 5 | (coilRotation @initform (qub::defineParam "string" "R0 R180")) 6 | (secondCoilDirection @initform (qub::defineParam "string" "up")) 7 | (allowOverlap @initform (qub::defineParam "boolean" "FALSE")))) 8 | 9 | (defmethod qub::draw ((device qub::transformerA)) 10 | (letseq ((coilSpacing (qub::getParamValue device 'coilSpacing)) 11 | (rotation (parseString (qub::getParamValue device 'coilRotation))) 12 | (dir (qub::getParamValue device 'secondCoilDirection)) 13 | (metalLayers (parseString (qub::getParamValue device 'metalStack))) 14 | (allowOverlap 15 | (nequal "FALSE" (qub::getParamValue device 'allowOverlap))) 16 | (portLength (qub::getParamValue device 'portLength)) 17 | (cv (slotValue device 'cvId)) 18 | (setMet (lambda (m1 m2) 19 | (qub::setParamValue device 20 | 'metalStack 21 | (sprintf nil "%s %s" m1 m2)))) 22 | (createInd (lambda (name m1 m2) 23 | (setMet m1 m2) 24 | (let ((figGroup (callNextMethod))) 25 | (putprop figGroup name 'name) 26 | figGroup))) 27 | (fg1 (createInd "Group1" (nth 0 metalLayers) (nth 1 metalLayers))) 28 | (fg2 (createInd "Group2" (nth 2 metalLayers) (nth 3 metalLayers))) 29 | (calc (lambda (x y) 30 | (if allowOverlap 31 | 0 32 | ((x fg1) - (y fg1))))) 33 | (height (calc topEdge bottomEdge)) 34 | (width (calc rightEdge leftEdge)) 35 | (fn (lambda (refDir x) 36 | (if (dir == refDir) 37 | (x + coilSpacing - portLength) 38 | (0 - x - coilSpacing)))) 39 | (transform (if (member dir (list "up" "down")) 40 | (list 0 (fn "up" height)) 41 | (list (fn "right" width) 0)))) 42 | (dbMoveFig fg1 cv (list 0:0 (car rotation))) 43 | (dbMoveFig fg2 cv (list transform (cadr rotation))))) 44 | 45 | (defmacro qub::createTransformerA (@key (library nil) 46 | (defaultTopMetalLayer nil) 47 | (defaultTopMinusOneMetalLayer nil)) 48 | (letseq ((cellName "transformerA") 49 | (cellType "layout") 50 | (cellObj (ddGetObj library cellName))) 51 | ; Delete the cell if it doesn't exist 52 | ; As re-running the macro doesn't seem to refresh the PCell instance 53 | (if cellObj 54 | (ddDeleteObj cellObj)) 55 | `(pcDefinePCell 56 | (list (ddGetObj ,library) ,cellName ,cellType) 57 | ((turns "int" 2) 58 | (width "float" 10.0) 59 | (spacing "float" 5.0) 60 | (apothem "float" 50.0) 61 | (nsides "int" 8) 62 | (coilSpacing "float" 5.0) 63 | (coilRotation "string" "R0 R180") 64 | (secondCoilDirection "string" "up") 65 | (allowOverlap "boolean" "FALSE") 66 | (metalStack "string" 67 | (strcat ,defaultTopMetalLayer 68 | " " 69 | ,defaultTopMinusOneMetalLayer 70 | " " 71 | ,defaultTopMetalLayer 72 | " " 73 | ,defaultTopMinusOneMetalLayer)) 74 | (portLength "float" 25.0)) 75 | (let ((pcell (makeInstance 'qub::transformerA))) 76 | (qub::setPcellParams pcell pcCellView) 77 | (qub::draw pcell))))) 78 | 79 | -------------------------------------------------------------------------------- /src/std/list.ils: -------------------------------------------------------------------------------- 1 | ; Join a list of lists 2 | ; Return: 3 | ; list 4 | ; Args: 5 | ; lst: A list of lists 6 | ; Example: 7 | ; >>> (qub::joinLists (list (list 1) (list 2))) 8 | ; (1 2) 9 | (defun qub::joinLists (lst) 10 | (qub::foldl append (list) lst)) 11 | 12 | ; Recursively flatten all lists 13 | ; Return: 14 | ; list 15 | ; Args: 16 | ; lst: A list of lists of any depth 17 | ; Example: 18 | ; >>> (qub::flattenList (list 1 (list 2) (list (list (list 3)) (list 4)))) 19 | ; (1 2 3 4) 20 | (defun qub::flattenList (lst) 21 | (letseq ((outputList (list))) 22 | (labels ((helper (a) 23 | (mapcar (lambda (x) 24 | (if (atom x) (pushf x outputList) (helper x))) 25 | a))) 26 | (helper lst) 27 | (reverse outputList)))) 28 | 29 | ; Creates a list of numbers. Modelled on Python 3's range function. 30 | ; Return: 31 | ; list of integers 32 | ; Args: 33 | ; start: int 34 | ; The starting value. It is inclusive and will be in the output list. 35 | ; stop: int 36 | ; The stop value. It is exclusive and will not be in the list 37 | ; step: int 38 | ; The step value of the internal loop 39 | ; Example: 40 | ; >>> (qub::range ?start 0 ?stop 5) 41 | ; (0 1 2 3 4) 42 | ; >>> (qub::range ?start 1 ?stop 11 ?step 2) 43 | ; (1 3 5 7 9) 44 | (defun qub::range (@key (start 0) (stop -1) (step 1)) 45 | (if (lessp stop start) 46 | (error "End value is lower than start value") 47 | (let ((values nil) 48 | (cur_val start)) 49 | (while (lessp cur_val stop) 50 | (pushf cur_val values) 51 | (setq cur_val (plus cur_val step))) 52 | (reverse values)))) 53 | 54 | ; Mutate a list by adding a new value to the end. 55 | ; Returns: 56 | ; The value that was added to the list. 57 | ; The original list is mutated 58 | ; Args: 59 | ; val: object 60 | ; The value to be added to the list 61 | ; lst: list 62 | ; The list to be mutated 63 | ; Example: 64 | ; >>> my_list = (list 1 2) 65 | ; (1 2) 66 | ; >>> (qub::pushEnd 3 my_list) 67 | ; (3) 68 | ; >>> my_list 69 | ; (1 2 3) 70 | (defun qub::pushEnd (val lst) 71 | (pushf val (cdr (last lst)))) 72 | 73 | ; Get the last atom from a list. 74 | ; Returns: 75 | ; The final atom in a list 76 | ; Args: 77 | ; lst: list 78 | ; The list to extract the atom from 79 | ; Example: 80 | ; >>> (qub::lastAtom (list 1 2 3)) 81 | ; 3 82 | (defun qub::lastAtom (lst) 83 | (car (last lst))) 84 | 85 | ; Adds each value in a list with the corresponding element in another list. 86 | ; Return: 87 | ; A list of values 88 | ; Args: 89 | ; a: A list of numbers 90 | ; b: A list of numbers 91 | ; Example: 92 | ; >>> (qub::addListElems (list 1 2 3) (list 1 2 3)) 93 | ; (2 4 6) 94 | (defun qub::addListElems (a b) 95 | (mapcar plus a b)) 96 | 97 | ; Copies the the values of a property list to a new property list. 98 | ; The new list is mutated 99 | ; Args: 100 | ; oldVals: property list 101 | ; The object you're copying from 102 | ; newVals: property list 103 | ; The object you're copying to 104 | ; Returns: 105 | ; Nothing, the newVals list is mutated. 106 | ; Example: 107 | ; >>> x = (list nil 'a 1 'b 2) 108 | ; (nil a 1 b 2) 109 | ; >>> x->?? 110 | ; (a 1 b 2) 111 | ; >>> y = (list nil) 112 | ; (nil) 113 | ; >>> y->?? 114 | ; nil 115 | ; >>> (qub::copyObjProperties x y) 116 | ; t 117 | ; >>> y->?? 118 | ; (b 2 a 1) 119 | (defun qub::copyObjProperties (oldVals newVals) 120 | (letseq ((properties oldVals->??) 121 | (numProp (length properties))) 122 | (foreach i (qub::range ?start 0 ?stop numProp ?step 2) 123 | (let ((newSym (nth i properties)) 124 | (newVal (nth i+1 properties))) 125 | (putprop newVals newVal newSym))) 126 | t)) 127 | -------------------------------------------------------------------------------- /src/ocean/passiveOnePort.ils: -------------------------------------------------------------------------------- 1 | ; Evaluate a passive component using a one-port S-parameter simulation 2 | ; Args: 3 | ; port: integer 4 | ; The index of the S-parameter port to analyse 5 | ; plotMode: (list symbol) 6 | ; Valid values: 7 | ; 'sL (series inductor) 8 | ; 'pL (parallel inductor) 9 | ; 'sC (series capacitor) 10 | ; 'pC (parallel capacitor) 11 | ; markerFreq: float 12 | ; Places a vertical marker in all plots at the desired frequency 13 | ; singleFreq: float 14 | ; Restrict the plot to values for a single frequency 15 | ; If it is not nil, markerFreq is ignored 16 | ; plotTitle: string 17 | ; The title of the ViVA plot 18 | (defun qub::ocnPassiveOnePort (@key (port 1) 19 | (markerFreq nil) 20 | (plotTitle "Passive Plot") 21 | (singleFreq nil) 22 | (plotMode (list 'sL 'pL 'sC 'pC))) 23 | (letseq ((f (qub::sToF)) 24 | (w (times 2.0 qub::m.PI f)) 25 | (Z (zpm "sp" port port)) 26 | (Rs (real Z)) 27 | (X (imag Z)) 28 | (Qs (quotient (abs X) Rs)) 29 | (Ls (qub::XToL X w)) 30 | (Cs (qub::XToC X w)) 31 | (TauL (quotient Ls Rs)) 32 | (TauC (times Rs Cs)) 33 | (Y (ypm "sp" port port)) 34 | (Rp (quotient 1.0 (real Y))) 35 | (Xp (quotient -1.0 (imag Y))) 36 | (Lp (qub::XToL Xp w)) 37 | (Cp (qub::XToC Xp w)) 38 | (Qp (abs (quotient Rp Xp))) 39 | (winId (newWindow)) 40 | (isSingleFreq (not (null singleFreq)))) 41 | (defun changeVarSingleFreq (_var) 42 | (if isSingleFreq (value _var singleFreq) _var)) 43 | (let ((Z (changeVarSingleFreq Z)) 44 | (Rs (changeVarSingleFreq Rs)) 45 | (X (changeVarSingleFreq X)) 46 | (Qs (changeVarSingleFreq Qs)) 47 | (Ls (changeVarSingleFreq Ls)) 48 | (Cs (changeVarSingleFreq Cs)) 49 | (TauL (changeVarSingleFreq TauL)) 50 | (TauC (changeVarSingleFreq TauC)) 51 | (Rp (changeVarSingleFreq Rp)) 52 | (Xp (changeVarSingleFreq Xp)) 53 | (Lp (changeVarSingleFreq Lp)) 54 | (Cp (changeVarSingleFreq Cp)) 55 | (Qp (changeVarSingleFreq Qp))) 56 | (define (plotSeriesInductance) 57 | (addSubwindowTitle "Series RL") 58 | (plot Rs X Qs Ls TauL (abs Z) 59 | ?expr (list "R" "X" "Q" "L" "Tau" "|Z|") 60 | ?yNumber (list 1 1 2 3 4 1))) 61 | (define (plotParallelInductance) 62 | (addSubwindowTitle "Parallel RL") 63 | (plot Rp Xp Qp Lp 64 | ?expr (list "R" "X" "Q" "L") 65 | ?yNumber (list 1 2 3 4))) 66 | (define (plotSeriesCapacitance) 67 | (addSubwindowTitle "Series RC") 68 | (plot Rs X Qs Cs TauC (abs Z) 69 | ?expr (list "R" "X" "Q" "C" "Tau" "|Z|") 70 | ?yNumber (list 1 1 2 3 4 1))) 71 | (define (plotParallelCapacitance) 72 | (addSubwindowTitle "Parallel RC") 73 | (plot Rp Xp Qp Cp 74 | ?expr (list "R" "X" "Q" "C") 75 | ?yNumber (list 1 2 3 4))) 76 | (let ((plotFns (makeTable "plotFunTable" 0))) 77 | plotFns['sL] = plotSeriesInductance 78 | plotFns['pL] = plotParallelInductance 79 | plotFns['sC] = plotSeriesCapacitance 80 | plotFns['pC] = plotParallelCapacitance 81 | (addTitle plotTitle) 82 | (foreach symIdx (qub::range ?start 0 ?stop (length plotMode)) 83 | ((plotFns[(nth symIdx plotMode)])) 84 | (if (and (not (null markerFreq)) (null singleFreq)) 85 | (awvPlaceXMarker winId markerFreq ?subwindow symIdx+1)) 86 | (if (equal symIdx (difference (length plotMode) 1)) 87 | t 88 | (addSubwindow))) 89 | t)))) 90 | -------------------------------------------------------------------------------- /src/ocean/ampTran.ils: -------------------------------------------------------------------------------- 1 | ; For a transient simulations of a power amplifier circuit, plot the following: 2 | ; output power 3 | ; gain 4 | ; drain efficiency 5 | ; power added efficiency 6 | ; Args: 7 | ; out: (list positive_net:string 8 | ; negative_net:string 9 | ; node:string) 10 | ; The net and node of the output port. 11 | ; in: (list positive_net:string 12 | ; negative_net:string 13 | ; node:string) 14 | ; The net and node of the input port. 15 | ; supplies: (list (list positive_net:string 16 | ; negative_net:string 17 | ; node:string) ...) 18 | ; The nets and nodes of the power supplies. 19 | ; Unlike out and in, this is a list of lists 20 | ; to support multiple supplies. 21 | ; pwrInWatts (list of floats or a float): Used for gain and PAE. 22 | ; nHarmonics: int 23 | ; The number of harmonics to plot. 24 | ; f0: float 25 | ; The fundamental frequency 26 | ; nSamples: integer 27 | ; The number of samples for the dft 28 | ; nCycles: integer 29 | ; The number of cycles before endTime to be included in the dft 30 | ; endTime: float 31 | ; The end of the sampling window for the dft 32 | ; If left nil, final time value of the simulation is used. 33 | (defun qub::ocnAmpTran (@key (out nil) 34 | (in nil) 35 | (supplies nil) 36 | (pwrInWatts nil) 37 | (nHarmonics 3) 38 | (nSamples 64) 39 | (f0 nil) 40 | (nCycles 1) 41 | (endTime nil)) 42 | (letseq ((t0 (quotient 1 f0)) 43 | (endTime (if (null endTime) 44 | (qub::lastAtom (sweepVarValues "time" ?result 'tran)) 45 | endTime)) 46 | (startTime (difference endTime (nCycles * t0))) 47 | (xdft (lambda (signal) (dft signal startTime endTime nSamples))) 48 | (vdft (lambda (pos_net neg_net) 49 | (xdft (difference (vtime 'tran pos_net) 50 | (vtime 'tran neg_net))))) 51 | (idft (lambda (node) (xdft (itime 'tran node)))) 52 | (getPwrX (lambda (connection scale_factor) 53 | (times scale_factor 54 | (abs (vdft (nth 0 connection) 55 | (nth 1 connection))) 56 | (abs (idft (nth 2 connection)))))) 57 | (getPwrSine (lambda (connection) (getPwrX connection 0.5))) 58 | (getPwrDC (lambda (connection) (getPwrX connection 1.0))) 59 | (pwrOutWatts (getPwrSine out)) 60 | (harmIdxs (qub::range ?start 1 ?stop nHarmonics+1 ?step 1)) 61 | (pwrOutWattsHarms 62 | (qub::lcmp (value pwrOutWatts (times x f0)) for x in harmIdxs)) 63 | (pwrOutWattsF0 (car pwrOutWattsHarms)) 64 | (pwrDc (qub::sum 65 | (qub::lcmp 66 | (abs (value (getPwrDC src) 0)) 67 | for src in supplies))) 68 | (gain (db10 (pwrOutWattsF0 / pwrInWatts))) 69 | (pae (times 100.0 (quotient (pwrOutWattsF0 - pwrInWatts) pwrDc))) 70 | (drain_efficiency (times 100.0 (quotient pwrOutWattsF0 pwrDc))) 71 | (winId (newWindow))) 72 | (if (or (drIsWaveform pae) (drIsParamWave pae)) 73 | (progn (plot gain drain_efficiency pae 74 | ?expr (list "Gain (dB)" "DE (%)" "PAE (%)")) 75 | (foreach idx (qub::range ?start 0 ?stop nHarmonics ?step 1) 76 | (plot (dbm (nth idx pwrOutWattsHarms)) 77 | ?expr (list (sprintf nil "Pout (dBm, %df0)" idx+1))))) 78 | (progn (printf "Pout (f0): %.2f dBm\n" (dBm pwrOutWattsF0)) 79 | (printf "PAE: %.2f%%\n" pae) 80 | (printf "DE: %.2f%%\n" drain_efficiency) 81 | (printf "Gain: %.2f dB\n" gain) 82 | (plot (dbm pwrOutWatts) ?expr (list "Pout")))))) -------------------------------------------------------------------------------- /src/ocean/outphasingSweep.ils: -------------------------------------------------------------------------------- 1 | ; Plot drain efficiency (DE) and power-added efficiency (PAE) for an amplifier 2 | ; where the two inputs are outphased. 3 | ; Assuming the simulation was done by sweeping the phase difference 4 | ; between the power of the two input ports, the DE and PAE will be plotted 5 | ; against the phase angle in radians and the output power back-off 6 | ; Args: 7 | ; outName: string 8 | ; Name of the output node 9 | ; 10 | ; inName: string 11 | ; Name of the input node 12 | ; vddName: string 13 | ; Instance name of the DC voltage source 14 | ; testType: string 15 | ; Either "hb" or "pss" for harmonic balance or periodic steady 16 | ; state simulations respectively. 17 | (defun qub::ocnOutphasingSweep (@key (outNode nil) 18 | (inNode1 nil) 19 | (inNode2 nil) 20 | (vddNode nil) 21 | (testType nil) 22 | (supplyNet nil) 23 | (outNet nil) 24 | (inNet1 nil) 25 | (inNet2 nil)) 26 | (letseq ((fundamentalHarmonicIndex 1) 27 | (dcHarmonicIndex 0) 28 | (gndNet 0) 29 | ; Determine simulation type 30 | ; Should only really be HB or PSS 31 | (simType (if (equal testType "hb") 32 | 'hb 33 | 'pss)) 34 | (simResultType (if (equal testType "hb") 35 | "hb_fd" 36 | "pss_fd")) 37 | (pvix 38 | (lambda (net node) 39 | (pvi simType net gndNet node gndNet fundamentalHarmonicIndex))) 40 | (outputPowerWatts (pvix outNet outNode)) 41 | (outputPowerDbm (dbm outputPowerWatts)) 42 | (inputPowerWatts1 (minus (pvix inNet1 inNode1))) 43 | (inputPowerWatts2 (minus (pvix inNet2 inNode2))) 44 | (vx (lambda (net) (v net ?result simResultType))) 45 | (ix (lambda (node) (i node ?result simResultType))) 46 | (supplyVoltage (vx supplyNet)) 47 | (supplyCurrent (ix vddNode)) 48 | (spectralDcPower (spectralPower supplyCurrent supplyVoltage)) 49 | (outCurrent (ix outNode)) 50 | (outVoltage (vx outNet)) 51 | (spectralOutputPower (spectralPower outCurrent outVoltage)) 52 | (inCurrent1 (ix inNode1)) 53 | (inVoltage1 (vx inNet1)) 54 | (spectralInputPower1 (spectralPower inCurrent1 inVoltage1)) 55 | (inCurrent2 (ix inNode2)) 56 | (inVoltage2 (vx inNet2)) 57 | (spectralInputPower2 (spectralPower inCurrent2 inVoltage2)) 58 | (paeNumerator (times 100.0 59 | (harmonic 60 | (plus spectralOutputPower 61 | spectralInputPower1 62 | spectralInputPower2) 63 | fundamentalHarmonicIndex))) 64 | (paeDenominator (minus (harmonic spectralDcPower dcHarmonicIndex))) 65 | (pae (quotient paeNumerator paeDenominator)) 66 | (drainEfficiencyNumerator 67 | (times 100.0 (harmonic spectralOutputPower 68 | fundamentalHarmonicIndex))) 69 | (drainEfficiency (quotient drainEfficiencyNumerator 70 | paeDenominator)) 71 | (backoff_simulated (difference outputPowerDbm 72 | (ymax outputPowerDbm)))) 73 | (printf "Assuming sim type: '%s'\n" simType) 74 | (plot outputPowerDbm 75 | drainEfficiency 76 | ?expr (list "Output Power (dBm)" "Drain Efficiency")) 77 | (awvSetXAxisLabel (currentWindow) "Phase Angle (rad)") 78 | (addSubwindow) 79 | (ocnYvsYplot ?wavex backoff_simulated 80 | ?wavey drainEfficiency 81 | ?titleList (list "Drain Efficiency (%)") 82 | ?colorList (list "y2")) 83 | (ocnYvsYplot ?wavex backoff_simulated 84 | ?wavey pae 85 | ?titleList (list "PAE (%)") 86 | ?colorList (list "y2")) 87 | (awvSetXAxisLabel (currentWindow) "Backoff"))) 88 | 89 | -------------------------------------------------------------------------------- /src/qtest/core.ils: -------------------------------------------------------------------------------- 1 | ; Represents the result of a single Test Case 2 | (defclass qtest:::Result () 3 | ; Result can be 'Pass or 'Fail 4 | ((result @initarg result 5 | @reader get_result 6 | @writer set_result) 7 | (msg @initarg msg 8 | @reader get_msg 9 | @writer set_msg) 10 | (inputs @initarg inputs 11 | @reader get_inputs 12 | @writer set_inputs) 13 | (evaluated_inputs @initarg evaluated_inputs 14 | @reader get_evaluated_inputs 15 | @writer set_evaluated_inputs))) 16 | 17 | (defmethod printself ((obj qtest:::Result)) 18 | (sprintf nil 19 | "Result: %s\n 20 | Message: %s\n 21 | Inputs: %A\n 22 | Evaluated Inputs: %A\n" 23 | (get_result obj) 24 | (get_msg obj) 25 | (get_inputs obj) 26 | (get_evaluated_inputs obj))) 27 | 28 | (defun qtest:::makeResult (result msg inputs evaluated_inputs) 29 | (makeInstance 'qtest:::Result 30 | ?result result 31 | ?msg msg 32 | ?inputs inputs 33 | ?evaluated_inputs evaluated_inputs)) 34 | 35 | ; Create function and return a list of its name and the function object. 36 | ; To be used inside qtest::TestRunner 37 | (defmacro qtest::TestCase (fn_name 38 | @key (skip nil) 39 | (expect_fail nil) 40 | @rest body) 41 | (if skip 42 | ; If we're skipping it then just create a function 43 | ; which returns a skipped result 44 | `(list ',fn_name 45 | (lambda () (qtest:::makeResult 'Skip "Test was skipped" () ()))) 46 | (if expect_fail 47 | ; If we expect a failure then wrap the function in a lambda 48 | ; that checks the output of the result 49 | ; If it was 'Fail then change it to 'ExpF (Expected Fail) 50 | `(list ',fn_name 51 | (lambda () 52 | (let ((result ,@body)) 53 | (if (eq 'Fail (get_result result)) 54 | ; Creating a new result object 55 | ; to show that the failure was expected 56 | (qtest:::makeResult 'ExpF 57 | (get_msg result) 58 | (get_inputs result) 59 | (get_evaluated_inputs result)) 60 | result)))) 61 | `(list ',fn_name (lambda () ,@body))))) 62 | 63 | ; Test runner. Automatically called by runTests 64 | ; Returns a list containing (fn name (symbol), qub::Result) 65 | (defun qtest::runTests (tests) 66 | (mapcar (lambda (x) (list (car x) (funcall (cadr x)))) tests)) 67 | 68 | ; Runs each of the tests defined in the body 69 | (defmacro qtest::TestSuite (let_lst @rest tests) 70 | `(let ,let_lst 71 | (list ,@tests))) 72 | 73 | ; Flatten all the suites into a list of tests 74 | ; Run each test and print the results 75 | (defun qtest::runSuites (@rest suites) 76 | (letseq ((tests (qub::joinLists suites)) 77 | (results (qtest::runTests tests)) 78 | (ntests (length tests)) 79 | (fails (setof x results (eq 'Fail (get_result (cadr x))))) 80 | (nfails (length fails)) 81 | (skipped (setof x results (eq 'Skip (get_result (cadr x))))) 82 | (nskipped (length skipped)) 83 | (expfails (setof x results (eq 'ExpF (get_result (cadr x))))) 84 | (nexpfails (length expfails)) 85 | (passed (setof x results (eq 'Pass (get_result (cadr x))))) 86 | (npassed (length passed))) 87 | (printf "%n of %n tests passed 88 | (%n failures) 89 | (%n skipped) 90 | (%n expected failures)\n" 91 | npassed 92 | ntests 93 | nfails 94 | nskipped 95 | nexpfails) 96 | (mapc (lambda (x) (printf "Test: %s\n%s\n" (car x) (cadr x))) fails) 97 | (printf "\n"))) 98 | 99 | ; Recursively look for all files prefixed with "test_" in a dir and load them 100 | (defun qtest::runAllTests (relPath) 101 | (letseq ((filesInDir (qub::listFileRec relPath)) 102 | (testFiles (qub::findFilesWithPrefix filesInDir "test_"))) 103 | (mapc (lambda (x) 104 | (printf "Running: %s\n" x) 105 | (load x)) 106 | testFiles))) -------------------------------------------------------------------------------- /src/geometry/point2d.ils: -------------------------------------------------------------------------------- 1 | ; Return the point with the greater y coordinate 2 | (defun qub::topPoint (a b) 3 | (if (greaterp (yCoord a) (yCoord b)) 4 | a 5 | b)) 6 | 7 | ; Return the point with the smaller y coordinate 8 | (defun qub::bottomPoint (a b) 9 | (if (lessp (yCoord a) (yCoord b)) 10 | a 11 | b)) 12 | 13 | ; Return the left-most point 14 | (defun qub::leftPoint (a b) 15 | (if (lessp (xCoord a) (xCoord b)) 16 | a 17 | b)) 18 | 19 | ; Return the right-most point 20 | (defun qub::rightPoint (a b) 21 | (if (greaterp (xCoord a) (xCoord b)) 22 | a 23 | b)) 24 | 25 | ; Is a's y coordinate greater than b's? 26 | (defun qub::higherp (a b) 27 | (greaterp (yCoord a) (yCoord b))) 28 | 29 | ; Move a point 30 | ; Args: 31 | ; Point: (list x:float y:float) 32 | ; Translation: (list dx:float dx:float) 33 | (defun qub::movePoint (point translation) 34 | (qub::addListElems point translation)) 35 | 36 | ; Move a point along its X axis 37 | ; Args: 38 | ; pt: (list x:float y:float) 39 | ; dist: float 40 | (defun qub::mvPtX (pt dist) 41 | (qub::movePoint pt dist:0)) 42 | 43 | ; Move a point along its Y axis 44 | ; Args: 45 | ; pt: (list x:float y:float) 46 | ; dist: float 47 | (defun qub::mvPtY (pt dist) 48 | (qub::movePoint pt 0:dist)) 49 | 50 | ; Find a point along the line formed between two points 51 | ; Args: 52 | ; p1: (list x:float y:float) 53 | ; p2: (list x:float y:float) 54 | ; distAlong: float 55 | ; The the point at a distance along the line from p1 to p2 56 | ; 0 would correspond to p1, 1 to p2, and 0.5 to the exact 57 | ; mid-point between the two 58 | (defun qub::midPoint (p1 p2 @key (distAlong 0.5)) 59 | (let ((p1Scalar distAlong) 60 | (p2Scalar (difference 1.0 distAlong))) 61 | (range 62 | (plus (times p1Scalar (xCoord p1)) (times p2Scalar (xCoord p2))) 63 | (plus (times p1Scalar (yCoord p1)) (times p2Scalar (yCoord p2)))))) 64 | 65 | ; Return the angle from one point to another 66 | ; Args: 67 | ; refPt: (list x:float y:float) 68 | ; targetPt: (list x:float y:float) 69 | (defun qub::getAngleToPt (refPt targetPt) 70 | (letseq ((d (mapcar difference targetPt refPt)) 71 | (dx (xCoord d)) 72 | (dy (yCoord d))) 73 | (qub::radToDeg (atan2 dy dx)))) 74 | 75 | ; Move a distance away from a point at an angle 76 | ; It assumes a polar plot i.e. east is 0 degrees 77 | ; Args: 78 | ; pt: (list x:float y:float) 79 | ; dist: float 80 | ; angle: float 81 | ; The angle is in degrees 82 | (defun qub::moveAtAngle (pt dist angle) 83 | (let ((dx (times dist (cos (qub::degToRad angle)))) 84 | (dy (times dist (sin (qub::degToRad angle))))) 85 | (qub::movePoint pt dx:dy))) 86 | 87 | ; Return the distance between two points 88 | ; Args: 89 | ; a: (list x:float y:float) 90 | ; b: (list x:float y:float) 91 | (defun qub::getDist (a b) 92 | (sqrt (plus 93 | (expt (difference (yCoord a) (yCoord b)) 2) 94 | (expt (difference (xCoord a) (xCoord b)) 2)))) 95 | 96 | ; Finds a point at a distance and angle from a reference point 97 | ; Args: 98 | ; angle: float 99 | ; The angle of the point from our reference 100 | ; The value is in degrees 101 | ; dist: float 102 | ; The distance of the point from the reference 103 | ; ref: (list float float) 104 | ; The reference point that we're moving away from 105 | (defun qub::getPoint (angle dist @key (ref 0.0:0.0)) 106 | (let ((x (xCoord ref)) 107 | (y (yCoord ref)) 108 | (dx (dist * (cos (qub::degToRad angle)))) 109 | (dy (dist * (sin (qub::degToRad angle))))) 110 | (range (x + dx) (y + dy)))) 111 | 112 | ; Rotate a point by an angle in degrees 113 | ; Args: 114 | ; refPt: (list float float) 115 | ; The reference point that we are rotating around 116 | ; pt: (list float float) 117 | ; The point we are rotating 118 | ; rotation: float 119 | ; The angle we are rotating by (degrees) 120 | ; Returns: 121 | ; The rotated point (list float float) 122 | (defun qub::rotatePoint (refPt pt rotation) 123 | (let ((distance (qub::getDist refPt pt)) 124 | (angle (qub::getAngleToPt refPt pt))) 125 | (qub::getPoint (plus angle rotation) distance ?ref refPt))) -------------------------------------------------------------------------------- /src/ocean/piNetwork.ils: -------------------------------------------------------------------------------- 1 | ; Plot the resistances and reactances of a passive network's Pi model 2 | ; Args: 3 | ; f0: float 4 | ; The frequency of the plot marker 5 | ; LC: bool 6 | ; Plot eqivalent series inductance/capacitance of the impedance 7 | ; ports: (list int int) 8 | ; The S-parameter index of the two ports under excitation 9 | ; zero_vals: bool 10 | ; Sets L and C values to zero if they're negative 11 | (defun qub::ocnPiNetwork (@key 12 | (f0 5.4G) 13 | (LC t) 14 | (ports (list 1 2)) 15 | (zero_vals t)) 16 | ; Y Network Parameters 17 | ; Assumed to be in the following form. 18 | ; 19 | ; O----Zz----O 20 | ; | | 21 | ; Zx Zy 22 | ; | | 23 | ; O----------O 24 | (letseq ((f (qub::sToF)) 25 | (p1 (car ports)) 26 | (p2 (cadr ports)) 27 | (w (times 2 qub::m.PI f)) 28 | (Y12 (ypm "sp" p1 p2)) 29 | (Y11 (ypm "sp" p1 p1)) 30 | (Y22 (ypm "sp" p2 p2)) 31 | (Zz (quotient -1.0 Y12)) 32 | (Zx (quotient 1.0 (plus Y11 Y12))) 33 | (Zy (quotient 1.0 (plus Y22 Y12))) 34 | (Xx (imag Zx)) 35 | (Xy (imag Zy)) 36 | (Xz (imag Zz)) 37 | (sRLCx (qub::ZToSeriesRLC Zx w)) 38 | (sRLCy (qub::ZToSeriesRLC Zy w)) 39 | (sRLCz (qub::ZToSeriesRLC Zz w)) 40 | (calcq (lambda (x r) (abs (quotient x r)))) 41 | (sQx (calcq Xx sRLCx->R)) 42 | (sQy (calcq Xy sRLCy->R)) 43 | (sQz (calcq Xz sRLCz->R)) 44 | (Yx (qub::ZToY Zx)) 45 | (Yy (qub::ZToY Zy)) 46 | (Yz (qub::ZToY Zz)) 47 | (pRLCx (qub::YToParallelRLC Yx w)) 48 | (pRLCy (qub::YToParallelRLC Yy w)) 49 | (pRLCz (qub::YToParallelRLC Yz w)) 50 | (setLC (lambda (x) 51 | (if zero_vals 52 | (qub::mapWaveform (lambda (x) (if (lessp x 0) 0 x)) x) 53 | x))) 54 | (winId (newWindow))) 55 | (addTitle "Coupled Coil Analysis") 56 | (addSubwindowTitle "Impedance") 57 | (plot Xx Xy Xz 58 | ?expr (list "Xx" "Xy" "Xz") 59 | ?yNumber (list 1 1 1)) 60 | (plot sRLCx->R sRLCy->R sRLCz->R 61 | ?expr (list "Rx" "Ry" "Rz") 62 | ?yNumber (list 2 2 2)) 63 | (plot sQx sQy sQz 64 | ?expr (list "sQx" "sQy" "sQz") 65 | ?yNumber (list 3 3 3)) 66 | (awvPlaceXMarker winId f0 ?subwindow 1) 67 | (addSubwindow) 68 | (addSubwindowTitle "Admittance") 69 | (plot (imag Yx) (imag Yy) (imag Yz) 70 | ?expr (list "Bx" "By" "Bz") 71 | ?yNumber (list 1 1 1)) 72 | (plot (real Yx) (real Yy) (real Yz) 73 | ?expr (list "Gx" "Gy" "Gz") 74 | ?yNumber (list 2 2 2)) 75 | (awvPlaceXMarker winId f0 ?subwindow 2) 76 | (when LC 77 | (let ((subwindowIdxZ (addSubwindow))) 78 | (addSubwindowTitle "Series RLC") 79 | (plot (setLC sRLCx->R) 80 | (setLC sRLCy->R) 81 | (setLC sRLCz->R) 82 | ?expr (list "Rx" "Ry" "Rz") 83 | ?yNumber (list 1 1 1)) 84 | (plot (setLC sRLCx->L) 85 | (setLC sRLCy->L) 86 | (setLC sRLCz->L) 87 | ?expr (list "Lx" "Ly" "Lz") 88 | ?yNumber (list 2 2 2)) 89 | (plot (setLC sRLCx->C) 90 | (setLC sRLCy->C) 91 | (setLC sRLCz->C) 92 | ?expr (list "Cx" "Cy" "Cz") 93 | ?yNumber (list 3 3 3)) 94 | (awvPlaceXMarker winId f0 ?subwindow subwindowIdxZ) 95 | (let ((subwindowIdxY (addSubwindow))) 96 | (addSubwindowTitle "Parallel RLC") 97 | (plot (setLC pRLCx->R) 98 | (setLC pRLCy->R) 99 | (setLC pRLCz->R) 100 | ?expr (list "Rx" "Ry" "Rz") 101 | ?yNumber (list 1 1 1)) 102 | (plot (setLC pRLCx->L) 103 | (setLC pRLCy->L) 104 | (setLC pRLCz->L) 105 | ?expr (list "Lx" "Ly" "Lz") 106 | ?yNumber (list 2 2 2)) 107 | (plot (setLC pRLCx->C) 108 | (setLC pRLCy->C) 109 | (setLC pRLCz->C) 110 | ?expr (list "Cx" "Cy" "Cz") 111 | ?yNumber (list 3 3 3)) 112 | (awvPlaceXMarker winId f0 ?subwindow subwindowIdxY)))))) -------------------------------------------------------------------------------- /src/qtest/test_assertions.ils: -------------------------------------------------------------------------------- 1 | ; A mini unit test for the qtest unit test framework 2 | ; It simply checks that the tests return a Pass, Fail or Skip as expected 3 | (letseq ((testCount 0) 4 | (num_tests 0) 5 | (num_fails 0) 6 | (assertPass (lambda (name test) 7 | (unless (eq 'Pass (get_result test)) 8 | (setq num_fails (add1 num_fails)) 9 | (printf "%s Failed.\n" name)) 10 | (setq num_tests (add1 num_tests)))) 11 | (assertFail (lambda (name test) 12 | (unless (eq 'Fail (get_result test)) 13 | (setq num_fails (add1 num_fails)) 14 | (printf "%s Passed (and shouldn't have).\n" 15 | name)) 16 | (setq num_tests (add1 num_tests)))) 17 | (assertSkip (lambda (name test) 18 | (unless (eq 'Skip (get_result test)) 19 | (setq num_fails (add1 num_fails)) 20 | (printf "%s wasn't skipped.\n" name)) 21 | (setq num_tests (add1 num_tests))))) 22 | ; assertEqual 23 | (assertPass 'equal_true (qtest::assertEqual 1 1)) 24 | (assertFail 'equal_false (qtest::assertEqual 1 2)) 25 | ; assertNotEqual 26 | (assertPass 'nequal_true (qtest::assertNotEqual 1 2)) 27 | (assertFail 'nequal_false (qtest::assertNotEqual 1 1)) 28 | ; assertTrue 29 | (assertPass 'asserttrue_true (qtest::assertTrue (equal 1 1))) 30 | (assertFail 'asserttrue_true (qtest::assertTrue (equal 1 2))) 31 | ; assertNil 32 | (assertPass 'assertnil_true (qtest::assertNil (equal 1 2))) 33 | (assertFail 'assertnil_false (qtest::assertNil (equal 1 1))) 34 | ; assertEq 35 | (assertPass 'assert_eq_true (qtest::assertEq 'this 'this)) 36 | (assertFail 'assert_eq_false (qtest::assertEq 'this 'that)) 37 | ; assertNotEq 38 | (assertPass 'assert_noteq_true (qtest::assertNotEq 'this 'that)) 39 | (assertFail 'assert_noteq_false (qtest::assertNotEq 'this 'this)) 40 | ; assertMember 41 | (assertPass 'assert_member_true (qtest::assertMember 1 (list 1 2 3))) 42 | (assertFail 'assert_member_false (qtest::assertMember 4 (list 1 2 3))) 43 | ; assertNotMember 44 | (assertPass 'assert_notmember_true (qtest::assertNotMember 4 (list 1 2 3))) 45 | (assertFail 'assert_not_member_false (qtest::assertNotMember 1 (list 1 2 3))) 46 | ; assertIsInstance 47 | (assertPass 'assert_isinstance_true (qtest::assertIsInstance 1 'fixnum)) 48 | (assertFail 'assert_isinstance_false (qtest::assertIsInstance 1 'flonum)) 49 | ; assertNotIsInstance 50 | (assertPass 'assert_notisinstance_true 51 | (qtest::assertNotIsInstance 1 'string)) 52 | (assertFail 'assert_notisinstance_false 53 | (qtest::assertNotIsInstance "2" 'string)) 54 | ; assertRaises 55 | (assertPass 'assert_raises_true (qtest::assertRaises (plus 1 "2"))) 56 | (assertFail 'assert_raises_false (qtest::assertRaises (plus 1 2))) 57 | ; assertAlmostEqual 58 | (assertPass 'assert_almost_equal_rel_true 59 | (qtest::assertAlmostEqual 100 104 ?rel_tol 0.05)) 60 | (assertFail 'assert_almost_equal_rel_false 61 | (qtest::assertAlmostEqual 100 106 ?rel_tol 0.05)) 62 | (assertPass 'assert_almost_equal_abs_true 63 | (qtest::assertAlmostEqual 100 104 ?abs_tol 5)) 64 | (assertFail 'assert_almost_equal_abs_false 65 | (qtest::assertAlmostEqual 100 106 ?abs_tol 5)) 66 | ; assertNotAlmostEqual 67 | (assertPass 'assert_not_almost_equal_rel_true 68 | (qtest::assertNotAlmostEqual 100 106 ?rel_tol 0.05)) 69 | (assertFail 'assert_not_almost_equal_rel_false 70 | (qtest::assertNotAlmostEqual 100 104 ?rel_tol 0.05)) 71 | (assertPass 'assert_not_almost_equal_abs_true 72 | (qtest::assertNotAlmostEqual 100 106 ?abs_tol 5)) 73 | (assertFail 'assert_not_almost_equal_abs_false 74 | (qtest::assertNotAlmostEqual 100 104 ?abs_tol 5)) 75 | ; Test skipped tests 76 | (letseq ((example_testcase (qtest::TestCase 'foo ?skip t ())) 77 | (example_result ((cadr example_testcase)))) 78 | (assertSkip 'assert_skipped_test example_result)) 79 | ; Test expected fail tests 80 | (letseq ((testcase (qtest::TestCase 'foo ?expect_fail t 81 | (qtest::assertEqual 1 2))) 82 | (result ((cadr testcase)))) 83 | (assertPass 'assert_expected_fail_works 84 | (qtest::assertEq 'ExpF (get_result result)))) 85 | (printf "Unit Test Self-Test Finished.\n%n of %n Passed\n\n" 86 | (difference num_tests num_fails) 87 | num_tests)) 88 | -------------------------------------------------------------------------------- /src/inductor_generator/symm_ind_pcell.ils: -------------------------------------------------------------------------------- 1 | (defclass qub::symmInductorPCell (qub::PcellParam) 2 | ((turns @initform (qub::defineParam "int" 2)) 3 | (width @initform (qub::defineParam "float" 10.0)) 4 | (underpassWidth @initform (qub::defineParam "underpassWidth" 10.0)) 5 | (spacing @initform (qub::defineParam "float" 5.0)) 6 | (apothem @initform (qub::defineParam "float" 50.0)) 7 | (nsides @initform (qub::defineParam "int" 8)) 8 | (metalStack @initform (qub::defineParam "string" "M1 M2")) 9 | (portLength @initform (qub::defineParam "float" "25.0")))) 10 | 11 | (defmethod qub::draw ((device qub::symmInductorPCell)) 12 | (letseq ((w (qub::getParamValue device 'width)) 13 | (underWidth (qub::getParamValue device 'underpassWidth)) 14 | (metalLayers (parseString (qub::getParamValue device 'metalStack))) 15 | (cv (slotValue device 'cvId)) 16 | (points 17 | (qub::getSymmetricSpiralPoints 18 | ?turns (qub::getParamValue device 'turns) 19 | ?width w 20 | ?spacing (qub::getParamValue device 'spacing) 21 | ?apothem (qub::getParamValue device 'apothem) 22 | ?nsides (qub::getParamValue device 'nsides) 23 | ?portLen (qub::getParamValue device 'portLength))) 24 | (objects nil) 25 | (mode "truncateExtend") 26 | (cp (lambda (pts metLyr width) 27 | (pushf (dbCreatePath cv 28 | (list metLyr) 29 | (mapcar qub::roundCoord5nm pts) 30 | width 31 | mode) 32 | objects))) 33 | (drawPts (lambda (pts metal width) (foreach p pts (cp p metal width))))) 34 | 35 | ; Generating main turns 36 | (foreach turn points->spiralCoords 37 | (cp turn->left (car metalLayers) w) 38 | (cp turn->right (car metalLayers) w)) 39 | 40 | ; Generating interconnects 41 | (drawPts points->intercons->topMetal (car metalLayers) w) 42 | (drawPts points->intercons->botMetal (cadr metalLayers) underWidth) 43 | (setq objects (leMergeShapes objects)) 44 | 45 | (letseq ((metalLayer (list (car metalLayers) "pin")) 46 | (f (lambda (x n) 47 | (leCreatePin cv 48 | metalLayer 49 | "rectangle" 50 | (list (qub::mvPtX x (w / 2)) 51 | (qub::movePoint x -(w / 2):w)) 52 | (sprintf nil "port%d" n) 53 | "inputOutput" 54 | (list "top" "bottom" "left" "right")) 55 | (dbCreateLabel cv 56 | metalLayer 57 | (qub::mvPtY x (w / 2)) 58 | (sprintf nil "port%d" n) 59 | "centerCenter" 60 | "R0" 61 | "stick" 62 | (w / 3))))) 63 | (f points->ports->left 0) 64 | (f points->ports->right 1)) 65 | 66 | ; Create fig group to organise the parts 67 | (let ((fg (dbCreateFigGroup cv "IndGroup" t 0.0:0.0 "R0"))) 68 | (mapc (lambda (obj) (dbAddFigToFigGroup fg obj)) objects) 69 | fg))) 70 | 71 | (defmacro qub::createSymmInductorPCell (@key (library nil) 72 | (defaultTopMetalLayer nil) 73 | (defaultTopMinusOneMetalLayer nil)) 74 | (letseq ((cellName "symmInd") 75 | (cellType "layout") 76 | (cellObj (ddGetObj library cellName))) 77 | ; Delete the cell if it doesn't exist 78 | ; As re-running the macro doesn't seem to refresh the PCell instance 79 | (if cellObj 80 | (ddDeleteObj cellObj)) 81 | `(pcDefinePCell 82 | (list (ddGetObj ,library) ,cellName ,cellType) 83 | ((turns "int" 2) 84 | (width "float" 10.0) 85 | (underpassWidth "float" 10.0) 86 | (spacing "float" 5.0) 87 | (apothem "float" 50.0) 88 | (nsides "int" 8) 89 | (metalStack "string" 90 | (strcat ,defaultTopMetalLayer 91 | " " 92 | ,defaultTopMinusOneMetalLayer)) 93 | (portLength "float" 25.0)) 94 | (let ((pcell (makeInstance 'qub::symmInductorPCell))) 95 | (qub::setPcellParams pcell pcCellView) 96 | (qub::draw pcell))))) 97 | 98 | -------------------------------------------------------------------------------- /src/qtest/assertions.ils: -------------------------------------------------------------------------------- 1 | (defun qtest:::assert (test 2 | quoted_inputs 3 | evaluated_inputs 4 | @key (msg "No msg.")) 5 | (if test 6 | (qtest:::makeResult 'Pass msg quoted_inputs evaluated_inputs) 7 | (qtest:::makeResult 'Fail msg quoted_inputs evaluated_inputs))) 8 | 9 | (defmacro qtest:::assertEqual (a b @key (msg "No msg.")) 10 | `(letseq ((x (gensym)) 11 | (y (gensym))) 12 | (setq x ,a) 13 | (setq y ,b) 14 | (qtest:::assert (qub::equal x y) 15 | (list ',a ',b) 16 | (list x y) 17 | ?msg ,msg))) 18 | 19 | (defmacro qtest:::assertNotEqual (a b @key (msg "No msg.")) 20 | `(letseq ((x (gensym)) 21 | (y (gensym))) 22 | (setq x ,a) 23 | (setq y ,b) 24 | (qtest:::assert (qub::nequal ,a ,b) 25 | (list ',a ',b) 26 | (list ,a ,b) 27 | ?msg ,msg))) 28 | 29 | (defmacro qtest:::assertTrue (x @key (msg "No msg.")) 30 | `(letseq ((a (gensym))) 31 | (setq a ,x) 32 | (qtest:::assert (not (null a)) 33 | (list ',x) 34 | (list a) 35 | ?msg ,msg))) 36 | 37 | (defmacro qtest:::assertNil (x @key (msg "No msg.")) 38 | `(letseq ((a (gensym))) 39 | (setq a ,x) 40 | (qtest:::assert (null a) 41 | (list ',x) 42 | (list a) 43 | ?msg ,msg))) 44 | 45 | (defmacro qtest:::assertEq (a b @key (msg "No msg.")) 46 | `(letseq ((x (gensym)) 47 | (y (gensym))) 48 | (setq x ,a) 49 | (setq y ,b) 50 | (qtest:::assert (eq x y) 51 | (list ',a ',b) 52 | (list x y) 53 | ?msg ,msg))) 54 | 55 | (defmacro qtest:::assertNotEq (a b @key (msg "No msg.")) 56 | `(letseq ((x (gensym)) 57 | (y (gensym))) 58 | (setq x ,a) 59 | (setq y ,b) 60 | (qtest:::assert (neq x y) 61 | (list ',a ',b) 62 | (list x y) 63 | ?msg ,msg))) 64 | 65 | (defmacro qtest:::assertMember (value lst @key (msg "No msg.")) 66 | `(letseq ((x (gensym)) 67 | (y (gensym))) 68 | (setq x ,value) 69 | (setq y ,lst) 70 | (qtest:::assert (member x y) 71 | (list ',value ',lst) 72 | (list x y) 73 | ?msg ,msg))) 74 | 75 | (defmacro qtest:::assertNotMember (value lst @key (msg "No msg.")) 76 | `(letseq ((x (gensym)) 77 | (y (gensym))) 78 | (setq x ,value) 79 | (setq y ,lst) 80 | (qtest:::assert (not (member x y)) 81 | (list ',value ',lst) 82 | (list x y) 83 | ?msg ,msg))) 84 | 85 | (defmacro qtest:::assertIsInstance (obj cls @key (msg "No msg.")) 86 | `(letseq ((x (gensym)) 87 | (y (gensym))) 88 | (setq x ,obj) 89 | (setq y ,cls) 90 | (qtest:::assert (classp x (findClass y)) 91 | (list ',obj ',cls) 92 | (list x y) 93 | ?msg ,msg))) 94 | 95 | (defmacro qtest:::assertNotIsInstance (obj cls @key (msg "No msg.")) 96 | `(letseq ((x (gensym)) 97 | (y (gensym))) 98 | (setq x ,obj) 99 | (setq y ,cls) 100 | (qtest:::assert (not (classp x (findClass y))) 101 | (list ',obj ',cls) 102 | (list x y) 103 | ?msg ,msg))) 104 | 105 | (defmacro qtest:::raised (fn) 106 | `(let ((exception_occured (gensym))) 107 | (setq exception_occured t) 108 | (catch t 109 | ,fn 110 | (setq exception_occured nil)) 111 | exception_occured)) 112 | 113 | (defmacro qtest:::assertRaises (fn @key (msg "No msg.")) 114 | `(qtest:::assert (qtest:::raised ,fn) 115 | (list ',fn) 116 | (list "Can't include it as it's meant to fail") 117 | ?msg ,msg)) 118 | 119 | (defmacro qtest:::assertAlmostEqual (a b @key (rel_tol 0) (abs_tol 0) (msg "No msg")) 120 | `(letseq ((x (gensym)) 121 | (y (gensym))) 122 | (setq x ,a) 123 | (setq y ,b) 124 | (qtest:::assert (qub::almostEqual x y ?rel_tol ,rel_tol ?abs_tol ,abs_tol) 125 | (list ',a ',b ',rel_tol ',abs_tol) 126 | (list x y ,rel_tol ,abs_tol) 127 | ?msg ,msg))) 128 | 129 | (defmacro qtest:::assertNotAlmostEqual (a b @key (rel_tol 0) (abs_tol 0) (msg "No msg")) 130 | `(letseq ((x (gensym)) 131 | (y (gensym))) 132 | (setq x ,a) 133 | (setq y ,b) 134 | (qtest:::assert (qub::notAlmostEqual x y ?rel_tol ,rel_tol ?abs_tol ,abs_tol) 135 | (list ',a ',b ',rel_tol ',abs_tol) 136 | (list x y ,rel_tol ,abs_tol) 137 | ?msg ,msg))) 138 | 139 | -------------------------------------------------------------------------------- /src/inductor_generator/shibata.ils: -------------------------------------------------------------------------------- 1 | ; apothem 2 | (defun genOctagon (a) 3 | (letseq ((angle 45.0) 4 | (sides 8.0) 5 | ; circumradius 6 | (cr (qub::apothemToCircumradius a sides))) 7 | (qub::lcmp 8 | ; 90 degrees is north 9 | ; Move by angle/2 clockwise so the shape is normal 10 | (qub::getPoint (difference 202.5 (times n angle)) cr) 11 | for n in (qub::range ?start 0 ?stop sides)))) 12 | 13 | ; width, spacing, apothem, number of shapes 14 | (defun genOctagons (w1 w2 s a n) 15 | (let ((sides 8) 16 | ; new apothem 17 | (na (lambda (w1 w2 s a n) 18 | (plus (plus a (quotient w1 2.0)) 19 | (times n (plus w1 s w2 s)))))) 20 | (qub::lcmp 21 | (genOctagon (na w1 w2 s a n)) 22 | for n in (qub::range ?start 0 ?stop n)))) 23 | 24 | ; Flatten octagons 25 | (defun getPointsFromOctagons (octs) 26 | (letseq ((nshapes (length octs)) 27 | (pts nil) 28 | (lastShape (nth (difference nshapes 1) octs))) 29 | (for shapeN 0 (difference nshapes 2) 30 | (letseq ((thisShape (nth shapeN octs)) 31 | (nextShape (nth (plus shapeN 1) octs)) 32 | (intersectionPoint 33 | (qub::findIntersectPoint (nth 6 thisShape) 34 | (nth 7 thisShape) 35 | (nth 0 nextShape) 36 | (nth 7 nextShape)))) 37 | (foreach pt thisShape 38 | (setq pts (cons pt pts))) 39 | (setq pts (cons intersectionPoint pts)))) 40 | (setq pts (cons (nth 0 lastShape) pts)) 41 | (setq pts (cons (nth 1 lastShape) pts)) 42 | pts)) 43 | 44 | ; a = apothem 45 | (defun drawShibata (w1 w2 s a trns) 46 | (letseq ((curWin (getCurrentWindow)) 47 | (cvId (geGetEditCellView curWin)) 48 | (layer (list "AP" "drawing")) 49 | ; We need to generate an extra shape 50 | ; as we're going to plot the first two points 51 | (octagons (genOctagons w1 w2 s a (plus trns 1.0))) 52 | (pts (reverse (getPointsFromOctagons octagons))) 53 | (mainPath (rodCreatePath 54 | ?name "Master Path" 55 | ?cvId cvId 56 | ?layer layer 57 | ?width w1 58 | ?justification 'center 59 | ; List of points to be drawn 60 | ?pts pts 61 | ; Contains a list of subpath-lists 62 | ?offsetSubPath (list (list ?layer layer 63 | ?width w2 64 | ?sep s 65 | ?justification 'left 66 | ?beginOffset 0)))) 67 | ; Make the final point of the outer coil 68 | ; Go a bit further north than the top of the bbox 69 | (lastInner (mainPath->endLast)) 70 | (pathSeparation (plus (quotient w1 2.0) (quotient w2 2.0) s)) 71 | (lastOuter (qub::mvPtX lastInner (minus pathSeparation))) 72 | (coilTop (topEdge mainPath->mppBBox)) 73 | (finalOuter (list (car lastOuter) (plus coilTop w1 w1 s))) 74 | (extensionPts (list lastOuter finalOuter)) 75 | (outExtensionPath (rodCreatePath 76 | ?name "Outer Extension" 77 | ?cvId cvId 78 | ?layer layer 79 | ?width w2 80 | ?justification 'center 81 | ?pts extensionPts)) 82 | ; Make final points for the inner path 83 | ; This will have to be tuned by the designer afterwards 84 | (lastOct (qub::lastAtom octagons)) 85 | (lastInnerLoopPt (qub::mvPtX (nth 3 lastOct) (times w1 4.0))) 86 | (extPts (append (qub::takeN 4 lastOct) (list lastInnerLoopPt))) 87 | (inExtPath (rodCreatePath 88 | ?name "Inner Extension" 89 | ?cvId cvId 90 | ?layer layer 91 | ?width w1 92 | ?justification 'center 93 | ?pts extPts)) 94 | ; Make an underpass to the inner points 95 | (firstInnerPoint mainPath->start0) 96 | ; We now add in the M8 bridge 97 | (underLayer (list "M8" "drawing")) 98 | (bridgeRightStart (qub::mvPtY firstInnerPoint (quotient w1 2.0))) 99 | (bridgeRightEnd 100 | (list (xCoord bridgeRightStart) 101 | (difference (bottomEdge mainPath->mppBBox) s w1))) 102 | (bridgeLeftStart (qub::mvPtX bridgeRightStart 103 | (minus pathSeparation))) 104 | (bridgeLeftEnd (qub::mvPtX bridgeRightEnd 105 | (minus pathSeparation))) 106 | (underBridgeRight (rodCreatePath 107 | ?name "Under path Right" 108 | ?cvId cvId 109 | ?layer underLayer 110 | ?width (min w1 12.0) 111 | ?justification 'center 112 | ?pts (list bridgeRightStart bridgeRightEnd))) 113 | (underBridgeLeft (rodCreatePath 114 | ?name "Under path Left" 115 | ?cvId cvId 116 | ?layer underLayer 117 | ?width (min w2 12.0) 118 | ?justification 'center 119 | ?pts (list bridgeLeftStart bridgeLeftEnd))) 120 | (tf (techGetTechFile cvId))) 121 | mainPath)) -------------------------------------------------------------------------------- /src/geometry/shapes.ils: -------------------------------------------------------------------------------- 1 | ; Convert an apothem to a circumradius 2 | ; Args: 3 | ; apothem: float 4 | ; num_sides: int or float 5 | (defun qub::apothemToCircumradius (apothem num_sides) 6 | (quotient apothem (cos (quotient qub::m.PI num_sides)))) 7 | 8 | ; Find the internal angle of a regular polygon 9 | ; Args: 10 | ; sides: int or float 11 | (defun qub::internalAngle (sides) 12 | (quotient 360.0 sides)) 13 | 14 | ; Generate the points of an octagon with a given apothem 15 | ; Args: 16 | ; a: float 17 | ; The apothem of the octagon 18 | (defun qub::genOctagon (a) 19 | (letseq ((angle 45.0) 20 | (sides 8.0) 21 | ; circumradius 22 | (cr (qub::apothemToCircumradius a sides))) 23 | (qub::lcmp 24 | ; Want the first point to start at the bottom left 25 | ; Move by angle/2 clockwise to do this 26 | ; Polar co-ordinates are used so 90 degrees is north, 0 degrees east etc 27 | (qub::getPoint (difference 202.5 (times n angle)) cr) 28 | for n in (qub::range ?start 0 ?stop sides)))) 29 | 30 | ; Generate a list of octagons 31 | ; Args: 32 | ; w: float 33 | ; Width of octagon line 34 | ; s: float 35 | ; Spacing between the inner octagon's outer edge and the outer octagon's 36 | ; inner edge 37 | ; a: float 38 | ; Apothem of the innermost octagon 39 | ; n: int or float 40 | ; The number of octagons to be generated 41 | (defun qub::genOctagons (w s a n) 42 | ; For each octagon, calculate a new apothem and generate the co-ordinates 43 | (mapcar (lambda (i) (qub::genOctagon (a + w/2.0 + i*(w+s)))) 44 | (qub::range ?start 0 ?stop n))) 45 | 46 | ; Create a coil from a list of octagons 47 | ; Args: 48 | ; shapes: list of octagons which are generated from qub::genOctagons 49 | ; nSegs: int 50 | ; Number of segments of the shape 51 | (defun qub::octagonsToCoil (shapes nSegs) 52 | (letseq ((inList (qub::joinLists shapes)) 53 | ; Create the output list with the first point added 54 | (outList (list (nth 0 inList))) 55 | ; A counter for the required segments 56 | (segCount 0)) 57 | ; Iterate until we have all of the points 58 | (while (neq segCount nSegs) 59 | ; Add the new point to the end of the out list 60 | (qub::pushEnd (nth (plus 1 segCount) inList) outList) 61 | ; Increment the counters 62 | (setq segCount (segCount + 1)) 63 | ; If we've reached the end of a shape 64 | ; Add the connecting bridge to the next shape 65 | ; Need to substract the index of the current shape 66 | ; Due to how we treat segments 67 | ; Coordinates of the final segment are 7 15 23 etc 68 | (if (zerop (mod (segCount + 1) 8)) 69 | (letseq ((currentPoint (nth segCount inList)) 70 | (previousPoint (nth (segCount - 1) inList)) 71 | (nextPoint (nth (segCount + 1) inList)) 72 | (lastPointNextShape (nth (segCount + 8) inList)) 73 | (newPoint (qub::findIntersectPoint previousPoint 74 | currentPoint 75 | nextPoint 76 | lastPointNextShape))) 77 | ; Find the intersection point between two lines 78 | ; (1) The last two points of the current shape 79 | ; This is a horizontal line. 80 | ; (2) The first and last points of the next shape 81 | ; This is a diagonal line. 82 | (qub::pushEnd newPoint outList)) 83 | ; Otherwise do nothing 84 | nil)) 85 | outList)) 86 | 87 | ; Find the co-ordinate for a polygon being generated. 88 | ; This is to be used with spiral generation so many of the parameters 89 | ; assume you're creating nested shapes that you connect together into a spiral. 90 | ; The co-ordinates are for the middle of the line, assuming you're making a 2D 91 | ; path where it has width in addition to length. 92 | ; 93 | ; 0 = half of the internal angle e.g. 45 for a square (??) 94 | ; 1 = next point anticlockwise = 135 for a square (??) 95 | ; 96 | ; Args: 97 | ; polyIdx: integer 98 | ; The index for our current polygon. 99 | ; e.g. polyIdx = 0 means that we're in the centre-most polygon 100 | ; polyIdx=1 would mean that we're in the next shape that encloses it 101 | ; pointIdx: integer 102 | ; The index of the current shape's co-ordinate 103 | ; The upper limit of the index is based on the shape 104 | ; e.g. square shapes would range from 0-3, pentagons 0-4, 105 | ; hexagons 0-5, etc 106 | ; nSides: integer 107 | ; The number of sides of the polygon 108 | ; circumradius: float 109 | ; Distance from the centre to the middle of a side 110 | ; spacing: float 111 | ; The distance between adjacent paths 112 | ; This is assuming each shape forms a 2D path with width 113 | ; width: float 114 | ; The width of the path formed by the polygon 115 | ; ref: (list float float) 116 | ; Reference co-ordinate for the centre of the shape 117 | ; rotation_anticlockwise: float 118 | ; The rotation of the shape 119 | (defun qub::getPolyPoint (polyIdx 120 | pointIdx 121 | nsides 122 | circumradius 123 | spacing 124 | width 125 | @key 126 | (ref 0.0:0.0) 127 | (rotation_anticlockwise 0.0)) 128 | (let ((a2c qub::apothemToCircumradius) 129 | (fa qub::fixAngle) 130 | (ia qub::internalAngle) 131 | (n nsides)) 132 | (let ((d (circumradius + (polyIdx * ((a2c spacing n) + (a2c width n))))) 133 | (angle (plus 134 | (difference 90.0 ((ia nsides) / 2.0)) 135 | rotation_anticlockwise 136 | (pointIdx * (ia nsides))))) 137 | (qub::getPoint (fa angle) d ?ref ref)))) 138 | 139 | 140 | -------------------------------------------------------------------------------- /src/inductor_generator/symmetric_inductor.ils: -------------------------------------------------------------------------------- 1 | ;;;; Module for generating a spiral inductor 2 | 3 | ; Extends the middle sections so they're closer together 4 | ; _ _ 5 | ; / \ 6 | ; | | 7 | ; 8 | ; versus 9 | ; 10 | ; ___ ___ 11 | ; / \ 12 | ; | | 13 | (defmacro qub:::xtendHelper (fn1 x fn2 y p) 14 | `(unless (equal (,fn1 ,x) (,fn2 ,y)) 15 | (,p (,fn1 ,x) ,y))) 16 | 17 | (defun qub::extendMiddle (turn width nturns turnIdx) 18 | (letseq ((left (qub::sortTopPoint turn->left)) 19 | (right (qub::sortTopPoint turn->right)) 20 | (getPort 21 | (lambda (left right) 22 | (let ((mp (qub::midPoint left right ?distAlong 0.5))) 23 | (list (qub::rightPoint left (qub::mvPtX mp -width)) 24 | (qub::leftPoint right (qub::mvPtX mp width)))))) 25 | (topPorts (getPort (car left) (car right))) 26 | (bottomPorts (getPort (qub::lastAtom left) (qub::lastAtom right))) 27 | (mutLast (lambda (lst obj) (setcar (last lst) obj)))) 28 | (mapc (if (or (and (evenp nturns) (evenp turnIdx)) 29 | (and (oddp nturns) (oddp turnIdx))) 30 | (lambda (p) (mutLast p (qub::mvPtX (cadr p) (width * -0.2)))) 31 | (lambda (p) (setcar p (qub::mvPtX (car p) (width * 0.2))))) 32 | (list topPorts bottomPorts)) 33 | 34 | ; Making sure that there are no duplicate coordinates added 35 | (qub:::xtendHelper car topPorts car left pushf) 36 | (qub:::xtendHelper cadr topPorts car right pushf) 37 | (qub:::xtendHelper car bottomPorts qub::lastAtom left qub::pushEnd) 38 | (qub:::xtendHelper cadr bottomPorts qub::lastAtom right qub::pushEnd) 39 | (list nil 'left left 'right right))) 40 | 41 | (defun qub::getBridgePoints (getPoint bridgePoint maxPointIdx maxHalfPointIdx) 42 | (let ((gp (lambda (x) (getPoint 0 x))) 43 | (x maxPointIdx) 44 | (y maxHalfPointIdx)) 45 | (if (eq bridgePoint 'bottom) 46 | (mapcar gp (list 1 0 x (x - 1))) 47 | (mapcar gp (list (y - 1) y (y + 1) (y + 2)))))) 48 | 49 | (defun qub::invertSide (side) 50 | (if (eq side 'top) 51 | 'bottom 52 | 'top)) 53 | 54 | (defun qub::addPorts (points width portLength) 55 | (letseq ((left (qub::lastAtom points)->left) 56 | (right (qub::lastAtom points)->right) 57 | (y (yCoord (qub::lastAtom left))) 58 | (porty (y - portLength - (width / 2))) 59 | (endLeft -width:porty) 60 | (endRight width:porty)) 61 | (qub::pushEnd -width:y left) 62 | (qub::pushEnd endLeft left) 63 | (qub::pushEnd width:y right) 64 | (qub::pushEnd endRight right) 65 | (list nil 'points points 'ports (list nil 'left endLeft 'right endRight)))) 66 | 67 | (defun qub:::getInterconnectsHelper (side refPts i width) 68 | (letseq ((f (if (eq side 'top) 69 | qub::sortTopPoint 70 | qub::sortBottomPoint)) 71 | (innerPts (nth i refPts)) 72 | (outerPts (nth (i + 1) refPts)) 73 | (getDist (lambda (x) (qub::getDist (car x) (cadr x)))) 74 | (a2p (lambda (x) (qub::getAngleToPt (car x) (cadr x)))) 75 | (getLst 76 | (lambda (leftPts rightPts) 77 | (let ((points nil) 78 | (maa (lambda (p a) 79 | (qub::moveAtAngle (car p) (min width (getDist p)) a)))) 80 | (pushf (maa leftPts (a2p leftPts)) points) 81 | (pushf (car leftPts) points) 82 | (pushf (car rightPts) points) 83 | (pushf (maa rightPts (a2p rightPts)) points) 84 | points))) 85 | (il2or (getLst (f innerPts->left) (f outerPts->right))) 86 | (ol2ir (getLst (f outerPts->left) (f innerPts->right)))) 87 | (if (eq side 'top) 88 | (list nil 'top ol2ir 'bot il2or) 89 | (list nil 'top il2or 'bot ol2ir)))) 90 | 91 | (defun qub::getInterconnects (width points getbridgepoint turns) 92 | (let ((pts (list nil 'topMetal nil 'botMetal nil)) 93 | ; Outer loop connects to nothing 94 | (maxPtIdx ((length points) - 2)) 95 | (side (if (evenp turns) 96 | 'top 97 | 'bottom))) 98 | (pushf (getbridgepoint) pts->topMetal) 99 | (for i 0 maxPtIdx 100 | (let ((interconnects (qub:::getInterconnectsHelper side points i width))) 101 | (pushf interconnects->top pts->topMetal) 102 | (pushf interconnects->bot pts->botMetal)) 103 | (setq side (qub::invertSide side))) 104 | pts)) 105 | 106 | ; Arranges the list so the upper point is the car 107 | ; The order of the points is unchanged 108 | (defun qub::sortTopPoint (points) 109 | (if (qub::higherp (car points) (qub::lastAtom points)) 110 | points 111 | (reverse points))) 112 | 113 | (defun qub::sortBottomPoint (pts) 114 | (reverse (qub::sortTopPoint pts))) 115 | 116 | (defun qub::getSymmetricSpiralPoints (@key 117 | (turns 2) (width 10.0) (spacing 5.0) (apothem 50.0) (nsides 8) (portLen 25.0)) 118 | (letseq ((atc qub::apothemToCircumradius) 119 | (getPoint 120 | (lambda (polyIdx pointIdx) 121 | (qub::getPolyPoint 122 | polyIdx 123 | pointIdx 124 | nsides 125 | (atc (plus apothem (width / 2.0)) nsides) 126 | spacing 127 | width 128 | ?rotation_anticlockwise 45.0))) 129 | (maxPointIdx (nsides - 1)) 130 | (maxTurnIdx (turns - 1)) 131 | (maxHalfPointIdx ((nsides / 2) - 1)) 132 | (gbp 133 | (lambda () 134 | (qub::getBridgePoints 135 | getPoint 136 | (if (evenp turns) 137 | 'top 138 | 'bottom) 139 | maxPointIdx 140 | maxHalfPointIdx))) 141 | (spiralCoords nil)) 142 | (for turnIdx 0 maxTurnIdx 143 | (let ((turn (list nil 'left nil 'right nil))) 144 | (for pIdx 0 maxHalfPointIdx 145 | (pushf (getPoint turnIdx pIdx) turn->left) 146 | (pushf (getPoint turnIdx (maxPointIdx - pIdx)) turn->right)) 147 | (pushf turn spiralCoords))) 148 | ; Reverse to fix the order of the spiral coords 149 | ; Add extra metal to reduce the gap between each half of the turns 150 | (setq spiralCoords 151 | (mapcar (lambda (trn i) (qub::extendMiddle trn width turns i)) 152 | (reverse spiralCoords) 153 | (qub::range ?stop (length spiralCoords)))) 154 | 155 | (let ((i (qub::getInterconnects width spiralCoords gbp turns)) 156 | (x (qub::addPorts spiralCoords width portLen))) 157 | (list nil 'spiralCoords x->points 158 | 'intercons i 159 | 'ports x->ports)))) 160 | -------------------------------------------------------------------------------- /src/ocean/coupledCoil.ils: -------------------------------------------------------------------------------- 1 | ; A two port measurement that uses both the Y and Z parameters of a coupled 2 | ; coil to get both its T and Pi representations. 3 | ; 4 | ; 5 | ; Args: 6 | ; f0: float 7 | ; The frequency for the plot markers 8 | ; plotMode: symbol (Either 'all, 'T, or 'Pi) 9 | ; ports: (list integer integer) 10 | ; The indexes of the two ports of the coil 11 | (defun qub::ocnCoupledCoil (@key (f0 nil) 12 | (plotMode 'all) 13 | (ports (list 1 2))) 14 | (letseq ((f (qub::sToF)) 15 | (w (times 2 qub::m.PI f)) 16 | (port1 (car ports)) 17 | (port2 (cadr ports)) 18 | ; T Network representation using Z-Parameters. 19 | ; T network is assumed to be in the following form. 20 | ; 21 | ; P1 O---Za-----Zb---O P2 22 | ; | 23 | ; Zc 24 | ; | 25 | ; Gnd O---------------O Gnd 26 | (Z11 (zpm "sp" port1 port1)) 27 | (Z12 (zpm "sp" port1 port2)) 28 | (Z22 (zpm "sp" port2 port2)) 29 | (Za (difference Z11 Z12)) 30 | (Zb (difference Z22 Z12)) 31 | (Zc Z12) 32 | (Ra (real Za)) 33 | (Rb (real Zb)) 34 | (Rc (real Zc)) 35 | (Xa (imag Za)) 36 | (Xb (imag Zb)) 37 | (Xc (imag Zc)) 38 | (Qa (quotient Xa Ra)) 39 | (Qb (quotient Xb Rb)) 40 | (Qc (quotient Xc Rc)) 41 | ; Delta increase 42 | (di (lambda (x y) 43 | (let ((delta (abs (difference x y)))) 44 | (times 100.0 (quotient delta (min x y)))))) 45 | (delta_Q_T_increase (di Qa Qb)) 46 | ; The branch inductaces 47 | (La (quotient Xa w)) 48 | (Lb (quotient Xb w)) 49 | (Lc (quotient Xc w)) 50 | (delta_L_T_increase (di La Lb)) 51 | ; The uncoupled branch inductances 52 | (Lax (quotient (imag Z11) w)) 53 | (Lbx (quotient (imag Z22) w)) 54 | ; M = Lc 55 | ; M = k*Lx 56 | ; k = M / Lx 57 | (Kac (quotient Lc Lax)) 58 | (Kbc (quotient Lc Lbx)) 59 | ; k = M / sqrt(Lax * Lbx) 60 | (K (quotient Lc (sqrt (times Lax Lbx)))) 61 | ; Y Network Parameters 62 | ; Assumed to be in the following form. 63 | ; 64 | ; P1 O----Zz----O P2 65 | ; | | 66 | ; Zx Zy 67 | ; | | 68 | ; Gnd O----------O Gnd 69 | (Y11 (ypm "sp" port1 port1)) 70 | (Y22 (ypm "sp" port2 port2)) 71 | (Y12 (ypm "sp" port1 port2)) 72 | (Zx (quotient 1.0 (plus Y11 Y12))) 73 | (Zy (quotient 1.0 (plus Y22 Y12))) 74 | (Zz (quotient -1.0 Y12)) 75 | (Rx (real Zx)) 76 | (Ry (real Zy)) 77 | (Rz (real Zz)) 78 | (Xx (imag Zx)) 79 | (Xy (imag Zy)) 80 | (Xz (imag Zz)) 81 | (Qx (quotient Xx Rx)) 82 | (Qy (quotient Xy Ry)) 83 | (Qz (quotient Xz Rz)) 84 | (delta_Q_Pi_increase (di Qx Qy)) 85 | (Lx (quotient Xx w)) 86 | (Ly (quotient Xy w)) 87 | (Lz (quotient Xz w)) 88 | (delta_L_Pi_increase (di Lx Ly)) 89 | ; Additional measurements for using the coil 90 | (top Rz**2 + Xz**2) 91 | (Rz_parallel (quotient top Rz)) 92 | (Xz_parallel (quotient top Xz)) 93 | (Lz_parallel (quotient Xz_parallel w)) 94 | ; To resonate out Lz_parallel 95 | (Cres (quotient 1.0 96 | (times (pow w 2.0) 97 | Lz_parallel))) 98 | ; Plotting the results 99 | (plotT (or (eq plotMode 'all) (eq plotMode 'T))) 100 | (plotPi (or (eq plotMode 'all) (eq plotMode 'Pi))) 101 | (winId (newWindow))) 102 | (addTitle "Coupled Coil Analysis") 103 | (addSubwindowTitle "Q Factors") 104 | (when plotT 105 | (plot Qa 106 | Qb 107 | Qc 108 | delta_Q_T_increase 109 | ?yNumber (list 1 1 1 2) 110 | ?expr (list "Qa (T)" 111 | "Qb (T)" 112 | "Qc (T-Virtual)" 113 | "QaQb Imbalance (%)"))) 114 | (when plotPi 115 | (plot Qx 116 | Qy 117 | Qz 118 | delta_Q_Pi_increase 119 | ?yNumber (list 1 1 1 2) 120 | ?expr (list "Qx (Pi)" 121 | "Qy (Pi)" 122 | "Qz (Pi-Virtual)" 123 | "QxQy Imbalance (%)"))) 124 | (addSubwindow) 125 | (addSubwindowTitle "Resistances") 126 | (when plotT (plot Ra Rb Rc ?expr (list "Ra (T)" "Rb (T)" "Rc (T-Virtual)"))) 127 | (when plotPi 128 | (plot Rx Ry Rz ?expr (list "Rx (Pi)" "Ry (Pi)" "Rz (Pi-Virtual)"))) 129 | (addSubwindow) 130 | (addSubwindowTitle "Inductances") 131 | (when plotT (plot La 132 | Lax 133 | Lb 134 | Lbx 135 | Lc 136 | delta_L_T_increase 137 | ?yNumber (list 1 1 1 1 1 2) 138 | ?expr (list "La (T)" 139 | "Lax (T)" 140 | "Lb (T)" 141 | "Lbx (T)" 142 | "Lc (T-Virtual)" 143 | "LaLb Imbalance (%)"))) 144 | (when plotPi (plot Lx 145 | Ly 146 | Lz 147 | Lc 148 | delta_L_Pi_increase 149 | ?yNumber (list 1 1 1 1 2) 150 | ?expr (list "Lx (Pi)" 151 | "Ly (Pi)" 152 | "Lz (Pi-Virtual)" 153 | "Lc (M)" 154 | "LxLy L (%)"))) 155 | (plot K ?yNumber (list 2) ?expr (list "K")) 156 | (when plotPi (progn (addSubwindow) 157 | (addSubwindowTitle "Miscellaneous") 158 | (plot Cres 159 | Lz_parallel 160 | Rz_parallel 161 | ?expr (list "Cres" 162 | "Lz (Parallel RL)" 163 | "Rz (Parallel RL)") 164 | ?yNumber (list 1 2 3)))) 165 | (when (or (floatp f0) (integerp f0)) 166 | (progn (awvPlaceXMarker winId f0 ?subwindow 1) 167 | (awvPlaceXMarker winId f0 ?subwindow 2) 168 | (awvPlaceXMarker winId f0 ?subwindow 3) 169 | (when plotPi (awvPlaceXMarker winId f0 ?subwindow 4)))))) 170 | -------------------------------------------------------------------------------- /src/inductor_generator/frlan_segment.ils: -------------------------------------------------------------------------------- 1 | ; Create one of the sub-coils of a Frlan coil 2 | 3 | ; Convert a list of octagons into a frlan coil 4 | ; Args: 5 | ; shapes: (list (list (list float float))) 6 | ; A list of octagon shapes 7 | ; Each octagon is a list of floating point co-ordinate pairs 8 | ; nSegs: integer 9 | ; The number of segments to be joined together 10 | ; Returns: 11 | ; List of co-ordinates of the coil 12 | (defun qub::octagonsToFrlan (shapes nSegs) 13 | (letseq (; Join the shapes into a single list 14 | (inList (qub::joinLists shapes)) 15 | ; Create the output list with the first point added 16 | (outList (list (nth 0 inList))) 17 | ; A counter for the required segments 18 | (segCount 0) 19 | ; Index for the current segment 20 | (segIndex 1)) 21 | ; Loop until we have all of the points 22 | (while (neq segCount nSegs) 23 | (cond 24 | ; If we're moving to the next shape 25 | ; e.g. index 8 would be the first coordinate of the new shape 26 | ; then make a bridge and move to the next shape. 27 | ((zerop (mod segIndex 8)) 28 | (letseq ((lastPoint (nth (segIndex-1) inList)) 29 | (secondLastPoint (nth (segIndex-2) inList)) 30 | (thisPoint (nth segIndex inList)) 31 | (nextPoint (nth (segIndex+7) inList)) 32 | (bridgePoint (qub::findIntersectPoint lastPoint 33 | secondLastPoint 34 | thisPoint 35 | nextPoint))) 36 | ; Add the bridge point 37 | (qub::pushEnd bridgePoint outList) 38 | ; Add the new point 39 | (qub::pushEnd thisPoint outList) 40 | ; Increment the counts 41 | (setq segCount (segCount + 1)) 42 | (setq segIndex (segIndex + 1)))) 43 | ; If we're at the top right of the shape 44 | ; e.g. index 3 45 | ; then move to the next shape. 46 | ((eq 4 (mod segIndex 8)) 47 | (letseq ((lastPoint (nth (segIndex-1) inList)) 48 | (secondLastPoint (nth (segIndex-2) inList)) 49 | (thisPoint (nth (segIndex+7) inList)) 50 | (nextPoint (nth (segIndex+8) inList)) 51 | (bridgePoint (qub::findIntersectPoint lastPoint 52 | secondLastPoint 53 | thisPoint 54 | nextPoint))) 55 | ; Add the bridge point 56 | (qub::pushEnd bridgePoint outList) 57 | ; Add the new point 58 | (qub::pushEnd nextPoint outList) 59 | ; Increment the counts 60 | (setq segCount (segCount + 1)) 61 | (setq segIndex (segIndex + 9)))) 62 | ; Otherwise just add the new coordinate and increment the counts 63 | (t 64 | (progn (qub::pushEnd (nth segIndex inList) outList) 65 | (setq segCount (segCount + 1)) 66 | (setq segIndex (segIndex + 1)))))) 67 | outList)) 68 | 69 | 70 | (defclass qub::frlanSegmentPCell (qub::PcellParam) 71 | ((width @initform (qub::defineParam "float" 15.0)) 72 | (spacing @initform (qub::defineParam "float" 2.0)) 73 | (apothem @initform (qub::defineParam "float" 30.0)) 74 | (turns @initform (qub::defineParam "float" 3.0)) 75 | (metalLayer @initform (qub::defineParam "string" "AP")) 76 | (drawnWidth @initform (qub::defineParam "float" 15.0)))) 77 | 78 | (defmethod qub::draw ((device qub::frlanSegmentPCell)) 79 | (letseq ((width (qub::getParamValue device 'width)) 80 | (spacing (qub::getParamValue device 'spacing)) 81 | (apothem (qub::getParamValue device 'apothem)) 82 | (turns (qub::getParamValue device 'turns)) 83 | (metalLayer (qub::getParamValue device 'metalLayer)) 84 | (drawnWidth (qub::getParamValue device 'drawnWidth)) 85 | ; Metal Layer Purpose Pair 86 | (layerPP (list metalLayer "drawing")) 87 | ; Number of shapes to be generated 88 | (nShapes ((turns * 2) + 1)) 89 | ; List of octagon shapes 90 | (octagons (qub::genOctagons width spacing apothem nShapes)) 91 | ; Length of a segment relative to the turn 92 | (segLen (quotient 1.0 8.0)) 93 | ; Number of segments needed 94 | (nSegs (floor (quotient turns segLen))) 95 | ; Process the coordinates into a coil 96 | ; We add an extra segment so we can have a good spot 97 | ; for the IO pins 98 | (coords (qub::octagonsToFrlan octagons (1+nSegs))) 99 | ; Get the last two points (we added an extra one) 100 | ; and find the midpoint of the two of them 101 | ; Then set the last point as the midpoint 102 | (nCoords (length coords)) 103 | (lastPoint (nth (nCoords-1) coords)) 104 | (secondLastPoint (nth (nCoords-2) coords)) 105 | (midPoint (qub::midPoint lastPoint secondLastPoint)) 106 | ; Get the angle of the final two points 107 | ; Add 90 degrees for the output section 108 | (ang (qub::getAngleToPt secondLastPoint lastPoint)) 109 | (newAng (ang + 90.0)) 110 | (finalPoint (qub::moveAtAngle midPoint width newAng)) 111 | ; Get the midpoint of the first two points so it's easy to 112 | ; connect the insides together 113 | (firstPoint (nth 0 coords)) 114 | (secondPoint (nth 1 coords)) 115 | (initMidpoint (qub::midPoint firstPoint secondPoint))) 116 | ; Mutate the final value of the last and change it to the midpoint 117 | (setf (nth (nCoords-1) coords) midPoint) 118 | ; Mutate the first value with the midpoint 119 | (setf (nth 0 coords) initMidpoint) 120 | ; Add the output point 121 | (qub::pushEnd finalPoint coords) 122 | ; Draw the coil itself 123 | (rodCreatePath ?name "path" 124 | ?layer layerPP 125 | ?width drawnWidth 126 | ?pts coords))) 127 | 128 | (defmacro qub::createFrlanSegmentPCell (@key (library nil) 129 | (defaultMetalLayer nil)) 130 | (letseq ((cellName "frlanSegment") 131 | (cellType "layout") 132 | (cellObj (ddGetObj library cellName))) 133 | ; Delete the cell if it doesn't exist 134 | ; As re-running the macro doesn't seem to refresh the PCell instance 135 | (if cellObj 136 | (ddDeleteObj cellObj)) 137 | `(pcDefinePCell 138 | (list (ddGetObj ,library) ,cellName ,cellType) 139 | ((width "float" 15.0) 140 | (spacing "float" 2.0) 141 | (apothem "float" 30.0) 142 | (turns "float" 3.0) 143 | (metalLayer "string" ,defaultMetalLayer) 144 | (drawnWidth "float" 15.0)) 145 | (let ((pcell (makeInstance 'qub::frlanSegmentPCell))) 146 | (qub::setPcellParams pcell pcCellView) 147 | (qub::draw pcell))))) -------------------------------------------------------------------------------- /src/design_environment/schematic.ils: -------------------------------------------------------------------------------- 1 | ; Code taken (and shortened) from CCSgetHierNetName.il 2 | ; "SKILL utility to show hierarchical net name 3 | ; for a selected wire in a schematic" 4 | ; 5 | ; Args 6 | ; net: A net object 7 | ; window: A window object 8 | (defun qub::getHierNetName (net @key (win (getCurrentWindow))) 9 | (letseq ((netName (strcat "/" 10 | (buildString 11 | (qub::lcmp (dbGetMemName (car i)->name (cadr i)) 12 | for i in (geGetHierMemInst win)) 13 | "/")))) 14 | (geGetAdjustedPath win (if (netName=="/") 15 | (strcat netName net->name) 16 | (strcat netName "/" net->name))))) 17 | 18 | ; The bbox reported by the instance object is interfered with by the 19 | ; various labels on it 20 | ; This returns the bbox of the selection box in the schematic 21 | (defun qub::getSchematicInstanceBBox (instance) 22 | (letseq ((instance_shapes instance->master->shapes) 23 | (is_selection_box (lambda (x) (and (x->layerName=="instance") 24 | (x->objType=="rect")))) 25 | (selection_box (car (setof x instance_shapes (is_selection_box x))))) 26 | selection_box->bBox)) 27 | 28 | ; Get the full net string of an instance 29 | (defun qub::getInstTermNet (inst term_name) 30 | (letseq ((term (car (setof term inst->instTerms term->name==term_name)))) 31 | (qub::getHierNetName term->net))) 32 | 33 | (defun qub::generateSchPinName (terminal prefix_instance_name) 34 | (letseq ((terminal_name (car (parseString terminal->name "<"))) 35 | (label_prefix (if prefix_instance_name 36 | (sprintf nil "%s_" terminal->inst->baseName) 37 | "")) 38 | (num_bits terminal->numBits) 39 | (bus_text (if (num_bits > 1) 40 | (sprintf nil "<0:%d>" num_bits-1) 41 | "")) 42 | (pin_name (sprintf nil 43 | "%s%s%s" 44 | label_prefix 45 | terminal_name 46 | bus_text))) 47 | pin_name)) 48 | 49 | 50 | ; Create a wire stub for a terminal 51 | ; Cadence can do this but it fails for buses of instances 52 | ; This version fixes that issue 53 | ; e.g. If you have a terminal X on instance Y, if you have multiple Ys in 54 | ; parallel, e.g. Y<0:1>, then the wire stub should be X<0:1> but instead 55 | ; it will just be X 56 | ; 57 | ; Args 58 | ; terminal: The terminal object 59 | ; wire_length: 60 | ; stub_settings: 61 | ; A property list with the following properties: 62 | ; wire_length: 63 | ; Length of the new schematic wire 64 | ; prefix_instance_name: 65 | ; Add the name of the instance to the wire 66 | ; Done to avoid name clashes when different instances share 67 | ; the same pin name 68 | (defun qub::makeTerminalStub (terminal stub_settings) 69 | (letseq ((cell_view terminal->cellView) 70 | (instance terminal->inst) 71 | (wire_length stub_settings->wire_length) 72 | (terminal_pin (car terminal->term->pins)) 73 | (pin_bbox (geTransformUserBBox (car terminal_pin->figs)->bBox 74 | instance->transform)) 75 | (instance_untransformed_bbox (qub::getSchematicInstanceBBox instance)) 76 | (instance_bbox (geTransformUserBBox instance_untransformed_bbox 77 | instance->transform)) 78 | (pin_middle (centerBox pin_bbox)) 79 | (instance_middle (centerBox instance_bbox)) 80 | (pin_x (xCoord pin_middle)) 81 | (pin_y (yCoord pin_middle)) 82 | (pin_side (qub::bboxFindSide instance_bbox pin_middle)) 83 | (new_point 84 | (cond ((equal pin_side 'left) (pin_x-wire_length):pin_y) 85 | ((equal pin_side 'right) (pin_x+wire_length):pin_y) 86 | ((equal pin_side 'top) pin_x:(pin_y+wire_length)) 87 | ((equal pin_side 'bottom) pin_x:(pin_y-wire_length)))) 88 | (snap_spacing (envGetVal "schematic" "schSnapSpacing" 'float)) 89 | (font_height (envGetVal "schematic" "createLabelFontHeight" 'float)) 90 | (font_style (envGetVal "schematic" "createLabelFontStyle" 'cyclic)) 91 | (new_wire (car (schCreateWire cell_view 92 | "draw" 93 | "full" 94 | (list pin_middle new_point) 95 | snap_spacing 96 | snap_spacing 97 | 0.0))) 98 | (wire_center (centerBox new_wire->bBox)) 99 | (label_name 100 | (qub::generateSchPinName terminal 101 | stub_settings->prefix_instance_name))) 102 | (schCreateWireLabel cell_view 103 | new_wire 104 | (list (xCoord wire_center) 105 | (plus (yCoord wire_center) 106 | (times 2.0 font_height))) 107 | label_name 108 | "upperCenter" 109 | "R0" 110 | font_style 111 | font_height 112 | nil))) 113 | 114 | 115 | (defun qub::addStubsToInstance (instance stub_settings) 116 | (foreach terminal instance->instTerms 117 | (qub::makeTerminalStub terminal stub_settings))) 118 | 119 | (defun qub::addStubsToInstances (instances stub_settings) 120 | (foreach instance instances 121 | (qub::addStubsToInstance instance stub_settings))) 122 | 123 | (defun qub::createInstancePins (instance prefix_instance_name) 124 | t) 125 | 126 | (defun qub::createPinsFromInstance (instance 127 | x_coord 128 | y_coord 129 | y_delta 130 | prefix_instance_name) 131 | (letseq ((io_pin_master (dbOpenCellView "basic" "iopin" "symbolr" nil "r")) 132 | (cell_view instance->cellView) 133 | (inst_name instance->name) 134 | (cell_name instance->cellName) 135 | (terminals instance->instTerms)) 136 | (for i 0 (sub1 (length terminals)) 137 | (letseq ((terminal (nth i terminals)) 138 | (pin_name (qub::generateSchPinName terminal 139 | prefix_instance_name)) 140 | (pin (schCreatePin cell_view 141 | io_pin_master 142 | pin_name 143 | "inputOutput" 144 | nil 145 | (list x_coord (plus y_coord (times i y_delta))) 146 | "R0"))) 147 | (when pin 148 | (letseq ((snap_spacing (envGetVal "schematic" "schSnapSpacing" 'float)) 149 | (pin_shape_bbox 150 | (car (qub::lcmp x->bBox 151 | for x in io_pin_master->shapes 152 | if (equal x->purpose "drawing9")))) 153 | (pin_width (qub::bboxWidth pin_shape_bbox)) 154 | (pin_new_xy (list (difference (xCoord pin->xy) 155 | (quotient pin_width 3.0)) 156 | (yCoord pin->xy)))) 157 | (setf pin->xy pin_new_xy))))))) 158 | 159 | ; prefix_instance_name: bool 160 | (defun qub::createInstancesPinsAndStubs (instances prefix_instance_name) 161 | (letseq ((num_instances (length instances)) 162 | (x_start 0) 163 | (y_start 0) 164 | (x_delta 1) 165 | (y_delta 0.25) 166 | (wire_length 0.75)) 167 | (for i 0 (sub1 num_instances) 168 | (letseq ((instance (nth i instances)) 169 | (pin_x (plus x_start (times i x_delta)))) 170 | (qub::createPinsFromInstance instance 171 | pin_x 172 | y_start 173 | y_delta 174 | prefix_instance_name))) 175 | (qub::addStubsToInstances 176 | instances 177 | (list nil 178 | 'wire_length 179 | wire_length 180 | 'prefix_instance_name 181 | prefix_instance_name)))) 182 | 183 | ; prefix_instance_name: bool 184 | (defun qub::createSchematicPinsAndStubs (prefix_instance_name) 185 | (letseq ((cell_view (geGetEditCellView))) 186 | (qub::createInstancesPinsAndStubs cell_view->instances 187 | prefix_instance_name))) 188 | 189 | (defun qub::createSelectedInstancesPinsAndStubs (prefix_instance_name) 190 | (let ((instances (geGetSelSet))) 191 | (when instances 192 | (qub::createInstancesPinsAndStubs instances 193 | prefix_instance_name)))) 194 | 195 | (defun qub::createSelectedInstancesStubs (prefix_instance_name) 196 | (let ((instances (geGetSelSet)) 197 | (wire_length 0.75)) 198 | (when instances 199 | (qub::addStubsToInstances instances 200 | (list nil 201 | 'wire_length 202 | wire_length 203 | 'prefix_instance_name 204 | prefix_instance_name))))) 205 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # SKILL Tools 2 | 3 | Tools to ease working with SKILL and SKILL++ in Cadence Virtuoso. The main feature of interest is the unit testing framework in the folder `qtest`. 4 | This repo contains the code that I use regularly in my PhD so it's also available in case anyone finds any use for it. 5 | There's not a lot of SKILL code in the wild beyond the Cadence forums. 6 | 7 | ## Project Structure 8 | 9 | | Folder | Namespace | Purpose 10 | |---|---|---| 11 | | `circuits` | `qub` | Circuit analysis 12 | | `design_environment` | `qub` | Manipulating the Cadence Virtuoso design environment 13 | | `geometry` | `qub` | Geometry calculations 14 | | `ocean` | `qub` | Processing simulation results 15 | | `pcell` | `qub` | Generating custom PCells 16 | | `qtest` | `qtest` | Unit testing. 17 | | `std` | `qub` | A "standard library" of useful functions. 18 | | `inductor_generator` | `qub` | Inductor PCell generation. 19 | 20 | ## Importing the project 21 | 22 | Create the environment variable `QUB_CODE_REPO_DIR` and point it at the repo root directory. 23 | Add the `src` folder to the SKILL path and run `(load (strcat (getShellEnvVar "QUB_CODE_REPO_DIR") "/src/init.ils"))` and all other modules will be imported. 24 | The `src` folder can be added to the SKILL path like so: `(setSkillPath (append (getSkillPath) (list (strcat (getShellEnvVar "QUB_CODE_REPO_DIR") "/src/"))))`. 25 | If the inductor PCell code is to be used, this code library needs to be re-added using libInit.il for the Virtuoso library that the PCell resides in or else stream-out will fail. 26 | 27 | ## Unit Testing 28 | 29 | The unit testing framework is modelled after [Python's `unittest` module](https://docs.python.org/3/library/unittest.html) and provides a basic set of assertions for testing your code. 30 | 31 | ### Example Test 32 | 33 | The test is run by calling `load` on the file containing the test. 34 | 35 | ```lisp 36 | (qtest::runSuites 37 | (qtest::TestSuite ((f qub:::join_lists)) 38 | (qtest::TestCase join_three_lists 39 | (qtest::assertEqual (list "X" 2 3 4 5 6) 40 | (f (list (list 1 2) (list 3 4) (list 5 6)))))) 41 | (qtest::TestSuite ((f qub::flatten_list)) 42 | (qtest::TestCase normal_use 43 | (qtest::assertEqual (list 1 2 3 4 5 6 7) 44 | (f (list 1 2 (list 3 (list 4 (list 5 6) 7)))))))) 45 | ``` 46 | 47 | ```lisp 48 | (load "./code/src/std/test_lists.ils") 49 | 1 of 2 tests passed (1 failures) (0 skipped) (0 expected failures) 50 | Test: join_three_lists 51 | Result: Fail 52 | Message: No msg. 53 | Inputs: (list("X" 2 3 4 5 6) (f list(list(1 2) list(3 4) list(5 6)))) 54 | Evaluated Inputs: (("X" 2 3 4 5 6) (1 2 3 4 5 6)) 55 | ``` 56 | 57 | ## Main Functions and Macros 58 | 59 | ### `qtest::runAllTests` 60 | 61 | | Parameter(s) | Side Effect(s) | 62 | |-----------------|----------------| 63 | | Directory (`string`)|Calls `load` on all files prefixed with `test_` in the directory | 64 | 65 | Takes a folder path and loads all modules prefixed with test_. 66 | 67 | ### `qtest::TestCase` 68 | 69 | | Parameters(s)| Keyword Parameters | Output(s) 70 | |---|---|---| 71 | | Test Name (unquoted symbol) |`skip` (bool)| List of the test name symbol and the function object 72 | |body| `expect_fail` (bool) 73 | 74 | Represents a single test. Can contain any code but must return the result of an assertion function. The test name is contained in a list with the function object so tests don't pollute the top level namespace. 75 | 76 | #### Skipping a test 77 | 78 | To mark a test to be skipped, set the `skip` keyword argument to true. 79 | 80 | #### Marking a test that you expect to fail 81 | 82 | Set the `expect_fail` keyword argument to true. If the test passes it will count as a pass but if it fail it will not be recorded as a fail. 83 | 84 | ### `qtest::TestSuite` 85 | 86 | | Parameter(s) | Output(s) | 87 | |---|---| 88 | | Test Cases | List containing lists returned by `qtest::TestCase` | 89 | 90 | A collection of test cases. A suite should be used to test a single function or method. 91 | 92 | ### `qtest::runSuites` 93 | 94 | | Parameter(s) | Side Effect(s) | 95 | |-----------------|----------------| 96 | | Any number of Test Suites | Prints the results of the tests | 97 | 98 | Test suites should be written in the body of this macro. When the module file is loaded in Virtuoso, the tests are initialised and the results are printed in the CIW. 99 | 100 | ## Assertions 101 | 102 | Each assertion takes a keyword argument `msg` which is printed if the test fails. 103 | 104 | ### `qtest::assertEqual` 105 | 106 | | Parameter(s) | Keyword Parameters | Output(s) | 107 | |-----------------|-----------|---| 108 | | a | msg (`string`) | `qtest::Result` | 109 | | b 110 | 111 | 112 | 113 | Checks if two objects are equal. Uses the `qub::equal` method to allow you to implement equality for your own objects as well as numbers, strings etc. 114 | 115 | ### `qtest::assertNotEqual` 116 | 117 | | Parameter(s) | Keyword Parameters | Output(s) | 118 | |-----------------|-----------|---| 119 | | a | msg (`string`) | `qtest::Result` | 120 | | b 121 | 122 | 123 | Checks if two objects are not equal. Uses the `qub::notEqual` method to allow you to implement equality for your own objects as well as numbers, strings etc. 124 | 125 | ### `qtest::assertTrue` 126 | 127 | | Parameter(s) | Keyword Parameters | Output(s) | 128 | |-----------------|-----------|---| 129 | | a | msg (`string`) | `qtest::Result` | 130 | 131 | Checks that the argument is true. 132 | 133 | ### `qtest::assertNil` 134 | 135 | | Parameter(s) | Keyword Parameters | Output(s) | 136 | |-----------------|-----------|---| 137 | | a | msg (`string`) | `qtest::Result` | 138 | 139 | Checks that the argument is `nil`. 140 | 141 | ### `qtest::assertEq` 142 | 143 | | Parameter(s) | Keyword Parameters | Output(s) | 144 | |-----------------|-----------|---| 145 | | a | msg (`string`) | `qtest::Result` | 146 | | b 147 | 148 | 149 | Both arguments are the same object. 150 | 151 | ### `qtest::assertNotEq` 152 | 153 | | Parameter(s) | Keyword Parameters | Output(s) | 154 | |-----------------|-----------|---| 155 | | a | msg (`string`) | `qtest::Result` | 156 | | b 157 | 158 | The arguments are different objects. 159 | 160 | ### `qtest::assertMember` 161 | 162 | | Parameter(s) | Keyword Parameters | Output(s) | 163 | |-----------------|-----------|---| 164 | | value | msg (`string`) | `qtest::Result` | 165 | | list 166 | 167 | The object is a member of the list. 168 | 169 | ### `qtest::assertNotMember` 170 | 171 | | Parameter(s) | Keyword Parameters | Output(s) | 172 | |-----------------|-----------|---| 173 | | value | msg (`string`) | `qtest::Result` | 174 | | list 175 | 176 | 177 | 178 | The object is not a member of the list. 179 | 180 | ### `qtest::assertIsInstance` 181 | 182 | | Parameter(s) | Keyword Parameters | Output(s) | 183 | |-----------------|-----------|---| 184 | | Object | msg (`string`) | `qtest::Result` | 185 | | Quoted Class 186 | 187 | 188 | The object is an instance of the class. 189 | 190 | ### `qtest::assertNotIsInstance` 191 | 192 | | Parameter(s) | Keyword Parameters | Output(s) | 193 | |-----------------|-----------|---| 194 | | Object | msg (`string`) | `qtest::Result` | 195 | | Quoted Class 196 | 197 | 198 | The object is not an instance of the class. 199 | 200 | ### `qtest::assertRaises` 201 | 202 | | Parameter(s) | Keyword Parameters | Output(s) | 203 | |-----------------|-----------|---| 204 | | Function Call | msg (`string`) | `qtest::Result` | 205 | 206 | Checks that the expression raises an error using `error`. 207 | It may not catch other errors within the expression. 208 | 209 | ### `qtest::assertAlmostEqual` 210 | 211 | | Parameter(s) | Keyword Parameters | Output(s) | 212 | |-----------------|-----------|---| 213 | | a | msg (`string`) | `qtest::Result` | 214 | | b 215 | 216 | 217 | Checks that two floats are almost equal. Uses `qub::almostEqual` which is a port of Python's [`math.isclose`](https://docs.python.org/3/library/math.html#math.isclose) and uses the same optional arguments. 218 | 219 | ### `qtest::assertNotAlmostEqual` 220 | 221 | | Parameter(s) | Keyword Parameters | Output(s) | 222 | |-----------------|-----------|---| 223 | | a | msg (`string`) | `qtest::Result` | 224 | | b 225 | 226 | Checks that two floats are not almost equal. 227 | 228 | ## Standard Library Functions and Macros 229 | 230 | ### `qub::checkType` 231 | 232 | | Parameters | Side Effects | 233 | | --- | --- | 234 | | Object | Throws an error if the object is of the expected type. 235 | | Quoted Class Name 236 | 237 | For implementing run-time type checking in functions. 238 | 239 | ### `qub::equal` 240 | 241 | | Parameters | Result | 242 | | --- | --- | 243 | | a | Bool 244 | | b 245 | 246 | A generic function for implementing equality for your own types. 247 | 248 | ### `qub::nequal` 249 | 250 | | Parameters | Result | 251 | | --- | --- | 252 | | a | Bool 253 | | b 254 | 255 | A generic function for implementing inequality for your own types. 256 | 257 | ### `qub::allEqual` 258 | 259 | | Parameters | Result | 260 | | --- | --- | 261 | | `@rest values` | bool 262 | 263 | Checks if all values are equal. It uses `qub::equal` for checking equality. 264 | 265 | ### `qub::listFileRec` 266 | 267 | | Parameter | Result | 268 | |---|---| 269 | | Path (string) | List of files in the directory | 270 | 271 | Recursively searches the path for all files. It ignores all dotted directories. 272 | 273 | ### `qub::foldl` 274 | 275 | | Paramater | Result | 276 | |---|---| 277 | | fn(accumulator value) | The "folded" value 278 | | Initial Value 279 | | List of Values 280 | 281 | The [HaskellWiki](https://wiki.haskell.org/Fold) can probably explain this better than I could. 282 | 283 | ### `qub::foldr` 284 | 285 | The same as `qub::foldl` but it calls `reverse` on the list before it processes it. 286 | 287 | ### `qub::sum` 288 | 289 | Sums a list of numbers 290 | 291 | ### `qub:::lcmp` 292 | 293 | An implementation of python's list comprehensions. 294 | The predicate is optional. 295 | 296 | ```lisp 297 | >>>(qub:::lcmp (times x 2) for x in (list 1 2 3 4) if (evenp x)) 298 | (4 8) 299 | ``` 300 | 301 | ### `qub::joinLists` 302 | 303 | | Paramater | Result | 304 | | --- | --- | 305 | | List of lists | A single list of the values within the input lists | 306 | 307 | ### `qub::range` 308 | 309 | | Keyword parameters | Result | 310 | | --- | --- | 311 | | start | List of integers 312 | | stop 313 | | step 314 | 315 | A port of python's `range` function. 316 | 317 | ```lisp 318 | >>>(qub::range ?start 0 ?stop 20 ?step 3) 319 | (0 3 6 9 12 320 | 15 18 321 | ) 322 | ``` 323 | 324 | ### `qub::almostEqual` 325 | 326 | | Parameters | Keyword Parameters | Result | 327 | |---|---|---| 328 | | a |`rel_tol`| Bool 329 | | b | `abs_tol` 330 | 331 | Checks that two values are almost equal. This is a generic function and can be applied to your own types. The method specialised on numbers is a port of Python's [`math.isclose`](https://docs.python.org/3/library/math.html#math.isclose) and uses the same optional arguments. 332 | 333 | `rel_tol` is the relative tolerance. 0.05 would be a 5% relative tolerance. 334 | `abs_tol` is the absolute tolerance. 335 | 336 | 337 | 338 | ### `qub::startsWith` 339 | 340 | | Parameters | Result | 341 | | ---|---| 342 | | string | Bool 343 | | prefix (string) 344 | 345 | Checks if a string begins with a particular prefix 346 | -------------------------------------------------------------------------------- /src/inductor_generator/frlan.ils: -------------------------------------------------------------------------------- 1 | ; Creates a Frlan PCell 2 | (defclass qub::frlanPCell (qub::PcellParam) 3 | ((width @initform (qub::defineParam "float" 15.0)) 4 | (spacing @initform (qub::defineParam "float" 2.0)) 5 | (apothem @initform (qub::defineParam "float" 30.0)) 6 | (turns @initform (qub::defineParam "float" 2.0)) 7 | (topMetalLayers @initform (qub::defineParam "string" "LB")) 8 | (underpassMetalLayers @initform (qub::defineParam "string" "IB")) 9 | (drawConnectingBridge @initform (qub::defineParam "boolean" nil)) 10 | (inputConnectorType @initform (qub::defineParam "string" "None")) 11 | (drawSecondInput @initform (qub::defineParam "boolean" nil)))) 12 | 13 | ; Draw the PCell in the layout 14 | ; Args: 15 | ; device: qub::frlanPCell 16 | ; The instance the method is acting on 17 | ; pcCellView: cellview object 18 | ; The cellview of the PCell instance 19 | ; segLib: string 20 | ; The library that the frlanSegment PCell resides in 21 | (defmethod qub::draw ((device qub::frlanPCell) 22 | @key 23 | pcCellView 24 | segLib) 25 | (letseq ((libObj (ddGetObj segLib)) 26 | (tf (techGetTechFile libObj)) 27 | (gpv (lambda (valName) (qub::getParamValue device valName))) 28 | (width (gpv 'width)) 29 | (limitWidth (lambda (layer) (qub::limitPathWidth tf width layer))) 30 | (topMetalLayers (parseString (gpv 'topMetalLayers))) 31 | (underpassMetalLayers (parseString (gpv 'underpassMetalLayers))) 32 | (origin 0:0) 33 | (pathCellId 34 | (dbOpenCellViewByType segLib "frlanSegment" "layout")) 35 | (makeLoop 36 | (lambda (rotation instName metalLayer) 37 | (letseq ((newWidth (limitWidth metalLayer)) 38 | (params (list (list "width" "float" width) 39 | (list "spacing" "float" (gpv 'spacing)) 40 | (list "apothem" "float" (gpv 'apothem)) 41 | (list "turns" "float" (gpv 'turns)) 42 | (list "metalLayer" "string" metalLayer) 43 | (list "drawnWidth" "float" newWidth)))) 44 | (dbCreateParamInst 45 | pcCellView pathCellId instName origin rotation 1 params)))) 46 | (mkCoil (lambda (rot idx metal) 47 | (makeLoop rot (sprintf nil "coil%d_%s" idx metal) metal))) 48 | (coils1 (mapcar (lambda (x) (mkCoil "R0" 1 x)) topMetalLayers)) 49 | (coils2 (mapcar (lambda (x) (mkCoil "R180" 2 x)) topMetalLayers)) 50 | (coil1 (car coils1)) 51 | (coil2 (car coils2)) 52 | (mkBridge 53 | (lambda (metLayer) 54 | (let ((instName (sprintf nil "Frlan Bridge_%s" metLayer))) 55 | (qub::createFrlanInnerBridge coil1 56 | coil2 57 | origin 58 | metLayer 59 | (limitWidth metLayer) 60 | instName)))) 61 | (drawInnerBridge 62 | (qub::getPcellBoolParamVal device 'drawConnectingBridge)) 63 | (innerBridges (when drawInnerBridge 64 | (mapcar mkBridge topMetalLayers))) 65 | (bbox (device->cvId->bBox)) 66 | (lowEdge (bottomEdge bbox)) 67 | (inConnType (gpv 'inputConnectorType)) 68 | (mkInConn (lambda (metLayer nameId rotation) 69 | (qub::createFrlanInputConnector lowEdge 70 | metLayer 71 | (limitWidth metLayer) 72 | inConnType 73 | coil1 74 | coil2 75 | origin 76 | nameId 77 | rotation))) 78 | (inputConnector 79 | (mapcar (lambda (x) (mkInConn x "A" 0.0)) underpassMetalLayers)) 80 | (inputConnector2 81 | (when (qub::getPcellBoolParamVal device 'drawSecondInput) 82 | (mapcar (lambda (x) (mkInConn x "B" 180.0)) 83 | underpassMetalLayers)))) 84 | t)) 85 | 86 | ; Returns nil or the bridge ROD object 87 | ; gpv is the "get parameter value" lambda defined in qub::draw 88 | (defun qub::createFrlanInnerBridge (coil1 89 | coil2 90 | origin 91 | metalLayer 92 | width 93 | bridgeName) 94 | (letseq ((coil1Points (car coil1->master->shapes)->points) 95 | (coil2Points (car coil2->master->shapes)->points) 96 | (bridgePoint1 (car coil1Points)) 97 | ; PCell points are given for their internal cellview 98 | ; and not the cellview that they reside in. 99 | ; We need to rotate the second point by 180 degrees 100 | ; in order to get the actual point. 101 | (coil2FirstPoint (car coil2Points)) 102 | (bridgePoint2 (qub::rotatePoint origin coil2FirstPoint 180.0)) 103 | (bridgePoints (list bridgePoint1 bridgePoint2)) 104 | (layerPP (list metalLayer "drawing"))) 105 | (rodCreatePath ?name bridgeName 106 | ?layer layerPP 107 | ?width width 108 | ?pts bridgePoints 109 | ?endType "variable" 110 | ?beginExt (quotient width 2) 111 | ?endExt (quotient width 2)))) 112 | 113 | (defun qub::createFrlanInputConnector (lowEdge 114 | metalLayer 115 | width 116 | connType 117 | coil1 118 | coil2 119 | origin 120 | nameId 121 | rotation) 122 | (letseq ((pathName (sprintf nil "inputConnector_%s_%s" nameId metalLayer)) 123 | (layerPP (list metalLayer "drawing")) 124 | (bottomY (difference lowEdge (times 1.5 width))) 125 | (createCentreBridge 126 | (lambda (x) (letseq ((pt1 x:0) 127 | (pt2 x:bottomY) 128 | (pts (list pt1 129 | (if rotation 130 | (qub::rotatePoint pt1 131 | pt2 132 | rotation) 133 | pt2)))) 134 | (rodCreatePath ?name pathName 135 | ?layer layerPP 136 | ?width width 137 | ?pts pts 138 | ?endType "variable" 139 | ?beginExt (quotient width 2) 140 | ?endExt 0))))) 141 | (cond ((equal connType "None") nil) 142 | ((equal connType "Coil Centre") (createCentreBridge 0)) 143 | ((equal connType "Coil Centre (Wide)") 144 | (let ((underpassName (sprintf nil "Bridge Under %s" metalLayer))) 145 | (createCentreBridge 0) 146 | (qub::createFrlanInnerBridge 147 | coil1 coil2 0:0 metalLayer width underpassName))) 148 | ((equal connType "Loop Centre") 149 | (letseq ((rot (lambda (x) 150 | (qub::rotatePoint origin x (plus 180.0 rotation)))) 151 | (coilPoints (car coil2->master->shapes)->points) 152 | (pt1 (rot (nth 2 coilPoints))) 153 | (pt2 (rot (nth 3 coilPoints))) 154 | (xVal (qub::mean (xCoord pt2) (xCoord pt1)))) 155 | (createCentreBridge xVal)))))) 156 | 157 | (defun qub::createFrlanCdf (library 158 | cellName 159 | defaultTopMetalLayer 160 | defaultBridgeMetalLayer) 161 | (letseq ((cellId (ddGetObj ,library ,cellName)) 162 | (cdfId (cdfCreateBaseCellCDF cellId))) 163 | (cdfCreateParam cdfId 164 | ?name "width" 165 | ?prompt "Path Width" 166 | ?type "float" 167 | ?defValue 15.0) 168 | (cdfCreateParam 169 | cdfId 170 | ?name "spacing" 171 | ?prompt "Spacing" 172 | ?type "float" 173 | ?defValue 2.0 174 | ?description "The space between the paths of adjacent coils") 175 | (cdfCreateParam cdfId 176 | ?name "apothem" 177 | ?prompt "Inner Diameter" 178 | ?type "float" 179 | ?defValue 30.0) 180 | (cdfCreateParam cdfId 181 | ?name "turns" 182 | ?prompt "Turns" 183 | ?type "float" 184 | ?defValue 2.0) 185 | (cdfCreateParam cdfId 186 | ?name "topMetalLayers" 187 | ?prompt "Top Metal Layer(s)" 188 | ?type "string" 189 | ?defValue defaultTopMetalLayer) 190 | (cdfCreateParam cdfId 191 | ?name "underpassMetalLayers" 192 | ?prompt "Underpass Metal Layer(s)" 193 | ?type "string" 194 | ?defValue defaultBridgeMetalLayer) 195 | (cdfCreateParam cdfId 196 | ?name "drawConnectingBridge" 197 | ?prompt "Connect inner." 198 | ?type "boolean" 199 | ?defValue nil 200 | ?description "Connect the inner ends of both loops") 201 | (cdfCreateParam cdfId 202 | ?name "inputConnectorType" 203 | ?prompt "Add input bridge" 204 | ?type "cyclic" 205 | ?choices (list "None" 206 | "Coil Centre" 207 | "Coil Centre (Wide)" 208 | "Loop Centre") 209 | ?defValue "None" 210 | ?description "Connect the inner ends of both loops") 211 | (cdfCreateParam cdfId 212 | ?name "drawSecondInput" 213 | ?prompt "Add second input" 214 | ?type "boolean" 215 | ?defValue nil 216 | ?description "Add a second input bridge for symmetry.") 217 | (cdfSaveCDF cdfId))) 218 | 219 | (defmacro qub::createFrlanPCell (@key (library nil) 220 | (defaultTopMetalLayer nil) 221 | (defaultBridgeMetalLayer nil)) 222 | (letseq ((cellName "Frlan") 223 | (cellType "layout") 224 | (cellObj (ddGetObj library cellName))) 225 | ; Delete the cell if it doesn't exist 226 | ; As re-running the macro doesn't seem to refresh the PCell instance 227 | (if cellObj 228 | (ddDeleteObj cellObj)) 229 | `(progn (pcDefinePCell 230 | (list (ddGetObj ,library) ,cellName ,cellType) 231 | ((width "float" 15.0) 232 | (spacing "float" 2.0) 233 | (apothem "float" 30.0) 234 | (turns "float" 2.0) 235 | (topMetalLayers "string" ,defaultTopMetalLayer) 236 | (underpassMetalLayers "string" ,defaultBridgeMetalLayer) 237 | (drawConnectingBridge "boolean" nil) 238 | (inputConnectorType "string" "None") 239 | (drawSecondInput "boolean" nil)) 240 | (let ((pcell (makeInstance 'qub::frlanPCell))) 241 | (qub::setPcellParams pcell pcCellView) 242 | (qub::draw pcell 243 | ?pcCellView pcCellView 244 | ?segLib ,library))) 245 | (qub::createFrlanCdf ,library 246 | ,cellName 247 | ,defaultTopMetalLayer 248 | ,defaultBridgeMetalLayer)))) --------------------------------------------------------------------------------