【前端验证】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


相关文章
|
2月前
|
前端开发 Java C++
每个前端都应该掌握的7个代码优化的小技巧
每个前端都应该掌握的7个代码优化的小技巧
|
9月前
|
前端开发 测试技术
【前端验证】记录将发包量作为传参以加速debug的环境优化记录
【前端验证】记录将发包量作为传参以加速debug的环境优化记录
|
9月前
|
前端开发 vr&ar
【前端验证】被动响应型uvm_model环境搭建——以握手型ram_model为例
【前端验证】被动响应型uvm_model环境搭建——以握手型ram_model为例
|
24天前
|
存储 前端开发 搜索推荐
Web前端网站(一) - 登录页面及账号密码验证
页面背景动态是烟花和文字特效与缓缓下落的雪花相结合,在登录表单的旁边还有五个白色光圈以不规则的方式环绕,当鼠标靠近时,会发出彩色的光芒~~~
36 1
Web前端网站(一) - 登录页面及账号密码验证
|
24天前
|
前端开发 JavaScript
阿里云验证码2.0 验证时报错 前端页面获取的验证参数有问题,动态JS加载失败,请问怎么解决啊?急,急,急。
用户反馈校验时遇到错误,日志显示验证码参数获取异常。采用无痕验证,失败后,返回`{captchaResult:false,bizResult:false}`,未触发滑块二次验证。
|
2月前
|
Dart 前端开发 安全
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
【4月更文挑战第30天】本文探讨了Flutter中线程管理和并发编程的关键性,强调其对应用性能和用户体验的影响。Dart语言提供了`async`、`await`、`Stream`和`Future`等原生异步支持。Flutter采用事件驱动的单线程模型,通过`Isolate`实现线程隔离。实践中,可利用`async/await`、`StreamBuilder`和`Isolate`处理异步任务,同时注意线程安全和性能调优。参考文献包括Dart异步编程、Flutter线程模型和DevTools文档。
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
|
2月前
|
前端开发 网络协议 JavaScript
如何在前端实现WebSocket发送和接收TCP消息(多线程模式)
请确保在你的服务器端实现WebSocket的处理,以便它可以接受和响应前端发送的消息。同时,考虑处理错误情况和关闭连接的情况以提高可靠性。
108 0
|
2月前
|
API 数据库 Python
多线程收集/验证IP从而搭建有效IP代理池
多线程收集/验证IP从而搭建有效IP代理池
|
9月前
|
前端开发 Python
【前端验证】通用型顺序比对的uvm scoreboard组件编写
【前端验证】通用型顺序比对的uvm scoreboard组件编写
128 0
|
2月前
|
JavaScript 前端开发 数据安全/隐私保护
jQuery最方便的前端验证方式2种(非空验证与比较验证)
jQuery最方便的前端验证方式2种(非空验证与比较验证)
67 0