Merge remote-tracking branch 'origin/dev' into rbl_revamp

This commit is contained in:
mrg 2019-07-12 11:10:07 -07:00
commit a189b325ed
20 changed files with 772 additions and 143 deletions

View File

@ -11,6 +11,7 @@ import os
import math import math
import tech import tech
class spice(): class spice():
""" """
This provides a set of useful generic types for hierarchy This provides a set of useful generic types for hierarchy
@ -132,13 +133,13 @@ class spice():
"""Adds a subckt/submodule to the subckt hierarchy""" """Adds a subckt/submodule to the subckt hierarchy"""
self.mods.append(mod) self.mods.append(mod)
def connect_inst(self, args, check=True): def connect_inst(self, args, check=True):
"""Connects the pins of the last instance added """Connects the pins of the last instance added
It is preferred to use the function with the check to find if It is preferred to use the function with the check to find if
there is a problem. The check option can be set to false there is a problem. The check option can be set to false
where we dynamically generate groups of connections after a where we dynamically generate groups of connections after a
group of modules are generated.""" group of modules are generated."""
if (check and (len(self.insts[-1].mod.pins) != len(args))): if (check and (len(self.insts[-1].mod.pins) != len(args))):
from pprint import pformat from pprint import pformat
modpins_string=pformat(self.insts[-1].mod.pins) modpins_string=pformat(self.insts[-1].mod.pins)

View File

