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

SystemVerilog中I2C Slave模块多驱动错误排查求助

I2C Slave模块多驱动错误修复方案

错误根源

address_counter和data_counter触发多驱动报错,是因为这两个变量被两个独立过程块同时驱动:

  • 第52、53行的always_comb块用阻塞赋值直接将其置0
  • 第169、180行的always_ff块用非阻塞赋值更新计数器值

SystemVerilog规范明确禁止单个变量被多个过程块驱动,这会导致综合工具冲突报错。


修复步骤

1. 移除组合逻辑中对计数器的非法赋值

直接删除always_comb块开头的两行代码:

address_counter = 0;
data_counter = 0;

计数器属于时序逻辑,只能在always_ff块中更新,组合逻辑块仅需读取其值用于状态判断,不能直接赋值。

2. 重写计数器时序逻辑

原计数器逻辑仅在值为0时才递增,完全不符合I2C逐位接收的需求。需结合当前状态控制计数器的递增与复位:

修改address_counter驱动块

// 从机地址接收与计数器控制
always_ff @(posedge i2c_slave_scl or negedge i2c_slave_rstn) begin
  if (!i2c_slave_rstn) begin
     address_counter <= '0;
     addr <= '0;
     read_write_bit <= '0;
  end
  else begin
    case(curr_state)
      IDLE: begin
        if(start_detect) address_counter <= '0; // 起始条件触发后复位计数器
      end
      SLAVE_ADDR: begin
        if(address_counter < 6) begin
          addr[address_counter] <= i2c_slave_sda_i;
          address_counter <= address_counter + 1;
        end else if(address_counter == 6) begin
          read_write_bit <= i2c_slave_sda_i; // 接收读写控制位
          address_counter <= address_counter + 1;
        end
      end
      default: begin
        if(stop_detect) address_counter <= '0; // 停止条件触发后复位
      end
    endcase
  end
end

修改data_counter驱动块

// 寄存器地址/数据接收与计数器控制
always_ff @(posedge i2c_slave_scl or negedge i2c_slave_rstn) begin
  if (!i2c_slave_rstn) begin
       data_counter <= '0;
       regaddr <= '0;
       data_in <= '0;
  end
  else begin
    case(curr_state)
      REGADDR: begin
        if(data_counter < 7) begin
          regaddr[data_counter] <= i2c_slave_sda_i;
          data_counter <= data_counter + 1;
        end
      end
      WRITE_DATA: begin
        if(data_counter < 7) begin
          data_in[data_counter] <= i2c_slave_sda_i;
          data_counter <= data_counter + 1;
        end
      end
      IDLE, READ_DATA: begin
        data_counter <= '0; // 进入空闲/读数据状态时复位计数器
      end
    endcase
  end
end

3. 修正状态机逻辑漏洞

原状态转移逻辑未判断从机地址是否匹配,补充关键判断逻辑:

always_comb begin
  send_ack = 0;
  reset_counter = 0;
  next_state = curr_state; // 默认保持当前状态,避免生成锁存器

  case(curr_state)
    IDLE: begin
      if (start_detect) next_state = SLAVE_ADDR;
    end

    SLAVE_ADDR: begin
      if (address_counter == 6) begin
        if(addr == slave_address) begin // 校验从机地址是否匹配
          next_state = WRITE;
          send_ack = 1; // 地址匹配后发送ACK
        end else begin
          next_state = IDLE;
        end
      end
    end

    // 其余状态逻辑按需修正,确保状态转移依赖正确的计数器值与信号
    // ...
  endcase
end

4. 修复起始/停止条件检测

原检测逻辑不符合I2C时序规范,正确的起始/停止条件需结合SCL电平判断:

// 起始条件检测:SCL为高时,SDA出现下降沿
always_ff @ (negedge i2c_slave_sda_i or negedge i2c_slave_rstn) begin
  if (!i2c_slave_rstn) begin
    start_detect <= 1'b0;
  end else if(i2c_slave_scl) begin
    start_detect <= 1'b1;
  end else begin
    start_detect <= 1'b0;
  end
end

// 停止条件检测:SCL为高时,SDA出现上升沿
always_ff @ (posedge i2c_slave_sda_i or negedge i2c_slave_rstn) begin
  if (!i2c_slave_rstn) begin
    stop_detect <= 1'b0;
  end else if(i2c_slave_scl) begin
    stop_detect <= 1'b1;
  end else begin
    stop_detect <= 1'b0;
  end
end

核心规范总结

  • 时序变量(计数器、寄存器)仅在always_ff块中驱动,组合逻辑块仅做读取判断
  • 状态机组合逻辑块必须默认保持当前状态,避免生成不必要的锁存器
  • I2C所有数据采样、状态更新必须严格同步到SCL上升沿

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 05:16:24