RTL8139收发原理

Tags: , ,

RTL8139的硬件接口由256个字节寄存器组成,这些寄存器按可读性分为只读和可读写两类;按实现收发功能分为发送相关的和接收相关的;按实现收发原理的角色分为,配置寄存器、命令寄存器(或者叫控制寄存器)和状态寄存器;没有数据寄存器,因为RTL8139是PCI设备,使用了一片DMAble的缓冲区作“数据寄存器”。

本文简要地介绍一下RTL8139收发原理,和实现原理的基本寄存器,其它特色功能寄存器请查阅手册。本文先按实现收包原理、发包原理和全局原理,三类精要地介绍相关实现的寄存器。最后简述收包发包过程。

1.寄存器类型

  • CMD:命令类型寄存器
  • CFG:配置类型寄存器
  • STA:状态类型寄存器

“命令”和“配置”的操作语义需要区分,用户发命令,管理员配置,因此,像软重置、开启发送功能等被一贯认为是命令操作,个人认为划归为配置类比较确切。判别“命令”和“配置”的一条简单的方法就是看这些操作是用在设备驱动哪个函数上 ,例如,开启发送功能的(CmdTxEnb)操作是在初始化函数里,所以CmdTxEnb是配置操作,而非命令操作。另一种方法是寄存器(对于主机CPU)的可读写性,STA必定是只读的,CFG必定是只写的,而CMD是可读可写的。

2. 全局原理

2.1 命令寄存器(CR)

Command Register
(Offset 0037h, R/W, CMD)
/* CR register commands */
#define RxBufEmpty 0x01    //STA | 收包缓冲区是否空
#define CmdTxEnb   0x04    //CFG | 激活发送状态机
#define CmdRxEnb   0x08    //CFG | 激活接包状态机
#define CmdReset   0x10    //CFG | 软件重置RTL8139

2.2 中断状态寄存器(ISR)

当RTL8139运行中出现各种事件需要报告给主机CPU,RTL8139置相应中断状态位,并发出中断请求信号。当然信号是否能发出,还取决于中断屏蔽的配置。

Interrupt Status Register
(Offset 003Eh-003Fh, R/W, STA)
/* ISR Bits */
#define RxOK       0x01
#define RxErr      0x02
#define TxOK       0x04
#define TxErr      0x08
#define RxOverFlow 0x10
#define RxUnderrun 0x20
#define RxFIFOOver 0x40
#define CableLen   0x2000
#define TimeOut    0x4000
#define SysErr     0x8000

2.3 中断屏蔽寄存器(IMR)

Interrupt Mask Register
(Offset 003Ch-003Dh, R/W, CFG)
PciErr        = (1 << 15), /* System error on the PCI bus */
TimerIntr    = (1 << 14), /* Asserted when TCTR reaches TimerInt value */
LenChg        = (1 << 13), /* Cable length change */
SWInt        = (1 << 8),  /* Software-requested interrupt */
TxEmpty        = (1 << 7),  /* No Tx descriptors available */
RxFIFOOvr    = (1 << 6),  /* Rx FIFO Overflow */
LinkChg        = (1 << 5),  /* Packet underrun, or link change */
RxEmpty        = (1 << 4),  /* No Rx descriptors available */
TxErr        = (1 << 3),  /* Tx error */
TxOK        = (1 << 2),  /* Tx packet sent */
RxErr        = (1 << 1),  /* Rx error */
RxOK        = (1 << 0),  /* Rx packet received */

3.发包原理

RTL8139被设计使用四块DMAble的缓冲区作“发包数据寄存器”,保存驱动程序发来的数据包。这些缓冲区的地址被配置到RTL8139称为发送描述符(Transmission Descriptors or TD)的寄存器上,共四个,如下图。实际上TD由两个单独的寄存器组成,TSAD保存缓冲区地址,TSD是发送状态,下面会详细介绍。

发包大致过程是,驱动程序首先将数据包拷进一块发送缓冲区,然后将发送状态数据(数据包大小、发送阈值和PCI操作命令)写入缓冲区对应的TSD来启动数据发送;当RTL8139检测到发送命令后[注],RTL8139通过PCI的DMA操作将该数据包读进内部的[Transmit FIFO]。[Transmit FIFO]是芯片内部的2K缓冲区,当[Transmit FIFO]达到由TSD配置的阈值(early transmit threshold)或已将整个数据包拷入时,数据包开始被发到网线上。