@ -18,10 +18,18 @@ class verilog:
def verilog_write(self,verilog_name): def verilog_write(self,verilog_name):
""" Write a behavioral Verilog model. """ """ Write a behavioral Verilog model. """
self.vf = open(verilog_name, "w") self.vf = open(verilog_name, "w")
# Determine if optional write mask is used
self.wmask_enabled = False
if self.word_size != self.write_size:
self.wmask_enabled = True
self.vf.write("// OpenRAM SRAM model\n") self.vf.write("// OpenRAM SRAM model\n")
self.vf.write("// Words: {0}\n".format(self.num_words)) self.vf.write("// Words: {0}\n".format(self.num_words))
self.vf.write("// Word size: {0}\n\n".format(self.word_size)) self.vf.write("// Word size: {0}\n".format(self.word_size))
if self.wmask_enabled:
self.vf.write("// Write size: {0}\n\n".format(self.write_size))
else:
self.vf.write("\n")
self.vf.write("module {0}(\n".format(self.name)) self.vf.write("module {0}(\n".format(self.name))
for port in self.all_ports: for port in self.all_ports:
@ -32,9 +40,15 @@ class verilog:
elif port in self.write_ports: elif port in self.write_ports:
self.vf.write("// Port {0}: W\n".format(port)) self.vf.write("// Port {0}: W\n".format(port))
if port in self.readwrite_ports: if port in self.readwrite_ports:
self.vf.write(" clk{0},csb{0},web{0},ADDR{0},DIN{0},DOUT{0}".format(port)) self.vf.write(" clk{0},csb{0},web{0},".format(port))
if self.wmask_enabled:
self.vf.write("wmask{},".format(port))
self.vf.write("ADDR{0},DIN{0},DOUT{0}".format(port))
elif port in self.write_ports: elif port in self.write_ports:
self.vf.write(" clk{0},csb{0},ADDR{0},DIN{0}".format(port)) self.vf.write(" clk{0},csb{0},".format(port))
if self.wmask_enabled:
self.vf.write("wmask{},".format(port))
self.vf.write("ADDR{0},DIN{0}".format(port))
elif port in self.read_ports: elif port in self.read_ports:
self.vf.write(" clk{0},csb{0},ADDR{0},DOUT{0}".format(port)) self.vf.write(" clk{0},csb{0},ADDR{0},DOUT{0}".format(port))
# Continue for every port on a new line # Continue for every port on a new line
@ -42,6 +56,9 @@ class verilog:
self.vf.write(",\n") self.vf.write(",\n")
self.vf.write("\n );\n\n") self.vf.write("\n );\n\n")
if self.wmask_enabled:
self.num_wmask = int(self.word_size/self.write_size)
self.vf.write(" parameter NUM_WMASK = {0} ;\n".format(self.num_wmask))
self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size)) self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size))
self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(self.addr_size)) self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(self.addr_size))
self.vf.write(" parameter RAM_DEPTH = 1 << ADDR_WIDTH;\n") self.vf.write(" parameter RAM_DEPTH = 1 << ADDR_WIDTH;\n")
@ -85,6 +102,9 @@ class verilog:
self.vf.write(" reg csb{0}_reg;\n".format(port)) self.vf.write(" reg csb{0}_reg;\n".format(port))
if port in self.readwrite_ports: if port in self.readwrite_ports:
self.vf.write(" reg web{0}_reg;\n".format(port)) self.vf.write(" reg web{0}_reg;\n".format(port))
if port in self.write_ports:
if self.wmask_enabled:
self.vf.write(" reg [NUM_WMASK-1:0] wmask{0}_reg;\n".format(port))
self.vf.write(" reg [ADDR_WIDTH-1:0] ADDR{0}_reg;\n".format(port)) self.vf.write(" reg [ADDR_WIDTH-1:0] ADDR{0}_reg;\n".format(port))
if port in self.write_ports: if port in self.write_ports:
self.vf.write(" reg [DATA_WIDTH-1:0] DIN{0}_reg;\n".format(port)) self.vf.write(" reg [DATA_WIDTH-1:0] DIN{0}_reg;\n".format(port))
@ -102,6 +122,9 @@ class verilog:
self.vf.write(" csb{0}_reg = csb{0};\n".format(port)) self.vf.write(" csb{0}_reg = csb{0};\n".format(port))
if port in self.readwrite_ports: if port in self.readwrite_ports:
self.vf.write(" web{0}_reg = web{0};\n".format(port)) self.vf.write(" web{0}_reg = web{0};\n".format(port))
if port in self.write_ports:
if self.wmask_enabled:
self.vf.write(" wmask{0}_reg = wmask{0};\n".format(port))
self.vf.write(" ADDR{0}_reg = ADDR{0};\n".format(port)) self.vf.write(" ADDR{0}_reg = ADDR{0};\n".format(port))
if port in self.write_ports: if port in self.write_ports:
self.vf.write(" DIN{0}_reg = DIN{0};\n".format(port)) self.vf.write(" DIN{0}_reg = DIN{0};\n".format(port))
@ -113,13 +136,19 @@ class verilog:
elif port in self.read_ports: elif port in self.read_ports:
self.vf.write(" if ( !csb{0}_reg ) \n".format(port)) self.vf.write(" if ( !csb{0}_reg ) \n".format(port))
self.vf.write(" $display($time,\" Reading %m ADDR{0}=%b DOUT{0}=%b\",ADDR{0}_reg,mem[ADDR{0}_reg]);\n".format(port)) self.vf.write(" $display($time,\" Reading %m ADDR{0}=%b DOUT{0}=%b\",ADDR{0}_reg,mem[ADDR{0}_reg]);\n".format(port))
if port in self.readwrite_ports: if port in self.readwrite_ports:
self.vf.write(" if ( !csb{0}_reg && !web{0}_reg )\n".format(port)) self.vf.write(" if ( !csb{0}_reg && !web{0}_reg )\n".format(port))
if self.wmask_enabled:
self.vf.write(" $display($time,\" Writing %m ADDR{0}=%b DIN{0}=%b wmask{0}=%b\",ADDR{0}_reg,DIN{0}_reg,wmask{0}_reg);\n".format(port))
else:
self.vf.write(" $display($time,\" Writing %m ADDR{0}=%b DIN{0}=%b\",ADDR{0}_reg,DIN{0}_reg);\n".format(port)) self.vf.write(" $display($time,\" Writing %m ADDR{0}=%b DIN{0}=%b\",ADDR{0}_reg,DIN{0}_reg);\n".format(port))
elif port in self.write_ports: elif port in self.write_ports:
self.vf.write(" if ( !csb{0}_reg )\n".format(port)) self.vf.write(" if ( !csb{0}_reg )\n".format(port))
if self.wmask_enabled:
self.vf.write(" $display($time,\" Writing %m ADDR{0}=%b DIN{0}=%b wmask{0}=%b\",ADDR{0}_reg,DIN{0}_reg,wmask{0}_reg);\n".format(port))
else:
self.vf.write(" $display($time,\" Writing %m ADDR{0}=%b DIN{0}=%b\",ADDR{0}_reg,DIN{0}_reg);\n".format(port)) self.vf.write(" $display($time,\" Writing %m ADDR{0}=%b DIN{0}=%b\",ADDR{0}_reg,DIN{0}_reg);\n".format(port))
self.vf.write(" end\n\n") self.vf.write(" end\n\n")
@ -131,6 +160,8 @@ class verilog:
self.vf.write(" input csb{0}; // active low chip select\n".format(port)) self.vf.write(" input csb{0}; // active low chip select\n".format(port))
if port in self.readwrite_ports: if port in self.readwrite_ports:
self.vf.write(" input web{0}; // active low write control\n".format(port)) self.vf.write(" input web{0}; // active low write control\n".format(port))
if (self.wmask_enabled):
self.vf.write(" input [NUM_WMASK-1:0] wmask{0}; // write mask\n".format(port))
self.vf.write(" input [ADDR_WIDTH-1:0] ADDR{0};\n".format(port)) self.vf.write(" input [ADDR_WIDTH-1:0] ADDR{0};\n".format(port))
if port in self.write_ports: if port in self.write_ports:
self.vf.write(" input [DATA_WIDTH-1:0] DIN{0};\n".format(port)) self.vf.write(" input [DATA_WIDTH-1:0] DIN{0};\n".format(port))
@ -148,9 +179,24 @@ class verilog:
self.vf.write(" always @ (negedge clk{0})\n".format(port)) self.vf.write(" always @ (negedge clk{0})\n".format(port))
self.vf.write(" begin : MEM_WRITE{0}\n".format(port)) self.vf.write(" begin : MEM_WRITE{0}\n".format(port))
if port in self.readwrite_ports: if port in self.readwrite_ports:
if self.wmask_enabled:
self.vf.write(" if ( !csb{0}_reg && !web{0}_reg ) begin\n".format(port))
else:
self.vf.write(" if ( !csb{0}_reg && !web{0}_reg )\n".format(port)) self.vf.write(" if ( !csb{0}_reg && !web{0}_reg )\n".format(port))
else:
if self.wmask_enabled:
self.vf.write(" if (!csb{0}_reg) begin\n".format(port))
else: else:
self.vf.write(" if (!csb{0}_reg)\n".format(port)) self.vf.write(" if (!csb{0}_reg)\n".format(port))
if self.wmask_enabled:
for mask in range(0,self.num_wmask):
lower = mask * self.write_size
upper = lower + self.write_size-1
self.vf.write(" if (wmask{0}_reg[{1}])\n".format(port,mask))
self.vf.write(" mem[ADDR{0}_reg][{1}:{2}] = DIN{0}_reg[{1}:{2}];\n".format(port,upper,lower))
self.vf.write(" end\n")
else:
self.vf.write(" mem[ADDR{0}_reg] = DIN{0}_reg;\n".format(port)) self.vf.write(" mem[ADDR{0}_reg] = DIN{0}_reg;\n".format(port))
self.vf.write(" end\n") self.vf.write(" end\n")

View File

@ -24,6 +24,7 @@ class simulation():
self.name = self.sram.name self.name = self.sram.name
self.word_size = self.sram.word_size self.word_size = self.sram.word_size
self.addr_size = self.sram.addr_size self.addr_size = self.sram.addr_size
self.write_size = self.sram.write_size
self.num_cols = self.sram.num_cols self.num_cols = self.sram.num_cols
self.num_rows = self.sram.num_rows self.num_rows = self.sram.num_rows
self.num_banks = self.sram.num_banks self.num_banks = self.sram.num_banks
@ -266,6 +267,8 @@ class simulation():
for port in range(total_ports): for port in range(total_ports):
if (port in read_index) and (port in write_index): if (port in read_index) and (port in write_index):
pin_names.append("WEB{0}".format(port)) pin_names.append("WEB{0}".format(port))
if (self.write_size != self.word_size):
pin_names.append("WMASK{0}".format(port))
for port in range(total_ports): for port in range(total_ports):
pin_names.append("{0}{1}".format(tech.spice["clk"], port)) pin_names.append("{0}{1}".format(tech.spice["clk"], port))

