第17章 Verilog中的高级结构 学习内容: 任务和函数的定义和调用 怎样使用命名块 怎样禁止命名块和任务 有限状态机(FSM)及建模.

Slides:



Advertisements
Similar presentations
什么是SOPC: SOPC是英文System On a Programmable Chip的缩写,称为片上可编程系统。SOPC将传统的EDA技术、计算机系统、嵌入式系统、数字信号处理等融为一体,综合了各自的优势,且在结构上形成一块芯片。 为什么用SOPC:SOPC是现代电子技术和电子系统设计的发展趋势,建立了电子系统设计的新模式。用户利用SOPC开发平台,自行设计高速、高性能的DSP处理器、特定功能的CPU及其外围接口电路,创建结构最为简洁的电子系统。
Advertisements

声明 本课件供《虚拟仪器技术》、《自动测试技术》等相关课程教师授课使用与参考. 教师可根据课程需要和实际情况在此课件基础上增删内容
第2章 SOPC硬件开发环境及流程.
第4章 VHDL设计初步.
Chapter 5 Sequential Logic Circuit
數位邏輯設計與實習 ch04 組合邏輯電路設計.
第三章 组合逻辑电路 第一节 组合电路的分析和设计 第二节 组合逻辑电路中的竞争与冒险 第三节 超高速集成电路硬件描述语言VHDL
第1章 FPGA概述 1.1 FPGA的发展历程 1.2 FPGA的基本原理 1.3 FPGA的设计方法 1.4 FPGA的设计流程
水煮FPGA 传统FPGA设计流程简介.
FC OB1 FB SFC 操作系统 SFB OBs 结构化编程 其它
正反器 Flip-Flop 閂鎖器 +邊緣觸發之控制信號 ∥ 正反器
一、實習目的 1、瞭解各種閂鎖器之特性。 2、瞭解各種正反器之特性。 3、瞭解各種正反器之邏輯功能。 4、瞭解正反器之應用。
Operating System Concepts 作業系統原理 Chapter 3 行程觀念 (Process Concept)
第10章 Verilog操作符 学习内容: 熟悉Verilog语言的操作符.
VHDL數位電路實習與專題設計 文魁資訊-UE301
触发器和时序电路分析 刘鹏 浙江大学信息与电子工程学院 March 30, 2017 ZDMC.
第5章 程序结构.
第4章 处理器(CPU) 4.1 引言 4.2 逻辑设计的一般方法 4.3 建立数据通路 4.4 一个简单的实现机制 4.5 多周期实现机制.
Timer & KEYPAD 11/24.
VHDL數位電路實習與專題設計 文魁資訊-UE301
Chapter 5 Verilog 硬體描述語言
Chapter 5 Verilog硬體描述語言
结构化编程 FC OB1 FB SFC 操作系统 SFB OBs 其它
邏輯設計.
Ch01-2 Verilog語法 資料流(DataFlow)設計 行為(Behavior)設計
EDA技术 廖义奎.
VHDL 硬體描述語言 數位電路設計實務 第四章 VHDL 的語言結構.
EDA技术实用教程 第1章 概 述.
VHDL 硬體描述語言 數位電路設計實務 第六章 函數副程序以及套件程式庫.
EDA原理及应用 何宾
EDA原理及应用 何宾
第四章 同步时序电路的分析 二进制串行计数器 二进制同步计数器 用跳越的方法实现任
第四阶段实验 ISP器件的设计与应用 一、实验目的 二、实验内容与要求 三、ISP器件的开发流程 四、EDA Pro2K实验系统介绍
欢迎参加VHDL培训 VHDL培训教程 浙江大学电子信息技术研究所 电子设计自动化(EDA)培训中心
数字系统设计复习 Digital System Design Summary
Danny Mok Altera HK FAE AHDL培训教材 Danny Mok Altera HK FAE 2018/12/9 P.1.
Verilog硬件描述语言基础.
第14章 其它DSP设计库 14.1 总线控制库 14.2 复数信号库 14.3 Gates库 14.4 状态机函数库
APEX 20K 用于系统集成的嵌入式 PLD 系列 © 1998 Altera Corporation 1
Programmable Logic Architecture Verilog HDL FPGA Design
使用VHDL設計--Moore Machine
JTAG INTERFACE SRAM TESTER WITH C-LCM
软件工程 Software Engineering
陳慶瀚 機器智慧與自動化技術(MIAT)實驗室 國立中央大學資工系 2013年5月28日
EDA 技术实用教程 第 5 章 QuartusII 应用向导.
语法进阶.
时序电路设计 刘鹏 浙江大学信息与电子工程系 Apr. 24, 2011 EE141
第12章 图像边缘检测器的设计与分析 12.1 系统设计要求 12.2 系统设计方案 12.3 主要LPM原理图和VHDL源程序
数字集成电路设计入门 --从HDL到版图 于敦山 北大微电子学系.
精简指令集(RISC)CPU的构造原理和设计方法
第6章 FIR数字滤波器设计 6.1 FIR数字滤波器原理 6.2 使用DSP Builder设计FIR数字滤波器
触发器和时序电路分析 刘鹏 浙江大学信息与电子工程学院 March 29, 2016 ZDMC.
第五章 VHDL设计基础 本章重点: 本章难点: VHDL程序结构 VHDL的基本数据类型 VHDL的基本描述语句 基本组合逻辑电路设计
第五章 VHDL主要描述语句.
第一次上机安排 第六周 第七周 周一晚(提高1、2,通信001~012) 周二上(通信014~085) 周四上(通信086~154)
计算机算法基础 周中成 苏州科技学院应用数学系 2019/4/18.
第14章 对验证的支持 学习内容 理解Verilog文本输出 理解不同的读取仿真时间的系统函数 理解 Verilog文件I/O功能.
计算机学院 数字逻辑实验的要求.
软件设计任务 从工程管理的角度来看,软件设计分两步完成。 概要设计,将软件需求转化为数据结构和软件的系统结构。
计算机EDA设计 教 程 北航计算机学院 艾明晶.
设计示例一 用门级结构描述D触发器:.
數位邏輯設計 VHDL.
陳慶瀚 機器智慧與自動化技術(MIAT)實驗室 國立中央大學資工系 2009年10月22日
FPGA组合逻辑 王安然.
Verilog HDL 基本语法 STEP 2016/12/3.
FPPA 程式設計 這一章開始正式進入FPPA程式設計的世界,我會從一個簡單的程式開始解說,一邊介紹專案開發時最常應用的技巧還有程式控制的手段,一邊把系統規畫的概念導入給大家,讓大家對專案的規畫能有一個初步的概念。
按键处理部分 王安然.
Programmable Logic System Design
陳慶瀚 機器智慧與自動化技術(MIAT)實驗室 國立中央大學資工系 2013年5月28日
第七章 基本逻辑电路设计.
Presentation transcript:

第17章 Verilog中的高级结构 学习内容: 任务和函数的定义和调用 怎样使用命名块 怎样禁止命名块和任务 有限状态机(FSM)及建模

