# Verilog Fundamentals

## **Module Declaration and Ports**

Every Verilog design starts with a module. Think of it like a black box with inputs and outputs.

```verilog
module my_module (
    input clk,           // Single bit input
    input rst,           // Single bit input  
    input [7:0] data_in, // 8-bit input bus
    output [7:0] data_out, // 8-bit output bus
    output valid         // Single bit output
);

// Module content goes here

endmodule
```

**Key Points:**

* `input` = signals coming INTO your module
    
* `output` = signals going OUT of your module
    
* `[7:0]` = 8-bit wide signal (bit 7 down to bit 0)
    
* All ports are implicitly `wire` type unless specified otherwise
    

## **Data Types: wire vs reg**

### **wire**

* Represents **physical connections** (like actual wires)
    
* Can't store values on their own
    
* Must be **continuously driven** by something
    
* Used for combinational logic outputs
    

```verilog
wire [7:0] sum;        // 8-bit wire
wire carry_out;        // 1-bit wire
wire [15:0] product;   // 16-bit wire
```

### **reg**

* Represents **storage elements** (like flip-flops or latches)
    
* Can **hold values** between clock edges
    
* Used in sequential logic (inside `always` blocks)
    
* **Note:** `reg` doesn't always mean a physical register!
    

```verilog
reg [7:0] counter;     // 8-bit register
reg state;             // 1-bit register  
reg [31:0] memory [0:255]; // Array of 32-bit registers
```

## **assign Statement (Combinational Logic)**

`assign` creates **continuous assignments** - like connecting wires together.

```verilog
module adder_example (
    input [7:0] a, b,
    input cin,
    output [7:0] sum,
    output cout
);

// Simple assignments
assign sum = a + b + cin;
assign cout = (a + b + cin) > 8'hFF;

// Conditional assignment (multiplexer)
assign sum = (cin) ? a + b + 1 : a + b;

// Bitwise operations
assign result = a & b;      // AND
assign result = a | b;      // OR  
assign result = a ^ b;      // XOR
assign result = ~a;         // NOT
assign result = a << 2;     // Shift left by 2
assign result = a >> 1;     // Shift right by 1

endmodule
```

**Key Points:**

* `assign` is for **combinational logic only**
    
* Output changes immediately when inputs change
    
* Think of it as permanent wire connections
    

## **always Blocks (Sequential and Combinational)**

`always` blocks are where the action happens. They describe **behavior**.

### **Sequential Logic (Clocked)**

```verilog
// D Flip-Flop
always @(posedge clk) begin
    if (rst) begin
        q <= 1'b0;        // Reset
    end else begin
        q <= d;           // Normal operation
    end
end

// Counter with enable
always @(posedge clk) begin
    if (rst) begin
        counter <= 8'b0;
    end else if (enable) begin
        counter <= counter + 1;
    end
end

// Shift register
always @(posedge clk) begin
    shift_reg <= {shift_reg[6:0], serial_in}; // Shift left, new bit in
end
```

### **Combinational Logic (always\_comb or always @(\*))**

```verilog
// ALU example
always @(*) begin  // or always_comb in SystemVerilog
    case (operation)
        2'b00: result = a + b;      // ADD
        2'b01: result = a - b;      // SUB  
        2'b10: result = a & b;      // AND
        2'b11: result = a | b;      // OR
        default: result = 8'b0;
    endcase
end

// Priority encoder
always @(*) begin
    if (input_vec[7])      output_code = 3'd7;
    else if (input_vec[6]) output_code = 3'd6;
    else if (input_vec[5]) output_code = 3'd5;
    // ... etc
    else                   output_code = 3'd0;
end
```

## **Edge Detection: posedge, negedge**

Controls **when** the always block executes.

```verilog
always @(posedge clk) begin    // Rising edge of clock
    // This runs when clk goes from 0 → 1
end

always @(negedge clk) begin    // Falling edge of clock  
    // This runs when clk goes from 1 → 0
end

always @(posedge clk or posedge rst) begin  // Multiple edges
    if (rst) begin
        // Asynchronous reset
    end else begin
        // Normal clocked operation
    end
end

always @(*) begin              // Any input change
    // Combinational logic - runs whenever ANY input changes
end
```

