Computer architecture is one of my passions. I find the process of imagining, designing and implementing new computer systems quite fascinating and engaging. So a few years ago, I joined a research project looking into memory garbage collection in the hardware. The idea is to explore how can we architect a processor that has a garbage collector implemented in the hardware.

We wanted to come up with a microarchitecture for our system. A key component in the design was the on-chip memory. We needed to make sure that the microarchitecture made the best possible use of the memory because both the processor and collector need to access it. So we really had to understand the capabilities of fabricable memories to make the correct design decisions.

Unfortunately, getting information about fabricable memories is hard! The chip foundries would have this information, but they tend to be secretive about the details of their technology. Also, we did not have access to professional tools, like Artisan, Legato and DesignWare, to explore different memory features and configurations. So we ended up using OpenRAM which is an open-source memory compiler.

OpenRAM is an SRAM memory compiler written in Python. It is surprisingly easy to use. You give it a configuration file along with a Process Design Kit (PDK) and it generates a memory. To make things easier, it comes packaged with two example PDKs: FreePDK45 and SCMOS.

OpenRAM outputs a lot of information about the generated memory: layouts, a Verilog model and characterization results including timing, power consumption and area. For example, I generated a few memories at 45nm with different word sizes and port configuration to get the following plot.

OpenRAM results

I would definitely recommend using OpenRAM for architectural exploration, or if you are just curious about memories!

Getting started

First things first: installation. OpenRAM is written in Python, so this only involves cloning their repository.

git clone git@github.com:VLSIDA/OpenRAM.git

You also need to install its dependencies which vary based on the PDK you would like to use. There is a comprehensive list here, but be careful because the version numbers in the list are not always up to date.

We then need to set up the following environment variables. You might also need to download the full PDK files as explained here.

export OPENRAM_HOME=<OPENRAM_DIR>/compiler
export OPENRAM_TECH=<OPENRAM_DIR>/technology
export OPENRAM_TMP=<OPENRAM_DIR>/tmp

# Only if using FreePDK45
export FREEPDK45=<FreePDK45_DIR>

NOTE: Be careful when starting multiple instances of OpenRAM in parallel as they would all use the same directory for intermediate files i.e. the one at $OPENRAM_TMP. This would typically cause a failure.

We also need to configure the memory to generate. We can do this via a configuration file written in Python (see the next section). Finally, we can run the program.

python3 $OPENRAM_HOME/openram.py config.py

Configuration

OpenRAM is mainly configured through a Python file that is then given to the tool as a command line argument. It is also possible to overwrite some of the configuration options via other command line arguments.

The possible configuration options and its default values are at compiler/options.py. So take a look there first if you are wondering whether the tool can do X, Y or Z. A basic configuration option looks like this.

# Number of bits per word
word_size = 2
# Number of 8-bit words (some people call this 'depth')
num_words = 16

# The fabrication technology. This must match the PDK name in $OPENRAM_TECH.
tech_name = "freepdk45"

# Process corners, temperature and voltage to characterize
process_corners = ["TT"]
supply_voltages = [1.0]
temperatures = [25]

# Path and name for the output files
output_path = "."
output_name = "sram_{0}_{1}_{2}".format(word_size, num_words, tech_name)

NOTE: Be careful when typing the variable names in the configuration file because a typo will not be caught. The tool would instead see the the default value for the option you were trying to overwrite.

FAQs

OpenRAM is relatively easy to use, but its documentation is limited. So I put together a short list of FAQs that I would have loved to see when I started using the tool.

How do I change the port configuration?

OpenRAM currently supports SRAMs with up to two ports. Ports can be configured using the following options.

# Read-Write ports
num_rw_ports = 1
# Read-only ports
num_r_ports = 0
# Write-only ports
num_w_ports = 0

OpenRAM will generate addition port circuitry for every port you add. But as of today, the bit-cells used for FreePDK45 memories with one read-only and one write-only port are the same as the bit-cells used for those with one read-write and one read-only port, even though there are better alternatives. So double-check that OpenRAM is using the bit-cell you expect when changing the port configuration.

Can I change the number of banks?

There is a num_banks option in compiler/options.py, but apparently it does not currently work. Multiple banks are useful when generating smaller memories, but the OpenRAM development team have focused their efforts elsewhere. You can simulate the effect of multiple banks by putting together smaller memories at the expense of larger area.

How do I configure the dependency tools used?

OpenRAM has dependencies on third-party tools for things like simulation and DRC checks. The tools to use can be set with the following configuration options (see here).

spice_name = ""
spice_exe = ""

drc_name = ""
drc_exe = None

lvs_name = ""
lvs_exe = None

pex_name = ""
pex_exe = None

magic_exe = None

How can I enable debugging?

Debugging is switched off by default. This can be changed by setting the debug_level variable to a value greater than 0 in the configuration file. OpenRAM will print a lot of information as it runs and it will start a Python debug prompt whenever there is a failure.

Analytical vs simulation characterisation?

OpenRAM characterizes the generated SRAMs to estimate their area, power consumption, etc. There are two methods to work out these values. The analytical is the quickest as it only uses a mathematical model, but it is rather inaccurate. For example, it often says that my generated memory with a 45nm technology node operates at over 13GHz. The alternative is to characterize the memory using simulation. Basically, it uses a tool like ngspice to simulate the SRAM circuitry which gives more accurate values. But it is also significantly slower, so OpenRAM can take days or even weeks to generate the memory.

Analytical delay is the default characterization method. You can force OpenRAM to use simulation instead by setting analytical_delay = True in the configuration file or by passing the -c flag as a command line argument.

OpenRAM is taking forever. How do I make it faster?

The quickest way to generate a memory is to use the analytical model.

If you must use the simulation characterization there there are a few alternatives to speed up OpenRAM.

  1. Disable power routing by setting route_supplies = False.
  2. If using ngspice, make sure the tool is compiled with OpenMP. Then add the num_threads=<SOME_INT> to the ngspice configuration file to run the simulation in parallel. For example echo "set num_threads=6" > ~/.spiceinit.
  3. Generate smaller memories with less ports.

Where is the documentation?

OpenRAM does not currently have a formal documentation site. Also, documentation in the code is sparse, but there are a few places with useful information.

How can I retain intermediate/temporary files?

Pass the -d flag when invoking OpenRAM. The files will be at the location indicated by $OPENRAM_TMP.

I am getting weird characterization values. Why?

Sometimes OpenRAM generates smaller memories are slower than larger ones. This is most likely because OpenRAM is trying to fit the generated memory into a square shape, so it sacrifices performance when it tries to generate a suitable column multiplexer. A workaround is to tell OpenRAM how many words to place in a row instead of attempting to make the memory squared. We can do so using the words_per_row option in the configuration file.

How do I fix a Sense amp enable timing error?

Increase the delays_chain_stages or delay_chain_fanout_per_stage options in the configuration file.