Write clean RTL that's easy to test
Every Verilog design is built from modules.
module module_name (
// Port declarations
input clk, // Single bit input
input rst_n, // Active low reset (n = negative)
input [7:0] data_in, // 8-bit input bus
output valid, // Single bit output
output [7:0] data_out, // 8-bit output bus
inout [7:0] bidir // Bidirectional (avoid if possible!)
);
// Internal signals
wire [7:0] internal_wire;
reg [7:0] internal_reg;
// Your logic here
endmodule
inout ports - they create test complications.= (Blocking)always @(*) begin // Sensitive to ALL inputs
case (sel)
2'b00: out = a;
2'b01: out = b;
2'b10: out = c;
default: out = d; // Always have default!
endcase
end
<= (Non-Blocking)always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
q1 <= 1'b0;
q2 <= 1'b0;
end else begin
q1 <= d;
q2 <= q1; // Creates shift register
end
end
This is the #1 source of bugs for beginners!
=) - Executes Sequentiallyalways @(posedge clk) begin
q1 = d; // q1 gets NEW value immediately
q2 = q1; // q2 gets NEW q1 (same as d!)
end
// Result: q1 = q2 = d (NOT a shift register!)
<=) - Executes in Parallelalways @(posedge clk) begin
q1 <= d; // Both read OLD values first
q2 <= q1; // Then both update together
end
// Result: Proper shift register!
| Logic Type | Use | Why |
|---|---|---|
| Combinational | = (blocking) | Sequential evaluation needed |
| Sequential | <= (non-blocking) | Parallel FF behavior |
module dff (
input clk,
input rst_n, // Active low async reset
input d,
output reg q
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0; // Reset value
else
q <= d; // Normal operation
end
endmodule
Note: This is what gets converted to a scan flip-flop during DFT insertion!
// WRONG - Creates unwanted latch!
always @(*) begin
if (sel)
out = a;
// Missing else! Tool infers latch to hold 'out'
end
// CORRECT - Complete assignment
always @(*) begin
if (sel)
out = a;
else
out = b; // Or use default at beginning
end
// WRONG - Simulation/synthesis mismatch
always @(a) begin // Missing 'b'!
out = a & b;
end
// CORRECT - Use @(*)
always @(*) begin // Auto-includes all
out = a & b;
end
// WRONG - Two always blocks driving same signal
always @(posedge clk) q <= a;
always @(posedge clk) q <= b; // Conflict!
// CORRECT - Single driver
always @(posedge clk)
q <= sel ? a : b;
assign y = a & b; // AND gate
assign mux_out = sel ? a : b; // 2:1 MUX
assign {cout, sum} = a + b; // Adder with concatenation
assign all_ones = &data; // AND all bits (1 if all 1s)
assign any_one = |data; // OR all bits (1 if any 1)
assign parity = ^data; // XOR all bits (odd parity)
After DFT insertion, your DFF becomes:
module scan_dff (
input clk,
input rst_n,
input d, // Functional data
input si, // Scan input
input se, // Scan enable
output reg q
);
wire mux_out;
// Scan MUX
assign mux_out = se ? si : d;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
q <= 1'b0;
else
q <= mux_out;
end
endmodule
| Topic | Key Point |
|---|---|
| Always blocks | @(*) for combo, @(posedge clk) for seq |
Blocking = | Use for combinational logic |
Non-blocking <= | Use for sequential logic |
| Latches | Avoid! Always have complete if-else |
| Sensitivity | Use @(*) to avoid mistakes |