[dv/clkmgr] Add test for extclk feature
Add randomized test for the external clock clkmgr feature.
Check the ast request/response handshake.
Check that the clock divisors get ramped up unless in scan mode.
Signed-off-by: Guillermo Maturana <maturana@google.com>
diff --git a/hw/ip/clkmgr/data/clkmgr_testplan.hjson b/hw/ip/clkmgr/data/clkmgr_testplan.hjson
index c56a4a9..75be259 100644
--- a/hw/ip/clkmgr/data/clkmgr_testplan.hjson
+++ b/hw/ip/clkmgr/data/clkmgr_testplan.hjson
@@ -80,22 +80,22 @@
desc: '''
Tests the functionality of enabling external clocks.
- - External clocks can be enabled writing `lc_ctrl_pkg::On` to CSR
- `extclk_sel`.
- - Writes to CSR `extclk_sel` are ignored unless the value of CSR
- `extclk_sel_regwen` is 1.
- - Regardless of the `extclk_sel` contents, external clocks won't
- be enabled unless `lc_dtl_en_i == lc_ctrl_pkg::On`.
- - External clocks can also be enabled if the input
- `lc_clk_byp_req_i == lc_ctrl_pkg::On`.
+ - External clock is enabled if the `lc_clk_byp_req_i` input from
+ `lc_ctrl` is `lc_ctrl_pkg::On`.
+ - External clock is also be enabled when CSR `extclk_sel` is set to
+ `lc_ctrl_pkg::On` and the `lc_dtl_en_i` input from `lc_ctrl` is
+ `lc_ctrl_pkg::On`.
+ - Notice writes to the `extclk_sel` register are ignored unless the
+ CSR `extclk_sel_regwen` is 1.
- A successful switch to external clocks will cause the clkmgr
- to undo a divide by 2 for io_div4 and io_div2 clocks unless in
- scan mode `(scanmode_i == lc_ctrl_pkg::On)`.
+ to undo a divide by 2 for io_div4 and io_div2 clocks except when
+ `(scanmode_i == lc_ctrl_pkg::On)`.
**Stimulus**:
- CSR writes to `extclk_sel` and `extclk_sel_regwen`.
- Setting `lc_dft_en_i`, `lc_clk_byp_req_i`, and the handshake to
ast via `ast_clk_byp_req_o` and `ast_clk_byp_ack_i`.
+ - Setting `scanmode_i`.
**Checks**:
- SVA assertions:
@@ -108,7 +108,7 @@
clocks to ramp up unless `scanmode_i == lc_ctrl_pkg::On`.
'''
milestone: V2
- tests: []
+ tests: ["clkmgr_extclk"]
}
]
covergroups: [
diff --git a/hw/ip/clkmgr/dv/clkmgr_sim_cfg.hjson b/hw/ip/clkmgr/dv/clkmgr_sim_cfg.hjson
index 15ad2f1..53e6e70 100644
--- a/hw/ip/clkmgr/dv/clkmgr_sim_cfg.hjson
+++ b/hw/ip/clkmgr/dv/clkmgr_sim_cfg.hjson
@@ -57,6 +57,10 @@
uvm_test_seq: clkmgr_smoke_vseq
}
{
+ name: clkmgr_extclk
+ uvm_test_seq: clkmgr_extclk_vseq
+ }
+ {
name: clkmgr_peri
uvm_test_seq: clkmgr_peri_vseq
}
diff --git a/hw/ip/clkmgr/dv/env/clkmgr_env.core b/hw/ip/clkmgr/dv/env/clkmgr_env.core
index f5be154..adfc356 100644
--- a/hw/ip/clkmgr/dv/env/clkmgr_env.core
+++ b/hw/ip/clkmgr/dv/env/clkmgr_env.core
@@ -19,6 +19,7 @@
- seq_lib/clkmgr_vseq_list.sv: {is_include_file: true}
- seq_lib/clkmgr_base_vseq.sv: {is_include_file: true}
- seq_lib/clkmgr_common_vseq.sv: {is_include_file: true}
+ - seq_lib/clkmgr_extclk_vseq.sv: {is_include_file: true}
- seq_lib/clkmgr_peri_vseq.sv: {is_include_file: true}
- seq_lib/clkmgr_smoke_vseq.sv: {is_include_file: true}
- seq_lib/clkmgr_trans_vseq.sv: {is_include_file: true}
diff --git a/hw/ip/clkmgr/dv/env/clkmgr_env_cov.sv b/hw/ip/clkmgr/dv/env/clkmgr_env_cov.sv
index 670948b..d06381a 100644
--- a/hw/ip/clkmgr/dv/env/clkmgr_env_cov.sv
+++ b/hw/ip/clkmgr/dv/env/clkmgr_env_cov.sv
@@ -45,7 +45,7 @@
trans_cg = new(name);
endfunction
- function sample(bit hint, bit ip_clk_en, bit scanmode, bit idle);
+ function void sample(bit hint, bit ip_clk_en, bit scanmode, bit idle);
trans_cg.sample(hint, ip_clk_en, scanmode, idle);
endfunction
endclass
diff --git a/hw/ip/clkmgr/dv/env/clkmgr_if.sv b/hw/ip/clkmgr/dv/env/clkmgr_if.sv
index 1a4d88f..395d40b 100644
--- a/hw/ip/clkmgr/dv/env/clkmgr_if.sv
+++ b/hw/ip/clkmgr/dv/env/clkmgr_if.sv
@@ -58,6 +58,12 @@
lc_ctrl_pkg::lc_tx_t extclk_sel;
logic jitter_enable;
+ // The expected and actual divided io clocks.
+ logic exp_clk_io_div2;
+ logic actual_clk_io_div2;
+ logic exp_clk_io_div4;
+ logic actual_clk_io_div4;
+
function automatic void update_extclk_sel(lc_ctrl_pkg::lc_tx_t value);
extclk_sel = value;
endfunction
@@ -82,14 +88,39 @@
scanmode_i = value;
endfunction
+ function automatic void update_lc_dft_en(lc_ctrl_pkg::lc_tx_t value);
+ lc_dft_en_i = value;
+ endfunction
+
+ function automatic void update_lc_clk_byp_req(lc_ctrl_pkg::lc_tx_t value);
+ lc_clk_byp_req = value;
+ endfunction
+
+ function automatic void update_ast_clk_byp_ack(lc_ctrl_pkg::lc_tx_t value);
+ ast_clk_byp_ack = value;
+ endfunction
+
function automatic logic get_clk_status();
return pwr_o.clk_status;
endfunction
- task automatic init(logic [NUM_TRANS-1:0] idle, logic ip_clk_en, lc_ctrl_pkg::lc_tx_t scanmode);
- lc_clk_byp_req = lc_ctrl_pkg::Off;
- ast_clk_byp_ack = lc_ctrl_pkg::Off;
- lc_dft_en_i = lc_ctrl_pkg::Off;
+ function automatic void update_exp_clk_io_divs(logic exp_div2_value,
+ logic actual_div2_value,
+ logic exp_div4_value,
+ logic actual_div4_value);
+ exp_clk_io_div2 = exp_div2_value;
+ actual_clk_io_div2 = actual_div2_value;
+ exp_clk_io_div4 = exp_div4_value;
+ actual_clk_io_div4 = actual_div4_value;
+ endfunction
+
+ task automatic init(logic [NUM_TRANS-1:0] idle, logic ip_clk_en, lc_ctrl_pkg::lc_tx_t scanmode,
+ lc_ctrl_pkg::lc_tx_t lc_dft_en = lc_ctrl_pkg::Off,
+ lc_ctrl_pkg::lc_tx_t lc_clk_byp_req = lc_ctrl_pkg::Off,
+ lc_ctrl_pkg::lc_tx_t ast_clk_byp_ack = lc_ctrl_pkg::Off);
+ update_ast_clk_byp_ack(ast_clk_byp_ack);
+ update_lc_clk_byp_req(lc_clk_byp_req);
+ update_lc_dft_en(lc_dft_en);
update_idle(idle);
update_ip_clk_en(ip_clk_en);
update_scanmode(scanmode);
@@ -157,7 +188,6 @@
endclocking
// Pipelining and clocking block for transactional unit clocks.
-
logic [PIPELINE_DEPTH-1:0][NUM_TRANS-1:0] clk_hints_ffs;
logic [PIPELINE_DEPTH-1:0] trans_clk_en_ffs;
always @(posedge clocks_o.clk_main_powerup) begin
@@ -172,4 +202,29 @@
input idle_i;
endclocking
+ clocking extclk_cb @(posedge clk);
+ input extclk_sel;
+ input lc_dft_en_i;
+ input ast_clk_byp_req;
+ input lc_clk_byp_req;
+ endclocking
+
+ // Pipelining and clocking block for external clock bypass. The divisor control is
+ // triggered by an ast ack, which goes through synchronizers.
+ logic step_down_ff;
+ always @(posedge clk) begin
+ if (rst_n) begin
+ step_down_ff <= ast_clk_byp_ack == lc_ctrl_pkg::On;
+ end
+ end
+ clocking step_down_cb @(posedge clk);
+ input step_down = step_down_ff;
+ endclocking
+
+ clocking div_clks_cb @(posedge clocks_o.clk_io_powerup);
+ input exp_clk_io_div2;
+ input actual_clk_io_div2;
+ input exp_clk_io_div4;
+ input actual_clk_io_div4;
+ endclocking
endinterface
diff --git a/hw/ip/clkmgr/dv/env/clkmgr_scoreboard.sv b/hw/ip/clkmgr/dv/env/clkmgr_scoreboard.sv
index b41ea1b..807f3a9 100644
--- a/hw/ip/clkmgr/dv/env/clkmgr_scoreboard.sv
+++ b/hw/ip/clkmgr/dv/env/clkmgr_scoreboard.sv
@@ -2,6 +2,82 @@
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
+// Auxiliary class to deal with divided clocks. It predicts the divided clocks depending
+// on whether the clock is divided as usual (step up), or if one division is undone (step
+// down).
+//
+// The internal div2 clock is always running, and step down means we select the base
+// clk_io instead of the internal clock.
+//
+// The div4 clock is handled differently: we internally maintain both a max and cnt. In
+// normal operation the max is 1, so the div4 clock flips every two cycles; when stepped down
+// the max is set to 0, so the clock flips every clk_io cycle.
+class clock_dividers;
+
+ // Step down means undo one clock divide: runs faster, as in "step down on the gas".
+ typedef enum {DivStepDown, DivStepUp} div_step_e;
+ typedef enum {Div2SelDiv2, Div2SelIo} div2_sel_e;
+
+ // The internal div2 clock, always running.
+ local bit clk_io_div2 = 1'b0;
+ // This selects the io clock when stepped down.
+ local div2_sel_e div2_sel = Div2SelDiv2;
+ // The predicted div4 clock.
+ local bit clk_io_div4 = 1'b0;
+ // The maximum value of cnt: becomes 0 when stepped down.
+ local bit clk_io_div4_max = 1;
+ local bit cnt = 0;
+
+ function new();
+ reset();
+ endfunction
+
+ function void reset();
+ clk_io_div2 = 1'b0;
+ div2_sel = Div2SelDiv2;
+ clk_io_div4 = 1'b0;
+ clk_io_div4_max = 1;
+ cnt = 0;
+ endfunction
+
+ function void increment_div2();
+ clk_io_div2 = ~clk_io_div2;
+ endfunction
+
+ function void increment_div4(bit in_scan_mode);
+ bit real_limit = in_scan_mode ? 1 : clk_io_div4_max;
+ if (cnt < real_limit) begin
+ cnt++;
+ end else begin
+ clk_io_div4 = ~clk_io_div4;
+ cnt = 0;
+ end
+ endfunction
+
+ function void step_div4(div_step_e step);
+ if (step == DivStepUp) clk_io_div4_max = 1;
+ else clk_io_div4_max = 0;
+ endfunction
+
+ function void step_div2(div_step_e step);
+ if (step == DivStepUp) div2_sel = Div2SelDiv2;
+ else div2_sel = Div2SelIo;
+ endfunction
+
+ function string show();
+ return $sformatf("clk_div2=%b div2_sel=%0s clk_div4=%b cnt=%b max=%b",
+ clk_io_div2, div2_sel.name, clk_io_div4, cnt, clk_io_div4_max);
+ endfunction
+
+ function bit get_div2_clk(bit actual_clk_io);
+ return div2_sel == Div2SelIo ? actual_clk_io : clk_io_div2;
+ endfunction
+
+ function bit get_div4_clk();
+ return clk_io_div4;
+ endfunction
+endclass : clock_dividers
+
class clkmgr_scoreboard extends cip_base_scoreboard #(
.CFG_T(clkmgr_env_cfg),
.RAL_T(clkmgr_reg_block),
@@ -28,9 +104,11 @@
task run_phase(uvm_phase phase);
super.run_phase(phase);
fork
+ monitor_ip_clk_en();
monitor_idle();
monitor_ip_clk_en();
monitor_scanmode();
+ monitor_ast_clk_byp();
begin : post_reset
fork
monitor_div4_peri_clock();
@@ -44,6 +122,7 @@
monitor_trans_clock(trans_index);
join_none
end
+ monitor_clk_dividers();
join_none
end
join_none
@@ -166,6 +245,96 @@
end
endtask
+ task monitor_ast_clk_byp();
+ lc_tx_t prev_lc_clk_byp_req = Off;
+ forever @cfg.clkmgr_vif.extclk_cb begin
+ if (cfg.clkmgr_vif.lc_clk_byp_req != prev_lc_clk_byp_req) begin
+ `uvm_info(`gfn, $sformatf("Got lc_clk_byp_req %s",
+ cfg.clkmgr_vif.lc_clk_byp_req == On ? "On" : "Off"),
+ UVM_MEDIUM)
+ prev_lc_clk_byp_req = cfg.clkmgr_vif.lc_clk_byp_req;
+ end
+ if (((cfg.clkmgr_vif.extclk_cb.extclk_sel == On) &&
+ (cfg.clkmgr_vif.extclk_cb.lc_dft_en_i == On)) ||
+ (cfg.clkmgr_vif.extclk_cb.lc_clk_byp_req == On)) begin
+ `DV_CHECK_EQ(cfg.clkmgr_vif.ast_clk_byp_req, On,
+ "Expected ast_clk_byp_req to be On")
+ end
+ end
+ endtask
+
+ task monitor_clk_dividers();
+ clock_dividers dividers = new();
+ clock_dividers::div_step_e prev_div_step = clock_dividers::DivStepUp;
+
+ #1;
+ cfg.io_clk_rst_vif.wait_for_reset();
+ fork
+ forever @(posedge cfg.io_clk_rst_vif.rst_n) begin : handle_dividers_reset
+ dividers.reset();
+ `uvm_info(`gfn, $sformatf("Reset divided clocks: %0s", dividers.show()), UVM_MEDIUM)
+ end
+ forever @cfg.clkmgr_vif.step_down_cb begin : handle_divider_step_change
+ clock_dividers::div_step_e div_step;
+ bit step_down = cfg.clkmgr_vif.step_down_cb.step_down;
+ #0;
+
+ step_down &= (cfg.clkmgr_vif.scanmode_i != On);
+ div_step = step_down ? clock_dividers::DivStepDown : clock_dividers::DivStepUp;
+ if (div_step != prev_div_step) begin
+ `uvm_info(`gfn, $sformatf("Got a %0s request", div_step.name), UVM_LOW)
+ dividers.step_div4(div_step);
+ prev_div_step = div_step;
+ @(negedge cfg.clkmgr_vif.clocks_o.clk_io_powerup) begin
+ // Reconsider scanmode_i since it is asynchronous.
+ dividers.step_div2(step_down && (cfg.clkmgr_vif.scanmode_i != On) ?
+ clock_dividers::DivStepDown : clock_dividers::DivStepUp);
+ end
+ `uvm_info(`gfn, $sformatf("Stepped divided clocks: %0s", dividers.show()), UVM_MEDIUM)
+ end
+ end
+ // Compare divided clocks, always based on values from clocking block (thus preponed).
+ forever @cfg.clkmgr_vif.div_clks_cb begin : check_clocks
+ `DV_CHECK_EQ(cfg.clkmgr_vif.div_clks_cb.actual_clk_io_div4,
+ cfg.clkmgr_vif.div_clks_cb.exp_clk_io_div4,
+ $sformatf("Mismatch for clk_io_div4_powerup, expected %b, got %b",
+ cfg.clkmgr_vif.div_clks_cb.exp_clk_io_div4,
+ cfg.clkmgr_vif.div_clks_cb.actual_clk_io_div4))
+ `DV_CHECK_EQ(cfg.clkmgr_vif.div_clks_cb.actual_clk_io_div2,
+ cfg.clkmgr_vif.div_clks_cb.exp_clk_io_div2,
+ $sformatf("Mismatch for clk_io_div2_powerup, expected %b, got %b",
+ cfg.clkmgr_vif.div_clks_cb.exp_clk_io_div2,
+ cfg.clkmgr_vif.div_clks_cb.actual_clk_io_div2))
+ end
+ forever @(posedge cfg.clkmgr_vif.clocks_o.clk_io_powerup) begin : increment_clocks
+ if (cfg.io_clk_rst_vif.rst_n) begin
+ bit in_scan_mode = cfg.clkmgr_vif.scanmode_i == On;
+ #0;
+ // Deal with div4 update, accounting for scanmode's asynchronicity.
+ // The incremented clock will be useful for the next comparison cycle.
+ dividers.increment_div4(in_scan_mode);
+ dividers.increment_div2();
+ `uvm_info(`gfn, $sformatf("Incremented divided clocks: %0s", dividers.show()), UVM_MEDIUM)
+ `uvm_info(`gfn,
+ $sformatf(
+ "Update for div clk cb: div2 exp=%b, actual=%b, div4 exp=%b, actual=%b",
+ dividers.get_div2_clk(cfg.clkmgr_vif.clocks_o.clk_io_powerup),
+ cfg.clkmgr_vif.clocks_o.clk_io_div2_powerup,
+ dividers.get_div4_clk(),
+ cfg.clkmgr_vif.clocks_o.clk_io_div4_powerup),
+ UVM_LOW)
+ // This delay seems to help with xcelium: without it the actual divided clocks are stale.
+ #1;
+ cfg.clkmgr_vif.update_exp_clk_io_divs(
+ .exp_div2_value(dividers.get_div2_clk(cfg.clkmgr_vif.clocks_o.clk_io_powerup)),
+ .actual_div2_value(cfg.clkmgr_vif.clocks_o.clk_io_div2_powerup),
+ .exp_div4_value(dividers.get_div4_clk()),
+ .actual_div4_value(cfg.clkmgr_vif.clocks_o.clk_io_div4_powerup));
+ end
+ end
+ join
+ endtask
+
virtual task process_tl_access(tl_seq_item item, tl_channels_e channel, string ral_name);
uvm_reg csr;
bit do_read_check = 1'b1;
diff --git a/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_base_vseq.sv b/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_base_vseq.sv
index 179ad75..dece598 100644
--- a/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_base_vseq.sv
+++ b/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_base_vseq.sv
@@ -24,8 +24,8 @@
rand bit ip_clk_en;
rand bit [NUM_TRANS-1:0] idle;
- // This selects scanmode according to sel_scanmode, which is randomized with weights.
- rand lc_tx_t scanmode;
+ // scanmode is set according to sel_scanmode, which is randomized with weights.
+ lc_tx_t scanmode;
rand lc_tx_t scanmode_other;
rand lc_tx_t_sel_e sel_scanmode;
int scanmode_on_weight = 8;
@@ -33,7 +33,16 @@
constraint scanmode_c {
sel_scanmode dist {LcTxTSelOn := scanmode_on_weight, LcTxTSelOff := 4, LcTxTSelOther := 4};
!(scanmode_other inside {On, Off});
- scanmode == get_lc_tx_t_from_sel(sel_scanmode, scanmode_other);
+ }
+
+ // extclk_sel is set according to sel_extclk_sel, which is randomized with weights.
+ lc_tx_t extclk_sel;
+ rand lc_tx_t extclk_sel_other;
+ rand lc_tx_t_sel_e sel_extclk_sel;
+
+ constraint extclk_sel_c {
+ sel_extclk_sel dist {LcTxTSelOn := 4, LcTxTSelOff := 2, LcTxTSelOther := 2};
+ !(extclk_sel_other inside {On, Off});
}
// various knobs to enable certain routines
@@ -41,11 +50,22 @@
`uvm_object_new
+ function void post_randomize();
+ super.post_randomize();
+ scanmode = get_lc_tx_t_from_sel(sel_scanmode, scanmode_other);
+ extclk_sel = get_lc_tx_t_from_sel(sel_extclk_sel, extclk_sel_other);
+ endfunction
+
+ virtual function void set_scanmode_on_low_weight();
+ scanmode_on_weight = 2;
+ endfunction
+
task pre_start();
// These are independent: do them in parallel since pre_start consumes time.
fork
begin
- cfg.clkmgr_vif.init(.idle('1), .ip_clk_en(ip_clk_en), .scanmode(scanmode));
+ cfg.clkmgr_vif.init(.idle('1), .ip_clk_en(ip_clk_en), .scanmode(scanmode),
+ .lc_dft_en(Off));
end
if (do_clkmgr_init) clkmgr_init();
super.pre_start();
diff --git a/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_extclk_vseq.sv b/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_extclk_vseq.sv
new file mode 100644
index 0000000..198550b
--- /dev/null
+++ b/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_extclk_vseq.sv
@@ -0,0 +1,113 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+// The extclk vseq causes the external clock selection to be triggered. More details
+// in the clkmgr_testplan.hjson file.
+class clkmgr_extclk_vseq extends clkmgr_base_vseq;
+ `uvm_object_utils(clkmgr_extclk_vseq)
+
+ `uvm_object_new
+
+ // If the extclk_sel_regwen is clear, it is not possible to select external clocks.
+ // This is tested in regular csr_rw, so here this register is simply set to 1.
+
+ // lc_dft_en is set according to sel_lc_dft_en, which is randomized with weights.
+ lc_tx_t lc_dft_en;
+ rand lc_tx_t lc_dft_en_other;
+ rand lc_tx_t_sel_e sel_lc_dft_en;
+
+ constraint lc_dft_en_c {
+ sel_lc_dft_en dist {LcTxTSelOn := 8, LcTxTSelOff := 2, LcTxTSelOther := 2};
+ !(lc_dft_en_other inside {On, Off});
+ }
+
+ // lc_clk_byp_req is set according to sel_lc_clk_byp_req, which is randomized with weights.
+ lc_tx_t lc_clk_byp_req;
+ rand lc_tx_t lc_clk_byp_req_other;
+ rand lc_tx_t_sel_e sel_lc_clk_byp_req;
+
+ constraint lc_clk_byp_req_c {
+ sel_lc_clk_byp_req dist {LcTxTSelOn := 8, LcTxTSelOff := 2, LcTxTSelOther := 2};
+ !(lc_clk_byp_req_other inside {On, Off});
+ }
+
+ // This randomizes the time when the extclk_sel CSR write and the lc_clk_byp_req
+ // input is asserted for good measure. Of course, there is a good chance only a single
+ // one of these trigger a request, so they are also independently tested.
+ rand int cycles_before_extclk_sel;
+ rand int cycles_before_lc_clk_byp_req;
+ rand int cycles_before_lc_clk_byp_ack;
+ rand int cycles_before_ast_clk_byp_ack;
+ rand int cycles_before_next_trans;
+
+ constraint trans_large_c { num_trans == 16; }
+ constraint cycles_to_stim_c {
+ cycles_before_extclk_sel inside {[4:20]};
+ cycles_before_lc_clk_byp_req inside {[4:20]};
+ cycles_before_lc_clk_byp_ack inside {[4:20]};
+ cycles_before_ast_clk_byp_ack inside {[3:11]};
+ cycles_before_next_trans inside {[15:25]};
+ }
+
+ function void post_randomize();
+ super.post_randomize();
+ lc_dft_en = get_lc_tx_t_from_sel(sel_lc_dft_en, lc_dft_en_other);
+ lc_clk_byp_req = get_lc_tx_t_from_sel(sel_lc_clk_byp_req, lc_clk_byp_req_other);
+ endfunction
+
+ task body();
+ update_csrs_with_reset_values();
+ set_scanmode_on_low_weight();
+ csr_wr(.ptr(ral.extclk_sel_regwen), .value(1));
+ fork
+ forever @cfg.clkmgr_vif.ast_clk_byp_req begin : ast_clk_byp_ack
+ if (cfg.clkmgr_vif.ast_clk_byp_req == lc_ctrl_pkg::On) begin
+ `uvm_info(`gfn, "Got ast_clk_byp_req on", UVM_MEDIUM)
+ cfg.clk_rst_vif.wait_clks(cycles_before_ast_clk_byp_ack);
+ cfg.clkmgr_vif.update_ast_clk_byp_ack(lc_ctrl_pkg::On);
+ end else begin
+ `uvm_info(`gfn, "Got ast_clk_byp_req off", UVM_MEDIUM)
+ cfg.clk_rst_vif.wait_clks(cycles_before_ast_clk_byp_ack);
+ cfg.clkmgr_vif.update_ast_clk_byp_ack(lc_ctrl_pkg::Off);
+ end
+ end
+ forever @cfg.clkmgr_vif.lc_clk_byp_ack begin : lc_clk_byp_ack
+ if (cfg.clkmgr_vif.lc_clk_byp_ack == lc_ctrl_pkg::On) begin
+ `uvm_info(`gfn, "Got lc_clk_byp_ack on", UVM_MEDIUM)
+ end else begin
+ `uvm_info(`gfn, "Got lc_clk_byp_req off", UVM_MEDIUM)
+ cfg.clk_rst_vif.wait_clks(cycles_before_lc_clk_byp_ack);
+ cfg.clkmgr_vif.update_lc_clk_byp_req(lc_ctrl_pkg::Off);
+ end
+ end
+ join_none
+ for (int i = 0; i < num_trans; ++i) begin
+ logic [TL_DW-1:0] value;
+ `DV_CHECK_RANDOMIZE_FATAL(this)
+ // Init needs to be synchronous.
+ @cfg.clk_rst_vif.cb begin
+ cfg.clkmgr_vif.init(.idle(idle), .ip_clk_en(ip_clk_en), .scanmode(scanmode),
+ .lc_dft_en(lc_dft_en));
+ end
+ fork
+ begin
+ cfg.clk_rst_vif.wait_clks(cycles_before_extclk_sel);
+ csr_wr(.ptr(ral.extclk_sel), .value(extclk_sel));
+ end
+ begin
+ cfg.clk_rst_vif.wait_clks(cycles_before_lc_clk_byp_req);
+ cfg.clkmgr_vif.update_lc_clk_byp_req(lc_clk_byp_req);
+ end
+ join
+ `uvm_info(`gfn,
+ $sformatf("extclk_sel=0x%0x, lc_clk_byp_req=0x%0x, lc_dft_en=0x%0x, scanmode=0x%0x",
+ extclk_sel, lc_clk_byp_req, lc_dft_en, scanmode),
+ UVM_MEDIUM)
+ csr_rd_check(.ptr(ral.extclk_sel), .compare_value(extclk_sel));
+ cfg.io_clk_rst_vif.wait_clks(cycles_before_next_trans);
+ end
+ disable fork;
+ endtask
+
+endclass
diff --git a/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_trans_vseq.sv b/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_trans_vseq.sv
index e8e3bcb..45cace6 100644
--- a/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_trans_vseq.sv
+++ b/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_trans_vseq.sv
@@ -23,6 +23,7 @@
`DV_CHECK_RANDOMIZE_FATAL(this)
cfg.clkmgr_vif.init(.idle(idle), .ip_clk_en(ip_clk_en), .scanmode(scanmode));
+
cfg.clk_rst_vif.wait_clks(10);
`uvm_info(`gfn, $sformatf("Updating hints to 0x%0x", initial_hints), UVM_MEDIUM)
csr_wr(.ptr(ral.clk_hints), .value(initial_hints));
diff --git a/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_vseq_list.sv b/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_vseq_list.sv
index 4cfc91c..a9cc4e4 100644
--- a/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_vseq_list.sv
+++ b/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_vseq_list.sv
@@ -4,6 +4,7 @@
`include "clkmgr_base_vseq.sv"
`include "clkmgr_common_vseq.sv"
+`include "clkmgr_extclk_vseq.sv"
`include "clkmgr_peri_vseq.sv"
`include "clkmgr_smoke_vseq.sv"
`include "clkmgr_trans_vseq.sv"