modified template engine & sram multibank class

This commit is contained in:
Bugra Onal 2022-06-09 21:40:19 -07:00
parent a497943be3
commit 6d6063ef4e
11 changed files with 273 additions and 513 deletions

View File

@ -7,7 +7,7 @@
#
import math
from tech import spice
import verilog_template
from verilog_template import verilog_template
class verilog:

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()

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])

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

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')

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

View File

@ -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

View File

@ -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()

View File

@ -1,13 +1,20 @@
from verilog_template import verilog_template
from template import template
t = verilog_template('../sram/multibank_template.v')
t.readTemplate()
t.setSectionRepeat('RW_PORTS', 1)
t.setSectionRepeat('R_PORTS', 0)
t.setSectionRepeat('BANK_DEFS', 2)
t.setSectionRepeat('BANK_INIT', 2)
t.setSectionRepeat('BANK_CASE', 2)
t.setTextDict('PORT_NUM', 0)
dict = {
'module_name': 'sram_1kbyte_32b_2bank',
'bank_module_name': 'sram_1kbyte_32b_2bank_1bank',
'vdd': 'vdd',
'gnd': 'gnd',
'ports': [0, 1],
'rw_ports': [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')

View File

@ -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

View File

@ -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)