当前位置:   article > 正文

FPGA课程实验报告(使用Quartus Ⅱ&Verilog语言开发)_verilog序列检测器实验结论

verilog序列检测器实验结论

目录

1  实验操作流程

1.1 ModelSim仿真

1.2 引脚配置

1.3 程序下载

2  键控LED灯

2.1 实验要求

2.2 程序设计

2.3 软件仿真

2.4 实验结果

3  跑马灯

3.1 实验要求

3.2 程序设计

3.3 软件仿真

3.4 实验结果

4  序列检测器

4.1 实验要求

4.2 程序设计

4.3 软件仿真

4.4 实验结果

5  变速数字时钟

5.1 实验要求

5.2 程序设计

5.3 软件仿真

6  实验心得



 实验操作流程

        在本章中,将简单介绍实验所用到的软件操作流程,以供同学们参考。个人水平有限,所提供的方法不一定是最正确的、最便捷的,如有疏漏之处敬请指正。

1.1 ModelSim仿真

        由于软环境中没有时钟、按键等激励输入,我们无法对设计的电路进行功能性测试。因此需要手动编写Test Bench文件,以根据需求测试使用Verilog设计电路的功能、性能与预期是否相符。

        Test Bench文件可以自行编写,也可以在Quartus Ⅱ菜单栏Processing→Start中选择Start Test Bench Template Writer,让Quartus Ⅱ帮助我们生成Test Bench模板。自动生成的文件位于工程目录下的:simulation\ModelSim\*.vt。在模板的基础上,根据实际需要进行改写。

        编写完Test Bench文件后,需要修改工程的Test Bench配置。在Quartus Ⅱ菜单栏选择Assignments→Settings,打开如图1.2所示窗口。进入“Simulation”栏,选择仿真工具为“ModelSim-Altera”(注意不要选择成“ModelSim”)。点击“Test Benchs”按钮,在弹出的对话框中添加.vt文件,并输入正确的文件名、顶层模块名。

 图1.2 修改工程Test Bench配置

     为了能够在Quartus Ⅱ中直接调用ModelSim运行仿真,需要在Quartus Ⅱ菜单栏Tools→Options→general→EDA tool options中正确配置ModelSim运行路径,如图1.3所示。具体路径根据每个人安装时的位置而不同,只要确保路径的末级目录为\win32aloem即可。

图1.3 配置ModelSim路径

        设置完成后,点击Quartus Ⅱ上方工具栏中的RTL Simulation(如图1.4所示)启动ModelSim,仿真将在程序启动后自动运行。

 图1.4 启动仿真按钮

        但是这会存在一个问题:在ModelSim启动后,仿真便会自动开始运行,必须手动点击暂停,不便于我们观察波形。解决方法是在Test Bench中initial块执行的第一句添加代码“$stop”,如下所示。

  1. initial
  2. begin
  3. $stop;
  4. //Write your code here
  5. //……
  6. end

        这样,当ModelSim运行时,会在一开始就执行$stop指令暂停仿真,然后,我们就可以通过图1.5上方红框处的按钮,根据实际需要设置仿真时间,进行单步仿真。

        如果在调试过程中发现仿真结果有误,需要修改程序重新进行仿真,可以点击图1.5下方的transcript栏,用键盘的上箭头键浏览历史命令,找到“do *.do”命令按回车执行,就可以直接重启仿真,而不需要重启ModelSim。

图1.5 单步仿真与重启仿真

        在使用上述方法重启仿真时请务必注意:如果修改了Test Bench程序,必须保存.vt文件;如果修改了Verilog程序,则必须在Quartus Ⅱ中重新编译程序,否则重启仿真后执行的仍然是修改前的程序。

