Applications 3

De la WikiLabs

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

Mux2.png


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.

Mux2a.png

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):

Dic lab3 mux fpga.png

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

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.

// 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

Half adder.png

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

Mux4.png

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

Mux4b.png

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!