mirror of https://github.com/VLSIDA/OpenRAM.git
merged branch wtih dev
This commit is contained in:
commit
9501b99df7
14
.coveragerc
14
.coveragerc
|
|
@ -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
|
||||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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. """
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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())
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:]
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
Loading…
Reference in New Issue