语法要点详细讲解 有关测试模块编写的语法 ; 语法的高级部分: 函数、任务、文件、存贮器建立模型、 双向总线、UDP、综合指令。。。。

Slides:



Advertisements
Similar presentations
高级服务器设计和实现 1 —— 基础与进阶 余锋
Advertisements

阻塞操作. 在 linux 里,一个等待队列由一个 wait_queue_head_t 类型的结构来描述 等待队列的初始化: static wait_queue_head_t testqueue; init_waitqueue_head(&testqueue);
Introduction to Verilog
实验四 利用中规模芯片设计时序电路(二).
基于解释性语言的手机跨平台架构 Sloan Yi. Qt MTK.
Oracle数据库 Oracle 子程序.
Verilog HDL 及Modelsim仿真
在PHP和MYSQL中实现完美的中文显示
Greatest Common Divisor ---最大公约数
Chapter 5 Verilog 硬體描述語言
Chapter 5 Verilog硬體描述語言
Verilog HDL 数字系统设计及实践 第5章 时序逻辑建模.
Ch01-2 Verilog語法 資料流(DataFlow)設計 行為(Behavior)設計
第17章 Verilog中的高级结构 学习内容: 任务和函数的定义和调用 怎样使用命名块 怎样禁止命名块和任务 有限状态机(FSM)及建模.
EDA技术 廖义奎.
管理信息结构SMI.
Verilog硬件描述语言基础.
走进编程 程序的顺序结构(二).
辅导课程六.
网络常用常用命令 课件制作人:谢希仁.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
以ISI平台为例,为您演示一下如何在Endnote文献中查看该文献的References
Verilog HDL 基础语法入门.
语法进阶.
时序电路设计 刘鹏 浙江大学信息与电子工程系 Apr. 24, 2011 EE141
动态规划(Dynamic Programming)
CPU结构和功能.
精简指令集(RISC)CPU的构造原理和设计方法
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
14.2 时序逻辑电路的分析 概述 时序逻辑电路是由存储电路和组合逻辑电路共同组成的,它的输出状态不仅与输入有关,还与电路的过去状态有关,即具有存储功能。 输入信号 输出信号 输出方程 驱动方程 描述时序逻辑电路的三个方程 状态方程 存储电路的输入信号 时序逻辑电路构成框图 存储电路的输出信号.
第一章 函数与极限.
C语言程序设计 主讲教师:陆幼利.
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
数字电子技术 Digital Electronics Technology
第14章 对验证的支持 学习内容 理解Verilog文本输出 理解不同的读取仿真时间的系统函数 理解 Verilog文件I/O功能.
线 性 代 数 厦门大学线性代数教学组 2019年4月24日6时8分 / 45.
计算机EDA设计 教 程 北航计算机学院 艾明晶.
VB与Access数据库的连接.
(Random Access Memory)
设计示例一 用门级结构描述D触发器:.
项目二:HTML语言基础.
Lightweight Data-flow Analysis for Execution-driven Constraint Solving
成绩是怎么算出来的? 16级第一学期半期考试成绩 班级 姓名 语文 数学 英语 政治 历史 地理 物理 化学 生物 总分 1 张三1 115
信号量(Semaphore).
第4章 Excel电子表格制作软件 4.4 函数(一).
实验三 16位算术逻辑运算实验 不带进位控制的算术运算 置AR=1: 设置开关CN 1 不带进位 0 带进位运算;
第九节 赋值运算符和赋值表达式.
iSIGHT 基本培训 使用 Excel的栅栏问题
实验六 触发器逻辑功能测试 一、实验目的 二、实验仪器 1、熟悉并掌握RS、D、JK触发器的构成、工作原理和 功能测试方法。
长春理工大学 电工电子实验教学中心 数字电路实验 数字电路实验室.
§6.7 子空间的直和 一、直和的定义 二、直和的判定 三、多个子空间的直和.
3.16 枚举算法及其程序实现 ——数组的作用.
本节内容 结构体 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
《数字电子技术基础》(第五版)教学课件 清华大学 阎石 王红
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
Visual Basic程序设计 第13章 访问数据库
魏新宇 MATLAB/Simulink 与控制系统仿真 魏新宇
HSC高速输出例程 HORNER APG.
临界区问题的硬件指令解决方案 (Synchronization Hardware)
本节内容 C语言的汇编表示 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
汽车单片机应用技术 学习情景1: 汽车空调系统的单片机控制 主讲:向楠.
基于列存储的RDF数据管理 朱敏
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
FPGA组合逻辑 王安然.
Verilog HDL 基本语法 STEP 2016/12/3.
本节内容 动态链接库 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
信号发生电路 -非正弦波发生电路.
第8章 创建与使用图块 将一个或多个单一的实体对象整合为一个对象,这个对象就是图块。图块中的各实体可以具有各自的图层、线性、颜色等特征。在应用时,图块作为一个独立的、完整的对象进行操作,可以根据需要按一定比例和角度将图块插入到需要的位置。 2019/6/30.
使用Fragment 本讲大纲: 1、创建Fragment 2、在Activity中添加Fragment
《手把手教你学STM32-STemWin》 主讲人 :正点原子团队 硬件平台:正点原子STM32开发板 版权所有:广州市星翼电子科技有限公司
Presentation transcript:

语法要点详细讲解 有关测试模块编写的语法 ; 语法的高级部分: 函数、任务、文件、存贮器建立模型、 双向总线、UDP、综合指令。。。。

语法详细讲解 Verilog测试模块的编写 目的: 复习如何编写较复杂的测试文件,对所做的设计 进行完整的测试和验证。 掌握组织模块测试的常用方法;学会编写常用的 测试代码。

注:虚线表示编译器能检查输入文件的可读性和是否存在以及是否允许生成输出文件 语法详细讲解 用Verilog设计的步骤 include 文件 仿真器 厂家元件 库文件 编译器 设计文件 输入文件:激励和期望的输出信号 仿真器 输出文件:激励和实际输出的信号 注:虚线表示编译器能检查输入文件的可读性和是否存在以及是否允许生成输出文件

语法详细讲解 测试平台的组成 需要验证的 激励 设计 信号 简单的测试平台 激励信号 和用于验 证的结果 需要验证的 设计 数据 语法详细讲解 测试平台的组成 需要验证的 设计 激励 信号 简单的测试平台 激励信号 和用于验 证的结果 数据 需要验证的 设计 复杂的测试平台

//这两个repeat开始执行时间不同,但能同时运行 #20 repeat (10) #10 data_bus = data_bus +1; 语法详细讲解 并行块 在测试块中常用到fork…join块。用并行块能表示以同一个时间起点算起的多个事 件的运行,并行地执行复杂的过程结构,如循环或任务。举例说明如下: module inline_tb; reg [7:0] data_bus; initial fork data_bus= 8’b00; #10 data_bus = 8’h45; //这两个repeat开始执行时间不同,但能同时运行 #20 repeat (10) #10 data_bus = data_bus +1; #25 repeat (5) # 20 data_bus = data_bus <<1; #140 data_bua = 8’h0f; join endmodule

