blob: bcb7ee752e3d717e6eb217736ad29710431e1a59 [file] [log] [blame]
// Copyright 2024 Google LLC
//
// 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.
class MultiFifoTransaction;
rand logic [3:0][31:0] data;
rand logic [2:0] valid; // Number of elements that producer is enqueuing
rand logic [2:0] ready; // Number of elements that consumer is dequeuing.
constraint valid_limit { valid < 4; };
constraint ready_limit { ready < 4; };
endclass
class MultiFifoTransactionGenerator;
rand MultiFifoTransaction transaction;
mailbox gen2driv;
int n;
event finished;
function new(mailbox gen2driv, int n, event finished);
this.gen2driv = gen2driv;
this.n = n;
this.finished = finished;
endfunction
task generate_transactions();
repeat(n) begin
transaction = new();
if( !transaction.randomize() ) $fatal("Gen:: trans randomization failed");
gen2driv.put(transaction);
end
-> finished;
endtask
endclass
interface MultiFifoInterface(input logic clk, rstn);
logic [2:0] valid_in;
logic [3:0][31:0] data_in;
logic [4:0] fill_level;
logic [3:0][31:0] data_out;
logic [2:0] ready_out;
clocking driver_cb @(posedge clk);
default input negedge output negedge;
input fill_level, data_out;
output valid_in, data_in, ready_out;
endclocking
modport DRIVER (clocking driver_cb, input clk, rstn);
endinterface
class MultiFifoDriver;
virtual MultiFifoInterface multififo_iface;
mailbox gen2driv;
mailbox enqueued;
mailbox dequeued;
MultiFifoTransaction transaction;
int n_transactions_processed;
logic [2:0] enqueued_fired; // Number of elements to enqueue
logic [2:0] dequeued_fired; // Number of elements to enqueue
logic [4:0] fill_level;
logic [3:0][31:0] data_out;
function new(virtual MultiFifoInterface multififo_iface,
mailbox gen2driv,
mailbox enqueued,
mailbox dequeued);
this.multififo_iface = multififo_iface;
this.gen2driv = gen2driv;
this.enqueued = enqueued;
this.dequeued = dequeued;
endfunction
task reset;
wait(!multififo_iface.rstn);
$display("Reset started.");
multififo_iface.DRIVER.driver_cb.valid_in <= 0;
multififo_iface.DRIVER.driver_cb.data_in <= 0;
multififo_iface.DRIVER.driver_cb.ready_out <= 0;
wait(multififo_iface.rstn);
$display("Finished resetting.");
endtask
task record_enqueue_dequeue;
endtask
task drive;
forever begin
$display("Running iteration ", n_transactions_processed);
gen2driv.get(transaction);
@(negedge multififo_iface.DRIVER.clk);
fill_level = multififo_iface.DRIVER.driver_cb.fill_level;
data_out = multififo_iface.DRIVER.driver_cb.data_out;
enqueued_fired = (transaction.valid < (16 - fill_level)) ?
transaction.valid : 16 - fill_level;
dequeued_fired = (transaction.ready > fill_level) ?
fill_level : transaction.ready;
multififo_iface.DRIVER.driver_cb.valid_in <= enqueued_fired;
multififo_iface.DRIVER.driver_cb.data_in <= transaction.data;
multififo_iface.DRIVER.driver_cb.ready_out <= dequeued_fired;
// Put enqueued and dequeued elements into checker mailboxes
for (int i = 0; i < 4; i++) begin
if (i < transaction.valid && i < enqueued_fired) begin
// $display("Enqueuing ", transaction.data[i]);
enqueued.put(transaction.data[i]);
end
end
for (int o = 0; o < 4; o++) begin
if (o < dequeued_fired && o < transaction.ready) begin
// $display("Dequeuing ", data_out[o]);
dequeued.put(data_out[o]);
end
end
n_transactions_processed++;
@(posedge multififo_iface.DRIVER.clk);
end
endtask
endclass
class MultiFifoComparator;
mailbox enqueued;
mailbox dequeued;
int n_transactions_processed = 0;
logic [31:0] enqueued_elem;
logic [31:0] dequeued_elem;
function new(mailbox enqueued,
mailbox dequeued);
this.enqueued = enqueued;
this.dequeued = dequeued;
endfunction
task check;
forever begin
enqueued.get(enqueued_elem);
dequeued.get(dequeued_elem);
if (enqueued_elem != dequeued_elem) begin
$display("Error: expected ", enqueued_elem, " got ", dequeued_elem);
end
n_transactions_processed++;
end
endtask
endclass
class MultiFifoEnvironment;
localparam ITERATIONS = 512;
MultiFifoTransactionGenerator gen;
MultiFifoDriver driv;
MultiFifoComparator comparator;
mailbox gen2driv;
mailbox enqueued;
mailbox dequeued;
event gen_ended;
virtual MultiFifoInterface multififo_iface;
function new(virtual MultiFifoInterface multififo_iface);
this.multififo_iface = multififo_iface;
gen2driv = new();
enqueued = new();
dequeued = new();
gen = new(gen2driv, ITERATIONS, gen_ended);
driv = new(multififo_iface, gen2driv, enqueued, dequeued);
comparator = new(enqueued, dequeued);
endfunction
task setup();
driv.reset();
endtask
task test();
fork
gen.generate_transactions();
driv.drive();
comparator.check();
join_any
endtask
task teardown();
wait(gen_ended.triggered);
wait(driv.n_transactions_processed == ITERATIONS);
endtask
task run;
setup();
test();
teardown();
$finish;
endtask
endclass;
program MultiFifoTest(MultiFifoInterface multififo_iface);
MultiFifoEnvironment env;
initial begin
env = new(multififo_iface);
env.run();
end
endprogram
module MultiFifo_tb();
logic clk;
logic rstn;
MultiFifoInterface multififo_iface(clk, rstn);
typedef logic[31:0] MyInt;
MultiFifo#(.T (MyInt), .N (4), .MAX_CAPACITY (16))
dut(
.clk(multififo_iface.clk),
.rstn(multififo_iface.rstn),
.valid_in(multififo_iface.valid_in),
.data_in(multififo_iface.data_in),
.fill_level(multififo_iface.fill_level),
.data_out(multififo_iface.data_out),
.ready_out(multififo_iface.ready_out)
);
// Clock and reset
initial begin
rstn = 0;
clk = 0;
#5
clk = 1;
#5
clk = 0;
rstn = 1;
forever
#5 clk = ~clk;
end
MultiFifoTest test(multififo_iface);
endmodule