现代信号处理第二章-Verilog电路设计语言

文章目录

  • 第二章 Verilog电路设计语言
    • 一、 Verilog的基本知识
      • 1. 硬件描述语言的概念
        • (1) 定义
        • (2) 作用
        • (3) 分类:HDL主要有两种:Verilog和VHDL
        • (4) 现代电路设计包含的层次
        • (5) Verilog HDL的五级
      • 2. 模块化设计
        • (1) 模块的内容是嵌在module和endmodule两个语句之间
        • (2) 模块包括接口描述部分和逻辑功能描述部分
          • (a) 接口描述部分
          • (b)逻辑功能描述部分
      • 3. Verilog HDL的建模方式
        • (1) 结构化建模
        • (2) 数据流建模
        • (3) 行为建模
        • 总结:
    • 二、 Verilog的语法规则
      • 1. 关键字
      • 2. 标识符
      • 3. 数据类型
      • 4. 常量
      • 5. 变量
      • 6. 运算符
        • (1) 算术运算符
        • (2) 逻辑运算符
        • (3) 关系运算符
        • (4) 等式运算符
        • (5) 缩减运算符
        • (6) 条件运算符
        • (7) 位运算符
        • (8) 移位运算符
        • (9) 位拼接运算符
      • 7. 块语句
        • (1) 顺序快
        • (2) 并行块
      • 8. 条件语句
        • (1) if 语句
        • (2)case 语句
      • 9. 循环语句
        • (1) for语句
        • (2) repeat语句
        • (3) while语句
        • (4) forever语句
      • 10. 编译预处理预计
        • (1) 宏定义
        • (2) `include语句
        • (4) `timescale语句
      • 11. 任务语句及函数语句
        • (1) 任务(task)
        • (2) 函数(function)
      • 12. Verilog语言过程语句
        • (1) always块语句
        • (2) initial语句
      • 13. Verilog语言赋值语句
        • (1) 连续赋值语句
        • (2) 过程赋值语句
    • 三、 Verilog的代码综合

第二章 Verilog电路设计语言

一、 Verilog的基本知识

1. 硬件描述语言的概念

(1) 定义

硬件描述语言HDL(Hardware Description Language)是一种利用形式化方法来描述电路和数字逻辑系统的语言

Verilog HDL 既是一种行为描述语言也是一种结构描述语言

(2) 作用

主要作用:用于编写硬件电路系统的设计文件,建立电子系统行为级的仿真模型

(3) 分类:HDL主要有两种:Verilog和VHDL

(4) 现代电路设计包含的层次

  1. 算法级设计:利用高级语言如C语言及其他一些系统分析工具(如MATLAB)对设计系统的算法级方式进行描述。算法不需要包含时序信息。
  2. RTL级:用于数据流在寄存器间传输的模式来对设计进行描述
  3. 开关级:用晶体管和寄存器及其连线关系来对设计进行描述

(5) Verilog HDL的五级

Verilog HDL语言直接对应于现代电路设计的四个层次,将电路抽象成为五级

  1. 系统级:高级语言结构(如case语句)实现的设计模块外部性能的模型
  2. 算法集:用高级语言结构实现的设计算法模型(写出逻辑表达式)
  3. RTL级:描述数据在寄存器之间的流动和如何处理这些数据的模型
  4. 门级:描述逻辑门(如与门、非门、或门、与非门、三态门)以及逻辑门之间连接的模型
  5. 开关级:描述器件中三极管和储存节点及其之间连接的模型

2. 模块化设计

一个设计是由一个个模块(module)构成的。一个模块的设计如下:

(1) 模块的内容是嵌在module和endmodule两个语句之间

模块具备以下特点:

  1. 模块内容嵌在moudle和endmodule两个语句之间。
  2. 每个模块实现特定的功能。
  3. 模块可以进行层次的嵌套。

Top-down设计思想:由于模块的以上特点,因此大型的数字电路设计可以通过设计不同的小模块来完成,最后通过顶层模块调用子模块来实现整体功能。

(2) 模块包括接口描述部分和逻辑功能描述部分

(a) 接口描述部分

