From 5de7ff377356e6cf7d4694fc134673a85f6ee198 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 11 Jan 2019 14:15:16 -0800 Subject: [PATCH] Updated Verilog to have multiport. Added 1rw,1rw/1r Verilog testbench. --- compiler/base/hierarchy_layout.py | 3 +- compiler/base/hierarchy_spice.py | 3 +- compiler/base/lef.py | 3 +- compiler/base/verilog.py | 185 ++++++++++++++---- .../example_configs/big_config_scn4m_subm.py | 14 ++ .../example_config_1rw_1r_scn4m_subm.py | 20 ++ .../example_config_freepdk45.py | 0 .../example_config_scn4m_subm.py | 0 .../medium_config_scn4m_subm.py | 14 ++ compiler/openram.py | 2 +- compiler/sram.py | 11 +- compiler/sram_1bank.py | 2 +- compiler/sram_base.py | 10 +- compiler/tests/sram_1rw_1r_tb.v | 143 ++++++++++++++ compiler/tests/sram_1rw_tb.v | 103 ++++++++++ 15 files changed, 459 insertions(+), 54 deletions(-) create mode 100644 compiler/example_configs/big_config_scn4m_subm.py create mode 100644 compiler/example_configs/example_config_1rw_1r_scn4m_subm.py rename compiler/{ => example_configs}/example_config_freepdk45.py (100%) rename compiler/{ => example_configs}/example_config_scn4m_subm.py (100%) create mode 100644 compiler/example_configs/medium_config_scn4m_subm.py create mode 100644 compiler/tests/sram_1rw_1r_tb.v create mode 100644 compiler/tests/sram_1rw_tb.v diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py index 9843dcef..c35f0709 100644 --- a/compiler/base/hierarchy_layout.py +++ b/compiler/base/hierarchy_layout.py @@ -10,7 +10,7 @@ from vector import vector from pin_layout import pin_layout import lef -class layout(lef.lef): +class layout(): """ Class consisting of a set of objs and instances for a module This provides a set of useful generic types for hierarchy @@ -21,7 +21,6 @@ class layout(lef.lef): """ def __init__(self, name): - lef.lef.__init__(self, ["metal1", "metal2", "metal3"]) self.name = name self.width = None self.height = None diff --git a/compiler/base/hierarchy_spice.py b/compiler/base/hierarchy_spice.py index ff87f9a5..3fda8598 100644 --- a/compiler/base/hierarchy_spice.py +++ b/compiler/base/hierarchy_spice.py @@ -2,9 +2,8 @@ import debug import re import os import math -import verilog -class spice(verilog.verilog): +class spice(): """ This provides a set of useful generic types for hierarchy management. If a module is a custom designed cell, it will read from diff --git a/compiler/base/lef.py b/compiler/base/lef.py index fb790403..270057cb 100644 --- a/compiler/base/lef.py +++ b/compiler/base/lef.py @@ -9,7 +9,8 @@ from collections import defaultdict class lef: """ SRAM LEF Class open GDS file, read pins information, obstruction - and write them to LEF file + and write them to LEF file. + This is inherited by the sram_base class. """ def __init__(self,layers): # LEF db units per micron diff --git a/compiler/base/verilog.py b/compiler/base/verilog.py index c4bbcd0c..7d713eca 100644 --- a/compiler/base/verilog.py +++ b/compiler/base/verilog.py @@ -1,60 +1,165 @@ import debug class verilog: - """ Create a behavioral Verilog file for simulation.""" - + """ + Create a behavioral Verilog file for simulation. + This is inherited by the sram_base class. + """ + def __init__(self): + 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\n".format(self.word_size)) - - self.vf.write("module {0}(DATA,ADDR,CSb,WEb,OEb,clk);\n".format(self.name)) - self.vf.write("\n") + + self.vf.write("module {0}(\n".format(self.name)) + for port in self.all_ports: + if port in self.readwrite_ports: + self.vf.write("// Port {0}: RW\n".format(port)) + elif port in self.read_ports: + self.vf.write("// Port {0}: R\n".format(port)) + elif port in self.write_ports: + self.vf.write("// Port {0}: W\n".format(port)) + if port in self.readwrite_ports: + self.vf.write(" clk{0},csb{0},web{0},ADDR{0},DIN{0},DOUT{0}".format(port)) + elif port in self.write_ports: + self.vf.write(" clk{0},csb{0},ADDR{0},DIN{0}".format(port)) + elif port in self.read_ports: + self.vf.write(" clk{0},csb{0},ADDR{0},DOUT{0}".format(port)) + # Continue for every port on a new line + if port != self.all_ports[-1]: + self.vf.write(",\n") + self.vf.write("\n );\n\n") + self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size)) self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(self.addr_size)) self.vf.write(" parameter RAM_DEPTH = 1 << ADDR_WIDTH;\n") + self.vf.write(" // FIXME: This delay is arbitrary.\n") self.vf.write(" parameter DELAY = 3 ;\n") - self.vf.write("\n") - self.vf.write(" inout [DATA_WIDTH-1:0] DATA;\n") - self.vf.write(" input [ADDR_WIDTH-1:0] ADDR;\n") - self.vf.write(" input CSb; // active low chip select\n") - self.vf.write(" input WEb; // active low write control\n") - self.vf.write(" input OEb; // active output enable\n") - self.vf.write(" input clk; // clock\n") self.vf.write("\n") - self.vf.write(" reg [DATA_WIDTH-1:0] data_out ;\n") - self.vf.write(" reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];\n") + + for port in self.all_ports: + self.add_inputs_outputs(port) + self.vf.write("\n") - self.vf.write(" // Tri-State Buffer control\n") - self.vf.write(" // output : When WEb = 1, oeb = 0, csb = 0\n") - self.vf.write(" assign DATA = (!CSb && !OEb && WEb) ? data_out : {0}'bz;\n".format(self.word_size)) - self.vf.write("\n") - self.vf.write(" // Memory Write Block\n") - self.vf.write(" // Write Operation : When WEb = 0, CSb = 0\n") - self.vf.write(" always @ (posedge clk)\n") - self.vf.write(" begin : MEM_WRITE\n") - self.vf.write(" if ( !CSb && !WEb ) begin\n") - self.vf.write(" mem[ADDR] = DATA;\n") - self.vf.write(" $display($time,\" Writing %m ABUS=%b DATA=%b\",ADDR,DATA);\n") - self.vf.write(" end\n") - self.vf.write(" end\n\n") - self.vf.write("\n") - self.vf.write(" // Memory Read Block\n") - self.vf.write(" // Read Operation : When WEb = 1, CSb = 0\n") - self.vf.write(" always @ (posedge clk)\n") - self.vf.write(" begin : MEM_READ\n") - self.vf.write(" if (!CSb && WEb) begin\n") - self.vf.write(" data_out <= #(DELAY) mem[ADDR];\n") - self.vf.write(" $display($time,\" Reading %m ABUS=%b DATA=%b\",ADDR,mem[ADDR]);\n") - self.vf.write(" end\n") - self.vf.write(" end\n") + + for port in self.all_ports: + self.register_inputs(port) + + # This is the memory array itself + self.vf.write("reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];\n") + + for port in self.all_ports: + if port in self.write_ports: + self.add_write_block(port) + if port in self.read_ports: + self.add_read_block(port) + self.vf.write("\n") self.vf.write("endmodule\n") - - self.vf.close() + + def register_inputs(self, port): + """ + Register the control signal, address and data inputs. + """ + self.add_regs(port) + self.add_flops(port) + + def add_regs(self, port): + """ + Create the input regs for the given port. + """ + self.vf.write(" reg csb{0}_reg;\n".format(port)) + if port in self.readwrite_ports: + self.vf.write(" reg web{0}_reg;\n".format(port)) + 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)) + if port in self.read_ports: + self.vf.write(" reg [DATA_WIDTH-1:0] DOUT{0};\n".format(port)) + + def add_flops(self, port): + """ + Add the flop behavior logic for a port. + """ + self.vf.write("\n") + self.vf.write(" // All inputs are registers\n") + self.vf.write(" always @(posedge clk{0})\n".format(port)) + self.vf.write(" begin\n") + self.vf.write(" csb{0}_reg = csb{0};\n".format(port)) + if port in self.readwrite_ports: + self.vf.write(" web{0}_reg = web{0};\n".format(port)) + self.vf.write(" ADDR{0}_reg = ADDR{0};\n".format(port)) + if port in self.write_ports: + self.vf.write(" DIN{0}_reg = DIN{0};\n".format(port)) + if port in self.read_ports: + self.vf.write(" DOUT{0} = {1}'bx;\n".format(port,self.word_size)) + if port in self.readwrite_ports: + self.vf.write(" if ( !csb{0}_reg && web{0}_reg ) \n".format(port)) + self.vf.write(" $display($time,\" Reading %m ADDR{0}=%b DOUT{0}=%b\",ADDR{0}_reg,mem[ADDR{0}_reg]);\n".format(port)) + elif port in self.read_ports: + self.vf.write(" if ( !csb{0}_reg ) \n".format(port)) + self.vf.write(" $display($time,\" Reading %m ADDR{0}=%b DOUT{0}=%b\",ADDR{0}_reg,mem[ADDR{0}_reg]);\n".format(port)) + + if port in self.readwrite_ports: + self.vf.write(" if ( !csb{0}_reg && !web{0}_reg )\n".format(port)) + self.vf.write(" $display($time,\" Writing %m ADDR{0}=%b DIN{0}=%b\",ADDR{0}_reg,DIN{0}_reg);\n".format(port)) + elif port in self.write_ports: + self.vf.write(" if ( !csb{0}_reg )\n".format(port)) + self.vf.write(" $display($time,\" Writing %m ADDR{0}=%b DIN{0}=%b\",ADDR{0}_reg,DIN{0}_reg);\n".format(port)) + self.vf.write(" end\n\n") + + + def add_inputs_outputs(self, port): + """ + Add the module input and output declaration for a port. + """ + self.vf.write(" input clk{0}; // clock\n".format(port)) + self.vf.write(" input csb{0}; // active low chip select\n".format(port)) + if port in self.readwrite_ports: + self.vf.write(" input web{0}; // active low write control\n".format(port)) + self.vf.write(" input [ADDR_WIDTH-1:0] ADDR{0};\n".format(port)) + if port in self.write_ports: + self.vf.write(" input [DATA_WIDTH-1:0] DIN{0};\n".format(port)) + if port in self.read_ports: + self.vf.write(" output [DATA_WIDTH-1:0] DOUT{0};\n".format(port)) + + def add_write_block(self, port): + """ + Add a write port block. Multiple simultaneous writes to the same address + have arbitrary priority and are not allowed. + """ + self.vf.write("\n") + self.vf.write(" // Memory Write Block Port {0}\n".format(port)) + self.vf.write(" // Write Operation : When web{0} = 0, csb{0} = 0\n".format(port)) + self.vf.write(" always @ (negedge clk{0})\n".format(port)) + self.vf.write(" begin : MEM_WRITE{0}\n".format(port)) + if port in self.readwrite_ports: + self.vf.write(" if ( !csb{0}_reg && !web{0}_reg )\n".format(port)) + else: + self.vf.write(" if (!csb{0}_reg)\n".format(port)) + self.vf.write(" mem[ADDR{0}_reg] = DIN{0}_reg;\n".format(port)) + self.vf.write(" end\n") + + def add_read_block(self, port): + """ + Add a read port block. + """ + self.vf.write("\n") + self.vf.write(" // Memory Read Block Port {0}\n".format(port)) + self.vf.write(" // Read Operation : When web{0} = 1, csb{0} = 0\n".format(port)) + self.vf.write(" always @ (negedge clk{0})\n".format(port)) + self.vf.write(" begin : MEM_READ{0}\n".format(port)) + if port in self.readwrite_ports: + self.vf.write(" if (!csb{0}_reg && web{0}_reg)\n".format(port)) + else: + self.vf.write(" if (!csb{0}_reg)\n".format(port)) + self.vf.write(" DOUT{0} <= #(DELAY) mem[ADDR{0}_reg];\n".format(port)) + self.vf.write(" end\n") + diff --git a/compiler/example_configs/big_config_scn4m_subm.py b/compiler/example_configs/big_config_scn4m_subm.py new file mode 100644 index 00000000..0f71169b --- /dev/null +++ b/compiler/example_configs/big_config_scn4m_subm.py @@ -0,0 +1,14 @@ +word_size = 8 +num_words = 128 + +tech_name = "scn4m_subm" +process_corners = ["TT"] +supply_voltages = [ 5.0 ] +temperatures = [ 25 ] + +output_path = "temp" +output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) + +drc_name = "magic" +lvs_name = "netgen" +pex_name = "magic" diff --git a/compiler/example_configs/example_config_1rw_1r_scn4m_subm.py b/compiler/example_configs/example_config_1rw_1r_scn4m_subm.py new file mode 100644 index 00000000..4d09cfee --- /dev/null +++ b/compiler/example_configs/example_config_1rw_1r_scn4m_subm.py @@ -0,0 +1,20 @@ +word_size = 2 +num_words = 16 + +bitcell = "bitcell_1rw_1r" +replica_bitcell = "replica_bitcell_1rw_1r" +num_rw_ports = 1 +num_r_ports = 1 +num_w_ports = 0 + +tech_name = "scn4m_subm" +process_corners = ["TT"] +supply_voltages = [5.0] +temperatures = [25] + +output_path = "temp" +output_name = "sram_1rw_1r_{0}_{1}_{2}".format(word_size,num_words,tech_name) + +drc_name = "magic" +lvs_name = "netgen" +pex_name = "magic" diff --git a/compiler/example_config_freepdk45.py b/compiler/example_configs/example_config_freepdk45.py similarity index 100% rename from compiler/example_config_freepdk45.py rename to compiler/example_configs/example_config_freepdk45.py diff --git a/compiler/example_config_scn4m_subm.py b/compiler/example_configs/example_config_scn4m_subm.py similarity index 100% rename from compiler/example_config_scn4m_subm.py rename to compiler/example_configs/example_config_scn4m_subm.py diff --git a/compiler/example_configs/medium_config_scn4m_subm.py b/compiler/example_configs/medium_config_scn4m_subm.py new file mode 100644 index 00000000..5faebb58 --- /dev/null +++ b/compiler/example_configs/medium_config_scn4m_subm.py @@ -0,0 +1,14 @@ +word_size = 16 +num_words = 256 + +tech_name = "scn4m_subm" +process_corners = ["TT"] +supply_voltages = [ 3.3 ] +temperatures = [ 25 ] + +output_path = "temp" +output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name) + +drc_name = "magic" +lvs_name = "netgen" +pex_name = "magic" diff --git a/compiler/openram.py b/compiler/openram.py index 9c98037c..78241f6a 100755 --- a/compiler/openram.py +++ b/compiler/openram.py @@ -50,7 +50,7 @@ print("Words per row: {}".format(c.words_per_row)) output_extensions = ["sp","v","lib","py","html"] if not OPTS.netlist_only: output_extensions.extend(["gds","lef"]) -output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in output_extensions] +output_files = ["{0}{1}.{2}".format(OPTS.output_path,OPTS.output_name,x) for x in output_extensions] print("Output files are: ") print(*output_files,sep="\n") diff --git a/compiler/sram.py b/compiler/sram.py index 47e5176a..4971de08 100644 --- a/compiler/sram.py +++ b/compiler/sram.py @@ -48,6 +48,9 @@ class sram(): def sp_write(self,name): self.s.sp_write(name) + def lef_write(self,name): + self.s.lef_write(name) + def gds_write(self,name): self.s.gds_write(name) @@ -63,21 +66,21 @@ class sram(): start_time = datetime.datetime.now() gdsname = OPTS.output_path + self.s.name + ".gds" print("GDS: Writing to {0}".format(gdsname)) - self.s.gds_write(gdsname) + self.gds_write(gdsname) print_time("GDS", datetime.datetime.now(), start_time) # Create a LEF physical model start_time = datetime.datetime.now() lefname = OPTS.output_path + self.s.name + ".lef" print("LEF: Writing to {0}".format(lefname)) - self.s.lef_write(lefname) + self.lef_write(lefname) print_time("LEF", datetime.datetime.now(), start_time) # Save the spice file start_time = datetime.datetime.now() spname = OPTS.output_path + self.s.name + ".sp" print("SP: Writing to {0}".format(spname)) - self.s.sp_write(spname) + self.sp_write(spname) print_time("Spice writing", datetime.datetime.now(), start_time) # Save the extracted spice file @@ -126,5 +129,5 @@ class sram(): start_time = datetime.datetime.now() vname = OPTS.output_path + self.s.name + ".v" print("Verilog: Writing to {0}".format(vname)) - self.s.verilog_write(vname) + self.verilog_write(vname) print_time("Verilog", datetime.datetime.now(), start_time) diff --git a/compiler/sram_1bank.py b/compiler/sram_1bank.py index 6b2caf30..cbfec653 100644 --- a/compiler/sram_1bank.py +++ b/compiler/sram_1bank.py @@ -21,7 +21,7 @@ class sram_1bank(sram_base): """ def __init__(self, name, sram_config): sram_base.__init__(self, name, sram_config) - + def create_modules(self): """ This adds the modules for a single bank SRAM with control diff --git a/compiler/sram_base.py b/compiler/sram_base.py index 9e2b6e2a..b51ebae9 100644 --- a/compiler/sram_base.py +++ b/compiler/sram_base.py @@ -8,14 +8,18 @@ from vector import vector from globals import OPTS, print_time import logical_effort from design import design - -class sram_base(design): +from verilog import verilog +from lef import lef + +class sram_base(design, verilog, lef): """ Dynamically generated SRAM by connecting banks to control logic. The number of banks should be 1 , 2 or 4 """ def __init__(self, name, sram_config): design.__init__(self, name) + lef.__init__(self, ["metal1", "metal2", "metal3"]) + verilog.__init__(self) self.sram_config = sram_config sram_config.set_local_config(self) @@ -531,4 +535,4 @@ class sram_base(design): return bank_sen_cin - \ No newline at end of file + diff --git a/compiler/tests/sram_1rw_1r_tb.v b/compiler/tests/sram_1rw_1r_tb.v new file mode 100644 index 00000000..9c212e0a --- /dev/null +++ b/compiler/tests/sram_1rw_1r_tb.v @@ -0,0 +1,143 @@ +`define assert(signal, value) \ +if (!(signal === value)) begin \ + $display("ASSERTION FAILED in %m: signal != value"); \ + $finish;\ +end + +module sram_1rw_1r_tb; + reg clk; + + // 1rw port + reg [3:0] addr0; + reg [1:0] din0; + reg csb0; + reg web0; + wire [1:0] dout0; + + // 1r port + reg [3:0] addr1; + reg csb1; + wire [1:0] dout1; + + sram_1rw_1r_2_16_scn4m_subm U0 (.DIN0(din0), + .DOUT0(dout0), + .ADDR0(addr0), + .csb0(csb0), + .web0(web0), + .clk0(clk), + .DOUT1(dout1), + .ADDR1(addr1), + .csb1(csb1), + .clk1(clk) + ); + + + initial + begin + + //$monitor("%g addr0=%b din0=%b dout0=%b addr1=%b dout1=%b", + // $time, addr0, din0, dout0, addr1, dout1); + + clk = 1; + csb0 = 1; + web0 = 1; + addr0 = 0; + din0 = 0; + + csb1 = 1; + addr1 = 0; + + // write + #10 din0=2'b10; + addr0=4'h1; + web0 = 0; + csb0 = 0; + // nop + csb1 = 1; + addr1 = 0; + + // write another + #10 din0=2'b01; + addr0=4'hC; + web0 = 0; + csb0 = 0; + // read last + csb1 = 0; + addr1 = 4'h1; + + #10 `assert(dout1, 2'b10) + + // read undefined + din0=2'b11; + addr0=4'h0; + web0 = 1; + csb0 = 0; + // read another + csb1 = 0; + addr1 = 4'hC; + + #10 `assert(dout0, 2'bxx) + `assert(dout1, 2'b01) + + // read defined + din0=2'b11; + addr0=4'hC; + web0 = 1; + csb0 = 0; + // read undefined + csb1 = 0; + addr1 = 4'hD; + + #10 `assert(dout0, 2'b01) + `assert(dout1, 2'bxx) + + // write another + din0=2'b11; + addr0=4'hA; + web0 = 0; + csb0 = 0; + // read the feedthrough value + csb1 = 0; + addr1 = 4'hA; + + #10 `assert(dout1, 2'b11) + + // read defined + din0=2'b11; + addr0=4'h1; + web0 = 1; + csb0 = 0; + // read old value + csb1 = 0; + addr1 = 4'h1; + + #10 `assert(dout0, 2'b10) + `assert(dout1, 2'b10) + + // read defined + din0=2'b11; + addr0=4'hA; + web0 = 1; + csb0 = 0; + // dual read + csb1 = 0; + addr1 = 4'hA; + #10 `assert(dout0, 2'b11) + `assert(dout1, 2'b11) + + // read undefined + din0=2'b11; + addr0=4'h0; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'bxx) + + #10 $finish; + + end + + always + #5 clk = !clk; + +endmodule diff --git a/compiler/tests/sram_1rw_tb.v b/compiler/tests/sram_1rw_tb.v new file mode 100644 index 00000000..31f120e8 --- /dev/null +++ b/compiler/tests/sram_1rw_tb.v @@ -0,0 +1,103 @@ +`define assert(signal, value) \ +if (!(signal === value)) begin \ + $display("ASSERTION FAILED in %m: signal != value"); \ + $finish;\ +end + +module sram_1rw_tb; + reg clk; + + reg [3:0] addr0; + reg [1:0] din0; + reg csb0; + reg web0; + wire [1:0] dout0; + + sram_2_16_scn4m_subm U0 (.DIN0(din0), + .DOUT0(dout0), + .ADDR0(addr0), + .csb0(csb0), + .web0(web0), + .clk0(clk) + ); + + + initial + begin + + //$monitor("%g addr0=%b din0=%b dout0=%b", + // $time, addr0, din0, dout0); + + + clk = 1; + csb0 = 1; + web0 = 1; + addr0 = 0; + din0 = 0; + + // write + #10 din0=2'b10; + addr0=4'h1; + web0 = 0; + csb0 = 0; + + // write another + #10 din0=2'b01; + addr0=4'hC; + web0 = 0; + csb0 = 0; + + // read undefined + #10 din0=2'b11; + addr0=4'h0; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'bxx) + + // read defined + din0=2'b11; + addr0=4'hC; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'b01) + + // write another + din0=2'b11; + addr0=4'hA; + web0 = 0; + csb0 = 0; + + // read defined + #10 din0=2'b11; + addr0=4'h1; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'b10) + + // read defined + din0=2'b11; + addr0=4'hA; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'b11) + + // read undefined + din0=2'b11; + addr0=4'h0; + web0 = 1; + csb0 = 0; + + #10 `assert(dout0, 2'bxx) + + #10 $finish; + + end + + always + #5 clk = !clk; + +endmodule