我们将为大家主要讲解从PHY的硬件连接、PHY的寄存器说明和Renesas工程样例代码三个方面的内容,旨在对Renesas RZ T/N系列、TPS-1、EC-1等芯片需要作PHY chip适配开发的客户提供参考。
RZ/T系列MPU
基于Arm®的RZ/T系列MPU通过工业以太网通信提供高性能和高速实时控制,为自动化市场构建高性能系统。RZ/T MPU和RZ/N MPU均基于类似的硬件架构开发而成,并共享相同的软件环境,可实现可扩展产品开发。RZ/T系列MPU支持多种协议,例如EtherCAT®、PROFINET、Ethernet/IP™和下一代TSN。RZ/T MPU配备编码器接口,可支持各种编码器协议。
RZ/T系列:https://www.renesas.cn/cn/zh/products/microcontrollers-microprocessors/rz-mpus/rzt-series-mpu
RZ/N系列MPU
基于Arm®的RZ/N系列MPU是可扩展的高性能通信处理器系列,可以轻松实现包括TSN在内的多种工业以太网协议以及用于工业系统的冗余网络技术。配备千兆交换机,可以轻松连接网络协议;具有丰富的外围设备,可以支持工业以太网的各种应用需求。
RZ/N系列:https://www.renesas.cn/cn/zh/products/microcontrollers-microprocessors/rz-mpus/rzn
目录
-
以太网PHY硬件连接
-
MAC与PHY之间的接口标准 MII/GMII
-
以太网PHY寄存器分析
-
以太网PHY驱动软件配置
-
其它
以太网PHY硬件连接
MAC与PHY之间的接口标准 MII/GMII
Media Independent Interface ( MII ),介质独立接口,起初是定义100M以太网(Fast Ethernet)的 MAC 层与 PHY 芯片之间的传输标准(802.3u)。介质独立的意思是指,MAC与PHY之间的通信不受具体传输介质(双绞线或光纤等)的影响,任何MAC和PHY都可以通过MII接口互连。MAC与PHY之间的MII连接可以是可插拔的连接器,或者是同一块PCB上MAC与PHY之间的走线。MDIO 是MII接口的一部分,用于在MAC和PHY之间传递配置信息。在系统上电瞬间,PHY芯片通过管脚的电平状态确定原始设置,进而通过MDIO更改配置。最初MII定义数据 4 bit 发送+ 4 bit 接收,每位数据速率 25MHz ,总数据速率 100Mbps 。其它 MII 标准的变种,包括 RMII,GMII,RGMII等等,基本上都是定位于更高速率或者更少的信号线的目的,如下图所示在以太网通信层次模型中MII接口的位置。

a. MII接口介绍
-
发送端信号:TXCLK, TXD[0-3], TXEN, TXER
-
接收端信号:RXCLK, RXD[0-3], RXDV,RXER,CRS,COL
-
配置信号:MDIO,MDC

b. 接口信号连接,如下图后示
其中TXER为选配。MII共有18根信号线,只有MDIO/MDC信号可以在不同PHY间级联。假定系统中有8个PHY的话,刚MII信号线总数为 8*16 + 2 = 130根信号线,为减少信号数,RMII接口应运而生。

c. RMII(Reduced Media Independent Interface)相比MII接口,有以下四处变化
RMII信号连接信图如下图所示,RMII只要9根信号线,相比于MII的18根信号线减少了不少。在同一个系统中的多个设备可以共享MIDO, MDC和REFCLK信号线。

d. GMII:Gigabit Media Independent Interface
GMII接口的数据速率可达 1000Mbps,其时钟频率为 125MHz ,单向数据位宽 8 bits。GMII向下兼容MII,可以像MII一样工作在 100Mbps 和 10Mbps 的数据速率。
GMII接口信号包括三类,分别为:
-
发送端信号:GTXCLK, TXCLK, TXD[0-7], TXEN, TXER
-
接收端信号:RXCLK, RXD[0-7], RXDV, RXER, CRS, COL
-
配置信号: MDIO, MDC
发送端包括两个时钟信号 GTCLK和TXCLK, 当设备工作于1000Mpbs模式时, TXD, TXEN, TXER 是与 GTXCLK (125MHz)同步的。而在 10/100Mbps 工作模时,以上数据信号是同步于同PHY供的TXCLK的,基中100Mbps时是25MHz,10Mbps时是2.5MHz。接收端时钟只有一个时钟信号RXCLK,它是从接收数据中恢复时钟。

