merged branch wtih dev

This commit is contained in:
Jesse Cirimelli-Low 2018-12-03 09:47:34 -08:00
commit 9501b99df7
57 changed files with 1230 additions and 1424 deletions

View File

@ -4,6 +4,10 @@ omit =
*/.local/*
# omit everything in /usr
/usr/*
# ignore the unit tests themselves
*/tests/*
# ignore the debug utilities
debug.py
[paths]
source =
/home/gitlab-runner/builds/2fd64746/0
@ -12,3 +16,13 @@ source =
/home/gitlab-runner/builds/2fd64746/3
/home/gitlab-runner/builds/2fd64746/4
/home/gitlab-runner/builds/2fd64746/5
[report]
exclude_lines =
pragma: no cover
def __repr__
except Exception
raise AssertionError
raise NotImplementedError
if 0:
if __name__ == "__main__":
if not OPTS.is_unit_test

View File

@ -673,11 +673,13 @@ class layout(lef.lef):
offset=bus_pos,
rotate=90)
def add_horizontal_trunk_route(self, pins, trunk_offset,
def add_horizontal_trunk_route(self,
pins,
trunk_offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None):
"""
Create a trunk route for all pins with the the trunk located at the given y offset.
Create a trunk route for all pins with the trunk located at the given y offset.
"""
if not pitch:
pitch = self.m1_pitch
@ -704,15 +706,18 @@ class layout(lef.lef):
# Route each pin to the trunk
for pin in pins:
# Bend to the center of the trunk so it adds a via automatically
mid = vector(pin.center().x, trunk_offset.y)
self.add_wire(layer_stack, [pin.center(), mid, trunk_mid])
self.add_path(layer_stack[2], [pin.center(), mid])
self.add_via_center(layers=layer_stack,
offset=mid)
def add_vertical_trunk_route(self, pins, trunk_offset,
def add_vertical_trunk_route(self,
pins,
trunk_offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None):
"""
Create a trunk route for all pins with the the trunk located at the given x offset.
Create a trunk route for all pins with the trunk located at the given x offset.
"""
if not pitch:
pitch = self.m2_pitch
@ -740,18 +745,21 @@ class layout(lef.lef):
# Route each pin to the trunk
for pin in pins:
# Bend to the center of the trunk so it adds a via automatically
mid = vector(trunk_offset.x, pin.center().y)
self.add_wire(layer_stack, [pin.center(), mid, trunk_mid])
self.add_path(layer_stack[0], [pin.center(), mid])
self.add_via_center(layers=layer_stack,
offset=mid,
rotate=90)
def create_channel_route(self, netlist, pins, offset,
layer_stack=("metal1", "via1", "metal2"), pitch=None,
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 pin
names to be connected. Pins is a dictionary of the pin names
to the pin structures. Offset is the lower-left of where the
The net list is a list of the nets. Each net is a list of pins
to be connected. Offset is the lower-left of where the
routing channel will start. This does NOT try to minimize the
number of tracks -- instead, it picks an order to avoid the
vertical conflicts between pins.
@ -786,7 +794,10 @@ class layout(lef.lef):
def vcg_pin_overlap(pin1, pin2, vertical):
""" 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
# extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x-pin2.center().x)<pitch
@ -815,10 +826,7 @@ class layout(lef.lef):
for pin_list in netlist:
net_name = "n{}".format(index)
index += 1
nets[net_name] = []
for pin_name in pin_list:
pin = pins[pin_name]
nets[net_name].append(pin)
nets[net_name] = pin_list
# Find the vertical pin conflicts
# FIXME: O(n^2) but who cares for now
@ -834,8 +842,6 @@ class layout(lef.lef):
if vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical):
vcg[net_name2].append(net_name1)
#FIXME: What if we have a cycle?
# list of routes to do
while vcg:
#from pprint import pformat
@ -868,23 +874,21 @@ class layout(lef.lef):
offset += vector(0,pitch)
def create_vertical_channel_route(self, netlist, pins, offset,
def create_vertical_channel_route(self, netlist, offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None):
"""
Wrapper to create a vertical channel route
"""
self.create_channel_route(netlist, pins, offset, layer_stack,
pitch, vertical=True)
self.create_channel_route(netlist, offset, layer_stack, pitch, vertical=True)
def create_horizontal_channel_route(self, netlist, pins, offset,
def create_horizontal_channel_route(self, netlist, offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None):
"""
Wrapper to create a horizontal channel route
"""
self.create_channel_route(netlist, pins, offset,
layer_stack, pitch, vertical=False)
self.create_channel_route(netlist, offset, layer_stack, pitch, vertical=False)
def add_enclosure(self, insts, layer="nwell"):
""" Add a layer that surrounds the given instances. Useful
@ -922,21 +926,30 @@ class layout(lef.lef):
def add_power_pin(self, name, loc, rotate=90):
def add_power_pin(self, name, loc, rotate=90, start_layer="metal1"):
"""
Add a single power pin from M3 down to M1 at the given center location
Add a single power pin from M3 down to M1 at the given center location.
The starting layer is specified to determine which vias are needed.
"""
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=loc,
rotate=float(rotate))
via=self.add_via_center(layers=("metal2", "via2", "metal3"),
if start_layer=="metal1":
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=loc,
rotate=float(rotate))
self.add_layout_pin_rect_center(text=name,
layer="metal3",
offset=loc,
width=via.width,
height=via.height)
if start_layer=="metal1" or start_layer=="metal2":
via=self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=loc,
rotate=float(rotate))
if start_layer=="metal3":
self.add_layout_pin_rect_center(text=name,
layer="metal3",
offset=loc)
else:
self.add_layout_pin_rect_center(text=name,
layer="metal3",
offset=loc,
width=via.width,
height=via.height)
def add_power_ring(self, bbox):
"""

View File

@ -80,7 +80,7 @@ class pbitcell(design.design):
self.offset_all_coordinates()
gnd_overlap = vector(0, 0.5*contact.well.width)
self.translate_all(gnd_overlap)
self.DRC_LVS()
def add_pins(self):
""" add pins and set names for bitlines and wordlines """
@ -323,20 +323,21 @@ class pbitcell(design.design):
# Add rails for vdd and gnd
gnd_ypos = self.rowline_offset - self.total_ports*self.rowline_spacing
self.gnd_position = vector(0, gnd_ypos)
self.gnd = self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=self.gnd_position,
width=self.width,
height=self.m1_width)
self.add_rect_center(layer="metal1",
offset=self.gnd_position,
width=self.width,
height=self.m1_width)
self.add_power_pin("gnd", vector(0,gnd_ypos))
vdd_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset
self.vdd_position = vector(0, vdd_ypos)
self.vdd = self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=self.vdd_position,
width=self.width,
height=self.m1_width)
self.add_rect_center(layer="metal1",
offset=self.vdd_position,
width=self.width,
height=self.m1_width)
self.add_power_pin("vdd", vector(0,vdd_ypos))
def create_readwrite_ports(self):
"""
Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell.

View File

@ -29,6 +29,7 @@ class functional(simulation):
self.set_spice_constants()
self.set_stimulus_variables()
self.create_signal_names()
# Number of checks can be changed
self.num_cycles = 2
@ -141,17 +142,17 @@ class functional(simulation):
sp_read_value = ""
for bit in range(self.word_size):
value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check))
if value > 0.88 * self.vdd_voltage:
if value > self.v_high:
sp_read_value = "1" + sp_read_value
elif value < 0.12 * self.vdd_voltage:
elif value < self.v_low:
sp_read_value = "0" + sp_read_value
else:
error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port,
bit,
value,
eo_period,
0.12*self.vdd_voltage,
0.88*self.vdd_voltage)
self.v_low,
self.v_high)
return (0, error)
self.read_check.append([sp_read_value, dout_port, eo_period, check])

View File

@ -37,6 +37,9 @@ class simulation():
self.period = tech.spice["feasible_period"]
self.slew = tech.spice["rise_time"]*2
self.load = tech.spice["msflop_in_cap"]*4
self.v_high = self.vdd_voltage - tech.spice["v_threshold_typical"]
self.v_low = tech.spice["v_threshold_typical"]
self.gnd_voltage = 0
def set_stimulus_variables(self):

View File

@ -3,8 +3,8 @@ num_words = 16
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [ 3.3 ]
temperatures = [ 25 ]
supply_voltages = [5.0]
temperatures = [25]
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)

View File

@ -10,7 +10,6 @@ from pinv import pinv
from pnand2 import pnand2
from pnor2 import pnor2
from vector import vector
from pinvbuf import pinvbuf
from globals import OPTS
@ -85,11 +84,12 @@ class bank(design.design):
self.add_pin("bank_sel{}".format(port),"INPUT")
for port in self.read_ports:
self.add_pin("s_en{0}".format(port), "INPUT")
for port in self.read_ports:
self.add_pin("p_en_bar{0}".format(port), "INPUT")
for port in self.write_ports:
self.add_pin("w_en{0}".format(port), "INPUT")
for port in self.all_ports:
self.add_pin("clk_buf_bar{0}".format(port),"INPUT")
self.add_pin("clk_buf{0}".format(port),"INPUT")
self.add_pin("wl_en{0}".format(port), "INPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
@ -226,16 +226,19 @@ class bank(design.design):
# UPPER LEFT QUADRANT
# To the left of the bitcell array
# The wordline driver is placed to the right of the main decoder width.
x_offset = self.central_bus_width + self.wordline_driver.width
x_offset = self.m2_gap + self.wordline_driver.width
self.wordline_driver_offsets[port] = vector(-x_offset,0)
x_offset += self.row_decoder.width + self.m2_gap
self.row_decoder_offsets[port] = vector(-x_offset,0)
# LOWER LEFT QUADRANT
# Place the col decoder left aligned with wordline driver plus halfway under row decoder
# Place the col decoder left aligned with row decoder (x_offset doesn't change)
# Below the bitcell array with well spacing
x_offset = self.central_bus_width[port] + self.wordline_driver.width
if self.col_addr_size > 0:
y_offset = self.column_decoder.height
x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = self.m2_gap + self.column_decoder.height
else:
y_offset = 0
y_offset += 2*drc("well_to_well")
@ -283,16 +286,18 @@ class bank(design.design):
# LOWER RIGHT QUADRANT
# To the left of the bitcell array
# The wordline driver is placed to the right of the main decoder width.
x_offset = self.bitcell_array.width + self.central_bus_width + self.wordline_driver.width
x_offset = self.bitcell_array.width + self.m2_gap + self.wordline_driver.width
self.wordline_driver_offsets[port] = vector(x_offset,0)
x_offset += self.row_decoder.width + self.m2_gap
self.row_decoder_offsets[port] = vector(x_offset,0)
# UPPER RIGHT QUADRANT
# Place the col decoder right aligned with row decoder (x_offset doesn't change)
# Place the col decoder right aligned with wordline driver plus halfway under row decoder
# Above the bitcell array with a well spacing
x_offset = self.bitcell_array.width + self.central_bus_width[port] + self.wordline_driver.width
if self.col_addr_size > 0:
y_offset = self.bitcell_array.height + self.column_decoder.height
x_offset += self.column_decoder.width + self.col_addr_bus_width
y_offset = self.bitcell_array.height + self.column_decoder.height + self.m2_gap
else:
y_offset = self.bitcell_array.height
y_offset += 2*drc("well_to_well")
@ -349,21 +354,25 @@ class bank(design.design):
# FIXME: This spacing should be width dependent...
self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space
# Number of control lines in the bus
self.num_control_lines = 4
# The order of the control signals on the control bus:
self.input_control_signals = []
port_num = 0
for port in range(OPTS.num_rw_ports):
self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num)])
self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)])
port_num += 1
for port in range(OPTS.num_w_ports):
self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "w_en{}".format(port_num)])
self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num)])
port_num += 1
for port in range(OPTS.num_r_ports):
self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "s_en{}".format(port_num)])
self.input_control_signals.append(["wl_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)])
port_num += 1
# Number of control lines in the bus for each port
self.num_control_lines = [len(x) for x in self.input_control_signals]
# The width of this bus is needed to place other modules (e.g. decoder) for each port
self.central_bus_width = [self.m2_pitch*x + self.m2_width for x in self.num_control_lines]
# These will be outputs of the gaters if this is multibank, if not, normal signals.
self.control_signals = []
for port in self.all_ports:
@ -371,15 +380,13 @@ class bank(design.design):
self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]])
else:
self.control_signals.append(self.input_control_signals[port])
# The central bus is the column address (one hot) and row address (binary)
if self.col_addr_size>0:
self.num_col_addr_lines = 2**self.col_addr_size
else:
self.num_col_addr_lines = 0
# The width of this bus is needed to place other modules (e.g. decoder)
# A width on each side too
self.central_bus_width = self.m2_pitch * self.num_control_lines + self.m2_width
self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines
# A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"),
@ -401,16 +408,15 @@ class bank(design.design):
setattr (self, "mod_"+mod_name, mod_class)
self.bitcell = self.mod_bitcell()
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
rows=self.num_rows)
self.add_mod(self.bitcell_array)
# create arrays of bitline and bitline_bar names for read, write, or all ports
self.bitcell = self.mod_bitcell()
self.bl_names = self.bitcell.list_all_bl_names()
self.br_names = self.bitcell.list_all_br_names()
self.wl_names = self.bitcell.list_all_wl_names()
self.bitline_names = self.bitcell.list_all_bitline_names()
@ -489,7 +495,7 @@ class bank(design.design):
for i in range(self.num_cols):
temp.append(self.bl_names[port]+"_{0}".format(i))
temp.append(self.br_names[port]+"_{0}".format(i))
temp.extend([self.prefix+"clk_buf_bar{0}".format(port), "vdd"])
temp.extend([self.prefix+"p_en_bar{0}".format(port), "vdd"])
self.connect_inst(temp)
@ -664,7 +670,7 @@ class bank(design.design):
temp.append("dec_out{0}_{1}".format(port,row))
for row in range(self.num_rows):
temp.append(self.wl_names[port]+"_{0}".format(row))
temp.append(self.prefix+"clk_buf{0}".format(port))
temp.append(self.prefix+"wl_en{0}".format(port))
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
@ -691,16 +697,19 @@ class bank(design.design):
if self.col_addr_size == 0:
return
elif self.col_addr_size == 1:
from pinvbuf import pinvbuf
self.column_decoder = pinvbuf(height=self.mod_dff.height)
self.add_mod(self.column_decoder)
elif self.col_addr_size == 2:
self.column_decoder = self.row_decoder.pre2_4
from hierarchical_predecode2x4 import hierarchical_predecode2x4 as pre2x4
self.column_decoder = pre2x4(height=self.mod_dff.height)
elif self.col_addr_size == 3:
self.column_decoder = self.row_decoder.pre3_8
from hierarchical_predecode3x8 import hierarchical_predecode3x8 as pre3x8
self.column_decoder = pre3x8(height=self.mod_dff.height)
else:
# No error checking before?
debug.error("Invalid column decoder?",-1)
self.add_mod(self.column_decoder)
self.column_decoder_inst = [None]*len(self.all_ports)
for port in self.all_ports:
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
@ -774,14 +783,14 @@ class bank(design.design):
""" Route the bank select logic. """
if self.port_id[port] == "rw":
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"]
bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"]
elif self.port_id[port] == "w":
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en"]
bank_sel_signals = ["clk_buf", "w_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en"]
else:
bank_sel_signals = ["clk_buf", "clk_buf_bar", "s_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_s_en"]
bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"]
copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)]
for signal in range(len(copy_control_signals)):
@ -835,8 +844,9 @@ class bank(design.design):
# Port 0
# The bank is at (0,0), so this is to the left of the y-axis.
# 2 pitches on the right for vias/jogs to access the inputs
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset)
control_bus_length = self.max_y_offset - self.min_y_offset
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_width, self.min_y_offset)
# The control bus is routed up to two pitches below the bitcell array
control_bus_length = -2*self.m1_pitch - self.min_y_offset
self.bus_xoffset[0] = self.create_bus(layer="metal2",
pitch=self.m2_pitch,
offset=control_bus_offset,
@ -847,7 +857,11 @@ class bank(design.design):
# Port 1
if len(self.all_ports)==2:
control_bus_offset = vector(self.bitcell_array.width + self.m2_width, self.min_y_offset)
# The other control bus is routed up to two pitches above the bitcell array
control_bus_length = self.max_y_offset - self.bitcell_array.height - 2*self.m1_pitch
control_bus_offset = vector(self.bitcell_array.width + self.m2_width,
self.max_y_offset - control_bus_length)
self.bus_xoffset[1] = self.create_bus(layer="metal2",
pitch=self.m2_pitch,
offset=control_bus_offset,
@ -891,6 +905,9 @@ class bank(design.design):
inst1 = self.bitcell_array_inst
inst1_bl_name = self.bl_names[port]+"_{}"
inst1_br_name = self.br_names[port]+"_{}"
# The column mux is constructed to match the bitline pitch, so we can directly connect
# here and not channel route the bitlines.
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
@ -911,8 +928,8 @@ class bank(design.design):
inst1_bl_name = "bl_{}"
inst1_br_name = "br_{}"
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
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)
def route_write_driver_to_column_mux_or_bitcell_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or bitcell array """
@ -937,7 +954,11 @@ class bank(design.design):
inst1 = self.write_driver_array_inst[port]
inst2 = self.sense_amp_array_inst[port]
self.connect_bitlines(inst1, inst2, self.word_size)
# These should be pitch matched in the cell library,
# but just in case, do a channel route.
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size)
def route_sense_amp_out(self, port):
@ -992,14 +1013,10 @@ class bank(design.design):
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
offset = bottom_inst.ul() + vector(0,self.m1_pitch)
for bit in range(num_bits):
bottom_names = [bottom_bl_name.format(bit), bottom_br_name.format(bit)]
top_names = [top_bl_name.format(bit), top_br_name.format(bit)]
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))]
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
route_map = list(zip(bottom_names, top_names))
bottom_pins = {key: bottom_inst.get_pin(key) for key in bottom_names }
top_pins = {key: top_inst.get_pin(key) for key in top_names }
all_pins = {**bottom_pins, **top_pins}
debug.check(len(all_pins)==len(bottom_pins)+len(top_pins),"Duplicate named pins in bitline channel route.")
self.create_horizontal_channel_route(route_map, all_pins, offset)
self.create_horizontal_channel_route(route_map, offset)
def connect_bitlines(self, inst1, inst2, num_bits,
@ -1078,79 +1095,41 @@ class bank(design.design):
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_column_address_lines(self, port):
""" Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0:
return
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
decode_names.append("out_{}".format(i))
for i in range(self.col_addr_size):
decoder_name = "in_{}".format(i)
addr_name = "addr{0}_{1}".format(port,i)
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
if port%2:
self.route_column_address_lines_right(port)
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0)
else:
self.route_column_address_lines_left(port)
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
def route_column_address_lines_left(self, port):
""" Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0:
return
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
decode_names.append("out_{}".format(i))
for i in range(self.col_addr_size):
decoder_name = "in_{}".format(i)
addr_name = "addr{0}_{1}".format(port,i)
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
route_map = list(zip(decode_names, sel_names))
decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names }
column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names }
# Combine the dff and bank pins into a single dictionary of pin name to pin.
all_pins = {**decode_pins, **column_mux_pins}
self.create_vertical_channel_route(route_map, all_pins, offset)
def route_column_address_lines_right(self, port):
""" Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0:
return
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
decode_names.append("out_{}".format(i))
for i in range(self.col_addr_size):
decoder_name = "in_{}".format(i)
addr_name = "addr{0}_{1}".format(port,i)
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0)
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
route_map = list(zip(decode_names, sel_names))
decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names }
column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names }
# Combine the dff and bank pins into a single dictionary of pin name to pin.
all_pins = {**decode_pins, **column_mux_pins}
self.create_vertical_channel_route(route_map, all_pins, offset)
decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names]
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
column_mux_pins = [self.column_mux_array_inst[port].get_pin(x) for x in sel_names]
route_map = list(zip(decode_pins, column_mux_pins))
self.create_vertical_channel_route(route_map, offset)
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
@ -1209,7 +1188,7 @@ class bank(design.design):
connection = []
if port in self.read_ports:
connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[port].get_pin("en").lc()))
connection.append((self.prefix+"p_en_bar{}".format(port), self.precharge_array_inst[port].get_pin("en_bar").lc()))
if port in self.write_ports:
connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc()))
@ -1225,9 +1204,13 @@ class bank(design.design):
rotate=90)
# clk to wordline_driver
control_signal = self.prefix+"clk_buf{}".format(port)
pin_pos = self.wordline_driver_inst[port].get_pin("en").bc()
mid_pos = pin_pos - vector(0,self.m1_pitch)
control_signal = self.prefix+"wl_en{}".format(port)
if port%2:
pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").uc()
mid_pos = pin_pos + vector(0,self.m2_gap) # to route down to the top of the bus
else:
pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").bc()
mid_pos = pin_pos - vector(0,self.m2_gap) # to route down to the top of the bus
control_x_offset = self.bus_xoffset[port][control_signal].x
control_pos = vector(control_x_offset, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])

View File

@ -132,40 +132,14 @@ class bitcell_array(design.design):
# increments to the next row height
offset.y += self.cell.height
# For every second row and column, add a via for vdd
# For every second row and column, add a via for gnd and vdd
for row in range(self.row_size):
for col in range(self.column_size):
inst = self.cell_inst[row,col]
for vdd_pin in inst.get_pins("vdd"):
# Drop to M1 if needed
if vdd_pin.layer == "metal1":
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=vdd_pin.center(),
rotate=90)
# Always drop to M2
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=vdd_pin.center())
self.add_layout_pin_rect_center(text="vdd",
layer="metal3",
offset=vdd_pin.center())
# For every second row and column (+1), add a via for gnd
for row in range(self.row_size):
for col in range(self.column_size):
inst = self.cell_inst[row,col]
for gnd_pin in inst.get_pins("gnd"):
# Drop to M1 if needed
if gnd_pin.layer == "metal1":
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=gnd_pin.center(),
rotate=90)
# Always drop to M2
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=gnd_pin.center())
self.add_layout_pin_rect_center(text="gnd",
layer="metal3",
offset=gnd_pin.center())
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
self.add_power_pin(pin_name, pin.center(), 0, pin.layer)
def analytical_delay(self, slew, load=0):
from tech import drc

View File

@ -4,11 +4,12 @@ from tech import drc, parameter
import debug
import contact
from pinv import pinv
from pbuf import pbuf
from pand2 import pand2
from pnand2 import pnand2
from pnand3 import pnand3
from pinvbuf import pinvbuf
from dff_inv import dff_inv
from dff_inv_array import dff_inv_array
from dff_buf import dff_buf
from dff_buf_array import dff_buf_array
import math
from vector import vector
from globals import OPTS
@ -47,9 +48,7 @@ class control_logic(design.design):
""" Create layout and route between modules """
self.place_instances()
self.route_all()
#self.add_lvs_correspondence_points()
self.DRC_LVS()
@ -65,28 +64,37 @@ class control_logic(design.design):
def add_modules(self):
""" Add all the required modules """
dff = dff_inv()
dff = dff_buf()
dff_height = dff.height
self.ctrl_dff_array = dff_inv_array(rows=self.num_control_signals,columns=1)
self.ctrl_dff_array = dff_buf_array(rows=self.num_control_signals,columns=1)
self.add_mod(self.ctrl_dff_array)
self.nand2 = pnand2(height=dff_height)
self.add_mod(self.nand2)
self.nand3 = pnand3(height=dff_height)
self.add_mod(self.nand3)
self.and2 = pand2(size=4,height=dff_height)
self.add_mod(self.and2)
# Special gates: inverters for buffering
# Size the clock for the number of rows (fanout)
clock_driver_size = max(1,int(self.num_rows/4))
self.clkbuf = pinvbuf(clock_driver_size,height=dff_height)
self.clkbuf = pbuf(size=clock_driver_size, height=dff_height)
self.add_mod(self.clkbuf)
self.buf16 = pbuf(size=16, height=dff_height)
self.add_mod(self.buf16)
self.buf8 = pbuf(size=8, height=dff_height)
self.add_mod(self.buf8)
self.inv = self.inv1 = pinv(size=1, height=dff_height)
self.add_mod(self.inv1)
self.inv2 = pinv(size=4, height=dff_height)
self.add_mod(self.inv2)
self.inv8 = pinv(size=16, height=dff_height)
self.inv8 = pinv(size=8, height=dff_height)
self.add_mod(self.inv8)
# self.inv2 = pinv(size=4, height=dff_height)
# self.add_mod(self.inv2)
#self.inv16 = pinv(size=16, height=dff_height)
#self.add_mod(self.inv16)
if (self.port_type == "rw") or (self.port_type == "r"):
from importlib import reload
@ -127,20 +135,22 @@ class control_logic(design.design):
# list of output control signals (for making a vertical bus)
if self.port_type == "rw":
self.internal_bus_list = ["clk_buf", "clk_buf_bar", "we", "cs"]
self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "we", "clk_buf", "we_bar", "cs"]
elif self.port_type == "r":
self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"]
else:
self.internal_bus_list = ["clk_buf", "clk_buf_bar", "cs"]
self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
# leave space for the bus plus one extra space
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
# Outputs to the bank
if self.port_type == "r":
self.output_list = ["s_en"]
elif self.port_type == "w":
self.output_list = ["w_en"]
if self.port_type == "rw":
self.output_list = ["s_en", "w_en", "p_en_bar"]
elif self.port_type == "r":
self.output_list = ["s_en", "p_en_bar"]
else:
self.output_list = ["s_en", "w_en"]
self.output_list.append("clk_buf_bar")
self.output_list = ["w_en"]
self.output_list.append("wl_en")
self.output_list.append("clk_buf")
self.supply_list = ["vdd", "gnd"]
@ -157,11 +167,16 @@ class control_logic(design.design):
def create_instances(self):
""" Create all the instances """
self.create_dffs()
self.create_clk_row()
self.create_clk_buf_row()
self.create_gated_clk_bar_row()
self.create_gated_clk_buf_row()
self.create_wlen_row()
if (self.port_type == "rw") or (self.port_type == "w"):
self.create_we_row()
if (self.port_type == "rw") or (self.port_type == "r"):
self.create_wen_row()
if self.port_type == "rw":
self.create_rbl_in_row()
if (self.port_type == "rw") or (self.port_type == "r"):
self.create_pen_row()
self.create_sen_row()
self.create_rbl()
@ -175,19 +190,33 @@ class control_logic(design.design):
# Add the control flops on the left of the bus
self.place_dffs()
# All of the control logic is placed to the right of the DFFs and bus
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width
row = 0
# Add the logic on the right of the bus
self.place_clk_row(row=row) # clk is a double-high cell
row += 2
self.place_clk_buf_row(row)
row += 1
self.place_gated_clk_bar_row(row)
row += 1
self.place_gated_clk_buf_row(row)
row += 1
self.place_wlen_row(row)
row += 1
if (self.port_type == "rw") or (self.port_type == "w"):
self.place_we_row(row=row)
self.place_wen_row(row)
height = self.w_en_inst.uy()
control_center_y = self.w_en_inst.uy()
row += 1
if (self.port_type == "rw") or (self.port_type == "r"):
self.place_rbl_in_row(row=row)
self.place_sen_row(row=row+1)
self.place_rbl(row=row+2)
if self.port_type == "rw":
self.place_rbl_in_row(row)
row += 1
if (self.port_type == "rw") or (self.port_type == "r"):
self.place_pen_row(row)
row += 1
self.place_sen_row(row)
row += 1
self.place_rbl(row)
height = self.rbl_inst.uy()
control_center_y = self.rbl_inst.by()
@ -197,122 +226,350 @@ class control_logic(design.design):
# Extra pitch on top and right
self.height = height + 2*self.m1_pitch
# Max of modules or logic rows
self.width = max([inst.rx() for inst in self.row_end_inst])
if (self.port_type == "rw") or (self.port_type == "r"):
self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch
else:
self.width = max([inst.rx() for inst in self.row_end_inst]) + self.m2_pitch
self.width = max(self.rbl_inst.rx() , self.width)
self.width += self.m2_pitch
def route_all(self):
""" Routing between modules """
self.route_rails()
self.route_dffs()
self.route_wlen()
if (self.port_type == "rw") or (self.port_type == "w"):
self.route_wen()
if (self.port_type == "rw") or (self.port_type == "r"):
self.route_rbl_in()
self.route_pen()
self.route_sen()
self.route_clk()
self.route_clk_buf()
self.route_gated_clk_bar()
self.route_gated_clk_buf()
self.route_supply()
def create_rbl(self):
""" Create the replica bitline """
if self.port_type == "r":
input_name = "gated_clk_bar"
else:
input_name = "rbl_in"
self.rbl_inst=self.add_inst(name="replica_bitline",
mod=self.replica_bitline)
self.connect_inst(["rbl_in", "pre_s_en", "vdd", "gnd"])
self.connect_inst([input_name, "pre_s_en", "vdd", "gnd"])
def place_rbl(self,row):
""" Place the replica bitline """
y_off = row * self.inv1.height + 2*self.m1_pitch
y_off = row * self.and2.height + 2*self.m1_pitch
# Add the RBL above the rows
# Add to the right of the control rows and routing channel
self.replica_bitline_offset = vector(0, y_off)
self.rbl_inst.place(self.replica_bitline_offset)
offset = vector(0, y_off)
self.rbl_inst.place(offset)
def create_clk_row(self):
""" Create the multistage clock buffer """
def create_clk_buf_row(self):
""" Create the multistage and gated clock buffer """
self.clkbuf_inst = self.add_inst(name="clkbuf",
mod=self.clkbuf)
self.connect_inst(["clk","clk_buf_bar","clk_buf","vdd","gnd"])
def place_clk_row(self,row):
""" Place the multistage clock buffer below the control flops """
x_off = self.ctrl_dff_array.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
clkbuf_offset = vector(x_off,y_off)
self.clkbuf_inst.place(clkbuf_offset)
self.row_end_inst.append(self.clkbuf_inst)
self.connect_inst(["clk","clk_buf","vdd","gnd"])
def place_clk_buf_row(self,row):
""" Place the multistage clock buffer below the control flops """
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off,y_off)
self.clkbuf_inst.place(offset, mirror)
self.row_end_inst.append(self.clkbuf_inst)
def route_clk_buf(self):
clk_pin = self.clkbuf_inst.get_pin("A")
clk_pos = clk_pin.center()
self.add_layout_pin_segment_center(text="clk",
layer="metal2",
start=clk_pos,
end=clk_pos.scale(1,0))
self.add_via_center(layers=("metal1","via1","metal2"),
offset=clk_pos)
clkbuf_map = zip(["Z"], ["clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.clkbuf_inst.get_pin("Z").center())
self.connect_output(self.clkbuf_inst, "Z", "clk_buf")
def create_gated_clk_bar_row(self):
self.clk_bar_inst = self.add_inst(name="inv_clk_bar",
mod=self.inv)
self.connect_inst(["clk_buf","clk_bar","vdd","gnd"])
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
mod=self.and2)
self.connect_inst(["cs","clk_bar","gated_clk_bar","vdd","gnd"])
def place_gated_clk_bar_row(self,row):
""" Place the gated clk logic below the control flops """
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off,y_off)
self.clk_bar_inst.place(offset, mirror)
x_off += self.inv.width
offset = vector(x_off,y_off)
self.gated_clk_bar_inst.place(offset, mirror)
self.row_end_inst.append(self.gated_clk_bar_inst)
def route_gated_clk_bar(self):
clkbuf_map = zip(["A"], ["clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets)
out_pos = self.clk_bar_inst.get_pin("Z").center()
in_pos = self.gated_clk_bar_inst.get_pin("B").center()
mid1 = vector(in_pos.x,out_pos.y)
self.add_path("metal1",[out_pos, mid1, in_pos])
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["A"], ["cs"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.gated_clk_bar_inst.get_pin("A").center())
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["Z"], ["gated_clk_bar"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.gated_clk_bar_inst.get_pin("Z").center())
def create_gated_clk_buf_row(self):
self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf",
mod=self.and2)
self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"])
def place_gated_clk_buf_row(self,row):
""" Place the gated clk logic below the control flops """
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off,y_off)
self.gated_clk_buf_inst.place(offset, mirror)
self.row_end_inst.append(self.gated_clk_buf_inst)
def route_gated_clk_buf(self):
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets)
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.gated_clk_buf_inst.get_pin("Z").center())
def create_wlen_row(self):
# input pre_p_en, output: wl_en
self.wl_en_inst=self.add_inst(name="buf_wl_en",
mod=self.buf16)
self.connect_inst(["gated_clk_bar", "wl_en", "vdd", "gnd"])
def place_wlen_row(self, row):
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off, y_off)
self.wl_en_inst.place(offset, mirror)
self.row_end_inst.append(self.wl_en_inst)
def route_wlen(self):
wlen_map = zip(["A"], ["gated_clk_bar"])
self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.rail_offsets)
self.connect_output(self.wl_en_inst, "Z", "wl_en")
def create_rbl_in_row(self):
self.rbl_in_bar_inst=self.add_inst(name="nand2_rbl_in_bar",
mod=self.nand2)
self.connect_inst(["clk_buf_bar", "cs", "rbl_in_bar", "vdd", "gnd"])
# input: rbl_in_bar, output: rbl_in
self.rbl_in_inst=self.add_inst(name="inv_rbl_in",
mod=self.inv1)
self.connect_inst(["rbl_in_bar", "rbl_in", "vdd", "gnd"])
# input: gated_clk_bar, we_bar, output: rbl_in
self.rbl_in_inst=self.add_inst(name="and2_rbl_in",
mod=self.and2)
self.connect_inst(["gated_clk_bar", "we_bar", "rbl_in", "vdd", "gnd"])
def place_rbl_in_row(self,row):
x_off = self.ctrl_dff_array.width + self.internal_bus_width
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off, y_off)
self.rbl_in_inst.place(offset, mirror)
self.rbl_in_bar_offset = vector(x_off, y_off)
self.rbl_in_bar_inst.place(offset=self.rbl_in_bar_offset,
mirror=mirror)
x_off += self.nand2.width
self.rbl_in_offset = vector(x_off, y_off)
self.rbl_in_inst.place(offset=self.rbl_in_offset,
mirror=mirror)
self.row_end_inst.append(self.rbl_in_inst)
def route_rbl_in(self):
""" Connect the logic for the rbl_in generation """
if self.port_type == "rw":
input_name = "we_bar"
# Connect the NAND gate inputs to the bus
rbl_in_map = zip(["A", "B"], ["gated_clk_bar", "we_bar"])
self.connect_vertical_bus(rbl_in_map, self.rbl_in_inst, self.rail_offsets)
# Connect the output of the precharge enable to the RBL input
if self.port_type == "rw":
out_pos = self.rbl_in_inst.get_pin("Z").center()
else:
out_pos = vector(self.rail_offsets["gated_clk_bar"].x, self.rbl_inst.by()-3*self.m2_pitch)
in_pos = self.rbl_inst.get_pin("en").center()
mid1 = vector(in_pos.x,out_pos.y)
self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, in_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=out_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=out_pos,
rotate=90)
def create_pen_row(self):
if self.port_type == "rw":
# input: gated_clk_bar, we_bar, output: pre_p_en
self.pre_p_en_inst=self.add_inst(name="and2_pre_p_en",
mod=self.and2)
self.connect_inst(["gated_clk_buf", "we_bar", "pre_p_en", "vdd", "gnd"])
input_name = "pre_p_en"
else:
input_name = "gated_clk_buf"
# input: pre_p_en, output: p_en_bar
self.p_en_bar_inst=self.add_inst(name="inv_p_en_bar",
mod=self.inv8)
self.connect_inst([input_name, "p_en_bar", "vdd", "gnd"])
def place_pen_row(self,row):
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
if self.port_type == "rw":
offset = vector(x_off, y_off)
self.pre_p_en_inst.place(offset, mirror)
x_off += self.and2.width
offset = vector(x_off,y_off)
self.p_en_bar_inst.place(offset, mirror)
self.row_end_inst.append(self.p_en_bar_inst)
def route_pen(self):
if self.port_type == "rw":
# Connect the NAND gate inputs to the bus
pre_p_en_in_map = zip(["A", "B"], ["gated_clk_buf", "we_bar"])
self.connect_vertical_bus(pre_p_en_in_map, self.pre_p_en_inst, self.rail_offsets)
out_pos = self.pre_p_en_inst.get_pin("Z").center()
in_pos = self.p_en_bar_inst.get_pin("A").lc()
mid1 = vector(out_pos.x,in_pos.y)
self.add_wire(("metal1","via1","metal2"),[out_pos,mid1,in_pos])
else:
in_map = zip(["A"], ["gated_clk_buf"])
self.connect_vertical_bus(in_map, self.p_en_bar_inst, self.rail_offsets)
self.connect_output(self.p_en_bar_inst, "Z", "p_en_bar")
def create_sen_row(self):
""" Create the sense enable buffer. """
# input: pre_s_en, output: pre_s_en_bar
self.pre_s_en_bar_inst=self.add_inst(name="inv_pre_s_en_bar",
mod=self.inv2)
self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"])
# BUFFER INVERTERS FOR S_EN
# input: input: pre_s_en_bar, output: s_en
self.s_en_inst=self.add_inst(name="inv_s_en",
mod=self.inv8)
self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"])
# BUFFER FOR S_EN
# input: pre_s_en, output: s_en
self.s_en_inst=self.add_inst(name="buf_s_en",
mod=self.buf8)
self.connect_inst(["pre_s_en", "s_en", "vdd", "gnd"])
def place_sen_row(self,row):
"""
The sense enable buffer gets placed to the far right of the
row.
"""
x_off = self.ctrl_dff_array.width + self.internal_bus_width
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
self.pre_s_en_bar_offset = vector(x_off, y_off)
self.pre_s_en_bar_inst.place(offset=self.pre_s_en_bar_offset,
mirror=mirror)
x_off += self.inv2.width
offset = vector(x_off, y_off)
self.s_en_inst.place(offset, mirror)
self.s_en_offset = vector(x_off, y_off)
self.s_en_inst.place(offset=self.s_en_offset,
mirror=mirror)
self.row_end_inst.append(self.s_en_inst)
def route_sen(self):
out_pos = self.rbl_inst.get_pin("out").bc()
in_pos = self.s_en_inst.get_pin("A").lc()
mid1 = vector(out_pos.x,in_pos.y)
self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos])
self.connect_output(self.s_en_inst, "Z", "s_en")
def create_wen_row(self):
# input: we (or cs) output: w_en
if self.port_type == "rw":
input_name = "we"
else:
# No we for write-only reports, so use cs
input_name = "cs"
# BUFFER FOR W_EN
self.w_en_inst = self.add_inst(name="buf_w_en_buf",
mod=self.buf8)
self.connect_inst([input_name, "w_en", "vdd", "gnd"])
def place_wen_row(self,row):
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off, y_off)
self.w_en_inst.place(offset, mirror)
self.row_end_inst.append(self.w_en_inst)
def route_wen(self):
if self.port_type == "rw":
input_name = "we"
else:
# No we for write-only reports, so use cs
input_name = "cs"
wen_map = zip(["A"], [input_name])
self.connect_vertical_bus(wen_map, self.w_en_inst, self.rail_offsets)
self.connect_output(self.w_en_inst, "Z", "w_en")
def create_dffs(self):
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
mod=self.ctrl_dff_array)
self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list)
def place_dffs(self):
self.ctrl_dff_inst.place(vector(0,0))
def route_dffs(self):
""" Route the input inverters """
if self.port_type == "r":
control_inputs = ["cs"]
if self.port_type == "rw":
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
elif self.port_type == "r":
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
else:
control_inputs = ["cs", "we"]
dff_out_map = zip(["dout_bar_{}".format(i) for i in range(2*self.num_control_signals - 1)], control_inputs)
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets)
dff_out_map = zip(["dout_bar_0"], ["cs"])
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# Connect the clock rail to the other clock rail
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
@ -327,207 +584,18 @@ class control_logic(design.design):
if (self.port_type == "rw"):
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
def create_dffs(self):
""" Add the three input DFFs (with inverters) """
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
mod=self.ctrl_dff_array)
self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list)
def place_dffs(self):
""" Place the input DFFs (with inverters) """
self.ctrl_dff_inst.place(vector(0,0))
def get_offset(self,row):
""" Compute the y-offset and mirroring """
y_off = row*self.inv1.height
y_off = row*self.and2.height
if row % 2:
y_off += self.inv1.height
y_off += self.and2.height
mirror="MX"
else:
mirror="R0"
return (y_off,mirror)
def create_we_row(self):
# input: WE, CS output: w_en_bar
if self.port_type == "rw":
nand_mod = self.nand3
temp = ["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"]
else:
nand_mod = self.nand2
temp = ["clk_buf_bar", "cs", "w_en_bar", "vdd", "gnd"]
self.w_en_bar_inst = self.add_inst(name="nand3_w_en_bar",
mod=nand_mod)
self.connect_inst(temp)
# input: w_en_bar, output: pre_w_en
self.pre_w_en_inst = self.add_inst(name="inv_pre_w_en",
mod=self.inv1)
self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"])
# BUFFER INVERTERS FOR W_EN
self.pre_w_en_bar_inst = self.add_inst(name="inv_pre_w_en_bar",
mod=self.inv2)
self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"])
self.w_en_inst = self.add_inst(name="inv_w_en2",
mod=self.inv8)
self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"])
def place_we_row(self,row):
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
w_en_bar_offset = vector(x_off, y_off)
self.w_en_bar_inst.place(offset=w_en_bar_offset,
mirror=mirror)
if self.port_type == "rw":
x_off += self.nand3.width
else:
x_off += self.nand2.width
pre_w_en_offset = vector(x_off, y_off)
self.pre_w_en_inst.place(offset=pre_w_en_offset,
mirror=mirror)
x_off += self.inv1.width
pre_w_en_bar_offset = vector(x_off, y_off)
self.pre_w_en_bar_inst.place(offset=pre_w_en_bar_offset,
mirror=mirror)
x_off += self.inv2.width
w_en_offset = vector(x_off, y_off)
self.w_en_inst.place(offset=w_en_offset,
mirror=mirror)
x_off += self.inv8.width
self.row_end_inst.append(self.w_en_inst)
def route_rbl_in(self):
""" Connect the logic for the rbl_in generation """
rbl_in_map = zip(["A", "B"], ["clk_buf_bar", "cs"])
self.connect_vertical_bus(rbl_in_map, self.rbl_in_bar_inst, self.rail_offsets)
# Connect the NAND3 output to the inverter
# The pins are assumed to extend all the way to the cell edge
rbl_in_bar_pos = self.rbl_in_bar_inst.get_pin("Z").center()
inv_in_pos = self.rbl_in_inst.get_pin("A").center()
mid1 = vector(inv_in_pos.x,rbl_in_bar_pos.y)
self.add_path("metal1",[rbl_in_bar_pos,mid1,inv_in_pos])
# Connect the output to the RBL
rbl_out_pos = self.rbl_in_inst.get_pin("Z").center()
rbl_in_pos = self.rbl_inst.get_pin("en").center()
mid1 = vector(rbl_in_pos.x,rbl_out_pos.y)
self.add_wire(("metal3","via2","metal2"),[rbl_out_pos,mid1,rbl_in_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rbl_out_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=rbl_out_pos,
rotate=90)
def connect_rail_from_right(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = inst.get_pin(pin).center()
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos,
rotate=90)
def connect_rail_from_right_m2m3(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = inst.get_pin(pin).center()
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
# Bring it up to M2 for M2/M3 routing
self.add_via_center(layers=("metal1","via1","metal2"),
offset=in_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=in_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=rail_pos,
rotate=90)
def connect_rail_from_left(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = inst.get_pin(pin).lc()
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos,
rotate=90)
def connect_rail_from_left_m2m3(self,inst, pin, rail):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
in_pos = inst.get_pin(pin).lc()
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=in_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=rail_pos,
rotate=90)
def route_wen(self):
if self.port_type == "rw":
wen_map = zip(["A", "B", "C"], ["clk_buf_bar", "cs", "we"])
else:
wen_map = zip(["A", "B"], ["clk_buf_bar", "cs"])
self.connect_vertical_bus(wen_map, self.w_en_bar_inst, self.rail_offsets)
# Connect the NAND3 output to the inverter
# The pins are assumed to extend all the way to the cell edge
w_en_bar_pos = self.w_en_bar_inst.get_pin("Z").center()
inv_in_pos = self.pre_w_en_inst.get_pin("A").center()
mid1 = vector(inv_in_pos.x,w_en_bar_pos.y)
self.add_path("metal1",[w_en_bar_pos,mid1,inv_in_pos])
self.add_path("metal1",[self.pre_w_en_inst.get_pin("Z").center(), self.pre_w_en_bar_inst.get_pin("A").center()])
self.add_path("metal1",[self.pre_w_en_bar_inst.get_pin("Z").center(), self.w_en_inst.get_pin("A").center()])
self.connect_output(self.w_en_inst, "Z", "w_en")
def route_sen(self):
rbl_out_pos = self.rbl_inst.get_pin("out").bc()
in_pos = self.pre_s_en_bar_inst.get_pin("A").lc()
mid1 = vector(rbl_out_pos.x,in_pos.y)
self.add_wire(("metal1","via1","metal2"),[rbl_out_pos,mid1,in_pos])
#s_en_pos = self.s_en.get_pin("Z").lc()
self.add_path("metal1",[self.pre_s_en_bar_inst.get_pin("Z").center(), self.s_en_inst.get_pin("A").center()])
self.connect_output(self.s_en_inst, "Z", "s_en")
def route_clk(self):
""" Route the clk and clk_buf_bar signal internally """
clk_pin = self.clkbuf_inst.get_pin("A")
self.add_layout_pin_segment_center(text="clk",
layer="metal2",
start=clk_pin.bc(),
end=clk_pin.bc().scale(1,0))
clkbuf_map = zip(["Z", "Zb"], ["clk_buf", "clk_buf_bar"])
self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Z", "clk_buf")
# self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Zb", "clk_buf_bar")
self.connect_output(self.clkbuf_inst, "Z", "clk_buf")
self.connect_output(self.clkbuf_inst, "Zb", "clk_buf_bar")
def connect_output(self, inst, pin_name, out_name):
""" Create an output pin on the right side from the pin of a given instance. """

View File

@ -59,7 +59,7 @@ class dff_array(design.design):
self.dff_insts={}
for row in range(self.rows):
for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col)
name = "dff_r{0}_c{1}".format(row,col)
self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff)
self.connect_inst([self.get_din_name(row,col),
@ -71,7 +71,7 @@ class dff_array(design.design):
def place_dff_array(self):
for row in range(self.rows):
for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col)
name = "dff_r{0}_c{1}".format(row,col)
if (row % 2 == 0):
base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0"

View File

@ -102,8 +102,7 @@ class dff_buf(design.design):
mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx())
mid1 = vector(mid_x_offset, q_pin.cy())
mid2 = vector(mid_x_offset, a1_pin.cy())
self.add_path("metal3",
[q_pin.center(), mid1, mid2, a1_pin.center()])
self.add_path("metal3", [q_pin.center(), mid1, mid2, a1_pin.center()])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=q_pin.center())
self.add_via_center(layers=("metal2","via2","metal3"),
@ -114,8 +113,10 @@ class dff_buf(design.design):
# Route inv1 z to inv2 a
z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
mid_x_offset = 0.5*(z1_pin.cx() + a2_pin.cx())
self.mid_qb_pos = vector(mid_x_offset, z1_pin.cy())
mid2 = vector(mid_x_offset, a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()])
def add_layout_pins(self):
@ -150,18 +151,22 @@ class dff_buf(design.design):
height=din_pin.height())
dout_pin = self.inv2_inst.get_pin("Z")
mid_pos = dout_pin.center() + vector(self.m1_pitch,0)
q_pos = mid_pos - vector(0,self.m2_pitch)
self.add_layout_pin_rect_center(text="Q",
layer="metal2",
offset=dout_pin.center())
offset=q_pos)
self.add_path("metal1", [dout_pin.center(), mid_pos, q_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=dout_pin.center())
offset=q_pos)
dout_pin = self.inv2_inst.get_pin("A")
qb_pos = self.mid_qb_pos + vector(0,self.m2_pitch)
self.add_layout_pin_rect_center(text="Qb",
layer="metal2",
offset=dout_pin.center())
offset=qb_pos)
self.add_path("metal1", [self.mid_qb_pos, qb_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=dout_pin.center())
offset=qb_pos)

View File

@ -61,7 +61,7 @@ class dff_buf_array(design.design):
self.dff_insts={}
for row in range(self.rows):
for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col)
name = "dff_r{0}_c{1}".format(row,col)
self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff)
self.connect_inst([self.get_din_name(row,col),

View File

@ -1,186 +0,0 @@
import debug
import design
from tech import drc
from math import log
from vector import vector
from globals import OPTS
import dff_inv
class dff_inv_array(design.design):
"""
This is a simple row (or multiple rows) of flops.
Unlike the data flops, these are never spaced out.
"""
unique_id = 1
def __init__(self, rows, columns, inv_size=2, name=""):
self.rows = rows
self.columns = columns
if name=="":
name = "dff_inv_array_{0}x{1}_{2}".format(rows, columns, dff_inv_array.unique_id)
dff_inv_array.unique_id += 1
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
self.inv_size = inv_size
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_pins()
self.add_modules()
self.create_dff_array()
def create_layout(self):
self.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height
self.place_dff_array()
self.add_layout_pins()
self.DRC_LVS()
def add_modules(self):
self.dff = dff_inv.dff_inv(self.inv_size)
self.add_mod(self.dff)
def add_pins(self):
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_din_name(row,col))
for row in range(self.rows):
for col in range(self.columns):
self.add_pin(self.get_dout_name(row,col))
self.add_pin(self.get_dout_bar_name(row,col))
self.add_pin("clk")
self.add_pin("vdd")
self.add_pin("gnd")
def create_dff_array(self):
self.dff_insts={}
for row in range(self.rows):
for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col)
self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff)
self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(row,col),
self.get_dout_bar_name(row,col),
"clk",
"vdd",
"gnd"])
def place_dff_array(self):
for row in range(self.rows):
for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col)
if (row % 2 == 0):
base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0"
else:
base = vector(col*self.dff.width,(row+1)*self.dff.height)
mirror = "MX"
self.dff_insts[row,col].place(offset=base,
mirror=mirror)
def get_din_name(self, row, col):
if self.columns == 1:
din_name = "din_{0}".format(row)
elif self.rows == 1:
din_name = "din_{0}".format(col)
else:
din_name = "din_{0}_{1}".format(row,col)
return din_name
def get_dout_name(self, row, col):
if self.columns == 1:
dout_name = "dout_{0}".format(row)
elif self.rows == 1:
dout_name = "dout_{0}".format(col)
else:
dout_name = "dout_{0}_{1}".format(row,col)
return dout_name
def get_dout_bar_name(self, row, col):
if self.columns == 1:
dout_bar_name = "dout_bar_{0}".format(row)
elif self.rows == 1:
dout_bar_name = "dout_bar_{0}".format(col)
else:
dout_bar_name = "dout_bar_{0}_{1}".format(row,col)
return dout_bar_name
def add_layout_pins(self):
for row in range(self.rows):
for col in range(self.columns):
# Adds power pin on left of row
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
self.add_power_pin("vdd", vdd_pin.lc())
# Adds gnd pin on left of row
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
self.add_power_pin("gnd", gnd_pin.lc())
for row in range(self.rows):
for col in range(self.columns):
din_pin = self.dff_insts[row,col].get_pin("D")
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
self.add_layout_pin(text=self.get_din_name(row,col),
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.dff_insts[row,col].get_pin("Q")
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
self.add_layout_pin(text=self.get_dout_name(row,col),
layer=dout_pin.layer,
offset=dout_pin.ll(),
width=dout_pin.width(),
height=dout_pin.height())
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2")
self.add_layout_pin(text=self.get_dout_bar_name(row,col),
layer=dout_bar_pin.layer,
offset=dout_bar_pin.ll(),
width=dout_bar_pin.width(),
height=dout_bar_pin.height())
# Create vertical spines to a single horizontal rail
clk_pin = self.dff_insts[0,0].get_pin("clk")
clk_ypos = 2*self.m3_pitch+self.m3_width
debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2")
if self.columns==1:
self.add_layout_pin(text="clk",
layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
else:
self.add_layout_pin_segment_center(text="clk",
layer="metal3",
start=vector(0,clk_ypos),
end=vector(self.width,clk_ypos))
for col in range(self.columns):
clk_pin = self.dff_insts[0,col].get_pin("clk")
# Make a vertical strip for each column
self.add_rect(layer="metal2",
offset=clk_pin.ll().scale(1,0),
width=self.m2_width,
height=self.height)
# Drop a via to the M3 pin
self.add_via_center(layers=("metal2","via2","metal3"),
offset=vector(clk_pin.cx(),clk_ypos))
def analytical_delay(self, slew, load=0.0):
return self.dff.analytical_delay(slew=slew, load=load)

View File

@ -17,15 +17,11 @@ class hierarchical_decoder(design.design):
"""
Dynamically generated hierarchical decoder.
"""
def __init__(self, rows):
design.design.__init__(self, "hierarchical_decoder_{0}rows".format(rows))
from importlib import reload
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
b = self.mod_bitcell()
self.bitcell_height = b.height
unique_id = 1
def __init__(self, rows, height=None):
design.design.__init__(self, "hierarchical_decoder_{0}rows_{1}".format(rows,hierarchical_decoder.unique_id))
hierarchical_decoder.unique_id += 1
self.NAND_FORMAT = "DEC_NAND_{0}"
self.INV_FORMAT = "DEC_INV_{0}"
@ -33,6 +29,7 @@ class hierarchical_decoder(design.design):
self.pre2x4_inst = []
self.pre3x8_inst = []
self.cell_height = height
self.rows = rows
self.num_inputs = int(math.log(self.rows, 2))
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
@ -60,21 +57,21 @@ class hierarchical_decoder(design.design):
self.DRC_LVS()
def add_modules(self):
self.inv = pinv()
self.inv = pinv(height=self.cell_height)
self.add_mod(self.inv)
self.nand2 = pnand2()
self.nand2 = pnand2(height=self.cell_height)
self.add_mod(self.nand2)
self.nand3 = pnand3()
self.nand3 = pnand3(height=self.cell_height)
self.add_mod(self.nand3)
self.add_decoders()
def add_decoders(self):
""" Create the decoders based on the number of pre-decodes """
self.pre2_4 = pre2x4()
self.pre2_4 = pre2x4(height=self.cell_height)
self.add_mod(self.pre2_4)
self.pre3_8 = pre3x8()
self.pre3_8 = pre3x8(height=self.cell_height)
self.add_mod(self.pre3_8)
def determine_predecodes(self,num_inputs):
@ -336,7 +333,7 @@ class hierarchical_decoder(design.design):
if (self.num_inputs == 4 or self.num_inputs == 5):
for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])):
row = len(self.predec_groups[1])*i + j
row = len(self.predec_groups[0])*j + i
name = self.NAND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name,
mod=self.nand2))
@ -352,8 +349,8 @@ class hierarchical_decoder(design.design):
for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])):
for k in range(len(self.predec_groups[2])):
row = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \
+ len(self.predec_groups[2])*j + k
row = (len(self.predec_groups[0])*len(self.predec_groups[1])) * k \
+ len(self.predec_groups[0])*j + i
name = self.NAND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name,
@ -523,8 +520,8 @@ class hierarchical_decoder(design.design):
"""
row_index = 0
if (self.num_inputs == 4 or self.num_inputs == 5):
for index_A in self.predec_groups[0]:
for index_B in self.predec_groups[1]:
for index_B in self.predec_groups[1]:
for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus?
predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
@ -533,9 +530,9 @@ class hierarchical_decoder(design.design):
row_index = row_index + 1
elif (self.num_inputs > 5):
for index_A in self.predec_groups[0]:
for index_C in self.predec_groups[2]:
for index_B in self.predec_groups[1]:
for index_C in self.predec_groups[2]:
for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus?
predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))

View File

@ -9,19 +9,18 @@ from globals import OPTS
from pnand2 import pnand2
from pnand3 import pnand3
class hierarchical_predecode(design.design):
"""
Pre 2x4 and 3x8 decoder shared code.
"""
def __init__(self, input_number):
unique_id = 1
def __init__(self, input_number, height=None):
self.number_of_inputs = input_number
self.cell_height = height
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
design.design.__init__(self, name="pre{0}x{1}".format(self.number_of_inputs,self.number_of_outputs))
from importlib import reload
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
design.design.__init__(self, name="pre{0}x{1}_{2}".format(self.number_of_inputs,self.number_of_outputs,hierarchical_predecode.unique_id))
hierarchical_predecode.unique_id += 1
def add_pins(self):
for k in range(self.number_of_inputs):
@ -34,7 +33,7 @@ class hierarchical_predecode(design.design):
def add_modules(self):
""" Add the INV and NAND gate modules """
self.inv = pinv()
self.inv = pinv(height=self.cell_height)
self.add_mod(self.inv)
self.add_nand(self.number_of_inputs)
@ -43,9 +42,9 @@ class hierarchical_predecode(design.design):
def add_nand(self,inputs):
""" Create the NAND for the predecode input stage """
if inputs==2:
self.nand = pnand2()
self.nand = pnand2(height=self.cell_height)
elif inputs==3:
self.nand = pnand3()
self.nand = pnand3(height=self.cell_height)
else:
debug.error("Invalid number of predecode inputs: {}".format(inputs),-1)
@ -90,7 +89,7 @@ class hierarchical_predecode(design.design):
""" Create the input inverters to invert input signals for the decode stage. """
self.in_inst = []
for inv_num in range(self.number_of_inputs):
name = "Xpre_inv_{0}".format(inv_num)
name = "pre_inv_{0}".format(inv_num)
self.in_inst.append(self.add_inst(name=name,
mod=self.inv))
self.connect_inst(["in_{0}".format(inv_num),
@ -114,7 +113,7 @@ class hierarchical_predecode(design.design):
""" Create inverters for the inverted output decode signals. """
self.inv_inst = []
for inv_num in range(self.number_of_outputs):
name = "Xpre_nand_inv_{}".format(inv_num)
name = "pre_nand_inv_{}".format(inv_num)
self.inv_inst.append(self.add_inst(name=name,
mod=self.inv))
self.connect_inst(["Z_{}".format(inv_num),

View File

@ -9,8 +9,8 @@ class hierarchical_predecode2x4(hierarchical_predecode):
"""
Pre 2x4 decoder used in hierarchical_decoder.
"""
def __init__(self):
hierarchical_predecode.__init__(self, 2)
def __init__(self, height=None):
hierarchical_predecode.__init__(self, 2, height)
self.create_netlist()
if not OPTS.netlist_only:

View File

@ -9,8 +9,8 @@ class hierarchical_predecode3x8(hierarchical_predecode):
"""
Pre 3x8 decoder used in hierarchical_decoder.
"""
def __init__(self):
hierarchical_predecode.__init__(self, 3)
def __init__(self, height=None):
hierarchical_predecode.__init__(self, 3, height)
self.create_netlist()
if not OPTS.netlist_only:

View File

@ -33,7 +33,7 @@ class precharge_array(design.design):
for i in range(self.columns):
self.add_pin("bl_{0}".format(i))
self.add_pin("br_{0}".format(i))
self.add_pin("en")
self.add_pin("en_bar")
self.add_pin("vdd")
def create_netlist(self):
@ -59,9 +59,9 @@ class precharge_array(design.design):
def add_layout_pins(self):
self.add_layout_pin(text="en",
self.add_layout_pin(text="en_bar",
layer="metal1",
offset=self.pc_cell.get_pin("en").ll(),
offset=self.pc_cell.get_pin("en_bar").ll(),
width=self.width,
height=drc("minwidth_metal1"))
@ -94,7 +94,7 @@ class precharge_array(design.design):
mod=self.pc_cell,
offset=offset)
self.local_insts.append(inst)
self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en", "vdd"])
self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en_bar", "vdd"])
def place_insts(self):

View File

@ -265,15 +265,8 @@ class replica_bitline(design.design):
pin = self.rbl_inv_inst.get_pin("vdd")
self.add_power_pin("vdd", pin.lc())
# Replica bitcell needs to be routed up to M3
pin=self.rbc_inst.get_pin("vdd")
# Don't rotate this via to vit in FreePDK45. In the custom cell, the pin cannot be placed
# directly on vdd or there will be a drc error with a wordline. Place the pin slightly farther
# away then route to it. A better solution would be to rotate the m1 in the via or move the pin
# a m1_pitch below the entire cell.
pin_extension = pin.center() - vector(0,self.m1_pitch)
self.add_power_pin("vdd", pin_extension, rotate=0)
self.add_path("metal1", [pin.center(), pin_extension])
self.add_power_pin("vdd", pin.center(), 0, pin.layer)
for pin in self.rbc_inst.get_pins("gnd"):
self.add_power_pin("gnd", pin.center())

View File

@ -44,7 +44,7 @@ class wordline_driver(design.design):
# Outputs from wordline_driver.
for i in range(self.rows):
self.add_pin("wl_{0}".format(i))
self.add_pin("en")
self.add_pin("en_bar")
self.add_pin("vdd")
self.add_pin("gnd")
@ -67,7 +67,7 @@ class wordline_driver(design.design):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
# Find the x offsets for where the vias/pins should be placed
a_xoffset = self.inv1_inst[0].rx()
a_xoffset = self.nand_inst[0].rx()
b_xoffset = self.inv2_inst[0].lx()
for num in range(self.rows):
# this will result in duplicate polygons for rails, but who cares
@ -95,24 +95,16 @@ class wordline_driver(design.design):
def create_drivers(self):
self.inv1_inst = []
self.nand_inst = []
self.inv2_inst = []
for row in range(self.rows):
name_inv1 = "wl_driver_inv_en{}".format(row)
name_nand = "wl_driver_nand{}".format(row)
name_inv2 = "wl_driver_inv{}".format(row)
# add inv1 based on the info above
self.inv1_inst.append(self.add_inst(name=name_inv1,
mod=self.inv_no_output))
self.connect_inst(["en",
"en_bar_{0}".format(row),
"vdd", "gnd"])
# add nand 2
self.nand_inst.append(self.add_inst(name=name_nand,
mod=self.nand2))
self.connect_inst(["en_bar_{0}".format(row),
self.connect_inst(["en_bar",
"in_{0}".format(row),
"wl_bar_{0}".format(row),
"vdd", "gnd"])
@ -125,8 +117,7 @@ class wordline_driver(design.design):
def place_drivers(self):
inv1_xoffset = 2*self.m1_width + 5*self.m1_space
nand2_xoffset = inv1_xoffset + self.inv.width
nand2_xoffset = 2*self.m1_width + 5*self.m1_space
inv2_xoffset = nand2_xoffset + self.nand2.width
self.width = inv2_xoffset + self.inv.width
@ -140,13 +131,9 @@ class wordline_driver(design.design):
y_offset = self.inv.height*row
inst_mirror = "R0"
inv1_offset = [inv1_xoffset, y_offset]
nand2_offset=[nand2_xoffset, y_offset]
inv2_offset=[inv2_xoffset, y_offset]
# add inv1 based on the info above
self.inv1_inst[row].place(offset=inv1_offset,
mirror=inst_mirror)
# add nand 2
self.nand_inst[row].place(offset=nand2_offset,
mirror=inst_mirror)
@ -159,7 +146,7 @@ class wordline_driver(design.design):
""" Route all of the signals """
# Wordline enable connection
en_pin=self.add_layout_pin(text="en",
en_pin=self.add_layout_pin(text="en_bar",
layer="metal2",
offset=[self.m1_width + 2*self.m1_space,0],
width=self.m2_width,
@ -167,12 +154,11 @@ class wordline_driver(design.design):
for row in range(self.rows):
inv1_inst = self.inv1_inst[row]
nand_inst = self.nand_inst[row]
inv2_inst = self.inv2_inst[row]
# en connection
a_pin = inv1_inst.get_pin("A")
# en_bar connection
a_pin = nand_inst.get_pin("A")
a_pos = a_pin.lc()
clk_offset = vector(en_pin.bc().x,a_pos.y)
self.add_segment_center(layer="metal1",
@ -181,13 +167,6 @@ class wordline_driver(design.design):
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=clk_offset)
# first inv to nand2 A
zb_pos = inv1_inst.get_pin("Z").bc()
zu_pos = inv1_inst.get_pin("Z").uc()
bl_pos = nand_inst.get_pin("A").lc()
br_pos = nand_inst.get_pin("A").rc()
self.add_path("metal1", [zb_pos, zu_pos, bl_pos, br_pos])
# Nand2 out to 2nd inv
zr_pos = nand_inst.get_pin("Z").rc()
al_pos = inv2_inst.get_pin("A").lc()

View File

@ -68,7 +68,7 @@ class write_driver_array(design.design):
def create_write_array(self):
self.driver_insts = {}
for i in range(0,self.columns,self.words_per_row):
name = "Xwrite_driver{}".format(i)
name = "write_driver{}".format(i)
index = int(i/self.words_per_row)
self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver)

128
compiler/pgates/pand2.py Normal file
View File

@ -0,0 +1,128 @@
import debug
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from pnand2 import pnand2
from pinv import pinv
import pgate
class pand2(pgate.pgate):
"""
This is a simple buffer used for driving loads.
"""
from importlib import reload
c = reload(__import__(OPTS.bitcell))
bitcell = getattr(c, OPTS.bitcell)
unique_id = 1
def __init__(self, size=1, height=None, name=""):
self.size = size
if name=="":
name = "pand2_{0}_{1}".format(size, pand2.unique_id)
pand2.unique_id += 1
pgate.pgate.__init__(self, name, height)
debug.info(1, "Creating {}".format(self.name))
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_pins()
self.create_modules()
self.create_insts()
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4
self.nand = pnand2(height=self.height)
self.add_mod(self.nand)
self.inv = pinv(size=self.size, height=self.height)
self.add_mod(self.inv)
def create_layout(self):
self.width = self.nand.width + self.inv.width
self.place_insts()
self.add_wires()
self.add_layout_pins()
def add_pins(self):
self.add_pin("A")
self.add_pin("B")
self.add_pin("Z")
self.add_pin("vdd")
self.add_pin("gnd")
def create_insts(self):
self.nand_inst=self.add_inst(name="pand2_nand",
mod=self.nand)
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
self.inv_inst=self.add_inst(name="pand2_inv",
mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_insts(self):
# Add NAND to the right
self.nand_inst.place(offset=vector(0,0))
# Add INV to the right
self.inv_inst.place(offset=vector(self.nand_inst.rx(),0))
def add_wires(self):
# nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z")
a2_pin = self.inv_inst.get_pin("A")
mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
mid2_point = vector(mid1_point, a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.inv_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll().scale(0,1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin=self.inv_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
width=self.width,
height=vdd_pin.height())
pin = self.inv_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=pin.layer,
offset=pin.center(),
width=pin.width(),
height=pin.height())
for pin_name in ["A","B"]:
pin = self.nand_inst.get_pin(pin_name)
self.add_layout_pin_rect_center(text=pin_name,
layer=pin.layer,
offset=pin.center(),
width=pin.width(),
height=pin.height())
def analytical_delay(self, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
nand_delay = selfnand.analytical_delay(slew=slew, load=self.inv.input_load())
inv_delay = self.inv.analytical_delay(slew=nand_delay.slew, load=load)
return nand_delay + inv_delay

View File

@ -1,12 +1,12 @@
import debug
import design
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from pinv import pinv
import pgate
class pbuf(design.design):
class pbuf(pgate.pgate):
"""
This is a simple buffer used for driving loads.
"""
@ -16,39 +16,32 @@ class pbuf(design.design):
unique_id = 1
def __init__(self, driver_size=4, height=bitcell.height, name=""):
def __init__(self, size=4, height=None, name=""):
stage_effort = 4
# FIXME: Change the number of stages to support high drives.
self.stage_effort = 4
self.size = size
self.height = height
if name=="":
name = "pbuf_{0}_{1}".format(driver_size, pbuf.unique_id)
name = "pbuf_{0}_{1}".format(self.size, pbuf.unique_id)
pbuf.unique_id += 1
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
pgate.pgate.__init__(self, name, height)
debug.info(1, "creating {0} with size of {1}".format(self.name,self.size))
# Shield the cap, but have at least a stage effort of 4
input_size = max(1,int(driver_size/stage_effort))
self.inv1 = pinv(size=input_size, height=height) # 1
self.add_mod(self.inv1)
self.inv2 = pinv(size=driver_size, height=height) # 2
self.add_mod(self.inv2)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
self.width = self.inv1.width + self.inv2.width
self.height = self.inv1.height
self.create_layout()
#self.offset_all_coordinates()
self.DRC_LVS()
def create_netlist(self):
self.add_pins()
self.create_modules()
self.create_insts()
def create_layout(self):
self.add_pins()
self.add_insts()
self.width = self.inv1.width + self.inv2.width
self.place_insts()
self.add_wires()
self.add_layout_pins()
@ -58,19 +51,31 @@ class pbuf(design.design):
self.add_pin("vdd")
self.add_pin("gnd")
def add_insts(self):
# Add INV1 to the right
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4
input_size = max(1,int(self.size/self.stage_effort))
self.inv1 = pinv(size=input_size, height=self.height)
self.add_mod(self.inv1)
self.inv2 = pinv(size=self.size, height=self.height)
self.add_mod(self.inv2)
def create_insts(self):
self.inv1_inst=self.add_inst(name="buf_inv1",
mod=self.inv1,
offset=vector(0,0))
mod=self.inv1)
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
# Add INV2 to the right
self.inv2_inst=self.add_inst(name="buf_inv2",
mod=self.inv2,
offset=vector(self.inv1_inst.rx(),0))
mod=self.inv2)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_insts(self):
# Add INV1 to the right
self.inv1_inst.place(vector(0,0))
# Add INV2 to the right
self.inv2_inst.place(vector(self.inv1_inst.rx(),0))
def add_wires(self):
@ -100,17 +105,17 @@ class pbuf(design.design):
z_pin = self.inv2_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer="metal2",
offset=z_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=z_pin.center())
layer=z_pin.layer,
offset=z_pin.center(),
width=z_pin.width(),
height=z_pin.height())
a_pin = self.inv1_inst.get_pin("A")
self.add_layout_pin_rect_center(text="A",
layer="metal2",
offset=a_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=a_pin.center())
layer=a_pin.layer,
offset=a_pin.center(),
width=a_pin.width(),
height=a_pin.height())

View File

@ -33,7 +33,6 @@ class pnand2(pgate.pgate):
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
#self.DRC_LVS()
def create_netlist(self):
@ -192,26 +191,33 @@ class pnand2(pgate.pgate):
""" Route the Z output """
# PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D")
top_pin_offset = pmos_pin.center()
# NMOS2 drain
nmos_pin = self.nmos2_inst.get_pin("D")
nmos_pin = self.nmos2_inst.get_pin("D")
bottom_pin_offset = nmos_pin.center()
# Output pin
mid_offset = vector(nmos_pin.center().x,self.inputA_yoffset)
out_offset = vector(nmos_pin.center().x + self.m1_pitch,self.inputA_yoffset)
# Midpoints of the L routes go horizontal first then vertical
mid1_offset = vector(out_offset.x, top_pin_offset.y)
mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center())
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center())
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=mid_offset,
offset=out_offset,
rotate=90)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[pmos_pin.bc(), mid_offset, nmos_pin.uc()])
self.add_path("metal2",[top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset])
# This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z",
layer="metal1",
offset=mid_offset,
offset=out_offset,
width=contact.m1m2.first_layer_height,
height=contact.m1m2.first_layer_width)

View File

@ -51,7 +51,7 @@ class precharge(pgate.pgate):
self.DRC_LVS()
def add_pins(self):
self.add_pin_list(["bl", "br", "en", "vdd"])
self.add_pin_list(["bl", "br", "en_bar", "vdd"])
def add_ptx(self):
"""
@ -92,15 +92,15 @@ class precharge(pgate.pgate):
self.lower_pmos_inst=self.add_inst(name="lower_pmos",
mod=self.pmos)
self.connect_inst(["bl", "en", "br", "vdd"])
self.connect_inst(["bl", "en_bar", "br", "vdd"])
self.upper_pmos1_inst=self.add_inst(name="upper_pmos1",
mod=self.pmos)
self.connect_inst(["bl", "en", "vdd", "vdd"])
self.connect_inst(["bl", "en_bar", "vdd", "vdd"])
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2",
mod=self.pmos)
self.connect_inst(["br", "en", "vdd", "vdd"])
self.connect_inst(["br", "en_bar", "vdd", "vdd"])
def place_ptx(self):
@ -161,7 +161,7 @@ class precharge(pgate.pgate):
rotate=90)
# adds the en rail on metal1
self.add_layout_pin_segment_center(text="en",
self.add_layout_pin_segment_center(text="en_bar",
layer="metal1",
start=offset.scale(0,1),
end=offset.scale(0,1)+vector(self.width,0))

View File

@ -68,12 +68,12 @@ class ptx(design.design):
# Just make a guess since these will actually be decided in the layout later.
area_sd = 2.5*drc("minwidth_poly")*self.tx_width
perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width
self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4}u ps={4}u as={5}p ad={5}p".format(spice[self.tx_type],
self.mults,
self.tx_width,
drc("minwidth_poly"),
perimeter_sd,
area_sd)
self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4:.2f}u ps={4:.2f}u as={5:.2f}p ad={5:.2f}p".format(spice[self.tx_type],
self.mults,
self.tx_width,
drc("minwidth_poly"),
perimeter_sd,
area_sd)
self.spice.append("\n* ptx " + self.spice_device)
# self.spice.append(".ENDS {0}".format(self.name))

View File

@ -34,8 +34,6 @@ class sram():
from sram_1bank import sram_1bank as sram
elif self.num_banks == 2:
from sram_2bank import sram_2bank as sram
elif self.num_banks == 4:
from sram_4bank import sram_4bank as sram
else:
debug.error("Invalid number of banks.",-1)

View File

@ -61,15 +61,18 @@ class sram_1bank(sram_base):
row_addr_pos = [None]*len(self.all_ports)
col_addr_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
data_gap = self.m2_pitch*(self.word_size+1)
# 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
max_gap_size = self.m2_pitch*max(self.word_size+1,self.col_addr_size+1) + 2*self.m1_pitch
# Port 0
port = 0
# This includes 2 M2 pitches for the row addr clock line
# This includes 2 M2 pitches for the row addr clock line.
# It is also placed to align with the column decoder (if it exists hence the bank gap)
control_pos[port] = vector(-self.control_logic_insts[port].width - 2*self.m2_pitch,
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y)
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y - self.bank.m2_gap)
self.control_logic_insts[port].place(control_pos[port])
# The row address bits are placed above the control logic aligned on the right.
@ -81,8 +84,8 @@ class sram_1bank(sram_base):
# Add the col address flops below the bank to the left of the lower-left of bank array
if self.col_addr_dff:
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.central_bus_width,
-data_gap - self.col_addr_dff_insts[port].height)
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
-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
@ -92,16 +95,18 @@ class sram_1bank(sram_base):
# sense amps.
if port in self.write_ports:
data_pos[port] = vector(self.bank.bank_array_ll.x,
-data_gap - self.data_dff_insts[port].height)
-max_gap_size - self.data_dff_insts[port].height)
self.data_dff_insts[port].place(data_pos[port])
if len(self.all_ports)>1:
# Port 1
port = 1
# This includes 2 M2 pitches for the row addr clock line
# It is also placed to align with the column decoder (if it exists hence the bank gap)
control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch,
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y)
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y + self.bank.m2_gap)
self.control_logic_insts[port].place(control_pos[port], mirror="MY")
# The row address bits are placed above the control logic aligned on the left.
@ -113,8 +118,8 @@ class sram_1bank(sram_base):
# Add the col address flops above the bank to the right of the upper-right of bank array
if self.col_addr_dff:
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.central_bus_width,
self.bank_inst.uy() + data_gap + self.col_addr_dff_insts[port].height)
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
self.bank.height + max_gap_size + self.col_addr_dff_insts[port].height)
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX")
# Add the data flops above the bank to the left of the upper-right of bank array
@ -124,7 +129,7 @@ class sram_1bank(sram_base):
# 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.uy() + data_gap + self.data_dff_insts[port].height)
self.bank.height + max_gap_size + self.data_dff_insts[port].height)
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
@ -178,27 +183,11 @@ class sram_1bank(sram_base):
# Connect all of these clock pins to the clock in the central bus
# This is something like a "spine" clock distribution. The two spines
# are clk_buf and clk_buf_bar
control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf")
control_clk_buf_pos = control_clk_buf_pin.center()
bank_clk_buf_pin = self.bank_inst.get_pin("clk_buf{}".format(port))
bank_clk_buf_pos = bank_clk_buf_pin.center()
bank_clk_buf_bar_pin = self.bank_inst.get_pin("clk_buf_bar{}".format(port))
bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center()
if self.col_addr_dff:
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
dff_clk_pos = dff_clk_pin.center()
mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos])
if port in self.write_ports:
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
data_dff_clk_pos = data_dff_clk_pin.center()
mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos])
# This uses a metal2 track to the right (for port0) of the control/row addr DFF
# to route vertically. For port1, it is to the left.
control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf")
row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk")
if port%2:
control_clk_buf_pos = control_clk_buf_pin.lc()
@ -210,17 +199,38 @@ class sram_1bank(sram_base):
row_addr_clk_pos = row_addr_clk_pin.rc()
mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch,
row_addr_clk_pos.y)
mid2_pos = vector(mid1_pos.x,
control_clk_buf_pos.y)
# This is the steiner point where the net branches out
clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y)
self.add_path("metal1", [control_clk_buf_pos, clk_steiner_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=clk_steiner_pos,
rotate=90)
# Note, the via to the control logic is taken care of when we route
# the control logic to the bank
self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, mid2_pos, control_clk_buf_pos])
self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos, control_clk_buf_pos])
if self.col_addr_dff:
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
dff_clk_pos = dff_clk_pin.center()
mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, clk_steiner_pos])
if port in self.write_ports:
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
data_dff_clk_pos = data_dff_clk_pin.center()
mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos])
def route_control_logic(self):
""" Route the outputs from the control logic module """
for port in self.all_ports:
for signal in self.control_logic_outputs[port]:
# The clock gets routed separately and is not a part of the bank
if "clk" in signal:
continue
src_pin = self.control_logic_insts[port].get_pin(signal)
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
@ -268,17 +278,20 @@ class sram_1bank(sram_base):
""" Connect the output of the data flops to the write driver """
# This is where the channel will start (y-dimension at least)
for port in self.write_ports:
offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
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)
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]
bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
route_map = list(zip(bank_names, dff_names))
dff_pins = {key: self.data_dff_insts[port].get_pin(key) for key in dff_names }
bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names }
# Combine the dff and bank pins into a single dictionary of pin name to pin.
all_pins = {**dff_pins, **bank_pins}
self.create_horizontal_channel_route(route_map, all_pins, offset)
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map = list(zip(bank_pins, dff_pins))
self.create_horizontal_channel_route(route_map, offset)

View File

@ -1,331 +0,0 @@
import sys
from tech import drc, spice
import debug
from math import log,sqrt,ceil
import datetime
import getpass
from vector import vector
from globals import OPTS, print_time
from sram_base import sram_base
from bank import bank
from dff_buf_array import dff_buf_array
from dff_array import dff_array
class sram_4bank(sram_base):
"""
Procedures specific to a four bank SRAM.
"""
def __init__(self, name, sram_config):
sram_base.__init__(self, name, sram_config)
def compute_bank_offsets(self):
""" Compute the overall offsets for a four bank SRAM """
# The main difference is that the four bank SRAM has the data bus in the middle of the four banks
# as opposed to the top of the banks.
# In 4 bank SRAM, the height is determined by the bank decoder and address flop
self.vertical_bus_height = 2*self.bank.height + 4*self.bank_to_bus_distance + self.data_bus_height \
+ self.supply_bus_height + self.msb_decoder.height + self.msb_address.width
# The address bus extends down through the power rails, but control and bank_sel bus don't
self.addr_bus_height = self.vertical_bus_height
self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 0)
self.data_bus_offset = vector(0, self.bank.height + self.bank_to_bus_distance)
self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height \
+ self.bank.height + 2*self.bank_to_bus_distance)
self.control_bus_offset = vector(0, self.supply_bus_offset.y + self.supply_bus_height)
self.bank_sel_bus_offset = self.vertical_bus_offset + vector(self.m2_pitch*self.control_size,0)
self.addr_bus_offset = self.bank_sel_bus_offset.scale(1,0) + vector(self.m2_pitch*self.num_banks,0)
# Control is placed at the top above the control bus and everything
self.control_logic_position = vector(0, self.control_bus_offset.y + self.control_bus_height + self.m1_pitch)
# Bank select flops get put to the right of control logic above bank1 and the buses
# Leave a pitch to get the vdd rails up to M2
self.msb_address_position = vector(self.bank_inst[1].lx() + 3*self.supply_rail_pitch,
self.supply_bus_offset.y + self.supply_bus_height \
+ 2*self.m1_pitch + self.msb_address.width)
# Decoder goes above the MSB address flops, and is flipped in Y
# separate the two by a bank to bus distance for nwell rules, just in case
self.msb_decoder_position = self.msb_address_position + vector(self.msb_decoder.width, self.bank_to_bus_distance)
def add_modules(self):
""" Adds the modules and the buses to the top level """
self.compute_bus_sizes()
self.add_banks()
self.compute_bank_offsets()
self.add_busses()
self.add_logic()
self.width = self.bank_inst[1].ur().x
self.height = max(self.control_logic_inst.uy(),self.msb_decoder_inst.uy())
def add_banks(self):
# Placement of bank 0 (upper left)
bank_position_0 = vector(self.bank.width,
self.bank.height + self.data_bus_height + 2*self.bank_to_bus_distance)
self.bank_inst=[self.add_bank(0, bank_position_0, 1, -1)]
# Placement of bank 1 (upper right)
x_off = self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance
bank_position_1 = vector(x_off, bank_position_0.y)
self.bank_inst.append(self.add_bank(1, bank_position_1, 1, 1))
# Placement of bank 2 (bottom left)
y_off = self.bank.height
bank_position_2 = vector(bank_position_0.x, y_off)
self.bank_inst.append(self.add_bank(2, bank_position_2, -1, -1))
# Placement of bank 3 (bottom right)
bank_position_3 = vector(bank_position_1.x, bank_position_2.y)
self.bank_inst.append(self.add_bank(3, bank_position_3, -1, 1))
def add_logic(self):
""" Add the control and MSB decode/bank select logic for four banks """
self.add_control_logic(position=self.control_logic_position)
self.msb_address_inst = self.add_inst(name="msb_address",
mod=self.msb_address,
offset=self.msb_address_position,
rotate=270)
self.msb_bank_sel_addr = ["ADDR[{}]".format(i) for i in range(self.addr_size-2,self.addr_size,1)]
temp = list(self.msb_bank_sel_addr)
temp.extend(["msb{0}[{1}]".format(j,i) for i in range(2) for j in ["","_bar"]])
temp.extend(["clk_buf", "vdd", "gnd"])
self.connect_inst(temp)
self.msb_decoder_inst = self.add_inst(name="msb_decoder",
mod=self.msb_decoder,
offset=self.msb_decoder_position,
mirror="MY")
temp = ["msb[{}]".format(i) for i in range(2)]
temp.extend(["bank_sel[{}]".format(i) for i in range(4)])
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def route_double_msb_address(self):
""" Route two MSB address bits and the bank decoder for 4-bank SRAM """
# connect the MSB flops to the address input bus
for i in [0,1]:
msb_pins = self.msb_address_inst.get_pins("din_{}".format(i))
for msb_pin in msb_pins:
if msb_pin.layer == "metal3":
msb_pin_pos = msb_pin.lc()
break
rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr[i]].x,msb_pin_pos.y)
self.add_path("metal3",[msb_pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# Connect clk
clk_pin = self.msb_address_inst.get_pin("clk")
clk_pos = clk_pin.bc()
rail_pos = self.horz_control_bus_positions["clk_buf"]
bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y)
self.add_path("metal1",[clk_pos,bend_pos,rail_pos])
# Connect bank decoder outputs to the bank select vertical bus wires
for i in range(self.num_banks):
msb_pin = self.msb_decoder_inst.get_pin("out_{}".format(i))
msb_pin_pos = msb_pin.lc()
rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,msb_pin_pos.y)
self.add_path("metal1",[msb_pin_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
# connect MSB flop outputs to the bank decoder inputs
msb_pin = self.msb_address_inst.get_pin("dout[0]")
msb_pin_pos = msb_pin.rc()
in_pin = self.msb_decoder_inst.get_pin("in[0]")
in_pos = in_pin.bc() + vector(0,1*self.m2_pitch,) # pin is up from bottom
out_pos = msb_pin_pos + vector(1*self.m2_pitch,0) # route out to the right
up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer
self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos])
self.add_via_center(("metal1","via1","metal2"),in_pos)
self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90)
msb_pin = self.msb_address_inst.get_pin("dout[1]")
msb_pin_pos = msb_pin.rc()
in_pin = self.msb_decoder_inst.get_pin("in[1]")
in_pos = in_pin.bc() + vector(0,self.bitcell.height+self.m2_pitch) # route the next row up
out_pos = msb_pin_pos + vector(2*self.m2_pitch,0) # route out to the right
up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer
self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos])
self.add_via_center(("metal1","via1","metal2"),in_pos)
self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90)
self.route_double_msb_address_supplies()
def route_double_msb_address_supplies(self):
""" Route the vdd/gnd bits of the 2-bit bank decoder. """
# Route the right-most vdd/gnd of the right upper bank to the top of the decoder
vdd_pins = self.bank_inst[1].get_pins("vdd")
left_bank_vdd_pin = None
right_bank_vdd_pin = None
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal2":
continue
if left_bank_vdd_pin == None or vdd_pin.lx()<left_bank_vdd_pin.lx():
left_bank_vdd_pin = vdd_pin
if right_bank_vdd_pin == None or vdd_pin.lx()>right_bank_vdd_pin.lx():
right_bank_vdd_pin = vdd_pin
# Route to top
self.add_rect(layer="metal2",
offset=vdd_pin.ul(),
height=self.height-vdd_pin.uy(),
width=vdd_pin.width())
gnd_pins = self.bank_inst[1].get_pins("gnd")
left_bank_gnd_pin = None
right_bank_gnd_pin = None
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal2":
continue
if left_bank_gnd_pin == None or gnd_pin.lx()<left_bank_gnd_pin.lx():
left_bank_gnd_pin = gnd_pin
if right_bank_gnd_pin == None or gnd_pin.lx()>right_bank_gnd_pin.lx():
right_bank_gnd_pin = gnd_pin
# Route to top
self.add_rect(layer="metal2",
offset=gnd_pin.ul(),
height=self.height-gnd_pin.uy(),
width=gnd_pin.width())
# Connect bank decoder vdd/gnd supplies using the previous bank pins
vdd_pins = self.msb_decoder_inst.get_pins("vdd")
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal1":
continue
rail1_pos = vector(left_bank_vdd_pin.cx(),vdd_pin.cy())
rail2_pos = vector(right_bank_vdd_pin.cx(),vdd_pin.cy())
self.add_path("metal1",[rail1_pos,rail2_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail1_pos,
rotate=90,
size=[1,3])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail2_pos,
rotate=90,
size=[1,3])
gnd_pins = self.msb_decoder_inst.get_pins("gnd")
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal1":
continue
rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy())
rail2_pos = vector(right_bank_gnd_pin.cx(),gnd_pin.cy())
self.add_path("metal1",[rail1_pos,rail2_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail1_pos,
rotate=90,
size=[1,3])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail2_pos,
rotate=90,
size=[1,3])
# connect the bank MSB flop supplies
vdd_pins = self.msb_address_inst.get_pins("vdd")
# vdd pins go down to the rail
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal1":
continue
vdd_pos = vdd_pin.bc()
down_pos = vdd_pos - vector(0,self.m1_pitch)
rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y)
self.add_path("metal1",[vdd_pos,down_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=down_pos,
rotate=90)
self.add_path("metal2",[down_pos,rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos)
# gnd pins go right to the rail
gnd_pins = self.msb_address_inst.get_pins("gnd")
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal2":
continue
rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy())
self.add_path("metal1",[rail1_pos,gnd_pin.lc()])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=gnd_pin.lc(),
rotate=90)
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail1_pos,
rotate=90,
size=[1,3])
def route(self):
""" Route all of the signals for the four bank SRAM. """
self.route_shared_banks()
# connect the data output to the data bus
for n in self.data_bus_names:
for i in [0,1]:
pin_pos = self.bank_inst[i].get_pin(n).bc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
for i in [2,3]:
pin_pos = self.bank_inst[i].get_pin(n).uc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# route msb address bits
# route 2:4 decoder
self.route_double_msb_address()
# connect the banks to the vertical address bus
# connect the banks to the vertical control bus
for n in self.addr_bus_names + self.control_bus_names:
# Skip these from the horizontal bus
if n in ["vdd", "gnd"]: continue
# This will be the bank select, so skip it
if n in self.msb_bank_sel_addr: continue
for bank_id in [0,2]:
pin0_pos = self.bank_inst[bank_id].get_pin(n).rc()
pin1_pos = self.bank_inst[bank_id+1].get_pin(n).lc()
rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y)
self.add_path("metal3",[pin0_pos,pin1_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
self.route_bank_supply_rails(left_banks=[0,2], bottom_banks=[2,3])
def add_lvs_correspondence_points(self):
"""
This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
if self.num_banks==1: return
for n in self.control_bus_names:
self.add_label(text=n,
layer="metal2",
offset=self.vert_control_bus_positions[n])
for n in self.bank_sel_bus_names:
self.add_label(text=n,
layer="metal2",
offset=self.vert_control_bus_positions[n])

View File

@ -140,11 +140,16 @@ class sram_base(design):
# The order of the control signals on the control bus:
self.control_bus_names = []
for port in self.all_ports:
self.control_bus_names[port] = ["clk_buf{}".format(port), "clk_buf_bar{}".format(port)]
if (self.port_id[port] == "rw") or (self.port_id[port] == "w"):
self.control_bus_names[port].append("w_en{}".format(port))
if (self.port_id[port] == "rw") or (self.port_id[port] == "r"):
self.control_bus_names[port].append("s_en{}".format(port))
self.control_bus_names[port] = ["clk_buf{}".format(port)]
wen = "w_en{}".format(port)
sen = "s_en{}".format(port)
pen = "p_en_bar{}".format(port)
if self.port_id[port] == "r":
self.control_bus_names[port].extend([sen, pen])
elif self.port_id[port] == "w":
self.control_bus_names[port].extend([wen])
else:
self.control_bus_names[port].extend([sen, wen, pen])
self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
pitch=self.m2_pitch,
offset=self.vertical_bus_offset,
@ -287,11 +292,12 @@ class sram_base(design):
temp.append("bank_sel{0}[{1}]".format(port,bank_num))
for port in self.read_ports:
temp.append("s_en{0}".format(port))
for port in self.read_ports:
temp.append("p_en_bar{0}".format(port))
for port in self.write_ports:
temp.append("w_en{0}".format(port))
for port in self.all_ports:
temp.append("clk_buf_bar{0}".format(port))
temp.append("clk_buf{0}".format(port))
temp.append("wl_en{0}".format(port))
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
@ -403,16 +409,21 @@ class sram_base(design):
mod = self.control_logic_r
insts.append(self.add_inst(name="control{}".format(port), mod=mod))
# Inputs
temp = ["csb{}".format(port)]
if port in self.readwrite_ports:
temp.append("web{}".format(port))
temp.append("clk{}".format(port))
# Ouputs
if port in self.read_ports:
temp.append("s_en{}".format(port))
if port in self.write_ports:
temp.append("w_en{}".format(port))
temp.extend(["clk_buf_bar{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"])
if port in self.read_ports:
temp.append("p_en_bar{}".format(port))
temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"])
self.connect_inst(temp)
return insts

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
Run a regression test on a dff_inv.
Run a regression test on a pand2 cell
"""
import unittest
@ -11,19 +11,22 @@ import globals
from globals import OPTS
import debug
class dff_inv_test(openram_test):
class pand2_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
import dff_inv
global verify
import verify
debug.info(2, "Testing dff_inv 4x")
a = dff_inv.dff_inv(4)
import pand2
debug.info(2, "Testing pand2 gate 4x")
a = pand2.pand2(4)
self.local_check(a)
globals.end_openram()
# run the test from the command line
# instantiate a copdsay of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]

0
compiler/tests/04_pbuf_test.py Normal file → Executable file
View File

View File

@ -1,39 +0,0 @@
#!/usr/bin/env python3
"""
Run a regression test on a dff_array.
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class dff_inv_array_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
import dff_inv_array
debug.info(2, "Testing dff_inv_array for 3x3")
a = dff_inv_array.dff_inv_array(rows=3, columns=3)
self.local_check(a)
debug.info(2, "Testing dff_inv_array for 1x3")
a = dff_inv_array.dff_inv_array(rows=1, columns=3)
self.local_check(a)
debug.info(2, "Testing dff_inv_array for 3x1")
a = dff_inv_array.dff_inv_array(rows=3, columns=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()

View File

@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
class psram_1bank_2mux_1rw_1w_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 1
OPTS.num_r_ports = 0
c = sram_config(word_size=4,
num_words=32,
num_banks=1)
c.num_words=32
c.words_per_row=2
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
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()

View File

@ -11,8 +11,8 @@ import globals
from globals import OPTS
import debug
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
class psram_1bank_2mux_test(openram_test):
#@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error")
class psram_1bank_2mux_1w_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
@ -30,7 +30,13 @@ class psram_1bank_2mux_test(openram_test):
num_banks=1)
c.num_words=32
c.words_per_row=2
debug.info(1, "Single bank two way column mux 1w/1r with control logic")
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
@unittest.skip("SKIPPING 20_psram_1bank_2mux_test, wide metal supply routing error")
class psram_1bank_2mux_test(openram_test):
def runTest(self):
@ -31,7 +31,13 @@ class psram_1bank_2mux_test(openram_test):
num_banks=1)
c.num_words=32
c.words_per_row=2
debug.info(1, "Single bank two way column mux with control logic")
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class psram_1bank_4mux_1rw_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell="replica_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 0
OPTS.num_r_ports = 1
c = sram_config(word_size=4,
num_words=64,
num_banks=1)
c.num_words=64
c.words_per_row=4
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
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()

View File

@ -29,7 +29,13 @@ class sram_1bank_2mux_1rw_1r_test(openram_test):
num_banks=1)
c.words_per_row=2
debug.info(1, "Single bank, two way column mux 1rw, 1r with control logic")
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -23,7 +23,13 @@ class sram_1bank_2mux_test(openram_test):
num_banks=1)
c.words_per_row=2
debug.info(1, "Single bank two way column mux with control logic")
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -23,7 +23,13 @@ class sram_1bank_4mux_test(openram_test):
num_banks=1)
c.words_per_row=4
debug.info(1, "Single bank, four way column mux with control logic")
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""
Run a regression test on a 1 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
class sram_1bank_8mux_1rw_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
OPTS.bitcell = "bitcell_1rw_1r"
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
c = sram_config(word_size=2,
num_words=128,
num_banks=1)
c.words_per_row=8
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
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()

View File

@ -23,7 +23,13 @@ class sram_1bank_8mux_test(openram_test):
num_banks=1)
c.words_per_row=8
debug.info(1, "Single bank, eight way column mux with control logic")
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -29,7 +29,13 @@ class sram_1bank_nomux_1rw_1r_test(openram_test):
num_banks=1)
c.words_per_row=1
debug.info(1, "Single bank, no column mux 1rw, 1r with control logic")
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -23,7 +23,13 @@ class sram_1bank_nomux_test(openram_test):
num_banks=1)
c.words_per_row=1
debug.info(1, "Single bank, no column mux with control logic")
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = sram(c, "sram")
self.local_check(a, final_verification=True)

View File

@ -1,55 +0,0 @@
#!/usr/bin/env python3
"""
Run a regression test on a 4 bank SRAM
"""
import unittest
from testutils import header,openram_test
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
from globals import OPTS
import debug
@unittest.skip("Multibank is not working yet.")
class sram_4bank_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
from sram import sram
from sram_config import sram_config
c = sram_config(word_size=16,
num_words=64,
num_banks=4)
debug.info(1, "Four bank, no column mux with control logic")
a = sram(c, "sram1")
self.local_check(a, final_verification=True)
c.num_words=128
c.words_per_row=2
debug.info(1, "Four bank two way column mux with control logic")
a = sram(c, "sram2")
self.local_check(a, final_verification=True)
c.num_words=256
c.words_per_row=4
debug.info(1, "Four bank, four way column mux with control logic")
a = sram(c, "sram3")
self.local_check(a, final_verification=True)
c.word_size=2
c.num_words=256
c.words_per_row=8
debug.info(1, "Four bank, eight way column mux with control logic")
a = sram.sram(c, "sram4")
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()

View File

@ -49,29 +49,28 @@ class timing_sram_test(openram_test):
#Combine info about port into all data
data.update(port_data[0])
#Assumes single rw port (6t sram)
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [2.5829000000000004],
'delay_lh': [0.2255964],
'leakage_power': 0.0019498999999999996,
golden_data = {'delay_hl': [2.6232],
'delay_lh': [0.2775342],
'leakage_power': 0.0020258999999999997,
'min_period': 4.844,
'read0_power': [0.055371399999999994],
'read1_power': [0.0520225],
'slew_hl': [0.0794261],
'slew_lh': [0.0236264],
'write0_power': [0.06545659999999999],
'write1_power': [0.057846299999999996]}
'read0_power': [0.0557804],
'read1_power': [0.0525619],
'slew_hl': [0.1082014],
'slew_lh': [0.0238257],
'write0_power': [0.0456528],
'write1_power': [0.0442747]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [3.452],
'delay_lh': [1.3792000000000002],
'leakage_power': 0.0257065,
'min_period': 4.688,
'read0_power': [15.0755],
'read1_power': [14.4526],
'slew_hl': [0.6137363],
'slew_lh': [0.3381045],
'write0_power': [16.9203],
'write1_power': [15.367]}
golden_data = {'delay_hl': [6.079300000000001],
'delay_lh': [1.7767000000000002],
'leakage_power': 0.026282499999999997,
'min_period': 9.375,
'read0_power': [6.5802],
'read1_power': [6.2815],
'slew_hl': [0.7396921999999999],
'slew_lh': [0.3397355],
'write0_power': [5.7337],
'write1_power': [5.8691]}
else:
self.assertTrue(False) # other techs fail
# Check if no too many or too few results

View File

@ -50,27 +50,27 @@ class timing_sram_test(openram_test):
data.update(port_data[0])
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [2.584251],
'delay_lh': [0.22870469999999998],
'leakage_power': 0.0009567935,
golden_data = {'delay_hl': [2.625351],
'delay_lh': [0.28080869999999997],
'leakage_power': 0.001040682,
'min_period': 4.844,
'read0_power': [0.0547588],
'read1_power': [0.051159970000000006],
'slew_hl': [0.08164099999999999],
'slew_lh': [0.025474979999999998],
'write0_power': [0.06513271999999999],
'write1_power': [0.058057000000000004]}
'read0_power': [0.0553667],
'read1_power': [0.05177618],
'slew_hl': [0.1099853],
'slew_lh': [0.02568626],
'write0_power': [0.04517803],
'write1_power': [0.04449207]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [3.644147],
'delay_lh': [1.629815],
'leakage_power': 0.001542964,
'min_period': 4.688,
'read0_power': [16.28732],
'read1_power': [15.75155],
'slew_hl': [0.6722473],
'slew_lh': [0.3386347],
'write0_power': [18.545450000000002],
'write1_power': [16.81084]}
golden_data = {'delay_hl': [6.45408],
'delay_lh': [2.0787519999999997],
'leakage_power': 0.001177846,
'min_period': 9.688,
'read0_power': [7.088419],
'read1_power': [6.824107000000001],
'slew_hl': [0.7980976999999999],
'slew_lh': [0.3393389],
'write0_power': [5.982207],
'write1_power': [6.28866]}
else:
self.assertTrue(False) # other techs fail

View File

@ -11,8 +11,8 @@ import globals
from globals import OPTS
import debug
#@unittest.skip("SKIPPING 22_psram_1bank_2mux_func_test")
class psram_1bank_2mux_func_test(openram_test):
@unittest.skip("SKIPPING 22_psram_1bank_2mux_1rw_1r_1w_func_test, third port reads are broken?")
class psram_1bank_2mux_1rw_1r_1w_func_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
@ -35,10 +35,13 @@ class psram_1bank_2mux_func_test(openram_test):
num_words=64,
num_banks=1)
c.words_per_row=2
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS
import debug
#@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test")
@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test, third port reads are broken?")
class psram_1bank_4mux_func_test(openram_test):
def runTest(self):
@ -35,10 +35,13 @@ class psram_1bank_4mux_func_test(openram_test):
num_words=256,
num_banks=1)
c.words_per_row=4
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)

View File

@ -35,10 +35,13 @@ class psram_1bank_8mux_func_test(openram_test):
num_words=256,
num_banks=1)
c.words_per_row=8
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)

View File

@ -35,10 +35,13 @@ class psram_1bank_nomux_func_test(openram_test):
num_words=32,
num_banks=1)
c.words_per_row=1
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)

View File

@ -45,13 +45,14 @@ class openram_test(openram_test):
OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME"))
cmd_string = "python3 {0}/openram.py -n -o {1} -p {2} {3} {0}/tests/config_20_{4}.py 2>&1 > {2}/output.log"
cmd = cmd_string.format(OPENRAM_HOME,
out_file,
out_path,
opts,
OPTS.tech_name)
# Always perform code coverage
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
cmd = "{0} -n -o {1} -p {2} {3} config_20_{4}.py 2>&1 > {5}/output.log".format(exe_name,
out_file,
out_path,
verbosity,
OPTS.tech_name,
out_path)
debug.info(1, cmd)
os.system(cmd)

View File

@ -2,34 +2,35 @@
.SUBCKT write_driver din bl br en vdd gnd
**** Inverter to conver Data_in to data_in_bar ******
M_1 din_bar din gnd gnd n W='1.2*1u' L=0.6u
M_2 din_bar din vdd vdd p W='2.1*1u' L=0.6u
* din_bar = inv(din)
M_1 din_bar din gnd gnd n W=1.2u L=0.6u
M_2 din_bar din vdd vdd p W=2.1u L=0.6u
**** 2input nand gate follwed by inverter to drive BL ******
M_3 din_bar_gated en net_7 gnd n W='2.1*1u' L=0.6u
M_4 net_7 din gnd gnd n W='2.1*1u' L=0.6u
M_5 din_bar_gated en vdd vdd p W='2.1*1u' L=0.6u
M_6 din_bar_gated din vdd vdd p W='2.1*1u' L=0.6u
M_7 net_1 din_bar_gated vdd vdd p W='2.1*1u' L=0.6u
M_8 net_1 din_bar_gated gnd gnd n W='1.2*1u' L=0.6u
* din_bar_gated = nand(en, din)
M_3 din_bar_gated en net_7 gnd n W=2.1u L=0.6u
M_4 net_7 din gnd gnd n W=2.1u L=0.6u
M_5 din_bar_gated en vdd vdd p W=2.1u L=0.6u
M_6 din_bar_gated din vdd vdd p W=2.1u L=0.6u
* din_bar_gated_bar = inv(din_bar_gated)
M_7 din_bar_gated_bar din_bar_gated vdd vdd p W=2.1u L=0.6u
M_8 din_bar_gated_bar din_bar_gated gnd gnd n W=1.2u L=0.6u
**** 2input nand gate follwed by inverter to drive BR******
M_9 din_gated en vdd vdd p W='2.1*1u' L=0.6u
M_10 din_gated en net_8 gnd n W='2.1*1u' L=0.6u
M_11 net_8 din_bar gnd gnd n W='2.1*1u' L=0.6u
M_12 din_gated din_bar vdd vdd p W='2.1*1u' L=0.6u
M_13 net_6 din_gated vdd vdd p W='2.1*1u' L=0.6u
M_14 net_6 din_gated gnd gnd n W='1.2*1u' L=0.6u
* din_gated = nand(en, din_bar)
M_9 din_gated en vdd vdd p W=2.1u L=0.6u
M_10 din_gated en net_8 gnd n W=2.1u L=0.6u
M_11 net_8 din_bar gnd gnd n W=2.1u L=0.6u
M_12 din_gated din_bar vdd vdd p W=2.1u L=0.6u
* din_gated_bar = inv(din_gated)
M_13 din_gated_bar din_gated vdd vdd p W=2.1u L=0.6u
M_14 din_gated_bar din_gated gnd gnd n W=1.2u L=0.6u
************************************************
M_15 bl net_6 net_5 gnd n W='3.6*1u' L=0.6u
M_16 br net_1 net_5 gnd n W='3.6*1u' L=0.6u
M_17 net_5 en gnd gnd n W='3.6*1u' L=0.6u
* pull down with en enable
M_15 bl din_gated_bar net_5 gnd n W=3.6u L=0.6u
M_16 br din_bar_gated_bar net_5 gnd n W=3.6u L=0.6u
M_17 net_5 en gnd gnd n W=3.6u L=0.6u

View File

@ -1,23 +1,37 @@
*********************** Write_Driver ******************************
.SUBCKT write_driver din bl br en vdd gnd
* SPICE3 file created from write_driver.ext - technology: scmos
M1000 a_44_708# a_36_700# bl gnd n w=2.4u l=0.4u
M1001 br a_16_500# a_44_708# gnd n w=2.4u l=0.4u
M1002 a_44_708# en gnd gnd n w=2.4u l=0.4u
M1003 gnd a_8_284# a_16_500# gnd n w=0.8u l=0.4u
M1004 a_36_700# a_20_328# gnd gnd n w=0.8u l=0.4u
M1005 vdd a_8_284# a_16_500# vdd p w=1.4u l=0.4u
M1006 a_36_700# a_20_328# vdd vdd p w=1.4u l=0.4u
M1007 vdd en a_20_328# vdd p w=1.4u l=0.4u
M1008 a_20_328# a_64_360# vdd vdd p w=1.4u l=0.4u
M1009 a_48_328# en a_20_328# gnd n w=1.4u l=0.4u
M1010 gnd a_64_360# a_48_328# gnd n w=1.4u l=0.4u
M1011 a_40_228# en a_8_284# gnd n w=1.4u l=0.4u
M1012 gnd din a_40_228# gnd n w=1.4u l=0.4u
M1013 a_64_360# din gnd gnd n w=0.8u l=0.4u
M1014 a_8_284# en vdd vdd p w=1.4u l=0.4u
M1015 vdd din a_8_284# vdd p w=1.4u l=0.4u
M1016 a_64_360# din vdd vdd p w=1.4u l=0.4u
**** Inverter to conver Data_in to data_in_bar ******
* din_bar = inv(din)
M_1 din_bar din gnd gnd n W=0.8u L=0.4u
M_2 din_bar din vdd vdd p W=1.4u L=0.4u
.ENDS
**** 2input nand gate follwed by inverter to drive BL ******
* din_bar_gated = nand(en, din)
M_3 din_bar_gated en net_7 gnd n W=1.4u L=0.4u
M_4 net_7 din gnd gnd n W=1.4u L=0.4u
M_5 din_bar_gated en vdd vdd p W=1.4u L=0.4u
M_6 din_bar_gated din vdd vdd p W=1.4u L=0.4u
* din_bar_gated_bar = inv(din_bar_gated)
M_7 din_bar_gated_bar din_bar_gated vdd vdd p W=1.4u L=0.4u
M_8 din_bar_gated_bar din_bar_gated gnd gnd n W=0.8u L=0.4u
**** 2input nand gate follwed by inverter to drive BR******
* din_gated = nand(en, din_bar)
M_9 din_gated en vdd vdd p W=1.4u L=0.4u
M_10 din_gated en net_8 gnd n W=1.4u L=0.4u
M_11 net_8 din_bar gnd gnd n W=1.4u L=0.4u
M_12 din_gated din_bar vdd vdd p W=1.4u L=0.4u
* din_gated_bar = inv(din_gated)
M_13 din_gated_bar din_gated vdd vdd p W=1.4u L=0.4u
M_14 din_gated_bar din_gated gnd gnd n W=0.8u L=0.4u
************************************************
* pull down with en enable
M_15 bl din_gated_bar net_5 gnd n W=2.4u L=0.4u
M_16 br din_bar_gated_bar net_5 gnd n W=2.4u L=0.4u
M_17 net_5 en gnd gnd n W=2.4u L=0.4u
.ENDS $ write_driver

View File

@ -240,7 +240,7 @@ spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"
#spice stimulus related variables
spice["feasible_period"] = 5 # estimated feasible period in ns
spice["feasible_period"] = 10 # estimated feasible period in ns
spice["supply_voltages"] = [4.5, 5.0, 5.5] # Supply voltage corners in [Volts]
spice["nom_supply_voltage"] = 5.0 # Nominal supply voltage in [Volts]
spice["rise_time"] = 0.05 # rise time in [Nano-seconds]