View File

@ -20,8 +20,8 @@ import copy
import importlib import importlib
USAGE = "Usage: openram.py [options] <config file>\nUse -h for help.\n" USAGE = "Usage: openram.py [options] <config file>\nUse -h for help.\n"
# Anonymous object that will be the options # Anonymous object that will be the options
OPTS = options.options() OPTS = options.options()
CHECKPOINT_OPTS=None CHECKPOINT_OPTS=None
@ -470,6 +470,18 @@ def report_status():
debug.error("{0} is not an integer in config file.".format(OPTS.word_size)) debug.error("{0} is not an integer in config file.".format(OPTS.word_size))
if type(OPTS.num_words)!=int: if type(OPTS.num_words)!=int:
debug.error("{0} is not an integer in config file.".format(OPTS.sram_size)) debug.error("{0} is not an integer in config file.".format(OPTS.sram_size))
if type(OPTS.write_size) != int and OPTS.write_size != None:
debug.error("{0} is not an integer in config file.".format(OPTS.write_size))
# Determine if a write mask is specified by the user; if it's not, the mask write size should
# be the same as the word size so that an entire word is written at once
if OPTS.write_size==None:
OPTS.write_size = OPTS.word_size
if (OPTS.write_size < 1 or OPTS.write_size > OPTS.word_size):
debug.error("Write size needs to be between 1 bit and {0} bits.".format(OPTS.word_size))
if (OPTS.word_size % OPTS.write_size != 0):
debug.error("Write size needs to be an integer multiple of word size.")
if not OPTS.tech_name: if not OPTS.tech_name:
debug.error("Tech name must be specified in config file.") debug.error("Tech name must be specified in config file.")
@ -483,9 +495,12 @@ def report_status():
debug.print_raw("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size, debug.print_raw("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size,
OPTS.num_words, OPTS.num_words,
OPTS.num_banks)) OPTS.num_banks))
if (OPTS.write_size != OPTS.word_size):
debug.print_raw("Write size: {}".format(OPTS.write_size))
debug.print_raw("RW ports: {0}\nR-only ports: {1}\nW-only ports: {2}".format(OPTS.num_rw_ports, debug.print_raw("RW ports: {0}\nR-only ports: {1}\nW-only ports: {2}".format(OPTS.num_rw_ports,
OPTS.num_r_ports, OPTS.num_r_ports,
OPTS.num_w_ports)) OPTS.num_w_ports))
if OPTS.netlist_only: if OPTS.netlist_only:
debug.print_raw("Netlist only mode (no physical design is being done, netlist_only=False to disable).") debug.print_raw("Netlist only mode (no physical design is being done, netlist_only=False to disable).")

View File

@ -84,6 +84,9 @@ class bank(design.design):
for port in self.write_ports: for port in self.write_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
self.add_pin("din{0}_{1}".format(port,bit),"IN") self.add_pin("din{0}_{1}".format(port,bit),"IN")
# if (self.word_size != self.write_size):
# for bit in range(self.word_size):
# self.add_pin()
for port in self.all_ports: for port in self.all_ports:
for bit in range(self.addr_size): for bit in range(self.addr_size):
self.add_pin("addr{0}_{1}".format(port,bit),"INPUT") self.add_pin("addr{0}_{1}".format(port,bit),"INPUT")

View File

@ -21,7 +21,7 @@ class control_logic(design.design):
Dynamically generated Control logic for the total SRAM circuit. Dynamically generated Control logic for the total SRAM circuit.
""" """
def __init__(self, num_rows, words_per_row, word_size, sram=None, port_type="rw", name=""): def __init__(self, num_rows, words_per_row, word_size, write_size, sram=None, port_type="rw", name=""):
""" Constructor """ """ Constructor """
name = "control_logic_" + port_type name = "control_logic_" + port_type
design.design.__init__(self, name) design.design.__init__(self, name)
@ -35,6 +35,7 @@ class control_logic(design.design):
self.words_per_row = words_per_row self.words_per_row = words_per_row
self.word_size = word_size self.word_size = word_size
self.port_type = port_type self.port_type = port_type
self.write_size = write_size
self.num_cols = word_size*words_per_row self.num_cols = word_size*words_per_row
self.num_words = num_rows*words_per_row self.num_words = num_rows*words_per_row
@ -748,6 +749,8 @@ class control_logic(design.design):
def route_dffs(self): def route_dffs(self):
if self.port_type == "rw": if self.port_type == "rw":
#print("hi")
#if (self.word_size == self.write_size):
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"]) dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
elif self.port_type == "r": elif self.port_type == "r":
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"]) dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])

View File

@ -50,7 +50,8 @@ from sram_config import sram_config
# Configure the SRAM organization # Configure the SRAM organization
c = sram_config(word_size=OPTS.word_size, c = sram_config(word_size=OPTS.word_size,
num_words=OPTS.num_words) num_words=OPTS.num_words,
write_size=OPTS.write_size)
debug.print_raw("Words per row: {}".format(c.words_per_row)) debug.print_raw("Words per row: {}".format(c.words_per_row))
#from parser import * #from parser import *

View File

@ -8,6 +8,7 @@
import optparse import optparse
import getpass import getpass
import os import os
#import sram_config
class options(optparse.Values): class options(optparse.Values):
""" """
@ -29,6 +30,9 @@ class options(optparse.Values):
num_r_ports = 0 num_r_ports = 0
num_w_ports = 0 num_w_ports = 0
# Write mask size, default will be overwritten with word_size if not user specified
write_size = None
# These will get initialized by the user or the tech file # These will get initialized by the user or the tech file
supply_voltages = "" supply_voltages = ""
temperatures = "" temperatures = ""

