集成电路技术分享

 找回密码
 我要注册

QQ登录

只需一步,快速开始

搜索
楼主: 小舍YZ

FPGA开发 实用内容

[复制链接]
 楼主| 小舍YZ 发表于 2017-4-5 12:11:19 | 显示全部楼层
2.3.5 运算符和表达式

在Verilog HDL语言中运算符所带的操作数是不同的,按其所带操作数的个数可以分为三种:
•        单目运算符:带一个操作数,且放在运算符的右边。
•        双目运算符:带两个操作数,且放在运算符的两边。
•        三目运算符:带三个操作数,且被运算符间隔开。
Verilog HDL语言参考了C语言中大多数算符的语法和句义,运算范围很广,其运算符按其功能分为下列9类:

1. 基本算术运算符

在Verilog HDL中,算术运算符又称为二进制运算符,有下列5种:
•        + 加法运算符或正值运算符,如:s1+s2; +5;
•        - 减法运算符或负值运算符,如:s1-s2; -5;
•        * 乘法运算符,如s1*5;
•        / 除法运算符,如s1/5;
•        % 模运算符,如s1%2;
在进行整数除法时,结果值要略去小数部分。在取模运算时,结果的符号位和模运算第一个操作数的符号位保持一致。例如:
运算表达式 结果 说明

12.5/3 4 结果为4,小数部分省去
12%4 0 整除,余数为0
-15%2 -1 结果取第一个数的符号,所以余数为-1
13/-3 1 结果取第一个数的符号,所以余数为1

注意:在进行基本算术运算时,如果某一操作数有不确定的值X,则运算结果也是不确定值X。

2. 赋值运算符

赋值运算分为连续赋值和过程赋值两种。

(1)连续赋值

连续赋值语句和过程块一样也是一种行为描述语句,有的文献中将其称为数据流描述形式,但本书将其视为一种行为描述语句。

连续赋值语句只能用来对线网型变量进行赋值,而不能对寄存器变量进行赋值,其基本的语法格式为:

        线网型变量类型 [线网型变量位宽] 线网型变量名;
        assign #(延时量) 线网型变量名 = 赋值表达式;
        例如:
        wire a;
        assign a = 1'b1;

一个线网型变量一旦被连续赋值语句赋值之后,赋值语句右端赋值表达式的值将持续对被赋值变量产生连续驱动。只要右端表达式任一个操作数的值发生变化,就会立即触发对被赋值变量的更新操作。

在实际使用中,连续赋值语句有下列几种应用:
•        对标量线网型赋值
wire a, b;
assign a = b;
•        对矢量线网型赋值
wire [7:0] a, b;
assign a = b;
•        对矢量线网型中的某一位赋值
wire [7:0] a, b;
assign a[3] = b[1];
•        对矢量线网型中的某几位赋值
wire [7:0] a, b;
assign a[3:0] = b[3:0];
•        对任意拼接的线网型赋值
wire a, b;
wire [1:0] c;
assign c ={a ,b};
(2)过程赋值

过程赋值主要用于两种结构化模块(initial模块和always模块)中的赋值语句。在过程块中只能使用过程赋值语句(不能在过程块中出现连续赋值语句),同时过程赋值语句也只能用在过程赋值模块中。

过程赋值语句的基本格式为:

<被赋值变量><赋值操作符><赋值表达式>

其中,<赋值操作符>是“=”或“<=”,它分别代表了阻塞赋值和非阻塞赋值类型。3.5.1节对阻塞赋值和非阻塞赋值操作进行了详细解释。

过程赋值语句只能对寄存器类型的变量(reg、integer、real和time)进行操作,经过赋值后,上面这些变量的取值将保持不变,直到另一条赋值语句对变量重新赋值为止。过程赋值操作的具体目标可以是:
&#8226;        reg、integer、real和time型变量(矢量和标量);
&#8226;        上述变量的一位或几位;
&#8226;        上述变量用{}操作符所组成的矢量;
&#8226;        存储器类型,只能对指定地址单元的整个字进行赋值,不能对其中某些位单独赋值。
例2-2 给出一个过程赋值的例子。
reg c;
always @(a)
    begin
    c = 1'b0;
