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): 描述逻辑门以及逻辑门之间的连接的模型。
模块的抽象及 数字电路的基本设计流程 技术指标: RTL/功能级: 门级/结构级: 版图布局/物理级: RTL design 综合前仿真 逻辑综合 综合前仿真 综合后仿真 布局布线 技术指标: 用文字表示 用算法表示 用高级行为的Verilog模块表示 RTL/功能级: 用可综合的Verilog模块表示 门级/结构级: 用实例引用的Verilog模块表示 版图布局/物理级: 用几何形状来表示
Verilog的层次性架构 一个复杂电路的完整Verilog HDL模型是由若个 Verilog HDL 模块构成的,每一个模块又可以由若干个子模块构成。 利用Verilog HDL语言结构所提供的这种功能就可以构造一个模块间的清晰层次结构来描述极其复杂的大型设计。 Verilog HDL行为描述语言作为一种结构化和过程性的语言,其语法结构非常适合于算法级和RTL级的模型设计。
Outline Verilog 简介 简单的 Verilog HDL 模块 Verilog 语法要点 Verilog 的逻辑值和数据类型 存储器及有限状态机建模 可综合风格的Verilog
module adder ( count,sum,a,b,cin ); 举例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 output out; input in, enable; mytri tri_inst(out,in,enable); 例[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); assign out = enable? In : 'bz; 上述程序例子通过另一种方法描述了一个三态门。 在这个例子中存在着两个模块:模块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), .in2(inb), .out1(outa), .out2(outb) ); //被测模块的实例引用 initial begin ….; ….; …. end //记录输出和响应 endmodule
测试模块中常用的过程块 所有的过程块都在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 Cadence的NC-Verilog Synopsys的VCS 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 的系统任务和函数 常用的系统任务和函数有下面几种: $time //找到当前的仿真时间 $display, $monitor //显示和监视信号值的变化 $stop //暂停仿真 $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 编译引导语句把所有设置的编译引导恢复到缺省状态 常用的编译引导有: `define `include `timescale ……..
编译引导语句: `define 使用`define 编译引导能提供简单的文本替代功能 在编译时会用宏文本来替代源代码中的宏名。 合理地使用`define可以提高程序的可读性 举例说明: `define on 1’b1 `define off 1’b0 `define and_delay #3 在程序中可以用有含义的文字来表示没有意思的数码提高了程序 的可读性,在程序中可以用 `on, `off, `and_delay 分别表 示 1,0,和 #3 。
编译引导语句: `include `include “global.v” `include “parts/counter.v” 举例说明: `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 的四种逻辑值 1 X Z buf bufif1 0、低、伪、逻辑低、地、VSS、负插入 1 X Z buf bufif1 0、低、伪、逻辑低、地、VSS、负插入 1、高、真、逻辑高、电源、VDD、正插入 X、不确定:逻辑冲突无法确定其逻辑值 HiZ、高阻抗、三态、无驱动源
Verilog主要的数据类型 Nets 表示器件之间的物理连接, 称为网络连接类型;一般用wire来表示 Register Parameter 表示运行时的常数,称为参数类型
如何选择正确的数据类型? 输入口(input)可以由寄存器或网络连接驱动,但它本身只能驱动网络连接。 输出口 (output)可以由寄存器或网络连接驱动,但它本身只能驱动网络连接。 输入/输出口(inout)只可以由网络连接驱动,但它本身只能驱动网络连接。 如果信号变量是在过程块 (initial块 或 always块)中被赋值的,必须把它声明为寄存器类型变量
举例说明数据类型的选择 module top; wire y; reg a, b; DUT u1(y,a,b); initial begin end endmodule 模块DUT的边界 输入口 输出口 输出/入口 net net/register inout module DUT(Y, A, B_); output Y; input A,B: wire Y, A, B; and (Y, A, B); endmodule
选择数据类型时常犯的错误 在过程块中对变量赋值时,忘了把它定义为寄存器类型(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逻辑值 Verilog 的数据类型
Outline Verilog 简介 简单的 Verilog HDL 模块 Verilog 语法要点 Verilog 的逻辑值和数据类型 存储器及有限状态机建模 可综合风格的Verilog
寄存器阵列 integer NUMS [7:0]; // 8个整型变量的寄存器阵列 举例说明: integer NUMS [7:0]; // 8个整型变量的寄存器阵列 time t_vals [3:0]; //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 my_rom_data 0000 0101 1100 0011 1101 0010 1111 1000 1001 0001 1010 ROM的数据存储在另外的一个独立的文件中
上页所示的ROM模型说明: 语法详细讲解 简单ROM建模 如何在Verilog中用二维的寄存器组来定义存储器。
语法详细讲解 简单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; //word of memory input [addr_bits:1] address; //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开始,这是因为存储单元是直接通过地址线寻址定位的。 同样地,也可以用下面的方法来定义存储器和寻址: reg [wordsize:1] mem [1:words]; //存储器地址 从1 开始 //地址一个一个地增加直到包含了每个地址对应的存储器 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 bus_a bus_b 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 在时间上分开
有限状态机 1 1 识别11序列 datain = 0 module exp(out, datain, clk, rst); 1 datain = 0 datain = 1 module exp(out, datain, clk, rst); input clk, rst, datain; output out; reg out; reg state; always @(posedge clk or posedge rst) if(rst) {state, out}=2’b00; else case(state) 1’b0: begin out=1’b0; if(!datain) state=1’b0; else state=1’b1; end 1’b1 begin out=datain; state=1’b0; default: {state, out}=2’b00; endcase endmodule 状态变量 case语句 1 识别11序列 clk rst 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) //实现 D 触发器 q<=d; always @(posedge clk or negedge rst_) if(!rst_) q<=0; else
同步寄存器示例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 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 y b b a clk clk
复位建模 复位是可综合风格代码的重要组成部分,通常在有限状态机中使用复位建模。 同步复位: 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 语句 阻塞与非阻塞 复位方式