没有幻灯片标题 - SOC@Fudan

Download Report

Transcript 没有幻灯片标题 - SOC@Fudan

Verilog HDL
及Modelsim仿真
范益波
Acknowledgment
• This slides is revised from “Verilog HDL基
础语法入门” by夏宇闻
Outline
•
•
•
•
•
•
Verilog 简介
简单的 Verilog HDL 模块
Verilog 语法要点
Verilog 的逻辑值和数据类型
存储器及有限状态机建模
可综合风格的Verilog
什么是verilog
• Verilog是一种硬件设计语言(Hardware
Description Language, HDL)
• 主要用于数字逻辑电路设计
• 跟VHDL类似
Verilog与C语言的关系
• Verilog HDL作为一种高级的硬件描述编程语言,
有着类似C语言的风格。其中有许多语句如:if语
句、case语句等和C语言中的对应语句十分相似。
如果读者已经掌握C语言编程的基础,那么学习
Verilog HDL并不困难,我们只要对Verilog HDL
某些语句的特殊方面着重理解,并加强上机练习
就能很好地掌握它,利用它的强大功能来设计复
杂的数字逻辑电路。
• 但是注意:Verilog是硬件设计语言,跟软件设计
有本质区别
Verilog具有不同的抽象级别
• 系统级(system): 用高级语言结构实现设计模
块的外部性能的模型。
• 算法级(algorithmic): 用高级语言结构实现设计
算法的模型。
• RTL级(Register Transfer Level): 描述数据在
寄存器之间流动和如何处理这些数据的模型。
• 可综合
• 相对高层的描述
• 门级(gate-level): 描述逻辑门以及逻辑门之间
的连接的模型。
模块的抽象及
数字电路的基本设计流程
技术指标:
用文字表示
用算法表示
用高级行为的Verilog模块表示
RTL design
综合前仿真
RTL/功能级:
用可综合的Verilog模块表示
逻辑综合
门级/结构级:
用实例引用的Verilog模块表示
版图布局/物理级:
综合后仿真
用几何形状来表示
布局布线
Verilog的层次性架构
• 一个复杂电路的完整Verilog HDL模型是由若个
Verilog HDL 模块构成的,每一个模块又可以由
若干个子模块构成。
–利用Verilog HDL语言结构所提供的这种功能就可以构
造一个模块间的清晰层次结构来描述极其复杂的大型
设计。
• Verilog HDL行为描述语言作为一种结构化和过程
性的语言,其语法结构非常适合于算法级和RTL
级的模型设计。
Outline
•
•
•
•
•
•
Verilog 简介
简单的 Verilog HDL 模块
Verilog 语法要点
Verilog 的逻辑值和数据类型
存储器及有限状态机建模
可综合风格的Verilog
举例1
例[2.1.1]:
module adder ( count,sum,a,b,cin );
input [2:0] a,b; //声明输出信号equal
input cin;
//声明输入信号
output count;
output [2:0] sum;
assign {count,sum}=a+b+cin;
endmodule
这个例子描述了一个三位的加法器。从例子中可以看
出 整 个 Verilog HDL 程 序 是 嵌 套 在 module 和
endmodule声明语句里的。
举例2
例[2.1.2]:
module compare ( equal,a,b );
output equal;
input [1:0] a,b;
assign equal=(a==b)?1:0;
/*如果两个输入信号相等,输出为1。否则为0*/
endmodule
这个程序描述了一个比较器.在这个程序中
,/*........*/和//.........表示注释部分,注释只
是为了方便程序员理解程序,对编译是不起作用的。
举例3
例[2.1.3]:
module trist1(out,in,enable);
output out;
input in, enable;
mytri tri_inst(out,in,enable);
endmodule
module mytri(out,in,enable);
output out;
input in, enable;
assign out = enable? In : 'bz;
endmodule
上述程序例子通过另一种方法描述了一个三态门。
在这个例子中存在着两个模块:模块trist1调用模块 mytri 的实
例元件。通过这种结构性模块构造可构成特大型模块。
简单举例后的小结
• 整 个 Verilog HDL 程 序 是 嵌 套 在 module 和
endmodule声明语句里的。
• 每个模块要进行端口定义,并说明输入输出口,然
后对模块的功能进行行为逻辑描述。
• Verilog HDL程序是由模块构成的。模块是可以进
行层次嵌套的。
• 除了endmodule(及后面会学到的initial,end等
)语句外,每个语句和数据定义的最后必须有分号
• 可以用/*.....*/和//...对Verilog HDL程序的任
何部分作注释。
模块的测试:基本概念
被测模块
激励和控
制信号
输出响应
和验证
测试模块的常见形式
module t;
reg …;
//被测模块输入/输出变量类型定义
wire…;
//被测模块输入/输出变量类型定义
initial begin …; …; …; end … …//产生测试信号
always #delay begin …; end … …//产生测试信号
Testedmd m
(.in1(ina),
.out2(outb) );
//被测模块的实例引用
initial begin ….; ….; ….
endmodule
end
.in2(inb),
.out1(outa),
//记录输出和响应
测试模块中常用的过程块
initial
always
所有的过程块都
在0时刻同时启
动;它们是并行
的,在模块中不
分前后。
 initial块 只
