From fa979e2d344181d791ad0241383ec0e61dd81ab5 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Sat, 6 Oct 2018 21:15:54 -0700 Subject: [PATCH 01/10] initial stages of html documentation generation --- compiler/characterizer/lib.py | 37 ++++- compiler/openram.py | 7 +- compiler/parser.py | 236 ++++++++++++++++++++++++++++++ compiler/tests/30_openram_test.py | 6 +- 4 files changed, 282 insertions(+), 4 deletions(-) create mode 100644 compiler/parser.py diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 9f04fd7f..54558b01 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -29,6 +29,8 @@ class lib: self.characterize_corners() + + def gen_port_names(self): """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. @@ -100,7 +102,7 @@ class lib: debug.info(1,"Writing to {0}".format(lib_name)) self.characterize() self.lib.close() - + self.parse_info() def characterize(self): """ Characterize the current corner. """ @@ -519,3 +521,36 @@ class lib: else: 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 (self.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() + diff --git a/compiler/openram.py b/compiler/openram.py index e4ab593e..b751b6df 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -40,7 +40,7 @@ report_status() import verify from sram import sram from sram_config import sram_config - +import parser output_extensions = ["sp","v","lib"] if not OPTS.netlist_only: output_extensions.extend(["gds","lef"]) @@ -63,8 +63,11 @@ s = sram(sram_config=c, # Output the files for the resulting SRAM s.save() +# generate datasheet from characterization of created SRAM +p = parser.parse(OPTS.openram_temp,os.environ.get('OPENRAM_HOME')+"/datasheets") + # Delete temp files etc. -end_openram() +#end_openram() print_time("End",datetime.datetime.now(), start_time) diff --git a/compiler/parser.py b/compiler/parser.py new file mode 100644 index 00000000..a9792b67 --- /dev/null +++ b/compiler/parser.py @@ -0,0 +1,236 @@ +#!/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 +import contextlib +from globals import OPTS + +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 + +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 + +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 + +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 + +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)': + if float(math.floor(1000/float(MIN_PERIOD)) < float(item.max)): + item.max = str(math.floor(1000/float(MIN_PERIOD))) + + + + 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','{1}'.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')) + new_sheet.operating.append(operating_conditions_item('Operating Frequency (F)','','',str(math.floor(1000/float(MIN_PERIOD))),'MHz')) + + new_sheet.timing.append(timing_and_current_data_item('1','2','3','4')) + + new_sheet.dlv.append(deliverables_item('.sp','SPICE netlists','{1}.{2}'.format(OUT_DIR,NAME,'sp'))) + new_sheet.dlv.append(deliverables_item('.v','Verilog simulation models','{1}.{2}'.format(OUT_DIR,NAME,'v'))) + new_sheet.dlv.append(deliverables_item('.gds','GDSII layout views','{1}.{2}'.format(OUT_DIR,NAME,'gds'))) + new_sheet.dlv.append(deliverables_item('.lef','LEF files','{1}.{2}'.format(OUT_DIR,NAME,'lef'))) + new_sheet.dlv.append(deliverables_item('.lib','Synthesis models','{1}'.format(LIB_NAME,LIB_NAME.replace(OUT_DIR,'')))) + + + +class datasheet(): + + def __init__(self,identifier): + self.corners = [] + self.timing = [] + self.operating = [] + self.dlv = [] + self.name = identifier + + def print(self): + print("""""") + print('

{0}

') + print('

{0}

') + print('

{0}

') + print('

Operating Conditions

') + print(operating_conditions(self.operating,table_id='data').__html__()) + print('

Timing and Current Data

') + print(timing_and_current_data(self.timing,table_id='data').__html__()) + print('

Characterization Corners

') + print(characterization_corners(self.corners,table_id='data').__html__()) + print('

Deliverables

') + print(deliverables(self.dlv,table_id='data').__html__().replace('<','<').replace('"','"').replace('>',">")) + + +class parse(): + def __init__(self,in_dir,out_dir): + + 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: + print (out_dir + sheets.name + ".html") + with open(out_dir + "/" + sheets.name + ".html", 'w+') as f: + with contextlib.redirect_stdout(f): + sheets.print() + + + + + + + + + + + + + diff --git a/compiler/tests/30_openram_test.py b/compiler/tests/30_openram_test.py index 81864840..983d746c 100755 --- a/compiler/tests/30_openram_test.py +++ b/compiler/tests/30_openram_test.py @@ -62,7 +62,11 @@ class openram_test(openram_test): import glob files = glob.glob('{0}/*.lib'.format(out_path)) self.assertTrue(len(files)>0) - + + # Make sure there is any .html file + datasheets = glob.glob('{0}/{1}/*html'.format(OPENRAM_HOME,datasheets)) + self.assertTrue(len(datasheets)>0) + # grep any errors from the output output_log = open("{0}/output.log".format(out_path),"r") output = output_log.read() From 49268b025ff894a94e08c6e23f632a31983a674e Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Sat, 6 Oct 2018 21:17:26 -0700 Subject: [PATCH 02/10] fixed /tmp/ typo --- compiler/openram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/openram.py b/compiler/openram.py index b751b6df..a6e407f9 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -67,7 +67,7 @@ s.save() p = parser.parse(OPTS.openram_temp,os.environ.get('OPENRAM_HOME')+"/datasheets") # Delete temp files etc. -#end_openram() +end_openram() print_time("End",datetime.datetime.now(), start_time) From bc54bc238fc3e0f39907588b404fa2a2964b1a1b Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 11 Oct 2018 11:18:40 -0700 Subject: [PATCH 03/10] removed tabs and fixed bug in which datasheets generated without the characterizer running --- compiler/openram.py | 3 +- compiler/parser.py | 155 ++++++++++++++---------------- compiler/tests/30_openram_test.py | 7 +- 3 files changed, 77 insertions(+), 88 deletions(-) diff --git a/compiler/openram.py b/compiler/openram.py index a6e407f9..c84817a3 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -64,7 +64,8 @@ s = sram(sram_config=c, s.save() # generate datasheet from characterization of created SRAM -p = parser.parse(OPTS.openram_temp,os.environ.get('OPENRAM_HOME')+"/datasheets") +if not OPTS.analytical_delay: + p = parser.parse(OPTS.openram_temp,os.environ.get('OPENRAM_HOME')+"/datasheets") # Delete temp files etc. end_openram() diff --git a/compiler/parser.py b/compiler/parser.py index a9792b67..031f43a9 100644 --- a/compiler/parser.py +++ b/compiler/parser.py @@ -17,61 +17,61 @@ import contextlib from globals import OPTS class deliverables(Table): - typ = Col('Type') - description = Col('Description') - link = Col('Link') - + 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 + def __init__(self, typ, description,link): + self.typ = typ + self.description = description + self.link = link class operating_conditions(Table): - parameter = Col('Parameter') - min = Col('Min') - typ = Col('Typ') - max = Col('Max') - units = Col('Units') + 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 + def __init__(self, parameter, min, typ, max, units): + self.parameter = parameter + self.min = min + self.typ = typ + self.max = max + self.units = units class timing_and_current_data(Table): - parameter = Col('Parameter') - min = Col('Min') - max = Col('Max') - units = Col('Units') + 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 + def __init__(self, parameter, min, max, units): + self.parameter = parameter + self.min = min + self.max = max + self.units = units 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') + 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 - + 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 + def process_name(corner): if corner == "TT": return "Typical - Typical" @@ -81,12 +81,12 @@ def process_name(corner): 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: + for row in csv_reader: found = 0 NAME = row[0] NUM_WORDS = row[1] @@ -102,14 +102,14 @@ def parse_file(f,pages): 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 @@ -117,7 +117,7 @@ def parse_file(f,pages): 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 @@ -125,36 +125,36 @@ def parse_file(f,pages): if float(VOLT) < float(item.min): item.typ = item.min item.min = VOLT - + if item.parameter == 'Operating Frequncy (F)': if float(math.floor(1000/float(MIN_PERIOD)) < float(item.max)): item.max = str(math.floor(1000/float(MIN_PERIOD))) - - - + + + 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','{1}'.format(LIB_NAME,LIB_NAME.replace(OUT_DIR,'')))) - - if found == 0: + + 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')) new_sheet.operating.append(operating_conditions_item('Operating Frequency (F)','','',str(math.floor(1000/float(MIN_PERIOD))),'MHz')) - + new_sheet.timing.append(timing_and_current_data_item('1','2','3','4')) - + new_sheet.dlv.append(deliverables_item('.sp','SPICE netlists','{1}.{2}'.format(OUT_DIR,NAME,'sp'))) new_sheet.dlv.append(deliverables_item('.v','Verilog simulation models','{1}.{2}'.format(OUT_DIR,NAME,'v'))) new_sheet.dlv.append(deliverables_item('.gds','GDSII layout views','{1}.{2}'.format(OUT_DIR,NAME,'gds'))) new_sheet.dlv.append(deliverables_item('.lef','LEF files','{1}.{2}'.format(OUT_DIR,NAME,'lef'))) new_sheet.dlv.append(deliverables_item('.lib','Synthesis models','{1}'.format(LIB_NAME,LIB_NAME.replace(OUT_DIR,'')))) - - - + + + class datasheet(): def __init__(self,identifier): @@ -163,7 +163,7 @@ class datasheet(): self.operating = [] self.dlv = [] self.name = identifier - + def print(self): print("""""") - print('

{0}

') - print('

{0}

') - print('

{0}

') - print('

Operating Conditions

') - print(operating_conditions(self.operating,table_id='data').__html__()) - print('

Timing and Current Data

') - print(timing_and_current_data(self.timing,table_id='data').__html__()) - print('

Characterization Corners

') - print(characterization_corners(self.corners,table_id='data').__html__()) - print('

Deliverables

') - print(deliverables(self.dlv,table_id='data').__html__().replace('<','<').replace('"','"').replace('>',">")) +""" + self.html +='

