verilog基础——always、initial

本文详细介绍了Verilog中initial和always块的使用,包括initial块的初始化功能,always块的边沿触发和电平触发,以及always@(*)和always wait()的应用。在Verilog编程中,initial块仅执行一次,而always块会根据条件反复执行,对于信号赋值和时序逻辑描述至关重要。

目录

前言

过程

总结

initial和always

  1、initial

  2、always

  3、always@() 

(1)always块的边沿触发和电平触发

(2)always@ (*)

(3)always wait()


前言

     验证random模块时,模块中的rst和soft_rst都是模块的端口,rst为高有效,soft_rst为低有效。前期并没有测试soft_rst,后期发现本测试点,并进行验证时,发现在vip中并没有soft_rst以端口形式存在。刚开始想自己加上,后来觉着将soft_rst和rst连接比较方便。尝试使用forever、initial和always,发现自己对这几个使用有所欠缺。

top_test中对rst的赋值

reg rst_n = 'b0;
initial begin
  #200;
  rst_n = 1'b1;
end

过程

    刚开始rst_n为0,在200ns之后再赋值为1,所以想在300ns后再将soft_rst赋值为rst,所以使用以下代码,代码可以编译通过,但是,仿真跑不起来,说明这种方法会导致仿真从一开始hang住,sim.log是空白的,猜测是rst_n刚开始有。

reg soft_rst = 'b0;
initial begin
  #300;
  forever begin
    rst_n = ~soft_rst;
  end
end

    后面将forever改为always,编译就会有问题,Error: Syntax error  : token  is  'always'

reg soft_rst = 'b0;
initial begin
  #300;
  if ($test$plusargs("random_soft_rst")) begin
    always@(soft_rst) begin
      rst_n = ~soft_rst;
    end
  end
end

后面查了一下这几个的用法和思考哪里出了问题,发现always和initial是从仿真一开始同时立即执行,所以always块不能放在initial块中,更加不能加上延时。后面又尝试将initial块和延时去掉,出现elab fail,原因是if语句不能孤立的放在module中。

改为下面,就没有问题了,但是rst_n在刚开始就为1,没办法像之前一样在200ns之后再改变,仿真暂时可以通过,就先这样了,如果有知道的小伙伴,告诉一下,感谢~

reg soft_rst = 'b0;
always@(soft_rst) begin
  if ($test$plusargs("random_soft_rst")) begin
    rst_n = ~soft_rst;
  end
end

总结

initial和always

    verilog在本质上是并发而非顺序的。verilog中的各个执行流程(进程)并发执行,而不是顺序执行的。每个initial语句和always语句代表一个独立的执行过程,每个执行过程从仿真时间0开始执行并且两种语句不能嵌套使用。  

    所有的initial语句内的语句构成了一个initial块。initial块从仿真0时刻开始执行,在整个仿真过程中只执行一次。如果一个模块中包括了若干个initial块,则这些initial块从仿真0时刻开始并发执行,且每个块的执行是各自独立的。如果在块内包含了多条行为语句,那么需要将这些语句组成一组,一般式使用关键字begin和end将他们组合在一个块语句;如果块内只有一条语句,则不必使用begin和end。
    两者的关系: 一个程序块可以有多个initial和always过程块。每个initial和always过程快在仿真的一开始同时立即执行initial语句只执行一次,而always语句则不断重复的活动着,直到仿真结束。但always块语句后面跟着的过程快是否运行,满足则运行一次,满足则运行一次,直到程序结束。一段程序中使用的initial和always语句的次数不受限制,他们都是同时开始运行的。

相同点
1:always语句 和 initial语句;可以多次使用;
2:always语句 和 initial语句;各语句块 整体 是独立运行;
3:always语句 和 initial语句;各语句块 整体 是并行执行;
不同点
1:initial语句不带触发条件;                            always语句:带触发条件;
2:initial语句只执行一次;                               always语句:每当“控制时间敏感表”条件满足时,就会执行一次always语句内的块语句;
3:initial语句不可综合;                                   always语句:可以被综合;
4:initial语句主要用于仿真测试中的初始化;   always语句:没有限定;

    always语句一般含有 “@(控制事情敏感表)”,如果控制事情敏感表的条件被满足,那么执行always语句内的块语句;如果没有控制事情敏感表,那就表示 always语句的条件一直被满足,可以不断执行always语句你内的块语句。

  1、initial

    (1)语句格式:

initial begin .......end    : 省略号中是 语句块,begin...end中按照顺序依次执行,仿真开始对各变量进行初始化,这个初始化不需要仿真时间,即在0ns时间内。

注意

一个模块中可以有许多个initial块,它们都是并行运行的。initial语句最常见的是用于测试文件里面的初始化语句,用来产生测试环境和设置信号记录。


  2、always

always语句在仿真过程中不断的运行着。initial语句只执行一次,而always语句则不断重复的活动着,直到仿真结束。
但always块语句后面跟着的过程快是否运行,满足则运行一次,满足则运行一次,直到程序结束。

always语句注意点:

1、不要在不同的always块内为同一个变量赋值。即某个信号出现在<=或者=左边时,只能在一个always块内。

