FPGA从零开始-Verilog语法学习笔记(一)

来源:互联网 发布:微信偷窥软件 编辑:程序博客网 时间:2024/05/21 10:25

 

前言: 学了这么多年的知识,却从未有真正的来整理过自己的资料,以至于学过就忘,多而不精,经过这些年的教训,顿觉有总结之必要,写博客也不失为一种很好的查漏补缺的好方法。

 

一、组合电路的verilog 描述

1.模块表达

module 模块名(模块端口名)

模块端口和模块功能描述。

endmodule 

//注意 moduleendmodule旁边都不加标点;所有的关键字都必须小写

2 .端口语句、端口信号名和端口模式

端口定义关键字:inputoutputinout(从端口的内部看,可以对端口进行赋值或者通过此端口读出外部的数据信息;从端口的外部看,信号既可以从端口流出也可以想此端口输入信号,如RaM的数据口、单片机的i/o口等)

input 端口名1,端口2-----

若描述一个多信号端口或者总线端口

output [3:0] c,d;//表示一个四个位宽的输出信号

3 .赋值语句和条件操作符

关键词assign 可以引导不同的幅值语句

assign y = a;//将信号a赋给y

assign y = a & b; 

4 .关键字 (moduleinputoutput) 

5 . 标识符(identifier)

设计者在程序中自定义的,用以标志不同的名称

6.文件的存取和读盘

一般情况下,文件名可由用户任意指定,但文件的后缀扩展名必须是 .v

,考虑到调用的方便性,建议程序的文件名与模块名相同。

二、几种简单的语法分析

举例:

a,b,cd 为四个输入端口,y为输出端口;

s1s0取值为00011011时,输出端y分别是来自a,b,c,d的数据

                 

  图一  四选一多路选择器

、程序:

module MUX41a(a,b,c,d.s0.s1,y)

input a,b,c,d;

intput s0,s1;

output y;   //3行为电路模块端口说明和定义段

 reg  y;//信号类型定义段

always @(a or b or c or d or s1 or s0)//任何一个信号变化都会起动过程语句的执行

    begin :MUX41//MUX41为块名,不参与编译,故可省略

    case({s1,s0})

2’b00:y<=a;

2’b01:y<=b;

2’b10:y<=c;

2’b11:y<=d;

default: y<=a;

endcase

       end      //电路模块功能描述段

endmodule

补充说明:

1module endmodule 引出完整的电路描述

2intput output 引出对模块的外部端口描述

3)以 reg等关键词定义模块内将出现的相关信号的特性和数据类型

4always@等关键词引导对模块的对模块逻辑功能的语句

2、代码语法详解

1) reg型变量定义

reg是定义寄存器类型变量的关键字,如:reg  变量1,变量2---

reg  [msb:lsb]变量1,变量2---

注意:verilog中常用变量为寄存器变量(用reg定义)和网线型变量(wire定义),默认情况为NET型,但是always@引导的顺序语句必须规定为reg型变量。

2)过程语句

   always@(敏感信号及敏感信号列表或表达式)包括块语句和各类顺序语句

//注意通常要求所有的敏感信号都写在括号内,但是不写也只会报警告信息,所以试图选择性的列入敏感信号来改变逻辑设计是无效的。

3)块语句begin_end

相当于一个括号,仅限于always@引导的过程语句之中使用,通常用它来组合顺序语句,故称其为顺序块。常用格式为:

begin[:块名]

语句1;语句二;.....;语句n;

end

4)case 条件语句和四种逻辑状态

   条件语句的条件值和标识符数据类型必须匹配;‚允许出现多个分支取值同时满足case表达式的情况,这种情况先处理最先满足表达式的分支项,随后跳出case语句;ƒ如果条件选择取值能覆盖所有的case语句表达式的值,必须使用default语句。

备注:四种逻辑状态的取值

  .0 含义四个 : 二进制 0、低电平、逻辑 0、事件为伪;

  .1 含义四个 : 二进制 1、高电平、逻辑 1、事件为真;

  .zZ  高阻态 或 高阻值;

  . X 或 不确定或未知的逻辑状态