View File

@ -35,7 +35,6 @@ class sram():
self.name = name self.name = name
if self.num_banks == 1: if self.num_banks == 1:
from sram_1bank import sram_1bank as sram from sram_1bank import sram_1bank as sram
elif self.num_banks == 2: elif self.num_banks == 2:
@ -84,8 +83,6 @@ class sram():
self.gds_write(gdsname) self.gds_write(gdsname)
print_time("GDS", datetime.datetime.now(), start_time) print_time("GDS", datetime.datetime.now(), start_time)
# Save the spice file # Save the spice file
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
spname = OPTS.output_path + self.s.name + ".sp" spname = OPTS.output_path + self.s.name + ".sp"

View File

@ -46,6 +46,9 @@ class sram_1bank(sram_base):
self.data_dff_insts = self.create_data_dff() self.data_dff_insts = self.create_data_dff()
if (self.write_size != self.word_size):
self.wmask_dff_insts = self.create_wmask_dff()
def place_instances(self): def place_instances(self):
""" """
@ -65,6 +68,7 @@ class sram_1bank(sram_base):
row_addr_pos = [None]*len(self.all_ports) row_addr_pos = [None]*len(self.all_ports)
col_addr_pos = [None]*len(self.all_ports) col_addr_pos = [None]*len(self.all_ports)
data_pos = [None]*len(self.all_ports) data_pos = [None]*len(self.all_ports)
wmask_pos = [None]*len(self.all_ports)
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk # This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
# The M1 pitch is for supply rail spacings # The M1 pitch is for supply rail spacings
@ -105,6 +109,29 @@ class sram_1bank(sram_base):
row_addr_pos[port] = vector(x_offset, y_offset) row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port]) self.row_addr_dff_insts[port].place(row_addr_pos[port])
# Add the col address flops below the bank to the left of the lower-left of bank array
if self.col_addr_dff:
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
-max_gap_size - self.col_addr_dff_insts[port].height)
self.col_addr_dff_insts[port].place(col_addr_pos[port])
# Add the data flops below the bank to the right of the lower-left of bank array
# This relies on the lower-left of the array of the bank
# decoder in upper left, bank in upper right, sensing in lower right.
# These flops go below the sensing and leave a gap to channel route to the
# sense amps.
if port in self.write_ports:
data_pos[port] = vector(self.bank.bank_array_ll.x,
-max_gap_size - self.data_dff_insts[port].height)
self.data_dff_insts[port].place(data_pos[port])
# Add the write mask flops to the left of the din flops.
if (self.write_size != self.word_size):
if port in self.write_ports:
wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
self.bank.height + max_gap_size + self.data_dff_insts[port].height)
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
if len(self.all_ports)>1: if len(self.all_ports)>1:
# Port 1 # Port 1
@ -119,6 +146,13 @@ class sram_1bank(sram_base):
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
self.bank.height + max_gap_size + self.dff.height) self.bank.height + max_gap_size + self.dff.height)
self.data_dff_insts[port].place(data_pos[port], mirror="MX") self.data_dff_insts[port].place(data_pos[port], mirror="MX")
# Add the write mask flops to the left of the din flops.
if (self.write_size != self.word_size):
if port in self.write_ports:
wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
self.bank.height + max_gap_size + self.data_dff_insts[port].height)
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
else: else:
data_pos[port] = self.bank_inst.ur() data_pos[port] = self.bank_inst.ur()
@ -326,10 +360,13 @@ class sram_1bank(sram_base):
offset=pin.center()) offset=pin.center())
def graph_exclude_data_dff(self): def graph_exclude_data_dff(self):
"""Removes data dff from search graph. """ """Removes data dff and wmask dff (if applicable) from search graph. """
#Data dffs are only for writing so are not useful for evaluating read delay. #Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay.
for inst in self.data_dff_insts: for inst in self.data_dff_insts:
self.graph_inst_exclude.add(inst) self.graph_inst_exclude.add(inst)
if (self.write_size != self.word_size):
for inst in self.wmask_dff_insts:
self.graph_inst_exclude.add(inst)
def graph_exclude_addr_dff(self): def graph_exclude_addr_dff(self):
"""Removes data dff from search graph. """ """Removes data dff from search graph. """

View File

