mirror of
https://github.com/VLSIDA/OpenRAM.git
synced 2026-08-29 17:39:02 +02:00
Rework hierarchical decoder to not be folded. Remove address from central bank bus and access via side pins now. Eight way column mux now works.
This commit is contained in:
@@ -30,6 +30,8 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||
# These modules ensure unique names or have no changes if they
|
||||
# aren't unique
|
||||
ok_list = ['ms_flop.ms_flop',
|
||||
'dff.dff',
|
||||
'dff_buf.dff_buf',
|
||||
'bitcell.bitcell',
|
||||
'contact.contact',
|
||||
'ptx.ptx',
|
||||
|
||||
@@ -91,6 +91,8 @@ class spice(verilog.verilog):
|
||||
group of modules are generated."""
|
||||
|
||||
if (check and (len(self.insts[-1].mod.pins) != len(args))):
|
||||
debug.error("Connections: {}".format(self.insts[-1].mod.pins))
|
||||
debug.error("Connections: {}".format(args))
|
||||
debug.error("Number of net connections ({0}) does not match last instance ({1})".format(len(self.insts[-1].mod.pins),
|
||||
len(args)), 1)
|
||||
self.conns.append(args)
|
||||
|
||||
+48
-122
@@ -161,17 +161,15 @@ class bank(design.design):
|
||||
# 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
|
||||
self.num_addr_lines = self.num_col_addr_lines + self.row_addr_size
|
||||
else:
|
||||
self.num_col_addr_lines = 0
|
||||
self.num_addr_lines = self.row_addr_size
|
||||
|
||||
# M1/M2 routing pitch is based on contacted pitch
|
||||
self.m1_pitch = contact.m1m2.height + max(self.m1_space,self.m2_space)
|
||||
self.m2_pitch = contact.m2m3.height + max(self.m2_space,self.m3_space)
|
||||
|
||||
# The width of this bus is needed to place other modules (e.g. decoder)
|
||||
self.central_bus_width = self.m2_pitch * (self.num_control_lines + self.num_addr_lines + 1)
|
||||
self.central_bus_width = self.m2_pitch * self.num_control_lines
|
||||
|
||||
|
||||
|
||||
@@ -362,12 +360,10 @@ class bank(design.design):
|
||||
# The predecoder is below the x-axis and the main decoder is above the x-axis
|
||||
# The address flop and decoder are aligned in the x coord.
|
||||
|
||||
decoder_x_offset = self.row_decoder.width + self.central_bus_width
|
||||
offset = vector(decoder_x_offset,
|
||||
self.row_decoder.predecoder_height)
|
||||
x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
||||
self.row_decoder_inst=self.add_inst(name="row_decoder",
|
||||
mod=self.row_decoder,
|
||||
offset=offset.scale(-1,-1))
|
||||
offset=vector(x_offset,0))
|
||||
|
||||
temp = []
|
||||
for i in range(self.row_addr_size):
|
||||
@@ -381,12 +377,10 @@ class bank(design.design):
|
||||
""" Wordline Driver """
|
||||
|
||||
# The wordline driver is placed to the right of the main decoder width.
|
||||
# This means that it slightly overlaps with the hierarchical decoder,
|
||||
# but it shares power rails. This may differ for other decoders later...
|
||||
x_offset = self.row_decoder.width + self.central_bus_width - self.row_decoder.row_decoder_width
|
||||
x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch
|
||||
self.wordline_driver_inst=self.add_inst(name="wordline_driver",
|
||||
mod=self.wordline_driver,
|
||||
offset=vector(x_offset,0).scale(-1,-1))
|
||||
offset=vector(x_offset,0))
|
||||
|
||||
temp = []
|
||||
for i in range(self.num_rows):
|
||||
@@ -404,8 +398,8 @@ class bank(design.design):
|
||||
Create a 2:4 or 3:8 column address decoder.
|
||||
"""
|
||||
# Place the col decoder aligned left to row decoder
|
||||
x_off = -(self.central_bus_width + self.row_decoder.width)
|
||||
y_off = -(self.row_decoder.predecoder_height + self.col_decoder.height + 2*drc["well_to_well"])
|
||||
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
||||
y_off = -(self.col_decoder.height + 2*drc["well_to_well"])
|
||||
self.col_decoder_inst=self.add_inst(name="col_address_decoder",
|
||||
mod=self.col_decoder,
|
||||
offset=vector(x_off,y_off))
|
||||
@@ -445,10 +439,10 @@ class bank(design.design):
|
||||
if not self.num_banks > 1:
|
||||
return
|
||||
|
||||
xoffset = -(self.central_bus_width + self.bank_select.width)
|
||||
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
|
||||
# extra space to allow vias
|
||||
yoffset = self.min_point + 2*self.supply_rail_pitch + self.m1_space
|
||||
self.bank_select_pos = vector(xoffset,yoffset)
|
||||
y_off = self.min_point + 2*self.supply_rail_pitch + self.m1_space
|
||||
self.bank_select_pos = vector(x_off,y_off)
|
||||
self.bank_select_inst = self.add_inst(name="bank_select",
|
||||
mod=self.bank_select,
|
||||
offset=self.bank_select_pos)
|
||||
@@ -536,12 +530,11 @@ class bank(design.design):
|
||||
""" Create the address, supply, and control signal central bus lines. """
|
||||
|
||||
# Overall central bus width. It includes all the column mux lines,
|
||||
# control lines, and address flop to decoder lines.
|
||||
# and control lines.
|
||||
# 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_x_offset = -self.m2_pitch * (self.num_control_lines)
|
||||
address_bus_x_offset = control_bus_x_offset - self.m2_pitch*(self.num_addr_lines)
|
||||
|
||||
control_bus_x_offset = -self.m2_pitch * self.num_control_lines
|
||||
|
||||
# Track the bus offsets for other modules to access
|
||||
self.bus_xoffset = {}
|
||||
|
||||
@@ -556,35 +549,6 @@ class bank(design.design):
|
||||
width=self.m2_width,
|
||||
height=self.height)
|
||||
|
||||
# Row address lines (to left of col address lines)
|
||||
# goes from bottom of bitcell array down to the bottom of the column decoder/addresses
|
||||
for i in range(self.row_addr_size):
|
||||
addr_idx = i + self.col_addr_size
|
||||
x_offset = address_bus_x_offset + i*self.m2_pitch
|
||||
name = "A[{}]".format(addr_idx)
|
||||
# Make the xoffset map the center of the rail
|
||||
self.bus_xoffset[name]=x_offset + 0.5*self.m2_width
|
||||
# Add a label pin for LVS correspondence and visual help inspecting the rail.
|
||||
self.add_layout_pin(text=name,
|
||||
layer="metal2",
|
||||
offset=vector(x_offset, self.min_point),
|
||||
width=self.m2_width,
|
||||
height=-self.min_point)
|
||||
|
||||
# Column mux lines if there is column mux
|
||||
# goes from bottom of bitcell array down to the bottom of the column decoder/addresses
|
||||
if self.col_addr_size>0:
|
||||
for i in range(self.num_col_addr_lines):
|
||||
x_offset = address_bus_x_offset + (i+self.row_addr_size)*self.m2_pitch
|
||||
name = "sel[{}]".format(i) # One hot select signals
|
||||
# Make the xoffset map the center of the rail
|
||||
self.bus_xoffset[name]=x_offset + 0.5*self.m2_width
|
||||
# Add a label pin for LVS correspondence
|
||||
self.add_label_pin(text=name,
|
||||
layer="metal2",
|
||||
offset=vector(x_offset, self.col_decoder_inst.by()),
|
||||
width=self.m2_width,
|
||||
height=-self.col_decoder_inst.by())
|
||||
|
||||
|
||||
def route_precharge_to_bitcell_array(self):
|
||||
@@ -654,33 +618,14 @@ class bank(design.design):
|
||||
def route_row_decoder(self):
|
||||
""" Routes the row decoder inputs and supplies """
|
||||
|
||||
for i in range(self.row_addr_size):
|
||||
addr_idx = i + self.col_addr_size
|
||||
# before this index, we are using 2x4 decoders
|
||||
switchover_index = 2*self.row_decoder.no_of_pre2x4
|
||||
# so decide what modulus to perform the height spacing
|
||||
if i < switchover_index:
|
||||
position_heights = i % 2
|
||||
else:
|
||||
position_heights = (i-switchover_index) % 3
|
||||
|
||||
# Connect the address rails to the decoder
|
||||
# Note that the decoder inputs are long vertical rails so spread out the connections vertically.
|
||||
decoder_in_position = self.row_decoder_inst.get_pin("A[{}]".format(i)).lr() \
|
||||
+ vector(0,position_heights*self.bitcell.height+self.m2_pitch)
|
||||
rail_position = vector(self.bus_xoffset["A[{}]".format(addr_idx)],
|
||||
decoder_in_position.y)
|
||||
self.add_path("metal1",[decoder_in_position,rail_position])
|
||||
|
||||
decoder_in_via = decoder_in_position - vector(0,0.5*self.m2_width)
|
||||
self.add_via(layers=("metal1", "via1", "metal2"),
|
||||
offset=decoder_in_via,
|
||||
rotate=90)
|
||||
# # Create inputs for the row address lines
|
||||
# for i in range(self.row_addr_size):
|
||||
# addr_idx = i + self.col_addr_size
|
||||
# decoder_name = "A[{}]".format(i)
|
||||
# addr_name = "A[{}]".format(addr_idx)
|
||||
# self.copy_layout_pin(self.row_decoder_inst, decoder_name, addr_name)
|
||||
|
||||
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=rail_position,
|
||||
rotate=90)
|
||||
|
||||
# Route the power and ground, but only BELOW the y=0 since the
|
||||
# others are connected with the wordline driver.
|
||||
# These must be on M3 to not interfere with column mux address pins.
|
||||
@@ -730,70 +675,51 @@ class bank(design.design):
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def route_column_address_lines(self):
|
||||
""" Connecting the select lines of column mux to the address bus """
|
||||
if not self.col_addr_size>0:
|
||||
return
|
||||
|
||||
# Connect the select lines to the column mux
|
||||
# These must be in metal3 so that they don't overlap any gnd lines from decoders
|
||||
for i in range(self.num_col_addr_lines):
|
||||
name = "sel[{}]".format(i)
|
||||
mux_addr_pos = self.col_mux_array_inst.get_pin(name).lc()
|
||||
wire_pos = vector(self.bus_xoffset[name], mux_addr_pos.y)
|
||||
self.add_path("metal1", [wire_pos,mux_addr_pos])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=wire_pos,
|
||||
rotate=90)
|
||||
|
||||
|
||||
if self.col_addr_size == 1:
|
||||
|
||||
decode_out_pos = self.col_decoder_inst.get_pin("Zb").rc()
|
||||
selx_pos = vector(self.bus_xoffset["sel[0]"],decode_out_pos.y)
|
||||
self.add_path("metal1",[decode_out_pos, selx_pos])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=selx_pos,
|
||||
rotate=90)
|
||||
decode_out_pos = self.col_decoder_inst.get_pin("Z").rc()
|
||||
selx_pos = vector(self.bus_xoffset["sel[1]"],decode_out_pos.y)
|
||||
self.add_path("metal1",[decode_out_pos, selx_pos])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=selx_pos,
|
||||
rotate=90)
|
||||
|
||||
# Connect to sel[0] and sel[1]
|
||||
decode_names = ["Zb", "Z"]
|
||||
|
||||
# The Address LSB
|
||||
decode_in_pin = self.col_decoder_inst.get_pin("A")
|
||||
pin_pos = vector(decode_in_pin.cx(), self.min_point)
|
||||
self.add_layout_pin_center_segment(text="A[0]",
|
||||
layer="metal2",
|
||||
start=pin_pos,
|
||||
end=decode_in_pin.bc())
|
||||
self.copy_layout_pin(self.col_decoder_inst, "A", "A[0]")
|
||||
|
||||
elif self.col_addr_size > 1:
|
||||
# Route the col decoder outputs to the col select bus
|
||||
decode_names = []
|
||||
for i in range(self.num_col_addr_lines):
|
||||
name = "sel[{}]".format(i)
|
||||
decode_out_pos = self.col_decoder_inst.get_pin("out[{}]".format(i)).rc()
|
||||
selx_pos = vector(self.bus_xoffset[name],decode_out_pos.y)
|
||||
self.add_path("metal1",[decode_out_pos, selx_pos])
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=selx_pos,
|
||||
rotate=90)
|
||||
decode_names.append("out[{}]".format(i))
|
||||
|
||||
# Route from the col decoder up to the address bus
|
||||
for i in range(self.col_addr_size):
|
||||
decoder_name = "in[{}]".format(i)
|
||||
addr_name = "A[{}]".format(i)
|
||||
decode_in_pin = self.col_decoder_inst.get_pin(decoder_name)
|
||||
pin_pos = vector(decode_in_pin.cx(), self.min_point)
|
||||
self.add_layout_pin_center_segment(text=addr_name,
|
||||
layer="metal2",
|
||||
start=pin_pos,
|
||||
end=decode_in_pin.bc())
|
||||
|
||||
self.copy_layout_pin(self.col_decoder_inst, decoder_name, addr_name)
|
||||
|
||||
|
||||
# This will do a quick "river route" on two layers.
|
||||
# When above the top select line it will offset "inward" again to prevent conflicts.
|
||||
# This could be done on a single layer, but we follow preferred direction rules for later routing.
|
||||
top_y_offset = self.col_mux_array_inst.get_pin("sel[{}]".format(self.num_col_addr_lines-1)).cy()
|
||||
for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)):
|
||||
mux_name = "sel[{}]".format(i)
|
||||
mux_addr_pos = self.col_mux_array_inst.get_pin(mux_name).lc()
|
||||
|
||||
decode_out_pos = self.col_decoder_inst.get_pin(decode_name).center()
|
||||
|
||||
# To get to the edge of the decoder and one track out
|
||||
delta_offset = self.col_decoder_inst.rx() - decode_out_pos.x + self.m2_pitch
|
||||
if decode_out_pos.y > top_y_offset:
|
||||
mid1_pos = vector(decode_out_pos.x + delta_offset + i*self.m2_pitch,decode_out_pos.y)
|
||||
else:
|
||||
mid1_pos = vector(decode_out_pos.x + delta_offset + (self.num_col_addr_lines-i)*self.m2_pitch,decode_out_pos.y)
|
||||
mid2_pos = vector(mid1_pos.x,mux_addr_pos.y)
|
||||
self.add_wire(("metal1","via1","metal2"),[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
|
||||
|
||||
# route the gnd rails, add contact to rail as well
|
||||
for gnd_pin in self.col_decoder_inst.get_pins("gnd"):
|
||||
left_rail_pos = vector(self.left_gnd_x_center, gnd_pin.cy())
|
||||
|
||||
@@ -154,12 +154,11 @@ class hierarchical_decoder(design.design):
|
||||
else:
|
||||
nand_width = self.nand3.width
|
||||
self.routing_width = self.metal2_pitch*self.total_number_of_predecoder_outputs
|
||||
self.row_decoder_width = nand_width + self.routing_width + self.inv.width
|
||||
self.row_decoder_height = self.inv.height * self.rows
|
||||
|
||||
# Calculates height and width of hierarchical decoder
|
||||
self.height = self.row_decoder_height
|
||||
self.width = self.predecoder_width + self.row_decoder_width
|
||||
self.width = self.predecoder_width + self.routing_width + nand_width + self.inv.width
|
||||
|
||||
def create_pre_decoder(self):
|
||||
""" Creates pre-decoder and places labels input address [A] """
|
||||
@@ -479,7 +478,7 @@ class hierarchical_decoder(design.design):
|
||||
|
||||
def connect_rail_m3(self, rail_index, pin):
|
||||
""" Connect the routing rail to the given metal1 pin """
|
||||
mid_point = vector(pin.cx(), pin.cy()-self.inv.height/2)
|
||||
mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2)
|
||||
rail_pos = vector(self.rail_x_offsets[rail_index],mid_point.y)
|
||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||
offset=pin.center(),
|
||||
|
||||
@@ -119,7 +119,7 @@ class single_level_column_mux_array(design.design):
|
||||
def add_horizontal_input_rail(self):
|
||||
""" Create address input rails on M1 below the mux transistors """
|
||||
for j in range(self.words_per_row):
|
||||
offset = vector(0, self.route_height - (j+1)*self.m1_pitch)
|
||||
offset = vector(0, self.route_height + (j-self.words_per_row)*self.m1_pitch)
|
||||
self.add_layout_pin(text="sel[{}]".format(j),
|
||||
layer="metal1",
|
||||
offset=offset,
|
||||
|
||||
@@ -22,6 +22,7 @@ class wordline_driver(design.design):
|
||||
self.rows = rows
|
||||
self.add_pins()
|
||||
self.design_layout()
|
||||
self.offset_all_coordinates()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
|
||||
@@ -28,7 +28,7 @@ class pinvbuf(design.design):
|
||||
self.inv2 = pinv(size=inv2_size)
|
||||
self.add_mod(self.inv2)
|
||||
|
||||
self.width = self.inv1.width + self.inv2.width
|
||||
self.width = 2*self.inv1.width + self.inv2.width
|
||||
self.height = 2*self.inv1.height
|
||||
|
||||
self.create_layout()
|
||||
|
||||
+54
-66
@@ -20,9 +20,15 @@ class sram(design.design):
|
||||
|
||||
c = reload(__import__(OPTS.control_logic))
|
||||
self.mod_control_logic = getattr(c, OPTS.control_logic)
|
||||
|
||||
c = reload(__import__(OPTS.dff_array))
|
||||
self.mod_dff_array = getattr(c, OPTS.dff_array)
|
||||
|
||||
if num_banks>1:
|
||||
# Use a buffered array for big arrays
|
||||
# Also ensures we have Qbar when FF doesn't
|
||||
c = reload(__import__(OPTS.dff_buf_array))
|
||||
self.mod_dff_array = getattr(c, OPTS.dff_buf_array)
|
||||
else:
|
||||
c = reload(__import__(OPTS.dff_array))
|
||||
self.mod_dff_array = getattr(c, OPTS.dff_array)
|
||||
|
||||
c = reload(__import__(OPTS.bitcell))
|
||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
||||
@@ -200,6 +206,32 @@ class sram(design.design):
|
||||
self.width = self.bank_inst[1].ur().x
|
||||
self.height = self.control_logic_inst.uy()
|
||||
|
||||
|
||||
def add_single_bank_modules(self):
|
||||
"""
|
||||
This adds the moduels for a single bank SRAM with control
|
||||
logic.
|
||||
"""
|
||||
|
||||
# No orientation or offset
|
||||
self.bank_inst = self.add_bank(0, [0, 0], 1, 1)
|
||||
|
||||
# 3/5/18 MRG: Cannot reference positions inside submodules because boundaries
|
||||
# are not recomputed using instance placement. So, place the control logic such that it aligns
|
||||
# with the top of the SRAM.
|
||||
control_gap = 2*self.m3_width
|
||||
control_pos = vector(-self.control_logic.width-control_gap,
|
||||
self.bank.height-self.control_logic.height-3*self.supply_rail_width)
|
||||
self.add_control_logic(position=control_pos)
|
||||
|
||||
# Leave room for the control routes to the left of the flops
|
||||
addr_pos = vector(self.control_logic_inst.lx() + 4*self.m2_pitch,
|
||||
3*self.supply_rail_pitch)
|
||||
self.add_control_addr_dff(addr_pos)
|
||||
|
||||
self.width = self.bank.width + self.control_logic.height + control_gap
|
||||
self.height = self.bank.height
|
||||
|
||||
|
||||
def route_shared_banks(self):
|
||||
""" Route the shared signals for two and four bank configurations. """
|
||||
@@ -777,9 +809,10 @@ class sram(design.design):
|
||||
|
||||
def create_multi_bank_modules(self):
|
||||
""" Create the multibank address flops and bank decoder """
|
||||
self.msb_address = self.mod_ms_flop_array(name="msb_address",
|
||||
columns=self.num_banks/2,
|
||||
word_size=self.num_banks/2)
|
||||
|
||||
self.msb_address = self.mod_dff_array(name="msb_address",
|
||||
rows=1,
|
||||
columns=self.num_banks/2)
|
||||
self.add_mod(self.msb_address)
|
||||
|
||||
if self.num_banks>2:
|
||||
@@ -794,9 +827,9 @@ class sram(design.design):
|
||||
self.add_mod(self.control_logic)
|
||||
|
||||
# Create the address and control flops (but not the clk)
|
||||
dff_size = self.addr_size + len(self.control_logic.get_inputs())-1
|
||||
self.addr_ctrl_dff = self.mod_dff_array(name="dff_array", rows=dff_size, columns=1)
|
||||
self.add_mod(self.addr_ctrl_dff)
|
||||
dff_size = self.addr_size
|
||||
self.addr_dff = self.mod_dff_array(name="dff_array", rows=dff_size, columns=1)
|
||||
self.add_mod(self.addr_dff)
|
||||
|
||||
# Create the bank module (up to four are instantiated)
|
||||
self.bank = bank(word_size=self.word_size,
|
||||
@@ -902,9 +935,9 @@ class sram(design.design):
|
||||
|
||||
def add_control_addr_dff(self, position):
|
||||
""" Add and place address and control flops """
|
||||
self.addr_ctrl_dff_inst = self.add_inst(name="address",
|
||||
mod=self.addr_ctrl_dff,
|
||||
offset=position)
|
||||
self.addr_dff_inst = self.add_inst(name="address",
|
||||
mod=self.addr_dff,
|
||||
offset=position)
|
||||
# inputs, outputs/output/bar
|
||||
inputs = []
|
||||
outputs = []
|
||||
@@ -912,12 +945,6 @@ class sram(design.design):
|
||||
inputs.append("ADDR[{}]".format(i))
|
||||
outputs.append("A[{}]".format(i))
|
||||
|
||||
for i in self.control_logic_inputs:
|
||||
if i == "clk":
|
||||
continue
|
||||
inputs.append(i)
|
||||
outputs.append(i+"_s")
|
||||
|
||||
self.connect_inst(inputs + outputs + ["clk", "vdd", "gnd"])
|
||||
|
||||
def add_control_logic(self, position):
|
||||
@@ -951,31 +978,6 @@ class sram(design.design):
|
||||
layer="metal2",
|
||||
offset=self.vert_control_bus_positions[n])
|
||||
|
||||
def add_single_bank_modules(self):
|
||||
"""
|
||||
This adds the moduels for a single bank SRAM with control
|
||||
logic.
|
||||
"""
|
||||
|
||||
# No orientation or offset
|
||||
self.bank_inst = self.add_bank(0, [0, 0], 1, 1)
|
||||
|
||||
# 3/5/18 MRG: Cannot reference positions inside submodules because boundaries
|
||||
# are not recomputed using instance placement. So, place the control logic such that it aligns
|
||||
# with the top of the SRAM.
|
||||
control_gap = 2*self.m3_width
|
||||
control_pos = vector(-self.control_logic.width-control_gap,
|
||||
self.bank.height-self.control_logic.height-3*self.supply_rail_width)
|
||||
self.add_control_logic(position=control_pos)
|
||||
|
||||
# Leave room for the control routes to the left of the flops
|
||||
addr_pos = vector(self.control_logic_inst.lx() + 4*self.m2_pitch,
|
||||
3*self.supply_rail_pitch)
|
||||
self.add_control_addr_dff(addr_pos)
|
||||
|
||||
self.width = self.bank.width + self.control_logic.height + control_gap
|
||||
self.height = self.bank.height
|
||||
|
||||
def add_single_bank_pins(self):
|
||||
"""
|
||||
Add the top-level pins for a single bank SRAM with control.
|
||||
@@ -985,13 +987,9 @@ class sram(design.design):
|
||||
self.copy_layout_pin(self.bank_inst, "DATA[{}]".format(i))
|
||||
|
||||
for i in range(self.addr_size):
|
||||
self.copy_layout_pin(self.addr_ctrl_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i))
|
||||
self.copy_layout_pin(self.addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i))
|
||||
|
||||
ctrl_flops = ["din[{}]".format(i) for i in range(self.addr_size,self.addr_size+3)]
|
||||
for (old,new) in zip(ctrl_flops,["CSb","WEb","OEb"]):
|
||||
self.copy_layout_pin(self.addr_ctrl_dff_inst, old, new)
|
||||
|
||||
self.copy_layout_pin(self.addr_ctrl_dff_inst, "clk")
|
||||
self.copy_layout_pin(self.addr_dff_inst, "clk")
|
||||
|
||||
# Power ring contains the power pins
|
||||
|
||||
@@ -1068,7 +1066,7 @@ class sram(design.design):
|
||||
for i in range(self.addr_size):
|
||||
flop_name = "dout[{}]".format(i)
|
||||
bank_name = "A[{}]".format(i)
|
||||
flop_pin = self.addr_ctrl_dff_inst.get_pin(flop_name)
|
||||
flop_pin = self.addr_dff_inst.get_pin(flop_name)
|
||||
bank_pin = self.bank_inst.get_pin(bank_name)
|
||||
flop_pos = flop_pin.center()
|
||||
bank_pos = vector(bank_pin.cx(),flop_pos.y)
|
||||
@@ -1080,24 +1078,14 @@ class sram(design.design):
|
||||
offset=bank_pos,
|
||||
rotate=90)
|
||||
|
||||
# Connect the output of the flops to the control pins
|
||||
for i in range(3):
|
||||
flop_name = "dout[{}]".format(self.addr_size+i)
|
||||
ctrl_name = ["csb","web","oeb"][i]
|
||||
flop_pin = self.addr_ctrl_dff_inst.get_pin(flop_name)
|
||||
ctrl_pin = self.control_logic_inst.get_pin(ctrl_name)
|
||||
flop_pos = flop_pin.center()
|
||||
ctrl_pos = ctrl_pin.bc()
|
||||
mid_pos = vector(ctrl_pos.x, flop_pos.y)
|
||||
self.add_wire(("metal3","via2","metal2"),[flop_pos, mid_pos, ctrl_pos])
|
||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||
offset=flop_pos,
|
||||
rotate=90)
|
||||
# Connect the control pins as inputs
|
||||
for n in self.control_logic_inputs + ["clk"]:
|
||||
self.copy_layout_pin(self.control_logic_inst, n.lower(), n)
|
||||
|
||||
# Connect the clock between the flops and control module
|
||||
# FIXME: Buffered clock should drive the flops, but then
|
||||
# it would change the setup time...
|
||||
flop_pin = self.addr_ctrl_dff_inst.get_pin("clk")
|
||||
flop_pin = self.addr_dff_inst.get_pin("clk")
|
||||
ctrl_pin = self.control_logic_inst.get_pin("clk")
|
||||
flop_pos = flop_pin.uc()
|
||||
ctrl_pos = ctrl_pin.bc()
|
||||
@@ -1111,7 +1099,7 @@ class sram(design.design):
|
||||
""" Route vdd for the control and dff array """
|
||||
|
||||
# Route the vdd rails to the LEFT
|
||||
modules = [ self.control_logic_inst, self.addr_ctrl_dff_inst]
|
||||
modules = [ self.control_logic_inst, self.addr_dff_inst]
|
||||
for inst in modules:
|
||||
for vdd_pin in inst.get_pins("vdd"):
|
||||
if vdd_pin.layer != "metal1":
|
||||
@@ -1140,7 +1128,7 @@ class sram(design.design):
|
||||
""" Route gnd for the control and dff array """
|
||||
|
||||
# Route the gnd rails to the LEFT
|
||||
modules = [ self.control_logic_inst, self.addr_ctrl_dff_inst]
|
||||
modules = [ self.control_logic_inst, self.addr_dff_inst]
|
||||
for inst in modules:
|
||||
for gnd_pin in inst.get_pins("gnd"):
|
||||
if gnd_pin.layer != "metal1":
|
||||
|
||||
@@ -33,10 +33,9 @@ class multi_bank_test(openram_test):
|
||||
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=2, name="bank3")
|
||||
self.local_check(a)
|
||||
|
||||
# Eight way has a short circuit of one column mux select to gnd rail
|
||||
# debug.info(1, "Eight way column mux")
|
||||
# a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4")
|
||||
# self.local_check(a)
|
||||
debug.info(1, "Eight way column mux")
|
||||
a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4")
|
||||
self.local_check(a)
|
||||
|
||||
OPTS.check_lvsdrc = True
|
||||
globals.end_openram()
|
||||
|
||||
Reference in New Issue
Block a user