执行一次。一般
只在测试模块中
使用
 always块 只
要符合触发条件
可以循环执行。
如何描述激励信号
module t;
reg a, b, sel;
wire out;
//引用多路器实例
mux2 m (out, a, b, sel);
//加入激励信号
initial begin a=0; b=1; sel=0;
#10 b=0;
#10 b=1; sel=1;
#10 a=1;
#10 $stop;
end
endmodule
建立时钟
虽然有时在设计中会包含时钟,但时钟通常用在测试模块中。
简单的对称方波时钟:
reg clk;
always begin
#period/2 clk=0;
#period/2 clk=1;
end
仿真工具简介
• Mentor公司的ModleSim
– Learn from the ‘help’ manual of the software
– Learn from some teaching books
• Cadence的NC-Verilog
• Synopsys的VCS
Outline
•
•
•
•
•
•
Verilog 简介
简单的 Verilog HDL 模块
Verilog 语法要点
Verilog 的逻辑值和数据类型
存储器及有限状态机建模
可综合风格的Verilog
Verilog与C的主要不同点
• Verilog 有许多语法规则与 C 语言一致,但与 C
语言有根本的区别:
– 并行性
– 块的含义: initial 块 和 always块
– 两种赋值语句: 阻塞赋值
“=”
非阻塞赋值
“〈= ”
整数和实常数
 整数可以标明位数也可以不标明位数,表示方法:
《位数》‘《基数》《值》
其中《位数》表明该数用二进制的几位来表示
《基数》可以是二(b)、八(O)、十(d)或
十六(h)进制
《数值》可以是所选基数的任何合法的值包括
不定值 x 位和高阻值 z。
如:64‘hff01 8’b1101_0001 ‘h83a
 实常数可以用十进制表示也可以用科学浮点数表示,如:
32e-4 (表示0.0032) 4.1E3( 表示 4100)
标识符
• 所谓标识别符就是用户为程序描述中的Verilog
对象所起的名字。
• 标识符必须以英语字母(a-z, A-Z)起头,或者
用下横线符( _ )起头。其中可以包含数字、$
符和下横线符。
• 标识符最长可以达到1023个字符。
• 模块名、端口名和实例名都是标识符。
• Verilog语言是大小写敏感的,因此sel 和 SEL
是两个不同的标识符。
合法和非法标识符举例
• 合法的:
–shift_reg_a
–busa_index
–bus263
• 非法的:
– 34net //不能用数字开头
– a*b_net //不能含有非字母符号*
– n@263 //不能含有非字母符号@
$<标识符>
 ‘$’ 符号表示 Verilog 的系统任务和函数
 常用的系统任务和函数有下面几种:
1) $time
//找到当前的仿真时间
2) $display, $monitor //显示和监视信号值的变化
3) $stop
//暂停仿真
4) $finish
//结束仿真
-------------------------------------------------------
例:
initial $monitor($time,,”a=%b, b=%b”, a, b);
//每当a 或b值变化时该系统任务都显示当前的仿真时刻并分别用二进制
和十六进制显示信号a和 b的值
特殊符号 “#”
特殊符号 “#” 常用来表示延迟:
 在过程赋值语句时表示延迟。
