blob: 3495bedfdea76e83f51078b701c5cbe3e3659159 [file] [log] [blame]
// Copyright 2020-2022 F4PGA Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
`timescale 1ns/1ps
`define STRINGIFY(x) `"x`"
module TB;
localparam PERIOD = 50;
localparam ADDR_INCR = 1;
reg clk_a;
reg rce_a;
reg [`ADDR_WIDTH-1:0] ra_a;
wire [`DATA_WIDTH-1:0] rq_a;
reg wce_a;
reg [`ADDR_WIDTH-1:0] wa_a;
reg [`DATA_WIDTH-1:0] wd_a;
reg clk_b;
reg rce_b;
reg [`ADDR_WIDTH-1:0] ra_b;
wire [`DATA_WIDTH-1:0] rq_b;
reg wce_b;
reg [`ADDR_WIDTH-1:0] wa_b;
reg [`DATA_WIDTH-1:0] wd_b;
initial clk_a = 0;
initial clk_b = 0;
initial ra_a = 0;
initial ra_b = 0;
initial rce_a = 0;
initial rce_b = 0;
initial forever #(PERIOD / 2.0) clk_a = ~clk_a;
initial begin
#(PERIOD / 4.0);
forever #(PERIOD / 2.0) clk_b = ~clk_b;
end
initial begin
$dumpfile(`STRINGIFY(`VCD));
$dumpvars;
end
integer a;
integer b;
reg done_a;
reg done_b;
initial done_a = 1'b0;
initial done_b = 1'b0;
wire done_sim = done_a & done_b;
reg [`DATA_WIDTH-1:0] expected_a;
reg [`DATA_WIDTH-1:0] expected_b;
always @(posedge clk_a) begin
expected_a <= (a | (a << 20) | 20'h55000) & {`DATA_WIDTH{1'b1}};
end
always @(posedge clk_b) begin
expected_b <= (b | (b << 20) | 20'h55000) & {`DATA_WIDTH{1'b1}};
end
wire error_a = a != 0 ? rq_a !== expected_a : 0;
wire error_b = b != (1<<`ADDR_WIDTH) / 2 ? rq_b !== expected_b : 0;
integer error_a_cnt = 0;
integer error_b_cnt = 0;
always @ (posedge clk_a)
begin
if (error_a)
error_a_cnt <= error_a_cnt + 1'b1;
end
always @ (posedge clk_b)
begin
if (error_b)
error_b_cnt <= error_b_cnt + 1'b1;
end
// PORT A
initial #(1) begin
// Write data
for (a = 0; a < (1<<`ADDR_WIDTH) / 2; a = a + ADDR_INCR) begin
@(negedge clk_a) begin
wa_a = a;
wd_a = a | (a << 20) | 20'h55000;
wce_a = 1;
end
@(posedge clk_a) begin
#(PERIOD/10) wce_a = 0;
end
end
// Read data
for (a = 0; a < (1<<`ADDR_WIDTH) / 2; a = a + ADDR_INCR) begin
@(negedge clk_a) begin
ra_a = a;
rce_a = 1;
end
@(posedge clk_a) begin
#(PERIOD/10) rce_a = 0;
if ( rq_a !== expected_a) begin
$display("%d: PORT A: FAIL: mismatch act=%x exp=%x at %x", $time, rq_a, expected_a, a);
end else begin
$display("%d: PORT A: OK: act=%x exp=%x at %x", $time, rq_a, expected_a, a);
end
end
end
done_a = 1'b1;
end
// PORT B
initial #(1) begin
// Write data
for (b = (1<<`ADDR_WIDTH) / 2; b < (1<<`ADDR_WIDTH); b = b + ADDR_INCR) begin
@(negedge clk_b) begin
wa_b = b;
wd_b = b | (b << 20) | 20'h55000;
wce_b = 1;
end
@(posedge clk_b) begin
#(PERIOD/10) wce_b = 0;
end
end
// Read data
for (b = (1<<`ADDR_WIDTH) / 2; b < (1<<`ADDR_WIDTH); b = b + ADDR_INCR) begin
@(negedge clk_b) begin
ra_b = b;
rce_b = 1;
end
@(posedge clk_b) begin
#(PERIOD/10) rce_b = 0;
if ( rq_b !== expected_b) begin
$display("%d: PORT B: FAIL: mismatch act=%x exp=%x at %x", $time, rq_b, expected_b, b);
end else begin
$display("%d: PORT B: OK: act=%x exp=%x at %x", $time, rq_b, expected_b, b);
end
end
end
done_b = 1'b1;
end
// Scan for simulation finish
always @(posedge clk_a, posedge clk_b) begin
if (done_sim)
$finish_and_return( (error_a_cnt == 0 & error_b_cnt == 0) ? 0 : -1 );
end
case (`STRINGIFY(`TOP))
"BRAM_TDP_36x1024": begin
BRAM_TDP_36x1024 #() bram (
.clk_a(clk_a),
.rce_a(rce_a),
.ra_a(ra_a),
.rq_a(rq_a),
.wce_a(wce_a),
.wa_a(wa_a),
.wd_a(wd_a),
.clk_b(clk_b),
.rce_b(rce_b),
.ra_b(ra_b),
.rq_b(rq_b),
.wce_b(wce_b),
.wa_b(wa_b),
.wd_b(wd_b)
);
end
"BRAM_TDP_32x1024": begin
BRAM_TDP_32x1024 #() bram (
.clk_a(clk_a),
.rce_a(rce_a),
.ra_a(ra_a),
.rq_a(rq_a),
.wce_a(wce_a),
.wa_a(wa_a),
.wd_a(wd_a),
.clk_b(clk_b),
.rce_b(rce_b),
.ra_b(ra_b),
.rq_b(rq_b),
.wce_b(wce_b),
.wa_b(wa_b),
.wd_b(wd_b)
);
end
"BRAM_TDP_18x2048": begin
BRAM_TDP_18x2048 #() bram (
.clk_a(clk_a),
.rce_a(rce_a),
.ra_a(ra_a),
.rq_a(rq_a),
.wce_a(wce_a),
.wa_a(wa_a),
.wd_a(wd_a),
.clk_b(clk_b),
.rce_b(rce_b),
.ra_b(ra_b),
.rq_b(rq_b),
.wce_b(wce_b),
.wa_b(wa_b),
.wd_b(wd_b)
);
end
"BRAM_TDP_16x2048": begin
BRAM_TDP_16x2048 #() bram (
.clk_a(clk_a),
.rce_a(rce_a),
.ra_a(ra_a),
.rq_a(rq_a),
.wce_a(wce_a),
.wa_a(wa_a),
.wd_a(wd_a),
.clk_b(clk_b),
.rce_b(rce_b),
.ra_b(ra_b),
.rq_b(rq_b),
.wce_b(wce_b),
.wa_b(wa_b),
.wd_b(wd_b)
);
end
"BRAM_TDP_9x4096": begin
BRAM_TDP_9x4096 #() bram (
.clk_a(clk_a),
.rce_a(rce_a),
.ra_a(ra_a),
.rq_a(rq_a),
.wce_a(wce_a),
.wa_a(wa_a),
.wd_a(wd_a),
.clk_b(clk_b),
.rce_b(rce_b),
.ra_b(ra_b),
.rq_b(rq_b),
.wce_b(wce_b),
.wa_b(wa_b),
.wd_b(wd_b)
);
end
"BRAM_TDP_8x4096": begin
BRAM_TDP_8x4096 #() bram (
.clk_a(clk_a),
.rce_a(rce_a),
.ra_a(ra_a),
.rq_a(rq_a),
.wce_a(wce_a),
.wa_a(wa_a),
.wd_a(wd_a),
.clk_b(clk_b),
.rce_b(rce_b),
.ra_b(ra_b),
.rq_b(rq_b),
.wce_b(wce_b),
.wa_b(wa_b),
.wd_b(wd_b)
);
end
"BRAM_TDP_4x8192": begin
BRAM_TDP_4x8192 #() bram (
.clk_a(clk_a),
.rce_a(rce_a),
.ra_a(ra_a),
.rq_a(rq_a),
.wce_a(wce_a),
.wa_a(wa_a),
.wd_a(wd_a),
.clk_b(clk_b),
.rce_b(rce_b),
.ra_b(ra_b),
.rq_b(rq_b),
.wce_b(wce_b),
.wa_b(wa_b),
.wd_b(wd_b)
);
end
"BRAM_TDP_2x16384": begin
BRAM_TDP_2x16384 #() bram (
.clk_a(clk_a),
.rce_a(rce_a),
.ra_a(ra_a),
.rq_a(rq_a),
.wce_a(wce_a),
.wa_a(wa_a),
.wd_a(wd_a),
.clk_b(clk_b),
.rce_b(rce_b),
.ra_b(ra_b),
.rq_b(rq_b),
.wce_b(wce_b),
.wa_b(wa_b),
.wd_b(wd_b)
);
end
"BRAM_TDP_1x32768": begin
BRAM_TDP_1x32768 #() bram (
.clk_a(clk_a),
.rce_a(rce_a),
.ra_a(ra_a),
.rq_a(rq_a),
.wce_a(wce_a),
.wa_a(wa_a),
.wd_a(wd_a),
.clk_b(clk_b),
.rce_b(rce_b),
.ra_b(ra_b),
.rq_b(rq_b),
.wce_b(wce_b),
.wa_b(wa_b),
.wd_b(wd_b)
);
end
endcase
endmodule