当前位置:   article > 正文

实验(二)基于BASYS3平台的FPGA流水灯实验

basys3

枯藤老树昏鸦,小桥流水人家。                                   — — — —《天净沙.秋思》马致远

文章目录  

  • 前言
  • 一、实验内容
  • 二、实验平台
  • 设计思路与方案
  • 四、RTL代码设计
  • 五、仿真测试程序设计
  • 六、电路与仿真结果
  • 七、管脚规划
  • 八、板子上电演示
  • 总结


前言

大四毕业后白嫖了电子创新实验室的一块FPGA,这块板子适合做数电实验,为了物尽其用,趁这个暑假搭配特权同学做的《深入浅出玩转FPGA》视频学习入门一下,用它整点活。

一、实验内容

让板载的8颗LED灯每隔1秒依次点亮,全亮后又每隔1秒依次熄灭,循环。

二、实验平台

RTL代码编写平台:Vivado 2019.1

FPGA开发板:Xilinx  BASYS3

芯片型号:Artix家族  xc7a35tcpg236-1

三、设计思路与方案

1.总体构想

如下图所示,我的构想由计时模块和类38线译码器模块组合而成,时钟和复位信号输入,8颗LED灯作为输出,计时模块处理LED灯亮灭的时序逻辑,产生1Hz方波进行计数,由于计数值为0~7共8位,所以联想到38译码器,这样可以通过3位二进制数作为地址输出给后级模块去控制8颗LED灯(模拟成8位二进制数),LED状态的刷新受到计时模块时钟的控制。

2.计时模块设计

查看BASYS3开发板的原理图可知,系统时钟由DSC1033CC1-100.0000T晶振产生,为100Mhz,如下图所示。

为了达到“每隔1秒亮一次”,需要利用系统时钟产生1Hz 的方波(周期为1s),通过对系统时钟上升沿的计数可以实现,由于(100MHz/100 000 000=1Hz),所以当从0计到99 999 999时就得到1Hz的频率,并且在一周期内对半周期前后的电平翻转就可以得到1Hz、占空比50%的延时时钟。

在产生了延时时钟后,需要再对这个时钟的下降沿进行计数,如下图所示,从0计数到7,每一个计数值对应了一个3位的地址,依次对应着8颗LED灯的一种亮灭状态;计数过了7后清零,并且翻转片选信号,片选信号为0时运行的是“8颗LED灯每隔1秒依次点亮”的过程,为1时运行的是“每隔1秒依次熄灭”的过程。

3.类3-8译码器的设计

模块的真值表如下图所示,地址和片选共同作用使8位LED值输出对应的电平信号,高电平点亮LED,低电平熄灭LED。

 

四、RTL代码设计

