Applications 3: Diferență între versiuni

De la WikiLabs
Jump to navigationJump to search
Fără descriere a modificării
 
(Nu s-au afișat 19 versiuni intermediare efectuate de același utilizator)
Linia 1: Linia 1:
Behavioral descriptions, continuous assignment, conditional instructions, use of blocks '''always''' for describing combinational circuits.
Many ways to describe a module:
 
* ''Behavioral'' description: Uses one or more '''always''' blocks that encloses procedural statements;
Structural descriptions, logic gates, verilog gate primitives.
* ''Data-flow'' description: employs parallel continuous assignments ('''assign'''), usually with logic operators;
 
* ''Structural'' description: sub-blocks (module instances, gate primitives) connected together;
Exhaustive verification. Loop statements in testbenches.
* ''Mixed'' description: combines two or all of the above kinds of description;


== Exercise 1 ==
== Exercise 1 ==
=== Half adder - gate-level description ===


The half-adder is the simplest, 1-bit, adder. It implements the addition of two bits.
Mux 2:1 (two to one multiplexer) is the simplest selection logic circuit. It selects between two inputs, in0 and in1, according to the value of the selection input, s:


{| class="wikitable" style="text-align: center;
{| class="wikitable" style="text-align: center;
! scope="col"| a
! scope="col"| s
! scope="col"| b
! scope="col"| y
! scope="col"| c
|-
|-
|0 ||0 || 0 0
|0 ||'''in0'''
|-
|-
|0 ||1 || 0 1
|1 ||'''in1'''
|-
|1 ||0 || 0 1
|-
|1 ||1 || 1 0
|}
|}
[[Fișier: Mux2.png]]


[[Fișier: app2_sum.png]]


Use data flow description (a simpler kind of behavioral description that uses logic expressions).
=== 2 to 1 multiplexer, data-flow description ===
 
Use one or more continuous assignments. A ''continuous assignment'' is a single assignment statement attached to an '''assign''' keyword. A continuous assignment updates the value of the RHS variable whenever any variable from the RHS expression changes. The RHS expressions is a logic expression, an expression that employs logic operators.
Logic expressions employ only logic operators.
Logic expressions employ only logic operators.


Linia 32: Linia 28:
! scope="col"| operator
! scope="col"| operator
! scope="col"| function
! scope="col"| function
! scope="col"| example
|-
|-
|~ ||not
|~ ||not||~a
|-
|-
|& ||and
|& ||and|| a & b
|-
|-
| | ||or
| | ||or|| a | b
|-
|-
|^ ||xor
|^ ||xor|| a ^ b
|}
|}


One '''assign''' instruction for each output. Concurrent assignments (in simulation they are evaluated in parallel, mimicking the parallelism of the real circuits)
<syntaxhighlight lang="Verilog">
module mux2(
    input s,
    input [1:0] in,
    output y
);
 
// dataflow description
assign y = (s & in[1]) | (~s & in[0]);
 
endmodule
</syntaxhighlight>
 
'''Note:''' The variable whose value is updated through a continuous assignment should be declared of type '''wire'''.
 
 
=== 2 to 1 multiplexer, behavioral description ===
 
Uses one or more '''always''' blocks. The template of an '''always''' block is:
 
'''always @('''''sensitivity list''''') begin'''<br />
&nbsp;&nbsp;''block of statements''<br />
'''end'''
 
For combinational circuits the safest way to declare the sensitivity list is to use the wildcard ('''*''').
It stands for all variables that appear inside RHS expressions of the '''always''' block.
The statements employed may be assignments, conditional assignments and procedural statements ('''if''', '''case''').
The '''always''' block of statements is executed whenever any of the variables from the sensitivity list changes its value. The statements are executed in the exact order in which they appear inside the '''always''' block.


<syntaxhighlight lang="Verilog">
<syntaxhighlight lang="Verilog">
assign c[0] = a ^ b;
module mux2(
assign c[1] = a & b;
    input s,
    input [1:0] in,
    output y
);
 
// behavioral description with if-else
always @(*) begin
    if(s)
        y = in[1];
    else
        y = in[0];
end
 
endmodule
</syntaxhighlight>
</syntaxhighlight>


