verilog-模块的结构、数据类型、变量、基本运算符号、赋值语句和块语句

1.模块的结构

verilog的基本设计单元是“模块”(block)。一个模块由两部分组成:接口和逻辑功能,即定义输入是如何影响输出的。
verilog模块的结构位于module和endmodule之间,每个verilog程序包含4个主要部分:端口定义、I/O说明、内部信号声明和功能定义。

举例:

module block(a,b,c,d);	//端口定义以及I/O说明
/*内部信号声明*/
input a,b;
output c,d;
/*功能定义(逻辑功能)*/
assign c = a|b;
assign d = a&b;
endmodule;

(1)模块的端口

模块的端口声明了模块的输入输出口,格式为:
module 模块名(inport1,inport2,…,outport3,ouoput4,…);
模块的端口表示的是模块的输入和输出口名,即它与其他模块联系端口的标识。
在模块被引用时,在引用的模块中,有些信号要输入到被引用的模块中,有的信号需要从被引用的模块中取出来。在引用模块时其端口可以用以下两种方法连接:

  • a.在引用时,严格按照模块定义的端口顺序来连接,不用标明原模块定义时规定的端口名,例如:
    模块名(连接端口1信号名,连接端口2信号名,…);
    mytri tri1(ins,outs);//mytri为调用的模块名,tri1为mytri模块的实例部件,ins和outs分别用于连接mytri模块()内定义的信号名
  • b.在引用时用“.”,标明原模块是为定义时规定的端口名,例如:
    模块名(.端口1名(连接信号1名),.端口2名(连接信号2名),…)
    mytri tri1(.in(ins),.out(outs));//mytri为调用的模块名,tri1为mytri模块的实例部件,in和out为mytri模块内定义的信号,ins和outs为当前模块使用的信号名

(2)I/O说明

I/O说明的格式为:

  • 输入口:
    input [width-1:0] inport1;
    input [width-1:0] inport2;
  • 输出口:
    output [width-1:0] outport1;
    output [width-1:0] outport2;
  • 输入/输出口:
    inout [width-1:0] inoutport1;
    inout [width-1:0] inoutport2;

    I/O说明也可以写在端口声明语句里面,其格式如下:
    module module_name(input inport1,input inport2,…,output1,output2,…);
    例如:
    input [6:0] a;
    input [6:0] b;
    output [7:0] sum;
    module twosum(
    input [6:0] a,
    input [6:0] b,
    output [7:0] sum
    );

(3)内部信号声明

模块内部信号类型一般为wire和reg类型。
格式为:
reg [width-1] R1,R2,…;
wire [width-1] W1,W2,…;
例如:
reg [7:0] led;
reg [19:0] cnt;
wire [1:0] out;

(4)功能定义

模块中产生逻辑的方法有以下三种:

  • a.用“assign”声明语句(assign是描述组合逻辑常用方法之一)
    如:assign a = b&c;
  • b.用实例元件
    如:and #2 u1(q,a,b);
    //表示设计中用到一个跟与门(and)一样的名为ul的与门,其输入端为a和b,输出端为q,输出延迟为2个单位时间。注意每个实例元件的名字必须唯一。
    采用实例元件的方法就像是在电路图输入方式下调入库文件,键入元件的名字和相连的管脚即可。
  • c.用“always”块(always既可用于描述组合逻辑,也可描述时序逻辑)
  • always块也成为过程块,两个或更多的always模块是同时执行的,而always模块内部的语句是顺序执行的。
    如://实现异步清零的d触发器
always @(posedge clk or negedge clr);
begin
	if(clr) q <= 0;
	else if(en) q <= d;
end

tips

对于过程块(如initial块、always块)、连续赋值语句和实例引用三者而言:
(1)在verilog模块内部三者是并行的;
(2)三者是一种通过变量名互相连接的关系;
(3)在同一模块中,三者出现的先后顺序没有关系;
(4)只有连续赋值语句assign和实例引用语句可以独立于过程块而存在于模块的功能定义部分。

