FPGA学习篇——Verilog学习4

发布于:2025-03-06 ⋅ 阅读:(15) ⋅ 点赞:(0)

1.1 结构语句

结构语句主要是initial语句和always语句,initial 语句它在模块中只执行一次,而always语句则不断重复执行,以下是一个比较好解释的图:

(图片来源于知乎博主罗成,画的很好很直观!)

1.1.1 initial语句

initial 语句它在模块中只执行一次

它常用于测试文件的编写,用来产生仿真测试信号(激励信号),或者用于对存储器变量赋初值。

语法格式:

initial begin
   
...
end

PS:当initial与语句内只有一个执行语句时,可以省略begin..end。

1.1.2 always语句

always 语句一直在不断地重复活动,但是只有和一定的时间控制结合在一起才有作用。

语法格式:

always @ (敏感列表) begin
    ...
end


PS:敏感列表即触发always语句执行的条件,多个触发条件用or连接。同样,当always语句内只有一个执行语句时,可以省略begin..end。


always语句可用于组合逻辑也可用于时序逻辑,电平触发往往是组合逻辑,边沿触发往往是时序逻辑,因此可进一步细分:

(1)电平触发——组合逻辑:

        这个程序表示每当信号abcdefghm的值(即电平变化)发生变化时,always块中的所有语句都会被执行 。

         如果组合逻辑块语句的输入变量很多,可简写以下形式,@(*)表示对后面语句块中所有输入变量的变化都是敏感的(即所有变量有任何一个变化就执行always语句。)

(2)边沿触发——时序逻辑:

当检测到边沿时,执行always语句,如:

posedge 和 negedge为关键字,分别表示上升沿和下降沿,这个程序表示当 sys_clk为上升沿或者 sys_ret_n为下降沿时,执行always语句。

1.1.3 实例理解

 理解:

initial语句中定义了三个信号,sys_clk,sys_rst_n, touch_key,以sys_rst_n, touch_key为例,波形图对应为第二行和第三行。以下操作只执行了一次。

  • 首先sys_rst_n, touch_key均赋值为0(程序从注释开始算第3行和第5行);
  • 代码第6行,延迟20ns后,sys_rst_n赋值为1,对应于波形图中红色线(20ns时刻)时sys_rst_n信号由低电平跳变至高电平。
  • 代码第7行,又延迟10ns后,即30ns处(蓝色线),touch_key赋值为1
  • 代码第8~10行,touch_key类似操作。

always语句则不断重复操作,每延迟10ns,sys_clk就取反一次,故波形图上(第一行)对应为产生一个周期信号。

1.2 赋值语句

1.2.1  阻塞赋值 “ = ”

        阻塞赋值可以认为只有一个步骤的操作:即计算 RHS 并更新LHS(RHS为式子右边,LHS为式子左边)。
        所谓阻塞的概念是指,在同一个always块中:后面的赋值语句是在前一句赋值语句结束后才开始赋值的。

以上例子理解:

当clk信号为上升沿或者rest_n信号处于下降沿时,执行操作。

  • 在波形图部分,从-6时刻开始,直接clk信号上升沿,且满足rest_n为低电平,执行if语句,故abc分别赋值为1,2,3。
  • 然后 -6 ~ 0时刻,rest_n恒为低电平,每到clk信号上升沿,执行操作中if语句,故-6 ~ 0时刻abc值恒为1,2,3。
  • 0时刻时,rest_n跳变为高电平,是上升沿不是下降沿,故不执行操作。
  • 0时刻后,rest_n恒为高电平,clk信号第一个上升沿为2时刻,执行操作,此时不满足if语句(rest_n为高电平),则执行else语句。由于赋值语句是在前一句赋值语句结束后才开始赋值的,故a=0执行完后,b = a = 0执行完, 在执行c = b = 0;
  • 2时刻后rest_n恒为高电平,每到clk信号上升沿,执行操作中else语句,故此后abc值都为0。