e. RGMII: Gigabit Media Independent Interface
RGMII相比GMII减少了近一半的管脚数,取消不重要的如CRS, COL等信号。在RGMII接口中MAC在TXC上一直提供时钟信号,而不像在GMII接口那样,10/100Mbps模式下时钟由PHY提供,而 1000Mbps 模式下时钟是由 MAC 提供(GTXCLK)。在RGMII中应用到源同步时钟,即数据与时钟信号是同步的。这要求在PCB设计中,要对时钟信号额外增加1.5~2ns的延迟以保证接收端的建立/保持时间满足要求。在RGMII v2.0规范中有定义MAC/PHY内部延迟(RGMII-ID),由此避免PCB设计中再要增加这个延迟。
在RGMII接口中:
- 1000Mbps模式,数据在时钟的上/下边沿均采样
- 10/100Mbps模式,数据仅在时钟上升沿采样
RXCTL 和 TXCLT 为复用的传输控制信号。RXCTL 在时钟的上升沿代表 RXDV,在时钟的下降沿代表(RXDV xor RXER);TXCTL 在时钟的上升沿代表 TXEN,在时钟的下降沿代表(TXEN xor TXER)。
RGMII v1.3采用2.5v CMOS电平,RGMII v2.0采用1.5v HSTL电平。

以太网PHY寄存器分析
\PHY是IEEE802.3中定义的一个标准模块,MAC或着MPU通过SMI(Serial Manage Interface)对PHY的行为、状态进行管理和控制,而具体管理和控制动作是通过读写PHY内部的寄存器实现的。PHY寄存器的地址空间为5位,从0到31最多可以定义32个寄存器(随着芯片功能不断增加,很多PHY芯片采用分页技术来扩展地址空间以定义更多的寄存器,在此不作讨论),IEEE802.3定义了地址为0-15这16个寄存器的功能,地址16-31的寄存器留给芯片制造商自由定义(不同厂商的芯片有所差异),如表1所示,对IEEE802.3定义的寄存器各项功能进行分析。
表1 PHY管理寄存器集

Control Register
寄存器0是PHY控制寄存器,通过Control Register可以对PHY的主要工作状态进行设置。Control Register的每一位完成的功能见下表。
表2 Control Register

Reset:Bit15控制的是PHY复位功能,在该位置写入1实现对PHY的复位操作。复位后该端口PHY的其他控制、状态寄存器将恢复到默认值,每次PHY复位应该在0.5s的时间内完成,复位过程中Bit15保持为1,复位完成之后该位应该自动清零。一般要改变端口的工作模式(如速率、双工、流控或协商信息等)时,在设置完相应位置的寄存器之后,需要通过Reset位复位PHY来使配置生效。
Loopback:Loopback是一个调试以及故障诊断中常用的功能,Bit14置1之后,PHY和外部MDI的连接在逻辑上将被断开,从MAC经过MII/GMII(也可能是其他的MAC/PHY接口)发送过来的数据将不会被发送到MDI上,而是在PHY内部(一般在PCS)回环到本端口的MII/GMII接收通道上,通过Loopback功能可以检查MII/GMII以及PHY接口部分是否工作正常,对于端口不通的情况可用于故障定位。如下图所示。需要注意的是,很多时候PHY设置Loopback后端口可能就Link down了,MAC无法向该端口发帧,这时就需要通过设置端口Force Link up才能使用Loopback功能

