目标FPGA HHH
quartusⅡ https://www.bilibili.com/read/cv6688454
vscode中编写代码:https://zhuanlan.zhihu.com/p/318366616
计算机和通信系统的广泛采用了数字信号处理专用集成电路,比如滤波,变换,加密,编码,压缩等操作。对于那些对时间要求非常苛刻的数字信号处理,就需要设计一个专用的高速硬件逻辑电路。专用的集成电路借助于电子电路设计自动化EDA工具完成。
一个复杂的数字系统设计往往是从算法到硬件链接的门级逻辑结构,在映射到硅片的实现过程。算法的完成可以使用FPGA 现场可编程门阵列,专用大规模集成电路ASIC,结合了IP的ASIC运算电路。这时电路的结构就很重要,需要多次仿真。性能方面ASIC芯片系统利用线程的微处理器IP核或特殊应用的微处理器核,结合专门设计的高速ASIC电路,性价比最高的。但是难,贵。
基础的算法描述核验证可以使用C语言实现,算法行为仿真Matlab更适合,硬件描述语言HDL针对硬件描述。HDL好处就是易于理解,维护,易于调试电路,有很多仿真,综合,布局布线工具。计算电路的结构和芯片工艺对运行速度有很大影响。结构完全确定之前需要多次仿真。
C语言和Verilog HDL可以配合使用,C语言有完备的编译环境,缺陷少,Verilog可靠性差,报错功能差。所以利用C语言设计符合功能的硬件电路系统,利用完善的差错编译环境,再改写成并行结构,翻译成Verilog语言。C程序是一行一行执行,顺序结构,Verilog描述的硬件是并行结构。对模块的调用是不同的,即使调用同一个模块,还需要指定不同的名字,能用的判断语句也有限,错误信息不完整等。C语言没有时间关系,转换后的Verilog程序必须做到没有任何外加的人工延时信号,也就是RTL级的Verilog,否则就没法翻译成门级逻辑。
RTL级,register transfer level,指的是用寄存器这一级别的描述方式来描述电路的数据流方式;
Behavior级指的是仅仅描述电路的功能而可以采用任何verilog语法的描述方式。鉴于这个区别,
RTL级描述的目标就是可综合,而行为级描述的目标就是实现特定的功能而没有可综合(可实现)的限制。行为级是RTL的上一层,行为级是最符合人类逻辑思维方式的描述角度,一般基于算法,用C/C++来描述。从行为级到RTL级的转换,一般都是由IC设计人员手工翻译。
Verilog是一种工业标准,将数字逻辑设计工作分为逻辑设计(前端),电路实现(后端),验证三个相互独立又相关的部分。各种常用的数字逻辑电路和组件(FFT算法,DCT算法部件,DDRAM读写控制器等)建成的宏单元,也叫软/硬核,即IP(知识产权内核)可以直接引用。电路的实现可以用综合工具,IP的重复利用,布局布线工具自动完成。
HDL一种用形式化方法描述数字电路和系统的语言。可以从抽象到具体实现设计,然后用电子设计自动化EDA工具逐层仿真验证,转换成门级电路网表,用FPGA 或 ASIC(专用集成电路)实现物理电路。之前可以使用Verilog的门级模型代替具体元件,逻辑功能和延时特殊完全一致,来验证复杂数字系统结构的正确性。
Verilog HDL就是一种,可以进行数字逻辑系统的仿真验证,时序分析,逻辑综合。容易移植到不同芯片,使用不同规模的引用,与工艺无关,不用太过考虑门级电路和工艺的实现细节。这就是利用了EDA工具下,将逻辑验证与具体工艺库匹配,布局及延时计算分成不同阶段。
软核具有最大的灵活性,可以借助EDA综合工具与其他外部逻辑结合为一体,但与固核,硬核一样还需要重用计算进行改动,适应工艺。
自顶向下 Top_Down 设计
将一个完整的设计任务划分为若干模块,编制出相应模型(行为级或结构级),通过仿真验证分别设计模块。还可以购买一些IP完成一些模块。就是从系统级,划分为基本单元,再一层层划分。
层次管理,将设计方案逐次分解为多个层次,每一层次硬件可以分为一些模块,这些模块互联描述这层次的硬件结构。这层的基本单元又有下一层次的基本单元互联成,这样就构成了树结构,节点就是该层次上基本对应的行为描述,树枝对应基本对应的结构分解。不同层次都可进行仿真对设计思想进行验证。EDA工具就可以方便管理层次,可以查看某一层某模块的源代码或电路图来改正仿真的错误。
模块设计流程主要两部分:1 设计开发:编写设计文件-综合到布局布线-电路生成 2. 设计验证:各种仿真。
Verilog HDL即是一种行为描述语言,也是一种结构描述语言。描述电路功能行为的模块,或描述元器件或较大部件互联的模块都可以用Verilog建立电路模型,最后都可以通过工具自动转换为门级互联的结构模块。Verilog模型可以是实际电路的不同级别的抽象:
一个复杂电路系统的完整Verilog模型由多个Verilog模块构成,每个模块又有多个子模块。有些模块需要具体电路,有些只是与用户所设计的模块有交互联系的现存电路或激励信号源。这些构成了模块间的层次结构。Verilog的行为描述语言有一下功能:
// muxtwo.v
module muxtwo ( // 二选一 选择器,out与a一致还是与b看sl的电平,sl低电平out与输入a相同。
out,a,b,sl
);
input a,b,sl;
output out;
reg out;
always @(sl or a or b) begin // sl or a or b 有一个变换执行以下语句
if (!sl) begin
out = a;
end
else out = b;
end
endmodule // 不用关心电路结构,只要关心逻辑功能的描述,这就是逻辑行为描述
module muxtwo (out,
a,
b,
sl);
input a,b,sl; // 输入信号名
output out; // 输出信号名
wire nsl,sela,selb; // 定义内部连接线
assign nsl = ~sl; // 反
assign sela = a&nsl; // 按位与
assign selb = b&sl;
assign out = sela|selb; // 按位或
endmodule
module muxtwo(out,
a,
b,
sl);
input a,b,sl;
output out; // 输出out 与a 一致还是b 由sl电平决定,功能与上面的一样
not u1(nsl,sl);
and #1 u2(sela,a,nsl); // and or not Verilog的门,第一个端口(参数)输出,
and #1 u3(selb,b,sl); // #1 #2表示门输入到输出的延迟为1,2单位时间,u1,u2,u3,u4对应不同的逻辑元件,是实例名称
or #1 u4(out,sela,selb); // 模块程序,基于逻辑单元互联结果的描述
endmodule
编写模块时,符合语法,符合一些基本规则,就可以将第一个程序(逻辑行为if-else),通过第二个程序(中间形式,逻辑功能或逻辑表达式and not or)自动转换成第三种的门级结构,这个过程就是综合(synthesis) ,第三种模式很容易与某种工艺的基本元件对应起来,再通过布局布线工具转为电路结构。综合就是通过工具将行为级描述的模块通过逻辑网表自动转换为门级形式的模块。最后产生由与门,或门,非门构成的组合逻辑
综合(synthesize),就是在标准单元库和特定的设计约束的基础上,将设计的高层次描述(Verilog 建模)转换为门级网表的过程。逻辑综合的目的是产生物理电路门级结构,并在逻辑、时序上进行一定程度的优化,寻求逻辑、面积、功耗的平衡,增强电路的可测试性。
但不是所有的 Verilog 语句都是可以综合成逻辑单元的,例如时延语句。
module adder( // 三位加法器 ,整个程序在 module endmodule 之间
cout,sum,a,b,cin
);
input[2:0] a,b; // 连续赋值语句,根据两个 3bits a,b和进位cin 计算和 sum 和向上进位 cout
input cin;
output cout;
output[2:0] sum;
assign {cout,sum} = a+b+cin;
endmodule
module compoare( // 比较器
equal,a,b
);
output equal;
input[1:0] a,b; // 2 bits 的a,b
assign equal=(a=b) ? 1:0; // a,b相等输出1,否则0
endmodule
module trist2 ( // 三态驱动器,使用verilog提供的原语库中现成的三态驱动器元件的bufif1实现逻辑功能
out,in,enable
);
output out;
input in,enable;
bufif1 mybuf(out,in,enable); // 元件名字叫mybuf,引用现成元件就是实例化,括号里的是与元件相连的线
endmodule
另一种三态门。有两个模块,trist1 引用模块mytri定义的实例tri_inst,trist1是上层模块,mytri就是子模块
module trist1 (
sout,sin,ena
);
output sout;
input sin,ena;
mytri tri_inst(.out(sout),.in(sin),.enable(ena)); // 带 . 表示被引用模块的端口,名字与被引用模块的端口定义一致
// 引用mytri 模块定义的元件 trii-nst ,小括号中表示本模块中与之相连的线路。
endmodule
module mytri (
out,in,enable
);
output out;
input in,enable;
assign out = enable ?in :'bz;
endmodule
以上的例子都是可以综合的,通过综合工具可以转换位与门,或门,非门组成的各种组合逻辑。即使逻辑图比较复杂,对于EDA也就是一个映射过程,不用关系逻辑构成的细节。
在一个模块中引用另一个模块,对其端口进行相关连接,叫做模块例化。模块例化建立了描述的层次。信号端口可以通过位置或名称关联,端口连接也必须遵循一些规则。
带 . 表示被引用模块的端口,端口顺序任意,保证端口名与外部信号匹配就行,另外如果某些输出口不需要与外部连接可以悬空或删除。input例化时一般不能删。
ouput端口悬空,等于删除。input悬空,逻辑表示为高阻态z,但一般不能悬空。
//output 端口 Co 悬空
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c==1'b1 ? 1'b0 : 1'b1),
.So (so_bit0),
.Co ());
//output 端口 Co 删除
full_adder1 u_adder0(
.Ai (a[0]),
.Bi (b[0]),
.Ci (c==1'b1 ? 1'b0 : 1'b1),
.So (so_bit0));
建议采用命名端口的方式对模块进行例化
这种方法将需要例化的模块端口按照模块声明时端口的顺序与外部信号进行匹配连接,位置要严格保持一致。例如例化一次 1bit 全加器的代码可以改为:
full_adder1 u_adder1(
a[1], b[1], co_temp[0], so_bit1, co_temp[1]);
例化端口与连续信号位宽不匹配,端口通过无符号数的右对齐或截断方式进行匹配。
描述测试信号的变换和测试过程的模块称为测试平台(testbench or testfixture) ,可以对上面的电路模块(行为级或结构级)进行动态的全面测试,可以调试验证逻辑系统的设计与结构。
// 测试程序 muxtwo.vt
`include “muxtwo.v”
module t;
reg ain, bin, select;
reg clock;
wire outw;
initial //把寄存器变量初始化为一确定值
begin
ain = 0;
bin = 0;
select =0;
clock = 0;
end
always #50 clock = ~clock; //产生一个不断重复的周期为100的时钟信号clock
always @(posedge clock)
begin // {$random} 为系统任务,它会产生一个随机数
ain = {$random}%2; // 产生随机的位信号流ain ,%2为做模2运算
#3 bin = {$random}%2; // 延迟3个时间单位后产生随机的位信号流bin
end
always #10000 select = !select;
//产生周期为10000个单位时间的选通信号变化
muxtwo m (.out (outw), .a(ain), .b(bin), .sl(select));
/* 实例引用多路器,并加入测试信号流,以观察模块的输出 out。其中
muxtwo是已经定义的(行为的或结构的)模块,m表示在本测试模块
中有一个名为m的muxtwo的模块,其四个端 口分别为:
.out( ) , .a( ), .b( ), .sl( ),
“.”表示端口,后面紧跟端口名,其名称必须与muxtwo模块定义的
端口名一致,小括号内的信号名为与该端口连接的信号线名,可以用
别的名,但必须在本模块中定义,说明其类型。 */
endmodule
测试的muxtwo 可以是行为模块,也可以使用布尔逻辑表达式,或门级结构模块,模块t 可以对测试模块进行测试,可以在功能级上进行,也可以在逻辑网表(表达式),门级结构上进行。分别称为 RTL仿真,逻辑网表仿真,门级仿真。门级结构模块与具体工艺对应,加上布局布线引入的延迟模型,这时进行的是布线后的仿真。
Verilog模块可以分为两种类型:1. 让模块最终生成电路的结构,2. 只是为了测试逻辑功能。
书写格式跟c差不多,一行可以些多个语句,一个语句可以写多行。语句,定义后接 ; 分号。注释一样。
将概念与硬件结构和测试联系,理解物理意义,与C区分。
Verilog的基本设计单元是模块,一个模块由两部分组成,一部分描述接口,一部分描述逻辑功能。Verilog程序包含四个部分:端口定义,IO说明,内部信号说明,功能定义。
module block(a,b,c,d); // 端口定义 module 模块名(口1, 口2,... ...)
/*
端口就是模块的输入,输出口名,与别的模块联系端口的标识。引用模块时有两种连接方法:
1. 严格按照模块定义的端口顺序写,不用标明原模块定义时规定的端口名
2. 引用时加 . 符号,标明原模块时定义时规定的端口名,端口名可以与被引用模块的端口相对应,不用严格按照端口顺序。
例: MyDesignMK M1(.sin(SerialIn), .pout(ParallelOut), ......);
.sin .pout 是M1的端口名,M1是与MyDesignMK一样的模块,后者已经在另一个模块中定义,有两个端口 sin, pout,与sin口连接的信号名为 SerialIn,与pout端口连接的信号名为 ParallelOut。
*/
input a,b;
output c,d; // 信号流向
assign c = a|b; // 逻辑功能
assign d = a&b;
endmodule
包括IO说明,内部信号声明,功能定义
1. I/O说明:
输入口: input [信号位宽-1:0] 端口名1; // 三位就是 a[2:0] 了
input [信号位宽-1:0] 端口名2;
输出口: output [信号位宽-1:0] 端口名1;
output [信号位宽-1:0] 端口名2;
输入/输出口: inout [信号位宽-1:0] 端口名1;
inout [信号位宽-1:0] 端口名2;
或者写在端口声明语句里: module name(input port1,input port2,..., output port1,output port2,...);
2. 内部信号说明:用到和端口有关的 wire reg 类型变量的声明
reg[width-1:0] R变量1,R变量2,...;
wire[width-1:0] W变量1,。。。;
3. 功能定义,最重要的部分就是逻辑功能定义。三种方法产生逻辑:
assign 声明语句, assign a = b & c; 后面加个方程式
实例元件, and #2 u1(q,a,b) ; 像在电路图输入方式下调入库原件,输入元件名称,相连的引脚,这里表示一个与门命名u1,输入端a,b 输出端q,输出延迟2个单位时间。每个实例元件名称唯一。
always 块,
always @(posedge clk or posedge clr);
begin
if(clr) q<=0;
else if(en)q <=d;
end
assign 描述组合逻辑最常用, always 块既可以用于描述组合逻辑,也可描述时许逻辑。
实现功能,首先认清那些同时发生,那些顺序发生,上面的 assign ,always ,实例元件三个例子描述的逻辑功能是同时执行,也就是写到一个文件里的话,他们的顺序不影响结果,三项同时执行,并发的。
always 块内,也叫过程块,逻辑是按照顺序执行的。多个always是并行的,模块内部的语句顺序执行。
Verilog模块中所有过程块(always,initial块),连续赋值语句assign,实例引用(实例元件)都是并行的。表示的是一种通过变量名互相连接的关系。同一模块中三者出现的先后顺序没关系,连续赋值语句assign 和实例引用语句可以独立于过程块存在于模块的功能定义部分。
Verilog中有19钟数据类型,表示数字电路硬件钟的数据存储和传送元素。于基本逻辑单元建库有关,于系统设计没有很大关系。
程序运行钟,值不能改变。
1. 数字:
整数,二进制(b,B),八进制(o/O),十六机制(h/H),十进制(d/D) ,表达方式:位宽 进制 数字 完整的,进制 数字 使用默认位宽(操作系统决定32位之类的),数字 默认进制(十进制)。
8’b10101100 位宽8 二进制表示 'b 二进制
8'ha2 位宽8,十六进制表示,一个十六进制数用4位二进制数表示
x 代表不定值 ,可以定义十六进制的4位二进制数状态,八进制的3位,二进制的1位。即在实际电路里,信号可能为 1,也可能为 0。
z 高阻值,表示方式同上,还可以用 ? 表示,在case 表达式好用。
4'b10x0 位宽4,低位第二位不定值
4’b101z 位宽4,低位第一位高阻值
12'dx 位宽12十进制,高阻值,12位二进制
8'h4x 位宽8十六进制,低四位值为不定值
负数,位宽前加个减号,要写作最前面
-8'd5 5的补数,八位二进制数表示
下划线,隔开数的表达提高可读性,只能用在具体数字之间,不能再位宽和进制处
16'b1010_1011_1111_1010
10 = 32'd10 = 32'b1010
1 = 32'd1 = 32'b1
-1 = -21'd1 = 32'hFFFFFFFF
'BX = 32'BX = 32'BXXXX..XXX
"AB" = 16'B01000001_01000010 字符串AB,十六进制 16'h4142
2. 参数型
用 parameter 定义标识符代表常量,即符号常量。paramerter 是参数型数据的确认符,后面跟赋值语句。等号右边必须是一个常数表达式。只能包含数字和以及定义过的参数。
常用于定义延迟时间,变量宽度
parameter msb=7, byte_size = 8, byte_msb = byte_size = -1, a = (r+f)/2; // 用之前定义过的参数表达式赋值
module Decode (
A,F
);
parameter width = 1,polarity=1; // 模块Decode定义两个参数型变量,Top模块钟引用实例,
...
endmodule
module Top; // 可通过参数的传递改变定义时的参数值
wire[3:0] A4;
wire[4:0] A5;
wire [15:0] F16;
wire[31:0] F32;
Decode #(4,0) D1(A4,F16); // #(4,0) 令实例D1引用的 width=4,polarity=0 的Decode模块
Decode #(5) D2(A5,F32); // #(5) D2引用的是 width= 5,polarity=1的Decode模块
endmodule // 利用参数编写模块的方法令已编写的底层模块有了更大灵活性。常用于表示门级电路的延迟。
'include "Top.v"
'inlucde "Block.v"
'include "Annotate.v"
module Test;
wire W;
Top T();
endmodule
module Top;
wire W;
Block B1();
Block B2();
endmodule
module Block;
Parameter P = 0;
endmodule
module Annotate;
defparam // 一个模块改变另一个模块参数时,要用这个
Test.T.B1.P=2; // 定义的2,3经过实例T对模块Top的引用,再模块Top中实例B1,B2对模块Block的引用
Test.T.B2.P=3; // 将参数值2,3 传递给模块Block中参数定义的地方,Block中P=0,实例B1,B2中等于2,3
endmodule
// 布线仿真时就是用这种方法吧布线延迟通过布线工具生成的延迟参数文件反标注到门级Verilog网表上。
程序运行中值可以改变的量。最常用的就是线网(网络类型) wire 和寄存器reg 其余类型可以看作这两种的扩展或辅助。
网络数据类型表示结构实体(例如门)之间的物理连接(导线吗),网络类型的变量不能存值,必须受到驱动器(例如门或连续赋值语句assign)的驱动,没有驱动器连接网络类型变量,该变量就是高阻值,值为z,常用的网络数据类型 wire 型,tri 型。用于连接器件单元,具有相同的语法格式和功能。wire型通常表示单个门驱动或连续赋值语句驱动的网络型数据,tri型变量表示多驱动器驱动的网络型数据,
1. wire 型
表示用以assign关键字指定的组合逻辑信号。输入输出信号类型默认自动定义为wire型,可以用作任何方程式的输入,也可以做assign语句或实例元件的输出。
wire a; // 一位的wire型数据
wire[4:1] c,d // 两个4位的wire型数据 [ n-1,0] = [n,1] ,两条总线,每条总线4条线路。。
2. reg型,寄存器数据类型,数据存储单元,通过赋值语句可以改变寄存器存储的值,默认初始值为不定值x,
配合控制结构描述硬件触发条件。常用于表示always模块内的指定信号,常代表触发器。
设计中always模块通过使用行为描述语句表达逻辑关系,内部被赋值的每一个信号都要定义成reg
reg [4:1] regc,regc // 两个4位reg型数据,
赋值语句就是改变一组触发器的存储单元的值,通过控制结构控制何时执行这些赋值语句。这些控制构造可以用来描述硬件触发器的各种具体情况。比如触发条件用时钟的上升沿,多路选择器。
3. memory型(数组),Verilog通过reg型变量建立数组对存储器建模,描述RAM存储器,ROM存储器reg文件,数组中每个单元通过索引寻址。
Verilog中使用memory型数据扩展reg型数据的地址范围生成。
reg[n-1:0] 存储器名[m-1:0]
reg[7:0] mema[255:0] 256个8位存储器,地址范围0-255
reg mema[n-1:0],rega; n个1位存储器,可以和寄存器同时定义
rega = 0; 寄存器可以再赋值语句赋值,mema=0 存储器不行的
mema[3] = 0 ; 读写操作通过索引指定单元再存储器中的地址。寻址方式可以是表达式。
reg [31:0] data_4d[11:0][3:0][3:0][255:0] ; 声明4维的32bit数据变量数组
assign data_bit[0][1] = 1'b1; 将数组data_bit的第1行第2列的元素赋值为1,
这里不能省略第二个访问标号,即 assign data_bit[0] = 1'b1; 是非法的。
4. 整数 integer
integer j; 声明时不用指明位宽,位宽和编辑器有关。也属于reg型,reg型变量是无符号,integer型是有符号数。
reg [31:0] data1 ;
reg [3:0] byte1 [7:0]; //数组变量
integer j ; //整型变量,用来辅助生成数字电路,再实际电路中没有j这个信号,只是辅助生成硬件电路。
always@* begin
for (j=0; j<=3;j=j+1) begin
byte1[j] = data1[(j+1)*8-1 : j*8];
//把data1[7:0]…data1[31:24]依次赋值给byte1[0][7:0]…byte[3][7:0]
end
end
5. 实数real ,默认0声明不带范围,一个实数赋值给整数,只有整数部分会赋值过去,可以用十进制或科学计数法表示
6. time 时间,对仿真时间进行保存,位宽一般64位,可以通过系统函数$time 获取当前仿真时间`current_time = $time`
7. 字符串,保存再reg类型的变量中,每个字符占用一字节(8位),不能多行书写,寄存器的宽度要足够大,
宽度小于字符串宽度去除字符串左边多余数据,少于用0填充左边空位。特殊字符\n \t可以显示
reg [0: 14*8-1] str ;
initial begin
str = "run.runoob.com"; // 十四个字符
end
算术+ , - ,× ,/ ,%求余
11%-3 = 2 结果取第一个操作数的符号位
-10 %3 = -1 一个操作数为不定值x,结果就是不定值
赋值= <=
关系 > < >= <=
逻辑 &&, ||, !
条件 ?: r = s?t:u 三目运算符
位运算 ~, | ,^异或, & ,^~同或
rega = 'b1010; rega = ~rega // ‘b0101
位数不同的位运算,右端对其,位数少的高位补零。
位移 >> <<
拼接 {}