例 : initial begin #10 rst=1; #50
rst=0; end
 在门级实例引用时表示延迟。
例:not #1 not1(nsel, sel);
and #2 and2(a1, a, nsel);
编译引导语句




编译引导语句用主键盘左上角小写键 “ ` ” 起头
用于指导仿真编译器在编译时采取一些特殊处理
编译引导语句一直保持有效,直到被取消或重写
`resetall 编译引导语句把所有设置的编译引导恢复到
缺省状态
 常用的编译引导有:
a) `define
b) `include
c) `timescale
……..
编译引导语句: `define
 使用`define 编译引导能提供简单的文本替代功能
`define <宏名> <宏文本>
在编译时会用宏文本来替代源代码中的宏名。
 合理地使用`define可以提高程序的可读性
举例说明:
`define on 1’b1
`define off 1’b0
`define and_delay #3
在程序中可以用有含义的文字来表示没有意思的数码提高了程序
的可读性,在程序中可以用 `on, `off, `and_delay 分别表
示 1,0,和 #3 。
编译引导语句: `include
 使用`include 编译引导,在编译时能把其指定的整
个文件包括进来一起处理
举例说明:
`include “global.v”
`include “parts/counter.v”
`include “../../library/mux.v”
 合理地使用`include 可以使程序简洁、清晰、条理
清楚、易于查错。
编译引导语句: `timescale
`timescale 用于说明程序中的时间单位和仿真精度
举例说明: `timescale 1ns/100ps
 `timescale 语句必须放在模块边界前面
举例说明: `timescale 1ns/100ps
module MUX2_1(out,a,b,sel);
……
not #1 not1(nsel, sel);
and #2 and1(a1, a, nsel);
……
endmodule
 尽可能地使精度与时间单位接近,只要满足设计的
实际需要就行。
举例说明:在上例中所有的时间单位都是1ns的整数倍
Outline
•
•
•
•
•
•
Verilog 简介
简单的 Verilog HDL 模块
Verilog 语法要点
Verilog 的逻辑值和数据类型
存储器及有限状态机建模
可综合风格的Verilog
Verilog 的四种逻辑值
buf
0
buf
0、低、伪、逻辑低、地、VSS、负插入
1、高、真、逻辑高、电源、VDD、正插入
1
buf
X
X、不确定:逻辑冲突无法确定其逻辑值
Z
HiZ、高阻抗、三态、无驱动源
bufif1
0
Verilog主要的数据类型
 Nets
表示器件之间的物理连接, 称为网络连接类型;一般
用wire来表示
 Register
表示抽象的储存单元,称为寄存器/变量类型;一般用
reg来表示
 Parameter
表示运行时的常数,称为参数类型
如何选择正确的数据类型?
 输入口(input)可以由寄存器或网络连接驱
动,但它本身只能驱动网络连接。
 输出口 (output)可以由寄存器或网络连接驱
动,但它本身只能驱动网络连接。
 输入/输出口(inout)只可以由网络连接驱动
,但它本身只能驱动网络连接。
 如果信号变量是在过程块 (initial块 或
always块)中被赋值的,必须把它声明为寄存
器类型变量
举例说明数据类型的选择
模块DUT的边界
module top;
wire y;
reg a, b;
DUT u1(y,a,b);
initial
begin
a = 0; b = 0;
#10 a =1; ….
end
endmodule
net
net/register
net/register
输入口
输出口
net
net
inout
输出/入口
module DUT(Y, A, B_);
output Y;
input A,B:
wire Y, A, B;
and (Y, A, B);
endmodule
net
选择数据类型时常犯的错误
 在过程块中对变量赋值时,忘了把它定义为
寄存器类型(reg)或已把它定义为连接类
型了(wire)
 把实例的输出连接出去时,把它定义为寄存
器类型了
 把模块的输入信号定义为寄存器类型了。
主要的数据类型:parameters



