mirror of https://github.com/VLSIDA/OpenRAM.git
Merge remote-tracking branch 'origin/datasheet_gen' into supply_routing
This commit is contained in:
commit
1c426aad29
|
|
@ -8,6 +8,7 @@ The OpenRAM compiler has very few dependencies:
|
||||||
* ngspice-26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later)
|
* ngspice-26 (or later) or HSpice I-2013.12-1 (or later) or CustomSim 2017 (or later)
|
||||||
* Python 3.5 and higher
|
* Python 3.5 and higher
|
||||||
* Python numpy
|
* Python numpy
|
||||||
|
* flask_table
|
||||||
* a setup script for each technology
|
* a setup script for each technology
|
||||||
* a technology directory for each technology with the base cells
|
* a technology directory for each technology with the base cells
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ class lib:
|
||||||
|
|
||||||
self.characterize_corners()
|
self.characterize_corners()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def gen_port_names(self):
|
def gen_port_names(self):
|
||||||
"""Generates the port names to be written to the lib file"""
|
"""Generates the port names to be written to the lib file"""
|
||||||
#This is basically a copy and paste of whats in delay.py as well. Something more efficient should be done here.
|
#This is basically a copy and paste of whats in delay.py as well. Something more efficient should be done here.
|
||||||
|
|
@ -96,7 +98,7 @@ class lib:
|
||||||
debug.info(1,"Writing to {0}".format(lib_name))
|
debug.info(1,"Writing to {0}".format(lib_name))
|
||||||
self.characterize()
|
self.characterize()
|
||||||
self.lib.close()
|
self.lib.close()
|
||||||
|
self.parse_info()
|
||||||
def characterize(self):
|
def characterize(self):
|
||||||
""" Characterize the current corner. """
|
""" Characterize the current corner. """
|
||||||
|
|
||||||
|
|
@ -516,3 +518,36 @@ class lib:
|
||||||
else:
|
else:
|
||||||
self.times = self.sh.analyze(self.slews,self.slews)
|
self.times = self.sh.analyze(self.slews,self.slews)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_info(self):
|
||||||
|
if OPTS.is_unit_test:
|
||||||
|
return
|
||||||
|
datasheet = open(OPTS.openram_temp +'/datasheet.info', 'a+')
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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),
|
||||||
|
OPTS.num_words,
|
||||||
|
OPTS.num_banks,
|
||||||
|
OPTS.num_rw_ports,
|
||||||
|
OPTS.num_w_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.tech_name,
|
||||||
|
self.corner[1],
|
||||||
|
self.corner[2],
|
||||||
|
self.corner[0],
|
||||||
|
round_time(self.char_results["min_period"]),
|
||||||
|
self.out_dir,
|
||||||
|
lib_name))
|
||||||
|
|
||||||
|
|
||||||
|
datasheet.close()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
from flask_table import *
|
||||||
|
|
||||||
|
class characterization_corners(Table):
|
||||||
|
corner_name = Col('Corner Name')
|
||||||
|
process = Col('Process')
|
||||||
|
power_supply = Col('Power Supply')
|
||||||
|
temperature = Col('Temperature')
|
||||||
|
library_name_suffix = Col('Library Name Suffix')
|
||||||
|
|
||||||
|
class characterization_corners_item(object):
|
||||||
|
def __init__(self, corner_name, process, power_supply, temperature, library_name_suffix):
|
||||||
|
self.corner_name = corner_name
|
||||||
|
self.process = process
|
||||||
|
self.power_supply = power_supply
|
||||||
|
self.temperature = temperature
|
||||||
|
self.library_name_suffix = library_name_suffix
|
||||||
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
from flask_table import *
|
||||||
|
from operating_conditions import *
|
||||||
|
from characterization_corners import *
|
||||||
|
from deliverables import *
|
||||||
|
from timing_and_current_data import *
|
||||||
|
|
||||||
|
class datasheet():
|
||||||
|
|
||||||
|
def __init__(self,identifier):
|
||||||
|
self.corners = []
|
||||||
|
self.timing = []
|
||||||
|
self.operating = []
|
||||||
|
self.dlv = []
|
||||||
|
self.name = identifier
|
||||||
|
self.html = ""
|
||||||
|
|
||||||
|
def generate_html(self):
|
||||||
|
self.html = """<style>
|
||||||
|
#data {
|
||||||
|
font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px
|
||||||
|
}
|
||||||
|
|
||||||
|
#data td, #data th {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#data tr:nth-child(even){background-color: #f2f2f2;}
|
||||||
|
|
||||||
|
#data tr:hover {background-color: #ddd;}
|
||||||
|
|
||||||
|
#data th {
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #4CAF50;
|
||||||
|
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;>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('>',">")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,133 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Datasheet Generator
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
locate all port elements in .lib
|
||||||
|
Locate all timing elements in .lib
|
||||||
|
Diagram generation
|
||||||
|
Improve css
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, math
|
||||||
|
import optparse
|
||||||
|
from flask_table import *
|
||||||
|
import csv
|
||||||
|
from globals import OPTS
|
||||||
|
from deliverables import *
|
||||||
|
from operating_conditions import *
|
||||||
|
from timing_and_current_data import *
|
||||||
|
from characterization_corners import *
|
||||||
|
from datasheet import *
|
||||||
|
|
||||||
|
def process_name(corner):
|
||||||
|
if corner == "TT":
|
||||||
|
return "Typical - Typical"
|
||||||
|
if corner == "SS":
|
||||||
|
return "Slow - Slow"
|
||||||
|
if corner == "FF":
|
||||||
|
return "Fast - Fast"
|
||||||
|
else:
|
||||||
|
return "custom"
|
||||||
|
|
||||||
|
def parse_file(f,pages):
|
||||||
|
with open(f) as csv_file:
|
||||||
|
csv_reader = csv.reader(csv_file, delimiter=',')
|
||||||
|
line_count = 0
|
||||||
|
for row in csv_reader:
|
||||||
|
found = 0
|
||||||
|
NAME = row[0]
|
||||||
|
NUM_WORDS = row[1]
|
||||||
|
NUM_BANKS = row[2]
|
||||||
|
NUM_RW_PORTS = row[3]
|
||||||
|
NUM_W_PORTS = row[4]
|
||||||
|
NUM_R_PORTS = row[5]
|
||||||
|
TECH_NAME = row[6]
|
||||||
|
TEMP = row[7]
|
||||||
|
VOLT = row[8]
|
||||||
|
PROC = row[9]
|
||||||
|
MIN_PERIOD = row[10]
|
||||||
|
OUT_DIR = row[11]
|
||||||
|
LIB_NAME = row[12]
|
||||||
|
for sheet in pages:
|
||||||
|
|
||||||
|
|
||||||
|
if sheet.name == row[0]:
|
||||||
|
found = 1
|
||||||
|
#if the .lib information is for an existing datasheet compare timing data
|
||||||
|
|
||||||
|
for item in sheet.operating:
|
||||||
|
|
||||||
|
if item.parameter == 'Operating Temperature':
|
||||||
|
if float(TEMP) > float(item.max):
|
||||||
|
item.typ = item.max
|
||||||
|
item.max = TEMP
|
||||||
|
if float(TEMP) < float(item.min):
|
||||||
|
item.typ = item.min
|
||||||
|
item.min = TEMP
|
||||||
|
|
||||||
|
if item.parameter == 'Power supply (VDD) range':
|
||||||
|
if float(VOLT) > float(item.max):
|
||||||
|
item.typ = item.max
|
||||||
|
item.max = VOLT
|
||||||
|
if float(VOLT) < float(item.min):
|
||||||
|
item.typ = item.min
|
||||||
|
item.min = VOLT
|
||||||
|
|
||||||
|
if item.parameter == 'Operating Frequncy (F)':
|
||||||
|
try:
|
||||||
|
if float(math.floor(1000/float(MIN_PERIOD)) < float(item.max)):
|
||||||
|
item.max = str(math.floor(1000/float(MIN_PERIOD)))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
new_sheet.corners.append(characterization_corners_item(PROC,process_name(PROC),VOLT,TEMP,LIB_NAME.replace(OUT_DIR,'').replace(NAME,'')))
|
||||||
|
new_sheet.dlv.append(deliverables_item('.lib','Synthesis models','<a href="file://{0}">{1}</a>'.format(LIB_NAME,LIB_NAME.replace(OUT_DIR,''))))
|
||||||
|
|
||||||
|
if found == 0:
|
||||||
|
new_sheet = datasheet(NAME)
|
||||||
|
pages.append(new_sheet)
|
||||||
|
|
||||||
|
new_sheet.corners.append(characterization_corners_item(PROC,process_name(PROC),VOLT,TEMP,LIB_NAME.replace(OUT_DIR,'').replace(NAME,'')))
|
||||||
|
|
||||||
|
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'))
|
||||||
|
except Exception:
|
||||||
|
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('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,''))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class datasheet_gen():
|
||||||
|
def datasheet_write(name):
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
from flask_table import *
|
||||||
|
|
||||||
|
class deliverables(Table):
|
||||||
|
typ = Col('Type')
|
||||||
|
description = Col('Description')
|
||||||
|
link = Col('Link')
|
||||||
|
|
||||||
|
|
||||||
|
class deliverables_item(object):
|
||||||
|
def __init__(self, typ, description,link):
|
||||||
|
self.typ = typ
|
||||||
|
self.description = description
|
||||||
|
self.link = link
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
from flask_table import *
|
||||||
|
|
||||||
|
class operating_conditions(Table):
|
||||||
|
parameter = Col('Parameter')
|
||||||
|
min = Col('Min')
|
||||||
|
typ = Col('Typ')
|
||||||
|
max = Col('Max')
|
||||||
|
units = Col('Units')
|
||||||
|
|
||||||
|
class operating_conditions_item(object):
|
||||||
|
def __init__(self, parameter, min, typ, max, units):
|
||||||
|
self.parameter = parameter
|
||||||
|
self.min = min
|
||||||
|
self.typ = typ
|
||||||
|
self.max = max
|
||||||
|
self.units = units
|
||||||
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
from flask_table import *
|
||||||
|
|
||||||
|
class timing_and_current_data(Table):
|
||||||
|
parameter = Col('Parameter')
|
||||||
|
min = Col('Min')
|
||||||
|
max = Col('Max')
|
||||||
|
units = Col('Units')
|
||||||
|
|
||||||
|
class timing_and_current_data_item(object):
|
||||||
|
def __init__(self, parameter, min, max, units):
|
||||||
|
self.parameter = parameter
|
||||||
|
self.min = min
|
||||||
|
self.max = max
|
||||||
|
self.units = units
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -8,7 +8,7 @@ supply_voltages = [1.0]
|
||||||
temperatures = [25]
|
temperatures = [25]
|
||||||
|
|
||||||
output_path = "temp"
|
output_path = "temp"
|
||||||
output_name = "sram_{0}_{1}_{2}_{3}".format(word_size,num_words,num_banks,tech_name)
|
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
|
||||||
|
|
||||||
#Setting for multiport
|
#Setting for multiport
|
||||||
# netlist_only = True
|
# netlist_only = True
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,5 @@ supply_voltages = [ 5.0 ]
|
||||||
temperatures = [ 25 ]
|
temperatures = [ 25 ]
|
||||||
|
|
||||||
output_path = "temp"
|
output_path = "temp"
|
||||||
output_name = "sram_{0}_{1}_{2}_{3}".format(word_size,num_words,num_banks,tech_name)
|
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
|
||||||
|
|
||||||
#Setting for multiport
|
|
||||||
# netlist_only = True
|
|
||||||
# bitcell = "pbitcell"
|
|
||||||
# replica_bitcell="replica_pbitcell"
|
|
||||||
# num_rw_ports = 1
|
|
||||||
# num_r_ports = 1
|
|
||||||
# num_w_ports = 0
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ if len(args) != 1:
|
||||||
# These depend on arguments, so don't load them until now.
|
# These depend on arguments, so don't load them until now.
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
|
|
||||||
init_openram(config_file=args[0], is_unit_test=False)
|
init_openram(config_file=args[0], is_unit_test=False)
|
||||||
|
|
||||||
# Only print banner here so it's not in unit tests
|
# Only print banner here so it's not in unit tests
|
||||||
|
|
@ -39,8 +38,8 @@ report_status()
|
||||||
import verify
|
import verify
|
||||||
from sram import sram
|
from sram import sram
|
||||||
from sram_config import sram_config
|
from sram_config import sram_config
|
||||||
|
#from parser import *
|
||||||
output_extensions = ["sp","v","lib"]
|
output_extensions = ["sp","v","lib","html"]
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
output_extensions.extend(["gds","lef"])
|
output_extensions.extend(["gds","lef"])
|
||||||
output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in output_extensions]
|
output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in output_extensions]
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,14 @@ class sram():
|
||||||
self.s.lef_write(lefname)
|
self.s.lef_write(lefname)
|
||||||
print_time("LEF", datetime.datetime.now(), start_time)
|
print_time("LEF", datetime.datetime.now(), start_time)
|
||||||
|
|
||||||
|
# Write the datasheet
|
||||||
|
start_time = datetime.datetime.now()
|
||||||
|
from datasheet_gen import datasheet_gen
|
||||||
|
dname = OPTS.output_path + self.s.name + ".html"
|
||||||
|
print("Datasheet: writing to {0}".format(dname))
|
||||||
|
datasheet_gen.datasheet_write(dname)
|
||||||
|
print_time("Datasheet", datetime.datetime.now(), start_time)
|
||||||
|
|
||||||
# Write a verilog model
|
# Write a verilog model
|
||||||
start_time = datetime.datetime.now()
|
start_time = datetime.datetime.now()
|
||||||
vname = OPTS.output_path + self.s.name + ".v"
|
vname = OPTS.output_path + self.s.name + ".v"
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,11 @@ class openram_test(openram_test):
|
||||||
files = glob.glob('{0}/*.lib'.format(out_path))
|
files = glob.glob('{0}/*.lib'.format(out_path))
|
||||||
self.assertTrue(len(files)>0)
|
self.assertTrue(len(files)>0)
|
||||||
|
|
||||||
|
# Make sure there is any .html file
|
||||||
|
if os.path.exists(out_path):
|
||||||
|
datasheets = glob.glob('{0}/*html'.format(out_path))
|
||||||
|
self.assertTrue(len(datasheets)>0)
|
||||||
|
|
||||||
# grep any errors from the output
|
# grep any errors from the output
|
||||||
output_log = open("{0}/output.log".format(out_path),"r")
|
output_log = open("{0}/output.log".format(out_path),"r")
|
||||||
output = output_log.read()
|
output = output_log.read()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue