3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-07 18:05:24 +00:00

Merge pull request #3403 from KrystalDelusion/mem-tests

This commit is contained in:
N. Engelhardt 2023-02-20 18:27:24 +01:00 committed by GitHub
commit c8966722d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 1696 additions and 14 deletions

View file

@ -22,7 +22,6 @@ module sync_ram_sp #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=10)
endmodule // sync_ram_sp
`default_nettype none
module sync_ram_sdp #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=10)
(input wire clk, write_enable,
input wire [DATA_WIDTH-1:0] data_in,
@ -45,3 +44,33 @@ module sync_ram_sdp #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=10)
endmodule // sync_ram_sdp
module sync_ram_tdp #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=10)
(input wire clk_a, clk_b,
input wire write_enable_a, write_enable_b,
input wire read_enable_a, read_enable_b,
input wire [DATA_WIDTH-1:0] write_data_a, write_data_b,
input wire [ADDRESS_WIDTH-1:0] addr_a, addr_b,
output reg [DATA_WIDTH-1:0] read_data_a, read_data_b);
localparam WORD = (DATA_WIDTH-1);
localparam DEPTH = (2**ADDRESS_WIDTH-1);
reg [WORD:0] mem [0:DEPTH];
always @(posedge clk_a) begin
if (write_enable_a)
mem[addr_a] <= write_data_a;
else
read_data_a <= mem[addr_a];
end
always @(posedge clk_b) begin
if (write_enable_b)
mem[addr_b] <= write_data_b;
else
read_data_b <= mem[addr_b];
end
endmodule // sync_ram_tdp

View file

@ -0,0 +1,32 @@
0x8000,0x8324,0x8647,0x896a,0x8c8b,0x8fab,0x92c7,0x95e1,
0x98f8,0x9c0b,0x9f19,0xa223,0xa527,0xa826,0xab1f,0xae10,
0xb0fb,0xb3de,0xb6b9,0xb98c,0xbc56,0xbf17,0xc1cd,0xc47a,
0xc71c,0xc9b3,0xcc3f,0xcebf,0xd133,0xd39a,0xd5f5,0xd842,
0xda82,0xdcb3,0xded7,0xe0eb,0xe2f1,0xe4e8,0xe6cf,0xe8a6,
0xea6d,0xec23,0xedc9,0xef5e,0xf0e2,0xf254,0xf3b5,0xf504,
0xf641,0xf76b,0xf884,0xf989,0xfa7c,0xfb5c,0xfc29,0xfce3,
0xfd89,0xfe1d,0xfe9c,0xff09,0xff61,0xffa6,0xffd8,0xfff5,
0xffff,0xfff5,0xffd8,0xffa6,0xff61,0xff09,0xfe9c,0xfe1d,
0xfd89,0xfce3,0xfc29,0xfb5c,0xfa7c,0xf989,0xf884,0xf76b,
0xf641,0xf504,0xf3b5,0xf254,0xf0e2,0xef5e,0xedc9,0xec23,
0xea6d,0xe8a6,0xe6cf,0xe4e8,0xe2f1,0xe0eb,0xded7,0xdcb3,
0xda82,0xd842,0xd5f5,0xd39a,0xd133,0xcebf,0xcc3f,0xc9b3,
0xc71c,0xc47a,0xc1cd,0xbf17,0xbc56,0xb98c,0xb6b9,0xb3de,
0xb0fb,0xae10,0xab1f,0xa826,0xa527,0xa223,0x9f19,0x9c0b,
0x98f8,0x95e1,0x92c7,0x8fab,0x8c8b,0x896a,0x8647,0x8324,
0x8000,0x7cdb,0x79b8,0x7695,0x7374,0x7054,0x6d38,0x6a1e,
0x6707,0x63f4,0x60e6,0x5ddc,0x5ad8,0x57d9,0x54e0,0x51ef,
0x4f04,0x4c21,0x4946,0x4673,0x43a9,0x40e8,0x3e32,0x3b85,
0x38e3,0x364c,0x33c0,0x3140,0x2ecc,0x2c65,0x2a0a,0x27bd,
0x257d,0x234c,0x2128,0x1f14,0x1d0e,0x1b17,0x1930,0x1759,
0x1592,0x13dc,0x1236,0x10a1,0xf1d,0xdab,0xc4a,0xafb,
0x9be,0x894,0x77b,0x676,0x583,0x4a3,0x3d6,0x31c,
0x276,0x1e2,0x163,0xf6,0x9e,0x59,0x27,0xa,
0x0,0xa,0x27,0x59,0x9e,0xf6,0x163,0x1e2,
0x276,0x31c,0x3d6,0x4a3,0x583,0x676,0x77b,0x894,
0x9be,0xafb,0xc4a,0xdab,0xf1d,0x10a1,0x1236,0x13dc,
0x1592,0x1759,0x1930,0x1b17,0x1d0e,0x1f14,0x2128,0x234c,
0x257d,0x27bd,0x2a0a,0x2c65,0x2ecc,0x3140,0x33c0,0x364c,
0x38e3,0x3b85,0x3e32,0x40e8,0x43a9,0x4673,0x4946,0x4c21,
0x4f04,0x51ef,0x54e0,0x57d9,0x5ad8,0x5ddc,0x60e6,0x63f4,
0x6707,0x6a1e,0x6d38,0x7054,0x7374,0x7695,0x79b8,0x7cdb,

View file

@ -0,0 +1,23 @@
read_verilog <<EOT
module top(
input clk,
output reg [15:0] sig1, sig2
);
reg [7:0] ptr1, ptr2;
reg [15:0] mem [0:255];
initial begin
$readmemh("bug1836.mem", mem);
end
always @(posedge clk) begin
sig1 <= mem[ptr1];
ptr1 <= ptr1 + 3;
sig2 <= mem[ptr2];
ptr2 <= ptr2 + 7;
end
endmodule
EOT
synth_ecp5 -top top
select -assert-count 1 t:DP16KD

View file

@ -260,3 +260,13 @@ setattr -set logic_block 1 m:memory
synth_ecp5 -top sync_rom; cd sync_rom
select -assert-count 0 t:DP16KD # requested LUTROM explicitly
select -assert-min 9 t:LUT4
# ============================== TDP RAM ==============================
# RAM bits <= 18K; Data width <= 18x2; Address width <= 9: -> DP16KD
design -reset; read_verilog -defer ../common/blockram.v
chparam -set ADDRESS_WIDTH 9 -set DATA_WIDTH 18 sync_ram_tdp
hierarchy -top sync_ram_tdp
synth_ecp5 -top sync_ram_tdp; cd sync_ram_tdp
select -assert-count 1 t:DP16KD
select -assert-none t:LUT4

View file

@ -6,6 +6,14 @@ cd sync_ram_sdp
select -assert-count 1 t:CC_BUFG
select -assert-count 1 t:CC_BRAM_20K
# 512 x 20 bit x 2 -> CC_BRAM_20K TDP RAM
design -reset
read_verilog ../common/blockram.v
chparam -set ADDRESS_WIDTH 9 -set DATA_WIDTH 20 sync_ram_tdp
synth_gatemate -top sync_ram_tdp -noiopad
select -assert-count 2 t:CC_BUFG
select -assert-count 1 t:CC_BRAM_20K
# 512 x 80 bit -> CC_BRAM_40K SDP RAM
design -reset
read_verilog ../common/blockram.v

22
tests/arch/ice40/spram.v Normal file
View file

@ -0,0 +1,22 @@
module top (clk, write_enable, read_enable, write_data, addr, read_data);
parameter DATA_WIDTH = 8;
parameter ADDR_WIDTH = 8;
parameter SKIP_RDEN = 1;
input clk;
input write_enable, read_enable;
input [DATA_WIDTH - 1 : 0] write_data;
input [ADDR_WIDTH - 1 : 0] addr;
output [DATA_WIDTH - 1 : 0] read_data;
(* ram_style = "huge" *)
reg [DATA_WIDTH - 1 : 0] mem [2**ADDR_WIDTH - 1 : 0];
always @(posedge clk) begin
if (write_enable)
mem[addr] <= write_data;
else if (SKIP_RDEN || read_enable)
read_data <= mem[addr];
end
endmodule

15
tests/arch/ice40/spram.ys Normal file
View file

@ -0,0 +1,15 @@
read_verilog spram.v
hierarchy -top top
synth_ice40
select -assert-count 1 t:SB_SPRAM256KA
select -assert-none t:SB_SPRAM256KA %% t:* %D
# Testing with pattern as described in pattern document
design -reset
read_verilog spram.v
chparam -set SKIP_RDEN 0
hierarchy -top top
synth_ice40
select -assert-count 1 t:SB_SPRAM256KA
# Below fails due to extra SB_LUT4
# select -assert-none t:SB_SPRAM256KA %% t:* %D

View file

@ -1,6 +1,6 @@
read_verilog ../common/blockram.v
chparam -set ADDRESS_WIDTH 10 -set DATA_WIDTH 10 sync_ram_sdp
synth_intel_alm -family cyclonev -noiopad -noclkbuf
synth_intel_alm -top sync_ram_sdp -family cyclonev -noiopad -noclkbuf
cd sync_ram_sdp
select -assert-count 1 t:MISTRAL_NOT
select -assert-count 1 t:MISTRAL_M10K

View file

@ -0,0 +1,50 @@
# Memory bits <= 18K; Data width <= 36; Address width <= 14: -> RAMB18E1
# w4b | r16b
design -reset
read_verilog asym_ram_sdp_read_wider.v
synth_xilinx -top asym_ram_sdp_read_wider -noiopad
select -assert-count 1 t:RAMB18E1
# w8b | r16b
design -reset
read_verilog asym_ram_sdp_read_wider.v
chparam -set WIDTHA 8 -set SIZEA 512 -set ADDRWIDTHA 9 asym_ram_sdp_read_wider
synth_xilinx -top asym_ram_sdp_read_wider -noiopad
select -assert-count 1 t:RAMB18E1
# w4b | r32b
design -reset
read_verilog asym_ram_sdp_read_wider.v
chparam -set WIDTHB 32 -set SIZEB 128 -set ADDRWIDTHB 7 asym_ram_sdp_read_wider
synth_xilinx -top asym_ram_sdp_read_wider -noiopad
select -assert-count 1 t:RAMB18E1
# w16b | r4b
design -reset
read_verilog asym_ram_sdp_write_wider.v
synth_xilinx -top asym_ram_sdp_write_wider -noiopad
select -assert-count 1 t:RAMB18E1
# w16b | r8b
design -reset
read_verilog asym_ram_sdp_write_wider.v
chparam -set WIDTHB 8 -set SIZEB 512 -set ADDRWIDTHB 9 asym_ram_sdp_read_wider
synth_xilinx -top asym_ram_sdp_write_wider -noiopad
select -assert-count 1 t:RAMB18E1
# w32b | r4b
design -reset
read_verilog asym_ram_sdp_write_wider.v
chparam -set WIDTHA 32 -set SIZEA 128 -set ADDRWIDTHA 7 asym_ram_sdp_read_wider
synth_xilinx -top asym_ram_sdp_write_wider -noiopad
select -assert-count 1 t:RAMB18E1
# w4b | r24b
design -reset
read_verilog asym_ram_sdp_read_wider.v
chparam -set SIZEA 768
chparam -set WIDTHB 24 -set SIZEB 128 -set ADDRWIDTHB 7 asym_ram_sdp_read_wider
synth_xilinx -top asym_ram_sdp_read_wider -noiopad
select -assert-count 1 t:RAMB18E1

View file

@ -0,0 +1,72 @@
// Asymmetric port RAM
// Read Wider than Write. Read Statement in loop
//asym_ram_sdp_read_wider.v
module asym_ram_sdp_read_wider (clkA, clkB, enaA, weA, enaB, addrA, addrB, diA, doB);
parameter WIDTHA = 4;
parameter SIZEA = 1024;
parameter ADDRWIDTHA = 10;
parameter WIDTHB = 16;
parameter SIZEB = 256;
parameter ADDRWIDTHB = 8;
input clkA;
input clkB;
input weA;
input enaA, enaB;
input [ADDRWIDTHA-1:0] addrA;
input [ADDRWIDTHB-1:0] addrB;
input [WIDTHA-1:0] diA;
output [WIDTHB-1:0] doB;
`define max(a,b) {(a) > (b) ? (a) : (b)}
`define min(a,b) {(a) < (b) ? (a) : (b)}
function integer log2;
input integer value;
reg [31:0] shifted;
integer res;
begin
if (value < 2)
log2 = value;
else
begin
shifted = value-1;
for (res=0; shifted>0; res=res+1)
shifted = shifted>>1;
log2 = res;
end
end
endfunction
localparam maxSIZE = `max(SIZEA, SIZEB);
localparam maxWIDTH = `max(WIDTHA, WIDTHB);
localparam minWIDTH = `min(WIDTHA, WIDTHB);
localparam RATIO = maxWIDTH / minWIDTH;
localparam log2RATIO = log2(RATIO);
reg [minWIDTH-1:0] RAM [0:maxSIZE-1];
reg [WIDTHB-1:0] readB;
always @(posedge clkA)
begin
if (enaA) begin
if (weA)
RAM[addrA] <= diA;
end
end
always @(posedge clkB)
begin : ramread
integer i;
reg [log2RATIO-1:0] lsbaddr;
if (enaB) begin
for (i = 0; i < RATIO; i = i+1) begin
lsbaddr = i;
readB[(i+1)*minWIDTH-1 -: minWIDTH] <= RAM[{addrB, lsbaddr}];
end
end
end
assign doB = readB;
endmodule

View file

@ -0,0 +1,71 @@
// Asymmetric port RAM
// Write wider than Read. Write Statement in a loop.
// asym_ram_sdp_write_wider.v
module asym_ram_sdp_write_wider (clkA, clkB, weA, enaA, enaB, addrA, addrB, diA, doB);
parameter WIDTHB = 4;
parameter SIZEB = 1024;
parameter ADDRWIDTHB = 10;
parameter WIDTHA = 16;
parameter SIZEA = 256;
parameter ADDRWIDTHA = 8;
input clkA;
input clkB;
input weA;
input enaA, enaB;
input [ADDRWIDTHA-1:0] addrA;
input [ADDRWIDTHB-1:0] addrB;
input [WIDTHA-1:0] diA;
output [WIDTHB-1:0] doB;
`define max(a,b) {(a) > (b) ? (a) : (b)}
`define min(a,b) {(a) < (b) ? (a) : (b)}
function integer log2;
input integer value;
reg [31:0] shifted;
integer res;
begin
if (value < 2)
log2 = value;
else
begin
shifted = value-1;
for (res=0; shifted>0; res=res+1)
shifted = shifted>>1;
log2 = res;
end
end
endfunction
localparam maxSIZE = `max(SIZEA, SIZEB);
localparam maxWIDTH = `max(WIDTHA, WIDTHB);
localparam minWIDTH = `min(WIDTHA, WIDTHB);
localparam RATIO = maxWIDTH / minWIDTH;
localparam log2RATIO = log2(RATIO);
reg [minWIDTH-1:0] RAM [0:maxSIZE-1];
reg [WIDTHB-1:0] readB;
always @(posedge clkB) begin
if (enaB) begin
readB <= RAM[addrB];
end
end
assign doB = readB;
always @(posedge clkA)
begin : ramwrite
integer i;
reg [log2RATIO-1:0] lsbaddr;
for (i=0; i< RATIO; i= i+ 1) begin : write1
lsbaddr = i;
if (enaA) begin
if (weA)
RAM[{addrA, lsbaddr}] <= diA[(i+1)*minWIDTH-1 -: minWIDTH];
end
end
end
endmodule

View file

@ -0,0 +1,122 @@
module priority_memory (
clk, wren_a, rden_a, addr_a, wdata_a, rdata_a,
wren_b, rden_b, addr_b, wdata_b, rdata_b
);
parameter ABITS = 12;
parameter WIDTH = 72;
input clk;
input wren_a, rden_a, wren_b, rden_b;
input [ABITS-1:0] addr_a, addr_b;
input [WIDTH-1:0] wdata_a, wdata_b;
output reg [WIDTH-1:0] rdata_a, rdata_b;
`ifdef USE_HUGE
(* ram_style = "huge" *)
`endif
reg [WIDTH-1:0] mem [0:2**ABITS-1];
integer i;
initial begin
rdata_a <= 'h0;
rdata_b <= 'h0;
end
`ifndef FLIP_PORTS
always @(posedge clk) begin
// A port
if (wren_a)
mem[addr_a] <= wdata_a;
else if (rden_a)
rdata_a <= mem[addr_a];
// B port
if (wren_b)
mem[addr_b] <= wdata_b;
else if (rden_b)
if (wren_a && addr_a == addr_b)
rdata_b <= wdata_a;
else
rdata_b <= mem[addr_b];
end
`else // FLIP PORTS
always @(posedge clk) begin
// A port
if (wren_b)
mem[addr_b] <= wdata_b;
else if (rden_b)
rdata_b <= mem[addr_b];
// B port
if (wren_a)
mem[addr_a] <= wdata_a;
else if (rden_a)
if (wren_b && addr_a == addr_b)
rdata_a <= wdata_b;
else
rdata_a <= mem[addr_a];
end
`endif
endmodule
module sp_write_first (clk, wren_a, rden_a, addr_a, wdata_a, rdata_a);
parameter ABITS = 12;
parameter WIDTH = 72;
input clk;
input wren_a, rden_a;
input [ABITS-1:0] addr_a;
input [WIDTH-1:0] wdata_a;
output reg [WIDTH-1:0] rdata_a;
(* ram_style = "huge" *)
reg [WIDTH-1:0] mem [0:2**ABITS-1];
integer i;
initial begin
rdata_a <= 'h0;
end
always @(posedge clk) begin
// A port
if (wren_a)
mem[addr_a] <= wdata_a;
if (rden_a)
if (wren_a)
rdata_a <= wdata_a;
else
rdata_a <= mem[addr_a];
end
endmodule
module sp_read_first (clk, wren_a, rden_a, addr_a, wdata_a, rdata_a);
parameter ABITS = 12;
parameter WIDTH = 72;
input clk;
input wren_a, rden_a;
input [ABITS-1:0] addr_a;
input [WIDTH-1:0] wdata_a;
output reg [WIDTH-1:0] rdata_a;
(* ram_style = "huge" *)
reg [WIDTH-1:0] mem [0:2**ABITS-1];
integer i;
initial begin
rdata_a <= 'h0;
end
always @(posedge clk) begin
// A port
if (wren_a)
mem[addr_a] <= wdata_a;
if (rden_a)
rdata_a <= mem[addr_a];
end
endmodule

View file

@ -0,0 +1,60 @@
# no uram by default
design -reset
read_verilog priority_memory.v
synth_xilinx -family xcup -top priority_memory
select -assert-none t:URAM288
# uram parameter
design -reset
read -define USE_HUGE
read_verilog priority_memory.v
synth_xilinx -family xcup -top priority_memory -noiopad
select -assert-count 1 t:URAM288
# uram option
design -reset
read_verilog priority_memory.v
synth_xilinx -family xcup -top priority_memory -noiopad -uram
# check for URAM block
select -assert-count 1 t:URAM288
# check port A in code maps to port A in hardware:
# %co:+[DOUT_A] selects everything connected to a URAM288.DOUT_A port
# w:rdata_a selects the wire rdata_a
# %i finds the intersection of the two above selections
# if the result is 1 then the wire rdata_a is connected to Port A correctly
select -assert-count 1 t:URAM288 %co:+[DOUT_A] w:rdata_a %i
# we expect no more than 2 LUT2s to control the hardware priority
# if there are extra LUTs, then it is likely emulating logic it shouldn't
# ignore anything using blif, since that doesn't seem to support priority logic
# and is indicative of using verific/tabby
select -assert-max 2 t:LUT* n:*blif* %d
# reverse priority
design -reset
read -define FLIP_PORTS
read_verilog priority_memory.v
synth_xilinx -family xcup -top priority_memory -noiopad -uram
# test priority is mapped correctly, rdata_a should now be connected to Port B
# see above for details
select -assert-count 1 t:URAM288 %co:+[DOUT_B] w:rdata_a %i
# sp write first
design -reset
read_verilog priority_memory.v
synth_xilinx -family xcup -top sp_write_first -noiopad
select -assert-count 1 t:URAM288
# write first connects rdata_a to port B
# similar to above, but also tests that rdata_a *isn't* connected to port A
select -assert-none 1 t:URAM288 %co:+[DOUT_A] w:rdata_a %i
select -assert-count 1 t:URAM288 %co:+[DOUT_B] w:rdata_a %i
# sp read first
design -reset
read_verilog priority_memory.v
synth_xilinx -family xcup -top sp_read_first -noiopad
select -assert-count 1 t:URAM288
# read first connects rdata_a to port A
# see above for details
select -assert-count 1 t:URAM288 %co:+[DOUT_A] w:rdata_a %i
select -assert-none 1 t:URAM288 %co:+[DOUT_B] w:rdata_a %i

View file

@ -1,13 +1,6 @@
# TODO:
# - memory initialization
# - clock polarity combinations
# - CE/srst/rdwr/be interactions
# - priority logic
# - byte enables, wrbe_separate
# - duplication for read ports
# - abits/dbits determination
# - mixed width
# - swizzles for weird width progressions
@ -22,6 +15,7 @@ class Test:
TESTS = []
### basic sanity tests
# Asynchronous-read RAM
ASYNC = """
module top(clk, ra, wa, rd, wd, we);
@ -56,6 +50,7 @@ TESTS += [
Test("async_small_block", ASYNC_SMALL, ["block_tdp"], [], {"RAM_BLOCK_TDP": 0}),
]
# Synchronous SDP read first
SYNC = """
module top(clk, ra, wa, rd, wd, we);
@ -95,6 +90,261 @@ TESTS += [
Test("sync_small_block_attr", SYNC_SMALL_BLOCK, ["lut", "block_tdp"], [], {"RAM_BLOCK_TDP": 1}),
]
### initialization values testing
LUT_INIT = """
module top(clk, ra, wa, rd, wd, we);
localparam ABITS = {abits};
localparam DBITS = {dbits};
input wire clk;
input wire we;
input wire [ABITS-1:0] ra, wa;
input wire [DBITS-1:0] wd;
output wire [DBITS-1:0] rd;
reg [DBITS-1:0] mem [0:2**ABITS-1];
integer i;
initial
for (i = 0; i < 2**ABITS-1; i = i + 1)
mem[i] = {ival};
always @(posedge clk)
if (we)
mem[wa] <= wd;
assign rd = mem[ra];
endmodule
"""
INIT_LUT_ZEROS = LUT_INIT.format(abits=4, dbits=4, ival=0);
INIT_LUT_VAL = LUT_INIT.format(abits=4, dbits=4, ival=5);
INIT_LUT_VAL2 = LUT_INIT.format(abits=6, dbits=6, ival="6'h12");
INIT_LUT_X = LUT_INIT.format(abits=4, dbits=4, ival="4'hx")
TESTS += [
Test("init_lut_zeros_zero", INIT_LUT_ZEROS, ["lut"], ["INIT_ZERO"], {"RAM_LUT":1}),
Test("init_lut_zeros_any", INIT_LUT_ZEROS, ["lut"], ["INIT_ANY"], {"RAM_LUT":1}),
Test("init_lut_val_zero", INIT_LUT_VAL, ["lut"], ["INIT_ZERO"], {"RAM_LUT":0}), #CHECK: no emulation?
Test("init_lut_val_any", INIT_LUT_VAL, ["lut"], ["INIT_ANY"], {"RAM_LUT":1}),
Test("init_lut_val_no_undef", INIT_LUT_VAL, ["lut"], ["INIT_NO_UNDEF"], {"RAM_LUT":1}),
Test("init_lut_val2_any", INIT_LUT_VAL2, ["lut"], ["INIT_ANY"], {"RAM_LUT":8}),
Test("init_lut_val2_no_undef", INIT_LUT_VAL2, ["lut"], ["INIT_NO_UNDEF"], {"RAM_LUT":8}),
Test("init_lut_x_none", INIT_LUT_X, ["lut"], ["INIT_NONE"], {"RAM_LUT":1}),
Test("init_lut_x_zero", INIT_LUT_X, ["lut"], ["INIT_ZERO"], {"RAM_LUT":1}),
Test("init_lut_x_any", INIT_LUT_X, ["lut"], ["INIT_ANY"], {"RAM_LUT":1}),
Test("init_lut_x_no_undef", INIT_LUT_X, ["lut"], ["INIT_NO_UNDEF"], {"RAM_LUT":1}),
]
### width testing 9-bit-per-byte
RAM_9b1B = """
module top(clk, ra, wa, rd, wd, we);
localparam ABITS = {abits};
localparam DBITS = {dbits};
input wire clk;
input wire we;
input wire [ABITS-1:0] ra, wa;
input wire [DBITS-1:0] wd;
output reg [DBITS-1:0] rd;
reg [DBITS-1:0] mem [0:2**ABITS-1];
always @(posedge clk)
if (we)
mem[wa] <= wd;
always @(posedge clk)
rd <= mem[ra];
endmodule
"""
RAM_18b2B = RAM_9b1B.format(abits=3, dbits=18);
RAM_9b1B = RAM_9b1B.format(abits=4, dbits=9);
RAM_4b1B = RAM_9b1B.format(abits=5, dbits=4);
RAM_2b1B = RAM_9b1B.format(abits=6, dbits=2);
RAM_1b1B = RAM_9b1B.format(abits=7, dbits=1);
TESTS += [
Test("ram_18b2B", RAM_18b2B, ["9b1B"], [], {"RAM_9b1B":1}),
Test("ram_9b1B", RAM_9b1B, ["9b1B"], [], {"RAM_9b1B":1}),
Test("ram_4b1B", RAM_4b1B, ["9b1B"], [], {"RAM_9b1B":1}),
Test("ram_2b1B", RAM_2b1B, ["9b1B"], [], {"RAM_9b1B":1}),
Test("ram_1b1B", RAM_1b1B, ["9b1B"], [], {"RAM_9b1B":1}),
]
### initializing 9-bits-per-byte
RAM_9b1B_init = """
module top(clk, ra, wa, rd, wd, we);
localparam ABITS = {abits};
localparam DBITS = {dbits};
input wire clk;
input wire we;
input wire [ABITS-1:0] ra, wa;
input wire [DBITS-1:0] wd;
output reg [DBITS-1:0] rd;
reg [DBITS-1:0] mem [0:2**ABITS-1];
integer i;
initial
for (i = 0; i < 2**ABITS-1; i = i + 1)
mem[i] = {ival};
always @(posedge clk)
if (we)
mem[wa] <= wd;
always @(posedge clk)
rd <= mem[ra];
endmodule
"""
INIT_9b1B_ZEROS = RAM_9b1B_init.format(abits=4, dbits=9, ival=0);
INIT_9b1B_VAL = RAM_9b1B_init.format(abits=4, dbits=9, ival=275);
INIT_13b2B_VAL = RAM_9b1B_init.format(abits=3, dbits=13, ival="13'h01f3")
INIT_18b2B_VAL = RAM_9b1B_init.format(abits=4, dbits=18, ival="18'h1f39a");
INIT_4b1B_X = RAM_9b1B_init.format(abits=5, dbits=4, ival="4'hx")
TESTS += [
Test("init_9b1B_zeros_zero", INIT_9b1B_ZEROS, ["9b1B"], ["INIT_ZERO"], {"RAM_9b1B":1}),
Test("init_9b1B_zeros_any", INIT_9b1B_ZEROS, ["9b1B"], ["INIT_ANY"], {"RAM_9b1B":1}),
Test("init_9b1B_val_zero", INIT_9b1B_VAL, ["9b1B"], ["INIT_ZERO"], {"RAM_9b1B":0}), #CHECK: no emulation?
Test("init_9b1B_val_any", INIT_9b1B_VAL, ["9b1B"], ["INIT_ANY"], {"RAM_9b1B":1}),
Test("init_9b1B_val_no_undef", INIT_9b1B_VAL, ["9b1B"], ["INIT_NO_UNDEF"], {"RAM_9b1B":1}),
Test("init_13b2B_val_any", INIT_13b2B_VAL, ["9b1B"], ["INIT_ANY"], {"RAM_9b1B":1}),
Test("init_18b2B_val_any", INIT_18b2B_VAL, ["9b1B"], ["INIT_ANY"], {"RAM_9b1B":2}),
Test("init_18b2B_val_no_undef", INIT_18b2B_VAL, ["9b1B"], ["INIT_NO_UNDEF"], {"RAM_9b1B":2}),
Test("init_4b1B_x_none", INIT_4b1B_X, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B":1}),
Test("init_4b1B_x_zero", INIT_4b1B_X, ["9b1B"], ["INIT_ZERO"], {"RAM_9b1B":1}),
Test("init_4b1B_x_any", INIT_4b1B_X, ["9b1B"], ["INIT_ANY"], {"RAM_9b1B":1}),
Test("init_4b1B_x_no_undef", INIT_4b1B_X, ["9b1B"], ["INIT_NO_UNDEF"], {"RAM_9b1B":1}),
]
### Clock polarity combinations
# I'm not entirely convinced auto-test is correctly testing clock edging
# but they do at least all gen/synth
SYNCCLOCK = """
module top(clk, ra, wa, rd, wd, we);
localparam ABITS = {abits};
localparam DBITS = 8;
input wire clk;
input wire we;
input wire [ABITS-1:0] ra, wa;
input wire [DBITS-1:0] wd;
output reg [DBITS-1:0] rd;
reg [DBITS-1:0] mem [0:2**ABITS-1];
always @(posedge clk)
if (we)
mem[wa] <= wd;
always @(posedge clk)
rd <= mem[ra];
endmodule
"""
for (abits, cnt, wclk, rclk, shared) in [
(4, 1, "ANY","ANY", False),
(4, 1, "ANY","NEG", False),
(4, 1, "ANY","POS", False),
(4, 1, "NEG","ANY", False),
(4, 1, "NEG","POS", False),
(4, 1, "NEG","NEG", False),
(4, 1, "POS","ANY", False),
(4, 1, "POS","NEG", False),
(4, 1, "POS","POS", False),
(4, 1, "ANY","ANY", True),
(4, 0, "NEG","POS", True), # FF mapping
(4, 1, "NEG","NEG", True),
(4, 0, "POS","NEG", True), # FF mapping
(4, 1, "POS","POS", True),
# cannot combine "ANY" with "POS|NEG" when using shared clock
]:
name = f"clock_a{abits}_w{wclk}r{rclk}s{shared}"
defs = ["WCLK_" + wclk, "RCLK_" + rclk]
if (shared):
defs.append("SHARED_CLK")
TESTS.append(Test(
name, SYNCCLOCK.format(abits=abits),
["clock_sdp"], defs, {"RAM_CLOCK_SDP": cnt}
))
### mixed width testing
# Wide write port
MIXED_WRITE = """
module top(clk, ra, wa, rd, wd, we);
localparam WABITS = {wabits};
localparam WDBITS = {wdbits};
localparam RABITS = {rabits};
localparam RDBITS = {rdbits};
input wire clk;
input wire we;
input wire [WABITS-1:0] wa;
input wire [WDBITS-1:0] wd;
input wire [RABITS-1:0] ra;
output reg [RDBITS-1:0] rd;
localparam DEPTH = (2**WABITS);
localparam OFFSET = RABITS-WABITS;
(* syn_ramstyle = "block_ram" *)
reg [WDBITS-1:0] mem [0:DEPTH-1];
always @(posedge clk)
if (we)
mem[wa] <= wd;
if (OFFSET > 0) begin
reg [WDBITS-1:0] mem_read;
reg [OFFSET-1:0] subaddr_r;
always @(posedge clk) begin
mem_read <= mem[ra[RABITS-1:OFFSET]];
subaddr_r <= ra[OFFSET-1:0];
end
always @(mem_read, subaddr_r)
rd <= mem_read[subaddr_r*RDBITS+:RDBITS];
end
else
begin
always @(posedge clk)
case (OFFSET)
0: rd <= mem[ra];
-1: rd <= {{ mem[ra], mem[ra+1] }};
endcase
end
endmodule
"""
UNMIXED = MIXED_WRITE.format(wabits=4, wdbits=9, rabits=4, rdbits=9)
MIXED_9_18 = MIXED_WRITE.format(wabits=5, wdbits=9, rabits=4, rdbits=18)
MIXED_18_9 = MIXED_WRITE.format(wabits=3, wdbits=18, rabits=4, rdbits=9)
MIXED_36_9 = MIXED_WRITE.format(wabits=3, wdbits=36, rabits=5, rdbits=9)
MIXED_4_2 = MIXED_WRITE.format(wabits=5, wdbits=4, rabits=6, rdbits=2);
TESTS += [
Test("unmixed", UNMIXED, ["9b1B"], [], {"RAM_9b1B":1}),
Test("mixed_9_18", MIXED_9_18, ["9b1B"], [], {"RAM_9b1B":4}), #CHECK: only using half the memory
Test("mixed_18_9", MIXED_18_9, ["9b1B"], [], {"RAM_9b1B":1}),
Test("mixed_36_9", MIXED_36_9, ["9b1B"], [], {"RAM_9b1B":2}),
Test("mixed_4_2", MIXED_4_2, ["9b1B"], [], {"RAM_9b1B":1}),
]
### basic TDP test
TDP = """
@ -131,7 +381,7 @@ TESTS += [
]
# shared clock
# Synchronous SDP with clock domain crossing
SYNC_2CLK = """
module top(rclk, wclk, ra, wa, rd, wd, we);
@ -163,7 +413,7 @@ TESTS += [
]
# inter-port transparency
# Synchronous SDP with write-first behaviour
SYNC_TRANS = """
module top(clk, ra, wa, rd, wd, we);
@ -201,7 +451,7 @@ TESTS += [
]
# rdwr checks
# Synchronous single-port RAM with mutually exclusive read/write
SP_NO_CHANGE = """
module top(clk, addr, rd, wd, we);
@ -247,6 +497,7 @@ end
endmodule
"""
# Synchronous single-port RAM with write-first behaviour
SP_NEW = """
module top(clk, addr, rd, wd, we);
@ -295,6 +546,7 @@ end
endmodule
"""
# Synchronous single-port RAM with read-first behaviour
SP_OLD = """
module top(clk, addr, rd, wd, we);
@ -373,6 +625,7 @@ TESTS += [
Test("sp_old_auto_be", SP_OLD_BE, ["block_sp"], ["RDWR_NO_CHANGE", "RDWR_OLD", "RDWR_NEW"], {"RAM_BLOCK_SP": (1, {"OPTION_RDWR": "OLD"})}),
]
# Synchronous read port with initial value
SP_INIT = """
module top(clk, addr, rd, wd, we, re);
@ -418,6 +671,7 @@ TESTS += [
Test("sp_init_v_any_re", SP_INIT_V, ["block_sp"], ["RDINIT_ANY", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
]
# Synchronous read port with asynchronous reset
SP_ARST = """
module top(clk, addr, rd, wd, we, re, ar);
@ -488,6 +742,7 @@ TESTS += [
Test("sp_arst_n_init_re", SP_ARST_N, ["block_sp"], ["RDINIT_ANY", "RDARST_INIT", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
]
# Synchronous read port with synchronous reset (reset priority over enable)
SP_SRST = """
module top(clk, addr, rd, wd, we, re, sr);
@ -515,6 +770,7 @@ end
endmodule
"""
# Synchronous read port with synchronous reet (enable priority over reset)
SP_SRST_G = """
module top(clk, addr, rd, wd, we, re, sr);
@ -602,6 +858,180 @@ TESTS += [
Test("sp_srst_gv_init_re", SP_SRST_GV, ["block_sp"], ["RDINIT_ANY", "RDSRST_INIT", "RDEN", "RDWR_OLD"], {"RAM_BLOCK_SP": 1}),
]
# Byte enables, wrbe_separate
SYNC_ENABLE = """
module top(clk, rwa, rd, wd, we);
localparam ABITS = {abits};
localparam DBITS = {dbits};
input wire clk;
input wire we;
input wire [ABITS-1:0] rwa;
input wire [DBITS-1:0] wd;
output reg [DBITS-1:0] rd;
reg [DBITS-1:0] mem [0:2**ABITS-1];
always @(posedge clk) begin
if (we)
mem[rwa] <= wd;
else
rd <= mem[rwa];
end
endmodule
"""
for (abits, dbits, sep, defs, cells) in [
(4, 4, False, ["NO_BYTE"], {"RAM_WREN": 1}),
(5, 4, False, ["NO_BYTE"], {"RAM_WREN": 2}),
(6, 4, False, ["NO_BYTE"], {"RAM_WREN": 4}),
# (4, 4, True, ["NO_BYTE"], {"RAM_WREN": 1}), # should throw an error
(3, 8, False, ["NO_BYTE"], {"RAM_WREN": 2}), # needs two write ports
(4, 8, False, ["NO_BYTE"], {"RAM_WREN": 2}),
(4, 4, False, ["W4_B4"], {"RAM_WREN": 1}),
(4, 8, True, ["W4_B4"], {"RAM_WREN": 2}),
(4, 8, False, ["W8_B4"], {"RAM_WREN": 1}),
(4, 8, True, ["W8_B4"], {"RAM_WREN": 1}),
(4, 8, False, ["W8_B8"], {"RAM_WREN": 1}),
(4, 8, True, ["W8_B8"], {"RAM_WREN": 1}),
]:
name = f"wren_a{abits}d{dbits}_{defs[0]}"
if (sep):
defs.append("WRBE_SEPARATE")
name += "_separate"
TESTS.append(Test(
name, SYNC_ENABLE.format(abits=abits, dbits=dbits),
["wren"], defs, cells
))
# Write port with byte enables
ENABLES = """
module top(clk, we, be, rwa, wd, rd);
localparam ABITS = {abits};
localparam WBITS = {wbits};
localparam WORDS = {words};
input wire clk;
input wire we;
input wire [WORDS-1:0] be;
input wire [ABITS-1:0] rwa;
input wire [(WBITS*WORDS)-1:0] wd;
output reg [(WBITS*WORDS)-1:0] rd;
reg [(WBITS*WORDS)-1:0] mem [0:2**ABITS-1];
integer i;
always @(posedge clk)
for (i=0; i<WORDS; i=i+1)
if (we && be[i])
mem[rwa][i*WBITS+:WBITS] <= wd[i*WBITS+:WBITS];
always @(posedge clk)
if (!we)
rd <= mem[rwa];
endmodule
"""
for (abits, wbits, words, defs, cells) in [
(4, 2, 8, ["W16_B4"], {"RAM_WREN": 2}),
(4, 4, 4, ["W16_B4"], {"RAM_WREN": 1}),
(5, 4, 2, ["W16_B4"], {"RAM_WREN": 1}),
(5, 4, 4, ["W16_B4"], {"RAM_WREN": 2}),
(4, 8, 2, ["W16_B4"], {"RAM_WREN": 1}),
(5, 8, 1, ["W16_B4"], {"RAM_WREN": 1}),
(5, 8, 2, ["W16_B4"], {"RAM_WREN": 2}),
(4,16, 1, ["W16_B4"], {"RAM_WREN": 1}),
(4, 4, 2, ["W8_B8"], {"RAM_WREN": 2}),
(4, 4, 1, ["W8_B8"], {"RAM_WREN": 1}),
(4, 8, 2, ["W8_B8"], {"RAM_WREN": 2}),
(3, 8, 2, ["W8_B8"], {"RAM_WREN": 2}),
(4, 4, 2, ["W8_B4"], {"RAM_WREN": 1}),
(4, 2, 4, ["W8_B4"], {"RAM_WREN": 2}),
(4, 4, 4, ["W8_B4"], {"RAM_WREN": 2}),
(4, 4, 4, ["W4_B4"], {"RAM_WREN": 4}),
(4, 4, 5, ["W4_B4"], {"RAM_WREN": 5}),
]:
name = f"wren_a{abits}d{wbits}w{words}_{defs[0]}"
TESTS.append(Test(
name, ENABLES.format(abits=abits, wbits=wbits, words=words),
["wren"], defs, cells
))
defs.append("WRBE_SEPARATE")
name += "_separate"
TESTS.append(Test(
name, ENABLES.format(abits=abits, wbits=wbits, words=words),
["wren"], defs, cells
))
# abits/dbits determination (aka general geometry picking)
GEOMETRIC = """
module top(clk, rwa, rd, wd, we);
localparam ABITS = {abits};
localparam DBITS = {dbits};
input wire clk;
input wire we;
input wire [ABITS-1:0] rwa;
input wire [DBITS-1:0] wd;
output reg [DBITS-1:0] rd;
(* ram_style="block" *)
reg [DBITS-1:0] mem [0:2**ABITS-1];
always @(posedge clk)
if (we)
mem[rwa] <= wd;
else
rd <= mem[rwa];
endmodule
"""
for (abits,dbits, libs, defs, cells) in [
# W64_B8 gives 16 * 64 = 1024 bits of memory with up to 8x8b rw ports
( 4, 64, ["wren"], ["W64_B8"], {"RAM_WREN": 1}),
( 5, 32, ["wren"], ["W64_B8"], {"RAM_WREN": 1}),
( 5, 64, ["wren"], ["W64_B8"], {"RAM_WREN": 2}),
( 6, 16, ["wren"], ["W64_B8"], {"RAM_WREN": 1}),
( 6, 30, ["wren"], ["W64_B8"], {"RAM_WREN": 2}),
( 6, 64, ["wren"], ["W64_B8"], {"RAM_WREN": 4}),
( 7, 4, ["wren"], ["W64_B8"], {"RAM_WREN": 1}),
( 7, 6, ["wren"], ["W64_B8"], {"RAM_WREN": 1}),
( 7, 8, ["wren"], ["W64_B8"], {"RAM_WREN": 1}),
( 7, 17, ["wren"], ["W64_B8"], {"RAM_WREN": 3}),
( 8, 4, ["wren"], ["W64_B8"], {"RAM_WREN": 2}),
( 8, 6, ["wren"], ["W64_B8"], {"RAM_WREN": 2}),
( 9, 4, ["wren"], ["W64_B8"], {"RAM_WREN": 4}),
( 9, 8, ["wren"], ["W64_B8"], {"RAM_WREN": 4}),
( 9, 5, ["wren"], ["W64_B8"], {"RAM_WREN": 4}),
( 9, 6, ["wren"], ["W64_B8"], {"RAM_WREN": 4}),
# 9b1B gives 128 bits of memory with 1 2 or 4 bit read and write ports
# or 144 bits with 9 or 18 bit read and write ports
( 3, 18, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 1}),
( 4, 4, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 1}),
( 4, 18, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 2}),
( 5, 32, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 8}),
( 6, 4, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 2}),
( 7, 11, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 11}),
( 7, 18, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 16}),
( 11, 1, ["9b1B"], ["INIT_NONE"], {"RAM_9b1B": 16}),
]:
name = f"geom_a{abits}d{dbits}_{libs[0]}"
TESTS.append(Test(
name, GEOMETRIC.format(abits=abits, dbits=dbits),
libs, defs, cells
))
# Mixed width testing
WIDE_SDP = """
module top(rclk, ra, rd, re, rr, wclk, wa, wd, we);
@ -857,6 +1287,233 @@ for (aw, rw, ww, bw, cntww, cntwr) in [
{"RAM_WIDE_WRITE": cntww}
))
# Multiple read ports
# 1rw port plus 3 (or 7) r ports
QUAD_PORT = """
module top(clk, rwa, r0a, r1a, r2a, rd, r0d, r1d, r2d, wd, we);
localparam ABITS = {abits};
localparam DBITS = {dbits};
input wire clk;
input wire we;
input wire [ABITS-1:0] rwa;
input wire [ABITS-1:0] r0a;
input wire [ABITS-1:0] r1a;
input wire [ABITS-1:0] r2a;
input wire [DBITS-1:0] wd;
output wire [DBITS-1:0] rd;
output wire [DBITS-1:0] r0d;
output wire [DBITS-1:0] r1d;
output wire [DBITS-1:0] r2d;
reg [DBITS-1:0] mem [0:2**ABITS-1];
always @(posedge clk)
if (we)
mem[rwa] <= wd;
assign rd = mem[rwa];
assign r0d = mem[r0a];
assign r1d = mem[r1a];
assign r2d = mem[r2a];
endmodule
"""
for (abits, dbits, cnt) in [
(2, 2, 1),
(4, 2, 1),
(5, 2, 2),
(4, 4, 2),
(6, 2, 4),
(4, 8, 4),
]:
TESTS.append(Test(
f"quad_port_a{abits}d{dbits}",
QUAD_PORT.format(abits=abits, dbits=dbits),
["multilut"], ["PORTS_QUAD"],
{"LUT_MULTI": cnt}
))
# Wide asynchronous read port
WIDE_READ = """
module top(clk, we, rwa, wd, rd);
localparam ABITS = {abits};
localparam WBITS = {wbits};
localparam RWORDS = {rwords};
input wire clk;
input wire we;
input wire [ABITS-1:0] rwa;
input wire [WBITS-1:0] wd;
output wire [(WBITS*RWORDS)-1:0] rd;
reg [WBITS-1:0] mem [0:2**ABITS-1];
always @(posedge clk)
if (we)
mem[rwa] <= wd;
genvar i;
generate
for (i = 0; i < RWORDS; i = i + 1)
assign rd[i*WBITS+:WBITS] = mem[rwa + i];
endgenerate
endmodule
"""
for (abits, wbits, rwords, cntquad, cntoct) in [
(4, 2, 1, 1, 1),
(4, 2, 2, 1, 1),
(4, 2, 3, 1, 1),
(4, 2, 4, 1, 1),
(4, 2, 5, 2, 1),
(4, 2, 6, 2, 1),
(4, 2, 7, 2, 1), # Write port needs to be duplicated, so only 3 extra read
(4, 2, 8, 3, 1), # ports per quad port LUT (i.e. 7 ports in 2, 8 ports in 3)
(4, 2, 9, 3, 2),
(4, 4, 1, 2, 2),
(4, 4, 4, 2, 2),
(4, 4, 6, 4, 2),
(4, 4, 9, 6, 4),
(5, 2, 1, 2, 2),
(5, 2, 4, 2, 2),
(5, 2, 9, 6, 4),
]:
TESTS.append(Test(
f"wide_quad_a{abits}w{wbits}r{rwords}",
WIDE_READ.format(abits=abits, wbits=wbits, rwords=rwords),
["multilut"], ["PORTS_QUAD"],
{"LUT_MULTI": cntquad}
))
TESTS.append(Test(
f"wide_oct_a{abits}w{wbits}r{rwords}",
WIDE_READ.format(abits=abits, wbits=wbits, rwords=rwords),
["multilut"], ["PORTS_OCT"],
{"LUT_MULTI": cntoct}
))
# signal priorities & pattern testing
PRIORITY = """
module top(clk, clken, wren, wben, rden, rst, addr, wdata, rdata);
localparam ABITS = {abits};
localparam WBITS = {wbits};
localparam WORDS = {words};
localparam BITS = WBITS * WORDS;
input wire clk, clken;
input wire wren, rden, rst;
input wire [WORDS-1:0] wben;
input wire [ABITS-1:0] addr;
input wire [BITS-1:0] wdata;
output reg [BITS-1:0] rdata;
reg [BITS-1:0] mem [0:2**ABITS-1];
integer i;
always @(posedge clk) begin
{code}
end
endmodule
"""
#reset_gate in ["ungated", "rst", "rden && rst"]
#clk_en in [True, False]
#rdwr in ["nc", "old", "new", "undef", "newdef"]
for (testname, reset_gate, rdwr, clk_en, add_logic) in [
("no_reset", "", "old", False, 0),
("gclken", "rst", "old", False, 0),
("ungated", "ungated", "old", False, 1), # muxes wren with rst
("gclken_ce", "rst", "old", True, 3), # AND to simulate CLK_EN
("grden", "rden && rst", "old", False, 1), # selects _clken, simulates _rden
("grden_ce", "rden && rst", "old", True, 4), # both of the above
("exclwr", "", "nc", False, 2), # selects new_only and simulates
("excl_rst", "rst", "nc", False, 3), # as above, extra gate for rst
("transwr", "", "new", False, 0),
("trans_rst", "rst", "new", False, 0),
]:
write = "if (wren) \n\t\tmem[addr] <= wdata;"
if rdwr == "new":
read = """if (rden)
if (wren)
rdata <= wdata;
else
rdata <= mem[addr];"""
else:
read = "if (rden) \n\t\trdata <= mem[addr];"
if "rst" in reset_gate:
read = f"if ({reset_gate})\n\t\trdata <= 0; \n\telse {read}"
if reset_gate == "ungated":
outer = "if (rst)\n\trdata <= 0;\nelse "
else:
outer = ""
if clk_en:
outer = f"{outer}if (clken) "
code = f"""{outer}begin
{write}
{"else " if rdwr == "nc" else ""}{read}
end"""
TESTS.append(Test(
testname, PRIORITY.format(code=code, abits=4, wbits=8, words=2),
["block_sp_full"], ["USE_SRST"],
{"RAM_BLOCK_SP": 1, "$*": add_logic}
))
for (testname, reset_gate, defs, rdwr, add_logic) in [
("wr_byte", "", ["USE_SRST_BLOCKING"], "old", 0),
("trans_byte", "", ["USE_SRST_BLOCKING"], "new", 0),
("wr_rst_byte", "rst", ["USE_SRST"], "old", 2), # expected mux to emulate blocking
("rst_wr_byte", "rst", ["USE_SRST_BLOCKING"], "old", 2), # should use hardware blocking, doesn't
("rdenrst_wr_byte", "rden && rst", ["USE_SRST"], "old", 3),
]:
wordsloop = "for (i=0; i<WORDS; i=i+1)"
if rdwr == "old":
read_write = f"""if (rden)
rdata = mem[addr];
{wordsloop}
if (wben[i])
mem[addr][i] <= wdata[i];"""
else:
read_write = f"""{wordsloop}
if (wben[i]) begin
mem[addr][i] <= wdata[i];
rdata[i] <= wdata[i];
end else
rdata[i] <= mem[addr][i];"""
if "rst" in reset_gate:
reset_rw = f"""if ({reset_gate})
rdata <= 0;
else begin
{read_write}
end"""
else:
reset_rw = read_write
if reset_gate == "ungated":
outer = "if (rst)\n\trdata <= 0;\nelse "
else:
outer = ""
code = f"{outer}\n{reset_rw}"
TESTS.append(Test(
testname, PRIORITY.format(code=code, abits=4, wbits=1, words=2),
["block_sp_full"], defs,
{"RAM_BLOCK_SP": 1, "$*": add_logic}
))
with open("run-test.mk", "w") as mf:
mf.write("ifneq ($(strip $(SEED)),)\n")
mf.write("SEEDOPT=-S$(SEED)\n")

View file

@ -0,0 +1,31 @@
ram block \RAM_9b1B {
cost 64;
abits 7;
widths 1 2 4 9 18 per_port;
byte 9;
ifdef INIT_NONE {
option "INIT" "NONE" {
init none;
}
} else ifdef INIT_ZERO {
option "INIT" "ZERO" {
init zero;
}
} else ifdef INIT_NO_UNDEF {
option "INIT" "NO_UNDEF" {
init no_undef;
}
} else ifdef INIT_ANY {
option "INIT" "ANY" {
init any;
}
}
port sw "W" {
clock anyedge;
}
port sr "R" {
clock anyedge;
}
}

View file

@ -0,0 +1,68 @@
module RAM_9b1B (
input PORT_R_CLK,
input [6:0] PORT_R_ADDR,
output reg [PORT_R_WIDTH-1:0] PORT_R_RD_DATA,
input PORT_W_CLK,
input [PORT_W_WR_EN_WIDTH-1:0] PORT_W_WR_EN,
input [6:0] PORT_W_ADDR,
input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA
);
parameter INIT = 0;
parameter OPTION_INIT = "UNDEFINED";
parameter PORT_R_WIDTH = 9;
parameter PORT_W_WIDTH = 9;
parameter PORT_R_CLK_POL = 0;
parameter PORT_W_CLK_POL = 0;
parameter PORT_W_WR_EN_WIDTH = 1;
reg [8:0] mem [0:15];
integer i;
initial
for (i = 0; i < 16; i += 1)
case (OPTION_INIT)
"NONE": mem[i] = mem[i]; //?
"ZERO": mem[i] = 9'h0;
"ANY": mem[i] = INIT[i*9+:9];
"NO_UNDEF": mem[i] = INIT[i*9+:9];
"UNDEFINED": mem[i] = 9'hx;
endcase
wire [3:0] addr_r;
assign addr_r = PORT_R_ADDR[6:3];
reg [17:0] mem_read;
reg [2:0] subaddr_r;
always @(negedge (PORT_R_CLK ^ PORT_R_CLK_POL)) begin
subaddr_r <= PORT_R_ADDR[2:0];
mem_read[8:0] <= mem[addr_r];
if (PORT_R_WIDTH == 18)
mem_read[17:9] <= mem[addr_r + 1];
end
always @(mem_read, subaddr_r) begin
case (PORT_R_WIDTH)
18: PORT_R_RD_DATA <= mem_read;
9: PORT_R_RD_DATA <= mem_read[8:0];
4: PORT_R_RD_DATA <= mem_read[subaddr_r[2]*4+:4];
2: PORT_R_RD_DATA <= mem_read[subaddr_r[2:1]*2+:2];
1: PORT_R_RD_DATA <= mem_read[subaddr_r];
endcase
end
wire [3:0] addr_w;
assign addr_w = PORT_W_ADDR[6:3];
always @(negedge (PORT_W_CLK ^ PORT_W_CLK_POL)) begin
if (PORT_W_WR_EN[0])
case (PORT_W_WIDTH)
18,
9: mem[addr_w] <= PORT_W_WR_DATA[8:0];
4: mem[addr_w][PORT_W_ADDR[2]*4+:4] <= PORT_W_WR_DATA;
2: mem[addr_w][PORT_W_ADDR[2:1]*2+:2] <= PORT_W_WR_DATA;
1: mem[addr_w][PORT_W_ADDR[2:0]] <= PORT_W_WR_DATA;
endcase
if (PORT_W_WR_EN[1])
mem[addr_w + 1] <= PORT_W_WR_DATA[17:9];
end
endmodule

View file

@ -0,0 +1,61 @@
ram block \RAM_BLOCK_SP {
cost 2;
abits 4;
width 16;
byte 8;
port srsw "A" {
clock posedge;
clken;
rden;
option "RDWR" "NO_CHANGE" {
rdwr no_change;
}
option "RDWR" "OLD" {
rdwr old;
}
option "RDWR" "NEW" {
rdwr new;
}
option "RDWR" "NEW_ONLY" {
rdwr new_only;
}
ifdef USE_ARST {
option "RDARST" "ZERO" {
rdarst zero;
}
option "RDARST" "ANY" {
rdarst any;
}
}
ifdef USE_SRST {
option "SRST_BLOCK" 0 {
option "SRST_GATE" 0 {
rdsrst zero ungated;
}
option "SRST_GATE" 1 {
rdsrst zero gated_clken;
}
option "SRST_GATE" 2 {
rdsrst zero gated_rden;
}
}
}
ifdef USE_SRST_BLOCKING {
option "SRST_BLOCK" 1 {
option "SRST_GATE" 0 {
rdsrst zero ungated block_wr;
}
option "SRST_GATE" 1 {
rdsrst zero gated_clken block_wr;
}
option "SRST_GATE" 2 {
rdsrst zero gated_rden block_wr;
}
}
}
}
}

View file

@ -0,0 +1,82 @@
module RAM_BLOCK_SP(
input PORT_A_CLK,
input PORT_A_CLK_EN,
input PORT_A_RD_EN,
input PORT_A_RD_ARST,
input PORT_A_RD_SRST,
input [1:0] PORT_A_WR_EN,
input [3:0] PORT_A_ADDR,
output reg [15:0] PORT_A_RD_DATA,
input [15:0] PORT_A_WR_DATA
);
parameter OPTION_RDWR = "UNDEFINED";
parameter OPTION_RDINIT = "UNDEFINED";
parameter OPTION_RDARST = "UNDEFINED";
parameter OPTION_RDSRST = "ZERO";
parameter OPTION_SRST_GATE = 0;
parameter OPTION_SRST_BLOCK = 0;
parameter PORT_A_RD_INIT_VALUE = 16'hxxxx;
parameter PORT_A_RD_ARST_VALUE = 16'hxxxx;
parameter PORT_A_RD_SRST_VALUE = 16'hxxxx;
reg [15:0] mem [0:15];
initial
if (OPTION_RDINIT == "ZERO")
PORT_A_RD_DATA = 0;
else if (OPTION_RDINIT == "ANY")
PORT_A_RD_DATA = PORT_A_RD_INIT_VALUE;
localparam ARST_VALUE =
(OPTION_RDARST == "ZERO") ? 16'h0000 :
(OPTION_RDARST == "INIT") ? PORT_A_RD_INIT_VALUE :
(OPTION_RDARST == "ANY") ? PORT_A_RD_ARST_VALUE :
16'hxxxx;
localparam SRST_VALUE =
(OPTION_RDSRST == "ZERO") ? 16'h0000 :
(OPTION_RDSRST == "INIT") ? PORT_A_RD_INIT_VALUE :
(OPTION_RDSRST == "ANY") ? PORT_A_RD_SRST_VALUE :
16'hxxxx;
pulldown (PORT_A_RD_ARST);
pulldown (PORT_A_RD_SRST);
always @(posedge PORT_A_CLK) begin
if (PORT_A_CLK_EN) begin
if (!(PORT_A_RD_SRST && OPTION_SRST_BLOCK)) begin
if (PORT_A_WR_EN[0])
mem[PORT_A_ADDR][7:0] <= PORT_A_WR_DATA[7:0];
if (PORT_A_WR_EN[1])
mem[PORT_A_ADDR][15:8] <= PORT_A_WR_DATA[15:8];
end
if (PORT_A_RD_EN && (!PORT_A_WR_EN || OPTION_RDWR != "NO_CHANGE")) begin
PORT_A_RD_DATA <= mem[PORT_A_ADDR];
if (PORT_A_WR_EN && OPTION_RDWR == "NEW_ONLY")
PORT_A_RD_DATA <= 16'hx;
if (PORT_A_WR_EN[0])
case (OPTION_RDWR)
"NEW": PORT_A_RD_DATA[7:0] <= PORT_A_WR_DATA[7:0];
"NEW_ONLY": PORT_A_RD_DATA[7:0] <= PORT_A_WR_DATA[7:0];
"UNDEFINED": PORT_A_RD_DATA[7:0] <= 8'hx;
endcase
if (PORT_A_WR_EN[1])
case (OPTION_RDWR)
"NEW": PORT_A_RD_DATA[15:8] <= PORT_A_WR_DATA[15:8];
"NEW_ONLY": PORT_A_RD_DATA[15:8] <= PORT_A_WR_DATA[15:8];
"UNDEFINED": PORT_A_RD_DATA[15:8] <= 8'hx;
endcase
end
end
if (PORT_A_RD_SRST && (!OPTION_SRST_GATE || (OPTION_SRST_GATE == 2 && PORT_A_RD_EN) || (OPTION_SRST_GATE == 1 && PORT_A_CLK_EN)))
PORT_A_RD_DATA <= SRST_VALUE;
end
always @(PORT_A_RD_ARST)
if (PORT_A_RD_ARST)
force PORT_A_RD_DATA = ARST_VALUE;
else
release PORT_A_RD_DATA;
endmodule

View file

@ -0,0 +1,76 @@
ram block \RAM_CLOCK_SDP {
cost 64;
abits 10;
widths 1 2 4 8 16 per_port;
init any;
port sw "W" {
ifdef SHARED_CLK {
ifdef WCLK_ANY {
option "WCLK" "ANY" {
clock anyedge "CLK";
}
}
ifdef WCLK_POS {
option "WCLK" "POS" {
clock posedge "CLK";
}
}
ifdef WCLK_NEG {
option "WCLK" "NEG" {
clock negedge "CLK";
}
}
} else {
ifdef WCLK_ANY {
option "WCLK" "ANY" {
clock anyedge;
}
}
ifdef WCLK_POS {
option "WCLK" "POS" {
clock posedge;
}
}
ifdef WCLK_NEG {
option "WCLK" "NEG" {
clock negedge;
}
}
}
}
port sr "R" {
ifdef SHARED_CLK {
ifdef RCLK_ANY {
option "RCLK" "ANY" {
clock anyedge "CLK";
}
}
ifdef RCLK_POS {
option "RCLK" "POS" {
clock posedge "CLK";
}
}
ifdef RCLK_NEG {
option "RCLK" "NEG" {
clock negedge "CLK";
}
}
} else {
ifdef RCLK_ANY {
option "RCLK" "ANY" {
clock anyedge;
}
}
ifdef RCLK_POS {
option "RCLK" "POS" {
clock posedge;
}
}
ifdef RCLK_NEG {
option "RCLK" "NEG" {
clock negedge;
}
}
}
}
}

View file

@ -0,0 +1,36 @@
module RAM_CLOCK_SDP(
input CLK_CLK,
input PORT_R_CLK,
input [9:0] PORT_R_ADDR,
output reg [15:0] PORT_R_RD_DATA,
input PORT_W_CLK,
input PORT_W_WR_EN,
input [9:0] PORT_W_ADDR,
input [15:0] PORT_W_WR_DATA
);
parameter INIT = 0;
parameter PORT_R_WIDTH = 1;
parameter PORT_W_WIDTH = 1;
parameter CLK_CLK_POL = 0;
parameter PORT_R_CLK_POL = 0;
parameter PORT_W_CLK_POL = 0;
parameter OPTION_WCLK = "ANY";
parameter OPTION_RCLK = "ANY";
reg [2**10-1:0] mem = INIT;
wire RCLK;
case (OPTION_RCLK)
"ANY": assign RCLK = PORT_R_CLK == PORT_R_CLK_POL;
"POS": assign RCLK = PORT_R_CLK;
"NEG": assign RCLK = ~PORT_R_CLK;
endcase
always @(posedge RCLK)
PORT_R_RD_DATA <= mem[PORT_R_ADDR+:PORT_R_WIDTH];
always @(negedge PORT_W_CLK ^ (PORT_W_CLK_POL || OPTION_WCLK == "POS"))
if (PORT_W_WR_EN)
mem[PORT_W_ADDR+:PORT_W_WIDTH] <= PORT_W_WR_DATA;
endmodule

View file

@ -1,7 +1,23 @@
ram distributed \RAM_LUT {
abits 4;
width 4;
init any;
ifdef INIT_NONE {
option "INIT" "NONE" {
init none;
}
} else ifdef INIT_ZERO {
option "INIT" "ZERO" {
init zero;
}
} else ifdef INIT_NO_UNDEF {
option "INIT" "NO_UNDEF" {
init no_undef;
}
} else {
option "INIT" "ANY" {
init any;
}
}
cost 4;
port ar "R" {
}

View file

@ -9,6 +9,7 @@ module RAM_LUT(
);
parameter INIT = 0;
parameter OPTION_INIT = "UNDEFINED";
parameter PORT_RW_CLK_POL = 1;
reg [3:0] mem [0:15];
@ -16,7 +17,13 @@ reg [3:0] mem [0:15];
integer i;
initial
for (i = 0; i < 16; i += 1)
mem[i] = INIT[i*4+:4];
case (OPTION_INIT)
"NONE": mem[i] = mem[i]; //?
"ZERO": mem[i] = 4'h0;
"ANY": mem[i] = INIT[i*4+:4];
"NO_UNDEF": mem[i] = INIT[i*4+:4];
"UNDEFINED": mem[i] = 4'hx;
endcase
assign PORT_R_RD_DATA = mem[PORT_R_ADDR];
assign PORT_RW_RD_DATA = mem[PORT_RW_ADDR];

View file

@ -0,0 +1,19 @@
ram distributed \LUT_MULTI {
abits 4;
width 2;
init any;
port arsw "RW" {
clock posedge;
}
ifdef PORTS_QUAD {
option "PORTS" "QUAD" {
port ar "R0" "R1" "R2" {
}
}
} else ifdef PORTS_OCT {
option "PORTS" "OCT" {
port ar "R0" "R1" "R2" "R3" "R4" "R5" "R6" {
}
}
}
}

View file

@ -0,0 +1,45 @@
module LUT_MULTI(
input [3:0] PORT_R0_ADDR,
input [3:0] PORT_R1_ADDR,
input [3:0] PORT_R2_ADDR,
input [3:0] PORT_R3_ADDR,
input [3:0] PORT_R4_ADDR,
input [3:0] PORT_R5_ADDR,
input [3:0] PORT_R6_ADDR,
input [3:0] PORT_RW_ADDR,
input PORT_RW_CLK,
input PORT_RW_WR_EN,
input [1:0] PORT_RW_WR_DATA,
output [1:0] PORT_R0_RD_DATA,
output [1:0] PORT_R1_RD_DATA,
output [1:0] PORT_R2_RD_DATA,
output [1:0] PORT_R3_RD_DATA,
output [1:0] PORT_R4_RD_DATA,
output [1:0] PORT_R5_RD_DATA,
output [1:0] PORT_R6_RD_DATA,
output [1:0] PORT_RW_RD_DATA
);
parameter INIT = 0;
parameter OPTION_PORTS = "UNDEFINED";
reg [1:0] mem [0:15];
integer i;
initial
for (i = 0; i < 16; i += 1)
mem[i] = INIT[i*4+:4];
assign PORT_R0_RD_DATA = mem[PORT_R0_ADDR];
assign PORT_R1_RD_DATA = mem[PORT_R1_ADDR];
assign PORT_R2_RD_DATA = mem[PORT_R2_ADDR];
assign PORT_R3_RD_DATA = mem[PORT_R3_ADDR];
assign PORT_R4_RD_DATA = mem[PORT_R4_ADDR];
assign PORT_R5_RD_DATA = mem[PORT_R5_ADDR];
assign PORT_R6_RD_DATA = mem[PORT_R6_ADDR];
assign PORT_RW_RD_DATA = mem[PORT_RW_ADDR];
always @(posedge PORT_RW_CLK)
if (PORT_RW_WR_EN)
mem[PORT_RW_ADDR] <= PORT_RW_WR_DATA;
endmodule

View file

@ -0,0 +1,37 @@
ram block \RAM_WREN {
abits 4;
init none;
ifdef NO_BYTE {
# single enable signal
widths 4 8 global;
} else ifdef W4_B4 {
widths 4 global;
byte 4;
} else ifdef W8_B4 {
widths 8 global;
option "BYTESIZE" 4 {
byte 4;
}
} else ifdef W8_B8 {
width 8;
byte 8;
} else ifdef W16_B4 {
widths 16 global;
option "BYTESIZE" 4 {
byte 4;
}
} else ifdef W64_B8 {
widths 64 global;
option "BYTESIZE" 8 {
byte 8;
}
}
port srsw "A" {
clock posedge;
ifdef WRBE_SEPARATE {
wrbe_separate;
}
}
}

View file

@ -0,0 +1,33 @@
module RAM_WREN (
input PORT_A_CLK,
input [ABITS-1:0] PORT_A_ADDR,
input [WIDTH-1:0] PORT_A_WR_DATA,
output reg [WIDTH-1:0] PORT_A_RD_DATA,
input [PORT_A_WR_EN_WIDTH-1:0] PORT_A_WR_EN,
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE
);
parameter ABITS=4;
parameter WIDTH=8;
parameter PORT_A_WR_EN_WIDTH=1;
parameter PORT_A_WR_BE_WIDTH=0;
parameter OPTION_BYTESIZE=WIDTH;
parameter WB=OPTION_BYTESIZE;
reg [WIDTH-1:0] mem [0:2**ABITS-1];
integer i;
always @(posedge PORT_A_CLK) begin
for (i=0; i<PORT_A_WR_EN_WIDTH; i=i+1) // use PORT_A_WR_EN
if (!PORT_A_WR_BE_WIDTH && PORT_A_WR_EN[i])
mem[PORT_A_ADDR][i*WB+:WB] <= PORT_A_WR_DATA[i*WB+:WB];
for (i=0; i<PORT_A_WR_BE_WIDTH; i=i+1) // use PORT_A_WR_BE
if (PORT_A_WR_EN[0] && PORT_A_WR_BE[i])
mem[PORT_A_ADDR][i*WB+:WB] <= PORT_A_WR_DATA[i*WB+:WB];
end
always @(posedge PORT_A_CLK)
if (!PORT_A_WR_EN[0])
PORT_A_RD_DATA <= mem[PORT_A_ADDR];
endmodule