? 本原创文章由深圳市小眼睛科技有限公司创作,版权归本公司所有,如需转载,需授权并注明出处(www.meyesemi.com)
1.实验简介
实验目的:
从创建工程到编写代码,完成引脚约束,最后生成 bit 流下载到开发板上,完成 Key0 控 制 led0 闪烁,Key1 控制 led1 亮灭。
实验环境:
Window11 PDS2022.2-SP6.4 芯片型号: PG2L50H-484
2.实验原理
通常的时,分,秒的计时进位大家应该不陌生;
1 小时=60 分钟=3600 秒,当时针转动 1 小时,秒针跳动 3600 次;
在数字电路中的时钟信号也是有固定的节奏的,这种节奏的开始到结束的时间,我们通常称之为周期(T)
?

在数字系统中通常关注到时钟的频率,那频率与周期的关系如下:

而本次开发板上的晶振提供了一个 25MHZ 的单端时钟。
所以其周期约为 40ns。而在我们FPGA 的设计中,我们的 always 块通常都是在时钟的上升沿时对数据进行赋值,因此我们可以定义一个变量,每到时钟的上升沿就让该变量+1,让其变成一个计数器,该变量每加 1 就表示经过了 40ns,那如果要定时 1s 的话,只需要让其计数到 24999999 即可,因为从 0 开始计数,所以计数到 24999999 即可,此时就是一秒了。以此类推,12499999 就是 0.5s。
?

** 错误!未定义书签。** 为开发板上 2 个 LED 灯的原理图。
?

** 错误!未定义书签。** 为开发板上 2 个按键的原理图。
KEY0 控制 LED0 每 1s 更换一次 LED 灯状态,KE1 控制 LELD1 亮灭状态。(高电平用 1 表示,低电平用 0 表示)
3.接口列表
接口列表 top.v 顶层模块接口列表:
端口 |
I/O |
位宽 |
描述 |
---|
sys_clk |
input |
1 |
系统时钟 25MHZ |
key0 |
input |
1 |
用户按键 0 |
key1 |
input |
1 |
用户按键 1 |
led_0 |
output |
1 |
led 灯控制信号 |
led1 |
output |
1 |
led 灯控制信号 |
btn_deb_fix.v 按键消抖模块接口列表:
端口 |
I/O |
位宽 |
描述 |
---|
BTN_WIDTH |
parameter |
4 |
案件数量 |
sys_clk |
input |
1 |
系统时钟 25MHZ |
rst_n |
input |
1 |
全局复位 |
btn_in |
input |
BTN_WIDTH |
用户按键输入 |
btn_deb_fix |
output |
BTN_WIDTH |
按键消抖后的输出(脉冲信号) |
4.工程说明
该工程框架如下所示:
?

本次工程主要完成按键控制 led 的状态。按键 0 控制 led0 闪烁,按键 1 控制 led1 亮灭。首先,key0 和 key1 均会经过按键消抖模块,因为开发板上使用的是机械按键,所以每次 按下均会产生抖动,如果不进行消抖,会造成误判。经过消抖后,每次按下按键均会产生持续一个 clk 的高电平即key0_flag和key1_flag。key0_flag控制是否打开 1s计数器来开启 led0 的闪烁,key1_flag 直接控制 LED1 翻转,每按下一次 key1,led 的状态翻转一次。
5.代码模块说明
module top(
input wire sys_clk ,
input wire key0 ,
input wire key1 ,
output reg led_0 ,
output reg led_1
);
parameter CNT_MAX = 32'd25_000_000 ;
reg [7:0] rsn_cnt =0 ;
reg [31:0] cnt_1s ;
reg led0_en ;
wire rst_n ;
wire key0_flag ;
wire key1_flag ;
always@(posedge sys_clk) begin
if(rsn_cnt >=100)
rsn_cnt <= rsn_cnt;
else
rsn_cnt <= rsn_cnt + 1'b1;
end
assign rst_n = (rsn_cnt>=100)?1'b1:1'b0 ;
always@(posedge sys_clk) begin
if(!rst_n)
led0_en <= 1'd0;
else if(key0_flag)
led0_en <= ~led0_en;
end
always@(posedge sys_clk) begin
if(!rst_n)
cnt_1s <= 32'd0;
else if(led0_en)
begin
if(cnt_1s == CNT_MAX-1)
cnt_1s <= 32'd0;
else
cnt_1s <= cnt_1s + 1'b1;
end
else
cnt_1s <= 32'd0;
end
always@(posedge sys_clk) begin
if(!rst_n)
led_0 <= 1'd0;
else if(led0_en)
begin
if(cnt_1s == CNT_MAX-1)
led_0 <= ~led_0;
else
led_0 <= led_0;
end
else
led_0 <= 1'd0;
end
always@(posedge sys_clk) begin
if(!rst_n)
led_1 <= 1'd0;
else if(key1_flag)
led_1 <= ~led_1;
end
btn_deb_fix#(
.BTN_WIDTH ( 4'd2 )
)u_btn_deb_fix(
.sys_clk ( sys_clk ),
.rst_n ( rst_n ),
.btn_in ( {key1,key0} ),
.btn_deb_fix ( {key1_flag,key0_flag} )
);
endmodule