接口描述部分需要包含两方面的内容:

  1. 端口的定义部分;

    adder8(cout,sum,a,b,cin);
    

    adder8是模块名
    ( )内是该模块的端口声明;
    ( )内定义了该模块的管脚名,是该模块与其他模块通讯的外部接口,相当于器件的pin口;

  2. 模块的内容,包括I/O声明,内部信号、调用模块等的声明部分和功能定义语句;

    input [7:0] a,b;input cin;output count;
    // 其中input、output、inout是保留字,用于定义管脚信号的流向
    // input [7:0] a,b; [n:0]表示该信号的位宽(总线或单根信号线)
    // input cin;
    // output count;
    // 其中的input,output是保留字
    

    可以将端口的定义部分和模块内容的定义和在一起,如以下写法:

    module Signal_release(input CLK,input RST,input[8:0] signal,output reg [10:0]resignal);
    
(b)逻辑功能描述部分
assign {cout,sum}=a+b+cin;

用来产生各种逻辑(组合逻辑和时序逻辑),可以用多种方法进行描述,还可以实例化一个器件。在逻辑功能描述中,主要用到assignalways两个语句。以下提供逻辑功能描述的三种方法:

  1. 用assign语句
assign x = (b & ~c);//连续赋值语句常用于描述组合逻辑
  1. 用元件例化
and myand3 (f,a,b,c);
// and 是 门关键字
// myand3 是例化元件名
// 门元件例化

值得注意的是,每个实例元件的名字必须唯一。以避免与其他调用元件的实例相混淆。

  1. 用“always”块语句 (结构说明语句)
always@(posedge clk)
    begin
        if(load)
            out = data;
        else
            out = data + 1 + cin;
    end
  1. "always"块语句与assign语句是并发执行的,assign语句一定要放在“always”块语句之外

  2. “always”块语句常用于描述时序逻辑,也可用于描述组合逻辑

3. Verilog HDL的建模方式

(1) 结构化建模

  1. 定义:通过对电路结构的描述来建模。即通过对器件的调用(HDL概念称为例化),并使用线网来连接各器件的描述方式。

  2. 举例:设计一个1bit全加器的电路

首先我们设计由门电路设计一个1bit全加器

现代信号处理第二章-Verilog电路设计语言_第1张图片

module FullAdditor(A,B,Cin,Sum,Cout);
    input A;
    input B;
    input Cin;
    output Sum;
    output Cout;
    wire S1,T1,T2,T3;
    xor x1(S1,A,B);
    xor x2(Sum,S1,Cin);
    and A1(T3,A,B);
    and A2(T2,B,Cin);
    and A3(T1,A,Cin);
    or O1(cout,T1,T2,T3);
endmodule
  1. 电路图表示,一个全加器由两个异或门、三个与门、一个或门构成。

  2. S1,T1,T2,T3是门与门之间的连线,xor,and,or是Verilog HDL内置的门器件。

  3. xor 表示调用一个内置的异或门,器件名称xor,代码实例化名x1。括号内中的A、B是输入,S1是输出。

(2) 数据流建模

  1. 定义:数据流的建模方式就是通过对数据流在设计中的具体流向描述来建模。最基本的机制就是用连续赋值语句。
