Чтобы сбросить отдельные агенты тестовой среды, я пытаюсь перенести их на отдельные домены. Однако я столкнулся с трудностью: когда я устанавливаю отдельный домен для агента, элементы последовательности перестают поступать к драйверу этого агента.
Ниже приведен самый простой пример, который я мог написать. Если закомментировать строки
ag1.set_domain (d1);
ag2.set_domain (d2);
затем данные получат водители агентов; если вы раскомментируете его, они остановятся. Однако, если вы поместите jump
внутри блока fork
, это произойдет.
Если вы переместите настройку домена в основную фазу тестового класса, данные перейдут, но переход от jump
к pre_reset_phase
не произойдет.
`include "uvm_macros.svh"
package t;
import uvm_pkg::*;
class seq_item extends uvm_sequence_item;
`uvm_object_utils(seq_item)
rand bit [31:0] data;
function new(string name = "apb_seq_item");
super.new(name);
endfunction: new
endclass: seq_item
class m_sequence extends uvm_sequence#(seq_item);
`uvm_object_utils(m_sequence)
function new(string name = "");
super.new(name);
endfunction: new
task body();
repeat(5) begin
req = seq_item::type_id::create("ap_it");
start_item(req);
req.randomize();
finish_item(req);
end
endtask: body
endclass: m_sequence
class driver extends uvm_driver#(seq_item);
`uvm_component_utils(driver)
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction: new
task main_phase(uvm_phase phase);
fork
super.main_phase(phase);
join_none
forever begin
seq_item_port.get_next_item(req);
`uvm_info(get_type_name(),$psprintf("Got item with data: %h",req.data),UVM_NONE);
seq_item_port.item_done();
end
endtask: main_phase
endclass: driver
class agent extends uvm_agent;
`uvm_component_utils(agent)
uvm_analysis_port#(seq_item) ap;
uvm_sequencer#(seq_item) seqr;
driver drv;
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
ap = new(.name("apb_ap"), .parent(this));
seqr= uvm_sequencer#(seq_item) ::type_id::create(.name("uvm_sequenver"), .parent(this) );
drv = driver ::type_id::create(.name("driver"), .parent(this) );
endfunction: build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(seqr.seq_item_export);
endfunction: connect_phase
task pre_reset_phase(uvm_phase phase);
fork
super.pre_reset_phase(phase);
join_none
`uvm_info(get_type_name(),"jumped into pre_reset_phase",UVM_NONE);
endtask
endclass: agent
class env extends uvm_env;
`uvm_component_utils(env)
agent ag1;
agent ag2;
uvm_domain d1;
uvm_domain d2;
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
ag1 = agent::type_id::create("ag1", this);
ag2 = agent::type_id::create("ag2", this);
d1 = new("d1");
d2 = new("d2");
ag1.set_domain(d1);
ag2.set_domain(d2);
endfunction: build_phase
endclass: env
class test extends uvm_test;
`uvm_component_utils(test)
env e;
m_sequence seq1;
m_sequence seq2;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
e = env::type_id::create("env",this);
endfunction
task main_phase(uvm_phase phase);
fork
super.main_phase(phase);
join_none
phase.raise_objection(this);
seq1 = m_sequence::type_id::create("se1");
seq2 = m_sequence::type_id::create("se2");
fork
seq1.start(e.ag1.seqr);
seq2.start(e.ag2.seqr);
join
e.d1.jump(uvm_pre_reset_phase::get());
phase.drop_objection(this);
endtask
endclass: test
endpackage
module top();
import uvm_pkg::*;
import t::*;
initial begin
run_test();
end
endmodule
Фазы выполнения UVM используются для управления порядок вещей. Если что-то делается на разных этапах, то вы можете гарантировать, что что-то, сделанное на более позднем этапе, произойдет до того, что будет сделано на более раннем этапе.
Ваш исходный код создает два новых фазовых домена и помещает двух агентов в эти новые домены. Остальная часть тестового стенда находится в исходном домене. Если вы не синхронизируете домены, вы больше не можете гарантировать, в каком порядке будут происходить события.
Итак, я внес несколько изменений в ваш код:
i) Я добавил ссылку на последовательность в агенте:
m_sequence seq;
ii) я добавил код в основную фазу агента, чтобы (а) поднять возражение и (б) запустить последовательность. Теперь (а) каждая последовательность запускается в нужное время в своей области и (б) что особенно важно, фаза не закончится, пока последовательность не будет завершена. (Вы возражаете против окончания фазы, поэтому теперь для каждой main_phase требуется свое возражение.)
task main_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info(get_type_name(),"jumped into main_phase",UVM_NONE);
seq.start(seqr);
phase.drop_objection(this);
endtask
iii) Я добавил дополнительный параметр в ваш вызов метода set_domain
, чтобы потомки агентов также помещались в новый домен (по умолчанию это не так):
ag1.set_domain(d1,1);
ag2.set_domain(d2,1);
iv) Я поместил код для создания последовательностей в configure_phase теста. Я сомневаюсь, что это гарантирует, что последовательности создаются до начала основных фаз, поэтому это работает скорее по счастливой случайности, чем по суждению. (Вам нужно использовать тот факт, что фазы гарантируют порядок выполнения, чтобы точно настроить это.)
task configure_phase(uvm_phase phase);
e.ag1.seq = m_sequence::type_id::create("se1");
e.ag2.seq = m_sequence::type_id::create("se2");
endtask
v) Наконец, чтобы гарантировать, что скачок фазы произойдет после завершения последовательностей, я добавил задержку перед его выполнением:
#1 e.d1.jump(uvm_pre_reset_phase::get());
Это немного взломать. Опять же, учитывая, что вы прыгнули в мир доменов фаз, вы, вероятно, захотите использовать фазы, чтобы гарантировать это.
Но, в конце концов... прыжок по фазе - это что-то вроде хака. Я бы рекомендовал использовать его только в крайнем случае. Нет ли более простого и традиционного способа повторения последовательности для домена 1?
`include "uvm_macros.svh"
package t;
import uvm_pkg::*;
class seq_item extends uvm_sequence_item;
`uvm_object_utils(seq_item)
rand bit [31:0] data;
function new(string name = "apb_seq_item");
super.new(name);
endfunction: new
endclass: seq_item
class m_sequence extends uvm_sequence#(seq_item);
`uvm_object_utils(m_sequence)
function new(string name = "");
super.new(name);
endfunction: new
task body();
repeat(5) begin
req = seq_item::type_id::create("ap_it");
start_item(req);
req.randomize();
finish_item(req);
end
endtask: body
endclass: m_sequence
class driver extends uvm_driver#(seq_item);
`uvm_component_utils(driver)
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction: new
task main_phase(uvm_phase phase);
fork
super.main_phase(phase);
join_none
forever begin
seq_item_port.get_next_item(req);
`uvm_info(get_type_name(),$psprintf("Got item with data: %h",req.data),UVM_NONE);
seq_item_port.item_done();
end
endtask: main_phase
endclass: driver
class agent extends uvm_agent;
`uvm_component_utils(agent)
uvm_analysis_port#(seq_item) ap;
m_sequence seq;
uvm_sequencer#(seq_item) seqr;
driver drv;
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
ap = new(.name("apb_ap"), .parent(this));
seqr= uvm_sequencer#(seq_item) ::type_id::create(.name("uvm_sequenver"), .parent(this) );
drv = driver ::type_id::create(.name("driver"), .parent(this) );
endfunction: build_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
drv.seq_item_port.connect(seqr.seq_item_export);
endfunction: connect_phase
task pre_reset_phase(uvm_phase phase);
fork
super.pre_reset_phase(phase);
join_none
`uvm_info(get_type_name(),"jumped into pre_reset_phase",UVM_NONE);
endtask
task main_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info(get_type_name(),"jumped into main_phase",UVM_NONE);
seq.start(seqr);
phase.drop_objection(this);
endtask
endclass: agent
class env extends uvm_env;
`uvm_component_utils(env)
agent ag1;
agent ag2;
uvm_domain d1;
uvm_domain d2;
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction: new
function void build_phase(uvm_phase phase);
super.build_phase(phase);
ag1 = agent::type_id::create("ag1", this);
ag2 = agent::type_id::create("ag2", this);
d1 = new("d1");
d2 = new("d2");
ag1.set_domain(d1,1);
ag2.set_domain(d2,1);
endfunction: build_phase
endclass: env
class test extends uvm_test;
`uvm_component_utils(test)
env e;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
e = env::type_id::create("env",this);
endfunction
task configure_phase(uvm_phase phase);
e.ag1.seq = m_sequence::type_id::create("se1");
e.ag2.seq = m_sequence::type_id::create("se2");
endtask
task main_phase(uvm_phase phase);
fork
super.main_phase(phase);
join_none
phase.raise_objection(this);
#1 e.d1.jump(uvm_pre_reset_phase::get());
phase.drop_objection(this);
endtask
endclass: test
endpackage
module top();
import uvm_pkg::*;
import t::*;
initial begin
run_test();
end
endmodule
https://www.edaplayground.com/x/imV
Вот статья, которая привела меня к идее использования фазовых переходов: sunburst-design.com/papers/HunterSNUGSV_UVM_Resets_paper.pdf
Относительно пункта 'iii': в документации сказано, что это излишне: If called from build, hier won't recurse into all chilren (which don't exist yet)
.
@АндрейСолодовников Что касается «hier», похоже, вы знаете больше, чем я. Если вы думаете, что прыжок по фазе — это выход, то не позволяйте мне вас останавливать.
Я автор статьи (sunburst-design.com/papers/HunterSNUGSV_UVM_Resets_paper.pdf). Прыжки по фазам — это не хак. Он используется на многих тестовых стендах в моей организации и очень надежен. Ваша проблема в том, что драйверы не должны запускать свои потоки во время основной фазы. (см. статью)
Большое спасибо за ответ! Дело в том, что мне нужен способ удобной обработки сброса. Прочитав несколько статей на эту тему, пришел к выводу, что сброс нужно обрабатывать с помощью фазы сброса. Вполне удобно для первоначального сброса и сброса между тестами. Однако при тестировании сложного модуля я столкнулся с тем, что одна его часть генерирует сброс для другой и мне нужно либо добавлять избыточную логику обработки для сброса драйвера, либо использовать фазовые скачки.