【前端验证】fork-join_none线程立即执行的一次代码优化记录

简介: 【前端验证】fork-join_none线程立即执行的一次代码优化记录


前言

【system verilog】fork-join_none与循环语句共同使用的行为探究


很早之前写过关于fork-join_none的探究文章,最近被人指出了一些错误:



我仔细理解了下他的意思,觉得确实使用#0来立刻进行进行阻塞,进而达到立即执行fork-join_none内语句的方式是比较合理的(当然了,其他阻塞行为一样会让fork-join_none内的语句执行,但不能达到立刻执行的效果)。经过这个勘误和指点后,我突然觉得又通彻了一些。


恰巧最近自己也在写一些验证的代码, 遇到了类似的问题,同时还帮别人debug了非常相似的问题(以下为被夸实录):







场景复盘

所以具体来说是个什么场景呢?最终完成部分代码片段如下:

while(1)begin
    bit[255:0]rdata;
    p_sequencer.a_port.get(a_trc);
    ...
    for(int i=0; i<a_trc.size; i=i+1)begin
        rdata[(i%32)*8 +:8] = p_sequencer.ram[addr][7:0];
        if(i == arsize-1)begin
            r_transaction  r_trc = new();
            fork begin
                automatic bit[255:0]send_rdata = rdata;
                `uvm_do_on_with(r_trc, p_sequencer.r_sqr, {r_trc.data == send_rdata; r_trc.last == 1'b1;})
                end join_none
                #0;
        end
        else begin
            ...
        end
    end
end


简单而言,就是一个简化的axi_slave(不过不需要总线地址对齐),当收到ar请求时要按照size返回对应笔rdata到总线上。因此代码组织最开始也比较简单,就是看arsize然后在ram中寻址返回数据并且给数据加一个随机延迟,因此最开始完成的代码是这样的:

while(1)begin
    bit[255:0]rdata;
    p_sequencer.a_port.get(a_trc);
    ...
    for(int i=0; i<a_trc.size; i=i+1)begin
        rdata[(i%32)*8 +:8] = p_sequencer.ram[addr][7:0];
        if(i == arsize-1)begin
            r_transaction  r_trc = new();
            `uvm_do_on_with(r_trc, p_sequencer.r_sqr, {r_trc.data == rdata; r_trc.last == 1'b1;})
        end
        else begin
            ...
        end
    end
end


然后就发现了第一个问题,`uvm_do的返回时间是在driver中的seq_item_port.item_done()之后的,那么也就是说回到a_port.get(a_trc)时候是前一个ar的rdata已经发完的时间,而不是真正的从总线上获取到ar请求的时间,在这段时间里ram中的数据可能已经被改写了,因此`uvm_do必须要提并行线程来执行,所以代码就改成了这样:

while(1)begin
    bit[255:0]rdata;
    p_sequencer.a_port.get(a_trc);
    ...
    for(int i=0; i<a_trc.size; i=i+1)begin
        rdata[(i%32)*8 +:8] = p_sequencer.ram[addr][7:0];
        if(i == arsize-1)begin
            r_transaction  r_trc = new();
            fork
                `uvm_do_on_with(r_trc, p_sequencer.r_sqr, {r_trc.data == rdata; r_trc.last == 1'b1;})
            join_none
        end
        else begin
            ...
        end
    end
end


然后继续仿真,就发现了第二个问题,r_trc并没有在`uvm_do_on_with的时刻就开始在driver内执行,而是呈现了比较奇怪的行为(当然这个问题并不一定是不加#0引起的),因此结合前言中的内容,我机智的加入了#0来及时执行提起的线程:

while(1)begin
    bit[255:0]rdata;
    p_sequencer.a_port.get(a_trc);
    ...
    for(int i=0; i<a_trc.size; i=i+1)begin
        rdata[(i%32)*8 +:8] = p_sequencer.ram[addr][7:0];
        if(i == arsize-1)begin
            r_transaction  r_trc = new();
            fork
                `uvm_do_on_with(r_trc, p_sequencer.r_sqr, {r_trc.data == rdata; r_trc.last == 1'b1;})
            join_none
            #0;
        end
        else begin
            ...
        end
    end
end


而后继续跑仿真,自然而然的遇到了第三个问题,图示如下:



我的rdata只有一份,但是线程有多个,那么会导致每个线程中使用的rdata并不是预期的值 ,因此还要进一步的优化代码:

while(1)begin
    bit[255:0]rdata;
    p_sequencer.a_port.get(a_trc);
    ...
    for(int i=0; i<a_trc.size; i=i+1)begin
        rdata[(i%32)*8 +:8] = p_sequencer.ram[addr][7:0];
        if(i == arsize-1)begin
            r_transaction  r_trc = new();
            fork begin
                automatic bit[255:0]send_rdata = rdata;
                `uvm_do_on_with(r_trc, p_sequencer.r_sqr, {r_trc.data == send_rdata; r_trc.last == 1'b1;})
                end join_none
                #0;
        end
        else begin
            ...
        end
    end
end


经过三次的优化之后,仿真行为终于符合了我的预期!


最后一下,经过和别人的讨论,其实不加#0也是可以的,这个时候需要把automatic赋值放在fork-join_none外面:

while(1)begin
    bit[255:0]rdata;
    p_sequencer.a_port.get(a_trc);
    ...
    for(int i=0; i<a_trc.size; i=i+1)begin
        rdata[(i%32)*8 +:8] = p_sequencer.ram[addr][7:0];
        if(i == arsize-1)begin
            r_transaction  r_trc = new();
            automatic bit[255:0]send_rdata = rdata;
            fork
                `uvm_do_on_with(r_trc, p_sequencer.r_sqr, {r_trc.data == send_rdata; r_trc.last == 1'b1;})
            join_none
        end
        else begin
            ...
        end
    end
end


相关文章
|
3月前
|
前端开发 Java C++
每个前端都应该掌握的7个代码优化的小技巧
每个前端都应该掌握的7个代码优化的小技巧
|
6月前
|
前端开发 测试技术
【前端验证】记录将发包量作为传参以加速debug的环境优化记录
【前端验证】记录将发包量作为传参以加速debug的环境优化记录
|
6月前
|
前端开发 vr&ar
【前端验证】被动响应型uvm_model环境搭建——以握手型ram_model为例
【前端验证】被动响应型uvm_model环境搭建——以握手型ram_model为例
|
6月前
|
前端开发 Python
【前端验证】通用型顺序比对的uvm scoreboard组件编写
【前端验证】通用型顺序比对的uvm scoreboard组件编写
|
6月前
|
前端开发 测试技术 芯片
【前端验证】关于那道经典概率题,用UVM环境来仿真下是男孩的概率
【前端验证】关于那道经典概率题,用UVM环境来仿真下是男孩的概率
|
2月前
|
JavaScript 前端开发 数据安全/隐私保护
jQuery最方便的前端验证方式2种(非空验证与比较验证)
jQuery最方便的前端验证方式2种(非空验证与比较验证)
43 0
|
8月前
|
前端开发
前端项目实战玖拾肆react-admin+material ui-踩坑-List的用法之disableAuthentication设置验证
前端项目实战玖拾肆react-admin+material ui-踩坑-List的用法之disableAuthentication设置验证
45 0
|
8月前
|
前端开发
前端学习笔记202305学习笔记第二十二天-接口说明和postman验证
前端学习笔记202305学习笔记第二十二天-接口说明和postman验证
44 0
|
4月前
|
canal 算法 前端开发
前端算法专栏-数组-125. 验证回文串
前端算法专栏-数组-125. 验证回文串
166 0
|
6月前
|
前端开发
【前端验证】对uvm_info宏的进一步封装尝试
【前端验证】对uvm_info宏的进一步封装尝试