2、不要在同一个always块内同时使用阻塞赋值(=)和非阻塞赋值(<=)。

3、在使用always块描述组合逻辑时使用阻塞赋值(=);使用always块描述时序逻辑时使用非阻塞赋值(<=)。

4、任何在always块内被赋值的变量都必须是寄存器型(reg)。

5、always的敏感列表中可以同时包括多个电平敏感事件,也可以包括多个边沿敏感事件,但不能同时有电平和边沿敏感事件。另外,在敏感列表中,同时包括一个信号的上升沿敏感事件和下降沿敏感事件是不容许的,因为这两个事件可以合并为一个电平事件。

(1)语句格式:

always <时序控制> <语句>
always语句由于其不断活动的特性,只有和一定的时序控制逻辑结合在一起才有用。如果一个always没有时序控制,它将会产生死锁,例如: always clk = ~clk;
加上时序控制 always可以用来产生时钟信号:always #10 clk = ~clk 表示10个时间单位翻转一次。

  3、always@() 

(1)always块的边沿触发和电平触发

always的时序控制可以使边沿触发(上升沿或者下降沿)也可以用电平触发,可以是单个信号也可以是多个信号,中间需要用关键字or连接。

     边沿触发时,always只要其中一个沿到来,就立即执行一次过程块,然后等待下一次的边沿到来。

    电平触发时,多个电平触发的的always块,只要a,b,c中的任何一个发生变化,从高到低或者从低到高都会执行一次过程。

> 边沿触发
always@(posedge clk or negedge  rst)
  begin
    ....
  end

> 电平触发
always@(a or b or c)
  begin
   ....
  end

一个模块可以包含多个always块,它们都是并行执行的。

示例

//有异步复位的电平敏感锁存器
always@(reset or clock or d)//也可以写成always@(a,b,c) 用,代替or
  begin
    if(reset)
       q = 1‘b0;
    else if(clock)
       q = d;
  end
//用reset异步下降沿复位,clock的正条边沿触发的D寄存器
always@(negedge reset , posedge clk)
  if(!reset)
    q  <= 0;
  else
    q  <= d;

(2)always@ (*)

用符号always@(*)可以把所有输入变量都包含进敏感列表

例如:
always@(a,b,c,d,e,f,g,h,r,m) begin
  out1 = a?(b+c):(d+e);
  out2 = f?(g+h):(r+m)
end

  可以用下面代码来代替

always@(*) begin
  out1 = a?(b+c):(d+e);
  out2 = f?(g+h):(r+m)
end

(3)always wait()

前面所讨论的时间控制都需要等待信号值的变化或者事件的触发,使用符号@和后面的敏感列表来表示。
verilog允许另外一种形式表示的电平敏感时序控制(即后面的语句或者语句块必须等到某个条件诶真是才能执行),具体请看下面例子:

always 
   wait(count_enabel)  # 20 count  = count   +1;
   
     ```

     上面例子中,仿真器连续监视count_enable的值,若其值为1,在20个时间单位过后执行后面的语句,若其值为0,则不执行后面的语句。若其值始终为1,那么count将每过20个时间单位加1.

未完。。。 

序列检测器是一种电路,用于检测输入信号序列是否符合某种特定的模式。在Verilog HDL语言中,可以使用状态机来实现序列检测器。以下是一个简单的序列检测器的Verilog HDL代码示例: ```verilog module sequence_detector( input clk, // 时钟信号 input reset, // 复位信号 input data, // 输入数据信号 output reg match // 匹配信号 ); // 定义状态 typedef enum logic [1:0] { STATE_IDLE, // 空闲状态 STATE_S1, // 状态1 STATE_S2, // 状态2 STATE_S3 // 状态3 } state_t; // 定义状态转移表 parameter [3:0] TRANS_TABLE [0:3] = '{4'b0000, 4'b0001, 4'b0010, 4'b0100}; // 定义当前状态变量和下一个状态变量 reg [1:0] state, next_state; // 初始化状态为IDLE initial begin state = STATE_IDLE; end // 定义状态机逻辑 always @ (posedge clk, posedge reset) begin if (reset) begin state <= STATE_IDLE; end else begin state <= next_state; end end // 定义状态转移逻辑 always @ (*) begin case (state) STATE_IDLE: begin if (data) begin next_state = STATE_S1; end else begin next_state = STATE_IDLE; end end STATE_S1: begin if (!data) begin next_state = STATE_IDLE; end else if (data) begin next_state = STATE_S2; end end STATE_S2: begin if (!data) begin next_state = STATE_IDLE; end else if (data) begin next_state = STATE_S3; end end STATE_S3: begin if (!data) begin next_state = STATE_IDLE; match = 1; end else begin next_state = STATE_S3; end end endcase end endmodule ``` 这个序列检测器可以检测输入数据信号是否符合“1101”这个模式。输入数据信号通过data端口输入,匹配结果通过match端口输出。当输入数据信号符合“1101”这个模式时,match信号会被置为1。如果输入数据信号不符合模式,match信号会保持为0。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值