1.2 引脚配置

        当程序编写完成,且仿真结果无误后,便可以进行引脚配置,为后续将程序下载到开发板上运行做最后的准备。

        首先,点击Quartus Ⅱ菜单栏中的Assignments→Device,在图1.6所示窗口中选择正确的FPGA芯片系列、芯片型号(以手中的开发板为准)。

 图1.6 FPGA型号设置

        然后点击上图中的“Device and Pin Options”,打开图1.7所示窗口。按图中的方式配置未使用引脚和复用引脚。

 图1.7 配置未使用引脚和复用引脚

       完成上述操作后,编译程序。待程序编译完成后,在Quartus Ⅱ菜单栏选择Assignments→Pin Planner,打开图1.6所示窗口。顶层模块中定义的所有输入输出引脚都会被罗列在下方(如果在打开Pin Planner前没有编译程序,列出的引脚可能会有误),根据开发板提供的手册,在图中红框标识处填入正确的引脚,按回车键确认输入。

        引脚分配完成后,必须在Quartus Ⅱ中再次编译程序,必须在Quartus Ⅱ中再次编译程序,必须在Quartus Ⅱ中再次编译程序,才能使引脚配置生效。

 图1.6 引脚分配

1.3 程序下载

        程序经上述操作完成,且编译无误后,使用USB-Blaster将开发板正确连接到计算机。点击图1.7上方的“Programmer”键,弹出Programmer窗口。点击“Hardware Setup”,选择“USB-Blaster”。点击“Add File”,将工程目录下\output_files文件夹中自动输出的.sof文件添加进来。最后点击“Start”,将程序下载到FPGA开发板。

 图1.7 程序下载

2  键控LED灯

2.1 实验要求

        设计一个键控LED灯,要求具有以下功能:

        (1)按键KEY状态为000时,第一个灯亮;

        (2)按键KEY状态为001时,第二个灯亮;

        (3)按键KEY状态为010时,第三个灯亮;

        (4)按键KEY状态为011时,第四个灯亮;

        (5)按键KEY状态为100时,第五个灯亮;

        (6)按键KEY状态为101时,第六个灯亮;

        (7)按键KEY状态为110时,第七个灯亮;

        (8)按键KEY状态为111时,第八个灯亮。

2.2 程序设计

        本实验的程序为单个文件。以下为LED.v文件代码,功能是利用case语句根据key的输入选择不同的led控制位输出。

  1. /*LED.V*/
  2. module LED(key,led);
  3. input [2:0]key;
  4. output [7:0]led;
  5. reg [7:0]led = 8'd0;
  6. always@(key)
  7. begin
  8. case(key)
  9. 3'b111:led = 8'b10000000;
  10. 3'b110:led = 8'b01000000;
  11. 3'b101:led = 8'b00100000;
  12. 3'b100:led = 8'b00010000;
  13. 3'b011:led = 8'b00001000;
  14. 3'b010:led = 8'b00000100;
  15. 3'b001:led = 8'b00000010;
  16. 3'b000:led = 8'b00000001;
  17. default:led = 8'd0;
  18. endcase
  19. end
  20. endmodule

