mirror of https://github.com/VLSIDA/OpenRAM.git
modified template engine & sram multibank class
This commit is contained in:
parent
a497943be3
commit
6d6063ef4e
|
|
@ -7,7 +7,7 @@
|
||||||
#
|
#
|
||||||
import math
|
import math
|
||||||
from tech import spice
|
from tech import spice
|
||||||
import verilog_template
|
from verilog_template import verilog_template
|
||||||
|
|
||||||
|
|
||||||
class verilog:
|
class verilog:
|
||||||
|
|
|
||||||
|
|
@ -36,14 +36,15 @@ class sram():
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
if self.num_banks == 1:
|
from sram_1bank import sram_1bank as sram
|
||||||
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)
|
|
||||||
|
|
||||||
self.s = sram(name, sram_config)
|
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()
|
self.s.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.s.create_layout()
|
self.s.create_layout()
|
||||||
|
|
|
||||||
|
|
@ -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])
|
|
||||||
|
|
@ -72,8 +72,8 @@ class sram_config:
|
||||||
|
|
||||||
bitcell = factory.create(module_type=OPTS.bitcell)
|
bitcell = factory.create(module_type=OPTS.bitcell)
|
||||||
|
|
||||||
debug.check(self.num_banks in [1, 2, 4],
|
debug.check(ceil(log(self.num_banks, 2)) == log(self.num_banks, 2) ,
|
||||||
"Valid number of banks are 1 , 2 and 4.")
|
"Number of banks should be power of 2.")
|
||||||
|
|
||||||
self.num_words_per_bank = self.num_words / self.num_banks
|
self.num_words_per_bank = self.num_words / self.num_banks
|
||||||
self.num_bits_per_bank = self.word_size * self.num_words_per_bank
|
self.num_bits_per_bank = self.word_size * self.num_words_per_bank
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
|
|
||||||
module multibank # (
|
|
||||||
DATA_WIDTH = 32,
|
|
||||||
ADDR_WIDTH= 8,
|
|
||||||
NUM_BANKS=2
|
|
||||||
)(
|
|
||||||
#<RW_PORTS
|
|
||||||
clk,
|
|
||||||
addr,
|
|
||||||
din,
|
|
||||||
csb,
|
|
||||||
web,
|
|
||||||
dout
|
|
||||||
#>RW_PORTS
|
|
||||||
#<R_PORTS
|
|
||||||
clk,
|
|
||||||
addr,
|
|
||||||
csb,
|
|
||||||
web,
|
|
||||||
dout
|
|
||||||
#>R_PORTS
|
|
||||||
);
|
|
||||||
|
|
||||||
parameter RAM_DEPTH = 1 << ADDR_WIDTH;
|
|
||||||
parameter BANK_SEL = (NUM_BANKS <= 2)? 1 :
|
|
||||||
(NUM_BANKS <= 4)? 2 :
|
|
||||||
(NUM_BANKS <= 8)? 3 :
|
|
||||||
(NUM_BANKS <= 16)? 4 : 5;
|
|
||||||
|
|
||||||
input clk;
|
|
||||||
input [ADDR_WIDTH -1 : 0] addr;
|
|
||||||
input [DATA_WIDTH - 1: 0] din;
|
|
||||||
input csb;
|
|
||||||
input web;
|
|
||||||
output reg [DATA_WIDTH - 1 : 0] dout;
|
|
||||||
|
|
||||||
#!PORT_NUM!0#
|
|
||||||
#<BANK_DEFS
|
|
||||||
reg csb#$PORT_NUM$#;
|
|
||||||
reg web#$PORT_NUM$#;
|
|
||||||
reg [DATA_WIDTH - 1 : 0] dout#$PORT_NUM$#;
|
|
||||||
#!PORT_NUM!PORT_NUM+1#
|
|
||||||
#>BANK_DEFS
|
|
||||||
|
|
||||||
#!PORT_NUM!0#
|
|
||||||
#<BANK_INIT
|
|
||||||
bank #(DATA_WIDTH, ADDR_WIDTH) bank#$PORT_NUM$# (
|
|
||||||
#<BANK_RW_PORTS
|
|
||||||
.clk(clk),
|
|
||||||
.addr(addr[ADDR_WIDTH - BANK_SEL - 1 : 0]),
|
|
||||||
.din(din),
|
|
||||||
.csb(csb#$PORT_NUM$#),
|
|
||||||
.web(web#$PORT_NUM$#),
|
|
||||||
.dout(dout#$PORT_NUM$#)
|
|
||||||
#!PORT_NUM!PORT_NUM+1#
|
|
||||||
#>BANK_RW_PORTS
|
|
||||||
);
|
|
||||||
#>BANK_INIT
|
|
||||||
|
|
||||||
always @(posedge clk) begin
|
|
||||||
case (addr[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL])
|
|
||||||
#!PORT_NUM!0#
|
|
||||||
#<BANK_CASE
|
|
||||||
#$PORT_NUM$#: begin
|
|
||||||
dout <= dout#$PORT_NUM$#;
|
|
||||||
web#$PORT_NUM$# <= web;
|
|
||||||
end
|
|
||||||
#!PORT_NUM!PORT_NUM+1#
|
|
||||||
#>BANK_CASE
|
|
||||||
endcase
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
import re
|
||||||
|
|
||||||
|
class baseSection:
|
||||||
|
children = []
|
||||||
|
|
||||||
|
def expand(self, dict, fd):
|
||||||
|
for c in self.children:
|
||||||
|
c.expand(dict, fd)
|
||||||
|
|
||||||
|
class loopSection(baseSection):
|
||||||
|
|
||||||
|
def __init__(self, var, key):
|
||||||
|
self.children = []
|
||||||
|
self.var = var
|
||||||
|
self.key = key
|
||||||
|
|
||||||
|
def expand(self, dict, fd):
|
||||||
|
for ind in dict[self.key]:
|
||||||
|
dict[self.var] = ind
|
||||||
|
for c in self.children:
|
||||||
|
c.expand(dict, fd)
|
||||||
|
if self.var in dict:
|
||||||
|
del dict[self.var]
|
||||||
|
|
||||||
|
class textSection(baseSection):
|
||||||
|
|
||||||
|
def __init__(self, text):
|
||||||
|
self.text = text
|
||||||
|
|
||||||
|
def expand(self, dict):
|
||||||
|
var_re = re.compile('\{\{ (\S*) \}\}')
|
||||||
|
vars = var_re.finditer(self.text)
|
||||||
|
newText = self.text
|
||||||
|
for var in vars:
|
||||||
|
newText = newText.replace('{{ ' + var.group(1) + ' }}', str(dict[var.group(1)]))
|
||||||
|
print(newText, end='', file=fd)
|
||||||
|
|
||||||
|
class template:
|
||||||
|
|
||||||
|
def __init__(self, template, dict):
|
||||||
|
self.template = template
|
||||||
|
self.dict = dict
|
||||||
|
|
||||||
|
def readTemplate(self):
|
||||||
|
lines = []
|
||||||
|
with open(self.template, 'r') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
self.baseSectionSection = baseSection()
|
||||||
|
sections = []
|
||||||
|
context = [self.baseSectionSection]
|
||||||
|
for_re = re.compile('\{% for (\S*) in (\S*) %\}')
|
||||||
|
end_re = re.compile('\{% endfor %\}')
|
||||||
|
for line in lines:
|
||||||
|
m = for_re.match(line)
|
||||||
|
if m:
|
||||||
|
section = loopSection(m.group(1), m.group(2))
|
||||||
|
sections.append(section)
|
||||||
|
context[-1].children.append(section)
|
||||||
|
context.append(section)
|
||||||
|
continue
|
||||||
|
if end_re.match(line):
|
||||||
|
context.pop()
|
||||||
|
else:
|
||||||
|
context[-1].children.append(textSection(line))
|
||||||
|
|
||||||
|
def write(self, filename):
|
||||||
|
fd = open(filename, 'w')
|
||||||
|
self.readTemplate()
|
||||||
|
self.baseSectionSection.expand(self.dict, fd)
|
||||||
|
fd.close()
|
||||||
|
|
||||||
|
|
@ -1,13 +1,20 @@
|
||||||
from verilog_template import verilog_template
|
from template import template
|
||||||
|
|
||||||
t = verilog_template('../sram/multibank_template.v')
|
dict = {
|
||||||
t.readTemplate()
|
'module_name': 'sram_1kbyte_32b_2bank',
|
||||||
t.setSectionRepeat('RW_PORTS', 1)
|
'bank_module_name': 'sram_1kbyte_32b_2bank_1bank',
|
||||||
t.setSectionRepeat('R_PORTS', 0)
|
'vdd': 'vdd',
|
||||||
t.setSectionRepeat('BANK_DEFS', 2)
|
'gnd': 'gnd',
|
||||||
t.setSectionRepeat('BANK_INIT', 2)
|
'ports': [0, 1],
|
||||||
t.setSectionRepeat('BANK_CASE', 2)
|
'rw_ports': [0],
|
||||||
t.setTextDict('PORT_NUM', 0)
|
'r_ports': [1],
|
||||||
|
'w_ports': [],
|
||||||
|
'banks': [0, 1],
|
||||||
|
'data_width': 32,
|
||||||
|
'addr_width': 8,
|
||||||
|
'bank_sel': 1,
|
||||||
|
'num_wmask': 4
|
||||||
|
}
|
||||||
|
t = template('../sram/sram_multibank_template.v', dict)
|
||||||
|
t.write(dict['module_name'] + '.v')
|
||||||
|
|
||||||
|
|
||||||
t.generate('test.v')
|
|
||||||
|
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
|
|
||||||
module multibank # (
|
|
||||||
DATA_WIDTH = 32,
|
|
||||||
ADDR_WIDTH= 8,
|
|
||||||
NUM_BANKS=2
|
|
||||||
)(
|
|
||||||
clk,
|
|
||||||
addr,
|
|
||||||
din,
|
|
||||||
csb,
|
|
||||||
web,
|
|
||||||
dout
|
|
||||||
);
|
|
||||||
|
|
||||||
parameter RAM_DEPTH = 1 << ADDR_WIDTH;
|
|
||||||
parameter BANK_SEL = (NUM_BANKS <= 2)? 1 :
|
|
||||||
(NUM_BANKS <= 4)? 2 :
|
|
||||||
(NUM_BANKS <= 8)? 3 :
|
|
||||||
(NUM_BANKS <= 16)? 4 : 5;
|
|
||||||
|
|
||||||
input clk;
|
|
||||||
input [ADDR_WIDTH -1 : 0] addr;
|
|
||||||
input [DATA_WIDTH - 1: 0] din;
|
|
||||||
input csb;
|
|
||||||
input web;
|
|
||||||
output reg [DATA_WIDTH - 1 : 0] dout;
|
|
||||||
|
|
||||||
reg csb0;
|
|
||||||
reg web0;
|
|
||||||
reg [DATA_WIDTH - 1 : 0] dout0;
|
|
||||||
reg csb1;
|
|
||||||
reg web1;
|
|
||||||
reg [DATA_WIDTH - 1 : 0] dout1;
|
|
||||||
|
|
||||||
bank #(DATA_WIDTH, ADDR_WIDTH) bank0 (
|
|
||||||
.clk(clk),
|
|
||||||
.addr(addr[ADDR_WIDTH - BANK_SEL - 1 : 0]),
|
|
||||||
.din(din),
|
|
||||||
.csb(csb0),
|
|
||||||
.web(web0),
|
|
||||||
.dout(dout0)
|
|
||||||
);
|
|
||||||
bank #(DATA_WIDTH, ADDR_WIDTH) bank1 (
|
|
||||||
.clk(clk),
|
|
||||||
.addr(addr[ADDR_WIDTH - BANK_SEL - 1 : 0]),
|
|
||||||
.din(din),
|
|
||||||
.csb(csb1),
|
|
||||||
.web(web1),
|
|
||||||
.dout(dout1)
|
|
||||||
);
|
|
||||||
|
|
||||||
always @(posedge clk) begin
|
|
||||||
case (addr[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL])
|
|
||||||
0: begin
|
|
||||||
dout <= dout0;
|
|
||||||
web0 <= web;
|
|
||||||
end
|
|
||||||
1: begin
|
|
||||||
dout <= dout1;
|
|
||||||
web1 <= web;
|
|
||||||
end
|
|
||||||
endcase
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
|
|
||||||
class text_section:
|
|
||||||
|
|
||||||
def __init__(self, name, parent):
|
|
||||||
self.name = name
|
|
||||||
self.parent = parent
|
|
||||||
self.lines = []
|
|
||||||
self.sections = []
|
|
||||||
self.sectionPos = []
|
|
||||||
self.lineNum = 0
|
|
||||||
self.repeat = 1
|
|
||||||
self.index = -1
|
|
||||||
|
|
||||||
def addLine(self, line):
|
|
||||||
self.lines.append(line)
|
|
||||||
self.lineNum+= 1
|
|
||||||
|
|
||||||
def addSection(self, section):
|
|
||||||
self.sections.append(section)
|
|
||||||
self.sectionPos.append(self.lineNum)
|
|
||||||
section.index = len(self.sections) - 1
|
|
||||||
|
|
||||||
def clone(self, cloneName):
|
|
||||||
other = text_section(cloneName, self.parent)
|
|
||||||
other.sections = self.sections.copy()
|
|
||||||
other.sectionPos = self.sectionPos.copy()
|
|
||||||
other.repeat = self.repeat
|
|
||||||
self.parent.sections.insert(self.index + 1, other)
|
|
||||||
|
|
||||||
def expand(self):
|
|
||||||
expanded = []
|
|
||||||
pos = 0
|
|
||||||
if self.repeat == 0:
|
|
||||||
return []
|
|
||||||
if len(self.sections) == 0:
|
|
||||||
return self.lines * self.repeat
|
|
||||||
|
|
||||||
for s, sPos in zip(self.sections, self.sectionPos):
|
|
||||||
if pos < sPos:
|
|
||||||
expanded += self.lines[pos:sPos]
|
|
||||||
pos = sPos
|
|
||||||
expanded += s.expand()
|
|
||||||
|
|
||||||
if pos < self.lineNum:
|
|
||||||
expanded += self.lines[pos:]
|
|
||||||
|
|
||||||
if self.repeat > 1:
|
|
||||||
expanded = expanded * self.repeat
|
|
||||||
|
|
||||||
return expanded
|
|
||||||
|
|
||||||
|
|
||||||
class verilog_template:
|
|
||||||
|
|
||||||
def __init__(self, template):
|
|
||||||
self.template = template
|
|
||||||
self.sections = {}
|
|
||||||
self.textDict = {}
|
|
||||||
self.baseSection = None
|
|
||||||
self.expanded = None
|
|
||||||
|
|
||||||
def readTemplate(self):
|
|
||||||
lines = []
|
|
||||||
with open(self.template, 'r') as f:
|
|
||||||
lines = f.readlines()
|
|
||||||
self.baseSection = text_section('base', None)
|
|
||||||
currentSection = self.baseSection
|
|
||||||
for line in lines:
|
|
||||||
if line[:2] == '#<':
|
|
||||||
section = text_section(line[2:].strip('\n'), currentSection)
|
|
||||||
currentSection.addSection(section)
|
|
||||||
currentSection = section
|
|
||||||
elif line[:2] == '#>' and line[2:].strip('\n') == currentSection.name:
|
|
||||||
self.sections[currentSection.name] = currentSection
|
|
||||||
currentSection = currentSection.parent
|
|
||||||
else:
|
|
||||||
currentSection.addLine(line)
|
|
||||||
|
|
||||||
def expand(self):
|
|
||||||
self.expanded = self.baseSection.expand()
|
|
||||||
|
|
||||||
def postProcess(self):
|
|
||||||
text = ""
|
|
||||||
for line in self.expanded:
|
|
||||||
if '#$' in line:
|
|
||||||
while True:
|
|
||||||
indStart = line.find('#$')
|
|
||||||
if indStart == -1:
|
|
||||||
break
|
|
||||||
indEnd = line.find('$#')
|
|
||||||
line = line[:indStart] + str(self.textDict[line[indStart + 2:indEnd]]) + line[indEnd + 2:]
|
|
||||||
text += line
|
|
||||||
elif '#!' in line:
|
|
||||||
indLabelStart = line.find('#!') + 2
|
|
||||||
indLabelEnd = line.find('!', indLabelStart)
|
|
||||||
label = line[indLabelStart:indLabelEnd]
|
|
||||||
self.textDict[label] = eval(line[indLabelEnd + 1:-1], self.textDict)
|
|
||||||
else:
|
|
||||||
text += line
|
|
||||||
return text
|
|
||||||
|
|
||||||
def generate(self, filename):
|
|
||||||
self.expand()
|
|
||||||
text = self.postProcess()
|
|
||||||
with open(filename, 'w') as f:
|
|
||||||
f.write(text)
|
|
||||||
|
|
||||||
def setSectionRepeat(self, name, repeat):
|
|
||||||
self.sections[name].repeat = repeat
|
|
||||||
|
|
||||||
def setTextDict(self, label, value):
|
|
||||||
self.textDict[label] = value
|
|
||||||
|
|
||||||
def cloneSection(self, name, cloneName):
|
|
||||||
self.sections[name].clone(cloneName)
|
|
||||||
Loading…
Reference in New Issue