mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into supply_routing
This commit is contained in:
commit
7591f25a2e
12
README.md
12
README.md
|
|
@ -7,8 +7,8 @@ https://github.com/mguthaus/OpenRAM/blob/master/OpenRAM_ICCAD_2016_presentation.
|
|||
The OpenRAM compiler has very few dependencies:
|
||||
* ngspice-26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later)
|
||||
* Python 3.5 and higher
|
||||
* Python numpy
|
||||
* flask_table
|
||||
* Python numpy (pip3 install numpy)
|
||||
* flask_table (pip3 install flask)
|
||||
* a setup script for each technology
|
||||
* a technology directory for each technology with the base cells
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ We do not distribute the PDK, but you may get it from:
|
|||
If you are using SCMOS, you should install Magic and netgen from:
|
||||
http://opencircuitdesign.com/magic/
|
||||
http://opencircuitdesign.com/netgen/
|
||||
We have included the SCN3ME design rules from QFlow:
|
||||
We have included the SCN4M design rules from QFlow:
|
||||
http://opencircuitdesign.com/qflow/
|
||||
|
||||
# DIRECTORY STRUCTURE
|
||||
|
|
@ -65,7 +65,7 @@ We have included the SCN3ME design rules from QFlow:
|
|||
* compiler/tests - unit tests
|
||||
* technology - openram technology directory (pointed to by OPENRAM_TECH)
|
||||
* technology/freepdk45 - example configuration library for freepdk45 technology node
|
||||
* technology/scn3me_subm - example configuration library SCMOS technology node
|
||||
* technology/scn4m_subm - example configuration library SCMOS technology node
|
||||
* technology/setup_scripts - setup scripts to customize your PDKs and OpenRAM technologies
|
||||
* docs - LaTeX manual (likely outdated)
|
||||
* lib - IP library of pregenerated memories
|
||||
|
|
@ -90,8 +90,8 @@ To increase the verbosity of the test, add one (or more) -v options:
|
|||
python tests/00_code_format_check_test.py -v -t freepdk45
|
||||
```
|
||||
To specify a particular technology use "-t <techname>" such as
|
||||
"-t scn3me_subm". The default for a unit test is freepdk45 whereas
|
||||
the default for openram.py is specified in the configuration file.
|
||||
"-t freepdk45" or "-t scn4m_subm". The default for a unit test is scn4m_subm.
|
||||
The default for openram.py is specified in the configuration file.
|
||||
|
||||
|
||||
# CREATING CUSTOM TECHNOLOGIES
|
||||
|
|
|
|||
|
|
@ -222,9 +222,9 @@ class pbitcell(design.design):
|
|||
self.rowline_spacing = self.m1_space + contact.m1m2.width
|
||||
|
||||
# spacing for vdd
|
||||
vdd_offset_well_constraint = self.well_enclose_active + 0.5*contact.well.width
|
||||
vdd_offset_metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space + 0.5*contact.well.width
|
||||
self.vdd_offset = max(vdd_offset_well_constraint, vdd_offset_metal1_constraint)
|
||||
implant_constraint = max(inverter_pmos_contact_extension, 0) + 2*self.implant_enclose_active + 0.5*(contact.well.width - self.m1_width)
|
||||
metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space
|
||||
self.vdd_offset = max(implant_constraint, metal1_constraint) + 0.5*self.m1_width
|
||||
|
||||
# read port dimensions
|
||||
width_reduction = self.read_nmos.active_width - self.read_nmos.get_pin("D").cx()
|
||||
|
|
@ -334,7 +334,7 @@ class pbitcell(design.design):
|
|||
layer="metal1",
|
||||
offset=self.gnd_position,
|
||||
width=self.width,
|
||||
height=contact.well.second_layer_width)
|
||||
height=self.m1_width)
|
||||
|
||||
vdd_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset
|
||||
self.vdd_position = vector(0, vdd_ypos)
|
||||
|
|
@ -342,7 +342,7 @@ class pbitcell(design.design):
|
|||
layer="metal1",
|
||||
offset=self.vdd_position,
|
||||
width=self.width,
|
||||
height=contact.well.second_layer_width)
|
||||
height=self.m1_width)
|
||||
|
||||
def create_readwrite_ports(self):
|
||||
"""
|
||||
|
|
@ -933,8 +933,9 @@ class pbitcell(design.design):
|
|||
|
||||
def route_rbc_short(self):
|
||||
""" route the short from Q_bar to gnd necessary for the replica bitcell """
|
||||
Q_bar_pos = self.inverter_pmos_right.get_pin("S").uc()
|
||||
vdd_pos = vector(Q_bar_pos.x, self.vdd_position.y)
|
||||
Q_bar_pos = self.inverter_pmos_right.get_pin("S").center()
|
||||
vdd_pos = self.inverter_pmos_right.get_pin("D").center()
|
||||
#vdd_pos = vector(Q_bar_pos.x, self.vdd_position.y)
|
||||
|
||||
self.add_path("metal1", [Q_bar_pos, vdd_pos])
|
||||
|
||||
|
|
@ -526,15 +526,15 @@ class lib:
|
|||
|
||||
for (corner, lib_name) in zip(self.corners, self.lib_files):
|
||||
|
||||
ports = ""
|
||||
if OPTS.num_rw_ports>0:
|
||||
ports += "{}_".format(OPTS.num_rw_ports)
|
||||
if OPTS.num_w_ports>0:
|
||||
ports += "{}_".format(OPTS.num_w_ports)
|
||||
if OPTS.num_r_ports>0:
|
||||
ports += "{}_".format(OPTS.num_r_ports)
|
||||
# ports = ""
|
||||
# if OPTS.num_rw_ports>0:
|
||||
# ports += "{}_".format(OPTS.num_rw_ports)
|
||||
# if OPTS.num_w_ports>0:
|
||||
# ports += "{}_".format(OPTS.num_w_ports)
|
||||
# if OPTS.num_r_ports>0:
|
||||
# ports += "{}_".format(OPTS.num_r_ports)
|
||||
|
||||
datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12}".format("sram_{0}_{1}_{2}{3}".format(OPTS.word_size, OPTS.num_words, ports, OPTS.tech_name),
|
||||
datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13}".format("sram_{0}_{1}_{2}".format(OPTS.word_size, OPTS.num_words, OPTS.tech_name),
|
||||
OPTS.num_words,
|
||||
OPTS.num_banks,
|
||||
OPTS.num_rw_ports,
|
||||
|
|
@ -546,7 +546,8 @@ class lib:
|
|||
self.corner[0],
|
||||
round_time(self.char_sram_results["min_period"]),
|
||||
self.out_dir,
|
||||
lib_name))
|
||||
lib_name,
|
||||
OPTS.word_size))
|
||||
|
||||
|
||||
datasheet.close()
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
|
|
@ -3,16 +3,21 @@ from operating_conditions import *
|
|||
from characterization_corners import *
|
||||
from deliverables import *
|
||||
from timing_and_current_data import *
|
||||
from in_out import *
|
||||
import os
|
||||
from globals import OPTS
|
||||
|
||||
class datasheet():
|
||||
|
||||
def __init__(self,identifier):
|
||||
self.io = []
|
||||
self.corners = []
|
||||
self.timing = []
|
||||
self.operating = []
|
||||
self.dlv = []
|
||||
self.name = identifier
|
||||
self.html = ""
|
||||
|
||||
|
||||
def generate_html(self):
|
||||
self.html = """<style>
|
||||
|
|
@ -40,16 +45,25 @@ class datasheet():
|
|||
color: white;
|
||||
}
|
||||
</style>"""
|
||||
self.html +='<p style=font-size: 20px;font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;>{0}</p>'
|
||||
self.html +='<p style=font-size: 20px;font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;>{0}</p>'
|
||||
self.html +='<p style=font-size: 20px;font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;>{0}</p>'
|
||||
self.html +='<p style=font-size: 20px;font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;>'+ self.name + '.html' + '</p>'
|
||||
# self.html +='<p style=font-size: 20px;font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;>{0}</p>'
|
||||
# self.html +='<p style=font-size: 20px;font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;>{0}</p>'
|
||||
|
||||
self.html +='<p style=font-size: 20px;font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;>Ports and Configuration (DEBUG)</p>'
|
||||
self.html += in_out(self.io,table_id='data').__html__().replace('<','<').replace('"','"').replace('>',">")
|
||||
|
||||
self.html +='<p style=font-size: 20px;font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;>Operating Conditions</p>'
|
||||
self.html += operating_conditions(self.operating,table_id='data').__html__()
|
||||
|
||||
self.html += '<p style=font-size: 20px;font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;>Timing and Current Data</p>'
|
||||
self.html += timing_and_current_data(self.timing,table_id='data').__html__()
|
||||
|
||||
self.html += '<p style=font-size: 20px;font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;>Characterization Corners</p>'
|
||||
self.html += characterization_corners(self.corners,table_id='data').__html__()
|
||||
|
||||
self.html +='<p style=font-size: 20px;font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;>Deliverables</p>'
|
||||
self.html += deliverables(self.dlv,table_id='data').__html__().replace('<','<').replace('"','"').replace('>',">")
|
||||
|
||||
|
||||
self.html +='<p style=font-size: 20px;font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;>*Feature only supported with characterizer</p>'
|
||||
|
||||
self.html +='<img src=' + os.path.abspath(os.environ.get("OPENRAM_HOME")) + '/datasheet/assets/vlsi_logo.png alt="VLSIDA" />'
|
||||
|
|
|
|||
|
|
@ -8,17 +8,24 @@ Locate all timing elements in .lib
|
|||
Diagram generation
|
||||
Improve css
|
||||
"""
|
||||
|
||||
import os, math
|
||||
import optparse
|
||||
from flask_table import *
|
||||
import csv
|
||||
import debug
|
||||
from globals import OPTS
|
||||
from deliverables import *
|
||||
from operating_conditions import *
|
||||
from timing_and_current_data import *
|
||||
from characterization_corners import *
|
||||
from datasheet import *
|
||||
|
||||
if OPTS.datasheet_gen:
|
||||
import flask_table
|
||||
import os, math
|
||||
import optparse
|
||||
import csv
|
||||
from deliverables import *
|
||||
from operating_conditions import *
|
||||
from timing_and_current_data import *
|
||||
from characterization_corners import *
|
||||
from datasheet import *
|
||||
from in_out import *
|
||||
else:
|
||||
debug.warning("Python library flask_table not found. Skipping html datasheet generation. This can be installed with pip install flask-table.")
|
||||
|
||||
|
||||
|
||||
def process_name(corner):
|
||||
if corner == "TT":
|
||||
|
|
@ -43,12 +50,13 @@ def parse_file(f,pages):
|
|||
NUM_W_PORTS = row[4]
|
||||
NUM_R_PORTS = row[5]
|
||||
TECH_NAME = row[6]
|
||||
TEMP = row[7]
|
||||
VOLT = row[8]
|
||||
TEMP = row[8]
|
||||
VOLT = row[7]
|
||||
PROC = row[9]
|
||||
MIN_PERIOD = row[10]
|
||||
OUT_DIR = row[11]
|
||||
LIB_NAME = row[12]
|
||||
WORD_SIZE = row[13]
|
||||
for sheet in pages:
|
||||
|
||||
|
||||
|
|
@ -95,39 +103,55 @@ def parse_file(f,pages):
|
|||
new_sheet.operating.append(operating_conditions_item('Power supply (VDD) range',VOLT,VOLT,VOLT,'Volts'))
|
||||
new_sheet.operating.append(operating_conditions_item('Operating Temperature',TEMP,TEMP,TEMP,'Celsius'))
|
||||
try:
|
||||
new_sheet.operating.append(operating_conditions_item('Operating Frequency (F)','','',str(math.floor(1000/float(MIN_PERIOD))),'MHz'))
|
||||
new_sheet.operating.append(operating_conditions_item('Operating Frequency (F)*','','',str(math.floor(1000/float(MIN_PERIOD))),'MHz'))
|
||||
except Exception:
|
||||
new_sheet.operating.append(operating_conditions_item('Operating Frequency (F)','','',"unknown",'MHz')) #analytical model fails to provide MIN_PERIOD
|
||||
new_sheet.operating.append(operating_conditions_item('Operating Frequency (F)*','','',"unknown",'MHz')) #analytical model fails to provide MIN_PERIOD
|
||||
|
||||
|
||||
new_sheet.timing.append(timing_and_current_data_item('Cycle time','2','3','4'))
|
||||
new_sheet.timing.append(timing_and_current_data_item('Access time','2','3','4'))
|
||||
new_sheet.timing.append(timing_and_current_data_item('Positive clk setup','2','3','4'))
|
||||
new_sheet.timing.append(timing_and_current_data_item('Positive clk hold','2','3','4'))
|
||||
new_sheet.timing.append(timing_and_current_data_item('RW setup','2','3','4'))
|
||||
new_sheet.timing.append(timing_and_current_data_item('RW hold','2','3','4'))
|
||||
new_sheet.timing.append(timing_and_current_data_item('AC current','2','3','4'))
|
||||
new_sheet.timing.append(timing_and_current_data_item('Standby current','2','3','4'))
|
||||
new_sheet.timing.append(timing_and_current_data_item('Area','2','3','4'))
|
||||
|
||||
|
||||
new_sheet.timing.append(timing_and_current_data_item('1','2','3','4'))
|
||||
|
||||
new_sheet.dlv.append(deliverables_item('.sp','SPICE netlists','<a href="file://{0}{1}.{2}">{1}.{2}</a>'.format(OUT_DIR,NAME,'sp')))
|
||||
new_sheet.dlv.append(deliverables_item('.v','Verilog simulation models','<a href="file://{0}{1}.{2}">{1}.{2}</a>'.format(OUT_DIR,NAME,'v')))
|
||||
new_sheet.dlv.append(deliverables_item('.gds','GDSII layout views','<a href="file://{0}{1}.{2}">{1}.{2}</a>'.format(OUT_DIR,NAME,'gds')))
|
||||
new_sheet.dlv.append(deliverables_item('.lef','LEF files','<a href="file://{0}{1}.{2}">{1}.{2}</a>'.format(OUT_DIR,NAME,'lef')))
|
||||
new_sheet.dlv.append(deliverables_item('.lib','Synthesis models','<a href="file://{0}">{1}</a>'.format(LIB_NAME,LIB_NAME.replace(OUT_DIR,''))))
|
||||
|
||||
new_sheet.io.append(in_out_item('WORD_SIZE',WORD_SIZE))
|
||||
new_sheet.io.append(in_out_item('NUM_WORDS',NUM_WORDS))
|
||||
new_sheet.io.append(in_out_item('NUM_BANKS',NUM_BANKS))
|
||||
new_sheet.io.append(in_out_item('NUM_RW_PORTS',NUM_RW_PORTS))
|
||||
new_sheet.io.append(in_out_item('NUM_R_PORTS',NUM_R_PORTS))
|
||||
new_sheet.io.append(in_out_item('NUM_W_PORTS',NUM_W_PORTS))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class datasheet_gen():
|
||||
def datasheet_write(name):
|
||||
|
||||
in_dir = OPTS.openram_temp
|
||||
|
||||
if not (os.path.isdir(in_dir)):
|
||||
os.mkdir(in_dir)
|
||||
if OPTS.datasheet_gen:
|
||||
in_dir = OPTS.openram_temp
|
||||
|
||||
if not (os.path.isdir(in_dir)):
|
||||
os.mkdir(in_dir)
|
||||
|
||||
#if not (os.path.isdir(out_dir)):
|
||||
# os.mkdir(out_dir)
|
||||
#if not (os.path.isdir(out_dir)):
|
||||
# os.mkdir(out_dir)
|
||||
|
||||
datasheets = []
|
||||
parse_file(in_dir + "/datasheet.info", datasheets)
|
||||
datasheets = []
|
||||
parse_file(in_dir + "/datasheet.info", datasheets)
|
||||
|
||||
|
||||
for sheets in datasheets:
|
||||
with open(name, 'w+') as f:
|
||||
sheets.generate_html()
|
||||
f.write(sheets.html)
|
||||
for sheets in datasheets:
|
||||
with open(name, 'w+') as f:
|
||||
sheets.generate_html()
|
||||
f.write(sheets.html)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
from flask_table import *
|
||||
|
||||
class in_out(Table):
|
||||
typ = Col('Type')
|
||||
description = Col('Description')
|
||||
|
||||
|
||||
class in_out_item(object):
|
||||
def __init__(self, typ, description):
|
||||
self.typ = typ
|
||||
self.description = description
|
||||
|
|
@ -100,9 +100,17 @@ def check_versions():
|
|||
minor_required = 5
|
||||
if not (major_python_version == major_required and minor_python_version >= minor_required):
|
||||
debug.error("Python {0}.{1} or greater is required.".format(major_required,minor_required),-1)
|
||||
|
||||
|
||||
# FIXME: Check versions of other tools here??
|
||||
# or, this could be done in each module (e.g. verify, characterizer, etc.)
|
||||
global OPTS
|
||||
|
||||
try:
|
||||
import flask_table
|
||||
OPTS.datasheet_gen = 1
|
||||
except:
|
||||
OPTS.datasheet_gen = 0
|
||||
|
||||
|
||||
def init_openram(config_file, is_unit_test=True):
|
||||
"""Initialize the technology, paths, simulators, etc."""
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ class replica_bitline(design.design):
|
|||
self.access_tx_offset = vector(-gap_width-self.access_tx.width-self.inv.width, 0.5*self.inv.height)
|
||||
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
""" Add the modules for later usage """
|
||||
|
||||
|
|
@ -184,19 +183,14 @@ class replica_bitline(design.design):
|
|||
pin = self.rbl_inst.get_pin(wl)
|
||||
|
||||
# Route the connection to the right so that it doesn't interfere with the cells
|
||||
# Wordlines may be close to each other when tiled, so gnd connections are routed in opposite directions
|
||||
if row % 2 == 0:
|
||||
vertical_extension = vector(0, 1.5*drc("minwidth_metal1") + 0.5*contact.m1m2.height)
|
||||
else:
|
||||
vertical_extension = vector(0, -1.5*drc("minwidth_metal1") - 1.5*contact.m1m2.height)
|
||||
|
||||
# Wordlines may be close to each other when tiled, so gnd connections are routed in opposite directions
|
||||
pin_right = pin.rc()
|
||||
pin_extension1 = pin_right + vector(self.m3_pitch,0)
|
||||
pin_extension2 = pin_extension1 + vertical_extension
|
||||
pin_extension = pin_right + vector(self.m3_pitch,0)
|
||||
|
||||
if pin.layer != "metal1":
|
||||
continue
|
||||
self.add_path("metal1", [pin_right, pin_extension1, pin_extension2])
|
||||
self.add_power_pin("gnd", pin_extension2)
|
||||
self.add_path("metal1", [pin_right, pin_extension])
|
||||
self.add_power_pin("gnd", pin_extension)
|
||||
|
||||
# for multiport, need to short wordlines to each other so they all connect to gnd
|
||||
wl_last = self.wl_list[self.total_ports-1]+"_{}".format(row)
|
||||
|
|
@ -280,7 +274,7 @@ class replica_bitline(design.design):
|
|||
# DRAIN ROUTE
|
||||
# Route the drain to the vdd rail
|
||||
drain_offset = self.tx_inst.get_pin("D").center()
|
||||
self.add_power_pin("vdd", drain_offset)
|
||||
self.add_power_pin("vdd", drain_offset, rotate=0)
|
||||
|
||||
# SOURCE ROUTE
|
||||
# Route the drain to the RBL inverter input
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ import verify
|
|||
from sram import sram
|
||||
from sram_config import sram_config
|
||||
#from parser import *
|
||||
output_extensions = ["sp","v","lib","html"]
|
||||
output_extensions = ["sp","v","lib"]
|
||||
if OPTS.datasheet_gen:
|
||||
output_extensions.append("html")
|
||||
if not OPTS.netlist_only:
|
||||
output_extensions.extend(["gds","lef"])
|
||||
output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in output_extensions]
|
||||
|
|
|
|||
|
|
@ -99,6 +99,26 @@ def write_netgen_script(cell_name, sp_name):
|
|||
f.close()
|
||||
os.system("chmod u+x {}".format(run_file))
|
||||
|
||||
setup_file = OPTS.openram_temp + "setup.tcl"
|
||||
f = open(setup_file, "w")
|
||||
f.write("ignore class c\n")
|
||||
f.write("equate class {{nfet {0}.spice}} {{n {1}}}\n".format(cell_name, sp_name))
|
||||
f.write("equate class {{pfet {0}.spice}} {{p {1}}}\n".format(cell_name, sp_name))
|
||||
# This circuit has symmetries and needs to be flattened to resolve them or the banks won't pass
|
||||
# Is there a more elegant way to add this when needed?
|
||||
f.write("flatten class {{{0}.spice bitcell_array}}\n".format(cell_name))
|
||||
f.write("flatten class {{{0}.spice precharge_array_1}}\n".format(cell_name))
|
||||
f.write("flatten class {{{0}.spice precharge_array_2}}\n".format(cell_name))
|
||||
f.write("flatten class {{{0}.spice precharge_array_3}}\n".format(cell_name))
|
||||
f.write("flatten class {{{0}.spice precharge_array_4}}\n".format(cell_name))
|
||||
f.write("property {{nfet {0}.spice}} remove as ad ps pd\n".format(cell_name))
|
||||
f.write("property {{pfet {0}.spice}} remove as ad ps pd\n".format(cell_name))
|
||||
f.write("property {{n {0}}} remove as ad ps pd\n".format(sp_name))
|
||||
f.write("property {{p {0}}} remove as ad ps pd\n".format(sp_name))
|
||||
f.write("permute transistors\n")
|
||||
f.write("permute pins n source drain\n")
|
||||
f.write("permute pins p source drain\n")
|
||||
f.close()
|
||||
|
||||
def run_drc(cell_name, gds_name, extract=False, final_verification=False):
|
||||
"""Run DRC check on a cell which is implemented in gds_name."""
|
||||
|
|
|
|||
Loading…
Reference in New Issue