注:发包原理中目前存有疑点,在手册上找不到说法,就是是主机主动通过TSD.OWN命令8139执行发送,还是TSD.OWN只是被动的命令配置位,8139循环检测TSD.OWN(四个),自动执行发送操作。目前的理解倾向于后者,因为如果前者的话,发送操作是主机与设备同步的,显然没必要设计四块缓冲区。如果是后者,那么“命令”与“配置”的语义界限变得模糊。这里用我的假设——8139循环检测命令位自动执行发送操作——来描述发包原理,有待进一步确认。

四个TD以循环制(round-robin)的方式使用。驱动程序必须使用两个全局变量——write_buff(记录最新可用缓冲区号)和read_buff(记录最后发送缓冲区号)——来协调循环制的方式使用。驱动程序将数据包拷进[最新可用]的一块发送缓冲区后,更新write_buff,如果write_buff等于read_buff,证明缓冲区已满,必须通知协议栈停止发送;当8139发送完一个数据包后发出“发送完中断请求”,中断处理必须更新read_buff,并唤醒发送队列 。

3.1 发送状态寄存器(TSD)

这是一个非常重要的寄存器,手册很多描述发送原理细节的地方使用了[发送状态描述符(Transmit Status Descriptors or TSD)]概念,但标准标定义却是TSR,看来RTL8139的设计师不只一位,并且没有沟通好。不管名字是什么,反正它就是一四字节寄存器。TSD很混杂,兼有状态、配置和命令的功能,当然只是针对单次发送的。

Transmit Status Register
(TSD0-3)(Offset 0010h-001Fh, R/W, STA|CFG|CMD )
//STA
Bit-14:    TxUnderrun
Bit-15:    TxStatOK
Bit-29:    TxOutOfWindow
Bit-30:    TxAborted
Bit-31:    TxCarrierLost
//CFG
Bit-0~12:    Transmit Byte Count
Bit-16~21:threshold level in the Tx FIFO
//CMD
Bit-13:    TxHostOwns

3.2 发送起始地址寄存器(TSAD)

这就是一个32位地址指针,指向一PCI设备能DAM访问的内存区,名曰发包缓冲区。而这一段缓冲区正是设备——建立在RTL8139硬件之上逻辑网络设备的“数据寄存器”。TSAD(四个)只在设备驱动的初始化代码配置好,一般不会再改写,证明了这一点。

Transmit Start Address of Descriptors
(TSAD0-3)(Offset 0020h-002Fh, R/W, CFG )

3.3 发送配置寄存器(TCR)

配置8139的发送功能,如错误重发、两帧间的时间间隙、添加CRC头和DMA突发访问的数据大小等。

Transmit Configuration Register
(Offset 0040h-0043h, R/W)
/* Bits in TxConfig. */
enum tx_config_bits {
    /* Interframe Gap Time. Only TxIFG96 doesn't violate IEEE 802.3 */
    TxIFGShift    = 24,
    TxIFG84        = (0 << TxIFGShift), /* 8.4us / 840ns (10 / 100Mbps) */
    TxIFG88        = (1 << TxIFGShift), /* 8.8us / 880ns (10 / 100Mbps) */
    TxIFG92        = (2 << TxIFGShift), /* 9.2us / 920ns (10 / 100Mbps) */
    TxIFG96        = (3 << TxIFGShift), /* 9.6us / 960ns (10 / 100Mbps) */

    TxLoopBack    = (1 << 18) | (1 << 17), /* enable loopback test mode */
    TxCRC        = (1 << 16),    /* DISABLE Tx pkt CRC append */
    TxClearAbt    = (1 << 0),    /* Clear abort (WO) */
    TxDMAShift    = 8, /* DMA burst value (0-7) is shifted X many bits */
    TxRetryShift    = 4, /* TXRR value (0-15) is shifted X many bits */

    TxVersionMask    = 0x7C800000, /* mask out version bits 30-26, 23 */
};

4. 收包原理