1.2.2  非阻塞赋值 “ <=”

        非阻塞赋值的操作过程可以看作两个步骤:(1)赋值开始的时候,计算RHS;(2)赋值结束的时候,更新LHS
        非阻塞赋值可以理解为:第一条非阻塞赋值语句开始时,所有非阻塞语句同时赋值计算好对应的RHS并保存放置一边,全部计算好后,再将各自保存好的RHS传递到对应的LHS中。
        非阻寒赋值只能用于对寄存器类型的变量进行赋值:因此只能用在initial块和always块等过程块中。

以上例子与1.1.1中例子只有阻塞赋值与非阻塞赋值的区别:

同理,当clk信号为上升沿或者rest_n信号处于下降沿时,执行操作。

  • 在波形图部分,从-6时刻开始,直接clk信号上升沿,且满足rest_n为低电平,执行if语句,赋值语句右边为1,2,3然后对应赋值到abc上。
  • 然后 -6 ~ 0时刻,rest_n恒为低电平,每到clk信号上升沿,执行操作中if语句,故-6 ~ 0时刻abc值恒为1,2,3。
  • 0时刻时,rest_n跳变为高电平,是上升沿不是下降沿,故不执行操作。
  • 0时刻后,rest_n恒为高电平,clk信号第一个上升沿为2时刻,执行操作,此时不满足if语句(rest_n为高电平),则执行else语句。赋值语句右边0,a(值为1),b(值为2)【由于此时ab值为临近2时刻的值】,则对应将其赋值到abc上得到a=0,b=1,c=2
  • 2时刻后rest_n恒为高电平,4时刻又到了clk信号上升沿,执行操作中else语句,。赋值语句右边0,a(值为0),b(值为1)【由于此时ab值为临近4时刻的值】,则对应将其赋值到abc上得到a=0,b=0,c=1;
  • 2时刻后rest_n恒为高电平,6时刻又到了clk信号上升沿,执行操作中else语句。赋值语句右边0,a(值为0),b(值为0)【由于此时ab值为临近6时刻的值】,则对应将其赋值到abc上得到a=0,b=0,c=0;
  • 此后,abc都为0,按照非阻塞也为0了。

1.3 条件语句

条件语句必须在过程块语句中使用,即是指由initial和always语向引导的块语句。

1.3.1 if语句

使用方法类似于C语言。

  • 允许一定形式的简写, if(a) 等同于 if(a==1);if(!a) 等同于 if(a!=1)
  • if语句对表达式的值进行判断,若为0,x,z则按假处理:若为1,按真处理:
  • if和else后面的操作语句可以用begin和end包含多个语句。
  • 允许if语句的嵌套。

1.3.2 case语句

case (<expression>)
    case1 :     ...
    case2:   ...
    case3 :     ...
    case4 :     begin
                          <multiple statements>
                        end
    default: <statement>
endcase


语法解释:

  • 其中,case1~case4这些写的是情况1~4,或者说是看<expression>符合case1~case4的哪一种情况,对应进入执行该情况;
  • default表示如果没有expression没有匹配的 case 情况,执行 default 语句;
  • 看case4知,如果情况中有多个执行语句,用begin..end来写。    

注意点:

  • 分支表达式的值互不相同(即每种情况应不同,不然编译会出现矛盾)
  • 所有表达式的位宽必须相等不能用:bx 来代替:n'bx

除此之外还有另外两种表达:casez...endcasecasex...endcase

casez不考虑表达式中的高阻值比较时;casex:不考虑高阻值之和不定值x。

例如:以下例子判定sel的值是否满足以下哪种情况,情况1是8’b1100_zzzz,其中后四位“zzzz”为高阻值不用考虑,即只需要看前4位是否与sel的值相匹配;情况2是8’b1100_xxzz,后两位才是高阻值不用考虑,看前6位是否与sel匹配。sel=1100_0011,可知前4位与情况1匹配,前6位与情况2不匹配,故进入情况1。


网站公告

今日签到

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