2.数据类型及其常量和变量

  • 最基本(常用)的数据类型:reg型、wire型、integer型、parameter型。
  • 常量:指程序运行过程中,值不能改变的量。通常有数字和参数两种类型。
  • 变量:指程序运行过程中,其值可以改变的量。通常有wire型、reg型和memory型。

2.1 常量

2.1.1 数字
  • (1)整数

    • 二进制整数(b or B)
    • 十进制整数 (d or D)
    • 十六进制整数 (h or H)
    • 八进制整数 (o or O)
      数字的表达方式有以下3种:
      1)<位宽><进制><数字>
      2)<进制><数字> 此方式种,数字的位宽采用默认位宽(由及其系统决定,但至少32位)
      3)<数字> 此方式采用默认进制(十进制)
      在数字的表达方式中,位宽指明了数字的精确位数。如:
      8’b10011100 //位宽为8的二进制数,'b表示二进制
      8’ha9 //位宽为8的十六进制数,'h表示十六进制
  • (2)x和z值
    在数字电路中,x表示不定值,一个x可用来定义十六进制的4位二进制状态,八进制的3位,二进制的1位。
    z表示高阻值,z的另外一种表示方式为“?”,其定义与x类似。
    如:
    4’b100x //位宽为4的二进制数,其中第0位为不定值
    4’b111z //位宽为4的二进制数,其中第0为为高阻值
    12’dz //位宽为12为的十进制数,其值为高阻值
    8’h1? //为宽为8的十六进制数,其低四位值为高阻值

  • (3)负数
    数字可以被定义为负数,表达方式为在位宽表达式前加“-”(减号),“-”位置固定,不能随意放置。
    如:
    -8’d10 //表示10的补码

  • (4)下划线_
    “_”可用来分割数字的表达以提高程序可读性,用于数字之间(也可以不用直接用一串数字)
    如:
    16’b0110_1010_1110_0011 等同于 16’b0110101011100011

  • tips:

当常量不指明位数时,默认值为32位,每个字母用8位的ASCII值表示。
如:
10等同于32’d10等同于32’b1010
1等同于32’d1
-1等同于-32’d1等同于32’hFFFFFFFF
"AB"等同于16’B01000001_01000010//A的ASCII码值为65,B的ASCII码值为66

2.1.2 参数(parameter)型

parameter用来定义一个标志符来代表一个常量,称为符号常量,即标识符形式的常量,来提高程序的可读性和可维护性。
参数型常量经常用于定义延迟时间和变量宽度。在模块或实例引用时,可通过参数传递改变在被引用模块或实例中已定义的参数。
格式如下:
parameter 参数名1=表达式,参数名2=表达式,…
如:
parameter len = 4;
parameter r = 6;
parameter msb = 7;
parameter byte_size = 8,byte_msb = byte_size-1;
paremeter average_delay = (len+r)/2;

2.2 变量

2.2.1 wire型

verilog程序中,输入、输出信号类型默认时自动定义为wire型。wire型信号可以用作任何方程式的输入,也可以用作assign语句或实例元件的输出。
wire型信号格式与reg型信号格式类似,格式如下:
wire [n-1:0] data1,data2,…;//数据位宽为n
或者
wire [n:1] data1,data2,…;//数据位宽为n
如:
wire a;//定义了一个1位的wire型数据
wire [7:0] b;//定义了一个8位的wire型数据
wire [4:1] c,d;//定义了二个4位的wire型数据,多个变量中间用","隔开

2.2.2 reg型

reg是寄存器数据类型的关键字,通过赋值语句可以改变寄存器储存的值,其作用与改变触发器储存的值相当。
reg型数据的格式为:
reg [n-1:0] data1,data2,…;//数据位宽为n
或者
reg [n:1] data1,data2,…;//数据位宽为n
如:
reg rega; //定义一个1位reg型数据
reg [3:0] regb; //定义一个4位reg型数据
reg [4:1] regc,regd; //定义两个4位reg型数据

  • tips:
    reg型数据的默认初始值为不定值。reg型数据可以赋正值,也可以赋负值。但当一个reg型数据是一个表达式中的操作数时,它的值被当做是无符号值,即正值。例如,当一个4位的寄存器用作表达式中的操作数时,如果开始寄存器被赋以值-1,则在表达式中进行运算时,其值被认为是+15。
