本实验通过一个简单的LED灯数字设计初步了解数字设计的基本思想,了解FPGA设计的基本流程,熟悉仿真软件ModelSim和综合编译工具Quartus II的使用方法和流程。要求通过FPGA来控制开发平台的核心板上8个LED灯的亮和灭,循环点亮核心板上的8个LED灯,每个LED点亮的时间为200毫秒。
图6-1给出了开发平台的核心板上LED灯的连接图。从图中可以看出,LED灯的正极连接到了3.3V,负极通过一个下拉电阻连接到了FPGA的管脚。为了点亮LED灯,连接负极的FPGA管脚必须拉低;为了熄灭LED灯,连接负极的FPGA管脚必须拉高。
为了使LED灯点亮的时间为200ms,那么连接到相应LED负极的FPGA管脚必须拉低200ms。由于核心板上的时钟是50MHz,如果拉低时间为200ms,则需要拉低的时钟周期数为:50,000,000*0.2=10,000,000。为此,需要一个24位(224=16,777,216>10,000,000)的计数器对点亮的时钟周期进行计数,计数器初始值为0,当计数到10,000,000时,则熄灭当前点亮的LED灯,点亮下一个LED灯。
为了分别控制每个LED灯,定义一个8位的寄存器,寄存器中的每一位分别控制一个LED灯,如果寄存器中的某一位设为0,则点亮相应的LED灯;如果某一位设为1,则熄灭相应的LED灯。
在仿真时需要注意的是,由于计数器的计数值非常大,为10,000,000,循环一次要10,000,000个时钟周期,仿真的时间会很长。在仿真的观测时间内很难看到计数器循环一次,所以在仿真时可以降低计数器的计数值,例如计数到10,减少仿真时间,在观测时间内至少应该看到计数器循环一次。
为了实现对8个LED灯的控制,设计了状态机State,状态机在不同的状态控制不同LED灯点亮。State有8个状态,每个状态停留200毫秒(图6-2)。
为了控制状态机在每个状态的停留时间,定义了计数器counter,counter从0~10,000,000循环计数,当计数器循环一次,状态State变迁一次。
整个功能由程序Ledcore.v实现,实现模块ledcore的信号定义如下:
input clk, reset; //时钟输入clk,同步复位输入reset。 output reg [7:0] led; //输出信号,控制LED灯的亮灭。 reg [2:0] state; //状态机State定义,7个状态需要3位。 reg [23:0] counter; //计数器counter定义,24位。
计数机循环计数,到10,000,000-1时归零:
always @(posedge clk) if(!reset) counter<= 24'b0; else if(counter == `COUNTER_NUMBER - 1 ) //`COUNTER_NUMBER在文件头定义,在仿真和实际运行时可以使用不同的值。 counter <= 24'b0; else counter<= counter+1'b1;
状态机控制逻辑,当计数器循环一次,状态机变迁一次:
always @(posedge clk) if(!reset) state <= 3'b000; else if(counter == 24'd0) state <= state + 1'b1;
组合逻辑,State的不同状态控制不同的Led灯点亮:
always @(state) case(state) 3'b0000 : led = 8'b1111_1110; 3'b0001 : led = 8'b1111_1101; 3'b0010 : led = 8'b1111_1011; 3'b0011 : led = 8'b1111_0111; 3'b0100 : led = 8'b1110_1111; 3'b0101 : led = 8'b1101_1111; 3'b0110 : led = 8'b1011_1111; 3'b0111 : led = 8'b0111_1111; Endcase
在程序仿真之前要设计Testbench,本实验的Testbench设计较为简单,只需要产生时钟和复位,ledtest.v是本程序的Testbench:
module test(); reg clk,reset; //需要给led_core产生的时钟和复位。 wire [7:0] led; //led用于观测输出结果。 led_core led_core( clk, reset ,led ); initial begin clk = 0; reset = 0; #40 reset = 1; //产生复位。 end always #10 clk=~clk; //产生时钟。 endmodule
图6-3是用modelsim仿真得到的结果。从图中可以看出,在程序运行的时刻,reset为高时,输出信号LED[7..0]中始终有一个信号为低电平,表示相应的灯亮。由于程序中计数器循环一次的时间太长,所以仿真时不容易看到状态的变化,所以仿真时可以降低计数器的计数值。本例中计数到10时清零。可以看出,计数器计到10时,State的状态转换一次。
文件名 | 功能 |
led_core.v | 主程序 |
ledtest.v | 仿真Testbench |
ledsim.mpf | ModelSim仿真项目 |
演示设备:核心板。 演示方法:将演示程序下载到开发系统后,可以观察到开发系统核心板上的LED灯按顺序依次点亮,reset为复位键。