def add_pin(self, name, pin_type="INOUT"): """ Adds a pin to the pins list. Default type is INOUT signal. """ self.pins.append(name) self.pin_type[name]=pin_type debug.check(pin_type in self.valid_signal_types, "Invalid signaltype for {0}: {1}".format(name, pin_type))
def add_layout_pin_center_segment(self, text, layer, start, end, width=None): """ Creates a path like pin with center-line convention """ debug.check(start.x == end.x or start.y == end.y, "Cannot have a non-manhatten layout pin.") if width == None: minwidth_layer = drc["minwidth_{}".format(layer)] else: minwidth_layer = width # one of these will be zero width = max(start.x, end.x) - min(start.x, end.x) height = max(start.y, end.y) - min(start.y, end.y) ll_offset = vector(min(start.x, end.x), min(start.y, end.y)) # Shift it down 1/2 a width in the 0 dimension if height == 0: ll_offset -= vector(0, 0.5 * minwidth_layer) if width == 0: ll_offset -= vector(0.5 * minwidth_layer, 0) # This makes sure it is long enough, but also it is not 0 width! height = max(minwidth_layer, height) width = max(minwidth_layer, width) return self.add_layout_pin(text, layer, ll_offset, width, height)
def setup_paths(): """ Set up the non-tech related paths. """ debug.info(2, "Setting up paths...") global OPTS try: OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) except: debug.error("$OPENRAM_HOME is not properly defined.", 1) debug.check(os.path.isdir(OPENRAM_HOME), "$OPENRAM_HOME does not exist: {0}".format(OPENRAM_HOME)) # Add all of the subdirs to the python path # These subdirs are modules and don't need # to be added: characterizer, verify subdirlist = [ item for item in os.listdir(OPENRAM_HOME) if os.path.isdir(os.path.join(OPENRAM_HOME, item)) ] for subdir in subdirlist: full_path = "{0}/{1}".format(OPENRAM_HOME, subdir) debug.check( os.path.isdir(full_path), "$OPENRAM_HOME/{0} does not exist: {1}".format(subdir, full_path)) if "__pycache__" not in full_path: sys.path.append("{0}".format(full_path)) if not OPTS.openram_temp.endswith('/'): OPTS.openram_temp += "/" debug.info(1, "Temporary files saved in " + OPTS.openram_temp)
def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout): """Determine the size of the delay chain used for the Sense Amp Enable using path delays""" from math import ceil previous_delay_chain_delay = ( previous_fanout + 1 + self.parasitic_inv_delay) * previous_stages debug.info( 2, "Previous delay chain produced {} delay units".format( previous_delay_chain_delay)) delay_fanout = 3 # This can be anything >=2 #The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each #inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value required_delay = self.wl_delay * self.wl_timing_tolerance - ( self.sen_delay - previous_delay_chain_delay) debug.check(required_delay > 0, "Cannot size delay chain to have negative delay") delay_stages = ceil(required_delay / (delay_fanout + 1 + self.parasitic_inv_delay)) if delay_stages % 2 == 1: #force an even number of stages. delay_stages += 1 #Fanout can be varied as well but is a little more complicated but potentially optimal. debug.info( 1, "Setting delay chain to {} stages with {} fanout to match {} delay" .format(delay_stages, delay_fanout, required_delay)) return (delay_stages, delay_fanout)
def __init__(self, sram_config, port, name=""): sram_config.set_local_config(self) self.port = port if self.write_size is not None: self.num_wmasks = int(self.word_size / self.write_size) else: self.num_wmasks = 0 if self.num_spare_cols is None: self.num_spare_cols = 0 if name == "": name = "port_data_{0}".format(self.port) design.design.__init__(self, name) debug.info( 2, "create data port of size {0} with {1} words per row".format( self.word_size, self.words_per_row)) self.create_netlist() if not OPTS.netlist_only: debug.check( len(self.all_ports) <= 2, "Bank layout cannot handle more than two ports.") self.create_layout() self.add_boundary()
def __init__(self, inv1_size=2, inv2_size=4, name=""): if name == "": name = "dff_buf_{0}".format(dff_buf.unique_id) dff_buf.unique_id += 1 design.design.__init__(self, name) debug.info(1, "Creating {}".format(self.name)) self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size)) # This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width. # This causes a DRC in the pinv which assumes min width rails. This ensures the output # contact does not violate spacing to the rail in the NMOS. debug.check( inv1_size >= 2, "Inverter must be greater than two for rail spacing DRC rules.") debug.check( inv2_size >= 2, "Inverter must be greater than two for rail spacing DRC rules.") self.inv1_size = inv1_size self.inv2_size = inv2_size self.create_netlist() if not OPTS.netlist_only: self.create_layout()
def setup_paths(): """ Set up the non-tech related paths. """ debug.info(2, "Setting up paths...") global OPTS try: OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) except: debug.error("$OPENRAM_HOME is not properly defined.", 1) debug.check(os.path.isdir(OPENRAM_HOME), "$OPENRAM_HOME does not exist: {0}".format(OPENRAM_HOME)) debug.check( os.path.isdir(OPENRAM_HOME + "/gdsMill"), "$OPENRAM_HOME/gdsMill does not exist: {0}".format(OPENRAM_HOME + "/gdsMill")) sys.path.append("{0}/gdsMill".format(OPENRAM_HOME)) debug.check( os.path.isdir(OPENRAM_HOME + "/tests"), "$OPENRAM_HOME/tests does not exist: {0}".format(OPENRAM_HOME + "/tests")) sys.path.append("{0}/tests".format(OPENRAM_HOME)) debug.check( os.path.isdir(OPENRAM_HOME + "/characterizer"), "$OPENRAM_HOME/characterizer does not exist: {0}".format( OPENRAM_HOME + "/characterizer")) sys.path.append("{0}/characterizer".format(OPENRAM_HOME)) debug.check( os.path.isdir(OPENRAM_HOME + "/router"), "$OPENRAM_HOME/router does not exist: {0}".format(OPENRAM_HOME + "/router")) sys.path.append("{0}/router".format(OPENRAM_HOME)) if not OPTS.openram_temp.endswith('/'): OPTS.openram_temp += "/" debug.info(1, "Temporary files saved in " + OPTS.openram_temp) cleanup_paths() # make the directory if it doesn't exist try: os.makedirs(OPTS.openram_temp, 0750) except OSError as e: if e.errno == 17: # errno.EEXIST os.chmod(OPTS.openram_temp, 0750) # Don't delete the output dir, it may have other files! # make the directory if it doesn't exist try: os.makedirs(OPTS.out_path, 0750) except OSError as e: if e.errno == 17: # errno.EEXIST os.chmod(OPTS.out_path, 0750) if OPTS.out_path == "": OPTS.out_path = "." if not OPTS.out_path.endswith('/'): OPTS.out_path += "/" debug.info(1, "Output saved in " + OPTS.out_path)
def gen_pwl(self, sig_name, clk_times, data_values, period, slew, setup): """ Generate a PWL stimulus given a signal name and data values at each period. Automatically creates slews and ensures each data occurs a setup before the clock edge. The first clk_time should be 0 and is the initial time that corresponds to the initial value. """ # the initial value is not a clock time str = "Clock and data value lengths don't match. {0} clock values, {1} data values for {2}" debug.check( len(clk_times) == len(data_values), str.format(len(clk_times), len(data_values), sig_name)) # shift signal times earlier for setup time times = np.array(clk_times) - setup * period values = np.array(data_values) * self.voltage half_slew = 0.5 * slew self.sf.write("* (time, data): {}\n".format( list(zip(clk_times, data_values)))) self.sf.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0])) for i in range(1, len(times)): self.sf.write("{0}n {1}v {2}n {3}v ".format( times[i] - half_slew, values[i - 1], times[i] + half_slew, values[i])) self.sf.write(")\n")
def __init__(self, gds_name=None, module=None): """Use the gds file or the cell for the blockages with the top module topName and layers for the layers to route on """ self.gds_name = gds_name self.module = module debug.check(not (gds_name and module), "Specify only a GDS file or module") # If we specified a module instead, write it out to read the gds # This isn't efficient, but easy for now if module: gds_name = OPTS.openram_temp + "temp.gds" module.gds_write(gds_name) # Load the gds file and read in all the shapes self.layout = gdsMill.VlsiLayout(units=tech.GDS["unit"]) self.reader = gdsMill.Gds2reader(self.layout) self.reader.loadFromFile(gds_name) self.top_name = self.layout.rootStructureName self.pins = {} self.blockages = [] # all the paths we've routed so far (to supplement the blockages) self.paths = [] # The boundary will determine the limits to the size of the routing grid self.boundary = self.layout.measureBoundary(self.top_name) self.ll = vector(self.boundary[0]) self.ur = vector(self.boundary[1])
def add_pin(self, pin_name, is_source=False): """ Mark the grids that are in the pin rectangle ranges to have the pin property. pin can be a location or a label. """ found_pin = False for pin in self.pins[pin_name]: (pin_in_tracks, blockage_in_tracks) = self.convert_pin_to_tracks(pin) if (len(pin_in_tracks) > 0): found_pin = True if is_source: debug.info( 1, "Set source: " + str(pin_name) + " " + str(pin_in_tracks)) self.rg.add_source(pin_in_tracks) else: debug.info( 1, "Set target: " + str(pin_name) + " " + str(pin_in_tracks)) self.rg.add_target(pin_in_tracks) self.rg.add_blockage(blockage_in_tracks) if not found_pin: self.write_debug_gds() debug.check(found_pin, "Unable to find pin on grid.")
def get_enable_name(self): """Returns name used for enable net""" # FIXME: A better programmatic solution to designate pins enable_name = self.en_name debug.check(enable_name in self.pin_names, "Enable name {} not found in pin list".format(enable_name)) return enable_name
def import_tech(): """ Dynamically adds the tech directory to the path and imports it. """ global OPTS debug.info(2,"Importing technology: " + OPTS.tech_name) # environment variable should point to the technology dir try: OPENRAM_TECH = os.path.abspath(os.environ.get("OPENRAM_TECH")) except: debug.error("$OPENRAM_TECH environment variable is not defined.",1) # Add all of the paths for tech_path in OPENRAM_TECH.split(":"): debug.check(os.path.isdir(tech_path),"$OPENRAM_TECH does not exist: {0}".format(tech_path)) sys.path.append(tech_path) debug.info(1, "Adding technology path: {}".format(tech_path)) # Import the tech try: tech_mod = __import__(OPTS.tech_name) except ImportError: debug.error("Nonexistent technology_setup_file: {0}.py".format(filename), -1) OPTS.openram_tech = os.path.dirname(tech_mod.__file__) + "/" # Add the tech directory tech_path = OPTS.openram_tech sys.path.append(tech_path) try: import tech except ImportError: debug.error("Could not load tech module.", -1)
def DRC_LVS(self, final_verification=False, top_level=False): """Checks both DRC and LVS for a module""" # Final verification option does not allow nets to be connected by label. # Unit tests will check themselves. if OPTS.is_unit_test: return if not OPTS.check_lvsdrc: return # Do not run if disabled in options. if (OPTS.inline_lvsdrc or top_level): global total_drc_errors global total_lvs_errors tempspice = "{0}/{1}.sp".format(OPTS.openram_temp,self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) self.sp_write(tempspice) self.gds_write(tempgds) num_drc_errors = verify.run_drc(self.name, tempgds, final_verification) num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification) debug.check(num_drc_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_drc_errors)) debug.check(num_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors)) total_drc_errors += num_drc_errors total_lvs_errors += num_lvs_errors os.remove(tempspice) os.remove(tempgds)
def add_via_center(self, layers, offset, size=[1,1], mirror="R0", rotate=0, implant_type=None, well_type=None): """ Add a three layer via structure by the center coordinate accounting for mirroring and rotation. """ import contact via = contact.contact(layer_stack=layers, dimensions=size, implant_type=implant_type, well_type=well_type) height = via.height width = via.width debug.check(mirror=="R0","Use rotate to rotate vias instead of mirror.") if rotate==0: corrected_offset = offset + vector(-0.5*width,-0.5*height) elif rotate==90: corrected_offset = offset + vector(0.5*height,-0.5*width) elif rotate==180: corrected_offset = offset + vector(0.5*width,0.5*height) elif rotate==270: corrected_offset = offset + vector(-0.5*height,0.5*width) else: debug.error("Invalid rotation argument.",-1) #print(rotate,offset,"->",corrected_offset) self.add_mod(via) inst=self.add_inst(name=via.name, mod=via, offset=corrected_offset, mirror=mirror, rotate=rotate) # We don't model the logical connectivity of wires/paths self.connect_inst([]) return inst
def add_read(self, comment, address, port): """ Add the control values for a read cycle. """ debug.check( port in self.read_ports, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}". format(port, self.read_ports)) debug.info(2, comment) self.fn_cycle_comments.append(comment) self.append_cycle_comment(port, comment) self.cycle_times.append(self.t_current) self.t_current += self.period self.add_control_one_port(port, "read") self.add_address(address, port) # If the port is also a readwrite then add # the same value as previous cycle if port in self.write_ports: try: self.add_data(self.data_value[port][-1], port) except: self.add_data("0" * (self.word_size + self.num_spare_cols), port) try: self.add_wmask(self.wmask_value[port][-1], port) except: self.add_wmask("0" * self.num_wmasks, port) self.add_spare_wen("0" * self.num_spare_cols, port) #Add noops to all other ports. for unselected_port in self.all_ports: if unselected_port != port: self.add_noop_one_port(unselected_port)
def analyze(self, probe_address, probe_data, slews, loads): """ Main function to test the delays of different bits. """ debug.check( OPTS.num_rw_ports < 2 and OPTS.num_w_ports < 1 and OPTS.num_r_ports < 1, "Bit testing does not currently support multiport.") #Dict to hold all characterization values char_sram_data = {} self.set_probe(probe_address, probe_data) #self.prepare_netlist() self.load = max(loads) self.slew = max(slews) # 1) Find a feasible period and it's corresponding delays using the trimmed array. feasible_delays = self.find_feasible_period() # 2) Find the delays of several bits test_bits = self.get_test_bits() bit_delays = self.simulate_for_bit_delays(test_bits) for i in range(len(test_bits)): debug.info( 1, "Bit tested: addr {0[0]} data_pos {0[1]}\n Values {1}".format( test_bits[i], bit_delays[i]))
def connect_bitline(self, inst1, inst2, inst1_name, inst2_name): """ Connect two pins of two modules. This assumes that they have sufficient space to create a jog in the middle between the two modules (if needed). """ # determine top and bottom automatically. # since they don't overlap, we can just check the bottom y coordinate. if inst1.by() < inst2.by(): (bottom_inst, bottom_name) = (inst1, inst1_name) (top_inst, top_name) = (inst2, inst2_name) else: (bottom_inst, bottom_name) = (inst2, inst2_name) (top_inst, top_name) = (inst1, inst1_name) bottom_pin = bottom_inst.get_pin(bottom_name) top_pin = top_inst.get_pin(top_name) debug.check(bottom_pin.layer == top_pin.layer, "Pin layers do not match.") bottom_loc = bottom_pin.uc() top_loc = top_pin.bc() yoffset = 0.5 * (top_loc.y + bottom_loc.y) self.add_path(top_pin.layer, [ bottom_loc, vector(bottom_loc.x, yoffset), vector(top_loc.x, yoffset), top_loc ])
def get_wl_name(self, port=0): """Get wl name""" if props.bitcell.split_wl: return "wl{}".format(port) else: debug.check(port == 0, "One port for bitcell only.") return props.bitcell.cell_6t.pin.wl
def setup_paths(): """ Set up the non-tech related paths. """ debug.info(2, "Setting up paths...") global OPTS try: OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) except: debug.error("$OPENRAM_HOME is not properly defined.", 1) debug.check(os.path.isdir(OPENRAM_HOME), "$OPENRAM_HOME does not exist: {0}".format(OPENRAM_HOME)) # Add all of the subdirs to the python path # These subdirs are modules and don't need to be added: characterizer, verify for subdir in ["gdsMill", "tests", "router", "modules", "base", "pgates"]: full_path = "{0}/{1}".format(OPENRAM_HOME, subdir) debug.check( os.path.isdir(full_path), "$OPENRAM_HOME/{0} does not exist: {1}".format(subdir, full_path)) sys.path.append("{0}".format(full_path)) if not OPTS.openram_temp.endswith('/'): OPTS.openram_temp += "/" debug.info(1, "Temporary files saved in " + OPTS.openram_temp) cleanup_paths() # make the directory if it doesn't exist try: os.makedirs(OPTS.openram_temp, 0o750) except OSError as e: if e.errno == 17: # errno.EEXIST os.chmod(OPTS.openram_temp, 0o750)
def __init__(self, sram_config, name=""): self.sram_config = sram_config sram_config.set_local_config(self) if self.write_size: self.num_wmasks = int(self.word_size / self.write_size) else: self.num_wmasks = 0 if name == "": name = "bank_{0}_{1}".format(self.word_size, self.num_words) design.design.__init__(self, name) debug.info( 2, "create sram of size {0} with {1} words".format( self.word_size, self.num_words)) # The local control signals are gated when we have bank select logic, # so this prefix will be added to all of the input signals to create # the internal gated signals. if self.num_banks > 1: self.prefix = "gated_" else: self.prefix = "" self.create_netlist() if not OPTS.netlist_only: debug.check( len(self.all_ports) <= 2, "Bank layout cannot handle more than two ports.") self.create_layout() self.add_boundary()
def LVS(self, final_verification=False): """Checks LVS for a module""" import verify # Unit tests will check themselves. # Do not run if disabled in options. # No layout to check if OPTS.netlist_only: return elif (not OPTS.is_unit_test and OPTS.check_lvsdrc and (OPTS.inline_lvsdrc or final_verification)): tempspice = "{0}/{1}.sp".format(OPTS.openram_temp, self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp, self.name) self.lvs_write(tempspice) self.gds_write(tempgds) num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification) debug.check( num_errors == 0, "LVS failed for {0} with {1} error(s)".format( self.name, num_errors)) if OPTS.purge_temp: os.remove(tempspice) os.remove(tempgds)
def __init__(self, sram_config, port, bit_offsets=None, name=""): sram_config.set_local_config(self) self.port = port if self.write_size is not None: self.num_wmasks = int(math.ceil(self.word_size / self.write_size)) else: self.num_wmasks = 0 if self.num_spare_cols is None: self.num_spare_cols = 0 if not bit_offsets: bitcell = factory.create(module_type="bitcell") self.bit_offsets = [] for i in range(self.num_cols + self.num_spare_cols): self.bit_offsets.append(i * bitcell.width) else: self.bit_offsets = bit_offsets if name == "": name = "port_data_{0}".format(self.port) super().__init__(name) debug.info( 2, "create data port of size {0} with {1} words per row".format( self.word_size, self.words_per_row)) self.create_netlist() if not OPTS.netlist_only: debug.check( len(self.all_ports) <= 2, "Bank layout cannot handle more than two ports.") self.create_layout() self.add_boundary()
def retrieve_pins(self, pin_name): """ Retrieve the pin shapes on metal 3 from the layout. """ debug.info(2, "Retrieving pins for {}.".format(pin_name)) shape_list = self.layout.getAllPinShapes(str(pin_name)) pin_set = set() for shape in shape_list: (layer, boundary) = shape # GDSMill boundaries are in (left, bottom, right, top) order # so repack and snap to the grid ll = vector(boundary[0], boundary[1]).snap_to_grid() ur = vector(boundary[2], boundary[3]).snap_to_grid() rect = [ll, ur] pin = pin_layout(pin_name, rect, layer) pin_set.add(pin) debug.check(len(pin_set) > 0, "Did not find any pin shapes for {0}.".format(str(pin_name))) self.pins[pin_name] = pin_set self.all_pins.update(pin_set) for pin in self.pins[pin_name]: debug.info(3, "Retrieved pin {}".format(str(pin)))
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0): """Initializes an instance to represent a module""" geometry.__init__(self) debug.check( mirror not in ["R90", "R180", "R270"], "Please use rotation and not mirroring during instantiation.") self.name = name self.mod = mod self.gds = mod.gds self.rotate = rotate self.offset = vector(offset).snap_to_grid() self.mirror = mirror if OPTS.netlist_only: self.width = 0 self.height = 0 else: if mirror in ["R90", "R270"] or rotate in [90, 270]: self.width = round_to_grid(mod.height) self.height = round_to_grid(mod.width) else: self.width = round_to_grid(mod.width) self.height = round_to_grid(mod.height) self.compute_boundary(offset, mirror, rotate) debug.info(4, "creating instance: " + self.name)
def compute_sizes(self): """ Computes the organization of the memory using bitcell size by trying to make it square.""" debug.check(self.num_banks in [1, 2, 4], "Valid number of banks are 1 , 2 and 4.") self.num_words_per_bank = self.num_words / self.num_banks self.num_bits_per_bank = self.word_size * self.num_words_per_bank # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry) self.bank_area = self.bitcell.width * self.bitcell.height * self.num_bits_per_bank self.bank_side_length = sqrt(self.bank_area) # Estimate the words per row given the height of the bitcell and the square side length self.tentative_num_cols = int(self.bank_side_length / self.bitcell.width) self.words_per_row = self.estimate_words_per_row( self.tentative_num_cols, self.word_size) # Estimate the number of rows given the tentative words per row self.tentative_num_rows = self.num_bits_per_bank / ( self.words_per_row * self.word_size) self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row) # Fix the number of columns and rows self.num_cols = self.words_per_row * self.word_size self.num_rows = self.num_words_per_bank / self.words_per_row # Compute the address and bank sizes self.row_addr_size = int(log(self.num_rows, 2)) self.col_addr_size = int(log(self.words_per_row, 2)) self.bank_addr_size = self.col_addr_size + self.row_addr_size self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
def compute_sizes(self): """ Computes the organization of the memory using bitcell size by trying to make it square.""" self.bitcell = factory.create(module_type="bitcell") debug.check(self.num_banks in [1,2,4], "Valid number of banks are 1 , 2 and 4.") self.num_words_per_bank = self.num_words/self.num_banks self.num_bits_per_bank = self.word_size*self.num_words_per_bank # If this was hard coded, don't dynamically compute it! if not self.words_per_row: # Compute the area of the bitcells and estimate a square bank (excluding auxiliary circuitry) self.bank_area = self.bitcell.width*self.bitcell.height*self.num_bits_per_bank self.bank_side_length = sqrt(self.bank_area) # Estimate the words per row given the height of the bitcell and the square side length self.tentative_num_cols = int(self.bank_side_length/self.bitcell.width) self.words_per_row = self.estimate_words_per_row(self.tentative_num_cols, self.word_size) # Estimate the number of rows given the tentative words per row self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size) self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row) debug.info(1,"Words per row: {}".format(self.words_per_row)) self.recompute_sizes()
def compute_bus_sizes(self): """ Compute the independent bus widths shared between two and four bank SRAMs """ # address size + control signals + one-hot bank select signals self.num_vertical_line = self.addr_size + self.control_size + log( self.num_banks, 2) + 1 # data bus size self.num_horizontal_line = self.word_size self.vertical_bus_width = self.m2_pitch * self.num_vertical_line # vertical bus height depends on 2 or 4 banks self.data_bus_height = self.m3_pitch * self.num_horizontal_line self.data_bus_width = 2 * (self.bank.width + self.bank_to_bus_distance ) + self.vertical_bus_width self.control_bus_height = self.m1_pitch * (self.control_size + 2) self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width self.supply_bus_height = self.m1_pitch * 2 # 2 for vdd/gnd placed with control bus self.supply_bus_width = self.data_bus_width # Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really) debug.check( self.bank.width + self.vertical_bus_width > 0.9 * self.control_logic.width, "Bank is too small compared to control logic.")
def __init__(self, name, size=1, height=None, add_wells=True): """ Creates a cell for a simple 3 input nand """ debug.info(2, "creating pnand4 structure {0} with size of {1}".format(name, size)) self.add_comment("size: {}".format(size)) # We have trouble pitch matching a 3x sizes to the bitcell... # If we relax this, we could size this better. self.size = size self.nmos_size = 2 * size self.pmos_size = parameter["beta"] * size self.nmos_width = self.nmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx") # FIXME: Allow these to be sized debug.check(size == 1, "Size 1 pnand4 is only supported now.") self.tx_mults = 1 if OPTS.tech_name == "sky130": self.nmos_width = self.nearest_bin("nmos", self.nmos_width) self.pmos_width = self.nearest_bin("pmos", self.pmos_width) # Creates the netlist and layout super().__init__(name, height, add_wells)
def import_tech(): global OPTS debug.info(2,"Importing technology: " + OPTS.tech_name) # Set the tech to the config file we read in instead of the command line value. OPTS.tech_name = OPTS.tech_name # environment variable should point to the technology dir try: OPENRAM_TECH = os.path.abspath(os.environ.get("OPENRAM_TECH")) except: debug.error("$OPENRAM_TECH is not properly defined.",1) debug.check(os.path.isdir(OPENRAM_TECH),"$OPENRAM_TECH does not exist: {0}".format(OPENRAM_TECH)) OPTS.openram_tech = OPENRAM_TECH + "/" + OPTS.tech_name if not OPTS.openram_tech.endswith('/'): OPTS.openram_tech += "/" debug.info(1, "Technology path is " + OPTS.openram_tech) try: filename = "setup_openram_{0}".format(OPTS.tech_name) # we assume that the setup scripts (and tech dirs) are located at the # same level as the compielr itself, probably not a good idea though. path = "{0}/setup_scripts".format(os.environ.get("OPENRAM_TECH")) debug.check(os.path.isdir(path),"OPENRAM_TECH does not exist: {0}".format(path)) sys.path.append(os.path.abspath(path)) __import__(filename) except ImportError: debug.error("Nonexistent technology_setup_file: {0}.py".format(filename)) sys.exit(1)
def add_read_one_port(self, comment, address, port): """ Add the control values for a read cycle. Does not increment the period. """ debug.check( port in self.read_ports, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}". format(port, self.read_ports)) debug.info(2, comment) self.fn_cycle_comments.append(comment) self.add_control_one_port(port, "read") self.add_address(address, port) # If the port is also a readwrite then add # the same value as previous cycle if port in self.write_ports: try: self.add_data(self.data_value[port][-1], port) except: self.add_data("0" * (self.word_size + self.num_spare_cols), port) try: self.add_wmask(self.wmask_value[port][-1], port) except: self.add_wmask("0" * self.num_wmasks, port) self.add_spare_wen("0" * self.num_spare_cols, port)
def disassemble(buf, virt, sections, binary): ''' Disassemble a block of x64 code. ''' virts = [(sections[sect]['virt'], sections[sect]['length']) for sect in sections if sections[sect]['virt']] objmin = min(virt[0] for virt in virts) objmax = max(virt[0]+virt[1] for virt in virts) objbounds = (objmin, objmax) FORMAT="INTEL" entries = [virt] result = {} while entries: addr = entries.pop() off = addr-virt while off < len(buf): p = Opcode(buf[off:], mode=64) pre = p.getPrefix() length = p.getSize() try: ins, r, w = p.getOpcode(FORMAT) except ValueError: break ins = repr_ins(ins, r, w, objbounds, sections, binary) if debug.check('asm_rw'): print(ins, r, w) debug_dis = { 'prefix': pre, 'binary': buf[off:off+length] } result[addr] = {'ins': ins, 'loc': addr, 'length': length, 'debug': debug_dis, 'display': True} if ins['op'] == 'return': break # if ins['op'] == 'call': # j_addr = addr+length+ins['dest']['repr'] # if j_addr not in result: # entries.append(j_addr) if ins['op'] == 'jump': j_addr = addr+length+ins['dest']['repr'] if j_addr not in result: entries.append(j_addr) result[addr]['display'] = False if ins['cond'] == 'true': break off += length addr += length return [result[key] for key in sorted(result.keys())]
def output_line(line, indent): ''' Output a line of code. A line contains an instruction and some metainformation about that instruction (its location, etc.) The pseudo-EBNF of lines is as follows: line = location, debug, instruction instruction = op, argument* argument = value, repr | instruction ''' def output_ins(ins): ''' Decompile an instruction. Instructions are trees, see the EBNF for output_line. ''' repr = {} prec = {} for k, arg in ins.items(): if k not in ('src', 'dest'): continue if 'op' in ins[k]: lhs, rhs, prec[k] = output_ins(ins[k]) repr[k] = rhs else: repr[k] = ins[k]['repr'] prec[k] = 20 if type(repr[k]) == int: repr[k] = repr_int(repr[k]) if ins['op'] == 'apply': lhs = '' args = [] for arg in ins['args']: if 'op' in arg: lhs, rhs, inner_prec = output_ins(arg) args.append(rhs) else: args.append(arg['repr']) rhs = '{fun}({args})'.format(fun=ins['function'], args=', '.join(args)) outer_prec = 20 else: try: lhs, rhs, outer_prec = output_op(ins['op']) except KeyError: lhs, rhs = '', '/* Unsupported immediate instruction: {ins[op]} */'.format(ins=ins) outer_prec = 20 for k in dict(repr): if outer_prec > prec[k]: repr[k] = '(' + repr[k] + ')' return lhs.format(i=repr), rhs.format(i=repr), outer_prec lhs, rhs, prec = output_ins(line['ins']) line_repr = indent.out() + lhs + rhs if lhs+rhs != '': line_repr += ';' if not line['display']: line_repr = '' if debug.check('misc'): from binascii import hexlify line_repr += '\n{indent}{blue}/* {line} */{black}'.format(indent=indent.out(), line=line, blue='\033[94m', black='\033[0m') return line_repr