From 500a872c10b0788caf824d88074d06c32786402b Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Wed, 12 Jan 2022 11:59:44 -0800 Subject: [PATCH 01/13] Templatable verilog file --- compiler/sram/multibank_template.v | 36 +++++++++++++++++++++++ compiler/sram/verilog_template.py | 47 ++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 compiler/sram/multibank_template.v create mode 100644 compiler/sram/verilog_template.py diff --git a/compiler/sram/multibank_template.v b/compiler/sram/multibank_template.v new file mode 100644 index 00000000..2c6d559e --- /dev/null +++ b/compiler/sram/multibank_template.v @@ -0,0 +1,36 @@ + +module multibank # ( + DATA_WIDTH = 32, + ADDR_WIDTH= 8 +)( +#RW_PORTS +#R_PORTS + ); + + parameter RAM_DEPTH = 1 << ADRR_WIDTH; + +#BANK_R_PORTS + ) +#>BANK_INIT diff --git a/compiler/sram/verilog_template.py b/compiler/sram/verilog_template.py new file mode 100644 index 00000000..2fc0656f --- /dev/null +++ b/compiler/sram/verilog_template.py @@ -0,0 +1,47 @@ + + +class TextSection: + + def __init__(self, name, parent): + self.name = name + self.parent = parent + self.lines = [] + self.sections = [] + self.sectionPos = [] + self.lineNum = 0 + self.repeat = 0 + + def addLine(self, line): + self.lines.append(line) + self.lineNum+= 1 + + def addSection(self, section): + self.sections.append(section) + self.sectionPos.append(self.lineNum) + + def expand(self): + for i + +class VerilogTemplate: + + def __init__(self, template, output); + self.template = template + self.output = output + self.sections = [] + + def readTemplate(self): + lines = [] + with open(self.template, 'r') as f: + lines = f.readlines() + currentSection = TextSection('base', None) + for line in lines: + if line[:2] == '#<': + section = TextSection(line[2:], currentSection) + currentSection.addSection(section) + currentSection = section + if line[:2] == '#>' and line[2:] == section.name: + currentSection = currentSection.parent + else: + currentSection.addLine(line) + + From f895937f02296e50d06d506b1b46d86f878648bc Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Wed, 26 Jan 2022 07:47:37 -0800 Subject: [PATCH 02/13] Bank select --- compiler/sram/multibank_template.v | 69 +++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 20 deletions(-) diff --git a/compiler/sram/multibank_template.v b/compiler/sram/multibank_template.v index 2c6d559e..e00ea4c3 100644 --- a/compiler/sram/multibank_template.v +++ b/compiler/sram/multibank_template.v @@ -1,36 +1,65 @@ module multibank # ( DATA_WIDTH = 32, - ADDR_WIDTH= 8 + ADDR_WIDTH= 8, + NUM_BANKS=2 )( #RW_PORTS #R_PORTS ); parameter RAM_DEPTH = 1 << ADRR_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] data; + +#BANK_DEFS + #BANK_R_PORTS + .clk(clk), + .addr(addr), + .din(din), + .csb(csb#$PORT_NUM$#), + .web(web#$PORT_NUM$#), + .dout(dout#$PORT_NUM$#), +#>BANK_RW_PORTS ) #>BANK_INIT + +always @(posedge clk) begin + case (addr[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL]) +#BANK_CASE + endcase +end From 07f84cb69f7a13fc2b7aa7c66bacc3f179612773 Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Wed, 26 Jan 2022 10:52:10 -0800 Subject: [PATCH 03/13] Template module done --- compiler/sram/multibank_template.v | 26 +++-- compiler/sram/verilog_template.py | 47 --------- compiler/verilogTemplate/test.py | 13 +++ compiler/verilogTemplate/test.v | 65 ++++++++++++ compiler/verilogTemplate/verilog_template.py | 103 +++++++++++++++++++ 5 files changed, 198 insertions(+), 56 deletions(-) delete mode 100644 compiler/sram/verilog_template.py create mode 100644 compiler/verilogTemplate/test.py create mode 100644 compiler/verilogTemplate/test.v create mode 100644 compiler/verilogTemplate/verilog_template.py diff --git a/compiler/sram/multibank_template.v b/compiler/sram/multibank_template.v index e00ea4c3..d5724cb6 100644 --- a/compiler/sram/multibank_template.v +++ b/compiler/sram/multibank_template.v @@ -10,18 +10,18 @@ module multibank # ( din, csb, web, - dout, + dout #>RW_PORTS #R_PORTS ); - parameter RAM_DEPTH = 1 << ADRR_WIDTH; + parameter RAM_DEPTH = 1 << ADDR_WIDTH; parameter BANK_SEL = (NUM_BANKS <= 2)? 1 : (NUM_BANKS <= 4)? 2 : (NUM_BANKS <= 8)? 3 : @@ -32,34 +32,42 @@ module multibank # ( input [DATA_WIDTH - 1: 0] din; input csb; input web; - output reg [DATA_WIDTH - 1 : 0] data; + output reg [DATA_WIDTH - 1 : 0] dout; +#!PORT_NUM!0# #BANK_DEFS +#!PORT_NUM!0# #BANK_RW_PORTS - ) + ); #>BANK_INIT always @(posedge clk) begin case (addr[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL]) +#!PORT_NUM!0# #BANK_CASE endcase end + +endmodule diff --git a/compiler/sram/verilog_template.py b/compiler/sram/verilog_template.py deleted file mode 100644 index 2fc0656f..00000000 --- a/compiler/sram/verilog_template.py +++ /dev/null @@ -1,47 +0,0 @@ - - -class TextSection: - - def __init__(self, name, parent): - self.name = name - self.parent = parent - self.lines = [] - self.sections = [] - self.sectionPos = [] - self.lineNum = 0 - self.repeat = 0 - - def addLine(self, line): - self.lines.append(line) - self.lineNum+= 1 - - def addSection(self, section): - self.sections.append(section) - self.sectionPos.append(self.lineNum) - - def expand(self): - for i - -class VerilogTemplate: - - def __init__(self, template, output); - self.template = template - self.output = output - self.sections = [] - - def readTemplate(self): - lines = [] - with open(self.template, 'r') as f: - lines = f.readlines() - currentSection = TextSection('base', None) - for line in lines: - if line[:2] == '#<': - section = TextSection(line[2:], currentSection) - currentSection.addSection(section) - currentSection = section - if line[:2] == '#>' and line[2:] == section.name: - currentSection = currentSection.parent - else: - currentSection.addLine(line) - - diff --git a/compiler/verilogTemplate/test.py b/compiler/verilogTemplate/test.py new file mode 100644 index 00000000..73033914 --- /dev/null +++ b/compiler/verilogTemplate/test.py @@ -0,0 +1,13 @@ +from verilog_template import verilog_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) + + +t.generate('test.v') diff --git a/compiler/verilogTemplate/test.v b/compiler/verilogTemplate/test.v new file mode 100644 index 00000000..390f9f47 --- /dev/null +++ b/compiler/verilogTemplate/test.v @@ -0,0 +1,65 @@ + +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 diff --git a/compiler/verilogTemplate/verilog_template.py b/compiler/verilogTemplate/verilog_template.py new file mode 100644 index 00000000..b5267d00 --- /dev/null +++ b/compiler/verilogTemplate/verilog_template.py @@ -0,0 +1,103 @@ + +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 + + def addLine(self, line): + self.lines.append(line) + self.lineNum+= 1 + + def addSection(self, section): + self.sections.append(section) + self.sectionPos.append(self.lineNum) + + 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 From 4d38e7df3cf966ca19150c13260403b760886b81 Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Wed, 2 Feb 2022 12:24:44 -0800 Subject: [PATCH 04/13] Base verilog template init --- compiler/base/verilog.v | 78 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 compiler/base/verilog.v diff --git a/compiler/base/verilog.v b/compiler/base/verilog.v new file mode 100644 index 00000000..f782c9d7 --- /dev/null +++ b/compiler/base/verilog.v @@ -0,0 +1,78 @@ +// OpenRAM SRAM model +// Words: #$WORDS$# +// Word size: #$WORD_SIZE$# +#WRITE_SIZE_CMT + +module #$MODULE_NAME$# ( +`ifdef USE_POWER_PINS + #$VDD$#, + #$GND$#, +`endif +#RW_PORT + ); + + parameter DATA_WIDTH = 2 ; + parameter ADDR_WIDTH = 4 ; + parameter RAM_DEPTH = 1 << ADDR_WIDTH; + // FIXME: This delay is arbitrary. + parameter DELAY = 3 ; + parameter VERBOSE = 1 ; //Set to 0 to only display warnings + parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary + +`ifdef USE_POWER_PINS + inout vdd; + inout gnd; +`endif + input clk0; // clock + input csb0; // active low chip select + input web0; // active low write control + input [ADDR_WIDTH-1:0] addr0; + input [DATA_WIDTH-1:0] din0; + output [DATA_WIDTH-1:0] dout0; + + reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; + + reg csb0_reg; + reg web0_reg; + reg [ADDR_WIDTH-1:0] addr0_reg; + reg [DATA_WIDTH-1:0] din0_reg; + reg [DATA_WIDTH-1:0] dout0; + + // All inputs are registers + always @(posedge clk0) + begin + csb0_reg = csb0; + web0_reg = web0; + addr0_reg = addr0; + din0_reg = din0; + #(T_HOLD) dout0 = 2'bx; + if ( !csb0_reg && web0_reg && VERBOSE ) + $display($time," Reading %m addr0=%b dout0=%b",addr0_reg,mem[addr0_reg]); + if ( !csb0_reg && !web0_reg && VERBOSE ) + $display($time," Writing %m addr0=%b din0=%b",addr0_reg,din0_reg); + end + + + // Memory Write Block Port 0 + // Write Operation : When web0 = 0, csb0 = 0 + always @ (negedge clk0) + begin : MEM_WRITE0 + if ( !csb0_reg && !web0_reg ) begin + mem[addr0_reg][1:0] = din0_reg[1:0]; + end + end + + // Memory Read Block Port 0 + // Read Operation : When web0 = 1, csb0 = 0 + always @ (negedge clk0) + begin : MEM_READ0 + if (!csb0_reg && web0_reg) + dout0 <= #(DELAY) mem[addr0_reg]; + end + +e From 6dc42a7bbbb4b05351ebe5ba1a99e72fdb79c4f9 Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Mon, 7 Feb 2022 11:23:43 -0800 Subject: [PATCH 05/13] Verilog Template additions --- compiler/base/verilog.v | 78 ------------------- compiler/base/verilog_template.v | 126 +++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 78 deletions(-) delete mode 100644 compiler/base/verilog.v create mode 100644 compiler/base/verilog_template.v diff --git a/compiler/base/verilog.v b/compiler/base/verilog.v deleted file mode 100644 index f782c9d7..00000000 --- a/compiler/base/verilog.v +++ /dev/null @@ -1,78 +0,0 @@ -// OpenRAM SRAM model -// Words: #$WORDS$# -// Word size: #$WORD_SIZE$# -#WRITE_SIZE_CMT - -module #$MODULE_NAME$# ( -`ifdef USE_POWER_PINS - #$VDD$#, - #$GND$#, -`endif -#RW_PORT - ); - - parameter DATA_WIDTH = 2 ; - parameter ADDR_WIDTH = 4 ; - parameter RAM_DEPTH = 1 << ADDR_WIDTH; - // FIXME: This delay is arbitrary. - parameter DELAY = 3 ; - parameter VERBOSE = 1 ; //Set to 0 to only display warnings - parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary - -`ifdef USE_POWER_PINS - inout vdd; - inout gnd; -`endif - input clk0; // clock - input csb0; // active low chip select - input web0; // active low write control - input [ADDR_WIDTH-1:0] addr0; - input [DATA_WIDTH-1:0] din0; - output [DATA_WIDTH-1:0] dout0; - - reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; - - reg csb0_reg; - reg web0_reg; - reg [ADDR_WIDTH-1:0] addr0_reg; - reg [DATA_WIDTH-1:0] din0_reg; - reg [DATA_WIDTH-1:0] dout0; - - // All inputs are registers - always @(posedge clk0) - begin - csb0_reg = csb0; - web0_reg = web0; - addr0_reg = addr0; - din0_reg = din0; - #(T_HOLD) dout0 = 2'bx; - if ( !csb0_reg && web0_reg && VERBOSE ) - $display($time," Reading %m addr0=%b dout0=%b",addr0_reg,mem[addr0_reg]); - if ( !csb0_reg && !web0_reg && VERBOSE ) - $display($time," Writing %m addr0=%b din0=%b",addr0_reg,din0_reg); - end - - - // Memory Write Block Port 0 - // Write Operation : When web0 = 0, csb0 = 0 - always @ (negedge clk0) - begin : MEM_WRITE0 - if ( !csb0_reg && !web0_reg ) begin - mem[addr0_reg][1:0] = din0_reg[1:0]; - end - end - - // Memory Read Block Port 0 - // Read Operation : When web0 = 1, csb0 = 0 - always @ (negedge clk0) - begin : MEM_READ0 - if (!csb0_reg && web0_reg) - dout0 <= #(DELAY) mem[addr0_reg]; - end - -e diff --git a/compiler/base/verilog_template.v b/compiler/base/verilog_template.v new file mode 100644 index 00000000..ebf221e8 --- /dev/null +++ b/compiler/base/verilog_template.v @@ -0,0 +1,126 @@ +// OpenRAM SRAM model +// Words: #$WORDS$# +// Word size: #$WORD_SIZE$# +#WRITE_SIZE_CMT + +module #$MODULE_NAME$# ( +`ifdef USE_POWER_PINS + #$VDD$#, + #$GND$#, +`endif +#WRITE_MASK +#RW_PORT +#RW_PORT +#W_PORT + ); +#WMASK_PAR + parameter DATA_WIDTH = #$DATA_WIDTH$# ; + parameter ADDR_WIDTH = #$ADD_WIDTH$# ; + parameter RAM_DEPTH = 1 << ADDR_WIDTH; + // FIXME: This delay is arbitrary. + parameter DELAY = 3 ; + parameter VERBOSE = 1 ; //Set to 0 to only display warnings + parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary + +`ifdef USE_POWER_PINS + inout #$VDD$#; + inout #$GND$#; +`endif + +#WRITE_MASK +#RW_PORT +#RW_PORT +#W_PORT + + reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; + +# Date: Wed, 16 Feb 2022 10:53:18 -0800 Subject: [PATCH 06/13] Base template additions --- compiler/base/verilog_template.v | 43 ++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/compiler/base/verilog_template.v b/compiler/base/verilog_template.v index ebf221e8..27b455e0 100644 --- a/compiler/base/verilog_template.v +++ b/compiler/base/verilog_template.v @@ -91,21 +91,48 @@ module #$MODULE_NAME$# ( reg [DATA_WIDTH-1:0] din#$PORT_NUM$#_reg; reg [DATA_WIDTH-1:0] dout#$PORT_NUM$#; #WEB_FLOP +#W_MASK_FLOP +#SPARE_WEN_FLOP + addr#$PORT_NUM$#_reg = addr#$PORT_NUM$#; +#RW_CHECKS if ( !csb0_reg && web0_reg && VERBOSE ) $display($time," Reading %m addr0=%b dout0=%b",addr0_reg,mem[addr0_reg]); if ( !csb0_reg && !web0_reg && VERBOSE ) $display($time," Writing %m addr0=%b din0=%b",addr0_reg,din0_reg); +#>FLOPS +#DIN_FLOP +#DOUT_FLOP +#RW_VERBOSE +#R_VERBOSE +#W_VERBOSE end - - // Memory Write Block Port 0 // Write Operation : When web0 = 0, csb0 = 0 always @ (negedge clk0) From f971a83e687662b59da8831428b9b235ac090f05 Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Wed, 23 Feb 2022 10:34:48 -0800 Subject: [PATCH 07/13] Base-verilog --- compiler/base/verilog.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index 414b4491..277aa273 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -7,6 +7,7 @@ # import math from tech import spice +import verilog_template class verilog: @@ -15,7 +16,8 @@ class verilog: This is inherited by the sram_base class. """ def __init__(self): - pass + self.template = verilog_template('verilog_template.v') + self.template.readTemplate() def verilog_write(self, verilog_name): """ Write a behavioral Verilog model. """ From b1585355e18db8767d97b63da9f3162179cb3475 Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Thu, 3 Mar 2022 11:48:29 -0800 Subject: [PATCH 08/13] TEmplate rework --- compiler/base/verilog.py | 21 ++--- compiler/base/verilog_template.v | 84 ++++++++++++++------ compiler/tests/sram_1b_16_1rw_scn4m_subm.log | 2 + compiler/tests/sram_1b_16_1rw_sky130.log | 3 + 4 files changed, 73 insertions(+), 37 deletions(-) create mode 100644 compiler/tests/sram_1b_16_1rw_scn4m_subm.log create mode 100644 compiler/tests/sram_1b_16_1rw_sky130.log diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index 277aa273..d0edfdbf 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -21,15 +21,8 @@ class verilog: def verilog_write(self, verilog_name): """ Write a behavioral Verilog model. """ - self.vf = open(verilog_name, "w") - - self.vf.write("// OpenRAM SRAM model\n") - self.vf.write("// Words: {0}\n".format(self.num_words)) - self.vf.write("// Word size: {0}\n".format(self.word_size)) if self.write_size: - self.vf.write("// Write size: {0}\n\n".format(self.write_size)) - else: - self.vf.write("\n") + self.template.setSectionRepeat('WRITE_SIZE_CMT', 1) try: self.vdd_name = spice["power"] @@ -40,11 +33,9 @@ class verilog: except KeyError: self.gnd_name = "gnd" - self.vf.write("module {0}(\n".format(self.name)) - self.vf.write("`ifdef USE_POWER_PINS\n") - self.vf.write(" {},\n".format(self.vdd_name)) - self.vf.write(" {},\n".format(self.gnd_name)) - self.vf.write("`endif\n") + self.template.setTextDict('MODULE_NAME', self.name) + self.template.setTextDict('VDD', self.vdd_name) + self.template.setTextDict('GND', self.gnd_name) for port in self.all_ports: if port in self.readwrite_ports: @@ -136,6 +127,10 @@ class verilog: self.vf.write(" reg [ADDR_WIDTH-1:0] addr{0}_reg;\n".format(port)) if port in self.write_ports: self.vf.write(" reg [DATA_WIDTH-1:0] din{0}_reg;\n".format(port)) + self.vf.write("`ifdef USE_POWER_PINS\n") + self.vf.write(" {},\n".format(self.vdd_name)) + self.vf.write(" {},\n".format(self.gnd_name)) + self.vf.write("`endif\n") if port in self.read_ports: self.vf.write(" reg [DATA_WIDTH-1:0] dout{0};\n".format(port)) diff --git a/compiler/base/verilog_template.v b/compiler/base/verilog_template.v index 27b455e0..c033410e 100644 --- a/compiler/base/verilog_template.v +++ b/compiler/base/verilog_template.v @@ -108,13 +108,8 @@ module #$MODULE_NAME$# ( addr#$PORT_NUM$#_reg = addr#$PORT_NUM$#; #RW_CHECKS - if ( !csb0_reg && web0_reg && VERBOSE ) - $display($time," Reading %m addr0=%b dout0=%b",addr0_reg,mem[addr0_reg]); - if ( !csb0_reg && !web0_reg && VERBOSE ) - $display($time," Writing %m addr0=%b din0=%b",addr0_reg,din0_reg); -#>FLOPS #DIN_FLOP @@ -124,30 +119,71 @@ module #$MODULE_NAME$# ( #RW_WMASK +#RW_NO_WMASK #>RW_VERBOSE #R_VERBOSE #W_WMASK +#W_NO_WMASK #>W_VERBOSE end - // Memory Write Block Port 0 - // Write Operation : When web0 = 0, csb0 = 0 - always @ (negedge clk0) - begin : MEM_WRITE0 - if ( !csb0_reg && !web0_reg ) begin - mem[addr0_reg][1:0] = din0_reg[1:0]; +#>FLOPS +#READ +#NO_READ +#W_MASK +#ONE_SPARE_COL +#!NUM!0# +#SPARE_COLS end end - - // Memory Read Block Port 0 - // Read Operation : When web0 = 1, csb0 = 0 - always @ (negedge clk0) - begin : MEM_READ0 - if (!csb0_reg && web0_reg) - dout0 <= #(DELAY) mem[addr0_reg]; +#>W_BLOCK +#WRITE +#NO_WRITE + dout#$PORT_NUM$# <= #(DELAY) mem[addr#$PORT_NUM$#_reg]; end - -e +#>R_BLOCK +endmodule diff --git a/compiler/tests/sram_1b_16_1rw_scn4m_subm.log b/compiler/tests/sram_1b_16_1rw_scn4m_subm.log new file mode 100644 index 00000000..1d80b5f9 --- /dev/null +++ b/compiler/tests/sram_1b_16_1rw_scn4m_subm.log @@ -0,0 +1,2 @@ +ERROR: file magic.py: line 358: sram LVS mismatch (results in /tmp/openram_bugra_12868_temp/sram.lvs.report) + diff --git a/compiler/tests/sram_1b_16_1rw_sky130.log b/compiler/tests/sram_1b_16_1rw_sky130.log new file mode 100644 index 00000000..f6367750 --- /dev/null +++ b/compiler/tests/sram_1b_16_1rw_sky130.log @@ -0,0 +1,3 @@ +ERROR: file design.py: line 47: Custom cell pin names do not match spice file: +['D', 'Q', 'CLK', 'VDD', 'GND'] vs [] + From 804e5a58c52f1ed822e927f75fb02e77cc179132 Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Wed, 9 Mar 2022 08:58:29 -0800 Subject: [PATCH 09/13] Template section clone method --- compiler/base/verilog.py | 2 ++ compiler/base/verilog_template.v | 2 ++ 2 files changed, 4 insertions(+) diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index d0edfdbf..a2978925 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -38,6 +38,8 @@ class verilog: self.template.setTextDict('GND', self.gnd_name) for port in self.all_ports: + self.template.cloneSection("PORTS", "PORTS" + str(port)) + if port in self.readwrite_ports: self.vf.write("// Port {0}: RW\n".format(port)) elif port in self.read_ports: diff --git a/compiler/base/verilog_template.v b/compiler/base/verilog_template.v index c033410e..2c40b767 100644 --- a/compiler/base/verilog_template.v +++ b/compiler/base/verilog_template.v @@ -6,6 +6,7 @@ #>WRITE_SIZE_CMT module #$MODULE_NAME$# ( +#W_PORT +#>PORTS ); # Date: Wed, 9 Mar 2022 08:59:20 -0800 Subject: [PATCH 10/13] Template section clone method --- compiler/verilogTemplate/verilog_template.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compiler/verilogTemplate/verilog_template.py b/compiler/verilogTemplate/verilog_template.py index b5267d00..f1945f54 100644 --- a/compiler/verilogTemplate/verilog_template.py +++ b/compiler/verilogTemplate/verilog_template.py @@ -9,6 +9,7 @@ class text_section: self.sectionPos = [] self.lineNum = 0 self.repeat = 1 + self.index = -1 def addLine(self, line): self.lines.append(line) @@ -17,6 +18,14 @@ class text_section: 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 = [] @@ -101,3 +110,6 @@ class verilog_template: def setTextDict(self, label, value): self.textDict[label] = value + + def cloneSection(self, name, cloneName): + self.sections[name].clone(cloneName) From 3dd65b1a01f3b004c1b07e1a3c2863b011b7ff89 Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Thu, 9 Jun 2022 21:40:19 -0700 Subject: [PATCH 11/13] modified template engine & sram multibank class --- compiler/base/verilog.py | 2 +- compiler/sram/multibank_template.v | 73 ------ compiler/sram/sram.py | 13 +- compiler/sram/sram_2bank.py | 240 ------------------- compiler/sram/sram_config.py | 4 +- compiler/sram/sram_multibank.py | 27 +++ compiler/sram/sram_multibank_template.v | 146 +++++++++++ compiler/verilogTemplate/template.py | 72 ++++++ compiler/verilogTemplate/test.py | 29 ++- compiler/verilogTemplate/test.v | 65 ----- compiler/verilogTemplate/verilog_template.py | 115 --------- 11 files changed, 273 insertions(+), 513 deletions(-) delete mode 100644 compiler/sram/multibank_template.v delete mode 100644 compiler/sram/sram_2bank.py create mode 100644 compiler/sram/sram_multibank.py create mode 100644 compiler/sram/sram_multibank_template.v create mode 100644 compiler/verilogTemplate/template.py delete mode 100644 compiler/verilogTemplate/test.v delete mode 100644 compiler/verilogTemplate/verilog_template.py diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index a2978925..2c91cbba 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -7,7 +7,7 @@ # import math from tech import spice -import verilog_template +from verilog_template import verilog_template class verilog: diff --git a/compiler/sram/multibank_template.v b/compiler/sram/multibank_template.v deleted file mode 100644 index d5724cb6..00000000 --- a/compiler/sram/multibank_template.v +++ /dev/null @@ -1,73 +0,0 @@ - -module multibank # ( - DATA_WIDTH = 32, - ADDR_WIDTH= 8, - NUM_BANKS=2 -)( -#RW_PORTS -#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 - -#!PORT_NUM!0# -#BANK_RW_PORTS - ); -#>BANK_INIT - -always @(posedge clk) begin - case (addr[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL]) -#!PORT_NUM!0# -#BANK_CASE - endcase -end - -endmodule diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 90855d8e..9f460c7a 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -37,14 +37,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() diff --git a/compiler/sram/sram_2bank.py b/compiler/sram/sram_2bank.py deleted file mode 100644 index 421641ac..00000000 --- a/compiler/sram/sram_2bank.py +++ /dev/null @@ -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 vector import vector -from globals import OPTS, print_time - -from sram_base import sram_base -from bank import bank -from dff_buf_array import dff_buf_array -from dff_array 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() 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) From 22c01d7f27e4691036b9e65e2d6e1c6d30787de3 Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Tue, 14 Jun 2022 17:57:04 -0700 Subject: [PATCH 12/13] Multibank file generation (messy) --- compiler/base/verilog.py | 30 +- compiler/modules/bank.py | 60 +- compiler/sram/sram.py | 13 +- compiler/sram/sram_1bank.py | 771 +++++++++++++++++- compiler/sram/sram_config.py | 3 +- compiler/sram/sram_multibank.py | 43 +- compiler/sram/sram_multibank_template.v | 33 +- .../template.py | 9 +- .../test.py | 0 9 files changed, 886 insertions(+), 76 deletions(-) rename compiler/{verilogTemplate => verilog_template}/template.py (98%) rename compiler/{verilogTemplate => verilog_template}/test.py (100%) diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index 2c91cbba..64155051 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -7,7 +7,6 @@ # import math from tech import spice -from verilog_template import verilog_template class verilog: @@ -16,13 +15,19 @@ class verilog: This is inherited by the sram_base class. """ def __init__(self): - self.template = verilog_template('verilog_template.v') - self.template.readTemplate() + pass def verilog_write(self, verilog_name): """ Write a behavioral Verilog model. """ + self.vf = open(verilog_name, "w") + + self.vf.write("// OpenRAM SRAM model\n") + self.vf.write("// Words: {0}\n".format(self.num_words)) + self.vf.write("// Word size: {0}\n".format(self.word_size)) if self.write_size: - self.template.setSectionRepeat('WRITE_SIZE_CMT', 1) + self.vf.write("// Write size: {0}\n\n".format(self.write_size)) + else: + self.vf.write("\n") try: self.vdd_name = spice["power"] @@ -33,13 +38,16 @@ class verilog: except KeyError: self.gnd_name = "gnd" - self.template.setTextDict('MODULE_NAME', self.name) - self.template.setTextDict('VDD', self.vdd_name) - self.template.setTextDict('GND', self.gnd_name) + if self.num_banks > 1: + self.vf.write("module {0}(\n".format(self.name + '_1bank')) + else: + self.vf.write("module {0}(\n".format(self.name)) + self.vf.write("`ifdef USE_POWER_PINS\n") + self.vf.write(" {},\n".format(self.vdd_name)) + self.vf.write(" {},\n".format(self.gnd_name)) + self.vf.write("`endif\n") for port in self.all_ports: - self.template.cloneSection("PORTS", "PORTS" + str(port)) - if port in self.readwrite_ports: self.vf.write("// Port {0}: RW\n".format(port)) elif port in self.read_ports: @@ -129,10 +137,6 @@ class verilog: self.vf.write(" reg [ADDR_WIDTH-1:0] addr{0}_reg;\n".format(port)) if port in self.write_ports: self.vf.write(" reg [DATA_WIDTH-1:0] din{0}_reg;\n".format(port)) - self.vf.write("`ifdef USE_POWER_PINS\n") - self.vf.write(" {},\n".format(self.vdd_name)) - self.vf.write(" {},\n".format(self.gnd_name)) - self.vf.write("`endif\n") if port in self.read_ports: self.vf.write(" reg [DATA_WIDTH-1:0] dout{0};\n".format(port)) diff --git a/compiler/modules/bank.py b/compiler/modules/bank.py index bd3fc0a4..af13661a 100644 --- a/compiler/modules/bank.py +++ b/compiler/modules/bank.py @@ -44,10 +44,10 @@ class bank(design.design): # The local control signals are gated when we have bank select logic, # so this prefix will be added to all of the input signals to create # the internal gated signals. - if self.num_banks>1: - self.prefix="gated_" - else: - self.prefix="" + #if self.num_banks>1: + # self.prefix="gated_" + #else: + self.prefix="" self.create_netlist() if not OPTS.netlist_only: @@ -98,9 +98,9 @@ class bank(design.design): # For more than one bank, we have a bank select and name # the signals gated_*. - if self.num_banks > 1: - for port in self.all_ports: - self.add_pin("bank_sel{}".format(port), "INPUT") + #if self.num_banks > 1: + # for port in self.all_ports: + # self.add_pin("bank_sel{}".format(port), "INPUT") for port in self.read_ports: self.add_pin("s_en{0}".format(port), "INPUT") for port in self.all_ports: @@ -128,8 +128,8 @@ class bank(design.design): self.route_port_address(port) self.route_column_address_lines(port) self.route_control_lines(port) - if self.num_banks > 1: - self.route_bank_select(port) + #if self.num_banks > 1: + # self.route_bank_select(port) self.route_supplies() @@ -171,7 +171,7 @@ class bank(design.design): self.create_port_data() self.create_port_address() self.create_column_decoder() - self.create_bank_select() + #self.create_bank_select() def compute_instance_offsets(self): """ @@ -252,8 +252,8 @@ class bank(design.design): y_offset = min(self.column_decoder_offsets[port].y, self.port_data[port].column_mux_offset.y) else: y_offset = self.port_address_offsets[port].y - if self.num_banks > 1: - y_offset += self.bank_select.height + drc("well_to_well") + #if self.num_banks > 1: + # y_offset += self.bank_select.height + drc("well_to_well") self.bank_select_offsets[port] = vector(-x_offset, -y_offset) def compute_instance_port1_offsets(self): @@ -311,20 +311,24 @@ class bank(design.design): self.place_port_address(self.port_address_offsets) self.place_column_decoder(self.column_decoder_offsets) - self.place_bank_select(self.bank_select_offsets) + #self.place_bank_select(self.bank_select_offsets) def compute_sizes(self): """ Computes the required sizes to create the bank """ - self.num_cols = int(self.words_per_row * self.word_size) - self.num_rows_temp = int(self.num_words / self.words_per_row) - self.num_rows = self.num_rows_temp + self.num_spare_rows + 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_cols = int(self.words_per_row * self.word_size) + self.num_rows_temp = int(self.num_words_per_bank / self.words_per_row) + self.num_rows = self.num_rows_temp + self.num_spare_rows + self.row_addr_size = ceil(log(self.num_rows, 2)) self.col_addr_size = int(log(self.words_per_row, 2)) self.addr_size = self.col_addr_size + self.row_addr_size - debug.check(self.num_rows_temp * self.num_cols==self.word_size * self.num_words, + debug.check(self.num_rows_temp * self.num_cols * self.num_banks ==self.word_size * self.num_words, "Invalid bank sizes.") debug.check(self.addr_size==self.col_addr_size + self.row_addr_size, "Invalid address break down.") @@ -351,10 +355,10 @@ class bank(design.design): # These will be outputs of the gaters if this is multibank, if not, normal signals. self.control_signals = [] for port in self.all_ports: - if self.num_banks > 1: - self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]]) - else: - self.control_signals.append(self.input_control_signals[port]) + #if self.num_banks > 1: + # self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]]) + #else: + self.control_signals.append(self.input_control_signals[port]) # The central bus is the column address (one hot) and row address (binary) @@ -406,8 +410,8 @@ class bank(design.design): bit_offsets=self.bit_offsets) self.port_data.append(temp_pre) - if(self.num_banks > 1): - self.bank_select = factory.create(module_type="bank_select") + #if(self.num_banks > 1): + # self.bank_select = factory.create(module_type="bank_select") def create_bitcell_array(self): """ Creating Bitcell Array """ @@ -577,7 +581,7 @@ class bank(design.design): def create_bank_select(self): """ Create the bank select logic. """ - if not self.num_banks > 1: + if not self.num_banks < 2: return self.bank_select_inst = [None] * len(self.all_ports) @@ -595,7 +599,7 @@ class bank(design.design): def place_bank_select(self, offsets): """ Place the bank select logic. """ - if not self.num_banks > 1: + if not self.num_banks < 2: return debug.check(len(offsets)>=len(self.all_ports), @@ -694,7 +698,8 @@ class bank(design.design): names=self.control_signals[0], length=control_bus_length, vertical=True, - make_pins=(self.num_banks==1), + #make_pins=(self.num_banks==1), + make_pins=(True), pitch=self.m3_pitch) self.copy_layout_pin(self.port_address_inst[0], "wl_en", self.prefix + "wl_en0") @@ -711,7 +716,8 @@ class bank(design.design): names=list(reversed(self.control_signals[1])), length=control_bus_length, vertical=True, - make_pins=(self.num_banks==1), + #make_pins=(self.num_banks==1), + make_pins=(True), pitch=self.m3_pitch) self.copy_layout_pin(self.port_address_inst[1], "wl_en", self.prefix + "wl_en1") diff --git a/compiler/sram/sram.py b/compiler/sram/sram.py index 9f460c7a..7c45d5a1 100644 --- a/compiler/sram/sram.py +++ b/compiler/sram/sram.py @@ -41,11 +41,6 @@ class 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() @@ -83,7 +78,11 @@ class sram(): shutil.move(unique_name, name) def verilog_write(self, name): - self.s.verilog_write(name) + self.s.verilog_write(name + '_1bank.v') + if self.num_banks != 1: + from sram_multibank import sram_multibank + mb = sram_multibank(self.s) + mb.verilog_write(name + '.v') def extended_config_write(self, name): """Dump config file with all options. @@ -184,7 +183,7 @@ class sram(): # Write a verilog model start_time = datetime.datetime.now() - vname = OPTS.output_path + self.s.name + ".v" + vname = OPTS.output_path + self.s.name debug.print_raw("Verilog: Writing to {0}".format(vname)) self.verilog_write(vname) print_time("Verilog", datetime.datetime.now(), start_time) diff --git a/compiler/sram/sram_1bank.py b/compiler/sram/sram_1bank.py index a6abeba6..5ede7e42 100644 --- a/compiler/sram/sram_1bank.py +++ b/compiler/sram/sram_1bank.py @@ -6,19 +6,780 @@ # All rights reserved. # from vector import vector -from sram_base import sram_base from contact import m2_via from channel_route import channel_route from router_tech import router_tech -from globals import OPTS +from globals import OPTS, print_time +import datetime +import debug +from math import log, ceil +from importlib import reload +from design import design +from verilog import verilog +from lef import lef +from sram_factory import factory +from tech import spice - -class sram_1bank(sram_base): +class sram_1bank(design, verilog, lef): """ Procedures specific to a one bank SRAM. """ def __init__(self, name, sram_config): - sram_base.__init__(self, name, sram_config) + design.__init__(self, name) + lef.__init__(self, ["m1", "m2", "m3", "m4"]) + verilog.__init__(self) + + self.sram_config = sram_config + sram_config.set_local_config(self) + + self.bank_insts = [] + + if self.write_size: + self.num_wmasks = int(ceil(self.word_size / self.write_size)) + else: + self.num_wmasks = 0 + + if not self.num_spare_cols: + self.num_spare_cols = 0 + + try: + from tech import power_grid + self.supply_stack = power_grid + except ImportError: + # if no power_grid is specified by tech we use sensible defaults + # Route a M3/M4 grid + self.supply_stack = self.m3_stack + + def add_pins(self): + """ Add pins for entire SRAM. """ + + for port in self.write_ports: + for bit in range(self.word_size + self.num_spare_cols): + self.add_pin("din{0}[{1}]".format(port, bit), "INPUT") + for port in self.all_ports: + for bit in range(self.addr_size): + self.add_pin("addr{0}[{1}]".format(port, bit), "INPUT") + + # These are used to create the physical pins + self.control_logic_inputs = [] + self.control_logic_outputs = [] + for port in self.all_ports: + if port in self.readwrite_ports: + self.control_logic_inputs.append(self.control_logic_rw.get_inputs()) + self.control_logic_outputs.append(self.control_logic_rw.get_outputs()) + elif port in self.write_ports: + self.control_logic_inputs.append(self.control_logic_w.get_inputs()) + self.control_logic_outputs.append(self.control_logic_w.get_outputs()) + else: + self.control_logic_inputs.append(self.control_logic_r.get_inputs()) + self.control_logic_outputs.append(self.control_logic_r.get_outputs()) + + for port in self.all_ports: + self.add_pin("csb{}".format(port), "INPUT") + for port in self.readwrite_ports: + self.add_pin("web{}".format(port), "INPUT") + for port in self.all_ports: + self.add_pin("clk{}".format(port), "INPUT") + # add the optional write mask pins + for port in self.write_ports: + for bit in range(self.num_wmasks): + self.add_pin("wmask{0}[{1}]".format(port, bit), "INPUT") + if self.num_spare_cols == 1: + self.add_pin("spare_wen{0}".format(port), "INPUT") + else: + for bit in range(self.num_spare_cols): + self.add_pin("spare_wen{0}[{1}]".format(port, bit), "INPUT") + for port in self.read_ports: + for bit in range(self.word_size + self.num_spare_cols): + self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT") + + # Standard supply and ground names + try: + self.vdd_name = spice["power"] + except KeyError: + self.vdd_name = "vdd" + try: + self.gnd_name = spice["ground"] + except KeyError: + self.gnd_name = "gnd" + + self.add_pin(self.vdd_name, "POWER") + self.add_pin(self.gnd_name, "GROUND") + self.ext_supplies = [self.vdd_name, self.gnd_name] + self.ext_supply = {"vdd" : self.vdd_name, "gnd" : self.gnd_name} + + def add_global_pex_labels(self): + """ + Add pex labels at the sram level for spice analysis + """ + + + + # add pex labels for bitcells + for bank_num in range(len(self.bank_insts)): + bank = self.bank_insts[bank_num] + pex_data = bank.reverse_transformation_bitcell(self.bitcell.name) + + bank_offset = pex_data[0] # offset bank relative to sram + Q_offset = pex_data[1] # offset of storage relative to bank + Q_bar_offset = pex_data[2] # offset of storage relative to bank + bl_offsets = pex_data[3] + br_offsets = pex_data[4] + bl_meta = pex_data[5] + br_meta = pex_data[6] + + bl = [] + br = [] + + storage_layer_name = "m1" + bitline_layer_name = self.bitcell.get_pin("bl").layer + + for cell in range(len(bank_offset)): + Q = [bank_offset[cell][0] + Q_offset[cell][0], + bank_offset[cell][1] + Q_offset[cell][1]] + Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0], + bank_offset[cell][1] + Q_bar_offset[cell][1]] + OPTS.words_per_row = self.words_per_row + row = int(cell % (OPTS.num_words / self.words_per_row)) + col = int(cell / (OPTS.num_words)) + self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num, + row, + col), + storage_layer_name, + Q) + self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num, + row, + col), + storage_layer_name, + Q_bar) + + for cell in range(len(bl_offsets)): + col = bl_meta[cell][0][2] + for bitline in range(len(bl_offsets[cell])): + bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0], + float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]] + bl.append([bitline_location, bl_meta[cell][bitline][3], col]) + + for cell in range(len(br_offsets)): + col = br_meta[cell][0][2] + for bitline in range(len(br_offsets[cell])): + bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0], + float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]] + br.append([bitline_location, br_meta[cell][bitline][3], col]) + + for i in range(len(bl)): + self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]), + bitline_layer_name, bl[i][0]) + + for i in range(len(br)): + self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]), + bitline_layer_name, br[i][0]) + + # add pex labels for control logic + for i in range(len(self.control_logic_insts)): + instance = self.control_logic_insts[i] + control_logic_offset = instance.offset + for output in instance.mod.output_list: + pin = instance.mod.get_pin(output) + pin.transform([0, 0], instance.mirror, instance.rotate) + offset = [control_logic_offset[0] + pin.center()[0], + control_logic_offset[1] + pin.center()[1]] + self.add_layout_pin_rect_center("{0}{1}".format(pin.name, i), + storage_layer_name, + offset) + + def create_netlist(self): + """ Netlist creation """ + + start_time = datetime.datetime.now() + + # Must create the control logic before pins to get the pins + self.add_modules() + self.add_pins() + self.create_modules() + + # This is for the lib file if we don't create layout + self.width=0 + self.height=0 + + if not OPTS.is_unit_test: + print_time("Submodules", datetime.datetime.now(), start_time) + + def create_layout(self): + """ Layout creation """ + start_time = datetime.datetime.now() + self.place_instances() + if not OPTS.is_unit_test: + print_time("Placement", datetime.datetime.now(), start_time) + + start_time = datetime.datetime.now() + self.route_layout() + + if not OPTS.is_unit_test: + print_time("Routing", datetime.datetime.now(), start_time) + + self.add_lvs_correspondence_points() + + self.offset_all_coordinates() + + highest_coord = self.find_highest_coords() + self.width = highest_coord[0] + self.height = highest_coord[1] + if OPTS.use_pex and OPTS.pex_exe[0] != "calibre": + debug.info(2, "adding global pex labels") + self.add_global_pex_labels() + self.add_boundary(ll=vector(0, 0), + ur=vector(self.width, self.height)) + + start_time = datetime.datetime.now() + if not OPTS.is_unit_test: + # We only enable final verification if we have routed the design + # Only run this if not a unit test, because unit test will also verify it. + self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=OPTS.check_lvsdrc) + print_time("Verification", datetime.datetime.now(), start_time) + + def create_modules(self): + debug.error("Must override pure virtual function.", -1) + + def route_supplies(self, bbox=None): + """ Route the supply grid and connect the pins to them. """ + + # Copy the pins to the top level + # This will either be used to route or left unconnected. + for pin_name in ["vdd", "gnd"]: + for inst in self.insts: + self.copy_power_pins(inst, pin_name, self.ext_supply[pin_name]) + + if not OPTS.route_supplies: + # Do not route the power supply (leave as must-connect pins) + return + elif OPTS.route_supplies == "grid": + from supply_grid_router import supply_grid_router as router + else: + from supply_tree_router import supply_tree_router as router + rtr=router(layers=self.supply_stack, + design=self, + bbox=bbox, + pin_type=OPTS.supply_pin_type) + + rtr.route() + + if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]: + # Find the lowest leftest pin for vdd and gnd + for pin_name in ["vdd", "gnd"]: + # Copy the pin shape(s) to rectangles + for pin in self.get_pins(pin_name): + self.add_rect(pin.layer, + pin.ll(), + pin.width(), + pin.height()) + + # Remove the pin shape(s) + self.remove_layout_pin(pin_name) + + # Get new pins + pins = rtr.get_new_pins(pin_name) + for pin in pins: + self.add_layout_pin(self.ext_supply[pin_name], + pin.layer, + pin.ll(), + pin.width(), + pin.height()) + + elif OPTS.route_supplies and OPTS.supply_pin_type == "single": + # Update these as we may have routed outside the region (perimeter pins) + lowest_coord = self.find_lowest_coords() + + # Find the lowest leftest pin for vdd and gnd + for pin_name in ["vdd", "gnd"]: + # Copy the pin shape(s) to rectangles + for pin in self.get_pins(pin_name): + self.add_rect(pin.layer, + pin.ll(), + pin.width(), + pin.height()) + + # Remove the pin shape(s) + self.remove_layout_pin(pin_name) + + # Get the lowest, leftest pin + pin = rtr.get_ll_pin(pin_name) + + pin_width = 2 * getattr(self, "{}_width".format(pin.layer)) + + # Add it as an IO pin to the perimeter + route_width = pin.rx() - lowest_coord.x + pin_offset = vector(lowest_coord.x, pin.by()) + self.add_rect(pin.layer, + pin_offset, + route_width, + pin.height()) + + self.add_layout_pin(self.ext_supply[pin_name], + pin.layer, + pin_offset, + pin_width, + pin.height()) + else: + # Grid is left with many top level pins + pass + + def route_escape_pins(self, bbox): + """ + Add the top-level pins for a single bank SRAM with control. + """ + + # List of pin to new pin name + pins_to_route = [] + for port in self.all_ports: + # Connect the control pins as inputs + for signal in self.control_logic_inputs[port]: + if signal.startswith("rbl"): + continue + if signal=="clk": + pins_to_route.append("{0}{1}".format(signal, port)) + else: + pins_to_route.append("{0}{1}".format(signal, port)) + + if port in self.write_ports: + for bit in range(self.word_size + self.num_spare_cols): + pins_to_route.append("din{0}[{1}]".format(port, bit)) + + if port in self.readwrite_ports or port in self.read_ports: + for bit in range(self.word_size + self.num_spare_cols): + pins_to_route.append("dout{0}[{1}]".format(port, bit)) + + for bit in range(self.col_addr_size): + pins_to_route.append("addr{0}[{1}]".format(port, bit)) + + for bit in range(self.row_addr_size): + pins_to_route.append("addr{0}[{1}]".format(port, bit + self.col_addr_size)) + + if port in self.write_ports: + if self.write_size: + for bit in range(self.num_wmasks): + pins_to_route.append("wmask{0}[{1}]".format(port, bit)) + + if port in self.write_ports: + if self.num_spare_cols == 1: + pins_to_route.append("spare_wen{0}".format(port)) + else: + for bit in range(self.num_spare_cols): + pins_to_route.append("spare_wen{0}[{1}]".format(port, bit)) + + from signal_escape_router import signal_escape_router as router + rtr=router(layers=self.m3_stack, + design=self, + bbox=bbox) + rtr.escape_route(pins_to_route) + + def compute_bus_sizes(self): + """ Compute the independent bus widths shared between two and four bank SRAMs """ + + # address size + control signals + one-hot bank select signals + self.num_vertical_line = self.addr_size + self.control_size + 1# + log(self.num_banks, 2) + 1 + # data bus size + self.num_horizontal_line = self.word_size + + self.vertical_bus_width = self.m2_pitch * self.num_vertical_line + # vertical bus height depends on 2 or 4 banks + + self.data_bus_height = self.m3_pitch * self.num_horizontal_line + self.data_bus_width = 2 * (self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width + + self.control_bus_height = self.m1_pitch * (self.control_size + 2) + self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width + + self.supply_bus_height = self.m1_pitch * 2 # 2 for vdd/gnd placed with control bus + self.supply_bus_width = self.data_bus_width + + # Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really) + debug.check(self.bank.width + self.vertical_bus_width > 0.9 * self.control_logic.width, + "Bank is too small compared to control logic.") + + def add_busses(self): + """ Add the horizontal and vertical busses """ + # Vertical bus + # The order of the control signals on the control bus: + self.control_bus_names = [] + for port in self.all_ports: + self.control_bus_names[port] = ["clk_buf{}".format(port)] + wen = "w_en{}".format(port) + sen = "s_en{}".format(port) + pen = "p_en_bar{}".format(port) + if self.port_id[port] == "r": + self.control_bus_names[port].extend([sen, pen]) + elif self.port_id[port] == "w": + self.control_bus_names[port].extend([wen, pen]) + else: + self.control_bus_names[port].extend([sen, wen, pen]) + self.vert_control_bus_positions = self.create_vertical_bus(layer="m2", + pitch=self.m2_pitch, + offset=self.vertical_bus_offset, + names=self.control_bus_names[port], + length=self.vertical_bus_height) + + self.addr_bus_names=["A{0}[{1}]".format(port, i) for i in range(self.addr_size)] + self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2", + pitch=self.m2_pitch, + offset=self.addr_bus_offset, + names=self.addr_bus_names, + length=self.addr_bus_height)) + + self.bank_sel_bus_names = ["bank_sel{0}_{1}".format(port, i) for i in range(1)]#range(self.num_banks)] + self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2", + pitch=self.m2_pitch, + offset=self.bank_sel_bus_offset, + names=self.bank_sel_bus_names, + length=self.vertical_bus_height)) + + # Horizontal data bus + self.data_bus_names = ["DATA{0}[{1}]".format(port, i) for i in range(self.word_size)] + self.data_bus_positions = self.create_horizontal_pin_bus(layer="m3", + pitch=self.m3_pitch, + offset=self.data_bus_offset, + names=self.data_bus_names, + length=self.data_bus_width) + + # Horizontal control logic bus + # vdd/gnd in bus go along whole SRAM + # FIXME: Fatten these wires? + self.horz_control_bus_positions = self.create_horizontal_bus(layer="m1", + pitch=self.m1_pitch, + offset=self.supply_bus_offset, + names=["vdd"], + length=self.supply_bus_width) + # The gnd rail must not be the entire width since we protrude the right-most vdd rail up for + # the decoder in 4-bank SRAMs + self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1", + pitch=self.m1_pitch, + offset=self.supply_bus_offset + vector(0, self.m1_pitch), + names=["gnd"], + length=self.supply_bus_width)) + self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1", + pitch=self.m1_pitch, + offset=self.control_bus_offset, + names=self.control_bus_names[port], + length=self.control_bus_width)) + + def add_multi_bank_modules(self): + """ Create the multibank address flops and bank decoder """ + from dff_buf_array import dff_buf_array + self.msb_address = dff_buf_array(name="msb_address", + rows=1, + columns=self.num_banks / 2) + + if self.num_banks>2: + self.msb_decoder = self.bank.decoder.pre2_4 + + def add_modules(self): + self.bitcell = factory.create(module_type=OPTS.bitcell) + self.dff = factory.create(module_type="dff") + + # Create the bank module (up to four are instantiated) + self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank") + + self.num_spare_cols = self.bank.num_spare_cols + + # Create the address and control flops (but not the clk) + self.row_addr_dff = factory.create("dff_array", module_name="row_addr_dff", rows=self.row_addr_size, columns=1) + + if self.col_addr_size > 0: + self.col_addr_dff = factory.create("dff_array", module_name="col_addr_dff", rows=1, columns=self.col_addr_size) + else: + self.col_addr_dff = None + + self.data_dff = factory.create("dff_array", module_name="data_dff", rows=1, columns=self.word_size + self.num_spare_cols) + + if self.write_size: + self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks) + + if self.num_spare_cols: + self.spare_wen_dff = factory.create("dff_array", module_name="spare_wen_dff", rows=1, columns=self.num_spare_cols) + + # Create bank decoder +# if(self.num_banks > 1): +# self.add_multi_bank_modules() + + self.bank_count = 0 + + c = reload(__import__(OPTS.control_logic)) + self.mod_control_logic = getattr(c, OPTS.control_logic) + + # Create the control logic module for each port type + if len(self.readwrite_ports) > 0: + self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows, + words_per_row=self.words_per_row, + word_size=self.word_size, + spare_columns=self.num_spare_cols, + sram=self, + port_type="rw") + if len(self.writeonly_ports) > 0: + self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows, + words_per_row=self.words_per_row, + word_size=self.word_size, + spare_columns=self.num_spare_cols, + sram=self, + port_type="w") + if len(self.readonly_ports) > 0: + self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows, + words_per_row=self.words_per_row, + word_size=self.word_size, + spare_columns=self.num_spare_cols, + sram=self, + port_type="r") + + def create_bank(self, bank_num): + """ Create a bank """ + self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num), + mod=self.bank)) + + temp = [] + for port in self.read_ports: + for bit in range(self.word_size + self.num_spare_cols): + temp.append("dout{0}[{1}]".format(port, bit)) + for port in self.all_ports: + temp.append("rbl_bl{0}".format(port)) + for port in self.write_ports: + for bit in range(self.word_size + self.num_spare_cols): + temp.append("bank_din{0}_{1}".format(port, bit)) + for port in self.all_ports: + for bit in range(self.bank_addr_size): + temp.append("a{0}_{1}".format(port, bit)) +# if(self.num_banks > 1): +# for port in self.all_ports: +# temp.append("bank_sel{0}_{1}".format(port, bank_num)) + for port in self.read_ports: + temp.append("s_en{0}".format(port)) + for port in self.all_ports: + temp.append("p_en_bar{0}".format(port)) + for port in self.write_ports: + temp.append("w_en{0}".format(port)) + for bit in range(self.num_wmasks): + temp.append("bank_wmask{0}_{1}".format(port, bit)) + for bit in range(self.num_spare_cols): + temp.append("bank_spare_wen{0}_{1}".format(port, bit)) + for port in self.all_ports: + temp.append("wl_en{0}".format(port)) + temp.extend(self.ext_supplies) + self.connect_inst(temp) + + return self.bank_insts[-1] + + def place_bank(self, bank_inst, position, x_flip, y_flip): + """ Place a bank at the given position with orientations """ + + # x_flip == 1 --> no flip in x_axis + # x_flip == -1 --> flip in x_axis + # y_flip == 1 --> no flip in y_axis + # y_flip == -1 --> flip in y_axis + + # x_flip and y_flip are used for position translation + + if x_flip == -1 and y_flip == -1: + bank_rotation = 180 + else: + bank_rotation = 0 + + if x_flip == y_flip: + bank_mirror = "R0" + elif x_flip == -1: + bank_mirror = "MX" + elif y_flip == -1: + bank_mirror = "MY" + else: + bank_mirror = "R0" + + bank_inst.place(offset=position, + mirror=bank_mirror, + rotate=bank_rotation) + + return bank_inst + + def create_row_addr_dff(self): + """ Add all address flops for the main decoder """ + insts = [] + for port in self.all_ports: + insts.append(self.add_inst(name="row_address{}".format(port), + mod=self.row_addr_dff)) + + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.row_addr_size): + inputs.append("addr{}[{}]".format(port, bit + self.col_addr_size)) + outputs.append("a{}_{}".format(port, bit + self.col_addr_size)) + + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) + + return insts + + def create_col_addr_dff(self): + """ Add and place all address flops for the column decoder """ + insts = [] + for port in self.all_ports: + insts.append(self.add_inst(name="col_address{}".format(port), + mod=self.col_addr_dff)) + + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.col_addr_size): + inputs.append("addr{}[{}]".format(port, bit)) + outputs.append("a{}_{}".format(port, bit)) + + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) + + return insts + + def create_data_dff(self): + """ Add and place all data flops """ + insts = [] + for port in self.all_ports: + if port in self.write_ports: + insts.append(self.add_inst(name="data_dff{}".format(port), + mod=self.data_dff)) + else: + insts.append(None) + continue + + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.word_size + self.num_spare_cols): + inputs.append("din{}[{}]".format(port, bit)) + outputs.append("bank_din{}_{}".format(port, bit)) + + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) + + return insts + + def create_wmask_dff(self): + """ Add and place all wmask flops """ + insts = [] + for port in self.all_ports: + if port in self.write_ports: + insts.append(self.add_inst(name="wmask_dff{}".format(port), + mod=self.wmask_dff)) + else: + insts.append(None) + continue + + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.num_wmasks): + inputs.append("wmask{}[{}]".format(port, bit)) + outputs.append("bank_wmask{}_{}".format(port, bit)) + + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) + + return insts + + def create_spare_wen_dff(self): + """ Add all spare write enable flops """ + insts = [] + for port in self.all_ports: + if port in self.write_ports: + insts.append(self.add_inst(name="spare_wen_dff{}".format(port), + mod=self.spare_wen_dff)) + else: + insts.append(None) + continue + + # inputs, outputs/output/bar + inputs = [] + outputs = [] + for bit in range(self.num_spare_cols): + inputs.append("spare_wen{}[{}]".format(port, bit)) + outputs.append("bank_spare_wen{}_{}".format(port, bit)) + + self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies) + + return insts + + def create_control_logic(self): + """ Add control logic instances """ + + insts = [] + for port in self.all_ports: + if port in self.readwrite_ports: + mod = self.control_logic_rw + elif port in self.write_ports: + mod = self.control_logic_w + else: + mod = self.control_logic_r + + insts.append(self.add_inst(name="control{}".format(port), mod=mod)) + + # Inputs + temp = ["csb{}".format(port)] + if port in self.readwrite_ports: + temp.append("web{}".format(port)) + temp.append("clk{}".format(port)) + temp.append("rbl_bl{}".format(port)) + + # Outputs + if port in self.read_ports: + temp.append("s_en{}".format(port)) + if port in self.write_ports: + temp.append("w_en{}".format(port)) + temp.append("p_en_bar{}".format(port)) + temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port)] + self.ext_supplies) + self.connect_inst(temp) + + return insts + + def sp_write(self, sp_name, lvs=False, trim=False): + # Write the entire spice of the object to the file + ############################################################ + # Spice circuit + ############################################################ + sp = open(sp_name, 'w') + + sp.write("**************************************************\n") + sp.write("* OpenRAM generated memory.\n") + sp.write("* Words: {}\n".format(self.num_words)) + sp.write("* Data bits: {}\n".format(self.word_size)) + sp.write("* Banks: {}\n".format(self.num_banks)) + sp.write("* Column mux: {}:1\n".format(self.words_per_row)) + sp.write("* Trimmed: {}\n".format(trim)) + sp.write("* LVS: {}\n".format(lvs)) + sp.write("**************************************************\n") + # This causes unit test mismatch + + # sp.write("* Created: {0}\n".format(datetime.datetime.now())) + # sp.write("* User: {0}\n".format(getpass.getuser())) + # sp.write(".global {0} {1}\n".format(spice["vdd_name"], + # spice["gnd_name"])) + usedMODS = list() + self.sp_write_file(sp, usedMODS, lvs=lvs, trim=trim) + del usedMODS + sp.close() + + def graph_exclude_bits(self, targ_row, targ_col): + """ + Excludes bits in column from being added to graph except target + """ + self.bank.graph_exclude_bits(targ_row, targ_col) + + def clear_exclude_bits(self): + """ + Clears the bit exclusions + """ + self.bank.clear_exclude_bits() + + def graph_exclude_column_mux(self, column_include_num, port): + """ + Excludes all columns muxes unrelated to the target bit being simulated. + """ + self.bank.graph_exclude_column_mux(column_include_num, port) + + def graph_clear_column_mux(self, port): + """ + Clear mux exclusions to allow different bit tests. + """ + self.bank.graph_clear_column_mux(port) def create_modules(self): """ diff --git a/compiler/sram/sram_config.py b/compiler/sram/sram_config.py index 5175770d..2eb64b87 100644 --- a/compiler/sram/sram_config.py +++ b/compiler/sram/sram_config.py @@ -121,7 +121,8 @@ class sram_config: self.row_addr_size = ceil(log(self.num_rows, 2)) self.col_addr_size = int(log(self.words_per_row, 2)) self.bank_addr_size = self.col_addr_size + self.row_addr_size - self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) + #self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2)) + self.addr_size = self.bank_addr_size debug.info(1, "Row addr size: {}".format(self.row_addr_size) + " Col addr size: {}".format(self.col_addr_size) + " Bank addr size: {}".format(self.bank_addr_size)) diff --git a/compiler/sram/sram_multibank.py b/compiler/sram/sram_multibank.py index 741d510c..65ba80ca 100644 --- a/compiler/sram/sram_multibank.py +++ b/compiler/sram/sram_multibank.py @@ -1,27 +1,32 @@ from template import template from globals import OPTS +import os +from math import ceil, log 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') + rw_ports = [i for i in sram.all_ports if i in sram.read_ports and i in sram.write_ports] + r_ports = [i for i in sram.all_ports if i in sram.read_ports and i not in sram.write_ports] + w_ports = [i for i in sram.all_ports if i not in sram.read_ports and i in sram.write_ports] + self.dict = { + 'module_name': OPTS.output_name, + 'bank_module_name': OPTS.output_name + '_1bank', + 'vdd': 'vdd', + 'gnd': 'gnd', + 'ports': sram.all_ports, + 'rw_ports': rw_ports, + 'r_ports': r_ports, + 'w_ports': w_ports, + 'banks': list(range(sram.num_banks)), + 'data_width': sram.word_size, + 'addr_width': sram.addr_size + ceil(log(sram.num_banks, 2)), + 'bank_sel': ceil(log(sram.num_banks, 2)), + 'num_wmask': sram.num_wmasks + } + def verilog_write(self, name): + template_filename = os.path.join(os.path.abspath(os.environ["OPENRAM_HOME"]), "sram/sram_multibank_template.v") + t = template(template_filename, self.dict) + t.write(name) diff --git a/compiler/sram/sram_multibank_template.v b/compiler/sram/sram_multibank_template.v index 0e24954a..f26643d8 100644 --- a/compiler/sram/sram_multibank_template.v +++ b/compiler/sram/sram_multibank_template.v @@ -126,7 +126,7 @@ module {{ module_name }} ( end {% endfor %} -{% for port in ports %} +{% for port in rw_ports %} always @(*) begin {% for bank in banks %} csb{{ port }}_bank{{ bank }} = 1'b1; @@ -143,4 +143,35 @@ module {{ module_name }} ( end {% endfor %} +{% for port in w_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 %} + +{% for port in r_ports %} + always @(*) begin +{% for bank in banks %} + csb{{ port }}_bank{{ bank }} = 1'b1; +{% endfor %} + case (addr{{ port }}[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL]) +{% for bank in banks %} + {{ bank }}: begin + csb{{ port }}_bank{{ bank }} = csb{{ port }}; + end +{% endfor %} + endcase + end +{% endfor %} endmodule diff --git a/compiler/verilogTemplate/template.py b/compiler/verilog_template/template.py similarity index 98% rename from compiler/verilogTemplate/template.py rename to compiler/verilog_template/template.py index d48321b5..623ceb41 100644 --- a/compiler/verilogTemplate/template.py +++ b/compiler/verilog_template/template.py @@ -1,5 +1,6 @@ import re + class baseSection: children = [] @@ -7,6 +8,7 @@ class baseSection: for c in self.children: c.expand(dict, fd) + class loopSection(baseSection): def __init__(self, var, key): @@ -22,12 +24,13 @@ class loopSection(baseSection): if self.var in dict: del dict[self.var] + class textSection(baseSection): - + def __init__(self, text): self.text = text - def expand(self, dict): + def expand(self, dict, fd): var_re = re.compile('\{\{ (\S*) \}\}') vars = var_re.finditer(self.text) newText = self.text @@ -35,6 +38,7 @@ class textSection(baseSection): newText = newText.replace('{{ ' + var.group(1) + ' }}', str(dict[var.group(1)])) print(newText, end='', file=fd) + class template: def __init__(self, template, dict): @@ -69,4 +73,3 @@ class template: self.readTemplate() self.baseSectionSection.expand(self.dict, fd) fd.close() - diff --git a/compiler/verilogTemplate/test.py b/compiler/verilog_template/test.py similarity index 100% rename from compiler/verilogTemplate/test.py rename to compiler/verilog_template/test.py From 3ebb7195350b7533e2ec50c16162f8dc17930634 Mon Sep 17 00:00:00 2001 From: Bugra Onal Date: Thu, 16 Jun 2022 15:12:43 -0700 Subject: [PATCH 13/13] Added conditional sections to template --- compiler/sram/sram_multibank_template.v | 22 +++++-- compiler/tests/sram_1b_16_1rw_scn4m_subm.log | 2 - compiler/tests/sram_1b_16_1rw_sky130.log | 3 - compiler/verilog_template/template.py | 61 +++++++++++++++++--- compiler/verilog_template/test.py | 20 ------- 5 files changed, 70 insertions(+), 38 deletions(-) delete mode 100644 compiler/tests/sram_1b_16_1rw_scn4m_subm.log delete mode 100644 compiler/tests/sram_1b_16_1rw_sky130.log delete mode 100644 compiler/verilog_template/test.py diff --git a/compiler/sram/sram_multibank_template.v b/compiler/sram/sram_multibank_template.v index f26643d8..86b1dc2e 100644 --- a/compiler/sram/sram_multibank_template.v +++ b/compiler/sram/sram_multibank_template.v @@ -9,7 +9,9 @@ module {{ module_name }} ( addr{{ port }}, din{{ port }}, csb{{ port }}, + {% if write_size > 1 %} wmask{{ port }}, + {% endif %} web{{ port }}, dout{{ port }}, {% endfor %} @@ -24,7 +26,9 @@ module {{ module_name }} ( addr{{ port }}, din{{ port }}, csb{{ port }}, + {% if write_size > 1 %} wmask{{ port }}, + {% endif %} web{{ port }}, {% endfor %} ); @@ -36,8 +40,8 @@ module {{ module_name }} ( parameter NUM_WMASK = {{ num_wmask }}; `ifdef USE_POWER_PINS - inout vdd; - inout gnd; + inout {{ vdd }}; + inout {{ gnd }}; `endif {% for port in rw_ports %} input clk{{ port }}; @@ -45,7 +49,9 @@ module {{ module_name }} ( input [DATA_WIDTH - 1: 0] din{{ port }}; input csb{{ port }}; input web{{ port }}; + {% if write_size > 1 %} input [NUM_WMASK - 1 : 0] wmask{{ port }}; + {% endif %} output reg [DATA_WIDTH - 1 : 0] dout{{ port }}; {% endfor %} {% for port in r_ports %} @@ -60,7 +66,9 @@ module {{ module_name }} ( input [DATA_WIDTH - 1: 0] din{{ port }}; input csb{{ port }}; input web{{ port }}; + {% if write_size > 1 %} input [NUM_WMASK - 1 : 0] wmask{{ port }}; + {% endif %} {% endfor %} {% for port in ports %} @@ -79,8 +87,8 @@ module {{ module_name }} ( {% for bank in banks %} {{ bank_module_name }} bank{{ bank }} ( `ifdef USE_POWER_PINS - .vdd(vdd), - .gnd(gnd), + .{{ vdd }}({{ vdd }}), + .{{ gnd }}({{ gnd }}), `endif {% for port in rw_ports %} .clk{{ port }}(clk{{ port }}), @@ -88,7 +96,9 @@ module {{ module_name }} ( .din{{ port }}(din{{ port }}), .csb{{ port }}(csb{{ port }}_bank{{ bank }}), .web{{ port }}(web{{ port }}_bank{{ bank }}), + {% if write_size > 1 %} .wmask{{ port }}(wmask{{ port }}), + {% endif %} .dout{{ port }}(dout{{ port }}_bank{{ bank }}), {% endfor %} {% for port in r_ports %} @@ -102,8 +112,10 @@ module {{ module_name }} ( .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 }}), + {% if write_size > 1 %} .wmask{{ port }}(wmask{{ port }}), + {% endif %} + .web{{ port }}(web{{ port }}_bank{{ bank }}), {% endfor %} ); {% endfor %} diff --git a/compiler/tests/sram_1b_16_1rw_scn4m_subm.log b/compiler/tests/sram_1b_16_1rw_scn4m_subm.log deleted file mode 100644 index 1d80b5f9..00000000 --- a/compiler/tests/sram_1b_16_1rw_scn4m_subm.log +++ /dev/null @@ -1,2 +0,0 @@ -ERROR: file magic.py: line 358: sram LVS mismatch (results in /tmp/openram_bugra_12868_temp/sram.lvs.report) - diff --git a/compiler/tests/sram_1b_16_1rw_sky130.log b/compiler/tests/sram_1b_16_1rw_sky130.log deleted file mode 100644 index f6367750..00000000 --- a/compiler/tests/sram_1b_16_1rw_sky130.log +++ /dev/null @@ -1,3 +0,0 @@ -ERROR: file design.py: line 47: Custom cell pin names do not match spice file: -['D', 'Q', 'CLK', 'VDD', 'GND'] vs [] - diff --git a/compiler/verilog_template/template.py b/compiler/verilog_template/template.py index 623ceb41..e9399d22 100644 --- a/compiler/verilog_template/template.py +++ b/compiler/verilog_template/template.py @@ -1,7 +1,18 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2021 Regents of the University of California +# Santa Cruz +# All rights reserved. +# import re class baseSection: + """ + This is the base section class for other section classes to inherit. + It is also used as the top most section. + """ + children = [] def expand(self, dict, fd): @@ -10,9 +21,11 @@ class baseSection: class loopSection(baseSection): - + """ + This section is for looping elements. It will repeat the children + sections based on the key list. + """ def __init__(self, var, key): - self.children = [] self.var = var self.key = key @@ -25,14 +38,33 @@ class loopSection(baseSection): del dict[self.var] +class conditionalSection(baseSection): + """ + This class will conditionally print it's children based on the 'cond' + element. + """ + def __init__(self, cond): + self.cond = cond + + def expand(self, dict, fd): + run = eval(self.cond, dict) + if run: + for c in self.children: + c.expand(dict, fd) + + class textSection(baseSection): + """ + This is plain text section. It can contain parameters that can be + replaced based on the dictionary. + """ def __init__(self, text): self.text = text def expand(self, dict, fd): - var_re = re.compile('\{\{ (\S*) \}\}') - vars = var_re.finditer(self.text) + varRE = re.compile('\{\{ (\S*) \}\}') + vars = varRE.finditer(self.text) newText = self.text for var in vars: newText = newText.replace('{{ ' + var.group(1) + ' }}', str(dict[var.group(1)])) @@ -40,6 +72,10 @@ class textSection(baseSection): class template: + """ + The template class will read a template and generate an output file + based on the template and the given dictionary. + """ def __init__(self, template, dict): self.template = template @@ -53,17 +89,26 @@ class template: self.baseSectionSection = baseSection() sections = [] context = [self.baseSectionSection] - for_re = re.compile('\{% for (\S*) in (\S*) %\}') - end_re = re.compile('\{% endfor %\}') + forRE = re.compile('\{% for (\S*) in (\S*) %\}') + endforRE = re.compile('\{% endfor %\}') + ifRE = re.compile('\{% if (.*) %\}') + endifRE = re.compile('\{% endif %\}') for line in lines: - m = for_re.match(line) + m = forRE.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): + m = ifRE.match(line) + if m: + section = conditionalSection(m.group(1)) + section.append(section) + context[-1].children.append(section) + context.append(section) + continue + if endforRE.match(line) or endifRE.match(line): context.pop() else: context[-1].children.append(textSection(line)) diff --git a/compiler/verilog_template/test.py b/compiler/verilog_template/test.py deleted file mode 100644 index 228b5042..00000000 --- a/compiler/verilog_template/test.py +++ /dev/null @@ -1,20 +0,0 @@ -from template import template - -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') -