打印
[RISC-V MCU 应用开发]

CH32V307以太网MAC层数据包收发速度测试

[复制链接]
24|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
测试平台:
CH32V307VCT6+RTL8211FS
PS:沁恒CH32V307VCT6-R2开发板带有千兆PHY(RTL8211FS),本次测试基于该开发板。该开发板原理图以及PCB图可在CH32V 307 EVT中获取到,如下图。
EVT下载链接如下:https://www.wch.cn/downloads/CH32V307EVT_ZIP.html

MCU介绍:
CH32V307是一款RISC-V内核的互联型MCU,系统主频最大支持144MHz,FLASH 256KB,RAM 64KB,FLASH、RAM大小可配置修改。支持以太网通信,集成千兆MAC,并内置10M PHY。本次测试主要对MAC层收发速度进行一个测试。


PHY介绍:
RTL8211FS是一款具有高集成度的千兆以太网PHY芯片,可用于网卡、路由器、交换机等网络设备。
测试程序:
CH32V307 EVT提供了以太网相关例程,包含了CH32V307 MAC_RAW的例程,如下图。MAC_RAM例程为MAC层接收和发送数据演示例程,本次测试程序基于该例程进行修改,进行接收、发送的速度测试。
例程修改:
由于测试是进行MAC层最大收发速度测试,以太网驱动文件选择RGMII.c这个文件,其他文件排除编译,如下图:
例程讲解:
测试例程为MAC层数据包发送和接收演示例程,不涉及以太网协议栈的处理,单纯进行MAC层的收发。关于该例程main函数,如下:
/*********************************************************************
* @fn      main
*
* [url=home.php?mod=space&uid=247401]@brief[/url]   Main program
*
* [url=home.php?mod=space&uid=266161]@return[/url]  none
*/
int main(void)
{
    u8 i;
    uint32_t PktCnt = 0;

    SystemCoreClockUpdate();
    Delay_Init();
    USART_Printf_Init(115200);                                  //USART initialize
    printf("MAC RAW Test\r\n");
    printf("SystemClk:%d\r\n", SystemCoreClock);
    printf("ChipID:%08x\r\n", DBGMCU_GetCHIPID());
    WCHNET_GetMacAddr(MACAddr);                                 //get the chip MAC address
    printf("mac addr:");
    for(i = 0; i < 6; i++)
        printf("%x ", MACAddr[i]);
    printf("\n");
    TIM2_Init();
    ETH_Init(MACAddr);
    /* change source MAC address */
    memcpy(&ARPPackage[6], MACAddr, 6);
    memcpy(&ARPPackage[22], MACAddr, 6);

    while(1)
    {
        WCHNET_MainTask();
        if(LinkSta)
        {
            MACRAW_Tx(ARPPackage, sizeof(ARPPackage));
            if(++PktCnt % 100 == 0)
                printf("PktCnt:%d\r\n",PktCnt);
            Delay_Ms(100);
        }
    }
}
SystemCoreClockUpdate函数、Delay_Init函数、USART_Printf_Init函数主要进行系统时钟、延时函数、串口打印函数的初始化,不做具体介绍。
WCHNET_GetMacAddr函数主要用于获取CH32V307的MAC地址。MAC地址,即发送这个帧的设备希望接收这个帧的设备地址,由IEEE为生产商分配,全球唯一,6字节48 位。 CH32V307的MAC地址出厂时已烧录在芯片内部。地址的发送遵循低有效位在前的原则。
* @fn      WCHNET_GetMacAddr
*
* [url=home.php?mod=space&uid=247401]@brief[/url]   Get the MAC address
*
* [url=home.php?mod=space&uid=266161]@return[/url]  none.
*/
void WCHNET_GetMacAddr( uint8_t *p )
{
    uint8_t i;
    uint8_t *macaddr = (uint8_t *)(ROM_CFG_USERADR_ID+5);

    for(i=0;i<6;i++)
    {
        *p = *macaddr;
        p++;
        macaddr--;
    }
}
WCHNET_GetMacAddr函数具体内容如上,MAC地址存放于0x1FFFF7E8+5起始处,读取对应地址可获取CH32V307内部MAC地址。main函数中获取MAC地址之后将其打印出来。
TIM2_Init函数主要用于检测以太网PHY连接的状态。定时器10ms进一次中断。在定时器中断函数中,主要对定时器周期进行加法计数。
LocalTime主要用于查询PHY状态,1s查询一次,PHY状态查询函数如下:
/*********************************************************************
* @fn      WCHNET_QueryPhySta
*
* @brief   Query external PHY status
*
* @return  none.
*/
#if !LINK_STAT_ACQUISITION_METHOD
void WCHNET_QueryPhySta(void)
{
    u16 phy_stat;
    if(QUERY_STAT_FLAG){                                         /* Query the PHY link status every 1s */
        LastQueryPhyTime = LocalTime / 1000;
        ETH_WritePHYRegister( PHY_ADDRESS, 0x1F, 0x0a43 );
        /*In some cases the status is not updated in time,
         * so read this register twice to get the correct status value.*/
        ETH_ReadPHYRegister( PHY_ADDRESS, 0x1A);
        phy_stat = ETH_ReadPHYRegister( PHY_ADDRESS, 0x1A) & 0x04;
        if(phy_stat != LastPhyStat){
            ETH_PHYLink();
        }
    }
}
#endif
WCHNET_QueryPhySta函数为外部PHY连接状态查询函数,函数中,首先对QUERY_STAT_FLAG标志进行判断,QUERY_STAT_FLAG的定义如下:
根据QUERY_STAT_FLAG的定义,当LocaTime的值为1000时,其值为1。WCHNET_QueryPhySta函数中if条件成立,执行if判断内的语句。在if语句中,LastQueryPhyTime记录查询次数,然后通过对PHY寄存器的操作查询外部PHY的连接状态;ETH_WritePHYRegister( PHY_ADDRESS, 0x1F, 0x0a43 );完成对PHY寄存器的写入,该操作相当于选择PHY寄存器0xa43这个页面,默认是0xa42,如下图

选择对应的PHY寄存器页面之后,可对该页面的寄存器进行读取。
ETH_ReadPHYRegister( PHY_ADDRESS, 0x1A);函数对0x1A寄存器进行读取,0x1A寄存器具体如下:
phy_stat = ETH_ReadPHYRegister( PHY_ADDRESS, 0x1A) & 0x04;主要对0x1A寄存器位2的值进行判断,位2表示Link连接状态,当Link连接,该位值为1,执行ETH_PHYLink函数。
ETH_PHYLink函数内容如下:
/*********************************************************************
* @fn      ETH_PHYLink
*
* @brief   Configure MAC parameters after the PHY Link is successful.
*
* @param   none.
*
* @return  none.
*/
void ETH_PHYLink( void )
{
    u32 phy_stat;

    ETH_WritePHYRegister( gPHYAddress, 0x1F, 0x0a43 );
    /*In some cases the status is not updated in time,
     * so read this register twice to get the correct status value.*/
    ETH_ReadPHYRegister( gPHYAddress, 0x1A);
    phy_stat = ETH_ReadPHYRegister( gPHYAddress, 0x1A);
#if !LINK_STAT_ACQUISITION_METHOD
    LastPhyStat = phy_stat & 0x04;
#endif
    if( phy_stat & 0x04 )
    {
        printf("Link Suc\r\n");
        if( phy_stat & 0x08 )
        {
            ETH->MACCR |= ETH_Mode_FullDuplex;
        }
        else
        {
            ETH->MACCR &= ~ETH_Mode_FullDuplex;
        }
        if( (phy_stat & 0x30) == 0x00 )
        {
            ETH->MACCR &= ~(ETH_Speed_100M|ETH_Speed_1000M);
        }
        else if( (phy_stat & 0x30) == 0x10 )
        {
            ETH->MACCR &= ~(ETH_Speed_100M|ETH_Speed_1000M);
            ETH->MACCR |= ETH_Speed_100M;
        }
        else if( (phy_stat & 0x30) == 0x20 )
        {
            ETH->MACCR &= ~(ETH_Speed_100M|ETH_Speed_1000M);
            ETH->MACCR |= ETH_Speed_1000M;
        }
        ETH_Start( );
        LinkSta = 1;
    }
    else {
        LinkSta = 0;
    }
    phy_stat = ETH_ReadPHYRegister( gPHYAddress, 0x1D);   /* Clear the Interrupt status */
}

以上判断设置完成后,调用ETH_Start函数开启MAC和DMA的接收传输,ETH_Start函数具体内容如下:
/*********************************************************************
* @fn      ETH_Start
*
* @brief   Enables ENET MAC and DMA reception/transmission.
*
* @return  none
*/
void ETH_Start(void)
{
    ETH_MACTransmissionCmd(ENABLE);
    ETH_FlushTransmitFIFO();
    ETH_MACReceptionCmd(ENABLE);
    ETH_DMATransmissionCmd(ENABLE);
    ETH_DMAReceptionCmd(ENABLE);
}
ETH_MACTransmissionCmd(ENABLE);函数使能开启MAC发送器;
ETH_FlushTransmitFIFO();使能复位DMA发送FIFO;
ETH_MACReceptionCmd(ENABLE);函数使能开启MAC接收器;
ETH_DMATransmissionCmd(ENABLE);使能开启发送控制位,把发送进程置位运行状态;
ETH_DMAReceptionCmd(ENABLE);使能开始接收控制位,开启接收流程,DMA从当前位置取接收描述符或者从标识符表头位置取接收描述符。
ETH_Start函数执行完成后,将LinkSta标志置1,phy_stat = ETH_ReadPHYRegister( gPHYAddress, 0x1D);函数将清除相应状态标志位。
回归到main函数,TIM2初始化完成后,进行以太网初始化,ETH_Init(MACAddr);函数具体内容如下:
/*********************************************************************
* @fn      ETH_Init
*
* @brief   Ethernet initialization.
*
* @return  none
*/
void ETH_Init( uint8_t *macAddr )
{
    ChipId = DBGMCU_GetCHIPID();
    ETH_Configuration( macAddr );
    ETH_DMATxDescChainInit(DMATxDscrTab, MACTxBuf, ETH_TXBUFNB);
    ETH_DMARxDescChainInit(DMARxDscrTab, MACRxBuf, ETH_RXBUFNB);
    pDMARxSet = DMARxDscrTab;
    pDMATxSet = DMATxDscrTab;
    NVIC_EnableIRQ(ETH_IRQn);
    NVIC_SetPriority(ETH_IRQn, 0);
}
ChipId = DBGMCU_GetCHIPID();该语句主要用于获取新片ID;
ETH_Configuration( macAddr );函数主要对以太网进行配置,该函数内容如下:
/*********************************************************************
* @fn      ETH_Configuration
*
* @brief   Ethernet configure.
*
* @return  none
*/
void ETH_Configuration( uint8_t *macAddr )
{
    ETH_InitTypeDef ETH_InitStructure;
    uint16_t timeout = 10000;

    /* Enable Ethernet MAC clock */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ETH_MAC | \
                          RCC_AHBPeriph_ETH_MAC_Tx | \
                          RCC_AHBPeriph_ETH_MAC_Rx, ENABLE);

    gPHYAddress = PHY_ADDRESS;

    /* Enable 1G MAC*/
    EXTEN->EXTEN_CTR |= EXTEN_ETH_RGMII_SEL;
    RCC_ETH1GCLKConfig(RCC_ETH1GCLKSource_PB1_IN);
    RCC_ETH1G_125Mcmd(ENABLE);
    /* Enable RGMII GPIO */
    ETH_RGMIIPinInit();

    /* Reset ETHERNET on AHB Bus */
    ETH_DeInit();

    /* Software reset */
    ETH_SoftwareReset();

    /* Wait for software reset */
    do{
        Delay_Us(10);
        if( !--timeout )  break;
    }while(ETH->DMABMR & ETH_DMABMR_SR);

    /* ETHERNET Configuration */
    /* Call ETH_StructInit if you don't like to configure all ETH_InitStructure parameter */
    ETH_StructInit(Ð_InitStructure);
    /* Fill ETH_InitStructure parameters */
    /*------------------------   MAC   -----------------------------------*/
    ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex;                                      //全双工模式
    ETH_InitStructure.ETH_Speed = ETH_Speed_1000M;                                         //1000M模式
    ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable;                    //使能开启自动协商
    ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable;                         //关闭自动循环模式
    ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable;
    ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable;         //填充&CRC 自动剥离使能位,配置MAC不改变帧的内容
    /* Filter function configuration */
    ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable;                             //MAC只把通过了过滤器的帧转发到接收队列中
    ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable;                   //关闭混杂模式
    ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;  //接收所有广播帧
    ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect;       //多播过滤,完美地址过滤
    ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect;           //单播过滤,完美地址过滤
    /*------------------------   DMA   -----------------------------------*/
    /* When we use the Checksum offload feature, we need to enable the Store and Forward mode:
    the store and forward guarantee that a whole frame is stored in the FIFO, so the MAC can insert/verify the checksum,
    if the checksum is OK the DMA can handle the frame otherwise the frame is dropped */
    ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable;
    ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable;
    ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable;
    ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Enable;                        //转发错误帧控制位,接收FIFO会丢弃有错误的帧
    ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Enable;      //转发过短帧控制位,接收FIFO转发长度过短的帧
    ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Disable;
    /* Configure Ethernet */
    ETH_RegInit( Ð_InitStructure, gPHYAddress );

    /* Configure MAC address */
    ETH->MACA0HR = (uint32_t)((macAddr[5]<<8) | macAddr[4]);
    ETH->MACA0LR = (uint32_t)(macAddr[0] | (macAddr[1]<<8) | (macAddr[2]<<16) | (macAddr[3]<<24));

    /* Mask the interrupt that Tx good frame count counter reaches half the maximum value */
    ETH->MMCTIMR = ETH_MMCTIMR_TGFM;
    /* Mask the interrupt that Rx good unicast frames counter reaches half the maximum value */
    /* Mask the interrupt that Rx crc error counter reaches half the maximum value */
    ETH->MMCRIMR = ETH_MMCRIMR_RGUFM | ETH_MMCRIMR_RFCEM;

    ETH_DMAITConfig(ETH_DMA_IT_NIS |\
                ETH_DMA_IT_R |\
                ETH_DMA_IT_T |\
                ETH_DMA_IT_AIS |\
                ETH_DMA_IT_RBU,\
                ENABLE);

    /* Configure the polarity and delay of TXC */
    RGMII_TXC_Delay(0, 4);
#if LINK_STAT_ACQUISITION_METHOD
    /* Configure the PHY interrupt function, the supported chip is: RTL8211FS */
    PHY_InterruptInit( );
    /* Configure EXTI Line7. */
    EXTI_Line_Init( );
#endif
}
(未完待续)


使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

27

主题

65

帖子

1

粉丝