RTL8139被设计成使用环形缓冲区(ring buffer)作为“收包数据寄存器”来接收数据包。环形缓冲区也必须是DMAble,也就是它是一段物理上连续的内存。RTL8139内置了三个寄存器——RBSTART、CAPR和CBA——实现与主机协作读写环形缓冲区来传递数据。RBSTART是缓冲区的首地址,外部数据包首先通过网线进入RTL8139内部的 Receive FIFO,然后当数据量达到预设的阀值,RTL8139将数据通过PCI拷到缓冲区CBA地址处,完了更新CBA;驱动程序的收包中断则从CAPR地址处读取数据,完了后更CAPR。所以,寄存器CBA(Current Buffer Address)是环形缓冲区的写指针,寄存器CAPR(Current Address of Packet Read)是环形缓冲区的读指针。整个过程是由RTL8139主导的,它负责缓冲区溢出管理。

4.1 接收状态寄存器

这不是一个寄存器,而是对[预-接收状态]的一个备份,附加在数据包的头,给驱动程序接收,以便后续处理。

Receive Status Register in Rx Packet Header
enum RxStatusBits {
    RxMulticast    = 0x8000,
    RxPhysical    = 0x4000,
    RxBroadcast    = 0x2000,
    RxBadSymbol    = 0x0020,
    RxRunt        = 0x0010,
    RxTooLong    = 0x0008,
    RxCRCErr    = 0x0004,
    RxBadAlign    = 0x0002,
    RxStatusOK    = 0x0001,
};

4.2 预-接收状态寄存器(ERSR)

Early Rx Status Register
(Offset 0036h, R, STA)

4.3 接收配置寄存器(RCR)

Receive Configuration Register
(Offset 0044h-0047h, R/W, CFG)

//接收模式配置位
enum rx_mode_bits {
    AcceptErr    = 0x20,
    AcceptRunt    = 0x10,
    AcceptBroadcast    = 0x08,
    AcceptMulticast    = 0x04,
    AcceptMyPhys    = 0x02,
    AcceptAllPhys    = 0x01,
};
//FIFO阀值、DMA burst大小和环缓冲长度的[配置值]
enum RxConfigBits {
    /* rx fifo threshold */
    RxCfgFIFOShift    = 13,
    RxCfgFIFONone    = (7 << RxCfgFIFOShift),

    /* Max DMA burst */
    RxCfgDMAShift    = 8,
    RxCfgDMAUnlimited = (7 << RxCfgDMAShift),

    /* rx ring buffer length */
    RxCfgRcv8K    = 0,
    RxCfgRcv16K    = (1 << 11),
    RxCfgRcv32K    = (1 << 12),
    RxCfgRcv64K    = (1 << 11) | (1 << 12),

    /* Disable packet wrap at end of Rx buffer. (not possible with 64k) */
    RxNoWrap    = (1 << 7),
};

5.发包过程

此过程假设已经配置好发包缓冲区。数据包的发送过程:

  1. 驱动程序将数据包拷到四块缓冲区中空闲的一块上;
  2. 驱动程序写入TSD,包括数据包大小、发送阈值(early transmit threshold)和PCI操作命令(清除OWN位);
  3. RTL8139通过PCI读取数据,在读取过程中,如果RTL8139检测到FIFO达到了阈值,或者发现已经读取了整个数据包,RTL8139开始将FIFO上的数据发到网线上;
  4. 当RTL8139将缓冲区上的数据包整个拷进了FIFO后,RTL8139置OWN为1;
  5. 当RTL8139将整个数据包发到网线上后,RTL8139置ISR.TOK为1,发出“发送完成中断”,如果IMR.TOK 是1,中断请求才成功发出;
  6. 驱动程序的中断处理函数被调用,处理函数过程中必须清除ISR.TOK。

发送过程的状态转换图 (ISR.TOK,ISR.OWN):

6.收包过程

此过程假设已经配置好收包缓冲区。数据包的接收过程:

  1. RTL8139启动后开始监测网线上数据;
  2. 如果有数据包,外部数据包首先通过网线进入芯片内部的 Receive FIFO;
  3. 当数据量达到预设的阀值,RTL8139将数据通过PCI拷到环形缓冲区;
  4. 当整个数据包都拷缓冲区后,RTL8139会在数据包的前面写数据头(包括接收状态和包长度信息),然后更新缓冲区的写指针CBA;
  5. RTL8139置CR.BUFE为1,收包缓冲不为空;然后置ISR.ROK,发出收包中断;
  6. RTL8139计算收包缓冲大小(比对CAPR和CBA),如果太小(如<3K),对外发出暂停控制帧,否则回到第一步;
  7. 驱动程序的中断处理函数被调用,处理收包,完了后清除ISR.ROK,还有更新CAPR。
刘建文原创,引用请注明出处。

Leave a comment