基于EP4CE6F17C8的FPGA流水灯实例
一、电路模块
1、LED
开发板板载了4个用户LED发光二极管,其原理图如下所示,当 FPGA的引脚输出为逻辑 0时,LED会熄灭。输出为逻辑1时,LED被点亮。
其实物图如下所示。
LED的引脚分配见下表。
2、时钟晶振
开发板板载了一个50MHz的有源晶振,为系统提供时钟。
其实物图如下所示。
时钟输出引脚分配见下表。
3、按键
开发板板载了4个独立按键,其中有3个用户按键(KEY1~KEY3),1个功能按键(RESET)。按键按下为低电平(0),释放为高电平(1),4个按键的原理图如下图所示。本例中只使用了RESET键。
其实物图如下所示。
按键的引脚分配见下表。
二、实验代码
本例实现4位LED的流水灯,输出使用4个发光管led1~led4。模块名称为led4_shift,文件名称为led4_shift.v,设置为顶层模块,代码如下。
module led4_shift( input clk, //板载50HMz系统时钟 input rst_n, //复位按键 output reg[3:0] led //4个LED ); reg [27:0] timer; //定义时钟计数器 //4秒循环计数 always@(posedge clk or negedge rst_n) //敏感信号为时钟上沿或复位下沿 begin if(rst_n == 1'b0) //低电平复位 timer <= 28'd0; //复位时时钟计数器清零 else if(timer == 28'd199_999_999) //时钟计数器到达4秒时 timer <= 28'd0; //时钟计数器清零 else timer <= timer + 28'd1; //时钟计数器加1,即来一次时钟脉冲加一次 end //LED控制 always@(posedge clk or negedge rst_n) //敏感信号为时钟上沿或复位下沿 begin if(rst_n == 1'b0) //低电平复位时LED全灭 led <= 4'b0000; else if(timer == 28'd49_999_999) //时钟到1秒时,LED0点亮 led <= 4'b0001; else if(timer == 28'd99_999_999) //时钟到2秒时,LED1点亮 led <= 4'b0010; else if(timer == 28'd149_999_999) //时钟到3秒时,LED2点亮 led <= 4'b0100; else if(timer == 28'd199_999_999) //时钟到4秒时,LED3点亮 led <= 4'b1000; end endmodule
代码也可以写成如下的方式。
module led4_shift( input clk, //板载50HMz系统时钟 input rst_n, //复位按键 output reg[3:0] led //4个LED ); reg [25:0] timer; //定义时钟计数器 reg [2:0] sec; //定义秒计数器 //4秒循环计数 always@(posedge clk or negedge rst_n) //敏感信号为时钟上沿或复位下沿 begin if(rst_n == 1'b0) //低电平复位 begin timer <= 26'd0; //复位时时钟计数器及秒计数器清零 sec <= 3'd0; end else if(timer == 26'd49_999_999) //时钟计数器到达1秒时 begin timer <= 26'd0; //时钟计数器清零 sec <= sec + 3'd1; //秒计数器加1 end else if(sec == 3'd4) //秒计数器加到4时恢复零 sec <= 3'd0; else timer <= timer + 26'd1; //时钟计数器加1,即来一次时钟脉冲加一次 end //LED控制 always@(posedge clk or negedge rst_n) //敏感信号为时钟上沿或复位下沿 begin if(rst_n == 1'b0) //低电平复位时LED全灭 led <= 4'b0000; else if(sec == 3'd1) //时钟到1秒时,LED0点亮 led <= 4'b0001; else if(sec == 3'd2) //时钟到2秒时,LED1点亮 led <= 4'b0010; else if(sec == 3'd3) //时钟到3秒时,LED2点亮 led <= 4'b0100; else if(sec == 3'd4) //时钟到4秒时,LED3点亮 led <= 4'b1000; end endmodule
三、代码说明
1、always语句为过程语句,用于引导顺序语句,设计模块中的任何顺序语句都必须放在过程语句结构中。在@之后的括号中,列出所有输入敏感信号。在always语句中申明的变量必须为reg类型,不能使用wire类型。
begin与end之间为顺序块语句,仅限于用在always引导的过程语句结构中(assign中不能使用块语句)。
2、“<=”属于非阻塞赋值符号(“=”属于阻塞型赋值),只能用于顺序语句中,不能在assign引导的并行语句中。即assign中只能使用“=”赋值,而always中可以两种("="和"<=")都使用,但在同一过程中对同一变量赋值,两种不允许混用。而且,在许多情况下,不同的赋值符号将导致不同的电路结构和逻辑功能的综合结果。注意,对同一个变量的赋值不能出现在两个以上的always语句中。
3、条件判断if语句必须放在由always引导的顺序结构中。数字常量要指明位宽及使用的进制方式,如4'b0001,4表示位宽为4,b表示使用二进制,后面的数字要符合位宽。
4、同一结果可以有多种程序设计形式,以消耗逻辑器件最少为佳。
四、实验步骤
FPGA开发的详细步骤请参见“基于EP4CE6F17C8的FPGA开发流程(以半加器为例)”一文,本例只对一同之处进行说明。
本例工程放在D:\EDA_FPGA\Exam_2文件夹下,工程名称为Exam_2。模块文件名称为led4_shift.v,并设置为顶层实体。其余步骤与“基于EP4CE6F17C8的FPGA开发流程”中的一样。
接下来看管脚约束,本例中不仅使用了4个LED,还引入了时钟晶振和复位按钮,具体的端口分配如下图所示。
对于未用到的引脚设置为三态输入方式,多用用途引脚全部做为普通I/O端口,电压设置为3.3-V LVTTL(与”基于EP4CE6F17C8的FPGA开发流程“中的一样)。需要注意,程序中的每个端口都必须为其分配管脚,如果系统中存在未分配的I/O,软件可能会进行随机分配,这将造成不可预料的后果,存在烧坏FPGA芯片的风险。
接下来对工程进行编译,编译完成后,可查看一下逻辑器件的消耗情况,第一种代码的消耗如下图所示。
第二种代码的消耗如下图所示。
可以看到,第二种方式要消耗掉更多的器件。另外,还可以点击菜单Tools->Netlist Viewers->RTL Viewer,查看一下生成的RTL电路图。
最后进行程序下载,并查看结果。
根据前面的程序,初始化时4个led均为熄灭状态,1秒后led0点亮其余熄灭,再过1秒后led1点亮其余熄灭,再过1秒后led2点亮其余熄灭,再过1秒后led3点亮其余熄灭, 再过1秒后led0再次被点亮其余熄灭,如此循环,如下面4张图片所示。
当按下RESET键时,所示LED均熄灭。如下图所示。