## **Module Instantiation and Hierarchy**

Modules are like LEGO blocks - you build complex designs by connecting simpler modules together.

### **Basic Module Instantiation**

```verilog
// Define a simple adder module
module adder_4bit (
    input [3:0] a, b,
    input cin,
    output [3:0] sum,
    output cout
);
    assign {cout, sum} = a + b + cin;
endmodule

// Use the adder in a larger design
module calculator (
    input [3:0] x, y, z,
    output [3:0] result1, result2,
    output overflow1, overflow2
);

// Instantiate two adders
adder_4bit add1 (
    .a(x),           // Connect x to port a
    .b(y),           // Connect y to port b  
    .cin(1'b0),      // Tie cin to 0
    .sum(result1),   // Connect sum to result1
    .cout(overflow1) // Connect cout to overflow1
);

adder_4bit add2 (
    .a(result1),     // Chain the outputs
    .b(z),
    .cin(1'b0),
    .sum(result2),
    .cout(overflow2)
);

endmodule
```

### **Positional vs Named Port Connections**

```verilog
// POSITIONAL (order matters - error prone!)
adder_4bit add1 (x, y, 1'b0, result1, overflow1);

// NAMED (explicit - much better!)
adder_4bit add1 (
    .a(x),
    .b(y), 
    .cin(1'b0),
    .sum(result1),
    .cout(overflow1)
);
```

### **Generate Statements (Arrays of Modules)**

```verilog
module ripple_carry_adder_16bit (
    input [15:0] a, b,
    input cin,
    output [15:0] sum,
    output cout
);

wire [16:0] carry;  // Internal carry chain
assign carry[0] = cin;
assign cout = carry[16];

// Generate 16 full adders
genvar i;
generate
    for (i = 0; i < 16; i = i + 1) begin : adder_stage
        full_adder fa (
            .a(a[i]),
            .b(b[i]),
            .cin(carry[i]),
            .sum(sum[i]),
            .cout(carry[i+1])
        );
    end
endgenerate

endmodule
```

## **Control Structures**

### **if-else Statements**

```verilog
// Simple if
always @(*) begin
    if (enable) begin
        output_data = input_data;
    end else begin
        output_data = 8'b0;
    end
end

// Nested if-else (priority encoder)
always @(*) begin
    if (priority[3]) begin
        grant = 4'b1000;
    end else if (priority[2]) begin
        grant = 4'b0100;
    end else if (priority[1]) begin
        grant = 4'b0010;
    end else if (priority[0]) begin
        grant = 4'b0001;
    end else begin
        grant = 4'b0000;
    end
end

// if in sequential logic
always @(posedge clk) begin
    if (rst) begin
        counter <= 0;
    end else if (load) begin
        counter <= load_value;
    end else if (enable) begin
        counter <= counter + 1;
    end
    // else counter keeps its value
end
```

### **case Statements**

```verilog
// Basic case (like switch statement)
always @(*) begin
    case (opcode)
        3'b000: result = a + b;      // ADD
        3'b001: result = a - b;      // SUB
        3'b010: result = a & b;      // AND
        3'b011: result = a | b;      // OR
        3'b100: result = a ^ b;      // XOR
        3'b101: result = ~a;         // NOT
        3'b110: result = a << 1;     // SHIFT LEFT
        3'b111: result = a >> 1;     // SHIFT RIGHT
        default: result = 8'b0;      // Always include default!
    endcase
end

// Case with don't cares (casez)
always @(*) begin
    casez (instruction[6:0])
        7'b0110011: instr_type = R_TYPE;    // R-type
        7'b0010011: instr_type = I_TYPE;    // I-type  
        7'b01100??: instr_type = B_TYPE;    // B-type (? = don't care)
        7'b???????:  instr_type = UNKNOWN;   // Catch-all
        default:    instr_type = UNKNOWN;
    endcase
end

// State machine with case
always @(posedge clk) begin
    if (rst) begin
        state <= IDLE;
    end else begin
        case (state)
            IDLE: begin
                if (start) state <= FETCH;
            end
            FETCH: begin
                state <= DECODE;
            end
            DECODE: begin
                state <= EXECUTE;
            end
            EXECUTE: begin
                if (done) state <= IDLE;
            end
            default: state <= IDLE;
        endcase
    end
end
```