要进一步更精确的定位问题,我们还可以去查询MAC芯片是否有类似的端口Loopback功能,如果有则在MAC内部也做一下Loopback观察是否有CRC;如果没有,可以将MAC和PHY的RGMII接口断开,将MAC的RGMII发送和接收通道自己连接起来,将PHY的RGMII发送和接收通道自己连接起来,分别做砸帧测试观察有没有CRC,这样就可以进一步的缩小范围。不过这个S3760的案例有其特殊性,98EX126没有端口的Loopback功能,而MAC的RGMII发送信号直接连接到PHY,中间没有电阻,而且两者都是BGA封装,这两个实验都没办法进行。因此故障排查中需要检查的范围就比较广一点了。但是从中我们我们可以看出,Loopback操作在故障定位中可以起到将各个功能模块隔离定位的作用,虽然这些模块在物理上是集成在一个芯片中的。这种分割隔离的思想在故障定位中是非常重要的。
peed Selection:Bit13和Bit6两位联合实现对端口的速率控制功能,具体的对应关系祥见表2。需要注意的是Speed Selection只有在自动协商关闭的情况下才起作用,如果自动协商设置为Enable状态,则该设置不起作用;并且,对Speed Selection的修改设置,往往需要复位端口才能配置生效。因此在设置该位置的时候需要检查自动协商的设置并通过Bit15复位端口。
Auto-Negotiation Enable:自动协商(AN)开关。设置为1表示打开AN功能,端口的工作模式通过和连接对端进行AN来确定。如果设置为0则AN功能关系,端口的工作模式通过Control Register相应位置的配置决定。必须注意的是,对于1000BASE-T接口,自动协商必须打开。
Power Down:端口工作开关。设置为1将使端口进入Power Down模式,正常情况下PHY在Power Down模式其MII和MDI均不会对外发送数据。Power Down模式一般在软件shut down端口的时候使用,需要注意的是端口从Power Down模式恢复,需要复位端口以保证端口可靠的连接。
Isolate:隔离状态开关。置1将导致PHY和MII接口之间处于电气隔离状态,除了MDC/MDIO接口的信号外,其他MII引脚处于高阻态。IEEE802.3没有对Isolate 时MDI接口的状态进行规范,此时MDI端可能还在正常运行。Isolate在实际应用中并没有用到。并且,值得注意的是,由于目前很多百兆的PHY芯片其MAC接口主流的都是SMII/S3MII,8个端口的接口是相互关联的,一个端口设置Isolate可能会影响其他端口的正常使用,因此在使用中注意不要随意更改bit10的状态。
Restart Auto-Negotiation :重新启动自动协商开关。Bit9置1将重新启动端口的自动协商进程,当然前提是Auto-Negotiation Enable是使能的。一般在修改端口的自动协商能力信息之后通过Bit9置1重新启动自动协商来使端口按照新的配置建立link。
Duplex Mode:双工模式设置。Bit8置1端口设置为全双工,置0则端设置为半双工,和Speed Selection的设置一样,Duplex Mode的设置只有在自动协商关闭的情况下才起作用,如果自动协商设置为Enable状态,则该设置不起作用,端口的双工模式根据AN结果来定。对Duplex Mode的修改配置也需要复位端口才能生效。
Collision Test:冲突信号(COL)测试开关。在需要对COL信号进行测试时,可以通过Bit7置1,这时PHY将输出一个COL脉冲以供测试。实际测试操作中也可以将端口配置为半双工状态,通过发帧冲突来测试COL信号,因此该配置实用价值不大。
Status Register
寄存器1是PHY状态寄存器,主要包含PHY的状态信息,大多数bit的值都是由芯片厂家确定的,每一个bit的功能在表3种已有详细说明。其中指示PHY所具有的工作模式能力的寄存器不再多讲,值得注意的有以下几位。
表3 Status register

Auto-Negotiation Complete:AN完成状态指示位。Bit5指示的是端口AN进程是否完成的状态位。在AN Enable的情况下,Bit5=1表示自动协商进程已经成功结束,此时PHY的其他和Link状态相关的寄存器才是正确可靠的。如果AN进程没有完成,则这些状态信息可能是错误的。在调试以及异常故障处理时,可以通过该位寄存器的状态判断AN是否成功,从而进一步的检查AN相关的设置是否正确,或者芯片的AN功能是否正常等。
Remote Fault:远端错误指示位。Bit4=1代表连接对端(Link Partner)出错,至于出错的具体类型以及错误检测机制在规范中并没有定义,由PHY的制造商自由发挥,一般的厂商都会在其他的寄存器(Register16-31由厂商自行定义)指示比较详细的错误类型。在与端口相关的故障查证中,Remote Fault是一个重要的指示信息,通过互联双方的Remote Fault信息(可能要加上其他的具体错误指示),可以帮助定位故障原因。
Link Status:Link状态指示位。Bit2=1代表端口Link up,0则代表端口Link down。实际应用中一般都是通过Bit2来判断端口的状态。而且,一般的MAC芯片也是通过轮询PHY的这个寄存器值来判断端口的Link状态的(这个过程可能有不同的名称,比如BCM叫做Link Scan,而Marvell叫做PHY Polling。)如前所述,在AN Enable的情况下,Link Status的信息只有在Auto-Negotiation Complete指示已经完成的情况下才是正确可靠的,否则有可能出错。
Jabber Detect:Jabber 检测指示位。IEEE802.3对Jabber的解释是"A condition wherein a station transmits for a period of time longer than the maximum permissible packet length, usually due to a fault condition"。这一位指示的是Link Partner发送的时间超过了规定的最大长度。值得注意的是,Jabber Detect只有在10BASE-T模式下才有意义,100和1000M模式是没有定义Jabber这一功能的。
PHY Identifier Register
寄存器2和3存放PHY芯片的型号代码,由芯片制造商自行定义,实际应用中软件通过读取这两个寄存器的内容可以识别PHY的型号和版本,这些内容都是只读寄存器,对PHY的功能没有影响,也不反映PHY的工作状态。
Auto-Negotiation Advertisement Register
寄存器4是自动协商的能力通告寄存器,在AN Enable的前提下(见寄存器0),端口根据该寄存器的相关配置将自动协商信息通过FLP在MDI上进行通告。当AN配置为Disable状态的时候,寄存器4的配置将不起作用,端口的工作模式由控制寄存器中的配置决定。寄存器4的详细定义对电口和光口PHY上有不同的定义,其中电口PHY的具体说明如表4A。每个bit的功能已有详细描述,无需赘述。
表4A Auto-Negotiation Advertisement Register(Copper)