`timescasle 1ns/100ps
module FullAdditor(A,B,Cin,Sum,Cout)
    input A,B,Cin;
    output Sum,Cout;
    wire S1,T1,T2,T3;
    assign #2 S1 = A ^ B;
    assign #2 Sum = S1 ^ Cin;
    assign #2 T3 = A & B;
    assign #2 T1 = A & Cin;
    assign #2 T2 = B & Cin;
endmodule
  1. Verilog语言描述的硬件电路,在实际工作时,一旦商店,各个元器件均并行开始工作。

  2. Verilog语言各实例单元assign语句都是并行执行的,即各语句的执行与语句之间的顺序无关。

  3. 上述代码中当A有个变化时,S1、T3、T1将同时变化,S1的变化又会造成Sum的变化。

(3) 行为建模

行为方式的建模是指采用对信号行为级的描述(不是结构级的描述)的方式来建模。

一般把用initial块语句或always块语句描述的归为行为建模方式

module FullAdditor(A,B,Cin,Sum,Cout)
    input A,B,Cin;
    output Sum,Cout;
    reg Sum,Cout;
    always@(A or B or Cin)
        begin
            {cout,Sum} = A + B + Cin;
        end
endmodule
  1. 在示例中对1bit全加器采用了更加高级的描述方式,即直接采用"+"来描述加法

  2. {Cout,Sum}表示对位数的扩展,因为有两个1bit相加会得到两位数据,低位放在Sum变量,高位放在Cout中

总结:

在实际的设计中,往往是多种设计模型混合的。一般,对顶层设计,采用结构描述方式,对低层模块,可采用数据流、行为级或两者结合的方式:

  1. 顶层模块 (FullAdditor_2bits)采用结构描述方式

  2. 底层模块(FA)可采用结构描述、数据流描述或行为级描述。

module FullAdditor_2bits(FA,FB,FCin,FSum,FCout)
    parameter SIZE = 2;
    input [SIZE-1:0] FA;
    input [SIZE-1:0] FB;
    input FCin;
    output [SIZE-1:0] FSum;
    output FCout;
    wire FTemp;
    FA_struct FA1(.A(FA[1]),.B(FB[1]),.Cin(FCin),.Sum(FSum[1]),.Cout(Ftemp));
    FA_struct FA2(.A(FA[2]),.B(FB[2]),.Cin(FTemp),.Sum(FSum[2]),.Cout(FCout));
endmodule

二、 Verilog的语法规则

1. 关键字

关键字:事先定义好的确认符,用来组织语言结构;或者用于定义Verilog HDL提供的门元件(如and,not,or,buf)。

现代信号处理第二章-Verilog电路设计语言_第2张图片

2. 标识符

  1. 标识符:任何用Verilog HDL语言描述的“东西”都通过其名字来识别,这个名字被称为标识符。

如源文件名、模块名、端口名、变量名、常量名、实例名等

  1. 标识符可由字母、数字、下划线和$符号构成;但第一个字符必须是字母或下划线,不能是数字或$符号。且标识符不能与关键字同名

现代信号处理第二章-Verilog电路设计语言_第3张图片

3. 数据类型

四种基本数据类型为:integer型,parameter型,reg型,wire型

4. 常量

在程序运行过程中,其值不能被改变的量,称为常量

  1. 数字(包括整数,x和z值,负数)

    • 整数型常量(即整常数)的4种进制表现形式:

      • 二进制整数(b或B)

      • 十进制整数(d或D)

      • 十六进制整数(h或H)

      • 八进制整数(o或O)

      表达方式 说明 举例
      <位宽>'<进制><数字> 完整表达 8’hc5或8’b11000011
      <进制><数字> 缺省位宽,位宽由计算机系统决定,32位或64位置(一般) hc5
      <数字> 缺省进制为十进制,位宽默认为32位 197
    • x和z值

      • x表示不定值,z表示高阻值

        8b'1001xxxx或8'h9x
        8b'1010zzzz或8'haz
        
      • 当用二进制表示时,已表明位宽的数若用x或z表示某些位,则只有在最左边的x或z具有扩展性。为清晰可见,最好直接写出每一位的值。

        8'bzx = 8'bzzzz_zzzx
        8'b1x = 8'b0000_001x
        
      • "?"是z的另一个表示符号,建议在case语句中使用?表示高阻态z

        case z (select)
        4'b ???1:out = a;
        4'b ??1?:out = b;
        4'b ?1??:out = c;
        4'b 1???:out = d;
        endcase
        
    • 负数

    在位宽前加一个减号,即表示负数

    如:-8’d5 //5的补数, = 8b’11111011

  2. parameter常量,即符号常量

用parameter来定义一个标识符,代表一个常量—称为符号常量

parameter 参数名1 = 表达式,参数名2 = 表达式,……;
  • 每个赋值语句的右边必须为常数表达式,即只能包含数字或先前定义过的符号常量
parameter addwidth = 16;//合法格式
parameter addwidth = datawidth*2;//非法格式
  • 参数是本地的,其定义只在本模块内有效

  • 常用参数来定义延迟时间变量宽度

5. 变量

在程序运行过程中,其值可以改变的量,称为变量

数据类型有19种,常用的有3种

  • 网络型(nets type)

  • 寄存器(register type)

  • 数组(memory type)

  1. nets型变量

定义:输出始终随输入的变化而变化的变量。表示结构实体(如门)之间的物理连接。

nets型变量不能储存值

现代信号处理第二章-Verilog电路设计语言_第4张图片

wire型变量为例:

  1. 最常用的nets型变量,常用来表示以assign语句赋值的组合逻辑信号。

  2. 模块中的输入/输出信号类型缺省为wire型

  3. 可用做任何方程式的输入,或“assign”语句和实例元件的输出。

wire 数据名1,数据名2,……,数据名m;
wire[n:1] 数据名1,数据名2,……,数据名m;
// 每条总线的位宽为n,共有m条总线
  1. register型变量

定义:对应具有状态保持作用的电路元件(如触发器、寄存器等),常用来表示过程块语句(如initial,always,task,function)内的指定信号。

现代信号处理第二章-Verilog电路设计语言_第5张图片

reg型变量为例:

  1. 在过程块中被赋值的信号,往往代表触发器。也可以代表组合逻辑信号
reg[n:0] 数据名1,数据名2,……,数据名n;
reg 数据名1,数据名2,……,数据名n;
  1. 用reg型变量生成触发器举例
module rw2(clk,d,out1,out2)
    input clk,d;
    output out1,out2;
    reg out1;
    wire out2;
    assign out2 = d & ~out1;
    always @(posedge clk)
        begin
            out1 <= d;
        end
endmodule
  1. memory型变量——数组

不推荐使用数组类型

  • 定义:由若干个相同宽度的reg型向量构成的数组

  • memory型变量可描述RAM,ROM和reg文件

  • memory型变量通过扩展reg型变量的地址范围来生成

reg[n-1:0] 存储器名 [m-1:0]
// 上述语句定义了一个存储器:每个存储单元位宽为n,存储单元的个数为m

memory型变量与reg型变量的区别

  1. 含义不同
reg[n-1:0] rega; //一个n位的寄存器
reg mema [n-1:0]; //由n个1位寄存器组成的存储器
  1. 赋值方式不同

如果要对某存储器中的存储单元进行读写操作,必须要指明该单元在存储器的地址

rega = 0; //合法赋值语句
mema = 0; //非法赋值语句
mema[8] = 1; //合法赋值语句
mema[1023:0] = 0; //合法赋值语句
// 上述 [ ]内就指明了存储单元的地址

6. 运算符

现代信号处理第二章-Verilog电路设计语言_第6张图片

(1) 算术运算符

+ , − , ∗ , / , % +,-,*,/,\% +,,,/,%

现代信号处理第二章-Verilog电路设计语言_第7张图片

进行算术运算时,若某操作数为不定值x,则整个结果也为x

(2) 逻辑运算符

  1. 逻辑运算后的结果为布尔值(1位的1或0或x)

现代信号处理第二章-Verilog电路设计语言_第8张图片

逻辑运算符把它的操作数当作布尔变量

  • 非零的操作数被认为是真(1’b1);

  • 零被认为是假(1’b0);

  • 不确定的操作数如4’bxx00,被认为是不确定的,记为1’bx;

  • 但4’bxx11被认为是真,记为1’b1;

  1. 运算符的优先级问题:

现代信号处理第二章-Verilog电路设计语言_第9张图片

(3) 关系运算符

  1. 运算结果为1位的逻辑值1或0或x

现代信号处理第二章-Verilog电路设计语言_第10张图片

  • 若关系为真,则返回值为1;

  • 若关系为假,则返回值为0;

  • 若某操作数为不定值x,则返回值为x

  1. 所有的关系运算符优先级别相同

  2. 关系运算符的优先级低于算术运算符

现代信号处理第二章-Verilog电路设计语言_第11张图片

(4) 等式运算符

现代信号处理第二章-Verilog电路设计语言_第12张图片

  1. 运算结果为1位的逻辑值1或0或x;

  2. 等于运算符()和全等运算符(=)的区别:

    • 使用等于运算符,两个操作数必须逐位相等,结果才为1;若某些位为x或z,这结果为x

    • 使用全等运算符,若两个操作数的相应位完全一致(即同时是1,或同时是0,或同时是x,或同时是z),则结果为1;否则为0

  3. 所有的等式运算符优先级相同

  4. = = = 和 ! = = 运算符用于case表达式的判别,又称为“case等式运算符”

(5) 缩减运算符

现代信号处理第二章-Verilog电路设计语言_第13张图片

(6) 条件运算符

现代信号处理第二章-Verilog电路设计语言_第14张图片

(7) 位运算符

现代信号处理第二章-Verilog电路设计语言_第15张图片

  • 位运算结果与操作数位数相同。位运算符中的双目运算符 要求 对两个操作数的相应位逐位进行运算;

  • 两个不同长度的操作数进行位运算时,将自动按右端对齐,位数少的操作数会在高位用0补齐;

若A = 5’b11001,B = 3’b101

则 A & B = (5’b11001) & (5’b00101) = 5b’00001

(8) 移位运算符

用法:A>>n 或 A<

现代信号处理第二章-Verilog电路设计语言_第16张图片

(9) 位拼接运算符

现代信号处理第二章-Verilog电路设计语言_第17张图片

  • 可用重复法简化表达式,如:{4{w}}等同于{w,w,w,w}

  • 还可用嵌套方法简化书写,如:

    {b,{3{a,b}}}等同于{b,{a,b},{a,b},{a,b}},也等同于{b,a,b,a,b,a,b}

现代信号处理第二章-Verilog电路设计语言_第18张图片

7. 块语句

块语句有两种:

  1. begin_end语句——标识顺序执行的语句

  2. fork_join语句——标识并行执行的语句

(1) 顺序快

用begin_end标识的块

  • 块内的语句是顺序执行的

  • 每条语句的延迟时间是相当于前一条语句的仿真时间而言的

  • 直到最后一条语句执行完,程序流程控制才跳出该顺序块

现代信号处理第二章-Verilog电路设计语言_第19张图片

\\ 用顺序块和延迟控制组合产生一个时序波形
parameter d = 50;
reg[7:0] r;
begin
    # d r = 'h35;
    # d r = 'hE2;
    # d r = 'h00;
    # d r = 'hF7;
    # d -> end_wave; //触发时间end_wave
end

(2) 并行块

  • 块内的语句是同时执行的

  • 块内每条延迟时间是相对于程序流程控制进入到块内的仿真时间而言的

  • 延迟时间的长短决定了赋值语句的时序

  • 按时间排序,延迟时间最长的语句将是最后一个执行的语句

现代信号处理第二章-Verilog电路设计语言_第20张图片

\\ 用并行块和延迟控制组合产生一个时序波形
reg[7:0] r;
begin
    # 50 r = 'h35;
    # 100 r = 'hE2;
    # 150 r = 'h00;
    # 200 r = 'hF7;
    # 250 -> end_wave; //触发时间end_wave
end

8. 条件语句

(1) if 语句

(2)case 语句

现代信号处理第二章-Verilog电路设计语言_第21张图片

case语句的变体:

  • 在case语句中,分支表达式每一位的值都是确定的(要么为1,要么为0)

  • 在casez语句中,若分支表达式某些位的值为高阻值z,则不考虑对这些位的比较

  • 在casex语句中,若分支表达式某些位的值为z或不定值x,则不考虑对这些位的比较

  • 在分支表达式中,可用"?"来标识x或z

  • 用casez描述的数据选择器

module mux_z(out,a,b,c,d,select)
    output out;
    input a,b,c,d,e;
    input[3:0] select;
    reg out;//必须声明
    always@(select[3:0] or a or b or c or d or e)
        begin
            casez(select)
                4'b???1:out = a;
                4'b??1?:out = b;
                4'b?1??:out = c;
                4'b1???:out = d;
                default:out = e;
            endcase
        end
endmodule

避免生成锁存器的原则:

  1. 如果用到if语句,最好写上else项;

  2. 如果用到case语句,最好写上default项

9. 循环语句

现代信号处理第二章-Verilog电路设计语言_第22张图片

(1) for语句

用for语句初始化memory

begin:init_mem //init_mem是块的名字
    reg[7:0]tempi; //存储器的地址变量
    for(tempi = 0;tempi

(2) repeat语句

连续执行一条或多条语句n次

现代信号处理第二章-Verilog电路设计语言_第23张图片

(3) while语句

(4) forever语句

无条件连续执行forever后面的语句或语句块

现代信号处理第二章-Verilog电路设计语言_第24张图片

  1. forever 语句常用在测试模块中产生周期性的波形,作为仿真激励型号

  2. 常用disable语句跳出循环

  3. forever语句不同于always语句,其不能独立写在程序中,一般用在initial语句块中

举例:产生时钟信号

initial
    begin:Clocking
        clk = 0;
        #10 forever #10 clk =~clk
    end
initial
    begin:Stimulus
    disable Clocking;//停止时钟
    end

10. 编译预处理预计

编译预处理语句以西文符号`开头;且在编译时,编译系统先对编译预处理预处理语句进行预处理,然后将处理结果和源程序一起进行编译。

