Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dv] Create and deploy reset agent #25463

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions hw/dv/sv/alert_esc_agent/alert_esc_agent.sv
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,16 @@ class alert_esc_agent extends dv_base_agent#(
end
endfunction

virtual task run_phase(uvm_phase phase);
// TODO MVy: I think an agent is not supposed to have a run phase, this is an issue
virtual task run_main();
if (cfg.is_alert && cfg.is_active && cfg.start_default_rsp_seq) begin
// For host mode, run alert ping auto-response sequence.
if (cfg.if_mode == dv_utils_pkg::Host) begin
alert_sender_ping_rsp_seq m_seq =
alert_sender_ping_rsp_seq::type_id::create("m_seq", this);
uvm_config_db#(uvm_object_wrapper)::set(null, {sequencer.get_full_name(), ".run_phase"},
"default_sequence", m_seq.get_type());
sequencer.start_phase_sequence(phase);
// sequencer.start_phase_sequence(phase); // has been deprecated from UVM ref manual
end else begin
alert_receiver_alert_rsp_seq response_seq;
alert_receiver_ping_seq ping_seq;
Expand All @@ -108,7 +109,7 @@ class alert_esc_agent extends dv_base_agent#(
response_seq = alert_receiver_alert_rsp_seq::type_id::create("response_seq", this);
uvm_config_db#(uvm_object_wrapper)::set(null, {sequencer.get_full_name(), ".run_phase"},
"default_sequence", response_seq.get_type());
sequencer.start_phase_sequence(phase);
// sequencer.start_phase_sequence(phase); // has been deprecated from UVM ref manual

// Also run an alert ping sequence, which will send back-to-back sequence items that wait a
// while and then request a ping (stopping immediately if a genuine alert has been raised).
Expand Down
6 changes: 6 additions & 0 deletions hw/dv/sv/cip_lib/cip_base_env.sv
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,15 @@ class cip_base_env #(type CFG_T = cip_base_env_cfg,
m_tl_agents[i].monitor.a_chan_port.connect(scoreboard.tl_a_chan_fifos[i].analysis_export);
m_tl_agents[i].monitor.d_chan_port.connect(scoreboard.tl_d_chan_fifos[i].analysis_export);
m_tl_agents[i].monitor.channel_dir_port.connect(scoreboard.tl_dir_fifos[i].analysis_export);
// Connect reset status analysis port from the reset agent to the analysis port of this agent
rst_agt.reset_st_ap.connect(m_tl_agents[i].reset_st_exp);
end
foreach (cfg.list_of_alerts[i]) begin
string alert_name = cfg.list_of_alerts[i];
m_alert_agent[alert_name].monitor.alert_esc_port.connect(
scoreboard.alert_fifos[alert_name].analysis_export);
// Connect reset status analysis port from the reset agent to the analysis port of this agent
rst_agt.reset_st_ap.connect(m_alert_agent[alert_name].reset_st_exp);
end

foreach (cfg.m_tl_agent_cfgs[i]) begin
Expand All @@ -155,6 +159,8 @@ class cip_base_env #(type CFG_T = cip_base_env_cfg,

foreach (m_edn_pull_agent[i]) begin
m_edn_pull_agent[i].monitor.analysis_port.connect(scoreboard.edn_fifos[i].analysis_export);
// Connect reset status analysis port from the reset agent to the analysis port of this agent
rst_agt.reset_st_ap.connect(m_edn_pull_agent[i].reset_st_exp);
end
endfunction

Expand Down
7 changes: 6 additions & 1 deletion hw/dv/sv/common_ifs/clk_rst_if.sv
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ interface clk_rst_if #(
// use IfName as a part of msgs to indicate which clk_rst_vif instance
string msg_id = $sformatf("[%m(clk_rst_if):%s]", IfName);

// Since a reset agent agent has been created, the reset shouldn't be driven from this interface
// anymore, but it is fed into it from the reset agent instead and the clock should only
// start once the reset had happen already
bit has_reset_agent = 0;

clocking cb @(posedge clk);
endclocking

