第一题
根据以下描述功能用verilog编写一段代码,并用状态机来实现该功能。
(1)状态机:实现一个测试过程,该过程包括启动准备状态、启动测试、停止测试、查询测试结果、显示测试结果、测试结束返回初始化6个状态;用时间来控制该过程,90秒内完成该过程;
(2)描述状态跳转时间;
(3)编码实现。
- 代码
module six_state ( //90s内六个状态切换
input wire clk, //时钟,50M
input wire rst_n, //复位信号
output wire [3:0] led //4个led灯
);
//6个转态空间
parameter S0= 3'd0; //准备状态
parameter S1= 3'd1; //启动测试
parameter S2= 3'd2; //停止测试
parameter S3= 3'd3; //查询测试结果
parameter S4= 3'd4; //显示测试结果
parameter S5= 3'd5; //测试结束返回初始状态
//切换时间状态为1s
parameter MAX_NUM=26'd49_999_999;//1秒寄存器
reg [3:0] led_r; //灯寄存器·
reg [2:0] cstate;//现态
reg [2:0] nstate;//次态
reg [25:0] cnt; //时间寄存器
//1S计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt <= 26'd0;
end
else if (cnt == MAX_NUM)begin
cnt <= 26'd0;
end
else begin
cnt <= cnt + 1'd1;
end
end
//现态跟随次态
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cstate <= S0;
end
else begin
cstate <= nstate;
end
end
//状态切换
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
nstate <= S1;
end
else begin
case(cstate)
3'd0: begin
if(cnt == MAX_NUM)begin
nstate = S1;
end
else begin
nstate = nstate;
end
end
3'd1:begin
if(cnt == MAX_NUM)begin
nstate = S2;
end
else begin
nstate = nstate;
end
end
3'd2:begin
if(cnt == MAX_NUM)begin
nstate = S3;
end
else begin
nstate = nstate;
end
end
3'd3:begin
if(cnt == MAX_NUM)begin
nstate = S4;
end
else begin
nstate = nstate;
end
end
3'd4:begin
if(cnt == MAX_NUM)begin
nstate = S5;
end
else begin
nstate = nstate;
end
end
3'd5:begin
if(cnt == MAX_NUM)begin
nstate = S0;
end
else begin
nstate = nstate;
end
end
default :begin
if(cnt == MAX_NUM)begin
nstate = S0;
end
else begin
nstate = nstate;
end
end
endcase
end
end
//跟随状态输出
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
led_r <= 4'b0;
else begin
case (cstate)
3'd0: led_r <= 4'b0001;
3'd1: led_r <= 4'b0010;
3'd2: led_r <= 4'b0100;
3'd3: led_r <= 4'b1000;
3'd4: led_r <= 4'b0011;
3'd5: led_r <= 4'b1111;
default : led_r <= led_r ;
endcase
end
end
assign led = led_r ;
endmodule
- 效果
FPGA状态机实现六种状态转换
第二题
画出可以检测10010串的状态图, 并用verilog编程实现之。
-
状态图:
-
按键消抖:
module key_debounce(
input wire clk,
input wire rst_n,
input wire key,
output wire leaf,
output wire key_value
);
parameter TIMER_20MS = 20'd1_000_000;//20ms
reg [19:0] timer_20ms_r;
reg key_r;
reg key_value_r;
reg leaf_r;
//将按键值存入寄存器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_r <= 1'b0;
end
else begin
key_r <= key;
end
end
//按键消抖
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
timer_20ms_r <= 20'd0;
end
else if(key_r == key && key != 1'b1)begin
if(timer_20ms_r <= TIMER_20MS - 1'b1)begin
timer_20ms_r <= timer_20ms_r + 1'b1;
end
else begin
timer_20ms_r <= TIMER_20MS;
end
end
else begin
timer_20ms_r <= 20'd0;
end
end
//按键按下标志
assign leaf_w = (timer_20ms_r == TIMER_20MS - 1'b1)? 1'b1:1'b0;
//保存当前按键值
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_value_r <= 1'b0;
end
else if(leaf_w)begin
key_value_r <= ~key_value_r;
end
else begin
key_value_r <= 1'b0;
end
end
//使信号输出,延迟一个clk周期
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
leaf_r <= 1'b0;
end
else begin
leaf_r <= leaf_w;
end
end
assign leaf = leaf_r;
assign key_value = key_value_r;
endmodule
- 识别10010:
/* 密码:10010 */
module password(
input wire clk, //时钟
input wire rst_n, //复位信号
input wire [1:0] key, //按键
output wire [3:0] led //led灯
);
//转态空间
parameter IDLE = 3'd0; //初始状态
parameter S1 = 3'd1; //按对一个
parameter S2 = 3'd2; //按对两个
parameter S3 = 3'd3; //按对三个
parameter S4 = 3'd4; //按对四个
parameter S5 = 3'd5; //按对五个
parameter MAX_NUM=26'd49_999_999; //1s计数
parameter CNT_02 = 21'd1_999_999; //0.2s计数
reg [2:0] cstate; //现态
reg [2:0] nstate; //次态
reg [3:0] led_r; //led灯寄存器
reg [25:0] cnt; //1s寄存器
reg [20:0] cnt_200ms; //0.2s寄存器
//1s计时器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 26'd0;
end
else if (cnt == MAX_NUM)begin
cnt <= 26'd0;
end
else begin
cnt <= cnt + 1'd1;
end
end
//0.2s计时器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_200ms <= 26'd0;
end
else if(cnt == CNT_02)begin
cnt_200ms <= 26'd0;
end
else begin
cnt_200ms <= cnt + 1'd1;
end
end
//现态跟随次态
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cstate <= IDLE;
end
else begin
cstate <= nstate;
end
end
//状态转换
always@(posedge clk or negedge rst_n)begin
if(!rst_n) begin
nstate <= IDLE;
end
else begin
case(cstate)
IDLE:begin
if(key[1])begin
nstate <= S1;
end
else if(key[0])
nstate <= IDLE;
else
nstate <= nstate;
end
S1:begin
if(key[0])begin
nstate <= S2;
end
else if(key[1])
nstate <= IDLE;
else
nstate <= nstate;
end
S2:begin
if(key[0])begin
nstate <= S3;
end
else if(key[1])
nstate <= IDLE;
else
nstate <= nstate;
end
S3:begin
if(key[1])begin
nstate <= S4;
end
else if(key[0])
nstate <= IDLE;
else
nstate <= nstate;
end
S4:begin
if(key[0])begin
nstate <= S5;
end
else if(key[1])
nstate <= IDLE;
else
nstate <= nstate;
end
S5:begin
if(cnt == MAX_NUM)begin
nstate <= IDLE;
end
else begin
nstate <= nstate;
end
end
default:nstate <= IDLE;
endcase
end
end
//状态输出
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
led_r <= 4'b0000;
else begin
case(cstate)
IDLE: led_r <= 4'b0000;
S1 : led_r <= 4'b0001;
S2 : led_r <= 4'b0011;
S3 : led_r <= 4'b0111;
S4 : led_r <= 4'b1111;
S5 : begin
if(cnt_200ms == CNT_02)begin
led_r <= ~led_r;
end
else begin
led_r <= led_r;
end
end
default led_r <= led_r;
endcase
end
end
assign led =led_r;
endmodule
- 顶层文件
module password_top(
input wire clk,
input wire rst_n,
input wire [1:0] key,
output wire [3:0] led
);
wire [1:0] key_value;
wire [1:0] leaf;
key_debounce iinst0_key_debounce(
.clk (clk),
.rst_n (rst_n),
.key (key[0]),
.key_value (key_value[0]), //按键稳定信号
.leaf (leaf[0])//按键稳定标志
);
key_debounce inst1_key_debounce(
.clk (clk),
.rst_n (rst_n),
.key (key[1]),
.key_value (key_value[1]), //按键稳定信号
.leaf (leaf[1])//按键稳定标志
);
password inst_password(
.clk (clk),
.rst_n (rst_n),
.key ({key_value[1]&&leaf[1],key_value[0]&&leaf[0]}),
.led (led)
);
endmodule
- 效果:
FPGA状态机实现识别10010密码串
总结
状态机还是比较容易理解,用状态机进行状态的转换可以有效解决因为时序产生的毛刺。实现的效果都比较简单,如果下功夫可以玩出新花样,我算是抛砖引玉了。