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