Bit12:5对应自动协商广播能力域(Technology Ability Field),每一位分别对应为下表A[7:0],每一位配置一种工作模式的能力。在实际应用中,如果PHY要支持该种工作模式则对应位置1,若不支持则对应位置0。注意到在这8位能力指示域中,并没有1000BASE-T能力的对应配置位,1000BASE-T的相关配置在寄存器9,MASTER-SLAVE Control Register来完成。
Bit4:0配置自动协商的类型,规范正在发送的自动协商信息遵从何种规范,我们所接触的以太网PHY遵从IEEE802.3规范,Selector Field=0001,该区域不可随意更改(很多PHY将此区域设计为只读寄存器,以免被修改)。
Technology Ability Field

Auto-Negotiation Link Partner Base
Page Ability Register
寄存器5保存的是本端PHY接收到的对端PHY所通告的端口能力,寄存器5的结构和寄存器4基本一致。应用上,寄存器5可以用于检测Link partner的自动协商配置,在端口Link故障的定位排查中可以发挥重要作用。特别是当Link Partner其内部寄存器信息我们是无法获取的,这是侯就只能通过寄存器5来获取对方的自动协商信息了。不单单是AN信息,端口的状态信息中所有关于Link Partner状态的指示信息在我们进行故障处理的时候都是很珍贵的第一手资料,通过分析这些信息对我们进行故障定位将有很大的帮助。
Auto-Negotiation Expansion Register
寄存器6保存了PHY自动协商过程的异常信息,每一位的作用在表5中一目了然。从这个寄存其中我们可以获取到Link Partner子否支持自动协商以及自动协商下一页有没有收到的信息。其中Parallel Detection Fault表示,端口在并行检测进程中出现了错误,这包含了两层意义:首先PHY已经启动并行检测,则Linkpartner不支持AN,再则并行检测不能成功的探测到Linkpartner的连接速率信息。
表5 Auto-Negotiation Expansion Register

寄存器7和8分别保存了Local PHY和Linkpartner的自动协商下一页信息,AN的下一页功能通常在1000M模式的自动协商下使用,详细地寄存器信息要结合PHY芯片的资料进行分析,本文不作详细讨论。
1000BASE-T Control Register
寄存器9保存的是1000BASE-T模式的配置信息,控制PHY的AN信息中与1000BASE-T相关的协商信息,以及PHY在1000BASE-T模式下的信息,详细信息见表6。
表6 MASTER-SLAVE Control Register

寄存器10是1000BASE-T模式的状态寄存器,指示PHY及其Linkpartner的状态信息。详细的状态描述见表7,表格中各个状态位的具体含义说明的相当清楚了,无需赘述。需要注意的是,关于Linkpartner的信息是通过自动协商完成的,而1000BASE-T的协商信息是通过Next Page交互的,因此只有在寄存器6中确认Next Page已经收到,寄存器10的Linkpartner信息才是有效的。否则有可能是错误信息。
表7 MASTER-SLAVE Status Register

MMD EEE Access Control Register & MMD
Address or Data Register
MMD (MDIO Manageable device) register,主要是提供对多个MMD 设备地址间接读/写访问,每个设备支持多达16位寄存器。关于支持MMD设备地址列表,需要参阅手册寄存器映射部分及相关寄存器地址。以Microsemi的VSC8541-02为例,当MMD Access Control Register 的14:15 = 2'b00, 需要访问的设备地址将体现在 MMD EEE Access Control Register的bit 4:0,否则就是在MMD Address or Data Register这个寄存器当中去写数据或者读数据。
Extended Status Register
寄存器Extended Status Register由PHY厂商在PHY中写入的指示PHY功能的状态寄存器,标明PHY是否具有1000BASE-X或者1000BASE-T的能力,实际应用和调试中实用价值不大。
PHY扩展寄存器分析
除了IEEE802.3定义的Register0-15外,Register16-31由PHY制造商自行定义,还有制造商通过分页存储技术扩展的更多寄存器空间,在这些寄存器中制造商定义了很多PHY的功能的控制以及状态指示信息,这些信息对我们在PHY的应用以及故障诊断中有时候可以起到决定性的作用,但是由于这写寄存器不是IEEE802.3标准定义的,因此寄存器的地址以及功能名称在不同厂家的资料中有很大的差异,甚至在同一厂家的不用芯片中也不尽相同,因此下面的讨论只能就某一类的功能应用或者状态指示进行说明,但是其详细的名称和寄存器的地址要结合具体芯片具体分析,这里不能给出一个确切的答案。如下图是microsemi的VSC8541-02/05的寄存器空间图:

Microchip KSZ9031RNX的寄存器空间图:

目前主流的PHY都通过分页技术对PHY寄存器空间进行扩展,提供更多的寄存器空间来控制PHY更多的功能行为和提供更多的PHY状态指示信息。分页扩展出来的寄存器空间其具体作用千变万化,在此不作详细讨论。
以太网PHY驱动软件配置
这里以Renesas提供的RZ/T2M工程样例“RZT2M_EtherCAT_RSK_rev0100”为例对PHY驱动的软件配置流程进行说明。此工程样例可以在Renesas提供的开发版上运行和调试。开发套件的使用文件《r20ut4939eg0050-rskrzt2m-usermanual_c.pdf》可以上Renesas官方网站上获取,开发板也可以申请购买或者是借用。
驱动配置的入口
void hal_entry (void)
{
fsp_err_t err;
/* TODO: add your own code here */
/* Initialize EtherCAT SSC Port */
err = RM_ETHERCAT_SSC_PORT_Open(gp_ethercat_ssc_port->p_ctrl, gp_ethercat_ssc_port->p_cfg);
if(FSP_SUCCESS != err)
{
__BKPT(0); /* Can't continue the stack */
}
...
}
进入RM_ETHERCAT_SSC_PORT_Open(), 这个EtherCAT接口配置函数之后,可以看到EtherCAT Slave Controller的一些初始化配置,其中就包括了PHY的初始化:
/* Open Ether-Phy Driver */
for (i = 0; BSP_FEATURE_ESC_MAX_PORTS > i; i++)
{
p_ether_PHY_instance = (ether_PHY_instance_t *) p_extend->p_ether_PHY_instance[i];
if (NULL != p_ether_PHY_instance)
{
err = p_ether_PHY_instance->p_api->open(p_ether_PHY_instance->p_ctrl, p_ether_PHY_instance->p_cfg);
}
if (FSP_SUCCESS == err)
{
opened_PHY[i] = 1;
}
else
{
break;
}
}
PHY驱动配置相关数据结构解析
这里初始化的一个PHY实例是:
p_ether_PHY_instance,它是一个ether_PHY_instance_t类型的变量。
typedef struct st_ether_PHY_instance
{
ether_PHY_ctrl_t * p_ctrl; ///< Pointer to the control structure for this instance
ether_PHY_cfg_t const * p_cfg; ///< Pointer to the configuration structure for this instance
ether_PHY_api_t const * p_api; ///< Pointer to the API structure for this instance
} ether_PHY_instance_t;
其中ether_PHY_ctrl_t是指向PHY实例的控制结构体;
ether_PHY_cfg_t是指向实例配置的结构体指针;
ether_PHY_api_t是实例配置过程中需要调用到的函数方法所组成的结构体指针;
这个PHY的实例是在调用RM_ETHERCAT_SSC_PORT_Open()函数的时候形参传递进来的,也就是gp_ethercat_ssc_port。
ethercat_ssc_port_instance_t const * gp_ethercat_ssc_port = &g_ethercat_ssc_port0;
而gp_ethercat_ssc_port这个ethercat_ssc_port_instance_t类型的全局指针是指向一个常量,也就是下面代码中的g_ethercat_ssc_port0。
/* Instance structure to use this module. */
const ethercat_ssc_port_instance_t g_ethercat_ssc_port0 =
{
.p_ctrl = &g_ethercat_ssc_port0_ctrl,
.p_cfg = &g_ethercat_ssc_port0_cfg,
.p_api = &g_ethercat_ssc_port_on_ethercat_ssc_port
};
可以看到g_ethercat_ssc_port0是一个常量结构体,它的成员变量分别是:
g_ethercat_ssc_port0_ctrl指向ethercat_ssc_port0控制结构体指针;
g_ethercat_ssc_port0_cfg指向ethercat_ssc_port0配置结构体指针;
g_ethercat_ssc_port_on_ethercat_ssc_port指向ethercat_ssc_port0配置方法的结构指针。
看到这里是不是有一种似成相识的感觉?g_ethercat_ssc_port0是对ethercat_ssc_port0这个外设的驱动的描述体,与前面PHY驱运的描述体“p_ether_PHY_instance”结构上很相似,其实工程样例中所有的外设驱动都可以使用类似的结构体去完成相应的初始化。比如说timer驱动描述结构体:
/** This structure encompasses everything that is needed to use an instance of this interface. */
typedef struct st_timer_instance
{
timer_ctrl_t * p_ctrl; ///< Pointer to the control structure for this instance
timer_cfg_t const * p_cfg; ///< Pointer to the configuration structure for this instance
timer_api_t const * p_api; ///< Pointer to the API structure for this instance
} timer_instance_t;
这种相似的驱动描述体其实就是工程样例驱动代码部分的大致框架所在,撑握了这个脉络即可以方便的看懂其它外设驱动的代码,也可以在以后的驱动开发过程中参考这种框架,提升代的通用性和可读性。
我们知道PHY的驱动是ethercat_ssc_port0外设驱动的子模块。因为要在RZ/T2M这个芯片上使能EtherCAT功能块,除了要完成芯片本身相关外设的初始化之外,还要完成与之对应的PHY的初始化。那么两者是如何关联在一起的呢?我们继续解读g_ethercat_ssc_port0这个全局结构体。可以看到g_ethercat_ssc_port0_cfg所指向的内容是配置ethercat_ssc_port0的描述体,如下所示:
/** Configuration parameters. */
typedef struct st_ethercat_ssc_port_cfg
{
uint32_t reset_hold_time; ///< PHY Reset signal hold time (ms)
uint32_t reset_wait_time; ///< Wait time after PHY reset relase (us)
uint32_t offset_address; ///< PHY offset PHYsical address
IRQn_Type esc_cat_irq; ///< EtherCAT IRQ interrupt number
uint8_t esc_cat_ipl; ///< EtherCAT interrupt priority
IRQn_Type esc_sync0_irq; ///< EtherCAT Sync0 IRQ interrupt number
uint8_t esc_sync0_ipl; ///< EtherCAT Sync0 interrupt priority
IRQn_Type esc_sync1_irq; ///< EtherCAT Sync1 IRQ interrupt number
uint8_t esc_sync1_ipl; ///< EtherCAT Sync1 interrupt priority
///< Callback provided when an ISR occurs
void (* p_callback)(ethercat_ssc_port_callback_args_t * p_args);
timer_instance_t const * p_timer_instance; ///< Pointer to Timer instance
/** Placeholder for user data. Passed to the user callback in ethercat_ssc_port_callback_args_t. */
void const * p_context;
void const * p_extend; ///< Placeholder for user extension.
} ethercat_ssc_port_cfg_t;
对PHY的复位信号保持时间有描述,还有对EtherCAT中断有作描述,在此不展开讨论。其中p_extend成员是用户用于扩展控制的占位符。这也正是PHY驱动与ethercat_ssc_port0驱动关联的关键所在。代码赋于这个占位符是一个指向扩展配置的结构体指针。具体可以看看这个结构体的内容如下:
/** Extended configuration */
typedef struct s_ethercat_ssc_port_extend_cfg
{
ethercat_ssc_port_eeprom_size_t eeprom_size; ///< EEPROM memory size
ethercat_ssc_port_txc_delay_t txc0; ///< Port 0 TXC delay time
ethercat_ssc_port_txc_delay_t txc1; ///< Port 1 TXC delay time
ethercat_ssc_port_txc_delay_t txc2; ///< Port 2 TXC delay time
ether_PHY_instance_t const * p_ether_PHY_instance[BSP_FEATURE_ESC_MAX_PORTS];
///< Pointer to ETHER_PHY instance
} ethercat_ssc_port_extend_cfg_t;
const ethercat_ssc_port_extend_cfg_t g_ethercat_ssc_port0_ext_cfg =
{
.eeprom_size = ETHERCAT_SSC_PORT_EEPROM_SIZE_UNDER_32KBIT,
.txc0 = ETHERCAT_SSC_PORT_TXC_DELAY_00NS,
.txc1 = ETHERCAT_SSC_PORT_TXC_DELAY_00NS,
.txc2 = ETHERCAT_SSC_PORT_TXC_DELAY_00NS,
.p_ether_PHY_instance[0] =
#define FSP_NOT_DEFINED (1)
#if (FSP_NOT_DEFINED == g_ether_PHY0)
NULL,
#else
&g_ether_PHY0,
#endif
.p_ether_PHY_instance[1] =
#if (FSP_NOT_DEFINED == g_ether_PHY1)
NULL,
#else
&g_ether_PHY1,
#endif
.p_ether_PHY_instance[2] =
#if (FSP_NOT_DEFINED == FSP_NOT_DEFINED)
NULL,
#else
&FSP_NOT_DEFINED,
#endif
};
里面就对应有ether_PHY_instance_t类体的初始化值,这值的类型正好是PHY实例所对应的描体结构体如下代码所示,所以关联就产生了。
typedef struct st_ether_PHY_instance
{
ether_PHY_ctrl_t * p_ctrl; ///< Pointer to the control structure for this instance
ether_PHY_cfg_t const * p_cfg; ///< Pointer to the configuration structure for this instance
ether_PHY_api_t const * p_api; ///< Pointer to the API structure for this instance
} wh wether_PHY_instance_t;
我们对g_ethercat_ssc_port0_ext_cfg这个全局变量深入追踪,其成员变量 g_ether_PHY0,正好是一个PHY实例的详细描述体。
/* Instance structure to use this module. */
const ether_PHY_instance_t g_ether_PHY0 =
{
.p_ctrl = &g_ether_PHY0_ctrl,
.p_cfg = &g_ether_PHY0_cfg,
.p_api = &g_ether_PHY_on_ether_PHY
};
其中g_ether_PHY0_cfg是pyh实例的配置结构体:
const ether_PHY_cfg_t g_ether_PHY0_cfg =
{
.channel = 0,
.PHY_lsi_address = 0,
.PHY_reset_wait_time = 0x00020000,
.mii_bit_access_wait_time = 0, // Unused
.flow_control = ETHER_PHY_FLOW_CONTROL_DISABLE,
.mii_type = (ether_PHY_mii_type_t) 0, // Unused
.p_context = NULL,
.p_extend = &g_ether_PHY0_extend
};
这里又通过p_extend 做了扩展配置(其实可以合并在一起)如下所示:
const ether_PHY_extend_cfg_t g_ether_PHY0_extend =
{
.port_type = ETHER_PHY_PORT_TYPE_ETHER_CAT,
.PHY_chip = (ether_PHY_chip_t) ETHER_PHY_CHIP_VSC8541,
.mdio_type = ETHER_PHY_MDIO_GMAC,
.bps = ETHER_PHY_SPEED_100,
.duplex = ETHER_PHY_DUPLEX_FULL,
.auto_negotiation = ETHER_PHY_AUTO_NEGOTIATION_ON,
.PHY_reset_pin = BSP_IO_PORT_20_PIN_7,
.PHY_reset_time = 15000,
.p_selector_instance = (ether_selector_instance_t *)&g_ether_selector0,
};
可以看到上面的扩展配置当中,PHY的具体硬件型号都已经列出,如PHY_chip = (ether_PHY_chip_t) ETHER_PHY_CHIP_;
可以看到在示例代码当中已经支持的PHY如下所示:
/** Identify PHY-LSI */
typedef enum e_ether_PHY_chip
{
ETHER_PHY_CHIP_VSC8541 = (1 << 0), ///< VSC8541
ETHER_PHY_CHIP_KSZ9131 = (1 << 1), ///< KSZ9131
ETHER_PHY_CHIP_KSZ9031 = (1 << 2), ///< KSZ9031
ETHER_PHY_CHIP_KSZ8081 = (1 << 3), ///< KSZ8081
ETHER_PHY_CHIP_KSZ8041 = (1 << 4) ///< KSZ8041
} ether_PHY_chip_t;
这里具体看一下 g_ether_selector0 这个 ether_selector_instance_t 类型的全局指针,指向 selector driver实例的成员变量:
typedef struct st_ether_selector_instance
{
ether_selector_ctrl_t * p_ctrl; ///< Pointer to the control structure for this instance
ether_selector_cfg_t const * p_cfg; ///< Pointer to the configuration structure for this instance
ether_selector_api_t const * p_api; ///< Pointer to the API structure for this instance
} ether_selector_instance_t;
这又是一个类似的结构体,通过三个指针来分别指向结构本身,selector的具体配置,和配置selector过程中所需要用的的成员方法api.
看一下selector的具体配置信息:
typedef struct st_ether_selector_cfg
{
uint8_t port; ///< Port number
ether_selector_PHYlink_polarity_t PHYlink; ///< PHY link signal polarity
ether_selector_interface_t interface; ///< Converter mode
ether_selector_speed_t speed; ///< Converter Speed
ether_selector_duplex_t duplex; ///< Converter Duplex
ether_selector_ref_clock_t ref_clock; ///< Converter REF_CLK
void const * p_extend; ///< Placeholder for user extension.
} ether_selector_cfg_t;
可以看到selector 对应的端口号,PHY连接信号对应的极性,接口模式,速率,全双工,以及外部时钟输入。再看一下配置selector的过程中所需要用到的API函数:
const ether_selector_api_t g_ether_selector_on_ether_selector =
{
.open = R_ETHER_SELECTOR_Open,
.converterSet = R_ETHER_SELECTOR_ConverterSet,
.close = R_ETHER_SELECTOR_Close,
.versionGet = R_ETHER_SELECTOR_VersionGet
};
其最主要的成员方法就是R_ETHER_SELECTOR_Open做了些什么:
先初始化ETHER_SELECTOR
/* One time initialization for all ETHER_SELECTOR instances. */
r_ether_selector_state_initialize();
/* Unlock write access protection for Ethernet subsystem registers */
r_ether_selector_reg_protection_disable(p_reg_ethss);
/* Set the function of Ethernet ports. */
sw_mode = ETHER_SELECTOR_CFG_MODE;
p_reg_ethss->MODCTRL_b.SW_MODE = sw_mode & ETHER_SELECTOR_MODCTRL_BIT_SWMODE_MASK;
/* Set the MAC of all port for half-duplex. */
p_reg_ethss->SWDUPC_b.PHY_DUPLEX = 0;
/* Set all Ethernet switch port to select not use 10Mbps. */
p_reg_ethss->SWCTRL_b.SET10 = 0;
根据端口号来选择对应控制寄存器
/* Set RGMII/RMII Converter configuration */
switch (port)
{
case 0:
{
p_reg_convctrl = (uint32_t *) &p_reg_ethss->CONVCTRL[0];
break;
}
case 1:
{
p_reg_convctrl = (uint32_t *) &p_reg_ethss->CONVCTRL[1];
break;
}
case 2:
default:
{
p_reg_convctrl = (uint32_t *) &p_reg_ethss->CONVCTRL[2];
break;
}
}
根据指向selector的配置信息:
const ether_selector_cfg_t g_ether_selector0_cfg =
{
.port = 0,
.PHYlink = ETHER_SELECTOR_PHYLINK_POLARITY_LOW,
.interface = ETHER_SELECTOR_INTERFACE_RGMII,
.speed = ETHER_SELECTOR_SPEED_100MBPS,
.duplex = ETHER_SELECTOR_DUPLEX_FULL,
.ref_clock = ETHER_SELECTOR_REF_CLOCK_INPUT,
.p_extend = NULL,
};
来对CONVCTRL[port_number]寄存器做相应的配置