end
3. 关系运算符

关系运算符总共有以下8种:
&#8226;        > 大于
&#8226;        >= 大于等于
&#8226;        < 小于
&#8226;        <= 小于等于
&#8226;        == 逻辑相等
&#8226;        != 逻辑不相等
&#8226;        === 实例相等
&#8226;        !== 实例不相等
在进行关系运算符时,如果操作数之间的关系成立,返回值为1;关系不成立,则返回值为0;若某一个操作数的值不定,则关系是模糊的,返回的是不定值X。

实例算子“===”和“!==”可以比较含有X和Z的操作数,在模块的功能仿真中有着广泛的应用。所有的关系运算符有着相同优先级,但低于算术运算符的优先级。

4. 逻辑运算符

Verilog HDL中有3类逻辑运算符:
&#8226;        && 逻辑与  
&#8226;         || 逻辑或
&#8226;        ! 逻辑非
其中“&&”和“||”是二目运算符,要求有两个操作数;而“!”是单目运算符,只要求一个操作数。“&&”和“||”的优先级高于算术运算符。逻辑运算符的真值表如下表所示:

表2-2 逻辑运算符的真值表

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?我要注册

x
 楼主| 小舍YZ 发表于 2017-4-5 12:11:41 | 显示全部楼层
5. 条件运算符

条件运算符的格式如下:

         y = x ? a : b;
        条件运算符有3个操作数,若第一个操作数y = x是True,算子返回第二个操作数a,否则返回第三个操作数b。
        如:
        wire y;
        assign y = (s1 == 1) ? a : b;
        嵌套的条件运算符可以实现多路选择。如:
        wire [1:0] s;
        assign s = (a >=2 ) ? 1 : (a < 0) ? 2: 0;
        //当a >=2时,s=1;当a <0时,s=2;在其余情况,s=0。

6. 位运算符

作为一种针对数字电路的硬件描述语言,Verilog HDL用位运算来描述电路信号中的与、或以及非操作,总共有7种位逻辑运算符:
&#8226;         ~ 非
&#8226;        & 与
&#8226;        | 或
&#8226;        ^ 异或
&#8226;        ^~ 同或
&#8226;        ~& 与非
&#8226;        ~| 或非
位运算符中除了“~”,都是二目运算符。位运算对其自变量的每一位进行操作,例如:s1&s2的含义就是s1和s2的对应位相与。如果两个操作数的长度不相等的话,将会对较短的数高位补零,然后进行对应位运算,使输出结果的长度与位宽较长的操作数长度保持一致。例如:

s1 = ~s1;
var = ce1 & ce2;

7. 移位运算符

移位运算符只有两种:“<<”(左移)和“>>”(右移),左移一位相当于乘2,右移一位相当于除2。其使用格式为:
s1 << N; 或 s1 >>N

其含义是将第一个操作数s1向左(右)移位,所移动的位数由第二个操作数N来决定,且都用0来填补移出的空位。

在实际运算中,经常通过不同移位数的组合来计算简单的乘法和除法。例如s1*20,因为20=16+4,所以可以通过s1<<4+s1<<2来实现。

8. 拼接运算符

拼接运算符可以将两个或更多个信号的某些位并接起来进行运算操作。其使用格式为:

{s1, s2, … , sn}

将某些信号的某些位详细地列出来,中间用逗号隔开,最后用一个大括号表示一个整体信号。

在工程实际中,拼接运算受到了广泛使用,特别是在描述移位寄存器时。

 楼主| 小舍YZ 发表于 2017-4-5 12:11:59 | 显示全部楼层
例2-3 给出拼接符的Verilog实例
reg [15:0] shiftreg;
always @( posedge clk)
shiftreg [15:0] <= {shiftreg [14:0], data_in};

9. 一元约简运算符

一元约简运算符是单目运算符,其运算规则类似于位运算符中的与、或、非,但其运算过程不同。约简运算符对单个操作数进行运算,最后返回一位数,其运算过程为:首先将操作数的第一位和第二位进行与、或、非运算;然后再将运算结果和第三位进行与、或、非运算;依次类推直至最后一位。

