路科IC验证--SVlab1中的细节

仿真器是questasim,运行lab1需要配置的文件

 

 本文主要分析tb4中的代码细节

tb4.sv代码

 

复制代码
`timescale 1ns/1ps


module chnl_initiator(
  input               clk,
  input               rstn,
  output logic [31:0] ch_data,
  output logic        ch_valid,
  input               ch_ready,
  input        [ 5:0] ch_margin
);

string name;

function void set_name(string s);
  name = s;
endfunction

task chnl_write(input logic[31:0] data);
  // USER TODO
  // drive valid data
  // ...
  @(posedge clk);
  ch_valid <= 1;
  ch_data <= data;
  @(negedge clk);
  wait(ch_ready === 'b1);
  $display("%t channel initial [%s] sent data %x", $time, name, data);
  chnl_idle();
endtask

task chnl_idle();
  // USER TODO
  // drive idle data
  // ...
  @(posedge clk);
  ch_valid <= 0;
  ch_data <= 0;
endtask

endmodule

module tb4;
logic         clk;
logic         rstn;
logic [31:0]  ch0_data;
logic         ch0_valid;
logic         ch0_ready;
logic [ 5:0]  ch0_margin;
logic [31:0]  ch1_data;
logic         ch1_valid;
logic         ch1_ready;
logic [ 5:0]  ch1_margin;
logic [31:0]  ch2_data;
logic         ch2_valid;
logic         ch2_ready;
logic [ 5:0]  ch2_margin;
logic [31:0]  mcdt_data;
logic         mcdt_val;
logic [ 1:0]  mcdt_id;

mcdt dut(
   .clk_i(clk)
  ,.rstn_i(rstn)
  ,.ch0_data_i(ch0_data)
  ,.ch0_valid_i(ch0_valid)
  ,.ch0_ready_o(ch0_ready)
  ,.ch0_margin_o(ch0_margin)
  ,.ch1_data_i(ch1_data)
  ,.ch1_valid_i(ch1_valid)
  ,.ch1_ready_o(ch1_ready)
  ,.ch1_margin_o(ch1_margin)
  ,.ch2_data_i(ch2_data)
  ,.ch2_valid_i(ch2_valid)
  ,.ch2_ready_o(ch2_ready)
  ,.ch2_margin_o(ch2_margin)
  ,.mcdt_data_o(mcdt_data)
  ,.mcdt_val_o(mcdt_val)
  ,.mcdt_id_o(mcdt_id)
);

// clock generation
initial begin 
  clk <= 0;
  forever begin
    #5 clk <= !clk;
  end
end

// reset trigger
initial begin 
  #10 rstn <= 0;
  repeat(10) @(posedge clk);
  rstn <= 1;
end

logic [31:0] chnl0_arr[];
logic [31:0] chnl1_arr[];
logic [31:0] chnl2_arr[];
// USER TODO
// generate 100 data for each dynamic array
initial begin
  chnl0_arr = new[100];
  chnl1_arr = new[100];
  chnl2_arr = new[100];
  foreach(chnl0_arr[i]) begin
    chnl0_arr[i] = 'h00C0_00000 + i;
    chnl1_arr[i] = 'h00C1_00000 + i;
    chnl2_arr[i] = 'h00C2_00000 + i;
  end
end

// USER TODO
// use the dynamic array, user would send all of data
// data test
initial begin 
  @(posedge rstn);
  repeat(5) @(posedge clk);
  chnl0_init.set_name("chnl0_init");
  foreach(chnl0_arr[i]) chnl0_init.chnl_write(chnl0_arr[i]);
end
initial begin 
  @(posedge rstn);
  repeat(5) @(posedge clk);
  chnl0_init.set_name("chnl0_init");
  foreach(chnl0_arr[i]) chnl0_init.chnl_write(chnl0_arr[i]);
end
initial begin 
  @(posedge rstn);
  repeat(5) @(posedge clk);
  chnl0_init.set_name("chnl0_init");
  foreach(chnl0_arr[i]) chnl0_init.chnl_write(chnl0_arr[i]);
end

chnl_initiator chnl0_init(
  .clk      (clk),
  .rstn     (rstn),
  .ch_data  (ch0_data),
  .ch_valid (ch0_valid),
  .ch_ready (ch0_ready),
  .ch_margin(ch0_margin) 
);

chnl_initiator chnl1_init(
  .clk      (clk),
  .rstn     (rstn),
  .ch_data  (ch1_data),
  .ch_valid (ch1_valid),
  .ch_ready (ch1_ready),
  .ch_margin(ch1_margin) 
);

chnl_initiator chnl2_init(
  .clk      (clk),
  .rstn     (rstn),
  .ch_data  (ch2_data),
  .ch_valid (ch2_valid),
  .ch_ready (ch2_ready),
  .ch_margin(ch2_margin) 
);

endmodule
View Code
复制代码

 


initial begin 
  @(posedge rstn);
  repeat(5) @(posedge clk);
  chnl0_init.set_name("chnl0_init");
  foreach(chnl0_arr[i]) chnl0_init.chnl_write(chnl0_arr[i]);
end

【问题】:三个initial中开启通道采样的代码中为什么用repeat(5)?

【解答与分析】

注意到repeat(5)后面没跟beign,所以只能作用到后面的第一个语句。即循环5次时钟上升沿。

对于复杂设计,其内部reset周期不一定在当前外部的reset周期,有可能需要消化,还要去过一拍两拍的情况,它才能做完reset。因此等待5个时钟上升沿,改为等待1个时钟上升沿也没问题(对于lab1这mcdt小型设计而言)

复制代码
task chnl_write(input logic[31:0] data);
  // USER TODO
  // drive valid data
  // ...
  @(posedge clk);
  ch_valid <= 1;
  ch_data <= data;
  @(negedge clk);
  wait(ch_ready === 'b1);
  $display("%t channel initial [%s] sent data %x", $time, name, data);
  chnl_idle();
endtask
复制代码

【问题】:delta-cycle竞争问题,如何理解@(negedge clk); wait(ch_ready ==='b1)代码含义?

仿真图

 

 【解答与分析】

看图中红线的时钟沿,在红线位置,ch2_ready_0采样到的信号到底是0还是1呢?

事实上,在当前的时钟沿,ch2_ready_0采样到的值只能是时钟沿左边的值0。从设计的逻辑来讲,ready信号的反馈和当前这一拍的时钟沿,它是在同一拍里边发生的,在同一拍发生的情况,要按照组合逻辑来处理,组合逻辑来处理的话,它是没有是时钟这样一个延时的关系的。(组合逻辑是瞬发的)而现在需要捕捉时钟上升沿右边的这部分值,才能实现:判断在这一时钟沿是ready信号的为高的功能如果你捕捉的是左边这个值,那么它在红线那一时刻是0,只有到下一个时钟上升沿才是1,而后面有chnl_idle的任务,会使得验证代码延时时间冗余。

一般使用统一延时时钟下降沿统一延时时钟下降沿其实就是为了捕捉的更准确,方便复用,因为一旦使用时钟的下降沿则不需要关心时钟的周期。当然直接延时1ps、1ns也是可以的,但是这样要考虑时钟周期,若原时钟周期1ns,直接延时2ns,则会出现延时了两个时钟周期,造成错误。

 

 

 

------------------

题外话:路桑对这块delta-cycle的讲解非常精彩

 

posted @   傅红雪a  阅读(181)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
Live2D
欢迎阅读『路科IC验证--SVlab1中的细节』
点击右上角即可分享
微信分享提示