(1) 宏定义

用一个指定的标识符(即宏名)来代表一个字符串(即宏内容);`define语句可以写在模块定义的外面或里面。宏名的有效范围为定义命令之后到源文件结束。

宏定义的作用:

  • 以一个简单的名字来代替一个长的字符串或复杂表达式

  • 以一个有含义的名字代替没有含义的数字和符号

module test();
    reg a,b,c,d,e,out;
    `define expression a+b+c+d
    // 值得注意的是,不能写成:`define expression a+b+c+d;,不能有分号。否则会使得分号也并入语句中,造成下面out赋值语句出错
    assign out = `expression + e;
endmodule;

(2) `include语句

文件包含语句——一个源文件可将另一个源文件的全部内容包含进来

`include"文件名"

值得注意的是`include语句只能指定一个被包含的文件;

若要包含n个文件,需要n个`include语句

现代信号处理第二章-Verilog电路设计语言_第25张图片

注意此处的实例化使用了 # 来改变一些特有的parameter参数

(4) `timescale语句

  1. 定义:时间尺度语句——用于定义跟在该命令后模块的时间单位和时间精度

  2. 定义格式`timescale<时间单位>/<时间精度>

现代信号处理第二章-Verilog电路设计语言_第26张图片

时间精度至少要和时间单位一样精确,时间精度值不能大于时间单位值

`timescale 1ps/1ns // 非法
`timescale 1ns/1ps // 合法

`timescale语句应用举例

