[dv/clkmgr] Fix frequency measurement test
Compute the correct expectation.
Improve randomization for thresholds.
On each test round set at most one clock measurement to fail so the
shared alert expectation functionality can handle the test.
Signed-off-by: Guillermo Maturana <maturana@google.com>
diff --git a/hw/ip/clkmgr/dv/env/clkmgr_env_pkg.sv b/hw/ip/clkmgr/dv/env/clkmgr_env_pkg.sv
index 197b281..47b7ba2 100644
--- a/hw/ip/clkmgr/dv/env/clkmgr_env_pkg.sv
+++ b/hw/ip/clkmgr/dv/env/clkmgr_env_pkg.sv
@@ -70,12 +70,12 @@
localparam int ClkInHz[ClkMesrUsb+1] = {IoClkHz, IoClkHz / 2, IoClkHz / 4, MainClkHz, UsbClkHz};
- localparam int ExpectedClkMeasurement[ClkMesrUsb+1] = {
- ClkInHz[ClkMesrIo] / AonClkHz,
- ClkInHz[ClkMesrIoDiv2] / AonClkHz,
- ClkInHz[ClkMesrIoDiv4] / AonClkHz,
- ClkInHz[ClkMesrMain] / AonClkHz,
- ClkInHz[ClkMesrUsb] / AonClkHz
+ localparam int ExpectedCounts[ClkMesrUsb+1] = {
+ ClkInHz[ClkMesrIo] / AonClkHz - 1,
+ ClkInHz[ClkMesrIoDiv2] / AonClkHz - 1,
+ ClkInHz[ClkMesrIoDiv4] / AonClkHz - 1,
+ ClkInHz[ClkMesrMain] / AonClkHz - 1,
+ ClkInHz[ClkMesrUsb] / AonClkHz - 1
};
// functions
diff --git a/hw/ip/clkmgr/dv/env/clkmgr_scoreboard.sv b/hw/ip/clkmgr/dv/env/clkmgr_scoreboard.sv
index 1e4c528..039a2b4 100644
--- a/hw/ip/clkmgr/dv/env/clkmgr_scoreboard.sv
+++ b/hw/ip/clkmgr/dv/env/clkmgr_scoreboard.sv
@@ -192,11 +192,13 @@
end
end
"recov_err_code": begin
+ do_read_check = 1'b0;
if (cfg.en_cov) begin
// TODO(maturana) Insert coverage.
end
end
"fatal_err_code": begin
+ do_read_check = 1'b0;
if (cfg.en_cov) begin
// TODO(maturana) Insert coverage.
end
diff --git a/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_frequency_vseq.sv b/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_frequency_vseq.sv
index bba5ccc..f2914cb 100644
--- a/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_frequency_vseq.sv
+++ b/hw/ip/clkmgr/dv/env/seq_lib/clkmgr_frequency_vseq.sv
@@ -9,17 +9,79 @@
`uvm_object_new
- rand int cycles_off[ClkMesrUsb + 1];
- constraint cycles_off_c {
- foreach (cycles_off[clk])
- cycles_off[clk] dist {
- [-4 : -2] := 2,
- 0 := 4,
- [2 : 4] := 2
- };
+ // This is measured in aon clocks, and is pretty tight.
+ localparam int CyclesToGetOneMeasurement = 2;
+ // This is measured in clkmgr clk_i clocks. It is set to cover worse case delays.
+ // The clk_i frequency is randomized for IPs, but the clkmgr is hooked to io_div4, which would
+ // allow a tighter number of cycles.
+ // TODO(maturana) Connect clkmgr's clk_i to the powerup io_div4 clock, as is in the chip.
+ localparam int CyclesForErrUpdate = 16;
+
+ // The min ands max offsets from the expected counts. Notice the count occasionally matches
+ // expected_counts - 1, so the offsets are set carefully to avoid spurious results.
+ //
+ // The exp_alert cip feature requires a single alert at a time, so we set at most one of the
+ // clocks to fail measurement.
+ rand int clk_tested;
+ constraint clk_tested_c {clk_tested inside {[ClkMesrIo : ClkMesrUsb]};}
+
+ typedef enum int {
+ MesrLow,
+ MesrRight,
+ MesrHigh
+ } mesr_e;
+ rand mesr_e mesr;
+ rand int min_offset;
+ rand int max_offset;
+
+ constraint thresholds_c {
+ solve clk_tested before mesr;
+ solve mesr before min_offset, max_offset;
+ if (mesr == MesrLow) {
+ min_offset inside {[-4 : -2]};
+ max_offset inside {[-4 : -2]};
+ min_offset <= max_offset;
+ } else
+ if (mesr == MesrRight) {
+ min_offset == -1;
+ max_offset == 1;
+ } else
+ if (mesr == MesrHigh) {
+ min_offset inside {[2 : 4]};
+ max_offset inside {[2 : 4]};
+ min_offset <= max_offset;
+ }
}
+ task disable_frequency_measurement(clk_mesr_e which);
+ `uvm_info(`gfn, $sformatf("Disabling frequency measurement for %0s", which.name), UVM_MEDIUM)
+ case (which)
+ ClkMesrIo: begin
+ csr_wr(.ptr(ral.io_measure_ctrl.en), .value(0));
+ end
+ ClkMesrIoDiv2: begin
+ csr_wr(.ptr(ral.io_div2_measure_ctrl.en), .value(0));
+ end
+ ClkMesrIoDiv4: begin
+ csr_wr(.ptr(ral.io_div4_measure_ctrl.en), .value(0));
+ end
+ ClkMesrMain: begin
+ csr_wr(.ptr(ral.main_measure_ctrl.en), .value(0));
+ end
+ ClkMesrUsb: begin
+ csr_wr(.ptr(ral.usb_measure_ctrl.en), .value(0));
+ end
+ endcase
+ endtask
+
task enable_frequency_measurement(clk_mesr_e which, int min_threshold, int max_threshold);
+ `uvm_info(`gfn, $sformatf(
+ "Enabling frequency measurement for %0s, min=%0d, max=%0d, expected=%0d",
+ which.name,
+ min_threshold,
+ max_threshold,
+ ExpectedCounts[which]
+ ), UVM_MEDIUM)
case (which)
ClkMesrIo: begin
ral.io_measure_ctrl.en.set(1);
@@ -54,15 +116,19 @@
endcase
endtask
+ // This waits a number of cycles so that:
+ // - only one measurement completes, or we could end up with multiple alerts which cause trouble
+ // for the cip exp_alert functionality, and,
+ // - the measurement has had time to update the recov_err_code CSR.
+ task wait_before_read_recov_err_code();
+ // Wait for one measurement (takes an extra cycle to really start).
+ cfg.aon_clk_rst_vif.wait_clks(CyclesToGetOneMeasurement);
+ // Wait for the result to propagate to the recov_err_code CSR.
+ cfg.clk_rst_vif.wait_clks(CyclesForErrUpdate);
+ endtask
+
task body();
logic [TL_DW-1:0] value;
- int expected_ratios[clk_mesr_e] = '{
- ClkMesrIo: IoClkHz / AonClkHz,
- ClkMesrIoDiv2: IoClkHz / 2 / AonClkHz,
- ClkMesrIoDiv4: IoClkHz / 4 / AonClkHz,
- ClkMesrMain: MainClkHz / AonClkHz,
- ClkMesrUsb: UsbClkHz / AonClkHz
- };
update_csrs_with_reset_values();
csr_wr(.ptr(ral.measure_ctrl_regwen), .value(1));
@@ -70,31 +136,70 @@
cfg.aon_clk_rst_vif.set_freq_khz(AonClkHz / 1_000);
// Set the thresholds to get no error.
- foreach (expected_ratios[clk]) begin
- enable_frequency_measurement(clk, expected_ratios[clk] - 2, expected_ratios[clk] + 2);
+ foreach (ExpectedCounts[clk]) begin
+ clk_mesr_e clk_mesr = clk_mesr_e'(clk);
+ enable_frequency_measurement(clk_mesr, ExpectedCounts[clk] - 1, ExpectedCounts[clk]);
end
-
- // Check over a period of many clocks to ensure no errors
- cfg.aon_clk_rst_vif.wait_clks(10);
+ wait_before_read_recov_err_code();
csr_rd_check(.ptr(ral.recov_err_code), .compare_value('0),
.err_msg("Expected no measurement errors"));
+ foreach (ExpectedCounts[clk]) begin
+ clk_mesr_e clk_mesr = clk_mesr_e'(clk);
+ disable_frequency_measurement(clk_mesr);
+ end
+ cfg.aon_clk_rst_vif.wait_clks(2);
// And clear errors.
csr_wr(.ptr(ral.recov_err_code), .value('1));
+ `uvm_info(`gfn, $sformatf("Will run %0d rounds", num_trans), UVM_MEDIUM)
for (int i = 0; i < num_trans; ++i) begin
+ logic [ClkMesrUsb:0] actual_recov_err = '0;
logic [ClkMesrUsb:0] expected_recov_err = '0;
`DV_CHECK_RANDOMIZE_FATAL(this)
- foreach (expected_ratios[clk]) begin
- int center_ratio = expected_ratios[clk] + cycles_off[clk];
- if (cycles_off[clk]) begin
- cfg.scoreboard.set_exp_alert(.alert_name("recov_fault"), .max_delay(40000));
- expected_recov_err[clk] = 1;
+ foreach (ExpectedCounts[clk]) begin
+ clk_mesr_e clk_mesr = clk_mesr_e'(clk);
+ int min_threshold;
+ int max_threshold;
+ int expected = ExpectedCounts[clk];
+ if (clk == clk_tested) begin
+ min_threshold = expected + min_offset;
+ max_threshold = expected + max_offset;
+ if (min_threshold > expected || max_threshold < expected - 1) begin
+ cfg.scoreboard.set_exp_alert(.alert_name("recov_fault"), .max_delay(4000));
+ expected_recov_err[clk] = 1;
+ end
+ end else begin
+ min_threshold = expected - 1;
+ max_threshold = expected;
end
- enable_frequency_measurement(clk, center_ratio - 1, center_ratio + 1);
+ enable_frequency_measurement(clk_mesr, min_threshold, max_threshold);
end
- cfg.aon_clk_rst_vif.wait_clks(4);
- csr_rd_check(.ptr(ral.recov_err_code), .compare_value(expected_recov_err),
- .err_msg("Mismatch in recoverable errors"));
+ wait_before_read_recov_err_code();
+ csr_rd(.ptr(ral.recov_err_code), .value(actual_recov_err));
+ if (actual_recov_err != expected_recov_err) begin
+ logic [ClkMesrUsb:0] mismatch_recov_err = actual_recov_err ^ expected_recov_err;
+ foreach (mismatch_recov_err[clk]) begin
+ clk_mesr_e clk_mesr = clk_mesr_e'(clk);
+ if (mismatch_recov_err[clk]) begin
+ `uvm_info(`gfn, $sformatf(
+ "Mismatch for %0s, expected %b, actual %b",
+ clk_mesr.name,
+ expected_recov_err[clk],
+ actual_recov_err[clk]
+ ), UVM_LOW)
+ end
+ end
+ `uvm_error(`gfn, $sformatf(
+ "Mismatch for recov_err, expected 0b%b, got 0x%b",
+ expected_recov_err,
+ actual_recov_err
+ ))
+ end
+ foreach (ExpectedCounts[clk]) begin
+ clk_mesr_e clk_mesr = clk_mesr_e'(clk);
+ disable_frequency_measurement(clk_mesr);
+ end
+ cfg.aon_clk_rst_vif.wait_clks(2);
// And clear errors.
csr_wr(.ptr(ral.recov_err_code), .value('1));
end
diff --git a/hw/ip/clkmgr/dv/sva/clkmgr_div_sva_if.sv b/hw/ip/clkmgr/dv/sva/clkmgr_div_sva_if.sv
index 053ac97..da1521c 100644
--- a/hw/ip/clkmgr/dv/sva/clkmgr_div_sva_if.sv
+++ b/hw/ip/clkmgr/dv/sva/clkmgr_div_sva_if.sv
@@ -11,7 +11,7 @@
//
// All checks at negedges for simplicity.
interface clkmgr_div_sva_if #(
- parameter int DIV
+ parameter int DIV = 2
) (
input logic clk,
input logic rst_n,
@@ -26,8 +26,15 @@
logic step_down;
always_comb step_down = (lc_step_down || sw_step_down) && !scanmode;
+ sequence WholeLeadHigh_S;
+ step_down || maybe_divided_clk ##1 step_down || !maybe_divided_clk;
+ endsequence
+
+ sequence WholeLeadLow_S;
+ step_down || !maybe_divided_clk ##1 step_down || maybe_divided_clk;
+ endsequence
+
if (DIV == 2) begin : g_div2
- `define _DIV Div2
sequence TracksClk_S;
!step_down || maybe_divided_clk ##1 !step_down || maybe_divided_clk;
@@ -37,9 +44,11 @@
// tracking.
`ASSERT(Div2Stepped_A, $rose(step_down) ##1 step_down [* WAIT_CYCLES] |-> TracksClk_S, !clk,
!rst_n)
+ `ASSERT(Div2Whole_A,
+ $fell(step_down) ##1 !step_down [* WAIT_CYCLES] |-> WholeLeadLow_S or WholeLeadHigh_S,
+ !clk, !rst_n)
end else begin : g_div4
- `define _DIV Div4
sequence StepLeadHigh_S;
!step_down || maybe_divided_clk ##1 !step_down || !maybe_divided_clk;
@@ -52,19 +61,9 @@
`ASSERT(Div4Stepped_A,
$rose(step_down) ##1 step_down [* WAIT_CYCLES] |-> StepLeadLow_S or StepLeadHigh_S,
!clk, !rst_n)
-
- end
-
- sequence WholeLeadHigh_S;
- step_down || maybe_divided_clk ##1 step_down || !maybe_divided_clk;
- endsequence
-
- sequence WholeLeadLow_S;
- step_down || !maybe_divided_clk ##1 step_down || maybe_divided_clk;
- endsequence
-
- `ASSERT(`_DIV``Whole_A,
+ `ASSERT(Div4Whole_A,
$fell(step_down) ##1 !step_down [* WAIT_CYCLES] |-> WholeLeadLow_S or WholeLeadHigh_S,
!clk, !rst_n)
- `undef _DIV
+
+ end
endinterface