blob: 79e47c07a66568db70392cf191636d763b2e4274 [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_io_n,
input logic rst_main_n,
input logic rst_usb_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.
prim_mubi_pkg::mubi4_t io_clk_byp_req;
prim_mubi_pkg::mubi4_t io_clk_byp_ack;
logic jitter_en_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_main;
logic otbn_io_div4;
logic kmac;
logic hmac;
logic aes;
} clk_hints_t;
// The CSR values from the testbench side.
clk_enables_t clk_enables_csr;
clk_hints_t clk_hints_csr;
lc_ctrl_pkg::lc_tx_t extclk_ctrl_csr_sel;
lc_ctrl_pkg::lc_tx_t extclk_ctrl_csr_step_down;
prim_mubi_pkg::mubi4_t jitter_enable_csr;
// 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_ctrl(logic [2*$bits(lc_ctrl_pkg::lc_tx_t)-1:0] value);
{extclk_ctrl_csr_step_down, extclk_ctrl_csr_sel} = value;
endfunction
function automatic void update_clk_enables(clk_enables_t value);
clk_enables_csr = value;
endfunction
function automatic void update_clk_hints(clk_hints_t value);
clk_hints_csr = value;
endfunction
function automatic void update_idle(bit [NUM_TRANS-1:0] 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(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_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 update_jitter_enable(prim_mubi_pkg::mubi4_t value);
jitter_enable_csr = value;
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, 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,
prim_mubi_pkg::mubi4_t io_clk_byp_ack = prim_mubi_pkg::MuBi4False);
update_io_clk_byp_ack(io_clk_byp_ack);
update_idle(idle);
update_lc_clk_byp_req(lc_clk_byp_req);
update_lc_dft_en(lc_dft_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] clk_hint_otbn_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
};
clk_hint_otbn_div4_ffs <= {
clk_hint_otbn_div4_ffs[PIPELINE_DEPTH-2:0], clk_hints_csr[TransOtbnIoDiv4]
};
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;
clk_hint_otbn_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];
input clk_hint_otbn = clk_hint_otbn_div4_ffs[PIPELINE_DEPTH-1];
input otbn_idle = idle_i[TransOtbnIoDiv4];
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 extclk_ctrl_csr_sel;
input lc_dft_en_i;
input io_clk_byp_req;
input lc_clk_byp_req;
input step_down = step_down_ff;
input jitter_enable_csr;
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