aboutsummaryrefslogblamecommitdiff
path: root/src/tb/tb_fmc.v
blob: b99771d06691c04c449bc05e60ce010e1f9f0732 (plain) (tree)
1
2
3
4
5
6
7
8







                                                                                


                                                               










                                                                              


                                                                         





















                                                                                





                               
                                             










































































































































































































































































































                                                                                                 





                                                                                
//------------------------------------------------------------------------------
//
// 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
//------------------------------------------------------------------------------