飞道的博客

【紫光同创国产FPGA教程】【第二章】LED流水灯实验及仿真

463人阅读  评论(0)

原创声明:

本原创教程由芯驿电子科技(上海)有限公司(ALINX)创作,版权归本公司所有,如需转载,需授权并注明出处(http://www.alinx.com)。

适用于板卡型号:

PGL22G/PGL12G

1. 实验简介

通过LED流水灯实验,介绍使用PDS软件开发FPGA的基本流程,器件选择、设置、代码编写、编译、分配管脚、下载、程序FLASH固化、擦除等;同时也检验板上LED灯是否正常。

2. 实验环境

  • Windows 10 64位
  • Pango Design Suite 2020.3

3. 实验原理

3.1 LED硬件电路

开发板 LED部分原理图

从上面的LED部分原理图可以看出,开发板都是将IO经过一个电阻和LED串联接电源端,FPGA的IO输出低电平点亮LED。IO输出高电平LED灯熄灭,其中的串联电阻都是为了限制电流。

3.2程序设计

FPGA的设计中通常使用计数器来计时,对于50Mhz的系统时钟,一个时钟周期是20ns,那么表示一秒需要50000000个时钟周期,如果一个时钟周期计数器累加一次,那么计数器从0到49999999正好是50000000个周期,就是1秒的时钟。

程序中定义了一个32位的计数器:


  
  1. //Define the time counter
  2. reg [31:0] timer;

最大可以表示4294967295,十六进制就是FFFFFFFF,如果计数器到最大值,可以表示85.89934592秒。程序设计中是每隔1秒LED变化一次,一共消耗4秒做一个循环。


  
  1. always@(posedge sys_clk ornegedge rst_n)
  2. begin
  3. if(~rst_n)
  4. timer <= 32'd 0;
  5. elseif(timer == 32'd 199_ 999_ 999)
  6. timer <= 32'd 0;
  7. else
  8. timer <= timer + 1'b 1;
  9. end

在第一秒、第二秒、第三秒、第四秒到来的时候分别改变LED的状态,其他时候都保持原来的值不变。


  
  1. // LED control
  2. always@(posedge sys_clk ornegedge rst_n)
  3. begin
  4. if(~rst_n)
  5. led <= 4 'b0000;
  6. elseif(timer == 32 'd49_999_999)
  7. led <= 4 'b0001;
  8. elseif(timer == 32 'd99_999_999)
  9. led <= 4 'b0010;
  10. elseif(timer == 32 'd149_999_999)
  11. led <= 4 'b0100;
  12. elseif(timer == 32 'd199_999_999)
  13. led <= 4 'b1000;
  14. end

4. PDS工程

4.1 创建工程

1)启动Pango Design Suite 2020.3开发环境(在开始菜单中选择pango->Pango Design Suite 2020.3>Pango Design Suite 。Pango Design Suite(简称PDS)或者双击桌面的Pango Design Suite 2020.3的图标直接打开软件。

2)在PDS 开发环境里双击Create Project或File->New Project...这两种方式都可,如下图:

3) 弹出一个PDS的工程向导,点击Next按钮。

4)在弹出的对话框中输入工程名和工程存放的目录,这里取一个led_test的工程名,点击Next;

5) 在下面的对话框中默认选择RTL Project, 因为我们这里使用verilog行为描述语言来编程,单击Next

6) 进入Add Design Source Files界面,这里先不添加任何设计文件。点击Next;

7)这里问是否添加已有的IP,保持默认不添加,单击Next;

8)提示是否添加已有的约束文件,这里约束文件我们也没有设计好,也不添加。

9)在接下来的对话框选择所用的FPGA器件,以及进行一些配置。开发板首先在Family栏里选择Logos,Device中选择PGL22G,在Package栏选择BG324, Speed grade栏选择-6;综合工具选择ADS;单击NEXT进入下一界面:

10)再次确认一下板子型号有没有选对, 没有问题再点击“Finish”完成工程创建。

11)工程创建后如下图所示:

4.2 编写流水灯的verilog代码

1)双击Sources下的Designs图标;

2) 在Add Design Source Files界面中进行如下设置,点击OK;

3)可以看到已经新建发led_test.v文件,点击OK按钮。

向导会提示您定义I/O的端口,这里我们可以不定义,后面自己在程序中编写就可以,单击OK完成。

这时在Navigator界面下的Designs里已经有了一个led_test.v文件, 并且自动成为项目的顶层(Top)模块了。

4)接下去我们来编写led_test.v的程序,这里我们定义了一个32位的寄存器timer, 用于循环计数0~199_999_999(4秒钟), 当计数到49_999_999(1秒)的时候,熄灭第一个LED灯;当计数到99_999_999(2秒)的时候,熄灭第二个LED灯;当计数到149_999_999(3秒)的时候,熄灭第三个LED灯;当计数到199_999_999(4秒)的时候,熄灭第四个LED灯,计数器再重新计数。具体的操作直接看代码吧。


  
  1. `timescale1ns/ 1ns
  2. module led_test
  3. (
  4. sys_clk, // system clock 50Mhz on board
  5. rst_n, // reset ,low active
  6. led // LED,use for control the LED signal on board
  7. );
  8. input sys_clk;
  9. input rst_n;
  10. output[ 3: 0] led;
  11. //define the time counter
  12. reg[ 31: 0] timer;
  13. reg[ 3: 0] led;
  14. always@(posedge sys_clk ornegedge rst_n)
  15. begin
  16. if(~rst_n)
  17. timer <= 32 'd0; // when the reset signal valid,time counter clearing
  18. elseif(timer == 32 'd199_999_999) //4 seconds count(50M*4-1=199999999)
  19. timer <= 32 'd0; //count done,clearing the time counter
  20. else
  21. timer <= timer + 1 'b1; //timer counter = timer counter + 1
  22. end
  23. always@(posedge sys_clk ornegedge rst_n)
  24. begin
  25. if(~rst_n)
  26. led <= 4 'b0000; //when the reset signal active
  27. elseif(timer == 32 'd49_999_999) //time counter count to 1st sec,LED1 lighten
  28. led <= 4 'b0001;
  29. elseif(timer == 32 'd99_999_999) //time counter count to 2nd sec,LED2 lighten
  30. begin
  31. led <= 4 'b0010;
  32. end
  33. elseif(timer == 32 'd149_999_999) //time counter count to 3nd sec,LED3 lighten
  34. led <= 4 'b0100;
  35. elseif(timer == 32 'd199_999_999) //time counter count to 4nd sec,LED4 lighten
  36. led <= 4 'b1000;
  37. end
  38. endmodule

5)编写好代码后保存,点击菜单File -Save All。

添加UCE约束

User Constraint Editor(Timing and Logic)简称UCE,主要是完成管脚的约束,时钟的约束, 以及组的约束。这里我们需要对led_test.v程序中的输入输出端口分配到FPGA的真实管脚上。

1)击菜单栏“Tools”下的"User Constraint Editor";

2)在弹出的界面中单击Device;

3)在Device中单击I/O,可看到工程中用到的IO端口;

4)按如下方式分配管脚,LOC就是与硬件中FPGA相对应的管脚,VCCIO是FPGA的IO的电压标准,与硬件对应,其它在这里保持默认即可;

5)单击保存后会弹对话框,在这里选择默认;

4.4 生成位流文件

双击Generate Bitstream,然后软件会按照Synthesize-> Device Map-> Place & Route-> Generate Bitstream来产生位流文件。

如果工程在生成位流文件过程中没有错误,则会出现下图中每一步都正确的“√”,否则就会在Messages栏中显示errors的错误。

位流文件生成完成后,我们可以在Report Summary页面的到了FPGA资源的使用情况。

此外还可以通过下图操作查看RTL视图;

4.5 下载和调试

在上面生成了位流文件(.sbit)后,我们可以把sbit文件下载到FPGA芯片中,看一下LED实际运行的效果。下载和调试之前先连接硬件,把JTAG下载器和开发板连接,然后开发板上电(下图为开发板的硬件连接图)。

1)单击界面中的“Configuration”按钮,作用一是下载程序到FPGA中运行;二是固化程序到flash中。

2)在弹出的界面中的单击“Boundary Scan”,然后在右侧空白区单击右键选择“Scan Device”;

3)在扫描到JTAG设备后会弹出如下对话框,并按如下加载.sbit文件即可;

4)然后可以看到左侧显示了要加载的文件,选中右侧绿色的方块,右击会弹出下拉菜单并选择"Program...",下载完成后在板上可以在开发板上看到LED流水灯的效果。注意:这种方式程序是在FPGA运行,掉电后会消失。

4.6 FLASH程序固化

可能已经有朋友发现下载.sbit文件到FPGA后,开发板重新上电后配置程序已经丢失,还需要JTAG下载。这岂不麻烦!好吧,这一节我们来介绍如何把配置程序固化到开发板上的FLASH中,这样不用担心掉电后程序丢失了。

在我们的开发板上有一个8Pin的128Mbit的FLASH, 用于存储配置程序。我们不能直接把sbit文件下载到这个FLASH中,只能下载sfc文件到flash中。下面为大家介绍FLASH程序的固化的流程。

1)首先,需要sbit文件转换成能下载的flash的sfc文件。在完成上节下载和调试后,选择菜单"Operations"下"Convert File"进行文件转换。

然后弹出如下界面,这里要根据硬件的flash型号来选择flash的厂家和设备型号,开发板用到的是WINBOND的W25Q128Q。Flash Read Mode 选择SPI X4然后选择要转换的sbit文件,点击OK即可转换;

转换完成后显示如下界面,单击OK;

2)选中右侧绿色的方块,右击会弹出下拉菜单并选择"Scan outer Flash"。

选择已生成的sfc文件,单击Open;

可以看到界面中有了flash器件,选中“Outer Flash”绿色方块并右击选择菜单中“Program...”

弹出正在编程的进度界面,flash编程完成后进度界面自动消失。

至此,SPI FLASH 烧写完毕,led_test程序已经固化到SPI FLASH中了。我们来验证一下,关电重新启动开发板,等待一会儿你就可以看到开发板上的LED灯已经在做跑马运动了。

4.7 仿真验证

接下来我们不妨小试牛刀,让仿真工具modelsim来输出波形验证流水灯程序设计结果和我们的预想是否一致。具体步骤如下:

1)添加激励测试文件,点击Project下的Add Source;

2)点击Add or create simulation sources并"Next";

3)在弹出的对话框中输入激励文件的名字,这里我们输入名为vtf_led_test,其它按下图设置;

4)点击OK按钮返回。

5)这里我们先不添加IO Ports,点击OK。

6)在Simulation目录下多了一个刚才添加的vtf_led_test文件。双击打开这个文件,可以看到里面只有module名的定义,其它都没有。

7) 接下去我们需要编写这个vtf_led_test.v文件的内容。首先定义输入和输出信号,然后需要实例化led_test模块,让led_test程序作为本测试程序的一部分。再添加复位和时钟的激励。完成后的vtf_led_test.v文件如下:


  
  1. `timescale1ns/ 1ns
  2. //
  3. // Module Name: vtf_led_test
  4. //
  5. module vtf_led_test;
  6. // Inputs
  7. reg sys_clk;
  8. reg rst_n;
  9. // Outputs
  10. wire[ 3: 0] led;
  11. // Instantiate the Unit Under Test (UUT)
  12. led_test uut (
  13. .sys_clk(sys_clk),
  14. .rst_n(rst_n),
  15. .led(led)
  16. );
  17. initialbegin
  18. // Initialize Inputs
  19. sys_clk = 0;
  20. rst_n = 0;
  21. // Wait 100 ns for global reset to finish
  22. #1000;
  23. rst_n = 1;
  24. // Add stimulus here
  25. #20000;
  26. // $stop;
  27. end
  28. always #10 sys_clk =~ sys_clk; //20ns,
  29. endmodule