常用参数来声明运行时的常数。
可用字符串表示的任何地方,都可以用定义的参数来代替。
参数是本地的,其定义只在本模块内有效。
举例说明:
module md1(out,in1,in2);
…..
parameter cycle=20, prop_del=3, setup=cycle/2-prop_del,
p1=8, x_word=16’bx,
file = “/user1/jmdong/design/mem_file.dat”;
wire [p1:0] w1; //用参数来说明wire 的位宽
….
initial begin $open(file); ……. #20000 display(“%s”,file); $stop end
….
endmodule
练习1
• 学习modelsim的用法
• 实现例1,2,3并编写相应的测试文件,在
modelsim中仿真实现
• 语法要点
– 整数表示方法
– 标示符
– 编译引导语句
• Verilog逻辑值
• Verilog 的数据类型
Outline
•
•
•
•
•
•
Verilog 简介
简单的 Verilog HDL 模块
Verilog 语法要点
Verilog 的逻辑值和数据类型
存储器及有限状态机建模
可综合风格的Verilog
寄存器阵列
举例说明:
integer NUMS [7:0];
time t_vals [3:0];
// 8个整型变量的寄存器阵列
//4个时间变量的寄存器阵列
数据类型为 reg 的阵列常称为存储器(即 memory):
reg [15:0] MEM [0:1023]; // 1K x 16 位的存储器
注意:这种方式写的存储器面积非常大
reg [7:0] PREP [‘hfffe : ‘hffff]; // 2 x 8 位的存储器
可以用参数来表示存储器的大小:
parameter wordsize = 16;
parameter memsize = 1024;
reg [wordsize-1:0] MEM3[memsize-1:0];
语法详细讲解
存储器建模
存储器建模必须注意以下两个方面的问题:
 声明存储器容量的大小。
 明确对存储器访问操作的权限。
例如:指出可以对存储器做以下哪几种操作:
1)只读
2)读写
3)同步读写
4)多次读,同时进行一次写
5)多次同步读写,同时提供一些方法保证一致性
语法详细讲解
简单 ROM 建模
`timescale 1ns/10ps
module myrom(read_data,addr,read_en_);
input read_en_;
input [3:0] addr;
output [3:0] read_data;
reg [3:0] read_data;
reg [3:0] mem [0:15];
initial
$readmemb(“my_rom_data”,mem);
always @ (addr or read_en_)
if(!read_en_)
read_data=mem[addr];
endmodule
ROM的数据存储
在另外的一个独
立的文件中
my_rom_data
0000
0101
1100
0011
1101
0010
0011
1111
1000
1001
1000
0001
1101
1010
0001
1101
语法详细讲解
简单ROM建模
上页所示的ROM模型说明:
 如何在Verilog中用二维的寄存器组来定义存储器。
 ROM中的数据保存在一个独立的文件中,如上页的右边的
虚线方框所示。
 这是一种保存ROM数据的通用的方法,它可以使数据和ROM
模型分开。
语法详细讲解
简单RAM建模
`timescale 1ns/1ns
module mymem(data,addr,read,write);
inout [3:0] data;
inout [3:0] addr;
input read, write;
reg [3:0] memory [0:15]; //4 bits, 16 words
//从存储器读出到总线上
assign data=read? memory[addr]:4’bz;
//从总线写入存储器
always @ (posedge write)
memory[addr]=data;
endmodule
语法详细讲解
简单RAM建模
RAM模型比ROM模型稍微复杂:
 它必须具有读写能力;
 进行读写时通常使用相同的数据总线;
 需要新技术来处理双向总线;
 当读信号无效时,RAM模型与总线脱离,如果此时写
信号也无效,总线无驱动源,则总线进入高阻状态,
这就避免了RAM中的读写竞争。
 上页的 RAM 模块是可综合的,但综合出来是一大堆寄存器
