//------------------------------------------------------------------------------
//
// tb_fmc.v
// -----------------------------------------------------------------------------
// Testbench for fixed latency FMC arbiter.
//
// Authors: Pavel Shatov
//
// Copyright 2018 NORDUnet A/S
// Copyright 2020-2021 The Commons Conservancy Cryptech Project
// SPDX-License-Identifier: BSD-3-Clause
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// - Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// - Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
`timescale 1ns / 1ps
//------------------------------------------------------------------------------
module tb_fmc;
//
// Settings
//
localparam NUM_TESTS = 100;
localparam integer STM32_FMC_LATENCY = 6;
//
// Clock
//
/* actual hardware uses 45 MHz, we use 50 for convenience */
localparam FMC_CLOCK_PERIOD = 20.0;
localparam FMC_CLOCK_PERIOD_HALF = 0.5 * FMC_CLOCK_PERIOD;
localparam FMC_CLOCK_PERIOD_QUARTER = 0.5 * FMC_CLOCK_PERIOD_HALF;
reg fmc_clk = 1'b0;
initial forever #FMC_CLOCK_PERIOD_HALF fmc_clk = ~fmc_clk;
//
// Clock Manager
//
wire io_clk;
wire sys_clk;
wire sys_rst_n;
wire core_clk;
alpha_clkmgr clkmgr_inst
(
.fmc_clk (fmc_clk),
.io_clk (io_clk),
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.core_clk (core_clk)
);
//
// FMC Arbiter - FPGA Side
//
wire [23: 0] sys_fmc_addr;
wire sys_fmc_wren;
wire sys_fmc_rden;
wire [31: 0] sys_fmc_dout;
reg [31: 0] sys_fmc_din;
//
// FMC Arbiter - STM32 Side
//
reg [23: 0] fmc_a = {24{1'bX}};
reg [31: 0] fmc_d_drive;
wire [31: 0] fmc_d_bidir;
reg fmc_ne1 = 1'b1;
reg fmc_noe = 1'b1;
reg fmc_nwe = 1'b1;
reg fmc_nl = 1'b1;
wire fmc_nwait_dummy;
assign fmc_d_bidir = fmc_noe ? fmc_d_drive : 32'hZZZZZZZZ;
fmc_arbiter #(.NUM_ADDR_BITS(24))
uut
(
// fmc bus
.fmc_a (fmc_a),
.fmc_d (fmc_d_bidir),
.fmc_ne1 (fmc_ne1),
.fmc_nl (fmc_nl),
.fmc_nwe (fmc_nwe),
.fmc_noe (fmc_noe),
.fmc_nwait (fmc_nwait_dummy),
// system clock, i/o clock
.io_clk (io_clk),
.sys_clk (sys_clk),
// user bus
.sys_addr (sys_fmc_addr),
.sys_wr_en (sys_fmc_wren),
.sys_data_out (sys_fmc_dout),
.sys_rd_en (sys_fmc_rden),
.sys_data_in (sys_fmc_din)
);
//
// "Random" Number Generator
//
reg [7:0] lfsr8;
task lfsr8_next;
lfsr8 = {lfsr8[6:0], lfsr8[8-1] ^ lfsr8[6-1] ^ lfsr8[5-1] ^ lfsr8[4-1]};
endtask
task lfsr8_seed;
input [7:0] lfsr8_in;
lfsr8 = lfsr8_in;
endtask
//
// Helper Tasks
//
//----------------------
task wait_quarter_tick;
//----------------------
#FMC_CLOCK_PERIOD_QUARTER;
endtask
//------------------
task wait_half_tick;
//------------------
begin
wait_quarter_tick;
wait_quarter_tick;
end
endtask
//------------------
task wait_full_tick;
//------------------
begin
wait_half_tick;
wait_half_tick;
end
endtask
//----------------
task wait_n_ticks;
//----------------
input integer n;
integer i;
for (i=0; i<n; i=i+1)
wait_full_tick;
endtask
//-------------
task fmc_write;
//-------------
input [23: 0] addr;
input [31: 0] data;
begin
fmc_ne1 = 1'b0; // select
fmc_nl = 1'b0; // set latch flag
fmc_a = addr; // set address
fmc_nwe = 1'b0; // set write-enable
wait_full_tick(); // mimic latency
fmc_nl = 1'b1; // clear latch flag
fmc_a = {24{1'bX}}; // clear address
wait_n_ticks(STM32_FMC_LATENCY); // mimic latency
fmc_d_drive = data; // set data
wait_half_tick(); // mimic latency
wait_quarter_tick();
fmc_ne1 = 1'b1; // deselect
fmc_nwe = 1'b1; // clear write-enable
fmc_d_drive = 32'hXXXXXXXX; // clear data
wait_quarter_tick(); // finish clock period
wait_full_tick(); // pause
end
endtask
//------------
task fmc_read;
//------------
input [23: 0] addr;
output [31: 0] data;
begin
fmc_ne1 = 1'b0; // select
fmc_nl = 1'b0; // set latch flag
fmc_a = addr; // set address
wait_full_tick(); // mimic latency
fmc_nl = 1'b1; // clear latch flag
fmc_a = {24{1'bX}}; // clear address
wait_full_tick(); // mimic latency
fmc_noe = 1'b0; // tri-state bus
wait_n_ticks(STM32_FMC_LATENCY-1); // mimic latency
wait_half_tick(); // mimic latency
data = fmc_d_bidir; // sample data
wait_half_tick(); // mimic latency
wait_full_tick(); // mimic latency
wait_half_tick(); // mimic latency
wait_quarter_tick();
fmc_ne1 = 1'b1; // deselect
fmc_noe = 1'b1; // drive bus
wait_quarter_tick(); // finish clock period
wait_full_tick(); // pause
end
endtask
//----------------
task gen_rnd_addr;
//----------------
output [31:0] addr;
reg [7:0] a, b, c;
begin
lfsr8_next; a = lfsr8;
lfsr8_next; b = lfsr8;
lfsr8_next; c = lfsr8;
addr = {a, b, c};
end
endtask
//----------------
task gen_rnd_data;
//----------------
output [31:0] data;
reg [7:0] a, b, c, d;
begin
lfsr8_next; a = lfsr8;
lfsr8_next; b = lfsr8;
lfsr8_next; c = lfsr8;
lfsr8_next; d = lfsr8;
data = {a, b, c, d};
end
endtask
//
// Script
//
reg [23:0] addr;
reg [31:0] data_wr;
reg [31:0] data_rd;
integer i;
initial begin
// let all signals initialize
wait_full_tick;
// wait for reset to complete
while (!sys_rst_n) begin
wait_full_tick;
end
// wait some more time
wait_n_ticks(10);
// write test
lfsr8_seed(8'hA5);
$display("Writing data...");
for (i=0; i<NUM_TESTS; i=i+1) begin
gen_rnd_addr(addr);
gen_rnd_data(data_wr);
fmc_write(addr, data_wr);
$display("Write %0d/%0d: @0x%x: 0x%x", i+1, NUM_TESTS, addr, data_wr);
end
lfsr8_seed(8'hA5);
$display("Reading data...");
for (i=0; i<NUM_TESTS; i=i+1) begin
gen_rnd_addr(addr);
gen_rnd_data(data_wr);
fmc_read(addr, data_rd);
$display("Read %0d/%0d: @0x%x: 0x%x | 0x%x", i+1, NUM_TESTS, addr, data_rd, data_wr);
if (data_rd !== data_wr) begin
$display("ERROR");
$finish;
end
end
$display("All tests passed. [OK]");
$finish;
end
//
// User-Side Memory
//
reg [31:0] mem[0:2**24-1];
// write handler
always @(posedge sys_clk)
//
if (sys_fmc_wren)
mem[sys_fmc_addr] <= sys_fmc_dout;
// read handler
always @(posedge sys_clk)
//
if (sys_fmc_rden)
sys_fmc_din <= mem[sys_fmc_addr];
endmodule
//------------------------------------------------------------------------------
// End-of-File
//------------------------------------------------------------------------------