8) 编写好后保存,vtf_led_test.v自动成了这个仿真的顶层了,它下面是设计文件led_test.v;

9)接下来设置PDS的仿真配置,在软件菜单Project->Project Setting,然后在弹出的界面中进行如下设置,注意仿真库的路径在《00.Pango Design Suite 2020.3安装》教程中已介绍。,设置好后单击OK。

10)右击仿真文件并在下拉菜单中选择Run Behavioral Simulation。这里我们做一下行为级的仿真就可以了。

如果没有错误,PDS会调用Modelsim仿真软件开始工作了。

11)在弹出仿真界面后如下图,界面是仿真软件自动运行到仿真设置的50ms的波形。

由于LED[3:0]在程序中设计的状态变化时间长,而仿真又比较耗时,在这里观测timer[31:0]计数器变化。把它放到Wave中观察(点击界面中的uut, 再右击右侧timer, 在弹出的下拉菜单里选择Add Wave)。

添加后timer显示在Wave的波形界面上,如下图所示。

12)点击Restart按钮复位一下,再点击Run All按钮。(需要耐心!!!),可以看到仿真波形与设计相符。

 

我们可以看到led的信号会逐一变1,说明LED1~LED4灯逐个熄灭。

这里为止,我们的第一个项目就圆满完成了,相信您也掌握了PDS的FPGA开发的整个流程,再也不是那个FPGA的门外汉了吧! 师傅领进门,修行还需要靠本身!PDS软件的一些技巧的使用和掌握就需要靠大家在长期实践和探索中慢慢熟悉了。