CNT_MAX定义了一个最大的计数值,由于我们的系统时钟是 25MHZ,也就是25000000,所以要让 LED 实现 1s 闪烁的话就是从 0 计数到 24999999 的时候让 led 进行一次翻转。
在 26-31 行中,利用系统时钟计数 100 个周期后产生了一个复位信号用来给后续模块和 时序逻辑提供复位。
在 43-55 行中,当 led0_en 拉高时才开始计数一秒。否则计数器一直保持 0。
在 82-89 行中,例化了一个按键消抖的模块,按键按下并松开后将产生一个脉冲信号即 key0_flag,key1_flag,其中 key_0flag 控制 led0 闪烁,key1_flag 控制 led1 翻转。
//按键消抖
`define UD #1
module btn_deb_fix#(
parameter BTN_WIDTH = 4'd8 //按键数量
)
(
input sys_clk ,
input wire rst_n ,
input [BTN_WIDTH-1:0] btn_in ,
output reg [BTN_WIDTH-1:0] btn_deb_fix
);
//
parameter CNT_20MS_MAX = 32'd500_000 ; //20MS 计数
//
reg [23:0] cnt[BTN_WIDTH-1:0]; //计数器
reg [BTN_WIDTH-1:0] btn_in_reg ; //寄存按键信号
//打一拍
always @(posedge sys_clk) begin
btn_in_reg <= btn_in;
end
//
genvar i;
generate
begin
for(i=0;i<BTN_WIDTH;i=i+1)
begin
always @(posedge sys_clk) begin
if(!rst_n)
cnt[i] <= 24'd0;
if (btn_in_reg[i] == 1'b0) //按下时 计数 20ms 时归零
cnt[i] <= 24'd0;
else if(cnt[i]==CNT_20MS_MAX) //抖动区间有效时计数
cnt[i] <= cnt[i];
else
cnt[i] <= cnt[i] + 1'b1;
end
always @(posedge sys_clk) begin
if(!rst_n)
btn_deb_fix[i] <= 1'd0;
else if(cnt[i]==CNT_20MS_MAX-1) //消抖后输出一个 clk 的高电平
btn_deb_fix[i] <= 1'b1;
else
btn_deb_fix[i] <= 1'b0;
end
end
end
endgenerate
endmodule

该部分为按键消抖模块,parameter 定义了按键输入的数量,模块的输出将产生一个脉冲信号即产生一个持续一个 clk 的高电平信号。
在 30-50 行中,cnt 会不断进行 20ms 的计数,当按键按下时,cnt 归 0,从 0 开始计数直到 20ms。当计数到 20ms 的时候,就输出一个 clk 的高电平,即将 btn_deb_fix 置 1,并让其只保持一个 clk。
6.实验步骤
这里将会详细介绍从新建工程到下载程序的具体步骤,后续的工程将不再详细解释。
6.1. 打开 PDS 软件,创建工程
Step1:打开 PDS 软件,点击 NEW Project,然后对其设置完成新建工程。
?

Step2:单击 NEXT
?

Step3:创建名为 led_water 的工程到对应的文件目录,之后单击 Next。
新建工程大致包括设置工程名和工程路径、工程类型、工程文件及器件信息。
【Project Name】是工程文件名称,默认为 project。(只允许字母、数字、下划线(_)、杠(-)、点(.))。
【Project Location】用于选择新工程的工作路径,文件夹名只允许字母、数字、下划线
(_)、杠(-)、点(.)、@、~、,、+、=、#、空格( ),但空格不能出现在路径名首尾,即工程文件放置的路径。
【Create Preject Subdirectory】将工程文件名作为工作目录的一部分。
?

Step4:选择 RTL project,点击 Next。
【RTL Project】用于创建 RTL 工程。新建的工程可以执行 synthesize,device map,place& route,report timing,report power,generate netlist 及 generate bitstream 等。
【Post-Synthesize Project】用于创建综合后工程。新建的工程可以执行 device map, place& route,report timing,report power, generate netlist 及 generate bitstream 等。
?

Step5:单击 Next
该界面可以 Add Files 和 Add Directories 来添加 rtl 源文件及新建 rtl 源文件,以及调整 rtl 文件编译顺序,Add Files 添加选中的文件,Add Directories 添加选中的文件夹下所有合适的文件,若勾选了下方的 Add source from subdirecotires 则添加所有的子目录下合适的文件,也可直接 NEXT 跳过添加文件。
?

Step6:单击 Next
?

Step7:单击 Next
?

Step8:选择器件系列、型号、封装、速率、综合工具,之后单击 Next
synthesize tool 中可以选择综合工具为 Synplify Pro 或 ADS,在实验中使用 ADS 综合工具。
?

Step9:在 summary 单击 Finish,完成工程的创建

?****
6.2. 添加设计文件
PDS 软件界面如下图:
?

双击 Designs,将前面设计的 module 新建到文件中,或者将前面编辑好的 verilog 文件添加到工程中:
?

添加文件到工程:
在窗口中点击 Add Files,选择添加文件到工程;
?

新建文件到工程:
1)在窗口中点击 Create File;
?

2)选择 Verilog Design File,文件名和 module 名一致,默认路径,点击 OK;
?

3)点击 OK;
?

4)点击 Cancel;
?

5)默认打开新建文件,将前面设计的 代码 复制进去,
?

6)点击保存,新建文件完成
?

Crtl+s 保存。
?

双击 Designs。
?

点击 Add Files;
?

添加 btn_deb_fix.v 模块,即按键消抖模块。
?

点击 OK。
6.3. 编译
可采用以下方式运行 Compile 流程:
(1)双击 Flow 中的 Compile 进行综合;
(2)右击 Compile 点击 Run 进行综合;
?

6.4. 工程约束
点击 Tools 选择 User Constraint Editor(Timing and Logic)或者点击工具栏图标 ,User Constraint Editor(Timing and Logic) 选择 Pre Synthesize UCE,如下图所示。
?

Tools 下的 User Constraint Editor(Timing and Logic)
6.4.1. 时钟约束
打开 UCE 后,选择 Timing Constraints 后选择 Create Clock 添加基准时钟,基准时钟一般是通过输入 port 输入用户所使用的板上时钟。
?

在弹窗中对时钟命名,关联时钟管脚,添加时钟参数,点击 OK 会创建一条时钟约束,Reset 重置该页面。创建完成如下图所示:
?

提供给开发板的时钟是 25MHZ,即周期为 40ns。
?

6.4.2. 物理约束
打开 UCE 后,选择 Device 后选择 I/O,根据原理图编辑 IO 的分配。
?

按照原理图编辑好 IO 分配后,点击保存,会生成.fdc 文件,完成约束。
6.5. 综合
运行 Synthesize 流程有以下四种方式可以实现:
(1)双击 Flow 中的 Synthesize 进行综合;
(2)右击 Synthesize 点击 Run 进行综合;
完成 Synthesize 操作后,会看到下图所示:
?

6.6. Device Map
Device Map 的主要作用是将设计映射到具体型号的子单元上(LUT、FF、Carry 等)。
运行 Device Map 流程有以下方式可以实现:
(1)直接双击 Device Map;
(2)右击 Device Map 点击 Run;
完成 Device Map 操作后,会看到下图所示:
?

6.7. Place & Route
布局布线(Place & Route)根据用户约束和物理约束,对设计模块进行实际的布局及布线。
运行 Place & Route 流程有以下方式可以实现:
(1)直接双击 Place & Route;
(2)右击 Place & Route 点击 Run;
完成 Device Map 操作后,会看到下图所示:
?

6.8. Generate Bitstream
Generate Bitstream 生成二进制位流文件。运行 Generate Bitstream 流程有以下方式可以实现: (1)直接双击 Generate Bitstream;
(2)右击 Generate Bitstream 点击 Run;
完成以上操作,将会产生位流文件。
运行 Generate Bitstream,可以看到界面如下图所示:

?
6.9. 下载生成的位流文件
点击 Tools 选择 Configuration 或者点击工具栏图标 Configuration,如下图所示。
?

Tools 下的 Configuration
工具栏 Configuration 图标
打开 Configuration 后直接选择 Scan Device 直接进行扫描 Jtag 链操作,初始化链成功,会将链上扫描到的所有器件显示于工作区内,并在器件属性窗口显示当前器件的器件 信息,并弹出对话显示能够为器件添加的配置文件:
?

初始化链成功
在对话框中选择位流文件,添加该配置文件,提示所载入文件的绝对路径并在信息栏中显示,如下图所示:
?

下载位流文件
?

当发现这 4 个信号均为 1 时,表示下载成功。
同时,开发板也配置了一个外部 flash,其中,若需要将程序固化到板卡上需要将尾流文 件转化为.sfc 文件。
首先点击 Configuration 页面的 Operations 选项的 Covert File 选项。
?

点击后会出现如下画面,在Generate Flash Programing File页面选择对应的Flash 器件的厂商名、型号、再在 BitStreamFile 位置选择位流文件的路径,点击 OK。(若使用的 flash 器件不在可选的 flash 列表中,需手动添加对应 flash 型号,操作步骤请参考开发板下载与固化相说明)

?
转化.sfc 文件成功后,页面会如下图所示,点击 OK。

?
页面会显示板卡搭载的 Flash 的型号,点击.sfc 文件,点击 OPEN。

?
在下图位置点击鼠标右键后,点击 Program。

?
固化 Flash 成功如下图所示:

?
此时将板卡断电,再重新上电,如果按下 key0 和 key1 能看到对应的实验现象的话,则表示固化成功。(等大概 15s)
?