MCDF实验1

菜鸟记录

TB1.从Verilog到SV的进场

1.1 修改tb1.sv,比较和tb.v之间的仿真行为差别:如下图所示并无差别,可见sv对Verilog是全兼容的。

1.2 将tb1.sv中的reg或者wire类型修改为logic类型,编译仿真行为无差别,说明,在SV中,reg和wire类型都可以简化为logic类型。

1.3  在1.2的基础上,将rstn的类型由logic变为bit,查看编译仿真行为,可见,一开始时钟信号便被拉低为0,这是以为bit二值逻辑默认为0,而logic四值逻辑默认为x,经过10ns后才被拉低为0。

TB2. 方法task和函数function

 2.1 不做修改的情况下, 对 tb2.sv 进行编译仿真, 时钟信号和复位信号还正常吗?为什么?

      不正常。

2.2 在两个 initial 块中分别调用产生时钟和复位的 task, 再编译仿真查看时钟信号和复位信号, 是否恢复正常呢?

        恢复正常。

2.3 为什么要将两个 task 在两个 initial 块中调用?这是为什么呢?是否可以在一个 initial 块中
调用呢?如果可以, 调用它们的顺序是什么?

        因为task和function一样,是一种函数,需要被调用才能被执行,必须被过程块initial或者always调用才能被执行。

        不可以放在一个initial块中调用,多个initial块是并行执行的,而initial块中的语句是串行执行的。

2.4 是否可以读出目前时钟的周期和频率呢?该如何测量呢?如果我们想进化 clk_gen()方法, 使其变为可以设置时钟周期的任务 ckgen(int peroid), 那么该怎么修改目前的任务clk_gen()呢?修改成功后, 请在 initial 块中调用任务 cdk_ gen(20), 看看波形中的时钟周期是否变为 20ns 呢?

        通过wave菜单栏,可以添加查看时钟频率,周期则可以通过两个bar之间数据读出。

task clk_gen(int period);
  clk <= 0;
  forever begin
    #(period/2) clk<= !clk;
  end
endtask

2.5 如果将 timescale 1ns/1ps 修改为 timescale 1ps/1ps, 那么仿真中的时钟周期是否发生变
化?这是为什么呢?

        周期发生变化,变为20ps,因为timescale 1ps/1ps,第一个参数为仿真所用时间单位,第二个参数为时间精度。

TB3. 数组的使用

要先生成 100 个数,并对它们按照日前的数值规则进行赋值,创建 3 个动态数组,分别放置要发送给 3 个 slave 的数据。利用之前生成的数组数据, 将它们读取并发送给三个 channel。
 

// 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

// use the dynamic array, user would send all of data
// data test
initial begin 
  @(posedge rstn);
  repeat(5) @(posedge clk);
  // channel 0 test
  // TODO use chnl0_arr to send all data
  foreach(chnl0_arr[i]) chnl_write(0,chnl0_arr[i]);
  // channel 1 test
  // TODO use chnl1_arr to send all data
  foreach(chnl1_arr[i]) chnl_write(1,chnl1_arr[i]);
  // channel 2 test
  // TODO use chnl2_arr to send all data
  foreach(chnl2_arr[i]) chnl_write(2,chnl2_arr[i]);
end

TB4. 验证结构

例化三个chnl_initiator 实例 chnl0_init、chnl1_ init 和 chnl2_init。它们的作用扮演每个channel_slave 通道对应的 stimulator,发送激励,因此需要在其模块chnl_initiator 中定义它的三个方法,即 set_ name()、chpl_write()和 chnl_idle()。
① chnl_idle() :要实现的一个时钟周期的空闲, 在该周期中, ch. valid 应为低,ch. data 应
为 0。

② chnl_ write():要实现一次有效的写数据,并随后调用 chnl_idle( ),实现一个空闲周期,在实现有效写数据时,需要考虑如何使用 ch_ ready 信号,结合功能描述的 channel_slave接口时序看,只有当 valid 为高且 ready 为高时,数据写入才算成功,如果此时 ready 为低,那么则应该保持数据和 valid 信号,直到 ready 拉高时,数据写入才算成功。
③ set_name( ):设置实例的名称,在 initial 过程块“data_test"中,在发送各个 channel 数据前,设置各个 channel_initiator 的实例名称,这样方便在仿真时各个实例的打印信总可以显示它们各自的名称、 数据发送时间和数据内容, 便于阅读和调试。

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

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值