TA的每日心情 | 衰 2024-10-10 16:16 |
---|
签到天数: 311 天 连续签到: 1 天 [LV.8]以坛为家I
|
本帖最后由 风之山谷 于 2019-1-9 02:33 编辑
利用了小时间来写了一段Verilog程序,利用Perf-V开发板上四个轻触按键和四个拔码开关来控制板上四个LED灯的亮度和亮灭。控制LED灯亮度是采用了PWM调制,就是通过控制PWM波的占空比来控制LED的亮度。
由于会Verilog并不一定能够顺利综合出对应的FPGA,因此在这个过程中调试了好一段时间。在这个程序中用到了Xilinx官方的两个IP核,一个是时钟配置IP核clk_wiz,用这个可以通过FPGA芯片内置的MMCM和PLL对内部所需时钟进行配置(在这里主要是对时钟信号整形)、以及另一个很实用的调试IP核——在线逻辑分析仪ILA(Integrated Logic Analyzer),原理就是通过FPGA内部逻辑综合出一个可以采集内部信号的记录仪,带有触发和数据采集功能,数据采集之后通过USB线把数据传回电脑,实现在线的逻辑分析仪功能。
主要功能是:拔码开关控制LED亮灭,LED带有四级亮度,点击对应轻触开关即可切换LED亮度,轻触开关带有20ms硬件防抖。
先贴Verilog代码:
- `timescale 1ns / 1ps
- //////////////////////////////////////////////////////////////////////////////////
- // Company:
- // Engineer:
- //
- // Create Date: 2018/12/24 01:02:35
- // Design Name:
- // Module Name: LED_PWM_TOP
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- //////////////////////////////////////////////////////////////////////////////////
- module LED_PWM_TOP(clk_in,
- rst,
- btn,
- sw,
- led
- );
- input clk_in,rst;
- input [3:0] btn;
- input [3:0] sw;
- output [3:0] led;
-
- wire clk;
- wire [3:0] led;
-
- clk_wiz_0 U1(clk, rst, clk_in);
- LED_PWM U2(clk, rst, btn, sw, led);
-
-
- endmodule
- module LED_PWM( clk,
- rst,
- btn,
- sw,
- led
- );
- input clk, rst;
- input [3:0]btn;
- input [3:0]sw;
- output [3:0]led;
- reg [16:0] cnt_100ns;
- reg clk_100ns;
- reg [1:0] bri_lev0;
- reg [1:0] bri_lev1;
- reg [1:0] bri_lev2;
- reg [1:0] bri_lev3;
- reg [3:0] led;
- reg [9:0] btn_cnt;
- reg [3:0] btn_pressdown_state;
- reg [3:0] btn_pressdown_check;
- //reg btn_deal_fb;
- reg [2:0] led_state;
- reg [3:0] bri_lev_s1;
- reg [3:0] bri_lev_s2;
- reg [3:0] bri_lev_s3;
- reg [3:0] led_pwm;
- reg btn_sign;
- parameter s0=3'b000, s1=3'b001, s2=3'b010, s3=3'b011, s4=3'b100, s5=3'b101, s6=3'b110, s7=3'b111;
- //ILA
- ila_0 U3( .clk(clk_100ns),
- .probe0(btn),
- .probe1(led),
- .probe2(btn_pressdown_check),
- .probe3(bri_lev0)
- );
- //Internal 100ns system clk => clk_100ns
- always@(posedge clk or negedge rst)
- if(!rst)
- begin
- clk_100ns <= 1'b0;
- cnt_100ns <= 17'b0;
- end
- else
- if( cnt_100ns >= 17'd5_000 )
- begin
- clk_100ns <= ~clk_100ns;
- cnt_100ns <= 17'b0;
- end
- else
- cnt_100ns <= cnt_100ns+17'b1;
- //LED state machine
- always@(posedge clk_100ns or negedge rst)
- if(!rst)
- begin
- led_state <= s0;
- led_pwm <= 4'b0000;
- end
- else
- begin
- case(led_state)
- s0://State 0
- begin
- led_pwm <= 4'b1111;
- led_state <= s1;
- end
- s1://State 1
- begin
- led_pwm <= bri_lev_s1;
- led_state <= s2;
- end
- s2://State 2
- begin
- led_pwm <= bri_lev_s1;
- led_state <= s3;
- end
- s3://State 3
- begin
- led_pwm <= bri_lev_s1;
- led_state <= s4;
- end
-
- s4://State 4
- begin
- led_pwm <= bri_lev_s2;
- led_state <= s5;
- end
-
- s5://State 5
- begin
- led_pwm <= bri_lev_s2;
- led_state <= s6;
- end
-
- s6://State 6
- begin
- led_pwm <= bri_lev_s3;
- led_state <= s7;
- end
-
- s7://State 7
- begin
- led_pwm <= bri_lev_s3;
- led_state <= s0;
- end
- default:;
- endcase
- end
- //Brightness level
- always@(bri_lev0 or bri_lev1 or bri_lev2 or bri_lev3)
- begin
- //case brighness level-0
- case(bri_lev0)
- 2'b10:
- begin
- bri_lev_s2[0] <= 1'b1;
- bri_lev_s3[0] <= 1'b0;
- end
- 2'b11:
- begin
- bri_lev_s2[0] <= 1'b1;
- bri_lev_s3[0] <= 1'b1;
- end
- default:
- begin
- bri_lev_s2[0] <= 1'b0;
- bri_lev_s3[0] <= 1'b0;
- end
- endcase
- //case brightness level-1
- case(bri_lev1)
- 2'b10:
- begin
- bri_lev_s2[1] <= 1'b1;
- bri_lev_s3[1] <= 1'b0;
- end
- 2'b11:
- begin
- bri_lev_s2[1] <= 1'b1;
- bri_lev_s3[1] <= 1'b1;
- end
- default:
- begin
- bri_lev_s2[1] <= 1'b0;
- bri_lev_s3[1] <= 1'b0;
- end
- endcase
- //case brightness level-2
- case(bri_lev2)
- 2'b10:
- begin
- bri_lev_s2[2] <= 1'b1;
- bri_lev_s3[2] <= 1'b0;
- end
- 2'b11:
- begin
- bri_lev_s2[2] <= 1'b1;
- bri_lev_s3[2] <= 1'b1;
- end
- default:
- begin
- bri_lev_s2[2] <= 1'b0;
- bri_lev_s3[2] <= 1'b0;
- end
- endcase
- //case brightness level-3
- case(bri_lev3)
- 2'b10:
- begin
- bri_lev_s2[3] <= 1'b1;
- bri_lev_s3[3] <= 1'b0;
- end
- 2'b11:
- begin
- bri_lev_s2[3] <= 1'b1;
- bri_lev_s3[3] <= 1'b1;
- end
- default:
- begin
- bri_lev_s2[3] <= 1'b0;
- bri_lev_s3[3] <= 1'b0;
- end
- endcase
- //bri_lev_s1
- bri_lev_s1[0] <= (bri_lev0[0]|bri_lev0[1]);
- bri_lev_s1[1] <= (bri_lev1[0]|bri_lev1[1]);
- bri_lev_s1[2] <= (bri_lev2[0]|bri_lev2[1]);
- bri_lev_s1[3] <= (bri_lev3[0]|bri_lev3[1]);
- end
- //LED output with enable switch
- always@(sw or led_pwm)
- led <= ~(sw & led_pwm);
- //Button press-down detect
- always@(posedge clk_100ns or negedge rst)
- if(!rst)
- begin
- btn_sign <= 1'b0;
- btn_pressdown_state <= 4'b0;
- btn_cnt <= 10'b0;
- btn_pressdown_check <= 4'b0;
- end
- else
- if((~btn) && (!btn_sign))
- begin
- btn_sign <= 1'b1;
- btn_pressdown_state <= btn;
- end
- else
- if(btn_sign)
- case(btn_cnt)
- 10'b01_1111_0100://20ms counter finish
- begin
- btn_pressdown_check <= ~(btn | btn_pressdown_state);
- if(~(btn | btn_pressdown_state))
- btn_cnt <= 10'b01_1111_0101;
- else
- begin
- btn_sign <= 1'b0;
- btn_pressdown_state <=4'b0;
- btn_pressdown_check <= 4'b0;
- btn_cnt <= 10'b0;
- end
- end
- 10'b01_1111_0101://reset until btn is released
- if(~(btn | btn_pressdown_state))
- ;
- else
- begin
- btn_sign <= 1'b0;
- btn_pressdown_state <=4'b0;
- btn_pressdown_check <= 4'b0;
- btn_cnt <= 10'b0;
- end
- default:
- begin
- btn_cnt <= btn_cnt + 10'b00_0000_0001;
- end
- endcase
- else
- begin
- btn_pressdown_check <= 4'b0;
- btn_cnt <= 10'b0;
- btn_pressdown_state <= 4'b0;
- end
-
- //Button brightness function
- always@(posedge btn_pressdown_check[0] or negedge rst)
- if(!rst)
- begin
- bri_lev0 <= 2'b00;
- end
- else
- begin
- bri_lev0 <= bri_lev0 + 2'b01;
- end
-
- //Button brightness function
- always@(posedge btn_pressdown_check[1] or negedge rst)
- if(!rst)
- begin
- bri_lev1 <= 2'b00;
- end
- else
- begin
- bri_lev1 <= bri_lev1 + 2'b01;
- end
-
- //Button brightness function
- always@(posedge btn_pressdown_check[2] or negedge rst)
- if(!rst)
- begin
- bri_lev2 <= 2'b00;
- end
- else
- begin
- bri_lev2 <= bri_lev2 + 2'b01;
- end
-
- //Button brightness function
- always@(posedge btn_pressdown_check[3] or negedge rst)
- if(!rst)
- begin
- bri_lev3 <= 2'b00;
- end
- else
- begin
- bri_lev3 <= bri_lev3 + 2'b01;
- end
- endmodule
复制代码
除了Verilog源代码外,还需要约束文件来配置输出管脚、配置时钟速率以及走线速率,Xilinx的约束文件为xdc。Perf-V开发板FPGA LED、轻触开关、拨码开关管脚约束文件:
- set_property IOSTANDARD LVCMOS33 [get_ports {btn[3]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {btn[2]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {btn[1]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {btn[0]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {sw[3]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {sw[2]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {sw[1]}]
- set_property IOSTANDARD LVCMOS33 [get_ports {sw[0]}]
- set_property IOSTANDARD LVCMOS33 [get_ports clk_in]
- set_property IOSTANDARD LVCMOS33 [get_ports rst]
- set_property PACKAGE_PIN R15 [get_ports {btn[3]}]
- set_property PACKAGE_PIN R16 [get_ports {btn[2]}]
- set_property PACKAGE_PIN T14 [get_ports {btn[1]}]
- set_property PACKAGE_PIN M15 [get_ports {btn[0]}]
- set_property PACKAGE_PIN K13 [get_ports {sw[3]}]
- set_property PACKAGE_PIN L14 [get_ports {sw[2]}]
- set_property PACKAGE_PIN M14 [get_ports {sw[1]}]
- set_property PACKAGE_PIN T15 [get_ports {sw[0]}]
- set_property PACKAGE_PIN N14 [get_ports clk_in]
- set_property PACKAGE_PIN L13 [get_ports rst]
- set_property PACKAGE_PIN P16 [get_ports {led[3]}]
- set_property PACKAGE_PIN P15 [get_ports {led[2]}]
- set_property PACKAGE_PIN N16 [get_ports {led[1]}]
- set_property PACKAGE_PIN M16 [get_ports {led[0]}]
复制代码
一般的FPGA开发流程是先编写Verilog源代码、调用IP核、编写约束文件、行为仿真、综合、综合后仿真、布局布线、布局后仿真、生成字节流文件、下载字节流文件、功能测试、(程序固化)。
下面建立工程文件就不说了,先来说说怎么调用IP核:
一、IP核调用
在Vivado左边流程导航(Flow Navigator)中点击IP Catalog,找到ILA,点击即可添加。图形化界面,都很好设置。
设置探头数量和位宽,数据储存深度等等。数据储存深度是比较吃FPGA上面RAM资源的,资源比较紧缺的FPGA型号不能选得太高。
这里选择四个探头来观察其逻辑。
点击OK即可,这里选择单独综合IP核,点击Generate来综合IP核。稍等一下即可。
等了一会之后,可以看见多了这么个东西,点击一下这个.v文件即可查看一下module接口,准备实例化这个IP核。
在这个Verilog文件中可见module接口参数,只需在需要观测的module上实例化这个IP核即可在下载BitSTEAM之后可以在线查看逻辑,相当于逻辑分析仪功能。
在module中实例化IP核。
clock wizard也是一样的操作。
由于只需要使用它对时钟信号进行整形,因此输出也选择100MHz。(晶振输入也是100MHz)点击OK即可添加。
二、Vivado中仿真Verilog代码
在编写好Verilog之后,由于综合和布局时间比较长,如果担心Verilog代码有问题,可以弄个行为仿真。一下为在Vivaodo中实现仿真功能。
先对着.v文件点右键->Move to simulation sources。这里别忘了,仿真的时候需要有Testbench文件来给予激励信号,这里别忘了编写和添加Testbench用的Verilog代码,此处为LED_PWM_TB文件,并且设置为顶层文件,并在Testbench中实例化待调试的module。
Verilog源代码文件和Testbench都就绪之后,右键sim_1,按图中所选。Vivado提供了5种仿真设置。Behavioral Simulation是行为级仿真,速度比较快,但是不考虑实际中线路长度所产生的延时问题;Post-Synthesis是综合之后的仿真,速度一般,不考虑走线延时,但是考虑寄存器等的延迟;Post-Implementation是布局布线后仿真,考虑寄存器、走线延迟,速度最慢。这里由于是低速应用,因此选择行为级仿真即可。
Vivado仿真界面和Modelsim软件界面十分相识,估计是Modelsim的OEM版本。在这里点击run即可仿真,仿真步进、仿真精度都可以选择,可以自己添加关心信号,把关心信号的逻辑显示出来,可以有效调试程序。仿真这部分不是本篇文章的重点,因此不仔细讲。
三、综合、布局布线、生成Bitsteam、下载、固化
这里不仔细说,可参考前一篇文章。
别忘记设置顶层文件。
点击OK即可。
RTL级原理图
布局之后原理图:
下载完成之后即可看到效果了。下载的时候别忘记把ITX调试探针文件也下载,不然在线逻辑分析仪功能不能用。
下面是逻辑分析仪,可以添加之前所选的信号进入,可以自定义触发条件。十分方便在线调试,可以十分方便捕捉到内部信号,不过缺点是不一定所有信号都在综合之后保留,有些会被综合掉了,这点需要注意。
|
|