1.计时模块leds程序

  1. module leds(
  2. input clk, //100Mhz系统时钟
  3. input rst, //复位信号,高电平有效
  4. output reg [2:0] ctrl_data, //控制数据,输出到后级译码器中
  5. output led_clk, //1Hz时钟
  6. output led_cs //片选信号,输出到后级译码器用于选择状态
  7. );
  8. reg[27:0] cnt; //计数值,用于产生1Hz信号
  9. parameter cnt_max = 28'd99_999_999; //计数最大值
  10. parameter cnt_half = 28'd49_999_999; //计数中值
  11. reg led_clk_r; //1Hz方波时钟
  12. reg led_cs_r; //片选信号
  13. /****产生1Hz的方波****/
  14. always @ (posedge clk or posedge rst)
  15. begin
  16. if(rst) cnt <= 28'd0;
  17. else if(cnt < cnt_max) cnt <= cnt+1'b1;
  18. else cnt <= 28'd0;
  19. end
  20. always @ (posedge clk or posedge rst)
  21. begin
  22. if(rst) led_clk_r <= 1'b0;
  23. else if(cnt < cnt_half) led_clk_r <= 1'b0;
  24. else led_clk_r <= 1'b1;
  25. end
  26. assign led_clk = led_clk_r;
  27. /****在1Hz方波时钟信号下计数****/
  28. always @ (negedge led_clk or posedge rst)
  29. begin
  30. if(rst)
  31. begin
  32. ctrl_data <= 3'd0;
  33. led_cs_r <= 1'b0;
  34. end
  35. else if(ctrl_data < 3'd7) ctrl_data <= ctrl_data+1'b1;
  36. else
  37. begin
  38. ctrl_data <= 3'd0;
  39. led_cs_r <= ~led_cs_r;
  40. end
  41. end
  42. assign led_cs = led_cs_r;
  43. endmodule

2.译码模块code程序

  1. module code(
  2. input clk, //100Mhz系统时钟
  3. input rst, //复位信号,高电平有效
  4. output reg [7:0] leds_out //输出8个LED信号
  5. );
  6. wire [2:0] ctrl; //前级输入的控制地址
  7. wire cs; //前级输入的片选信号
  8. /****leds模块例化语句****/
  9. leds myleds(
  10. .clk(clk),
  11. .rst(rst),
  12. .ctrl_data(ctrl),
  13. .led_cs(cs)
  14. );
  15. /****类3-8线译码器的设计****/
  16. always @ (ctrl or cs or rst)
  17. begin
  18. if(rst) leds_out = 8'b0000_0000;
  19. else
  20. begin
  21. if(!cs)
  22. begin
  23. case(ctrl)
  24. 3'b000: leds_out = 8'b0000_0001;
  25. 3'b001: leds_out = 8'b0000_0011;
  26. 3'b010: leds_out = 8'b0000_0111;
  27. 3'b011: leds_out = 8'b0000_1111;
  28. 3'b100: leds_out = 8'b0001_1111;
  29. 3'b101: leds_out = 8'b0011_1111;
  30. 3'b110: leds_out = 8'b0111_1111;
  31. 3'b111: leds_out = 8'b1111_1111;
  32. endcase
  33. end
  34. else
  35. begin
  36. case(ctrl)
  37. 3'b000: leds_out = 8'b1111_1110;
  38. 3'b001: leds_out = 8'b1111_1100;
  39. 3'b010: leds_out = 8'b1111_1000;
  40. 3'b011: leds_out = 8'b1111_0000;
  41. 3'b100: leds_out = 8'b1110_0000;
  42. 3'b101: leds_out = 8'b1100_0000;
  43. 3'b110: leds_out = 8'b1000_0000;
  44. 3'b111: leds_out = 8'b0000_0000;
  45. endcase
  46. end
  47. end
  48. end
  49. endmodule

五、仿真测试程序设计

注意,板子上的按钮为摁下接到高电平,松开接到低电平,原理图如下所示,这里我们用到的是5个中的1个,在后续小节会讲到如何分配管脚,所以在设计仿真测试程序时要让rst信号由低电平跳上高电平然后过一点点时间再拉低。

 1.leds计时模块仿真测试程序

  1. module leds_sim;
  2. reg clk;
  3. reg rst;
  4. wire [2:0] ctrl_data;
  5. wire led_clk;
  6. wire led_cs;
  7. leds text_leds(
  8. .clk(clk),
  9. .rst(rst),
  10. .ctrl_data(ctrl_data),
  11. .led_clk(led_clk),
  12. .led_cs(led_cs)
  13. );
  14. initial begin
  15. clk = 0;
  16. rst = 0;
  17. #1000 rst = 1;
  18. #1000 rst = 0;
  19. end
  20. always #5 clk = ~clk; //输出100Mhz方波信号,每隔5ns翻转一次电平
  21. endmodule

2.计时&译码仿真测试程序

  1. module cod_sim;
  2. reg clk;
  3. reg rst;
  4. wire [7:0] leds_out;
  5. code text_code(
  6. .clk(clk),
  7. .rst(rst),
  8. .leds_out(leds_out)
  9. );
  10. initial begin
  11. clk = 0;
  12. rst = 0;
  13. #1000 rst = 1;
  14. #1000 rst = 0;
  15. end
  16. always #5 clk = ~clk;
  17. endmodule

六、电路与仿真结果

下图为leds计时模块的行为仿真结果。

下图为leds&code总模块的仿真结果。

下图为综合后总模块的RTL原理图。

 

七、管脚规划

由下图所示,100Mhz系统时钟输入到了W5引脚中,所以我们给clk引脚配置为W5。

 

rst复位引脚我们配置为T17,原理图如下图所示。

 8颗LED灯管脚号如下图所示,从右往左位数依次升高。

 总的管脚配置文件如下所示。

  1. set_property PACKAGE_PIN T17 [get_ports rst]
  2. set_property PACKAGE_PIN U16 [get_ports {leds_out[0]}]
  3. set_property PACKAGE_PIN E19 [get_ports {leds_out[1]}]
  4. set_property PACKAGE_PIN U19 [get_ports {leds_out[2]}]
  5. set_property PACKAGE_PIN V19 [get_ports {leds_out[3]}]
  6. set_property PACKAGE_PIN W18 [get_ports {leds_out[4]}]
  7. set_property PACKAGE_PIN U15 [get_ports {leds_out[5]}]
  8. set_property PACKAGE_PIN U14 [get_ports {leds_out[6]}]
  9. set_property PACKAGE_PIN V14 [get_ports {leds_out[7]}]
  10. set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[7]}]
  11. set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[6]}]
  12. set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[5]}]
  13. set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[4]}]
  14. set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[3]}]
  15. set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[2]}]
  16. set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[1]}]
  17. set_property IOSTANDARD LVCMOS33 [get_ports {leds_out[0]}]
  18. set_property IOSTANDARD LVCMOS33 [get_ports rst]
  19. set_property PACKAGE_PIN W5 [get_ports clk]
  20. set_property IOSTANDARD LVCMOS33 [get_ports clk]

八、板子上电演示

仿真、综合、布局布线、分配管脚后,可以生成bit文件,通过USB线给开发板上电,vivado连上器件xc7a35tcpg236-1后将bit文件烧录至开发板。

实验运行视频如下所示。

QQ视频20230709170724

总结

 

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号