@ -68,7 +68,10 @@ class sram_base(design, verilog, lef):
self.add_pin("web{}".format(port),"INPUT") self.add_pin("web{}".format(port),"INPUT")
for port in self.all_ports: for port in self.all_ports:
self.add_pin("clk{}".format(port),"INPUT") self.add_pin("clk{}".format(port),"INPUT")
# add the optional write mask pins
if self.word_size != self.write_size:
for port in self.write_ports:
self.add_pin("wmask{}".format(port),"INPUT")
for port in self.read_ports: for port in self.read_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
self.add_pin("DOUT{0}[{1}]".format(port,bit),"OUTPUT") self.add_pin("DOUT{0}[{1}]".format(port,bit),"OUTPUT")
@ -150,9 +153,6 @@ class sram_base(design, verilog, lef):
rtr.route() rtr.route()
def compute_bus_sizes(self): def compute_bus_sizes(self):
""" Compute the independent bus widths shared between two and four bank SRAMs """ """ Compute the independent bus widths shared between two and four bank SRAMs """
@ -278,6 +278,8 @@ class sram_base(design, verilog, lef):
self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size) self.data_dff = dff_array(name="data_dff", rows=1, columns=self.word_size)
self.add_mod(self.data_dff) self.add_mod(self.data_dff)
self.wmask_dff = dff_array(name="wmask_dff", rows=1, columns=int(self.word_size/self.write_size))
self.add_mod(self.wmask_dff)
# Create the bank module (up to four are instantiated) # Create the bank module (up to four are instantiated)
@ -303,6 +305,7 @@ class sram_base(design, verilog, lef):
self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows, self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows,
words_per_row=self.words_per_row, words_per_row=self.words_per_row,
word_size=self.word_size, word_size=self.word_size,
write_size = self.write_size,
sram=self, sram=self,
port_type="rw") port_type="rw")
self.add_mod(self.control_logic_rw) self.add_mod(self.control_logic_rw)
@ -310,6 +313,7 @@ class sram_base(design, verilog, lef):
self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows, self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows,
words_per_row=self.words_per_row, words_per_row=self.words_per_row,
word_size=self.word_size, word_size=self.word_size,
write_size=self.write_size,
sram=self, sram=self,
port_type="w") port_type="w")
self.add_mod(self.control_logic_w) self.add_mod(self.control_logic_w)
@ -317,6 +321,7 @@ class sram_base(design, verilog, lef):
self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows, self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows,
words_per_row=self.words_per_row, words_per_row=self.words_per_row,
word_size=self.word_size, word_size=self.word_size,
write_size=self.write_size,
sram=self, sram=self,
port_type="r") port_type="r")
self.add_mod(self.control_logic_r) self.add_mod(self.control_logic_r)
@ -448,6 +453,29 @@ class sram_base(design, verilog, lef):
return insts return insts
def create_wmask_dff(self):
""" Add and place all wmask flops """
num_wmask = int(self.word_size/self.write_size)
insts = []
for port in self.all_ports:
if port in self.write_ports:
insts.append(self.add_inst(name="wmask_dff{}".format(port),
mod=self.wmask_dff))
else:
insts.append(None)
continue
# inputs, outputs/output/bar
inputs = []
outputs = []
for bit in range(num_wmask):
inputs.append("wmask{}[{}]".format(port, bit))
outputs.append("BANK_WMASK{}[{}]".format(port, bit))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port), "vdd", "gnd"])
return insts
def create_control_logic(self): def create_control_logic(self):
""" Add control logic instances """ """ Add control logic instances """

View File

@ -14,14 +14,18 @@ from sram_factory import factory
class sram_config: class sram_config:
""" This is a structure that is used to hold the SRAM configuration options. """ """ This is a structure that is used to hold the SRAM configuration options. """
def __init__(self, word_size, num_words, num_banks=1, words_per_row=None): def __init__(self, word_size, num_words, write_size = None, num_banks=1, words_per_row=None):
self.word_size = word_size self.word_size = word_size
self.num_words = num_words self.num_words = num_words
self.write_size = write_size
self.num_banks = num_banks self.num_banks = num_banks
# This will get over-written when we determine the organization # This will get over-written when we determine the organization
self.words_per_row = words_per_row self.words_per_row = words_per_row
if self.write_size == None:
self.write_size = self.word_size
self.compute_sizes() self.compute_sizes()

View File

@ -34,19 +34,19 @@ class control_logic_test(openram_test):
OPTS.num_r_ports = 1 OPTS.num_r_ports = 1
debug.info(1, "Testing sample for control_logic for multiport, only write control logic") debug.info(1, "Testing sample for control_logic for multiport, only write control logic")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=8, port_type="rw") a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=8, write_size=8, port_type="rw")
self.local_check(a) self.local_check(a)
# OPTS.num_rw_ports = 0 # OPTS.num_rw_ports = 0
# OPTS.num_w_ports = 1 # OPTS.num_w_ports = 1
debug.info(1, "Testing sample for control_logic for multiport, only write control logic") debug.info(1, "Testing sample for control_logic for multiport, only write control logic")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=8, port_type="w") a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=8, write_size=8, port_type="w")
self.local_check(a) self.local_check(a)
# OPTS.num_w_ports = 0 # OPTS.num_w_ports = 0
# OPTS.num_r_ports = 1 # OPTS.num_r_ports = 1
debug.info(1, "Testing sample for control_logic for multiport, only read control logic") debug.info(1, "Testing sample for control_logic for multiport, only read control logic")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=8, port_type="r") a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=8, write_size=8, port_type="r")
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -24,7 +24,7 @@ class control_logic_test(openram_test):
# check control logic for single port # check control logic for single port
debug.info(1, "Testing sample for control_logic") debug.info(1, "Testing sample for control_logic")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32) a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, write_size=32)
self.local_check(a) self.local_check(a)
# run the test from the command line # run the test from the command line

View File