`timescale 10ns/1ns // 时间单位为10ns,时间精度为1ns
...... // 省略号表示省略了结构上的代码

reg sel;
initial
    begin
        #10 sel = 0; // 在10ns x 10时刻,sel变量被赋值为0
        #10 sel = 1; // 在10ns x 20时刻,sel变量被赋值为1
    end
......

11. 任务语句及函数语句

(1) 任务(task)

  • 但希望能够对一些信号进行运算并输出多个结果(即有多个输出变量时),适合采用任务结构

  • 常常利用任务来帮助实现结构化的模块设计,将批量的操作以任务的形式独立出来,使设计简单明了。

现代信号处理第二章-Verilog电路设计语言_第27张图片

  1. 任务的定义与调用必须在一个module模块内

  2. 任务被调用,需列出端口名列表,且必须与任务定义的I/O变量一一对应

  3. 一个任务可以调用其他任务和函数

// 任务定义
task my_task;
    input a,b;
    inout c;
    output d,e;
        ......
        <语句> // 执行任务工作相应的语句
        ......
    c = foo1;
    d = foo2; // 对任务的输出变量赋值
    e = foo3;
endtask

// 任务调用
my_task(v,w,x,y,z);
  • 当任务启动时,由v,w和x传入的变量赋给了a,b,c;

  • 当任务完成后,输出通过c、d和e赋给了x、y和z。

举例:系统任务

  • $random为系统任务,返回一个32位的带符号的随机数

  • 一般用法为

$random % b;
// 其中 b>0,它给出一个范围在-b+1~b+1的随机数
{$random} % b;
// {$random} % b通过位拼接操作,产生一个0~14之间的随机数

(2) 函数(function)

  1. 定义:

    1. 函数的目的是通过返回一个用于某表达式的值,来响应输入信号。适于对不同变量采取同一运算的操纵

    2. 函数在模块内部定义,通常在本模块中调用,也能根据按模块层次分级命名的函数名从其他模块调用。而任务只能在同一模块内定义与调用

现代信号处理第二章-Verilog电路设计语言_第28张图片

在这里插入图片描述

// 函数的定义
function[7:0] gefun;
    input [7:0] x;
    ......        // 进行运算
    gefun = cout; // 赋值语句
endfunction
// 函数的调用
assign number = gefun(rega);
  1. 函数的定义不能包含任何时间控制语句

    时间控制语句:用延迟#,事件控制@或等待wait标识的语句

  2. 函数不能调用任务task

  3. 定义函数时至少有一个输入参量,且不能有任何输出或输入的双向变量

  4. 在函数的定义中必须有一条赋值语句,给函数中的一个内部寄存器赋以函数的结果值,该内部寄存器与函数同名

  1. 举例:利用函数对一个8位二进制中为0的位进行技术
// Count the number of 0 in rega[7...0]
module count0s_function(number,rega);
    output [7:0] number;
    input [7:0] rega;

    function[7:0] gefun; // 函数定义
        input[7:0] x; // 只有输入变量
        reg[7:0] count;
        integer i;
        begin
            count = 0;
            for(i = 0;i<=7;i = i + 1)
                if(x[i] == 1'b0) count = count = 1;
            gefun = count; // 返回函数返回值
        end
    endfunction

    assign number = gefun(rega); // 调用函数
endmodule
  1. 举例:阶乘运算函数
// factorial function
module tryfunct(result,clk,reset,n);
// 函数定义
    function[31:0] factorial;
        input[3:0] op;
        reg[3:0] ina;
        begin
            factorial = op?1:0;
            for(ina = 2;ina <= op;ina = ina + 1)
                factorial = ina*factorial;
        end
    endfunction

    output[31:0] result;
    input[3:0] n;
    input reset,clk;
    reg[31:0]result;

    always@(posedge clk)
        begin
            if(!reset) result <= 0;
            else result <= factorial(n);
        end
endmodule

对于阶乘模块的测试模块

// The test module of tryfunct.v
`include "./tryfunct.v"
`timescale 1ns/100ps
`define clk_cycle 50


