Changes for test generation and simulation

This commit is contained in:
Jacob Walker 2023-02-27 14:48:10 -08:00
parent 89c7d50bd1
commit bbf2cd2913
12 changed files with 363 additions and 178 deletions

View File

@ -787,13 +787,15 @@ class layout():
end=bot_pos) end=bot_pos)
def connect_col_pins(self, layer, pins, name=None, full=False): def connect_col_pins(self, layer, pins, name=None, full=False, round=False, directions="pref"):
""" """
Connects top/bot columns that are aligned. Connects top/bot columns that are aligned.
""" """
bins = {} bins = {}
for pin in pins: for pin in pins:
x = pin.cx() x = pin.cx()
if round:
x = round_to_grid(x)
try: try:
bins[x].append(pin) bins[x].append(pin)
except KeyError: except KeyError:
@ -819,7 +821,8 @@ class layout():
self.add_via_stack_center(from_layer=pin.layer, self.add_via_stack_center(from_layer=pin.layer,
to_layer=layer, to_layer=layer,
offset=pin.center(), offset=pin.center(),
min_area=True) min_area=True,
directions=directions)
if name: if name:
self.add_layout_pin_segment_center(text=name, self.add_layout_pin_segment_center(text=name,

View File

@ -20,7 +20,7 @@ class pinv_dec(pinv):
Other stuff is the same (netlist, sizes, etc.) Other stuff is the same (netlist, sizes, etc.)
""" """
def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True): def __init__(self, name, size=1, beta=parameter["beta"], height=None, add_wells=True, flip_io=False):
debug.info(2, debug.info(2,
"creating pinv_dec structure {0} with size of {1}".format(name, "creating pinv_dec structure {0} with size of {1}".format(name,
@ -37,7 +37,7 @@ class pinv_dec(pinv):
self.supply_layer = "m1" self.supply_layer = "m1"
else: else:
self.supply_layer = "m2" self.supply_layer = "m2"
self.flip_io=flip_io
super().__init__(name, size, beta, self.cell_height, add_wells) super().__init__(name, size, beta, self.cell_height, add_wells)
def determine_tx_mults(self): def determine_tx_mults(self):
@ -76,9 +76,14 @@ class pinv_dec(pinv):
pmos_gate_pos = pmos_gate_pin.lc() pmos_gate_pos = pmos_gate_pin.lc()
self.add_path("poly", [nmos_gate_pos, pmos_gate_pos]) self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
# Center is completely symmetric. # Center is completely symmetric.
contact_width = self.poly_contact.width contact_width = self.poly_contact.width
contact_offset = nmos_gate_pin.lc() \ if self.flip_io:
contact_offset = pmos_gate_pin.rc() \
+ vector(self.poly_extend_active + 0.6 * contact_width, 0)
else:
contact_offset = nmos_gate_pin.lc() \
- vector(self.poly_extend_active + 0.5 * contact_width, 0) - vector(self.poly_extend_active + 0.5 * contact_width, 0)
via = self.add_via_stack_center(from_layer="poly", via = self.add_via_stack_center(from_layer="poly",
to_layer=self.route_layer, to_layer=self.route_layer,
@ -107,8 +112,11 @@ class pinv_dec(pinv):
height=self.height - ll.y + 0.5 * self.pwell_contact.height + self.well_enclose_active) height=self.height - ll.y + 0.5 * self.pwell_contact.height + self.well_enclose_active)
if "nwell" in layer: if "nwell" in layer:
ll = (self.pmos_inst.ll() - vector(2 * [self.well_enclose_active])).scale(1, 0) poly_offset = 0
ur = self.pmos_inst.ur() + vector(2 * [self.well_enclose_active]) if self.flip_io:
poly_offset = 1.2 * self.poly_contact.width
ll = (self.pmos_inst.ll() - vector(2 * [self.well_enclose_active])).scale(1, 0) - vector(0, poly_offset)
ur = self.pmos_inst.ur() + vector(2 * [self.well_enclose_active + poly_offset])
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
offset=ll, offset=ll,
width=ur.x - ll.x, width=ur.x - ll.x,

View File

@ -1,39 +0,0 @@
from openram.base import design
from openram.base import vector
from openram.sram_factory import factory
class rom_array_gnd_tap(design):
def __init__(self, name, length, cell_name=None, prop=None):
super().__init__(name, cell_name, prop)
self.length = length
self.create_layout()
def create_layout(self):
self.add_cell()
self.add_boundary()
self.place_gnd_rail()
def add_boundary(self):
self.height = self.dummy.height
self.width = self.dummy.width
super().add_boundary()
def add_cell(self):
self.dummy = factory.create(module_type="rom_dummy_cell")
def place_gnd_rail(self):
rail_start = vector(-self.dummy.width / 2 ,0)
rail_end = vector(self.dummy.width * self.length, 0)
self.add_layout_pin_rect_ends( name="gnd",
layer="m1",
start=rail_start,
end=rail_end)

View File

@ -58,16 +58,17 @@ class rom_base_array(bitcell_base_array):
self.place_precharge() self.place_precharge()
self.place_wordline_contacts() self.place_wordline_contacts()
self.place_bitline_contacts() self.place_bitline_contacts()
self.add_boundary()
self.route_precharge() self.route_precharge()
self.add_boundary()
self.place_rails() self.place_rails()
self.connect_taps() self.connect_taps()
def add_boundary(self): def add_boundary(self):
ll = self.find_lowest_coords() ll = self.find_lowest_coords()
bottom_offset = - self.zero_cell.nmos.end_to_contact + self.precharge_inst.offset.y
m1_offset = self.m1_width m1_offset = self.m1_width
self.translate_all(vector(0, ll.y + 0.5 * m1_offset)) self.translate_all(vector(0, ll.y + 0.5 * m1_offset))
ur = self.find_highest_coords() ur = self.find_highest_coords()
@ -209,7 +210,7 @@ class rom_base_array(bitcell_base_array):
prechrg_pins.append("precharge") prechrg_pins.append("precharge")
prechrg_pins.append("vdd") prechrg_pins.append("vdd")
self.precharge_inst = self.add_inst(name="decode_array_precharge", mod=self.precharge_array) self.precharge_inst = self.add_inst(name="bitcell_array_precharge", mod=self.precharge_array)
self.connect_inst(prechrg_pins) self.connect_inst(prechrg_pins)
@ -390,6 +391,9 @@ class rom_base_array(bitcell_base_array):
self.add_path(layer="m1", coordinates=[start, mid1, mid2, end]) self.add_path(layer="m1", coordinates=[start, mid1, mid2, end])
self.add_layout_pin_rect_center(text="precharge_r", layer="m1", offset=mid1)
def connect_taps(self): def connect_taps(self):
array_pins = [self.tap_list[i].get_pin("poly_tap") for i in range(len(self.tap_list))] array_pins = [self.tap_list[i].get_pin("poly_tap") for i in range(len(self.tap_list))]

View File

@ -17,7 +17,7 @@ class rom_base_bank(design):
def __init__(self, strap_spacing=0, data_file=None, name="", word_size=2): def __init__(self, strap_spacing=0, data_file=None, name="", word_size=2):
super().__init__(name=name) super().__init__(name=name)
self.word_size = word_size * 8 self.word_size = word_size * 8
self.read_binary(word_size=word_size, data_file=data_file) self.read_binary(word_size=word_size, data_file=data_file, scramble_bits=True, endian="little")
self.num_outputs = self.rows self.num_outputs = self.rows
self.num_inputs = ceil(log(self.rows, 2)) self.num_inputs = ceil(log(self.rows, 2))
@ -48,7 +48,7 @@ class rom_base_bank(design):
sets the row and column size based on the size of binary input, tries to keep array as square as possible, sets the row and column size based on the size of binary input, tries to keep array as square as possible,
""" """
def read_binary(self, data_file, word_size=2, endian="big"): def read_binary(self, data_file, word_size=2, endian="big", scramble_bits=False):
# Read data as hexidecimal text file # Read data as hexidecimal text file
hex_file = open(data_file, 'r') hex_file = open(data_file, 'r')
hex_data = hex_file.read() hex_data = hex_file.read()
@ -72,24 +72,55 @@ class rom_base_bank(design):
bits_per_row = self.words_per_row * word_size * 8 bits_per_row = self.words_per_row * word_size * 8
self.cols = bits_per_row
self.rows = int(num_words / (self.words_per_row))
chunked_data = [] chunked_data = []
for i in range(0, len(bin_data), bits_per_row): for i in range(0, len(bin_data), bits_per_row):
word = bin_data[i:i + bits_per_row] row_data = bin_data[i:i + bits_per_row]
if len(word) < bits_per_row: if len(row_data) < bits_per_row:
word = [0] * (bits_per_row - len(word)) + word row_data = [0] * (bits_per_row - len(row_data)) + row_data
chunked_data.append(word) chunked_data.append(row_data)
# if endian == "big":
if endian == "big":
chunked_data.reverse()
self.data = chunked_data self.data = chunked_data
self.cols = bits_per_row if scramble_bits:
self.rows = int(num_words / (self.words_per_row)) scrambled_chunked = []
debug.info(1, "Read rom binary: length {0} bytes, {1} words, set number of cols to {2}, rows to {3}, with {4} words per row".format(data_size, num_words, self.cols, self.rows, self.words_per_row))
for row_data in chunked_data:
scambled_data = []
for bit in range(self.word_size):
for word in range(self.words_per_row):
scambled_data.append(row_data[bit + word * self.word_size])
scrambled_chunked.append(scambled_data)
self.data = scrambled_chunked
# self.data.reverse()
debug.info(1, "Read rom binary: length {0} bytes, {1} words, set number of cols to {2}, rows to {3}, with {4} words per row".format(data_size, num_words, self.cols, self.rows, self.words_per_row))
# self.print_data(chunked_data)
# print("Scrambled")
# self.print_data(scrambled_chunked)
# self.print_word(self.data, 0, 0)
# self.print_word(self.data, 0, 1)
# self.print_word(self.data, 0, 2)
# self.print_word(self.data, 0, 3)
# print("hex: {0}, binary: {1}, chunked: {2}".format(hex_data, bin_data, chunked_data)) # print("hex: {0}, binary: {1}, chunked: {2}".format(hex_data, bin_data, chunked_data))
def print_data(self, data_array):
for row in range(len(data_array)):
print(data_array[row])
def print_word(self, data_array, bl, word):
for bit in range(self.word_size):
print(data_array[bl][word + self.words_per_row * bit], end =" ")
print("")
def create_netlist(self): def create_netlist(self):
self.add_modules() self.add_modules()
@ -147,6 +178,20 @@ class rom_base_bank(design):
def add_modules(self): def add_modules(self):
print("Creating bank modules") print("Creating bank modules")
# TODO: provide technology-specific calculation of these parameters
# in sky130 the address control buffer is composed of 2 size 2 NAND gates,
# with a beta of 3, each of these gates has gate capacitance of 2 min sized inverters, therefor a load of 4
addr_control_buffer_effort = 4
# a single min sized nmos makes up 1/4 of the input capacitance of a min sized inverter
bitcell_effort = 0.25
# Takes into account inverter sizing
wordline_effort = bitcell_effort * 0.5
# a single min sized pmos plus a single min sized nmos have approximately half the gate capacitance of a min inverter
# an additional 0.2 accounts for the long wire capacitance and add delay to gaurentee the read timing
precharge_cell_effort = 0.5 + 0.2
self.array = factory.create(module_type="rom_base_array", self.array = factory.create(module_type="rom_base_array",
cols=self.cols, cols=self.cols,
rows=self.rows, rows=self.rows,
@ -163,7 +208,7 @@ class rom_base_bank(design):
num_outputs=self.rows, num_outputs=self.rows,
strap_spacing=self.strap_spacing, strap_spacing=self.strap_spacing,
route_layer=self.route_layer, route_layer=self.route_layer,
cols=self.cols) fanout=(self.cols)*wordline_effort )
self.column_mux = factory.create(module_type="rom_column_mux_array", self.column_mux = factory.create(module_type="rom_column_mux_array",
@ -178,18 +223,27 @@ class rom_base_bank(design):
num_outputs=self.words_per_row, num_outputs=self.words_per_row,
strap_spacing=self.strap_spacing, strap_spacing=self.strap_spacing,
route_layer=self.route_layer, route_layer=self.route_layer,
cols=1, fanout=2,
invert_outputs=True ) invert_outputs=True )
self.control_logic = factory.create(module_type="rom_control_logic", self.control_logic = factory.create(module_type="rom_control_logic",
num_outputs=(self.rows + self.cols + self.words_per_row) * 0.5, num_outputs=(self.cols + self.words_per_row * precharge_cell_effort) \
clk_fanout=(self.col_bits + self.row_bits) * 2, + (addr_control_buffer_effort * self.col_bits),
clk_fanout=(self.row_bits * addr_control_buffer_effort) + (precharge_cell_effort * self.rows),
height=self.column_decode.height ) height=self.column_decode.height )
self.output_buffer = factory.create(module_type="rom_wordline_driver_array", self.bitline_inv = factory.create(module_type="rom_wordline_driver_array",
module_name="rom_bitline_inverter",
rows=self.cols,
fanout=4,
invert_outputs=True,
tap_spacing=0,
flip_io=True)
self.output_inv = factory.create(module_type="rom_wordline_driver_array",
module_name="rom_output_buffer", module_name="rom_output_buffer",
rows=self.word_size, rows=self.word_size,
cols=4) fanout=4,
invert_outputs=True)
def create_instances(self): def create_instances(self):
@ -206,18 +260,21 @@ class rom_base_bank(design):
select_lines = ["word_sel_{}".format(word) for word in range(self.words_per_row)] select_lines = ["word_sel_{}".format(word) for word in range(self.words_per_row)]
bitline_bar = ["bl_b_{}".format(bl) for bl in range(self.cols)]
pre_buf_outputs = ["rom_out_prebuf_{}".format(bit) for bit in range(self.word_size)] pre_buf_outputs = ["rom_out_prebuf_{}".format(bit) for bit in range(self.word_size)]
outputs = ["rom_out_{}".format(bl) for bl in range(self.word_size)] outputs = ["rom_out_{}".format(bl) for bl in range(self.word_size)]
array_pins = bitlines + wordlines + prechrg + vdd + gnd array_pins = bitlines + wordlines + prechrg + vdd + gnd
row_decode_pins = addr_msb + wordlines + prechrg + clk + vdd + gnd row_decode_pins = addr_msb + wordlines + clk + clk + vdd + gnd
col_decode_pins = addr_lsb + select_lines + prechrg + clk + vdd + gnd col_decode_pins = addr_lsb + select_lines + prechrg + prechrg + vdd + gnd
col_mux_pins = bitlines + select_lines + pre_buf_outputs + gnd col_mux_pins = bitline_bar + select_lines + pre_buf_outputs + gnd
output_buffer_pins = pre_buf_outputs + outputs + vdd + gnd bitline_inv_pins = bitlines + bitline_bar + vdd + gnd
output_buf_pins = pre_buf_outputs + outputs + vdd + gnd
self.array_inst = self.add_inst(name="rom_bit_array", mod=self.array) self.array_inst = self.add_inst(name="rom_bit_array", mod=self.array)
self.connect_inst(array_pins) self.connect_inst(array_pins)
@ -234,14 +291,19 @@ class rom_base_bank(design):
self.col_decode_inst = self.add_inst(name="rom_column_decoder", mod=self.column_decode) self.col_decode_inst = self.add_inst(name="rom_column_decoder", mod=self.column_decode)
self.connect_inst(col_decode_pins) self.connect_inst(col_decode_pins)
self.output_buf_inst = self.add_inst(name="rom_output_buffer", mod=self.output_buffer) self.bitline_inv_inst = self.add_inst(name="rom_bitline_inverter", mod=self.bitline_inv)
self.connect_inst(output_buffer_pins) self.connect_inst(bitline_inv_pins)
self.output_inv_inst = self.add_inst(name="rom_output_inverter", mod=self.output_inv)
self.connect_inst(output_buf_pins)
def place_instances(self): def place_instances(self):
self.place_row_decoder() self.place_row_decoder()
self.place_data_array() self.place_data_array()
self.place_bitline_inverter()
self.place_col_mux() self.place_col_mux()
self.place_col_decoder() self.place_col_decoder()
self.place_control_logic() self.place_control_logic()
@ -249,7 +311,7 @@ class rom_base_bank(design):
def place_row_decoder(self): def place_row_decoder(self):
self.decode_offset = vector(0, self.control_inst.height - self.decode_array.control_array.height) self.decode_offset = vector(0, self.control_inst.height )
self.decode_inst.place(offset=self.decode_offset) self.decode_inst.place(offset=self.decode_offset)
def place_data_array(self): def place_data_array(self):
@ -263,11 +325,18 @@ class rom_base_bank(design):
array_align = self.decode_inst.get_pin("wl_0").cy() - self.array_inst.get_pin("wl_0_0").cy() array_align = self.decode_inst.get_pin("wl_0").cy() - self.array_inst.get_pin("wl_0_0").cy()
self.array_inst.place(offset=(self.array_offset + vector(0, array_align))) self.array_inst.place(offset=(self.array_offset + vector(0, array_align)))
def place_bitline_inverter(self):
self.bitline_inv_inst.place(offset=[0,0], rotate=90)
inv_y_offset = self.array_inst.by() - self.bitline_inv_inst.width - 2 * self.m1_pitch
inv_x_offset = self.array_inst.get_pin("bl_0_0").cx() - self.bitline_inv_inst.get_pin("out_0").cx()
self.inv_offset = vector(inv_x_offset, inv_y_offset)
self.bitline_inv_inst.place(offset=self.inv_offset, rotate=90)
def place_control_logic(self): def place_control_logic(self):
self.control_offset = vector(self.col_decode_inst.lx() - self.control_inst.width - 3 * self.m1_pitch, self.decode_inst.by() - self.control_logic.height - self.m1_pitch)
self.control_offset = vector(self.control_inst.width + self.decode_array.control_array.width + 2 * (self.route_layer_pitch + self.route_layer_width), self.col_decode_inst.by() + self.control_logic.height) self.control_inst.place(offset=self.control_offset)
self.control_inst.place(offset=self.control_offset, mirror="XY")
def place_col_decoder(self): def place_col_decoder(self):
col_decode_y = self.mux_inst.get_pin("sel_0").cy() - self.col_decode_inst.get_pin("wl_0").cy() col_decode_y = self.mux_inst.get_pin("sel_0").cy() - self.col_decode_inst.get_pin("wl_0").cy()
@ -275,27 +344,17 @@ class rom_base_bank(design):
self.col_decode_inst.place(offset=self.col_decode_offset) self.col_decode_inst.place(offset=self.col_decode_offset)
def place_col_mux(self): def place_col_mux(self):
mux_y_offset = self.array_inst.by() - self.mux_inst.height - 5 * self.route_layer_pitch mux_y_offset = self.bitline_inv_inst.by() - self.mux_inst.height - 5 * self.route_layer_pitch
mux_x_offset = self.array_inst.get_pin("bl_0_0").cx() - self.mux_inst.get_pin("bl_0").cx() mux_x_offset = self.bitline_inv_inst.get_pin("out_0").cx() - self.mux_inst.get_pin("bl_0").cx()
self.mux_offset = vector(mux_x_offset, mux_y_offset) self.mux_offset = vector(mux_x_offset, mux_y_offset)
self.mux_inst.place(offset=self.mux_offset) self.mux_inst.place(offset=self.mux_offset)
def place_output_buffer(self): def place_output_buffer(self):
output_x = self.col_decode_inst.rx() + self.output_buf_inst.height output_x = self.col_decode_inst.rx() + self.output_inv_inst.height
output_y = self.col_decode_inst.by() + self.output_buf_inst.width output_y = self.mux_inst.by() - self.word_size * self.m1_pitch
self.output_buf_offset = vector(output_x, output_y) self.output_inv_offset = vector(output_x, output_y)
self.output_buf_inst.place(offset=self.output_buf_offset, rotate=270) self.output_inv_inst.place(offset=self.output_inv_offset, rotate=270)
# def create_wl_bus(self):
# bus_x = self.decode_inst.width + ( drc["minwidth_{}".format(self.bus_layer)] + 1.5 * drc["{0}_to_{0}".format(self.bus_layer)] )
# bus_y = self.array_inst.by() + self.bus_layer_pitch + self.bus_layer_width
# self.wl_interconnects = []
# for wl in range(self.rows):
# self.wl_interconnects.append("wl_interconnect_{}".format(wl))
# self.wl_bus = self.create_vertical_bus(self.bus_layer, vector(bus_x, bus_y), self.wl_interconnects, self.decode_inst.uy() - self.array_inst.by() )
def route_decode_outputs(self): def route_decode_outputs(self):
# for the row decoder # for the row decoder
@ -325,88 +384,105 @@ class rom_base_bank(design):
start = vector(wl_bus_wire.cx(), end.y) start = vector(wl_bus_wire.cx(), end.y)
self.add_segment_center(self.interconnect_layer, start, end) self.add_segment_center(self.interconnect_layer, start, end)
self.add_via_stack_center(start, self.route_layer, self.interconnect_layer )
def route_precharge(self): def route_precharge(self):
prechrg_control = self.control_inst.get_pin("prechrg") prechrg_control = self.control_inst.get_pin("prechrg")
row_decode_prechrg = self.decode_inst.get_pin("precharge")
col_decode_prechrg = self.col_decode_inst.get_pin("precharge") col_decode_prechrg = self.col_decode_inst.get_pin("precharge_r")
col_decode_clk = self.col_decode_inst.get_pin("clk")
array_prechrg = self.array_inst.get_pin("precharge") array_prechrg = self.array_inst.get_pin("precharge")
# Route precharge signal to the row decoder # Route precharge signal to the row decoder
end = vector(row_decode_prechrg.cx() - 0.5 * self.interconnect_layer_width, prechrg_control.cy()) # end = vector(row_decode_prechrg.cx() - 0.5 * self.interconnect_layer_width, prechrg_control.cy())
self.add_segment_center(self.interconnect_layer, prechrg_control.center(), end) # self.add_segment_center(self.interconnect_layer, prechrg_control.center(), end)
start = end + vector(0.5 * self.interconnect_layer_width, 0) # start = end + vector(0.5 * self.interconnect_layer_width, 0)
self.add_segment_center(self.interconnect_layer, start, row_decode_prechrg.center()) # self.add_segment_center(self.interconnect_layer, start, row_decode_prechrg.center())
self.add_via_stack_center(from_layer=self.route_stack[0], self.add_via_stack_center(from_layer=self.route_stack[0],
to_layer=prechrg_control.layer, to_layer=prechrg_control.layer,
offset=prechrg_control.center()) offset=prechrg_control.center())
# Route precharge to col decoder # Route precharge to col decoder
start = row_decode_prechrg.center() - vector(0, self.route_layer_pitch + 2 * self.route_layer_width) start = prechrg_control.center()
mid = vector(col_decode_prechrg.cx(), start.y) mid1 = vector(self.control_inst.rx(), prechrg_control.cy())
end = vector(col_decode_prechrg.cx(), 0.5 * (self.col_decode_inst.uy() + mid.y) ) mid2 = vector(self.control_inst.rx(), col_decode_prechrg.cy())
self.add_path(self.route_stack[0], [start, mid, end]) end = col_decode_prechrg.center()
self.add_path(self.route_stack[0], [start, mid1, mid2, end])
self.add_via_stack_center(from_layer=self.route_stack[0], self.add_via_stack_center(from_layer=self.route_stack[0],
to_layer=col_decode_prechrg.layer, to_layer=col_decode_prechrg.layer,
offset=end) offset=end)
self.add_segment_center(col_decode_prechrg.layer, end, col_decode_prechrg.center())
start = mid1
mid1 = vector(self.control_inst.rx(), start.y)
mid2 = vector(mid1.x, col_decode_clk.cy())
end = col_decode_clk.center()
self.add_path(self.route_stack[0], [start, mid1, mid2, end])
# self.add_segment_center(col_decode_prechrg.layer, end, col_decode_prechrg.center())
# Route precharge to main array # Route precharge to main array
end = vector(col_decode_prechrg.cx(), array_prechrg.cy()) # end = vector(col_decode_prechrg.cx(), array_prechrg.cy())
self.add_segment_center(self.route_stack[0], array_prechrg.center(), end) mid = vector(col_decode_prechrg.cx(), array_prechrg.cy() )
self.add_path(self.route_stack[0], [array_prechrg.center(), mid, col_decode_prechrg.center()])
def route_clock(self): def route_clock(self):
clk_out = self.control_inst.get_pin("clk_out") clk_out = self.control_inst.get_pin("clk_out")
row_decode_clk = self.decode_inst.get_pin("clk") row_decode_clk = self.decode_inst.get_pin("clk")
col_decode_clk = self.col_decode_inst.get_pin("clk")
self.add_via_stack_center(from_layer=self.route_stack[2], self.add_via_stack_center(from_layer=self.route_stack[2],
to_layer=clk_out.layer, to_layer=clk_out.layer,
offset=clk_out.center()) offset=clk_out.center())
# Route clock to row decoder # Route clock to row decoder
end = row_decode_clk.rc() + vector( 2 * self.route_layer_pitch + self.route_layer_width, 0) mid = vector(self.control_inst.rx() + self.m1_pitch, clk_out.cy())
self.add_path(self.route_stack[2], [clk_out.center(), end])
addr_control_clk = row_decode_clk.rc() + vector( 2 * self.route_layer_pitch + self.route_layer_width, 0)
row_decode_prechrg = self.decode_inst.get_pin("precharge")
self.add_path(self.route_stack[2], [clk_out.center(), mid, addr_control_clk, row_decode_prechrg.center()])
self.add_via_stack_center(from_layer=self.route_stack[2], self.add_via_stack_center(from_layer=self.route_stack[2],
to_layer=row_decode_clk.layer, to_layer=row_decode_clk.layer,
offset=end) offset=addr_control_clk)
self.add_segment_center(row_decode_clk.layer, end, row_decode_clk.rc()) self.add_segment_center(row_decode_clk.layer, addr_control_clk, row_decode_clk.rc())
# Route clock to column decoder # Route clock to column decoder
end = col_decode_clk.lc() - vector( 2 * self.route_layer_pitch + self.route_layer_width, 0) # end = col_decode_clk.lc() - vector( 2 * self.route_layer_pitch + self.route_layer_width, 0)
self.add_path(self.route_stack[2], [clk_out.center(), end]) # self.add_path(self.route_stack[2], [clk_out.center(), end])
self.add_via_stack_center(from_layer=self.route_stack[2], # self.add_via_stack_center(from_layer=self.route_stack[2],
to_layer=row_decode_clk.layer, # to_layer=row_decode_clk.layer,
offset=end) # offset=end)
self.add_segment_center(col_decode_clk.layer, end, col_decode_clk.lc()) # self.add_segment_center(col_decode_clk.layer, end, col_decode_clk.lc())
def route_array_outputs(self): def route_array_outputs(self):
for i in range(self.cols): array_out_pins = [self.array_inst.get_pin("bl_0_{}".format(bl)) for bl in range(self.cols)]
bl_out = self.array_inst.get_pin("bl_0_{}".format(i)).center() inv_in_pins = [self.bitline_inv_inst.get_pin("in_{}".format(bl)) for bl in range(self.cols)]
inv_out_pins = [self.bitline_inv_inst.get_pin("out_{}".format(bl)) for bl in range(self.cols)]
mux_pins = [self.mux_inst.get_pin("bl_{}".format(bl)) for bl in range(self.cols)]
self.connect_col_pins(self.interconnect_layer, array_out_pins + inv_in_pins, round=True, directions="nonpref")
self.connect_col_pins(self.interconnect_layer, inv_out_pins + mux_pins, round=True, directions="nonpref")
bl_mux = self.mux_inst.get_pin("bl_{}".format(i)).center()
self.add_path(self.array.bitline_layer, [bl_out, bl_mux])
def route_output_buffers(self): def route_output_buffers(self):
mux = self.mux_inst mux = self.mux_inst
buf = self.output_buf_inst buf = self.output_inv_inst
route_nets = [ [mux.get_pin("bl_out_{}".format(bit)), buf.get_pin("in_{}".format(bit))] for bit in range(self.word_size)] route_nets = [ [mux.get_pin("bl_out_{}".format(bit)), buf.get_pin("in_{}".format(bit))] for bit in range(self.word_size)]
channel_ll = vector( route_nets[0][0].cx(), route_nets[0][1].cy() + self.m1_pitch * 3) channel_ll = vector( route_nets[0][0].cx(), route_nets[0][1].cy() + self.m1_pitch * 3)
@ -429,7 +505,7 @@ class rom_base_bank(design):
self.copy_layout_pin(self.control_inst, "clk_in", "clk") self.copy_layout_pin(self.control_inst, "clk_in", "clk")
for i in range(self.word_size): for i in range(self.word_size):
self.copy_layout_pin(self.output_buf_inst, "out_{}".format(i), "rom_out_{}".format(i)) self.copy_layout_pin(self.output_inv_inst, "out_{}".format(i), "rom_out_{}".format(i))
for lsb in range(self.col_bits): for lsb in range(self.col_bits):
name = "addr_{}".format(lsb) name = "addr_{}".format(lsb)
self.copy_layout_pin(self.col_decode_inst, "A{}".format(lsb), name) self.copy_layout_pin(self.col_decode_inst, "A{}".format(lsb), name)
@ -448,27 +524,7 @@ class rom_base_bank(design):
if not inst.mod.name.__contains__("contact"): if not inst.mod.name.__contains__("contact"):
self.copy_layout_pin(inst, "vdd") self.copy_layout_pin(inst, "vdd")
self.copy_layout_pin(inst, "gnd") self.copy_layout_pin(inst, "gnd")
# gnd_start = vector(self.array_inst.get_pins("gnd")[0].cx(),0)
# decode_gnd = self.decode_inst.get_pin("gnd")
# decode_vdd = self.decode_inst.get_pin("vdd")
# array_vdd = self.array_inst.get_pin("vdd")
# # self.add_segment_center("m1", gnd_start, decode_gnd.center())
# self.add_power_pin("gnd", decode_vdd.center())
# self.add_power_pin("vdd", decode_gnd.center())
# vdd_start = vector(array_vdd.lx() + 0.5 * self.via1_space, array_vdd.cy())
# end = vector(decode_vdd.lx(), vdd_start.y)
# self.add_segment_center(self.interconnect_layer, vdd_start, end)
# self.add_via_stack_center(vdd_start, "m1", self.interconnect_layer)
# vdd_start = vector(decode_vdd.cx(), vdd_start.y)
# self.add_segment_center(self.interconnect_layer, vdd_start, decode_vdd.center())

View File

@ -20,7 +20,7 @@ class rom_control_logic(design):
if self.height is not None: if self.height is not None:
self.driver_height = 0.5 * self.height self.driver_height = 0.5 * self.height
self.gate_height = 0.25 * self.height self.gate_height = 0.5 * self.height
else: else:
self.gate_height = 20 * self.m1_pitch self.gate_height = 20 * self.m1_pitch
self.driver_height = self.gate_height self.driver_height = self.gate_height
@ -51,16 +51,18 @@ class rom_control_logic(design):
self.route_insts() self.route_insts()
def add_modules(self): def add_modules(self):
self.buf_mod = factory.create(module_type="pdriver",
self.buf_mod = factory.create(module_type="pinvbuf", module_name="rom_clock_driver",
module_name="rom_control_logic_pinv",
height=self.gate_height, height=self.gate_height,
route_in_cell=True ) fanout=self.clk_fanout + 2,
add_wells=True,
)
self.nand_mod = factory.create(module_type="pnand2", self.nand_mod = factory.create(module_type="pnand2",
module_name="rom_control_nand", module_name="rom_control_nand",
height=self.gate_height, height=self.gate_height,
add_wells=False) add_wells=False)
self.driver_mod = factory.create(module_type="pdriver", self.driver_mod = factory.create(module_type="pdriver",
module_name="rom_precharge_driver",
inverting=True, inverting=True,
fanout=self.output_size, fanout=self.output_size,
height=self.driver_height, height=self.driver_height,
@ -77,34 +79,36 @@ class rom_control_logic(design):
def create_instances(self): def create_instances(self):
self.buf_inst = self.add_inst(name="clk_invbuf", mod=self.buf_mod) self.buf_inst = self.add_inst(name="clk_driver", mod=self.buf_mod)
self.connect_inst(["clk_in", "clk_bar", "clk_out", "vdd", "gnd"]) self.connect_inst(["clk_in", "clk_out", "vdd", "gnd"])
self.nand_inst = self.add_inst(name="control_nand", mod=self.nand_mod) self.nand_inst = self.add_inst(name="control_nand", mod=self.nand_mod)
self.connect_inst(["CS", "clk_out", "pre_drive", "vdd", "gnd"]) self.connect_inst(["CS", "clk_out", "pre_drive", "vdd", "gnd"])
self.driver_inst = self.add_inst(name="driver_inst", mod=self.driver_mod) self.driver_inst = self.add_inst(name="precharge_driver", mod=self.driver_mod)
self.connect_inst(["pre_drive", "prechrg", "vdd", "gnd"]) self.connect_inst(["pre_drive", "prechrg", "vdd", "gnd"])
def place_instances(self): def place_instances(self):
buf_correction = drc["minwidth_{}".format(self.route_stack[0])] * 0.5
# nand_y = self.buf_inst.get_pin("vdd").cy() - self.nand_inst.get_pin("vdd").cy() # nand_y = self.buf_inst.get_pin("vdd").cy() - self.nand_inst.get_pin("vdd").cy()
self.nand_inst.place(offset=[0, self.nand_inst.height + self.buf_mod.inv2.height + buf_correction], mirror="MX") self.nand_inst.place(offset=[self.buf_inst.width, 0])
self.driver_inst.place(offset=[0, self.buf_inst.height + buf_correction]) self.driver_inst.place(offset=[0, self.buf_inst.height + self.driver_inst.height], mirror="MX")
offset = self.driver_inst.get_pin("vdd").cy() - self.nand_inst.get_pin("vdd").cy()
print("offset: {}".format(offset))
self.driver_inst.place(offset=[0, self.buf_inst.height + self.driver_inst.height - offset], mirror="MX")
def route_insts(self): def route_insts(self):
route_width = drc["minwidth_{}".format(self.route_stack[2])] route_width = drc["minwidth_{}".format(self.route_stack[2])]
self.copy_layout_pin(self.buf_inst, "A", "clk_in") self.copy_layout_pin(self.buf_inst, "A", "clk_in")
self.copy_layout_pin(self.buf_inst, "Zb", "clkb_out")
self.copy_layout_pin(self.buf_inst, "Z", "clk_out") self.copy_layout_pin(self.buf_inst, "Z", "clk_out")
self.copy_layout_pin(self.driver_inst, "Z", "prechrg") self.copy_layout_pin(self.driver_inst, "Z", "prechrg")
self.copy_layout_pin(self.nand_inst, "A", "CS") self.copy_layout_pin(self.nand_inst, "A", "CS")
self.copy_layout_pin(self.buf_inst, "gnd") self.copy_layout_pin(self.buf_inst, "gnd")
self.copy_layout_pin(self.driver_inst, "vdd") self.copy_layout_pin(self.driver_inst, "vdd")
self.copy_layout_pin(self.buf_inst, "vdd") self.copy_layout_pin(self.buf_inst, "vdd")
# self.copy_layout_pin(self.buf_inst, "vdd")
clk = self.buf_inst.get_pin("Z") clk = self.buf_inst.get_pin("Z")
@ -114,7 +118,7 @@ class rom_control_logic(design):
# Connect buffered clock bar to nand input # Connect buffered clock bar to nand input
mid = vector(clk.lx() - route_width - 2 * self.m1_space) mid = vector(clk.lx() - route_width - 2 * self.m1_space)
self.add_path(self.route_stack[2], [clk.center(), mid, nand_B.center()]) self.add_path(self.route_stack[2], [clk.center(), nand_B.center()])
self.add_via_stack_center(from_layer=clk.layer, self.add_via_stack_center(from_layer=clk.layer,
to_layer=self.route_stack[2], to_layer=self.route_stack[2],
@ -126,15 +130,17 @@ class rom_control_logic(design):
# Connect nand output to precharge driver # Connect nand output to precharge driver
nand_Z = self.nand_inst.get_pin("Z") nand_Z = self.nand_inst.get_pin("Z")
nand_output = vector(nand_Z.cx(), nand_B.cy() + 3 * route_width)
driver_A = self.driver_inst.get_pin("A") driver_A = self.driver_inst.get_pin("A")
self.add_path(self.route_stack[2], [nand_output, driver_A.center()])
mid = vector(driver_A.cx(), driver_A.cy() - 4 * route_width)
self.add_path(self.route_stack[2], [nand_Z.center(), mid, driver_A.center()])
self.add_via_stack_center(from_layer=nand_Z.layer, self.add_via_stack_center(from_layer=nand_Z.layer,
to_layer=self.route_stack[2], to_layer=self.route_stack[2],
offset=nand_output) offset=nand_Z.center())
self.add_via_stack_center(from_layer=driver_A.layer, self.add_via_stack_center(from_layer=driver_A.layer,
to_layer=self.route_stack[2], to_layer=self.route_stack[2],

View File

@ -14,7 +14,7 @@ from openram.tech import drc
class rom_decoder(design): class rom_decoder(design):
def __init__(self, num_outputs, cols, strap_spacing, name="", route_layer="m1", output_layer="m1", invert_outputs=False): def __init__(self, num_outputs, fanout, strap_spacing, name="", route_layer="m1", output_layer="m1", invert_outputs=False):
# word lines/ rows / inputs in the base array become the address lines / cols / inputs in the decoder # word lines/ rows / inputs in the base array become the address lines / cols / inputs in the decoder
# bit lines / cols / outputs in the base array become the word lines / rows / outputs in the decoder # bit lines / cols / outputs in the base array become the word lines / rows / outputs in the decoder
@ -33,7 +33,7 @@ class rom_decoder(design):
self.route_layer = route_layer self.route_layer = route_layer
self.output_layer = output_layer self.output_layer = output_layer
self.inv_route_layer = "m2" self.inv_route_layer = "m2"
self.cols=cols self.fanout=fanout
self.invert_outputs=invert_outputs self.invert_outputs=invert_outputs
self.create_netlist() self.create_netlist()
@ -58,6 +58,17 @@ class rom_decoder(design):
self.route_supplies() self.route_supplies()
self.add_boundary() self.add_boundary()
def add_boundary(self):
ll = self.find_lowest_coords()
m1_offset = self.m1_width
self.translate_all(vector(0, ll.y))
ur = self.find_highest_coords()
ur = vector(ur.x, ur.y)
super().add_boundary(ll, ur)
self.width = ur.x
self.height = ur.y
def setup_layout_constants(self): def setup_layout_constants(self):
self.inv_route_width = drc["minwidth_{}".format(self.inv_route_layer)] self.inv_route_width = drc["minwidth_{}".format(self.inv_route_layer)]
@ -114,7 +125,7 @@ class rom_decoder(design):
self.wordline_buf = factory.create(module_type="rom_wordline_driver_array", module_name="{}_wordline_buffer".format(self.name), self.wordline_buf = factory.create(module_type="rom_wordline_driver_array", module_name="{}_wordline_buffer".format(self.name),
rows=self.num_outputs, rows=self.num_outputs,
cols=ceil(self.cols * 0.5), fanout=ceil(self.fanout),
invert_outputs=self.invert_outputs, invert_outputs=self.invert_outputs,
tap_spacing=self.strap_spacing) tap_spacing=self.strap_spacing)
@ -224,7 +235,7 @@ class rom_decoder(design):
def connect_inputs(self): def connect_inputs(self):
self.copy_layout_pin(self.array_inst, "precharge") self.copy_layout_pin(self.array_inst, "precharge")
self.copy_layout_pin(self.array_inst, "precharge_r")
for i in range(self.num_inputs): for i in range(self.num_inputs):
wl = (self.num_inputs - i) * 2 - 1 wl = (self.num_inputs - i) * 2 - 1
wl_bar = wl - 1 wl_bar = wl - 1

View File

@ -19,16 +19,16 @@ class rom_wordline_driver_array(design):
Creates a Wordline Buffer/Inverter array Creates a Wordline Buffer/Inverter array
""" """
def __init__(self, name, rows, cols, invert_outputs=False, tap_spacing=4): def __init__(self, name, rows, fanout, invert_outputs=False, tap_spacing=4, flip_io=False):
design.__init__(self, name) design.__init__(self, name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.add_comment("rows: {0} cols: {1}".format(rows, cols)) self.add_comment("rows: {0} Buffer size of: {1}".format(rows, fanout))
self.rows = rows self.rows = rows
self.cols = cols self.fanout = fanout
self.invert_outputs=invert_outputs self.invert_outputs=invert_outputs
self.tap_spacing = tap_spacing self.tap_spacing = tap_spacing
self.flip_io = flip_io
if OPTS.tech_name == "sky130": if OPTS.tech_name == "sky130":
self.supply_layer = "m1" self.supply_layer = "m1"
else: else:
@ -51,7 +51,8 @@ class rom_wordline_driver_array(design):
self.place_drivers() self.place_drivers()
self.route_layout() self.route_layout()
self.route_supplies() self.route_supplies()
self.place_taps() if self.tap_spacing != 0:
self.place_taps()
self.add_boundary() self.add_boundary()
def add_pins(self): def add_pins(self):
@ -70,13 +71,14 @@ class rom_wordline_driver_array(design):
if self.invert_outputs: if self.invert_outputs:
self.wl_driver = factory.create(module_type="pinv_dec", self.wl_driver = factory.create(module_type="pinv_dec",
size=self.cols, size=self.fanout,
height=b.height, height=b.height,
add_wells=False) add_wells=False,
flip_io=self.flip_io)
else: else:
self.wl_driver = factory.create(module_type="pbuf_dec", self.wl_driver = factory.create(module_type="pbuf_dec",
size=self.cols, size=self.fanout,
height=b.height, height=b.height,
add_wells=False) add_wells=False)
@ -108,7 +110,7 @@ class rom_wordline_driver_array(design):
def place_drivers(self): def place_drivers(self):
y_offset = 0 y_offset = 0
for row in range(self.rows): for row in range(self.rows):
if row % self.tap_spacing == 0: if self.tap_spacing != 0 and row % self.tap_spacing == 0:
y_offset += self.tap.pitch_offset y_offset += self.tap.pitch_offset
offset = [0, y_offset] offset = [0, y_offset]
@ -123,20 +125,34 @@ class rom_wordline_driver_array(design):
""" Route all of the signals """ """ Route all of the signals """
route_width = drc["minwidth_{}".format(self.route_layer)] route_width = drc["minwidth_{}".format(self.route_layer)]
for row in range(self.rows): for row in range(self.rows):
inst = self.wld_inst[row] if self.flip_io:
row_num = self.rows - row - 1
else:
row_num = row
inst = self.wld_inst[row_num]
self.copy_layout_pin(inst, "vdd") self.copy_layout_pin(inst, "vdd")
self.copy_layout_pin(inst, "gnd") self.copy_layout_pin(inst, "gnd")
self.copy_layout_pin(inst, "A", "in_{0}".format(row)) self.copy_layout_pin(inst, "A", "in_{0}".format(row))
out_pin = inst.get_pin("Z")
# output each WL on the right # output each WL on the right
wl_offset = inst.get_pin("Z").rc() - vector( 0.5 * route_width, 0) if self.flip_io:
wl_offset = out_pin.lc() - vector(1.6 * route_width, 0)
else:
wl_offset = out_pin.rc() - vector( 0.5 * route_width, 0)
end = vector(wl_offset.x, \ end = vector(wl_offset.x, \
self.get_pin("in_{}".format(row)).cy() + 0.5 * route_width) self.get_pin("in_{}".format(row)).cy() + 0.5 * route_width)
self.add_segment_center(layer=self.route_layer, self.add_segment_center(layer=self.route_layer,
start=wl_offset, start=wl_offset,
end=end) end=end)
if self.flip_io:
self.add_segment_center(layer=self.route_layer,
start=out_pin.lc(),
end=vector(wl_offset.x - 0.5 * route_width, out_pin.cy()))
self.add_layout_pin_rect_center(text="out_{}".format(row), layer=self.route_layer, offset=end - vector(0, 0.5 * route_width)) self.add_layout_pin_rect_center(text="out_{}".format(row), layer=self.route_layer, offset=end - vector(0, 0.5 * route_width))

View File

@ -24,8 +24,11 @@ class rom_bank_test(openram_test):
debug.info(1, "Testing 1kB rom cell") debug.info(1, "Testing 1kB rom cell")
a = factory.create(module_type="rom_base_bank", strap_spacing = 8, data_file="/openram/technology/rom_data_1kB", word_size=1) a = factory.create(module_type="rom_base_bank", strap_spacing = 8, data_file="/openram/technology/rom_data_1kB", word_size=1)
print('wriitng file')
a.sp_write(OPTS.openram_temp + 'simulation_file.sp')
self.local_check(a) self.local_check(a)
openram.end_openram() openram.end_openram()
# run the test from the command line # run the test from the command line

View File

@ -0,0 +1,39 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 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.
#
import unittest
from testutils import *
import sys, os
import openram
from openram import OPTS
from openram.sram_factory import factory
from openram import debug
class rom_bank_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
debug.info(1, "Testing 2kB rom cell")
a = factory.create(module_type="rom_base_bank", strap_spacing = 8, data_file="/openram/technology/rom_data_2kB", word_size=1)
print('wriitng file')
a.sp_write(OPTS.openram_temp + 'simulation_file.sp')
self.local_check(a)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,39 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 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.
#
import unittest
from testutils import *
import sys, os
import openram
from openram import OPTS
from openram.sram_factory import factory
from openram import debug
class rom_bank_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
debug.info(1, "Testing 4kB rom cell")
a = factory.create(module_type="rom_base_bank", strap_spacing = 8, data_file="/openram/technology/rom_data_4kB", word_size=2)
print('wriitng file')
a.sp_write(OPTS.openram_temp + 'simulation_file.sp')
self.local_check(a)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,39 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 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.
#
import unittest
from testutils import *
import sys, os
import openram
from openram import OPTS
from openram.sram_factory import factory
from openram import debug
class rom_bank_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
openram.init_openram(config_file, is_unit_test=True)
debug.info(1, "Testing 8kB rom cell")
a = factory.create(module_type="rom_base_bank", strap_spacing = 8, data_file="/openram/technology/rom_data_8kB", word_size=2)
print('wriitng file')
a.sp_write(OPTS.openram_temp + 'simulation_file_8kB.sp')
self.local_check(a)
openram.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = openram.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())