@ -0,0 +1,135 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California
# All rights reserved.
#
"""
Run regression tests/pex test on an extracted pinv to ensure pex functionality
with HSPICE.
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class hspice_pex_pinv_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
import pinv
# load the hspice
OPTS.spice_name="hspice"
OPTS.analytical_delay = False
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
import characterizer
reload(characterizer)
# generate the pinv
prev_purge_value = OPTS.purge_temp
OPTS.purge_temp = False # force set purge to false to save the sp file
debug.info(2, "Checking 1x size inverter")
tx = pinv.pinv(name="pinv", size=1)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp,tx.name)
tx.gds_write(tempgds)
tempsp = "{0}{1}.sp".format(OPTS.openram_temp,tx.name)
tx.sp_write(tempsp)
# make sure that the library simulation is successful\
sp_delay = self.simulate_delay(test_module = tempsp,
top_level_name = tx.name)
if sp_delay is "Failed":
self.fail('Library Spice module did not behave as expected')
# now generate its pex file
pex_file = self.run_pex(tx)
OPTS.purge_temp = prev_purge_value # restore the old purge value
# generate simulation for pex, make sure the simulation is successful
pex_delay = self.simulate_delay(test_module = pex_file,
top_level_name = tx.name)
# make sure the extracted spice simulated
if pex_delay is "Failed":
self.fail('Pex file did not behave as expected')
# if pex data is bigger than original spice file then result is ok
# However this may not always be true depending on the netlist provided
# comment out for now
#debug.info(2,"pex_delay: {0}".format(pex_delay))
#debug.info(2,"sp_delay: {0}".format(sp_delay))
#assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\
#.format(pex_delay,sp_delay)
globals.end_openram()
def simulate_delay(self, test_module, top_level_name):
from characterizer import charutils
from charutils import parse_spice_list
# setup simulation
sim_file = OPTS.openram_temp + "stim.sp"
log_file_name = "timing"
test_sim = self.write_simulation(sim_file, test_module, top_level_name)
test_sim.run_sim()
delay = parse_spice_list(log_file_name, "pinv_delay")
return delay
def write_simulation(self, sim_file, cir_file, top_module_name):
""" write pex spice simulation for a pinv test"""
import tech
from characterizer import measurements, stimuli
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
sim_file = open(sim_file, "w")
simulation = stimuli(sim_file,corner)
# library files
simulation.write_include(cir_file)
# supply voltages
simulation.gen_constant(sig_name ="vdd",
v_val = tech.spice["nom_supply_voltage"])
simulation.gen_constant(sig_name = "gnd",
v_val = "0v")
run_time = tech.spice["feasible_period"] * 4
# input voltage
clk_period = tech.spice["feasible_period"]
simulation.gen_pwl(sig_name ="input",
clk_times = [clk_period,clk_period],
data_values = [1,0],
period = clk_period,
slew = 0.001*tech.spice["feasible_period"],
setup = 0)
# instantiation of simulated pinv
simulation.inst_model(pins = ["input", "output", "vdd", "gnd"],
model_name = top_module_name)
# delay measurement
delay_measure = measurements.delay_measure(measure_name = "pinv_delay",
trig_name = "input",
targ_name = "output",
trig_dir_str = "FALL",
targ_dir_str = "RISE",
has_port = False)
trig_td = trag_td = 0.01 * run_time
rest_info = trig_td,trag_td,tech.spice["nom_supply_voltage"]
delay_measure.write_measure(simulation, rest_info)
simulation.write_control(end_time = run_time)
sim_file.close()
return simulation
# 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()

View File

@ -0,0 +1,137 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California
# All rights reserved.
#
"""
Run regression tests/pex test on an extracted pinv to ensure pex functionality
with Ngspice.
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class ngspice_pex_pinv_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
import pinv
# load the ngspice
OPTS.spice_name="ngspice"
OPTS.analytical_delay = False
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
import characterizer
reload(characterizer)
# generate the pinv module
prev_purge_value = OPTS.purge_temp
OPTS.purge_temp = False # force set purge to false to save the sp file
debug.info(2, "Checking 1x size inverter")
tx = pinv.pinv(name="pinv", size=1)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp,tx.name)
tx.gds_write(tempgds)
tempsp = "{0}{1}.sp".format(OPTS.openram_temp,tx.name)
tx.sp_write(tempsp)
# make sure that the library simulation is successful
sp_delay = self.simulate_delay(test_module = tempsp,
top_level_name = tx.name)
if sp_delay is "Failed":
self.fail('Library Spice module did not behave as expected')
# now generate its pex file
pex_file = self.run_pex(tx)
OPTS.purge_temp = prev_purge_value # restore the old purge value
# generate simulation for pex, make sure the simulation is successful
pex_delay = self.simulate_delay(test_module = pex_file,
top_level_name = tx.name)
# make sure the extracted spice simulated
if pex_delay is "Failed":
self.fail('Pex file did not behave as expected')
# if pex data is bigger than original spice file then result is ok
# However this may not always be true depending on the netlist provided
# comment out for now
#debug.info(2,"pex_delay: {0}".format(pex_delay))
#debug.info(2,"sp_delay: {0}".format(sp_delay))
#assert pex_delay > sp_delay, "pex delay {0} is smaller than sp_delay {1}"\
#.format(pex_delay,sp_delay)
globals.end_openram()
def simulate_delay(self, test_module, top_level_name):
from characterizer import charutils
from charutils import parse_spice_list
# setup simulation
sim_file = OPTS.openram_temp + "stim.sp"
log_file_name = "timing"
test_sim = self.write_simulation(sim_file, test_module, top_level_name)
test_sim.run_sim()
delay = parse_spice_list(log_file_name, "pinv_delay")
return delay
def write_simulation(self, sim_file, cir_file, top_module_name):
""" write pex spice simulation for a pinv test"""
import tech
from characterizer import measurements, stimuli
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
sim_file = open(sim_file, "w")
simulation = stimuli(sim_file,corner)
# library files
simulation.write_include(cir_file)
# supply voltages
simulation.gen_constant(sig_name ="vdd",
v_val = tech.spice["nom_supply_voltage"])
# The scn4m_subm and ngspice combination will have a gnd source error:
# "Fatal error: instance vgnd is a shorted VSRC"
# However, remove gnd power for all techa pass for this test
# simulation.gen_constant(sig_name = "gnd",
# v_val = "0v")
run_time = tech.spice["feasible_period"] * 4
# input voltage
clk_period = tech.spice["feasible_period"]
simulation.gen_pwl(sig_name ="input",
clk_times = [clk_period,clk_period],
data_values = [1,0],
period = clk_period,
slew = 0.001*tech.spice["feasible_period"],
setup = 0)
# instantiation of simulated pinv
simulation.inst_model(pins = ["input", "output", "vdd", "gnd"],
model_name = top_module_name)
# delay measurement
delay_measure = measurements.delay_measure(measure_name = "pinv_delay",
trig_name = "input",
targ_name = "output",
trig_dir_str = "FALL",
targ_dir_str = "RISE",
has_port = False)
trig_td = trag_td = 0.01 * run_time
rest_info = trig_td,trag_td,tech.spice["nom_supply_voltage"]
delay_measure.write_measure(simulation, rest_info)
simulation.write_control(end_time = run_time)
sim_file.close()
return simulation
# 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()

View File

