11verilog过程赋值

Verilog过程赋值详解

📑 目录


1. 过程赋值概述

1.1 什么是过程赋值

过程赋值(Procedural Assignment)是在过程块(initial或always语句块)中对变量进行赋值的操作,是Verilog HDL中实现时序逻辑和复杂组合逻辑的核心机制。

1.2 过程赋值的特点

  • 🎯 执行时机: 只有在语句执行时才起作用
  • 📊 目标类型: 只能赋值给reg类型变量
  • 💾 状态保持: 赋值后保持该值直到重新赋值
  • ⏱️ 时序控制: 可以控制赋值的时序特性

1.3 过程赋值 vs 连续赋值

特性 过程赋值 连续赋值
使用位置 initial/always块内 模块内部
目标类型 reg类型 wire类型
执行特性 语句执行时生效 始终激活
关键字 =<= assign
应用场景 时序/复杂组合逻辑 简单组合逻辑

1.4 过程赋值分类

graph TD A[过程赋值] --> B[阻塞赋值 =] A --> C[非阻塞赋值 <=] B --> B1[顺序执行] B --> B2[立即更新] B --> B3[组合逻辑] C --> C1[并行调度] C --> C2[延迟更新] C --> C3[时序逻辑] A --> D[应用场景] D --> D1[组合逻辑建模] D --> D2[时序逻辑建模] D --> D3[状态机设计]

2. 阻塞赋值详解

2.1 基本语法与特性

2.1.1 语法格式

变量名 = 表达式;

2.1.2 执行特性

  • ⏱️ 顺序执行: 当前语句完成后才执行下一条语句
  • 🔄 立即更新: 赋值立即生效,后续语句可以使用新值
  • 📝 仿真特性: 在Delta时间内完成赋值
  • 🎯 用途: 主要用于组合逻辑建模

2.2 基本应用示例

2.2.1 简单阻塞赋值

module blocking_basic_example;
    reg [3:0] a, b, c, d;
    
    initial begin
        // 阻塞赋值序列
        a = 4'b0001;        // a立即变为1
        b = a + 1;          // b使用a的新值,b=2
        c = b + a;          // c使用a和b的新值,c=3
        d = c * 2;          // d使用c的新值,d=6
        
        $display("Results: a=%d, b=%d, c=%d, d=%d", a, b, c, d);
        // 输出: Results: a=1, b=2, c=3, d=6
    end
endmodule

2.2.2 时序控制的阻塞赋值

module blocking_with_timing;
    reg [7:0] data;
    reg clk;
    
    initial begin
        data = 8'h00;
        clk = 0;
        
        // 带时序控制的阻塞赋值
        #10 data = 8'hAA;   // 10ns后赋值
        #20 data = 8'h55;   // 再过20ns后赋值
        #30 data = 8'hFF;   // 再过30ns后赋值
        
        $display("Final data value: %h at time %0t", data, $time);
    end
endmodule

2.3 组合逻辑建模

2.3.1 ALU设计示例

