```////////////////////////////////////////////////////////////////////////////////
//
/// LSU EE 4755 Fall 2017 Homework 4
//
/// SOLUTION

/// Assignment  http://www.ece.lsu.edu/koppel/v/2017/hw04.pdf

//
// Verilog Documentation
//    The Verilog Standard
//    Introductory Treatment (Warning: Does not include SystemVerilog)
//      Brown & Vranesic, Fundamentals of Digital Logic with Verilog, 3rd Ed.
//
// Account Setup and Emacs (Text Editor) Instructions
//      http://www.ece.lsu.edu/koppel/v/proc.html
//      To learn Emacs look for Emacs tutorial.

`default_nettype none

//////////////////////////////////////////////////////////////////////////////
///  Problem 1
//
/// Modify maxrun so that it keeps track of the current and maximum runs.
//
//     [✔] Module must be synthesizable.
//     [✔] Code must be reasonably efficient.

/// Solution 1: Written for maximum code clarity.
//
module maxrun
#( int w = 2,
int c = 4 )
( output uwire [w-1:0] len,
output logic [c-1:0] mr_char,
input uwire clk, reset, mr,
input uwire [c-1:0] in_char );

logic [w-1:0] cr_len, mr_len;
logic [c-1:0] prev_char;

assign len = mr ? mr_len : cr_len;

always_ff @( posedge clk ) begin

if ( reset ) mr_len = 0;

if ( !reset && in_char == prev_char )
cr_len++;
else
cr_len = 1;

if ( cr_len > mr_len )
begin
mr_len = cr_len;
mr_char = in_char;
end

prev_char = in_char;

end

endmodule

/// Solution 2: Written for high performance.
//
module maxrun_opt
#( int w = 2,
int c = 4 )
( output uwire [w-1:0] len,
output logic [c-1:0] mr_char,
input uwire clk, reset, mr,
input uwire [c-1:0] in_char );

logic [w-1:0] cr_len, mr_len;
logic [c-1:0] prev_char;

assign len = mr ? mr_len : cr_len;

always_ff @( posedge clk ) begin

logic match;
match = in_char == prev_char;

/// Approach to Reducing Critical Path
//
//   To keep addition off the critical path ..
//   .. check cr_len >= mr_len && match ..
//   .. rather than using incremented cr_len for: cr_len > mr_len.
//
//   Based on experimentation, use ..
//   .. cr_len >= mr_len ..
//   .. cr_len == mr_len ..
//   .. even though cr_len == mr_len is easier to compute.

if ( reset ) begin
mr_len = 1;
mr_char = in_char;
end else if ( cr_len >= mr_len && match ) begin
mr_len = cr_len + 1;
mr_char = in_char;
end

if ( !reset && match )
cr_len = cr_len + 1;
else
cr_len = 1;

prev_char = in_char;

end

endmodule

//////////////////////////////////////////////////////////////////////////////
/// Testbench Code
//
//  The code below instantiates some of the modules above,
//  provides test inputs, and verifies the outputs.
//
//  The testbench may be modified to facilitate your solution. Of
//  course, the removal of tests which your module fails is not a
//  method of fixing a broken module. (One might modify the testbench
//  so that the first tests it performs are those which make it easier
//  to determine what the problem is, for example, test inputs that
//  are all 0's or all 1's.)

program reactivate
(output uwire clk_reactive, output int cycle_reactive,
input uwire clk, input var int cycle);
assign clk_reactive = clk;
assign cycle_reactive = cycle;
endprogram

module testbench;

localparam int char_wid = 8;
localparam int count_wid = 10;

localparam int test_num_chars = 100;
localparam int cycle_limit = test_num_chars + 20;

localparam int nmuts = 1;

localparam int char_mask = ( 1 << char_wid ) - 1;

uwire [count_wid-1:0] len[nmuts];
uwire [char_wid-1:0] mr_char[nmuts];
logic mr;

logic clock, reset;
bit done;
int cycle;

logic clk_reactive;
int cycle_reactive;
reactivate ra(clk_reactive,cycle_reactive,clock,cycle);

initial begin
clock = 0;
cycle = 0;

fork
forever #10 cycle += clock++;
wait( done );
wait( cycle >= cycle_limit )
\$write("*** Cycle limit exceeded, ending.\n");
join_any;

\$finish();
end

maxrun_opt #(count_wid,char_wid) mr1 (len[0],mr_char[0],clock,reset,mr,char);

initial begin

automatic int n_err_cr_len = 0, n_err_mr_len = 0, n_err_mr_char = 0;
bit is_err_cr_len, is_err_mr_len, is_err_mr_char;

done = 0;
reset = 0;
char = 0;
mr = 0;

@( posedge clk_reactive );

for ( int i=0; i<test_num_chars; i++ ) begin

automatic bit do_reset = i == 0 || {\$random} % 10 == 0;
automatic bit do_new_char = {\$random} % 3 == 0;
logic [count_wid-1:0] mr_len, cr_len;

@( negedge clock );

if ( do_new_char ) char = {\$random} & char_mask;
reset = do_reset;

if ( !do_reset && char === shadow_last_char )
else

if ( do_reset )

end

@( posedge clk_reactive );

repeat ( 2 ) begin
if ( mr ) mr_len = len[0]; else cr_len = len[0];
mr = !mr;
#0; #0;
end

\$write
("%5d %1s c=%2x    cr_len %3d %s    mr_len %3d %s    mr_c %2x %s\n",
i, do_reset ? "r" : " ", char,
cr_len,
is_err_cr_len ? \$sformatf("!= %3d", shadow_cr_len) : "ok    ",
mr_len,
is_err_mr_len ? \$sformatf("!= %3d", shadow_mr_len) : "ok    ",
mr_char[0],
is_err_mr_char ? \$sformatf("!= %2x", shadow_mr_char) : "ok   " );

if ( shadow_cr_len !== cr_len ) n_err_cr_len++;
if ( shadow_mr_len !== mr_len ) n_err_mr_len++;
if ( shadow_mr_char !== mr_char[0] ) n_err_mr_char++;

end

\$write("Done with %0d tests, %0d %0d %0d errors found.\n",
test_num_chars,
n_err_cr_len,
n_err_mr_len,
n_err_mr_char);

done = 1;

end

endmodule