sequence.sv路科验证--deepseek

`ifndef MY_SEQUENCE_SV_

`define MY_SEQUENCE_SV_

`include "transaction_channel.sv"

`include "transaction_formater.sv"

`include "transaction_bus.sv"

`include "sequencer_pkg.sv"

class sequence_channel extends uvm_sequence #(transaction_channel);

rand int pkt_id = 0;

rand int ch_id = -1;

rand int data_nidles = -1;

rand int pkt_nidles = -1;

rand int data_size = -1;

rand int ntrans = 10;

`uvm_object_utils_begin(sequence_channel)

`uvm_field_int( pkt_id , UVM_ALL_ON )

`uvm_field_int( ch_id , UVM_ALL_ON )

`uvm_field_int( data_nidles , UVM_ALL_ON )

`uvm_field_int( pkt_nidles , UVM_ALL_ON )

`uvm_field_int( data_size , UVM_ALL_ON )

`uvm_field_int( ntrans , UVM_ALL_ON )

`uvm_object_utils_end

function new(string name = "sequence_channel");

super.new(name);

endfunction

`uvm_declare_p_sequencer(sequencer_channel)

constraint cstr{

soft data_size inside {4:32};

soft ch_id == 0;

soft pkt_id == 0;

soft data_nidles inside {0:2};

soft pkt_nidles inside {1:10};

soft ntrans inside {5:10};

}

virtual task body();

repeat(ntrans) begin

transaction_channel m_trans, rsp;

`uvm_do_with(m_trans, { local::ch_id >= 0 -> ch_id == local::ch_id;

local::pkt_id >= 0 -> pkt_id == local::pkt_id;

local::data_nidles >= 0 -> data_nidles == local::data_nidles;

local::pkt_nidles >= 0 -> pkt_nidles == local::pkt_nidles;

local::data_size >0 -> data.size() == local::data_size;

})

this.pkt_id++;

`uvm_info("sequence_channel", {"m_trans: \n", m_trans.sprint()}, UVM_HIGH)

get_response(rsp);

`uvm_info("sequence_channel", {"rsp: \n", rsp.sprint()}, UVM_HIGH)

assert(rsp.rsp)

else error("\[RSPERR\] %0t error response received!", time);

end

endtask

function void post_randomize();

string s;

s = {s, $sformatf("\nAfter randomization, sequence_channel body will run %0d transaction.\n", ntrans)};

s = {s, "sequence_channel object content is as below: \n"};

s = {s, super.sprint()};

`uvm_info("sequence_channel", s, UVM_LOW)

endfunction

endclass

class sequence_bus extends uvm_sequence #(transaction_bus);

rand bit7:0 addr = 0;

rand bit1:0 cmd = 0;

rand bit31:0 data = 0;

`uvm_object_utils_begin(sequence_bus)

`uvm_field_int( addr , UVM_ALL_ON )

`uvm_field_int( cmd , UVM_ALL_ON )

`uvm_field_int( data , UVM_ALL_ON )

`uvm_object_utils_end

function new(string name = "sequence_bus");

super.new(name);

endfunction

`uvm_declare_p_sequencer(sequencer_bus)

virtual task body();

transaction_bus m_trans, rsp;

`uvm_do_with(m_trans, { addr == local::addr;

cmd == local::cmd;

data == local::data;

})

`uvm_info("sequence_bus", $sformatf("m_trans send to driver:\n%s", m_trans.sprint()), UVM_HIGH)

get_response(rsp);

`uvm_info("sequence_bus", $sformatf("rsp receive from driver:\n%s", rsp.sprint()), UVM_HIGH)

