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
相关产品推荐
相关产品推荐

