In this article, we will learn to
- Describe the Priority Encoder using different levels of abstraction in Verilog – Gate level, Dataflow, behavioral, and structural modeling.
- Generate the RTL schematic for the Priority Encoder.
- Write the testbench.
- Generate simulated waveforms.
Contents
What is a Priority Encoder?
Before explaining the priority encoder, you must know what an encoder is.
But, a normal encoder has a problem. If there is more than one input line with logic 1 value, it will encode the wrong output. It only works when only one of the inputs is high. It malfunctions in the case of multiple high inputs.
Thus, to solve the above disadvantage, we “prioritize” the level of each input. Hence, if multiple input lines are selected, the output code will correspond to the input with the highest designated priority. This type of encoder is called the Priority Encoder.
Now, we will learn how to design a 4:2 Priority Encoder using different modeling styles in Verilog.
Gate Level Modeling
This is virtually the lowest abstraction layer, which is used by designers for implementing the lowest level modules, as the switch level modeling isn’t that common. As the name suggests, gate-level modeling makes use of the gate primitives available in Verilog. The prerequisite for this style is knowing the basic logic diagram of the digital circuit that you wish to code. You can learn everything about the Gate level modeling method in Verilog over here.
As we are describing a Priority Encoder using Gate-Level modeling, let’s see the logic circuit:
From the circuit, we can observe that one AND, two OR and one NOT gates are required for designing. Let’s start coding.
Gate level Modeling for 4:2 priority encoder:
As any Verilog code, we start by declaring the module
and terminal ports.
module priority_encoder_42(A0,A1,Y0,Y1,Y2,Y3); .... endmodule
Note that we declare outputs first followed by inputs as the built-in gates also follow the same pattern.
Let’s declare the input and output ports.
input Y3, Y2, Y1, Y0; output A0, A1;
Now, we can declare the intermediate signals. These are signals that are not the terminal ports. From the above circuit, the signals from NOT and AND gates can be treated as intermediate signals.
wire y2bar; //not of y2 wire and_out; // and of y2bar and y1
Time for us to define the logic gates. We use the gate (<outputs>,<inputs>) syntax to use the in-built gates in Verilog.
not(y2bar, y2); and(and_out, y2bar, y1); or(A1, Y3, Y2); or(A0, and_out, Y3);
So, our final code is:
module priority_encoder_42(A0,A1,Y0,Y1,Y2,Y3); input Y3, Y2, Y1, Y0; output A0, A1; wire y2bar; //not of y2 wire and_out; // and of y2bar and y1 not(y2bar, y2); and(and_out, y2bar, y1); or(A1, Y3, Y2); or(A0, and_out, Y3); endmodule
Dataflow Modeling
In this modeling technique, we use logic equations to describe the flow of data from input to the output. We need not bother about the gates that make up the circuit. Hence, it is much easier to construct complex circuits using this level of abstraction since there is no need to know the actual physical layout. You can read all about the dataflow modeling technique in Verilog over here.
To describe the circuit using the logic equation, this modeling uses the keyword assign
. We can see how it is done.
Shouldn’t we know the logic equation for the priority encoder?
Correct! Here it is:
A1 = Y3 + Y2
A0 = Y3 + Y1Y2′
Now, let’s proceed with the coding.
Dataflow modeling of 4:2 Priority Encoder
As always, we start with the module
and port declarations:
module priority_encoder_datafloe(A0,A1,Y0,Y1,Y2,Y3); input Y0,Y1,Y2,Y3; output A0,A1;
Now, we have to describe the flow of data to the outputs using assign
.
assign A1 = Y3 + Y2; assign A0 = Y3 + ((~Y2)&Y1);
Hence, our final code:
module priority_encoder_datafloe(A0,A1,Y0,Y1,Y2,Y3); input Y0,Y1,Y2,Y3; output A0,A1; assign A1 = Y3 + Y2; assign A0 = Y3 + ((~Y2)&Y1); endmodule
Behavioral Modeling
Behavioral Modeling is the highest level of abstraction in Verilog HDL. We can describe the circuit by just knowing how it works. Moreover, there’s additional good news! We do not need to know the logic circuit or logic equation. We just need a simple truth table. You can check out our in-depth guide on behavioral modeling in Verilog here.
Y3 | Y2 | Y1 | Y0 | A1 | A0 |
0 | 0 | 0 | 1 | 0 | 0 |
0 | 0 | 1 | x | 0 | 1 |
0 | 1 | x | x | 1 | 0 |
1 | x | x | x | 1 | 1 |
With this truth table, we can design our priority Encoder using Verilog.
Let’s see how:
Behavioral Modeling of 4:2 Priority Encoder
As usual, starting with the module
and the port declaration.
module priority_encoderbehave(A, Y); input [3:0]Y; output reg [1:0]A;
reg
in behavioral modeling. As we use procedural assignments in this style of modeling, we have to make sure the outputs retain their value until the next value is given to them.always@(Y) begin .... end
What we have declared in brackets is the sensitivity list. Here, depending on the value of Y
, the statements between begin
and end
will be executed. The always
keyword will make sure that the statements get executed every time the sensitivity list is triggered.
In between begin
and end
, we write the procedure for how the system works:
casex(Y) 4'b0001:A = 2'b00; 4'b001x:A = 2'b01; 4'b01xx:A = 2'b10; 4'b1xxx:A = 2'b11; default:$display("Error!"); endcase
The case
compares an expression to a series of cases and executes the statement or statement group associated with the first matching case. We have used casex
, which is a special version of the case
. This will treat the x and z values as don’t cares.
Therefore, our final code will be:
module priority_encoderbehave(A, Y); input [3:0]Y; output reg [1:0]A; always@(Y) begin casex(Y) 4'b0001:A = 2'b00; 4'b001x:A = 2'b01; 4'b01xx:A = 2'b10; 4'b1xxx:A = 2'b11; default:$display("Error!"); endcase end endmodule
Structural Modeling
Structural modeling describes the hardware structure of a digital system. It is somewhat similar to gate-level modeling. The only difference is it doesn’t include any built-in gates. We create separate modules for each gate and then integrate to form the whole circuit
Logic Circuit
In the case of 4:2 priority encoder, we require two OR, an AND and a NOT gates.
We have to structurize each gate with their respective module
.
Structural Modeling of 4:2 Priority Encoder
To start with code, we will first structurize the OR gate.
We declare the module
as or_gate. Then, we declare input and output ports
module or_gate(c,a,b); input a,b output c;
Then, we use assign
statement to write the logical expression for OR.
assign c = a|b;
Thus our OR gate module will be:
module or_gate(c,a,b); input a,b; output c; assign c = a|b; endmodule
Similarly, we do for AND gate:
module and_gate(z,x,y); input x,y; output z; assign z = x&y; endmodule
And NOT gate:
module not_gate(f,e); input e; output f; assign f = ~e; endmodule
Note: We keep variables for assigning inputs and outputs in one module different from others. This ensures mixing up of signals does not happen during a simulation.
Now we can proceed describing the Priority Encoder as the top-level module.
As usual, start with the module and port declarations.
module priority_encoder_struct(A0,A1,Y0,Y1,Y2,Y3); input Y0, Y1, Y2, Y3; output A0,A1;
Time for us to combine these individual modules for logic gates into one single module for the top module. This is done with the help of a concept called module instantiation in which top modules are build using lower modules.
Using the logic circuit, we will instantiate the lower modules in this top using instantiation by port name.
not_gate u1(.f(y2bar), .e(y2)); and_gate u2(.z(w1), .x(y2bar), .y(y1)); or_gate(.c(A1), .a(Y3), .b(Y2)); or_gate(.c(A0), .a(Y1), .b(w1));
Hence, the Verilog code for the priority encoder in structural style is:
module or_gate(c,a,b); input a,b; output c; assign c = a|b; endmodule module not_gate(f,e); input e; output f; assign f = ~e; endmodule module and_gate(z,x,y); input x,y; output z; assign z = x&y; endmodule module priority_encoder_struct(A0,A1,Y0,Y1,Y2,Y3);
input Y0, Y1, Y2, Y3; output A0,A1; not_gate u1(.f(y2bar), .e(y2)); and_gate u2(.z(w1), .x(y2bar), .y(y1)); or_gate(.c(A1), .a(Y3), .b(Y2)); or_gate(.c(A0), .a(Y1), .b(w1)); endmodoule
Testbench
A testbench is an HDL module that is used to test another module, called the device under test (DUT). The test bench contains statements to apply inputs to the DUT and, ideally, to check that the correct outputs are produced. The input and desired output patterns are called test vectors.
Let’s see the test bench for the priority encoder:
module PriorityEncoder_Test; //Reg and Wire declarations reg [3:0] y; wire [1:0] a; // Instantiate the Unit Under Test (UUT) priority_encoderbehave uut (.Y(y), .A(a)); initial begin // Initialize Inputs Y = 0; // Wait 100 ns for global reset to finish #100; // apply test vectors #10 Y = 4'b0000; #10 Y = 4'b1000; #10 Y = 4'b0100; #10 Y = 4'b0010; #10 Y = 4'b0001; #10 Y = 4'b1010; #10 Y = 4'b1111; end initial begin $monitor("time=",$time,, "D=%b : Y=%b V=%b",D,Y,V); end endmodule
RTL Schematic
Here’s how the RTL Schematic will look if we peek into the elaborate design of the behavioral model of the Priority Encoder.
Simulated Waveforms
We can verify the functional correctness of described Priority Encoder by simulation. Here’s the simulated waveform.
We hope you understood the implementation of the Priority Encoder using the various modeling styles in Verilog. For any queries, leave us a comment below.