From ec28bc6dfdc02a5ae33b789721cb5ff1830904da Mon Sep 17 00:00:00 2001 From: rlin50 Date: Sun, 22 Feb 2026 22:11:35 -0800 Subject: [PATCH 1/2] Fix sky130 1rw LVS mismatch by correcting col_cap pin order --- technology/sky130/custom/sky130_bitcell_base_array.py | 9 +++++---- technology/sky130/custom/sky130_col_cap_array.py | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/technology/sky130/custom/sky130_bitcell_base_array.py b/technology/sky130/custom/sky130_bitcell_base_array.py index 9263f8dd1..a26f323ea 100644 --- a/technology/sky130/custom/sky130_bitcell_base_array.py +++ b/technology/sky130/custom/sky130_bitcell_base_array.py @@ -100,14 +100,15 @@ def get_col_cap_pins(self, row, col): strap_pins = [] for port in self.all_ports: strap_pins.extend([x for x in self.get_bitline_names(port) if "bl" in x and x.endswith("_{0}".format(col))]) - strap_pins.extend(["vdd", "gnd"]) for port in self.all_ports: strap_pins.extend([x for x in self.get_bitline_names(port) if "br" in x and x.endswith("_{0}".format(col))]) + # col_cap_1port_bitcell port order: + # [bl, br, vdd, gnd, vpb, vnb, gate] + strap_pins.extend(["vdd", "gnd", "vdd", "gnd"]) if row == 0: - strap_pins.extend(["top_gate"]) + strap_pins.append("top_gate") else: - strap_pins.extend(["bot_gate"]) - strap_pins.extend(["vdd", "gnd"]) + strap_pins.append("bot_gate") return strap_pins def get_row_cap_pins(self, row, col): diff --git a/technology/sky130/custom/sky130_col_cap_array.py b/technology/sky130/custom/sky130_col_cap_array.py index 445c918c8..ded320394 100644 --- a/technology/sky130/custom/sky130_col_cap_array.py +++ b/technology/sky130/custom/sky130_col_cap_array.py @@ -75,12 +75,12 @@ def create_instances(self): row_layout.append(self.colend1) self.cell_inst[col]=self.add_inst(name=name, mod=self.colend1) pins.append("fake_bl_{}".format(bitline)) + pins.append("fake_br_{}".format(bitline)) pins.append("vdd") pins.append("gnd") - pins.append("fake_br_{}".format(bitline)) - pins.append("gate") pins.append("vdd") pins.append("gnd") + pins.append("gate") bitline += 1 elif col % 4 == 1: row_layout.append(self.colend2) @@ -92,12 +92,12 @@ def create_instances(self): row_layout.append(self.colend1) self.cell_inst[col]=self.add_inst(name=name, mod=self.colend1) pins.append("fake_bl_{}".format(bitline)) + pins.append("fake_br_{}".format(bitline)) pins.append("vdd") pins.append("gnd") - pins.append("fake_br_{}".format(bitline)) - pins.append("gate") pins.append("vdd") pins.append("gnd") + pins.append("gate") bitline += 1 elif col % 4 ==3: row_layout.append(self.colend2) From 6d14626a75f82113b812a20141b2f352a2502112 Mon Sep 17 00:00:00 2001 From: rlin50 Date: Mon, 23 Feb 2026 15:32:08 -0800 Subject: [PATCH 2/2] Fix address bit ordering in sky130 1rw characterization --- compiler/characterizer/delay.py | 8 ++++++-- compiler/characterizer/simulation.py | 16 ++++++++++++---- compiler/characterizer/trim_spice.py | 9 ++++++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py index 758d7af16..93b7c9836 100644 --- a/compiler/characterizer/delay.py +++ b/compiler/characterizer/delay.py @@ -1388,11 +1388,15 @@ def get_delay_lists(self, value_dict): def calculate_inverse_address(self): """Determine dummy test address based on probe address and column mux size.""" - # The inverse address needs to share the same bitlines as the probe address as the trimming will remove all other bitlines + # The inverse address needs to share the same bitlines as the probe address as the trimming will remove all other bitlines. # This is only an issue when there is a column mux and the address maps to different bitlines. column_addr = self.get_column_addr() # do not invert this part inverse_address = "" - for c in self.probe_address[self.sram.col_addr_size:]: # invert everything else + if self.sram.col_addr_size > 0: + row_address = self.probe_address[:-self.sram.col_addr_size] + else: + row_address = self.probe_address + for c in row_address: # invert row bits only if c=="0": inverse_address += "1" elif c=="1": diff --git a/compiler/characterizer/simulation.py b/compiler/characterizer/simulation.py index 2dec0010e..1e6a34ab5 100644 --- a/compiler/characterizer/simulation.py +++ b/compiler/characterizer/simulation.py @@ -126,8 +126,10 @@ def set_probe(self, probe_address, probe_data): def get_data_bit_column_number(self, probe_address, probe_data): """Calculates bitline column number of data bit under test using bit position and mux size""" - if self.sram.col_addr_size>0: - col_address = int(probe_address[0:self.sram.col_addr_size], 2) + # Address pins are ordered a*_0 ... a*_N, where a*_0 is the LSB. + # So the column mux select bits are the rightmost bits in the binary address string. + if self.sram.col_addr_size > 0: + col_address = int(probe_address[-self.sram.col_addr_size:], 2) else: col_address = 0 bl_column = int(self.sram.words_per_row * probe_data + col_address) @@ -136,7 +138,11 @@ def get_data_bit_column_number(self, probe_address, probe_data): def get_address_row_number(self, probe_address): """Calculates wordline row number of data bit under test using address and column mux size""" - return int(probe_address[self.sram.col_addr_size:], 2) + if self.sram.col_addr_size > 0: + row_address = probe_address[:-self.sram.col_addr_size] + else: + row_address = probe_address + return int(row_address, 2) if row_address else 0 def add_control_one_port(self, port, op): """Appends control signals for operation to a given port""" @@ -484,7 +490,9 @@ def gen_pin_names(self, port_signal_names, port_info, abits, dbits): def get_column_addr(self): """Returns column address of probe bit""" - return self.probe_address[:self.sram.col_addr_size] + if self.sram.col_addr_size == 0: + return "" + return self.probe_address[-self.sram.col_addr_size:] def add_graph_exclusions(self): """ diff --git a/compiler/characterizer/trim_spice.py b/compiler/characterizer/trim_spice.py index e3892d9dc..603934c0f 100644 --- a/compiler/characterizer/trim_spice.py +++ b/compiler/characterizer/trim_spice.py @@ -56,12 +56,15 @@ def trim(self, address, data_bit): # Always start fresh if we do multiple reductions self.sp_buffer = self.spice - # Split up the address and convert to an int - wl_address = int(address[self.col_addr_size:], 2) + # Address pins are ordered with bit 0 as LSB, so mux column bits + # are the rightmost bits in the binary address string. if self.col_addr_size > 0: - col_address = int(address[0:self.col_addr_size], 2) + row_address = address[:-self.col_addr_size] + col_address = int(address[-self.col_addr_size:], 2) else: + row_address = address col_address = 0 + wl_address = int(row_address, 2) if row_address else 0 # 1. Keep cells in the bitcell array based on WL and BL wl_name = "wl_{}".format(wl_address)