- `always @(posedge opt_txclk)` 触发后,调用内部有 `@(posedge opt_txclk)`的task

问题背景

  • always @(posedge opt_txclk) 触发后,调用 task
  • task 内部还有 @(posedge opt_txclk),但在下一个时钟沿到来之前,always 块会不会重新触发,从而导致错误。

Verilog 的事件调度机制

  1. always @(posedge opt_txclk)

    • 每当 opt_txclk 出现上升沿时,always 块会被触发,进入执行状态。
  2. 任务中的 @(posedge opt_txclk)

    • 任务中是独立的事件等待语句。它会等待 下一个 上升沿,而不会与调用任务的 always 同时竞争。
    • 一旦任务被调用,它的执行会被 阻塞 在第一个 @(posedge opt_txclk) 处,直到时钟的下一个上升沿发生。
  3. 关键点

    • Verilog 是单线程执行的:当任务被调用时,always 块本身的执行会被暂停,直到任务完成。因此,always 块不会 “重新触发” 而中断任务的执行。

模拟分析

以下是一个简化的代码示例,用于说明 always 块中调用任务的执行顺序。

module test_task;

    reg opt_txclk;        // 时钟信号
    reg [31:0] cmd;       // 输入命令
    reg busy;             // 忙状态信号

    initial begin
        opt_txclk = 0;
        forever #5 opt_txclk = ~opt_txclk; // 模拟时钟,周期为10
    end

    // 任务定义
    task send_cmd;
        input [31:0] cmd_data;
        begin
            wait(~busy);             // 等待空闲
            busy = 1;
            @(posedge opt_txclk);    // 等待第一个时钟上升沿
            $display("Task: Sent cmd_data = %h", cmd_data);
            busy = 0;
        end
    endtask

    // 使用 always 调用任务
    always @(posedge opt_txclk) begin
        if (!busy) begin
            send_cmd(cmd); // 调用任务
        end
    end

    initial begin
        busy = 0;
        cmd = 32'hDEADBEEF;
        #50;
        cmd = 32'hCAFEBABE;
        #50 $finish;
    end

endmodule

执行分析

  1. 时钟信号周期opt_txclk 每 10 时间单位发生一个周期。

  2. 事件执行顺序

    • 在每个 posedge opt_txclk 时,always 块会被触发。
    • 如果 busy0send_cmd 被调用。
    • send_cmd 内部,执行 @(posedge opt_txclk) 会暂停任务,直到时钟的 下一个上升沿
    • 此时,always 块已完成,等待下一次 posedge opt_txclk
  3. 结果

    • 每次调用 send_cmd 时,always 块不会重新触发,因为任务执行是阻塞的,always 必须等待任务完成。

关键点:为什么不会重新触发?

  1. 任务阻塞

    • always 块调用 task 时,Verilog 的事件调度会阻塞 always,直到任务完成。
    • 即使 always 是在 @(posedge opt_txclk) 上触发的,它也会等待 task 中的 @(posedge opt_txclk) 运行完。
  2. 时序保证

    • @(posedge opt_txclk) 内部是按照事件的先后顺序执行的。不会因为两个 @(posedge opt_txclk) 的位置不同而跳过中间的逻辑。

改进建议

如果你担心任务和 always 块竞争 @(posedge opt_txclk),可以增加一个 busy 信号确保任务的独占性。例如:

always @(posedge opt_txclk) begin
    if (!busy) begin
        send_cmd(cmd); // 仅在 busy=0 时调用任务
    end
end

send_cmd 中:

task send_cmd;
    input [31:0] cmd_data;
    begin
        busy = 1;
        @(posedge opt_txclk);
        // 发送数据的逻辑
        busy = 0;
    end
endtask

总结

  • 每次调用 task 时,always 块会阻塞直到任务完成。
  • @(posedge opt_txclk)task 中会等待下一个时钟沿,而不会和 always 的触发冲突。
  • 使用 busy 信号可以确保任务执行期间不会重复触发。

你可能感兴趣的:(fpga开发)