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


相关文章
|
6月前
|
前端开发 Java C++
每个前端都应该掌握的7个代码优化的小技巧
每个前端都应该掌握的7个代码优化的小技巧
112 0
|
前端开发 测试技术
【前端验证】记录将发包量作为传参以加速debug的环境优化记录
【前端验证】记录将发包量作为传参以加速debug的环境优化记录
|
1月前
|
前端开发 数据安全/隐私保护
angular前端基本页面验证
angular前端基本页面验证
29 1
|
1月前
|
存储 前端开发 Java
验证码案例 —— Kaptcha 插件介绍 后端生成验证码,前端展示并进行session验证(带完整前后端源码)
本文介绍了使用Kaptcha插件在SpringBoot项目中实现验证码的生成和验证,包括后端生成验证码、前端展示以及通过session进行验证码校验的完整前后端代码和配置过程。
107 0
验证码案例 —— Kaptcha 插件介绍 后端生成验证码,前端展示并进行session验证(带完整前后端源码)
|
3月前
|
前端开发 JavaScript
这篇文章介绍了如何使用form表单结合Bootstrap格式将前端数据通过action属性提交到后端的servlet,包括前端表单的创建、数据的一级和二级验证,以及后端servlet的注解和参数获取。
这篇文章介绍了使用AJAX技术将前端页面中表单接收的多个参数快速便捷地传输到后端servlet的方法,并通过示例代码展示了前端JavaScript中的AJAX调用和后端servlet的接收处理。
这篇文章介绍了如何使用form表单结合Bootstrap格式将前端数据通过action属性提交到后端的servlet,包括前端表单的创建、数据的一级和二级验证,以及后端servlet的注解和参数获取。
|
3月前
|
前端开发 JavaScript 大数据
React与Web Workers:开启前端多线程时代的钥匙——深入探索计算密集型任务的优化策略与最佳实践
【8月更文挑战第31天】随着Web应用复杂性的提升,单线程JavaScript已难以胜任高计算量任务。Web Workers通过多线程编程解决了这一问题,使耗时任务独立运行而不阻塞主线程。结合React的组件化与虚拟DOM优势,可将大数据处理等任务交由Web Workers完成,确保UI流畅。最佳实践包括定义清晰接口、加强错误处理及合理评估任务特性。这一结合不仅提升了用户体验,更为前端开发带来多线程时代的全新可能。
75 1
|
3月前
|
前端开发 开发者 UED
数据校验的艺术:揭秘JSF如何将前端与后端验证合二为一,打造无缝用户体验
【8月更文挑战第31天】JavaServer Faces(JSF)是构建企业级Web应用的Java规范,提供了丰富的组件和API,便于快速搭建用户界面。JSF验证框架基于JavaBean验证API(JSR 303/JSR 380),利用注解如`@NotNull`、`@Size`等在模型类上定义验证规则,结合前端的`&lt;h:inputText&gt;`和`&lt;h:message&gt;`标签展示错误信息。
42 0
|
3月前
|
前端开发 数据库 Python
中后台前端开发问题之验证用户提供的uid和token的有效性如何解决
中后台前端开发问题之验证用户提供的uid和token的有效性如何解决
20 0
|
5月前
|
存储 前端开发 搜索推荐
Web前端网站(一) - 登录页面及账号密码验证
页面背景动态是烟花和文字特效与缓缓下落的雪花相结合,在登录表单的旁边还有五个白色光圈以不规则的方式环绕,当鼠标靠近时,会发出彩色的光芒~~~
93 1
Web前端网站(一) - 登录页面及账号密码验证
|
6月前
|
Dart 前端开发 安全
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
【4月更文挑战第30天】本文探讨了Flutter中线程管理和并发编程的关键性,强调其对应用性能和用户体验的影响。Dart语言提供了`async`、`await`、`Stream`和`Future`等原生异步支持。Flutter采用事件驱动的单线程模型,通过`Isolate`实现线程隔离。实践中,可利用`async/await`、`StreamBuilder`和`Isolate`处理异步任务,同时注意线程安全和性能调优。参考文献包括Dart异步编程、Flutter线程模型和DevTools文档。
217 1
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践