AXI4 REG_SLICE 设计文档
1. 整体说明
ax4_reg_slice 是一个通用地址通道寄存器打拍逻辑,可同时用于写地址通道或读地址通道。作用是将valid/ready
通路进行寄存打拍,以实现时序上的隔离,防止valid/ready的组合相关路径过长,setup 违例。该寄存器切片内部复
用了三个通用子组件:
ful_regd_slice(全寄存器切片);
fwd_regd_slice(前向寄存器切片);
rev_regd_slice(反向寄存器切片);
ful_regd_slice 是一个通用组件,可在源接口与目标接口之间提供完整的双向时序隔离,能够复用于任意 AXI 通道。
fwd_regd_slice 是一个通用组件,可在源接口与目标接口之间提供前向路径时序隔离,能够复用于任意 AXI 通道。
rev_regd_slice 是一个通用组件,可在源接口与目标接口之间提供反向路径时序隔离,能够复用于任意 AXI 通道。
2. 子系统特性列表
-
参数化设计:可通过传参进行4种打拍配置;地址/ID等位宽也可以通过传参进行配置;
-
AXI4 协议:支持AXI4协议;
-
支持模式时序打拍隔离:
1: HNDSHAK == 0 fullly registered regiester slice ( valid & ready 路径都打拍进行时序隔离) ;2:HNDSHAK == 1 registered on forward patch only (valid path 路径打拍进行时序隔离)
3:HNDSHAK == 2 registered on reverse patch only (ready path 路径打拍进行时序隔离)
4:HNDSHAK == 3 register slice bypass (直通模式,不进行打拍处理进行时序隔离)
3. 参数定义
| 参数名 | 默认值 | 描述 |
| ID_WIDTH | 4 | ID 位宽 |
| USER_WIDTH | 32 | 用户侧带信号位宽 |
| HNDSHK_MODE | 0 | 握手模式,一共4种,见子系统特性列表; |
| ADDR_WIDTH | 32 | 地址位宽 |
4. 接口描述
4.1 Clk & reset 接口
| 信号名字 | 方向 | 位宽 | 描述 |
| aclk | input | 1 | 时钟 |
| areset | input | 1 | 复位reset |
4.2 Master 接口 (输出)
| 信号名字 | 方向 | 位宽 | 参数说明 | 描述 |
| axidm | output | [ID_MAX-1 :0] | ID_MAX = ID_WIDTH -1 ;ID_WIDT可传参进行配置; | id field |
| axaddrm | output | [ADDR_MAX:0] | ADDR_MAX可传参进行配置 | address field |
| axlenm | output | [7:0] | length field | |
| axsizem | output | [2:0] | size field | |
| axburstm | output | [1:0] | burst field | |
| axlockm | output | 1 | lock field | |
| axcashem | output | [3:0] | cache field | |
| axprotm | output | [2:0] | protection field | |
| axuserm | output | [USER_MAX:0] | USER_MAX = USER_WIDTH -1 ;USER_WIDTH 可进行传参进行配置; | user field |
| axvalidm | output | 1 | transfer valid | |
| axreadym | input | 1 | ready for transfer |
4.3 Slave 接口 (输入)
| 信号名字 | 方向 | 位宽 | 参数说明 | 描述 |
| axids | input | [ID_MAX-1 :0] | ID_MAX = ID_WIDTH -1 ;ID_WIDT可传参进行配置; | id field |
| axaddrs | input | [ADDR_MAX:0] | ADDR_MAX可传参进行配置 | address field |
| axlens | input | [7:0] | length field | |
| axsizes | input | [2:0] | size field | |
| axbursts | input | [1:0] | burst field | |
| axlocks | input | 1 | lock field | |
| axcashes | input | [3:0] | cache field | |
| axprots | input | [2:0] | protection field | |
| axusers | input | [USER_MAX:0] | USER_MAX = USER_WIDTH -1 ;USER_WIDTH 可进行传参进行配置; | user field |
| axvalids | input | 1 | transfer valid | |
| axreadys | output | 1 | ready for transfer |
5. 功能原理
主要结构由三部分构成,可由参数进行控制的MUX选择器,三个HNDSHK_MODE不同模式的时序隔离模块:
rec_regs_slice/ ful_regd_slice/fwd_regd_slice
结构框图如下:

