blob: 395d40b94f5f02f3b8ac1641e4c0dd4d10ff4223 [file] [log] [blame]
// 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 rst_main_n);
import clkmgr_env_pkg::*;
// The ports to the dut side.
// Encodes the transactional units that are idle.
logic [NUM_TRANS-1:0] 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 == lc_ctrl_pkg::On defeats all clock gating.
lc_ctrl_pkg::lc_tx_t scanmode_i;
// Life cycle enables clock bypass functionality.
lc_ctrl_pkg::lc_tx_t lc_dft_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 and ast ack.
lc_ctrl_pkg::lc_tx_t ast_clk_byp_req;
lc_ctrl_pkg::lc_tx_t ast_clk_byp_ack;
logic jitter_en_o;
clkmgr_pkg::clkmgr_ast_out_t clocks_ast_o;
clkmgr_pkg::clkmgr_out_t clocks_o;
// Types for CSR values.
typedef struct packed {
logic usb_peri_en;
logic io_peri_en;
logic io_div2_peri_en;
logic io_div4_peri_en;
} clk_enables_t;
typedef struct packed {
logic otbn;
logic kmac;
logic hmac;
logic aes;
} clk_hints_t;
// The CSR values from the testbench side.
clk_enables_t clk_enables;
clk_hints_t clk_hints;
clk_hints_t clk_hints_status;
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
function automatic void update_clk_enables(clk_enables_t value);
clk_enables = value;
endfunction
function automatic void update_clk_hints(clk_hints_t value);
clk_hints = value;
endfunction
function automatic void update_idle(bit [NUM_TRANS-1:0] value);
idle_i = value;
endfunction
function automatic void update_ip_clk_en(bit value);
pwr_i.ip_clk_en = value;
endfunction
function automatic void update_scanmode(lc_ctrl_pkg::lc_tx_t value);
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
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);
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) begin
if (rst_n) begin
clk_enable_div4_ffs <= {clk_enable_div4_ffs[PIPELINE_DEPTH-2:0], clk_enables.io_div4_peri_en};
ip_clk_en_div4_ffs <= {ip_clk_en_div4_ffs[PIPELINE_DEPTH-2:0], pwr_i.ip_clk_en};
end
end
clocking peri_div4_cb @(posedge clocks_o.clk_io_div4_powerup);
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) begin
if (rst_n) begin
clk_enable_div2_ffs <= {clk_enable_div2_ffs[PIPELINE_DEPTH-2:0], clk_enables.io_div2_peri_en};
ip_clk_en_div2_ffs <= {ip_clk_en_div2_ffs[PIPELINE_DEPTH-2:0], pwr_i.ip_clk_en};
end
end
clocking peri_div2_cb @(posedge clocks_o.clk_io_div2_powerup);
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) begin
if (rst_n) begin
clk_enable_io_ffs <= {clk_enable_io_ffs[PIPELINE_DEPTH-2:0], clk_enables.io_peri_en};
ip_clk_en_io_ffs <= {ip_clk_en_io_ffs[PIPELINE_DEPTH-2:0], pwr_i.ip_clk_en};
end
end
clocking peri_io_cb @(posedge clocks_o.clk_io_powerup);
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) begin
if (rst_n) begin
clk_enable_usb_ffs <= {clk_enable_usb_ffs[PIPELINE_DEPTH-2:0], clk_enables.usb_peri_en};
ip_clk_en_usb_ffs <= {ip_clk_en_usb_ffs[PIPELINE_DEPTH-2:0], pwr_i.ip_clk_en};
end
end
clocking peri_usb_cb @(posedge clocks_o.clk_usb_powerup);
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) begin
if (rst_n) begin
clk_hints_ffs <= {clk_hints_ffs[PIPELINE_DEPTH-2:0], clk_hints};
trans_clk_en_ffs <= {trans_clk_en_ffs[PIPELINE_DEPTH-2:0], pwr_i.ip_clk_en};
end
end
clocking trans_cb @(posedge clocks_o.clk_main_powerup);
input ip_clk_en = trans_clk_en_ffs[PIPELINE_DEPTH-1];
input clk_hints = clk_hints_ffs[PIPELINE_DEPTH-1];
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