├── LICENSE.md ├── README.md ├── doc ├── README.md ├── blog_1.md ├── blog_2.md ├── blog_image_1.svg ├── fig1.png ├── fig10.png ├── fig11.png ├── fig12.png ├── fig13.png ├── fig13__.png ├── fig14.png ├── fig15.png ├── fig15_.png ├── fig15b.png ├── fig16.png ├── fig17.png ├── fig18.png ├── fig19.png ├── fig2_movie.gif ├── fig3.png ├── fig3b.png ├── fig4_movie.gif ├── fig5.png ├── fig6.png ├── fig7.png ├── fig8.png └── fig9.png └── src ├── README.md ├── fokker_plank ├── README.md ├── analytical │ ├── analytical.py │ ├── fvm │ ├── fvm_lib │ ├── ivp_lib │ └── python_compact_model └── fvm │ ├── fvm_lib │ └── fvm_classes.py │ └── mtj_fp_fvm.py ├── python_compact_model ├── README.md ├── ivp_lib │ └── ode_solver_custom_fn.py ├── sllgs_solver.py ├── stochastic_multithread_simulation.py └── test_sllgs_solver.py ├── sllgs_fpe_comparison ├── plot_sllgs_fpe_comparison.py ├── python_compact_model └── sllgs_importer.py └── verilog_a_compact_model ├── README.md ├── mram_lib ├── conductance_parameters.va ├── dimension_parameters.va ├── llg_parameters.va ├── llg_spherical_solver.va ├── mtj_lib.scs ├── mtj_structure.va ├── mtj_subcircuit.scs ├── physical_constants.va └── vectorial_fn.va └── tb ├── tb.scs └── tb_subckt.scs /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Arm Limited or its affiliates. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | * [Arm's MRAM Simulation/Characterization Framework](#arms-mram-simulationcharacterization-framework) 4 | * [Authors and Related Publications](#authors-and-related-publications) 5 | * [Quick Start & More info](#quick-start--more-info) 6 | * [Files organization](#files-organization) 7 | * [s-LLGS Solvers](#s-llgs-solvers) 8 | * [No-thermal or emulated-thermal simulations](#no-thermal-or-emulated-thermal-simulations) 9 | * [Thermal Stochastic Simulations](#thermal-stochastic-simulations) 10 | 11 | 12 | # Arm's MRAM Simulation/Characterization Framework 13 | 14 | ## Authors and Related Publications 15 | 16 | * Fernando Garcia Redondo, fernando.garciaredondo@arm.com 17 | * Pranay Prabhat 18 | * Mudit Bhargava 19 | 20 | Thanks to Cyrille Dray and Milos Milosavljevic for his helpful discussions. 21 | 22 | The following frameworks have been presented at 23 | * ***A Compact Model for Scalable MTJ Simulation***, IEEE International Conference on Synthesis, Modeling, Analysis and Simulation Methods and Applications to Circuit Design, SMACD 2021. [link to manuscript](https://arxiv.org/abs/2106.04976) 24 | * ***A Fokker-Planck Solver to Model MTJ Stochasticity*** European Solid-State Device Research Conference, ESSDERC 2021. [link to manuscript](https://arxiv.org/abs/2106.12304) 25 | 26 | This repository contains a framework for the characterization 27 | and analysis of MRAM behavior including stochasticity, 28 | and a compact model and framework 29 | for the efficient and scalable simulation of circuits with MRAMs. 30 | 31 | We provide Verilog-A and Python compact models, able to emulate the behavior of 32 | MRAMs switching at significant statistic events. 33 | To calibrate the models for such stochastic based events, 34 | we implemented and analyzed two FPE solvers (numerical FVM and analytical), and 35 | presented an optimization module that orchestrates the efficient computation 36 | of MRAM statistics and parameter regression. 37 | 38 | ## Quick Start & More info 39 | Summary: 40 | * `test_sllgs_solver.py` shows you the basic s-LLGS solver config, calling (`sllgs_solver.py`) 41 | * `stochastic_multithread_simulation.py` (calling `sllgs_solver.py`) is the script 42 | that helps you launching parallel python s-LLGS simulations 43 | * These results can be compared against Fooker-Plank simulations (see `plot_sllgs_fpe_comparison.py` script) 44 | * `analytical.py` and `mtj_fp_fvm.py` contain the Fooker-Plank solvers. Analytical contains the WER/RER fitter for the problem optimization 45 | * Verilog-a compact models: run the testbenches `tb.scs` and `tb_subckt.scs` 46 | 47 | Please, read the full description at [MRAM Framework Description](./doc/README.md). 48 | 49 | **IMPORTANT: Before using the compact models**, read the [s-LLGS Solvers](#s-llgs-solvers) info. 50 | 51 | 52 | ## Files organization 53 | * `doc` 54 | * [README.md](./doc/README.md) for the **full MRAM framework description** 55 | * `src` 56 | * `python_compact_model` 57 | * [README.md](./python_compact_model/README.md) for the MRAM python s-LLGS description 58 | * `sllgs_solver.py` Python s-LLGS solver 59 | * `stochastic_multithread_simulation.py` Multi-thread stochastic simulations 60 | * `test_sllgs_solver.py` Simple s-LLGS tests 61 | * `ode_solver_custom_fn.py` *solve_ivp* auxilar fns derived from Scipy 62 | * `sllgs_fpe_comparison` 63 | * `plot_sllgs_fpe_comparison.py` Script for s-LLGS/Fooker-Plank comparison 64 | * `sllgs_importer.py` Script for importing `stochastic_multithread_simulation.py` results 65 | * `fokker_plank` 66 | * [README.md](./fokker_plank/README.md) for the MRAM Fokker-Plank description 67 | * `fvm` 68 | * `fvm_classes.py` Finite Volume Method classes, see [FVM](https://github.com/danieljfarrell/FVM) 69 | * `mtj_fp_fvm.py` MTJ Fokker-Plank FVM solver 70 | * `analytical` 71 | * `analytical.py` MTJ Fokker-Plank Analytical solver 72 | and WER/RER curves fitter 73 | * `verilog_a_compact_model` 74 | * [README.md](./verilog_a_compact_model/README.md) for the MRAM verilog-a compact model description 75 | * `tb` Testbenches 76 | * `tb.scs` Example testbench calling full Verilog-a model (conduction and s-LLGS fully written in verilog-a) 77 | * `tb_subckt.scs` Example testbench calling full Spectre subcircuit model (s-LLGS fully written in verilog-a, conduction writen in Spectre) 78 | * `mram_lib` Verilog-a compact model and Spectre library 79 | * `llg_spherical_solver.va` Verilog-a s-LLGS solver, key file 80 | * `*.va` Parameters or auxiliar functions 81 | * `*.scs` Spectre subcircuits and library 82 | 83 | 84 | ## s-LLGS Solvers 85 | 86 | ### No-thermal or emulated-thermal simulations 87 | * Use Scipy solver in python (`scipy_ivp=True`) 88 | * Use Spherical coordinates 89 | * Control the simulation through tolerances (`atol, rtol` in python) 90 | 91 | ``` 92 | # No thermal, no fake_thermal, solved with scipy_ivp 93 | llg_a = sllg_solver.LLG(do_fake_thermal=False, 94 | do_thermal=False, 95 | i_amp_fn=current, 96 | seed=seed_0) 97 | data_a = llg_a.solve_and_plot(15e-9, 98 | scipy_ivp=True, 99 | solve_spherical=True, 100 | solve_normalized=True, 101 | rtol=1e-4, 102 | atol=1e-9) 103 | # No thermal, EMULATED THERMAL, solved with scipy_ivp 104 | llg_b = sllg_solver.LLG(do_fake_thermal=True, 105 | d_theta_fake_th=1/30, 106 | do_thermal=False, 107 | i_amp_fn=current, 108 | seed=seed_0) 109 | data_b = llg_b.solve_and_plot(15e-9, 110 | scipy_ivp=True, 111 | solve_spherical=True, 112 | solve_normalized=True, 113 | max_step=1e-11, 114 | rtol=1e-4, 115 | atol=1e-9) 116 | ``` 117 | ### Thermal Stochastic Simulations 118 | Require stochastic differential equation solvers 119 | * Use SDE solvers in python (`scipy_ivp=False`) 120 | * Use Cartesian coordinates 121 | * Control the simulation through maximum time step (`max_step` in python) 122 | ``` 123 | llg_c = sllg_solver.LLG(do_fake_thermal=False, 124 | do_thermal=True, 125 | i_amp_fn=current, 126 | seed=seed_0) 127 | data_c = llg_c.solve_and_plot(10e-9, 128 | scipy_ivp=False, 129 | solve_spherical=False, 130 | solve_normalized=True, 131 | max_step=1e-13, 132 | method='stratonovich_heun') 133 | ``` 134 | ![MRAM Magnetization and stochasticity](./doc/fig4_movie.gif) 135 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | * [Authors and Related Publications](#authors-and-related-publications) 5 | * [Motivation](#motivation) 6 | * [Introduction to MRAM technologies](#introduction-to-mram-technologies) 7 | * [MRAM stochasticity](#mram-stochasticity) 8 | * [Arm's MRAM Simulation Framework](#arms-mram-simulation-framework) 9 | * ["A Compact Model for Scalable MTJ Simulation", or how to reliably and efficiently simulate circuits with MRAM, including stochasticity](#a-compact-model-for-scalable-mtj-simulation-or-how-to-reliably-and-efficiently-simulate-circuits-with-mram-including-stochasticity) 10 | * [Motivation for a new Verilog-a/Python Compact model](#motivation-for-a-new-verilog-apython-compact-model) 11 | * [Coordinate Systems and Implications](#coordinate-systems-and-implications) 12 | * [Integration Mechanism and Solver](#integration-mechanism-and-solver) 13 | * [Thermal Noise effects and Damping](#thermal-noise-effects-and-damping) 14 | * [Proposed Framework and Compact Model](#proposed-framework-and-compact-model) 15 | * [1-Mb MRAM Macro Benchmark and Conclusions](#1-mb-mram-macro-benchmark-and-conclusions) 16 | * ["A Fokker-Planck Solver to Model MTJ Stochasticity", or how to efficiently analyze stochasticity in MRAM.](#a-fokker-planck-solver-to-model-mtj-stochasticity-or-how-to-efficiently-analyze-stochasticity-in-mram) 17 | * [Fokker-Plank Equation](#fokker-plank-equation) 18 | * [Presented Solution Framework](#presented-solution-framework) 19 | * [References](#references) 20 | 21 | 22 | 23 | # Authors and Related Publications 24 | 25 | * Fernando Garcia Redondo, 26 | * Pranay Prabhat 27 | * Mudit Bhargava 28 | 29 | 30 | Thanks to Cyrille Dray and Milos Milosavljevic for his helpful discussions. 31 | 32 | The following frameworks have been presented at 33 | * ***A Compact Model for Scalable MTJ Simulation***, IEEE International Conference on Synthesis, Modeling, Analysis and Simulation Methods and Applications to Circuit Design, SMACD 2021. 34 | * ***A Fokker-Planck Solver to Model MTJ Stochasticity*** European Solid-State Device Research Conference, ESSDERC 2021. 35 | 36 | 37 | # Motivation 38 | 39 | ## Introduction to MRAM technologies 40 | 41 | Since it was discovered in 1975, Tunneling magnetoresistance (TMR) 42 | has been actively investigated. 43 | Since 2000s, the advances in process technologies have made possible 44 | the miniaturization of the Magnetic-Random-Access-Memories (MRAMs) based on TMR devices, 45 | together with its integration into traditional CMOS processes. 46 | 47 | ![MRAM Cell](./fig1.png) 48 | **Figure 1. MRAM Structure** 49 | 50 | As depicted in Figure 1, the basic MTJ structure is composed of two ferromagnetic materials insulated by a (traditionally) oxide layer. 51 | The atom spins in each layer constitute the layer magnetization. 52 | The pinned-layer's magnetization is fixed, but the free-layer's magnetization can be 53 | altered. 54 | The resistivity of the cell is determined by the magnetization direction of the two layers. 55 | The resistance between the two terminals is minimum when both FL and PL magnetizations are parallel, (P State) and maximum when anti-parallel (AP State). 56 | 57 | The discovery and later industrial manufacturing of Spin-Transfer-Torque (STT) MRAMs, 58 | coming with enough endurance, retention, scalability and lower power consumptions, 59 | postulated MRAM as the replacement of FLASH as the 60 | near future dominant Non-Volatile Memories (NVMs) technology. 61 | 62 | In STT MRAMs the writing current flowing through the device produces a torque 63 | momentum over the FL magnetization, flipping it should the current be large enough. 64 | Figure 2 describes how the magnetization vector evolves through time, from 65 | a *z* ~ +1, to *z* ~ -1. 66 | 67 | ![MRAM Magnetization during switching](./fig2_movie.gif) 68 | **Figure 2. MRAM Magnetization during switching** 69 | 70 | Bottom graph describes the *x, y, z* components magnetization, 71 | which we will use throughout this article. 72 | The temporal evolution of the MTJ magnetization m as a monodomain 73 | nanomagnet, influenced by external and anisotropy 74 | fields, thermal noise and STT, is described by the stochastic Landau-Lifshitz-Gilbert- 75 | Slonczewsky (s-LLGS) equation [OOMMF]. 76 | 77 | ![MRAM Magnetization during switching](./fig3.png) 78 | ![MRAM Magnetization during switching](./fig3b.png) 79 | **Figure 3. s-LLGS equations** 80 | 81 | Where the effective field, Heff, is determined by the external field, 82 | the anisotropy field (uniaxial and demagnetization), the voltage controlled anisotropy field, 83 | and the thermal field. 84 | The STT spin term is defined by the MRAM characteristics and the current applied between the two cell terminals. 85 | 86 | 87 | ## MRAM stochasticity 88 | 89 | The random thermal noise induced field, Hth, converts the deterministic LLGS differential equation 90 | into a stochastic differential system (s-LLGS). 91 | Leaving process/voltage/temperature (PVT) variations out of the picture, this stochasticity 92 | translates into uncertainty during operations. 93 | The same cell, written at two different moments in time, behaves differently. 94 | Consequently an MRAM cannot be considered a deterministic system, but a stochastic one. 95 | 96 | In the next figure, we represent the *x, y, z* magnetization components 97 | during two different write operations, occurring **on the same cell**. 98 | Even though no PVT variations are present, we see how in two writing iterations (0 in blue, 1 in orange), 99 | the writing time (*z* flipping sign) varies considerably. 100 | 101 | ![MRAM Magnetization and stochasticity](./fig4_movie.gif) 102 | **Figure 4. Stochasticity causes two different write operations in the same cell to behave differently** 103 | 104 | 105 | Translated to the circuit domain, the periphery surrounding the MRAM cell should be 106 | aware of this behavior, and designed accordingly to be able to reliably write/read 107 | the MRAM cells incurring into low Write/Read Error Rates (WER and RER respectively). 108 | **Therefore the circuit design relies then on the accurate modeling and simulation of 109 | the stochastic behavior of MRAM cells.** 110 | 111 | Moreover, the simulation of **stochastic differential equation (SDE) systems require 112 | the use of Ito or Stratonovich calculus** *[P. Horley, S. Ament]*. 113 | Standard methods are not applicable, 114 | and the simulation of SDEs becomes a problem that involves myriad of simulations 115 | using of small time step, requiring huge computational resources. 116 | 117 | In these document, related to the works presented at ***"A Compact Model for Scalable MTJ Simulation", SMACD 2021***, 118 | and ***"A Fokker-Planck Solver to Model MTJ Stochasticity" ESSDERC 2021***, we 119 | present the solutions to the following two problems: 120 | * **how to reliably and efficiently simulate circuits with MRAM compact models, including stochasticity?** 121 | * **how to efficiently analyze stochasticity?** 122 | 123 | In the following sections we present the solutions we developed answering these questions. 124 | 125 | # Arm's MRAM Simulation Framework 126 | 127 | We present a framework for the characterization 128 | and analysis of MRAM stochasticity, and a compact model and framework 129 | for the efficient and scalable simulation of circuits with MRAMs. 130 | 131 | We provide Verilog-A and Python compact models, able to emulate the behavior of 132 | MRAMs switching at significant statistic events. 133 | To calibrate the models for such stochastic based events, 134 | we implemented and analyzed two FPE solvers (numerical FVM and analytical), and 135 | presented an optimization module that orchestrates the efficient computation 136 | of MRAM statistics and parameter regression. 137 | 138 | ## "A Compact Model for Scalable MTJ Simulation", or how to reliably and efficiently simulate circuits with MRAM, including stochasticity 139 | 140 | 141 | ### Motivation for a new Verilog-a/Python Compact model 142 | 143 | Many MTJ models have been presented, from micro-magnetic approaches to macro-magnetic SPICE and 144 | Verilog-A compact models for circuit simulations. 145 | Compact models present in the literature account for 146 | different behavioral aspects: a better temperature dependence, 147 | a more accurate anisotropy formulation for particular MTJ 148 | structures, or the integration of Spin-Orbit Torque 149 | (SOT) for three-terminal devices, for example. 150 | 151 | These prior works focus on the ability to model a specific new behavior for the 152 | simulation of a single or a few MTJ devices in a small circuit. 153 | A product-ready PDKs requires not only research contributions capturing novel device 154 | behavior, but also optimization stages enabling 155 | circuit design. 156 | 157 | #### Coordinate Systems and Implications 158 | STT-MRAM circuit design needs the 159 | complex device dynamics to be incorporated into the standard 160 | SPICE-like solvers. 161 | And doing it efficiently is not an easy task. 162 | The s-LLGS system resolution can easily lead to artificial 163 | oscillations if solved in a Cartesian reference system when 164 | using the Euler, Gear and Trapezoidal methods provided by 165 | circuit solvers. 166 | In *[S. Ament]* it is studied how MTJ devices benefit 167 | from resolution in spherical coordinates, specially when using 168 | implicit midpoint and explicit RK45 methods. Additionally, 169 | Cartesian methods require a non-physical normalization 170 | stage for the magnetization vector *[S. Ament]*. 171 | 172 | #### Integration Mechanism and Solver 173 | Secondly, MTJ Verilog-A models present in the literature use a naive Euler/Trapezoidal 174 | methods to integrate the magnetization vector 175 | 176 | *m(t) = m(t − dt) + dmdt (2)* 177 | 178 | However, Equation 1 causes *m_φ* to exceed the *[0, 2π)* 179 | physically allowed range, 180 | and grow indefinitely over time, leading 181 | to unnatural voltages that eventually prevent simulator convergence. 182 | Our solution is to use the circular integration idtmod 183 | function provided by Spectre solvers, which wraps *m_φ* at 184 | 2π preventing it from increasing indefinitely. 185 | 186 | More importantly, there is a hard trade-off when simulating *m_θ* on Equation (1) 187 | between its accuracy, the integration method and the minimum timestep used. 188 | Vector *m_θ* represents the binary state of the MTJ data 189 | bit and is the most critical component to accurately model 190 | MTJ switching. 191 | 192 | ![MRAM Simulation and Integration Methods](./fig5.png) 193 | **Figure 5. Solver/time-step and accuracy trade-off.** 194 | 195 | The naive integration of *m_θ* directly encoding 196 | Equation 1 in the Verilog-A description incurs a substantial 197 | error during a switching event, as shown in Figure 5. 198 | Reducing the timestep improves the RMS error (against OOMMF reference) but at the cost of a large 199 | increase in computational load. Our solution is to use the idt 200 | function for the circuit solvers provided by Spectre, 201 | which adapt the integration based on multiple evaluations of 202 | the differential terms at different timesteps, leading to better 203 | accuracy. 204 | 205 | Regarding scalability, if a larger circuit is to be simulated and fixed timesteps 206 | banned, it is essential to manage the tolerance/timestep scheme, 207 | Otherwise during the computation of the *m_φ* component the asymptote 208 | *dmφ/dt → ∞* at *m_θ = 0*. 209 | This precession mechanism acceleration, described by the next figure, 210 | requires the solver to accordingly increase its resolution. 211 | Thus, an appropriate tolerance scheme is to be used. 212 | ![MRAM Simulation and Integration Methods](./fig6.png) 213 | **Figure 6. Asymptote simulation requires smaller time steps to ensure convergence.** 214 | 215 | #### Thermal Noise effects and Damping 216 | *H_th* follows a Wiener process in which each *x, y, z* 217 | random component is independent of each other and previous 218 | states. 219 | This implies that the computation of *H_th* requires large 220 | independent variations between steps, hindering the solver’s 221 | attempts to guarantee signal continuity under small tolerances. 222 | Under default solver tolerances aiming for circuit accuracy, the simulation 223 | leads to computational errors, and excessive computational 224 | load under 1 ps bounded time steps, and require from user-defined Stratonovich 225 | Heun/RK extra steps. 226 | 227 | Three solutions have been proposed in the literature. First, 228 | to emulate the random field by using an external current or 229 | resistor-like noise source. However, SPICE simulators 230 | impose a minimum capacitance on these nodes filtering the 231 | randomness response, therefore preventing a true Wiener process 232 | from being simulated, as they are later integrated using deterministic methods. 233 | Second, to bound a fixed small 234 | timestep to the solver, but as described before this is 235 | not feasible for large circuits. Third, to only consider scenarios 236 | where the field generated by the writing current is much 237 | larger than *H_th* , forcing *H_th = 0*. 238 | 239 | This has strong implications when moving from single to multiple successive 240 | switching event simulations. Under no *H_th* thermal field, 241 | the magnetization damping collapses *m_θ* to either *0* or *π*. 242 | s-LLGS dynamics imply that the smaller the *m_θ* the harder 243 | it is for the cell to switch, and if completely collapsed, it is 244 | impossible. 245 | 246 | ![MRAM Simulation and Integration Methods](./fig7.png) 247 | **Figure 7. *m_z* collapses *m_θ=0* due to dampening under no thermal noise field presence, 248 | Making impossible to simulate multiple-writes or long simulations** 249 | 250 | The above picture depicts this artificial effect, which does 251 | not have an equivalent in reality, as *H_th != 0* imposes a 252 | random angle. Design, validation and signoff for large memory 253 | blocks with integrated periphery and control circuits requires 254 | the simulation of sequences of read and write operations, with 255 | each operation showing identical predictable and switching 256 | characteristics. However, the damping artifact discussed above 257 | prevents or slows down subsequent switches after the first 258 | event, since the subsequent events see an initial *θ* value 259 | vanishingly close to zero. 260 | 261 | ### Proposed Framework and Compact Model 262 | ![MRAM Simulation and Integration Methods](./fig8.png) 263 | **Figure 8. Proposed compact model and Framework methodology.** 264 | 265 | The above Figure describes the implemented compact model 266 | and model analysis/calibration procedure and validation against OOMMF. 267 | First, given a set of MRAM parameters, initial non-stochastic simulations 268 | are compared against OOMMF simulation results. The tolerances are adjusted 269 | till the results match. 270 | At this point, the model is frozen and exported to Verilog-A. 271 | The subsequent simulation validates the tolerances needed for the required accuracy. 272 | Finally, the coefficients of the thermal noise emulation mechanism explained bellow 273 | are regressed and the Verilog-A model library finalized. 274 | 275 | The model is composed of two modules: 276 | the Conduction and Dynamics modules. 277 | The conduction scheme describing the instantaneous MTJ 278 | resistance is dependent on the foundry engineered stack. Our 279 | modular approach allows foundry-specific conduction mechanisms to complement 280 | the basic Tunnel-Magneto-Resistance 281 | (TMR) scheme. The Dynamics module describes 282 | the temporal evolution of the MTJ magnetization *m*. 283 | 284 | The compact model has been implemented in Python and Verilog-A. 285 | Python model supports traditional 286 | Ordinary Differential Equations (ODE) and SDE solvers, for the 287 | simulation of *H_th* as a pure Wiener process [S. Ament, P. Horley]. 288 | The parallel Python engine enables MC and statistical studies. The 289 | Verilog-A implementation uses idt /idtmod integration 290 | schemes with parameterizable integration tolerances. 291 | The following Figure describes our Verilog-A model once parameterized validated against OOMMF. 292 | ![MRAM Simulation and Integration Methods](./fig9.png) 293 | **Figure 9. Validation against OOMMF** 294 | 295 | To enable the efficient simulation of the effects caused by the stochastic *H_th* 296 | two solutions are proposed. 297 | The purpose is to be able to simulate 298 | being simulate the mean switching behavior, and those switchings related 299 | to given error rate *WER_i* behaviors. Thus, enabling the analysis of 300 | how a given circuit instantiating that MRAM device would behave statistically 301 | with negligible simulation performance degradation. 302 | 303 | **Windowing Function Approach:** Our first objective is 304 | to provide a mechanism for *m_θ* to saturate naturally to the 305 | equilibrium value given by *H_th* during switching events. By 306 | redefining the evolution of *m* on its *θ* component we are able 307 | to saturate its angle at *θ_0* , the second moment of the Maxwell- 308 | Boltzmann distribution of *m_θ* under no external field [OOMMF] 309 | 310 | This function slows down the magnetization evolution 311 | when reaching the angle *c_wi θ_0*, therefore saturating *m_θ* 312 | and avoiding *m* from collapsing over *z*. Moreover, by using 313 | *c_wi* we are able to define the angle that statistically follows 314 | different stochastic behaviors corresponding to different write error rates. 315 | 316 | **Emulated Magnetic Term Approach** 317 | The expansion of s-LLGS equation after its expression in spherical coordinates 318 | describes *m_θ* evolution as proportional to *H_eff_φ + αH_eff_θ ~ H_eff_φ*. 319 | We propose to add a fictitious term *H_fth_φ* with the purpose of 320 | emulating the mean/*WER_i* *H_th* contribution that generates *θ_0* (*θ_0_i* for *WER_i*). 321 | 322 | Thanks to the proposed approaches we can efficiently extract the mean/*WER_i* behaviors, 323 | and generate the corresponding and calibrated Verilog-A models ready to be efficiently simulated. 324 | ![MRAM Simulation and Integration Methods](./fig10.png) 325 | **Figure 10. Stochastic SDE simulations requiring high computational resources and proposed *H_fth* simulation, 326 | matching the mean stochastic behavior of the cell** 327 | 328 | ### 1-Mb MRAM Macro Benchmark and Conclusions 329 | To validate scalability on a commercial product, the model 330 | is instantiated into the 64 × 4 memory top block of the 331 | extracted netlist from a 1-Mb 28 nm MRAM macro [E. M. Boujamma], and 332 | simulated with macro-specific tolerance settings. The emulated 333 | magnetic term enables the previously 334 | impossible capability of simulating successive writes with 335 | identical transition times due to non-desired over-damping. 336 | 337 | The following Figure –resistance/voltage units omitted for confidentiality– 338 | describes a writing operation 10 µs after power-on sequence. 339 | We combine the s-LLGS OOMMF validated dynamics with 340 | foundry-given thermal/voltage conductance dependence, providing 341 | the accurate resistance response over time. Compared 342 | to using fixed resistors, there is an overhead of 3.1× CPU time 343 | and 1.5× RAM usage. In return, circuit designers can observe 344 | accurate transient switching behaviour and read disturbs. 345 | ![MRAM Simulation and Integration Methods](./fig11.png) 346 | **Figure 11. Magnetization, BL, WL, SL and resistance of a cell writen within a 1Mb Macro** 347 | 348 | 349 | ## "A Fokker-Planck Solver to Model MTJ Stochasticity", or how to efficiently analyze stochasticity in MRAM. 350 | 351 | As seen in Figure 10, the computation of 352 | WER/RER with s-LLGS simulations requires a large number 353 | of random walks, especially for the low error rates (<< 1ppm) 354 | required for volume production. 355 | 356 | For example, the simulation of 10000 random walks solving the SDE using Stratonovich 357 | Heun algorithm in Cartesian coordinates system (0.1ps time step) 358 | for the write error rate computation seen in Figure 12 took ~1150 hours. 359 | The characterization of a given MRAM cell for *WER=1e-8* 360 | would require a non-manageable amount of time and computational resources. 361 | ![MRAM Simulation and Integration Methods](./fig12.png) 362 | **Figure 12. WER simulation. 10000 SDE simulations using Stratonovich Heun method, 363 | solved for cartesian coordinates system and 0.1ps time step took ~1150 hours. 364 | Taking advantage of the presented python framework and multi-thread capabilities, 365 | this computation time was reduced to ~18h (64 threads). 366 | The Fokker-Plank WER computation is solved in seconds, providing the analytical solution.** 367 | 368 | To alleviate this issue, Stochastic Differential Equation 369 | (SDE) tools such as the Fokker–Planck Equation (FPE) statis- 370 | tically analyze the MTJ magnetization and provide a simplified 371 | solution with sufficient accuracy to analyze such error rates 372 | [Tzoufras], [Y. Xie], [W. H. Butler]. 373 | Compared against a set of s-LLGS random-walk 374 | transient simulations, the FPE accurately evolves an initial 375 | MTJ magnetization probability through time based on the cur- 376 | rent and external fields. Instead of independent transients, the 377 | FPE computes the probability distribution of the magnetization 378 | at a given instant, thus capturing the statistical behavior of the 379 | MTJ cell. Figure 12 highlights how FPE is able to solve in seconds 380 | what otherwise requires huge computational resources (myriads of s-LLGS simulations). 381 | 382 | The problem magnifies when fitting to measured silicon data. Silicon measurement needs 383 | low error rates to be captured accurately from finite memory 384 | arrays without compromising test throughput in volume production, 385 | requiring high currents to allow extrapolation from 386 | higher, more easily measurable error rates. 387 | As a result, foundry 388 | data could consist of a set of data points with the error rate 389 | spanning orders of magnitude. 390 | 391 | Addressing both problems, 392 | the proposed stochastic framework 393 | for the characterization and analysis of MRAM stochastic effects is described, 394 | and the fitting of the complex set of MRAM parameters onto 395 | such heterogeneous data points through a case study with 396 | published foundry data. 397 | 398 | With the proposed solution, we are able to generate WER/RER in seconds, 399 | enabling the search of the set of physical parameters 400 | that best fit a collection of ER points as a 401 | function of a current pulse width and amplitude. 402 | 403 | ### Fokker-Plank Equation 404 | The advection-diffusion or Fokker-Plank equation has been widely used to analyze 405 | the evolution over time of the 406 | probability density function of the magnetization of an MRAM cell: 407 | ![MRAM Simulation and Integration Methods](./fig13.png) 408 | **Figure 13. Fokker-Plank equation** 409 | 410 | Prior work numerically solves the FPE through finite differences 411 | or finite volume methods (FVM) or analytical solutions. 412 | ![MRAM Simulation and Integration Methods](./fig15_.png) 413 | **Figure 14. Computational load comparison of a single s-LLGS random walk and different FPE solvers** 414 | In the above Figure we characterize the computational load required to simulate 415 | a single s-LLGS stochastic random walk, different FVM FPE simulations using 416 | different time and spatial resolutions, and various analytical FPE simulations 417 | varying the number of coefficients of the expansion series used. 418 | First, it can be clearly seen how computing millions of s-LLGS is simply unmanageable. 419 | Second, while FVM FPE approaches are a good method for small amount of WER/RER computations, 420 | should multiple simulations be required, like it is the case on the design space exploration 421 | occurring during MRAM parameters fitting, the faster yet accurate analytical 422 | solutions approach is required. 423 | 424 | It is important to highlight that even FVM FPE solutions constituted a reasonable 425 | solution enabling statistical analyses otherwise impossible using billions of s-LLGS, 426 | the simulation time is directly proportional at the writing pulse width being simulated. 427 | Therefore longer simulated times, required to characterize low-current regimes, 428 | still involve huge computational resources. 429 | On the contrary, the analytical FPE approach otherwise requires constant time to simulate 430 | longer pulse widths. 431 | 432 | The following Figure describes how the different resolutions and number of coefficients used 433 | affect the accuracy of the computation of the magnetization probability evolution. 434 | ![MRAM Simulation and Integration Methods](./fig15b.png) 435 | **Figure 15. Accuracy comparision for different resolution/number of coefficients.** 436 | 437 | ### Presented Solution Framework 438 | 439 | Complementing the compact model and simulation framework that enable the 440 | efficient simulation of circuits with MRAMs, the MRAM characterization 441 | framework focus on the MRAM behavior statistical analysis. 442 | 443 | As described in the following Figure, 444 | it enables the otherwise impossible MRAM parameter set regression from 445 | foundry WER curves. 446 | ![MRAM Simulation and Integration Methods](./fig16.png) 447 | **Figure 16. Proposed framework and methodology** 448 | 449 | At the end of the process, the circuit designer obtains a set of MRAM compact models, 450 | ready to be simulated in traditional circuit simulators, that have been 451 | accurately calibrated to represent the mean/*WER_i* cell behavior. 452 | 453 | As a case of study, we take the two stack processes presented in [G. Hu], 454 | where a set of current/mean switching time points are provided. 455 | First the most computing intensive tasks, regressing the MRAM parameters set 456 | that best describe the provided stochastic behaviors takes place. 457 | Our framework finds the proper parameters in reasonable time (*< 3 days*), 458 | a task otherwise completely impossible using s-LLGS simulations, 459 | and much slower (almost impractical) using FVM approaches. 460 | ![MRAM Simulation and Integration Methods](./fig17.png) 461 | **Figure 17. Current/switching time fitting for two MRAM stack processes without any prior knowledge** 462 | 463 | At this point, as circuit designers we can easily compare the error rates 464 | on both stacks, and determine the appropriate operating points, based on results 465 | as the shown in the following Figure: 466 | ![MRAM Simulation and Integration Methods](./fig18.png) 467 | **Figure 18. WER/RER analyses for the regressed stacks** 468 | 469 | With the physical parameters set found, we make use of the compact model presented at 470 | ["A Compact Model for Scalable MTJ Simulation"](#a-compact-model-for-scalable-mtj-simulation-or-how-to-reliably-and-efficiently-simulate-circuits-with-mram-including-stochasticity) 471 | section, and compute the *H_fth_i* parameters that enable circuit designers 472 | to accurately simulate the required MRAM cell at the significant *WER_i* statistical events: 473 | ![MRAM Simulation and Integration Methods](./fig19.png) 474 | 475 | 476 | # References 477 | 478 | [P. Horley] Horley, P., et al. (2011). 479 | Numerical Simulations of Nano-Scale Magnetization Dynamics. 480 | Numerical Simulations of Physical and Engineering Processes. https://doi.org/10.5772/23745 481 | 482 | [S. Ament] S. Ament et al., “Solving the stochastic Landau-Lifshitz-Gilbert- 483 | Slonczewski equation for monodomain nanomagnets : A survey and 484 | analysis of numerical techniques,” 2016. 485 | 486 | [OOMMF] Donahue, M. J., & Porter, D. G. (1999). 487 | OOMMF user’s guide, version 1.0. In National Institute of Standards and Technology. https://doi.org/10.6028/NIST.IR.6376 488 | 489 | [E. M. Boujamaa] E. M. Boujamaa et al., “A 14.7Mb/mm2 28nm FDSOI STT-MRAM with 490 | Current Starved Read Path, 52Ω/Sigma Offset Voltage Sense Amplifier 491 | and Fully Trimmable CTAT Reference,” IEEE Symp. VLSI Circuits, Dig. 492 | Tech. Pap., vol. 2020-June, 2020. 493 | 494 | [Tzoufras] Tzoufras, M. (2018). Switching probability of all-perpendicular spin valve nanopillars. AIP Advances, 8(5). https://doi.org/10.1063/1.5003832 495 | 496 | [Y.Xie] Xie, Y., et al. (2017). Fokker-Planck Study of Parameter Dependence on Write Error Slope in Spin-Torque Switching. IEEE Transactions on Electron Devices, 64(1), 319–324. https://doi.org/10.1109/TED.2016.2632438 497 | 498 | [W. H. Butler] Butler, W. H., Mewes, T., Mewes, C. K. A., Visscher, P. B., Rippard, W. H., Russek, S. E., & Heindl, R. (2012). Switching distributions for perpendicular spin-torque devices within the macrospin approximation. IEEE Transactions on Magnetics, 48(12), 4684–4700. https://doi.org/10.1109/TMAG.2012.2209122 499 | 500 | [G. Hu] Hu G. et al., “Spin-transfer torque MRAM with reliable 2 ns writing for 501 | last level cache applications,” Tech. Dig. - Int. Electron Devices Meet. 502 | IEDM, vol. 2019-Decem, pp. 2019–2022, 2019. 503 | -------------------------------------------------------------------------------- /doc/blog_1.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | * [Motivation](#motivation) 5 | * [Introduction to MRAM technologies](#introduction-to-mram-technologies) 6 | * [MRAM stochasticity](#mram-stochasticity) 7 | * ["A Compact Model for Scalable MTJ Simulation", or how to reliably and efficiently simulate circuits with MRAM, including stochasticity](#a-compact-model-for-scalable-mtj-simulation-or-how-to-reliably-and-efficiently-simulate-circuits-with-mram-including-stochasticity) 8 | * [Motivation for a new Verilog-a/Python Compact model](#motivation-for-a-new-verilog-apython-compact-model) 9 | * [Proposed Framework and Compact Model](#proposed-framework-and-compact-model) 10 | * [1-Mb MRAM Macro Benchmark and Conclusions](#1-mb-mram-macro-benchmark-and-conclusions) 11 | * [But the MRAM story continues](#but-the-mram-story-continues) 12 | * [References](#references) 13 | 14 | 15 | 16 | * Fernando Garcia Redondo, 17 | * Pranay Prabhat 18 | * Mudit Bhargava 19 | 20 | 21 | Thanks to Cyrille Dray and Milos Milosavljevic for his helpful discussions. 22 | 23 | The following frameworks have been presented at 24 | * ***A Compact Model for Scalable MTJ Simulation***, IEEE International Conference on Synthesis, Modeling, Analysis and Simulation Methods and Applications to Circuit Design, SMACD 2021. 25 | * ***A Fokker-Planck Solver to Model MTJ Stochasticity*** European Solid-State Device Research Conference, ESSDERC 2021. 26 | 27 | 28 | # Motivation 29 | 30 | ## Introduction to MRAM technologies 31 | 32 | Since it was discovered in 1975, Tunneling magnetoresistance (TMR) 33 | has been actively investigated. 34 | Since 2000s, the advances in process technologies have made possible 35 | the miniaturization of the Magnetic-Random-Access-Memories (MRAMs) based on TMR devices, 36 | together with its integration into traditional CMOS processes. 37 | 38 | ![MRAM Cell](./fig1.png) 39 | **Figure 1. MRAM Structure** 40 | 41 | As depicted in Figure 1, the basic MTJ structure is composed of two ferromagnetic materials insulated by a (traditionally) oxide layer. 42 | The atom spins in each layer constitute the layer magnetization. 43 | The pinned-layer's magnetization is fixed, but the free-layer's magnetization can be 44 | altered. 45 | The resistivity of the cell is determined by the magnetization direction of the two layers. 46 | The resistance between the two terminals is minimum when both FL and PL magnetizations are parallel, (P State) and maximum when anti-parallel (AP State). 47 | 48 | The discovery and later industrial manufacturing of Spin-Transfer-Torque (STT) MRAMs, 49 | coming with enough endurance, retention, scalability and lower power consumptions, 50 | postulated MRAM as the replacement of FLASH as the 51 | near future dominant Non-Volatile Memories (NVMs) technology. 52 | 53 | In STT MRAMs the writing current flowing through the device produces a torque 54 | momentum over the FL magnetization, flipping it should the current be large enough. 55 | Figure 2 describes how the magnetization vector evolves through time, from 56 | a *z* ~ +1, to *z* ~ -1. 57 | 58 | ![MRAM Magnetization during switching](./fig2_movie.gif) 59 | **Figure 2. MRAM Magnetization during switching** 60 | 61 | Bottom graph describes the *x, y, z* components magnetization, 62 | which we will use throughout this article. 63 | The temporal evolution of the MTJ magnetization m as a monodomain 64 | nanomagnet, influenced by external and anisotropy 65 | fields, thermal noise and STT, is described by the stochastic Landau-Lifshitz-Gilbert- 66 | Slonczewsky (s-LLGS) equation [OOMMF]. 67 | 68 | ![MRAM Magnetization during switching](./fig3.png) 69 | ![MRAM Magnetization during switching](./fig3b.png) 70 | **Figure 3. s-LLGS equations** 71 | 72 | Where the effective field, Heff, is determined by the external field, 73 | the anisotropy field (uniaxial and demagnetization), the voltage controlled anisotropy field, 74 | and the thermal field. 75 | The STT spin term is defined by the MRAM characteristics and the current applied between the two cell terminals. 76 | 77 | 78 | ## MRAM stochasticity 79 | 80 | The random thermal noise induced field, Hth, converts the deterministic LLGS differential equation 81 | into a stochastic differential system (s-LLGS). 82 | Leaving process/voltage/temperature (PVT) variations out of the picture, this stochasticity 83 | translates into uncertainty during operations. 84 | The same cell, written at two different moments in time, behaves differently. 85 | Consequently an MRAM cannot be considered a deterministic system, but a stochastic one. 86 | 87 | In the next figure, we represent the *x, y, z* magnetization components 88 | during two different write operations, occurring **on the same cell**. 89 | Even though no PVT variations are present, we see how in two writing iterations (0 in blue, 1 in orange), 90 | the writing time (*z* flipping sign) varies considerably. 91 | 92 | ![MRAM Magnetization and stochasticity](./fig4_movie.gif) 93 | **Figure 4. Stochasticity causes two different write operations in the same cell to behave differently** 94 | 95 | 96 | Translated to the circuit domain, the periphery surrounding the MRAM cell should be 97 | aware of this behavior, and designed accordingly to be able to reliably write/read 98 | the MRAM cells incurring into low Write/Read Error Rates (WER and RER respectively). 99 | **Therefore the circuit design relies then on the accurate modeling and simulation of 100 | the stochastic behavior of MRAM cells.** 101 | 102 | Moreover, the simulation of **stochastic differential equation (SDE) systems require 103 | the use of Ito or Stratonovich calculus** *[P. Horley, S. Ament]*. 104 | Standard methods are not applicable, 105 | and the simulation of SDEs becomes a problem that involves myriad of simulations 106 | using of small time step, requiring huge computational resources. 107 | 108 | In these blog posts related to the works presented at ***"A Compact Model for Scalable MTJ Simulation", SMACD 2021***, 109 | and ***"A Fokker-Planck Solver to Model MTJ Stochasticity" ESSDERC 2021***, 110 | we present a framework for the characterization 111 | and analysis of MRAM stochasticity, and a compact model and framework 112 | for the efficient and scalable simulation of circuits with MRAMs. 113 | 114 | We provide Verilog-A and Python compact models, able to emulate the behavior of 115 | MRAMs switching at significant statistic events. 116 | To calibrate the models for such stochastic based events, 117 | we implemented and analyzed two FPE solvers (numerical FVM and analytical), and 118 | presented an optimization module that orchestrates the efficient computation 119 | of MRAM statistics and parameter regression. 120 | 121 | Therefore, in this series of posts, we present the solutions and answers 122 | to the following two problems: 123 | * **how to reliably and efficiently simulate circuits with MRAM compact models, including stochasticity?**, blog post #1 124 | * **how to efficiently analyze stochasticity?**, blog post #2 125 | 126 | 127 | ## "A Compact Model for Scalable MTJ Simulation", or how to reliably and efficiently simulate circuits with MRAM, including stochasticity 128 | 129 | 130 | ### Motivation for a new Verilog-a/Python Compact model 131 | 132 | Many MTJ models have been presented, from micro-magnetic approaches to macro-magnetic SPICE and 133 | Verilog-A compact models for circuit simulations. 134 | Compact models present in the literature account for 135 | different behavioral aspects: a better temperature dependence, 136 | a more accurate anisotropy formulation for particular MTJ 137 | structures, or the integration of Spin-Orbit Torque 138 | (SOT) for three-terminal devices, for example. 139 | 140 | These prior works focus on the ability to model a specific new behavior for the 141 | simulation of a single or a few MTJ devices in a small circuit. 142 | A product-ready PDKs requires not only research contributions capturing novel device 143 | behavior, but also optimization stages enabling 144 | circuit design. 145 | 146 | STT-MRAM circuit design needs the 147 | complex device dynamics to be incorporated into the standard 148 | SPICE-like solvers. 149 | And doing it efficiently is not an easy task. 150 | The s-LLGS system resolution, even when the stochasticity is not under consideration, 151 | can easily lead to non-convergence and error issues. 152 | 153 | Following the work initiated in *[S. Ament]* 154 | we analyze the integration methods and solver problems most commonly encountered 155 | when solving s-LLGS systems, focusing on the SPICE-like circuit simulators approach. 156 | We study the accuracy, time-step and solver characteristics trade-offs. 157 | In particular, and focusing on the issues related to explicit integration methods, 158 | we analyze the time-step dependence or the problematic of simulating 159 | a Wiener process using Euler/Trapezoidal integrators. 160 | 161 | Similarly important is the computational resources involved in simulating 162 | *H_th* stochastic simulations using the appropriate Stratonovich stochastic differential 163 | calculus. 164 | This implies that the computation of *H_th* requires large 165 | independent variations between steps, hindering the solver’s 166 | attempts to guarantee signal continuity under small tolerances. 167 | Under default solver tolerances aiming for circuit accuracy, the simulation 168 | leads to computational errors, and excessive computational 169 | load under 1 ps bounded time steps, and require from user-defined Stratonovich 170 | Heun/RK extra steps. 171 | 172 | Solutions have been proposed in the literature. First, 173 | to emulate the random field by using an external current or 174 | resistor-like noise source. However, this noise sources 175 | are not appropriate for simulating true Wiener processes, 176 | as they are later integrated using deterministic methods. 177 | The second solutions only consider scenarios 178 | where the field generated by the writing current is much 179 | larger than *H_th* , forcing *H_th = 0*. 180 | This has strong implications when moving from single to multiple successive 181 | switching event simulations. Under no *H_th* thermal field, 182 | the magnetization damping collapses *m_θ* to either *0* or *π*. 183 | s-LLGS dynamics imply that the smaller the *m_θ* the harder 184 | it is for the cell to switch, and if completely collapsed, it is 185 | impossible. 186 | 187 | ![MRAM Simulation and Integration Methods](./fig7.png) 188 | **Figure 5. *m_z* collapses *m_θ=0* due to dampening under no thermal noise field presence, 189 | Making impossible to simulate multiple-writes or long simulations** 190 | 191 | The above picture depicts this artificial effect, which does 192 | not have an equivalent in reality, as *H_th != 0* imposes a 193 | random angle. Design, validation and signoff for large memory 194 | blocks with integrated periphery and control circuits requires 195 | the simulation of sequences of read and write operations, with 196 | each operation showing identical predictable and switching 197 | characteristics. However, the damping artifact discussed above 198 | prevents or slows down subsequent switches after the first 199 | event, since the subsequent events see an initial *θ* value 200 | vanishingly close to zero. 201 | 202 | ### Proposed Framework and Compact Model 203 | ![MRAM Simulation and Integration Methods](./fig8.png) 204 | **Figure 8. Proposed compact model and Framework methodology.** 205 | 206 | The above Figure describes the implemented compact model 207 | and model analysis/calibration procedure and validation against OOMMF. 208 | First, given a set of MRAM parameters, initial non-stochastic simulations 209 | are compared against OOMMF simulation results. The tolerances are adjusted 210 | till the results match. 211 | At this point, the model is frozen and exported to Verilog-A. 212 | The subsequent simulation validates the tolerances needed for the required accuracy. 213 | Finally, the coefficients of the thermal noise emulation mechanism explained bellow 214 | are regressed and the Verilog-A model library finalized. 215 | 216 | The model is composed of two modules: 217 | the Conduction and Dynamics modules. 218 | The conduction scheme describing the instantaneous MTJ 219 | resistance is dependent on the foundry engineered stack. Our 220 | modular approach allows foundry-specific conduction mechanisms to complement 221 | the basic Tunnel-Magneto-Resistance 222 | (TMR) scheme. The Dynamics module describes 223 | the temporal evolution of the MTJ magnetization *m*. 224 | 225 | The compact model has been implemented in Python and Verilog-A. 226 | Python model supports traditional 227 | Ordinary Differential Equations (ODE) and SDE solvers, for the 228 | simulation of *H_th* as a pure Wiener process [S. Ament, P. Horley]. 229 | The parallel Python engine enables MC and statistical studies. The 230 | Verilog-A implementation uses idt /idtmod integration 231 | schemes with parameterizable integration tolerances. 232 | The following Figure describes our Verilog-A model once parameterized validated against OOMMF. 233 | ![MRAM Simulation and Integration Methods](./fig9.png) 234 | **Figure 9. Validation against OOMMF** 235 | 236 | To enable the efficient simulation of the effects caused by the stochastic *H_th* 237 | two solutions are proposed. 238 | The purpose is to be able to simulate 239 | being simulate the mean switching behavior, and those switchings related 240 | to given error rate *WER_i* behaviors. Thus, enabling the analysis of 241 | how a given circuit instantiating that MRAM device would behave statistically 242 | with negligible simulation performance degradation. 243 | The first solution smoothly saturates *m_θ* preventing it to saturate completely. 244 | 245 | The second approach adds a fictitious term *H_fth* with the purpose of 246 | emulating the mean/*WER_i* *H_th* contribution that generates *θ_0* (*θ_0_i* for *WER_i*). 247 | Thanks to the proposed approaches we can efficiently extract the mean/*WER_i* behaviors, 248 | and generate the corresponding and calibrated Verilog-A models ready to be efficiently simulated. 249 | ![MRAM Simulation and Integration Methods](./fig10.png) 250 | **Figure 10. Stochastic SDE simulations requiring high computational resources and proposed *H_fth* simulation, 251 | matching the mean stochastic behavior of the cell** 252 | 253 | ### 1-Mb MRAM Macro Benchmark and Conclusions 254 | To validate scalability on a commercial product, the model 255 | is instantiated into the 64 × 4 memory top block of the 256 | extracted netlist from a 1-Mb 28 nm MRAM macro [E. M. Boujamma], and 257 | simulated with macro-specific tolerance settings. The emulated 258 | magnetic term enables the previously 259 | impossible capability of simulating successive writes with 260 | identical transition times due to non-desired over-damping. 261 | 262 | The following Figure –resistance/voltage units omitted for confidentiality– 263 | describes a writing operation 10 µs after power-on sequence. 264 | We combine the s-LLGS OOMMF validated dynamics with 265 | foundry-given thermal/voltage conductance dependence, providing 266 | the accurate resistance response over time. Compared 267 | to using fixed resistors, there is an overhead of 3.1× CPU time 268 | and 1.5× RAM usage. In return, circuit designers can observe 269 | accurate transient switching behaviour and read disturbs. 270 | ![MRAM Simulation and Integration Methods](./fig11.png) 271 | **Figure 11. Magnetization, BL, WL, SL and resistance of a cell writen within a 1Mb Macro** 272 | 273 | ### But the MRAM story continues 274 | 275 | So far, we have walked together through the adventure of designing an efficient 276 | compact model that enables circuit designers to design scalable circuits 277 | and simulate them on well known and required significant statistical events. 278 | 279 | However, the statistical analysis of an MRAM technology is unfeasible just using 280 | s-LLGS based systems. The statistical characterization would require of millions 281 | of stochastic LLGS walks, involving huge computational resources that would make 282 | the validation of large circuit simply impossible. 283 | 284 | In the blog post ***"A Fokker-Planck Solver to Model MTJ Stochasticity", or how to efficiently analyze stochasticity in MRAM.***, we answer this question, and present a solution 285 | for this important problem. 286 | 287 | 288 | # References 289 | 290 | [P. Horley] Horley, P., et al. (2011). 291 | Numerical Simulations of Nano-Scale Magnetization Dynamics. 292 | Numerical Simulations of Physical and Engineering Processes. https://doi.org/10.5772/23745 293 | 294 | [S. Ament] S. Ament et al., “Solving the stochastic Landau-Lifshitz-Gilbert- 295 | Slonczewski equation for monodomain nanomagnets : A survey and 296 | analysis of numerical techniques,” 2016. 297 | 298 | [OOMMF] Donahue, M. J., & Porter, D. G. (1999). 299 | OOMMF user’s guide, version 1.0. In National Institute of Standards and Technology. https://doi.org/10.6028/NIST.IR.6376 300 | 301 | [E. M. Boujamaa] E. M. Boujamaa et al., “A 14.7Mb/mm2 28nm FDSOI STT-MRAM with 302 | Current Starved Read Path, 52Ω/Sigma Offset Voltage Sense Amplifier 303 | and Fully Trimmable CTAT Reference,” IEEE Symp. VLSI Circuits, Dig. 304 | Tech. Pap., vol. 2020-June, 2020. 305 | -------------------------------------------------------------------------------- /doc/blog_2.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | * [MRAM Stochasticity, but how do MRAM cells work?](#mram-stochasticity-but-how-do-mram-cells-work) 5 | * ["A Fokker-Planck Solver to Model MTJ Stochasticity", or how to efficiently analyze stochasticity in MRAM.](#a-fokker-planck-solver-to-model-mtj-stochasticity-or-how-to-efficiently-analyze-stochasticity-in-mram) 6 | * [Fokker-Plank Equation](#fokker-plank-equation) 7 | * [Presented Solution Framework](#presented-solution-framework) 8 | * [Arm's MRAM Simulation Framework, an overview.](#arms-mram-simulation-framework-an-overview) 9 | * [References](#references) 10 | 11 | 12 | 13 | * Fernando Garcia Redondo, 14 | * Pranay Prabhat 15 | * Mudit Bhargava 16 | 17 | 18 | Thanks to Cyrille Dray and Milos Milosavljevic for his helpful discussions. 19 | 20 | The following frameworks have been presented at 21 | * ***A Compact Model for Scalable MTJ Simulation***, IEEE International Conference on Synthesis, Modeling, Analysis and Simulation Methods and Applications to Circuit Design, SMACD 2021. 22 | * ***A Fokker-Planck Solver to Model MTJ Stochasticity*** European Solid-State Device Research Conference, ESSDERC 2021. 23 | 24 | 25 | # MRAM Stochasticity, but how do MRAM cells work? 26 | 27 | In the previous post, ***"A Compact Model for Scalable MTJ Simulation", or how to reliably and efficiently simulate circuits with MRAM, including stochasticity*** we introduce 28 | MRAM technologies, and analyze compact model for the efficient simulation 29 | of MRAM cells in large-scale circuits. 30 | 31 | MRAM is a NVM technology intrinsically stochastic. 32 | Translated to the circuit domain, the periphery surrounding the MRAM cell should be 33 | aware of this behavior, and designed accordingly to be able to reliably write/read 34 | the MRAM cells incurring into low Write/Read Error Rates (WER and RER respectively). 35 | **Therefore the circuit design relies then on the accurate modeling and simulation of 36 | the stochastic behavior of MRAM cells.** 37 | ![MRAM Magnetization and stochasticity](./fig4_movie.gif) 38 | **Figure 1. Stochasticity causes two different write operations in the same cell to behave differently** 39 | 40 | Moreover, there we described how the simulation of 41 | **stochastic differential equation (SDE) systems require 42 | the use of Ito or Stratonovich calculus** *[P. Horley, S. Ament]*. 43 | Standard methods are not applicable, 44 | and the simulation of SDEs becomes a problem that involves myriad of simulations 45 | using of small time step, requiring huge computational resources. 46 | 47 | In the present blog post, 48 | related to the work ***"A Fokker-Planck Solver to Model MTJ Stochasticity" ESSDERC 2021***, 49 | we present a framework for the characterization 50 | and analysis of MRAM stochasticity. 51 | To calibrate the models for such stochastic based events, 52 | we implemented and analyzed two FPE solvers (numerical FVM and analytical), and 53 | presented an optimization module that orchestrates the efficient computation 54 | of MRAM statistics and parameter regression. 55 | 56 | 57 | # "A Fokker-Planck Solver to Model MTJ Stochasticity", or how to efficiently analyze stochasticity in MRAM. 58 | 59 | As seen in Figure 10, the computation of 60 | WER/RER with s-LLGS simulations requires a large number 61 | of random walks, especially for the low error rates (<< 1ppm) 62 | required for volume production. 63 | 64 | For example, the simulation of 10000 random walks solving the SDE using Stratonovich 65 | Heun algorithm in Cartesian coordinates system (0.1ps time step) 66 | for the write error rate computation seen in Figure 12 took ~1150 hours. 67 | The characterization of a given MRAM cell for *WER=1e-8* 68 | would require a non-manageable amount of time and computational resources. 69 | ![MRAM Simulation and Integration Methods](./fig12.png) 70 | **Figure 12. WER simulation. 10000 SDE simulations using Stratonovich Heun method, 71 | solved for cartesian coordinates system and 0.1ps time step took ~1150 hours. 72 | Taking advantage of the presented python framework and multi-thread capabilities, 73 | this computation time was reduced to ~18h (64 threads). 74 | The Fokker-Plank WER computation is solved in seconds, providing the analytical solution.** 75 | 76 | To alleviate this issue, Stochastic Differential Equation 77 | (SDE) tools such as the Fokker–Planck Equation (FPE) statis- 78 | tically analyze the MTJ magnetization and provide a simplified 79 | solution with sufficient accuracy to analyze such error rates 80 | [Tzoufras], [Y. Xie], [W. H. Butler]. 81 | Compared against a set of s-LLGS random-walk 82 | transient simulations, the FPE accurately evolves an initial 83 | MTJ magnetization probability through time based on the cur- 84 | rent and external fields. Instead of independent transients, the 85 | FPE computes the probability distribution of the magnetization 86 | at a given instant, thus capturing the statistical behavior of the 87 | MTJ cell. Figure 12 highlights how FPE is able to solve in seconds 88 | what otherwise requires huge computational resources (myriads of s-LLGS simulations). 89 | 90 | The problem magnifies when fitting to measured silicon data. Silicon measurement needs 91 | low error rates to be captured accurately from finite memory 92 | arrays without compromising test throughput in volume production, 93 | requiring high currents to allow extrapolation from 94 | higher, more easily measurable error rates. 95 | As a result, foundry 96 | data could consist of a set of data points with the error rate 97 | spanning orders of magnitude. 98 | 99 | Addressing both problems, 100 | the proposed stochastic framework 101 | for the characterization and analysis of MRAM stochastic effects is described, 102 | and the fitting of the complex set of MRAM parameters onto 103 | such heterogeneous data points through a case study with 104 | published foundry data. 105 | 106 | With the proposed solution, we are able to generate WER/RER in seconds, 107 | enabling the search of the set of physical parameters 108 | that best fit a collection of ER points as a 109 | function of a current pulse width and amplitude. 110 | 111 | ### Fokker-Plank Equation 112 | The advection-diffusion or Fokker-Plank equation has been widely used to analyze 113 | the evolution over time of the 114 | probability density function of the magnetization of an MRAM cell: 115 | ![MRAM Simulation and Integration Methods](./fig13.png) 116 | **Figure 13. Fokker-Plank equation** 117 | 118 | Prior work numerically solves the FPE through finite differences 119 | or finite volume methods (FVM) or analytical solutions. 120 | ![MRAM Simulation and Integration Methods](./fig15_.png) 121 | **Figure 14. Computational load comparison of a single s-LLGS random walk and different FPE solvers** 122 | In the above Figure we characterize the computational load required to simulate 123 | a single s-LLGS stochastic random walk, different FVM FPE simulations using 124 | different time and spatial resolutions, and various analytical FPE simulations 125 | varying the number of coefficients of the expansion series used. 126 | First, it can be clearly seen how computing millions of s-LLGS is simply unmanageable. 127 | Second, while FVM FPE approaches are a good method for small amount of WER/RER computations, 128 | should multiple simulations be required, like it is the case on the design space exploration 129 | occurring during MRAM parameters fitting, the faster yet accurate analytical 130 | solutions approach is required. 131 | 132 | It is important to highlight that even FVM FPE solutions constituted a reasonable 133 | solution enabling statistical analyses otherwise impossible using billions of s-LLGS, 134 | the simulation time is directly proportional at the writing pulse width being simulated. 135 | Therefore longer simulated times, required to characterize low-current regimes, 136 | still involve huge computational resources. 137 | On the contrary, the analytical FPE approach otherwise requires constant time to simulate 138 | longer pulse widths. 139 | 140 | ### Presented Solution Framework 141 | 142 | Complementing the compact model and simulation framework that enable the 143 | efficient simulation of circuits with MRAMs, the MRAM characterization 144 | framework focus on the MRAM behavior statistical analysis. 145 | 146 | As described in the following Figure, 147 | it enables the otherwise impossible MRAM parameter set regression from 148 | foundry WER curves. 149 | ![MRAM Simulation and Integration Methods](./fig16.png) 150 | **Figure 16. Proposed framework and methodology** 151 | 152 | At the end of the process, the circuit designer obtains a set of MRAM compact models, 153 | ready to be simulated in traditional circuit simulators, that have been 154 | accurately calibrated to represent the mean/*WER_i* cell behavior. 155 | 156 | As a case of study, we take the two stack processes presented in [G. Hu], 157 | where a set of current/mean switching time points are provided. 158 | First the most computing intensive tasks, regressing the MRAM parameters set 159 | that best describe the provided stochastic behaviors takes place. 160 | Our framework finds the proper parameters in reasonable time (*< 3 days*), 161 | a task otherwise completely impossible using s-LLGS simulations, 162 | and much slower (almost impractical) using FVM approaches. 163 | ![MRAM Simulation and Integration Methods](./fig17.png) 164 | **Figure 17. Current/switching time fitting for two MRAM stack processes without any prior knowledge** 165 | 166 | With the physical parameters set found, we make use of the compact model presented at 167 | ["A Compact Model for Scalable MTJ Simulation"](#a-compact-model-for-scalable-mtj-simulation-or-how-to-reliably-and-efficiently-simulate-circuits-with-mram-including-stochasticity) 168 | section, and compute the *H_fth_i* parameters that enable circuit designers 169 | to accurately simulate the required MRAM cell at the significant *WER_i* statistical events: 170 | ![MRAM Simulation and Integration Methods](./fig19.png) 171 | 172 | # Arm's MRAM Simulation Framework, an overview. 173 | 174 | In this series of posts, we presented a framework for the characterization 175 | and analysis of MRAM stochasticity, and a compact model and framework 176 | for the efficient and scalable simulation of circuits with MRAMs. 177 | 178 | We provided Verilog-A and Python compact models, able to emulate the behavior of 179 | MRAMs switching at significant statistic events. 180 | To calibrate the models for such stochastic based events, 181 | we implemented and analyzed two FPE solvers (numerical FVM and analytical), and 182 | presented an optimization module that orchestrates the efficient computation 183 | of MRAM statistics and parameter regression. 184 | 185 | # References 186 | 187 | [P. Horley] Horley, P., et al. (2011). 188 | Numerical Simulations of Nano-Scale Magnetization Dynamics. 189 | Numerical Simulations of Physical and Engineering Processes. https://doi.org/10.5772/23745 190 | 191 | [S. Ament] S. Ament et al., “Solving the stochastic Landau-Lifshitz-Gilbert- 192 | Slonczewski equation for monodomain nanomagnets : A survey and 193 | analysis of numerical techniques,” 2016. 194 | 195 | [OOMMF] Donahue, M. J., & Porter, D. G. (1999). 196 | OOMMF user’s guide, version 1.0. In National Institute of Standards and Technology. https://doi.org/10.6028/NIST.IR.6376 197 | 198 | [E. M. Boujamaa] E. M. Boujamaa et al., “A 14.7Mb/mm2 28nm FDSOI STT-MRAM with 199 | Current Starved Read Path, 52Ω/Sigma Offset Voltage Sense Amplifier 200 | and Fully Trimmable CTAT Reference,” IEEE Symp. VLSI Circuits, Dig. 201 | Tech. Pap., vol. 2020-June, 2020. 202 | 203 | [Tzoufras] Tzoufras, M. (2018). Switching probability of all-perpendicular spin valve nanopillars. AIP Advances, 8(5). https://doi.org/10.1063/1.5003832 204 | 205 | [Y.Xie] Xie, Y., et al. (2017). Fokker-Planck Study of Parameter Dependence on Write Error Slope in Spin-Torque Switching. IEEE Transactions on Electron Devices, 64(1), 319–324. https://doi.org/10.1109/TED.2016.2632438 206 | 207 | [W. H. Butler] Butler, W. H., Mewes, T., Mewes, C. K. A., Visscher, P. B., Rippard, W. H., Russek, S. E., & Heindl, R. (2012). Switching distributions for perpendicular spin-torque devices within the macrospin approximation. IEEE Transactions on Magnetics, 48(12), 4684–4700. https://doi.org/10.1109/TMAG.2012.2209122 208 | 209 | [G. Hu] Hu G. et al., “Spin-transfer torque MRAM with reliable 2 ns writing for 210 | last level cache applications,” Tech. Dig. - Int. Electron Devices Meet. 211 | IEDM, vol. 2019-Decem, pp. 2019–2022, 2019. 212 | -------------------------------------------------------------------------------- /doc/fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig1.png -------------------------------------------------------------------------------- /doc/fig10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig10.png -------------------------------------------------------------------------------- /doc/fig11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig11.png -------------------------------------------------------------------------------- /doc/fig12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig12.png -------------------------------------------------------------------------------- /doc/fig13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig13.png -------------------------------------------------------------------------------- /doc/fig13__.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig13__.png -------------------------------------------------------------------------------- /doc/fig14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig14.png -------------------------------------------------------------------------------- /doc/fig15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig15.png -------------------------------------------------------------------------------- /doc/fig15_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig15_.png -------------------------------------------------------------------------------- /doc/fig15b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig15b.png -------------------------------------------------------------------------------- /doc/fig16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig16.png -------------------------------------------------------------------------------- /doc/fig17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig17.png -------------------------------------------------------------------------------- /doc/fig18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig18.png -------------------------------------------------------------------------------- /doc/fig19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig19.png -------------------------------------------------------------------------------- /doc/fig2_movie.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig2_movie.gif -------------------------------------------------------------------------------- /doc/fig3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig3.png -------------------------------------------------------------------------------- /doc/fig3b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig3b.png -------------------------------------------------------------------------------- /doc/fig4_movie.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig4_movie.gif -------------------------------------------------------------------------------- /doc/fig5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig5.png -------------------------------------------------------------------------------- /doc/fig6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig6.png -------------------------------------------------------------------------------- /doc/fig7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig7.png -------------------------------------------------------------------------------- /doc/fig8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig8.png -------------------------------------------------------------------------------- /doc/fig9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ARM-software/mram_simulation_framework/2624e42012c33a3c105062a5f1c07e53a648bc0a/doc/fig9.png -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Arm's MRAM Simulation/Characterization Framework 2 | 3 | ## Quick Start & More info 4 | Full readme: [README.md](../README.md) 5 | Summary: 6 | * `test_sllgs_solver.py` shows you the basic s-LLGS solver config, calling (`sllgs_solver.py`) 7 | * `stochastic_multithread_simulation.py` (calling `sllgs_solver.py`) is the script 8 | that helps you launching parallel python s-LLGS simulations 9 | * These results can be compared against Fooker-Plank simulations (see `plot_sllgs_fpe_comparison.py` script) 10 | * `analytical.py` and `mtj_fp_fvm.py` contain the Fooker-Plank solvers. Analytical contains the WER/RER fitter for the problem optimization 11 | * Verilog-a compact models: run the testbenches `tb.scs` and `tb_subckt.scs` 12 | 13 | Please, read the full description at [MRAM Framework Description](./doc/README.md). 14 | 15 | -------------------------------------------------------------------------------- /src/fokker_plank/README.md: -------------------------------------------------------------------------------- 1 | ## Quick Start & More info 2 | Summary: 3 | * Fooker-Plank simulations (see `plot_sllgs_fpe_comparison.py` script) `analytical.py` and `mtj_fp_fvm.py` 4 | 5 | Please, read the full description at [MRAM Framework Description](./doc/README.md). 6 | 7 | 8 | ## Files organization 9 | * `doc` 10 | * [README.md](./doc/README.md) for the **full MRAM framework description** 11 | * `src` 12 | * `fokker_plank` 13 | * [README.md](./fokker_plank/README.md) for the MRAM Fokker-Plank description 14 | * `fvm` 15 | * `fvm_classes.py` Finite Volume Method classes, see [FVM](https://github.com/danieljfarrell/FVM) 16 | * `mtj_fp_fvm.py` MTJ Fokker-Plank FVM solver 17 | * `analytical` 18 | * `analytical.py` MTJ Fokker-Plank Analytical solver 19 | and WER/RER curves fitter 20 | 21 | -------------------------------------------------------------------------------- /src/fokker_plank/analytical/analytical.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright (c) 2021-2021 Arm Ltd. 5 | # All rights reserved. 6 | # 7 | # SPDX-License-Identifier: BSD-3-Clause 8 | 9 | """ 10 | Basic functions solving FPE. 11 | 12 | Based on 13 | Tzoufras, M. (2018). 14 | Switching probability of all-perpendicular spin valve nanopillars. 15 | AIP Advances, 8(5). 16 | https://doi.org/10.1063/1.5003832 17 | 18 | FPE is expanded into Legendre pilynomials: 19 | ρ(θ, τ) = sum[ r_n (τ) Pn(cos θ) 20 | P_n(x) are the Legendre polynomials solution to eq (3) in [1] 21 | 22 | ∂ρ/∂τ = sum[ sum [r_n a_{n+k, n} P_{n+k}] ] -> 23 | ∂r/∂τ = Ar⇒r(τ) = e^(Aτ)r(0) eq (11) at[1] 24 | A is the pentadiagonal matrix with Legendre coefficients (at generate_A()) 25 | 26 | 27 | Notes: 28 | ρ area over theta should be 1/2pi, following [1] 29 | as it integration should be over phi dimension as well in spherical coord 30 | (i.e. int( 2*pi*f(theta)*sin(theta)d_theta ) 31 | """ 32 | 33 | import numpy as np 34 | import scipy as scipy 35 | import scipy.special 36 | from scipy.stats import maxwell 37 | from scipy import optimize 38 | 39 | import fvm.mtj_fp_fvm as fvm 40 | import python_compact_model.sllgs_solver as sllgs_solver 41 | 42 | 43 | def sph_area_rho_over_theta(rho, theta, axis=-1): 44 | """ 45 | Compute the area of rho over theta in an spherical system. 46 | 47 | a) d_line_theta = sin(theta) dtheta 48 | b) d_solid_angle = sin(theta) dtheta dphi 49 | c) surface element in a surface of polar angle θ constant 50 | (a cone with vertex the origin): 51 | d_s_theta = r sin(theta) dphy dr 52 | d) surface element in a surface of azimuth φ const (a vertical half-plane): 53 | d_s_phi = r dr dtheta 54 | Expects theta between [PI, 0] (comming from z in [-1, 1]). 55 | """ 56 | return np.abs(np.trapz(rho*np.sin(theta), x=theta)) 57 | 58 | 59 | def generate_A(i, h, delta, j): 60 | """Compute the pentadiagonal matrix.""" 61 | # print(f'[debug] i: {i}, h: {h}, delta: {delta}') 62 | field_diff = i - h 63 | inv_2_delta = 1.0/(2.0*delta) 64 | A = np.zeros([j+1, j+1], dtype=float) 65 | # fill matrix 66 | for n in range(2, j-1): 67 | n2 = 2*n 68 | A[n-2, n] = -1.0*n*(n-1)*(n-2)/((n2+1)*(n2-1)) 69 | A[n-1, n] = field_diff*n*(n-1)/(n2+1) 70 | A[n, n] = -1.0*n*(n+1)*(inv_2_delta-1/((n2+3)*(n2-1))) 71 | A[n+1, n] = -1.0*field_diff*(n+1)*(n+2)/(n2+1) 72 | A[n+2, n] = (n+1)*(n+2)*(n+3)/((n2+1)*(n2+3)) 73 | # initial cases and last cases 74 | # [0,0] = 0 75 | A[1, 0] = -1.0*field_diff*2 76 | A[2, 0] = 2.0 77 | # [0,1] = 0 78 | A[1, 1] = -2.0*(inv_2_delta - 1/5) 79 | A[2, 1] = -1.0*field_diff*2 80 | A[3, 1] = 2*3*4/(3*5) 81 | A[j-3, j-1] = -1.0*(j-1)*(j-2)*(j-3)/((2*j-1)*(2*j-3)) 82 | A[j-2, j-1] = field_diff*(j-1)*(j-2)/(2*j-1) 83 | A[j-1, j-1] = -1.0*(j-1)*j*(inv_2_delta - 1/((2*j+1)*(2*j-3))) 84 | A[j, j-1] = -1.0*field_diff*j*(j+1)/(2*j-1) 85 | A[j-2, j] = -1.0*j*(j-1)*(j-2)/((2*j+1)*(2*j-1)) 86 | A[j-1, j] = field_diff*j*(j-1)/(2*j+1) 87 | A[j, j] = -1.0*j*(j+1)*(inv_2_delta - 1/((2*j+3)*(2*j-1))) 88 | 89 | return A 90 | 91 | 92 | def get_state(tau, i=None, h=None, delta=None, state_0=None, a_matrix=None): 93 | """ 94 | Compute the magnetization state. 95 | 96 | r(τ) = e^(Aτ)r(0) eq (11) at[1] 97 | """ 98 | if a_matrix is not None: 99 | # get state from a known A matrix 100 | # A matrix can be shared and it takes time to build 101 | return np.matmul(scipy.linalg.expm(tau*a_matrix), state_0) 102 | return np.matmul(scipy.linalg.expm( 103 | tau*generate_A(i, h, delta, state_0.size-1)), state_0) 104 | 105 | 106 | def untangle_state(m_state, 107 | lin_space_z=False, 108 | dim_points=2000): 109 | """Untangle the Legendre series from its coefficients.""" 110 | if lin_space_z: 111 | z = np.linspace(-1, 1, dim_points) 112 | theta = np.arccos(z) 113 | return theta, np.polynomial.legendre.legval(z, m_state) 114 | theta = np.linspace(np.pi, 0, dim_points) 115 | return theta, np.polynomial.legendre.legval(np.cos(theta), m_state) 116 | 117 | 118 | # @np.vectorize 119 | def get_time_to_sw(a_matrix, s_rho_0, 120 | rho_0_at_pi, 121 | target_sw_prob=0.5, 122 | target_tolerance=1e-3, 123 | t_max=10, 124 | do_manual_sw_prob=False, 125 | sn=None): 126 | """Compute time to switch for a given MTJ for a given A matrix.""" 127 | dim_points = 10000 128 | max_tau_considered = 1e15 129 | max_iteration = int(1e2) 130 | t_min = 0 131 | 132 | # initialize integration matrix 133 | if not do_manual_sw_prob and sn is None: 134 | sn = get_sn(s_rho_0.shape[0]-1) 135 | 136 | # ensure switching 137 | while True: 138 | new_state = get_state(t_max, a_matrix=a_matrix, state_0=s_rho_0) 139 | if do_manual_sw_prob: 140 | theta, data = untangle_state(new_state, dim_points=dim_points) 141 | sw_prob = compute_sw_prob(data=data, 142 | theta=theta, 143 | rho_0_at_pi=rho_0_at_pi, 144 | normalize=True) 145 | else: 146 | sw_prob = 1 - get_analytical_sw_prob(new_state, sn) 147 | if sw_prob > target_sw_prob: 148 | break 149 | if t_max > max_tau_considered: 150 | print('tmax does not meet requirements') 151 | return np.inf 152 | t_max *= 10 153 | 154 | iteration = 0 155 | while iteration < max_iteration: 156 | t = (t_min + t_max)/2 157 | new_state = get_state(t, a_matrix=a_matrix, state_0=s_rho_0) 158 | theta, data = untangle_state(new_state, dim_points=dim_points) 159 | if do_manual_sw_prob: 160 | theta, data = untangle_state(new_state, dim_points=dim_points) 161 | sw_prob = compute_sw_prob(data=data, 162 | theta=theta, 163 | rho_0_at_pi=rho_0_at_pi, 164 | normalize=True) 165 | else: 166 | sw_prob = 1 - get_analytical_sw_prob(new_state, sn) 167 | if sw_prob < 0: 168 | print(f'[error] negative sw t: {t}, sw_prob: {sw_prob}') 169 | return np.inf 170 | if np.abs(sw_prob - target_sw_prob) < target_tolerance: 171 | print(f'\t\tfound t: {t}, sw prob: {sw_prob}') 172 | return t 173 | if iteration > 1 and iteration % 50 == 1: 174 | print(f'iteration: {iteration}, t: {t}, sw_prob: {sw_prob}' 175 | f' t_max: {t_max}, t_min: {t_min}') 176 | if sw_prob < target_sw_prob: 177 | t_min = t 178 | else: 179 | t_max = t 180 | iteration += 1 181 | # max iterations 182 | return t 183 | 184 | 185 | def get_time_to_sw_fitting(c, delta, nu, alpha, h_k, 186 | temperature=300, 187 | rho_0_at_pi=False): 188 | """ 189 | Compute time to switch for a given MTJ/current for curve fitting. 190 | 191 | h=0 and only current/delta can vary. 192 | parameters are: i_c, t_d, delta 193 | """ 194 | temperature = 300 195 | L0_max = 150 196 | lin_space_z = False 197 | # i 198 | _, _, s_rho_0 = get_state_rho_0(delta=delta, 199 | do_maxwell=False, 200 | L0=L0_max, 201 | rho_0_at_pi=rho_0_at_pi, 202 | lin_space_z=lin_space_z) 203 | print(f'nu passed: {nu} alpha passed: {alpha} h_k: {h_k}') 204 | i_c = delta*(4*alpha*sllgs_solver.c_E*sllgs_solver.c_KB * 205 | temperature)/(nu*sllgs_solver.c_hbar) 206 | t_d = (1+alpha*alpha)/(alpha*sllgs_solver.c_gamma_0*sllgs_solver.c_U0*h_k) 207 | i = c/i_c 208 | sw_t = np.zeros(i.shape) 209 | # alpha = 0.02 210 | # nu = 0.3 211 | # temperature = 300 212 | # delta = nu*llg.c_hbar * i_c / (4*alpha*llg.c_E*llg.c_KB*temperature) 213 | print(f'delta: {delta} for ic: {i_c}, t_d {t_d}') 214 | for ii_idx, ii in enumerate(i): 215 | # share A 216 | A = generate_A(ii, 0.0, delta, s_rho_0.size-1) 217 | sw_t[ii_idx] = get_time_to_sw(A, s_rho_0, rho_0_at_pi) 218 | 219 | sw_t *= t_d 220 | if np.any(sw_t <= 0): 221 | print('[error] negative sw time') 222 | return -1 223 | 224 | return sw_t 225 | 226 | 227 | def basic_error_fn(error_mode, _get_time_to_sw_fitting, currents, _times): 228 | """Specify basic error fn.""" 229 | if error_mode == 0: 230 | print('[fitting] Error mode 0: doing abs(err/x)^2') 231 | 232 | def err(p): return np.mean(( 233 | (_get_time_to_sw_fitting(currents, *p)-_times)/_times)**2) 234 | elif error_mode == 1: 235 | print('[fitting] Error mode 1: doing abs(err)^2') 236 | 237 | def err(p): return np.mean(( 238 | (_get_time_to_sw_fitting(currents, *p)-_times))**2) 239 | elif error_mode == 2: 240 | print('[fitting] Error mode 2: doing abs(err/x)') 241 | 242 | def err(p): return np.mean(np.abs( 243 | (_get_time_to_sw_fitting(currents, *p)-_times)/_times)) 244 | elif error_mode == 3: 245 | print('[fitting] Error mode 3: doing abs(err)') 246 | 247 | def err(p): return np.mean(np.abs( 248 | (_get_time_to_sw_fitting(currents, *p)-_times))) 249 | return err 250 | 251 | 252 | def _minimize_error(err, minimize_mode, p0, bounds): 253 | """ 254 | Perform minimization. 255 | 256 | Mode 0: global using basinhopping 257 | Mode 1: global using shgo 258 | Mode 2: global using brute force 259 | Mode 3: local using minimize 260 | """ 261 | # choose minimization scheme 262 | if minimize_mode == 0: 263 | print('[fitting] Optimization algorithm: Basin Hopping') 264 | # basinhopping for global minima 265 | res = optimize.basinhopping(err, 266 | p0, 267 | # stepsize=0.5, 268 | niter=500, 269 | # accept_test=my_bounds, 270 | minimizer_kwargs={'bounds': bounds} 271 | ) 272 | print(f'[res] Mode {minimize_mode}: {res}') 273 | popt = res.x 274 | return popt 275 | elif minimize_mode == 1: 276 | print('[fitting] Optimization algorithm: SHGO') 277 | # shgo 278 | res = optimize.shgo(err, 279 | bounds=bounds, 280 | iters=200) 281 | print(f'[res] Mode {minimize_mode}: {res}') 282 | popt = res.x 283 | return popt 284 | elif minimize_mode == 2: 285 | print('[fitting] Optimization algorithm: Brute') 286 | # shgo 287 | res = optimize.brute(err, 288 | ranges=bounds, 289 | finish=optimize.fmin) 290 | print(f'[res] Mode {minimize_mode}: {res}') 291 | # if full_output is set to True 292 | # res[0] has the params, res[1] the error evaluation 293 | # otherwise: 294 | popt = res 295 | return popt 296 | elif minimize_mode == 3: 297 | print('[fitting] Optimization using local algorithm') 298 | # minimize for local minima 299 | res = optimize.minimize(err, 300 | x0=p0, 301 | bounds=bounds, 302 | method='L-BFGS-B', 303 | options={'eps': 1e-13}, 304 | # method='dogbox', 305 | ) 306 | print(f'[res] Mode {minimize_mode}: {res}') 307 | popt = res.x 308 | return popt 309 | 310 | 311 | def _fit_current_time_points(currents, 312 | times, 313 | rho_0_at_pi, 314 | p0=None, 315 | bounds=((0., 0.), (np.inf, np.inf)), 316 | do_log=False, 317 | minimize_mode=0, 318 | error_mode=0): 319 | """Fit current/time 2d array to i/h params.""" 320 | # generate s_rho_0 321 | L0_max = 150 322 | lin_space_z = False 323 | temperature = 300 324 | # define internal fn to not pass extra params like rho_0_at_pi 325 | 326 | # delta = 55 327 | # def _get_time_to_sw_fitting(c, nu, alpha, h_k): 328 | def _get_time_to_sw_fitting(c, delta, nu, alpha, h_k): 329 | """ 330 | Compute time to switch for a given MTJ/current for curve fitting. 331 | 332 | h=0 and only current/delta can vary. 333 | parameters are: delta, nu, alpha, h_k 334 | """ 335 | print(f'delta: {delta}, nu: {nu} alpha: {alpha} h_k: {h_k}') 336 | _p = np.array([delta, nu, alpha, h_k]) 337 | if np.any(np.isnan(_p)) or np.any(np.isinf(_p)): 338 | print('[warning] optimizer passing NaN') 339 | return np.nan 340 | # initial state cannot be passed 341 | _, _, s_rho_0 = get_state_rho_0(delta=delta, 342 | do_maxwell=False, 343 | L0=L0_max, 344 | rho_0_at_pi=rho_0_at_pi, 345 | lin_space_z=lin_space_z) 346 | # i 347 | i_c = delta*(4*alpha*sllgs_solver.c_E*sllgs_solver.c_KB * 348 | temperature)/(nu*sllgs_solver.c_hbar) 349 | t_d = (1+alpha*alpha) / \ 350 | (alpha*sllgs_solver.c_gamma_0*sllgs_solver.c_U0*h_k) 351 | i = c/i_c 352 | sw_t = np.zeros(i.shape) 353 | # alpha = 0.02 354 | # nu = 0.3 355 | # temperature = 300 356 | # delta = nu*llg.c_hbar * i_c / (4*alpha*llg.c_E*llg.c_KB*temperature) 357 | print(f'delta: {delta} for ic: {i_c}, t_d {t_d}') 358 | for ii_idx, ii in enumerate(i): 359 | # share A 360 | A = generate_A(ii, 0.0, delta, s_rho_0.size-1) 361 | sw_t[ii_idx] = get_time_to_sw(A, s_rho_0, rho_0_at_pi) 362 | 363 | sw_t *= t_d 364 | if np.any(sw_t <= 0): 365 | print('[error] negative sw time') 366 | return -np.inf 367 | 368 | if do_log: 369 | return np.log(sw_t) 370 | return sw_t 371 | 372 | _times = times 373 | if do_log: 374 | _times = np.log(times) 375 | 376 | # minimize approach 377 | err = basic_error_fn(error_mode=error_mode, 378 | _get_time_to_sw_fitting=_get_time_to_sw_fitting, 379 | currents=currents, 380 | _times=_times) 381 | 382 | # minimize 383 | popt = _minimize_error(err, minimize_mode, p0, bounds) 384 | 385 | if do_log: 386 | return popt, np.exp(_get_time_to_sw_fitting(currents, *popt)) 387 | return popt, _get_time_to_sw_fitting(currents, *popt) 388 | 389 | 390 | def get_nc(delta): 391 | """Critical Nc.""" 392 | return np.sqrt((delta/2)+1)-1/2 393 | 394 | 395 | def get_state_rho_0(delta, 396 | L0=150, 397 | rho_0_at_pi=False, 398 | lin_space_z=False, 399 | do_maxwell=False, 400 | maxwell_loc=None, 401 | maxwell_scale=None, 402 | dim_points=2000): 403 | """Generate the initial rho_0 distribution.""" 404 | if lin_space_z: 405 | # rho with equidistant z (fitting on z, so best option) 406 | z = np.linspace(-1, 1, dim_points) 407 | theta = np.arccos(z) 408 | else: 409 | # rho with equidistance theta 410 | theta = np.linspace(np.pi, 0, dim_points) 411 | z = np.cos(theta) 412 | 413 | if do_maxwell: 414 | if maxwell_loc is None or maxwell_scale is None: 415 | theta_0 = 1/np.sqrt(2*delta) 416 | maxwell_scale = theta_0 417 | # theta = np.arccos(z) 418 | # maxwell_loc = 0 # theta_0 419 | maxwell_loc = -theta_0 420 | # maxwell_loc = -theta_0/2 421 | # fitted 422 | rho_0 = maxwell.pdf(theta, 423 | loc=maxwell_loc, 424 | scale=maxwell_scale) 425 | else: 426 | sin_theta = np.sin(theta) 427 | rho_0 = np.exp(-delta*sin_theta*sin_theta)*np.heaviside(np.pi/2-theta, 428 | 0.5) 429 | # area over theta should be 1/2pi:, following 430 | # Tzoufras, M. (2018). 431 | # Switching probability of all-perpendicular spin valve nanopillars. 432 | # AIP Advances, 8(5). 433 | # https://doi.org/10.1063/1.5003832 434 | area = sph_area_rho_over_theta(rho_0, theta) 435 | rho_0 /= (2*np.pi*area) 436 | # flip if rho at pi 437 | if rho_0_at_pi: 438 | rho_0 = rho_0[::-1] 439 | 440 | # fit legendre coefficients 441 | print(f'[debug] fitting to delta {delta}') 442 | s_rho_0 = np.polynomial.legendre.legfit(x=z, 443 | y=rho_0, 444 | deg=L0) 445 | 446 | return z, rho_0, s_rho_0 447 | 448 | 449 | def get_sn(L0): 450 | """ 451 | Compute s_n vector from eq (12) in [1]. 452 | 453 | s_n = int_0^1 (P_n(x) dx) 454 | """ 455 | dtype = np.longdouble 456 | # dtype = np.float 457 | # print('using double precision and exact factorial2') 458 | fact_type = np.int 459 | print('using exact factorial2') 460 | 461 | sn = np.arange(L0+1, dtype=dtype) 462 | sign = -1.0 * np.ones(sn[3::2].shape[0]).astype(dtype) 463 | sign[1::2] = 1.0 464 | # factorials 465 | nf = np.array([scipy.special.factorial2(f, exact=True) 466 | for f in sn[3::2].astype(fact_type)]).astype(dtype) 467 | nfm1 = np.array([scipy.special.factorial2(f, exact=True) 468 | for f in sn[2:-1:2].astype(fact_type)]).astype(dtype) 469 | # odd numbers 470 | sn[3::2] = sign * nf / (sn[3::2]*(sn[3::2]+1)*nfm1) 471 | # even numbers 472 | sn[2::2] = 0.0 473 | # n==0 and n==1 474 | sn[0] = 1.0 475 | sn[1] = 0.5 476 | 477 | return sn.astype(float) 478 | 479 | 480 | def get_analytical_sw_prob(state, sn=None): 481 | """Compute switching probability eq (12) in [1].""" 482 | if sn is None: 483 | sn = get_sn(state.shape[0]) 484 | return np.dot(2.0*np.pi*sn, state) 485 | 486 | 487 | def compute_sw_prob(data, theta, rho_0_at_pi, normalize=False): 488 | """ 489 | Compute switching probability with area on spherical coordinates. 490 | 491 | data is expected to be [time, theta] or [theta]. 492 | FVM and legendre analytical compute over z. 493 | Transformation is from int^1_-1 [ f(z)dz ] to 494 | int^hemisphere [ f(theta) sin(theta) dtheta ]. 495 | area is scalled by 2PI as the integral of rho should also be done 496 | over unit phi vector between 0-2PI. 497 | """ 498 | if len(data.shape) == 1: 499 | data = np.expand_dims(data, axis=0) 500 | # area = sph_area_rho_over_theta(data, theta, axis=-1) 501 | # print(f'[debug] area*2pi: {2*np.pi*area}') 502 | # normalize not needed 503 | # if normalize: 504 | # area = sph_area_rho_over_theta(data, theta, axis=-1) 505 | # print(f'[debug] area*2pi: {2*np.pi*area}') 506 | # # print('[debug] area*2pi over theta ' 507 | # # f'{2*np.pi*np.trapz(data, np.cos(theta))}') 508 | # first_theta = theta[0] 509 | # if first_theta > np.pi/2: 510 | # # theta pi->0 511 | # # broadcast op with None 512 | # data /= (-area[:, None]) 513 | # else: 514 | # # theta 0-> pi 515 | # # broadcast op with None 516 | # data /= area[:, None] 517 | if rho_0_at_pi: 518 | return 2*np.pi*sph_area_rho_over_theta(data[:, theta < np.pi/2], 519 | theta[theta < np.pi/2]) 520 | return 2*np.pi*sph_area_rho_over_theta(data[:, theta > np.pi/2], 521 | theta[theta > np.pi/2]) 522 | 523 | 524 | def get_sw_prob(s_rho_0, tau, delta, i, h, 525 | rho_0_at_pi=False, 526 | compute_fvm=False, 527 | compute_analytical_sw=False, 528 | compute_analytical_manual=False, 529 | dim_points=500, 530 | lin_space_z=False, 531 | t_step=1e-3, 532 | sn=None): 533 | """ 534 | Evolve FPE and compute switching probability over theta. 535 | 536 | FVM and legendre analytical compute over z. 537 | Transformation is from int^1_-1 [ f(z)dz ] to 538 | int^hemisphere [ f(theta) sin(theta) dtheta ]. 539 | """ 540 | # analytical rho computation 541 | new_state = get_state(tau, i, h, delta, s_rho_0) 542 | theta, data = untangle_state(new_state, dim_points=5000) 543 | if compute_analytical_manual: 544 | manual_prob = compute_sw_prob(data=data, 545 | theta=theta, 546 | rho_0_at_pi=rho_0_at_pi, 547 | normalize=True) 548 | else: 549 | manual_prob = -1 550 | if compute_fvm: 551 | theta, rho_init = untangle_state(s_rho_0, 552 | dim_points=dim_points, 553 | lin_space_z=lin_space_z) 554 | rho_init = np.array(rho_init) 555 | fvm_data = fvm.solve_mtj_fp(rho_init=rho_init, 556 | delta=delta, 557 | i0=i, 558 | h=h, 559 | T=tau, 560 | dim_points=dim_points+1, 561 | t_step=t_step, 562 | do_3d=False, 563 | lin_space_z=lin_space_z) 564 | theta = np.arccos(fvm_data['z0']) 565 | data = fvm_data['rho'] 566 | fvm_prob = compute_sw_prob(data=data, 567 | theta=theta, 568 | rho_0_at_pi=rho_0_at_pi, 569 | normalize=True) 570 | else: 571 | fvm_prob = -1 572 | if compute_analytical_sw: 573 | analytical_prob = 1 - get_analytical_sw_prob(new_state, sn) 574 | else: 575 | analytical_prob = -1 576 | return {'manual_prob': manual_prob, 577 | 'fvm_prob': fvm_prob, 578 | 'analytical_prob': analytical_prob} 579 | 580 | 581 | def get_sw_continuous_prob(s_rho_0, tau, delta, i, h, 582 | rho_0_at_pi=False, 583 | compute_fvm=False, 584 | compute_analytical_sw=False, 585 | compute_analytical_manual=False, 586 | dim_points=1000, 587 | lin_space_z=False, 588 | t_step=1e-3, 589 | sn=None): 590 | """Evolve FPE and compute switching probability over time between 0-tau.""" 591 | # analytical rho computation 592 | time = np.arange(0, tau, t_step) 593 | fvm_time = np.arange(0, tau, t_step) 594 | manual_prob = np.zeros(time.shape[0]) 595 | distribution = np.zeros((time.shape[0], dim_points)) 596 | analytical_prob = np.zeros(time.shape[0]) 597 | # share A 598 | A = generate_A(i, h, delta, s_rho_0.size-1) 599 | for t_idx, t in enumerate(time): 600 | new_state = get_state(t, a_matrix=A, state_0=s_rho_0) 601 | theta, data = untangle_state(new_state, dim_points=dim_points) 602 | distribution[t_idx] = data 603 | if compute_analytical_manual: 604 | manual_prob[t_idx] = compute_sw_prob(data=data, 605 | theta=theta, 606 | rho_0_at_pi=rho_0_at_pi, 607 | normalize=True) 608 | else: 609 | manual_prob[t_idx] = -1 610 | if compute_analytical_sw: 611 | analytical_prob[t_idx] = 1 - get_analytical_sw_prob(new_state, sn) 612 | else: 613 | analytical_prob[t_idx] = -1 614 | fvm_theta = theta 615 | if compute_fvm: 616 | theta, rho_init = untangle_state(s_rho_0, 617 | dim_points=dim_points, 618 | lin_space_z=lin_space_z) 619 | rho_init = np.array(rho_init) 620 | fvm_data = fvm.solve_mtj_fp(rho_init=rho_init, 621 | delta=delta, 622 | i0=i, 623 | h=h, 624 | T=tau, 625 | dim_points=dim_points+1, 626 | t_step=t_step, 627 | do_3d=True, 628 | lin_space_z=lin_space_z) 629 | fvm_theta = np.arccos(fvm_data['z0']) 630 | data = fvm_data['rho'].T 631 | fvm_time = fvm_data['t0'] 632 | fvm_prob = compute_sw_prob(data=data, 633 | theta=fvm_theta, 634 | rho_0_at_pi=rho_0_at_pi, 635 | normalize=True) 636 | else: 637 | fvm_prob = -1*np.ones(time.shape[0]) 638 | return {'time': time, 639 | 'theta': theta, 640 | 'manual_prob': manual_prob, 641 | 'pdf': distribution, 642 | 'fvm_time': fvm_time, 643 | 'fvm_theta': fvm_theta, 644 | 'fvm_prob': fvm_prob, 645 | 'analytical_prob': analytical_prob} 646 | -------------------------------------------------------------------------------- /src/fokker_plank/analytical/fvm: -------------------------------------------------------------------------------- 1 | ../fvm/ -------------------------------------------------------------------------------- /src/fokker_plank/analytical/fvm_lib: -------------------------------------------------------------------------------- 1 | fvm/fvm_lib/ -------------------------------------------------------------------------------- /src/fokker_plank/analytical/ivp_lib: -------------------------------------------------------------------------------- 1 | python_compact_model/ivp_lib/ -------------------------------------------------------------------------------- /src/fokker_plank/analytical/python_compact_model: -------------------------------------------------------------------------------- 1 | ../../python_compact_model/ -------------------------------------------------------------------------------- /src/fokker_plank/fvm/fvm_lib/fvm_classes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | 5 | # Copyright (c) 2021. Daniel J. Farrell 6 | # All rights reserved. 7 | # 8 | # SPDX-License-Identifier: BSD-3-Clause 9 | # adapted from Daniel J Farrell notes no FVM 10 | # danieljfarrell.github.io/FVM/overview.html 11 | 12 | 13 | """Finite Volume Method Classes.""" 14 | 15 | import numpy as np 16 | from scipy import sparse 17 | from scipy.sparse import dia_matrix 18 | 19 | 20 | # Supporting functions 21 | def check_index_within_bounds(i, min_i, max_i): 22 | """Check that the index (number or an iterable) is within the range.""" 23 | return np.logical_and(i >= min_i, i <= max_i).all() 24 | 25 | 26 | class Mesh(object): 27 | """A 1D cell centered mesh defined by faces for the FVM.""" 28 | 29 | def __init__(self, faces): 30 | """Init method.""" 31 | super(Mesh, self).__init__() 32 | 33 | # Check for duplicated points 34 | if len(faces) != len(set(faces)): 35 | raise ValueError( 36 | "The faces array contains duplicated positions." 37 | "No cell can have zero volume so please" 38 | " update with unique face positions.") 39 | self.faces = np.array(faces) 40 | self.cells = 0.5 * (self.faces[0:-1] + self.faces[1:]) 41 | self.J = len(self.cells) 42 | self.cell_widths = (self.faces[1:] - self.faces[0:-1]) 43 | 44 | def h(self, i): 45 | """Return the width of the cell at the specified index.""" 46 | return self.cell_widths[i] 47 | 48 | def hm(self, i): 49 | """Distance between centroids in the backwards direction.""" 50 | if not check_index_within_bounds(i, 1, self.J-1): 51 | raise ValueError("hm index runs out of bounds") 52 | return (self.cells[i] - self.cells[i-1]) 53 | 54 | def hp(self, i): 55 | """Distance between centroids in the forward direction.""" 56 | if not check_index_within_bounds(i, 0, self.J-2): 57 | raise ValueError("hp index runs out of bounds") 58 | return (self.cells[i+1] - self.cells[i]) 59 | 60 | 61 | class CellVariable(np.ndarray): 62 | """ 63 | Representation of a variable defined at the cell centers. 64 | 65 | Provides interpolation functions to calculate the value at cell faces. 66 | """ 67 | # http://docs.scipy.org/doc/numpy/user/basics.subclassing.html 68 | def __new__(cls, input_array, mesh=None): 69 | """Init like method.""" 70 | # If `input_array` is actually just a constant 71 | # convert it to an array of len the number of cells. 72 | try: 73 | len(input_array) 74 | except TypeError: 75 | input_array = input_array*np.ones(len(mesh.cells)) 76 | 77 | obj = np.asarray(input_array).view(cls) 78 | obj.mesh = mesh 79 | return obj 80 | 81 | def __array_finalize__(self, obj): 82 | """Build like method.""" 83 | if obj is None: 84 | return 85 | self.mesh = getattr(obj, 'mesh', None) 86 | self.__get_items__ = getattr(obj, '__get_items__', None) 87 | 88 | def m(self, i): 89 | """ 90 | Linear interpolation of the cell value at the right hand face . 91 | 92 | i.e. along the _m_inus direction. 93 | """ 94 | return (self.mesh.h(i)/(2*self.mesh.hm(i))*self[i-1] 95 | + self.mesh.h(i-1)/(2*self.mesh.hm(i))*self[i]) 96 | 97 | def p(self, i): 98 | """ 99 | Linear interpolation of the cell value at the right hand face. 100 | 101 | i.e. along the _p_lus direction. 102 | """ 103 | return (self.mesh.h(i+1)/(2*self.mesh.hp(i))*self[i] 104 | + self.mesh.h(i)/(2*self.mesh.hp(i))*self[i+1]) 105 | 106 | 107 | class AdvectionDiffusionModel(object): 108 | """A model for the advection-diffusion equation.""" 109 | 110 | def __init__(self, faces, a, d, k, discretization="central"): 111 | """Init.""" 112 | super(AdvectionDiffusionModel, self).__init__() 113 | 114 | self.mesh = Mesh(faces) 115 | self.a = CellVariable(a, mesh=self.mesh) 116 | self.d = CellVariable(d, mesh=self.mesh) 117 | self.t_step = k 118 | self.discretization = discretization 119 | 120 | # Check Peclet number 121 | mu = self.peclet_number() 122 | mu_max = np.max(np.abs(mu)) 123 | if mu_max >= 1.5 and mu_max < 2.0: 124 | print(f'\n\nThe Peclet number is {mu_max},' 125 | ' this is getting close to the limit of mod 2.' 126 | '\nINCREASE precision on spatial axis') 127 | # warnings.warn('The Peclet number is %g,' 128 | # ' this is getting close to the limit of mod 2.') 129 | elif mu_max > 2: 130 | print(f'\n\nThe Peclet number {mu_max} has exceeded the maximum' 131 | ' value of mod 2 for the central discretization scheme.' 132 | '\nINCREASE precision on spatial axis') 133 | # warnings.warn( 134 | # 'The Peclet number %g has exceeded the maximum' 135 | # ' value of mod 2 for the central discretization scheme.', 136 | # mu_max) 137 | 138 | # Check CFL condition 139 | CFL = self.CFL_condition() 140 | CFL_max = np.max(np.abs(CFL)) 141 | if CFL_max > 0.5 and CFL_max < 1.0: 142 | print(f'\n\n[WARNING] The CFL condition is {CFL_max}', 143 | 'it is getting close to the upper limit.' 144 | '\nINCREASE precision on time axis') 145 | # warnings.warn('[WARNING] The CFL condition is %g', 146 | # 'it is getting close to the upper limit.', 147 | # CFL_max) 148 | elif np.max(np.abs(CFL)) > 1: 149 | print(f'\n\n[WARNING] The CFL condition is {CFL_max},' 150 | 'and has gone above the upper limit.' 151 | '\nINCREASE precision on time axis') 152 | # warnings.warn('[WARNING] The CFL condition is %g,' 153 | # 'and has gone above the upper limit.', 154 | # CFL_max) 155 | 156 | if discretization == 'exponential': 157 | self.kappa = (np.exp(mu) + 1)/(np.exp(mu) - 1) - 2/mu 158 | self.kappa[np.where(mu == 0.0)] = 0 159 | self.kappa[np.where(np.isposinf(mu))] = 1 160 | self.kappa[np.where(np.isneginf(mu))] = -1 161 | elif discretization == 'upwind': 162 | kappa_neg = np.where(self.a < 0, -1, 0) 163 | kappa_pos = np.where(self.a > 0, 1, 0) 164 | self.kappa = kappa_neg + kappa_pos 165 | elif discretization == 'central': 166 | self.kappa = np.zeros(self.mesh.J) 167 | else: 168 | print('Please set "discretization" to one of the following:' 169 | '"upwind", "central" or "exponential"') 170 | 171 | # Artificially modify the diffusion coefficient 172 | # to introduce adpative discretization 173 | self.d = self.d + 0.5 * self.a * self.mesh.cell_widths * self.kappa 174 | print(f'kappa min:{np.min(self.kappa)}, max:{np.max(self.kappa)}') 175 | # print(f'kappa: {self.kappa}') 176 | 177 | def peclet_number(self): 178 | """Get Peclet number.""" 179 | return self.a * self.mesh.cell_widths / self.d 180 | 181 | def CFL_condition(self): 182 | """Get CFL condition.""" 183 | return self.a * self.t_step / self.mesh.cell_widths 184 | 185 | def set_boundary_conditions(self, 186 | left_flux=None, 187 | right_flux=None, 188 | left_value=None, 189 | right_value=None): 190 | """ 191 | Boundary conditions. 192 | 193 | Make sure this function is used 194 | sensibly otherwise the matrix will be ill posed. 195 | """ 196 | self.left_flux = left_flux 197 | self.right_flux = right_flux 198 | self.left_value = left_value 199 | self.right_value = right_value 200 | 201 | def _interior_matrix_elements(self, i): 202 | """Set interior coefficients for matrix equation.""" 203 | def ra(i, a, d, m): 204 | return 1./m.h(i) * (a.m(i)*m.h(i)/(2*m.hm(i)) + d.m(i)/m.hm(i)) 205 | 206 | def rb(i, a, d, m): 207 | return 1./m.h(i)*( 208 | a.m(i)*m.h(i-1)/(2*m.hm(i)) - 209 | a.p(i)*m.h(i+1)/(2*m.hp(i)) - d.m(i)/m.hm(i) - d.p(i)/m.hp(i)) 210 | 211 | def rc(i, a, d, m): 212 | return 1./m.h(i) * (-a.p(i)*m.h(i)/(2*m.hp(i)) + d.p(i)/m.hp(i)) 213 | 214 | return (ra(i, self.a, self.d, self.mesh), 215 | rb(i, self.a, self.d, self.mesh), 216 | rc(i, self.a, self.d, self.mesh)) 217 | 218 | def _neumann_boundary_c_m_e_left(self): 219 | """Set Left hand side Neumann boundary conditions.""" 220 | def b1(a, d, m): 221 | return 1./m.h(0) * (-a.p(0)*m.h(1)/(2*m.hp(0)) - d.p(0)/m.hp(0)) 222 | 223 | def c1(a, d, m): 224 | return 1./m.h(0) * (-a.p(0)*m.h(0)/(2*m.hp(0)) + d.p(0)/m.hp(0)) 225 | 226 | # Index and element value 227 | locations = [(0, 0), (0, 1)] 228 | values = (b1(self.a, self.d, self.mesh), 229 | c1(self.a, self.d, self.mesh)) 230 | return tuple([list(x) for x in zip(locations, values)]) 231 | 232 | def _neumann_boundary_c_m_e_right(self, matrix=None): 233 | """Set right hand side Neumann boundary conditions.""" 234 | def aJ(a, d, m): 235 | return 1./m.h(m.J-1)*( 236 | a.m(m.J-1) * m.h(m.J-1)/(2*m.hm(m.J-1)) 237 | + d.m(m.J-1)/m.hm(m.J-1)) 238 | 239 | def bJ(a, d, m): 240 | return 1./m.h(m.J-1)*( 241 | a.m(m.J-1) * m.h(m.J-2)/(2*m.hm(m.J-1)) 242 | - d.m(m.J-1)/m.hm(m.J-1)) 243 | # Index and element value 244 | J = self.mesh.J 245 | 246 | # Index and element value 247 | locations = [(J-1, J-2), (J-1, J-1)] 248 | values = (aJ(self.a, self.d, self.mesh), 249 | bJ(self.a, self.d, self.mesh)) 250 | return tuple([list(x) for x in zip(locations, values)]) 251 | 252 | def _neumann_boundary_c_v_e_left(self): 253 | """Index and boundary cond vector elements for Neumann conditions.""" 254 | location = [0] 255 | value = [self.left_flux/self.mesh.h(0)] 256 | return tuple([list(x) for x in zip(location, value)]) 257 | 258 | def _neumann_boundary_c_v_e_right(self): 259 | """Index and boundary cond. vector elements for Neumann conditions.""" 260 | location = [self.mesh.J-1] 261 | value = [-self.right_flux/self.mesh.h(self.mesh.J-1)] 262 | return tuple([list(x) for x in zip(location, value)]) 263 | 264 | def _dirichlet_boundary_c_m_e_left(self): 265 | """Set left hand side Neumann boundary coefficients for matrix eq.""" 266 | def rb(i, a, d, m): 267 | return 1./m.h(i)*( 268 | a.m(i)*m.h(i-1)/(2*m.hm(i)) 269 | - a.p(i)*m.h(i+1)/(2*m.hp(i)) 270 | - d.m(i)/m.hm(i) 271 | - d.p(i)/m.hp(i)) 272 | 273 | def rc(i, a, d, m): 274 | return 1./m.h(i) * (-a.p(i)*m.h(i)/(2*m.hp(i)) + d.p(i)/m.hp(i)) 275 | 276 | # Index and element value 277 | locations = [(0, 0), (0, 1)] 278 | # values = ( rb(0, self.a, self.d, self.mesh ), 279 | # rc(0, self.a, self.d, self.mesh ) ) 280 | values = (0, 281 | 1) 282 | return tuple([list(x) for x in zip(locations, values)]) 283 | 284 | def _dirichlet_boundary_c_m_e_right(self): 285 | """Set right hand side Neumann boundary coefficients for matrix eq.""" 286 | def ra(i, a, d, m): 287 | return 1./m.h(i) * (a.m(i)*m.h(i)/(2*m.hm(i)) + d.m(i)/m.hm(i)) 288 | 289 | def rb(i, a, d, m): 290 | return 1./m.h(i)*( 291 | a.m(i)*m.h(i-1)/(2*m.hm(i)) 292 | - a.p(i)*m.h(i+1)/(2*m.hp(i)) 293 | - d.m(i)/m.hm(i) 294 | - d.p(i)/m.hp(i)) 295 | J = self.mesh.J # Index and element value 296 | 297 | # Index and element value 298 | locations = [(J-1, J-2), (J-1, J-1)] 299 | # values = ( ra(self.J-1, self.a, self.d, self.mesh ), 300 | # rb(self.J-1, self.a, self.d, self.mesh ) ) 301 | values = (0, 302 | 1) 303 | return tuple([list(x) for x in zip(locations, values)]) 304 | 305 | def _dirichlet_boundary_c_v_e_left(self): 306 | """ 307 | Index and boundary condition vector elements for Dirichlet conditions. 308 | 309 | NB these are always zero, unless BCs are time varying. 310 | """ 311 | location = [0] 312 | value = [0] 313 | return tuple([list(x) for x in zip(location, value)]) 314 | 315 | def _dirichlet_boundary_c_v_e_right(self): 316 | """ 317 | Index and boundary condition vector elements for Dirichlet conditions. 318 | 319 | NB these are always zero, unless BCs are time varying. 320 | """ 321 | location = [self.mesh.J-1] 322 | value = [0] 323 | return tuple([list(x) for x in zip(location, value)]) 324 | 325 | def alpha_matrix(self): 326 | """ 327 | Set alpha matrix. 328 | 329 | The alpha matrix is used to mask boundary conditions values 330 | for Dirichlet conditions. Otherwise for a fully Neumann (or Robin) 331 | system it is equal to the identity matrix. 332 | """ 333 | a1 = 0 if self.left_flux is None else 1 334 | aJ = 0 if self.left_flux is None else 1 335 | diagonals = np.ones(self.mesh.J) 336 | diagonals[0] = a1 337 | diagonals[-1] = aJ 338 | return sparse.diags(diagonals, 0) 339 | 340 | def beta_vector(self): 341 | """Return the Neumann boundary condition vector.""" 342 | b = np.zeros(self.mesh.J) 343 | 344 | if self.left_flux is not None: 345 | left_bc_elements = self._neumann_boundary_c_v_e_left() 346 | 347 | if self.right_flux is not None: 348 | right_bc_elements = self._neumann_boundary_c_v_e_right() 349 | 350 | if self.left_value is not None: 351 | left_bc_elements = self._dirichlet_boundary_c_v_e_left() 352 | 353 | if self.right_value is not None: 354 | right_bc_elements = self._dirichlet_boundary_c_v_e_right() 355 | 356 | bcs = left_bc_elements + right_bc_elements 357 | for inx, value in bcs: 358 | b[inx] = value 359 | return b 360 | 361 | def coefficient_matrix(self): 362 | """Return the coeff matrix which appears on the left hand side.""" 363 | J = self.mesh.J 364 | # k = self.k 365 | # m = self.mesh 366 | # a = self.a 367 | # d = self.d 368 | 369 | # A element which is pushed off the 370 | # edge of the matrix by the spdiags function 371 | padding = np.array([0]) 372 | # Yes, its the same. But this element 373 | # is included in the matrix (semantic difference). 374 | zero = padding 375 | # one = np.array([1]) # 376 | 377 | if self.left_flux is not None: 378 | left_bc_elements = self._neumann_boundary_c_m_e_left() 379 | 380 | if self.right_flux is not None: 381 | right_bc_elements = self._neumann_boundary_c_m_e_right() 382 | 383 | if self.left_value is not None: 384 | left_bc_elements = self._dirichlet_boundary_c_m_e_left() 385 | 386 | if self.right_value is not None: 387 | right_bc_elements = self._dirichlet_boundary_c_m_e_right() 388 | 389 | # Use the functions to layout the matrix Note that the boundary 390 | # condition elements are set to zero, they are filled in as 391 | # the next step. 392 | inx = np.array(list(range(1, J-1))) 393 | ra, rb, rc = self._interior_matrix_elements(inx) 394 | # c1 395 | upper = np.concatenate([padding, zero, rc]) 396 | 397 | # b1 bJ 398 | central = np.concatenate([zero, rb, zero]) 399 | 400 | # aJ 401 | lower = np.concatenate([ra, zero, padding]) 402 | 403 | A = sparse.spdiags([lower, central, upper], [-1, 0, 1], J, J).todok() 404 | 405 | # Apply boundary conditions elements 406 | bcs = left_bc_elements + right_bc_elements 407 | for inx, value in bcs: 408 | print(f'boundary conditions. A[{inx}]={value}') 409 | A[inx] = value 410 | return dia_matrix(A) 411 | -------------------------------------------------------------------------------- /src/fokker_plank/fvm/mtj_fp_fvm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2020-2021 Arm Ltd. 4 | # All rights reserved. 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | 8 | """ 9 | MTJ Fokker-Plank Finite Volume Method Solver. 10 | 11 | Fokker-Plank or advection-diffusion for 12 | MTJ magnetization probability evolution. 13 | """ 14 | 15 | import numpy as np 16 | from scipy import sparse 17 | from scipy.sparse import linalg 18 | import matplotlib.pyplot as plt 19 | import matplotlib.cm as cm 20 | 21 | import fvm_lib.fvm_classes as fvm 22 | 23 | np.random.seed(seed=1) 24 | 25 | 26 | def solve_mtj_fp(dim_points=1000, 27 | rho_init=None, 28 | delta=60, 29 | i0=1.5, 30 | h=0., 31 | t_step=0.001, 32 | T=10, 33 | lin_space_z=False, 34 | do_3d=True): 35 | """Solve FVM for the FPE on a perpendicular symetric MTJ.""" 36 | # cranck-nickolson 37 | theta = 0.5 38 | # fully implicit 39 | # theta = 1. 40 | 41 | # discretization 42 | discretization = 'exponential' 43 | # discretization = 'upwind' 44 | # discretization = 'central' 45 | 46 | # Dirichlet boundary conditions 47 | # right_value = 1.0 48 | # left_value = 1.0 49 | # Neumann boundary conditions 50 | left_flux = 0.0 51 | right_flux = 0.0 52 | 53 | if lin_space_z: 54 | # linear in z 55 | faces = np.linspace(-1, 1, dim_points) 56 | else: 57 | # linear in theta 58 | faces = np.cos(np.linspace(np.pi, 0, dim_points)) 59 | mesh = fvm.Mesh(faces) 60 | 61 | # drift/advection/convection 62 | U = (i0-h-mesh.cells)*(1-mesh.cells*mesh.cells) 63 | # diffusion 64 | D = (1-mesh.cells*mesh.cells)/(2*delta) 65 | 66 | # drift/advection/convection 67 | a = fvm.CellVariable(-U, mesh=mesh) 68 | # diffusion 69 | d = fvm.CellVariable(D, mesh=mesh) 70 | # Source term 71 | # s[int(np.median(range(mesh.J)))] = 0.0 72 | 73 | # Initial conditions 74 | if rho_init is None: 75 | # w_init = np.exp( 76 | # -delta*(1-mesh.cells*mesh.cells))*np.heaviside(mesh.cells, 0.5) 77 | _theta_x = np.arccos(mesh.cells) 78 | rho_init = np.exp( 79 | -delta*np.sin(_theta_x)*np.sin(_theta_x))*np.heaviside(mesh.cells, 80 | 0.5) 81 | rho_init /= np.trapz(rho_init, x=mesh.cells) 82 | # w_init = w_init[::-1] 83 | print(f'\trho_init area: {np.trapz(rho_init, x=mesh.cells)}') 84 | 85 | model = fvm.AdvectionDiffusionModel( 86 | faces, a, d, t_step, discretization=discretization) 87 | # model.set_boundary_conditions(left_value=1., right_value=0.) 88 | model.set_boundary_conditions(left_flux=left_flux, right_flux=right_flux) 89 | M = model.coefficient_matrix() 90 | alpha = model.alpha_matrix() 91 | beta = model.beta_vector() 92 | identity = sparse.identity(model.mesh.J) 93 | print(f'\talpha: [{np.min(alpha.todense())}, {np.max(alpha.todense())}]') 94 | print(f'\tbeta: [{np.min(beta)}, {np.max(beta)}]') 95 | print(f'\tidentity: {identity.shape}') 96 | 97 | # Construct linear system from discretised matrices, A.x = d 98 | A = identity - t_step*theta*alpha*M 99 | d = (identity + t_step*(1-theta)*alpha*M)*rho_init + beta 100 | 101 | print("\tPeclet number", np.min(model.peclet_number()), 102 | np.max(model.peclet_number())) 103 | print("\tCFL condition", np.min(model.CFL_condition()), 104 | np.max(model.CFL_condition())) 105 | 106 | rho = rho_init 107 | 108 | t0 = np.linspace(0, T, int(T/t_step)+1) 109 | if do_3d: 110 | rho = np.zeros((t0.shape[0], mesh.cells.shape[0])) 111 | area = np.zeros(t0.shape[0]) 112 | rho[0] = rho_init 113 | area[0] = np.trapz(rho[0], x=mesh.cells) 114 | for i in range(1, t0.shape[0]): 115 | d = (identity + t_step*(1-theta)*alpha*M)*rho[i-1] + beta 116 | rho[i] = linalg.spsolve(A, d) 117 | # normalization not needed, flux is kept 118 | # PS/PNS 119 | ps = np.trapz(rho.T[mesh.cells < 0], 120 | x=mesh.cells[mesh.cells < 0], axis=0) 121 | t_sw = t0[np.argmax(ps > 0.5)] 122 | else: 123 | rho = np.array(rho_init) 124 | rho_next = np.array(rho_init) 125 | t_sw = 0 126 | for i in range(1, t0.shape[0]): 127 | d = (identity + t_step*(1-theta)*alpha*M)*rho + beta 128 | rho_next = linalg.spsolve(A, d) 129 | # normalization not needed, flux is kept 130 | ps = np.trapz(rho.T[mesh.cells < 0], 131 | x=mesh.cells[mesh.cells < 0], 132 | axis=0) 133 | if t_sw == 0 and ps > 0.5: 134 | t_sw = t_step*i 135 | # update variable by switching 136 | rho_next, rho = rho, rho_next 137 | 138 | # return 139 | return {'t_sw': t_sw, 140 | 'rho': rho.T, 141 | 't0': t0, 142 | 'z0': mesh.cells} 143 | 144 | 145 | def simple_test(): 146 | """FVM simple test.""" 147 | z_points = 500 148 | delta = 60 149 | i0 = 1.5 150 | h = 0. 151 | # time step 152 | t_step = 0.001 153 | T = 20 154 | 155 | data = solve_mtj_fp(dim_points=z_points, 156 | delta=delta, 157 | i0=i0, 158 | h=h, 159 | t_step=t_step, 160 | T=T) 161 | rho = data['rho'] 162 | t0 = data['t0'] 163 | z0 = data['z0'] 164 | _theta_x = np.arccos(z0) 165 | t_mesh0, z_mesh0 = np.meshgrid(t0, z0) 166 | fig = plt.figure() 167 | ax = fig.add_subplot(111, projection='3d') 168 | # z_mesh0 = np.arccos(z_mesh0) 169 | ax.plot_surface(z_mesh0, 170 | t_mesh0, 171 | rho, 172 | alpha=0.7, 173 | cmap='viridis', 174 | edgecolor='k') 175 | plt.show() 176 | 177 | print('plotting 2d') 178 | fig = plt.figure() 179 | ax_0 = fig.add_subplot(211) 180 | ax_1 = fig.add_subplot(212) 181 | 182 | fixed_list = [0, 1, 2, 4, 8, 14, T] 183 | for tt_idx, tt in enumerate(fixed_list): 184 | t0_idx = np.argmax(t0 >= tt) 185 | if tt == t0[-1]: 186 | t0_idx = -1 187 | ax_0.plot(_theta_x, rho[:, t0_idx], label=f't={tt}') 188 | ax_1.plot(z0, rho[:, t0_idx], label=f't={tt}') 189 | ax_0.legend() 190 | ax_0.set_yscale('log', base=10) 191 | ax_1.legend() 192 | ax_1.set_yscale('log', base=10) 193 | # ax.set_ylim(bottom=1e-10) 194 | ax_0.grid() 195 | ax_1.grid() 196 | plt.show() 197 | 198 | fig = plt.figure() 199 | ax = fig.add_subplot(1, 1, 1) 200 | # colors 201 | name = "tab10" 202 | cmap = cm.get_cmap(name) # type: matplotlib.colors.ListedColormap 203 | colors = cmap.colors # type: list 204 | # plot_3d_evolution(p_imp_0, t0, z0, plot_res=1e6, 205 | # title='ro implicit matmult') 206 | pt = np.trapz(rho, z0, axis=0) 207 | ps = np.trapz(y=rho[z0 < 0], x=z0[z0 < 0], axis=0)/pt 208 | pns = np.trapz(y=rho[z0 >= 0], x=z0[z0 >= 0], axis=0)/pt 209 | 210 | ax.plot(t0, 211 | ps, 212 | '--', 213 | color=colors[0], 214 | alpha=0.5, 215 | label=f'ps i: {i0}') 216 | ax.plot(t0, 217 | pns, 218 | label=f'pns i: {i0}') 219 | ax.set_yscale('log', base=10) 220 | # ax.set_ylim(bottom=1e-10) 221 | ax.legend() 222 | ax.grid() 223 | plt.show() 224 | 225 | 226 | if __name__ == '__main__': 227 | simple_test() 228 | -------------------------------------------------------------------------------- /src/python_compact_model/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | * [Quick Start & More info](#quick-start--more-info) 4 | * [Files organization](#files-organization) 5 | * [s-LLGS Solvers](#s-llgs-solvers) 6 | * [No-thermal or emulated-thermal simulations](#no-thermal-or-emulated-thermal-simulations) 7 | * [Thermal Stochastic Simulations](#thermal-stochastic-simulations) 8 | 9 | 10 | ## Quick Start & More info 11 | Full readme: [README.md](../README.md) 12 | 13 | Summary: 14 | * `test_sllgs_solver.py` shows you the basic s-LLGS solver config, calling (`sllgs_solver.py`) 15 | * `stochastic_multithread_simulation.py` (calling `sllgs_solver.py`) is the script 16 | that helps you launching parallel python s-LLGS simulations 17 | * These results can be compared against Fooker-Plank simulations (see `plot_sllgs_fpe_comparison.py` script) 18 | * `analytical.py` and `mtj_fp_fvm.py` contain the Fooker-Plank solvers. Analytical contains the WER/RER fitter for the problem optimization 19 | * Verilog-a compact models: run the testbenches `tb.scs` and `tb_subckt.scs` 20 | 21 | Please, read the full description at [MRAM Framework Description](./doc/README.md). 22 | 23 | **IMPORTANT: Before using the compact models**, read the [s-LLGS Solvers](#s-llgs-solvers) info. 24 | 25 | 26 | ## Files organization 27 | * `doc` 28 | * [README.md](./doc/README.md) for the **full MRAM framework description** 29 | * `src` 30 | * `python_compact_model` 31 | * [README.md](./python_compact_model/README.md) for the MRAM python s-LLGS description 32 | * `sllgs_solver.py` Python s-LLGS solver 33 | * `stochastic_multithread_simulation.py` Multi-thread stochastic simulations 34 | * `test_sllgs_solver.py` Simple s-LLGS tests 35 | * `ode_solver_custom_fn.py` *solve_ivp* auxilar fns derived from Scipy 36 | 37 | ## s-LLGS Solvers 38 | 39 | ### No-thermal or emulated-thermal simulations 40 | * Use Scipy solver in python (`scipy_ivp=True`) 41 | * Use Spherical coordinates 42 | * Control the simulation through tolerances (`atol, rtol` in python) 43 | 44 | ``` 45 | # No thermal, no fake_thermal, solved with scipy_ivp 46 | llg_a = sllg_solver.LLG(do_fake_thermal=False, 47 | do_thermal=False, 48 | i_amp_fn=current, 49 | seed=seed_0) 50 | data_a = llg_a.solve_and_plot(15e-9, 51 | scipy_ivp=True, 52 | solve_spherical=True, 53 | solve_normalized=True, 54 | rtol=1e-4, 55 | atol=1e-9) 56 | # No thermal, EMULATED THERMAL, solved with scipy_ivp 57 | llg_b = sllg_solver.LLG(do_fake_thermal=True, 58 | d_theta_fake_th=1/30, 59 | do_thermal=False, 60 | i_amp_fn=current, 61 | seed=seed_0) 62 | data_b = llg_b.solve_and_plot(15e-9, 63 | scipy_ivp=True, 64 | solve_spherical=True, 65 | solve_normalized=True, 66 | max_step=1e-11, 67 | rtol=1e-4, 68 | atol=1e-9) 69 | ``` 70 | ### Thermal Stochastic Simulations 71 | Require stochastic differential equation solvers 72 | * Use SDE solvers in python (`scipy_ivp=False`) 73 | * Use Cartesian coordinates 74 | * Control the simulation through maximum time step (`max_step` in python) 75 | ``` 76 | llg_c = sllg_solver.LLG(do_fake_thermal=False, 77 | do_thermal=True, 78 | i_amp_fn=current, 79 | seed=seed_0) 80 | data_c = llg_c.solve_and_plot(10e-9, 81 | scipy_ivp=False, 82 | solve_spherical=False, 83 | solve_normalized=True, 84 | max_step=1e-13, 85 | method='stratonovich_heun') 86 | ``` 87 | ![MRAM Magnetization and stochasticity](./doc/fig4_movie.gif) 88 | -------------------------------------------------------------------------------- /src/python_compact_model/ivp_lib/ode_solver_custom_fn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | # 4 | # Copyright (c) 2020-2021 Scipy. 5 | # All rights reserved. 6 | # 7 | # SPDX-License-Identifier: BSD-3-Clause 8 | # 9 | # The following fns are modifications from Scipy 10 | # rk.py and base.py files Latest commit a534a56 on Aug 18, 2020 11 | 12 | """ 13 | Auxiliar functions for solve_ivp problem. 14 | 15 | Required to pass dt to the integration function. 16 | """ 17 | 18 | import numpy as np 19 | 20 | 21 | def ode_solver_init_custom(self, fun, t0, y0, t_bound, vectorized, 22 | support_complex=False): 23 | """Replace for OdeSolver __init__ passing h to the wrappers.""" 24 | self.t_old = None 25 | self.t = t0 26 | self._fun, self.y = check_arguments_custom(fun, y0, support_complex) 27 | self.t_bound = t_bound 28 | self.vectorized = vectorized 29 | 30 | if vectorized: 31 | def fun_single(t, y, h): 32 | return self._fun(t, y[:, None], h).ravel() 33 | fun_vectorized = self._fun 34 | else: 35 | fun_single = self._fun 36 | 37 | def fun_vectorized(t, y, h): 38 | f = np.empty_like(y) 39 | for i, yi in enumerate(y.T): 40 | f[:, i] = self._fun(t, yi, h) 41 | return f 42 | 43 | def fun(t, y, h=1e-10): 44 | self.nfev += 1 45 | return self.fun_single(t, y, h) 46 | 47 | self.fun = fun 48 | self.fun_single = fun_single 49 | self.fun_vectorized = fun_vectorized 50 | 51 | self.direction = np.sign(t_bound - t0) if t_bound != t0 else 1 52 | self.n = self.y.size 53 | self.status = 'running' 54 | 55 | self.nfev = 0 56 | self.njev = 0 57 | self.nlu = 0 58 | 59 | 60 | def check_arguments_custom(fun, y0, support_complex): 61 | """Helper function for checking arguments common to all solvers.""" 62 | y0 = np.asarray(y0) 63 | if np.issubdtype(y0.dtype, np.complexfloating): 64 | if not support_complex: 65 | raise ValueError("`y0` is complex, but the chosen solver does " 66 | "not support integration in a complex domain.") 67 | dtype = complex 68 | else: 69 | dtype = float 70 | y0 = y0.astype(dtype, copy=False) 71 | 72 | if y0.ndim != 1: 73 | raise ValueError("`y0` must be 1-dimensional.") 74 | 75 | def fun_wrapped(t, y, dt=1e-10): 76 | return np.asarray(fun(t, y, dt), dtype=dtype) 77 | 78 | return fun_wrapped, y0 79 | 80 | 81 | def rk_step_custom(fun, t, y, f, h, A, B, C, K): 82 | """Perform a single Runge-Kutta step. 83 | 84 | This function computes a prediction of an explicit Runge-Kutta method and 85 | also estimates the error of a less accurate method. 86 | 87 | Notation for Butcher tableau is as in [1]_. 88 | 89 | Parameters 90 | ---------- 91 | fun : callable 92 | Right-hand side of the system. 93 | t : float 94 | Current time. 95 | y : ndarray, shape (n,) 96 | Current state. 97 | f : ndarray, shape (n,) 98 | Current value of the derivative, i.e., ``fun(x, y)``. 99 | h : float 100 | Step to use. 101 | A : ndarray, shape (n_stages, n_stages) 102 | Coefficients for combining previous RK stages to compute the next 103 | stage. For explicit methods the coefficients at and above the main 104 | diagonal are zeros. 105 | B : ndarray, shape (n_stages,) 106 | Coefficients for combining RK stages for computing the final 107 | prediction. 108 | C : ndarray, shape (n_stages,) 109 | Coefficients for incrementing time for consecutive RK stages. 110 | The value for the first stage is always zero. 111 | K : ndarray, shape (n_stages + 1, n) 112 | Storage array for putting RK stages here. Stages are stored in rows. 113 | The last row is a linear combination of the previous rows with 114 | coefficients 115 | 116 | Returns 117 | ------- 118 | y_new : ndarray, shape (n,) 119 | Solution at t + h computed with a higher accuracy. 120 | f_new : ndarray, shape (n,) 121 | Derivative ``fun(t + h, y_new)``. 122 | 123 | References 124 | ---------- 125 | .. [1] E. Hairer, S. P. Norsett G. Wanner, "Solving Ordinary Differential 126 | Equations I: Nonstiff Problems", Sec. II.4. 127 | """ 128 | # print(f'fn: {fun}') 129 | 130 | K[0] = f 131 | for s, (a, c) in enumerate(zip(A[1:], C[1:]), start=1): 132 | dy = np.dot(K[:s].T, a[:s]) * h 133 | K[s] = fun(t + c * h, y + dy, c * h) 134 | 135 | y_new = y + h * np.dot(K[:-1].T, B) 136 | f_new = fun(t + h, y_new, h) 137 | 138 | K[-1] = f_new 139 | 140 | return y_new, f_new 141 | 142 | 143 | def rk_step_custom_circular(fun, t, y, f, h, A, B, C, K): 144 | """Perform a single Runge-Kutta step. 145 | 146 | This function computes a prediction of an explicit Runge-Kutta method and 147 | also estimates the error of a less accurate method. 148 | 149 | Notation for Butcher tableau is as in [1]_. 150 | 151 | Parameters 152 | ---------- 153 | fun : callable 154 | Right-hand side of the system. 155 | t : float 156 | Current time. 157 | y : ndarray, shape (n,) 158 | Current state. 159 | f : ndarray, shape (n,) 160 | Current value of the derivative, i.e., ``fun(x, y)``. 161 | h : float 162 | Step to use. 163 | A : ndarray, shape (n_stages, n_stages) 164 | Coefficients for combining previous RK stages to compute the next 165 | stage. For explicit methods the coefficients at and above the main 166 | diagonal are zeros. 167 | B : ndarray, shape (n_stages,) 168 | Coefficients for combining RK stages for computing the final 169 | prediction. 170 | C : ndarray, shape (n_stages,) 171 | Coefficients for incrementing time for consecutive RK stages. 172 | The value for the first stage is always zero. 173 | K : ndarray, shape (n_stages + 1, n) 174 | Storage array for putting RK stages here. Stages are stored in rows. 175 | The last row is a linear combination of the previous rows with 176 | coefficients 177 | 178 | Returns 179 | ------- 180 | y_new : ndarray, shape (n,) 181 | Solution at t + h computed with a higher accuracy. 182 | f_new : ndarray, shape (n,) 183 | Derivative ``fun(t + h, y_new)``. 184 | 185 | References 186 | ---------- 187 | .. [1] E. Hairer, S. P. Norsett G. Wanner, "Solving Ordinary Differential 188 | Equations I: Nonstiff Problems", Sec. II.4. 189 | """ 190 | # print(f'fn: {fun}') 191 | 192 | def _circular_theta(m): 193 | # circular operator on theta 194 | if m[1] > np.pi: 195 | m[1] = m[1] - np.pi 196 | elif m[1] < 0: 197 | m[1] = - m[1] 198 | return m 199 | 200 | K[0] = f 201 | # circular operator on theta 202 | K[0] = _circular_theta(K[0]) 203 | for s, (a, c) in enumerate(zip(A[1:], C[1:]), start=1): 204 | dy = np.dot(K[:s].T, a[:s]) * h 205 | K[s] = fun(t + c * h, y + dy, c * h) 206 | # circular operator on theta 207 | K[s] = _circular_theta(K[s]) 208 | 209 | y_new = y + h * np.dot(K[:-1].T, B) 210 | f_new = fun(t + h, y_new, h) 211 | # circular operator on theta 212 | f_new = _circular_theta(f_new) 213 | 214 | K[-1] = f_new 215 | 216 | return y_new, f_new 217 | -------------------------------------------------------------------------------- /src/python_compact_model/stochastic_multithread_simulation.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | # 4 | # Copyright (c) 2020-2021 Arm Ltd. 5 | # All rights reserved. 6 | # 7 | # SPDX-License-Identifier: BSD-3-Clause 8 | 9 | """ 10 | Multi-threading capabilities example. 11 | 12 | Generation of 'total_th_sims' random walks for later comparison against FP. 13 | """ 14 | 15 | import numpy as np 16 | import argparse 17 | import matplotlib.pyplot as plt 18 | import multiprocessing 19 | 20 | import sllgs_solver as sllg_solver 21 | 22 | 23 | ################################# 24 | ################################# 25 | # Parameters not passed to the worker, 26 | # change accordingly to your needs 27 | cores = 32 28 | total_th_sims = 10000 29 | 30 | c_KB = 1.3806503e-23 # boltzmann's constant [J/K] 31 | diam = 50e-9 32 | t_fl = 1.e-9 33 | temperature = 300 34 | k_i_0 = 1.0e-3 35 | ms = 1.2e6 # magnetisation saturation (A/m) 36 | H_ext = (0., 0., 0.) # low external magnetic field (A/m) (test anisotropy) 37 | # stt params 38 | eps_prime = 0.0 # [FITTING] constant 39 | p = 0.75 40 | lambda_s = 1. 41 | z0 = 1 42 | initial_m = None 43 | 44 | # just for export 45 | t_res = 1e-11 46 | ################################# 47 | ################################# 48 | 49 | 50 | def str2bool(b_string): 51 | """Convert string to bool.""" 52 | b_string = b_string.strip() 53 | if b_string.lower() in ('yes', 'true', 't', 'y', '1'): 54 | return True 55 | elif b_string.lower() in ('no', 'false', 'f', 'n', '0'): 56 | return False 57 | # error 58 | print('[ERROR] Boolean value expected.') 59 | return 'a' 60 | 61 | 62 | def worker_stt_simplest(idx, 63 | solve_normalized, 64 | method, 65 | I0, 66 | t_delay, 67 | t_pulse, 68 | t_final, 69 | H_ext, 70 | initial_m, 71 | w_fl, 72 | l_fl, 73 | t_fl, 74 | ms, 75 | alpha, 76 | k_i_0, 77 | temperature, 78 | p, 79 | lambda_s, 80 | eps_prime, 81 | do_thermal, 82 | do_theta_windowing, 83 | do_fake_thermal, 84 | theta_windowing_factor, 85 | d_theta_fake_th, 86 | max_step): 87 | """Parallel worker fn. A simple wrapper.""" 88 | # current 89 | I0 = -np.sign(z0)*I0 90 | 91 | def test_h_ext(t): 92 | """H_ext fn.""" 93 | return np.array(H_ext) 94 | 95 | def test_current(t): 96 | """Current fn.""" 97 | if type(t) is np.ndarray: 98 | c = np.zeros(t.shape) + I0 99 | c[t < t_delay] = 0 100 | c[t > t_delay+t_pulse] = -I0 101 | return c 102 | if t < t_delay: 103 | return 0 104 | elif t > t_delay+t_pulse: 105 | return 0 106 | return I0 107 | 108 | print(f'[info][parallel_solver] doing s: {idx} I0 {I0} alpha {alpha} ' 109 | f'max_step {max_step} tf {t_final}') 110 | llg_b = sllg_solver.LLG( 111 | w_fl=diam, 112 | l_fl=diam, 113 | t_fl=t_fl, 114 | ms=ms, 115 | alpha=alpha, 116 | k_i_0=k_i_0, 117 | temperature=temperature, 118 | stt_mode='stt_oommf_simple', 119 | p=p, 120 | lambda_s=lambda_s, 121 | eps_prime=eps_prime, 122 | # not include temperature noise, 123 | # and do not force its effects on the theta_0 124 | do_thermal=do_thermal, 125 | do_theta_windowing=do_theta_windowing, 126 | do_fake_thermal=do_fake_thermal, 127 | theta_windowing_factor=theta_windowing_factor, 128 | d_theta_fake_th=d_theta_fake_th, 129 | m_init=initial_m, 130 | theta_pl=0.0, # [rad] pinned layer theta 131 | phi_pl=0.0, # [rad] pinned layer phi 132 | h_ext_cart=test_h_ext, 133 | i_amp_fn=test_current, 134 | seed=idx+2000) 135 | llg_sol = llg_b.solve_and_plot(final_t=t_final, 136 | max_step=max_step, 137 | solve_spherical=False, 138 | solve_normalized=solve_normalized, 139 | scipy_ivp=False, 140 | method=method, 141 | plot=False, 142 | plot_simple=False) 143 | print(f'[info][parallel_solver] done {idx} I0 {I0}' 144 | f' alpha: {alpha} t_final: {t_final}') 145 | 146 | # do interpolation in the thread 147 | t_i = np.linspace(0, t_final, int(t_final/t_res)) 148 | theta_i = np.interp(t_i, llg_sol.t, llg_sol.y[1]) 149 | 150 | # do interpolation in the thread 151 | return theta_i 152 | 153 | 154 | def run_walks( 155 | suffix, 156 | solve_normalized, 157 | method, 158 | I0, 159 | alpha, 160 | max_step, 161 | t_delay, 162 | t_pulse=60e-9, 163 | total_th_sims=10000, 164 | cores=32, 165 | plot_walks=True, 166 | ): 167 | """Run walks.""" 168 | t_final = 2*t_delay + t_pulse 169 | 170 | # pool 171 | pool = multiprocessing.Pool(cores) 172 | results_a = [pool.apply_async( 173 | worker_stt_simplest, 174 | args=( 175 | idx, 176 | solve_normalized, 177 | method, 178 | I0, 179 | t_delay, 180 | t_pulse, 181 | t_final, 182 | H_ext, 183 | initial_m, 184 | diam, # w_fl, 185 | diam, # l_fl, 186 | t_fl, 187 | ms, 188 | alpha, 189 | k_i_0, 190 | temperature, 191 | p, 192 | lambda_s, 193 | eps_prime, 194 | True, # do_thermal, 195 | False, # do_theta_windowing, 196 | False, # do_fake_thermal, 197 | 0., # theta_windowing_factor, 198 | 1/13, # d_theta_fake_th 199 | max_step, 200 | )) for idx in range(total_th_sims)] 201 | print('[info][parallel_solver] all threads launched') 202 | 203 | theta_i_list = [r.get() for r in results_a] 204 | t_i = np.linspace(0, t_final, int(t_final/t_res)) 205 | print('[info][parallel_solver] all threads computed') 206 | 207 | np.savetxt( 208 | f'{suffix}_{t_delay}_del_{I0}A_interp_{total_th_sims}_sims.csv', 209 | np.array(theta_i_list)) 210 | np.savetxt( 211 | f'{suffix}_{t_delay}_del_{I0}A_time_interp_' 212 | f'{total_th_sims}_sims.csv', 213 | t_i) 214 | 215 | if not plot_walks: 216 | return 217 | 218 | print('[info][parallel_solver] plotting') 219 | fig, ax = plt.subplots(1, 1, figsize=(6, 2.3)) 220 | 221 | for i in np.arange(total_th_sims): 222 | ax.plot(1e9*t_i, np.cos(theta_i_list[i]), 'gray', alpha=0.5) 223 | 224 | ax.plot(np.nan, 225 | color='gray', alpha=0.5, label='Stochastic H_th') 226 | ax.set_yticks([-1, -0.5, 0, 0.5, 1]) 227 | ax.set_ylabel('m_z') 228 | ax.set_xlabel('time [ns]') 229 | ax.legend(loc='upper right') 230 | ax.grid() 231 | ax.set_title(suffix) 232 | plt.show() 233 | plt.savefig(f'{suffix}_{t_delay}_del_{I0}A_time_interp.png') 234 | 235 | 236 | if __name__ == '__main__': 237 | parser = argparse.ArgumentParser() 238 | 239 | parser.add_argument( 240 | '--solve-normalized', 241 | type=str2bool, 242 | default=False, 243 | help='use normalized solver') 244 | parser.add_argument( 245 | '--method', 246 | type=str, 247 | default='heun', 248 | help='sde solver') 249 | parser.add_argument( 250 | '--suffix', 251 | type=str, 252 | default='rand_walk', 253 | help='suffix') 254 | parser.add_argument( 255 | '--max-step', 256 | type=float, 257 | default=1e-13, 258 | help='max step [s]') 259 | parser.add_argument( 260 | '--I0', 261 | type=float, 262 | default=40e-6, 263 | help='wr current') 264 | parser.add_argument( 265 | '--alpha', 266 | type=float, 267 | default=0.01, 268 | help='damping') 269 | parser.add_argument( 270 | '--t-delay', 271 | type=float, 272 | default=3e-9, 273 | help='delay time') 274 | parser.add_argument( 275 | '--t-pulse', 276 | type=float, 277 | default=60e-9, 278 | help='sim time') 279 | parser.add_argument( 280 | '--total-th-sims', 281 | type=int, 282 | default=int(1e2), 283 | help='sims') 284 | parser.add_argument( 285 | '--cores', 286 | type=int, 287 | default=int(32), 288 | help='cores') 289 | parser.add_argument( 290 | '--plot-walks', 291 | type=str2bool, 292 | default=False, 293 | help='Plot distribution') 294 | 295 | args = parser.parse_args() 296 | 297 | # # parse and run 298 | # FLAGS, unparsed = parser.parse_known_args() 299 | print('\t[experiment] reproduce results with:') 300 | for k in args.__dict__: 301 | print('\t\t', k, ': ', args.__dict__[k]) 302 | 303 | run_walks( 304 | solve_normalized=args.solve_normalized, 305 | method=args.method, 306 | suffix=args.suffix, 307 | max_step=args.max_step, 308 | I0=args.I0, 309 | alpha=args.alpha, 310 | t_delay=args.t_delay, 311 | t_pulse=args.t_pulse, 312 | total_th_sims=args.total_th_sims, 313 | cores=args.cores, 314 | plot_walks=args.plot_walks 315 | ) 316 | 317 | print('\t[experiment] reproduce results with:') 318 | for k in args.__dict__: 319 | print('\t\t', k, ': ', args.__dict__[k]) 320 | -------------------------------------------------------------------------------- /src/python_compact_model/test_sllgs_solver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | # 4 | # Copyright (c) 2020-2021 Arm Ltd. 5 | # All rights reserved. 6 | # 7 | # SPDX-License-Identifier: BSD-3-Clause 8 | 9 | """s-LLGS basic examples""" 10 | 11 | import sllgs_solver as sllg_solver 12 | 13 | 14 | def current(t): 15 | """Current waveform.""" 16 | return -100e-6 17 | 18 | 19 | def do_test(seed_0=100): 20 | """Simple s-LLGS tests.""" 21 | sllg_solver.eq_info() 22 | # No thermal, no fake_thermal, solved with scipy_ivp 23 | llg_a = sllg_solver.LLG(do_fake_thermal=False, 24 | do_thermal=False, 25 | i_amp_fn=current, 26 | seed=seed_0) 27 | data_a = llg_a.solve_and_plot(15e-9, 28 | scipy_ivp=True, 29 | solve_spherical=True, 30 | solve_normalized=True, 31 | max_step=1e-11, 32 | rtol=1e-4, 33 | atol=1e-9) 34 | 35 | # No thermal, fake_thermal, solved with scipy_ivp 36 | llg_b = sllg_solver.LLG(do_fake_thermal=True, 37 | d_theta_fake_th=1/30, 38 | do_thermal=False, 39 | i_amp_fn=current, 40 | seed=seed_0) 41 | data_b = llg_b.solve_and_plot(15e-9, 42 | scipy_ivp=True, 43 | solve_spherical=True, 44 | solve_normalized=True, 45 | max_step=1e-11, 46 | rtol=1e-4, 47 | atol=1e-9) 48 | 49 | llg_c = sllg_solver.LLG(do_fake_thermal=False, 50 | do_thermal=True, 51 | i_amp_fn=current, 52 | seed=seed_0) 53 | data_c = llg_c.solve_and_plot(10e-9, 54 | scipy_ivp=False, 55 | solve_spherical=False, 56 | solve_normalized=True, 57 | max_step=1e-13, 58 | method='stratonovich_heun') 59 | print(f'data ready to inspect: {data_a}, {data_b}, {data_c}') 60 | 61 | 62 | do_test() 63 | -------------------------------------------------------------------------------- /src/sllgs_fpe_comparison/plot_sllgs_fpe_comparison.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | # 4 | # Copyright (c) 2020-2021 Arm Ltd. 5 | # All rights reserved. 6 | # 7 | # SPDX-License-Identifier: BSD-3-Clause 8 | 9 | """Plot s-LLGS/FPE evolution. 10 | 11 | Please, setup all the required parameters. 12 | Inputs are the files generated by stochastic_multithread_simulation.py 13 | """ 14 | 15 | import numpy as np 16 | import matplotlib.pyplot as plt 17 | import itertools 18 | import matplotlib.colors as mcolors 19 | 20 | import fooker_plank.analytical as analytical 21 | import python_compact_model.sllgs_solver as sllgs_solver 22 | import sllgs_importer as sllgs_i 23 | 24 | ###################################################################### 25 | # PARAMS for building llgs object 26 | # these should match the ones you used for the s-LLGS simulation 27 | ###################################################################### 28 | # Define a macrospin mesh (i.e. one discretisation cell). 29 | z0 = 1 30 | initial_m = None # np.array([0.01, 0.01, z0]) # vector in x direction 31 | diam = 50e-9 32 | t_fl = 1.e-9 33 | temperature = 300 34 | k_i_0 = 1.0e-3 35 | alpha = 0.01 # Gilbert damping 36 | Ms = 1.2e6 # magnetisation saturation (A/m) 37 | # Zeeman (external field) validation 38 | Hext = (0., 0., 0.) # low external magnetic field (A/m) (test anisotropy) 39 | # stt params 40 | eps_prime = 0.0 # [FITTING] constant 41 | P = 0.75 42 | Lambda = 1. 43 | 44 | ################################### 45 | sllgs_files = [ 46 | 'path_to_csv_file_exported_by_stochastic_multithread_simulation.py', 47 | ] 48 | sllgs_time_files = [ 49 | 'path_to_time_csv_file_exported_by_stochastic_multithread_simulation.py', 50 | ] 51 | I0_uA = [50]*len(sllgs_files) 52 | t_delays_ns = [5]*len(sllgs_files) 53 | 54 | ps_sslgs = [None] * len(t_delays_ns) 55 | time_sslgs = [None] * len(t_delays_ns) 56 | ps_fp = [None] * len(t_delays_ns) 57 | time_fp = [None] * len(t_delays_ns) 58 | compute_analytical_manual = False 59 | plot_intermediate = False 60 | colors = itertools.cycle(mcolors.TABLEAU_COLORS) 61 | 62 | 63 | def test_h_ext(t): 64 | """Set H_ext.""" 65 | return np.array(Hext) 66 | 67 | 68 | def test_current(t): 69 | """Test current.""" 70 | return 0. 71 | 72 | 73 | # get Ic, tau_d etc 74 | llg_o = sllgs_solver.LLG(w_fl=diam, l_fl=diam, t_fl=t_fl, 75 | ms=Ms, 76 | alpha=alpha, 77 | k_i_0=k_i_0, 78 | # thermal_stability_0=thermal_stability_0, 79 | temperature=temperature, 80 | stt_mode='stt_oommf_simple', 81 | p=P, 82 | lambda_s=Lambda, 83 | eps_prime=eps_prime, 84 | # not include temperature noise, 85 | # and do not force its effects on the theta_0 86 | do_thermal=False, 87 | do_fake_thermal=False, 88 | do_theta_windowing=False, 89 | m_init=initial_m, 90 | # shape_ani_demag_mode=0, 91 | # theta_init=0.09, 92 | theta_pl=0.0, # [rad] pinned layer theta 93 | phi_pl=0.0, # [rad] pinned layer phi 94 | h_ext_cart=test_h_ext, 95 | i_amp_fn=test_current) 96 | 97 | print(f'P: {P}, P/2: {P/2}, eps: {llg_o.get_epsilon_stt(1)}') 98 | 99 | ############################################################################## 100 | # importing raw data 101 | # ~/SharePoint/ProjectEnergyScalableMRAM/docs/essderc_2021/raw_data 102 | ############################################################################## 103 | 104 | 105 | def analyze_s_llgs_tolerances(data_file, time_file, t_delay, I0_uA, 106 | compute_analytical_manual=False, 107 | plot_intermediate=True, 108 | dim_points=1000): 109 | """Analyze s-LLGS.""" 110 | # get all data, note it is not delayed anymore 111 | sllgs_time, sllgs_data, sllgs_sw_idx, sllgs_sw_ps = sllgs_i.process_sllgs_data( 112 | data_file, time_file, t_delay, dim_points) 113 | theta_axis = np.linspace(np.pi, 0, dim_points) 114 | 115 | # debug sllgs 116 | # return { 117 | # 'sllgs_time': sllgs_time, 118 | # 'sllgs_theta': theta_axis, 119 | # 'sllgs_data': sllgs_data, 120 | # 'sllgs_ps': sllgs_sw_ps 121 | # } 122 | ######################################################################## 123 | # Initializations 124 | I0 = np.sign(z0)*I0_uA*1e-6 125 | 126 | lin_space_z = False 127 | L0_max = 200 128 | Nmax = L0_max 129 | 130 | # maxwell_loc = -0.016632336229383336 131 | # maxwell_scale = 0.041628860303107654 132 | maxwell_loc = None 133 | maxwell_scale = None 134 | 135 | # i, h, delta 136 | # llg_o.ic = llg_o.ic * 1.1 137 | # llg_o.tau_d = llg_o.tau_d * 1.1 138 | print(f'Ic from: {llg_o._get_Ic()}') 139 | ic = np.abs(llg_o._get_Ic()) 140 | # ic = 4*llg.c_E * llg_o.alpha * llg_o.thermal_stability * \ 141 | # llg.c_KB * temperature / (llg.c_hbar * llg_o.get_epsilon_stt(1)*2) 142 | print(f'Ic from compact modeling: {ic}') 143 | print(f'epsilon: {llg_o.get_epsilon_stt(1)}') 144 | i = I0/ic 145 | print(f'[debug] i: {i}') 146 | h = 0 147 | delta = llg_o.thermal_stability 148 | 149 | # FP Data 150 | xlim = sllgs_time[-1] 151 | tau = xlim/np.abs(llg_o._get_tau_d()) 152 | tau_step = 0.2e-9/np.abs(llg_o._get_tau_d()) 153 | t_pulse = 60e-9 154 | print(f't_final: {xlim}') 155 | print(f'tau: {tau} i: {i}') 156 | lin_space_z = False 157 | rho_0_at_pi = z0 < 0 158 | 159 | s_rho_0_fit = np.polynomial.legendre.legfit( 160 | x=np.cos(theta_axis), 161 | # sllgs is normalized to 1 already 162 | y=sllgs_data[:, 0][::-1], 163 | deg=L0_max) 164 | _, rho_0_a, s_rho_0_a = analytical.get_state_rho_0( 165 | delta=delta, 166 | do_maxwell=True, 167 | maxwell_loc=maxwell_loc, 168 | maxwell_scale=maxwell_scale, 169 | L0=Nmax, 170 | lin_space_z=lin_space_z, 171 | rho_0_at_pi=rho_0_at_pi) 172 | _, _, s_rho_0_b = analytical.get_state_rho_0(delta=delta, 173 | do_maxwell=False, 174 | L0=Nmax, 175 | dim_points=dim_points, 176 | lin_space_z=lin_space_z, 177 | rho_0_at_pi=rho_0_at_pi) 178 | 179 | # fergar01 180 | # s_rho_0_fit = s_rho_0_b 181 | 182 | fp_theta, ddata_fit = analytical.untangle_state(s_rho_0_fit, 183 | dim_points=dim_points, 184 | lin_space_z=lin_space_z) 185 | fp_theta, ddata_a = analytical.untangle_state(s_rho_0_a, 186 | lin_space_z=lin_space_z, 187 | dim_points=dim_points) 188 | fp_theta, ddata_b = analytical.untangle_state(s_rho_0_b, 189 | lin_space_z=lin_space_z, 190 | dim_points=dim_points) 191 | 192 | fig, axs = plt.subplots(1, 1, 193 | figsize=(8, 6)) 194 | axs.plot(theta_axis, 195 | sllgs_data[:, 0][::-1], 196 | '+-', 197 | label='sllgs') 198 | axs.plot(fp_theta, 199 | ddata_fit, 200 | label='fit') 201 | axs.plot(fp_theta, 202 | ddata_a, 203 | '--', 204 | label='A') 205 | axs.plot(fp_theta, 206 | ddata_b, 207 | '--', 208 | label='B') 209 | axs.grid() 210 | axs.legend() 211 | if plot_intermediate: 212 | plt.show() 213 | else: 214 | plt.savefig(f'fit_{data_file.split("/")[-1]}.png') 215 | 216 | # use fitted 217 | s_rho_0_a = s_rho_0_fit 218 | # use default 219 | # s_rho_0_a = s_rho_0_a 220 | 221 | sn = analytical.get_sn(Nmax) 222 | fp_data_a = analytical.get_sw_continuous_prob( 223 | s_rho_0=s_rho_0_a, 224 | tau=tau, 225 | delta=delta, 226 | i=i, 227 | h=h, 228 | rho_0_at_pi=rho_0_at_pi, 229 | compute_fvm=False, 230 | compute_analytical_sw=True, 231 | compute_analytical_manual=compute_analytical_manual, 232 | dim_points=dim_points, 233 | lin_space_z=lin_space_z, 234 | t_step=tau_step, 235 | sn=sn) 236 | fp_data_b = analytical.get_sw_continuous_prob( 237 | s_rho_0=s_rho_0_b, 238 | tau=tau, 239 | delta=delta, 240 | i=i, 241 | h=h, 242 | rho_0_at_pi=rho_0_at_pi, 243 | compute_fvm=False, 244 | compute_analytical_sw=True, 245 | compute_analytical_manual=compute_analytical_manual, 246 | dim_points=dim_points, 247 | lin_space_z=lin_space_z, 248 | t_step=tau_step, 249 | sn=sn) 250 | fp_time = fp_data_a['time']*np.abs(llg_o._get_tau_d()) 251 | if compute_analytical_manual: 252 | fp_a_ps_manual = fp_data_a['manual_prob'] 253 | # fp_b_ps_manual = fp_data_b['manual_prob'] 254 | else: 255 | fp_a_ps_manual = None 256 | # fp_a_psmanual = None 257 | fp_a_ps = fp_data_a['analytical_prob'] 258 | fp_b_ps = fp_data_b['analytical_prob'] 259 | fp_data_a = fp_data_a['pdf'].T 260 | fp_data_b = fp_data_b['pdf'].T 261 | fp_a_switching_idx = np.min([len(fp_a_ps)-1, 262 | np.searchsorted(fp_a_ps, 0.5)]) 263 | fp_b_switching_idx = np.min([len(fp_b_ps)-1, 264 | np.searchsorted(fp_b_ps, 0.5)]) 265 | print(f'fp_a_switching_idx{fp_a_switching_idx}') 266 | print(f'fp_b_switching_idx{fp_b_switching_idx}') 267 | print(f'fp_time: {fp_time.shape}') 268 | print(f'fp_theta: {fp_theta.shape}') 269 | print(f'fp_data_a: {fp_data_a.shape}') 270 | 271 | ########################################################## 272 | # plot 273 | ########################################################## 274 | if False and plot_intermediate: 275 | fig, axs = plt.subplots(3, 1, 276 | sharex=True, 277 | figsize=(8, 6)) 278 | cmap = plt.cm.cividis 279 | # cmap = plt.cm.PuBu_r 280 | 281 | # s-LLGS 282 | v_min = np.max([1e-8, np.min(sllgs_data)]) 283 | v_max = np.max(sllgs_data) 284 | 285 | sllgs_data[sllgs_data < v_min] = v_min 286 | fp_data_a[fp_data_a < v_min] = v_min 287 | fp_data_b[fp_data_b < v_min] = v_min 288 | 289 | print(f'ranges: {v_min}, {v_max}') 290 | ax0 = axs[0] 291 | ax0.pcolormesh(1e9*sllgs_time, 292 | np.pi-fp_theta, 293 | sllgs_data, 294 | # vmin=v_min, 295 | norm=mcolors.LogNorm(vmin=v_min, 296 | vmax=v_max), 297 | cmap=cmap, 298 | # shading='auto') 299 | shading='gouraud') 300 | ax0.set_ylabel(r'$10^5$ s-LLGS sims' 301 | r'$\theta$ [rad]') 302 | ax00 = ax0.twinx() 303 | ax00.plot([1e9*sllgs_time[sllgs_sw_idx], 304 | 1e9*sllgs_time[sllgs_sw_idx]], 305 | [0, 1], 306 | '--', color='tomato') 307 | ax00.plot([1e9*(t_pulse), 308 | 1e9*(t_pulse)], 309 | [0, 1], 310 | '--', color='tomato') 311 | # axx.plot([0, 1e9*time_tols[-1]], 312 | # [0.5, 0.5], 313 | # '--', color='tomato') 314 | ax00.plot(1e9*sllgs_time, sllgs_sw_ps, 315 | '--', color='whitesmoke') 316 | # ax11.grid() 317 | ax00.set_ylabel(r'Switching probability') 318 | ax00.annotate(f'sw @ {1e9*sllgs_time[sllgs_sw_idx]:.2f} ns', 319 | xy=(1e9*sllgs_time[sllgs_sw_idx], 0.5), 320 | xytext=(1e9*sllgs_time[sllgs_sw_idx]+1, 0.5), 321 | color='whitesmoke' 322 | ) 323 | ax00.annotate(f'write ends @ {1e9*(t_pulse):.2f} ns', 324 | xy=(1e9*(t_pulse), 0.5), 325 | xytext=(1e9*(t_pulse) + 1, 0.5), 326 | color='whitesmoke' 327 | ) 328 | 329 | ax2 = axs[1] 330 | ax3 = axs[2] 331 | 332 | # FP a 333 | ax2.pcolormesh(1e9*fp_time, fp_theta, fp_data_b, 334 | norm=mcolors.LogNorm(vmin=v_min, 335 | vmax=v_max), 336 | cmap=cmap, 337 | shading='auto') # , vmax=v_max) 338 | # ax2.set_title('FPE simulations (Maxwell Prob)') 339 | ax2.set_ylabel( 340 | 'FPE simulation\n(Maxwell Prob)\n' 341 | r'$\theta$ [rad]' 342 | ) 343 | ax2.plot([1e9*fp_time[fp_a_switching_idx], 344 | 1e9*fp_time[fp_a_switching_idx]], 345 | [0, np.pi], 346 | '--', color='tomato') 347 | ax22 = ax2.twinx() 348 | ax22.plot(1e9*fp_time, fp_a_ps, 349 | '--', color='whitesmoke') 350 | ax22.set_ylabel(r'Switching probability') 351 | ax22.plot([1e9*(t_pulse), 352 | 1e9*(t_pulse)], 353 | [0, 1], 354 | '--', color='tomato') 355 | 356 | # FP b 357 | ax3.pcolormesh(1e9*fp_time, fp_theta, fp_data_b, 358 | norm=mcolors.LogNorm(vmin=v_min, 359 | vmax=v_max), 360 | cmap=cmap, 361 | shading='auto') # , vmax=v_max) 362 | # ax4.set_title('FPE simulations (Literature)') 363 | ax3.set_ylabel( 364 | 'FPE simulation\n(Literature Prob)\n' 365 | r'$\theta$ [rad]' 366 | ) 367 | ax3.set_xlabel(r'time [ns]') 368 | ax3.plot([1e9*fp_time[fp_b_switching_idx], 369 | 1e9*fp_time[fp_b_switching_idx]], 370 | [0, np.pi], 371 | '--', color='tomato') 372 | ax22 = ax3.twinx() 373 | ax22.plot(1e9*fp_time, fp_b_ps, 374 | '--', color='whitesmoke') 375 | ax22.plot([1e9*(t_pulse), 376 | 1e9*(t_pulse)], 377 | [0, 1], 378 | '--', color='tomato') 379 | ax22.set_ylabel(r'Switching probability') 380 | 381 | # ax1.set_xlim([0., 1e9*xlim]) 382 | # ax2.set_xlim([0., 1e9*xlim]) 383 | # ax3.set_xlim([0., 1e9*xlim]) 384 | # ax4.set_xlim([0., 1e9*xlim]) 385 | 386 | ax22.annotate(f'write ends @ {1e9*(t_pulse):.2f} ns', 387 | xy=(1e9*(t_pulse), 0.5), 388 | xytext=(1e9*(t_pulse) + 1, 0.5), 389 | color='whitesmoke' 390 | ) 391 | ax22.annotate(f'write ends @ {1e9*(t_pulse):.2f} ns', 392 | xy=(1e9*(t_pulse), 0.5), 393 | xytext=(1e9*(t_pulse) + 1, 0.5), 394 | color='whitesmoke' 395 | ) 396 | 397 | ax2.annotate(f'sw @ {1e9*fp_time[fp_a_switching_idx]:.2f} ns', 398 | xy=(1e9*fp_time[fp_a_switching_idx], 0.5), 399 | xytext=(1e9*fp_time[fp_a_switching_idx]+1, 0.5), 400 | color='whitesmoke' 401 | ) 402 | ax3.annotate(f'sw @ {1e9*fp_time[fp_b_switching_idx]:.2f} ns', 403 | xy=(1e9*fp_time[fp_b_switching_idx], 0.5), 404 | xytext=(1e9*fp_time[fp_b_switching_idx]+1, 0.5), 405 | color='whitesmoke' 406 | ) 407 | for ax in axs: 408 | ax.set_yticks([0, np.pi/4, np.pi/2, 3*np.pi/4, np.pi]) 409 | ax.set_yticklabels([r'$0$', r'$\pi/4$', 410 | r'$\pi/2$', r'$3\pi/4$', r'$\pi$']) 411 | 412 | plt.show() 413 | return {'fp_time': fp_time, 414 | 'fp_theta': fp_theta, 415 | 'fp_data': fp_data_b, 416 | 'fp_ps': fp_a_ps, 417 | 'fp_ps_manual': fp_a_ps_manual, 418 | 'sllgs_time': sllgs_time, 419 | 'sllgs_theta': theta_axis, 420 | 'sllgs_data': sllgs_data, 421 | 'sllgs_ps': sllgs_sw_ps 422 | } 423 | 424 | 425 | ################################ 426 | # comparison 427 | ################################ 428 | 429 | if compute_analytical_manual: 430 | ps_fp_manual = [None] * len(t_delays_ns) 431 | 432 | plot_idx = 3 433 | for idx in range(len(sllgs_files)): 434 | # if idx%4 != plot_idx: 435 | # continue 436 | print('---------------------', idx) 437 | data = analyze_s_llgs_tolerances( 438 | sllgs_files[idx], 439 | sllgs_time_files[idx], 440 | t_delays_ns[idx]*1e-9, 441 | I0_uA[idx], 442 | compute_analytical_manual=compute_analytical_manual, 443 | plot_intermediate=plot_intermediate) 444 | ps_sslgs[idx] = data['sllgs_ps'] 445 | time_sslgs[idx] = data['sllgs_time'] 446 | ps_fp[idx] = data['fp_ps'] 447 | time_fp[idx] = data['fp_time'] 448 | if compute_analytical_manual: 449 | ps_fp_manual[idx] = data['fp_ps_manual'] 450 | 451 | fig, ax = plt.subplots(1, 1, 452 | sharex=True, 453 | figsize=(8, 6)) 454 | markers = itertools.cycle( 455 | ('o', '*', 'd', '1', ',', '+', '.', 's', 'X', 'x')) 456 | for idx in range(len(sllgs_files)): 457 | # if idx%4 != plot_idx: 458 | # continue 459 | print('---------------------', idx) 460 | color = next(colors) 461 | marker = next(markers) 462 | np.savetxt(f'dummy_sllgs_wer_{idx}.csv', 463 | np.array([time_sslgs[idx], 1-ps_sslgs[idx]])) 464 | ax.plot(1e9*time_sslgs[idx], 1-ps_sslgs[idx], 465 | # label=sllgs_files[idx].split('/')[-1], 466 | label='10000 s-LLGS walks', 467 | marker=marker, 468 | color=color) 469 | ax.plot(1e9*time_fp[idx], 470 | 1-ps_fp[idx], 471 | '--', 472 | # label=f'fp {sllgs_files[idx]}', 473 | label='Fokker-Plank', 474 | color=color) 475 | if compute_analytical_manual: 476 | ax.plot(1e9*time_fp[idx], 477 | 1-ps_fp_manual[idx], 478 | ':', 479 | # label=sllgs_files[idx], 480 | color=color) 481 | # ax.plot(1e9*fp_time, 1-fp_b_ps, label='fpb') 482 | # ax.plot(1e9*time, 1-sw_ps, '--', label='sllgs') 483 | ax.set_title('WER over time') 484 | ax.set_xlabel('time [a.u.]') 485 | ax.set_ylabel('WER') 486 | ax.set_yscale('log', base=10) 487 | ax.legend() 488 | ax.grid() 489 | plt.show() 490 | -------------------------------------------------------------------------------- /src/sllgs_fpe_comparison/python_compact_model: -------------------------------------------------------------------------------- 1 | ../python_compact_model/ -------------------------------------------------------------------------------- /src/sllgs_fpe_comparison/sllgs_importer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | # 4 | # Copyright (c) 2020-2021 Arm Ltd. 5 | # All rights reserved. 6 | # 7 | # SPDX-License-Identifier: BSD-3-Clause 8 | 9 | """s-LLGs importing tool.""" 10 | 11 | import numpy as np 12 | 13 | 14 | def process_sllgs_data(data_file, time_file, _t_delay, dim_points): 15 | """ 16 | Process s-LLGS. 17 | 18 | Note it removes the delayed samples. 19 | """ 20 | print(f'[debug] reading {data_file}') 21 | data_tol = np.loadtxt(data_file, delimiter=' ') 22 | _time_tol = np.loadtxt(time_file, delimiter=' ') 23 | theta_axis = np.linspace(np.pi, 0, dim_points) 24 | 25 | #################################################################### 26 | # Process data and doing FPE 27 | # ~/SharePoint/ProjectEnergyScalableMRAM/docs/essderc_2021/raw_data 28 | #################################################################### 29 | data_tol = np.abs(data_tol) 30 | print(f'max theta in data_tol {np.max(data_tol)}') 31 | print(data_tol.shape) 32 | x = np.zeros(data_tol.shape[0]*data_tol.shape[1]) 33 | y = np.zeros(data_tol.shape[0]*data_tol.shape[1]) 34 | _sllgs_ps_tol = np.zeros(_time_tol.shape[0]) 35 | for i in range(data_tol.shape[0]): 36 | for j in range(data_tol.shape[1]): 37 | idx = i*data_tol.shape[1] + j 38 | x[idx] = _time_tol[j] 39 | y[idx] = np.abs(data_tol[i, j]) 40 | _s_llgs_bins = [np.max([50, data_tol.shape[1]]), dim_points] 41 | _sllgs_rho = np.histogram2d(x, y, 42 | range=[None, [0, np.pi]], 43 | bins=_s_llgs_bins, 44 | density=True) 45 | print(f'histogram shape: {_sllgs_rho[0].shape}') 46 | # normalize and log 47 | _sllgs_tol_sw_idx = 0 48 | for h_idx, h in enumerate(_sllgs_rho[0]): 49 | area = np.abs(np.trapz(h*np.sin(theta_axis), x=theta_axis)) 50 | _sllgs_rho[0][h_idx] = h/(2*np.pi*area) 51 | 52 | _sllgs_ps_tol = np.zeros(_time_tol.shape[0]) 53 | _sllgs_tol_sw_idx = 0 54 | for j in range(data_tol.shape[1]): 55 | _sllgs_ps_tol[j] = float( 56 | np.sum(data_tol[:, j] > np.pi/2))/float(data_tol.shape[0]) 57 | if _sllgs_tol_sw_idx == 0 and _sllgs_ps_tol[j] >= 0.5: 58 | _sllgs_tol_sw_idx = j 59 | print('switch at ', j) 60 | 61 | # H_tol = np.log(H_tol.T) 62 | print('H computed, sw at {1e9*_time_tol[sllgs_low_tol_switching_idx]}') 63 | rho_0_time_idx = np.searchsorted(_time_tol, _t_delay) 64 | print(f'fitting time: {_time_tol[rho_0_time_idx]}') 65 | # shifting till delay 66 | _time_tol = _time_tol[rho_0_time_idx:] - _t_delay 67 | # re-make the tupple 68 | _sllgs_rho = ((_sllgs_rho[0].T)[:, rho_0_time_idx:], 69 | _sllgs_rho[1], 70 | _sllgs_rho[2]) 71 | print(f'H_tol shape: {_sllgs_rho[0].shape}') 72 | _sllgs_tol_sw_idx -= rho_0_time_idx 73 | _sllgs_ps_tol = _sllgs_ps_tol[rho_0_time_idx:] 74 | return _time_tol, _sllgs_rho, _sllgs_tol_sw_idx, _sllgs_ps_tol 75 | -------------------------------------------------------------------------------- /src/verilog_a_compact_model/README.md: -------------------------------------------------------------------------------- 1 | ## Quick Start & More info 2 | Summary: 3 | * Verilog-a compact models: run the testbenches `tb.scs` and `tb_subckt.scs` 4 | 5 | Please, read the full description at [MRAM Framework Description](./doc/README.md). 6 | 7 | 8 | ## Files organization 9 | * `doc` 10 | * [README.md](./doc/README.md) for the **full MRAM framework description** 11 | * `src` 12 | * `verilog_a_compact_model` 13 | * [README.md](./verilog_a_compact_model/README.md) for the MRAM verilog-a compact model description 14 | * `tb` Testbenches 15 | * `tb.scs` Example testbench calling full Verilog-a model (conduction and s-LLGS fully written in verilog-a) 16 | * `tb_subckt.scs` Example testbench calling full Spectre subcircuit model (s-LLGS fully written in verilog-a, conduction writen in Spectre) 17 | * `mram_lib` Verilog-a compact model and Spectre library 18 | * `llg_spherical_solver.va` Verilog-a s-LLGS solver, key file 19 | * `*.va` Parameters or auxiliar functions 20 | * `*.scs` Spectre subcircuits and library 21 | -------------------------------------------------------------------------------- /src/verilog_a_compact_model/mram_lib/conductance_parameters.va: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Arm Ltd. 2 | // All rights reserved. 3 | // 4 | // SPDX-License-Identifier: BSD-3-Clause 5 | // Engineer: fernando.garciaredondo@arm.com 6 | // pranay.prabhat@arm.com 7 | // 8 | // Arm LLGs model for PMA-MTJs, conductance parameters 9 | 10 | // use only if required: 11 | // `ifndef _conductance_parameters_ 12 | // `define _conductance_parameters_ 13 | 14 | `include "disciplines.vams" 15 | `include "constants.vams" 16 | 17 | // PARAMETERS 18 | // TMR and conductance 19 | parameter real p_r_p=6e3; // RP resistance, [ohm] 20 | parameter real p_tmr = 2*p_p*p_p/(1-p_p*p_p); 21 | parameter real p_r_contact = 2; 22 | 23 | // use only if required: 24 | // `endif 25 | -------------------------------------------------------------------------------- /src/verilog_a_compact_model/mram_lib/dimension_parameters.va: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Arm Ltd. 2 | // All rights reserved. 3 | // 4 | // SPDX-License-Identifier: BSD-3-Clause 5 | // Engineer: fernando.garciaredondo@arm.com 6 | // pranay.prabhat@arm.com 7 | // 8 | // Arm LLGs model for PMA-MTJs, dimension parameters 9 | 10 | // use only if required: 11 | // `ifndef _dimension_parameters_ 12 | // `define _dimension_parameters_ 13 | 14 | `include "disciplines.vams" 15 | `include "constants.vams" 16 | 17 | 18 | // PARAMETERS 19 | // dimmensions 20 | parameter real p_t_fl=1.0e-9; // [m] thickness of free layer 21 | parameter real p_t_ox=1.6e-9; // [m] thickness of oxide 22 | parameter real p_w_fl=50e-9; // [m] mtj width 23 | parameter real p_l_fl=50e-9; // [m] mtj length 24 | 25 | 26 | parameter real _area = `M_PI/4*p_w_fl*p_l_fl; // [m^2} 27 | parameter real _volume = _area*p_t_fl; // [m^3] 28 | 29 | // use only if required: 30 | // `endif 31 | -------------------------------------------------------------------------------- /src/verilog_a_compact_model/mram_lib/llg_parameters.va: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Arm Ltd. 2 | // All rights reserved. 3 | // 4 | // SPDX-License-Identifier: BSD-3-Clause 5 | // 6 | // Engineer: fernando.garciaredondo@arm.com 7 | // pranay.prabhat@arm.com 8 | // 9 | // Arm LLGs model for PMA-MTJs, s-LLGS parameters 10 | 11 | // use only if required 12 | // `ifndef _llg_parameters_ 13 | // `define _llg_parameters_ 14 | 15 | `include "disciplines.vams" 16 | `include "constants.vams" 17 | 18 | // PARAMETERS 19 | parameter real p_atol=1e-8; 20 | 21 | // thermal handling 22 | parameter integer p_do_thermal=0; // simulate thermal noise 23 | parameter integer p_do_theta_windowing=0; // force a min theta angle 24 | // (set to theta_init) if 25 | // thermal is not computed, 26 | // to avoid total damping 27 | parameter real p_theta_windowing_factor = 3; // defines theta_min = theta_0 * param 28 | // if windowed 29 | parameter integer p_do_fake_thermal=1; // simulate fake thermal noise 30 | parameter real p_d_theta_fake_th=0.01; // d_theta will have an extra 31 | // term based on this 32 | // randomness 33 | parameter real p_seed=0; 34 | 35 | // pinned layer angle 36 | parameter real p_theta_pl=0.0; // [rad] pinned layer theta 37 | parameter real p_phi_pl=0.0; // [rad] pinned layer phi 38 | 39 | // initial angle 40 | parameter integer p_state_init = 2; // 0: P, 1: AP, 2: theta_init 41 | parameter real p_theta_init=0.01414119; // [rad] 42 | // if state_init 0/1: 43 | // -> (pi -) 1/sqrt(2delta(T)) 44 | parameter real p_phi_init=0.7854; // [rad] 45 | 46 | // magnetic parameters 47 | parameter real p_k_i_0=1e-3; // uniaxial anisotropy [J/m^2] 48 | parameter real p_alpha=0.01; // alpha damping factor 49 | parameter real p_ms=1.2e6; // magnetization sat. [A/m] 50 | // shape anisotropy 51 | parameter real p_shape_ani_demag_mode=1; // 0: no shape anisotropy 52 | // oommf validation 53 | // parameter real p_shape_ani_demag_mode=1; // 0: no shape anisotropy 54 | // 1: shape_ani_demag_n vector 55 | // 2 and 3: 56 | // two different literature 57 | // implementations 58 | // oomff validation 59 | parameter real p_shape_ani_demag_n_x=0.029; // Shape anisotropy demag vector 60 | parameter real p_shape_ani_demag_n_y=0.029; // if -1, it is computed 61 | parameter real p_shape_ani_demag_n_z=0.941; // as a cylinder 62 | // exchange energy 63 | parameter integer p_do_a_exchange=0; // whether do or not energy 64 | // exchange 65 | parameter real p_a_exchange=1e-11; // Exchange constant [J/m] 66 | // stt parameters 67 | parameter real p_stt_mode=1; // 0: stt_oommf_full: 68 | // lamb. & P free per layer 69 | // 1: stt_oommf_simple: 70 | // single lambda and P 71 | // 2: stt_simplest: just p 72 | // stt mode oomf_full 73 | parameter real p_stt_scale_mult = 1; // 1 matching OOMMF 74 | parameter real p_p_pl=0.3; // polarization factor 75 | parameter real p_p_fl=0.25; // polarization factor 76 | parameter real p_lambda_pl=1.2; // [FITTING] parameter 77 | parameter real p_lambda_fl=1.2; // [FITTING] parameter 78 | // for out of plane torque 79 | // stt mode oomf_simple 80 | parameter real p_lambda_s=1.0; // [FITTING] parameter 81 | parameter real p_p=0.75; // polarization factor 82 | // stt mode simplest 83 | parameter real p_nabla_mult=1; // epsilon = nabla/2 84 | // secondary STT term 85 | parameter real p_eps_prime=0.0; // [FITTING] constant 86 | // vcma paramters 87 | parameter integer p_do_vcma=0; 88 | parameter real p_xi=61e-15; // vcma coeff, [J/(V*m)] 89 | 90 | //use only if needed 91 | // `endif 92 | -------------------------------------------------------------------------------- /src/verilog_a_compact_model/mram_lib/llg_spherical_solver.va: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Arm Ltd. 2 | // All rights reserved. 3 | // 4 | // SPDX-License-Identifier: BSD-3-Clause 5 | // 6 | // Engineer: fernando.garciaredondo@arm.com 7 | // pranay.prabhat@arm.com 8 | // 9 | // Arm LLGs model for PMA-MTJs, s-LLGS solver 10 | 11 | `ifndef _llg_spherical_solver_ 12 | `define _llg_spherical_solver_ 13 | 14 | `include "disciplines.vams" 15 | `include "constants.vams" 16 | 17 | // fl or nothing: free layer 18 | // pl: pinned layer 19 | 20 | // Behavioral Model for a single magnetic tunnel junction 21 | module LLGs_va (i_pl_fl, v_pl_fl, v_m_z, h_ext_x, h_ext_y, h_ext_z); 22 | 23 | // functions 24 | `include "vectorial_fn.va" 25 | // constants 26 | `include "physical_constants.va" 27 | // parameters 28 | `include "llg_parameters.va" 29 | `include "dimension_parameters.va" 30 | `include "conductance_parameters.va" 31 | 32 | ground gnd; 33 | 34 | input i_pl_fl, v_pl_fl; 35 | output v_m_z; 36 | input h_ext_x, h_ext_y, h_ext_z; 37 | 38 | // electrical variables 39 | electrical h_ext_x, h_ext_y, h_ext_z; 40 | // current, voltage 41 | current i_pl_fl; 42 | voltage v_pl_fl; 43 | voltage v_m_z; 44 | 45 | // seed 46 | integer seed; 47 | 48 | // last time 49 | real t_prev; 50 | real dt; 51 | 52 | // reduced gamma [A/m] 53 | real c_gamma; 54 | 55 | // relevant angles 56 | real theta_init, phi_init; 57 | real theta_rand; 58 | real theta_0, theta_min; 59 | 60 | // magnetic vector differentials 61 | // real sin_min_theta; 62 | real d_m_theta, d_m_phi; 63 | real d_fake_th_theta; 64 | // magnetic vector 65 | real m_rho, m_theta, m_phi; 66 | real m_x, m_y, m_z; 67 | // pinned layer magnetic vector 68 | real pl_x, pl_y, pl_z; 69 | 70 | // h_eff_cartesians 71 | real h_eff_x, h_eff_y, h_eff_z; 72 | // noise 73 | real h_th_x, h_th_y, h_th_z; 74 | // h_stt1 and h_stt2 for in-out/plane stt components 75 | real epsilon, beta; 76 | real h_stt_1_x, h_stt_1_y, h_stt_1_z; 77 | real h_stt_2_x, h_stt_2_y, h_stt_2_z; 78 | // h_aux_cartesians 79 | real h_inc_aux_x, h_inc_aux_y, h_inc_aux_z; 80 | 81 | // spherical components 82 | real d_h_eff_theta, d_h_eff_phi, d_h_stt_1_theta, d_h_stt_1_phi, d_h_stt_2_theta, d_h_stt_2_phi; 83 | 84 | // mdp product 85 | real mdp; 86 | 87 | // unit vector terms 88 | real dr_theta_x, dr_theta_y, dr_theta_z; 89 | real dr_phi_x, dr_phi_y, dr_phi_z; 90 | 91 | // ms and ki (dependent on T) 92 | real ms; 93 | real k_i; 94 | // thermal stability 95 | real h_k_0; 96 | real thermal_stability; 97 | real th_power_noise_std; 98 | 99 | // STT related constants/magnitudes following OOMMF. 100 | // three different modes: 101 | // a) OOMMFC, where lambda_pl != lambda_fl 102 | // see full equation at 103 | // https://kelvinxyfong.wordpress.com/research/research-interests 104 | // /oommf-extensions/oommf-extension-xf_thermspinxferevolve/ 105 | // b) OOMMFC, where lambda_pl == lambda_fl == lambda 106 | // c) stt_simplest: 107 | // Khvalkovskiy, A. V., et. al. 108 | // Basic principles of STT-MRAM cell operation in memory arrays. 109 | // Journal of Physics D: Applied Physics, 46(7), 074001. 110 | // https://doi.org/10.1088/0022-3727/46/7/074001 111 | // 112 | // precomputed once for efficiency 113 | real _stt_scale_fac; 114 | real _lamb_PL2, _lamb_FL2; 115 | real _ap, _am, _ap2, _am2; 116 | real _kp, _km; 117 | real _eps_simple_num, _eps_simple_den0, _eps_simple_den1; 118 | 119 | // shape anisotropy demag_n 120 | real shape_ani_demag_n_x, shape_ani_demag_n_y, shape_ani_demag_n_z; 121 | 122 | // aux variable for user defined fn 123 | real fn; 124 | real init_aux; 125 | 126 | // Inits shape anisotropy constants. 127 | analog function real initialize_shape_anisotropy_demag; 128 | output shape_ani_demag_n_x, shape_ani_demag_n_y, shape_ani_demag_n_z; 129 | real shape_ani_demag_n_x, shape_ani_demag_n_y, shape_ani_demag_n_z, 130 | r, nz; 131 | begin 132 | // mode 0, no shape anisotropy 133 | if (p_shape_ani_demag_mode == 0) begin 134 | shape_ani_demag_n_x = 0.0; 135 | shape_ani_demag_n_y = 0.0; 136 | shape_ani_demag_n_z = 0.0; 137 | end 138 | // mode 0.5, given shape anisotropy 139 | else if (p_shape_ani_demag_mode == 1 && 140 | p_shape_ani_demag_n_x >= 0 && 141 | p_shape_ani_demag_n_y >= 0 && 142 | p_shape_ani_demag_n_z >= 0) begin 143 | shape_ani_demag_n_x = p_shape_ani_demag_n_x; 144 | shape_ani_demag_n_y = p_shape_ani_demag_n_y; 145 | shape_ani_demag_n_z = p_shape_ani_demag_n_z; 146 | end 147 | else if (p_shape_ani_demag_mode == 2) begin 148 | // ferromagnetic cylinder 149 | // Sato, M., & Ishii, Y. (1989). 150 | // Simple and approximate expressions of demagnetizing 151 | // factors of uniformly magnetized rectangular 152 | // rod and cylinder. 153 | // Journal of Applied Physics, 66(2), 983–985. 154 | // https://doi.org/10.1063/1.343481 155 | r = $sqrt(p_w_fl*p_l_fl)/2; 156 | nz = 1/(2*p_t_fl/(r*$sqrt(`M_PI)) + 1); 157 | shape_ani_demag_n_x = (1-nz)/2; 158 | shape_ani_demag_n_y = (1-nz)/2; 159 | shape_ani_demag_n_z = nz; 160 | end 161 | else if (p_shape_ani_demag_mode == 3) begin 162 | // Zhang, K., et al. 163 | // Compact Modeling and Analysis of Voltage-Gated 164 | // Spin-Orbit Torque Magnetic Tunnel Junction. 165 | // IEEE Access, 8, 50792–50800. 166 | // https://doi.org/10.1109/ACCESS.2020.2980073 167 | shape_ani_demag_n_x = `M_PI*p_t_fl / (4*$sqrt(p_w_fl*p_l_fl)); 168 | shape_ani_demag_n_y = `M_PI*p_t_fl / (4*$sqrt(p_w_fl*p_l_fl)); 169 | shape_ani_demag_n_z = 1 - 2 * `M_PI*p_t_fl/(4*$sqrt(p_w_fl*p_l_fl)); 170 | end 171 | else begin 172 | $strobe("\n\n**************************************"); 173 | $strobe("\t[ERROR] in anisotropy definition."); 174 | end 175 | // dummyV 176 | initialize_shape_anisotropy_demag = 0; 177 | end 178 | endfunction 179 | 180 | analog function real initialize_stt_oommf_full; 181 | input ms; 182 | output _stt_scale_fac, _ap, _am, _kp, _km; 183 | real ms, 184 | _stt_scale_fac, _ap, _am, _kp, _km, 185 | // internal aux 186 | _lamb_PL2, _lamb_FL2, _ap2, _am2; 187 | begin 188 | _stt_scale_fac = p_stt_scale_mult * c_HBAR / ( 189 | c_U0 * c_E * ms * _volume); 190 | _lamb_PL2 = p_lambda_pl*p_lambda_pl; 191 | _lamb_FL2 = p_lambda_fl*p_lambda_fl; 192 | _ap = $sqrt(_lamb_PL2+1)*(_lamb_FL2+1); 193 | _am = $sqrt(_lamb_PL2-1)*(_lamb_FL2-1); 194 | _ap2 = (_lamb_PL2+1)*(_lamb_FL2+1); 195 | _am2 = (_lamb_PL2-1)*(_lamb_FL2-1); 196 | _kp = p_p_pl*_lamb_PL2 * $sqrt((_lamb_FL2+1)/(_lamb_PL2+1)) + 197 | p_p_fl * _lamb_FL2*$sqrt((_lamb_PL2-1)/(_lamb_FL2-1)); 198 | _km = p_p_pl*_lamb_PL2 * $sqrt((_lamb_FL2+1)/(_lamb_PL2+1)) - 199 | p_p_fl * _lamb_FL2*$sqrt((_lamb_PL2-1)/(_lamb_FL2-1)); 200 | // dummyV 201 | initialize_stt_oommf_full = 0; 202 | end 203 | endfunction 204 | 205 | analog function real initialize_stt_oommf_simple; 206 | input ms; 207 | output _stt_scale_fac, _eps_simple_num, _eps_simple_den0, _eps_simple_den1; 208 | real ms, 209 | _stt_scale_fac, _eps_simple_num, _eps_simple_den0, _eps_simple_den1; 210 | begin 211 | _stt_scale_fac = p_stt_scale_mult * c_HBAR / ( 212 | c_U0 * c_E * ms * _volume); 213 | _eps_simple_num = p_p*p_lambda_s*p_lambda_s; 214 | _eps_simple_den0 = p_lambda_s*p_lambda_s + 1; 215 | _eps_simple_den1 = p_lambda_s*p_lambda_s - 1; 216 | // dummyV 217 | initialize_stt_oommf_simple = 0; 218 | end 219 | endfunction 220 | 221 | analog function real initialize_stt_simplest; 222 | input ms; 223 | output _stt_scale_fac; 224 | real _stt_scale_mult, ms, _volume, 225 | _stt_scale_fac; 226 | begin 227 | _stt_scale_fac = p_stt_scale_mult * c_HBAR / ( 228 | c_U0 * c_E * ms * _volume); 229 | // epsilon = nabla/2 230 | // nabla = _nabla_mult * P/(1+/-P^2) 231 | // so epsilon = _nabla_mult/2 * P/(1+/-P^2) 232 | // see model code, different research groups 233 | // use different factors for SMTJ 234 | // dummyV 235 | initialize_stt_simplest = 0; 236 | end 237 | endfunction 238 | 239 | analog function real initialize_stt_constants; 240 | input ms; 241 | output _stt_scale_fac, _ap, _am, _kp, _km, 242 | _eps_simple_num, _eps_simple_den0, _eps_simple_den1; 243 | real ms, 244 | _stt_scale_fac, _ap, _am, _kp, _km, 245 | _eps_simple_num, _eps_simple_den0, _eps_simple_den1, 246 | fn; 247 | begin 248 | if (p_stt_mode==0) begin // 0: stt_oommf_full: 249 | fn = initialize_stt_oommf_full( 250 | // inputs 251 | ms, 252 | // outputs 253 | _stt_scale_fac, _ap, _am, _kp, _km); 254 | end 255 | else if (p_stt_mode==1) begin // 1: stt_oommf_simple: 256 | fn = initialize_stt_oommf_simple( 257 | // inputs 258 | ms, 259 | // outputs 260 | _stt_scale_fac, _eps_simple_num, _eps_simple_den0, _eps_simple_den1); 261 | end 262 | else if (p_stt_mode==2) begin // simplest, just p 263 | fn = initialize_stt_simplest( 264 | // inputs 265 | ms, 266 | // outputs 267 | _stt_scale_fac); 268 | end 269 | else begin 270 | $strobe("\n\n**************************************"); 271 | $strobe("\t[ERROR] in STT mode definition."); 272 | end 273 | // dummy 274 | fn = 0; 275 | end 276 | endfunction 277 | 278 | // Init thermal constants. 279 | analog function real initialize_thermal_constants; 280 | input ms, _volume; 281 | output th_power_noise_std; 282 | real ms, _volume, th_power_noise_std; 283 | begin 284 | 285 | // Equation from newest Purdue's paper 286 | // Torunbalci, M. M., et. al (2018). 287 | // Modular Compact Modeling of MTJ Devices. 288 | // IEEE Transactions on Electron Devices, 65(10), 4628–4634. 289 | // https:#doi.org/10.1109/TED.2018.2863538 290 | // and 291 | // De Rose, R., et al. 292 | // A Variation-Aware Timing Modeling Approach for Write Operation 293 | // in Hybrid CMOS/STT-MTJ Circuits. 294 | // IEEE Transactions on Circuits and Systems I: Regular Papers, 295 | // 65(3), 1086–1095. 296 | // https:#doi.org/10.1109/TCSI.2017.2762431 297 | // Please, 298 | // note it is different from: 299 | // Ament, S., Rangarajan, N., Parthasarathy, A., & Rakheja, S. (2016). 300 | // Solving the stochastic Landau-Lifshitz-Gilbert-Slonczewski equation 301 | // for monodomain nanomagnets : A survey and analysis 302 | // of numerical techniques. 1–19. 303 | // http://arxiv.org/abs/1607.04596 304 | 305 | // We also include the (1+alpha^2) effect from 306 | // Lee, H., Lee, A., Wang, S., Ebrahimi, F., Gupta, 307 | // P., Khalili Amiri, P., & Wang, K. L. (2018). 308 | // Analysis and Compact Modeling of Magnetic Tunnel Junctions 309 | // Utilizing Voltage-Controlled Magnetic Anisotropy. 310 | // IEEE Transactions on Magnetics, 54(4). 311 | // https://doi.org/10.1109/TMAG.2017.2788010 312 | 313 | // Also, note that the field generated later is 314 | // sqrt(2 alpha temp Kb / ((1+alpha^2) u0 gamma Ms V dt)) 315 | 316 | // Units: A/m -> (note gamma = c_GAMMA_0*c_U0) 317 | // (J/K) * K /(H/m *A/m * m/C * m^3 * s) 318 | // th_gamma = c_GAMMA_0 * c_U0 / (1 + p_alpha*p_alpha) 319 | 320 | th_power_noise_std = $sqrt(2 * p_alpha * $temperature * c_KB / 321 | (c_U0 * c_GAMMA_0 * c_U0 / (1 + p_alpha*p_alpha) * ms * _volume)); 322 | // dummyV 323 | initialize_thermal_constants = 0; 324 | end 325 | endfunction 326 | 327 | // init magnetic vectors 328 | analog function real initialize_magnetic_vectors; 329 | input theta_init, phi_init, theta_0, theta_rand; 330 | output theta, phi, mx, my, mz, plx, ply, plz, theta_min; 331 | real theta_init, phi_init, state, theta_0, theta_rand, 332 | theta, phi, mx, my, mz, plx, ply, plz, theta_min, 333 | theta_init_aux, fn; 334 | begin 335 | // initial angles under no variability 336 | 337 | // mean angle in maxwell boltzman distribution: 338 | // 2*scale*$sqrt(2/`M_PI)+loc 339 | // default theta_init if theta was not specified 340 | if (p_state_init != 2) begin 341 | if (theta_init < `M_PI/2) begin 342 | theta_init_aux = 2*theta_0*$sqrt(2/`M_PI) + 0; 343 | end 344 | else begin 345 | theta_init_aux = `M_PI - (2*theta_0*$sqrt(2/`M_PI) + 0); 346 | end 347 | end 348 | else begin 349 | theta_init_aux = theta_init; 350 | end 351 | 352 | // initial angle variability, 353 | // f noise is present and an initial angle has not been passed 354 | if (p_do_thermal) begin 355 | if (theta_init_aux < `M_PI/2) begin 356 | theta_init_aux = theta_rand; 357 | end 358 | else begin 359 | theta_init_aux = `M_PI - theta_rand; 360 | end 361 | end 362 | 363 | // windowing case 364 | theta_min = p_theta_windowing_factor * theta_0; 365 | // force min theta even when passed initial params 366 | // in case it is out of boundaries 367 | if (p_do_theta_windowing) begin 368 | if (theta_init_aux > `M_PI/2 && (`M_PI-theta_init_aux < theta_min)) begin 369 | theta_init_aux = `M_PI - theta_min; 370 | end 371 | else if (theta_init_aux < theta_min) begin 372 | theta_init_aux = theta_min; 373 | end 374 | $strobe("windowing theta_init_aux: %f", theta_init_aux); 375 | end 376 | 377 | // init vectors 378 | theta = theta_init_aux; 379 | phi = phi_init; 380 | fn = cart_from_spherical(1.0, theta, phi, mx, my, mz); 381 | fn = cart_from_spherical(1.0, p_theta_pl, p_phi_pl, plx, ply, plz); 382 | // dummyV 383 | initialize_magnetic_vectors = 0; 384 | end 385 | endfunction 386 | 387 | // shape anisotropy demagnetization 388 | analog function real get_h_demag; 389 | input mx, my, mz, ms, nx, ny, nz; 390 | output hx, hy, hz; 391 | real mx, my, mz, ms, nx, ny, nz, 392 | hx, hy, hz; 393 | begin 394 | // Get H_demag field vector due to shape anisotropy in the FL. 395 | // 396 | // Zhang, K., et. al. 397 | // Compact Modeling and Analysis of Voltage-Gated Spin-Orbit 398 | // Torque Magnetic Tunnel Junction. 399 | // IEEE Access, 8, 50792–50800. 400 | // https://doi.org/10.1109/ACCESS.2020.2980073 401 | // 402 | // Full anisotropy: 403 | // Watanabe, K., 404 | // Shape anisotropy revisited in single-digit nanometer magnetic 405 | // tunnel junctions. 406 | // Nature Communications, 9(1), 5–10. 407 | // https://doi.org/10.1038/s41467-018-03003-7 408 | // given [nx, ny, xz] 409 | hx = -ms*nx * mx; 410 | hy = -ms*ny * my; 411 | hz = -ms*nz * mz; 412 | // dummyV 413 | get_h_demag = 0; 414 | end 415 | endfunction 416 | 417 | // VCMA 418 | analog function real get_h_vcma; 419 | input v_pl_fl, ms, mz; 420 | output hz; 421 | real v_pl_fl, ms, mz, 422 | hz; 423 | begin 424 | // Get VCMA vector. 425 | // Zhang, K., et. al. 426 | // Compact Modeling and Analysis of Voltage-Gated Spin-Orbit 427 | // Torque Magnetic Tunnel Junction. 428 | // IEEE Access, 8, 50792–50800. 429 | // https://doi.org/10.1109/ACCESS.2020.2980073 430 | 431 | if (!p_do_vcma) begin 432 | hz = 0.0; 433 | end 434 | else begin 435 | // note instead of -2 * p_xi... 436 | // that is as the voltage v_pl_fl refers PL/FL voltage, 437 | // and not FL/PL voltage as in the paper references 438 | hz = 2 * p_xi * v_pl_fl * mz / (p_t_fl * p_t_ox * c_U0 * ms); 439 | end 440 | // dummyV 441 | get_h_vcma = 0; 442 | end 443 | endfunction 444 | 445 | // uniaxial anisotropy 446 | analog function real get_h_unia; 447 | input mz, ki, ms; 448 | output hz; 449 | real mz, ki, ms, 450 | hz; 451 | begin 452 | 453 | // We consider interfacial PMA anisotropy. 454 | // The geometry defines it. 455 | // See Figure 2 at 456 | // Khvalkovskiy, A. V., et. al. 457 | // Basic principles of STT-MRAM cell operation in memory arrays. 458 | // Journal of Physics D: Applied Physics, 46(7), 074001. 459 | // https://doi.org/10.1088/0022-3727/46/7/074001 460 | // 461 | // Full anisotropy: 462 | // Watanabe, K., 463 | // Shape anisotropy revisited in single-digit nanometer magnetic 464 | // tunnel junctions. 465 | // Nature Communications, 9(1), 5–10. 466 | // https://doi.org/10.1038/s41467-018-03003-7 467 | // vector u_anisotropy == unitary z 468 | // # uniaxial aniotropy constant in J/m^3 469 | // p_k_u = p_thermal_stability * p_temperature * \ 470 | // c_KB / (p__volume) 471 | // print(f'[debug] k_u: {p_k_u}') 472 | // print(f'[debug] thermal stability: {p_thermal_stability}') 473 | // return np.array([0., 474 | // 0., 475 | // 2 * p_k_u * m_cart[2] / (c_U0 * p_ms) 476 | // ]) 477 | hz = 2 * ki * mz / (p_t_fl * c_U0 * ms); 478 | // dummyV 479 | get_h_unia = 0; 480 | end 481 | endfunction 482 | 483 | // STT epsilon 484 | analog function real get_epsilon_stt; 485 | input _kp, _ap, _km, _am, mdp, 486 | _eps_simple_num, _eps_simple_den0, _eps_simple_den1; 487 | // output epsilon; 488 | real _kp, _ap, _km, _am, mdp, 489 | _eps_simple_num, _eps_simple_den0, _eps_simple_den1; 490 | begin 491 | // Compute OOMMF epsilon term based on mdp vector. 492 | 493 | // a) OOMMFC, where lambda_pl != lambda_fl 494 | // b) OOMMFC, where lambda_pl == lambda_fl == lambda 495 | // c) stt_simplest: 496 | // Khvalkovskiy, A. V., et. al. 497 | // Basic principles of STT-MRAM cell operation in memory arrays. 498 | // Journal of Physics D: Applied Physics, 46(7), 074001. 499 | // https://doi.org/10.1088/0022-3727/46/7/074001 500 | if (p_stt_mode == 0) begin 501 | if (p_lambda_pl == 1.0 || p_lambda_fl == 1.0) begin 502 | get_epsilon_stt = 0.5 * p_p; 503 | end 504 | else begin 505 | get_epsilon_stt = _kp / (_ap + _am * mdp) + _km / (_ap - _am * mdp); 506 | end 507 | end 508 | else if (p_stt_mode == 1) begin 509 | get_epsilon_stt = _eps_simple_num / (_eps_simple_den0 + _eps_simple_den1 * mdp); 510 | end 511 | else if (p_stt_mode == 2) begin 512 | get_epsilon_stt = p_nabla_mult/2*p_p/(1 + p_p * p_p * mdp); 513 | end 514 | else begin 515 | $strobe("[ERROR] Non-valid stt_mode "); 516 | // dummy 517 | get_epsilon_stt = -1; 518 | end 519 | end 520 | endfunction 521 | 522 | analog function real tukey_window; 523 | input x, x_min; 524 | real x, x_min; 525 | begin 526 | // Computes Tukey window 527 | // 0.5*(1 - cos(2*pi*x / (theta_min/2))) 528 | if (x < x_min/4) begin 529 | tukey_window = 0.5*(1 - $cos(2*`M_PI*x / (x_min/2))); 530 | end 531 | else begin 532 | tukey_window = 1.0; 533 | end 534 | end 535 | endfunction 536 | 537 | // theta window definition 538 | analog function real theta_window; 539 | input d_theta, theta, theta_min; 540 | real d_theta, theta, theta_min; 541 | begin 542 | // Windowing fn for theta. 543 | // Requires theta between [0, np.pi], not an open integration. 544 | if ((theta > `M_PI/2 && d_theta < 0) || (theta < `M_PI/2 && d_theta > 0)) begin 545 | theta_window = 1.0; 546 | end 547 | else if (theta >= `M_PI/2 && (`M_PI - theta > theta_min)) begin 548 | // x = -theta + (pi - theta_min) 549 | theta_window = tukey_window(-theta + (`M_PI-theta_min), theta_min); 550 | end 551 | else if (theta <= `M_PI/2 && theta > theta_min) begin 552 | // x = theta - theta_min 553 | theta_window = tukey_window(theta -theta_min, theta_min); 554 | end 555 | else begin 556 | theta_window = 0.0; 557 | end 558 | end 559 | endfunction 560 | 561 | analog begin : LLGs_behav 562 | 563 | @(initial_step) begin 564 | 565 | if (p_do_thermal + p_do_theta_windowing + p_do_fake_thermal > 1) begin 566 | $strobe("\n\n**************************************"); 567 | $strobe("\t[ERROR], only one thermal field scheme can be selected"); 568 | $fatal(0); 569 | end 570 | // check parameters 571 | // thermal, anysotropy, stt, 572 | 573 | // need of initialization 574 | seed = p_seed; 575 | 576 | // Inits shape anisotropy constants. 577 | fn = initialize_shape_anisotropy_demag( 578 | // inputs 579 | // outputs 580 | shape_ani_demag_n_x, shape_ani_demag_n_y, shape_ani_demag_n_z); 581 | 582 | k_i = p_k_i_0; 583 | ms = p_ms; 584 | 585 | c_gamma = c_U0 * c_GAMMA_0 / (1 + p_alpha*p_alpha); 586 | 587 | // initial theta_init guess, if not specified 588 | if (p_state_init == 2) begin 589 | theta_init = p_theta_init; 590 | end 591 | // approximate m_cart for initial h_k 592 | // will be updated appropriately in init_mag_vectors 593 | else begin 594 | if (p_state_init == 0) begin 595 | theta_init = 0; 596 | end 597 | else if (p_state_init == 1) begin 598 | theta_init = `M_PI; 599 | end 600 | else begin 601 | $strobe("\n\n**************************************"); 602 | $strobe("\t[ERROR] not supported state_init."); 603 | $fatal(0); 604 | end 605 | end 606 | // p_phi_init always provided 607 | phi_init = p_phi_init; 608 | 609 | // thermal stability 610 | // h_k perpendicular anisotropy component 611 | h_k_0 = 2 * k_i * abs($cos(theta_init)) / (p_t_fl * c_U0 * ms); 612 | // h_k demag component 613 | h_k_0 = h_k_0 - ms*shape_ani_demag_n_z * abs($cos(theta_init)); 614 | // VCMA not considered as no voltage is applied at the begining 615 | thermal_stability = (h_k_0 * c_U0 * ms * _volume) / (2 * c_KB * $temperature); 616 | // theta_0 617 | theta_0 = $sqrt(1/(2*thermal_stability)); 618 | 619 | // STT constants, see llg_parameters.va 620 | fn = initialize_stt_constants( 621 | // inputs 622 | ms, 623 | // outputs 624 | _stt_scale_fac, _ap, _am, _kp, _km, 625 | _eps_simple_num, _eps_simple_den0, _eps_simple_den1); 626 | 627 | // thermal constants 628 | fn = initialize_thermal_constants(ms, _volume, th_power_noise_std); 629 | 630 | // we could add 1ns of stabilization before injecting current 631 | // or, by adding noise with gaussian/maxwell-boltzmann distribution, 632 | // where the second moment gives 633 | // the thermal average (theta_0 square) 634 | 635 | // # Maxwell-Boltzmann distribution info: 636 | // a) Switching distributions for perpendicular spin-torque devices 637 | // within the macrospin approximation. 638 | // IEEE Transactions on Magnetics, 48(12), 4684–4700. 639 | // https://doi.org/10.1109/TMAG.2012.2209122 640 | // b) Khvalkovskiy, A. V., et. al. 641 | // Basic principles of STT-MRAM cell operation in memory arrays. 642 | // Journal of Physics D: Applied Physics, 46(7), 074001. 643 | // https://doi.org/10.1088/0022-3727/46/7/074001 644 | 645 | // theta_0 can be given by 1/sqrt(2*delta) (most common aproach) 646 | // a) Sun, J. Z. (2006). 647 | // Spin angular momentum transfer in current-perpendicular 648 | // nanomagnetic junctions. IBM Journal of Research and Development 649 | // https://doi.org/10.1147/rd.501.0081< 650 | // Butler, W. H., et al. 651 | // b) Switching distributions for perpendicular spin-torque devices 652 | // within the macrospin approximation. 653 | // IEEE Transactions on Magnetics, 48(12), 4684–4700. 654 | // https://doi.org/10.1109/TMAG.2012.2209122 655 | 656 | // or 1/sqrt(delta) 657 | // c) Khvalkovskiy, A. V., et al. 658 | // Basic principles of STT-MRAM cell operation in memory arrays. 659 | // Journal of Physics D: Applied Physics, 46(7), 074001. 660 | // https://doi.org/10.1088/0022-3727/46/7/074001 661 | //get maxwell distribution from chi squared distribution 662 | // if X~maxwell then 663 | // X^2 ~ X_chisquared^2(3) 664 | theta_rand = $sqrt($rdist_chi_square(seed, 3))*theta_0; 665 | fn =initialize_magnetic_vectors( 666 | // inputs 667 | theta_init, phi_init, 668 | theta_0, theta_rand, 669 | // outputs 670 | m_theta, m_phi, m_x, m_y, m_z, pl_x, pl_y, pl_z, theta_min); 671 | 672 | // time reference 673 | dt = 0.0; 674 | t_prev = -1e-9; 675 | $strobe("Init abstime: %r, prev: %r", $abstime*1e9, t_prev*1e9); 676 | 677 | $strobe("\n\n**********************************************"); 678 | $strobe("\tidt atol: %e", p_atol); 679 | $strobe("\tdo Thermal Noise: %d", $rtoi(p_do_thermal)); 680 | $strobe("\tdo Theta Thermal Windowing: %d", $rtoi(p_do_theta_windowing)); 681 | $strobe("\t\ttheta_windowing_factor: %r", p_theta_windowing_factor); 682 | $strobe("\tdo Fake Theta: %d", $rtoi(p_do_fake_thermal)); 683 | $strobe("\t\td_theta_fake_th: %r", $rtoi(p_d_theta_fake_th)); 684 | $strobe("\tSeed: %d", $rtoi(p_seed)); 685 | 686 | $strobe("**********************************************"); 687 | $strobe("\tdo VCMA: %d", $rtoi(p_do_vcma)); 688 | $strobe("\ttheta_0: %f", theta_0); 689 | $strobe("\ttheta_min: %f", theta_min); 690 | $strobe("\tepsilon_0: %f", epsilon); 691 | $strobe("\tthermal_stability: %f", thermal_stability); 692 | $strobe("**********************************************"); 693 | $strobe("shape ani N: [%f, %f, %f]", shape_ani_demag_n_x, shape_ani_demag_n_y, shape_ani_demag_n_z); 694 | $strobe("\tPL_x: %f, pl_y: %f, pl_z: %f", 695 | pl_x, pl_y, pl_z); 696 | $strobe("\tInitial m_x: %f, m_y: %f, m_z: %f", 697 | m_x, m_y, m_z); 698 | $strobe("\tinitial theta: %f", m_theta); 699 | $strobe("\tRecovered theta: %f, recovered phi: %f", 700 | $acos(m_z), $atan2(m_y, m_x)); 701 | $strobe("\**********************************************\n\n"); 702 | end 703 | 704 | // Compute dm_sph/dt fn. 705 | // 706 | // LLG equation (with secondary stt term) at eq 1.3 707 | // Switching distributions for perpendicular spin-torque devices 708 | // within the macrospin approximation. 709 | // IEEE Transactions on Magnetics, 48(12), 4684–4700. 710 | // https://doi.org/10.1109/TMAG.2012.2209122 711 | // see OOMMF for the value of the different terms 712 | // and 713 | // Ament, S., et al. 714 | // Solving the stochastic Landau-Lifshitz-Gilbert-Slonczewski 715 | // equation for monodomain nanomagnets : 716 | // A survey and analysis of numerical techniques. 717 | // 1–19. Retrieved from http://arxiv.org/abs/1607.04596 718 | 719 | // time reference 720 | dt = $abstime - t_prev; 721 | t_prev = $abstime; 722 | // called twice at t=0: 723 | // if ($abstime == 0.0) begin 724 | // dt = 1e9; 725 | // t_prev = -1e-9; 726 | // end 727 | if ($abstime > 0 && dt <= 0) begin 728 | $strobe("\n\n**************************************"); 729 | $strobe("\t[ERROR] negative/zero time step: %r ns", 1e9*dt); 730 | $fatal(0); 731 | end 732 | 733 | fn = cart_from_spherical(1.0, m_theta, m_phi, m_x, m_y, m_z); 734 | // output m_z for conductance model 735 | V(v_m_z, gnd) <+ m_z; 736 | 737 | // Converting spherical coordinates to Cartesian to check rho evolution 738 | m_rho = $sqrt(m_x*m_x + m_y*m_y + m_z*m_z); 739 | if (m_rho > 1.0001) begin 740 | $strobe("!!rho check @ %g ", m_rho); 741 | end 742 | // m p dot product, defines state 743 | fn = udv_cart(m_x, m_y, m_z, pl_x, pl_y, pl_z, mdp); 744 | 745 | // LLG 746 | // 747 | // [Future] [1] Ms and Ku dependence on temperature 748 | // De Rose, R., et al. 749 | // A Compact Model with Spin-Polarization Asymmetry for Nanoscaled Perpendicular MTJs. 750 | // IEEE Transactions on Electron Devices, 64(10), 4346–4353. 751 | // https://doi.org/10.1109/TED.2017.2734967 752 | // 753 | // Prajapati, S., et. al. 754 | // Modeling of a Magnetic Tunnel Junction for a 755 | // Multilevel STT-MRAM Cell. 756 | // IEEE Transactions on Nanotechnology, 18, 1005–1014. 757 | // https://doi.org/10.1109/TNANO.2018.2875491 758 | 759 | 760 | // effective field components 761 | // external field 762 | h_eff_x = V(h_ext_x); 763 | h_eff_y = V(h_ext_y); 764 | h_eff_z = V(h_ext_z); 765 | 766 | // demagnetization anisotropy 767 | fn = get_h_demag( 768 | // inputs 769 | m_x, m_y, m_z, 770 | ms, 771 | shape_ani_demag_n_x, shape_ani_demag_n_y, shape_ani_demag_n_z, 772 | // outputs 773 | h_inc_aux_x, h_inc_aux_y, h_inc_aux_z); 774 | h_eff_x = h_eff_x + h_inc_aux_x; 775 | h_eff_y = h_eff_y + h_inc_aux_y; 776 | h_eff_z = h_eff_z + h_inc_aux_z; 777 | 778 | // uniaxial anisotropy 779 | fn = get_h_unia( 780 | // inputs 781 | m_z, k_i, ms, 782 | // outputs 783 | h_inc_aux_z); 784 | h_eff_z = h_eff_z + h_inc_aux_z; 785 | 786 | // uniaxial anisotropy 787 | fn = get_h_vcma( 788 | // inputs 789 | V(v_pl_fl), ms, m_z, 790 | // outputs 791 | h_inc_aux_z); 792 | h_eff_z = h_eff_z + h_inc_aux_z; 793 | 794 | // thermal induced field 795 | // thermal noise could be handler by simulator as the following white_noise 796 | // if (analysis("noise")) begin 797 | // h_th_x = white_noise(th_noise_std); 798 | // h_th_y = white_noise(th_noise_std); 799 | // h_th_z = white_noise(th_noise_std); 800 | // end 801 | if( p_do_thermal ) begin 802 | h_th_x = $rdist_normal(seed, 0, th_power_noise_std)/$sqrt(dt); 803 | h_th_y = $rdist_normal(seed, 0, th_power_noise_std)/$sqrt(dt); 804 | h_th_z = $rdist_normal(seed, 0, th_power_noise_std)/$sqrt(dt); 805 | h_eff_x = h_eff_x + h_th_x; 806 | h_eff_y = h_eff_y + h_th_y; 807 | h_eff_z = h_eff_z + h_th_z; 808 | end 809 | 810 | // STT in/out plane components 811 | epsilon = get_epsilon_stt( 812 | // oommf_full 813 | _kp, _ap, _km, _am, mdp, 814 | // oommf simple 815 | _eps_simple_num, _eps_simple_den0, _eps_simple_den1); 816 | // beta computation as in OOMMF 817 | // beta units are [A/m] 818 | beta = I(i_pl_fl) * _stt_scale_fac; 819 | // both eps and eps prime contributions 820 | // h_stt components in [A/m] 821 | h_stt_1_x = beta * (epsilon * pl_x); 822 | h_stt_1_y = beta * (epsilon * pl_y); 823 | h_stt_1_z = beta * (epsilon * pl_z); 824 | h_stt_2_x = beta * (p_eps_prime * pl_x); 825 | h_stt_2_y = beta * (p_eps_prime * pl_y); 826 | h_stt_2_z = beta * (p_eps_prime * pl_z); 827 | 828 | // h_eff_phi (local orthogonal unit vector in the direction 829 | // of increasing phi), etc 830 | // differential terms from cart to spherical 831 | fn = diff_unit_theta_vector( 832 | // inputs 833 | 1.0, m_theta, m_phi, 834 | // outputs 835 | dr_theta_x, dr_theta_y, dr_theta_z); 836 | fn = diff_unit_phi_vector( 837 | // inputs 838 | 1.0, m_theta, m_phi, 839 | // outputs 840 | dr_phi_x, dr_phi_y, dr_phi_z); 841 | 842 | d_h_eff_theta = h_eff_x * dr_theta_x 843 | + h_eff_y * dr_theta_y 844 | + h_eff_z * dr_theta_z; 845 | d_h_eff_phi = h_eff_x * dr_phi_x 846 | + h_eff_y * dr_phi_y 847 | + h_eff_z * dr_phi_z; 848 | d_h_stt_1_theta = h_stt_1_x * dr_theta_x 849 | + h_stt_1_y * dr_theta_y 850 | + h_stt_1_z * dr_theta_z; 851 | d_h_stt_1_phi = h_stt_1_x * dr_phi_x 852 | + h_stt_1_y * dr_phi_y 853 | + h_stt_1_z * dr_phi_z; 854 | d_h_stt_2_theta = h_stt_2_x * dr_theta_x 855 | + h_stt_2_y * dr_theta_y 856 | + h_stt_2_z * dr_theta_z; 857 | d_h_stt_2_phi = h_stt_2_x * dr_phi_x 858 | + h_stt_2_y * dr_phi_y 859 | + h_stt_2_z * dr_phi_z; 860 | 861 | // as specified in OOMMF, gamma([(rad)/s/T]) should interface 862 | // the fields in A/m, so introducing u0 863 | d_m_theta = c_gamma * (d_h_eff_phi + d_h_stt_1_theta - d_h_stt_2_phi + 864 | p_alpha*(d_h_eff_theta - d_h_stt_1_phi - d_h_stt_2_theta)); 865 | d_m_phi = c_gamma * ( 1 / $sin(m_theta)) * ( 866 | -d_h_eff_theta + d_h_stt_1_phi + d_h_stt_2_theta + 867 | p_alpha*(d_h_eff_phi + d_h_stt_1_theta - d_h_stt_2_phi)); 868 | 869 | // Thermal field handling if thermal field not present 870 | 871 | // fake theta term 872 | // extra term can be seen as a direct contribution to h_phi 873 | // as d_theta/dt dependence on h_th is 874 | // ~ c_gamma (h_th_phi + alpha * h_th_theta) 875 | // opposing damping 876 | if (!p_do_thermal && p_do_fake_thermal) begin 877 | d_fake_th_theta = p_d_theta_fake_th * c_gamma * th_power_noise_std / $sqrt(dt); 878 | d_m_theta = d_m_theta -sign(m_theta-`M_PI/2) * d_fake_th_theta; 879 | end 880 | 881 | // saturate d_theta to emulate min field generated 882 | // by the thermal noise, avoiding total damping 883 | if (!p_do_thermal && p_do_theta_windowing) begin 884 | d_m_theta = d_m_theta * theta_window(d_m_theta, m_theta, theta_min); 885 | end 886 | 887 | 888 | // For solving the differential equation (LLG) 889 | // a) providing initial conditions 890 | // b) limiting phi to physical boundaries: idtmod and min/max fn 891 | 892 | m_theta = idt(d_m_theta, theta_init, 0, p_atol); 893 | m_phi = idtmod(d_m_phi, phi_init, 2*`M_PI, 0, p_atol); 894 | 895 | // no abs control 896 | // m_theta = idt(d_m_theta, theta_init); 897 | // m_phi = idtmod(d_m_phi, phi_init, 2*`M_PI); 898 | 899 | // naive integration, do not use 900 | // m_theta = m_theta + d_m_theta*dt; 901 | // m_phi = m_phi + d_m_phi*dt; 902 | 903 | end 904 | 905 | endmodule 906 | 907 | `endif 908 | -------------------------------------------------------------------------------- /src/verilog_a_compact_model/mram_lib/mtj_lib.scs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Arm Ltd. 2 | // All rights reserved. 3 | // 4 | // SPDX-License-Identifier: BSD-3-Clause 5 | // 6 | // Engineer: fernando.garciaredondo@arm.com 7 | // pranay.prabhat@arm.com 8 | // 9 | // Arm LLGs model for PMA-MTJs, mtj lib 10 | 11 | simulator lang=spectre 12 | 13 | library mtj 14 | section tt 15 | include "mtj_subcircuit.scs" 16 | 17 | // other wrappers from pdk, needed 18 | endsection tt 19 | endlibrary mtj 20 | 21 | -------------------------------------------------------------------------------- /src/verilog_a_compact_model/mram_lib/mtj_structure.va: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Arm Ltd. 2 | // All rights reserved. 3 | // 4 | // SPDX-License-Identifier: BSD-3-Clause 5 | // 6 | // Engineer: fernando.garciaredondo@arm.com 7 | // pranay.prabhat@arm.com 8 | // 9 | // Arm MTJ structure, with generic conduction 10 | 11 | `ifndef _mtj_structure_ 12 | `define _mtj_structure_ 13 | 14 | `include "disciplines.vams" 15 | `include "constants.vams" 16 | 17 | // fl: free layer 18 | // pl: pinned layer 19 | 20 | // llg 21 | `include "llg_spherical_solver.va" 22 | 23 | // Behavioral Model for a single magnetic tunnel junction 24 | module mtj_subcircuit(pl, fl, mz, h_ext_x, h_ext_y, h_ext_z); 25 | 26 | 27 | // functions 28 | `include "vectorial_fn.va" 29 | // // constants 30 | `include "physical_constants.va" 31 | 32 | // parameters 33 | `include "llg_parameters.va" 34 | `include "dimension_parameters.va" 35 | `include "conductance_parameters.va" 36 | 37 | 38 | inout fl, pl; 39 | output mz; 40 | inout h_ext_x, h_ext_y, h_ext_z; 41 | 42 | electrical fl, pl; 43 | current i_mtj; 44 | voltage v_mtj, mz; 45 | electrical h_ext_x, h_ext_y, h_ext_z; 46 | 47 | // electrical variables 48 | real r_mtj; 49 | 50 | // seed 51 | integer seed; 52 | 53 | 54 | // sub modules instantiation 55 | // LLGs_va #(.p_do_thermal(p_do_thermal)) 56 | LLGs_va llgs (.i_mtj(i_mtj), 57 | .v_mtj(v_mtj), 58 | .v_m_z(mz), 59 | .h_ext_x(h_ext_x), 60 | .h_ext_y(h_ext_y), 61 | .h_ext_z(h_ext_z) ); 62 | 63 | 64 | analog function real get_r; 65 | // Julliere, M. (1975). 66 | // Tunneling between ferromagnetic films. 67 | // Physics Letters A, 54(3), 225–226. 68 | // https://doi.org/10.1016/0375-9601(75)90174-7 69 | 70 | // Lee, H., et.al. 71 | // Analysis and Compact Modeling of Magnetic Tunnel Junctions Utilizing 72 | // Voltage-Controlled Magnetic Anisotropy. 73 | // IEEE Transactions on Magnetics, 54(4). 74 | // https://doi.org/10.1109/TMAG.2017.2788010 75 | input m_z; 76 | real m_z; 77 | begin 78 | get_r = p_r_p*(1 + p_tmr/(p_tmr + 2))/(1 - p_tmr/(p_tmr + 2)*m_z); 79 | end 80 | endfunction 81 | 82 | 83 | analog begin : mtj_behav 84 | 85 | @(initial_step) begin 86 | $strobe("\**********************************************\n\n"); 87 | end 88 | 89 | 90 | // conduction 91 | r_mtj = get_r(V(mz)); 92 | I(i_mtj) <+ I(fl, pl); 93 | V(v_mtj) <+ V(fl, pl); 94 | 95 | // I(fl, pl) <+ V(fl, pl)/r_mtj; 96 | V(fl, pl) <+ I(fl, pl)*r_mtj; 97 | 98 | end 99 | 100 | endmodule 101 | 102 | `endif 103 | -------------------------------------------------------------------------------- /src/verilog_a_compact_model/mram_lib/mtj_subcircuit.scs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Arm Ltd. 2 | // All rights reserved. 3 | // 4 | // SPDX-License-Identifier: BSD-3-Clause 5 | // 6 | // Engineer: fernando.garciaredondo@arm.com 7 | // pranay.prabhat@arm.com 8 | // 9 | // Arm MTJ structure, with generic conduction, in a subcircuit form 10 | 11 | simulator lang=spectre 12 | 13 | ahdl_include "llg_spherical_solver.va" 14 | 15 | 16 | subckt mtj_subcircuit (pl fl mz h_ext_x h_ext_y h_ext_z) 17 | parameters p_do_thermal=0 p_do_theta_windowing=0 p_do_fake_thermal=0 p_atol=1e-6 18 | + p_theta_init=0.01414119 19 | + p_p=0.75 20 | + p_tmr = 2*p_p*p_p/(1-p_p*p_p) 21 | + p_r_p=6e3 22 | + p_r_contact = 2 23 | 24 | // llgs solver 25 | llgs ( aux_0 aux_1 mz h_ext_x h_ext_y h_ext_z) LLGs_va \ 26 | p_theta_init=p_theta_init \ 27 | p_do_thermal=p_do_thermal \ 28 | p_do_theta_windowing=p_do_theta_windowing \ 29 | p_do_fake_thermal=p_do_fake_thermal \ 30 | p_atol=p_atol 31 | 32 | i_aux (0 aux_0) bsource i=i(r_mtj_module) 33 | v_aux (aux_1 0) bsource v=v(fl, pl) 34 | r_aux (aux_0 0) resistor r=1 35 | 36 | // simple TMR based conduction 37 | r_mtj_module (fl pl) bsource r=p_r_p*(1 + p_tmr/(p_tmr + 2))/(1 - p_tmr/(p_tmr + 2)*V(mz)) 38 | 39 | ends mtj_subcircuit 40 | 41 | subckt mtj_subcircuit_no_hext (pl fl) 42 | parameters p_do_thermal=0 p_do_theta_windowing=0 p_do_fake_thermal=1 p_atol=1e-6 43 | + p_theta_init=0.01414119 44 | 45 | // __NO_H_EXT__ 46 | V_hext_x h_ext_x 0 vsource type=dc dc=0 47 | // __NO_H_EXT__ 48 | V_hext_y h_ext_y 0 vsource type=dc dc=0 49 | // __NO_H_EXT__ 50 | V_hext_z h_ext_z 0 vsource type=dc dc=0 51 | 52 | mtj (fl pl mz h_ext_x h_ext_y h_ext_z) mtj_subcircuit \ 53 | p_theta_init=p_theta_init \ 54 | p_do_thermal=p_do_thermal \ 55 | p_do_theta_windowing=p_do_theta_windowing \ 56 | p_do_fake_thermal=p_do_fake_thermal \ 57 | p_atol=p_atol 58 | ends mtj_subcircuit_no_hext 59 | -------------------------------------------------------------------------------- /src/verilog_a_compact_model/mram_lib/physical_constants.va: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Arm Ltd. 2 | // All rights reserved. 3 | // 4 | // SPDX-License-Identifier: BSD-3-Clause 5 | // 6 | // Engineer: fernando.garciaredondo@arm.com 7 | // pranay.prabhat@arm.com 8 | // 9 | // Arm physical constants 10 | 11 | `ifndef _physical_constants_ 12 | `define _physical_constants_ 13 | 14 | `include "disciplines.vams" 15 | `include "constants.vams" 16 | 17 | // // CONSTANTS 18 | // // gyromagnetic ratio 19 | // // https://www.ctcms.nist.gov/~rdm/mumag.org.html 20 | // // equivalent to c_U0*2.211e5 (ommited often [rad]) * [m/C] = ([rad]) * [ m/A/s] 21 | // `define c_GAMMA_0 1.76e11 // [rad/s/T] 22 | // 23 | // // elementary charge, [C] 24 | // `define c_E `P_Q // 1.602176462e-19 25 | // // reduced plank's constant [J s] 26 | // `define c_HBAR (`P_H/(2*`M_PI)) // 6.6260755e-34 / 2*PI 27 | // // vacuum permeability, 4 pi / 1e7 [H/m] 28 | // `define c_U0 (4*`M_PI/1e7) // 1.25663706212e-6 29 | // // boltzmann's constant [J/K] 30 | // `define c_KB 1.3806503e-23 31 | // // Borh magneton [J/T] 32 | // `define c_UB (c_GAMMA_0 * c_HBAR / 2) 33 | 34 | // gyromagnetic ratio 35 | // https://www.ctcms.nist.gov/~rdm/mumag.org.html 36 | // equivalent to c_U0*2.211e5 (ommited often [rad]) * [m/C] = ([rad]) * [ m/A/s] 37 | parameter real c_GAMMA_0 = 1.76e11; // [rad/s/T] 38 | // elementary charge, [C] 39 | parameter real c_E = `P_Q; // 1.602176462e-19 40 | // reduced plank's constant [J s] 41 | parameter real c_HBAR = `P_H/(2*`M_PI); // 6.6260755e-34 / 2*PI 42 | // vacuum permeability, 4 pi / 1e7 [H/m] 43 | parameter real c_U0 = 4*`M_PI/1e7; // 1.25663706212e-6 44 | // boltzmann's constant [J/K] 45 | parameter real c_KB = 1.3806503e-23; 46 | // Borh magneton [J/T] 47 | parameter real c_UB = c_GAMMA_0 * c_HBAR / 2; 48 | 49 | `endif 50 | -------------------------------------------------------------------------------- /src/verilog_a_compact_model/mram_lib/vectorial_fn.va: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Arm Ltd. 2 | // All rights reserved. 3 | // 4 | // SPDX-License-Identifier: BSD-3-Clause 5 | // 6 | // Engineer: fernando.garciaredondo@arm.com 7 | // pranay.prabhat@arm.com 8 | // 9 | // Support functions 10 | 11 | // `ifndef _vectorial_fn_ 12 | // `define _vectorial_fn_ 13 | 14 | analog function real sign; 15 | input x; 16 | real x; 17 | begin 18 | if (x >=0) begin 19 | sign=1.0; 20 | end 21 | else begin 22 | sign = -1.0; 23 | end 24 | end 25 | endfunction 26 | 27 | // compute cartesian dot product u * v 28 | analog function real udv_cart; 29 | input u_x,u_y,u_z,v_x,v_y,v_z; 30 | output d; 31 | real u_x, u_y, u_z, v_x, v_y, v_z, d; 32 | begin 33 | d = u_x*v_x + u_y * v_y + u_z * v_z; 34 | // dummy 35 | udv_cart = 0; 36 | end 37 | endfunction 38 | 39 | // compute cartesian vectorial product u^v 40 | analog function real uxv_cart; 41 | input u_x,u_y,u_z,v_x,v_y,v_z; 42 | output w_x, w_y, w_z; 43 | real u_x, u_y, u_z, v_x, v_y, v_z, w_x, w_y, w_z; 44 | begin 45 | w_x = (u_y*v_z - v_y*u_z); 46 | w_y = (-u_x*v_z + u_z*v_x); 47 | w_z = (u_x*v_y - u_y*v_x); 48 | // dummy 49 | uxv_cart = 0; 50 | end 51 | endfunction 52 | 53 | // transform cartesian vector to spherical 54 | analog function real cart_from_spherical; 55 | input rho, theta, phi; 56 | output x, y, z; 57 | real theta, phi, rho, x, y, z; 58 | begin 59 | x = rho * $sin(theta)*$cos(phi); 60 | y = rho * $sin(theta)*$sin(phi); 61 | z = rho * $cos(theta); 62 | // dummy 63 | cart_from_spherical = 0; 64 | end 65 | endfunction 66 | 67 | // compute differential unit vectors 68 | 69 | // Line element (length element) computation 70 | // Compute dr (theta component) 71 | // given infinitesimal displacement from rho, theta, phi. 72 | // note rho==1 73 | analog function real diff_unit_theta_vector; 74 | input m_sph_rho, m_sph_theta, m_sph_phi; 75 | output dr_theta_x, dr_theta_y, dr_theta_z; 76 | real m_sph_rho, m_sph_theta, m_sph_phi, 77 | dr_theta_x, dr_theta_y, dr_theta_z; 78 | begin 79 | dr_theta_x = $cos(m_sph_theta)*$cos(m_sph_phi); 80 | dr_theta_y = $cos(m_sph_theta)*$sin(m_sph_phi); 81 | dr_theta_z = -$sin(m_sph_theta); 82 | // dummy 83 | diff_unit_theta_vector = 0; 84 | 85 | end 86 | endfunction 87 | 88 | // Line element (length element) computation 89 | // Compute dr (phi component) 90 | // given infinitesimal displacement from rho, theta, phi. 91 | // note rho==1 92 | analog function real diff_unit_phi_vector; 93 | input m_sph_rho, m_sph_theta, m_sph_phi; 94 | output dr_phi_x, dr_phi_y, dr_phi_z; 95 | real m_sph_rho, m_sph_theta, m_sph_phi, 96 | dr_phi_x, dr_phi_y, dr_phi_z; 97 | begin 98 | dr_phi_x = -$sin(m_sph_phi); 99 | dr_phi_y = $cos(m_sph_phi); 100 | dr_phi_z = 0; 101 | // dummy 102 | diff_unit_phi_vector = 0; 103 | 104 | end 105 | endfunction 106 | 107 | // `endif 108 | -------------------------------------------------------------------------------- /src/verilog_a_compact_model/tb/tb.scs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Arm Ltd. 2 | // All rights reserved. 3 | // 4 | // SPDX-License-Identifier: BSD-3-Clause 5 | 6 | // example PMA MTJ testbench 7 | 8 | simulator lang=spectre 9 | global 0 10 | 11 | ahdl_include "../mram_lib/mtj_structure.va" 12 | 13 | 14 | parameters 15 | + i_dc=0e-6 16 | + i_pulse_ap2p=-35e-6 17 | + w_pulse=50e-9 18 | + input_delay=-2e-9 19 | + temp_celsius=26.85 20 | + do_thermal=0 21 | + do_theta_windowing=0 22 | + do_fake_thermal=0 23 | + sim_pivrel=0.1 24 | + sim_rtol=1e-4 25 | + sim_atol=1e-8 26 | + maxstep=1e-12 27 | + method=traponly 28 | // + method=gear2only 29 | // [noise] 30 | // + sim_rtol=1e-2 31 | // + sim_atol=1e-5 32 | 33 | 34 | // AP->P 35 | // IDC0 0 e0 isource type=dc dc=i_dc 36 | // (pl, fl) pl first to keep sm2tj compatibility 37 | ITRAN0 0 e0 isource type=pulse dc=i_dc val0=0 val1=-i_pulse_ap2p width=w_pulse rise=2n delay=input_delay 38 | // __H_EXT__ MTJ_ap2p (e0 0 h_ext_x h_ext_y h_ext_z)\ 39 | // __NO_H_EXT__ 40 | mtj_ap2p (e0 0 mz 0 0 0) mtj_subcircuit 41 | + p_do_thermal=do_thermal 42 | + p_do_theta_windowing=do_theta_windowing 43 | + p_do_fake_thermal=do_fake_thermal 44 | + p_atol=sim_atol 45 | 46 | 47 | //////////////////////////////////////////// 48 | // sim options 49 | highVoltageOptions options highvoltage=yes bin_relref=yes 50 | 51 | simulatorOptions options \ 52 | temp=(temp_celsius) \ 53 | tnom=25 scalem=1.0 scale=1.0 gmin=1e-12 rforce=1 maxnotes=5 maxwarns=5 \ 54 | digits=5 cols=80 \ 55 | reltol=sim_rtol \ 56 | // [noise] for noise sim 57 | // vabstol=1e-2 iabstol=1e-3 \ 58 | // others, not needed 59 | // vabstol=sim_atol iabstol=sim_atol \ 60 | // pivrel=sim_rtol \ 61 | //sensfile="../psf/sens.output" \ 62 | // checklimitdest=psf 63 | 64 | finalTimeOP info what=oppoint where=rawfile 65 | modelParameter info what=models where=rawfile 66 | element info what=inst where=rawfile 67 | outputParameter info what=output where=rawfile 68 | // designParamVals info what+ eters where=rawfile 69 | primitives info what=primitives where=rawfile 70 | subckts info what=subckts where=rawfile 71 | // save all options 72 | saveOptions options save=all subcktprobelvl=3 saveahdlvars=all exportfile="./exported_test.data" 73 | // save selected options 74 | // saveOptions options save=selected subcktprobelvl=3 saveahdlvars=selected exportfile="./exported_test.data" 75 | // required by export 76 | 77 | // parameters to adjust 78 | 79 | // simulate with traponly ++aps for calibrated model 80 | 81 | // tran_tt tran stop=(2*input_delay + w_pulse) write="spectre.ic" writefinal="spectre.fc" \ 82 | tran_tt tran stop=(50n) write="spectre.ic" writefinal="spectre.fc" \ 83 | annotate=status maxiters=5 \ 84 | method=method \ 85 | // maxstep=maxstep \ 86 | // step=maxstep \ 87 | -------------------------------------------------------------------------------- /src/verilog_a_compact_model/tb/tb_subckt.scs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021 Arm Ltd. 2 | // All rights reserved. 3 | // 4 | // SPDX-License-Identifier: BSD-3-Clause 5 | 6 | // example PMA MTJ testbench 7 | 8 | simulator lang=spectre 9 | global 0 10 | 11 | include "../mram_lib/mtj_subcircuit.scs" 12 | 13 | 14 | parameters 15 | + i_dc=0e-6 16 | + i_pulse_ap2p=-35e-6 17 | + w_pulse=50e-9 18 | + input_delay=-2e-9 19 | + temp_celsius=26.85 20 | + do_thermal=0 21 | + do_theta_windowing=0 22 | + do_fake_thermal=0 23 | + sim_pivrel=0.1 24 | + sim_rtol=1e-4 25 | + sim_atol=1e-8 26 | + maxstep=1e-12 27 | + method=traponly 28 | // + method=gear2only 29 | // [noise] 30 | // + sim_rtol=1e-2 31 | // + sim_atol=1e-5 32 | 33 | 34 | // AP->P 35 | // IDC0 0 e0 isource type=dc dc=i_dc 36 | // (pl, fl) pl first to keep sm2tj compatibility 37 | ITRAN0 0 e0 isource type=pulse dc=i_dc val0=0 val1=-i_pulse_ap2p width=w_pulse rise=2n delay=input_delay 38 | // __H_EXT__ MTJ_ap2p (e0 0 h_ext_x h_ext_y h_ext_z)\ 39 | // __NO_H_EXT__ 40 | mtj_ap2p (e0 0 mz 0 0 0) mtj_subcircuit 41 | + p_do_thermal=do_thermal 42 | + p_do_theta_windowing=do_theta_windowing 43 | + p_do_fake_thermal=do_fake_thermal 44 | + p_atol=sim_atol 45 | 46 | 47 | //////////////////////////////////////////// 48 | // sim options 49 | highVoltageOptions options highvoltage=yes bin_relref=yes 50 | 51 | simulatorOptions options \ 52 | temp=(temp_celsius) \ 53 | tnom=25 scalem=1.0 scale=1.0 gmin=1e-12 rforce=1 maxnotes=5 maxwarns=5 \ 54 | digits=5 cols=80 \ 55 | reltol=sim_rtol \ 56 | // [noise] for noise sim 57 | // vabstol=1e-2 iabstol=1e-3 \ 58 | // others, not needed 59 | // vabstol=sim_atol iabstol=sim_atol \ 60 | // pivrel=sim_rtol \ 61 | //sensfile="../psf/sens.output" \ 62 | // checklimitdest=psf 63 | 64 | finalTimeOP info what=oppoint where=rawfile 65 | modelParameter info what=models where=rawfile 66 | element info what=inst where=rawfile 67 | outputParameter info what=output where=rawfile 68 | // designParamVals info what+ eters where=rawfile 69 | primitives info what=primitives where=rawfile 70 | subckts info what=subckts where=rawfile 71 | // save all options 72 | saveOptions options save=all subcktprobelvl=3 saveahdlvars=all exportfile="./exported_test.data" 73 | // save selected options 74 | // saveOptions options save=selected subcktprobelvl=3 saveahdlvars=selected exportfile="./exported_test.data" 75 | // required by export 76 | 77 | // parameters to adjust 78 | 79 | // simulate with traponly ++aps for calibrated model 80 | 81 | // tran_tt tran stop=(2*input_delay + w_pulse) write="spectre.ic" writefinal="spectre.fc" \ 82 | tran_tt tran stop=(50n) write="spectre.ic" writefinal="spectre.fc" \ 83 | annotate=status maxiters=5 \ 84 | method=method \ 85 | // maxstep=maxstep \ 86 | // step=maxstep \ 87 | --------------------------------------------------------------------------------