,占比较大的面积,经济上不太合算。
语法详细讲解
存储量可变的只读存储器建模
例:
module scalable_ROM (mem_word, address);
parameter addr_bits=8;
//size of address bus
parameter wordsize=8;
//width of a word
parameter words=(1<<addr_bits); //size of mem
output [wordsize:1] mem_word;
input [addr_bits:1] address;
//word of memory
//address bus
reg [wordsize:1] mem [0 : words-1]; //mem declaration
//output one word of memory
wire [wordsize:1] mem_word=mem[address];
endmodule
语法详细讲解
存储量可变的只读存储器建模
上述的例子演示了怎样通过设置字长和地址位数来编
写 只读存储器的行为模块。
[注意] !! 在上例中,存储字的范围从0开始的,而不是
从1开始,这是因为存储单元是直接通过地址线寻址
定位的。
同样地,也可以用下面的方法来定义存储器和寻址:
//存储器地址 从1 开始
//地址一个一个地增加直到包含了每个地址对应的存储器
reg [wordsize:1] mem [1:words];
wire [wordsize:1] mem_word = mem[address+1];
语法详细讲解
存储器的加载
可以在初始化块中用一个循环或系统任务把初始数据存入存
储器的每个单元。
 使用循环把值赋给存储器数组。
for(i=0;i<memsize;i=i+i)
// initialize memory
mema[i]={wordsize{1’b1}};
 调用$readmem系统任务。
//从文件 mem_file.txt 中, 把初始数据存入存储器(mem)的每个单元
$readmemb(“mem_file.txt”,mem);
注意:上面两项必须写 在initial 块中,加载这些初始化数据不需要
时间。
语法详细讲解
怎样使用双向口
使用inout关键字声明端口为双向口。
inout [7:0] databus;
使用双向口必需遵循下面的规则:
 inout口只能声明为网络连接类型, 不允许把它声明为寄
存器类型。(所以仿真器能确定多个驱动源的最终值。)
 在设计中,每次只能从一个方向来驱动inout口。
