FPGA学习(基于小梅哥Xilinx FPGA)学习笔记
文章目录
- 一、整个工程的流程
- 二、基于Vivado的FPGA开发流程实践(二选一多路器)
- 什么是二选一多路器
- 用verilog语言,Vivado软件进行该电路实现
- 1、设计输入:Design Sources中的代码
- 2、分析和综合:分析设计输入中是否有错误
- 3、功能仿真,Simulation Sources中的仿真实现代码(Test_Bench)
- 4、布局布线
- 5、时序仿真
- 6、板级调试
- (1)IO引脚分配
- (2)程序下载
- 三、3-8译码器的实现
- 什么是3-8译码器
- 1、设计输入
- 用到的语法
- 整个3-8译码器代码为下
- 2、分析综合
- 3、仿真
- 4、时序仿真
- 5、IO引脚分配
- 6、生成代码,板级调试
- 课后题,写4-16译码器代码
- 1、module设计输入
- 2、功能仿真
- 3、布局布线
- 4、 时序仿真
- 5、IO引脚分配
- 6、生成代码,板级调试
- 四、时序逻辑计数器设计
- 什么是D触发器
- 什么是计数器
- 用Verilog代码实现led闪烁
- 1、设计输入:module编写
- 2、分析综合
- 3、仿真模拟
- 4、 IO设定
- 5、生成程序、板级调试
相关资源网站(小梅哥FPGA)
https://www.corecourse.cn/forum.php?mod=viewthread&tid=27978
https://www.corecourse.cn/forum.php?mod=viewthread&tid=28730
本篇文章使用的开发板为:
小梅哥 Xilinx FPGA
型号:XC7A35T
芯片型号:XC7A35TFGG484-2
以下所有文章内容,均来自小梅哥FPGA教学视频,这个文章只是针对每个教学视频的个人理解总结。
一、整个工程的流程
写一套硬件描述语言,能够在指定的硬件平台上实现相应的功能
- 设计定义(让LED一秒闪烁一次)
- 设计输入(编写逻辑(使用Verilog代码描述逻辑),画逻辑图,使用IP盒(即提供的成品功能器件))
- 综合工具(由专业的EDA软件进行,Quartus、Vivado、ISE),对所写的逻辑内容进行分析,并得到逻辑门级别的电路内容。(如if else就是一个二选一多路器)
- 功能仿真(使用专门的仿真工具进行仿真,验证设计的逻辑功能能够实现。对于数字电路来说 ,仿真时基本接近于真实情况的,是可信的)
- 布局布线(走线带来延迟,延迟越长,主频越低)(在指定器件上将设计的逻辑电路实现)
- 分析性能(1、时序仿真(非常耗费时间),仿真软件进行观察;2、静态时序分析(平时用这个))
- 下载到目标板运行,查看运行结果。(板级调试)(使用ILA(嵌入式逻辑分析仪)(Vivado))
- 让设计的逻辑在目标板上正常工作(功能正常,性能稳定)
总结
- 设计定义
- 设计输入
- 分析综合(EDA、Vivado、Quartus)
- 功能仿真(Modelsim)
- 布局布线(Vivado、Quartus)
- 分析性能(时序仿真Modelsim、静态时序分析Vivado、Quartus)
- 板级调试
- OK
2024.12.22
二、基于Vivado的FPGA开发流程实践(二选一多路器)
打开Vivado并创建工程mux2后,Vivado界面上左边流程
即该软件就是按照开发流程进行运行的
什么是二选一多路器
当SEL=1时,out=a
当SEL=0时,out=b
用verilog语言,Vivado软件进行该电路实现
下图要注意符号的运用
1、设计输入:Design Sources中的代码
用到的语法(Verilog语言)
- 定义模块
module xxx(
......
)
endmodule
//定义一个模块
- 定义输入输出
input a;
output out;
- 赋值语句
assign out = (sel==1)?a:b;
//在sel=1的情况下,将a的值赋予out,否则将b的值赋予out
assign out = ~a;
//将a取反的值赋予out
2、分析和综合:分析设计输入中是否有错误
分析综合运行后,看message,其中没有报错那么这一步就完成了,接下来就进行仿真验证
3、功能仿真,Simulation Sources中的仿真实现代码(Test_Bench)
在仿真平台中,通过代码编写,搭建一个仿真平台,取验证之前编写的module是否OK
我们个人一般用不到ps级别的延时,ns够用
如
#1 //延时1ns
#2 //延时2ns
#1.001 //延时1.001ns,这是在配置为1ns/1ps情况下是可以实现的
写一个可以测试之前定义的module mux2的功能的仿真代码
reg s_a; //定义测试平台的输入信号,可接0和1逻辑输入
.a(s_a); //.代表测试端子,a代表module中a端口,(s_a)代表测试平台的reg端口
//这句话的意思是,将测试平台的(s_a)端子与module的a端子通过.来相接
wire out; //定义平台的输出信号,可根据分析输入信号和module来确认这个输出信号
initial begin
......
end
//测试平台的操作
跑仿真后出现这个界面,这个就是仿真的时序图,
第一次跑不全,再跑一次
此时可以看着时序图对out的信号进行对比,看是否是我们需求的信号输出结果。
仿真这一步挺重要的,建议需要掌握
4、布局布线
点击红框中的符号可以看到目前的编译状态
5、时序仿真
时序仿真电路中可以看到真实的电路中的信号延迟情况,
out都滞后于输入信号的变化,根据时序仿真,可以看出延迟情况,来判断 这个延迟我们能否接受
6、板级调试
(1)IO引脚分配
根据上图进行引脚分配
分配完成后,在引脚分配界面进行ctrl+s进行保存,保存名称和项目名称可以一样(不强求,也可以不一样)
(2)程序下载
点击后就可以进行创建,
创建完成后可以进行程序下载
这样就下载完成了,之后就可以进行板级调试。
2021.12.22
三、3-8译码器的实现
什么是3-8译码器
1、设计输入
3-8译码器
创建module,创建3-8译码器的端口
端口创建完成后,定义端口的输入输出状态
输入输出状态定义完成后,进行端口之间的逻辑编写
用到的语法
-
always块
-
位拼接
d是一个4位数据,通过assign将 a, 1bit常量值为0,b , c这四个数拼在一块赋值给d -
描述多位格式的常用限定符号
这两个表达方式是一样的
b 二进制 3‘b101 8'b0000_1010
o 八进制
d 十进制 3'd5 8'd10
h 十六进制 8'ha
- case() endcase
case括号中,为满足操作的条件
在满足条件下进行操作,如:\
3‘b000 : out = 8'b0000_0001;
具体解释为拼接后的{a,b,c}在满足3位bit值是000的情况下,将8bit的0000_0001赋值给out
整个3-8译码器代码为下
2、分析综合
分析综合的作用,检查设计输入的语法有无问题
分析之后查看reports,有错误则报红
3、仿真
创建一个测试平台,命名规则一般为(文件名_tb),tb代表test bench
测试平台的代码具体的流程为
- 定义测试平台的时间尺度
- 定义一个测试平台
- 定义测试平台的端口,标注好reg型(输入)和wire型(输出)
- 将模块放置到测试平台上
- 将模块中的端口与测试平台的端口互相连接
- 定义测试平台的测试动作(给各信号端口激励信号并延时)
- end
所有代码完成后,进行仿真
仿真第一次跑会根据仿真设置中的默认跑1000ns来跑,所以第一次的仿真都是1000ns的时间段中的动作
这里我们可以不用管,直接再次跑,在动作结尾加上 $stop; 即可跑到这里自动停止
4、时序仿真
跳过,这个项目中只进行了模拟仿真,并没有进行时仿真
原因是项目比较简单,无进行必要。但也可以进行。这个步骤有无都无所谓
5、IO引脚分配
打开综合类工程
必须打开后才能看到IO planning这个选项
IO设置中首先将I/O Std电压设定为3.3(LVCMOS33),只针对于目前手中的这个开发板进行的设定
其次根据引脚对应表,将拨码开关引脚进行设定
LED灯对应的引脚(高电平点亮)
根据表进行设定
设定完成后进行ctrl+s保存
6、生成代码,板级调试
因为之前没有进行布局布线,即
在生成BitStream时候会进行检查,没布局布线的话这一步会自动进行,所以这一步在之前省略了
点击之后,等待完成。
下一步连接硬件板卡进行程序下载
之后进行板级调试就OK
三个拨码开关,设定为a,b,c
8个LED灯代表out 0-7
拨码开关不同状态代表不同输出,led显示表示输出
课后题,写4-16译码器代码
1、module设计输入
代码完成后进行分析综合,即检查下代码有无错误
2、功能仿真
代码完成后进行分析综合,即检查下代码有无错误
3、布局布线
略
4、 时序仿真
略
5、IO引脚分配
略
6、生成代码,板级调试
略
2024.12.23
四、时序逻辑计数器设计
什么是D触发器
CK上升沿的时候,将D端口的信号输出给Q端口
即D触发器有存储特性,只在CK端口上升沿变化,其余时间一概不管,即Q端口可以存储D的电平状态
什么是计数器
counter = counter+1
目标,设计1s频率闪烁的LED灯(亮灭各500ms)
目前板子的时钟是50MHz,那么执行每步操作时候为20ns
则500ms需要计数500ms/20ns=25000000
用Verilog代码实现led闪烁
1、设计输入:module编写
- 确认module端口,需要有时钟信号Clk,复位信号Reset_n(低电平复位),Led控制信号
- 确认信号的类型
时钟信号为输入 input
复位信号为输入 input
led信号为输出 output
counter信号为内部计数,多位D触发器,具体多少位,通过以下计算
即需要25位D触发器,设定为reg类型的counter:reg [24:0]counter;
即最终的端口定义是这样的
通过这个定义可以看出,内部端口也是需要进行定义的,而且内部端口不再试input和output类型的端口了,这里则使用的试reg寄存器类型来表示。
同时对需要进行复杂操作的端口也要进行reg的定义,如之后的代码需要让Led端口自行取反,即Led=!Led; - 端口的逻辑操作,两种写法:1:综合在一块写;2:分开写
综合在一块写(不推荐)
分开写(推荐,构成的电路会更简便)
即最终的module代码:
这个图试后续运行后发现代码无法实现功能反回来查找问题,各种语句使用的都是“<=”,否则会有问题。
<= :非阻塞赋值
= : 阻塞赋值
阻塞赋值 =
阻塞赋值是顺序执行的。在一个begin - end块中,多条阻塞赋值语句按照顺序依次执行,下一条语句会等待当前阻塞赋值语句执行完成后才开始执行。例如:
always @(posedge clk) begina = b;c = a;
end
当clk上升沿到来时,先执行a = b,将b的值赋给a,然后执行c = a,此时a已经更新为b的值,所以c得到的是更新后的a(也就是b的值)。应用场景:
常用于组合逻辑电路建模,特别是在需要按照特定顺序进行计算的组合逻辑中。例如,在实现一个简单的算术逻辑单元(ALU)的组合逻辑部分时,按照运算优先级进行计算就可以使用阻塞赋值。
还用于initial块中初始化变量,因为initial块主要是顺序执行的初始化代码,例如:
initial beginreg_a = 0;reg_b = 1;
end对仿真的影响:
在仿真过程中,由于阻塞赋值是顺序执行的,所以赋值语句的执行顺序会直接影响变量的值和仿真结果。如果赋值顺序不当,可能会导致不符合预期的结果。
例如,如果在组合逻辑中有相互依赖的阻塞赋值,可能会出现竞争冒险(race condition)现象,导致输出出现毛刺(glitch)。
非阻塞赋值 <=
非阻塞赋值是并发执行的。在一个always块中,所有非阻塞赋值语句在同一时刻开始计算表达式的值,但赋值操作会在当前时间步(time step)结束时同时更新目标变量。例如:
always @(posedge clk) begina <= b;c <= a;
end
当clk上升沿到来时,a <= b和c <= a这两个赋值语句会同时开始计算右侧表达式的值。此时计算c <= a时,a的值是更新前的值,等到这个always块执行结束后,a和c才会同时更新。所以c得到的是a的旧值。应用场景:
主要用于对时序逻辑电路进行建模,特别是在多个寄存器之间需要传递数据,并且这些寄存器的更新应该在时钟沿触发后同时进行的情况。例如,在一个简单的移位寄存器设计中:
always @(posedge clk) beginreg1 <= in_data;reg2 <= reg1;reg3 <= reg2;
end对仿真的影响:
非阻塞赋值在仿真时会在每个时间步结束时更新所有被赋值的变量,这样可以更好地模拟实际硬件中的寄存器等时序元件的行为。
有助于避免在时序逻辑建模中因为赋值顺序不当而产生的错误,但如果在组合逻辑中错误地使用非阻塞赋值,也可能会导致意想不到的结果,因为组合逻辑通常期望是立即更新输出的。
使用 = ,直接结果是后续仿真的时候高电平只持续了1个周期,即20ns就又变为低电平
使用<=,则可以实现500.00002ms一反转
为什么是500.000002ms,因为计数是从0开始,计数到25000000时候多计数了一个周期,即20ns,这里需要把计数改为24999999就OK
用到的语法
always@(posedge Clk or negedge Reset_n)//当时钟(Clk)的上升沿或者复位(Reset_n)的下降沿时候,这个代码块工作,其他时候不工作。
counter <= 0;//counter清零,<=是非阻塞赋值的意思
counter=counter + 1'd1;//counter自加1
2、分析综合
3、仿真模拟
最终在module为以下情况下,进行仿真
得出下图:即高低电平切换时间为500ms
4、 IO设定
按键
LED灯
时钟引脚为Y18
5、生成程序、板级调试
略
2024.12.23