mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'add_wmask' into dev
This commit is contained in:
commit
ee2456f433
|
|
@ -680,81 +680,92 @@ class layout():
|
|||
self.add_via_center(layers=layer_stack,
|
||||
offset=bus_pos,
|
||||
rotate=90)
|
||||
|
||||
def get_layer_pitch(self, layer):
|
||||
""" Return the track pitch on a given layer """
|
||||
if layer=="metal1":
|
||||
return (self.m1_pitch,self.m1_pitch-self.m1_space,self.m1_space)
|
||||
elif layer=="metal2":
|
||||
return (self.m2_pitch,self.m2_pitch-self.m2_space,self.m2_space)
|
||||
elif layer=="metal3":
|
||||
return (self.m3_pitch,self.m3_pitch-self.m3_space,self.m3_space)
|
||||
elif layer=="metal4":
|
||||
from tech import layer as tech_layer
|
||||
if "metal4" in tech_layer:
|
||||
return (self.m3_pitch,self.m3_pitch-self.m4_space,self.m4_space)
|
||||
else:
|
||||
return (self.m3_pitch,self.m3_pitch-self.m3_space,self.m3_space)
|
||||
else:
|
||||
debug.error("Cannot find layer pitch.")
|
||||
|
||||
def add_horizontal_trunk_route(self,
|
||||
pins,
|
||||
trunk_offset,
|
||||
layer_stack=("metal1", "via1", "metal2"),
|
||||
pitch=None):
|
||||
layer_stack,
|
||||
pitch):
|
||||
"""
|
||||
Create a trunk route for all pins with the trunk located at the given y offset.
|
||||
"""
|
||||
if not pitch:
|
||||
pitch = self.m1_pitch
|
||||
|
||||
max_x = max([pin.center().x for pin in pins])
|
||||
min_x = min([pin.center().x for pin in pins])
|
||||
|
||||
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[0])]
|
||||
|
||||
# if we are less than a pitch, just create a non-preferred layer jog
|
||||
if max_x-min_x < pitch:
|
||||
if max_x-min_x <= pitch:
|
||||
|
||||
half_layer_width = 0.5*drc["minwidth_{0}".format(self.vertical_layer)]
|
||||
|
||||
# Add the horizontal trunk on the vertical layer!
|
||||
self.add_path(layer_stack[2],[vector(min_x-half_minwidth,trunk_offset.y), vector(max_x+half_minwidth,trunk_offset.y)])
|
||||
self.add_path(self.vertical_layer,[vector(min_x-half_layer_width,trunk_offset.y), vector(max_x+half_layer_width,trunk_offset.y)])
|
||||
|
||||
# Route each pin to the trunk
|
||||
for pin in pins:
|
||||
# No bend needed here
|
||||
mid = vector(pin.center().x, trunk_offset.y)
|
||||
self.add_path(layer_stack[2], [pin.center(), mid])
|
||||
self.add_path(self.vertical_layer, [pin.center(), mid])
|
||||
else:
|
||||
# Add the horizontal trunk
|
||||
self.add_path(layer_stack[0],[vector(min_x,trunk_offset.y), vector(max_x,trunk_offset.y)])
|
||||
self.add_path(self.horizontal_layer,[vector(min_x,trunk_offset.y), vector(max_x,trunk_offset.y)])
|
||||
trunk_mid = vector(0.5*(max_x+min_x),trunk_offset.y)
|
||||
|
||||
# Route each pin to the trunk
|
||||
for pin in pins:
|
||||
mid = vector(pin.center().x, trunk_offset.y)
|
||||
self.add_path(layer_stack[2], [pin.center(), mid])
|
||||
self.add_path(self.vertical_layer, [pin.center(), mid])
|
||||
self.add_via_center(layers=layer_stack,
|
||||
offset=mid)
|
||||
|
||||
def add_vertical_trunk_route(self,
|
||||
pins,
|
||||
trunk_offset,
|
||||
layer_stack=("metal1", "via1", "metal2"),
|
||||
pitch=None):
|
||||
layer_stack,
|
||||
pitch):
|
||||
"""
|
||||
Create a trunk route for all pins with the trunk located at the given x offset.
|
||||
"""
|
||||
if not pitch:
|
||||
pitch = self.m2_pitch
|
||||
|
||||
max_y = max([pin.center().y for pin in pins])
|
||||
min_y = min([pin.center().y for pin in pins])
|
||||
|
||||
# Add the vertical trunk
|
||||
half_minwidth = 0.5*drc["minwidth_{}".format(layer_stack[2])]
|
||||
|
||||
# if we are less than a pitch, just create a non-preferred layer jog
|
||||
if max_y-min_y < pitch:
|
||||
# Add the horizontal trunk on the vertical layer!
|
||||
self.add_path(layer_stack[0],[vector(trunk_offset.x,min_y-half_minwidth), vector(trunk_offset.x,max_y+half_minwidth)])
|
||||
if max_y-min_y <= pitch:
|
||||
|
||||
half_layer_width = 0.5*drc["minwidth_{0}".format(self.horizontal_layer)]
|
||||
|
||||
# Add the vertical trunk on the horizontal layer!
|
||||
self.add_path(self.horizontal_layer,[vector(trunk_offset.x,min_y-half_layer_width), vector(trunk_offset.x,max_y+half_layer_width)])
|
||||
|
||||
# Route each pin to the trunk
|
||||
for pin in pins:
|
||||
# No bend needed here
|
||||
mid = vector(trunk_offset.x, pin.center().y)
|
||||
self.add_path(layer_stack[0], [pin.center(), mid])
|
||||
self.add_path(self.horizontal_layer, [pin.center(), mid])
|
||||
else:
|
||||
# Add the vertical trunk
|
||||
self.add_path(layer_stack[2],[vector(trunk_offset.x,min_y), vector(trunk_offset.x,max_y)])
|
||||
self.add_path(self.vertical_layer,[vector(trunk_offset.x,min_y), vector(trunk_offset.x,max_y)])
|
||||
trunk_mid = vector(trunk_offset.x,0.5*(max_y+min_y),)
|
||||
|
||||
# Route each pin to the trunk
|
||||
for pin in pins:
|
||||
mid = vector(trunk_offset.x, pin.center().y)
|
||||
self.add_path(layer_stack[0], [pin.center(), mid])
|
||||
self.add_path(self.horizontal_layer, [pin.center(), mid])
|
||||
self.add_via_center(layers=layer_stack,
|
||||
offset=mid)
|
||||
|
||||
|
|
@ -762,7 +773,6 @@ class layout():
|
|||
def create_channel_route(self, netlist,
|
||||
offset,
|
||||
layer_stack=("metal1", "via1", "metal2"),
|
||||
pitch=None,
|
||||
vertical=False):
|
||||
"""
|
||||
The net list is a list of the nets. Each net is a list of pins
|
||||
|
|
@ -786,7 +796,7 @@ class layout():
|
|||
g[other_pin]=conflicts
|
||||
return g
|
||||
|
||||
def vcg_nets_overlap(net1, net2, vertical):
|
||||
def vcg_nets_overlap(net1, net2, vertical, pitch):
|
||||
"""
|
||||
Check all the pin pairs on two nets and return a pin
|
||||
overlap if any pin overlaps
|
||||
|
|
@ -794,12 +804,12 @@ class layout():
|
|||
|
||||
for pin1 in net1:
|
||||
for pin2 in net2:
|
||||
if vcg_pin_overlap(pin1, pin2, vertical):
|
||||
if vcg_pin_overlap(pin1, pin2, vertical, pitch):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def vcg_pin_overlap(pin1, pin2, vertical):
|
||||
def vcg_pin_overlap(pin1, pin2, vertical, pitch):
|
||||
""" Check for vertical or horizontal overlap of the two pins """
|
||||
# FIXME: If the pins are not in a row, this may break.
|
||||
# However, a top pin shouldn't overlap another top pin, for example, so the
|
||||
|
|
@ -813,10 +823,15 @@ class layout():
|
|||
overlaps = (not vertical and x_overlap) or (vertical and y_overlap)
|
||||
return overlaps
|
||||
|
||||
if self.get_preferred_direction(layer_stack[0])=="V":
|
||||
self.vertical_layer = layer_stack[0]
|
||||
self.horizontal_layer = layer_stack[2]
|
||||
else:
|
||||
self.vertical_layer = layer_stack[2]
|
||||
self.horizontal_layer = layer_stack[0]
|
||||
|
||||
|
||||
if not pitch:
|
||||
pitch = self.m2_pitch
|
||||
(self.vertical_pitch,self.vertical_width,self.vertical_space) = self.get_layer_pitch(self.vertical_layer)
|
||||
(self.horizontal_pitch,self.horizontal_width,self.horizontal_space) = self.get_layer_pitch(self.horizontal_layer)
|
||||
|
||||
|
||||
# FIXME: Must extend this to a horizontal conflict graph too if we want to minimize the
|
||||
|
|
@ -846,7 +861,9 @@ class layout():
|
|||
# Skip yourself
|
||||
if net_name1 == net_name2:
|
||||
continue
|
||||
if vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical):
|
||||
if vertical and vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical, self.vertical_pitch):
|
||||
vcg[net_name2].append(net_name1)
|
||||
elif not vertical and vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical, self.horizontal_pitch):
|
||||
vcg[net_name2].append(net_name1)
|
||||
|
||||
# list of routes to do
|
||||
|
|
@ -874,28 +891,26 @@ class layout():
|
|||
|
||||
# Add the trunk routes from the bottom up for horizontal or the left to right for vertical
|
||||
if vertical:
|
||||
self.add_vertical_trunk_route(pin_list, offset, layer_stack, pitch)
|
||||
offset += vector(pitch,0)
|
||||
self.add_vertical_trunk_route(pin_list, offset, layer_stack, self.vertical_pitch)
|
||||
offset += vector(self.vertical_pitch,0)
|
||||
else:
|
||||
self.add_horizontal_trunk_route(pin_list, offset, layer_stack, pitch)
|
||||
offset += vector(0,pitch)
|
||||
self.add_horizontal_trunk_route(pin_list, offset, layer_stack, self.horizontal_pitch)
|
||||
offset += vector(0,self.horizontal_pitch)
|
||||
|
||||
|
||||
def create_vertical_channel_route(self, netlist, offset,
|
||||
layer_stack=("metal1", "via1", "metal2"),
|
||||
pitch=None):
|
||||
layer_stack=("metal1", "via1", "metal2")):
|
||||
"""
|
||||
Wrapper to create a vertical channel route
|
||||
"""
|
||||
self.create_channel_route(netlist, offset, layer_stack, pitch, vertical=True)
|
||||
self.create_channel_route(netlist, offset, layer_stack, vertical=True)
|
||||
|
||||
def create_horizontal_channel_route(self, netlist, offset,
|
||||
layer_stack=("metal1", "via1", "metal2"),
|
||||
pitch=None):
|
||||
layer_stack=("metal1", "via1", "metal2")):
|
||||
"""
|
||||
Wrapper to create a horizontal channel route
|
||||
"""
|
||||
self.create_channel_route(netlist, offset, layer_stack, pitch, vertical=False)
|
||||
self.create_channel_route(netlist, offset, layer_stack, vertical=False)
|
||||
|
||||
def add_boundary(self, offset=vector(0,0)):
|
||||
""" Add boundary for debugging dimensions """
|
||||
|
|
|
|||
|
|
@ -102,7 +102,6 @@ class functional(simulation):
|
|||
# Write at least once
|
||||
addr = self.gen_addr()
|
||||
word = self.gen_data()
|
||||
# print("write", self.t_current, addr, word)
|
||||
comment = self.gen_cycle_comment("write", word, addr, self.wmask, 0, self.t_current)
|
||||
self.add_write(comment, addr, word, self.wmask, 0)
|
||||
self.stored_words[addr] = word
|
||||
|
|
@ -113,9 +112,8 @@ class functional(simulation):
|
|||
if port in self.write_ports:
|
||||
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port)
|
||||
else:
|
||||
# print("read", self.t_current, addr, word)
|
||||
comment = self.gen_cycle_comment("read", word, addr, self.wmask, port, self.t_current)
|
||||
self.add_read_one_port(comment, addr, rw_read_din_data, "1"*self.num_wmasks, port)
|
||||
self.add_read_one_port(comment, addr, rw_read_din_data, "0"*self.num_wmasks, port)
|
||||
self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check])
|
||||
check += 1
|
||||
self.cycle_times.append(self.t_current)
|
||||
|
|
@ -150,7 +148,7 @@ class functional(simulation):
|
|||
self.stored_words[addr] = word
|
||||
w_addrs.append(addr)
|
||||
elif op == "partial_write":
|
||||
#write only to a word that's been written to
|
||||
# write only to a word that's been written to
|
||||
(addr,old_word) = self.get_data()
|
||||
word = self.gen_data()
|
||||
wmask = self.gen_wmask()
|
||||
|
|
@ -177,7 +175,7 @@ class functional(simulation):
|
|||
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port)
|
||||
else:
|
||||
comment = self.gen_cycle_comment("read", word, addr, self.wmask, port, self.t_current)
|
||||
self.add_read_one_port(comment, addr, rw_read_din_data, "1"*self.num_wmasks, port)
|
||||
self.add_read_one_port(comment, addr, rw_read_din_data, "0"*self.num_wmasks, port)
|
||||
self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check])
|
||||
check += 1
|
||||
|
||||
|
|
@ -259,7 +257,7 @@ class functional(simulation):
|
|||
|
||||
def get_data(self):
|
||||
""" Gets an available address and corresponding word. """
|
||||
# Currently unused but may need later depending on how the functional test develops
|
||||
# Used for write masks since they should be writing to previously written addresses
|
||||
addr = random.choice(list(self.stored_words.keys()))
|
||||
word = self.stored_words[addr]
|
||||
return (addr,word)
|
||||
|
|
|
|||
|
|
@ -476,10 +476,14 @@ def report_status():
|
|||
# If a write mask is specified by the user, the mask write size should be the same as
|
||||
# the word size so that an entire word is written at once.
|
||||
if OPTS.write_size is not None:
|
||||
if (OPTS.write_size < 1 or OPTS.write_size > OPTS.word_size):
|
||||
debug.error("Write size needs to be between 1 bit and {0} bits.".format(OPTS.word_size))
|
||||
if (OPTS.word_size % OPTS.write_size != 0):
|
||||
debug.error("Write size needs to be an integer multiple of word size.")
|
||||
# If write size is more than half of the word size, then it doesn't need a write mask. It would be writing
|
||||
# the whole word.
|
||||
if (OPTS.write_size < 1 or OPTS.write_size > OPTS.word_size/2):
|
||||
debug.error("Write size needs to be between 1 bit and {0} bits/2.".format(OPTS.word_size))
|
||||
|
||||
|
||||
|
||||
if not OPTS.tech_name:
|
||||
debug.error("Tech name must be specified in config file.")
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ class bank(design.design):
|
|||
self.route_column_address_lines(port)
|
||||
self.route_control_lines(port)
|
||||
if self.num_banks > 1:
|
||||
self.route_bank_select(port)
|
||||
self.route_bank_select(port)
|
||||
|
||||
self.route_supplies()
|
||||
|
||||
|
|
@ -711,8 +711,9 @@ class bank(design.design):
|
|||
decoder_name = "addr_{}".format(row)
|
||||
addr_name = "addr{0}_{1}".format(port,addr_idx)
|
||||
self.copy_layout_pin(self.port_address_inst[port], decoder_name, addr_name)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def route_port_data_in(self, port):
|
||||
""" Connecting port data in """
|
||||
|
||||
|
|
@ -720,6 +721,14 @@ class bank(design.design):
|
|||
data_name = "din_{}".format(row)
|
||||
din_name = "din{0}_{1}".format(port,row)
|
||||
self.copy_layout_pin(self.port_data_inst[port], data_name, din_name)
|
||||
|
||||
if self.word_size is not None:
|
||||
for row in range(self.num_wmasks):
|
||||
wmask_name = "bank_wmask_{}".format(row)
|
||||
bank_wmask_name = "bank_wmask{0}_{1}".format(port, row)
|
||||
self.copy_layout_pin(self.port_data_inst[port], wmask_name, bank_wmask_name)
|
||||
|
||||
|
||||
|
||||
def channel_route_bitlines(self, inst1, inst2, num_bits,
|
||||
inst1_bl_name="bl_{}", inst1_br_name="br_{}",
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ class port_data(design.design):
|
|||
|
||||
|
||||
def route_layout(self):
|
||||
""" Create routing amoung the modules """
|
||||
""" Create routing among the modules """
|
||||
self.route_data_lines()
|
||||
self.route_layout_pins()
|
||||
self.route_supplies()
|
||||
|
|
@ -123,7 +123,9 @@ class port_data(design.design):
|
|||
""" Route the bitlines depending on the port type rw, w, or r. """
|
||||
|
||||
if self.port in self.readwrite_ports:
|
||||
# write_driver -> sense_amp -> (column_mux) -> precharge -> bitcell_array
|
||||
# (write_mask_and ->) write_driver -> sense_amp -> (column_mux ->) precharge -> bitcell_array
|
||||
self.route_write_mask_and_array_in(self.port)
|
||||
self.route_write_mask_and_array_to_write_driver(self.port)
|
||||
self.route_write_driver_in(self.port)
|
||||
self.route_sense_amp_out(self.port)
|
||||
self.route_write_driver_to_sense_amp(self.port)
|
||||
|
|
@ -135,8 +137,10 @@ class port_data(design.design):
|
|||
self.route_sense_amp_to_column_mux_or_precharge_array(self.port)
|
||||
self.route_column_mux_to_precharge_array(self.port)
|
||||
else:
|
||||
# write_driver -> (column_mux ->) precharge -> bitcell_array
|
||||
self.route_write_driver_in(self.port)
|
||||
# (write_mask_and ->) write_driver -> (column_mux ->) precharge -> bitcell_array
|
||||
self.route_write_mask_and_array_in(self.port)
|
||||
self.route_write_mask_and_array_to_write_driver(self.port)
|
||||
self.route_write_driver_in(self.port)
|
||||
self.route_write_driver_to_column_mux_or_precharge_array(self.port)
|
||||
self.route_column_mux_to_precharge_array(self.port)
|
||||
|
||||
|
|
@ -298,23 +302,23 @@ class port_data(design.design):
|
|||
""" Placing Sense amp """
|
||||
self.sense_amp_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
|
||||
def create_write_driver_array(self):
|
||||
""" Creating Write Driver """
|
||||
self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port),
|
||||
self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port),
|
||||
mod=self.write_driver_array)
|
||||
|
||||
temp = []
|
||||
for bit in range(self.word_size):
|
||||
temp.append("din_{}".format(bit))
|
||||
|
||||
for bit in range(self.word_size):
|
||||
for bit in range(self.word_size):
|
||||
if (self.words_per_row == 1):
|
||||
temp.append(self.bl_names[self.port]+"_{0}".format(bit))
|
||||
temp.append(self.br_names[self.port]+"_{0}".format(bit))
|
||||
temp.append(self.bl_names[self.port] + "_{0}".format(bit))
|
||||
temp.append(self.br_names[self.port] + "_{0}".format(bit))
|
||||
else:
|
||||
temp.append(self.bl_names[self.port]+"_out_{0}".format(bit))
|
||||
temp.append(self.br_names[self.port]+"_out_{0}".format(bit))
|
||||
temp.append(self.bl_names[self.port] + "_out_{0}".format(bit))
|
||||
temp.append(self.br_names[self.port] + "_out_{0}".format(bit))
|
||||
|
||||
if self.write_size:
|
||||
for i in range(self.num_wmasks):
|
||||
|
|
@ -326,10 +330,15 @@ class port_data(design.design):
|
|||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_write_driver_array(self, offset):
|
||||
""" Placing Write Driver """
|
||||
self.write_driver_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
def create_write_mask_and_array(self):
|
||||
""" Creating Write Masks """
|
||||
""" Creating Write Mask AND Array """
|
||||
self.write_mask_and_array_inst = self.add_inst(name="write_mask_and_array{}".format(self.port),
|
||||
mod=self.write_mask_and_array)
|
||||
mod=self.write_mask_and_array)
|
||||
|
||||
temp = []
|
||||
for bit in range(self.num_wmasks):
|
||||
|
|
@ -341,10 +350,10 @@ class port_data(design.design):
|
|||
self.connect_inst(temp)
|
||||
|
||||
|
||||
def place_write_driver_array(self, offset):
|
||||
""" Placing Write Driver """
|
||||
self .write_driver_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
def place_write_mask_and_array(self, offset):
|
||||
""" Placing Write Mask AND array """
|
||||
self.write_mask_and_array_inst.place(offset=offset, mirror="MX")
|
||||
|
||||
|
||||
def compute_instance_offsets(self):
|
||||
"""
|
||||
|
|
@ -356,41 +365,41 @@ class port_data(design.design):
|
|||
vertical_port_order.append(self.column_mux_array_inst)
|
||||
vertical_port_order.append(self.sense_amp_array_inst)
|
||||
vertical_port_order.append(self.write_driver_array_inst)
|
||||
vertical_port_order.append(self.write_mask_and_array_inst)
|
||||
|
||||
# Add one column for the the RBL
|
||||
if self.port==0:
|
||||
x_offset = self.bitcell.width
|
||||
else:
|
||||
x_offset = 0
|
||||
|
||||
vertical_port_offsets = 4*[None]
|
||||
|
||||
vertical_port_offsets = 5 * [None]
|
||||
self.width = x_offset
|
||||
self.height = 0
|
||||
for i,p in enumerate(vertical_port_order):
|
||||
if p==None:
|
||||
for i, p in enumerate(vertical_port_order):
|
||||
if p == None:
|
||||
continue
|
||||
self.height += (p.height + self.m2_gap)
|
||||
self.width = max(self.width, p.width)
|
||||
vertical_port_offsets[i]=vector(x_offset,self.height)
|
||||
vertical_port_offsets[i] = vector(x_offset, self.height)
|
||||
|
||||
# Reversed order
|
||||
self.write_mask_and_offset = vertical_port_offsets[4]
|
||||
self.write_driver_offset = vertical_port_offsets[3]
|
||||
self.sense_amp_offset = vertical_port_offsets[2]
|
||||
self.column_mux_offset = vertical_port_offsets[1]
|
||||
self.precharge_offset = vertical_port_offsets[0]
|
||||
# Shift the precharge left if port 0
|
||||
if self.precharge_offset and self.port==0:
|
||||
self.precharge_offset -= vector(x_offset,0)
|
||||
|
||||
if self.precharge_offset and self.port == 0:
|
||||
self.precharge_offset -= vector(x_offset, 0)
|
||||
|
||||
|
||||
|
||||
|
||||
def place_instances(self):
|
||||
""" Place the instances. """
|
||||
|
||||
# These are fixed in the order: write driver, sense amp, clumn mux, precharge,
|
||||
# These are fixed in the order: write mask ANDs, write driver, sense amp, column mux, precharge,
|
||||
# even if the item is not used in a given port (it will be None then)
|
||||
if self.write_mask_and_offset:
|
||||
self.place_write_mask_and_array(self.write_mask_and_offset)
|
||||
if self.write_driver_offset:
|
||||
self.place_write_driver_array(self.write_driver_offset)
|
||||
if self.sense_amp_offset:
|
||||
|
|
@ -400,6 +409,7 @@ class port_data(design.design):
|
|||
if self.column_mux_offset:
|
||||
self.place_column_mux_array(self.column_mux_offset)
|
||||
|
||||
|
||||
def route_sense_amp_out(self, port):
|
||||
""" Add pins for the sense amp output """
|
||||
|
||||
|
|
@ -410,7 +420,8 @@ class port_data(design.design):
|
|||
offset=data_pin.center(),
|
||||
height=data_pin.height(),
|
||||
width=data_pin.width())
|
||||
|
||||
|
||||
|
||||
def route_write_driver_in(self, port):
|
||||
""" Connecting write driver """
|
||||
|
||||
|
|
@ -418,7 +429,58 @@ class port_data(design.design):
|
|||
data_name = "data_{}".format(row)
|
||||
din_name = "din_{}".format(row)
|
||||
self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name)
|
||||
|
||||
|
||||
|
||||
def route_write_mask_and_array_in(self, port):
|
||||
""" Add pins for the write mask and array input """
|
||||
|
||||
for bit in range(self.num_wmasks):
|
||||
wmask_in_name = "wmask_in_{}".format(bit)
|
||||
bank_wmask_name = "bank_wmask_{}".format(bit)
|
||||
self.copy_layout_pin(self.write_mask_and_array_inst, wmask_in_name, bank_wmask_name)
|
||||
|
||||
|
||||
def route_write_mask_and_array_to_write_driver(self,port):
|
||||
""" Routing of wdriver_sel_{} between write mask AND array and write driver array. Adds layout pin for write
|
||||
mask AND array output and via for write driver enable """
|
||||
|
||||
inst1 = self.write_mask_and_array_inst
|
||||
inst2 = self.write_driver_array_inst
|
||||
|
||||
loc = 0
|
||||
for bit in range(self.num_wmasks):
|
||||
# Bring write mask AND array output pin to port data level
|
||||
self.copy_layout_pin(inst1, "wmask_out_{0}".format(bit), "wdriver_sel_{0}".format(bit))
|
||||
|
||||
wmask_out_pin = inst1.get_pin("wmask_out_{0}".format(bit))
|
||||
wdriver_en_pin = inst2.get_pin("en_{0}".format(bit))
|
||||
|
||||
# The metal2 wdriver_sel_{} wire must hit the en_{} pin after the closest bitline pin that's right of the
|
||||
# the wdriver_sel_{} pin in the write driver AND array.
|
||||
if bit == 0:
|
||||
# When the write mask output pin is right of the bitline, the target is found
|
||||
while (wmask_out_pin.lx() + self.m2_pitch > inst2.get_pin("data_{0}".format(loc)).rx()):
|
||||
loc += 1
|
||||
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch
|
||||
debug.check(loc<=self.num_wmasks,"Couldn't route the write mask select.")
|
||||
else:
|
||||
# Stride by the write size rather than finding the next pin to the right
|
||||
loc += self.write_size
|
||||
length = inst2.get_pin("data_{0}".format(loc)).rx() + self.m2_pitch
|
||||
|
||||
|
||||
beg_pos = wmask_out_pin.center()
|
||||
middle_pos = vector(length,wmask_out_pin.cy())
|
||||
end_pos = vector(length, wdriver_en_pin.cy())
|
||||
|
||||
# Add via for the write driver array's enable input
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=end_pos)
|
||||
|
||||
# Route between write mask AND array and write driver array
|
||||
self.add_wire(("metal1","via1","metal2"), [beg_pos, middle_pos, end_pos])
|
||||
|
||||
|
||||
def route_column_mux_to_precharge_array(self, port):
|
||||
""" Routing of BL and BR between col mux and precharge array """
|
||||
|
||||
|
|
@ -434,7 +496,6 @@ class port_data(design.design):
|
|||
self.connect_bitlines(inst1, inst2, self.num_cols)
|
||||
|
||||
|
||||
|
||||
def route_sense_amp_to_column_mux_or_precharge_array(self, port):
|
||||
""" Routing of BL and BR between sense_amp and column mux or precharge array """
|
||||
inst2 = self.sense_amp_array_inst
|
||||
|
|
@ -459,6 +520,7 @@ class port_data(design.design):
|
|||
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)
|
||||
|
||||
|
||||
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
|
||||
|
|
@ -481,10 +543,11 @@ class port_data(design.design):
|
|||
|
||||
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)
|
||||
|
||||
|
||||
|
||||
def route_write_driver_to_sense_amp(self, port):
|
||||
""" Routing of BL and BR between write driver and sense amp """
|
||||
|
||||
|
||||
inst1 = self.write_driver_array_inst
|
||||
inst2 = self.sense_amp_array_inst
|
||||
|
||||
|
|
@ -508,13 +571,13 @@ class port_data(design.design):
|
|||
else:
|
||||
bit_offset=0
|
||||
|
||||
|
||||
for bit in range(self.num_cols):
|
||||
if self.precharge_array_inst:
|
||||
self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit+bit_offset), "bl_{}".format(bit))
|
||||
self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(bit+bit_offset), "br_{}".format(bit))
|
||||
else:
|
||||
debug.error("Didn't find precharge arra.")
|
||||
debug.error("Didn't find precharge array.")
|
||||
|
||||
|
||||
def route_control_pins(self):
|
||||
""" Add the control pins: s_en, p_en_bar, w_en """
|
||||
|
|
@ -527,7 +590,15 @@ class port_data(design.design):
|
|||
if self.sense_amp_array_inst:
|
||||
self.copy_layout_pin(self.sense_amp_array_inst, "en", "s_en")
|
||||
if self.write_driver_array_inst:
|
||||
self.copy_layout_pin(self.write_driver_array_inst, "en", "w_en")
|
||||
if self.write_mask_and_array_inst:
|
||||
for bit in range(self.num_wmasks):
|
||||
# Add write driver's en_{} pins
|
||||
self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit), "wdriver_sel_{}".format(bit))
|
||||
else:
|
||||
self.copy_layout_pin(self.write_driver_array_inst, "en", "w_en")
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ class write_driver_array(design.design):
|
|||
self.width = self.columns * self.bitcell.width
|
||||
else:
|
||||
self.width = self.columns * self.driver.width
|
||||
|
||||
self.height = self.driver.height
|
||||
|
||||
self.place_write_array()
|
||||
|
|
@ -63,9 +62,9 @@ class write_driver_array(design.design):
|
|||
for i in range(self.word_size):
|
||||
self.add_pin("bl_{0}".format(i), "OUTPUT")
|
||||
self.add_pin("br_{0}".format(i), "OUTPUT")
|
||||
if self.write_size != None:
|
||||
if self.write_size is not None:
|
||||
for i in range(self.num_wmasks):
|
||||
self.add_pin("en_{}".format(i), "INPUT")
|
||||
self.add_pin("en_{0}".format(i), "INPUT")
|
||||
else:
|
||||
self.add_pin("en", "INPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
|
|
@ -95,6 +94,7 @@ class write_driver_array(design.design):
|
|||
"br_{0}".format(index),
|
||||
"en_{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
|
||||
|
|
@ -107,13 +107,12 @@ class write_driver_array(design.design):
|
|||
|
||||
def place_write_array(self):
|
||||
if self.bitcell.width > self.driver.width:
|
||||
driver_spacing = self.bitcell.width
|
||||
self.driver_spacing = self.bitcell.width
|
||||
else:
|
||||
driver_spacing = self.driver.width
|
||||
|
||||
self.driver_spacing = self.driver.width
|
||||
for i in range(0,self.columns,self.words_per_row):
|
||||
index = int(i/self.words_per_row)
|
||||
base = vector(i * driver_spacing,0)
|
||||
index = int(i/self.words_per_row)
|
||||
base = vector(i * self.driver_spacing, 0)
|
||||
self.driver_insts[index].place(base)
|
||||
|
||||
|
||||
|
|
@ -149,15 +148,28 @@ class write_driver_array(design.design):
|
|||
self.add_layout_pin_rect_center(text=n,
|
||||
layer="metal3",
|
||||
offset=pin_pos)
|
||||
if self.write_size is not None:
|
||||
for bit in range(self.num_wmasks):
|
||||
en_pin = self.driver_insts[bit*self.write_size].get_pin("en")
|
||||
# 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="en_{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",
|
||||
layer="metal1",
|
||||
offset=self.driver_insts[0].get_pin("en").ll().scale(0,1),
|
||||
width=self.width)
|
||||
|
||||
|
||||
|
||||
self.add_layout_pin(text="en",
|
||||
layer="metal1",
|
||||
offset=self.driver_insts[0].get_pin("en").ll().scale(0,1),
|
||||
width=self.width,
|
||||
height=drc('minwidth_metal1'))
|
||||
|
||||
|
||||
|
||||
def get_w_en_cin(self):
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ from globals import OPTS
|
|||
|
||||
class write_mask_and_array(design.design):
|
||||
"""
|
||||
Array of tristate drivers to write to the bitlines through the column mux.
|
||||
Dynamically generated write driver array of all bitlines.
|
||||
Array of AND gates to turn write mask signal on only when w_en is on.
|
||||
The write mask AND array goes between the write driver array and the sense amp array.
|
||||
"""
|
||||
|
||||
def __init__(self, name, columns, word_size, write_size):
|
||||
|
|
@ -34,29 +34,23 @@ class write_mask_and_array(design.design):
|
|||
self.num_wmasks = int(word_size / write_size)
|
||||
|
||||
self.create_netlist()
|
||||
# if not OPTS.netlist_only:
|
||||
# self.create_layout()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
# self.create_write_mask_array()
|
||||
self.create_and2_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 create_layout(self):
|
||||
self.place_and2_array()
|
||||
spacing = self.wmask_en_len - self.and2.width
|
||||
self.width = (self.num_wmasks*self.and2.width) + ((self.num_wmasks-1)*spacing)
|
||||
self.height = self.and2.height
|
||||
self.add_layout_pins()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for bit in range(self.num_wmasks):
|
||||
|
|
@ -68,28 +62,13 @@ class write_mask_and_array(design.design):
|
|||
self.add_pin("gnd","GROUND")
|
||||
|
||||
def add_modules(self):
|
||||
self.wmask = factory.create(module_type="dff_buf")
|
||||
#self.add_mod(self.wmask)
|
||||
dff_height = self.wmask.height
|
||||
|
||||
# Size the AND gate for the number of write drivers it drives, which is equal to the write size.
|
||||
# Assume stage effort of 3 to compute the size
|
||||
self.and2 = factory.create(module_type="pand2",
|
||||
size=4,
|
||||
height=dff_height)
|
||||
size=self.write_size/4.0)
|
||||
self.add_mod(self.and2)
|
||||
|
||||
|
||||
# def create_write_mask_array(self):
|
||||
# self.wmask_insts = {}
|
||||
# for bit in range(self.num_wmask):
|
||||
# name = "write_mask_{}".format(bit)
|
||||
# self.wmask_insts[bit] = self.add_inst(name=name,
|
||||
# mod=self.wmask)
|
||||
#
|
||||
# self.connect_inst(["wmask_{}".format(bit),
|
||||
# "bank_wmask_{}".format(bit),
|
||||
# "bank_wmask_bar_{}".format(bit),
|
||||
# "clk", "vdd", "gnd"])
|
||||
|
||||
def create_and2_array(self):
|
||||
self.and2_insts = {}
|
||||
for bit in range(self.num_wmasks):
|
||||
|
|
@ -102,59 +81,84 @@ class write_mask_and_array(design.design):
|
|||
"vdd", "gnd"])
|
||||
|
||||
|
||||
# def place_write_array(self):
|
||||
# if self.bitcell.width > self.driver.width:
|
||||
# driver_spacing = self.bitcell.width
|
||||
# else:
|
||||
# driver_spacing = self.driver.width
|
||||
#
|
||||
# for i in range(0, self.columns, self.words_per_row):
|
||||
# index = int(i / self.words_per_row)
|
||||
# base = vector(i * driver_spacing, 0)
|
||||
# self.driver_insts[index].place(base)
|
||||
def place_and2_array(self):
|
||||
# Place the write mask AND array at the start of each write driver enable length.
|
||||
# This ensures the write mask AND array will be directly under the corresponding write driver enable wire.
|
||||
|
||||
# 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),
|
||||
# layer="metal2",
|
||||
# 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),
|
||||
# layer="metal2",
|
||||
# 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),
|
||||
# layer="metal2",
|
||||
# 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:
|
||||
# pin_pos = pin.center()
|
||||
# # Add the M2->M3 stack
|
||||
# self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
# offset=pin_pos)
|
||||
# self.add_layout_pin_rect_center(text=n,
|
||||
# layer="metal3",
|
||||
# offset=pin_pos)
|
||||
#
|
||||
# self.add_layout_pin(text="en",
|
||||
# layer="metal1",
|
||||
# offset=self.driver_insts[0].get_pin("en").ll().scale(0, 1),
|
||||
# width=self.width,
|
||||
# height=drc('minwidth_metal1'))
|
||||
# This is just used for measurements, so don't add the module
|
||||
self.bitcell = factory.create(module_type="bitcell")
|
||||
self.driver = factory.create(module_type="write_driver")
|
||||
if self.bitcell.width > self.driver.width:
|
||||
self.driver_spacing = self.bitcell.width
|
||||
else:
|
||||
self.driver_spacing = self.driver.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)
|
||||
self.wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing)
|
||||
debug.check(self.wmask_en_len >= self.and2.width,
|
||||
"Write mask AND is wider than the corresponding write drivers {0} vs {1}.".format(self.and2.width,self.wmask_en_len))
|
||||
|
||||
for i in range(self.num_wmasks):
|
||||
base = vector(i * self.wmask_en_len, 0)
|
||||
self.and2_insts[i].place(base)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
self.nand2 = factory.create(module_type="pnand2")
|
||||
supply_pin=self.nand2.get_pin("vdd")
|
||||
for i in range(self.num_wmasks):
|
||||
wmask_in_pin = self.and2_insts[i].get_pin("A")
|
||||
self.add_layout_pin(text="wmask_in_{0}".format(i),
|
||||
layer=wmask_in_pin.layer,
|
||||
offset=wmask_in_pin.ll(),
|
||||
width=wmask_in_pin.width(),
|
||||
height=wmask_in_pin.height())
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=wmask_in_pin.center())
|
||||
|
||||
en_pin = self.and2_insts[i].get_pin("B")
|
||||
# Add the M1->M2 stack
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=en_pin.center())
|
||||
# Add the M2->M3 stack
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=en_pin.center())
|
||||
|
||||
# Route en pin between AND gates
|
||||
if i < self.num_wmasks-1:
|
||||
self.add_layout_pin(text="en",
|
||||
layer="metal3",
|
||||
offset=en_pin.bc(),
|
||||
width = self.en_width(i),
|
||||
height = drc('minwidth_metal3'))
|
||||
|
||||
wmask_out_pin = self.and2_insts[i].get_pin("Z")
|
||||
self.add_layout_pin(text="wmask_out_{0}".format(i),
|
||||
layer=wmask_out_pin.layer,
|
||||
offset=wmask_out_pin.ll(),
|
||||
width=wmask_out_pin.width(),
|
||||
height=wmask_out_pin.height())
|
||||
|
||||
self.add_power_pin("gnd", vector(supply_pin.width() + i * self.wmask_en_len, 0))
|
||||
self.add_power_pin("vdd", vector(supply_pin.width() + i * self.wmask_en_len, self.height))
|
||||
|
||||
if i < self.num_wmasks-1:
|
||||
for n in ["gnd","vdd"]:
|
||||
pin = self.and2_insts[i].get_pin(n)
|
||||
next_pin = self.and2_insts[i+1].get_pin(n)
|
||||
self.add_path("metal1",[pin.center(),next_pin.center()])
|
||||
|
||||
|
||||
def en_width(self, pin):
|
||||
en_pin = self.and2_insts[pin].get_pin("B")
|
||||
next_en_pin = self.and2_insts[pin+1].get_pin("B")
|
||||
width = next_en_pin.center() - en_pin.center()
|
||||
# Return x coordinates only
|
||||
return width[0]
|
||||
|
||||
|
||||
def get_cin(self):
|
||||
"""Get the relative capacitance of all the input connections in the bank"""
|
||||
# The enable is connected to an and2 for every row.
|
||||
return self.and2.get_cin() * len(self.and2_insts)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ class pand2(pgate.pgate):
|
|||
# Shield the cap, but have at least a stage effort of 4
|
||||
self.nand = factory.create(module_type="pnand2",height=self.height)
|
||||
self.add_mod(self.nand)
|
||||
|
||||
self.inv = factory.create(module_type="pinv", size=self.size, height=self.height)
|
||||
|
||||
self.inv = factory.create(module_type="pdriver", neg_polarity=True, fanout=3*self.size, height=self.height)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
def create_layout(self):
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class pand3(pgate.pgate):
|
|||
# Shield the cap, but have at least a stage effort of 4
|
||||
self.nand = factory.create(module_type="pnand3",height=self.height)
|
||||
self.add_mod(self.nand)
|
||||
|
||||
|
||||
self.inv = factory.create(module_type="pinv", size=self.size, height=self.height)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class pdriver(pgate.pgate):
|
|||
self.num_stages = len(self.size_list)
|
||||
else:
|
||||
# Find the optimal number of stages for the given effort
|
||||
self.num_stages = max(1,int(round(log(self.fanout)/log(self.stage_effort))))
|
||||
self.num_stages = max(1,int(round(self.fanout**(1/self.stage_effort))))
|
||||
|
||||
# Increase the number of stages if we need to fix polarity
|
||||
if self.neg_polarity and (self.num_stages%2==0):
|
||||
|
|
|
|||
|
|
@ -470,9 +470,9 @@ class pin_group:
|
|||
# Now, make sure each pin touches an enclosure. If not, add another (diagonal) connector.
|
||||
# This could only happen when there was no enclosure in any cardinal direction from a pin
|
||||
if not self.overlap_any_shape(self.pins, self.enclosures):
|
||||
connector = self.find_smallest_connector(pin_list, self.enclosures)
|
||||
connector = self.find_smallest_connector(self.pins, self.enclosures)
|
||||
if connector==None:
|
||||
debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures))
|
||||
debug.error("Could not find a connector for {} with {}".format(self.pins, self.enclosures))
|
||||
self.router.write_debug_gds("no_connector.gds")
|
||||
self.enclosures.append(connector)
|
||||
|
||||
|
|
|
|||
|
|
@ -43,13 +43,13 @@ class sram_1bank(sram_base):
|
|||
|
||||
if self.col_addr_dff:
|
||||
self.col_addr_dff_insts = self.create_col_addr_dff()
|
||||
|
||||
self.data_dff_insts = self.create_data_dff()
|
||||
|
||||
if self.write_size:
|
||||
self.wmask_dff_insts = self.create_wmask_dff()
|
||||
self.data_dff_insts = self.create_data_dff()
|
||||
else:
|
||||
self.data_dff_insts = self.create_data_dff()
|
||||
|
||||
|
||||
def place_instances(self):
|
||||
"""
|
||||
This places the instances for a single bank SRAM with control
|
||||
|
|
@ -67,8 +67,8 @@ class sram_1bank(sram_base):
|
|||
control_pos = [None]*len(self.all_ports)
|
||||
row_addr_pos = [None]*len(self.all_ports)
|
||||
col_addr_pos = [None]*len(self.all_ports)
|
||||
data_pos = [None]*len(self.all_ports)
|
||||
wmask_pos = [None]*len(self.all_ports)
|
||||
data_pos = [None]*len(self.all_ports)
|
||||
|
||||
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
||||
# The M1 pitch is for supply rail spacings
|
||||
|
|
@ -77,17 +77,34 @@ class sram_1bank(sram_base):
|
|||
# Port 0
|
||||
port = 0
|
||||
|
||||
# Add the data flops below the bank to the right of the lower-left of bank array
|
||||
# This relies on the lower-left of the array of the bank
|
||||
# decoder in upper left, bank in upper right, sensing in lower right.
|
||||
# These flops go below the sensing and leave a gap to channel route to the
|
||||
# sense amps.
|
||||
if port in self.write_ports:
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
||||
-max_gap_size - self.dff.height)
|
||||
self.data_dff_insts[port].place(data_pos[port])
|
||||
if self.write_size is not None:
|
||||
if port in self.write_ports:
|
||||
# Add the write mask flops below the write mask AND array.
|
||||
wmask_pos[port] = vector(self.bank.bank_array_ll.x,
|
||||
-0.5*max_gap_size - self.dff.height)
|
||||
self.wmask_dff_insts[port].place(wmask_pos[port])
|
||||
|
||||
# Add the data flops below the write mask flops.
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
||||
-1.5*max_gap_size - 2*self.dff.height)
|
||||
self.data_dff_insts[port].place(data_pos[port])
|
||||
else:
|
||||
wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0)
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x,0)
|
||||
|
||||
else:
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x,0)
|
||||
# Add the data flops below the bank to the right of the lower-left of bank array
|
||||
# This relies on the lower-left of the array of the bank
|
||||
# decoder in upper left, bank in upper right, sensing in lower right.
|
||||
# These flops go below the sensing and leave a gap to channel route to the
|
||||
# sense amps.
|
||||
if port in self.write_ports:
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
||||
-max_gap_size - self.dff.height)
|
||||
self.data_dff_insts[port].place(data_pos[port])
|
||||
|
||||
else:
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x,0)
|
||||
|
||||
# Add the col address flops below the bank to the left of the lower-left of bank array
|
||||
if self.col_addr_dff:
|
||||
|
|
@ -115,46 +132,32 @@ class sram_1bank(sram_base):
|
|||
-max_gap_size - self.col_addr_dff_insts[port].height)
|
||||
self.col_addr_dff_insts[port].place(col_addr_pos[port])
|
||||
|
||||
# Add the data flops below the bank to the right of the lower-left of bank array
|
||||
# This relies on the lower-left of the array of the bank
|
||||
# decoder in upper left, bank in upper right, sensing in lower right.
|
||||
# These flops go below the sensing and leave a gap to channel route to the
|
||||
# sense amps.
|
||||
if port in self.write_ports:
|
||||
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
||||
-max_gap_size - self.data_dff_insts[port].height)
|
||||
self.data_dff_insts[port].place(data_pos[port])
|
||||
|
||||
# Add the write mask flops to the left of the din flops.
|
||||
if self.write_size:
|
||||
if port in self.write_ports:
|
||||
wmask_pos[port] = vector(self.bank.bank_array_ll.x - self.control_logic_insts[port].width,
|
||||
-max_gap_size - self.wmask_dff_insts[port].height)
|
||||
self.wmask_dff_insts[port].place(wmask_pos[port])
|
||||
|
||||
|
||||
if len(self.all_ports)>1:
|
||||
# Port 1
|
||||
port = 1
|
||||
|
||||
# Add the data flops above the bank to the left of the upper-right of bank array
|
||||
# This relies on the upper-right of the array of the bank
|
||||
# decoder in upper left, bank in upper right, sensing in lower right.
|
||||
# These flops go below the sensing and leave a gap to channel route to the
|
||||
# sense amps.
|
||||
if port in self.write_ports:
|
||||
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
||||
self.bank.height + max_gap_size + self.dff.height)
|
||||
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
||||
|
||||
# Add the write mask flops to the left of the din flops.
|
||||
if self.write_size:
|
||||
if port in self.write_ports:
|
||||
if self.write_size is not None:
|
||||
# Add the write mask flops below the write mask AND array.
|
||||
wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
||||
self.bank.height + max_gap_size + self.data_dff_insts[port].height)
|
||||
self.bank.height + 0.5*max_gap_size + self.dff.height)
|
||||
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
|
||||
else:
|
||||
data_pos[port] = self.bank_inst.ur()
|
||||
|
||||
# Add the data flops below the write mask flops
|
||||
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
||||
self.bank.height + 1.5*max_gap_size + 2*self.dff.height)
|
||||
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
||||
|
||||
else:
|
||||
# Add the data flops above the bank to the left of the upper-right of bank array
|
||||
# This relies on the upper-right of the array of the bank
|
||||
# decoder in upper left, bank in upper right, sensing in lower right.
|
||||
# These flops go below the sensing and leave a gap to channel route to the
|
||||
# sense amps.
|
||||
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
||||
self.bank.height + max_gap_size + self.dff.height)
|
||||
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
||||
|
||||
# Add the col address flops above the bank to the right of the upper-right of bank array
|
||||
if self.col_addr_dff:
|
||||
|
|
@ -178,24 +181,7 @@ class sram_1bank(sram_base):
|
|||
y_offset = min(self.control_logic_insts[port].by(), self.bank_inst.by() + self.row_addr_dff_insts[port].height)
|
||||
row_addr_pos[port] = vector(x_offset, y_offset)
|
||||
self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY")
|
||||
|
||||
# Add the data flops above the bank to the left of the upper-right of bank array
|
||||
# This relies on the upper-right of the array of the bank
|
||||
# decoder in upper left, bank in upper right, sensing in lower right.
|
||||
# These flops go below the sensing and leave a gap to channel route to the
|
||||
# sense amps.
|
||||
if port in self.write_ports:
|
||||
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
||||
self.bank.height + max_gap_size + self.dff.height)
|
||||
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
||||
|
||||
# Add the write mask flops to the left of the din flops.
|
||||
if self.write_size:
|
||||
if port in self.write_ports:
|
||||
wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
||||
self.bank.height + max_gap_size + self.data_dff_insts[port].height)
|
||||
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
"""
|
||||
|
|
@ -220,6 +206,11 @@ class sram_1bank(sram_base):
|
|||
if port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.copy_layout_pin(self.data_dff_insts[port], "din_{}".format(bit), "din{0}[{1}]".format(port,bit))
|
||||
|
||||
if self.write_size is not None:
|
||||
for bit in range(self.num_wmasks):
|
||||
self.copy_layout_pin(self.wmask_dff_insts[port], "din_{}".format(bit), "wmask{0}[{1}]".format(port,bit))
|
||||
|
||||
|
||||
def route_layout(self):
|
||||
""" Route a single bank SRAM """
|
||||
|
|
@ -236,6 +227,9 @@ class sram_1bank(sram_base):
|
|||
self.route_col_addr_dff()
|
||||
|
||||
self.route_data_dff()
|
||||
|
||||
if self.write_size is not None:
|
||||
self.route_wmask_dff()
|
||||
|
||||
def route_clk(self):
|
||||
""" Route the clock network """
|
||||
|
|
@ -288,6 +282,15 @@ class sram_1bank(sram_base):
|
|||
self.add_path("metal2",[mid_pos, clk_steiner_pos], width=max(m2m3.width,m2m3.height))
|
||||
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||
|
||||
if self.write_size is not None:
|
||||
wmask_dff_clk_pin = self.wmask_dff_insts[port].get_pin("clk")
|
||||
wmask_dff_clk_pos = wmask_dff_clk_pin.center()
|
||||
mid_pos = vector(clk_steiner_pos.x, wmask_dff_clk_pos.y)
|
||||
# In some designs, the steiner via will be too close to the mid_pos via
|
||||
# so make the wire as wide as the contacts
|
||||
self.add_path("metal2", [mid_pos, clk_steiner_pos], width=max(m2m3.width, m2m3.height))
|
||||
self.add_wire(("metal3", "via2", "metal2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||
|
||||
|
||||
def route_control_logic(self):
|
||||
""" Route the control logic pins that are not inputs """
|
||||
|
|
@ -354,19 +357,70 @@ class sram_1bank(sram_base):
|
|||
if port%2:
|
||||
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch)
|
||||
else:
|
||||
offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
|
||||
offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
|
||||
|
||||
|
||||
dff_names = ["dout_{}".format(x) for x in range(self.word_size)]
|
||||
dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
|
||||
if self.write_size is not None:
|
||||
for x in dff_names:
|
||||
pin_offset = self.data_dff_insts[port].get_pin(x).center()
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pin_offset,
|
||||
directions = ("V", "V"))
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=pin_offset)
|
||||
self.add_via_center(layers=("metal3", "via3", "metal4"),
|
||||
offset=pin_offset)
|
||||
|
||||
bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
|
||||
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||
|
||||
if self.write_size is not None:
|
||||
for x in bank_names:
|
||||
pin_offset = self.bank_inst.get_pin(x).bc()
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pin_offset)
|
||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||
offset=pin_offset)
|
||||
self.add_via_center(layers=("metal3", "via3", "metal4"),
|
||||
offset=pin_offset)
|
||||
|
||||
route_map = list(zip(bank_pins, dff_pins))
|
||||
self.create_horizontal_channel_route(route_map, offset)
|
||||
|
||||
|
||||
if self.write_size is not None:
|
||||
self.create_horizontal_channel_route(netlist=route_map,
|
||||
offset=offset,
|
||||
layer_stack=("metal3", "via3", "metal4"))
|
||||
else:
|
||||
self.create_horizontal_channel_route(route_map, offset)
|
||||
|
||||
def route_wmask_dff(self):
|
||||
""" Connect the output of the wmask flops to the write mask AND array """
|
||||
# This is where the channel will start (y-dimension at least)
|
||||
for port in self.write_ports:
|
||||
if port % 2:
|
||||
offset = self.wmask_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m1_pitch)
|
||||
else:
|
||||
offset = self.wmask_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
|
||||
|
||||
dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)]
|
||||
dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names]
|
||||
for x in dff_names:
|
||||
offset_pin = self.wmask_dff_insts[port].get_pin(x).center()
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset_pin,
|
||||
directions=("V", "V"))
|
||||
|
||||
bank_names = ["bank_wmask{0}_{1}".format(port, x) for x in range(self.num_wmasks)]
|
||||
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||
for x in bank_names:
|
||||
offset_pin = self.bank_inst.get_pin(x).center()
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=offset_pin)
|
||||
|
||||
|
||||
route_map = list(zip(bank_pins, dff_pins))
|
||||
self.create_horizontal_channel_route(route_map,offset)
|
||||
|
||||
|
||||
def add_lvs_correspondence_points(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -501,7 +501,7 @@ class sram_base(design, verilog, lef):
|
|||
temp.append("clk{}".format(port))
|
||||
temp.append("rbl_bl{}".format(port))
|
||||
|
||||
# Ouputs
|
||||
# Outputs
|
||||
if port in self.read_ports:
|
||||
temp.append("s_en{}".format(port))
|
||||
if port in self.write_ports:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env python3
|
||||
# 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.
|
||||
#
|
||||
import unittest
|
||||
from testutils import *
|
||||
import sys, os
|
||||
|
||||
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
|
||||
class write_driver_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
|
||||
# check write driver array for single port
|
||||
debug.info(2, "Testing write_driver_array for columns=8, word_size=8, write_size=4")
|
||||
a = factory.create(module_type="write_driver_array", columns=8, word_size=8, write_size=4)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(2, "Testing write_driver_array for columns=16, word_size=16, write_size=2")
|
||||
a = factory.create(module_type="write_driver_array", columns=16, word_size=16, write_size=2)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(2, "Testing write_driver_array for columns=16, word_size=8, write_size=4")
|
||||
a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4)
|
||||
self.local_check(a)
|
||||
|
||||
# check write driver array for multi-port
|
||||
OPTS.bitcell = "pbitcell"
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
OPTS.num_r_ports = 0
|
||||
|
||||
factory.reset()
|
||||
debug.info(2, "Testing write_driver_array for columns=8, word_size=8, write_size=4 (multi-port case)")
|
||||
a = factory.create(module_type="write_driver_array", columns=8, word_size=8, write_size=4)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(2, "Testing write_driver_array for columns=16, word_size=8, write_size=4 (multi-port case)")
|
||||
a = factory.create(module_type="write_driver_array", columns=16, word_size=8, write_size=4)
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
# run the test from the command line
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env python3
|
||||
# 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.
|
||||
#
|
||||
import unittest
|
||||
from testutils import *
|
||||
import sys, os
|
||||
|
||||
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
|
||||
class write_mask_and_array_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
|
||||
# check write driver array for single port
|
||||
debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4")
|
||||
a = factory.create(module_type="write_mask_and_array", columns=8, word_size=8, write_size=4)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(2, "Testing write_mask_and_array for columns=16, word_size=16, write_size=4")
|
||||
a = factory.create(module_type="write_mask_and_array", columns=16, word_size=16, write_size=4)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(2, "Testing write_mask_and_array for columns=16, word_size=8, write_size=2")
|
||||
a = factory.create(module_type="write_mask_and_array", columns=16, word_size=8, write_size=2)
|
||||
self.local_check(a)
|
||||
|
||||
# check write driver array for multi-port
|
||||
OPTS.bitcell = "pbitcell"
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_w_ports = 0
|
||||
OPTS.num_r_ports = 0
|
||||
|
||||
factory.reset()
|
||||
debug.info(2, "Testing write_mask_and_array for columns=8, word_size=8, write_size=4 (multi-port case)")
|
||||
a = factory.create(module_type="write_mask_and_array", columns=8, word_size=8, write_size=4)
|
||||
self.local_check(a)
|
||||
|
||||
debug.info(2, "Testing write_mask_and_array for columns=16, word_size=8, write_size=2 (multi-port case)")
|
||||
a = factory.create(module_type="write_mask_and_array", columns=16, word_size=8, write_size=2)
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
# run the test from the command line
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/env python3
|
||||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2019 Regents of the University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
import unittest
|
||||
from testutils import *
|
||||
import sys, os
|
||||
|
||||
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
|
||||
class port_data_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
from sram_config import sram_config
|
||||
|
||||
c = sram_config(word_size=16,
|
||||
write_size=4,
|
||||
num_words=16)
|
||||
|
||||
c.words_per_row = 1
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "No column mux")
|
||||
a = factory.create("port_data", sram_config=c, port=0)
|
||||
self.local_check(a)
|
||||
|
||||
c.num_words = 32
|
||||
c.words_per_row = 2
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Two way column mux")
|
||||
a = factory.create("port_data", sram_config=c, port=0)
|
||||
self.local_check(a)
|
||||
|
||||
c.num_words = 64
|
||||
c.words_per_row = 4
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Four way column mux")
|
||||
a = factory.create("port_data", sram_config=c, port=0)
|
||||
self.local_check(a)
|
||||
|
||||
c.num_words = 128
|
||||
c.words_per_row = 8
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Eight way column mux")
|
||||
a = factory.create("port_data", sram_config=c, port=0)
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.bitcell = "bitcell_1w_1r"
|
||||
OPTS.num_rw_ports = 0
|
||||
OPTS.num_r_ports = 1
|
||||
OPTS.num_w_ports = 1
|
||||
|
||||
c.num_words = 16
|
||||
c.words_per_row = 1
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "No column mux")
|
||||
a = factory.create("port_data", sram_config=c, port=0)
|
||||
self.local_check(a)
|
||||
a = factory.create("port_data", sram_config=c, port=1)
|
||||
self.local_check(a)
|
||||
#
|
||||
c.num_words = 32
|
||||
c.words_per_row = 2
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Two way column mux")
|
||||
a = factory.create("port_data", sram_config=c, port=0)
|
||||
self.local_check(a)
|
||||
a = factory.create("port_data", sram_config=c, port=1)
|
||||
self.local_check(a)
|
||||
|
||||
c.num_words = 64
|
||||
c.words_per_row = 4
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Four way column mux")
|
||||
a = factory.create("port_data", sram_config=c, port=0)
|
||||
self.local_check(a)
|
||||
a = factory.create("port_data", sram_config=c, port=1)
|
||||
self.local_check(a)
|
||||
|
||||
c.word_size = 8
|
||||
c.num_words = 128
|
||||
c.words_per_row = 8
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Eight way column mux")
|
||||
a = factory.create("port_data", sram_config=c, port=0)
|
||||
self.local_check(a)
|
||||
a = factory.create("port_data", sram_config=c, port=1)
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
# run the test from the command line
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env python3
|
||||
# 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.
|
||||
#
|
||||
import unittest
|
||||
from testutils import *
|
||||
import sys,os
|
||||
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
class single_bank_wmask_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
from sram_config import sram_config
|
||||
|
||||
|
||||
c = sram_config(word_size=8,
|
||||
write_size=4,
|
||||
num_words=16,
|
||||
num_banks=1)
|
||||
|
||||
c.words_per_row=1
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "No column mux")
|
||||
a = factory.create("bank", sram_config=c)
|
||||
self.local_check(a)
|
||||
|
||||
c.num_words=32
|
||||
c.words_per_row=2
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Two way column mux")
|
||||
a = factory.create("bank", sram_config=c)
|
||||
self.local_check(a)
|
||||
|
||||
c.num_words=64
|
||||
c.words_per_row=4
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Four way column mux")
|
||||
a = factory.create("bank", sram_config=c)
|
||||
self.local_check(a)
|
||||
|
||||
c.num_words=128
|
||||
c.words_per_row=8
|
||||
factory.reset()
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Eight way column mux")
|
||||
a = factory.create("bank", sram_config=c)
|
||||
self.local_check(a)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
# run the test from the command line
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/env python3
|
||||
# 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.
|
||||
#
|
||||
import unittest
|
||||
from testutils import *
|
||||
import sys, os
|
||||
|
||||
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
|
||||
# @unittest.skip("SKIPPING 20_sram_1bank_2mux_wmask_test")
|
||||
class sram_1bank_2mux_wmask_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=8,
|
||||
write_size=4,
|
||||
num_words=64,
|
||||
num_banks=1)
|
||||
|
||||
c.words_per_row = 2
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w sram "
|
||||
"with {} bit words, {} words, {} bit writes, {} words per "
|
||||
"row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.write_size,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
a = factory.create(module_type="sram", sram_config=c)
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
# run the test from the command line
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/env python3
|
||||
# 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.
|
||||
#
|
||||
import unittest
|
||||
from testutils import *
|
||||
import sys, os
|
||||
|
||||
sys.path.append(os.getenv("OPENRAM_HOME"))
|
||||
import globals
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
import debug
|
||||
|
||||
|
||||
# @unittest.skip("SKIPPING 20_sram_1bank_nomux_wmask_test")
|
||||
class sram_1bank_nomux_wmask_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
from sram_config import sram_config
|
||||
c = sram_config(word_size=8,
|
||||
write_size=4,
|
||||
num_words=16,
|
||||
num_banks=1)
|
||||
|
||||
c.words_per_row = 1
|
||||
c.recompute_sizes()
|
||||
debug.info(1, "Layout test for {}rw,{}r,{}w sram "
|
||||
"with {} bit words, {} words, {} bit writes, {} words per "
|
||||
"row, {} banks".format(OPTS.num_rw_ports,
|
||||
OPTS.num_r_ports,
|
||||
OPTS.num_w_ports,
|
||||
c.word_size,
|
||||
c.num_words,
|
||||
c.write_size,
|
||||
c.words_per_row,
|
||||
c.num_banks))
|
||||
a = factory.create(module_type="sram", sram_config=c)
|
||||
self.local_check(a, final_verification=True)
|
||||
|
||||
globals.end_openram()
|
||||
|
||||
|
||||
# run the test from the command line
|
||||
if __name__ == "__main__":
|
||||
(OPTS, args) = globals.parse_args()
|
||||
del sys.argv[1:]
|
||||
header(__file__, OPTS.tech_name)
|
||||
unittest.main(testRunner=debugTestRunner())
|
||||
|
|
@ -17,8 +17,8 @@ from sram_factory import factory
|
|||
import debug
|
||||
|
||||
|
||||
# @unittest.skip("SKIPPING psram_wmask_func_test")
|
||||
class psram_wmask_func_test(openram_test):
|
||||
# @unittest.skip("SKIPPING sram_wmask_1w_1r_func_test")
|
||||
class sram_wmask_1w_1r_func_test(openram_test):
|
||||
|
||||
def runTest(self):
|
||||
globals.init_openram("config_{0}".format(OPTS.tech_name))
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
|||
magic
|
||||
tech scmos
|
||||
timestamp 1564675126
|
||||
timestamp 1565304081
|
||||
<< nwell >>
|
||||
rect -3 101 37 138
|
||||
rect -3 0 37 51
|
||||
|
|
@ -177,8 +177,8 @@ rect 27 45 31 60
|
|||
rect 3 35 7 38
|
||||
rect 19 35 23 38
|
||||
rect 7 31 19 35
|
||||
rect 0 24 7 28
|
||||
rect 11 24 36 28
|
||||
rect 3 24 7 28
|
||||
rect 11 24 31 28
|
||||
<< m2contact >>
|
||||
rect 10 193 14 197
|
||||
rect 20 190 24 194
|
||||
|
|
@ -198,7 +198,6 @@ rect 15 0 19 6
|
|||
rect 0 0 34 203
|
||||
<< labels >>
|
||||
rlabel metal2 15 1 15 1 1 din
|
||||
rlabel metal1 2 25 2 25 3 en
|
||||
rlabel m2contact 21 66 21 66 1 gnd
|
||||
rlabel m2contact 28 88 28 88 1 gnd
|
||||
rlabel m2contact 21 33 21 33 1 vdd
|
||||
|
|
@ -206,4 +205,5 @@ rlabel m2contact 18 120 18 120 1 vdd
|
|||
rlabel metal2 12 201 12 201 5 bl
|
||||
rlabel metal2 22 201 22 201 5 br
|
||||
rlabel m2contact 14 161 14 161 1 gnd
|
||||
rlabel metal1 17 26 17 26 1 en
|
||||
<< end >>
|
||||
|
|
|
|||
Loading…
Reference in New Issue