常用的约简运算符的关键字和位操作符关键字一样,仅仅由单目运算和双目运算的区别。

例2-4 给出一元简约运算符的Verilog实例

reg [3:0] s1;
reg s2;
s2 = &s1; //&即为一元约简运算符“与”


 楼主| 小舍YZ 发表于 2017-4-5 12:13:06 | 显示全部楼层

4节 Verilog HDL语言的描述语句

2.4.1 结构描述形式

通过实例进行描述的方法,将Verilog HDL预先定义的基本单元实例嵌入到代码中,监控实例的输入。Verilog HDL中定义了26个有关门级的关键字,比较常用的有8个。在实际工程中,简单的逻辑电路由逻辑门和开关组成,通过门元语可以直观地描述其结构。

基本的门类型关键字如下所述:
&#8226;        and
&#8226;        nand
&#8226;        nor
&#8226;        or
&#8226;        xor
&#8226;        xnor
&#8226;        buf
&#8226;        not
        Verilog HDL支持的基本逻辑部件是由该基本逻辑器件的原语提供的。其调用格式为:
        门类型 <实例名> (输出,输入1,输入2,……,输入N)

        例如,nand na01(na_out, a, b, c );
        表示一个名字为na01的与非门,输出为na_out,输入为a, b, c。

        例2-5 一个简单的全加器例子:

module ADD(A, B, Cin, Sum, Cout);
        input A, B, Cin;
        output Sum, Cout;
        // 声明变量
        wire S1, T1, T2, T3;

        xor X1 (S1, A, B),
              X2 (Sum, S1, Cin);

        and A1 (T3, A, B),
                A2 (T2, B, Cin),
                A3 (T1, A, Cin);

         or O1 (Cout, T1, T2, T3);
endmodule


在这一实例中,模块包含门的实例语句,也就是包含内置门xor、and和or的实例语句。门实例由线网型变量S1、T1、T2和T3互连。由于未指定顺序,门实例语句可以以任何顺序出现。

门级描述本质上也是一种结构网表。在实际中的使用方式为:先使用门逻辑构成常用的触发器、选择器、加法器等模块,再利用已经设计的模块构成更高一层的模块,依次重复几次,便可以构成一些结构复杂的电路。其缺点是:不易管理,难度较大且需要一定的资源积累。

 楼主| 小舍YZ 发表于 2017-4-5 12:14:13 | 显示全部楼层
2.4.2 数据流描述形式

数据流型描述一般都采用assign连续赋值语句来实现,主要用于实现组合功能。连续赋值语句右边所有的变量受持续监控,只要这些变量有一个发生变化,整个表达式被重新赋值给左端。这种方法只能用于实现组合逻辑电路。其格式如下:

        assign L_s = R_s;

例2-6 一个利用数据流描述的移位器

module mlshift2(a, b);
        input a;
        output b;

        assign b = a<<2;
endmodule

在上述模块中,只要a的值发生变化,b就会被重新赋值,所赋值为a左移两位后的值。

2.4.3 行为描述形式

行为型描述主要包括过程结构、语句块、时序控制、流控制等4个方面,主要用于时序逻辑功能的实现。

1.过程结构

过程结构采用下面4种过程模块来实现,具有强的通用型和有效性。
&#8226;        initial模块
&#8226;        always模块
&#8226;        任务(task)模块
&#8226;         函数(function)模块
一个程序可以有多个initial模块、always模块、task模块和function模块。initial模块和always模块都是同时并行执行的,区别在于initial模块只执行一次,而always模块则是不断重复地运行。另外,task模块和function模块能被多次调用,其具体使用方法可参见3.5.3节的专题。
(1)initial 模块

在进行仿真时,一个initial模块从模拟0时刻开始执行,且在仿真过程中只执行一次,在执行完一次后,该initial就被挂起,不再执行。如果仿真中有两个initial模块,则同时从0时刻开始并行执行。
        
initial模块是面向仿真的,是不可综合的,通常被用来描述测试模块的初始化、监视、波形生成等功能。其格式为:

initial begin/fork
        块内变量说明
        时序控制1 行为语句1;
        ……
        时序控制n 行为语句n;
end/join