An ''exahaustive test'' is a test that generates all possible combinations of values for the inputs of the dut.
'''Note:''' Any variable that gets its value through assignments inside an '''always''' block must be declared of type '''reg'''.
Write a testbench for the half-adder that uses a '''repeat''' statement to generate all input combinations, and a concatenation operator that makes possible to update two or more variables at the same time.
If the variable is also an output of the module, the output is declared as <syntaxhighlight lang="Verilog" inline>output reg</syntaxhighlight>.
 
'''Note:''' Bit selection operator ('''[ ]''') is used to select a bit (or a couple of consecutive bits) from a multibit variable. '''in[0]''' is bit 0 from the input vector '''in'''.
 
 
=== 2 to 1 multiplexer, alternative behavioral description ===
 
The same module may be described using different procedural statements. Some descriptions may be higher-level than others. Usually, the higher the level of description, the shorter is the code.
 
The codes below show three alternative behavioral descriptions for the above multiplexer:


<syntaxhighlight lang="Verilog">
<syntaxhighlight lang="Verilog">
initial begin
// behavioral description with case
     {a, b} = 0;
always @(*) begin
     repeat (4) #1 {a, b} = {a, b} + 1;
     case(s)
    0: y = in[0];
     1: y = in[1];
    endcase
end
</syntaxhighlight>
 
 
<syntaxhighlight lang="Verilog">
// behavioral description with conditional assignment
always @(*) begin
    y = s ? in[1] : in[0];  
end
end
</syntaxhighlight>
</syntaxhighlight>


Some remarks:
* There is no increment operator in verilog.
* Adding 1 to 3 on 2 bits turns around to 0.
* A delay may precede a statement.


== Exercise 2 ==
<syntaxhighlight lang="Verilog">
=== 1-bit half adder - structural description ===
// behavioral description with vector selection
always @(*) begin
    y = in[s];
end
</syntaxhighlight>
 
'''Note:''' The last two alternatives may be also employed in continuous assignments, because they are single statements. For example vector selection may be used inside an '''assign''' instead of an '''always''': <syntaxhighlight lang="Verilog" inline>assign y = in[s];</syntaxhighlight>.


Work on the same project as that of Exercise 1. You need only to modify the description of the half-adder module.


'''Gate-level description''' = a structural description that uses only logic gates.
=== 2 to 1 multiplexer, structural description: gate-level ===


Verilog gate primitives: '''not''', '''and''', '''nand''', '''or''', '''nor''', '''xor''' and '''xnor'''.
The ''gate-level description'' is a structural description that uses only logic gates. Verilog language offers predefined logic gates that may be instantiated in structural descriptions. The verilog gate primitives are '''not''', '''and''', '''nand''', '''or''', '''nor''', '''xor''', and '''xnor'''.
Except for the '''not''' gate, all other primitive gates may have 2, 3 or more inputs.
Except for the '''not''' gate, all other primitive gates may have 2, 3 or more inputs.


Linia 80: Linia 138:
<syntaxhighlight lang="Verilog" inline>nand g2(dout, d1, d2);</syntaxhighlight>  is a '''nand''' gate with dout at output and two wires, d1 and d2, connected to its inputs.
<syntaxhighlight lang="Verilog" inline>nand g2(dout, d1, d2);</syntaxhighlight>  is a '''nand''' gate with dout at output and two wires, d1 and d2, connected to its inputs.


<syntaxhighlight lang="Verilog" inline>nand g3(dout, d1, d2, d3, d4);</syntaxhighlight>  is a '''nand''' gate with dout at output and for inputs, connected to d1, d2, d3 and d4.
<syntaxhighlight lang="Verilog" inline>nand g3(dout, d1, d2, d3, d4);</syntaxhighlight>  is a '''nand''' gate with dout at output and four inputs, connected to d1, d2, d3 and d4.
 
[[Fișier: Mux2a.png]]
 
<syntaxhighlight lang="Verilog">
module mux2(
    input s,
    input [1:0] in,
    output y
);
 
wire s_n;
wire w1;
wire w0;
 
// gate-level structural description
not (s_n, s);
and (w1, in[1], s);
and (w0, in[0], s_n);
or  (y, w1, w0);
 
endmodule
</syntaxhighlight>
 
 
=== 2 to 1 multiplexer, FPGA implementation ===
 
Implement one of the previous multiplexer descriptions using 2 buttons (BTN0  and BTN1 ) for the inputs in0 and in1, one switch (SW0) for selection, and one LED (LED0) for the multiplexer output (y):
 
