FPGA学习笔记
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1、了解电子学的发展史,具体要具备相关的专业知识背景。
2、注意新建工程的时候先建好自己的文件夹,不然在一个大的文件夹下面不方便进行管理,比较的杂乱!
3、注意掌握VERILOG HDL语言的基本语法,一定要学的扎实!在实践中学习!
4、在Verilog 语言中定义的input 、output在默认的情况下面是wire型的数据类型,但是在时序逻辑模块的内部不允许出现等号的左边是wire型的变量,要进行变量类型的重新申明(比如reg)。
5、在if else和case语句中的区别在于,if else 是顺序判断的,有优先级(设计中应该尽量避免多个if嵌套,因为用硬件实现优先级是比较耗费资源的);但是在case语句中是全部并行的进行逻辑判断(没有优先级)!
补充一点的是两种其他的case声明
@1、casez 认为case条件下的所有z不重要,不是逻辑值;
@2、casex 认为case条件下的所有x和z都不重要,不是逻辑值。
6、注意阻塞赋值和非阻塞赋值语句的区别和联系:
阻塞赋值(=):按顺序模块中指定的次序进行
非阻塞赋值(<=):(并行)不对顺序模块后的声明进行阻塞赋值,支持对赋值的调度。
例子:initial
begin
#5 a=b;
#10 c=d;
end
这里的在5时刻b赋值给a,第15时刻将d赋值给c;
initial
begin
#5 a=b;
#10 c=d;
end
这里的在第5时刻将b赋值给a,第10时刻将d赋值给c;
不阻塞(non-blocking) 赋值语句 ( b<= a):
- 块内的赋值语句同时赋值;
- b 的值被赋成新值 a 的操作, 是与块内其他
赋值语句同时完成的;
- 建议在可综合风格的模块中使用不阻塞赋值。
阻塞(blocking) 赋值语句 ( b = a):
- 完成该赋值语句后才能做下一句的操作;
- b 的值立刻被赋成新值 a;
- 硬件没有对应的电路,因而综合结果未知。
7、使用进程模块的电路类型:
组合电路:即对组合逻辑中使用的所有输入敏感;
例子:always @(a or b or c)
时钟电路:对时钟及控制信号敏感。
例子:always @(posedge clk or negedge clr)
8、函数和任务:
函数和任务都是子程序,用于可以重复的代码,增加模块的可读性。
函数:根据输入返回一个值,产生组合逻辑,可以使能其他函数,但不是其他任务,不能包括任何的时序控制声明,至少有一个输入变量,总是返回一个数值,不能输出或者inout变量;
任务:可以是组合或者寄存,可以使能其他任务或者函数,可以含有延时或者时序的声明,可以有零个或者更多的输入输出或者inout变量,返回零个或者更多的数值。
9、模块端口是指模块与外界交互的接口,包括3种类型:
input 输入端口
output 输出端口
inout 输入
输出端口
上面的三种端口中,input只能是wire类型,output可以是wire型也可以是reg类型,inout只能为wire类型。
但是在默认情况下,定义了端口之后系统默认是wire类型的,如果要改变数据类型,需要另行申明。比如:
定义了 output a;//此时系统默认是wire类型的
reg a; //此时就是将这个a端口变为了reg类型
10、注意线网类型的变量只能用assign语句赋值,不能在always里面和initial里面赋值!连续赋值语句只能驱动线网类型变量!线网类型的变量的赋值(也就是驱动)只能通过数据流assgin操作来完成。
11、层次化设计的核心思想有两个:一个是模块化,还有就是木块的例化。注意模块例化的意思就是模块的调用。
12、习惯将一个module放在一个.V文件下面,方便检查。
13、模块例化的方法有3种:
即位置映射法,信号名映射法以及二者的混合映射法。
例子说明:
module demo1(result,a,b);//子模块
input [7:0] a,b;
output result;
assign result = (a == b) ? 1 : 0 ;
endmodule
module demo(result0,a0,b0,
result1,a1,b1);//主模块(顶层模块)
input [7:0] a0,a1,b0,b1;
output result0,result1;
//第一次调用子模块,利用位置映射法
demo1 demo_2(result0,a0,b0);
//第二次调用子模块,利用信号映射法
demo1 demo_3(
.result(result1),//注意这里的变量次序可以任意改变
.a(a1),
.b(b1)
);
采用信号映射法时,前面的信号是指被调用模块的(子模块),括号里面对应的是顶层模块的。
模块例化的特殊情况:
(1)悬空端口处理
DFF d1(//空白处理
.q(q1),
.data(),//悬空端口
.clk(clk1)
);
DFF d1(//不调用处理
.q(q1),
//.data(),//悬空端口
.clk(clk1)
);
(2)不同端口位宽处理,端口通过无符号数的右对齐截断方式匹配!
调用模块是的参数映射:# defparam..........查资料
14、注意硬件描述语言与普通的编程语言的差别,他们的关注点是不同的,硬件设计并不是追求代码的精简,而是关注设计的时序和面积的性能等特征,这一点可以从硬件描述语言的循环语句中体现。
15、begin...end可综合
fork...in不可综合,多用于仿真代码
关于循环语句(下面的均是等价的)。下面的都是可以综合的,forever是不可综合的循环语句
repeat(5) | i=0; | for(i=0;i<=4;i=i+1)
begin | while(i<5) | begin
.. | begin | .
. | . | .
end | . | .
| i=i+1'b1; |end
|end |
16、关于位操作和逻辑操作的几点体会:
验证代码如下:
module function_exercise(a,b,c);
input [3:0] a;
//input [2:0] b;
input [3:0] b;
output [3:0] c;
assign c = a & b ;
//assgin c = a && b;
endmodule
比如,a=4'b1101;b=4'b10
10;定义c;
那么c=a&&b;和c=a&b;的值到底是什么样子的呢?
首先来说c=a&b; 这个&操作是位操作,也就是说是一位一位的进行逻辑与,那么c=a&b;这个表达式的值就是将这个变量a,b的每个位进行相与的操作,得出的结果是和这个a,b的位长相等的一个值那么在这个例子总就是c=4'b1000;
其次讨论下这个&&操作,这个经过我的仿真,发现只要a,b两个只要有一个为零,那么结果就是零,其他的如果不是零的两个数就是1,而且结果是一位的数值零或者一。
下面讨论特殊的情况,就是当这两个变量a,b位数不相等的时候;比如此时的
a=4'b1101;b=3'b100;求c=a&&b;或者c=a&b;
进过仿真后得出的结论是:c=a&&b;和上面的结论是一样的,只要a,b有一个为零,那么结果就是零,其他的全部是1 !
c=a&b;结果是c=4'b01001,这个默认的方式是右对齐,同时将左边的补上0,然后每位进行与操作;
这里我是将c定义为与a相同的4位数,但是编译的结果会显示:
Warning: Design contains 1 input pin(s) that do not drive logic
Warning (15610): No output dependent on input pin "a[3]"
下面我将这个c定义为3位数,结果是c=3'b100;编译的结果显示:
Warning (10230): Verilog HDL assignment warning at function_exercise.v(19): truncated value with size 4 to match size of target (3)
Warning: Design contains 1 input pin(s) that do not drive logic
Warning (15610): No output dependent on input pin "a[3]"
但是这个的结果一样的,反正a多出的位数是在少的b的左边补零的(也就是多出的位相与全是零)。
17、在建立仿真波形文件(Vector Waveform File)后,在给仿真信号赋值的时候可以选中其中的某一段区域来进行赋值!
18、仿真参数设置:单击按钮Edit,选择End Time 进行设置;同时,在Edit菜单下面可以选择Grid Size进行仿真每格的时间设置。
19、关于FPGA ROM的设计:/view/f843164cf7ec4afe04a1df8d.html百度文库链接。
方法1:需要先生成你的.hex文件(既是放的数据),然后tools--megawizard plug-in manager按照向导即可生成相应的ROM块。
方法2:直接新建一个.bdf文件,然后在元件库中调用rom模块,具体在magafunction里面,然后根据向导即可生成。
方法3:直接用硬件描述语言进行设计。
19、边沿触发和电平触发不要放在一起
错误报告:mixed single- and double-edge expressions are not supported 源程序:always @(signal or posedge clk or negedge rst_n)20、在组合逻辑设计中:有两种描述方法:一是always模块触发的敏感事件列表;二是用assign关键字描述的数据流赋值语句。
always模块中的信号必须定义为reg类型,不过最终的实现结果中并没有寄存器,这是由于在组合逻辑电路描述中,将信号定
义为reg型只是为了满足语法要求。
21、在组合逻辑设计中,always内的敏感信号列表必须保证完整性,记住EDA工具都会默认将所有的输入信号和条件判断语句作为触发信号,增减敏感信号来得到不同的逻辑,那就错了,或许在仿真的时候可以看到不同的逻辑,但是焼写到芯片内部就不可预知了。
因此为了保证电路稳定,必须保证敏感信号完整性,可以用下面的语句:
always @(* ) begin
.....
end
此时综合工具会自动将所有的敏感信号加入敏感信号列表。
在组合逻辑设计中一定注意避免引入逻辑环路,会有不可预知的结果!
22、在时序电路设计中:always里面的reg型变量全部会被综合成寄存器,这是和组合逻辑电路所不同的。时序逻辑是通过时钟信号的跳变沿来控制的,所以在敏感信号列表中只需要加入所用的时钟信号触发沿即可。
23、同步电路设计的准则:
(1)单时钟策略、单时钟沿策略;
使用混合时钟沿会使静态时序分析复杂,并导致电路工作频率降低。
对于FPGA器件,不推荐同时使用同一信号的两个沿,这是因为FPGA器件内部的时钟处理电路,只能保证时钟的一个沿具有非常好的指标,而另外一个沿的抖动、偏斜以及过度时间等指标都不保证。(两个沿驱动always模块相当于这个时钟信号的两倍频率驱动,如果需要实现2倍,可以先将这个时钟信号倍频,然后单沿检测)
(2)避免使用门控时钟;
如果一个时钟节点由组合逻辑驱动,那么就形成了门控时钟。门控时钟常用来减少功耗,但是门控会污染时钟,带来不可预知的后果。
(3)不要在子模块内部使用计数器分频产生所需时钟;
这样会导致时钟管理混乱,推荐的方式是由一个专门的子模块来管理系统时钟,产生其他模块所需时钟。
24、关于always里面的电路的执行:
假如是时序逻辑,那么肯定有一个时钟的触发沿来控制这个always模块,比如
always @(posedge clk) begin
...
end
在clk一个上升沿驱动这个电路,然而有没有想过在执行这个电路的过程中如果再来一个这个clk的上升沿,那么原先在执行的电路会中断当前的执行重新执行,还是继续等到这个电路执行完?这个就像是中断里面的中断自己中断自己了。。。会发生什么样的后果呢。
你们想的都是错的!根本不会发生上面的情况,也就是说一个时钟的上升沿,在下一个时钟上升沿到来之前,这个always里面的电路会全部执行一遍!等待下一个时钟上升沿的到来!再执行!(这是对面研究生实验室学长的原话)
25、摩尔状态机设计:一般采用三段式设计原则
(1)状态转移部分;
(2)状态转移条件部分;
(3)输出逻
辑部分;
(2)是纯组合逻辑,实现状态的条件判断。
26、下面是对于volatile的解释。
推荐一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。