module alu_blocking_design(
    input wire [7:0] a, b,
    input wire [2:0] op_code,
    output reg [7:0] result,
    output reg zero_flag, carry_flag
);
    
    always @(*) begin
        // 使用阻塞赋值建模组合逻辑
        carry_flag = 1'b0;  // 默认值
        
        case (op_code)
            3'b000: result = a + b;              // 加法
            3'b001: result = a - b;              // 减法
            3'b010: result = a & b;              // 逻辑与
            3'b011: result = a | b;              // 逻辑或
            3'b100: result = a ^ b;              // 逻辑异或
            3'b101: result = ~a;                 // 逻辑非
            3'b110: {carry_flag, result} = a + b; // 带进位加法
            3'b111: result = a << 1;             // 左移
            default: result = 8'h00;
        endcase
        
        // 零标志检测
        zero_flag = (result == 8'h00);
    end
endmodule

2.3.2 多路选择器

module mux_blocking_design #(
    parameter DATA_WIDTH = 8,
    parameter SEL_WIDTH = 2
)(
    input wire [DATA_WIDTH-1:0] data_in [0:(1<<SEL_WIDTH)-1],
    input wire [SEL_WIDTH-1:0] sel,
    output reg [DATA_WIDTH-1:0] data_out
);
    
    integer i;
    
    always @(*) begin
        // 默认值
        data_out = {DATA_WIDTH{1'b0}};
        
        // 使用阻塞赋值的选择逻辑
        for (i = 0; i < (1<<SEL_WIDTH); i = i + 1) begin
            if (sel == i) begin
                data_out = data_in[i];
            end
        end
    end
endmodule

2.4 阻塞赋值的时序特性

2.4.1 执行顺序演示

module blocking_order_demo;
    reg [3:0] x, y, z;
    
    initial begin
        $monitor("Time=%0t: x=%d, y=%d, z=%d", $time, x, y, z);
        
        x = 1;
        y = x + 1;      // y = 2 (使用x的新值)
        z = y + x;      // z = 3 (使用x和y的新值)
        
        #10;
        x = 5;
        y = x * 2;      // y = 10
        z = y - x;      // z = 5
    end
endmodule

3. 非阻塞赋值详解

3.1 基本语法与特性

3.1.1 语法格式

变量名 <= 表达式;

3.1.2 执行特性

  • 并行调度: 所有非阻塞赋值同时调度
  • 延迟更新: 在当前时间片结束时统一更新
  • 🔄 使用旧值: 表达式计算使用变量的当前值
  • 🎯 用途: 主要用于时序逻辑建模

3.2 基本应用示例

3.2.1 简单非阻塞赋值

module nonblocking_basic_example;
    reg [3:0] a, b, c;
    
    initial begin
        a = 4'b0001;
        b = 4'b0010;
        c = 4'b0011;
        
        $monitor("Time=%0t: a=%d, b=%d, c=%d", $time, a, b, c);
        
        // 非阻塞赋值:并行调度
        #10;
        a <= b;         // a将获得b的当前值(2)
        b <= c;         // b将获得c的当前值(3)
        c <= a;         // c将获得a的当前值(1)
        
        // 在这个时间点,所有赋值同时生效
        #1 $display("After NBA: a=%d, b=%d, c=%d", a, b, c);
        // 输出: After NBA: a=2, b=3, c=1
    end
endmodule

3.3 时序逻辑建模

3.3.1 D触发器

module d_flipflop(
    input wire clk, rst_n, d,
    output reg q, q_n
);
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            q <= 1'b0;
            q_n <= 1'b1;
        end else begin
            q <= d;
            q_n <= ~d;
        end
    end
endmodule

3.3.2 移位寄存器

module shift_register #(
    parameter WIDTH = 8
)(
    input wire clk, rst_n, serial_in, shift_en,
    output reg [WIDTH-1:0] parallel_out,
    output wire serial_out
);
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            parallel_out <= {WIDTH{1'b0}};
        end else if (shift_en) begin
            // 非阻塞赋值确保正确的移位操作
            parallel_out <= {parallel_out[WIDTH-2:0], serial_in};
        end
    end
    
    assign serial_out = parallel_out[WIDTH-1];
endmodule

3.3.3 计数器链

module counter_chain(
    input wire clk, rst_n, enable,
    output reg [7:0] count0, count1, count2
);
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            count0 <= 8'h00;
            count1 <= 8'h00;
            count2 <= 8'h00;
        end else if (enable) begin
            // 非阻塞赋值创建正确的计数器链
            count0 <= count0 + 1;
            count1 <= count0;        // 使用count0的旧值
            count2 <= count1;        // 使用count1的旧值
        end
    end
endmodule

3.4 非阻塞赋值的调度机制

3.4.1 调度演示

module nba_scheduling_demo;
    reg clk;
    reg [3:0] a, b, c, d;
    
    initial begin
        clk = 0;
        forever #5 clk = ~clk;
    end
    
    initial begin
        a = 1; b = 2; c = 3; d = 4;
        $monitor("Time=%0t: a=%d, b=%d, c=%d, d=%d", $time, a, b, c, d);
    end
    
    always @(posedge clk) begin
        // 所有非阻塞赋值使用当前值计算,然后同时更新
        a <= b;    // a将得到2
        b <= c;    // b将得到3
        c <= d;    // c将得到4
        d <= a;    // d将得到1
        
        $display("  During evaluation: a=%d, b=%d, c=%d, d=%d", a, b, c, d);
    end
endmodule

4. 赋值类型对比分析

4.1 执行行为对比

4.1.1 相同逻辑的不同实现

module assignment_comparison;
    reg clk;
    reg [3:0] a_block, b_block, c_block;      // 阻塞赋值版本
    reg [3:0] a_nonblock, b_nonblock, c_nonblock; // 非阻塞赋值版本
    
    initial begin
        clk = 0;
        forever #10 clk = ~clk;
    end
    
    initial begin
        // 初始化
        a_block = 1; b_block = 2; c_block = 3;
        a_nonblock = 1; b_nonblock = 2; c_nonblock = 3;
        
        $monitor("Time=%0t: Block: a=%d,b=%d,c=%d | NonBlock: a=%d,b=%d,c=%d", 
                 $time, a_block, b_block, c_block, a_nonblock, b_nonblock, c_nonblock);
    end
    
    // 阻塞赋值版本
    always @(posedge clk) begin
        a_block = b_block;     // a得到b的新值
        b_block = c_block;     // b得到c的新值
        c_block = a_block;     // c得到a的新值(已更新)
    end
    
    // 非阻塞赋值版本
    always @(posedge clk) begin
        a_nonblock <= b_nonblock;  // a得到b的旧值
        b_nonblock <= c_nonblock;  // b得到c的旧值
        c_nonblock <= a_nonblock;  // c得到a的旧值
    end
endmodule

4.2 应用场景对比

4.2.1 组合逻辑:阻塞赋值

module combinational_logic_correct;
    input wire [7:0] a, b, c;
    input wire [1:0] sel;
    output reg [7:0] result;
    
    always @(*) begin
        // ✅ 组合逻辑应该使用阻塞赋值
        case (sel)
            2'b00: result = a;
            2'b01: result = b;
            2'b10: result = c;
            2'b11: result = a + b + c;
        endcase
    end
endmodule

4.2.2 时序逻辑:非阻塞赋值

module sequential_logic_correct;
    input wire clk, rst_n;
    input wire [7:0] data_in;
    output reg [7:0] pipe_stage1, pipe_stage2, pipe_stage3;
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            // ✅ 时序逻辑应该使用非阻塞赋值
            pipe_stage1 <= 8'h00;
            pipe_stage2 <= 8'h00;
            pipe_stage3 <= 8'h00;
        end else begin
            pipe_stage1 <= data_in;
            pipe_stage2 <= pipe_stage1;
            pipe_stage3 <= pipe_stage2;
        end
    end
endmodule

4.3 混合使用的问题

4.3.1 错误的混合使用

// ❌ 错误示例:混合使用导致意外结果
module mixed_assignment_bad;
    reg clk, a, b, c;
    
    always @(posedge clk) begin
        a <= 1'b1;      // 非阻塞:a在时钟边沿后更新
        b = a;           // 阻塞:b立即使用a的当前值(旧值)
        c <= b;          // 非阻塞:c使用b的新值
    end
endmodule

// ✅ 正确示例:统一使用非阻塞赋值
module mixed_assignment_good;
    reg clk, a, b, c;
    
    always @(posedge clk) begin
        a <= 1'b1;
        b <= a;          // 统一使用非阻塞赋值
        c <= b;
    end
endmodule

5. 竞争与冒险问题

5.1 竞争条件的产生

5.1.1 阻塞赋值引起的竞争

// ❌ 问题代码:阻塞赋值导致的竞争
module race_condition_blocking_bad;
    reg clk, a, b, c;
    
    // 过程1
    always @(posedge clk) begin
        a = b;           // a立即获得b的值
        c = a;           // c获得a的新值
    end
    
    // 过程2(同时执行)
    always @(posedge clk) begin
        b = c;           // b立即获得c的值
    end
    
    // 结果取决于过程的执行顺序,产生竞争条件
endmodule

// ✅ 解决方案:使用非阻塞赋值
module race_condition_nonblocking_good;
    reg clk, a, b, c;
    
    always @(posedge clk) begin
        a <= b;          // 使用旧值
        c <= a;          // 使用旧值
        b <= c;          // 使用旧值
    end
    
    // 所有信号正确轮换,无竞争条件
endmodule

5.2 冒险现象分析

5.2.1 冒险的产生机制

module hazard_demonstration;
    reg clk, data, enable;
    reg output1, output2;
    
    // 可能产生冒险的设计
    always @(posedge clk) begin
        if (enable) begin
            output1 = data;      // 阻塞赋值
            output2 <= output1;  // 非阻塞赋值使用output1的新值
        end
    end
    
    // 更安全的设计
    always @(posedge clk) begin
        if (enable) begin
            output1 <= data;     // 统一使用非阻塞赋值
            output2 <= output1;  // 使用output1的旧值
        end
    end
endmodule

5.3 避免竞争的设计原则

5.3.1 原则1:分离组合和时序逻辑

module separate_logic_design;
    input wire clk, rst_n;
    input wire [7:0] data_in;
    input wire [2:0] operation;
    output reg [7:0] result;
    
    // 组合逻辑:计算下一个状态
    reg [7:0] next_result;
    always @(*) begin
        case (operation)
            3'b000: next_result = data_in;
            3'b001: next_result = data_in + 1;
            3'b010: next_result = data_in - 1;
            3'b011: next_result = ~data_in;
            default: next_result = 8'h00;
        endcase
    end
    
    // 时序逻辑:寄存器更新
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            result <= 8'h00;
        end else begin
            result <= next_result;
        end
    end
endmodule

5.3.2 原则2:单一时钟域设计

module single_clock_domain;
    input wire clk, rst_n;
    input wire [3:0] input_data;
    output reg [3:0] stage1, stage2, stage3, stage4;
    
    // 所有时序逻辑使用同一时钟
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            stage1 <= 4'h0;
            stage2 <= 4'h0;
            stage3 <= 4'h0;
            stage4 <= 4'h0;
        end else begin
            stage1 <= input_data;
            stage2 <= stage1;
            stage3 <= stage2;
            stage4 <= stage3;
        end
    end
endmodule

6. 时序建模最佳实践

6.1 同步设计原则

6.1.1 标准同步时序设计

module synchronous_design_template(
    input wire clk, rst_n,
    input wire [DATA_WIDTH-1:0] data_in,
    input wire enable, load,
    output reg [DATA_WIDTH-1:0] data_out,
    output reg valid_out
);
    parameter DATA_WIDTH = 8;
    
    // 内部状态寄存器
    reg [DATA_WIDTH-1:0] internal_reg;
    reg valid_reg;
    
    // 同步时序逻辑
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            // 异步复位
            internal_reg <= {DATA_WIDTH{1'b0}};
            valid_reg <= 1'b0;
            data_out <= {DATA_WIDTH{1'b0}};
            valid_out <= 1'b0;
        end else begin
            // 同步操作
            if (load) begin
                internal_reg <= data_in;
                valid_reg <= 1'b1;
            end else if (enable) begin
                internal_reg <= internal_reg + 1;
            end
            
            // 输出寄存器
            data_out <= internal_reg;
            valid_out <= valid_reg;
        end
    end
endmodule

6.2 状态机设计

6.2.1 三段式状态机

module three_segment_fsm(
    input wire clk, rst_n,
    input wire start, done, error,
    output reg busy, complete, error_flag
);
    
    // 状态定义
    typedef enum reg [2:0] {
        IDLE    = 3'b000,
        INIT    = 3'b001,
        ACTIVE  = 3'b010,
        WAIT    = 3'b011,
        FINISH  = 3'b100,
        ERROR   = 3'b101
    } state_t;
    
    state_t current_state, next_state;
    
    // 第一段:状态寄存器(时序逻辑)
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            current_state <= IDLE;
        end else begin
            current_state <= next_state;
        end
    end
    
    // 第二段:次态逻辑(组合逻辑)
    always @(*) begin
        next_state = current_state;  // 默认保持当前状态
        
        case (current_state)
            IDLE: begin
                if (start) next_state = INIT;
            end
            INIT: begin
                if (error) next_state = ERROR;
                else next_state = ACTIVE;
            end
            ACTIVE: begin
                if (error) next_state = ERROR;
                else if (done) next_state = FINISH;
            end
            WAIT: begin
                next_state = IDLE;
            end
            FINISH: begin
                next_state = WAIT;
            end
            ERROR: begin
                if (!start) next_state = IDLE;
            end
            default: next_state = IDLE;
        endcase
    end
    
    // 第三段:输出逻辑(组合逻辑)
    always @(*) begin
        // 默认输出
        busy = 1'b0;
        complete = 1'b0;
        error_flag = 1'b0;
        
        case (current_state)
            IDLE: begin
                // 所有输出为默认值
            end
            INIT, ACTIVE: begin
                busy = 1'b1;
            end
            FINISH: begin
                complete = 1'b1;
            end
            ERROR: begin
                error_flag = 1'b1;
            end
        endcase
    end
endmodule

6.3 流水线设计

6.3.1 数据通路流水线

module pipeline_datapath #(
    parameter DATA_WIDTH = 32,
    parameter STAGES = 4
)(
    input wire clk, rst_n, enable,
    input wire [DATA_WIDTH-1:0] data_in,
    output reg [DATA_WIDTH-1:0] data_out,
    output reg valid_out
);
    
    // 流水线寄存器
    reg [DATA_WIDTH-1:0] stage_data [0:STAGES-1];
    reg stage_valid [0:STAGES-1];
    
    integer i;
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            // 复位所有流水线级
            for (i = 0; i < STAGES; i = i + 1) begin
                stage_data[i] <= {DATA_WIDTH{1'b0}};
                stage_valid[i] <= 1'b0;
            end
            data_out <= {DATA_WIDTH{1'b0}};
            valid_out <= 1'b0;
        end else if (enable) begin
            // 流水线数据流动
            stage_data[0] <= data_in;
            stage_valid[0] <= 1'b1;
            
            for (i = 1; i < STAGES; i = i + 1) begin
                stage_data[i] <= stage_data[i-1] + 1;  // 简单处理
                stage_valid[i] <= stage_valid[i-1];
            end
            
            // 输出级
            data_out <= stage_data[STAGES-1];
            valid_out <= stage_valid[STAGES-1];
        end
    end
endmodule

7. 综合性考虑

7.1 可综合的赋值模式

7.1.1 推荐的综合模式

module synthesizable_patterns;
    input wire clk, rst_n;
    input wire [7:0] data_in;
    input wire enable, load;
    output reg [7:0] data_out;
    
    // ✅ 推荐模式1:同步复位
    always @(posedge clk) begin
        if (!rst_n) begin
            data_out <= 8'h00;
        end else if (load) begin
            data_out <= data_in;
        end else if (enable) begin
            data_out <= data_out + 1;
        end
    end
    
    // ✅ 推荐模式2:异步复位
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_out <= 8'h00;
        end else begin
            if (load) begin
                data_out <= data_in;
            end else if (enable) begin
                data_out <= data_out + 1;
            end
        end
    end
endmodule

7.1.2 避免的综合模式

// ❌ 不推荐的模式
module non_synthesizable_patterns;
    reg clk, data;
    
    // 问题1:延时语句
    always @(posedge clk) begin
        data <= #5 1'b1;  // 延时不可综合
    end
    
    // 问题2:多时钟
    always @(posedge clk1 or posedge clk2) begin  // 多时钟不推荐
        // ...
    end
    
    // 问题3:不完整的复位
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            // 只复位部分信号
        end else begin
            // 使用了未复位的信号
        end
    end
endmodule

7.2 综合优化指导

7.2.1 资源共享

module resource_sharing_example;
    input wire clk, rst_n, sel;
    input wire [15:0] a, b, c, d;
    output reg [16:0] result;
    
    // ✅ 资源共享:复用加法器
    reg [15:0] operand1, operand2;
    
    always @(*) begin
        if (sel) begin
            operand1 = a;
            operand2 = b;
        end else begin
            operand1 = c;
            operand2 = d;
        end
    end
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            result <= 17'h00000;
        end else begin
            result <= operand1 + operand2;  // 共享的加法器
        end
    end
endmodule

8. 实际应用案例

8.1 UART接收器设计

8.1.1 完整的UART接收器

module uart_receiver #(
    parameter CLOCK_FREQ = 50000000,
    parameter BAUD_RATE = 115200
)(
    input wire clk, rst_n, rx_serial,
    output reg [7:0] rx_data,
    output reg rx_valid, rx_error
);
    
    // 波特率计算
    localparam BAUD_TICKS = CLOCK_FREQ / BAUD_RATE;
    localparam HALF_BAUD_TICKS = BAUD_TICKS / 2;
    
    // 状态定义
    typedef enum reg [2:0] {
        IDLE = 3'b000,
        START = 3'b001,
        DATA = 3'b010,
        STOP = 3'b011,
        ERROR = 3'b100
    } uart_state_t;
    
    uart_state_t current_state, next_state;
    
    // 内部寄存器
    reg [$clog2(BAUD_TICKS)-1:0] baud_counter;
    reg [2:0] bit_counter;
    reg [7:0] shift_register;
    reg rx_sync1, rx_sync2;  // 同步器
    
    // 输入同步化
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rx_sync1 <= 1'b1;
            rx_sync2 <= 1'b1;
        end else begin
            rx_sync1 <= rx_serial;
            rx_sync2 <= rx_sync1;
        end
    end
    
    // 状态机寄存器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            current_state <= IDLE;
        end else begin
            current_state <= next_state;
        end
    end
    
    // 次态逻辑
    always @(*) begin
        next_state = current_state;
        
        case (current_state)
            IDLE: begin
                if (!rx_sync2) next_state = START;  // 检测起始位
            end
            START: begin
                if (baud_counter == HALF_BAUD_TICKS) begin
                    if (!rx_sync2) begin
                        next_state = DATA;
                    end else begin
                        next_state = ERROR;  // 假起始位
                    end
                end
            end
            DATA: begin
                if (baud_counter == BAUD_TICKS && bit_counter == 7) begin
                    next_state = STOP;
                end
            end
            STOP: begin
                if (baud_counter == BAUD_TICKS) begin
                    if (rx_sync2) begin
                        next_state = IDLE;
                    end else begin
                        next_state = ERROR;  // 停止位错误
                    end
                end
            end
            ERROR: begin
                if (rx_sync2) next_state = IDLE;  // 等待回到空闲
            end
        endcase
    end
    
    // 计数器和数据寄存器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            baud_counter <= 0;
            bit_counter <= 0;
            shift_register <= 8'h00;
        end else begin
            case (current_state)
                IDLE: begin
                    baud_counter <= 0;
                    bit_counter <= 0;
                end
                START: begin
                    baud_counter <= baud_counter + 1;
                end
                DATA: begin
                    if (baud_counter == BAUD_TICKS) begin
                        baud_counter <= 0;
                        bit_counter <= bit_counter + 1;
                        shift_register <= {rx_sync2, shift_register[7:1]};
                    end else begin
                        baud_counter <= baud_counter + 1;
                    end
                end
                STOP: begin
                    baud_counter <= baud_counter + 1;
                end
                ERROR: begin
                    baud_counter <= 0;
                    bit_counter <= 0;
                end
            endcase
        end
    end
    
    // 输出逻辑
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rx_data <= 8'h00;
            rx_valid <= 1'b0;
            rx_error <= 1'b0;
        end else begin
            rx_valid <= 1'b0;  // 默认无效
            rx_error <= 1'b0;  // 默认无错误
            
            if (current_state == STOP && next_state == IDLE) begin
                rx_data <= shift_register;
                rx_valid <= 1'b1;
            end else if (current_state == ERROR) begin
                rx_error <= 1'b1;
            end
        end
    end
endmodule

8.2 SPI主控制器

8.2.1 SPI控制器设计

module spi_master #(
    parameter DATA_WIDTH = 8,
    parameter CLOCK_DIV = 4
)(
    input wire clk, rst_n,
    input wire [DATA_WIDTH-1:0] tx_data,
    input wire start_tx,
    output reg [DATA_WIDTH-1:0] rx_data,
    output reg tx_done, busy,
    
    // SPI接口
    output reg sclk, mosi, cs_n,
    input wire miso
);
    
    // 内部信号
    reg [$clog2(CLOCK_DIV)-1:0] clk_counter;
    reg [$clog2(DATA_WIDTH)-1:0] bit_counter;
    reg [DATA_WIDTH-1:0] tx_shift_reg, rx_shift_reg;
    reg sclk_enable;
    
    // 状态定义
    typedef enum reg [1:0] {
        IDLE = 2'b00,
        TRANSMIT = 2'b01,
        COMPLETE = 2'b10
    } spi_state_t;
    
    spi_state_t current_state, next_state;
    
    // 时钟分频器
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            clk_counter <= 0;
            sclk <= 1'b0;
        end else if (sclk_enable) begin
            if (clk_counter == CLOCK_DIV - 1) begin
                clk_counter <= 0;
                sclk <= ~sclk;
            end else begin
                clk_counter <= clk_counter + 1;
            end
        end else begin
            clk_counter <= 0;
            sclk <= 1'b0;
        end
    end
    
    // 状态机
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            current_state <= IDLE;
        end else begin
            current_state <= next_state;
        end
    end
    
    // 次态逻辑
    always @(*) begin
        next_state = current_state;
        
        case (current_state)
            IDLE: begin
                if (start_tx) next_state = TRANSMIT;
            end
            TRANSMIT: begin
                if (bit_counter == DATA_WIDTH - 1 && 
                    clk_counter == CLOCK_DIV - 1 && sclk) begin
                    next_state = COMPLETE;
                end
            end
            COMPLETE: begin
                next_state = IDLE;
            end
        endcase
    end
    
    // 数据传输逻辑
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            tx_shift_reg <= 0;
            rx_shift_reg <= 0;
            bit_counter <= 0;
            sclk_enable <= 1'b0;
            cs_n <= 1'b1;
            mosi <= 1'b0;
        end else begin
            case (current_state)
                IDLE: begin
                    if (start_tx) begin
                        tx_shift_reg <= tx_data;
                        rx_shift_reg <= 0;
                        bit_counter <= 0;
                        sclk_enable <= 1'b1;
                        cs_n <= 1'b0;
                        mosi <= tx_data[DATA_WIDTH-1];
                    end else begin
                        sclk_enable <= 1'b0;
                        cs_n <= 1'b1;
                    end
                end
                TRANSMIT: begin
                    if (clk_counter == CLOCK_DIV - 1) begin
                        if (!sclk) begin  // 上升沿
                            rx_shift_reg <= {rx_shift_reg[DATA_WIDTH-2:0], miso};
                        end else begin    // 下降沿
                            if (bit_counter < DATA_WIDTH - 1) begin
                                bit_counter <= bit_counter + 1;
                                tx_shift_reg <= {tx_shift_reg[DATA_WIDTH-2:0], 1'b0};
                                mosi <= tx_shift_reg[DATA_WIDTH-2];
                            end
                        end
                    end
                end
                COMPLETE: begin
                    sclk_enable <= 1'b0;
                    cs_n <= 1'b1;
                end
            endcase
        end
    end
    
    // 输出信号
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rx_data <= 0;
            tx_done <= 1'b0;
            busy <= 1'b0;
        end else begin
            tx_done <= (current_state == COMPLETE);
            busy <= (current_state != IDLE);
            
            if (current_state == COMPLETE) begin
                rx_data <= rx_shift_reg;
            end
        end
    end
endmodule

9. 常见错误与调试

9.1 赋值相关错误

9.1.1 错误类型总结

module common_assignment_errors;
    reg clk, rst_n, data_in;
    reg a, b, c, d;
    
    // ❌ 错误1:时序逻辑中使用阻塞赋值
    always @(posedge clk) begin
        a = data_in;     // 应该使用 <=
        b = a;           // 会导致组合逻辑行为
    end
    
    // ❌ 错误2:组合逻辑中使用非阻塞赋值
    always @(*) begin
        c <= a + b;     // 应该使用 =
    end
    
    // ❌ 错误3:混合使用
    always @(posedge clk) begin
        a <= data_in;   // 非阻塞
        b = a;          // 阻塞:使用a的旧值
        c <= b;         // 非阻塞:使用b的新值
    end
    
    // ✅ 正确的做法
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            a <= 1'b0;
            b <= 1'b0;
            c <= 1'b0;
        end else begin
            a <= data_in;
            b <= a;      // 使用a的旧值
            c <= b;      // 使用b的旧值
        end
    end
endmodule

9.2 调试技巧

9.2.1 添加调试信号

module assignment_debug;
    input wire clk, rst_n, enable;
    input wire [7:0] data_in;
    output reg [7:0] data_out;
    
    // 调试信号
    `ifdef DEBUG
    reg [31:0] assignment_count;
    reg last_enable;
    wire enable_edge;
    `endif
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            data_out <= 8'h00;
            `ifdef DEBUG
            assignment_count <= 0;
            last_enable <= 1'b0;
            `endif
        end else begin
            `ifdef DEBUG
            last_enable <= enable;
            `endif
            
            if (enable) begin
                data_out <= data_in;
                `ifdef DEBUG
                assignment_count <= assignment_count + 1;
                $display("Assignment #%0d at time %0t: data_out <= %h", 
                         assignment_count + 1, $time, data_in);
                `endif
            end
        end
    end
    
    `ifdef DEBUG
    assign enable_edge = enable && !last_enable;
    
    always @(posedge enable_edge) begin
        $display("Enable edge detected at time %0t", $time);
    end
    `endif
endmodule

9.2.2 波形分析工具

module waveform_analysis_testbench;
    reg clk, rst_n;
    reg [3:0] data_in;
    wire [3:0] data_out;
    
    // 被测模块
    assignment_debug dut(
        .clk(clk),
        .rst_n(rst_n),
        .enable(1'b1),
        .data_in(data_in[7:0]),
        .data_out(data_out[7:0])
    );
    
    // 时钟生成
    initial begin
        clk = 0;
        forever #5 clk = ~clk;
    end
    
    // 测试序列
    initial begin
        // 波形文件生成
        $dumpfile("assignment_test.vcd");
        $dumpvars(0, waveform_analysis_testbench);
        
        rst_n = 0;
        data_in = 4'h0;
        
        #20 rst_n = 1;
        
        // 测试数据序列
        repeat(16) begin
            #10 data_in = data_in + 1;
        end
        
        #50 $finish;
    end
    
    // 信号监控
    always @(posedge clk) begin
        $strobe("Time=%0t: data_in=%h, data_out=%h", $time, data_in, data_out);
    end
endmodule

10. 高级主题

10.1 跨时钟域处理

10.1.1 异步FIFO设计

module async_fifo #(
    parameter DATA_WIDTH = 8,
    parameter ADDR_WIDTH = 4
)(
    // 写端口
    input wire wr_clk, wr_rst_n,
    input wire wr_en,
    input wire [DATA_WIDTH-1:0] wr_data,
    output reg wr_full,
    
    // 读端口
    input wire rd_clk, rd_rst_n,
    input wire rd_en,
    output reg [DATA_WIDTH-1:0] rd_data,
    output reg rd_empty
);
    
    localparam DEPTH = 1 << ADDR_WIDTH;
    
    // 存储器
    reg [DATA_WIDTH-1:0] memory [0:DEPTH-1];
    
    // 格雷码指针
    reg [ADDR_WIDTH:0] wr_ptr_gray, wr_ptr_gray_next;
    reg [ADDR_WIDTH:0] rd_ptr_gray, rd_ptr_gray_next;
    
    // 二进制指针
    reg [ADDR_WIDTH:0] wr_ptr_bin, wr_ptr_bin_next;
    reg [ADDR_WIDTH:0] rd_ptr_bin, rd_ptr_bin_next;
    
    // 同步化的指针
    reg [ADDR_WIDTH:0] wr_ptr_gray_sync, rd_ptr_gray_sync;
    reg [ADDR_WIDTH:0] wr_ptr_gray_sync_r, rd_ptr_gray_sync_r;
    
    // 写指针逻辑
    always @(*) begin
        wr_ptr_bin_next = wr_ptr_bin + (wr_en & ~wr_full);
        wr_ptr_gray_next = (wr_ptr_bin_next >> 1) ^ wr_ptr_bin_next;
    end
    
    always @(posedge wr_clk or negedge wr_rst_n) begin
        if (!wr_rst_n) begin
            wr_ptr_bin <= 0;
            wr_ptr_gray <= 0;
        end else begin
            wr_ptr_bin <= wr_ptr_bin_next;
            wr_ptr_gray <= wr_ptr_gray_next;
        end
    end
    
    // 读指针逻辑
    always @(*) begin
        rd_ptr_bin_next = rd_ptr_bin + (rd_en & ~rd_empty);
        rd_ptr_gray_next = (rd_ptr_bin_next >> 1) ^ rd_ptr_bin_next;
    end
    
    always @(posedge rd_clk or negedge rd_rst_n) begin
        if (!rd_rst_n) begin
            rd_ptr_bin <= 0;
            rd_ptr_gray <= 0;
        end else begin
            rd_ptr_bin <= rd_ptr_bin_next;
            rd_ptr_gray <= rd_ptr_gray_next;
        end
    end
    
    // 指针同步化
    always @(posedge wr_clk or negedge wr_rst_n) begin
        if (!wr_rst_n) begin
            rd_ptr_gray_sync_r <= 0;
            rd_ptr_gray_sync <= 0;
        end else begin
            rd_ptr_gray_sync_r <= rd_ptr_gray;
            rd_ptr_gray_sync <= rd_ptr_gray_sync_r;
        end
    end
    
    always @(posedge rd_clk or negedge rd_rst_n) begin
        if (!rd_rst_n) begin
            wr_ptr_gray_sync_r <= 0;
            wr_ptr_gray_sync <= 0;
        end else begin
            wr_ptr_gray_sync_r <= wr_ptr_gray;
            wr_ptr_gray_sync <= wr_ptr_gray_sync_r;
        end
    end
    
    // 状态标志
    always @(*) begin
        wr_full = (wr_ptr_gray_next == {~rd_ptr_gray_sync[ADDR_WIDTH:ADDR_WIDTH-1],
                                        rd_ptr_gray_sync[ADDR_WIDTH-2:0]});
        rd_empty = (rd_ptr_gray == wr_ptr_gray_sync);
    end
    
    // 存储器读写
    always @(posedge wr_clk) begin
        if (wr_en && !wr_full) begin
            memory[wr_ptr_bin[ADDR_WIDTH-1:0]] <= wr_data;
        end
    end
    
    always @(posedge rd_clk or negedge rd_rst_n) begin
        if (!rd_rst_n) begin
            rd_data <= 0;
        end else if (rd_en && !rd_empty) begin
            rd_data <= memory[rd_ptr_bin[ADDR_WIDTH-1:0]];
        end
    end
endmodule

10.2 复杂控制逻辑

10.2.1 多级流水线控制器

module pipeline_controller #(
    parameter STAGES = 5,
    parameter DATA_WIDTH = 32
)(
    input wire clk, rst_n,
    input wire pipeline_enable, flush,
    input wire [DATA_WIDTH-1:0] data_in,
    input wire valid_in,
    output reg [DATA_WIDTH-1:0] data_out,
    output reg valid_out,
    output wire pipeline_ready
);
    
    // 流水线寄存器
    reg [DATA_WIDTH-1:0] stage_data [0:STAGES-1];
    reg stage_valid [0:STAGES-1];
    reg stage_enable [0:STAGES-1];
    
    // 流水线控制信号
    wire stall;
    reg [STAGES-1:0] stage_stall;
    
    integer i;
    
    // 停顿检测逻辑
    assign stall = |stage_stall;  // 任一级停顿则整个流水线停顿
    assign pipeline_ready = pipeline_enable && !stall;
    
    // 流水线主控制逻辑
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            // 复位所有流水线级
            for (i = 0; i < STAGES; i = i + 1) begin
                stage_data[i] <= 0;
                stage_valid[i] <= 1'b0;
                stage_enable[i] <= 1'b0;
            end
            data_out <= 0;
            valid_out <= 1'b0;
        end else if (flush) begin
            // 流水线冲刷
            for (i = 0; i < STAGES; i = i + 1) begin
                stage_valid[i] <= 1'b0;
            end
            valid_out <= 1'b0;
        end else if (pipeline_enable && !stall) begin
            // 正常流水线操作
            
            // 第一级:输入
            stage_data[0] <= data_in;
            stage_valid[0] <= valid_in;
            stage_enable[0] <= 1'b1;
            
            // 中间级:数据传递和处理
            for (i = 1; i < STAGES; i = i + 1) begin
                stage_data[i] <= stage_data[i-1] + 1;  // 简单处理
                stage_valid[i] <= stage_valid[i-1];
                stage_enable[i] <= stage_enable[i-1];
            end
            
            // 输出级
            data_out <= stage_data[STAGES-1];
            valid_out <= stage_valid[STAGES-1];
        end
    end
    
    // 各级停顿条件(示例)
    always @(*) begin
        for (i = 0; i < STAGES; i = i + 1) begin
            stage_stall[i] = 1'b0;  // 默认不停顿
            
            // 可以根据具体需求添加停顿条件
            // 例如:资源冲突、数据依赖等
        end
    end
endmodule

🎯 总结

核心要点回顾

  1. 过程赋值基础: 只能在过程块中使用,目标必须是reg类型
  2. 阻塞赋值 (=): 顺序执行,立即更新,适用于组合逻辑
  3. 非阻塞赋值 (<=): 并行调度,延迟更新,适用于时序逻辑
  4. 竞争避免: 正确选择赋值类型是避免竞争的关键
  5. 设计原则: 分离组合和时序逻辑,使用统一的时钟域

设计指导原则

  • 🎯 组合逻辑: 使用阻塞赋值 (=) 和 always @(*)
  • 时序逻辑: 使用非阻塞赋值 (<=) 和时钟边沿
  • 🔄 避免混合: 不要在同一个always块中混用两种赋值
  • 📊 状态机: 推荐三段式设计模式
  • 🛡️ 复位策略: 异步复位、同步释放

学习建议

  1. 深入理解两种赋值的执行机制
  2. 练习组合逻辑和时序逻辑的标准写法
  3. 学习状态机和流水线设计模式
  4. 掌握跨时钟域处理技术
  5. 培养良好的调试和验证习惯

💡 重要提醒: 正确使用阻塞和非阻塞赋值是Verilog设计的基础技能。记住:阻塞赋值用于组合逻辑,非阻塞赋值用于时序逻辑,这是避免设计错误的黄金法则!

posted @ 2025-07-04 15:52  SiliconDragon  阅读(13)  评论(0)    收藏  举报