这里结合RZ/T2M的用户手册,很容易理解其中的意思:

结合代码来看,总体ETHER_SELECTOR 的驱动的配置流程图台下所示:

在对ETHER_SELECTOR驱动做完配置后,下面具体看一下对ETHER_PHY_CHIP这个PHY,代码具体做了哪些操作:
首先是做初始化:

oid ether_PHY_targets_initialize_vsc8541 (ether_PHY_instance_ctrl_t * p_instance_ctrl)
{
/* Vendor Specific PHY Registers */
#define ETHER_PHY_REG_LED_MODE_SELECT (0x1D)
#define ETHER_PHY_REG_LED_BEHAVIOR (0x1E)
#define ETHER_PHY_REG_EXTEND_GPIO_PAGE (0x1F)
...
这个初始化函数,并没有对IEEE 标准规定的16个寄存器做读写操作,只对厂商自定义的寄存器做了配置。初始化完成之后,对是否打开自动协商的功能对PHY进行了读写:


这里可以看到对PHY芯生来说,需要配置的寄存器并不是很多,大多数情况下,把自动协商寄存器配置好,就可以了。除此之后就是厂商自定义的寄存器的一些自定义的功能。这部分功能需要结合用户手册来理解和使用,大部分也是用来调试和指示的作用以及一些IEEE基本标准之外的特色功能,比如节能标准之类的。
对于用户说来,搞清楚数据结构之间的关联,剩下的就是驱动代码的执行逻辑,考虑到执行逻辑并不复杂,这里不展开来说。用户可以参考录屏材料进一步深入了解。
其它
经过验证的PHY芯片列表:

您可复制下方网址到浏览器中打开进入瑞萨中文论坛查看:https://community-ja.renesas.com/zh/forums-groups/mcu-mpu/