测试平台: 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
- }
(未完待续)
|