## **Loops (for, while, repeat)**

**Important:** Loops in Verilog create **hardware**, not software! Each loop iteration becomes parallel hardware.

### **for Loops**

```verilog
// Parallel hardware - all operations happen simultaneously
always @(*) begin
    integer i;
    parity = 1'b0;
    for (i = 0; i < 8; i = i + 1) begin
        parity = parity ^ data[i];  // XOR all bits together
    end
end

// Array initialization
integer j;
always @(posedge clk) begin
    if (rst) begin
        for (j = 0; j < 16; j = j + 1) begin
            register_file[j] <= 32'b0;  // Clear all registers
        end
    end else if (write_enable) begin
        register_file[write_addr] <= write_data;
    end
end

// Generate-for (creates actual hardware instances)
generate
    for (genvar k = 0; k < 8; k = k + 1) begin : byte_lane
        assign byte_valid[k] = |data_bus[k*8 +: 8];  // OR all bits in byte
    end
endgenerate
```

### **while and repeat Loops**

```verilog
// while loop (rare in synthesizable code)
always @(*) begin
    temp = input_val;
    count = 0;
    while (temp != 0) begin
        count = count + 1;
        temp = temp >> 1;  // Count leading zeros
    end
end

// repeat loop (fixed number of iterations)  
always @(*) begin
    shifted_data = input_data;
    repeat (shift_amount) begin
        shifted_data = shifted_data << 1;
    end
end
```

## **Common Data Structures**

### **Arrays and Memories**

```verilog
// Register array (like register file)
reg [31:0] registers [0:31];    // 32 registers, each 32-bits wide

// 2D array
reg [7:0] memory [0:255][0:3];  // 256 rows × 4 columns of bytes

// Accessing arrays
always @(posedge clk) begin
    if (write_enable) begin
        registers[write_addr] <= write_data;
    end
    read_data <= registers[read_addr];
end

// Initialize array
integer i;
initial begin
    for (i = 0; i < 32; i = i + 1) begin
        registers[i] = 32'b0;
    end
end
```

### **Packed vs Unpacked Arrays**

```verilog
// PACKED - contiguous bits, can be accessed as vector
reg [7:0] packed_array [0:15];     // 16 elements of 8 bits each
wire [127:0] flat_view = packed_array;  // Can treat as 128-bit vector

// UNPACKED - separate storage locations  
reg unpacked_array [0:15][7:0];    // Same data, different organization
// Cannot directly convert to single vector
```

### **Bit Selection and Slicing**

```verilog
wire [31:0] data = 32'hDEADBEEF;

// Bit selections
wire msb = data[31];           // Most significant bit
wire lsb = data[0];            // Least significant bit

// Bit slicing  
wire [7:0] byte3 = data[31:24]; // Upper byte
wire [7:0] byte0 = data[7:0];   // Lower byte
wire [15:0] upper = data[31:16]; // Upper 16 bits

// Variable bit selection (careful!)
wire selected_bit = data[bit_index];  // bit_index must be constant!

// Part-select with variable index
wire [7:0] byte_sel = data[byte_index*8 +: 8];  // +: means "8 bits starting at"
wire [7:0] byte_sel2 = data[byte_index*8 + 7 : byte_index*8];  // Same thing
```

## **Advanced Control Structures**

### **Conditional Operator (Ternary)**

```verilog
// Simple mux
assign output = select ? input1 : input0;

// Nested conditionals  
assign priority_out = (high_pri) ? high_data :
                     (med_pri)  ? med_data :
                     (low_pri)  ? low_data : default_data;

// Inside always blocks
always @(*) begin
    next_state = (current_state == IDLE) ? 
                 (start ? ACTIVE : IDLE) :
                 (done ? IDLE : ACTIVE);
end
```

