This session is a real example of how design and verification happens in the real industry. We'll go through the design specification, write a test plan that details how the design will be tested, develop a UVM testbench structure and verify the design.
Design
This is a simple pattern detector written in Verilog to identify a pattern in a stream of input values. On every clock, there is a new input to the design and when it matches the pattern '1011', the output out will be set to 1. For this purpose, the design is implemented as a state machine which moves through different stages as it progresses through pattern identification sequence.
Test Plan
The verification testbench will be developed in UVM and has the following block diagram:
- The sequence generates a random stream of input values that will be passed to the driver as a
uvm_sequence_item
- The driver receives the item and drives it to the DUT through a virtual interface
- The monitor captures values on the DUT's input and output pin, creates a packet and sends to the scoreboard
- The scoreboard is primarily responsible for checking the functional correctness of the design based on the input and output values it receives from the monitor.
The input stream of values has to be random for maximum efficiency. It should be able to catch the following scenarios:
- 011011011010
- 101011100
- 111011011
Testbench
Sequence Item
// This is the base transaction object that will be used
// in the environment to initiate new transactions and
// capture transactions at DUT interface
class Item extends uvm_sequence_item;
`uvm_object_utils(Item)
rand bit in;
bit out;
virtual function string convert2str();
return $sformatf("in=%0d, out=%0d", in, out);
endfunction
function new(string name = "Item");
super.new(name);
endfunction
constraint c1 { in dist {0:/20, 1:/80}; }
endclass
Sequence
class gen_item_seq extends uvm_sequence;
`uvm_object_utils(gen_item_seq)
function new(string name="gen_item_seq");
super.new(name);
endfunction
rand int num; // Config total number of items to be sent
constraint c1 { soft num inside {[10:50]}; }
virtual task body();
for (int i = 0; i < num; i ++) begin
Item m_item = Item::type_id::create("m_item");
start_item(m_item);
m_item.randomize();
`uvm_info("SEQ", $sformatf("Generate new item: %s", m_item.convert2str()), UVM_HIGH)
finish_item(m_item);
end
`uvm_info("SEQ", $sformatf("Done generation of %0d items", num), UVM_LOW)
endtask
endclass
Driver
// The driver is responsible for driving transactions to the DUT
// All it does is to get a transaction from the mailbox if it is
// available and drive it out into the DUT interface.
class driver extends uvm_driver #(Item);
`uvm_component_utils(driver)
function new(string name = "driver", uvm_component parent=null);
super.new(name, parent);
endfunction
virtual des_if vif;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual des_if)::get(this, "", "des_vif", vif))
`uvm_fatal("DRV", "Could not get vif")
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
forever begin
Item m_item;
`uvm_info("DRV", $sformatf("Wait for item from sequencer"), UVM_HIGH)
seq_item_port.get_next_item(m_item);
drive_item(m_item);
seq_item_port.item_done();
end
endtask
virtual task drive_item(Item m_item);
@(vif.cb);
vif.cb.in <= m_item.in;
endtask
endclass
Monitor
// The monitor has a virtual interface handle with which
// it can monitor the events happening on the interface.
// It sees new transactions and then captures information
// into a packet and sends it to the scoreboard
// using another mailbox.
class monitor extends uvm_monitor;
`uvm_component_utils(monitor)
function new(string name="monitor", uvm_component parent=null);
super.new(name, parent);
endfunction
uvm_analysis_port #(Item) mon_analysis_port;
virtual des_if vif;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual des_if)::get(this, "", "des_vif", vif))
`uvm_fatal("MON", "Could not get vif")
mon_analysis_port = new ("mon_analysis_port", this);
endfunction
virtual task run_phase(uvm_phase phase);
super.run_phase(phase);
// This task monitors the interface for a complete
// transaction and writes into analysis port when complete
forever begin
@ (vif.cb);
if (vif.rstn) begin
Item item = Item::type_id::create("item");
item.in = vif.in;
item.out = vif.cb.out;
mon_analysis_port.write(item);
`uvm_info("MON", $sformatf("Saw item %s", item.convert2str()), UVM_HIGH)
end
end
endtask
endclass
Scoreboard
// The scoreboard is responsible to check design functionality and
// should track input and try to match the pattern and ensure that
// the design has found the pattern as well. The scoreboard should
// flag an error if the design didnt find the pattern and ensure
// that "out" remains zero, and if the design found the pattern,
// "out" is set to the correct value.
class scoreboard extends uvm_scoreboard;
`uvm_component_utils(scoreboard)
function new(string name="scoreboard", uvm_component parent=null);
super.new(name, parent);
endfunction
bit[`LENGTH-1:0] ref_pattern;
bit[`LENGTH-1:0] act_pattern;
bit exp_out;
uvm_analysis_imp #(Item, scoreboard) m_analysis_imp;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
m_analysis_imp = new("m_analysis_imp", this);
if (!uvm_config_db#(bit[`LENGTH-1:0])::get(this, "*", "ref_pattern", ref_pattern))
`uvm_fatal("SCBD", "Did not get ref_pattern !")
endfunction
virtual function write(Item item);
act_pattern = act_pattern << 1 | item.in;
`uvm_info("SCBD", $sformatf("in=%0d out=%0d ref=0b%0b act=0b%0b",
item.in, item.out, ref_pattern, act_pattern), UVM_LOW)
// Always check that expected out value is the actual observed value
// Since it takes 1 clock for out to be updated after pattern match,
// do the check first and then update exp_out value
if (item.out != exp_out) begin
`uvm_error("SCBD", $sformatf("ERROR ! out=%0d exp=%0d",
item.out, exp_out))
end else begin
`uvm_info("SCBD", $sformatf("PASS ! out=%0d exp=%0d",
item.out, exp_out), UVM_HIGH)
end
// If current index has reached the full pattern, then set exp_out to be 1
// which will be checked in the next clock. If pattern is not complete, keep
// exp_out to zero
if (!(ref_pattern ^ act_pattern)) begin
`uvm_info("SCBD", $sformatf("Pattern found to match, next out should be 1"), UVM_LOW)
exp_out = 1;
end else begin
exp_out = 0;
end
endfunction
endclass
Agent and Environment
// Create an intermediate container called "agent" to hold
// driver, monitor and sequencer
class agent extends uvm_agent;
`uvm_component_utils(agent)
function new(string name="agent", uvm_component parent=null);
super.new(name, parent);
endfunction
driver d0; // Driver handle
monitor m0; // Monitor handle
uvm_sequencer #(Item) s0; // Sequencer Handle
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
s0 = uvm_sequencer#(Item)::type_id::create("s0", this);
d0 = driver::type_id::create("d0", this);
m0 = monitor::type_id::create("m0", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
d0.seq_item_port.connect(s0.seq_item_export);
endfunction
endclass
// The environment is a container object simply to hold
// all verification components together. This environment can
// then be reused later and all components in it would be
// automatically connected and available for use
class env extends uvm_env;
`uvm_component_utils(env)
function new(string name="env", uvm_component parent=null);
super.new(name, parent);
endfunction
agent a0; // Agent handle
scoreboard sb0; // Scoreboard handle
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
a0 = agent::type_id::create("a0", this);
sb0 = scoreboard::type_id::create("sb0", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
a0.m0.mon_analysis_port.connect(sb0.m_analysis_imp);
endfunction
endclass
Test
Since we want to be able to reuse the same verification environment to detect designs with patterns other than "1011", it has to be developed with enough flexibility to easily change the pattern. For this reason, we have chose to represent the pattern as an N-bit value where N is defined by the macro LENGTH as 4. Also, the base test sets up the environment, config_db and other parameters for the test so that derived tests simply have to provide a new pattern if required along with the total number of data items to be sent to the DUT as input stream. Number of data items to DUT is a randomized value that is part of the sequence and randomized to a different value in the derived test.
// Test class instantiates the environment and starts it.
class base_test extends uvm_test;
`uvm_component_utils(base_test)
function new(string name = "base_test", uvm_component parent=null);
super.new(name, parent);
endfunction
env e0;
bit[`LENGTH-1:0] pattern = 4'b1011;
gen_item_seq seq;
virtual des_if vif;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Create the environment
e0 = env::type_id::create("e0", this);
// Get virtual IF handle from top level and pass it to everything
// in env level
if (!uvm_config_db#(virtual des_if)::get(this, "", "des_vif", vif))
`uvm_fatal("TEST", "Did not get vif")
uvm_config_db#(virtual des_if)::set(this, "e0.a0.*", "des_vif", vif);
// Setup pattern queue and place into config db
uvm_config_db#(bit[`LENGTH-1:0])::set(this, "*", "ref_pattern", pattern);
// Create sequence and randomize it
seq = gen_item_seq::type_id::create("seq");
seq.randomize();
endfunction
virtual task run_phase(uvm_phase phase);
phase.raise_objection(this);
apply_reset();
seq.start(e0.a0.s0);
#200;
phase.drop_objection(this);
endtask
virtual task apply_reset();
vif.rstn <= 0;
vif.in <= 0;
repeat(5) @ (posedge vif.clk);
vif.rstn <= 1;
repeat(10) @ (posedge vif.clk);
endtask
endclass
class test_1011 extends base_test;
`uvm_component_utils(test_1011)
function new(string name="test_1011", uvm_component parent=null);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
pattern = 4'b1011;
super.build_phase(phase);
seq.randomize() with { num inside {[300:500]}; };
endfunction
endclass
Interface
// The interface allows verification components to access DUT signals
// using a virtual interface handle
interface des_if (input bit clk);
logic rstn;
logic in;
logic out;
clocking cb @(posedge clk);
default input #1step output #3ns;
input out;
output in;
endclocking
endinterface
Testbench Top
module tb;
reg clk;
always #10 clk =~ clk;
des_if _if (clk);
det_1011 u0 ( .clk(clk),
.rstn(_if.rstn),
.in(_if.in),
.out(_if.out));
initial begin
clk <= 0;
uvm_config_db#(virtual des_if)::set(null, "uvm_test_top", "des_vif", _if);
run_test("test_1011");
end
endmodule
Design v1.0
module det_1011 ( input clk,
input rstn,
input in,
output out );
parameter IDLE = 0,
S1 = 1,
S10 = 2,
S101 = 3,
S1011 = 4;
reg [2:0] cur_state, next_state;
assign out = cur_state == S1011 ? 1 : 0;
always @ (posedge clk) begin
if (!rstn)
cur_state <= IDLE;
else
cur_state <= next_state;
end
always @ (cur_state or in) begin
case (cur_state)
IDLE : begin
if (in) next_state = S1;
else next_state = IDLE;
end
S1: begin
if (in) next_state = IDLE;
else next_state = S10;
end
S10 : begin
if (in) next_state = S101;
else next_state = IDLE;
end
S101 : begin
if (in) next_state = S1011;
else next_state = IDLE;
end
S1011: begin
next_state = IDLE;
end
endcase
end
endmodule
When we simulate this design in our verification environment, we can see that there are many errors.
...
UVM_INFO testbench.sv(149) @ 350: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b111
UVM_INFO testbench.sv(149) @ 370: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1111
UVM_INFO testbench.sv(149) @ 390: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1110
UVM_INFO testbench.sv(149) @ 410: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101
UVM_INFO testbench.sv(149) @ 430: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1011
UVM_INFO testbench.sv(166) @ 430: uvm_test_top.e0.sb0 [SCBD] Pattern found to match, next out should be 1
UVM_INFO testbench.sv(149) @ 450: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b111
UVM_ERROR testbench.sv(156) @ 450: uvm_test_top.e0.sb0 [SCBD] ERROR ! out=0 exp=1
UVM_INFO testbench.sv(149) @ 470: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1110
...
** Report counts by severity
UVM_INFO : 527
UVM_WARNING : 0
UVM_ERROR : 19
UVM_FATAL : 0
** Report counts by id
[RNTST] 1
[SCBD] 542
[SEQ] 1
[TEST_DONE] 1
[UVM/RELNOTES] 1
The verification engineer debugs the error to confirm that these are design bugs and are reported back to the designer. The design debugs his design to find the root cause of errors, assumes he has fixed all errors and releases a second version of the design.
Click here to see the log for design v1.0
Design v1.1
module det_1011 ( input clk,
input rstn,
input in,
output out );
parameter IDLE = 0,
S1 = 1,
S10 = 2,
S101 = 3,
S1011 = 4;
reg [2:0] cur_state, next_state;
assign out = cur_state == S1011 ? 1 : 0;
always @ (posedge clk) begin
if (!rstn)
cur_state <= IDLE;
else
cur_state <= next_state;
end
always @ (cur_state or in) begin
case (cur_state)
IDLE : begin
if (in) next_state = S1;
else next_state = IDLE;
end
S1: begin
// Designer assumed that if next input is 1,
// state should start from IDLE. But he forgot
// that it should stay in same state since it
// already matched part of the pattern which
// is the starting "1"
if (in) next_state = S1; // Fix for bug found in v1.0
else next_state = S10;
end
S10 : begin
if (in) next_state = S101;
else next_state = IDLE;
end
S101 : begin
if (in) next_state = S1011;
else next_state = IDLE;
end
S1011: begin
next_state = IDLE;
end
endcase
end
endmodule
The verification engineer reruns the same test using the same verification environment on the second design, and finds out that the number of errors have decreased but not zero yet.
...
UVM_INFO testbench.sv(149) @ 950: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1111
UVM_INFO testbench.sv(149) @ 970: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1110
UVM_INFO testbench.sv(149) @ 990: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101
UVM_INFO testbench.sv(149) @ 1010: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1010
UVM_INFO testbench.sv(149) @ 1030: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b101
UVM_INFO testbench.sv(149) @ 1050: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1011
UVM_INFO testbench.sv(166) @ 1050: uvm_test_top.e0.sb0 [SCBD] Pattern found to match, next out should be 1
UVM_INFO testbench.sv(149) @ 1070: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b111
UVM_ERROR testbench.sv(156) @ 1070: uvm_test_top.e0.sb0 [SCBD] ERROR ! out=0 exp=1
UVM_INFO testbench.sv(149) @ 1090: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1110
...
** Report counts by severity
UVM_INFO : 527
UVM_WARNING : 0
UVM_ERROR : 16
UVM_FATAL : 0
** Report counts by id
[RNTST] 1
[SCBD] 539
[SEQ] 1
[TEST_DONE] 1
[UVM/RELNOTES] 1
Another ticket is raised to the designer to debug and fix his design which still seem to have many bugs. The designer realizes some of the errors and releases another version.
Click here to see the log for design v1.1
Design v1.2
module det_1011 ( input clk,
input rstn,
input in,
output out );
parameter IDLE = 0,
S1 = 1,
S10 = 2,
S101 = 3,
S1011 = 4;
reg [2:0] cur_state, next_state;
assign out = cur_state == S1011 ? 1 : 0;
always @ (posedge clk) begin
if (!rstn)
cur_state <= IDLE;
else
cur_state <= next_state;
end
always @ (cur_state or in) begin
case (cur_state)
IDLE : begin
if (in) next_state = S1;
else next_state = IDLE;
end
S1: begin
if (in) next_state = S1; // Fix for bug found in v1.0
else next_state = S10;
end
S10 : begin
if (in) next_state = S101;
else next_state = IDLE;
end
S101 : begin
if (in) next_state = S1011;
// Designer assumed that if next input is 0,
// then pattern fails to match and should
// restart. But he forgot that S101 followed
// by 0 actually matches part of the pattern
// which is "10" and only "11" is remaining
// So it should actually go back to S10
else next_state = S10; // Fix for bug found in v1.1
end
S1011: begin
next_state = IDLE;
end
endcase
end
endmodule
The verification engineer reruns the same test on the same environment and sees that although errors have reduced, it is not zero yet.
...
UVM_INFO testbench.sv(149) @ 4130: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101
UVM_INFO testbench.sv(149) @ 4150: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1011
UVM_INFO testbench.sv(166) @ 4150: uvm_test_top.e0.sb0 [SCBD] Pattern found to match, next out should be 1
UVM_INFO testbench.sv(149) @ 4170: uvm_test_top.e0.sb0 [SCBD] in=1 out=1 ref=0b1011 act=0b111
UVM_INFO testbench.sv(149) @ 4190: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1110
UVM_INFO testbench.sv(149) @ 4210: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101
UVM_INFO testbench.sv(149) @ 4230: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1011
UVM_INFO testbench.sv(166) @ 4230: uvm_test_top.e0.sb0 [SCBD] Pattern found to match, next out should be 1
UVM_INFO testbench.sv(149) @ 4250: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b110
UVM_ERROR testbench.sv(156) @ 4250: uvm_test_top.e0.sb0 [SCBD] ERROR ! out=0 exp=1
UVM_INFO testbench.sv(149) @ 4270: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101
...
** Report counts by severity
UVM_INFO : 527
UVM_WARNING : 0
UVM_ERROR : 6
UVM_FATAL : 0
** Report counts by id
[RNTST] 1
[SCBD] 529
[SEQ] 1
[TEST_DONE] 1
[UVM/RELNOTES] 1
Lets assume another ticket is raised to the designer who debugs again, finds out that he missed to take some scenarios under consideration and fixes the design to release v1.3
Click here to see the log for design v1.2
Design v1.3
module det_1011 ( input clk,
input rstn,
input in,
output out );
parameter IDLE = 0,
S1 = 1,
S10 = 2,
S101 = 3,
S1011 = 4;
reg [2:0] cur_state, next_state;
assign out = cur_state == S1011 ? 1 : 0;
always @ (posedge clk) begin
if (!rstn)
cur_state <= IDLE;
else
cur_state <= next_state;
end
always @ (cur_state or in) begin
case (cur_state)
IDLE : begin
if (in) next_state = S1;
else next_state = IDLE;
end
S1: begin
if (in) next_state = S1; // Fix for bug found in v1.0
else next_state = S10;
end
S10 : begin
if (in) next_state = S101;
else next_state = IDLE;
end
S101 : begin
if (in) next_state = S1011;
else next_state = S10; // Fix for bug found in v1.1
end
S1011: begin
// Designer assumed next state should always
// be IDLE since the pattern has matched. But
// he forgot that if next input is 1, it is
// already the start of another sequence and
// instead should go to S1.
if (in) next_state = S1;
else next_state = IDLE; // Fix for bug found in v1.2
end
endcase
end
endmodule
Rerun same test on the design and we see that number of errors have come down but not zero yet. Again the designer looks into the errors, and fixes those errors and provides v1.4
...
UVM_INFO testbench.sv(149) @ 4190: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b1110
UVM_INFO testbench.sv(149) @ 4210: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101
UVM_INFO testbench.sv(149) @ 4230: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1011
UVM_INFO testbench.sv(166) @ 4230: uvm_test_top.e0.sb0 [SCBD] Pattern found to match, next out should be 1
UVM_INFO testbench.sv(149) @ 4250: uvm_test_top.e0.sb0 [SCBD] in=0 out=1 ref=0b1011 act=0b110
UVM_INFO testbench.sv(149) @ 4270: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101
UVM_INFO testbench.sv(149) @ 4290: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1011
UVM_INFO testbench.sv(166) @ 4290: uvm_test_top.e0.sb0 [SCBD] Pattern found to match, next out should be 1
UVM_INFO testbench.sv(149) @ 4310: uvm_test_top.e0.sb0 [SCBD] in=0 out=0 ref=0b1011 act=0b110
UVM_ERROR testbench.sv(156) @ 4310: uvm_test_top.e0.sb0 [SCBD] ERROR ! out=0 exp=1
UVM_INFO testbench.sv(149) @ 4330: uvm_test_top.e0.sb0 [SCBD] in=1 out=0 ref=0b1011 act=0b1101
...
** Report counts by severity
UVM_INFO : 527
UVM_WARNING : 0
UVM_ERROR : 3
UVM_FATAL : 0
** Report counts by id
[RNTST] 1
[SCBD] 526
[SEQ] 1
[TEST_DONE] 1
[UVM/RELNOTES] 1
Click here to see the log for design v1.3
Design v1.4
module det_1011 ( input clk,
input rstn,
input in,
output out );
parameter IDLE = 0,
S1 = 1,
S10 = 2,
S101 = 3,
S1011 = 4;
reg [2:0] cur_state, next_state;
assign out = cur_state == S1011 ? 1 : 0;
always @ (posedge clk) begin
if (!rstn)
cur_state <= IDLE;
else
cur_state <= next_state;
end
always @ (cur_state or in) begin
case (cur_state)
IDLE : begin
if (in) next_state = S1;
else next_state = IDLE;
end
S1: begin
if (in) next_state = S1; // Fix for bug found in v1.0
else next_state = S10;
end
S10 : begin
if (in) next_state = S101;
else next_state = IDLE;
end
S101 : begin
if (in) next_state = S1011;
else next_state = S10; // Fix for bug found in v1.1
end
S1011: begin
if (in) next_state = S1; // Fix for bug found in v1.2
// Designer forgot again that if next input is 0
// then pattern still matches "10" and should
// go to S10 instead of IDLE.
else next_state = S10; // Fix for bug found in v1.3
end
endcase
end
endmodule
Finally all errors have been solved and the design is now bug free for the given seed. Now the test has to pass hundreds of seeds to ensure the design is bug free. Ideally, the designer should run the test and resolve all bugs before making a design release, as this would reduce turn around time significantly.
Click here to see the log for design v1.4
** Report counts by severity
UVM_INFO : 527
UVM_WARNING : 0
UVM_ERROR : 0
UVM_FATAL : 0
** Report counts by id
[RNTST] 1
[SCBD] 523
[SEQ] 1
[TEST_DONE] 1
[UVM/RELNOTES] 1
