Verilog模块语法

     1、Verilog的基本设计单元是模块(block)。一个模块是有两部分组成的 ,一部分描述接口,另一部分描述逻辑功能,及定义输入时如何影响输出的。下图为模块结构的组成。


模块特点

Verilog HDL程序是由模块构成的。每个模块的内容都是嵌在module和endmodule两个语句之间。每个模块实现特定的功能。模块可以进行层次嵌套。

每个模块要进行端口定义,并说明输入输出口,然后对模块的功能进行行为逻辑描述。

Verilog HDL程序的书写格式自由,一行可以写几个语句,一个语句也可以分写多行。

除了endmodule语句外,每个语句和数据定义的最后必须有分号。

可以用/…../和//…….对Verilog HDL程序的任何部分作注释。一个好的,有使 
用价值的源程序都应当加上必要的注释,以增强程序的可读性和可维护性。

模块的结构

module <模块名> (<端口列表>)
<I/O说明>
<内部信号声明>
<功能定义>
endmodule

模块的端口定义:

模块的端口声明了模块的输入输出口。格式:

module 模块名(口1,口2,口3,口4,......)
  • 1

模块的内容:

I/O说明

输入口(input 端口名1,端口名2,……端口名n)

扫描二维码关注公众号,回复: 2164232 查看本文章

输出口(output 端口名1,端口名2,……端口名n)

I/O\说明也可以卸载端口声明语句中: module module_name(input port1,input port2,…output port1,output port2…)

内部信号说明

在模块内用到的和与端口有关的wire和reg变量的声明,比如:reg[width-1:0] R1,R2…; wire[width-1:0] W1,W2…

 功能定义

模块中最重要的部分是逻辑功能定义部分。有三种方法可在模块中产生逻辑:

用“assign”声明语句

“assign”,后面再加一个方程式即可

assign a = b & c;//两个输入的与门
  • 1

“assign”语句是描述组合逻辑最常用的方法之一

用实例元件

and and_inst( q, a, b );

采用实例元件的方法象在电路图输入方式下,调入库元件一样。键入元件的名字和相连的引脚即可, 
表示在设计中用到一个跟与门(and)一样的名为and_inst的与门,其输入端为a, b,输出为q。要求 
每个实例元件的名字必须是唯一的,以避免与其他调用与门(and) 的实例混

模块(module)是Verilog 的基本描述单位,用于描述某个设计的功能或结构及与其他模块通信的外部端口。

模块在概念上可等同一个器件就如我们调用通用器件(与门、三态门等)或通用宏单元(计数器、ALU、CPU)等,因此,一个模块可在另一个模块中调用。

一个电路设计可由多个模块组合而成,因此一个模块的设计只是一个系统设计中的某个层次设计,模块设计可采用多种建模方式。

过程块

  1. always过程块

模板:

always @(<敏感信号表达式>)
begin
    //过程赋值
    //if语句  
    //case语句
    //while、repeat、for语句
    //task、function调用
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

当敏感信号表达式的值改变时候,就执行一遍块内语句。同时always过程块是不能够嵌套使用的。

关键字posedgenegedge关键字分别是上升沿以及下降沿

例如:同步时序电路的时钟信号为clk,clear为异步清零信号。敏感信号可写为:

//上升沿触发,高电平清0有效
always @(posedge clk or posedge clear)

//上升沿触发,低电平清0有效
always @(posedge clk or negedge clear)
  • 1
  • 2
  • 3
  • 4
  • 5

例如当negedge clear表示当clear==0

always @(posedge clk or negedge clear)
    begin
        if(!clear)//当clear==0时候,always会由事件驱动
            qout=0;
        else
            qout=in;
    end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. initial过程块

initial模板:

initial
begin
    语句1;
    语句2;
    ......
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

对变量和存贮器初始化

initial
begin
    reg1=0;
    for(addr=0;addr<size;addr=addr+1)
        memory[addr]=0;
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • initial语句主要面向功能模拟,通常不具有可综合性。

  • 模拟0时刻开始执行,只执行一次

  • 同一模块内的多个initial过程块,模拟0时刻开始并行执行。

initial与always语句一样,是不能嵌套使用的。即在initial语句中不能再次嵌套initial语句块。

连续赋值

用连续赋值语句表达的是: assign val=newval;

任何一个输入的改变都将立即导致输出更新;

module orand(out,a,b,c,d,e);
    input a,b,c,d,e;
    output out;
    assign out=3&(a|b)&(c|d);
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5

过程赋值语句

过程赋值语句常用于对reg变量进行赋值。一般分为两种,阻塞赋值非阻塞赋值

阻塞与非阻塞赋值

赋值的类型选择取决于建模的逻辑类型。

  • 在时序块的RTL代码中使用非阻塞赋值<=。非阻塞赋值在块结束后才完成赋值操作。此赋值方式可以避免在仿真出现魔仙和竞争现象。

  • 在组合的RTL代码中使用阻塞赋值=。使用阻塞赋值方式对一个变量进行赋值时,此变量的值在赋值语句执行完后才能之后就立即改变。

compare