### **Functions and Tasks**

```verilog
// Function (combinational only, returns value)
function [7:0] count_ones;
    input [31:0] data;
    integer i;
    begin
        count_ones = 0;
        for (i = 0; i < 32; i = i + 1) begin
            count_ones = count_ones + data[i];
        end
    end
endfunction

// Task (can have delays, multiple outputs)
task write_memory;
    input [7:0] addr;
    input [31:0] data;
    begin
        @(posedge clk);
        mem_addr <= addr;
        mem_data <= data;
        mem_we <= 1'b1;
        @(posedge clk);
        mem_we <= 1'b0;
    end
endtask

// Using function and task
always @(*) begin
    ones_count = count_ones(input_vector);
end

always @(posedge clk) begin
    if (write_request) begin
        write_memory(address, write_data);
    end
end
```

### **Parameters and Localparam**

```verilog
// Module parameters (configurable)
module fifo #(
    parameter WIDTH = 8,
    parameter DEPTH = 16
) (
    input clk, rst,
    input [WIDTH-1:0] din,
    input push, pop,
    output [WIDTH-1:0] dout,
    output full, empty
);

localparam ADDR_BITS = $clog2(DEPTH);  // Local parameter

reg [WIDTH-1:0] memory [0:DEPTH-1];
reg [ADDR_BITS:0] wr_ptr, rd_ptr;

// Implementation...

endmodule

// Instantiate with different parameters
fifo #(.WIDTH(32), .DEPTH(64)) data_fifo (...);
fifo #(.WIDTH(16), .DEPTH(8))  cmd_fifo (...);
```

## **Complete Examples**

### **Simple Counter**

```verilog
module counter (
    input clk,
    input rst,
    input enable,
    output reg [7:0] count
);

always @(posedge clk) begin
    if (rst) begin
        count <= 8'b0;           // Use <= for sequential
    end else if (enable) begin
        count <= count + 1;
    end
end

endmodule
```

### **State Machine**

```verilog
module fsm (
    input clk, rst,
    input start, done,
    output reg busy,
    output reg [1:0] state
);

parameter IDLE = 2'b00;
parameter WORKING = 2'b01; 
parameter FINISH = 2'b10;

reg [1:0] next_state;

// State register
always @(posedge clk) begin
    if (rst) begin
        state <= IDLE;
    end else begin
        state <= next_state;
    end
end

// Next state logic  
always @(*) begin
    case (state)
        IDLE: begin
            if (start) next_state = WORKING;
            else       next_state = IDLE;
        end
        WORKING: begin
            if (done)  next_state = FINISH;
            else       next_state = WORKING;
        end
        FINISH: begin
            next_state = IDLE;
        end
        default: next_state = IDLE;
    endcase
end

// Output logic
always @(*) begin
    busy = (state == WORKING);
end

endmodule
```

### **Memory Module**

```verilog
module simple_memory (
    input clk,
    input [7:0] addr,
    input [31:0] data_in,
    input we,                    // Write enable
    output reg [31:0] data_out
);

reg [31:0] memory [0:255];      // 256 words of 32-bit memory

always @(posedge clk) begin
    if (we) begin
        memory[addr] <= data_in;  // Write
    end
    data_out <= memory[addr];     // Read (always happens)
end

endmodule
```

## **Real-World Example: UART Transmitter**

