【盘古100Pro+开发板实验例程】FPGA学习 | 基于紫光 FPGA 的键控 LED 流水灯

发布于:2025-08-01 ⋅ 阅读:(19) ⋅ 点赞:(0)

本原创文章由深圳市小眼睛科技有限公司创作,版权归本公司所有,如需转载,需授权并注明出处(www.meyesemi.com)

1. 实验简介

实验目的:

      通过按键控制 8 个 LED 灯按顺序依次点亮和熄灭。

实验环境:

      Window11

      PDS2022.2-SP6.4

硬件环境:

      MES2L676-100HP

2. 实验原理

通常的时,分,秒的计时进位大家应该不陌生;

、1 小时=60 分钟=3600 秒,当时针转动 1 小时,秒针跳动 3600 次;

在数字电路中的时钟信号也是有固定的节奏的,这种节奏的开始到结束的时间,我们通常称之为周期(T)。

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

f = 1/T

 在 100K 板卡上单端时钟有一个 27MHZ 的时钟。

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

图为开发板上 8 个 LED 灯的原理图。

控制 0.5s 更换一次 LED 灯状态,使 LED 灯呈现流水灯现象,可以每 0.5s 依次点亮一个 LED 灯。(高电平用 1 表示,低电平用 0 表示)

故只需要定义一个变量,让其在时钟上升沿达到时就+1,计数到 13500000-1 即可,此时就是 0.5S。

3. 接口列表

端口 I/O 位宽 描述
CNT_MAX Parameter 26 计数的最大值,修改定时的时间。
clk input 1 系统时钟,27MHZ
key input 1 按键信号
rst_n input 1 复位信号,低有效
led[7:0] output 8 led 灯控制信号

4. 工程说明

5. 代码模块说明

`timescale 1ns/1ns
`define UD #1

module key_led_test
#(
    parameter CNT_MAX = 26'd13_500_000
)
(
    input clk,
    input rstn,
    input key,
    
    output [7:0] led
);

//==============================================================================
// reg and wire

reg [25:0] led_light_cnt = 26'd0;
reg [7:0] led_status = 8'b0000_0001;

// time counter
always @(posedge clk) begin
    if (!rstn)
        led_light_cnt <= `UD 26'd0;
    else if (led_light_cnt == CNT_MAX-1)
        led_light_cnt <= `UD 26'd0;
    else
        led_light_cnt <= `UD led_light_cnt + 26'd1;
end

// led status change
always @(posedge clk) begin
    if (!rstn)
        led_status <= `UD 8'b0000_0001;
    else if (led_light_cnt == CNT_MAX-1 && key)
        led_status <= `UD {led_status[6:0], led_status[7]};
    else if (led_light_cnt == CNT_MAX-1 && !key)
        led_status <= `UD {led_status[0], led_status[7:1]};
end

assign led = led_status;

endmodule

      代码的第 5 行所定义的参数 CNT_MAX 是用来设定计数的最大值,默认是 13500000,也就是定时 0.5S,可以通过修改该值来改变定时的时间。

      代码的 24-32 行,用变量 led_light_cnt 实现了定时器的功能,每到时钟的上升沿就让其加 1,不断计数。由于从 0 开始计数,所以计数到 CNT_MAX-1 即把它清 0。

      代码的 35-43 行,实现了 LED 灯的状态控制,led_status 是个 8bit 的变量,当 key 没按下时,也就是 key 的值为 1 时,且 Led_light_cnt=CNT_MAX-1 时,就让 led_status 向左移 1 位。实现 LED0->LED7。当按键按下时,key 为 0,实现 LED7->LED0。

      代码 45 行,就通过组合逻辑,将 led_status 的值赋值给 led。

6. 代码仿真

