From 5e1f64c8f94972c122c0d165a1fb3339ca29f2c9 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 17 Feb 2020 14:09:50 +0100 Subject: [PATCH 01/13] modules/port_data: Add get_bl/br_name method if we rely on the names of the submodules (sense_amp_array, write_driver_array, etc.) for port_data's pins, we get into trouble on multiport SRAMs. To avoid this we use explicit names for br/bl depending on the port number in port_data. Now each submodule does no longer need to figure out the right name depending on the port number. Signed-off-by: Bastian Koppelmann --- compiler/modules/port_data.py | 32 +++++++++++++------ compiler/modules/precharge_array.py | 15 +++------ compiler/modules/sense_amp_array.py | 15 +++------ .../modules/single_level_column_mux_array.py | 13 ++------ compiler/modules/write_driver_array.py | 14 +++----- 5 files changed, 37 insertions(+), 52 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index d1c2c671..7776c045 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -46,7 +46,19 @@ class port_data(design.design): # br lines are connect from the precharger return self.precharge.get_br_names() + def get_bl_name(self, port=0): + bl_name = "bl" + if len(self.all_ports) == 1: + return bl_name + else: + return bl_name + "{}".format(port) + def get_br_name(self, port=0): + br_name = "br" + if len(self.all_ports) == 1: + return br_name + else: + return br_name + "{}".format(port) def create_netlist(self): self.precompute_constants() @@ -94,8 +106,8 @@ class port_data(design.design): self.add_pin("rbl_bl","INOUT") self.add_pin("rbl_br","INOUT") for bit in range(self.num_cols): - bl_name = self.precharge_array.get_bl_name(self.port) - br_name = self.precharge_array.get_br_name(self.port) + bl_name = self.get_bl_name(self.port) + br_name = self.get_br_name(self.port) self.add_pin("{0}_{1}".format(bl_name, bit),"INOUT") self.add_pin("{0}_{1}".format(br_name, bit),"INOUT") if self.port in self.read_ports: @@ -253,8 +265,8 @@ class port_data(design.design): self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port), mod=self.precharge_array) - bl_name = self.precharge_array.get_bl_name(self.port) - br_name = self.precharge_array.get_br_name(self.port) + bl_name = self.get_bl_name(self.port) + br_name = self.get_br_name(self.port) temp = [] # Use left BLs for RBL @@ -285,8 +297,8 @@ class port_data(design.design): self.column_mux_array_inst = self.add_inst(name="column_mux_array{}".format(self.port), mod=self.column_mux_array) - bl_name = self.column_mux_array.get_bl_name(self.port) - br_name = self.column_mux_array.get_br_name(self.port) + bl_name = self.get_bl_name(self.port) + br_name = self.get_br_name(self.port) temp = [] for col in range(self.num_cols): temp.append("{0}_{1}".format(bl_name, col)) @@ -315,8 +327,8 @@ class port_data(design.design): self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port), mod=self.sense_amp_array) - bl_name = self.sense_amp_array.get_bl_name(self.port) - br_name = self.sense_amp_array.get_br_name(self.port) + bl_name = self.get_bl_name(self.port) + br_name = self.get_br_name(self.port) temp = [] for bit in range(self.word_size): temp.append("dout_{}".format(bit)) @@ -341,8 +353,8 @@ class port_data(design.design): """ Creating Write Driver """ self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port), mod=self.write_driver_array) - bl_name = self.write_driver_array.get_bl_name(self.port) - br_name = self.write_driver_array.get_br_name(self.port) + bl_name = self.get_bl_name(self.port) + br_name = self.get_br_name(self.port) temp = [] for bit in range(self.word_size): diff --git a/compiler/modules/precharge_array.py b/compiler/modules/precharge_array.py index 5ac97b4d..efc9c4a3 100644 --- a/compiler/modules/precharge_array.py +++ b/compiler/modules/precharge_array.py @@ -32,20 +32,13 @@ class precharge_array(design.design): if not OPTS.netlist_only: self.create_layout() - def get_bl_name(self, port=0): + def get_bl_name(self): bl_name = self.pc_cell.get_bl_names() - if len(self.all_ports) == 1: - return bl_name - else: - return bl_name + "{}".format(port) + return bl_name - def get_br_name(self, port=0): + def get_br_name(self): br_name = self.pc_cell.get_br_names() - if len(self.all_ports) == 1: - return br_name - else: - return br_name + "{}".format(port) - + return br_name def add_pins(self): """Adds pins for spice file""" diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 8ac5146c..f62b7a1d 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -33,20 +33,13 @@ class sense_amp_array(design.design): if not OPTS.netlist_only: self.create_layout() - - def get_bl_name(self, port=0): + def get_bl_name(self): bl_name = self.amp.get_bl_names() - if len(self.all_ports) == 1: - return bl_name - else: - return bl_name + "{}".format(port) + return bl_name - def get_br_name(self, port=0): + def get_br_name(self): br_name = self.amp.get_br_names() - if len(self.all_ports) == 1: - return br_name - else: - return br_name + "{}".format(port) + return br_name def create_netlist(self): self.add_modules() diff --git a/compiler/modules/single_level_column_mux_array.py b/compiler/modules/single_level_column_mux_array.py index 1c181574..324b7415 100644 --- a/compiler/modules/single_level_column_mux_array.py +++ b/compiler/modules/single_level_column_mux_array.py @@ -37,20 +37,13 @@ class single_level_column_mux_array(design.design): if not OPTS.netlist_only: self.create_layout() - def get_bl_name(self, port=0): + def get_bl_name(self): bl_name = self.mux.get_bl_names() - if len(self.all_ports) == 1: - return bl_name - else: - return bl_name + "{}".format(port) + return bl_name def get_br_name(self, port=0): br_name = self.mux.get_br_names() - if len(self.all_ports) == 1: - return br_name - else: - return br_name + "{}".format(port) - + return br_name def create_netlist(self): self.add_modules() diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index dd7430a9..e6aaa118 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -37,19 +37,13 @@ class write_driver_array(design.design): if not OPTS.netlist_only: self.create_layout() - def get_bl_name(self, port=0): + def get_bl_name(self): bl_name = self.driver.get_bl_names() - if len(self.all_ports) == 1: - return bl_name - else: - return bl_name + "{}".format(port) + return bl_name - def get_br_name(self, port=0): + def get_br_name(self): br_name = self.driver.get_br_names() - if len(self.all_ports) == 1: - return br_name - else: - return br_name + "{}".format(port) + return br_name def create_netlist(self): self.add_modules() From 656fdd10083c5567be96a5cf2e53d08c353c784c Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Fri, 14 Feb 2020 14:00:43 +0100 Subject: [PATCH 02/13] port_data: Refactor channel_route/connect_bitlines() both functions share a lot of code and are passing around a lot of data under similar names (inst1, inst1_start_bit, inst1_bl_name, ...). Thus we group all these elements in a named tuple to ease passing around these elements. All callers of channel_route/connect_bitlines() either pass in the bl/br names or rely on "br_{}"/"bl_{}" as defaults. These hard coded values should be determined by the instances. Thus we get the bitline names based on the instances passed in. The callers only provide a template string, to take care of the case that bitlines are called "bl_out_{}". Signed-off-by: Bastian Koppelmann --- compiler/modules/port_data.py | 159 ++++++++++++++++++++++------------ 1 file changed, 106 insertions(+), 53 deletions(-) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 7776c045..7c37d670 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -8,6 +8,7 @@ from tech import drc, parameter import debug import design from sram_factory import factory +from collections import namedtuple from vector import vector from globals import OPTS @@ -535,13 +536,16 @@ class port_data(design.design): # Only do this if we have a column mux! if self.col_addr_size==0: return - + inst1 = self.column_mux_array_inst inst2 = self.precharge_array_inst - if self.port==0: - self.connect_bitlines(inst1, inst2, self.num_cols, inst2_start_bit=1) - else: - self.connect_bitlines(inst1, inst2, self.num_cols) + + insn2_start_bit = 1 if self.port == 0 else 0 + + self.connect_bitlines(inst1=inst1, + inst2=inst2, + num_bits=self.num_cols, + inst2_start_bit=insn2_start_bit) def route_sense_amp_to_column_mux_or_precharge_array(self, port): @@ -551,46 +555,49 @@ class port_data(design.design): if self.col_addr_size>0: # Sense amp is connected to the col mux inst1 = self.column_mux_array_inst - inst1_bl_name = "bl_out_{}" - inst1_br_name = "br_out_{}" + inst1_bls_templ = "{inst}_out_{bit}" start_bit = 0 else: # Sense amp is directly connected to the precharge array inst1 = self.precharge_array_inst - inst1_bl_name = "bl_{}" - inst1_br_name = "br_{}" + inst1_bls_templ="{inst}_{bit}" + if self.port==0: start_bit=1 else: start_bit=0 - - self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, - inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit) + self.channel_route_bitlines(inst1=inst1, + inst1_bls_template=inst1_bls_templ, + inst2=inst2, + num_bits=self.word_size, + inst1_start_bit=start_bit) def route_write_driver_to_column_mux_or_precharge_array(self, port): """ Routing of BL and BR between sense_amp and column mux or precharge array """ inst2 = self.write_driver_array_inst - + inst2_bl_name = inst2.mod.get_bl_name() + inst2_br_name = inst2.mod.get_br_name() + if self.col_addr_size>0: # Write driver is connected to the col mux inst1 = self.column_mux_array_inst - inst1_bl_name = "bl_out_{}" - inst1_br_name = "br_out_{}" + inst1_bls_templ = "{inst}_out_{bit}" start_bit = 0 else: # Sense amp is directly connected to the precharge array inst1 = self.precharge_array_inst - inst1_bl_name = "bl_{}" - inst1_br_name = "br_{}" + inst1_bls_templ="{inst}_{bit}" if self.port==0: start_bit=1 else: start_bit=0 - - self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size, - inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit) + + self.channel_route_bitlines(inst1=inst1, inst2=inst2, + num_bits=self.word_size, + inst1_bls_template=inst1_bls_templ, + inst1_start_bit=start_bit) def route_write_driver_to_sense_amp(self, port): @@ -601,7 +608,9 @@ class port_data(design.design): # These should be pitch matched in the cell library, # but just in case, do a channel route. - self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size) + self.channel_route_bitlines(inst1=inst1, + inst2=inst2, + num_bits=self.word_size) def route_bitline_pins(self): @@ -647,64 +656,108 @@ class port_data(design.design): if self.write_mask_and_array_inst: self.copy_layout_pin(self.write_mask_and_array_inst, "en", "w_en") - - - def channel_route_bitlines(self, inst1, inst2, num_bits, - inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0, - inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0): + + + def _group_bitline_instances(self, inst1, inst2, num_bits, + inst1_bls_template, + inst1_start_bit, + inst2_bls_template, + inst2_start_bit): """ - Route the bl and br of two modules using the channel router. + Groups all the parameters into a named tuple and seperates them into + top and bottom instances. """ - + inst_group = namedtuple('InstanceGroup', ('inst', 'bls_template', + 'bl_name', 'br_name', 'start_bit')) + + inst1_group = inst_group(inst1, inst1_bls_template, + inst1.mod.get_bl_name(), + inst1.mod.get_br_name(), + inst1_start_bit) + inst2_group = inst_group(inst2, inst2_bls_template, + inst2.mod.get_bl_name(), + inst2.mod.get_br_name(), + inst2_start_bit) # 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_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit) - (top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit) + bot_inst_group = inst1_group + top_inst_group = inst2_group else: - (bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit) - (top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit) + bot_inst_group = inst2_group + top_inst_group = inst1_group + return (bot_inst_group, top_inst_group) + + def _get_bitline_pins(self, inst_group, bit): + """ + Extracts bl/br pins from an InstanceGroup based on the bit modifier. + """ + full_bl_name = inst_group.bls_template.format( + **{'inst' : inst_group.bl_name, + 'bit' : inst_group.start_bit + bit} + ) + full_br_name = inst_group.bls_template.format( + **{'inst' : inst_group.br_name, + 'bit' : inst_group.start_bit + bit} + ) + return (inst_group.inst.get_pin(full_bl_name), + inst_group.inst.get_pin(full_br_name)) + + + def channel_route_bitlines(self, inst1, inst2, num_bits, + inst1_bls_template="{inst}_{bit}", + inst1_start_bit=0, + inst2_bls_template="{inst}_{bit}", + inst2_start_bit=0): + """ + Route the bl and br of two modules using the channel router. + """ + + bot_inst_group, top_inst_group = self._group_bitline_instances( + inst1, inst2, num_bits, + inst1_bls_template, inst1_start_bit, + inst2_bls_template, inst2_start_bit) # Channel route each mux separately since we don't minimize the number # of tracks in teh channel router yet. If we did, we could route all the bits at once! - offset = bottom_inst.ul() + vector(0,self.m1_pitch) + offset = bot_inst_group.inst.ul() + vector(0,self.m1_pitch) for bit in range(num_bits): - bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit+bottom_start_bit)), bottom_inst.get_pin(bottom_br_name.format(bit+bottom_start_bit))] - top_names = [top_inst.get_pin(top_bl_name.format(bit+top_start_bit)), top_inst.get_pin(top_br_name.format(bit+top_start_bit))] + bottom_names = self._get_bitline_pins(bot_inst_group, bit) + top_names = self._get_bitline_pins(top_inst_group, bit) + route_map = list(zip(bottom_names, top_names)) self.create_horizontal_channel_route(route_map, offset, self.m1_stack) def connect_bitlines(self, inst1, inst2, num_bits, - inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0, - inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0): - + inst1_bls_template="{inst}_{bit}", + inst1_start_bit=0, + inst2_bls_template="{inst}_{bit}", + inst2_start_bit=0): """ Connect the bl and br 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_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit) - (top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit) - else: - (bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit) - (top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit) + bot_inst_group, top_inst_group = self._group_bitline_instances( + inst1, inst2, num_bits, + inst1_bls_template, inst1_start_bit, + inst2_bls_template, inst2_start_bit) + bottom_inst = bot_inst_group.inst + top_inst = top_inst_group.inst for col in range(num_bits): - bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col+bottom_start_bit)).uc() - bottom_br = bottom_inst.get_pin(bottom_br_name.format(col+bottom_start_bit)).uc() - top_bl = top_inst.get_pin(top_bl_name.format(col+top_start_bit)).bc() - top_br = top_inst.get_pin(top_br_name.format(col+top_start_bit)).bc() + bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col) + top_bl_pin, top_br_pin = self._get_bitline_pins(top_inst_group, col) + bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc() + top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc() - yoffset = 0.5*(top_bl.y+bottom_bl.y) - self.add_path("m2",[bottom_bl, vector(bottom_bl.x,yoffset), + yoffset = 0.5*(top_bl.y+bot_bl.y) + self.add_path("m2",[bot_bl, vector(bot_bl.x,yoffset), vector(top_bl.x,yoffset), top_bl]) - self.add_path("m2",[bottom_br, vector(bottom_br.x,yoffset), + self.add_path("m2",[bot_br, vector(bot_br.x,yoffset), vector(top_br.x,yoffset), top_br]) def graph_exclude_precharge(self): From c06cb2bfc2be827196311c533ab0c362e1c8d150 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 17 Feb 2020 14:23:26 +0100 Subject: [PATCH 03/13] write_driver/array: Remove hardcoded pin names all pin names should be wrapped into a function/property. This ensures that there is exactly one place to change the name. Signed-off-by: Bastian Koppelmann --- compiler/modules/write_driver.py | 8 ++++ compiler/modules/write_driver_array.py | 61 +++++++++++++++----------- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/compiler/modules/write_driver.py b/compiler/modules/write_driver.py index db08bcf4..78920a10 100644 --- a/compiler/modules/write_driver.py +++ b/compiler/modules/write_driver.py @@ -43,6 +43,14 @@ class write_driver(design.design): def get_br_names(self): return "br" + @property + def din_name(self): + return "din" + + @property + def en_name(self): + return "en" + def get_w_en_cin(self): """Get the relative capacitance of a single input""" # This is approximated from SCMOS. It has roughly 5 3x transistor gates. diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index e6aaa118..16233c88 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -45,6 +45,14 @@ class write_driver_array(design.design): br_name = self.driver.get_br_names() return br_name + @property + def data_name(self): + return "data" + + @property + def en_name(self): + return "en" + def create_netlist(self): self.add_modules() self.add_pins() @@ -65,15 +73,15 @@ class write_driver_array(design.design): def add_pins(self): for i in range(self.word_size): - self.add_pin("data_{0}".format(i), "INPUT") - for i in range(self.word_size): - self.add_pin("bl_{0}".format(i), "OUTPUT") - self.add_pin("br_{0}".format(i), "OUTPUT") + self.add_pin(self.data_name + "_{0}".format(i), "INPUT") + for i in range(self.word_size): + self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT") + self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT") if self.write_size: for i in range(self.num_wmasks): - self.add_pin("en_{0}".format(i), "INPUT") + self.add_pin(self.en_name + "_{0}".format(i), "INPUT") else: - self.add_pin("en", "INPUT") + self.add_pin(self.en_name, "INPUT") self.add_pin("vdd", "POWER") self.add_pin("gnd", "GROUND") @@ -96,20 +104,20 @@ class write_driver_array(design.design): mod=self.driver) if self.write_size: - self.connect_inst(["data_{0}".format(index), - "bl_{0}".format(index), - "br_{0}".format(index), - "en_{0}".format(windex), "vdd", "gnd"]) + self.connect_inst([self.data_name + "_{0}".format(index), + self.get_bl_name() + "_{0}".format(index), + self.get_br_name() + "_{0}".format(index), + self.en_name + "_{0}".format(windex), "vdd", "gnd"]) w+=1 # when w equals write size, the next en pin can be connected since we are now at the next wmask bit if w == self.write_size: w = 0 windex+=1 else: - self.connect_inst(["data_{0}".format(index), - "bl_{0}".format(index), - "br_{0}".format(index), - "en", "vdd", "gnd"]) + self.connect_inst([self.data_name + "_{0}".format(index), + self.get_bl_name() + "_{0}".format(index), + self.get_br_name() + "_{0}".format(index), + self.en_name, "vdd", "gnd"]) def place_write_array(self): @@ -134,21 +142,22 @@ class write_driver_array(design.design): def add_layout_pins(self): for i in range(self.word_size): - din_pin = self.driver_insts[i].get_pin("din") - self.add_layout_pin(text="data_{0}".format(i), + 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", offset=din_pin.ll(), width=din_pin.width(), height=din_pin.height()) - bl_pin = self.driver_insts[i].get_pin("bl") - self.add_layout_pin(text="bl_{0}".format(i), + bl_pin = inst.get_pin(inst.mod.get_bl_names()) + self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i), layer="m2", offset=bl_pin.ll(), width=bl_pin.width(), height=bl_pin.height()) - - br_pin = self.driver_insts[i].get_pin("br") - self.add_layout_pin(text="br_{0}".format(i), + + br_pin = inst.get_pin(inst.mod.get_br_names()) + self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i), layer="m2", offset=br_pin.ll(), width=br_pin.width(), @@ -163,7 +172,8 @@ class write_driver_array(design.design): start_layer = "m2") if self.write_size: for bit in range(self.num_wmasks): - en_pin = self.driver_insts[bit*self.write_size].get_pin("en") + 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) if (self.words_per_row == 1): @@ -171,15 +181,16 @@ class write_driver_array(design.design): else: en_gap = self.driver_spacing - self.add_layout_pin(text="en_{0}".format(bit), + 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, height=en_pin.height()) else: - self.add_layout_pin(text="en", + inst = self.driver_insts[0] + self.add_layout_pin(text=self.en_name, layer="m1", - offset=self.driver_insts[0].get_pin("en").ll().scale(0,1), + offset=inst.get_pin(inst.mod.en_name).ll().scale(0,1), width=self.width) From 9a12b68680167d7277ca64ba5c7496640393cacf Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 17 Feb 2020 14:25:00 +0100 Subject: [PATCH 04/13] write_driver: Allow custom pin names we don't want to propagate the write driver bl/br names out of the write_driver_array. Thus the write_driver_array gets them named as "bl"/"br" again. Signed-off-by: Bastian Koppelmann --- compiler/base/custom_cell_properties.py | 9 + compiler/modules/write_driver.py | 17 +- compiler/modules/write_driver_array.py | 4 +- compiler/modules/write_driver_array.py.orig | 202 ++++++++++++++++++++ 4 files changed, 225 insertions(+), 7 deletions(-) create mode 100644 compiler/modules/write_driver_array.py.orig diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index 45887561..04d27379 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -116,6 +116,12 @@ class cell_properties(): self._dff_buff_array = _dff_buff_array(use_custom_ports = False, add_body_contacts = False) + self._write_driver = _cell({'din': 'din', + 'bl' : 'bl', + 'br' : 'br', + 'en' : 'en'}) + + @property def bitcell(self): return self._bitcell @@ -132,3 +138,6 @@ class cell_properties(): def dff_buff_array(self): return self._dff_buff_array + @property + def write_driver(self): + return self._write_driver diff --git a/compiler/modules/write_driver.py b/compiler/modules/write_driver.py index 78920a10..9afac81b 100644 --- a/compiler/modules/write_driver.py +++ b/compiler/modules/write_driver.py @@ -10,6 +10,7 @@ import design import utils from globals import OPTS from tech import GDS,layer +from tech import cell_properties as props class write_driver(design.design): """ @@ -19,7 +20,13 @@ class write_driver(design.design): the technology library. """ - pin_names = ["din", "bl", "br", "en", "vdd", "gnd"] + pin_names = [props.write_driver.pin.din, + props.write_driver.pin.bl, + props.write_driver.pin.br, + props.write_driver.pin.en, + props.write_driver.pin.vdd, + props.write_driver.pin.gnd] + type_list = ["INPUT", "OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] if not OPTS.netlist_only: (width,height) = utils.get_libcell_size("write_driver", GDS["unit"], layer["boundary"]) @@ -38,18 +45,18 @@ class write_driver(design.design): self.add_pin_types(self.type_list) def get_bl_names(self): - return "bl" + return props.write_driver.pin.bl def get_br_names(self): - return "br" + return props.write_driver.pin.br @property def din_name(self): - return "din" + return props.write_driver.pin.din @property def en_name(self): - return "en" + return props.write_driver.pin.en def get_w_en_cin(self): """Get the relative capacitance of a single input""" diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 16233c88..08a2e007 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -38,11 +38,11 @@ class write_driver_array(design.design): self.create_layout() def get_bl_name(self): - bl_name = self.driver.get_bl_names() + bl_name = "bl" return bl_name def get_br_name(self): - br_name = self.driver.get_br_names() + br_name = "br" return br_name @property diff --git a/compiler/modules/write_driver_array.py.orig b/compiler/modules/write_driver_array.py.orig new file mode 100644 index 00000000..16233c88 --- /dev/null +++ b/compiler/modules/write_driver_array.py.orig @@ -0,0 +1,202 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +from 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): + 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.columns = columns + self.word_size = word_size + self.write_size = write_size + self.words_per_row = int(columns / word_size) + + if self.write_size: + self.num_wmasks = int(self.word_size/self.write_size) + + self.create_netlist() + if not OPTS.netlist_only: + self.create_layout() + + def get_bl_name(self): + bl_name = self.driver.get_bl_names() + return bl_name + + def get_br_name(self): + br_name = self.driver.get_br_names() + return br_name + + @property + def data_name(self): + return "data" + + @property + def en_name(self): + return "en" + + def create_netlist(self): + 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() + self.DRC_LVS() + + def add_pins(self): + for i in range(self.word_size): + self.add_pin(self.data_name + "_{0}".format(i), "INPUT") + for i in range(self.word_size): + self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT") + self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT") + if self.write_size: + for i in range(self.num_wmasks): + self.add_pin(self.en_name + "_{0}".format(i), "INPUT") + else: + self.add_pin(self.en_name, "INPUT") + self.add_pin("vdd", "POWER") + self.add_pin("gnd", "GROUND") + + def add_modules(self): + self.driver = factory.create(module_type="write_driver") + self.add_mod(self.driver) + + # This is just used for measurements, + # so don't add the module + self.bitcell = factory.create(module_type="bitcell") + + def create_write_array(self): + self.driver_insts = {} + w = 0 + windex=0 + for i in range(0,self.columns,self.words_per_row): + name = "write_driver{}".format(i) + index = int(i/self.words_per_row) + self.driver_insts[index]=self.add_inst(name=name, + mod=self.driver) + + if self.write_size: + self.connect_inst([self.data_name + "_{0}".format(index), + self.get_bl_name() + "_{0}".format(index), + self.get_br_name() + "_{0}".format(index), + self.en_name + "_{0}".format(windex), "vdd", "gnd"]) + w+=1 + # when w equals write size, the next en pin can be connected since we are now at the next wmask bit + if w == self.write_size: + w = 0 + windex+=1 + else: + self.connect_inst([self.data_name + "_{0}".format(index), + self.get_bl_name() + "_{0}".format(index), + 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) + xoffset = i * self.driver_spacing + + if cell_properties.bitcell.mirror.y and i % 2: + mirror = "MY" + xoffset = xoffset + self.driver.width + else: + mirror = "" + + 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", + 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", + 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", + offset=br_pin.ll(), + width=br_pin.width(), + height=br_pin.height()) + + 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(), + vertical=True, + start_layer = "m2") + if self.write_size: + for bit in range(self.num_wmasks): + 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) + if (self.words_per_row == 1): + en_gap = self.driver_spacing - en_pin.width() + else: + en_gap = self.driver_spacing + + 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, + 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), + 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. + return self.driver.get_w_en_cin() * len(self.driver_insts) From 680dc6d2c7a2069a616cebcb91acd2b0bd669ad3 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 17 Feb 2020 14:25:55 +0100 Subject: [PATCH 05/13] sense_amp/array: Remove hardcoded pin names all pin names should be wrapped into a function/property. This ensures that there is exactly one place to change the name. Signed-off-by: Bastian Koppelmann --- compiler/modules/sense_amp.py | 11 ++++++-- compiler/modules/sense_amp_array.py | 44 +++++++++++++++++------------ 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index eb624111..0318d7bc 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -35,6 +35,14 @@ class sense_amp(design.design): def get_br_names(self): return "br" + @property + def dout_name(self): + return "dout" + + @property + def en_name(self): + return "en" + def __init__(self, name): design.design.__init__(self, name) debug.info(2, "Create sense_amp") @@ -79,11 +87,10 @@ class sense_amp(design.design): def get_enable_name(self): """Returns name used for enable net""" #FIXME: A better programmatic solution to designate pins - enable_name = "en" + 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 build_graph(self, graph, inst_name, port_nets): """Adds edges based on inputs/outputs. Overrides base class function.""" self.add_graph_edges(graph, port_nets) - \ No newline at end of file diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index f62b7a1d..452c0767 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -41,6 +41,14 @@ class sense_amp_array(design.design): br_name = self.amp.get_br_names() return br_name + @property + def data_name(self): + return "data" + + @property + def en_name(self): + return "en" + def create_netlist(self): self.add_modules() self.add_pins() @@ -62,10 +70,10 @@ class sense_amp_array(design.design): def add_pins(self): for i in range(0,self.word_size): - self.add_pin("data_{0}".format(i), "OUTPUT") - self.add_pin("bl_{0}".format(i), "INPUT") - self.add_pin("br_{0}".format(i), "INPUT") - self.add_pin("en", "INPUT") + 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") @@ -85,10 +93,10 @@ class sense_amp_array(design.design): name = "sa_d{0}".format(i) self.local_insts.append(self.add_inst(name=name, mod=self.amp)) - self.connect_inst(["bl_{0}".format(i), - "br_{0}".format(i), - "data_{0}".format(i), - "en", "vdd", "gnd"]) + self.connect_inst([self.get_bl_name() + "_{0}".format(i), + self.get_br_name() + "_{0}".format(i), + self.data_name + "_{0}".format(i), + self.en_name, "vdd", "gnd"]) def place_sense_amp_array(self): from tech import cell_properties @@ -128,22 +136,22 @@ class sense_amp_array(design.design): start_layer="m2", vertical=True) - bl_pin = inst.get_pin("bl") - br_pin = inst.get_pin("br") - dout_pin = inst.get_pin("dout") - - self.add_layout_pin(text="bl_{0}".format(i), + bl_pin = inst.get_pin(inst.mod.get_bl_names()) + br_pin = inst.get_pin(inst.mod.get_br_names()) + dout_pin = inst.get_pin(inst.mod.dout_name) + + self.add_layout_pin(text=self.get_bl_name() + "_{0}".format(i), layer="m2", offset=bl_pin.ll(), width=bl_pin.width(), height=bl_pin.height()) - self.add_layout_pin(text="br_{0}".format(i), + self.add_layout_pin(text=self.get_br_name() + "_{0}".format(i), layer="m2", offset=br_pin.ll(), width=br_pin.width(), height=br_pin.height()) - - self.add_layout_pin(text="data_{0}".format(i), + + self.add_layout_pin(text=self.data_name + "_{0}".format(i), layer="m2", offset=dout_pin.ll(), width=dout_pin.width(), @@ -152,8 +160,8 @@ class sense_amp_array(design.design): def route_rails(self): # add sclk rail across entire array - sclk_offset = self.amp.get_pin("en").ll().scale(0,1) - self.add_layout_pin(text="en", + 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, From 76256a2f1bc2587fd9cc6554754d657772b67941 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Mon, 17 Feb 2020 14:27:35 +0100 Subject: [PATCH 06/13] sense_amp: Allow custom pin names we don't want to propagate the sense amp's bl/br names out of the sense_amp_array. Thus the sense_amp_array gets them named as "bl"/"br" again. Signed-off-by: Bastian Koppelmann --- compiler/base/custom_cell_properties.py | 9 ++++++++- compiler/modules/sense_amp.py | 17 +++++++++++------ compiler/modules/sense_amp_array.py | 4 ++-- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/compiler/base/custom_cell_properties.py b/compiler/base/custom_cell_properties.py index 04d27379..ba671279 100644 --- a/compiler/base/custom_cell_properties.py +++ b/compiler/base/custom_cell_properties.py @@ -120,7 +120,10 @@ class cell_properties(): 'bl' : 'bl', 'br' : 'br', 'en' : 'en'}) - + self._sense_amp = _cell({'bl' : 'bl', + 'br' : 'br', + 'dout' : 'dout', + 'en' : 'en'}) @property def bitcell(self): @@ -141,3 +144,7 @@ class cell_properties(): @property def write_driver(self): return self._write_driver + + @property + def sense_amp(self): + return self._sense_amp diff --git a/compiler/modules/sense_amp.py b/compiler/modules/sense_amp.py index 0318d7bc..ff5638ba 100644 --- a/compiler/modules/sense_amp.py +++ b/compiler/modules/sense_amp.py @@ -9,6 +9,7 @@ import design import debug import utils from tech import GDS,layer, parameter,drc +from tech import cell_properties as props from globals import OPTS import logical_effort @@ -19,8 +20,12 @@ class sense_amp(design.design): the technology library. Sense amplifier to read a pair of bit-lines. """ - - pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"] + pin_names = [props.sense_amp.pin.bl, + props.sense_amp.pin.br, + props.sense_amp.pin.dout, + props.sense_amp.pin.en, + props.sense_amp.pin.vdd, + props.sense_amp.pin.gnd] type_list = ["INPUT", "INPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] if not OPTS.netlist_only: (width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"]) @@ -30,18 +35,18 @@ class sense_amp(design.design): pin_map = [] def get_bl_names(self): - return "bl" + return props.sense_amp.pin.bl def get_br_names(self): - return "br" + return props.sense_amp.pin.br @property def dout_name(self): - return "dout" + return props.sense_amp.pin.dout @property def en_name(self): - return "en" + return props.sense_amp.pin.en def __init__(self, name): design.design.__init__(self, name) diff --git a/compiler/modules/sense_amp_array.py b/compiler/modules/sense_amp_array.py index 452c0767..322cc1b3 100644 --- a/compiler/modules/sense_amp_array.py +++ b/compiler/modules/sense_amp_array.py @@ -34,11 +34,11 @@ class sense_amp_array(design.design): self.create_layout() def get_bl_name(self): - bl_name = self.amp.get_bl_names() + bl_name = "bl" return bl_name def get_br_name(self): - br_name = self.amp.get_br_names() + br_name = "br" return br_name @property From 843fce41d73e7dd34531e950aa8ecc3872dd662e Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 19 Feb 2020 03:06:11 -0800 Subject: [PATCH 07/13] Fixed issues with sen control logic for read ports. --- compiler/characterizer/delay.py | 2 +- compiler/modules/control_logic.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 2a8d5293..94e38ff2 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -650,7 +650,7 @@ class delay(simulation): debug.error("Timed out, could not find a feasible period.",2) # Clear any write target ports and set read port - self.targ_write_ports = [port] + self.targ_write_ports = [] self.targ_read_ports = [port] debug.info(1, "Trying feasible period: {0}ns on Port {1}".format(feasible_period, port)) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index e22d05b3..458b9b96 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -647,7 +647,7 @@ class control_logic(design.design): if self.port_type=="rw": input_name = "we_bar" else: - input_name = "cs_bar" + input_name = "cs" # GATE FOR S_EN self.s_en_gate_inst = self.add_inst(name="buf_s_en_and", mod=self.sen_and3) From e4fef73e3f3d6e80e1e0cd801e223533a293737b Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 19 Feb 2020 15:34:31 -0800 Subject: [PATCH 08/13] Fixed issues with bitcell measurements variable names, made target write ports required during characterization --- compiler/characterizer/delay.py | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 94e38ff2..a9f9542c 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -180,30 +180,30 @@ class delay(simulation): def create_read_bit_measures(self): """ Adds bit measurements for read0 and read1 cycles """ - self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} + self.read_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} meas_cycles = (sram_op.READ_ZERO, sram_op.READ_ONE) for cycle in meas_cycles: meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name) single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data) for polarity,meas in single_bit_meas.items(): meas.meta_str = cycle - self.bit_meas[polarity].append(meas) + self.read_bit_meas[polarity].append(meas) # Dictionary values are lists, reduce to a single list of measurements - return [meas for meas_list in self.bit_meas.values() for meas in meas_list] + return [meas for meas_list in self.read_bit_meas.values() for meas in meas_list] def create_write_bit_measures(self): """ Adds bit measurements for write0 and write1 cycles """ - self.bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} + self.write_bit_meas = {bit_polarity.NONINVERTING:[], bit_polarity.INVERTING:[]} meas_cycles = (sram_op.WRITE_ZERO, sram_op.WRITE_ONE) for cycle in meas_cycles: meas_tag = "a{}_b{}_{}".format(self.probe_address, self.probe_data, cycle.name) single_bit_meas = self.get_bit_measures(meas_tag, self.probe_address, self.probe_data) for polarity,meas in single_bit_meas.items(): meas.meta_str = cycle - self.bit_meas[polarity].append(meas) + self.write_bit_meas[polarity].append(meas) # Dictionary values are lists, reduce to a single list of measurements - return [meas for meas_list in self.bit_meas.values() for meas in meas_list] + return [meas for meas_list in self.write_bit_meas.values() for meas in meas_list] def get_bit_measures(self, meas_tag, probe_address, probe_data): """ @@ -649,8 +649,9 @@ class delay(simulation): if (time_out <= 0): debug.error("Timed out, could not find a feasible period.",2) - # Clear any write target ports and set read port - self.targ_write_ports = [] + # Write ports are assumed non-critical to timing, so the first available is used + self.targ_write_ports = [self.write_ports[0]] + # Set target read port for simulation self.targ_read_ports = [port] debug.info(1, "Trying feasible period: {0}ns on Port {1}".format(feasible_period, port)) @@ -733,7 +734,8 @@ class delay(simulation): # First, check that the memory has the right values at the right times - if not self.check_bit_measures(): + if not self.check_bit_measures(self.read_bit_meas) or \ + not self.check_bit_measures(self.write_bit_meas): return(False,{}) for port in self.targ_write_ports: @@ -824,13 +826,13 @@ class delay(simulation): return dout_success - def check_bit_measures(self): + def check_bit_measures(self, bit_measures): """ Checks the measurements which represent the internal storage voltages at the end of the read cycle. """ success = False - for polarity, meas_list in self.bit_meas.items(): + for polarity, meas_list in bit_measures.items(): for meas in meas_list: val = meas.retrieve_measure() debug.info(2,"{}={}".format(meas.name, val)) @@ -965,7 +967,8 @@ class delay(simulation): # Binary search algorithm to find the min period (max frequency) of input port time_out = 25 - self.targ_write_ports = [port] + # Write ports are assumed non-critical to timing, so the first available is used + self.targ_write_ports = [self.write_ports[0]] self.targ_read_ports = [port] while True: time_out -= 1 @@ -1253,8 +1256,8 @@ class delay(simulation): """ # Using this requires setting at least one port to target for simulation. - if len(self.targ_write_ports) == 0 and len(self.targ_read_ports) == 0: - debug.error("No port selected for characterization.",1) + if len(self.targ_write_ports) == 0 or len(self.targ_read_ports) == 0: + debug.error("Write and read port must be specified for characterization.",1) self.set_stimulus_variables() # Get any available read/write port in case only a single write or read ports is being characterized. From df2f981a34a38ea1760e3c02b3847ec7cf4e069c Mon Sep 17 00:00:00 2001 From: Hunter Nichols Date: Wed, 19 Feb 2020 15:59:26 -0800 Subject: [PATCH 09/13] Adds checks to prevent characterization of redundant corners. --- compiler/characterizer/lib.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 6baff201..6d6c6ce5 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -81,17 +81,25 @@ class lib: self.lib_files = [] # Nominal corner - self.add_corner(nom_process, nom_supply, nom_temperature) + corner_set = set() + nom_corner = (nom_process, nom_supply, nom_temperature) + corner_set.add(nom_corner) if not OPTS.nominal_corner_only: # Temperature corners - self.add_corner(nom_process, nom_supply, min_temperature) - self.add_corner(nom_process, nom_supply, max_temperature) + corner_set.add((nom_process, nom_supply, min_temperature)) + corner_set.add((nom_process, nom_supply, max_temperature)) # Supply corners - self.add_corner(nom_process, min_supply, nom_temperature) - self.add_corner(nom_process, max_supply, nom_temperature) + corner_set.add((nom_process, min_supply, nom_temperature)) + corner_set.add((nom_process, max_supply, nom_temperature)) # Process corners - self.add_corner(min_process, nom_supply, nom_temperature) - self.add_corner(max_process, nom_supply, nom_temperature) + corner_set.add((min_process, nom_supply, nom_temperature)) + corner_set.add((max_process, nom_supply, nom_temperature)) + + # Enforce that nominal corner is the first to be characterized + self.add_corner(*nom_corner) + corner_set.remove(nom_corner) + for corner_tuple in corner_set: + self.add_corner(*corner_tuple) def add_corner(self, proc, volt, temp): self.corner_name = "{0}_{1}_{2}V_{3}C".format(self.sram.name, From d6987ac584b188f765c5ba8a129fae17ddf50bfe Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Wed, 19 Feb 2020 16:26:52 -0800 Subject: [PATCH 10/13] added purposes to addText(), removed reference to specific tech from gdsMill --- compiler/base/geometry.py | 55 +++++----- compiler/base/pin_layout.py | 77 +++++++------- compiler/gdsMill/gdsMill/gdsPrimitives.py | 9 +- compiler/gdsMill/gdsMill/vlsiLayout.py | 124 +++++++++++----------- 4 files changed, 130 insertions(+), 135 deletions(-) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index f354cf02..0041d3c9 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -51,7 +51,7 @@ class geometry: y = item[0] * math.sin(angle) + item[1] * mirr * math.cos(angle) + offset[1] coordinate += [[x, y]] return coordinate - + def normalize(self): """ Re-find the LL and UR points after a transform """ (first, second) = self.boundary @@ -64,14 +64,14 @@ class geometry: def update_boundary(self): """ Update the boundary with a new placement. """ self.compute_boundary(self.offset, self.mirror, self.rotate) - + def compute_boundary(self, offset=vector(0, 0), mirror="", rotate=0): """ Transform with offset, mirror and rotation to get the absolute pin location. We must then re-find the ll and ur. The master is the cell instance. """ if OPTS.netlist_only: self.boundary = [vector(0,0), vector(0,0)] return - + (ll, ur) = [vector(0, 0), vector(self.width, self.height)] if mirror == "MX": @@ -83,7 +83,7 @@ class geometry: elif mirror == "XY": ll = ll.scale(-1, -1) ur = ur.scale(-1, -1) - + if rotate == 90: ll = ll.rotate_scale(-1, 1) ur = ur.rotate_scale(-1, 1) @@ -96,19 +96,19 @@ class geometry: self.boundary = [offset + ll, offset + ur] self.normalize() - + def ll(self): """ Return the lower left corner """ return self.boundary[0] - + def ur(self): """ Return the upper right corner """ return self.boundary[1] - + def lr(self): """ Return the lower right corner """ return vector(self.boundary[1].x, self.boundary[0].y) - + def ul(self): """ Return the upper left corner """ return vector(self.boundary[0].x, self.boundary[1].y) @@ -132,12 +132,12 @@ class geometry: def cx(self): """ Return the center x """ return 0.5 * (self.boundary[0].x + self.boundary[1].x) - + def cy(self): """ Return the center y """ return 0.5 * (self.boundary[0].y + self.boundary[1].y) - - + + class instance(geometry): """ An instance of an instance/module with a specified location and @@ -148,7 +148,7 @@ class instance(geometry): 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 @@ -166,7 +166,7 @@ class instance(geometry): 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 get_blockages(self, lpp, top=False): @@ -202,11 +202,11 @@ class instance(geometry): new_blockages.append(self.transform_coords(b,self.offset, mirr, angle)) return new_blockages - + def gds_write_file(self, new_layout): """Recursively writes all the sub-modules in this instance""" debug.info(4, "writing instance: " + self.name) - # make sure to write out my module/structure + # make sure to write out my module/structure # (it will only be written the first time though) self.mod.gds_write_file(self.gds) # now write an instance of my module/structure @@ -215,7 +215,7 @@ class instance(geometry): offsetInMicrons=self.offset, mirror=self.mirror, rotate=self.rotate) - + def place(self, offset, mirror="R0", rotate=0): """ This updates the placement of an instance. """ # Update the placement of an already added instance @@ -224,8 +224,8 @@ class instance(geometry): self.rotate = rotate self.update_boundary() debug.info(3, "placing instance {}".format(self)) - - + + def get_pin(self,name,index=-1): """ Return an absolute pin that is offset and transformed based on this instance location. Index will return one of several pins.""" @@ -243,20 +243,20 @@ class instance(geometry): def get_num_pins(self, name): """ Return the number of pins of a given name """ return len(self.mod.get_pins(name)) - + def get_pins(self,name): """ Return an absolute pin that is offset and transformed based on this instance location. """ - + import copy pin = copy.deepcopy(self.mod.get_pins(name)) - + new_pins = [] for p in pin: - p.transform(self.offset,self.mirror,self.rotate) + p.transform(self.offset,self.mirror,self.rotate) new_pins.append(p) return new_pins - + def __str__(self): """ override print function output """ return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")" @@ -293,7 +293,7 @@ class path(geometry): def get_blockages(self, layer): """ Fail since we don't support paths yet. """ assert(0) - + def __str__(self): """ override print function output """ return "path: layer=" + self.layerNumber + " w=" + self.width @@ -329,6 +329,7 @@ class label(geometry): debug.info(4, "writing label (" + str(self.layerNumber) + "): " + self.text) new_layout.addText(text=self.text, layerNumber=self.layerNumber, + layerPurpose=self.layerPurpose, offsetInMicrons=self.offset, magnification=self.zoom, rotate=None) @@ -336,7 +337,7 @@ class label(geometry): def get_blockages(self, layer): """ Returns an empty list since text cannot be blockages. """ return [] - + def __str__(self): """ override print function output """ return "label: " + self.text + " layer=" + str(self.layerNumber) @@ -345,7 +346,7 @@ class label(geometry): """ override print function output """ return "( label: " + self.text + " @" + str(self.offset) + " layer=" + str(self.layerNumber) + " )" - + class rectangle(geometry): """Represents a rectangular shape""" @@ -363,7 +364,7 @@ class rectangle(geometry): debug.info(4, "creating rectangle (" + str(self.layerNumber) + "): " + str(self.width) + "x" + str(self.height) + " @ " + str(self.offset)) - + def get_blockages(self, layer): """ Returns a list of one rectangle if it is on this layer""" if self.layerNumber == layer: diff --git a/compiler/base/pin_layout.py b/compiler/base/pin_layout.py index a057f3e0..f18956a8 100644 --- a/compiler/base/pin_layout.py +++ b/compiler/base/pin_layout.py @@ -30,7 +30,7 @@ class pin_layout: debug.check(self.width() > 0, "Zero width pin.") debug.check(self.height() > 0, "Zero height pin.") - + # if it's a string, use the name if type(layer_name_pp) == str: self._layer = layer_name_pp @@ -93,17 +93,17 @@ class pin_layout: is a major speedup, if pin_layout is used as a key for dicts. """ return self._hash - + def __lt__(self, other): """ Provide a function for ordering items by the ll point """ (ll, ur) = self.rect (oll, our) = other.rect - + if ll.x < oll.x and ll.y < oll.y: return True - + return False - + def __eq__(self, other): """ Check if these are the same pins for duplicate checks """ if isinstance(other, self.__class__): @@ -128,14 +128,14 @@ class pin_layout: max_y = max(max_y, pin.ur().y) self.rect = [vector(min_x, min_y), vector(max_x, max_y)] - + def fix_minarea(self): """ Try to fix minimum area rule. """ min_area = drc("{}_minarea".format(self.layer)) pass - + def inflate(self, spacing=None): """ Inflate the rectangle by the spacing (or other rule) @@ -143,12 +143,12 @@ class pin_layout: """ if not spacing: spacing = 0.5*drc("{0}_to_{0}".format(self.layer)) - + (ll, ur) = self.rect spacing = vector(spacing, spacing) newll = ll - spacing newur = ur + spacing - + return (newll, newur) def intersection(self, other): @@ -191,7 +191,7 @@ class pin_layout: y_overlaps = True return y_overlaps - + def xcontains(self, other): """ Check if shape contains the x overlap """ (ll, ur) = self.rect @@ -205,13 +205,13 @@ class pin_layout: (oll, our) = other.rect return (oll.y >= ll.y and our.y <= ur.y) - + def contains(self, other): """ Check if a shape contains another rectangle """ # If it is the same shape entirely, it is contained! if self == other: return True - + # Can only overlap on the same layer if not self.same_lpp(self.lpp, other.lpp): return False @@ -230,13 +230,13 @@ class pin_layout: if shape.contains(self): return True return False - + def overlaps(self, other): """ Check if a shape overlaps with a rectangle """ # Can only overlap on the same layer if not self.same_lpp(self.lpp, other.lpp): return False - + x_overlaps = self.xoverlaps(other) y_overlaps = self.yoverlaps(other) @@ -245,11 +245,11 @@ class pin_layout: def area(self): """ Return the area. """ return self.height()*self.width() - + def height(self): """ Return height. Abs is for pre-normalized value.""" return abs(self.rect[1].y-self.rect[0].y) - + def width(self): """ Return width. Abs is for pre-normalized value.""" return abs(self.rect[1].x-self.rect[0].x) @@ -260,7 +260,7 @@ class pin_layout: ll = vector(min(first[0], second[0]), min(first[1], second[1])) ur = vector(max(first[0], second[0]), max(first[1], second[1])) self.rect=[ll, ur] - + def transform(self, offset, mirror, rotate): """ Transform with offset, mirror and rotation @@ -278,7 +278,7 @@ class pin_layout: elif mirror == "XY": ll = ll.scale(-1, -1) ur = ur.scale(-1, -1) - + if rotate == 90: ll = ll.rotate_scale(-1, 1) ur = ur.rotate_scale(-1, 1) @@ -303,7 +303,7 @@ class pin_layout: def cy(self): """ Center y """ return 0.5*(self.rect[0].y+self.rect[1].y) - + # The four possible corners def ll(self): """ Lower left point """ @@ -320,7 +320,7 @@ class pin_layout: def ur(self): """ Upper right point """ return self.rect[1] - + # The possible y edge values def uy(self): """ Upper y value """ @@ -331,15 +331,15 @@ class pin_layout: return self.rect[0].y # The possible x edge values - + def lx(self): """ Left x value """ return self.rect[0].x - + def rx(self): """ Right x value """ return self.rect[1].x - + # The edge centers def rc(self): """ Right center point """ @@ -350,7 +350,7 @@ class pin_layout: """ Left center point """ return vector(self.rect[0].x, 0.5*(self.rect[0].y+self.rect[1].y)) - + def uc(self): """ Upper center point """ return vector(0.5*(self.rect[0].x+self.rect[1].x), @@ -378,6 +378,7 @@ class pin_layout: # imported into Magic. newLayout.addText(text=self.name, layerNumber=layer_num, + purposeNumber=purpose, offsetInMicrons=self.center(), magnification=GDS["zoom"], rotate=None) @@ -392,7 +393,7 @@ class pin_layout: dy = min(r1_ur.y, r2_ur.y) - max(r1_ll.y, r2_ll.y) dx = min(r1_ur.x, r2_ur.x) - max(r1_ll.x, r2_ll.x) - + if dx >= 0 and dy >= 0: return [dx, dy] else: @@ -407,7 +408,7 @@ class pin_layout: def dist(x1, y1, x2, y2): return math.sqrt((x2-x1)**2 + (y2-y1)**2) - + left = r2_ur.x < r1_ll.x right = r1_ur.x < r2_ll.x bottom = r2_ur.y < r1_ll.y @@ -432,7 +433,7 @@ class pin_layout: else: # rectangles intersect return 0 - + def overlap_length(self, other): """ Calculate the intersection segment and determine its length @@ -453,7 +454,7 @@ class pin_layout: # This is where we had a corner intersection or none return 0 - + def compute_overlap_segment(self, other): """ Calculate the intersection segment of two rectangles @@ -469,19 +470,19 @@ class pin_layout: r2_lr = vector(r2_ur.x, r2_ll.y) from itertools import tee - + def pairwise(iterable): "s -> (s0,s1), (s1,s2), (s2, s3), ..." a, b = tee(iterable) next(b, None) return zip(a, b) - + # R1 edges CW r1_cw_points = [r1_ll, r1_ul, r1_ur, r1_lr, r1_ll] r1_edges = [] for (p, q) in pairwise(r1_cw_points): r1_edges.append([p, q]) - + # R2 edges CW r2_cw_points = [r2_ll, r2_ul, r2_ur, r2_lr, r2_ll] r2_edges = [] @@ -509,9 +510,9 @@ class pin_layout: q.y <= max(p.y, r.y) and \ q.y >= min(p.y, r.y): return True - + return False - + def segment_intersection(self, s1, s2): """ Determine the intersection point of two segments @@ -524,22 +525,22 @@ class pin_layout: a1 = b.y - a.y b1 = a.x - b.x c1 = a1*a.x + b1*a.y - + # Line CD represented as a2x + b2y = c2 a2 = d.y - c.y b2 = c.x - d.x c2 = a2*c.x + b2*c.y - + determinant = a1*b2 - a2*b1 if determinant != 0: x = (b2*c1 - b1*c2)/determinant y = (a1*c2 - a2*c1)/determinant - + r = vector(x, y).snap_to_grid() if self.on_segment(a, r, b) and self.on_segment(c, r, d): return r - + return None def same_lpp(self, lpp1, lpp2): @@ -549,5 +550,5 @@ class pin_layout: """ if lpp1[1] == None or lpp2[1] == None: return lpp1[0] == lpp2[0] - + return lpp1[0] == lpp2[0] and lpp1[1] == lpp2[1] diff --git a/compiler/gdsMill/gdsMill/gdsPrimitives.py b/compiler/gdsMill/gdsMill/gdsPrimitives.py index 13b8acf9..8e07524c 100644 --- a/compiler/gdsMill/gdsMill/gdsPrimitives.py +++ b/compiler/gdsMill/gdsMill/gdsPrimitives.py @@ -1,12 +1,5 @@ import math -from globals import OPTS -# default purpose layer is used for addText() in vlsiLayout.py -if OPTS.tech_name == "s8": - purposeLayer=20 -else: - purposeLayer=0 - class GdsStructure: """Class represent a GDS Structure Object""" def __init__(self): @@ -147,7 +140,7 @@ class GdsText: self.elementFlags="" self.plex="" self.drawingLayer="" - self.purposeLayer=purposeLayer + self.purposeLayer=0 self.transFlags=[0,0,0] self.magFactor="" self.rotateAngle="" diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py index 412a430c..979180cd 100644 --- a/compiler/gdsMill/gdsMill/vlsiLayout.py +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -35,7 +35,7 @@ class VlsiLayout: modDate.hour, modDate.minute, modDate.second) - + self.info = dict() #information gathered from the GDSII header self.info['units']=self.units self.info['dates']=(modDate.year, @@ -52,12 +52,12 @@ class VlsiLayout: modDate.second) self.info['libraryName']=libraryName self.info['gdsVersion']=gdsVersion - + self.xyTree = [] #This will contain a list of all structure names #expanded to include srefs / arefs separately. #each structure will have an X,Y,offset, and rotate associated #with it. Populate via traverseTheHierarchy method. - + #temp variables used in delegate functions self.tempCoordinates=None self.tempPassFail = True @@ -73,14 +73,14 @@ class VlsiLayout: if(rotateAngle): angle = math.radians(float(rotateAngle)) - coordinatesRotate = [] #this will hold the rotated values + coordinatesRotate = [] #this will hold the rotated values for coordinate in coordinatesToRotate: # This is the CCW rotation matrix newX = coordinate[0]*math.cos(angle) - coordinate[1]*math.sin(angle) newY = coordinate[0]*math.sin(angle) + coordinate[1]*math.cos(angle) coordinatesRotate.extend((newX,newY)) return coordinatesRotate - + def rename(self,newName): #take the root structure and copy it to a new structure with the new name self.structures[newName] = self.structures[self.rootStructureName] @@ -129,8 +129,8 @@ class VlsiLayout: modDate.hour, modDate.minute, modDate.second) - - + + #repopulate the 2d map so drawing occurs correctly self.prepareForWrite() @@ -155,15 +155,15 @@ class VlsiLayout: debug.check(len(structureNames)==1,"Multiple possible root structures in the layout: {}".format(str(structureNames))) self.rootStructureName = structureNames[0] - - def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None, + + def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None, transformPath = [], rotateAngle = 0, transFlags = [0,0,0], coordinates = (0,0)): #since this is a recursive function, must deal with the default - #parameters explicitly + #parameters explicitly if startingStructureName == None: - startingStructureName = self.rootStructureName + startingStructureName = self.rootStructureName - #set up the rotation matrix + #set up the rotation matrix if(rotateAngle == None or rotateAngle == ""): angle = 0 else: @@ -193,10 +193,10 @@ class VlsiLayout: try: if(len(self.structures[startingStructureName].srefs)>0): #does this structure reference any others? #if so, go through each and call this function again - #if not, return back to the caller (caller can be this function) + #if not, return back to the caller (caller can be this function) for sref in self.structures[startingStructureName].srefs: - #here, we are going to modify the sref coordinates based on the parent objects rotation - self.traverseTheHierarchy(startingStructureName = sref.sName, + #here, we are going to modify the sref coordinates based on the parent objects rotation + self.traverseTheHierarchy(startingStructureName = sref.sName, delegateFunction = delegateFunction, transformPath = transformPath, rotateAngle = sref.rotateAngle, @@ -204,12 +204,12 @@ class VlsiLayout: coordinates = sref.coordinates) except KeyError: debug.error("Could not find structure {} in GDS file.".format(startingStructureName),-1) - + #MUST HANDLE AREFs HERE AS WELL #when we return, drop the last transform from the transformPath del transformPath[-1] return - + def initialize(self): self.deduceHierarchy() # self.traverseTheHierarchy() @@ -217,17 +217,17 @@ class VlsiLayout: for layerNumber in self.layerNumbersInUse: self.processLabelPins((layerNumber, None)) - - + + def populateCoordinateMap(self): def addToXyTree(startingStructureName = None,transformPath = None): uVector = np.array([[1.0],[0.0],[0.0]]) #start with normal basis vectors vVector = np.array([[0.0],[1.0],[0.0]]) origin = np.array([[0.0],[0.0],[1.0]]) #and an origin (Z component is 1.0 to indicate position instead of vector) - #make a copy of all the transforms and reverse it + #make a copy of all the transforms and reverse it reverseTransformPath = transformPath[:] if len(reverseTransformPath) > 1: - reverseTransformPath.reverse() + reverseTransformPath.reverse() #now go through each transform and apply them to our basis and origin in succession for transform in reverseTransformPath: origin = np.dot(transform[0], origin) #rotate @@ -237,20 +237,20 @@ class VlsiLayout: uVector = np.dot(transform[1], uVector) #scale vVector = np.dot(transform[1], vVector) #scale origin = np.dot(transform[2], origin) #translate - #we don't need to do a translation on the basis vectors + #we don't need to do a translation on the basis vectors #uVector = transform[2] * uVector #translate #vVector = transform[2] * vVector #translate #populate the xyTree with each structureName and coordinate space self.xyTree.append((startingStructureName,origin,uVector,vVector)) self.traverseTheHierarchy(delegateFunction = addToXyTree) - + def microns(self, userUnits): """Utility function to convert user units to microns""" userUnit = self.units[1]/self.units[0] userUnitsPerMicron = userUnit / userunit layoutUnitsPerMicron = userUnitsPerMicron / self.units[0] return userUnits / layoutUnitsPerMicron - + def userUnits(self, microns): """Utility function to convert microns to user units""" userUnit = self.units[1]/self.units[0] @@ -270,7 +270,7 @@ class VlsiLayout: if self.debug: debug.info(0,"DEBUG: GdsMill vlsiLayout: changeRoot: %s "%newRoot) - + # Determine if newRoot exists # layoutToAdd (default) or nameOfLayout if (newRoot == 0 | ((newRoot not in self.structures) & ~create)): @@ -282,19 +282,19 @@ class VlsiLayout: self.rootStructureName = newRoot - + def addInstance(self,layoutToAdd,nameOfLayout=0,offsetInMicrons=(0,0),mirror=None,rotate=None): """ Method to insert one layout into another at a particular offset. """ offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1])) - if self.debug: + if self.debug: debug.info(0,"DEBUG: GdsMill vlsiLayout: addInstance: type {0}, nameOfLayout {1}".format(type(layoutToAdd),nameOfLayout)) debug.info(0,"DEBUG: name={0} offset={1} mirror={2} rotate={3}".format(layoutToAdd.rootStructureName,offsetInMicrons, mirror, rotate)) - # Determine if we are instantiating the root design of + # Determine if we are instantiating the root design of # layoutToAdd (default) or nameOfLayout if nameOfLayout == 0: StructureFound = True @@ -303,7 +303,7 @@ class VlsiLayout: StructureName = nameOfLayout #layoutToAdd StructureFound = False for structure in layoutToAdd.structures: - if StructureName in structure: + if StructureName in structure: if self.debug: debug.info(1,"DEBUG: Structure %s Found"%StructureName) StructureFound = True @@ -311,7 +311,7 @@ class VlsiLayout: debug.check(StructureFound,"Could not find layout to instantiate {}".format(StructureName)) - # If layoutToAdd is a unique object (not this), then copy hierarchy, + # If layoutToAdd is a unique object (not this), then copy hierarchy, # otherwise, if it is a text name of an internal structure, use it. if layoutToAdd != self: @@ -330,7 +330,7 @@ class VlsiLayout: layoutToAddSref.coordinates = offsetInLayoutUnits if mirror or rotate: - + layoutToAddSref.transFlags = [0,0,0] # transFlags = (mirror around x-axis, magnification, rotation) # If magnification or rotation is true, it is the flags are then @@ -356,7 +356,7 @@ class VlsiLayout: #add the sref to the root structure self.structures[self.rootStructureName].srefs.append(layoutToAddSref) - + def addBox(self,layerNumber=0, purposeNumber=0, offsetInMicrons=(0,0), width=1.0, height=1.0,center=False): """ Method to add a box to a layout @@ -373,7 +373,7 @@ class VlsiLayout: (offsetInLayoutUnits[0],offsetInLayoutUnits[1]+heightInLayoutUnits), offsetInLayoutUnits] else: - startPoint = (offsetInLayoutUnits[0]-widthInLayoutUnits/2.0, offsetInLayoutUnits[1]-heightInLayoutUnits/2.0) + startPoint = (offsetInLayoutUnits[0]-widthInLayoutUnits/2.0, offsetInLayoutUnits[1]-heightInLayoutUnits/2.0) coordinates=[startPoint, (startPoint[0]+widthInLayoutUnits,startPoint[1]), (startPoint[0]+widthInLayoutUnits,startPoint[1]+heightInLayoutUnits), @@ -386,7 +386,7 @@ class VlsiLayout: boundaryToAdd.purposeLayer = purposeNumber #add the sref to the root structure self.structures[self.rootStructureName].boundaries.append(boundaryToAdd) - + def addPath(self, layerNumber=0, purposeNumber=0, coordinates=[(0,0)], width=1.0): """ Method to add a path to a layout @@ -405,11 +405,12 @@ class VlsiLayout: pathToAdd.coordinates = layoutUnitCoordinates #add the sref to the root structure self.structures[self.rootStructureName].paths.append(pathToAdd) - - def addText(self, text, layerNumber=0, offsetInMicrons=(0,0), magnification=0.1, rotate = None): + + def addText(self, text, layerNumber=0, purposeNumber=0, offsetInMicrons=(0,0), magnification=0.1, rotate = None): offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1])) textToAdd = GdsText() textToAdd.drawingLayer = layerNumber + textToAdd.purposeLayer = purposeNumber textToAdd.coordinates = [offsetInLayoutUnits] textToAdd.transFlags = [0,0,0] textToAdd.textString = self.padText(text) @@ -438,7 +439,7 @@ class VlsiLayout: return 1 else: return 0 - + def intersectionPoint(self,startPoint1,endPoint1,startPoint2,endPoint2): if((endPoint1[0]-startPoint1[0])!=0 and (endPoint2[0]-startPoint2[0])!=0): pSlope = (endPoint1[1]-startPoint1[1])/(endPoint1[0]-startPoint1[0]) @@ -458,7 +459,7 @@ class VlsiLayout: newY = None elif((endPoint1[0]-startPoint1[0])==0 and (endPoint2[0]-startPoint2[0])!=0): qSlope = (endPoint2[1]-startPoint2[1])/(endPoint2[0]-startPoint2[0]) - qIntercept = startPoint2[1]-qSlope*startPoint2[0] + qIntercept = startPoint2[1]-qSlope*startPoint2[0] newX=endPoint1[0] newY=qSlope*newX+qIntercept elif((endPoint1[0]-startPoint1[0])!=0 and (endPoint2[0]-startPoint2[0])==0): @@ -467,14 +468,14 @@ class VlsiLayout: newX=endPoint2[0] newY=pSlope*newX+pIntercept return (newX,newY) - + def isCollinear(self,testPoint,point1,point2): slope1 = (testPoint[1]-point1[1])/(testPoint[0]-point1[0]) slope2 = (point2[1]-point1[1])/(point2[0]-point1[0]) if slope1 == slope2: return True return False - + def doShapesIntersect(self,shape1Coordinates, shape2Coordinates): """ Utility function to determine if 2 arbitrary shapes intersect. @@ -491,7 +492,7 @@ class VlsiLayout: if(self.isBounded(intersect,startPoint1,endPoint1) and self.isBounded(intersect,startPoint2,endPoint2)): return True #these shapes overlap! return False #these shapes are ok - + def isPointInsideOfBox(self,pointCoordinates,boxCoordinates): """ Check if a point is contained in the shape @@ -516,7 +517,7 @@ class VlsiLayout: pointCoordinates[1] Date: Wed, 19 Feb 2020 23:32:11 -0800 Subject: [PATCH 11/13] Changed layout input names of s_en AND gate to match the schematic --- compiler/modules/control_logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/modules/control_logic.py b/compiler/modules/control_logic.py index 458b9b96..1612938d 100644 --- a/compiler/modules/control_logic.py +++ b/compiler/modules/control_logic.py @@ -670,7 +670,7 @@ class control_logic(design.design): if self.port_type=="rw": input_name = "we_bar" else: - input_name = "cs_bar" + input_name = "cs" sen_map = zip(["A", "B", "C"], ["rbl_bl_delay", "gated_clk_bar", input_name]) self.connect_vertical_bus(sen_map, self.s_en_gate_inst, self.rail_offsets) From c9cb387912b62b13ec3eed726ae98de20a445233 Mon Sep 17 00:00:00 2001 From: Joey Kunzler Date: Thu, 20 Feb 2020 18:35:54 -0800 Subject: [PATCH 12/13] fixed variable typo --- compiler/base/geometry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/base/geometry.py b/compiler/base/geometry.py index 0041d3c9..cfda3553 100644 --- a/compiler/base/geometry.py +++ b/compiler/base/geometry.py @@ -329,7 +329,7 @@ class label(geometry): debug.info(4, "writing label (" + str(self.layerNumber) + "): " + self.text) new_layout.addText(text=self.text, layerNumber=self.layerNumber, - layerPurpose=self.layerPurpose, + purposeNumber=self.layerPurpose, offsetInMicrons=self.offset, magnification=self.zoom, rotate=None) From 0e641bf9054e55197cbd4988a5765cd7590bb1f7 Mon Sep 17 00:00:00 2001 From: Bastian Koppelmann Date: Fri, 21 Feb 2020 13:29:43 +0100 Subject: [PATCH 13/13] Remove write_driver_array.py.orig this was the remainder of applying a diff using "patch". To avoid this mistake, add the filetypes created by "patch" to the .gitignore. Signed-off-by: Bastian Koppelmann --- .gitignore | 4 +- compiler/modules/write_driver_array.py.orig | 202 -------------------- 2 files changed, 3 insertions(+), 203 deletions(-) delete mode 100644 compiler/modules/write_driver_array.py.orig diff --git a/.gitignore b/.gitignore index b16e3d0b..948e7d19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ .DS_Store *~ +*.orig +*.rej *.pyc *.aux *.out *.toc *.synctex.gz -**/model_data \ No newline at end of file +**/model_data diff --git a/compiler/modules/write_driver_array.py.orig b/compiler/modules/write_driver_array.py.orig deleted file mode 100644 index 16233c88..00000000 --- a/compiler/modules/write_driver_array.py.orig +++ /dev/null @@ -1,202 +0,0 @@ -# See LICENSE for licensing information. -# -# Copyright (c) 2016-2019 Regents of the University of California and The Board -# of Regents for the Oklahoma Agricultural and Mechanical College -# (acting for and on behalf of Oklahoma State University) -# All rights reserved. -# -from 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): - 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.columns = columns - self.word_size = word_size - self.write_size = write_size - self.words_per_row = int(columns / word_size) - - if self.write_size: - self.num_wmasks = int(self.word_size/self.write_size) - - self.create_netlist() - if not OPTS.netlist_only: - self.create_layout() - - def get_bl_name(self): - bl_name = self.driver.get_bl_names() - return bl_name - - def get_br_name(self): - br_name = self.driver.get_br_names() - return br_name - - @property - def data_name(self): - return "data" - - @property - def en_name(self): - return "en" - - def create_netlist(self): - 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() - self.DRC_LVS() - - def add_pins(self): - for i in range(self.word_size): - self.add_pin(self.data_name + "_{0}".format(i), "INPUT") - for i in range(self.word_size): - self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT") - self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT") - if self.write_size: - for i in range(self.num_wmasks): - self.add_pin(self.en_name + "_{0}".format(i), "INPUT") - else: - self.add_pin(self.en_name, "INPUT") - self.add_pin("vdd", "POWER") - self.add_pin("gnd", "GROUND") - - def add_modules(self): - self.driver = factory.create(module_type="write_driver") - self.add_mod(self.driver) - - # This is just used for measurements, - # so don't add the module - self.bitcell = factory.create(module_type="bitcell") - - def create_write_array(self): - self.driver_insts = {} - w = 0 - windex=0 - for i in range(0,self.columns,self.words_per_row): - name = "write_driver{}".format(i) - index = int(i/self.words_per_row) - self.driver_insts[index]=self.add_inst(name=name, - mod=self.driver) - - if self.write_size: - self.connect_inst([self.data_name + "_{0}".format(index), - self.get_bl_name() + "_{0}".format(index), - self.get_br_name() + "_{0}".format(index), - self.en_name + "_{0}".format(windex), "vdd", "gnd"]) - w+=1 - # when w equals write size, the next en pin can be connected since we are now at the next wmask bit - if w == self.write_size: - w = 0 - windex+=1 - else: - self.connect_inst([self.data_name + "_{0}".format(index), - self.get_bl_name() + "_{0}".format(index), - 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) - xoffset = i * self.driver_spacing - - if cell_properties.bitcell.mirror.y and i % 2: - mirror = "MY" - xoffset = xoffset + self.driver.width - else: - mirror = "" - - 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", - 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", - 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", - offset=br_pin.ll(), - width=br_pin.width(), - height=br_pin.height()) - - 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(), - vertical=True, - start_layer = "m2") - if self.write_size: - for bit in range(self.num_wmasks): - 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) - if (self.words_per_row == 1): - en_gap = self.driver_spacing - en_pin.width() - else: - en_gap = self.driver_spacing - - 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, - 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), - 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. - return self.driver.get_w_en_cin() * len(self.driver_insts)