Lemmings4,两个问题卡了很久。
这里将总结和最后代码附上。
本次出现了两个问题。
问题1:
这个题目里边儿,我的状态转换的逻辑是对的。但是计时器的时序逻辑错了。
计时器用的是寄存器变量,需要在cnt自增的always块中赋值,如果此时判断的条件是cur_st的话(即(cur_st == FALL_L || cur_st == FALL_R)),
计时器cnt的值的改变就会比cur_st的改变又慢一个周期——在状态FALL_L或者FALL_R中,判断条件
对于cnt>20,此时,cnt的值还是上一次的值。特别是对于临界值的判断,比如cnt = 20的时候,在
cnt>20的时候,拿到的值还是19.所以最终在波形上就会出现“当碎而未碎”的情况。
因此,使用cnt>20的判断条件,就需要将cnt自增时序逻辑中的条件换为nxt_st。
当然,使用cur_st作为cnt自增的判断条件,也可以将cnt>20改为cnt>19.就是cnt少计时一个周期。
问题2:
计时器的位宽问题。
按照我设计的逻辑,等到Lemming落地的时候,才会判断下落的周数。
但是Lemming下降的过程中,落地之前,周期数是无限的,可能超过20个。
因此,按照逻辑判断的逻辑,最好的办法是当周期数目超过20个,计时器就停止自增。
或者,采取另一种逻辑:当cnt计时到20个周期,就进入到另一个状态:虽然此时没落地,但是周期超时,Lemming必然“碎”。
当然到了另一个状态,计时逻辑也要停止。
module top_module(
input clk,
input areset,
input bump_left,
input bump_right,
input ground,
input dig,
output walk_left,
output walk_right,
output aaah,
output digging
);
localparam
LEFT = 3'b000,
RIGHT = 3'b001,
FALL_L = 3'b010,
FALL_R = 3'b011,
DIG_L = 3'b100,
DIG_R = 3'b101,
CEASE_L = 3'b110,
CEASE_R = 3'b111;
reg [2:0] cur_st, nxt_st;
reg [4:0] cnt;
always @(posedge clk or posedge areset) begin
if (areset) cur_st <= LEFT;
else cur_st <= nxt_st;
end
always @(posedge clk or posedge areset) begin
if (areset) cnt <= 0;
else if (cur_st == FALL_L || cur_st == FALL_R) begin
if (cnt > 20) cnt <= cnt;
else cnt <= cnt+1'b1;
end
else cnt <= 0;
// $display("当前状态:%d; 当前计时:%d", cur_st, cnt);
end
always @(*) begin
nxt_st = LEFT;
case (cur_st)
LEFT: nxt_st = ground?(dig?DIG_L:(bump_left?RIGHT:LEFT)):FALL_L;
RIGHT: nxt_st = ground?(dig?DIG_R:(bump_right?LEFT:RIGHT)):FALL_R;
DIG_L: nxt_st = ground?DIG_L:FALL_L;
DIG_R: nxt_st = ground?DIG_R:FALL_R;
FALL_L: nxt_st = ground?(cnt>19?CEASE_L:LEFT):FALL_L;
FALL_R: nxt_st = ground?(cnt>19?CEASE_R:RIGHT):FALL_R;
CEASE_L: nxt_st = CEASE_L;
CEASE_R: nxt_st = CEASE_R;
endcase
end
assign walk_left = (cur_st == LEFT);
assign walk_right = (cur_st == RIGHT);
assign digging = (cur_st == DIG_L || cur_st == DIG_R);
assign aaah = (cur_st == FALL_L || cur_st == FALL_R);
endmodule