一、 模块
每一个模块代表着一个具有特定功能的电路,,一个电路是由很多模块组合而成,因此一个模块设计只是一个系统设计中某个层次的设计。模块的调用对应的是数字电路中某个功能的调用,电路是由多个模块构成的,体现在模块直接的调用。
1. 模块的基本结构
(1) 端口定义:
端口是模块与外界或其他模块沟通的信号线。模块的端口类型有三种,分别是输入端口(input)、输出端口(output)和双向端口(inout)。
“模块端口列表” 列出了模块具有的外部可见的端口,该“模块端口列表”内的每一个端口都代表着一个模块端口。
有些module不包含端口。例如,在仿真平台的顶层模块中,其内部已经实例化了,所有的设计模块和激励模块是封闭的系统,没有输入和输出。一般这种没有端口的模块用于仿真的,不用作实际电路设计。
(2)数据类型说明
数据类型包括wire、reg、memory和parameter等,一般来说,module的input缺省定义为wire类型;output信号可以用wire类型,也可以用reg类型(条件是在always或initial语句块中被赋值);
inout一般为tri(三态线)类型,表示具有 双向输入输出功能。
(3) 参数说明:使用parameter定义
(4) 逻辑功能描述: 用来产生各种逻辑电路(主要有组合逻辑电路和时序逻辑电路)
二、 模块调用
被调用的模块需要将模块的输入和输出信号连接在调用模块中。有两种模块调用端口对应方式,即端口位置对应方式和端口名称对应方式。
(1) 端口位置对应方式
模块名<参数值列表> 实例名(<信号名1>,<信号名2>,... ,<信号名n>);
实例化端口与外部信号端子
(2) 端口名称对应方式
模块名<参数值列表> 实例名(.端口名1(<信号名1>),.端口名2(<信号名2>),... ,.端口名n(<信号名n>));
举栗子
module halfadder(a,b,s,c); //半加器模块
input a,b;
output c,s;
assign s= a^b;
assign c=a&b;
endmodule
module fulladder(p,q,ci,co,sum); //全加器模块(端口位置对应方式)
input p,q,ci;
output co,sum;
wire w1,w1,w3;
halfadder U1(p,q,w1,w2);
halfadder U1(ci,w1,sum,w3);
or U3(co,w2,w3);
endmodule
or
module fulladder(p,q,ci,co,sum); //全加器模块(端口名称对应方式)
input p,q,ci;
output co,sum;
wire w1,w1,w3;
halfadder U1(a.(p),b.(q),s.(w1),c.(w2));
halfadder U1(a.(ci),b.(w1),s.(sum),c.(w3));
or U3(co,w2,w3);
endmodule
三、 数据流描述
数据流建模是一种使用verilog HDL连续赋值语句,利用运算符表达式形式,对门级建模电路进行verilog HDL程序设计的方式,可提高门级建模的效率。
1. 连续赋值语句(assign)
连续性赋值语句是数据流建模的基本语句,用于对线网(wire)进行赋值。
连续性赋值语句,只要输入端操作数的值发生变化,该语句就重新计算并刷新赋值结果,通常可以使用连续性赋值语句来描述组合逻辑电路,而不需要用门级电路和互联线。
连续赋值的目标主要是标量线网和向量线网。不能是标量或向量寄存器。标量线网如“wire a,b;” ,向量线网如“wire [3:0] a,b;”。
举栗子
1. 数据流描述设计1位全加器(将两个加数和进位数相加就得到最后结果)
module adder(out,a,b,cin)
input a,b;
input cin;
output [1:0] out;
assign out=a+b+cin;
endmodule
2. 数据流描述设计4位全加器电路
module adder4(out,sum,a,b,cin)
input[3:0] a,b;
input cin;
output out;
output [3:0] sum;
wire cout,cin;
wire [3:0] a,b,sum;
assign {cout,sum}=a+b+cin;
endmodule
3. 数据流描述设计16位全加器电路
module adder16(out,a,b,cin)
input [15:0] a,b;
input cin;
output cout;
output [15:0] sum;
assign {cout,sum}=a+b+cin;
endmodule
只需要修改位宽,有时候只需要对程序做少量修改,就可以描述一个复杂程度很高的电路。
连续性赋值语句使用注意事项:
(1)赋值目标只能是线网类型wire,或者是标量向量或向量线网的拼接,而不能是标量或向量寄存器。
(2)连续性赋值语句在连续性赋值过程中总是处于激活状态,只要赋值语句右边表达式中任一一个变量有变化,表达式就立即被计算,计算结果立即赋给左边信号。
(3) 连续性赋值语句不能出现在过程块中
(4) 多个连续性赋值语句之间是并行计算关系,与位置顺序无关
2. 运算符
(1) 算术逻辑符(+、- 、 * 、/ 、%(取模))
(2)关系运算符(大于(>)、小于(<) 、大于等于(>=) 、小于等于(<=))
(3) 相等运算符(等于(==)、不等于(!=) 、全等(===)、不全等(!==))
(4) 逻辑运算符(&& 、 || 、!)
(5)按位运算符(~ 、&、| 、^ (按位异或)、 ^~(按位同或)))
(6)规约运算符(& 、| 、^ 、~& 、~| 、~^ 、^~)
(7) 移位运算符(<<(左移)、>>(右移))
(8)条件运算符(<条件表达式>?<表达式1>:<表达式2>)
(9) 连接和复制运算符
四、 行为级描述
行为级描述是HDL语言特有的一种对于电路的描述方式,通过对于电路行为的计算机程序语言设计,综合工具可以生成相应的由基本单元构成的数字电路。
行为级描述是否正确的唯一标准是综合后生成的电路是否正确,常用于复杂数字逻辑系统的某种特定功能电路的设计中,例如计数器、移位寄存器、序列产生器等,同时行为建模可以用来生成仿真测试信号,对已设计的模块进行检测。
1. 过程语句
过程块是由过程语句所组成的。过程语句有两种,分别是initial过程语句和always过程语句。
(1) initial过程语句
initial过程语句通常用于仿真模块中对激励向量的描述、赋初值、信号监视或用于给寄存器变量赋初值。
initial语句实际上在信号初始化时没有任何意义的,因为在数字电路中,实现初始化的是全局复位信号。系统在上电后的初始状态是由复位信号决定的,与初始状态没有关系。
(2)always过程语句和敏感事件表
(3) 过程语句使用中信号类型的定义
在大多数高级程序语句中都是在过程中使用,它既可以描述时序逻辑电路也可以描述组合逻辑电路,在过程语句(initial和always)中,被赋值信号必须定义为“reg”类型。
2. 语句块
当语句数超过一条时,需要采用语句块,语句块就是以块标识符begin....end 或 fork...join界定的一组行为描述语句。语句块相当于给块中的这组行为描述语句进行打包处理,使之在形式上与上一条语句相一致。
(1)串行语句块 : 关键字begin...end, 其中的语句是按照顺序执行,可以用于可综合电路程序和仿真测试程序。
(2)并行语句块: 关键字fork....join, 只能用于仿真测试程序,不能用于可综合电路程序
3. 过程赋值语句
过程块中的赋值语句被称为过程赋值语句。过程赋值语句是在initial语句或always语句内赋值。过程语句有阻塞赋值语句和非阻塞赋值语句两种。
(1) 阻塞赋值语句 “=”
变量=表达式
(2) 非阻塞赋值语句 “<=”
变量<= 表达式
4. 条件分支语句
HDL的条件分支语句有两种: if条件分支语句和case条件分支语句。这俩种条件分支语句可以用于综合电路设计和测试仿真程序。
5. 循环语句
HDL中规定了四种循环语句,分别是forever、repeat、while和for循环语句。多用于测试仿真程序。
五、 测试与仿真
Testbench的主要功能:
(1)提供激励 信号
(2)正确实例化DUV
(3)将仿真数据显示在终端或者存在文件 ,也可以显示在波形窗口中以供分析检查
六、 组合逻辑电路
在组合逻辑电路中,任意时刻的输出只取决于该时刻的输入变量的状态,而与以前时刻的输入状态无关
1. 数字加法器
2. 数据比较器
3. 数据选择器
4. 数据分配器
5. 数据编码器
6. 数据效验器
七、时序电路设计
时序逻辑电路与组合逻辑电路不同,在时序逻辑电路中,输出信号不仅仅取决于当时的输入信号,还取决于电路原来存储的状态。
由于时序逻辑电路的输出信号不仅与当前的输入信号有关,而且还与电路原来的状态有关,因此,电路中必须包含存储电路,由它将某一时刻之前的电路状态保存下来,存储电路可以由触发器来构成。
1. 触发器
2. 计数器
3. 移位寄存器
4. 信号产生器
5. 有限状态机