MCDF实验2
接口的使用
实验用于验证组件和DUT之间通过接口连接
tb1.1:可以看到之前的实验 channel initiator 发送的数据例如 valid 和 data 与时钟 clk 均在同一个变化沿,没有任何延迟,这种0延迟的数据发送不利于波形查看和阅读,因此在已有代码的基础上使用 intf.ck 的方式来做数据驱动,并且再观察波形,查看驱动的数据与时钟上升沿的延迟是多少?
答:此处实验并没有更改代码,有延迟1ns,不加也没发现需要
tb1.2:为了更好地控制相邻数据之间的空闲间隔,引入一个变量 idle_cycles, 它表示相邻有效数据之间的间隔。已有代码会使得有效数据之间保持固定的一个空闲周期,我们需要使用 idle_cycles 变量, 来灵活控制有效数据之间的空闲周期。通过这个方法,在 tb 的 initial 块中我们通过方法 set_idle_cycles() 使得三个 channel initiator 的空闲周期变为 0, 即可以实现有效数据的连续发送。
仿真的结束
通过fork-join的功能和方法,实现三个chnl_initiator同时发送数据的要求。同时将不同test也组装到task中,以此区分不同的测试内容。
fork join :内部是并行执行
1.fork join:当fork join内部全部执行完之后,才会继续往下顺序执行,不用同时执行完,只要都执行过就可以;
2.fork join_none:fork join_none语句块执行的同时,父线程会继续执行,即全部并行执行;
3.fork join_any:执行到fork join_any内部语句时,当有任意一个语句执行完,那么后续的父类线程也会继续顺序执行。
4.可以通过 wait fork ,等待所有的点火的线程都执行完。点火的线程在task推出后其实还有可能在后他执行。
5.使用disable forkname,停止指定的线程,会迭代停止内部的线程,同时也好注意会不会停止调用的其他不想停的同名线程。
6.disable fork,会停止当前以及外层线程
要求2.1
要求2.2
fork: fork_all_run forever chnl0_init.chnl_write(chnl0_gen.get_data()); forever chnl1_init.chnl_write(chnl1_gen.get_data()); forever chnl2_init.chnl_write(chnl2_gen.get_data()); join_none fork wait(chnl0_init.intf.ch_margin == 0); wait(chnl1_init.intf.ch_margin == 0); wait(chnl2_init.intf.ch_margin == 0); join disable fork_all_run; //????can not understand fork //close initial wait(chnl0_init.chnl_idle()); wait(chnl1_init.chnl_idle()); wait(chnl2_init.chnl_idle()); join fork //wait for transmission to complete wait(chnl0_init.intf.ch_margin == 'h20); wait(chnl1_init.intf.ch_margin == 'h20); wait(chnl2_init.intf.ch_margin == 'h20); join
类的例化和类的成员
将之前硬件盒子(module)中的数据内容移植到软件盒子(class)
// USER TODO 3.4 // check if the object use is correct? class chnl_generator; chnl_trans trans[$]; int num; int id; function new(int n); this.id = n this.num = 0;z // t = new(); // only one t if instantiate it here endfunction function chnl_trans get_trans(); chnl_trans t = new(); //each run yield a new t t.data = 'h00C0_0000 + (this.id<<16) + this.num; t.id = this.id; t.num = this.num; this.num++; this.trans.push_back(t); //返回句柄??? return t; endfunction
trans[$]用于存放句柄。在function new(int n)中创建实例,三个chnl在初始化时只创建了一个实例,而在get_trans()函数中只有一个实例的话,不论data产生了多少,最终句柄都指向一个对象,只有一个数字,因为数据不断被覆盖直至最后一个。
正确的是应该在get_trans()内创建实例,每次调用产生数据都会创建一个对象,并将句柄保存至trans[$]中,如果repeat(500),那么会产生500个句柄和500个不同的数据。
包的定义和类的继承
程序经过不断的抽象,理解起来难了
- 通过agent将generator和initiator整合起来,整合的同时需要注意属性、方法的传递和如何和接口进行交互。
- 最外面通过root-test对接口进行连接,对agent进行整体的测试。测试时只需要对最外面的root-test进行配置和运行。
整体结构需要仔细理解
有一些不明白的:
class chnl_initiator; local string name; local int idle_cycles; local virtual chnl_intf intf; //为什么属性要声明为virtual function void set_interface(virtual chnl_intf intf);//此处传参也不理解 if(intf == null) $error("interface handle is NULL, please check if target interface has been intantiated"); else this.intf = intf; endfunction task chnl_write(input chnl_trans t); @(posedge intf.clk); // USER TODO 1.1 // Please use the clocking drv_ck of chnl_intf to drive data intf.drv_ck.ch_valid <= 1; intf.drv_ck.ch_data <= t.data; @(negedge intf.clk); wait(intf.ch_ready === 'b1); $display("%t channel initiator [%s] sent data %x", $time, name, t.data); // USER TODO 1.2 // Apply variable idle_cycles and decide how many idle cycles to be // inserted between two sequential data repeat(this.idle_cycles) chnl_idle(); endtask task chnl_idle(); @(posedge intf.clk); // USER TODO 1.1 // Please use the clocking drv_ck of chnl_intf to drive data intf.drv_ck.ch_valid <= 0; intf.drv_ck.ch_data <= 0; endtask endclass: chnl_initiator
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?