The game Lemmings involves critters with fairly simple brains. So simple
that we are going to model it using a finite state machine. In the
Lemmings' 2D world, Lemmings can be in one of two states: walking left
(walk_left is 1) or walking right (walk_right is 1). It will switch
directions if it hits an obstacle. In particular, if a Lemming is bumped
on the left (by receiving a 1 on bump_left), it will walk right. If it's
bumped on the right (by receiving a 1 on bump_right), it will walk left.
If it's bumped on both sides at the same time, it will still switch
directions.

In addition to walking left and right and changing direction when bumped,
when ground=0, the Lemming will fall and say ""aaah!"". When the ground
reappears (ground=1), the Lemming will resume walking in the same
direction as before the fall. Being bumped while falling does not affect
the walking direction, and being bumped in the same cycle as ground
disappears (but not yet falling), or when the ground reappears while
still falling, also does not affect the walking direction.

In addition to walking and falling, Lemmings can sometimes be told to do
useful things, like dig (it starts digging when dig=1). A Lemming can dig
if it is currently walking on ground (ground=1 and not falling), and will
continue digging until it reaches the other side (ground=0). At that
point, since there is no ground, it will fall (aaah!), then continue
walking in its original direction once it hits ground again. As with
falling, being bumped while digging has no effect, and being told to dig
when falling or when there is no ground is ignored. (In other words, a
walking Lemming can fall, dig, or switch directions. If more than one of
these conditions are satisfied, fall has higher precedence than dig,
which has higher precedence than switching directions.)

Although Lemmings can walk, fall, and dig, Lemmings aren't invulnerable.
If a Lemming falls for too long then hits the ground, it can splatter. In
particular, if a Lemming falls for more than 20 clock cycles then hits
the ground, it will splatter and cease walking, falling, or digging (all
4 outputs become 0), forever (Or until the FSM gets reset). There is no
upper limit on how far a Lemming can fall before hitting the ground.
Lemmings only splatter when hitting the ground; they do not splatter in
mid-air.

Implement a Moore state machine that models this behaviour. areset is
positive edge triggered asynchronous reseting the Lemming machine to walk
left.

module TopModule (
  input clk,
  input areset,
  input bump_left,
  input bump_right,
  input ground,
  input dig,
  output walk_left,
  output walk_right,
  output aaah,
  output digging
);


Reference solution:

module RefModule (
  input clk,
  input areset,
  input bump_left,
  input bump_right,
  input ground,
  input dig,
  output walk_left,
  output walk_right,
  output aaah,
  output digging
);

  parameter WL=0, WR=1, FALLL=2, FALLR=3, DIGL=4, DIGR=5, DEAD=6;
  reg [2:0] state;
  reg [2:0] next;

  reg [4:0] fall_counter;

  always_comb begin
    case (state)
      WL: if (!ground) next = FALLL;
        else if (dig) next = DIGL;
        else if (bump_left) next = WR;
        else next = WL;
      WR:
        if (!ground) next = FALLR;
        else if (dig) next = DIGR;
        else if (bump_right) next = WL;
        else next = WR;
      FALLL: next = ground ? (fall_counter >= 20 ? DEAD : WL) : FALLL;
      FALLR: next = ground ? (fall_counter >= 20 ? DEAD : WR) : FALLR;
      DIGL: next = ground ? DIGL : FALLL;
      DIGR: next = ground ? DIGR : FALLR;
      DEAD: next = DEAD;
    endcase
  end

  always @(posedge clk, posedge areset) begin
    if (areset) state <= WL;
      else state <= next;
  end

  always @(posedge clk) begin
    if (state == FALLL || state == FALLR) begin
      if (fall_counter < 20)
        fall_counter <= fall_counter + 1'b1;
    end
    else
      fall_counter <= 0;
  end

  assign walk_left = (state==WL);
  assign walk_right = (state==WR);
  assign aaah = (state == FALLL) || (state == FALLR);
  assign digging = (state == DIGL) || (state == DIGR);

endmodule


