```////////////////////////////////////////////////////////////////////////////////
//
/// LSU EE 4755 Fall 2021 Homework 1
/// SOLUTION

/// Assignment  https://www.ece.lsu.edu/koppel/v/2021/hw01.pdf
/// Solution Discussion  https://www.ece.lsu.edu/koppel/v/2021/hw01_sol.pdf

`default_nettype none

//////////////////////////////////////////////////////////////////////////////
///  Problem 1
//
///   Complete insert_at so that output o is set to ia with ib inserted at pos.
///
//
//     [✔] Module must be synthesizable. Use command: genus -files syn.tcl
//
//     [✔] Do not use procedural code.
//     [✔] Do not use the << or >> operators (or anything similar).
//     [✔] Use the shift and mask modules to provide shifted values
//
//     [✔] Don't assume any particular parameter value.
//
//     [✔] Code must be written clearly.
//     [✔] Pay attention to cost and performance.

module insert_at
#( int wa = 20, wb = 10, wo = wa+wb, walg = \$clog2(wa+1) )
( output logic [wo-1:0] o,
input uwire [wa-1:0] ia,
input uwire [wb-1:0] ib,
input uwire [walg-1:0] pos );

/// SOLUTION

/// :Example: Input Values:
//
//  ia =              aaaaaaaa   (Each a is a bit of ia, it can be 0 or 1.)
//  ib =                   bbb   (Each b is a bit of ib, it can be 0 or 1.)
//  pos = 2
//
/// Desired Output Value
//
//  o      =       aaaaaabbbaa   (Notice that ib is inserted at pos 2.)

uwire [wa-1:0] ia_low = ia & mask_low;
uwire [wa-1:0] ia_high_low = ia & ~mask_low;

//  ia =              aaaaaaaa
//  mask_low =        00000011  (Two low bits are 1 because pos=2.)
//  ia_low =          000000aa  (ia_low has the bits to the right of pos.)
//  ia_high_low =     aaaaaa00  (ia_high_low: the bits to the left of pos.)

localparam int wblg = \$clog2(wb);
uwire [wo-1:0] ia_high;
shift_left #(wa,wo,wblg) slc( ia_high, ia_high_low, wblg'(wb) );

//  ia_high_low =     aaaaaa00
//  ia_high =      aaaaaa00000 (Shift wb bits to make room for ib.)

uwire [wo-1:0] ib_at_pos;
shift_left #(wb,wo,walg) slb( ib_at_pos, ib, pos );

//  ib =                   bbb
//  ib_at_pos =    000000bbb00 (Shifted pos bits, and widened to wo bits.)

assign o = ia_high | ib_at_pos | ia_low;

//  ia_high =      aaaaaa00000
//  ib_at_pos =    000000bbb00
//  ia_low =          000000aa
//  o      =       aaaaaabbbaa

endmodule

module shift_left
#( int wi = 4, wo = wi, wolg = \$clog2(wo) )
( output uwire [wo-1:0] o,
input uwire [wi-1:0] i,
input uwire [wolg-1:0] amt );
assign o = i << amt;
endmodule

module shift_right
#( int wi = 4, wo = wi, wolg = \$clog2(wo) )
( output uwire [wo-1:0] o,
input uwire [wi-1:0] i,
input uwire [wolg-1:0] amt );
assign o = i >> amt;
endmodule

#( int wo = 6, wp = \$clog2(wo+1) )
( output logic [wo-1:0] o, input uwire [wp-1:0] n1 );
always_comb for ( int i=0; i<wo; i++ ) o[i] = i < n1;
endmodule

#( int wo = 6, wp = \$clog2(wo+1) )
( output logic [wo-1:0] o, input uwire [wp-1:0] n1 );
always_comb for ( int i=0; i<wo; i++ ) o[wo-i-1] = i < n1;
endmodule

//////////////////////////////////////////////////////////////////////////////
/// Testbench Code

module testbench;

logic done [1:-1];
initial done[-1] = 1;

testbench_size #(8,3, "Set 1") tb1(done[0],done[-1]);
testbench_size #(4,5, "Set 2") tb2(done[1],done[0]);

endmodule

module testbench_size
#( int wa = 8, int wb = 3, string label = "set me" )
( output logic done_me,
input uwire logic done_pred );

localparam int wo = wa+wb;
localparam int walg = \$clog2(wa+1);

localparam int n_tests = (wa+1) * 3;

logic [wa-1:0] ia;
logic [wb-1:0] ib;
uwire [wo-1:0] o;
logic [walg-1:0] pos;

insert_at #(wa,wb) iat(o, ia, ib, pos);

initial begin

automatic int n_err = 0;

wait ( done_pred === 1 );

for ( int tn = 0; tn < n_tests; tn++ ) begin

automatic int rnd = tn / (wa+1);

pos = tn % (wa+1);
case ( rnd )
0: begin ia = -1; ib = 0; end
1: begin ia = 0;  ib = -1; end
default: {ia,ib} = {\$random};
endcase

#1;

for ( int i=0; i<pos; i++ ) o_shadow[i] = ia[i];
for ( int i=0; i<wb; i++ ) o_shadow[i+pos] = ib[i];
for ( int i=pos; i<wa; i++ ) o_shadow[i+wb] = ia[i];

if ( o_shadow !== o ) begin
n_err++;
if ( n_err < 6 )
\$write("Error %s for ia=%b  ib=%b  pos=%d  %b != %b (correct)\n",
label,
end

end

\$write("For %s, done with %0d tests, %0d errors found.\n",
label, n_tests, n_err);

done_me = 1;

end

endmodule