例如:当使用总线读RAM中的数据时,如果同时又向
RAM模型的双向数据总线写数据,就会产生逻辑竞争,导
致总线数据无法确定。所以必须为inout口设计控制逻辑
,只有这样才能保证正确的操作。
语法详细讲解
双向口建模
使用连续赋值为双向口建模:
en_a_b
b1
bus_a
bus_b
b2
en_b_a
module bus_xcvr
(bus_a,bus_b,en_a_b,en_b_a);
inout bus_a,bus_b;
input en_a_b,en_b_a;
assign bus_b=en_a_b? bus_a:’bz;
assign bus_a=en_b_a? bus_b:’bz;
//结构模块逻辑
endmodule
当en_a_b=1时,bus_a的
值传到bus_b上
当en_b_a=1时,bus_b的
值传到bus_a上
控制信号 en_a_b 和
en_b_a 在时间上分开
有限状态机
datain = 0
module exp(out, datain, clk, rst);
input clk, rst, datain;
output out; reg out;
状态变量
reg state;
0
always @(posedge clk or posedge rst)
if(rst) {state, out}=2’b00;
else
datain = 1
case语句
case(state)
1’b0: begin
out=1’b0;
1
if(!datain) state=1’b0;
clk
rst
else state=1’b1;
end
1’b1 begin
0 11 01 识别11序列
out=datain;
转到下一个状态
state=1’b0;
end
default: {state, out}=2’b00;
默认状态指针
endcase
endmodule
out
有限状态机讨论
• 在过程块中可以使用一个时钟沿和 case 语
句来描述一个显式状态机。
• 必须指定一个状态变量,来记录状态机的
状态。
• 要改变当前的状态,必须改变状态变量的
值, 其改变要与时钟沿同步。
• 写得比较好的状态机常为不应产生的条件
规定一个默认动作。
Outline
•
•
•
•
•
•
Verilog 简介
简单的 Verilog HDL 模块
Verilog 语法要点
Verilog 的逻辑值和数据类型
存储器及有限状态机建模
可综合风格的Verilog
两种可综合风格的
Verilog建模类型
• 组合逻辑:
–任何时候,如果输出信号直接由当前的输入信
号的组合决定,则此逻辑为组合逻辑。
• 时序逻辑:
–如果逻辑中具有记忆功能,则此逻辑为时序逻
辑。在任何给定的时刻,如果输出不能完全由
输入信号确定,则此逻辑具有记忆功能。
不能综合的 Verilog结构
•
•
Initial
循环语句:
–
–
–
–
•
Repeat
Forever
While
for 的非结构用法
一部分数据类型
–
–
–
–
–
–
•
Event
Real
Time
UDPs
fork…join 块
wait
过程连续赋值语句
–
–
•
assign 和 deassign
force 和 release
部分操作符
–
–
===
!= =
过程块
• 由输入信号中任意一个电平发生变化所引起的过程块,可
以通过综合产生组合逻辑。该过程块称为组合块。
always @(a or b)
y=a&b;
// 实现与门
• 由控制信号的跳变沿(下降沿或上升沿)启动的过程块通
过综合可以生成同步逻辑。该过程块称为同步块。
always @(posedge clk)
q<=d;
//实现 D 触发器
always @(posedge clk or negedge rst_)
if(!rst_)
q<=0;
else
q<=d;
同步寄存器示例1
在下面的例子中,rega 仅用作临时存储器,因此在综合时它将被优化掉。
module ex1reg(d, clk, q);
input d, clk;
output q;
reg q, rega;
always @(posedge clk)
begin
rega = 0;
if(d) rega = 1;
q = rega;
end
endmodule
同步寄存器示例2
在下面的例子中,用两个always块,它们的触发条件是相同的:即
用同一时钟沿来处理两个存储元素,这样就可以使综合器在综合过
程中保留rega,使它不被优化掉。
module ex2reg(d, clk, q);
input d, clk;
output q;
reg q, rega;
always @(posedge clk)
begin
rega=0;
if(d) rega=1;
end
always @(posedge clk)
q = rega;
endmodule
组合寄存器示例1
y和rega 不断被赋新值(因为语句中有else rega = 0;),综合出的电路是一
个纯组合逻辑。 rega 是临时变量,在综合中会被优化掉。
module ex3reg(y, a, b, c);
input a, b, c;
output y;
reg y, rega;
always @(a or b or c)
begin
if(a&b)
rega=c;
else
rega=0;
y=rega;
end
endmodule
组合寄存器示例2
在下面的例子中,rega 只是有时被赋新值 (没有else 语句,rega在条件
不符合时保持原值);因此综合出来的是一个以 y 作为输出的锁存器。
rega 是临时变量,在综合中会被优化掉。
moudule ex4reg(y, a, b, c);
input a, b, c;
output y;
reg y, rega;
always @(a or b or c)
begin
if(a&b)
rega=c;
y=rega;
end
endmodule
完整的电平敏感列表
module sens(q, a, b, sl);
input a, b, sl;
output q;
reg q;
always @(sl or a or b)
begin
if(!sl)
q=a;
else
q=b;
end
endmodule
不完整电平敏感列表
在电平敏感列表中最好包括所有的输入。对于不完整的列表,
不同的综合工具处理的方法不同
module sens(a, q, b, sl);
input a, b, sl;
output q;
reg q;
always @(sl)
begin
if(!sl)
q=a;
else
q=b;
end
endmodule
连续赋值
用连续赋值语句表达的是:任何一个输入的改变都将立即导致输出
更新;与理想的与、或、非门的线路连接一致。
module orand(out, a, b, c, d, e);
input a, b, c, d, e;
output out;
assign out=3&(a|b)&(c|d);
endmodule
case条件语句
module comcase(a, b, c, d, e);
input a, b, c, d;
output e;
reg e;
always @(a or b or c or d)
case ({a,b})
2’b11: e=d;
2’b10: e=~c;
2’b01: e=1’b0;
2’b00: e=1’b1;
endcase
endmodule
if条件语句
module compif(a, b, c, d, e);
input a, b, c, d;
output e;
reg e;
always @(a or b or c or d)
if(a&b)
e=d;
else if (a&~b)
e=~c;
else if (~ a&b)
e=1’b0;
else if (~a&~b)
e=1’b1;
endmodule
不完整条件语句1
module inccase(a, b, c, d, e);
input a, b, c, d;
output e;
reg e;
always @(a or b or c ord)
case ({a,b})
2’b11: e=d;
2’b10: e=~c;
endcase
Endmodule
当 a 为 0 时,没有值赋给 e。因此,e 将保存原来的值,直到 a 变为 1。
此行为与锁存器的特性相同。
不完整条件语句2
module incpif(a, b, c, d, e);
input a, b, c, d;
output e;
reg e;
always @(a or b or c or d)
if (a&b)
e=d;
else if(a&~b)
e=~c;
endmodule
带有缺省项的完整条件语句
module comcase(a, b, c, d, e);
input a, b, c, d;
output e;
reg e;
always @(a or b or c or d)
case ({a,b})
2’b11: e=d;
2’b10: e=~c;
default: e=‘bx;
endmodule
虽然没有定义所有可能的选择,但为没有定义的选择定义了缺省的行为。
因此,它们都是纯的组合逻辑,并没有产生额外的锁存器。
带有缺省项的完整条件语句
module compif(a, b, c, d, e);
input a, b, c, d;
output e;
reg e;
always @(a or b or c or d)
if (a&b)
e=d;
else if (a&~b)
e=~c;
else
e=‘bx;
endmodule
怎样产生锁存器
在 always 块中,如果没有规定所有的条件,则会产生锁存器。在下
面的例子中,当 enable 为低电平时,没有定义怎样处理 q 和 data,
因此 data 的值将会被保存下来。综合器必须使用存储元件来编译此
逻辑。
module latch(q, data, enable);
input data,enable;
output q;
reg q;
always @(enable or data)
if(enable)
q=data;
endmodule
阻塞与非阻塞(赋值方式)
赋值的类型的选择取决于建模的逻辑类型
 在时序块的 RTL 代码中使用非阻塞赋值。
