题目:
首先沿用了 Lemmings1 的条件,这里给出:
《旅鼠》这款游戏涉及大脑相当简单的动物。简单到我们要用有限状态机来建模。在《旅鼠》的2D世界中,旅鼠可以有两种状态:向左走或向右走。如果碰到障碍物,它会改变方向。特别是,如果旅鼠在左边被撞了,它会向右走。如果它在右边被撞了一下,它就会向左走。如果两边同时碰撞,它仍然会改变方向。实现具有两个状态、两个输入和一个输出的Moore状态机,以模拟此行为。
Lemmings2 多出的条件:
英文原文:
翻译:
除了左右行走之外,如果地面消失在它们脚下,旅鼠还会摔倒(大概会发出“啊!”的声音)。除了左右行走和颠簸时改变方向外,当地面=0时,旅鼠会摔倒并发出“啊!”当地面重新出现时(地面=1),旅鼠将继续沿着坠落前的方向行走。在下落过程中被撞不影响行走方向,在地面消失(但尚未下落)的同一周期内被撞,或者地面在下落过程中再次出现,也不影响行走方向。构建一个有限状态机来模拟这种行为。
下面直接给出仿真成功的代码,以及我自己额外写的测试代码,有兴趣的可以直接拿来在自己电脑上跑着玩:
RTL 代码:
module Lemmings(
input clk,
input areset,
input bump_left, bump_right,
input ground,
output walk_left,
output walk_right,
output aaah
);
reg [1:0] state, next_state;
parameter left = 2'b00, right = 2'b01, fall_left = 2'b10, fall_right = 2'b11;
always@(posedge clk, posedge areset) begin
if(areset) begin
state <= left;
end
else begin
state <= next_state;
end
end
always@(*) begin
case(state)
left: begin
if(~ground) begin
next_state = fall_left;
end
else if(bump_left) begin
next_state = right;
end
else begin
next_state = left;
end
end
right: begin
if(~ground) begin
next_state = fall_right;
end
else if(bump_right) begin
next_state = left;
end
else begin
next_state = right;
end
end
fall_left: next_state = ground?left:fall_left;
fall_right: next_state = ground?right:fall_right;
default: next_state = state;
endcase
end
assign walk_left = (state == left);
assign walk_right = (state == right);
assign aaah = (state == fall_left || state == fall_right);
endmodule
TB 代码:
module Lemmings_tb;
reg clk, areset;
reg bump_left, bump_right;
reg ground;
wire walk_left, walk_right;
wire aaah;
reg [4:0] ground_delay;
parameter hclk = 10;
Lemmings u_Lemmings(
.clk(clk),
.areset(areset),
.bump_left(bump_left),
.bump_right(bump_right),
.ground(ground),
.walk_left(walk_left),
.walk_right(walk_right),
.aaah(aaah)
);
initial begin
clk = 0;
areset = 0;
bump_left = 0;
bump_right = 0;
ground = 1;
ground_delay = 5'd10;
#5 areset = 1;
#5 areset = 0;
end
always #hclk clk = ~clk;
// always begin
// #(2*hclk*({$random()}%20)) ground = 0;
// #(2*hclk*({$random()}%10)) ground = 1;
// end
always@(posedge clk) begin
if(ground_delay==0) begin
ground <= ~ground;
if(~ground) begin
ground_delay <= {$random()}%20;
end
else begin
ground_delay <= {$random()}%10;
end
end
else begin
ground <= ground;
ground_delay <= ground_delay - 1;
end
end
always@(posedge clk) begin
if({$random()}%6<1) begin
bump_left <= 1;
end
else begin
bump_left <= 0;
end
end
always@(posedge clk) begin
if({$random()}%6<1) begin
bump_right <= 1;
end
else begin
bump_right <= 0;
end
end
initial begin
#4000 $finish;
end
endmodule
波形:
本题为有限状态机,且题目中已经很贴心的给出了一个状态转移图:
将上述状态转移图补全:
根据题目可知,ground 这一条件的优先级要高于 dump_left 和 dump_right,因此得到代码:
always@(*) begin
case(state)
left: begin
if(~ground) begin
next_state = fall_left;
end
else if(bump_left) begin
next_state = right;
end
else begin
next_state = left;
end
end
right: begin
if(~ground) begin
next_state = fall_right;
end
else if(bump_right) begin
next_state = left;
end
else begin
next_state = right;
end
end
fall_left: next_state = ground?left:fall_left;
fall_right: next_state = ground?right:fall_right;
default: next_state = state;
endcase
end
这里面:
left: begin
if(~ground) begin
next_state = fall_left;
end
else if(bump_left) begin
next_state = right;
end
else begin
next_state = left;
end
end
先判断 ground 是否为0,然后再去判断 bump_left 和 bump_right,对于其他情况保持原状态 left,对于状态 right,同理,这里不再赘述。
always@(posedge clk, posedge areset) begin
if(areset) begin
state <= left;
end
else begin
state <= next_state;
end
end
这里面有一个异步复位,并为 state 更新状态。
assign walk_left = (state == left);
assign walk_right = (state == right);
assign aaah = (state == fall_left || state == fall_right);
最后得到输出,整体采用三段式写法,易于修改和阅读。
测试代码有一个地方说一下:
always@(posedge clk) begin
if(ground_delay==0) begin
ground <= ~ground;
if(~ground) begin
ground_delay <= {$random()}%20;
end
else begin
ground_delay <= {$random()}%10;
end
end
else begin
ground <= ground;
ground_delay <= ground_delay - 1;
end
end
这里面 ground 在最前面设置了初值为1,我想让 ground 为1的时间长一些,为0的时间短一些。设计上采用时序逻辑电路,先对 ground 进行赋值,再进行判断,这里面的 if 中一定要采用 ~ground,因为这里判断的是上一个时钟的 ground 值,也就是还没有取非的 ground 的值,所以上一个时钟 ground 要是0,那么下一个时刻就会是1,delay 就多取一些。