Expand Down Expand Up @@ -302,7 +307,7 @@ interface clk_rst_if #(
fork
begin
// Only wait for reset if driving it, otherwise it may never come.
if (drive_rst_n) begin
if (drive_rst_n || has_reset_agent) begin
wait_for_reset(.wait_posedge(1'b0));

// Wait a short time after reset before starting to drive the clock.
Expand Down
27 changes: 27 additions & 0 deletions hw/dv/sv/dv_lib/dv_base_agent.core
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
CAPI=2:
# Copyright lowRISC contributors (OpenTitan project).
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
name: "lowrisc:dv:dv_base_agent"
description: "DV base agent"

filesets:
files_dv:
depend:
- lowrisc:dv:dv_utils
files:
- dv_base_agent_pkg.sv

- dv_base_agent_cfg.sv: {is_include_file: true}
- dv_base_agent_cov.sv: {is_include_file: true}
- dv_base_monitor.sv: {is_include_file: true}
- dv_base_sequencer.sv: {is_include_file: true}
- dv_base_driver.sv: {is_include_file: true}
- dv_base_agent.sv: {is_include_file: true}
- dv_base_seq.sv: {is_include_file: true}
file_type: systemVerilogSource

targets:
default:
filesets:
- files_dv
82 changes: 81 additions & 1 deletion hw/dv/sv/dv_lib/dv_base_agent.sv
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// Use this UVM macro as we may need to implement multiple uvm_analysis_imp, which means
// implemneting multiple write methods which is not possible with the same name.
`uvm_analysis_imp_decl(_agt_reset)

class dv_base_agent #(type CFG_T = dv_base_agent_cfg,
type DRIVER_T = dv_base_driver,
type HOST_DRIVER_T = DRIVER_T,
Expand All @@ -19,7 +23,17 @@ class dv_base_agent #(type CFG_T = dv_base_agent_cfg,
SEQUENCER_T sequencer;
MONITOR_T monitor;

`uvm_component_new
// Analysis port only to forward the reset state to the sub-components to let them implement
// their own write method from the uvm_analysis_imp according to their needs.
uvm_analysis_export #(reset_state_e) reset_st_exp;

uvm_analysis_imp_agt_reset #(reset_state_e, dv_base_agent#(CFG_T,DRIVER_T,HOST_DRIVER_T,
DEVICE_DRIVER_T,SEQUENCER_T,MONITOR_T,COV_T)) reset_st_imp;

function new (string name="", uvm_component parent=null);
super.new(name, parent);
reset_st_imp = new ("reset_st_imp", this);
endfunction : new

function void build_phase(uvm_phase phase);
super.build_phase(phase);
Expand All @@ -29,6 +43,10 @@ class dv_base_agent #(type CFG_T = dv_base_agent_cfg,
end
`uvm_info(`gfn, $sformatf("\n%0s", cfg.sprint()), UVM_HIGH)

if (cfg.has_reset) begin
reset_st_exp = new("reset_st_exp", this);
end

// create components
if (cfg.en_cov) begin
cov = COV_T ::type_id::create("cov", this);
Expand Down Expand Up @@ -56,6 +74,21 @@ class dv_base_agent #(type CFG_T = dv_base_agent_cfg,
if (cfg.is_active && cfg.has_driver) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
// Manage connections to the reset analysis port
if (cfg.has_reset) begin
// To this agent
reset_st_exp.connect(reset_st_imp);
// To the driver
if (cfg.has_driver) begin
reset_st_exp.connect(driver.reset_st_imp);
end
// To the sequencer
if (cfg.is_active) begin
reset_st_exp.connect(sequencer.reset_st_imp);
end
// To the monitor
reset_st_exp.connect(monitor.reset_st_imp);
end
if (cfg.has_req_fifo) begin
monitor.req_analysis_port.connect(sequencer.req_analysis_fifo.analysis_export);
end
Expand All @@ -64,4 +97,51 @@ class dv_base_agent #(type CFG_T = dv_base_agent_cfg,
end
endfunction

