Verilog实现I2C遇多常量驱动错误,求排查及实现方案
错误原因分析
Error (10028)的核心问题是同一个寄存器rComStarted被两个独立的always块同时驱动。Verilog综合规则明确要求:一个寄存器变量只能由一个always块赋值,多个驱动源会让综合器无法确定该寄存器的最终状态,因此抛出冲突错误。
你的代码里,第一个always块在SDAIn下降沿触发时给rComStarted置1,第二个always块在SDAIn上升沿触发时给它置0,这就违反了“单驱动源”规则。
实现预期功能的最佳方案
修正思路
- 将两个触发条件合并到同一个
always块中,用posedge SDAIn or negedge SDAIn作为敏感列表,同时捕获SDA的上升沿和下降沿。 - 时序逻辑中必须使用非阻塞赋值
<=,避免仿真和综合结果不一致的问题(你之前用的阻塞赋值=更适合组合逻辑)。 - 在块内判断当前触发的是上升沿还是下降沿,结合SCL的电平完成寄存器的置位/复位。
修正后的代码
module SafeI2CSlave(input SDAIn, output SDAOut, input SCL); reg rComStarted; reg rChipSelect; assign SDAOut = rComStarted; // 合并触发条件到一个always块,使用非阻塞赋值 always@(posedge SDAIn or negedge SDAIn) begin if(SCL) begin // 仅当SCL为高时处理SDA边沿 if(negedge SDAIn) begin // SDA下降沿:置位rComStarted rComStarted <= 1'h1; end else begin // SDA上升沿:拉低rComStarted rComStarted <= 1'h0; end end end endmodule
兼容性优化说明
部分仿真器对always块内直接判断negedge/posedge的支持有限,可通过缓存SDA前一状态的方式准确识别边沿,修改后代码如下:
module SafeI2CSlave(input SDAIn, output SDAOut, input SCL); reg rComStarted; reg rChipSelect; reg rSDAIn_prev; // 缓存SDAIn的上一拍状态 assign SDAOut = rComStarted; // 实时更新SDAIn的前一状态 always@(posedge SCL or posedge SDAIn or negedge SDAIn) begin rSDAIn_prev <= SDAIn; end always@(posedge SDAIn or negedge SDAIn) begin if(SCL) begin if(rSDAIn_prev && !SDAIn) begin // 从高到低:识别下降沿 rComStarted <= 1'h1; end else if(!rSDAIn_prev && SDAIn) begin // 从低到高:识别上升沿 rComStarted <= 1'h0; end end end endmodule
内容的提问来源于stack exchange,提问作者Tzanker
相关产品推荐
相关产品推荐

