由于我在项目中将该芯片作为PHY和SERDES使用,因此本文内容主要还是围绕PHY和SERDES的相关功能,至于其他功能则没有进行深入研究。
工作模式
在之前的硬件篇中有提到,该芯片有两种寻址模式:单芯片寻址和多芯片寻址。
- 多芯片寻址
对于多芯片寻址,需要给芯片设置一个非零的SMI地址(ADDR[4:0]的反码)。在多芯片寻址模式下,仅有两个寄存器(0x00:SMI Command Register 0x01:SMI Data Register)能够使用,内部各PORT的寄存器均由这两个寄存器间接访问。
通过向SMI Command Register的[9:5]写入内部SMI设备地址,[4:0]写入内部SMI寄存器地址来访问各个Port的内部寄存器,[11:10]决定决前是写入还是读出操作。
通过向SMI Data Register的[15:0]位写入或读取SMI Command Register设定的寄存器内容。
- 单芯片寻址
对于单芯片寻址,其SMI地址为0x00(针对整个芯片),即ADDR[4:0]=0x1F。
在单芯片寻址模式下,每个Port都有各自的SMI地址(针对内部每个Port),Port0-Port6对应的SMI地址分别为0x10-0x16(直接访问)。
若Port0、1、2、5、6接有外部PHY,则必须使用以下SMI地址,0x00对应Port0,0x01对应Port1,0x02对应Port2,0x05对应Port5,0x06对应Port6,这样PPU才能自动的轮询各个PHY获取各自的连接状况,速度,双工情况,流控状态等信息,这些外部PHY需要使用Global2 offset 0x18、0x19进行访问。对于Port3、Port4的内部PHY地址映射为0x03、0x04;Port0、Port1的内部SERDES映射为0x0C、0x0D,同样的,也是需要使用Global2 offset 0x18、0x19进行访问。
Global2 寄存器的 Offset24,25 寄存器
前文提到,两个非常重要的寄存器,即Global2 offset 0x18、0x19寄存器,在单芯片寻址模式下PHY和SERDES寄存器的访问都必须通过这两个寄存器间接访问。
- Global2 offset 0x18 (SMI PHY Command Register)寄存器
与多芯片存储能够访问的Command Register寄存器一样,[9:5]写入SMI设备地址,[4:0]写入SMI寄存器地址来访问各个Port的内部寄存器,[11:10]决定当前是写入还是读出操作。
- Global2 offset 0x19 (SMI PHY Data Register)寄存器
与SMI Data Register相同,通过[15:0]位写入或读取SMI Command Register设定的寄存器内容。
- PHY读写函数(伪代码)
对于单纯的PHY器件,如LAN8720A,我们对于PHY寄存器的访问(按照Clauses 22)直接通过SMI接口设置phy地址,和内部寄存器地址就可以访问了。
Preamble(32bits) | Start(2bits) | OP Code(2bits) | PHYAD(5bits) | REGAD(5bits) | Turn Around(2bits) | Data(16bits) | Idle | |
---|---|---|---|---|---|---|---|---|
Read | 1…1 | 01 | 10 | A4A3A2A1A0 | R4R3R2R1R0 | Z0 | D15…D0 | Z* |
Write | 1…1 | 01 | 01 | A4A3A2A1A0 | R4R3R2R1R0 | Z0 | D15…D0 | Z* |
而这里,对于88E6321的访问就不那么直接了,下面通过伪代码形式讲述如何正确读写内部PHY寄存器:
首先必须实现最直接的SMI读写函数,不同的平台实现方式可能不同,在STM32的HAL库下,提供了
HAL_StatusTypeDef HAL_ETH_WritePHYRegister(ETH_HandleTypeDef *heth, uint16_t PHYReg, uint32_t RegValue)
HAL_StatusTypeDef HAL_ETH_ReadPHYRegister(ETH_HandleTypeDef *heth, uint16_t PHYReg, uint32_t RegValue)
但是这个函数的PhyAddress是由heth带入的,而我们要频繁在各个Port与Global寄存器间切换,因此改写该库函数,PhyAddress主动传入。
HAL_StatusTypeDef HAL_ETH_WritePHYRegister_New(ETH_HandleTypeDef *heth, uint16_t DevAddr, uint16_t PHYReg, uint32_t *RegValue)
HAL_StatusTypeDef HAL_ETH_ReadPHYRegister_New(ETH_HandleTypeDef *heth, uint16_t DevAddr, uint16_t PHYReg, uint32_t *RegValue)
//具体实现就是把原来heth传入的地址替换为DevAddr
有了最基础读写的函数,我们来构建内部PHY读写函数:
/*!
* \fn write_smi_phy_reg
* \brief PHY寄存器设置
*
* \param [in] ETH_HandleTypeDef *heth STM32以太网结构体定义
* \param [in] uint16_t dev_addr PHY地址,单芯片寻址模式下为0x03或0x04
* \param [in] uint16_t reg_addr 寄存器地址
* \param [in] uint32_t reg_value 写入值
*
* \retval error_code_t
*/
error_code_t write_smi_phy_reg(ETH_HandleTypeDef *heth, uint16_t dev_addr, uint16_t reg_addr, uint32_t reg_value)
{
volatile uint16_t time_out;
uint32_t smi_reg;
time_out = 100;
/*!
* 检测当前PHY是否Busy