Verilog设计过程中的一些经验与知识点

描述

 

  “ 本文主要分享了在Verilog设计过程中一些经验与知识点,主要包括块语句、阻塞赋值和非阻塞赋值 以及结构说明语句(initial, always, task, function)

01块语句

  • 顺序块 begin…end

    • 块内的语句是按照顺序执行的;

    • 块内的每条语句延时控制都是相对于上条语句结束的时刻;

    • 仿真时,执行到最后一条语句该语句块执行结束。

  • 并行块 fork…end

    • 块内的语句是按照独立的同时开始执行的;

    • 块内的每条语句延时控制都是相对于程序进入该语句块的时刻而言;

    • 仿真时,所需最长时间的语句执行结束后,该语句块执行结束。

 

例:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
reg  [7:0]    l1,l2;  reg  [7:0]    k1,k2;  initial   begin      l1 = 0l2 = 0;      #15  l1 = 2;      #10  l2 = 8;   endinitial  fork    k1 = 0;  k2 = 0;    #15  k1 = 2;    #10  k2 = 8;  join
仿真结果如下:

仿真

从仿真结果可以看出:在顺序块中,15ns的时候,l1被赋值为8’h2,在25ns的时候,l2被赋值为8’h8;而在并行块中,10ns的时候,k2被赋值为8’h8,在15ns的时候,k1被赋值为8’h2。可以很容易明白顺序块和并行块的特性。

 

02阻塞赋值和非阻塞赋值

  • 阻塞赋值(Blocking)

阻塞赋值用“=”表示:在赋值时,先计算等号(“=”)右边部分的值,这时赋值语句不允许其他的语句干扰,直到赋值完成,也就是说“阻塞”是指在当前的赋值完成前阻塞其他类型的赋值任务 
  • 非阻塞赋值(Non_Blocking)

非阻塞赋值用“<=”表示:在赋值操作时刻开始计算非阻塞赋值右边部分的值,赋值操作结束时刻才更新左边部分。

例1:组合逻辑中的阻塞与非阻塞

阻塞代码如下:
  •  
  •  
  •  
  •  
  •  
  •  
always@(a,b,c,d)  begin    i1 = a & b;    i2 = c & d;    i3 = i1 & i2;  end
仿真结果如下:

仿真

非阻塞代码如下:
  •  
  •  
  •  
  •  
  •  
  •  
always@(a,b,c,d)  begin    i1 <= a & b;    i2 <= c & d;    i3 <= i1 & i2;  end

仿真结果如下:

仿真

可以看出i1和i2在阻塞和非阻塞中结果相同,但是i3的结果却不同。这是因为在阻塞赋值中,i3的赋值使用的是i1和i2更新后的值,而非阻塞赋值中i3的赋值则使用的是i1和i2更新前的值。要想解决这一问题,则需要将“always@(a,b,c,d)”改成“always@(a,b,c,d,i1,i2)”代码如下:
  •  
  •  
  •  
  •  
  •  
  •  
always@(a,b,c,d,i1,i2)  begin    i1 <= a & b;    i2 <= c & d;    i3 <= i1 & i2;  end
仿真结果如下:

仿真

综上,组合逻辑中更适合用阻塞赋值语句。

例2:时序逻辑中的阻塞和非阻塞。

以反馈振荡器的代码为例。非阻塞赋值代码:
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
always@(posedge  clk,posedge  rst)    begin      if(rst)  a1 <=0;      else    a1 <=a2;    endalways@(posedge  clk,posedge  rst)    begin      if(rst)  a2 <=1;      else    a2 <=a1;    end

     阻塞赋值代码:

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
always@(posedge  clk,posedge  rst)  begin      if(rst)   b1 = 0;      else      b1 = b2;  endalways@(posedge  clk,posedge  rst)  begin      if(rst)   b2 = 1;      else      b2 = b1;    end

仿真结果如下:

仿真

