2018-10-12 01:03:05 +02:00
#!/usr/bin/env python3
"""
2018-10-31 05:37:30 +01:00
This is a script to load data from the characterization and layout processes into
a web friendly html datasheet . This script requres the python - flask and flask - table
packages to be installed .
2018-10-12 01:03:05 +02:00
"""
2018-10-31 05:37:30 +01:00
#TODO:
#locate all port elements in .lib
#Locate all timing elements in .lib
#Diagram generation
#Improve css
2018-12-06 19:13:28 +01:00
2018-10-18 18:58:19 +02:00
import debug
2018-10-12 01:03:05 +02:00
from globals import OPTS
2018-10-18 18:58:19 +02:00
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. " )
2018-10-31 05:37:30 +01:00
#make sure appropriate python libraries are installed
2018-10-18 18:58:19 +02:00
2018-10-12 01:03:05 +02:00
def process_name ( corner ) :
2018-10-31 05:37:30 +01:00
"""
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 "
2018-11-09 06:30:17 +01:00
def parse_characterizer_csv ( sram , f , pages ) :
2018-10-31 05:37:30 +01:00
"""
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 = ' , ' )
line_count = 0
for row in csv_reader :
2018-11-11 05:23:26 +01:00
2018-10-12 01:03:05 +02:00
found = 0
2018-11-11 05:23:26 +01:00
col = 0
2018-10-31 05:37:30 +01:00
#defines layout of csv file
2018-11-11 05:23:26 +01:00
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
2018-12-03 19:53:50 +01:00
ORIGIN_ID = row [ col ]
col + = 1
2018-11-11 05:23:26 +01:00
2018-10-31 06:56:13 +01:00
2018-10-12 01:03:05 +02:00
for sheet in pages :
2018-11-11 05:23:26 +01:00
if sheet . name == NAME :
2018-10-31 06:32:19 +01:00
2018-10-12 01:03:05 +02:00
found = 1
#if the .lib information is for an existing datasheet compare timing data
for item in sheet . operating :
2018-12-03 19:53:50 +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 . 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) ' :
2018-10-12 22:22:12 +02:00
try :
if float ( math . floor ( 1000 / float ( MIN_PERIOD ) ) < float ( item . max ) ) :
item . max = str ( math . floor ( 1000 / float ( MIN_PERIOD ) ) )
except Exception :
pass
2018-10-12 01:03:05 +02:00
2018-11-11 05:23:26 +01:00
while ( True ) :
if ( row [ col ] . startswith ( ' DIN ' ) ) :
start = col
for item in sheet . timing :
if item . parameter . startswith ( row [ col ] ) :
if item . parameter . endswith ( ' setup rising ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' setup falling ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' hold rising ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' hold falling ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
col + = 1
elif ( row [ col ] . startswith ( ' DOUT ' ) ) :
start = col
for item in sheet . timing :
if item . parameter . startswith ( row [ col ] ) :
if item . parameter . endswith ( ' cell rise ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' cell fall ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' rise transition ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' fall transition ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
col + = 1
elif ( row [ col ] . startswith ( ' CSb ' ) ) :
start = col
for item in sheet . timing :
if item . parameter . startswith ( row [ col ] ) :
if item . parameter . endswith ( ' setup rising ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' setup falling ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' hold rising ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' hold falling ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
col + = 1
elif ( row [ col ] . startswith ( ' WEb ' ) ) :
start = col
for item in sheet . timing :
if item . parameter . startswith ( row [ col ] ) :
if item . parameter . endswith ( ' setup rising ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' setup falling ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' hold rising ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' hold falling ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
col + = 1
elif ( row [ col ] . startswith ( ' ADDR ' ) ) :
start = col
for item in sheet . timing :
if item . parameter . startswith ( row [ col ] ) :
if item . parameter . endswith ( ' setup rising ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' setup falling ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' hold rising ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
elif item . parameter . endswith ( ' hold falling ' ) :
if float ( row [ col + 1 ] ) < float ( item . min ) :
item . min = row [ col + 1 ]
if float ( row [ col + 2 ] ) > float ( item . max ) :
item . max = row [ col + 2 ]
col + = 2
col + = 1
else :
break
2018-10-12 01:03:05 +02:00
2018-10-31 05:37:30 +01:00
#regardless of if there is already a corner for the current sram, append the new corner to the datasheet
2018-10-12 01:03:05 +02:00
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 :
2018-10-31 06:56:13 +01:00
2018-10-31 05:37:30 +01:00
#if this is the first corner for this sram, run first time configuration and set up tables
2018-10-12 01:03:05 +02:00
new_sheet = datasheet ( NAME )
pages . append ( new_sheet )
2018-12-03 19:53:50 +01:00
new_sheet . git_id = ORIGIN_ID
2018-10-12 01:03:05 +02:00
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 ' ) )
2018-10-12 22:22:12 +02:00
try :
2018-12-06 19:17:12 +01:00
new_sheet . operating . append ( operating_conditions_item ( ' Operating Frequency (F) ' , ' ' , ' ' , str ( math . floor ( 1000 / float ( MIN_PERIOD ) ) ) , ' MHz ' ) )
2018-10-12 22:22:12 +02:00
except Exception :
2018-12-06 19:29:48 +01:00
new_sheet . operating . append ( operating_conditions_item ( ' Operating Frequency (F) ' , ' ' , ' ' , " not available in netlist only " , ' MHz ' ) ) #failed to provide non-zero MIN_PERIOD
2018-10-31 05:37:30 +01:00
2018-11-11 05:23:26 +01:00
while ( True ) :
if ( row [ col ] . startswith ( ' DIN ' ) ) :
start = col
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} setup rising ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} setup falling ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} hold rising ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} hold falling ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
col + = 1
elif ( row [ col ] . startswith ( ' DOUT ' ) ) :
start = col
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} cell rise ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} cell fall ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} rise transition ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} fall transition ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
col + = 1
elif ( row [ col ] . startswith ( ' CSb ' ) ) :
start = col
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} setup rising ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} setup falling ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} hold rising ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} hold falling ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
col + = 1
elif ( row [ col ] . startswith ( ' WEb ' ) ) :
start = col
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} setup rising ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} setup falling ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} hold rising ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} hold falling ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
col + = 1
elif ( row [ col ] . startswith ( ' ADDR ' ) ) :
start = col
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} setup rising ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} setup falling ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} hold rising ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
new_sheet . timing . append ( timing_and_current_data_item ( ' {0} hold falling ' . format ( row [ start ] ) , row [ col + 1 ] , row [ col + 2 ] , ' ns ' ) )
col + = 2
col + = 1
else :
break
2018-12-06 19:17:12 +01:00
2018-10-27 20:21:06 +02:00
if not OPTS . netlist_only :
2018-10-31 05:37:30 +01:00
#physical layout files should not be generated in netlist only mode
2018-12-06 19:13:28 +01:00
new_sheet . dlv . append ( deliverables_item ( ' .gds ' , ' GDSII layout views ' , ' <a href= " {0} . {1} " > {0} . {1} </a> ' . format ( OPTS . output_name , ' gds ' ) ) )
new_sheet . dlv . append ( deliverables_item ( ' .lef ' , ' LEF files ' , ' <a href= " {0} . {1} " > {0} . {1} </a> ' . format ( OPTS . output_name , ' lef ' ) ) )
2018-10-27 20:21:06 +02:00
2018-10-12 01:03:05 +02:00
2018-11-20 21:27:54 +01:00
new_sheet . dlv . append ( deliverables_item ( ' .sp ' , ' SPICE netlists ' , ' <a href= " {0} . {1} " > {0} . {1} </a> ' . format ( OPTS . output_name , ' sp ' ) ) )
new_sheet . dlv . append ( deliverables_item ( ' .v ' , ' Verilog simulation models ' , ' <a href= " {0} . {1} " > {0} . {1} </a> ' . format ( OPTS . output_name , ' v ' ) ) )
new_sheet . dlv . append ( deliverables_item ( ' .html ' , ' This datasheet ' , ' <a href= " {0} . {1} " > {0} . {1} </a> ' . format ( OPTS . output_name , ' html ' ) ) )
new_sheet . dlv . append ( deliverables_item ( ' .lib ' , ' Synthesis models ' , ' <a href= " {1} " > {1} </a> ' . format ( LIB_NAME , LIB_NAME . replace ( OUT_DIR , ' ' ) ) ) )
new_sheet . dlv . append ( deliverables_item ( ' .py ' , ' OpenRAM configuration file ' , ' <a href= " {0} . {1} " > {0} . {1} </a> ' . format ( OPTS . output_name , ' py ' ) ) )
2018-10-31 20:29:13 +01:00
2018-10-27 20:21:06 +02:00
2018-10-31 05:37:30 +01:00
#debug table for multiport information
2018-10-18 04:27:09 +02:00
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 ) )
2018-11-09 06:30:17 +01:00
new_sheet . io . append ( in_out_item ( ' Area ' , sram . width * sram . height ) )
2018-10-18 04:27:09 +02:00
2018-10-12 01:03:05 +02:00
2018-10-12 22:22:12 +02:00
class datasheet_gen ( ) :
2018-11-09 06:30:17 +01:00
def datasheet_write ( sram , name ) :
2018-10-12 22:22:12 +02:00
2018-10-18 18:58:19 +02:00
if OPTS . datasheet_gen :
in_dir = OPTS . openram_temp
if not ( os . path . isdir ( in_dir ) ) :
os . mkdir ( in_dir )
2018-10-12 01:03:05 +02:00
2018-10-18 18:58:19 +02:00
datasheets = [ ]
2018-11-09 06:30:17 +01:00
parse_characterizer_csv ( sram , in_dir + " /datasheet.info " , datasheets )
2018-10-12 01:03:05 +02:00
2018-10-18 18:58:19 +02:00
for sheets in datasheets :
with open ( name , ' w+ ' ) as f :
sheets . generate_html ( )
f . write ( sheets . html )