Verilog的任务及函数 结构化设计是将任务分解为较小的,更易管理的单元,并将可重用代码进行封装。这通过将设计分成模块,或任务和函数实现。 任务(task) 通常用于调试,或对硬件进行行为描述 可以包含时序控制(#延迟,@, wait) 可以有 input,output,和inout参数 可以调用其他任务或函数 函数(function) 通常用于计算,或描述组合逻辑 不能包含任何延迟;函数仿真时间为0 只含有input参数并由函数名返回一个结果 可以调用其他函数,但不能调用任务

Verilog的任务及函数 任务和函数必须在module内调用 在任务和函数中不能声明wire 所有输入/输出都是局部寄存器 任务/函数执行完成后才返回结果。 例如,若任务/函数中有forever语句,则永远不会返回结果

任务 下面的任务中含有时序控制和一个输入,并引用了一个module变量,但没有输出、输入输出和内部变量,也不显示任何结果。 时序控制中使用的信号(例如ck)一定不能作为任务的输入,因为输入值只向该任务传送一次。 module top; reg clk, a, b; DUT u1 (out, a, b, clk); always #5 clk = !clk; task neg_clocks; 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

任务 主要特点: 任务可以有input,output 和 inout参数。 传送到任务的参数和与任务I/O说明顺序相同。尽管传送到任务的参数名称与任务内部I/O说明的名字可以相同,但在实际中这通常不是一个好的方法。参数名的唯一性可以使任务具有好的模块性。 可以在任务内使用时序控制。 在Verilog中任务定义一个新范围(scope) 要禁止任务,使用关键字disable 。 从代码中多处调用任务时要小心。因为任务的局部变量的只有一个拷贝,并行调用任务可能导致错误的结果。在任务中使用时序控制时这种情况时常发生。 在任务或函数中引用调用模块的变量时要小心。如果想使任务或函数能从另一个模块调用,则所有在任务或函数内部用到的变量都必须列在端口列表中。

任务 下面的任务中有输入,输出,时序控制和一个内部变量,并且引用了一个module变量。但没有双向端口,也没有显示。 任务调用时的参数按任务定义的顺序列出。 module mult (clk, a, b, out, en_mult); input clk, en_mult; input [3: 0] a, b; output [7: 0] out; reg [7: 0] out; always @( posedge clk) multme (a, b, out); // 任务调用 task multme; // 任务定义 input [3: 0] xme, tome; output [7: 0] result; wait (en_mult) result = xme * tome; endtask endmodule

函数(function) 函数中不能有时序控制,但调用它的过程可以有时序控制。 函数名f_or_and在函数中作为register使用 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; input [7:0] a, b, c, d, e; if (e = = 1) f_or_and = (a | b) & (c | d); else f_or_and = 0; endfunction endmodule 函数中不能有时序控制,但调用它的过程可以有时序控制。 函数名f_or_and在函数中作为register使用

函数 主要特性: 函数定义中不能包含任何时序控制语句。 函数至少有一个输入,不能包含任何输出或双向端口。 函数只返回一个数据,其缺省为reg类型。 传送到函数的参数顺序和函数输入参数的说明顺序相同。 函数在模块(module)内部定义。 函数不能调用任务,但任务可以调用函数。 函数在Verilog中定义了一个新的范围(scope)。 虽然函数只返回单个值,但返回的值可以直接给信号连接赋值。这在需要有多个输出时非常有效。 {o1, o2, o3, o4} = f_ or_ and (a, b, c, d, e);

函数 要返回一个向量值(多于一位),在函数定义时在函数名前说明范围。函数中需要多条语句时用begin和end。 不管在函数内对函数名进行多少次赋值,值只返回一次。下例中,函数还在内部声明了一个整数。 module foo; input [7: 0] loo; output [7: 0] goo; // 可以持续赋值中调用函数 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

函数 函数返回值可以声明为其它register类型:integer, real, 或time。 在任何表达式中都可调用函数 module checksub (neg, a, b); output neg; reg neg; input a, b; function integer subtr; input [7: 0] in_a, in_b; subtr = in_a - in_b; // 结果可能为负 endfunction always @ (a or b) if (subtr( a, b) < 0) neg = 1; else neg = 0; endmodule

函数 函数中可以对返回值的个别位进行赋值。 函数值的位数、函数端口甚至函数功能都可以参数化。 . . . parameter MAX_BITS = 8; reg [MAX_BITS: 1] D; function [MAX_BITS: 1] reverse_bits; input [MAX_BITS-1: 0] data; integer K; for (K = 0; K < MAX_BITS; K = K + 1) reverse_ bits [MAX_BITS - (K+ 1)] = data [K]; endfunction always @ (posedge clk) D = reverse_bits (D) ;

命名块(named block) 在关键词begin或fork后加上 :<块名称> 对块进行命名 在命名块中可以声明局部变量 module named_ blk; . . . begin : seq_blk end fork : par_blk join endmodule 在命名块中可以声明局部变量 可以使用关键词disable禁止一个命名块 命名块定义了一个新的范围 命名块会降低仿真速度

禁止命名块和任务 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 [15: 0] out; always @( posedge clk) begin : arith_block // *** 命名块 *** reg [3: 0] tmp1, tmp2; // *** 局部变量 *** {tmp1, tmp2} = f_or_and (a, b, c, d, e); // 函数调用 if (en_mult) multme (tmp1, tmp2, out); // 任务调用 end always @( negedge en_mult) begin // 中止运算 disable multme ; // *** 禁止任务 *** disable arith_block; // *** 禁止命名块 *** // 下面[定义任务和函数 …… endmodule

禁止命名块和任务 disable语句终结一个命名块或任务的所有活动。也就是说,在一个命名块或任务中的所有语句执行完之前就返回。 语法: 当命名块或任务被禁止时,所有因他们调度的事件将从事件队列中清除 disable是典型的不可综合语句。 在前面的例子中,只禁止命名块也可以达到同样的目的:所有由命名块、任务及其中的函数调度的事件都被取消。

有限状态机 隐式状态机FSM 不需要声明状态寄存器 仿真效率高 只适合于线性的状态改变 大多数综合工具不能处理 显式FSM: 利于结构化 易于处理缺省条件 能处理复杂的状态改变 所有综合工具都支持

有限状态机 在隐式FSM中,只要数据在一个时钟沿写入并在另一个周期读出, 则会生成寄存器。 通常,如果状态改变简单明确,且综合工具接受隐式状态机,就 可以使用隐式类型。如果状态改变很复杂,则显式类型更加有效。 隐式状态机是一个行为级而非RTL代码的典型例子。这种代码依 赖循环和内嵌时序控制,有时也有命名事件、wait和disable语句。 因此,隐式状态机在综合时通常不被支持。 线性FSM是指从一个状态到下一个状态的转换不需要任何条件。

显式有限状态机 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语句显式地描述FSM。 必须声明定义状态机的状态状态变量。 要改变当前状态,必须在时钟边沿改变状态变量的值。 给通常不会发生的条件指定缺省动作是一个很好的描述方式。

隐式有限状态机 module imp (out, datain, clk, rst); output out; reg out; input clk, datain, rst; always @( rst) // Synergy reset method if (rst) assign out = 1’b0; else begin deassign out; disable seq_block; //返回初始状态 end always @( posedge clk) begin: seq_block out = 1’b0; if (!datain) // 状态1: output = 0 disable seq_block; // 状态2: output = 2nd bit @( posedge clk) out = datain; endmodule

隐式有限状态机 可以在过程块中用多个时钟边沿(每个状态一个),条件语句, 循环,和disable语句隐式地描述一个FSM。通常不可综合。 不必声明状态变量。 在下一个时钟边沿改变状态,除非迫使状态重复。例如在一个循 环中或用一个disable语句。下一个状态可以由条件语句决定。

复习 在Verilog中什么结构能产生一个新的“范围” ? 哪些结构可以被禁止? 什么时候一个函数比一个任务更合适?反过来呢? 解答 命名块和任务可以被禁止。 函数更适用于组合逻辑描述,并且使用灵活(例如在一个持续赋值的右边或在一个端口列表里)。如果需要时序控制,则任务更适合。任务还可以被禁止。

第18章 用户定义基本单元 学习内容: 学习如何使用用户定义基本单元进行逻辑设计

术语及定义 UDP: 用户定义基本单元,其行为和Verilog内部的基本单元相似。其功能用真值表定义。

什么是UDP 在Verilog结构级描述中,可以使用: UDP在ASIC库单元开发、中小型芯片设计中很有用 二十多个内部门级基本单元 用户自定义基本单元 UDP在ASIC库单元开发、中小型芯片设计中很有用 可以使用UDP扩充已定义的基本单元集 UDP是自包容的,也就是不需要实例化其它模块 UDP可以表示时序元件和组合元件 UDP的行为由真值表表示 UDP实例化与基本单元实例化相同

什么是UDP 可以使用UDP扩充已定义的基本单元集 UDP是一种非常紧凑的逻辑表示方法。 UDP可以减少消极(pessimism)因素,因为一个input上的x不会像基本单元那样自动传送到output。 一个UDP可以替代多个基本单元构成的逻辑,因此可以大幅减少仿真时间和存储需求。相同逻辑的行为级模型甚至可以更快,这取决于仿真器。

UDP的特点 UDP只能有一个输出 如果在功能上要求有多个输出,则需要在UDP输出端连接其它的基本单元,或者同时使用几个UDP。 若输入端口超过5,存储需求会大幅增加。下表列出输入端口数与存储需求的关系。 #输入 存储器(KB) 1-5 1 8 56 6 5 9 187 7 17 10 623 所有端口必须为标量且不允许双向端口 不支持逻辑值Z 输出端口必须列为端口列表的第一个 时序UDP输出端可以用initial语句初始化为一个确定值。 UDP不可综合

组合逻辑举例:2-1多路器 UDP名称 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 输出端口 真值表中?表示的逻辑值为:0、1或x 这两行表示不管b为何值,若s为1,o输出a值 这两行表示不管a为何值,若s为o,o输出b值 这两行用于减少消极因素。表示若a, b有相同逻辑值,即使sel=x,o也输出与a,b相同的值。Verilog内部基本单元不能描述这种行为。UDP将X作为真实世界的未知值(0或1),而不是Verilog值,描述也更为精确。 UDP在模块(module)外部定义。 没有在真值表中说明的输入组合,输出X。 真值表中输入信号按端口列表顺序给出。

组合逻辑举例:全加器 全加器可以由两个组合逻辑UDP实现 // FULL ADDER SUM TERM 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 // FULL ADDER CARRY-OUT TERM 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; endtable endprimitive

组合逻辑举例:全加器 全加器可以由两个组合逻辑UDP实现,而不使用内部基本单元。 当需要大量全加器时,可以大幅度减少存储器需求

电平敏感时序元件举例:锁存器latch 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 输出必须声明为reg以保存前一状态 时序UDP初始化语句,将输出初始化为1 用另一个场表示下一状态 ‘-’状态值表示输出没有变化 输入及当前状态中的?表示无关值 锁存器的行为如下: 当时钟输入为0时,data输入的值传送到输出。 当时钟输入为1时,输出不变。 这种加电初始化在实际元件中很少见,但在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 ; //忽略时钟下降沿 (?0) ? : ? : - ; (1x) ? : ? : - ; //时钟稳定时忽略data变化 ? (??) : ? : - ; endtable endprimitive 表里有边沿项表示输入跳变。 在一条入口语句中只能说明一个输入跳变,因为Verilog仿真是基于事件,一次只允许一个事件发生。 在每个时间步中,电平入口优先于边沿入口,因为电平最后处理。因此,下面的出口: (? 0) ? : ? : - ; 可由下式取代: 0 ? : ? : - ; 两个都给出时,只有后者起作用 在任何一个真值表入口语句中只能说明一个输入跳变。 如果说明了任何输入跳变,则必须说明所有输入上的所有跳变。

提高可读性的简写形式 Verilog中有一些符号可用于UDP真值表中以提高可读性 符号 表示 解释 - 没有变化 时序元件输出的下一个值与当前值相同 ? 0、1或x 任何值 b 0或1 任何确定值 r (01) 0->1跳变 f (10) 1->0跳变 p (01)、(0x)或(x1) 任何上升沿(posedge) n (10)、(1x)或(x0) 任何下降沿(negedge) * (??) 任何跳变

提高可读性的简写形式 table // clk dat state next r 0 : ? : 0 ; r 1 : ? : 1 ; // 忽略时钟的下降沿 n ? : ? : - ; // 忽略时钟稳定时的任何数据变化 ? * : ? : - ; endtable

带同步复位的D触发器 primitive U_ff_p_cl( q, d, clk, cl); input d, clk, cl; output q; reg q; table // d clk cl :q :q+ 1 1 r 1 :? : 1; // clock 1 0 r ? :? : 0; // clock 0 ? r 0 :? : 0; // reset ? p 0 :0 : -; // reducing pessimism 1 p 1 :1 : -; 0 p ? :0 : -; ? n ? :? : -; // ignore falling clk * ? ? :? : -; // ignore changes on d ? ? * :? : -; // ignore changes on clk endtable endprimitive

带使能和复位的锁存器 当使能g为高(H)时,锁存器锁存d;只有当g为低时复位信号cl才有效(高有效)。 primitive u_latch_cl (q, d, g, cl); output q; input d, g, cl; reg q; table // d g cl :q :q+ 1 0 1 ? :? : 0; 1 1 ? :? : 1; 1 ? 0 :1 : 1; // reducing pessimism 0 ? 0 :0 : 0; // reducing pessimism 0 ? 1 :? : 0; // reducing pessimism 0 ? ? :0 : 0; // reducing pessimism ? 0 0 :? : -; // latch disabled ? 0 1 :? : 0; // clear endtable endprimitive

使用通报符(notifier)的寄存器 下面的例子是异步复位的上升沿D触发器,有时序检查和路径延迟。这个模型使用了一个UDP,并将通报符作为UDP的一个输入。 primitive U_ FFD_ RB (Q, D, CP, RB,NT); output Q; reg Q; input D, CP, RB, NT; table // D CP RB NT :Q :Q+ 1 0 r ? ? :? : 0; // clock a 0 1 r 1 ? :? : 1; // clock a 1 1 p 1 ? :1 : -; // reducing pessimism 0 p ? ? :0 : -; // reducing pessimism ? ? 0 ? :? : 0; // asynchronous reset ? ? x ? :0 : -; // reducing pessimism ? n ? ? :? : -; // ignore falling clock * ? ? ? :? : -; // ignore rising edges on D ? ? * ? :? : -; // ignore changes on reset ? ? ? * :? : x; // NT变化使Q产生x endtable endprimitive `timescale 1ns/ 1ns module dff_nt (q, ck, d, rst); input ck, d, rst; output q; reg nt; U_FFD_RB i1 (q, d, ck, rst, nt); specify specparam tsu = 2; (ck => q) = (2: 3: 4); $setup (d, posedge ck, tsu, nt); endspecify endmodule

第19章 Verilog的可综合描述风格 学习目标: 学习组合逻辑和时序逻辑的可综合的描述风格及技术,包括: 不支持的Verilog结构 过程块 寄存器 敏感列表 持续赋值 综合指导 条件结构 阻塞及非阻塞赋值 锁存器/MUX推断 函数function 任务task 复位 有限状态机FSM 宏库及设计复用

描述风格简介 如果逻辑输出在任何时候都直接由当前输入组合决定,则为组合逻辑。 如果逻辑暗示存储则为时序逻辑。如果输出在任何给定时刻不能由输入的状态决定,则暗示存储。 通常综合输出不会只是一个纯组合或纯时序逻辑。 一定要清楚所写的源代码会产生什么类型输出, 并能够反过来确定为什么所用的综合工具产生这个输出, 这是非常重要的。

不支持的Verilog结构 综合工具通常不支持下列Verilog结构: initial 循环: UDP repeat fork…join块 forever while 非结构化的for语句 数据类型: event real time UDP fork…join块 wait 过程持续赋值: assign deassign force release 操作符: = = = ! = =

过程块 任意边沿 在所有输入信号的任意边沿进入的过程块产生组合逻辑。这种过程块称为组合块。 always @( a or b) // 与门 y = a & b; 单个边沿 在一个控制信号的单一边沿上进入的过程块产生同步逻辑。这种过程块称为同步块。 always @( posedge clk) // D flip-flop q <= d; 同步块也可以对异步复位信号的变化产生敏感 always @( posedge clk or negedge rst_) if (! rst_) q <= 0; else q <= d;

过程块中的寄存器类型 若同步块中使用一个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 在这个例子中,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

组合逻辑中的寄存器类型举例 在下面的例子, rega是暂存变量,并被优化掉 在这个例子中, y和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 在这个例子中,rega不总是产生新值,因此会产生一个锁存器,y是锁存器的输出 module 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用作条件 a、b用在过程赋值语句的右边 敏感表不完全: 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 sensc (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 将块的所有输入都列入敏感表是很好的描述习惯。不同的综合工具对不完全敏感表的处理有所不同。有的将不完全敏感表当作非法。其他的则产生一个警告并假设敏感表是完全的。在这种情况下,综合输出和RTL描述的仿真结果可能不一致。

敏感列表 将块的所有输入都列入敏感表是很好的描述习惯。不同的综合工具对不完全敏感表的处理有所不同。有的将不完全敏感表当作非法。其他的则产生一个警告并假设敏感表是完全的。在这种情况下,综合输出和RTL描述的仿真结果可能不一致。 上述两例综合结果(SYNOPSYS)相同,但RTL描述的仿真结果不同。也就是左边的敏感表不完全的例子的RTL描述和综合出的网表的仿真结果不同。 module sens_t; reg a, b, sl; sens u1(a, q, b, sl); sensc u2(qc, a, b, sl); initial begin $monitor($time," %b %b %b %b %b", a, b, sl, q, qc); a =0;b=0;sl = 0; #10 a =1; #10 sl = 1; #10 sl = 0; #10 $finish; end endmodule 0 0 0 0 0 0 10 1 0 0 0 1 20 1 0 1 0 0 30 1 0 0 1 1

持续赋值 持续赋值驱动值到net上。因为驱动是持续的,所以输出将随任意输入的改变而随时更新,因此将产生组合逻辑。 module orand (out, a, b, c, d, e); input a, b, c, d, e; output out; assign out = e & (a | b) & (c | d); endmodule

过程持续赋值 过程持续赋值是在过程块(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

综合指示 大多数综合工具都能处理综合指示。 综合指示可以嵌在Verilog注释中,因此他们在Verilog仿真时忽略,只在综合工具解析时有意义。 不同工具使用的综合指示在语法上不同。但其目的相同,都是在RTL代码内部进行最优化。 通常综合指示中包含工具或公司的名称。例如,下面介绍的Envisia Ambit synthesis工具的编译指示都以ambit synthesis开头。

综合指示 这里列出部分Cadence综合工具中综合指示。这些与其他工具,如Synopsys Design Compiler,中的指示很相似。 // ambit synthesis on // ambit synthesis off // ambit synthesis case = full, parallel, mux 结构指示 // ambit synthesis architecture = cla or rpl FSM指示 // ambit synthesis enum xyz // ambit synthesis state_vector sig state_vector_ flag

综合指示 — case指示 case语句通常综合为一个优先级编码器,列表中每个case项都比后面的case项的优先级高。 //ambit synthesis case = parallel 建立并行的编码逻辑,彼此无优先级。 //ambit synthesis case = mux 若库中有多路器,使用多路器建立编码逻辑。 //ambit synthesis case = full 假定所有缺少的case项都是“无关”项,使逻辑更为优化并避免产生锁存器。

条件语句 自然完全的条件语句 例中定义了所有可能的选项,综合结果是纯组合逻辑,没有不期望的锁存器产生。 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 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 例中定义了所有可能的选项,综合结果是纯组合逻辑,没有不期望的锁存器产生。

不完全条件语句 e 为何值 若 a 变为 0, 在上面的例子中,当a变为零时,不对e赋新值。因此e保存其值直到a变为1。这是锁存器的特性。 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; always @( a or b or c or d) if (a & b) e = d; else if (a & ~b) e = ~c; endmodule 在上面的例子中,当a变为零时,不对e赋新值。因此e保存其值直到a变为1。这是锁存器的特性。

default完全条件语句 综合工具将 ‘bx作为无关值,因此if语句类似于“ full 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; endcase 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 综合工具将 ‘bx作为无关值,因此if语句类似于“ full 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项不会发生。结果也为纯组合逻辑,没有不期望锁存器产生。注意如果缺少的case项发生,而其结果未定义,综合结果和RTL的描述的行为可能不同。

case指示例外 有时使用了case full指示,case语句也可能综合出latch。 下面的描述综合时产生了一个latch。 module select (a, b, sl); input [1: 0] sl; output a, b; req a, b; always @( sl) case (sl) // ambit synthesis case = full 2b'00: begin a = 0; b = 0; end 2b'01: begin a = 1; b = 1; end 2b'10: begin a = 0; b = 1; end 2b'11: b = 1; default: begin a = 'bx; b = 'bx; end endcase endmodule

函数 函数没有时序控制,因此综合结果为组合逻辑。函数可以在过程块内或持续赋值语句中调用。 下例中的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

锁存器(latch)推断 在always块中,如果没有说明所有条件,将产生latch。在下面的例子中,由于没有定义enable为低电平时data的状态,因此enable为低电平时data的值必须保持,综合时将产生一个存储元件 module latch (q, data, enable); input data, enable; output q; reg q; always @( enable or data) if (enable) q = data; endmodule

同步反馈(feedback)推断 综合工具一般不支持组合逻辑反馈,但支持同步反馈。 在同步过程块中,如果条件语句的一个分支没有给所有输出赋值,则推断出反馈。 无反馈: 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

带使能的寄存器 上述带反馈的描述用于带使能端的寄存器的描述。在寄存器的描述中,敏感列表是不完全的。 module dffn (q, d, clk, en); input d, clk, en; output q; reg q; always @( negedge clk) if (en) q <= d; endmodule

阻塞或非阻塞 使用的赋值类型依赖于所描述的逻辑类型: 在时序块RTL代码中使用非阻塞赋值 非阻塞赋值保存值直到时间片段的结束,从而避免仿真时的竞争情况或结果的不确定性 在组合的RTL代码中使用阻塞赋值 阻塞赋值立即执行

阻塞、非阻塞对比 非阻塞赋值语句并行执行,因此临时变量不可避免地在一个周期中被赋值,在下一个周期中被采样。 使用阻塞赋值,此描述综合出一个D flip-flop: 使用非阻塞赋值,此描述将综合出两个D Flip-flop。 module bloc (clk, a, b); input clk, a; output b; reg y; reg b; always @( posedge clk) begin y =a; b =y; end endmodule module nonbloc (clk, a, b); input clk, a; output b; reg y; reg b; always @( posedge clk) begin y <= a; b <= y; end endmodule

复位 复位是可综合编码风格的重要环节。状态机中一般都有复位。 同步复位 同步块的异步复位 module sync( q, ck, r, d); input ck, d, rst; 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 同步复位描述:在同步块内,当复位信号有效时,进行复位操作;当复位信号无效时,执行该块的同步行为。如果将复位信号作为条件语句的条件,且在第一个分支中进行复位,综合工具可以更容易的识别复位信号。 异步复位:在同步块的敏感表中包含复位信号的激活边沿。在块内,复位描述方式与同步方式相同。

复位 下面的异步复位描述(异步复位和同步块分开)是一种很不好的描述风格,并且有的综合工具不支持。在仿真中,如果r和ck在同一时刻改变,则结果不可确定。 不好的异步复位描述方式 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

带复位、置位的锁存器latch 下面的例子给出了一个复杂一些的复位分支。由于是一个latch,因此敏感表是完全的。 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

有限状态机 有限状态机有两种不同类型 — 显式和隐式。 隐式状态机用多个@(posedge clk)语句指出状态跳变。 隐式状态机的抽象级比显式状态机高,通常不可综合。 显式状态机用case语句显式定义每个可能状态。 通常,显式状态机用于可综合代码描述。

显式有限状态机 always @( state) // 定义下一状态的组合逻辑 `timescale 1ns/100ps case (state) stateA: begin nextstate = stateB; out = 2‘b00; // 输出决定于当前状态 end stateB: begin nextstate = stateC; out = 2'b11; stateC: begin nextstate = stateD; out = 2'b10; stateD: begin nextstate = stateA; out = 2'b00; endcase endmodule `timescale 1ns/100ps module state4 (clock, reset, out); input reset, clock; output [1: 0] out; reg [1: 0] out; parameter //状态变量枚举 stateA = 2'b00, stateB = 2'b01, stateC = 2'b10, stateD = 2'b11; reg [1: 0] state; //状态寄存器 reg [1: 0] nextstate; always @( posedge clock) if (reset) //同步复位 state <= stateA; else state <= nextstate;

有限状态机FSM指导 状态机的描述也有综合指导。在RTL代码中,FSM指导向优化器传递状态机有关的特性信息。 这些指导有: enum指导 状态赋值枚举,也用来将状态赋值捆绑到状态向量。 state_vector指导 定义状态寄存器和编码类型

FSM指导 `timescale 1ns/ 100ps module state4 (clock, reset, out); input reset, clock; output [1: 0] out; reg [1: 0] out; parameter /* 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; always @( posedge clock) /* ambit synthesis state_vector state -encoding one_hot */ if (reset) state <= stateA; else state <= nextstate; . . . 枚举名称定义 枚举名称限用于state、nextstate向量 定义状态寄存器并指定编码格式

资源共享 资源共享是指多节代码共享一组逻辑。例如: 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; 大多数工具可以将它映射为一个乘法器。 专用宏单元库中可能有乘法器。宏单元库中的元件的复杂程度要比常规单元库高。 宏单元库可以包含部分可重用设计,如FIFO,加法器,减法器(各种结构),移位寄存器,计数器和解码器等。、 宏单元库还可以包括用户自定义的可重用块,由用户自己设计并综合。

综合工具不能胜任的工作 时钟树 复杂的时钟方案 组合逻辑反馈循环和脉冲发生器 存储器,IO 专用宏单元 总做得和你一样好

综合工具不能胜任的工作 综合工具善于优化组合逻辑。但设计中有很大一部分不是组合逻辑。 例如,时钟树。时钟树是全局的、芯片范围的问题。在没有版图布局信息的情况下,要给出较优的结果,综合工具对块的大小有一定的限制。 综合工具不能很好地处理复杂时钟。通常,只允许要综合的块含有一个时钟。但设计中经常使用两相时钟或在双沿时钟。 综合工具不易实现脉冲产生逻辑,如单个脉冲,或结果依赖于反馈路径延迟的组合反馈逻辑。对这种情况,插入延迟元件使一个信号推迟到达的效果并不好。 不能用综合产生大块存储器,因为综合工具会用flip-flop实现。 不是所有的综合工具都能很好地从工艺库里挑选择大的单元或宏单元,这需要用户人工实例化。一些宏单元,例如大的结构规则的数据通路元件,最好使用生产商提供的硅编译器产生。 综合工具不保证产生最小结果。通常综合结果不如人工结果,只要你有足够的时间。

可编程逻辑器件相关问题 迄今为止,很多注释假定综合为ASIC。对FPGA,存在一些不同问题。所有ASIC综合工具以同样的方式工作,使用同样的优化算法,且目标工艺库的单元也是相似的。众所周知,FPGA使用不同的技术,EEPROM,SRAM和anti-fuse,且每个开发商有不同的构造块,这些块比ASIC使用的基本门要大很多。 对特定的FPGA,其结构是固定的,限制了静态时序分析的效用,因为ASIC的路径延迟变化的范围很大,而FPGA是可预测的。而且,FPGA设计时通常使用专用结构,而这些专用结构很难由综合工具实现。 因此Verilog代码需要技术与工艺专用指导,这些指导由FPGA开发商提供的软件的算法识别,而仿真时忽略。这些指导通常由用户自定义属性,注释或直接实例化专用单元。这会限制Verilog的技术独立性和可移植性。 关键问题是综合工具能够多好地处理所选的技术。要得到一个好的结果,需要结构专用算法。

第21章 SDF时序标注 学习内容: 延迟计算器 标准延迟格式(Standard Delay Format)(SDF) 标注SDF数据

术语及定义 CTLF:(Compiled Timing Library Format)编译的时序库格式。特定工艺元件数据的标准格式。 GCF:(General constraint Format)通用约束格式。约束数据的标准格式。 MIPD:(Module Input Port Delay)模块输入端口延时。模块输入或输入输出端口的固有互连延时 MITD:(Multi-source Interconnect Transport Delay)多重互连传输延时。与SITD相似,但支持多个来源的不同延时。 PLI:(Programming Language Interface)编程语言界面。基于C的对Verilog数据结构的程序访问。 SDF:Standard Delay Format.(标准延迟格式)。时序数据OVI标准格式。 SITD:Single-Source Interconnect Transprot Delay,单一源互连传输延迟。和MIPD相似,但支持带脉冲控制的传输延迟。 SPF:Standard Parasitic Format.(标准寄生参数格式)。提取的寄生参数数据的标准格式。

时序标注 通常的Verilog元件库仅包含固有时序数据。 若要进行精确的时序仿真,还需要的数据有: 输入传输时间 固有延迟 驱动强度 总负载 互连寄生 环境因子 过程 温度 电压 同时还需要仿真最坏情况下的数据和最佳情况下时钟,反过来也要做一次。在没有时序标注时Verilog仿真器做不到这一点。

时序数据流

时序数据流程 延时计算器需要: 延迟计算器可以产生: 综合出来的网表 布局布线工具产生的简化的寄生参数 粗略延迟,仅基于设计连线和层次 详细延迟,由后端工具提取的寄生参数信息 有时序驱动的自顶而下的设计方法中,时序约束贯穿整个设计流程。与时序数据仅向后反馈的情况,如从布线布线工具反馈到综合工具,相比,这种方法时序收敛速度快。 前端和后端工具使用统一的延迟计算器 会提高时序收敛速度。 大多数EDA工具接受标准延迟格式(SDF)。

延迟计算器 延时计算器主要有两类: 嵌入在工具中的延迟计算器 用户延迟计算器 用户自定义 开发商提供 延迟计算器可以产生SDF数据,或直接使用PLI标注时序数据。 延迟计算器可以自定义,但必须选择一个合适的延迟公式。 大多数ASIC生产商提供自己的生产工艺的延迟计算器。这些延迟计算器通常用PLI编写并直接在仿真时标注到设计中。但计算器也可以是独立的程序,产生的SDF由内嵌的延迟标注工具进行标注。

SDF(标准延迟格式) 标准延迟格式(SDF)是统一的时序信息表示方法,与工具无关。它可以表示: 模块通路延迟——条件的和无条件的 器件延迟 互连延迟 端口延迟 时序检查 通路和net时序约束 注意:在specity块中不能说明互连延迟或输入端口延迟。要用互连延迟仿真,必须进行时序标注。 模块输入端口延迟(MIPD)描述的是到模块输入端口或双向端口的延迟。延迟为惯性的且影响三种跳变:到1,到0,和到z。 单一源输入传输延迟(SITD)和MIPD相似,但使用传输延迟并且有全局和局部脉冲控制。SITD影响6种跳变:0到1,1到0,0到z,z到0,1到z,z到1。 多重输入传输延迟(MITDs)和SITD相似,但允许为每个源-负载通路说明独立延迟。

SDF举例 SDF文件配置信息 (DELAYFILE (DESIGN "system") (DATE "Mon Jun 1 14:54:29 PST 1992") (VENDOR "Cadence") (PROGRAM "delay_calc") (VERSION "1.6a, 4") (DIVIDER /) /* hierarchical divider */ (VOLTAGE 4.5:5.0: 5.5) (PROCESS "worst") (TIMESCALE 1ns) /* delay time units */ (CELL (CELLTYPE "system") (INSTANCE block_1) /* top level blocks */ (DELAY (ABSOLUTE (INTERCONNECT D1/z P3/i (. 155::. 155) (. 130::. 130))))) (CELL (CELLTYPE "INV") (INSTANCE ) /* all instances of "INV" */ (DELAY (INCREMENT (IOPATH i z (. 345::. 348) (. 325::. 329))))) (CELL (CELLTYPE "OR2") (INSTANCE B1/C1) /* this instances of "OR2" */ (IOPATH i1 z (. 300::. 300) (. 325::. 325)) (IOPATH i2 z (. 300::. 300) (. 325::. 325))))) ) // end delay file SDF文件配置信息 可以指定某种单元 的所有实例或某个实例 延迟可以是绝对的或相对的

SDF标注工具 用系统任务$sdf_annotate标注SDF时序信息。 可以交互式界面调用这个任务,或在源代码中调任务。 $sdf_annotate ("sdf_file", [module_instance, "config_file"," log_file", "mtm_spec", "scale_factors"," scale_type"]); sdf_file:SDF文件的绝对或相对路径 module_instance:标注范围。缺省为调用$sdf_annotate所在的范围 config_file:配置文件的绝对或相对路径。缺省使用预设的设置。 Log_file:日志文件名,缺省为sdf.log。可以用+sdf_verbose选项生成一个日志文件。 Mtm_spec:选择标注的时序值,可以是{MINIMUM,TYPICAL,MAXIMUM,TOOL_CONTROL}之一。缺省为TOOL_CONTROL(命令行选项)。这个参数覆盖配置文件中MTM关键字。 Scale_factors:min:typ:max格式的比例因子,缺省为1.0:1.0:1.0。这个参数覆盖配置文件SCALE_FACTORS关键字。 Scale_type:选择比例因子;可以是{FROM_MINIMUM, FROM_TYPICAL, FROM_MAXIMUM, FROM_MTM}之一。缺省为FROM_MTM。这个参数覆盖配置文件中SCALE_TYPE关键字。 注意:除sdf_file的所有参数可以忽略。sdf_file可以是任意名字,然后在运行时使用命令行选项+sdf_file选项指定一个sdf_file。

执行SDF标注 在下面的例子中,在设计的最顶层进行带比例的SDF标注 在下面的例子中,对不同的实例分开标注 module top; . . . . . . . . initial $sdf_annotate ("my. sdf", , , , , 1.6:1.4:1.2); endmodule 在下面的例子中,对不同的实例分开标注 module top; . . . . . . . . cpu u1 ( . . . fpu u2 ( . . . dma u3 ( . . . initial begin $sdf_annotate ("sdffiles/cpu.sdf", u1, ,"logfiles/cpu_sdf.log"); $sdf_annotate ("sdffiles/fpu.sdf", u2, ,"logfiles/fpu_sdf.log"); $sdf_annotate ("sdffiles/dma.sdf", u3, ,"logfiles/dma_sdf.log"); end endmodule

执行SDF标注 和SDF标注相关的命令行选项: 命令 解释 +sdf_cputime 记录用于标注的CPU秒数 命令 解释 +sdf_cputime 记录用于标注的CPU秒数 +sdf_error_info 显示PLI标注工具错误信息 +sdf_file<filename> 覆盖系统任务$sdf_annotate中的文件名 +sdf_nocheck_celltype 禁止逐个实例进行单元类型确认 +sdf_no_errors 禁止SDF标注的错误信息 +sdf_nomsrc_int 通知标注工具没有MITD;可以提高性能 +sdf_no_warnings 禁止SDF标注的警告信息 +sdf_verbose 详细记录标注的过程信息  

总结 在本章中学习了: 延迟计算器 标准延迟格式SDF SDF数据标注

复习 问题: 解答: 什么情况下要进行时序标注? 延迟计算器通常需要哪些输入? 在设计的什么地方可以调用$sdf_annotate系统任务? 使用互连延迟仿真时进行时序标注,对同一个模块的不同实例使用不同的时序,这些时序是由元件物理特性计算出来的。 任何延迟计算器都需要物理连接和层次信息、生产商元件技术库、元件环境信息以及用户的指导(如,想让它做什么?)。另外,计算器根据后端工具提取的简化的寄生参数,可以提供更好的延迟估算。 通常,用户在设计的最顶层或testbench的initial块中调用$sdf_annotate系统任务,这样任务在时刻0时执行一次。也可以在交互式模式执行系统任务$sdf_annotate。 仿真器并不限制在哪里使用这个系统任务。

第22章 Coding Styles for Synthesis 主要内容: if语句和case语句的编码风格 if语句和case语句中晚到达信号的处理 逻辑块的编码风格 高性能编码技术 其它问题

if 语句 例1.1a 单个 if 语句 例1.1b 多重 if 语句 注意代码的优先级 module single_if(a, b, c, d, sel, z); input a, b, c, d; input [3:0] sel; output z; reg z; always @(a or b or c or d or sel) begin if (sel[3]) z = d; else if (sel[2]) z = c; else if (sel[1]) z = b; else if (sel[0]) z = a; else z = 0; end endmodule module mult_if(a, b, c, d, sel, z); input a, b, c, d; input [3:0] sel; output z; reg z; always @(a or b or c or d or sel) begin z = 0; if (sel[0]) z = a; if (sel[1]) z = b; if (sel[2]) z = c; if (sel[3]) z = d; end endmodule 注意代码的优先级

if语句

case语句 例1.2 case 语句 casex具有使用无关项的优点,不用列出sel的所有组合。 module case1(a, b, c, d, sel, z); input a, b, c, d; input [3:0] sel; output z; reg z; always @(a or b or c or d or sel) begin casex (sel) 4’b1xxx: z = d; 4’bx1xx: z = c; 4’bxx1x: z = b; 4’bxxx1: z = a; default: z = 1’b0; endcase end endmodule casex具有使用无关项的优点,不用列出sel的所有组合。

晚到达信号处理 设计时通常知道哪一个 信号到达的时间要晚一些。这些信息可用于构造HDL,使到达晚的信号离输出近一些。 下面的例子中,针对晚到达信号重新构造if和case语句,以提高逻辑性能。

晚到达的是数据信号 具有优先级的if结构 无优先级的if结构 顺序if语句可以根据关键信号构造HDL。在例1.1a 中,输入信号d处于选择链的最后一级,也就是说d最靠近输出。 假如信号b_is_late是晚到达信号,我们就要重新构造例1.1a使其最优化。 具有优先级的if结构 无优先级的if结构 module mult_if_improved(a, b_is_late, c, d, sel, z); input a, b_is_late, c, d; input [3:0] sel; output z; reg z, z1; always @(a or b_is_late or c or d or sel) begin z1 = 0; if (sel[0]) z1 = a; if (sel[2]) z1 = c; if (sel[3]) z1 = d; if (sel[1] & ~(sel[2]|sel[3])) z = b_is_late; else z = z1; end endmodule module single_if(a, b, c, d, sel, z); input a, b, c, d; input [3:0] sel; output z; reg z; always @(a or b or c or d or sel) begin if (sel[1]) z = b_is_late; else if (sel[2]) z = c; else if (sel[3]) z = d; else if (sel[0]) z = a; else z = 0; end endmodule

晚到达的是数据信号

晚到达的是控制信号 如果晚到达信号作为if语句条件分支的条件,也应使这个信号离输出最近。在下面的例子中,CTRL_is _late是晚到达的控制信号 module single_if_late(A, C, CTRL_is_late, Z); input [6:1] A; input [5:1] C; input CTRL_is_late; output Z; reg Z; always @(C or A or CTRL_is_late) if (C[1] == 1’b1) Z = A[1]; else if (C[2] == 1’b0) Z = A[2]; else if (C[3] == 1’b1) Z = A[3]; else if (C[4] == 1’b1 && CTRL_is_late == 1’b0) // late arriving signal in if condition Z = A[4]; else if (C[5] == 1’b0) Z = A[5]; else Z = A[6]; endmodule

晚到达的是控制信号 module single_if_late(A, C, CTRL_is_late, Z); input [6:1] A; input [5:1] C; input CTRL_is_late; output Z; reg Z; always @(C or A or CTRL_is_late) // late arriving signal in if condition if (C[4] == 1’b1 && CTRL_is_late == 1’b0) Z = A[4]; else if (C[1] == 1’b1) Z = A[1]; else if (C[2] == 1’b0) Z = A[2]; else if (C[3] == 1’b1) Z = A[3]; else if (C[5] == 1’b0) Z = A[5]; else Z = A[6]; endmodule

if-case嵌套语句 module case_in_if_01(A, DATA_is_late_arriving, C, sel, Z); input [8:1] A; input DATA_is_late_arriving; input [2:0] sel; input [5:1] C; output Z; reg Z; always @ (sel or C or A or DATA_is_late_arriving) if (C[1]) Z = A[5]; else if (C[2] = = 1’b0) Z = A[4]; else if (C[3]) Z = A[1]; else if (C[4]) case (sel) 3’b010: Z = A[8]; 3’b011: Z = DATA_is_late_arriving; 3’b101: Z = A[7]; 3’b110: Z = A[6]; default: Z = A[2]; endcase else if (C[5] = = 1’b0) Z = A[2]; else Z = A[3]; endmodule

if-case嵌套语句 Case语句 if语句

if-case嵌套语句—修改后 always @(sel or C or A or DATA_is_late_arriving) begin if (C[1]) Z1 = A[5]; else if (C[2] == 1’b0) Z1= A[4]; else if (C[3]) Z1 = A[1]; else if (C[4]) case (sel) 3’b010: Z1 = A[8]; //3’b011: Z1 = DATA_is_late_arriving; 3’b101: Z1 = A[7]; 3’b110: Z1 = A[6]; default: Z1 = A[2]; endcase else if (C[5] == 1’b0) Z1 = A[2]; else Z1 = A[3]; FIRST_IF = (C[1] == 1’b1) || (C[2] == 1’b0) || (C[3] == 1’b1); if (!FIRST_IF && C[4] && (sel = = 3’b011)) Z = DATA_is_late_arriving; else Z = Z1; end

if-case嵌套语句—修改后

逻辑构造块的编码格式 下面介绍某些常用逻辑块,如译码器的不同的编码格式。每种块给出了一个通常格式和建议格式。 所有的例子的位宽都是参数化的。

3-8译码器 index方式 loop方式 module decoder_index (in1, out1); parameter N = 8; parameter log2N = 3; input [log2N-1:0] in1; output [N-1:0] out1; reg [N-1:0] out1; always @(in1) begin out1 = 0; out1[in1] = 1’b1; end endmodule module decoder38_loop (in1, out1); parameter N = 8; parameter log2N = 3; input [log2N-1:0] in1; output [N-1:0] out1; reg [N-1:0] out1; integer i; always @(in1) begin for(i=0;i<N;i=i+1) out1[i] = (in1 == i); end endmodule

译码器

优先级编码器—高位优先 线性结构描述 1???_???? : 111 01??_???? : 110 001?_???? : 101 module priority_low_high (A, P); parameter N = 8; parameter log2N = 3; input [N-1:0] A; //Input Vector output [log2N-1:0] P; // High Priority Index reg [log2N-1:0] P; function [log2N-1:0] priority; input [N-1:0] A; integer I; begin priority = 3’b0; for (I=0; I<N; I=I+1) if (A[I]) priority = I;// Override previous index end endfunction always @(A) P <= priority(A); endmodule 线性结构描述 1???_???? : 111 01??_???? : 110 001?_???? : 101 0001_???? : 100 0000_1??? : 011 0000_01?? : 010 0000_001? : 001 0000_000? : 000

优先级编码器 线性结构 树形结构

归约XOR 线性结构 module XOR_reduce (data_in, data_out); parameter N = 5; input [N-1:0] data_in; output data_out; reg data_out; function XOR_reduce_func; input [N-1:0] data; integer I; begin XOR_reduce_func = 0; for (I = N-1; I >= 0; I=I-1) XOR_reduce_func = XOR_reduce_func ^ data[I]; end endfunction always @(data_in) data_out <= XOR_reduce_func(data_in); endmodule

归约XOR 线性结构 树形结构

归约XOR 树形结构 if (even(NUM)) for (I=NUM-1; I>=0; I=I-2) begin result[J] = temp[I] ^ temp[I-1]; J = J-1; end else begin for (I=NUM-1; I>=1; I=I-2) begin result[0] = temp[0]; temp[N-1:0] = result[N-1:0]; NUM = (NUM+1)/2; XOR_tree_func = result[0]; endfunction always @(data_in) data_out <= XOR_tree_func(data_in); endendmodule module XOR_tree(data_in, data_out); parameter N = 5; parameter logN = 3; input [N-1:0] data_in; output data_out; reg data_out; function even; input [31:0] num; even = ~num[0]; endfunction function XOR_tree_func; input [N-1:0] data; integer I, J, K, NUM; reg [N-1:0] temp, result; begin temp[N-1:0] = data_in[N-1:0]; NUM = N; for (K=logN-1; K>=0; K=K-1) begin J = (NUM+1)/2; J = J-1;

高性能编码技术 在某些情况下,可以通过重复逻辑来提高速度。 在下面的例子中,CONTROL是一个晚到达的输入信号。要提高性能,就要减少CONTROL到输出之间的逻辑。 module BEFORE (ADDRESS, PTR1, PTR2, B, CONTROL, COUNT); input [7:0] PTR1,PTR2; input [15:0] ADDRESS, B; input CONTROL; // CONTROL is late arriving output [15:0] COUNT; parameter [7:0] BASE = 8’b10000000; wire [7:0] PTR, OFFSET; wire [15:0] ADDR; assign PTR = (CONTROL == 1’b1) ? PTR1 : PTR2; assign OFFSET = BASE - PTR; assign ADDR = ADDRESS - {8’h00, OFFSET}; assign COUNT = ADDR + B; endmodule

高性能编码技术

高性能编码技术 module PRECOMPUTED (ADDRESS, PTR1, PTR2, B, CONTROL, COUNT); input [7:0] PTR1, PTR2; input [15:0] ADDRESS, B; input CONTROL; output [15:0] COUNT; parameter [7:0] BASE = 8’b10000000; wire [7:0] OFFSET1,OFFSET2; wire [15:0] ADDR1,ADDR2,COUNT1,COUNT2; assign OFFSET1 = BASE - PTR1; // Could be f(BASE,PTR) assign OFFSET2 = BASE - PTR2; // Could be f(BASE,PTR) assign ADDR1 = ADDRESS - {8’h00 , OFFSET1}; assign ADDR2 = ADDRESS - {8’h00 , OFFSET2}; assign COUNT1 = ADDR1 + B; assign COUNT2 = ADDR2 + B; assign COUNT = (CONTROL == 1’b1) ? COUNT1 : COUNT2; endmodule

高性能编码技术 在下面的例子中,if语句的条件表达中包含有操作符。 module cond_oper(A, B, C, D, Z); parameter N = 8; input [N-1:0] A, B, C, D; //A is late arriving output [N-1:0] Z; reg [N-1:0] Z; always @(A or B or C or D) begin if (A + B < 24) Z <= C; else Z <= D; end endmodule

高性能编码技术 若条件表达式中的信号A是晚到达信号。因此要移动信号A使其离输出近一些。

高性能编码技术 module cond_oper_improved (A, B, C, D, Z); parameter N = 8; input [N-1:0] A, B, C, D; // A is late arriving output [N-1:0] Z; reg [N-1:0] Z; always @(A or B or C or D) begin if (A < 24 - B) Z <= C; else Z <= D; end endmodule

其它要注意的问题 不要引入不必要的latch 敏感表要完整 非结构化的for循环 资源共享

不要产生不需要的latch 条件分支不完全的条件语句(if和case语句)将会产生锁存器 always @(cond_1) begin if (cond_1) data_out <= data_in; end always @(sel or a or b or c or d) begin case (sel) 2’b00: a = b; 2’b01: a = c; 2’b10: a = d; end

敏感表要完整 不完整的的敏感表将引起综合后网表的仿真结果与以前的不一致。 always @(d or clr) if (clr) q = 1’b0 else if (e) q = d; always @(d or clr or e) if (clr) q = 1’b0 else if (e) q = d;

非结构化的for循环 综合工具处理循环的方法是将循环内的结构重复。在循环中包含不变化的表达式会使综合工具花很多时间优化这些冗余逻辑。 for( I =0; i<4; i=i+1) begin sig1 = sig2; -- unchanging statement data_out(I) = data_in(I); end sig1 = sig2; -- unchanging statement for( I =0; i<4; i=i+1) data_out(I) = data_in(I);

资源共享 只有在同一个条件语句(if和case)不同的分支中的算术操作才会共享。 条件操作符 ?: 中的算术操作不共享。 if (cond) 条件操作符 ?: 中的算术操作不共享。 if (cond) z = a + b; else z = c + d; Z = (cond) ? (a + b) : (c + d);

括号的作用 a + + b + c z d a + b + z c + d 利用括号分割逻辑。 z = a + b + c + d;