[[Fișier: Dic_lab3_mux_fpga.png]]


[[Fișier: half_adder.png]]
The constraints file sets the connections between the mux ports and the FPGA pins.
 
<syntaxhighlight>
##Switches
set_property -dict { PACKAGE_PIN V2  IOSTANDARD LVCMOS33 } [get_ports { s }]; #SW 0


Using gate primitives, a possible structural description of the half-adder may be:
##Buttons
set_property -dict { PACKAGE_PIN J2  IOSTANDARD LVCMOS33 } [get_ports { in[0] }]; #BTN 0
set_property -dict { PACKAGE_PIN J5  IOSTANDARD LVCMOS33 } [get_ports { in[1] }]; #BTN 1


<syntaxhighlight lang="Verilog">
##LEDs
xor g1(c[0], a, b);
set_property -dict { PACKAGE_PIN G1  IOSTANDARD LVCMOS33 } [get_ports { y }]; #LED 0
and g2(c[1], a, b);
</syntaxhighlight>
</syntaxhighlight>


Recompile the design file (from the simulator), and restart simulation.


== Exercise 3 ==
== Exercise 2 ==
=== 2 to 1 multiplexer, behavioral description ===


The simplest selection logic circuit. It selects between two inputs, in0 and in1, based on the value of the selection input, s:
Write a testbench for the 2:1 multiplexer, using verilog statements to generate all input combinations. There are 3 input bits: one selection bit, and two input bits to select from. Therefore there are possible 8 input combinations. An exhaustive verification should check the output for all those 8 input combinations:


{| class="wikitable" style="text-align: center;
{| class="wikitable"
! scope="col"| s
|-
! scope="col"| c
! s !! in[1] !! in[0]
|-
| 0|| 0|| 0
|-
| 0 || 0|| 1
|-
| 0|| 1|| 0
|-
| 0|| 1|| 1
|-
| 1|| 0|| 0
|-
| 1|| 0|| 1
|-
|-
|0 ||'''in0'''
| 1|| 1|| 0
|-
|-
|1 ||'''in1'''
| 1|| 1|| 1
|}
|}
[[Fișier: Mux2.png]]


'''always''' block. Used for behavioral descriptions. Template:
A convenient way is to generate those 8 combinations in the order in which they appear in the truth table, starting with '''0 0 0''', continuing with '''0 0 1''', '''0 1 0''', a.s.o.
 
There are many ways to generate such a combination of values for the dut input variables:
'''always @('''''sensitivity list''''') begin'''<br />
&nbsp;&nbsp;''block of instructions''<br />
'''end'''
 
For combinational circuits the safest way to declare the sensitivity list is to use the wildcard ('''*''').
It stands for all variables that appear on RHS expressions and in evaluation expressions.


=== simple assignments ===
<syntaxhighlight lang="Verilog">
<syntaxhighlight lang="Verilog">
always @(*) begin
initial begin
  if(s)
      {s, a, b} = 3'b000;
     c = in[1];
    #1 {s, a, b} = 3'b001;
  else
    #1 {s, a, b} = 3'b010;
     c = in[0];
    #1 {s, a, b} = 3'b011;
     #1 {s, a, b} = 3'b100;
    #1 {s, a, b} = 3'b101;
    #1 {s, a, b} = 3'b110;
     #1 {s, a, b} = 3'b111;
    #1 $stop;
end
end
</syntaxhighlight>
</syntaxhighlight>


Write a testbench using a '''for''' statement to generate all input combinations. Its template is:
 
=== for statement ===
Its template is:


'''for('''''initial_assignment''''';''' ''expression''''';''' ''step_assignment''''')''' ''block of statements'';
'''for('''''initial_assignment''''';''' ''expression''''';''' ''step_assignment''''')''' ''block of statements'';


The foor loop is repeated until the ''expression'' evaluates true. The simplest '''for''' statement uses an integer index that is incremented at the end of each iteration.
The foor loop is repeated until the ''expression'' evaluates true. The simplest '''for''' statement uses an integer index that is incremented at the end of each iteration.
The for loop below generates all combinations from 000 to 111 for tree logic variables, s, a and b.
The for loop below generates all combinations from 000 to 111 for tree logic variables, s, a and b. The index must be declared as a variable of type '''integer'''.


