嵌入式系统工程系 第二章 Verilog HDL基础 Verilog HDL历史与现状 Verilog HDL与VHDL 系统建模概述 简单的Verilog HDL示例 语言要素 表达式、操作数、操作符 门级建模 数据流建模 行为建模 Verilog HDL源代码设计 Testbench验证 嵌入式系统工程系 Verilog HDL历史 1983年,Gateway Design Automation公司推出Verilog语言,开发 了仿真与验证工具; 1985年,GDA推出Verilog仿真器Verilog-XL:仿真速度快,处理能 力强,具有交互式调试手段; 1987年,Synopsys公司的综合软件开始接受Verilog输入; 1989年,Cadence公司收购GDA,进一步扩大Verilog的影响; 1990年,Open Verilog International(OVI)成立,推广Verilog HDL和Veriog-XL被广泛推广; 1993年,OVI推出Verilog2.0,作为IEEE提案提出申请; 1995年,IEEE(Institute of Electrical and Electronics Engineers) 通过Verilog HDL标准IEEE Std.1364-1995; 2001年,IEEE 发布了Verilog IEEE 1364-2001标准。 本课程以IEEE Std.1364-1995为主 嵌入式系统工程系 Verilog HDL现状 Verilog HDL是最广泛使用的、具有国际标准支 持的硬件描述语言,绝大多数的EDA厂商都支持; 在工业界和ASIC设计领域,Verilog HDL应用更 加广泛。 嵌入式系统工程系 Verilog HDL与VHDL VHDL Very-High-Speed Integrated Description Language Circuit Hardware 诞生于1982年;1987年底被IEEE和美国国防部 确认为标准硬件描述语言 。 IEEE 1076(1983) IEEE 1076-1995 …… 嵌入式系统工程系 Verilog HDL与VHDL 建模层次 系统级(system): 用高级语言结构实 现设计模块的外部性能的模型。 算法级(algorithmic): 用高级语言结 构实现设计算法的模型。 RTL级(Register Transfer Level): 描述数据在寄存器之间流动和如何 处理这些数据的模型。 门级(gate-level): 描述逻辑门以及 逻辑门之间的连接的模型。 开关级(switch-level): 描述器件中 三极管和储存节点以及它们之间连 接的模型。 系统级 算法级 算法级 RTL 级 RTL 级 门级 门级 逻辑级 电路级 开关级 Verilog HDL 行为级 VHDL 嵌入式系统工程系 Verilog HDL与VHDL 相同点: 都能形式化抽象表示电路行为和结构; 支持逻辑设计中层次与范围的描述; 具有电路仿真和验证机制; 与工艺无关。不专门面向FPGA设计 不同点: Verilog与C语言相似,语法灵活;VHDL源于Ada语言, 语法严格; Verilog更适合ASIC设计。 嵌入式系统工程系 SystemVerilog与SystemC SystemVerilog:IEEE 1364 Verilog-2001 标准 的扩展增强,兼容Verilog 2001,将硬件描述语 言(HDL)与现代的高层级验证语言(HVL)结 合。 SystemC:一种软/硬件协同设计语言 ,既是系 统级语言,也是硬件描述语言。 嵌入式系统工程系 系统建模 设计方法学 自顶向下 自底向上 混合式 描述方式 数据流描述:描述电路数据流行为:assign 行为描述:描述功能:initial,always 结构化描述:描述元器件间连接关系:例化 混合描述:Verilog允许多描述方式共存于同一模块。 嵌入式系统工程系 简单的Verilog程序 module trist1(out,in,enable); 三态门 output out; /*输出信号*/ 模块trist1 调用 input in, enable; //输入信号 模 块 mytri 的 实 mytri tri_inst(out,in,enable); 例元件tri_inst; endmodule 通过这种结构性 模块构造可构成 module mytri(out,in,enable); 特大型模块。 output out; input in, enable; assign out = enable? in : 'bz; endmodule 嵌入式系统工程系 简单的Verilog程序 三态门(综合) 嵌入式系统工程系 简单的Verilog程序 Verilog HDL程序是由模块构成的; 每个模块要进行端口定义,并说明输入输出口,然 后对模块的功能进行逻辑描述; Verilog HDL程序的书写格式自由,一行可以写几 个语句,一个语句也可以分写多行; 除了endmodule语句外,每个语句和数据定义的最 后必须有分号。 嵌入式系统工程系 同一电路的多种描述方法 RTL级行为描述 module muxtwo(out,a,b,sl); 二选一的选择器 input a, b, sl; 门级(结构化)描述 output out; module muxtwo(out,a,b,sl); reg out; input a, b, sl; always @(sl or a or b) output out; if(!sl) out=a; else out=b; wire nsl, sela,selb; not #1 u1(nsl,sl); // #1是仿真延迟 endmodule 布尔代数级行为描述 and #1 u2(sela,a,nsl); module muxtwo(out,a,b,sl); and #1 u3(selb,b,sl); input a, b, sl; or #1 u4(out, sela, selb); output out; endmodule assign out = sel?b:a; endmodule 嵌入式系统工程系 同一电路的多种描述方法 二选一的选择器(综合) 嵌入式系统工程系 混合描述 混合设计方式的1位全加器实例 module FA_Mix (A, B, Cin, Sum, Cout); input A, B, Cin; output Sum, Cout; reg Cout; reg T1, T2, T3; wire S1; xor X1(S1, A, B); // 门实例语句。 always @ ( A or B or Cin ) // always 语句。 begin T1 = A & Cin; T2 = B & Cin; T3 = A & B; Cout = (T1 | T2) | T3; end assign Sum = S1 ^ Cin; // 连续赋值语句。 endmodule 嵌入式系统工程系 混合描述 混合设计方式的1位全加器实例(综合) 嵌入式系统工程系 模块基本结构 module 模块名(端口列表); 端口I/O说明 内部信号声明 功能定义 endmodule module block (a,b,c,d); input a,b; output c,d; assign c= a | b ; assign d= a & b; endmodule a c b d 嵌入式系统工程系 时延 Verilog HDL模型中的所有时延都根据时间单位定义。下 面是带时延的连续赋值语句实例。 assign #2 Sum = A ^ B; # 2指2个时间单位。使用编译指令将时间单位与物理时间相关联。 这样的编译器指令需在模块描述前定义,如下所示: ` timescale 1ns /100ps 此语句说明时,延时间单位为1ns并且时间精度为100ps (时间精 度是指所有的时延必须被限定在0.1ns内)。如果此编译器指令所 在的模块包含上面的连续赋值语句, #2 代表2ns。 如果没有这样的编译器指令, Verilog HDL 模拟器会指定 一个缺省时间单位。IEEE Verilog HDL 标准中没有规定 缺省时间单位。 嵌入式系统工程系 语言要素:标识符 所谓标识别符就是用户为程序描述中的Verilog 对象所起 的名字。 模块名、变量名、常量名、函数名、任务名 标识符必须以英语字母(a-z, A-Z)起头,或者用下横线 符( _ )起头。其中可以包含数字、$符和下划线符。 标识符最长可以达到1023个字符。 模块名、端口名和实例名都是标识符。 Verilog语言大小写敏感, sel 和 SEL 是两个不同的标识符。 所有的关键词都是小写的。 嵌入式系统工程系 语言要素:系统任务和函数 以$字符开始的标识符表示系统任务或系统函数。 任务可以返回0个或多个值,函数除只能返回一个 值以外与任务相同。 函数在0时刻执行,即不允许延迟,而任务可以带 有延迟。 常用于测试模拟,一般不用于源代码设计。 $display ("Hi, you have reached LT today"); /*$display系统任务在新的一行中显示。* / $time //该系统任务返回当前的模拟时间。 嵌入式系统工程系 语言要素:编译指令 以`(反引号)开始的某些标识符是编译器指令。 `define 和`undef,很像C语言中的宏定义指令 `ifdef、`else 和`endif,用于条件编译 ` include 文件既可以用相对路径名定义,也可以绝对 路径 ` timescale 编译器指令将时间单位与实际时间相关联。 该指令用于定义时延的单位和时延精度。 嵌入式系统工程系 语言要素:值集合 Verilog HDL有下列四种基本的值: 0:逻辑0或“假” 1:逻辑1或“真” x:未知 z:高阻 (x,z不区分大小写) Verilog HDL中有三类常量: 整型 实数型 字符串型 嵌入式系统工程系 语言要素:常量 1. 整数 表达方式: <位宽>’<进制><数字>:标准方式 ’<进制><数字>:默认位宽,与机器类型有关 <数字>:不指明进制默认为十进制 进制 二进制(b或B):8’b10101100, ’b1010 十进制(d或D):4’d1543, 512 十六进制(h或H): 8’ha2 八进制(o或O):6’O41 x和z值 x:不确定:4’b100x z:高阻:16’hzzzz,没有驱动元件连接到线网,线网的缺省值为z。 嵌入式系统工程系 语言要素:常量 负数: 在位宽表达式前加一个减号,如 -8’d5 减号不可以放在位宽和进制之间,也不可以放在进制和具体的数之间, 如8’d-5 下划线: 只能用在具体的数字之间,如16’b1010_1111_1010 位数指的是二进制位数。 数位扩展:(定义的长度比为常量指定的长度长) 最高位是0、1,高位用0扩展:8’b1111 等于 8’b00001111 最高位是z、x,高位自动扩展:4’bz 等于 4’bzzzz 数位截断: 如果长度定义得更小,最左边的位被截断,如: 3 ‘ b1001_0011 等于 3’b011,5'H0FFF等于5'H1F 嵌入式系统工程系 语言要素:常量 2. 实数 十进制计数法;例如 2.0 5.68 科学计数法; 23_5.1e2 其值为23510.0,忽略下划线 3.6E2 其值为360.0 ( e与E相同) 实数通常不用于FPGA源代码的常量 嵌入式系统工程系 语言要素:常量 3. 字符串 字符串是双引号内的字符序列。字符串不能分成多行 书写。例如: "INTERNAL ERROR" " REACHED->HERE “ 用8位ASCII值表示的字符可看作是无符号整数。 为存储字符串“INTERNAL ERROR”,变量需要8 *14位。 reg [1 : 8*14] Message; (Message = “INTERNAL ERROR“) 字符串较少用于FPGA源代码的常量 嵌入式系统工程系 语言要素:数据类型 两大类数据类型 wire [msb:lsb] reg1, reg2, ... regN; 线网型 寄存器型 线网型 包含下述不同种类的线网子类型 wire //FPGA设计中,通常只用wire型 tri wor trior wand triand trireg tri1 tri0 supply0 supply1 msb和lsb 定义了范围, 并且均为常数值表达式。 范围定义是可选的;如果 没有定义范围,缺省值为 1位线网 wire Reset; wire [3:0] data_in; wire [3:2] select; wire [0:2] point; 嵌入式系统工程系 语言要素:数据类型 reg [msb:lsb] reg1, reg2, ... regN; 寄存器型 5种不同的寄存器类型。 reg //FPGA设计中,通常只用 reg型,默认初始值x。 integer //其他类型用于仿真 time real realtime msb和lsb 定义了范围,并且 均为常数值表达式。范围定 义是可选的;如果没有定义 范围,缺省值为1位寄存器 reg Reset; reg [3:0] data_in; reg [3:2] select; reg [0:2] point; 嵌入式系统工程系 语言要素:数据类型 存储器 存储器是一个寄存器数组。存储器使用如下方式说明 reg [msb: lsb] memory1 [upper1: lower1] , memory2 [upper2: lower2] ,... ; reg [3:0] MyMem [63 : 0] // MyMem为64个4位寄存器的数组。 reg Bog [1 : 5] // Bog为5个1位寄存器的数组。 存储器赋值不能在一条赋值语句中完成,寄存器可以。 存储器常用于FPGA外围器件的仿真建模 嵌入式系统工程系 语言要素:数据类型 存储器赋值 2)系统任务赋值 reg [3:0] RomB [7:0] ; $readmemb ("ram.patt", RomB); Romb是存储器。文件“ram.patt”必须包含二进制值。文件也可以 包含空白空间和注释。下面是文件中可能内容的实例。 1101 1110 1000 0111 0000 1001 0011 0001 嵌入式系统工程系 语言要素:数据类型 存储器赋值 1)对每个单元逐一赋值 reg [0:3] Xrom [0:2]; ... Xrom[0] = 4'hA; Xrom[1] = 4'h8; Xrom[2] = 4‘hF; 嵌入式系统工程系 语言要素:参数 参数是一个常量,常用于定义时延和变量的宽度。 parameter LINELENGTH = 132; parameter ALL_X_S = 16'bx; parameter BIT=1, BYTE = 8, PI = 3.14; parameter STROBE_DELAY = (BYTE + BIT) / 2; 参数值也可以在编译时被改变。改变参数值可以 使用参数定义语句或通过在模块初始化语句中定 义参数值 嵌入式系统工程系 语言要素:空白符、注释 除了字符串中的空白符,其他空白符编译被忽略 注释 多行注释 /* 单行注释 // */ (不允许嵌套) 嵌入式系统工程系 嵌入式系统工程系 嵌入式系统工程系 嵌入式系统工程系 嵌入式系统工程系 嵌入式系统工程系 习题1 1 使用`timescale 编译器指令的目的是什么? 2 写出产生下图所示波形的变量BullsEye的初始化语句。 3 使用数据流描述方式编写下图所示的异或逻辑的Verilog HDL描述, 并使用规定的时延。 4下列表达式的位模式是什么? 7'o44, 'Bx0, 5'bx110, 'hA0, 10'd2, 'hzF 嵌入式系统工程系 表达式 表达式由操作数和操作符组成; 表达式可以在出现数值的任何地方使用; 表达式是数据流描述的基础。 A&B Addr1[3:0] +Addr2[3:0] Count + 1 (a[0] ^ b[0] ) | (a[1] & ~b[1]) 嵌入式系统工程系 表达式:操作数 操作数可以是以下类型中的一种: 常数 参数 线网 寄存器 位选择 部分选择 存储器单元 函数调用 嵌入式系统工程系 表达式:操作数 常数 表达式中的整数值可被解释为有符号数或无符号数; 如果整数是基数型整数,作为无符号数对待。 12 01100的5位向量形式 (有符号) -12 10100的5位向量形式 (有符号) 5‘b01100 十进制数12 (无符号) 参数 参数类似于常量,并且使用参数声明进行说明。例如 parameter LOAD = 4'd12, STORE = 4'd10; LOAD和STORE为参数,值分别被声明为12和10。 嵌入式系统工程系 表达式:操作数 线网 线网中的值被解释为无符号数, 表达式中可使用:标量线网(1位)和向量线网(多位)。 wire [3:0] led; //4位向量线网。 wire line; //标量线网。 assign led = 4‘ha; //被赋于位向量1010,为十进制10。 嵌入式系统工程系 表达式:操作数 寄存器 integer型的值被解释为有符号的二进制补码数, reg型或time型的值被解释为无符号数, real型和realtime的值被解释为有符号浮点数。 reg [4:0] state; State = 5‘b01011; // 值为位向量01011,十进制值11。 State = 9; // 值为位向量01001,十进制值9。 为何没有用assign语句赋值? 嵌入式系统工程系 表达式:操作数 位选择 位选择从向量中抽取特定的位。形式如下: net_or_reg_vector[bit_select_expr] State[1] && State[4] led[0] | line //寄存器位选择。 //线网位选择。 如果选择表达式的值为x、z或越界,则位选择的值为 State[x]值为x。(FPGA设计中禁用) 嵌入式系统工程系 表达式:操作数 部分选择 net_or_reg_vector[msb_const_expr:lsb_const_expr] State [4:1] //寄存器部分选择。 reg [4:0] state; led [2:0] //线网部分选择。 wire [3:0] led; 选择范围越界或为x、z时,部分选择的值为x。 (FPGA设计中禁用越界) 嵌入式系统工程系 表达式:操作数 存储器单元 存储器单元从存储器中选择一个memory[word_address] reg [7 : 0] Dram [63 : 0]; Dram [60]; //存储器的第61个单元。 不允许对存储器变量值部分选择或位选择。 (思考:在存储器中读取一个位或部分选择一个字?) 函数调用 表达式中可使用函数调用。 $time + SumOfEvents (A, B) /* $time是系统函数,并且SumOfEvents是在别处定义的 用户自定义函数。*/ 嵌入式系统工程系 表达式:操作符 Verilog HDL中的操作符可以分为下述类型: 算术操作符 关系操作符 相等操作符 逻辑操作符 按位操作符 归约操作符 移位操作符 条件操作符 连接和复制操作符 嵌入式系统工程系 表达式:操作符 操作符从最高优先级到最低优先级排列。同一行 中的操作符优先级相同。 嵌入式系统工程系 表达式:操作符 除条件操作符从右向左关联外,其余所有操作符 自左向右关联。 A+B-C A?B:C?D:F 等价于:(A + B ) - C 等价于:A?B:(C?D:F) 圆扩号能够用于改变优先级 (A?B:C)?D:F //自左向右 //从右向左 嵌入式系统工程系 表达式:操作符 算术运算符 + (加) -(减) * (乘) / (除) %(取模) 1) 后三种不常用,是具体设计结构而定; 2) 任意操作数是X或Z,那么整个结果为X; 3) 结果的长度由最长的操作数决定; 4) reg和wire保存无符号数。 嵌入式系统工程系 表达式:操作符 关系操作符有: >(大于) <(小于) >=(不小于) <=(不大于) 习题:求下列表达式真值 23 > 45 52 < 8'hxFF 'b1000 > = 'b01110 1) 关系操作符的结果为真(1)或假(0); 2) 如果操作数中有一位为X或Z,那么结果为X。 嵌入式系统工程系 表达式:操作符 等式运算符(相等关系运算符) 习题:求下列表达式真值 = =(逻辑相等) 假定 Data = ‘b11x0; ! = (逻辑不等) Addr = ‘b11x0; = = =(全等) 求 Data = = Addr ! = =(非全等) Data = = = Addr 1) 如果比较结果为假则结果为0,为真结果为1; 2) 在全等比较中,值x和z严格按位比较。 === 0 1 x z == 0 1 x z 0 1 0 0 0 0 1 0 x x 1 0 1 0 0 1 0 1 x x x 0 0 1 0 x x x x x z 0 0 0 1 z x x x x 嵌入式系统工程系 表达式:操作符 逻辑操作符有: && (逻辑与) || (逻辑或) ! (逻辑非) 习题:求下列表达式真值 假定: 求 1) 只对逻辑值运算,结果一位,逻辑 值1、0或x; 2) 对于向量操作, 非0向量作为1处理; 3) 如果任意一个操作数包含x,结果 也为x。 C = 'b0; //0为假 D = ‘b1; //1为真 A_Bus = 'b0110; B_Bus = 'b0110; C && D C || D !D A_Bus && B_Bus A_Bus || B_Bus !A_Bus 嵌入式系统工程系 表达式:操作符 按位操作符有: • ~(一元非) • &(二元与) • |(二元或) • ^(二元异或) • ~^或^~(二元异或非) 操作数对应位上按位操 作,并产生向量结果。 习题:求下列表达式真值 假定 求 A = 'b0110; B = 'b0100; A|B A&B 嵌入式系统工程系 表达式:操作符 归约操作符有: & (归约与) ~& (归约与非) | (归约或) ~| (归约或非) ^ (归约异或) ~^ (归约异或非) 习题:求下列表达式真值 假定: 求 A = ‘b0110; B = ‘b0100; MyReg = 4‘b01x0; ~&A ^A |B &B |MyReg ^MyReg 在单一操作数的所有位上操作,并产生1位结果。 嵌入式系统工程系 表达式:操作符 移位操作符有: 习题:求Qreg的值 假定: reg [7: 0] Qreg; Qreg = 4'b0111; Qreg >> 2 •<< (左移) 移位 >> (右移) 1)左侧操作数移动右侧操作数表示的次数,逻辑移位, 空闲位添0补位; 2)如果右侧操作数的值为x或z, 移位操作的结果为x。 应用实例:使用移位操作为2 - 4解码器建模 wire [3:0] DecodeOut; assign DecodeOut = 4'b1 << Address [1:0]; 嵌入式系统工程系 表达式:操作符 条件操作符 根据条件表达式的值选择表达式,形式: cond_expr ? expr1 : expr2 三目运算符 wire [2 : 0] Student; assign Student = Marks > 18 ? Grade_A : Grade_C; 嵌入式系统工程系 表达式:操作符 位拼接运算符(连接和复制操作) 连接操作:将小表达式合并形成大表达式的操作。形式: {expr1, expr2, . . .,exprN} 复制操作:指定重复次数来执行操作。如下: {repetition_number {expr1, expr2, ...,exprN} 实例: wire [7:0] Dbus, [11:0] Abus; assign Dbus [7:4] = { Dbus[0], Dbus[1], Dbus[2], Dbus[3] }; assign Abus = {3{4'b1011}}; / /位向量12'b1011_1011_1011) assign Abus = {{4{Dbus[7]}}, Dbus}; /*符号扩展*/ 嵌入式系统工程系 习题2 1.说明参数GATE_DELAY, 参数值为5。 2.假定长度为64个字的存储器, 每个字8位,编写Verilog 代码,按逆序 交换存储器的内容。即将第0个字与第63个字交换,第1个字与第62个 字交换,依此类推。 3. 假定32位总线Address_Bus, 编写一个表达式,计算从第11位到第 2 0位的归约与非。 4. 假定一条总线Control_Bus [15 : 0],编写赋值语句将总线分为两条 总线: Abus [0 : 9 和Bbus[6 : 1]。 5. 编写一个表达式,执行算术移位,将Qparity 中包含的8位有符号数 算术移位。 6. 使用条件操作符, 编写赋值语句选择NextState的值。如果Current State的值为RESET, 那么NextState的值为GO;如果CurrentState的 值 为 GO , 则 NextState 的 值 为 BUSY ; 如 果 CurrentState 的 值 为 BUSY ;则NextState的值为RESET 。 7.如何从标量变量A,B,C和D中产生总线BusQ[0:3]? 如何从两条总 线B usA [0 : 3]和BusY[20 : 15]形成新的总线BusR[1 0 : 1] ? 嵌入式系统工程系 模块与端口 模块:基本单元定义成模块形式 module module_name (port _list) ; Declarations_and_Statements endmodule 端口队列port_list列出了该模块通过哪些端口与外部 模块通信。 嵌入式系统工程系 模块与端口 端口 模块的端口可以是 input(输入端口)、output (输出端口) 或者inout (双向 端口); 缺省的端口类型为wire型; output或inout能够被重新声明为reg型,但是input不 可以; 线网或寄存器必须与端口说明中指定的长度相同。 嵌入式系统工程系 模块与端口 例: module Micro (PC, Instr, NextAddr); //端口说明 input [3:1] PC; output [1:8] Instr; inout [16:1] NextAddr; //重新说明端口类型: wire [16:1] NextAddr; //该说明是可选的,但如果指定了,就必须与它的端口说明保持相同 长度。 reg [1:8] Instr; //Instr已被重新说明为reg型,因此能在always语句或在initial语句 中赋值。 ... endmodule 嵌入式系统工程系 模块与端口 模块实语句 一个模块能够在另外一个模块中被引用,这样就建立了描述的层次。模 块实例语句形式 module_name instance_name (port _associations) ; 信号端口可以通过位置或名称关联;但是关联方式不能够混合使用。端 口官廉形式 port_expr //通过位置,隐式关联 .PortName (port_expr) //通过名称,显示关联,强烈推荐! port_expr可以是以下的任何类型: 1) 标识符(reg型或wire型) 2) 位选择 3) 部分选择 4) 上述类型的合并 5) 表达式(只适用于input型信号) Micro M1 ( UdIn[3:0], {WrN, RdN}, Status[0], Status[1] , &UdOut[0:7], TxData) ; 嵌入式系统工程系 模块与端口 使用两个半加器模块构造全加器 module HA (A , B , S , C); input A , B; output S, C; assign S = A ^ B; assign C = A & B; endmodule module FA (P, Q, Cin, Sum, Cout) ; input P, Q, Cin; output Sum, Cout; wire S1, C1, C2; HA h1 (P, Q, S1, C1); //通过位置关联。 HA h2 (.A(Cin), .S(Sum), .B(S1), .C(C2)); //通过端口与信号的名字关联。 or O1 (Cout, C1, C2) ; //或门实例语句 endmodule 考虑如何模块参数化? 嵌入式系统工程系 模块与端口 使用两个半加器模块构造全加器(模块参数化) module HA (A , B , S , C); input A , B; output S, C; parameter AND_DELAY = 1, XOR_DELAY = 2; assign #XOR_DELAY S = A ^ B; assign #AND_DELAY C = A & B; endmodule module FA (P, Q, Cin, Sum, Cout) ; input P, Q, Cin; output Sum, Cout; parameter OR_DELAY = 1; wire S1, C1, C2; HA h1 (P, Q, S1, C1); //通过位置关联。 HA h2 (.A(Cin), .S(Sum), .B(S1), .C(C2)); //通过端口与信号的名字关联。 or #OR_DELAY O1 (Cout, C1, C2) ; //或门实例语句 endmodule 嵌入式系统工程系 模块与端口 悬空端口 通过将端口表达式表示为空白来指定为悬空端口 DFF d1(.Q(QS), .Qbar(), .Data(D),.Preset(), .Clock(CK)); 端口长度不同 通过无符号数的右对齐或截断方式进行匹配 module Child(Pba, Ppy) ; input [5:0] Pba; output [2:0] Ppy; ... endmodule module Top; wire [1:2] Bdl; wire [2:6] Mpr; Child C1 (.Pba(Bdl), .Ppy(Mpr)); endmodule 嵌入式系统工程系 模块与端口 模块参数值改变 1)参数定义语句(defparam) module TOP (NewA , NewB , NewS , NewC) ; input New A , New B; output New S , New C; defparam Ha1. XOR_DELAY = 5, //实例Ha1中的参数XOR_DELAY。 Ha1. AND_DELAY = 2; //实例Ha1中参数的AND_DELAY。 HA Ha1 (NewA, NewB, NewS, NewC) ; endmodule 嵌入式系统工程系 模块与端口 模块参数值改变 2) 带参数值的模块引用 module TOP (NewA , NewB , NewS , NewC) ; input New A , New B; output New S , New C; HA #(5,2) Ha1(NewA , NewB , NewS , NewC) ; //第1个值5赋给参数AND_DELAY,该参数在模块HA中说明。 //第2个值2赋给参数XOR_DELAY,该参数在模块HA中说明。 endmodule 嵌入式系统工程系 模块与端口 外部端口 显式地指定外部端口。(较少使用) module Scram_B ( .Data(Arb), .Control(Ctrl),.Mem_Word(Mem_Blk), .Addr(Byte) ); input [0:3] Arb; input Ctrl; input [8 : 0] Mem_Blk; output [0:3] Byte; ... endmodule 嵌入式系统工程系 习题3 1 .模块实例语句与门实例语句的区别是什么? 2 .当端口悬空时,即端口没有被连接时,端口的 值是什么? 3 .用本章讲述的模块FA编写执行加法和减法的4 位ALU的结构模型。 嵌入式系统工程系 门级建模 FPGA设计中较少使用 Verilog HDL中提供下列内置基本门: 1) 多输入门: and, nand, or, nor, xor, xnor 2) 多输出门: buf, not 3) 三态门: bufif0, bufif1, notif0, notif1 4) 上拉、下拉电阻: pullup, pulldown 5) MOS开关: cmos, nmos, pmos, rcmos, rnmos, rpmos 6) 双向开关: tran, tranif0, tranif1, rtran, rtranif0, rtranif1 嵌入式系统工程系 用于定义原语(UDP) FPGA设计中通常不 使用; UDP 实 例 语 句 的 语 法与基本门的实例 语句语法一致; UDP 中 可 以 描 述 组 合电路和时序电路。 Primitive D_Edge_FF (Q, Clk, Data) ; output Q; reg Q ; input Data, Clk; initialQ = 0; table // Clk Data Q (State) Q(next ) (01) 0 : ? : 0; (01) 1 : ? : 1; (0x) 1 : 1 : 1; (0x) 0 : 0 : 0; // 忽略时钟负边沿: (?0) ? : ? : -; // 忽略在稳定时钟上的数据变化 (??) ? : ? : -; endtable endprimitive 嵌入式系统工程系 数据流建模 连续赋值用于数据流建模(描述),生成组合逻辑 电路。 连续赋值使用连续赋值语句 assign语句,格式为: assign LHS_target = RHS_expression; 例如 wire Z1, Preset, Clear; //线网说明 assign Z1 = Preset & Clear; //连续赋值语句 wire [15:0] data_in; wire [15:0] data_tmp; wire data_tmp = {data_in[7:0], data_in[15:8]}; 嵌入式系统工程系 数据流建模 连续赋值语句在什么时候执行呢? 只要在右端表达式的操作数上有事件发生(值 变化),表达式立即被计算,新结果就赋给左边的 线网。 连续赋值的目标类型(左侧操作数类型) 1) 标量线网 assign Z1 = … ; 2) 向量线网 assign data_tmp = … ; 3) 向量的常数型位选择 assign data_tmp[2] = … ; 4) 向量的常数型部分选择 assign data_tmp[7:0] = … ; 5) 上述类型的任意的拼接运算结果 assign { Z1, data_tmp[15]} = 2’b10; 嵌入式系统工程系 数据流建模 例:数据流描述的一位全加器 module FA_Df (A, B, Cin, Sum, Cout) ; input A, B, Cin; output Sum, Cout ; assign Sum = A^B ^Cin; assign Cout = (A & Cin) | (B & Cin) | (A & B) ; endmodule 1)assign语句之间是并发的,与其书写的顺序无关; 2)线网的赋值可以在声明时赋值,例如 wire Sum = A^B ^Cin; 嵌入式系统工程系 数据流建模 数据流建模的时延 assign #2 Sum = A ^ B ^ Cin; #2表示右侧表达式的值延迟两个时间单位赋给Sum; 时间单位是多少?由谁来决定? `timescale 1ns/100ps FPGA设计中的时延仅在功能仿真时有效,不影响实际 电路生成。 嵌入式系统工程系 数据流建模 数据流建模注意事项: 1)wire型变量如果不赋值,默认值为z; 2)数据流建模没有存储功能,不能保存数据; 3)wire型变量只能在声明时赋值或者assing语句赋值; 4)assign语句并发执行,实际的延迟又物理芯片的布线 结果决定。 5)最基本的FPGA设计源代码描述语句之一,用于生成 组合逻辑,定制LUT的逻辑功能。常作为中间信号的描 述用于控制寄存器的输入输出。 嵌入式系统工程系 习题4 1. 使用assign语句描述一个时钟信号clk,频率 为100MHz。 assign #5 clk = ~clk; 2. 请指出下列语句是否合法?描述了怎样的功能? assign Mux = (S = = 0)? A : 'bz; assign Mux = (S = = 1)? B : 'bz; assign Mux = (S = = 2)? C : 'bz; assign Mux = (S = = 3)? D : 'bz; 嵌入式系统工程系 行为建模 过程赋值用于行为建模(描述) 行为建模的主要机制: 1) initial 语句 2) always 语句 主要用于仿真文件(模拟) 用于源文件和仿真文件 所有initial语句和always语句之间都是并发执行; 执行顺序与其在模块中书写顺序无关。 嵌入式系统工程系 行为建模:initial语句 initial 语句只执行一次; 在模拟开始时执行,即在0时刻开始执行; 不能嵌套使用。 initial [timing_control] procedural_statement procedural_statement可以是: procedural_continuous_assignment 过程赋值(阻塞或者非阻塞) conditional_statement -> if case_statement -> case loop_statement -> for, forever, repeat, while wait_statement -> wait disable_statement -> disable(相当于C中的break) event_trigger -> @ (event) sequential_block -> begin ... end parallel_block -> fork ... join task_enable (user or system) 嵌入式系统工程系 行为建模:initial语句 例: reg Curt; ... initial #2 Curt = 1; 例: parameter SIZE = 1024; reg [7:0] RAM [0 : SIZE-1] ; reg RibReg; Initial begin: SEQ_BLK_A //顺序过程的标记,如果没有局部声明,则不需要 integer Index; RibReg = 0; for (Index = 0; Index < SIZE; Index = Index + 1) RAM [Index] = 0; end 嵌入式系统工程系 行为建模:initial语句 initial语句在仿真文件产生时钟和构造数据简单示例 parameter APPLY_DELAY = 5; reg [0 : 7] port_A; reg clk; ... initial begin Port_A = 'h20 ; #APPLY_DELAY Port_A= 'hF2; #APPLY_DELAY Port_A= 'h41; #APPLY_DELAY Port_A= 'h0A; initial begin clk = 0; while(1) //或者 forever clk = #5 ~clk; //或者 #5 clk = ~clk; end 嵌入式系统工程系 行为建模:always语句 always语句重复执行,语法和initial语句相同: always [timing_control] procedural_statement procedural_statement可以是: procedural_continuous_assignment 过程赋值(阻塞或者非阻塞) conditional_statement -> if case_statement -> case loop_statement -> wait_statement -> disable_statement -> event_trigger -> sequential_block -> parallel_block -> task_enable (user or system) for , forever, repeat, while wait disable(相当于C中的break) @ (event) begin ... end fork ... join 嵌入式系统工程系 行为建模:always语句 两种典型的always语句 1) 组合逻辑(电平触发) reg c; always @ ( a or b or sel ) c = sel ? a : b; 说明: (1)虽然c是reg型,但综合的 结果是组合电路; (2)等同于数据流描述 wire c; assign c = sel ? a : b; (3) FPGA设计中不建议使用; 此外,容易产生锁存器 嵌入式系统工程系 行为建模:always语句 说明: (1) 在 always 语 句中所有被赋值的信号 2) 时序逻辑(时钟沿触发) 必须是reg型; reg [8:0]count; (2) 综合为触发器, always @ (posedge clk or negedge reset) 推荐使用; begin if (~reset) (3) 异步时序逻辑 两种典型的always语句 count = 0; else begin if (count == 511) count = 0; else count = count + 1; end end 嵌入式系统工程系 常见过程语句 时序控制语句 仅用于仿真测试 1)时序控制 reg Stream ; initial Begin Stream = 0; #12 Stream = 1; #5 Stream = 0; #3 Stream = 1; #4 Stream = 0; #2 Stream = 1; #5 Stream = 0; end 嵌入式系统工程系 常见过程语句 时序控制语句 2)事件控制 边沿触发事件 reg [9:0]addr; integer i; initial begin for (i=0; i<5 ;i=i+1) @ (posedge clk) addr = addr +1; end 电平触发事件 initial begin wait (Sum > 22) Sum = 0; end 嵌入式系统工程系 常见过程语句 顺序语句块 begin … end 源程序、测试文件 块内语句顺序执行 思考题: initial语句若使用 fork…join如何描 述右图时序? 并行语句块 fork … join 测试文件 块内语句并行执行 嵌入式系统工程系 常见过程语句 问题: (1)时序电路的行为具有并行特性:寄存器 都受到时钟的控制,流水线… (2)既然fork … join不能在源文件中使用, 在行为描述中如何描述并行语句? (3)begin … end中的语句是顺序执行,在 同一时钟边沿触发下,每个寄存器变量为何赋值 有先有后?这与实际电路是否矛盾? 嵌入式系统工程系 常见过程语句 过程赋值语句 定义:initial和always语句中的赋值语句 区别于数据流描述的连续赋值语句(assign) 分为阻塞过程赋值和非阻塞过程赋值两种 阻塞过程赋值 非阻塞过程赋值 always @ (posedge clk or negedge rst) begin if (~rst) …//寄存器复位 else if (… ) begin a = 1‘b1; b = a; end end b = ? always @ (posedge clk or negedge rst) begin if (~rst) …//寄存器复位 else if (… ) begin a <= 1‘b1; b <= a; end end 嵌入式系统工程系 常见过程语句 结论: 源代码设计推荐使用非阻塞过程赋值“ <= ” • 可以有效综合为寄存器逻辑电路 • 符合实际,时序分析简单 • 语句之间并行执行,不再有顺序关系 阻塞过程赋值“ = ”多用于仿真测试文件 • 适合构造仿真模型和仿真行为 • 不容易直接综合为FPGA资源 嵌入式系统工程系 常见过程语句 initial begin Clr = #5 0; Clr = #4 1; Clr = #10 0; end initial begin Clr <= #5 Clr <= #4 Clr <= #10 end 1; 0; 0; 嵌入式系统工程系 常见过程语句 过程赋值与连续赋值的比较 嵌入式系统工程系 常见过程语句 if语句 与C语言类似 If (condition_1) procedural_statement_1 {else if(condition_2) procedural_statement_2} {else procedural_statement_3} 注: 1)条件语句必须在过程块语句中使用,不能单独使用; 2) if后面的表达式的值只有为1时才按“真”处理。 嵌入式系统工程系 常见过程语句 if语句的嵌套 if(表达式1) if (表达式2) 语句1; else 语句2; else if (表达式3) 语句3; else 语句4; 注: 1) else总是与它最上面的最近的if配对; 2) 如果if与else的数目不一样,为了实现程序设计者的目 的,可以用begin…end语句确定配对关系; 3) 强烈建议保留else分支。 嵌入式系统工程系 常见过程语句 例: always @ (posedge clk or negedge rst) begin if (~rst) begin ctrl flag <= #1 2’b00; <= #1 0; end else if(~flag) begin ctrl flag <= #1 2’b01; <= #1 1; end else flag end <= #1 0; 嵌入式系统工程系 常见过程语句 综合电路 嵌入式系统工程系 常见过程语句 case语句 类似C语言的switch case语句 case (case_expr) case_item_expr{ ,case_item_expr} : procedural_statement ... ... [default: procedural_statement] endcase 嵌入式系统工程系 常见过程语句 reg e; always @ (posedge clk or negedge rst) begin if (~rst) e <= #1 0; else case ( {a, b} ) 2’b00: 2’b01: 2’b11: 2’b11: e <= #1 d; e <= #1 ~c; e <= #1 1’b0; e <= #1 1’b1; default: ; endcase end //空语句,强烈建议保留default分支表达式 嵌入式系统工程系 常见过程语句 综合电路 嵌入式系统工程系 常见过程语句 casex语句和casez语句 语法与case非常相似 不建议使用 casez(ir) 8b’1???????: instruction1(ir); 8b’01??????: instruction2(ir); 8b’00010???: instruction3(ir); 8b’000001??: instruction4(ir); endcase 嵌入式系统工程系 常见过程语句 循环语句 forever语句 repeat语句 while语句 for语句 思考题: 用四种循环语句分别实现 initial中的时钟产生: 1)在100ns出开始; 2)周期10ns。 (1)连续执行的循环; (2)只用于测试程序的initial块中; (3)综合工具很难综合成FPGA的逻辑电路。 嵌入式系统工程系 习题5 1. 描述电路行为:该电路在每一个时钟下跳沿 (负沿)检查输入数据,当输入数据Usg为1011 时,输出Asm被置为1。 2. 描述电路行为:输入为12位的向量。如果其中 1的数量超过0的数量,输出设置为1。当 Data_Ready为1时,才对输入数据进行检查。 提示:输入信号均有clk和rst,采用时序逻辑设计 (always语句) 嵌入式系统工程系 Verilog HDL源代码设计 基于本章内容,可以设计FPGA可实现的Verilog 源代码; 自顶向下设计; 采用数据流建模、行为建模、结构化建模三种方 式; 开始你的第一个Verilog功能模块源代码设计! 嵌入式系统工程系 Verilog HDL源代码设计 基本设计流程 1)根据需求,进行模块功能划分,自顶向下设计; 2)定义各个模块的接口信号(包括方向、类型、宽度); 3)定义全局时钟信号和全局复位信号; 4)编写顶层模块,例化子模块; 5)子模块功能设计,以时序逻辑设计为主; 与软件源代码设计最大的不同:时序的严格性! 嵌入式系统工程系 Verilog HDL源代码设计 如何验证源代码设计的正确性? 功能仿真(前仿真) 进行语法检查,error和warning 设计testbench,根据激励输入验证逻辑功能 逻辑综合 … … 嵌入式系统工程系 Testbench验证 Testbench 模拟实际环境的输入激励和输出校验的一种“虚拟平 台” 以输入激励为主 ,输出校验可以通过波形观测 Testbench (Board) CLK RST Peripheral_1 Top Module (FPGA) …… Peripheral_n signals 1)Testbench和源 代码都是.v文件 2) Testbench和源 代码都是module 3) Testbench不能 综合成FPGA内部 电路 嵌入式系统工程系 Testbench验证 接口信号定义 被测试模块的输入激励设置为reg型; 被测试模块的输出设置为wire型; 双向端口inout在测试中需要进行特殊处理。 为什么信号方向与类型的对应关系与之前的要求不同? 源代码看作testbench子模块 源代码顶层的输入是testbench的输出 源代码顶层的输出是testbench的输入 嵌入式系统工程系 Testbench验证 Testbench中inout信号的使用 本质上是三态门 inout[15:0] data; wire [15:0] data; reg [15:0] data_out; reg data_enable; 方法1: assign data = data_enable ? data_out : 16‘hz; 方法2: IOBUF(.I(data_out), .O(), .T(data_enable), .IO(data)); 嵌入式系统工程系 Testbench验证 Testbench的结构 module testbench(); //信号类型定义(wire或者reg),注意testench没有输入输出。 … //例化顶层模块 … //激励行为描述,通常都包含clk和rst的产生描述 initial … //可使用各种合法语句 always … assign … task … //类似于函数 endmodule 嵌入式系统工程系 Testbench验证 Testbench自动生成模板 Xilinx ISE工具提供testbench的自动生成模板 在同一项目中的 Verilog Test Fixture 与哪个源代码文件关联就生成对应层次的Testbench 嵌入式系统工程系 Testbench验证 空模板样例 需增加clk的产生 rst的使能描述 输入信号的行为 描述 嵌入式系统工程系 Testbench验证 值序列产生 产生值序列的最简单是使用initial语句。例如: initial begin Reset = 0; #100 Reset = 1; #80 Reset = 0; #30 Reset = 1; end 嵌入式系统工程系 Testbench验证 值序列产生 重复序列可由always语句产生,例如: parameter REPEAT_DELAY = 35; integer CoinValue; always begin CoinValue = 0; #7 CoinValue = 25; #2 CoinValue = 5; #8 CoinValue = 10; #6 CoinValue = 5; # REPEAT_DELAY; end 嵌入式系统工程系 Testbench验证 重复模式(例如时钟信号) wire Clock; assign # (PERIOD/2) Clock = ~ Clock; 初值多少? initial Clock = 0; Clock数据类型是否正确? 建议使用行为描述方式产生时钟 嵌入式系统工程系 Testbench验证 重复模式(例如时钟信号) reg Clk_A ; //推荐使用的仿真时钟生成方法 parameter tPERIOD = 10; initial Clk_A = 0; always # (tPERIOD/2) Clk_A = ~ Clk_A; 嵌入式系统工程系 Testbench验证 状态检测器验证实例 module Top; reg Data, Clock; integer Out_File; //待测试模块的应用实例 Count3_ls F1(Data, Clock, Detect) ; initial //产生时钟 begin Clock = 0; forever #5 Clock = ~ Clock; end initial begin Data = 0; #5 Data = 1; #40 Data = 0; #10 Data = 1; #40 Data = 0; #20 $stop; // 模拟结束 end initial //文件保存监控信息 begin Out_File = $fopen ("results.vectors") ; $fmonitor (Out_File ,"Clock=%b, Data=%b, Detect=%b", Clock, Data, Detect); end endmodule 嵌入式系统工程系 Testbench验证 任务 类似过程,从描述的不同位置执行共同的代码段; 任务可以包含时序控制,也能调用其它任务和函数; 任务参数数量不限(可以是0),需要声明输入输出。 构造复杂的仿真行为,推荐使用! task task_id; [declarations] procedural_statement endtask 嵌入式系统工程系 Testbench验证 一个任务实例 parameter MAXBITS = 8; task Reverse_Bits; input [MAXBITS - 1:0] Din; output [MAXBITS - 1:0] Dout; integer K; begin for (K = 0; K < MAXBITS; K = K + 1) Dout [MAXBITS - K] = Din[K] ; end endtask 嵌入式系统工程系 Testbench验证 任务的调用 如同函数调用 在initial或者always语句中使用 任务中可以调用其他任务 注意:实际仿真时,常常构造带有时序逻辑的任务构 造数据 嵌入式系统工程系 Testbench验证 PCI控制器DMA模式写数据任务 task pci_w; input start; input [31:0] data0; input [31:0] data1; input [31:0] data2; input [31:0] data3; input [31:0] data4; input [11:0] addr; reg flag; begin TENABLE <= 0; @ (posedge clk66); if (start) #1 pci_lhold_im <= 1; else #1 pci_lhold_im <= 0; @ (posedge clk66); #1 flag <= 0; @ (posedge clk66); @ (posedge clk66); if (pci_lholda_om & ~flag) begin #1 pci_ads_iw <= 0; pci_addr_i <= addr; pci_data_i <= 32'h14; end while(~flag) begin if(~pci_ready_ow) #1 flag <= 1; @ (posedge clk66); begin #1 pci_ads_iw <= 1; if (flag) pci_data_i <= data0; end end @(posedge clk66); #1 pci_data_i <= data1; @(posedge clk66); #1 pci_data_i <= data2; @(posedge clk66); #1 pci_data_i <= data3; @(posedge clk66); begin #1 pci_data_i <= data4; pci_blast_iw <= 0; end @(posedge clk66); #1 pci_blast_iw <= 1; @(posedge clk66); if(pci_ready_ow) pci_lhold_im <= 0; T_ENABLE <= 1; end endtask 嵌入式系统工程系 Testbench验证 函数 与任务类似,但是只能返回一个值;仿真中涉猎不多。 Verilog有丰富的系统函数,与C语言系统函数类似, 请查阅手册; 系统函数通常用于输出校验和存储器仿真。 嵌入式系统工程系 习题6 1. 产生一个高电平持续时间和低电平持续时间分 别为3 ns和10 ns的时钟。 2. 编写测时序检测器源代码以及测试验证程序。 时序列检测器按模式10010在每个时钟正沿检查 输入数据流。如果找到该模式,将输出置为1;否 则输出置为0。
© Copyright 2024 ExpyDoc