语法详细讲解 并行块 时间 data_bus 时间 data_bus 上面模块的仿真输出如下: 0 8’b0000_0000

语法详细讲解 强制激励 在一个过程块中,可以用两种不同的方式对信号变量或表达式进行连续赋值。 过程连续赋值往往是不可以综合的,通常用在测试模块中。 两种方式都有各自配套的命令来停止赋值过程。 两种不同方式均不允许赋值语句间的时间控制。 assign和deassign 适用于对寄存器类型的信号 (例如:RTL级上的节点或测试模块中在多个地方被赋值的信号)进行赋值。 initial begin #10 assign top.dut.fsml.state_reg = `init_state;

语法详细讲解 强制激励 #20 deassign top.dut.fsml.state_reg; end force 和 release 用于寄存器类型和网络连接类型(例如:门级扫描寄存器的输出)的强制赋值,强制改写其它地方的赋值。 initial begin # 10 force top.dut.counter.scan_reg.q=0; # 20 release top.dut.counter.scan_reg.q; 在以上两个例子中,在10到20 这个时间段内,网络或寄存器类型的信号被强制赋值,而别处对该变量的赋值均无效。 force的赋值优先级高于assign。 如果先使用assign,再使用force对同一信号赋值,则信号的值为force所赋 的值,

当执行release后,则信号的值为assign所赋 的值。 语法详细讲解 强制激励 当执行release后,则信号的值为assign所赋 的值。 如果用force对同一个信号赋了几次值,再执行release,则所有赋的值均不再存在。 可以对信号的某(确定)位、某些(确定)位或拼接的信号,使用force和release赋值;但不能对信号的可变位使用force和release 来赋值。 不能对寄存器类型的信号某位或某些位使用 assign 和deassign 来赋值。

虽然有时在设计中会包含时钟,但时钟通常用在测试模块中。下面 三个例子分别说明如何在门级和行为级建立不同波形的时钟模型。 语法详细讲解 建立时钟 虽然有时在设计中会包含时钟,但时钟通常用在测试模块中。下面 三个例子分别说明如何在门级和行为级建立不同波形的时钟模型。 [例1] 简单的对称方波时钟: reg go; wire clk; nand #(period/2) ul (clk,clk,go); initial begin go=0; #(period/2) go=1; end reg clk; always begin #period/2 clk=0; #period/2 clk=1; end 注:在有些仿真器中,如果设计所用的时钟是由与其相同抽象级别的时钟模型产生的,则仿真器的性能就能得到提高。

nand #(period/2) ul (clk,clk,go); initial begin go=0; #(period) go=1; 语法详细讲解 建立时钟 [例2]简单的带延迟的对称方波时钟: reg clk; initial begin clk=0; #(period) forever #(period/2) clk=!clk end reg go; wire clk; nand #(period/2) ul (clk,clk,go); initial begin go=0; #(period) go=1; end 注:这两个时钟模型有些不同,行为描述的模型延迟期间一直是低电平,而门级描述的模型开始延迟有半个周期是不确定的。

[例3]. 带延迟、头一个脉冲不规则的、占空比不为1的时钟: 语法详细讲解 建立时钟 [例3]. 带延迟、头一个脉冲不规则的、占空比不为1的时钟: reg clk; initial begin #(period+1) clk=1; #(period/2-1) forever begin #(period/4) clk=0; #(3*period/4) clk=1; end reg go; wire clk; nand #(3*period/4,period/4) ul (clk,clk,go); initial begin #(period/4+1) go=0; #(5*period/4-1) go=1; end 注:这两个时钟模型也有些不同,行为描述的模型一开始就有确定的电平,而门级描述的模型有延迟, 开始时电平是不确定的。

forever #(period/2) clk=!clk end reg go; wire clk; 语法详细讲解 建立时钟 [例2]简单的带延迟的对称方波时钟: reg clk; initial begin clk=0; #(period) forever #(period/2) clk=!clk end reg go; wire clk; nand #(period/2) ul (clk,clk,go); initial begin go=0; #(period) go=1; end 注:这两个时钟模型有些不同,行为描述的模型延迟期间一直是低电平,而门级描述的模型开始延迟有半个周期是不确定的。

举例说明如何使用任务: 语法详细讲解 怎样使用任务 module bus_ctrl_tb; reg [7:0] data; reg data_valid, data_rd; cpu ul(data_valid,data,data_rd); initial begin cpu_driver (8’b0000_0000); cpu_driver (8’b1010_1010); cpu_driver (8’b0101_0101); end

语法详细讲解 怎样使用任务 task cpu_driver; input [7:0] data_in; begin #30 data_valid =1; wait (data_rd = =1); #20 data = data_in; wait (data_rd= =0); #20 data = 8’hzz; #30 data_valid = 0; end endtask endmodule

cpu_data clk data_valid data_rd read_cpu_state 语法详细讲解 怎样使用任务 在测试模块中使用任务可以提高程序代码的效率,可以用任务把多次重复的操作包装起来。 wait data1 data2 data3 data4 cpu_data clk data_valid data_rd read_cpu_state

学会如何用Verilog中对双向(即输入/输出)端口, (inout)建模。 语法详细讲解 存储建模 目标 学会如何用Verilog对存储器建模。 学会如何用Verilog中对双向(即输入/输出)端口, (inout)建模。

存储器建模必须注意以下两个方面的问题: 声明存储器容量的大小。 明确对存储器访问操作的权限。 例如:指出可以对存储器做以下哪几种操作: 语法详细讲解 存储器建模 存储器建模必须注意以下两个方面的问题: 声明存储器容量的大小。 明确对存储器访问操作的权限。 例如:指出可以对存储器做以下哪几种操作: 1)只读 2)读写 3)同步读写 4)多次读,同时进行一次写 5)多次同步读写,同时提供一些方法保证一致性

