| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| // |
| |
| // Enhanced DV_ASSERT_CTRL which kills active assertions before disabling them |
| `ifndef ADC_CTRL_DV_ASSERT_CTRL |
| `define ADC_CTRL_DV_ASSERT_CTRL(LABEL_, HIER_, LEVELS_ = 0, SCOPE_ = "", ID_ = $sformatf("%m")) \ |
| initial begin \ |
| bit assert_en; \ |
| forever begin \ |
| uvm_config_db#(bit)::wait_modified(null, SCOPE_, LABEL_); \ |
| if (!uvm_config_db#(bit)::get(null, SCOPE_, LABEL_, assert_en)) begin \ |
| `uvm_fatal(ID_, $sformatf("Failed to get \"%0s\" from uvm_config_db", LABEL_)) \ |
| end \ |
| if (assert_en) begin \ |
| `uvm_info(ID_, $sformatf("Enabling assertions: %0s", `DV_STRINGIFY(HIER_)), UVM_LOW) \ |
| $asserton(LEVELS_, HIER_); \ |
| end else begin \ |
| `uvm_info(ID_, $sformatf("Disabling assertions: %0s", `DV_STRINGIFY(HIER_)), UVM_LOW) \ |
| $assertkill(LEVELS_, HIER_); \ |
| $assertoff(LEVELS_, HIER_); \ |
| end \ |
| end \ |
| end |
| `endif |
| |
| |
| module tb; |
| // dep packages |
| import uvm_pkg::*; |
| import dv_utils_pkg::*; |
| |
| import adc_ctrl_env_pkg::*; |
| import adc_ctrl_test_pkg::*; |
| |
| // macro includes |
| `include "uvm_macros.svh" |
| `include "dv_macros.svh" |
| `include "prim_assert.sv" |
| |
| wire clk, rst_n; |
| wire clk_aon, rst_aon_n; |
| wire devmode; |
| wire [NUM_MAX_INTERRUPTS-1:0] interrupts; |
| wire wakeup_req; |
| wire [ADC_CTRL_CHANNELS - 1 : 0] adc_channel_sel, adc_data_valid; |
| logic [ADC_CTRL_CHANNELS - 1 : 0] adc_if_reqs; |
| wire [ADC_CTRL_CHANNELS - 1 : 0][ADC_CTRL_DATA_WIDTH - 1 : 0] adc_data; |
| wire ast_pkg::adc_ast_req_t adc_o; |
| ast_pkg::adc_ast_rsp_t adc_i; |
| adc_ctrl_env_cfg cfg; |
| // Basic testing mode |
| adc_ctrl_testmode_e cfg_testmode; |
| // Auxiliary logic to time power up -> first channel request |
| int pwrup_time, cfg_pwrup_time; |
| bit pwrup_time_en; |
| // Auxiliary logic to time power down -> power up |
| int wakeup_time, cfg_wakeup_time; |
| bit wakeup_time_en; |
| logic pd_prev; |
| bit cfg_lp_mode; |
| |
| `DV_ALERT_IF_CONNECT() |
| |
| // interfaces |
| clk_rst_if clk_rst_if ( |
| .clk (clk), |
| .rst_n(rst_n) |
| ); |
| clk_rst_if clk_aon_rst_if ( |
| .clk (clk_aon), |
| .rst_n(rst_aon_n) |
| ); |
| pins_if #(NUM_MAX_INTERRUPTS) intr_if (interrupts); |
| pins_if #(1) wakeup_if (wakeup_req); |
| pins_if #(1) devmode_if (devmode); |
| tl_if tl_if ( |
| .clk (clk), |
| .rst_n(rst_n) |
| ); |
| |
| // Array of push pull interfaces, one per ADC channel |
| push_pull_if #( |
| .DeviceDataWidth(ADC_CTRL_DATA_WIDTH) |
| ) adc_if[ADC_CTRL_CHANNELS] ( |
| .clk (clk_aon), |
| .rst_n(rst_aon_n) |
| ); |
| |
| // dut |
| adc_ctrl dut ( |
| .clk_i (clk), |
| .rst_ni (rst_n), |
| .clk_aon_i (clk_aon), |
| .rst_aon_ni (rst_aon_n), |
| .tl_i (tl_if.h2d), |
| .tl_o (tl_if.d2h), |
| .alert_rx_i (alert_rx), |
| .alert_tx_o (alert_tx), |
| .adc_o (adc_o), |
| .adc_i (adc_i), |
| .intr_match_done_o (interrupts[ADC_CTRL_INTERRUPT_INDEX]), |
| .wkup_req_o (wakeup_req) |
| ); |
| |
| initial begin |
| // drive clk and rst_n from clk_if |
| clk_aon_rst_if.set_active(); |
| clk_rst_if.set_active(); |
| clk_aon_rst_if.set_freq_khz(200); |
| uvm_config_db#(virtual clk_rst_if)::set(null, "*.env", "clk_rst_vif", clk_rst_if); |
| uvm_config_db#(virtual clk_rst_if)::set(null, "*.env", "clk_aon_rst_vif", clk_aon_rst_if); |
| uvm_config_db#(intr_vif)::set(null, "*.env", "intr_vif", intr_if); |
| uvm_config_db#(wakeup_vif_t)::set(null, "*.env", "wakeup_vif", wakeup_if); |
| uvm_config_db#(devmode_vif)::set(null, "*.env", "devmode_vif", devmode_if); |
| uvm_config_db#(virtual tl_if)::set(null, "*.env.m_tl_agent*", "vif", tl_if); |
| $timeformat(-12, 0, " ps", 12); |
| run_test(); |
| end |
| |
| initial begin |
| #1ps; |
| if (!uvm_config_db#(adc_ctrl_env_cfg)::get( |
| null, "uvm_test_top.env", "cfg", cfg |
| ) || cfg == null) begin |
| `uvm_fatal("TB", "Couldn't find the environment config") |
| end |
| `uvm_info("TB", "Found environment config", UVM_MEDIUM) |
| |
| // Constantly update from configuration object |
| forever begin |
| cfg_testmode = cfg.testmode; |
| cfg_pwrup_time = cfg.pwrup_time; |
| cfg_wakeup_time = cfg.wakeup_time; |
| cfg_lp_mode = cfg.lp_mode; |
| @(cfg.pwrup_time or cfg.wakeup_time or cfg.testmode or cfg.lp_mode); |
| end |
| end |
| |
| // Push pull agents |
| // Need to use generate loop as idx must be an elaborataion time constant |
| for (genvar idx = 0; idx < ADC_CTRL_CHANNELS; idx++) begin : g_adc_if_connections |
| initial begin |
| uvm_config_db#(adc_push_pull_vif_t)::set(null, $sformatf("*env.m_adc_push_pull_agent_%0d", idx |
| ), "vif", adc_if[idx]); |
| end |
| |
| // Assign inputs and outputs |
| |
| // Convert data valid and data into a packed arrays for the Mux below. |
| assign adc_data_valid[idx] = adc_if[idx].ack; |
| assign adc_data[idx] = adc_if[idx].d_data; |
| |
| // Connect requests |
| //assign adc_if[idx].req = adc_o.channel_sel[idx] & ~adc_o.pd; |
| assign adc_if[idx].req = adc_if_reqs[idx]; |
| end |
| |
| // Output decode |
| // We assert an adc_if request if: |
| // 1. The coresponding channel is selected |
| // 2. Power Down is not asserted |
| // 3. No other channel has an acknowledge |
| always_comb begin : adc_o_decode |
| // default all off |
| adc_if_reqs = 0; |
| for (int idx = 0; idx < ADC_CTRL_CHANNELS; idx++) begin |
| if (adc_o.channel_sel[idx] === 1 && adc_o.pd === 0) begin |
| adc_if_reqs[idx] = 1; |
| // Check no ack from another channel |
| for (int idx_1 = 0; idx_1 < ADC_CTRL_CHANNELS; idx_1++) begin |
| if (adc_data_valid[idx_1] === 1 && (idx_1 != idx)) adc_if_reqs[idx] = 0; |
| end |
| end |
| end |
| end |
| |
| // Input mux |
| always_comb begin : adc_i_mux |
| // Just or the valids |
| adc_i.data_valid = |adc_data_valid; |
| adc_i.data = 'X; |
| if (adc_o.pd === 0) begin |
| // Only if power down deasserted |
| for (int idx = 0; idx < ADC_CTRL_CHANNELS; idx++) begin |
| if (adc_data_valid[idx] === 1) begin |
| //adc_i.data_valid = adc_data_valid[idx]; |
| adc_i.data = adc_data[idx]; |
| break; |
| end |
| end |
| end |
| end |
| |
| // Auxiliary logic to time clocks since power down deasserted |
| always @(posedge clk_aon or negedge rst_aon_n) begin |
| if (!rst_aon_n) begin |
| pwrup_time <= 0; |
| pwrup_time_en <= 1; |
| end else begin |
| if (adc_o.pd == 1) begin |
| pwrup_time <= 0; |
| pwrup_time_en <= 1; |
| end else begin |
| if (|adc_o.channel_sel) pwrup_time_en <= 0; |
| else if (pwrup_time_en) pwrup_time <= pwrup_time + 1; |
| end |
| end |
| end |
| // Pulse to check power up counter |
| wire pwrup_time_chk = |adc_o.channel_sel & pwrup_time_en; |
| |
| // Auxiliary logic to time clocks from power down to power up |
| always @(posedge clk_aon or negedge rst_aon_n) begin |
| if (!rst_aon_n) begin |
| wakeup_time <= 0; |
| wakeup_time_en <= 0; |
| pd_prev <= 0; |
| end else begin |
| if (adc_o.pd & ~pd_prev) begin |
| // Positive edge on adc_o.pd begin counting wakeup time |
| wakeup_time <= 0; |
| wakeup_time_en <= 1; |
| end else if (~adc_o.pd & pd_prev) begin |
| // Negative edge on adc_o.pd stop counting wakeup time |
| wakeup_time_en <= 0; |
| end else if (wakeup_time_en) begin |
| wakeup_time <= wakeup_time + 1; |
| end |
| // Delay PD for edge detect |
| pd_prev <= adc_o.pd; |
| end |
| end |
| // Pulse to check wake up counter |
| wire wakeup_time_chk = ~adc_o.pd & pd_prev; |
| // Model expects RTL to be in low power mode. |
| wire testmode_low_power = cfg_testmode inside {AdcCtrlTestmodeLowpower} && cfg_lp_mode; |
| |
| // Check the DUT enters low power |
| // In low power test mode, after falling edges on power down |
| // and the last ADC channel select, power down should be re-asserted within 10 clock cycles |
| //verilog_format: off - avoid bad formatting |
| property EnterLowPower_P; |
| first_match($fell(adc_o.pd) ##[+] $fell(adc_o.channel_sel[ADC_CTRL_CHANNELS - 1])) |=> |
| ##[0:3] adc_o.pd; |
| endproperty |
| //verilog_format: on |
| |
| // Assertions |
| `ASSERT(ChannelSelOnehot_A, $onehot0(adc_o.channel_sel), clk_aon, ~rst_aon_n) |
| `ASSERT_KNOWN(ChannelSelKnown_A, adc_o.channel_sel, clk_aon, ~rst_aon_n) |
| `ASSERT_KNOWN(PdKnown_A, adc_o.pd, clk_aon, ~rst_aon_n) |
| `ASSERT(PwrupTime_A, $rose(pwrup_time_chk) |-> pwrup_time == (cfg_pwrup_time + 1), clk_aon, |
| ~rst_aon_n) |
| `ASSERT(WakeupTime_A, $rose(wakeup_time_chk) |-> wakeup_time == cfg_wakeup_time, clk_aon, |
| ~rst_aon_n) |
| `ASSERT(EnterLowPower_A, EnterLowPower_P, clk_aon, ~rst_aon_n | ~testmode_low_power) |
| |
| |
| // Assertion controls |
| `ADC_CTRL_DV_ASSERT_CTRL("ADC_IF_A_CTRL", adc_if[0]) |
| `ADC_CTRL_DV_ASSERT_CTRL("ADC_IF_A_CTRL", adc_if[1]) |
| `DV_ASSERT_CTRL("PwrupTime_A_CTRL", PwrupTime_A) |
| `DV_ASSERT_CTRL("WakeupTime_A_CTRL", WakeupTime_A) |
| `DV_ASSERT_CTRL("EnterLowPower_A_CTRL", EnterLowPower_A) |
| `DV_ASSERT_CTRL("ADC_CTRL_FSM_A_CTRL", dut.u_adc_ctrl_core.u_adc_ctrl_fsm) |
| |
| endmodule |