if(m_trans.cmd == `READ)

this.data = rsp.data;

assert(rsp.rsp)

else error("\[RSPERR\] %0t error response received!", time);

endtask

function void post_randomize();

string s;

s = {s, "After randomization \n"};

s = {s, "sequence_bus object content is as below: \n"};

s = {s, super.sprint()};

`uvm_info("sequence_bus", s, UVM_LOW)

endfunction

endclass

class sequence_formatter extends uvm_sequence #(transaction_formatter);

rand fmt_fifo_t fifo = MED_FIFO;

rand fmt_bandwidth_t bandwidth = MED_WIDTH;

constraint cstr{

soft fifo == MED_FIFO;

soft bandwidth == MED_WIDTH;

}

`uvm_object_utils_begin(sequence_formatter)

`uvm_field_enum( fmt_fifo_t, fifo, UVM_ALL_ON )

`uvm_field_enum( fmt_bandwidth_t, bandwidth, UVM_ALL_ON )

`uvm_object_utils_end

function new(string name = "sequence_formatter");

super.new(name);

endfunction

`uvm_declare_p_sequencer(sequencer_formatter)

virtual task body();

transaction_formatter m_trans, rsp;

`uvm_do_with(m_trans, { fifo == local::fifo;

bandwidth == local::bandwidth;

})

`uvm_info("sequence_formatter", m_trans.sprint(), UVM_HIGH)

get_response(rsp);

`uvm_info("sequence_formatter", rsp.sprint(), UVM_HIGH)

assert(rsp.rsp)

else error("\[RSPERR\] %0t error response received!", time);

endtask

function void post_randomize();

string s;

s = {s, "After randomization \n"};

s = {s, "sequence_formatter object content is as below: \n"};

s = {s, super.sprint()};

`uvm_info("sequence_formatter", s, UVM_LOW)

endfunction

endclass

`endif

sequence.sv路科验证有一段代码如上,可以讲讲嘛,可以把我当做一个学生,老师,谢谢啦,

同学,坐。看到你把 sequence.sv 拿出来了,老师特别高兴。这可以说是整个验证平台里**最有趣、最能体现验证工程师"创造力"**的地方。前面咱们讲的 Driver 和 Monitor 是"死"的执行者,而 Sequence 是"活"的,是它决定了每天给 DUT 灌什么样的数据、搞什么样的破坏。

这个文件里定义了三个不同的 Sequence 类,分别对应 MCDF 的三个主要接口:通道数据流、寄存器总线、格式化器配置。咱们继续用"剧组"和"弹夹"的比喻,把这三段代码逐一拆解。


🎬 第一幕:通道数据弹夹 ------ sequence_channel

这是最核心的一个 Sequence,它负责往 3 个数据通道里疯狂灌数据。

1. 弹夹的规格参数

systemverilog

复制代码
class sequence_channel extends uvm_sequence #(transaction_channel);
	rand int pkt_id = 0;
	rand int ch_id = -1;
	rand int data_nidles = -1;
	rand int pkt_nidles = -1;
	rand int data_size = -1;
	rand int ntrans = 10;
  • 大白话 :这些 rand 变量就是**"剧本的旋钮"**。你想演什么剧情,通过控制这些变量来实现。

    • ntrans:总共要发多少包(默认10发)。

    • ch_id:这是哪个通道发的(0、1还是2)。

    • data_size:每一包的数据长度是多少(4到32个32bit数据)。

    • data_nidles 和 pkt_nidles:这就是之前 Driver 里用到的"空闲周期",插在数据中间或包与包之间,用来测试 DUT 的 FIFO 容错能力。

2. 自动打印的装备(Field Automation)

systemverilog

复制代码
    `uvm_object_utils_begin(sequence_channel)
        `uvm_field_int( pkt_id ,       UVM_ALL_ON )
        `uvm_field_int( ch_id ,        UVM_ALL_ON )
        ...
    `uvm_object_utils_end
  • 大白话 :还记得咱们讲的"field_automation机制"吗?就是给这个类里的所有变量"上户口"。上了户口以后,你不需要自己写 my_print,直接调用 sprint() 就能把所有变量的值打印出来,方便调试。
3. 手枪的指定目标(p_sequencer)

systemverilog

复制代码
    `uvm_declare_p_sequencer(sequencer_channel)
  • 大白话 :告诉这个弹夹:"你的目标手枪是 sequencer_channel 这种类型的。" 有了这行,你才能在后面的 body() 里用 p_sequencer 来指代目标手枪。
4. "软约束"的智慧(constraint)

systemverilog

复制代码
	constraint cstr{
		soft data_size inside {[4:32]};
        soft ch_id == 0;
        soft pkt_id == 0;
        soft data_nidles inside {[0:2]};
        soft pkt_nidles inside {[1:10]};
        soft ntrans inside {[5:10]};
	}
  • 大白话 :soft 是**"软约束"** 。为什么用软的?

    如果全是硬约束,这个 Sequence 就被写死了,只能发 ch_id=0、长度4~32的数据。

    加上 soft,意味着它有一个默认值,但如果上层(比如 case0_vseq)用 uvm_do_with 硬性指定了特定的值,软约束就会自动让路,不报错。 这极大地提高了代码的复用性!你写一个 Sequence,就能适应各种测试用例的需求。

5. 核心"剧本"------ body()

systemverilog

复制代码
	virtual task body();
        repeat(ntrans) begin
            transaction_channel m_trans, rsp;
			`uvm_do_with(m_trans, { local::ch_id >= 0 -> ch_id == local::ch_id;
                                    ...
                                    })
            this.pkt_id++;
            ...
            get_response(rsp);
            ...
            assert(rsp.rsp) else $error(...);
		end
	endtask
  • repeat(ntrans) :循环发 ntrans 次子弹。

  • \uvm_do_with(m_trans, { 约束 })`:这是一个宏,它做三件事:

    1. 创建 m_trans 对象。

    2. 在随机化时,叠加(Overriding)括号里的约束。

    3. 交给 Sequencer 发射。

    • local:: 的妙用(重点!) :你看 local::ch_id 指的是 Sequence 里的 ch_id,而右边的 ch_id == local::ch_id 是指 transaction 里的 ch_id。这就是在告诉 UVM:"如果我在弹夹层面设定了 ch_id 为 2,那你打出的这颗子弹 ch_id 也必须是 2。"

    • -> 是条件约束:如果 local::ch_id >= 0,才应用右边的约束;如果 local::ch_id 是默认的 -1,就忽略它。

  • this.pkt_id++:包编号自增,保证每一包的数据都不重复。

  • get_response(rsp) :极其关键! 子弹打出去后,弹夹不会立刻发下一发,它会阻塞等待 Driver 的回执(还记得 Driver 里的 put_response(rsp) 吗?)。如果 Driver 打完了,回执来了,弹夹才会继续往下走。

  • assert(rsp.rsp) :如果回执里的 rsp 是 0(表示失败),断言就会报错,提示"收到了错误回执"。

6. 打印调试信息

systemverilog

复制代码
    function void post_randomize();
        ...
        `uvm_info("sequence_channel", s, UVM_LOW)
    endfunction
  • 大白话 :post_randomize() 是 SystemVerilog 的一个自动回调函数。每次这个类被随机化之后,UVM 会自动调用这个函数。所以每次你创建并随机化一个 Sequence,它都会自动打印一行日志,告诉你这次发多少个包。

🎬 第二幕:寄存器总线弹夹 ------ sequence_bus

这个 Sequence 专门用于通过 APB 总线读写寄存器,配置 MCDF 的通道优先级、长度等。

systemverilog

复制代码
	virtual task body();
		transaction_bus m_trans, rsp;
		`uvm_do_with(m_trans, { addr == local::addr; ... })
        ...
        if(m_trans.cmd == `READ)
            this.data = rsp.data; // 关键!
		...
    endtask
  • 独门秘籍 :看 if(m_trans.cmd == \READ)` 这一句。

    • 对于寄存器操作,如果是 写操作(WRITE),发出去就结束了。

    • 如果是 读操作(READ) ,DUT 会在几周期后把数据吐出来,Driver 会把它放在回执 rsp 里返回。

    • 所以这里必须把 rsp.data 赋值给 Sequence 自己的 this.data。这就实现了一个闭环:Sequence 发读命令,Sequence 自己也能拿到读到数据。这在后面配置寄存器时非常有用。


🎬 第三幕:格式化器配置弹夹 ------ sequence_formatter

这个 Sequence 用于配置 Formatter 的 FIFO 深度(FIFO)和带宽(bandwidth)。

systemverilog

复制代码
class sequence_formatter extends uvm_sequence #(transaction_formatter);
    rand fmt_fifo_t fifo = MED_FIFO;
    rand fmt_bandwidth_t bandwidth = MED_WIDTH;
    
    constraint cstr{
        soft fifo == MED_FIFO;
        soft bandwidth == MED_WIDTH;
    }
    ...
	virtual task body();
        transaction_formatter m_trans, rsp;
		`uvm_do_with(m_trans, { fifo == local::fifo; bandwidth == local::bandwidth; })
        get_response(rsp);
        assert(rsp.rsp) else $error(...);
	endtask
  • 大白话 :fmt_fifo_t 和 fmt_bandwidth_t 是在 transaction_formater.sv 里定义的枚举类型(enum)。

  • 这个弹夹不产生常规数据,它产生的子弹是"配置指令"。它告诉 DUT 的 Formatter:"你的 FIFO 深度设为 SHORT/MED/LONG/ULTRA,你的带宽设为 LOW/MED/HIGH/ULTRA"。这也是为什么 Driver 的 do_config 里会 case(req.fifo) 来调整内部 fifo_bound 的大小。