可以看出阻塞赋值语句并不能达到我们想要的效果;而且综合后阻塞赋值语句中,无法确定哪个always块中的时钟沿先到达,哪个always块中的时钟后到达,所以存在一个冒险竞争的问题。综上,时序逻辑中更适合用非阻塞赋值语句。  

03结构说明语句(initial, always, task, function

  • 语句initial

语法格式如下:
  •  
  •  
  •  
initial begin  // Add code hereend
一般initial语句用于测试文件的编写;但是随着编译器的进步,现在也可以综合,常用于一些变量的初始化。无论是用在什么场景,initial语句只执行一次。
  • 语句always

语法格式如下:     always <时序控制> <语句>

例1:生成仿真时的信号波形

always可以用于仿真时的波形生成:always #5 clk = ~clk;这个例子就形成了一个周期为10ns(时间单位ns根据`timescale确定)的方波,常用来描述时钟信号(如果将#5去掉,那么会生成一个延迟为0的无限循环跳变过程,会发生仿真锁死,这是不推荐的)。仿真结果如下:

仿真

例2:

实现锁存器和触发器

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
always@(posedge  clk or posedge   rst)   begin    if(rst)  cnt <= 0;      else    cnt <= cnt + 1;  endalways@(posedge  clk , posedge   rst)  begin    //add codes  end
多个敏感事件可以用“or”或者“,”区分(rst为复位信号,可以是posedge也可以是negedge)。

仿真

例3:实现组合逻辑

利用always实现组合逻辑时,要将所有的信号放进敏感列表,而时序逻辑中则不需要。
  •  
  •  
  •  
  •  
always@(a or b or c)    begin    x = x+1;  end 
上面的代码表示,a、b、c中任意电平发生变化,begin…end语句就会被触发。仿真结果如下所示:

仿真

  •  
  •  
  •  
  •  
always@(a or b or c or d or e)     begin       out = a + b + c + d + e;    end  
如上所示,因为敏感列表比较长,容易写错,所以Verilog又提供了两个特殊的符号:@*和@(*)。简化代码如下:
  •  
  •  
  •  
  •  
always@(*)  begin     out = a + b + c + d + e;                     end 
仿真结果如下图所示:

仿真

注意:always模块内被赋值的每一个信号都必须定义为reg型 。
  • 语句task

语句task的定义:

         task <任务名>;

                   <端口及数据类型声明语句>;

                   <语句1>;

                   …

                   <语句n>;

         endtask

示例代码如下:
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
reg      [7:0]      j,k,i,x;  always@(posedge  clk  or posedge  rst)    begin      if(rst)        begin          i <= 0;          j <= 0;          k <= 0;          x <= 0;          task1(i,j,k);        end      else        begin          i <= i + 1;          x <= i + 10;          task1(i,j,k);        end    end  task task1;      input    [7:0]      i;      output   [7:0]      j1;      output   [7:0]      k1;    begin      j1 = i + 10;      k1 = i + 11;    end  endtask
仿真结果如下图所示:

仿真

  • 语句function

函数(function)的目的是返回一个用于表达式的值。

语句function的定义:

function<返回值的类型或范围>(函数名)

     <端口说明语句>

     <变量类型说明语句>

                begin

                       …

                end

endfunction

示意代码如下:
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  reg      [7:0]      i,j;  reg      [8:0]      sum_data;    always@(posedge  clk  or posedge  rst)    begin      if(rst)        begin          i <= 100;          j <= 31;          sum_data <= sum(i,j);        end      else        begin          i <= i + 1;          j <= j + 2;          sum_data <= sum(i,j);        end    end
  function  [8:0]  sum;    input  [7:0]  i1;    input  [7:0]  j1;      begin        sum = i1 + j1;      end  endfunction

仿真结果如下图所示:

仿真

 

注意:initial、always、task和function都是可以综合的。

原文标题:Verilog基础知识学习笔记(二)

文章出处:【微信公众号:FPGA之家】欢迎添加关注!文章转载请注明出处。

审核编辑:汤梓红
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分