在平台的搭建中,需要关注的重点一是平台的结构,怎么样便于复用,怎么样便于使用,不需要知道内部层次结构就可以很好的配置和使用。另一点就是实现了,驱动怎么实现?monitor怎么实现?sequence怎么实现?这些在VIP里都是加密的,都是“核心机密”。而其他结构agent, coverge collector, scoreboard等等结构基本大同小异。本文主要讨论“实现”这一方面的一小部分内容,给大家一点参考,有不正确的地方请不吝指正。
(转载请注明出处,谢谢!seabeam)
按照mentor的分类方法,driver的model分为
Unidirectional Non-Pipelined: ADPCM, PCM
Bidirectional Non-Pipelined: AMBA APB
Pipelined: AMBA AHB
Out Of Order Pipelined: AMBA AXI, OCP
前两种相对简单,双向的无非加一个读写判断就搞定了,后两种的实现就比较麻烦,对于新手来说是个不小的挑战。
下面是mentor的例子:
1
2
3
4
5
6
7
8
9
10
11
12class mbus_pipelined_driver extends uvm_driver #(mbus_seq_item); ... task run_phase(uvm_phase phase); @(posedge MBUS.MRESETN); @(posedge MBUS.MCLK); fork do_pipelined_transfer; do_pipelined_transfer; join endtask ...
继续前文,关键问题在于两个相同的线程,怎么样让后一个线程在前一个线程的特定时间后延迟一段特定的时间再驱动。没错,do_pipelined_transfer()就是核心中的核心了,看一看怎么实现的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39task automatic do_pipelined_transfer; mbus_seq_item req; forever begin pipeline_lock.get(); seq_item_port.get(req); accept_tr(req, $time); void'(begin_tr(req, "pipelined_driver")); MBUS.MADDR <= req.MADDR; MBUS.MREAD <= req.MREAD; MBUS.MOPCODE <= req.MOPCODE; @(posedge MBUS.MCLK); while(!MBUS.MRDY == 1) begin @(posedge MBUS.MCLK); end // End of command phase: // - unlock pipeline semaphore pipeline_lock.put(); // Complete the data phase if(req.MREAD == 1) begin @(posedge MBUS.MCLK); while(MBUS.MRDY != 1) begin @(posedge MBUS.MCLK); end req.MRESP = MBUS.MRESP; req.MRDATA = MBUS.MRDATA; end else begin MBUS.MWDATA <= req.MWDATA; @(posedge MBUS.MCLK); while(MBUS.MRDY != 1) begin @(posedge MBUS.MCLK); end req.MRESP = MBUS.MRESP; end // Return the request as a response seq_item_port.put(req); end_tr(req); end endtask: do_pipelined_transfer
红色高亮就是关键所在,延时全靠它了。pipeline_lock是一个SV的内建数据类型信号量( semaphore),正是它保证了两个线程的pipeline操作。当第一个线程的第一个phase开始时,信号量锁住,第二个线程需要等待信号量的释放。当第一个线程把第一个phase驱动完毕后,释放信号量,这时候第一个线程开始驱动其二个phase,而第二个线程也可以取数开始驱动第一个phase了。
如果有更多的phase怎么办?mentor没有提及,其实原理是类似的,有n级流水线,那么run_phase()里就应该开启多条并行线程,但是do_pipeline_transfer()里还是一样,只锁住第一个stage即可。sequence就只管不停的发数,driver只管来一个transaction驱动一个,所以是forever无线循环。
这种方法的好处是直观,很符合通常的印象,但是并行线程debug起来可不是那么容易啊。
最后
以上就是无情雨最近收集整理的关于SV(UVM)实现多phase pipeline driver建模的全部内容,更多相关SV(UVM)实现多phase内容请搜索靠谱客的其他文章。
发表评论 取消回复