2.2.3 memory型

Verilog HDL通过对reg型变量建立数组来对存储器建模,可以描述RAM型寄存器、ROM型寄存器和reg文件。数组中的每一个单元通过一个数组索引进行寻址。在verilog语言中没有多维数组存在。memory型数据是通过扩展reg型数据的地址范围来生成的。
格式如下:
reg [n-1:0] memoryname[m-1:0]
或者
reg [n-1:0] memoryname[m:1]
其中,reg[n-1:0]定义了存储器中每一个存储单元的大小,即每个存储单元都是一个n位的寄存器,而[m:1]则定义了存储单元的数量。
如:
reg [7:0] mem1[255:0]; //定义了一个mem1存储器,其有256个8位的存储器,存储器的地址范围是0到255

  • tips:
    • 对存储器进行地址索引的表达式必须是常数表达式。
    • 在同一个数据类型声明语句里,可以同时定义存储器型数据和reg型数据。如:
      parameter wordside = 16,
      memsize = 256;
      reg [wordside-1:0] mem[memsize-1:0], writereg, readreg;
    • memory型数据和reg型数据的格式比较类似,使用时需要加以区分。如:
      reg [n-1:0] rega;//一个n位的寄存器
      reg mem1[n-1:0];//一个由1位寄存器构成的寄存器组
    • 关于赋值:
      一个n位的寄存器可以在一条赋值语句中进行赋值,而一个寄存器则不行。如:
      rega = 0;//合法赋值语句
      mema = 0; //非法赋值语句
    • 关于存储器读写:
      对寄存单元进行读写,必须指定该单元在存储器中的地址,如:
      mema[3] = 0;//给memory中第3个存储单元赋值为0

3.运算符与表达式

  • (1)算术运算符
    • +(加法运算符,如rega+regb)
    • -(减法运算符,如rega-3)
    • x(乘法运算符,如rega*3)
    • /(除法运算符,如5/3)
    • %(模运算符或取余运算符,要求%左右两边均为整型数据,如7%3=1)
  • 赋值运算符
    • =(阻塞赋值方式,常用组合逻辑,即时生效,如b=a;b的值在赋值语句执行完后立刻改变)
    • <= (非阻塞赋值方式,常用于时序逻辑,有延时,块结束后才能完成本次赋值操作,而所赋的变量值是上一次赋值得到的)
      赋值语句实例,如:
      (1)用非阻塞赋值法确定reg型信号b和c
always @(posedge clk)
	begin
		b <= a;
		c <= b;
	end

代码实现功能为:在clk上升沿到来时,b等于a,c等于b,用到了两个触发器(注:赋值操作是在always块结束后执行的,c应为原来的b值。)。所实现的电路功能如下:
在这里插入图片描述
(2)用阻塞赋值法确定reg型信号b和c

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