<syntaxhighlight lang="Verilog">
<syntaxhighlight lang="Verilog">
integer index;
initial begin
initial begin
     {s, a, b} = 0;
     {s, a, b} = 0;
Linia 139: Linia 243:
         #1 {s, a, b} = {s, a, b} + 1;
         #1 {s, a, b} = {s, a, b} + 1;
     end
     end
    #1 $stop;
end
</syntaxhighlight>
=== repeat statement ===
Its syntax is '''repeat''' (''number of times'') ''block of statements''
<syntaxhighlight lang="Verilog">
initial begin
    {s, a, b} = 0;
    repeat (8) #1 {s, a, b} = {s, a, b} + 1;
    #1 $stop;
end
end
</syntaxhighlight>
== Exercise 3 ==
The half-adder is the simplest, 1-bit, adder. It implements the addition of two bits.
{| class="wikitable" style="text-align: center;
! scope="col"| a
! scope="col"| b
! scope="col"| s
|-
|0 ||0 || 0 0
|-
|0 ||1 || 0 1
|-
|1 ||0 || 0 1
|-
|1 ||1 || 1 0
|}
[[Fișier: Dic_lab2_adder.png]]
This table may be read either as the addition table, with '''s''' considered as a base-2 number value, or it may be read as a truth table, if '''s''' is viewed as a pair of two logic values. Each bit of '''s''' may be computed as a logic function with two arguments.
=== half adder, data-flow description ===
Use data flow description with '''assign''' statement for each output. They are concurrent assignments: in simulation they are evaluated in parallel, mimicking the parallelism of the real circuits.
<syntaxhighlight lang="Verilog">
// data-flow description
assign s[0] = a ^ b;
assign s[1] = a & b;
</syntaxhighlight>
'''Note:''' Despite '''s''' being declared as one variable, its bits are separately updated by concurrent assignments.


== Exercise 4 ==
=== 2 to 1 multiplexer - gate-level description ===


Work on the same project as that of Exercise 3. You need only to modify the description of the 2 to 1 multiplexer module.
=== half adder, structural description ===


Implement the 2 to 1 multiplexer using only verilog gate primitives. For simulation you may reuse the testbench from Exercise 3 (copy its file into the directory of this project and set it as the testbench). Change the type of the multiplexer's instance in the testbench, if it is different from Exercise 3.
[[Fișier: half_adder.png]]


[[Fișier: Mux2a.png]]
Using gate primitives, a possible structural description of the half-adder may be:
 
<syntaxhighlight lang="Verilog">
// gate-level description
xor g1(s[0], a, b);
and g2(s[1], a, b);
</syntaxhighlight>


Recompile the design file (from the simulator), and restart simulation.


== Exercise 5 ==
== Exercise 4 ==
=== 4 to 1 multiplexer - behavioral description ===
=== 4 to 1 multiplexer, behavioral description ===


[[Fișier: mux4.png]]
[[Fișier: mux4.png]]
Linia 168: Linia 326:


<syntaxhighlight lang="Verilog">
<syntaxhighlight lang="Verilog">
case(s)
always @(*) begin
  0: c = in[0];
    case(s)
  1: c = in[1];
        0: y = in[0];
  2: c = in[2];
        1: y = in[1];
  3: c = in[3];
        2: y = in[2];
endcase  
        3: y = in[3];
    endcase
end
</syntaxhighlight>
</syntaxhighlight>


== Exercise 6 ==
'''s''' is a two-bit variable, therefore it may take values from the integer set {0, 1, 2, 3}.
=== 4 to 1 multiplexer - structural description ===
 
'''Note:''' The '''case''' statement employed for a combinational logic circuit description SHOULD update the variable it controls for any possible value of the ''selectVariable''.
 
'''Note:''' As in Exercise 1, a much more compact description is possible with bit selection, <syntaxhighlight lang="Verilog" inline>assign y = in[s];</syntaxhighlight>.
 
== Exercise 5 ==
=== 4 to 1 multiplexer, structural description ===


[[Fișier: mux4a.png]]
[[Fișier: mux4b.png]]


Implement the 4 to 1 multiplexer using three instances of the 2 to 1 multiplexer (add to the project the file of that module either from Exercise 3, or Exercise 4) and a primitive gate.
Design the 4 to 1 multiplexer using three instances of the 2 to 1 multiplexer (from Exercise 1).


