aboutsummaryrefslogtreecommitdiffhomepage
path: root/sim/src/tb.sv
diff options
context:
space:
mode:
authorPrivate Island Networks Inc <opensource@privateisland.tech>2026-05-13 12:52:39 -0400
committerPrivate Island Networks Inc <opensource@privateisland.tech>2026-05-13 12:52:39 -0400
commit694f71a280c7c386a7f9c6dcc220563fe7b61313 (patch)
treed2624afb31f6d6da08624d4c2175a00fc2ad0eb5 /sim/src/tb.sv
initial commit, see README in top folderHEADmaster
Diffstat (limited to 'sim/src/tb.sv')
-rw-r--r--sim/src/tb.sv188
1 files changed, 188 insertions, 0 deletions
diff --git a/sim/src/tb.sv b/sim/src/tb.sv
new file mode 100644
index 0000000..107df8e
--- /dev/null
+++ b/sim/src/tb.sv
@@ -0,0 +1,188 @@
+/*
+ * tb.sv
+ *
+ * Copyright (C) 2026 Private Island Networks Inc.
+ *
+ * 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.
+ *
+ * function: test bench for ML Module Agilex
+ *
+ * Notes:
+ *
+ * RX data is from the TB to the DUT (ML Module)
+ * TX data is from the DUT to the TB
+ *
+ */
+
+`timescale 1ns / 1ps
+
+module tb;
+
+ // System clocks
+ parameter PERIOD_CLK_25 = 40; // 25 MHz
+ parameter PERIOD_CLK_125 = 8; // 125 MHz
+ parameter PERIOD_CLK_250 = 4; // 250 MHz to support RGMII DDR
+
+ localparam IDLE_SHIFT = 4; // Used to multiply the specified # of idle clocks
+
+ localparam RX_CLK_CNT_START = 'd20; // clocks to wait after PLL Lock
+
+ // FPGA I/O
+ reg rstn;
+ reg clk_125, clk_25, clk_250;
+ wire rgmii_rx_clk;
+ reg rgmii_rx_ctl;
+ reg [3:0] rgmii_rx_d;
+ wire rgmii_tx_clk;
+ wire rgmii_tx_ctl;
+ wire [3:0] rgmii_tx_d;
+ wire rgmii_mdc, rgmii_mdio, rgmii_resetn, rgmii_intn;
+ wire [2:0] led;
+
+ // sim only I/O
+ wire pclk;
+ wire pll_lock;
+ wire [2:0] phy_up;
+
+ ml_module_agilex dut(
+ .rstn(rstn),
+ .clk_i(clk_25),
+
+ // Sim Only
+ .pll_locked_o(pll_lock),
+ .pclk(pclk),
+
+ .rgmii_rx_clk(rgmii_rx_clk),
+ .rgmii_rx_ctl(rgmii_rx_ctl),
+ .rgmii_rx_d(rgmii_rx_d),
+ .rgmii_tx_clk(rgmii_tx_clk),
+ .rgmii_tx_ctl(rgmii_tx_ctl),
+ .rgmii_tx_d(rgmii_tx_d),
+ .rgmii_mdc(rgmii_mdc),
+ .rgmii_mdio(rgmii_mdio),
+ .rgmii_intn(rgmii_intn),
+ .rgmii_gpio(),
+
+ .flash_clk(),
+ .flash_dqs(),
+ .flash_seln(),
+ .flash_d(),
+
+ .fpga_led(led)
+
+ );
+
+ assign #2 rgmii_rx_clk = !clk_125; // Provide 2ns clock skew similar to what an Ethernet PHY supports
+
+
+ reg [23:0] rx_clk_cnt;
+ reg [23:0] rx_clk_cnt_start;
+ reg [13:0] rx_idle_cnt;
+ reg [13:0] rx_data_cnt;
+ reg rx_last_byte;
+ reg [8:0] rx_d[0:16383]; // 2**14
+ reg [8:0] rx_d_byte;
+
+
+initial begin
+ $readmemh("../data/ml.dat",rx_d);
+ $display("[%0t ns] ==INFO== Load memory from file for rx: %0s.", $time, "ml.dat");
+end
+
+ initial begin
+ rstn = 1'b0;
+ clk_25 = 1'b0;
+ clk_125 = 1'b0;
+ clk_250 = 0;
+ #25 rstn = 1'b1;
+ end
+
+ // Clocks
+ always
+ #(PERIOD_CLK_125/2) clk_125 = ~clk_125;
+
+ always
+ #(PERIOD_CLK_25/2) clk_25 = ~clk_25;
+
+ always
+ #(PERIOD_CLK_250/2) clk_250 = ~clk_250;
+
+
+ // DDR clk count. Use bit 0 to indicate rising edge
+ always @(posedge clk_250, negedge rstn)
+ if (!rstn)
+ rx_clk_cnt <= 24'd0;
+ else if (pll_lock)
+ rx_clk_cnt <= rx_clk_cnt + 1'b1;
+
+ // Capture the number of idle clocks before next packet (for debugging)
+ always @(posedge clk_125, negedge rstn)
+ if (!rstn)
+ rx_idle_cnt <= 14'd0;
+ else if (rx_d[rx_data_cnt][8])
+ rx_idle_cnt <= rx_d[rx_data_cnt+1][7:0] << IDLE_SHIFT;
+
+ // Counter to determine next packet to transmit into the DUT (receive path)
+ always @(posedge clk_125, negedge rstn)
+ if (!rstn)
+ rx_clk_cnt_start <= RX_CLK_CNT_START;
+ else if (rx_d[rx_data_cnt][8])
+ rx_clk_cnt_start <= rx_clk_cnt + (rx_d[rx_data_cnt+1][7:0] << IDLE_SHIFT) + 1'b1;
+
+ // The MSB bit (9th bit) of the data file denotes the last byte to transmit into the DUT
+ always @(negedge clk_250, negedge rstn)
+ if (!rstn)
+ rx_last_byte <= 1'b0;
+ else if (rx_d[rx_data_cnt][8] && rx_clk_cnt[0])
+ rx_last_byte <= 1'b1;
+ else
+ rx_last_byte <= 1'b0;
+
+ // rx_d_byte helps with debugging
+ always @(negedge clk_250, negedge rstn)
+ if (!rstn)
+ rx_d_byte <= 9'd0;
+ else
+ rx_d_byte <= rx_d[rx_data_cnt];
+
+ // RX data logic. Refer to RGMII spec for info on use of CTL bits.
+ always @(negedge clk_250, negedge rstn)
+ if (!rstn) begin
+ rgmii_rx_ctl <= 1'b0;
+ rgmii_rx_d <= 4'hD;
+ rx_data_cnt <= 24'd0;
+ end
+ else if (rx_clk_cnt >= rx_clk_cnt_start && !rx_last_byte) begin
+ if (!rx_clk_cnt[0]) begin
+ rgmii_rx_ctl <= 1'b1;
+ rgmii_rx_d <= rx_d[rx_data_cnt][3:0];
+ end
+ else begin
+ rgmii_rx_ctl <= 1'b1;
+ rgmii_rx_d <= rx_d[rx_data_cnt][7:4];
+ rx_data_cnt <= rx_data_cnt + 1'b1;
+ end
+ end
+ else if (rx_last_byte) begin
+ rgmii_rx_ctl <= 1'b0;
+ rgmii_rx_d <= 4'hD;
+ rx_data_cnt <= rx_data_cnt + 1'b1;
+ end
+ else begin
+ rgmii_rx_ctl <= 1'b0;
+ rgmii_rx_d <= 4'hD;
+ end
+
+endmodule
+
+

Highly Recommended Verilog Books