2.3 软件仿真

        编写Test Bench文件代码如下。初始按键值为3'b0,按键值每100ns加一,以模拟被顺序按下的情景。

  1. `timescale 1 ns/ 100 ps
  2. module led_tb();
  3. reg eachvec;
  4. reg [2:0] key_test;
  5. wire [7:0] led_test;
  6. LED i1 (
  7. .key(key_test),
  8. .led(led_test));
  9. initial
  10. begin
  11. $stop;
  12. key_test = 3'b0;
  13. end
  14. always
  15. #100 key_test = key_test + 3'b1;
  16. endmodule

        运行ModelSim,仿真结果如下图所示。

 图2.1 键控LED灯ModelSim仿真波形图

2.4 实验结果

        正确分配引脚后,将程序下载到开发板中,运行效果如图2.2所示。

图2.2 键控LED灯运行效果

3  跑马灯

3.1 实验要求

        设计一个双向跑马灯,具体要求如下:

        (1)当按键KEY=1时,8路LED灯从左往右依次亮灯,每次只亮一个灯,亮灯间隔时间1s;

        (2)当按键KEY=0时,8路LED灯从右往左依次亮灯,每次只亮一个灯,亮灯间隔时间1s;

        (3)当复位信号nRST为0时,恢复初始状态。

3.2 程序设计

        本实验的程序分为三个文件。以下为divide_N.v文件代码,功能是对FPGA的50MHz时钟进行分频。

  1. /*divide_N.v*/
  2. module divide_N(clk_in,clk_out,rst);
  3. input clk_in;
  4. input rst;
  5. output clk_out;
  6. reg clk_out;
  7. reg [25:0] cnt;
  8. parameter N = 50000000;//仿真时填入500,否则会出错
  9. always @(posedge clk_in or negedge rst)
  10. begin
  11. if(!rst)
  12. cnt <= 6'd0;
  13. else if(cnt == N-1)
  14. cnt <= 6'd0;
  15. else
  16. cnt <= cnt + 1'b1;
  17. end
  18. always @(posedge clk_in or negedge rst)
  19. begin
  20. if(!rst)
  21. clk_out <= 0;
  22. else if(cnt == N-1)
  23. clk_out <= 1'b1;
  24. else
  25. clk_out <= 1'b0;
  26. end
  27. endmodule

        以下为ctr_led.v文件代码,功能是控制引脚输出,实现跑马灯效果。其中,第12~15行是通过位拼接运算符实现了八位的循环位移,从而使LED灯依次循环点亮。

        以下为top.v文件代码,是本工程的顶层模块,功能是将上面两个文件中的模块连接起来。

  1. /*top.v*/
  2. module top(clk,rst,key,led);
  3. input clk;
  4. input rst;
  5. input key;
  6. output [7:0]led;
  7. wire clk_div;
  8. divide_N u1(
  9. .clk_in(clk),
  10. .clk_out(clk_div),
  11. .rst(rst));
  12. ctr_led u2(
  13. .clk_in(clk_div),
  14. .rst(rst),
  15. .key(key),
  16. .led(led));
  17. endmodule

        在多文件工程中,需要手动定义顶层文件,从而让编译器知道哪个是程序的顶层模块。设置方法为:右键点击编写的顶层文件(这里以top.v为例),点击“Set as Top-Level Entity”,如图3.1所示。

 图3.1 设置顶层文件

3.3 软件仿真

        跑马灯程序在FPGA开发板上运行时,闪烁时间为1s,以便肉眼能够清晰的观察到运行效果。但是在仿真实验过程中发现,当时间尺度过长时,无法得到正确的仿真结果。目前我仍然没有解决这个问题,临时的处理方法为:在仿真时使用较小的分频系数,即修改divide_N.v文件第十行N的值为500,此时跑马灯闪烁周期为10us,仿真结果正确。同时,欢迎有遇到类似情况的同学与我交流。

        编写Test Bench文件代码如下。初始按键值为1,在程序复位后的第40us,按键值变为0,跑马灯反向运行。

        运行ModelSim,仿真结果如下图所示。

 图3.2 跑马灯ModelSim仿真波形图

3.4 实验结果

        正确分配引脚后,将程序下载到开发板中,运行效果如图3.3所示。跑马灯运行方向由波动开关控制,闪烁周期为1s。

 图3.3 跑马灯运行效果

4  序列检测器

4.1 实验要求

        利用状态机设计一个序列检测器,实现以下功能:将一个指定序列从数字码流中识别出来。本实验要求设计一个“10010”序列的检测器。设X为数字码流的输入,Z为检测出标记输出,Z平时为高电平,一旦发现指定的序列10010,则变为低电平。

        由于码流难以产生,因此采用两个按键来模拟码流,设置两个按键,按键A和按键B,按键A按下代表输入了一个“1”,按键B按下代表输入了一个“0”,如果按键顺序为A-B-B-A-B,则代表输入了“10010”。

        另外,输出状态的改变可通过LED灯来指示。平时LED灯保持灭的状态,一旦检测到“10010”,LED灯亮。

4.2 程序设计

        本实验的程序分为三个文件。以下是shake_handle.v文件代码,功能是对FPGA的50MHz时钟进行分频,然后通过连续三次判断按键状态,实现按键消抖。

  1. /*shake_handle.v*/
  2. module shake_handle(
  3. input clk,
  4. input key,
  5. input nRST,
  6. output clk_5KHz,
  7. output reg key_o);
  8. reg [20:0]cnt;
  9. reg key_d1;
  10. reg key_d2;
  11. //10000分频,得到一个5kHz时钟
  12. always @(posedge clk or negedge nRST)
  13. begin
  14. if(!nRST)
  15. cnt <= 21'd0;
  16. else
  17. begin
  18. if(cnt<21'd9999)
  19. cnt <= cnt + 1'b1;
  20. else
  21. cnt <= 21'd0;
  22. end
  23. end
  24. assign clk_5KHz = (cnt<21'd5000)? 1'b1:1'b0;
  25. //按键消抖
  26. always @(posedge clk_5KHz or negedge nRST)
  27. begin
  28. if(!nRST)
  29. begin
  30. key_d1 <= 0;
  31. key_d2 <= 0;
  32. end
  33. else
  34. begin
  35. key_d1 <= key;
  36. key_d2 <= key_d1;
  37. end
  38. end
  39. always @(posedge clk_5KHz or negedge nRST)
  40. begin
  41. if(!nRST)
  42. key_o <= 1'b0;
  43. else
  44. begin
  45. if(key_d1 & key_d2 & key)//只有连续出现3个1,才认为是1
  46. key_o <= 1'b1;
  47. else if(!key_d1 & !key_d2 & !key)//只有连续出现3个0,才认为是0
  48. key_o <= 1'b0;
  49. end
  50. end
  51. endmodule

        以下为state_machine.v文件代码,功能是通过Verilog程序构建D触发器实现按键的下降沿检测,同时利用有限状态机实现序列检测功能。

  1. /*state_machine.v*/
  2. module state_machine(
  3. input clk,
  4. input nRST,
  5. input key_high,
  6. input key_low,
  7. output reg [4:0]state,
  8. output reg flag);
  9. //下面两个信号是在key的基础上延时一个时钟周期
  10. reg key_high_d;
  11. reg key_low_d;
  12. always @(posedge clk)
  13. begin
  14. key_high_d <= key_high;
  15. end
  16. always @(posedge clk)
  17. begin
  18. key_low_d <= key_low;
  19. end
  20. parameter
  21. S0 = 5'b00000,
  22. S1 = 5'b00001,
  23. S2 = 5'b00010,
  24. S3 = 5'b00100,
  25. S4 = 5'b01000,
  26. S5 = 5'b10000;
  27. //状态机
  28. always @(posedge clk or negedge nRST)
  29. begin
  30. if(!nRST)
  31. begin
  32. flag <= 0;//指示信号复位
  33. state <= S0;//状态变量复位
  34. end
  35. else
  36. case(state)
  37. S0:
  38. begin
  39. if(key_high_d & !key_high)//1
  40. state <= S1;
  41. else
  42. state<= state;
  43. end
  44. S1:
  45. begin
  46. if(key_high_d & !key_high)
  47. state <= S0;
  48. else if(key_low_d & !key_low)//0
  49. state<= S2;
  50. else
  51. state<= state;
  52. end
  53. S2:
  54. begin
  55. if(key_high_d & !key_high)
  56. state <= S0;
  57. else if(key_low_d & !key_low) //0
  58. state<= S3;
  59. else
  60. state<= state;
  61. end
  62. S3:
  63. begin
  64. if(key_high_d & !key_high)//1
  65. state <= S4;
  66. else if(key_low_d & !key_low)
  67. state<= S0;
  68. else
  69. state<= state;
  70. end
  71. S4:
  72. begin
  73. if(key_high_d & !key_high)
  74. state <= S0;
  75. else if(key_low_d & !key_low) //0
  76. state<= S5;
  77. else
  78. state<= state;
  79. end
  80. S5:
  81. begin
  82. flag <= 1'b1;
  83. state <= state;//死循环,除非复位,才会跳出循环
  84. end
  85. default:state<=S0;
  86. endcase
  87. end
  88. endmodule

        以下为top.v文件代码,是本工程的顶层模块,功能是将上面两个文件中的模块连接起来。

  1. /*top.v*/
  2. module top(
  3. clk_50MHz,
  4. key_A,
  5. key_B,
  6. nRST,
  7. state,
  8. LED);
  9. input wire clk_50MHz;
  10. input wire key_A;
  11. input wire key_B;
  12. input wire nRST;
  13. output wire LED;
  14. output wire [4:0]state;
  15. wire clk_5KHz;
  16. wire key1;
  17. wire key2;
  18. shake_handle u1(
  19. .clk(clk_50MHz),
  20. .key(key_A),
  21. .nRST(nRST),
  22. .clk_5KHz(clk_5KHz),
  23. .key_o(key1));
  24. shake_handle u2(
  25. .clk(clk_50MHz),
  26. .key(key_B),
  27. .nRST(nRST),
  28. .key_o(key2));
  29. state_machine u3(
  30. .clk(clk_5KHz),
  31. .nRST(nRST),
  32. .key_high(key1),
  33. .key_low(key2),
  34. .state(state),
  35. .flag(LED));
  36. endmodule

4.3 软件仿真

        按键消抖是毫秒级操作,在使用ModelSim进行仿真时,同样会出现类似于3.3节介绍的由于时间维度过大,导致仿真结果错误的问题。

        考虑到仿真时,按键的输入信号是不含任何抖动的,不需要进行消抖。因此,这里跳过了按键消抖模块,仅对状态机模块进行仿真验证。具体操作方法为:将state_machine.v设置为顶层模块,在编写Test Bench文件时,只对state_machine模块进行定义,具体代码如下。

        运行ModelSim,仿真结果如下图所示。

 图4.1 序列检测器ModelSim仿真波形图

4.4 实验结果

        正确分配引脚后,将程序下载到开发板中,运行效果如图4.2所示。两个按键分别代表“1”和“0”,输入时,LED灯会依次点亮。若输入正确的密码序列“10010”,密码正确指示灯点亮,如果过程中任意一位输入错误,LED灯会全部熄灭。

 图4.2 序列检测器运行效果

5  变速数字时钟

5.1 实验要求

        设计一个变速数字时钟,要求数字时钟的速度有三个档位:第一个档位为标准数字时钟,每隔1S秒计数器加1;第二个档位为快速数字时钟,每隔0.1S秒计数器加1;第三个档位为超快速数字时钟,每隔0.01S秒计数器加1。三个档位可用按键切换。

        除此之外,时钟具备按键清零功能;具有整点报时功能,即在59分59秒时给出指示信息(LED灯亮),持续时间为1s/0.1s/0.01s,指示信号结束的时刻恰好为正点时刻。

5.2 程序设计

        本实验的程序为单个文件。以下为clock.v文件代码,功能是实现时钟计时、进位,整点报时,数码管显示。

  1. /*clock.v*/
  2. module clock(CLK,nRST,key,seg,dig,led);
  3. input CLK,nRST;
  4. input [1:0]key;
  5. output [7:0]dig;
  6. output [6:0]seg;
  7. output led;
  8. reg led=1'b0;
  9. reg [7:0]dig,dis_data,num_data=3'd0;
  10. reg [6:0]seg;
  11. reg [31:0]count=32'd0;
  12. reg [5:0]shi=6'd0,fen=6'd0,miao=6'd0;
  13. reg [31:0]num;
  14. always @(key)//挡位调整
  15. begin
  16. if(key==2'd0)
  17. num<=32'd1000;//速度为1s
  18. else if(key==2'd1)
  19. num<=32'd100;//速度为0.1s
  20. else num<=32'd10;//速度为0.01s
  21. end
  22. //计时模块
  23. always @(posedge CLK or negedge nRST)
  24. begin
  25. if(!nRST)
  26. count<=32'd0;
  27. else if(count==num)
  28. count<=32'd0;
  29. else count<=count+1'b1;
  30. end
  31. always @(count)
  32. begin
  33. if(!nRST)
  34. miao<=6'd57;
  35. else if(count==num)
  36. if(miao==6'd59)
  37. miao<=6'd0;
  38. else miao<=miao+1'b1;
  39. end
  40. always @(miao)
  41. begin
  42. if(!nRST)
  43. fen<=6'd59;
  44. else if(miao==6'd00)
  45. if(fen==6'd59)
  46. fen<=6'd0;
  47. else fen<=fen+1'b1;
  48. end
  49. always @(fen)
  50. begin
  51. if(!nRST)
  52. shi<=6'd0;
  53. else if(fen==6'd00)
  54. if(shi==6'd23)
  55. shi<=6'd0;
  56. else shi<=shi+1'b1;
  57. end//该模块最后得到shi,fen,miao的输出
  58. //指示灯功能
  59. always @(miao)
  60. begin
  61. if(fen==6'd59)
  62. if(miao==6'd59)
  63. led<=1'b1;
  64. else led<=1'b0;
  65. else led<=1'b0;
  66. end
  67. //数码管显示模块
  68. always @(posedge CLK)
  69. begin
  70. if(num_data==5)
  71. num_data<=0;
  72. else num_data<=num_data+1'b1;
  73. end
  74. always @(posedge CLK)//1ms一次
  75. case(num_data)
  76. 3'd0:dis_data <= miao%6'd10;
  77. 3'd1:dis_data <= miao/6'd10;
  78. 3'd2:dis_data <= fen%6'd10;
  79. 3'd3:dis_data <= fen/6'd10;
  80. 3'd4:dis_data <= shi%6'd10;
  81. 3'd5:dis_data <= shi/6'd10;
  82. default:dis_data <= 6'd0;
  83. endcase
  84. always @(dis_data)
  85. case(dis_data )
  86. 4'd1:seg <= 7'b0000110; //’1’
  87. 4'd2:seg <= 7'b1011011; //’2’
  88. 4'd3:seg <= 7'b1001111; //’3’
  89. 4'd4:seg <= 7'b1100110; //’4’
  90. 4'd5:seg <= 7'b1101101; //’5’
  91. 4'd6:seg <= 7'b1111101; //’6’
  92. 4'd7:seg <= 7'b0000111; //’7’
  93. 4'd8:seg <= 7'b1111111; //’8’
  94. 4'd9:seg <= 7'b1101111; //’9’
  95. default:seg <= 7'b0111111; //’0’
  96. endcase
  97. always @(posedge CLK)
  98. case(num_data)
  99. 3'd0:dig <= 8'b0111_1111; //选择第1个数码管
  100. 3'd1:dig <= 8'b1011_1111; //选择第2个数码管
  101. 3'd2:dig <= 8'b1101_1111; //选择第3个数码管
  102. 3'd3:dig <= 8'b1110_1111; //选择第4个数码管
  103. 3'd4:dig <= 8'b1111_0111; //选择第5个数码管
  104. 3'd5:dig <= 8'b1111_1011; //选择第6个数码管
  105. 3'd6:dig <= 8'b1111_1101; //选择第7个数码管
  106. 3'd7:dig <= 8'b1111_1110; //选择第8个数码管
  107. default:dig <= 8'b1111_1111; //不选择
  108. endcase
  109. endmodule

5.3 软件仿真

        编写Test Bench文件代码如下。初始按键值为2'b0,即挡位为正常速度。提供50MHz时钟信号以供时钟运行。由于仿真程序时间尺度为秒级,设置仿真精度为100us以提高仿真效率。

  1. `timescale 100 us/ 10 us
  2. module led1_vlg_tst();
  3. reg CLK;
  4. reg [1:0]key;
  5. reg nRST;
  6. wire [7:0]dig;
  7. wire led;
  8. wire [6:0]seg;
  9. led1 i1 (
  10. .CLK(CLK),
  11. .dig(dig),
  12. .key(key),
  13. .led(led),
  14. .nRST(nRST),
  15. .seg(seg));
  16. initial
  17. begin
  18. $stop;
  19. CLK=1'b0;
  20. nRST=1'b0;
  21. key=2'b0;
  22. #100 nRST=1'b1; //10个时钟周期
  23. end
  24. always
  25. #5 CLK=~CLK;
  26. endmodule

        运行ModelSim,仿真结果如下图所示。

 图5.1 变速数字时钟ModelSim仿真波形图

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/411104
推荐阅读
相关标签
  

闽ICP备14008679号