virtual function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
// Check if this agent reset state port has been connected, this info is only valid from
// the end_of_elaboration phase. Do this check only if has_reset.
if (cfg.has_reset && (reset_st_exp.size() < 1)) begin
`uvm_fatal(`gfn, "Reset analysis port hasn't been connected to any reset agent")
end
endfunction : end_of_elaboration_phase

virtual task run_phase(uvm_phase phase);
// After each reset, the current transaction should be dropped and get_and_drive should be
// restarted once the reset is being deasserted.
forever begin
// This isolation fork is needed to ensure that "disable fork" call won't kill any other
// processes at the same level from the base classes
fork begin : isolation_fork
fork
begin : main_thread
run_main();
wait(0); // Wait indefinitely to ensure the fork will end because of a reset detection
end
begin : reset_thread
if (cfg.has_reset) begin
wait(cfg.in_reset);
end else begin
wait(0);
end
end
join_any
disable fork; // Terminates all descendants and sub-descendants of isolation_fork
wait(!cfg.in_reset);
end join
end
endtask : run_phase

virtual task run_main();
// All the agents using the run_phase to run some things which should be restarted after each
// reset operation, should overwrite this task as it will be called after reset detection.
endtask : run_main

// This function will be executed each time the reset monitor will detect a reset activity. As
// the monitor will broadcast this activity on a UVM TLM port uvm_analysis_port which is connected
// to this component via a UVM analysis import.
virtual function void write_agt_reset(reset_state_e reset_st);
// TODO MVy: use under_reset or cfg.in_reset ?
endfunction : write_agt_reset

endclass
16 changes: 15 additions & 1 deletion hw/dv/sv/dv_lib/dv_base_agent_cfg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,28 @@ class dv_base_agent_cfg extends uvm_object;
// use for phase_ready_to_end to add additional delay after ok_to_end is set
int ok_to_end_delay_ns = 1000;

// Indicates that the interface is under reset. The derived monitor detects and maintains it.
// Indicates that the interface is under reset. The monitor detects and maintains it as this
// component is always created.
// TODO MVy: that's probably not a good approach to share information via the config class.
// So this should be renamed into under_reset to be more coherent or this should be removed and
// managed locally by each component (monitor, driver and sequencer).
bit in_reset;

// Control knob to set whether the agent requires reset handling. This will imply this agent
// to react to reset state changes passed via the dedicated analysis port.
// Note that most of the agents will require to implement this given that sequencer, driver and
// monitor should drop any ongoing operation when a reset on-the-fly is occuring.
bit has_reset = 1;

bit en_monitor = 1'b1;

`uvm_object_utils_begin(dv_base_agent_cfg)
`uvm_field_int (is_active, UVM_DEFAULT)
`uvm_field_int (en_cov, UVM_DEFAULT)
`uvm_field_int (en_monitor, UVM_DEFAULT)
`uvm_field_int (has_driver, UVM_DEFAULT)
`uvm_field_int (in_reset, UVM_DEFAULT)
`uvm_field_int (has_reset, UVM_DEFAULT)
`uvm_field_enum(if_mode_e, if_mode, UVM_DEFAULT)
`uvm_field_int (has_req_fifo, UVM_DEFAULT)
`uvm_field_int (has_rsp_fifo, UVM_DEFAULT)
Expand Down
66 changes: 60 additions & 6 deletions hw/dv/sv/dv_lib/dv_base_driver.sv
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

// Use this UVM macro as we may need to implement multiple uvm_analysis_imp, which means
// implemneting multiple write methods which is not possible with the same name.
`uvm_analysis_imp_decl(_drv_reset)

class dv_base_driver #(type ITEM_T = uvm_sequence_item,
type CFG_T = dv_base_agent_cfg,
type RSP_ITEM_T = ITEM_T)
Expand All @@ -11,18 +15,68 @@ class dv_base_driver #(type ITEM_T = uvm_sequence_item,
.CFG_T (CFG_T),
.RSP_ITEM_T (RSP_ITEM_T)))

bit under_reset;
CFG_T cfg;

`uvm_component_new
uvm_analysis_imp_drv_reset #(
reset_state_e, dv_base_driver#(ITEM_T,CFG_T,RSP_ITEM_T)) reset_st_imp;

