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

结构框图如下:
image

整体代码如下:

点击查看代码
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 ;双接口的时序参考如下:
image
具体代码如下:

点击查看代码
//-----------------------------------------------------------------------------
//
//                          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,表示缓冲空。

大概的结构如下
image

工况 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)

  1. valid_src=1,ready_dst=1
  2. buffer_en = valid_src & ~buffer_full[0] & ~ready_dst = 1 & 0 & 0 = 0 → 不写缓存,数据直接直通
  3. buffer_full_en = (buffer_en | ready_dst )= 0 | 1 = 1buffer_full <= 0(保持空)
  4. ready_src = ~buffer_full[0] = 1 → 上游一直可以发数据
  5. valid_dst = (valid_src | payload_src)= 1 | 0 = 1 → 下游一直看到有效

现象:全程直通、无延迟、连续传输。 设计目的达成:正常工况不额外加延迟。

场景 二:下游突然拒收(产生背压,触发缓存)

  1. valid_src=1,ready_dst=0(下游不收了)
  2. buffer_en = valid_src & ~buffer_full[0] & ~ready_dst= 1 & 0 & 1 = 1 → 数据写入 payload_reg 缓存
  3. buffer_full_en =(buffer_en | ready_dst )= 1 | 0 = 1buffer_full <= 1(标记缓存满)
  4. ready_src = ~buffer_full[0]= ~1 = 0 → 向上游发背压,上游停止发数据
  5. valid_dst =(valid_src | payload_src)= 0 | 1 = 1 → 下游依旧看到 “有数据”

现象:数据被缓存,上游被暂停,数据不丢失。

场景 3:下游恢复接收(释放背压,读空缓存)

  1. valid_src=0,ready_dst=1(上游停发,下游恢复接收)
  2. buffer_en =valid_src & ~buffer_full[0] & ~ready_dst = 0 & 1 & 0 = 0 → 不写新数据
  3. buffer_full_en = 0 | 1 = 1buffer_full <= 0(缓存清空)
  4. ready_src = ~buffer_full[0] =~0 = 1 → 背压解除,上游可以再次发送
  5. valid_dst = 0 | 0 = 0 → 无数据,传输结束

现象:缓存数据被读完,整个模块回到空闲状态。

5.3 ful_regd_slice
HNDSHK_MODE == 0 ;该模式下对axvalid -> axvalidm path 进行打拍时序处理 ;axreadys -> axreadym path 也同
时进行一周期的打拍的时序隔离。采用双寄存器乒乓架构(缓存深度为1个完整事务),支持无气泡背靠背的传输;双接口的时序参考如下:
image

代码如下:

点击查看代码
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
//-----------------------------------------------------------------------------

大致结构图如下:
image

正向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 不会打拍的时序隔离,采用直通;
提供一个事务的缓存深度,支持无气泡背靠背传输双接口的时序参考如下:

结构如下:
image

代码如下:

点击查看代码
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(持续向上游发背压)
状态转移:保持满状态,数据不会被覆盖,不会丢失

posted on 2026-06-12 15:57  狐狸耳朵FOX  阅读(126)  评论(0)    收藏  举报

导航