320 likes | 514 Views
FPGA 和 SOPC 应用 —— 可综合的 VerilogHDL. 华中科技大学启明学院电工电子科技创新中心 王贞炎. 内容. 综合的概念 词法与数据类型 基本结构 运算符 控制语句 函数与任务 模块与参数. 综合的概念. 综合就是将 VerilogHDL 设计(一般为系统级和算法级的行为描述)转变为能用硬件电路实现的基本逻辑单元的连接( RTL 级或门级网表)的过程。 可综合 VerilogHDL 是 VerilogHDL 的一个有 限子集,对于不同的综合工具可综合子集一般并不完全相同。 FPGA 工程的整个编译过程分为: Analysis
E N D
FPGA和SOPC应用——可综合的VerilogHDL 华中科技大学启明学院电工电子科技创新中心 王贞炎
内容 • 综合的概念 • 词法与数据类型 • 基本结构 • 运算符 • 控制语句 • 函数与任务 • 模块与参数
综合的概念 • 综合就是将VerilogHDL设计(一般为系统级和算法级的行为描述)转变为能用硬件电路实现的基本逻辑单元的连接(RTL级或门级网表)的过程。 • 可综合VerilogHDL是VerilogHDL的一个有限子集,对于不同的综合工具可综合子集一般并不完全相同。 • FPGA工程的整个编译过程分为: • Analysis • Synthesis • Fit • Assembly
词法与数据类型 • 标识符 • 任意一组字母、数字、$符号和_(下划线)符号的组合,第一个字符必须是字母或者下划线。标识符区分大小写! • 逻辑值体系 • 0: 逻辑0 • 1: 逻辑1 • z / Z: 高阻抗 或 无关值(casex与casez语句) • x / X: 无关值 或 不定值
词法与数据类型 • 变量的数据类型 • 线网数据类型:wire, wor, wand, tri, supply0, supply1 • wire 一般连线,最常用 • wor和wand实现多驱动源驱动同一根网线 • tri与wire综合效果相同 • supply0固定连接0电平,supply1固定连接1电平 • 寄存器数据类型:reg, integer • reg 寄存器 • integer 最大32位整数,寄存器,用二进制补码形式表示
词法与数据类型 • 变量的定义 • 格式: • VarType <signed/unsigned> [msb : lsb] var1<, var2, …>; • 变量数组(存储器): • varType <signed/unsigned> [msb : lsb] varArray[0 : M]; • 示例: • wire [7 : 0] databus; • reg signed [15 : 0] cnt1, cnt2; • reg [31 : 0] mem[0 : 1023];
词法与数据类型 • 可综合的常量:整型常量 • 简单的十进制表示: • 解释为有符号数,综合时用32位二进制补码形式表示 • 30 32位有符号数 • -2 二进制补码表示的32位有符号数 • 基数格式表示:<+/-><size’ <s>base>num • 2’b10: 2位无符号数2 • -8’sh55: 8为有符号数-85 • -6’d4: 6位无符号数60 • -8’h6a: 8位无符号数150
词法与数据类型 • 可综合的常量:整型常量 • 含x和z的常量: • 二进制表示中,一个x或z代表一个二进制位;八进制和十六进制表示中,它们分别代表3或4个二进制位。 • 4’b1x0z • 16’ha5zz • 8’dz • 没有位宽说明时,数值默认为32位 • 赋值时: • 目标字长小于原字长:高位丢失; • 目标字长大于源字长:源值为无符号数时高位填充0;为有符号数高位填充符号;源值最高位为x或z时,高位对应填充x或z。
词法与数据类型 • 参数 • 参数是命名常量 • parameter RED=-1, GREEN=2; • RED和GREEN是两个32位有符号常量 • parameter READY=3’b010, BUSY=3’b001; • READY和BUSY是2个位宽为3的参数。 • localparam S0=1, S1=2, S2=4;
词法与数据类型 • 三种值保持: • 线网 • 触发器(边沿触发的存储元件) • 锁存器(电平敏感的存储元件) • 线网类型的变量被综合成硬件中的连线 • 寄存器类型则根据上下文环境来确定综合成连线或者触发器或者锁存器
基本结构 • 模块 • 模块是Verilog的基本单位,规模可大可小,用于描述一个具体的功能。可被实例化,可嵌套。 • 格式: module 模块名 #(参数定义1<, …>) (端口定义1<, …>); 内部逻辑… endmodule
基本结构 • 过程赋值 • 直接对线网赋值: • wire out = in; • assign语句对已定义线网赋值: • assign out = in; • always块中的赋值: • always…… begin out = in; / out <= in; end
基本结构 • 两种过程赋值语句 • 阻塞式“=”(顺序赋值、结构上顺序、时间上并行) • 非阻塞“<=”(同时赋值、结构上并行、时间上并行) • 赋值的对象会综合成连线、触发器或者存储器,这取决于赋值语句的上下文环境。 • 一般:组合逻辑用阻塞;时序逻辑用非阻塞。 • 赋值限制: • 延迟控制被忽略 • 对同一个赋值对象不能既使用阻塞式赋值,又使用非阻塞式赋值。
基本结构 • 数据流描述和行为级描述 • 数据流描述:赋值、assign语句 • 行为级描述:一般是always语句 • always语句 • 格式 always@(敏感列表 / 时钟) begin … end • 敏感列表<->组合逻辑,时钟<->时序逻辑。 • 由敏感值列表指定的事件或时钟触发其内部逻辑执行。
基本结构 • 几点要求: • always描述的组合逻辑内一律采用阻塞赋值“=” • always描述的时序逻辑内一律采用非阻塞赋值“<=” • always描述的组合逻辑中,如果分支不完备,会出现锁存器,在FPGA的逻辑中尽量不要使用锁存器,更要杜绝无意产生的锁存器 • always描述的组合逻辑中,如果敏感值列表不完备,会出现非预想的结果,可以采用“*”代替敏感值列表,让综合器自动分析敏感值 • always块尽量简短,尽量不要将大量无关的被驱动值放在同一个always块中赋值
运算符 • 逻辑运算符 • 标量运算、一位结果、非“0”即为“1” • && • || • ! • 矢量运算、多位结果 • ~ • | • & • ^ • ^~
运算符 • 数学运算符 • + • - • * • / • % • integer被解释成补码形式的有符号数 • 组合逻辑实现,速度慢(特别是乘、除、余) • 乘、除、余运算消耗逻辑资源多,慎用
运算符 • 关系运算符 • > • >= • == • != • <= • <integer被解释成补码形式的有符号数 • 结果为一位二进制,真为“1”,假为“0”
运算符 • 移位运算符 • << • >> • 移出的空位补“0” • 带符号的移位运算符 • <<< • >>> • 对有符号数: • 左移空出的位补零,符号位不变 • 右移空出的位补符号位 • 对无符号数:移出的空位补“0” • 有无符号取决于变量/常量的定义
运算符 • 位选取运算符 • [] • 常量下标被综合为线网连接 • 变量下标被综合为数据选择器 • 位拼接运算符 • {}
运算符 • 缩减运算符 • & • | • ~& • ~| • ^ • ^~ • 单目运算符,一个操作数,一位结果
运算符 • 条件运算符 • (条件)?(表达式1):(表达式2) • 长条件写法: Grade = (Score >= 90)? ‘A’ : (Score >= 75)? ‘B’ : (Score >= 60)? ‘C’ : ‘D’;
控制语句 • if … else • if语句可以嵌套 • if … else if … else if … else • if语句只能在过程块内使用 • 组合逻辑中,变量应该在所有分支中都被赋值,否则会综合出锁存器 • 时序逻辑中,未在某些分支中被赋值的变量,在出现这些分支情况时,值不变。但建议写全,或在条件外写上默认赋值(会在未赋值的条件中有效),以避免因遗漏而出现非预想的情况
控制语句 • case case(<case表达式>) case分支项A1<, case分支项A2, …>: <语句A> case分支项B1<, case分支项B2, …>: <语句B> … default:<语句X> endcase • casez • 分支项表达式中的z值被视为无关值 • casex • 分支项表达式中的x值和z值被视为无关值 • case语句的分支项有优先级顺序
控制语句 • for • for循环语句是能用于综合的循环语句 • for循环语句的综合是通过展开循环语句来实现的,得到一个并发执行的单元重复的组合逻辑 • 对for语句的循环边界需要加以限制,保证循环边界都是常量,这样才能综合。
函数和任务 • 函数 • 函数调用表达的是组合逻辑,不可包含always块 • 函数调用是通过把被调用的函数直接插入调用代码中展开的方式来实现综合的 • 在函数内局部声明的任何变量都被当作纯粹的临时量来处理,这样的变量会被综合成连线。但函数内不能有wire型态的数据声明 • 函数调用只能限于模块内 • 函数必须包含至少一个以上的input信号,不容许有输出信号。函数至多只有一个返回值 • 函数可以再调用函数
函数和任务 • 函数定义 function <返回值类型或位宽><函数名>; <输入端口声明> <局部变量声明> 行为语句; endfunction • 函数的调用 <函数名> (<输入表达式1>,…,<输入表达式n>);
函数和任务 • 函数举例 module FunctionCall(XBC, DataIn); input [3 : 0] DataIn; output XBC; function [1 : 0] CountOnes; input [3 : 0] A; integer K; begin CountOnes = 0; for(K = 0; K <= 3; K = K + 1) if(A[K]) CountOnes = CountOnes + 1; end endfunction assign XBC = CountOnes(DataIn) > 2; endmodule
函数和任务 • 任务 • 任务调用既可以表达组合逻辑,也可以表达时序逻辑,这取决于上下文环境。但任务中不能含有always块 • 任务调用是通过把被调用的任务直接插入调用代码中这种展开方式来实现综合的 • 任务内不能有wire型态的数据声明。任务调用只能限于某个模块内 • 任务可以再调用任务或者函数 • 任务可以有零个或者多个输入或者输出 • 任务可以直接访问模块内的reg变量
函数和任务 • 任务的定义 task <任务名>; <端口与类型说明> <变量声明> <语句块> endtask • 任务的调用 <任务名> (<端口1>,…,<端口n>);
函数和任务 • 任务举例 module SynTask(ByteIn, Clock, ByteOut); input [7 : 0] ByteIn; input Clock; output [7 : 0] ByteOut; task ReverseByte; input [7 : 0] A; output [7 : 0] Z; integer K; begin for(K = 7; K >= 0; K = K - 1) Z[K] = A[7 - K]; end endtask always@(negedge Clock) ReverseByte(ByteIn, ByteOut); endmodule
结构化与参数 • 模块实例化格式: ModuleName <#(.ParamName1(param1)<, ......>)> Instance1(.PortName1(port1)<, ......>)<, ...>; ModuleName <#(param1<, ......>)> Instance1(port1<, ......>)<, ...>; • 模块定义: • module Multiply(OpdA,OpdB,Result); • input [1:0] OpdA,OpdB; • output [3:0] Result; • assign Result=OpdA*OpdB; • endmodule • 模块调用: • module PreDefMulRed • (OpdA,OpdB,ReducedResult); • input [1:0] OpdA,OpdB; • output ReducedResult; • wire [3:0] Result; • Multiply M1(OpdA, OpdB, Result); • assign ReducedResult=&Result; • endmodule