@ -0,0 +1,110 @@
`define assert(signal, value) \
if (!(signal === value)) begin \
$display("ASSERTION FAILED in %m: signal != value"); \
$finish;\
end
module sram_1rw_wmask_tb;
reg clk;
reg [3:0] addr0;
reg [1:0] din0;
reg csb0;
reg web0;
reg [1:0] wmask0;
wire [1:0] dout0;
sram_2b_16_1rw_freepdk45 U0 (.DIN0(din0),
.DOUT0(dout0),
.ADDR0(addr0),
.csb0(csb0),
.web0(web0),
.wmask0(wmask0),
.clk0(clk)
);
initial
begin
//$monitor("%g addr0=%b din0=%b dout0=%b",
// $time, addr0, din0, dout0);
clk = 1;
csb0 = 1;
web0 = 1;
wmask0 = 2'b01;
addr0 = 0;
din0 = 0;
// write
#10 din0=2'b10;
addr0=4'h1;
web0 = 0;
csb0 = 0;
wmask0 = 2'b10;
// read
#10 din0=2'b11;
addr0=4'h1;
web0 = 1;
csb0 = 0;
#10 `assert(dout0, 2'b1x)
// write another
#10 din0=2'b01;
addr0=4'hC;
web0 = 0;
csb0 = 0;
wmask0 = 2'b01;
// read undefined
#10 din0=2'b11;
addr0=4'h0;
web0 = 1;
csb0 = 0;
wmask0 = 2'b01;
#10 `assert(dout0, 2'bxx)
// read defined
din0=2'b11;
addr0=4'hC;
web0 = 1;
csb0 = 0;
wmask0 = 2'b01;
#10 `assert(dout0, 2'bx1)
// write another
din0=2'b01;
addr0=4'h1;
web0 = 0;
csb0 = 0;
// read defined
#10 din0=2'b11;
addr0=4'h1;
web0 = 1;
csb0 = 0;
#10 `assert(dout0, 2'b11)
// read undefined
din0=2'b11;
addr0=4'h0;
web0 = 1;
csb0 = 0;
#10 `assert(dout0, 2'bxx)
#10 $finish;
end
always
#5 clk = !clk;
endmodule

View File

@ -65,6 +65,17 @@ class openram_test(unittest.TestCase):
if OPTS.purge_temp: if OPTS.purge_temp:
self.cleanup() self.cleanup()
def run_pex(self, a, output=None):
if output == None:
output = OPTS.openram_temp + a.name + ".pex.netlist"
tempspice = "{0}{1}.sp".format(OPTS.openram_temp,a.name)
tempgds = "{0}{1}.gds".format(OPTS.openram_temp,a.name)
import verify
result=verify.run_pex(a.name, tempgds, tempspice, output=output, final_verification=False)
if result != 0:
self.fail("PEX ERROR: {}".format(a.name))
return output
def find_feasible_test_period(self, delay_obj, sram, load, slew): def find_feasible_test_period(self, delay_obj, sram, load, slew):
"""Creates a delay simulation to determine a feasible period for the functional tests to run. """Creates a delay simulation to determine a feasible period for the functional tests to run.
@ -300,16 +311,18 @@ def header(filename, technology):
def debugTestRunner(post_mortem=None): def debugTestRunner(post_mortem=None):
"""unittest runner doing post mortem debugging on failing tests""" """unittest runner doing post mortem debugging on failing tests"""
if post_mortem is None: if post_mortem is None and not OPTS.purge_temp:
post_mortem = pdb.post_mortem post_mortem = pdb.post_mortem
class DebugTestResult(unittest.TextTestResult): class DebugTestResult(unittest.TextTestResult):
def addError(self, test, err): def addError(self, test, err):
# called before tearDown() # called before tearDown()
traceback.print_exception(*err) traceback.print_exception(*err)
if post_mortem:
post_mortem(err[2]) post_mortem(err[2])
super(DebugTestResult, self).addError(test, err) super(DebugTestResult, self).addError(test, err)
def addFailure(self, test, err): def addFailure(self, test, err):
traceback.print_exception(*err) traceback.print_exception(*err)
if post_mortem:
post_mortem(err[2]) post_mortem(err[2])
super(DebugTestResult, self).addFailure(test, err) super(DebugTestResult, self).addFailure(test, err)
return unittest.TextTestRunner(resultclass=DebugTestResult) return unittest.TextTestRunner(resultclass=DebugTestResult)

View File

@ -138,9 +138,12 @@ def write_calibre_pex_script(cell_name, extract, output, final_verification):
# check if lvs report has been done # check if lvs report has been done
# if not run drc and lvs # if not run drc and lvs
if not os.path.isfile(cell_name + ".lvs.report"): if not os.path.isfile(cell_name + ".lvs.report"):
gds_name = OPTS.openram_temp +"/"+ cell_name + ".gds"
sp_name = OPTS.openram_temp +"/"+ cell_name + ".sp"
run_drc(cell_name, gds_name) run_drc(cell_name, gds_name)
run_lvs(cell_name, gds_name, sp_name) run_lvs(cell_name, gds_name, sp_name)
from tech import drc
pex_rules = drc["xrc_rules"] pex_rules = drc["xrc_rules"]
pex_runset = { pex_runset = {
'pexRulesFile': pex_rules, 'pexRulesFile': pex_rules,
@ -317,7 +320,7 @@ def run_pex(cell_name, gds_name, sp_name, output=None, final_verification=False)
global num_pex_runs global num_pex_runs
num_pex_runs += 1 num_pex_runs += 1
write_calibre_pex_script() write_calibre_pex_script(cell_name,True,output,final_verification)
(outfile, errfile, resultsfile) = run_script(cell_name, "pex") (outfile, errfile, resultsfile) = run_script(cell_name, "pex")

View File

@ -257,9 +257,9 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
global num_pex_runs global num_pex_runs
num_pex_runs += 1 num_pex_runs += 1
#debug.warning("PEX using magic not implemented.")
debug.warning("PEX using magic not implemented.") #return 1
return 1 os.chdir(OPTS.openram_temp)
from tech import drc from tech import drc
if output == None: if output == None:
@ -271,7 +271,50 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
run_drc(name, gds_name) run_drc(name, gds_name)
run_lvs(name, gds_name, sp_name) run_lvs(name, gds_name, sp_name)
# pex_fix did run the pex using a script while dev orignial method
# use batch mode.
# the dev old code using batch mode does not run and is split into functions
#pex_runset = write_batch_pex_rule(gds_name,name,sp_name,output)
pex_runset = write_script_pex_rule(gds_name,name,output)
errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, name)
outfile = "{0}{1}.pex.out".format(OPTS.openram_temp, name)
# bash mode command from dev branch
#batch_cmd = "{0} -gui -pex {1}pex_runset -batch 2> {2} 1> {3}".format(OPTS.pex_exe,
# OPTS.openram_temp,
# errfile,
# outfile)
script_cmd = "{0} 2> {1} 1> {2}".format(pex_runset,
errfile,
outfile)
cmd = script_cmd
debug.info(2, cmd)
os.system(cmd)
# rename technology models
pex_nelist = open(output, 'r')
s = pex_nelist.read()
pex_nelist.close()
s = s.replace('pfet','p')
s = s.replace('nfet','n')
f = open(output, 'w')
f.write(s)
f.close()
# also check the output file
f = open(outfile, "r")
results = f.readlines()
f.close()
out_errors = find_error(results)
debug.check(os.path.isfile(output),"Couldn't find PEX extracted output.")
correct_port(name,output,sp_name)
return out_errors
def write_batch_pex_rule(gds_name,name,sp_name,output):
""" """
The dev branch old batch mode runset
2. magic can perform extraction with the following: 2. magic can perform extraction with the following:
#!/bin/sh #!/bin/sh
rm -f $1.ext rm -f $1.ext
@ -289,7 +332,6 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
quit -noprompt quit -noprompt
EOF EOF
""" """
pex_rules = drc["xrc_rules"] pex_rules = drc["xrc_rules"]
pex_runset = { pex_runset = {
'pexRulesFile': pex_rules, 'pexRulesFile': pex_rules,
@ -307,42 +349,89 @@ def run_pex(name, gds_name, sp_name, output=None, final_verification=False):
} }
# write the runset file # write the runset file
f = open(OPTS.openram_temp + "pex_runset", "w") file = OPTS.openram_temp + "pex_runset"
for k in sorted(pex_runset.iterkeys()): f = open(file, "w")
for k in sorted(pex_runset.keys()):
f.write("*{0}: {1}\n".format(k, pex_runset[k])) f.write("*{0}: {1}\n".format(k, pex_runset[k]))
f.close() f.close()
return file
# run pex def write_script_pex_rule(gds_name,cell_name,output):
cwd = os.getcwd() global OPTS
os.chdir(OPTS.openram_temp) run_file = OPTS.openram_temp + "run_pex.sh"
errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, name) f = open(run_file, "w")
outfile = "{0}{1}.pex.out".format(OPTS.openram_temp, name) f.write("#!/bin/sh\n")
f.write("{} -dnull -noconsole << eof\n".format(OPTS.drc_exe[1]))
f.write("gds polygon subcell true\n")
f.write("gds warning default\n")
f.write("gds read {}\n".format(gds_name))
f.write("load {}\n".format(cell_name))
f.write("select top cell\n")
f.write("expand\n")
f.write("port makeall\n")
extract = True
if not extract:
pre = "#"
else:
pre = ""
f.write(pre+"extract\n".format(cell_name))
#f.write(pre+"ext2spice hierarchy on\n")
#f.write(pre+"ext2spice format ngspice\n")
#f.write(pre+"ext2spice renumber off\n")
#f.write(pre+"ext2spice scale off\n")
#f.write(pre+"ext2spice blackbox on\n")
f.write(pre+"ext2spice subcircuit top on\n")
#f.write(pre+"ext2spice global off\n")
f.write(pre+"ext2spice {}\n".format(cell_name))
f.write("quit -noprompt\n")
f.write("eof\n")
f.write("mv {0}.spice {1}\n".format(cell_name,output))
cmd = "{0} -gui -pex {1}pex_runset -batch 2> {2} 1> {3}".format(OPTS.pex_exe,
OPTS.openram_temp,
errfile,
outfile)
debug.info(2, cmd)
os.system(cmd)
os.chdir(cwd)
# also check the output file
f = open(outfile, "r")
results = f.readlines()
f.close() f.close()
os.system("chmod u+x {}".format(run_file))
return run_file
def find_error(results):
# Errors begin with "ERROR:" # Errors begin with "ERROR:"
test = re.compile("ERROR:") test = re.compile("ERROR:")
stdouterrors = list(filter(test.search, results)) stdouterrors = list(filter(test.search, results))
for e in stdouterrors: for e in stdouterrors:
debug.error(e.strip("\n")) debug.error(e.strip("\n"))
out_errors = len(stdouterrors) out_errors = len(stdouterrors)
debug.check(os.path.isfile(output),"Couldn't find PEX extracted output.")
return out_errors return out_errors
def correct_port(name, output_file_name, ref_file_name):
pex_file = open(output_file_name, "r")
contents = pex_file.read()
# locate the start of circuit definition line
match = re.search(".subckt " + str(name) + ".*", contents)
match_index_start = match.start()
pex_file.seek(match_index_start)
rest_text = pex_file.read()
# locate the end of circuit definition line
match = re.search(r'\n', rest_text)
match_index_end = match.start()
# store the unchanged part of pex file in memory
pex_file.seek(0)
part1 = pex_file.read(match_index_start)
pex_file.seek(match_index_start + match_index_end)
part2 = pex_file.read()
pex_file.close()
# obtain the correct definition line from the original spice file
sp_file = open(ref_file_name, "r")
contents = sp_file.read()
circuit_title = re.search(".SUBCKT " + str(name) + ".*\n", contents)
circuit_title = circuit_title.group()
sp_file.close()
# write the new pex file with info in the memory
output_file = open(output_file_name, "w")
output_file.write(part1)
output_file.write(circuit_title)
output_file.write(part2)
output_file.close()
def print_drc_stats(): def print_drc_stats():
debug.info(1,"DRC runs: {0}".format(num_drc_runs)) debug.info(1,"DRC runs: {0}".format(num_drc_runs))
def print_lvs_stats(): def print_lvs_stats():