本人学习FPGA的笔记,稍作整理,将作不定时更新。希望能帮到大家,也希望大家能一起交流学习。
1.Function的使用
function中要有begin-end
Eg:fuction [7:0] test;
Input [7:0] a,b;
Output [7:0] out;
Begin
...
End
2.延迟一个时钟周期
(深入浅出玩转fpga第71页)
Input reg;
Reg regr1,regr2,regr3;
Begin
Regr1 <= reg;
Regr2 <= regr1;
Regr3 <= regr2;
Wire pos_regr1 = regr1 & ~regr2;
//pos_regr2比pos_regr1延迟一个时钟周期,确保
Wire pos_regr2 = regr2 & ~regr3;
//数据被稳定锁存
Pos_regr2比reg上升沿延迟一个时钟周期,pos_regr1跟reg上升沿同个周期触发
3.维持一个时钟周期
(深入浅出玩转fpga第191页)
用寄存器把值存起来,将寄存器的值跟新数据取反再按位与,非零的话说明两次数据不一样,存到一个wire型变量,这个变量将维持一个时钟周期。
Eg:input a;
Reg a_r;
Always @(posedge clk)
begin
A_r <= a;
end
Wire a_ctrl = a_r & ~a;
电平触发的可以通过取反在按位与/按位或编程边沿触发。
4.模块划分的原则
(1)对每个同步时序设计的子模块的输出使用寄存器。
(2)将相关的逻辑或者可以复用的逻辑划分在同一模块内。
(3)将不同优化目标的逻辑分开。
(4)将松约束的逻辑归到同一模块。
(5)将存储逻辑独立划分成模块。
(6)合适的模块规模。
5.异或的应用
Always模块中用reg型变量记录每次采集到的数据,与前一次数据进行异或,能将乒乓开关转换成琴键开关,即电平触发的可以通过取反在按位与/按位或编程边沿触发。
Eg:input a;
Reg a_r;
Always @(posedge clk)
begin
A_r <= a;
end
Wire a_ctrl = a_r ^ a;
Always块中能用寄存器存储前一个时钟的值,与当前的值进行比较处理。
6.时序逻辑要注意的问题
语句是并行执行的,如果接下来的语句要对上一条语句进行判断,是在下一次判断条件来时再判断。如果判断条件是clock,问题不大。如果判断条件是case语句或者if语句,要注意下一次能不能跳到语句中。
7.模块划分原则
一个功能一个模块
8.模块接口
采用类似C语言函数的数据传入,数据输出,以及返回值。(个人看法)
9.输出寄存器化
输出一般定义一个reg型变量,在always中赋值,再用assign将变量输出到端口。
10.存储器类型建模
Eg.定义10个数据位宽为8bit的RAM:
(不是1024个8位的ram)
Reg[7:0] RAM[0:9]
在使用存储单元时,不能直接引用存储器某地址的某比特位值,下面两种描述方法都是错误的。
RAM[32][2]
RAM[32][0:7]
正确的操作方法是将存储单元赋值给某个寄存器,再对寄存器的某个比特位进行操作。
Reg[7:0] rRAM;
rRAM <= RAM[32];
//READ
RAM[32] <= rRAM[7:0];
//WRITE
11.读写FIFO
写FIFO:写信号和数据同个周期发送
读FIFO:送读信号,数据到数据线Q,数据线上的Q一直保存着上一次FIFO输出的数,上位机再读数据线Q上的值。最好是上位机发一个写指令让FIFO把数据读出来,再发一个读指令,把数据从数据线上读走。因为读FIFO有一个周期的延迟,数据线上的数据可能是错的。
12.宏定义的使用
多使用宏定义,方便修改程序。
13.参数定义
注意位数,特别是wire型,要定义,没定义默认的是1bit的。
14.参数初始化
要注意初始化,起始值不对可能会对程序造成影响
15.赋值要注意的问题
两个语句中同时对一个变量进行赋值会出错,特别是没初始化变量的时候。
Eg. 两个if中,一个对变量赋值,一个让变量保持原本的数值,如果两个条件都成立的话,变量将保持原本的数值,另一个if条件中将赋值不成功。
16.综合通过,编译没通过,报管脚不存在的原因
编译时错误如下:
Error: PDC01: port name doesn't exist in the netlist or is not connected to an IoCell macro at PDC Line : set_io TrigInput -direction Input
综合通过,编译没通过。这是因为程序中的引脚没使用,综合工具把引脚综合掉了。这是程序上的错误。解决方法:检查程序的逻辑。
遇到的问题:
SB1_RX经过SB1,传RESULT到CCU,CCU再将结果传到TXRESULT。SB1程序没问题,将RESULT传到CCU后CCU做处理,CCU程序有问题,没有将结果传到TXRESULT,也会将SB1_RX管脚综合掉。
17.驱动使用要注意的问题
驱动中对FPGA下操作指令前下个初始化的指令,防止出错。
18.SRAM使用中遇到的问题
Pdf中:
写使能信号,数据,地址同个周期给,再给读使能和地址,可以读出数据。
编程中可以把写地址先减1,然后地址,数据,写使能同时给。
读的时候不能连续读,两次读之间需要有延时。(自己测试出来的,在用户手册中没找到原因)。
Eg:
写过程:
RAM_WADDR <= 10'd1023;
if (PC_Write && Addr == Addr_RAMScanListInput)
begin
RAM_WEN <= enable;
RAM_WADDR <= RAM_WADDR + 1;
RAM_WD <= LD[8:0];
End
读过程:
RAM_RADDR <= 10'd0;
RAM_RADDR <= RAM_RADDR + 1;
{ENorNot, rScanList[7:0]} <= RAM_RD[8:0];
19.嵌套if要主要的问题
如果嵌套几个if语句,在最外层if的else里面对变量进行赋值,防止不在条件里面时变量的值是错的。
20.Task和function使用方法
task和function可以综合。两个之间的区别:task返回的数值是通过输出端口的变量输出的。函数是通过函数本身的返回值返回的。task相当于C语言中的函数声明为void,参数列表中指针。Function相当于函数声明为int,参数列表中没有指针。(Task和function在一个时钟周期之后就返回,所以所有操作需要在一个时钟周期内完成。有误!)
Eg:
reg[7:0] aa;
reg[7:0] bb;
always @(posedge Lclk or negedge Lreset)
begin
if(!Lreset)
begin
aa <= 8'd1;
bb <= 8'd1;
end
else
begin
task1(1,2,aa);
bb <= fun1(1);
end
end
task task1;
input[7:0] a,b;
output[7:0] c;
c = a + b;
endtask
function[7:0] fun1;
input[7:0] a;
begin
fun1 = 1 + a;
end
Endfunction
21.驱动发数要主要的问题
驱动中将32位的数分2次16位的数发送,需要注意:
(U16)(maximumTime_ms >> 16) 要加括号 不能写成(U16)maximumTime_ms >> 16
22.代码注释
代码注释不要只注释代码本身所实现的功能,要写清楚代码的作用。
23.打两拍原理
深入浅出玩转fpga第43页
异步复位,同步释放,即双缓冲(打两拍)的原理:
以复位信号为例:
1.出现亚稳态的原因:在CLK来时,复位出现在D触发器的建立时间或保持时间内,将导致D触发器处于不稳定状态,可能处于复位状态或者正常的Q = D
2.如果这个复位信号用到工程中的多个D触发器中,以下图的两个为例,将产生2的n次方种情况,例子中为4中。(两个都复位,两个都是Q=D,一个复位,一个不复位),将导致C的值处于亚稳态。
3.下图为打两拍的情况:
rst_n的输入如果只处于一个D触发器的建立时间和保持时间内,只要有一个D触发器正常工作,同步后的复位信号将正常工作。如果两个D触发器都不能正常工作,将使复位信号出现亚稳态。但打两拍后出现亚稳态的几率将大大降低。打多拍效果更好,但是打两拍后出现亚稳态的几率已经很低,没必要打多拍。这也是打多拍后出现亚稳态的几率很低,但还是无法消除亚稳态的原因。
4.打一拍,也能降低亚稳态出现的几率。只有复位在D触发器的建立时间或保持时间内,并且表现的现象为信号没生效(低电平有效,没生效即高电平),复位信号才没生效。亚稳态的概率下降了很多。(个人理解)
5.引申:用寄存器同步输入信号。防止输入信号到达不同D触发器的D端时间不同,有亚稳态的存在。同步后到达各个D触发器的时间相同,降低亚稳态的概率。(个人理解)
6.总结:复位等输入信号要打两拍,或者使用ram、fifo等寄存器来同步数据。(个人总结)
24.握手信号的通信流程
发送端发送data,然后发送请求信号req,接收端收到req后处理data,然后发送应答信号ack,发送端收到ack后撤销req。
25.时序分析笔记
深入浅出玩转FPGA.第138页
分析一个系统最大的时钟频率,即最小的时钟周期要满足什么条件。
Tclk >Tco - (Tc2d - Tc2s) + Tsu
可以理解为Tclk必须大于从clk_2的上升沿到data_2出数据的时间,加上clk_2触发器需要的建立时间。
因为如果时钟周期小于这个值,data_2的值还未被稳定保持下来,下个时钟周期就来了,数据将混乱。
做系统时序优化时,由于其他因素相对固定,所以主要是围绕Tco做文章。
26.FPGA命名规则
.v文件:ModuleName.v 首字母大写
变量名:每个单词的首字母大写
Parameter:每个单词的首字母大写
C命名规则:
1.函数名用大写字母开头的单词组合
2.变量和参数用小写字母开头的单词组合
3.常量全用大写的字母,用下划线分割单词。
4.静态变量加前缀s_(表示static)
5.全局变量加前缀g_(表示global)
27.跨时钟域的信号处理
1.同步设计思想
2.单向控制信号检测(电平信号、脉冲)
3.专用握手信号
4.借用于寄存器(ram、fifo等)
5.同频异相:打两拍,但还是存在亚稳态,所有仅仅适用于对少量错误不敏感的功能模块。可靠的方法是使用DPRAM或者FIFO。
异频:可靠的方法是使用DPRAM或者FIFO。
备注:
如果容许少量错误数据的使用打两拍就行,如果对输入正确率要求很高的,或者数据吞吐量大的,使用DPRAM或FIFO。
28.结构层次化编码
1、结构的层次不要太深,一般为3~5层即可。
2、顶层模块最好仅仅包含对所有模块的组织和调用,而不应该完成比较复杂的逻辑功能。较为合理的顶层模块由输入输出管脚声明、模块的调用和实例化、全局时钟资源、全局置位/复位、三态缓冲和一些简单的组合逻辑等构成。
3、所有的I/O信号,如输入、输出、双向信号等的描述都在顶层模块中完成。
4、子模块之间可以有接口,但最好不要建立子模块间跨层次的接口。
备注:
I/O(包括复位,不包括时钟)放在顶层文件,在顶层文件中对输入信号进行打两拍后再输出给其他模块。在顶层文件中对模块进行调用。
29.’define和parameter的区别
’define宏定义在编译时会自动替换整个设计中所定义的宏,parameter仅仅定义模块内部的参数,定义的参数不会与模块外的其他参数混淆。
30.三段式状态机
第一段:使用非阻塞,cs <= ns;
第二段:使用阻塞,case(cs),判断转移条件,转移条件满足,ns就换到其他状态
第三段:使用非阻塞,case(ns),做相应的操作。
31.Testbench编写
设计与验证verilog HDL第七章
1.例化中input转换为reg,output和inout转换为wire。
2.initial只执行一次,always不断执行。Always通常加条件@(…)。
3.循环语句:repeat、while、for、forever。
Repeat可以加次数,加条件。
eg: repeat (3) @(negedge Clk)
4.#用来延迟。
5.用系统函数和系统任务:$display、$time等
6.从文本文件中读出和写入数据:$readmemh()、$fopen()、$fdisplay()、$fclose()等
7.并行激励:fork…join
32.总线
包括读写信号、片选信号、地址线、数据线
备注:
CCU(centre control unit)对每个子模块用一套总线进行控制。共用一套总线会造成没法对两个模块同时进行控制。(个人理解)
33.case语句
Casez:出现在条件表达式和任意分支项表达式的值为z的位都被认为是无关位,不进行比较。
Casex:出现在条件表达式和任意分支项表达式的值为z和x的位都被认为是无关位,不进行比较。
Case的语句是有优先级的,条件分支项考前的优先级高。
备注:
Case因为分支互斥,所以一般都认为是无优先级的。但是casex和casez中由于不同的分支存在包含关系,如果出现敏感表达式同时满足多个分支的情况,那么将会进入最靠前的分支,忽略后面的分支,所以存在优先级。
34.乒乓操作
在第一个缓冲周期,将输入的数据流缓存到“数据缓冲模块1”,在第二个缓冲周期,通过“输入数据流选择单元”的切换,同时将数据流缓存到“数据缓冲模块2”,与此同时,将“数据缓冲模块1”的数据通过“输出数据流选择单元”的选择,送到“数据流运算处理模块”处理。
备注:
乒乓操作要实现的操作就是数据流没有时间停顿的被送到“数据流运算处理模块”。
35.串并转换
可以用位操作来将串行输入数据转换成并行输出数据。
Pr <= {pr, sr}
其中:sr为串行输入数据
pr为并行输出数据
36.流水线操作
设计可以分为若干步骤进行处理,整个数据处理流程是单向的,前一个步骤的输出是下一个步骤的输入,就可以考虑使用流水线操作。
典型的流水线设计就是将原本一个时钟周期完成的较大的组合逻辑分割后由多个时钟周期完成。
备注:
看过设计例程,人家是将在第一个时钟周期处理后的数据当作输出,送给下一个时钟周期的处理模块当输入,第二个时钟周期处理后再送给下一个时钟周期,一直下去直到完成整个的数据处理。
37.添加引脚后IO分配报错
解决方法:
1.在IO Constraints的文件中自己添加引脚。
2.删除原有的IO Constraints文件,重新添加文件,添加文件后再把原有的引脚分配赋值到新的文件中。
注意:要在下面两个地方删除,没有两个都删除会报错。
38.功能仿真和时序仿真结果不同
程序中有一个三段式状态机,自己编写测试文件。功能仿真出来的结果是正常的,时序仿真出来的结果是复位结束后cstate为未知状态,修改测试文件,将Addr、LD、WR任意一个加初始化后就正常了。
图1:cstate为复位后为不定态
图2:对Addr、LD、WR任意一个加初始化后cstate就正常了
39.DLL编写规则
stdcall调用比extern C调用更基础,更通用,使用标准C写的要更通用,所以就使用标准调用,但用标准调用后编译器会将导出的函数名字改了,所以要使用def对导出名字进行修改,让编译器按照def的名字进行编译。
使用dumpbin -exports *.dll可以查看dll导出函数。
40.RAM控制问题
一个控制器可以控制多个RAM,但是不能多个控制器控制一个RAM。如果一个RAM需要存储多种数据,可以将RAM的地址分成多个区域,不同区域存放不同的数据。
41.ifdef用法
跟C语言的ifdef用法一样,具体使用例子:
`define VGA_1920_1080
`ifdef VGA_640_480
//VGA Timing 640*480 & 25MHz & 60Hz
assign clk = clk_25m;
parameter VGA_HTT = 12'd800-12'd1;
//Hor Total Time
parameter VGA_HST = 12'd96;
//Hor Sync Time
parameter VGA_HBP = 12'd48;//+12'd16;
//Hor Back Porch
parameter VGA_HVT = 12'd640;
//Hor Valid Time
parameter VGA_HFP = 12'd16;
//Hor Front Porch
parameter VGA_VTT = 12'd525-12'd1;
//Ver Total Time
parameter VGA_VST = 12'd2;
//Ver Sync Time
parameter VGA_VBP = 12'd33;//-12'd4;
//Ver Back Porch
parameter VGA_VVT = 12'd480;
//Ver Valid Time
parameter VGA_VFP = 12'd10;
//Ver Front Porch
parameter VGA_CORBER = 12'd80;
//8�ȷ���Color bar��ʾ
`endif
42.顶层文件的模块中多了个p1的端口
文件中端口声明结尾处多了个“,”
43.驱动write函数在CVI中控制不了FPGA
用两个函数,一个名为write,一个为其他的,函数体一样,结果write函数不起作用,推测write为CVI保留函数。
44.布局布线CLK报错
FPGA没法把时钟信号做成全局时钟(单独把时钟放在电路板的一层里面),添加锁相环可以让FPGA优化时钟布线。
45.综合工具对wire型信号的优化
在多个模块中使用wire型进行传数,综合工具和同步时钟能使数据同步上。
例如:
模块1:发送读信号,并且数据线去采集模块2数据线上的数据。
模块2:模块为多路选择的功能。收到读信号和数据线地址线的数据后将数据分配给其他的模块。
assign LD = (PC_Read == Yes) ? CCU0_LD : 16'hz;
模块3:收到读信号后将数据放到数据线上。
assign LD = ( PC_Read && Addr == 8'd255 ) ? data[15 :0] : 16'hz;
读信号从模块1传到模块2,再传到模块3。然后数据从模块3传到模块2,再传到模块1,模块1可以收到数据。
46.时钟信号经过多路选择模块再输出会出错
例子:AD的时钟信号输入到FPGA后经过了多路选择模块,通过模块后在输出给相应模块。板级调试时发现程序乱跑,而且每次烧写程序出现的现象都不同。将AD的时钟直接设置成输入信号,不通过多路选择模块后故障现象消失。
本人学习FPGA的笔记,稍作整理,将作不定时更新。希望能帮到大家,也希望大家能一起交流学习。
1.Function的使用
function中要有begin-end
Eg:fuction [7:0] test;
Input [7:0] a,b;
Output [7:0] out;
Begin
...
End
2.延迟一个时钟周期
(深入浅出玩转fpga第71页)
Input reg;
Reg regr1,regr2,regr3;
Begin
Regr1 <= reg;
Regr2 <= regr1;
Regr3 <= regr2;
Wire pos_regr1 = regr1 & ~regr2;
//pos_regr2比pos_regr1延迟一个时钟周期,确保
Wire pos_regr2 = regr2 & ~regr3;
//数据被稳定锁存
Pos_regr2比reg上升沿延迟一个时钟周期,pos_regr1跟reg上升沿同个周期触发
3.维持一个时钟周期
(深入浅出玩转fpga第191页)
用寄存器把值存起来,将寄存器的值跟新数据取反再按位与,非零的话说明两次数据不一样,存到一个wire型变量,这个变量将维持一个时钟周期。
Eg:input a;
Reg a_r;
Always @(posedge clk)
begin
A_r <= a;
end
Wire a_ctrl = a_r & ~a;
电平触发的可以通过取反在按位与/按位或编程边沿触发。
4.模块划分的原则
(1)对每个同步时序设计的子模块的输出使用寄存器。
(2)将相关的逻辑或者可以复用的逻辑划分在同一模块内。
(3)将不同优化目标的逻辑分开。
(4)将松约束的逻辑归到同一模块。
(5)将存储逻辑独立划分成模块。
(6)合适的模块规模。
5.异或的应用
Always模块中用reg型变量记录每次采集到的数据,与前一次数据进行异或,能将乒乓开关转换成琴键开关,即电平触发的可以通过取反在按位与/按位或编程边沿触发。
Eg:input a;
Reg a_r;
Always @(posedge clk)
begin
A_r <= a;
end
Wire a_ctrl = a_r ^ a;
Always块中能用寄存器存储前一个时钟的值,与当前的值进行比较处理。
6.时序逻辑要注意的问题
语句是并行执行的,如果接下来的语句要对上一条语句进行判断,是在下一次判断条件来时再判断。如果判断条件是clock,问题不大。如果判断条件是case语句或者if语句,要注意下一次能不能跳到语句中。
7.模块划分原则
一个功能一个模块
8.模块接口
采用类似C语言函数的数据传入,数据输出,以及返回值。(个人看法)
9.输出寄存器化
输出一般定义一个reg型变量,在always中赋值,再用assign将变量输出到端口。
10.存储器类型建模
Eg.定义10个数据位宽为8bit的RAM:
(不是1024个8位的ram)
Reg[7:0] RAM[0:9]
在使用存储单元时,不能直接引用存储器某地址的某比特位值,下面两种描述方法都是错误的。
RAM[32][2]
RAM[32][0:7]
正确的操作方法是将存储单元赋值给某个寄存器,再对寄存器的某个比特位进行操作。
Reg[7:0] rRAM;
rRAM <= RAM[32];
//READ
RAM[32] <= rRAM[7:0];
//WRITE
11.读写FIFO
写FIFO:写信号和数据同个周期发送
读FIFO:送读信号,数据到数据线Q,数据线上的Q一直保存着上一次FIFO输出的数,上位机再读数据线Q上的值。最好是上位机发一个写指令让FIFO把数据读出来,再发一个读指令,把数据从数据线上读走。因为读FIFO有一个周期的延迟,数据线上的数据可能是错的。
12.宏定义的使用
多使用宏定义,方便修改程序。
13.参数定义
注意位数,特别是wire型,要定义,没定义默认的是1bit的。
14.参数初始化
要注意初始化,起始值不对可能会对程序造成影响
15.赋值要注意的问题
两个语句中同时对一个变量进行赋值会出错,特别是没初始化变量的时候。
Eg. 两个if中,一个对变量赋值,一个让变量保持原本的数值,如果两个条件都成立的话,变量将保持原本的数值,另一个if条件中将赋值不成功。
16.综合通过,编译没通过,报管脚不存在的原因
编译时错误如下:
Error: PDC01: port name doesn't exist in the netlist or is not connected to an IoCell macro at PDC Line : set_io TrigInput -direction Input
综合通过,编译没通过。这是因为程序中的引脚没使用,综合工具把引脚综合掉了。这是程序上的错误。解决方法:检查程序的逻辑。
遇到的问题:
SB1_RX经过SB1,传RESULT到CCU,CCU再将结果传到TXRESULT。SB1程序没问题,将RESULT传到CCU后CCU做处理,CCU程序有问题,没有将结果传到TXRESULT,也会将SB1_RX管脚综合掉。
17.驱动使用要注意的问题
驱动中对FPGA下操作指令前下个初始化的指令,防止出错。
18.SRAM使用中遇到的问题
Pdf中:
写使能信号,数据,地址同个周期给,再给读使能和地址,可以读出数据。
编程中可以把写地址先减1,然后地址,数据,写使能同时给。
读的时候不能连续读,两次读之间需要有延时。(自己测试出来的,在用户手册中没找到原因)。
Eg:
写过程:
RAM_WADDR <= 10'd1023;
if (PC_Write && Addr == Addr_RAMScanListInput)
begin
RAM_WEN <= enable;
RAM_WADDR <= RAM_WADDR + 1;
RAM_WD <= LD[8:0];
End
读过程:
RAM_RADDR <= 10'd0;
RAM_RADDR <= RAM_RADDR + 1;
{ENorNot, rScanList[7:0]} <= RAM_RD[8:0];
19.嵌套if要主要的问题
如果嵌套几个if语句,在最外层if的else里面对变量进行赋值,防止不在条件里面时变量的值是错的。
20.Task和function使用方法
task和function可以综合。两个之间的区别:task返回的数值是通过输出端口的变量输出的。函数是通过函数本身的返回值返回的。task相当于C语言中的函数声明为void,参数列表中指针。Function相当于函数声明为int,参数列表中没有指针。(Task和function在一个时钟周期之后就返回,所以所有操作需要在一个时钟周期内完成。有误!)
Eg:
reg[7:0] aa;
reg[7:0] bb;
always @(posedge Lclk or negedge Lreset)
begin
if(!Lreset)
begin
aa <= 8'd1;
bb <= 8'd1;
end
else
begin
task1(1,2,aa);
bb <= fun1(1);
end
end
task task1;
input[7:0] a,b;
output[7:0] c;
c = a + b;
endtask
function[7:0] fun1;
input[7:0] a;
begin
fun1 = 1 + a;
end
Endfunction
21.驱动发数要主要的问题
驱动中将32位的数分2次16位的数发送,需要注意:
(U16)(maximumTime_ms >> 16) 要加括号 不能写成(U16)maximumTime_ms >> 16
22.代码注释
代码注释不要只注释代码本身所实现的功能,要写清楚代码的作用。
23.打两拍原理
深入浅出玩转fpga第43页
异步复位,同步释放,即双缓冲(打两拍)的原理:
以复位信号为例:
1.出现亚稳态的原因:在CLK来时,复位出现在D触发器的建立时间或保持时间内,将导致D触发器处于不稳定状态,可能处于复位状态或者正常的Q = D
2.如果这个复位信号用到工程中的多个D触发器中,以下图的两个为例,将产生2的n次方种情况,例子中为4中。(两个都复位,两个都是Q=D,一个复位,一个不复位),将导致C的值处于亚稳态。
3.下图为打两拍的情况:
rst_n的输入如果只处于一个D触发器的建立时间和保持时间内,只要有一个D触发器正常工作,同步后的复位信号将正常工作。如果两个D触发器都不能正常工作,将使复位信号出现亚稳态。但打两拍后出现亚稳态的几率将大大降低。打多拍效果更好,但是打两拍后出现亚稳态的几率已经很低,没必要打多拍。这也是打多拍后出现亚稳态的几率很低,但还是无法消除亚稳态的原因。
4.打一拍,也能降低亚稳态出现的几率。只有复位在D触发器的建立时间或保持时间内,并且表现的现象为信号没生效(低电平有效,没生效即高电平),复位信号才没生效。亚稳态的概率下降了很多。(个人理解)
5.引申:用寄存器同步输入信号。防止输入信号到达不同D触发器的D端时间不同,有亚稳态的存在。同步后到达各个D触发器的时间相同,降低亚稳态的概率。(个人理解)
6.总结:复位等输入信号要打两拍,或者使用ram、fifo等寄存器来同步数据。(个人总结)
24.握手信号的通信流程
发送端发送data,然后发送请求信号req,接收端收到req后处理data,然后发送应答信号ack,发送端收到ack后撤销req。
25.时序分析笔记
深入浅出玩转FPGA.第138页
分析一个系统最大的时钟频率,即最小的时钟周期要满足什么条件。
Tclk >Tco - (Tc2d - Tc2s) + Tsu
可以理解为Tclk必须大于从clk_2的上升沿到data_2出数据的时间,加上clk_2触发器需要的建立时间。
因为如果时钟周期小于这个值,data_2的值还未被稳定保持下来,下个时钟周期就来了,数据将混乱。
做系统时序优化时,由于其他因素相对固定,所以主要是围绕Tco做文章。
26.FPGA命名规则
.v文件:ModuleName.v 首字母大写
变量名:每个单词的首字母大写
Parameter:每个单词的首字母大写
C命名规则:
1.函数名用大写字母开头的单词组合
2.变量和参数用小写字母开头的单词组合
3.常量全用大写的字母,用下划线分割单词。
4.静态变量加前缀s_(表示static)
5.全局变量加前缀g_(表示global)
27.跨时钟域的信号处理
1.同步设计思想
2.单向控制信号检测(电平信号、脉冲)
3.专用握手信号
4.借用于寄存器(ram、fifo等)
5.同频异相:打两拍,但还是存在亚稳态,所有仅仅适用于对少量错误不敏感的功能模块。可靠的方法是使用DPRAM或者FIFO。
异频:可靠的方法是使用DPRAM或者FIFO。
备注:
如果容许少量错误数据的使用打两拍就行,如果对输入正确率要求很高的,或者数据吞吐量大的,使用DPRAM或FIFO。
28.结构层次化编码
1、结构的层次不要太深,一般为3~5层即可。
2、顶层模块最好仅仅包含对所有模块的组织和调用,而不应该完成比较复杂的逻辑功能。较为合理的顶层模块由输入输出管脚声明、模块的调用和实例化、全局时钟资源、全局置位/复位、三态缓冲和一些简单的组合逻辑等构成。
3、所有的I/O信号,如输入、输出、双向信号等的描述都在顶层模块中完成。
4、子模块之间可以有接口,但最好不要建立子模块间跨层次的接口。
备注:
I/O(包括复位,不包括时钟)放在顶层文件,在顶层文件中对输入信号进行打两拍后再输出给其他模块。在顶层文件中对模块进行调用。
29.’define和parameter的区别
’define宏定义在编译时会自动替换整个设计中所定义的宏,parameter仅仅定义模块内部的参数,定义的参数不会与模块外的其他参数混淆。
30.三段式状态机
第一段:使用非阻塞,cs <= ns;
第二段:使用阻塞,case(cs),判断转移条件,转移条件满足,ns就换到其他状态
第三段:使用非阻塞,case(ns),做相应的操作。
31.Testbench编写
设计与验证verilog HDL第七章
1.例化中input转换为reg,output和inout转换为wire。
2.initial只执行一次,always不断执行。Always通常加条件@(…)。
3.循环语句:repeat、while、for、forever。
Repeat可以加次数,加条件。
eg: repeat (3) @(negedge Clk)
4.#用来延迟。
5.用系统函数和系统任务:$display、$time等
6.从文本文件中读出和写入数据:$readmemh()、$fopen()、$fdisplay()、$fclose()等
7.并行激励:fork…join
32.总线
包括读写信号、片选信号、地址线、数据线
备注:
CCU(centre control unit)对每个子模块用一套总线进行控制。共用一套总线会造成没法对两个模块同时进行控制。(个人理解)
33.case语句
Casez:出现在条件表达式和任意分支项表达式的值为z的位都被认为是无关位,不进行比较。
Casex:出现在条件表达式和任意分支项表达式的值为z和x的位都被认为是无关位,不进行比较。
Case的语句是有优先级的,条件分支项考前的优先级高。
备注:
Case因为分支互斥,所以一般都认为是无优先级的。但是casex和casez中由于不同的分支存在包含关系,如果出现敏感表达式同时满足多个分支的情况,那么将会进入最靠前的分支,忽略后面的分支,所以存在优先级。
34.乒乓操作
在第一个缓冲周期,将输入的数据流缓存到“数据缓冲模块1”,在第二个缓冲周期,通过“输入数据流选择单元”的切换,同时将数据流缓存到“数据缓冲模块2”,与此同时,将“数据缓冲模块1”的数据通过“输出数据流选择单元”的选择,送到“数据流运算处理模块”处理。
备注:
乒乓操作要实现的操作就是数据流没有时间停顿的被送到“数据流运算处理模块”。
35.串并转换
可以用位操作来将串行输入数据转换成并行输出数据。
Pr <= {pr, sr}
其中:sr为串行输入数据
pr为并行输出数据
36.流水线操作
设计可以分为若干步骤进行处理,整个数据处理流程是单向的,前一个步骤的输出是下一个步骤的输入,就可以考虑使用流水线操作。
典型的流水线设计就是将原本一个时钟周期完成的较大的组合逻辑分割后由多个时钟周期完成。
备注:
看过设计例程,人家是将在第一个时钟周期处理后的数据当作输出,送给下一个时钟周期的处理模块当输入,第二个时钟周期处理后再送给下一个时钟周期,一直下去直到完成整个的数据处理。
37.添加引脚后IO分配报错
解决方法:
1.在IO Constraints的文件中自己添加引脚。
2.删除原有的IO Constraints文件,重新添加文件,添加文件后再把原有的引脚分配赋值到新的文件中。
注意:要在下面两个地方删除,没有两个都删除会报错。
38.功能仿真和时序仿真结果不同
(没搞懂为什么会这样)
程序中有一个三段式状态机,自己编写测试文件。功能仿真出来的结果是正常的,时序仿真出来的结果是复位结束后cstate为未知状态,修改测试文件,将Addr、LD、WR任意一个加初始化后就正常了。
图1:cstate为复位后为不定态
图2:对Addr、LD、WR任意一个加初始化后cstate就正常了
39.DLL编写规则
stdcall调用比extern C调用更基础,更通用,使用标准C写的要更通用,所以就使用标准调用,但用标准调用后编译器会将导出的函数名字改了,所以要使用def对导出名字进行修改,让编译器按照def的名字进行编译。
使用dumpbin -exports *.dll可以查看dll导出函数。
40.RAM控制问题
一个控制器可以控制多个RAM,但是不能多个控制器控制一个RAM。如果一个RAM需要存储多种数据,可以将RAM的地址分成多个区域,不同区域存放不同的数据。
41.ifdef用法
跟C语言的ifdef用法一样,具体使用例子:
`define VGA_1920_1080
`ifdef VGA_640_480
//VGA Timing 640*480 & 25MHz & 60Hz
assign clk = clk_25m;
parameter VGA_HTT = 12'd800-12'd1;
//Hor Total Time
parameter VGA_HST = 12'd96;
//Hor Sync Time
parameter VGA_HBP = 12'd48;//+12'd16;
//Hor Back Porch
parameter VGA_HVT = 12'd640;
//Hor Valid Time
parameter VGA_HFP = 12'd16;
//Hor Front Porch
parameter VGA_VTT = 12'd525-12'd1;
//Ver Total Time
parameter VGA_VST = 12'd2;
//Ver Sync Time
parameter VGA_VBP = 12'd33;//-12'd4;
//Ver Back Porch
parameter VGA_VVT = 12'd480;
//Ver Valid Time
parameter VGA_VFP = 12'd10;
//Ver Front Porch
parameter VGA_CORBER = 12'd80;
//8�ȷ���Color bar��ʾ
`endif
42.顶层文件的模块中多了个p1的端口
文件中端口声明结尾处多了个“,”
43.驱动write函数在CVI中控制不了FPGA
用两个函数,一个名为write,一个为其他的,函数体一样,结果write函数不起作用,推测write为CVI保留函数。
44.布局布线CLK报错
FPGA没法把时钟信号做成全局时钟(单独把时钟放在电路板的一层里面),添加锁相环可以让FPGA优化时钟布线。
45.综合工具对wire型信号的优化
在多个模块中使用wire型进行传数,综合工具和同步时钟能使数据同步上。
例如:
模块1:发送读信号,并且数据线去采集模块2数据线上的数据。
模块2:模块为多路选择的功能。收到读信号和数据线地址线的数据后将数据分配给其他的模块。
assign LD = (PC_Read == Yes) ? CCU0_LD : 16'hz;
模块3:收到读信号后将数据放到数据线上。
assign LD = ( PC_Read && Addr == 8'd255 ) ? data[15 :0] : 16'hz;
读信号从模块1传到模块2,再传到模块3。然后数据从模块3传到模块2,再传到模块1,模块1可以收到数据。
46.时钟信号经过多路选择模块再输出会出错
例子:AD的时钟信号输入到FPGA后经过了多路选择模块,通过模块后在输出给相应模块。板级调试时发现程序乱跑,而且每次烧写程序出现的现象都不同。将AD的时钟直接设置成输入信号,不通过多路选择模块后故障现象消失。