其中,begin……end块定义语句中的语句是串行执行的,而fork……join块语句中的语句定义是并行执行的。当块内只有一条语句且不需要定义局部变量时,可以省略begin……end/ fork……join。

例2-7 下面给出一个initial模块的实例。

initial begin
        // 初始化输入向量
        clk = 0;
        ar = 0;
        ai = 0;
        br = 0;
        bi = 0;
        // 等待100ns,全局reset信号有效
        #100;
        ar = 20;
        ai = 10;
        br = 10;
        bi = 10;
end

(2)always 模块

和initial模块不同,always模块是一直重复执行的,并且可被综合。always过程块由always过程语句和语句块组成的,其格式为:

always @ (敏感事件列表) begin/fork
        块内变量说明
        时序控制1 行为语句1;
        ……
        时序控制n 行为语句n;
end/join

其中,begin……end/fork……join的使用方法和initial模块中的一样。敏感事件列表是可选项,但在实际工程中却很常用,而且是比较容易出错的地方。敏感事件表的目的就是触发always模块的运行,而initial后面是不允许有敏感事件表的。

敏感事件表由一个或多个事件表达式构成,事件表达式就是模块启动的条件。当存在多个事件表达式时,要使用关键词or将多个触发条件结合起来。Verilog HDL的语法规定:对于这些表达式所代表的多个触发条件,只要有一个成立,就可以启动块内语句的执行。例如,在语句

        always@ (a or b or c) begin
       ……
end

中,always过程块的多个事件表达式所代表的触发条件是:只要a、b、c信号的电平有任意一个发生变化,begin……end语句就会被触发。
always模块主要是对硬件功能的的行为进行描述,可以实现锁存器和触发器,也可以用来实现组合逻辑。利用always实现组合逻辑时,要将所有的信号放进敏感列表,而实现时序逻辑时却不一定要将所有的结果放进敏感信号列表。敏感信号列表未包含所有输入的情况称为不完整事件说明,有时可能会引起综合器的误解,产生许多意想不到的结果。

 楼主| 小舍YZ 发表于 2017-4-5 12:32:57 | 显示全部楼层
例2-8 下例给出敏感事件未包含所有输入信号的情况

module and3(f, a, b, c);
        input a, b, c;
        output f;
        reg f;

        always @(a or b )begin
        f = a & b & c;
        end
endmodule

其中,由于c不在敏感变量列表中,所以当c值变化时,不会重新计算f值。所以上面的程序并不能实现3输入的与门功能行为。正确的3输入与门应当采用下面的表述形式。

module and3(f, a, b, c);
        input a, b, c;
        output f;
        reg f;

        always @(a or b or c )begin
                       f = a & b & c;
        end
endmodule

2.语句块

语句块就是在initial或always模块中位于begin……end/fork……join块定义语句之间的一组行为语句。语句块可以有个名字,写在块定义语句的第一个关键字之后,即begin或fork之后,可以唯一地标识出某一语句块。如果有了块名字,则该语句块被称为一个有名块。在有名块内部可以定义内部寄存器变量,且可以使用“disable”中断语句中断。块名提供了唯一标识寄存器的一种方法。

例2-9 语句块使用例子

always @ (a or b )
begin : adder1
         c = a + b;
end

定义了一个名为adder1的语句块,实现输入数据的相加。

按照界定不同分为两种:

(1)begin……end,用来组合需要顺序执行的语句,被称为串行块。例如:

parameter d = 50;
reg[7:0] r;
begin //由一系列延迟产生的波形
         # d r = ' h35 ; //语句1
         # d r = ' hE2 ; //语句2
         # d r = ' h00 ; //语句3
         # d r = ' hF7 ; //语句4
         # d –> end_wave; //语句5,触发事件end_wave
end
串行块的执行特点如下:
&#8226;        串行块内的各条语句是按它们在块内的语句逐次逐条顺序执行的,当前一条执行完之后,才能执行下一条。如上例中语句1至语句5是顺序执行的。
&#8226;        块内每一条语句中的延时控制都是相对于前一条语句结束时刻的延时控制。如上例中语句2的时延为2d。
&#8226;        在进行仿真时,整个语句块总的执行时间等于所有语句执行时间之和。如上例中语句块中总的执行时间为5d。
(2)fork……join,用来组合需要并行执行的语句,被称为并行块。例如:

parameter d = 50;
reg[7:0] r;
fork //由一系列延迟产生的波形
     # d r = ' h35 ; //语句1
     # 2d r = ' hE2 ; //语句2
     # 3d r = ' h00 ; //语句3
     # 4d r = ' hF7 ; //语句4
     # 5d –> end_wave; //语句5,触发事件end_wave
   join

并行块的执行特点为:
&#8226;        并行语句块内各条语句是各自独立地同时开始执行的,各条语句的起始执行时间都等于程序流程进入该语句块的时间。如上例中语句2并不需要等语句1执行完才开始执行,它与语句1是同时开始的。
&#8226;        块内每一条语句中的延时控制都是相对于程序流程进入该语句块的时间而言的。如上例中语句2的延时为2d。
&#8226;        在进行仿真时,整个语句块总的执行时间等于执行时间最长的那条语句所需要的执行时间,如上例中整个语句块的执行时间为5d。

(3)混合使用

在分别对串行块和并行块进行了介绍之后,还需要讨论一下二者的混合使用。混合使用可以分为下面两种情况。
&#8226;        串行块和并行块分别属于不同的过程块时,串行块和并行块是并行执行的。例如一个串行块和并行块分别存在于两个initial过程块中,由于这两个过程块是并行执行的,所以其中所包含的串行语句和并行语句也是同时并行执行的。在串行块内部,其语句是串行执行的;在并行块内部,其语句是并行执行的。
&#8226;        当串行块和并行块嵌套在同一过程块中时,内层语句可以看作是外层语句块中的一条普通语句,内层语句块什么时候得到执行是由外层语句块的规则决定的;而在内层语句块开始执行时,其内部语句怎么执行就要遵守内层语句块的规则。

 楼主| 小舍YZ 发表于 2017-4-5 12:34:02 | 显示全部楼层
3.时序控制

Verilog HDL提供了两种类型的显示时序控制,一种是延迟控制,在这种类型的时序控制中通过表达式定义开始遇到这一语句和真正执行这一语句之间的延迟时间。另外一种是事件控制,这种时序控制是通过表达式来完成的,只有当某一事件发生时才允许语句继续向下执行。

(1)延时控制

        延时控制的语法如下:
        # 延时数 表达式;
        延时控制表示在语句执行前的“等待时延”,下面给出一个例子:
initial
begin
      #5 clk = ~clk;
end

延时控制只能在仿真中使用,是不可综合的。在综合时,所有的延时控制都会被忽略。

(2)事件控制

事件控制分为两种:边沿触发事件控制和电平触发事件控制。
&#8226;        边沿触发事件是指指定信号的边沿信号跳变时发生指定的行为,分为信号的上升沿和下降沿控制。上升沿用posedge关键字来描述,下降沿用negedge关键字描述。边沿触发事件控制的语法格式为:
第一种:@(<边沿触发事件>) 行为语句;
第二种:@(<边沿触发事件1> or <边沿触发事件2> or …… or <边沿触发事件n>) 行为语句;

例2-10 边沿触发事件计数器
reg [4:0] cnt;
always @(posedge clk) begin
     if (reset)
         cnt <= 0;
     else
         cnt <= cnt +1;
end

        上面这个例子表明:只要clk信号有上升沿,那么cnt信号就会加1,完成计数的功能。这种边沿计数器在同步分频电路中有着广泛的应用。
&#8226;        电平敏感事件是指指定信号的电平发生变化时发生指定的行为。下面是电平触发事件控制的语法和实例:
第一种:@(<电平触发事件>) 行为语句;
第二种:@(<电平触发事件1> or <电平触发事件2> or …… or <电平触发事件n>) 行为语句;

例2-11 电平沿触发计数器
reg [4:0] cnt;
always @(a or b or c) begin
      if (reset)
          cnt <= 0;
      else
          cnt <= cnt +1;
      end

        其中,只要a,b,c信号的电平有变化,信号cnt的值就会加1,这可以用于记录a,b,c变化的次数。

