\section{Software Implementation} \label{sec:implementation} OpenRAM is implemented using object-oriented data structures in the Python programming language. The top-level executable is \verb|openram.py| which parses input arguments, creates the memory and saves the output. \subsection{Design Hierarchy} \label{sec:design} All modules in OpenRAM are derived from the \verb|design| class in \verb|design.py|. The design class is a data structure that consists of a spice netlist, a layout, and a name. The spice netlist capabilities are inherited from the \verb|hierarchy_spice| class while the layout capabilities are inherited from the \verb|hierarchy_layout| class. The only additional function in design.py is \verb|DRC_LVS()|, which performs a DRC/LVS check on the module. \begin{figure}[htb] \centering \includegraphics[width=10cm]{./figs/class_hierarchy.pdf} \caption{Class hierarchy} \label{fig:class_hierarchy} \end{figure} \subsubsection{Spice Hierarchy} The spice hierarchy is stored in the \verb|spice| class in \verb|hierarchy_spice.py|. When the design class is initialized for a module, a data structure for the spice hierarchy is created. The spice data stucture name becomes the name of the top-level subcircuit definition for the module. The list of pins for the module are added to the subcircuit definition by using the \verb|add_pin()| function. The \verb|add_mod()| function adds an instance of a module/library\_cell/parameterized\_cell as a subcircuit to the top-level structure. Each time a sub-module has been added to the hierarchy, the pins of the sub-module must be connected using the \verb|connect_pins()| function. It is important to note that the pins must be listed in the same order as they were added to the submodule. Also, an assertion error will occur if there is a mismatch in the number of net connections. The \verb|spice| class also contains functions for reading or writing spice files: \begin{itemize} \item \verb|sp_read():| this function is used to read in spice netlists and parse the inputs defined by the ``subckt'' definition. \item \verb|sp_write():| this function creates an empty spice file in write mode and calls \verb|sp_write_file()|. \item \verb|sp_write_file():| this function recursively writes the modules and sub-modules from the data structure into the spice file created by \verb|sp_write()|. \end{itemize} \subsubsection{Layout Hierarchy} The layout hierarchy is stroed in the \verb|layout| class in \verb|hierarchy_layout.py|. When the design class is initialized for a module, a data structure for the layout hierarchy is created. The layout data structure has two main components: a structure for the instances of sub-modules contained in the layout, and a structure for the objects (such as shapes, labels, etc...) contained in the layout. The functions included in the \verb|layout| class are: \begin{itemize} \item \verb|def add_inst(self,name,mod,offset,mirror):| adds an instance of a physical layout (library cell, module, or parameterized cell) to the module. The input parameters are : \begin{description} \item[name] - name for the instance. \item[mod] - the associated spice module. \item[offset] - the x-y coordinates, in microns, where the instance should be placed in the layout. \item[mirror] - mirror or rotate the instance before it is added to the layout. Accepted values for mirror are: \verb|"R0", "R90", "R180", "R270"| $^\ast$Currently, only ``R0'' works.\\ \verb|"MX" or "x", "MY" or "y", "XY" or "xy"| (``xy'' is equivalent to ``R180'') \end{description} \item \verb|add_rect(self,layerNumber,offset,width,height):| adds a rectangle to the module's layout. The inputs are: \begin{description} \item[layernumber] - the layer that the rectangle is to be drawn in. \item[offset] - the x-y coordinates, in microns, where the rectangle's origin will be placed in the layout. \item[width] - the width of the rectangle, can be positive or negative value. \item[height] - the height of the rectangle, can be positive or negative value. \end{description} \item \verb|add_label(self,text,layerNumber,offset,zoom):| adds a label to the layout. The inputs are: \begin{description} \item[text] - the text for the label \item[layernumber] - the layer that the label is to be drawn in . \item[offset] - the x-y coordinates, in microns, where the label will be placed in the layout. \item[zoom] - magnification of the label (ex: ``1e9''). \end{description} \item \verb|add_path(self,layerNumber,coordinates,width):| this function is under construction... \item \verb|gds_read():| reads in a GDSII file and creates a \verb|VlsiLayout()| class for it. \item \verb|gds_write():| writes the entire GDS of the object to a file by gdsMill \verb|vlsiLayout()| class and calling the \verb|gds2writer()| (see Sections~\ref{sec:vlsilayout} and~\ref{sec:gdsmill}. \item \verb|gds_write_file():| recursively the instances and objects in layout data structure to the gds file. \item \verb|pdf_write():| this function is under construction... \end{itemize} \subsection{Creating a New Design Module} \label{sec:new_design} Each module in the SRAM is its own Python class, which contains a design class, or data structure, for the layout and spice. The \verb|design| class (\verb|design.py|) is initialized within the module class, subsequently creating separate data structurse to hold the layout (\verb|hierarchy_layout|) and spice (\verb|hierarchy_spice|) information. By having a class for each module, it is very easy to instatiate instances of the modules in any level of the hierarchy. Follow these guidelines when creating a new module: \begin{itemize} \item Derive your class from the design module: \begin{verbatim} class bitcell_array(design.design): \end{verbatim} \item Always use the python constructor \verb|__init__| method so that your class is initialized when an object of the module is instatiated. The module parameters should also be declared: \begin{verbatim} def __init__(self, cols, rows): \end{verbatim} \item In the constructor, call the base class constructor with the name such as: \begin{verbatim} design.design.__init__(self,"bitcell_array") \end{verbatim} \item Add the pins that will be used in the spice netlist for your module using the \verb|add_pin()| function from the \verb|hierarchy_spice| class. \begin{verbatim} self.add_pin("vdd") \end{verbatim} \item Create an instance of the module/library\_cell/parameterized cell that you want to add to your module: \begin{verbatim} cell=bitcell.bitcell(cell_6t) \end{verbatim} \item Add the subckt/submodule instance to the spice hierarchy using the \verb|add_mod()| function from the \verb|hierarchy_spice| class: \begin{verbatim} self.add_mod(cell) \end{verbatim} \item Add layout instance into your module's layout hierarchy using the \verb|add_instance|() function, which takes a name, mod, offset, and mirror as inputs: \begin{verbatim} self.add_inst(name=name,mod=cell,offset=[x_off,y_off],mirror=x) \end{verbatim} \item Connect the pins of the instance that was just added by using the \verb|connect_pins| function from the \verb|hierarchy_spice| class: \begin{verbatim} self.connect_inst([BL[%d]%col, BR[%d]%col, WL[%d]%row, gnd, vdd]). \end{verbatim} The pins must be listed in the same order as they were added to the submodule. Also, an assertion error will occur if there is a mismatch in the number of net connections. \item Do whatever else needs to be done. Add rectangles for power/ground rails or routing, add labels, etc... \item Every module needs to have ``self'' height and width variable that can be accessed from outside of the module class. These paramaters are commonly used for placing instances modules in a layout. For library cells, the \verb|self.width| and \verb|self.height| variables are automatically parsed from the GDSII layout using the \verb|cell_size()| function in \verb|vlsi_layout|. Users must define the width and height of dynamically generated designs. \item Add a call to the \verb|DRC_LVS()| function. \end{itemize} \subsection{GDSII Files and GdsMill)} \label{sec:gds} GDSII is the standard file used in indusrty to store the layout information of an integrated circuit. The GDSII file is a stream file that consists of records and data types that hold the data for the various instances, shapes, labels, etc.. in the layout. In OpenRAM, we utlize a nifty tool, called gdsMill, to read, write, and manipulate GDSII files. GdsMill was developed by Michael Wieckowski at the University of Michigan. \subsubsection{GDSII File Format} \label{sec:format} The format of gds file contains several parts, as it could be shown in Figure~\ref{fig:gds_file}. \begin{figure}[htb] \centering \includegraphics[width=10cm]{./figs/gds_file} \caption{example of a GDSII file} \label{fig:gds_file} \end{figure} The first part is the gds file header, which the contains GDSII version number, date modified, date last accessed, library, user units, and database units. The second part is the list of structures. These structures contain geometries or references to other structures of the layout in heirarchical form. Within a structure there are several kinds of records: \begin{itemize} \item Rectangle - basic geometry unit in a design, represent one layer of material in a circuit(i.e. a metal pin). Five coordinates and layer number are stored in rectangle record. \item Structure Reference - a structure that is used in this structure. The information about this reference will be used store as a structure in the same gds file. \item Text - a text record used for labels. \item Path - used to represent a wire. \item Boundary - defines a filled polygon. \item Array Reference - specifies an array of structure instances \item Node - Electrical nets may be specified with the NODE record \end{itemize} The last part is the tail of the GDSII file which ends the GDS Library. \fixme{Provide a link to the complete GDSII specification.} \subsubsection{GdsMill} \label{sec:gdsmill} As previously stated, GdsMill is a set of scripts that can be used to read, write, and manipulate GDSII files. \paragraph{The gds2\_reader and gds2\_writer:} In GdsMill, the \verb|gds2_reader| and \verb|gds2_writer| classes contain the various functions used to convert data between GDSII files and the \verb|vlsilayout| class. These classes process the data by iterating through every record in the GDS structures and check or write every data record. The record type (see Section~\ref{sec:format}),is tracked and identified using flags. \fixme{Do we need more information of these classes, or should we just point to the GdsMill documentation?} \paragraph{The VlsiLayout Class:} \label{sec:vlsilayout} After the \verb|gds2_reader| class reads in the records, the data has to be stored in a way that can be easily used by our code. Thus, the \verb|VlsiLayout| class is made to represent the layout. \verb|VlsiLayout| contains the same information as GDSII file but in a different way. \verb|VlsiLayout| stores records in data structures, which are defined in \verb|gdsPrimitives.py|. Each record type has a corresponding class defined in \verb|gdsPrimitives|. Thus, a vlsilayout should at least contains following member data: \begin{itemize} \item \verb|self.rootStructureName| - name of the top design. \item \verb|self.structures| -list of structure that are used in the class. \item \verb|self.xyTree| - contains a list of all structure names that appeared in the design. \end{itemize} The \verb|VlsiLayout| class also contains many functions for adding structures and records to a layout class, but the important and most useful functions have been aggregated into a wrapper file. This wrapper is called \verb|geometry.py| and is located in the \verb|compiler| directory. \subsubsection{OpenRAM-GdsMill Interface} \label{sec:wrapper} Dynamically generated cells and arrays each need to build a \verb|VlsiLayout| data structure to represent the hierarchical layout. This is performed using various functions from the \verb|VlsiLayout| class in GdsMill, but the GdsMill file is very large and can be difficult to understand. To make things easier, OpenRAM has its own wrapper class called \verb|geometry| in \verb|geometry.py|. This wrapper class initializes data structures for the instances and objects that will be added to the \verb|VlsiLayout| class. The functions \verb|add_inst()|, \verb|add_rect()|, \verb|add_label()| in \verb|hierarchy_layout|, add the structures to the \verb|geometry| class, which is then written out to a GDSII file using \verb|VlsiLayout| and the \verb|gds2_writer|. User included library cells, which should be in gds files, can be used as dynamically generated cells by using GDSMill. Cell information such as cell size and pin location can be obtained by using built in functions in the \verb|VlsiLayout| class. Cell size can be finded by using the \verb|readLayoutBorder| function of the \verb|VlsiLayout| class. A boundary layer should be drawn in each library cell to indicate the cell area. The \verb|readLayoutBorder| function will return the width and height of the boundary. If a boundary layer do not exist in the layout, then \verb|measureSize| can find the physical size cell. The first method is used as primary method in \verb|auto_Measure_libcell| the lib\_utility.py, while the second method is used as a back up one. Each technolgy setup will import this utility function and read the library cell. Pin location can be find by using the \verb|readPin| function of the \verb|VlsiLayout| class. The \verb|readPin| function will return the biggest boundary which covers the label and is at the same layer as the label is. \subsection{Technology Directory} \label{sec:techdir} The aim of creating technology directory is to make OpenRAM portable to different technologies. This directory contains all the information related to the specific process/technology that is being used. In OpenRAM, the default technology is FreePDK45, which has it own technolony directory in the trunk. The technology-specific directory should consist of the following: \begin{itemize} \item Technology Setup FIle - In \verb|/techdir/setup_scripts|, there should be a Python file that sets up the PDK and defines anything necessary for a given technology. This file should be named \verb|setup_openram_.py| where techname is the name used to identify it in configuration scripts. \item Technology-Specific Parameters - These parameters should include layer numbers and any design rules that may be needed for generating dynamic designs (DRC rules). The parameters should be added in \verb|techname/tech/tech.py| and optinally in a \verb|techname/layer.map| for DRC/LVS streaming. \item Library Cells - The library cells and corresponding spice netlists should be added to the \verb|techname/gds_lib| and \verb|techname/sp_lib| directories. \item Spice Models - If models are not supplied in the PDK, they can be placed in the technology directory as done in SCMOS. \end{itemize} The height and width of library cells is determined by the bounding box of all geometries. Sometimes this is not desired, for example, when a rail must be shared. In this case, the boundary layer in the technology file is used to define the height and width of the cell. Pins are recognized in library cells by the largest rectangle that encloses the pin label text. Multiple pins with the same name are supported. Pins with the same name such as gnd are assumed to be ``must connect'' which requires that they later be connected. For more information regarding the technology directory and how to set one up for a new technology, refer to Section~\ref{sec:porting} \subsection{DRC/LVS Interface} \label{sec:drclvs} Each design class contains a function \verb|DRC_LVS()| that performs both DRC and LVS on the current design module. This enables bottom-up correct-by-construction design and easy identification of where errors occur. It does incur some run-time overhead and can be disabled on the command line. The \verb|DRC_LVS()| function saves a GDSII file and a Spice file into a temporary directory and then calls two functions to perform DRC and LVS that are tool-dependent. Wrapper implementation for DRC and LVS functions are provided for the open-source tools Magic+Netgen and the commercial tool, Cadence Calibre. Each of these functions generates a batch-mode script or runset file which contains the options to correctly run DRC and LVS. The functions then parse the batch mode output for any potential errors and returns the number of errors encountered. The function \verb|run_drc()| requires a cell name and a GDSII file. The cell name corresponds to the top level cell in the GDSII file. For Calibre, it also uses the layer map file for the technology to correctly import the GDSII file into the Cadence database to perform DRC. The function returns the number of DRC violations. The function \verb|run_lvs()| requires a cell name, a GDSII file, and a Spice file. Magic or Calibre will extract an extracted Spice netlist from the GDSII file and will then compare this netlist with the OpenRAM Spice netlist. The function returns the number of errors encountered if there is an LVS mismatch. For both DRC and LVS, the summary file and other report files are left in the OpenRAM temporary directory after DRC/LVS is run. These report files can be examined to further understand why errors were encountered. In addition, by increasing the debug level with one or more ``-v'' command-line parametres, the command to re-create the DRC/LVS check can be obtained and run manually.