//////////////////////////////////////////////////////////////////////////////// // /// LSU EE 4755 Fall 2016 Homework 2 -- SOLUTION // /// Assignment http://www.ece.lsu.edu/koppel/v/2016/hw02.pdf `default_nettype none ////////////////////////////////////////////////////////////////////////////// /// Problem 1 // /// Modify aa_digit_val so that it works for any radix, not just 10. // // [✔] The code must be synthesizable. // [✔] Make sure that the testbench does not report errors. // [✔] Can use behavioral or implicit structural code. module aa_decimal_digit_val ( output uwire [3:0] val, output uwire is_dig, input uwire [7:0] char ); // Do not edit this module. assign is_dig = char >= "0" && char <= "9"; assign val = is_dig ? char - "0" : 0; endmodule module aa_digit_val #( int radix = 10 ) ( output uwire [3:0] val, output uwire is_dig, input uwire [7:0] char ); /// SOLUTION // Check whether char is in range 0-9 or a-f, regardless of radix. // uwire is_dig_09 = char >= "0" && char <= "9"; uwire is_dig_af = char >= "a" && char <= "f"; // Convert char to binary, assuming that it is hexadecimal. // uwire [3:0] val_raw = is_dig_09 ? char - "0" : char - "a" + 10; // Determine whether char is a valid digit in radix radix. // assign is_dig = ( is_dig_09 || is_dig_af ) && val_raw < radix; assign val = is_dig ? val_raw : 0; endmodule ////////////////////////////////////////////////////////////////////////////// /// Problem 2 // /// Modify aa_full_adder so that it adds two radix-RADIX ASCII-encoded digits. // // [✔] The code must be synthesizable. // [✔] Make sure that the testbench does not report errors. // [✔] Can use behavioral or implicit structural code. module aa_full_adder #( int radix = 10 ) ( output uwire [7:0] sum, output uwire carry_out, output uwire is_dig_out, input uwire [7:0] a, b, input uwire carry_in, input uwire is_dig_in); /// SOLUTION // Instantiate two aa_digit_val modules, connecting one to each // input digit. These will determine whether each character input // is a valid digit and if so provide the binary value of the // digit. // uwire [3:0] val_a, val_b; uwire is_dig_a, is_dig_b; aa_digit_val #(radix) dva(val_a, is_dig_a, a); aa_digit_val #(radix) dvb(val_b, is_dig_b, b); // Compute the sum of carry_in and the binary versions of a and // b. Note that the sum may contain a carry, and so it can not be // assigned to the module output. // uwire [4:0] sum_val = carry_in + val_a + val_b; // Determine whether there is a carry out. // assign carry_out = sum_val >= radix; // Determine the sum, in binary, with the carry removed. // uwire [3:0] sum_dig_val = carry_out ? sum_val - radix : sum_val; // Convert the sum to ASCII or to a blank if we don't have a valid digit. // assign sum = !is_dig_out ? " " : sum_dig_val < 10 ? "0" + sum_dig_val : "a" + sum_dig_val - 10; // If the value of is_dig_out, below, is true then output sum will // be set to a digit of the sum. Otherwise sum should be set to a // blank. The value of is_dig_out will be false when we are past // the last digit of both a and b, and we don't have a carry out // from the previous digit. // assign is_dig_out = is_dig_in && ( carry_in || is_dig_a || is_dig_b ); endmodule module aa_width2 #( int radix = 10 ) ( output uwire [1:0][7:0] sum, output uwire c_out, output uwire is_dig_out, input uwire [1:0][7:0] a, b, input uwire c_in, input uwire is_dig_in); uwire co0, id_0; aa_full_adder #(radix) fa1(sum[0],co0,id_0,a[0],b[0],c_in,is_dig_in); aa_full_adder #(radix) fa2(sum[1],c_out,is_dig_out,a[1],b[1],co0,id_0); endmodule module reference_adder #( int radix = 10, int digits = 2, int width = $clog2( radix ** digits ) ) ( output logic [width-1:0] sum, output logic carry_out, input uwire [width-1:0] a, b, input uwire carry_in ); always_comb { carry_out, sum } = 0 + carry_in + a + b; 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 thoe 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 // Convert integer A into a radix RADIX ASCII representation. function automatic string radtos(int unsigned a, int radix); begin radtos = ""; while ( 1 ) begin automatic int dig = a % radix; if ( radtos.len() > 0 && a == 0 ) break; a = a / radix; radtos = { dig < 10 ? "0" + dig : "a" + dig - 10, radtos }; end end endfunction module aa_test; logic start_done[32]; for ( genvar radix = 2; radix <= 16; radix++ ) aa_test_digit_val #(radix) aa(start_done[radix],start_done[radix-1]); for ( genvar radix = 2; radix <= 16; radix++ ) aa_test_width2 #(radix) aa(start_done[15+radix],start_done[15+radix-1]); initial begin start_done[1] = 1; end endmodule module aa_test_digit_val #( int radix = 10 ) ( output logic done, input uwire start ); localparam int err_limit = 10; logic [7:0] a; uwire [3:0] dval; uwire is_d; aa_digit_val #(radix) dv1(dval,is_d,a); int digit_vals[256]; int num_errs; initial begin num_errs = 0; wait ( start == 1 ); digit_vals = { 256 { -1 } }; for ( int i=0; i<radix; i++ ) digit_vals[ i < 10 ? "0" + i : "a" + i - 10 ] = i; for ( int i=0; i<256; i++ ) begin automatic bit is_d_shadow = digit_vals[i] >= 0 ; #1; a = i; #1; if ( is_d != is_d_shadow ) begin $write ("Error in aa_digit_val for char %c (%0d) radix %0d, is_d %0d != %0d (correct)\n", i, i, radix, is_d, is_d_shadow ); num_errs++; if ( num_errs > err_limit ) $finish(2); continue; end if ( is_d_shadow && dval != digit_vals[i] ) begin $write ("Error in aa_digit_val for char %c (%0d) radix %0d: val %0d != %0d (correct)\n", i, i, radix, dval, digit_vals[i]); num_errs++; if ( num_errs > err_limit ) $finish(2); continue; end end done = 1; end endmodule module aa_test_width2 #( int radix = 10 ) ( output logic done, input uwire start ); localparam int err_limit = 10; localparam int max_digits = 2; localparam int max_dno = max_digits - 1; localparam int num_tests = 100; uwire [max_dno:0][7:0] sum; logic [max_dno:0][7:0] a, b, shadow_sum; uwire co; logic fo, ci, fi; aa_width2 #(radix) fa1(sum[1:0],co,fo,a[1:0],b[1:0],ci,fi); int unsigned aval, bval, ssum_val; int num_errs; initial begin num_errs = 0; wait ( start == 1 ); for ( int i=0; i<num_tests; i++ ) begin automatic int width = max_digits; aval = {$random()} >> 1; bval = {$random()} >> 1; ssum_val = aval + bval; a = radtos(aval,radix); b = radtos(bval,radix); shadow_sum = radtos(ssum_val,radix); ci = 0; fi = 1; #1; if ( sum !== shadow_sum ) begin $write("Error %s + %s != %s (%s correct).\n", a,b,sum,shadow_sum); num_errs++; if ( num_errs > err_limit ) $finish(2); end #1; end done = 1; end endmodule // cadence translate_on