// TODO MVy: some drivers extended from this class are also declaring this bit and managing it,
// so it should be removed from these child-classes as the configuration class is already
// providing a similar information. There is also probably some methods which can be removed then.
bit under_reset;


function new (string name="", uvm_component parent=null);
super.new(name, parent);
reset_st_imp = new ("reset_st_imp", this);
endfunction : new

virtual task run_phase(uvm_phase phase);
fork
reset_signals();
get_and_drive();
join
// After each reset, the current transaction should be dropped and get_and_drive should be
// restarted once the reset is being deasserted.
forever begin
// This isolation fork is needed to ensure that "disable fork" call won't kill any other
// processes at the same level from the base classes
fork begin : isolation_fork
fork
begin : reset_signals_thread
// TODO MVy: reset_signals should probably be a function. This should also be changed
// in all the child classes. Meanwhile, spawn an unblocking thread. Instead of doing this
// in the future we'll be able to only call this reset_signals function, which will be
// implemented by all the extended drivers to actually act on the interface level as
// required. It would be great to uniformise this as some drivers are doing to in different
// ways, via different tasks/functions: invalidate_signals, do_reset_signals, do_reset...
// Additionnally, each drivers should drop the reset management tasks: reset_thread,
// reset_signals...
reset_signals();
wait(0); // Wait indefinitely to ensure the fork will end because of a reset detection
end
begin : main_thread
get_and_drive();
wait(0); // Wait indefinitely to ensure the fork will end because of a reset detection
end
begin : reset_thread
wait(cfg.in_reset);
end
join_any
disable fork; // Terminates all descendants and sub-descendants of isolation_fork
wait(!cfg.in_reset);
end join
end
endtask

// This function will be executed each time the reset monitor will detect a reset activity. As
// the monitor will broadcast this activity on a UVM TLM port uvm_analysis_port which is connected
// to this component via a UVM analysis import.
virtual function void write_drv_reset(reset_state_e reset_st);
if (reset_st == ResetAsserted) begin
// TODO MVy: use under_reset or cfg.in_reset ?
under_reset = 1;
end else begin
under_reset = 0;
end
endfunction : write_drv_reset

// reset signals
virtual task reset_signals();
`uvm_fatal(`gfn, "this is implemented as pure virtual task - please extend")
Expand Down
18 changes: 17 additions & 1 deletion hw/dv/sv/dv_lib/dv_base_env.sv
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class dv_base_env #(type CFG_T = dv_base_env_cfg,
SCOREBOARD_T scoreboard;
COV_T cov;

reset_agent rst_agt;

`uvm_component_new

virtual function void build_phase(uvm_phase phase);
Expand Down Expand Up @@ -52,8 +54,10 @@ class dv_base_env #(type CFG_T = dv_base_env_cfg,
cfg.clk_rst_vif.set_freq_mhz(cfg.clk_freq_mhz);
end


// create components
uvm_config_db #(reset_agent_cfg)::set(this, "*", "cfg", cfg.rst_agt_cfg);
rst_agt = reset_agent::type_id::create("rst_agt", this);

if (cfg.en_cov) begin
cov = COV_T::type_id::create("cov", this);
cov.cfg = cfg;
Expand All @@ -71,4 +75,16 @@ class dv_base_env #(type CFG_T = dv_base_env_cfg,
scoreboard.cov = cov;
endfunction

function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
virtual_sequencer.rst_sqr = rst_agt.sequencer;
if (cfg.en_cov) begin
rst_agt.reset_tr_ap.connect(cov.reset_tr_imp);
end
if (cfg.is_active) begin
rst_agt.reset_st_ap.connect(virtual_sequencer.reset_st_imp);
end
rst_agt.reset_st_ap.connect(scoreboard.reset_st_imp);
endfunction : connect_phase

endclass
Loading
Loading