hdk/common/verif/include/aws_clk_gen_utils.svh (291 lines of code) (raw):

// ============================================================================ // Amazon FPGA Hardware Development Kit // // Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Amazon Software License (the "License"). You may not use // this file except in compliance with the License. A copy of the License is // located at // // http://aws.amazon.com/asl/ // // or in the "license" file accompanying this file. This file is distributed on // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or // implied. See the License for the specific language governing permissions and // limitations under the License. // ============================================================================ // START SDA OFFSETS // // MMCM Regs address used by AWS_CLK_GEN // `define AWS_CLKGEN_BASE_B 64'h50000 `define AWS_CLKGEN_BASE_C 64'h51000 `define AWS_CLKGEN_BASE_A 64'h52000 `define AWS_CLKGEN_BASE_HBM 64'h54000 `define AWS_CLKGEN_BASE_REG 64'h58000 // // AWS_CLK_GEN Specific Regs // `define AWS_CLKGEN_ID_REG (`AWS_CLKGEN_BASE_REG + 64'h0) `define AWS_CLKGEN_VER_REG (`AWS_CLKGEN_BASE_REG + 64'h4) `define AWS_CLKGEN_BLD_REG (`AWS_CLKGEN_BASE_REG + 64'h8) `define AWS_CLKGEN_GRST_REG (`AWS_CLKGEN_BASE_REG + 64'h10) `define AWS_CLKGEN_SYSRST_REG (`AWS_CLKGEN_BASE_REG + 64'h14) `define AWS_CLKGEN_LOCK_REG (`AWS_CLKGEN_BASE_REG + 64'h20) `define AWS_CLKGEN_LOCK 32'h151 `define MMCM_STATUS 64'h04 `define MMCM_MAIN_CFG 64'h200 // Main clock multiplier/dividers `define MMCM_CLK0_CFG (`MMCM_MAIN_CFG + 64'h08) // clk0 dividers `define MMCM_CLK1_CFG (`MMCM_MAIN_CFG + 64'h14) // clk1 divider `define MMCM_CLK2_CFG (`MMCM_MAIN_CFG + 64'h20) // clk2 divider `define MMCM_LOAD_CFG (`MMCM_MAIN_CFG + 64'h5C) // LOAD/SEN register // END SDA OFFSETS // START OCL OFFSETS // // CL_CLK_FREQ measurement regs // `define CLK_FREQ_OFFSET (64'h600) `define CTL_REG (`CLK_FREQ_OFFSET + 64'h00) `define REF_FREQ_CTR (`CLK_FREQ_OFFSET + 64'h04) `define FREQ_CTR_0 (`CLK_FREQ_OFFSET + 64'h10) `define FREQ_CTR_1 (`CLK_FREQ_OFFSET + 64'h14) `define FREQ_CTR_2 (`CLK_FREQ_OFFSET + 64'h18) `define FREQ_CTR_3 (`CLK_FREQ_OFFSET + 64'h1C) `define FREQ_CTR_4 (`CLK_FREQ_OFFSET + 64'h20) `define FREQ_CTR_5 (`CLK_FREQ_OFFSET + 64'h24) `define FREQ_CTR_6 (`CLK_FREQ_OFFSET + 64'h28) `define FREQ_CTR_7 (`CLK_FREQ_OFFSET + 64'h2C) `define FREQ_CTR_8 (`CLK_FREQ_OFFSET + 64'h30) `define FREQ_CTR_9 (`CLK_FREQ_OFFSET + 64'h34) // END OCL OFFSETS // // Frequency Table with multiplier and divider values for MMCM, sorted as per increasing frequency order. // `define AWS_CLKGEN_TABLE_ROWS 27 `define AWS_CLKGEN_TABLE_COLS 6 int FREQ_TABLE [`AWS_CLKGEN_TABLE_ROWS][`AWS_CLKGEN_TABLE_COLS] = '{ /*idx=0 */ '{/*freq*/ 87, /*sharedMult*/ 87, /*sharedDiv*/ 10, /*sharedMultFrac*/ 000, /*div0*/ 10, /*div0Frac*/ 000}, /*idx=1 */ '{/*freq*/ 97, /*sharedMult*/ 97, /*sharedDiv*/ 10, /*sharedMultFrac*/ 000, /*div0*/ 10, /*div0Frac*/ 000}, /*idx=2 */ '{/*freq*/ 109, /*sharedMult*/ 109, /*sharedDiv*/ 10, /*sharedMultFrac*/ 000, /*div0*/ 10, /*div0Frac*/ 000}, /*idx=3 */ '{/*freq*/ 125, /*sharedMult*/ 125, /*sharedDiv*/ 10, /*sharedMultFrac*/ 000, /*div0*/ 10, /*div0Frac*/ 000}, /*idx=4 */ '{/*freq*/ 136, /*sharedMult*/ 136, /*sharedDiv*/ 10, /*sharedMultFrac*/ 000, /*div0*/ 10, /*div0Frac*/ 000}, /*idx=5 */ '{/*freq*/ 156, /*sharedMult*/ 156, /*sharedDiv*/ 10, /*sharedMultFrac*/ 000, /*div0*/ 10, /*div0Frac*/ 000}, /*idx=6 */ '{/*freq*/ 166, /*sharedMult*/ 166, /*sharedDiv*/ 20, /*sharedMultFrac*/ 000, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=7 */ '{/*freq*/ 171, /*sharedMult*/ 171, /*sharedDiv*/ 20, /*sharedMultFrac*/ 000, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=8 */ '{/*freq*/ 177, /*sharedMult*/ 177, /*sharedDiv*/ 20, /*sharedMultFrac*/ 000, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=9 */ '{/*freq*/ 182, /*sharedMult*/ 182, /*sharedDiv*/ 20, /*sharedMultFrac*/ 000, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=10*/ '{/*freq*/ 187, /*sharedMult*/ 187, /*sharedDiv*/ 20, /*sharedMultFrac*/ 000, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=11*/ '{/*freq*/ 196, /*sharedMult*/ 196, /*sharedDiv*/ 20, /*sharedMultFrac*/ 000, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=12*/ '{/*freq*/ 218, /*sharedMult*/ 218, /*sharedDiv*/ 20, /*sharedMultFrac*/ 000, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=13*/ '{/*freq*/ 227, /*sharedMult*/ 227, /*sharedDiv*/ 20, /*sharedMultFrac*/ 000, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=14*/ '{/*freq*/ 250, /*sharedMult*/ 250, /*sharedDiv*/ 20, /*sharedMultFrac*/ 000, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=15*/ '{/*freq*/ 265, /*sharedMult*/ 26, /*sharedDiv*/ 02, /*sharedMultFrac*/ 500, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=16*/ '{/*freq*/ 273, /*sharedMult*/ 27, /*sharedDiv*/ 02, /*sharedMultFrac*/ 250, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=17*/ '{/*freq*/ 291, /*sharedMult*/ 29, /*sharedDiv*/ 02, /*sharedMultFrac*/ 000, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=18*/ '{/*freq*/ 318, /*sharedMult*/ 31, /*sharedDiv*/ 02, /*sharedMultFrac*/ 750, /*div0*/ 05, /*div0Frac*/ 000}, /*idx=19*/ '{/*freq*/ 333, /*sharedMult*/ 33, /*sharedDiv*/ 04, /*sharedMultFrac*/ 250, /*div0*/ 02, /*div0Frac*/ 500}, /*idx=20*/ '{/*freq*/ 343, /*sharedMult*/ 34, /*sharedDiv*/ 04, /*sharedMultFrac*/ 250, /*div0*/ 02, /*div0Frac*/ 500}, /*idx=21*/ '{/*freq*/ 364, /*sharedMult*/ 36, /*sharedDiv*/ 04, /*sharedMultFrac*/ 750, /*div0*/ 02, /*div0Frac*/ 500}, /*idx=22*/ '{/*freq*/ 375, /*sharedMult*/ 37, /*sharedDiv*/ 04, /*sharedMultFrac*/ 500, /*div0*/ 02, /*div0Frac*/ 500}, /*idx=23*/ '{/*freq*/ 437, /*sharedMult*/ 43, /*sharedDiv*/ 04, /*sharedMultFrac*/ 625, /*div0*/ 02, /*div0Frac*/ 500}, /*idx=24*/ '{/*freq*/ 450, /*sharedMult*/ 45, /*sharedDiv*/ 04, /*sharedMultFrac*/ 000, /*div0*/ 02, /*div0Frac*/ 500}, /*idx=25*/ '{/*freq*/ 458, /*sharedMult*/ 45, /*sharedDiv*/ 04, /*sharedMultFrac*/ 750, /*div0*/ 02, /*div0Frac*/ 500}, /*idx=26*/ '{/*freq*/ 500, /*sharedMult*/ 50, /*sharedDiv*/ 04, /*sharedMultFrac*/ 000, /*div0*/ 02, /*div0Frac*/ 500} }; typedef struct { string name; int frequency; // MHz logic [63:0] addr; } clk; task aws_clkgen_asrt_rst(); // Task to assert all resets from AWS_CLK_GEN IP logic [31:0] read_data; $display("__INFO__: Writing SDA interface at Base addr = 0x%0x\n", `AWS_CLKGEN_BASE_REG); tb.poke_sda(.addr(`AWS_CLKGEN_SYSRST_REG ), .data(32'hFFFF_FFFF)); // SYS_RST_REG tb.poke_sda(.addr(`AWS_CLKGEN_BASE_REG + 64'h18 ), .data(32'd1)); // DIS_RST_MAIN_REG endtask // aws_clkgen_asrt_rst task aws_clkgen_dsrt_rst(); // // Task to de-assert all resets from AWS_CLK_GEN IP // logic [31:0] read_data; $display("__INFO__: Writing SDA interface at Base addr = 0x%0x\n", `AWS_CLKGEN_BASE_REG); tb.poke_sda(.addr(`AWS_CLKGEN_GRST_REG), .data(32'd0)); // G_RST_REG tb.poke_sda(.addr(`AWS_CLKGEN_SYSRST_REG ), .data(32'd0)); // SYS_RST_REG tb.poke_sda(.addr(`AWS_CLKGEN_BASE_REG + 64'h18 ), .data(32'd0)); // DIS_RST_MAIN_REG // wait for MMCMs to lock do begin tb.wait_clock(100); tb.peek_sda(.addr(`AWS_CLKGEN_LOCK_REG), .data(read_data)); $display("__INFO__: Reading SDA interface at addr = 0x%0x | read_data = 0x%0x\n", (`AWS_CLKGEN_LOCK_REG), read_data); end while (read_data != `AWS_CLKGEN_LOCK); endtask // aws_clkgen_dsrt_rst `define SAMPLE_COUNT 100 task measure_cl_clk_freq(clk clks [10:0]); // Configure CL_CLK_FREQ block to measure clock frequencies of AWS_CLK_GEN clocks logic [11:0] offset; logic [31:0] read_data; logic [31:0] clk_speed; // localparam REF_CNT_MAX = 10000 from cl_clk_freq.sv divided by 100 // Clear all the counters tb.poke_ocl(.addr(`CTL_REG), .data(32'h8000_0000)); tb.poke_ocl(.addr(`CTL_REG), .data(32'h0000_0000)); // Start Measurement tb.poke_ocl(.addr(`CTL_REG), .data(32'h0000_0001)); do begin tb.wait_clock(5); tb.peek_ocl(.addr(`CTL_REG), .data(read_data)); end while (read_data[0] != 1'b0); // Wait until bit[0] is clear for (int i = 0; i < 11; i++) begin tb.peek_ocl(.addr(clks[i].addr), .data(read_data)); clk_speed = read_data / `SAMPLE_COUNT; $display("__INFO__: Clk %12s Reg %2d at OCL addr = 0x%0x | read_data = 0x%03x | %4d MHz\n", clks[i].name, i, clks[i].addr, read_data, clk_speed); end endtask // measure_cl_clk_freq `define FREQ_RANGE 1 // MHz band to compare around the expected value task compare_cl_clk_freq(clk clks [10:0]); // Configure CL_CLK_FREQ block to measure clock frequencies of AWS_CLK_GEN clocks logic [11:0] offset; logic [31:0] read_data; logic [31:0] clk_speed; // localparam REF_CNT_MAX = 10000 from cl_clk_freq.sv divided by 100 // Clear all the counters tb.poke_ocl(.addr(`CTL_REG), .data(32'h8000_0000)); tb.poke_ocl(.addr(`CTL_REG), .data(32'h0000_0000)); // Start Measurement tb.poke_ocl(.addr(`CTL_REG), .data(32'h0000_0001)); do begin tb.wait_clock(5); tb.peek_ocl(.addr(`CTL_REG), .data(read_data)); end while (read_data[0] != 1'b0); // Wait until bit[0] is clear for (int i = 0; i < 11; i++) begin tb.peek_ocl(.addr(clks[i].addr), .data(read_data)); clk_speed = read_data / `SAMPLE_COUNT; if (clk_speed > clks[i].frequency + `FREQ_RANGE || clk_speed < clks[i].frequency - `FREQ_RANGE) begin error_count++; $error("__ERROR__: Clk %12s Reg %2d at OCL addr = 0x%0x | read_data = 0x%03x | %4d MHz | Execpted clk speed = %4d MHz\n", clks[i].name, i, clks[i].addr, read_data, clk_speed, clks[i].frequency); end $display("__INFO__: Clk %12s Reg %2d at OCL addr = 0x%0x | read_data = 0x%03x | %4d MHz\n", clks[i].name, i, clks[i].addr, read_data, clk_speed); end endtask // compare_cl_clk_freq task aws_clkgen_set_freq(logic [63:0] mmcm_base, int req_freq, bit reset); automatic int freq_sel = 0; automatic int mult = 1; automatic int mult_frac = 0; automatic int div = 1; automatic int div0 = 1; automatic int div0_frac = 0; const int div1 = 15; const int div2 = 15; int idx_choice; // If requested frequency is lower than min supported then error out if (!reset && (req_freq < FREQ_TABLE[0][0])) begin error_count++; $error("__ERROR__: Requested frequency (%d MHz) is lower than min supported (%d MHz)", req_freq, FREQ_TABLE[0][0]); end if (!reset) begin // get index of closest match to the target freq from the FREQ_TABLE for (int ii = 0; ii < `AWS_CLKGEN_TABLE_ROWS; ii++) begin idx_choice = ii; if (FREQ_TABLE[ii][0] > req_freq) begin idx_choice = (ii == 0) ? 0 : ii-1; break; end end freq_sel = FREQ_TABLE[idx_choice][0]; mult = FREQ_TABLE[idx_choice][1]; div = FREQ_TABLE[idx_choice][2]; mult_frac = FREQ_TABLE[idx_choice][3]; div0 = FREQ_TABLE[idx_choice][4]; div0_frac = FREQ_TABLE[idx_choice][5]; end $display("__DEBUG__: MMCM = 0x%08x | Requested Freq = %d MHz | MMCM Freq = %d MHz\n", mmcm_base, req_freq, freq_sel); // Program MMCM aws_clkgen_set_mmcm(.mmcm_base(mmcm_base), .mult(mult), .mult_frac(mult_frac), .div(div), .div0(div0), .div0_frac(div0_frac), .div1(div1), .div2(div2), .reset(reset)); endtask // aws_clkgen_set_freq task aws_clkgen_set_mmcm(logic [63:0] mmcm_base, int mult, int mult_frac, int div, int div0, int div0_frac, int div1, int div2, bit reset); logic [31:0] wdata; int tmp_mult; int tmp_div; if (!reset) begin // Set main clock divider and multiplier values. Ensure non-zero values. tmp_mult = (mult == 0) ? 1 : mult; tmp_div = (div == 0) ? 1 : div; wdata = ((tmp_div << 0) & 32'hFF) | ((tmp_mult << 8) & 32'hFF00) | ((mult_frac << 16) & 32'h0FFF_0000); tb.poke_sda(.addr(mmcm_base + `MMCM_MAIN_CFG), .data(wdata)); // Set clk0 divider values. Ensure non-zero values. tmp_div = (div0 == 0) ? 1 : div0; wdata = ((tmp_div << 0) & 32'hFF) | ((div0_frac << 8) & 32'h000F_FF00); tb.poke_sda(.addr(mmcm_base + `MMCM_CLK0_CFG), .data(wdata)); // Set clk1 divider values tmp_div = (div1 == 0) ? 1 : div1; wdata = ((tmp_div << 0) & 32'hFF); tb.poke_sda(.addr(mmcm_base + `MMCM_CLK1_CFG), .data(wdata)); // Set clk2 divider values tmp_div = (div2 == 0) ? 1 : div2; wdata = ((tmp_div << 0) & 32'hFF); tb.poke_sda(.addr(mmcm_base + `MMCM_CLK2_CFG), .data(wdata)); end check_clkgen_status_locked(.mmcm_base(mmcm_base)); // Load clock configurations wdata = reset ? 32'h1 : 32'h3; tb.poke_sda(.addr(mmcm_base + `MMCM_LOAD_CFG), .data(wdata)); check_clkgen_status_locked(.mmcm_base(mmcm_base)); endtask // aws_clkgen_set_mmcm `define LOCKED 1 `define MAX_CLKGEN_LOOP_RETRIES 1000 `define LOOP_WAIT_NS 100 // Wait until mmcm_base + `MMCM_STATUS bit[0] is 1 for locked, 0 for unlocked task check_clkgen_status_locked(logic [63:0] mmcm_base); // Check Status Register is currently locked. automatic int loop_count = 0; logic [63:0] read_data; do begin tb.nsec_delay(`LOOP_WAIT_NS); tb.peek_sda(.addr(mmcm_base + `MMCM_STATUS), .data(read_data)); loop_count++; end while ((read_data[0] != `LOCKED) && (loop_count < `MAX_CLKGEN_LOOP_RETRIES)); if (loop_count >= `MAX_CLKGEN_LOOP_RETRIES) begin error_count++; $display("__ERROR__: Timeout: MMCM is not locked after %d ns. MMCM address = 0x%08x\n", loop_count * `LOOP_WAIT_NS, mmcm_base + `MMCM_STATUS); end endtask // check_clkgen_status_reaches_state task aws_clkgen_reset(bit reset); automatic int loop_count = 0; logic [31:0] read_data; // Clear global reset reg tb.poke_sda(.addr(`AWS_CLKGEN_GRST_REG), .data(32'd0)); // Wait for MMCM lock if de-asserting resets if (!reset) begin do begin tb.nsec_delay(`LOOP_WAIT_NS); tb.peek_sda(.addr(`AWS_CLKGEN_LOCK_REG), .data(read_data)); loop_count++; end while ((read_data != `AWS_CLKGEN_LOCK) && (loop_count < `MAX_CLKGEN_LOOP_RETRIES)); if (loop_count >= `MAX_CLKGEN_LOOP_RETRIES) begin error_count++; $display("__ERROR__: Timeout: Failed to achieve MMCM lock after %d iterations. @addr = 0x%08x | lock_status = 0x%08x | lock_expected = 0x%08x ", loop_count * `LOOP_WAIT_NS, `AWS_CLKGEN_LOCK_REG, read_data, `AWS_CLKGEN_LOCK); end end // assert/de-assert SYS_RST REG tb.poke_sda(.addr(`AWS_CLKGEN_SYSRST_REG), .data(reset ? 32'hFFFFFFFF : 32'h0)); endtask task aws_clkgen_dynamic_freq(int freq_clk_a, int freq_clk_b, int freq_clk_c, int freq_clk_hbm, bit reset); aws_clkgen_reset(.reset(1)); aws_clkgen_set_freq(.mmcm_base(`AWS_CLKGEN_BASE_A), .req_freq(freq_clk_a), .reset(reset)); aws_clkgen_set_freq(.mmcm_base(`AWS_CLKGEN_BASE_B), .req_freq(freq_clk_b), .reset(reset)); aws_clkgen_set_freq(.mmcm_base(`AWS_CLKGEN_BASE_C), .req_freq(freq_clk_c), .reset(reset)); aws_clkgen_set_freq(.mmcm_base(`AWS_CLKGEN_BASE_HBM), .req_freq(freq_clk_hbm), .reset(reset)); aws_clkgen_reset(.reset(0)); endtask