Verilog HDL Concepts, Assignments, and Digital Circuit Implementation
1. Verilog Implementation of Digital Circuits
1a) 4-to-1 MUX Implementation (AND-OR-INVERTER Logic)
Illustrate and write the Verilog description and stimulus code for an AND-OR-INVERTER based 4-to-1 multiplexer.
Verilog Description
module mux4_to_1(out, i0, i1, i2, i3, s1, s0);
output out;
input i0, i1, i2, i3;
input s1, s0;
wire s1n, s0n;
wire y0, y1, y2, y3;
not (s1n, s1);
not (s0n, s0);
// AND gates for selection
and (y0, i0, s1n, s0n); // Select 00
and (y1, i1, s1n, s0); // Select 01
and (y2, i2, s1, s0n); // Select 10
and (y3, i3, s1, s0); // Select 11
// OR gate for output
or (out, y0, y1, y2, y3);
endmoduleStimulus Code (Testbench)
module stimulus;
reg I0, I1, I2, I3;
reg s1, s0;
wire OUTPUT;
// Instantiate the MUX
mux4_to_1 mymux(OUTPUT, I0, I1, I2, I3, s1, s0);
initial
begin
// Initialize inputs
I0 = 1'b1; I1 = 1'b0; I2 = 1'b1; I3 = 1'b0;
#1 $display ("I0 = %b, I1 = %b, I2 = %b, I3 = %b", I0, I1, I2, I3);
// Case 1: s1=0, s0=0 (Select I0)
s1 = 0; s0 = 0;
#1 $display ("s1 = %b, s0 = %b, OUTPUT = %b\n", s1, s0, OUTPUT);
// Case 2: s1=0, s0=1 (Select I1)
s1 = 0; s0 = 1;
#1 $display ("s1 = %b, s0 = %b, OUTPUT = %b\n", s1, s0, OUTPUT);
// Case 3: s1=1, s0=0 (Select I2)
s1 = 1; s0 = 0;
#1 $display ("s1 = %b, s0 = %b, OUTPUT = %b\n", s1, s0, OUTPUT);
// Case 4: s1=1, s0=1 (Select I3)
s1 = 1; s0 = 1;
#1 $display ("s1 = %b, s0 = %b, OUTPUT = %b\n", s1, s0, OUTPUT);
#1 $finish;
end
endmodule1b) Verilog Assignments and Procedural Blocks
(i) Blocking and Non-Blocking Assignments
Blocking Assignments (`=`)
Blocking assignment statements are executed in the order they are specified in a sequential block. They block the execution of the next statement until the current assignment is complete.
reg x, y, z;
reg [15:0] reg_a, reg_b;
integer count;
initial
begin
x = 0; y = 1; z = 1;
count = 0;
reg_a = 16'b0; reg_b = reg_a;
// These assignments execute sequentially
#15 reg_a[2] = 1'b1; // Blocks execution for 15 time units
#10 reg_b[15:13] = {x, y, z}; // Blocks execution for 10 time units
count = count + 1; // Executes only after the previous #10 delay
endNon-Blocking Assignments (`<=`)
Non-blocking assignments allow scheduling of assignments without blocking the execution of the statements that follow in a sequential block. They are typically used for modeling synchronous sequential logic.
reg x, y, z;
reg [15:0] reg_a, reg_b;
integer count;
initial
begin
x = 0; y = 1; z = 1;
count = 0;
reg_a = 16'b0; reg_b = reg_a;
// These assignments are scheduled to happen concurrently at future times
reg_a[2] <= #15 1'b1;
reg_b[15:13] <= #10 {x, y, z};
count <= count + 1; // Scheduled immediately, without waiting for #15 or #10
end(ii) Sequential and Parallel Blocks with Examples
Sequential Block (`begin` and `end`)
The keywords begin and end are used to group statements into a sequential block. Statements within this block execute one after the other.
Sequential without Delays
reg x, y; reg [1:0] z, w;
initial
begin
x = 1'b0;
y = 1'b1;
z = {x, y}; // Executes after y = 1'b1
w = {y, x}; // Executes after z = {x, y}
endSequential with Delay
reg x, y; reg [1:0] z, w;
initial
begin
x = 1'b0;
#5 y = 1'b1; // Waits 5 units after x=0
#10 z = {x, y}; // Waits 10 units after y=1
#20 w = {y, x}; // Waits 20 units after z={x,y}
endParallel Block (`fork` and `join`)
Parallel blocks, specified by keywords fork and join, provide interesting simulation features by allowing statements to execute concurrently. The block finishes when the longest running statement completes.
Parallel with Delay
reg x, y;
reg [1:0] z, w;
initial
fork
x = 1'b0; // Executes immediately
#5 y = 1'b1; // Executes after 5 units
#10 z = {x, y}; // Executes after 10 units
#20 w = {y, x}; // Executes after 20 units
join // Block finishes at time 20Parallel with Deliberate Race Condition
reg x, y;
reg [1:0] z, w;
initial
fork
x = 1'b0;
y = 1'b1;
z = {x, y}; // Reads x and y immediately
w = {y, x}; // Reads y and x immediately
join // All statements execute concurrently at time 02. Verilog Tasks, Functions, and MUX Implementation
2a) Comparison of Verilog Tasks and Functions
The key differences between Verilog tasks and functions are:
| Feature | Tasks | Functions |
|---|---|---|
| Invocation | Can enable other tasks and functions. | Can enable other functions, but not other tasks. |
| Timing | May contain delay, event, or timing control statements. | Must execute in zero simulation time; must not contain any delay or timing control statements. |
| Arguments | May have 0 or more arguments of type input, output, or inout. | Must have at least one input argument. Cannot have output or inout arguments. |
| Return Value | Do not return a value, but can pass multiple values through output and inout arguments. | Always return a single value (the function name acts as the return register). |
2b) Verilog Code for 8-to-1 MUX using Case Statements
module mux8_1(S, I, Y);
input [2:0] S; // Select lines
input [7:0] I; // Data inputs
output Y;
reg Y;
always @(S or I)
begin
case(S)
3'b000: Y = I[0];
3'b001: Y = I[1];
3'b010: Y = I[2];
3'b011: Y = I[3];
3'b100: Y = I[4];
3'b101: Y = I[5];
3'b110: Y = I[6];
3'b111: Y = I[7];
default: Y = 1'bx; // Handle undefined states
endcase
end
endmodule3. Advanced Verilog Constructs and Arithmetic Circuits
3a) Explanation of Verilog Procedural Statements
1. If-Else Statement
Used for conditional decision making, similar to the if statement in C. If the condition is true, statement1 executes; otherwise, statement2 executes.
Syntax:
if (condition)
statement1;
else
statement2;Example:
always @(a or b) begin
if (a > b)
max = a;
else
max = b;
end2. For Loop
Used to repeat operations a fixed number of times. Primarily used for looping through arrays/registers in testbenches or generating repetitive logic (when used with generate).
Syntax:
for (initialization; condition; update)
statement;Example:
reg [7:0] mem [0:15]; // 16-byte memory
integer i;
always @(*) begin
for (i = 0; i < 16; i = i + 1)
mem[i] = 8'h00; // Initialize memory
end3. While Loop
Used for conditional looping where the number of iterations is not fixed at compile time. This construct is generally non-synthesizable if used with unknown iteration bounds.
Syntax:
while (condition)
statement;Example:
integer i;
initial begin
i = 0;
while (i < 5) begin
$display("i = %d", i);
i = i + 1;
end
end4. CaseX Statement
The case statement is used for multi-way branching, similar to switch in C. caseX treats ‘x’ and ‘z’ values in the case expression as don’t cares, making it useful for decoding logic or FSMs where certain inputs are irrelevant.
Syntax:
case (expression)
value1: statement1;
value2: statement2;
default: default_statement;
endcaseExample:
always @(sel or a or b) begin
case (sel)
2'b00: y = 0;
2'b01: y = a;
2'b10: y = b;
default: y = 8'hFF;
endcase
end3b) 4-bit Full Adder with Carry Look-Ahead Logic (CLA)
module fulladder(sum, c_out, a, b, c_in);
output [3:0] sum;
output c_out;
input [3:0] a, b;
input c_in;
wire p0, g0, p1, g1, p2, g2, p3, g3;
wire c1, c2, c3, c4;
// Generate (G) and Propagate (P) signals for each bit
assign p0 = a[0] ^ b[0]; assign g0 = a[0] & b[0];
assign p1 = a[1] ^ b[1]; assign g1 = a[1] & b[1];
assign p2 = a[2] ^ b[2]; assign g2 = a[2] & b[2];
assign p3 = a[3] ^ b[3]; assign g3 = a[3] & b[3];
// Carry generation using CLA logic
assign c1 = g0 | (p0 & c_in);
assign c2 = g1 | (p1 & g0) | (p1 & p0 & c_in);
assign c3 = g2 | (p2 & g1) | (p2 & p1 & g0) | (p2 & p1 & p0 & c_in);
assign c4 = g3 | (p3 & g2) | (p3 & p2 & g1) | (p3 & p2 & p1 & g0) | (p3 & p2 & p1 & p0 & c_in);
// Sum outputs
assign sum[0] = p0 ^ c_in;
assign sum[1] = p1 ^ c1;
assign sum[2] = p2 ^ c2;
assign sum[3] = p3 ^ c3;
// Final carry out
assign c_out = c4;
endmodule4. Counters and Structural Modeling
4a) 4-bit Ripple Counter and Stimulus Code
4-bit Synchronous Counter Code (Often Misnamed Ripple)
module ripple_counter (
input clk,
input reset,
output [3:0] q
);
reg [3:0] q_reg;
assign q = q_reg;
// Note: This implementation is synchronous, as all flip-flops share the same clock.
always @(posedge clk or posedge reset) begin
if (reset)
q_reg <= 4'b0000;
else
q_reg <= q_reg + 1;
end
endmoduleStimulus Code (Testbench)
module tb_ripple_counter;
reg clk;
reg reset;
wire [3:0] q;
// Instantiate the counter
ripple_counter uut (
.clk(clk),
.reset(reset),
.q(q)
);
// Clock generation: toggle every 5 time units
initial begin
clk = 0;
forever #5 clk = ~clk;
end
// Stimulus
initial begin
$display("Time\tReset\tQ");
$monitor("%0t\t%b\t%04b", $time, reset, q);
reset = 1; // Apply reset
#10;
reset = 0; // Release reset
#100; // Run for 100 time units
$finish;
end
endmodule4b) Structural Description and Testbench for Logic Gate Diagram
(Assuming the diagram represents an AND gate followed by an OR gate: OUT = (A & B) | C)
Verilog Code
module D(out, a, b, c);
output out;
input a, b, c;
wire e;
// AND gate with 5 unit delay
and #(5) a1(e, a, b);
// OR gate with 4 unit delay
or #(4) o1(out, e, c);
endmoduleStimulus Code
module stimulus;
reg A, B, C;
wire OUT;
// Instantiate the module
D d1(OUT, A, B, C);
initial begin
// Initial state: A=0, B=0, C=0. OUT should be 0 (after delays).
A = 1'b0; B = 1'b0; C = 1'b0;
// Change 1: A=1, B=1, C=1. OUT should become 1 (after 5+4=9 units).
#10 A = 1'b1; B = 1'b1; C = 1'b1;
// Change 2: A=1, B=0, C=0. OUT should become 0 (after 5+4=9 units).
#10 A = 1'b1; B = 1'b0; C = 1'b0;
#20 $finish;
end
endmodule(Note: The simulation waveform cannot be generated in this text format.)
5. Verilog Delay Types and Structural Implementations
5a) Explanation of Verilog Delay Types
1) Rise, Fall, and Turn-off Delays
In Verilog, you can specify different delay values for rise (0 to 1), fall (1 to 0), and turn-off (X/Z transitions) transitions of signals to simulate real hardware behavior more accurately.
Syntax:
#(rise_delay, fall_delay, turnoff_delay)Example:
module delays_demo;
reg a;
wire b;
// Buffer with rise=2, fall=4, turnoff=6 units
buf #(2, 4, 6) (b, a);
initial begin
a = 0;
#5 a = 1; // Rise delay of 2 will apply to b
#5 a = 0; // Fall delay of 4 will apply to b
#5 a = 1'bz; // Turn-off delay of 6 will apply to b
end
endmodule2) Assignment Delay, Implicit Assignment Delay, and Net Declaration Delay
These methods introduce delays into signal assignments for simulation purposes.
a) Assignment Delay (Procedural)
This delay is specified when assigning a value to a signal using blocking (`=`) or non-blocking (`<=`) assignments in procedural blocks (like always or initial).
Example:
always @(posedge clk)
q <= #5 d; // Non-blocking assignment delay of 5 time unitsb) Implicit Assignment Delay (Continuous)
This occurs in continuous assignments (assign) where the delay is specified before the expression.
Example:
assign #3 y = a & b; // Output y updates 3 time units after a or b changesc) Net Declaration Delay
Delay can also be specified at the time of declaring a wire or net, applying a delay to any value driven onto that net.
Example:
wire #4 delayed_signal = original_signal;5b) Structural Verilog Implementations
2-to-4 Decoder with Tri-State Output
module decoder_2_4(D, E, I);
output [3:0] D; // Decoder outputs
input E; // Enable signal (Active High)
input [1:0] I; // Select inputs
wire [1:0] Ibar;
wire [3:0] s; // Intermediate decoded signals
// Inverters for select lines
not N1 (Ibar[1], I[1]);
not N2 (Ibar[0], I[0]);
// AND gates for decoding (s[i] = minterm i)
and A1 (s[0], Ibar[1], Ibar[0]); // 00
and A2 (s[1], Ibar[1], I[0]); // 01
and A3 (s[2], I[1], Ibar[0]); // 10 (Fixed typo in original logic)
and A4 (s[3], I[1], I[0]); // 11
// Tri-state buffers (bufif1: output is high-Z if control E is low)
bufif1 B1 (D[0], s[0], E);
bufif1 B2 (D[1], s[1], E); // Added missing connection D[1]
bufif1 B3 (D[2], s[2], E);
bufif1 B4 (D[3], s[3], E);
endmoduleFull Adder using Two Half Adders
module full_adder(sum, carry, A, B, Cin);
output sum, carry;
input A, B, Cin;
wire S0, X0, X1;
// HA1: Inputs B, Cin. Outputs: Sum (S0), Carry (X0)
half_adder HA1(S0, X0, B, Cin);
// HA2: Inputs A, S0. Outputs: Sum (sum), Carry (X1)
half_adder HA2(sum, X1, A, S0);
// Final Carry: OR of carries from HA1 and HA2
or O1 (carry, X1, X0);
endmodule
module half_adder(P, Q, X, Y);
output P, Q; // P=XOR (Sum), Q=AND (Carry)
input X, Y;
xor X1(P, X, Y);
and A1(Q, X, Y);
endmodule6. Tasks and Bitwise Operations
6a) Task Declaration and Invocation Example
A task in Verilog is used to group a sequence of statements that can be reused. Unlike functions, tasks can contain delay, timing controls, and have multiple outputs.
module task_example;
reg [3:0] a, b;
reg [4:0] temp; // Register to hold the task output
// Declare the task
task add_and_display;
input [3:0] x, y;
output [4:0] sum;
begin
sum = x + y;
$display("At time %0t: %0d + %0d = %0d", $time, x, y, sum);
end
endtask
initial begin
a = 4; b = 5;
// Task invocation: arguments are passed by position
add_and_display(a, b, temp);
#10;
a = 10; b = 3;
// Task invocation again
add_and_display(a, b, temp);
#10;
$finish;
end
endmodule6b) Verilog Bitwise and Reduction Operations
Given A = 4’b1101 and B = 4’b0101:
-
&A (Reduction AND of A)
A = 1 & 1 & 0 & 1 = 0
-
A << 2 (Left shift A by 2)
1101 << 2 = 110100. Since A is 4 bits, the result is truncated/padded to 4 bits: 4’b0100
-
{2{B}} (Concatenate two copies of B)
B = 0101. Result: 8’b01010101
-
A ^ B (Bitwise XOR)
1101 ^ 0101 = 4’b1000
-
A | B (Bitwise OR)
1101 | 0101 = 4’b1101