4.流控制

流控制语句包括3类,即跳转、分支和循环语句。

(1)if语句
        if语句的语法如下:

if (条件1)
     语句块1
else if (条件2)
     语句块2
……
else
     语句块n

如果条件1的表达式为真(或非0值),那么语句块1被执行,否则语句块不被执行,然后依次判断条件2至条件n是否满足,如果满足就执行相应的语句块,最后跳出if语句,整个模块结束。如果所有的条件都不满足,则执行最后一个else分支。在应用中,else if分支的语句数目由实际情况决定;else分支也可以缺省,但会产生一些不可预料的结果,生成本不期望的锁存器。

例2-12 下面给出一个if语句的例子,并说明省略else分支所产生的一些结果。

always @(a1 or b1)
begin
     if (a1) q<= d;
end

if语句只能保证当a1=1时,q才取d的值,但程序没有给出a1=0时的结果。因此在缺少else语句的情况下,即使a1=0时,q的值会保持a1=1的原值,这就综合成了一个锁存器。

如果希望a1=0时,q的值为0或者其他值,那么else分支是必不可少的。下面给出a1=0,q=0的设计:

always @(a1 or b1)
begin
       if (a1) q <= d;
       else q <= 0;
end
(2)case语句

case语句是一个多路条件分支形式,其用法和C语言的csae语句是一样的。

下面给出一个case语句的例子:

reg [2:0] cnt;
case (cnt)
           3'b000: q = q + 1;
           3'b001: q = q + 2;
           3'b010: q = q + 3;
           3'b011: q = q + 4;
           3'b100: q = q + 5;
           3'b101: q = q + 6;
           3'b110: q = q + 7;
           3'b111: q = q + 8;
           default: q <= q+ 1;
endcase

需要指出的是,case语句的default分支虽然可以缺省,但是一般不要缺省,否则会和if语句中缺少else分支一样,生成锁存器。


 楼主| 小舍YZ 发表于 2017-4-5 12:34:32 | 显示全部楼层
例2-13 给出case语句的Verilog实例

always @(a1[1:0] or b1)
begin
      case (a1)
      2'b00: q <= b'1;
      2'b01: q <= b'1 + 1;
end

这样就会生成锁存器。一般为了使case语句可控,都需要加上default选项。

always @(a1[1:0] or b1)
begin
        case (a1)
        2'b00: q <= b1;
        2'b01: q <= b1 + 1;
        default: q <= b1 + 2;
end

在实际开发中,要避免生成锁存器的错误。如果用if语句,最好写上else选项;如果用case语句,最好写上default项。遵循上面两条原则,就可以避免发生这种错误,使设计者更加明确设计目标,同时也增加了Verilog程序的可读性。

此外,还需要解释在硬件语言中使用if语句和case语句的区别。在实际中如果有分支情况,尽量选择case语句。这是因为case语句的分支是并行执行的,各个分支没有优先级的区别。而if语句的选择分支是串行执行的,是按照书写的顺序逐次判断的。如果设计没有这种优先级的考虑,if语句和case语句相比,需要占用额外的硬件资源。

(3)循环语句

        Verilog HDL中提供了4种循环语句:for循环、while循环、forever循环和repeat循环。其语法和用途与C语言很类似。
&#8226;        for循环照指定的次数重复执行过程赋值语句。for循环的语法为:
    for(表达式1; 表达式2; 表达式3) 语句
for循环语句最简单的应用形式是很容易理解的,其形式为:
    for(循环变量赋初值; 循环结束条件; 循环变量增值)
例:for语句的应用实例
for(bindex = 1; bindex <= size; bindex = bindex + 1)
    result = resul + (a <<(bindex-1));
&#8226;        while循环执行过程赋值语句直到指定的条件为假。如果表达式条件在开始不为真(包括假、x以及z),那么过程语句将永远不会被执行。while循环的语法为:
while (表达式) begin
……
end
例:while语句的应用实例
while (temp) begin
    count = count + 1;
