MCDT测试结构
MCDT测试平台结构
要区分哪部分是硬件、哪部分是软件
HW : 待测的设计
SW : 测试环境
软硬件之间交互通过interface
要清楚验证环境的各个组件之间的位置层次关系和它们之间是否有数据通信 !
验证组件之间的通信都是基于事务级的传送;
验证组件和硬件的通信必须依赖接口,通过硬件时序才能实现;
理解仿真如何开始运转,各个验证组件如何工作,最后仿真如何结束
一些要注意的细节:
channel initiator 和 channel monitor一一对应;
initiator和monitor被封装在了agent中;
由于arbiter不需要与下行握手,所以只外置了mcdt monitor而不需要mcdt responder;
所有的monitor都将监测到的数据送到mcdt checker;
MCDT checker内部例化了若干FIFO,是为了接收从MCDT的输入端和输出端的数据;
由于checker输入端和输出端FIFO中存放的数据类型一致,也为数据比较带来了方便;
激励发生器
监测器
比较器
实现激励发生器 实现 channel stimulator
数据事务: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 class chnl_trans; rand bit [31 :0 ] data[]; rand int ch_id; rand int pkt_id; rand int data_nidles; rand int pkt_nidles; bit rsp; local static int obj_id=0 ; constraint cstr { soft data.size inside {[4 :8 ]}; foreach (data[i]) data[i] == 'hC000_0000 + (this .ch_id << 24 ) + (this .pkt_id << 8 ) + i; soft ch_id == 0 ; soft pkt_id == 0 ; data_nidles inside {[0 :2 ]}; pkt_nidles inside {[1 :10 ]}; }; function new (); this .obj_id ++; endfunction function chnl_trans clone(); chnl_trans c = new (); c.data = this .data ; c.ch_id = this .ch_id ; c.pkt_id = this .pkt_id ; c.data_nidles = this .data_nidles ; c.pkt_nidles = this .pkt_nidles ; c.rsp = this .rsp ; return c; endfunction endclass : chnl_trans
驱动激励的组件:
chnl_initiator的作用是从chnl_generator中获取chnl_trans的数据,并将其驱动到接口chnl_interface上面 ;
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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class chnl_initiator; local string name; local virtual chnl_intf intf; mailbox #(chnl_trans) req_mb; mailbox #(chnl_trans) rsp_mb; function new (string name = "chnl_initiator" ); this .name = name; endfunction function void set_interface(virtual chnl_intf intf); if (intf == null ) $error ("interface handle is NULL, please check if target interface has been intrantiated" ); else this .intf = intf; endfunction task run(); this .driver (); endtask task drive(); chnl_trans req, rsp; @(posedge intf.rstn ); forever begin this .req_mb .get (req); this .chnl_write (req); rsp = req.clone (); rsp.rsp = 1 ; this .rsp_mb .put (rsp); end endtask task chnl_write(input chnl_trans t); foreach (t.data [i]) begin @(posedge intf.clk ); intf.ch_valid <= 1 ; intf.ch_data <= t.data [i]; @(negedge intf.clk ); wait (intf.ch_ready == 'b1 ); $display ("%0t: channel initiator [%s] sent data %x" , $time , name, t.data [i]); repeat (t.data_nidles ) chnl_idle(); end repeat (t.pkt_nidles ) chnl_idle(); endtask task chnl_idle(); @(posedge intf.clk ); intf.ch_valid <= 0 ; intf.ch_data <= 0 ; endtask endclass : chnl_initiator
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 39 40 41 42 43 44 45 46 47 48 49 50 class chnl_generator; rand int pkt_id = -1 ; rand int ch_id = -1 ; rand int data_nidles = -1 ; rand int pkt_nidles = -1 ; rand int data_size = -1 ; rand int ntrans = 10 ; mailbox #(chnl_trans) req_mb; mainbox #(chnl_trans) rsp_mb; constraint cstr { soft ch_id == -1 ; soft pkt_id == -1 ; soft data_size == -1 ; soft data_nidles == -1 ; soft pkt_nidles == -1 ; soft ntrans == 10 ; } function new (); this .req_mb = new (); this .rsp_mb = new (); endfunction task run(); repeat (ntrans) send_trans; run_stop_flags.put (); endtask task send_trans(); chnl_trans req, rsp; req = new (); assert (req.randomize with { local ::ch_id >=0 -> ch_id == local ::ch_id; local ::pkt_id >=0 -> pkt_id == local ::pkt_id; local ::data_nidles >=0 -> data_nidles == local ::data_nidles; local ::pkt_nidles >=0 -> pkt_nidles == local ::pkt_nidles; local ::data_size >=0 -> data_size == local ::data_size; }) else $fatal ("[RNDFAIL] channel packet randomization failure!" ); this .pkt_id ++; this .req_mb .put (req); this .rsp_mb .get (rsp); assert (rsp.rsp ) else $error ("[RSPERR] %0t error response received!" , $time ); endtask endclass : chnl_generator
监测器和检查器 监测器
用户自定义结构体mon_data_t
用来存放监测到的数据,被chnl_monitor
和mcdt_monitor
使用
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 typedef struct packed { bit [31 :0 ] data; bit [1 :0 ] id; } mon_data_t; class chnl_monitor; local string name; local virtual chnl_intf intf; mailbox #(mon_data_t) mon_mb; function new (string name = "chnl_monitor" ); this .name = name; endfunction function void set_interface(virtual chnl_intf intf); if (intf == null ) $error ("interface handle is NULL, please check if target interface has been intrantiated" ); else this .intf = intf; endfunction task run(); this .mon_trans (); endtask task mon_trans(); mon_data_t m; forever begin @(posedge intf.clk iff (intf.ch_valid ==='b1 && intf.ch_ready ==='b1 )); m.data = intf.ch_data ; mon_mb.put (m); $display ("%0t %s channel monitor data %8x" , $time , this .name , m.data ); end endtask endclass : chnl_monitor
再实现一个mcdt_monitor
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 class mcdt_monitor; local string name; local virtual mcdt_intf intf; mailbox #(mon_data_t) mon_mb; function new (string name = "mcdt_monitor" ); this .name = name; endfunction function void set_interface(virtual mcdt_intf intf); if (intf == null ) $error ("interface handle is NULL, please check if target interface has been intrantiated" ); else this .intf = intf; endfunction task run(); this .mon_trans (); endtask task mon_trans(); mon_data_t m; forever begin @(posedge intf.clk iff intf.mcdt_val ==='b1 ); m.data = intf.mcdt_data ; m.id = intf.mcdt_id ; mon_mb.put (m); $display ("%0t %s mcdt monitor data %8x and id %0d" , $time , this .name , m.data , m.id ); end endtask endclass : mcdt_monitor
检查器 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 39 40 41 42 43 class mcdt_checker; local string name; local int error_count; local int cmp_count; mailbox #(mon_data_t) in_mbs[3 ]; mainbox #(mon_data_t) out_mb; function new (string name = "mcdt_checker" ); this .name = name; foreach (this .in_mbs [i]) this .in_mbs [i] = new (); this .out_mb = new (); this .error_count = 0 ; this .cmp_count = 0 ; endfunction task run(); this .do_compare (); endtask task do_compare(); mon_data_t im, om; forever begin out_mb.get (m); case (om.id ) 0 : in_mbs[0 ].get (im); 1 : in_mbs[1 ].get (im); 2 : in_mbs[2 ].get (im); default : $fatal ("id %0d is not available" , om.id ); endcase if (om.data != im.id ) begin this .error_count ++; $error ("[CMPFAIL] Compared failed! mcdt out data %8x ch_id %0d is not equal with channel in data %8x" , om.data , om.id , im.data ); end else begin $display ("[CMPSUCD] Compared success! mcdt out data %8x ch_id %0d is equal with channel in data %8x" , om.data , om.id , im.data ); end this .cmp_count ++; end endtask endclass : mcdt_checker
顶层环境和测试用例 实现顶层环境1-agent 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 class chnl_agent; local string name; chnl_initiator init; chnl_monitor mon; virtual chnl_intf vif; function new (string name = "chnl_agent" ); this .name = name; this .init = new ({name, ".init" }); this .mon = new ({name, ".mon" }); endfunction function void set_interface(virtual chnl_intf vif); this .vif = vif; init.set_interface (vif); mon.set_interface (vif); endfunction task run(); fork : init.run (); mon.run (); join endtask endclass : chnl_agent
实现顶层环境2-mcdt_root_test 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 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 class mcdt_root_test; chnl_generator gen[3 ]; chnl_agent agents[3 ]; mcdt_monitor mcdt_mon; mcdt_checker chker; protected string name; event gen_stop_e; function new (string name = "mcdt_root_test" ); this .name = name; this .chker = new (); foreach (agents[i]) begin this .agents [i] = new ($sformatf ("agent%0d" , i)); this .gen [i] = new (); this .agents [i].init .req_mb = this .gen [i].req_mb ; this .agents [i].init .rsp_mb = this .gen [i].rsp_mb ; this .agents [i].mon .mon_mb = this .chker .in_mbs [i]; end this .mcdt_mon = new (); this .mcdt_mon .mon_mb = this .chker .out_mb ; $display ("%s instantiated and connected objects" , this .name ); endfunction virtual task gen_stop_callback(); endtask virtual task run_stop_callback(); $diplay("run_stop_callback enterred" ); $display ("%s: wait for all generators have generated and tranferred transcations" , this .name ); run_stop_flags.get (3 ); $finish (); endtask virtual task run(); this .do_config (); fork agents[0 ].run (); agents[1 ].run (); agents[2 ].run (); mcdt_mon.run (); chker.run (); join_none fork : gen_threads gen[0 ].run (); gen[1 ].run (); gen[2 ].run (); join run_stop_callback(); endtask virtual function void set_interface(virtual chnl_intf ch0_vif, virtual chnl_intf ch1_vif, virtual chnl_intf ch2_vif, virtual mcdt_intf mcdt_vif); agents[0 ].set_interface (ch0_vif); agents[1 ].set_interface (ch1_vif); agents[2 ].set_interface (ch2_vif); mcdt_mon.set_interface (mcdt_vif); endfunction virtual function void do_config(); endfunction endclass : mcdt_root_test
实现测试用例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class mcdt_basic_test extends mcdt_root_test; function new (string name = "mcdt_basic_test" ); super .new (name); endfunction virtual function void do_config(); super .do_config (); assert (gen[0 ].randomize () with {ntrans==100 ; data_nidles==0 ; pkt_nidles==0 ; data_size==8 ;}) else $fatal ("[RNDFAIL] gen[0] randomize failed" ); assert (gen[1 ].randomize () with {ntrans==50 ; data_nidles inside {[1 :2 ]}; pkt_nidles inside {[3 :5 ]}; data_size==6 ;}) else $fatal ("[RNDFAIL] gen[1] randomize failed" ); assert (gen[2 ].randomize () with {ntrans==80 ; data_nidles inside {[0 :1 ]}; pkt_nidles inside {[1 :2 ]}; data_size==32 ;}) else $fatal ("[RNDFAIL] gen[2] randomize failed" ); endfunction endclass : mcdt_basic_test