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

SystemVerilog FSM中重复SPI读取状态能否封装为可综合Task?

用SystemVerilog Task封装SPI字节读取逻辑实现可复用状态机(Gowin FPGA适配)

完全可以用SystemVerilog Task封装你的SPI字节读取逻辑,Gowin IDE支持符合规则的可综合Task,这种写法能直接替代原来的RXSPIBITS状态+ReturnState机制,让状态机结构更简洁、复用性更强。

一、可综合的GetSPIByte Task实现

Gowin对可综合Task的要求是:不能包含#延迟、fork-join、wait(除同步时钟沿检测)等不可综合语句,逻辑必须和模块的时钟/复位同步。下面是适配你需求的Task:

task automatic GetSPIByte(
    input  logic        sck_rising,
    input  logic        mosi,
    output logic [7:0]  rx_byte,
    output logic        byte_done
);
    static logic [2:0] bit_cnt; // 静态变量保存计数,替代原BitCnt寄存器
    static logic [7:0] rx_shift; // 静态变量保存移位数据,替代原RxBits寄存器

    if (sck_rising) begin
        rx_shift <= {rx_shift[6:0], mosi};
        bit_cnt  <= bit_cnt + 1'b1;
        
        if (bit_cnt == 3'd7) begin
            rx_byte  <= rx_shift;
            byte_done <= 1'b1;
            bit_cnt   <= 3'd0; // 完成后复位计数,准备下一次调用
        end else begin
            byte_done <= 1'b0;
        end
    end else begin
        byte_done <= 1'b0;
    end
endtask

说明:

  • 使用static变量在Task调用间保持状态,替代原来模块级的BitCnt和RxBits寄存器
  • byte_done作为完成标志,通知调用状态字节读取完成
  • automatic关键字确保Task在多状态调用时不会出现状态冲突(Gowin推荐添加)

二、修改后的状态机实现

原来的GetCommand、GetParams状态可以直接调用Task,不需要单独的RXSPIBITS状态和returnstate变量:

typedef enum logic [1:0] {
    IDLE,
    GetCommand,
    GetParams,
    Execute
} state_t;

state_t current_state, next_state;
logic [7:0] cmd_byte, param_byte;
logic cmd_done, param_done;

always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        current_state <= IDLE;
        cmd_byte      <= 8'd0;
        param_byte    <= 8'd0;
    end else begin
        current_state <= next_state;
        case (current_state)
            IDLE: begin
                // 触发条件:比如CS拉低
                if (!cs_n) begin
                    next_state <= GetCommand;
                end
            end

            GetCommand: begin
                // 调用Task读取命令字节
                GetSPIByte(SCK_risingedge, mosi, cmd_byte, cmd_done);
                
                if (cmd_done) begin
                    next_state <= GetParams;
                end
            end

            GetParams: begin
                // 调用Task读取参数字节
                GetSPIByte(SCK_risingedge, mosi, param_byte, param_done);
                
                if (param_done) begin
                    next_state <= Execute;
                end
            end

            Execute: begin
                // 执行指令逻辑
                next_state <= IDLE;
            end
        endcase
    end
end

三、Gowin综合器的关键注意事项

  1. Task不能包含动态行为:禁止使用#1、wait(!sck)这类非同步逻辑,所有操作必须绑定到时钟沿
  2. 静态变量的使用:static变量在Gowin综合中会被综合为寄存器,确保Task调用间的状态保持
  3. 参数传递方式:推荐用input/output传递信号,避免直接访问模块内部寄存器(如果需要访问模块信号,用ref类型参数,Gowin支持)
  4. 综合选项配置:在Gowin IDE的综合设置中,确保开启SystemVerilog Support(默认已开启),无需额外配置即可综合此类Task

四、对比原实现的优势

  • 去掉了RXSPIBITS状态和returnstate变量,状态机分支更清晰
  • 读取逻辑集中在Task中,修改时只需维护一处代码
  • 各状态调用逻辑一致,降低了出错概率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 10:25:11