mirror of https://github.com/VLSIDA/OpenRAM.git
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:
parent
1f81b24e96
commit
bab92fcf38
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
120
compiler/sram.py
120
compiler/sram.py
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue