DSP上的SRIO编程及调试指南

0 说明

基于 SRIO Programming and Performance Data on Keystone DSP 对其进行中文翻译及自己的说明。

这份文件是关于Keystone DSP上的Serial RapidIO (SRIO)编程和性能数据的应用报告。报告详细介绍了SRIO在Keystone DSP上的配置、编程、性能测试以及一些编程技巧。

主要内容包括:

  1. SRIO配置:介绍了如何配置SRIO端口模式(1x/2x/4x)、设备ID、Load Store Unit (LSU)、消息和数据包DMA、中断设置、回环模式以及数据包转发。

  2. SRIO传输编程:讨论了如何使用LSU进行数据传输,包括直接I/O、门铃和维护传输。提供了基于优先级的RX缓冲区分配和SRIO寄存器检查的示例代码。

  3. 其他SRIO编程考虑:包括匹配ACKID、软重置以及一些编程技巧和提示。

  4. SRIO性能数据:提供了在不同操作条件下测量的性能数据,包括传输开销、不同类型数据包的吞吐量、不同链路配置下的吞吐量以及不同内存缓冲对SRIO吞吐量的影响。

  5. 附录A:提供了一个示例项目的介绍,该项目包含了用于配置Keystone设备外设的通用配置文件,以及用于不同测试案例的源代码。

下面开始对该文件进行详细分析

1 引言

RapidIO 是一种非专有的、高带宽的系统级互连。它是一种包交换互连,主要用作芯片间和板卡间的内部系统接口,性能水平达到每秒千兆字节。Keystone DSP 上的 SRIO(串行 RapidIO)非常灵活且功能强大;它为客户提供了许多模式和选项,SRIO 用户指南详细介绍了这些内容。本应用笔记提供了一些关于 SRIO 编程的补充信息。示例代码/项目随本应用说明一起提供。关于 SRIO 编程的示例代码基于寄存器层 CSL(芯片支持层)。大部分代码使用如下定义的 SRIO 寄存器指针:

#include <ti/csl/cslr_srio.h>
#include <ti/csl/cslr_device.h>
CSL_SrioRegs * srioRegs = (CSL_SrioRegs *)CSL_SRIO_CONFIG_REGS;

2 SRIO配置

2.1 1x/2x/4x配置

KeyStone Family SRIO端口配置使用PLM端口路径控制寄存器来配置SRIO端口模式,PATH_CONFIG字段用于配置使用多少信道,PATH_MODE字段用于选择1x、2x或4x端口的组合。下图展示了端口配置的详细描述。

下面是配置它的示例代码:

// 定义一个枚举类型,用于配置SRIO的1x、2x或4x路径模式
typedef enum
{
    // 1xLaneA模式:1条Lane A,位移操作设置路径配置和模式
    SRIO_PATH_CTL_1xLaneA = 
    (1 << CSL_SRIO_RIO_PLM_SP_PATH_CTL_PATH_CONFIGURATION_SHIFT) |
    (0 << CSL_SRIO_RIO_PLM_SP_PATH_CTL_PATH_MODE_SHIFT),
    
    // 1xLaneA_1xLaneB模式:1条Lane A和1条Lane B
    SRIO_PATH_CTL_1xLaneA_1xLaneB = 
    (2 << CSL_SRIO_RIO_PLM_SP_PATH_CTL_PATH_CONFIGURATION_SHIFT) |
    (0 << CSL_SRIO_RIO_PLM_SP_PATH_CTL_PATH_MODE_SHIFT),
    
    // 2xLaneAB模式:2条Lane AB
    SRIO_PATH_CTL_2xLaneAB = 
    (2 << CSL_SRIO_RIO_PLM_SP_PATH_CTL_PATH_CONFIGURATION_SHIFT) |
    (1 << CSL_SRIO_RIO_PLM_SP_PATH_CTL_PATH_MODE_SHIFT),
    
    // 更多模式,每种模式都通过位移操作设置路径配置和模式
    // ...(此处省略以简化)

    // 4xLaneABCD模式:4条Lane ABCD
    SRIO_PATH_CTL_4xLaneABCD = 
    (4 << CSL_SRIO_RIO_PLM_SP_PATH_CTL_PATH_CONFIGURATION_SHIFT) |
    (4 << CSL_SRIO_RIO_PLM_SP_PATH_CTL_PATH_MODE_SHIFT)
} SRIO_1x2x4x_Path_Control;

// 配置SRIO 1x 2x或4x路径模式的函数
void Keystone_SRIO_set_1x2x4x_Path(SRIO_1x2x4x_Path_Control srio_1x2x4x_path_control)
{
    // 这个寄存器是一个全局寄存器,即使它可以从任何一个端口访问。
    // 因此,不需要从每个端口编程,它基本上是一个单一的寄存器。
    srioRegs->RIO_PLM[0].RIO_PLM_SP_PATH_CTL = 
    (srioRegs->RIO_PLM[0].RIO_PLM_SP_PATH_CTL & (~SRIO_1x2x4x_PATH_CONTROL_MASK)) |
    srio_1x2x4x_path_control;
}

// ...(此处省略)

// 调用函数,设置SRIO为1xLaneA模式
Keystone_SRIO_set_1x2x4x_Path(SRIO_PATH_CTL_1xLaneA);

在这段代码中,Keystone_SRIO_set_1x2x4x_Path 函数用于设置SRIO的路径模式。通过调用这个函数并传递一个枚举值作为参数,可以配置SRIO使用不同的路径配置和模式。例如,最后一行代码调用这个函数并设置SRIO为1xLaneA模式。

在SRIO(Serial RapidIO)的配置中,端口根据所使用的“最低”通道(Lane)进行编号。这里的“最低”通道指的是在多通道配置中,端口所使用的通道中编号最小的一个。根据这个规则,端口的编号和其可能的配置如下:
如果一个端口使用通道A,那么该端口的编号是0,并且该端口可能是1x、2x或4x配置。
如果最低的通道是B,那么端口的编号是1,并且该端口必须是1x配置。
如果最低的通道是C,那么端口的编号是2,并且该端口可能是1x或2x配置。
如果最低的通道是D,那么端口的编号是3,并且该端口必须是1x配置。


以下是一些不同配置下的端口编号示例:
对于1xLaneA配置,只有一个端口,编号为0。
对于1xLaneA_1xLaneB配置,有两个端口,编号分别为0和1。
对于2xLaneAB配置,只有一个端口,编号为0。
对于1xLaneA_1xLaneB_1xLaneC_1xLaneD配置,有四个端口,编号分别为0、1、2和3。
对于2xLaneAB_1xLaneC_1xLaneD配置,有三个端口,编号分别为0、1和2。
对于1xLaneA_1xLaneB_2xLaneCD配置,有三个端口,编号分别为0、1和2。
对于2xLaneAB_2xLaneCD配置,有两个端口,编号分别为0和2。
对于4xLaneABCD配置,只有一个端口,编号为0。
这些配置和编号规则有助于在编程时正确地设置和管理SRIO端口和通道。

在SRIO配置完成后,我们必须轮询SP_ERR_STATUS寄存器来检查端口状态是否为PORT_OK。

2.2 设备ID配置

KeyStone系列的SRIO支持16个本地设备ID。

下面是配置设备id的示例代码

// 定义一个结构体,用于配置SRIO的设备ID路由
typedef struct { 
    /* PATTERN提供与传入目标ID一对一比较的16位模式 */
    Uint16 idPattern; 
    /* MATCH指示目标ID的哪16位与PATTERN进行比较 */
    Uint16 idMatchMask; 
    /* 将与此BRR匹配的目标ID的维护请求/保留数据包路由到LLM */
    Uint8 routeMaintenance; 
} SRIO_Device_ID_Routing_Config;

// 设置SRIO设备ID的函数
void Keystone_SRIO_set_device_ID(
    SRIO_Device_ID_Routing_Config *device_id_routing_config,
    Uint32 uiDeviceIdNum)
{
    int i;
    // TLM_SP(n)_BRR_x_PATTERN_MATCH寄存器持有15个允许的目标ID,
    // 注意第一个寄存器不被使用。我们使用RIO_BASE_ID寄存器来保存第一个ID
    srioRegs->RIO_BASE_ID = device_id_routing_config[0].idPattern | // 大ID
                            ((device_id_routing_config[0].idPattern & 0xFF) << 16); // 小ID
    uiDeviceIdNum = _min2(SRIO_MAX_DEVICEID_NUM, uiDeviceIdNum);
    for (i = 1; i < uiDeviceIdNum; i++)
    {
        // 请注意,必须启用SRIO块5~8,以使相应的RIO_TLM[0:3]生效
        srioRegs->RIO_TLM[i/4].brr[i&3].RIO_TLM_SP_BRR_CTL = 
            (device_id_routing_config[i].routeMaintenance << 
             CSL_SRIO_RIO_TLM_SP_BRR_1_CTL_ROUTE_MR_TO_LLM_SHIFT) |
            (0 << CSL_SRIO_RIO_TLM_SP_BRR_1_CTL_PRIVATE_SHIFT) |
            (1 << CSL_SRIO_RIO_TLM_SP_BRR_1_CTL_ENABLE_SHIFT);
        srioRegs->RIO_TLM[i/4].brr[i&3].RIO_TLM_SP_BRR_PATTERN_MATCH = 
            (device_id_routing_config[i].idPattern << 
             CSL_SRIO_RIO_TLM_SP_BRR_1_PATTERN_MATCH_PATTERN_SHIFT) |
            (device_id_routing_config[i].idMatchMask << 
             CSL_SRIO_RIO_TLM_SP_BRR_1_PATTERN_MATCH_MATCH_SHIFT);
    }
}

// 这里可以设置最多16个设备ID
SRIO_Device_ID_Routing_Config dsp0_device_ID_routing_config[] =
{
    // idPattern idMatchMask routeMaintenance
    {DSP0_SRIO_BASE_ID + 0, 0xFFFF, 1},
    {DSP0_SRIO_BASE_ID + 1, 0xFFFF, 1},
    {DSP0_SRIO_BASE_ID + 2, 0xFFFF, 1},
    {DSP0_SRIO_BASE_ID + 3, 0xFFFF, 1},
    {DSP0_SRIO_BASE_ID + 4, 0xFFFF, 1},
    {DSP0_SRIO_BASE_ID + 5, 0xFFFF, 1},
    {DSP0_SRIO_BASE_ID + 6, 0xFFFF, 1},
    {DSP0_SRIO_BASE_ID + 7, 0xFFFF, 1},
};

// ...

// 调用函数,配置设备ID
Keystone_SRIO_set_device_ID(&dsp0_device_ID_routing_config, 
                            sizeof(dsp0_device_ID_routing_config) / sizeof(SRIO_Device_ID_Routing_Config));

这段代码定义了一个结构体 SRIO_Device_ID_Routing_Config,用于配置Serial RapidIO(SRIO)的设备ID路由。该结构体包含三个字段:idPattern 用于指定与传入目标ID一对一比较的16位模式,idMatchMask 用于指示目标ID的哪16位与idPattern进行比较,以及routeMaintenance 用于指示是否将匹配此路由寄存器的维护请求/保留数据包路由到LLM(逻辑链路管理器)。
Keystone_SRIO_set_device_ID 函数用于设置SRIO的设备ID。它接受一个指向SRIO_Device_ID_Routing_Config结构体数组的指针和一个表示设备ID数量的无符号整数。函数首先设置RIO_BASE_ID寄存器,然后遍历数组,为每个设备ID配置相应的路由寄存器。
在函数的最后,有一个示例数组dsp0_device_ID_routing_config,它定义了8个设备ID的路由配置。然后调用Keystone_SRIO_set_device_ID函数,将这个数组和设备ID的数量传递给它,以配置这些设备ID。 

四个TLM端口基础路由寄存器集属于四个端口数据路径,因此必须为寄存器集启用相应的BLK5~BLK8。如果仅启用部分端口数据路径块,那么支持的dest ID数量将减少。例如,如果只启用BLK5(端口0数据路径),那么只能支持4个dest ID。再举一个例子,如果您使用配置0,模式1(1x模式),通常只需启用BLK5,但如果您想支持超过4个ID,则必须启用BLK6~BLK8。
在为一条端口数据路径配置的寄存器集中的ID,如果PRIVATE位=0,则可以用于其他端口。
PATTERN和MATCH字段是16位。在仅使用8位DeviceID的系统上,可以通过适当地设置MATCH字段来从比较中移除上八位。在混合使用8位和16位DeviceID的系统上,可以使用全部16位进行比较,在这种情况下,由硬件在比较前用八个零位前缀接收到的8位destID。

2.3 LSU(加载存储单元)设置

在KeyStone家族的SRIO中,Direct IO、门铃(doorbell)和维护(maintenance)传输是通过LSU(加载/存储单元)模块实现的。总共有8个LSU。每个LSU寄存器集代表一个传输请求,为了支持多个挂起的DirectIO传输请求,设有两组影子LSU寄存器,每组有16个影子寄存器集。影子寄存器组0由LSU0至LSU3共享,影子寄存器组1由LSU4至LSU7共享。

LSU_SETUP_REG0用于为每个LSU设置影子寄存器的数量。下表显示了影子寄存器的配置。
 

LSU_SETUP_REG1用于选择在LSU传输完成时设置的中断标志位。


如果LSU_SETUP_REG1中的LSU_EDMA位被清零为0,则使用传输的源ID映射索引来选择LSU中断状态寄存器0(LSU0_ICSR)中的一个标志位。源ID索引是TLM端口基路由寄存器(n)模式与匹配寄存器中16个设备ID的索引。例如,如果传输使用设备ID 1作为源ID,那么当传输完成时,LSU中断状态寄存器0(LSU0_ICSR)的第1位将被设置。

如果LSU_SETUP_REG1中的LSU_EDMA位被设置为1,则使用传输的LSU编号来选择LSU中断状态寄存器1中的一个标志位。例如,如果LSU编号为2,则LSU中断状态寄存器1(LSU1_ICSR)的第2位被设置。

请注意,只有当LSU被禁用而外设被启用时,LSU_SETUP_REG0和LSU_SETUP_REG1才是可编程的。如果SRIO初始化代码首先禁用SRIO,这些寄存器同样无法正确写入,以下代码显示了操作过程。

typedef enum
{
    SRIO_LSU_SHADOW_REGS_SETUP_4_4_4_4,    // SRIO LSU影子寄存器设置4_4_4_4
    SRIO_LSU_SHADOW_REGS_SETUP_5_5_5_1,    // SRIO LSU影子寄存器设置5_5_5_1
    SRIO_LSU_SHADOW_REGS_SETUP_5_5_4_2,    // SRIO LSU影子寄存器设置5_5_4_2
    SRIO_LSU_SHADOW_REGS_SETUP_5_5_3_3,    // SRIO LSU影子寄存器设置5_5_3_3
    SRIO_LSU_SHADOW_REGS_SETUP_5_4_4_3,    // SRIO LSU影子寄存器设置5_4_4_3
    SRIO_LSU_SHADOW_REGS_SETUP_6_6_3_1,    // SRIO LSU影子寄存器设置6_6_3_1
    // ... 其他设置省略
    SRIO_LSU_SHADOW_REGS_SETUP_9_3_2_2     // SRIO LSU影子寄存器设置9_3_2_2
} SRIO_LSU_Shadow_Registers_Setup;        // SRIO LSU影子寄存器设置

typedef enum {
    LSU_INT_DRIVE_BY_SRCID = 0,            // LSU中断由源ID驱动,值为0
    LSU_INT_DRIVE_BY_LSU_NUM               // LSU中断由LSU编号驱动
} SRIO_LSU_Interrupt_setup;               // SRIO LSU中断设置

typedef struct
{
    SRIO_LSU_Shadow_Registers_Setup lsuGrp0ShadowRegsSetup;  // LSU组0的影子寄存器设置
    SRIO_LSU_Shadow_Registers_Setup lsuGrp1ShadowRegsSetup;  // LSU组1的影子寄存器设置
    SRIO_LSU_Interrupt_setup lsuIntSetup[8];                 // 8个LSU中断设置
} SRIO_LSU_Cfg;                            // SRIO LSU配置

// ... 代码段省略

/* 全局启用SRIO中使用的块,包括MMR块 */
Keystone_SRIO_GlobalEnable();

/* LSU设置寄存器仅在LSU禁用且外设启用时可编程 */
if(srio_cfg->lsu_cfg)
{
    /* 在LSU之间设置影子寄存器的分配 */
    srioRegs->RIO_LSU_SETUP_REG0 = 
        (srio_cfg->lsu_cfg->lsuGrp0ShadowRegsSetup << CSL_SRIO_RIO_LSU_SETUP_REG0_SHADOW_GRP0_SHIFT) |
        (srio_cfg->lsu_cfg->lsuGrp1ShadowRegsSetup << CSL_SRIO_RIO_LSU_SETUP_REG0_SHADOW_GRP1_SHIFT);

    /* 根据LSU编号或源ID设置LSU中断 */
    cfgValue = 0;
    for(i=0; i<SRIO_MAX_LSU_NUM; i++)
    { 
        cfgValue |= srio_cfg->lsu_cfg->lsuIntSetup[i] << i; 
    } 
    srioRegs->RIO_LSU_SETUP_REG1 = cfgValue; 
} 

/* 启用其他可选块 */
Keystone_SRIO_enable_blocks(&srio_cfg->blockEn);

这段代码是配置串行快速IO(SRIO)的负载存储单元(LSU)的设置。代码首先定义了两个枚举类型和一个结构体类型,然后进行了一些配置操作。
SRIO_LSU_Shadow_Registers_Setup 枚举类型定义了多种LSU影子寄存器的配置选项,每个选项代表不同的寄存器分配方式。
SRIO_LSU_Interrupt_setup 枚举类型定义了LSU中断的两种配置方式:基于源ID(LSU_INT_DRIVE_BY_SRCID)和基于LSU编号(LSU_INT_DRIVE_BY_LSU_NUM)。
SRIO_LSU_Cfg 结构体类型包含了LSU影子寄存器的配置和中断设置。
在代码的主要部分,首先调用了 Keystone_SRIO_GlobalEnable() 函数来全局启用SRIO,包括MMR块。然后,如果 srio_cfg->lsu_cfg 非空,代码将配置LSU影子寄存器的分配,并通过循环设置中断配置。最后,调用 Keystone_SRIO_enable_blocks() 函数启用其他可选的块。

2.4 消息和数据包DMA设置

在KeyStone家族的SRIO(Serial RapidIO)中,类型11(消息)和类型9(数据流)使用数据包DMA(Direct Memory Access)在DSP内部传输和接收数据。
在SRIO中,消息传输(Message TX)专门有16个队列,这些队列的编号从672到687。这些队列用于管理消息数据的传输,确保数据能够高效地在DSP和外部设备之间传输。
数据包DMA是一种硬件机制,它允许数据在不需要CPU直接介入的情况下,在内存和传输介质(如SRIO接口)之间移动。这种机制可以显著提高数据传输的效率,减少CPU的负载,从而提高系统的整体性能。在使用这些队列时,通常需要进行配置,包括队列的大小、传输模式、中断设置等。

在KeyStone家族的SRIO中,由于有16个发送队列(TX队列)和4个端口,我们需要将发送队列映射到端口,并为每个队列设置优先级。RIO_TX_QUEUE_SCH_INFO 寄存器用于配置TX队列的输出端口和CRF(Critical Request Flow)标志以确定优先级。PACKET_DMA_TX_CHANNEL_SCHEDULER_CONFIG_REG 寄存器用于配置类型11(消息)和类型9(数据流)的输出数据包优先级。以下代码展示了如何进行这些配置。
请注意,优先级不能设置为3,因为消息响应(PRIO)的优先级必须比发送消息的优先级高1,以防止死锁的发生。

// 定义一个结构体,用于存储SRIO传输队列的调度信息
typedef struct 
{ 
    Uint8 outputPort;    // 输出端口
    Uint8 priority;      // 优先级
    Uint8 CRF;           // CRF,可能是某种标志或配置信息
} SRIO_TX_Queue_Sch_Info; 

// 配置Keystone设备的SRIO传输队列
void Keystone_SRIO_TX_Queue_Cfg( 
    SRIO_TX_Queue_Sch_Info * TX_Queue_Sch_Info, // 指向传输队列调度信息的指针
    Uint32 uiNumTxQueue)                         // 传输队列的数量
{ 
    int i; 
    Uint32 uiMask, uiShift, uiRegIndex; 
    // 对于SRIO,优先级3是最高的,0是最低的
    // 对于PktDMA通道,优先级0是最高的,3是最低的
    Uint32 mapSrioPriToTxChPri[4]={3,2,1,0}; 
    // 将传输队列的数量限制为最小值,以防止超出设备的最大通道数
    uiNumTxQueue= _min2(SRIO_PKTDMA_MAX_CH_NUM, uiNumTxQueue);
    // 遍历每个队列
    for(i=0; i< uiNumTxQueue; i++)
    {
        // 计算寄存器索引、位移和掩码
        uiRegIndex= i/4;
        uiShift= (i&3)*8;
        uiMask= 0xFF<<uiShift;
        // 更新设备寄存器中的传输队列调度信息
        srioRegs->RIO_TX_QUEUE_SCH_INFO[uiRegIndex] =
        (srioRegs->RIO_TX_QUEUE_SCH_INFO[uiRegIndex]&(~uiMask)) // 清除字段
        |((TX_Queue_Sch_Info[i].CRF
        |(TX_Queue_Sch_Info[i].outputPort<<4))<<uiShift);
        // PRIO字段在TX_QUEUE_SCH_INFOx中是只读的,
        // 实际生效的优先级信息来自于PKTDMA TX通道
        srioDmaTxChPriority[i] = 
        mapSrioPriToTxChPri[TX_Queue_Sch_Info[i].priority];
    }
}

// 定义一个数组,包含多达16个传输队列的配置信息
SRIO_TX_Queue_Sch_Info TX_Queue_Sch_Info[]=
{
    // 输出端口 优先级 CRF
    {0, 0, 0},
    {1, 0, 0},
    {2, 0, 0},
    {3, 0, 0},
    {0, 0, 1},
    {1, 0, 1},
    {2, 0, 1},
    {3, 0, 1},
    {0, 1, 0},
    {1, 1, 0},
    {2, 1, 0},
    {3, 1, 0},
    {0, 1, 1},
    {1, 1, 1},
    {2, 1, 1},
    {3, 1, 1}
};

// 应用传输队列的配置
Keystone_SRIO_TX_Queue_Cfg(&TX_Queue_Sch_Info, 
    sizeof(TX_Queue_Sch_Info)/sizeof(SRIO_TX_Queue_Sch_Info));

这段代码定义了一个结构体SRIO_TX_Queue_Sch_Info,用于存储SRIO发送队列的调度信息,包括输出端口、优先级和CRF标志。然后,它实现了一个函数Keystone_SRIO_TX_Queue_Cfg,用于配置SRIO的发送队列。
在Keystone_SRIO_TX_Queue_Cfg函数中,首先定义了一个数组mapSrioPriToTxChPri,用于将SRIO优先级映射到Packet DMA TX通道的优先级。在SRIO中,优先级3是最高的,而在Packet DMA通道中,优先级0是最高的,因此需要进行映射。
函数遍历传入的发送队列信息数组TX_Queue_Sch_Info,并根据数组中的信息配置每个队列。每个RIO_TX_QUEUE_SCH_INFO寄存器控制4个队列,因此需要计算寄存器索引uiRegIndex和位移uiShift。使用位掩码uiMask来清除相应的字段,然后设置输出端口和CRF标志。
由于TX_QUEUE_SCH_INFOx寄存器中的PRIO字段是只读的,实际的优先级信息来自Packet DMA TX通道。因此,代码设置了srioDmaTxChPriority[i]数组,其中包含了映射后的优先级。
在代码的最后部分,定义了一个TX_Queue_Sch_Info数组,其中包含了多达16个发送队列的配置信息。然后调用Keystone_SRIO_TX_Queue_Cfg函数,将这个数组传递给它,以便配置SRIO的发送队列。

下图显示了SRIO类型9和类型11数据包接收数据路径。

有20个接收流(Rx flows)。 在SRIO中的关键配置是根据SrcId(源ID)、DstId(目标ID)、Mailbox(邮箱)、Letter(信件)、StreamId(流ID)、COS(服务类别)等将Rx消息映射到一个流。

类型11使用RIO_RXU_MAP_L和RIO_RXU_MAP_H来配置根据邮箱、信件和设备ID的映射;类型9使用RIO_RXU_TYPE9_MAP0、RIO_RXU_TYPE9_MAP1和RIO_RXU_TYPE9_MAP2来配置根据COS、流ID和设备ID的映射。类型11和类型9共享相同的RIO_RXU_MAP_QID用于接收流和目标队列选择。请注意,当目标队列设置为0x1FFF时,RX目标队列将由RX流中的配置决定。以下是消息接收配置的示例代码。

/* 定义一个结构体,用于确定数据包被推送到哪个流和队列 */
typedef struct {
    /* 将匹配的消息映射到流ID和可选的目的队列ID */
    Uint8 flowId;            // 流ID
    Uint16 destQuID;         // 目的队列ID
    /* 通用字段匹配 */
    Uint16 dstId;            // 目的设备ID
    Uint8 dstProm;           // 目的设备优先级
    Uint16 srcId;            // 源设备ID
    Uint8 srcProm;           // 源设备优先级
    Uint8 tt;                // 传输类型
    /* 类型11消息字段匹配 */
    Uint8 mbx;               // 消息邮箱
    Uint8 mbxMask;           // 消息邮箱掩码
    Uint8 ltr;               // 长途路由
    Uint8 ltrMask;           // 长途路由掩码
    Uint8 segMap;            // 段映射
    /* 类型9消息字段匹配 */
    Uint8 cos;               // 服务类别
    Uint8 cosMask;           // 服务类别掩码
    Uint16 streamId;         // 流ID
    Uint16 streamMask;       // 流掩码
} SRIO_RX_Message_Map;

/* 配置消息与PacketDMA流和队列之间的映射 */
void Keystone_map_SRIO_RX_message(SRIO_RX_Message_Map * srio_message_map, Uint32 uiNumMessageMap) {
    int i;
    uiNumMessageMap = _min2(SRIO_MAX_MSG_MAP_ENTRY_NUM, uiNumMessageMap); // 限制映射条目的数量
    for (i = 0; i < uiNumMessageMap; i++) {
        // 配置接收单元映射寄存器低32位
        srioRegs->RXU_MAP[i].RIO_RXU_MAP_L = 
            (srio_message_map[i].ltrMask << CSL_SRIO_RIO_RXU_MAP_L_LTR_MASK_SHIFT) | 
            (srio_message_map[i].mbxMask << CSL_SRIO_RIO_RXU_MAP_L_MBX_MASK_SHIFT) | 
            (srio_message_map[i].ltr << CSL_SRIO_RIO_RXU_MAP_L_LTR_SHIFT) | 
            (srio_message_map[i].mbx << CSL_SRIO_RIO_RXU_MAP_L_MBX_SHIFT) | 
            (srio_message_map[i].srcId << CSL_SRIO_RIO_RXU_MAP_L_SRCID_SHIFT);
        // 配置接收单元映射寄存器高32位
        srioRegs->RXU_MAP[i].RIO_RXU_MAP_H = 
            (srio_message_map[i].dstId << CSL_SRIO_RIO_RXU_MAP_H_DEST_ID_SHIFT) | 
            (srio_message_map[i].dstProm << CSL_SRIO_RIO_RXU_MAP_H_DEST_PROM_SHIFT) | 
            (srio_message_map[i].tt << CSL_SRIO_RIO_RXU_MAP_H_TT_MASK) | 
            (srio_message_map[i].srcProm << CSL_SRIO_RIO_RXU_MAP_H_SRC_PROM_SHIFT) | 
            (srio_message_map[i].segMap << CSL_SRIO_RIO_RXU_MAP_H_SEG_MAP_SHIFT);
        // 配置类型9消息的映射寄存器
        srioRegs->RXU_TYPE9_MAP[i].RIO_RXU_TYPE9_MAP0 = 
            (srio_message_map[i].cosMask << CSL_SRIO_RIO_RXU_TYPE9_MAP0_COS_MASK_SHIFT) | 
            (srio_message_map[i].cos << CSL_SRIO_RIO_RXU_TYPE9_MAP0_COS_SHIFT) | 
            (srio_message_map[i].srcId << CSL_SRIO_RIO_RXU_TYPE9_MAP0_SRCID_SHIFT);
        srioRegs->RXU_TYPE9_MAP[i].RIO_RXU_TYPE9_MAP1 = 
            (srio_message_map[i].dstId << CSL_SRIO_RIO_RXU_TYPE9_MAP1_DEST_ID

这段代码主要用于配置一个消息映射,以确定数据包接收流和队列。

下面是这段代码的简要说明:

结构体定义 (SRIO_RX_Message_Map):
此结构体用于存储与SRIO消息相关的各种字段,如流ID、目标队列ID、源/目标设备ID、优先级、传输类型等。它还包含用于匹配特定类型消息的字段,例如类型11消息的字段(mailbox、letter等)和类型9消息的字段(服务类别、流ID等)。
函数 Keystone_map_SRIO_RX_message:
此函数用于将消息映射到PacketDMA流和队列。它接受一个指向SRIO_RX_Message_Map数组和一个表示映射条目数量的整数。函数首先确保映射条目数量不超过硬件支持的最大值(SRIO_MAX_MSG_MAP_ENTRY_NUM)。然后,它遍历映射数组,并将每个条目的信息写入相应的硬件寄存器,以配置消息到流和队列的映射。
消息映射数组 (DSP0_message_map):
这是一个SRIO_RX_Message_Map类型的数组,包含了预定义的消息映射配置。
每个条目定义了一个流ID、目标队列ID以及其他用于匹配接收消息的字段。
在这个例子中,只有目标设备ID用于确定接收流ID,而目标队列ID在流配置中设定。
配置调用:
代码的最后部分调用了Keystone_map_SRIO_RX_message函数,传递了DSP0_message_map数组和其大小,以配置硬件的消息映射。

2.5 中断设置

SRIO(Serial RapidIO)生成112个中断事件;这些事件被映射到24个中断输出,这些输出可以路由到DSP核心或DMA(直接存储器访问)。

所有的112个中断都可以映射到INTDST0~15。64个门铃事件可以根据INTERRUPT_CTL寄存器的设置映射到INTDST0~15或INTDST16~23。如果INTERRUPT_CTL=0,门铃事件将映射到INTDST16~23;如果INTERRUPT_CTL=1,门铃事件同样映射到INTDST16~23。
请注意,中断节流默认是启用的。中断节流要求DSP核心在每次中断服务后重写INTDSTn_RATE_CNT寄存器以启用下一个中断,否则,无论内部中断状态如何变化,中断都不会再次触发。这就是用户通常只看到一个SRIO中断的常见原因。如果对于特定的INTDST不希望使用中断节流,可以使用INTDST_RATE_DIS寄存器来禁用。
所有与消息相关的中断或事件都由Packet DMA和QMSS(队列管理子系统)处理。

下面是显示SRIO中断设置的示例代码。

/*Word index of the Interrupt Routing Registers*/ 
typedef enum 
{ 
DOORBELL0_ICRR1 = (0x00/4), 
DOORBELL0_ICRR2 = (0x04/4), 
DOORBELL1_ICRR1 = (0x0C/4), 
DOORBELL1_ICRR2 = (0x10/4), 
DOORBELL2_ICRR1 = (0x18/4), 
DOORBELL2_ICRR2 = (0x1C/4), 
DOORBELL3_ICRR1 = (0x24/4), 
DOORBELL3_ICRR2 = (0x28/4), 
LSU_SRCID_ICRR1 = (0x30/4), 
LSU_SRCID_ICRR2 = (0x34/4), 
LSU_SRCID_ICRR3 = (0x38/4), 
LSU_SRCID_ICRR4 = (0x3C/4), 
LSU_ICRR1 = (0x40/4), 
ERR_RST_EVNT_ICRR1 = (0x50/4), 
ERR_RST_EVNT_ICRR2 = (0x54/4), 
ERR_RST_EVNT_ICRR3 = (0x58/4) 
}SRIO_ICRR_Index; 
typedef enum 
{ 
/* SRIO interrupt source constant, 
high 16 bits is the ICRR register index, 
lower 16 bits is the offset of the field in the register*/ 
DOORBELL0_0_INT = ((DOORBELL0_ICRR1 << 16) | 0x0000), 
DOORBELL0_1_INT = ((DOORBELL0_ICRR1 << 16) | 0x0004), 
DOORBELL0_2_INT = ((DOORBELL0_ICRR1 << 16) | 0x0008), 
DOORBELL0_3_INT = ((DOORBELL0_ICRR1 << 16) | 0x000C), 
DOORBELL0_4_INT = ((DOORBELL0_ICRR1 << 16) | 0x0010), 
DOORBELL0_5_INT = ((DOORBELL0_ICRR1 << 16) | 0x0014), 
DOORBELL0_6_INT = ((DOORBELL0_ICRR1 << 16) | 0x0018), 
DOORBELL0_7_INT = ((DOORBELL0_ICRR1 << 16) | 0x001C), 
DOORBELL0_8_INT = ((DOORBELL0_ICRR2 << 16) | 0x0000), 
DOORBELL0_9_INT = ((DOORBELL0_ICRR2 << 16) | 0x0004), 
DOORBELL0_10_INT = ((DOORBELL0_ICRR2 << 16) | 0x0008), 
DOORBELL0_11_INT = ((DOORBELL0_ICRR2 << 16) | 0x000C),
DOORBELL0_12_INT = ((DOORBELL0_ICRR2 << 16) | 0x0010), 
DOORBELL0_13_INT = ((DOORBELL0_ICRR2 << 16) | 0x0014), 
DOORBELL0_14_INT = ((DOORBELL0_ICRR2 << 16) | 0x0018), 
DOORBELL0_15_INT = ((DOORBELL0_ICRR2 << 16) | 0x001C), 
DOORBELL1_0_INT = ((DOORBELL1_ICRR1 << 16) | 0x0000), 
DOORBELL1_1_INT = ((DOORBELL1_ICRR1 << 16) | 0x0004), 
DOORBELL1_2_INT = ((DOORBELL1_ICRR1 << 16) | 0x0008), 
DOORBELL1_3_INT = ((DOORBELL1_ICRR1 << 16) | 0x000C), 
DOORBELL1_4_INT = ((DOORBELL1_ICRR1 << 16) | 0x0010), 
DOORBELL1_5_INT = ((DOORBELL1_ICRR1 << 16) | 0x0014), 
DOORBELL1_6_INT = ((DOORBELL1_ICRR1 << 16) | 0x0018), 
DOORBELL1_7_INT = ((DOORBELL1_ICRR1 << 16) | 0x001C), 
DOORBELL1_8_INT = ((DOORBELL1_ICRR2 << 16) | 0x0000), 
DOORBELL1_9_INT = ((DOORBELL1_ICRR2 << 16) | 0x0004), 
DOORBELL1_10_INT = ((DOORBELL1_ICRR2 << 16) | 0x0008), 
DOORBELL1_11_INT = ((DOORBELL1_ICRR2 << 16) | 0x000C), 
DOORBELL1_12_INT = ((DOORBELL1_ICRR2 << 16) | 0x0010), 
DOORBELL1_13_INT = ((DOORBELL1_ICRR2 << 16) | 0x0014), 
DOORBELL1_14_INT = ((DOORBELL1_ICRR2 << 16) | 0x0018), 
DOORBELL1_15_INT = ((DOORBELL1_ICRR2 << 16) | 0x001C), 
DOORBELL2_0_INT = ((DOORBELL2_ICRR1 << 16) | 0x0000), 
DOORBELL2_1_INT = ((DOORBELL2_ICRR1 << 16) | 0x0004), 
DOORBELL2_2_INT = ((DOORBELL2_ICRR1 << 16) | 0x0008), 
DOORBELL2_3_INT = ((DOORBELL2_ICRR1 << 16) | 0x000C), 
DOORBELL2_4_INT = ((DOORBELL2_ICRR1 << 16) | 0x0010), 
DOORBELL2_5_INT = ((DOORBELL2_ICRR1 << 16) | 0x0014), 
DOORBELL2_6_INT = ((DOORBELL2_ICRR1 << 16) | 0x0018), 
DOORBELL2_7_INT = ((DOORBELL2_ICRR1 << 16) | 0x001C), 
DOORBELL2_8_INT = ((DOORBELL2_ICRR2 << 16) | 0x0000), 
DOORBELL2_9_INT = ((DOORBELL2_ICRR2 << 16) | 0x0004), 
DOORBELL2_10_INT = ((DOORBELL2_ICRR2 << 16) | 0x0008), 
DOORBELL2_11_INT = ((DOORBELL2_ICRR2 << 16) | 0x000C), 
DOORBELL2_12_INT = ((DOORBELL2_ICRR2 << 16) | 0x0010), 
DOORBELL2_13_INT = ((DOORBELL2_ICRR2 << 16) | 0x0014), 
DOORBELL2_14_INT = ((DOORBELL2_ICRR2 << 16) | 0x0018), 
DOORBELL2_15_INT = ((DOORBELL2_ICRR2 << 16) | 0x001C), 
DOORBELL3_0_INT = ((DOORBELL3_ICRR1 << 16) | 0x0000), 
DOORBELL3_1_INT = ((DOORBELL3_ICRR1 << 16) | 0x0004), 
DOORBELL3_2_INT = ((DOORBELL3_ICRR1 << 16) | 0x0008), 
DOORBELL3_3_INT = ((DOORBELL3_ICRR1 << 16) | 0x000C), 
DOORBELL3_4_INT = ((DOORBELL3_ICRR1 << 16) | 0x0010), 
DOORBELL3_5_INT = ((DOORBELL3_ICRR1 << 16) | 0x0014), 
DOORBELL3_6_INT = ((DOORBELL3_ICRR1 << 16) | 0x0018), 
DOORBELL3_7_INT = ((DOORBELL3_ICRR1 << 16) | 0x001C), 
DOORBELL3_8_INT = ((DOORBELL3_ICRR2 << 16) | 0x0000), 
DOORBELL3_9_INT = ((DOORBELL3_ICRR2 << 16) | 0x0004), 
DOORBELL3_10_INT = ((DOORBELL3_ICRR2 << 16) | 0x0008), 
DOORBELL3_11_INT = ((DOORBELL3_ICRR2 << 16) | 0x000C), 
DOORBELL3_12_INT = ((DOORBELL3_ICRR2 << 16) | 0x0010), 
DOORBELL3_13_INT = ((DOORBELL3_ICRR2 << 16) | 0x0014), 
DOORBELL3_14_INT = ((DOORBELL3_ICRR2 << 16) | 0x0018), 
DOORBELL3_15_INT = ((DOORBELL3_ICRR2 << 16) | 0x001C), 
SRCID0_Transaction_Complete_OK = ((LSU_SRCID_ICRR1 << 16) | 0x0000), 
SRCID1_Transaction_Complete_OK = ((LSU_SRCID_ICRR1 << 16) | 0x0004), 
SRCID2_Transaction_Complete_OK = ((LSU_SRCID_ICRR1 << 16) | 0x0008), 
SRCID3_Transaction_Complete_OK = ((LSU_SRCID_ICRR1 << 16) | 0x000C), 
SRCID4_Transaction_Complete_OK = ((LSU_SRCID_ICRR1 << 16) | 0x0010), 
SRCID5_Transaction_Complete_OK = ((LSU_SRCID_ICRR1 << 16) | 0x0014), 
SRCID6_Transaction_Complete_OK = ((LSU_SRCID_ICRR1 << 16) | 0x0018), 
SRCID7_Transaction_Complete_OK = ((LSU_SRCID_ICRR1 << 16) | 0x001C), 
SRCID8_Transaction_Complete_OK = ((LSU_SRCID_ICRR2 << 16) | 0x0000), 
SRCID9_Transaction_Complete_OK = ((LSU_SRCID_ICRR2 << 16) | 0x0004), 
SRCID10_Transaction_Complete_OK = ((LSU_SRCID_ICRR2 << 16) | 0x0008), 
SRCID11_Transaction_Complete_OK = ((LSU_SRCID_ICRR2 << 16) | 0x000C), 
SRCID12_Transaction_Complete_OK = ((LSU_SRCID_ICRR2 << 16) | 0x0010), 
SRCID13_Transaction_Complete_OK = ((LSU_SRCID_ICRR2 << 16) | 0x0014), 
SRCID14_Transaction_Complete_OK = ((LSU_SRCID_ICRR2 << 16) | 0x0018), 
SRCID15_Transaction_Complete_OK = ((LSU_SRCID_ICRR2 << 16) | 0x001C), 
SRCID0_Transaction_Complete_ERR = ((LSU_SRCID_ICRR3 << 16) | 0x0000), 
SRCID1_Transaction_Complete_ERR = ((LSU_SRCID_ICRR3 << 16) | 0x0004), 
SRCID2_Transaction_Complete_ERR = ((LSU_SRCID_ICRR3 << 16) | 0x0008), 
SRCID3_Transaction_Complete_ERR = ((LSU_SRCID_ICRR3 << 16) | 0x000C), 
SRCID4_Transaction_Complete_ERR = ((LSU_SRCID_ICRR3 << 16) | 0x0010), 
SRCID5_Transaction_Complete_ERR = ((LSU_SRCID_ICRR3 << 16) | 0x0014), 
SRCID6_Transaction_Complete_ERR = ((LSU_SRCID_ICRR3 << 16) | 0x0018), 
SRCID7_Transaction_Complete_ERR = ((LSU_SRCID_ICRR3 << 16) | 0x001C), 
SRCID8_Transaction_Complete_ERR = ((LSU_SRCID_ICRR4 << 16) | 0x0000), 
SRCID9_Transaction_Complete_ERR = ((LSU_SRCID_ICRR4 << 16) | 0x0004), 
SRCID10_Transaction_Complete_ERR = ((LSU_SRCID_ICRR4 << 16) | 0x0008), 
SRCID11_Transaction_Complete_ERR = ((LSU_SRCID_ICRR4 << 16) | 0x000C), 
SRCID12_Transaction_Complete_ERR = ((LSU_SRCID_ICRR4 << 16) | 0x0010), 
SRCID13_Transaction_Complete_ERR = ((LSU_SRCID_ICRR4 << 16) | 0x0014), 
SRCID14_Transaction_Complete_ERR = ((LSU_SRCID_ICRR4 << 16) | 0x0018), 
SRCID15_Transaction_Complete_ERR = ((LSU_SRCID_ICRR4 << 16) | 0x001C), 
LSU0_Transaction_Complete_OK = ((LSU_ICRR1 << 16) | 0x0000), 
LSU1_Transaction_Complete_OK = ((LSU_ICRR1 << 16) | 0x0004), 
LSU2_Transaction_Complete_OK = ((LSU_ICRR1 << 16) | 0x0008), 
LSU3_Transaction_Complete_OK = ((LSU_ICRR1 << 16) | 0x000C), 
LSU4_Transaction_Complete_OK = ((LSU_ICRR1 << 16) | 0x0010), 
LSU5_Transaction_Complete_OK = ((LSU_ICRR1 << 16) | 0x0014), 
LSU6_Transaction_Complete_OK = ((LSU_ICRR1 << 16) | 0x0018), 
LSU7_Transaction_Complete_OK = ((LSU_ICRR1 << 16) | 0x001C), 
Multicast_event = ((ERR_RST_EVNT_ICRR1<<16)|0), 
Port_write_In_received = ((ERR_RST_EVNT_ICRR1<<16)|4), 
Logical_Layer_Error = ((ERR_RST_EVNT_ICRR1<<16)|8), 
Port0_Error = ((ERR_RST_EVNT_ICRR2<<16)|0), 
Port1_Error = ((ERR_RST_EVNT_ICRR2<<16)|4), 
Port2_Error = ((ERR_RST_EVNT_ICRR2<<16)|8), 
Port3_Error = ((ERR_RST_EVNT_ICRR2<<16)|12), 
Device_Reset = ((ERR_RST_EVNT_ICRR3<<16)|0) 
}SRIO_Interrupt_Source;

中断路由寄存器索引的词索引,用于表示中断路由寄存器(Interrupt Routing Registers)的索引和中断源常量。
定义一个枚举类型SRIO_ICRR_Index,包含以下值:
- DOORBELL0_ICRR1到DOORBELL3_ICRR2:表示不同门铃中断控制寄存器的索引。
- LSU_SRCID_ICRR1到LSU_SRCID_ICRR4:表示源ID相关中断控制寄存器的索引。
- LSU_ICRR1:表示LSU(负载存储单元)中断控制寄存器的索引。
- ERR_RST_EVNT_ICRR1到ERR_RST_EVNT_ICRR3:表示错误和复位事件中断控制寄存器的索引。
定义一个枚举类型SRIO_Interrupt_Source,包含以下值:
- DOORBELL0_0_INT到DOORBELL3_15_INT:表示不同门铃中断的源,每个门铃有16个中断源。
- SRCID0_Transaction_Complete_OK到SRCID15_Transaction_Complete_ERR:表示不同源ID的交易完成中断,分为成功和错误两种情况,每个源ID有一个成功和一个错误的中断源。
- LSU0_Transaction_Complete_OK到LSU7_Transaction_Complete_OK:表示LSU的交易完成中断,共有8个中断源。
- Multicast_event、Port_write_In_received、Logical_Layer_Error:表示不同类型的中断源。
- Port0_Error到Port3_Error:表示不同端口的错误中断源。
- Device_Reset:表示设备复位中断源。
每个中断源的定义都是通过将中断控制寄存器的索引左移16位,然后与一个偏移量进行按位或操作得到的。这个偏移量表示中断字段在寄存器中的位置。

// SRIO中断目的地的枚举定义
typedef enum {
    // 中断目的地从0到15
    INTDST_0 = 0, // 中断目的地0
    INTDST_1,     // 中断目的地1
    // ...以此类推,直到INTDST_15
    // 仅门铃使用的中断目的地
    INTDST_16 = 0, // 门铃中断目的地0
    INTDST_17,     // 门铃中断目的地1
    // ...以此类推,直到INTDST_23
} SRIO_Interrupt_Dest;

// 定义SRIO中断映射结构,包含中断源和中断目的地编号
typedef struct {
    SRIO_Interrupt_Source interrupt_event; // 中断事件源
    SRIO_Interrupt_Dest INTDST_number;     // 中断目的地编号
} SRIO_Interrupt_Map;

// 定义SRIO中断速率结构,包含中断目的地编号和中断速率计数器
typedef struct {
    SRIO_Interrupt_Dest INTDST_number;     // 中断目的地编号
    Uint32 interrupt_rate_counter;         // 中断速率计数器
} SRIO_Interrupt_Rate;

// 定义SRIO门铃路由控制枚举,用于控制门铃中断是路由到专用中断还是通用中断
typedef enum {
    SRIO_DOORBELL_ROUTE_TO_DEDICATE_INT = 0, // 路由到专用中断
    SRIO_DOORBELL_ROUTE_TO_GENERAL_INT       // 路由到通用中断
} SRIO_Doorbell_Route_Coutrol;

// 定义SRIO中断配置结构,包含中断映射表、中断速率配置等
typedef struct {
    SRIO_Interrupt_Map *interrupt_map;      // 中断映射表指针
    Uint32 uiNumInterruptMap;               // 中断映射数量
    SRIO_Interrupt_Rate *interrupt_rate;    // 中断速率配置指针
    Uint32 uiNumInterruptRateCfg;           // 具有速率配置的中断目的地数量
    SRIO_Doorbell_Route_Coutrol doorbell_route_ctl; // 门铃路由控制
} SRIO_Interrupt_Cfg;

// Keystone_SRIO_Interrupt_init函数用于初始化SRIO中断
void Keystone_SRIO_Interrupt_init(SRIO_Interrupt_Cfg *interrupt_cfg) {
    // ...函数实现代码...
}

// 定义一个SRIO中断映射数组,用于指定中断事件到特定核心的中断目的地
SRIO_Interrupt_Map interrupt_map[] = {
    {DOORBELL0_0_INT, INTDST_16}, // 将DOORBELL0_0_INT路由到核心0
    // ...其他中断映射...
};

// 创建一个SRIO中断配置结构实例
SRIO_Interrupt_Cfg interrupt_cfg;
// ...其他代码...
interrupt_cfg.interrupt_map = interrupt_map;
interrupt_cfg.uiNumInterruptMap = sizeof(interrupt_map) / sizeof(SRIO_Interrupt_Map);
// 在这个测试中不使用中断速率控制
interrupt_cfg.interrupt_rate = NULL;
interrupt_cfg.uiNumInterruptRateCfg = 0;
interrupt_cfg.doorbell_route_ctl = SRIO_DOORBELL_ROUTE_TO_DEDICATE_INT;
// 初始化SRIO中断
Keystone_SRIO_Interrupt_init(&interrupt_cfg);


2.6 回环模式

2.6.1 内部数字回环

在内部数字回环模式下,TX数据在数字域内进行回环。

回环[3:0]字段在外设设置控制寄存器1中用于设置数字回环测试。以下代码展示了配置过程:

srioRegs->RIO_PER_SET_CNTL1 |= (0xF<<CSL_SRIO_RIO_PER_SET_CNTL1_LOOPBACK_SHIFT);

2.6.2 内部SERDES回环

如果配置了SERDES回环,TX数据将在Serdes内部进行回环。

SRIO SERDES发送通道配置寄存器中的环回字段[22:21]和SRIO SERDES接收通道配置寄存器中的环回字段[24:23]用于启用SERDES环回测试。以下代码展示了如何进行配置:

serdesRegs->link[i].CFGTX |= (serdes_cfg->linkSetup[i]->loopBack<<21); 
serdesRegs->link[i].CFGRX |= (serdes_cfg->linkSetup[i]->loopBack<<23);

2.6.3 外部线路环回

如果启用了外部线路环回,则在 RX “线路”上接收到的数据将直接发送回 TX “线路”。 下图显示了一个外部线路环回测试。在此测试中,DSP 1 被配置为线路环回模式,以支持 DSP 0 测试两个 DSP 之间的外部“线路”。在这种模式下,DSP 1 不能发送或接收数据。

PLM Port(n) 实现特定控制寄存器的位 23 LLB_EN 用于控制外部线路环回测试。以下代码显示了配置过程:

for(i=0; i<SRIO_MAX_PORT_NUM; i++)
{
    srioRegs->RIO_PLM[i].RIO_PLM_SP_IMP_SPEC_CTL =
    (1 << CSL_SRIO_RIO_PLM_SP_IMP_SPEC_CTL_LLB_EN_SHIFT);
}

这段代码它遍历所有的 SRIO 端口。对于每个端口,它将 RIO_PLM_SP_IMP_SPEC_CTL 寄存器的 LLB_EN 位设置为 1,从而启用线路环回功能。CSL_SRIO_RIO_PLM_SP_IMP_SPEC_CTL_LLB_EN_SHIFT 是一个宏,它定义了 LLB_EN 位在寄存器中的位置。通过将 1 左移这个位数,代码将相应位置的位设置为 1,而其他位保持不变。这表明在编写这段代码时,应该有一个定义了 SRIO_MAX_PORT_NUM 的地方,它指定了 SRIO 端口的最大数量。

2.7 数据包转发

KeyStone 系列的 SRIO 支持数据包转发功能,它可以将从一个端口接收到的数据转发到任何其他端口。通过这个功能,多个 DSP 可以像链条一样连接起来。 下图显示了一个外部转发回环测试。在这种模式下,当某些数据包被转发出去时,DSP 1 仍然可以发送或接收自己的数据。


Keystone 系列的 SRIO 转发功能由 MAU(内存访问单元)模块处理,它只支持 NREAD、NWRITE、NWRITE_R、SWRITE 和门铃(Doorbell)数据包类型。所有其他类型都不支持,可能会导致未定义的行为,包括生成错误响应(ERROR response)。
PF_16b_CNTL[0:7] 和 PF_8b_CNTL[0:7] 控制数据包转发设备 ID 边界和输出端口选择。当入站设备 ID 与设置的数据包转发条目匹配时,数据包将被转发到 MAU 并进行进一步处理。


以下代码展示了数据包转发的配置过程:

// 定义一个结构体,用于配置SRIO数据包的转发
typedef struct 
{ 
    Uint16 forwardingID_up_8;    // 上8位设备ID边界
    Uint16 forwardingID_lo_8;    // 下8位设备ID边界
    Uint16 forwardingID_up_16;   // 上16位设备ID边界
    Uint16 forwardingID_lo_16;   // 下16位设备ID边界
    Uint32 outport;              // 目的ID落在此表项8位或16位范围内的数据包的输出端口编号
} SRIO_PktForwarding_Cfg; 
// 配置SRIO数据包转发的函数
void Keystone_SRIO_packet_forwarding_Cfg( 
    SRIO_PktForwarding_Cfg * PktForwardingEntry_cfg, // 指向转发配置结构的指针
    Uint32 pktForwardingEntryNum)                    // 转发条目的数量
{ 
    int i = 0; 
    // 确保转发条目数量不超过最大值
    pktForwardingEntryNum= _min2(SRIO_MAX_FORWARDING_ENTRY_NUM, pktForwardingEntryNum); 
    // 遍历所有转发条目
    for(i=0; i<pktForwardingEntryNum; i++) 
    { 
        // 设置16位控制寄存器,用于指定16位设备ID的上界和下界
        srioRegs->PF_CNTL[i].RIO_PF_16B_CNTL = 
            (PktForwardingEntry_cfg[i].forwardingID_up_16 << CSL_SRIO_RIO_PF_16B_CNTL_DEVID_16B_UP_SHIFT) |
            (PktForwardingEntry_cfg[i].forwardingID_lo_16 << CSL_SRIO_RIO_PF_16B_CNTL_DEVID_16B_LO_SHIFT);
        
        // 设置8位控制寄存器,用于指定8位设备ID的上界和下界以及输出端口
        srioRegs->PF_CNTL[i].RIO_PF_8B_CNTL = 
            (PktForwardingEntry_cfg[i].forwardingID_up_8 << CSL_SRIO_RIO_PF_8B_CNTL_DEVID_8B_UP_SHIFT) |
            (PktForwardingEntry_cfg[i].forwardingID_lo_8 << CSL_SRIO_RIO_PF_8B_CNTL_DEVID_8B_LO_SHIFT) |
            (PktForwardingEntry_cfg[i].outport << CSL_SRIO_RIO_PF_8B_CNTL_OUT_PORT_SHIFT); 
    } 
}
// 配置数组,最多可以设置8个条目
SRIO_PktForwarding_Cfg DSP1_PktForwarding_Cfg[]= 
{ 
    // 以下为具体的转发配置,包括设备ID范围和相应的输出端口
    {DSP0_SRIO_BASE_ID+0, DSP0_SRIO_BASE_ID+1, DSP0_SRIO_BASE_ID+0, DSP0_SRIO_BASE_ID+1, 2}, 
    {DSP0_SRIO_BASE_ID+2, DSP0_SRIO_BASE_ID+2, DSP0_SRIO_BASE_ID+2, DSP0_SRIO_BASE_ID+2, 2}, 
    {DSP0_SRIO_BASE_ID+3, DSP0_SRIO_BASE_ID+3, DSP0_SRIO_BASE_ID+3, DSP0_SRIO_BASE_ID+3, 3}, 
    {DSP0_SRIO_BASE_ID+4, DSP0_SRIO_BASE_ID+7, DSP0_SRIO_BASE_ID+4, DSP0_SRIO_BASE_ID+7, 3} 
}; 
…… 
// 调用函数进行配置,传入配置数组和条目数量
Keystone_SRIO_packet_forwarding_Cfg(&DSP1_PktForwarding_Cfg, 
    sizeof(DSP1_PktForwarding_Cfg)/sizeof(SRIO_PktForwarding_Cfg));

typedef struct: 定义了一个结构体SRIO_PktForwarding_Cfg,用于存储数据包转发的配置信息。这个结构体包含了转发ID的上界和下界(8位和16位),以及输出端口编号。
void Keystone_SRIO_packet_forwarding_Cfg(...): 这是一个函数,用于配置SRIO的数据包转发。它接受一个指向SRIO_PktForwarding_Cfg结构体数组的指针和一个表示数组大小的整数。
在函数内部,首先使用_min2函数确保转发表项的数量不超过SRIO_MAX_FORWARDING_ENTRY_NUM定义的最大值。
接下来,使用一个for循环遍历所有的转发表项。对于每个表项,代码将设置两个寄存器:RIO_PF_16B_CNTL和RIO_PF_8B_CNTL。这些寄存器用于指定16位和8位设备ID的范围,以及输出端口。
SRIO_PktForwarding_Cfg DSP1_PktForwarding_Cfg[]: 定义了一个SRIO_PktForwarding_Cfg类型的数组,用于存储DSP1的转发配置。这个数组包含了4个表项,每个表项都指定了8位和16位设备ID的范围,以及输出端口。
最后,调用Keystone_SRIO_packet_forwarding_Cfg函数,将DSP1的转发配置传递给它。这将设置SRIO的转发表,以便根据设备ID将数据包转发到正确的输出端口。


2.8 接收模式

SRIO如何处理接收到的数据包由三个配置位控制。

log_tgt_id_dis (PER_SET_CNTL,Bit 27):
0b0 — 仅支持目标ID等于BASE_ID或其他15个设备ID或8个多播ID的数据包。
0b1 — 支持混杂ID/单播ID。
所有数据包,无论目标ID是什么,都会被逻辑层接收并由相应的功能块处理。
mtc_tgt_id_dis (TLM_SP{0..3}_CONTROL, Bit 20):
0b0 — 仅接受目标ID等于BASE_ID或其他15个设备ID的维护数据包。
0b1 — 接受所有维护数据包,无论目标ID是什么。
tgt_id_dis (TLM_SP{0..3}_CONTROL, Bit 21):
0b0 — 不允许数据包转发和多播。
0b1 — 允许数据包转发和多播。

 
请注意,如果 log_tgt_id_dis = 1,无论 tgt_id_dis 的值是什么,所有数据包都将在本地接收,数据包转发将不起作用。
以下代码显示了Rx模式的配置:
// 定义一个结构体,用于配置SRIO端口的接收模式
typedef struct {
    Bool accept_maintenance_with_any_ID; // 是否接受任何ID的维护报文
    // 如果accept_data_with_any_ID为真,则不会转发任何数据包
    Bool support_multicast_forwarding;   // 是否支持多播转发
} SRIO_Port_RX_Mode;

// 定义一个结构体,用于配置SRIO的接收模式
typedef struct {
    // 如果accept_data_with_any_ID为真,则不会转发任何数据包
    Bool accept_data_with_any_ID;
    SRIO_Port_RX_Mode port_rx_mode[4]; // 四个端口的接收模式配置
} SRIO_RX_Mode;

// 配置SRIO的接收模式
void Keystone_SRIO_RxMode_Setup(SRIO_RX_Mode * rxMode)
{
    int i;
    if(rxMode) // 如果传入的rxMode指针非空
    {
        // 配置接收任何ID的数据包
        srioRegs->RIO_PER_SET_CNTL = (srioRegs->RIO_PER_SET_CNTL &
                                      (~CSL_SRIO_RIO_PER_SET_CNTL_LOG_TGT_ID_DIS_MASK)) |
                                     (rxMode->accept_data_with_any_ID <<
                                      CSL_SRIO_RIO_PER_SET_CNTL_LOG_TGT_ID_DIS_SHIFT);

        // 为所有端口设置RX模式
        for(i = 0; i < SRIO_MAX_PORT_NUM; i++)
        {
            // 配置每个端口的接收模式
            srioRegs->RIO_TLM[i].RIO_TLM_SP_CONTROL = (srioRegs->RIO_TLM[i].RIO_TLM_SP_CONTROL &
                                                       (~(CSL_SRIO_RIO_TLM_SP_CONTROL_TGT_ID_DIS_MASK |
                                                          CSL_SRIO_RIO_TLM_SP_CONTROL_MTC_TGT_ID_DIS_MASK))) |
                                                      (rxMode->port_rx_mode[i].accept_maintenance_with_any_ID <<
                                                       CSL_SRIO_RIO_TLM_SP_CONTROL_MTC_TGT_ID_DIS_SHIFT) |
                                                      (rxMode->port_rx_mode[i].support_multicast_forwarding <<
                                                       CSL_SRIO_RIO_TLM_SP_CONTROL_TGT_ID_DIS_SHIFT);
        }
    }
}

// ... 其他代码 ...

SRIO_RX_Mode rxMode; // 创建一个SRIO_RX_Mode类型的变量rxMode
// ... 其他代码 ...

// 清除配置结构体,确保未使用的字段为0
memset(&rxMode, 0, sizeof(rxMode));
rxMode.port_rx_mode[0].support_multicast_forwarding = TRUE; // 端口0支持多播转发
rxMode.port_rx_mode[1].support_multicast_forwarding = TRUE; // 端口1支持多播转发
rxMode.port_rx_mode[2].support_multicast_forwarding = TRUE; // 端口2支持多播转发
rxMode.port_rx_mode[3].support_multicast_forwarding = TRUE; // 端口3支持多播转发
Keystone_SRIO_RxMode_Setup(&rxMode); // 调用函数,设置SRIO的接收模式

3 SRIO 传输编程

3.1 LSU 传输

Direct IO, doorbell and maintenance transfers在LSU模块中实现,每个LSU寄存器组代表一个传输请求。LSU寄存器的编程相当直接。如上一节所讨论的,存在多个影子寄存器组,我们必须在编程寄存器之前,通过轮询LSU_REG6中的FULL和BUSY位,直到有一个影子寄存器可用。下面是编程LSU的示例代码。

// SRIO LSU传输的结构体定义
typedef struct SRIO_LSU_Transfer {
    Uint32 rioAddressMSB;                // RapidIO地址的最高32位
    Uint32 rioAddressLSB_ConfigOffset;   // RapidIO地址的最低32位和配置偏移
    Uint32 localDspAddress;              // 本地DSP地址
    Uint32 bytecount;                    // 字节数
    SRIO_Packet_Type packetType;         // 包类型
    Uint16 dstID;                        // 目的地ID
    Uint16 doorbellInfo;                 // 门铃信息
    Bool waitLsuReady;                   // 如果BUSY或FULL,是否等待LSU就绪
    Uint8 lsuNum;                        // 用于此传输的LSU编号
    Uint8 doorbellValid;                 // 门铃有效标志
    Uint8 intrRequest;                   // 中断请求
    Uint8 supGoodInt;                    // 支持良好中断
    Uint8 priority;                      // 优先级
    Uint8 outPortID;                     // 输出端口ID
    Uint8 idSize;                        // ID大小
    Uint8 srcIDMap;                      // 源ID映射
    Uint8 hopCount;                      // 跳数
    Uint8 transactionID;                 // 交易ID,用于完成状态检查
    Uint8 contextBit;                    // 交易上下文位,用于完成状态检查
} SRIO_LSU_Transfer;

// LSU传输函数
void Keystone_SRIO_LSU_transfer(SRIO_LSU_Transfer *transfer) {
    // ... 函数实现 ...
}

LSU传输完成情况通过LSU_STAT_REG寄存器进行检查。从LSU_REG6读取的交易ID用于确定LSU_STAT_REG寄存器中完成代码的索引;从LSU_REG6读取的上下文位用于确定完成代码的有效性。上下文位会为每个事务切换,例如,对于第一个交易ID=2的传输,上下文位=1;然后,对于下一个交易ID=2的传输,上下文位=0。因此,只有在LSU_STAT_REG寄存器中交易ID=2的上下文位从1切换到0后,才表示第二个传输完成,并且完成代码有效。以下是LSU完成检查的示例代码。
 

//这是一个用于指示LSU状态寄存器中状态索引的表格,用于SRIO_MAX_LSU_NUM个LSU的事务。
Uint8 LSU_state_index_table[SRIO_MAX_LSU_NUM][SRIO_LSU0_4_MAX_SHADOW_REG_SET]=
{
/*LSU0的索引*/ {0, 1, 2, 3, 4, 5, 6, 7, 8},
/*LSU1的索引*/ {9, 10, 11, 12, 13, 14},
/*LSU2的索引*/ {15, 16, 17, 18, 19},
/*LSU3的索引*/ {20, 21, 22, 23},
/*LSU4的索引*/ {24, 25, 26, 27, 28, 29, 30, 31, 32},
/*LSU5的索引*/ {33, 34, 35, 36, 37, 38},
/*LSU6的索引*/ {39, 40, 41, 42, 43},
/*LSU7的索引*/ {44, 45, 46, 47}
};
/*等待LSU传输完成,并返回完成代码*/
Uint32 Keystone_SRIO_wait_LSU_completion(Uint32 lsuNum,
Uint32 transactionID, Uint32 contextBit)
{
Uint32 uiStateIndex= LSU_state_index_table[lsuNum][transactionID];
Uint32 uiCompletionCode;
do
{
uiCompletionCode=(srioRegs->LSU_STAT_REG[uiStateIndex/8]>>
((uiStateIndex&7)*4))&0xF;
}
while((uiCompletionCode&1) != contextBit);
return (uiCompletionCode>>1);
}

LSU_state_index_table 是一个二维数组,它定义了每个LSU的传输ID到状态寄存器索引的映射。每个LSU有一系列的传输槽,这些槽对应的状态索引存储在这个表中。
Keystone_SRIO_wait_LSU_completion 函数有三个参数:lsuNum 表示LSU的编号,transactionID 表示传输的ID,contextBit 表示上下文位。
函数首先根据LSU编号和传输ID在LSU_state_index_table表中查找对应的状态索引。
然后,函数进入一个循环,不断读取对应的状态寄存器来检查传输是否完成。这里使用了一个位掩码操作来提取完成码:uiCompletionCode=(srioRegs->LSU_STAT_REG[uiStateIndex/8]>>((uiStateIndex&7)*4))&0xF; 这里srioRegs->LSU_STAT_REG是一个结构体指针,指向包含状态寄存器的结构体,uiStateIndex/8和(uiStateIndex&7)*4分别用于计算正确的寄存器和位位置。
循环会一直执行,直到uiCompletionCode的最低有效位(LSB)与传入的contextBit相匹配,这表明传输已经完成。
最后,函数返回移除了最低有效位的uiCompletionCode,即return (uiCompletionCode>>1);,这个返回值表示传输的完成状态。

下面是直接IO传输的示例代码。

// 定义SRIO数据包类型的枚举
typedef enum
{
    SRIO_PKT_TYPE_NREAD = 0x24,     // 非维持读操作
    SRIO_PKT_TYPE_NWRITE = 0x54,    // 非维持写操作
    SRIO_PKT_TYPE_NWRITE_R = 0x55,  // 非维持写响应操作
    SRIO_PKT_TYPE_SWRITE = 0x60,    // 维持写操作
    SRIO_PKT_TYPE_MTN_READ = 0x80,  // 维持多传输读操作
    SRIO_PKT_TYPE_MTN_WRITE = 0x81, // 维持多传输写操作
    SRIO_PKT_TYPE9_STREAM = 0x90,   // 流数据包类型
    SRIO_PKT_TYPE_DOORBELL = 0xA0,  // 门铃数据包类型
    SRIO_PKT_TYPE11_MESSAGE = 0xB0  // 消息数据包类型
} SRIO_Packet_Type;

// Keystone的SRIO直接输入/输出操作函数
Int32 Keystone_SRIO_DirectIO(Uint32 uiLocalAddress, Uint32 uiRemoteAddress, 
                             Uint32 uiDestID, Uint32 uiByteCount, Uint32 uiPort, Uint32 uiLSU_No, 
                             SRIO_Packet_Type packetType)
{
    SRIO_LSU_Transfer lsuTransfer; // 定义一个SRIO LSU传输结构体
    lsuTransfer.rioAddressMSB = 0;  // SRIO地址的高位
    lsuTransfer.rioAddressLSB_ConfigOffset = uiRemoteAddress; // SRIO地址的低位和配置偏移
    lsuTransfer.localDspAddress = uiLocalAddress; // 本地DSP地址
    lsuTransfer.bytecount = uiByteCount; // 字节数
    lsuTransfer.packetType = packetType; // 数据包类型
    lsuTransfer.dstID = uiDestID; // 目的地ID
    lsuTransfer.doorbellInfo = 0; // 门铃信息
    lsuTransfer.waitLsuReady = 1; // 等待LSU准备
    lsuTransfer.lsuNum = uiLSU_No; // LSU编号
    lsuTransfer.doorbellValid = 0; // 门铃有效标志
    lsuTransfer.intrRequest = 0; // 中断请求标志
    lsuTransfer.supGoodInt = 0; // 支持良好中断标志
    lsuTransfer.priority = 0; // 优先级
    lsuTransfer.outPortID = uiPort; // 输出端口ID
    lsuTransfer.idSize = 0; // ID大小
    lsuTransfer.srcIDMap = 0; // 源ID映射
    lsuTransfer.hopCount = 0; // 跳数计数

    Keystone_SRIO_LSU_transfer(&lsuTransfer); // 执行SRIO LSU传输
    return Keystone_SRIO_wait_LSU_completion(uiLSU_No, // 等待LSU传输完成
                                             lsuTransfer.transactionID, // 事务ID
                                             lsuTransfer.contextBit); // 上下文位
}

3.2 消息传输

KeyStone系列通过QMSS(Quick Messaging Subsystem)和Packet DMA(Packet Direct Memory Access)来处理SRIO Type 11和Type 9数据包。有关详细信息,请参阅KeyStone架构多核导航用户指南(SPRUGR9)。
对于数据接收,如果消息接收映射和接收流设置得当,一旦接收到数据,Packet DMA会将数据包推送到接收目标队列。然后,应用程序可以从接收队列中获取数据包。对于数据传输,应用程序只需将数据包推送到16个发送队列中的一个,Packet DMA和SRIO外围设备将根据发送队列调度配置来传输数据包。

SRIO的特殊点在于数据包描述符中的协议特定字段。它们的定义如下。

/****************************************************************************/
/* 定义SRIO Type 9消息发送描述符的位和字布局 */
/****************************************************************************/
#ifdef _BIG_ENDIAN
// 如果是大端模式
typedef struct
{
 /* 字0 */
 Uint32 SRC_ID : 16; // 源节点ID
 Uint32 Dest_ID : 16; // 消息发送到的目标节点ID
 /* 字1 */
 Uint32 StreamID : 16; // 事务的流ID
 Uint32 reserved0 : 5; // 保留位
 Uint32 TT : 2; // RapidIO tt字段,8位或16位ID
 Uint32 reserved1 : 1; // 保留位
 Uint32 COS : 8; // 服务等级
} SRIO_Type9_Message_TX_Descriptor;
#else
// 如果是小端模式
typedef struct
{
 /* 字0 */
 Uint32 Dest_ID : 16; // 消息发送到的目标节点ID
 Uint32 SRC_ID : 16; // 源节点ID
 /* 字1 */
 Uint32 COS : 8; // 服务等级
 Uint32 reserved1 : 1; // 保留位
 Uint32 TT : 2; // RapidIO tt字段,8位或16位ID
 Uint32 reserved0 : 5; // 保留位
 Uint32 StreamID : 16; // 事务的流ID
} SRIO_Type9_Message_TX_Descriptor;
#endif
/****************************************************************************/
/* 定义SRIO Type 9消息接收描述符的位和字布局 */
/****************************************************************************/
#ifdef _BIG_ENDIAN
// 如果是大端模式
typedef struct
{
 /* 字0 */
 Uint32 SRC_ID : 16; // 源节点ID
 Uint32 Dest_ID : 16; // 消息发送到的目标节点ID
 /* 字1 */
 Uint32 StreamID : 16; // 事务的流ID
 Uint32 reserved0 : 1; // 保留位
 Uint32 PRI : 4; // 消息优先级(虚拟通道||优先级||流量控制)
 Uint32 T : 1; // 这是TT [0]位
 Uint32 CC : 2; // 完成码
 Uint32 COS : 8; // 服务等级
} SRIO_Type9_Message_RX_Descriptor;
#else
// 如果是小端模式
typedef struct
{
 /* 字0 */
 Uint32 Dest_ID : 16; // 消息发送到的目标节点ID
 Uint32 SRC_ID : 16; // 源节点ID
 /* 字1 */
 Uint32 COS : 8; // 服务等级
 Uint32 CC : 2; // 完成码
 Uint32 T : 1; // 这是TT [0]位
 Uint32 PRI : 4; // 消息优先级(虚拟通道||优先级||流量控制)
 Uint32 reserved0 : 1; // 保留位
 Uint32 StreamID : 16; // 事务的流ID
} SRIO_Type9_Message_RX_Descriptor;
#endif
/****************************************************************************/
/* 定义SRIO Type 11消息接收描述符的位和字布局 */
/****************************************************************************/
#ifdef _BIG_ENDIAN
// 如果是大端模式
typedef struct
{
 /* 字0 */
 Uint32 SRC_ID : 16; // 源节点ID
 Uint32 Dest_ID : 16; // 消息发送到的目标节点ID
 /* 字1 */
 Uint32 reserved0 : 15; // 保留位
 Uint32 CC : 2; // 完成码
 Uint32 PRI : 4; // 消息优先级(虚拟通道||优先级||流量控制)
 Uint32 TT : 2; // RapidIO tt字段,8位或16位设备ID
 Uint32 LTR : 3; // 目的地字母
 Uint32 MailBox : 6; // 目的地邮箱
} SRIO_Type11_Message_RX_Descriptor;
#else
// 如果是小端模式
typedef struct
{
 /* 字0 */
 Uint32 Dest_ID : 16; // 消息发送到的目标节点ID
 Uint32 SRC_ID : 16; // 源节点ID
 /* 字1 */
 Uint32 MailBox : 6; // 目的地邮箱
 Uint32 LTR : 3; // 目的地字母
 Uint32 TT : 2; // RapidIO tt字段,8位或16位设备ID
 Uint32 PRI : 4; // 消息优先级(虚拟通道||优先级||流量控制)
 Uint32 CC : 2; // 完成码
 Uint32 reserved0 : 15; // 保留位
} SRIO_Type11_Message_RX_Descriptor;

另一个我们需要注意的点是,对于数据传输,主机数据包描述符中的Packet_Type字段必须正确设置,它用于识别SRIO事务类型,对于类型9,Packet_Type应设置为30;如果Packet_Type为31,则选择类型11。应用程序只需要从FDQ中弹出一个描述符,并在描述符中填充PS字段,然后将描述符推送到相关的TX队列。
以下是填充TX数据包描述符的示例代码。

typedef enum
{
SRIO_TYPE9_CPPI_PACKET = 30,
SRIO_TYPE11_CPPI_PACKET = 31
}SRIO_CPPI_Packet_Type;
//Build SRIO type11 message descriptor
void Keystone_SRIO_Build_Type11_Msg_Desc(
HostPacketDescriptor * hostDescriptor, Uint32 uiSrcID, Uint32 uiDestID, 
Uint32 uiByteCount, Uint32 uiMailBox, Uint32 uiLetter)
{
SRIO_Type11_Message_TX_Descriptor * type11MsgTxDesc;
hostDescriptor->packet_type = SRIO_TYPE11_CPPI_PACKET;
hostDescriptor->packet_length= uiByteCount;
hostDescriptor->buffer_len= uiByteCount;
hostDescriptor->psv_word_count= 2; //SRIO uses 2 Protocol Specific words
type11MsgTxDesc= (SRIO_Type11_Message_TX_Descriptor *)
(((Uint32)hostDescriptor)+32);
type11MsgTxDesc->Dest_ID = uiDestID;
type11MsgTxDesc->SRC_ID = uiSrcID;
type11MsgTxDesc->Retry_Count= 1;
type11MsgTxDesc->SSIZE= SRIO_SSIZE_256_BYTES;
type11MsgTxDesc->TT = 0;
type11MsgTxDesc->LTR= uiLetter;
type11MsgTxDesc->MailBox= uiMailBox;
}
//Build SRIO type9 data stream message Descriptor
void Keystone_SRIO_Build_Type9_Msg_Desc(
HostPacketDescriptor * hostDescriptor, Uint32 uiSrcID, Uint32 uiDestID, 
Uint32 uiByteCount, Uint32 uiStreamID, Uint32 uiCOS)
{
SRIO_Type9_Message_TX_Descriptor * type9MsgTxDesc;
hostDescriptor->packet_type = SRIO_TYPE9_CPPI_PACKET;
hostDescriptor->packet_length= uiByteCount; 
hostDescriptor->buffer_len= uiByteCount; 
hostDescriptor->psv_word_count= 2; //SRIO uses 2 Protocol Specific words 
type9MsgTxDesc= (SRIO_Type9_Message_TX_Descriptor *) 
(((Uint32)hostDescriptor)+32); 
type9MsgTxDesc->Dest_ID = uiDestID; 
type9MsgTxDesc->SRC_ID = uiSrcID; 
type9MsgTxDesc->StreamID = uiStreamID; 
type9MsgTxDesc->TT = 0; 
type9MsgTxDesc->COS = uiCOS; 
}

请注意,类型9的数据包支持最大64K字节的负载,而类型11的数据包只支持最大4K字节的负载。
 

  • 9
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值