Updated Verilog to have multiport. Added 1rw,1rw/1r Verilog testbench.

This commit is contained in:
Matt Guthaus 2019-01-11 14:15:16 -08:00
parent f0ab155172
commit 5de7ff3773
15 changed files with 459 additions and 54 deletions

View File

@ -10,7 +10,7 @@ from vector import vector
from pin_layout import pin_layout from pin_layout import pin_layout
import lef import lef
class layout(lef.lef): class layout():
""" """
Class consisting of a set of objs and instances for a module Class consisting of a set of objs and instances for a module
This provides a set of useful generic types for hierarchy This provides a set of useful generic types for hierarchy
@ -21,7 +21,6 @@ class layout(lef.lef):
""" """
def __init__(self, name): def __init__(self, name):
lef.lef.__init__(self, ["metal1", "metal2", "metal3"])
self.name = name self.name = name
self.width = None self.width = None
self.height = None self.height = None

View File

@ -2,9 +2,8 @@ import debug
import re import re
import os import os
import math import math
import verilog
class spice(verilog.verilog): class spice():
""" """
This provides a set of useful generic types for hierarchy This provides a set of useful generic types for hierarchy
management. If a module is a custom designed cell, it will read from management. If a module is a custom designed cell, it will read from

View File

@ -9,7 +9,8 @@ from collections import defaultdict
class lef: class lef:
""" """
SRAM LEF Class open GDS file, read pins information, obstruction 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): def __init__(self,layers):
# LEF db units per micron # LEF db units per micron

View File

@ -1,60 +1,165 @@
import debug import debug
class verilog: 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): def verilog_write(self,verilog_name):
""" Write a behavioral Verilog model. """ """ Write a behavioral Verilog model. """
self.vf = open(verilog_name, "w") self.vf = open(verilog_name, "w")
self.vf.write("// OpenRAM SRAM model\n") self.vf.write("// OpenRAM SRAM model\n")
self.vf.write("// Words: {0}\n".format(self.num_words)) 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("// 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("module {0}(\n".format(self.name))
self.vf.write("\n") 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 DATA_WIDTH = {0} ;\n".format(self.word_size))
self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(self.addr_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(" 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(" 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("\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("\n")
self.vf.write(" // Tri-State Buffer control\n")
self.vf.write(" // output : When WEb = 1, oeb = 0, csb = 0\n") for port in self.all_ports:
self.vf.write(" assign DATA = (!CSb && !OEb && WEb) ? data_out : {0}'bz;\n".format(self.word_size)) self.register_inputs(port)
self.vf.write("\n")
self.vf.write(" // Memory Write Block\n") # This is the memory array itself
self.vf.write(" // Write Operation : When WEb = 0, CSb = 0\n") self.vf.write("reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];\n")
self.vf.write(" always @ (posedge clk)\n")
self.vf.write(" begin : MEM_WRITE\n") for port in self.all_ports:
self.vf.write(" if ( !CSb && !WEb ) begin\n") if port in self.write_ports:
self.vf.write(" mem[ADDR] = DATA;\n") self.add_write_block(port)
self.vf.write(" $display($time,\" Writing %m ABUS=%b DATA=%b\",ADDR,DATA);\n") if port in self.read_ports:
self.vf.write(" end\n") self.add_read_block(port)
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")
self.vf.write("\n") self.vf.write("\n")
self.vf.write("endmodule\n") self.vf.write("endmodule\n")
self.vf.close() 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")

View File

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

View File

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

View File

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

View File

@ -50,7 +50,7 @@ print("Words per row: {}".format(c.words_per_row))
output_extensions = ["sp","v","lib","py","html"] output_extensions = ["sp","v","lib","py","html"]
if not OPTS.netlist_only: if not OPTS.netlist_only:
output_extensions.extend(["gds","lef"]) 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 are: ")
print(*output_files,sep="\n") print(*output_files,sep="\n")

View File

@ -48,6 +48,9 @@ class sram():
def sp_write(self,name): def sp_write(self,name):
self.s.sp_write(name) self.s.sp_write(name)
def lef_write(self,name):
self.s.lef_write(name)
def gds_write(self,name): def gds_write(self,name):
self.s.gds_write(name) self.s.gds_write(name)
@ -63,21 +66,21 @@ class sram():
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
gdsname = OPTS.output_path + self.s.name + ".gds" gdsname = OPTS.output_path + self.s.name + ".gds"
print("GDS: Writing to {0}".format(gdsname)) print("GDS: Writing to {0}".format(gdsname))
self.s.gds_write(gdsname) self.gds_write(gdsname)
print_time("GDS", datetime.datetime.now(), start_time) print_time("GDS", datetime.datetime.now(), start_time)
# Create a LEF physical model # Create a LEF physical model
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
lefname = OPTS.output_path + self.s.name + ".lef" lefname = OPTS.output_path + self.s.name + ".lef"
print("LEF: Writing to {0}".format(lefname)) print("LEF: Writing to {0}".format(lefname))
self.s.lef_write(lefname) self.lef_write(lefname)
print_time("LEF", datetime.datetime.now(), start_time) print_time("LEF", datetime.datetime.now(), start_time)
# Save the spice file # Save the spice file
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
spname = OPTS.output_path + self.s.name + ".sp" spname = OPTS.output_path + self.s.name + ".sp"
print("SP: Writing to {0}".format(spname)) 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) print_time("Spice writing", datetime.datetime.now(), start_time)
# Save the extracted spice file # Save the extracted spice file
@ -126,5 +129,5 @@ class sram():
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
vname = OPTS.output_path + self.s.name + ".v" vname = OPTS.output_path + self.s.name + ".v"
print("Verilog: Writing to {0}".format(vname)) print("Verilog: Writing to {0}".format(vname))
self.s.verilog_write(vname) self.verilog_write(vname)
print_time("Verilog", datetime.datetime.now(), start_time) print_time("Verilog", datetime.datetime.now(), start_time)

View File

@ -21,7 +21,7 @@ class sram_1bank(sram_base):
""" """
def __init__(self, name, sram_config): def __init__(self, name, sram_config):
sram_base.__init__(self, name, sram_config) sram_base.__init__(self, name, sram_config)
def create_modules(self): def create_modules(self):
""" """
This adds the modules for a single bank SRAM with control This adds the modules for a single bank SRAM with control

View File

@ -8,14 +8,18 @@ from vector import vector
from globals import OPTS, print_time from globals import OPTS, print_time
import logical_effort import logical_effort
from design import design from design import design
from verilog import verilog
class sram_base(design): from lef import lef
class sram_base(design, verilog, lef):
""" """
Dynamically generated SRAM by connecting banks to control logic. The Dynamically generated SRAM by connecting banks to control logic. The
number of banks should be 1 , 2 or 4 number of banks should be 1 , 2 or 4
""" """
def __init__(self, name, sram_config): def __init__(self, name, sram_config):
design.__init__(self, name) design.__init__(self, name)
lef.__init__(self, ["metal1", "metal2", "metal3"])
verilog.__init__(self)
self.sram_config = sram_config self.sram_config = sram_config
sram_config.set_local_config(self) sram_config.set_local_config(self)
@ -531,4 +535,4 @@ class sram_base(design):
return bank_sen_cin return bank_sen_cin

View File

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

View File

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