SystemVerilog中SRAM索引时钟上升沿切换数据异常问题排查
指令缓存索引切换的SystemVerilog时序问题分析
问题描述
我设计了一个指令缓存,遇到的问题是:时钟上升沿切换SRAM索引时,输出仍为前一索引的数据。单独测试SRAM模块时表现正常,但将其实例化到data_array和tag_array模块后,输出始终保留前一索引的数据,无法定位原因。
在top模块中,输入Program Counter(PC)后,若将提取出的index连线连接到data_array的索引端口会出现该问题,但直接将pc[1:0](索引部分)传入data_array模块则正常。现询问该现象在SystemVerilog(Vivado环境)中的成因,以及这是否属于竞争条件,哪种行为是正确的?
相关代码示例
测试平台代码
module top_tb; // Parameters // Ports logic clk = 0; logic rst = 0; logic flush = 0; logic write_en = 0; logic [3:0] i_data; logic [9:0] pc; logic [3:0] o_data; top top_dut ( .clk (clk ), .rst (rst ), .flush (flush ), .write_en (write_en ), .i_data (i_data ), .pc (pc ), .o_data (o_data) ); initial begin begin rst = 1; flush = 0; write_en = 0; i_data = 0; pc = 0; #10; // Flush cycle keep 16 cycle rst = 0; flush = 0; // no change write_en = 0; i_data = 0; pc = 0; #45; // end of flush write_en = 1; i_data = 1; pc = 1; #10; write_en = 1; i_data = 2; pc = 2; #10; write_en = 0; end end always #5 clk = ! clk ; endmodule
异常波形对应的top模块代码
`timescale 1ns / 1ps module top( input logic clk, input logic rst, input logic flush, input logic write_en, input logic [3:0] i_data, input logic [9:0] pc, output logic [3:0] o_data ); // Ports logic [1:0] index; assign index = pc[1:0]; test test_ex ( .clk (clk), .rst (rst), .i_flush (flush), .i_wr_en (write_en), .i_data (i_data), .i_index (index), .o_data (o_data) ); endmodule
正常波形对应的top模块代码
`timescale 1ns / 1ps module top( input logic clk, input logic rst, input logic flush, input logic write_en, input logic [3:0] i_data, input logic [9:0] pc, output logic [3:0] o_data ); // Ports logic [1:0] index; assign index = pc[1:0]; test test_ex ( .clk (clk), .rst (rst), .i_flush (flush), .i_wr_en (write_en), .i_data (i_data), .i_index (pc[1:0]), .o_data (o_data) ); endmodule
test模块代码
module test( input logic clk, input logic rst, input logic i_flush, input logic i_wr_en, input logic [3:0] i_data, input logic [1:0] i_index, output logic [3:0] o_data ); logic flush_q; logic [15:0] memory [3:0]; logic [1:0] flush_index; logic [1:0] wr_index; logic [15:0] wr_data; assign wr_data = flush_q ? '0 : i_data; assign wr_index = flush_q ? flush_index : i_index; always_ff@(posedge clk)begin if(i_wr_en | flush_q)begin memory[wr_index] <= wr_data; end else begin o_data <= memory[wr_index]; end end always_ff @(posedge clk) begin if(rst) begin flush_q <= 1'b1; flush_index <= '0; end else begin if(flush_q) begin if(flush_index == 3) begin flush_index <= '0; flush_q <= 1'b0; end else begin flush_index <= flush_index + 1'b1; end end else begin flush_q <= i_flush; end end end endmodule
成因分析与结论
问题成因
这不属于竞争条件,而是Vivado工具在综合/仿真阶段引入的信号路径延迟差异导致的:
- 直接传递
pc[1:0]时,综合器会将该路径视为组合逻辑直通,时钟上升沿到来时,PC的最新值低两位会被test模块内的逻辑直接采样,无额外延迟。 - 通过中间
index连线传递时,尽管代码是纯连线赋值,但工具可能为了布线或时序优化插入微小缓冲,导致index的更新晚于pc[1:0]到达test模块的i_index端口。此时test模块内的wr_index会采样到前一个时钟周期的index值,进而读取旧的内存数据,出现输出不更新的现象。
正确行为判断
直接传递pc[1:0]的行为是符合设计预期的正确行为,它保证了时钟上升沿能准确采样到PC的最新索引值。中间连线导致的延迟是工具引入的非预期时序偏差,并非代码逻辑错误,但会破坏功能正确性。
解决建议
- 优先直接使用原始信号的位切片作为模块输入,避免不必要的中间组合逻辑连线。
- 若必须保留中间信号,可添加
(* dont_touch *)属性标记该连线,禁止工具插入额外缓冲:(* dont_touch *) logic [1:0] index; assign index = pc[1:0]; - 添加时序约束,确保
pc到i_index的路径满足建立时间要求,避免信号在时钟边沿附近变化。
内容的提问来源于stack exchange,提问作者Kerim Turak
相关产品推荐
相关产品推荐

