// Copyright 2018 ETH Zurich and University of Bologna. // // Copyright and related rights are licensed under the Solderpad Hardware // License, Version 0.51 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law // or agreed to in writing, software, hardware and materials distributed under // this 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. // // Fabian Schuiki <fschuiki@iis.ee.ethz.ch> module cdc_2phase_tb; parameter int UNTIL = 100000; parameter bit INJECT_DELAYS = 1; parameter bit POST_SYNTHESIS = 0; time tck_src = 10ns; time tck_dst = 10ns; bit src_done = 0; bit dst_done = 0; bit done; assign done = src_done & dst_done; // Signals of the design under test. logic src_rst_ni = 1; logic src_clk_i = 0; logic [31:0] src_data_i = 0; logic src_valid_i = 0; logic src_ready_o; logic dst_rst_ni = 1; logic dst_clk_i = 0; logic [31:0] dst_data_o; logic dst_valid_o; logic dst_ready_i = 0; // Instantiate the design under test. if (POST_SYNTHESIS) begin : g_dut cdc_2phase_synth i_dut (.*); end else if (INJECT_DELAYS) begin : g_dut cdc_2phase_tb_delay_injector #(0.8ns) i_dut (.*); end else begin : g_dut cdc_2phase #(logic [31:0]) i_dut (.*); end // Mailbox with expected items on destination side. mailbox #(int) dst_mbox = new(); int num_sent = 0; int num_received = 0; int num_failed = 0; // Clock generators. initial begin static int num_items, num_clks; num_items = 10; num_clks = 0; #10ns; src_rst_ni = 0; #10ns; src_rst_ni = 1; #10ns; while (!done) begin src_clk_i = 1; #(tck_src/2); src_clk_i = 0; #(tck_src/2); // Modulate the clock frequency. num_clks++; if (num_sent >= num_items && num_clks > 10) begin num_items = num_sent + 10; num_clks = 0; tck_src = $urandom_range(1000, 10000) * 1ps; assert(tck_src > 0); end end end initial begin static int num_items, num_clks; num_items = 10; num_clks = 0; #10ns; dst_rst_ni = 0; #10ns; dst_rst_ni = 1; #10ns; while (!done) begin dst_clk_i = 1; #(tck_dst/2); dst_clk_i = 0; #(tck_dst/2); // Modulate the clock frequency. num_clks++; if (num_received >= num_items && num_clks > 10) begin num_items = num_received + 10; num_clks = 0; tck_dst = $urandom_range(1000, 10000) * 1ps; assert(tck_dst > 0); end end end // Source side sender. task src_cycle_start; #(tck_src*0.8); endtask task src_cycle_end; @(posedge src_clk_i); endtask initial begin @(negedge src_rst_ni); @(posedge src_rst_ni); repeat(3) @(posedge src_clk_i); for (int i = 0; i < UNTIL; i++) begin static integer stimulus; stimulus = $random(); src_data_i <= #(tck_src*0.2) stimulus; src_valid_i <= #(tck_src*0.2) 1; dst_mbox.put(stimulus); num_sent++; src_cycle_start(); while (!src_ready_o) begin src_cycle_end(); src_cycle_start(); end src_cycle_end(); src_valid_i <= #(tck_src*0.2) 0; end src_done = 1; end // Destination side receiver. task dst_cycle_start; #(tck_dst*0.8); endtask task dst_cycle_end; @(posedge dst_clk_i); endtask initial begin @(negedge dst_rst_ni); @(posedge dst_rst_ni); repeat(3) @(posedge dst_clk_i); while (!src_done || dst_mbox.num() > 0) begin static integer expected, actual; static int cooldown; dst_ready_i <= #(tck_dst*0.2) 1; dst_cycle_start(); while (!dst_valid_o) begin dst_cycle_end(); dst_cycle_start(); end actual = dst_data_o; num_received++; if (dst_mbox.num() == 0) begin $error("unexpected transaction: data=%0h", actual); num_failed++; end else begin dst_mbox.get(expected); if (actual != expected) begin $error("transaction mismatch: exp=%0h, act=%0h", expected, actual); num_failed++; end end dst_cycle_end(); dst_ready_i <= #(tck_dst*0.2) 0; // Insert a random cooldown period. cooldown = $urandom_range(0, 40); if (cooldown < 20) repeat(cooldown) @(posedge dst_clk_i); end if (num_sent != num_received) begin $error("%0d items sent, but %0d items received", num_sent, num_received); end if (num_failed > 0) begin $error("%0d/%0d items mismatched", num_failed, num_sent); end else begin $info("%0d items passed", num_sent); end dst_done = 1; end endmodule module cdc_2phase_tb_delay_injector #( parameter time MAX_DELAY = 0ns )( input logic src_rst_ni, input logic src_clk_i, input logic [31:0] src_data_i, input logic src_valid_i, output logic src_ready_o, input logic dst_rst_ni, input logic dst_clk_i, output logic [31:0] dst_data_o, output logic dst_valid_o, input logic dst_ready_i ); logic async_req_o, async_req_i; logic async_ack_o, async_ack_i; logic [31:0] async_data_o, async_data_i; always @(async_req_o) begin automatic time d = $urandom_range(0, MAX_DELAY); async_req_i <= #d async_req_o; end always @(async_ack_o) begin automatic time d = $urandom_range(0, MAX_DELAY); async_ack_i <= #d async_ack_o; end for (genvar i = 0; i < 32; i++) begin always @(async_data_o[i]) begin automatic time d = $urandom_range(0, MAX_DELAY); async_data_i[i] <= #d async_data_o[i]; end end cdc_2phase_src #(logic [31:0]) i_src ( .rst_ni ( src_rst_ni ), .clk_i ( src_clk_i ), .data_i ( src_data_i ), .valid_i ( src_valid_i ), .ready_o ( src_ready_o ), .async_req_o ( async_req_o ), .async_ack_i ( async_ack_i ), .async_data_o ( async_data_o ) ); cdc_2phase_dst #(logic [31:0]) i_dst ( .rst_ni ( dst_rst_ni ), .clk_i ( dst_clk_i ), .data_o ( dst_data_o ), .valid_o ( dst_valid_o ), .ready_i ( dst_ready_i ), .async_req_i ( async_req_i ), .async_ack_o ( async_ack_o ), .async_data_i ( async_data_i ) ); endmodule