`timescale 1ns/1ns

module tb_key_led_test();

    reg clk;
    reg rst_n;
    wire [7:0] led;
    reg key;
    
    reg [7:0] data;

    initial begin
        rst_n <= 0;
        clk <= 0;
        key <= 0;
        #20;
        rst_n <= 1;
        key <= 1;
        #2000;
        key <= 0;
        #2000;
        $display("I am stop");
        $stop;
    end

    always #10 clk = ~clk; // 20ns 50MHz

    key_led_test #(
        .CNT_MAX(10)
    ) u_key_led_test(
        .clk(clk),
        .rstn(rst_n),
        .key(key),
        .led(led)
    );

    initial begin
        $monitor("led:%b", led);
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            data <= 8'd0;
        else begin
            data <= {$random} % 256;
            $display("Now data is %d", data);
        end
    end

endmodule

      该 testbench 部分代码是用于测试一些仿真函数使用,在前面的 Modelsim 的使用章节已经做了介绍,所以本次只关注代码的 28-35 行,例化了我们的流水灯模块。可以看到代码的 29 行,CNT_ MAX传入的参数给的是10,这是为了减少仿真的时间,如果还是 13500000 的话,我们需要仿真 0.5s 才能看到结果,这非常的久。

      代码的 11-26 行依旧是对复位和时钟赋初值,延时 20ns 后,复位结束。时钟依然是每隔 10ns 取反一次,来生成 50MHZ 的时钟。

      上图为仿真结果,可以看到 led_light_cnt 每次计数 10 个数据后,led_status 会向左移一位,符合实验结果。

7.7. 实验步骤

这里将会详细介绍从新建工程到下载程序的具体步骤,后续的工程将不再详细解释。

7.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,完成工程的创建

7.2. 添加设计文件

 PDS 软件界面如下图:

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

     添加文件到工程:

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

    新建文件到工程:

    1)在窗口中点击 Create File;

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

      3)点击 OK;

     4)点击 Cancel;

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

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

7.3. 编译

可采用以下方式运行 Compile 流程:

(1)双击 Flow 中的 Compile 进行综合;

(2)右击 Compile 点击 Run 进行综合;

7.4. 工程约束

点击 Tools 选择 User Constraint Editor(Timing and Logic)或者点击工具栏图标 ,User Constraint Editor(Timing and Logic) 选择 Pre Synthesize UCE,如下图所示:

Tools 下的 User Constraint Editor(Timing and Logic)。

工具栏 User Constraint Editor(Timing and Logic)图标。

7.4.1. 时钟约束

打开 UCE 后,选择 Timing Constraints 后选择 Create Clock 添加基准时钟,基准时钟一般是通过输入 port 输入用户所使用的板上时钟。

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

7.4.2. 物理约束

打开 UCE 后,选择 Device 后选择 I/O,根据原理图编辑 IO 的分配。

按照原理图编辑好 IO 分配后,点击保存,会生成.fdc 文件,完成约束。

7.5. 综合

运行 Synthesize 流程有以下四种方式可以实现:

(1)双击 Flow 中的 Synthesize 进行综合;

(2)右击 Synthesize 点击 Run 进行综合;

完成 Synthesize 操作后,会看到下图所示:

7.6. Device Map

Device Map 的主要作用是将设计映射到具体型号的子单元上(LUT、FF、Carry 等)。运行 Device Map 流程有以下方式可以实现:

(1)直接双击 Device Map;

(2)右击 Device Map 点击 Run;

完成 Device Map 操作后,会看到下图所示:

7.7. Place & Route

布局布线(Place & Route)根据用户约束和物理约束,对设计模块进行实际的布局及布线。运行 Place & Route 流程有以下方式可以实现:

(1)直接双击 Place & Route;

(2)右击 Place & Route 点击 Run;

完成 Device Map 操作后,会看到下图所示:

7.8. Generate Bitstream

Generate Bitstream 生成二进制位流文件。运行 Generate Bitstream 流程有以下方式可以实现:

(1)直接双击 Generate Bitstream;

(2)右击 Generate Bitstream 点击 Run;

完成以上操作,将会产生位流文件。运行 Generate Bitstream,可以看到界面如下图所示:

7.9. 下载生成的位流文件

点击 Tools 选择 Configuration 或者点击工具栏图标 Configuration,如下图所示:

上图为 Tools 下的 Configuration 选项。

上图为工具栏 Configuration 图标。

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

在对话框中选择位流文件,添加该配置文件,提示所载入文件的绝对路径并在信息栏中显示,右键后点击 program 下载位流文件如下图所示:

下载位流文件成功如下图所示:

PG2L100H 板卡为 PG2L100H 的 FPGA 配置了一个外部 flash,其中,若需要将程序固化到板卡上需要将尾流文件转化为.sfc 文件。

首先点击 Configuration 页面的 Operations 选项的 Covert File 选项;

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

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

用户可通过右键下图位置,点击 Scan Outer Flash;

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

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

固化 Flash 成功如下图所示:

7.10. 上板验证

连接好电源和 jtag,然后打开板卡上的电源;

点击 PDS 软件上方的下载按钮,红框所示部分:

在弹出来的界面再点击如图红框部分:

点击后会自动搜索设备,如果连线没问题的话可以看到自动弹出一个界面来选择 sbit。然后选择 led_test.sbit;

右键“芯片”,然后选择 program,等待烧录完成即可。接下来观察上板现象。