|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | // | 
|  | // clkmgr interface. | 
|  |  | 
|  | interface clkmgr_if ( | 
|  | input logic clk, | 
|  | input logic rst_n, | 
|  | input logic clk_main, | 
|  | input logic rst_io_n, | 
|  | input logic rst_main_n, | 
|  | input logic rst_usb_n | 
|  | ); | 
|  | import uvm_pkg::*; | 
|  | import clkmgr_env_pkg::*; | 
|  |  | 
|  | // The ports to the dut side. | 
|  |  | 
|  | localparam int LcTxTWidth = $bits(lc_ctrl_pkg::lc_tx_t); | 
|  |  | 
|  | // Encodes the transactional units that are idle. | 
|  | mubi_hintables_t idle_i; | 
|  |  | 
|  | // pwrmgr req contains ip_clk_en, set to enable the gated clocks. | 
|  | pwrmgr_pkg::pwr_clk_req_t pwr_i; | 
|  |  | 
|  | // outputs clk_status: transitions to 1 if all clocks are enabled, and | 
|  | // to 0 when all are disabled. | 
|  | pwrmgr_pkg::pwr_clk_rsp_t pwr_o; | 
|  |  | 
|  | // scanmode_i == MuBi4True defeats all clock gating. | 
|  | prim_mubi_pkg::mubi4_t scanmode_i; | 
|  |  | 
|  | // Life cycle enables clock bypass functionality. | 
|  | lc_ctrl_pkg::lc_tx_t lc_hw_debug_en_i; | 
|  |  | 
|  | // Life cycle clock bypass request and clkmgr ack. | 
|  | lc_ctrl_pkg::lc_tx_t lc_clk_byp_req; | 
|  | lc_ctrl_pkg::lc_tx_t lc_clk_byp_ack; | 
|  | // clkmgr clock bypass request for io clocks and ast ack: triggered by lc_clk_byp_req. | 
|  | prim_mubi_pkg::mubi4_t io_clk_byp_req; | 
|  | prim_mubi_pkg::mubi4_t io_clk_byp_ack; | 
|  | // clkmgr clock bypass request for all clocks and ast ack: triggered by software. | 
|  | prim_mubi_pkg::mubi4_t all_clk_byp_req; | 
|  | prim_mubi_pkg::mubi4_t all_clk_byp_ack; | 
|  |  | 
|  | prim_mubi_pkg::mubi4_t div_step_down_req; | 
|  |  | 
|  | prim_mubi_pkg::mubi4_t jitter_en_o; | 
|  | clkmgr_pkg::clkmgr_out_t clocks_o; | 
|  |  | 
|  | prim_mubi_pkg::mubi4_t calib_rdy; | 
|  | prim_mubi_pkg::mubi4_t hi_speed_sel; | 
|  |  | 
|  | // Internal DUT signals. | 
|  | // TODO: This is a core env component (i.e. reusable entity) that makes hierarchical references | 
|  | // into the DUT. A better strategy would be to bind this interface to the DUT in tb.sv and use | 
|  | // relative paths instead. | 
|  | `ifndef CLKMGR_HIER | 
|  | `define CLKMGR_HIER tb.dut | 
|  | `endif | 
|  |  | 
|  | // The CSR values from the testbench side. | 
|  | clk_enables_t clk_enables_csr; | 
|  | always_comb | 
|  | clk_enables_csr = '{ | 
|  | usb_peri_en: `CLKMGR_HIER.reg2hw.clk_enables.clk_usb_peri_en.q, | 
|  | io_peri_en: `CLKMGR_HIER.reg2hw.clk_enables.clk_io_peri_en.q, | 
|  | io_div2_peri_en: `CLKMGR_HIER.reg2hw.clk_enables.clk_io_div2_peri_en.q, | 
|  | io_div4_peri_en: `CLKMGR_HIER.reg2hw.clk_enables.clk_io_div4_peri_en.q | 
|  | }; | 
|  |  | 
|  | clk_hints_t clk_hints_csr; | 
|  | always_comb | 
|  | clk_hints_csr = '{ | 
|  | otbn_main: `CLKMGR_HIER.reg2hw.clk_hints.clk_main_otbn_hint.q, | 
|  | kmac: `CLKMGR_HIER.reg2hw.clk_hints.clk_main_kmac_hint.q, | 
|  | hmac: `CLKMGR_HIER.reg2hw.clk_hints.clk_main_hmac_hint.q, | 
|  | aes: `CLKMGR_HIER.reg2hw.clk_hints.clk_main_aes_hint.q | 
|  | }; | 
|  |  | 
|  | clk_hints_t clk_hints_status_csr; | 
|  | always_comb | 
|  | clk_hints_status_csr = '{ | 
|  | otbn_main: `CLKMGR_HIER.u_reg.clk_hints_status_clk_main_otbn_val_qs, | 
|  | kmac: `CLKMGR_HIER.u_reg.clk_hints_status_clk_main_kmac_val_qs, | 
|  | hmac: `CLKMGR_HIER.u_reg.clk_hints_status_clk_main_hmac_val_qs, | 
|  | aes: `CLKMGR_HIER.u_reg.clk_hints_status_clk_main_aes_val_qs | 
|  | }; | 
|  |  | 
|  | prim_mubi_pkg::mubi4_t extclk_ctrl_csr_sel; | 
|  | always_comb begin | 
|  | extclk_ctrl_csr_sel = prim_mubi_pkg::mubi4_t'(`CLKMGR_HIER.reg2hw.extclk_ctrl.sel.q); | 
|  | end | 
|  |  | 
|  | prim_mubi_pkg::mubi4_t extclk_ctrl_csr_step_down; | 
|  | always_comb begin | 
|  | extclk_ctrl_csr_step_down = prim_mubi_pkg::mubi4_t'( | 
|  | `CLKMGR_HIER.reg2hw.extclk_ctrl.hi_speed_sel.q); | 
|  | end | 
|  |  | 
|  | prim_mubi_pkg::mubi4_t jitter_enable_csr; | 
|  | always_comb begin | 
|  | jitter_enable_csr = prim_mubi_pkg::mubi4_t'(`CLKMGR_HIER.reg2hw.jitter_enable.q); | 
|  | end | 
|  |  | 
|  | freq_measurement_t io_freq_measurement; | 
|  | logic io_timeout_err; | 
|  | always @(posedge `CLKMGR_HIER.u_io_meas.u_meas.clk_i) begin | 
|  | if (`CLKMGR_HIER.u_io_meas.u_meas.valid_o) begin | 
|  | io_freq_measurement = '{valid: `CLKMGR_HIER.u_io_meas.u_meas.valid_o, | 
|  | slow: `CLKMGR_HIER.u_io_meas.u_meas.slow_o, | 
|  | fast: `CLKMGR_HIER.u_io_meas.u_meas.fast_o}; | 
|  | `uvm_info("clkmgr_if", $sformatf("Sampled coverage for ClkMesrIo as %p", io_freq_measurement), | 
|  | UVM_HIGH) | 
|  | end | 
|  | end | 
|  | always_comb io_timeout_err = `CLKMGR_HIER.u_io_meas.timeout_err_o; | 
|  |  | 
|  | freq_measurement_t io_div2_freq_measurement; | 
|  | logic io_div2_timeout_err; | 
|  | always @(posedge `CLKMGR_HIER.u_io_div2_meas.u_meas.clk_i) begin | 
|  | if (`CLKMGR_HIER.u_io_div2_meas.u_meas.valid_o) begin | 
|  | io_div2_freq_measurement = '{valid: `CLKMGR_HIER.u_io_div2_meas.u_meas.valid_o, | 
|  | slow: `CLKMGR_HIER.u_io_div2_meas.u_meas.slow_o, | 
|  | fast: `CLKMGR_HIER.u_io_div2_meas.u_meas.fast_o}; | 
|  | `uvm_info("clkmgr_if", $sformatf( | 
|  | "Sampled coverage for ClkMesrIoDiv2 as %p", io_div2_freq_measurement), UVM_HIGH) | 
|  | end | 
|  | end | 
|  | always_comb io_div2_timeout_err = `CLKMGR_HIER.u_io_div2_meas.timeout_err_o; | 
|  |  | 
|  | freq_measurement_t io_div4_freq_measurement; | 
|  | logic io_div4_timeout_err; | 
|  | always @(posedge `CLKMGR_HIER.u_io_div4_meas.u_meas.clk_i) begin | 
|  | if (`CLKMGR_HIER.u_io_div4_meas.u_meas.valid_o) begin | 
|  | io_div4_freq_measurement = '{valid: `CLKMGR_HIER.u_io_div4_meas.u_meas.valid_o, | 
|  | slow: `CLKMGR_HIER.u_io_div4_meas.u_meas.slow_o, | 
|  | fast: `CLKMGR_HIER.u_io_div4_meas.u_meas.fast_o}; | 
|  | `uvm_info("clkmgr_if", $sformatf( | 
|  | "Sampled coverage for ClkMesrIoDiv4 as %p", io_div4_freq_measurement), UVM_HIGH) | 
|  | end | 
|  | end | 
|  | always_comb io_div4_timeout_err = `CLKMGR_HIER.u_io_div4_meas.timeout_err_o; | 
|  |  | 
|  | freq_measurement_t main_freq_measurement; | 
|  | logic main_timeout_err; | 
|  | always @(posedge `CLKMGR_HIER.u_main_meas.u_meas.clk_i) begin | 
|  | if (`CLKMGR_HIER.u_main_meas.u_meas.valid_o) begin | 
|  | main_freq_measurement = '{valid: `CLKMGR_HIER.u_main_meas.u_meas.valid_o, | 
|  | slow: `CLKMGR_HIER.u_main_meas.u_meas.slow_o, | 
|  | fast: `CLKMGR_HIER.u_main_meas.u_meas.fast_o}; | 
|  | `uvm_info("clkmgr_if", $sformatf( | 
|  | "Sampled coverage for ClkMesrMain as %p", main_freq_measurement), UVM_HIGH) | 
|  | end | 
|  | end | 
|  | always_comb main_timeout_err = `CLKMGR_HIER.u_main_meas.timeout_err_o; | 
|  |  | 
|  | freq_measurement_t usb_freq_measurement; | 
|  | logic usb_timeout_err; | 
|  | always @(posedge `CLKMGR_HIER.u_usb_meas.u_meas.clk_i) begin | 
|  | if (`CLKMGR_HIER.u_usb_meas.u_meas.valid_o) begin | 
|  | usb_freq_measurement = '{valid: `CLKMGR_HIER.u_usb_meas.u_meas.valid_o, | 
|  | slow: `CLKMGR_HIER.u_usb_meas.u_meas.slow_o, | 
|  | fast: `CLKMGR_HIER.u_usb_meas.u_meas.fast_o}; | 
|  | `uvm_info("clkmgr_if", $sformatf("Sampled coverage for ClkMesrUsb as %p", usb_freq_measurement | 
|  | ), UVM_HIGH) | 
|  | end | 
|  | end | 
|  | always_comb usb_timeout_err = `CLKMGR_HIER.u_usb_meas.timeout_err_o; | 
|  |  | 
|  | function automatic void update_calib_rdy(prim_mubi_pkg::mubi4_t value); | 
|  | calib_rdy = value; | 
|  | endfunction | 
|  |  | 
|  | function automatic void update_idle(mubi_hintables_t value); | 
|  | idle_i = value; | 
|  | endfunction | 
|  |  | 
|  | function automatic void update_io_ip_clk_en(bit value); | 
|  | pwr_i.io_ip_clk_en = value; | 
|  | endfunction | 
|  |  | 
|  | function automatic void update_main_ip_clk_en(bit value); | 
|  | pwr_i.main_ip_clk_en = value; | 
|  | endfunction | 
|  |  | 
|  | function automatic void update_usb_ip_clk_en(bit value); | 
|  | pwr_i.usb_ip_clk_en = value; | 
|  | endfunction | 
|  |  | 
|  | function automatic void update_scanmode(prim_mubi_pkg::mubi4_t value); | 
|  | scanmode_i = value; | 
|  | endfunction | 
|  |  | 
|  | function automatic void update_lc_debug_en(lc_ctrl_pkg::lc_tx_t value); | 
|  | lc_hw_debug_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_all_clk_byp_ack(prim_mubi_pkg::mubi4_t value); | 
|  | `uvm_info("clkmgr_if", $sformatf("In clkmgr_if update_all_clk_byp_ack with %b", value), | 
|  | UVM_MEDIUM) | 
|  | all_clk_byp_ack = value; | 
|  | endfunction | 
|  |  | 
|  | function automatic void update_div_step_down_req(prim_mubi_pkg::mubi4_t value); | 
|  | `uvm_info("clkmgr_if", $sformatf("In clkmgr_if update_div_step_down_req with %b", value), | 
|  | UVM_MEDIUM) | 
|  | div_step_down_req = value; | 
|  | endfunction | 
|  |  | 
|  | function automatic void update_io_clk_byp_ack(prim_mubi_pkg::mubi4_t value); | 
|  | io_clk_byp_ack = value; | 
|  | endfunction | 
|  |  | 
|  | // TODO:: this fix is not right since there are now 3 status | 
|  | function automatic logic get_clk_status(); | 
|  | return pwr_o.main_status; | 
|  | endfunction | 
|  |  | 
|  | function automatic void force_high_starting_count(clk_mesr_e clk); | 
|  | `uvm_info("clkmgr_if", $sformatf("Forcing count of %0s to all 1.", clk.name()), UVM_MEDIUM) | 
|  | case (clk) | 
|  | ClkMesrIo: `CLKMGR_HIER.u_io_meas.u_meas.cnt = '1; | 
|  | ClkMesrIoDiv2: `CLKMGR_HIER.u_io_div2_meas.u_meas.cnt = '1; | 
|  | ClkMesrIoDiv4: `CLKMGR_HIER.u_io_div4_meas.u_meas.cnt = '1; | 
|  | ClkMesrMain: `CLKMGR_HIER.u_main_meas.u_meas.cnt = '1; | 
|  | ClkMesrUsb: `CLKMGR_HIER.u_usb_meas.u_meas.cnt = '1; | 
|  | default: ; | 
|  | endcase | 
|  | endfunction | 
|  |  | 
|  | task automatic init(mubi_hintables_t idle, prim_mubi_pkg::mubi4_t scanmode, | 
|  | lc_ctrl_pkg::lc_tx_t lc_debug_en = lc_ctrl_pkg::Off, | 
|  | lc_ctrl_pkg::lc_tx_t lc_clk_byp_req = lc_ctrl_pkg::Off, | 
|  | prim_mubi_pkg::mubi4_t calib_rdy = prim_mubi_pkg::MuBi4True); | 
|  | `uvm_info("clkmgr_if", "In clkmgr_if init", UVM_MEDIUM) | 
|  | update_calib_rdy(calib_rdy); | 
|  | update_idle(idle); | 
|  | update_lc_clk_byp_req(lc_clk_byp_req); | 
|  | update_lc_debug_en(lc_debug_en); | 
|  | update_scanmode(scanmode); | 
|  | endtask | 
|  |  | 
|  | // Pipeline signals that go through synchronizers with the target clock domain's clock. | 
|  | // thus the PIPELINE_DEPTH is 2. | 
|  |  | 
|  | // Use clocking blocks clocked by the target clock domain's clock to transfer relevant | 
|  | // control signals back to the scoreboard. | 
|  | localparam int PIPELINE_DEPTH = 2; | 
|  |  | 
|  | // Pipelines and clocking blocks for peripheral clocks. | 
|  |  | 
|  | logic [PIPELINE_DEPTH-1:0] clk_enable_div4_ffs; | 
|  | logic [PIPELINE_DEPTH-1:0] ip_clk_en_div4_ffs; | 
|  | always @(posedge clocks_o.clk_io_div4_powerup or negedge rst_io_n) begin | 
|  | if (rst_io_n) begin | 
|  | clk_enable_div4_ffs <= { | 
|  | clk_enable_div4_ffs[PIPELINE_DEPTH-2:0], clk_enables_csr.io_div4_peri_en | 
|  | }; | 
|  | ip_clk_en_div4_ffs <= {ip_clk_en_div4_ffs[PIPELINE_DEPTH-2:0], pwr_i.io_ip_clk_en}; | 
|  | end else begin | 
|  | clk_enable_div4_ffs <= '0; | 
|  | ip_clk_en_div4_ffs  <= '0; | 
|  | end | 
|  | end | 
|  | clocking peri_div4_cb @(posedge clocks_o.clk_io_div4_powerup or negedge rst_io_n); | 
|  | input ip_clk_en = ip_clk_en_div4_ffs[PIPELINE_DEPTH-1]; | 
|  | input clk_enable = clk_enable_div4_ffs[PIPELINE_DEPTH-1]; | 
|  | endclocking | 
|  |  | 
|  | logic [PIPELINE_DEPTH-1:0] clk_enable_div2_ffs; | 
|  | logic [PIPELINE_DEPTH-1:0] ip_clk_en_div2_ffs; | 
|  | always @(posedge clocks_o.clk_io_div2_powerup or negedge rst_io_n) begin | 
|  | if (rst_io_n) begin | 
|  | clk_enable_div2_ffs <= { | 
|  | clk_enable_div2_ffs[PIPELINE_DEPTH-2:0], clk_enables_csr.io_div2_peri_en | 
|  | }; | 
|  | ip_clk_en_div2_ffs <= {ip_clk_en_div2_ffs[PIPELINE_DEPTH-2:0], pwr_i.io_ip_clk_en}; | 
|  | end else begin | 
|  | clk_enable_div2_ffs <= '0; | 
|  | ip_clk_en_div2_ffs  <= '0; | 
|  | end | 
|  | end | 
|  | clocking peri_div2_cb @(posedge clocks_o.clk_io_div2_powerup or negedge rst_io_n); | 
|  | input ip_clk_en = ip_clk_en_div2_ffs[PIPELINE_DEPTH-1]; | 
|  | input clk_enable = clk_enable_div2_ffs[PIPELINE_DEPTH-1]; | 
|  | endclocking | 
|  |  | 
|  | logic [PIPELINE_DEPTH-1:0] clk_enable_io_ffs; | 
|  | logic [PIPELINE_DEPTH-1:0] ip_clk_en_io_ffs; | 
|  | always @(posedge clocks_o.clk_io_powerup or negedge rst_io_n) begin | 
|  | if (rst_io_n) begin | 
|  | clk_enable_io_ffs <= {clk_enable_io_ffs[PIPELINE_DEPTH-2:0], clk_enables_csr.io_peri_en}; | 
|  | ip_clk_en_io_ffs  <= {ip_clk_en_io_ffs[PIPELINE_DEPTH-2:0], pwr_i.io_ip_clk_en}; | 
|  | end else begin | 
|  | clk_enable_io_ffs <= '0; | 
|  | ip_clk_en_io_ffs  <= '0; | 
|  | end | 
|  | end | 
|  | clocking peri_io_cb @(posedge clocks_o.clk_io_powerup or negedge rst_io_n); | 
|  | input ip_clk_en = ip_clk_en_io_ffs[PIPELINE_DEPTH-1]; | 
|  | input clk_enable = clk_enable_io_ffs[PIPELINE_DEPTH-1]; | 
|  | endclocking | 
|  |  | 
|  | logic [PIPELINE_DEPTH-1:0] clk_enable_usb_ffs; | 
|  | logic [PIPELINE_DEPTH-1:0] ip_clk_en_usb_ffs; | 
|  | always @(posedge clocks_o.clk_usb_powerup or negedge rst_usb_n) begin | 
|  | if (rst_usb_n) begin | 
|  | clk_enable_usb_ffs <= {clk_enable_usb_ffs[PIPELINE_DEPTH-2:0], clk_enables_csr.usb_peri_en}; | 
|  | ip_clk_en_usb_ffs  <= {ip_clk_en_usb_ffs[PIPELINE_DEPTH-2:0], pwr_i.usb_ip_clk_en}; | 
|  | end else begin | 
|  | clk_enable_usb_ffs <= '0; | 
|  | ip_clk_en_usb_ffs  <= '0; | 
|  | end | 
|  | end | 
|  | clocking peri_usb_cb @(posedge clocks_o.clk_usb_powerup or negedge rst_usb_n); | 
|  | input ip_clk_en = ip_clk_en_usb_ffs[PIPELINE_DEPTH-1]; | 
|  | input clk_enable = clk_enable_usb_ffs[PIPELINE_DEPTH-1]; | 
|  | 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 or negedge rst_main_n) begin | 
|  | if (rst_main_n) begin | 
|  | clk_hints_ffs <= {clk_hints_ffs[PIPELINE_DEPTH-2:0], clk_hints_csr}; | 
|  | trans_clk_en_ffs <= {trans_clk_en_ffs[PIPELINE_DEPTH-2:0], pwr_i.main_ip_clk_en}; | 
|  | end else begin | 
|  | clk_hints_ffs <= '0; | 
|  | trans_clk_en_ffs <= '0; | 
|  | end | 
|  | end | 
|  | clocking trans_cb @(posedge clocks_o.clk_main_powerup or negedge rst_main_n); | 
|  | input ip_clk_en = trans_clk_en_ffs[PIPELINE_DEPTH-1]; | 
|  | input clk_hints = clk_hints_ffs[PIPELINE_DEPTH-1]; | 
|  | input idle_i; | 
|  | 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 <= io_clk_byp_ack == prim_mubi_pkg::MuBi4True; | 
|  | end else begin | 
|  | step_down_ff <= 1'b0; | 
|  | end | 
|  | end | 
|  |  | 
|  | clocking clk_cb @(posedge clk); | 
|  | input calib_rdy; | 
|  | input extclk_ctrl_csr_sel; | 
|  | input extclk_ctrl_csr_step_down; | 
|  | input lc_hw_debug_en_i; | 
|  | input io_clk_byp_req; | 
|  | input lc_clk_byp_req; | 
|  | input step_down = step_down_ff; | 
|  | input jitter_enable_csr; | 
|  | endclocking | 
|  |  | 
|  | endinterface |