{0}

' + self.html +='

{0}

' + self.html +='

{0}

' + self.html +='

Operating Conditions

' + self.html += operating_conditions(self.operating,table_id='data').__html__() + self.html += '

Timing and Current Data

' + self.html += timing_and_current_data(self.timing,table_id='data').__html__() + self.html += '

Characterization Corners

' + self.html += characterization_corners(self.corners,table_id='data').__html__() + self.html +='

Deliverables

' + self.html += deliverables(self.dlv,table_id='data').__html__().replace('<','<').replace('"','"').replace('>',">") class parse(): @@ -217,7 +218,7 @@ class parse(): for sheets in datasheets: - print (out_dir + sheets.name + ".html") +# print (out_dir + sheets.name + ".html") with open(out_dir + "/" + sheets.name + ".html", 'w+') as f: - with contextlib.redirect_stdout(f): - sheets.print() + sheets.generate_html() + f.write(sheets.html) From cfb5921d987bb1195c5d0a69c182450778e5af15 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 11 Oct 2018 15:59:06 -0700 Subject: [PATCH 06/10] reorganized code structure --- compiler/globals.py | 2 +- compiler/openram.py | 7 +- compiler/parser.py | 224 ------------------------------ compiler/tests/30_openram_test.py | 6 +- 4 files changed, 8 insertions(+), 231 deletions(-) delete mode 100644 compiler/parser.py diff --git a/compiler/globals.py b/compiler/globals.py index af89eaa4..f19559e2 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -287,7 +287,7 @@ def setup_paths(): # Add all of the subdirs to the python path # These subdirs are modules and don't need to be added: characterizer, verify - for subdir in ["gdsMill", "tests", "modules", "base", "pgates"]: + for subdir in ["gdsMill", "tests", "modules", "base", "pgates", "datasheet"]: full_path = "{0}/{1}".format(OPENRAM_HOME,subdir) debug.check(os.path.isdir(full_path), "$OPENRAM_HOME/{0} does not exist: {1}".format(subdir,full_path)) diff --git a/compiler/openram.py b/compiler/openram.py index c84817a3..a588f806 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -27,7 +27,6 @@ if len(args) != 1: # These depend on arguments, so don't load them until now. import debug - init_openram(config_file=args[0], is_unit_test=False) # Only print banner here so it's not in unit tests @@ -40,7 +39,7 @@ report_status() import verify from sram import sram from sram_config import sram_config -import parser +#from parser import * output_extensions = ["sp","v","lib"] if not OPTS.netlist_only: output_extensions.extend(["gds","lef"]) @@ -65,7 +64,9 @@ s.save() # generate datasheet from characterization of created SRAM if not OPTS.analytical_delay: - p = parser.parse(OPTS.openram_temp,os.environ.get('OPENRAM_HOME')+"/datasheets") + import datasheet_gen + p = datasheet_gen.parse(OPTS.openram_temp,os.environ.get('OPENRAM_HOME')+"/datasheet/datasheets") + # Delete temp files etc. end_openram() diff --git a/compiler/parser.py b/compiler/parser.py deleted file mode 100644 index 4d514014..00000000 --- a/compiler/parser.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/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 -import contextlib -from globals import OPTS - -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 - -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 - -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 - -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 - -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)': - if float(math.floor(1000/float(MIN_PERIOD)) < float(item.max)): - item.max = str(math.floor(1000/float(MIN_PERIOD))) - - - - 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','{1}'.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')) - new_sheet.operating.append(operating_conditions_item('Operating Frequency (F)','','',str(math.floor(1000/float(MIN_PERIOD))),'MHz')) - - new_sheet.timing.append(timing_and_current_data_item('1','2','3','4')) - - new_sheet.dlv.append(deliverables_item('.sp','SPICE netlists','{1}.{2}'.format(OUT_DIR,NAME,'sp'))) - new_sheet.dlv.append(deliverables_item('.v','Verilog simulation models','{1}.{2}'.format(OUT_DIR,NAME,'v'))) - new_sheet.dlv.append(deliverables_item('.gds','GDSII layout views','{1}.{2}'.format(OUT_DIR,NAME,'gds'))) - new_sheet.dlv.append(deliverables_item('.lef','LEF files','{1}.{2}'.format(OUT_DIR,NAME,'lef'))) - new_sheet.dlv.append(deliverables_item('.lib','Synthesis models','{1}'.format(LIB_NAME,LIB_NAME.replace(OUT_DIR,'')))) - - - -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 += """""" - self.html +='

