|  | // 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_0; | 
|  | reg rce_a_1; | 
|  | reg [`ADDR_WIDTH0-1:0] ra_a_0; | 
|  | reg [`ADDR_WIDTH1-1:0] ra_a_1; | 
|  | wire [`DATA_WIDTH0-1:0] rq_a_0; | 
|  | wire [`DATA_WIDTH1-1:0] rq_a_1; | 
|  | reg wce_a_0; | 
|  | reg wce_a_1; | 
|  | reg [`ADDR_WIDTH0-1:0] wa_a_0; | 
|  | reg [`ADDR_WIDTH1-1:0] wa_a_1; | 
|  | reg [`DATA_WIDTH0-1:0] wd_a_0; | 
|  | reg [`DATA_WIDTH1-1:0] wd_a_1; | 
|  |  | 
|  | reg clk_b; | 
|  | reg rce_b_0; | 
|  | reg rce_b_1; | 
|  | reg [`ADDR_WIDTH0-1:0] ra_b_0; | 
|  | reg [`ADDR_WIDTH1-1:0] ra_b_1; | 
|  | wire [`DATA_WIDTH0-1:0] rq_b_0; | 
|  | wire [`DATA_WIDTH1-1:0] rq_b_1; | 
|  | reg wce_b_0; | 
|  | reg wce_b_1; | 
|  | reg [`ADDR_WIDTH0-1:0] wa_b_0; | 
|  | reg [`ADDR_WIDTH1-1:0] wa_b_1; | 
|  | reg [`DATA_WIDTH0-1:0] wd_b_0; | 
|  | reg [`DATA_WIDTH1-1:0] wd_b_1; | 
|  |  | 
|  |  | 
|  | initial clk_a = 0; | 
|  | initial clk_b = 0; | 
|  | initial ra_a_0 = 0; | 
|  | initial ra_a_1 = 0; | 
|  | initial ra_b_0 = 0; | 
|  | initial ra_b_1 = 0; | 
|  | initial rce_a_0 = 0; | 
|  | initial rce_a_1 = 0; | 
|  | initial rce_b_0 = 0; | 
|  | initial rce_b_1 = 0; | 
|  | initial wce_a_0 = 0; | 
|  | initial wce_a_1 = 0; | 
|  | initial wce_b_0 = 0; | 
|  | initial wce_b_1 = 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 a0; | 
|  | integer b0; | 
|  | integer a1; | 
|  | integer b1; | 
|  |  | 
|  | reg done_a0; | 
|  | reg done_b0; | 
|  | reg done_a1; | 
|  | reg done_b1; | 
|  | initial done_a0 = 1'b0; | 
|  | initial done_b0 = 1'b0; | 
|  | initial done_a1 = 1'b0; | 
|  | initial done_b1 = 1'b0; | 
|  | wire done_sim = done_a0 & done_b0 & done_a1 & done_b1; | 
|  |  | 
|  | reg [`DATA_WIDTH0-1:0] expected_a_0; | 
|  | reg [`DATA_WIDTH1-1:0] expected_a_1; | 
|  | reg [`DATA_WIDTH0-1:0] expected_b_0; | 
|  | reg [`DATA_WIDTH1-1:0] expected_b_1; | 
|  |  | 
|  | always @(posedge clk_a) begin | 
|  | expected_a_0 <= (a0 | (a0 << 20) | 20'h55000) & {`DATA_WIDTH0{1'b1}}; | 
|  | expected_a_1 <= ((a1+1) | ((a1+1) << 20) | 20'h55000) & {`DATA_WIDTH1{1'b1}}; | 
|  | end | 
|  | always @(posedge clk_b) begin | 
|  | expected_b_0 <= ((b0+2) | ((b0+2) << 20) | 20'h55000) & {`DATA_WIDTH0{1'b1}}; | 
|  | expected_b_1 <= ((b1+3) | ((b1+3) << 20) | 20'h55000) & {`DATA_WIDTH1{1'b1}}; | 
|  | end | 
|  |  | 
|  | wire error_a_0 = a0 != 0 ? (rq_a_0 !== expected_a_0) : 0; | 
|  | wire error_a_1 = a1 != 0 ? (rq_a_1 !== expected_a_1) : 0; | 
|  | wire error_b_0 = b0 != (1<<`ADDR_WIDTH0) / 2 ? (rq_b_0 !== expected_b_0) : 0; | 
|  | wire error_b_1 = b1 != (1<<`ADDR_WIDTH1) / 2 ? (rq_b_1 !== expected_b_1) : 0; | 
|  |  | 
|  | integer error_a_0_cnt = 0; | 
|  | integer error_a_1_cnt = 0; | 
|  | integer error_b_0_cnt = 0; | 
|  | integer error_b_1_cnt = 0; | 
|  |  | 
|  | always @ (posedge clk_a) | 
|  | begin | 
|  | if (error_a_0) | 
|  | error_a_0_cnt <= error_a_0_cnt + 1'b1; | 
|  | if (error_a_1) | 
|  | error_a_1_cnt <= error_a_1_cnt + 1'b1; | 
|  | end | 
|  | always @ (posedge clk_b) | 
|  | begin | 
|  | if (error_b_0) | 
|  | error_b_0_cnt <= error_b_0_cnt + 1'b1; | 
|  | if (error_b_1) | 
|  | error_b_1_cnt <= error_b_1_cnt + 1'b1; | 
|  | end | 
|  |  | 
|  | // PORTs A0 | 
|  | initial #(1) begin | 
|  | // Write data | 
|  | for (a0 = 0; a0 < (1<<`ADDR_WIDTH0) / 2; a0 = a0 + ADDR_INCR) begin | 
|  | @(negedge clk_a) begin | 
|  | wa_a_0 = a0; | 
|  | wd_a_0 = a0 | (a0 << 20) | 20'h55000; | 
|  | wce_a_0 = 1; | 
|  | end | 
|  | @(posedge clk_a) begin | 
|  | #(PERIOD/10) wce_a_0 = 0; | 
|  | end | 
|  | end | 
|  | // Read data | 
|  | for (a0 = 0; a0 < (1<<`ADDR_WIDTH0) / 2; a0 = a0 + ADDR_INCR) begin | 
|  | @(negedge clk_a) begin | 
|  | ra_a_0 = a0; | 
|  | rce_a_0 = 1; | 
|  | end | 
|  | @(posedge clk_a) begin | 
|  | #(PERIOD/10) rce_a_0 = 0; | 
|  | if ( rq_a_0 !== expected_a_0) begin | 
|  | $display("%d: PORT A0: FAIL: mismatch act=%x exp=%x at %x", $time, rq_a_0, expected_a_0, a0); | 
|  | end else begin | 
|  | $display("%d: PORT A0: OK: act=%x exp=%x at %x", $time, rq_a_0, expected_a_0, a0); | 
|  | end | 
|  | end | 
|  | end | 
|  | done_a0 = 1'b1; | 
|  | a0 = 0; | 
|  | // PORTs B0 | 
|  | @(posedge clk_b) | 
|  | #2; | 
|  | // Write data | 
|  | for (b0 = (1<<`ADDR_WIDTH0) / 2; b0 < (1<<`ADDR_WIDTH0); b0 = b0 + ADDR_INCR) begin | 
|  | @(negedge clk_b) begin | 
|  | wa_b_0 = b0; | 
|  | wd_b_0 = (b0+2) | ((b0+2) << 20) | 20'h55000; | 
|  | wce_b_0 = 1; | 
|  | end | 
|  | @(posedge clk_b) begin | 
|  | #(PERIOD/10) wce_b_0 = 0; | 
|  | end | 
|  | end | 
|  | // Read data | 
|  | for (b0 = (1<<`ADDR_WIDTH0) / 2; b0 < (1<<`ADDR_WIDTH0); b0 = b0 + ADDR_INCR) begin | 
|  | @(negedge clk_b) begin | 
|  | ra_b_0 = b0; | 
|  | rce_b_0 = 1; | 
|  | end | 
|  | @(posedge clk_b) begin | 
|  | #(PERIOD/10) rce_b_0 = 0; | 
|  | if ( rq_b_0 !== expected_b_0) begin | 
|  | $display("%d: PORT B0: FAIL: mismatch act=%x exp=%x at %x", $time, rq_b_0, expected_b_0, b0); | 
|  | end else begin | 
|  | $display("%d: PORT B0: OK: act=%x exp=%x at %x", $time, rq_b_0, expected_b_0, b0); | 
|  | end | 
|  | end | 
|  | end | 
|  | done_b0 = 1'b1; | 
|  | b0 = (1<<`ADDR_WIDTH0) / 2; | 
|  | // PORTs A1 | 
|  | @(posedge clk_a) | 
|  | #2; | 
|  | // Write data | 
|  | for (a1 = 0; a1 < (1<<`ADDR_WIDTH1) / 2; a1 = a1 + ADDR_INCR) begin | 
|  | @(negedge clk_a) begin | 
|  | wa_a_1 = a1; | 
|  | wd_a_1 = (a1+1) | ((a1+1) << 20) | 20'h55000; | 
|  | wce_a_1 = 1; | 
|  | end | 
|  | @(posedge clk_a) begin | 
|  | #(PERIOD/10) wce_a_1 = 0; | 
|  | end | 
|  | end | 
|  | // Read data | 
|  | for (a1 = 0; a1 < (1<<`ADDR_WIDTH1) / 2; a1 = a1 + ADDR_INCR) begin | 
|  | @(negedge clk_a) begin | 
|  | ra_a_1 = a1; | 
|  | rce_a_1 = 1; | 
|  | end | 
|  | @(posedge clk_a) begin | 
|  | #(PERIOD/10) rce_a_1 = 0; | 
|  | if ( rq_a_1 !== expected_a_1) begin | 
|  | $display("%d: PORT A1: FAIL: mismatch act=%x exp=%x at %x", $time, rq_a_1, expected_a_1, a1); | 
|  | end else begin | 
|  | $display("%d: PORT A1: OK: act=%x exp=%x at %x", $time, rq_a_1, expected_a_1, a1); | 
|  | end | 
|  | end | 
|  | end | 
|  | done_a1 = 1'b1; | 
|  | a1 = 0; | 
|  | // PORTs B1 | 
|  | @(posedge clk_b) | 
|  | #2; | 
|  | // Write data | 
|  | for (b1 = (1<<`ADDR_WIDTH1) / 2; b1 < (1<<`ADDR_WIDTH1); b1 = b1 + ADDR_INCR) begin | 
|  | @(negedge clk_b) begin | 
|  | wa_b_1 = b1; | 
|  | wd_b_1 = (b1+3) | ((b1+3) << 20) | 20'h55000; | 
|  | wce_b_1 = 1; | 
|  | end | 
|  | @(posedge clk_b) begin | 
|  | #(PERIOD/10) wce_b_1 = 0; | 
|  | end | 
|  | end | 
|  | // Read data | 
|  | for (b1 = (1<<`ADDR_WIDTH1) / 2; b1 < (1<<`ADDR_WIDTH1); b1 = b1 + ADDR_INCR) begin | 
|  | @(negedge clk_b) begin | 
|  | ra_b_1 = b1; | 
|  | rce_b_1 = 1; | 
|  | end | 
|  | @(posedge clk_b) begin | 
|  | #(PERIOD/10) rce_b_1 = 0; | 
|  | if ( rq_b_1 !== expected_b_1) begin | 
|  | $display("%d: PORT B1: FAIL: mismatch act=%x exp=%x at %x", $time, rq_b_1, expected_b_1, b1); | 
|  | end else begin | 
|  | $display("%d: PORT B1: OK: act=%x exp=%x at %x", $time, rq_b_1, expected_b_1, b1); | 
|  | end | 
|  | end | 
|  | end | 
|  | done_b1 = 1'b1; | 
|  | b1 = (1<<`ADDR_WIDTH1) / 2; | 
|  | end | 
|  |  | 
|  | // Scan for simulation finish | 
|  | always @(posedge clk_a, posedge clk_b) begin | 
|  | if (done_sim) | 
|  | $finish_and_return( (error_a_0_cnt == 0 & error_b_0_cnt == 0 & error_a_1_cnt == 0 & error_b_1_cnt == 0) ? 0 : -1 ); | 
|  | end | 
|  |  | 
|  | case (`STRINGIFY(`TOP)) | 
|  | "dpram_18x1024_9x2048": begin | 
|  | dpram_18x1024_9x2048 #() bram ( | 
|  | .clk_a_0(clk_a), | 
|  | .REN_a_0(rce_a_0), | 
|  | .RD_ADDR_a_0(ra_a_0), | 
|  | .RDATA_a_0(rq_a_0), | 
|  | .WEN_a_0(wce_a_0), | 
|  | .WR_ADDR_a_0(wa_a_0), | 
|  | .WDATA_a_0(wd_a_0), | 
|  | .clk_b_0(clk_b), | 
|  | .REN_b_0(rce_b_0), | 
|  | .RD_ADDR_b_0(ra_b_0), | 
|  | .RDATA_b_0(rq_b_0), | 
|  | .WEN_b_0(wce_b_0), | 
|  | .WR_ADDR_b_0(wa_b_0), | 
|  | .WDATA_b_0(wd_b_0), | 
|  |  | 
|  | .clk_a_1(clk_a), | 
|  | .REN_a_1(rce_a_1), | 
|  | .RD_ADDR_a_1(ra_a_1), | 
|  | .RDATA_a_1(rq_a_1), | 
|  | .WEN_a_1(wce_a_1), | 
|  | .WR_ADDR_a_1(wa_a_1), | 
|  | .WDATA_a_1(wd_a_1), | 
|  | .clk_b_1(clk_b), | 
|  | .REN_b_1(rce_b_1), | 
|  | .RD_ADDR_b_1(ra_b_1), | 
|  | .RDATA_b_1(rq_b_1), | 
|  | .WEN_b_1(wce_b_1), | 
|  | .WR_ADDR_b_1(wa_b_1), | 
|  | .WDATA_b_1(wd_b_1) | 
|  | ); | 
|  | end | 
|  | "dpram_9x2048_x2": begin | 
|  | dpram_9x2048_x2 #() bram ( | 
|  | .clk_a_0(clk_a), | 
|  | .REN_a_0(rce_a_0), | 
|  | .RD_ADDR_a_0(ra_a_0), | 
|  | .RDATA_a_0(rq_a_0), | 
|  | .WEN_a_0(wce_a_0), | 
|  | .WR_ADDR_a_0(wa_a_0), | 
|  | .WDATA_a_0(wd_a_0), | 
|  | .clk_b_0(clk_b), | 
|  | .REN_b_0(rce_b_0), | 
|  | .RD_ADDR_b_0(ra_b_0), | 
|  | .RDATA_b_0(rq_b_0), | 
|  | .WEN_b_0(wce_b_0), | 
|  | .WR_ADDR_b_0(wa_b_0), | 
|  | .WDATA_b_0(wd_b_0), | 
|  |  | 
|  | .clk_a_1(clk_a), | 
|  | .REN_a_1(rce_a_1), | 
|  | .RD_ADDR_a_1(ra_a_1), | 
|  | .RDATA_a_1(rq_a_1), | 
|  | .WEN_a_1(wce_a_1), | 
|  | .WR_ADDR_a_1(wa_a_1), | 
|  | .WDATA_a_1(wd_a_1), | 
|  | .clk_b_1(clk_b), | 
|  | .REN_b_1(rce_b_1), | 
|  | .RD_ADDR_b_1(ra_b_1), | 
|  | .RDATA_b_1(rq_b_1), | 
|  | .WEN_b_1(wce_b_1), | 
|  | .WR_ADDR_b_1(wa_b_1), | 
|  | .WDATA_b_1(wd_b_1) | 
|  | ); | 
|  | end | 
|  | "dpram_18x1024_x2": begin | 
|  | dpram_18x1024_x2 #() bram ( | 
|  | .clk_a_0(clk_a), | 
|  | .REN_a_0(rce_a_0), | 
|  | .RD_ADDR_a_0(ra_a_0), | 
|  | .RDATA_a_0(rq_a_0), | 
|  | .WEN_a_0(wce_a_0), | 
|  | .WR_ADDR_a_0(wa_a_0), | 
|  | .WDATA_a_0(wd_a_0), | 
|  | .clk_b_0(clk_b), | 
|  | .REN_b_0(rce_b_0), | 
|  | .RD_ADDR_b_0(ra_b_0), | 
|  | .RDATA_b_0(rq_b_0), | 
|  | .WEN_b_0(wce_b_0), | 
|  | .WR_ADDR_b_0(wa_b_0), | 
|  | .WDATA_b_0(wd_b_0), | 
|  |  | 
|  | .clk_a_1(clk_a), | 
|  | .REN_a_1(rce_a_1), | 
|  | .RD_ADDR_a_1(ra_a_1), | 
|  | .RDATA_a_1(rq_a_1), | 
|  | .WEN_a_1(wce_a_1), | 
|  | .WR_ADDR_a_1(wa_a_1), | 
|  | .WDATA_a_1(wd_a_1), | 
|  | .clk_b_1(clk_b), | 
|  | .REN_b_1(rce_b_1), | 
|  | .RD_ADDR_b_1(ra_b_1), | 
|  | .RDATA_b_1(rq_b_1), | 
|  | .WEN_b_1(wce_b_1), | 
|  | .WR_ADDR_b_1(wa_b_1), | 
|  | .WDATA_b_1(wd_b_1) | 
|  | ); | 
|  | end | 
|  | endcase | 
|  | endmodule |