modified template engine & sram multibank class

This commit is contained in:
Bugra Onal
2022-07-21 15:56:29 -07:00
parent a497943be3
commit 6d6063ef4e
11 changed files with 273 additions and 513 deletions
+7 -6
View File
@@ -36,14 +36,15 @@ class sram():
self.name = name
if self.num_banks == 1:
from .sram_1bank import sram_1bank as sram
elif self.num_banks == 2:
from .sram_2bank import sram_2bank as sram
else:
debug.error("Invalid number of banks.", -1)
from sram_1bank import sram_1bank as sram
self.s = sram(name, sram_config)
if self.num_banks != 1:
from sram_multibank import sram_multibank
mb = sram_multibank(s)
mb.verilog_write()
self.s.create_netlist()
if not OPTS.netlist_only:
self.s.create_layout()
-240
View File
@@ -1,240 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import sys
from tech import drc, spice
import debug
from math import log,sqrt,ceil
import datetime
import getpass
from base import vector
from globals import OPTS, print_time
from .sram_base import sram_base
from modules import bank
from modules import dff_buf_array
from modules import dff_array
class sram_2bank(sram_base):
"""
Procedures specific to a two 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 two bank SRAM """
# In 2 bank SRAM, the height is determined by the control bus which is higher than the msb address
self.vertical_bus_height = self.bank.height + 2*self.bank_to_bus_distance + self.data_bus_height + self.control_bus_height
# 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.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)
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 = self.control_logic_inst.uy()
def add_banks(self):
# Placement of bank 0 (left)
bank_position_0 = vector(self.bank.width,
self.bank.height)
self.bank_inst=[self.add_bank(0, bank_position_0, -1, -1)]
# Placement of bank 1 (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))
def add_logic(self):
""" Add the control and MSB logic """
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(self.addr_size-1)
self.connect_inst([self.msb_bank_sel_addr,"bank_sel[1]","bank_sel[0]","clk_buf", "vdd", "gnd"])
def route_shared_banks(self):
""" Route the shared signals for two and four bank configurations. """
# create the input control pins
for n in self.control_logic_inputs + ["clk"]:
self.copy_layout_pin(self.control_logic_inst, n)
# connect the control logic to the control bus
for n in self.control_logic_outputs + ["vdd", "gnd"]:
pins = self.control_logic_inst.get_pins(n)
for pin in pins:
if pin.layer=="m2":
pin_pos = pin.bc()
break
rail_pos = vector(pin_pos.x,self.horz_control_bus_positions[n].y)
self.add_path("m2",[pin_pos,rail_pos])
self.add_via_center(self.m1_stack,rail_pos)
# connect the control logic cross bar
for n in self.control_logic_outputs:
cross_pos = vector(self.vert_control_bus_positions[n].x,self.horz_control_bus_positions[n].y)
self.add_via_center(self.m1_stack,cross_pos)
# connect the bank select signals to the vertical bus
for i in range(self.num_banks):
pin = self.bank_inst[i].get_pin("bank_sel")
pin_pos = pin.rc() if i==0 else pin.lc()
rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,pin_pos.y)
self.add_path("m3",[pin_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
def route_single_msb_address(self):
""" Route one MSB address bit for 2-bank SRAM """
# connect the bank MSB flop supplies
vdd_pins = self.msb_address_inst.get_pins("vdd")
for vdd_pin in vdd_pins:
if vdd_pin.layer != "m1": 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("m1",[vdd_pos,down_pos])
self.add_via_center(self.m1_stack,down_pos,rotate=90)
self.add_path("m2",[down_pos,rail_pos])
self.add_via_center(self.m1_stack,rail_pos)
gnd_pins = self.msb_address_inst.get_pins("gnd")
# Only add the ground connection to the lowest metal2 rail in the flop array
# FIXME: SCMOS doesn't have a vertical rail in the cell, or we could use those
lowest_y = None
for gnd_pin in gnd_pins:
if gnd_pin.layer != "m2": continue
if lowest_y==None or gnd_pin.by()<lowest_y:
lowest_y=gnd_pin.by()
gnd_pos = gnd_pin.ur()
rail_pos = vector(gnd_pos.x,self.horz_control_bus_positions["gnd"].y)
self.add_path("m2",[gnd_pos,rail_pos])
self.add_via_center(self.m1_stack,rail_pos)
# connect the MSB flop to the address input bus
msb_pins = self.msb_address_inst.get_pins("din[0]")
for msb_pin in msb_pins:
if msb_pin.layer == "m3":
msb_pin_pos = msb_pin.lc()
break
rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr].x,msb_pin_pos.y)
self.add_path("m3",[msb_pin_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
# Connect the output bar to select 0
msb_out_pin = self.msb_address_inst.get_pin("dout_bar[0]")
msb_out_pos = msb_out_pin.rc()
out_extend_right_pos = msb_out_pos + vector(2*self.m2_pitch,0)
out_extend_up_pos = out_extend_right_pos + vector(0,self.m2_width)
rail_pos = vector(self.vert_control_bus_positions["bank_sel[0]"].x,out_extend_up_pos.y)
self.add_path("m2",[msb_out_pos,out_extend_right_pos,out_extend_up_pos])
self.add_wire(("m3","via2","m2"),[out_extend_right_pos,out_extend_up_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
# Connect the output to select 1
msb_out_pin = self.msb_address_inst.get_pin("dout[0]")
msb_out_pos = msb_out_pin.rc()
out_extend_right_pos = msb_out_pos + vector(2*self.m2_pitch,0)
out_extend_down_pos = out_extend_right_pos - vector(0,2*self.m1_pitch)
rail_pos = vector(self.vert_control_bus_positions["bank_sel[1]"].x,out_extend_down_pos.y)
self.add_path("m2",[msb_out_pos,out_extend_right_pos,out_extend_down_pos])
self.add_wire(("m3","via2","m2"),[out_extend_right_pos,out_extend_down_pos,rail_pos])
self.add_via_center(self.m2_stack,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("m1",[clk_pos,bend_pos,rail_pos])
def route(self):
""" Route all of the signals for the two bank SRAM. """
self.route_shared_banks()
# connect the horizontal control bus to the vertical bus
# 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).uc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("m2",[pin_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
self.route_single_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 == self.msb_bank_sel_addr: continue
pin0_pos = self.bank_inst[0].get_pin(n).rc()
pin1_pos = self.bank_inst[1].get_pin(n).lc()
rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y)
self.add_path("m3",[pin0_pos,pin1_pos])
self.add_via_center(self.m2_stack,rail_pos)
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="m2",
offset=self.vert_control_bus_positions[n])
for n in self.bank_sel_bus_names:
self.add_label(text=n,
layer="m2",
offset=self.vert_control_bus_positions[n])
+2 -2
View File
@@ -72,8 +72,8 @@ class sram_config:
bitcell = factory.create(module_type=OPTS.bitcell)
debug.check(self.num_banks in [1, 2, 4],
"Valid number of banks are 1 , 2 and 4.")
debug.check(ceil(log(self.num_banks, 2)) == log(self.num_banks, 2) ,
"Number of banks should be power of 2.")
self.num_words_per_bank = self.num_words / self.num_banks
self.num_bits_per_bank = self.word_size * self.num_words_per_bank
+27
View File
@@ -0,0 +1,27 @@
from template import template
from globals import OPTS
class sram_multibank:
def __init__(self, sram):
dict = {
'module_name': OPTS.output_name,
'bank_module_name': OPTS.output_name + '_1bank',
'vdd': 'vdd',
'gnd': 'gnd',
'ports': sram.all_ports,
'rw_ports': sram.readwrite_ports,
'r_ports': sram.read_ports,
'w_ports': sram.write_ports,
'banks': sram.banks,
'data_width': sram.word_size,
'addr_width': sram.addr_size,
'bank_sel': list(range(sram.num_banks)),
'num_wmask': sram.num_wmasks
}
def verilog_write():
t = template('../sram/sram_multibank_template.v', dict)
t.write(OPTS.output_name + '.v')
+146
View File
@@ -0,0 +1,146 @@
module {{ module_name }} (
`ifdef USE_POWER_PINS
{{ vdd }},
{{ gnd }},
`endif
{% for port in rw_ports %}
clk{{ port }},
addr{{ port }},
din{{ port }},
csb{{ port }},
wmask{{ port }},
web{{ port }},
dout{{ port }},
{% endfor %}
{% for port in r_ports %}
clk{{ port }},
addr{{ port }},
csb{{ port }},
dout{{ port }},
{% endfor %}
{% for port in w_ports %}
clk{{ port }},
addr{{ port }},
din{{ port }},
csb{{ port }},
wmask{{ port }},
web{{ port }},
{% endfor %}
);
parameter DATA_WIDTH = {{ data_width }};
parameter ADDR_WIDTH= {{ addr_width }};
parameter BANK_SEL = {{ bank_sel }};
parameter NUM_WMASK = {{ num_wmask }};
`ifdef USE_POWER_PINS
inout vdd;
inout gnd;
`endif
{% for port in rw_ports %}
input clk{{ port }};
input [ADDR_WIDTH - 1 : 0] addr{{ port }};
input [DATA_WIDTH - 1: 0] din{{ port }};
input csb{{ port }};
input web{{ port }};
input [NUM_WMASK - 1 : 0] wmask{{ port }};
output reg [DATA_WIDTH - 1 : 0] dout{{ port }};
{% endfor %}
{% for port in r_ports %}
input clk{{ port }};
input [ADDR_WIDTH - 1 : 0] addr{{ port }};
input csb{{ port }};
output reg [DATA_WIDTH - 1 : 0] dout{{ port }};
{% endfor %}
{% for port in w_ports %}
input clk{{ port }};
input [ADDR_WIDTH - 1 : 0] addr{{ port }};
input [DATA_WIDTH - 1: 0] din{{ port }};
input csb{{ port }};
input web{{ port }};
input [NUM_WMASK - 1 : 0] wmask{{ port }};
{% endfor %}
{% for port in ports %}
reg [ADDR_WIDTH - 1 : 0] addr{{ port }}_reg;
{% for bank in banks %}
wire [DATA_WIDTH - 1 : 0] dout{{ port }}_bank{{ bank }};
reg web{{ port }}_bank{{ bank }};
reg csb{{ port }}_bank{{ bank }};
{% endfor %}
{% endfor %}
{% for bank in banks %}
{{ bank_module_name }} bank{{ bank }} (
`ifdef USE_POWER_PINS
.vdd(vdd),
.gnd(gnd),
`endif
{% for port in rw_ports %}
.clk{{ port }}(clk{{ port }}),
.addr{{ port }}(addr{{ port }}[ADDR_WIDTH - BANK_SEL - 1 : 0]),
.din{{ port }}(din{{ port }}),
.csb{{ port }}(csb{{ port }}_bank{{ bank }}),
.web{{ port }}(web{{ port }}_bank{{ bank }}),
.wmask{{ port }}(wmask{{ port }}),
.dout{{ port }}(dout{{ port }}_bank{{ bank }}),
{% endfor %}
{% for port in r_ports %}
.clk{{ port }}(clk{{ port }}),
.addr{{ port }}(addr{{ port }}[ADDR_WIDTH - BANK_SEL - 1 : 0]),
.csb{{ port }}(csb{{ port }}_bank{{ bank }}),
.dout{{ port }}(dout{{ port }}_bank{{ bank }}),
{% endfor %}
{% for port in w_ports %}
.clk{{ port }}(clk{{ port }}),
.addr{{ port }}(addr{{ port }}[ADDR_WIDTH - BANK_SEL - 1 : 0]),
.din{{ port }}(din{{ port }}),
.csb{{ port }}(csb{{ port }}_bank{{ bank }}),
.web{{ port }}(web{{ port }}_bank{{ bank }}),
.wmask{{ port }}(wmask{{ port }}),
{% endfor %}
);
{% endfor %}
{% for port in ports %}
always @(posedge clk{{ port }}) begin
addr{{ port }}_reg <= addr{{ port }};
end
{% endfor %}
{% for port in ports %}
always @(*) begin
case (addr{{ port }}_reg[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL])
{% for bank in banks %}
{{ bank }}: begin
dout{{ port }} = dout{{ port }}_bank{{ bank }};
end
{% endfor %}
endcase
end
{% endfor %}
{% for port in ports %}
always @(*) begin
{% for bank in banks %}
csb{{ port }}_bank{{ bank }} = 1'b1;
web{{ port }}_bank{{ bank }} = 1'b1;
{% endfor %}
case (addr{{ port }}[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL])
{% for bank in banks %}
{{ bank }}: begin
web{{ port }}_bank{{ bank }} = web{{ port }};
csb{{ port }}_bank{{ bank }} = csb{{ port }};
end
{% endfor %}
endcase
end
{% endfor %}
endmodule