module tryfuncttop;
    reg[3:0] n,i;
    reg reset,clk;
    wire[31:0] result;
    // initial 表示了对各变量进行初始化,并产生激励波形
    initial
        begin
            n = 0;
            reset = 1;
            clk = 0;
            #100 reset = 0; // 产生复位信号
            #100 reset = 1;
            for(i = 0;i<=15;i+1) // 产生激励波形
                begin
                    #200 n = i;
                end
            #100 $stop
        end
    always #'clk_cycle clk = ~clk;// 产生时钟波形

endmodule
任务(task) 函数(function)
目的或用途 可计算多个结果值 通过返回一个值,来响应输入信号
输入与输出 可为各种类型(包括inout型) 至少有一个输入变量,但不能有任何output或inout型变量
被调用 只可在过程赋值语句中调用,不能再连续赋值语句中调用(assign语句) 可作为表达式中的一个操作数来调用,在过程赋值和连续赋值语句中均可调用
调用其他任务和函数 任务可调用其他任务函数 函数可调用其他函数,但不可调用其他任务
返回值 不向表达式返回值 向调用它的表达式返回一个值

12. Verilog语言过程语句

initial说明语句 always说明语句
只执行一次 不断重复执行,直到仿真结束

(1) always块语句

  • 定义:包含一个或一个以上的声明语句(如:过程赋值语句、任务调用、条件语句和循环语句等),在运行的全过程中,在定时控制下被反复执行。

  • 在always块中被赋值的只能是register型变量(如reg,integer,real,time)

  • 每个always块在仿真一开始便开始执行,当执行完块中最后一个语句,继续从always块的开头执行。

  • 如果always块中包含一个以上的语句,则这些语句必须放在begin_end或fork_join块中

