"); //-->
Verilog HDL描述的电路大都是并行实现的,状态机就是通过不同的状态迁移来完成一些特定的顺序逻辑。
流水灯状态机
用一个按键sel来控制流水灯的流向,而流水灯的流动是通过状态机来实现的,设计一个流水灯,由四个LED组成,流动间隔一秒,可以按键控制流向(向左还是右),代码如下:
/********************************文件信息**************************************
** 文件名称:state_machine
** 文件作者:樊文杰
** 创建日期:2012.02.16
** 软件平台:Quartus II 9.0
** 硬件平台:EP2C5T144C8
** 功能描述:状态机的行为级描述(顶层模块)
** 实验效果:按下复位键(P67)则四个LED全亮,松开则开始流水效果,按下SEL(P57)
** 则在等待一次闪动后,开始向相反方向流水(原因是在下一个时钟周期内才检测按键)
**
**--------------------------------------修改文件的相关信息----------------------
** 修改人:
** 修改日期:
** 版本号:
** 修改内容:
**
*******************************************************************************/
module state_machine(
clk, //时钟输入,20M
reset_n, //复位输入,低电平有效
sel, //流水灯方向控制端,0:按下,1:放开
Q //输出,1:点亮,0:熄灭
);
input clk; //输入
input reset_n;
input sel;
output [3:0] Q; //输出
reg [3:0] Q; //寄存器定义
wire clk_out; //计数的时钟,分频得到,周期:1S
reg [2:0] sts; //状态机的状态
parameter st1 = 2'b00, //状态机状态1参数定义
st2 = 2'b01, //状态机状态2参数定义
st3 = 2'b10, //状态机状态3参数定义
st4 = 2'b11; //状态机状态4参数定义
//***********************************************************/
// 模块名称:状态机描述
// 模块功能:根据不同的状态实现流水灯的左移和右移
//***********************************************************/
always@(posedge clk_out or negedge reset_n)
begin
if(!reset_n)
begin
Q <= 4'b1111; //复位的时候值为灯全亮
sts <= st1; //初始状态
end
else
case(sts) //通过不同的状态来实现流水灯的迁移
st1: //状态1
begin
Q <= 4'b0001; //第一状态的第一位输出1
if(sel)
sts <= st2; //根据SEL判断流水灯方向
else
sts <= st4;
end
st2:
begin
Q <= 4'b0010; //第二个状态第二位输出1
if(sel)
sts <= st3;
else
sts <= st1;
end
st3:
begin
Q <= 4'b0100; //第三个状态第三位输出1
if(sel)
sts <= st4;
else
sts <= st2;
end
st4:
begin
Q <= 4'b1000; //第四个状态第四位输出1
if(sel)
sts <= st1;
else
sts <= st3;
end
default:
sts <= st1; //复位的时候为状态1
endcase
end
//***********************************************************/
// 分频模块例化
// 从分频模块得到周期为1s 的方波
//***********************************************************/
clk_div clk_div_0
(
.clk(clk),
.reset_n(reset_n),
.clk_out(clk_out)
);
Endmodule
以下是分频模块源码:
/********************************文件信息**************************************
** 文件名称:state_machine
** 文件作者:樊文杰
** 创建日期:2012.02.16
** 软件平台:Quartus II 9.0
** 硬件平台:EP2C5T144C8
** 功能描述:状态机的行为级描述(分频模块,产生周期为1S的方波)
** 实验效果:
**
**--------------------------------------修改文件的相关信息----------------------
** 修改人:
** 修改日期:
** 版本号:
** 修改内容:
**
*******************************************************************************/
module clk_div(
clk, // 时钟输入,20M
reset_n, // 复位,高电平有效
clk_out // 分频时钟输出
);
parameter cnt_top = 26'd10000000; // 分频系数
input clk; // 端口
input reset_n;
output clk_out;
reg clk_out;
reg [25:0] clk_cnt; // 时钟计数器
always@(posedge clk or negedge reset_n)
begin
if(!reset_n) // 复位
begin
clk_out <= 1'b0; // 清零
clk_cnt <= 0;
end
else
begin
if(clk_cnt == cnt_top - 1) // 0.5 S计数到
begin
clk_out <= ~clk_out;// 分频输出(翻转电平)
clk_cnt <= 0; // 计数清零
end
else
clk_cnt <= clk_cnt + 1'b1;// 计数器加1
end
end
endmodule
实验效果是,流水灯从LED1向LED4流动,按下sel键后在等待一次流动再向相反方向流动,按下复位键,灯全亮!
四个流水灯共四种状态来实现“流动”效果,而这四种状态是预先定义好的,通过时钟周期来向下一个状态迁移达到效果,所以就定义了st1-st4这四种状态,在每种状态中都已经定义好了LED的亮灭情况,那么为什么按下sel后再等待一次闪动才相反流动呢,因为,FPGA是按时钟周期工作的,在这个时钟周期开始时已经判断了进入哪种状态,然后执行这个状态中的已定义好的逻辑,所以这时在这种状态中并没有检测sel的状态,而是按这个时钟周期开始前的sel状态执行,然后切换状态(已经按下了sel键),在下一个时钟周期,从新检测sel并判断状态,然后才按sel的状态切换状态,从而改变流动方向,应该是这要的。
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。