| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| class aon_timer_scoreboard extends cip_base_scoreboard #( |
| .CFG_T(aon_timer_env_cfg), |
| .RAL_T(aon_timer_reg_block), |
| .COV_T(aon_timer_env_cov) |
| ); |
| `uvm_component_utils(aon_timer_scoreboard) |
| |
| // local variables |
| local bit wkup_en; |
| local bit wkup_num_update_due; |
| local uint wkup_count; |
| local uint prescaler; |
| local uint wkup_thold; |
| |
| local bit wdog_en; |
| local bit wdog_regwen; |
| local bit wdog_pause_in_sleep; |
| local bit wdog_num_update_due; |
| local uint wdog_count; |
| local uint bark_thold; |
| local uint bite_thold; |
| |
| local bit wkup_cause; |
| local uint wkup_num; |
| local uint wdog_bark_num; |
| local uint wdog_bite_num; |
| |
| // expected values |
| local bit intr_status_exp [2]; |
| local bit wdog_rst_req_exp = 0; |
| |
| typedef enum logic { |
| WKUP = 1'b0, |
| WDOG = 1'b1 |
| } timers_e; |
| |
| // TLM agent fifos |
| |
| // local queues to hold incoming packets pending comparison |
| |
| `uvm_component_new |
| |
| function void build_phase(uvm_phase phase); |
| super.build_phase(phase); |
| endfunction |
| |
| function void connect_phase(uvm_phase phase); |
| super.connect_phase(phase); |
| endfunction |
| |
| task run_phase(uvm_phase phase); |
| super.run_phase(phase); |
| fork |
| compute_num_clks(); |
| check_interrupt(); |
| monitor_interrupts(); |
| join_none |
| endtask |
| |
| task monitor_interrupts(); |
| forever begin |
| @(cfg.aon_intr_vif.pins); |
| // Sample interrupt pin coverage for interrupt pins |
| if (cfg.en_cov) begin |
| foreach (cfg.aon_intr_vif.pins[i]) begin |
| cov.intr_pins_cg.sample(i, cfg.aon_intr_vif.sample_pin(.idx(i))); |
| end |
| end |
| end |
| 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; |
| bit write = item.is_write(); |
| uvm_reg_addr_t csr_addr = cfg.ral_models[ral_name].get_word_aligned_addr(item.a_addr); |
| |
| bit addr_phase_read = (!write && channel == AddrChannel); |
| bit addr_phase_write = (write && channel == AddrChannel); |
| bit data_phase_read = (!write && channel == DataChannel); |
| bit data_phase_write = (write && channel == DataChannel); |
| |
| // if access was to a valid csr, get the csr handle |
| if (csr_addr inside {cfg.ral_models[ral_name].csr_addrs}) begin |
| csr = cfg.ral_models[ral_name].default_map.get_reg_by_offset(csr_addr); |
| `DV_CHECK_NE_FATAL(csr, null) |
| end |
| else begin |
| `uvm_fatal(`gfn, $sformatf("Access unexpected addr 0x%0h", csr_addr)) |
| end |
| |
| // if incoming access is a write to a valid csr, then make updates right away |
| if (addr_phase_write) begin |
| void'(csr.predict(.value(item.a_data), .kind(UVM_PREDICT_WRITE), .be(item.a_mask))); |
| if (cfg.en_cov) begin |
| //Sample configuration coverage |
| cov.timer_cfg_cg.sample(prescaler, bark_thold, bite_thold, |
| wkup_thold, wdog_regwen, wdog_pause_in_sleep, wkup_cause); |
| end |
| end |
| |
| // process the csr req |
| // for write, update local variable and fifo at address phase |
| // for read, update predication at address phase and compare at data phase |
| case (csr.get_name()) |
| // add individual case item for each csr |
| "intr_state": begin |
| do_read_check = 1'b0; |
| if (data_phase_write) begin |
| uint intr_state_val = item.a_data; |
| if (intr_state_val[WKUP]) intr_status_exp[WKUP] = 1'b0; |
| if (intr_state_val[WDOG]) intr_status_exp[WDOG] = 1'b0; |
| end |
| // INTR_EN register does not exists in AON timer because the interrupts are |
| // enabled as long as timers are enabled. |
| if (cfg.en_cov && data_phase_read) begin |
| cov.intr_cg.sample(WKUP, wkup_en, item.d_data); |
| cov.intr_cg.sample(WDOG, wdog_en, item.d_data); |
| end |
| end |
| "wkup_ctrl": begin |
| prescaler = get_reg_fld_mirror_value(ral, csr.get_name(), "prescaler"); |
| wkup_en = get_reg_fld_mirror_value(ral, csr.get_name(), "enable"); |
| if (data_phase_write) wkup_num_update_due = 1; |
| end |
| "wkup_cause": begin |
| wkup_cause = csr.get_mirrored_value(); |
| intr_status_exp[WKUP] = item.a_data; |
| end |
| "wkup_count": begin |
| wkup_count = csr.get_mirrored_value(); |
| if (data_phase_write) wkup_num_update_due = 1; |
| end |
| "wkup_thold": begin |
| wkup_thold = csr.get_mirrored_value(); |
| if (data_phase_write) wkup_num_update_due = 1; |
| end |
| "wdog_ctrl": begin |
| wdog_en = get_reg_fld_mirror_value(ral, csr.get_name(), "enable"); |
| wdog_pause_in_sleep = get_reg_fld_mirror_value(ral, csr.get_name(), "pause_in_sleep"); |
| end |
| "wdog_count": begin |
| wdog_count = csr.get_mirrored_value(); |
| if (data_phase_write) wdog_num_update_due = 1; |
| end |
| "wdog_regwen": begin |
| wdog_regwen = csr.get_mirrored_value(); |
| end |
| "wdog_bark_thold": begin |
| bark_thold = csr.get_mirrored_value(); |
| if (data_phase_write) wdog_num_update_due = 1; |
| end |
| "wdog_bite_thold": begin |
| bite_thold = csr.get_mirrored_value(); |
| if (data_phase_write) wdog_num_update_due = 1; |
| end |
| "intr_test": begin |
| uint intr_test_val = item.a_data; |
| if (intr_test_val[WKUP]) intr_status_exp[WKUP] = 1'b1; |
| if (intr_test_val[WDOG]) intr_status_exp[WDOG] = 1'b1; |
| if (cfg.en_cov) begin |
| cov.intr_test_cg.sample(WKUP, intr_test_val[WKUP], |
| wkup_en, intr_status_exp[WKUP]); |
| cov.intr_test_cg.sample(WDOG, intr_test_val[WDOG], |
| wdog_en, intr_status_exp[WDOG]); |
| end |
| end |
| default: begin |
| // No other special behaviour for writes |
| end |
| endcase |
| |
| // On reads, if do_read_check, is set, then check mirrored_value against item.d_data |
| if (data_phase_read) begin |
| if (do_read_check) begin |
| `DV_CHECK_EQ(csr.get_mirrored_value(), item.d_data, |
| $sformatf("reg name: %0s", csr.get_full_name())) |
| end |
| void'(csr.predict(.value(item.d_data), .kind(UVM_PREDICT_READ))); |
| end |
| endtask |
| |
| // Task : check_interrupt |
| // wait for expected # of clocks and check for interrupt state reg and pin |
| virtual task check_interrupt(); |
| forever begin |
| wait (!under_reset); |
| |
| fork : isolation_fork |
| fork |
| wait (under_reset); |
| run_wkup_timer(); |
| run_wdog_timer(); |
| join_any |
| |
| // run_wkup_timer and run_wdog_timer never return so if we've got here then we've gone into |
| // reset. Kill the two timer processes then go around and wait until we come out of reset |
| // again. |
| disable fork; |
| join |
| end |
| endtask : check_interrupt |
| |
| virtual task compute_num_clks(); |
| forever begin : compute_num_clks |
| // calculate number of clocks required to have interrupt from wkup |
| @(wkup_num_update_due or wdog_num_update_due); |
| wait(!under_reset); |
| if (wkup_num_update_due) begin |
| if (wkup_count <= wkup_thold) begin |
| wkup_num = ((wkup_thold - wkup_count + 1) * (prescaler + 1)); |
| end |
| else begin |
| wkup_num = 0; |
| end |
| `uvm_info(`gfn, $sformatf("Calculated WKUP_NUM: %d", wkup_num), UVM_HIGH) |
| end |
| if (wdog_num_update_due) begin |
| // calculate wdog bark |
| if (wdog_count < bark_thold) begin |
| wdog_bark_num = bark_thold - wdog_count; |
| end |
| else begin |
| wdog_bark_num = 0; |
| end |
| `uvm_info(`gfn, $sformatf("Calculated wdog_bark_num: %d", wdog_bark_num), UVM_HIGH) |
| if (wdog_count < bite_thold) begin |
| wdog_bite_num = bite_thold - wdog_count; |
| end |
| else begin |
| wdog_bite_num = 0; |
| end |
| `uvm_info(`gfn, $sformatf("Calculated wdog_bite_num: %d", wdog_bite_num), UVM_HIGH) |
| end |
| wkup_num_update_due = 0; |
| wdog_num_update_due = 0; |
| end // compute_num_clks |
| endtask |
| |
| virtual task run_wkup_timer(); |
| forever begin |
| wait (wkup_en); |
| fork |
| begin |
| // trying to count how many cycles we need to count |
| uint count = 0; |
| // We are catching the enable signal too early. It takes one cycle to save to the register |
| // one more cycle to propagate to hw from the read port of it. |
| cfg.aon_clk_rst_vif.wait_clks(2); |
| while (count <= wkup_num) begin |
| @cfg.aon_clk_rst_vif.cb; |
| // reset the cycle counter when we update the cycle count needed |
| count = wkup_num_update_due ? 0 : (count + 1); |
| `uvm_info(`gfn, $sformatf("WKUP Timer count: %d", count), UVM_HIGH) |
| end |
| `uvm_info(`gfn, $sformatf("WKUP Timer expired check for interrupts"), UVM_HIGH) |
| // Interrupt should happen N+1 clock ticks after count == wkup_num. |
| cfg.aon_clk_rst_vif.wait_clks(prescaler+1); |
| intr_status_exp[WKUP] = 1'b1; |
| `DV_CHECK_CASE_EQ(intr_status_exp[WKUP], |
| cfg.aon_intr_vif.sample_pin(.idx(1))) |
| `uvm_info(`gfn, $sformatf("WKUP Timer check passed."), UVM_HIGH) |
| end |
| begin |
| wait (!wkup_en || cfg.aon_clk_rst_vif.rst_n); |
| `uvm_info(`gfn, $sformatf("WKUP Timer disabled, quit scoring"), UVM_HIGH) |
| wkup_en = 0; |
| end |
| join_any |
| disable fork; |
| end |
| endtask |
| |
| virtual task run_wdog_timer(); |
| forever begin |
| wait (wdog_en); |
| fork |
| begin |
| // trying to count how many cycles we need to count |
| uint count = 0; |
| // We are catching the enable signal too early. It takes one cycle to save to the register |
| // one more cycle to propagate to hw from the read port of it. |
| cfg.aon_clk_rst_vif.wait_clks(2); |
| while (count <= wdog_bark_num || count <= wdog_bite_num) begin |
| @cfg.aon_clk_rst_vif.cb; |
| // reset the cycle counter when we update the cycle count needed |
| count = wdog_num_update_due ? 0 : (count + 1); |
| `uvm_info(`gfn, $sformatf("WDOG Timer count: %d", count), UVM_HIGH) |
| end |
| `uvm_info(`gfn, $sformatf("WDOG Timer expired check for interrupts"), UVM_HIGH) |
| if (count > wdog_bark_num) intr_status_exp[WDOG] = 1'b1; |
| if (count > wdog_bite_num) wdog_rst_req_exp = 1'b1; |
| // Propagation delay of one cycle from aon_core to interrupt pins. |
| cfg.aon_clk_rst_vif.wait_clks(1); |
| `DV_CHECK_CASE_EQ(intr_status_exp[WDOG], |
| cfg.aon_intr_vif.sample_pin(.idx(1))) |
| `DV_CHECK_CASE_EQ(wdog_rst_req_exp, |
| cfg.aon_intr_vif.sample_pin(.idx(0))) |
| `uvm_info(`gfn, |
| $sformatf("WDOG INTR Bark: %d, Bite: %d", |
| intr_status_exp[WDOG], |
| wdog_rst_req_exp), |
| UVM_HIGH) |
| end |
| begin |
| wait (!wdog_en || cfg.aon_clk_rst_vif.rst_n); |
| `uvm_info(`gfn, $sformatf("WDOG Timer disabled, quit scoring"), UVM_HIGH) |
| wdog_en = 0; |
| end |
| join_any |
| disable fork; |
| end |
| endtask |
| |
| virtual function void reset(string kind = "HARD"); |
| super.reset(kind); |
| // reset local fifos queues and variables |
| endfunction |
| |
| function void check_phase(uvm_phase phase); |
| super.check_phase(phase); |
| // post test checks - ensure that all local fifos and queues are empty |
| endfunction |
| |
| endclass |