使用非阻塞赋值方式进行赋值时,各个赋值语句同步执行;因此通常在一个时钟沿对临时变量进行赋值,而在另一个时钟沿对其进行采样。因为在相同的时钟沿采样赋值,采样的还是原来的值,赋值操作是在块结束时进行。

  • 阻塞赋值

下面模块会综合成为触发器

module block(clk,a,b);
input clk,a;
output b;
reg b;
always @(posedge clk)
    begin
        y=a;
        b=y;
    end
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 非阻塞赋值

下面的模块会综合成两个触发器

module block(clk,a,b);
input clk,a;
output b;
reg b;
always @(posedge clk)
    begin
        y<=a;
        b<=y;
    end
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

上图左侧是阻塞赋值的综合结果,右侧则为非阻塞赋值。相比左侧,右侧的例子会造成一个时钟周期的延迟


小程序

加法器

module addr(a,b,cin,count,sum);
    input [2,0]a;
    input [2,0]b;
    input cin;
    output count;
    output [2,0]sum;
    assign {count,sum}=a+b+cin
endmodule

上面的程序描述的是一个3位加法器,可以看出来,程序从module开始,以endmodule结束。

  • input [2,0]a

表示声明一个3bit的输入变量,命名为a

  • assign {count,sum}=a+b+cin

表明为线网类型赋值,{}是连接符号,count是1bit,sum是3bit,所以连接之后是4bit,最高位是count。等式右边是2个3bit相加,再加上一个1bit的,实现的全加器。

比较器

module compareequalab);
input [1:0] a,b; // declare the input signal ;
output equare ; // declare the output signal;
assign equare = (a == b) ? 1:0 ;
/ * if a = b , output 1, otherwise 0;*/
endmodule

逻辑部分是一个三目运算符号,有C语言基础的都可以看懂。

三态驱动器

module mytri (din, d_en, d_out);
    input din;
    input d_en;
    output d_out;
    // -- Enter your statements here -- //
    assign d_out = d_en ? din :'bz;
endmodule

module trist (din, d_en, d_out);
    input din;
    input d_en;
    output d_out;
    // --statements here -- //
    mytri u_mytri(din,d_en,d_out);
endmodule

全加器

一位全加器

如上图是一位全加器

这里先说明下什么是全加器,并说下全加器半加器的区别:

  • 半加器不考虑低位过来的进位,只计算2个一位二进制数相加。产生一个本位和,还有一个向高位的进位信号。

  • 全加器考虑低位过来的进位,计算2个一位二进制数相加。产生一个本位和,还有一个向高位的进位信号。

  • 即半加器有二个输入,二个输出。全加器有三个输入,2个输出。

module FA_struct (A, B, Cin, Sum, Count);
    input A;
    input B;
    input Cin;
    output Sum;
    output Count;
    wire S1, T1, T2, T3;
    // -- statements -- //
    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

该实例显示了一个全加器由两个异或门、三个与门、一个或门构成。S1、T1、T2、T3则是门与门之间的连线。代码显示了用纯结构的建模方式,其中xor 、and、or 是Verilog HDL 内置的器件。以 xor x1 (S1, A, B) 该例化语句为例: 
xor 表明调用一个内置的异或门,器件名称xor ,代码实例化名x1(类似原理图输入方式)。括号内的S1,A,B 表明该器件管脚的实际连接线(信号)的名称,其中 A、B是输入,S1是输出。其他同。

两位全加器

两位的全加器可通过调用两个一位的全加器来实现。该设计的设计层次示意图和结构图如下:

module Four_bit_FA (FA, FB, FCin, FSum, FCout ) ;
    parameter SIZE = 2;
    input [SIZE:1] FA;
    input [SIZE:1] FB;
    input FCin;
    output [SIZE:1] 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 (FCount )
    );
endmodule

除了低位的进位Fcin,输入FA与FB都是两位,将输入的两位分别放到两个一位全加器上面,就好像我们在做两位数加法时,也是将个位、十位分别相加,再加上进位。

该实例用结构化建模方式进行一个两位的全加器的设计,顶层模块Four_bit_FA 调用了两个一位的全加器 FA_struct 。在这里,以前的设计模块FA_struct 对顶层而言是一个现成的器件,顶层模块只要进行例化就可以了。注意这里的例化中,端口映射(管脚的连线)采用名字关联,如 .A (FA[2]) ,其中.A 表示 调用器件的管脚A,括号中的信号表示接到该管脚A的电路中的具体信号。wire 保留字表明信号Ftemp 是属线网类型(下面有具体描述)。

Verilog建模

Verilog有三种建模方式,分别是

  • 结构化描述方式

  • 数据流描述方式

  • 行为描述方式

其中数据流描述方式经常使用连续赋值语句,某个值被赋给某个网线变量。

assign [delay] net_name = expression;

注意在各assign 语句之间,是并行执行的,即各语句的执行与语句之间的顺序无关。

行为描述方式经常使用always、initial语句赋值。使用reg进行寄存器的声明。always是指一直在重复运行,由always后面括号的变量变化时触发。在always以及end之间是串行顺序执行的。

猜你喜欢

转载自blog.csdn.net/liudongdong19/article/details/80993203