mirror of https://github.com/VLSIDA/OpenRAM.git
RELEASE 1.0
This commit is contained in:
parent
bdcebad086
commit
f48272bde6
|
|
@ -0,0 +1,10 @@
|
|||
.DS_Store
|
||||
*.bak
|
||||
openram.pdf
|
||||
main.aux
|
||||
main.bbl
|
||||
main.blg
|
||||
main.log
|
||||
main.out
|
||||
main.pdf
|
||||
main.synctex.gz
|
||||
Binary file not shown.
|
|
@ -0,0 +1,11 @@
|
|||
#To create a PDF version of the paper, named: openram.pdf,
|
||||
#use this command: ./t.sh
|
||||
#
|
||||
#This script is used to correctly setup the references/bib
|
||||
#for the paper and uses pdflatex to create the PDF.
|
||||
#
|
||||
#DEBUGGING:
|
||||
#If the generation of the PDF failed, read the error messages
|
||||
#and line numbers and use these keyboard commands to get out
|
||||
#of the current error messages: Ctrl+D
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
\begin{abstract}
|
||||
Computer systems research is often inhibited by the availability of
|
||||
memory designs. Existing Process Design Kits (PDKs) frequently lack
|
||||
memory compilers, while expensive commercial solutions only provide
|
||||
memory models with immutable cells, limited configurations, and
|
||||
restrictive licenses. Manually creating memories can be time consuming
|
||||
and tedious and the designs are usually inflexible. This paper
|
||||
introduces OpenRAM, an open-source memory compiler, that provides a
|
||||
platform for the generation, characterization, and verification of
|
||||
fabricable memory designs across various technologies, sizes, and
|
||||
configurations. It enables research in computer architecture,
|
||||
system-on-chip design, memory circuit and device research, and
|
||||
computer-\allowbreak aided design.
|
||||
\end{abstract}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
\section{Acknowledgments}
|
||||
\label{sec:acknowledgements}
|
||||
This material is based upon work supported by the National Science
|
||||
Foundation under Grant No. CNS-1205685 and CNS-1205493. Many students
|
||||
have contributed to the project throughout their studies including
|
||||
Jeff Butera, Tom Golubev, Seokjoong Kim, Matthew Gaalswyk, and Son
|
||||
Bui.
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,221 @@
|
|||
%%
|
||||
%% This is file `acmcopyright.sty',
|
||||
%% generated with the docstrip utility.
|
||||
%%
|
||||
%% The original source files were:
|
||||
%%
|
||||
%% acmcopyright.dtx (with options: `style')
|
||||
%%
|
||||
%% IMPORTANT NOTICE:
|
||||
%%
|
||||
%% For the copyright see the source file.
|
||||
%%
|
||||
%% Any modified versions of this file must be renamed
|
||||
%% with new filenames distinct from acmcopyright.sty.
|
||||
%%
|
||||
%% For distribution of the original source see the terms
|
||||
%% for copying and modification in the file acmcopyright.dtx.
|
||||
%%
|
||||
%% This generated file may be distributed as long as the
|
||||
%% original source files, as listed above, are part of the
|
||||
%% same distribution. (The sources need not necessarily be
|
||||
%% in the same archive or directory.)
|
||||
%% \CharacterTable
|
||||
%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
|
||||
%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z
|
||||
%% Digits \0\1\2\3\4\5\6\7\8\9
|
||||
%% Exclamation \! Double quote \" Hash (number) \#
|
||||
%% Dollar \$ Percent \% Ampersand \&
|
||||
%% Acute accent \' Left paren \( Right paren \)
|
||||
%% Asterisk \* Plus \+ Comma \,
|
||||
%% Minus \- Point \. Solidus \/
|
||||
%% Colon \: Semicolon \; Less than \<
|
||||
%% Equals \= Greater than \> Question mark \?
|
||||
%% Commercial at \@ Left bracket \[ Backslash \\
|
||||
%% Right bracket \] Circumflex \^ Underscore \_
|
||||
%% Grave accent \` Left brace \{ Vertical bar \|
|
||||
%% Right brace \} Tilde \~}
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{acmcopyright}
|
||||
[2014/06/29 v1.2 Copyright statemens for ACM classes]
|
||||
\newif\if@printcopyright
|
||||
\@printcopyrighttrue
|
||||
\newif\if@printpermission
|
||||
\@printpermissiontrue
|
||||
\newif\if@acmowned
|
||||
\@acmownedtrue
|
||||
\RequirePackage{xkeyval}
|
||||
\define@choicekey*{ACM@}{acmcopyrightmode}[%
|
||||
\acm@copyrightinput\acm@copyrightmode]{none,acmcopyright,acmlicensed,%
|
||||
rightsretained,usgov,usgovmixed,cagov,cagovmixed,%
|
||||
licensedusgovmixed,licensedcagovmixed,othergov,licensedothergov}{%
|
||||
\@printpermissiontrue
|
||||
\@printcopyrighttrue
|
||||
\@acmownedtrue
|
||||
\ifnum\acm@copyrightmode=0\relax % none
|
||||
\@printpermissionfalse
|
||||
\@printcopyrightfalse
|
||||
\@acmownedfalse
|
||||
\fi
|
||||
\ifnum\acm@copyrightmode=2\relax % acmlicensed
|
||||
\@acmownedfalse
|
||||
\fi
|
||||
\ifnum\acm@copyrightmode=3\relax % rightsretained
|
||||
\@acmownedfalse
|
||||
\fi
|
||||
\ifnum\acm@copyrightmode=4\relax % usgov
|
||||
\@printpermissiontrue
|
||||
\@printcopyrightfalse
|
||||
\@acmownedfalse
|
||||
\fi
|
||||
\ifnum\acm@copyrightmode=6\relax % cagov
|
||||
\@acmownedfalse
|
||||
\fi
|
||||
\ifnum\acm@copyrightmode=8\relax % licensedusgovmixed
|
||||
\@acmownedfalse
|
||||
\fi
|
||||
\ifnum\acm@copyrightmode=9\relax % licensedcagovmixed
|
||||
\@acmownedfalse
|
||||
\fi
|
||||
\ifnum\acm@copyrightmode=10\relax % othergov
|
||||
\@acmownedtrue
|
||||
\fi
|
||||
\ifnum\acm@copyrightmode=11\relax % licensedothergov
|
||||
\@acmownedfalse
|
||||
\@printcopyrightfalse
|
||||
\fi}
|
||||
\def\setcopyright#1{\setkeys{ACM@}{acmcopyrightmode=#1}}
|
||||
\setcopyright{acmcopyright}
|
||||
\def\@copyrightowner{%
|
||||
\ifcase\acm@copyrightmode\relax % none
|
||||
\or % acmcopyright
|
||||
ACM.
|
||||
\or % acmlicensed
|
||||
Copyright held by the owner/author(s). Publication rights licensed to
|
||||
ACM.
|
||||
\or % rightsretained
|
||||
Copyright held by the owner/author(s).
|
||||
\or % usgov
|
||||
\or % usgovmixed
|
||||
ACM.
|
||||
\or % cagov
|
||||
Crown in Right of Canada.
|
||||
\or %cagovmixed
|
||||
ACM.
|
||||
\or %licensedusgovmixed
|
||||
Copyright held by the owner/author(s). Publication rights licensed to
|
||||
ACM.
|
||||
\or %licensedcagovmixed
|
||||
Copyright held by the owner/author(s). Publication rights licensed to
|
||||
ACM.
|
||||
\or % othergov
|
||||
ACM.
|
||||
\or % licensedothergov
|
||||
\fi}
|
||||
\def\@copyrightpermission{%
|
||||
\ifcase\acm@copyrightmode\relax % none
|
||||
\or % acmcopyright
|
||||
Permission to make digital or hard copies of all or part of this
|
||||
work for personal or classroom use is granted without fee provided
|
||||
that copies are not made or distributed for profit or commercial
|
||||
advantage and that copies bear this notice and the full citation on
|
||||
the first page. Copyrights for components of this work owned by
|
||||
others than ACM must be honored. Abstracting with credit is
|
||||
permitted. To copy otherwise, or republish, to post on servers or to
|
||||
redistribute to lists, requires prior specific permission
|
||||
and\hspace*{.5pt}/or a fee. Request permissions from
|
||||
permissions@acm.org.
|
||||
\or % acmlicensed
|
||||
Permission to make digital or hard copies of all or part of this
|
||||
work for personal or classroom use is granted without fee provided
|
||||
that copies are not made or distributed for profit or commercial
|
||||
advantage and that copies bear this notice and the full citation on
|
||||
the first page. Copyrights for components of this work owned by
|
||||
others than the author(s) must be honored. Abstracting with credit
|
||||
is permitted. To copy otherwise, or republish, to post on servers
|
||||
or to redistribute to lists, requires prior specific permission
|
||||
and\hspace*{.5pt}/or a fee. Request permissions from
|
||||
permissions@acm.org.
|
||||
\or % rightsretained
|
||||
Permission to make digital or hard copies of part or all of this work
|
||||
for personal or classroom use is granted without fee provided that
|
||||
copies are not made or distributed for profit or commercial advantage
|
||||
and that copies bear this notice and the full citation on the first
|
||||
page. Copyrights for third-party components of this work must be
|
||||
honored. For all other uses, contact the
|
||||
owner\hspace*{.5pt}/author(s).
|
||||
\or % usgov
|
||||
This paper is authored by an employee(s) of the United States
|
||||
Government and is in the public domain. Non-exclusive copying or
|
||||
redistribution is allowed, provided that the article citation is
|
||||
given and the authors and agency are clearly identified as its
|
||||
source.
|
||||
\or % usgovmixed
|
||||
ACM acknowledges that this contribution was authored or co-authored
|
||||
by an employee, or contractor of the national government. As such,
|
||||
the Government retains a nonexclusive, royalty-free right to
|
||||
publish or reproduce this article, or to allow others to do so, for
|
||||
Government purposes only. Permission to make digital or hard copies
|
||||
for personal or classroom use is granted. Copies must bear this
|
||||
notice and the full citation on the first page. Copyrights for
|
||||
components of this work owned by others than ACM must be
|
||||
honored. To copy otherwise, distribute, republish, or post,
|
||||
requires prior specific permission and\hspace*{.5pt}/or a
|
||||
fee. Request permissions from permissions@acm.org.
|
||||
\or % cagov
|
||||
This article was authored by employees of the Government of Canada.
|
||||
As such, the Canadian government retains all interest in the
|
||||
copyright to this work and grants to ACM a nonexclusive,
|
||||
royalty-free right to publish or reproduce this article, or to allow
|
||||
others to do so, provided that clear attribution is given both to
|
||||
the authors and the Canadian government agency employing them.
|
||||
Permission to make digital or hard copies for personal or classroom
|
||||
use is granted. Copies must bear this notice and the full citation
|
||||
on the first page. Copyrights for components of this work owned by
|
||||
others than the Canadain Government must be honored. To copy
|
||||
otherwise, distribute, republish, or post, requires prior specific
|
||||
permission and\hspace*{.5pt}/or a fee. Request permissions from
|
||||
permissions@acm.org.
|
||||
\or % cagovmixed
|
||||
ACM acknowledges that this contribution was co-authored by an
|
||||
affiliate of the national government of Canada. As such, the Crown
|
||||
in Right of Canada retains an equal interest in the copyright.
|
||||
Reprints must include clear attribution to ACM and the author's
|
||||
government agency affiliation. Permission to make digital or hard
|
||||
copies for personal or classroom use is granted. Copies must bear
|
||||
this notice and the full citation on the first page. Copyrights for
|
||||
components of this work owned by others than ACM must be honored.
|
||||
To copy otherwise, distribute, republish, or post, requires prior
|
||||
specific permission and\hspace*{.5pt}/or a fee. Request permissions
|
||||
from permissions@acm.org.
|
||||
\or % licensedusgovmixed
|
||||
Publication rights licensed to ACM. ACM acknowledges that this
|
||||
contribution was authored or co-authored by an employee, contractor
|
||||
or affiliate of the United States government. As such, the
|
||||
Government retains a nonexclusive, royalty-free right to publish or
|
||||
reproduce this article, or to allow others to do so, for Government
|
||||
purposes only.
|
||||
\or % licensedcagovmixed
|
||||
Publication rights licensed to ACM. ACM acknowledges that this
|
||||
contribution was authored or co-authored by an employee, contractor
|
||||
or affiliate of the national government of Canada. As such, the
|
||||
Government retains a nonexclusive, royalty-free right to publish or
|
||||
reproduce this article, or to allow others to do so, for Government
|
||||
purposes only.
|
||||
\or % othergov
|
||||
ACM acknowledges that this contribution was authored or co-authored
|
||||
by an employee, contractor or affiliate of a national government. As
|
||||
such, the Government retains a nonexclusive, royalty-free right to
|
||||
publish or reproduce this article, or to allow others to do so, for
|
||||
Government purposes only.
|
||||
\or % licensedothergov
|
||||
Publication rights licensed to ACM. ACM acknowledges that this
|
||||
contribution was authored or co-authored by an employee, contractor
|
||||
or affiliate of a national government. As such, the Government
|
||||
retains a nonexclusive, royalty-free right to publish or reproduce
|
||||
this article, or to allow others to do so, for Government purposes
|
||||
only.
|
||||
\fi}
|
||||
\endinput
|
||||
%%
|
||||
%% End of file `acmcopyright.sty'.
|
||||
|
|
@ -0,0 +1 @@
|
|||
\appendix
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
\section{Architecture}
|
||||
\label{sec:architecture}
|
||||
|
||||
% Overview of SRAM blocks
|
||||
The OpenRAM SRAM architecture is based on a bank of memory cells
|
||||
with peripheral circuits and control logic as illustrated in
|
||||
Figure~\ref{fig:structure}. These are further refined into eight major
|
||||
blocks: the bit-cell array, the address decoder, the word-line drivers,
|
||||
the column multiplexer, the pre-charge circuitry, the sense amplifier,
|
||||
the write drivers, and the control logic.
|
||||
|
||||
\begin{figure}[tb]
|
||||
\centering
|
||||
\includegraphics[width=8cm]{./figs/sram_structure.pdf}
|
||||
\caption{An OpenRAM SRAM consists of a bit-cell array along with decoder,
|
||||
reading and writing circuitry and control logic timed with a replica
|
||||
bit-line.
|
||||
\label{fig:structure}}
|
||||
\end{figure}
|
||||
|
||||
% we don't implement these yet, so don't give a tutorial on them
|
||||
%% General memories and Register Files (RF) are both examples of what an
|
||||
%% memory compiler can generate. General memories usually have shared
|
||||
%% read/write ports whereas RFs typically have separate ports. All of
|
||||
%% these options are permitted through the use of different types of
|
||||
%% memory cells such as 6, 8, and 12 transistor (T) cells which contains
|
||||
%% 1-4 access transistor pairs and their associated bit-lines. Some basic
|
||||
%% memory array options are available below:
|
||||
%% \begin{itemize}
|
||||
%% \setlength{\itemsep}{0pt} \setlength{\parskip}{0pt}
|
||||
%% \item Standard 6T cell for single-port memory
|
||||
%% \item Dual-port 8T cell for dual-port memory or separate read/write ports
|
||||
%% \item Four-port 12T cell for dual separate read/write ports
|
||||
%% \item Custom sense amplifier designs for different performances
|
||||
%% \item Different types of address decoders for different performances
|
||||
%% \end{itemize}
|
||||
|
||||
\begin{figure*}[tb]
|
||||
\centering
|
||||
\subfigure[Read operation timing]{
|
||||
\includegraphics[width = 8cm]{figs/timing_read.pdf}
|
||||
\label{fig:timing_read}}
|
||||
\subfigure[Write operation timing]{
|
||||
\includegraphics[width = 8cm]{figs/timing_write.pdf}
|
||||
\label{fig:timing_write}}
|
||||
\caption{OpenRAM uses a synchronous SRAM interface using a system
|
||||
clock (clk) along with control signals: output enable (OEb), chip
|
||||
select (CSb) and write enable (WEb).}
|
||||
\label{fig:timing}
|
||||
\end{figure*}
|
||||
|
||||
{\bf Bit-cell Array:} In the initial release of OpenRAM, the $6$T cell
|
||||
is the default memory cell because it is the most commonly used cell
|
||||
in SRAM devices. $6$T cells are tiled together with abutting word- and
|
||||
bit-lines to make up the memory array. The bit-cell array's aspect
|
||||
ratio is made as square as possible using multiple columns of data
|
||||
words. The memory cell is a custom designed library cell for each technology.
|
||||
Other types of memory cells, such as $7$T, $8$T, and $10$T cells, can be used
|
||||
as alternatives to the $6$T cell.
|
||||
|
||||
{\bf Address Decoder:} The address decoder takes the row address bits
|
||||
as inputs and asserts the appropriate word-line so that the correct
|
||||
memory cells can be read from or written to. The address decoder is
|
||||
placed to the left of the memory array and spans the array's vertical
|
||||
length. Different types of decoders can be used such as an included
|
||||
dynamic NAND decoder, but OpenRAM's default option is a hierarchical CMOS
|
||||
decoder.
|
||||
|
||||
{\bf Word-Line Driver:} Word-line drivers are inserted between the
|
||||
address decoder and the memory array as buffers. The word-line drivers
|
||||
are sized based on the width of the memory array so that they can drive
|
||||
the row select signal across the bit-cell array.
|
||||
|
||||
{\bf Column Multiplexer:} The column multiplexer is an optional block
|
||||
that uses the lower address bits to select the associated word in a
|
||||
row. The column mux is dynamically generated and can be omitted or can
|
||||
have 2 or 4 inputs. Larger column muxes are possible, but are not
|
||||
frequently used in memories. There are options for a multi-level tree
|
||||
mux as well.
|
||||
|
||||
{\bf Bit-line Precharge:} This circuitry pre-charges
|
||||
the bit-lines during the first phase of the clock for read
|
||||
operations. The precharge circuit is placed on top of every column in
|
||||
the memory array and equalizes the bit-line voltages so that the
|
||||
sense amplifier can sense the voltage difference between the two
|
||||
bit-lines.
|
||||
|
||||
{\bf Sense Amplifier:} A differential sense amplifier is used to sense
|
||||
the voltage difference between the bit-lines of a memory cell while a
|
||||
read operation is performed. The sense amplifier uses a bit-line
|
||||
isolation technique to increase performance. The sense amplifier
|
||||
circuitry is placed below the column multiplexer or the memory
|
||||
array if no column multiplexer is used. There is one sense amplifier for
|
||||
each output bit.
|
||||
|
||||
{\bf Write Driver:} The write drivers send the input data signals onto the
|
||||
bit-lines for a write operation. The write drivers are tri-stated
|
||||
so that they can be placed between the column multiplexer/memory array
|
||||
and the sense amplifiers. There is one write driver for each input
|
||||
data bit.
|
||||
|
||||
%% \subsubsection{Bit-cell and Bit-cell Array}
|
||||
%% A bit-cell class is provided to instantiate the custom designed memory
|
||||
%% cell located in the technology directory. Then the bit-cell array class
|
||||
%% will take the single bit-cell instance to dynamically generate the
|
||||
%% memory array. Using the functionality of GdsMill, we can rotate and/or
|
||||
%% mirror an instance. Doing so, will allow us to abut the power rails.
|
||||
|
||||
%% \subsubsection{Address Decoder}
|
||||
%% The hierarchical decoder is the default row address decoder that is
|
||||
%% used in OpenRAM. The hierarchical decoder is dynamically generated
|
||||
%% using the inverter and NAND gates with the help of basic shapes. The
|
||||
%% height of each decoder row will match the height of the memory cell so
|
||||
%% that the power rails can be abutted. OpenRAM also provides a NAND
|
||||
%% decoder as an alternative. NAND decoder uses NMOS and PMOS transistors
|
||||
%% created by ptx class. User can define type of the decoder in the
|
||||
%% configuration file.
|
||||
|
||||
%% \subsubsection{Word-line Driver}
|
||||
%% The word-line driver will be a column of alternating "mirrored"
|
||||
%% inverters instances that is used to drive the signal to access the
|
||||
%% memory cells in the row. The inverters will be sized accordingly
|
||||
%% depending on the size of the memory array.
|
||||
|
||||
%% \subsubsection{Column Multiplexer}
|
||||
%% The column multiplexer is an optional block that is used depending on
|
||||
%% the size of the memory array. By generating an instance of a 1-1
|
||||
%% multiplexer, we can then tile them to create bigger multiplexers such
|
||||
%% as 2-1, 4-1, etc. OpenRAM has two options for column multiplexing.
|
||||
%% Single-level-column-mux is the default column multiplexer but user can
|
||||
%% choose Tree-Column-Mux in configuration file. Both multiplexers use
|
||||
%% transistors created by ptx class.
|
||||
|
||||
%% \subsubsection{Precharge and Precharge Array}
|
||||
%% The precharge circuitry is dynamically generated using the transistor
|
||||
%% instances and various basic shapes. The precharge class dynamically
|
||||
%% generates an instance for a single column. The precharge array class
|
||||
%% takes that instance and tiles them horizontally to match the number of
|
||||
%% columns in the memory array. The width of the precharge cell is
|
||||
%% determined by the width of the user-created memory cell.
|
||||
|
||||
%% \subsubsection{Sense Amplifier and Sense Amplifier Array}
|
||||
%% The sense amplifier is user-designed analog circuit that is placed in
|
||||
%% the technology directory. The sense amplifier class instantiates the
|
||||
%% library cell and the sense amplifier array takes that instance to
|
||||
%% create a horizontal array matching the number of output bits for the
|
||||
%% memory. When designing this library cell, the user should match this
|
||||
%% cell's width and bit-lines to the memory cell's.
|
||||
|
||||
%% \subsubsection{Write Driver and Write Driver Array}
|
||||
%% Similar to the precharge classes, the write driver class will generate
|
||||
%% an instance for a single bit and the write driver array will tile them
|
||||
%% horizontally to match the number of input bits for the memory. The
|
||||
%% write drivers will be dynamically sized accordingly based on the size
|
||||
%% of the memory array.
|
||||
|
||||
%% \subsubsection{Control Logic}
|
||||
%% There will be a control logic module that will arrange the
|
||||
%% master-slave flip-flops and the logic associated with the control
|
||||
%% signals into a single design. Flip-flops are used to drive the control
|
||||
%% signals and standard library cells such as NAND and NOR gates will be
|
||||
%% used for the logic. A RBL is also generated using parameterized gates
|
||||
%% and Replica Cell (RC). RC is a 6T SRAM memory cell which is hard-wired
|
||||
%% to store a zero in order to discharge the RBL and generate the sense
|
||||
%% amplifier enable signal in read mode.
|
||||
|
||||
%% \subsubsection{Additional Arrays}
|
||||
%% In addition to the eight main blocks, there are helper modules that
|
||||
%% help simplify the designs in the eight main blocks. We have a
|
||||
%% flip-flop array class that takes the custom designed master-slave
|
||||
%% flip-flop library cell to create a tiled array. We also have the
|
||||
%% tri-state array class that will generate the array of tri-states for
|
||||
%% the DATA bus.
|
||||
|
||||
% Overview of signal inputs and timing
|
||||
{\bf Control Logic:} The OpenRAM SRAM architecture incorporates a
|
||||
standard synchronous memory interface using a system clock (clk). The
|
||||
control logic uses an externally provided, active-low output enable
|
||||
(OEb), chip select (CSb), and write enable (WEb) to combine multiple
|
||||
SRAMs into a larger structure. Internally, the OpenRAM compiler can
|
||||
have $1$, $2$, or $4$ memory banks to amortize the area/power cost of
|
||||
control logic and peripheral circuitry.
|
||||
|
||||
All of the input control signals are stored using master-slave (MS)
|
||||
flip-flops (FF) to ensure that the signals are valid for the entire
|
||||
clock cycle. During a read operation, data is available after the
|
||||
negative clock edge (second half of cycle) as shown in
|
||||
Figure~\ref{fig:timing_read}. To avoid dead cycles which degrade
|
||||
performance, a Zero Bus Turn-around (ZBT) technique is used in OpenRAM
|
||||
timing. The ZBT enables higher memory throughput since there are no
|
||||
wait states. During ZBT writes, data is set up before the negative
|
||||
clock edge and is captured on the negative edge. Figure~\ref{fig:timing_write}
|
||||
shows the timing for input signals during the write operation.
|
||||
|
||||
The internal control signals are generated using a replica bit-line (RBL)
|
||||
structure for the timing of the sense amplifier enable and output
|
||||
data storage~\cite{RBL:1998}. The RBL turns on the sense amplifiers
|
||||
at the exact time in presence of process variability in sub-$100$nm
|
||||
technologies.
|
||||
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
\section{Background}
|
||||
\label{sec:background}
|
||||
% brief origin/background of memory compilers
|
||||
|
||||
% Existence of memory compilers from the beginning
|
||||
Memory compilers have been used in Electronic Design Automation (EDA)
|
||||
design flows to reduce the design
|
||||
time long before contemporary
|
||||
compilers~\cite{broderson:sicompiler,johannsen:blocks}.
|
||||
However, these compilers were generally not portable as they were
|
||||
nothing more
|
||||
than quick scripts to aid designers. Porting to a new technology
|
||||
essentially required rewriting the scripts. However, the increase in
|
||||
design productivity when porting designs between technologies has led to
|
||||
more research on memory array
|
||||
compilers~\cite{cabe:flexible,huang:array,poechmueller:array,Xu:2007}.
|
||||
|
||||
% Reason why compilers evolved to today's current version
|
||||
As technology entered the Deep Sub-Micron (DSM) era, memory designs
|
||||
became one of the most challenging parts of circuit design
|
||||
due to decreasing static noise margins (SNM), increasing fabrication
|
||||
variability, and increasing leakage power consumption.
|
||||
This increased the complexity of memory compilers dramatically as they had to
|
||||
adapt to the ever-changing technologies. Simultaneously, design
|
||||
methodologies shifted from silicon compilers to standard cell place
|
||||
and route methods which required large optimized libraries. During
|
||||
this time, industry began using third-party suppliers of standard cell
|
||||
libraries and memory compilers that allowed their reuse to amortize
|
||||
development costs. These next-generation memory compilers provided
|
||||
silicon-verification that allowed designers to focus on their new
|
||||
design contribution rather than time-consuming tasks such as memory
|
||||
generation.
|
||||
|
||||
% Commercial industry memory compilers' description and cost
|
||||
Contemporary memory compilers have been widely used by industry, but
|
||||
the internal operation is typically hidden. Several prominent
|
||||
companies and foundries have provided memory compilers to their
|
||||
customers. These memory compilers usually allow customers to view
|
||||
front-end simulation, timing/power values, and pin locations after a
|
||||
license agreement is signed. Back-end features such as layout are
|
||||
normally supplied directly to the fab and are only given to the user
|
||||
for a licensing fee.
|
||||
|
||||
% Examples of commercial compilers' drawbacks
|
||||
Specifically, Global Foundries offers front-end PDKs for free, but not
|
||||
back-end detailed views~\cite{globalfoundries:2015}. Faraday
|
||||
Technologies provides a \enquote{black box} design kit where users do
|
||||
not know the details of the internal memory
|
||||
design~\cite{faraday:2015}. Dolphin Technology offers closed-source
|
||||
compilers which can create RAMs, ROMs, and CAMs for a variety of
|
||||
technologies~\cite{dolphin:2015}. The majority of these commercial
|
||||
compilers do not allow the customer to alter the base design, are
|
||||
restricted by the company's license, and usually require a fee. This
|
||||
makes them virtually unavailable and not useful for many academic
|
||||
research projects.
|
||||
|
||||
% Describe the problem (no free open-source that is widely distributed)
|
||||
In addition to memory compilers provided by industry, various research
|
||||
groups have released scripts to generate memories. However, these
|
||||
designs are not silicon verified and are usually only composed of
|
||||
simple structures. For example, FabMem is able to
|
||||
create small arrays, but it is highly dependent on the Cadence design
|
||||
tools~\cite{fabmem:2010}. The scripts do not provide any characterization capability
|
||||
and cannot easily integrate with commercial place and route tools.
|
||||
|
||||
% Industry's attempt to provide academia a memory compiler
|
||||
Another recent, promising solution for academia is the Synopsys
|
||||
Generic Memory Compiler (GMC)~\cite{Goldman:2014}. The software is
|
||||
provided with sample generic libraries such as Synopsys' $32$/$28$nm and
|
||||
$90$nm abstract technologies and can generate the entire SRAM for these
|
||||
technologies. The GMC generates GDSII layout data, SPICE netlists,
|
||||
Verilog and VHDL models, timing/power libraries, and DRC/LVS
|
||||
verification reports. GMC, however, is not recommended for
|
||||
fabrication since the technologies it supports are not real. Its sole
|
||||
purpose is to aid students in VLSI courses to learn about using
|
||||
memories in design flows.
|
||||
|
||||
% Academia's' attempts at a memory compiler
|
||||
There have been multiple attempts by academia to implement a memory
|
||||
compiler that is not restricted: the Institute of
|
||||
Microelectronics' SRAM IP Compiler~\cite{Xu:2007}, School of
|
||||
Electronic Science and Engineering at Southeast University's Memory IP
|
||||
Compiler~\cite{Chen:2012}, and Tsinghua University's Low Power SRAM
|
||||
Compiler~\cite{Wu:2010}. These are all methodologies and design flows
|
||||
for a memory compiler, but there are no public releases.
|
||||
|
||||
% State what we are looking for in academia. -- duplicate from introduction
|
||||
%% With all these attempts, there still isn't a complete solution for
|
||||
%% academia's research needs. Researchers need a memory compiler that is
|
||||
%% open-source, platform- and tool-portable, technology independent, and
|
||||
%% can generate fabricable memory designs.
|
||||
|
||||
|
||||
Binary file not shown.
|
|
@ -0,0 +1,21 @@
|
|||
\section{Conclusions}
|
||||
\label{sec:conclusions}
|
||||
This paper introduced OpenRAM, an open-source and portable memory
|
||||
compiler. OpenRAM generates the circuit, functional model, and layout
|
||||
of variable-sized SRAMs. In addition, a memory characterizer
|
||||
provides synthesis timing/power models.
|
||||
|
||||
The main motivation behind OpenRAM is to promote and simplify
|
||||
memory-related research in academia. Since OpenRAM is open-sourced,
|
||||
flexible, and portable, this memory compiler can be adapted to various
|
||||
technologies and is easily modified to address specific design
|
||||
requirements. Therefore, OpenRAM provides a platform to implement and test
|
||||
new memory designs.
|
||||
|
||||
Designs are currently being fabricated to test designs using the
|
||||
OpenRAM framework in SCMOS. We are also continuously introducing new
|
||||
features, such as non-6T memories, variability characterization,
|
||||
word-line segmenting, characterization speed-up, and a graphical user
|
||||
interface (GUI). We hope to engage an active community in the future
|
||||
development of OpenRAM.
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
psfile=${1%.pdf}.ps
|
||||
gs -dSAFER -dNOPLATFONTS -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sPAPERSIZE=letter -dCompatibilityLevel=1.4 -dPDFSETTINGS=/printer -dCompatibilityLevel=1.4 -dMaxSubsetPct=100 -dSubsetFonts=true -dEmbedAllFonts=true -sOutputFile=temp.pdf -f $1
|
||||
mv temp.pdf $1
|
||||
pdf2ps $1 $psfile
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
|
@ -0,0 +1,55 @@
|
|||
clc;
|
||||
|
||||
X = [ 2 , 8 , 32, 128];
|
||||
Y1_Freepdk =[0.0048413475625; 0.0135007585125;0.051075243575; 0.283865472];
|
||||
Y2_Freepdk=[0.0048094391125; 0.0135007585125; 0.0435568917; 0.2118510735];
|
||||
Y3_Freepdk=[0.0047775306625; 0.0129289229375; 0.0419903161125; 0.156997489175];
|
||||
Y4_Freepdk=[0.0052897701375; 0.0128789376875; 0.0419019176625; 0.1512635205];
|
||||
|
||||
|
||||
Y1_SCN3ME =[0.75276018; 2.08835631; 7.89312366; 40.6931238];
|
||||
Y2_SCN3ME =[0.74817639; 2.0216187; 6.804401625; 31.6371744];
|
||||
Y3_SCN3ME =[0.7435926; 2.01449475; 6.62959215; 24.64420014];
|
||||
Y4_SCN3ME =[0.83660283; 2.0073708; 6.61707036; 23.839544025];
|
||||
|
||||
Y1_T_Freepdk =[0.861; 1.32; 1.8; 2.2];
|
||||
Y2_T_Freepdk =[1.02; 1.33; 1.83; 2.6];
|
||||
Y3_T_Freepdk =[0.86; 1.5; 1.9; 6.75];
|
||||
Y4_T_Freepdk =[1.076; 1.34; 2.01; 9.86];
|
||||
|
||||
Y1_T_SCN3ME =[9.42; 8.25; 11.69; 12.7];
|
||||
Y2_T_SCN3ME =[11.82; 16.04; 14.7; 18.21];
|
||||
Y3_T_SCN3ME =[14.81; 19.24; 23.82; 30.25];
|
||||
Y4_T_SCN3ME =[22.9; 23.12; 30.75; 44.95];
|
||||
|
||||
|
||||
subplot(4,1,1)
|
||||
plot (X, Y1_Freepdk, X, Y2_Freepdk, X, Y3_Freepdk, X, Y4_Freepdk,'LineWidth',2);
|
||||
grid on;
|
||||
ylabel('Area (mm^2)','FontSize',12, 'Color','k');
|
||||
xlabel('Total Size (Kbits)','FontSize',12, 'Color','k');
|
||||
subplot(4,1,2)
|
||||
plot (X, Y1_SCN3ME, X, Y2_SCN3ME, X, Y3_SCN3ME, X, Y4_SCN3ME,'LineWidth',2);
|
||||
grid on;
|
||||
ylabel('Area (mm^2)','FontSize',12, 'Color','k');
|
||||
xlabel('Total Size (Kbits)','FontSize',12, 'Color','k');
|
||||
subplot(4,1,3)
|
||||
plot (X, Y1_T_Freepdk, X, Y2_T_Freepdk, X, Y3_T_Freepdk, X, Y4_T_Freepdk,'LineWidth',2);
|
||||
grid on;
|
||||
ylabel('Access time (ns)','FontSize',12, 'Color','k');
|
||||
xlabel('Total Size (Kbits)','FontSize',12, 'Color','k');
|
||||
subplot(4,1,4)
|
||||
plot (X, Y1_T_SCN3ME, X, Y2_T_SCN3ME, X, Y3_T_SCN3ME, X, Y4_T_SCN3ME,'LineWidth',2);
|
||||
ylabel('Access time (ns)','FontSize',12, 'Color','k');
|
||||
xlabel('Total Size (Kbits)','FontSize',12, 'Color','k');
|
||||
|
||||
|
||||
grid on;
|
||||
legend({'16-bit word size', '32-bit word size','64-bit word size', '128-bit word size'},'Location','northwest','orientation', 'vertical' , 'FontSize',12, 'LineWidth',1.2);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,14 @@
|
|||
set terminal pdf dashed
|
||||
set output "../Freepdk_Area.pdf"
|
||||
set palette color
|
||||
set xlabel "Total Size (Kbit)"
|
||||
set ylabel "Area (mm^2)"
|
||||
set key below
|
||||
plot 'freepdk45_size.dat' using ($1/1024):($2/1e6) with line title '16-bit word' lt 0 lw 5 lc 0 ,\
|
||||
'freepdk45_size.dat' using ($1/1024):($2/1e6) with points title '' lt 0 lw 5 lc 0 ,\
|
||||
'freepdk45_size.dat' using ($1/1024):($3/1e6) with line title '32-bit word' lt 1 lw 5 lc 1 ,\
|
||||
'freepdk45_size.dat' using ($1/1024):($3/1e6) with points title '' lt 1 lw 5 lc 1 ,\
|
||||
'freepdk45_size.dat' using ($1/1024):($4/1e6) with line title '64-bit word' lt 2 lw 5 lc 2 ,\
|
||||
'freepdk45_size.dat' using ($1/1024):($4/1e6) with points title '' lt 2 lw 5 lc 2 ,\
|
||||
'freepdk45_size.dat' using ($1/1024):($5/1e6) with line title '128-bit word' lt 3 lw 5 lc 3 ,\
|
||||
'freepdk45_size.dat' using ($1/1024):($5/1e6) with points title '' lt 3 lw 5 lc 3
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
2048 4841.3475625 4809.4391125 4777.5306625 5289.7701375
|
||||
8192 13500.7585125 12978.9081875 12928.9229375 12878.9376875
|
||||
#16384 25605.7869 22997.2777125 22908.8792625 22820.4808125
|
||||
32768 51075.243575 43556.8917 41990.3161125 41901.9176625
|
||||
#65536 142381.5903 86382.658775 79459.1013 79292.00325
|
||||
131072 283865.472 211851.0735 156997.489175 151263.5205
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
set terminal pdf dashed
|
||||
set output "../Scn3me_Area.pdf"
|
||||
set palette color
|
||||
set xlabel "Total Size (Kbit)"
|
||||
set ylabel "Area (mm^2)"
|
||||
set key below
|
||||
plot 'scn3me_size.dat' using ($1/1024):($2/1e6) with line title '16-bit word' lt 0 lw 5 lc 0 ,\
|
||||
'scn3me_size.dat' using ($1/1024):($2/1e6) with points title '' lt 0 lw 5 lc 0 ,\
|
||||
'scn3me_size.dat' using ($1/1024):($3/1e6) with line title '32-bit word' lt 1 lw 5 lc 1 ,\
|
||||
'scn3me_size.dat' using ($1/1024):($3/1e6) with points title '' lt 1 lw 5 lc 1 ,\
|
||||
'scn3me_size.dat' using ($1/1024):($4/1e6) with line title '64-bit word' lt 2 lw 5 lc 2 ,\
|
||||
'scn3me_size.dat' using ($1/1024):($4/1e6) with points title '' lt 2 lw 5 lc 2 ,\
|
||||
'scn3me_size.dat' using ($1/1024):($5/1e6) with line title '128-bit word' lt 3 lw 5 lc 3 ,\
|
||||
'scn3me_size.dat' using ($1/1024):($5/1e6) with points title '' lt 3 lw 5 lc 3
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
2048 752760.18 748176.39 743592.6 836602.83
|
||||
8192 2088356.31 2021618.7 2014494.75 2007370.8
|
||||
32768 7893123.66 6804401.625 6629592.15 6617070.36
|
||||
131072 40693123.8 31637174.4 24644200.14 23839544.025
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
#!/usr/bin/gnuplot
|
||||
#
|
||||
# Demonstration of a common use scenario of the multiplot environment.
|
||||
#
|
||||
# AUTHOR: Hagen Wierstorf
|
||||
#
|
||||
|
||||
reset
|
||||
|
||||
set terminal pdf dashed size 8cm,12cm
|
||||
set output "Results.pdf"
|
||||
set palette color
|
||||
|
||||
unset key
|
||||
|
||||
# Enable the use of macros
|
||||
set macros
|
||||
|
||||
# MACROS
|
||||
# Margins for each row resp. column
|
||||
# top of top fig, bottom of top fig
|
||||
TMARGIN = "set tmargin at screen 0.9; set bmargin at screen 0.575"
|
||||
# top of lower fig, bottom of lower fig
|
||||
BMARGIN = "set tmargin at screen 0.525; set bmargin at screen 0.15"
|
||||
# left of left fig, right of left fig
|
||||
LMARGIN = "set lmargin at screen 0.1; set rmargin at screen 0.48"
|
||||
# left point of right fig ,right most
|
||||
RMARGIN = "set lmargin at screen 0.52; set rmargin at screen 0.9"
|
||||
|
||||
# Placement of the a,b,c,d labels in the graphs
|
||||
POSA = "at graph 0.6,0.2 font ',5'"
|
||||
POSB = "at graph 0.5,0.2 font ',5'"
|
||||
|
||||
### Start multiplot (2x2 layout)
|
||||
set multiplot layout 4,1
|
||||
# --- GRAPH a
|
||||
set key outside center vertical top box 3
|
||||
set lmargin at screen 0.2; set rmargin at screen 0.9
|
||||
set tmargin at screen 0.88; set bmargin at screen 0.68
|
||||
#@TMARGIN; @LMARGIN
|
||||
#@NOXTICS; @YTICS
|
||||
set label 1 '45nm Area' @POSA
|
||||
set ylabel "mm^2"
|
||||
plot 'density_data/freepdk45_size.dat' using ($1/1024):($2/1e6) with line axis x1y1 title '16-bit word size' lt 0 lw 5 lc 0 ,\
|
||||
'density_data/freepdk45_size.dat' using ($1/1024):($2/1e6) with points axis x1y1 title '' lt 0 lw 5 lc 0 ,\
|
||||
'density_data/freepdk45_size.dat' using ($1/1024):($3/1e6) with line axis x1y1 title '32-bit word size' lt 1 lw 5 lc 1 ,\
|
||||
'density_data/freepdk45_size.dat' using ($1/1024):($3/1e6) with points axis x1y1 title '' lt 1 lw 5 lc 1 ,\
|
||||
'density_data/freepdk45_size.dat' using ($1/1024):($4/1e6) with line axis x1y1 title '64-bit word size' lt 2 lw 5 lc 2 ,\
|
||||
'density_data/freepdk45_size.dat' using ($1/1024):($4/1e6) with points axis x1y1 title '' lt 2 lw 5 lc 2 ,\
|
||||
'density_data/freepdk45_size.dat' using ($1/1024):($5/1e6) with line axis x1y1 title '128-bit word size' lt 3 lw 5 lc 3 ,\
|
||||
'density_data/freepdk45_size.dat' using ($1/1024):($5/1e6) with points axis x1y1 title '' lt 3 lw 5 lc 3
|
||||
|
||||
# --- GRAPH b
|
||||
unset key
|
||||
set tmargin at screen 0.68; set bmargin at screen 0.48
|
||||
#@TMARGIN; @RMARGIN
|
||||
#@NOXTICS; @NOYTICS
|
||||
set label 1 '180nm Area' @POSA
|
||||
set ylabel "mm^2"
|
||||
plot 'density_data/scn3me_size.dat' using ($1/1024):($2/1e6) with line axis x1y1 title '16-bit word size' lt 0 lw 5 lc 0 ,\
|
||||
'density_data/scn3me_size.dat' using ($1/1024):($2/1e6) with points axis x1y1 title '' lt 0 lw 5 lc 0 ,\
|
||||
'density_data/scn3me_size.dat' using ($1/1024):($3/1e6) with line axis x1y1 title '32-bit word size' lt 1 lw 5 lc 1 ,\
|
||||
'density_data/scn3me_size.dat' using ($1/1024):($3/1e6) with points axis x1y1 title '' lt 1 lw 5 lc 1 ,\
|
||||
'density_data/scn3me_size.dat' using ($1/1024):($4/1e6) with line axis x1y1 title '64-bit word size' lt 2 lw 5 lc 2 ,\
|
||||
'density_data/scn3me_size.dat' using ($1/1024):($4/1e6) with points axis x1y1 title '' lt 2 lw 5 lc 2 ,\
|
||||
'density_data/scn3me_size.dat' using ($1/1024):($5/1e6) with line axis x1y1 title '128-bit word size' lt 3 lw 5 lc 3 ,\
|
||||
'density_data/scn3me_size.dat' using ($1/1024):($5/1e6) with points axis x1y1 title '' lt 3 lw 5 lc 3
|
||||
|
||||
# --- GRAPH c
|
||||
set tmargin at screen 0.48; set bmargin at screen 0.28
|
||||
#@BMARGIN; @LMARGIN
|
||||
#@XTICS; @YTICS
|
||||
set label 1 '45nm Access time' @POSB
|
||||
set ylabel "ns"
|
||||
plot 'timing_data/freepdk45_timing.dat' using ($1/1024):2 with line axis x1y2 title '16-bit word' lt 0 lw 5 lc 0 ,\
|
||||
'timing_data/freepdk45_timing.dat' using ($1/1024):2 with points axis x1y2 title '' lt 0 lw 5 lc 0 ,\
|
||||
'timing_data/freepdk45_timing.dat' using ($1/1024):3 with line axis x1y2 title '32-bit word' lt 1 lw 5 lc 1 ,\
|
||||
'timing_data/freepdk45_timing.dat' using ($1/1024):3 with points axis x1y2 title '' lt 1 lw 5 lc 1 ,\
|
||||
'timing_data/freepdk45_timing.dat' using ($1/1024):4 with line axis x1y2 title '64-bit word' lt 2 lw 5 lc 2 ,\
|
||||
'timing_data/freepdk45_timing.dat' using ($1/1024):4 with points axis x1y2 title '' lt 2 lw 5 lc 2 ,\
|
||||
'timing_data/freepdk45_timing.dat' using ($1/1024):5 with line axis x1y2 title '128-bit word' lt 3 lw 5 lc 3 ,\
|
||||
'timing_data/freepdk45_timing.dat' using ($1/1024):5 with points axis x1y2 title '' lt 3 lw 5 lc 3
|
||||
|
||||
# --- GRAPH d
|
||||
set tmargin at screen 0.28; set bmargin at screen 0.08
|
||||
#@BMARGIN; @RMARGIN
|
||||
#@XTICS; @NOYTICS
|
||||
set ylabel "ns"
|
||||
set xlabel "Total Size (Kbits)"
|
||||
set label 1 '180nm Access time' @POSB
|
||||
plot 'timing_data/scn3me_timing.dat' using ($1/1024):2 with line axis x1y2 title '16-bit word' lt 0 lw 5 lc 0 ,\
|
||||
'timing_data/scn3me_timing.dat' using ($1/1024):2 with points axis x1y2 title '' lt 0 lw 5 lc 0 ,\
|
||||
'timing_data/scn3me_timing.dat' using ($1/1024):3 with line axis x1y2 title '32-bit word' lt 1 lw 5 lc 1 ,\
|
||||
'timing_data/scn3me_timing.dat' using ($1/1024):3 with points axis x1y2 title '' lt 1 lw 5 lc 1 ,\
|
||||
'timing_data/scn3me_timing.dat' using ($1/1024):4 with line axis x1y2 title '64-bit word' lt 2 lw 5 lc 2 ,\
|
||||
'timing_data/scn3me_timing.dat' using ($1/1024):4 with points axis x1y2 title '' lt 2 lw 5 lc 2 ,\
|
||||
'timing_data/scn3me_timing.dat' using ($1/1024):5 with line axis x1y2 title '128-bit word' lt 3 lw 5 lc 3 ,\
|
||||
'timing_data/scn3me_timing.dat' using ($1/1024):5 with points axis x1y2 title '' lt 3 lw 5 lc 3
|
||||
unset multiplot
|
||||
### End multiplot
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,866 @@
|
|||
%!PS-Adobe-3.0 EPSF-3.0
|
||||
%%Creator: cairo 1.8.8 (http://cairographics.org)
|
||||
%%CreationDate: Mon Oct 17 14:59:32 2011
|
||||
%%Pages: 1
|
||||
%%BoundingBox: 0 0 630 399
|
||||
%%DocumentData: Clean7Bit
|
||||
%%LanguageLevel: 2
|
||||
%%EndComments
|
||||
%%BeginProlog
|
||||
/cairo_eps_state save def
|
||||
/dict_count countdictstack def
|
||||
/op_count count 1 sub def
|
||||
userdict begin
|
||||
/q { gsave } bind def
|
||||
/Q { grestore } bind def
|
||||
/cm { 6 array astore concat } bind def
|
||||
/w { setlinewidth } bind def
|
||||
/J { setlinecap } bind def
|
||||
/j { setlinejoin } bind def
|
||||
/M { setmiterlimit } bind def
|
||||
/d { setdash } bind def
|
||||
/m { moveto } bind def
|
||||
/l { lineto } bind def
|
||||
/c { curveto } bind def
|
||||
/h { closepath } bind def
|
||||
/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto
|
||||
0 exch rlineto 0 rlineto closepath } bind def
|
||||
/S { stroke } bind def
|
||||
/f { fill } bind def
|
||||
/f* { eofill } bind def
|
||||
/B { fill stroke } bind def
|
||||
/B* { eofill stroke } bind def
|
||||
/n { newpath } bind def
|
||||
/W { clip } bind def
|
||||
/W* { eoclip } bind def
|
||||
/BT { } bind def
|
||||
/ET { } bind def
|
||||
/pdfmark where { pop globaldict /?pdfmark /exec load put }
|
||||
{ globaldict begin /?pdfmark /pop load def /pdfmark
|
||||
/cleartomark load def end } ifelse
|
||||
/BDC { mark 3 1 roll /BDC pdfmark } bind def
|
||||
/EMC { mark /EMC pdfmark } bind def
|
||||
/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def
|
||||
/Tj { show currentpoint cairo_store_point } bind def
|
||||
/TJ {
|
||||
{
|
||||
dup
|
||||
type /stringtype eq
|
||||
{ show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse
|
||||
} forall
|
||||
currentpoint cairo_store_point
|
||||
} bind def
|
||||
/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore
|
||||
cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def
|
||||
/Tf { pop /cairo_font exch def /cairo_font_matrix where
|
||||
{ pop cairo_selectfont } if } bind def
|
||||
/Td { matrix translate cairo_font_matrix matrix concatmatrix dup
|
||||
/cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point
|
||||
/cairo_font where { pop cairo_selectfont } if } bind def
|
||||
/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def
|
||||
cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def
|
||||
/g { setgray } bind def
|
||||
/rg { setrgbcolor } bind def
|
||||
/d1 { setcachedevice } bind def
|
||||
%%EndProlog
|
||||
11 dict begin
|
||||
/FontType 42 def
|
||||
/FontName /f-0-0 def
|
||||
/PaintType 0 def
|
||||
/FontMatrix [ 1 0 0 1 0 0 ] def
|
||||
/FontBBox [ 0 0 0 0 ] def
|
||||
/Encoding 256 array def
|
||||
0 1 255 { Encoding exch /.notdef put } for
|
||||
Encoding 1 /uni004D put
|
||||
Encoding 2 /uni0065 put
|
||||
Encoding 3 /uni006D put
|
||||
Encoding 4 /uni006F put
|
||||
Encoding 5 /uni0072 put
|
||||
Encoding 6 /uni0079 put
|
||||
Encoding 7 /uni0020 put
|
||||
Encoding 8 /uni0043 put
|
||||
Encoding 9 /uni0070 put
|
||||
Encoding 10 /uni0069 put
|
||||
Encoding 11 /uni006C put
|
||||
Encoding 12 /uni0028 put
|
||||
Encoding 13 /uni0050 put
|
||||
Encoding 14 /uni0074 put
|
||||
Encoding 15 /uni0068 put
|
||||
Encoding 16 /uni006E put
|
||||
Encoding 17 /uni0029 put
|
||||
Encoding 18 /uni004C put
|
||||
Encoding 19 /uni0067 put
|
||||
Encoding 20 /uni0063 put
|
||||
Encoding 21 /uni0061 put
|
||||
Encoding 22 /uni0045 put
|
||||
Encoding 23 /uni0046 put
|
||||
Encoding 24 /uni002F put
|
||||
Encoding 25 /uni0052 put
|
||||
Encoding 26 /uni0041 put
|
||||
Encoding 27 /uni0047 put
|
||||
Encoding 28 /uni0044 put
|
||||
Encoding 29 /uni0053 put
|
||||
Encoding 30 /uni0049 put
|
||||
Encoding 31 /uni0062 put
|
||||
Encoding 32 /uni002E put
|
||||
Encoding 33 /uni0056 put
|
||||
Encoding 34 /uni002D put
|
||||
Encoding 35 /uni0064 put
|
||||
Encoding 36 /uni0073 put
|
||||
Encoding 37 /uni0054 put
|
||||
Encoding 38 /uni0077 put
|
||||
Encoding 39 /uni007A put
|
||||
Encoding 40 /uni0075 put
|
||||
Encoding 41 /uni002C put
|
||||
Encoding 42 /uni0078 put
|
||||
Encoding 43 /uni0042 put
|
||||
Encoding 44 /uni006B put
|
||||
Encoding 45 /uni0055 put
|
||||
Encoding 46 /uni0066 put
|
||||
/CharStrings 47 dict dup begin
|
||||
/.notdef 0 def
|
||||
/uni004D 1 def
|
||||
/uni0065 2 def
|
||||
/uni006D 3 def
|
||||
/uni006F 4 def
|
||||
/uni0072 5 def
|
||||
/uni0079 6 def
|
||||
/uni0020 7 def
|
||||
/uni0043 8 def
|
||||
/uni0070 9 def
|
||||
/uni0069 10 def
|
||||
/uni006C 11 def
|
||||
/uni0028 12 def
|
||||
/uni0050 13 def
|
||||
/uni0074 14 def
|
||||
/uni0068 15 def
|
||||
/uni006E 16 def
|
||||
/uni0029 17 def
|
||||
/uni004C 18 def
|
||||
/uni0067 19 def
|
||||
/uni0063 20 def
|
||||
/uni0061 21 def
|
||||
/uni0045 22 def
|
||||
/uni0046 23 def
|
||||
/uni002F 24 def
|
||||
/uni0052 25 def
|
||||
/uni0041 26 def
|
||||
/uni0047 27 def
|
||||
/uni0044 28 def
|
||||
/uni0053 29 def
|
||||
/uni0049 30 def
|
||||
/uni0062 31 def
|
||||
/uni002E 32 def
|
||||
/uni0056 33 def
|
||||
/uni002D 34 def
|
||||
/uni0064 35 def
|
||||
/uni0073 36 def
|
||||
/uni0054 37 def
|
||||
/uni0077 38 def
|
||||
/uni007A 39 def
|
||||
/uni0075 40 def
|
||||
/uni002C 41 def
|
||||
/uni0078 42 def
|
||||
/uni0042 43 def
|
||||
/uni006B 44 def
|
||||
/uni0055 45 def
|
||||
/uni0066 46 def
|
||||
end readonly def
|
||||
/sfnts [
|
||||
<00010000000a008000030020636d61700252f32d00001fd00000009c63767420ffd31d390000
|
||||
206c000001fc6670676de7b4f1c4000022680000008b676c79661cacd2b0000000ac00001f24
|
||||
68656164dd84a2d0000022f40000003668686561104507920000232c00000024686d7478d559
|
||||
18d200002350000000bc6c6f6361aa82b2fc0000240c000000606d617870046a063a0000246c
|
||||
00000020707265703b07f1000000248c0000056800020066fe96046605a400030007001a400c
|
||||
04fb0006fb0108057f0204002fc4d4ec310010d4ecd4ec301311211125211121660400fc7303
|
||||
1bfce5fe96070ef8f2720629000100c90000061f05d5000c00bf403403110708070211010208
|
||||
080702110302090a0901110a0a09420a070203080300af080b050908030201050a061c043e0a
|
||||
1c00040d10fcecfcec11173931002f3cc4ec32111739304b5358071005ed071008ed071008ed
|
||||
071005ed5922b2700e01015d405603070f080f09020a15021407130a260226072007260a200a
|
||||
3407350a69027c027b07790a80028207820a90021604010b0313011b0323012c032708280934
|
||||
013c035608590965086a097608790981018d0395019b03145d005d1321090121112311012301
|
||||
1123c9012d017d017f012dc5fe7fcbfe7fc405d5fc0803f8fa2b051ffc000400fae100000002
|
||||
0071ffe3047f047b0014001b00704024001501098608880515a90105b90c01bb18b912b80c8c
|
||||
1c1b1502081508004b02120f451c10fcecf4ecc4111239310010e4f4ece410ee10ee10f4ee11
|
||||
12393040293f1d701da01dd01df01d053f003f013f023f153f1b052c072f082f092c0a6f006f
|
||||
016f026f156f1b095d71015d0115211e0133323637150e01232000111000333200072e012322
|
||||
0607047ffcb20ccdb76ac76263d06bfef4fec70129fce20107b802a5889ab90e025e5abec734
|
||||
34ae2a2c0138010a01130143feddc497b4ae9e00000100ba0000071d047b0022005a40260612
|
||||
09180f00061d07150c871d2003b81bbc19100700110f0808065011080f501c18081a462310fc
|
||||
ec32fcfcfcec11123931002f3c3ce4f43cc4ec32111217393040133024502470249024a024a0
|
||||
24bf24df24ff2409015d013e0133321615112311342623220615112311342623220615112311
|
||||
33153e01333216042945c082afbeb972758fa6b972778da6b9b93fb0797aab03897c76f5e2fd
|
||||
5c029ea19cbea4fd87029ea29bbfa3fd870460ae67627c00000000020071ffe30475047b000b
|
||||
0017004a401306b91200b90cb8128c1809120f51031215451810fcecf4ec310010e4f4ec10ee
|
||||
3040233f197b007b067f077f087f097f0a7f0b7b0c7f0d7f0e7f0f7f107f117b12a019f01911
|
||||
015d012206151416333236353426273200111000232200111000027394acab9593acac93f001
|
||||
12feeef0f1feef011103dfe7c9c9e7e8c8c7e99cfec8feecfeedfec701390113011401380000
|
||||
000100ba0000034a047b001100304014060b0700110b03870eb809bc070a06080008461210fc
|
||||
c4ec3231002fe4f4ecc4d4cc11123930b450139f1302015d012e012322061511231133153e01
|
||||
33321617034a1f492c9ca7b9b93aba85132e1c03b41211cbbefdb20460ae6663050500000001
|
||||
003dfe56047f0460000f01a240430708020911000f0a110b0a00000f0e110f000f0d110c0d00
|
||||
000f0d110e0d0a0b0a0c110b0b0a420d0b0910000b058703bd0e0bbc100e0d0c0a0906030008
|
||||
0f040f0b1010d4c4c4111739310010e432f4ec113911391239304b5358071005ed071008ed07
|
||||
1008ed071005ed071008ed0705ed17325922014bb00a544bb008545b58bd0010ffc000010010
|
||||
001000403811373859014bb0145458bd00100040000100100010ffc0381137385940f0060005
|
||||
080609030d160a170d100d230d350d490a4f0a4e0d5a095a0a6a0a870d800d930d120a000a09
|
||||
060b050c0b0e0b0f1701150210041005170a140b140c1a0e1a0f270024012402200420052908
|
||||
2809250a240b240c270d2a0e2a0f201137003501350230043005380a360b360c380d390e390f
|
||||
30114100400140024003400440054006400740084209450a470d490e490f4011540051015102
|
||||
5503500450055606550756085709570a550b550c590e590f501166016602680a690e690f6011
|
||||
7b08780e780f89008a09850b850c890d890e890f9909950b950c9a0e9a0fa40ba40cab0eab0f
|
||||
b011cf11df11ff11655d005d050e012b01353332363f01013309013302934e947c936c4c5433
|
||||
21fe3bc3015e015ec368c87a9a488654044efc94036c00010073ffe3052705f000190036401a
|
||||
0da10eae0a951101a100ae04951791118c1a07190d003014101a10fcec32ec310010e4f4ecf4
|
||||
ec10eef6ee30b40f1b1f1b02015d01152e0123200011100021323637150e0123200011100021
|
||||
3216052766e782ff00fef00110010082e7666aed84feadfe7a0186015386ed0562d55f5efec7
|
||||
fed8fed9fec75e5fd34848019f01670168019f470000000200bafe5604a4047b0010001c003e
|
||||
401b1ab9000e14b90508b80e8c01bd03bc1d11120b471704000802461d10fcec3232f4ec3100
|
||||
10e4e4e4f4c4ec10c4ee304009601e801ea01ee01e04015d2511231133153e01333212111002
|
||||
2322260134262322061514163332360173b9b93ab17bccffffcc7bb10238a79292a7a79292a7
|
||||
a8fdae060aaa6461febcfef8fef8febc6101ebcbe7e7cbcbe7e7000200c10000017906140003
|
||||
0007002b400e06be04b100bc020501080400460810fc3cec3231002fe4fcec30400b10094009
|
||||
50096009700905015d1333112311331523c1b8b8b8b80460fba00614e900000100c100000179
|
||||
061400030022b7009702010800460410fcec31002fec30400d10054005500560057005f00506
|
||||
015d13331123c1b8b80614f9ec00000100b0fef2027b0612000d004f400f069800970e0d0700
|
||||
03120600130a0e10dce432ec113939310010fcec30014bb0135458bd000e00400001000e000e
|
||||
ffc03811373859014bb00f5458bd000effc00001000e000e0040381137385901060215141217
|
||||
23260235341237027b86828385a0969594970612e6fe3ee7e7fe3be5eb01c6e0df01c4ec0002
|
||||
00c90000048d05d500080013003a40180195100095098112100a0802040005190d3f11001c09
|
||||
041410fcec32fcec11173931002ff4ecd4ec30400b0f151f153f155f15af1505015d01113332
|
||||
3635342623252132041514042b0111230193fe8d9a9a8dfe3801c8fb0101fefffbfeca052ffd
|
||||
cf92878692a6e3dbdde2fda800010037000002f2059e0013003840190e05080f03a9001101bc
|
||||
08870a0b08090204000810120e461410fc3cc4fc3cc432393931002fecf43cc4ec3211393930
|
||||
b2af1501015d01112115211114163b01152322263511233533110177017bfe854b73bdbdd5a2
|
||||
8787059efec28ffda0894e9a9fd202608f013e000000000100ba000004640614001300344019
|
||||
030900030e0106870e11b80c970a010208004e0d09080b461410fcec32f4ec31002f3cecf4c4
|
||||
ec1112173930b2601501015d0111231134262322061511231133113e013332160464b87c7c95
|
||||
acb9b942b375c1c602a4fd5c029e9f9ebea4fd870614fd9e6564ef00000100ba00000464047b
|
||||
001300364019030900030e0106870e11b80cbc0a010208004e0d09080b461410fcec32f4ec31
|
||||
002f3ce4f4c4ec1112173930b46015cf1502015d0111231134262322061511231133153e0133
|
||||
32160464b87c7c95acb9b942b375c1c602a4fd5c029e9f9ebea4fd870460ae6564ef000100a4
|
||||
fef2026f0612000d001f400f079800970e0701000b12041308000e10dc3cf4ec113939310010
|
||||
fcec301333161215140207233612353402a4a096959596a08583830612ecfe3cdfe0fe3aebe5
|
||||
01c5e7e701c20000000100c90000046a05d500050025400c0295008104011c033a00040610fc
|
||||
ecec31002fe4ec304009300750078003800404015d133311211521c9ca02d7fc5f05d5fad5aa
|
||||
00020071fe56045a047b000b0028004a4023190c1d0912861316b90f03b92623b827bc09b90f
|
||||
bd1a1d261900080c4706121220452910fcc4ecf4ec323231002fc4e4ece4f4c4ec10fed5ee11
|
||||
12393930b6602a802aa02a03015d01342623220615141633323617100221222627351e013332
|
||||
363d010e0123220211101233321617353303a2a59594a5a59495a5b8fefefa61ac51519e52b5
|
||||
b439b27ccefcfcce7cb239b8023dc8dcdcc8c7dcdcebfee2fee91d1eb32c2abdbf5b6362013a
|
||||
01030104013a6263aa0000010071ffe303e7047b0019003f401b00860188040e860d880ab911
|
||||
04b917b8118c1a07120d004814451a10fce432ec310010e4f4ec10fef4ee10f5ee30400b0f1b
|
||||
101b801b901ba01b05015d01152e0123220615141633323637150e0123220011100021321603
|
||||
e74e9d50b3c6c6b3509d4e4da55dfdfed6012d010655a20435ac2b2be3cdcde32b2baa242401
|
||||
3e010e0112013a2300000002007bffe3042d047b000a002500bc4027191f0b17090e00a91706
|
||||
b90e1120861fba1cb923b8118c170c001703180d09080b1f030814452610fcecccd4ec323211
|
||||
393931002fc4e4f4fcf4ec10c6ee10ee11391139123930406e301d301e301f3020302130223f
|
||||
27401d401e401f402040214022501d501e501f50205021502250277027851d871e871f872087
|
||||
2185229027a027f0271e301e301f30203021401e401f40204021501e501f50205021601e601f
|
||||
60206021701e701f70207021801e801f80208021185d015d0122061514163332363d01371123
|
||||
350e01232226353436332135342623220607353e0133321602bedfac816f99b9b8b83fbc88ac
|
||||
cbfdfb0102a79760b65465be5af3f00233667b6273d9b4294cfd81aa6661c1a2bdc0127f8b2e
|
||||
2eaa2727fc00000100c90000048b05d5000b002e401506950402950081089504ad0a05010907
|
||||
031c00040c10fcec32d4c4c431002fececf4ec10ee30b21f0d01015d13211521112115211121
|
||||
1521c903b0fd1a02c7fd3902f8fc3e05d5aafe46aafde3aa0000000100c90000042305d50009
|
||||
002940120695040295008104ad08050107031c00040a10fcec32d4c431002fecf4ec10ee30b2
|
||||
0f0b01015d13211521112115211123c9035afd700250fdb0ca05d5aafe48aafd370000010000
|
||||
ff4202b205d50003002d4014001a010201021a03000342029f008104020001032fc439393100
|
||||
10f4ec304b5358071005ed071005ed5922013301230208aafdf8aa05d5f96d000000000200c9
|
||||
0000055405d50013001c00b14035090807030a06110304030511040403420604001503041595
|
||||
0914950d810b040506031109001c160e050a191904113f140a1c0c041d10fcec32fcc4ec1117
|
||||
391139393931002f3cf4ecd4ec123912391239304b5358071005ed071005ed1117395922b240
|
||||
1e01015d40427a13010500050105020603070415001501140216031704250025012502260327
|
||||
06260726082609201e3601360246014602680575047505771388068807980698071f5d005d01
|
||||
1e01171323032e012b01112311212016151406011133323635342623038d417b3ecdd9bf4a8b
|
||||
78dcca01c80100fc83fd89fe9295959202bc16907efe68017f9662fd8905d5d6d88dba024ffd
|
||||
ee8783838500000200100000056805d50002000a00ba40410011010004050402110505040111
|
||||
0a030a0011020003030a0711050406110505040911030a08110a030a42000307950103810905
|
||||
09080706040302010009050a0b10d4c4173931002f3ce4d4ec1239304b5358071005ed0705ed
|
||||
071005ed0705ed071008ed071005ed071005ed071008ed5922b2200c01015d403a0f00580076
|
||||
0070008c000507010802060309041601190256015802500c67016802780176027c0372047707
|
||||
780887018802800c980299039604175d005d090121013301230321032302bcfeee0225fe7be5
|
||||
0239d288fd5f88d5050efd1903aefa2b017ffe81000000010073ffe3058b05f0001d00394020
|
||||
00051b0195031b950812a111ae15950e91088c1e02001c1134043318190b101e10fcecfce4fc
|
||||
c4310010e4f4ecf4ec10fed4ee11393930251121352111060423200011100021320417152626
|
||||
23200011100021323604c3feb6021275fee6a0fea2fe75018b015e9201076f70fc8bfeeefeed
|
||||
011301126ba8d50191a6fd7f53550199016d016e01994846d75f60fecefed1fed2fece250000
|
||||
000200c9000005b005d500080011002e4015009509810195100802100a0005190d32001c0904
|
||||
1210fcecf4ec113939393931002fecf4ec30b2601301015d0111332000111000212521200011
|
||||
100029010193f40135011ffee1fecbfe42019f01b20196fe68fe50fe61052ffb770118012e01
|
||||
2c0117a6fe97fe80fe7efe96000000010087ffe304a205f00027007e403c0d0c020e0b021e1f
|
||||
1e080902070a021f1f1e420a0b1e1f0415010015a11494189511049500942591118c281e0a0b
|
||||
1f1b0700221b190e2d071914222810dcc4ecfcece4111239393939310010e4f4e4ec10eef6ee
|
||||
10c6111739304b535807100eed11173907100eed1117395922b20f2901015db61f292f294f29
|
||||
035d01152e012322061514161f011e0115140421222627351e013332363534262f012e013534
|
||||
24333216044873cc5fa5b377a67ae2d7feddfee76aef807bec72adbc879a7be2ca0117f569da
|
||||
05a4c53736807663651f192bd9b6d9e0302fd04546887e6e7c1f182dc0abc6e42600000100c9
|
||||
0000019305d500030039b700af02011c00040410fcec31002fec30014bb0105458bd0004ffc0
|
||||
00010004000400403811373859400d30054005500560058f059f05065d13331123c9caca05d5
|
||||
fa2b0000000200baffe304a40614000b001c0038401903b90c0f09b918158c0fb81b97190012
|
||||
1247180c06081a461d10fcec3232f4ec31002fece4f4c4ec10c6ee30b6601e801ea01e03015d
|
||||
013426232206151416333236013e01333212111002232226271523113303e5a79292a7a79292
|
||||
a7fd8e3ab17bccffffcc7bb13ab9b9022fcbe7e7cbcbe7e702526461febcfef8fef8febc6164
|
||||
a8061400000100db000001ae00fe00030011b7008302011900180410fcec31002fec30373315
|
||||
23dbd3d3fefe000100100000056805d5000600b7402704110506050311020306060503110403
|
||||
000100021101010042030401af0006040302000505010710d4c4173931002fec3239304b5358
|
||||
071005ed071008ed071008ed071005ed5922b2500801015d406200032a03470447055a037d03
|
||||
8303070600070208040906150114021a041a052a002601260229042905250620083800330133
|
||||
023c043c053706480045014502490449054706590056066602690469057a0076017602790479
|
||||
057506800898009706295d005d21013309013301024afdc6d301d901dad2fdc705d5fb1704e9
|
||||
fa2b0001006401df027f028300030011b6009c020401000410dccc310010d4ec301321152164
|
||||
021bfde50283a40000020071ffe3045a06140010001c003840191ab9000e14b905088c0eb801
|
||||
970317040008024711120b451d10fcecf4ec323231002fece4f4c4ec10c4ee30b6601e801ea0
|
||||
1e03015d0111331123350e0123220211101233321601141633323635342623220603a2b8b83a
|
||||
b17ccbffffcb7cb1fdc7a79292a8a89292a703b6025ef9eca86461014401080108014461fe15
|
||||
cbe7e7cbcbe7e7000001006fffe303c7047b002700e7403c0d0c020e0b531f1e080902070a53
|
||||
1e1f1e420a0b1e1f041500860189041486158918b91104b925b8118c281e0a0b1f1b0700521b
|
||||
080e07081422452810fcc4ecd4ece4111239393939310010e4f4ec10fef5ee10f5ee12173930
|
||||
4b535807100eed111739070eed1117395922b2002701015d406d1c0a1c0b1c0c2e092c0a2c0b
|
||||
2c0c3b093b0a3b0b3b0c0b200020012402280a280b2a132f142f152a16281e281f2920292124
|
||||
27860a860b860c860d12000000010202060a060b030c030d030e030f03100319031a031b031c
|
||||
041d09272f293f295f297f2980299029a029f029185d005d7101152e012322061514161f011e
|
||||
0115140623222627351e013332363534262f012e01353436333216038b4ea85a898962943fc4
|
||||
a5f7d85ac36c66c661828c65ab40ab98e0ce66b4043fae282854544049210e2a99899cb62323
|
||||
be353559514b50250f2495829eac1e0000000001fffa000004e905d50007004a400e06029500
|
||||
81040140031c0040050810d4e4fce431002ff4ec3230014bb00a5458bd000800400001000800
|
||||
08ffc03811373859401300091f00100110021f071009400970099f09095d0321152111231121
|
||||
0604effdeecbfdee05d5aafad5052b0000010056000006350460000c0201404905550605090a
|
||||
0904550a0903550a0b0a025501020b0b0a061107080705110405080807021103020c000c0111
|
||||
00000c420a050203060300bf0b080c0b0a09080605040302010b07000d10d4cc173931002f3c
|
||||
ec32321739304b5358071005ed071008ed071008ed071005ed071008ed071005ed0705ed0710
|
||||
08ed5922014bb00a544bb011545b4bb012545b4bb013545b4bb00b545b58bd000dffc0000100
|
||||
0d000d00403811373859014bb00c544bb00d545b4bb010545b58bd000d00400001000d000dff
|
||||
c0381137385940ff050216021605220a350a49024905460a400a5b025b05550a500a6e026e05
|
||||
660a79027f0279057f05870299029805940abc02bc05ce02c703cf051d0502090306040b050a
|
||||
080b09040b050c1502190316041a051b081b09140b150c250025012302270321042505220622
|
||||
0725082709240a210b230c390336043608390c300e4602480346044004420540064007400844
|
||||
09440a440b400e400e560056015602500451055206520750085309540a550b6300640165026a
|
||||
0365046a056a066a076e09610b670c6f0e7500750179027d0378047d057a067f067a077f0778
|
||||
0879097f097b0a760b7d0c870288058f0e97009701940293039c049b05980698079908402f96
|
||||
0c9f0ea600a601a402a403ab04ab05a906a907ab08a40caf0eb502b103bd04bb05b809bf0ec4
|
||||
02c303cc04ca05795d005d13331b01331b013301230b012356b8e6e5d9e6e5b8fedbd9f1f2d9
|
||||
0460fc96036afc96036afba00396fc6a000000010058000003db0460000900b4401a08110203
|
||||
0203110708074208a900bc03a905080301000401060a10dcc432c411393931002fecf4ec304b
|
||||
5358071005ed071005ed5922014bb00b544bb00c545b58bd000a00400001000a000affc03811
|
||||
373859014bb0135458bd000affc00001000a000a004038113738594042050216022602470249
|
||||
07050b080f0b18031b082b08200b36033908300b400140024503400440054308570359085f0b
|
||||
6001600266036004600562087f0b800baf0b1b5d005d1321150121152135012171036afd4c02
|
||||
b4fc7d02b4fd650460a8fcdb93a803250000000100aeffe30458046000130036401903090003
|
||||
0e0106870e118c0a01bc0c0d09080b4e020800461410fcecf4ec3231002fe432f4c4ec111217
|
||||
3930b46015cf1502015d1311331114163332363511331123350e01232226aeb87c7c95adb8b8
|
||||
43b175c1c801ba02a6fd619f9fbea4027bfba0ac6663f0000001009eff1201c300fe00050019
|
||||
400c039e0083060304011900180610fcecd4cc310010fcec30373315032313f0d3a48152feac
|
||||
fec001400001003b000004790460000b015a4046051106070604110304070706041105040102
|
||||
0103110202010b110001000a11090a0101000a110b0a0708070911080807420a070401040800
|
||||
bf05020a0704010408000208060c10d4c4d4c411173931002f3cec321739304b5358071005ed
|
||||
071008ed071008ed071005ed071005ed071008ed071008ed071005ed5922014bb00a544bb00f
|
||||
545b4bb010545b4bb011545b58bd000cffc00001000c000c00403811373859014bb0145458bd
|
||||
000c00400001000c000cffc0381137385940980a04040a1a04150a260a3d04310a5504570758
|
||||
0a660a76017a047607740a8d04820a99049f049707920a900aa601a904af04a507a30aa00a1c
|
||||
0a03040505090a0b1a03150515091a0b2903260525092a0b200d3a013903370534073609390b
|
||||
300d4903460545094a0b400d590056015902590357055606590756085609590b500d6f0d7801
|
||||
7f0d9b019407ab01a407b00dcf0ddf0dff0d2f5d005d09022309012309013309010464fe6b01
|
||||
aad9febafebad901b3fe72d9012901290460fddffdc101b8fe48024a0216fe71018f00000003
|
||||
00c9000004ec05d5000800110020004340231900950a0995128101950aad1f110b080213191f
|
||||
05000e1c1605191c2e09001c12042110fcec32fcecd4ec111739393931002fececf4ec10ee39
|
||||
30b20f2201015d01112132363534262301112132363534262325213216151406071e01151404
|
||||
232101930144a39d9da3febc012b94919194fe0b0204e7fa807c95a5fef0fbfde802c9fddd87
|
||||
8b8c850266fe3e6f727170a6c0b189a21420cb98c8da000100ba0000049c0614000a00bc4029
|
||||
0811050605071106060503110405040211050504420805020303bc0097090605010406080108
|
||||
00460b10fcec32d4c4113931002f3cece41739304b5358071004ed071005ed071005ed071004
|
||||
ed5922b2100c01015d405f04020a081602270229052b0856026602670873027705820289058e
|
||||
08930296059708a3021209050906020b030a072803270428052b062b07400c6803600c890385
|
||||
0489058d068f079a039707aa03a705b607c507d607f703f003f704f0041a5d71005d13331101
|
||||
33090123011123bab90225ebfdae026bf0fdc7b90614fc6901e3fdf4fdac0223fddd000100b2
|
||||
ffe3052905d50011004b40160802110b0005950e8c09008112081c0a38011c00411210fcecfc
|
||||
ec310010e432f4ec113939393930014bb0105458bd00120040000100120012ffc03811373859
|
||||
b61f138f139f13035d133311141633323635113311100021200011b2cbaec3c2aecbfedffee6
|
||||
fee5fedf05d5fc75f0d3d3f0038bfc5cfedcfed6012a012400000001002f000002f806140013
|
||||
0070401c0510010c08a906018700970e06bc0a02130700070905080d0f0b4c1410fc3cc4fc3c
|
||||
c4c412393931002fe432fcec10ee3212393930014bb00a5458bd0014ffc00001001400140040
|
||||
3811373859014bb00e5458bd00140040000100140014ffc03811373859b640155015a015035d
|
||||
01152322061d012115211123112335333534363302f8b0634d012ffed1b9b0b0aebd06149950
|
||||
68638ffc2f03d18f4ebbab000000000200030000000000140001000000000034000400200000
|
||||
0004000400010000f02effff0000f000ffff10000001000000000006006800000000002f0000
|
||||
000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013
|
||||
001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026
|
||||
002700280029002a002b002c002d002e013500b800cb00cb00c100aa009c01a600b800660000
|
||||
007100cb00a002b20085007500b800c301cb0189022d00cb00a600f000d300aa008700cb03aa
|
||||
0400014a003300cb000000d9050200f4015400b4009c01390114013907060400044e04b40452
|
||||
04b804e704cd0037047304cd04600473013303a2055605a60556053903c5021200c9001f00b8
|
||||
01df007300ba03e9033303bc0444040e00df03cd03aa00e503aa0404000000cb008f00a4007b
|
||||
00b80014016f007f027b0252008f00c705cd009a009a006f00cb00cd019e01d300f000ba0183
|
||||
00d5009803040248009e01d500c100cb00f600830354027f00000333026600d300c700a400cd
|
||||
008f009a0073040005d5010a00fe022b00a400b4009c00000062009c0000001d032d05d505d5
|
||||
05d505f0007f007b005400a406b80614072301d300b800cb00a601c301ec069300a000d3035c
|
||||
037103db0185042304a80448008f0139011401390360008f05d5019a06140723066601790460
|
||||
04600460047b009c00000277046001aa00e904600762007b00c5007f027b000000b4025205cd
|
||||
006600bc00660077061000cd013b01850389008f007b0000001d00cd074a042f009c009c0000
|
||||
077d006f0000006f0335006a006f007b00ae00b2002d0396008f027b00f600830354063705f6
|
||||
008f009c04e10266008f018d02f600cd03440029006604ee007300001400b606050403020100
|
||||
2c2010b002254964b040515820c859212d2cb002254964b040515820c859212d2c20100720b0
|
||||
0050b00d7920b8ffff5058041b0559b0051cb0032508b0042523e120b00050b00d7920b8ffff
|
||||
5058041b0559b0051cb0032508e12d2c4b505820b0fd454459212d2cb002254560442d2c4b53
|
||||
58b00225b0022545445921212d2c45442d000001000000020000322394a85f0f3cf5001f0800
|
||||
00000000bab9f0b800000000bac26791fe89fe1d0a4c076d0000000800010000000000000001
|
||||
0000076dfe1d00000abcfe89fe890a4c00010000000000000000000000000000002f04cd0066
|
||||
06e700c904ec007107cb00ba04e50071034a00ba04bc003d028b000005960073051400ba0239
|
||||
00c1023900c1031f00b004d300c903230037051200ba051200ba031f00a4047500c905140071
|
||||
0466007104e7007b050e00c9049a00c902b20000058f00c90579001006330073062900c90514
|
||||
0087025c00c9051400ba028b00db0579001002e3006405140071042b006f04e3fffa068b0056
|
||||
04330058051200ae028b009e04bc003b057d00c904a200ba05db00b202d1002f0000002200a0
|
||||
010a016c01be01f602e602e60332038003a803c6040a044a048804c40500052c054e05b205fe
|
||||
069406c406ee0714079e0818086c08ac09280952099e09b20a220a380a840b340b6c0c8a0cfc
|
||||
0d380d540e220e7a0ef20f3a0f9200010000002f004d00070042000400020010004000070000
|
||||
0415056800030001b8028040fffbfe03fa1403f92503f83203f79603f60e03f5fe03f4fe03f3
|
||||
2503f20e03f19603f02503ef8a4105effe03ee9603ed9603ecfa03ebfa03eafe03e93a03e842
|
||||
03e7fe03e63203e5e45305e59603e48a4105e45303e3e22f05e3fa03e22f03e1fe03e0fe03df
|
||||
3203de1403dd9603dcfe03db1203da7d03d9bb03d8fe03d68a4105d67d03d5d44705d57d03d4
|
||||
4703d3d21b05d3fe03d21b03d1fe03d0fe03cffe03cefe03cd9603cccb1e05ccfe03cb1e03ca
|
||||
3203c9fe03c6851105c61c03c51603c4fe03c3fe03c2fe03c1fe03c0fe03bffe03befe03bdfe
|
||||
03bcfe03bbfe03ba1103b9862505b9fe03b8b7bb05b8fe03b7b65d05b7bb03b78004b6b52505
|
||||
b65d40ff03b64004b52503b4fe03b39603b2fe03b1fe03b0fe03affe03ae6403ad0e03acab25
|
||||
05ac6403abaa1205ab2503aa1203a98a4105a9fa03a8fe03a7fe03a6fe03a51203a4fe03a3a2
|
||||
0e05a33203a20e03a16403a08a4105a096039ffe039e9d0c059efe039d0c039c9b19059c6403
|
||||
9b9a10059b19039a1003990a0398fe0397960d0597fe03960d03958a410595960394930e0594
|
||||
2803930e0392fa039190bb0591fe03908f5d0590bb039080048f8e25058f5d038f40048e2503
|
||||
8dfe038c8b2e058cfe038b2e038a8625058a410389880b05891403880b038786250587640386
|
||||
85110586250385110384fe038382110583fe0382110381fe0380fe037ffe0340ff7e7d7d057e
|
||||
fe037d7d037c64037b5415057b25037afe0379fe03780e03770c03760a0375fe0374fa0373fa
|
||||
0372fa0371fa0370fe036ffe036efe036c21036bfe036a1142056a530369fe03687d03671142
|
||||
0566fe0365fe0364fe0363fe0362fe03613a0360fa035e0c035dfe035bfe035afe0359580a05
|
||||
59fa03580a035716190557320356fe035554150555420354150353011005531803521403514a
|
||||
130551fe03500b034ffe034e4d10054efe034d10034cfe034b4a13054bfe034a4910054a1303
|
||||
491d0d05491003480d0347fe0346960345960344fe0343022d0543fa0342bb03414b0340fe03
|
||||
3ffe033e3d12053e14033d3c0f053d12033c3b0d053c40ff0f033b0d033afe0339fe03383714
|
||||
0538fa033736100537140336350b05361003350b03341e03330d0332310b0532fe03310b0330
|
||||
2f0b05300d032f0b032e2d09052e10032d09032c32032b2a25052b64032a2912052a25032912
|
||||
032827250528410327250326250b05260f03250b0324fe0323fe03220f032101100521120320
|
||||
64031ffa031e1d0d051e64031d0d031c1142051cfe031bfa031a42031911420519fe03186403
|
||||
1716190517fe031601100516190315fe0314fe0313fe031211420512fe0311022d0511420310
|
||||
7d030f64030efe030d0c16050dfe030c0110050c16030bfe030a100309fe0308022d0508fe03
|
||||
0714030664030401100504fe03401503022d0503fe0302011005022d0301100300fe0301b801
|
||||
64858d012b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b002b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b
|
||||
2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b1d00>
|
||||
] def
|
||||
FontName currentdict end definefont pop
|
||||
%%Page: 1 1
|
||||
%%BeginPageSetup
|
||||
%%PageBoundingBox: 0 0 630 399
|
||||
%%EndPageSetup
|
||||
q
|
||||
0 g
|
||||
1.005231 w
|
||||
0 J
|
||||
0 j
|
||||
[] 0.0 d
|
||||
4 M q 1 0 0 -1 0 398.268463 cm
|
||||
220.387 54.336 m 351.496 54.336 l 351.496 88.773 l 220.387 88.773 l
|
||||
220.387 54.336 l h
|
||||
220.387 54.336 m S Q
|
||||
BT
|
||||
9.6 0 0 9.6 240.400003 325.596368 Tm
|
||||
/f-0-0 1 Tf
|
||||
[<01>1<02>2<03>2<0405>-1<060708>-1<0403>2<090a0b>1<02>2<05>]TJ
|
||||
0 -1.25 Td
|
||||
[<0c0d>-1<060e0f>-1<0410>-1<11>]TJ
|
||||
ET
|
||||
1.60016 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
175.582 163.477 m 175.582 168.566 160.82 172.695 142.613 172.695 c
|
||||
124.402 172.695 109.645 168.566 109.645 163.477 c 109.645 158.383
|
||||
124.402 154.254 142.613 154.254 c 160.82 154.254 175.582 158.383
|
||||
175.582 163.477 c h
|
||||
175.582 163.477 m S Q
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
175.504 212.371 m 175.504 217.461 160.742 221.59 142.535 221.59 c
|
||||
124.328 221.59 109.566 217.461 109.566 212.371 c 109.566 207.277
|
||||
124.328 203.148 142.535 203.148 c 160.742 203.148 175.504 207.277
|
||||
175.504 212.371 c h
|
||||
175.504 212.371 m S Q
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
175.023 163.754 m 175.023 212.371 l S Q
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
109.645 163.477 m 109.645 212.09 l S Q
|
||||
1.60016 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
311.406 163.895 m 311.406 168.988 296.645 173.113 278.438 173.113 c
|
||||
260.227 173.113 245.469 168.988 245.469 163.895 c 245.469 158.801
|
||||
260.227 154.676 278.438 154.676 c 296.645 154.676 311.406 158.801
|
||||
311.406 163.895 c h
|
||||
311.406 163.895 m S Q
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
311.328 212.789 m 311.328 217.879 296.566 222.008 278.359 222.008 c
|
||||
260.152 222.008 245.391 217.879 245.391 212.789 c 245.391 207.695
|
||||
260.152 203.57 278.359 203.57 c 296.566 203.57 311.328 207.695 311.328
|
||||
212.789 c h
|
||||
311.328 212.789 m S Q
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
310.844 164.176 m 310.844 212.789 l S Q
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
245.469 163.895 m 245.469 212.508 l S Q
|
||||
1.60016 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
439.926 166.688 m 439.926 171.781 425.164 175.91 406.957 175.91 c
|
||||
388.75 175.91 373.988 171.781 373.988 166.688 c 373.988 161.598 388.75
|
||||
157.469 406.957 157.469 c 425.164 157.469 439.926 161.598 439.926
|
||||
166.688 c h
|
||||
439.926 166.688 m S Q
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
439.848 215.582 m 439.848 220.676 425.09 224.801 406.883 224.801 c
|
||||
388.672 224.801 373.914 220.676 373.914 215.582 c 373.914 210.492
|
||||
388.672 206.363 406.883 206.363 c 425.09 206.363 439.848 210.492
|
||||
439.848 215.582 c h
|
||||
439.848 215.582 m S Q
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
439.367 166.969 m 439.367 215.582 l S Q
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
373.988 166.688 m 373.988 215.305 l S Q
|
||||
1.60016 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
252.73 339.355 m 252.73 344.445 237.973 348.574 219.762 348.574 c
|
||||
201.555 348.574 186.793 344.445 186.793 339.355 c 186.793 334.262
|
||||
201.555 330.133 219.762 330.133 c 237.973 330.133 252.73 334.262 252.73
|
||||
339.355 c h
|
||||
252.73 339.355 m S Q
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
252.656 388.25 m 252.656 393.34 237.895 397.469 219.688 397.469 c
|
||||
201.48 397.469 186.719 393.34 186.719 388.25 c 186.719 383.156 201.48
|
||||
379.027 219.688 379.027 c 237.895 379.027 252.656 383.156 252.656
|
||||
388.25 c h
|
||||
252.656 388.25 m S Q
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
252.172 339.633 m 252.172 388.25 l S Q
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
186.793 339.355 m 186.793 387.969 l S Q
|
||||
BT
|
||||
9.6 0 0 9.6 75.557163 243.454333 Tm
|
||||
/f-0-0 1 Tf
|
||||
[<12>18<04>1<130a14>1<15>-2<0b>]TJ
|
||||
18.684519 -2.996216 Td
|
||||
[<1216>1<17>1<18>-1<17>1<19>41<1a01>]TJ
|
||||
0 -1.25 Td
|
||||
[<1b1c1d>-2<1e1e>]TJ
|
||||
12.805588 0.0276477 Td
|
||||
[<120a>1<1f02>2<05>-1<0e>-1<06070c>-1<200b0a>1<1f11>]TJ
|
||||
-26.891736 1.133586 Td
|
||||
[<1d>-2<090a>1<14>1<02>1<18>-1<12>110<21>1<1d>]TJ
|
||||
0 -1.25 Td
|
||||
[<21>79<02>2<05>-1<0a0b>1<0413>]TJ
|
||||
8.43187 5.747114 Td
|
||||
[<17>73<05>21<0410>-1<0e22>-1<16>1<10>-1<23>]TJ
|
||||
0 -1.25 Td
|
||||
[<0d0f>-1<0624>-1<0a>1<14>1<15>-3<0b>]TJ
|
||||
25.203725 -0.0305608 Td
|
||||
[<16>1<24>-1<0e0a03>2<15>-2<0e>-1<02>2<23>]TJ
|
||||
0 -1.25 Td
|
||||
[<25>29<0a03>2<0a10>-1<1318>-1<0d>35<042602>2<05>]TJ
|
||||
ET
|
||||
1.005231 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
156.406 248.234 m 287.516 248.234 l 287.516 282.676 l 156.406 282.676 l
|
||||
156.406 248.234 l h
|
||||
156.406 248.234 m S Q
|
||||
BT
|
||||
9.6 0 0 9.6 168.816605 130.165637 Tm
|
||||
/f-0-0 1 Tf
|
||||
[<01>1<02>2<03>2<0405>-1<060708>-1<0f>-1<15>-2<05>-1<15>-3<14>1<0e02>2<05>-1<0a
|
||||
27>]TJ
|
||||
10.126953 0 Td
|
||||
[<02>2<05>]TJ
|
||||
-10.126953 -1.25 Td
|
||||
[<0c0d>-1<060e0f>-1<0410>-1<11>]TJ
|
||||
ET
|
||||
0.921608 w
|
||||
[ 5.529648 0.921608] 0 d
|
||||
q 1 0 0 1 0 398.268463 cm
|
||||
0.461 -281.039 m 116.008 -281.039 l 116.008 -248.191 l 0.461 -248.191 l
|
||||
0.461 -281.039 l h
|
||||
0.461 -281.039 m S Q
|
||||
BT
|
||||
9.6 0 0 9.6 4.795825 134.635925 Tm
|
||||
/f-0-0 1 Tf
|
||||
[<1d>-2<0a>1<03>1<28>-1<0b>1<15>-3<0e0405>]TJ
|
||||
0 -1.25 Td
|
||||
[<0c02>1<201320>-1<0710>-1<1324090a14>1<02>2<29>-1<0724>-1<0902>2<14>1<0e
|
||||
05>]TJ
|
||||
10.197266 0 Td
|
||||
[<02>2<11>]TJ
|
||||
ET
|
||||
0.874385 w
|
||||
[ 0.874385 1.748769] 0 d
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
331.52 249.285 m 435.379 249.285 l 435.379 282.18 l 331.52 282.18 l
|
||||
331.52 249.285 l h
|
||||
331.52 249.285 m S Q
|
||||
BT
|
||||
9.6 0 0 9.6 345.902591 136.871082 Tm
|
||||
/f-0-0 1 Tf
|
||||
[<16>1<2a0e>-1<0515>-3<14>1<0e0405>]TJ
|
||||
0 -1.25 Td
|
||||
[<0c02>1<201320>-1<0708>-1<15>-2<0b0a>1<1f05>20<02>2<11>]TJ
|
||||
-23.853645 -9.999148 Td
|
||||
[<1a1010>-1<040e15>-3<0e02>2<23>]TJ
|
||||
0 -1.25 Td
|
||||
[<25>29<0a03>2<0a10>-1<1318>-1<0d>35<042602>2<05>]TJ
|
||||
7.661297 2.021848 Td
|
||||
[<120a>1<1f02>2<05>-1<0e>-1<06070c>-1<200b0a>1<1f11>]TJ
|
||||
0 -1.25 Td
|
||||
[<1d>-2<090a>1<14>1<02>]TJ
|
||||
ET
|
||||
1.6 w
|
||||
[] 0.0 d
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
280.633 88.879 m 145.965 152.578 l S Q
|
||||
151.75 248.425 m 153.273 252.686 l 144.52 245.007 l 156.012 246.901 l
|
||||
151.75 248.425 l h
|
||||
151.75 248.425 m f*
|
||||
0.723173 w
|
||||
q 1 0.473029 0.473029 -1 0 398.268463 cm
|
||||
66.083 181.103 m 68.975 178.209 l 58.853 181.101 l 68.977 183.995 l
|
||||
66.083 181.103 l h
|
||||
66.083 181.103 m S Q
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
280.074 89.438 m 280.074 153.695 l S Q
|
||||
280.074 250.972 m 276.875 254.171 l 280.074 242.972 l 283.273 254.171 l
|
||||
280.074 250.972 l h
|
||||
280.074 250.972 m f*
|
||||
0.8 w
|
||||
q -0.000000000000000061 1 1 0.000000000000000061 0 398.268463 cm
|
||||
-147.297 280.074 m -144.098 276.875 l -155.297 280.074 l -144.098
|
||||
283.273 l -147.297 280.074 l h
|
||||
-147.297 280.074 m S Q
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
285.27 89.043 m 340.207 121.863 l S Q
|
||||
334.711 279.69 m 330.324 278.581 l 341.578 275.585 l 333.605 284.077 l
|
||||
334.711 279.69 l h
|
||||
334.711 279.69 m f*
|
||||
0.686779 w
|
||||
q -1 0.597406 0.597406 1 0 398.268463 cm
|
||||
-298.881 59.975 m -296.137 57.226 l -305.75 59.973 l -296.135 62.722 l
|
||||
-298.881 59.975 l h
|
||||
-298.881 59.975 m S Q
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
155.465 265.453 m 116.348 265.453 l S Q
|
||||
149.062 132.815 m 145.863 129.612 l 157.062 132.815 l 145.863 136.015 l
|
||||
149.062 132.815 l h
|
||||
149.062 132.815 m f*
|
||||
0.8 w
|
||||
q -1 -0.000000000000000122 -0.000000000000000122 1 0 398.268463 cm
|
||||
-149.062 -265.453 m -145.863 -268.656 l -157.062 -265.453 l -145.863
|
||||
-262.254 l -149.062 -265.453 l h
|
||||
-149.062 -265.453 m S Q
|
||||
122.75 132.815 m 125.949 136.015 l 114.75 132.815 l 125.949 129.612 l
|
||||
122.75 132.815 l h
|
||||
122.75 132.815 m f*
|
||||
q 1 -0.000000000000000122 -0.000000000000000122 -1 0 398.268463 cm
|
||||
122.75 265.453 m 125.949 262.254 l 114.75 265.453 l 125.949 268.656 l
|
||||
122.75 265.453 l h
|
||||
122.75 265.453 m S Q
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
252.395 219.602 m 218.934 247.098 l S Q
|
||||
223.879 155.233 m 224.32 159.737 l 217.699 150.155 l 228.383 154.792 l
|
||||
223.879 155.233 l h
|
||||
223.879 155.233 m f*
|
||||
0.618042 w
|
||||
q 1 0.821886 0.821886 -1 0 398.268463 cm
|
||||
14.403 254.873 m 16.876 252.401 l 8.224 254.872 l 16.874 257.345 l
|
||||
14.403 254.873 l h
|
||||
14.403 254.873 m S Q
|
||||
1.005231 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
341.922 104.066 m 473.031 104.066 l 473.031 138.508 l 341.922 138.508 l
|
||||
341.922 104.066 l h
|
||||
341.922 104.066 m S Q
|
||||
BT
|
||||
9.6 0 0 9.6 354.334622 274.333276 Tm
|
||||
/f-0-0 1 Tf
|
||||
[<01>1<02>2<03>2<0405>-1<060708>-1<0f>-1<15>-2<05>-1<15>-3<14>1<0e02>2<05>-1<0a
|
||||
27>]TJ
|
||||
10.126953 0 Td
|
||||
[<02>2<05>]TJ
|
||||
-10.126953 -1.25 Td
|
||||
[<0c0d>-1<060e0f>-1<0410>-1<11>]TJ
|
||||
ET
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
408.594 137.887 m 408.594 156.578 l S Q
|
||||
408.594 248.089 m 405.395 251.288 l 408.594 240.089 l 411.797 251.288 l
|
||||
408.594 248.089 l h
|
||||
408.594 248.089 m f*
|
||||
0.8 w
|
||||
q -0.000000000000000061 1 1 0.000000000000000061 0 398.268463 cm
|
||||
-150.18 408.594 m -146.98 405.395 l -158.18 408.594 l -146.98 411.797 l
|
||||
-150.18 408.594 l h
|
||||
-150.18 408.594 m S Q
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
157.699 221.309 m 200.727 247.016 l S Q
|
||||
195.23 154.538 m 190.844 153.429 l 202.098 150.433 l 194.125 158.925 l
|
||||
195.23 154.538 l h
|
||||
195.23 154.538 m f*
|
||||
0.68678 w
|
||||
q -1 0.597403 0.597403 1 0 398.268463 cm
|
||||
-251.189 -93.67 m -248.444 -96.418 l -258.057 -93.672 l -248.443
|
||||
-90.923 l -251.189 -93.67 l h
|
||||
-251.189 -93.67 m S Q
|
||||
2.4 w
|
||||
[ 7.2 7.2] 0 d
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
22.473 236.957 m 597.465 236.957 l S Q
|
||||
BT
|
||||
16 0 0 16 5.708505 64.082971 Tm
|
||||
/f-0-0 1 Tf
|
||||
[<2b15>-2<14>1<2c>1<22161023>]TJ
|
||||
0 -1 Td
|
||||
[<01>1<02>2<0e0f>-1<042304>1<0b04>1<1306>]TJ
|
||||
0.674965 15.682794 Td
|
||||
[<17>73<05>21<0410>-1<0e22>-1<16>1<10>-1<23>]TJ
|
||||
0 -1 Td
|
||||
[<01>1<02>2<0e0f>-1<042304>1<0b04>1<1306>]TJ
|
||||
ET
|
||||
0.921608 w
|
||||
[ 5.529648 0.921608] 0 d
|
||||
q 1 0 0 1 0 398.268463 cm
|
||||
514.727 -138.27 m 630.273 -138.27 l 630.273 -105.422 l 514.727 -105.422
|
||||
l 514.727 -138.27 l h
|
||||
514.727 -138.27 m S Q
|
||||
BT
|
||||
9.6 0 0 9.6 519.059866 277.406592 Tm
|
||||
/f-0-0 1 Tf
|
||||
[<1d>-2<0a>1<03>1<28>-1<0b>1<15>-3<0e0405>]TJ
|
||||
0 -1.25 Td
|
||||
[<0c02>1<201320>-1<0710>-1<1324090a14>1<02>2<29>-1<0724>-1<0902>2<14>1<0e
|
||||
05>]TJ
|
||||
10.197266 0 Td
|
||||
[<02>2<11>]TJ
|
||||
ET
|
||||
1.6 w
|
||||
[] 0.0 d
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
513.824 122.125 m 474.711 122.125 l S Q
|
||||
507.426 276.143 m 504.227 272.944 l 515.426 276.143 l 504.227 279.343 l
|
||||
507.426 276.143 l h
|
||||
507.426 276.143 m f*
|
||||
0.8 w
|
||||
q -1 -0.000000000000000122 -0.000000000000000122 1 0 398.268463 cm
|
||||
-507.426 -122.125 m -504.227 -125.324 l -515.426 -122.125 l -504.227
|
||||
-118.926 l -507.426 -122.125 l h
|
||||
-507.426 -122.125 m S Q
|
||||
481.109 276.143 m 484.312 279.343 l 473.109 276.143 l 484.312 272.944 l
|
||||
481.109 276.143 l h
|
||||
481.109 276.143 m f*
|
||||
q 1 -0.000000000000000122 -0.000000000000000122 -1 0 398.268463 cm
|
||||
481.109 122.125 m 484.312 118.926 l 473.109 122.125 l 484.312 125.324 l
|
||||
481.109 122.125 l h
|
||||
481.109 122.125 m S Q
|
||||
1.60016 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
488.539 29.785 m 488.539 34.879 473.781 39.004 455.57 39.004 c 437.363
|
||||
39.004 422.602 34.879 422.602 29.785 c 422.602 24.691 437.363 20.566
|
||||
455.57 20.566 c 473.781 20.566 488.539 24.691 488.539 29.785 c h
|
||||
488.539 29.785 m S Q
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
488.465 78.68 m 488.465 83.77 473.703 87.898 455.496 87.898 c 437.289
|
||||
87.898 422.527 83.77 422.527 78.68 c 422.527 73.586 437.289 69.461
|
||||
455.496 69.461 c 473.703 69.461 488.465 73.586 488.465 78.68 c h
|
||||
488.465 78.68 m S Q
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
487.98 30.062 m 487.98 78.68 l S Q
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
422.602 29.785 m 422.602 78.398 l S Q
|
||||
BT
|
||||
9.6 0 0 9.6 441.005374 347.123721 Tm
|
||||
/f-0-0 1 Tf
|
||||
[<25>167<02>2<14>1<0f>]TJ
|
||||
0 -1.25 Td
|
||||
[<120a>1<1f05>-1<15>-2<05>-1<06>]TJ
|
||||
ET
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
422.809 52.973 m 353.52 73.09 l S Q
|
||||
359.664 326.964 m 361.848 330.929 l 351.984 324.733 l 363.629 324.784 l
|
||||
359.664 326.964 l h
|
||||
359.664 326.964 m f*
|
||||
0.768277 w
|
||||
q 1 0.290323 0.290323 -1 0 398.268463 cm
|
||||
312.613 162.063 m 315.689 158.991 l 304.933 162.064 l 315.686 165.135 l
|
||||
312.613 162.063 l h
|
||||
312.613 162.063 m S Q
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
282.879 22.918 m 282.32 51.973 l S Q
|
||||
282.441 352.694 m 279.305 355.956 l 282.289 344.694 l 285.703 355.831 l
|
||||
282.441 352.694 l h
|
||||
282.441 352.694 m f*
|
||||
0.799852 w
|
||||
q 0.0192309 1 1 -0.0192309 0 398.268463 cm
|
||||
-40.128 283.213 m -36.928 280.015 l -48.128 283.215 l -36.93 286.413 l
|
||||
-40.128 283.213 l h
|
||||
-40.128 283.213 m S Q
|
||||
BT
|
||||
9.6 0 0 9.6 199.049588 390.974713 Tm
|
||||
/f-0-0 1 Tf
|
||||
[<2d24>-1<02>2<05>-1<071d>-2<0902>2<140a>1<2e>-1<0a14>1<15>-2<0e>-1<0a>1<04
|
||||
10>]TJ
|
||||
0 -1.25 Td
|
||||
[<0c26>-1<04>1<05>16<230724>-1<0a>1<27>-1<02>2<2907>-1<03>2<02>2<03>1<04
|
||||
050607>]TJ
|
||||
10.234375 0 Td
|
||||
[<240a2702>1<2907>-1<15>-2<240902>2<140e0705>-1<15>-3<0e0a04>1<29>-1<07
|
||||
02>2<0e>]TJ
|
||||
10.169922 0 Td
|
||||
<1420>Tj
|
||||
ET
|
||||
1.6 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
327.57 265.453 m 288.457 265.453 l S Q
|
||||
321.172 132.815 m 317.973 129.612 l 329.172 132.815 l 317.973 136.015 l
|
||||
321.172 132.815 l h
|
||||
321.172 132.815 m f*
|
||||
0.8 w
|
||||
q -1 -0.000000000000000122 -0.000000000000000122 1 0 398.268463 cm
|
||||
-321.172 -265.453 m -317.973 -268.656 l -329.172 -265.453 l -317.973
|
||||
-262.254 l -321.172 -265.453 l h
|
||||
-321.172 -265.453 m S Q
|
||||
294.855 132.815 m 298.055 136.015 l 286.855 132.815 l 298.055 129.612 l
|
||||
294.855 132.815 l h
|
||||
294.855 132.815 m f*
|
||||
q 1 -0.000000000000000122 -0.000000000000000122 -1 0 398.268463 cm
|
||||
294.855 265.453 m 298.055 262.254 l 286.855 265.453 l 298.055 268.656 l
|
||||
294.855 265.453 l h
|
||||
294.855 265.453 m S Q
|
||||
1.370434 w
|
||||
q 1 0 0 -1 0 398.268463 cm
|
||||
219.727 282.898 m 219.727 328.188 l S Q
|
||||
219.727 75.561 m 216.984 78.304 l 219.727 68.71 l 222.465 78.304 l
|
||||
219.727 75.561 l h
|
||||
219.727 75.561 m f*
|
||||
0.685217 w
|
||||
q -0.000000000000000061 1 1 0.000000000000000061 0 398.268463 cm
|
||||
-322.707 219.727 m -319.965 216.984 l -329.559 219.727 l -319.965
|
||||
222.465 l -322.707 219.727 l h
|
||||
-322.707 219.727 m S Q
|
||||
Q
|
||||
showpage
|
||||
%%Trailer
|
||||
count op_count sub {pop} repeat
|
||||
countdictstack dict_count sub {end} repeat
|
||||
cairo_eps_state restore
|
||||
%%EOF
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 51 KiB |
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 69 KiB |
Binary file not shown.
|
|
@ -0,0 +1,14 @@
|
|||
set terminal pdf dashed
|
||||
set output "../Freepdk_Read_Access_time.pdf"
|
||||
set palette color
|
||||
set xlabel "Total Size (Kbit)"
|
||||
set ylabel "Read Access time (ns)"
|
||||
set key below
|
||||
plot 'freepdk45_timing.dat' using ($1/1024):2 with line title '16-bit word' lt 0 lw 5 lc 0 ,\
|
||||
'freepdk45_timing.dat' using ($1/1024):2 with points title '' lt 0 lw 5 lc 0 ,\
|
||||
'freepdk45_timing.dat' using ($1/1024):3 with line title '32-bit word' lt 1 lw 5 lc 1 ,\
|
||||
'freepdk45_timing.dat' using ($1/1024):3 with points title '' lt 1 lw 5 lc 1 ,\
|
||||
'freepdk45_timing.dat' using ($1/1024):4 with line title '64-bit word' lt 2 lw 5 lc 2 ,\
|
||||
'freepdk45_timing.dat' using ($1/1024):4 with points title '' lt 2 lw 5 lc 2 ,\
|
||||
'freepdk45_timing.dat' using ($1/1024):5 with line title '128-bit word' lt 3 lw 5 lc 3 ,\
|
||||
'freepdk45_timing.dat' using ($1/1024):5 with points title '' lt 3 lw 5 lc 3
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
2048 0.861 1.02 0.86 1.076
|
||||
8192 1.32 1.33 1.5 1.34
|
||||
32768 1.8 1.83 1.9 2.01
|
||||
131072 2.2 2.6 6.75 9.86
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
set terminal pdf dashed
|
||||
set output "../Scn3me_Read_Access_time.pdf"
|
||||
set palette color
|
||||
set xlabel "Total Size (Kbit)"
|
||||
set ylabel "Read Access time (ns)"
|
||||
set key below
|
||||
plot 'scn3me_timing.dat' using ($1/1024):2 with line title '16-bit word' lt 0 lw 5 lc 0 ,\
|
||||
'scn3me_timing.dat' using ($1/1024):2 with points title '' lt 0 lw 5 lc 0 ,\
|
||||
'scn3me_timing.dat' using ($1/1024):3 with line title '32-bit word' lt 1 lw 5 lc 1 ,\
|
||||
'scn3me_timing.dat' using ($1/1024):3 with points title '' lt 1 lw 5 lc 1 ,\
|
||||
'scn3me_timing.dat' using ($1/1024):4 with line title '64-bit word' lt 2 lw 5 lc 2 ,\
|
||||
'scn3me_timing.dat' using ($1/1024):4 with points title '' lt 2 lw 5 lc 2 ,\
|
||||
'scn3me_timing.dat' using ($1/1024):5 with line title '128-bit word' lt 3 lw 5 lc 3 ,\
|
||||
'scn3me_timing.dat' using ($1/1024):5 with points title '' lt 3 lw 5 lc 3
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
2048 9.42 11.82 14.81 22.9
|
||||
8192 8.25 16.04 19.24 23.12
|
||||
32768 11.69 14.7 23.82 30.75
|
||||
131072 12.7 18.21 30.25 44.95
|
||||
Binary file not shown.
|
|
@ -0,0 +1,658 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="797.85938"
|
||||
height="399.22998"
|
||||
id="svg3956"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="timing_read.svg">
|
||||
<defs
|
||||
id="defs3958">
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path4694"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path4697"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-5"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-5"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-6"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-6"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-7"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-7"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-8"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-0"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-3"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-8"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-5"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-3"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-3-5"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-8-3"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-5-2"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-3-8"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-54"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-70"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-9"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-9"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-2"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-0"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-51"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-8"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-33"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-90"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-7"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.2291699"
|
||||
inkscape:cx="732.4176"
|
||||
inkscape:cy="166.50869"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="751"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3964"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true"
|
||||
originx="66.859375px"
|
||||
originy="-225.09817px" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata3961">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(66.859375,-428.03404)">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 90,436.27609 0,0 0,0 100,0 10,50 100,0 10,-50 100,0 10,50 100,0 10,-50 100,0 10,50 90,0"
|
||||
id="path3966"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 90,546.27609 150,0 10,50 110,0 10,-50 90,0 10,50 100,0 10,-50 150,0"
|
||||
id="path4501"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 90,611.27609 150,0 10,50 110,0 10,-50 90,0 10,50 100,0 10,-50 150,0"
|
||||
id="path4501-4"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<g
|
||||
id="g4534"
|
||||
transform="translate(0,3.9139099)">
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4521"
|
||||
d="m 140,354.09448 590,0"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4523"
|
||||
d="m 130,404.09448 10,-50"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4525"
|
||||
d="m 130,404.09448 -40,0"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</g>
|
||||
<g
|
||||
id="g4529"
|
||||
transform="translate(-20,68.91391)">
|
||||
<path
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4497"
|
||||
d="m 110,114.09448 150,0 10,50 210,0 10,-50 200,0 10,50 50,0"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4499"
|
||||
d="m 110,164.09448 150,0 10,-50 210,0 10,50 200,0 10,-50"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4527"
|
||||
d="m 700,114.09448 50,0"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.3223573;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2.64471461, 2.64471461;stroke-dashoffset:0"
|
||||
d="m 305,429.92915 0,388.5842"
|
||||
id="path4566"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.32278144;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2.64556289, 2.64556289;stroke-dashoffset:0"
|
||||
d="m 524,428.69543 0,388.8335"
|
||||
id="path4566-2"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-12"
|
||||
y="470.27609"
|
||||
id="text4586"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4588"
|
||||
x="-12"
|
||||
y="470.27609"
|
||||
style="font-size:32px">CLK</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-26"
|
||||
y="527.27606"
|
||||
id="text4590"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4592"
|
||||
x="-26"
|
||||
y="527.27606"
|
||||
style="font-size:32px">ADDR</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-18"
|
||||
y="576.27612"
|
||||
id="text4594"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4596"
|
||||
x="-18"
|
||||
y="576.27612"
|
||||
style="font-size:32px">CSb</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-18"
|
||||
y="647.27612"
|
||||
id="text4598"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4600"
|
||||
x="-18"
|
||||
y="647.27612"
|
||||
style="font-size:32px">OEb</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-26"
|
||||
y="696.27612"
|
||||
id="text4602"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4604"
|
||||
x="-26"
|
||||
y="696.27612"
|
||||
style="font-size:32px">WEb</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-70"
|
||||
y="805.46796"
|
||||
id="text4606"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4608"
|
||||
x="-70"
|
||||
y="805.46796"
|
||||
style="font-size:32px">DATA OUT</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="340"
|
||||
y="527.27606"
|
||||
id="text4610"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4612"
|
||||
x="340"
|
||||
y="527.27606"
|
||||
style="font-size:32px">A0</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="560"
|
||||
y="527.27606"
|
||||
id="text4614"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4616"
|
||||
x="560"
|
||||
y="527.27606"
|
||||
style="font-size:32px">A1</tspan></text>
|
||||
<g
|
||||
id="g4620"
|
||||
transform="matrix(1.0000534,0,0,0.88368291,-20.022979,120.16312)">
|
||||
<g
|
||||
id="g4529-7"
|
||||
transform="translate(0,320)">
|
||||
<path
|
||||
sodipodi:nodetypes="cccccccc"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 110,114.09448 80,0 10,50 289.14867,0 10,-50 192.1027,0 10,50 48.74863,0"
|
||||
id="path4497-7"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="translate(0,308.2677)" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 110,164.09448 80,0 10,-50 289.14867,0 10,50 192.1027,0 10,-50"
|
||||
id="path4499-4"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="translate(0,308.2677)" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 700,114.09448 30,0"
|
||||
id="path4527-5"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="translate(0,308.2677)" />
|
||||
</g>
|
||||
<path
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4618"
|
||||
d="m 650,434.09448 100,0"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:16px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||
x="527.25018"
|
||||
y="811.45892"
|
||||
id="text4652"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4654"
|
||||
x="527.25018"
|
||||
y="811.45892"
|
||||
style="font-size:32px">D0</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:16px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||
x="684.11365"
|
||||
y="808.77997"
|
||||
id="text4656"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4658"
|
||||
x="684.11365"
|
||||
y="808.77997"
|
||||
style="font-size:32px">D1</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 250,574.27609 50,0"
|
||||
id="path4682"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 250,637.27609 50,0"
|
||||
id="path4682-2"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 310,637.27609 50,0"
|
||||
id="path4682-2-2"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 310,574.27609 50,0"
|
||||
id="path4682-2-3"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="250"
|
||||
y="627.27612"
|
||||
id="text5843"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5845"
|
||||
x="250"
|
||||
y="627.27612"
|
||||
style="font-size:18px">Setup</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="314"
|
||||
y="629.27612"
|
||||
id="text5847"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5849"
|
||||
x="314"
|
||||
y="629.27612"
|
||||
style="font-size:18px">Hold</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="248"
|
||||
y="566.27612"
|
||||
id="text5843-2"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5845-4"
|
||||
x="248"
|
||||
y="566.27612"
|
||||
style="font-size:18px">Setup</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="314"
|
||||
y="568.27612"
|
||||
id="text5847-7"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5849-7"
|
||||
x="314"
|
||||
y="568.27612"
|
||||
style="font-size:18px">Hold</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Mstart-2);marker-end:url(#Arrow1Mend-51);stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 310.93011,798.61859 157.28627,0"
|
||||
id="path6523"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="349.70599"
|
||||
y="794.34436"
|
||||
id="text7149"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7151"
|
||||
x="352.57123"
|
||||
y="794.34436"
|
||||
style="font-size:18px;text-align:center;text-anchor:middle">Read </tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="349.70599"
|
||||
y="816.84436"
|
||||
style="font-size:18px;text-align:center;text-anchor:middle"
|
||||
id="tspan3253">Delay</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="250"
|
||||
y="515.27612"
|
||||
id="text5843-2-3"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5845-4-5"
|
||||
x="250"
|
||||
y="515.27612"
|
||||
style="font-size:18px">Setup</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 250,521.27609 50,0"
|
||||
id="path4682-4"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 90,771.27609 220,0 10,-50 100,0 10,50 100,0 10,-50 100,0 10,50 80,0"
|
||||
id="path3080"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-26"
|
||||
y="755.27612"
|
||||
id="text3946"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3948"
|
||||
x="-26"
|
||||
y="755.27612"
|
||||
style="font-size:32px">SCLK</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.13527045;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:0.27054089, 0.27054089;stroke-dashoffset:0"
|
||||
d="m 524.00609,826.54582 0,0.65056"
|
||||
id="path4004"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
|
@ -0,0 +1,858 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="829.35938"
|
||||
height="452.07816"
|
||||
id="svg3956"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="timing_write.svg">
|
||||
<defs
|
||||
id="defs3958">
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path4694"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
id="path4697"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)"
|
||||
inkscape:connector-curvature="0" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-5"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-5"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-6"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-6"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-7"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-7"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-8"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-0"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-3"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-8"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-5"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-3"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-3-5"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-8-3"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-5-2"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-3-8"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-54"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-70"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-9"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-9"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-2"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-0"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-51"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-8"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-33"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-1"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-90"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-7"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-4"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-703"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-84"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-70"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-32"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-73"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-4"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-76"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mstart"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mstart-32-6"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4694-73-8"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(0.4,0,0,0.4,4,0)" />
|
||||
</marker>
|
||||
<marker
|
||||
inkscape:stockid="Arrow1Mend"
|
||||
orient="auto"
|
||||
refY="0"
|
||||
refX="0"
|
||||
id="Arrow1Mend-4-3"
|
||||
style="overflow:visible">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4697-76-9"
|
||||
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
|
||||
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
|
||||
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
|
||||
</marker>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="723.39201"
|
||||
inkscape:cy="156.33517"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="751"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3964"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true"
|
||||
originx="98.359375px"
|
||||
originy="-167.25px" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata3961">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(98.359375,-433.03404)">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 90,436.375 0,0 0,0 100,0 10,50 100,0 10,-50 100,0 10,50 100,0 10,-50 100,0 10,50 90,0"
|
||||
id="path3966"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 90,546.375 150,0 10,50 110,0 10,-50 90,0 10,50 100,0 10,-50 150,0"
|
||||
id="path4501"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 90,601.375 150,0 10,50 110,0 10,-50 90,0 10,50 100,0 10,-50 150,0"
|
||||
id="path4501-4"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<g
|
||||
id="g4534"
|
||||
transform="translate(0,4.01282)">
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4521"
|
||||
d="m 140,354.09448 590,0"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4523"
|
||||
d="m 130,404.09448 10,-50"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
<path
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4525"
|
||||
d="m 130,404.09448 -40,0"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
|
||||
</g>
|
||||
<g
|
||||
id="g4529"
|
||||
transform="translate(-20,69.01282)">
|
||||
<path
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4497"
|
||||
d="m 110,114.09448 150,0 10,50 210,0 10,-50 200,0 10,50 50,0"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4499"
|
||||
d="m 110,164.09448 150,0 10,-50 210,0 10,50 200,0 10,-50"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4527"
|
||||
d="m 700,114.09448 50,0"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2,2;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 305,433.69543 0,448.8335"
|
||||
id="path4566"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2,2;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 524,435.01508 0,449.46746"
|
||||
id="path4566-2"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-29"
|
||||
y="476.375"
|
||||
id="text4586"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4588"
|
||||
x="-29"
|
||||
y="476.375"
|
||||
style="font-size:32px">CLK</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-47"
|
||||
y="527.375"
|
||||
id="text4590"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4592"
|
||||
x="-47"
|
||||
y="527.375"
|
||||
style="font-size:32px">ADDR</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-38"
|
||||
y="593.375"
|
||||
id="text4594"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4596"
|
||||
x="-38"
|
||||
y="593.375"
|
||||
style="font-size:32px">CSb</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-39"
|
||||
y="705.375"
|
||||
id="text4598"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4600"
|
||||
x="-39"
|
||||
y="705.375"
|
||||
style="font-size:32px">OEb</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-38"
|
||||
y="647.375"
|
||||
id="text4602"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4604"
|
||||
x="-38"
|
||||
y="647.375"
|
||||
style="font-size:32px">WEb</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-59"
|
||||
y="819.375"
|
||||
id="text4606"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4608"
|
||||
x="-59"
|
||||
y="819.375"
|
||||
style="font-size:32px">DATA IN</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="337"
|
||||
y="525.375"
|
||||
id="text4610"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4612"
|
||||
x="337"
|
||||
y="525.375"
|
||||
style="font-size:32px">A0</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="549"
|
||||
y="527.375"
|
||||
id="text4614"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4616"
|
||||
x="549"
|
||||
y="527.375"
|
||||
style="font-size:32px">A1</tspan></text>
|
||||
<g
|
||||
transform="translate(-70,355.01282)"
|
||||
id="g4529-7">
|
||||
<path
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4497-7"
|
||||
d="m 270,164.09448 0,0 -123.5,0 273.5,0 10,-50 120,0 10,50 80,0"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="cccccccc" />
|
||||
<path
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4499-4"
|
||||
d="m 270,114.09448 0,0 -123,0 273,0 10,50 120,0 10,-50"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
<path
|
||||
transform="translate(0,308.2677)"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4527-5"
|
||||
d="m 700,114.09448 0,0"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
sodipodi:nodetypes="cc" />
|
||||
</g>
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 580,777.375 0,0"
|
||||
id="path4618"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:16px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||
x="399.46103"
|
||||
y="825.74933"
|
||||
id="text4652"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4654"
|
||||
x="399.46103"
|
||||
y="825.74933"
|
||||
style="font-size:20px">D0</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:16px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#666666;fill-opacity:1;stroke:none"
|
||||
x="609"
|
||||
y="814.375"
|
||||
id="text4656"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4658"
|
||||
x="609"
|
||||
y="814.375"
|
||||
style="font-size:32px;fill:#000000">D1</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 250,574.375 50,0"
|
||||
id="path4682"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 250,629.375 50,0"
|
||||
id="path4682-2"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 310,629.375 50,0"
|
||||
id="path4682-2-2"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 310,574.375 50,0"
|
||||
id="path4682-2-3"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="249"
|
||||
y="621.375"
|
||||
id="text5843"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5845"
|
||||
x="249"
|
||||
y="621.375"
|
||||
style="font-size:18px">Setup</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="314.25"
|
||||
y="623.625"
|
||||
id="text5847"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5849"
|
||||
x="314.25"
|
||||
y="623.625"
|
||||
style="font-size:18px">Hold</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="246"
|
||||
y="565.375"
|
||||
id="text5843-2"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5845-4"
|
||||
x="246"
|
||||
y="565.375"
|
||||
style="font-size:18px">Setup</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="312.25"
|
||||
y="567.125"
|
||||
id="text5847-7"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5849-7"
|
||||
x="312.25"
|
||||
y="567.125"
|
||||
style="font-size:18px">Hold</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="250"
|
||||
y="515.375"
|
||||
id="text5843-2-3"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5845-4-5"
|
||||
x="250"
|
||||
y="515.375"
|
||||
style="font-size:18px">Setup</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 251,523.375 50,0"
|
||||
id="path4682-4"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 90,771.375 220,0 10,-50 100,0 10,50 100,0 10,-50 100,0 10,50 80,0"
|
||||
id="path3080"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cccccccccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-61"
|
||||
y="761.375"
|
||||
id="text3946"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3948"
|
||||
x="-61"
|
||||
y="761.375"
|
||||
style="font-size:32px">WD_EN</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2,2;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m 416,435.36218 0,445"
|
||||
id="path3969"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0"
|
||||
d="m 372,769.375 0,50"
|
||||
id="path4382"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="opacity:0.31687245;color:#000000;fill:#999999;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d=""
|
||||
id="path4390"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="translate(0,308.2677)" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 360.23744,803.99543 50,0"
|
||||
id="path4682-2-9"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:16px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||
x="361.44366"
|
||||
y="797.12195"
|
||||
id="text5843-3"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5845-5"
|
||||
x="361.44366"
|
||||
y="797.12195"
|
||||
style="font-size:18px">Setup</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.29484856;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
|
||||
d="m 419.70711,804.4651 58.21641,0"
|
||||
id="path4682-2-2-0"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:16px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||
x="427.04504"
|
||||
y="799.76324"
|
||||
id="text5847-2"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan5849-8"
|
||||
x="427.04504"
|
||||
y="799.76324"
|
||||
style="font-size:18px">Hold</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 580,777.375 -90,0"
|
||||
id="path3165"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 570,827.375 10,-50 120,0 10,50 22,0"
|
||||
id="path4060"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 570,777.375 10,50 120,0 10,-50 22,0"
|
||||
id="path4062"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.31687245;fill:#666666;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;marker:none;enable-background:accumulate"
|
||||
d="m 490.11279,820.40644 c -3.43878,-16.01305 -3.5001,-17.72303 -1.07757,-30.05012 l 2.29927,-11.69988 38.74001,0 38.74001,0 2.2939,11.67258 2.29391,11.67258 -2.27882,11.82742 -2.27882,11.82742 -38.80223,0 -38.80223,0 -1.12743,-5.25 z"
|
||||
id="path4064"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 77,832.375 423,0 10,50 210,0 10,-50"
|
||||
id="path4068"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 77.5,882.375 422.5,0 10,-50 210,0 10,50"
|
||||
id="path4070"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:16px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||
x="580"
|
||||
y="866.375"
|
||||
id="text4652-4"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4654-9"
|
||||
x="580"
|
||||
y="866.375"
|
||||
style="font-size:32px">D0</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:16px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
|
||||
x="-9"
|
||||
y="870.375"
|
||||
id="text4114"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
x="-9"
|
||||
y="870.375"
|
||||
id="tspan4118"
|
||||
style="font-size:32px;text-align:center;text-anchor:middle">X Mem Cell</tspan></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Mstart-32);marker-end:url(#Arrow1Mend-4)"
|
||||
d="m 420,858.375 80,0"
|
||||
id="path4120"
|
||||
inkscape:connector-curvature="0" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:12px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none"
|
||||
x="459.04199"
|
||||
y="854.375"
|
||||
id="text4592"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4594"
|
||||
x="461.58887"
|
||||
y="854.375"
|
||||
style="font-size:16px;text-align:center;text-anchor:middle">Write </tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="459.04199"
|
||||
y="874.375"
|
||||
style="font-size:16px;text-align:center;text-anchor:middle"
|
||||
id="tspan3259">Delay</tspan></text>
|
||||
<rect
|
||||
style="opacity:0;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#0000ff;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||
id="rect3230"
|
||||
width="89"
|
||||
height="32"
|
||||
x="135.64062"
|
||||
y="556.38782" />
|
||||
<flowRoot
|
||||
xml:space="preserve"
|
||||
id="flowRoot3232"
|
||||
style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
|
||||
transform="translate(-98.359375,322.375)"><flowRegion
|
||||
id="flowRegion3234"><rect
|
||||
id="rect3236"
|
||||
width="1010"
|
||||
height="85"
|
||||
x="-103"
|
||||
y="330" /></flowRegion><flowPara
|
||||
id="flowPara3238" /></flowRoot> </g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 33 KiB |
|
|
@ -0,0 +1,214 @@
|
|||
\section{Implementation}
|
||||
\label{sec:implementation}
|
||||
|
||||
% source langauge
|
||||
OpenRAM's methodology is implemented using an object-oriented approach
|
||||
in the Python programming language. Python is a simple, yet powerful
|
||||
language that is easy to learn and very human-readable. Moreover, Python
|
||||
enables portability to most operating systems. OpenRAM has no additional
|
||||
dependencies except a DRC/LVS tool, but that is disabled with a
|
||||
warning if the tools are unavailable.
|
||||
|
||||
% portability of tools and technologies
|
||||
In addition to system portability, OpenRAM is also translatable across
|
||||
numerous process technologies. This is accomplished by using
|
||||
generalized routines to generate the memory based on common features
|
||||
across all technologies. To facilitate user modification and
|
||||
technology interoperability, OpenRAM provides a reference implementation in
|
||||
$45$nm FreePDK45~\cite{4231502} and a fabricable option using the
|
||||
MOSIS Scalable CMOS (SCMOS) design rules~\cite{scmos}. FreePDK45 uses
|
||||
many design rules found in modern technologies, but is non-fabricable,
|
||||
while SCMOS enables fabrication of designs using the MOSIS foundry
|
||||
services. SCMOS is not confidential and an implementation using it is
|
||||
included, however, it does not include many advanced DSM design
|
||||
rules. OpenRAM has also been ported to other commercial technologies,
|
||||
but these are not directly included due to licensing issues.
|
||||
|
||||
% methodology overview
|
||||
OpenRAM's framework is divided into \enquote{front-end} and \enquote{back-end}
|
||||
methodologies as shown in Figure~\ref{fig:methodology}. The front-end
|
||||
has the compiler and the characterizer. The compiler generates
|
||||
SPICE models and its GDSII layouts based on user inputs. The
|
||||
characterizer calls a SPICE simulator to produce timing and power
|
||||
results. The back-end uses a spice netlist extracted from the GDSII
|
||||
layout using to generate annotated timing and power models.
|
||||
|
||||
\begin{figure}[tb]
|
||||
\centering
|
||||
\includegraphics[width=8cm]{./figs/methodology.pdf}
|
||||
\caption{Overall Compilation and Characterization Methodology}
|
||||
\label{fig:methodology}
|
||||
\end{figure}
|
||||
|
||||
%\fixme{We actually dont have back end done yet.}
|
||||
|
||||
\subsection{Base Data Structures}
|
||||
|
||||
The design modules in OpenRAM are derived from the {\it design} class
|
||||
(design.py). The design class has a name, a SPICE model (netlist), and
|
||||
a layout. Both the SPICE model and the layout inherit their
|
||||
capabilities from a hierarchical class. The design class also provides
|
||||
inherited functions to perform DRC and LVS verification of any
|
||||
sub-design for hierarchical debugging.
|
||||
|
||||
The design class derives from the {\it spice} class
|
||||
(hierarchy\_\allowbreak spice.py) which has a data structure to
|
||||
maintain the circuit hierarchy. This class maintains the design
|
||||
instances, their pins, and their connections as well as helper
|
||||
functions to maintain the structure and connectivity of the circuit
|
||||
hierarchy.
|
||||
|
||||
The design class also derives from a {\it layout} class (hierarchy\_layout.py).
|
||||
This class has a list of physical instances of sub-modules in the layout and
|
||||
a structure for simple objects such as shapes and labels in the
|
||||
current hierarchy level. In addition, there are helper functions that
|
||||
maintain the physical layout structures.
|
||||
|
||||
OpenRAM has an integrated, custom GDSII library to read, write, and
|
||||
manipulate GDSII files. The library, originally called
|
||||
GdsMill~\cite{gdsmill}, has been modified, debugged, and extended for
|
||||
OpenRAM. Full rights were given to include the GdsMill source with
|
||||
OpenRAM, but to make the interfacing easier and porting to other
|
||||
physical layout databases possible, OpenRAM implements a {\it
|
||||
geometry} wrapper class (geometry.py) that abstracts the GdsMill
|
||||
library.
|
||||
|
||||
\subsection{Technology and Tool Portability}
|
||||
|
||||
% technology overview
|
||||
OpenRAM is technology-independent by using a technology directory that
|
||||
includes the technology's specific information, rules, and library
|
||||
cells. Technology parameters such as the design rule check (DRC) rules
|
||||
and the GDS layer map are required to ensure that the dynamically
|
||||
generated designs are DRC clean. Custom designed library cells such as
|
||||
the memory cell and the sense amplifier are also placed in this
|
||||
directory. A very simple design rule parameter file has the most
|
||||
important design rules for constructing basic interconnect and
|
||||
transistor devices. FreePDK45 and SCMOS reference technologies are provided.
|
||||
|
||||
% hand-optimized cells
|
||||
OpenRAM uses some custom-designed library primitives as technology
|
||||
input. Since density is extremely important, the following cells are
|
||||
pre-designed in each technology: 6T cell, sense amplifier,
|
||||
master-slave flip-flop, tri-state gate, and write driver. All other
|
||||
cells are generated on-the-fly using parameterizable transistor and
|
||||
gate primitives.
|
||||
|
||||
% technology specific features
|
||||
OpenRAM can be used for various technologies since it creates the
|
||||
basic components of memory designs that are common over these
|
||||
technologies. For technologies that have specific design requirements,
|
||||
such as specialized well contacts, the user can include call-back
|
||||
helper functions in the technology directory. This is done so that the
|
||||
main compiler remains free of dependencies to specific technologies.
|
||||
|
||||
% DRC and LVS
|
||||
OpenRAM has two functions that provide a wrapper interface with DRC
|
||||
and LVS tools. These two functions perform DRC and LVS using the GDSII
|
||||
layout and SPICE netlist files. Since each DRC and LVS tool has
|
||||
different output, this routine is customized per tool to parse DRC/LVS
|
||||
reports and return the number of errors while also outputting debug
|
||||
information. These routines allow flexibility of any DRC/LVS tool,
|
||||
but the default implementation calls Calibre nmDRC and nmLVS. In
|
||||
OpenRAM, both DRC and LVS are performed at all levels of the design
|
||||
hierarchy to enhance bug tracking. DRC and LVS can be disabled for
|
||||
improved run-time or if tool licenses are not available.
|
||||
|
||||
\subsection{Class Hierarchy}
|
||||
|
||||
\subsubsection{High-Level Classes}
|
||||
|
||||
The {\it openram} class (openram.py) organizes execution and
|
||||
instantiates a single memory design using the {\it sram} class. It
|
||||
accepts user-provided parameters to generate the design, performs the
|
||||
optional extraction, performs characterization, and saves the
|
||||
resulting design files.
|
||||
|
||||
The {\it sram} class (sram.py) decides the appropriate internal parameter
|
||||
dependencies shown in Table~\ref{table:variables}. They are dependent
|
||||
on the user-desired data word size, number of words, and number of banks.
|
||||
It is responsible for instantiation of the single control logic module which
|
||||
controls the SRAM banks. The control logic ensures that only one bank
|
||||
is active in a given address range.
|
||||
|
||||
The {\it bank} class (bank.py) does the bulk of the non-control memory layout. It
|
||||
instantiates $1$, $2$, or $4$ bit-cell arrays and coordinates the row and column
|
||||
address decoders along with their pre-charge, sense amplifiers, and input/output
|
||||
data flops.
|
||||
|
||||
\begin{table}
|
||||
\centering
|
||||
\caption{Dependencies required for sub-modules}
|
||||
\begin{tabular}{|c|l|} \hline
|
||||
Variable&Equation \\ \hline
|
||||
\texttt{Total Bits} & $word\_size*num\_words$ \\ \hline
|
||||
\texttt{Words Per Row} & $\sqrt(num\_words)/word\_size$ \\ \hline
|
||||
\texttt{Num of Rows} & $num\_words/words\_per\_row$ \\ \hline
|
||||
\texttt{Num of Cols} & $words\_per\_row*word\_size$ \\ \hline
|
||||
\texttt{Col Addr Size} & $\log_2(words\_per\_row)$ \\ \hline
|
||||
\texttt{Row Addr Size} & $\log_2(num\_of\_rows)$ \\ \hline
|
||||
\texttt{Total Addr Size} & $row\_addr\_size + col\_addr\_size$ \\ \hline
|
||||
\texttt{Data Size} & $word\_size$ \\ \hline
|
||||
\texttt{Num of Bank} & $num\_banks$ \\ \hline
|
||||
\end{tabular}
|
||||
\label{table:variables}
|
||||
\end{table}
|
||||
|
||||
\subsubsection{Block Classes}
|
||||
|
||||
Every other block in the memory design has a class for its base cell
|
||||
(e.g., sense\_amplifier.py) and an array class (e.g.,
|
||||
sense\_amplifier\_array.py) that is responsible for tiling the base
|
||||
cell. Each class is responsible for physically placing and logically
|
||||
connecting its own sub-circuits while passing its dimensions and port
|
||||
locations up to higher-level modules.
|
||||
|
||||
\subsubsection{Low-Level Classes}
|
||||
|
||||
OpenRAM provides parameterized transistor and logic gate
|
||||
classes that help with technology portability. These classes generate
|
||||
a technology-specific transistor and simple logic gate layouts so that
|
||||
many modules do not rely on library cells. It is also used
|
||||
when a module such as the write driver needs transistor sizing
|
||||
to optimize performance. The parameterized transistor (ptx.py) generates a
|
||||
basic transistor of specified type and size. The parameterized
|
||||
transistor class is used to provide several parameterized gates
|
||||
including pinv.py, nand2.py, nand3.py, and nor2.py.
|
||||
|
||||
% FIXME
|
||||
% crude fix to preven widow Section
|
||||
%\clearpage
|
||||
\subsection{Characterization}
|
||||
|
||||
% overview
|
||||
OpenRAM includes a memory characterizer that measures the timing and
|
||||
power characteristics through SPICE simulation. The
|
||||
characterizer has four main stages: generating the SPICE stimulus,
|
||||
running the circuit simulations, parsing the simulator's output, and
|
||||
producing the characteristics in a Liberty (.lib) file.
|
||||
|
||||
% standard format of stimulus
|
||||
The stimulus is written in standard SPICE format and can be used with
|
||||
any simulator that supports this. The stimulus only uses the
|
||||
interface of the memory (e.g., bi-directional data bus, address bus,
|
||||
and control signals) to perform \enquote{black box} timing measurements.
|
||||
|
||||
% what is measured and how
|
||||
Results from simulations are used to produce the average power,
|
||||
setup/hold times, and timing delay of the memory design. Setup and
|
||||
hold times are obtained by analyzing the flip-flop library cell
|
||||
because OpenRAM uses a completely synchronous input interface. The
|
||||
setup time, hold time, and delay are found using a fast bisection
|
||||
search.
|
||||
|
||||
\subsection{Unit Tests}
|
||||
|
||||
Probably the most important feature of OpenRAM is the set of thorough
|
||||
regression tests implemented with the Python unit test framework.
|
||||
These unit tests allow users to add features and easily verifying if
|
||||
functionality is broken. The tests also work in multiple technologies
|
||||
so they can guide users when porting to new technologies. Every module
|
||||
has its own regression test and there are also regression tests for
|
||||
memory functionality, verifying library cells, timing
|
||||
characterization, and technology verification.
|
||||
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
\section{Introduction}
|
||||
\label{sec:introduction}
|
||||
|
||||
% why memory compilers are important
|
||||
Static Random Access Memories (SRAMs) have become a standard component
|
||||
embedded in all System-on-Chip (SoC), Application-Specific Integrated
|
||||
Circuit (ASIC), and micro-processor designs. Their wide application
|
||||
leads to a variety of requirements in circuit design and memory
|
||||
configuration. However, manual design is
|
||||
too time consuming. The
|
||||
regular structure of memories leads well to automation that produces
|
||||
size and configuration variations quickly, but developing this with
|
||||
multiple technologies and tool methodologies is challenging. In
|
||||
addition, memory designs play a significant role in overall system
|
||||
performance and costs, so optimization is important. Thus, a memory
|
||||
compiler is a critical tool.
|
||||
|
||||
% why academics need memory compilers
|
||||
Most academic ICs design methodologies are limited by the availability
|
||||
of memories. Many standard-cell Process Design Kits (PDKs) are
|
||||
available from foundries and vendors, but these PDKs frequently do not
|
||||
come with memory arrays or memory compilers. If a memory compiler is
|
||||
freely available, it often only supports a generic process technology
|
||||
that is not fabricable. Due to academic funding restrictions,
|
||||
commercial industry solutions are often not feasible for
|
||||
researchers. In addition, these commercial solutions are limited in
|
||||
customization of the memory sizes and specific components of the
|
||||
memory. PDKs may have the options to request \enquote{black box}
|
||||
memory models, but these are also not modifiable and have limited
|
||||
available configurations. These restrictions and licensing issues make
|
||||
comparison and experimentation with real world memories impossible.
|
||||
|
||||
% manually designing is time consuming
|
||||
Academic researchers are able to design their own custom memories, but
|
||||
this can be a tedious and time-consuming task and may not be the intended
|
||||
purpose of the research. Frequently, the memory design is the bare
|
||||
minimum that the research project requires,
|
||||
and, because of this, the memory designs are often inferior and are not
|
||||
optimized. In memory research, peripheral circuits are often not
|
||||
considered when comparing memory performance and density. The
|
||||
lack of a customizable compiler makes it difficult for researchers to
|
||||
prototype and verify circuits and methodologies beyond a single row or
|
||||
column of memory cells.
|
||||
|
||||
% what are the goals of OpenRAM
|
||||
The OpenRAM project aims to provide an open-source memory compiler
|
||||
development framework for memories. It provides reference circuit and
|
||||
physical implementations in a generic $45$nm technology and fabricable
|
||||
Scalable CMOS (SCMOS), but it has also been ported to several
|
||||
commercial technology nodes using a simple technology file. OpenRAM
|
||||
also includes a characterization methodology so that it can generate
|
||||
the timing and power characterization results in addition to circuits and
|
||||
layout while remaining independent of specific commercial tools. Most
|
||||
importantly, OpenRAM is completely user-modifiable since all source
|
||||
code is open source at:
|
||||
\begin{center}
|
||||
\url{https://openram.soe.ucsc.edu/}
|
||||
\end{center}
|
||||
|
||||
The remainder of this paper is organized as follows:
|
||||
Section~\ref{sec:background} provides a background on previous memory
|
||||
compilers. Section~\ref{sec:architecture} presents the reference
|
||||
memory architecture in OpenRAM. Section~\ref{sec:implementation}
|
||||
specifically introduces the implementation and main features of the
|
||||
OpenRAM memory compiler. In Section~\ref{sec:results}, an analysis of
|
||||
the area, timing and power is shown for different sizes and
|
||||
technologies of memory. Finally, the paper is summarized in
|
||||
Section~\ref{sec:conclusions}.
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
% This file should be compiled with V2.5 of "sig-alternate.cls" May 2012
|
||||
% This file has been modified by Brian Chen (bchen12@ucsc.edu) for the purpose of simplifying the sections
|
||||
%
|
||||
% This example file demonstrates the use of the 'sig-alternate.cls'
|
||||
% V2.5 LaTeX2e document class file. It is for those submitting
|
||||
% articles to ACM Conference Proceedings WHO DO NOT WISH TO
|
||||
% STRICTLY ADHERE TO THE SIGS (PUBS-BOARD-ENDORSED) STYLE.
|
||||
% The 'sig-alternate.cls' file will produce a similar-looking,
|
||||
% albeit, 'tighter' paper resulting in, invariably, fewer pages.
|
||||
%
|
||||
% ----------------------------------------------------------------------------------------------------------------
|
||||
% This .tex file (and associated .cls V2.5) produces:
|
||||
% 1) The Permission Statement
|
||||
% 2) The Conference (location) Info information
|
||||
% 3) The Copyright Line with ACM data
|
||||
% 4) NO page numbers
|
||||
%
|
||||
% as against the acm_proc_article-sp.cls file which
|
||||
% DOES NOT produce 1) thru' 3) above.
|
||||
%
|
||||
% Using 'sig-alternate.cls' you have control, however, from within
|
||||
% the source .tex file, over both the CopyrightYear
|
||||
% (defaulted to 200X) and the ACM Copyright Data
|
||||
% (defaulted to X-XXXXX-XX-X/XX/XX).
|
||||
% e.g.
|
||||
% \CopyrightYear{2007} will cause 2007 to appear in the copyright line.
|
||||
% \crdata{0-12345-67-8/90/12} will cause 0-12345-67-8/90/12 to appear in the copyright line.
|
||||
%
|
||||
% ---------------------------------------------------------------------------------------------------------------
|
||||
% This .tex source is an example which *does* use
|
||||
% the .bib file (from which the .bbl file % is produced).
|
||||
% REMEMBER HOWEVER: After having produced the .bbl file,
|
||||
% and prior to final submission, you *NEED* to 'insert'
|
||||
% your .bbl file into your source .tex file so as to provide
|
||||
% ONE 'self-contained' source file.
|
||||
%
|
||||
% ================= IF YOU HAVE QUESTIONS =======================
|
||||
% Questions regarding the SIGS styles, SIGS policies and
|
||||
% procedures, Conferences etc. should be sent to
|
||||
% Adrienne Griscti (griscti@acm.org)
|
||||
%
|
||||
% Technical questions _only_ to
|
||||
% Gerald Murray (murray@hq.acm.org)
|
||||
% ===============================================================
|
||||
%
|
||||
% For tracking purposes - this is V2.0 - May 2012
|
||||
% Custom Modified Version - November 2013
|
||||
|
||||
\documentclass{sig-alternate-05-2015}
|
||||
%\RequirePackage[pdftex]{hyperref}
|
||||
\usepackage{comment}
|
||||
\usepackage{graphicx}
|
||||
\usepackage[autostyle]{csquotes}
|
||||
\usepackage{subfigure}
|
||||
|
||||
\newcommand{\fixme}[1]{{\Large FIXME:} {\bf #1}}
|
||||
\newcommand{\todo}[1]{{\bf TODO: {#1}}\\}
|
||||
\newcommand{\note}[1]{{\bf Note:} \{#1\}\\}
|
||||
\newcommand{\comm}[1]{\small{\it{ //{#1}}}}
|
||||
|
||||
|
||||
% --- Author Metadata here ---
|
||||
\conferenceinfo{ICCAD}{International Conference on Computer-Aided Design}
|
||||
%\CopyrightYear{2007} % Allows default copyright year (20XX) to be over-ridden - IF NEED BE.
|
||||
%\crdata{0-12345-67-8/90/01} % Allows default copyright data (0-89791-88-6/97/05) to be over-ridden - IF NEED BE.
|
||||
% --- End of Author Metadata ---
|
||||
|
||||
\title{OpenRAM: An Open-Source Memory Compiler\\
|
||||
\vspace{-0.5cm}\center{\normalsize{Invited Paper}}}
|
||||
%\titlenote{Some Copyright info about OpenRAM??????}}
|
||||
|
||||
\numberofauthors{1}
|
||||
\author{
|
||||
%% TO DAC: Guthaus, Stine, Ataei, Chen, Wu, Sarwar
|
||||
\alignauthor Matthew R. Guthaus$^1$, James E. Stine$^2$, Samira Ataei$^2$, \\Brian Chen$^1$, Bin Wu$^1$, Mehedi Sarwar$^2$ \\
|
||||
\affaddr{$^1$ Department of Computer Engineering, University of California Santa Cruz, Santa Cruz, CA 95064}\\
|
||||
\affaddr\{mrg, bchen12, bwu8\}@ucsc.edu \\
|
||||
\affaddr{$^2$ Electrical and Computer Engineering Department, Oklahoma State University, Stillwater, OK 74078}\\
|
||||
\affaddr\{james.stine, ataei, mehedis\}@okstate.edu}
|
||||
|
||||
%% \alignauthor Matthew Guthaus, Brian Chen, Bin Wu \\
|
||||
%% \affaddr{Department of Computer Engineering} \\
|
||||
%% \affaddr{University of California Santa Cruz} \\
|
||||
%% \affaddr{Santa Cruz, CA 95064, USA} \\
|
||||
%% \affaddr{\{mrg,bchen12,bwu8\}@ucsc.edu}
|
||||
%% \and
|
||||
%% \alignauthor James Stine, Samira Ataei, Mehedi Sarwar \\
|
||||
%% \affaddr{Electrical and Computer Engineering Department} \\
|
||||
%% \affaddr{Oklahoma State University} \\
|
||||
%% \affaddr{Stillwater, OK 74078} \\
|
||||
%% \affaddr{\{james.stine,ataei,XXXX\}@okstate.edu}
|
||||
%%}
|
||||
|
||||
|
||||
\begin{document}
|
||||
|
||||
\CopyrightYear{2016}
|
||||
\setcopyright{acmlicensed}
|
||||
\conferenceinfo{ICCAD '16,}{November 07 - 10, 2016, Austin, TX, USA}
|
||||
\isbn{978-1-4503-4466-1/16/11}\acmPrice{\$15.00}
|
||||
\doi{http://dx.doi.org/10.1145/2966986.2980098}
|
||||
\maketitle
|
||||
|
||||
\input{abstract}
|
||||
|
||||
%\category{J.6}{COMPUTER-AIDED ENGINEERING}{\\Computer-aided design (CAD)}
|
||||
%\terms{Design, Algorithms}
|
||||
%\keywords{OpenRAM, Memory Compiler, Open-source}
|
||||
|
||||
\input{introduction}
|
||||
\input{background}
|
||||
\input{architecture}
|
||||
\input{implementation}
|
||||
\input{results}
|
||||
\input{conclusion}
|
||||
\input{acknowledgments}
|
||||
|
||||
\bibliographystyle{abbrv}
|
||||
\bibliography{references} % Create bibliography using the file: references.bib
|
||||
|
||||
%\input{appendix}
|
||||
\end{document}
|
||||
|
|
@ -0,0 +1,586 @@
|
|||
@Comment @string{DAC = "ACM/IEEE Design Automation Conference~(DAC)"}
|
||||
@Comment @string{TDEV = "IEEE Transactions on Electron Devices"}
|
||||
@Comment @string{DATE = "IEEE Design, Automation and Test in Europe~(DATE)"}
|
||||
@Comment @string{ISSCC = "IEEE International Solid-State Circuits Conference~(ISSCC)"}
|
||||
@Comment @string{TVLSI = "IEEE Transactions on Very Large Scale Integration~(VLSI) Systems"}
|
||||
@Comment @string{JSSC = "IEEE Journal of Solid-State Circuits~(JSSC)"}
|
||||
@Comment @string{ICCD = "International Conference on Computer Design~(ICCD)"}
|
||||
@Comment @string{ISLPED = "IEEE International Symposium on Low Power Electronics and Design~(ISLPED)"}
|
||||
@Comment @STRING{ICCAD = "IEEE/ACM International Conference on Computer-Aided Design~(ICCAD)"}
|
||||
@Comment @string{ASP-DAC = "IEEE Asia and South Pacific Design Automation Conference~(ASP-DAC)"}
|
||||
@Comment @string{ISCAS = "IEEE International Symposium on Circuits and Systems~(ISCAS)"}
|
||||
@Comment @string{TCAD = "IEEE Transactions on Computer-Aided Design of Integrated Circuits and Systems~(TCAD)"}
|
||||
@Comment @string{GLSVLSI = "ACM Great Lakes Symposium on VLSI~(GLSVLSI)"}
|
||||
@Comment @string{TCASI = "IEEE Transactions on Circuits and Systems I~(TCAS-I)"}
|
||||
@Comment @string{TCASII = "IEEE Transactions on Circuits and Systems II~(TCAS-II)"}
|
||||
@Comment @string{TC = "IEEE Transactions on Computers"}
|
||||
@Comment @string{ISPD = "IEEE International Symposium on Physical Design~(ISPD)"}
|
||||
@Comment @string{TODAES = "ACM Transactions on Design Automation of Electronic Systems~(TODAES)"}
|
||||
@Comment @string{ISVLSI = "IEEE International Symposium on Very Large Scale Integration~(ISVLSI)"}
|
||||
@Comment @string{ISQED = "International Symposium on Quality Electronic Design~(ISQED)"}
|
||||
@Comment @string{TNUKE = "IEEE Transactions on Nuclear Science"}
|
||||
@Comment @string{MWSCAS = "IEEE Midwest Symposium on Circuits and Systems~(MWSCAS)"}
|
||||
@Comment @string{MSE = "IEEE International Conference on Microelectronic Systems Education~(MSE)"}
|
||||
|
||||
@string{DAC = "DAC"}
|
||||
@string{TDEV = "TDEV"}
|
||||
@string{DATE = "DATE"}
|
||||
@string{ISSCC = "ISSCC"}
|
||||
@string{TVLSI = "TVLSI"}
|
||||
@string{JSSC = "JSSC"}
|
||||
@string{ICCD = "ICCD"}
|
||||
@string{ISLPED = "ISLPED"}
|
||||
@STRING{ICCAD = "ICCAD"}
|
||||
@string{ASP-DAC = "ASP-DAC"}
|
||||
@string{ISCAS = "ISCAS"}
|
||||
@string{TCAD = "TCAD"}
|
||||
@string{GLSVLSI = "GLSVLSI"}
|
||||
@string{TCASI = "TCAS-I"}
|
||||
@string{TCASII = "TCAS-II"}
|
||||
@string{TC = "TCOMP"}
|
||||
@string{ISPD = "ISPD"}
|
||||
@string{TODAES = "TODAES"}
|
||||
@string{ISVLSI = "ISVLSI"}
|
||||
@string{ISQED = "ISQED"}
|
||||
@string{TNUKE = "Trans. on Nuclear Science"}
|
||||
@string{MWSCAS = "MWSCAS"}
|
||||
@string{MSE = "MSE"}
|
||||
|
||||
@book{Rabaey:2003,
|
||||
title = {Digital Integrated Circuits: A Design Perspective},
|
||||
author = {J. Rabaey and A. Chandrakasan and B. Nikolić},
|
||||
year = {2003},
|
||||
publisher = {Pearson Education, Inc.},
|
||||
edition = {2nd}
|
||||
}
|
||||
|
||||
@book{Chandrakasan:2001,
|
||||
title = {Design of High Performance Microprocessor Circuits},
|
||||
booktitle = {Design of High Performance Microprocessor Circuits},
|
||||
author = {A. Chandrakasan and W.J. Bowhill and F. Fox},
|
||||
year = {2001},
|
||||
publisher = {IEEE Press}
|
||||
}
|
||||
|
||||
@manual{gdsmill,
|
||||
title = {GDS Mill User Manual},
|
||||
author = {M. Wieckowski},
|
||||
year = {2010}
|
||||
}
|
||||
|
||||
%these are end of chapter references from Rabaey
|
||||
%%%%%%%%%%%
|
||||
@article{Amrutur:2001,
|
||||
author = {B.S. Amrutur and M.A. Horowitz},
|
||||
journal = JSSC,
|
||||
title = {Fast Low-Power Decoders for RAMs},
|
||||
number = {10},
|
||||
pages = {1506-1515},
|
||||
volume = {36},
|
||||
year = {2001},
|
||||
month = {Oct}
|
||||
}
|
||||
|
||||
@inbook{Preston:2001,
|
||||
title = {Register Files and Caches},
|
||||
author = {R.P. Preston},
|
||||
crossref = {Chandrakasan:2001}
|
||||
}
|
||||
|
||||
@book{Itoh:2001,
|
||||
title = {VLSI Memory Chip Design},
|
||||
author = {K. Itoh},
|
||||
publisher = {Springer-Verlag},
|
||||
year = {2001}
|
||||
}
|
||||
|
||||
@article{Itoh:1990,
|
||||
author = {K. Itoh},
|
||||
journal = JSSC,
|
||||
title = {Trends in Megabit DRAM Circuit Design},
|
||||
number = {3},
|
||||
pages = {778-798},
|
||||
volume = {25},
|
||||
year = {1990},
|
||||
month = {Jun}
|
||||
}
|
||||
|
||||
@article{May:1979,
|
||||
author = {T. May and M. Woods},
|
||||
journal = TDEV,
|
||||
title = {Aplha-Particle-Induced Soft Errors in Dynamic Memories},
|
||||
number = {1},
|
||||
pages = {2-9},
|
||||
volume = {26},
|
||||
year = {1979},
|
||||
month = {Jan}
|
||||
}
|
||||
|
||||
@ARTICLE{Tosaka:1997,
|
||||
author={Y. Tosaka and S. Satoh and T. Itakura and K. Suzuki and T. Sugii and H. Ehara and G.A. Woffinden},
|
||||
journal=TDEV,
|
||||
title={Cosmic Ray Neutron-Induced Soft Errors in Sub-Half Micron CMOS Circuits},
|
||||
year={1997},
|
||||
volume={18},
|
||||
number={3},
|
||||
pages={99-101}
|
||||
}
|
||||
|
||||
@ARTICLE{Regitz:1970,
|
||||
author={W.M. Regitz and J. Karp},
|
||||
journal=JSSC,
|
||||
title={Three-transistor-cell 1024-bit 500-ns MOS RAM},
|
||||
year={1970},
|
||||
volume={5},
|
||||
number={5},
|
||||
pages={181-186}
|
||||
}
|
||||
|
||||
@INPROCEEDINGS{Kim1:2011,
|
||||
author={S. Kim and M. Guthaus},
|
||||
booktitle=DAC,
|
||||
title={Leakage-aware redundancy for reliable sub-threshold memories},
|
||||
year={2011},
|
||||
pages={435-440}
|
||||
}
|
||||
|
||||
@INPROCEEDINGS{Kim2:2011,
|
||||
author={S. Kim and M. Guthaus},
|
||||
booktitle=VLSISOC,
|
||||
title={SNM-aware power reduction and reliability improvement in 45nm {SRAM}s},
|
||||
year={2011},
|
||||
pages={204-207}
|
||||
}
|
||||
|
||||
@INPROCEEDINGS{Kim3:2011,
|
||||
author={S. Kim and M. Guthaus},
|
||||
booktitle=ICCAD,
|
||||
title={Low-power multiple-bit upset tolerant memory optimization},
|
||||
year={2011},
|
||||
pages={577-581}
|
||||
}
|
||||
|
||||
@INPROCEEDINGS{Kim:2012,
|
||||
author={S. Kim and M. Guthaus},
|
||||
booktitle=VLSISOC,
|
||||
title={Dynamic voltage scaling for SEU-tolerance in low-power memories},
|
||||
year={2012},
|
||||
pages={207-212}
|
||||
}
|
||||
|
||||
|
||||
@ARTICLE{Rusu:2003,
|
||||
author={S. Rusu and J. Stinson and S. Tam and J. Leung and H. Muljono and B. Cherkauer},
|
||||
journal=JSSC,
|
||||
title={A 1.5-GHz 130-nm Itanium reg; 2 Processor with 6-MB on-die L3 cache},
|
||||
year={2003},
|
||||
volume={38},
|
||||
number={11},
|
||||
pages={1887-1895}
|
||||
}
|
||||
|
||||
@article{itrs:2012,
|
||||
author = {International Technology Roadmap for Semiconductors},
|
||||
title = {2012 ITRS Report: System Drivers},
|
||||
howpublished = {www.itrs.net},
|
||||
year = {2012}
|
||||
}
|
||||
|
||||
@article{Kurdahi:2006,
|
||||
author = {F.J. Kurdahi and A.M. Eltawil and Y.H. Park and R.N Kanj and S.R. Nassif},
|
||||
journal = ISQED,
|
||||
title = {System-level {SRAM} Yield Enhancement},
|
||||
year = {2006},
|
||||
month = {Mar}
|
||||
}
|
||||
|
||||
|
||||
@misc{i7:2011,
|
||||
author = {A. Shimpi},
|
||||
title = {Intel Core i7 3960X (Sandy Bridge) Review: Keeping the High-End Alive},
|
||||
howpublished = {\url{http://www.anandtech.com/show/5091/intel-core-i7-3960x-sandy-bridge-e-review-keeping-the-high-end-alive}},
|
||||
year = {2011},
|
||||
month = {Nov}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@misc{calibre:2013,
|
||||
author = {Mentor Graphics},
|
||||
title = {Calibre nmDRC and nmLVS},
|
||||
howpublished = {\url{http://www.mentor.com/products/ic_nanometer_design/verification-signoff/physical-verification/}},
|
||||
year = {2013}
|
||||
}
|
||||
|
||||
@misc{hspice:2013,
|
||||
author = {Synopsis},
|
||||
title = {HSPICE},
|
||||
howpublished = {\url{http://www.synopsys.com/tools/Verification/AMSVerification/CircuitSimulation/HSPICE/Pages/default.aspx}},
|
||||
year = {2013}
|
||||
}
|
||||
|
||||
@INPROCEEDINGS{Athe:2009,
|
||||
author={P. Athe and S. Dasgupta},
|
||||
booktitle={{ISIEA}},
|
||||
title={A comparative study of 6T, 8T and 9T decanano {SRAM} cell},
|
||||
year={2009},
|
||||
volume={2},
|
||||
pages={889-894}
|
||||
}
|
||||
|
||||
@ARTICLE{Calin:1996,
|
||||
author={T. Calin and M. Nicolaidis and R. Velazco},
|
||||
journal=TNUKE,
|
||||
title={Upset hardened memory design for submicron CMOS technology},
|
||||
year={1996},
|
||||
volume={43},
|
||||
number={6},
|
||||
pages={2874-2878}
|
||||
}
|
||||
|
||||
@INPROCEEDINGS{Jung:2012,
|
||||
author={I. Jung and Y. Kim and F. Lombardi},
|
||||
booktitle=MWSCAS,
|
||||
title={A novel sort error hardened 10T {SRAM} cells for low voltage operation},
|
||||
year={2012},
|
||||
pages={714-717}
|
||||
}
|
||||
|
||||
@ARTICLE{Goudarzi:2010,
|
||||
author={M. Goudarzi and T. Ishihara},
|
||||
journal=TVLSI,
|
||||
title={{SRAM} Leakage Reduction by Row/Column Redundancy Under Random Within-Die Delay Variation},
|
||||
year={2010},
|
||||
volume={18},
|
||||
number={12},
|
||||
pages={1660-1671}
|
||||
}
|
||||
|
||||
@techreport{ibm:1997,
|
||||
author = {IBM},
|
||||
title = {Understanding Static RAM Operation},
|
||||
howpublished = {IBM Applications Note},
|
||||
year = {1997},
|
||||
month = {Mar}
|
||||
}
|
||||
|
||||
|
||||
@misc{python:2013,
|
||||
author = {Python},
|
||||
title = {The Python Programming Language},
|
||||
howpublished = {\url{http://www.python.org}},
|
||||
year = {2013}
|
||||
}
|
||||
|
||||
|
||||
@misc{Wieckowski:2010,
|
||||
author = {Michael Wieckowski},
|
||||
title = {GDS Mill},
|
||||
howpublished = {\url{http://michaelwieckowski.com/?page_id=190}},
|
||||
year = {2010}
|
||||
}
|
||||
|
||||
@misc{globalfoundries:2015,
|
||||
author = {{Global Foundries}},
|
||||
title = {{ASICs}},
|
||||
howpublished = {\url{http://www.globalfoundries.com/technology-solutions/asics}},
|
||||
year = {2015}
|
||||
}
|
||||
|
||||
@misc{synopsys:2015,
|
||||
author = {Synopsys},
|
||||
title = {DesignWare Memory Compilers},
|
||||
howpublished = {\url{http://www.synopsys.com/dw/ipdir.php?ds=dwc_sram_memory_compilers}},
|
||||
year = {2015}
|
||||
}
|
||||
|
||||
@misc{dolphin:2015,
|
||||
author = {{Dolphin Technology}},
|
||||
title = {Memory Products},
|
||||
howpublished = {\url{http://www.dolphin-ic.com/memory-products.html}},
|
||||
year = {2015}
|
||||
}
|
||||
|
||||
@misc{faraday:2015,
|
||||
author = {{Faraday Technologies}},
|
||||
title = {Memory Compiler Architecture},
|
||||
howpublished = {\url{http://www.faraday-tech.com/html/Product/IPProduct/LibraryMemoryCompiler/index.htm}},
|
||||
year = {2015}
|
||||
}
|
||||
|
||||
@misc{arm:2015,
|
||||
author = {ARM},
|
||||
title = {Embedded Memory {IP}},
|
||||
howpublished = {\url{http://www.arm.com/products/physical-ip/embedded-memory-ip/index.php}},
|
||||
year = {2015}
|
||||
}
|
||||
|
||||
|
||||
@misc{scmos,
|
||||
author = {MOSIS},
|
||||
title = {{MOSIS} Scalable {CMOS} ({SCMOS})},
|
||||
howpublished = {\url{https://www.mosis.com/files/scmos/scmos.pdf}},
|
||||
year = {2015}
|
||||
}
|
||||
|
||||
%%%look at this paper
|
||||
@article{Hanson:2008,
|
||||
author = {S. Hanson and M. Seok and D. Sylvester and D. Blaauw},
|
||||
journal = TDEV,
|
||||
title = {Nanometer device scaling in subthreshold logic and {SRAM}},
|
||||
number = {1},
|
||||
pages = {175-185},
|
||||
volume = {55},
|
||||
year = {2008}
|
||||
}
|
||||
|
||||
%%%look at this paper
|
||||
@article{Baeg:2009,
|
||||
author={S. Baeg and S. Wen and R. Wong},
|
||||
journal=TNUKE,
|
||||
title={{SRAM} Interleaving Distance Selection With a Soft Error Failure Model},
|
||||
year={2009},
|
||||
month={Aug.},
|
||||
volume={56},
|
||||
number={4},
|
||||
pages={2111-2118}
|
||||
}
|
||||
|
||||
%%%look at this paper
|
||||
@article{Amrutur:2001,
|
||||
author={B. Amrutur and M. Horowitz},
|
||||
journal=JSSC,
|
||||
title={Fast low-power decoders for {RAMs}},
|
||||
year={2001},
|
||||
month={Oct},
|
||||
volume={36},
|
||||
number={10},
|
||||
pages={1506-1515}
|
||||
}
|
||||
|
||||
@inproceedings{Chen:2012,
|
||||
author={Chen Ming and Bai Na},
|
||||
booktitle={{CyberC}},
|
||||
title={An Efficient and Flexible Embedded Memory {IP} Compiler},
|
||||
year={2012},
|
||||
month={Oct},
|
||||
pages={268-273},
|
||||
keywords={SRAM chips;embedded systems;interpolation;polynomials;circuit structure;efficient embedded memory IP compiler;flexible embedded memory IP compiler;polynomial interpolation algorithm;single-port SRAM compiler;Integrated circuit modeling;Interpolation;Mathematical model;Memory management;Random access memory;Tiles;Timing;SRAM;interpolation;memory compiler;modeling;tiling},
|
||||
doi={10.1109/CyberC.2012.52}
|
||||
}
|
||||
|
||||
@inproceedings{Wu:2010,
|
||||
author={Sheng Wu and Xiang Zheng and Zhiqiang Gao and Xiangqing He},
|
||||
booktitle={{DDECS}},
|
||||
title={A 65nm embedded low power {SRAM} compiler},
|
||||
year={2010},
|
||||
month={April},
|
||||
pages={123-124},
|
||||
keywords={CMOS technology;Design methodology;Helium;Kernel;Layout;Libraries;Microelectronics;Program processors;Random access memory;SRAM chips;SRAM compiler;SoC IP;low power},
|
||||
doi={10.1109/DDECS.2010.5491802}
|
||||
}
|
||||
|
||||
@inproceedings{Xu:2007,
|
||||
author={Yi Xu and Zhiqiang Gao and Xiangqing He},
|
||||
booktitle=ISCAS,
|
||||
title={A Flexible Embedded {SRAM} {IP} Compiler},
|
||||
year={2007},
|
||||
month={May},
|
||||
pages={3756-3759},
|
||||
keywords={SRAM chips;circuit layout CAD;elemental semiconductors;embedded systems;logic design;program compilers;silicon;Si;atatic random access memory;block assembly techniques;embedded SRAM IP compiler;physical data syntax;silicon compiler;Assembly;Capacitance;Circuits;Energy consumption;Graphical user interfaces;Helium;Microelectronics;Random access memory;SRAM chips;Silicon compiler},
|
||||
doi={10.1109/ISCAS.2007.378778}
|
||||
}
|
||||
|
||||
%%%%Newest memory compiler on market in 2014
|
||||
@inproceedings{Goldman:2014,
|
||||
author={Goldman, R. and Bartleson, K. and Wood, T. and Melikyan, V. and Babayan, E.},
|
||||
booktitle={{EWME}},
|
||||
title={Synopsys' Educational Generic Memory Compiler},
|
||||
year={2014},
|
||||
month={May},
|
||||
pages={89-92},
|
||||
keywords={SRAM chips;courseware;electronic engineering computing;electronic engineering education;GMC software tool;Synopsys educational generic memory compiler software tool;automatic SRAM cell generation;automatic static RAM cell generation;educational designs;educational process;intellectual property restrictions;Educational institutions;Layout;Memory management;Multiplexing;Ports (Computers);Random access memory;Software},
|
||||
doi={10.1109/EWME.2014.6877402}
|
||||
}
|
||||
|
||||
@mastersthesis{butera:2013,
|
||||
author = {J. Butera},
|
||||
title = {OpenRAM: An Open-Source Memory Compiler},
|
||||
school = {University of California - Santa Cruz},
|
||||
year = {2013}
|
||||
}
|
||||
|
||||
@inproceedings{johannsen:blocks,
|
||||
author = {D. Jahannsen},
|
||||
title = {Bristle Blocks: A Silicon Compiler},
|
||||
booktitle = DAC,
|
||||
pages = {195-198},
|
||||
year = {1979}
|
||||
}
|
||||
|
||||
@book{broderson:sicompiler,
|
||||
author = {R. Broderson},
|
||||
title = {Anatomy of a Silicon Compiler},
|
||||
publisher = {Springer},
|
||||
year = {1992}
|
||||
}
|
||||
|
||||
@inproceedings{poechmueller:array,
|
||||
author = {P. Poechmueller and G.~K. Sharma and M. Glesner},
|
||||
title = {A {CAD} Tool for Designing Large, Fault-Tolerant {VLSI} Arrays},
|
||||
booktitle = GLSVLSI,
|
||||
year = {1991}
|
||||
}
|
||||
|
||||
@inproceedings{huang:array,
|
||||
author = {T.-H. Huang and C.-M. Liu and C.-W. Jen},
|
||||
title = {A High-Level Synthesizer for {VLSI} Array Architectures Dedicated to Digital Signal Processing},
|
||||
booktitle = {International Conference on Acoustics, Speech and Signal Processing},
|
||||
pages = {1221-1224},
|
||||
year = {1991}
|
||||
}
|
||||
|
||||
|
||||
@article{cabe:flexible,
|
||||
author = {AC Cabe and Z Qi and W Huang and Y Zhang and MR Stan and GS Rose},
|
||||
journal = {Cadence CDNLive},
|
||||
title = {A flexible, technology adaptive memory generation tool},
|
||||
year = {2006},
|
||||
}
|
||||
|
||||
@mastersthesis{fabmem:2010,
|
||||
author = {T. Shah},
|
||||
title = {{FabMem}: A Multiported {RAM} and {CAM} Compiler for Superscalar Design Space Exploration},
|
||||
school = {North Carolina State University},
|
||||
year = {2010}
|
||||
}
|
||||
|
||||
@misc{virage:2015,
|
||||
author = {{Virage Logic}},
|
||||
title = {{SiWare} Memory},
|
||||
howpublished = {\url{http://www.viragelogic.com}},
|
||||
year = {2015}
|
||||
}
|
||||
|
||||
|
||||
@article{RBL:1998,
|
||||
author = {B. S. Amrutur and M. A. Horowitz},
|
||||
journal = JSSC,
|
||||
title = {A Replica Technique for Wordline and Sense Control in Low-Power {SRAM}s},
|
||||
number = {8},
|
||||
pages = {1208-1219},
|
||||
volume = {33},
|
||||
year = {1998},
|
||||
month = {Aug}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
% references for bit-density comparison
|
||||
|
||||
|
||||
@article{Bit_Density_1,
|
||||
author = {K. Kushida and others},
|
||||
journal = JSSC,
|
||||
title = {A 0.7 {V} Single-Supply {SRAM} With 0.495 $um^2$ Cell in 65 nm Technology Utilizing Self-Write-Back Sense Amplifier and Cascaded Bit Line Scheme},
|
||||
number = {4},
|
||||
pages = {1192-1198},
|
||||
volume = {44},
|
||||
year = {2009},
|
||||
month = {Apr}
|
||||
}
|
||||
|
||||
|
||||
@article{Bit_Density_2,
|
||||
author = {Sh. Miyano and others},
|
||||
journal = JSSC,
|
||||
title = {Highly Energy-Efficient {SRAM} With Hierarchical Bit Line Charge-Sharing Method Using Non-Selected Bit Line Charges},
|
||||
number = {4},
|
||||
pages = {924-931},
|
||||
volume = {48},
|
||||
year = {2013},
|
||||
month = {Apr}
|
||||
}
|
||||
|
||||
@article{Bit_Density_3,
|
||||
author = {S. O. Toh and Zh. Guo and T. K. Liu and B. Nikolic},
|
||||
journal = JSSC,
|
||||
title = {Characterization of Dynamic {SRAM} Stability in 45 nm {CMOS}},
|
||||
number = {11},
|
||||
pages = {2702-2712},
|
||||
volume = {46},
|
||||
year = {2011},
|
||||
month = {Nov}
|
||||
}
|
||||
|
||||
@article{Bit_Density_4,
|
||||
author = {K. Yamaguchi and others},
|
||||
journal = JSSC,
|
||||
title = {A 1.5-ns Access Time, 78- $um^2$ Memory-Cell Size, 64-kb {ECL-CMOS SRAM}},
|
||||
number = {2},
|
||||
pages = {167-174},
|
||||
volume = {27},
|
||||
year = {1992},
|
||||
month = {Feb}
|
||||
}
|
||||
|
||||
@article{Bit_Density_5,
|
||||
author = {N. Shibata and H. Morimura and M. Watanabe},
|
||||
journal = JSSC,
|
||||
title = {A {1-V}, {10-MHz}, 3.5-mW, {1-Mb} {MTCMOS SRAM} with Charge-Recycling Input/Output Buffers},
|
||||
number = {6},
|
||||
pages = {866-877},
|
||||
volume = {34},
|
||||
year = {1999},
|
||||
month = {Jun}
|
||||
}
|
||||
|
||||
@article{Bit_Density_6,
|
||||
author = {N. Tamba and others},
|
||||
journal = JSSC,
|
||||
title = {A 1.5-ns 256-kb {BiCMOS SRAM} with 60-ps 11-K Logic Gates},
|
||||
number = {11},
|
||||
pages = {1344-1352},
|
||||
volume = {48},
|
||||
year = {1994},
|
||||
month = {Nov}
|
||||
}
|
||||
|
||||
%author={Yamaguchi, K. and Nambu, H. and Kanetani, K. and Idei, Y. and Homma, N. and Hiramoto, T. and Tamba, N. and Watanabe, K. and Odaka, Masanori and Ikeda, T. and Ohhata, K. and Sakurai, Y.},
|
||||
@ARTICLE{127339,
|
||||
author={Yamaguchi, K. and others},
|
||||
journal=JSSC,
|
||||
title={A $1.5$-ns access time, $78~um^2$ memory-cell size, $64$-kb {ECL-CMOS SRAM}},
|
||||
year={1992},
|
||||
volume={27},
|
||||
number={2},
|
||||
pages={167-174},
|
||||
doi={10.1109/4.127339},
|
||||
ISSN={0018-9200},
|
||||
month={Feb},
|
||||
}
|
||||
|
||||
%author={Kushida, K. and Suzuki, A. and Fukano, G. and Kawasumi, A. and Hirabayashi, O. and Takeyama, Y. and Sasaki, T. and Katayama, A. and Fujimura, Y. and Yabe, T.},
|
||||
@INPROCEEDINGS{4585946,
|
||||
author={Kushida, K. and others},
|
||||
booktitle=ISVLSI,
|
||||
title={A $0.7$V single-supply {SRAM} with $0.495~um^2$ cell in $65$nm technology
|
||||
utilizing self-write-back sense amplifier and cascaded bit
|
||||
line scheme},
|
||||
year={2008},
|
||||
pages={46-47},
|
||||
doi={10.1109/VLSIC.2008.4585946},
|
||||
month={June}
|
||||
}
|
||||
|
||||
%author={Stine, J.E. and Castellanos, I. and Wood, M. and Henson, J. and Love, F. and Davis, W.R. and Franzon, P.D. and Bucher, M. and Basavarajaiah, S. and Julie Oh and Jenkal, R.},
|
||||
@INPROCEEDINGS{4231502,
|
||||
author={J. E. Stine and others},
|
||||
booktitle=MSE,
|
||||
title={{FreePDK}: An Open-Source Variation-Aware Design Kit},
|
||||
year={2007},
|
||||
pages={173-174},
|
||||
doi={10.1109/MSE.2007.44},
|
||||
month={June}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
\section{Results}
|
||||
\label{sec:results}
|
||||
|
||||
Figure~\ref{fig:layout} shows several different SRAM layouts
|
||||
generated by OpenRAM in FreePDK45. OpenRAM can generate single
|
||||
bank and multi-bank SRAM arrays. Banks are
|
||||
symmetrically placed to have the same delay for data and address
|
||||
while sharing peripheral blocks such as decoders.
|
||||
\begin{figure}[tb]
|
||||
\centering
|
||||
\includegraphics[scale=.4]{./figs/layout.pdf}
|
||||
\caption{Single bank and multi-bank SRAMs (not to scale) use
|
||||
symmetrical bank placement to share peripheral circuitry and
|
||||
equalize signal delays.}
|
||||
\label{fig:layout}
|
||||
\end{figure}
|
||||
|
||||
Figure~\ref{fig:density_figure} shows the memory area of different
|
||||
total size and data word width memories in both FreePDK45 and
|
||||
SCMOS. As expected, the smaller process technology (45nm) has lower
|
||||
total area overall but the trends are similar in both technologies.
|
||||
|
||||
Figure~\ref{fig:density_figure} also shows the access time of
|
||||
different size and data word width in FreePDK45 and SCMOS. Increasing
|
||||
the memory size generally increases the access time; long bit-lines
|
||||
and word-lines increase the access time by adding more parasitic
|
||||
capacitance and resistance. Since OpenRAM uses multiple banks and
|
||||
column muxing, it is possible to have a smaller access time for larger
|
||||
memory designs, but this will sacrifice density.
|
||||
|
||||
\begin{figure}[tb]
|
||||
\begin{center}
|
||||
\centering
|
||||
%\includegraphics[width=8.5cm]{./figs/Results.pdf}
|
||||
\includegraphics[width=7.5cm , height=14cm]{./figs/Results2.pdf}
|
||||
% \subfigure[FreePDK45 memory area \label{fig:freepdk_area}]{
|
||||
% \includegraphics[scale=1]{./figs/Freepdk_Area.pdf}}
|
||||
% \subfigure[SCMOS memory area \label{fig:scn3me_area}]{
|
||||
% \includegraphics[scale=.5]{./figs/Scn3me_Area.pdf}}
|
||||
\caption{OpenRAM provides high-density memories in multiple
|
||||
technologies and sizes with corresponding characterized
|
||||
delays. \label{fig:density_figure}}
|
||||
\vspace{-0.5cm}
|
||||
\end{center}
|
||||
\end{figure}
|
||||
|
||||
%Table~\ref{table:bit-density-comparison} shows a comparison between bit
|
||||
%density of OpenRAM's generated memory designs and other publications
|
||||
%which are close in technology node with FreePDK45 and SCMOS. As shown
|
||||
%in this table, OpenRAM provides very dense SRAM arrays in both technologies.
|
||||
|
||||
\begin{table}[t]
|
||||
\centering
|
||||
\caption{OpenRAM has high density compared to other published memories in
|
||||
similar technologies.}
|
||||
\begin{tabular}{|c|c|c|c|l|l|l|l|l|} \hline
|
||||
\texttt{Ref.} & \texttt{Feature} & \texttt{Tech.} & \texttt{Density} \\
|
||||
& \texttt{Size} & & [Mb/$mm^2$] \\
|
||||
\hline \hline
|
||||
$~\cite{4585946}$ & $65$ nm & CMOS & $0.7700$ \\ \hline
|
||||
$~\cite{Bit_Density_3}$ & $45$ nm & CMOS & $0.3300$ \\ \hline
|
||||
$~\cite{Bit_Density_2}$ & $40$ nm & CMOS & $0.9400$ \\ \hline
|
||||
\verb+OpenRAM+ & $45$ nm & FreePDK45 & $0.8260$ \\ \hline \hline
|
||||
$~\cite{127339}$ & $0.5$ um & CMOS & $0.0036$ \\ \hline
|
||||
$~\cite{Bit_Density_6}$ & $0.5$ um & BiCMOS & $0.0020$ \\ \hline
|
||||
$~\cite{Bit_Density_5}$ & $0.5$ um & CMOS & $0.0050$ \\ \hline
|
||||
\verb+OpenRAM+ & $0.5$ um & SCMOS & $0.0050$ \\ \hline
|
||||
\end{tabular}
|
||||
\label{table:bit-density-comparison}
|
||||
\end{table}
|
||||
|
||||
%\begin{table*}
|
||||
%\centering
|
||||
%\caption{OpenRAM has high density, fast access time and low power consumption compared to other published memories in similar technologies.}
|
||||
%\begin{tabular}{|c|l|l|l|l|l|l|l|l|} \hline
|
||||
%\texttt{Reference} & \texttt{Technology} & \texttt{Density (Mb/$mm^2$)}& \texttt{Access time (ns)}& \texttt{Power consumption} \\ \hline \hline
|
||||
%$~\cite{Bit_Density_1}$ & $65 nm CMOS$ & $0.77$ & $28$ & $22$ $uW/MHz$ \\ \hline
|
||||
%$~\cite{Bit_Density_2}$ & $40 nm CMOS$ & $0.94$ & $45$ & $13.8$ $pJ/access/Mbit$ \\ \hline
|
||||
%$OpenRAM$ & $45 nm FreePDK45$ & $0.826$ & $9.86$ & $13.14$ $mW$ \\ \hline \hline
|
||||
%$~\cite{Bit_Density_4}$ & $0.5 um CMOS$ & $0.0036$ & $1.5$ & $6$ $W$ \\ \hline
|
||||
%$~\cite{Bit_Density_6}$ & $0.5 um BiCMOS$ & $0.002$ & $1.5$ & $35$ $W$ \\ \hline
|
||||
%$~\cite{Bit_Density_5}$ & $0.5 um CMOS$ & $0.005$ & $75$ & $3.9$ $mW$ \\ \hline
|
||||
%$OpenRAM$ & $0.5 um SCMOS$ & $0.005$ & $44.9$ & $115$ $mW$ \\ \hline
|
||||
%\end{tabular}
|
||||
%\label{table:bit-density-comparison}
|
||||
%\end{table*}
|
||||
|
||||
Comparison of power consumption and read access time of different
|
||||
memories is a bit more complicated to make a conclusion, because there
|
||||
are many trade-offs. Power and performance are highly dependent on
|
||||
circuit style (CMOS, ECL, etc.), memory organization (more banks is
|
||||
faster but sacrifices density), and the optimization goal: low-power
|
||||
or high-performance. In general, OpenRAM has reasonable trade-off
|
||||
between the two and can be customized by using an alternate sense
|
||||
amplifiers, decoders, or overall dimensional organization.
|
||||
Table~\ref{table:bit-density-comparison} compares the bit-density of
|
||||
OpenRAM against published designs using similar technology nodes. The
|
||||
results show the benefit of technology scaling and that OpenRAM has
|
||||
very good density in both technologies. As a comparison, a 76ns SRAM
|
||||
consumes 3.9mW~\cite{Bit_Density_5} while OpenRAM is much faster at
|
||||
44.9ns but consumes 115mW for the same size.
|
||||
|
||||
%Table~\ref{table:bit-density-comparison} shows a comparison between bit density, access
|
||||
%time and power consumption of OpenRAM’s generated mem-
|
||||
%ory designs and other publications which are close in tech-
|
||||
%nology node with FreePDK45 and SCMOS. As shown in this
|
||||
%table, OpenRAM provides very dense SRAM arrays in both
|
||||
%technologies. There is no easy comparison on power con-
|
||||
%sumption and read access time as these values vary with the
|
||||
%array size and configuration. Therefore, we only try to com-
|
||||
%pare the features of each work from a more general point of
|
||||
%view.
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
# This is a short script to simplify generating a PDF document from LaTeX and BibTeX code.
|
||||
# The script cleans plausibly existing files, generates the PDF, then cleans furthur generated files.
|
||||
|
||||
# Clean any latent files.
|
||||
rm -rf *.aux *.bbl *.blg *.log #*.pdf
|
||||
|
||||
# Generate the actual output.
|
||||
pdflatex main.tex
|
||||
bibtex main.aux
|
||||
pdflatex main.tex
|
||||
pdflatex main.tex
|
||||
mv main.pdf openram.pdf
|
||||
|
||||
# Clean all the generated files except for the .pdf
|
||||
rm -rf *.aux *.bbl *.blg *.log *.lof *.lot *.out *.toc
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
###############################################################################
|
||||
BASIC SETUP
|
||||
|
||||
-The OpenRAM compiler has very few dependencies:
|
||||
|
||||
1) ngspice v20 or later or HSpice I-2013.12-1 or later
|
||||
2) Python 2.7 and higher (currently excludes Python 3 and up)
|
||||
3) a setup script for each technology
|
||||
4) a technology directory for each technology with the base cells
|
||||
|
||||
- You must set two environment variables: OPENRAM_HOME should point to
|
||||
the compiler source directory OPENERAM_TECH should point to a root
|
||||
technology directory that contains subdirs of all other technologies.
|
||||
|
||||
-All setup scripts should be in the setup_scripts directory under the
|
||||
technology directory. Please look at the file
|
||||
setup_openram_freepdk45.py for an example of what is needed for
|
||||
OpenRAM. Each setup script should be named as: setup_openram_{tech
|
||||
folder name}.py.
|
||||
|
||||
-Each specific technology (e.g. freepdk45) should be a subdirectory
|
||||
and include certain folders and files:
|
||||
|
||||
1) gds_lib folder with all the .gds (premade) library cells. At a
|
||||
minimum this includes:
|
||||
ms_flop.gds
|
||||
sense_amp.gds
|
||||
write_driver.gds
|
||||
cell_6t.gds
|
||||
replica_cell_6t.gds
|
||||
tri_gate.gds
|
||||
|
||||
2) sp_lib folder with all the .sp (premade) library netlists for the above cells.
|
||||
|
||||
3) layers.map
|
||||
|
||||
4) References in tech.py to spice models that correspond to the
|
||||
transistors in the cells
|
||||
|
||||
5) OpenRAM tech file (tech.py) that contains library, DRC/LVS
|
||||
information, layer information, etc. for the technology
|
||||
|
||||
- In order to debug, it is useful to have a GDS viewer. In addition to
|
||||
normal layout tools, we recommend the following viewers:
|
||||
|
||||
LayoutEditor http://www.layouteditor.net/
|
||||
GLADE http://www.peardrop.co.uk/
|
||||
Magic http://opencircuitdesign.com/magic/
|
||||
|
||||
###############################################################################
|
||||
DIRECTORY STRUCTURE
|
||||
|
||||
compiler - openram compiler itself (pointed to by OPENRAM_HOME)
|
||||
compiler/characterizer - timing characterization code
|
||||
compiler/gdsMill - gds reader/writer
|
||||
compiler/tests - unit tests
|
||||
technology/freepdk45 - example configuration library for freepdk45 technology node
|
||||
technology/scn3me_subm - example configuration library SCMOS technology node
|
||||
technology/setup_scripts - setup scripts to customize your PDKs and OpenRAM technologies
|
||||
|
||||
###############################################################################
|
||||
Example to output/input .gds layout files from/to Cadence
|
||||
|
||||
1) To create your component layouts, you should stream them to
|
||||
individual gds files using our provided layermap and flatten
|
||||
cells. For example,
|
||||
|
||||
strmout -layerMap layers.map -library sram -topCell $i -view layout -flattenVias -flattenPcells -strmFile ../gds_lib/$i.gds
|
||||
|
||||
2) To stream a layout back into Cadence, do this:
|
||||
|
||||
strmin -layerMap layers.map -attachTechFileOfLib NCSU_TechLib_FreePDK45 -library sram_4_32 -strmFile sram_4_32.gds
|
||||
|
||||
When you import a gds file, make sure to attach the correct tech lib
|
||||
or you will get incorrect layers in the resulting library.
|
||||
|
||||
|
||||
|
||||
###############################################################################
|
||||
UNIT TESTS
|
||||
|
||||
Regression testing performs a number of tests for all modules in OpenRAM.
|
||||
|
||||
Steps to run regression testing:
|
||||
1) First, ensure your setup_scripts is correctly setup.
|
||||
2) Navigate to the compiler directory (trunk/compiler/tests)
|
||||
3) Use the command:
|
||||
python regress.py
|
||||
4) To run a specific test:
|
||||
python {unit test}.py
|
||||
|
||||
The unit tests take the same arguments as openram.py itself.
|
||||
|
||||
To increase the verbosity of the test, add one (or more) -v options:
|
||||
python tests/00_code_format_check_test.py -v
|
||||
|
||||
To specify a particular technology use "-t <techname>" such as
|
||||
"-t scn3me_subm"
|
||||
|
||||
A regression daemon script that can be used with cron is included:
|
||||
regress_daemon.py
|
||||
regress_daemon.sh
|
||||
|
||||
This updates a git repository, checks out code, and sends an email
|
||||
report with status information.
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
Develop set of vector/point manipulation functions and replace
|
||||
everywhere. (Bin)
|
||||
|
||||
Use signal names from the technology file. Right now they are hard
|
||||
coded everywhere. For example: DATA, ADDR, etc.
|
||||
|
||||
Cell name (ms_flop) is hard coded in characterizer, pin names are hard
|
||||
coded too. This should come from the config file which dynamically
|
||||
loads the module names.
|
||||
|
||||
Autodetect ideal number of threads for hspice.
|
||||
|
||||
vdd and gnd are hard coded in some places. The names should come from
|
||||
the tech file.
|
||||
|
||||
Some modules use upper/lower via layer instead of min width DRC rule
|
||||
from tech file.
|
||||
|
||||
Fix the size of labels. For some reason, they are HUGE. (Samira)
|
||||
|
||||
Add the clock buffer internal to control logic. Simulation uses
|
||||
1-4-8-16 inverters right now. Replace simulation with simple clock
|
||||
buffer after fixing.
|
||||
|
||||
Check out the multibank organization in sram.py and bank.py to see if
|
||||
it can be reduced or made more readable.
|
||||
|
||||
Move/modify similar functions in hierarchical_predecode2x4 and
|
||||
hierarchical_predecode3x8 to hierarchical_predecode class
|
||||
|
||||
Fix stimuli.py to be more readable.
|
||||
|
||||
Add tests for bitcell, ms_flop, replica_bitcell, sens_amp, tri_gate, write_driver?
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,23 @@
|
|||
import design
|
||||
import debug
|
||||
import utils
|
||||
from tech import GDS,layer
|
||||
|
||||
class bitcell(design.design):
|
||||
"""
|
||||
A single bit cell (6T, 8T, etc.) This module implements the
|
||||
single memory cell used in the design. It is a hand-made cell, so
|
||||
the layout and netlist should be available in the technology
|
||||
library.
|
||||
"""
|
||||
|
||||
pins = ["BL", "BR", "WL", "vdd", "gnd"]
|
||||
chars = utils.auto_measure_libcell(pins, "cell_6t", GDS["unit"], layer["boundary"])
|
||||
|
||||
def __init__(self, name="cell_6t"):
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "Create bitcell object")
|
||||
|
||||
self.width = bitcell.chars["width"]
|
||||
self.height = bitcell.chars["height"]
|
||||
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
import debug
|
||||
import design
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class bitcell_array(design.design):
|
||||
"""
|
||||
Creates a rows x cols array of memory cells. Assumes bit-lines
|
||||
and word line is connected by abutment.
|
||||
Connects the word lines and bit lines.
|
||||
"""
|
||||
|
||||
def __init__(self, name, cols, rows):
|
||||
design.design.__init__(self, name)
|
||||
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
|
||||
|
||||
|
||||
self.column_size = cols
|
||||
self.row_size = rows
|
||||
|
||||
c = reload(__import__(OPTS.config.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
|
||||
self.bitcell_chars = self.mod_bitcell.chars
|
||||
|
||||
self.add_pins()
|
||||
self.create_layout()
|
||||
self.add_labels()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for col in range(self.column_size):
|
||||
self.add_pin("bl[{0}]".format(col))
|
||||
self.add_pin("br[{0}]".format(col))
|
||||
for row in range(self.row_size):
|
||||
self.add_pin("wl[{0}]".format(row))
|
||||
self.add_pin("vdd")
|
||||
self.add_pin("gnd")
|
||||
|
||||
def create_layout(self):
|
||||
self.create_cell()
|
||||
self.setup_layout_constants()
|
||||
self.add_cells()
|
||||
self.offset_all_coordinates()
|
||||
|
||||
def setup_layout_constants(self):
|
||||
self.vdd_positions = []
|
||||
self.gnd_positions = []
|
||||
self.BL_positions = []
|
||||
self.BR_positions = []
|
||||
self.WL_positions = []
|
||||
self.height = self.row_size * self.cell.height
|
||||
self.width = self.column_size * self.cell.width
|
||||
|
||||
def create_cell(self):
|
||||
self.cell = self.mod_bitcell()
|
||||
self.add_mod(self.cell)
|
||||
|
||||
def add_cells(self):
|
||||
xoffset = 0.0
|
||||
for col in range(self.column_size):
|
||||
yoffset = 0.0
|
||||
for row in range(self.row_size):
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
|
||||
if row % 2:
|
||||
tempy = yoffset + self.cell.height
|
||||
dir_key = "MX"
|
||||
else:
|
||||
tempy = yoffset
|
||||
dir_key = "R0"
|
||||
|
||||
if OPTS.trim_noncritical == True:
|
||||
if row == self.row_size - 1:
|
||||
self.add_inst(name=name,
|
||||
mod=self.cell,
|
||||
offset=[xoffset, tempy],
|
||||
mirror=dir_key)
|
||||
self.connect_inst(["bl[{0}]".format(col),
|
||||
"br[{0}]".format(col),
|
||||
"wl[{0}]".format(row),
|
||||
"vdd",
|
||||
"gnd"])
|
||||
else:
|
||||
self.add_inst(name=name,
|
||||
mod=self.cell,
|
||||
offset=[xoffset, tempy],
|
||||
mirror=dir_key)
|
||||
self.connect_inst(["bl[{0}]".format(col),
|
||||
"br[{0}]".format(col),
|
||||
"wl[{0}]".format(row),
|
||||
"vdd",
|
||||
"gnd"])
|
||||
yoffset += self.cell.height
|
||||
xoffset += self.cell.width
|
||||
|
||||
def add_labels(self):
|
||||
offset = vector(0.0, 0.0)
|
||||
for col in range(self.column_size):
|
||||
offset.y = 0.0
|
||||
self.add_label(text="bl[{0}]".format(col),
|
||||
layer="metal2",
|
||||
offset=offset + vector(self.bitcell_chars["BL"][0],0))
|
||||
self.add_label(text="br[{0}]".format(col),
|
||||
layer="metal2",
|
||||
offset=offset + vector(self.bitcell_chars["BR"][0],0))
|
||||
self.BL_positions.append(offset + vector(self.bitcell_chars["BL"][0],0))
|
||||
self.BR_positions.append(offset + vector(self.bitcell_chars["BR"][0],0))
|
||||
|
||||
# gnd offset is 0 in our cell, but it be non-zero
|
||||
self.add_label(text="gnd",
|
||||
layer="metal2",
|
||||
offset=offset + vector(self.bitcell_chars["gnd"][0],0))
|
||||
self.gnd_positions.append(offset + vector(self.bitcell_chars["gnd"][0],0))
|
||||
|
||||
for row in range(self.row_size):
|
||||
# only add row labels on the left most column
|
||||
if col == 0:
|
||||
# flipped row
|
||||
if row % 2:
|
||||
base_offset = offset + vector(0, self.cell.height)
|
||||
vdd_offset = base_offset - vector(0,self.bitcell_chars["vdd"][1])
|
||||
wl_offset = base_offset - vector(0,self.bitcell_chars["WL"][1])
|
||||
# unflipped row
|
||||
else:
|
||||
vdd_offset = offset + vector(0,self.bitcell_chars["vdd"][1])
|
||||
wl_offset = offset + vector(0,self.bitcell_chars["WL"][1])
|
||||
# add vdd label and offset
|
||||
self.add_label(text="vdd",
|
||||
layer="metal1",
|
||||
offset=vdd_offset)
|
||||
self.vdd_positions.append(vdd_offset)
|
||||
# add gnd label and offset
|
||||
self.add_label(text="wl[{0}]".format(row),
|
||||
layer="metal1",
|
||||
offset=wl_offset)
|
||||
self.WL_positions.append(wl_offset)
|
||||
|
||||
# increments to the next row height
|
||||
offset.y += self.cell.height
|
||||
# increments to the next column width
|
||||
offset.x += self.cell.width
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
"""
|
||||
This is a DRC/LVS interface for calibre. It implements completely
|
||||
independently two functions: run_drc and run_lvs, that perform these
|
||||
functions in batch mode and will return true/false if the result
|
||||
passes. All of the setup (the rules, temp dirs, etc.) should be
|
||||
contained in this file. Replacing with another DRC/LVS tool involves
|
||||
rewriting this code to work properly. Porting to a new technology in
|
||||
Calibre means pointing the code to the proper DRC and LVS rule files.
|
||||
|
||||
A calibre DRC runset file contains, at the minimum, the following information:
|
||||
|
||||
*drcRulesFile: /mada/software/techfiles/FreePDK45/ncsu_basekit/techfile/calibre/calibreDRC.rul
|
||||
*drcRunDir: .
|
||||
*drcLayoutPaths: ./cell_6t.gds
|
||||
*drcLayoutPrimary: cell_6t
|
||||
*drcLayoutSystem: GDSII
|
||||
*drcResultsformat: ASCII
|
||||
*drcResultsFile: cell_6t.drc.results
|
||||
*drcSummaryFile: cell_6t.drc.summary
|
||||
*cmnFDILayerMapFile: ./layer.map
|
||||
*cmnFDIUseLayerMap: 1
|
||||
|
||||
This can be executed in "batch" mode with the following command:
|
||||
|
||||
calibre -gui -drc example_drc_runset -batch
|
||||
|
||||
To open the results, you can do this:
|
||||
|
||||
calibre -rve cell_6t.drc.results
|
||||
|
||||
*lvsRulesFile: /mada/software/techfiles/FreePDK45/ncsu_basekit/techfile/calibre/calibreLVS.rul
|
||||
*lvsRunDir: .
|
||||
*lvsLayoutPaths: ./cell_6t.gds
|
||||
*lvsLayoutPrimary: cell_6t
|
||||
*lvsSourcePath: ./cell_6t.sp
|
||||
*lvsSourcePrimary: cell_6t
|
||||
*lvsSourceSystem: SPICE
|
||||
*lvsSpiceFile: extracted.sp
|
||||
*lvsPowerNames: vdd
|
||||
*lvsGroundNames: vss
|
||||
*lvsIgnorePorts: 1
|
||||
*lvsERCDatabase: cell_6t.erc.results
|
||||
*lvsERCSummaryFile: cell_6t.erc.summary
|
||||
*lvsReportFile: cell_6t.lvs.report
|
||||
*lvsMaskDBFile: cell_6t.maskdb
|
||||
*cmnFDILayerMapFile: ./layer.map
|
||||
*cmnFDIUseLayerMap: 1
|
||||
|
||||
To run and see results:
|
||||
|
||||
calibre -gui -lvs example_lvs_runset -batch
|
||||
more cell_6t.lvs.report
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import debug
|
||||
import globals
|
||||
import subprocess
|
||||
|
||||
|
||||
def run_drc(name, gds_name):
|
||||
"""Run DRC check on a given top-level name which is
|
||||
implemented in gds_name."""
|
||||
OPTS = globals.get_opts()
|
||||
|
||||
# the runset file contains all the options to run calibre
|
||||
from tech import drc
|
||||
drc_rules = drc["drc_rules"]
|
||||
|
||||
drc_runset = {
|
||||
'drcRulesFile': drc_rules,
|
||||
'drcRunDir': OPTS.openram_temp,
|
||||
'drcLayoutPaths': gds_name,
|
||||
'drcLayoutPrimary': name,
|
||||
'drcLayoutSystem': 'GDSII',
|
||||
'drcResultsformat': 'ASCII',
|
||||
'drcResultsFile': OPTS.openram_temp + name + ".drc.results",
|
||||
'drcSummaryFile': OPTS.openram_temp + name + ".drc.summary",
|
||||
'cmnFDILayerMapFile': drc["layer_map"],
|
||||
'cmnFDIUseLayerMap': 1
|
||||
}
|
||||
|
||||
# write the runset file
|
||||
f = open(OPTS.openram_temp + "drc_runset", "w")
|
||||
for k in sorted(drc_runset.iterkeys()):
|
||||
f.write("*%s: %s\n" % (k, drc_runset[k]))
|
||||
f.close()
|
||||
|
||||
# run drc
|
||||
os.chdir(OPTS.openram_temp)
|
||||
errfile = "%s%s.drc.err" % (OPTS.openram_temp, name)
|
||||
outfile = "%s%s.drc.out" % (OPTS.openram_temp, name)
|
||||
|
||||
cmd = "{0} -gui -drc {1}drc_runset -batch 2> {2} 1> {3}".format(
|
||||
OPTS.calibre_exe, OPTS.openram_temp, errfile, outfile)
|
||||
debug.info(1, cmd)
|
||||
os.system(cmd)
|
||||
|
||||
# check the result for these lines in the summary:
|
||||
# TOTAL Original Layer Geometries: 106 (157)
|
||||
# TOTAL DRC RuleChecks Executed: 156
|
||||
# TOTAL DRC Results Generated: 0 (0)
|
||||
f = open(drc_runset['drcSummaryFile'], "r")
|
||||
results = f.readlines()
|
||||
f.close()
|
||||
# those lines should be the last 3
|
||||
results = results[-3:]
|
||||
geometries = int(re.split("\W+", results[0])[5])
|
||||
rulechecks = int(re.split("\W+", results[1])[4])
|
||||
errors = int(re.split("\W+", results[2])[5])
|
||||
|
||||
# always display this summary
|
||||
if errors > 0:
|
||||
debug.error("%-25s\tGeometries: %d\tChecks: %d\tErrors: %d" %
|
||||
(name, geometries, rulechecks, errors))
|
||||
else:
|
||||
debug.info(1, "%-25s\tGeometries: %d\tChecks: %d\tErrors: %d" %
|
||||
(name, geometries, rulechecks, errors))
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def run_lvs(name, gds_name, sp_name):
|
||||
"""Run LVS check on a given top-level name which is
|
||||
implemented in gds_name and sp_name. """
|
||||
OPTS = globals.get_opts()
|
||||
from tech import drc
|
||||
lvs_rules = drc["lvs_rules"]
|
||||
lvs_runset = {
|
||||
'lvsRulesFile': lvs_rules,
|
||||
'lvsRunDir': OPTS.openram_temp,
|
||||
'lvsLayoutPaths': gds_name,
|
||||
'lvsLayoutPrimary': name,
|
||||
'lvsSourcePath': sp_name,
|
||||
'lvsSourcePrimary': name,
|
||||
'lvsSourceSystem': 'SPICE',
|
||||
'lvsSpiceFile': OPTS.openram_temp + "extracted.sp",
|
||||
'lvsPowerNames': 'vdd',
|
||||
'lvsGroundNames': 'gnd',
|
||||
'lvsIncludeSVRFCmds': 1,
|
||||
'lvsSVRFCmds': '{VIRTUAL CONNECT NAME VDD? GND? ?}',
|
||||
'lvsIgnorePorts': 1,
|
||||
'lvsERCDatabase': OPTS.openram_temp + name + ".erc.results",
|
||||
'lvsERCSummaryFile': OPTS.openram_temp + name + ".erc.summary",
|
||||
'lvsReportFile': OPTS.openram_temp + name + ".lvs.report",
|
||||
'lvsMaskDBFile': OPTS.openram_temp + name + ".maskdb",
|
||||
'cmnFDILayerMapFile': drc["layer_map"],
|
||||
'cmnFDIUseLayerMap': 1,
|
||||
'cmnVConnectNames': 'vdd, gnd',
|
||||
#'cmnVConnectNamesState' : 'ALL', #connects all nets with the same name
|
||||
}
|
||||
|
||||
# write the runset file
|
||||
f = open(OPTS.openram_temp + "lvs_runset", "w")
|
||||
for k in sorted(lvs_runset.iterkeys()):
|
||||
f.write("*%s: %s\n" % (k, lvs_runset[k]))
|
||||
f.close()
|
||||
|
||||
# run LVS
|
||||
os.chdir(OPTS.openram_temp)
|
||||
errfile = "%s%s.lvs.err" % (OPTS.openram_temp, name)
|
||||
outfile = "%s%s.lvs.out" % (OPTS.openram_temp, name)
|
||||
|
||||
cmd = "calibre -gui -lvs %slvs_runset -batch 2> %s 1> %s" % (
|
||||
OPTS.openram_temp, errfile, outfile)
|
||||
debug.info(2, cmd)
|
||||
os.system(cmd)
|
||||
|
||||
# check the result for these lines in the summary:
|
||||
f = open(lvs_runset['lvsReportFile'], "r")
|
||||
results = f.readlines()
|
||||
f.close()
|
||||
|
||||
# NOT COMPARED
|
||||
# CORRECT
|
||||
# INCORRECT
|
||||
test = re.compile("# CORRECT #")
|
||||
correct = filter(test.search, results)
|
||||
test = re.compile("NOT COMPARED")
|
||||
notcompared = filter(test.search, results)
|
||||
test = re.compile("# INCORRECT #")
|
||||
incorrect = filter(test.search, results)
|
||||
|
||||
# Errors begin with "Error:"
|
||||
test = re.compile("\s+Error:")
|
||||
errors = filter(test.search, results)
|
||||
for e in errors:
|
||||
debug.error(e.strip("\n"))
|
||||
|
||||
summary_errors = len(notcompared) + len(incorrect) + len(errors)
|
||||
|
||||
# also check the extraction summary file
|
||||
f = open(lvs_runset['lvsReportFile'] + ".ext", "r")
|
||||
results = f.readlines()
|
||||
f.close()
|
||||
|
||||
test = re.compile("ERROR:")
|
||||
exterrors = filter(test.search, results)
|
||||
for e in exterrors:
|
||||
debug.error(e.strip("\n"))
|
||||
|
||||
test = re.compile("WARNING:")
|
||||
extwarnings = filter(test.search, results)
|
||||
for e in extwarnings:
|
||||
debug.error(e.strip("\n"))
|
||||
|
||||
ext_errors = len(exterrors) + len(extwarnings)
|
||||
|
||||
# also check the output file
|
||||
f = open(outfile, "r")
|
||||
results = f.readlines()
|
||||
f.close()
|
||||
|
||||
# Errors begin with "ERROR:"
|
||||
test = re.compile("ERROR:")
|
||||
stdouterrors = filter(test.search, results)
|
||||
for e in stdouterrors:
|
||||
debug.error(e.strip("\n"))
|
||||
|
||||
out_errors = len(stdouterrors)
|
||||
|
||||
return summary_errors + out_errors + ext_errors
|
||||
|
||||
|
||||
def run_pex(name, gds_name, sp_name, output=None):
|
||||
"""Run pex on a given top-level name which is
|
||||
implemented in gds_name and sp_name. """
|
||||
OPTS = globals.get_opts()
|
||||
from tech import drc
|
||||
if output == None:
|
||||
output = name + ".pex.netlist"
|
||||
|
||||
# check if lvs report has been done
|
||||
# if not run drc and lvs
|
||||
if not os.path.isfile(name + ".lvs.report"):
|
||||
run_drc(name, gds_name)
|
||||
run_lvs(name, gds_name, sp_name)
|
||||
|
||||
pex_rules = drc["xrc_rules"]
|
||||
pex_runset = {
|
||||
'pexRulesFile': pex_rules,
|
||||
'pexRunDir': OPTS.openram_temp,
|
||||
'pexLayoutPaths': gds_name,
|
||||
'pexLayoutPrimary': name,
|
||||
#'pexSourcePath' : OPTS.openram_temp+"extracted.sp",
|
||||
'pexSourcePath': sp_name,
|
||||
'pexSourcePrimary': name,
|
||||
'pexReportFile': name + ".lvs.report",
|
||||
'pexPexNetlistFile': output,
|
||||
'pexPexReportFile': name + ".pex.report",
|
||||
'pexMaskDBFile': name + ".maskdb",
|
||||
'cmnFDIDEFLayoutPath': name + ".def",
|
||||
}
|
||||
|
||||
# write the runset file
|
||||
f = open(OPTS.openram_temp + "pex_runset", "w")
|
||||
for k in sorted(pex_runset.iterkeys()):
|
||||
f.write("*{0}: {1}\n".format(k, pex_runset[k]))
|
||||
f.close()
|
||||
|
||||
# run pex
|
||||
os.chdir(OPTS.openram_temp)
|
||||
errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, name)
|
||||
outfile = "{0}{1}.pex.out".format(OPTS.openram_temp, name)
|
||||
|
||||
cmd = "{0} -gui -pex {1}pex_runset -batch 2> {2} 1> {3}".format(OPTS.calibre_exe,
|
||||
OPTS.openram_temp,
|
||||
errfile,
|
||||
outfile)
|
||||
debug.info(2, cmd)
|
||||
os.system(cmd)
|
||||
|
||||
# also check the output file
|
||||
f = open(outfile, "r")
|
||||
results = f.readlines()
|
||||
f.close()
|
||||
|
||||
# Errors begin with "ERROR:"
|
||||
test = re.compile("ERROR:")
|
||||
stdouterrors = filter(test.search, results)
|
||||
for e in stdouterrors:
|
||||
debug.error(e.strip("\n"))
|
||||
|
||||
out_errors = len(stdouterrors)
|
||||
|
||||
assert(os.path.isfile(output))
|
||||
correct_port(name, output, sp_name)
|
||||
|
||||
return out_errors
|
||||
|
||||
|
||||
def correct_port(name, output_file_name, ref_file_name):
|
||||
pex_file = open(output_file_name, "r")
|
||||
contents = pex_file.read()
|
||||
# locate the start of circuit definition line
|
||||
match = re.search(".subckt " + str(name) + ".*", contents)
|
||||
match_index_start = match.start()
|
||||
pex_file.seek(match_index_start)
|
||||
rest_text = pex_file.read()
|
||||
# locate the end of circuit definition line
|
||||
match = re.search("\* \n", rest_text)
|
||||
match_index_end = match.start()
|
||||
# store the unchanged part of pex file in memory
|
||||
pex_file.seek(0)
|
||||
part1 = pex_file.read(match_index_start)
|
||||
pex_file.seek(match_index_start + match_index_end)
|
||||
part2 = pex_file.read()
|
||||
pex_file.close()
|
||||
|
||||
# obatin the correct definition line from the original spice file
|
||||
sp_file = open(ref_file_name, "r")
|
||||
contents = sp_file.read()
|
||||
circuit_title = re.search(".SUBCKT " + str(name) + ".*\n", contents)
|
||||
circuit_title = circuit_title.group()
|
||||
sp_file.close()
|
||||
|
||||
# write the new pex file with info in the memory
|
||||
output_file = open(output_file_name, "w")
|
||||
output_file.write(part1)
|
||||
output_file.write(circuit_title)
|
||||
output_file.write(part2)
|
||||
output_file.close()
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
import globals
|
||||
import re
|
||||
import debug
|
||||
|
||||
OPTS = globals.get_opts()
|
||||
|
||||
# 0.1% is the relative tolerance for convergence
|
||||
error_tolerance = 0.001
|
||||
|
||||
# times are in ns, so this is how many digits of precision
|
||||
# 3 digits = 1ps
|
||||
# 4 digits = 0.1ps
|
||||
# etc.
|
||||
time_precision = 3
|
||||
# voltages are in volts
|
||||
# 3 digits = 1mv
|
||||
# 4 digits = 0.1mv
|
||||
# 5 digits = 0.01mv
|
||||
# 6 digits = 1uv
|
||||
# etc
|
||||
voltage_precision = 5
|
||||
|
||||
def relative_compare(value1,value2):
|
||||
""" This is used to compare relative values for convergence. """
|
||||
return (abs(value1 - value2) / max(value1,value2) <= error_tolerance)
|
||||
|
||||
|
||||
def parse_output(filename, key):
|
||||
"""Parses a hspice output.lis file for a key value"""
|
||||
f = open("{0}/{1}.lis".format(OPTS.openram_temp, filename), "r")
|
||||
contents = f.read()
|
||||
val = re.search(r"{0}\s*=\s*(-?\d+.?\d*\S*)\s+.*".format(key), contents)
|
||||
if val != None:
|
||||
debug.info(3, "Key = " + key + " Val = " + val.group(1))
|
||||
return val.group(1)
|
||||
else:
|
||||
return "Failed"
|
||||
|
||||
def round_time(time):
|
||||
return round(time,time_precision)
|
||||
|
||||
def round_voltage(voltage):
|
||||
return round(voltage,voltage_precision)
|
||||
|
||||
def convert_to_float(number):
|
||||
"""Converts a string into a (float) number; also converts units(m,u,n,p)"""
|
||||
if number == "Failed":
|
||||
return False
|
||||
|
||||
# start out with a binary value
|
||||
float_value = False
|
||||
try:
|
||||
# checks if string is a float without letter units
|
||||
float_value = float(number)
|
||||
except ValueError:
|
||||
# see if it is in scientific notation
|
||||
unit = re.search(r"(-?\d+\.?\d*)e(\-?\+?\d+)", number)
|
||||
if unit != None:
|
||||
float_value=float(unit.group(1)) * (10 ^ float(unit.group(2)))
|
||||
|
||||
# see if it is in spice notation
|
||||
unit = re.search(r"(-?\d+\.?\d*)(m?u?n?p?f?)", number)
|
||||
if unit != None:
|
||||
float_value = {
|
||||
'm': lambda x: x * 0.001, # milli
|
||||
'u': lambda x: x * 0.000001, # micro
|
||||
'n': lambda x: x * 0.000000001, # nano
|
||||
'p': lambda x: x * 0.000000000001, # pico
|
||||
'f': lambda x: x * 0.000000000000001 # femto
|
||||
}[unit.group(2)](float(unit.group(1)))
|
||||
|
||||
# if we weren't able to convert it to a float then error out
|
||||
if not type(float_value)==float:
|
||||
debug.error("Invalid number: {0}".format(number),1)
|
||||
|
||||
return float_value
|
||||
|
|
@ -0,0 +1,444 @@
|
|||
import sys
|
||||
import re
|
||||
import globals
|
||||
import debug
|
||||
import tech
|
||||
import math
|
||||
import stimuli
|
||||
import charutils as ch
|
||||
|
||||
OPTS = globals.get_opts()
|
||||
|
||||
class delay():
|
||||
"""
|
||||
Functions to measure the delay of the SRAM at a given address and
|
||||
data bit.
|
||||
"""
|
||||
|
||||
def __init__(self,sram,spfile):
|
||||
self.name = sram.name
|
||||
self.num_words = sram.num_words
|
||||
self.word_size = sram.word_size
|
||||
self.addr_size = sram.addr_size
|
||||
self.sram_sp_file = spfile
|
||||
|
||||
|
||||
def check_arguments(self):
|
||||
"""Checks if arguments given for write_stimulus() meets requirements"""
|
||||
try:
|
||||
int(self.probe_address, 2)
|
||||
except ValueError:
|
||||
debug.error("Probe Address is not of binary form: {0}".format(self.probe_address),1)
|
||||
|
||||
if len(self.probe_address) != self.addr_size:
|
||||
debug.error("Probe Address's number of bits does not correspond to given SRAM",1)
|
||||
|
||||
if not isinstance(self.probe_data, int) or self.probe_data>self.word_size or self.probe_data<0:
|
||||
debug.error("Given probe_data is not an integer to specify a data bit",1)
|
||||
|
||||
|
||||
def write_stimulus(self, feasible_period, target_period, data_value):
|
||||
"""Creates a stimulus file for simulations to probe a certain bitcell, given an address and data-position of the data-word
|
||||
(probe-address form: '111010000' LSB=0, MSB=1)
|
||||
(probe_data form: number corresponding to the bit position of data-bus, begins with position 0)
|
||||
"""
|
||||
self.check_arguments()
|
||||
|
||||
# obtains list of time-points for each rising clk edge
|
||||
self.obtain_cycle_times(slow_period=feasible_period,
|
||||
fast_period=target_period)
|
||||
|
||||
# creates and opens stimulus file for writing
|
||||
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
|
||||
self.sf = open(temp_stim, "w")
|
||||
self.sf.write("* Stimulus data value of {0} for target period of {1}n\n".format(data_value, target_period))
|
||||
self.sf.write("\n")
|
||||
|
||||
# include files in stimulus file
|
||||
model_list = tech.spice["fet_models"] + [self.sram_sp_file]
|
||||
stimuli.write_include(stim_file=self.sf, models=model_list)
|
||||
self.sf.write("\n")
|
||||
|
||||
# add vdd/gnd statements
|
||||
self.sf.write("* Global Power Supplies\n")
|
||||
stimuli.write_supply(stim_file=self.sf,
|
||||
vdd_name=tech.spice["vdd_name"],
|
||||
gnd_name=tech.spice["gnd_name"],
|
||||
vdd_voltage=tech.spice["supply_voltage"],
|
||||
gnd_voltage=tech.spice["gnd_voltage"])
|
||||
self.sf.write("\n")
|
||||
|
||||
# instantiate the sram
|
||||
self.sf.write("* Instantiation of the SRAM\n")
|
||||
stimuli.inst_sram(stim_file=self.sf,
|
||||
abits=self.addr_size,
|
||||
dbits=self.word_size,
|
||||
sram_name=self.name)
|
||||
self.sf.write("\n")
|
||||
|
||||
# create a buffer and an inverter
|
||||
self.sf.write("* Buffers and inverter Initialization\n")
|
||||
# FIXME: We should replace the clock buffer with the same
|
||||
# 2x buffer for control signals. This needs the buffer to be
|
||||
# added to the control logic though.
|
||||
stimuli.create_buffer(stim_file=self.sf,
|
||||
buffer_name="clk1_buffer",
|
||||
size=[1, 4])
|
||||
self.sf.write("\n")
|
||||
stimuli.create_buffer(stim_file=self.sf,
|
||||
buffer_name="clk2_buffer",
|
||||
size=[8, 16])
|
||||
self.sf.write("\n")
|
||||
|
||||
stimuli.create_buffer(stim_file=self.sf,
|
||||
buffer_name="buffer",
|
||||
size=[1, 2])
|
||||
self.sf.write("\n")
|
||||
|
||||
stimuli.create_inverter(stim_file=self.sf)
|
||||
self.sf.write("\n")
|
||||
|
||||
# add a buffer for each signal and an inverter for WEb
|
||||
signal_list = []
|
||||
for i in range(self.word_size):
|
||||
signal_list.append("D[{0}]".format(i))
|
||||
for j in range(self.addr_size):
|
||||
signal_list.append("A[{0}]".format(j))
|
||||
for k in tech.spice["control_signals"]:
|
||||
signal_list.append(k)
|
||||
self.sf.write("*Buffers for each generated signal and Inv for WEb\n")
|
||||
stimuli.add_buffer(stim_file=self.sf,
|
||||
buffer_name="buffer",
|
||||
signal_list=signal_list)
|
||||
stimuli.add_buffer(stim_file=self.sf,
|
||||
buffer_name="clk1_buffer",
|
||||
signal_list=["clk"])
|
||||
stimuli.add_buffer(stim_file=self.sf,
|
||||
buffer_name="clk2_buffer",
|
||||
signal_list=["clk_buf"])
|
||||
stimuli.add_buffer(stim_file=self.sf,
|
||||
buffer_name="buffer",
|
||||
signal_list=["WEb_trans"])
|
||||
stimuli.add_inverter(stim_file=self.sf,
|
||||
signal_list=["WEb_trans"])
|
||||
self.sf.write("\n")
|
||||
|
||||
# add access transistors for data-bus
|
||||
self.sf.write("* Transmission Gates for data-bus\n")
|
||||
stimuli.add_accesstx(stim_file=self.sf, dbits=self.word_size)
|
||||
self.sf.write("\n")
|
||||
|
||||
# generate data and addr signals
|
||||
self.sf.write("*Generation of data and address signals\n")
|
||||
if data_value == tech.spice["supply_voltage"]:
|
||||
v_val = tech.spice["gnd_voltage"]
|
||||
else:
|
||||
v_val = tech.spice["supply_voltage"]
|
||||
for i in range(self.word_size):
|
||||
if i == self.probe_data:
|
||||
stimuli.gen_data_pwl(stim_file=self.sf,
|
||||
key_times=self.cycle_times,
|
||||
sig_name="D[{0}]".format(i),
|
||||
data_value=data_value,
|
||||
feasible_period=feasible_period,
|
||||
target_period=target_period,
|
||||
t_rise=tech.spice["rise_time"],
|
||||
t_fall=tech.spice["fall_time"])
|
||||
else:
|
||||
stimuli.gen_constant(stim_file=self.sf,
|
||||
sig_name="D[{0}]".format(i),
|
||||
v_ref=tech.spice["gnd_voltage"],
|
||||
v_val=v_val)
|
||||
|
||||
stimuli.gen_addr_pwl(stim_file=self.sf,
|
||||
key_times=self.cycle_times,
|
||||
addr=self.probe_address,
|
||||
feasible_period=feasible_period,
|
||||
target_period=target_period,
|
||||
t_rise=tech.spice["rise_time"],
|
||||
t_fall=tech.spice["fall_time"])
|
||||
self.sf.write("\n")
|
||||
|
||||
# generate control signals
|
||||
self.sf.write("*Generation of control signals\n")
|
||||
# CSb
|
||||
(x_list, y_list) = stimuli.gen_csb_pwl(key_times=self.cycle_times,
|
||||
feasible_period=feasible_period,
|
||||
target_period=target_period,
|
||||
t_rise=tech.spice["rise_time"],
|
||||
t_fall=tech.spice["fall_time"])
|
||||
stimuli.gen_pwl(stim_file=self.sf,
|
||||
sig_name="CSb",
|
||||
x_list=x_list,
|
||||
y_list=y_list)
|
||||
# WEb
|
||||
(x_list, y_list) = stimuli.gen_web_pwl(key_times=self.cycle_times,
|
||||
feasible_period=feasible_period,
|
||||
target_period=target_period,
|
||||
t_rise=tech.spice["rise_time"],
|
||||
t_fall=tech.spice["fall_time"])
|
||||
stimuli.gen_pwl(stim_file=self.sf,
|
||||
sig_name="WEb",
|
||||
x_list=x_list,
|
||||
y_list=y_list)
|
||||
# OEb
|
||||
(x_list, y_list) = stimuli.gen_oeb_pwl(key_times=self.cycle_times,
|
||||
feasible_period=feasible_period,
|
||||
target_period=target_period,
|
||||
t_rise=tech.spice["rise_time"],
|
||||
t_fall=tech.spice["fall_time"])
|
||||
stimuli.gen_pwl(stim_file=self.sf,
|
||||
sig_name="OEb",
|
||||
x_list=x_list,
|
||||
y_list=y_list)
|
||||
# WEb_transmission_gate
|
||||
(x_list, y_list) = stimuli.gen_web_trans_pwl(key_times=self.cycle_times,
|
||||
feasible_period=feasible_period,
|
||||
target_period=target_period,
|
||||
t_rise=tech.spice["rise_time"],
|
||||
t_fall=tech.spice["fall_time"])
|
||||
stimuli.gen_pwl(stim_file=self.sf,
|
||||
sig_name="WEb_trans",
|
||||
x_list=x_list,
|
||||
y_list=y_list)
|
||||
self.sf.write("\n")
|
||||
|
||||
self.write_clock()
|
||||
|
||||
self.write_measures(data_value)
|
||||
|
||||
self.write_control()
|
||||
|
||||
self.sf.close()
|
||||
|
||||
def write_clock(self):
|
||||
# generate clk PWL based on the clock periods
|
||||
self.sf.write("* Generation of global clock signal\n")
|
||||
stimuli.gen_clk_pwl(stim_file=self.sf,
|
||||
cycle_times=self.cycle_times,
|
||||
t_rise=tech.spice["rise_time"],
|
||||
t_fall=tech.spice["fall_time"])
|
||||
self.sf.write("\n")
|
||||
|
||||
def write_measures(self, data_value):
|
||||
# meas statement for delay and power measurements
|
||||
self.sf.write("* Measure statements for delay and power\n")
|
||||
|
||||
# add measure statments for delay
|
||||
trig_name = tech.spice["clk"] + "_buf_buf"
|
||||
targ_name = "{0}".format("DATA[{0}]".format(self.probe_data))
|
||||
trig_val = targ_val = 0.5 * tech.spice["supply_voltage"]
|
||||
trig_dir = self.read_cycle
|
||||
targ_dir = "RISE" if data_value == tech.spice["supply_voltage"] else "FALL"
|
||||
td = self.cycle_times[self.clear_bus_cycle]
|
||||
stimuli.gen_meas_delay(stim_file=self.sf,
|
||||
meas_name="DELAY",
|
||||
trig_name=trig_name,
|
||||
targ_name=targ_name,
|
||||
trig_val=trig_val,
|
||||
targ_val=targ_val,
|
||||
trig_dir=trig_dir,
|
||||
targ_dir=targ_dir,
|
||||
td=td)
|
||||
|
||||
# add measure statements for power
|
||||
t_initial = self.cycle_times[self.write_cycle]
|
||||
t_final = self.cycle_times[self.write_cycle+1]
|
||||
stimuli.gen_meas_power(stim_file=self.sf,
|
||||
meas_name="POWER_WRITE",
|
||||
t_initial=t_initial,
|
||||
t_final=t_final)
|
||||
|
||||
t_initial = self.cycle_times[self.read_cycle]
|
||||
t_final = self.cycle_times[self.read_cycle+1]
|
||||
stimuli.gen_meas_power(stim_file=self.sf,
|
||||
meas_name="POWER_READ",
|
||||
t_initial=t_initial,
|
||||
t_final=t_final)
|
||||
self.sf.write("\n")
|
||||
|
||||
|
||||
def write_control(self):
|
||||
# run until the last cycle time
|
||||
end_time = self.cycle_times[-1]
|
||||
self.sf.write(".TRAN 5p {0}n\n".format(end_time))
|
||||
|
||||
if OPTS.spice_version == "hspice":
|
||||
# create plots for all signals
|
||||
self.sf.write(".OPTIONS POST=1 PROBE\n")
|
||||
self.sf.write(".probe V(*)\n")
|
||||
# end the stimulus file
|
||||
self.sf.write(".end\n")
|
||||
self.sf.close()
|
||||
else:
|
||||
self.sf.write(".control\n")
|
||||
self.sf.write("run\n")
|
||||
self.sf.write("quit\n")
|
||||
self.sf.write(".endc\n")
|
||||
self.sf.write(".end\n")
|
||||
|
||||
|
||||
|
||||
def find_feasible_period(self,initial_period):
|
||||
"""Uses an initial period and finds a feasible period before we
|
||||
run the binary search algorithm to find min period. We check if
|
||||
the given clock period is valid and if it's not, we continue to
|
||||
double the period until we find a valid period to use as a
|
||||
starting point. """
|
||||
|
||||
feasible_period = initial_period
|
||||
time_out = 8
|
||||
while True:
|
||||
debug.info(1, "Finding feasible period: {0}ns".format(feasible_period))
|
||||
time_out -= 1
|
||||
|
||||
if (time_out <= 0):
|
||||
debug.error("Timed out, could not find a feasible period.",2)
|
||||
|
||||
(success, delay_out)=self.try_period(feasible_period,feasible_period,tech.spice["supply_voltage"])
|
||||
if not success:
|
||||
feasible_period = 2 * feasible_period
|
||||
continue
|
||||
|
||||
(success, delay_out)=self.try_period(feasible_period,feasible_period,tech.spice["gnd_voltage"])
|
||||
if not success:
|
||||
feasible_period = 2 * feasible_period
|
||||
continue
|
||||
|
||||
debug.info(1, "Starting Binary Search Algorithm with feasible_period: {0}ns".format(feasible_period))
|
||||
return feasible_period
|
||||
|
||||
|
||||
def try_period(self, feasible_period, target_period, data_value):
|
||||
""" This tries to simulate a period and checks if the result
|
||||
works. If so, it returns True. If not, it it doubles the
|
||||
period and returns False."""
|
||||
|
||||
# Checking from not data_value to data_value
|
||||
self.write_stimulus(feasible_period, target_period, data_value)
|
||||
stimuli.run_sim()
|
||||
delay_value = ch.convert_to_float(ch.parse_output("timing", "delay"))
|
||||
|
||||
# if it failed or the read was longer than a period
|
||||
if type(delay_value)!=float or delay_value*1e9>target_period:
|
||||
debug.info(2,"Infeasible period " + str(target_period) + " delay " + str(delay_value*1e9) + "ns")
|
||||
return (False, "NA")
|
||||
else:
|
||||
debug.info(2,"Feasible period " + str(feasible_period) \
|
||||
+ ", target period " + str(target_period) \
|
||||
+ ", read/write of " + str(data_value) \
|
||||
+ ", delay=" + str(delay_value*1e9) + "ns")
|
||||
#key=raw_input("press return to continue")
|
||||
|
||||
return (True, delay_value*1e9)
|
||||
|
||||
|
||||
def find_min_period(self,data_value):
|
||||
"""Creates a spice test and instantiates a single SRAM for
|
||||
testing. Returns a tuple of the lowest period and its
|
||||
clk-to-q delay for the specified data_value."""
|
||||
|
||||
# Find a valid and feasible period before starting the binary search
|
||||
# We just try 2.0ns here, but any value would work albeit slower.
|
||||
feasible_period = self.find_feasible_period(tech.spice["feasible_period"])
|
||||
previous_period = ub_period = feasible_period
|
||||
lb_period = 0.0
|
||||
|
||||
# Binary search algorithm to find the min period (max frequency) of design
|
||||
time_out = 25
|
||||
while True:
|
||||
time_out -= 1
|
||||
if (time_out <= 0):
|
||||
debug.error("Timed out, could not converge on minimum period.",2)
|
||||
|
||||
target_period = 0.5 * (ub_period + lb_period)
|
||||
debug.info(1, "MinPeriod Search: {0}ns (ub: {1} lb: {2})".format(target_period,
|
||||
ub_period,
|
||||
lb_period))
|
||||
|
||||
(success, delay_out) = self.try_period(feasible_period, target_period, data_value)
|
||||
if success:
|
||||
if ch.relative_compare(ub_period, target_period):
|
||||
# use the two values to compare, but only return the ub since it is guaranteed feasible
|
||||
(success, delay_out) = self.try_period(feasible_period, ub_period, data_value)
|
||||
return (ub_period, delay_out)
|
||||
fail_flag = False
|
||||
ub_period = target_period
|
||||
else:
|
||||
lb_period = target_period
|
||||
|
||||
|
||||
self.error("Should not reach here.",-1)
|
||||
return (target_period, delay_out)
|
||||
|
||||
def set_probe(self,probe_address, probe_data):
|
||||
""" Probe address and data can be set separately to utilize other
|
||||
functions in this characterizer besides analyze."""
|
||||
self.probe_address = probe_address
|
||||
self.probe_data = probe_data
|
||||
|
||||
def analyze(self,probe_address, probe_data):
|
||||
"""main function to calculate the min period for a low_to_high
|
||||
transistion and a high_to_low transistion returns a dictionary
|
||||
that contains all both the min period and associated delays
|
||||
Dictionary Keys: min_period1, delay1, min_period0, delay0
|
||||
"""
|
||||
self.set_probe(probe_address, probe_data)
|
||||
|
||||
(min_period1, delay1) = self.find_min_period(tech.spice["supply_voltage"])
|
||||
if (min_period1 == None) or (delay1 == None):
|
||||
return None
|
||||
debug.info(1, "Min Period for low_to_high transistion: {0}n with a delay of {1}".format(min_period1, delay1))
|
||||
(min_period0, delay0) = self.find_min_period(tech.spice["gnd_voltage"])
|
||||
if (min_period0 == None) or (delay0 == None):
|
||||
return None
|
||||
debug.info(1, "Min Period for high_to_low transistion: {0}n with a delay of {1}".format(min_period0, delay0))
|
||||
data = {"min_period1": min_period1, # period in ns
|
||||
"delay1": delay1, # delay in s
|
||||
"min_period0": min_period0,
|
||||
"delay0": delay0
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
def obtain_cycle_times(self, slow_period, fast_period):
|
||||
"""Returns a list of key time-points [ns] of the waveform (each rising edge)
|
||||
of the cycles to do a timing evaluation. The last time is the end of the simulation
|
||||
and does not need a rising edge."""
|
||||
|
||||
# idle: half cycle, no operation
|
||||
t_current = 0.5 * slow_period
|
||||
|
||||
# slow cycle1: W data 1 address 1111 to initialize cell to a value
|
||||
self.cycle_times = [t_current]
|
||||
self.init_cycle=1
|
||||
t_current += slow_period
|
||||
|
||||
# slow cycle2: R data 1 address 1111 (to ensure cell was written to opposite data value)
|
||||
self.cycle_times.append(t_current)
|
||||
self.verify_init_cycle=2
|
||||
t_current += slow_period
|
||||
|
||||
# fast cycle3: W data 0 address 1111 (to ensure a write of opposite value works)
|
||||
self.cycle_times.append(t_current)
|
||||
self.write_cycle=3
|
||||
t_current += fast_period
|
||||
|
||||
# fast cycle4: W data 1 address 0000 (to invert the bus cap from prev write)
|
||||
self.cycle_times.append(t_current)
|
||||
self.clear_bus_cycle=4
|
||||
t_current += fast_period
|
||||
|
||||
# fast cycle5: R data 0 address 1111 (to ensure that a read works and gets the value)
|
||||
self.cycle_times.append(t_current)
|
||||
self.read_cycle=5
|
||||
t_current += fast_period
|
||||
|
||||
# slow cycle 6: wait a slow clock period to end the simulation
|
||||
self.cycle_times.append(t_current)
|
||||
self.wait_cycle=6
|
||||
t_current += slow_period
|
||||
|
||||
# end time of simulation
|
||||
self.cycle_times.append(t_current)
|
||||
|
||||
|
|
@ -0,0 +1,294 @@
|
|||
import os
|
||||
import sys
|
||||
import re
|
||||
import globals
|
||||
import debug
|
||||
import tech
|
||||
import math
|
||||
import setup_hold
|
||||
import delay
|
||||
import charutils as ch
|
||||
|
||||
|
||||
OPTS = globals.get_opts()
|
||||
|
||||
class lib:
|
||||
""" lib file generation."""
|
||||
|
||||
def __init__(self, libname, sram, spfile):
|
||||
self.name = sram.name
|
||||
self.num_words = sram.num_words
|
||||
self.word_size = sram.word_size
|
||||
self.addr_size = sram.addr_size
|
||||
|
||||
self.sh = setup_hold.setup_hold()
|
||||
self.d = delay.delay(sram, spfile)
|
||||
|
||||
debug.info(1,"Writing to {0}".format(libname))
|
||||
self.lib = open(libname, "w")
|
||||
|
||||
self.lib.write("library ({0}_lib)".format(self.name))
|
||||
self.lib.write("{\n")
|
||||
self.lib.write(" delay_model : \"table_lookup\";\n")
|
||||
|
||||
self.write_units()
|
||||
self.write_defaults()
|
||||
self.write_LUT()
|
||||
|
||||
self.lib.write(" default_operating_conditions : TT; \n")
|
||||
|
||||
self.write_bus()
|
||||
|
||||
self.lib.write("cell ({0})".format(self.name))
|
||||
self.lib.write("{\n")
|
||||
self.lib.write(" memory(){ \n")
|
||||
self.lib.write(" type : ram;\n")
|
||||
self.lib.write(" address_width : {0};\n".format(self.addr_size))
|
||||
self.lib.write(" word_width : {0};\n".format(self.word_size))
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" interface_timing : true;\n")
|
||||
self.lib.write(" dont_use : true;\n")
|
||||
self.lib.write(" map_only : true;\n")
|
||||
self.lib.write(" dont_touch : true;\n")
|
||||
self.lib.write(" area : {0};\n\n".format(sram.width * sram.height))
|
||||
|
||||
times = self.sh.analyze()
|
||||
|
||||
for i in times.keys():
|
||||
times[i] = ch.round_time(times[i])
|
||||
|
||||
|
||||
probe_address = "1" * self.addr_size
|
||||
probe_data = self.word_size - 1
|
||||
|
||||
data = self.d.analyze(probe_address, probe_data)
|
||||
for i in data.keys():
|
||||
data[i] = ch.round_time(data[i])
|
||||
|
||||
|
||||
self.write_data_bus(data, times)
|
||||
self.write_addr_bus(times)
|
||||
self.write_control_pins(times)
|
||||
self.write_clk(data)
|
||||
|
||||
self.lib.close()
|
||||
|
||||
|
||||
def write_units(self):
|
||||
""" Adds default units for time, voltage, current,..."""
|
||||
|
||||
self.lib.write(" time_unit : \"1ns\" ;\n")
|
||||
self.lib.write(" voltage_unit : \"1v\" ;\n")
|
||||
self.lib.write(" current_unit : \"1mA\" ;\n")
|
||||
self.lib.write(" resistance_unit : \"1kohm\" ;\n")
|
||||
self.lib.write(" capacitive_load_unit(1 ,fF) ;\n")
|
||||
self.lib.write(" leakage_power_unit : \"1uW\" ;\n")
|
||||
self.lib.write(" pulling_resistance_unit :\"1kohm\" ;\n")
|
||||
self.lib.write(" operating_conditions(TT){\n")
|
||||
self.lib.write(" voltage : {0} ;\n".format(tech.spice["supply_voltage"]))
|
||||
self.lib.write(" temperature : 25.000 ;\n")
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
def write_defaults(self):
|
||||
""" Adds default values for slew and capacitance."""
|
||||
|
||||
self.lib.write(" input_threshold_pct_fall : 50.0 ;\n")
|
||||
self.lib.write(" output_threshold_pct_fall : 50.0 ;\n")
|
||||
self.lib.write(" input_threshold_pct_rise : 50.0 ;\n")
|
||||
self.lib.write(" output_threshold_pct_rise : 50.0 ;\n")
|
||||
self.lib.write(" slew_lower_threshold_pct_fall : 10.0 ;\n")
|
||||
self.lib.write(" slew_upper_threshold_pct_fall : 90.0 ;\n")
|
||||
self.lib.write(" slew_lower_threshold_pct_rise : 10.0 ;\n")
|
||||
self.lib.write(" slew_upper_threshold_pct_rise : 90.0 ;\n\n")
|
||||
|
||||
self.lib.write(" default_cell_leakage_power : 0.0 ;\n")
|
||||
self.lib.write(" default_leakage_power_density : 0.0 ;\n")
|
||||
self.lib.write(" default_input_pin_cap : 1.0 ;\n")
|
||||
self.lib.write(" default_inout_pin_cap : 1.0 ;\n")
|
||||
self.lib.write(" default_output_pin_cap : 0.0 ;\n")
|
||||
self.lib.write(" default_max_transition : 0.5 ;\n")
|
||||
self.lib.write(" default_fanout_load : 1.0 ;\n")
|
||||
self.lib.write(" default_max_fanout : 4.0 ;\n")
|
||||
self.lib.write(" default_connection_class : universal ;\n\n")
|
||||
|
||||
def write_LUT(self):
|
||||
""" Adds lookup_table format (A 1x1 lookup_table)."""
|
||||
|
||||
Tran = ["CELL_UP_FOR_CLOCK" , "CELL_DN_FOR_CLOCK"]
|
||||
for i in Tran:
|
||||
self.lib.write(" lu_table_template({0})".format(i))
|
||||
self.lib.write("{\n")
|
||||
self.lib.write(" variable_1 : input_net_transition;\n")
|
||||
self.lib.write(" variable_2 : total_output_net_capacitance;\n")
|
||||
self.lib.write(" index_1 (\"0.5\");\n")
|
||||
self.lib.write(" index_2 (\"0.5\");\n")
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
CONS = ["CONSTRAINT_HIGH_POS" , "CONSTRAINT_LOW_POS"]
|
||||
for i in CONS:
|
||||
self.lib.write(" lu_table_template({0})".format(i))
|
||||
self.lib.write("{\n")
|
||||
self.lib.write(" variable_1 : related_pin_transition;\n")
|
||||
self.lib.write(" variable_2 : constrained_pin_transition;\n")
|
||||
self.lib.write(" index_1 (\"0.5\");\n")
|
||||
self.lib.write(" index_2 (\"0.5\");\n")
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
self.lib.write(" lu_table_template(CLK_TRAN) {\n")
|
||||
self.lib.write(" variable_1 : constrained_pin_transition;\n")
|
||||
self.lib.write(" index_1 (\"0.5\");\n")
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
self.lib.write(" lu_table_template(TRAN) {\n")
|
||||
self.lib.write(" variable_1 : total_output_net_capacitance;\n")
|
||||
self.lib.write(" index_1 (\"0.5\");\n")
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
|
||||
def write_bus(self):
|
||||
""" Adds format of DATA and ADDR bus."""
|
||||
|
||||
self.lib.write("\n\n")
|
||||
self.lib.write(" type (DATA){\n")
|
||||
self.lib.write(" base_type : array;\n")
|
||||
self.lib.write(" data_type : bit;\n")
|
||||
self.lib.write(" bit_width : {0};\n".format(self.word_size))
|
||||
self.lib.write(" bit_from : 0;\n")
|
||||
self.lib.write(" bit_to : {0};\n".format(self.word_size - 1))
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
self.lib.write(" type (ADDR){\n")
|
||||
self.lib.write(" base_type : array;\n")
|
||||
self.lib.write(" data_type : bit;\n")
|
||||
self.lib.write(" bit_width : {0};\n".format(self.addr_size))
|
||||
self.lib.write(" bit_from : 0;\n")
|
||||
self.lib.write(" bit_to : {0};\n".format(self.addr_size - 1))
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
|
||||
def write_timing(self, times):
|
||||
""" Adds Setup and Hold timing results"""
|
||||
|
||||
self.lib.write(" timing(){ \n")
|
||||
self.lib.write(" timing_type : setup_rising; \n")
|
||||
self.lib.write(" related_pin : \"clk\"; \n")
|
||||
self.lib.write(" rise_constraint(CONSTRAINT_HIGH_POS) {\n")
|
||||
self.lib.write(" values(\"{0}\"); \n".format(times["setup_time_one"]))
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" fall_constraint(CONSTRAINT_LOW_POS) {\n")
|
||||
self.lib.write(" values(\"{0}\"); \n".format(times["setup_time_zero"]))
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" timing(){ \n")
|
||||
self.lib.write(" timing_type : hold_rising; \n")
|
||||
self.lib.write(" related_pin : \"clk\"; \n")
|
||||
self.lib.write(" rise_constraint(CONSTRAINT_HIGH_POS) {\n")
|
||||
self.lib.write(" values(\"{0}\"); \n".format(times["hold_time_one"]))
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" fall_constraint(CONSTRAINT_LOW_POS) {\n")
|
||||
self.lib.write(" values(\"{0}\"); \n".format(times["hold_time_zero"]))
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" }\n")
|
||||
|
||||
|
||||
|
||||
def write_data_bus(self, data, times):
|
||||
""" Adds data bus timing results."""
|
||||
self.lib.write(" bus(DATA){\n")
|
||||
self.lib.write(" bus_type : DATA; \n")
|
||||
self.lib.write(" direction : inout; \n")
|
||||
self.lib.write(" max_capacitance : {0}; \n".format(tech.spice["FF_in_cap"] + tech.spice["tri_gate_out_cap"] ))
|
||||
self.lib.write(" pin(DATA[{0}:0])".format(self.word_size - 1))
|
||||
self.lib.write("{\n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" three_state : \"OEb & !clk\"; \n")
|
||||
|
||||
self.lib.write(" memory_write(){ \n")
|
||||
self.lib.write(" address : ADDR; \n")
|
||||
self.lib.write(" clocked_on : clk; \n")
|
||||
self.lib.write(" }\n")
|
||||
self.write_timing(times)
|
||||
self.lib.write(" memory_read(){ \n")
|
||||
self.lib.write(" address : ADDR; \n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" timing(){ \n")
|
||||
self.lib.write(" timing_sense : non_unate; \n")
|
||||
self.lib.write(" related_pin : \"clk\"; \n")
|
||||
self.lib.write(" timing_type : rising_edge; \n")
|
||||
self.lib.write(" cell_rise(CELL_UP_FOR_CLOCK) {\n")
|
||||
self.lib.write(" values(\"{0}\"); \n".format(data["delay1"]))
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" cell_fall(CELL_DN_FOR_CLOCK) {\n")
|
||||
self.lib.write(" values(\"{0}\"); \n".format(data["delay0"]))
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" rise_transition(TRAN) {\n")
|
||||
self.lib.write(" values(\"{0}\"); \n".format(data["delay1"]))
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" fall_transition(TRAN) {\n")
|
||||
self.lib.write(" values(\"{0}\"); \n".format(data["delay0"]))
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
|
||||
def write_addr_bus(self, times):
|
||||
""" Adds addr bus timing results."""
|
||||
|
||||
self.lib.write(" bus(ADDR){\n")
|
||||
self.lib.write(" bus_type : ADDR; \n")
|
||||
self.lib.write(" direction : input; \n")
|
||||
self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"]))
|
||||
self.lib.write(" max_transition : 0.5;\n")
|
||||
self.lib.write(" fanout_load : 1.000000;\n")
|
||||
self.lib.write(" pin(ADDR[{0}:0])".format(self.addr_size - 1))
|
||||
self.lib.write("{\n")
|
||||
self.lib.write(" }\n")
|
||||
self.write_timing(times)
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
|
||||
def write_control_pins(self, times):
|
||||
""" Adds control pins timing results."""
|
||||
|
||||
ctrl_pin_names = ["CSb", "OEb", "WEb"]
|
||||
for i in ctrl_pin_names:
|
||||
self.lib.write(" pin({0})".format(i))
|
||||
self.lib.write("{\n")
|
||||
self.lib.write(" direction : input; \n")
|
||||
self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"]))
|
||||
self.write_timing(times)
|
||||
self.lib.write(" }\n\n")
|
||||
|
||||
|
||||
def write_clk(self, data):
|
||||
""" Adds clk pin timing results."""
|
||||
|
||||
self.lib.write(" pin(clk){\n")
|
||||
self.lib.write(" clock : true;\n")
|
||||
self.lib.write(" direction : input; \n")
|
||||
self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"]))
|
||||
self.lib.write(" min_pulse_width_high : {0} ; \n".format(ch.round_time(data["min_period1"])))
|
||||
self.lib.write(" min_pulse_width_low : {0} ; \n".format(ch.round_time(data["min_period0"])))
|
||||
self.lib.write(" timing(){ \n")
|
||||
self.lib.write(" timing_type :\"min_pulse_width\"; \n")
|
||||
self.lib.write(" related_pin : clk; \n")
|
||||
self.lib.write(" rise_constraint(CLK_TRAN) {\n")
|
||||
self.lib.write(" values(\"0\"); \n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" fall_constraint(CLK_TRAN) {\n")
|
||||
self.lib.write(" values(\"0\"); \n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" timing(){ \n")
|
||||
self.lib.write(" timing_type :\"minimum_period\"; \n")
|
||||
self.lib.write(" related_pin : clk; \n")
|
||||
self.lib.write(" rise_constraint(CLK_TRAN) {\n")
|
||||
self.lib.write(" values(\"0\"); \n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" fall_constraint(CLK_TRAN) {\n")
|
||||
self.lib.write(" values(\"0\"); \n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write(" }\n")
|
||||
self.lib.write("}\n")
|
||||
|
|
@ -0,0 +1,328 @@
|
|||
import sys
|
||||
import globals
|
||||
import tech
|
||||
import stimuli
|
||||
import debug
|
||||
import charutils as ch
|
||||
import ms_flop
|
||||
|
||||
OPTS = globals.get_opts()
|
||||
|
||||
vdd = tech.spice["supply_voltage"]
|
||||
gnd = tech.spice["gnd_voltage"]
|
||||
|
||||
|
||||
class setup_hold():
|
||||
"""
|
||||
Functions to calculate the setup and hold times of the SRAM
|
||||
(Bisection Methodology)
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# This must match the spice model order
|
||||
self.pins = ["data_buf", "dout", "dout_bar", "clk_buf", "vdd", "gnd"]
|
||||
self.output_name = "dout"
|
||||
self.model_name = "ms_flop"
|
||||
self.model_location = OPTS.openram_tech + "sp_lib/ms_flop.sp"
|
||||
|
||||
|
||||
def check_arguments(self, correct_value, period):
|
||||
"""Checks if given arguments for write_stimulus() meets requirements"""
|
||||
if not isinstance(correct_value, float):
|
||||
if not isinstance(correct_value, int):
|
||||
debug.error("Given correct_value is not a valid number",1)
|
||||
|
||||
if not isinstance(period, float):
|
||||
if not isinstance(period, int):
|
||||
debug.error("Given period is not a valid number",1)
|
||||
|
||||
|
||||
def write_stimulus(self, mode, target_time, correct_value, period, noise_margin):
|
||||
"""Creates a stimulus file for SRAM setup/hold time calculation"""
|
||||
self.check_arguments(correct_value,period)
|
||||
|
||||
# creates and opens the stimulus file for writing
|
||||
temp_stim = OPTS.openram_temp + "stim.sp"
|
||||
self.sf = open(temp_stim, "w")
|
||||
|
||||
self.write_header(correct_value, period)
|
||||
|
||||
# instantiate the master-slave d-flip-flop
|
||||
self.sf.write("* Instantiation of the Master-Slave D-flip-flop\n")
|
||||
stimuli.inst_model(stim_file=self.sf,
|
||||
pins=self.pins,
|
||||
model_name=self.model_name)
|
||||
self.sf.write("\n")
|
||||
|
||||
# create a buffer for the inputs
|
||||
self.sf.write("* Buffer subckt\n")
|
||||
stimuli.create_buffer(stim_file=self.sf,
|
||||
buffer_name="buffer",
|
||||
size=[1, 1])
|
||||
self.sf.write("\n")
|
||||
|
||||
self.write_data(mode=mode,
|
||||
target_time=target_time,
|
||||
period=period,
|
||||
correct_value=correct_value)
|
||||
|
||||
self.write_clock(period)
|
||||
|
||||
self.write_measures(mode=mode,
|
||||
correct_value=correct_value,
|
||||
noise_margin=noise_margin,
|
||||
period=period)
|
||||
|
||||
self.write_control(period=period)
|
||||
|
||||
self.sf.close()
|
||||
|
||||
def write_header(self, correct_value, period):
|
||||
""" Write the header file with all the models and the power supplies. """
|
||||
self.sf.write("* Stimulus for setup/hold: data {0} period {1}n\n".format(correct_value, period))
|
||||
self.sf.write("\n")
|
||||
|
||||
# include files in stimulus file
|
||||
self.model_list = tech.spice["fet_models"] + [self.model_location]
|
||||
stimuli.write_include(stim_file=self.sf,
|
||||
models=self.model_list)
|
||||
self.sf.write("\n")
|
||||
|
||||
# add vdd/gnd statements
|
||||
self.sf.write("* Global Power Supplies\n")
|
||||
stimuli.write_supply(stim_file=self.sf,
|
||||
vdd_name=tech.spice["vdd_name"],
|
||||
gnd_name=tech.spice["gnd_name"],
|
||||
vdd_voltage=vdd,
|
||||
gnd_voltage=gnd)
|
||||
self.sf.write("\n")
|
||||
|
||||
|
||||
def write_data(self, mode, period, target_time, correct_value):
|
||||
""" Create the buffered data signals for setup/hold analysis """
|
||||
self.sf.write("* Buffer for the DATA signal\n")
|
||||
stimuli.add_buffer(stim_file=self.sf,
|
||||
buffer_name="buffer",
|
||||
signal_list=["DATA"])
|
||||
self.sf.write("* Generation of the data and clk signals\n")
|
||||
incorrect_value = stimuli.get_inverse_value(correct_value)
|
||||
if mode=="HOLD":
|
||||
start_value = correct_value
|
||||
end_value = incorrect_value
|
||||
else:
|
||||
start_value = incorrect_value
|
||||
end_value = correct_value
|
||||
|
||||
stimuli.gen_pulse(stim_file=self.sf,
|
||||
sig_name="DATA",
|
||||
v1=start_value,
|
||||
v2=end_value,
|
||||
offset=target_time,
|
||||
period=2*period,
|
||||
t_rise=tech.spice["rise_time"],
|
||||
t_fall=tech.spice["fall_time"])
|
||||
self.sf.write("\n")
|
||||
|
||||
def write_clock(self,period):
|
||||
""" Create the buffered clock signal for setup/hold analysis """
|
||||
self.sf.write("* Buffer for the clk signal\n")
|
||||
stimuli.add_buffer(stim_file=self.sf,
|
||||
buffer_name="buffer",
|
||||
signal_list=["clk"])
|
||||
self.sf.write("\n")
|
||||
stimuli.gen_pulse(stim_file=self.sf,
|
||||
sig_name="clk",
|
||||
offset=period,
|
||||
period=period,
|
||||
t_rise=tech.spice["rise_time"],
|
||||
t_fall=tech.spice["fall_time"])
|
||||
self.sf.write("\n")
|
||||
|
||||
|
||||
def write_measures(self, mode, correct_value, noise_margin, period):
|
||||
""" Measure statements for setup/hold with right phases. """
|
||||
|
||||
if correct_value == vdd:
|
||||
max_or_min = "MAX"
|
||||
rise_or_fall = "RISE"
|
||||
else:
|
||||
max_or_min = "MIN"
|
||||
rise_or_fall = "FALL"
|
||||
|
||||
incorrect_value = stimuli.get_inverse_value(correct_value)
|
||||
|
||||
self.sf.write("* Measure statements for pass/fail verification\n")
|
||||
self.sf.write(".IC v({0})={1}\n".format(self.output_name, incorrect_value))
|
||||
#self.sf.write(".MEASURE TRAN {0}VOUT {0} v({1}) GOAL={2}\n".format(max_or_min, output_name, noise_margin))
|
||||
# above is the old cmd for hspice, below is the one work for both
|
||||
self.sf.write(".MEASURE TRAN {0}VOUT {0} v({1}) from ={2}n to ={3}n\n".format(max_or_min,
|
||||
self.output_name,
|
||||
1.5*period,
|
||||
2*period))
|
||||
self.sf.write("\n")
|
||||
|
||||
|
||||
def write_control(self, period):
|
||||
# transient window
|
||||
end_time = 2 * period
|
||||
self.sf.write(".TRAN 1n {0}n\n".format(end_time))
|
||||
self.sf.write(".OPTIONS POST=1 PROBE\n")
|
||||
|
||||
if OPTS.spice_version == "hspice":
|
||||
self.sf.write(".probe V(*)\n")
|
||||
# end the stimulus file
|
||||
self.sf.write(".end\n")
|
||||
else:
|
||||
self.sf.write(".control\n")
|
||||
self.sf.write("run\n")
|
||||
self.sf.write("quit\n")
|
||||
self.sf.write(".endc\n")
|
||||
self.sf.write(".end\n")
|
||||
|
||||
|
||||
def bidir_search(self, correct_value, noise_margin, measure_name, mode):
|
||||
""" This will perform a bidirectional search for either setup or hold times.
|
||||
It starts with the feasible priod and looks a half period beyond or before it
|
||||
depending on whether we are doing setup or hold.
|
||||
"""
|
||||
period = tech.spice["feasible_period"]
|
||||
|
||||
# The clock will start being offset by a period, so we want to look before and after
|
||||
# theis time.
|
||||
if mode == "HOLD":
|
||||
target_time = 1.5 * period
|
||||
lower_bound = 0.5*period
|
||||
upper_bound = 1.5 * period
|
||||
else:
|
||||
target_time = 0.5 * period
|
||||
lower_bound = 0.5 * period
|
||||
upper_bound = 1.5*period
|
||||
|
||||
previous_time = target_time
|
||||
# Initial Check if reference setup time passes for correct_value
|
||||
self.write_stimulus(mode=mode,
|
||||
target_time=target_time,
|
||||
correct_value=correct_value,
|
||||
period=period,
|
||||
noise_margin=noise_margin)
|
||||
stimuli.run_sim()
|
||||
output_value = ch.convert_to_float(ch.parse_output("timing", measure_name))
|
||||
debug.info(3,"Correct: {0} Output: {1} NM: {2}".format(correct_value,output_value,noise_margin))
|
||||
if mode == "HOLD":
|
||||
setuphold_time = target_time - period
|
||||
else:
|
||||
setuphold_time = period - target_time
|
||||
debug.info(3,"Target time: {0} Low: {1} Up: {2} Measured: {3}".format(target_time,
|
||||
lower_bound,
|
||||
upper_bound,
|
||||
setuphold_time))
|
||||
if not self.pass_fail_test(output_value, correct_value, noise_margin):
|
||||
debug.error("Initial period/target hold time fails for data value",2)
|
||||
|
||||
# We already found it feasible, so advance one step first thing.
|
||||
if mode == "HOLD":
|
||||
target_time -= 0.5 * (upper_bound - lower_bound)
|
||||
else:
|
||||
target_time += 0.5 * (upper_bound - lower_bound)
|
||||
while True:
|
||||
self.write_stimulus(mode=mode,
|
||||
target_time=target_time,
|
||||
correct_value=correct_value,
|
||||
period=period,
|
||||
noise_margin=noise_margin)
|
||||
if mode == "HOLD":
|
||||
setuphold_time = target_time - period
|
||||
else:
|
||||
setuphold_time = period - target_time
|
||||
debug.info(3,"Target time: {0} Low: {1} Up: {2} Measured: {3}".format(target_time,
|
||||
lower_bound,
|
||||
upper_bound,
|
||||
setuphold_time))
|
||||
|
||||
stimuli.run_sim()
|
||||
output_value = ch.convert_to_float(ch.parse_output("timing", measure_name))
|
||||
debug.info(3,"Correct: {0} Output: {1} NM: {2}".format(correct_value,output_value,noise_margin))
|
||||
if self.pass_fail_test(output_value,correct_value,noise_margin):
|
||||
debug.info(3,"PASS")
|
||||
if ch.relative_compare(target_time, previous_time):
|
||||
debug.info(3,"CONVERGE " + str(target_time) + " " + str(previous_time))
|
||||
break
|
||||
previous_time = target_time
|
||||
if mode == "HOLD":
|
||||
upper_bound = target_time
|
||||
target_time -= 0.5 * (upper_bound - lower_bound)
|
||||
else:
|
||||
lower_bound = target_time
|
||||
target_time += 0.5 * (upper_bound - lower_bound)
|
||||
else:
|
||||
debug.info(3,"FAIL")
|
||||
if mode == "HOLD":
|
||||
lower_bound = target_time
|
||||
target_time += 0.5 * (upper_bound - lower_bound)
|
||||
else:
|
||||
upper_bound = target_time
|
||||
target_time -= 0.5 * (upper_bound - lower_bound)
|
||||
#raw_input("Press Enter to continue...")
|
||||
# the clock starts offset by one clock period,
|
||||
# so we always measure our setup or hold relative to this time
|
||||
if mode == "HOLD":
|
||||
setuphold_time = target_time - period
|
||||
else:
|
||||
setuphold_time = period - target_time
|
||||
return setuphold_time
|
||||
|
||||
|
||||
def setup_time(self):
|
||||
"""Calculates the setup time for low-to-high and high-to-low
|
||||
transition for a D-flip-flop"""
|
||||
|
||||
one_found = self.bidir_search(vdd, 0.9*vdd, "maxvout", "SETUP")
|
||||
|
||||
zero_found = self.bidir_search(gnd, 0.1*vdd, "minvout", "SETUP")
|
||||
|
||||
return [one_found, zero_found]
|
||||
|
||||
def hold_time(self):
|
||||
"""Calculates the hold time for low-to-high and high-to-low
|
||||
transition for a D-flip-flop"""
|
||||
|
||||
one_found = self.bidir_search(vdd, 0.9*vdd, "maxvout", "HOLD")
|
||||
|
||||
zero_found = self.bidir_search(gnd, 0.1*vdd, "minvout", "HOLD")
|
||||
|
||||
return [one_found, zero_found]
|
||||
|
||||
|
||||
def pass_fail_test(self,value,correct_value,noise_margin):
|
||||
"""Function to Test if the output value reached the
|
||||
noise_margin to determine if it passed or failed"""
|
||||
if correct_value == vdd:
|
||||
return True if value >= noise_margin else False
|
||||
else:
|
||||
return True if value <= noise_margin else False
|
||||
|
||||
|
||||
|
||||
|
||||
def analyze(self):
|
||||
"""main function to calculate both setup and hold time for the
|
||||
d-flip-flop returns a dictionary that contains 4 times for both
|
||||
setup/hold times for high_to_low and low_to_high transition
|
||||
dictionary keys: setup_time_one (low_to_high), setup_time_zero
|
||||
(high_to_low), hold_time_one (low_to_high), hold_time_zero
|
||||
(high_to_low)
|
||||
"""
|
||||
|
||||
[one_setup_time, zero_setup_time] = self.setup_time()
|
||||
[one_hold_time, zero_hold_time] = self.hold_time()
|
||||
debug.info(1, "Setup Time for low_to_high transistion: {0}".format(one_setup_time))
|
||||
debug.info(1, "Setup Time for high_to_low transistion: {0}".format(zero_setup_time))
|
||||
debug.info(1, "Hold Time for low_to_high transistion: {0}".format(one_hold_time))
|
||||
debug.info(1, "Hold Time for high_to_low transistion: {0}".format(zero_hold_time))
|
||||
times = {"setup_time_one": one_setup_time,
|
||||
"setup_time_zero": zero_setup_time,
|
||||
"hold_time_one": one_hold_time,
|
||||
"hold_time_zero": zero_hold_time
|
||||
}
|
||||
return times
|
||||
|
||||
|
|
@ -0,0 +1,467 @@
|
|||
"""
|
||||
This file generates the test structure and stimulus for an sram
|
||||
simulation. There are various functions that can be be used to
|
||||
generate stimulus for other simulations as well.
|
||||
"""
|
||||
|
||||
import globals
|
||||
import tech
|
||||
import debug
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
|
||||
OPTS = globals.get_opts()
|
||||
|
||||
vdd = tech.spice["supply_voltage"]
|
||||
gnd = tech.spice["gnd_voltage"]
|
||||
vdd_name = tech.spice["vdd_name"]
|
||||
gnd_name = tech.spice["gnd_name"]
|
||||
pmos_name = tech.spice["pmos_name"]
|
||||
nmos_name = tech.spice["nmos_name"]
|
||||
tx_width = tech.spice["minwidth_tx"]
|
||||
tx_length = tech.spice["channel"]
|
||||
|
||||
def inst_sram(stim_file, abits, dbits, sram_name):
|
||||
"""function to instatiate the sram subckt"""
|
||||
stim_file.write("Xsram ")
|
||||
for i in range(dbits):
|
||||
stim_file.write("DATA[{0}] ".format(i))
|
||||
for i in range(abits):
|
||||
stim_file.write("A[{0}]_buf ".format(i))
|
||||
for i in tech.spice["control_signals"]:
|
||||
stim_file.write("{0}_buf ".format(i))
|
||||
stim_file.write("{0}_buf_buf ".format(tech.spice["clk"]))
|
||||
stim_file.write("{0} {1} ".format(vdd_name, gnd_name))
|
||||
stim_file.write("{0}\n".format(sram_name))
|
||||
|
||||
|
||||
def inst_model(stim_file, pins, model_name):
|
||||
"""function to instantiate a model"""
|
||||
stim_file.write("X{0} ".format(model_name))
|
||||
for pin in pins:
|
||||
stim_file.write("{0} ".format(pin))
|
||||
stim_file.write("{0}\n".format(model_name))
|
||||
|
||||
|
||||
def create_inverter(stim_file, size=1, beta=2.5):
|
||||
"""Generates inverter for the top level signals (only for sim purposes)"""
|
||||
stim_file.write(".SUBCKT test_inv in out {0} {1}\n".format(vdd_name, gnd_name))
|
||||
stim_file.write("mpinv out in {0} {0} {1} w={2}u l={3}u\n".format(vdd_name,
|
||||
pmos_name,
|
||||
beta * size * tx_width,
|
||||
tx_length))
|
||||
stim_file.write("mninv out in {0} {0} {1} w={2}u l={3}u\n".format(gnd_name,
|
||||
nmos_name,
|
||||
size * tx_width,
|
||||
tx_length))
|
||||
stim_file.write(".ENDS test_inv\n")
|
||||
|
||||
|
||||
def create_buffer(stim_file, buffer_name, size=[1,3], beta=2.5):
|
||||
"""Generates buffer for top level signals (only for sim
|
||||
purposes). Size is pair for PMOS, NMOS width multiple. It includes
|
||||
a beta of 3."""
|
||||
|
||||
stim_file.write(".SUBCKT test_{2} in out {0} {1}\n".format(vdd_name,
|
||||
gnd_name,
|
||||
buffer_name))
|
||||
stim_file.write("mpinv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(vdd_name,
|
||||
pmos_name,
|
||||
beta * size[0] * tx_width,
|
||||
tx_length))
|
||||
stim_file.write("mninv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(gnd_name,
|
||||
nmos_name,
|
||||
size[0] * tx_width,
|
||||
tx_length))
|
||||
stim_file.write("mpinv2 out out_inv {0} {0} {1} w={2}u l={3}u\n".format(vdd_name,
|
||||
pmos_name,
|
||||
beta * size[1] * tx_width,
|
||||
tx_length))
|
||||
stim_file.write("mninv2 out out_inv {0} {0} {1} w={2}u l={3}u\n".format(gnd_name,
|
||||
nmos_name,
|
||||
size[1] * tx_width,
|
||||
tx_length))
|
||||
stim_file.write(".ENDS test_{0}\n".format(buffer_name))
|
||||
|
||||
|
||||
def add_buffer(stim_file, buffer_name, signal_list):
|
||||
"""Adds buffers to each top level signal that is in signal_list (only for sim purposes)"""
|
||||
for signal in signal_list:
|
||||
stim_file.write("X{0}_buffer {0} {0}_buf {1} {2} test_{3}\n".format(signal,
|
||||
"test"+vdd_name,
|
||||
"test"+gnd_name,
|
||||
buffer_name))
|
||||
|
||||
|
||||
def add_inverter(stim_file, signal_list):
|
||||
"""Adds inv for each signal that needs its inverted version (only for sim purposes)"""
|
||||
for signal in signal_list:
|
||||
stim_file.write("X{0}_inv {0} {0}_inv {1} {2} test_inv\n".format(signal,
|
||||
"test"+vdd_name,
|
||||
"test"+gnd_name))
|
||||
|
||||
|
||||
def add_accesstx(stim_file, dbits):
|
||||
"""Adds transmission gate for inputs to data-bus (only for sim purposes)"""
|
||||
stim_file.write("* Tx Pin-list: Drain Gate Source Body\n")
|
||||
for i in range(dbits):
|
||||
pmos_access_string="mp{0} DATA[{0}] WEb_trans_buf D[{0}]_buf {1} {2} w={3}u l={4}u\n"
|
||||
stim_file.write(pmos_access_string.format(i,
|
||||
"test"+vdd_name,
|
||||
pmos_name,
|
||||
2 * tx_width,
|
||||
tx_length))
|
||||
nmos_access_string="mn{0} DATA[{0}] WEb_trans_inv D[{0}]_buf {1} {2} w={3}u l={4}u\n"
|
||||
stim_file.write(nmos_access_string.format(i,
|
||||
"test"+gnd_name,
|
||||
nmos_name,
|
||||
2 * tx_width,
|
||||
tx_length))
|
||||
|
||||
def gen_pulse(stim_file, sig_name, v1=gnd, v2=vdd, offset=0, period=1, t_rise=0, t_fall=0):
|
||||
"""Generates a periodic signal with 50% duty cycle and slew rates. Period is measured
|
||||
from 50% to 50%."""
|
||||
pulse_string="V{0} {0} 0 PULSE ({1} {2} {3}n {4}n {5}n {6}n {7}n)\n"
|
||||
stim_file.write(pulse_string.format(sig_name,
|
||||
v1,
|
||||
v2,
|
||||
offset,
|
||||
t_rise,
|
||||
t_fall,
|
||||
0.5*period-0.5*t_rise-0.5*t_fall,
|
||||
period))
|
||||
|
||||
|
||||
|
||||
def gen_clk_pwl(stim_file, cycle_times, t_fall, t_rise):
|
||||
"""Generates a clk signal using pwl. The cycle times are the times of the
|
||||
clock rising edge. It is assumed to start at time 0 with clock 0. Duty
|
||||
cycle is assumed to be 50%. Rise/fall times are 0-100%."""
|
||||
stim_file.write("V{0} {0} 0 PWL (0n 0v ".format(tech.spice["clk"]))
|
||||
for i in range(len(cycle_times)-1):
|
||||
period = cycle_times[i+1] - cycle_times[i]
|
||||
t_current = cycle_times[i] - 0.5*t_rise # 50% point is at cycle time
|
||||
t_current2 = t_current + t_rise
|
||||
# rising edge
|
||||
stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, gnd, t_current2, vdd))
|
||||
t_current = t_current + 0.5*period - 0.5*t_fall # 50% point is at cycle time
|
||||
t_current2 = t_current + t_fall
|
||||
# falling edge
|
||||
stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, vdd, t_current2, gnd))
|
||||
# end time doesn't need a rising edge
|
||||
stim_file.write("{0}n {1}v)\n".format(cycle_times[-1], gnd))
|
||||
|
||||
|
||||
def gen_data_pwl(stim_file, key_times, sig_name, data_value, feasible_period, target_period, t_rise, t_fall):
|
||||
"""Generates the PWL data inputs for a simulation timing test."""
|
||||
data_value_invert = gnd if data_value == vdd else vdd
|
||||
|
||||
t_current = 0.0
|
||||
stim_file.write("V{0} {0} 0 PWL ({1}n {2}v ".format(sig_name, t_current, data_value_invert))
|
||||
t_current = key_times[2] - 0.25 * target_period
|
||||
t_current += (0.5 * target_period) # uses falling edge for ZBT mode
|
||||
slew_time = t_rise if data_value_invert == gnd else t_fall
|
||||
t_current2 = t_current + slew_time
|
||||
stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, data_value_invert, t_current2, data_value))
|
||||
t_current = key_times[2] + 0.25 * target_period
|
||||
t_current += (0.5 * target_period) # uses falling edge for ZBT mode
|
||||
slew_time = t_rise if data_value == gnd else t_fall
|
||||
t_current2 = t_current + slew_time
|
||||
stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, data_value, t_current2, data_value_invert))
|
||||
t_current = key_times[5] + 0.25 * feasible_period
|
||||
stim_file.write("{0}n {1}v)\n".format(t_current, data_value_invert))
|
||||
|
||||
|
||||
def gen_addr_pwl(stim_file, key_times, addr, feasible_period, target_period, t_rise, t_fall):
|
||||
"""Generates the PWL for address inputs for a simulation timing test"""
|
||||
# reverse string
|
||||
reversed_addr = addr[::-1]
|
||||
# inverts all bits in address using intermediate value of 2
|
||||
invert_addr = reversed_addr.replace('1', '2').replace('0', '1').replace('2', '0')
|
||||
|
||||
for i in range(len(reversed_addr)):
|
||||
v_val = gnd if reversed_addr[i] == '0' else vdd
|
||||
v_val_invert = gnd if invert_addr[i] == '0' else vdd
|
||||
t_current = 0.0
|
||||
stim_file.write("V{0} {0} 0 PWL ({1}n {2}v ".format("A[{0}]".format(i), t_current, v_val))
|
||||
t_current = key_times[3] - 0.25 * target_period
|
||||
slew_time = t_rise if v_val == gnd else t_fall
|
||||
t_current2 = t_current + slew_time
|
||||
stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, v_val, t_current2, v_val_invert))
|
||||
t_current = key_times[4] - 0.25 * target_period
|
||||
slew_time = t_rise if v_val_invert == gnd else t_fall
|
||||
t_current2 = t_current + slew_time
|
||||
stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, v_val_invert, t_current2, v_val))
|
||||
t_current = key_times[5] + 0.25 * feasible_period
|
||||
stim_file.write("{0}n {1}v)\n".format(t_current, v_val))
|
||||
|
||||
|
||||
def gen_constant(stim_file, sig_name, v_ref, v_val):
|
||||
"""Generates a constant signal with reference voltage and the voltage value"""
|
||||
stim_file.write("V{0} {0} {1} DC {2}\n".format(sig_name, v_ref, v_val))
|
||||
|
||||
def gen_csb_pwl(key_times, feasible_period, target_period, t_rise, t_fall):
|
||||
"""Returns two lists for x,y coordinates for the generation of CSb pwl"""
|
||||
t_current = 0.0
|
||||
x_list = [t_current]
|
||||
y_list = [vdd]
|
||||
|
||||
for key_time in key_times[:2]:
|
||||
t_current = key_time - 0.25 * feasible_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(vdd)
|
||||
x_list.append(t_current + t_fall)
|
||||
y_list.append(gnd)
|
||||
|
||||
t_current = key_time + 0.25 * feasible_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(gnd)
|
||||
x_list.append(t_current + t_rise)
|
||||
y_list.append(vdd)
|
||||
|
||||
for key_time in key_times[2:-1]:
|
||||
t_current = key_time - 0.25 * target_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(vdd)
|
||||
x_list.append(t_current + t_fall)
|
||||
y_list.append(gnd)
|
||||
|
||||
t_current = key_time + 0.25 * target_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(gnd)
|
||||
x_list.append(t_current + t_rise)
|
||||
y_list.append(vdd)
|
||||
|
||||
return (x_list, y_list)
|
||||
|
||||
|
||||
def gen_web_pwl(key_times, feasible_period, target_period, t_rise, t_fall):
|
||||
"""Returns two lists for x,y coordinates for the generation of WEb pwl"""
|
||||
|
||||
t_current = 0.0
|
||||
x_list = [t_current]
|
||||
y_list = [vdd]
|
||||
|
||||
t_current = key_times[0] - 0.25 * feasible_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(vdd)
|
||||
x_list.append(t_current + t_fall)
|
||||
y_list.append(gnd)
|
||||
|
||||
t_current = key_times[0] + 0.25 * feasible_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(gnd)
|
||||
x_list.append(t_current + t_rise)
|
||||
y_list.append(vdd)
|
||||
|
||||
t_current = key_times[2] - 0.25 * target_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(vdd)
|
||||
x_list.append(t_current + t_fall)
|
||||
y_list.append(gnd)
|
||||
|
||||
t_current = key_times[2] + 0.25 * target_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(gnd)
|
||||
x_list.append(t_current + t_rise)
|
||||
y_list.append(vdd)
|
||||
|
||||
t_current = key_times[3] - 0.25 * target_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(vdd)
|
||||
x_list.append(t_current + t_fall)
|
||||
y_list.append(gnd)
|
||||
|
||||
t_current = key_times[3] + 0.25 * target_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(gnd)
|
||||
x_list.append(t_current + t_rise)
|
||||
y_list.append(vdd)
|
||||
|
||||
return (x_list, y_list)
|
||||
|
||||
|
||||
def gen_oeb_pwl(key_times, feasible_period, target_period, t_rise, t_fall):
|
||||
"""Returns two lists for x,y coordinates for the generation of OEb pwl"""
|
||||
|
||||
t_current = 0.0
|
||||
x_list = [t_current]
|
||||
y_list = [vdd]
|
||||
|
||||
t_current = key_times[1] - 0.25 * feasible_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(vdd)
|
||||
x_list.append(t_current + t_fall)
|
||||
y_list.append(gnd)
|
||||
|
||||
t_current = key_times[1] + 0.25 * feasible_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(gnd)
|
||||
x_list.append(t_current + t_rise)
|
||||
y_list.append(vdd)
|
||||
|
||||
t_current = key_times[4] - 0.25 * target_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(vdd)
|
||||
x_list.append(t_current + t_fall)
|
||||
y_list.append(gnd)
|
||||
|
||||
t_current = key_times[4] + 0.25 * target_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(gnd)
|
||||
x_list.append(t_current + t_rise)
|
||||
y_list.append(vdd)
|
||||
|
||||
return (x_list, y_list)
|
||||
|
||||
|
||||
def gen_web_trans_pwl(key_times, feasible_period, target_period, t_rise, t_fall):
|
||||
"""Returns two lists for x,y coordinates for the generation of WEb_transmission_gate pwl"""
|
||||
|
||||
t_current = 0.0
|
||||
x_list = [t_current]
|
||||
y_list = [vdd]
|
||||
|
||||
t_current = key_times[0] + 0.5 * feasible_period
|
||||
t_current -= 0.25 * feasible_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(vdd)
|
||||
x_list.append(t_current + t_fall)
|
||||
y_list.append(gnd)
|
||||
|
||||
t_current += 0.5 * feasible_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(gnd)
|
||||
x_list.append(t_current + t_rise)
|
||||
y_list.append(vdd)
|
||||
|
||||
t_current = key_times[2] + 0.5 * target_period
|
||||
t_current -= 0.25 * target_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(vdd)
|
||||
x_list.append(t_current + t_fall)
|
||||
y_list.append(gnd)
|
||||
|
||||
t_current += 0.5 * target_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(gnd)
|
||||
x_list.append(t_current + t_rise)
|
||||
y_list.append(vdd)
|
||||
|
||||
t_current = key_times[3] + 0.5 * target_period
|
||||
t_current -= 0.25 * target_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(vdd)
|
||||
x_list.append(t_current + t_fall)
|
||||
y_list.append(gnd)
|
||||
|
||||
t_current += 0.5 * target_period
|
||||
x_list.append(t_current)
|
||||
y_list.append(gnd)
|
||||
x_list.append(t_current + t_rise)
|
||||
y_list.append(vdd)
|
||||
|
||||
return (x_list, y_list)
|
||||
|
||||
|
||||
def get_inverse_value(value):
|
||||
if value > 0.5*vdd:
|
||||
return gnd
|
||||
elif value <= 0.5*vdd:
|
||||
return vdd
|
||||
else:
|
||||
debug.error("Invalid value to get an inverse of: {0}".format(value))
|
||||
|
||||
|
||||
def gen_pwl(stim_file, sig_name, x_list, y_list):
|
||||
"""Generates an arbitrary pwl for a signal where xlist is times in
|
||||
ns and ylist is voltage. """
|
||||
|
||||
t_current = 0.0
|
||||
stim_file.write("V{0} {0} 0 PWL (".format(sig_name))
|
||||
for p in zip(x_list,y_list):
|
||||
stim_file.write("{0}n {1}v ".format(p[0],p[1]))
|
||||
stim_file.write(")\n")
|
||||
|
||||
def gen_trap_pwl(stim_file, sig_name, x_list, y_list, t_rise, t_fall):
|
||||
"""Generates a trapezoidal pwl for a signal where xlist is times in ns and ylist is voltage.
|
||||
Transitions are assumed to ignore slew and the slew rates are generated automatically
|
||||
using the provided 0-100% slew times and centering times at the 50% point.."""
|
||||
|
||||
stim_file.write("V{0} {0} 0 PWL (".format(sig_name))
|
||||
for p in zip(x_list,y_list):
|
||||
slew = t_rise if p[1]>0.5*vdd else t_fall
|
||||
start = max(p[0]-0.5*slew,0)
|
||||
end = p[0]+0.5*slew
|
||||
stim_file.write("{0}n {1}v ".format(start, get_inverse_value(p[1])))
|
||||
stim_file.write("{0}n {1}v ".format(end, p[1]))
|
||||
stim_file.write(")\n")
|
||||
|
||||
|
||||
|
||||
def gen_meas_delay(stim_file, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, td):
|
||||
"""Creates the .meas statement for the measurement of delay"""
|
||||
measure_string=".meas tran {0} TRIG v({1}) VAL={2} RISE={3} TARG v({4}) VAL={5} TD={7}n {6}=1\n"
|
||||
stim_file.write(measure_string.format(meas_name,
|
||||
trig_name,
|
||||
trig_val,
|
||||
trig_dir,
|
||||
targ_name,
|
||||
targ_val,
|
||||
targ_dir,
|
||||
td))
|
||||
|
||||
def gen_meas_power(stim_file, meas_name, t_initial, t_final):
|
||||
"""Creates the .meas statement for the measurement of avg power"""
|
||||
# power mea cmd is different in different spice:
|
||||
if OPTS.spice_version == "hspice":
|
||||
power_exp = "power"
|
||||
else:
|
||||
power_exp = "par('(-1*v(" + str(vdd_name) + ")*I(v" + str(vdd_name) + "))')"
|
||||
stim_file.write(".meas tran {0} avg {1} from={2}n to={3}n\n".format(meas_name,
|
||||
power_exp,
|
||||
t_initial,
|
||||
t_final))
|
||||
|
||||
|
||||
def write_include(stim_file, models):
|
||||
"""Writes include statements, inputs are lists of model files"""
|
||||
for item in list(models):
|
||||
stim_file.write(".include \"{0}\"\n".format(item))
|
||||
|
||||
|
||||
def write_supply(stim_file, vdd_name, gnd_name, vdd_voltage, gnd_voltage):
|
||||
"""Writes supply voltage statements"""
|
||||
stim_file.write("V{0} {0} 0.0 {1}\n".format(vdd_name, vdd_voltage))
|
||||
stim_file.write("V{0} {0} 0.0 {1}\n".format(gnd_name, gnd_voltage))
|
||||
# This is for the test power supply
|
||||
stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+vdd_name, vdd_voltage))
|
||||
stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+gnd_name, gnd_voltage))
|
||||
|
||||
|
||||
|
||||
|
||||
def run_sim():
|
||||
"""Run hspice in batch mode and output rawfile to parse."""
|
||||
temp_stim = "{0}stim.sp".format(OPTS.openram_temp)
|
||||
if OPTS.spice_version == "hspice":
|
||||
# TODO: Should make multithreading parameter a configuration option
|
||||
cmd_args = "-mt 8 -i {1} -o {2}timing 2>&1 /dev/null".format(OPTS.spice_exe,
|
||||
temp_stim,
|
||||
OPTS.openram_temp)
|
||||
else:
|
||||
cmd_args = "-b -i {1} -o {2}timing.lis 2>&1 /dev/null".format(OPTS.spice_exe,
|
||||
temp_stim,
|
||||
OPTS.openram_temp)
|
||||
|
||||
FNULL = open(os.devnull, 'w')
|
||||
debug.info(2, OPTS.spice_exe + " " + cmd_args)
|
||||
retcode = subprocess.call([OPTS.spice_exe, cmd_args], stdout=FNULL, stderr=FNULL)
|
||||
FNULL.close()
|
||||
|
||||
if (retcode > 0):
|
||||
debug.error("Spice simulation error: " + OPTS.spice_exe + " " + cmd_args)
|
||||
sys.exit(-1)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
import design
|
||||
import debug
|
||||
from tech import drc
|
||||
from vector import vector
|
||||
|
||||
class contact(design.design):
|
||||
"""
|
||||
Object for a contact shape with its conductor enclosures
|
||||
Creates a contact array minimum active or poly enclosure and metal1 enclosure.
|
||||
This class has enclosure on multiple sides of the contact whereas a via may
|
||||
have extension on two or four sides.
|
||||
"""
|
||||
unique_contact_id = 1
|
||||
|
||||
def __init__(self, layer_stack, dimensions=[1,1], offset=(0,0)):
|
||||
name = "{0}_{1}x{2}_num{3}".format(layer_stack[1],
|
||||
dimensions[0],
|
||||
dimensions[1],
|
||||
contact.unique_contact_id)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "create contact object {0}".format(name))
|
||||
contact.unique_contact_id += 1
|
||||
|
||||
self.layer_stack = layer_stack
|
||||
self.dimensions = dimensions
|
||||
self.offset = offset
|
||||
self.pins = [] # used for matching parm lengths
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
self.setup_layers()
|
||||
self.setup_layout_constants()
|
||||
self.create_contact_array()
|
||||
self.create_first_layer_enclosure()
|
||||
self.create_second_layer_enclosure()
|
||||
self.offset_all_coordinates()
|
||||
|
||||
def offset_all_coordinates(self):
|
||||
coordinate = self.find_lowest_coords()
|
||||
self.offset_attributes(coordinate)
|
||||
self.translate(coordinate)
|
||||
|
||||
self.height = max(obj.offset[1] + obj.height for obj in self.objs)
|
||||
self.width = max(obj.offset[0] + obj.width for obj in self.objs)
|
||||
|
||||
def setup_layers(self):
|
||||
(first_layer, via_layer, second_layer) = self.layer_stack
|
||||
self.first_layer_name = first_layer
|
||||
self.via_layer_name = via_layer
|
||||
self.second_layer_name = second_layer
|
||||
|
||||
def setup_layout_constants(self):
|
||||
self.contact_width = drc["minwidth_{0}". format(self.via_layer_name)]
|
||||
self.contact_to_contact = drc["{0}_to_{0}".format(self.via_layer_name)]
|
||||
self.contact_pitch = self.contact_width + self.contact_to_contact
|
||||
self.contact_array_width = self.contact_width \
|
||||
+ (self.dimensions[0] - 1) * self.contact_pitch
|
||||
self.contact_array_height = self.contact_width \
|
||||
+ (self.dimensions[1] - 1) * self.contact_pitch
|
||||
|
||||
# FIME break this up
|
||||
self.first_layer_horizontal_enclosure = max((drc["minwidth_{0}".format(self.first_layer_name)] - self.contact_array_width) / 2,
|
||||
drc["{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)])
|
||||
self.first_layer_vertical_enclosure = max((drc["minarea_{0}".format(self.first_layer_name)]
|
||||
/ (self.contact_array_width + 2 * self.first_layer_horizontal_enclosure) - self.contact_array_height) / 2,
|
||||
(drc["minheight_{0}".format(
|
||||
self.first_layer_name)] - self.contact_array_height) / 2,
|
||||
drc["{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)])
|
||||
|
||||
self.second_layer_horizontal_enclosure = max((drc["minwidth_{0}".format(self.second_layer_name)] - self.contact_array_width) / 2,
|
||||
drc["{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)])
|
||||
self.second_layer_vertical_enclosure = max((drc["minarea_{0}".format(self.second_layer_name)]
|
||||
/ (self.contact_array_width + 2 * self.second_layer_horizontal_enclosure) - self.contact_array_height) / 2,
|
||||
(drc["minheight_{0}".format(
|
||||
self.second_layer_name)] - self.contact_array_height) / 2,
|
||||
drc["{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)])
|
||||
|
||||
def create_contact_array(self):
|
||||
""" Create the contact array at the origin"""
|
||||
self.via_layer_position = vector(0, 0)
|
||||
for i in range(self.dimensions[1]):
|
||||
offset = [0, 0 + self.contact_pitch * i]
|
||||
for j in range(self.dimensions[0]):
|
||||
self.add_rect(layer=self.via_layer_name,
|
||||
offset=offset,
|
||||
width=self.contact_width,
|
||||
height=self.contact_width)
|
||||
offset = [offset[0] + self.contact_pitch, offset[1]]
|
||||
|
||||
def create_first_layer_enclosure(self):
|
||||
width = self.first_layer_width = self.contact_array_width \
|
||||
+ 2 * self.first_layer_horizontal_enclosure
|
||||
height = self.first_layer_height = self.contact_array_height \
|
||||
+ 2 * self.first_layer_vertical_enclosure
|
||||
offset = self.first_layer_position = vector(-self.first_layer_horizontal_enclosure,
|
||||
-self.first_layer_vertical_enclosure)
|
||||
self.add_rect(layer=self.first_layer_name,
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=height)
|
||||
|
||||
def create_second_layer_enclosure(self):
|
||||
width = self.second_layer_width = self.contact_array_width \
|
||||
+ 2 * self.second_layer_horizontal_enclosure
|
||||
height = self.second_layer_height = self.contact_array_height \
|
||||
+ 2 * self.second_layer_vertical_enclosure
|
||||
offset = self.second_layer_position = vector(-self.second_layer_horizontal_enclosure,
|
||||
-self.second_layer_vertical_enclosure)
|
||||
self.add_rect(layer=self.second_layer_name,
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=height)
|
||||
|
|
@ -0,0 +1,696 @@
|
|||
from math import log
|
||||
import design
|
||||
from tech import drc, parameter, cell
|
||||
import debug
|
||||
from ms_flop_array import ms_flop_array
|
||||
from wordline_driver import wordline_driver
|
||||
from contact import contact
|
||||
from pinv import pinv
|
||||
from nand_2 import nand_2
|
||||
from nand_3 import nand_3
|
||||
from nor_2 import nor_2
|
||||
from replica_bitline import replica_bitline
|
||||
import math
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
|
||||
class control_logic(design.design):
|
||||
"""
|
||||
Dynamically generated Control logic for the total SRAM circuit.
|
||||
"""
|
||||
|
||||
def __init__(self, num_rows):
|
||||
""" Constructor """
|
||||
design.design.__init__(self, "control_logic")
|
||||
debug.info(1, "Creating %s" % self.name)
|
||||
|
||||
self.num_rows = num_rows
|
||||
self.create_layout()
|
||||
self.DRC_LVS()
|
||||
|
||||
def create_layout(self):
|
||||
""" Create layout and route between modules """
|
||||
self.create_modules()
|
||||
self.setup_layout_offsets()
|
||||
self.add_modules()
|
||||
self.add_routing()
|
||||
self.add_pin_labels()
|
||||
|
||||
def create_modules(self):
|
||||
""" add all the required modules """
|
||||
c = reload(__import__(OPTS.config.ms_flop))
|
||||
self.mod_ms_flop = getattr(c, OPTS.config.ms_flop)
|
||||
self.ms_flop = self.mod_ms_flop("ms_flop")
|
||||
self.add_mod(self.ms_flop)
|
||||
self.inv = pinv(name="pinv",
|
||||
nmos_width=drc["minwidth_tx"],
|
||||
beta=parameter["pinv_beta"])
|
||||
|
||||
self.add_mod(self.inv)
|
||||
self.nand2 = nand_2(name="nand2",
|
||||
nmos_width=2 * drc["minwidth_tx"])
|
||||
self.add_mod(self.nand2)
|
||||
self.NAND3 = nand_3(name="NAND3",
|
||||
nmos_width=3 * drc["minwidth_tx"])
|
||||
self.add_mod(self.NAND3)
|
||||
|
||||
# Special gates: 4x Inverter
|
||||
self.inv4 = pinv(name="pinv4",
|
||||
nmos_width=4 * drc["minwidth_tx"],
|
||||
beta=parameter["pinv_beta"])
|
||||
self.add_mod(self.inv4)
|
||||
|
||||
self.nor2 = nor_2(name="nor2",
|
||||
nmos_width=drc["minwidth_tx"])
|
||||
self.add_mod(self.nor2)
|
||||
|
||||
self.msf_control = ms_flop_array(name="msf_control",
|
||||
array_type="data_in",
|
||||
columns=3,
|
||||
word_size=3)
|
||||
self.add_mod(self.msf_control)
|
||||
|
||||
self.replica_bitline = replica_bitline("replica_bitline",
|
||||
int(math.ceil(self.num_rows / 10.0)))
|
||||
self.add_mod(self.replica_bitline)
|
||||
|
||||
def add_pin_labels(self):
|
||||
""" Add pins and labels after everything is done """
|
||||
input_lst =["CSb","WEb","OEb"]
|
||||
output_lst = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar"]
|
||||
clk =["clk"]
|
||||
rails = ["vdd", "gnd"]
|
||||
pin_lst = input_lst + output_lst + clk + rails
|
||||
for pin in pin_lst:
|
||||
self.add_pin(pin)
|
||||
|
||||
# add label of input, output and clk in metal3 layer
|
||||
input_lst =["CSb","WEb","OEb"]
|
||||
output_lst = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar"]
|
||||
for pin in input_lst + output_lst + ["clk"]:
|
||||
self.add_label(text=pin,
|
||||
layer="metal3",
|
||||
offset=getattr(self, pin+"_position"))
|
||||
# add label of vdd and gnd manually cause non-uniformed names and layers
|
||||
self.add_label(text="vdd",
|
||||
layer="metal1",
|
||||
offset=self.vdd1_position)
|
||||
self.add_label(text="vdd",
|
||||
layer="metal2",
|
||||
offset=self.vdd2_position)
|
||||
self.add_label(text="gnd",
|
||||
layer="metal2",
|
||||
offset=self.gnd_position)
|
||||
|
||||
def setup_layout_offsets(self):
|
||||
""" Setup layout offsets, determine the size of the busses etc """
|
||||
# This isn't for instantiating, but we use it to get the dimensions
|
||||
m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
|
||||
|
||||
# Vertical metal rail gap definition
|
||||
self.metal2_extend_contact = (m1m2_via.second_layer_height - m1m2_via.contact_width) / 2
|
||||
self.gap_between_rails = self.metal2_extend_contact + drc["metal2_to_metal2"]
|
||||
self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"]
|
||||
self.via_shift = (m1m2_via.second_layer_width - m1m2_via.first_layer_width) / 2
|
||||
|
||||
# used to shift contact when connecting to NAND3 C pin down
|
||||
self.contact_shift = (m1m2_via.first_layer_width - m1m2_via.contact_width) / 2
|
||||
|
||||
# Common parameters for rails
|
||||
self.rail_width = drc["minwidth_metal2"]
|
||||
self.rail_gap = 2 * drc["metal2_to_metal2"]
|
||||
self.rail_offset_gap = self.rail_width + self.rail_gap
|
||||
|
||||
# First RAIL Parameters
|
||||
self.num_rails_1 = 6
|
||||
self.overall_rail_1_gap = (self.num_rails_1 + 1) * self.rail_offset_gap
|
||||
self.rail_1_x_offsets = []
|
||||
|
||||
# Second RAIL Parameters
|
||||
self.num_rails_2 = 4
|
||||
self.overall_rail_2_gap = (self.num_rails_2 + 1) * self.rail_offset_gap
|
||||
self.rail_2_x_offsets = []
|
||||
|
||||
# GAP between main control and REPLICA BITLINE
|
||||
self.replica_bitline_gap = self.rail_offset_gap * 2
|
||||
|
||||
self.output_port_gap = 3 * drc["minwidth_metal3"]
|
||||
self.logic_height = max(self.replica_bitline.width, 4 * self.inv.height)
|
||||
|
||||
def add_modules(self):
|
||||
""" Place all the modules """
|
||||
self.add_msf_control()
|
||||
self.set_msf_control_pins()
|
||||
self.add_1st_row(self.output_port_gap)
|
||||
self.add_2nd_row(self.output_port_gap + 2 * self.inv.height)
|
||||
|
||||
# Height and width
|
||||
self.height = self.logic_height + self.output_port_gap
|
||||
self.width = self.offset_replica_bitline[0] + self.replica_bitline.height
|
||||
|
||||
def add_routing(self):
|
||||
""" Routing between modules """
|
||||
self.add_msf_control_routing()
|
||||
self.add_1st_row_routing()
|
||||
self.add_2nd_row_routing()
|
||||
self.add_vdd_routing()
|
||||
self.add_gnd_routing()
|
||||
self.add_input_routing()
|
||||
self.add_output_routing()
|
||||
|
||||
def add_msf_control(self):
|
||||
""" ADD ARRAY OF MS_FLOP"""
|
||||
self.offset_msf_control = vector(0, self.logic_height + self.output_port_gap)
|
||||
self.add_inst(name="msf_control",
|
||||
mod=self.msf_control,
|
||||
offset=self.offset_msf_control,
|
||||
mirror="R270")
|
||||
# don't change this order. This pins are meant for internal connection of msf array inside the control logic.
|
||||
# These pins are connecting the msf_array inside of control_logic.
|
||||
temp = ["CSb", "WEb", "OEb", "CS_bar", "CS", "WE_bar",
|
||||
"WE", "OE_bar", "OE", "clk", "vdd", "gnd"]
|
||||
self.connect_inst(temp)
|
||||
|
||||
def set_msf_control_pins(self):
|
||||
# msf_control inputs
|
||||
correct = vector(0, 0.5 * drc["minwidth_metal2"])
|
||||
def translate_inputs(pt1,pt2):
|
||||
return pt1 + pt2.rotate().scale(1,-1) - correct
|
||||
|
||||
# msf_control outputs
|
||||
def translate_outputs(pt1,pt2):
|
||||
return pt1 - correct + vector(self.msf_control.height,- pt2[0])
|
||||
|
||||
# set CSS WE OE signal groups(in, out, bar)
|
||||
pt1 = self.offset_msf_control
|
||||
pin_set = ["CSb","WEb","OEb"]
|
||||
pt2in = self.msf_control.din_positions[0:len(pin_set)]
|
||||
pt2out = self.msf_control.dout_positions[0:len(pin_set)]
|
||||
pt2bar = self.msf_control.dout_bar_positions[0:len(pin_set)]
|
||||
for i in range(len(pin_set)):
|
||||
value = translate_inputs(pt1,pt2in[i])
|
||||
setattr(self,"msf_control_"+pin_set[i]+"_position",value)
|
||||
value = translate_outputs(pt1,pt2out[i])
|
||||
setattr(self,"msf_control_"+pin_set[i][0:2]+"_bar_position",value)
|
||||
value = translate_outputs(pt1,pt2bar[i])
|
||||
setattr(self,"msf_control_"+pin_set[i][0:2]+"_position",value)
|
||||
|
||||
# clk , vdd
|
||||
base = self.offset_msf_control - vector(0.5 * drc["minwidth_metal2"], 0)
|
||||
msf_clk = self.msf_control.clk_positions[0].rotate().scale(1,-1)
|
||||
self.msf_control_clk_position = base + msf_clk
|
||||
msf_vdd = self.msf_control.vdd_positions[0].rotate().scale(1,-1)
|
||||
self.msf_control_vdd_position = base + msf_vdd
|
||||
|
||||
# gnd
|
||||
self.msf_control_gnd_positions = []
|
||||
for gnd_offset in self.msf_control.gnd_positions:
|
||||
offset = self.offset_msf_control + vector(self.msf_control.height,
|
||||
- gnd_offset[0])
|
||||
self.msf_control_gnd_positions.append(offset - correct)
|
||||
|
||||
def add_1st_row(self,y_off):
|
||||
# inv1 with clk as gate input.
|
||||
msf_control_rotate_x = self.offset_msf_control[0] + self.msf_control.height
|
||||
self.offset_inv1 = vector(msf_control_rotate_x - self.inv4.width, y_off)
|
||||
self.add_inst(name="clk_inverter",
|
||||
mod=self.inv4,
|
||||
offset=self.offset_inv1)
|
||||
self.connect_inst(["clk", "clk_bar", "vdd", "gnd"])
|
||||
# set pin offset as attr
|
||||
self.inv1_A_position = self.offset_inv1 + self.inv4.A_position.scale(0,1)
|
||||
base = self.offset_inv1 + vector(self.inv4.width, 0)
|
||||
for pin in ["Z_position", "vdd_position", "gnd_position"]:
|
||||
setattr(self, "inv1_"+pin, base + getattr(self.inv4, pin).scale(0,1))
|
||||
|
||||
# nor2
|
||||
self.offset_nor2 = vector(self.nor2.width + 2 * drc["minwidth_metal3"],
|
||||
y_off)
|
||||
self.add_inst(name="nor2",
|
||||
mod=self.nor2,
|
||||
offset=self.offset_nor2,
|
||||
mirror="MY")
|
||||
self.connect_inst(["clk", "OE_bar", "tri_en", "vdd", "gnd"])
|
||||
self.set_nand2_nor2_pin("nor2",[-1,1])
|
||||
|
||||
x_off = msf_control_rotate_x + self.overall_rail_1_gap
|
||||
self.nand_array_position = vector(x_off, y_off)
|
||||
|
||||
# nand2_1 input: OE, clk_bar output: tri_en_bar
|
||||
self.offset_nand2 = self.nand_array_position
|
||||
self.add_inst(name="nand2_tri_en",
|
||||
mod=self.nand2,
|
||||
offset=self.offset_nand2)
|
||||
self.connect_inst(["OE", "clk_bar", "tri_en_bar", "vdd", "gnd"])
|
||||
# set pin offset as attr
|
||||
self.set_nand2_nor2_pin("nand2",[1,1])
|
||||
|
||||
# REPLICA BITLINE
|
||||
base_x = self.nand_array_position[0] + self.NAND3.width + 3 * self.inv.width
|
||||
total_rail_gap = self.rail_offset_gap + self.overall_rail_2_gap
|
||||
x_off = base_x + total_rail_gap + self.replica_bitline_gap
|
||||
self.offset_replica_bitline = vector(x_off, y_off)
|
||||
self.add_inst(name="replica_bitline",
|
||||
mod=self.replica_bitline,
|
||||
offset=self.offset_replica_bitline,
|
||||
mirror="MX",
|
||||
rotate=90)
|
||||
self.connect_inst(["rblk", "pre_s_en", "vdd", "gnd"])
|
||||
|
||||
# BUFFER INVERTERS FOR S_EN
|
||||
# inv_4 input: input: pre_s_en_bar, output: s_en
|
||||
self.offset_inv4 = vector(base_x - 2 * self.inv.width, y_off)
|
||||
self.add_inst(name="inv_s_en1",
|
||||
mod=self.inv,
|
||||
offset=self.offset_inv4,
|
||||
mirror="MY")
|
||||
self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"])
|
||||
self.set_inv2345_pins(inv_name="inv4", inv_scale=[-1, 1])
|
||||
|
||||
# inv_5 input: pre_s_en, output: pre_s_en_bar
|
||||
self.offset_inv5 = vector(base_x - self.inv.width, y_off)
|
||||
self.add_inst(name="inv_s_en2",
|
||||
mod=self.inv,
|
||||
offset=self.offset_inv5,
|
||||
mirror="MY")
|
||||
self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"])
|
||||
self.set_inv2345_pins(inv_name="inv5", inv_scale=[-1, 1])
|
||||
|
||||
# set pin offset as attr
|
||||
pin_offset = self.replica_bitline.en_input_offset.rotate()
|
||||
self.replica_en_offset = self.offset_replica_bitline + pin_offset
|
||||
pin_offset = self.replica_bitline.out_offset.rotate()
|
||||
self.replica_out_offset = self.offset_replica_bitline + pin_offset
|
||||
|
||||
def add_2nd_row(self, y_off):
|
||||
# Nand3_1 input: OE, clk_bar,CS output: rblk_bar
|
||||
self.offset_nand3_1 = vector(self.nand_array_position[0], y_off)
|
||||
self.add_inst(name="NAND3_rblk_bar",
|
||||
mod=self.NAND3,
|
||||
offset=self.offset_nand3_1,
|
||||
mirror="MX")
|
||||
self.connect_inst(["clk_bar", "OE", "CS", "rblk_bar", "vdd", "gnd"])
|
||||
# set pin offset as attr
|
||||
self.set_Nand3_pins(nand_name = "nand3_1",nand_scale = [0,-1])
|
||||
|
||||
# Nand3_2 input: WE, clk_bar,CS output: w_en_bar
|
||||
self.offset_nand3_2 = vector(self.nand_array_position[0], y_off)
|
||||
self.add_inst(name="NAND3_w_en_bar",
|
||||
mod=self.NAND3,
|
||||
offset=self.offset_nand3_2,
|
||||
mirror="RO")
|
||||
self.connect_inst(["clk_bar", "WE", "CS", "w_en_bar", "vdd", "gnd"])
|
||||
# set pin offset as attr
|
||||
self.set_Nand3_pins(nand_name = "nand3_2",nand_scale = [0,1])
|
||||
|
||||
# connect nand2 and nand3 to inv
|
||||
nand3_to_inv_connection_height = self.NAND3.Z_position[1] - self.inv.A_position[1] + drc["minwidth_metal1"]
|
||||
self.add_rect(layer="metal1",
|
||||
offset=self.nand3_1_Z_position,
|
||||
width=drc["minwidth_metal1"],
|
||||
height=nand3_to_inv_connection_height)
|
||||
self.add_rect(layer="metal1",
|
||||
offset=self.nand3_2_Z_position + vector(0,drc["minwidth_metal1"]),
|
||||
width=drc["minwidth_metal1"],
|
||||
height=-nand3_to_inv_connection_height)
|
||||
|
||||
# inv_2 input: rblk_bar, output: rblk
|
||||
x_off = self.nand_array_position[0] + self.NAND3.width
|
||||
self.offset_inv2 = vector(x_off, y_off)
|
||||
self.add_inst(name="inv_rblk",
|
||||
mod=self.inv,
|
||||
offset=self.offset_inv2,
|
||||
mirror="MX")
|
||||
self.connect_inst(["rblk_bar", "rblk", "vdd", "gnd"])
|
||||
# set pin offset as attr
|
||||
self.set_inv2345_pins(inv_name="inv2", inv_scale=[1,-1])
|
||||
|
||||
# inv_3 input: w_en_bar, output: pre_w_en
|
||||
self.offset_inv3 = self.offset_inv2
|
||||
self.add_inst(name="inv_w_en",
|
||||
mod=self.inv,
|
||||
offset=self.offset_inv3,
|
||||
mirror="RO")
|
||||
self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"])
|
||||
# set pin offset as attr
|
||||
self.set_inv2345_pins(inv_name="inv3", inv_scale=[1, 1])
|
||||
|
||||
# BUFFER INVERTERS FOR W_EN
|
||||
x_off = self.nand_array_position[0] + self.NAND3.width + self.inv.width
|
||||
self.offset_inv6 = vector(x_off, y_off)
|
||||
self.add_inst(name="inv_w_en1",
|
||||
mod=self.inv,
|
||||
offset=self.offset_inv6,
|
||||
mirror="RO")
|
||||
self.connect_inst(["pre_w_en", "pre_w_en1", "vdd", "gnd"])
|
||||
|
||||
x_off = self.nand_array_position[0] + self.NAND3.width + 2 * self.inv.width
|
||||
self.offset_inv7 = [x_off, y_off]
|
||||
self.add_inst(name="inv_w_en2",
|
||||
mod=self.inv,
|
||||
offset=self.offset_inv7,
|
||||
mirror="RO")
|
||||
self.connect_inst(["pre_w_en1", "w_en", "vdd", "gnd"])
|
||||
# set pin offset as attr
|
||||
self.inv7_Z_position = self.offset_inv7 + vector(self.inv.width,
|
||||
self.inv.Z_position[1])
|
||||
|
||||
def set_nand2_nor2_pin(self,mod,scale):
|
||||
offset = getattr (self, "offset_"+mod)
|
||||
for pin in ["A","B"]:
|
||||
pin_xy = getattr(getattr(self, mod), pin+"_position")
|
||||
setattr(self, mod+"_1_"+pin+"_position", offset + pin_xy.scale(0,1))
|
||||
base = offset + vector(getattr(self, mod).width,0).scale(scale[0],0)
|
||||
for pin in ["Z","vdd","gnd"]:
|
||||
pin_xy = getattr(getattr(self, mod), pin+"_position")
|
||||
setattr(self, mod+"_1_"+pin+"_position", base + pin_xy.scale(0,1))
|
||||
|
||||
def set_Nand3_pins(self,nand_name,nand_scale):
|
||||
base = getattr(self, "offset_"+nand_name)
|
||||
extra = vector(0, drc["minwidth_metal1"]* (1 - nand_scale[1]) *0.5)
|
||||
off1 = base - extra
|
||||
off2 = base - extra + vector(self.NAND3.width, 0)
|
||||
self.set_Nand3_pins_sub(nand_name,["A","B","C"],off1,[0,nand_scale[1]])
|
||||
self.set_Nand3_pins_sub(nand_name,["Z","vdd","gnd"],off2,[0,nand_scale[1]])
|
||||
|
||||
def set_Nand3_pins_sub(self,nand_name,pin_lst,base,nand_scale):
|
||||
for pin in pin_lst:
|
||||
pin_xy = getattr(self.NAND3, pin+"_position").scale(0,nand_scale[1])
|
||||
setattr(self, nand_name+"_"+pin+"_position", base + pin_xy)
|
||||
|
||||
def set_inv2345_pins(self,inv_name,inv_scale):
|
||||
base_xy = getattr(self, "offset_"+inv_name)
|
||||
correct= vector(0, (1-inv_scale[1]) * 0.5 * drc["minwidth_metal1"])
|
||||
# pin A
|
||||
pin_xy = vector(0, self.inv4.A_position.y).scale(0,inv_scale[1])
|
||||
setattr(self, inv_name+"_A_position", base_xy + pin_xy - correct)
|
||||
# Z, vdd, gnd
|
||||
for pin in ["Z_position", "vdd_position", "gnd_position"]:
|
||||
pin_xy = getattr(self.inv, pin).scale(0,inv_scale[1])
|
||||
rotated_pin_xy = vector(self.inv.width * inv_scale[0], 0) + pin_xy
|
||||
setattr(self, inv_name+"_"+pin, base_xy + rotated_pin_xy - correct)
|
||||
|
||||
def add_msf_control_routing(self):
|
||||
# FIRST RAIL : MSF_CONTROL OUTPUT RAIL
|
||||
rail1_start = vector(self.msf_control_WE_position[0],
|
||||
self.output_port_gap)
|
||||
for i in range(self.num_rails_1):
|
||||
correct = vector((i+1) * self.rail_offset_gap, 0)
|
||||
offset = rail1_start + correct
|
||||
self.add_rect(layer="metal2",
|
||||
offset=offset,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.logic_height)
|
||||
self.rail_1_x_offsets.append(offset[0])
|
||||
|
||||
rail2_start_x = (self.nand_array_position[0] + self.NAND3.width
|
||||
+ 3 * self.inv.width + self.rail_offset_gap)
|
||||
for i in range(self.num_rails_2):
|
||||
offset = [rail2_start_x + i * self.rail_offset_gap,
|
||||
self.output_port_gap]
|
||||
self.add_rect(layer="metal2",
|
||||
offset=offset,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.logic_height)
|
||||
self.rail_2_x_offsets.append(offset[0])
|
||||
|
||||
def add_1st_row_routing(self):
|
||||
# First rail routing left
|
||||
left_side = []
|
||||
for pin in ["OE_bar","OE","CS","WE"]:
|
||||
left_side.append(getattr(self,"msf_control_"+pin+"_position"))
|
||||
line_indices = [1, 2, 3, 4]
|
||||
for i in range(len(left_side)):
|
||||
offset = left_side[i]
|
||||
line_x_offset = self.rail_1_x_offsets[line_indices[i]]
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=line_x_offset - offset[0] + drc["minwidth_metal2"],
|
||||
height=drc["minwidth_metal1"])
|
||||
correct1 = vector(self.gap_between_rails, - self.via_shift)
|
||||
correct2 = vector(self.contact_shift + drc["minwidth_metal2"],0)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset + correct1 - correct2,
|
||||
rotate=90)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=vector(line_x_offset, offset[1]) + correct1,
|
||||
rotate=90)
|
||||
# First rail routing Right
|
||||
right_side = []
|
||||
right_side.append(self.nand2_1_A_position)
|
||||
right_side.append(self.nand2_1_B_position)
|
||||
for size in ["1","2"]:
|
||||
for pin in ["A","B","C"]:
|
||||
right_side.append(getattr(self,"nand3_"+size+"_"+pin+"_position"))
|
||||
|
||||
line_indices = [2, 5, 5, 2, 3, 5, 4, 3]
|
||||
for i in range(len(right_side)):
|
||||
offset = right_side[i]
|
||||
line_x_offset = self.rail_1_x_offsets[line_indices[i]]
|
||||
base = vector(line_x_offset, offset[1])
|
||||
self.add_rect(layer="metal1",
|
||||
offset=base,
|
||||
width=offset[0] - line_x_offset,
|
||||
height=drc["minwidth_metal1"])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=base + correct1,
|
||||
rotate=90)
|
||||
|
||||
# OE_bar [Bus # 1] to nor2 B input
|
||||
layer_stack = ("metal2", "via1", "metal1")
|
||||
start = self.nor2_1_B_position
|
||||
mid1 = [self.nor2_1_B_position[0] + 2 * drc["minwidth_metal2"], start[1]]
|
||||
mid2 = [mid1[0], self.nor2_1_gnd_position[1] - 2 * drc["minwidth_metal1"]]
|
||||
mid3 = [self.rail_1_x_offsets[1] + 0.5 * drc["minwidth_metal2"], mid2[1]]
|
||||
end = [mid3[0], self.output_port_gap]
|
||||
self.add_wire(layer_stack, [start, mid1, mid2, mid3, end])
|
||||
|
||||
layer_stack = ("metal1")
|
||||
start = [self.inv1_Z_position[0], self.inv1_Z_position[1] + 0.5 * drc["minwidth_metal1"]]
|
||||
mid1 = [start[0] + drc["minwidth_metal2"], start[1]]
|
||||
mid2 = [mid1[0], self.nand2_1_B_position
|
||||
[1] + 0.5 * drc["minwidth_metal1"]]
|
||||
end = [self.nand2_1_B_position[0], mid2[1]]
|
||||
self.add_path(layer_stack, [start, mid1, mid2, end])
|
||||
|
||||
def add_2nd_row_routing(self):
|
||||
# Second rail routing
|
||||
left_side = []
|
||||
left_side.append(self.inv2_Z_position)
|
||||
left_side.append(self.inv7_Z_position)
|
||||
left_side.append(self.inv5_A_position)
|
||||
line_indices = [1, 0, 2]
|
||||
#line_indices = [1,2]
|
||||
for i in range(len(left_side)):
|
||||
offset = left_side[i]
|
||||
line_x_offset = self.rail_2_x_offsets[line_indices[i]]
|
||||
self.add_rect(layer="metal1",
|
||||
offset=offset,
|
||||
width=line_x_offset - offset[0] + drc["minwidth_metal2"],
|
||||
height=drc["minwidth_metal1"])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[line_x_offset + self.gap_between_rails,
|
||||
offset[1] - self.via_shift],
|
||||
rotate=90)
|
||||
|
||||
# Replica bitline (rblk to replica bitline input)
|
||||
layer_stack = ("metal2", "via1", "metal1")
|
||||
start = [self.rail_2_x_offsets[1] + 0.5 * drc["minwidth_metal2"],
|
||||
self.output_port_gap]
|
||||
mid1 = [start[0], 0.5 * drc["minwidth_metal1"]]
|
||||
end = [self.replica_en_offset[0], mid1[1]]
|
||||
|
||||
self.add_wire(layer_stack, [start, mid1, end])
|
||||
|
||||
height = self.replica_en_offset[1] - end[1] + 0.5 * drc["minwidth_metal1"]
|
||||
|
||||
self.add_rect(layer="metal1",
|
||||
offset=end - vector([0.5 * drc["minwidth_metal1"]] * 2),
|
||||
width=drc["minwidth_metal1"],
|
||||
height=height)
|
||||
|
||||
# Replica bitline (replica bitline output to the buffer [inv4,inv5])
|
||||
start = [self.rail_2_x_offsets[2], self.replica_out_offset[1]]
|
||||
end = self.replica_out_offset - vector(0.5 * drc["minwidth_metal1"],0)
|
||||
self.add_rect(layer="metal3",
|
||||
offset=start,
|
||||
width=self.replica_out_offset[0] - self.rail_2_x_offsets[2],
|
||||
height=drc["minwidth_metal3"])
|
||||
self.add_via(layers=("metal2", "via2", "metal3"),
|
||||
offset=start)
|
||||
self.add_via(layers=("metal2", "via2", "metal3"),
|
||||
offset=end)
|
||||
|
||||
def add_vdd_routing(self):
|
||||
""" VDD routing between modules """
|
||||
vdd_rail_index = self.num_rails_2 - 1
|
||||
rail_2_x = self.rail_2_x_offsets[vdd_rail_index]
|
||||
|
||||
# Connection between nor2 vdd to nand3 vdd
|
||||
self.add_rect(layer="metal1",
|
||||
offset=self.nor2_1_vdd_position,
|
||||
width=rail_2_x + drc["minwidth_metal2"],
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
# Connection between top AND_Array vdd to the last line on rail2
|
||||
self.add_rect(layer="metal1",
|
||||
offset=self.nand3_2_vdd_position,
|
||||
width=(rail_2_x + drc["minwidth_metal2"]
|
||||
- self.nand3_2_vdd_position[0]),
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
# Connection in horizontal metal2 vdd rail
|
||||
base = vector(rail_2_x + self.gap_between_rails,
|
||||
- self.via_shift)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=base + self.nand2_1_vdd_position.scale(0, 1),
|
||||
rotate=90)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=base + self.nand3_2_vdd_position.scale(0, 1),
|
||||
rotate=90)
|
||||
|
||||
# Connection of msf_vdd to inv1 vdd
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[self.msf_control_vdd_position[0],
|
||||
self.inv1_vdd_position[1]],
|
||||
width=drc["minwidth_metal1"],
|
||||
height=self.msf_control_vdd_position[1] - self.inv1_vdd_position[1])
|
||||
|
||||
vdd_offset = vector(self.replica_bitline.height,3 * drc["minwidth_metal1"])
|
||||
|
||||
self.vdd1_position = vdd_offset + self.offset_replica_bitline
|
||||
self.vdd2_position = vector(rail_2_x, self.output_port_gap)
|
||||
|
||||
def add_gnd_routing(self):
|
||||
""" GND routing """
|
||||
self.gnd_position = self.offset_replica_bitline
|
||||
|
||||
# Connection of msf_control gnds to the metal2 gnd rail
|
||||
for gnd_offset in self.msf_control_gnd_positions:
|
||||
self.add_rect(layer="metal2",
|
||||
offset=gnd_offset,
|
||||
width=(self.rail_1_x_offsets[0] - gnd_offset[0]
|
||||
+ drc["minwidth_metal2"]),
|
||||
height=drc["minwidth_metal2"])
|
||||
|
||||
# Connect msf_control gnd to nand3 gnd
|
||||
self.add_rect(layer="metal1",
|
||||
offset=self.nor2_1_gnd_position,
|
||||
width=self.offset_replica_bitline[0],
|
||||
height=drc["minwidth_metal1"])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=[self.rail_1_x_offsets[0] + self.gap_between_rails,
|
||||
self.nor2_1_gnd_position[1] - self.via_shift],
|
||||
rotate=90)
|
||||
|
||||
# nand3 gnd to replica bitline gnd
|
||||
self.add_rect(layer="metal1",
|
||||
offset=self.nand3_2_gnd_position,
|
||||
width=(self.offset_replica_bitline[0]
|
||||
- self.nand3_2_gnd_position[0]),
|
||||
height=drc["minwidth_metal1"])
|
||||
|
||||
def add_input_routing(self):
|
||||
""" Input pin routing """
|
||||
# WEb, CEb, OEb assign from msf_control pin
|
||||
self.WEb_position = self.msf_control_WEb_position
|
||||
self.CSb_position = self.msf_control_CSb_position
|
||||
self.OEb_position = self.msf_control_OEb_position
|
||||
|
||||
# Clk
|
||||
clk_y = self.inv1_vdd_position[1] + 6 * drc["minwidth_metal1"]
|
||||
self.clk_position = vector(0, clk_y)
|
||||
|
||||
# clk port to inv1 A
|
||||
layer_stack = ("metal2", "via1", "metal1")
|
||||
start = self.inv1_A_position + vector(0, 0.5 * drc["minwidth_metal1"])
|
||||
mid1 = vector(self.inv1_A_position[0] - 2 * drc["minwidth_metal2"],
|
||||
start.y)
|
||||
mid2 = vector(mid1.x, clk_y)
|
||||
self.clk_position = vector(0, mid2[1])
|
||||
|
||||
self.add_wire(layer_stack, [start, mid1, mid2, self.clk_position])
|
||||
|
||||
|
||||
# clk line to msf_control_clk
|
||||
self.add_rect(layer="metal1",
|
||||
offset=[self.msf_control_clk_position[0],
|
||||
self.clk_position[1]],
|
||||
width=drc["minwidth_metal1"],
|
||||
height=(self.msf_control_clk_position[1]
|
||||
- self.clk_position[1]))
|
||||
|
||||
# clk connection to nor2 A input
|
||||
start = [self.inv1_A_position[0] - 2 * drc["minwidth_metal2"],
|
||||
self.inv1_A_position[1] + 0.5 * drc["minwidth_metal1"]]
|
||||
mid1 = [start[0] - 3 * drc["minwidth_metal2"], start[1]]
|
||||
mid2 = [mid1[0], self.nor2_1_A_position[1]]
|
||||
|
||||
self.add_path("metal1", [start, mid1, mid2, self.nor2_1_A_position])
|
||||
|
||||
correct = vector(0, 0.5 * drc["minwidth_metal1"])
|
||||
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=self.clk_position + correct,
|
||||
rotate=270)
|
||||
self.add_via(layers=("metal2", "via2", "metal3"),
|
||||
offset=self.clk_position + correct,
|
||||
rotate=270)
|
||||
|
||||
def add_output_routing(self):
|
||||
""" Output pin routing """
|
||||
# clk_bar
|
||||
self.clk_bar_position = vector(self.rail_1_x_offsets[self.num_rails_1 - 1],
|
||||
0)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=self.clk_bar_position,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.output_port_gap)
|
||||
self.add_via(layers=("metal2", "via2", "metal3"),
|
||||
offset=self.clk_bar_position)
|
||||
|
||||
|
||||
# tri_en
|
||||
correct = vector (0, 0.5 * drc["minwidth_metal1"])
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=self.nor2_1_Z_position + correct,
|
||||
rotate=270)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=self.nor2_1_Z_position.scale(1, 0),
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.nor2_1_Z_position.y + correct.y)
|
||||
self.add_via(layers=("metal2", "via2", "metal3"),
|
||||
offset=self.nor2_1_Z_position.scale(1, 0))
|
||||
self.tri_en_position = vector(self.nor2_1_Z_position[0], 0)
|
||||
|
||||
# tri_en_bar
|
||||
correct = vector(drc["minwidth_metal2"], 0)
|
||||
self.tri_en_bar_position = self.nand2_1_Z_position.scale(1, 0) - correct
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=self.nand2_1_Z_position - correct)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=self.tri_en_bar_position,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.nand2_1_Z_position[1] + drc["minwidth_metal1"])
|
||||
self.add_via(layers=("metal2", "via2", "metal3"),
|
||||
offset=self.tri_en_bar_position)
|
||||
|
||||
# w_en
|
||||
self.w_en_position = vector(self.rail_2_x_offsets[0], 0)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=self.w_en_position,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.output_port_gap)
|
||||
self.add_via(layers=("metal2", "via2", "metal3"),
|
||||
offset=self.w_en_position)
|
||||
|
||||
# s_en
|
||||
self.s_en_position = self.inv4_Z_position.scale(1,0)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=self.inv4_Z_position)
|
||||
self.add_rect(layer="metal2",
|
||||
offset=self.s_en_position,
|
||||
width=drc["minwidth_metal2"],
|
||||
height=self.inv4_Z_position[1] + drc["minwidth_metal1"])
|
||||
self.add_via(layers=("metal2", "via2", "metal3"),
|
||||
offset=self.s_en_position)
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import os
|
||||
import inspect
|
||||
import globals
|
||||
import sys
|
||||
|
||||
# the debug levels:
|
||||
# 0 = minimum output (default)
|
||||
# 1 = major stages
|
||||
# 2 = verbose
|
||||
# n = custom setting
|
||||
|
||||
|
||||
def error(str,return_value=None):
|
||||
(frame, filename, line_number, function_name, lines,
|
||||
index) = inspect.getouterframes(inspect.currentframe())[1]
|
||||
print "ERROR: file ", os.path.basename(filename), ": line ", line_number, ": ", str
|
||||
if return_value:
|
||||
sys.exit(return_value)
|
||||
|
||||
def warning(str):
|
||||
(frame, filename, line_number, function_name, lines,
|
||||
index) = inspect.getouterframes(inspect.currentframe())[1]
|
||||
print "WARNING: file ", os.path.basename(filename), ": line ", line_number, ": ", str
|
||||
|
||||
|
||||
def info(lev, str):
|
||||
OPTS = globals.get_opts()
|
||||
if (OPTS.debug_level >= lev):
|
||||
frm = inspect.stack()[1]
|
||||
mod = inspect.getmodule(frm[0])
|
||||
print "\n[", frm[0].f_code.co_name, "]: ", str
|
||||
# This sometimes gets a NoneType mod...
|
||||
# print "[" , mod.__name__ , "]: ", str
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
import hierarchy_layout
|
||||
import hierarchy_spice
|
||||
import globals
|
||||
import calibre
|
||||
import os
|
||||
|
||||
OPTS = globals.get_opts()
|
||||
|
||||
class design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||
"""
|
||||
Design Class for all modules to inherit the base features.
|
||||
Class consisting of a set of modules and instances of these modules
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds"
|
||||
self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp"
|
||||
|
||||
self.name = name
|
||||
hierarchy_layout.layout.__init__(self, name)
|
||||
hierarchy_spice.spice.__init__(self, name)
|
||||
|
||||
def DRC_LVS(self):
|
||||
"""Checks both DRC and LVS for a module"""
|
||||
if OPTS.check_lvsdrc:
|
||||
tempspice = OPTS.openram_temp + "/temp.sp"
|
||||
tempgds = OPTS.openram_temp + "/temp.gds"
|
||||
self.sp_write(tempspice)
|
||||
self.gds_write(tempgds)
|
||||
assert calibre.run_drc(self.name, tempgds) == 0
|
||||
assert calibre.run_lvs(self.name, tempgds, tempspice) == 0
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
def DRC(self):
|
||||
"""Checks DRC for a module"""
|
||||
if OPTS.check_lvsdrc:
|
||||
tempgds = OPTS.openram_temp + "/temp.gds"
|
||||
self.gds_write(tempgds)
|
||||
assert calibre.run_drc(self.name, tempgds) == 0
|
||||
os.remove(tempgds)
|
||||
|
||||
def LVS(self):
|
||||
"""Checks LVS for a module"""
|
||||
if OPTS.check_lvsdrc:
|
||||
tempspice = OPTS.openram_temp + "/temp.sp"
|
||||
tempgds = OPTS.openram_temp + "/temp.gds"
|
||||
self.sp_write(tempspice)
|
||||
self.gds_write(tempgds)
|
||||
assert calibre.run_lvs(self.name, tempgds, tempspice) == 0
|
||||
os.remove(tempspice)
|
||||
os.remove(tempgds)
|
||||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
return "design: " + self.name
|
||||
|
||||
def __repr__(self):
|
||||
""" override print function output """
|
||||
text="( design: " + self.name + " pins=" + str(self.pins) + " " + str(self.width) + "x" + str(self.height) + " )\n"
|
||||
for i in self.objs:
|
||||
text+=str(i)+",\n"
|
||||
for i in self.insts:
|
||||
text+=str(i)+",\n"
|
||||
return text
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
word_size = 1
|
||||
num_words = 16
|
||||
num_banks = 1
|
||||
|
||||
tech_name = "freepdk45"
|
||||
|
||||
decoder = "hierarchical_decoder"
|
||||
ms_flop = "ms_flop"
|
||||
ms_flop_array = "ms_flop_array"
|
||||
control_logic = "control_logic"
|
||||
bitcell_array = "bitcell_array"
|
||||
sense_amp = "sense_amp"
|
||||
sense_amp_array = "sense_amp_array"
|
||||
precharge_array = "precharge_array"
|
||||
column_mux_array = "single_level_column_mux_array"
|
||||
write_driver = "write_driver"
|
||||
write_driver_array = "write_driver_array"
|
||||
tri_gate = "tri_gate"
|
||||
tri_gate_array = "tri_gate_array"
|
||||
wordline_driver = "wordline_driver"
|
||||
replica_bitcell = "replica_bitcell"
|
||||
bitcell = "bitcell"
|
||||
delay_chain = "logic_effort_dc"
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
Delivered-To: mrg@ucsc.edu
|
||||
Received: by 10.216.164.197 with SMTP id c47cs36474wel;
|
||||
Sat, 19 Nov 2011 21:23:28 -0800 (PST)
|
||||
Received: by 10.216.133.5 with SMTP id p5mr1106280wei.105.1321766513080;
|
||||
Sat, 19 Nov 2011 21:21:53 -0800 (PST)
|
||||
Received-SPF: softfail (google.com: best guess record for domain of transitioning wieckows@umich.edu does not designate 128.114.48.32 as permitted sender) client-ip=128.114.48.32;
|
||||
Received: by 10.241.242.69 with POP3 id 5mf3470160wwf.5;
|
||||
Sat, 19 Nov 2011 21:21:53 -0800 (PST)
|
||||
X-Gmail-Fetch-Info: mguthaus@gmail.com 1 smtp.gmail.com 995 mguthaus
|
||||
Delivered-To: mguthaus@gmail.com
|
||||
Received: by 10.231.207.15 with SMTP id fw15cs52829ibb;
|
||||
Thu, 14 Oct 2010 12:49:35 -0700 (PDT)
|
||||
Received: by 10.142.224.8 with SMTP id w8mr3585480wfg.123.1287085774723;
|
||||
Thu, 14 Oct 2010 12:49:34 -0700 (PDT)
|
||||
Return-Path: <wieckows@umich.edu>
|
||||
Received: from mail-01.cse.ucsc.edu (mail-01.cse.ucsc.edu [128.114.48.32])
|
||||
by mx.google.com with ESMTP id x31si25240170wfd.118.2010.10.14.12.49.34;
|
||||
Thu, 14 Oct 2010 12:49:34 -0700 (PDT)
|
||||
Received-SPF: neutral (google.com: 128.114.48.32 is neither permitted nor denied by best guess record for domain of wieckows@umich.edu) client-ip=128.114.48.32;
|
||||
Authentication-Results: mx.google.com; spf=neutral (google.com: 128.114.48.32 is neither permitted nor denied by best guess record for domain of wieckows@umich.edu) smtp.mail=wieckows@umich.edu
|
||||
Received: from services.cse.ucsc.edu (services.cse.ucsc.edu [128.114.48.10])
|
||||
by mail-01.cse.ucsc.edu (Postfix) with ESMTP id 60660100985C
|
||||
for <mrg@mail-01.cse.ucsc.edu>; Thu, 14 Oct 2010 12:49:34 -0700 (PDT)
|
||||
Received: from mailgw.soe.ucsc.edu (mailgw.cse.ucsc.edu [128.114.48.9])
|
||||
by services.cse.ucsc.edu (8.13.6/8.13.6) with ESMTP id o9EJnXcg026705
|
||||
(version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL)
|
||||
for <mrg@soe.ucsc.edu>; Thu, 14 Oct 2010 12:49:34 -0700 (PDT)
|
||||
X-ASG-Debug-ID: 1287085773-35c0a0820001-k66d7V
|
||||
Received: from hellskitchen.mr.itd.umich.edu (smtp.mail.umich.edu [141.211.14.82]) by mailgw.soe.ucsc.edu with ESMTP id iCFqYJFhVj1wHPNy for <mrg@soe.ucsc.edu>; Thu, 14 Oct 2010 12:49:33 -0700 (PDT)
|
||||
X-Barracuda-Envelope-From: wieckows@umich.edu
|
||||
X-Barracuda-Apparent-Source-IP: 141.211.14.82
|
||||
Received: FROM [10.0.1.4] (adsl-99-67-97-169.dsl.sfldmi.sbcglobal.net [99.67.97.169])
|
||||
By hellskitchen.mr.itd.umich.edu ID 4CB75ECA.5F7DC.12653 ;
|
||||
Authuser wieckows;
|
||||
14 Oct 2010 15:49:30 EDT
|
||||
Content-Type: text/plain; charset=us-ascii
|
||||
Mime-Version: 1.0 (Apple Message framework v1081)
|
||||
Subject: Re: GDS Mill
|
||||
From: Michael Wieckowski <wieckows@umich.edu>
|
||||
X-ASG-Orig-Subj: Re: GDS Mill
|
||||
In-Reply-To: <BEC6726E-7B98-43D8-8AAA-2AA259DFFDA8@soe.ucsc.edu>
|
||||
Date: Thu, 14 Oct 2010 15:49:29 -0400
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
Message-Id: <7C1B8C49-7D87-4BF2-8ABF-6555CF7B37AD@umich.edu>
|
||||
References: <BEC6726E-7B98-43D8-8AAA-2AA259DFFDA8@soe.ucsc.edu>
|
||||
To: Matthew Guthaus <mrg@soe.ucsc.edu>
|
||||
X-Mailer: Apple Mail (2.1081)
|
||||
X-Barracuda-Connect: smtp.mail.umich.edu[141.211.14.82]
|
||||
X-Barracuda-Start-Time: 1287085773
|
||||
X-Barracuda-URL: http://mailgw.cse.ucsc.edu:8000/cgi-mod/mark.cgi
|
||||
X-Virus-Scanned: by bsmtpd at soe.ucsc.edu
|
||||
|
||||
Hi Matt,
|
||||
|
||||
Feel free to use / modify / distribute the code as you like.
|
||||
|
||||
-Mike
|
||||
|
||||
|
||||
On Oct 14, 2010, at 3:07 PM, Matthew Guthaus wrote:
|
||||
|
||||
> Hi Michael (& Dennis),
|
||||
>=20
|
||||
> A student and I were looking at your GDS tools, but we noticed that =
|
||||
there is no license. What is the license?
|
||||
>=20
|
||||
> Thanks,
|
||||
>=20
|
||||
> Matt
|
||||
>=20
|
||||
> ---
|
||||
> Matthew Guthaus
|
||||
> Assistant Professor, Computer Engineering
|
||||
> University of California Santa Cruz
|
||||
> http://vlsida.soe.ucsc.edu/
|
||||
>=20
|
||||
>=20
|
||||
>=20
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
README BY TOM GOLUBEV
|
||||
|
||||
gdsMill was adapted from gdsMill by Michael Wieckowski.
|
||||
|
||||
gdsMill allows one to work with GDS files, opening, reading, writing and altering.
|
||||
It is a library and does not work by itself. To get started, refer to ExampleUserDir.
|
||||
To look at sram compiler related example, refer to the sram_examples subdir.
|
||||
|
||||
gdsMill takes a layermap, in a standard format (virtuoso .map files). This is necessary
|
||||
for drawing text and rectangles, since those functions take layernames, and gdsMill needs layer numbers.
|
||||
|
||||
|
||||
gdsMill funcionality:
|
||||
|
||||
The main functions are from vlsilayout. They allow creating a new layout, fill in a box, text, and instancing other structures.
|
||||
|
||||
Files:
|
||||
|
||||
sram_examples:
|
||||
|
||||
gdsMill.sh: Adds gdsMill to python path.
|
||||
Source this before working
|
||||
|
||||
printGDS.py: will dump a text version structures within the gds file.
|
||||
usage: python printGDS.py file
|
||||
|
||||
cell6tDemo.py: Will tile cell6t from sram_lib2.gds and output into layoutB.gds. All cells from source are copied into layoutB.gds.
|
||||
usage: python ./cell6tDemo.py
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#!/usr/bin/env python
|
||||
import gdsMill
|
||||
#we are going to make an array of instances of an existing layout
|
||||
#assume that we designed the "base cell" in cadence
|
||||
#step 1 is to stream it out of cadence into a GDS to work with
|
||||
# creater a streamer object to interact with the cadence libraries
|
||||
streamer = gdsMill.GdsStreamer()
|
||||
|
||||
# use the streamer to take a cadence layout, and convert it to GDS 2 for us to work with
|
||||
# the GDS will be named testLayoutA.gds
|
||||
#streamer.streamFromCadence(cadenceLibraryContainerPath = "~/design/600nmAmi",
|
||||
# libraryName = "gdsMillTest",
|
||||
# cellName = "testLayoutA",
|
||||
# outputPath = "./gdsFiles")
|
||||
|
||||
#next, load our base cell layout from the GDS generated above
|
||||
arrayCellLayout = gdsMill.VlsiLayout()
|
||||
reader = gdsMill.Gds2reader(arrayCellLayout)
|
||||
reader.loadFromFile("./gdsFiles/testLayoutA.gds")
|
||||
|
||||
##since we will be streaming into the same library that testLayout came from
|
||||
#let's rename it here so that we don't overwrite accidentally later
|
||||
arrayCellLayout.rename("arrayCell")
|
||||
|
||||
#now create a new layout
|
||||
#be sure to assign a name, since this will be the root object in our hierarchy to which
|
||||
#all other objects are referenced
|
||||
newLayout = gdsMill.VlsiLayout(name="arrayExample")
|
||||
|
||||
#now place an instnace of our top level layout into the filled layout
|
||||
#hierarchy looks like this:
|
||||
# array example
|
||||
# array cell layout
|
||||
# layout elements
|
||||
# layout elements
|
||||
# layout elements
|
||||
# cell instance
|
||||
# cell instance
|
||||
# cell instance
|
||||
# connection elements .....
|
||||
|
||||
#now create the array of instances
|
||||
for xIndex in range(0,10):
|
||||
for yIndex in range(0,10):
|
||||
if(yIndex%2 == 0):
|
||||
mirror = "MX"
|
||||
else:
|
||||
mirror = "R0"
|
||||
newLayout.addInstance(arrayCellLayout,
|
||||
offsetInMicrons = (xIndex*10.0,yIndex*15.0),
|
||||
mirror = mirror,
|
||||
rotate = 0.0)
|
||||
|
||||
#add a "wire" that in a real example might be a power rail, data bus, etc.
|
||||
newLayout.addPath(layerNumber = newLayout.layerNumbersInUse[7],
|
||||
coordinates = [(-20.0,0.0),(25.0,0),(25.0,10.0)],
|
||||
width = 1.0)
|
||||
|
||||
#add some text that in a real example might be an I/O pin
|
||||
newLayout.addText(text = "Hello",
|
||||
layerNumber = newLayout.layerNumbersInUse[5],
|
||||
offsetInMicrons = (0,0),
|
||||
magnification = 1,
|
||||
rotate = None)
|
||||
|
||||
|
||||
newLayout.prepareForWrite()
|
||||
|
||||
#and now dump the filled layout to a new GDS file
|
||||
writer = gdsMill.Gds2writer(newLayout)
|
||||
writer.writeToFile("./gdsFiles/arrayLayout.gds")
|
||||
#and stream it into cadence
|
||||
#streamer.streamToCadence(cadenceLibraryContainerPath = "~/design/600nmAmi",
|
||||
# libraryName = "gdsMillTest",
|
||||
# inputPath = "./gdsFiles/arrayLayout.gds")
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env python
|
||||
import gdsMill
|
||||
|
||||
#we will add the filler at a higher level of hiearchy
|
||||
#so first, load our top level layout from GDS
|
||||
myTopLevelLayout = gdsMill.VlsiLayout()
|
||||
reader = gdsMill.Gds2reader(myTopLevelLayout)
|
||||
reader.loadFromFile("./gdsFiles/testLayoutA.gds")
|
||||
|
||||
#now create a new layout
|
||||
#be sure to assign a name, since this will be the root object in our hierarchy to which
|
||||
#all other objects are referenced
|
||||
filledLayout = gdsMill.VlsiLayout(name="filledLayout")
|
||||
|
||||
#now place an instnace of our top level layout into the filled layout
|
||||
#hierarchy looks like this:
|
||||
# filled layout
|
||||
# top level layout
|
||||
# layout elements
|
||||
# layout elements
|
||||
# layout elements
|
||||
# fill elements
|
||||
# fill elements .....
|
||||
filledLayout.addInstance(myTopLevelLayout,
|
||||
offsetInMicrons = (0,0),
|
||||
mirror = "",
|
||||
rotate = 0.0)
|
||||
|
||||
#now actaully add the fill - gds mill will create an array of boxes
|
||||
# maintaining spacing from existing layout elements
|
||||
#we'll do it once for two different layers
|
||||
filledLayout.fillAreaDensity(layerToFill = myTopLevelLayout.layerNumbersInUse[5],
|
||||
offsetInMicrons = (-10.0,-10.0), #this is where to start from
|
||||
coverageWidth = 40.0, #size of the fill area in microns
|
||||
coverageHeight = 40.0,
|
||||
minSpacing = 0.5, #distance between fill blocks
|
||||
blockSize = 2.0 #width and height of each filler block in microns
|
||||
)
|
||||
filledLayout.fillAreaDensity(layerToFill = myTopLevelLayout.layerNumbersInUse[7],
|
||||
offsetInMicrons = (-11.0,-11.0), #this is where to start from
|
||||
coverageWidth = 40.0, #size of the fill area in microns
|
||||
coverageHeight = 40.0,
|
||||
minSpacing = 0.5, #distance between fill blocks
|
||||
blockSize = 3.0 #width and height of each filler block in microns
|
||||
)
|
||||
#and now dump the filled layout to a new GDS file
|
||||
writer = gdsMill.Gds2writer(filledLayout)
|
||||
writer.writeToFile("./gdsFiles/filledLayout.gds")
|
||||
#and strea
|
||||
streamer = gdsMill.GdsStreamer()
|
||||
streamer.streamToCadence(cadenceLibraryContainerPath = "~/design/600nmAmi",
|
||||
libraryName = "gdsMillTest",
|
||||
inputPath = "./gdsFiles/filledLayout.gds")
|
||||
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
|||
#-----------------------------
|
||||
#Need a path to a recent python distribution
|
||||
#If a recent one is already installed locally, just comment this out
|
||||
#setenv PATH /some/path/to/python/Python2.6.1/bin\:$PATH
|
||||
|
||||
#-----------------------------
|
||||
#To do automatic stream IN/OUT, we need a path to PIPO (the cadence stream executable)
|
||||
#PIPO is usually inside the dfII/bin directory of your cadence installation
|
||||
#if not used, this can be commented out
|
||||
#setenv PATH /some/path/to/cadence/ic-5.141_usr5/tools/dfII/bin\:/some/path/to/cadence/ic-5.141_usr5/tools/bin\:$PATH
|
||||
|
||||
#-----------------------------
|
||||
#This is the search path where Python will find GDSMill
|
||||
if ( ! ($?PYTHONPATH) ) then
|
||||
setenv PYTHONPATH /design/common/GDSMill
|
||||
else
|
||||
setenv PYTHONPATH /design/common/GDSMill\:$PYTHONPATH
|
||||
endif
|
||||
|
||||
#-----------------------------
|
||||
#If you are going to use GDS Mill in conjunction with a particular Cadence work directory, it often makes sense to source the shell script
|
||||
#required within that directory (for example, a shell script in my work directory sets up permissions to access the design kit files)
|
||||
source ~/design/600nmAmi/600nmAmi.cshrc
|
||||
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env python
|
||||
import gdsMill
|
||||
|
||||
#creater a streamer object to interact with the cadence libraries
|
||||
streamer = gdsMill.GdsStreamer()
|
||||
|
||||
#use the streamer to take a cadence layout, and convert it to GDS 2 for us to work with
|
||||
#the GDS will be named testLayoutA.gds
|
||||
streamer.streamFromCadence(cadenceLibraryContainerPath = "/soe/bchen12/openram/branches/newptx/freepdk45/openram_temp",
|
||||
libraryName = "gdsMillTest",
|
||||
cellName = "testLayoutA",
|
||||
outputPath = "./gdsFiles")
|
||||
|
||||
#create a layout object - this object represents all of the elements within the layout
|
||||
myLayout = gdsMill.VlsiLayout()
|
||||
|
||||
#give our layout object to a gds reader object. The gds reader will look at the binary gds 2 file and
|
||||
#populate the layout object with the file's contents
|
||||
reader = gdsMill.Gds2reader(myLayout)
|
||||
|
||||
#tell the reader object to process the gds file that we streamed in above
|
||||
#un-comment the next line to see some details about the gds contents
|
||||
#reader.debugToTerminal=1
|
||||
reader.loadFromFile("./gdsFiles/testLayoutA.gds")
|
||||
|
||||
#our layout object now contains all of the elements of the layout
|
||||
#let add a box to the layout
|
||||
myLayout.addBox(layerNumber = myLayout.layerNumbersInUse[0], #pick some layer
|
||||
offsetInMicrons = (-10,0), #location
|
||||
width = 10.0, #units are microns
|
||||
height = 5.0,
|
||||
# updateInternalMap = True, #This is important for visualization - see note 1 below
|
||||
center = True) #origin is in the center or the bottom left corner
|
||||
#Note 1:the layout object keeps track of its elements in a 2D map (no hiearachy)
|
||||
#this makes visualization more efficient. Therefore, to do a PDF output or some other
|
||||
#flat output, you need to "update the internal map" of the layout object. Only do this
|
||||
# ONE TIME after all the objects you are manipulating are fixed
|
||||
|
||||
#let's take out new layout and stream it back into a cadence library
|
||||
#first, create a new GDS
|
||||
#we change the root structure name (this will be the cellview name upon stream in)
|
||||
myLayout.rename("testLayoutB")
|
||||
writer = gdsMill.Gds2writer(myLayout)
|
||||
writer.writeToFile("./gdsFiles/testLayoutB.gds")
|
||||
|
||||
streamer.streamToCadence(cadenceLibraryContainerPath = "~/design/600nmAmi",
|
||||
libraryName = "gdsMillTest",
|
||||
inputPath = "./gdsFiles/testLayoutB.gds")
|
||||
|
||||
#let's create a PDF view of the layout object
|
||||
#first, create the object to represent the visual output
|
||||
visualizer = gdsMill.PdfLayout(myLayout)
|
||||
|
||||
#since we have no knowledge of what the layer numbers mean for this particular technology
|
||||
#we need to assign some colors to them
|
||||
|
||||
#uncomment the following line if you want to actually see the layout numbers in use
|
||||
#print myLayout.layerNumbersInUse
|
||||
|
||||
#for each layer number used in the layout, we will asign it a layer color as a RGB Hex
|
||||
visualizer.layerColors[myLayout.layerNumbersInUse[0]]="#219E1C"
|
||||
visualizer.layerColors[myLayout.layerNumbersInUse[1]]="#271C9E"
|
||||
visualizer.layerColors[myLayout.layerNumbersInUse[2]]="#CC54C8"
|
||||
visualizer.layerColors[myLayout.layerNumbersInUse[3]]="#E9C514"
|
||||
visualizer.layerColors[myLayout.layerNumbersInUse[4]]="#856F00"
|
||||
visualizer.layerColors[myLayout.layerNumbersInUse[5]]="#BD1444"
|
||||
visualizer.layerColors[myLayout.layerNumbersInUse[6]]="#FD1444"
|
||||
visualizer.layerColors[myLayout.layerNumbersInUse[7]]="#FD1414"
|
||||
|
||||
#set the scale so that our PDF isn't enormous
|
||||
visualizer.setScale(500)
|
||||
#tell the pdf layout object to draw everything in our layout
|
||||
visualizer.drawLayout()
|
||||
#and finally, dump it out to a file
|
||||
visualizer.writeToFile("./gdsFiles/gdsOut.pdf")
|
||||
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#-----------------------------
|
||||
#To do automatic stream IN/OUT, we need a path to PIPO (the cadence stream executable)
|
||||
#PIPO is usually inside the dfII/bin directory of your cadence installation
|
||||
#if not used, this can be commented out
|
||||
#setenv PATH /some/path/to/cadence/ic-5.141_usr5/tools/dfII/bin\:/some/path/to/cadence/ic-5.141_usr5/tools/bin\:$PATH
|
||||
|
||||
#-----------------------------
|
||||
#This is the search path where Python will find GDSMill
|
||||
|
||||
export PYTHONPATH=`pwd`/:$PYTHONPATH
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
"""
|
||||
Python GDS Mill Package
|
||||
|
||||
GDS Mill is a Python package for the creation and manipulation of binary GDS2 layout files.
|
||||
"""
|
||||
|
||||
from gds2reader import *
|
||||
from gds2writer import *
|
||||
from pdfLayout import *
|
||||
from vlsiLayout import *
|
||||
from gdsStreamer import *
|
||||
from gdsPrimitives import *
|
||||
|
||||
|
|
@ -0,0 +1,820 @@
|
|||
#!/usr/bin/env python
|
||||
import struct
|
||||
from gdsPrimitives import *
|
||||
|
||||
class Gds2reader:
|
||||
"""Class to read in a file in GDSII format and populate a layout class with it"""
|
||||
## Based on info from http://www.rulabinsky.com/cavd/text/chapc.html
|
||||
global offset
|
||||
offset=0
|
||||
|
||||
def __init__(self,layoutObject,debugToTerminal = 0):
|
||||
self.fileHandle = None
|
||||
self.layoutObject = layoutObject
|
||||
self.debugToTerminal=debugToTerminal
|
||||
|
||||
#do we dump debug data to the screen
|
||||
|
||||
def print64AsBinary(self,number):
|
||||
for index in range(0,64):
|
||||
print (number>>(63-index))&0x1,
|
||||
print "\n"
|
||||
|
||||
def strip_non_ascii(self,string):
|
||||
#''' Returns the string without non ASCII characters'''
|
||||
stripped = (c for c in string if 0 < ord(c) < 127)
|
||||
return "".join(stripped)
|
||||
|
||||
def ieeeDoubleFromIbmData(self,ibmData):
|
||||
#the GDS double is in IBM 370 format like this:
|
||||
#(1)sign (7)exponent (56)mantissa
|
||||
#exponent is excess 64, mantissa has no implied 1
|
||||
#a normal IEEE double is like this:
|
||||
#(1)sign (11)exponent (52)mantissa
|
||||
data = struct.unpack('>q',ibmData)[0]
|
||||
sign = (data >> 63)&0x01
|
||||
exponent = (data >> 56) & 0x7f
|
||||
mantissa = data<<8 #chop off sign and exponent
|
||||
|
||||
if mantissa == 0:
|
||||
newFloat = 0.0
|
||||
else:
|
||||
exponent = ((exponent-64)*4)+1023 #convert to double exponent
|
||||
#re normalize
|
||||
while mantissa & 0x8000000000000000 == 0:
|
||||
mantissa<<=1
|
||||
exponent-=1
|
||||
mantissa<<=1 #remove the assumed high bit
|
||||
exponent-=1
|
||||
#check for underflow error -- should handle these properly!
|
||||
if(exponent<=0):
|
||||
print "Underflow Error"
|
||||
elif(exponent == 2047):
|
||||
print "Overflow Error"
|
||||
#re assemble
|
||||
newFloat=(sign<<63)|(exponent<<52)|((mantissa>>12)&0xfffffffffffff)
|
||||
asciiDouble = struct.pack('>q',newFloat)
|
||||
#convert back to double
|
||||
newFloat = struct.unpack('>d',asciiDouble)[0]
|
||||
return newFloat
|
||||
|
||||
def ieeeFloatCheck(self,aFloat):
|
||||
asciiDouble = struct.pack('>d',aFloat)
|
||||
data = struct.unpack('>q',asciiDouble)[0]
|
||||
sign = data >> 63
|
||||
exponent = ((data >> 52) & 0x7ff)-1023
|
||||
# BINWU: Cleanup
|
||||
print exponent+1023
|
||||
mantissa = data << 12 #chop off sign and exponent
|
||||
# BINWU: Cleanup
|
||||
#self.print64AsBinary((sign<<63)|((exponent+1023)<<52)|(mantissa>>12))
|
||||
asciiDouble = struct.pack('>q',(sign<<63)|(exponent+1023<<52)|(mantissa>>12))
|
||||
newFloat = struct.unpack('>d',asciiDouble)[0]
|
||||
print "Check:"+str(newFloat)
|
||||
|
||||
def readNextRecord(self):
|
||||
global offset
|
||||
recordLengthAscii = self.fileHandle.read(2) #first 2 bytes tell us the length of the record
|
||||
recordLength = struct.unpack(">h",recordLengthAscii) #gives us a tuple with a short int inside
|
||||
offlist = list(recordLength) #change tuple to a list
|
||||
offset += float(offlist[0]) #count offset
|
||||
#print float(offlist[0])
|
||||
#print offset #print out the record numbers for de-bugging
|
||||
record = self.fileHandle.read(recordLength[0]-2) #read the rest of it (first 2 bytes were already read)
|
||||
return record
|
||||
|
||||
def readHeader(self):
|
||||
self.layoutObject.info.clear()
|
||||
## Header
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if(idBits==('\x00','\x02') and len(record)==4):
|
||||
gdsVersion = struct.unpack(">h",record[2]+record[3])[0]
|
||||
self.layoutObject.info["gdsVersion"]=gdsVersion
|
||||
if(self.debugToTerminal==1):
|
||||
print "GDS II Version "+str(gdsVersion)
|
||||
else:
|
||||
if(self.debugToTerminal==1):
|
||||
print "Invalid GDSII Header"
|
||||
return -1
|
||||
|
||||
#read records until we hit the UNITS section... this is the last part of the header
|
||||
while 1:
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
## Modified Date
|
||||
if(idBits==('\x01','\x02') and len(record)==26):
|
||||
modYear = struct.unpack(">h",record[2]+record[3])[0]
|
||||
modMonth = struct.unpack(">h",record[4]+record[5])[0]
|
||||
modDay = struct.unpack(">h",record[6]+record[7])[0]
|
||||
modHour = struct.unpack(">h",record[8]+record[9])[0]
|
||||
modMinute = struct.unpack(">h",record[10]+record[11])[0]
|
||||
modSecond = struct.unpack(">h",record[12]+record[13])[0]
|
||||
lastAccessYear = struct.unpack(">h",record[14]+record[15])[0]
|
||||
lastAccessMonth = struct.unpack(">h",record[16]+record[17])[0]
|
||||
lastAccessDay = struct.unpack(">h",record[18]+record[19])[0]
|
||||
lastAccessHour = struct.unpack(">h",record[20]+record[21])[0]
|
||||
lastAccessMinute = struct.unpack(">h",record[22]+record[23])[0]
|
||||
lastAccessSecond = struct.unpack(">h",record[24]+record[25])[0]
|
||||
self.layoutObject.info["dates"]=(modYear,modMonth,modDay,modHour,modMinute,modSecond,\
|
||||
lastAccessYear,lastAccessMonth,lastAccessDay,lastAccessHour,lastAccessMinute,lastAccessSecond)
|
||||
if(self.debugToTerminal==1):
|
||||
print "Date Modified:"+str(modYear)+","+str(modMonth)+","+str(modDay)+","+str(modHour)+","+str(modMinute)+","+str(modSecond)
|
||||
print "Date Last Accessed:"+str(lastAccessYear)+","+str(lastAccessMonth)+","+str(lastAccessDay)+\
|
||||
","+str(lastAccessHour)+","+str(lastAccessMinute)+","+str(lastAccessSecond)
|
||||
## LibraryName
|
||||
elif(idBits==('\x02','\x06')):
|
||||
libraryName = record[2::]
|
||||
self.layoutObject.info["libraryName"]=libraryName
|
||||
if(self.debugToTerminal==1):
|
||||
print "Library: "+libraryName
|
||||
## reference libraries
|
||||
elif(idBits==('\x1F','\x06')):
|
||||
referenceLibraryA = record[2:46]
|
||||
referenceLibraryB = record[47:91]
|
||||
self.layoutObject.info["referenceLibraries"]=(referenceLibraryA,referenceLibraryB)
|
||||
if(self.debugToTerminal==1):
|
||||
print "Reference Libraries:"+referenceLibraryA+","+referenceLibraryB
|
||||
elif(idBits==('\x20','\x06')):
|
||||
fontA = record[2:45]
|
||||
fontB = record[46:89]
|
||||
fontC = record[90:133]
|
||||
fontD = record[134:177]
|
||||
self.layoutObject.info["fonts"]=(fontA,fontB,fontC,fontD)
|
||||
if(self.debugToTerminal==1):
|
||||
print "Fonts:"+fontA+","+fontB+","+fontC+","+fontD
|
||||
elif(idBits==('\x23','\x06')):
|
||||
attributeTable = record[2:45]
|
||||
self.layoutObject.info["attributeTable"]=attributeTable
|
||||
if(self.debugToTerminal==1):
|
||||
print "Attributes:"+attributeTable
|
||||
elif(idBits==('\x22','\x02')):
|
||||
generations = struct.unpack(">h",record[2]+record[3])
|
||||
self.layoutObject.info["generations"]=generations
|
||||
if(self.debugToTerminal==1):
|
||||
print "Generations:"+generations
|
||||
elif(idBits==('\x36','\x02')):
|
||||
fileFormat = struct.unpack(">h",record[2]+record[3])
|
||||
self.layoutObject.info["fileFormat"]=fileFormat
|
||||
if(self.debugToTerminal==1):
|
||||
print "File Format:"+fileFormat
|
||||
elif(idBits==('\x37','\x06')):
|
||||
mask = record[2::]
|
||||
self.layoutObject.info["mask"] = mask
|
||||
if(self.debugToTerminal==1):
|
||||
print "Mask: "+mask
|
||||
elif(idBits==('\x03','\x05')): #this is also wrong b/c python doesn't natively have an 8 byte float
|
||||
userUnits=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9])
|
||||
dbUnits=self.ieeeDoubleFromIbmData
|
||||
self.layoutObject.info["units"] = (userUnits,dbUnits)
|
||||
|
||||
#print "userUnits %s"%((record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9])).encode("hex")
|
||||
#print "dbUnits %s"%(record[10]+record[11]+record[12]+record[13]+record[14]+record[15]+record[16]+record[17]).encode("hex")
|
||||
|
||||
if(self.debugToTerminal==1):
|
||||
print "Units: 1 user unit="+str(userUnits)+" database units, 1 database unit="+str(dbUnits)+" meters."
|
||||
break;
|
||||
if(self.debugToTerminal==1):
|
||||
print "End of GDSII Header Found"
|
||||
return 1
|
||||
|
||||
def readBoundary(self):
|
||||
##reads in a boundary type structure = a filled polygon
|
||||
thisBoundary=GdsBoundary()
|
||||
while 1:
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if(idBits==('\x26','\x01')): #ELFLAGS
|
||||
elementFlags = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisBoundary.elementFlags=elementFlags
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tElement Flags: "+str(elementFlags)
|
||||
elif(idBits==('\x2F','\x03')): #PLEX
|
||||
plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0]
|
||||
thisBoundary.plex=plex
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tPLEX: "+str(plex)
|
||||
elif(idBits==('\x0D','\x02')): #Layer
|
||||
drawingLayer = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisBoundary.drawingLayer=drawingLayer
|
||||
if drawingLayer not in self.layoutObject.layerNumbersInUse:
|
||||
self.layoutObject.layerNumbersInUse += [drawingLayer]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tDrawing Layer: "+str(drawingLayer)
|
||||
elif(idBits==('\x16','\x02')): #Purpose
|
||||
purposeLayer = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisBoundary.purposeLayer=purposeLayer
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tPurpose Layer: "+str(purposeLayer)
|
||||
elif(idBits==('\x0E','\x02')): #DataType
|
||||
dataType = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisBoundary.dataType=dataType
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tData Type: "+str(dataType)
|
||||
elif(idBits==('\x10','\x03')): #XY Data Points
|
||||
numDataPoints = len(record)-2 #packed as XY coordinates 4 bytes each
|
||||
thisBoundary.coordinates=[]
|
||||
for index in range(2,numDataPoints+2,8): #incorporate the 2 byte offset
|
||||
x=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0]
|
||||
y=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0]
|
||||
thisBoundary.coordinates+=[(x,y)]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tXY Point: "+str(x)+","+str(y)
|
||||
elif(idBits==('\x11','\x00')): #End Of Element
|
||||
break;
|
||||
return thisBoundary
|
||||
|
||||
def readPath(self): #reads in a path structure
|
||||
thisPath=GdsPath()
|
||||
while 1:
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if(idBits==('\x26','\x01')): #ELFLAGS
|
||||
elementFlags = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisPath.elementFlags=elementFlags
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tElement Flags: "+str(elementFlags)
|
||||
elif(idBits==('\x2F','\x03')): #PLEX
|
||||
plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0]
|
||||
thisPath.plex=plex
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tPLEX: "+str(plex)
|
||||
elif(idBits==('\x0D','\x02')): #Layer
|
||||
drawingLayer = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisPath.drawingLayer=drawingLayer
|
||||
if drawingLayer not in self.layoutObject.layerNumbersInUse:
|
||||
self.layoutObject.layerNumbersInUse += [drawingLayer]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tDrawing Layer: "+str(drawingLayer)
|
||||
elif(idBits==('\x16','\x02')): #Purpose
|
||||
purposeLayer = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisPath.purposeLayer=purposeLayer
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tPurpose Layer: "+str(purposeLayer)
|
||||
elif(idBits==('\x21','\x02')): #Path type
|
||||
pathType = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisPath.pathType=pathType
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tPath Type: "+str(pathType)
|
||||
elif(idBits==('\x0F','\x03')): #Path width
|
||||
pathWidth = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0]
|
||||
thisPath.pathWidth=pathWidth
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tPath Width: "+str(pathWidth)
|
||||
elif(idBits==('\x10','\x03')): #XY Data Points
|
||||
numDataPoints = len(record)-2 #packed as XY coordinates 4 bytes each
|
||||
thisPath.coordinates=[]
|
||||
for index in range(2,numDataPoints+2,8): #incorporate the 2 byte offset
|
||||
x=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0]
|
||||
y=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0]
|
||||
thisPath.coordinates+=[(x,y)]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tXY Point: "+str(x)+","+str(y)
|
||||
elif(idBits==('\x11','\x00')): #End Of Element
|
||||
break;
|
||||
return thisPath
|
||||
|
||||
def readSref(self): #reads in a reference to another structure
|
||||
thisSref=GdsSref()
|
||||
while 1:
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if(idBits==('\x26','\x01')): #ELFLAGS
|
||||
elementFlags = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisSref.elementFlags=elementFlags
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tElement Flags: "+str(elementFlags)
|
||||
elif(idBits==('\x2F','\x03')): #PLEX
|
||||
plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0]
|
||||
thisSref.plex=plex
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tPLEX: "+str(plex)
|
||||
elif(idBits==('\x12','\x06')): #Reference Name
|
||||
sName = self.strip_non_ascii(record[2::])
|
||||
thisSref.sName=sName.rstrip()
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tReference Name:"+sName
|
||||
elif(idBits==('\x1A','\x01')): #Transformation
|
||||
transFlags = struct.unpack(">H",record[2]+record[3])[0]
|
||||
mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy
|
||||
rotateFlag = bool(transFlags&0x0002)
|
||||
magnifyFlag = bool(transFlags&0x0004)
|
||||
thisSref.transFlags=(mirrorFlag,rotateFlag,magnifyFlag)
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tMirror X:"+str(mirrorFlag)
|
||||
print "\t\t\tRotate:"+str(rotateFlag)
|
||||
print "\t\t\tMagnify:"+str(magnifyFlag)
|
||||
elif(idBits==('\x1B','\x05')): #Magnify
|
||||
magFactor=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9])
|
||||
thisSref.magFactor=magFactor
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tMagnification:"+str(magFactor)
|
||||
elif(idBits==('\x1C','\x05')): #Rotate Angle
|
||||
rotateAngle=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9])
|
||||
thisSref.rotateAngle=rotateAngle
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tRotate Angle (CCW):"+str(rotateAngle)
|
||||
elif(idBits==('\x10','\x03')): #XY Data Points
|
||||
index=2
|
||||
x=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0]
|
||||
y=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0]
|
||||
thisSref.coordinates=(x,y)
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tXY Point: "+str(x)+","+str(y)
|
||||
elif(idBits==('\x11','\x00')): #End Of Element
|
||||
break;
|
||||
return thisSref
|
||||
|
||||
def readAref(self): #an array of references
|
||||
thisAref = GdsAref()
|
||||
while 1:
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if(idBits==('\x26','\x01')): #ELFLAGS
|
||||
elementFlags = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisAref.elementFlags=elementFlags
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tElement Flags: "+str(elementFlags)
|
||||
elif(idBits==('\x2F','\x03')): #PLEX
|
||||
plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0]
|
||||
thisAref.plex=plex
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tPLEX: "+str(plex)
|
||||
elif(idBits==('\x12','\x06')): #Reference Name
|
||||
aName = record[2::]
|
||||
thisAref.aName=aName
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tReference Name:"+aName
|
||||
elif(idBits==('\x1A','\x01')): #Transformation
|
||||
transFlags = struct.unpack(">H",record[2]+record[3])[0]
|
||||
mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy
|
||||
rotateFlag = bool(transFlags&0x0002)
|
||||
magnifyFlag = bool(transFlags&0x0004)
|
||||
thisAref.transFlags=(mirrorFlag,rotateFlag,magnifyFlag)
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tMirror X:"+str(mirrorFlag)
|
||||
print "\t\t\tRotate:"+str(rotateFlag)
|
||||
print "\t\t\tMagnify:"+str(magnifyFlag)
|
||||
elif(idBits==('\x1B','\x05')): #Magnify
|
||||
magFactor=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9])
|
||||
thisAref.magFactor=magFactor
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tMagnification:"+str(magFactor)
|
||||
elif(idBits==('\x1C','\x05')): #Rotate Angle
|
||||
rotateAngle=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9])
|
||||
thisAref.rotateAngle=rotateAngle
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tRotate Angle (CCW):"+str(rotateAngle)
|
||||
elif(idBits==('\x10','\x03')): #XY Data Points
|
||||
index=2
|
||||
topLeftX=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0]
|
||||
topLeftY=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0]
|
||||
rightMostX=struct.unpack(">i",record[index+8]+record[index+9]+record[index+10]+record[index+11])[0]
|
||||
bottomMostY=struct.unpack(">i",record[index+12]+record[index+13]+record[index+14]+record[index+15])[0]
|
||||
thisAref.coordinates=[(topLeftX,topLeftY),(rightMostX,topLeftY),(topLeftX,bottomMostY)]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tTop Left Point: "+str(topLeftX)+","+str(topLeftY)
|
||||
print "\t\t\t\tArray Width: "+str(rightMostX-topLeftX)
|
||||
print "\t\t\t\tArray Height: "+str(topLeftY-bottomMostY)
|
||||
elif(idBits==('\x11','\x00')): #End Of Element
|
||||
break;
|
||||
return thisAref
|
||||
|
||||
def readText(self):
|
||||
##reads in a text structure
|
||||
thisText=GdsText()
|
||||
while 1:
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if(idBits==('\x26','\x01')): #ELFLAGS
|
||||
elementFlags = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisText.elementFlags=elementFlags
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tElement Flags: "+str(elementFlags)
|
||||
elif(idBits==('\x2F','\x03')): #PLEX
|
||||
plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0]
|
||||
thisText.plex=plex
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tPLEX: "+str(plex)
|
||||
elif(idBits==('\x0D','\x02')): #Layer
|
||||
drawingLayer = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisText.drawingLayer=drawingLayer
|
||||
if drawingLayer not in self.layoutObject.layerNumbersInUse:
|
||||
self.layoutObject.layerNumbersInUse += [drawingLayer]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tDrawing Layer: "+str(drawingLayer)
|
||||
elif(idBits==('\x16','\x02')): #Purpose
|
||||
purposeLayer = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisText.purposeLayer=purposeLayer
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tPurpose Layer: "+str(purposeLayer)
|
||||
elif(idBits==('\x1A','\x01')): #Transformation
|
||||
transFlags = struct.unpack(">H",record[2]+record[3])[0]
|
||||
mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy
|
||||
rotateFlag = bool(transFlags&0x0002)
|
||||
magnifyFlag = bool(transFlags&0x0004)
|
||||
thisText.transFlags=(mirrorFlag,rotateFlag,magnifyFlag)
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tMirror X:"+str(mirrorFlag)
|
||||
print "\t\t\tRotate:"+str(rotateFlag)
|
||||
print "\t\t\tMagnify:"+str(magnifyFlag)
|
||||
elif(idBits==('\x1B','\x05')): #Magnify
|
||||
magFactor=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9])
|
||||
thisText.magFactor=magFactor
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tMagnification:"+str(magFactor)
|
||||
elif(idBits==('\x1C','\x05')): #Rotate Angle
|
||||
rotateAngle=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9])
|
||||
thisText.rotateAngle=rotateAngle
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tRotate Angle (CCW):"+str(rotateAngle)
|
||||
elif(idBits==('\x21','\x02')): #Path type
|
||||
pathType = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisText.pathType=pathType
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tPath Type: "+str(pathType)
|
||||
elif(idBits==('\x0F','\x03')): #Path width
|
||||
pathWidth = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0]
|
||||
thisText.pathWidth=pathWidth
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tPath Width: "+str(pathWidth)
|
||||
elif(idBits==('\x1A','\x01')): #Text Presentation
|
||||
presentationFlags = struct.unpack(">H",record[2]+record[3])[0]
|
||||
font = (presentationFlags&0x0030)>>4 ##these flags are a bit sketchy
|
||||
verticalFlags = (presentationFlags&0x000C)
|
||||
horizontalFlags = (presentationFlags&0x0003)
|
||||
thisText.presentationFlags=(font,verticalFlags,horizontalFlags)
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tFont:"+str(font)
|
||||
if(verticalFlags==0):
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tVertical: Top"
|
||||
elif(verticalFlags==1):
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tVertical: Middle"
|
||||
elif(verticalFlags==2):
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tVertical: Bottom"
|
||||
if(horizontalFlags==0):
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tHorizontal: Left"
|
||||
elif(horizontalFlags==1):
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tHorizontal: Center"
|
||||
elif(horizontalFlags==2):
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tHorizontal: Right"
|
||||
elif(idBits==('\x10','\x03')): #XY Data Points
|
||||
index=2
|
||||
x=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0]
|
||||
y=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0]
|
||||
thisText.coordinates=[(x,y)]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tXY Point: "+str(x)+","+str(y)
|
||||
elif(idBits==('\x19','\x06')): #Text String - also the last record in this element
|
||||
textString = record[2::]
|
||||
thisText.textString=textString
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tText String: "+textString
|
||||
elif(idBits==('\x11','\x00')): #End Of Element
|
||||
break;
|
||||
return thisText
|
||||
|
||||
def readNode(self):
|
||||
##reads in a node type structure = an electrical net
|
||||
thisNode = GdsNode()
|
||||
while 1:
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if(idBits==('\x26','\x01')): #ELFLAGS
|
||||
elementFlags = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisNode.elementFlags=elementFlags
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tElement Flags: "+str(elementFlags)
|
||||
elif(idBits==('\x2F','\x03')): #PLEX
|
||||
plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0]
|
||||
thisNode.plex=plex
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tPLEX: "+str(plex)
|
||||
elif(idBits==('\x0D','\x02')): #Layer
|
||||
drawingLayer = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisNode.drawingLayer=drawingLayer
|
||||
if drawingLayer not in self.layoutObject.layerNumbersInUse:
|
||||
self.layoutObject.layerNumbersInUse += [drawingLayer]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tDrawing Layer: "+str(drawingLayer)
|
||||
elif(idBits==('\x2A','\x02')): #Node Type
|
||||
nodeType = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisNode.nodeType=nodeType
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tNode Type: "+str(nodeType)
|
||||
elif(idBits==('\x10','\x03')): #XY Data Points
|
||||
numDataPoints = len(record)-2 #packed as XY coordinates 4 bytes each
|
||||
thisNode.coordinates=[]
|
||||
for index in range(2,numDataPoints+2,8): #incorporate the 2 byte offset
|
||||
x=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0]
|
||||
y=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0]
|
||||
thisNode.coordinates+=[(x,y)]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tXY Point: "+str(x)+","+str(y)
|
||||
elif(idBits==('\x11','\x00')): #End Of Element
|
||||
break;
|
||||
return thisNode
|
||||
|
||||
def readBox(self):
|
||||
##reads in a gds BOX structure
|
||||
thisBox = GdsBox()
|
||||
while 1:
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if(idBits==('\x26','\x01')): #ELFLAGS
|
||||
elementFlags = struct.unpack(">h",record[2]+record[3])
|
||||
thisBox.elementFlags=elementFlags
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tElement Flags: "+str(elementFlags)
|
||||
elif(idBits==('\x2F','\x03')): #PLEX
|
||||
plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0]
|
||||
thisBox.plex=plex
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tPLEX: "+str(plex)
|
||||
elif(idBits==('\x0D','\x02')): #Layer
|
||||
drawingLayer = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisBox.drawingLayer=drawingLayer
|
||||
if drawingLayer not in self.layoutObject.layerNumbersInUse:
|
||||
self.layoutObject.layerNumbersInUse += [drawingLayer]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tDrawing Layer: "+str(drawingLayer)
|
||||
elif(idBits==('\x16','\x02')): #Purpose
|
||||
purposeLayer = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisBox.purposeLayer=purposeLayer
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tPurpose Layer: "+str(purposeLayer)
|
||||
elif(idBits==('\x2D','\x00')): #Box
|
||||
boxValue = struct.unpack(">h",record[2]+record[3])[0]
|
||||
thisBox.boxValue=boxValue
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\tBox Value: "+str(boxValue)
|
||||
elif(idBits==('\x10','\x03')): #XY Data Points that form a closed box
|
||||
numDataPoints = len(record)-2 #packed as XY coordinates 4 bytes each
|
||||
thisBox.coordinates=[]
|
||||
for index in range(2,numDataPoints+2,8): #incorporate the 2 byte offset
|
||||
x=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0]
|
||||
y=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0]
|
||||
thisBox.coordinates+=[(x,y)]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\t\t\tXY Point: "+str(x)+","+str(y)
|
||||
elif(idBits==('\x11','\x00')): #End Of Element
|
||||
break;
|
||||
return thisBox
|
||||
|
||||
def readNextStructure(self):
|
||||
thisStructure = GdsStructure()
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if(idBits==('\x05','\x02') and len(record)==26):
|
||||
createYear = struct.unpack(">h",record[2]+record[3])[0]
|
||||
createMonth = struct.unpack(">h",record[4]+record[5])[0]
|
||||
createDay = struct.unpack(">h",record[6]+record[7])[0]
|
||||
createHour = struct.unpack(">h",record[8]+record[9])[0]
|
||||
createMinute = struct.unpack(">h",record[10]+record[11])[0]
|
||||
createSecond = struct.unpack(">h",record[12]+record[13])[0]
|
||||
modYear = struct.unpack(">h",record[14]+record[15])[0]
|
||||
modMonth = struct.unpack(">h",record[16]+record[17])[0]
|
||||
modDay = struct.unpack(">h",record[18]+record[19])[0]
|
||||
modHour = struct.unpack(">h",record[20]+record[21])[0]
|
||||
modMinute = struct.unpack(">h",record[22]+record[23])[0]
|
||||
modSecond = struct.unpack(">h",record[24]+record[25])[0]
|
||||
thisStructure.createDate=(createYear,createMonth,createDay,createHour,createMinute,createSecond)
|
||||
thisStructure.modDate=(modYear,modMonth,modDay,modHour,modMinute,modSecond)
|
||||
else:
|
||||
#means we have hit the last structure, so return the record
|
||||
#to whoever called us to do something with it
|
||||
return record
|
||||
while 1:
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if idBits==('\x07','\x00'): break; #we've reached the end of the structure
|
||||
elif(idBits==('\x06','\x06')):
|
||||
structName = self.strip_non_ascii(record[2::]) #(record[2:1] + record[1::]).rstrip()
|
||||
# print ''.[x for x in structName if ord(x) < 128]
|
||||
# stripped = (c for c in structName if 0 < ord(c) < 127)
|
||||
# structName = "".join(stripped)
|
||||
# print self.strip_non_ascii(structName) ##FIXME: trimming by Tom g. ##could be an issue here with string trimming!
|
||||
thisStructure.name = structName
|
||||
if(self.debugToTerminal==1):
|
||||
print "\tStructure Name: "+structName
|
||||
elif(idBits==('\x08','\x00')):
|
||||
thisStructure.boundaries+=[self.readBoundary()]
|
||||
elif(idBits==('\x09','\x00')):
|
||||
thisStructure.paths+=[self.readPath()]
|
||||
elif(idBits==('\x0A','\x00')):
|
||||
thisStructure.srefs+=[self.readSref()]
|
||||
elif(idBits==('\x0B','\x00')):
|
||||
thisStructure.arefs+=[self.readAref()]
|
||||
elif(idBits==('\x0C','\x00')):
|
||||
thisStructure.texts+=[self.readText()]
|
||||
elif(idBits==('\x15','\x00')):
|
||||
thisStructure.nodes+=[self.readNode()]
|
||||
elif(idBits==('\x2E','\x02')):
|
||||
thisStructure.boxes+=[self.readBox()]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\tEnd of Structure."
|
||||
self.layoutObject.structures[structName]=thisStructure #add this structure to the layout object
|
||||
return 1
|
||||
|
||||
def readGds2(self):
|
||||
if(self.readHeader()): #did the header read ok?
|
||||
record = self.readNextStructure()
|
||||
while(record == 1):
|
||||
record = self.readNextStructure()
|
||||
#now we have fallen out of the while, which means we are out of structures
|
||||
#so test for end of library
|
||||
if(len(record)>1):
|
||||
idBits = (record[0],record[1])
|
||||
if idBits==('\x04','\x00'): #we've reached the end of the library
|
||||
if(self.debugToTerminal==1):
|
||||
print "End of GDS Library."
|
||||
else:
|
||||
print "There was an error reading the structure list."
|
||||
else:
|
||||
print "There was an error parsing the GDS header. Aborting..."
|
||||
|
||||
def loadFromFile(self, fileName):
|
||||
self.fileHandle = open(fileName,"rb")
|
||||
self.readGds2()
|
||||
self.fileHandle.close()
|
||||
self.layoutObject.initialize()
|
||||
|
||||
##############################################
|
||||
|
||||
def findStruct(self,fileName,findStructName):
|
||||
#print"find struct"
|
||||
self.fileHandle = open(fileName,"rb")
|
||||
self.debugToTerminal=0
|
||||
if(self.readHeader()): #did the header read ok?
|
||||
record = self.findStruct_readNextStruct(findStructName)
|
||||
while(record == 1):
|
||||
record = self.findStruct_readNextStruct(findStructName)
|
||||
#now we have fallen out of the while, which means we are out of structures
|
||||
#so test for end of library
|
||||
else:
|
||||
print "There was an error parsing the GDS header. Aborting..."
|
||||
self.fileHandle.close()
|
||||
#print "End the search of",findStructName
|
||||
#self.layoutObject.initialize()
|
||||
return record
|
||||
|
||||
def findStruct_readNextStruct(self,findStructName):
|
||||
self.debugToTerminal=0
|
||||
thisStructure = GdsStructure()
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if(idBits==('\x05','\x02') and len(record)==26):
|
||||
createYear = struct.unpack(">h",record[2]+record[3])[0]
|
||||
createMonth = struct.unpack(">h",record[4]+record[5])[0]
|
||||
createDay = struct.unpack(">h",record[6]+record[7])[0]
|
||||
createHour = struct.unpack(">h",record[8]+record[9])[0]
|
||||
createMinute = struct.unpack(">h",record[10]+record[11])[0]
|
||||
createSecond = struct.unpack(">h",record[12]+record[13])[0]
|
||||
modYear = struct.unpack(">h",record[14]+record[15])[0]
|
||||
modMonth = struct.unpack(">h",record[16]+record[17])[0]
|
||||
modDay = struct.unpack(">h",record[18]+record[19])[0]
|
||||
modHour = struct.unpack(">h",record[20]+record[21])[0]
|
||||
modMinute = struct.unpack(">h",record[22]+record[23])[0]
|
||||
modSecond = struct.unpack(">h",record[24]+record[25])[0]
|
||||
thisStructure.createDate=(createYear,createMonth,createDay,createHour,createMinute,createSecond)
|
||||
thisStructure.modDate=(modYear,modMonth,modDay,modHour,modMinute,modSecond)
|
||||
else:
|
||||
#means we have hit the last structure, so return the record
|
||||
#to whoever called us to do something with it
|
||||
return record
|
||||
wantedStruct=0
|
||||
while 1:
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if idBits==('\x07','\x00'): break; #we've reached the end of the structure
|
||||
elif(idBits==('\x06','\x06')):
|
||||
structName = self.strip_non_ascii(record[2::]) #(record[2:1] + record[1::]).rstrip()
|
||||
# print ''.[x for x in structName if ord(x) < 128]
|
||||
# stripped = (c for c in structName if 0 < ord(c) < 127)
|
||||
# structName = "".join(stripped)
|
||||
# print self.strip_non_ascii(structName) ##FIXME: trimming by Tom g. ##could be an issue here with string trimming!
|
||||
thisStructure.name = structName
|
||||
if(findStructName==thisStructure.name):
|
||||
wantedStruct=1
|
||||
if(self.debugToTerminal==1):
|
||||
print "\tStructure Name: "+structName
|
||||
elif(idBits==('\x08','\x00')):
|
||||
thisStructure.boundaries+=[self.readBoundary()]
|
||||
elif(idBits==('\x09','\x00')):
|
||||
thisStructure.paths+=[self.readPath()]
|
||||
elif(idBits==('\x0A','\x00')):
|
||||
thisStructure.srefs+=[self.readSref()]
|
||||
elif(idBits==('\x0B','\x00')):
|
||||
thisStructure.arefs+=[self.readAref()]
|
||||
elif(idBits==('\x0C','\x00')):
|
||||
thisStructure.texts+=[self.readText()]
|
||||
elif(idBits==('\x15','\x00')):
|
||||
thisStructure.nodes+=[self.readNode()]
|
||||
elif(idBits==('\x2E','\x02')):
|
||||
thisStructure.boxes+=[self.readBox()]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\tEnd of Structure."
|
||||
self.layoutObject.structures[structName]=thisStructure #add this structure to the layout object
|
||||
if(wantedStruct == 0):
|
||||
return 1
|
||||
else:
|
||||
#print "\tDone with collectting bound. Return"
|
||||
return [0,thisStructure.boundaries]
|
||||
|
||||
def findLabel(self,fileName,findLabelName):
|
||||
#print"find Label"
|
||||
self.fileHandle = open(fileName,"rb")
|
||||
self.debugToTerminal=0
|
||||
if(self.readHeader()): #did the header read ok?
|
||||
record = self.findLabel_readNextStruct(findLabelName)
|
||||
while(record == 1):
|
||||
record = self.findLabel_readNextStruct(findLabelName)
|
||||
#now we have fallen out of the while, which means we are out of structures
|
||||
#so test for end of library
|
||||
else:
|
||||
print "There was an error parsing the GDS header. Aborting..."
|
||||
self.fileHandle.close()
|
||||
#print "End the search of",findStructName
|
||||
#self.layoutObject.initialize()
|
||||
return record
|
||||
|
||||
def findLabel_readNextStruct(self,findLabelName):
|
||||
self.debugToTerminal=0
|
||||
thisStructure = GdsStructure()
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if(idBits==('\x05','\x02') and len(record)==26):
|
||||
createYear = struct.unpack(">h",record[2]+record[3])[0]
|
||||
createMonth = struct.unpack(">h",record[4]+record[5])[0]
|
||||
createDay = struct.unpack(">h",record[6]+record[7])[0]
|
||||
createHour = struct.unpack(">h",record[8]+record[9])[0]
|
||||
createMinute = struct.unpack(">h",record[10]+record[11])[0]
|
||||
createSecond = struct.unpack(">h",record[12]+record[13])[0]
|
||||
modYear = struct.unpack(">h",record[14]+record[15])[0]
|
||||
modMonth = struct.unpack(">h",record[16]+record[17])[0]
|
||||
modDay = struct.unpack(">h",record[18]+record[19])[0]
|
||||
modHour = struct.unpack(">h",record[20]+record[21])[0]
|
||||
modMinute = struct.unpack(">h",record[22]+record[23])[0]
|
||||
modSecond = struct.unpack(">h",record[24]+record[25])[0]
|
||||
thisStructure.createDate=(createYear,createMonth,createDay,createHour,createMinute,createSecond)
|
||||
thisStructure.modDate=(modYear,modMonth,modDay,modHour,modMinute,modSecond)
|
||||
else:
|
||||
#means we have hit the last structure, so return the record
|
||||
#to whoever called us to do something with it
|
||||
return record
|
||||
wantedLabel=0
|
||||
wantedtexts=[GdsText()]
|
||||
while 1:
|
||||
record = self.readNextRecord()
|
||||
idBits = (record[0],record[1])
|
||||
if idBits==('\x07','\x00'): break; #we've reached the end of the structure
|
||||
elif(idBits==('\x06','\x06')):
|
||||
structName = self.strip_non_ascii(record[2::]) #(record[2:1] + record[1::]).rstrip()
|
||||
# print ''.[x for x in structName if ord(x) < 128]
|
||||
# stripped = (c for c in structName if 0 < ord(c) < 127)
|
||||
# structName = "".join(stripped)
|
||||
# print self.strip_non_ascii(structName) ##FIXME: trimming by Tom g. ##could be an issue here with string trimming!
|
||||
thisStructure.name = structName
|
||||
if(self.debugToTerminal==1):
|
||||
print "\tStructure Name: "+structName
|
||||
elif(idBits==('\x08','\x00')):
|
||||
thisStructure.boundaries+=[self.readBoundary()]
|
||||
elif(idBits==('\x09','\x00')):
|
||||
thisStructure.paths+=[self.readPath()]
|
||||
elif(idBits==('\x0A','\x00')):
|
||||
thisStructure.srefs+=[self.readSref()]
|
||||
elif(idBits==('\x0B','\x00')):
|
||||
thisStructure.arefs+=[self.readAref()]
|
||||
elif(idBits==('\x0C','\x00')):
|
||||
label=self.readText()
|
||||
#Be careful: label.textString contains one space string in it. Delete that one before use it
|
||||
if( findLabelName == label.textString[0:(len(label.textString)-1)] ):
|
||||
wantedLabel=1
|
||||
# BINWU: Cleanup
|
||||
#print"Find the Label",findLabelName
|
||||
wantedtexts+=[label]
|
||||
thisStructure.texts+=[label]
|
||||
if(self.debugToTerminal == 1):
|
||||
print label.textString[0:(len(label.textString)-1)],findLabelName,( findLabelName == label.textString[0:(len(label.textString)-1)] )
|
||||
# BINWU: Cleanup
|
||||
#print thisStructure.name
|
||||
#print thisStructure.texts
|
||||
elif(idBits==('\x15','\x00')):
|
||||
thisStructure.nodes+=[self.readNode()]
|
||||
elif(idBits==('\x2E','\x02')):
|
||||
thisStructure.boxes+=[self.readBox()]
|
||||
if(self.debugToTerminal==1):
|
||||
print "\tEnd of Structure."
|
||||
self.layoutObject.structures[structName]=thisStructure #add this structure to the layout object
|
||||
if(wantedLabel == 0):
|
||||
return 1
|
||||
else:
|
||||
#print "\tDone with collectting bound. Return"
|
||||
return [0,wantedtexts]
|
||||
|
||||
|
|
@ -0,0 +1,540 @@
|
|||
#!/usr/bin/env python
|
||||
import struct
|
||||
from gdsPrimitives import *
|
||||
|
||||
class Gds2writer:
|
||||
"""Class to take a populated layout class and write it to a file in GDSII format"""
|
||||
## Based on info from http://www.rulabinsky.com/cavd/text/chapc.html
|
||||
|
||||
def __init__(self,layoutObject):
|
||||
self.fileHandle = 0
|
||||
self.layoutObject = layoutObject
|
||||
self.debugToTerminal=0 #do we dump debug data to the screen
|
||||
|
||||
def print64AsBinary(self,number):
|
||||
#debugging method for binary inspection
|
||||
for index in range(0,64):
|
||||
print (number>>(63-index))&0x1,
|
||||
print "\n"
|
||||
|
||||
def ieeeDoubleFromIbmData(self,ibmData):
|
||||
#the GDS double is in IBM 370 format like this:
|
||||
#(1)sign (7)exponent (56)mantissa
|
||||
#exponent is excess 64, mantissa has no implied 1
|
||||
#a normal IEEE double is like this:
|
||||
#(1)sign (11)exponent (52)mantissa
|
||||
data = struct.unpack('>q',ibmData)[0]
|
||||
sign = (data >> 63)&0x01
|
||||
exponent = (data >> 56) & 0x7f
|
||||
mantissa = data<<8 #chop off sign and exponent
|
||||
|
||||
if mantissa == 0:
|
||||
newFloat = 0.0
|
||||
else:
|
||||
exponent = ((exponent-64)*4)+1023 #convert to double exponent
|
||||
#re normalize
|
||||
while mantissa & 0x8000000000000000 == 0:
|
||||
mantissa<<=1
|
||||
exponent-=1
|
||||
mantissa<<=1 #remove the assumed high bit
|
||||
exponent-=1
|
||||
#check for underflow error -- should handle these properly!
|
||||
if(exponent<=0):
|
||||
print "Underflow Error"
|
||||
elif(exponent == 2047):
|
||||
print "Overflow Error"
|
||||
#re assemble
|
||||
newFloat=(sign<<63)|(exponent<<52)|((mantissa>>12)&0xfffffffffffff)
|
||||
asciiDouble = struct.pack('>q',newFloat)
|
||||
#convert back to double
|
||||
newFloat = struct.unpack('>d',asciiDouble)[0]
|
||||
return newFloat
|
||||
|
||||
def ibmDataFromIeeeDouble(self,ieeeDouble):
|
||||
asciiDouble = struct.pack('>d',ieeeDouble)
|
||||
data = struct.unpack('>q',asciiDouble)[0]
|
||||
sign = (data >> 63) & 0x01
|
||||
exponent = ((data >> 52) & 0x7ff)-1023
|
||||
mantissa = data << 12 #chop off sign and exponent
|
||||
if(ieeeDouble == 0):
|
||||
mantissa = 0
|
||||
exponent = 0
|
||||
sign = 0
|
||||
else:
|
||||
#add back the assumed digit
|
||||
mantissa >>= 1
|
||||
mantissa = mantissa|0x8000000000000000
|
||||
exponent += 1
|
||||
#convert the exponent
|
||||
#need to do this in a loop to prevent sign extension!
|
||||
for index in range (0,-exponent&3):
|
||||
mantissa >>= 1
|
||||
mantissa = mantissa & 0x7fffffffffffffff
|
||||
|
||||
exponent = (exponent+3) >> 2
|
||||
exponent+=64
|
||||
|
||||
newFloat =(sign<<63)|(exponent<<56)|((mantissa>>8)&0xffffffffffffff)
|
||||
asciiDouble = struct.pack('>q',newFloat)
|
||||
return asciiDouble
|
||||
|
||||
def ieeeFloatCheck(self,aFloat):
|
||||
#debugging method for float construction
|
||||
asciiDouble = struct.pack('>d',aFloat)
|
||||
data = struct.unpack('>q',asciiDouble)[0]
|
||||
sign = data >> 63
|
||||
exponent = ((data >> 52) & 0x7ff)-1023
|
||||
print exponent+1023
|
||||
mantissa = data << 12 #chop off sign and exponent
|
||||
#self.print64AsBinary((sign<<63)|((exponent+1023)<<52)|(mantissa>>12))
|
||||
asciiDouble = struct.pack('>q',(sign<<63)|(exponent+1023<<52)|(mantissa>>12))
|
||||
newFloat = struct.unpack('>d',asciiDouble)[0]
|
||||
print "Check:"+str(newFloat)
|
||||
|
||||
def writeRecord(self,record):
|
||||
recordLength = len(record)+2 #make sure to include this in the length
|
||||
recordLengthAscii=struct.pack(">h",recordLength)
|
||||
self.fileHandle.write(recordLengthAscii+record)
|
||||
|
||||
def writeHeader(self):
|
||||
## Header
|
||||
if("gdsVersion" in self.layoutObject.info):
|
||||
idBits='\x00\x02'
|
||||
gdsVersion = struct.pack(">h",self.layoutObject.info["gdsVersion"])
|
||||
self.writeRecord(idBits+gdsVersion)
|
||||
## Modified Date
|
||||
if("dates" in self.layoutObject.info):
|
||||
idBits='\x01\x02'
|
||||
modYear = struct.pack(">h",self.layoutObject.info["dates"][0])
|
||||
modMonth = struct.pack(">h",self.layoutObject.info["dates"][1])
|
||||
modDay = struct.pack(">h",self.layoutObject.info["dates"][2])
|
||||
modHour = struct.pack(">h",self.layoutObject.info["dates"][3])
|
||||
modMinute = struct.pack(">h",self.layoutObject.info["dates"][4])
|
||||
modSecond = struct.pack(">h",self.layoutObject.info["dates"][5])
|
||||
lastAccessYear = struct.pack(">h",self.layoutObject.info["dates"][6])
|
||||
lastAccessMonth = struct.pack(">h",self.layoutObject.info["dates"][7])
|
||||
lastAccessDay = struct.pack(">h",self.layoutObject.info["dates"][8])
|
||||
lastAccessHour = struct.pack(">h",self.layoutObject.info["dates"][9])
|
||||
lastAccessMinute = struct.pack(">h",self.layoutObject.info["dates"][10])
|
||||
lastAccessSecond = struct.pack(">h",self.layoutObject.info["dates"][11])
|
||||
self.writeRecord(idBits+modYear+modMonth+modDay+modHour+modMinute+modSecond+\
|
||||
lastAccessYear+lastAccessMonth+lastAccessDay+lastAccessHour+\
|
||||
lastAccessMinute+lastAccessSecond)
|
||||
## LibraryName
|
||||
if("libraryName" in self.layoutObject.info):
|
||||
idBits='\x02\x06'
|
||||
libraryName = self.layoutObject.info["libraryName"]
|
||||
self.writeRecord(idBits+libraryName)
|
||||
## reference libraries
|
||||
if("referenceLibraries" in self.layoutObject.info):
|
||||
idBits='\x1F\x06'
|
||||
referenceLibraryA = self.layoutObject.info["referenceLibraries"][0]
|
||||
referenceLibraryB = self.layoutObject.info["referenceLibraries"][1]
|
||||
self.writeRecord(idBits+referenceLibraryA+referenceLibraryB)
|
||||
if("fonts" in self.layoutObject.info):
|
||||
idBits='\x20\x06'
|
||||
fontA = self.layoutObject.info["fonts"][0]
|
||||
fontB = self.layoutObject.info["fonts"][1]
|
||||
fontC = self.layoutObject.info["fonts"][2]
|
||||
fontD = self.layoutObject.info["fonts"][3]
|
||||
self.writeRecord(idBits+fontA+fontB+fontC+fontD)
|
||||
if("attributeTable" in self.layoutObject.info):
|
||||
idBits='\x23\x06'
|
||||
attributeTable = self.layoutObject.info["attributeTable"]
|
||||
self.writeRecord(idBits+attributeTable)
|
||||
if("generations" in self.layoutObject.info):
|
||||
idBits='\x22\x02'
|
||||
generations = struct.pack(">h",self.layoutObject.info["generations"])
|
||||
self.writeRecord(idBits+generations)
|
||||
if("fileFormat" in self.layoutObject.info):
|
||||
idBits='\x36\x02'
|
||||
fileFormat = struct.pack(">h",self.layoutObject.info["fileFormat"])
|
||||
self.writeRecord(idBits+fileFormat)
|
||||
if("mask" in self.layoutObject.info):
|
||||
idBits='\x37\x06'
|
||||
mask = self.layoutObject.info["mask"]
|
||||
self.writeRecord(idBits+mask)
|
||||
if("units" in self.layoutObject.info):
|
||||
idBits='\x03\x05'
|
||||
userUnits=self.ibmDataFromIeeeDouble(self.layoutObject.info["units"][0])
|
||||
dbUnits=self.ibmDataFromIeeeDouble((self.layoutObject.info["units"][0]*1e-6/self.layoutObject.info["units"][1])*self.layoutObject.info["units"][1])
|
||||
|
||||
#User Units are hardcoded, since the floating point implementation of gdsMill is not adequate,
|
||||
#resulting in a different value being written in output stream. Hardcoded to sram compiler's outputed gds units.
|
||||
#db="39225c17d04dad2a"
|
||||
#uu="3e20c49ba5e353f8"
|
||||
|
||||
#userUnits="3e20c49ba5e353f8".decode("hex")
|
||||
#dbUnits="39225c17d04dad2a".decode("hex")
|
||||
|
||||
#dbUnits="39225c17d04dad2a".decode("hex")
|
||||
#db=39225c17d04dad2a
|
||||
|
||||
|
||||
self.writeRecord(idBits+userUnits+dbUnits)
|
||||
if(self.debugToTerminal==1):
|
||||
print "writer: userUnits %s"%(userUnits.encode("hex"))
|
||||
print "writer: dbUnits %s"%(dbUnits.encode("hex"))
|
||||
#self.ieeeFloatCheck(1.3e-6)
|
||||
|
||||
print "End of GDSII Header Written"
|
||||
return 1
|
||||
|
||||
def writeBoundary(self,thisBoundary):
|
||||
idBits = '\x08\x00' #record Type
|
||||
self.writeRecord(idBits)
|
||||
if(thisBoundary.elementFlags!=""):
|
||||
idBits='\x26\x01' #ELFLAGS
|
||||
elementFlags = struct.pack(">h",thisBoundary.elementFlags)
|
||||
self.writeRecord(idBits+elementFlags)
|
||||
if(thisBoundary.plex!=""):
|
||||
idBits='\x2F\x03' #PLEX
|
||||
plex = struct.pack(">i",thisBoundary.plex)
|
||||
self.writeRecord(idBits+plex)
|
||||
if(thisBoundary.drawingLayer!=""):
|
||||
idBits='\x0D\x02' #drawig layer
|
||||
drawingLayer = struct.pack(">h",thisBoundary.drawingLayer)
|
||||
self.writeRecord(idBits+drawingLayer)
|
||||
if(thisBoundary.purposeLayer):
|
||||
idBits='\x16\x02' #purpose layer
|
||||
purposeLayer = struct.pack(">h",thisBoundary.purposeLayer)
|
||||
self.writeRecord(idBits+purposeLayer)
|
||||
if(thisBoundary.dataType!=""):
|
||||
idBits='\x0E\x02'#DataType
|
||||
dataType = struct.pack(">h",thisBoundary.dataType)
|
||||
self.writeRecord(idBits+dataType)
|
||||
if(thisBoundary.coordinates!=""):
|
||||
idBits='\x10\x03' #XY Data Points
|
||||
coordinateRecord = idBits
|
||||
for coordinate in thisBoundary.coordinates:
|
||||
x=struct.pack(">i",coordinate[0])
|
||||
y=struct.pack(">i",coordinate[1])
|
||||
coordinateRecord+=x
|
||||
coordinateRecord+=y
|
||||
self.writeRecord(coordinateRecord)
|
||||
idBits='\x11\x00' #End Of Element
|
||||
coordinateRecord = idBits
|
||||
self.writeRecord(coordinateRecord)
|
||||
|
||||
def writePath(self,thisPath): #writes out a path structure
|
||||
idBits = '\x09\x00' #record Type
|
||||
self.writeRecord(idBits)
|
||||
if(thisPath.elementFlags != ""):
|
||||
idBits='\x26\x01' #ELFLAGS
|
||||
elementFlags = struct.pack(">h",thisPath.elementFlags)
|
||||
self.writeRecord(idBits+elementFlags)
|
||||
if(thisPath.plex!=""):
|
||||
idBits='\x2F\x03' #PLEX
|
||||
plex = struct.pack(">i",thisPath.plex)
|
||||
self.writeRecord(idBits+plex)
|
||||
if(thisPath.drawingLayer):
|
||||
idBits='\x0D\x02' #drawig layer
|
||||
drawingLayer = struct.pack(">h",thisPath.drawingLayer)
|
||||
self.writeRecord(idBits+drawingLayer)
|
||||
if(thisPath.purposeLayer):
|
||||
idBits='\x16\x02' #purpose layer
|
||||
purposeLayer = struct.pack(">h",thisPath.purposeLayer)
|
||||
self.writeRecord(idBits+purposeLayer)
|
||||
if(thisPath.pathType):
|
||||
idBits='\x21\x02' #Path type
|
||||
pathType = struct.pack(">h",thisPath.pathType)
|
||||
self.writeRecord(idBits+pathType)
|
||||
if(thisPath.pathWidth):
|
||||
idBits='\x0F\x03'
|
||||
pathWidth = struct.pack(">i",thisPath.pathWidth)
|
||||
self.writeRecord(idBits+pathWidth)
|
||||
if(thisPath.coordinates):
|
||||
idBits='\x10\x03' #XY Data Points
|
||||
coordinateRecord = idBits
|
||||
for coordinate in thisPath.coordinates:
|
||||
x=struct.pack(">i",coordinate[0])
|
||||
y=struct.pack(">i",coordinate[1])
|
||||
coordinateRecord+=x
|
||||
coordinateRecord+=y
|
||||
self.writeRecord(coordinateRecord)
|
||||
idBits='\x11\x00' #End Of Element
|
||||
coordinateRecord = idBits
|
||||
self.writeRecord(coordinateRecord)
|
||||
|
||||
def writeSref(self,thisSref): #reads in a reference to another structure
|
||||
idBits = '\x0A\x00' #record Type
|
||||
self.writeRecord(idBits)
|
||||
if(thisSref.elementFlags != ""):
|
||||
idBits='\x26\x01' #ELFLAGS
|
||||
elementFlags = struct.pack(">h",thisSref.elementFlags)
|
||||
self.writeRecord(idBits+elementFlags)
|
||||
if(thisSref.plex!=""):
|
||||
idBits='\x2F\x03' #PLEX
|
||||
plex = struct.pack(">i",thisSref.plex)
|
||||
self.writeRecord(idBits+plex)
|
||||
if(thisSref.sName!=""):
|
||||
idBits='\x12\x06'
|
||||
sName = thisSref.sName
|
||||
self.writeRecord(idBits+sName)
|
||||
if(thisSref.transFlags!=""):
|
||||
idBits='\x1A\x01'
|
||||
mirrorFlag = int(thisSref.transFlags[0])<<15
|
||||
rotateFlag = int(thisSref.transFlags[1])<<1
|
||||
magnifyFlag = int(thisSref.transFlags[2])<<3
|
||||
transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag)
|
||||
self.writeRecord(idBits+transFlags)
|
||||
if(thisSref.magFactor!=""):
|
||||
idBits='\x1B\x05'
|
||||
magFactor=self.ibmDataFromIeeeDouble(thisSref.magFactor)
|
||||
self.writeRecord(idBits+magFactor)
|
||||
if(thisSref.rotateAngle!=""):
|
||||
idBits='\x1C\x05'
|
||||
rotateAngle=self.ibmDataFromIeeeDouble(thisSref.rotateAngle)
|
||||
self.writeRecord(idBits+rotateAngle)
|
||||
if(thisSref.coordinates!=""):
|
||||
idBits='\x10\x03' #XY Data Points
|
||||
coordinateRecord = idBits
|
||||
coordinate = thisSref.coordinates
|
||||
x=struct.pack(">i",coordinate[0])
|
||||
y=struct.pack(">i",coordinate[1])
|
||||
coordinateRecord+=x
|
||||
coordinateRecord+=y
|
||||
#print thisSref.coordinates
|
||||
self.writeRecord(coordinateRecord)
|
||||
idBits='\x11\x00' #End Of Element
|
||||
coordinateRecord = idBits
|
||||
self.writeRecord(coordinateRecord)
|
||||
|
||||
def writeAref(self,thisAref): #an array of references
|
||||
idBits = '\x0B\x00' #record Type
|
||||
self.writeRecord(idBits)
|
||||
if(thisAref.elementFlags!=""):
|
||||
idBits='\x26\x01' #ELFLAGS
|
||||
elementFlags = struct.pack(">h",thisAref.elementFlags)
|
||||
self.writeRecord(idBits+elementFlags)
|
||||
if(thisAref.plex):
|
||||
idBits='\x2F\x03' #PLEX
|
||||
plex = struct.pack(">i",thisAref.plex)
|
||||
self.writeRecord(idBits+plex)
|
||||
if(thisAref.aName):
|
||||
idBits='\x12\x06'
|
||||
aName = thisAref.aName
|
||||
self.writeRecord(idBits+aName)
|
||||
if(thisAref.transFlags):
|
||||
idBits='\x1A\x01'
|
||||
mirrorFlag = int(thisAref.transFlags[0])<<15
|
||||
rotateFlag = int(thisAref.transFlags[1])<<1
|
||||
magnifyFlag = int(thisAref.transFlags[0])<<3
|
||||
transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag)
|
||||
self.writeRecord(idBits+transFlags)
|
||||
if(thisAref.magFactor):
|
||||
idBits='\x1B\x05'
|
||||
magFactor=self.ibmDataFromIeeeDouble(thisAref.magFactor)
|
||||
self.writeRecord(idBits+magFactor)
|
||||
if(thisAref.rotateAngle):
|
||||
idBits='\x1C\x05'
|
||||
rotateAngle=self.ibmDataFromIeeeDouble(thisAref.rotateAngle)
|
||||
self.writeRecord(idBits+rotateAngle)
|
||||
if(thisAref.coordinates):
|
||||
idBits='\x10\x03' #XY Data Points
|
||||
coordinateRecord = idBits
|
||||
for coordinate in thisAref.coordinates:
|
||||
x=struct.pack(">i",coordinate[0])
|
||||
y=struct.pack(">i",coordinate[1])
|
||||
coordinateRecord+=x
|
||||
coordinateRecord+=y
|
||||
self.writeRecord(coordinateRecord)
|
||||
idBits='\x11\x00' #End Of Element
|
||||
coordinateRecord = idBits
|
||||
self.writeRecord(coordinateRecord)
|
||||
|
||||
def writeText(self,thisText):
|
||||
idBits = '\x0C\x00' #record Type
|
||||
self.writeRecord(idBits)
|
||||
if(thisText.elementFlags!=""):
|
||||
idBits='\x26\x01' #ELFLAGS
|
||||
elementFlags = struct.pack(">h",thisText.elementFlags)
|
||||
self.writeRecord(idBits+elementFlags)
|
||||
if(thisText.plex !=""):
|
||||
idBits='\x2F\x03' #PLEX
|
||||
plex = struct.pack(">i",thisText.plex)
|
||||
self.writeRecord(idBits+plex)
|
||||
if(thisText.drawingLayer != ""):
|
||||
idBits='\x0D\x02' #drawing layer
|
||||
drawingLayer = struct.pack(">h",thisText.drawingLayer)
|
||||
self.writeRecord(idBits+drawingLayer)
|
||||
#if(thisText.purposeLayer):
|
||||
idBits='\x16\x02' #purpose layer
|
||||
purposeLayer = struct.pack(">h",thisText.purposeLayer)
|
||||
self.writeRecord(idBits+purposeLayer)
|
||||
if(thisText.transFlags != ""):
|
||||
idBits='\x1A\x01'
|
||||
mirrorFlag = int(thisText.transFlags[0])<<15
|
||||
rotateFlag = int(thisText.transFlags[1])<<1
|
||||
magnifyFlag = int(thisText.transFlags[0])<<3
|
||||
transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag)
|
||||
self.writeRecord(idBits+transFlags)
|
||||
if(thisText.magFactor != ""):
|
||||
idBits='\x1B\x05'
|
||||
magFactor=self.ibmDataFromIeeeDouble(thisText.magFactor)
|
||||
self.writeRecord(idBits+magFactor)
|
||||
if(thisText.rotateAngle != ""):
|
||||
idBits='\x1C\x05'
|
||||
rotateAngle=self.ibmDataFromIeeeDouble(thisText.rotateAngle)
|
||||
self.writeRecord(idBits+rotateAngle)
|
||||
if(thisText.pathType !=""):
|
||||
idBits='\x21\x02' #Path type
|
||||
pathType = struct.pack(">h",thisText.pathType)
|
||||
self.writeRecord(idBits+pathType)
|
||||
if(thisText.pathWidth != ""):
|
||||
idBits='\x0F\x03'
|
||||
pathWidth = struct.pack(">i",thisText.pathWidth)
|
||||
self.writeRecord(idBits+pathWidth)
|
||||
if(thisText.presentationFlags!=""):
|
||||
idBits='\x1A\x01'
|
||||
font = thisText.presentationFlags[0]<<4
|
||||
verticalFlags = int(thisText.presentationFlags[1])<<2
|
||||
horizontalFlags = int(thisText.presentationFlags[2])
|
||||
presentationFlags = struct.pack(">H",font|verticalFlags|horizontalFlags)
|
||||
self.writeRecord(idBits+transFlags)
|
||||
if(thisText.coordinates!=""):
|
||||
idBits='\x10\x03' #XY Data Points
|
||||
coordinateRecord = idBits
|
||||
for coordinate in thisText.coordinates:
|
||||
x=struct.pack(">i",coordinate[0])
|
||||
y=struct.pack(">i",coordinate[1])
|
||||
coordinateRecord+=x
|
||||
coordinateRecord+=y
|
||||
self.writeRecord(coordinateRecord)
|
||||
if(thisText.textString):
|
||||
idBits='\x19\x06'
|
||||
textString = thisText.textString
|
||||
self.writeRecord(idBits+textString)
|
||||
|
||||
idBits='\x11\x00' #End Of Element
|
||||
coordinateRecord = idBits
|
||||
self.writeRecord(coordinateRecord)
|
||||
|
||||
def writeNode(self,thisNode):
|
||||
idBits = '\x15\x00' #record Type
|
||||
self.writeRecord(idBits)
|
||||
if(thisNode.elementFlags!=""):
|
||||
idBits='\x26\x01' #ELFLAGS
|
||||
elementFlags = struct.pack(">h",thisNode.elementFlags)
|
||||
self.writeRecord(idBits+elementFlags)
|
||||
if(thisNode.plex!=""):
|
||||
idBits='\x2F\x03' #PLEX
|
||||
plex = struct.pack(">i",thisNode.plex)
|
||||
self.writeRecord(idBits+plex)
|
||||
if(thisNode.drawingLayer!=""):
|
||||
idBits='\x0D\x02' #drawig layer
|
||||
drawingLayer = struct.pack(">h",thisNode.drawingLayer)
|
||||
self.writeRecord(idBits+drawingLayer)
|
||||
if(thisNode.nodeType!=""):
|
||||
idBits='\x2A\x02'
|
||||
nodeType = struct.pack(">h",thisNode.nodeType)
|
||||
self.writeRecord(idBits+nodeType)
|
||||
if(thisText.coordinates!=""):
|
||||
idBits='\x10\x03' #XY Data Points
|
||||
coordinateRecord = idBits
|
||||
for coordinate in thisText.coordinates:
|
||||
x=struct.pack(">i",coordinate[0])
|
||||
y=struct.pack(">i",coordinate[1])
|
||||
coordinateRecord+=x
|
||||
coordinateRecord+=y
|
||||
self.writeRecord(coordinateRecord)
|
||||
|
||||
idBits='\x11\x00' #End Of Element
|
||||
coordinateRecord = idBits
|
||||
self.writeRecord(coordinateRecord)
|
||||
|
||||
def writeBox(self,thisBox):
|
||||
idBits = '\x2E\x02' #record Type
|
||||
self.writeRecord(idBits)
|
||||
if(thisBox.elementFlags!=""):
|
||||
idBits='\x26\x01' #ELFLAGS
|
||||
elementFlags = struct.pack(">h",thisBox.elementFlags)
|
||||
self.writeRecord(idBits+elementFlags)
|
||||
if(thisBox.plex!=""):
|
||||
idBits='\x2F\x03' #PLEX
|
||||
plex = struct.pack(">i",thisBox.plex)
|
||||
self.writeRecord(idBits+plex)
|
||||
if(thisBox.drawingLayer!=""):
|
||||
idBits='\x0D\x02' #drawig layer
|
||||
drawingLayer = struct.pack(">h",thisBox.drawingLayer)
|
||||
self.writeRecord(idBits+drawingLayer)
|
||||
if(thisBox.purposeLayer):
|
||||
idBits='\x16\x02' #purpose layer
|
||||
purposeLayer = struct.pack(">h",thisBox.purposeLayer)
|
||||
self.writeRecord(idBits+purposeLayer)
|
||||
if(thisBox.boxValue!=""):
|
||||
idBits='\x2D\x00'
|
||||
boxValue = struct.pack(">h",thisBox.boxValue)
|
||||
self.writeRecord(idBits+boxValue)
|
||||
if(thisBox.coordinates!=""):
|
||||
idBits='\x10\x03' #XY Data Points
|
||||
coordinateRecord = idBits
|
||||
for coordinate in thisBox.coordinates:
|
||||
x=struct.pack(">i",coordinate[0])
|
||||
y=struct.pack(">i",coordinate[1])
|
||||
coordinateRecord+=x
|
||||
coordinateRecord+=y
|
||||
self.writeRecord(coordinateRecord)
|
||||
|
||||
idBits='\x11\x00' #End Of Element
|
||||
coordinateRecord = idBits
|
||||
self.writeRecord(coordinateRecord)
|
||||
|
||||
def writeNextStructure(self,structureName):
|
||||
#first put in the structure head
|
||||
thisStructure = self.layoutObject.structures[structureName]
|
||||
idBits='\x05\x02'
|
||||
createYear = struct.pack(">h",thisStructure.createDate[0])
|
||||
createMonth = struct.pack(">h",thisStructure.createDate[1])
|
||||
createDay = struct.pack(">h",thisStructure.createDate[2])
|
||||
createHour = struct.pack(">h",thisStructure.createDate[3])
|
||||
createMinute = struct.pack(">h",thisStructure.createDate[4])
|
||||
createSecond = struct.pack(">h",thisStructure.createDate[5])
|
||||
modYear = struct.pack(">h",thisStructure.modDate[0])
|
||||
modMonth = struct.pack(">h",thisStructure.modDate[1])
|
||||
modDay = struct.pack(">h",thisStructure.modDate[2])
|
||||
modHour = struct.pack(">h",thisStructure.modDate[3])
|
||||
modMinute = struct.pack(">h",thisStructure.modDate[4])
|
||||
modSecond = struct.pack(">h",thisStructure.modDate[5])
|
||||
self.writeRecord(idBits+createYear+createMonth+createDay+createHour+createMinute+createSecond\
|
||||
+modYear+modMonth+modDay+modHour+modMinute+modSecond)
|
||||
#now the structure name
|
||||
idBits='\x06\x06'
|
||||
##caveat: the name needs to be an EVEN number of characters
|
||||
if(len(structureName)%2 == 1):
|
||||
#pad with a zero
|
||||
structureName = structureName + '\x00'
|
||||
self.writeRecord(idBits+structureName)
|
||||
#now go through all the structure elements and write them in
|
||||
|
||||
for boundary in thisStructure.boundaries:
|
||||
self.writeBoundary(boundary)
|
||||
for path in thisStructure.paths:
|
||||
self.writePath(path)
|
||||
for sref in thisStructure.srefs:
|
||||
self.writeSref(sref)
|
||||
for aref in thisStructure.arefs:
|
||||
self.writeAref(aref)
|
||||
for text in thisStructure.texts:
|
||||
self.writeText(text)
|
||||
for node in thisStructure.nodes:
|
||||
self.writeNode(node)
|
||||
for box in thisStructure.boxes:
|
||||
self.writeBox(box)
|
||||
#put in the structure tail
|
||||
idBits='\x07\x00'
|
||||
self.writeRecord(idBits)
|
||||
|
||||
def writeGds2(self):
|
||||
self.writeHeader(); #first, put the header in
|
||||
#go through each structure in the layout and write it to the file
|
||||
for structureName in self.layoutObject.structures:
|
||||
self.writeNextStructure(structureName)
|
||||
#at the end, put in the END LIB record
|
||||
idBits='\x04\x00'
|
||||
self.writeRecord(idBits)
|
||||
|
||||
def writeToFile(self,fileName):
|
||||
self.fileHandle = open(fileName,"wb")
|
||||
self.writeGds2()
|
||||
self.fileHandle.close()
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
import math
|
||||
|
||||
class GdsStructure:
|
||||
"""Class represent a GDS Structure Object"""
|
||||
def __init__(self):
|
||||
self.name=""
|
||||
self.createDate=()
|
||||
self.modDate=()
|
||||
#these are the primitives defined in GDS2, and we will maintain lists of them all
|
||||
self.boundaries=[]
|
||||
self.paths=[]
|
||||
self.srefs=[]
|
||||
self.arefs=[]
|
||||
self.texts=[]
|
||||
self.nodes=[]
|
||||
self.boxes=[]
|
||||
|
||||
class GdsBoundary:
|
||||
"""Class represent a GDS Boundary Object"""
|
||||
def __init__(self):
|
||||
self.elementFlags=""
|
||||
self.plex=""
|
||||
self.drawingLayer=""
|
||||
self.purposeLayer = None
|
||||
self.dataType=""
|
||||
self.coordinates=""
|
||||
|
||||
class GdsPath:
|
||||
"""Class represent a GDS Path Object"""
|
||||
def __init__(self):
|
||||
self.elementFlags=""
|
||||
self.plex=""
|
||||
self.drawingLayer=""
|
||||
self.purposeLayer = None
|
||||
self.pathType=""
|
||||
self.pathWidth=""
|
||||
self.coordinates=""
|
||||
|
||||
def equivalentBoundaryCoordinates(self):
|
||||
"""Convert the path to a set of boundary coordinates that define it"""
|
||||
halfWidth = (self.pathWidth/2)
|
||||
lastX = lastY = None #start of the path
|
||||
#go through every point
|
||||
#always draw on the "left side of the line"
|
||||
#this way, we can append two copies of the coordinates and just trace in order
|
||||
# i.e. coordinates are (A,B,C,D) - we just make a new array (A,B,C,D,C,B,A) and trace with a fixed offset to the "left"
|
||||
coordinatesCopy = self.coordinates[:]
|
||||
coordinatesCopy.reverse()
|
||||
coordinates=self.coordinates[:]+coordinatesCopy
|
||||
boundaryEquivalent = []
|
||||
#create the first point
|
||||
x=(coordinates[0][0])
|
||||
y=(coordinates[0][1])
|
||||
boundaryEquivalent += [(x, y)]
|
||||
for index in range(0,len(coordinates)):
|
||||
x=(coordinates[index][0])
|
||||
y=(coordinates[index][1])
|
||||
if index < len(coordinates)-1:
|
||||
nextX=(coordinates[index+1][0])
|
||||
nextY=(coordinates[index+1][1])
|
||||
else: #end of the path b/c no next points
|
||||
nextX = None;
|
||||
nextY = None;
|
||||
if lastX==None: #start of the path
|
||||
if nextX>x:#moving right
|
||||
boundaryEquivalent+=[(x,y+halfWidth)]
|
||||
if nextX<x:#moving left
|
||||
boundaryEquivalent+=[(x,y-halfWidth)]
|
||||
if nextY>y:#moving up
|
||||
boundaryEquivalent+=[(x-halfWidth,y)]
|
||||
if nextY<y:#moving down
|
||||
boundaryEquivalent+=[(x+halfWidth,y)]
|
||||
if (nextX != None) and (lastX!=None): #somewhere in the middle
|
||||
if (x == lastX and nextX == x) and ((lastY < y) or (nextY > y)): #vertical line up
|
||||
boundaryEquivalent+=[(x-halfWidth,y)]
|
||||
if (x == lastX and nextX == x) and ((lastY > y) or (nextY < y)): #vertical line down
|
||||
boundaryEquivalent+=[(x+halfWidth,y)]
|
||||
if (y == lastY and nextY == y) and ((lastX < x) or (nextX > x)): #horizontal line right
|
||||
boundaryEquivalent+=[(x,y+halfWidth)]
|
||||
if (y == lastY and nextY == y) and ((lastX > x) or (nextX < x)): #horizontal line left
|
||||
boundaryEquivalent+=[(x,y-halfWidth)]
|
||||
###### TAKE CARE OF THE CORNERS / BENDS HERE - there are 8 types of corners (4 angles * 2 directions)
|
||||
if(y < nextY and x < lastX):
|
||||
boundaryEquivalent+=[(x-halfWidth,y-halfWidth)]
|
||||
if(y < lastY and x < nextX):
|
||||
boundaryEquivalent+=[(x+halfWidth,y+halfWidth)]
|
||||
if(y > nextY and x < lastX):
|
||||
boundaryEquivalent+=[(x+halfWidth,y-halfWidth)]
|
||||
if(y > lastY and x < nextX):
|
||||
boundaryEquivalent+=[(x-halfWidth,y+halfWidth)]
|
||||
if(y > nextY and x > lastX):
|
||||
boundaryEquivalent+=[(x+halfWidth,y+halfWidth)]
|
||||
if(y > lastY and x > nextX):
|
||||
boundaryEquivalent+=[(x-halfWidth,y-halfWidth)]
|
||||
if(y < nextY and x > lastX):
|
||||
boundaryEquivalent+=[(x-halfWidth,y+halfWidth)]
|
||||
if(y < lastY and x > nextX):
|
||||
boundaryEquivalent+=[(x+halfWidth,y-halfWidth)]
|
||||
|
||||
if nextX == None: #end of path, put in the last 2 points
|
||||
if lastX<x:#moving right
|
||||
boundaryEquivalent+=[(x,y+halfWidth)]
|
||||
if lastX>x:#moving left
|
||||
boundaryEquivalent+=[(x,y-halfWidth)]
|
||||
if lastY<y:#moving up
|
||||
boundaryEquivalent+=[(x-halfWidth,y)]
|
||||
if lastY>y:#moving down
|
||||
boundaryEquivalent+=[(x+halfWidth,y)]
|
||||
#return to beginning
|
||||
boundaryEquivalent+=[(x,y)]
|
||||
lastX = x
|
||||
lastY = y
|
||||
return boundaryEquivalent
|
||||
|
||||
class GdsSref:
|
||||
"""Class represent a GDS structure reference Object"""
|
||||
def __init__(self):
|
||||
self.elementFlags=""
|
||||
self.plex=""
|
||||
self.sName=""
|
||||
self.transFlags=(False,False,False)
|
||||
self.magFactor=""
|
||||
self.rotateAngle=""
|
||||
self.coordinates=""
|
||||
|
||||
class GdsAref:
|
||||
"""Class represent a GDS array reference Object"""
|
||||
def __init__(self):
|
||||
self.elementFlags=""
|
||||
self.plex=""
|
||||
self.aName=""
|
||||
self.transFlags=(False,False,False)
|
||||
self.magFactor=""
|
||||
self.rotateAngle=""
|
||||
self.coordinates=""
|
||||
|
||||
class GdsText:
|
||||
"""Class represent a GDS text Object"""
|
||||
def __init__(self):
|
||||
self.elementFlags=""
|
||||
self.plex=""
|
||||
self.drawingLayer=""
|
||||
self.purposeLayer = None
|
||||
self.transFlags=(False,False,False)
|
||||
self.magFactor=""
|
||||
self.rotateAngle=""
|
||||
self.pathType=""
|
||||
self.pathWidth=""
|
||||
self.presentationFlags=""
|
||||
self.coordinates=""
|
||||
self.textString = ""
|
||||
|
||||
class GdsNode:
|
||||
"""Class represent a GDS Node Object"""
|
||||
def __init__(self):
|
||||
self.elementFlags=""
|
||||
self.plex=""
|
||||
self.drawingLayer=""
|
||||
self.nodeType=""
|
||||
self.coordinates=""
|
||||
|
||||
class GdsBox:
|
||||
"""Class represent a GDS Box Object"""
|
||||
def __init__(self):
|
||||
self.elementFlags=""
|
||||
self.plex=""
|
||||
self.drawingLayer=""
|
||||
self.purposeLayer = None
|
||||
self.boxValue=""
|
||||
self.coordinates=""
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
import os
|
||||
|
||||
class GdsStreamer:
|
||||
"""
|
||||
This class is used to stream GDS files in and out of the Cadence toolsuite.
|
||||
"""
|
||||
def __init__(self, workingDirectory = "."):
|
||||
self.workingDirectory = os.path.abspath(workingDirectory)
|
||||
|
||||
def createStreamOutTemplate(self, sourceLibraryName, sourceCellName, gdsDestinationPath):
|
||||
templateFile = open(self.workingDirectory+"/partStreamOut.tmpl","w")
|
||||
templateFile.write("streamOutKeys = list(nil\n")
|
||||
templateFile.write("'runDir \".\"\n")
|
||||
templateFile.write("'libName \""+sourceLibraryName+"\"\n")
|
||||
templateFile.write("'primaryCell \""+sourceCellName+"\"\n")
|
||||
templateFile.write("'viewName \"layout\"\n")
|
||||
templateFile.write("'outFile \""+gdsDestinationPath+"/"+sourceCellName+".gds\"\n")
|
||||
templateFile.write("'scale 0.001000\n")
|
||||
templateFile.write("'units \"micron\"\n")
|
||||
templateFile.write("'compression \"none\"\n")
|
||||
templateFile.write("'hierDepth 32\n")
|
||||
templateFile.write("'convertToGeo nil\n")
|
||||
templateFile.write("'maxVertices 200\n")
|
||||
templateFile.write("'refLib nil\n")
|
||||
templateFile.write("'libVersion \"5.0\"\n")
|
||||
templateFile.write("'checkPolygon nil\n")
|
||||
templateFile.write("'snapToGrid nil\n")
|
||||
templateFile.write("'simMosaicToArray t\n")
|
||||
templateFile.write("'caseSensitivity \"preserve\"\n")
|
||||
templateFile.write("'lineToZeroPath \"path\"\n")
|
||||
templateFile.write("'convertDot \"ignore\"\n")
|
||||
templateFile.write("'rectToBox nil\n")
|
||||
templateFile.write("'convertPathToPoly nil\n")
|
||||
templateFile.write("'keepPcell nil\n")
|
||||
templateFile.write("'replaceBusBitChar nil\n")
|
||||
templateFile.write("'useParentXYforText nil\n")
|
||||
templateFile.write("'reportPrecision nil\n")
|
||||
templateFile.write("'runQuiet nil\n")
|
||||
templateFile.write("'comprehensiveLog nil\n")
|
||||
templateFile.write("'ignorePcellEvalFail nil\n")
|
||||
templateFile.write("'errFile \"PIPO.LOG\"\n")
|
||||
templateFile.write("'NOUnmappingLayerWarning nil\n")
|
||||
templateFile.write("'techFileChoice nil\n")
|
||||
templateFile.write("'pcellSuffix \"DbId\"\n")
|
||||
templateFile.write("'respectGDSIILimits nil\n")
|
||||
templateFile.write("'dumpPcellInfo nil\n")
|
||||
templateFile.write("'genListHier nil\n")
|
||||
templateFile.write("'cellMapTable \"\"\n")
|
||||
templateFile.write("'layerTable \"\"\n")
|
||||
templateFile.write("'textFontTable \"\"\n")
|
||||
templateFile.write("'convertPin \"geometry\"\n")
|
||||
templateFile.write("'pinInfo 0\n")
|
||||
templateFile.write("'pinTextMapTable \"\"\n")
|
||||
templateFile.write("'propMapTable \"\"\n")
|
||||
templateFile.write("'propSeparator \",\"\n")
|
||||
templateFile.write("'userSkillFile \"\"\n")
|
||||
templateFile.write("'rodDir \"\"\n")
|
||||
templateFile.write("'refLibList \"\"\n")
|
||||
templateFile.write(")\n")
|
||||
templateFile.close()
|
||||
|
||||
def createStreamInTemplate(self, sourceLibraryName = None,inputGdsPath = None, retainReferenceLibraries = True):
|
||||
# retainReferenceLibraries added to tell PIPO whether it should import all SREFS as new cellviews or to
|
||||
#look inside of existing libraries for cellviews with the same name.
|
||||
templateParameters = dict()
|
||||
if retainReferenceLibraries:
|
||||
templateParameters["ref"] = "t"
|
||||
else:
|
||||
templateParameters["ref"] = "nil"
|
||||
templateFile = open(self.workingDirectory+"/partStreamIn.tmpl","w")
|
||||
templateFile.write("streamInKeys = list(nil\n")
|
||||
templateFile.write("'runDir \".\"\n")
|
||||
templateFile.write("'inFile \""+inputGdsPath+"\"\n")
|
||||
templateFile.write("'primaryCell \"\"\n")
|
||||
templateFile.write("'libName \""+sourceLibraryName+"\"\n")
|
||||
templateFile.write("'techFileName \"\"\n")
|
||||
templateFile.write("'scale 0.001000\n")
|
||||
templateFile.write("'units \"micron\"\n")
|
||||
templateFile.write("'errFile \"PIPO.LOG\"\n")
|
||||
templateFile.write("'refLib "+templateParameters["ref"]+"\n")
|
||||
templateFile.write("'hierDepth 32\n")
|
||||
templateFile.write("'maxVertices 1024\n")
|
||||
templateFile.write("'checkPolygon nil\n")
|
||||
templateFile.write("'snapToGrid nil\n")
|
||||
templateFile.write("'arrayToSimMosaic t\n")
|
||||
templateFile.write("'caseSensitivity \"preserve\"\n")
|
||||
templateFile.write("'zeroPathToLine \"path\"\n")
|
||||
templateFile.write("'convertNode \"ignore\"\n")
|
||||
templateFile.write("'keepPcell nil\n")
|
||||
templateFile.write("'replaceBusBitChar nil\n")
|
||||
templateFile.write("'skipUndefinedLPP nil\n")
|
||||
templateFile.write("'ignoreBox nil\n")
|
||||
templateFile.write("'mergeUndefPurposToDrawing nil\n")
|
||||
templateFile.write("'reportPrecision nil\n")
|
||||
templateFile.write("'keepStreamCells nil\n")
|
||||
templateFile.write("'attachTechfileOfLib \"\"\n")
|
||||
templateFile.write("'runQuiet nil\n")
|
||||
templateFile.write("'noWriteExistCell nil\n")
|
||||
templateFile.write("'NOUnmappingLayerWarning nil\n")
|
||||
templateFile.write("'comprehensiveLog nil\n")
|
||||
templateFile.write("'ignorePcellEvalFail nil\n")
|
||||
templateFile.write("'appendDB nil\n")
|
||||
templateFile.write("'genListHier nil\n")
|
||||
templateFile.write("'cellMapTable \"\"\n")
|
||||
templateFile.write("'layerTable \"\"\n")
|
||||
templateFile.write("'textFontTable \"\"\n")
|
||||
templateFile.write("'restorePin 0\n")
|
||||
templateFile.write("'propMapTable \"\"\n")
|
||||
templateFile.write("'propSeparator \",\"\n")
|
||||
templateFile.write("'userSkillFile \"\"\n")
|
||||
templateFile.write("'rodDir \"\"\n")
|
||||
templateFile.write("'refLibOrder \"\"\n")
|
||||
templateFile.write(")\n")
|
||||
templateFile.close()
|
||||
|
||||
def streamFromCadence(self, cadenceLibraryContainerPath, libraryName, cellName, outputPath):
|
||||
#change into the cadence directory
|
||||
outputPath = os.path.abspath(outputPath)
|
||||
currentPath = os.path.abspath(".")
|
||||
os.chdir(cadenceLibraryContainerPath)
|
||||
self.createStreamOutTemplate(libraryName,cellName,outputPath)
|
||||
#stream the gds out from cadence
|
||||
worker = os.popen("pipo strmout "+self.workingDirectory+"/partStreamOut.tmpl")
|
||||
#dump the outputs to the screen line by line
|
||||
print "Streaming Out From Cadence......"
|
||||
while 1:
|
||||
line = worker.readline()
|
||||
if not line: break #this means sim is finished so jump out
|
||||
#else: print line #for debug only
|
||||
worker.close()
|
||||
#now remove the template file
|
||||
os.remove(self.workingDirectory+"/partStreamOut.tmpl")
|
||||
#and go back to whever it was we started from
|
||||
os.chdir(currentPath)
|
||||
|
||||
def streamToCadence(self,cadenceLibraryContainerPath, libraryName, inputPath):
|
||||
#change into the cadence directory
|
||||
inputPath = os.path.abspath(inputPath)
|
||||
currentPath = os.path.abspath(".")
|
||||
os.chdir(cadenceLibraryContainerPath)
|
||||
self.createStreamInTemplate(libraryName,inputPath)
|
||||
#stream the gds out from cadence
|
||||
worker = os.popen("pipo strmin "+self.workingDirectory+"/partStreamIn.tmpl")
|
||||
#dump the outputs to the screen line by line
|
||||
print "Streaming In To Cadence......"
|
||||
while 1:
|
||||
line = worker.readline()
|
||||
if not line: break #this means sim is finished so jump out
|
||||
#else: print line #for debug only
|
||||
worker.close()
|
||||
#now remove the template file
|
||||
os.remove(self.workingDirectory+"/partStreamIn.tmpl")
|
||||
#and go back to whever it was we started from
|
||||
os.chdir(currentPath)
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
import pyx
|
||||
import math
|
||||
import mpmath
|
||||
from gdsPrimitives import *
|
||||
import random
|
||||
|
||||
class pdfLayout:
|
||||
"""Class representing a view for a layout as a PDF"""
|
||||
def __init__(self,theLayout):
|
||||
self.canvas = pyx.canvas.canvas()
|
||||
self.layout = theLayout
|
||||
self.layerColors=dict()
|
||||
self.scale = 1.0
|
||||
|
||||
def setScale(self,newScale):
|
||||
self.scale = float(newScale)
|
||||
|
||||
def hexToRgb(self,hexColor):
|
||||
"""
|
||||
Takes a hexadecimal color string i.e. "#219E1C" and converts it to an rgb float triplet ranging 0->1
|
||||
"""
|
||||
red = int(hexColor[1:3],16)
|
||||
green = int(hexColor[3:5],16)
|
||||
blue = int(hexColor[5:7],16)
|
||||
return (float(red)/255,float(green)/255,float(blue)/255)
|
||||
|
||||
def randomHexColor(self):
|
||||
"""
|
||||
Generates a random color in hex using the format #ABC123
|
||||
"""
|
||||
red = hex(random.randint(0,255)).lstrip("0x")
|
||||
green = hex(random.randint(0,255)).lstrip("0x")
|
||||
blue = hex(random.randint(0,255)).lstrip("0x")
|
||||
return "#"+red+green+blue
|
||||
|
||||
def transformCoordinates(self,uvCoordinates,origin,uVector,vVector):
|
||||
"""
|
||||
This helper method will convert coordinates from a UV space to the cartesian XY space
|
||||
"""
|
||||
xyCoordinates = []
|
||||
#setup a translation matrix
|
||||
tMatrix = mpmath.matrix([[1.0,0.0,origin[0]],[0.0,1.0,origin[1]],[0.0,0.0,1.0]])
|
||||
#and a rotation matrix
|
||||
rMatrix = mpmath.matrix([[uVector[0],vVector[0],0.0],[uVector[1],vVector[1],0.0],[0.0,0.0,1.0]])
|
||||
for coordinate in uvCoordinates:
|
||||
#grab the point in UV space
|
||||
uvPoint = mpmath.matrix([coordinate[0],coordinate[1],1.0])
|
||||
#now rotate and translate it back to XY space
|
||||
xyPoint = rMatrix * uvPoint
|
||||
xyPoint = tMatrix * xyPoint
|
||||
xyCoordinates += [(xyPoint[0],xyPoint[1])]
|
||||
return xyCoordinates
|
||||
|
||||
def drawBoundary(self,boundary,origin,uVector,vVector):
|
||||
#get the coordinates in the correct coordinate space
|
||||
coordinates = self.transformCoordinates(boundary.coordinates,origin,uVector,vVector)
|
||||
#method to draw a boundary with an XY offset
|
||||
x=(coordinates[0][0])/self.scale
|
||||
y=(coordinates[0][1])/self.scale
|
||||
shape = pyx.path.path(pyx.path.moveto(x, y))
|
||||
for index in range(1,len(coordinates)):
|
||||
x=(coordinates[index][0])/self.scale
|
||||
y=(coordinates[index][1])/self.scale
|
||||
shape.append(pyx.path.lineto(x,y))
|
||||
self.canvas.stroke(shape, [pyx.style.linewidth.thick])
|
||||
if(boundary.drawingLayer in self.layerColors):
|
||||
layerColor = self.hexToRgb(self.layerColors[boundary.drawingLayer])
|
||||
self.canvas.fill(shape, [pyx.color.rgb(layerColor[0],layerColor[1],layerColor[2]), pyx.color.transparency(0.5)])
|
||||
|
||||
def drawPath(self,path,origin,uVector,vVector):
|
||||
#method to draw a path with an XY offset
|
||||
boundaryCoordinates = self.transformCoordinates(path.equivalentBoundaryCoordinates(),origin,uVector,vVector)
|
||||
shape = pyx.path.path(pyx.path.moveto((boundaryCoordinates[0][0])/self.scale,(boundaryCoordinates[0][1])/self.scale))
|
||||
for coordinate in boundaryCoordinates[1::]:
|
||||
shape.append(pyx.path.lineto((coordinate[0])/self.scale,(coordinate[1])/self.scale))
|
||||
self.canvas.stroke(shape, [pyx.style.linewidth.thick])
|
||||
if(path.drawingLayer in self.layerColors):
|
||||
layerColor = self.hexToRgb(self.layerColors[path.drawingLayer])
|
||||
self.canvas.fill(shape, [pyx.color.rgb(layerColor[0],layerColor[1],layerColor[2]), pyx.color.transparency(0.5)])
|
||||
|
||||
def drawLayout(self):
|
||||
#use the layout xyTree and structureList
|
||||
#to draw ONLY the geometry in each structure
|
||||
#SREFS and AREFS are handled in the tree
|
||||
for element in self.layout.xyTree:
|
||||
#each element is (name,offsetTuple,rotate)
|
||||
structureToDraw = self.layout.structures[element[0]]
|
||||
for boundary in structureToDraw.boundaries:
|
||||
self.drawBoundary(boundary,element[1],element[2], element[3])
|
||||
for path in structureToDraw.paths:
|
||||
self.drawPath(path,element[1],element[2], element[3])
|
||||
|
||||
def writeToFile(self,filename):
|
||||
self.canvas.writePDFfile(filename)
|
||||
|
|
@ -0,0 +1,749 @@
|
|||
from gdsPrimitives import *
|
||||
from datetime import *
|
||||
import mpmath
|
||||
import gdsPrimitives
|
||||
import debug
|
||||
debug_level=4
|
||||
|
||||
class VlsiLayout:
|
||||
"""Class represent a hierarchical layout"""
|
||||
|
||||
def __init__(self, name=None, units=(0.001,1e-9), libraryName = "DEFAULT.DB", gdsVersion=5):
|
||||
#keep a list of all the structures in this layout
|
||||
self.units = units
|
||||
#print units
|
||||
modDate = datetime.now()
|
||||
self.structures=dict()
|
||||
self.layerNumbersInUse = []
|
||||
self.debug = debug
|
||||
if name:
|
||||
self.rootStructureName=name
|
||||
#create the ROOT structure
|
||||
self.structures[self.rootStructureName] = GdsStructure()
|
||||
self.structures[self.rootStructureName].name = name
|
||||
self.structures[self.rootStructureName].createDate = (modDate.year,
|
||||
modDate.month,
|
||||
modDate.day,
|
||||
modDate.hour,
|
||||
modDate.minute,
|
||||
modDate.second)
|
||||
self.structures[self.rootStructureName].modDate = (modDate.year,
|
||||
modDate.month,
|
||||
modDate.day,
|
||||
modDate.hour,
|
||||
modDate.minute,
|
||||
modDate.second)
|
||||
|
||||
self.info = dict() #information gathered from the GDSII header
|
||||
self.info['units']=self.units
|
||||
self.info['dates']=(modDate.year,
|
||||
modDate.month,
|
||||
modDate.day,
|
||||
modDate.hour,
|
||||
modDate.minute,
|
||||
modDate.second,
|
||||
modDate.year,
|
||||
modDate.month,
|
||||
modDate.day,
|
||||
modDate.hour,
|
||||
modDate.minute,
|
||||
modDate.second)
|
||||
self.info['libraryName']=libraryName
|
||||
self.info['gdsVersion']=gdsVersion
|
||||
|
||||
self.xyTree = [] #This will contain a list of all structure names
|
||||
#expanded to include srefs / arefs separately.
|
||||
#each structure will have an X,Y,offset, and rotate associated
|
||||
#with it. Populate via traverseTheHierarchy method.
|
||||
|
||||
#temp variables used in delegate functions
|
||||
self.tempCoordinates=None
|
||||
self.tempPassFail = True
|
||||
|
||||
def strip_non_ascii(string):
|
||||
''' Returns the string without non ASCII characters'''
|
||||
stripped = (c for c in string if 0 < ord(c) < 127)
|
||||
return ''.join(stripped)
|
||||
|
||||
|
||||
def rotatedCoordinates(self,coordinatesToRotate,rotateAngle):
|
||||
#helper method to rotate a list of coordinates
|
||||
angle=math.radians(float(0))
|
||||
if(rotateAngle):
|
||||
angle = math.radians(float(repr(rotateAngle)))
|
||||
|
||||
coordinatesRotate = [] #this will hold the rotated values
|
||||
for coordinate in coordinatesToRotate:
|
||||
newX = coordinate[0]*math.cos(angle) - coordinate[1]*math.sin(angle)
|
||||
newY = coordinate[0]*math.sin(angle) + coordinate[1]*math.cos(angle)
|
||||
coordinatesRotate += [(newX,newY)]
|
||||
return coordinatesRotate
|
||||
|
||||
def rename(self,newName):
|
||||
#make sure the newName is a multiple of 2 characters
|
||||
if(len(newName)%2 == 1):
|
||||
#pad with a zero
|
||||
newName = newName + '\x00'
|
||||
#take the root structure and copy it to a new structure with the new name
|
||||
self.structures[newName] = self.structures[self.rootStructureName]
|
||||
self.structures[newName].name = newName
|
||||
#and delete the old root
|
||||
del self.structures[self.rootStructureName]
|
||||
self.rootStructureName = newName
|
||||
#repopulate the 2d map so drawing occurs correctly
|
||||
del self.xyTree[:]
|
||||
self.populateCoordinateMap()
|
||||
|
||||
def newLayout(self,newName):
|
||||
#if (newName == "" | newName == 0):
|
||||
# print("ERROR: vlsiLayout.py:newLayout newName is null")
|
||||
|
||||
#make sure the newName is a multiple of 2 characters
|
||||
#if(len(newName)%2 == 1):
|
||||
#pad with a zero
|
||||
#newName = newName + '\x00'
|
||||
#take the root structure and copy it to a new structure with the new name
|
||||
#self.structures[newName] = self.structures[self.rootStructureName]
|
||||
|
||||
modDate = datetime.now()
|
||||
|
||||
self.structures[newName] = GdsStructure()
|
||||
self.structures[newName].name = newName
|
||||
|
||||
|
||||
|
||||
self.rootStructureName = newName
|
||||
|
||||
self.rootStructureName=newName
|
||||
|
||||
#create the ROOT structure
|
||||
self.structures[self.rootStructureName] = GdsStructure()
|
||||
#self.structures[self.rootStructureName].name = name
|
||||
self.structures[self.rootStructureName].createDate = (modDate.year,
|
||||
modDate.month,
|
||||
modDate.day,
|
||||
modDate.hour,
|
||||
modDate.minute,
|
||||
modDate.second)
|
||||
self.structures[self.rootStructureName].modDate = (modDate.year,
|
||||
modDate.month,
|
||||
modDate.day,
|
||||
modDate.hour,
|
||||
modDate.minute,
|
||||
modDate.second)
|
||||
|
||||
|
||||
#repopulate the 2d map so drawing occurs correctly
|
||||
self.prepareForWrite()
|
||||
|
||||
def prepareForWrite(self):
|
||||
del self.xyTree[:]
|
||||
self.populateCoordinateMap()
|
||||
|
||||
def deduceHierarchy(self):
|
||||
#first, find the root of the tree.
|
||||
#go through and get the name of every structure.
|
||||
#then, go through and find which structure is not
|
||||
#contained by any other structure. this is the root.
|
||||
structureNames=[]
|
||||
for name in self.structures:
|
||||
#print "deduceHierarchy: structure.name[%s]",name //FIXME: Added By Tom G.
|
||||
structureNames+=[name]
|
||||
|
||||
for name in self.structures:
|
||||
if(len(self.structures[name].srefs)>0): #does this structure reference any others?
|
||||
for sref in self.structures[name].srefs: #go through each reference
|
||||
if sref.sName in structureNames: #and compare to our list
|
||||
structureNames.remove(sref.sName)
|
||||
|
||||
self.rootStructureName = structureNames[0]
|
||||
|
||||
def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None,
|
||||
transformPath = [], rotateAngle = 0, transFlags = (0,0,0), coordinates = (0,0)):
|
||||
#since this is a recursive function, must deal with the default
|
||||
#parameters explicitly
|
||||
if startingStructureName == None:
|
||||
startingStructureName = self.rootStructureName
|
||||
|
||||
#set up the rotation matrix
|
||||
if(rotateAngle == None or rotateAngle == ""):
|
||||
rotateAngle = 0
|
||||
else:
|
||||
rotateAngle = math.radians(float(rotateAngle))
|
||||
mRotate = mpmath.matrix([[math.cos(rotateAngle),-math.sin(rotateAngle),0.0],
|
||||
[math.sin(rotateAngle),math.cos(rotateAngle),0.0],[0.0,0.0,1.0],])
|
||||
#set up the translation matrix
|
||||
translateX = float(coordinates[0])
|
||||
translateY = float(coordinates[1])
|
||||
mTranslate = mpmath.matrix([[1.0,0.0,translateX],[0.0,1.0,translateY],[0.0,0.0,1.0]])
|
||||
#set up the scale matrix (handles mirror X)
|
||||
scaleX = 1.0
|
||||
if(transFlags[0]):
|
||||
scaleY = -1.0
|
||||
else:
|
||||
scaleY = 1.0
|
||||
mScale = mpmath.matrix([[scaleX,0.0,0.0],[0.0,scaleY,0.0],[0.0,0.0,1.0]])
|
||||
|
||||
#we need to keep track of all transforms in the hierarchy
|
||||
#when we add an element to the xy tree, we apply all transforms from the bottom up
|
||||
transformPath += [(mRotate,mScale,mTranslate)]
|
||||
if delegateFunction != None:
|
||||
delegateFunction(startingStructureName, transformPath)
|
||||
#starting with a particular structure, we will recursively traverse the tree
|
||||
#********might have to set the recursion level deeper for big layouts!
|
||||
if(len(self.structures[startingStructureName].srefs)>0): #does this structure reference any others?
|
||||
#if so, go through each and call this function again
|
||||
#if not, return back to the caller (caller can be this function)
|
||||
for sref in self.structures[startingStructureName].srefs:
|
||||
#here, we are going to modify the sref coordinates based on the parent objects rotation
|
||||
# if (sref.sName.count("via") == 0):
|
||||
self.traverseTheHierarchy(startingStructureName = sref.sName,
|
||||
delegateFunction = delegateFunction,
|
||||
transformPath = transformPath,
|
||||
rotateAngle = sref.rotateAngle,
|
||||
transFlags = sref.transFlags,
|
||||
coordinates = sref.coordinates)
|
||||
# else:
|
||||
# print "WARNING: via encountered, ignoring:", sref.sName
|
||||
#MUST HANDLE AREFs HERE AS WELL
|
||||
#when we return, drop the last transform from the transformPath
|
||||
del transformPath[-1]
|
||||
return
|
||||
|
||||
def initialize(self):
|
||||
self.deduceHierarchy()
|
||||
#self.traverseTheHierarchy()
|
||||
self.populateCoordinateMap()
|
||||
|
||||
def populateCoordinateMap(self):
|
||||
def addToXyTree(startingStructureName = None,transformPath = None):
|
||||
#print"populateCoordinateMap"
|
||||
uVector = mpmath.matrix([1.0,0.0,0.0]) #start with normal basis vectors
|
||||
vVector = mpmath.matrix([0.0,1.0,0.0])
|
||||
origin = mpmath.matrix([0.0,0.0,1.0]) #and an origin (Z component is 1.0 to indicate position instead of vector)
|
||||
#make a copy of all the transforms and reverse it
|
||||
reverseTransformPath = transformPath[:]
|
||||
if len(reverseTransformPath) > 1:
|
||||
reverseTransformPath.reverse()
|
||||
#now go through each transform and apply them to our basis and origin in succession
|
||||
for transform in reverseTransformPath:
|
||||
origin = transform[0] * origin #rotate
|
||||
uVector = transform[0] * uVector #rotate
|
||||
vVector = transform[0] * vVector #rotate
|
||||
origin = transform[1] * origin #scale
|
||||
uVector = transform[1] * uVector #rotate
|
||||
vVector = transform[1] * vVector #rotate
|
||||
origin = transform[2] * origin #translate
|
||||
#we don't need to do a translation on the basis vectors
|
||||
self.xyTree+=[(startingStructureName,origin,uVector,vVector)] #populate the xyTree with each
|
||||
#structureName and coordinate space
|
||||
self.traverseTheHierarchy(delegateFunction = addToXyTree)
|
||||
|
||||
def microns(self,userUnits):
|
||||
"""Utility function to convert user units to microns"""
|
||||
userUnit = self.units[1]/self.units[0]
|
||||
userUnitsPerMicron = userUnit / (userunit)
|
||||
layoutUnitsPerMicron = userUnitsPerMicron / self.units[0]
|
||||
return userUnits / layoutUnitsPerMicron
|
||||
|
||||
def userUnits(self,microns):
|
||||
"""Utility function to convert microns to user units"""
|
||||
userUnit = self.units[1]/self.units[0]
|
||||
#userUnitsPerMicron = userUnit / 1e-6
|
||||
userUnitsPerMicron = userUnit / (userUnit)
|
||||
layoutUnitsPerMicron = userUnitsPerMicron / self.units[0]
|
||||
#print "userUnit:",userUnit,"userUnitsPerMicron",userUnitsPerMicron,"layoutUnitsPerMicron",layoutUnitsPerMicron,[microns,microns*layoutUnitsPerMicron]
|
||||
return round(microns*layoutUnitsPerMicron,0)
|
||||
|
||||
def changeRoot(self,newRoot, create=False):
|
||||
"""
|
||||
Method to change the root pointer to another layout.
|
||||
"""
|
||||
|
||||
#if self.debug: print "DEBUG: GdsMill vlsiLayout: changeRoot: %s "%newRoot
|
||||
debug.info(debug_level,"DEBUG: GdsMill vlsiLayout: changeRoot: %s "%newRoot)
|
||||
|
||||
# Determine if newRoot exists
|
||||
# layoutToAdd (default) or nameOfLayout
|
||||
if (newRoot == 0 | ((newRoot not in self.structures) & ~create)):
|
||||
#print "ERROR: vlsiLayout.changeRoot: Name of new root [%s] not found and create flag is false"%newRoot
|
||||
debug.error(debug_level,"ERROR: vlsiLayout.changeRoot: Name of new root [%s] not found and create flag is false"+str(newRoot))
|
||||
exit(1)
|
||||
else:
|
||||
if ((newRoot not in self.structures) & create):
|
||||
self.newLayout(newRoot)
|
||||
self.rootStructureName = newRoot
|
||||
|
||||
|
||||
|
||||
def addInstance(self,layoutToAdd,nameOfLayout=0,offsetInMicrons=(0,0),mirror=None,rotate=None):
|
||||
"""
|
||||
Method to insert one layout into another at a particular offset.
|
||||
"""
|
||||
offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1]))
|
||||
#print "addInstance:offsetInLayoutUnits",offsetInLayoutUnits
|
||||
#offsetInLayoutUnits = ((offsetInMicrons[0]),(offsetInMicrons[1]))
|
||||
#print "DEBUG: addInstance offsetInLayoutUnits: %f, %f"%(self.userUnits(offsetInMicrons[0]), self.userUnits(offsetInMicrons[1]))
|
||||
#if self.debug==1:
|
||||
# print "DEBUG: GdsMill vlsiLayout: addInstance: type %s, nameOfLayout "%type(layoutToAdd),nameOfLayout
|
||||
# print offsetInMicrons
|
||||
# print offsetInLayoutUnits
|
||||
debug.info(debug_level,"DEBUG: GdsMill vlsiLayout: addInstance: type "+str(layoutToAdd.rootStructureName))
|
||||
debug.info(debug_level,"offset In Microns:"+str(offsetInMicrons)+"offset In LayoutUnits:"+str(offsetInLayoutUnits))
|
||||
|
||||
|
||||
|
||||
# Determine if we are instantiating the root design of
|
||||
# layoutToAdd (default) or nameOfLayout
|
||||
if nameOfLayout == 0:
|
||||
StructureFound = True
|
||||
StructureName = layoutToAdd.rootStructureName
|
||||
else:
|
||||
StructureName = nameOfLayout #layoutToAdd
|
||||
StructureFound = False
|
||||
for structure in layoutToAdd.structures:
|
||||
# if self.debug: print structure, "N","N"
|
||||
if StructureName in structure:
|
||||
debug.info(debug_level,"DEBUG: Structure %s Found"+str(StructureName))
|
||||
#if self.debug: print "DEBUG: Structure %s Found"%StructureName
|
||||
StructureFound = True
|
||||
|
||||
|
||||
|
||||
# If layoutToAdd is a unique object (not this), then copy heirarchy,
|
||||
# otherwise, if it is a text name of an internal structure, use it.
|
||||
|
||||
if layoutToAdd != self:
|
||||
#first, we need to combine the structure dictionaries from both layouts
|
||||
for structure in layoutToAdd.structures:
|
||||
if structure not in self.structures:
|
||||
self.structures[structure]=layoutToAdd.structures[structure]
|
||||
#also combine the "layers in use" list
|
||||
for layerNumber in layoutToAdd.layerNumbersInUse:
|
||||
if layerNumber not in self.layerNumbersInUse:
|
||||
self.layerNumbersInUse += [layerNumber]
|
||||
#Also, check if the user units / microns is the same as this Layout
|
||||
#if (layoutToAdd.units != self.units):
|
||||
#print "WARNING: VlsiLayout: Units from design to be added do not match target Layout"
|
||||
|
||||
# if debug: print "DEBUG: vlsilayout: Using %d layers"
|
||||
|
||||
# If we can't find the structure, error
|
||||
#if StructureFound == False:
|
||||
#print "ERROR: vlsiLayout.addInstance: [%s] Name not found in local structures, "%(nameOfLayout)
|
||||
#return #FIXME: remove!
|
||||
#exit(1)
|
||||
|
||||
|
||||
#add a reference to the new layout structure in this layout's root
|
||||
layoutToAddSref = GdsSref()
|
||||
layoutToAddSref.sName = StructureName
|
||||
layoutToAddSref.coordinates = offsetInLayoutUnits
|
||||
|
||||
if mirror or rotate:
|
||||
########flags = (mirror around x-axis, absolute rotation, absolute magnification)
|
||||
layoutToAddSref.transFlags = (False,False,False)
|
||||
#Below angles are angular angles(relative), not absolute
|
||||
if mirror=="R90":
|
||||
rotate = 90.0
|
||||
if mirror=="R180":
|
||||
rotate = 180.0
|
||||
if mirror=="R270":
|
||||
rotate = 270.0
|
||||
if rotate:
|
||||
layoutToAddSref.rotateAngle = rotate
|
||||
if mirror == "x" or mirror == "MX":
|
||||
layoutToAddSref.transFlags = (True,False,False)
|
||||
if mirror == "y" or mirror == "MY": #NOTE: "MY" option will override specified rotate angle
|
||||
layoutToAddSref.transFlags = (True,False,False)
|
||||
layoutToAddSref.rotateAngle = 180.0
|
||||
if mirror == "xy" or mirror == "XY": #NOTE: "XY" option will override specified rotate angle
|
||||
layoutToAddSref.transFlags = (False,False,False)
|
||||
layoutToAddSref.rotateAngle = 180.0
|
||||
|
||||
#add the sref to the root structure
|
||||
self.structures[self.rootStructureName].srefs+=[layoutToAddSref]
|
||||
|
||||
def addBox(self,layerNumber=0, purposeNumber=None, offsetInMicrons=(0,0), width=1.0, height=1.0,center=False):
|
||||
"""
|
||||
Method to add a box to a layout
|
||||
"""
|
||||
offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1]))
|
||||
#print "addBox:offsetInLayoutUnits",offsetInLayoutUnits
|
||||
widthInLayoutUnits = self.userUnits(width)
|
||||
heightInLayoutUnits = self.userUnits(height)
|
||||
#print "offsetInLayoutUnits",widthInLayoutUnits,"heightInLayoutUnits",heightInLayoutUnits
|
||||
if not center:
|
||||
coordinates=[offsetInLayoutUnits,
|
||||
(offsetInLayoutUnits[0]+widthInLayoutUnits,offsetInLayoutUnits[1]),
|
||||
(offsetInLayoutUnits[0]+widthInLayoutUnits,offsetInLayoutUnits[1]+heightInLayoutUnits),
|
||||
(offsetInLayoutUnits[0],offsetInLayoutUnits[1]+heightInLayoutUnits),
|
||||
offsetInLayoutUnits]
|
||||
else:
|
||||
|
||||
#is there where gdsmill is halving the coordinates???
|
||||
#if you printGDS of temp.gds, the header says 1 user unit = .0005 database units. By default user units = .001.
|
||||
#something to do with the ieeedouble in gdswriter.py????
|
||||
startPoint = (offsetInLayoutUnits[0]-widthInLayoutUnits/2, offsetInLayoutUnits[1]-heightInLayoutUnits/2) #width/2 height/2
|
||||
coordinates=[startPoint,
|
||||
(startPoint[0]+widthInLayoutUnits,startPoint[1]),
|
||||
(startPoint[0]+widthInLayoutUnits,startPoint[1]+heightInLayoutUnits),
|
||||
(startPoint[0],startPoint[1]+heightInLayoutUnits),
|
||||
startPoint]
|
||||
|
||||
boundaryToAdd = GdsBoundary()
|
||||
boundaryToAdd.drawingLayer = layerNumber
|
||||
boundaryToAdd.dataType = 0
|
||||
boundaryToAdd.coordinates = coordinates
|
||||
boundaryToAdd.purposeLayer = purposeNumber
|
||||
#add the sref to the root structure
|
||||
self.structures[self.rootStructureName].boundaries+=[boundaryToAdd]
|
||||
|
||||
def addPath(self, layerNumber=0, purposeNumber = None, coordinates=[(0,0)], width=1.0):
|
||||
"""
|
||||
Method to add a path to a layout
|
||||
"""
|
||||
widthInLayoutUnits = self.userUnits(width)
|
||||
layoutUnitCoordinates = []
|
||||
#first convert to proper units
|
||||
for coordinate in coordinates:
|
||||
cX = self.userUnits(coordinate[0])
|
||||
cY = self.userUnits(coordinate[1])
|
||||
layoutUnitCoordinates += [(cX,cY)]
|
||||
pathToAdd = GdsPath()
|
||||
pathToAdd.drawingLayer=layerNumber
|
||||
pathToAdd.purposeLayer = purposeNumber
|
||||
pathToAdd.pathWidth=widthInLayoutUnits
|
||||
pathToAdd.coordinates=layoutUnitCoordinates
|
||||
#add the sref to the root structure
|
||||
self.structures[self.rootStructureName].paths+=[pathToAdd]
|
||||
|
||||
def addText(self, text, layerNumber=0, purposeNumber = None, offsetInMicrons=(0,0), magnification=0.1, rotate = None):
|
||||
offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1]))
|
||||
textToAdd = GdsText()
|
||||
textToAdd.drawingLayer = layerNumber
|
||||
textToAdd.purposeLayer = purposeNumber
|
||||
textToAdd.dataType = 0
|
||||
textToAdd.coordinates = [offsetInLayoutUnits]
|
||||
if(len(text)%2 == 1):
|
||||
#pad with a zero
|
||||
text = text + '\x00'
|
||||
textToAdd.textString = text
|
||||
textToAdd.transFlags = (False,False,True)
|
||||
textToAdd.magFactor = magnification
|
||||
if rotate:
|
||||
textToAdd.transFlags = (False,True,True)
|
||||
textToAdd.rotateAngle = rotate
|
||||
#add the sref to the root structure
|
||||
self.structures[self.rootStructureName].texts+=[textToAdd]
|
||||
|
||||
def isBounded(self,testPoint,startPoint,endPoint):
|
||||
#these arguments are touples of (x,y) coordinates
|
||||
if testPoint == None:
|
||||
return 0
|
||||
if(testPoint[0]<=max(endPoint[0],startPoint[0]) and \
|
||||
testPoint[0]>=min(endPoint[0],startPoint[0]) and \
|
||||
testPoint[1]<=max(endPoint[1],startPoint[1]) and \
|
||||
testPoint[1]>=min(endPoint[1],startPoint[1])):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def intersectionPoint(self,startPoint1,endPoint1,startPoint2,endPoint2):
|
||||
if((endPoint1[0]-startPoint1[0])!=0 and (endPoint2[0]-startPoint2[0])!=0):
|
||||
pSlope = (endPoint1[1]-startPoint1[1])/(endPoint1[0]-startPoint1[0])
|
||||
pIntercept = startPoint1[1]-pSlope*startPoint1[0]
|
||||
qSlope = (endPoint2[1]-startPoint2[1])/(endPoint2[0]-startPoint2[0])
|
||||
qIntercept = startPoint2[1]-qSlope*startPoint2[0]
|
||||
if(pSlope!=qSlope):
|
||||
newX=(qIntercept-pIntercept)/(pSlope-qSlope)
|
||||
newY=pSlope*newX+pIntercept
|
||||
else:
|
||||
#parallel lines can't intersect
|
||||
newX=None
|
||||
newY=None
|
||||
elif((endPoint1[0]-startPoint1[0])==0 and (endPoint2[0]-startPoint2[0])==0):
|
||||
#two vertical lines cannot intersect
|
||||
newX = None
|
||||
newY = None
|
||||
elif((endPoint1[0]-startPoint1[0])==0 and (endPoint2[0]-startPoint2[0])!=0):
|
||||
qSlope = (endPoint2[1]-startPoint2[1])/(endPoint2[0]-startPoint2[0])
|
||||
qIntercept = startPoint2[1]-qSlope*startPoint2[0]
|
||||
newX=endPoint1[0]
|
||||
newY=qSlope*newX+qIntercept
|
||||
elif((endPoint1[0]-startPoint1[0])!=0 and (endPoint2[0]-startPoint2[0])==0):
|
||||
pSlope = (endPoint1[1]-startPoint1[1])/(endPoint1[0]-startPoint1[0])
|
||||
pIntercept = startPoint1[1]-pSlope*startPoint1[0]
|
||||
newX=endPoint2[0]
|
||||
newY=pSlope*newX+pIntercept
|
||||
return (newX,newY)
|
||||
|
||||
def isCollinear(self,testPoint,point1,point2):
|
||||
slope1 = (testPoint[1]-point1[1])/(testPoint[0]-point1[0])
|
||||
slope2 = (point2[1]-point1[1])/(point2[0]-point1[0])
|
||||
if slope1 == slope2:
|
||||
return True
|
||||
return False
|
||||
|
||||
def doShapesIntersect(self,shape1Coordinates, shape2Coordinates):
|
||||
"""
|
||||
Utility function to determine if 2 arbitrary shapes intersect.
|
||||
We define intersection by taking pairs of points in each shape (assuming they are in order)
|
||||
and seeing if any of the lines formed by these pais intersect.
|
||||
"""
|
||||
for shape1Index in range(0,len(shape1Coordinates)-1):
|
||||
for shape2Index in range(0,len(shape2Coordinates)-1):
|
||||
startPoint1 = shape1Coordinates[shape1Index]
|
||||
endPoint1 = shape1Coordinates[shape1Index+1]
|
||||
startPoint2 = shape2Coordinates[shape2Index]
|
||||
endPoint2 = shape2Coordinates[shape2Index+1]
|
||||
intersect = self.intersectionPoint(startPoint1,endPoint1,startPoint2,endPoint2)
|
||||
if(self.isBounded(intersect,startPoint1,endPoint1) and self.isBounded(intersect,startPoint2,endPoint2)):
|
||||
return True #these shapes overlap!
|
||||
return False #these shapes are ok
|
||||
|
||||
def isPointInsideOfBox(self,pointCoordinates,boxCoordinates):
|
||||
leftBound = boxCoordinates[0][0]
|
||||
rightBound = boxCoordinates[0][0]
|
||||
topBound = boxCoordinates[0][1]
|
||||
bottomBound = boxCoordinates[0][1]
|
||||
for point in boxCoordinates:
|
||||
if point[0]<leftBound:
|
||||
leftBound = point[0]
|
||||
if point[0]>rightBound:
|
||||
rightBound = point[0]
|
||||
if point[1]<bottomBound:
|
||||
bottomBound = point[1]
|
||||
if point[1]>topBound:
|
||||
topBound = point[1]
|
||||
if(pointCoordinates[0]>rightBound or
|
||||
pointCoordinates[0]<leftBound or
|
||||
pointCoordinates[1]>topBound or
|
||||
pointCoordinates[1]<bottomBound):
|
||||
return False
|
||||
return True
|
||||
|
||||
def isShapeInsideOfBox(self,shapeCoordinates, boxCoordinates):
|
||||
#go through every point in the shape to test if they are all inside the box
|
||||
for point in shapeCoordinates:
|
||||
if not self.isPointInsideOfBox(point,boxCoordinates):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def fillAreaDensity(self, layerToFill = 0, offsetInMicrons = (0,0), coverageWidth = 100.0, coverageHeight = 100.0,
|
||||
minSpacing = 0.22, blockSize = 1.0):
|
||||
effectiveBlock = blockSize+minSpacing
|
||||
widthInBlocks = int(coverageWidth/effectiveBlock)
|
||||
heightInBlocks = int(coverageHeight/effectiveBlock)
|
||||
passFailRecord = []
|
||||
|
||||
debug.info(debug_level,"Filling layer:"+str(layerToFill))
|
||||
#print "Filling layer:",layerToFill
|
||||
def isThisBlockOk(startingStructureName,coordinates,rotateAngle=None):
|
||||
#go through every boundary and check
|
||||
for boundary in self.structures[startingStructureName].boundaries:
|
||||
#only test shapes on the same layer
|
||||
if(boundary.drawingLayer == layerToFill):
|
||||
#remap coordinates
|
||||
shiftedBoundaryCoordinates = []
|
||||
for shapeCoordinate in boundary.rotatedCoordinates(rotateAngle):
|
||||
shiftedBoundaryCoordinates+=[(shapeCoordinate[0]+coordinates[0],shapeCoordinate[1]+coordinates[1])]
|
||||
joint = self.doShapesIntersect(self.tempCoordinates, shiftedBoundaryCoordinates)
|
||||
if joint:
|
||||
self.tempPassFail = False
|
||||
common = self.isShapeInsideOfBox(shiftedBoundaryCoordinates,self.tempCoordinates)
|
||||
if common:
|
||||
self.tempPassFail = False
|
||||
for path in self.structures[startingStructureName].paths:
|
||||
#only test shapes on the same layer
|
||||
if(path.drawingLayer == layerToFill):
|
||||
#remap coordinates
|
||||
shiftedBoundaryCoordinates = []
|
||||
for shapeCoordinate in path.equivalentBoundaryCoordinates(rotateAngle):
|
||||
shiftedBoundaryCoordinates+=[(shapeCoordinate[0]+coordinates[0],shapeCoordinate[1]+coordinates[1])]
|
||||
joint = self.doShapesIntersect(self.tempCoordinates, shiftedBoundaryCoordinates)
|
||||
if joint:
|
||||
self.tempPassFail = False
|
||||
common = self.isShapeInsideOfBox(shiftedBoundaryCoordinates,self.tempCoordinates)
|
||||
if common:
|
||||
self.tempPassFail = False
|
||||
|
||||
for yIndex in range(0,heightInBlocks):
|
||||
for xIndex in range(0,widthInBlocks):
|
||||
percentDone = (float((yIndex*heightInBlocks)+xIndex) / (heightInBlocks*widthInBlocks))*100
|
||||
blockX = (xIndex*effectiveBlock)+offsetInMicrons[0]
|
||||
blockY = (yIndex*effectiveBlock)+offsetInMicrons[1]
|
||||
self.tempCoordinates=[(self.userUnits(blockX-minSpacing),self.userUnits(blockY-minSpacing)),
|
||||
(self.userUnits(blockX-minSpacing),self.userUnits(blockY+effectiveBlock)),
|
||||
(self.userUnits(blockX+effectiveBlock),self.userUnits(blockY+effectiveBlock)),
|
||||
(self.userUnits(blockX+effectiveBlock),self.userUnits(blockY-minSpacing)),
|
||||
(self.userUnits(blockX-minSpacing),self.userUnits(blockY-minSpacing))]
|
||||
self.tempPassFail = True
|
||||
#go through the hierarchy and see if the block will fit
|
||||
self.traverseTheHierarchy(delegateFunction = isThisBlockOk)
|
||||
#if its bad, this global tempPassFail will be false
|
||||
#if true, we can add the block
|
||||
passFailRecord+=[self.tempPassFail]
|
||||
debug.info(debug_level,"Percent Complete:"+str(percentDone))
|
||||
|
||||
|
||||
passFailIndex=0
|
||||
for yIndex in range(0,heightInBlocks):
|
||||
for xIndex in range(0,widthInBlocks):
|
||||
blockX = (xIndex*effectiveBlock)+offsetInMicrons[0]
|
||||
blockY = (yIndex*effectiveBlock)+offsetInMicrons[1]
|
||||
if passFailRecord[passFailIndex]:
|
||||
self.addBox(layerToFill, (blockX,blockY), width=blockSize, height=blockSize)
|
||||
passFailIndex+=1
|
||||
debug.info(debug_level,"Done\n\n")
|
||||
|
||||
def readLayoutBorder(self,borderlayer):
|
||||
for boundary in self.structures[self.rootStructureName].boundaries:
|
||||
if boundary.drawingLayer==borderlayer:
|
||||
debug.info(debug_level,"Find border "+str(boundary.coordinates))
|
||||
left_button=boundary.coordinates[0]
|
||||
right_top=boundary.coordinates[2]
|
||||
cellSize=[right_top[0]-left_button[0],right_top[1]-left_button[1]]
|
||||
cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]]
|
||||
if not(cellSizeMicron):
|
||||
debug.error("Error: "+str(self.rootStructureName)+".cell_size information not found yet")
|
||||
return cellSizeMicron
|
||||
|
||||
def measureSize(self,startStructure):
|
||||
self.rootStructureName=startStructure
|
||||
self.populateCoordinateMap()
|
||||
cellBoundary = [None, None, None, None]
|
||||
for TreeUnit in self.xyTree:
|
||||
cellBoundary=self.measureSizeInStruture(TreeUnit,cellBoundary)
|
||||
cellSize=[cellBoundary[2]-cellBoundary[0],cellBoundary[3]-cellBoundary[1]]
|
||||
cellSizeMicron=[cellSize[0]*self.units[0],cellSize[1]*self.units[0]]
|
||||
return cellSizeMicron
|
||||
|
||||
def measureSizeInStruture(self,Struture,cellBoundary):
|
||||
StrutureName=Struture[0]
|
||||
StrutureOrgin=[Struture[1][0],Struture[1][1]]
|
||||
StrutureuVector=[Struture[2][0],Struture[2][1],Struture[2][2]]
|
||||
StruturevVector=[Struture[3][0],Struture[3][1],Struture[3][2]]
|
||||
debug.info(debug_level,"Checking Structure: "+str(StrutureName))
|
||||
debug.info(debug_level,"-Structure Struture Orgin:"+str(StrutureOrgin))
|
||||
debug.info(debug_level,"-Structure direction: uVector["+str(StrutureuVector)+"]")
|
||||
debug.info(debug_level,"-Structure direction: vVector["+str(StruturevVector)+"]")
|
||||
|
||||
for boundary in self.structures[str(StrutureName)].boundaries:
|
||||
left_button=boundary.coordinates[0]
|
||||
right_top=boundary.coordinates[2]
|
||||
thisBoundary=[left_button[0],left_button[1],right_top[0],right_top[1]]
|
||||
thisBoundary=self.tranformRectangle(thisBoundary,StrutureuVector,StruturevVector)
|
||||
thisBoundary=[thisBoundary[0]+StrutureOrgin[0],thisBoundary[1]+StrutureOrgin[1],
|
||||
thisBoundary[2]+StrutureOrgin[0],thisBoundary[3]+StrutureOrgin[1]]
|
||||
cellBoundary=self.update_boundary(thisBoundary,cellBoundary)
|
||||
return cellBoundary
|
||||
|
||||
def update_boundary(self,thisBoundary,cellBoundary):
|
||||
[left_butt_X,left_butt_Y,right_top_X,right_top_Y]=thisBoundary
|
||||
if cellBoundary==[None,None,None,None]:
|
||||
cellBoundary=thisBoundary
|
||||
else:
|
||||
if cellBoundary[0]>left_butt_X:
|
||||
cellBoundary[0]=left_butt_X
|
||||
if cellBoundary[1]>left_butt_Y:
|
||||
cellBoundary[1]=left_butt_Y
|
||||
if cellBoundary[2]<right_top_X:
|
||||
cellBoundary[2]=right_top_X
|
||||
if cellBoundary[3]<right_top_Y:
|
||||
cellBoundary[3]=right_top_Y
|
||||
return cellBoundary
|
||||
|
||||
def readPin(self,label_name,mod="offset"):
|
||||
label_layer = None
|
||||
label_coordinate = [None, None]
|
||||
|
||||
for Text in self.structures[self.rootStructureName].texts:
|
||||
debug.info(debug_level,"Check Text object "+str(Text.textString)+" in "+str(self.rootStructureName))
|
||||
debug.info(debug_level,"Length of text object: "+str(len(Text.textString)))
|
||||
if Text.textString == label_name or Text.textString == label_name+"\x00":
|
||||
label_layer = Text.drawingLayer
|
||||
label_coordinate = Text.coordinates
|
||||
debug.info(debug_level,"Find label "+str(Text.textString)+" at "+str(Text.coordinates))
|
||||
|
||||
pin_boundary=self.readPinInStructureList(label_coordinate, label_layer)
|
||||
debug.info(debug_level, "Find pin covers "+str(label_name)+" at "+str(pin_boundary))
|
||||
|
||||
pin_boundary=[pin_boundary[0]*self.units[0],pin_boundary[1]*self.units[0],pin_boundary[2]*self.units[0],pin_boundary[3]*self.units[0]]
|
||||
return [label_name, label_layer, pin_boundary]
|
||||
|
||||
def readPinInStructureList(self,label_coordinates,layer):
|
||||
label_boundary = [None,None,None,None]
|
||||
for TreeUnit in self.xyTree:
|
||||
label_boundary=self.readPinInStruture(label_coordinates,layer,TreeUnit,label_boundary)
|
||||
return label_boundary
|
||||
|
||||
|
||||
def readPinInStruture(self,label_coordinates,layer,Struture,label_boundary):
|
||||
StrutureName=Struture[0]
|
||||
StrutureOrgin=[Struture[1][0],Struture[1][1]]
|
||||
StrutureuVector=[Struture[2][0],Struture[2][1],Struture[2][2]]
|
||||
StruturevVector=[Struture[3][0],Struture[3][1],Struture[3][2]]
|
||||
debug.info(debug_level,"Checking Structure: "+str(StrutureName))
|
||||
debug.info(debug_level,"-Structure Struture Orgin:"+str(StrutureOrgin))
|
||||
debug.info(debug_level,"-Structure direction: uVector["+str(StrutureuVector)+"]")
|
||||
debug.info(debug_level,"-Structure direction: vVector["+str(StruturevVector)+"]")
|
||||
|
||||
for boundary in self.structures[str(StrutureName)].boundaries:
|
||||
if layer==boundary.drawingLayer:
|
||||
left_button=boundary.coordinates[0]
|
||||
right_top=boundary.coordinates[2]
|
||||
MetalBoundary=[left_button[0],left_button[1],right_top[0],right_top[1]]
|
||||
MetalBoundary=self.tranformRectangle(MetalBoundary,StrutureuVector,StruturevVector)
|
||||
MetalBoundary=[MetalBoundary[0]+StrutureOrgin[0],MetalBoundary[1]+StrutureOrgin[1],
|
||||
MetalBoundary[2]+StrutureOrgin[0],MetalBoundary[3]+StrutureOrgin[1]]
|
||||
|
||||
result = self.labelInRectangle(label_coordinates[0],MetalBoundary)
|
||||
if (result):
|
||||
debug.info(debug_level,"Rectangle(layer"+str(layer)+") at "+str(MetalBoundary))
|
||||
debug.info(debug_level,"covers label (offset"+str(label_coordinates)+")")
|
||||
label_boundary=self.returnBiggerBoundary(MetalBoundary,label_boundary)
|
||||
return label_boundary
|
||||
|
||||
def tranformRectangle(self,orignalRectangle,uVector,vVector):
|
||||
LeftButton=mpmath.matrix([orignalRectangle[0],orignalRectangle[1]])
|
||||
LeftButton=self.tranformCoordinate(LeftButton,uVector,vVector)
|
||||
|
||||
RightUp=mpmath.matrix([orignalRectangle[2],orignalRectangle[3]])
|
||||
RightUp=self.tranformCoordinate(RightUp,uVector,vVector)
|
||||
|
||||
Left=min(LeftButton[0],RightUp[0])
|
||||
Button=min(LeftButton[1],RightUp[1])
|
||||
Right=max(LeftButton[0],RightUp[0])
|
||||
Up=max(LeftButton[1],RightUp[1])
|
||||
|
||||
return [Left,Button,Right,Up]
|
||||
|
||||
def tranformCoordinate(self,Coordinate,uVector,vVector):
|
||||
x=Coordinate[0]*uVector[0]+Coordinate[1]*uVector[1]
|
||||
y=Coordinate[1]*vVector[1]+Coordinate[0]*vVector[0]
|
||||
tranformCoordinate=[x,y]
|
||||
return tranformCoordinate
|
||||
|
||||
|
||||
def labelInRectangle(self,label_coordinate,Rectangle):
|
||||
coordinate_In_Rectangle_x_range=(label_coordinate[0]>=int(Rectangle[0]))&(label_coordinate[0]<=int(Rectangle[2]))
|
||||
coordinate_In_Rectangle_y_range=(label_coordinate[1]>=int(Rectangle[1]))&(label_coordinate[1]<=int(Rectangle[3]))
|
||||
if coordinate_In_Rectangle_x_range & coordinate_In_Rectangle_y_range:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def returnBiggerBoundary(self,comparedRectangle,label_boundary):
|
||||
if label_boundary[0]== None:
|
||||
label_boundary=comparedRectangle
|
||||
debug.info(debug_level,"The label_boundary is initialized to "+str(label_boundary))
|
||||
else:
|
||||
area_label_boundary=(label_boundary[2]-label_boundary[0])*(label_boundary[3]-label_boundary[1])
|
||||
area_comparedRectangle=(comparedRectangle[2]-comparedRectangle[0])*(comparedRectangle[3]-comparedRectangle[1])
|
||||
if area_label_boundary<=area_comparedRectangle:
|
||||
label_boundary = comparedRectangle
|
||||
debug.info(debug_level,"The label_boundary is updated to "+str(label_boundary))
|
||||
return label_boundary
|
||||
|
||||
|
|
@ -0,0 +1,386 @@
|
|||
__version__ = '0.14'
|
||||
|
||||
from usertools import monitor, timing
|
||||
|
||||
from ctx_fp import FPContext
|
||||
from ctx_mp import MPContext
|
||||
|
||||
fp = FPContext()
|
||||
mp = MPContext()
|
||||
|
||||
fp._mp = mp
|
||||
mp._mp = mp
|
||||
mp._fp = fp
|
||||
fp._fp = fp
|
||||
|
||||
# XXX: extremely bad pickle hack
|
||||
import ctx_mp as _ctx_mp
|
||||
_ctx_mp._mpf_module.mpf = mp.mpf
|
||||
_ctx_mp._mpf_module.mpc = mp.mpc
|
||||
|
||||
make_mpf = mp.make_mpf
|
||||
make_mpc = mp.make_mpc
|
||||
|
||||
extraprec = mp.extraprec
|
||||
extradps = mp.extradps
|
||||
workprec = mp.workprec
|
||||
workdps = mp.workdps
|
||||
|
||||
mag = mp.mag
|
||||
|
||||
bernfrac = mp.bernfrac
|
||||
|
||||
jdn = mp.jdn
|
||||
jsn = mp.jsn
|
||||
jcn = mp.jcn
|
||||
jtheta = mp.jtheta
|
||||
calculate_nome = mp.calculate_nome
|
||||
|
||||
nint_distance = mp.nint_distance
|
||||
|
||||
plot = mp.plot
|
||||
cplot = mp.cplot
|
||||
splot = mp.splot
|
||||
|
||||
odefun = mp.odefun
|
||||
|
||||
jacobian = mp.jacobian
|
||||
findroot = mp.findroot
|
||||
multiplicity = mp.multiplicity
|
||||
|
||||
isinf = mp.isinf
|
||||
isnan = mp.isnan
|
||||
isint = mp.isint
|
||||
almosteq = mp.almosteq
|
||||
nan = mp.nan
|
||||
rand = mp.rand
|
||||
|
||||
absmin = mp.absmin
|
||||
absmax = mp.absmax
|
||||
|
||||
fraction = mp.fraction
|
||||
|
||||
linspace = mp.linspace
|
||||
arange = mp.arange
|
||||
|
||||
mpmathify = convert = mp.convert
|
||||
mpc = mp.mpc
|
||||
mpi = mp.mpi
|
||||
|
||||
nstr = mp.nstr
|
||||
nprint = mp.nprint
|
||||
chop = mp.chop
|
||||
|
||||
fneg = mp.fneg
|
||||
fadd = mp.fadd
|
||||
fsub = mp.fsub
|
||||
fmul = mp.fmul
|
||||
fdiv = mp.fdiv
|
||||
fprod = mp.fprod
|
||||
|
||||
quad = mp.quad
|
||||
quadgl = mp.quadgl
|
||||
quadts = mp.quadts
|
||||
quadosc = mp.quadosc
|
||||
|
||||
pslq = mp.pslq
|
||||
identify = mp.identify
|
||||
findpoly = mp.findpoly
|
||||
|
||||
richardson = mp.richardson
|
||||
shanks = mp.shanks
|
||||
nsum = mp.nsum
|
||||
nprod = mp.nprod
|
||||
diff = mp.diff
|
||||
diffs = mp.diffs
|
||||
diffun = mp.diffun
|
||||
differint = mp.differint
|
||||
taylor = mp.taylor
|
||||
pade = mp.pade
|
||||
polyval = mp.polyval
|
||||
polyroots = mp.polyroots
|
||||
fourier = mp.fourier
|
||||
fourierval = mp.fourierval
|
||||
sumem = mp.sumem
|
||||
chebyfit = mp.chebyfit
|
||||
limit = mp.limit
|
||||
|
||||
matrix = mp.matrix
|
||||
eye = mp.eye
|
||||
diag = mp.diag
|
||||
zeros = mp.zeros
|
||||
ones = mp.ones
|
||||
hilbert = mp.hilbert
|
||||
randmatrix = mp.randmatrix
|
||||
swap_row = mp.swap_row
|
||||
extend = mp.extend
|
||||
norm = mp.norm
|
||||
mnorm = mp.mnorm
|
||||
|
||||
lu_solve = mp.lu_solve
|
||||
lu = mp.lu
|
||||
unitvector = mp.unitvector
|
||||
inverse = mp.inverse
|
||||
residual = mp.residual
|
||||
qr_solve = mp.qr_solve
|
||||
cholesky = mp.cholesky
|
||||
cholesky_solve = mp.cholesky_solve
|
||||
det = mp.det
|
||||
cond = mp.cond
|
||||
|
||||
expm = mp.expm
|
||||
sqrtm = mp.sqrtm
|
||||
powm = mp.powm
|
||||
logm = mp.logm
|
||||
sinm = mp.sinm
|
||||
cosm = mp.cosm
|
||||
|
||||
mpf = mp.mpf
|
||||
j = mp.j
|
||||
exp = mp.exp
|
||||
expj = mp.expj
|
||||
expjpi = mp.expjpi
|
||||
ln = mp.ln
|
||||
im = mp.im
|
||||
re = mp.re
|
||||
inf = mp.inf
|
||||
ninf = mp.ninf
|
||||
sign = mp.sign
|
||||
|
||||
eps = mp.eps
|
||||
pi = mp.pi
|
||||
ln2 = mp.ln2
|
||||
ln10 = mp.ln10
|
||||
phi = mp.phi
|
||||
e = mp.e
|
||||
euler = mp.euler
|
||||
catalan = mp.catalan
|
||||
khinchin = mp.khinchin
|
||||
glaisher = mp.glaisher
|
||||
apery = mp.apery
|
||||
degree = mp.degree
|
||||
twinprime = mp.twinprime
|
||||
mertens = mp.mertens
|
||||
|
||||
ldexp = mp.ldexp
|
||||
frexp = mp.frexp
|
||||
|
||||
fsum = mp.fsum
|
||||
fdot = mp.fdot
|
||||
|
||||
sqrt = mp.sqrt
|
||||
cbrt = mp.cbrt
|
||||
exp = mp.exp
|
||||
ln = mp.ln
|
||||
log = mp.log
|
||||
log10 = mp.log10
|
||||
power = mp.power
|
||||
cos = mp.cos
|
||||
sin = mp.sin
|
||||
tan = mp.tan
|
||||
cosh = mp.cosh
|
||||
sinh = mp.sinh
|
||||
tanh = mp.tanh
|
||||
acos = mp.acos
|
||||
asin = mp.asin
|
||||
atan = mp.atan
|
||||
asinh = mp.asinh
|
||||
acosh = mp.acosh
|
||||
atanh = mp.atanh
|
||||
sec = mp.sec
|
||||
csc = mp.csc
|
||||
cot = mp.cot
|
||||
sech = mp.sech
|
||||
csch = mp.csch
|
||||
coth = mp.coth
|
||||
asec = mp.asec
|
||||
acsc = mp.acsc
|
||||
acot = mp.acot
|
||||
asech = mp.asech
|
||||
acsch = mp.acsch
|
||||
acoth = mp.acoth
|
||||
cospi = mp.cospi
|
||||
sinpi = mp.sinpi
|
||||
sinc = mp.sinc
|
||||
sincpi = mp.sincpi
|
||||
fabs = mp.fabs
|
||||
re = mp.re
|
||||
im = mp.im
|
||||
conj = mp.conj
|
||||
floor = mp.floor
|
||||
ceil = mp.ceil
|
||||
root = mp.root
|
||||
nthroot = mp.nthroot
|
||||
hypot = mp.hypot
|
||||
modf = mp.modf
|
||||
ldexp = mp.ldexp
|
||||
frexp = mp.frexp
|
||||
sign = mp.sign
|
||||
arg = mp.arg
|
||||
phase = mp.phase
|
||||
polar = mp.polar
|
||||
rect = mp.rect
|
||||
degrees = mp.degrees
|
||||
radians = mp.radians
|
||||
atan2 = mp.atan2
|
||||
fib = mp.fib
|
||||
fibonacci = mp.fibonacci
|
||||
lambertw = mp.lambertw
|
||||
zeta = mp.zeta
|
||||
altzeta = mp.altzeta
|
||||
gamma = mp.gamma
|
||||
factorial = mp.factorial
|
||||
fac = mp.fac
|
||||
fac2 = mp.fac2
|
||||
beta = mp.beta
|
||||
betainc = mp.betainc
|
||||
psi = mp.psi
|
||||
#psi0 = mp.psi0
|
||||
#psi1 = mp.psi1
|
||||
#psi2 = mp.psi2
|
||||
#psi3 = mp.psi3
|
||||
polygamma = mp.polygamma
|
||||
digamma = mp.digamma
|
||||
#trigamma = mp.trigamma
|
||||
#tetragamma = mp.tetragamma
|
||||
#pentagamma = mp.pentagamma
|
||||
harmonic = mp.harmonic
|
||||
bernoulli = mp.bernoulli
|
||||
bernfrac = mp.bernfrac
|
||||
stieltjes = mp.stieltjes
|
||||
hurwitz = mp.hurwitz
|
||||
dirichlet = mp.dirichlet
|
||||
bernpoly = mp.bernpoly
|
||||
eulerpoly = mp.eulerpoly
|
||||
eulernum = mp.eulernum
|
||||
polylog = mp.polylog
|
||||
clsin = mp.clsin
|
||||
clcos = mp.clcos
|
||||
gammainc = mp.gammainc
|
||||
gammaprod = mp.gammaprod
|
||||
binomial = mp.binomial
|
||||
rf = mp.rf
|
||||
ff = mp.ff
|
||||
hyper = mp.hyper
|
||||
hyp0f1 = mp.hyp0f1
|
||||
hyp1f1 = mp.hyp1f1
|
||||
hyp1f2 = mp.hyp1f2
|
||||
hyp2f1 = mp.hyp2f1
|
||||
hyp2f2 = mp.hyp2f2
|
||||
hyp2f0 = mp.hyp2f0
|
||||
hyp2f3 = mp.hyp2f3
|
||||
hyp3f2 = mp.hyp3f2
|
||||
hyperu = mp.hyperu
|
||||
hypercomb = mp.hypercomb
|
||||
meijerg = mp.meijerg
|
||||
appellf1 = mp.appellf1
|
||||
erf = mp.erf
|
||||
erfc = mp.erfc
|
||||
erfi = mp.erfi
|
||||
erfinv = mp.erfinv
|
||||
npdf = mp.npdf
|
||||
ncdf = mp.ncdf
|
||||
expint = mp.expint
|
||||
e1 = mp.e1
|
||||
ei = mp.ei
|
||||
li = mp.li
|
||||
ci = mp.ci
|
||||
si = mp.si
|
||||
chi = mp.chi
|
||||
shi = mp.shi
|
||||
fresnels = mp.fresnels
|
||||
fresnelc = mp.fresnelc
|
||||
airyai = mp.airyai
|
||||
airybi = mp.airybi
|
||||
ellipe = mp.ellipe
|
||||
ellipk = mp.ellipk
|
||||
agm = mp.agm
|
||||
jacobi = mp.jacobi
|
||||
chebyt = mp.chebyt
|
||||
chebyu = mp.chebyu
|
||||
legendre = mp.legendre
|
||||
legenp = mp.legenp
|
||||
legenq = mp.legenq
|
||||
hermite = mp.hermite
|
||||
gegenbauer = mp.gegenbauer
|
||||
laguerre = mp.laguerre
|
||||
spherharm = mp.spherharm
|
||||
besselj = mp.besselj
|
||||
j0 = mp.j0
|
||||
j1 = mp.j1
|
||||
besseli = mp.besseli
|
||||
bessely = mp.bessely
|
||||
besselk = mp.besselk
|
||||
hankel1 = mp.hankel1
|
||||
hankel2 = mp.hankel2
|
||||
struveh = mp.struveh
|
||||
struvel = mp.struvel
|
||||
whitm = mp.whitm
|
||||
whitw = mp.whitw
|
||||
ber = mp.ber
|
||||
bei = mp.bei
|
||||
ker = mp.ker
|
||||
kei = mp.kei
|
||||
coulombc = mp.coulombc
|
||||
coulombf = mp.coulombf
|
||||
coulombg = mp.coulombg
|
||||
lambertw = mp.lambertw
|
||||
barnesg = mp.barnesg
|
||||
superfac = mp.superfac
|
||||
hyperfac = mp.hyperfac
|
||||
loggamma = mp.loggamma
|
||||
siegeltheta = mp.siegeltheta
|
||||
siegelz = mp.siegelz
|
||||
grampoint = mp.grampoint
|
||||
zetazero = mp.zetazero
|
||||
riemannr = mp.riemannr
|
||||
primepi = mp.primepi
|
||||
primepi2 = mp.primepi2
|
||||
primezeta = mp.primezeta
|
||||
bell = mp.bell
|
||||
polyexp = mp.polyexp
|
||||
expm1 = mp.expm1
|
||||
powm1 = mp.powm1
|
||||
unitroots = mp.unitroots
|
||||
cyclotomic = mp.cyclotomic
|
||||
|
||||
|
||||
# be careful when changing this name, don't use test*!
|
||||
def runtests():
|
||||
"""
|
||||
Run all mpmath tests and print output.
|
||||
"""
|
||||
import os.path
|
||||
from inspect import getsourcefile
|
||||
import tests.runtests as tests
|
||||
testdir = os.path.dirname(os.path.abspath(getsourcefile(tests)))
|
||||
importdir = os.path.abspath(testdir + '/../..')
|
||||
tests.testit(importdir, testdir)
|
||||
|
||||
def doctests():
|
||||
try:
|
||||
import psyco; psyco.full()
|
||||
except ImportError:
|
||||
pass
|
||||
import sys
|
||||
from timeit import default_timer as clock
|
||||
filter = []
|
||||
for i, arg in enumerate(sys.argv):
|
||||
if '__init__.py' in arg:
|
||||
filter = [sn for sn in sys.argv[i+1:] if not sn.startswith("-")]
|
||||
break
|
||||
import doctest
|
||||
globs = globals().copy()
|
||||
for obj in globs: #sorted(globs.keys()):
|
||||
if filter:
|
||||
if not sum([pat in obj for pat in filter]):
|
||||
continue
|
||||
print obj,
|
||||
t1 = clock()
|
||||
doctest.run_docstring_examples(globs[obj], {}, verbose=("-v" in sys.argv))
|
||||
t2 = clock()
|
||||
print round(t2-t1, 3)
|
||||
|
||||
if __name__ == '__main__':
|
||||
doctests()
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import calculus
|
||||
# XXX: hack to set methods
|
||||
import approximation
|
||||
import differentiation
|
||||
import extrapolation
|
||||
import polynomials
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
from calculus import defun
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# Approximation methods #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
# The Chebyshev approximation formula is given at:
|
||||
# http://mathworld.wolfram.com/ChebyshevApproximationFormula.html
|
||||
|
||||
# The only major changes in the following code is that we return the
|
||||
# expanded polynomial coefficients instead of Chebyshev coefficients,
|
||||
# and that we automatically transform [a,b] -> [-1,1] and back
|
||||
# for convenience.
|
||||
|
||||
# Coefficient in Chebyshev approximation
|
||||
def chebcoeff(ctx,f,a,b,j,N):
|
||||
s = ctx.mpf(0)
|
||||
h = ctx.mpf(0.5)
|
||||
for k in range(1, N+1):
|
||||
t = ctx.cos(ctx.pi*(k-h)/N)
|
||||
s += f(t*(b-a)*h + (b+a)*h) * ctx.cos(ctx.pi*j*(k-h)/N)
|
||||
return 2*s/N
|
||||
|
||||
# Generate Chebyshev polynomials T_n(ax+b) in expanded form
|
||||
def chebT(ctx, a=1, b=0):
|
||||
Tb = [1]
|
||||
yield Tb
|
||||
Ta = [b, a]
|
||||
while 1:
|
||||
yield Ta
|
||||
# Recurrence: T[n+1](ax+b) = 2*(ax+b)*T[n](ax+b) - T[n-1](ax+b)
|
||||
Tmp = [0] + [2*a*t for t in Ta]
|
||||
for i, c in enumerate(Ta): Tmp[i] += 2*b*c
|
||||
for i, c in enumerate(Tb): Tmp[i] -= c
|
||||
Ta, Tb = Tmp, Ta
|
||||
|
||||
@defun
|
||||
def chebyfit(ctx, f, interval, N, error=False):
|
||||
r"""
|
||||
Computes a polynomial of degree `N-1` that approximates the
|
||||
given function `f` on the interval `[a, b]`. With ``error=True``,
|
||||
:func:`chebyfit` also returns an accurate estimate of the
|
||||
maximum absolute error; that is, the maximum value of
|
||||
`|f(x) - P(x)|` for `x \in [a, b]`.
|
||||
|
||||
:func:`chebyfit` uses the Chebyshev approximation formula,
|
||||
which gives a nearly optimal solution: that is, the maximum
|
||||
error of the approximating polynomial is very close to
|
||||
the smallest possible for any polynomial of the same degree.
|
||||
|
||||
Chebyshev approximation is very useful if one needs repeated
|
||||
evaluation of an expensive function, such as function defined
|
||||
implicitly by an integral or a differential equation. (For
|
||||
example, it could be used to turn a slow mpmath function
|
||||
into a fast machine-precision version of the same.)
|
||||
|
||||
**Examples**
|
||||
|
||||
Here we use :func:`chebyfit` to generate a low-degree approximation
|
||||
of `f(x) = \cos(x)`, valid on the interval `[1, 2]`::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> poly, err = chebyfit(cos, [1, 2], 5, error=True)
|
||||
>>> nprint(poly)
|
||||
[0.00291682, 0.146166, -0.732491, 0.174141, 0.949553]
|
||||
>>> nprint(err, 12)
|
||||
1.61351758081e-5
|
||||
|
||||
The polynomial can be evaluated using ``polyval``::
|
||||
|
||||
>>> nprint(polyval(poly, 1.6), 12)
|
||||
-0.0291858904138
|
||||
>>> nprint(cos(1.6), 12)
|
||||
-0.0291995223013
|
||||
|
||||
Sampling the true error at 1000 points shows that the error
|
||||
estimate generated by ``chebyfit`` is remarkably good::
|
||||
|
||||
>>> error = lambda x: abs(cos(x) - polyval(poly, x))
|
||||
>>> nprint(max([error(1+n/1000.) for n in range(1000)]), 12)
|
||||
1.61349954245e-5
|
||||
|
||||
**Choice of degree**
|
||||
|
||||
The degree `N` can be set arbitrarily high, to obtain an
|
||||
arbitrarily good approximation. As a rule of thumb, an
|
||||
`N`-term Chebyshev approximation is good to `N/(b-a)` decimal
|
||||
places on a unit interval (although this depends on how
|
||||
well-behaved `f` is). The cost grows accordingly: ``chebyfit``
|
||||
evaluates the function `(N^2)/2` times to compute the
|
||||
coefficients and an additional `N` times to estimate the error.
|
||||
|
||||
**Possible issues**
|
||||
|
||||
One should be careful to use a sufficiently high working
|
||||
precision both when calling ``chebyfit`` and when evaluating
|
||||
the resulting polynomial, as the polynomial is sometimes
|
||||
ill-conditioned. It is for example difficult to reach
|
||||
15-digit accuracy when evaluating the polynomial using
|
||||
machine precision floats, no matter the theoretical
|
||||
accuracy of the polynomial. (The option to return the
|
||||
coefficients in Chebyshev form should be made available
|
||||
in the future.)
|
||||
|
||||
It is important to note the Chebyshev approximation works
|
||||
poorly if `f` is not smooth. A function containing singularities,
|
||||
rapid oscillation, etc can be approximated more effectively by
|
||||
multiplying it by a weight function that cancels out the
|
||||
nonsmooth features, or by dividing the interval into several
|
||||
segments.
|
||||
"""
|
||||
a, b = ctx._as_points(interval)
|
||||
orig = ctx.prec
|
||||
try:
|
||||
ctx.prec = orig + int(N**0.5) + 20
|
||||
c = [chebcoeff(ctx,f,a,b,k,N) for k in range(N)]
|
||||
d = [ctx.zero] * N
|
||||
d[0] = -c[0]/2
|
||||
h = ctx.mpf(0.5)
|
||||
T = chebT(ctx, ctx.mpf(2)/(b-a), ctx.mpf(-1)*(b+a)/(b-a))
|
||||
for k in range(N):
|
||||
Tk = T.next()
|
||||
for i in range(len(Tk)):
|
||||
d[i] += c[k]*Tk[i]
|
||||
d = d[::-1]
|
||||
# Estimate maximum error
|
||||
err = ctx.zero
|
||||
for k in range(N):
|
||||
x = ctx.cos(ctx.pi*k/N) * (b-a)*h + (b+a)*h
|
||||
err = max(err, abs(f(x) - ctx.polyval(d, x)))
|
||||
finally:
|
||||
ctx.prec = orig
|
||||
if error:
|
||||
return d, +err
|
||||
else:
|
||||
return d
|
||||
|
||||
@defun
|
||||
def fourier(ctx, f, interval, N):
|
||||
r"""
|
||||
Computes the Fourier series of degree `N` of the given function
|
||||
on the interval `[a, b]`. More precisely, :func:`fourier` returns
|
||||
two lists `(c, s)` of coefficients (the cosine series and sine
|
||||
series, respectively), such that
|
||||
|
||||
.. math ::
|
||||
|
||||
f(x) \sim \sum_{k=0}^N
|
||||
c_k \cos(k m) + s_k \sin(k m)
|
||||
|
||||
where `m = 2 \pi / (b-a)`.
|
||||
|
||||
Note that many texts define the first coefficient as `2 c_0` instead
|
||||
of `c_0`. The easiest way to evaluate the computed series correctly
|
||||
is to pass it to :func:`fourierval`.
|
||||
|
||||
**Examples**
|
||||
|
||||
The function `f(x) = x` has a simple Fourier series on the standard
|
||||
interval `[-\pi, \pi]`. The cosine coefficients are all zero (because
|
||||
the function has odd symmetry), and the sine coefficients are
|
||||
rational numbers::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> c, s = fourier(lambda x: x, [-pi, pi], 5)
|
||||
>>> nprint(c)
|
||||
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
>>> nprint(s)
|
||||
[0.0, 2.0, -1.0, 0.666667, -0.5, 0.4]
|
||||
|
||||
This computes a Fourier series of a nonsymmetric function on
|
||||
a nonstandard interval::
|
||||
|
||||
>>> I = [-1, 1.5]
|
||||
>>> f = lambda x: x**2 - 4*x + 1
|
||||
>>> cs = fourier(f, I, 4)
|
||||
>>> nprint(cs[0])
|
||||
[0.583333, 1.12479, -1.27552, 0.904708, -0.441296]
|
||||
>>> nprint(cs[1])
|
||||
[0.0, -2.6255, 0.580905, 0.219974, -0.540057]
|
||||
|
||||
It is instructive to plot a function along with its truncated
|
||||
Fourier series::
|
||||
|
||||
>>> plot([f, lambda x: fourierval(cs, I, x)], I) #doctest: +SKIP
|
||||
|
||||
Fourier series generally converge slowly (and may not converge
|
||||
pointwise). For example, if `f(x) = \cosh(x)`, a 10-term Fourier
|
||||
series gives an `L^2` error corresponding to 2-digit accuracy::
|
||||
|
||||
>>> I = [-1, 1]
|
||||
>>> cs = fourier(cosh, I, 9)
|
||||
>>> g = lambda x: (cosh(x) - fourierval(cs, I, x))**2
|
||||
>>> nprint(sqrt(quad(g, I)))
|
||||
0.00467963
|
||||
|
||||
:func:`fourier` uses numerical quadrature. For nonsmooth functions,
|
||||
the accuracy (and speed) can be improved by including all singular
|
||||
points in the interval specification::
|
||||
|
||||
>>> nprint(fourier(abs, [-1, 1], 0), 10)
|
||||
([0.5000441648], [0.0])
|
||||
>>> nprint(fourier(abs, [-1, 0, 1], 0), 10)
|
||||
([0.5], [0.0])
|
||||
|
||||
"""
|
||||
interval = ctx._as_points(interval)
|
||||
a = interval[0]
|
||||
b = interval[-1]
|
||||
L = b-a
|
||||
cos_series = []
|
||||
sin_series = []
|
||||
cutoff = ctx.eps*10
|
||||
for n in xrange(N+1):
|
||||
m = 2*n*ctx.pi/L
|
||||
an = 2*ctx.quadgl(lambda t: f(t)*ctx.cos(m*t), interval)/L
|
||||
bn = 2*ctx.quadgl(lambda t: f(t)*ctx.sin(m*t), interval)/L
|
||||
if n == 0:
|
||||
an /= 2
|
||||
if abs(an) < cutoff: an = ctx.zero
|
||||
if abs(bn) < cutoff: bn = ctx.zero
|
||||
cos_series.append(an)
|
||||
sin_series.append(bn)
|
||||
return cos_series, sin_series
|
||||
|
||||
@defun
|
||||
def fourierval(ctx, series, interval, x):
|
||||
"""
|
||||
Evaluates a Fourier series (in the format computed by
|
||||
by :func:`fourier` for the given interval) at the point `x`.
|
||||
|
||||
The series should be a pair `(c, s)` where `c` is the
|
||||
cosine series and `s` is the sine series. The two lists
|
||||
need not have the same length.
|
||||
"""
|
||||
cs, ss = series
|
||||
ab = ctx._as_points(interval)
|
||||
a = interval[0]
|
||||
b = interval[-1]
|
||||
m = 2*ctx.pi/(ab[-1]-ab[0])
|
||||
s = ctx.zero
|
||||
s += ctx.fsum(cs[n]*ctx.cos(m*n*x) for n in xrange(len(cs)) if cs[n])
|
||||
s += ctx.fsum(ss[n]*ctx.sin(m*n*x) for n in xrange(len(ss)) if ss[n])
|
||||
return s
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
class CalculusMethods(object):
|
||||
pass
|
||||
|
||||
def defun(f):
|
||||
setattr(CalculusMethods, f.__name__, f)
|
||||
|
|
@ -0,0 +1,438 @@
|
|||
from calculus import defun
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# Differentiation #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
@defun
|
||||
def difference_delta(ctx, s, n):
|
||||
r"""
|
||||
Given a sequence `(s_k)` containing at least `n+1` items, returns the
|
||||
`n`-th forward difference,
|
||||
|
||||
.. math ::
|
||||
|
||||
\Delta^n = \sum_{k=0}^{\infty} (-1)^{k+n} {n \choose k} s_k.
|
||||
"""
|
||||
n = int(n)
|
||||
d = ctx.zero
|
||||
b = (-1) ** (n & 1)
|
||||
for k in xrange(n+1):
|
||||
d += b * s[k]
|
||||
b = (b * (k-n)) // (k+1)
|
||||
return d
|
||||
|
||||
@defun
|
||||
def diff(ctx, f, x, n=1, method='step', scale=1, direction=0):
|
||||
r"""
|
||||
Numerically computes the derivative of `f`, `f'(x)`. Optionally,
|
||||
computes the `n`-th derivative, `f^{(n)}(x)`, for any order `n`.
|
||||
|
||||
**Basic examples**
|
||||
|
||||
Derivatives of a simple function::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> diff(lambda x: x**2 + x, 1.0)
|
||||
3.0
|
||||
>>> diff(lambda x: x**2 + x, 1.0, 2)
|
||||
2.0
|
||||
>>> diff(lambda x: x**2 + x, 1.0, 3)
|
||||
0.0
|
||||
|
||||
The exponential function is invariant under differentiation::
|
||||
|
||||
>>> nprint([diff(exp, 3, n) for n in range(5)])
|
||||
[20.0855, 20.0855, 20.0855, 20.0855, 20.0855]
|
||||
|
||||
**Method**
|
||||
|
||||
One of two differentiation algorithms can be chosen with the
|
||||
``method`` keyword argument. The two options are ``'step'``,
|
||||
and ``'quad'``. The default method is ``'step'``.
|
||||
|
||||
``'step'``:
|
||||
|
||||
The derivative is computed using a finite difference
|
||||
approximation, with a small step h. This requires n+1 function
|
||||
evaluations and must be performed at (n+1) times the target
|
||||
precision. Accordingly, f must support fast evaluation at high
|
||||
precision.
|
||||
|
||||
``'quad'``:
|
||||
|
||||
The derivative is computed using complex
|
||||
numerical integration. This requires a larger number of function
|
||||
evaluations, but the advantage is that not much extra precision
|
||||
is required. For high order derivatives, this method may thus
|
||||
be faster if f is very expensive to evaluate at high precision.
|
||||
|
||||
With ``'quad'`` the result is likely to have a small imaginary
|
||||
component even if the derivative is actually real::
|
||||
|
||||
>>> diff(sqrt, 1, method='quad') # doctest:+ELLIPSIS
|
||||
(0.5 - 9.44...e-27j)
|
||||
|
||||
**Scale**
|
||||
|
||||
The scale option specifies the scale of variation of f. The step
|
||||
size in the finite difference is taken to be approximately
|
||||
eps*scale. Thus, for example if `f(x) = \cos(1000 x)`, the scale
|
||||
should be set to 1/1000 and if `f(x) = \cos(x/1000)`, the scale
|
||||
should be 1000. By default, scale = 1.
|
||||
|
||||
(In practice, the default scale will work even for `\cos(1000 x)` or
|
||||
`\cos(x/1000)`. Changing this parameter is a good idea if the scale
|
||||
is something *preposterous*.)
|
||||
|
||||
If numerical integration is used, the radius of integration is
|
||||
taken to be equal to scale/2. Note that f must not have any
|
||||
singularities within the circle of radius scale/2 centered around
|
||||
x. If possible, a larger scale value is preferable because it
|
||||
typically makes the integration faster and more accurate.
|
||||
|
||||
**Direction**
|
||||
|
||||
By default, :func:`diff` uses a central difference approximation.
|
||||
This corresponds to direction=0. Alternatively, it can compute a
|
||||
left difference (direction=-1) or right difference (direction=1).
|
||||
This is useful for computing left- or right-sided derivatives
|
||||
of nonsmooth functions:
|
||||
|
||||
>>> diff(abs, 0, direction=0)
|
||||
0.0
|
||||
>>> diff(abs, 0, direction=1)
|
||||
1.0
|
||||
>>> diff(abs, 0, direction=-1)
|
||||
-1.0
|
||||
|
||||
More generally, if the direction is nonzero, a right difference
|
||||
is computed where the step size is multiplied by sign(direction).
|
||||
For example, with direction=+j, the derivative from the positive
|
||||
imaginary direction will be computed.
|
||||
|
||||
This option only makes sense with method='step'. If integration
|
||||
is used, it is assumed that f is analytic, implying that the
|
||||
derivative is the same in all directions.
|
||||
|
||||
"""
|
||||
if n == 0:
|
||||
return f(ctx.convert(x))
|
||||
orig = ctx.prec
|
||||
try:
|
||||
if method == 'step':
|
||||
ctx.prec = (orig+20) * (n+1)
|
||||
h = ctx.ldexp(scale, -orig-10)
|
||||
# Applying the finite difference formula recursively n times,
|
||||
# we get a step sum weighted by a row of binomial coefficients
|
||||
# Directed: steps x, x+h, ... x+n*h
|
||||
if direction:
|
||||
h *= ctx.sign(direction)
|
||||
steps = xrange(n+1)
|
||||
norm = h**n
|
||||
# Central: steps x-n*h, x-(n-2)*h ..., x, ..., x+(n-2)*h, x+n*h
|
||||
else:
|
||||
steps = xrange(-n, n+1, 2)
|
||||
norm = (2*h)**n
|
||||
v = ctx.difference_delta([f(x+k*h) for k in steps], n)
|
||||
v = v / norm
|
||||
elif method == 'quad':
|
||||
ctx.prec += 10
|
||||
radius = ctx.mpf(scale)/2
|
||||
def g(t):
|
||||
rei = radius*ctx.expj(t)
|
||||
z = x + rei
|
||||
return f(z) / rei**n
|
||||
d = ctx.quadts(g, [0, 2*ctx.pi])
|
||||
v = d * ctx.factorial(n) / (2*ctx.pi)
|
||||
else:
|
||||
raise ValueError("unknown method: %r" % method)
|
||||
finally:
|
||||
ctx.prec = orig
|
||||
return +v
|
||||
|
||||
@defun
|
||||
def diffs(ctx, f, x, n=None, method='step', scale=1, direction=0):
|
||||
r"""
|
||||
Returns a generator that yields the sequence of derivatives
|
||||
|
||||
.. math ::
|
||||
|
||||
f(x), f'(x), f''(x), \ldots, f^{(k)}(x), \ldots
|
||||
|
||||
With ``method='step'``, :func:`diffs` uses only `O(k)`
|
||||
function evaluations to generate the first `k` derivatives,
|
||||
rather than the roughly `O(k^2)` evaluations
|
||||
required if one calls :func:`diff` `k` separate times.
|
||||
|
||||
With `n < \infty`, the generator stops as soon as the
|
||||
`n`-th derivative has been generated. If the exact number of
|
||||
needed derivatives is known in advance, this is further
|
||||
slightly more efficient.
|
||||
|
||||
**Examples**
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15
|
||||
>>> nprint(list(diffs(cos, 1, 5)))
|
||||
[0.540302, -0.841471, -0.540302, 0.841471, 0.540302, -0.841471]
|
||||
>>> for i, d in zip(range(6), diffs(cos, 1)): print i, d
|
||||
...
|
||||
0 0.54030230586814
|
||||
1 -0.841470984807897
|
||||
2 -0.54030230586814
|
||||
3 0.841470984807897
|
||||
4 0.54030230586814
|
||||
5 -0.841470984807897
|
||||
|
||||
"""
|
||||
if n is None:
|
||||
n = ctx.inf
|
||||
else:
|
||||
n = int(n)
|
||||
|
||||
if method != 'step':
|
||||
k = 0
|
||||
while k < n:
|
||||
yield ctx.diff(f, x, k)
|
||||
k += 1
|
||||
return
|
||||
|
||||
targetprec = ctx.prec
|
||||
|
||||
def getvalues(m):
|
||||
callprec = ctx.prec
|
||||
try:
|
||||
ctx.prec = workprec = (targetprec+20) * (m+1)
|
||||
h = ctx.ldexp(scale, -targetprec-10)
|
||||
if direction:
|
||||
h *= ctx.sign(direction)
|
||||
y = [f(x+h*k) for k in xrange(m+1)]
|
||||
hnorm = h
|
||||
else:
|
||||
y = [f(x+h*k) for k in xrange(-m, m+1, 2)]
|
||||
hnorm = 2*h
|
||||
return y, hnorm, workprec
|
||||
finally:
|
||||
ctx.prec = callprec
|
||||
|
||||
yield f(ctx.convert(x))
|
||||
if n < 1:
|
||||
return
|
||||
|
||||
if n == ctx.inf:
|
||||
A, B = 1, 2
|
||||
else:
|
||||
A, B = 1, n+1
|
||||
|
||||
while 1:
|
||||
y, hnorm, workprec = getvalues(B)
|
||||
for k in xrange(A, B):
|
||||
try:
|
||||
callprec = ctx.prec
|
||||
ctx.prec = workprec
|
||||
d = ctx.difference_delta(y, k) / hnorm**k
|
||||
finally:
|
||||
ctx.prec = callprec
|
||||
yield +d
|
||||
if k >= n:
|
||||
return
|
||||
A, B = B, int(A*1.4+1)
|
||||
B = min(B, n)
|
||||
|
||||
@defun
|
||||
def differint(ctx, f, x, n=1, x0=0):
|
||||
r"""
|
||||
Calculates the Riemann-Liouville differintegral, or fractional
|
||||
derivative, defined by
|
||||
|
||||
.. math ::
|
||||
|
||||
\,_{x_0}{\mathbb{D}}^n_xf(x) \frac{1}{\Gamma(m-n)} \frac{d^m}{dx^m}
|
||||
\int_{x_0}^{x}(x-t)^{m-n-1}f(t)dt
|
||||
|
||||
where `f` is a given (presumably well-behaved) function,
|
||||
`x` is the evaluation point, `n` is the order, and `x_0` is
|
||||
the reference point of integration (`m` is an arbitrary
|
||||
parameter selected automatically).
|
||||
|
||||
With `n = 1`, this is just the standard derivative `f'(x)`; with `n = 2`,
|
||||
the second derivative `f''(x)`, etc. With `n = -1`, it gives
|
||||
`\int_{x_0}^x f(t) dt`, with `n = -2`
|
||||
it gives `\int_{x_0}^x \left( \int_{x_0}^t f(u) du \right) dt`, etc.
|
||||
|
||||
As `n` is permitted to be any number, this operator generalizes
|
||||
iterated differentiation and iterated integration to a single
|
||||
operator with a continuous order parameter.
|
||||
|
||||
**Examples**
|
||||
|
||||
There is an exact formula for the fractional derivative of a
|
||||
monomial `x^p`, which may be used as a reference. For example,
|
||||
the following gives a half-derivative (order 0.5)::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> x = mpf(3); p = 2; n = 0.5
|
||||
>>> differint(lambda t: t**p, x, n)
|
||||
7.81764019044672
|
||||
>>> gamma(p+1)/gamma(p-n+1) * x**(p-n)
|
||||
7.81764019044672
|
||||
|
||||
Another useful test function is the exponential function, whose
|
||||
integration / differentiation formula easy generalizes
|
||||
to arbitrary order. Here we first compute a third derivative,
|
||||
and then a triply nested integral. (The reference point `x_0`
|
||||
is set to `-\infty` to avoid nonzero endpoint terms.)::
|
||||
|
||||
>>> differint(lambda x: exp(pi*x), -1.5, 3)
|
||||
0.278538406900792
|
||||
>>> exp(pi*-1.5) * pi**3
|
||||
0.278538406900792
|
||||
>>> differint(lambda x: exp(pi*x), 3.5, -3, -inf)
|
||||
1922.50563031149
|
||||
>>> exp(pi*3.5) / pi**3
|
||||
1922.50563031149
|
||||
|
||||
However, for noninteger `n`, the differentiation formula for the
|
||||
exponential function must be modified to give the same result as the
|
||||
Riemann-Liouville differintegral::
|
||||
|
||||
>>> x = mpf(3.5)
|
||||
>>> c = pi
|
||||
>>> n = 1+2*j
|
||||
>>> differint(lambda x: exp(c*x), x, n)
|
||||
(-123295.005390743 + 140955.117867654j)
|
||||
>>> x**(-n) * exp(c)**x * (x*c)**n * gammainc(-n, 0, x*c) / gamma(-n)
|
||||
(-123295.005390743 + 140955.117867654j)
|
||||
|
||||
|
||||
"""
|
||||
m = max(int(ctx.ceil(ctx.re(n)))+1, 1)
|
||||
r = m-n-1
|
||||
g = lambda x: ctx.quad(lambda t: (x-t)**r * f(t), [x0, x])
|
||||
return ctx.diff(g, x, m) / ctx.gamma(m-n)
|
||||
|
||||
@defun
|
||||
def diffun(ctx, f, n=1, **options):
|
||||
"""
|
||||
Given a function f, returns a function g(x) that evaluates the nth
|
||||
derivative f^(n)(x)::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> cos2 = diffun(sin)
|
||||
>>> sin2 = diffun(sin, 4)
|
||||
>>> cos(1.3), cos2(1.3)
|
||||
(0.267498828624587, 0.267498828624587)
|
||||
>>> sin(1.3), sin2(1.3)
|
||||
(0.963558185417193, 0.963558185417193)
|
||||
|
||||
The function f must support arbitrary precision evaluation.
|
||||
See :func:`diff` for additional details and supported
|
||||
keyword options.
|
||||
"""
|
||||
if n == 0:
|
||||
return f
|
||||
def g(x):
|
||||
return ctx.diff(f, x, n, **options)
|
||||
return g
|
||||
|
||||
@defun
|
||||
def taylor(ctx, f, x, n, **options):
|
||||
r"""
|
||||
Produces a degree-`n` Taylor polynomial around the point `x` of the
|
||||
given function `f`. The coefficients are returned as a list.
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> nprint(chop(taylor(sin, 0, 5)))
|
||||
[0.0, 1.0, 0.0, -0.166667, 0.0, 0.00833333]
|
||||
|
||||
The coefficients are computed using high-order numerical
|
||||
differentiation. The function must be possible to evaluate
|
||||
to arbitrary precision. See :func:`diff` for additional details
|
||||
and supported keyword options.
|
||||
|
||||
Note that to evaluate the Taylor polynomial as an approximation
|
||||
of `f`, e.g. with :func:`polyval`, the coefficients must be reversed,
|
||||
and the point of the Taylor expansion must be subtracted from
|
||||
the argument:
|
||||
|
||||
>>> p = taylor(exp, 2.0, 10)
|
||||
>>> polyval(p[::-1], 2.5 - 2.0)
|
||||
12.1824939606092
|
||||
>>> exp(2.5)
|
||||
12.1824939607035
|
||||
|
||||
"""
|
||||
return [d/ctx.factorial(i) for i, d in enumerate(ctx.diffs(f, x, n, **options))]
|
||||
|
||||
@defun
|
||||
def pade(ctx, a, L, M):
|
||||
r"""
|
||||
Computes a Pade approximation of degree `(L, M)` to a function.
|
||||
Given at least `L+M+1` Taylor coefficients `a` approximating
|
||||
a function `A(x)`, :func:`pade` returns coefficients of
|
||||
polynomials `P, Q` satisfying
|
||||
|
||||
.. math ::
|
||||
|
||||
P = \sum_{k=0}^L p_k x^k
|
||||
|
||||
Q = \sum_{k=0}^M q_k x^k
|
||||
|
||||
Q_0 = 1
|
||||
|
||||
A(x) Q(x) = P(x) + O(x^{L+M+1})
|
||||
|
||||
`P(x)/Q(x)` can provide a good approximation to an analytic function
|
||||
beyond the radius of convergence of its Taylor series (example
|
||||
from G.A. Baker 'Essentials of Pade Approximants' Academic Press,
|
||||
Ch.1A)::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> one = mpf(1)
|
||||
>>> def f(x):
|
||||
... return sqrt((one + 2*x)/(one + x))
|
||||
...
|
||||
>>> a = taylor(f, 0, 6)
|
||||
>>> p, q = pade(a, 3, 3)
|
||||
>>> x = 10
|
||||
>>> polyval(p[::-1], x)/polyval(q[::-1], x)
|
||||
1.38169105566806
|
||||
>>> f(x)
|
||||
1.38169855941551
|
||||
|
||||
"""
|
||||
# To determine L+1 coefficients of P and M coefficients of Q
|
||||
# L+M+1 coefficients of A must be provided
|
||||
assert(len(a) >= L+M+1)
|
||||
|
||||
if M == 0:
|
||||
if L == 0:
|
||||
return [ctx.one], [ctx.one]
|
||||
else:
|
||||
return a[:L+1], [ctx.one]
|
||||
|
||||
# Solve first
|
||||
# a[L]*q[1] + ... + a[L-M+1]*q[M] = -a[L+1]
|
||||
# ...
|
||||
# a[L+M-1]*q[1] + ... + a[L]*q[M] = -a[L+M]
|
||||
A = ctx.matrix(M)
|
||||
for j in range(M):
|
||||
for i in range(min(M, L+j+1)):
|
||||
A[j, i] = a[L+j-i]
|
||||
v = -ctx.matrix(a[(L+1):(L+M+1)])
|
||||
x = ctx.lu_solve(A, v)
|
||||
q = [ctx.one] + list(x)
|
||||
# compute p
|
||||
p = [0]*(L+1)
|
||||
for i in range(L+1):
|
||||
s = a[i]
|
||||
for j in range(1, min(M,i) + 1):
|
||||
s += q[j]*a[i-j]
|
||||
p[i] = s
|
||||
return p, q
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,287 @@
|
|||
from bisect import bisect
|
||||
|
||||
class ODEMethods(object):
|
||||
pass
|
||||
|
||||
def ode_taylor(ctx, derivs, x0, y0, tol_prec, n):
|
||||
h = tol = ctx.ldexp(1, -tol_prec)
|
||||
dim = len(y0)
|
||||
xs = [x0]
|
||||
ys = [y0]
|
||||
x = x0
|
||||
y = y0
|
||||
orig = ctx.prec
|
||||
try:
|
||||
ctx.prec = orig*(1+n)
|
||||
# Use n steps with Euler's method to get
|
||||
# evaluation points for derivatives
|
||||
for i in range(n):
|
||||
fxy = derivs(x, y)
|
||||
y = [y[i]+h*fxy[i] for i in xrange(len(y))]
|
||||
x += h
|
||||
xs.append(x)
|
||||
ys.append(y)
|
||||
# Compute derivatives
|
||||
ser = [[] for d in range(dim)]
|
||||
for j in range(n+1):
|
||||
s = [0]*dim
|
||||
b = (-1) ** (j & 1)
|
||||
k = 1
|
||||
for i in range(j+1):
|
||||
for d in range(dim):
|
||||
s[d] += b * ys[i][d]
|
||||
b = (b * (j-k+1)) // (-k)
|
||||
k += 1
|
||||
scale = h**(-j) / ctx.fac(j)
|
||||
for d in range(dim):
|
||||
s[d] = s[d] * scale
|
||||
ser[d].append(s[d])
|
||||
finally:
|
||||
ctx.prec = orig
|
||||
# Estimate radius for which we can get full accuracy.
|
||||
# XXX: do this right for zeros
|
||||
radius = ctx.one
|
||||
for ts in ser:
|
||||
if ts[-1]:
|
||||
radius = min(radius, ctx.nthroot(tol/abs(ts[-1]), n))
|
||||
radius /= 2 # XXX
|
||||
return ser, x0+radius
|
||||
|
||||
def odefun(ctx, F, x0, y0, tol=None, degree=None, method='taylor', verbose=False):
|
||||
r"""
|
||||
Returns a function `y(x) = [y_0(x), y_1(x), \ldots, y_n(x)]`
|
||||
that is a numerical solution of the `n+1`-dimensional first-order
|
||||
ordinary differential equation (ODE) system
|
||||
|
||||
.. math ::
|
||||
|
||||
y_0'(x) = F_0(x, [y_0(x), y_1(x), \ldots, y_n(x)])
|
||||
|
||||
y_1'(x) = F_1(x, [y_0(x), y_1(x), \ldots, y_n(x)])
|
||||
|
||||
\vdots
|
||||
|
||||
y_n'(x) = F_n(x, [y_0(x), y_1(x), \ldots, y_n(x)])
|
||||
|
||||
The derivatives are specified by the vector-valued function
|
||||
*F* that evaluates
|
||||
`[y_0', \ldots, y_n'] = F(x, [y_0, \ldots, y_n])`.
|
||||
The initial point `x_0` is specified by the scalar argument *x0*,
|
||||
and the initial value `y(x_0) = [y_0(x_0), \ldots, y_n(x_0)]` is
|
||||
specified by the vector argument *y0*.
|
||||
|
||||
For convenience, if the system is one-dimensional, you may optionally
|
||||
provide just a scalar value for *y0*. In this case, *F* should accept
|
||||
a scalar *y* argument and return a scalar. The solution function
|
||||
*y* will return scalar values instead of length-1 vectors.
|
||||
|
||||
Evaluation of the solution function `y(x)` is permitted
|
||||
for any `x \ge x_0`.
|
||||
|
||||
A high-order ODE can be solved by transforming it into first-order
|
||||
vector form. This transformation is described in standard texts
|
||||
on ODEs. Examples will also be given below.
|
||||
|
||||
**Options, speed and accuracy**
|
||||
|
||||
By default, :func:`odefun` uses a high-order Taylor series
|
||||
method. For reasonably well-behaved problems, the solution will
|
||||
be fully accurate to within the working precision. Note that
|
||||
*F* must be possible to evaluate to very high precision
|
||||
for the generation of Taylor series to work.
|
||||
|
||||
To get a faster but less accurate solution, you can set a large
|
||||
value for *tol* (which defaults roughly to *eps*). If you just
|
||||
want to plot the solution or perform a basic simulation,
|
||||
*tol = 0.01* is likely sufficient.
|
||||
|
||||
The *degree* argument controls the degree of the solver (with
|
||||
*method='taylor'*, this is the degree of the Taylor series
|
||||
expansion). A higher degree means that a longer step can be taken
|
||||
before a new local solution must be generated from *F*,
|
||||
meaning that fewer steps are required to get from `x_0` to a given
|
||||
`x_1`. On the other hand, a higher degree also means that each
|
||||
local solution becomes more expensive (i.e., more evaluations of
|
||||
*F* are required per step, and at higher precision).
|
||||
|
||||
The optimal setting therefore involves a tradeoff. Generally,
|
||||
decreasing the *degree* for Taylor series is likely to give faster
|
||||
solution at low precision, while increasing is likely to be better
|
||||
at higher precision.
|
||||
|
||||
The function
|
||||
object returned by :func:`odefun` caches the solutions at all step
|
||||
points and uses polynomial interpolation between step points.
|
||||
Therefore, once `y(x_1)` has been evaluated for some `x_1`,
|
||||
`y(x)` can be evaluated very quickly for any `x_0 \le x \le x_1`.
|
||||
and continuing the evaluation up to `x_2 > x_1` is also fast.
|
||||
|
||||
**Examples of first-order ODEs**
|
||||
|
||||
We will solve the standard test problem `y'(x) = y(x), y(0) = 1`
|
||||
which has explicit solution `y(x) = \exp(x)`::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> f = odefun(lambda x, y: y, 0, 1)
|
||||
>>> for x in [0, 1, 2.5]:
|
||||
... print f(x), exp(x)
|
||||
...
|
||||
1.0 1.0
|
||||
2.71828182845905 2.71828182845905
|
||||
12.1824939607035 12.1824939607035
|
||||
|
||||
The solution with high precision::
|
||||
|
||||
>>> mp.dps = 50
|
||||
>>> f = odefun(lambda x, y: y, 0, 1)
|
||||
>>> f(1)
|
||||
2.7182818284590452353602874713526624977572470937
|
||||
>>> exp(1)
|
||||
2.7182818284590452353602874713526624977572470937
|
||||
|
||||
Using the more general vectorized form, the test problem
|
||||
can be input as (note that *f* returns a 1-element vector)::
|
||||
|
||||
>>> mp.dps = 15
|
||||
>>> f = odefun(lambda x, y: [y[0]], 0, [1])
|
||||
>>> f(1)
|
||||
[2.71828182845905]
|
||||
|
||||
:func:`odefun` can solve nonlinear ODEs, which are generally
|
||||
impossible (and at best difficult) to solve analytically. As
|
||||
an example of a nonlinear ODE, we will solve `y'(x) = x \sin(y(x))`
|
||||
for `y(0) = \pi/2`. An exact solution happens to be known
|
||||
for this problem, and is given by
|
||||
`y(x) = 2 \tan^{-1}\left(\exp\left(x^2/2\right)\right)`::
|
||||
|
||||
>>> f = odefun(lambda x, y: x*sin(y), 0, pi/2)
|
||||
>>> for x in [2, 5, 10]:
|
||||
... print f(x), 2*atan(exp(mpf(x)**2/2))
|
||||
...
|
||||
2.87255666284091 2.87255666284091
|
||||
3.14158520028345 3.14158520028345
|
||||
3.14159265358979 3.14159265358979
|
||||
|
||||
If `F` is independent of `y`, an ODE can be solved using direct
|
||||
integration. We can therefore obtain a reference solution with
|
||||
:func:`quad`::
|
||||
|
||||
>>> f = lambda x: (1+x**2)/(1+x**3)
|
||||
>>> g = odefun(lambda x, y: f(x), pi, 0)
|
||||
>>> g(2*pi)
|
||||
0.72128263801696
|
||||
>>> quad(f, [pi, 2*pi])
|
||||
0.72128263801696
|
||||
|
||||
**Examples of second-order ODEs**
|
||||
|
||||
We will solve the harmonic oscillator equation `y''(x) + y(x) = 0`.
|
||||
To do this, we introduce the helper functions `y_0 = y, y_1 = y_0'`
|
||||
whereby the original equation can be written as `y_1' + y_0' = 0`. Put
|
||||
together, we get the first-order, two-dimensional vector ODE
|
||||
|
||||
.. math ::
|
||||
|
||||
\begin{cases}
|
||||
y_0' = y_1 \\
|
||||
y_1' = -y_0
|
||||
\end{cases}
|
||||
|
||||
To get a well-defined IVP, we need two initial values. With
|
||||
`y(0) = y_0(0) = 1` and `-y'(0) = y_1(0) = 0`, the problem will of
|
||||
course be solved by `y(x) = y_0(x) = \cos(x)` and
|
||||
`-y'(x) = y_1(x) = \sin(x)`. We check this::
|
||||
|
||||
>>> f = odefun(lambda x, y: [-y[1], y[0]], 0, [1, 0])
|
||||
>>> for x in [0, 1, 2.5, 10]:
|
||||
... nprint(f(x), 15)
|
||||
... nprint([cos(x), sin(x)], 15)
|
||||
... print "---"
|
||||
...
|
||||
[1.0, 0.0]
|
||||
[1.0, 0.0]
|
||||
---
|
||||
[0.54030230586814, 0.841470984807897]
|
||||
[0.54030230586814, 0.841470984807897]
|
||||
---
|
||||
[-0.801143615546934, 0.598472144103957]
|
||||
[-0.801143615546934, 0.598472144103957]
|
||||
---
|
||||
[-0.839071529076452, -0.54402111088937]
|
||||
[-0.839071529076452, -0.54402111088937]
|
||||
---
|
||||
|
||||
Note that we get both the sine and the cosine solutions
|
||||
simultaneously.
|
||||
|
||||
**TODO**
|
||||
|
||||
* Better automatic choice of degree and step size
|
||||
* Make determination of Taylor series convergence radius
|
||||
more robust
|
||||
* Allow solution for `x < x_0`
|
||||
* Allow solution for complex `x`
|
||||
* Test for difficult (ill-conditioned) problems
|
||||
* Implement Runge-Kutta and other algorithms
|
||||
|
||||
"""
|
||||
if tol:
|
||||
tol_prec = int(-ctx.log(tol, 2))+10
|
||||
else:
|
||||
tol_prec = ctx.prec+10
|
||||
degree = degree or (3 + int(3*ctx.dps/2.))
|
||||
workprec = ctx.prec + 40
|
||||
try:
|
||||
len(y0)
|
||||
return_vector = True
|
||||
except TypeError:
|
||||
F_ = F
|
||||
F = lambda x, y: [F_(x, y[0])]
|
||||
y0 = [y0]
|
||||
return_vector = False
|
||||
ser, xb = ode_taylor(ctx, F, x0, y0, tol_prec, degree)
|
||||
series_boundaries = [x0, xb]
|
||||
series_data = [(ser, x0, xb)]
|
||||
# We will be working with vectors of Taylor series
|
||||
def mpolyval(ser, a):
|
||||
return [ctx.polyval(s[::-1], a) for s in ser]
|
||||
# Find nearest expansion point; compute if necessary
|
||||
def get_series(x):
|
||||
if x < x0:
|
||||
raise ValueError
|
||||
n = bisect(series_boundaries, x)
|
||||
if n < len(series_boundaries):
|
||||
return series_data[n-1]
|
||||
while 1:
|
||||
ser, xa, xb = series_data[-1]
|
||||
if verbose:
|
||||
print "Computing Taylor series for [%f, %f]" % (xa, xb)
|
||||
y = mpolyval(ser, xb-xa)
|
||||
xa = xb
|
||||
ser, xb = ode_taylor(ctx, F, xb, y, tol_prec, degree)
|
||||
series_boundaries.append(xb)
|
||||
series_data.append((ser, xa, xb))
|
||||
if x <= xb:
|
||||
return series_data[-1]
|
||||
# Evaluation function
|
||||
def interpolant(x):
|
||||
x = ctx.convert(x)
|
||||
orig = ctx.prec
|
||||
try:
|
||||
ctx.prec = workprec
|
||||
ser, xa, xb = get_series(x)
|
||||
y = mpolyval(ser, x-xa)
|
||||
finally:
|
||||
ctx.prec = orig
|
||||
if return_vector:
|
||||
return [+yk for yk in y]
|
||||
else:
|
||||
return +y[0]
|
||||
return interpolant
|
||||
|
||||
ODEMethods.odefun = odefun
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
doctest.testmod()
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,189 @@
|
|||
from calculus import defun
|
||||
|
||||
#----------------------------------------------------------------------------#
|
||||
# Polynomials #
|
||||
#----------------------------------------------------------------------------#
|
||||
|
||||
# XXX: extra precision
|
||||
@defun
|
||||
def polyval(ctx, coeffs, x, derivative=False):
|
||||
r"""
|
||||
Given coefficients `[c_n, \ldots, c_2, c_1, c_0]` and a number `x`,
|
||||
:func:`polyval` evaluates the polynomial
|
||||
|
||||
.. math ::
|
||||
|
||||
P(x) = c_n x^n + \ldots + c_2 x^2 + c_1 x + c_0.
|
||||
|
||||
If *derivative=True* is set, :func:`polyval` simultaneously
|
||||
evaluates `P(x)` with the derivative, `P'(x)`, and returns the
|
||||
tuple `(P(x), P'(x))`.
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.pretty = True
|
||||
>>> polyval([3, 0, 2], 0.5)
|
||||
2.75
|
||||
>>> polyval([3, 0, 2], 0.5, derivative=True)
|
||||
(2.75, 3.0)
|
||||
|
||||
The coefficients and the evaluation point may be any combination
|
||||
of real or complex numbers.
|
||||
"""
|
||||
if not coeffs:
|
||||
return ctx.zero
|
||||
p = ctx.convert(coeffs[0])
|
||||
q = ctx.zero
|
||||
for c in coeffs[1:]:
|
||||
if derivative:
|
||||
q = p + x*q
|
||||
p = c + x*p
|
||||
if derivative:
|
||||
return p, q
|
||||
else:
|
||||
return p
|
||||
|
||||
@defun
|
||||
def polyroots(ctx, coeffs, maxsteps=50, cleanup=True, extraprec=10, error=False):
|
||||
"""
|
||||
Computes all roots (real or complex) of a given polynomial. The roots are
|
||||
returned as a sorted list, where real roots appear first followed by
|
||||
complex conjugate roots as adjacent elements. The polynomial should be
|
||||
given as a list of coefficients, in the format used by :func:`polyval`.
|
||||
The leading coefficient must be nonzero.
|
||||
|
||||
With *error=True*, :func:`polyroots` returns a tuple *(roots, err)* where
|
||||
*err* is an estimate of the maximum error among the computed roots.
|
||||
|
||||
**Examples**
|
||||
|
||||
Finding the three real roots of `x^3 - x^2 - 14x + 24`::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = True
|
||||
>>> nprint(polyroots([1,-1,-14,24]), 4)
|
||||
[-4.0, 2.0, 3.0]
|
||||
|
||||
Finding the two complex conjugate roots of `4x^2 + 3x + 2`, with an
|
||||
error estimate::
|
||||
|
||||
>>> roots, err = polyroots([4,3,2], error=True)
|
||||
>>> for r in roots:
|
||||
... print r
|
||||
...
|
||||
(-0.375 + 0.59947894041409j)
|
||||
(-0.375 - 0.59947894041409j)
|
||||
>>>
|
||||
>>> err
|
||||
2.22044604925031e-16
|
||||
>>>
|
||||
>>> polyval([4,3,2], roots[0])
|
||||
(2.22044604925031e-16 + 0.0j)
|
||||
>>> polyval([4,3,2], roots[1])
|
||||
(2.22044604925031e-16 + 0.0j)
|
||||
|
||||
The following example computes all the 5th roots of unity; that is,
|
||||
the roots of `x^5 - 1`::
|
||||
|
||||
>>> mp.dps = 20
|
||||
>>> for r in polyroots([1, 0, 0, 0, 0, -1]):
|
||||
... print r
|
||||
...
|
||||
1.0
|
||||
(-0.8090169943749474241 + 0.58778525229247312917j)
|
||||
(-0.8090169943749474241 - 0.58778525229247312917j)
|
||||
(0.3090169943749474241 + 0.95105651629515357212j)
|
||||
(0.3090169943749474241 - 0.95105651629515357212j)
|
||||
|
||||
**Precision and conditioning**
|
||||
|
||||
Provided there are no repeated roots, :func:`polyroots` can typically
|
||||
compute all roots of an arbitrary polynomial to high precision::
|
||||
|
||||
>>> mp.dps = 60
|
||||
>>> for r in polyroots([1, 0, -10, 0, 1]):
|
||||
... print r
|
||||
...
|
||||
-3.14626436994197234232913506571557044551247712918732870123249
|
||||
-0.317837245195782244725757617296174288373133378433432554879127
|
||||
0.317837245195782244725757617296174288373133378433432554879127
|
||||
3.14626436994197234232913506571557044551247712918732870123249
|
||||
>>>
|
||||
>>> sqrt(3) + sqrt(2)
|
||||
3.14626436994197234232913506571557044551247712918732870123249
|
||||
>>> sqrt(3) - sqrt(2)
|
||||
0.317837245195782244725757617296174288373133378433432554879127
|
||||
|
||||
**Algorithm**
|
||||
|
||||
:func:`polyroots` implements the Durand-Kerner method [1], which
|
||||
uses complex arithmetic to locate all roots simultaneously.
|
||||
The Durand-Kerner method can be viewed as approximately performing
|
||||
simultaneous Newton iteration for all the roots. In particular,
|
||||
the convergence to simple roots is quadratic, just like Newton's
|
||||
method.
|
||||
|
||||
Although all roots are internally calculated using complex arithmetic,
|
||||
any root found to have an imaginary part smaller than the estimated
|
||||
numerical error is truncated to a real number. Real roots are placed
|
||||
first in the returned list, sorted by value. The remaining complex
|
||||
roots are sorted by real their parts so that conjugate roots end up
|
||||
next to each other.
|
||||
|
||||
**References**
|
||||
|
||||
1. http://en.wikipedia.org/wiki/Durand-Kerner_method
|
||||
|
||||
"""
|
||||
if len(coeffs) <= 1:
|
||||
if not coeffs or not coeffs[0]:
|
||||
raise ValueError("Input to polyroots must not be the zero polynomial")
|
||||
# Constant polynomial with no roots
|
||||
return []
|
||||
|
||||
orig = ctx.prec
|
||||
weps = +ctx.eps
|
||||
try:
|
||||
ctx.prec += 10
|
||||
tol = ctx.eps * 128
|
||||
deg = len(coeffs) - 1
|
||||
# Must be monic
|
||||
lead = ctx.convert(coeffs[0])
|
||||
if lead == 1:
|
||||
coeffs = map(ctx.convert, coeffs)
|
||||
else:
|
||||
coeffs = [c/lead for c in coeffs]
|
||||
f = lambda x: ctx.polyval(coeffs, x)
|
||||
roots = [ctx.mpc((0.4+0.9j)**n) for n in xrange(deg)]
|
||||
err = [ctx.one for n in xrange(deg)]
|
||||
# Durand-Kerner iteration until convergence
|
||||
for step in xrange(maxsteps):
|
||||
if abs(max(err)) < tol:
|
||||
break
|
||||
for i in xrange(deg):
|
||||
if not abs(err[i]) < tol:
|
||||
p = roots[i]
|
||||
x = f(p)
|
||||
for j in range(deg):
|
||||
if i != j:
|
||||
try:
|
||||
x /= (p-roots[j])
|
||||
except ZeroDivisionError:
|
||||
continue
|
||||
roots[i] = p - x
|
||||
err[i] = abs(x)
|
||||
# Remove small imaginary parts
|
||||
if cleanup:
|
||||
for i in xrange(deg):
|
||||
if abs(ctx._im(roots[i])) < weps:
|
||||
roots[i] = roots[i].real
|
||||
elif abs(ctx._re(roots[i])) < weps:
|
||||
roots[i] = roots[i].imag * 1j
|
||||
roots.sort(key=lambda x: (abs(ctx._im(x)), ctx._re(x)))
|
||||
finally:
|
||||
ctx.prec = orig
|
||||
if error:
|
||||
err = max(err)
|
||||
err = max(err, ctx.ldexp(1, -orig+1))
|
||||
return [+r for r in roots], +err
|
||||
else:
|
||||
return [+r for r in roots]
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,8 @@
|
|||
# The py library is part of the "py.test" testing suite (python-codespeak-lib
|
||||
# on Debian), see http://codespeak.net/py/
|
||||
|
||||
import py
|
||||
|
||||
#this makes py.test put mpath directory into the sys.path, so that we can
|
||||
#"import mpmath" from tests nicely
|
||||
rootdir = py.magic.autopath().dirpath()
|
||||
|
|
@ -0,0 +1,324 @@
|
|||
from operator import gt, lt
|
||||
|
||||
from functions.functions import SpecialFunctions
|
||||
from functions.rszeta import RSCache
|
||||
from calculus.quadrature import QuadratureMethods
|
||||
from calculus.calculus import CalculusMethods
|
||||
from calculus.optimization import OptimizationMethods
|
||||
from calculus.odes import ODEMethods
|
||||
from matrices.matrices import MatrixMethods
|
||||
from matrices.calculus import MatrixCalculusMethods
|
||||
from matrices.linalg import LinearAlgebraMethods
|
||||
from identification import IdentificationMethods
|
||||
from visualization import VisualizationMethods
|
||||
|
||||
import libmp
|
||||
|
||||
class Context(object):
|
||||
pass
|
||||
|
||||
class StandardBaseContext(Context,
|
||||
SpecialFunctions,
|
||||
RSCache,
|
||||
QuadratureMethods,
|
||||
CalculusMethods,
|
||||
MatrixMethods,
|
||||
MatrixCalculusMethods,
|
||||
LinearAlgebraMethods,
|
||||
IdentificationMethods,
|
||||
OptimizationMethods,
|
||||
ODEMethods,
|
||||
VisualizationMethods):
|
||||
|
||||
NoConvergence = libmp.NoConvergence
|
||||
ComplexResult = libmp.ComplexResult
|
||||
|
||||
def __init__(ctx):
|
||||
ctx._aliases = {}
|
||||
# Call those that need preinitialization (e.g. for wrappers)
|
||||
SpecialFunctions.__init__(ctx)
|
||||
RSCache.__init__(ctx)
|
||||
QuadratureMethods.__init__(ctx)
|
||||
CalculusMethods.__init__(ctx)
|
||||
MatrixMethods.__init__(ctx)
|
||||
|
||||
def _init_aliases(ctx):
|
||||
for alias, value in ctx._aliases.items():
|
||||
try:
|
||||
setattr(ctx, alias, getattr(ctx, value))
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
_fixed_precision = False
|
||||
|
||||
# XXX
|
||||
verbose = False
|
||||
|
||||
def warn(ctx, msg):
|
||||
print "Warning:", msg
|
||||
|
||||
def bad_domain(ctx, msg):
|
||||
raise ValueError(msg)
|
||||
|
||||
def _re(ctx, x):
|
||||
if hasattr(x, "real"):
|
||||
return x.real
|
||||
return x
|
||||
|
||||
def _im(ctx, x):
|
||||
if hasattr(x, "imag"):
|
||||
return x.imag
|
||||
return ctx.zero
|
||||
|
||||
def chop(ctx, x, tol=None):
|
||||
"""
|
||||
Chops off small real or imaginary parts, or converts
|
||||
numbers close to zero to exact zeros. The input can be a
|
||||
single number or an iterable::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = False
|
||||
>>> chop(5+1e-10j, tol=1e-9)
|
||||
mpf('5.0')
|
||||
>>> nprint(chop([1.0, 1e-20, 3+1e-18j, -4, 2]))
|
||||
[1.0, 0.0, 3.0, -4.0, 2.0]
|
||||
|
||||
The tolerance defaults to ``100*eps``.
|
||||
"""
|
||||
if tol is None:
|
||||
tol = 100*ctx.eps
|
||||
try:
|
||||
x = ctx.convert(x)
|
||||
absx = abs(x)
|
||||
if abs(x) < tol:
|
||||
return ctx.zero
|
||||
if ctx._is_complex_type(x):
|
||||
if abs(x.imag) < min(tol, absx*tol):
|
||||
return x.real
|
||||
if abs(x.real) < min(tol, absx*tol):
|
||||
return ctx.mpc(0, x.imag)
|
||||
except TypeError:
|
||||
if isinstance(x, ctx.matrix):
|
||||
return x.apply(lambda a: ctx.chop(a, tol))
|
||||
if hasattr(x, "__iter__"):
|
||||
return [ctx.chop(a, tol) for a in x]
|
||||
return x
|
||||
|
||||
def almosteq(ctx, s, t, rel_eps=None, abs_eps=None):
|
||||
r"""
|
||||
Determine whether the difference between `s` and `t` is smaller
|
||||
than a given epsilon, either relatively or absolutely.
|
||||
|
||||
Both a maximum relative difference and a maximum difference
|
||||
('epsilons') may be specified. The absolute difference is
|
||||
defined as `|s-t|` and the relative difference is defined
|
||||
as `|s-t|/\max(|s|, |t|)`.
|
||||
|
||||
If only one epsilon is given, both are set to the same value.
|
||||
If none is given, both epsilons are set to `2^{-p+m}` where
|
||||
`p` is the current working precision and `m` is a small
|
||||
integer. The default setting typically allows :func:`almosteq`
|
||||
to be used to check for mathematical equality
|
||||
in the presence of small rounding errors.
|
||||
|
||||
**Examples**
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15
|
||||
>>> almosteq(3.141592653589793, 3.141592653589790)
|
||||
True
|
||||
>>> almosteq(3.141592653589793, 3.141592653589700)
|
||||
False
|
||||
>>> almosteq(3.141592653589793, 3.141592653589700, 1e-10)
|
||||
True
|
||||
>>> almosteq(1e-20, 2e-20)
|
||||
True
|
||||
>>> almosteq(1e-20, 2e-20, rel_eps=0, abs_eps=0)
|
||||
False
|
||||
|
||||
"""
|
||||
t = ctx.convert(t)
|
||||
if abs_eps is None and rel_eps is None:
|
||||
rel_eps = abs_eps = ctx.ldexp(1, -ctx.prec+4)
|
||||
if abs_eps is None:
|
||||
abs_eps = rel_eps
|
||||
elif rel_eps is None:
|
||||
rel_eps = abs_eps
|
||||
diff = abs(s-t)
|
||||
if diff <= abs_eps:
|
||||
return True
|
||||
abss = abs(s)
|
||||
abst = abs(t)
|
||||
if abss < abst:
|
||||
err = diff/abst
|
||||
else:
|
||||
err = diff/abss
|
||||
return err <= rel_eps
|
||||
|
||||
def arange(ctx, *args):
|
||||
r"""
|
||||
This is a generalized version of Python's :func:`range` function
|
||||
that accepts fractional endpoints and step sizes and
|
||||
returns a list of ``mpf`` instances. Like :func:`range`,
|
||||
:func:`arange` can be called with 1, 2 or 3 arguments:
|
||||
|
||||
``arange(b)``
|
||||
`[0, 1, 2, \ldots, x]`
|
||||
``arange(a, b)``
|
||||
`[a, a+1, a+2, \ldots, x]`
|
||||
``arange(a, b, h)``
|
||||
`[a, a+h, a+h, \ldots, x]`
|
||||
|
||||
where `b-1 \le x < b` (in the third case, `b-h \le x < b`).
|
||||
|
||||
Like Python's :func:`range`, the endpoint is not included. To
|
||||
produce ranges where the endpoint is included, :func:`linspace`
|
||||
is more convenient.
|
||||
|
||||
**Examples**
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = False
|
||||
>>> arange(4)
|
||||
[mpf('0.0'), mpf('1.0'), mpf('2.0'), mpf('3.0')]
|
||||
>>> arange(1, 2, 0.25)
|
||||
[mpf('1.0'), mpf('1.25'), mpf('1.5'), mpf('1.75')]
|
||||
>>> arange(1, -1, -0.75)
|
||||
[mpf('1.0'), mpf('0.25'), mpf('-0.5')]
|
||||
|
||||
"""
|
||||
if not len(args) <= 3:
|
||||
raise TypeError('arange expected at most 3 arguments, got %i'
|
||||
% len(args))
|
||||
if not len(args) >= 1:
|
||||
raise TypeError('arange expected at least 1 argument, got %i'
|
||||
% len(args))
|
||||
# set default
|
||||
a = 0
|
||||
dt = 1
|
||||
# interpret arguments
|
||||
if len(args) == 1:
|
||||
b = args[0]
|
||||
elif len(args) >= 2:
|
||||
a = args[0]
|
||||
b = args[1]
|
||||
if len(args) == 3:
|
||||
dt = args[2]
|
||||
a, b, dt = ctx.mpf(a), ctx.mpf(b), ctx.mpf(dt)
|
||||
assert a + dt != a, 'dt is too small and would cause an infinite loop'
|
||||
# adapt code for sign of dt
|
||||
if a > b:
|
||||
if dt > 0:
|
||||
return []
|
||||
op = gt
|
||||
else:
|
||||
if dt < 0:
|
||||
return []
|
||||
op = lt
|
||||
# create list
|
||||
result = []
|
||||
i = 0
|
||||
t = a
|
||||
while 1:
|
||||
t = a + dt*i
|
||||
i += 1
|
||||
if op(t, b):
|
||||
result.append(t)
|
||||
else:
|
||||
break
|
||||
return result
|
||||
|
||||
def linspace(ctx, *args, **kwargs):
|
||||
"""
|
||||
``linspace(a, b, n)`` returns a list of `n` evenly spaced
|
||||
samples from `a` to `b`. The syntax ``linspace(mpi(a,b), n)``
|
||||
is also valid.
|
||||
|
||||
This function is often more convenient than :func:`arange`
|
||||
for partitioning an interval into subintervals, since
|
||||
the endpoint is included::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = False
|
||||
>>> linspace(1, 4, 4)
|
||||
[mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0')]
|
||||
>>> linspace(mpi(1,4), 4)
|
||||
[mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0')]
|
||||
|
||||
You may also provide the keyword argument ``endpoint=False``::
|
||||
|
||||
>>> linspace(1, 4, 4, endpoint=False)
|
||||
[mpf('1.0'), mpf('1.75'), mpf('2.5'), mpf('3.25')]
|
||||
|
||||
"""
|
||||
if len(args) == 3:
|
||||
a = ctx.mpf(args[0])
|
||||
b = ctx.mpf(args[1])
|
||||
n = int(args[2])
|
||||
elif len(args) == 2:
|
||||
assert hasattr(args[0], '_mpi_')
|
||||
a = args[0].a
|
||||
b = args[0].b
|
||||
n = int(args[1])
|
||||
else:
|
||||
raise TypeError('linspace expected 2 or 3 arguments, got %i' \
|
||||
% len(args))
|
||||
if n < 1:
|
||||
raise ValueError('n must be greater than 0')
|
||||
if not 'endpoint' in kwargs or kwargs['endpoint']:
|
||||
if n == 1:
|
||||
return [ctx.mpf(a)]
|
||||
step = (b - a) / ctx.mpf(n - 1)
|
||||
y = [i*step + a for i in xrange(n)]
|
||||
y[-1] = b
|
||||
else:
|
||||
step = (b - a) / ctx.mpf(n)
|
||||
y = [i*step + a for i in xrange(n)]
|
||||
return y
|
||||
|
||||
def cos_sin(ctx, z, **kwargs):
|
||||
return ctx.cos(z, **kwargs), ctx.sin(z, **kwargs)
|
||||
|
||||
def _default_hyper_maxprec(ctx, p):
|
||||
return int(1000 * p**0.25 + 4*p)
|
||||
|
||||
_gcd = staticmethod(libmp.gcd)
|
||||
list_primes = staticmethod(libmp.list_primes)
|
||||
bernfrac = staticmethod(libmp.bernfrac)
|
||||
moebius = staticmethod(libmp.moebius)
|
||||
_ifac = staticmethod(libmp.ifac)
|
||||
_eulernum = staticmethod(libmp.eulernum)
|
||||
|
||||
def sum_accurately(ctx, terms, check_step=1):
|
||||
prec = ctx.prec
|
||||
try:
|
||||
extraprec = 10
|
||||
while 1:
|
||||
ctx.prec = prec + extraprec + 5
|
||||
max_mag = ctx.ninf
|
||||
s = ctx.zero
|
||||
k = 0
|
||||
for term in terms():
|
||||
s += term
|
||||
if (not k % check_step) and term:
|
||||
term_mag = ctx.mag(term)
|
||||
max_mag = max(max_mag, term_mag)
|
||||
sum_mag = ctx.mag(s)
|
||||
if sum_mag - term_mag > ctx.prec:
|
||||
break
|
||||
k += 1
|
||||
cancellation = max_mag - sum_mag
|
||||
if cancellation != cancellation:
|
||||
break
|
||||
if cancellation < extraprec or ctx._fixed_precision:
|
||||
break
|
||||
extraprec += min(ctx.prec, cancellation)
|
||||
return s
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
|
||||
def power(ctx, x, y):
|
||||
return ctx.convert(x) ** ctx.convert(y)
|
||||
|
||||
def _zeta_int(ctx, n):
|
||||
return ctx.zeta(n)
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
from ctx_base import StandardBaseContext
|
||||
|
||||
import math
|
||||
import cmath
|
||||
import math2
|
||||
|
||||
import function_docs
|
||||
|
||||
from libmp import mpf_bernoulli, to_float, int_types
|
||||
import libmp
|
||||
|
||||
class FPContext(StandardBaseContext):
|
||||
"""
|
||||
Context for fast low-precision arithmetic (53-bit precision, giving at most
|
||||
about 15-digit accuracy), using Python's builtin float and complex.
|
||||
"""
|
||||
|
||||
def __init__(ctx):
|
||||
StandardBaseContext.__init__(ctx)
|
||||
|
||||
# Override SpecialFunctions implementation
|
||||
ctx.loggamma = math2.loggamma
|
||||
ctx._bernoulli_cache = {}
|
||||
ctx.pretty = False
|
||||
|
||||
ctx._init_aliases()
|
||||
|
||||
_mpq = lambda cls, x: float(x[0])/x[1]
|
||||
|
||||
NoConvergence = libmp.NoConvergence
|
||||
|
||||
def _get_prec(ctx): return 53
|
||||
def _set_prec(ctx, p): return
|
||||
def _get_dps(ctx): return 15
|
||||
def _set_dps(ctx, p): return
|
||||
|
||||
_fixed_precision = True
|
||||
|
||||
prec = property(_get_prec, _set_prec)
|
||||
dps = property(_get_dps, _set_dps)
|
||||
|
||||
zero = 0.0
|
||||
one = 1.0
|
||||
eps = math2.EPS
|
||||
inf = math2.INF
|
||||
ninf = math2.NINF
|
||||
nan = math2.NAN
|
||||
j = 1j
|
||||
|
||||
# Called by SpecialFunctions.__init__()
|
||||
@classmethod
|
||||
def _wrap_specfun(cls, name, f, wrap):
|
||||
if wrap:
|
||||
def f_wrapped(ctx, *args, **kwargs):
|
||||
convert = ctx.convert
|
||||
args = [convert(a) for a in args]
|
||||
return f(ctx, *args, **kwargs)
|
||||
else:
|
||||
f_wrapped = f
|
||||
f_wrapped.__doc__ = function_docs.__dict__.get(name, "<no doc>")
|
||||
setattr(cls, name, f_wrapped)
|
||||
|
||||
def bernoulli(ctx, n):
|
||||
cache = ctx._bernoulli_cache
|
||||
if n in cache:
|
||||
return cache[n]
|
||||
cache[n] = to_float(mpf_bernoulli(n, 53, 'n'), strict=True)
|
||||
return cache[n]
|
||||
|
||||
pi = math2.pi
|
||||
e = math2.e
|
||||
euler = math2.euler
|
||||
sqrt2 = 1.4142135623730950488
|
||||
sqrt5 = 2.2360679774997896964
|
||||
phi = 1.6180339887498948482
|
||||
ln2 = 0.69314718055994530942
|
||||
ln10 = 2.302585092994045684
|
||||
euler = 0.57721566490153286061
|
||||
catalan = 0.91596559417721901505
|
||||
khinchin = 2.6854520010653064453
|
||||
apery = 1.2020569031595942854
|
||||
|
||||
absmin = absmax = abs
|
||||
|
||||
def _as_points(ctx, x):
|
||||
return x
|
||||
|
||||
def fneg(ctx, x, **kwargs):
|
||||
return -ctx.convert(x)
|
||||
|
||||
def fadd(ctx, x, y, **kwargs):
|
||||
return ctx.convert(x)+ctx.convert(y)
|
||||
|
||||
def fsub(ctx, x, y, **kwargs):
|
||||
return ctx.convert(x)-ctx.convert(y)
|
||||
|
||||
def fmul(ctx, x, y, **kwargs):
|
||||
return ctx.convert(x)*ctx.convert(y)
|
||||
|
||||
def fdiv(ctx, x, y, **kwargs):
|
||||
return ctx.convert(x)/ctx.convert(y)
|
||||
|
||||
def fsum(ctx, args, absolute=False, squared=False):
|
||||
if absolute:
|
||||
if squared:
|
||||
return sum((abs(x)**2 for x in args), ctx.zero)
|
||||
return sum((abs(x) for x in args), ctx.zero)
|
||||
if squared:
|
||||
return sum((x**2 for x in args), ctx.zero)
|
||||
return sum(args, ctx.zero)
|
||||
|
||||
def fdot(ctx, xs, ys=None):
|
||||
if ys is not None:
|
||||
xs = zip(xs, ys)
|
||||
return sum((x*y for (x,y) in xs), ctx.zero)
|
||||
|
||||
def is_special(ctx, x):
|
||||
return x - x != 0.0
|
||||
|
||||
def isnan(ctx, x):
|
||||
return x != x
|
||||
|
||||
def isinf(ctx, x):
|
||||
return abs(x) == math2.INF
|
||||
|
||||
def isnpint(ctx, x):
|
||||
if type(x) is complex:
|
||||
if x.imag:
|
||||
return False
|
||||
x = x.real
|
||||
return x <= 0.0 and round(x) == x
|
||||
|
||||
mpf = float
|
||||
mpc = complex
|
||||
|
||||
def convert(ctx, x):
|
||||
try:
|
||||
return float(x)
|
||||
except:
|
||||
return complex(x)
|
||||
|
||||
power = staticmethod(math2.pow)
|
||||
sqrt = staticmethod(math2.sqrt)
|
||||
exp = staticmethod(math2.exp)
|
||||
ln = log = staticmethod(math2.log)
|
||||
cos = staticmethod(math2.cos)
|
||||
sin = staticmethod(math2.sin)
|
||||
tan = staticmethod(math2.tan)
|
||||
cos_sin = staticmethod(math2.cos_sin)
|
||||
acos = staticmethod(math2.acos)
|
||||
asin = staticmethod(math2.asin)
|
||||
atan = staticmethod(math2.atan)
|
||||
cosh = staticmethod(math2.cosh)
|
||||
sinh = staticmethod(math2.sinh)
|
||||
tanh = staticmethod(math2.tanh)
|
||||
gamma = staticmethod(math2.gamma)
|
||||
fac = factorial = staticmethod(math2.factorial)
|
||||
floor = staticmethod(math2.floor)
|
||||
ceil = staticmethod(math2.ceil)
|
||||
cospi = staticmethod(math2.cospi)
|
||||
sinpi = staticmethod(math2.sinpi)
|
||||
cbrt = staticmethod(math2.cbrt)
|
||||
_nthroot = staticmethod(math2.nthroot)
|
||||
_ei = staticmethod(math2.ei)
|
||||
_e1 = staticmethod(math2.e1)
|
||||
_zeta = _zeta_int = staticmethod(math2.zeta)
|
||||
|
||||
# XXX: math2
|
||||
def arg(ctx, z):
|
||||
z = complex(z)
|
||||
return math.atan2(z.imag, z.real)
|
||||
|
||||
def expj(ctx, x):
|
||||
return ctx.exp(ctx.j*x)
|
||||
|
||||
def expjpi(ctx, x):
|
||||
return ctx.exp(ctx.j*ctx.pi*x)
|
||||
|
||||
ldexp = math.ldexp
|
||||
frexp = math.frexp
|
||||
|
||||
def mag(ctx, z):
|
||||
if z:
|
||||
return ctx.frexp(abs(z))[1]
|
||||
return ctx.ninf
|
||||
|
||||
def isint(ctx, z):
|
||||
if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5
|
||||
if z.imag:
|
||||
return False
|
||||
z = z.real
|
||||
try:
|
||||
return z == int(z)
|
||||
except:
|
||||
return False
|
||||
|
||||
def nint_distance(ctx, z):
|
||||
if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5
|
||||
n = round(z.real)
|
||||
else:
|
||||
n = round(z)
|
||||
if n == z:
|
||||
return n, ctx.ninf
|
||||
return n, ctx.mag(abs(z-n))
|
||||
|
||||
def _convert_param(ctx, z):
|
||||
if type(z) is tuple:
|
||||
p, q = z
|
||||
return ctx.mpf(p) / q, 'R'
|
||||
if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5
|
||||
intz = int(z.real)
|
||||
else:
|
||||
intz = int(z)
|
||||
if z == intz:
|
||||
return intz, 'Z'
|
||||
return z, 'R'
|
||||
|
||||
def _is_real_type(ctx, z):
|
||||
return isinstance(z, float) or isinstance(z, int_types)
|
||||
|
||||
def _is_complex_type(ctx, z):
|
||||
return isinstance(z, complex)
|
||||
|
||||
def hypsum(ctx, p, q, types, coeffs, z, maxterms=6000, **kwargs):
|
||||
coeffs = list(coeffs)
|
||||
num = range(p)
|
||||
den = range(p,p+q)
|
||||
tol = ctx.eps
|
||||
s = t = 1.0
|
||||
k = 0
|
||||
while 1:
|
||||
for i in num: t *= (coeffs[i]+k)
|
||||
for i in den: t /= (coeffs[i]+k)
|
||||
k += 1; t /= k; t *= z; s += t
|
||||
if abs(t) < tol:
|
||||
return s
|
||||
if k > maxterms:
|
||||
raise ctx.NoConvergence
|
||||
|
||||
def atan2(ctx, x, y):
|
||||
return math.atan2(x, y)
|
||||
|
||||
def psi(ctx, m, z):
|
||||
m = int(m)
|
||||
if m == 0:
|
||||
return ctx.digamma(z)
|
||||
return (-1)**(m+1) * ctx.fac(m) * ctx.zeta(m+1, z)
|
||||
|
||||
digamma = staticmethod(math2.digamma)
|
||||
|
||||
def harmonic(ctx, x):
|
||||
x = ctx.convert(x)
|
||||
if x == 0 or x == 1:
|
||||
return x
|
||||
return ctx.digamma(x+1) + ctx.euler
|
||||
|
||||
nstr = str
|
||||
|
||||
def to_fixed(ctx, x, prec):
|
||||
return int(math.ldexp(x, prec))
|
||||
|
||||
def rand(ctx):
|
||||
import random
|
||||
return random.random()
|
||||
|
||||
_erf = staticmethod(math2.erf)
|
||||
_erfc = staticmethod(math2.erfc)
|
||||
|
||||
def sum_accurately(ctx, terms, check_step=1):
|
||||
s = ctx.zero
|
||||
k = 0
|
||||
for term in terms():
|
||||
s += term
|
||||
if (not k % check_step) and term:
|
||||
if abs(term) <= 1e-18*abs(s):
|
||||
break
|
||||
k += 1
|
||||
return s
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,986 @@
|
|||
#from ctx_base import StandardBaseContext
|
||||
|
||||
from libmp import (MPZ, MPZ_ZERO, MPZ_ONE, int_types, repr_dps,
|
||||
round_floor, round_ceiling, dps_to_prec, round_nearest, prec_to_dps,
|
||||
ComplexResult, to_pickable, from_pickable, normalize,
|
||||
from_int, from_float, from_str, to_int, to_float, to_str,
|
||||
from_rational, from_man_exp,
|
||||
fone, fzero, finf, fninf, fnan,
|
||||
mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_mul_int,
|
||||
mpf_div, mpf_rdiv_int, mpf_pow_int, mpf_mod,
|
||||
mpf_eq, mpf_cmp, mpf_lt, mpf_gt, mpf_le, mpf_ge,
|
||||
mpf_hash, mpf_rand,
|
||||
mpf_sum,
|
||||
bitcount, to_fixed,
|
||||
mpc_to_str,
|
||||
mpc_to_complex, mpc_hash, mpc_pos, mpc_is_nonzero, mpc_neg, mpc_conjugate,
|
||||
mpc_abs, mpc_add, mpc_add_mpf, mpc_sub, mpc_sub_mpf, mpc_mul, mpc_mul_mpf,
|
||||
mpc_mul_int, mpc_div, mpc_div_mpf, mpc_pow, mpc_pow_mpf, mpc_pow_int,
|
||||
mpc_mpf_div,
|
||||
mpf_pow,
|
||||
mpi_mid, mpi_delta, mpi_str,
|
||||
mpi_abs, mpi_pos, mpi_neg, mpi_add, mpi_sub,
|
||||
mpi_mul, mpi_div, mpi_pow_int, mpi_pow,
|
||||
mpf_pi, mpf_degree, mpf_e, mpf_phi, mpf_ln2, mpf_ln10,
|
||||
mpf_euler, mpf_catalan, mpf_apery, mpf_khinchin,
|
||||
mpf_glaisher, mpf_twinprime, mpf_mertens,
|
||||
int_types)
|
||||
|
||||
import rational
|
||||
import function_docs
|
||||
|
||||
new = object.__new__
|
||||
|
||||
class mpnumeric(object):
|
||||
"""Base class for mpf and mpc."""
|
||||
__slots__ = []
|
||||
def __new__(cls, val):
|
||||
raise NotImplementedError
|
||||
|
||||
class _mpf(mpnumeric):
|
||||
"""
|
||||
An mpf instance holds a real-valued floating-point number. mpf:s
|
||||
work analogously to Python floats, but support arbitrary-precision
|
||||
arithmetic.
|
||||
"""
|
||||
__slots__ = ['_mpf_']
|
||||
|
||||
def __new__(cls, val=fzero, **kwargs):
|
||||
"""A new mpf can be created from a Python float, an int, a
|
||||
or a decimal string representing a number in floating-point
|
||||
format."""
|
||||
prec, rounding = cls.context._prec_rounding
|
||||
if kwargs:
|
||||
prec = kwargs.get('prec', prec)
|
||||
if 'dps' in kwargs:
|
||||
prec = dps_to_prec(kwargs['dps'])
|
||||
rounding = kwargs.get('rounding', rounding)
|
||||
if type(val) is cls:
|
||||
sign, man, exp, bc = val._mpf_
|
||||
if (not man) and exp:
|
||||
return val
|
||||
v = new(cls)
|
||||
v._mpf_ = normalize(sign, man, exp, bc, prec, rounding)
|
||||
return v
|
||||
elif type(val) is tuple:
|
||||
if len(val) == 2:
|
||||
v = new(cls)
|
||||
v._mpf_ = from_man_exp(val[0], val[1], prec, rounding)
|
||||
return v
|
||||
if len(val) == 4:
|
||||
sign, man, exp, bc = val
|
||||
v = new(cls)
|
||||
v._mpf_ = normalize(sign, MPZ(man), exp, bc, prec, rounding)
|
||||
return v
|
||||
raise ValueError
|
||||
else:
|
||||
v = new(cls)
|
||||
v._mpf_ = mpf_pos(cls.mpf_convert_arg(val, prec, rounding), prec, rounding)
|
||||
return v
|
||||
|
||||
@classmethod
|
||||
def mpf_convert_arg(cls, x, prec, rounding):
|
||||
if isinstance(x, int_types): return from_int(x)
|
||||
if isinstance(x, float): return from_float(x)
|
||||
if isinstance(x, basestring): return from_str(x, prec, rounding)
|
||||
if isinstance(x, cls.context.constant): return x.func(prec, rounding)
|
||||
if hasattr(x, '_mpf_'): return x._mpf_
|
||||
if hasattr(x, '_mpmath_'):
|
||||
t = cls.context.convert(x._mpmath_(prec, rounding))
|
||||
if hasattr(t, '_mpf_'):
|
||||
return t._mpf_
|
||||
raise TypeError("cannot create mpf from " + repr(x))
|
||||
|
||||
@classmethod
|
||||
def mpf_convert_rhs(cls, x):
|
||||
if isinstance(x, int_types): return from_int(x)
|
||||
if isinstance(x, float): return from_float(x)
|
||||
if isinstance(x, complex_types): return cls.context.mpc(x)
|
||||
if isinstance(x, rational.mpq):
|
||||
p, q = x
|
||||
return from_rational(p, q, cls.context.prec)
|
||||
if hasattr(x, '_mpf_'): return x._mpf_
|
||||
if hasattr(x, '_mpmath_'):
|
||||
t = cls.context.convert(x._mpmath_(*cls.context._prec_rounding))
|
||||
if hasattr(t, '_mpf_'):
|
||||
return t._mpf_
|
||||
return t
|
||||
return NotImplemented
|
||||
|
||||
@classmethod
|
||||
def mpf_convert_lhs(cls, x):
|
||||
x = cls.mpf_convert_rhs(x)
|
||||
if type(x) is tuple:
|
||||
return cls.context.make_mpf(x)
|
||||
return x
|
||||
|
||||
man_exp = property(lambda self: self._mpf_[1:3])
|
||||
man = property(lambda self: self._mpf_[1])
|
||||
exp = property(lambda self: self._mpf_[2])
|
||||
bc = property(lambda self: self._mpf_[3])
|
||||
|
||||
real = property(lambda self: self)
|
||||
imag = property(lambda self: self.context.zero)
|
||||
|
||||
conjugate = lambda self: self
|
||||
|
||||
def __getstate__(self): return to_pickable(self._mpf_)
|
||||
def __setstate__(self, val): self._mpf_ = from_pickable(val)
|
||||
|
||||
def __repr__(s):
|
||||
if s.context.pretty:
|
||||
return str(s)
|
||||
return "mpf('%s')" % to_str(s._mpf_, s.context._repr_digits)
|
||||
|
||||
def __str__(s): return to_str(s._mpf_, s.context._str_digits)
|
||||
def __hash__(s): return mpf_hash(s._mpf_)
|
||||
def __int__(s): return int(to_int(s._mpf_))
|
||||
def __long__(s): return long(to_int(s._mpf_))
|
||||
def __float__(s): return to_float(s._mpf_)
|
||||
def __complex__(s): return complex(float(s))
|
||||
def __nonzero__(s): return s._mpf_ != fzero
|
||||
|
||||
def __abs__(s):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
v = new(cls)
|
||||
v._mpf_ = mpf_abs(s._mpf_, prec, rounding)
|
||||
return v
|
||||
|
||||
def __pos__(s):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
v = new(cls)
|
||||
v._mpf_ = mpf_pos(s._mpf_, prec, rounding)
|
||||
return v
|
||||
|
||||
def __neg__(s):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
v = new(cls)
|
||||
v._mpf_ = mpf_neg(s._mpf_, prec, rounding)
|
||||
return v
|
||||
|
||||
def _cmp(s, t, func):
|
||||
if hasattr(t, '_mpf_'):
|
||||
t = t._mpf_
|
||||
else:
|
||||
t = s.mpf_convert_rhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
return func(s._mpf_, t)
|
||||
|
||||
def __cmp__(s, t): return s._cmp(t, mpf_cmp)
|
||||
def __lt__(s, t): return s._cmp(t, mpf_lt)
|
||||
def __gt__(s, t): return s._cmp(t, mpf_gt)
|
||||
def __le__(s, t): return s._cmp(t, mpf_le)
|
||||
def __ge__(s, t): return s._cmp(t, mpf_ge)
|
||||
|
||||
def __ne__(s, t):
|
||||
v = s.__eq__(t)
|
||||
if v is NotImplemented:
|
||||
return v
|
||||
return not v
|
||||
|
||||
def __rsub__(s, t):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
if type(t) in int_types:
|
||||
v = new(cls)
|
||||
v._mpf_ = mpf_sub(from_int(t), s._mpf_, prec, rounding)
|
||||
return v
|
||||
t = s.mpf_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
return t - s
|
||||
|
||||
def __rdiv__(s, t):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
if isinstance(t, int_types):
|
||||
v = new(cls)
|
||||
v._mpf_ = mpf_rdiv_int(t, s._mpf_, prec, rounding)
|
||||
return v
|
||||
t = s.mpf_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
return t / s
|
||||
|
||||
def __rpow__(s, t):
|
||||
t = s.mpf_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
return t ** s
|
||||
|
||||
def __rmod__(s, t):
|
||||
t = s.mpf_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
return t % s
|
||||
|
||||
def sqrt(s):
|
||||
return s.context.sqrt(s)
|
||||
|
||||
def ae(s, t, rel_eps=None, abs_eps=None):
|
||||
return s.context.almosteq(s, t, rel_eps, abs_eps)
|
||||
|
||||
def to_fixed(self, prec):
|
||||
return to_fixed(self._mpf_, prec)
|
||||
|
||||
|
||||
mpf_binary_op = """
|
||||
def %NAME%(self, other):
|
||||
mpf, new, (prec, rounding) = self._ctxdata
|
||||
sval = self._mpf_
|
||||
if hasattr(other, '_mpf_'):
|
||||
tval = other._mpf_
|
||||
%WITH_MPF%
|
||||
ttype = type(other)
|
||||
if ttype in int_types:
|
||||
%WITH_INT%
|
||||
elif ttype is float:
|
||||
tval = from_float(other)
|
||||
%WITH_MPF%
|
||||
elif hasattr(other, '_mpc_'):
|
||||
tval = other._mpc_
|
||||
mpc = type(other)
|
||||
%WITH_MPC%
|
||||
elif ttype is complex:
|
||||
tval = from_float(other.real), from_float(other.imag)
|
||||
mpc = self.context.mpc
|
||||
%WITH_MPC%
|
||||
if isinstance(other, mpnumeric):
|
||||
return NotImplemented
|
||||
try:
|
||||
other = mpf.context.convert(other, strings=False)
|
||||
except TypeError:
|
||||
return NotImplemented
|
||||
return self.%NAME%(other)
|
||||
"""
|
||||
|
||||
return_mpf = "; obj = new(mpf); obj._mpf_ = val; return obj"
|
||||
return_mpc = "; obj = new(mpc); obj._mpc_ = val; return obj"
|
||||
|
||||
mpf_pow_same = """
|
||||
try:
|
||||
val = mpf_pow(sval, tval, prec, rounding) %s
|
||||
except ComplexResult:
|
||||
if mpf.context.trap_complex:
|
||||
raise
|
||||
mpc = mpf.context.mpc
|
||||
val = mpc_pow((sval, fzero), (tval, fzero), prec, rounding) %s
|
||||
""" % (return_mpf, return_mpc)
|
||||
|
||||
def binary_op(name, with_mpf='', with_int='', with_mpc=''):
|
||||
code = mpf_binary_op
|
||||
code = code.replace("%WITH_INT%", with_int)
|
||||
code = code.replace("%WITH_MPC%", with_mpc)
|
||||
code = code.replace("%WITH_MPF%", with_mpf)
|
||||
code = code.replace("%NAME%", name)
|
||||
np = {}
|
||||
exec code in globals(), np
|
||||
return np[name]
|
||||
|
||||
_mpf.__eq__ = binary_op('__eq__',
|
||||
'return mpf_eq(sval, tval)',
|
||||
'return mpf_eq(sval, from_int(other))',
|
||||
'return (tval[1] == fzero) and mpf_eq(tval[0], sval)')
|
||||
|
||||
_mpf.__add__ = binary_op('__add__',
|
||||
'val = mpf_add(sval, tval, prec, rounding)' + return_mpf,
|
||||
'val = mpf_add(sval, from_int(other), prec, rounding)' + return_mpf,
|
||||
'val = mpc_add_mpf(tval, sval, prec, rounding)' + return_mpc)
|
||||
|
||||
_mpf.__sub__ = binary_op('__sub__',
|
||||
'val = mpf_sub(sval, tval, prec, rounding)' + return_mpf,
|
||||
'val = mpf_sub(sval, from_int(other), prec, rounding)' + return_mpf,
|
||||
'val = mpc_sub((sval, fzero), tval, prec, rounding)' + return_mpc)
|
||||
|
||||
_mpf.__mul__ = binary_op('__mul__',
|
||||
'val = mpf_mul(sval, tval, prec, rounding)' + return_mpf,
|
||||
'val = mpf_mul_int(sval, other, prec, rounding)' + return_mpf,
|
||||
'val = mpc_mul_mpf(tval, sval, prec, rounding)' + return_mpc)
|
||||
|
||||
_mpf.__div__ = binary_op('__div__',
|
||||
'val = mpf_div(sval, tval, prec, rounding)' + return_mpf,
|
||||
'val = mpf_div(sval, from_int(other), prec, rounding)' + return_mpf,
|
||||
'val = mpc_mpf_div(sval, tval, prec, rounding)' + return_mpc)
|
||||
|
||||
_mpf.__mod__ = binary_op('__mod__',
|
||||
'val = mpf_mod(sval, tval, prec, rounding)' + return_mpf,
|
||||
'val = mpf_mod(sval, from_int(other), prec, rounding)' + return_mpf,
|
||||
'raise NotImplementedError("complex modulo")')
|
||||
|
||||
_mpf.__pow__ = binary_op('__pow__',
|
||||
mpf_pow_same,
|
||||
'val = mpf_pow_int(sval, other, prec, rounding)' + return_mpf,
|
||||
'val = mpc_pow((sval, fzero), tval, prec, rounding)' + return_mpc)
|
||||
|
||||
_mpf.__radd__ = _mpf.__add__
|
||||
_mpf.__rmul__ = _mpf.__mul__
|
||||
_mpf.__truediv__ = _mpf.__div__
|
||||
_mpf.__rtruediv__ = _mpf.__rdiv__
|
||||
|
||||
|
||||
class _constant(_mpf):
|
||||
"""Represents a mathematical constant with dynamic precision.
|
||||
When printed or used in an arithmetic operation, a constant
|
||||
is converted to a regular mpf at the working precision. A
|
||||
regular mpf can also be obtained using the operation +x."""
|
||||
|
||||
def __new__(cls, func, name, docname=''):
|
||||
a = object.__new__(cls)
|
||||
a.name = name
|
||||
a.func = func
|
||||
a.__doc__ = getattr(function_docs, docname, '')
|
||||
return a
|
||||
|
||||
def __call__(self, prec=None, dps=None, rounding=None):
|
||||
prec2, rounding2 = self.context._prec_rounding
|
||||
if not prec: prec = prec2
|
||||
if not rounding: rounding = rounding2
|
||||
if dps: prec = dps_to_prec(dps)
|
||||
return self.context.make_mpf(self.func(prec, rounding))
|
||||
|
||||
@property
|
||||
def _mpf_(self):
|
||||
prec, rounding = self.context._prec_rounding
|
||||
return self.func(prec, rounding)
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s: %s~>" % (self.name, self.context.nstr(self))
|
||||
|
||||
|
||||
class _mpc(mpnumeric):
|
||||
"""
|
||||
An mpc represents a complex number using a pair of mpf:s (one
|
||||
for the real part and another for the imaginary part.) The mpc
|
||||
class behaves fairly similarly to Python's complex type.
|
||||
"""
|
||||
|
||||
__slots__ = ['_mpc_']
|
||||
|
||||
def __new__(cls, real=0, imag=0):
|
||||
s = object.__new__(cls)
|
||||
if isinstance(real, complex_types):
|
||||
real, imag = real.real, real.imag
|
||||
elif hasattr(real, '_mpc_'):
|
||||
s._mpc_ = real._mpc_
|
||||
return s
|
||||
real = cls.context.mpf(real)
|
||||
imag = cls.context.mpf(imag)
|
||||
s._mpc_ = (real._mpf_, imag._mpf_)
|
||||
return s
|
||||
|
||||
real = property(lambda self: self.context.make_mpf(self._mpc_[0]))
|
||||
imag = property(lambda self: self.context.make_mpf(self._mpc_[1]))
|
||||
|
||||
def __getstate__(self):
|
||||
return to_pickable(self._mpc_[0]), to_pickable(self._mpc_[1])
|
||||
|
||||
def __setstate__(self, val):
|
||||
self._mpc_ = from_pickable(val[0]), from_pickable(val[1])
|
||||
|
||||
def __repr__(s):
|
||||
if s.context.pretty:
|
||||
return str(s)
|
||||
r = repr(s.real)[4:-1]
|
||||
i = repr(s.imag)[4:-1]
|
||||
return "%s(real=%s, imag=%s)" % (type(s).__name__, r, i)
|
||||
|
||||
def __str__(s):
|
||||
return "(%s)" % mpc_to_str(s._mpc_, s.context._str_digits)
|
||||
|
||||
def __complex__(s):
|
||||
return mpc_to_complex(s._mpc_)
|
||||
|
||||
def __pos__(s):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_pos(s._mpc_, prec, rounding)
|
||||
return v
|
||||
|
||||
def __abs__(s):
|
||||
prec, rounding = s.context._prec_rounding
|
||||
v = new(s.context.mpf)
|
||||
v._mpf_ = mpc_abs(s._mpc_, prec, rounding)
|
||||
return v
|
||||
|
||||
def __neg__(s):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_neg(s._mpc_, prec, rounding)
|
||||
return v
|
||||
|
||||
def conjugate(s):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_conjugate(s._mpc_, prec, rounding)
|
||||
return v
|
||||
|
||||
def __nonzero__(s):
|
||||
return mpc_is_nonzero(s._mpc_)
|
||||
|
||||
def __hash__(s):
|
||||
return mpc_hash(s._mpc_)
|
||||
|
||||
@classmethod
|
||||
def mpc_convert_lhs(cls, x):
|
||||
try:
|
||||
y = cls.context.convert(x)
|
||||
return y
|
||||
except TypeError:
|
||||
return NotImplemented
|
||||
|
||||
def __eq__(s, t):
|
||||
if not hasattr(t, '_mpc_'):
|
||||
if isinstance(t, str):
|
||||
return False
|
||||
t = s.mpc_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
return s.real == t.real and s.imag == t.imag
|
||||
|
||||
def __ne__(s, t):
|
||||
b = s.__eq__(t)
|
||||
if b is NotImplemented:
|
||||
return b
|
||||
return not b
|
||||
|
||||
def _compare(*args):
|
||||
raise TypeError("no ordering relation is defined for complex numbers")
|
||||
|
||||
__gt__ = _compare
|
||||
__le__ = _compare
|
||||
__gt__ = _compare
|
||||
__ge__ = _compare
|
||||
|
||||
def __add__(s, t):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
if not hasattr(t, '_mpc_'):
|
||||
t = s.mpc_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
if hasattr(t, '_mpf_'):
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_add_mpf(s._mpc_, t._mpf_, prec, rounding)
|
||||
return v
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_add(s._mpc_, t._mpc_, prec, rounding)
|
||||
return v
|
||||
|
||||
def __sub__(s, t):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
if not hasattr(t, '_mpc_'):
|
||||
t = s.mpc_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
if hasattr(t, '_mpf_'):
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_sub_mpf(s._mpc_, t._mpf_, prec, rounding)
|
||||
return v
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_sub(s._mpc_, t._mpc_, prec, rounding)
|
||||
return v
|
||||
|
||||
def __mul__(s, t):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
if not hasattr(t, '_mpc_'):
|
||||
if isinstance(t, int_types):
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_mul_int(s._mpc_, t, prec, rounding)
|
||||
return v
|
||||
t = s.mpc_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
if hasattr(t, '_mpf_'):
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_mul_mpf(s._mpc_, t._mpf_, prec, rounding)
|
||||
return v
|
||||
t = s.mpc_convert_lhs(t)
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_mul(s._mpc_, t._mpc_, prec, rounding)
|
||||
return v
|
||||
|
||||
def __div__(s, t):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
if not hasattr(t, '_mpc_'):
|
||||
t = s.mpc_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
if hasattr(t, '_mpf_'):
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_div_mpf(s._mpc_, t._mpf_, prec, rounding)
|
||||
return v
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_div(s._mpc_, t._mpc_, prec, rounding)
|
||||
return v
|
||||
|
||||
def __pow__(s, t):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
if isinstance(t, int_types):
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_pow_int(s._mpc_, t, prec, rounding)
|
||||
return v
|
||||
t = s.mpc_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
v = new(cls)
|
||||
if hasattr(t, '_mpf_'):
|
||||
v._mpc_ = mpc_pow_mpf(s._mpc_, t._mpf_, prec, rounding)
|
||||
else:
|
||||
v._mpc_ = mpc_pow(s._mpc_, t._mpc_, prec, rounding)
|
||||
return v
|
||||
|
||||
__radd__ = __add__
|
||||
|
||||
def __rsub__(s, t):
|
||||
t = s.mpc_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
return t - s
|
||||
|
||||
def __rmul__(s, t):
|
||||
cls, new, (prec, rounding) = s._ctxdata
|
||||
if isinstance(t, int_types):
|
||||
v = new(cls)
|
||||
v._mpc_ = mpc_mul_int(s._mpc_, t, prec, rounding)
|
||||
return v
|
||||
t = s.mpc_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
return t * s
|
||||
|
||||
def __rdiv__(s, t):
|
||||
t = s.mpc_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
return t / s
|
||||
|
||||
def __rpow__(s, t):
|
||||
t = s.mpc_convert_lhs(t)
|
||||
if t is NotImplemented:
|
||||
return t
|
||||
return t ** s
|
||||
|
||||
__truediv__ = __div__
|
||||
__rtruediv__ = __rdiv__
|
||||
|
||||
def ae(s, t, rel_eps=None, abs_eps=None):
|
||||
return s.context.almosteq(s, t, rel_eps, abs_eps)
|
||||
|
||||
|
||||
complex_types = (complex, _mpc)
|
||||
|
||||
|
||||
class PythonMPContext:
|
||||
|
||||
def __init__(ctx):
|
||||
ctx._prec_rounding = [53, round_nearest]
|
||||
ctx.mpf = type('mpf', (_mpf,), {})
|
||||
ctx.mpc = type('mpc', (_mpc,), {})
|
||||
ctx.mpf._ctxdata = [ctx.mpf, new, ctx._prec_rounding]
|
||||
ctx.mpc._ctxdata = [ctx.mpc, new, ctx._prec_rounding]
|
||||
ctx.mpf.context = ctx
|
||||
ctx.mpc.context = ctx
|
||||
ctx.constant = type('constant', (_constant,), {})
|
||||
ctx.constant._ctxdata = [ctx.mpf, new, ctx._prec_rounding]
|
||||
ctx.constant.context = ctx
|
||||
|
||||
def make_mpf(ctx, v):
|
||||
a = new(ctx.mpf)
|
||||
a._mpf_ = v
|
||||
return a
|
||||
|
||||
def make_mpc(ctx, v):
|
||||
a = new(ctx.mpc)
|
||||
a._mpc_ = v
|
||||
return a
|
||||
|
||||
def default(ctx):
|
||||
ctx._prec = ctx._prec_rounding[0] = 53
|
||||
ctx._dps = 15
|
||||
ctx.trap_complex = False
|
||||
|
||||
def _set_prec(ctx, n):
|
||||
ctx._prec = ctx._prec_rounding[0] = max(1, int(n))
|
||||
ctx._dps = prec_to_dps(n)
|
||||
|
||||
def _set_dps(ctx, n):
|
||||
ctx._prec = ctx._prec_rounding[0] = dps_to_prec(n)
|
||||
ctx._dps = max(1, int(n))
|
||||
|
||||
prec = property(lambda ctx: ctx._prec, _set_prec)
|
||||
dps = property(lambda ctx: ctx._dps, _set_dps)
|
||||
|
||||
def convert(ctx, x, strings=True):
|
||||
"""
|
||||
Converts *x* to an ``mpf``, ``mpc`` or ``mpi``. If *x* is of type ``mpf``,
|
||||
``mpc``, ``int``, ``float``, ``complex``, the conversion
|
||||
will be performed losslessly.
|
||||
|
||||
If *x* is a string, the result will be rounded to the present
|
||||
working precision. Strings representing fractions or complex
|
||||
numbers are permitted.
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = False
|
||||
>>> mpmathify(3.5)
|
||||
mpf('3.5')
|
||||
>>> mpmathify('2.1')
|
||||
mpf('2.1000000000000001')
|
||||
>>> mpmathify('3/4')
|
||||
mpf('0.75')
|
||||
>>> mpmathify('2+3j')
|
||||
mpc(real='2.0', imag='3.0')
|
||||
|
||||
"""
|
||||
if type(x) in ctx.types: return x
|
||||
if isinstance(x, int_types): return ctx.make_mpf(from_int(x))
|
||||
if isinstance(x, float): return ctx.make_mpf(from_float(x))
|
||||
if isinstance(x, complex):
|
||||
return ctx.make_mpc((from_float(x.real), from_float(x.imag)))
|
||||
prec, rounding = ctx._prec_rounding
|
||||
if isinstance(x, rational.mpq):
|
||||
p, q = x
|
||||
return ctx.make_mpf(from_rational(p, q, prec))
|
||||
if strings and isinstance(x, basestring):
|
||||
try:
|
||||
_mpf_ = from_str(x, prec, rounding)
|
||||
return ctx.make_mpf(_mpf_)
|
||||
except ValueError:
|
||||
pass
|
||||
if hasattr(x, '_mpf_'): return ctx.make_mpf(x._mpf_)
|
||||
if hasattr(x, '_mpc_'): return ctx.make_mpc(x._mpc_)
|
||||
if hasattr(x, '_mpmath_'):
|
||||
return ctx.convert(x._mpmath_(prec, rounding))
|
||||
return ctx._convert_fallback(x, strings)
|
||||
|
||||
def isnan(ctx, x):
|
||||
"""
|
||||
For an ``mpf`` *x*, determines whether *x* is not-a-number (nan)::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> isnan(nan), isnan(3)
|
||||
(True, False)
|
||||
"""
|
||||
if not hasattr(x, '_mpf_'):
|
||||
return False
|
||||
return x._mpf_ == fnan
|
||||
|
||||
def isinf(ctx, x):
|
||||
"""
|
||||
For an ``mpf`` *x*, determines whether *x* is infinite::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> isinf(inf), isinf(-inf), isinf(3)
|
||||
(True, True, False)
|
||||
"""
|
||||
if not hasattr(x, '_mpf_'):
|
||||
return False
|
||||
return x._mpf_ in (finf, fninf)
|
||||
|
||||
def isint(ctx, x):
|
||||
"""
|
||||
For an ``mpf`` *x*, or any type that can be converted
|
||||
to ``mpf``, determines whether *x* is exactly
|
||||
integer-valued::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> isint(3), isint(mpf(3)), isint(3.2)
|
||||
(True, True, False)
|
||||
"""
|
||||
if isinstance(x, int_types):
|
||||
return True
|
||||
try:
|
||||
x = ctx.convert(x)
|
||||
except:
|
||||
return False
|
||||
if hasattr(x, '_mpf_'):
|
||||
if ctx.isnan(x) or ctx.isinf(x):
|
||||
return False
|
||||
return x == int(x)
|
||||
if isinstance(x, ctx.mpq):
|
||||
p, q = x
|
||||
return not (p % q)
|
||||
return False
|
||||
|
||||
def fsum(ctx, terms, absolute=False, squared=False):
|
||||
"""
|
||||
Calculates a sum containing a finite number of terms (for infinite
|
||||
series, see :func:`nsum`). The terms will be converted to
|
||||
mpmath numbers. For len(terms) > 2, this function is generally
|
||||
faster and produces more accurate results than the builtin
|
||||
Python function :func:`sum`.
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = False
|
||||
>>> fsum([1, 2, 0.5, 7])
|
||||
mpf('10.5')
|
||||
|
||||
With squared=True each term is squared, and with absolute=True
|
||||
the absolute value of each term is used.
|
||||
"""
|
||||
prec, rnd = ctx._prec_rounding
|
||||
real = []
|
||||
imag = []
|
||||
other = 0
|
||||
for term in terms:
|
||||
reval = imval = 0
|
||||
if hasattr(term, "_mpf_"):
|
||||
reval = term._mpf_
|
||||
elif hasattr(term, "_mpc_"):
|
||||
reval, imval = term._mpc_
|
||||
else:
|
||||
term = ctx.convert(term)
|
||||
if hasattr(term, "_mpf_"):
|
||||
reval = term._mpf_
|
||||
elif hasattr(term, "_mpc_"):
|
||||
reval, imval = term._mpc_
|
||||
else:
|
||||
if absolute: term = ctx.absmax(term)
|
||||
if squared: term = term**2
|
||||
other += term
|
||||
continue
|
||||
if imval:
|
||||
if squared:
|
||||
if absolute:
|
||||
real.append(mpf_mul(reval,reval))
|
||||
real.append(mpf_mul(imval,imval))
|
||||
else:
|
||||
reval, imval = mpc_pow_int((reval,imval),2,prec+10)
|
||||
real.append(reval)
|
||||
imag.append(imval)
|
||||
elif absolute:
|
||||
real.append(mpc_abs((reval,imval), prec))
|
||||
else:
|
||||
real.append(reval)
|
||||
imag.append(imval)
|
||||
else:
|
||||
if squared:
|
||||
reval = mpf_mul(reval, reval)
|
||||
elif absolute:
|
||||
reval = mpf_abs(reval)
|
||||
real.append(reval)
|
||||
s = mpf_sum(real, prec, rnd, absolute)
|
||||
if imag:
|
||||
s = ctx.make_mpc((s, mpf_sum(imag, prec, rnd)))
|
||||
else:
|
||||
s = ctx.make_mpf(s)
|
||||
if other is 0:
|
||||
return s
|
||||
else:
|
||||
return s + other
|
||||
|
||||
def fdot(ctx, A, B=None):
|
||||
r"""
|
||||
Computes the dot product of the iterables `A` and `B`,
|
||||
|
||||
.. math ::
|
||||
|
||||
\sum_{k=0} A_k B_k.
|
||||
|
||||
Alternatively, :func:`fdot` accepts a single iterable of pairs.
|
||||
In other words, ``fdot(A,B)`` and ``fdot(zip(A,B))`` are equivalent.
|
||||
|
||||
The elements are automatically converted to mpmath numbers.
|
||||
|
||||
Examples::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 15; mp.pretty = False
|
||||
>>> A = [2, 1.5, 3]
|
||||
>>> B = [1, -1, 2]
|
||||
>>> fdot(A, B)
|
||||
mpf('6.5')
|
||||
>>> zip(A, B)
|
||||
[(2, 1), (1.5, -1), (3, 2)]
|
||||
>>> fdot(_)
|
||||
mpf('6.5')
|
||||
|
||||
"""
|
||||
if B:
|
||||
A = zip(A, B)
|
||||
prec, rnd = ctx._prec_rounding
|
||||
real = []
|
||||
imag = []
|
||||
other = 0
|
||||
hasattr_ = hasattr
|
||||
types = (ctx.mpf, ctx.mpc)
|
||||
for a, b in A:
|
||||
if type(a) not in types: a = ctx.convert(a)
|
||||
if type(b) not in types: b = ctx.convert(b)
|
||||
a_real = hasattr_(a, "_mpf_")
|
||||
b_real = hasattr_(b, "_mpf_")
|
||||
if a_real and b_real:
|
||||
real.append(mpf_mul(a._mpf_, b._mpf_))
|
||||
continue
|
||||
a_complex = hasattr_(a, "_mpc_")
|
||||
b_complex = hasattr_(b, "_mpc_")
|
||||
if a_real and b_complex:
|
||||
aval = a._mpf_
|
||||
bre, bim = b._mpc_
|
||||
real.append(mpf_mul(aval, bre))
|
||||
imag.append(mpf_mul(aval, bim))
|
||||
elif b_real and a_complex:
|
||||
are, aim = a._mpc_
|
||||
bval = b._mpf_
|
||||
real.append(mpf_mul(are, bval))
|
||||
imag.append(mpf_mul(aim, bval))
|
||||
elif a_complex and b_complex:
|
||||
re, im = mpc_mul(a._mpc_, b._mpc_, prec+20)
|
||||
real.append(re)
|
||||
imag.append(im)
|
||||
else:
|
||||
other += a*b
|
||||
s = mpf_sum(real, prec, rnd)
|
||||
if imag:
|
||||
s = ctx.make_mpc((s, mpf_sum(imag, prec, rnd)))
|
||||
else:
|
||||
s = ctx.make_mpf(s)
|
||||
if other is 0:
|
||||
return s
|
||||
else:
|
||||
return s + other
|
||||
|
||||
def _wrap_libmp_function(ctx, mpf_f, mpc_f=None, mpi_f=None, doc="<no doc>"):
|
||||
"""
|
||||
Given a low-level mpf_ function, and optionally similar functions
|
||||
for mpc_ and mpi_, defines the function as a context method.
|
||||
|
||||
It is assumed that the return type is the same as that of
|
||||
the input; the exception is that propagation from mpf to mpc is possible
|
||||
by raising ComplexResult.
|
||||
|
||||
"""
|
||||
def f(x, **kwargs):
|
||||
if type(x) not in ctx.types:
|
||||
x = ctx.convert(x)
|
||||
prec, rounding = ctx._prec_rounding
|
||||
if kwargs:
|
||||
prec = kwargs.get('prec', prec)
|
||||
if 'dps' in kwargs:
|
||||
prec = dps_to_prec(kwargs['dps'])
|
||||
rounding = kwargs.get('rounding', rounding)
|
||||
if hasattr(x, '_mpf_'):
|
||||
try:
|
||||
return ctx.make_mpf(mpf_f(x._mpf_, prec, rounding))
|
||||
except ComplexResult:
|
||||
# Handle propagation to complex
|
||||
if ctx.trap_complex:
|
||||
raise
|
||||
return ctx.make_mpc(mpc_f((x._mpf_, fzero), prec, rounding))
|
||||
elif hasattr(x, '_mpc_'):
|
||||
return ctx.make_mpc(mpc_f(x._mpc_, prec, rounding))
|
||||
elif hasattr(x, '_mpi_'):
|
||||
if mpi_f:
|
||||
return ctx.make_mpi(mpi_f(x._mpi_, prec))
|
||||
raise NotImplementedError("%s of a %s" % (name, type(x)))
|
||||
name = mpf_f.__name__[4:]
|
||||
f.__doc__ = function_docs.__dict__.get(name, "Computes the %s of x" % doc)
|
||||
return f
|
||||
|
||||
# Called by SpecialFunctions.__init__()
|
||||
@classmethod
|
||||
def _wrap_specfun(cls, name, f, wrap):
|
||||
if wrap:
|
||||
def f_wrapped(ctx, *args, **kwargs):
|
||||
convert = ctx.convert
|
||||
args = [convert(a) for a in args]
|
||||
prec = ctx.prec
|
||||
try:
|
||||
ctx.prec += 10
|
||||
retval = f(ctx, *args, **kwargs)
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
return +retval
|
||||
else:
|
||||
f_wrapped = f
|
||||
f_wrapped.__doc__ = function_docs.__dict__.get(name, "<no doc>")
|
||||
setattr(cls, name, f_wrapped)
|
||||
|
||||
def _convert_param(ctx, x):
|
||||
if hasattr(x, "_mpc_"):
|
||||
v, im = x._mpc_
|
||||
if im != fzero:
|
||||
return x, 'C'
|
||||
elif hasattr(x, "_mpf_"):
|
||||
v = x._mpf_
|
||||
else:
|
||||
if type(x) in int_types:
|
||||
return int(x), 'Z'
|
||||
p = None
|
||||
if isinstance(x, tuple):
|
||||
p, q = x
|
||||
elif isinstance(x, basestring) and '/' in x:
|
||||
p, q = x.split('/')
|
||||
p = int(p)
|
||||
q = int(q)
|
||||
if p is not None:
|
||||
if not p % q:
|
||||
return p // q, 'Z'
|
||||
return ctx.mpq((p,q)), 'Q'
|
||||
x = ctx.convert(x)
|
||||
if hasattr(x, "_mpc_"):
|
||||
v, im = x._mpc_
|
||||
if im != fzero:
|
||||
return x, 'C'
|
||||
elif hasattr(x, "_mpf_"):
|
||||
v = x._mpf_
|
||||
else:
|
||||
return x, 'U'
|
||||
sign, man, exp, bc = v
|
||||
if man:
|
||||
if exp >= -4:
|
||||
if sign:
|
||||
man = -man
|
||||
if exp >= 0:
|
||||
return int(man) << exp, 'Z'
|
||||
if exp >= -4:
|
||||
p, q = int(man), (1<<(-exp))
|
||||
return ctx.mpq((p,q)), 'Q'
|
||||
x = ctx.make_mpf(v)
|
||||
return x, 'R'
|
||||
elif not exp:
|
||||
return 0, 'Z'
|
||||
else:
|
||||
return x, 'U'
|
||||
|
||||
def _mpf_mag(ctx, x):
|
||||
sign, man, exp, bc = x
|
||||
if man:
|
||||
return exp+bc
|
||||
if x == fzero:
|
||||
return ctx.ninf
|
||||
if x == finf or x == fninf:
|
||||
return ctx.inf
|
||||
return ctx.nan
|
||||
|
||||
def mag(ctx, x):
|
||||
"""
|
||||
Quick logarithmic magnitude estimate of a number.
|
||||
Returns an integer or infinity `m` such that `|x| <= 2^m`.
|
||||
It is not guaranteed that `m` is an optimal bound,
|
||||
but it will never be off by more than 2 (and probably not
|
||||
more than 1).
|
||||
"""
|
||||
if hasattr(x, "_mpf_"):
|
||||
return ctx._mpf_mag(x._mpf_)
|
||||
elif hasattr(x, "_mpc_"):
|
||||
r, i = x._mpc_
|
||||
if r == fzero:
|
||||
return ctx._mpf_mag(i)
|
||||
if i == fzero:
|
||||
return ctx._mpf_mag(r)
|
||||
return 1+max(ctx._mpf_mag(r), ctx._mpf_mag(i))
|
||||
elif isinstance(x, int_types):
|
||||
if x:
|
||||
return bitcount(abs(x))
|
||||
return ctx.ninf
|
||||
elif isinstance(x, rational.mpq):
|
||||
p, q = x
|
||||
if p:
|
||||
return 1 + bitcount(abs(p)) - bitcount(abs(q))
|
||||
return ctx.ninf
|
||||
else:
|
||||
x = ctx.convert(x)
|
||||
if hasattr(x, "_mpf_") or hasattr(x, "_mpc_"):
|
||||
return ctx.mag(x)
|
||||
else:
|
||||
raise TypeError("requires an mpf/mpc")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,7 @@
|
|||
import functions
|
||||
# Hack to update methods
|
||||
import factorials
|
||||
import hypergeometric
|
||||
import elliptic
|
||||
import zeta
|
||||
import rszeta
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,196 @@
|
|||
from functions import defun, defun_wrapped
|
||||
|
||||
@defun
|
||||
def gammaprod(ctx, a, b, _infsign=False):
|
||||
a = [ctx.convert(x) for x in a]
|
||||
b = [ctx.convert(x) for x in b]
|
||||
poles_num = []
|
||||
poles_den = []
|
||||
regular_num = []
|
||||
regular_den = []
|
||||
for x in a: [regular_num, poles_num][ctx.isnpint(x)].append(x)
|
||||
for x in b: [regular_den, poles_den][ctx.isnpint(x)].append(x)
|
||||
# One more pole in numerator or denominator gives 0 or inf
|
||||
if len(poles_num) < len(poles_den): return ctx.zero
|
||||
if len(poles_num) > len(poles_den):
|
||||
# Get correct sign of infinity for x+h, h -> 0 from above
|
||||
# XXX: hack, this should be done properly
|
||||
if _infsign:
|
||||
a = [x and x*(1+ctx.eps) or x+ctx.eps for x in poles_num]
|
||||
b = [x and x*(1+ctx.eps) or x+ctx.eps for x in poles_den]
|
||||
return ctx.sign(ctx.gammaprod(a+regular_num,b+regular_den)) * ctx.inf
|
||||
else:
|
||||
return ctx.inf
|
||||
# All poles cancel
|
||||
# lim G(i)/G(j) = (-1)**(i+j) * gamma(1-j) / gamma(1-i)
|
||||
p = ctx.one
|
||||
orig = ctx.prec
|
||||
try:
|
||||
ctx.prec = orig + 15
|
||||
while poles_num:
|
||||
i = poles_num.pop()
|
||||
j = poles_den.pop()
|
||||
p *= (-1)**(i+j) * ctx.gamma(1-j) / ctx.gamma(1-i)
|
||||
for x in regular_num: p *= ctx.gamma(x)
|
||||
for x in regular_den: p /= ctx.gamma(x)
|
||||
finally:
|
||||
ctx.prec = orig
|
||||
return +p
|
||||
|
||||
@defun
|
||||
def beta(ctx, x, y):
|
||||
x = ctx.convert(x)
|
||||
y = ctx.convert(y)
|
||||
if ctx.isinf(y):
|
||||
x, y = y, x
|
||||
if ctx.isinf(x):
|
||||
if x == ctx.inf and not ctx._im(y):
|
||||
if y == ctx.ninf:
|
||||
return ctx.nan
|
||||
if y > 0:
|
||||
return ctx.zero
|
||||
if ctx.isint(y):
|
||||
return ctx.nan
|
||||
if y < 0:
|
||||
return ctx.sign(ctx.gamma(y)) * ctx.inf
|
||||
return ctx.nan
|
||||
return ctx.gammaprod([x, y], [x+y])
|
||||
|
||||
@defun
|
||||
def binomial(ctx, n, k):
|
||||
return ctx.gammaprod([n+1], [k+1, n-k+1])
|
||||
|
||||
@defun
|
||||
def rf(ctx, x, n):
|
||||
return ctx.gammaprod([x+n], [x])
|
||||
|
||||
@defun
|
||||
def ff(ctx, x, n):
|
||||
return ctx.gammaprod([x+1], [x-n+1])
|
||||
|
||||
@defun_wrapped
|
||||
def fac2(ctx, x):
|
||||
if ctx.isinf(x):
|
||||
if x == ctx.inf:
|
||||
return x
|
||||
return ctx.nan
|
||||
return 2**(x/2)*(ctx.pi/2)**((ctx.cospi(x)-1)/4)*ctx.gamma(x/2+1)
|
||||
|
||||
@defun_wrapped
|
||||
def barnesg(ctx, z):
|
||||
if ctx.isinf(z):
|
||||
if z == ctx.inf:
|
||||
return z
|
||||
return ctx.nan
|
||||
if ctx.isnan(z):
|
||||
return z
|
||||
if (not ctx._im(z)) and ctx._re(z) <= 0 and ctx.isint(ctx._re(z)):
|
||||
return z*0
|
||||
# Account for size (would not be needed if computing log(G))
|
||||
if abs(z) > 5:
|
||||
ctx.dps += 2*ctx.log(abs(z),2)
|
||||
# Reflection formula
|
||||
if ctx.re(z) < -ctx.dps:
|
||||
w = 1-z
|
||||
pi2 = 2*ctx.pi
|
||||
u = ctx.expjpi(2*w)
|
||||
v = ctx.j*ctx.pi/12 - ctx.j*ctx.pi*w**2/2 + w*ctx.ln(1-u) - \
|
||||
ctx.j*ctx.polylog(2, u)/pi2
|
||||
v = ctx.barnesg(2-z)*ctx.exp(v)/pi2**w
|
||||
if ctx._is_real_type(z):
|
||||
v = ctx._re(v)
|
||||
return v
|
||||
# Estimate terms for asymptotic expansion
|
||||
# TODO: fixme, obviously
|
||||
N = ctx.dps // 2 + 5
|
||||
G = 1
|
||||
while abs(z) < N or ctx.re(z) < 1:
|
||||
G /= ctx.gamma(z)
|
||||
z += 1
|
||||
z -= 1
|
||||
s = ctx.mpf(1)/12
|
||||
s -= ctx.log(ctx.glaisher)
|
||||
s += z*ctx.log(2*ctx.pi)/2
|
||||
s += (z**2/2-ctx.mpf(1)/12)*ctx.log(z)
|
||||
s -= 3*z**2/4
|
||||
z2k = z2 = z**2
|
||||
for k in xrange(1, N+1):
|
||||
t = ctx.bernoulli(2*k+2) / (4*k*(k+1)*z2k)
|
||||
if abs(t) < ctx.eps:
|
||||
#print k, N # check how many terms were needed
|
||||
break
|
||||
z2k *= z2
|
||||
s += t
|
||||
#if k == N:
|
||||
# print "warning: series for barnesg failed to converge", ctx.dps
|
||||
return G*ctx.exp(s)
|
||||
|
||||
@defun
|
||||
def superfac(ctx, z):
|
||||
return ctx.barnesg(z+2)
|
||||
|
||||
@defun_wrapped
|
||||
def hyperfac(ctx, z):
|
||||
# XXX: estimate needed extra bits accurately
|
||||
if z == ctx.inf:
|
||||
return z
|
||||
if abs(z) > 5:
|
||||
extra = 4*int(ctx.log(abs(z),2))
|
||||
else:
|
||||
extra = 0
|
||||
ctx.prec += extra
|
||||
if not ctx._im(z) and ctx._re(z) < 0 and ctx.isint(ctx._re(z)):
|
||||
n = int(ctx.re(z))
|
||||
h = ctx.hyperfac(-n-1)
|
||||
if ((n+1)//2) & 1:
|
||||
h = -h
|
||||
if ctx._is_complex_type(z):
|
||||
return h + 0j
|
||||
return h
|
||||
zp1 = z+1
|
||||
# Wrong branch cut
|
||||
#v = ctx.gamma(zp1)**z
|
||||
#ctx.prec -= extra
|
||||
#return v / ctx.barnesg(zp1)
|
||||
v = ctx.exp(z*ctx.loggamma(zp1))
|
||||
ctx.prec -= extra
|
||||
return v / ctx.barnesg(zp1)
|
||||
|
||||
@defun_wrapped
|
||||
def loggamma(ctx, z):
|
||||
a = ctx._re(z)
|
||||
b = ctx._im(z)
|
||||
if not b and a > 0:
|
||||
return ctx.ln(ctx.gamma(z))
|
||||
u = ctx.arg(z)
|
||||
w = ctx.ln(ctx.gamma(z))
|
||||
if b:
|
||||
gi = -b - u/2 + a*u + b*ctx.ln(abs(z))
|
||||
n = ctx.floor((gi-ctx._im(w))/(2*ctx.pi)+0.5) * (2*ctx.pi)
|
||||
return w + n*ctx.j
|
||||
elif a < 0:
|
||||
n = int(ctx.floor(a))
|
||||
w += (n-(n%2))*ctx.pi*ctx.j
|
||||
return w
|
||||
|
||||
'''
|
||||
@defun
|
||||
def psi0(ctx, z):
|
||||
"""Shortcut for psi(0,z) (the digamma function)"""
|
||||
return ctx.psi(0, z)
|
||||
|
||||
@defun
|
||||
def psi1(ctx, z):
|
||||
"""Shortcut for psi(1,z) (the trigamma function)"""
|
||||
return ctx.psi(1, z)
|
||||
|
||||
@defun
|
||||
def psi2(ctx, z):
|
||||
"""Shortcut for psi(2,z) (the tetragamma function)"""
|
||||
return ctx.psi(2, z)
|
||||
|
||||
@defun
|
||||
def psi3(ctx, z):
|
||||
"""Shortcut for psi(3,z) (the pentagamma function)"""
|
||||
return ctx.psi(3, z)
|
||||
'''
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue