aboutsummaryrefslogtreecommitdiff
path: root/src/tb
diff options
context:
space:
mode:
Diffstat (limited to 'src/tb')
-rw-r--r--src/tb/tb_fmc.v522
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