Verilog:HDLBITS Lemmings2 仿真成功代码+自己另外写的测试代码

题目:

首先沿用了 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 就多取一些。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王bf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值