diff --git a/compiler/base/lef.py b/compiler/base/lef.py index 9ff02816..f0d6dda0 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -69,25 +69,31 @@ class lef: def lef_write(self, lef_name): """ Write the entire lef of the object to the file. """ + # Can possibly use magic lef write to create the LEF + # if OPTS.drc_exe and OPTS.drc_exe[0] == "magic": + # self.magic_lef_write(lef_name) + # return + + # To maintain the indent level easily + self.indent = "" if OPTS.detailed_lef: debug.info(3, "Writing detailed LEF to {0}".format(lef_name)) - self.detailed_lef_write(lef_name) else: debug.info(3, "Writing abstract LEF to {0}".format(lef_name)) - # Can possibly use magic lef write to create the LEF - # if OPTS.drc_exe and OPTS.drc_exe[0] == "magic": - # self.magic_lef_write(lef_name) - # return - self.abstract_lef_write(lef_name) - - def abstract_lef_write(self, lef_name): - # To maintain the indent level easily - self.indent = "" + self.compute_abstract_blockages() self.lef = open(lef_name, "w") self.lef_write_header() + for pin_name in self.pins: + self.lef_write_pin(pin_name) + + self.lef_write_obstructions(OPTS.detailed_lef) + self.lef_write_footer() + self.lef.close() + + def compute_abstract_blockages(self): # Start with blockages on all layers the size of the block # minus the pin escape margin (hard coded to 4 x m3 pitch) # These are a pin_layout to use their geometric functions @@ -118,23 +124,6 @@ class lef: new_blockages = blockage.cut(intersection_pin) self.blockages[pin.layer].extend(new_blockages) - self.lef_write_pin(pin_name) - - self.lef_write_obstructions(abstracted=True) - self.lef_write_footer() - self.lef.close() - - def detailed_lef_write(self, lef_name): - # To maintain the indent level easily - self.indent = "" - - self.lef = open(lef_name, "w") - self.lef_write_header() - for pin in self.pins: - self.lef_write_pin(pin) - self.lef_write_obstructions() - self.lef_write_footer() - self.lef.close() def lef_write_header(self): """ Header of LEF file """ @@ -155,8 +144,8 @@ class lef: self.lef.write("{0}SYMMETRY X Y R90 ;\n".format(self.indent)) def lef_write_footer(self): - self.lef.write("{0}END {1}\n".format(self.indent, self.name)) self.indent = self.indent[:-3] + self.lef.write("{0}END {1}\n".format(self.indent, self.name)) self.lef.write("END LIBRARY\n") def lef_write_pin(self, name): @@ -188,20 +177,20 @@ class lef: self.indent = self.indent[:-3] self.lef.write("{0}END {1}\n".format(self.indent, name)) - def lef_write_obstructions(self, abstracted=False): + def lef_write_obstructions(self, detailed=False): """ Write all the obstructions on each layer """ self.lef.write("{0}OBS\n".format(self.indent)) for layer in self.lef_layers: self.lef.write("{0}LAYER {1} ;\n".format(self.indent, layer_names[layer])) self.indent += " " - if abstracted: - blockages = self.blockages[layer] - for b in blockages: - self.lef_write_shape(b.rect) - else: + if detailed: blockages = self.get_blockages(layer, True) for b in blockages: self.lef_write_shape(b) + else: + blockages = self.blockages[layer] + for b in blockages: + self.lef_write_shape(b.rect) self.indent = self.indent[:-3] self.lef.write("{0}END\n".format(self.indent)) diff --git a/compiler/custom/nand2_dec.py b/compiler/custom/nand2_dec.py index 98992f42..893bb34f 100644 --- a/compiler/custom/nand2_dec.py +++ b/compiler/custom/nand2_dec.py @@ -70,3 +70,7 @@ class nand2_dec(design.design): """ self.add_graph_edges(graph, port_nets) + def is_non_inverting(self): + """Return input to output polarity for module""" + + return False diff --git a/compiler/custom/nand3_dec.py b/compiler/custom/nand3_dec.py index 34890a9c..a887e38f 100644 --- a/compiler/custom/nand3_dec.py +++ b/compiler/custom/nand3_dec.py @@ -70,3 +70,7 @@ class nand3_dec(design.design): """ self.add_graph_edges(graph, port_nets) + def is_non_inverting(self): + """Return input to output polarity for module""" + + return False diff --git a/compiler/custom/nand4_dec.py b/compiler/custom/nand4_dec.py index d89dc926..d3b6491d 100644 --- a/compiler/custom/nand4_dec.py +++ b/compiler/custom/nand4_dec.py @@ -70,3 +70,7 @@ class nand4_dec(design.design): """ self.add_graph_edges(graph, port_nets) + def is_non_inverting(self): + """Return input to output polarity for module""" + + return False diff --git a/compiler/example_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py b/compiler/example_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py new file mode 100644 index 00000000..5d86dff6 --- /dev/null +++ b/compiler/example_configs/sky130_sram_1kbyte_1r1w_8x1024_8.py @@ -0,0 +1,20 @@ +""" +Pseudo-dual port (independent read and write ports), 8bit word, 1 kbyte SRAM. + +Useful as a byte FIFO between two devices (the reader and the writer). +""" +word_size = 8 # Bits +num_words = 1024 +human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) + +# Allow byte writes +write_size = 8 # Bits + +# Dual port +num_rw_ports = 0 +num_r_ports = 1 +num_w_ports = 1 +ports_human = '1r1w' + +import os +exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) diff --git a/compiler/example_configs/sky130_sram_1kbyte_1rw1r_32x256_8.py b/compiler/example_configs/sky130_sram_1kbyte_1rw1r_32x256_8.py new file mode 100644 index 00000000..51d64589 --- /dev/null +++ b/compiler/example_configs/sky130_sram_1kbyte_1rw1r_32x256_8.py @@ -0,0 +1,21 @@ +""" +Dual port (1 read/write + 1 read only) 1 kbytes SRAM with byte write. + +FIXME: What is this useful for? +FIXME: Why would you want byte write on this? +""" +word_size = 32 # Bits +num_words = 256 +human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) + +# Allow byte writes +write_size = 8 # Bits + +# Dual port +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 +ports_human = '1rw1r' + +import os +exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) diff --git a/compiler/example_configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py b/compiler/example_configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py new file mode 100644 index 00000000..ef62221c --- /dev/null +++ b/compiler/example_configs/sky130_sram_1kbyte_1rw1r_8x1024_8.py @@ -0,0 +1,21 @@ +""" +Dual port (1 read/write + 1 read only) 1 kbytes SRAM with byte write. + +FIXME: What is this useful for? +FIXME: Why would you want byte write on this? +""" +word_size = 8 # Bits +num_words = 1024 +human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) + +# Allow byte writes +write_size = 8 # Bits + +# Dual port +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 +ports_human = '1rw1r' + +import os +exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) diff --git a/compiler/example_configs/sky130_sram_1kbyte_1rw_32x256_8.py b/compiler/example_configs/sky130_sram_1kbyte_1rw_32x256_8.py new file mode 100644 index 00000000..955d3959 --- /dev/null +++ b/compiler/example_configs/sky130_sram_1kbyte_1rw_32x256_8.py @@ -0,0 +1,19 @@ +""" +Single port, 1 kbytes SRAM, with byte write, useful for RISC-V processor main +memory. +""" +word_size = 32 # Bits +num_words = 256 +human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) + +# Allow byte writes +write_size = 8 # Bits + +# Single port +num_rw_ports = 1 +num_r_ports = 0 +num_w_ports = 0 +ports_human = '1rw' + +import os +exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) diff --git a/compiler/example_configs/sky130_sram_2kbyte_1rw1r_32x512_8.py b/compiler/example_configs/sky130_sram_2kbyte_1rw1r_32x512_8.py new file mode 100644 index 00000000..0492902a --- /dev/null +++ b/compiler/example_configs/sky130_sram_2kbyte_1rw1r_32x512_8.py @@ -0,0 +1,21 @@ +""" +Dual port (1 read/write + 1 read only), 2 kbytes SRAM (with byte write). + +FIXME: What is this useful for? +FIXME: Why would you want byte write on this? +""" +word_size = 32 # Bits +num_words = 512 +human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) + +# Allow byte writes +write_size = 8 # Bits + +# Dual port +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 +ports_human = '1rw1r' + +import os +exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) diff --git a/compiler/example_configs/sky130_sram_2kbyte_1rw_32x512_8.py b/compiler/example_configs/sky130_sram_2kbyte_1rw_32x512_8.py new file mode 100644 index 00000000..7f64d18c --- /dev/null +++ b/compiler/example_configs/sky130_sram_2kbyte_1rw_32x512_8.py @@ -0,0 +1,19 @@ +""" +Single port, 2 kbytes SRAM, with byte write, useful for RISC-V processor main +memory. +""" +word_size = 32 # Bits +num_words = 512 +human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) + +# Allow byte writes +write_size = 8 # Bits + +# Single port +num_rw_ports = 1 +num_r_ports = 0 +num_w_ports = 0 +ports_human = '1rw' + +import os +exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) diff --git a/compiler/example_configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py b/compiler/example_configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py new file mode 100644 index 00000000..8d2f40e0 --- /dev/null +++ b/compiler/example_configs/sky130_sram_4kbyte_1rw1r_32x1024_8.py @@ -0,0 +1,22 @@ +""" +Dual port (1 read/write + 1 read only), 4 kbytes SRAM (with byte write). + +FIXME: What is this useful for? +FIXME: Why would you want byte write on this? +""" + +word_size = 32 # Bits +num_words = 1024 +human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) + +# Allow byte writes +write_size = 8 # Bits + +# Dual port +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 +ports_human = '1rw1r' + +import os +exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) diff --git a/compiler/example_configs/sky130_sram_4kbyte_1rw_32x1024_8.py b/compiler/example_configs/sky130_sram_4kbyte_1rw_32x1024_8.py new file mode 100644 index 00000000..571ca030 --- /dev/null +++ b/compiler/example_configs/sky130_sram_4kbyte_1rw_32x1024_8.py @@ -0,0 +1,20 @@ +""" +Single port, 4 kbytes SRAM, with byte write, useful for RISC-V processor main +memory. +""" + +word_size = 32 # Bits +num_words = 1024 +human_byte_size = "{:.0f}kbytes".format((word_size * num_words)/1024/8) + +# Allow byte writes +write_size = 8 # Bits + +# Single port +num_rw_ports = 1 +num_r_ports = 0 +num_w_ports = 0 +ports_human = '1rw' + +import os +exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) diff --git a/compiler/example_configs/sky130_sram_common.py b/compiler/example_configs/sky130_sram_common.py new file mode 100644 index 00000000..0e3443b1 --- /dev/null +++ b/compiler/example_configs/sky130_sram_common.py @@ -0,0 +1,19 @@ +# Include with +# import os +# exec(open(os.path.join(os.path.dirname(__file__), 'sky130_sram_common.py')).read()) + + +tech_name = "sky130" +nominal_corner_only = True + +# Local wordlines have issues with met3 power routing for now +#local_array_size = 16 + +#route_supplies = False +check_lvsdrc = True +#perimeter_pins = False +#netlist_only = True +#analytical_delay = False + +output_name = "{tech_name}_sram_{human_byte_size}_{ports_human}_{word_size}x{num_words}_{write_size}".format(**locals()) +output_path = "macro/{output_name}".format(**locals()) diff --git a/compiler/globals.py b/compiler/globals.py index 1d813e97..185d3c02 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -329,7 +329,7 @@ def read_config(config_file, is_unit_test=True): debug.info(1, "Configuration file is " + config_file + ".py") try: config = importlib.import_module(module_name) - except: + except ImportError: debug.error("Unable to read configuration file: {0}".format(config_file), 2) OPTS.overridden = {} diff --git a/compiler/modules/write_mask_and_array.py b/compiler/modules/write_mask_and_array.py index c0bd8b84..2ccf34a1 100644 --- a/compiler/modules/write_mask_and_array.py +++ b/compiler/modules/write_mask_and_array.py @@ -117,9 +117,10 @@ class write_mask_and_array(design.design): for i in range(self.num_wmasks): # Route the A pin over to the left so that it doesn't conflict with the sense # amp output which is usually in the center - a_pin = self.and2_insts[i].get_pin("A") + inst = self.and2_insts[i] + a_pin = inst.get_pin("A") a_pos = a_pin.center() - in_pos = vector(self.and2_insts[i].lx(), + in_pos = vector(inst.lx(), a_pos.y) self.add_via_stack_center(from_layer=a_pin.layer, to_layer="m2", @@ -130,21 +131,31 @@ class write_mask_and_array(design.design): self.add_path(a_pin.layer, [in_pos, a_pos]) # Copy remaining layout pins - self.copy_layout_pin(self.and2_insts[i], "Z", "wmask_out_{0}".format(i)) + self.copy_layout_pin(inst, "Z", "wmask_out_{0}".format(i)) # Add via connections to metal3 for AND array's B pin - en_pin = self.and2_insts[i].get_pin("B") + en_pin = inst.get_pin("B") en_pos = en_pin.center() self.add_via_stack_center(from_layer=en_pin.layer, to_layer="m3", offset=en_pos) + + # Add connection to the supply + for supply_name in ["gnd", "vdd"]: + supply_pin = inst.get_pin(supply_name) + self.add_via_stack_center(from_layer=supply_pin.layer, + to_layer="m1", + offset=supply_pin.center()) for supply in ["gnd", "vdd"]: supply_pin = self.and2_insts[0].get_pin(supply) supply_pin_yoffset = supply_pin.cy() left_loc = vector(0, supply_pin_yoffset) right_loc = vector(self.width, supply_pin_yoffset) - self.add_path(supply_pin.layer, [left_loc, right_loc]) - self.copy_power_pin(supply_pin, loc=left_loc) - self.copy_power_pin(supply_pin, loc=right_loc) + self.add_path("m1", [left_loc, right_loc]) + for loc in [left_loc, right_loc]: + self.add_via_stack_center(from_layer=supply_pin.layer, + to_layer="m1", + offset=loc) + self.copy_power_pin(supply_pin, loc=loc)