//////////////////////////////////////////////////////////////////////////////// // /// LSU EE 4755 Fall 2017 Homework 4 // /// SOLUTION /// Assignment http://www.ece.lsu.edu/koppel/v/2017/hw04.pdf /// Additional Resources // // Verilog Documentation // The Verilog Standard // http://standards.ieee.org/getieee/1800/download/1800-2012.pdf // 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. // // [✔] Make sure that the testbench does not report errors. // [✔] 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 .. // .. instead of .. // .. 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.) // cadence translate_off 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 [char_wid-1:0] char, shadow_last_char; 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; int shadow_mr_len, shadow_mr_char, shadow_cr_len; 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 ); shadow_last_char = char; if ( do_new_char ) char = {$random} & char_mask; reset = do_reset; if ( !do_reset && char === shadow_last_char ) shadow_cr_len++; else shadow_cr_len = 1; if ( do_reset ) shadow_mr_len = 0; if ( shadow_cr_len > shadow_mr_len ) begin shadow_mr_len = shadow_cr_len; shadow_mr_char = char; end @( posedge clk_reactive ); repeat ( 2 ) begin if ( mr ) mr_len = len[0]; else cr_len = len[0]; mr = !mr; #0; #0; end is_err_cr_len = shadow_cr_len !== cr_len; is_err_mr_len = shadow_mr_len !== mr_len; is_err_mr_char = shadow_mr_char !== mr_char[0]; $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 // cadence translate_on