always@(posedge clk or negedge clear)
    begin
        if(!clear) qout = 0; //异步清零
        else qout = 1;
    end
  • always语句必须与一定的时序控制结合在一起才有用,如果没有时序控制,则易形成仿真死锁
  1. 在测试文件中,常用这种方法形成一个无限延续的信号波形——时钟信号
`define half_period 50
module half_clk_top;
    reg reset,clk; // 输入信号
    wire clk_out; // 输出信号
    always #half_period clk = ~clk;
endmodule 
  1. 用always块语句产生二分频和8位二进制计数器
module always_demo (counter,tick,clk);
    output [7:0] counter;
    output tick;
    input clk;
    reg [7:0] counter;
    reg tick;
    always@(posedge clk)
        begin
            tick = ~tick; // 产生二分频信号
            counter = counter + 1; // 产生8位二进制计数器
        end
endmodule
  • always块语句模板

现代信号处理第二章-Verilog电路设计语言_第29张图片

  1. 敏感信号表达式又称为事件表达式或敏感表,当其值改变时,则执行一遍块内语句

  2. 在敏感信号表达式中应列出影响块内取值的所有信号

  3. 敏感信号不要为x或z,否则会阻挡进程

  4. 当always块有多个敏感信号时,一定要采用if - else if语句,而不能采用并列的if语句。否则易造成一个寄存器有多个时钟驱动,将出现编译错误

现代信号处理第二章-Verilog电路设计语言_第30张图片

  1. always块敏感事件表达式中写在最前面的信号为主激励信号,在程序中不需要出现,or后面连接的信号为辅激励信号,在always块中要有所应用,否则就没必要写了

现代信号处理第二章-Verilog电路设计语言_第31张图片

(2) initial语句

现代信号处理第二章-Verilog电路设计语言_第32张图片

13. Verilog语言赋值语句

(1) 连续赋值语句

  1. 定义:assign语句,用于对wire型变量赋值,是描述组合逻辑常用的方式之一

assign c = a&b;//a,b,c均为wire型变量

(2) 过程赋值语句

用于对reg型变量赋值

  1. 非阻塞赋值方式

赋值符号为<=,如b <= a;

always@(posedge clk)
    begin
        b <= a;
        c <= b;
    end

非阻塞赋值在结束时才完成赋值操作。这在上述代码中体现为,c的值比b的值落后一个时钟周期。

如果初始状态下a = 1,b = 0,c = 0。那么

  • 第一次执行块语句时:b = 1;c = 0;

  • 第二次执行块语句时:b = 1;c = 1;

因此a的值需要经过两个时钟周期才会传递给c。

  1. 阻塞赋值方式

赋值符号为=,如b = a;

always@(posedge clk)
    begin
        b = a;
        c = b;
    end

阻塞赋值在该语句结束时就完成赋值操作。在一个快语句中,如果有多条阻塞赋值语句,在前面的赋值语句没有完成之前,后面的语句就不能被执行。

在上述代码中,初始状态下a = 1,b = 0,c = 0。那么

  • 第一次执行块语句时:b = 1;c = 1;

因此a的值会在第一次执行块语句时,传递给b和c。

现代信号处理第二章-Verilog电路设计语言_第33张图片

  1. 在描述组合逻辑的always块中用阻塞赋值,则综合成组合逻辑的电路结构
  • 一般可综合的阻塞赋值操作在RHS不能设定有延迟。因为本身阻塞语句已经具有顺序了。

  • 所谓阻塞的概念是指在同一个always块中,其后面的赋值语句从概念上(即使不设定延迟)是在前一句赋值语句结束后再开始赋值的。

现代信号处理第二章-Verilog电路设计语言_第34张图片

  1. 在描述时序逻辑的always块中用非阻塞赋值,则综合成时序逻辑的电路结构
  • 非阻塞赋值的操作可以看作两个步骤的过程:

    1. 在赋值时刻开始时,计算所有非阻塞赋值RHS表达式

    2. 在赋值时刻结束时,更新非阻塞赋值LHS表达式

  • 非阻塞赋值操作只能用于对寄存器类型变量进行赋值,因此只能用在“initial”块和“always”块等过程块中。非阻塞赋值不允许用于连续赋值。

现代信号处理第二章-Verilog电路设计语言_第35张图片

  1. 非阻塞赋值和阻塞赋值的代码建议

现代信号处理第二章-Verilog电路设计语言_第36张图片

三、 Verilog的代码综合

  1. Verilog中没有“编译”的概念,而只有综合的概念。

  2. 综合的定义:将用HDL语言或图形方式描述的电路设计转换为实际门级电路(如触发器、逻辑门等),得到一个网表文件,用于进行适配(在实际器件中进行布局和布线)

  3. 在使用Verilog语言时,首先要有所要写的module在硬件上如何实现,而不是去想编译器如何去解释这个module。比如在决定reg是否定义时,需要考虑物理上是否存在这个寄存器。

你可能感兴趣的:(fpga开发)