tag 标签: verilog语法

相关博文
  • 热度 11
    2016-1-29 17:59
    2165 次阅读|
    1 个评论
             小梅哥接触过不少的从0开始接触Verilog语法的朋友,他们对于语法的掌握基本趋近于0,对于使用Verilog描述一个逻辑模块的最基本结构都不清楚,因此这里小梅哥特地使用一个例子来介绍一下使用Verilog设计数字逻辑模块的一般结构。          本章主要讲解Verilog基础语法的内容,文章以一个最简单的例子“二选一多路器”来引入一个最简单的Verilog设计文件的基本结构。 以下为本章中例子中的代码: 01  /*======================================= 02  *   file neme : mux2.v 03  *   author :   小梅哥 04  *   Verison    :   V1.0 05  *   date       :   2015年07月01日 06  *   description: 07  *          当sel为0时,将输入端口in_a上的数据通过out端口输出, 08  *      否则将输入端口in_b上的数据通过out端口输出 09  =======================================*/ 10 11  module mux2 ( in_a , in_b , sel , out ); 12 13     input    in_a ;   //输入端口in_a 14     input in_b ; //输入端口in_b 15     16     input    sel ;    //通路选择线 17     18     output out ; //输出端口out 19 20  /*--------------------------------------------   21     当sel为0时,选择将in_a端口的数据通过out端口输出, 22     当sel为1时,选择将in_b端口的数据通过out端口输出. 23  --------------------------------------------*/    24     assign out = ( sel == 1'b0 )? in_a : in_b ; 25 26  endmodule            (注:最左侧一列行号是作者为了讲解方便加上的,不属于代码内容,请知悉) 以上代码描述的电路如下图所示: 图:二选一多路器电路模型   第1到9行:          该部分为文件头,记录了本设计文件中的一些基本信息如设计者、版本号、修改历史以及代码实现的功能描述。在编译时,该部分属于注释内容,将不被综合成任何内容。   第11行: module mux2 ( in_a , in_b , sel , out );          该行第一个字符串“module”为Verilog中的一个保留字(关键词),该保留字的出现表明了一个模块内容的开始。相对应的,第26行的“endmodule”也是一个保留字,该保留字的出现表明了一个模块内容的结束。在Verilog语法中,module和encmodule总是成对出现,module和encmodule之间的内容则是用户代码。          module之后是一个空格(或制表符“tab”),空格或制表符数量不一定限定为1个,也可以是多个,对结果没有任何影响。紧跟空格之后的mux2则是模块名称,该名称由用户自己定义,但一般要求与设计内容有一定关系,通过该名称能够体现出模块的功能或作用,例如这里使用mux2表明这是一个二选一多路器,让人一目了然。换个例子,如果要做PWM波生成模块,则可以直接以PWM作为模块名( module PWM (……); )这样简洁直观,一看就能理解模块的功能。          mux2之后使用圆括号括起来的部分则是端口列表,该列表中列出了该模块所有需要外部输入或者需要输出到外部的信号,信号间以英文中的逗号“,”隔开。端口名的命名也尽量能够代表该端口信号的实际功能或意义,例如“in_a”,很清晰的就能告诉读者这是数据输入通道的a端口。   第13行到18行: 13       input    in_a ;   //输入端口in_a 14       input in_b ; //输入端口in_b 15       16       input    sel ;    //通路选择线 17       18       output out ; //输出端口out          这里蓝色保留字“ input ”和“ output ”是端口类型,input表示该端口是本模块的输入型端口,output表示该端口是本模块的输出型端口。另外,在实际项目中,还有一种很常见的端口类型是双向端口,Verilog中用关键字“ inout ”来表示。在本例中,由于没有使用到双向端口,因此不做介绍,该部分内容将在后面以一个单独的章节来进行讲解。          第13行紧跟着“ input ”之后用方括号括起来的部分 表示端口的宽度,这里表示输入端口in_a的位宽为2。需要注意的是,定义位宽时虽然写成 和 表示的位宽是一致的,但是Verilog中习惯使用 的形式,请大家在自己写代码时也统一遵照这一要求。当位宽为1时,位宽定义部分可以省略,例如第16行的 是可以省略的,即写成 “ input sel ; ” , 与 “ input sel ; ” 效果是一样的。   第20行到23行:          该部分为注释内容,注释主要是为了方便阅读和理解代码,在综合电路时会被忽略。Verilog语法中,注释的格式与C语言中一致,也支持单行注释和块注释。单行注释以“//”开始,有效作用域只在以此符号开始的本行紧随其后的内容,换行后就不起作用了。例如第13行的“//输入端口in_a”就是注释内容,换行后第14行就不再是被注释掉的内容了。第20行到第23行为块注释,块注释以“/*    */”组织,两个“*”之间的内容即为注释内容,此种注释方式支持换行,注释内容以“/*”开始,换行后注释属性依然有效,直到出现“*/”,则注释部分结束。不过需要注意的是,块注释不支持嵌套,当强行使用这种方式时会导致编译错误。例如以下这种格式就是错误的: /*  一级块注释内容 /*  二级块注释内容 */ …… */ 第24行: 24     assign out = ( sel == 1'b0 )? in_a : in_b ;          这一行为赋值语句,这里是Verilog中最基础的一种赋值方式——连续赋值语句。这段代码的意义就是“判断括号中的条件是否成立,若成立,则将in_a的值赋给out,否则,则将in_b的值赋给out”,即通过这种方式实现了根据条件选择通道的功能。其中,“?”之后“:”之前的信号为括号中条件满足时需要赋给out的的源信号,“:”之后的信号则是括号中条件不满足时需要赋给out的源信号。此种赋值方式还支持多重选择,例如下面的代码: assign out = ( sel1 == 1'b0 )? in_a : ( sel2 == 1 )? in_b : in_c ;            这里有三个数据输入端口“in_a , in_b , in_c”,两个选择输入端口“sel1 , sel2”。该句话的意思就是,当第一个括号中的条件满足时,则将in_a的数据赋值给out,否则再判断第二个括号中的条件是否满足,满足则将in_b的值赋给out,不满足则将in_c的值赋给out。括号中的内容可以直接简写为(!sel1)或者(!sel2),这一点与C语言一样。            通过这一章,我们学习了采用Verilog HDL设计简单的模块的基本代码结构,包括声明模块的”module   endmodule”、端口的定义、端口类型以及位宽的声明、注释内容的格式以及连续赋值语句的写法,而且通过此例子完成了一个简单的二选一多路器的设计。希望对于Verilog HDL语法零基础的读者在看罢此文后能够关闭此文档,在Quartus II或Modelsim、ISE等工具中实际动手敲一遍。可结合前面介绍Altera 公司FPGA器件开发流程一章中的步骤,实际建立工程并输入代码,然后分析和综合,通过综合工具,查看自己编写的代码是否有错误,如有错误,则根据软件提示加以改正。          下一章,将以此模块的测试文件(testbench)为例,介绍Testbench的基本语法。     如有更多问题,欢迎留言,也可加入技术支持群一起交流学习。群号:472607506   小梅哥 芯航线电子工作室    
  • 热度 13
    2014-4-23 16:00
    8845 次阅读|
    1 个评论
    以前很少用for语句,以为是不可综合的。在印象中好像只用循环生成(for...generate)语句综合过多个模块。最近在仔细研读《轻松成为设计高手——Verilog HDL使用精解》这本书,才知道for语句在组合逻辑中大有用处。   (1)for用在纯组合逻辑中 举例:4位左移器(将低4位输入的数移到高4位) //Leftshift for 4 bits module For_Leftshift( input wire inp, input wire L_EN, output reg result );   integer i; always@(inp or L_EN) begin  result = 0;  result = inp;  if(L_EN == 1)  begin   for(i=4;i=7;i=i+1)   begin    result = result ;   end   result = 0;  end end   endmodule   综合结果(RTL视图,实际是一个4位选择器) (2)for不仅可以用在组合逻辑中,而且还可以用在时序逻辑中,用于在1个周期类完成整个for循环。 举例:在一个周期类完成对输入总线中高电平位的计数,则利用for循环实现加法器 module For_Counter( input wire clk, input wire rst_n, input wire data, output wire numout ); integer i; reg num;   always @(posedge clk)  begin  if(!rst_n)   num = 0;  else   begin   for(i=0;i13;i=i+1)    if(data ) num = num + 1;   end  end   assign numout = num;   endmodule   综合结果(RTL视图,加法器+触发器)     综上,可以看出for循环是可以综合的,而且效率很高。但所消耗的逻辑资源较大。在对速度(时钟周期数)要求不是很高的情况下,可以多用几个时钟周期完成任务,而没有必要用for循环来做。  
  • 热度 19
    2014-3-5 10:05
    1539 次阅读|
    4 个评论
        FPGA有两种硬件描述语言,一个是Verilog,一个是VHDL。Verilog适合系统级,算法级,寄存器传输级,逻辑级,门级和开关电路级的设计,而VHDL更适合特大型的系统级设计。由于一开始接触的是Verilog,所以学的是verilog。     #程序实例参考手边三本书   1、标准模版 module模块名(模块端口列表) 声明语句 模块组项 函数和任务定义 Specify块 endmodule *这里要分清模块和单元的概念,单元组成模块,有些小的单元(像以后会写到的阻塞赋值,8位乘法器这样的)是有自己的定义的,像时钟产生器这种能当作系统的子单元的才能算模块。(个人理解,有错提出必改正!)   2、声明语句 Parameter:参数声明语句 Input:输入端口声明语句 Output:输出端口声明语句 net-type:连线类型变量声明语句 register-type:寄存器类型变量声明语句 time:时间型变量声明语句 integer:整数类型变量声明语句 real:实数类型变量声明语句 event:有名事件声明语句 *声明语句,用在开头   3,模块(也称建模):行为描述模块,数据流描述模块,结构描述模块 (1),数据流描述方式            主要用assign连续赋值语句,包括显式和隐式两种。 显式:连线型变量类型 连线型变量名            assign#(延时量)连线型变量名=赋值表达式 隐式:连线型变量类型(赋值驱动强度) #(延时量)连线型变量名=赋值表达式 #assign连续赋值语句的功能:当赋值表达式中变量的值发生变化时,重新计算赋值表达式的值,并在指定时延后将所得到的结果赋给左端的连线型变量。 (时延定义了右边的表达式操作数变化时间对左边变量进行赋值时刻之间的延迟时间,如果没有定义,缺省时延为0。) 例:与非门的数据流描述 module NAND(a,b,out); input a,b; output out; assign out=~(ab);//连续赋值语句 endmodule *类似于C中的顺序结构吧,但是概念上要广一点,打个不恰当的比方就是:一条流水线,正常运转条件在监控下一点事都没有,一但出了问题赶快采取措施。 (2),行为描述方式 行为语句:initial 语句:只执行一次                   always语句:循环执行 (只有寄存器类型数据能在这两种语句中被赋值!!寄存器类型数据在被赋值之前保持原有的数值不变。所有的initial和always语句在0时刻并发执行。) 例:1位加法器(有个长的好像@的符号打不出来,暂时就用@代替了) module ADDER(a,b,cin,sum,cout); input a,b,cin; output sum,cout; reg sum,cout; reg t1,t2,t3; always @(a or b or cin) sum=(a^b)^cin; t1=acin; t2=bcin; t3=ab; cout=(t1|t2)|t3; end endmodule *类似于C中的循环结构,一但不满足条件就跳出循环! (3),结构描述方式(通过实例进行描述) 把预定义里面的基本元件实例放到语言中,监控实例的输入变化,一旦改变就重新运算并输出相应的值。 四种可用来描述的部件:用户自定义的模块/用户自定义的UDP/内置门级元件/内置开关级元件 例:由两个NAND生成的与门结构型描述 module AND(a,b,out); input a,b; output out; wire w1;                           //内部连线w1连接两个模块 NAND NAND1(a,b,w1);     //实例1 NAND NAD2(w1,w1,out);//实例2 endmodule *前两种都没有这种直观,这种直接举例说明,把例子引入程序中,如果是正确的就继续,错误的就跳出来。 (4)混合型描述方式 略 *听名字就知道是三种杂糅了。。= =   4,数据类型 (1),物理数据类型 连线型数据    (连线型数据不能储存值,而且必须要受到驱动器或者assign语句的驱动,如果没有驱动,变量为高阻z) 常用变量:wire(单个门驱动或assign驱动),tri(多驱动)  寄存器型数据 (数据存储单元的抽象) 关键字reg(缺省是为1位数) #reg常用来表示触发器,always内被赋值的每一个信号都必须定义成reg型。 memory型数据(通过扩展reg型数据的地址范围生成) 建立存储器数据的格式: //存储器的例子 parameter             //实时参数 wordsize=16; memsize=256; reg mem ; writereg,readreg; *之前已经说过FPGA的概念了,wire用来连接单元和模块,寄存器用来存储信息,驱动器用来驱动硬件,触发器相当于一个使能端。 (2)抽象数据类型(纯数学的抽象描述,不与任何实际的物理硬件电路相对应) interger整型,time时间型(常与系统函数$time一起使用),real实型(可用于对延迟时间的计算),parameter参数型。 #以上两种数据的变量在定义时两端需设置位宽,当为缺省状态时位宽默认为1位。变量的每一位可以取0,1,x或z中任意值。 (x表示一个未知初始状态的变量或者由于多个驱动源试图将其设定为不同的值而引起的冲突(FPGA的时序问题),z表示高阻状态或者浮空量)  
相关资源