You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在两个always块中操作通用寄存器?AXI与自定义模块同步方案咨询

解决FPGA多Always块写同一寄存器的冲突问题

这在FPGA设计里是个典型的「多驱动冲突」坑——Verilog/SV里绝对不允许同一个寄存器被多个always块直接赋值,否则综合工具要么报错,要么生成不可控的组合逻辑,硬件行为完全不可预测。我给你两个工业界常用的靠谱方案,结合你的需求来实现:

方案一:单驱动源+脉冲触发(最直接)

核心思路是把原来两个always块的写操作,拆分成置位/清零脉冲信号,然后让flag寄存器只由一个always块驱动,通过多路选择逻辑决定最终值。如果AXI接口和主时钟(mainclk)是不同时钟域,还要先做跨时钟域同步。

// 定义控制信号:把原有的直接写flag,改成生成单周期脉冲
reg axi_set_flag;       // AXI侧产生的置位脉冲
reg axi_set_flag_sync;  // 同步到mainclk域的置位脉冲(跨时钟域用)
reg task_done;          // 自定义任务完成后的清零脉冲
reg flag;               // 最终的flag寄存器

// 1. AXI接口的always块(假设AXI时钟为s_axi_aclk)
always @(posedge s_axi_aclk) begin
    // 当用户通过AXI写flag寄存器时,生成单周期置位脉冲
    if (axi_write_flag_trigger) begin // 替换成你实际的AXI写触发条件
        axi_set_flag <= 1'b1;
    end else begin
        axi_set_flag <= 1'b0;
    end
    // 其他AXI接口逻辑...
end

// 2. 跨时钟域同步(如果AXI时钟和mainclk不同,必须做!避免亚稳态)
always @(posedge mainclk) begin
    axi_set_flag_sync <= axi_set_flag;
end

// 3. 自定义任务的always块(mainclk域)
always @(posedge mainclk) begin
    if (flag == 1'b1) begin
        // 执行你的任务逻辑...
        // 任务完成时生成单周期清零脉冲
        if (task_finish_condition) begin // 替换成你实际的任务完成判断
            task_done <= 1'b1;
        end else begin
            task_done <= 1'b0;
        end
    end else begin
        task_done <= 1'b0;
    end
end

// 4. 唯一驱动flag的always块(关键!避免多驱动)
always @(posedge mainclk) begin
    if (axi_set_flag_sync) begin
        flag <= 1'b1; // AXI触发置位
    end else if (task_done) begin
        flag <= 1'b0; // 任务完成清零
    end
    // 注:如果两个脉冲同时触发(概率极低,同步后基本不会发生),这里优先级是置位优先,可按需调整
end

方案二:状态机整合逻辑(更适合复杂任务)

如果你的自定义任务逻辑比较复杂,用状态机统一管理任务的「空闲-启动-运行-完成」生命周期,flag的赋值完全由状态机控制,从根源上避免多驱动问题。

// 定义状态机状态
reg [1:0] task_state;
localparam IDLE    = 2'b00;
localparam RUNNING = 2'b01;
localparam DONE    = 2'b10;

reg axi_start_task; // AXI侧触发的任务启动信号(需同步到mainclk域)
reg flag;

// 1. AXI接口逻辑(同步到mainclk,或先做跨时钟域同步)
always @(posedge s_axi_aclk) begin
    if (axi_write_flag_trigger) begin
        axi_start_task <= 1'b1;
    end else begin
        axi_start_task <= 1'b0;
    end
end

// 2. 跨时钟域同步(如果需要)
always @(posedge mainclk) begin
    axi_start_task <= axi_start_task_raw; // axi_start_task_raw是AXI侧的原始信号
end

// 3. 主状态机always块(mainclk域,唯一控制flag)
always @(posedge mainclk) begin
    case(task_state)
        IDLE: begin
            flag <= 1'b0;
            // AXI触发任务启动
            if (axi_start_task) begin
                task_state <= RUNNING;
                flag <= 1'b1;
            end
        end
        RUNNING: begin
            // 执行你的任务逻辑...
            // 任务完成后跳转到DONE状态
            if (task_finish_condition) begin
                task_state <= DONE;
            end
        end
        DONE: begin
            flag <= 1'b0;
            task_state <= IDLE; // 回到空闲状态,等待下一次触发
        end
        default: task_state <= IDLE;
    endcase
end

关键注意事项

  • 跨时钟域必须同步:如果AXI接口的时钟和mainclk不是同一个,一定要用两级触发器对AXI侧的控制信号做同步,否则会出现亚稳态,导致逻辑异常。
  • 脉冲信号要单周期:不管是置位还是清零信号,都要确保是单周期脉冲,避免重复触发任务或者重复清零。
  • 绝对避免多驱动:永远不要让同一个寄存器被多个always块直接赋值,这是FPGA设计的基本规范。

内容的提问来源于stack exchange,提问作者Rahul Tungar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:01:53