背景
在现代数字系统设计中,流水线技术是提高处理器和其他硬件模块性能的关键技术。通过将复杂的操作分解成多个简单的阶段并行执行,流水线能够显著提高系统的吞吐量。然而,在实现这种技术时,数据流的管理成为一个重要的挑战。本文将介绍流水线机制及其相关信号控制,并深入探讨Spill Register的应用。
数字电路中的流水线
流水线是一种分阶段执行的设计方法,将一个复杂的操作划分为多个子操作,每个子操作由一个流水级来完成。通过让多个子操作同时进行,流水线能够在一个时钟周期内处理多个数据,从而提高运算速度。
流水线中的握手信号
在流水线设计中,valid和ready信号用于协调各个级之间的数据传输:
valid: 指示当前级的数据是否有效。
ready: 指示下一级是否准备好接收数据。
一个典型的握手协议中,当前级发出的ready_o信号可以用以下逻辑表达式表示:
ready_o = !valid_o | ready_i;
发出的valid_o信号则可以定义为:
valid_o = ready_o && valid_i;
反压机制
反压机制用于处理下游级无法接收数据的情况。当下游级未准备好时,通过将ready信号传递回上游级,通知其暂停发送数据。这一机制确保数据不会丢失,同时也防止系统溢出。
多级流水线中的信号传递
在多级流水线中,ready信号可能会形成一条长的组合逻辑路径,这会导致时序问题。为了改善时序,通常需要对ready信号进行打拍处理,使其在每一级之间有寄存器缓存。
Spill Register的引入
在设计复杂的数字系统时,常常需要处理数据流的管理,以确保在处理器或其他硬件模块中数据能够高效、可靠地传输和存储。为此,硬件设计中引入了一些特殊的模块和机制,如spill register、axi_cut、和skidbuffer,以满足高性能应用的需求。
在ara向量处理器中,给axi通路插入了多个axi_cut模块(vlsu和soc),以打断组合逻辑路径、提升总线的频率。而axi_cut模块就是给axi每个通道用了一个Spill Register。
而Spill Register中有2个寄存器,可以交替填充和排空,以处理短暂的数据流不匹配,以及减少数据传输过程中的气泡。
spill register的细节
工作机制
Spill Register由两个寄存器组成:a和b。其基本工作流程如下:
- 数据首先进入寄存器a。
- 如果下游无法接收数据,a中的数据转移至b,此时新数据可以继续进入a。
当下游恢复接收能力时,数据依次从b和a输出,恢复正常的数据流。
这种机制确保了即使在下游突然不ready的情况下,数据也不会丢失。结构
信号
a_fill
- 输入握手时可放入a
a_fill = valid_i && ready_o && (!flush_i)
a_drain
- a有数据,b不满时时可以排出a
a_drain = (a_full_q && !b_full_q) || flush_i
b_fill
- a排出并且下游不ready时可以放入b
b_fill = a_drain && (!ready_i) && (!flush_i)
b_drain
- b满并且下游ready时可以排出b
b_drain = (b_full_q && ready_i) || flush_i
valid_o
- a满或者b满时表示有效,可发出
valid_o = a_full_q | b_full_q
ready_o
- a、b不全满时表示ready,可放入
ready_o = !a_full_q || !b_full_q
- 是2个寄存器输出的组合输出
data_o
- b满时用b的数据,否则用a的数据
data_o = b_full_q ? b_data_q : a_data_q
从信号可以看出,正常情况,数据会先进到a,然后再从a输出出去;
而当下游突然不能接收时,数据会从a放到b,新到的数据会放到a,然后a、b都满了,不能再接收新的数据,ready_o=0。也就是模块可以接收突然下游不ready时上游发出的那个数据;
而当下游恢复时,先发出b中的数据,再发出a中的数据,然后再恢复到正常模式
代码
module spill_register_flushable #(
parameter type T = logic,
parameter bit Bypass = 1'b0 // make this spill register transparent
) (
input logic clk_i ,
input logic rst_ni ,
input logic valid_i ,
input logic flush_i ,
output logic ready_o ,
input T data_i ,
output logic valid_o ,
input logic ready_i ,
output T data_o
);
if (Bypass) begin : gen_bypass
assign valid_o = valid_i;
assign ready_o = ready_i;
assign data_o = data_i;
end else begin : gen_spill_reg
// The A register.
T a_data_q;
logic a_full_q;
logic a_fill, a_drain;
always_ff @(posedge clk_i or negedge rst_ni) begin : ps_a_data
if (!rst_ni)
a_data_q <= '0;
else if (a_fill)
a_data_q <= data_i;
end
always_ff @(posedge clk_i or negedge rst_ni) begin : ps_a_full
if (!rst_ni)
a_full_q <= 0;
else if (a_fill || a_drain)
a_full_q <= a_fill;
end
// The B register.
T b_data_q;
logic b_full_q;
logic b_fill, b_drain;
always_ff @(posedge clk_i or negedge rst_ni) begin : ps_b_data
if (!rst_ni)
b_data_q <= '0;
else if (b_fill)
b_data_q <= a_data_q;
end
always_ff @(posedge clk_i or negedge rst_ni) begin : ps_b_full
if (!rst_ni)
b_full_q <= 0;
else if (b_fill || b_drain)
b_full_q <= b_fill;
end
// Fill the A register when the A or B register is empty. Drain the A register
// whenever it is full and being filled, or if a flush is requested.
assign a_fill = valid_i && ready_o && (!flush_i);
assign a_drain = (a_full_q && !b_full_q) || flush_i;
// Fill the B register whenever the A register is drained, but the downstream
// circuit is not ready. Drain the B register whenever it is full and the
// downstream circuit is ready, or if a flush is requested.
assign b_fill = a_drain && (!ready_i) && (!flush_i);
assign b_drain = (b_full_q && ready_i) || flush_i;
// We can accept input as long as register B is not full.
// Note: flush_i and valid_i must not be high at the same time,
// otherwise an invalid handshake may occur
assign ready_o = !a_full_q || !b_full_q;
// The unit provides output as long as one of the registers is filled.
assign valid_o = a_full_q | b_full_q;
// We empty the spill register before the slice register.
assign data_o = b_full_q ? b_data_q : a_data_q;
`ifndef SYNTHESIS
`ifndef COMMON_CELLS_ASSERTS_OFF
flush_valid : assert property (
@(posedge clk_i) disable iff (~rst_ni) (flush_i |-> ~valid_i)) else
$warning("Trying to flush and feed the spill register simultaneously. You will lose data!");
`endif
`endif
end
endmodule
仿真
未完待续……
本文由 BWonder 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。