路科V0—基础(2)设计特性与接口

发布于:2025-07-02 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、Always语句分化

1. always_comb

  • 只能被综合成组合逻辑,若逻辑综合为时序逻辑(触发器、锁存器)则编译过程中会报错
  • always_comb可以自动嵌入敏感列表,并可以将调用的函数中的可能参与运算的信号声明到敏感列表
  • always_comb会限制其他过程对于同一变量进行赋值
  • always_comb在0时刻会自动触发,无论信号在0时刻是否有变化

2. always_latch

  • 表示锁存逻辑,并且会自动嵌入敏感列表
  • 在编译的过程中会自动检查是否生成了锁存逻辑

3. always_ff

  • 敏感列表必须指明posedge、negedge,从而使得EDA工具实现同步或异步逻辑
  • 在编译的过程中会自动检查是否实现了时序逻辑

二、赋值操作

1. SV可以使用'0 '1 'z 'x来快速填充1、0、z、x

2.

  • == 对于带有z、x的变量会是一个unknow的状态  === 也会将z 、x进行比较
  • ==?对于x位可以匹配成任意的数字,而对于z只能匹配成unknow状态

三、unique case 和 priority case

unique case

  • 要求case有并且只能有一条符合匹配条件的语句
  • unique case选项可以并行执行,并且选项必须完备

priority case

  • 要求case语句中至少要有一个满足匹配条件,若多个条件满足,则会执行第一条语句
  • priority case语句和if。。。else。。。等同

四、接口(interface)

将多个信号集束在一个大的信号中,可以非常方便的在各个模块中进行例化

接口的声明与模块类似:

接口与模块之间的区别:

  • 模块的一些端口可以在例化时候不连接信号,但是若模块的接口声明为interface,在例化的时候必须连接到一个端口实例
  • 接口声明时需要在模块或程序之外进行声明。
  • 在声明中可以使用modport指定信号的方向

  • 在接口中可以使用时钟块(clocking block)来指定同步信号相对于时钟的时序。测试平台可以使用@interf.cb表达式来驱动时钟

五、程序块(Program block)

在verilog各个事件的调度顺序如下:

从图中可以看出各个事件的调度顺序,阻塞赋值与非阻塞赋值的调度是不一样的,而#0延时处于中间状态。

由于各个事件的调度顺序不同,就有可能导致程序执行过程中,相应事件采集到错误的值。

例如:

module counter(input clk);
  bit [3:0] cnt;

  always @(posedge clk) begin
    cnt <= cnt + 3;
    $display("%0t DUT cnt = %0d", $time, cnt);
  end
endmodule

module tb1;
bit clk;
bit [3:0] cnt;

  initial begin
    forever #5ns clk <= ~clk;
  end
  counter counter_inst(clk);
  always @(posedge clk) begin
    $display("%0t TB cnt = %0d", $time, counter_inst.cnt);
  end
endmodule

 运行结果:

tb中$dispaly的目的是监视cnt的变化,但是由于系统函数优先调度,所以$display打印出来的一直是cnt变化之前的值,也就是采样错误。

System Verilog  多添加了几个调度区域

前三个是verilog的调度区域,observed是为断言准备的,而在reactive区域正式进行断言判断

要解决前一个例子出现的问题,可以使用程序块

program block主要是将验证与设计分离,让他们处于不同的时间调度中

module counter(input clk);
  bit [3:0] cnt;

  always @(posedge clk) begin
    cnt <= cnt + 3;
    $display("%0t DUT cnt = %0d", $time, cnt);
  end
endmodule

program sample(input clk);
  initial begin
    forever begin
      @(posedge clk)
        $display("%0t TB cnt = %0d", $time, counter_inst.cnt);
    end
  end
endprogram

module tb1;
bit clk;
bit [3:0] cnt;

  initial begin
    forever #5ns clk <= ~clk;
  end
  counter counter_inst(clk);
  sample(clk);

  endmodule

仿真结果 

 

加入了program block块后,tb中的$display在reactive的时间区域进行调度,避免了和设计的竞争问题。

program需要注意的问题

  • program中不能例化其他program和module
  • 不能出现interface和always,可以使用initial forever替代always
  • program内部可以发起多个initial块
  • program中内部定义的变量最好采用阻塞赋值,当然采用非阻塞仿真器也不会产生error,驱动外部信号则应该采用非阻塞赋值
  • program中的initial块和module中的initial块执行位置不同,前者在reactive,后者在active块中执行。
  • program中存在的多个initial块中,如果有一个initial采用了退出系统函数$exit(),则会结束该program,而不仅仅是该initial块。

 


网站公告

今日签到

点亮在社区的每一天
去签到