end
&#8226;        forever循环语句连续执行过程语句。为跳出这样的循环,中止语句可以与过程语句共同使用。同时,在过程语句中必须使用某种形式的时序控制,否则forever循环将永远循环下去。forever语句必须写在initial模块中,用于产生周期性波形。forever循环的语法为
forever begin
……
end
例:forever语句的应用实例
initial
forever begin
    if(d) a = b + c;
    else a= 0;
end
&#8226;        repeat循环语句执行指定循环数,如果循环计数表达式的值不确定,即为x或z时,那么循环次数按0处理。repeat循环语句的语法为
repeat(表达式) begin
……
end
例:repeat语句的应用实例
repeat (size) begin
    c = b << 1;
end
 楼主| 小舍YZ 发表于 2017-4-5 12:34:54 | 显示全部楼层
2.4.4 混合设计模式

在模型中,结构描述、数据流描述和行为描述可以自由混合。也就是说,模块描述中可以包括实例化的门、模块实例化语句、连续赋值语句以及行为描述语句的混合,它们之间可以相互包含。使用always语句和initial语句(切记只有寄存器类型数据才可以在模块中赋值)来驱动门和开关,而来自于门或连续赋值语句(只能驱动线网型)的输出能够反过来用于触发always语句和initial语句。

下面给出一个混合设计方式的实例。

例2-14 用结构和行为实体描述了一个4位全加器。
module adder4(in1, in2, sum, flag);
      input [3:0] in1;
      input [3:0] in2;
      output [4:0] sum;
      output flag;

       wire c0, c1, c2;

       fulladd u1 (in1 [0], in2 [0], 0, sum[0], c0);
       fulladd u2 (in1 [1], in2 [1], c0, sum[1], c1);
       fulladd u3 (in1 [2], in2 [2], c1, sum[2], c2);
       fulladd u4 (in1 [3], in2 [3], c2, sum[3], sum[4]);

      assign flag = sum ? 0 : 1;
endmodule

在这个例子中,用结构化模块计数sum输出,用行为级模块输出标志位。

 楼主| 小舍YZ 发表于 2017-4-5 12:35:51 | 显示全部楼层
本帖最后由 小舍YZ 于 2017-4-6 10:47 编辑

第5节 Verilog代码书写规范

2.5.1 信号命名规则

       信号命名规则在团队开发中占据着重要地位,统一、有序的命名能大幅减少设计人员之间的冗余工作,还可便于团队成员代码的查错和验证。比较著名的信号命名规则当推Microsoft公司的“匈牙利”法,该命名规则的主要思想是“在变量和函数名中加入前缀以增进人们对程序的理解”。例如所有的字符变量均以ch为前缀,若是常数变量则追加前缀c。信号命名的整体要求为:命名字符具有一定的意义,直白易懂,且项目命名规则唯一。对于HDL设计,设计人员还需要注意以下命名规则。

1.系统级信号的命名

系统级信号指复位信号,置位信号,时钟信号等需要输送到各个模块的全局信号。系统信号以字符串sys或syn开头;时钟信号以clk开头,并在后面添加相应的频率值;复位信号一般以rst或reset开头;置位信号为st或set开头。典型的信号命名方式如下所示:

wire [7:0] sys_dout, sys_din;
wire clk_32p768MHz;
wire reset;
wire st_counter;

2.低电平有效的信号命名

低电平有效的信号后一律加下划线和字母n。如:

wire SysRst_n;
wire FifoFull_n;

3.过锁存器锁存后的信号

经过锁存器锁存后的信号,后加下划线和字母r,与锁存前的信号区别。如:

        信号CpuRamRd信号,经锁存后应命名为CpuRamRd_r。
        低电平有效的信号经过锁存器锁存后,其命名应在_n后加r。如:
        CpuRamRd_n信号,经锁存后应命名为CpuRamRd_nr
        多级锁存的信号,可多加r以标明。如:
        CpuRamRd信号,经两级触发器锁存后,应命名为CpuRamRd_rr。







本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?我要注册

x
您需要登录后才可以回帖 登录 | 我要注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|小黑屋|手机版|Archiver|fpga论坛|fpga设计论坛 ( 京ICP备20003123号-1 )

GMT+8, 2025-6-10 14:01 , Processed in 0.075998 second(s), 17 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表