//casezcasex 对应以上的后两种状态

5)并位操作和数字表达

{a1,b1,2{a2,b2}} = {a1,b1,{a2,b2},{a2,b2}}={a1,b1,a2,b2,a2,b2}

例如,表示一个二进制的一般格式如下:

   <位宽>´<进制><数字>

2.   4四选多路选择器及其数据流描述方式

源代码:

module MUX41aa,b,c,d,s1,s0,y

input a,b,c,d,s1,s0;

out put y;

wire [1:0] SEL;

wire AT,BT,CT,DT;

assign SEL = {s1,s0}; //assign 定义的变量必须是网线型的

assign AT = {SEL = = 2’D0};

assign BT = {SEL = = 2’D1};

assign CT = {SEL = = 2’D2};

assign DT = {SEL = = 2’D3};

assign y =(a & AT) | (b & BT) |(c & CT) | (d & DT);

endmodule

源码语法分析:

1)按位逻辑操作符

如:A = 1’b0 , B = 1’b0 => A ~ ^B = 1’b0//其中~^为同或信号

2)等式操作符

=s = = //全等        != =//不全等

3)assign 连续赋值语句

上例中采用的是并行语句加纯布尔函数的表达式实现模块的功能,属于所谓的数据流描述方式,通常使用连续赋值语句来描述输入与输出之间的逻辑关系。

连续赋值语句的基本格式如下:

assign 目标变量名 驱动表达式;

assign DOUT = a & b;//a,b为显示的敏感信号,此语句被执行一次的唯一条件是此语句右边的变量发生变化。

注意一个wire变量不允许有多个时钟源,如下描述是不允许 的,如:

assign DOUT = a & b | c;   assign DOUT = a & b | f;

4)wire 定义网线型变量

    用wire定义的网线型变量可以在任何类型的表达式或赋值语句中(包括连续赋值或者过程赋值)用作输入信号,也可在连续赋值语句或试题元件例化中用作输出信号。

此外还可以用wire语句代替assign语句:

module MUX41a(A,B,C,D,s1,s0,y)

input A,B,C,D,s1,s0,y;

output y;

wire AT = s0 ? D : C ;

wire BT = s0 ? B : A ;

wire Y = (s1 ?  AT  : BT );

endmodule 

  图二   RTL 图

3  选一 及if 语句描述方式

module MUX41a (A,B,C,D,s1,s0,y)

input A,B,C,D,s1,s0;

output y;

reg[1 :0] SEL;

reg y;

always @(A,B,C,D,SEL) begin //快语句开始

SEL = {s1,s0}

if(SEL = = 0) y = A ;

else if (SEl = =  1) y = B;

else if (SEL = =  2) y = C ;

else y = D ;

end     //快语句结束

endmodule

源码分析:

1)if _ else 语句

   和C语言类似这里不做强调

2)过程赋值语句

 阻塞式赋值 

  “=” 如果在一个块语句中有多条阻塞式赋值语句时,当执行到其中某条赋值语句时,其他语句被顺序执行。assign 语句和always 语句出现的等号理论上是不同的,因为前者属于连续类赋值语句,具有并行执行赋值特性;后者属于过程赋值中的顺序赋值语句。但从综合效果的角度看其结果是相同点,因为assign语句不能使用块语句,故只允许引导一条含有“=”符号的赋值语句;而assign语句作为并行语句的限制下,即使其中的语句具有顺序执行功能,也无从发挥。

 ‚非阻塞式赋值

<=”,必须在快语句执行结束后才整完成赋值。

3)数据表示方式

数据的自动匹配:

例如:wire Y = 9 ; Y<= 9;//不报错,verilog会截掉高位赋给Y

Y的值为 2’b01

注意:verlog 的语法比VHDL更宽松适合初学者入门,但正因为如此,程序设计更要小心,其排错查错时可能要花费更多的力气。