RELEASE 1.0

This commit is contained in:
Matt Guthaus 2016-11-08 09:57:35 -08:00
parent bdcebad086
commit f48272bde6
463 changed files with 122469 additions and 0 deletions

10
ICCAD16_openram_paper/.gitignore vendored Normal file
View File

@ -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.

View File

@ -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

View File

@ -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}

View File

@ -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

View File

@ -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'.

View File

@ -0,0 +1 @@
\appendix

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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}.

View File

@ -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}

View File

@ -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}
}

View File

@ -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 OpenRAMs 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

16
ICCAD16_openram_paper/t.sh Executable file
View File

@ -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

107
README Normal file
View File

@ -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.

34
compiler/TODO Normal file
View File

@ -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?

1518
compiler/bank.py Normal file

File diff suppressed because it is too large Load Diff

23
compiler/bitcell.py Normal file
View File

@ -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"]

142
compiler/bitcell_array.py Normal file
View File

@ -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

325
compiler/calibre.py Normal file
View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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")

View File

@ -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

View File

@ -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)

112
compiler/contact.py Normal file
View File

@ -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)

696
compiler/control_logic.py Normal file
View File

@ -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)

33
compiler/debug.py Normal file
View File

@ -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

65
compiler/design.py Normal file
View File

@ -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

View File

@ -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"

78
compiler/gdsMill/LICENSE Normal file
View File

@ -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

30
compiler/gdsMill/README Normal file
View File

@ -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

View File

@ -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")

View File

@ -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")

View File

@ -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

View File

@ -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")

10
compiler/gdsMill/gdsMill.sh Executable file
View File

@ -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

View File

@ -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 *

View File

@ -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]

View File

@ -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()

View File

@ -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=""

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -0,0 +1,6 @@
import calculus
# XXX: hack to set methods
import approximation
import differentiation
import extrapolation
import polynomials

View File

@ -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

View File

@ -0,0 +1,5 @@
class CalculusMethods(object):
pass
def defun(f):
setattr(CalculusMethods, f.__name__, f)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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