From dd2effd28d8bfebc0a9c3ed3adb9a01b0cba28cc Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Feb 2022 10:54:39 -0800 Subject: [PATCH 01/17] Initial work on separate delay and func simulation --- compiler/base/design.py | 1 - compiler/gen_stimulus.py | 40 +++++++++----------- compiler/openram.py | 11 +++--- compiler/sram/sram.py | 72 +++++++++++++++++++----------------- compiler/sram/sram_base.py | 14 ++----- compiler/sram/sram_config.py | 53 +++++++++++++++++++++++++- 6 files changed, 116 insertions(+), 75 deletions(-) diff --git a/compiler/base/design.py b/compiler/base/design.py index 70a9047b..12fd6c56 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -318,4 +318,3 @@ class design(hierarchy_design): design.setup_drc_constants() design.setup_layer_constants() - diff --git a/compiler/gen_stimulus.py b/compiler/gen_stimulus.py index 69e53cd8..d69d867d 100755 --- a/compiler/gen_stimulus.py +++ b/compiler/gen_stimulus.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # See LICENSE for licensing information. # # Copyright (c) 2016-2021 Regents of the University of California and The Board @@ -15,6 +15,7 @@ corner, but should probably be extended. import sys from globals import * +from importlib import reload (OPTS, args) = parse_args() @@ -41,36 +42,30 @@ slew = float(args[3]) import debug init_openram(config_file=config_file, is_unit_test=False) + +from sram_config import sram_config +c = sram_config(word_size=OPTS.word_size, + num_words=OPTS.num_words, + write_size=OPTS.write_size, + num_banks=OPTS.num_banks, + words_per_row=OPTS.words_per_row, + num_spare_rows=OPTS.num_spare_rows, + num_spare_cols=OPTS.num_spare_cols) + +OPTS.netlist_only = True OPTS.check_lvsdrc = False # Put the temp output in the output path since it is what we want to generate! old_openram_temp = OPTS.openram_temp OPTS.openram_temp = OPTS.output_path - - -import sram -class fake_sram(sram.sram): - """ This is an SRAM that doesn't actually create itself, just computes - the sizes. """ - def __init__(self, word_size, num_words, num_banks, name, num_spare_rows): - self.name = name - self.word_size = word_size - self.num_words = num_words - self.num_banks = num_banks - self.num_spare_rows = num_spare_rows - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell = self.mod_bitcell() - # to get the row, col, etc. - self.compute_sizes() - -sram = fake_sram(OPTS.word_size, OPTS.num_words, OPTS.num_banks, OPTS.output_name) -sp_file = OPTS.output_path+OPTS.output_name + ".sp" +from sram import sram +s = sram(name=OPTS.output_name, sram_config=c) +s.create() from characterizer import delay import tech # Set up the delay and set to the nominal corner -d = delay.delay(sram, sp_file, ("TT", tech.spice["nom_supply_voltage"], tech.spice["nom_temperature"])) +d = delay(s, s.get_sp_name(), ("TT", tech.spice["nom_supply_voltage"], tech.spice["nom_temperature"])) # Set the period d.period = period # Set the load of outputs and slew of inputs @@ -91,4 +86,3 @@ print("Output files are:\n{0}stim.sp\n{0}sram.sp\n{0}reduced.sp".format(OPTS.out OPTS.openram_temp = old_openram_temp # Delete temp files, remove the dir, etc. end_openram() - diff --git a/compiler/openram.py b/compiler/openram.py index 089c0f3a..74f80128 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -48,7 +48,7 @@ g.print_time("Start", start_time) g.report_status() from sram_config import sram_config - +from sram import sram # Configure the SRAM organization c = sram_config(word_size=OPTS.word_size, @@ -73,9 +73,10 @@ for path in output_files: debug.print_raw(path) -from sram import sram -s = sram(sram_config=c, - name=OPTS.output_name) +s = sram(name=OPTS.output_name, sram_config=c) + +# Actually build the SRAM +s.create() # Output the files for the resulting SRAM s.save() @@ -83,5 +84,3 @@ s.save() # Delete temp files etc. g.end_openram() g.print_time("End", datetime.datetime.now(), start_time) - - diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 90855d8e..afd57f48 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -21,30 +21,39 @@ class sram(): results. We can later add visualizer and other high-level functions as needed. """ - def __init__(self, sram_config, name): + def __init__(self, name, sram_config): + self.name = name + self.config = sram_config sram_config.set_local_config(self) + self.sp_name = OPTS.output_path + self.name + ".sp" + self.lvs_name = OPTS.output_path + self.name + ".lvs.sp" + self.pex_name = OPTS.output_path + self.name + ".pex.sp" + self.gds_name = OPTS.output_path + self.name + ".gds" + self.lef_name = OPTS.output_path + self.name + ".lef" + self.v_name = OPTS.output_path + self.name + ".v" + # reset the static duplicate name checker for unit tests # in case we create more than one SRAM from design import design design.name_map=[] + def create(self): debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size, self.num_words, self.num_banks)) start_time = datetime.datetime.now() - self.name = name - if self.num_banks == 1: - from sram_1bank import sram_1bank as sram + from sram_1bank import sram_1bank as sram_banked elif self.num_banks == 2: - from sram_2bank import sram_2bank as sram + from sram_2bank import sram_2bank as sram_banked else: debug.error("Invalid number of banks.", -1) - self.s = sram(name, sram_config) + self.s = sram_banked(name=self.name, + sram_config=self.config) self.s.create_netlist() if not OPTS.netlist_only: self.s.create_layout() @@ -52,6 +61,14 @@ class sram(): if not OPTS.is_unit_test: print_time("SRAM creation", datetime.datetime.now(), start_time) + def get_sp_name(self): + if OPTS.use_pex: + # Use the extracted spice file + return self.pex_name + else: + # Use generated spice file for characterization + return self.sp_name + def sp_write(self, name, lvs=False, trim=False): self.s.sp_write(name, lvs, trim) @@ -101,11 +118,10 @@ class sram(): # Save the spice file start_time = datetime.datetime.now() - spname = OPTS.output_path + self.s.name + ".sp" - debug.print_raw("SP: Writing to {0}".format(spname)) - self.sp_write(spname) + debug.print_raw("SP: Writing to {0}".format(self.sp_name)) + self.sp_write(self.sp_name) functional(self.s, - os.path.basename(spname), + os.path.basename(self.sp_name), cycles=200, output_path=OPTS.output_path) print_time("Spice writing", datetime.datetime.now(), start_time) @@ -113,12 +129,11 @@ class sram(): if not OPTS.netlist_only: # Write the layout start_time = datetime.datetime.now() - gdsname = OPTS.output_path + self.s.name + ".gds" - debug.print_raw("GDS: Writing to {0}".format(gdsname)) - self.gds_write(gdsname) + debug.print_raw("GDS: Writing to {0}".format(self.gds_name)) + self.gds_write(self.gds_name) if OPTS.check_lvsdrc: verify.write_drc_script(cell_name=self.s.name, - gds_name=os.path.basename(gdsname), + gds_name=os.path.basename(self.gds_name), extract=True, final_verification=True, output_path=OPTS.output_path) @@ -126,20 +141,18 @@ class sram(): # Create a LEF physical model start_time = datetime.datetime.now() - lefname = OPTS.output_path + self.s.name + ".lef" - debug.print_raw("LEF: Writing to {0}".format(lefname)) - self.lef_write(lefname) + debug.print_raw("LEF: Writing to {0}".format(self.lef_name)) + self.lef_write(self.lef_name) print_time("LEF", datetime.datetime.now(), start_time) # Save the LVS file start_time = datetime.datetime.now() - lvsname = OPTS.output_path + self.s.name + ".lvs.sp" - debug.print_raw("LVS: Writing to {0}".format(lvsname)) - self.sp_write(lvsname, lvs=True) + debug.print_raw("LVS: Writing to {0}".format(self.lvs_name)) + self.sp_write(self.lvs_name, lvs=True) if not OPTS.netlist_only and OPTS.check_lvsdrc: verify.write_lvs_script(cell_name=self.s.name, - gds_name=os.path.basename(gdsname), - sp_name=os.path.basename(lvsname), + gds_name=os.path.basename(self.gds_name), + sp_name=os.path.basename(self.lvs_name), final_verification=True, output_path=OPTS.output_path) print_time("LVS writing", datetime.datetime.now(), start_time) @@ -148,14 +161,8 @@ class sram(): if OPTS.use_pex: start_time = datetime.datetime.now() # Output the extracted design if requested - pexname = OPTS.output_path + self.s.name + ".pex.sp" - spname = OPTS.output_path + self.s.name + ".sp" - verify.run_pex(self.s.name, gdsname, spname, output=pexname) - sp_file = pexname + verify.run_pex(self.s.name, self.gds_name, self.sp_name, output=self.pex_name) print_time("Extraction", datetime.datetime.now(), start_time) - else: - # Use generated spice file for characterization - sp_file = spname # Save a functional simulation file @@ -163,7 +170,7 @@ class sram(): start_time = datetime.datetime.now() from characterizer import lib debug.print_raw("LIB: Characterizing... ") - lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file) + lib(out_dir=OPTS.output_path, sram=self.s, sp_file=self.get_sp_name()) print_time("Characterization", datetime.datetime.now(), start_time) # Write the config file @@ -183,9 +190,8 @@ class sram(): # Write a verilog model start_time = datetime.datetime.now() - vname = OPTS.output_path + self.s.name + ".v" - debug.print_raw("Verilog: Writing to {0}".format(vname)) - self.verilog_write(vname) + debug.print_raw("Verilog: Writing to {0}".format(self.v_name)) + self.verilog_write(self.v_name) print_time("Verilog", datetime.datetime.now(), start_time) # Write out options if specified diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 72c5e3fe..6ecc3a5a 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -7,7 +7,7 @@ # import datetime import debug -from math import log, ceil +from math import log from importlib import reload from vector import vector from globals import OPTS, print_time @@ -20,8 +20,7 @@ from tech import spice class sram_base(design, verilog, lef): """ - Dynamically generated SRAM by connecting banks to control logic. The - number of banks should be 1 , 2 or 4 + Dynamically generated SRAM by connecting banks to control logic. """ def __init__(self, name, sram_config): design.__init__(self, name) @@ -30,17 +29,10 @@ class sram_base(design, verilog, lef): self.sram_config = sram_config sram_config.set_local_config(self) + sram_config.compute_sizes() self.bank_insts = [] - if self.write_size: - self.num_wmasks = int(ceil(self.word_size / self.write_size)) - else: - self.num_wmasks = 0 - - if not self.num_spare_cols: - self.num_spare_cols = 0 - try: from tech import power_grid self.supply_stack = power_grid diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index 4c1bb117..a65387fb 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -14,7 +14,8 @@ from sram_factory import factory class sram_config: """ This is a structure that is used to hold the SRAM configuration options. """ - def __init__(self, word_size, num_words, write_size=None, num_banks=1, words_per_row=None, num_spare_rows=0, num_spare_cols=0): + def __init__(self, word_size, num_words, write_size=None, num_banks=1, + words_per_row=None, num_spare_rows=0, num_spare_cols=0): self.word_size = word_size self.num_words = num_words # Don't add a write mask if it is the same size as the data word @@ -37,6 +38,16 @@ class sram_config: except ImportError: self.array_col_multiple = 1 + if self.write_size: + self.num_wmasks = int(ceil(self.word_size / self.write_size)) + else: + self.num_wmasks = 0 + + if not self.num_spare_cols: + self.num_spare_cols = 0 + + if not self.num_spare_rows: + self.num_spare_rows = 0 # This will get over-written when we determine the organization self.words_per_row = words_per_row @@ -167,3 +178,43 @@ class sram_config: return int(words_per_row * tentative_num_rows / 16) return words_per_row + + def setup_multiport_constants(self): + """ + These are contants and lists that aid multiport design. + Ports are always in the order RW, W, R. + Port indices start from 0 and increment. + A first RW port will have clk0, csb0, web0, addr0, data0 + A first W port (with no RW ports) will be: clk0, csb0, addr0, data0 + + """ + total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports + + # These are the read/write port indices. + self.readwrite_ports = [] + # These are the read/write and write-only port indices + self.write_ports = [] + # These are the write-only port indices. + self.writeonly_ports = [] + # These are the read/write and read-only port indices + self.read_ports = [] + # These are the read-only port indices. + self.readonly_ports = [] + # These are all the ports + self.all_ports = list(range(total_ports)) + + # The order is always fixed as RW, W, R + port_number = 0 + for port in range(OPTS.num_rw_ports): + self.readwrite_ports.append(port_number) + self.write_ports.append(port_number) + self.read_ports.append(port_number) + port_number += 1 + for port in range(OPTS.num_w_ports): + self.write_ports.append(port_number) + self.writeonly_ports.append(port_number) + port_number += 1 + for port in range(OPTS.num_r_ports): + self.read_ports.append(port_number) + self.readonly_ports.append(port_number) + port_number += 1 From a87b2e9446caae0bb367643c850c43d1ad4e38d2 Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Tue, 1 Mar 2022 15:12:03 -0800 Subject: [PATCH 02/17] Adding characterizer executable --- compiler/characterize_existing.py | 64 +++++++++++++++++++++++++++++++ compiler/sram/sram.py | 5 ++- 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100755 compiler/characterize_existing.py diff --git a/compiler/characterize_existing.py b/compiler/characterize_existing.py new file mode 100755 index 00000000..82e95b1b --- /dev/null +++ b/compiler/characterize_existing.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +""" +This script will characterize an SRAM previously generated by OpenRAM given a +configuration file. Configuration option "use_pex" determines whether extracted +or generated spice is used and option "analytical_delay" determines whether +an analytical model or spice simulation is used for characterization. +""" + +import sys +import datetime +from globals import * +from importlib import reload + +(OPTS, args) = parse_args() + +# Override the usage +USAGE = "Usage: {} [options] \nUse -h for help.\n".format(__file__) + +# Check that we are left with a single configuration file as argument. +if len(args) != 1: + print(USAGE) + sys.exit(2) + +# These depend on arguments, so don't load them until now. +import debug + +# Parse config file and set up all the options +init_openram(config_file=args[0], is_unit_test=False) + +# Configure the SRAM organization (duplicated from openram.py) +from sram_config import sram_config +c = sram_config(word_size=OPTS.word_size, + num_words=OPTS.num_words, + write_size=OPTS.write_size, + num_banks=OPTS.num_banks, + words_per_row=OPTS.words_per_row, + num_spare_rows=OPTS.num_spare_rows, + num_spare_cols=OPTS.num_spare_cols) + +# Initialize and create the sram object +from sram import sram +s = sram(name=OPTS.output_name, sram_config=c) +s.create() + +# Characterize the design +start_time = datetime.datetime.now() +from characterizer import lib +debug.print_raw("LIB: Characterizing... ") +lib(out_dir=OPTS.output_path, sram=s.s, sp_file=s.get_sp_name()) +print_time("Characterization", datetime.datetime.now(), start_time) + +# Output info about this run +print("Output file is:\n{0}.lib".format(OPTS.output_path)) +#report_status() #could modify this function to provide relevant info + +# Delete temp files, remove the dir, etc. +end_openram() diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index afd57f48..fdc8ec76 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -25,6 +25,7 @@ class sram(): self.name = name self.config = sram_config + sram_config.setup_multiport_constants() sram_config.set_local_config(self) self.sp_name = OPTS.output_path + self.name + ".sp" @@ -120,6 +121,8 @@ class sram(): start_time = datetime.datetime.now() debug.print_raw("SP: Writing to {0}".format(self.sp_name)) self.sp_write(self.sp_name) + + # Save a functional simulation file with default period functional(self.s, os.path.basename(self.sp_name), cycles=200, @@ -164,8 +167,6 @@ class sram(): verify.run_pex(self.s.name, self.gds_name, self.sp_name, output=self.pex_name) print_time("Extraction", datetime.datetime.now(), start_time) - # Save a functional simulation file - # Characterize the design start_time = datetime.datetime.now() from characterizer import lib From d47b5612d515082d683b99c1500dea4f57f2e168 Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Wed, 2 Mar 2022 12:18:54 -0800 Subject: [PATCH 03/17] characterizer and functional simulator working from command line --- compiler/characterize_existing.py | 2 + compiler/characterizer/functional.py | 7 +-- compiler/func_sim_existing.py | 70 ++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) create mode 100755 compiler/func_sim_existing.py diff --git a/compiler/characterize_existing.py b/compiler/characterize_existing.py index 82e95b1b..f6c83c4b 100755 --- a/compiler/characterize_existing.py +++ b/compiler/characterize_existing.py @@ -34,6 +34,8 @@ import debug # Parse config file and set up all the options init_openram(config_file=args[0], is_unit_test=False) +print_banner() + # Configure the SRAM organization (duplicated from openram.py) from sram_config import sram_config c = sram_config(word_size=OPTS.word_size, diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index e7d03f25..3bc94ab7 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -36,9 +36,6 @@ class functional(simulation): if not corner: corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - if period: - self.period = period - if not output_path: self.output_path = OPTS.openram_temp else: @@ -73,6 +70,10 @@ class functional(simulation): self.set_spice_constants() self.set_stimulus_variables() + # Override default period + if period: + self.period = period + # For the debug signal names self.wordline_row = 0 self.bitline_column = 0 diff --git a/compiler/func_sim_existing.py b/compiler/func_sim_existing.py new file mode 100755 index 00000000..ec89c3d7 --- /dev/null +++ b/compiler/func_sim_existing.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +""" +This script will characterize an SRAM previously generated by OpenRAM given a +configuration file. Configuration option "use_pex" determines whether extracted +or generated spice is used and option "analytical_delay" determines whether +an analytical model or spice simulation is used for characterization. +""" + +import sys +import datetime +from globals import * +from importlib import reload + +(OPTS, args) = parse_args() + +# Override the usage +USAGE = "Usage: {} [options] \nUse -h for help.\n".format(__file__) + +# Check that we are left with a single configuration file as argument. +if len(args) != 3: + print(USAGE) + sys.exit(2) + +# Parse argument +config_file = args[0] +cycles = int(args[1]) +period = float(args[2]) + +# These depend on arguments, so don't load them until now. +import debug + +# Parse config file and set up all the options +init_openram(config_file=config_file, is_unit_test=False) + +print_banner() + +# Configure the SRAM organization (duplicated from openram.py) +from sram_config import sram_config +c = sram_config(word_size=OPTS.word_size, + num_words=OPTS.num_words, + write_size=OPTS.write_size, + num_banks=OPTS.num_banks, + words_per_row=OPTS.words_per_row, + num_spare_rows=OPTS.num_spare_rows, + num_spare_cols=OPTS.num_spare_cols) + +# Initialize and create the sram object +from sram import sram +s = sram(name=OPTS.output_name, sram_config=c) +s.create() + +# Generate stimulus and run functional simulation on the design +start_time = datetime.datetime.now() +from characterizer import functional +debug.print_raw("Functional simulation... ") +f = functional(s.s, cycles=cycles, spfile=s.get_sp_name(), period=period, output_path=OPTS.openram_temp) +(fail, error) = f.run() +print_time("Functional simulation", datetime.datetime.now(), start_time) +print(error) + +# Delete temp files, remove the dir, etc. after success +if fail: + end_openram() From 910980328c3292029b1a94bf57c2471979925fe3 Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Wed, 2 Mar 2022 12:47:05 -0800 Subject: [PATCH 04/17] clarify file location message for user --- compiler/characterize_existing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/characterize_existing.py b/compiler/characterize_existing.py index f6c83c4b..c8456b9f 100755 --- a/compiler/characterize_existing.py +++ b/compiler/characterize_existing.py @@ -59,7 +59,7 @@ lib(out_dir=OPTS.output_path, sram=s.s, sp_file=s.get_sp_name()) print_time("Characterization", datetime.datetime.now(), start_time) # Output info about this run -print("Output file is:\n{0}.lib".format(OPTS.output_path)) +print("Output files are:\n{0}*.lib".format(OPTS.output_path)) #report_status() #could modify this function to provide relevant info # Delete temp files, remove the dir, etc. From 1acc6be3ce12f483c9467393c2f310d687ff84fb Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Wed, 2 Mar 2022 16:38:39 -0800 Subject: [PATCH 05/17] call create() function from sram/__init__ --- compiler/characterize_existing.py | 1 - compiler/func_sim_existing.py | 1 - compiler/gen_stimulus.py | 1 - compiler/openram.py | 7 ++----- compiler/sram/sram.py | 2 ++ 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/characterize_existing.py b/compiler/characterize_existing.py index c8456b9f..73a7c235 100755 --- a/compiler/characterize_existing.py +++ b/compiler/characterize_existing.py @@ -49,7 +49,6 @@ c = sram_config(word_size=OPTS.word_size, # Initialize and create the sram object from sram import sram s = sram(name=OPTS.output_name, sram_config=c) -s.create() # Characterize the design start_time = datetime.datetime.now() diff --git a/compiler/func_sim_existing.py b/compiler/func_sim_existing.py index ec89c3d7..0d693e91 100755 --- a/compiler/func_sim_existing.py +++ b/compiler/func_sim_existing.py @@ -54,7 +54,6 @@ c = sram_config(word_size=OPTS.word_size, # Initialize and create the sram object from sram import sram s = sram(name=OPTS.output_name, sram_config=c) -s.create() # Generate stimulus and run functional simulation on the design start_time = datetime.datetime.now() diff --git a/compiler/gen_stimulus.py b/compiler/gen_stimulus.py index d69d867d..c9b4e350 100755 --- a/compiler/gen_stimulus.py +++ b/compiler/gen_stimulus.py @@ -60,7 +60,6 @@ OPTS.openram_temp = OPTS.output_path from sram import sram s = sram(name=OPTS.output_name, sram_config=c) -s.create() from characterizer import delay import tech diff --git a/compiler/openram.py b/compiler/openram.py index 74f80128..240a3f9f 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -72,13 +72,10 @@ debug.print_raw("Output files are: ") for path in output_files: debug.print_raw(path) - +# Initialize the SRAM s = sram(name=OPTS.output_name, sram_config=c) -# Actually build the SRAM -s.create() - -# Output the files for the resulting SRAM +# Generate and output the files for the resulting SRAM s.save() # Delete temp files etc. diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index fdc8ec76..afa8cd58 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -40,6 +40,8 @@ class sram(): from design import design design.name_map=[] + self.create() + def create(self): debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size, self.num_words, From 353ff7859c0835d7acb7cc9d0b5830986f4c8b36 Mon Sep 17 00:00:00 2001 From: mrg Date: Wed, 16 Feb 2022 10:54:39 -0800 Subject: [PATCH 06/17] Initial work on separate delay and func simulation --- compiler/base/design.py | 1 - compiler/gen_stimulus.py | 40 +++++++++----------- compiler/openram.py | 11 +++--- compiler/sram/sram.py | 72 +++++++++++++++++++----------------- compiler/sram/sram_base.py | 14 ++----- compiler/sram/sram_config.py | 53 +++++++++++++++++++++++++- 6 files changed, 116 insertions(+), 75 deletions(-) diff --git a/compiler/base/design.py b/compiler/base/design.py index 70a9047b..12fd6c56 100644 --- a/compiler/base/design.py +++ b/compiler/base/design.py @@ -318,4 +318,3 @@ class design(hierarchy_design): design.setup_drc_constants() design.setup_layer_constants() - diff --git a/compiler/gen_stimulus.py b/compiler/gen_stimulus.py index 69e53cd8..d69d867d 100755 --- a/compiler/gen_stimulus.py +++ b/compiler/gen_stimulus.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # See LICENSE for licensing information. # # Copyright (c) 2016-2021 Regents of the University of California and The Board @@ -15,6 +15,7 @@ corner, but should probably be extended. import sys from globals import * +from importlib import reload (OPTS, args) = parse_args() @@ -41,36 +42,30 @@ slew = float(args[3]) import debug init_openram(config_file=config_file, is_unit_test=False) + +from sram_config import sram_config +c = sram_config(word_size=OPTS.word_size, + num_words=OPTS.num_words, + write_size=OPTS.write_size, + num_banks=OPTS.num_banks, + words_per_row=OPTS.words_per_row, + num_spare_rows=OPTS.num_spare_rows, + num_spare_cols=OPTS.num_spare_cols) + +OPTS.netlist_only = True OPTS.check_lvsdrc = False # Put the temp output in the output path since it is what we want to generate! old_openram_temp = OPTS.openram_temp OPTS.openram_temp = OPTS.output_path - - -import sram -class fake_sram(sram.sram): - """ This is an SRAM that doesn't actually create itself, just computes - the sizes. """ - def __init__(self, word_size, num_words, num_banks, name, num_spare_rows): - self.name = name - self.word_size = word_size - self.num_words = num_words - self.num_banks = num_banks - self.num_spare_rows = num_spare_rows - c = reload(__import__(OPTS.bitcell)) - self.mod_bitcell = getattr(c, OPTS.bitcell) - self.bitcell = self.mod_bitcell() - # to get the row, col, etc. - self.compute_sizes() - -sram = fake_sram(OPTS.word_size, OPTS.num_words, OPTS.num_banks, OPTS.output_name) -sp_file = OPTS.output_path+OPTS.output_name + ".sp" +from sram import sram +s = sram(name=OPTS.output_name, sram_config=c) +s.create() from characterizer import delay import tech # Set up the delay and set to the nominal corner -d = delay.delay(sram, sp_file, ("TT", tech.spice["nom_supply_voltage"], tech.spice["nom_temperature"])) +d = delay(s, s.get_sp_name(), ("TT", tech.spice["nom_supply_voltage"], tech.spice["nom_temperature"])) # Set the period d.period = period # Set the load of outputs and slew of inputs @@ -91,4 +86,3 @@ print("Output files are:\n{0}stim.sp\n{0}sram.sp\n{0}reduced.sp".format(OPTS.out OPTS.openram_temp = old_openram_temp # Delete temp files, remove the dir, etc. end_openram() - diff --git a/compiler/openram.py b/compiler/openram.py index 089c0f3a..74f80128 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -48,7 +48,7 @@ g.print_time("Start", start_time) g.report_status() from sram_config import sram_config - +from sram import sram # Configure the SRAM organization c = sram_config(word_size=OPTS.word_size, @@ -73,9 +73,10 @@ for path in output_files: debug.print_raw(path) -from sram import sram -s = sram(sram_config=c, - name=OPTS.output_name) +s = sram(name=OPTS.output_name, sram_config=c) + +# Actually build the SRAM +s.create() # Output the files for the resulting SRAM s.save() @@ -83,5 +84,3 @@ s.save() # Delete temp files etc. g.end_openram() g.print_time("End", datetime.datetime.now(), start_time) - - diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 90855d8e..afd57f48 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -21,30 +21,39 @@ class sram(): results. We can later add visualizer and other high-level functions as needed. """ - def __init__(self, sram_config, name): + def __init__(self, name, sram_config): + self.name = name + self.config = sram_config sram_config.set_local_config(self) + self.sp_name = OPTS.output_path + self.name + ".sp" + self.lvs_name = OPTS.output_path + self.name + ".lvs.sp" + self.pex_name = OPTS.output_path + self.name + ".pex.sp" + self.gds_name = OPTS.output_path + self.name + ".gds" + self.lef_name = OPTS.output_path + self.name + ".lef" + self.v_name = OPTS.output_path + self.name + ".v" + # reset the static duplicate name checker for unit tests # in case we create more than one SRAM from design import design design.name_map=[] + def create(self): debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size, self.num_words, self.num_banks)) start_time = datetime.datetime.now() - self.name = name - if self.num_banks == 1: - from sram_1bank import sram_1bank as sram + from sram_1bank import sram_1bank as sram_banked elif self.num_banks == 2: - from sram_2bank import sram_2bank as sram + from sram_2bank import sram_2bank as sram_banked else: debug.error("Invalid number of banks.", -1) - self.s = sram(name, sram_config) + self.s = sram_banked(name=self.name, + sram_config=self.config) self.s.create_netlist() if not OPTS.netlist_only: self.s.create_layout() @@ -52,6 +61,14 @@ class sram(): if not OPTS.is_unit_test: print_time("SRAM creation", datetime.datetime.now(), start_time) + def get_sp_name(self): + if OPTS.use_pex: + # Use the extracted spice file + return self.pex_name + else: + # Use generated spice file for characterization + return self.sp_name + def sp_write(self, name, lvs=False, trim=False): self.s.sp_write(name, lvs, trim) @@ -101,11 +118,10 @@ class sram(): # Save the spice file start_time = datetime.datetime.now() - spname = OPTS.output_path + self.s.name + ".sp" - debug.print_raw("SP: Writing to {0}".format(spname)) - self.sp_write(spname) + debug.print_raw("SP: Writing to {0}".format(self.sp_name)) + self.sp_write(self.sp_name) functional(self.s, - os.path.basename(spname), + os.path.basename(self.sp_name), cycles=200, output_path=OPTS.output_path) print_time("Spice writing", datetime.datetime.now(), start_time) @@ -113,12 +129,11 @@ class sram(): if not OPTS.netlist_only: # Write the layout start_time = datetime.datetime.now() - gdsname = OPTS.output_path + self.s.name + ".gds" - debug.print_raw("GDS: Writing to {0}".format(gdsname)) - self.gds_write(gdsname) + debug.print_raw("GDS: Writing to {0}".format(self.gds_name)) + self.gds_write(self.gds_name) if OPTS.check_lvsdrc: verify.write_drc_script(cell_name=self.s.name, - gds_name=os.path.basename(gdsname), + gds_name=os.path.basename(self.gds_name), extract=True, final_verification=True, output_path=OPTS.output_path) @@ -126,20 +141,18 @@ class sram(): # Create a LEF physical model start_time = datetime.datetime.now() - lefname = OPTS.output_path + self.s.name + ".lef" - debug.print_raw("LEF: Writing to {0}".format(lefname)) - self.lef_write(lefname) + debug.print_raw("LEF: Writing to {0}".format(self.lef_name)) + self.lef_write(self.lef_name) print_time("LEF", datetime.datetime.now(), start_time) # Save the LVS file start_time = datetime.datetime.now() - lvsname = OPTS.output_path + self.s.name + ".lvs.sp" - debug.print_raw("LVS: Writing to {0}".format(lvsname)) - self.sp_write(lvsname, lvs=True) + debug.print_raw("LVS: Writing to {0}".format(self.lvs_name)) + self.sp_write(self.lvs_name, lvs=True) if not OPTS.netlist_only and OPTS.check_lvsdrc: verify.write_lvs_script(cell_name=self.s.name, - gds_name=os.path.basename(gdsname), - sp_name=os.path.basename(lvsname), + gds_name=os.path.basename(self.gds_name), + sp_name=os.path.basename(self.lvs_name), final_verification=True, output_path=OPTS.output_path) print_time("LVS writing", datetime.datetime.now(), start_time) @@ -148,14 +161,8 @@ class sram(): if OPTS.use_pex: start_time = datetime.datetime.now() # Output the extracted design if requested - pexname = OPTS.output_path + self.s.name + ".pex.sp" - spname = OPTS.output_path + self.s.name + ".sp" - verify.run_pex(self.s.name, gdsname, spname, output=pexname) - sp_file = pexname + verify.run_pex(self.s.name, self.gds_name, self.sp_name, output=self.pex_name) print_time("Extraction", datetime.datetime.now(), start_time) - else: - # Use generated spice file for characterization - sp_file = spname # Save a functional simulation file @@ -163,7 +170,7 @@ class sram(): start_time = datetime.datetime.now() from characterizer import lib debug.print_raw("LIB: Characterizing... ") - lib(out_dir=OPTS.output_path, sram=self.s, sp_file=sp_file) + lib(out_dir=OPTS.output_path, sram=self.s, sp_file=self.get_sp_name()) print_time("Characterization", datetime.datetime.now(), start_time) # Write the config file @@ -183,9 +190,8 @@ class sram(): # Write a verilog model start_time = datetime.datetime.now() - vname = OPTS.output_path + self.s.name + ".v" - debug.print_raw("Verilog: Writing to {0}".format(vname)) - self.verilog_write(vname) + debug.print_raw("Verilog: Writing to {0}".format(self.v_name)) + self.verilog_write(self.v_name) print_time("Verilog", datetime.datetime.now(), start_time) # Write out options if specified diff --git a/compiler/sram/sram_base.py b/compiler/sram/sram_base.py index 72c5e3fe..6ecc3a5a 100644 --- a/compiler/sram/sram_base.py +++ b/compiler/sram/sram_base.py @@ -7,7 +7,7 @@ # import datetime import debug -from math import log, ceil +from math import log from importlib import reload from vector import vector from globals import OPTS, print_time @@ -20,8 +20,7 @@ from tech import spice class sram_base(design, verilog, lef): """ - Dynamically generated SRAM by connecting banks to control logic. The - number of banks should be 1 , 2 or 4 + Dynamically generated SRAM by connecting banks to control logic. """ def __init__(self, name, sram_config): design.__init__(self, name) @@ -30,17 +29,10 @@ class sram_base(design, verilog, lef): self.sram_config = sram_config sram_config.set_local_config(self) + sram_config.compute_sizes() self.bank_insts = [] - if self.write_size: - self.num_wmasks = int(ceil(self.word_size / self.write_size)) - else: - self.num_wmasks = 0 - - if not self.num_spare_cols: - self.num_spare_cols = 0 - try: from tech import power_grid self.supply_stack = power_grid diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index 4c1bb117..a65387fb 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -14,7 +14,8 @@ from sram_factory import factory class sram_config: """ This is a structure that is used to hold the SRAM configuration options. """ - def __init__(self, word_size, num_words, write_size=None, num_banks=1, words_per_row=None, num_spare_rows=0, num_spare_cols=0): + def __init__(self, word_size, num_words, write_size=None, num_banks=1, + words_per_row=None, num_spare_rows=0, num_spare_cols=0): self.word_size = word_size self.num_words = num_words # Don't add a write mask if it is the same size as the data word @@ -37,6 +38,16 @@ class sram_config: except ImportError: self.array_col_multiple = 1 + if self.write_size: + self.num_wmasks = int(ceil(self.word_size / self.write_size)) + else: + self.num_wmasks = 0 + + if not self.num_spare_cols: + self.num_spare_cols = 0 + + if not self.num_spare_rows: + self.num_spare_rows = 0 # This will get over-written when we determine the organization self.words_per_row = words_per_row @@ -167,3 +178,43 @@ class sram_config: return int(words_per_row * tentative_num_rows / 16) return words_per_row + + def setup_multiport_constants(self): + """ + These are contants and lists that aid multiport design. + Ports are always in the order RW, W, R. + Port indices start from 0 and increment. + A first RW port will have clk0, csb0, web0, addr0, data0 + A first W port (with no RW ports) will be: clk0, csb0, addr0, data0 + + """ + total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports + + # These are the read/write port indices. + self.readwrite_ports = [] + # These are the read/write and write-only port indices + self.write_ports = [] + # These are the write-only port indices. + self.writeonly_ports = [] + # These are the read/write and read-only port indices + self.read_ports = [] + # These are the read-only port indices. + self.readonly_ports = [] + # These are all the ports + self.all_ports = list(range(total_ports)) + + # The order is always fixed as RW, W, R + port_number = 0 + for port in range(OPTS.num_rw_ports): + self.readwrite_ports.append(port_number) + self.write_ports.append(port_number) + self.read_ports.append(port_number) + port_number += 1 + for port in range(OPTS.num_w_ports): + self.write_ports.append(port_number) + self.writeonly_ports.append(port_number) + port_number += 1 + for port in range(OPTS.num_r_ports): + self.read_ports.append(port_number) + self.readonly_ports.append(port_number) + port_number += 1 From f9bec36da53b8cac389f9157e87bc7b9b5616149 Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Tue, 1 Mar 2022 15:12:03 -0800 Subject: [PATCH 07/17] Adding characterizer executable --- compiler/characterize_existing.py | 64 +++++++++++++++++++++++++++++++ compiler/sram/sram.py | 5 ++- 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100755 compiler/characterize_existing.py diff --git a/compiler/characterize_existing.py b/compiler/characterize_existing.py new file mode 100755 index 00000000..82e95b1b --- /dev/null +++ b/compiler/characterize_existing.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +""" +This script will characterize an SRAM previously generated by OpenRAM given a +configuration file. Configuration option "use_pex" determines whether extracted +or generated spice is used and option "analytical_delay" determines whether +an analytical model or spice simulation is used for characterization. +""" + +import sys +import datetime +from globals import * +from importlib import reload + +(OPTS, args) = parse_args() + +# Override the usage +USAGE = "Usage: {} [options] \nUse -h for help.\n".format(__file__) + +# Check that we are left with a single configuration file as argument. +if len(args) != 1: + print(USAGE) + sys.exit(2) + +# These depend on arguments, so don't load them until now. +import debug + +# Parse config file and set up all the options +init_openram(config_file=args[0], is_unit_test=False) + +# Configure the SRAM organization (duplicated from openram.py) +from sram_config import sram_config +c = sram_config(word_size=OPTS.word_size, + num_words=OPTS.num_words, + write_size=OPTS.write_size, + num_banks=OPTS.num_banks, + words_per_row=OPTS.words_per_row, + num_spare_rows=OPTS.num_spare_rows, + num_spare_cols=OPTS.num_spare_cols) + +# Initialize and create the sram object +from sram import sram +s = sram(name=OPTS.output_name, sram_config=c) +s.create() + +# Characterize the design +start_time = datetime.datetime.now() +from characterizer import lib +debug.print_raw("LIB: Characterizing... ") +lib(out_dir=OPTS.output_path, sram=s.s, sp_file=s.get_sp_name()) +print_time("Characterization", datetime.datetime.now(), start_time) + +# Output info about this run +print("Output file is:\n{0}.lib".format(OPTS.output_path)) +#report_status() #could modify this function to provide relevant info + +# Delete temp files, remove the dir, etc. +end_openram() diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index afd57f48..fdc8ec76 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -25,6 +25,7 @@ class sram(): self.name = name self.config = sram_config + sram_config.setup_multiport_constants() sram_config.set_local_config(self) self.sp_name = OPTS.output_path + self.name + ".sp" @@ -120,6 +121,8 @@ class sram(): start_time = datetime.datetime.now() debug.print_raw("SP: Writing to {0}".format(self.sp_name)) self.sp_write(self.sp_name) + + # Save a functional simulation file with default period functional(self.s, os.path.basename(self.sp_name), cycles=200, @@ -164,8 +167,6 @@ class sram(): verify.run_pex(self.s.name, self.gds_name, self.sp_name, output=self.pex_name) print_time("Extraction", datetime.datetime.now(), start_time) - # Save a functional simulation file - # Characterize the design start_time = datetime.datetime.now() from characterizer import lib From a89cd4deeed0650744a0f300363f477104114589 Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Wed, 2 Mar 2022 12:18:54 -0800 Subject: [PATCH 08/17] characterizer and functional simulator working from command line --- compiler/characterize_existing.py | 2 + compiler/characterizer/functional.py | 7 +-- compiler/func_sim_existing.py | 70 ++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) create mode 100755 compiler/func_sim_existing.py diff --git a/compiler/characterize_existing.py b/compiler/characterize_existing.py index 82e95b1b..f6c83c4b 100755 --- a/compiler/characterize_existing.py +++ b/compiler/characterize_existing.py @@ -34,6 +34,8 @@ import debug # Parse config file and set up all the options init_openram(config_file=args[0], is_unit_test=False) +print_banner() + # Configure the SRAM organization (duplicated from openram.py) from sram_config import sram_config c = sram_config(word_size=OPTS.word_size, diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index e7d03f25..3bc94ab7 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -36,9 +36,6 @@ class functional(simulation): if not corner: corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) - if period: - self.period = period - if not output_path: self.output_path = OPTS.openram_temp else: @@ -73,6 +70,10 @@ class functional(simulation): self.set_spice_constants() self.set_stimulus_variables() + # Override default period + if period: + self.period = period + # For the debug signal names self.wordline_row = 0 self.bitline_column = 0 diff --git a/compiler/func_sim_existing.py b/compiler/func_sim_existing.py new file mode 100755 index 00000000..ec89c3d7 --- /dev/null +++ b/compiler/func_sim_existing.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +""" +This script will characterize an SRAM previously generated by OpenRAM given a +configuration file. Configuration option "use_pex" determines whether extracted +or generated spice is used and option "analytical_delay" determines whether +an analytical model or spice simulation is used for characterization. +""" + +import sys +import datetime +from globals import * +from importlib import reload + +(OPTS, args) = parse_args() + +# Override the usage +USAGE = "Usage: {} [options] \nUse -h for help.\n".format(__file__) + +# Check that we are left with a single configuration file as argument. +if len(args) != 3: + print(USAGE) + sys.exit(2) + +# Parse argument +config_file = args[0] +cycles = int(args[1]) +period = float(args[2]) + +# These depend on arguments, so don't load them until now. +import debug + +# Parse config file and set up all the options +init_openram(config_file=config_file, is_unit_test=False) + +print_banner() + +# Configure the SRAM organization (duplicated from openram.py) +from sram_config import sram_config +c = sram_config(word_size=OPTS.word_size, + num_words=OPTS.num_words, + write_size=OPTS.write_size, + num_banks=OPTS.num_banks, + words_per_row=OPTS.words_per_row, + num_spare_rows=OPTS.num_spare_rows, + num_spare_cols=OPTS.num_spare_cols) + +# Initialize and create the sram object +from sram import sram +s = sram(name=OPTS.output_name, sram_config=c) +s.create() + +# Generate stimulus and run functional simulation on the design +start_time = datetime.datetime.now() +from characterizer import functional +debug.print_raw("Functional simulation... ") +f = functional(s.s, cycles=cycles, spfile=s.get_sp_name(), period=period, output_path=OPTS.openram_temp) +(fail, error) = f.run() +print_time("Functional simulation", datetime.datetime.now(), start_time) +print(error) + +# Delete temp files, remove the dir, etc. after success +if fail: + end_openram() From b216552c3feb1006687d6512436b99ccf1fe4103 Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Wed, 2 Mar 2022 12:47:05 -0800 Subject: [PATCH 09/17] clarify file location message for user --- compiler/characterize_existing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/characterize_existing.py b/compiler/characterize_existing.py index f6c83c4b..c8456b9f 100755 --- a/compiler/characterize_existing.py +++ b/compiler/characterize_existing.py @@ -59,7 +59,7 @@ lib(out_dir=OPTS.output_path, sram=s.s, sp_file=s.get_sp_name()) print_time("Characterization", datetime.datetime.now(), start_time) # Output info about this run -print("Output file is:\n{0}.lib".format(OPTS.output_path)) +print("Output files are:\n{0}*.lib".format(OPTS.output_path)) #report_status() #could modify this function to provide relevant info # Delete temp files, remove the dir, etc. From 043e93e75e38ac0f565057802446a0c106ae1ef1 Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Wed, 2 Mar 2022 16:38:39 -0800 Subject: [PATCH 10/17] call create() function from sram/__init__ --- compiler/characterize_existing.py | 1 - compiler/func_sim_existing.py | 1 - compiler/gen_stimulus.py | 1 - compiler/openram.py | 7 ++----- compiler/sram/sram.py | 2 ++ 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/characterize_existing.py b/compiler/characterize_existing.py index c8456b9f..73a7c235 100755 --- a/compiler/characterize_existing.py +++ b/compiler/characterize_existing.py @@ -49,7 +49,6 @@ c = sram_config(word_size=OPTS.word_size, # Initialize and create the sram object from sram import sram s = sram(name=OPTS.output_name, sram_config=c) -s.create() # Characterize the design start_time = datetime.datetime.now() diff --git a/compiler/func_sim_existing.py b/compiler/func_sim_existing.py index ec89c3d7..0d693e91 100755 --- a/compiler/func_sim_existing.py +++ b/compiler/func_sim_existing.py @@ -54,7 +54,6 @@ c = sram_config(word_size=OPTS.word_size, # Initialize and create the sram object from sram import sram s = sram(name=OPTS.output_name, sram_config=c) -s.create() # Generate stimulus and run functional simulation on the design start_time = datetime.datetime.now() diff --git a/compiler/gen_stimulus.py b/compiler/gen_stimulus.py index d69d867d..c9b4e350 100755 --- a/compiler/gen_stimulus.py +++ b/compiler/gen_stimulus.py @@ -60,7 +60,6 @@ OPTS.openram_temp = OPTS.output_path from sram import sram s = sram(name=OPTS.output_name, sram_config=c) -s.create() from characterizer import delay import tech diff --git a/compiler/openram.py b/compiler/openram.py index 74f80128..240a3f9f 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -72,13 +72,10 @@ debug.print_raw("Output files are: ") for path in output_files: debug.print_raw(path) - +# Initialize the SRAM s = sram(name=OPTS.output_name, sram_config=c) -# Actually build the SRAM -s.create() - -# Output the files for the resulting SRAM +# Generate and output the files for the resulting SRAM s.save() # Delete temp files etc. diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index fdc8ec76..afa8cd58 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -40,6 +40,8 @@ class sram(): from design import design design.name_map=[] + self.create() + def create(self): debug.info(2, "create sram of size {0} with {1} num of words {2} banks".format(self.word_size, self.num_words, From ef710eae7aab2dcba4a415f8bd0ce52d31971580 Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Fri, 4 Mar 2022 13:53:20 -0800 Subject: [PATCH 11/17] reorder sram __init__() argument order for tests that rely on the order --- compiler/sram/sram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index afa8cd58..627f58bd 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -21,7 +21,7 @@ class sram(): results. We can later add visualizer and other high-level functions as needed. """ - def __init__(self, name, sram_config): + def __init__(self, sram_config, name): self.name = name self.config = sram_config From 4514927a7b40851812037852050d08193a620720 Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Wed, 9 Mar 2022 09:43:00 -0800 Subject: [PATCH 12/17] log sim result after func_sim --- compiler/func_sim_existing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/func_sim_existing.py b/compiler/func_sim_existing.py index 0d693e91..7616fc81 100755 --- a/compiler/func_sim_existing.py +++ b/compiler/func_sim_existing.py @@ -61,8 +61,8 @@ from characterizer import functional debug.print_raw("Functional simulation... ") f = functional(s.s, cycles=cycles, spfile=s.get_sp_name(), period=period, output_path=OPTS.openram_temp) (fail, error) = f.run() +debug.print_raw(error) print_time("Functional simulation", datetime.datetime.now(), start_time) -print(error) # Delete temp files, remove the dir, etc. after success if fail: From 06cd2620f54c713637a31c314d69fe9d08403b14 Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Thu, 14 Apr 2022 10:04:07 -0700 Subject: [PATCH 13/17] force netlist only mode in memchar memfunc, rename char and func scripts, add description for func script --- compiler/{characterize_existing.py => memchar.py} | 2 ++ compiler/{func_sim_existing.py => memfunc.py} | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) rename compiler/{characterize_existing.py => memchar.py} (98%) rename compiler/{func_sim_existing.py => memfunc.py} (86%) diff --git a/compiler/characterize_existing.py b/compiler/memchar.py similarity index 98% rename from compiler/characterize_existing.py rename to compiler/memchar.py index 73a7c235..d824e5a6 100755 --- a/compiler/characterize_existing.py +++ b/compiler/memchar.py @@ -46,6 +46,8 @@ c = sram_config(word_size=OPTS.word_size, num_spare_rows=OPTS.num_spare_rows, num_spare_cols=OPTS.num_spare_cols) +OPTS.netlist_only = True + # Initialize and create the sram object from sram import sram s = sram(name=OPTS.output_name, sram_config=c) diff --git a/compiler/func_sim_existing.py b/compiler/memfunc.py similarity index 86% rename from compiler/func_sim_existing.py rename to compiler/memfunc.py index 7616fc81..7d8ed83e 100755 --- a/compiler/func_sim_existing.py +++ b/compiler/memfunc.py @@ -7,10 +7,10 @@ # All rights reserved. # """ -This script will characterize an SRAM previously generated by OpenRAM given a -configuration file. Configuration option "use_pex" determines whether extracted -or generated spice is used and option "analytical_delay" determines whether -an analytical model or spice simulation is used for characterization. +This script will functionally simulate an SRAM previously generated by OpenRAM +given a configuration file. Configuration option "use_pex" determines whether +extracted or generated spice is used. Command line arguments dictate the +number of cycles and period to be simulated. """ import sys @@ -51,6 +51,8 @@ c = sram_config(word_size=OPTS.word_size, num_spare_rows=OPTS.num_spare_rows, num_spare_cols=OPTS.num_spare_cols) +OPTS.netlist_only = True + # Initialize and create the sram object from sram import sram s = sram(name=OPTS.output_name, sram_config=c) From 9ec87c4d526671e3f9938f6b3f40b4195f008d5f Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Thu, 14 Apr 2022 10:13:48 -0700 Subject: [PATCH 14/17] disable lvs/drc in char and func scripts --- compiler/memchar.py | 1 + compiler/memfunc.py | 1 + 2 files changed, 2 insertions(+) diff --git a/compiler/memchar.py b/compiler/memchar.py index d824e5a6..b5eb4909 100755 --- a/compiler/memchar.py +++ b/compiler/memchar.py @@ -47,6 +47,7 @@ c = sram_config(word_size=OPTS.word_size, num_spare_cols=OPTS.num_spare_cols) OPTS.netlist_only = True +OPTS.check_lvsdrc = False # Initialize and create the sram object from sram import sram diff --git a/compiler/memfunc.py b/compiler/memfunc.py index 7d8ed83e..87686455 100755 --- a/compiler/memfunc.py +++ b/compiler/memfunc.py @@ -52,6 +52,7 @@ c = sram_config(word_size=OPTS.word_size, num_spare_cols=OPTS.num_spare_cols) OPTS.netlist_only = True +OPTS.check_lvsdrc = False # Initialize and create the sram object from sram import sram From 45df34c7747d199e59e7e26b0c6b38dfb392cd0c Mon Sep 17 00:00:00 2001 From: samuelkcrow Date: Thu, 21 Apr 2022 13:06:13 -0700 Subject: [PATCH 15/17] add tests --- compiler/tests/30_memchar_test.py | 95 ++++++++++++++++++ compiler/tests/30_memfunc_test.py | 98 +++++++++++++++++++ .../tests/configs/config_mem_char_func.py | 16 +++ 3 files changed, 209 insertions(+) create mode 100755 compiler/tests/30_memchar_test.py create mode 100755 compiler/tests/30_memfunc_test.py create mode 100644 compiler/tests/configs/config_mem_char_func.py diff --git a/compiler/tests/30_memchar_test.py b/compiler/tests/30_memchar_test.py new file mode 100755 index 00000000..faca2154 --- /dev/null +++ b/compiler/tests/30_memchar_test.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import unittest +from testutils import * +import sys, os, re, shutil +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +import debug +import getpass + + +class openram_front_end_test(openram_test): + + def runTest(self): + OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) + config_file = "{}/tests/configs/config_mem_char_func".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(1, "Testing commandline characterizer script memchar.py with 2-bit, 16 word SRAM.") + out_file = "testsram" + out_path = "/tmp/testsram_{0}_{1}_{2}".format(OPTS.tech_name, getpass.getuser(), os.getpid()) + + # make sure we start without the files existing + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path), False) + + try: + os.makedirs(out_path, 0o0750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(out_path, 0o0750) + + # specify the same verbosity for the system call + options = "" + for i in range(OPTS.verbose_level): + options += " -v" + + if OPTS.spice_name: + options += " -s {}".format(OPTS.spice_name) + + if OPTS.tech_name: + options += " -t {}".format(OPTS.tech_name) + + options += " -j 2" + + # Always perform code coverage + if OPTS.coverage == 0: + debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") + exe_name = "{0}/memchar.py ".format(OPENRAM_HOME) + else: + exe_name = "{0}{1}/memchar.py ".format(OPTS.coverage_exe, OPENRAM_HOME) + config_name = "{0}/tests/configs/config_mem_char_func.py".format(OPENRAM_HOME) + cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, + out_file, + out_path, + options, + config_name, + out_path) + debug.info(1, cmd) + os.system(cmd) + + # Make sure there is any .lib file + import glob + files = glob.glob('{0}/*.lib'.format(out_path)) + self.assertTrue(len(files)>0) + + # grep any errors from the output + output_log = open("{0}/output.log".format(out_path), "r") + output = output_log.read() + output_log.close() + self.assertEqual(len(re.findall('ERROR', output)), 0) + self.assertEqual(len(re.findall('WARNING', output)), 0) + + # now clean up the directory + if not OPTS.keep_temp: + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path), False) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/30_memfunc_test.py b/compiler/tests/30_memfunc_test.py new file mode 100755 index 00000000..c9cf6eb8 --- /dev/null +++ b/compiler/tests/30_memfunc_test.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from asyncio import subprocess +import unittest +from testutils import * +import sys, os, re, shutil +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +import debug +import getpass + + +class openram_front_end_test(openram_test): + + def runTest(self): + OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) + config_file = "{}/tests/configs/config_mem_char_func".format(os.getenv("OPENRAM_HOME")) + globals.init_openram(config_file) + + debug.info(1, "Testing commandline functional simulator script memfunc.py with 2-bit, 16 word SRAM.") + out_file = "testsram" + out_path = "/tmp/testsram_{0}_{1}_{2}".format(OPTS.tech_name, getpass.getuser(), os.getpid()) + + # make sure we start without the files existing + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path), False) + + try: + os.makedirs(out_path, 0o0750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(out_path, 0o0750) + + # copy the 2x16 sram spice file into out_path because memfunc.py expects it there + sp_src_file = "{0}/tests/golden/sram_2_16_1_{1}.sp".format(OPENRAM_HOME, OPTS.tech_name) + sp_dst_file = out_path + "/" + OPTS.output_name + ".sp" + shutil.copy(sp_src_file, sp_dst_file) + + # specify the same verbosity for the system call + options = "" + for i in range(OPTS.verbose_level): + options += " -v" + + if OPTS.spice_name: + options += " -s {}".format(OPTS.spice_name) + + if OPTS.tech_name: + options += " -t {}".format(OPTS.tech_name) + + options += " -j 2" + + # Always perform code coverage + if OPTS.coverage == 0: + debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") + exe_name = "{0}/memfunc.py ".format(OPENRAM_HOME) + else: + exe_name = "{0}{1}/memfunc.py ".format(OPTS.coverage_exe, OPENRAM_HOME) + config_name = "{0}/tests/configs/config_mem_char_func.py".format(OPENRAM_HOME) + period_and_cycles = 10 + cmd = "{0} -n -o {1} -p {2} {3} {4} {5} {5} 2>&1 > {6}/output.log".format(exe_name, + out_file, + out_path, + options, + config_name, + period_and_cycles, + out_path) + debug.info(1, cmd) + os.system(cmd) + + # grep any errors from the output + output_log = open("{0}/output.log".format(out_path), "r") + output = output_log.read() + output_log.close() + self.assertEqual(len(re.findall('ERROR', output)), 0) + self.assertEqual(len(re.findall('WARNING', output)), 0) + + # now clean up the directory + if not OPTS.keep_temp: + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path), False) + + globals.end_openram() + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) diff --git a/compiler/tests/configs/config_mem_char_func.py b/compiler/tests/configs/config_mem_char_func.py new file mode 100644 index 00000000..a825acfc --- /dev/null +++ b/compiler/tests/configs/config_mem_char_func.py @@ -0,0 +1,16 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from globals import OPTS +word_size = 2 +num_words = 16 + +tech_name = OPTS.tech_name +output_name = "sram" + +nominal_corner_only = True +spice_name = "ngspice" From 9aafada1abdf04b29188db2e5b8efc37fd6928a0 Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Thu, 19 May 2022 09:07:45 -0700 Subject: [PATCH 16/17] Fake SRAM and Xyce RAW file option --- compiler/characterizer/fake_sram.py | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 compiler/characterizer/fake_sram.py diff --git a/compiler/characterizer/fake_sram.py b/compiler/characterizer/fake_sram.py new file mode 100644 index 00000000..c114d790 --- /dev/null +++ b/compiler/characterizer/fake_sram.py @@ -0,0 +1,61 @@ +import sram_config + +class fake_sram(sram_config.sram_config): + """ This is an SRAM that doesn't actually create itself, just computes + the sizes. """ + def __init__(self, word_size, num_words, num_banks, name, num_spare_rows): + self.name = name + self.word_size = word_size + self.num_words = num_words + self.num_banks = num_banks + self.num_spare_rows = num_spare_rows + # TODO: Get width and height from gds bbox + self.width = 0 + self.height = 0 + #c = reload(__import__(OPTS.bitcell)) + #self.mod_bitcell = getattr(c, OPTS.bitcell) + #self.bitcell = self.mod_bitcell() + # to get the row, col, etc. + self.compute_sizes() + self.setup_multiport_constants() + + def setup_multiport_constants(self): + """ + Taken from ../base/design.py + These are contants and lists that aid multiport design. + Ports are always in the order RW, W, R. + Port indices start from 0 and increment. + A first RW port will have clk0, csb0, web0, addr0, data0 + A first W port (with no RW ports) will be: clk0, csb0, addr0, data0 + + """ + total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports + + # These are the read/write port indices. + self.readwrite_ports = [] + # These are the read/write and write-only port indices + self.write_ports = [] + # These are the write-only port indices. + self.writeonly_ports = [] + # These are the read/write and read-only port indices + self.read_ports = [] + # These are the read-only port indices. + self.readonly_ports = [] + # These are all the ports + self.all_ports = list(range(total_ports)) + + # The order is always fixed as RW, W, R + port_number = 0 + for port in range(OPTS.num_rw_ports): + self.readwrite_ports.append(port_number) + self.write_ports.append(port_number) + self.read_ports.append(port_number) + port_number += 1 + for port in range(OPTS.num_w_ports): + self.write_ports.append(port_number) + self.writeonly_ports.append(port_number) + port_number += 1 + for port in range(OPTS.num_r_ports): + self.read_ports.append(port_number) + self.readonly_ports.append(port_number) + port_number += 1 From 471f68fab2d5797be6444ae73668c2859763d2c8 Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Mon, 23 May 2022 10:56:22 -0700 Subject: [PATCH 17/17] Characterizer options --- compiler/options.py | 2 ++ compiler/tests/sram_1b_16_1rw_scn4m_subm.log | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 compiler/tests/sram_1b_16_1rw_scn4m_subm.log diff --git a/compiler/options.py b/compiler/options.py index f3c19283..b7ce0f54 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -91,6 +91,8 @@ class options(optparse.Values): sim_data_path = None # A list of load/slew tuples use_specified_load_slew = None + # Spice simulation raw file + spice_raw_file = None ################### # Run-time vs accuracy options. diff --git a/compiler/tests/sram_1b_16_1rw_scn4m_subm.log b/compiler/tests/sram_1b_16_1rw_scn4m_subm.log new file mode 100644 index 00000000..0164bbb4 --- /dev/null +++ b/compiler/tests/sram_1b_16_1rw_scn4m_subm.log @@ -0,0 +1,2 @@ +ERROR: file magic.py: line 358: sram LVS mismatch (results in /tmp/openram_bugra_14157_temp/sram.lvs.report) +