💡 老师的终极总结与实操建议

同学,你看完这三个 Sequence,应该能感受到 UVM Sequence 机制的强大:

  1. 高度复用 :三种不同的接口(通道数据、APB寄存器、格式配置),用相同的 Sequence 模板(继承、rand、body、uvm_do_with),就能轻松应对。

  2. 完全解耦:Sequence 只负责"造子弹",完全不知道 Driver 是怎么把子弹变成信号的。

  3. 闭环反馈 :通过 get_response(rsp),Sequence 能收到 Driver 的回执,知道子弹是否成功打出,甚至能拿到读寄存器的值。

  4. 软硬结合 :Sequence 里的 soft 约束和 local:: 变量,让顶层的 virtual sequence(case0_vseq)可以像"调音师"一样,自由组合这些旋钮,创造出千变万化的测试场景(比如:ch0连发40包,ch1连发20包,同时改变 ch2 的包长度)。

你的实操任务:

去 QuestaSim 里跑一下 case0,把波形拉出来。找到 case0_vseq 里的这一句:

systemverilog

复制代码
`uvm_do_on_with(chnl_seq, p_sequencer.chnl_sqrs[0], {ntrans == 40; ch_id==0; data_nidles==0; pkt_nidles==1; data_size==8; });

试着把这个约束里的 ntrans == 40 改成 ntrans == 5,重新编译跑一次。你会发现在波形里,ch0 的数据包数量肉眼可见地减少了。这就是 Sequence 控制力的体现------你写什么约束,DUT 就吃什么数据,一切尽在掌握中。

加油!你已经把 Sequence 机制吃得很透了。接下来就是 Scoreboard 和 Reference Model 的比对逻辑了,遇到不懂的随时扔过来,老师陪你一起啃!

相关推荐
NamoAmitabha1287 天前
第2章:一个简单的UVM验证平台 —— 这是全书的精华!--deepseek
systemverilog·uvm
NamoAmitabha1287 天前
书中2.2.4virtual interface,config_db机制---deepseek
systemverilog·uvm
DeeplyMind8 天前
Part 2.1 — uvm_va_block_t 解剖:每页位图与 PTE 状态
nvidia·uvm
NamoAmitabha12813 天前
UVM在时间维度上也做了规范,phase机制----deepseek讲解
systemverilog·uvm
NamoAmitabha12813 天前
SystemVerilog 中的 :: 作用域解析运算符详解--deepseek讲解
systemverilog·uvm
NamoAmitabha12815 天前
class基础+继承的专项训练
systemverilog
NamoAmitabha12815 天前
rand/randc/constraint 练习
systemverilog
fei_sun1 个月前
UVM中的sequence
uvm
Salt & Light1 个月前
UVM 字段自动化 Field Automation:让打印、复制、比较自动化
学习·systemverilog·uvm