整体代码如下:
点击查看代码
module ax4_reg_slice
(
// global interconnect inputs
aresetn,
aclk,
// slave port interface
axids,
axaddrs,
axlens,
axsizes,
axbursts,
axlocks,
axcaches,
axprots,
axusers,
axvalids,
axreadys,
// master port interface
axidm,
axaddrm,
axlenm,
axsizem,
axburstm,
axlockm,
axcachem,
axprotm,
axuserm,
axvalidm,
axreadym
);
//-----------------------------------------------------------------------------
// parameters
//-----------------------------------------------------------------------------
// user parameters
parameter ID_WIDTH = 4; // width of the id field
parameter USER_WIDTH = 32; // width of the user sideband field
parameter HNDSHK_MODE = 0; // register slice handshake mode
// HNDSHK_MODE == 0, fully registered register slice
// HNDSHK_MODE == 1, registered on forward path only
// HNDSHK_MODE == 2, registered on reverse path only
// HNDSHK_MODE == 3, register slice bypass
//-----------------------------------------------------------------------------
// calculated parameters (do not modify)
//-----------------------------------------------------------------------------
localparam ID_MAX = (ID_WIDTH - 1);
localparam USER_MAX = (USER_WIDTH-1);
localparam PAYLD_WIDTH = (ID_WIDTH + USER_WIDTH + ADDR_WIDTH + 21);
localparam PAYLD_MAX = (PAYLD_WIDTH - 1);
localparam ADDR_WIDTH = (ADDR_WIDTH - 1);
localparam INT_HNDSHK_MODE = HNDSHK_MODE; // Internal register slice handshake mode
//-----------------------------------------------------------------------------
// Port definitions
//-----------------------------------------------------------------------------
// global interconnect inputs
input aresetn; // axi reset
input aclk; // axi clock
// slave port interface
input [ID_MAX:0] axids; // id field
input [ADDR_MAX:0] axaddrs; // address field
input [7:0] axlens; // length field
input [2:0] axsizes; // size field
input [1:0] axbursts; // burst field
input axlocks; // lock field
input [3:0] axcaches; // cache field
input [2:0] axprots; // protection field
input [USER_MAX:0] axusers; // user field
input axvalids; // transfer valid
output axreadys; // ready for transfer
// master port interface
output [ID_MAX:0] axidm; // id field
output [ADDR_MAX:0] axaddrm; // address field
output [7:0] axlenm; // length field
output [2:0] axsizem; // size field
output [1:0] axburstm; // burst field
output axlockm; // lock field
output [3:0] axcaches; // cache field
output [2:0] axprotm; // protection field
output [USER_MAX:0] axuserm; // user field
output axvalidm; // transfer valid
input axreadym; // ready for transfer
//-----------------------------------------------------------------------------
// Port type definitions
//-----------------------------------------------------------------------------
// global interconnect inputs
wire aresetn; // axi reset
wire aclk; // axi clock
// slave port interface
wire [ID_MAX:0] axids; // id field
wire [ADDR_MAX:0] axaddrs; // address field
wire [7:0] axlens; // length field
wire [2:0] axsizes; // size field
wire [1:0] axbursts; // burst field
wire axlocks; // lock field
wire [3:0] axcaches; // cache field
wire [2:0] axprots; // protection field
wire [USER_MAX:0] axusers; // user field
wire axvalids; // transfer valid
wire axreadys; // ready for transfer
// master port interface
wire [ID_MAX:0] axidm; // id field
wire [ADDR_MAX:0] axaddrm; // address field
wire [7:0] axlenm; // length field
wire [2:0] axsizem; // size field
wire [1:0] axburstm; // burst field
wire axlockm; // lock field
wire [3:0] axcaches; // cache field
wire [2:0] axprotm; // protection field
wire [USER_MAX:0] axuserm; // user field
wire axvalidm; // transfer valid
wire axreadym; // ready for transfer
//-----------------------------------------------------------------------------
// Internal signals
//-----------------------------------------------------------------------------
wire [PAYLD_MAX:0] payld_src; // concatenation of the inputs
wire [PAYLD_MAX:0] payld_regd; // concatenation of the registered inputs
wire [PAYLD_MAX:0] payld_fwd_regd;// concatenation of the fwd path isolated slice
wire [PAYLD_MAX:0] payld_rev_regd;// concatenation of the rev path isolated slice
wire ax_valid_regd; // valid from the fully isolated slice
wire ax_valid_fwd_regd;// valid from the fwd path isolated slice
wire ax_valid_rev_regd;// valid from the rev path isolated slice
wire ax_ready_regd; // ready from the fully isolated slice
wire ax_ready_fwd_regd;// ready from the fwd path isolated slice
wire ax_ready_rev_regd;// ready from the rev path isolated slice
//-----------------------------------------------------------------------------
// start of code
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// output axreadys;
//-----------------------------------------------------------------------------
// selection of the ready as required by the static mode parameter. If the
// mode parameter is out of bounds the output are tied to the dest input.
assign axreadys = ((INT_HNDSHK_MODE == 0) ? ax_ready_regd
:((INT_HNDSHK_MODE == 1) ? ax_ready_fwd_regd
:((INT_HNDSHK_MODE == 2) ? ax_ready_rev_regd
: axreadym)));
//-----------------------------------------------------------------------------
// Master port outputs
//-----------------------------------------------------------------------------
// expand the concatenated registered values to the master port outputs
// as required by the static mode parameter. If the mode parameter is
// out of bounds the outputs are tied to the source inputs.
assign {axidm,
axaddrm,
axlenm,
axsizem,
axburstm,
axlockm,
axcachem,
axprotm,
axuserm} = ((INT_HNDSHK_MODE == 0) ? payld_regd
:((INT_HNDSHK_MODE == 1) ? payld_fwd_regd
:((INT_HNDSHK_MODE == 2) ? payld_rev_regd
: {axids,
axaddrs,
axlens,
axsizes,
axbursts,
axlocks,
axcaches,
axprots,
axusers})));
//-----------------------------------------------------------------------------
// output axvalidm;
//-----------------------------------------------------------------------------
// selection of the valid as required by the static mode parameter. If the
// mode parameter is out of bounds the output are tied to the source input.
assign axvalidm = ((INT_HNDSHK_MODE == 0) ? ax_valid_regd
:((INT_HNDSHK_MODE == 1) ? ax_valid_fwd_regd
:((INT_HNDSHK_MODE == 2) ? ax_valid_rev_regd
: axvalids)));
//-----------------------------------------------------------------------------
// wire [PAYLD_MAX:0] payld_src;
//-----------------------------------------------------------------------------
// the inputs are concatenated to interface to the generic register set
assign payld_src = {axids,
axaddrs,
axlens,
axsizes,
axbursts,
axlocks,
axcaches,
axprots,
axusers};
generate
if (INT_HNDSHK_MODE == 0) begin
//-----------------------------------------------------------------------------
// Full Timing Isolation Register Slice
//-----------------------------------------------------------------------------
ful_regd_slice #(
.PAYLD_WIDTH(PAYLD_WIDTH)
)
u_ful_regd_slice
(
// global interconnect inputs
.aresetn (aresetn),
.aclk (aclk),
// inputs
.valid_src (axvalids),
.ready_dst (axreadym),
.payload_src (payld_src),
// outputs
.ready_src (ax_ready_regd),
.valid_dst (ax_valid_regd),
.payload_dst (payld_regd)
);
end
else if (INT_HNDSHK_MODE == 1) begin
//-----------------------------------------------------------------------------
// Forward Timing Isolation Register Slice
//-----------------------------------------------------------------------------
fwd_regd_slice #(
.PAYLD_WIDTH(PAYLD_WIDTH)
)
u_fwd_regd_slice
(
// global interconnect inputs
.aresetn (aresetn),
.aclk (aclk),
// inputs
.valid_src (axvalids),
.ready_dst (axreadym),
.payload_src (payld_src),
// outputs
.ready_src (ax_ready_fwd_regd),
.valid_dst (ax_valid_fwd_regd),
.payload_dst (payld_fwd_regd)
);
end
else if (INT_HNDSHK_MODE == 2) begin
//-----------------------------------------------------------------------------
// Reverse Timing Isolation Register Slice
//-----------------------------------------------------------------------------
rev_regd_slice #(
.PAYLD_WIDTH(PAYLD_WIDTH)
)
u_rev_regd_slice
(
// global interconnect inputs
.aresetn (aresetn),
.aclk (aclk),
// inputs
.valid_src (axvalids),
.ready_dst (axreadym),
.payload_src (payld_src),
// outputs
.ready_src (ax_ready_rev_regd),
.valid_dst (ax_valid_rev_regd),
.payload_dst (payld_rev_regd)
);
end
endgenerate
endmodule
//-----------------------------------------------------------------------------
5.1 MUX选择
输出信号是用两个MUX 分别作用于valid & ready 的不同模式的下选择,在不同的HNDSHK_MODE下
选择不同的打拍时序隔离输出。
5.2 rec_regd_slice
用于在源端和目的端接口之间提供反向路径时序隔离。它会监控并驱动主 / 从接口的握手信号,实例化一个
存储寄存器,通过 valid_src 到 valid_dst 的组合路径保证最小延迟为 1,并支持背靠背传输。
反向路径时序优化:将 ready_src 转为寄存器输出,解决 ready_dst → ready_src 的组合路径时序违例问题;
HNDSHK_MODE == 2 该模式下对 axreadys -> axreadym path 进行一周期的打拍的时序隔离,对axvalids ->
axvalidm path 不进行打拍时序处理,采用直通;缓存事务深度为1 ;双接口的时序参考如下:

具体代码如下:
点击查看代码
//-----------------------------------------------------------------------------
//
// rev_regd_slice.v
// =================
//-----------------------------------------------------------------------------
//
// Overview
// ========
//
// The rev_regd_slice is a sub-component of the reg_slice_axi that provides
// reverse-path timing isolation between the source and destination
// interfaces.
//
// The rev_regd_slice monitors and drives the handshake signals from/to
// the master and slave interfaces. The component instantiates one storage
// register and ensures a minimum latency of 1 and back to back transfer
// support by using a combinatorial path from valid_src to valid_dst.
//
// A single variable size signal array is used to enable reuse over all AXI
// channels.
//
// buffer_full is split into a bused signal in order to reduce the loading on
// the registers generating buffer_full. Each buffer_full[] signal drives no
// more than 8 multiplexor elements. buffer_full[NUM_SEL_LINES] drives the
// remainder, buffer_full[0] is used in the control logic.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Module Declaration
//-----------------------------------------------------------------------------
module rev_regd_slice
(
// global signals
aresetn,
aclk,
// Inputs
valid_src,
ready_dst,
payload_src,
// outputs
valid_dst,
ready_src,
payload_dst
);
//-----------------------------------------------------------------------------
// Parameters
//-----------------------------------------------------------------------------
parameter PAYLD_WIDTH = 1;
// calculated parameters (do not alter)
localparam PAYLD_MAX = PAYLD_WIDTH - 1;
localparam NUM_SEL_LINES = (PAYLD_WIDTH + 7) / 8;
localparam REM_SEL_LINE = (((PAYld_WIDTH - 1) % 8) + 1);
//-----------------------------------------------------------------------------
// Inputs / Outputs
//-----------------------------------------------------------------------------
// Global signals
input aclk; // global AXI clock
input aresetn; // global AXI reset
input [PAYLD_MAX:0] payload_src; // Input payload
output [PAYLD_MAX:0] payload_dst; // Output payload
// AXI muxed address input port
input valid_src; // Address/Control valid handshake
output ready_src; // Address/Control ready handshake
// AXI register address output port
output valid_dst; // Address/Control valid handshake
input ready_dst; // Address/Control ready handshake
//-----------------------------------------------------------------------------
// Port signal declarations
//-----------------------------------------------------------------------------
reg [PAYLD_MAX:0] payload_dst; // Output payload
//-----------------------------------------------------------------------------
// Internal signal declarations
//-----------------------------------------------------------------------------
reg [PAYLD_MAX:0] payload_reg; // Internal buffer
wire buffer_en; // Enable for internal buffer
wire buffer_full_en; // D-type enable for buffer_full
reg [NUM_SEL_LINES:0] buffer_full; // Indicates when the buffer is in use
//-----------------------------------------------------------------------------
// BEGINNING OF MAIN CODE
//-----------------------------------------------------------------------------
// Use an enable on the buffer to save power
assign buffer_en = (valid_src & ~buffer_full[0] & ~ready_dst);
always @(posedge aclk)
begin : p_buffer_seq
if (buffer_en)
payload_reg <= payload_src;
end
// Set the buffer_full flag when the buffer is loaded. Clear the flag when
// the stored data is accepted
assign buffer_full_en = (buffer_en | ready_dst);
// Register buffer_full
always @(negedge aresetn or posedge aclk)
begin : p_buffer_full_seq
if (~aresetn)
buffer_full <= {NUM_SEL_LINES+1{1'b0}};
else if (buffer_full_en)
buffer_full <= {NUM_SEL_LINES+1{buffer_en}};
end
// Registered ready output
assign ready_src = ~buffer_full[0];
// Combinatorial valid and payload outputs
assign valid_dst = (valid_src | buffer_full[0]);
always @(buffer_full or payload_reg or payload_src)
begin : p_payload_mux
integer i; // Loop counter through the select lines
integer j; // Loop counter through the bits in each mux array
integer base; // Increment through the bit number of the payload
base = 0;
// Mux for the full bytes of payload
for (i=1; i<NUM_SEL_LINES; i=i+1)
begin
for (j=base; j<(base+8); j=j+1)
payload_dst[j] = (buffer_full[i] ? payload_reg[j] : payload_src[j]);
base = (8 * i);
end
// Mux for the remaining bits of payload
for (j=base; j<(base+REM_SEL_LINE); j=j+1)
payload_dst[j] = (buffer_full[NUM_SEL_LINES] ? payload_reg[j]
: payload_src[j]);
end
endmodule // rev_regd_slice
//-----------------------------------------------------------------------------
关键信号说明:
ready_src(向上游反馈就绪):直接由 ~buffer_full[0] 驱动,是打拍后的寄存器输出。\
valid_dst (向下游输出有效):组合逻辑实现,只要上游发数据(valid_src)或缓冲寄存器有数
据(buffer_full[0]),就向下游输出有效。
buffer_en 仅在以下场景进行更新:
1:上游发送数据(valid_src = 1);
2: 缓冲寄存器为空(~buffer_full[0] =1 );
3: 下游未就绪(!ready_dst =1 );
-->>>>>>> 此时将上游数据锁存到payload_reg,避免数据丢失;
** buffer_full 缓冲状态更新:**
1:更新时机:buffer_en=1(数据写入缓冲)或 ready_dst=1(下游取走数据);
2:更新逻辑:将 buffer_full 设为 buffer_en 的值:
buffer_en = 1:写入数据,buffer_full 置 1,表示缓冲满;
buffer_en =0 :下游取走数据,buffer_full 置 0,表示缓冲空。
大概的结构如下

工况 1:下游正常接收 ready_dst=1(主流场景)
上游发数据、下游也收 → 数据直接直通走 payload_src → payload_dst 不需要存缓存,追求零延迟、高吞吐,这是设计优先目标。
工况 2:下游拒收 ready_dst=0(背压场景)
上游还在发数据,但下游不收 → 数据不能直接往下走,必须临时存起来,否则数据丢失 → 此时 buffer_en=1,把数据锁进 payload_reg 缓存。
工况 3:缓存已经满 buffer_full=1
不再允许写入新数据,向上游拉低 ready_src 发背压,上游停止发送。
**场景说明: **
场景 一:上下游正常传输(下游一直就绪 ready_dst=1)
valid_src=1,ready_dst=1buffer_en = valid_src & ~buffer_full[0] & ~ready_dst = 1 & 0 & 0 = 0→ 不写缓存,数据直接直通buffer_full_en = (buffer_en | ready_dst )= 0 | 1 = 1→buffer_full <= 0(保持空)ready_src = ~buffer_full[0] = 1→ 上游一直可以发数据valid_dst = (valid_src | payload_src)= 1 | 0 = 1→ 下游一直看到有效
现象:全程直通、无延迟、连续传输。 设计目的达成:正常工况不额外加延迟。
场景 二:下游突然拒收(产生背压,触发缓存)
valid_src=1,ready_dst=0(下游不收了)buffer_en = valid_src & ~buffer_full[0] & ~ready_dst= 1 & 0 & 1 = 1→ 数据写入 payload_reg 缓存buffer_full_en =(buffer_en | ready_dst )= 1 | 0 = 1→buffer_full <= 1(标记缓存满)ready_src = ~buffer_full[0]= ~1 = 0→ 向上游发背压,上游停止发数据valid_dst =(valid_src | payload_src)= 0 | 1 = 1→ 下游依旧看到 “有数据”
现象:数据被缓存,上游被暂停,数据不丢失。
场景 3:下游恢复接收(释放背压,读空缓存)
valid_src=0,ready_dst=1(上游停发,下游恢复接收)buffer_en =valid_src & ~buffer_full[0] & ~ready_dst = 0 & 1 & 0 = 0→ 不写新数据buffer_full_en = 0 | 1 = 1→buffer_full <= 0(缓存清空)ready_src = ~buffer_full[0] =~0 = 1→ 背压解除,上游可以再次发送valid_dst = 0 | 0 = 0→ 无数据,传输结束
现象:缓存数据被读完,整个模块回到空闲状态。
5.3 ful_regd_slice
HNDSHK_MODE == 0 ;该模式下对axvalid -> axvalidm path 进行打拍时序处理 ;axreadys -> axreadym path 也同
时进行一周期的打拍的时序隔离。采用双寄存器乒乓架构(缓存深度为1个完整事务),支持无气泡背靠背的传输;双接口的时序参考如下:

代码如下:
点击查看代码
module ful_regd_slice
(
// global signals
aresetn,
aclk,
// Inputs
valid_src,
ready_dst,
payload_src,
// outputs
valid_dst,
ready_src,
payload_dst
);
//-----------------------------------------------------------------------------
// parameters
//-----------------------------------------------------------------------------
// user defined parameters
parameter PAYLD_WIDTH = 2;
// calculated parameters (do not alter)
localparam PAYLD_MAX = (PAYLD_WIDTH - 1);
localparam NUM_SEL_LINES = (PAYLD_WIDTH + 7) / 8;
localparam REM_SEL_LINE = (((PAYLD_WIDTH - 1) % 8) + 1);
//-----------------------------------------------------------------------------
// port definitions
//-----------------------------------------------------------------------------
// global interconnect inputs
input aresetn; // AXI global reset
input aclk; // AXI global clock
// inputs
input valid_src; // transfer valid from the source
input ready_dst; // destination ready to accept transfer
input [PAYLD_MAX:0] payload_src;// transfer payload from source
// outputs
output valid_dst; // transfer valid to the destination
output ready_src; // source ready to accept transfer
output [PAYLD_MAX:0] payload_dst;// transfer payload from source
//-----------------------------------------------------------------------------
// port type declarations
//-----------------------------------------------------------------------------
wire aresetn; // AXI global reset
wire aclk; // AXI global clock
// inputs
wire valid_src; // transfer valid from the source
wire ready_dst; // destination ready to accept transfer
wire [PAYLD_MAX:0] payload_src;// transfer payload from source
// outputs
wire valid_dst; // transfer valid to the destination
wire ready_src; // source ready to accept transfer
reg [PAYLD_MAX:0] payload_dst;// transfer payload to destination
//-----------------------------------------------------------------------------
// internal signals declarations
//-----------------------------------------------------------------------------
wire enable_a; // enable the load of payload register A
wire enable_b; // enable the load of payload register B
wire load_sel_en;// enable the load selection to change
wire nxt_valid_a;// next value for valid_a
wire nxt_valid_b;// next value for valid_b
reg [PAYLD_MAX:0] payload_reg_a;// storage register A
reg [PAYLD_MAX:0] payload_reg_b;// storage register B
reg valid_a; // status of the register set A
reg valid_b; // status of the register set A
wire iready_src; // internal version of ready_src
wire ivalid_dst; // internal version of valid_dst
reg load_b; // load selection for the next transfer
reg [NUM_SEL_LINES:0] sel_b; // select driver for payload_dst
wire sel_b_en; // enable for select driver register
//-----------------------------------------------------------------------------
// start of code
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// output valid_dst;
//-----------------------------------------------------------------------------
assign valid_dst = ivalid_dst;
//-----------------------------------------------------------------------------
// output ready_src;
//-----------------------------------------------------------------------------
assign ready_src = iready_src;
//-----------------------------------------------------------------------------
// output payload_dst;
//-----------------------------------------------------------------------------
// If sel_b is driven high, drive the contents of payload B onto the output.
// If sel_b is not driven high, drive the contents of payload A onto the output.
always @(sel_b or payload_reg_a or payload_reg_b)
begin : p_payload_mux
integer i; // Loop counter through the select lines
integer j; // Loop counter through the bits in each mux array
integer base; // Increment through the bit number of the payload
base = 0;
// Mux for the full bytes of payload
for (i=1; i<NUM_SEL_LINES; i=i+1)
begin
for (j=base; j<(base+8); j=j+1)
payload_dst[j] = (sel_b[i] ? payload_reg_b[j] : payload_reg_a[j]);
base = (8 * i);
end
// Mux for the remaining bits of payload
for (j=base; j<(base+REM_SEL_LINE); j=j+1)
payload_dst[j] = (sel_b[NUM_SEL_LINES] ? payload_reg_b[j] : payload_reg_a[j]);
end
//-----------------------------------------------------------------------------
// wire enable_a;
//-----------------------------------------------------------------------------
// Enable signal to load payload register A. This can happen when selected
// by the load_b signal and a handshake on the source interface. The signal
// iready_src is only driven high when either of the payload registers are
// empty, which ensures the payload cannot be overridden.
assign enable_a = ~load_b & valid_src & iready_src;
//-----------------------------------------------------------------------------
// wire enable_b;
//-----------------------------------------------------------------------------
// Enable signal to load payload register B. This can happen when selected
// by the load_b signal and a handshake on the source interface. The signal
// iready_src is only driven high when either of the payload registers are
// empty, which ensures the payload cannot be overridden.
assign enable_b = load_b & valid_src & iready_src;
//-----------------------------------------------------------------------------
// wire load_sel_en;
//-----------------------------------------------------------------------------
// Enable signal for the register that selects which payload register will
// be loaded next. The control is updated when a handshake occurs on the
// source interface. The signal iready_src is only driven high when either of
// the payload registers are empty, which ensures the payload cannot be
// overridden.
assign load_sel_en = valid_src & iready_src;
//-----------------------------------------------------------------------------
// wire nxt_valid_a;
//-----------------------------------------------------------------------------
// Next value for the payload register A status flag. If the payload
// register A is loaded, as flagged by the enable_a signal, it will contain
// a valid payload. If payload A is driven on the destination interface
// and a valid-ready handshake occurs, then the payload is invalid.
assign nxt_valid_a = (enable_a ? 1'b1
: ((~sel_b[0] & ivalid_dst & ready_dst) ? 1'b0
: valid_a));
//-----------------------------------------------------------------------------
// wire nxt_valid_b;
//-----------------------------------------------------------------------------
// Next value for the payload register B status flag. If the payload
// register B is loaded, as flagged by the enable_b signal, it will contain
// a valid payload. If payload B is driven on the destination interface
// and a valid-ready handshake occurs, then the payload is invalid.
assign nxt_valid_b = (enable_b ? 1'b1
: ((sel_b[0] & ivalid_dst & ready_dst) ? 1'b0
: valid_b));
//-----------------------------------------------------------------------------
// reg payload_reg_a;
//-----------------------------------------------------------------------------
// Holds transfer payload information. A payload is loaded when a
// handshake occurs on the source interface and this register has been
// selected. See enable_a for details.
always @(posedge aclk)
begin : p_payload_reg_a
if (enable_a)
payload_reg_a <= payload_src;
end // block : p_payload_reg_a
//-----------------------------------------------------------------------------
// reg payload_reg_b;
//-----------------------------------------------------------------------------
// Holds transfer payload information. A payload is loaded when a
// handshake occurs on the source interface and this register has been
// selected. See enable_b for details.
always @(posedge aclk)
begin : p_payload_reg_b
if (enable_b)
payload_reg_b <= payload_src;
end // block : p_payload_reg_b
//-----------------------------------------------------------------------------
// reg valid_a;
//-----------------------------------------------------------------------------
// Status of payload register A. This register is driven high when a
// payload is loaded in to payload register A. It is driven low when the
// stored payload has been driven on the destination interface, and a
// valid-ready handshake has occurred. See nxt_valid_a for more details.
always @(posedge aclk or negedge aresetn)
begin : p_valid_a
if (~aresetn)
valid_a <= 1'b0;
else
valid_a <= nxt_valid_a;
end // block : p_valid_a
//-----------------------------------------------------------------------------
// reg valid_b;
//-----------------------------------------------------------------------------
// Status of payload register B. This register is driven high when a
// payload is loaded in to payload register B. It is driven low when the
// stored payload has been driven on the destination interface, and a
// valid-ready handshake has occurred. See nxt_valid_b for more details.
always @(posedge aclk or negedge aresetn)
begin : p_valid_b
if (~aresetn)
valid_b <= 1'b0;
else
valid_b <= nxt_valid_b;
end // block : p_valid_b
//-----------------------------------------------------------------------------
// wire iready_src;
//-----------------------------------------------------------------------------
// Calculated ready status of the ful_regd_slice component. If either payload
// register A or payload register B are empty, then the ful_regd_slice
// component is ready to accept a transfer payload.
assign iready_src = ~valid_a | ~valid_b;
//-----------------------------------------------------------------------------
// wire ivalid_dst;
//-----------------------------------------------------------------------------
// Calculated valid status of the ful_regd_slice component. If either payload
// register A or payload register B contain a valid payload, then the
// ful_regd_slice component can issue a valid transfer payload.
assign ivalid_dst = valid_a | valid_b;
//-----------------------------------------------------------------------------
// reg load_b;
//-----------------------------------------------------------------------------
// Control flag to select in which payload register to load the next
// transfer payload. The control toggled to indicate an alternate selection
// between the two payload registers. See load_sel_en for more details.
always @(posedge aclk or negedge aresetn)
begin : p_load_b
if (~aresetn)
load_b <= 1'b0;
else
if (load_sel_en)
load_b <= ~load_b;
end // block : p_load_b
//-----------------------------------------------------------------------------
// wire sel_b_en;
//-----------------------------------------------------------------------------
// Determines when to toggle the output select. Only toggles when a
// register contains a valid payload in order to reduce toggle power.
assign sel_b_en = ((~sel_b[0] & nxt_valid_b & (~valid_a | ready_dst)) |
(sel_b[0] & nxt_valid_a & (~valid_b | ready_dst)));
//-----------------------------------------------------------------------------
// reg sel_b;
//-----------------------------------------------------------------------------
// Control flag to select from which payload register to drive the
// destination output. The control toggled to indicate an alternate selection
// between the two payload registers. See drive_sel_en for more details.
always @(posedge aclk or negedge aresetn)
begin : p_sel_b
if (~aresetn)
sel_b <= {NUM_SEL_LINES+1{1'b0}};
else
if (sel_b_en)
sel_b <= ~sel_b;
end // block : p_sel_b
endmodule // ful_regd_slice
//-----------------------------------------------------------------------------
大致结构图如下:

正向valid信号打拍说明: valid_a/b 是打拍后的状态;上游的valid_src 和 paylaod_src ,必须等到下一时钟沿才会锁存
到 payload_reg_a/b;同时valid_a/b置为成1;下游的valid_dst 是valid_a/b的或,所以正好比valid_src晚一周期;
关键信号说明:
ready_src 向上游反馈就绪):只要两个寄存器中有一个为空(~valid_a 或 ~valid_b ),就向上游反馈"就绪",允许上游发送数据;
valid_dst(向下游发送有效):只要两个寄存器中有一个存了有效数据(valid_a 或 valid_b),就向下游发送“有效”,表示很模块
有数据可以发送。
valid_a 或 valid_b: 指示寄存器是否有有效数据,写使能时候(enable /enable_b )将valid_a/valid_b 置为成1,表示寄存器存入
了有效数据;读完成时(输出选择对应寄存器 + 下游握手成功 ivalid_dst & ready_dst):将 valid_a/valid_b 置 0,表示数据已被
下游取走,寄存器变为空。
**load_b **逻辑:作为乒乓切换标志,每次上游握手成功(valid_src & iready_src )时候翻转;
sel_b 逻辑:切换时机:当另一个寄存器写入了新数据(nxt_valid_b/nxt_valid_a),且当前选择的寄存器数据已被取走或下游就绪时,
切换输出选择,保证输出始终是最新的有效数据。
模块工作流程:
1:初始状态: valid_a = 0 , valid_b = 0 , load_b = 0 , sel_b = 0 ;两个寄存器均为空;
2:上游发送第一个数据: load_b =0 ,因此enable_a =1 ,数据写入payload_reg_a , valid_a = 1 ; 同时load_b 翻转到1,下次写
入payload_reg_b;
3:模块输出第一个数据:sel_b = 0 ,选择payload_reg_a 输出,valid_dst=1 ;下游接收成功(ready_dst = 1 ),则valid_a 被置0;
4:上游发送第二个数据: load_b =1 ,因此enable_b =1 ,数据写入payload_reg_b, valid_b= 1 ; 同时load_b翻转到0,下次写
入payload_reg_a;
5:模块输出第二个数据:当payloa_reg_a 数据被取走后,sel_b 切换到1,选择payload_reg_b输出,valid_dst =1 ;下游写入payload_reg_a;
6:循环往复:交替写入/读出两个寄存器,实现无气泡的流水传输,同时打断了valid_src 和valid_dst 、ready_dst到ready_src 的组合
路径,优化时序。
**5.4 fwd_regd_slice **
HNDSHK_MODE == 1 ; 该模式下对axvalid -> axvalidm path 进行打拍时序处理。axreadys -> axreadym path 不会打拍的时序隔离,采用直通;
提供一个事务的缓存深度,支持无气泡背靠背传输双接口的时序参考如下:
结构如下:

代码如下:
点击查看代码
module fwd_regd_slice
(
// global interconnect inputs
aresetn,
aclk,
// inputs
valid_src,
ready_dst,
payload_src,
// outputs
ready_src,
valid_dst,
payload_dst
);
//-----------------------------------------------------------------------------
// parameters
//-----------------------------------------------------------------------------
// user defined parameters
parameter PAYLD_WIDTH = 2;
// calculated parameters (do not alter)
localparam PAYLD_MAX = (PAYLD_WIDTH - 1);
//-----------------------------------------------------------------------------
// Port definitions
//-----------------------------------------------------------------------------
// global interconnect inputs
input aresetn; // AXI global reset
input aclk; // AXI global clock
// inputs
input valid_src; // transfer valid from the source
input ready_dst; // destination ready to accept transfer
input [PAYLD_MAX:0] payload_src;// transfer payload from source
// outputs
output ready_src; // source ready to accept transfer
output valid_dst; // transfer valid to the destination
output [PAYLD_MAX:0] payload_dst;// transfer payload for destination
//-----------------------------------------------------------------------------
// Port type definitions
//-----------------------------------------------------------------------------
wire aresetn; // AXI global reset
wire aclk; // AXI global clock
// inputs
wire valid_src; // transfer valid from the source
wire ready_dst; // destination ready to accept transfer
wire [PAYLD_MAX:0] payload_src;// transfer payload from source
// outputs
wire valid_dst; // transfer valid to the destination
wire ready_src; // source ready to accept transfer
reg [PAYLD_MAX:0] payload_dst;// transfer payload for destination
//-----------------------------------------------------------------------------
// Internal signals
//-----------------------------------------------------------------------------
wire payload_en; // enable for storage register
wire valid_dst_en; // enable for valid_dst
reg ivalid_dst; // internal version of valid_dst
//-----------------------------------------------------------------------------
// start of code
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// output valid_dst;
//-----------------------------------------------------------------------------
assign valid_dst = ivalid_dst;
//-----------------------------------------------------------------------------
// output ready_src;
//-----------------------------------------------------------------------------
// the register slice is ready if the destination is ready to accept a
// transfer, or if the slice doesn't hold a transfer.
assign ready_src = ready_dst | ~ivalid_dst;
//-----------------------------------------------------------------------------
// output [PAYLD_MAX:0] payload_dst;
//-----------------------------------------------------------------------------
// Payload register. This register holds the payload transfer. It is
// updated when a new transfer payload is accepted on the source interface.
// See payload_en for more details.
always @(posedge aclk)
begin : p_payload_dst
if (payload_en)
payload_dst <= payload_src;
end // block : p_payload_dst
//-----------------------------------------------------------------------------
// wire payload_en;
//-----------------------------------------------------------------------------
// the transfer should be loaded in to the registers if the source is
// attempting to transfer and the slice is empty, or if the source is
// attempting to transfer and the slice is full but the destination is
// removing the current transfer
assign payload_en = ((valid_src & ~ivalid_dst) |
(valid_src & ivalid_dst & ready_dst));
//-----------------------------------------------------------------------------
// wire valid_dst_en;
//-----------------------------------------------------------------------------
// the register slice will contain a transfer if:
// 1. the source is attempting to transfer
// 2. if the slice is full but the destination cannot accept it.
assign valid_dst_en = (valid_src | ready_dst);
//-----------------------------------------------------------------------------
// wire ivalid_dst;
//-----------------------------------------------------------------------------
// Payload status flag. This register indicates whether the payload register
// holds a valid transfer payload. The value is driven high if a new
// payload transfer is loaded or if a current payload is not accepted by
// the destination interface. See valid_dst_nxt for more details.
always @(posedge aclk or negedge aresetn)
begin : p_ivalid_dst
if (~aresetn)
ivalid_dst <= 1'b0;
else if (valid_dst_en)
ivalid_dst <= valid_src;
end // block : p_ivalid_dst
endmodule // fwd_regd_slice
//-----------------------------------------------------------------------------
关键信号说明:
valid_dst(向下游输出有效):直接由内部状态 ivalid_dst驱动,只要寄存器中有有效数据,就向下游输出valid=1;
ready_src (向上游反馈就绪):若寄存器为空(~ivalid_dst):直接向上游反馈ready =1 ,允许写入新数据;
若寄存器已满(ivalid_dst):只有当下游就绪(ready_dst = 1),才向上游反馈ready = 1,允许新数据覆盖旧数据(同时旧数据被下游取走)。
payload_en: 写使能信号,用于更新payload信息;写使能payload_en在两种场景下有效:寄存器为空(~ivalid_dst),上游发送数据(valid_src= 1 ):直接写入;
寄存器已满(ivalid_dst),且下游就绪(ready_dst =1):写入新数据的同时,旧数据被下游取走,实现 “背靠背” 传输。
故 payload_en = ((valid_src & ~ivalid_dst) | (valid_src & ivalid_dst & ready_dst));
ivalid_dst : 有效状态标志更新;
更新时机:当上游发送数据(valid_src= 1)或下游接收数据(ready_dst =1)时更新状态;
更新逻辑:将ivalid_dst更新为当前的valid_src值:
- 若上游发送数据(valid_src =1 ):ivalid_dst 置 1,表示寄存器中存入了有效数据;
- 若上游不发送数据(valid_src = 0)但下游接收数据(ready_dst=1 ):ivalid_dst置 0,表示寄存器中的数据已被下游取走,变为空。
场景详解
场景一:空闲状态 + 上游发数据 + 下游就绪(正常传输)
初始状态:ivalid_dst = 0(空闲)
输入条件:valid_src = 1,ready_dst = 1
逻辑执行:
payload_en = (valid_src & ~ivalid_dst) |(valid_src & ivalid_dst & ready_dst)= (1 & 1) | (1 & 0 & 1) = 1 → 数据写入payload_dst
valid_dst_en = (valid_src | ready_dst)= 1 | 1 = 1 → ivalid_dst <= valid_src = 1
输出结果:
valid_dst = 1(向下游输出有效)
ready_src = (ready_dst | ~ivalid_dst)= 1 | 0 = 1(继续向上游反馈就绪)
状态转移:保持满状态(无气泡,支持背靠背传输)
场景二:空闲状态 + 上游发数据 + 下游未就绪(下游反压)
初始状态:ivalid_dst = 0(空闲)
** 输入条件**:valid_src = 1,ready_dst = 0
逻辑执行:
payload_en = (valid_src & ~ivalid_dst) |(valid_src & ivalid_dst & ready_dst)= (1 & 1) | (1 & 0 & 0) = 1 → 数据写入payload_dst
valid_dst_en = (valid_src | ready_dst)= (1 | 0) = 1 → ivalid_dst <= valid_src = 1
输出结果:
valid_dst = 1(向下游输出有效,等待下游接收)
ready_src = (ready_dst | ~ivalid_dst)= 0 | 0 = 0(向上游发背压,禁止上游继续发送)
状态转移:进入满状态,数据暂存在寄存器中
场景 三:满状态 + 上游发数据 + 下游就绪(背靠背连续传输)
初始状态:ivalid_dst = 1(满)
输入条件:valid_src = 1,ready_dst = 1
逻辑执行:
payload_en = (valid_src & ~ivalid_dst) |(valid_src & ivalid_dst & ready_dst)=(1 & 0) | (1 & 1 & 1) = 1 → 新数据覆盖旧数据
valid_dst_en = (valid_src | ready_dst)= (1 | 1) = 1 → ivalid_dst <= valid_src = 1
输出结果:
valid_dst = 1(持续向下游输出有效)
ready_src =(ready_dst | ~ivalid_dst)= 1 | 0 = 1(继续向上游反馈就绪)
状态转移:保持满状态,实现无气泡连续传输
场景 四:满状态 + 上游不发数据 + 下游就绪(数据被取走)
初始状态:ivalid_dst = 1(满)
** 输入条件**:valid_src = 0,ready_dst = 1
逻辑执行:
payload_en = (valid_src & ~ivalid_dst) |(valid_src & ivalid_dst & ready_dst)=(0 & 0) | (0 & 1 & 1) = 0 → 不写入新数据
valid_dst_en = (valid_src | ready_dst)= (0 | 1) = 1 → ivalid_dst <= valid_src = 0
输出结果:
valid_dst = 0(无数据输出)
ready_src =(ready_dst | ~ivalid_dst)= 1 | 1 = 1(向上游反馈就绪,允许再次发送)
状态转移:回到空闲状态
场景 五:满状态 + 上游不发数据 + 下游未就绪(持续反压)
初始状态:ivalid_dst = 1(满)
输入条件:valid_src = 0,ready_dst = 0
逻辑执行:
payload_en = (valid_src & ~ivalid_dst) |(valid_src & ivalid_dst & ready_dst) = (0 & 0) | (0 & 1 & 0) = 0 → 不写入新数据
valid_dst_en = (valid_src | ready_dst)= (0 | 0) = 0 → 不更新ivalid_dst,保持原值
输出结果:
valid_dst = 1(持续向下游输出有效,等待下游接收)
ready_src =(ready_dst | ~ivalid_dst)= 0 | 0 = 0(持续向上游发背压)
状态转移:保持满状态,直到下游就绪
场景 六:满状态 + 上游发数据 + 下游未就绪(反压叠加)
初始状态:ivalid_dst = 1(满)
输入条件:valid_src = 1,ready_dst = 0
逻辑执行:
payload_en = (valid_src & ~ivalid_dst) |(valid_src & ivalid_dst & ready_dst) =(1 & 0) | (1 & 1 & 0) = 0 → 禁止写入新数据
valid_dst_en = (valid_src | ready_dst)= (1 | 0) = 1 → ivalid_dst <= valid_src = 1
输出结果:
valid_dst = 1(持续向下游输出有效)
ready_src =(ready_dst | ~ivalid_dst)= 0 | 0 = 0(持续向上游发背压)
状态转移:保持满状态,数据不会被覆盖,不会丢失
浙公网安备 33010602011771号