导言
HDLBits状态机章节Lemmings1~4是有关于旅鼠的一个系列,解前面3题时没遇到什么阻碍,第4题耽误了一些时间。
- 第一处阻碍是何时进入splatter状态,即计数器何时开始计数:是next_state为下落还是current_state为下落时开始计数?
- 第二处阻碍是本应该进入splatter状态而进入了left状态,波形上表现为:输出不能都保持为0。
原代码
module top_module(
input clk,
input areset, // Freshly brainwashed Lemmings walk left.
input bump_left,
input bump_right,
input ground,
input dig,
output walk_left,
output walk_right,
output aaah,
output digging );
// states definition
parameter L=0,R=1,L_G=2,R_G=3,L_D=4,R_D=5,S=6;
reg [2:0] current_state, next_state;
// states transmission
always@(posedge clk or posedge areset) begin
if(areset) begin
current_state <= L;
end
else begin
current_state <= next_state;
end
end
// counting for splatter state
reg [4:0] cout;
always@(posedge clk or posedge areset) begin
if(areset) begin
cout <= 5'd0;
end
else if( (next_state == L_G) || (next_state == R_G) ) begin
cout <= cout + 1'b1;
end
else begin
cout <= 5'd0;
end
end
// next_state judgement
always@(*) begin
case(current_state)
L: next_state = ~ground ? L_G : ( dig ? L_D : (bump_left ? R : L) );
R: next_state = ~ground ? R_G : ( dig ? R_D : (bump_right ? L : R) );
L_G: next_state = ground ? ( cout > 5'd20 ? S : L ) : L_G;
R_G: next_state = ground ? ( cout > 5'd20 ? S : R ) : R_G;
L_D: next_state = ~ground ? L_G : L_D;
R_D: next_state = ~ground ? R_G : R_D;
S: next_state = S;
endcase
end
// output logic
assign walk_left = (current_state == L);
assign walk_right = (current_state == R);
assign aaah = ((current_state == L_G) || (current_state == R_G));
assign digging = ((current_state == L_D) || (current_state == R_D));
endmodule
上述代码仿真结果总是incorrect,HDLBits仿真只能看到输入输出信号波形,并不能看到内部信号(或许是我打开方式不对=_=)。对比了HDLbits答案更新系列这篇博文和评论,才发现是因为计数器寄存器cout的位宽定义导致的,由[4:0]改成[6:0]后运行结果为success。
原因应该在于:题目中说旅鼠只要不摔到地面上,下落的时间将是无限的。而count的位宽总是有限的,理论上来看这样写就不合适了。
更改后
module top_module(
input clk,
input areset, // Freshly brainwashed Lemmings walk left.
input bump_left,
input bump_right,
input ground,
input dig,
output walk_left,
output walk_right,
output aaah,
output digging );
parameter LEFT=0,RIGHT=1,LEFT_GRD=2,RIGHT_GRD=3,LEFT_DIG=4,RIGHT_DIG=5,SPLAT=6;
reg [2:0] current_state, next_state;
always@(posedge clk or posedge areset) begin
if(areset) begin
current_state <= LEFT;
end
else begin
current_state <= next_state;
end
end
reg [4:0] cout;
reg timeout;
always@(posedge clk or posedge areset) begin
if(areset) begin
cout <= 5'd0;
end
else if( (next_state == LEFT_GRD) || (next_state == RIGHT_GRD) ) begin // same as: ~ground
cout <= cout + 1'b1;
end
else begin
cout <= 5'd0;
end
end
always@(posedge clk or posedge areset) begin
if(areset) begin
timeout <= 1'b0;
end
else if(cout == 5'd20) begin
timeout <= 1'b1;
end
else begin
timeout <= timeout;
end
end
always@(*) begin
case(current_state)
LEFT: next_state = ground ? ( dig ? LEFT_DIG : (bump_left ? RIGHT : LEFT) ):LEFT_GRD;
RIGHT: next_state = ground ? ( dig ? RIGHT_DIG : (bump_right ? LEFT : RIGHT) ):RIGHT_GRD;
LEFT_GRD: next_state = ground ? ( timeout ? SPLAT : LEFT ):LEFT_GRD;
RIGHT_GRD: next_state = ground ? ( timeout ? SPLAT : RIGHT ):RIGHT_GRD;
LEFT_DIG: next_state = ground ? LEFT_DIG : LEFT_GRD;
RIGHT_DIG: next_state = ground ? RIGHT_DIG : RIGHT_GRD;
SPLAT: next_state = SPLAT;
endcase
end
assign walk_left = (current_state == LEFT);
assign walk_right = (current_state == RIGHT);
assign aaah = ((current_state == LEFT_GRD) || (current_state == RIGHT_GRD));
assign digging = ((current_state == LEFT_DIG) || (current_state == RIGHT_DIG));
endmodule
更新后的代码增加了一个叫做timeout的标志位,当时间达到20个cycle以后,该标志位一直保持高电平,只有reset以后才会恢复低电平。仿真结果为success!
- L即LEFT:往左
- R即RIGHT:往右
- L_G即LEFT_GRD:原往左,后下落
- R_G即RIGHT_GRD:原往右,后下落
- D_G即LEFT_GRD:原往左,后下挖
- D_G即RIGHT_GRD:原往右,后下挖
- S即SPLAT:摔到地面
小结
仔细读题是关键!另外,其实可以拿其他仿真工具看一下内部信号是怎么变化的,应该很快能看出问题所在。
第一个阻碍我还没有完全想明白,凭感觉作出的判断,哪位路过的盆友能指教一下就好啦。