Merge branch 'add_wmask' into dev

This commit is contained in:
Matt Guthaus 2019-08-22 15:01:41 -07:00
commit ee2456f433
23 changed files with 855 additions and 277 deletions

View File

@ -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 """

View File

@ -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)

View File

@ -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.")

View 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_{}",

View File

@ -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,

View File

@ -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):

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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):
"""

View File

@ -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:

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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))

View File

@ -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 >>