diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 049d2307..63d53d39 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -9,6 +9,7 @@ import collections import geometry import gdsMill import debug +from math import sqrt from tech import drc, GDS from tech import layer as techlayer from tech import layer_stacks @@ -1193,11 +1194,9 @@ class layout(): "supply router." .format(name,inst.name,self.pwr_grid_layer)) - - def add_power_pin(self, name, loc, size=[1, 1], vertical=False, start_layer="m1"): """ - Add a single power pin from the lowest power_grid layer down to M1 at + Add a single power pin from the lowest power_grid layer down to M1 (or li) at the given center location. The starting layer is specified to determine which vias are needed. """ @@ -1211,23 +1210,27 @@ class layout(): else: direction = None - via = self.add_via_stack_center(from_layer=start_layer, to_layer=self.pwr_grid_layer, size=size, offset=loc, direction=direction) - if start_layer == self.pwr_grid_layer: self.add_layout_pin_rect_center(text=name, layer=self.pwr_grid_layer, offset=loc) else: + # Hack for min area + if OPTS.tech_name == "s8": + height = width = sqrt(drc["minarea_m3"]) + else: + width = via.width + height = via.height self.add_layout_pin_rect_center(text=name, layer=self.pwr_grid_layer, offset=loc, - width=via.width, - height=via.height) + width=width, + height=height) def add_power_ring(self, bbox): """ diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 322cc1b3..d8cb2869 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -13,6 +13,7 @@ import debug from globals import OPTS import logical_effort + class sense_amp_array(design.design): """ Array of sense amplifiers to read the bitlines through the column mux. @@ -22,7 +23,7 @@ class sense_amp_array(design.design): def __init__(self, name, word_size, words_per_row): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) - self.add_comment("word_size {0}".format(word_size)) + self.add_comment("word_size {0}".format(word_size)) self.add_comment("words_per_row: {0}".format(words_per_row)) self.word_size = word_size @@ -56,7 +57,7 @@ class sense_amp_array(design.design): def create_layout(self): self.height = self.amp.height - + if self.bitcell.width > self.amp.width: self.width = self.bitcell.width * self.word_size * self.words_per_row else: @@ -69,26 +70,26 @@ class sense_amp_array(design.design): self.DRC_LVS() def add_pins(self): - for i in range(0,self.word_size): + for i in range(0, self.word_size): self.add_pin(self.data_name + "_{0}".format(i), "OUTPUT") self.add_pin(self.get_bl_name() + "_{0}".format(i), "INPUT") self.add_pin(self.get_br_name() + "_{0}".format(i), "INPUT") self.add_pin(self.en_name, "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") - + def add_modules(self): self.amp = factory.create(module_type="sense_amp") - + self.add_mod(self.amp) # This is just used for measurements, # so don't add the module self.bitcell = factory.create(module_type="bitcell") - + def create_sense_amp_array(self): self.local_insts = [] - for i in range(0,self.word_size): + for i in range(0, self.word_size): name = "sa_d{0}".format(i) self.local_insts.append(self.add_inst(name=name, @@ -105,7 +106,7 @@ class sense_amp_array(design.design): else: amp_spacing = self.amp.width * self.words_per_row - for i in range(0,self.word_size): + for i in range(0, self.word_size): xoffset = amp_spacing * i # align the xoffset to the grid of bitcells. This way we @@ -119,20 +120,19 @@ class sense_amp_array(design.design): mirror = "" amp_position = vector(xoffset, 0) - self.local_insts[i].place(offset=amp_position,mirror=mirror) + self.local_insts[i].place(offset=amp_position, mirror=mirror) - def add_layout_pins(self): for i in range(len(self.local_insts)): inst = self.local_insts[i] - - self.add_power_pin(name = "gnd", - loc = inst.get_pin("gnd").center(), + + self.add_power_pin(name="gnd", + loc=inst.get_pin("gnd").center(), start_layer="m2", vertical=True) - self.add_power_pin(name = "vdd", - loc = inst.get_pin("vdd").center(), + self.add_power_pin(name="vdd", + loc=inst.get_pin("vdd").center(), start_layer="m2", vertical=True) @@ -141,43 +141,43 @@ class sense_amp_array(design.design): dout_pin = inst.get_pin(inst.mod.dout_name) self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i), - layer="m2", + layer=bl_pin.layer, offset=bl_pin.ll(), width=bl_pin.width(), height=bl_pin.height()) self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i), - layer="m2", + layer=br_pin.layer, offset=br_pin.ll(), width=br_pin.width(), height=br_pin.height()) self.add_layout_pin(text=self.data_name + "_{0}".format(i), - layer="m2", + layer=dout_pin.layer, offset=dout_pin.ll(), width=dout_pin.width(), height=dout_pin.height()) - - + def route_rails(self): # add sclk rail across entire array - sclk_offset = self.amp.get_pin(self.amp.en_name).ll().scale(0,1) + sclk = self.amp.get_pin(self.amp.en_name) + sclk_offset = self.amp.get_pin(self.amp.en_name).ll().scale(0, 1) self.add_layout_pin(text=self.en_name, - layer="m1", - offset=sclk_offset, - width=self.width, - height=drc("minwidth_m1")) + layer=sclk.layer, + offset=sclk_offset, + width=self.width, + height=drc("minwidth_" + sclk.layer)) def input_load(self): return self.amp.input_load() - + def get_en_cin(self): """Get the relative capacitance of all the sense amp enable connections in the array""" sense_amp_en_cin = self.amp.get_en_cin() return sense_amp_en_cin * self.word_size - + def get_drain_cin(self): """Get the relative capacitance of the drain of the PMOS isolation TX""" from tech import parameter - #Bitcell drain load being used to estimate PMOS drain load + # Bitcell drain load being used to estimate PMOS drain load drain_load = logical_effort.convert_farad_to_relative_c(parameter['bitcell_drain_cap']) return drain_load diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 08a2e007..d1244aed 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -5,25 +5,24 @@ # (acting for and on behalf of Oklahoma State University) # All rights reserved. # -from math import log import design -from tech import drc import debug from sram_factory import factory from vector import vector from globals import OPTS + class write_driver_array(design.design): """ Array of tristate drivers to write to the bitlines through the column mux. Dynamically generated write driver array of all bitlines. """ - def __init__(self, name, columns, word_size,write_size=None): + def __init__(self, name, columns, word_size, write_size=None): design.design.__init__(self, name) debug.info(1, "Creating {0}".format(self.name)) self.add_comment("columns: {0}".format(columns)) - self.add_comment("word_size {0}".format(word_size)) + self.add_comment("word_size {0}".format(word_size)) self.columns = columns self.word_size = word_size @@ -31,7 +30,7 @@ class write_driver_array(design.design): self.words_per_row = int(columns / word_size) if self.write_size: - self.num_wmasks = int(self.word_size/self.write_size) + self.num_wmasks = int(self.word_size / self.write_size) self.create_netlist() if not OPTS.netlist_only: @@ -57,15 +56,15 @@ class write_driver_array(design.design): self.add_modules() self.add_pins() self.create_write_array() - + def create_layout(self): - + if self.bitcell.width > self.driver.width: self.width = self.columns * self.bitcell.width else: self.width = self.columns * self.driver.width self.height = self.driver.height - + self.place_write_array() self.add_layout_pins() self.add_boundary() @@ -97,9 +96,9 @@ class write_driver_array(design.design): self.driver_insts = {} w = 0 windex=0 - for i in range(0,self.columns,self.words_per_row): + for i in range(0, self.columns, self.words_per_row): name = "write_driver{}".format(i) - index = int(i/self.words_per_row) + index = int(i / self.words_per_row) self.driver_insts[index]=self.add_inst(name=name, mod=self.driver) @@ -119,15 +118,14 @@ class write_driver_array(design.design): self.get_br_name() + "_{0}".format(index), self.en_name, "vdd", "gnd"]) - def place_write_array(self): from tech import cell_properties if self.bitcell.width > self.driver.width: self.driver_spacing = self.bitcell.width else: self.driver_spacing = self.driver.width - for i in range(0,self.columns,self.words_per_row): - index = int(i/self.words_per_row) + for i in range(0, self.columns, self.words_per_row): + index = int(i / self.words_per_row) xoffset = i * self.driver_spacing if cell_properties.bitcell.mirror.y and i % 2: @@ -139,26 +137,25 @@ class write_driver_array(design.design): base = vector(xoffset, 0) self.driver_insts[index].place(offset=base, mirror=mirror) - def add_layout_pins(self): for i in range(self.word_size): inst = self.driver_insts[i] din_pin = inst.get_pin(inst.mod.din_name) self.add_layout_pin(text=self.data_name + "_{0}".format(i), - layer="m2", + layer=din_pin.layer, offset=din_pin.ll(), width=din_pin.width(), height=din_pin.height()) bl_pin = inst.get_pin(inst.mod.get_bl_names()) self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i), - layer="m2", + layer=bl_pin.layer, offset=bl_pin.ll(), width=bl_pin.width(), height=bl_pin.height()) br_pin = inst.get_pin(inst.mod.get_br_names()) self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i), - layer="m2", + layer=br_pin.layer, offset=br_pin.ll(), width=br_pin.width(), height=br_pin.height()) @@ -166,16 +163,16 @@ class write_driver_array(design.design): for n in ["vdd", "gnd"]: pin_list = self.driver_insts[i].get_pins(n) for pin in pin_list: - self.add_power_pin(name = n, - loc = pin.center(), + self.add_power_pin(name=n, + loc=pin.center(), vertical=True, - start_layer = "m2") + start_layer="m2") if self.write_size: for bit in range(self.num_wmasks): - inst = self.driver_insts[bit*self.write_size] + inst = self.driver_insts[bit * self.write_size] en_pin = inst.get_pin(inst.mod.en_name) # Determine width of wmask modified en_pin with/without col mux - wmask_en_len = self.words_per_row*(self.write_size * self.driver_spacing) + wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing) if (self.words_per_row == 1): en_gap = self.driver_spacing - en_pin.width() else: @@ -184,19 +181,16 @@ class write_driver_array(design.design): self.add_layout_pin(text=self.en_name + "_{0}".format(bit), layer=en_pin.layer, offset=en_pin.ll(), - width=wmask_en_len-en_gap, + width=wmask_en_len - en_gap, height=en_pin.height()) else: inst = self.driver_insts[0] self.add_layout_pin(text=self.en_name, layer="m1", - offset=inst.get_pin(inst.mod.en_name).ll().scale(0,1), + offset=inst.get_pin(inst.mod.en_name).ll().scale(0, 1), width=self.width) - - - def get_w_en_cin(self): """Get the relative capacitance of all the enable connections in the bank""" - #The enable is connected to a nand2 for every row. + # The enable is connected to a nand2 for every row. return self.driver.get_w_en_cin() * len(self.driver_insts) diff --git a/compiler/pgates/pnand3.py b/compiler/pgates/pnand3.py index 5d6999e6..9aa54733 100644 --- a/compiler/pgates/pnand3.py +++ b/compiler/pgates/pnand3.py @@ -214,14 +214,16 @@ class pnand3(pgate.pgate): "B", position="center") - self.inputC_yoffset = self.inputB_yoffset - m1_pitch + # FIXME: constant hack + self.inputC_yoffset = self.inputB_yoffset - 1.1 * m1_pitch self.route_input_gate(self.pmos3_inst, self.nmos3_inst, self.inputC_yoffset, "C", position="center") - self.inputA_yoffset = self.inputB_yoffset + m1_pitch + # FIXME: constant hack + self.inputA_yoffset = self.inputB_yoffset + 1.1 * m1_pitch self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, diff --git a/compiler/pgates/precharge.py b/compiler/pgates/precharge.py index 56382712..ee0f96d1 100644 --- a/compiler/pgates/precharge.py +++ b/compiler/pgates/precharge.py @@ -96,17 +96,24 @@ class precharge(design.design): height=layer_width) pmos_pin = self.upper_pmos2_inst.get_pin("S") + # center of vdd rail pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y) self.add_path("m1", [pmos_pin.uc(), pmos_vdd_pos]) + + # if enable is not on M1, the supply can be if self.en_layer != "m1": self.add_via_center(layers=self.m1_stack, offset=pmos_vdd_pos) + self.add_power_pin("vdd", + self.well_contact_pos, + vertical=True) - - # Add vdd pin above the transistor - self.add_power_pin("vdd", self.well_contact_pos, vertical=True) + # Hack for li layers + if OPTS.tech_name == "s8": + self.add_via_center(layers=self.li_stack, + offset=self.well_contact_pos) def create_ptx(self): """ @@ -191,7 +198,6 @@ class precharge(design.design): if self.en_layer == "m2": self.add_via_center(layers=self.m1_stack, offset=offset) - # adds the en rail on metal1 self.add_layout_pin_segment_center(text="en_bar", @@ -205,9 +211,11 @@ class precharge(design.design): """ # adds the contact from active to metal1 - self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \ - + vector(0, self.upper_pmos1_inst.uy() + contact.active_contact.height / 2 \ - + self.nwell_extend_active) + offset_height = self.upper_pmos1_inst.uy() + \ + 0.5 * contact.active_contact.height + \ + self.nwell_extend_active + self.well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) + \ + vector(0, offset_height) self.add_via_center(layers=self.active_stack, offset=self.well_contact_pos, implant_type="n", diff --git a/compiler/pgates/ptx.py b/compiler/pgates/ptx.py index dee475c9..8fbb94d8 100644 --- a/compiler/pgates/ptx.py +++ b/compiler/pgates/ptx.py @@ -99,7 +99,7 @@ class ptx(design.design): body_dir = 'POWER' dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir] self.add_pin_list(pin_list, dir_list) - + # self.spice.append("\n.SUBCKT {0} {1}".format(self.name, # " ".join(self.pins))) # Just make a guess since these will actually diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py index 3baf51f0..88b5a9aa 100755 --- a/compiler/tests/09_sense_amp_array_test.py +++ b/compiler/tests/09_sense_amp_array_test.py @@ -22,6 +22,10 @@ class sense_amp_test(openram_test): globals.init_openram(config_file) # check sense amp array for single port + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=1") + a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=1) + self.local_check(a) + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=2) self.local_check(a) @@ -29,7 +33,7 @@ class sense_amp_test(openram_test): debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4) self.local_check(a) - + # check sense amp array for multi-port OPTS.bitcell = "pbitcell" OPTS.num_rw_ports = 1 @@ -44,9 +48,9 @@ class sense_amp_test(openram_test): debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=4 (multi-port case)") a = factory.create(module_type="sense_amp_array", word_size=4, words_per_row=4) self.local_check(a) - + globals.end_openram() - + # run the test from the command line if __name__ == "__main__": (OPTS, args) = globals.parse_args() diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index 63aeaabe..1d3562cc 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -22,7 +22,6 @@ and include its appropriate license. import os import re -import time import shutil import debug from globals import OPTS @@ -67,26 +66,29 @@ def write_magic_script(cell_name, extract=False, final_verification=False): else: pre = "" if final_verification: - f.write(pre+"extract unique all\n".format(cell_name)) - f.write(pre+"extract\n".format(cell_name)) - #f.write(pre+"ext2spice hierarchy on\n") - #f.write(pre+"ext2spice scale off\n") + f.write(pre + "extract unique all\n".format(cell_name)) + # Hack to work around unit scales in SkyWater + if OPTS.tech_name=="s8": + f.write(pre + "extract style ngspice(si)\n") + f.write(pre + "extract\n".format(cell_name)) + # f.write(pre + "ext2spice hierarchy on\n") + # f.write(pre + "ext2spice scale off\n") # lvs exists in 8.2.79, but be backword compatible for now - #f.write(pre+"ext2spice lvs\n") - f.write(pre+"ext2spice hierarchy on\n") - f.write(pre+"ext2spice format ngspice\n") - f.write(pre+"ext2spice cthresh infinite\n") - f.write(pre+"ext2spice rthresh infinite\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 auto\n") - f.write(pre+"ext2spice global off\n") + # f.write(pre + "ext2spice lvs\n") + f.write(pre + "ext2spice hierarchy on\n") + f.write(pre + "ext2spice format ngspice\n") + f.write(pre + "ext2spice cthresh infinite\n") + f.write(pre + "ext2spice rthresh infinite\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 auto\n") + f.write(pre + "ext2spice global off\n") # Can choose hspice, ngspice, or spice3, # but they all seem compatible enough. - #f.write(pre+"ext2spice format ngspice\n") - f.write(pre+"ext2spice {}\n".format(cell_name)) + #f.write(pre + "ext2spice format ngspice\n") + f.write(pre + "ext2spice {}\n".format(cell_name)) f.write("quit -noprompt\n") f.write("EOF\n")