5. 附录

led_test.v(verilog代码)


  
  1. `timescale1ns/ 1ps
  2. module led_test
  3. (
  4. input sys_clk, // system clock 50Mhz on board
  5. input rst_n, // reset ,low active
  6. outputreg[ 3: 0] led // LED,use for control the LED signal on board
  7. );
  8. //define the time counter
  9. reg[ 31: 0] timer;
  10. // cycle counter:from 0 to 4 sec
  11. always@(posedge sys_clk ornegedge rst_n)
  12. begin
  13. if(~rst_n)
  14. timer <= 32 'd0; //when the reset signal valid,time counter clearing
  15. elseif(timer == 32 'd199_999_999) //4 seconds count(50M*4-1=199999999)
  16. timer <= 32 'd0; //count done,clearing the time counter
  17. else
  18. timer <= timer + 1 'b1; //timer counter = timer counter + 1
  19. end
  20. // LED control
  21. always@(posedge sys_clk ornegedge rst_n)
  22. begin
  23. if(~rst_n)
  24. led <= 4 'b0000; //when the reset signal active
  25. elseif(timer == 32 'd49_999_999) //time counter count to 1st sec,LED1 lighten
  26. led <= 4 'b0001;
  27. elseif(timer == 32 'd99_999_999) //time counter count to 2nd sec,LED2 lighten
  28. led <= 4 'b0010;
  29. elseif(timer == 32 'd149_999_999) //time counter count to 3rd sec,LED3 lighten
  30. led <= 4 'b0100;
  31. elseif(timer == 32 'd199_999_999) //time counter count to 4th sec,LED4 lighten
  32. led <= 4 'b1000;
  33. end
  34. endmodule

注意:在定义寄存器时,如果寄存器在always块里使用必须定义为reg类型,如果仅是用于连线或是直接赋值需定义为wire类型,输入信号的类型不能定义为reg型,不管是reg类型信号还是wire类型的信号,定义的寄存器宽度必须满足使用时的需要,但必须稍大于或等于需要使用的位宽。若定义寄存器位宽远远大于使用需求则会浪费资源,如果定义的位宽小于使用需求,则会造成数据位截断,导致程序错误。还有其他信号的类型及用法请大家参考Verilog语法教程。


转载:https://blog.csdn.net/m0_53439615/article/details/113371986
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场