OpenRAM/compiler/datasheet/datasheet_gen.py

552 lines
22 KiB
Python
Raw Normal View History

2018-10-12 01:03:05 +02:00
#!/usr/bin/env python3
"""
2019-01-16 04:47:48 +01:00
This is a script to load data from the characterization and layout processes into
a web friendly html datasheet.
2018-10-12 01:03:05 +02:00
"""
2019-01-16 04:47:48 +01:00
# TODO:
# include power
2019-01-16 04:47:48 +01:00
# Diagram generation
# Improve css
2018-12-06 19:13:28 +01:00
2018-10-12 01:03:05 +02:00
from globals import OPTS
2019-01-16 04:47:48 +01:00
import os
import math
import csv
2019-01-16 04:47:48 +01:00
import datasheet
import table_gen
2018-10-12 01:03:05 +02:00
def process_name(corner):
"""
Expands the names of the characterization corner types into something human friendly
"""
2018-10-12 01:03:05 +02:00
if corner == "TT":
return "Typical - Typical"
if corner == "SS":
return "Slow - Slow"
if corner == "FF":
return "Fast - Fast"
else:
return "custom"
2019-01-16 04:47:48 +01:00
def parse_characterizer_csv(f, pages):
"""
Parses output data of the Liberty file generator in order to construct the timing and
current table
"""
2018-10-12 01:03:05 +02:00
with open(f) as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',')
for row in csv_reader:
2018-10-12 01:03:05 +02:00
found = 0
col = 0
2019-01-16 04:47:48 +01:00
# defines layout of csv file
NAME = row[col]
col += 1
NUM_WORDS = row[col]
col += 1
NUM_BANKS = row[col]
col += 1
NUM_RW_PORTS = row[col]
col += 1
NUM_W_PORTS = row[col]
col += 1
NUM_R_PORTS = row[col]
col += 1
TECH_NAME = row[col]
col += 1
TEMP = row[col]
col += 1
VOLT = row[col]
col += 1
PROC = row[col]
col += 1
MIN_PERIOD = row[col]
col += 1
OUT_DIR = row[col]
col += 1
LIB_NAME = row[col]
col += 1
WORD_SIZE = row[col]
col += 1
2019-01-16 04:47:48 +01:00
2018-12-03 19:53:50 +01:00
ORIGIN_ID = row[col]
col += 1
2019-01-02 19:30:03 +01:00
DATETIME = row[col]
2019-01-16 04:47:48 +01:00
col += 1
2019-01-02 19:30:03 +01:00
DRC = row[col]
col += 1
LVS = row[col]
col += 1
2018-10-12 01:03:05 +02:00
AREA = row[col]
col += 1
2019-01-16 04:47:48 +01:00
for sheet in pages:
2018-10-12 01:03:05 +02:00
if sheet.name == NAME:
2018-10-31 06:32:19 +01:00
2018-10-12 01:03:05 +02:00
found = 1
2019-01-16 04:47:48 +01:00
# if the .lib information is for an existing datasheet compare timing data
2018-10-12 01:03:05 +02:00
for item in sheet.operating_table.rows:
2019-01-16 04:47:48 +01:00
# check if the new corner data is worse than the previous worse corner data
2018-10-12 01:03:05 +02:00
if item[0] == 'Operating Temperature':
if float(TEMP) > float(item[3]):
item[2] = item[3]
item[3] = TEMP
if float(TEMP) < float(item[1]):
item[2] = item[1]
item[1] = TEMP
if item[0] == 'Power supply (VDD) range':
if float(VOLT) > float(item[3]):
item[2] = item[3]
item[3] = VOLT
if float(VOLT) < float(item[1]):
item[2] = item[1]
item[1] = VOLT
if item[0] == 'Operating Frequncy (F)':
try:
if float(math.floor(1000/float(MIN_PERIOD)) < float(item[3])):
2019-01-16 04:47:48 +01:00
item[3] = str(math.floor(
1000/float(MIN_PERIOD)))
except Exception:
pass
while(True):
if(row[col].startswith('DIN')):
start = col
for item in sheet.timing_table.rows:
if item[0].startswith(row[col]):
if item[0].endswith('setup rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('setup falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('hold rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('hold falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 1
elif(row[col].startswith('DOUT')):
start = col
for item in sheet.timing_table.rows:
if item[0].startswith(row[col]):
if item[0].endswith('cell rise'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('cell fall'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('rise transition'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('fall transition'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 1
elif(row[col].startswith('CSb')):
start = col
for item in sheet.timing_table.rows:
if item[0].startswith(row[col]):
if item[0].endswith('setup rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('setup falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('hold rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('hold falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 1
elif(row[col].startswith('WEb')):
start = col
for item in sheet.timing_table.rows:
if item[0].startswith(row[col]):
if item[0].endswith('setup rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('setup falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('hold rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('hold falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 1
elif(row[col].startswith('ADDR')):
start = col
for item in sheet.timing_table.rows:
if item[0].startswith(row[col]):
if item[0].endswith('setup rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('setup falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('hold rising'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
elif item[0].endswith('hold falling'):
if float(row[col+1]) < float(item[1]):
item[1] = row[col+1]
if float(row[col+2]) > float(item[2]):
item[2] = row[col+2]
col += 2
col += 1
else:
break
2019-01-16 04:47:48 +01:00
datasheet.new_sheet.corners_table.add_row([PROC, process_name(
PROC), VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')])
datasheet.new_sheet.dlv_table.add_row(
['.lib', 'Synthesis models', '<a href="file://{0}">{1}</a>'.format(LIB_NAME, LIB_NAME.replace(OUT_DIR, ''))])
2018-10-12 01:03:05 +02:00
if found == 0:
2019-01-16 04:47:48 +01:00
# if this is the first corner for this sram, run first time configuration and set up tables
new_sheet = datasheet.datasheet(NAME)
2018-10-12 01:03:05 +02:00
pages.append(new_sheet)
2019-01-02 19:30:03 +01:00
new_sheet.git_id = ORIGIN_ID
new_sheet.time = DATETIME
new_sheet.DRC = DRC
new_sheet.LVS = LVS
2019-01-16 04:47:48 +01:00
new_sheet.description = [NAME, NUM_WORDS, NUM_BANKS, NUM_RW_PORTS,
NUM_W_PORTS, NUM_R_PORTS, TECH_NAME, WORD_SIZE, ORIGIN_ID, DATETIME]
new_sheet.corners_table = table_gen.table_gen("corners")
new_sheet.corners_table.add_row(
['Corner Name', 'Process', 'Power Supply', 'Temperature', 'Library Name Suffix'])
new_sheet.corners_table.add_row([PROC, process_name(
PROC), VOLT, TEMP, LIB_NAME.replace(OUT_DIR, '').replace(NAME, '')])
new_sheet.operating_table = table_gen.table_gen("operating_table")
new_sheet.operating_table.add_row(
['Parameter', 'Min', 'Typ', 'Max', 'Units'])
new_sheet.operating_table.add_row(
['Power supply (VDD) range', VOLT, VOLT, VOLT, 'Volts'])
new_sheet.operating_table.add_row(
['Operating Temperature', TEMP, TEMP, TEMP, 'Celsius'])
try:
2019-01-16 04:47:48 +01:00
new_sheet.operating_table.add_row(['Operating Frequency (F)', '', '', str(
math.floor(1000/float(MIN_PERIOD))), 'MHz'])
except Exception:
2019-01-16 04:47:48 +01:00
# failed to provide non-zero MIN_PERIOD
new_sheet.operating_table.add_row(
['Operating Frequency (F)', '', '', "not available in netlist only", 'MHz'])
new_sheet.timing_table = table_gen.table_gen("timing")
new_sheet.timing_table.add_row(
['Parameter', 'Min', 'Max', 'Units'])
while(True):
if(row[col].startswith('DIN')):
start = col
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} setup rising'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} setup falling'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} hold rising'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} hold falling'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
col += 1
elif(row[col].startswith('DOUT')):
start = col
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} cell rise'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} cell fall'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} rise transition'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} fall transition'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
col += 1
elif(row[col].startswith('CSb')):
start = col
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} setup rising'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} setup falling'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} hold rising'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} hold falling'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
col += 1
elif(row[col].startswith('WEb')):
start = col
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} setup rising'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} setup falling'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} hold rising'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} hold falling'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
col += 1
elif(row[col].startswith('ADDR')):
start = col
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} setup rising'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} setup falling'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} hold rising'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
new_sheet.timing_table.add_row(
['{0} hold falling'.format(row[start]), row[col+1], row[col+2], 'ns'])
col += 2
2019-01-16 04:47:48 +01:00
col += 1
else:
break
2019-01-16 04:47:48 +01:00
new_sheet.dlv_table = table_gen.table_gen("dlv")
new_sheet.dlv_table.add_row(['Type', 'Description', 'Link'])
2019-01-16 04:47:48 +01:00
new_sheet.io_table = table_gen.table_gen("io")
new_sheet.io_table.add_row(['Type', 'Value'])
if not OPTS.netlist_only:
2019-01-16 04:47:48 +01:00
# physical layout files should not be generated in netlist only mode
new_sheet.dlv_table.add_row(
['.gds', 'GDSII layout views', '<a href="{0}.{1}">{0}.{1}</a>'.format(OPTS.output_name, 'gds')])
new_sheet.dlv_table.add_row(
['.lef', 'LEF files', '<a href="{0}.{1}">{0}.{1}</a>'.format(OPTS.output_name, 'lef')])
new_sheet.dlv_table.add_row(
['.log', 'OpenRAM compile log', '<a href="{0}.{1}">{0}.{1}</a>'.format(OPTS.output_name, 'log')])
new_sheet.dlv_table.add_row(
['.v', 'Verilog simulation models', '<a href="{0}.{1}">{0}.{1}</a>'.format(OPTS.output_name, 'v')])
new_sheet.dlv_table.add_row(
['.html', 'This datasheet', '<a href="{0}.{1}">{0}.{1}</a>'.format(OPTS.output_name, 'html')])
new_sheet.dlv_table.add_row(
['.lib', 'Synthesis models', '<a href="{1}">{1}</a>'.format(LIB_NAME, LIB_NAME.replace(OUT_DIR, ''))])
new_sheet.dlv_table.add_row(
['.py', 'OpenRAM configuration file', '<a href="{0}.{1}">{0}.{1}</a>'.format(OPTS.output_name, 'py')])
new_sheet.dlv_table.add_row(
['.sp', 'SPICE netlists', '<a href="{0}.{1}">{0}.{1}</a>'.format(OPTS.output_name, 'sp')])
new_sheet.io_table.add_row(['WORD_SIZE', WORD_SIZE])
new_sheet.io_table.add_row(['NUM_WORDS', NUM_WORDS])
new_sheet.io_table.add_row(['NUM_BANKS', NUM_BANKS])
new_sheet.io_table.add_row(['NUM_RW_PORTS', NUM_RW_PORTS])
new_sheet.io_table.add_row(['NUM_R_PORTS', NUM_R_PORTS])
new_sheet.io_table.add_row(['NUM_W_PORTS', NUM_W_PORTS])
new_sheet.io_table.add_row(['Area', AREA])
2018-10-12 01:03:05 +02:00
class datasheet_gen():
def datasheet_write(name):
in_dir = OPTS.openram_temp
if not (os.path.isdir(in_dir)):
os.mkdir(in_dir)
2018-10-12 01:03:05 +02:00
datasheets = []
parse_characterizer_csv(in_dir + "/datasheet.info", datasheets)
2018-10-12 01:03:05 +02:00
for sheets in datasheets:
with open(name, 'w+') as f:
sheets.generate_html()
f.write(sheets.html)