{0}

' - self.html +='

{0}

' - self.html +='

{0}

' - self.html +='

Operating Conditions

' - self.html += operating_conditions(self.operating,table_id='data').__html__() - self.html += '

Timing and Current Data

' - self.html += timing_and_current_data(self.timing,table_id='data').__html__() - self.html += '

Characterization Corners

' - self.html += characterization_corners(self.corners,table_id='data').__html__() - self.html +='

Deliverables

' - self.html += deliverables(self.dlv,table_id='data').__html__().replace('<','<').replace('"','"').replace('>',">") - - -class parse(): - def __init__(self,in_dir,out_dir): - - 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: -# print (out_dir + sheets.name + ".html") - with open(out_dir + "/" + sheets.name + ".html", 'w+') as f: - sheets.generate_html() - f.write(sheets.html) diff --git a/compiler/tests/30_openram_test.py b/compiler/tests/30_openram_test.py index 7be820e0..d53182fc 100755 --- a/compiler/tests/30_openram_test.py +++ b/compiler/tests/30_openram_test.py @@ -63,9 +63,9 @@ class openram_test(openram_test): files = glob.glob('{0}/*.lib'.format(out_path)) self.assertTrue(len(files)>0) - # Make sure there is any .html file if characterizer was ran - if not OPTS.analytical_delay: - datasheets = glob.glob('{0}/{1}/*html'.format(OPENRAM_HOME,'datasheets')) + # Make sure there is any .html file + if os.path.exists(os.environ.get('OPENRAM_HOME')+"/datasheet/datasheets"): + datasheets = glob.glob('{0}/{1}/*html'.format(OPENRAM_HOME,'datasheet/datasheets')) self.assertTrue(len(datasheets)>0) # grep any errors from the output From 35e0ba6fc429448d859e3709439771a42d43e19b Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 11 Oct 2018 16:03:05 -0700 Subject: [PATCH 07/10] fixed merge error --- .../datasheet/characterization_corners.py | 17 +++ compiler/datasheet/datasheet.py | 55 ++++++++ compiler/datasheet/datasheet_gen.py | 123 ++++++++++++++++++ .../datasheets/sram_2_16_1_scn4m_subm.html | 51 ++++++++ compiler/datasheet/deliverables.py | 13 ++ compiler/datasheet/operating_conditions.py | 17 +++ compiler/datasheet/timing_and_current_data.py | 16 +++ 7 files changed, 292 insertions(+) create mode 100644 compiler/datasheet/characterization_corners.py create mode 100644 compiler/datasheet/datasheet.py create mode 100644 compiler/datasheet/datasheet_gen.py create mode 100644 compiler/datasheet/datasheets/sram_2_16_1_scn4m_subm.html create mode 100644 compiler/datasheet/deliverables.py create mode 100644 compiler/datasheet/operating_conditions.py create mode 100644 compiler/datasheet/timing_and_current_data.py diff --git a/compiler/datasheet/characterization_corners.py b/compiler/datasheet/characterization_corners.py new file mode 100644 index 00000000..54f75c3f --- /dev/null +++ b/compiler/datasheet/characterization_corners.py @@ -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 + diff --git a/compiler/datasheet/datasheet.py b/compiler/datasheet/datasheet.py new file mode 100644 index 00000000..396215a8 --- /dev/null +++ b/compiler/datasheet/datasheet.py @@ -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 = """""" + self.html +='

{0}

' + self.html +='

{0}

' + self.html +='

{0}

' + self.html +='

Operating Conditions

' + self.html += operating_conditions(self.operating,table_id='data').__html__() + self.html += '

Timing and Current Data

' + self.html += timing_and_current_data(self.timing,table_id='data').__html__() + self.html += '

Characterization Corners

' + self.html += characterization_corners(self.corners,table_id='data').__html__() + self.html +='

Deliverables

' + self.html += deliverables(self.dlv,table_id='data').__html__().replace('<','<').replace('"','"').replace('>',">") + + diff --git a/compiler/datasheet/datasheet_gen.py b/compiler/datasheet/datasheet_gen.py new file mode 100644 index 00000000..f15223bd --- /dev/null +++ b/compiler/datasheet/datasheet_gen.py @@ -0,0 +1,123 @@ +#!/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)': + if float(math.floor(1000/float(MIN_PERIOD)) < float(item.max)): + item.max = str(math.floor(1000/float(MIN_PERIOD))) + + + + 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','{1}'.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')) + new_sheet.operating.append(operating_conditions_item('Operating Frequency (F)','','',str(math.floor(1000/float(MIN_PERIOD))),'MHz')) + + new_sheet.timing.append(timing_and_current_data_item('1','2','3','4')) + + new_sheet.dlv.append(deliverables_item('.sp','SPICE netlists','{1}.{2}'.format(OUT_DIR,NAME,'sp'))) + new_sheet.dlv.append(deliverables_item('.v','Verilog simulation models','{1}.{2}'.format(OUT_DIR,NAME,'v'))) + new_sheet.dlv.append(deliverables_item('.gds','GDSII layout views','{1}.{2}'.format(OUT_DIR,NAME,'gds'))) + new_sheet.dlv.append(deliverables_item('.lef','LEF files','{1}.{2}'.format(OUT_DIR,NAME,'lef'))) + new_sheet.dlv.append(deliverables_item('.lib','Synthesis models','{1}'.format(LIB_NAME,LIB_NAME.replace(OUT_DIR,'')))) + + + +class parse(): + def __init__(self,in_dir,out_dir): + + 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: +# print (out_dir + sheets.name + ".html") + with open(out_dir + "/" + sheets.name + ".html", 'w+') as f: + sheets.generate_html() + f.write(sheets.html) diff --git a/compiler/datasheet/datasheets/sram_2_16_1_scn4m_subm.html b/compiler/datasheet/datasheets/sram_2_16_1_scn4m_subm.html new file mode 100644 index 00000000..ebe538eb --- /dev/null +++ b/compiler/datasheet/datasheets/sram_2_16_1_scn4m_subm.html @@ -0,0 +1,51 @@ +

{0}

{0}

{0}

Operating Conditions

+ + + + + + +
ParameterMinTypMaxUnits
Power supply (VDD) range252525Volts
Operating Temperature5.05.05.0Celsius
Operating Frequency (F)213MHz

Timing and Current Data

+ + + + +
ParameterMinMaxUnits
1234

Characterization Corners

+ + + + +
Corner NameProcessPower SupplyTemperatureLibrary Name Suffix
TTTypical - Typical255.0_TT_5p0V_25C.lib

Deliverables

+ + + + + + + + +
TypeDescriptionLink
.spSPICE netlistssram_2_16_1_scn4m_subm.sp
.vVerilog simulation modelssram_2_16_1_scn4m_subm.v
.gdsGDSII layout viewssram_2_16_1_scn4m_subm.gds
.lefLEF filessram_2_16_1_scn4m_subm.lef
.libSynthesis modelssram_2_16_1_scn4m_subm_TT_5p0V_25C.lib
\ No newline at end of file diff --git a/compiler/datasheet/deliverables.py b/compiler/datasheet/deliverables.py new file mode 100644 index 00000000..d5287c3a --- /dev/null +++ b/compiler/datasheet/deliverables.py @@ -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 diff --git a/compiler/datasheet/operating_conditions.py b/compiler/datasheet/operating_conditions.py new file mode 100644 index 00000000..e08adc61 --- /dev/null +++ b/compiler/datasheet/operating_conditions.py @@ -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 + diff --git a/compiler/datasheet/timing_and_current_data.py b/compiler/datasheet/timing_and_current_data.py new file mode 100644 index 00000000..ebf489e8 --- /dev/null +++ b/compiler/datasheet/timing_and_current_data.py @@ -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 + + From 50cc8023a4a0823a627850e4a44c5841d1129bd7 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Thu, 11 Oct 2018 16:04:43 -0700 Subject: [PATCH 08/10] deleted output file left in previous commit --- .../datasheets/sram_2_16_1_scn4m_subm.html | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 compiler/datasheet/datasheets/sram_2_16_1_scn4m_subm.html diff --git a/compiler/datasheet/datasheets/sram_2_16_1_scn4m_subm.html b/compiler/datasheet/datasheets/sram_2_16_1_scn4m_subm.html deleted file mode 100644 index ebe538eb..00000000 --- a/compiler/datasheet/datasheets/sram_2_16_1_scn4m_subm.html +++ /dev/null @@ -1,51 +0,0 @@ -

{0}

{0}

{0}

Operating Conditions

- - - - - - -
ParameterMinTypMaxUnits
Power supply (VDD) range252525Volts
Operating Temperature5.05.05.0Celsius
Operating Frequency (F)213MHz

Timing and Current Data

- - - - -
ParameterMinMaxUnits
1234

Characterization Corners

- - - - -
Corner NameProcessPower SupplyTemperatureLibrary Name Suffix
TTTypical - Typical255.0_TT_5p0V_25C.lib

Deliverables

- - - - - - - - -
TypeDescriptionLink
.spSPICE netlistssram_2_16_1_scn4m_subm.sp
.vVerilog simulation modelssram_2_16_1_scn4m_subm.v
.gdsGDSII layout viewssram_2_16_1_scn4m_subm.gds
.lefLEF filessram_2_16_1_scn4m_subm.lef
.libSynthesis modelssram_2_16_1_scn4m_subm_TT_5p0V_25C.lib
\ No newline at end of file From 5e9fe65907f1f04b937f981214433ce7dfd05a33 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 12 Oct 2018 10:23:34 -0700 Subject: [PATCH 09/10] Remove banks from example configs --- compiler/example_config_freepdk45.py | 2 +- compiler/example_config_scn4m_subm.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/example_config_freepdk45.py b/compiler/example_config_freepdk45.py index c550211c..2d820e9a 100644 --- a/compiler/example_config_freepdk45.py +++ b/compiler/example_config_freepdk45.py @@ -7,7 +7,7 @@ supply_voltages = [1.0] temperatures = [25] 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) #Below are some additions to test additional ports on sram #bitcell = "pbitcell" diff --git a/compiler/example_config_scn4m_subm.py b/compiler/example_config_scn4m_subm.py index 5c7d555f..68307927 100644 --- a/compiler/example_config_scn4m_subm.py +++ b/compiler/example_config_scn4m_subm.py @@ -7,4 +7,4 @@ supply_voltages = [ 5.0 ] temperatures = [ 25 ] 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) From afba54a22db9b830458119789056d480e35ff767 Mon Sep 17 00:00:00 2001 From: Jesse Cirimelli-Low Date: Fri, 12 Oct 2018 13:22:12 -0700 Subject: [PATCH 10/10] added analytical model support, added proper output with sram.py --- compiler/characterizer/lib.py | 2 +- compiler/datasheet/datasheet_gen.py | 28 +++++++++++++++++++--------- compiler/openram.py | 8 +------- compiler/sram.py | 10 +++++++++- compiler/tests/30_openram_test.py | 4 ++-- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 54558b01..8ce0193b 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -527,7 +527,7 @@ class lib: return datasheet = open(OPTS.openram_temp +'/datasheet.info', 'a+') - for (self.corner,lib_name) in zip(self.corners,self.lib_files): + for (corner, lib_name) in zip(self.corners, self.lib_files): ports = "" if OPTS.num_rw_ports>0: diff --git a/compiler/datasheet/datasheet_gen.py b/compiler/datasheet/datasheet_gen.py index f15223bd..6bfb165f 100644 --- a/compiler/datasheet/datasheet_gen.py +++ b/compiler/datasheet/datasheet_gen.py @@ -75,8 +75,11 @@ def parse_file(f,pages): item.min = VOLT if item.parameter == 'Operating Frequncy (F)': - if float(math.floor(1000/float(MIN_PERIOD)) < float(item.max)): - item.max = str(math.floor(1000/float(MIN_PERIOD))) + try: + if float(math.floor(1000/float(MIN_PERIOD)) < float(item.max)): + item.max = str(math.floor(1000/float(MIN_PERIOD))) + except Exception: + pass @@ -91,7 +94,13 @@ 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')) - new_sheet.operating.append(operating_conditions_item('Operating Frequency (F)','','',str(math.floor(1000/float(MIN_PERIOD))),'MHz')) + 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')) @@ -103,21 +112,22 @@ def parse_file(f,pages): -class parse(): - def __init__(self,in_dir,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) + #if not (os.path.isdir(out_dir)): + # os.mkdir(out_dir) datasheets = [] parse_file(in_dir + "/datasheet.info", datasheets) for sheets in datasheets: -# print (out_dir + sheets.name + ".html") - with open(out_dir + "/" + sheets.name + ".html", 'w+') as f: + with open(name, 'w+') as f: sheets.generate_html() f.write(sheets.html) diff --git a/compiler/openram.py b/compiler/openram.py index a588f806..6fc6ec71 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -40,7 +40,7 @@ import verify from sram import sram 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: output_extensions.extend(["gds","lef"]) output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in output_extensions] @@ -62,12 +62,6 @@ s = sram(sram_config=c, # Output the files for the resulting SRAM s.save() -# generate datasheet from characterization of created SRAM -if not OPTS.analytical_delay: - import datasheet_gen - p = datasheet_gen.parse(OPTS.openram_temp,os.environ.get('OPENRAM_HOME')+"/datasheet/datasheets") - - # Delete temp files etc. end_openram() print_time("End",datetime.datetime.now(), start_time) diff --git a/compiler/sram.py b/compiler/sram.py index 0feea1b3..59b7d7e8 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -57,7 +57,7 @@ class sram(): def verilog_write(self,name): self.s.verilog_write(name) - + def save(self): """ Save all the output files while reporting time to do it as well. """ @@ -107,6 +107,14 @@ class sram(): print("LEF: Writing to {0}".format(lefname)) self.s.lef_write(lefname) 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 start_time = datetime.datetime.now() diff --git a/compiler/tests/30_openram_test.py b/compiler/tests/30_openram_test.py index d53182fc..038a2e15 100755 --- a/compiler/tests/30_openram_test.py +++ b/compiler/tests/30_openram_test.py @@ -64,8 +64,8 @@ class openram_test(openram_test): self.assertTrue(len(files)>0) # Make sure there is any .html file - if os.path.exists(os.environ.get('OPENRAM_HOME')+"/datasheet/datasheets"): - datasheets = glob.glob('{0}/{1}/*html'.format(OPENRAM_HOME,'datasheet/datasheets')) + 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