```verilog
module uart_tx #(
    parameter CLOCK_FREQ = 50_000_000,
    parameter BAUD_RATE = 115200
) (
    input clk, rst,
    input [7:0] data,
    input send,
    output reg tx,
    output reg busy
);

localparam CLKS_PER_BIT = CLOCK_FREQ / BAUD_RATE;
localparam COUNTER_BITS = $clog2(CLKS_PER_BIT);

// State machine states
localparam IDLE      = 3'b000;
localparam START_BIT = 3'b001;
localparam DATA_BITS = 3'b010;
localparam STOP_BIT  = 3'b011;

reg [2:0] state;
reg [COUNTER_BITS-1:0] clk_counter;
reg [2:0] bit_counter;
reg [7:0] tx_data;

always @(posedge clk) begin
    if (rst) begin
        state <= IDLE;
        tx <= 1'b1;
        busy <= 1'b0;
        clk_counter <= 0;
        bit_counter <= 0;
    end else begin
        case (state)
            IDLE: begin
                tx <= 1'b1;
                busy <= 1'b0;
                clk_counter <= 0;
                bit_counter <= 0;
                
                if (send) begin
                    tx_data <= data;
                    state <= START_BIT;
                    busy <= 1'b1;
                end
            end
            
            START_BIT: begin
                tx <= 1'b0;  // Start bit is 0
                
                if (clk_counter < CLKS_PER_BIT - 1) begin
                    clk_counter <= clk_counter + 1;
                end else begin
                    clk_counter <= 0;
                    state <= DATA_BITS;
                end
            end
            
            DATA_BITS: begin
                tx <= tx_data[bit_counter];
                
                if (clk_counter < CLKS_PER_BIT - 1) begin
                    clk_counter <= clk_counter + 1;
                end else begin
                    clk_counter <= 0;
                    
                    if (bit_counter < 7) begin
                        bit_counter <= bit_counter + 1;
                    end else begin
                        bit_counter <= 0;
                        state <= STOP_BIT;
                    end
                end
            end
            
            STOP_BIT: begin
                tx <= 1'b1;  // Stop bit is 1
                
                if (clk_counter < CLKS_PER_BIT - 1) begin
                    clk_counter <= clk_counter + 1;
                end else begin
                    state <= IDLE;
                end
            end
            
            default: state <= IDLE;
        endcase
    end
end

endmodule
```

## **Key Rules and Best Practices**

### **Blocking vs Non-Blocking Assignments**

```verilog
// BLOCKING (=) - happens immediately
always @(*) begin
    temp = a + b;      // This happens first
    result = temp + c; // This uses the NEW value of temp
end

// NON-BLOCKING (<=) - happens at end of time step  
always @(posedge clk) begin
    temp <= a + b;      // Both assignments happen
    result <= temp + c; // simultaneously using OLD values
end
```

**Rule of Thumb:**

* Use `<=` for **sequential logic** (flip-flops, registers)
    
* Use `=` for **combinational logic** (inside always @(\*))
    

### **Common Patterns**

```verilog
// Reset pattern
always @(posedge clk) begin
    if (rst) begin
        // Reset all registers to known values
        counter <= 0;
        state <= IDLE;
        valid <= 0;
    end else begin
        // Normal operation
    end
end

// Enable pattern
always @(posedge clk) begin
    if (rst) begin
        data <= 0;
    end else if (enable) begin
        data <= new_data;  // Only update when enabled
    end
    // else data keeps its old value
end

// Mux pattern
assign output = (select) ? input1 : input0;

// Decoder pattern  
always @(*) begin
    outputs = 8'b0;           // Default all off
    outputs[address] = 1'b1;  // Turn on selected bit
end
```

## **Summary**

* **wire**: For connections and combinational outputs
    
* **reg**: For storage and variables in always blocks
    
* **assign**: Continuous combinational logic
    
* **always @(posedge clk)**: Sequential logic (flip-flops)
    
* **always @(\*)**: Combinational logic
    
* **&lt;=**: Non-blocking (use for sequential)
    
* **\=**: Blocking (use for combinational)
    
* **Modules**: Building blocks - connect them to make complex designs
    
* **if/case**: Control flow - creates multiplexers and decoders in hardware
    
* **for loops**: Create parallel hardware, not sequential execution
    
* **Functions/tasks**: Reusable code blocks
    
* **Parameters**: Make modules configurable
    

The key is understanding **when things happen**:

* `assign` and `always @(*)` react **immediately** to input changes
    
* `always @(posedge clk)` only updates on **clock edges**
    
* **Loops unroll into parallel hardware**
    
* **Each module instance is separate physical hardware**