代码实现功能为:在clk上升沿到来时,b马上取a的值,c马上取b的值,所实现的电路功能如下:
在这里插入图片描述

  • 关系运算符
    • >(大于运算符)
    • <(小于运算符)
    • >=(大于等于运算符)
    • <=(小于等于运算符)
  • 逻辑运算符
    • &&(与运算符)
    • ||(或运算符)
    • !(取反运算符)
  • 条件运算符
    • ?:(a=(b==c)?d:e;b与c相等(b==c成立),则a=d,否则a=e)
  • 位运算符
    • ~(取反)
    • |(按位或)
    • ^(按位异或)
    • &(按位与)
    • ^~(按位同或)
  • 移位运算符(左侧为操作数,右侧为需要移位的位数)
    • <<(左移,a<<b表示a左移b位,移出位用0填充)
    • >>(右移,a>>b表示a右移b位,移出位用0填充)
      如:
      4’b0011 << 1(4’b0011 左移1位后,其值变为5’b00110)
      1<<6(1左移6位后,其值变为32’b1000000)
      4’b0011 >> 1(4’b0011 右移1位后,其值变为4’b0001)
      4’b1001>>4(4’b0011 右移4位后,其值变为4’b0000)
  • 拼接运算符
    • {}(位拼接运算符)
      格式为:
      {signala[x:y],signalb[z],…}
      可以把某些信号的某些位拼接在一起,中间用,分割。
      如:
      {led[6:0],led[7]}//可以实现流水灯效果
      {a,b[3:0],w,3’b101}//等价于{a,b[3],b[2],b[1],w,1’b1,1’b0,1’b1}
      位拼接可以用重复发来简化表达式,如:
      {4{w}}//等同于{w,w,w,w}
      {b,{3{a,b}}}//等同于{b,a,b,a,b,a,b}
      注意:重复表达式中必须明确指明重复的次数(重复次数必须是常量表达式)
      • 等式运算符
      • ==(等于)
      • !=(不等于)
      • ===(等于)
      • !==(不等于)
      • tips:
        ==当操作数某些位为不定值x或高阻值z时,结果可能为不定值x。
        ===在对操作数进行比较时对某些位的不定值x和高阻值z页进行比较,左右两边的操作数只要完全一致,结果就为1,否则为0
        等式运算符的真值表如下:
        在这里插入图片描述
  • 缩减运算符
    缩减运算是对单个操作数进行与、或、非递推运算,最后的运算结果是1位二进制数。运算过程为:第一步先将操作数的第1位与第2位进行与、或、非运算,第二部将第一步的运算结果与第3位进行与、或、非运算,依此类推,直至最后1位。如:
    reg [3:0] B;
    reg C;
    C = &B;//等价于C=(((B[0]&B[1])&B[2])&B[3])

4. 块语句

  • begin_end语句(顺序块,块内语句顺序执行)
  • fork_join语句(并行块,块内语句并行执行)

顺序块begin_end

格式为:
begin
statement1;
statement2;

end
或者
begin:块名
块内声明语句;
statement1;
statement2;

end
其中,块名,为该块的名字,是一个标识名。
对于顺序快,起始时间为第一条语句开始被执行的事件,结束时间为最后一条语句执行完的时间。
如( 1):
begin
areg=breg;
#10 creg=areg;//两条赋值语句之间延迟10个时间单位
end
(2):

parameter d=50;//声明的是一个参数
reg[7:0] r;//声明r是一个8位寄存器变量
begin//由一系列延迟产生波形
	#d r='h23;
	#d r='hab;
	#d r='h1f;
	#d r='hd3;
	#d ->end_wave;//->表示触发事件end_wave使其翻转
	end

并行块fork_join

并行块的特点:
(1)块内语句同时进行;
(2)块内每条语句的延迟时间是相对于程序流程控制进入到块内的仿真时间而言的;
(3)延迟时间用来给赋值语句提供执行时序;
(4)当按时间顺序执行完最后的雨具,或一个disable语句执行时,程序流程控制跳出该程序块。
并行块格式如下:
fork
statement1;
statement2;

join
或者
fork:块名
块内声明语句;
statement1;
statement2;

join
如:

fork
	#50 r='h23;
	#100 r='hab;
	#150 r='h1f;
	#200 r='hd3;
	#250 ->end_wave;//->表示触发事件end_wave使其翻转
join
此例子与顺序块中的例子(2),生成的波形是一样的。

对于并行块,起始时间对于块内所有的语句是相同的,程序流程控制进入该块的事件即为起始时间,结束时间为时间排序在最后的语句执行结束的时间。
如(3):

fork
	#250 ->end_wave;//->表示触发事件end_wave使其翻转
	#200 r='hd3;
	#150 r='h1f;
	#100 r='hab;
	#50 r='h23;
join

此并行块生成的波形与并行块(2)生成的波形是一样的。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值