Be careful to distinctly name each instance and each internal wire!
Be careful to distinctly name each instance and each internal wire!

Versiunea curentă din 13 martie 2023 10:00

Many ways to describe a module:

  • Behavioral description: Uses one or more always blocks that encloses procedural statements;
  • Data-flow description: employs parallel continuous assignments (assign), usually with logic operators;
  • Structural description: sub-blocks (module instances, gate primitives) connected together;
  • Mixed description: combines two or all of the above kinds of description;

Exercise 1

Mux 2:1 (two to one multiplexer) is the simplest selection logic circuit. It selects between two inputs, in0 and in1, according to the value of the selection input, s:

s y
0 in0
1 in1


2 to 1 multiplexer, data-flow description

Use one or more continuous assignments. A continuous assignment is a single assignment statement attached to an assign keyword. A continuous assignment updates the value of the RHS variable whenever any variable from the RHS expression changes. The RHS expressions is a logic expression, an expression that employs logic operators. Logic expressions employ only logic operators.

operator function example
~ not ~a
& and a & b
| or a | b
^ xor a ^ b
module mux2(
    input s,
    input [1:0] in,
    output y
);

// dataflow description
assign y = (s & in[1]) | (~s & in[0]);

endmodule

Note: The variable whose value is updated through a continuous assignment should be declared of type wire.


2 to 1 multiplexer, behavioral description

Uses one or more always blocks. The template of an always block is:

always @(sensitivity list) begin
  block of statements
end

For combinational circuits the safest way to declare the sensitivity list is to use the wildcard (*). It stands for all variables that appear inside RHS expressions of the always block. The statements employed may be assignments, conditional assignments and procedural statements (if, case). The always block of statements is executed whenever any of the variables from the sensitivity list changes its value. The statements are executed in the exact order in which they appear inside the always block.

module mux2(
    input s,
    input [1:0] in,
    output y
);

// behavioral description with if-else
always @(*) begin
    if(s)
        y = in[1];
    else
        y = in[0];
end

endmodule

Note: Any variable that gets its value through assignments inside an always block must be declared of type reg. If the variable is also an output of the module, the output is declared as output reg.

Note: Bit selection operator ([ ]) is used to select a bit (or a couple of consecutive bits) from a multibit variable. in[0] is bit 0 from the input vector in.


2 to 1 multiplexer, alternative behavioral description

The same module may be described using different procedural statements. Some descriptions may be higher-level than others. Usually, the higher the level of description, the shorter is the code.

The codes below show three alternative behavioral descriptions for the above multiplexer:

// behavioral description with case
always @(*) begin
    case(s)
    0: y = in[0];
    1: y = in[1];
    endcase
end


// behavioral description with conditional assignment
always @(*) begin
    y = s ? in[1] : in[0]; 
end


// behavioral description with vector selection
always @(*) begin
    y = in[s]; 
end

Note: The last two alternatives may be also employed in continuous assignments, because they are single statements. For example vector selection may be used inside an assign instead of an always: assign y = in[s];.


2 to 1 multiplexer, structural description: gate-level

The gate-level description is a structural description that uses only logic gates. Verilog language offers predefined logic gates that may be instantiated in structural descriptions. The verilog gate primitives are not, and, nand, or, nor, xor, and xnor. Except for the not gate, all other primitive gates may have 2, 3 or more inputs.

All gate primitives are instantiated using the same template: gatePrimitiveType myGate(outputConnection, inputConnection1, inputConnection2 ...) as in the following examples:

not g1(dout, din); is a not gate with dout at output and din connected at its input.

nand g2(dout, d1, d2); is a nand gate with dout at output and two wires, d1 and d2, connected to its inputs.

nand g3(dout, d1, d2, d3, d4); is a nand gate with dout at output and four inputs, connected to d1, d2, d3 and d4.

module mux2(
    input s,
    input [1:0] in,
    output y
);

wire s_n;
wire w1;
wire w0;

// gate-level structural description
not (s_n, s);
and (w1, in[1], s);
and (w0, in[0], s_n);
or  (y, w1, w0);

endmodule


2 to 1 multiplexer, FPGA implementation

