diff options
-rw-r--r-- | src/tb/tb_fmc.v | 522 |
1 files changed, 307 insertions, 215 deletions
diff --git a/src/tb/tb_fmc.v b/src/tb/tb_fmc.v index 20b602c..ba436b9 100644 --- a/src/tb/tb_fmc.v +++ b/src/tb/tb_fmc.v @@ -6,7 +6,7 @@ // // Authors: Pavel Shatov // -// Copyright (c) 2018, NORDUnet A/S +// Copyright (c) 2018-2019, NORDUnet A/S // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: @@ -43,220 +43,312 @@ module tb_fmc; - // - // Clock (100 MHz) - // - localparam CLOCK_PERIOD = 10.0; - localparam HALF_CLOCK_PERIOD = 0.5 * CLOCK_PERIOD; - - reg clk_fmc = 1'b0; - //reg clk_45 = 1'b0; - //reg clk_90 = 1'b0; - - initial begin - forever #HALF_CLOCK_PERIOD clk_fmc = ~clk_fmc; - end - - wire clk_45 = clk_fmc; - wire clk_90 = clk_fmc; - - //initial begin - //#1.25; - //forever #HALF_CLOCK_PERIOD clk_45 = ~clk_45; - //end - - //initial begin - //#2.5; - //forever #HALF_CLOCK_PERIOD clk_90 = ~clk_90; - //end - - - // - // 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; - - 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 - .sys_clk (clk_45), - .fmc_clk_aux (clk_90), - - // 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) - ); - - - // - // Helper Tasks - // - task wait_ticks; - input integer num_ticks; - integer cnt; - begin - for (cnt=0; cnt<num_ticks; cnt=cnt+1) - #CLOCK_PERIOD; - end - endtask - - task wait_half_tick; - begin - #HALF_CLOCK_PERIOD; - end - 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_ticks(1); // mimic latency - - fmc_nl = 1'b1; // clear latch flag - fmc_a = {24{1'bX}}; // clear address - - wait_ticks(3); // mimic latency - - fmc_d_drive = data; // set data - - wait_ticks(1); // mimic latency - - fmc_ne1 = 1'b1; // deselect - fmc_nwe = 1'b1; // clear write-enable - fmc_d_drive = 32'hXXXXXXXX; // clear data - - wait_ticks(1); // 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_ticks(1); // mimic latency - - fmc_nl = 1'b1; // clear latch flag - fmc_a = {24{1'bX}}; // clear address - - wait_ticks(1); // mimic latency - - fmc_noe = 1'b0; // tri-state bus - - wait_ticks(3); // mimic latency - wait_half_tick(); // mimic latency - data = fmc_d_bidir; // sample data - wait_half_tick(); // mimic latency - - wait_ticks(2); // mimic bus turnaround - - fmc_ne1 = 1'b1; // deselect - fmc_noe = 1'b1; // drive bus - - wait_ticks(1); // pause - end - endtask - - - // - // Script - // - reg [31:0] data; - initial begin - - wait_ticks(200); - - fmc_write(24'h223344, 32'hCCAA5533); - fmc_write(24'h667788, 32'hEDCBEDCB); - fmc_write(24'h223344, 32'hCCAA5533); - fmc_write(24'h667788, 32'hEDCBEDCB); - - fmc_read(24'h223344, data); - fmc_read(24'h667788, data); - fmc_read(24'h223344, data); - fmc_read(24'h667788, data); - - fmc_write(24'h223344, 32'hCCAA5533); - fmc_write(24'h667788, 32'hEDCBEDCB); - fmc_write(24'h223344, 32'hCCAA5533); - fmc_write(24'h667788, 32'hEDCBEDCB); - - fmc_read(24'h223344, data); - fmc_read(24'h667788, data); - fmc_read(24'h223344, data); - fmc_read(24'h667788, data); - - end - - - - // - // Demo Registers - // - reg [31: 0] reg_test_a; - reg [31: 0] reg_test_b; - reg [31: 0] reg_test_c; - - always @(posedge clk_45) - // - if (sys_fmc_wren) - // - case (sys_fmc_addr) - 24'h000000: reg_test_a <= sys_fmc_dout; - 24'hFFFFFF: reg_test_c <= sys_fmc_dout; - default: reg_test_b <= sys_fmc_dout; - endcase - - always @(posedge clk_45) - // - if (sys_fmc_rden) - // - case (sys_fmc_addr) - 24'h000000: sys_fmc_din <= reg_test_a; - 24'hFFFFFF: sys_fmc_din <= reg_test_c; - default: sys_fmc_din <= reg_test_b; - endcase - // - else - // - sys_fmc_din <= 32'hXXXXXXXX; - - + // + // Settings + // + localparam NUM_TESTS = 100; + + + localparam integer STM32_FMC_LATENCY = 4; + + // + // 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 |