非阻塞赋值在块结束后才完成赋值操作,此赋值方式可以避免在仿真
出现冒险和竞争现象。
 在组合的 RTL 代码中使用阻塞赋值。
使用阻塞方式对一个变量进行赋值时,此变量的值在在赋值语句执行
完后就立即改变。
阻塞与非阻塞(赋值方式)
使用非阻塞赋值方式进行赋值时,各个赋值语句同步执行;因此,
通常在一个时钟沿对临时变量进行赋值,而在另一个时钟沿对其
进行采样。
下面的模块综合为触发器,
其中采用了阻塞赋值方式:
module bloc(clk,a,b);
input clk, a;
output b; reg b;
reg y;
always @(posedge clk)
begin
y=a;
b=y;
end
endmodule
下面的模块综合为两个触发器
,其中采用了非阻塞赋值方式:
module nonbloc(clk,a,b);
input clk, a;
output b; reg b;
reg y;
always @(posedge clk)
begin
y<=a;
b<=y;
end
endmodule
阻塞与非阻塞(赋值方式)
上面的两个例子的综合的结果不同,左边的例子使用了阻塞赋值方式,
综合器将其综合为一个触发器。右边的例子使用了非阻塞赋值方式,
综合器将其综合为两个触发器,y 将出现在综合列表中,作为第二个
触发器的输入。综合结果如下所示:
a
clk
b
a
clk
y
b
复位建模
复位是可综合风格代码的重要组成部分,通常在有限状态机中使用复
位建模。
同步复位:
module sync(q,ck,r,d);
input ck, d, r;
output q; reg q;
always @(negedge ck)
if(r)
q<=0;
else
q<=d;
endmodule
同步块中的异步复位:
module async(q,ck,r,d);
input ck, d, r;
output q; reg q;
always @(negedge ck or
posedge r)
if(r)
q<=0;
else
q<=d;
endmodule
复位建模
在下面的例子中,使用了一个独立的异步复位来实现异步复位。
module async(q, ck, r, d);
input ck, d, r;
output q; reg q;
always @(negedge ck)
if(!r) q<=d;
always @(posedge r)
q<=0;
endmodule
不提倡使用上述风格的异步复位代码,上述代码是不可综合的。在
仿真时,若 r 和 ck 同时在同一个时间单元中改变,则结果是不确定
的。
锁存器复位
下面的例子演示了更加复杂的复位建模。其中的敏感列表是完全的,
因为这是一个锁存器。
module latch(q, enable, set, clr, d);
input enable, d, set, clr;
output q; reg q;
always @(enable or set or clr or d)
begin
if(set)
q<=1;
else if (clr)
q<=0;
else if (enable)
q<=d;
end
endmodule
练习2
•
•
•
•
•
•
•
•
•
存储器
有限状态机
同步寄存器
组合寄存器
电平敏感列表
Case 语句
If 语句
阻塞与非阻塞
复位方式