Implement one of the previous multiplexer descriptions using 2 buttons (BTN0 and BTN1 ) for the inputs in0 and in1, one switch (SW0) for selection, and one LED (LED0) for the multiplexer output (y):

The constraints file sets the connections between the mux ports and the FPGA pins.

##Switches
set_property -dict { PACKAGE_PIN V2   IOSTANDARD LVCMOS33 } [get_ports { s }]; #SW 0

##Buttons
set_property -dict { PACKAGE_PIN J2   IOSTANDARD LVCMOS33 } [get_ports { in[0] }]; #BTN 0
set_property -dict { PACKAGE_PIN J5   IOSTANDARD LVCMOS33 } [get_ports { in[1] }]; #BTN 1

##LEDs
set_property -dict { PACKAGE_PIN G1   IOSTANDARD LVCMOS33 } [get_ports { y }]; #LED 0


Exercise 2

Write a testbench for the 2:1 multiplexer, using verilog statements to generate all input combinations. There are 3 input bits: one selection bit, and two input bits to select from. Therefore there are possible 8 input combinations. An exhaustive verification should check the output for all those 8 input combinations:

s in[1] in[0]
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

A convenient way is to generate those 8 combinations in the order in which they appear in the truth table, starting with 0 0 0, continuing with 0 0 1, 0 1 0, a.s.o. There are many ways to generate such a combination of values for the dut input variables:

simple assignments

initial begin
       {s, a, b} = 3'b000;
    #1 {s, a, b} = 3'b001;
    #1 {s, a, b} = 3'b010;
    #1 {s, a, b} = 3'b011;
    #1 {s, a, b} = 3'b100;
    #1 {s, a, b} = 3'b101;
    #1 {s, a, b} = 3'b110;
    #1 {s, a, b} = 3'b111;
    #1 $stop;
end


for statement

Its template is:

for(initial_assignment; expression; step_assignment) block of statements;

The foor loop is repeated until the expression evaluates true. The simplest for statement uses an integer index that is incremented at the end of each iteration. The for loop below generates all combinations from 000 to 111 for tree logic variables, s, a and b. The index must be declared as a variable of type integer.

integer index;
initial begin
    {s, a, b} = 0;
    for(index = 0; index < 8; index = index + 1) begin
        #1 {s, a, b} = {s, a, b} + 1;
    end
    #1 $stop;
end


repeat statement

Its syntax is repeat (number of times) block of statements

initial begin
    {s, a, b} = 0;
    repeat (8) #1 {s, a, b} = {s, a, b} + 1;
    #1 $stop;
end


Exercise 3

The half-adder is the simplest, 1-bit, adder. It implements the addition of two bits.

a b s
0 0 0 0
0 1 0 1
1 0 0 1
1 1 1 0

This table may be read either as the addition table, with s considered as a base-2 number value, or it may be read as a truth table, if s is viewed as a pair of two logic values. Each bit of s may be computed as a logic function with two arguments.


half adder, data-flow description

Use data flow description with assign statement for each output. They are concurrent assignments: in simulation they are evaluated in parallel, mimicking the parallelism of the real circuits.

// data-flow description
assign s[0] = a ^ b;
assign s[1] = a & b;

Note: Despite s being declared as one variable, its bits are separately updated by concurrent assignments.


half adder, structural description

Using gate primitives, a possible structural description of the half-adder may be:

// gate-level description
xor g1(s[0], a, b);
and g2(s[1], a, b);


Exercise 4

4 to 1 multiplexer, behavioral description

case instruction. Selects between multiple cases. Template

case(selectVariable)
  value1 : instruction block 1
  value2 : instruction block 2
   a.s.o.
endcase

value1, value2 a.s.o. are integer values of the selectVariable

always @(*) begin
    case(s)
        0: y = in[0];
        1: y = in[1];
        2: y = in[2];
        3: y = in[3];
    endcase
end

s is a two-bit variable, therefore it may take values from the integer set {0, 1, 2, 3}.

Note: The case statement employed for a combinational logic circuit description SHOULD update the variable it controls for any possible value of the selectVariable.

Note: As in Exercise 1, a much more compact description is possible with bit selection, assign y = in[s];.

Exercise 5

4 to 1 multiplexer, structural description

Design the 4 to 1 multiplexer using three instances of the 2 to 1 multiplexer (from Exercise 1).

Be careful to distinctly name each instance and each internal wire!