Testbench:
`timescale 1 ps/1 ps
`define OK 12
`define INCORRECT 13


module stimulus_gen (
	input clk,
	output logic areset,
	output logic bump_left,
	output logic bump_right,
	output logic dig,
	output logic ground,
	output reg[511:0] wavedrom_title,
	output reg wavedrom_enable,
	input tb_match
);

	reg reset;
	assign areset = reset;

	task reset_test(input async=0);
		bit arfail, srfail, datafail;
	
		@(posedge clk);
		@(posedge clk) reset <= 0;
		repeat(3) @(posedge clk);
	
		@(negedge clk) begin datafail = !tb_match ; reset <= 1; end
		@(posedge clk) arfail = !tb_match;
		@(posedge clk) begin
			srfail = !tb_match;
			reset <= 0;
		end
		if (srfail)
			$display("Hint: Your reset doesn't seem to be working.");
		else if (arfail && (async || !datafail))
			$display("Hint: Your reset should be %0s, but doesn't appear to be.", async ? "asynchronous" : "synchronous");
		// Don't warn about synchronous reset if the half-cycle before is already wrong. It's more likely
		// a functionality error than the reset being implemented asynchronously.
	
	endtask


// Add two ports to module stimulus_gen:
//    output [511:0] wavedrom_title
//    output reg wavedrom_enable

	task wavedrom_start(input[511:0] title = "");
	endtask
	
	task wavedrom_stop;
		#1;
	endtask	



	initial begin
		reset <= 1'b1;
		@(posedge clk);
		reset <= 0;
		{bump_left, bump_right, ground, dig} <= 2;
		repeat(2) @(posedge clk);
		{bump_left, bump_right, ground, dig} <= 3;
		@(posedge clk);
		{bump_left, bump_right, ground, dig} <= 2;
		@(posedge clk);
		{bump_left, bump_right, ground, dig} <= 10;
		@(posedge clk);
		{bump_left, bump_right, ground, dig} <= 2;
		@(posedge clk);
		{bump_left, bump_right, ground, dig} <= 0;
		repeat(3) @(posedge clk);
		{bump_left, bump_right, ground, dig} <= 3;
		@(posedge clk);
		{bump_left, bump_right, ground, dig} <= 2;
		repeat(4) @(posedge clk);
		
		{bump_left, bump_right, ground, dig} <= 0;		// Fall
		repeat(20) @(posedge clk);
		{bump_left, bump_right, ground, dig} <= 2;		// Survive
		repeat(1) @(posedge clk);

		{bump_left, bump_right, ground, dig} <= 0;		// Fall
		repeat(21) @(posedge clk);
		{bump_left, bump_right, ground, dig} <= 2;		// Splat after falling left
		repeat(20) @(posedge clk) begin
			{dig, bump_right, bump_left} <= $random & $random;	// See if it's handled correctly.
			ground <= |($random & 7);
		end
			

		reset <= 1;
		{bump_left, bump_right, ground, dig} <= 2;		// Normal
		@(posedge clk)
		reset <= 0;		// Resurrect.
		bump_left <= 1;
		repeat(5) @(posedge clk);
		{bump_left, bump_right, ground, dig} <= 0;		// Fall
		repeat(21) @(posedge clk);
		{bump_left, bump_right, ground, dig} <= 2;		// Splat after falling right
		repeat(20) @(posedge clk) begin
			{dig, bump_right, bump_left} <= $random & $random;	// See if it's handled correctly.
			ground <= |($random & 7);
		end

		reset <= 1;
		@(posedge clk)
		reset <= 0;		// Resurrect.
		{bump_left, bump_right, ground, dig} <= 2;		// Normal
		wavedrom_start("Splat?");
		@(posedge clk);
		{bump_left, bump_right, ground, dig} <= 0;		// Fall
		repeat(24) @(posedge clk);
		{bump_left, bump_right, ground, dig} <= 2;		// Splat? (24-cycles)
		repeat(2) @(posedge clk);
		wavedrom_stop();
		
		reset <= 1;
		@(posedge clk)
		reset <= 0;		// Resurrect.
		{bump_left, bump_right, ground, dig} <= 2;		// Normal
		@(posedge clk);
		@(posedge clk);
		{bump_left, bump_right, ground, dig} <= 0;		// Fall
		repeat(35) @(posedge clk);
		{bump_left, bump_right, ground, dig} <= 2;		// Splat? (Test for 5-bit non-saturating counter)
		repeat(2) @(posedge clk);
		repeat(20) @(posedge clk) begin
			{dig, bump_right, bump_left} <= $random & $random;	// See if it's handled correctly.
			ground <= |($random & 7);
		end		

		reset <= 1;
		{bump_left, bump_right, ground, dig} <= 2;		// Normal
		@(posedge clk);
		reset <= 0;		// Resurrect.
		@(posedge clk);
		{bump_left, bump_right, ground, dig} <= 0;		// Fall
		repeat(67) @(posedge clk);
		{bump_left, bump_right, ground, dig} <= 2;		// Splat? (Test for 6-bit non-saturating counter)
		repeat(20) @(posedge clk) begin
			{dig, bump_right, bump_left} <= $random & $random;	// See if it's handled correctly.
			ground <= |($random & 7);
		end
		
		reset <= 1;
		{bump_left, bump_right, ground, dig} <= 2;		// Normal
		@(posedge clk)
		reset <= 0;		// Resurrect.
		
		
		repeat(400) @(posedge clk, negedge clk) begin
			{dig, bump_right, bump_left} <= $random & $random;
			ground <= |($random & 7);
			reset <= !($random & 31);
		end

		#1 $finish;
	end
	
endmodule

module tb();

	typedef struct packed {
		int errors;
		int errortime;
		int errors_walk_left;
		int errortime_walk_left;
		int errors_walk_right;
		int errortime_walk_right;
		int errors_aaah;
		int errortime_aaah;
		int errors_digging;
		int errortime_digging;

		int clocks;
	} stats;
	
	stats stats1;
	
	
	wire[511:0] wavedrom_title;
	wire wavedrom_enable;
	int wavedrom_hide_after_time;
	
	reg clk=0;
	initial forever
		#5 clk = ~clk;

	logic areset;
	logic bump_left;
	logic bump_right;
	logic ground;
	logic dig;
	logic walk_left_ref;
	logic walk_left_dut;
	logic walk_right_ref;
	logic walk_right_dut;
	logic aaah_ref;
	logic aaah_dut;
	logic digging_ref;
	logic digging_dut;

	initial begin 
		$dumpfile("wave.vcd");
		$dumpvars(1, stim1.clk, tb_mismatch ,clk,areset,bump_left,bump_right,ground,dig,walk_left_ref,walk_left_dut,walk_right_ref,walk_right_dut,aaah_ref,aaah_dut,digging_ref,digging_dut );
	end


	wire tb_match;		// Verification
	wire tb_mismatch = ~tb_match;
	
	stimulus_gen stim1 (
		.clk,
		.* ,
		.areset,
		.bump_left,
		.bump_right,
		.ground,
		.dig );
	RefModule good1 (
		.clk,
		.areset,
		.bump_left,
		.bump_right,
		.ground,
		.dig,
		.walk_left(walk_left_ref),
		.walk_right(walk_right_ref),
		.aaah(aaah_ref),
		.digging(digging_ref) );
		
	TopModule top_module1 (
		.clk,
		.areset,
		.bump_left,
		.bump_right,
		.ground,
		.dig,
		.walk_left(walk_left_dut),
		.walk_right(walk_right_dut),
		.aaah(aaah_dut),
		.digging(digging_dut) );

	
	bit strobe = 0;
	task wait_for_end_of_timestep;
		repeat(5) begin
			strobe <= !strobe;  // Try to delay until the very end of the time step.
			@(strobe);
		end
	endtask	

	
	final begin
		if (stats1.errors_walk_left) $display("Hint: Output '%s' has %0d mismatches. First mismatch occurred at time %0d.", "walk_left", stats1.errors_walk_left, stats1.errortime_walk_left);
		else $display("Hint: Output '%s' has no mismatches.", "walk_left");
		if (stats1.errors_walk_right) $display("Hint: Output '%s' has %0d mismatches. First mismatch occurred at time %0d.", "walk_right", stats1.errors_walk_right, stats1.errortime_walk_right);
		else $display("Hint: Output '%s' has no mismatches.", "walk_right");
		if (stats1.errors_aaah) $display("Hint: Output '%s' has %0d mismatches. First mismatch occurred at time %0d.", "aaah", stats1.errors_aaah, stats1.errortime_aaah);
		else $display("Hint: Output '%s' has no mismatches.", "aaah");
		if (stats1.errors_digging) $display("Hint: Output '%s' has %0d mismatches. First mismatch occurred at time %0d.", "digging", stats1.errors_digging, stats1.errortime_digging);
		else $display("Hint: Output '%s' has no mismatches.", "digging");

		$display("Hint: Total mismatched samples is %1d out of %1d samples\n", stats1.errors, stats1.clocks);
		$display("Simulation finished at %0d ps", $time);
		$display("Mismatches: %1d in %1d samples", stats1.errors, stats1.clocks);
	end
	
	// Verification: XORs on the right makes any X in good_vector match anything, but X in dut_vector will only match X.
	assign tb_match = ( { walk_left_ref, walk_right_ref, aaah_ref, digging_ref } === ( { walk_left_ref, walk_right_ref, aaah_ref, digging_ref } ^ { walk_left_dut, walk_right_dut, aaah_dut, digging_dut } ^ { walk_left_ref, walk_right_ref, aaah_ref, digging_ref } ) );
	// Use explicit sensitivity list here. @(*) causes NetProc::nex_input() to be called when trying to compute
	// the sensitivity list of the @(strobe) process, which isn't implemented.
	always @(posedge clk, negedge clk) begin

		stats1.clocks++;
		if (!tb_match) begin
			if (stats1.errors == 0) stats1.errortime = $time;
			stats1.errors++;
		end
		if (walk_left_ref !== ( walk_left_ref ^ walk_left_dut ^ walk_left_ref ))
		begin if (stats1.errors_walk_left == 0) stats1.errortime_walk_left = $time;
			stats1.errors_walk_left = stats1.errors_walk_left+1'b1; end
		if (walk_right_ref !== ( walk_right_ref ^ walk_right_dut ^ walk_right_ref ))
		begin if (stats1.errors_walk_right == 0) stats1.errortime_walk_right = $time;
			stats1.errors_walk_right = stats1.errors_walk_right+1'b1; end
		if (aaah_ref !== ( aaah_ref ^ aaah_dut ^ aaah_ref ))
		begin if (stats1.errors_aaah == 0) stats1.errortime_aaah = $time;
			stats1.errors_aaah = stats1.errors_aaah+1'b1; end
		if (digging_ref !== ( digging_ref ^ digging_dut ^ digging_ref ))
		begin if (stats1.errors_digging == 0) stats1.errortime_digging = $time;
			stats1.errors_digging = stats1.errors_digging+1'b1; end

	end

   // add timeout after 100K cycles
   initial begin
     #1000000
     $display("TIMEOUT");
     $finish();
   end

endmodule