module myrom(read_data,addr,read_en_); input read_en_; `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的数据存储在另外的一个独立的文件中

如何在Verilog中用二维的寄存器组来定义存储器。 语法详细讲解 简单ROM建模 上页所示的ROM模型说明: 如何在Verilog中用二维的寄存器组来定义存储器。 ROM中的数据保存在一个独立的文件中,如上页的右边的虚线方框所示。 这是一种保存ROM数据的通用的方法,它可以使数据和ROM模型分开。

module mymem(data,addr,read,write); inout [3:0] data; 语法详细讲解 简单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 个单元 //从存储器读出到总线上 assign data=read? memory[addr]:4’bz; //从总线写入存储器 always @ (posedge write) memory[addr]=data; endmodule

RAM模型比ROM模型稍微复杂: 语法详细讲解 简单RAM建模 它必须具有读写能力; 进行读写时通常使用相同的数据总线; 需要新技术来处理双向总线; 当读信号无效时,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 语法详细讲解 存储器的加载 可以在初始化块中用一个循环或系统任务把初始数据存入存储器的每个单元。 使用循环把值赋给存储器数组。 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关键字声明端口为双向口。 inout [7:0] databus; 使用双向口必需遵循下面的规则: inout口只能声明为网络连接类型, 不允许把它声明为寄存器类型。(所以仿真器能确定多个驱动源的最终值。) 在设计中,每次只能从一个方向来驱动inout口。 例如:当使用总线读RAM中的数据时,如果同时又向RAM模型的双向数据总线写数据,就会产生逻辑竞争,导致总线数据无法确定。所以必须为inout口设计控制逻辑,只有这样才能保证正确的操作。

语法详细讲解 怎样使用双向口 [注意]: 声明一个inout口,可以用来输入或输出数据。inout口默认为网络连接类型。不允许在过程块(initial 或always块)中对网络连接类型的数据进行过程赋值;但可以在过程块外把一个寄存器数据类型通过连续赋值语句赋给它(inout口),或者把它与用户定义的源语(UDP)相连。 必须为inout口设计控制逻辑,用来保证正确的操作。当把inout口作为输入口时,必须通过控制逻辑禁止输出到inout口。

使用Verilog中的基本元件(bufif1)为双向口建模: 语法详细讲解 双向口建模 使用Verilog中的基本元件(bufif1)为双向口建模: b2 b1 en_a_b en_b_a bus_a bus_b

module bus_xcvr(bus_a,bus_b,en_a_b,en_b_a); inout bus_a,bus_b; 语法详细讲解 双向口建模 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; bufifl b1(bus_b,bus_a,en_a_b); bufifl b2(bus_a,bus_b,en_b_a); //结构模块逻辑 endmodule 当en_a_b=1时,元器件b1激活,bus_a的值传到bus_b上 当en_b_a=1时,元器件b1激活,bus_b的值传到bus_a上 [注意]: 在上页的例子中,使用en_a_b和en_b_a 来控制元器件bufifl,如果控制信号同时有效,则结果无法确定。 所以必须把控制信号 en_a_b 和 en_b_a 在时间上分开。

语法详细讲解 双向口建模 使用连续赋值为双向口建模: b2 b1 en_a_b en_b_a bus_a bus_b

语法详细讲解 双向口建模 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上 [注意]:在assign语句中,通过en_a_b和en_b_a控制bus_a与bus_b之间的数据交换。如果控制信号同时有效,则结果不能确定。所以必须把控制信号 en_a_b 和 en_b_a 在时间上分开。

语法详细讲解 双向口建模 存储器的端口建模: module ram_cell(databus,rd.wr); inout databus; 测试模块 RAM单元 数据总线 数据 寄存 器 rd wr module ram_cell(databus,rd.wr); inout databus; input rd,wr; reg datareg; assign databus = rd ? datareg:’bz; always @(negedge wr) datareg<=databus; endmodule 当rd等于1时datareg的值被赋给databus 当wr的下降沿到达时,databus的值被写入datareg

语法详细讲解 双向口建模 [注意]: 上页中存储单元在wr的下降沿到达时存入数据。上页模块在 wr处于高电平时,通过数据总线写入数据,但必须保证wr的高电平维持时间长于数据的写入时间。 在rd处于高电平时,上述存储单元通过数据总线读出数据。由于此模型为单口存储模型,因此wr变低电平时,rd不能同时为高电平,否则就无法确定存储器的读出/写入的结果。

理解有限状态机的作用,学会如何显式地为有限状态机建模。 语法详细讲解 Verilog中的高级结构 目标: 学会怎样定义或调用任务和函数。 学会怎样使用命名块。 学会怎样禁止命名块和任务。 理解有限状态机的作用,学会如何显式地为有限状态机建模。

语法详细讲解 Verilog中的高级结构 通过把代码分成小的模块或者使用任务和函数,可把一项任务分成许多较小的、易于管理的部分,从而提高代码的可读性、可维护性和可重用性。 任务: 一般用于编写测试模块或者行为描述的模块。其中可以包含时间控制(如:# delays, @, wait);也可以包含input, output 、inout 端口定义和参数;也可以调用其他的任务或函数

函数只有input变量,虽然没有output变量, 但可以通过函数名返回一个值。 可以调用其他的函数,但不可以调用任务 语法详细讲解 Verilog中的高级结构 函数: 一般用于计算,或者用来代替组合逻辑。 不能包含任何延迟;函数在零时间执行。 函数只有input变量,虽然没有output变量, 但可以通过函数名返回一个值。 可以调用其他的函数,但不可以调用任务

在任务和函数中不能声明网络连接类型的变量。 所有的输入和输出变量实际上都是本地寄存器 。 只有当任务或函数调用并执行完后,才能有返回值。 语法详细讲解 Verilog中的高级结构 [注意]: 只能调用本模块内的任务和函数。 在任务和函数中不能声明网络连接类型的变量。 所有的输入和输出变量实际上都是本地寄存器 。 只有当任务或函数调用并执行完后,才能有返回值。 [举例说明]: 若任务或函数中包含一个forever循环时,永远无法执行完,就不可能有返回值。

下面模块中的任务含有定时控制和一个输入,并且引用了一个本模块的变量,但是没有输出,也没有双向总线和内部变量,不显示任何内容。 语法详细讲解 Verilog 任务 下面模块中的任务含有定时控制和一个输入,并且引用了一个本模块的变量,但是没有输出,也没有双向总线和内部变量,不显示任何内容。 用于定时控制的信号,例如 clk,绝对不能作为任务的输入,这是因为输入值只向任务内部传递一次。 module top; reg clk, a, b; DUT u1(out, a, b, clk); always #5 clk=!clk;

task neg_clocks; 语法详细讲解 Verilog 任务 input [31:0] number_of_edges; repeat(number_of_edges) @(negedge clk); endtask initial begin clk=0; a=1; b=1; neg_clocks(3); //任务调用 a=0; neg_clocks(5); b=0; end endmodule

语法详细讲解 Verilog 任务 要点: 任务调用是通过在Verilog模块中写入任务名来实现的。 任务中可以包含input, output和inout端口变量的声明。 传递给任务的变量与任务I/O端口变量的声明次序相同。虽然传递给任务的变量名可以和任务内声明的I/O端口变量名相同,但是为了使任务成为一个独立的可共用的任务块,建议不要使用与任务内声明的I/O端口变量名相同的变量名,最好给传递到任务的变量起新的不同的名字。 在任务中可以使用时间控制。 任务使Verilog有更广阔的适用范围。 关键字disable可以用来禁止任务的执行。

语法详细讲解 Verilog 任务 [注意]: 不要在程序的不同部分同时调用同一个任务。这是因为任务只有一组本地变量,同一时刻调用两次相同的任务将会导致错误。这种情况常发生在使用定时控制的任务中。 在任务或函数中,引用父模块中声明的变量时要特别注意(即注意变量的层次命名规则)。若想在其它模块中调用任务或函数,该任务和函数中所使用的变量必须全都包含在输入/输出口列表中。

在任务调用时,任务的输入变量(端口)在任务内部被当作寄存器类型变量处理。 语法详细讲解 Verilog 任务 下面模块中的任务只含有一个双向总线(inout)端口和一个内部变量,没有其它输入端口、输出端口和定时控制,没有引用模块变量,不显示任何内容。 在任务调用时,任务的输入变量(端口)在任务内部被当作寄存器类型变量处理。

inout [7:0] data; //双向总线端口被当作寄存器类型! integer K; 语法详细讲解 Verilog 任务 parameter MAX_BITS=8; reg [MAX_BITS:1] D; task reverse_bits; inout [7:0] data; //双向总线端口被当作寄存器类型! integer K; for (k=0; k<MAX_BITS; K=K+1) reverse_bits [MAXBITS – (K+1)] = data[K]; endtask always @ (posedge clk) reverse_bits (D); ……

语法详细讲解 Verilog 任务 下面模块中定义的任务含有输入、输出、时间控制和一个内部变量,并且引用了一个本模块的变量,但是没有输出,不显示任何内容。任务调用时变量顺序应与任务定义中声明的顺序相同。 module mult(clk, a, b, out, en_mult); input clk, en_mult; input [3:0] a, b; output [7:0] out; reg [15:0] out; always @ (posedge clk) multme(a, b, out); //任务调用

语法详细讲解 Verilog 任务 task multme; //任务定义 input [3:0] xme, tome; output [7:0] result; wait (en_mult) result=xme*tome; endtask endmodule

语法详细讲解 Verilog 函数 module orand (a, b, c, d, e, out); input [7:0] a, b, c, d, e; output [7:0] out; reg [7:0] out; always @ (a or b or c or d or e) out = f_or_and (a, b, c, d, e); //函数调用 function [7:0] f_or_and; if (e= =1) f_or_and = (a|b) & (c|d); else f_or_and=0; endfunction endmodule

语法详细讲解 Verilog 函数 虽然函数不能包含定时控制,但是可以在包含定时控制的过程块中调用函数。在模块中,使用名为f_or_and的函数时,是把它作为名为f_or_and 的寄存器类型变量来处理的。

语法详细讲解 Verilog 函数 要点 函数定义不能包含任何定时控制语句。 函数必须至少有一个输入,但绝不能含有任 何输出和总线口; 一个函数只能返回一个值,该值的变量名与 函数同名,数据类型默认为reg类型。 传递给函数的变量顺序与函数输入口声明的 顺序相同。 函数定义必须包含在模块定义之内。 函数不能调用任务,但任务可以调用函数。 函数使Verilog有更广阔的适用范围。 虽然函数只能返回一个值,但是它的返回值 可以直接赋给一个由多个子信号拼接构成的 信号变量, 使其实际等效于产生了多个输出。 {o1, o2, o3, o4} = f_or_and(a, b, c, d, e);

在函数内,无论以函数名命名的变量被赋了多少次值,函数只有一个返回值。 语法详细讲解 Verilog 函数 在函数定义时,如果在函数名前面定义了位宽,该函数就可以返回由多位构成的矢量。如果定义函数的语句比较多时,可以用 begin 和end 把它们组合起来。 在函数内,无论以函数名命名的变量被赋了多少次值,函数只有一个返回值。 下例中的函数,声明了一个内部整型变量。举例说明如下:

wire [7:0] goo = zero_count (loo); function [3:0] zero_count; 语法详细讲解 Verilog 函数 module foo; input [7:0] loo; //也可以用连续赋值语句调用函数 wire [7:0] goo = zero_count (loo); function [3:0] zero_count; input [7:0] in_bus; integer I; begin zero_count = 0; for (I=0; I<8; I= I+1) if (!in_bus[I]) zero_count = zero_count +1; end endfunction endmodule

module checksub(neg,in_a,in_b); output neg; input a, b; reg neg; 语法详细讲解 Verilog 函数 若把函数定义为整型、实型或时间类型,就可以返回相应类型的数据。我们可以在任何类型的表达式中调用函数。 module checksub(neg,in_a,in_b); output neg; input a, b; reg neg; function integer subtr; input [7:0] in_a, in_b; subtr = in_a – in_b; //运算结果可以为负数 endfunction

begin if ( subtr (a,b) <0) neg = 1; else neg = 0; end endmodule 语法详细讲解 Verilog 函数 always @ (a or b) begin if ( subtr (a,b) <0) neg = 1; else neg = 0; end endmodule

语法详细讲解 Verilog 函数 函数类型、端口和行为定义时也可以使用参数, 这样就可以构成参数化函数使其返回的数据类型、输入端口的位宽等很容易做修改。所以参数化函数就有更广泛的适用范围。

语法详细讲解 Verilog 函数 …. parameter MAX_BITS =8; reg [MAX_BITS:1] D; function [ MAX_BIT:1] reverse_bits; input [7:0] data; for(K=0; K< MAX_BITS; K=K+1) reverse_bits[ MAX_BITS – (K+1)] = data [K]; endfunction always @ (posedge clk) begin …. D= reverse_bits(D); ….. end ………

语法详细讲解 命名块 可以通过在关键字begin或fork后加上:〈块名〉来给块命名。 module named_blk; …… begin :seq_blk end fork : par_blk join endmodule 可以在命名块中声明本地变量。 可以使用disable禁止命名块。

命名块使Verilog有更广阔的适用范围。 命名块的使用缩短了仿真的时间。 语法详细讲解 命名块 注意: 命名块使Verilog有更广阔的适用范围。 命名块的使用缩短了仿真的时间。

语法详细讲解 禁止命名块和任务 module do_arith(out, a, b, c, d, e, clk, en_mult); input clk, en_mult; input [7:0] a, b, c, d, e; output [15:0] out; reg [14:0] out; always @(posedge clk) begin : arith_block //***命名名为arith_block的块*** reg [3:0] tmp1, tmp2; //***本地变量*** {tmp, tmp2} = f_or_and(a, b, c, d, e); // 函数调用 if(en_mult) multme(tmp1, tmp2, out); //任务调用 end

disable multme; //**禁止任务的执行 diable arith_block; //**禁止命名块的执行 end 语法详细讲解 禁止命名块和任务 always @(negedge en_mult) begin //停止计算 disable multme; //**禁止任务的执行 diable arith_block; //**禁止命名块的执行 end //在此定义任务和函数 ………….. endmodle

语法详细讲解 禁止命名块和任务 注意: disable语句用来终止命名块或任务的执行。这是指在尚未执行该命名块或任务任何一条语句前,就从该命名块/任务执行中返回。 语法: disable 〈块名〉 或 disable 〈任务名〉 禁止执行命名块或任务后,所有在事件队列中由该命名块/任务安排的事件都将被删除。 一般 情况下disable语句是不可综合的。 在上页的例子中,只禁止命名块也可以得到预期的结果:命名块中所有的事件,包括任务和函数的执行都将被取消。

学习LPM(Library of Parameterrized Modules) 资源的利用; 语法详细讲解 系统资源和任务的利用 目的: 学习LPM(Library of Parameterrized Modules) 资源的利用; 学习调试用系统任务的利用: $random; $fmonitor; $fdisplay….. $fopen ; $fclose; $readmemh…..

module adderlpm(cin,x,y,s,cout); input cin; input [15:0] x,y; 语法详细讲解 怎样利用参数化模块 在LPM库中必须已经有该具体参数化模块,名称和使用注意点已经通过阅读说明书搞清楚了,举例如下: module adderlpm(cin,x,y,s,cout); input cin; input [15:0] x,y; output [15:0] s; output cout; lpm_add_sub m (.cin(cin), .dataa(x), .datab(y), .result(s), .cout(cout)); defparam m.lmp_width=16; endmodule

语法要点:$random; $random(<seed>); 举例如下: module t; …… integer r_seed; rom m1(data, addr); …. initial r_seed=2; always @(posedge clk) addr = {$random(r_seed)} %1024; …. //用来测试地址为0到1024之间任意值时的Rom。 endmodule

〈file_handle〉=$fopen("文件名"); 语法详细讲解 文件的使用 语法要点:$fopen("文件名"); 〈file_handle〉=$fopen("文件名"); //标准的输出显示器 descriptor=32'h0000_0001(bit 0 set) integer handle1, handle2…..; initial begin handle1= $fopen("file1.out"); //bit 1 set handle2= $fopen("file2.out"); //bit 2 set handle3= $fopen("file3.out"); //bit 2 set ……… end

语法要点: $fdisplay (<文件描述号>,p1,p2,….pn); 语法详细讲解 写到文件中去 语法要点: $fdisplay (<文件描述号>,p1,p2,….pn); $fmonitor (<文件描述号>,p1,p2,….pn); integer desc1,desc2,desc3; initial begin desc1=handle1|1; fdisplay(desc1, " Display 1……",); //写到文件file1.out (handle1)和显示器(1)(stdout) desc2=handle2|handle1; fdisplay(desc2, " Display 2……",); //写到文件file1.out (handle1)和文件file2.(handle2) desc3=handle3; fdisplay(desc3, " Display 3……",); //只写到文件file3.out (handle1)中 end

语法要点: $fclose (<文件_handle>); 举例: $fclose(handle1); //关闭file1.out 语法详细讲解 关闭文件 语法要点: $fclose (<文件_handle>); 举例: $fclose(handle1); //关闭file1.out

可以用于任何显示任务如:$display,$write,$monitor,$strobe…. 举例: module M; ………. 语法详细讲解 层次信息的显示 可以用于任何显示任务如:$display,$write,$monitor,$strobe…. 举例: module M; ………. initial $display ("Displaying in %m"); endmodule module top; M m1( ); //显示层次信息 M m2( ); M m3( ); …….

显示任务结果如下: Displaying in top.m1 Displaying in top.m2 语法详细讲解 层次信息的显示 显示任务结果如下: Displaying in top.m1 Displaying in top.m2 Displaying in top.m3

$readmemb(〈文件名〉,〈存储器名〉); $readmemb(〈文件名〉,〈 存储器名〉,起始地址); 语法详细讲解 用文件中的数据初始化存储器 语法要点: $readmemb(〈文件名〉,〈存储器名〉); $readmemb(〈文件名〉,〈 存储器名〉,起始地址); $readmemh(〈文件名〉,〈 存储器名〉,起始地址); 举例: module test; reg [7:0] memory [0:1023] integer i; inititial begin $readmemb ("init.dat",memory); for (i=0;i<1024;i=i+1;) $display ("memory [%0d]=%b", i, memory); end endmodule

语法详细讲解 用文件中的数据初始化存储器 init.dat 文件内容: @002 1111_1111 0101_0101 0000_0000 1010_1010 @008 Zzzz_1111 0000_1111 1111_0000 0001_1000 ……. memory [0]=xxxxxxxx memory [1]=xxxxxxxx memory [2]=11111111 memory [3]=01010101 memory [4]=00000000 memory [5]=10101010 memory [6]=xxxxxxxx memory [7]=xxxxxxxx memory [8]=zzzz1111 memory [9]=00001111 memory [10]=11110000 memory [11]=00011000 ……………. ………………

隐式FSM: 语法详细讲解 有限状态机(FSM) state 1 不需要状态寄存器 仿真更加有效 只能很好地处理线性的状态改变

显式FSM: 语法详细讲解 有限状态机(FSM) 结构比较复杂 可以很方便地用来处理默认状态 能够处理复杂的状态改变 state A state B1 state B2 state C state D

语法详细讲解 有限状态机(FSMs) 注意: 在隐式状态机中,只要发生在一个时钟周期内写数据,在另一个时钟周期内读数据的情况,都会生成寄存器。 任何状态机都必须有复位控制信号,状态的改变必需只与某单一时钟信号沿同步。 一般情况下,如果状态改变比较简单,又定义得比较好,而且综合工具支持隐式状态机的综合,就可以使用隐式状态机。如果状态改变比较复杂,最好使用显式状态机,这样效果更好。 隐式状态机属于行为级,不属于RTL级。代码中主要包含循环语句、嵌入的定时控制,有时也含有命名事件、wait 和 disable 语句。一般情况下,常用的综合工具不支持隐式状态机的综合。

1 语法详细讲解 显式有限状态机 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 状态变量 case语句

default: {state, out}=2’b00; endcase endmodule 注: 语法详细讲解 显式有限状态机 out=datain; state=1’b0; end default: {state, out}=2’b00; endcase endmodule 注: 在过程块中可以使用一个时钟沿和 case 语句来描述一个显式状态机。 必须指定一个状态变量,来记录状态机的状态。 要改变当前的状态,必须改变状态变量的值, 其改变要与时钟沿同步。 写得比较好的状态机常为不应产生的条件规定一个默认动作。 转到下一个状态 默认状态指针 1 识别11序列 clk rst out

@(posedge clk) //状态二:输出第二位 out=datain; end endmodule 语法详细讲解 隐式有限状态机 begin: seq_block out=1’b0; if(!datain) //状态一:输出零 disable seq_block; @(posedge clk) //状态二:输出第二位 out=datain; end endmodule 1 识别11序列 clk rst out

语法详细讲解 隐式有限状态机 注意: 在过程块中可以使用多个时钟沿(即每次状态改变都用一个新的时钟沿)、条件语句、循环语句、disable语句来描述隐式FSM。隐式FSM往往是不可综合的。 隐式FSM不必指定状态变量。 当下一个激活时钟沿到达时,状态就有可能发生改变。下一个状态是否改变,将由条件语句决定;除非用强制性语句使状态重复(例如:用循环语句或用disable语句来强制改变状态), 在隐式状态机中,很难规定一个默认动作。

目标 学会怎样使用用户定义的原语来创建逻辑。 语法详细讲解 用户定义的原语 目标 学会怎样使用用户定义的原语来创建逻辑。 用户定义的原语元件 (UDP) 其行为与 Verilog 语法中本来就存在的primitive(原语元件)相似,它用一个表格来定义它的逻辑功能。

语法详细讲解 什么是UDP? 在 Verilog 结构建模时,可以使用: 二十多个门级源语元件(primitives)。 用户定义的源语元件(UDP)。 UDP 可用于ASIC 库中的基本元件(cell)设计,以及小规模芯片和中规模芯片的设计。 使用 UDP可以在现有的Verilog 语言支持的源语元件的基础上编写新的源语元件。 UDP 是一个独立元件, 不能用实例调用的方法调用其他的模块。 UDP 既可以用来表示时序逻辑元件,也可以表示组合逻辑元件。 UDP 的行为是使用真值表来描述的 。 调用 UDP 的方式与调用Verilog语言提供的源语元件的方式相同。

UDP 是一种紧凑的表示简单逻辑关系部件的方法。 注意: UDP 是一种紧凑的表示简单逻辑关系部件的方法。 在Verilog语言提供的几种基本源语元件中,若在输入中包含不确定值x,则在输出时可能出现不确定值 x;而在 UDP 中则不允许出现此种情况。 由几个原语元件组成的逻辑可以用一个UDP表示。在仿真时使用这样的UDP来代替分散的原语元件可以节省计算资源,加快仿真速度。一般的仿真器处理行为模型表示的逻辑所需时间比处理用门级语句表示的相同逻辑所需时间少;而硬件仿真器正好相反。

UDP 只能有一个输出端,而且必须是端口说明列表的第一项。 UDP 可以有多个输入端,最多允许有 10 个。 UDP 不支持Z(高阻)逻辑值。 在仿真的开始时刻,可以使用 initial 语句把UDP 的输出初始化为一个已知值。 UDP 不支持综合,即不能通过综合把它转变为门级结构逻辑。

语法详细讲解 UDP的特点 注: UDP 只能有一个输出。如果逻辑功能要求有多个输出端时,则需要把其它的原语元件连接到 UDP的 输出,或同时使用多个 UDP,保证其最终输出只有一个。 UDP 输入端最多可以有 10 个,但是当输入端的个数多于 5 个时,仿真时需要的内存个数将呈现近似指数的增加。下表列出了当输入数目不同时,在仿真过程中,对每个输入信号,计算机中所需要开销的内存数目。

语法详细讲解 UDP的特点 输入端口的个数 所需内存的字节数 1-5 <1 6 5 7 17 8 56 9 187 10 623

组合逻辑示例:2-1 多路器 语法详细讲解 举例说明 原语名 primitive multiplexer(o, a, b, s); output o; input s, a, b; table // a b s : o 0 ? 1 : 0; 1 ? 1 : 1; ? 0 0 : 0; ? 1 0 : 1; 0 0 x : 0; 1 1 x : 1; endtable endprimitive 输出端口必须为第一个端口

语法详细讲解 举例说明 注: 在模块外定义 UDP 。 如果在表中没有规定输入组合,将输出不确定逻辑值 (x)。 表的列中元素的顺序应与端口列表中的一致。 表中的 ?的意义是:重复的输入 0,1或 任意不确定逻辑值(x)。 表中开始两行表示:当 s等于 1 时,不管 b 逻辑值如何变化,输出 o 将与 输入 a 保持一致。 表中的下两行表示:当 s 等于 0 时,不管 a逻辑值如何变化,输出 o 将与输入 b 保持一致。 表中 的最后两行使此器件的描述更加的全面、准确。它们表示:当输入 a 和 b 的逻辑值相同时,如果 sel 逻辑值不确定,则输出 o 的值 将与输入 a 和 b 的值相同。这种行为不能使用 Verilog 语言提供的基本原语元件进行建模。UDP 将 x 作为实际的未知值,而不是 Verilog 语言逻辑值来进行处理,因此使其比Verilog语言提供的基本原语元件更加准确。

语法详细讲解 组合逻辑示例:全加器 可以只使用两个 UDP 来描述全加器的逻辑功能。 // 全加器进位实现部分 primitive U_ADDR2_C (CO, A, B, CI); output CO; input A, B, CI, table // A B CI : CO 1 1 ? : 1; 1 ? 1 : 1; ? 1 1 : 1; 0 0 ? : 0; 0 ? 0 : 0; ? 0 0 : 0; endtalbe endprimitive 向上一级进位 下一级来的进位

语法详细讲解 组合逻辑示例:全加器 //全加器求和实现部分 primitive U_ADDR2_S(S, A, B,CI); output S; input A, B, CI; table // A B CI : S 0 0 0 : 0; 0 0 1 : 1; 0 1 0 : 1; 0 1 1 : 0; 1 0 0 : 1; 1 0 1 : 0; 1 1 0 : 0; 1 1 1 : 1; endtable endprimitive

语法详细讲解 组合逻辑示例:全加器 若使用 UDP 设计全加器,仅需要两个 UDP; 而使用 Verilog 原语元件,则需要 5 个Verilog语言提供的基本原语元件。 当设计需要使用大量全加器时,采用UDP来表示全加器,将大大减少内存的需要。 事件的数目将大大降低。 ?表示逻辑值可以为 0,1或 x。

语法详细讲解 电平敏感的时序逻辑示例:锁存器 primitive latch(q, clock, data); output q; reg q; input clock, data; initial q=1’b1; table // clock data current next // state state 0 1 : ? 1; 0 0 : ? 0; 1 ? : : -; endtable endprimitive 注意此寄存器的用法,此寄存器用来存储。 输出初始化为 1‘b1. ? 表示无须考虑输入和当前状态的值

语法详细讲解 电平敏感的时序逻辑示例:锁存器 注: 锁存器的动作行为如下: 当时钟信号为 0时,输入数据的值直接传给输出。 当时钟信号为1时,输出保持当前状态不变。 next state 栏中的 “-” 表示输出保持不变。 输出必须定义为寄存器类型,用来保存前一个状态。 initial q=1’b1; 是时序 UDP 的初始化语句。使用此语句可以在仿真的开始对输出进行赋值。 在实际的部件模型中,很少使用初始赋值。但在测试 UDP 的功能时,初始赋值相当有用。

语法详细讲解 跳边沿敏感的时序逻辑示例:D 触发器 primitive d_edge_ff (q, clk, data); output q; input clk, data; reg q; table // clk dat state next (01) 0 : ? : 0; (01) 1 : ? : 1; (0x) 1 : 1 : 1; (0x) 0 : 0 : 0; (x1) 0 : 0 : 0; (x1) 1 : 1 : 1;

语法详细讲解 跳边沿敏感的时序逻辑示例:D 触发器 // 忽略时钟的下降沿 (?0) ? : ? : -; (1x) ? : ? : -; // 忽略时钟稳定时的数据改变 endtable endprimitive 在大多数情况下,可以在任何表入口语句中规定一个输入过渡。 如果规定了任何输入过渡,则必须规定所有输入的所有过渡。

语法详细讲解 $setup 和 $hold 时间的概念 建立时间和保持时间的概念: clock holdup time data setup 3 5

语法详细讲解 检查建立时间的系统任务:$setup //setup check is set //clock is the reference //data is being checked for violation //violation reported if Tposedge_clk -Tdata<3 specify $setup (data, posedge clock, 3); endspecify

语法详细讲解 检查保持时间的系统任务 $hold //Hold check is set //clock is the reference //data is being checked for violation //violation reported if Tdata -Tposedge_clk < 5 specify $hold ( posedge clock, data, 5); endspecify

$width ( posedge clear, 6); endspecify 脉冲宽度 6ns //Hold check is set //posedge of clear is the reference //the next negedge of clear is the data_event //violation reported if Tposedge_clk - Tnegedge_clk < 6 specify $width ( posedge clear, 6); endspecify

语法详细讲解 可综合风格的Verilog建模类型 可综合建模类型只有两种: 组合逻辑: 任何时候,如果输出信号直接由当前的输入信号的组合决定,则此逻辑为组合逻辑。 时序逻辑: 如果逻辑中具有记忆功能,则此逻辑为时序逻辑。在任何给定的时刻,如果输出不能完全由输入信号确定,则此逻辑具有记忆功能。

语法详细讲解 不能综合的 Verilog结构 initial 循环语句: repeat forever while for 的非结构用法 一部分数据类型 event real time

语法详细讲解 不能综合的 Verilog 结构 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 q<=d;

语法详细讲解 过程块中寄存器类型的信号变量 在同步块中使用 reg 类型变量: 如果在一个时钟沿对reg变量赋值,而在下一个时钟沿对其采样,则综合器把该reg变量转换为硬件寄存器。 如果只把reg变量作为基本输出,则综合器不一定把它转换为硬件寄存器。 如果不属于上述两种情况,同步块中的reg变量有可能被优化掉。 在组合块中使用 reg 类型变量: 当组合块中任何一个输入变量的值改变时,reg变量的值也随之改变,则综合器不会把该reg变量转换为硬件寄存器。 当块的某一个输入的值改变时,reg变量的值不一定立即改变,而要等其他输入信号的值改变时才改变,则综合器将把该reg变量转换为锁存器。

语法详细讲解 寄存器 同步寄存器示例: 在下面的例子中,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

语法详细讲解 寄存器 在下面的例子中,用两个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 注:在后面的always块中,块执行的顺序是不确定的,因此 q 可以获得在前一个周期中赋给 rega 的值。

组合寄存器示例: 下面的两个例子中,rega 都是临时变量,在 语法详细讲解 寄存器 组合寄存器示例: 下面的两个例子中,rega 都是临时变量,在 综合中它们都会被优化掉。在本例中,y 和rega 不断被赋新值(因 为语句中有else rega = 0;),综合出的电路是一个纯组合逻辑。 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 在下面的例子中,rega 只是有时被赋新值 (没有else 语句,rega在条 件不符合时保持原值);因此综合出来的是一个以 y 作为输出的锁存器。

语法详细讲解 寄存器 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

语法详细讲解 电平敏感列表 在下面的例子中,a、b 和 sl 均是块的输入。 在两个例子中, sl 均为 always 块的条件。 在第二个例子中, a 和 b 也用作always 块的条件。 不完整电平敏感列表: 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 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 orand(out, a, b, c, d, e); input a, b, c, d, e; output out; assign out=3&(a|b)&(c|d); endmodule

module latch_quasi(q, en, d); input en, d; output q; reg q; 语法详细讲解 过程连续赋值 过程连续赋值是在过程块(always 和 initial 块)内部 对寄存器类型的变量进行的连续赋值。 module latch_quasi(q, en, d); input en, d; output q; reg q; always @(en) if(en) assign q=d; else deassign q; endmodule

[例1] 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

[例2] module compif(a, b, c, d, e); 语法详细讲解 CASE条件语句 [例2] 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) //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

module inccase(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 ; endcase endmodule

module incpif(a, b, c, d, e); input a, b, c, d; output e; reg e; 语法详细讲解 条件不完整的CASE语句 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

语法详细讲解 条件不完整的CASE语句 在上面的例子中,当 a 变为 0 时,因为条件语句中没有这种情况,不会有值赋给 e,因此,e 将保存原来的值。直到 a 变为 1时,才有值赋给e。此行为表明,a 变为0电平,不会立即引起输出的重新赋值,要等到a 变为1时,e 的值才被重新赋值,a 的变化被保存起来不让它产生影响,类似与把 a 的低电平变化存入锁存器。

语法详细讲解 带缺省项条件完整的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; default: e=‘bx; endmodule

module compif(a, b, c, d, e); input a, b, c, d; output e; reg e; 语法详细讲解 带有缺省项的完整条件语句 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

语法详细讲解 带有缺省项的完整条件语句 在前面的例子中,虽然没有定义所有可能的选择,但为没有定义的选择规定了缺省的行为。因此,它们都是纯的组合逻辑,不会产生多余的锁存器。

语法详细讲解 由综合指令实现的完整 case 语句 module dircase(a, b, c, d) input b, c; input [1:0] a; output d; reg d; always @(a or b or c) case (a) //ambit synthesis case = full 2’b00: d=b; 2’b01: d=c; endcase endmodule 在此例中,虽然没有定义所有可能的选择,但其中的综合指令通知优化器没有定义的选择将不会发生。此例为纯组合逻辑,不会产生锁存器。

语法详细讲解 case 指令的例外情况 当设置了 case 综合指令为 full 时,也可从 case 语句中综合出锁存 器。举例说明如下: module select ( a, b, sl); input [1:0] sl; output a, b; reg a, b; always @(sl) case (sl) //ambit synthesis case = full 2’b00: begin a=0; b=0; end 2’b01: begin a=1; b=1; end 2’b10: begin a=0; b=1; end

语法详细讲解 case 指令的例外情况 2‘b11: b=1; default: begin a=‘bx; b=‘bx; end endcase endmodule 虽然case覆盖的情况是全的,但电平敏感列表不全,a ,b 都没有列 入。所以有锁存器产生。

语法详细讲解 函数 函数不包含时间控制,因此它们综合为组合逻辑。可以在过程块和连续赋值语句中调用函数。 下面是 or/and 块,其中在连续赋值语句中调用了函数。 module orand(out, a, b, c, d, e); input a, b, c, d, e; output out; wire out; assign out=forand(a, b, c, d, e); function forand; if(e==1) forand(a|b)&(c|d);

语法详细讲解 函数 else forand=0; endfunction endmodule

语法详细讲解 任务 任务仅用于测试模块中,因为: 不包含时间控制的任务与函数的作用相似。 包含时间控制的任务是不可综合的。 下面的 or/and 块中调用了任务: module orandtask(out, a, b, c, d, e); input a, b, c, d, e; output out; reg out; always @(a or b or c or d or e) orand(out, a, b, c, d, e); task orand;

语法详细讲解 任务 output out; if(e==1) out=(a|b)&(c|d); else out=0; endtask endmodule

语法详细讲解 锁存器是怎样产生的? 在 always 块中,如果条件语句中的规定的条件不完整,综合时则 会产生锁存器。在下面的例子中,因为未定义若当 enable 为低电平 时,怎样来处理 q 和 data,故 data 的值会保存在q寄存器中。为 保证语义的正确解释,综合器必须使用存储元件才能产生与此模 块对应的逻辑。 module latch(q, data, enable); input data,enable; output q; reg q; always @(enable or data) if(enable) q=data; endmodule

语法详细讲解 同步反馈是怎样产生的? endmodule 一般的综合工具只能支持同步时序电路中的组合反馈电路的生成, 语法详细讲解 同步反馈是怎样产生的? 一般的综合工具只能支持同步时序电路中的组合反馈电路的生成, 而不能支持组合电路中的组合反馈电路的生成。综合时如果在条件语 句的分支中, 出现有的分支没有为输出赋值,就会在生成的电路中产生 组合反馈电路。 不产生反馈: module dffn(q,d,clk,en); input d, clk, en; output q; reg q; always @(negedge clk) if(en) q<=d; else q<=‘bx; endmodule 产生反馈: module dffn(q,d,clk,en); input d, clk, en; output q; reg q; always @(negedge clk) if(en) q<=d; endmodule

非阻塞赋值在块结束后才完成赋值操作,此赋值方式可以避免在仿真出现冒险和竞争现象。 语法详细讲解 阻塞与非阻塞赋值方式 赋值的类型的选择取决于建模的逻辑类型 在时序块的 RTL 代码中使用非阻塞赋值。 非阻塞赋值在块结束后才完成赋值操作,此赋值方式可以避免在仿真出现冒险和竞争现象。 在组合的 RTL 代码中使用阻塞赋值。 使用阻塞方式对一个变量进行赋值时,此变量的值在在赋值语句执行完后就立即改变。

语法详细讲解 阻塞与非阻塞(赋值方式) 使用非阻塞赋值方式进行赋值时,各个赋值语句同步执行;因此,通常在一个时钟沿对临时变量进行赋值,而在另一个时钟沿对其进行采样。 下面的模块综合为两个触发器 ,其中采用了非阻塞赋值方式: module nonbloc(clk,a,b); input clk, a; output b; reg b; reg y; always @(posedge clk) begin y<=a; b<=y; end endmodule 下面的模块综合为触发器, 其中采用了阻塞赋值方式: module bloc(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 clk, d, r; output q; reg q; always @(negedge clk) if(r) q<=0; else q<=d; endmodule 同步块中的异步复位: module async(q,ck,r,d); input clk, d, r; output q; reg q; always @(negedge clk or posedge r) if(r) q<=0; else q<=d; endmodule

语法详细讲解 带复位端的时序电路建模 在下面的例子中,使用了异步复位信号,用以在同步块中实现异步 复位。 module async(q, clk, r, d); input clk, d, r; output q; reg q; always @(negedge clk) if(!r) q<=d; always @(posedge r) q<=0; endmodule 不提倡使用上述风格的异步复位代码,上述代码是不可综合的。在 仿真时,若 r 和 clk 同时在同一个时间单元中改变,则结果是不确 定的。

语法详细讲解 带复位端的锁存器建模 下面的例子表示一个比较复杂的带复位端的锁存器的模型。其中使用了完 全敏感列表,以便综合器能将它综合为锁存器。 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

语法详细讲解 综合指令 大部分综合工具都能按照约定,正确地处理在源代码中用注释行表示的指导综合器如何转换到门级网表的指令即综合指令。 可以在 Verilog 语句之间用注释行嵌入综合指令,Verilog 仿真器运行时将忽略嵌入的指令,而当综合工具编译时,这些符合约定的综合指令是有意义的,能对综合过程进行干预和指导。 不同的综合工具有不同的综合指令集,使用综合指令为的是:使同样的RTL代码能综合出更高质量优化的门级网表。 下面列出了一部分 Cadence 综合工具支持的综合指令,它们与其他综合工具(例如:Synopsys DC)中的指令非常相似。 //ambit synthesis on //ambit synthesis off //ambit synthesis case=full、parallel、mux

语法详细讲解 综合指令 结构指令 //ambit synthesis architecture=cla or rpl 有限状态机指令 //ambit synthesis enum xyz //ambit synthesis stat_vector sig state_vector_flag 注:指令中通常包括综合工具或公司的名称,例如:上面指令中的ambit 表示使用的综合器是Envisia Ambit公司的产品。

语法详细讲解 综合指令 当Verilog 模块中case –endcase 块被综合时,有多种门级实现方法可 供选择:综合指令case 可以用来干预综合过程。让由case 指定的编 译方式被综合器优先考虑,即采用 case = 后列出的方法转换为门级 电路。case 综合指令的含义如下所示: //ambit synthesis case=parallel 对解码逻辑进行并行编译,没有优先级。 //ambit synthesis case=mux 如果库中含有多路器,则使用多路器编译解码逻辑。 //ambit synthesis case=full 不用考虑没有包含在 case 条件语句中的情形(即这些情形不会发生),这样设定,可以综合出的优化的门级逻辑,并可避免发生状态机死锁。

语法详细讲解 FSM 综合指令 在状态机建模中也可以使用综合指令。FSM 综合指令在综合时将 RTL级代码中有关状态机的性质和要求信息告诉给综合器。 FSM 指令包括: enum 指令 用于状态赋值列举,还用于将状态值绑定到状态矢量上。 state_vector 指令 用于帮助综合器识别状态寄存器和解码类型。

语法详细讲解 FSM 综合指令 `timescale 1ns/100ps module state4(clock, reset, out); input reset, clock; outpt [1:0] out; reg [1:0] out; paramete /*ambit synthesis enum state_info */ stateA=2’b00, stateB=2’b01, stateC=2’b10, stateD=2’b11; reg [1:0] /*ambit synthesis enum state_info */ state; reg [1:0] /*ambit synthesis enum state_info */ nextstate; 将列举名绑定到矢量state和nextstate上 定义列举名

语法详细讲解 FSM 综合指令 always @(posedge clock) /* ambit synthesis state_vector state –encoding one_hot*/ if(reset) state<=stateA; else state<=nextstate; 定义状态寄存器并指定所需的解码类型

资源共享即 RTL 级模块中的两个或两个以上的部分共享一组逻辑。例如: always @(a or b or c or d) if(a) 语法详细讲解 资源共享的表示 资源共享即 RTL 级模块中的两个或两个以上的部分共享一组逻辑。例如: always @(a or b or c or d) if(a) out = b+c; else out = b+d; 注:资源共享与否取决于综合工具。一般情况下,资源共享的条件是共享逻辑所在的语句位于同一模块,同一 always 块的同一条件语句中。

+ 语法详细讲解 资源共享的表示 然而,可以通过改变 RTL 级代码的编码风格控制资源共享,例如: 资源不共享 资源共享 原代码: if (a) out=b+c; else out=b+d; 强制实现资源共享的代码: temp=a ? c : d; out=b+temp; 或 out=b+(a ? c : d); 资源不共享 资源共享 + out a b c d out a b c d +