接上文Lwip中实现DM9000/DM9003驱动之一_龙赤子的博客-CSDN博客
目录
四:DM9003EP介绍
1.芯片特性
DM9003EP是一款完全集成的,高性能的,并且是经济适用的快速以太网交换控制器(switch controller)。它具有如下特性:
- 一个通用总线接口以及两个10M/100Mbps PHY端口
- 提供TCP/UDP/IPv4校验和的生成和确认,减轻处理器负担
- 支持4个优先级队列,可以通过端口、802.1P QoS以及IP TOS Priority设置
- 支持802.1Q VLAN,可达16组
- 支持VLAN ID tan/untag选项设置
- 支持带宽、输入以及输出速率的控制
- 支持广播风暴过滤
- 支持达1K的单播MAC地址表
- 支持基于MIB计数器的诊断
- 支持惠普自适应MDI/MDIX
2.芯片的Block Diagram
图7
3.构建的网络环境
图8
在上图中,由于P3具有普通网卡的功能,因此它具有自己的MAC地址,另外,P0端口的外接口直接连到了板子上的Cable Modem,所以,它也有自己的MAC地址,其实是它连接的Cable Modem的MAC地址。
4.分类总结
处理器总线接口以及两个PHY端口共同提供或支持如下特性:
- 四种优先级的传输队列,可以通过端口、802.1P QoS以及IP TOS Priority设置优先级,匹配多样的服务
- 基于端口(port)或者802.1p的VLAN设置,总共可达16组,提供有效的包转发
- 提供正确的带宽(输入输出单独或联合)速率控制
- 诊断相关的服务,包括MIB计数器,Loop-back测试等
两个PHY端口提供或支持如下特性:
- 自适应MDI/MDIX(IEEE 802.3u std)
处理器端口提供或支持如下特性:
- TCP/UDP/IPv4校验和的生成与确认,减轻处理负担
- 状态的读取与命令的设置
其实处理器端口担当的角色类似于网卡芯片,它有自己的MAC地址,完成数据的收发。另外,芯片的寄存器可以分为两大类:MAC相关类和PHY相关类。MAC类主要完成链路层的工作,大部分的命令、数据以及状态通过这些寄存器的设置来完成。相对于MAC类寄存器的数据链路层工作,PHY寄存器主要完成物理层的工作,包括编码、加扰以及串并转换等相关工作。这里我们访问PHY寄存器主要关注于与其相关的链路的工作方式,比如单工还是双工,10M还是100M以及自适应等等。
需要注意的部分:处理器端口号为3,并非2,在这里2是保留的。
五:驱动实现
前三部分描述了驱动的整个框架,第四部分介绍了芯片,基于上述二者,下面介绍驱动的具体实现。
1.基本操作部分
1.读写MAC寄存器
仅仅有两个端口可以访问主机接口,其一是索引(INDEX)口,另外一个是数据(DATA)口。索引口中的内容是数据寄存器的地址,因此要访问一个数据寄存器要通过如下的两步:
1.如果要写寄存器,先将寄存器的偏移(也就是我们后面说的寄存器的地址)写进索引端口,然后将数据写入数据端口;
2.如果要读寄存器,同样要先将寄存器的偏移写进索引端口,然后从数据端口读取数据。
硬件通过CMD管脚来区分当前是写索引端口还是写数据端口。(当我们按照索引地址写寄存器时,该地址就会将CMD管脚设置为0,同样,写数据地址时会被拉为1。)
MAC寄存器访问方式为字节方式
函数接口
/********************************************/
/* read a byte from dm9003 io port */
static u8_t dm9003_ior(int reg)
/* write a byte to dm9003 io port */
static void dm9003_iow(int reg, u8_t value)
/*******************************************/
2.读写PHY寄存器
DM9003有两个内部PHYs,访问PHY寄存器的步骤如下:
1.写PHY端口到REG.0CH的高两位(后面凡是提到REG.XXH均指MAC Register)
2.写PHY寄存器偏移到REG.0CH的低四位
3.写REG.0BH的Bit3选择操作PHY
4.如果为读数据,写“读命令”到REG.0BH的Bit2
5.如果为写数据,写“写命令”到REG.0BH的Bit1,并写数据到REG0DH—REG0EH
6.等待REG.0BH的Bit0指示为1,表示处理完成,如果是读过程,此时可以读取寄存器REG0DH—REG0EH的内容了
须注意如下两点:第一,PHY寄存器访问方式以字为单位;第二,手册中出现的PHY Address就是我们提到的PHY端口,范围是0~2,而PHY Register Adderss就是PHY寄存器的偏移,后面遇到会直接称为PHY寄存器地址。
函数接口
/*****************************************/
/* Read a word from phyxcer */
static u16_t dm9003phy_read(int reg, int port)
/* Write a word to phyxcer */
static void dm9003phy_write(int reg, u16_t value, int port)
/*****************************************/
2.初始化部分
1.PHY模式设置
有关PHY模式的设置这里提供了三个接口,分别是:PHY模式复位、PHY模式初始化以及带参数的PHY模式设置
1.PHY复位,设置复位位
函数接口
/********************************************/
/* reset dm9003 phy registers */
static void dm9003_phy_reset()
/********************************************/
2.PHY模式初始化,其实就是将其设置为自适应模式
函数接口
/********************************************/
/* init the phy as auto-negotiation mode */
static void dm9003phy_mode_init(void)
/********************************************/
3.PHY模式设定,提供参数,根据参数完成相应工作模式的设定。具体的工作模式有10M单工、10M双工、100M单工、100M双工以及自适应。如果芯片提供自适应的工作方式,我们就可以简单的通过初始化接口将其设置为该方式,以避免人为设置可能产生的错误。
函数接口
/********************************************/
/* Set PHY operationg mode */
static void dm9003phy_mode_set(int port ,u8_t mode)
/********************************************/
2.MAC工作模式设置
通过对MAC地址的设置,我们可以让芯片对数据包进行如下的过滤:只接收单播包、可以接收组播包、可以接收广播包以及混杂方式。关于MAC地址的设置,需要简单的说明一下,正如我们前面所介绍的,MAC地址是通过netif结构体作为载体传给初始化函数的。关于MAC地址的设定,须注意两个地址不能使用:其一,FF—FF—FF—FF—FF—FF,该地址是作为广播地址的;其二,01—XX—XX—XX—XX—XX(X代表0~F中的任意值),因为当MAC地址的最低位为1则表明是组播地址,特别的,01—00—5E—XX—XX—XX是用于以太网的组播地址,在后面我们会用到该地址。
1.接收单播包:我们只需要正确的设置MAC地址即可
函数接口
/*******************************************/
static void dm9003_unicastmode_set(struct netif *netif)
/*******************************************/
2.接收组播包:第一种情况是接收所有组播包,我们可以通过设置REG.05H的Bit3来完成;第二种情况是接收某一范围的组播包,需要编程设置REG.16H~REG.1DH来完成。这是一个64位的哈系值,通过对我们给出的组播MAC地址进行32位的循环冗余校验(CRC32)计算来得到。每一个组播地址会计算出一个值,所有给的组播MAC地址通过校验计算会得到一系列CRC值,所有CRC值经过散列相与得到最终的哈希值,也就是我们要设置寄存器REG.16H~REG.1DH的数据,总共64位。
上述工作通过两个函数来完成,分别是void dm9003_hash_table()与dm9003_crc_calculate。dm9003_crc_calculate有两个版本,一个是通过查表的办法来计算CRC32值,一个是直接计算,可以肯定地是通过查表的办法来计算速度要快一点。
当我们设置了过滤值(暂且这样用)后,如果收到组播包,芯片就会根据我们的设置来进行过滤。符合我们给定地址的组播包会被接收,其余的直接会被芯片丢弃。需要注意的是,通过哈希进行过滤不是一种完美的过滤,也就是说不是我们期望的组播包仍然有可能收到,因为不同的组播地址可能会产生相同的HASH值。其实,不论采用什么方法,我们都不可能在MAC层进行完美的过滤,因为从IP组播地址到MAC组播地址的映射就是一种多对一的映射,精确点就是32个IP组播地址会映射到一个MAC组播地址上。(详细的情况可参阅网络方面的书籍)这是否就意味着无法实现完美的过滤了呢,当然不是,这里只是映射,而非修改,我们仍然可以在IP层进行过滤,拿到我们最终想要的数据包。虽然存在地址映射不完美和需要硬件过滤的不足,组播仍然要比广播好。
函数接口
/*******************************************/
static u32_t dm9003_crc_calculate(u8_t * pdata, int len, int flag)
static void dm9003_hash_table()
static void dm9003_multimode_set(int select)
/*******************************************/
3.接收广播包:我们只需要将REG.1DH设置为0X80H即可。为什么要这么设置呢,因为广播MAC地址为FF—FF—FF—FF—FF—FF,而其通过CRC32计算的校验码为63,也就是64位HASH值的最高位。
函数接口
/*******************************************/
static void dm9003_broadmode_set()
/*******************************************/
4.接收所有包:具有网卡功能的芯片都提供了一种所谓混杂模式的工作方式,在这种模式下,芯片会接收并缓存所有经过芯片的数据包,而不管包的目的域是什么。我们可以通过设置寄存器REG.05H的Bit1来实现。
函数接口:
/*******************************************/
static void dm9003_promismode_set()
/*******************************************/
3.Switch设置
我们在前面介绍了Switch的基本功能就是过滤和转发,其实还应该加上一点就是存储,连起来就是过滤——存储——转发。我们刚介绍了过滤功能,现在来介绍它的核心功能——转发功能,这才是switch的特色。存储是通过优先级队列来实现的,这将在稍后介绍。有效的过滤和带有优先级的存储可以提升switch的效率。
Switch的设置可以通过四个模块来完成,他们分别是switch核复位、VLAN Setup、QoS Setup以及带宽控制。
1.Switch核的复位:对switch core复位后,此时芯片已经具备基本的交换功能,这是通过地址自学习机制来实现的。通过自学习,芯片会在它的存储空间中建立一张存储转发表,并为其中的表项设置时效,该时效也可以通过编程的方式来设置。通过存储转发表以及相应的转发规则,switch核就能够正确地将输入的包转发到对应的接口,从而完成switch的基本功能。
我们可以通过设置REG.52H的Bit6完成switch core的复位
函数接口
/*****************************************************/
static void dm9003_switchcore_reset()
/*****************************************************/
2.VLAN Setup的设置:也许是必要的,因为通过设置VLAN,我们可以对switch的端口进行分组,到达某个端口的包只可以转发给与其是一组的其他端口,这可以有效的减轻网络负担(抑制广播风暴,部分包会在这里丢弃),同时,VLAN的设置也可以是基于安全的考虑,VLAN中的V就是代表虚拟的意思,我们可以将不同的实际局域网内的几台主机通过switch构建为一个虚拟的局域网,处于虚拟局域网内的主机也可以相互通信,这就是通过VLAN来实现的。对于Dm9003EP,我们可以通过两种方式来设置VLAN分组,一种是基于端口的,一种是基于TAG域的(这种方式针对具有Tagged frame,相对于标准的以太网帧,它在源MAC地址和类型之间加上了两个字节的标识和两个字节的Tag控制信息)
所谓基于端口的,就是以端口作为划分的依据(DM9003EP具有P0、P1以及P3三个端口),我们可以将某个或某几个端口划分到一个VLAN中,这需要如下的两部操作
首先,为每个端口设置一个PVID(port vid)号
其次,基于上面设置的PVID建立映射关系
运行时,当一个包到达某个端口后,会根据该端口的PVID号去查看对应的B0~BF寄存器(因为DM9003EP支持16组VLAN,所以PVID号的范围应该是0~15,对应的寄存器就是B0~BF16个寄存器)。在B0~BF寄存器的低四位我们以比特对应的关系给出了映射,也就是Bit0对应P0端口,将相应位置1表示对应端口加入该PVID号对应的VLAN,根据该映射关系端口将数据包转发给与它是同一VLAN分组的其他端口。比如,如果我们设置P0的PVID为1,同时设置B1的低四位为1001,那么此时P0与P3就输入同一个VLAN分组。到达P0端口的数据包就会转发给P3端口。
相对于端口方式,基于TAG域的实现只是PVID的设置不同,而映射关系不变。此时的PVID来自于数据包本身,在前面已经介绍了,它是针对Tagged frame的。在该格式的数据包中,有一个12bits的VID域,利用该VID的低四位作为索引来对应B0~BF寄存器,从而建立相应地分组。
函数接口
/*********************************************/
static void vlan_group_map(u8_t reg_group, u8_t mapping)
static int vlan_port_vid(int port, u8_t VID)
static int vlan_outpkt_tag(int port)
static void VLAN_SETUP(int vlan_type)
/*********************************************/
3.QoS Setup的设置:除了划分分组外,我们还要建立基于优先级的排队机制,这就是通过QoS Setup来完成的。调用它我们可以建立一种排队机制,根据预先设置的原则,将输入的包放在具有不同优先级的队列上,也就是说我们可以将需要优先处理的包放到具有高优先级的队列上,以使它们得到更多地服务,更好的处理,这就可以匹配多样的应用,特别是具有音视频服务的应用领域。DM9003EP提供具有四种级别的优先级队列,并且提供两种包调度算法,一种是基于权重划分的队列(Weighted Fair Queuing),一种是基于严格优先级的队列(Strict Priority Queuing)。这里所说的就是具体要给的优先级,是像8、4、2、1这种有阶梯的呢还是像4、3、2、1这种单纯的递增递减。
队列优先级的设置可以通过三种方式来实现,分别是端口、TAG以及TOS。
基于端口的实现:
类似于VLAN的设置,基于端口的设置提供了一种简单的方式来设置QoS优先级服务队列,在这种方式下,我们可以直接为端口指定其队列的优先级,这通过如下两步来完成:首先写寄存器REG0X60选择端口;其次写寄存器REG0X6D的低两位设置优先级
函数接口
/*************************************************/
static int qos_port_pri(int port, u8_t priority)
/*************************************************/
基于tag域的实现:
在这种方式下就存在一种映射关系,因为此时我们针对的不再是端口,而是收到的数据包本身。类似于VLAN的设置,同样这里我们也是就Tagged Frame而言的,此时利用的是3位的Priority域。因为3位Priority对应8种状态,所以这种映射关系就不再是一对一的对应关系了,这可以通过D0~D1寄存器来映射。我们已经将D0~D1寄存器的位对应到了不同的Tag域状态上,因此只需在相应的位上设置优先级即可。比如,如果我们要将Tag优先级域为011的包放到优先级是1的队列上,则我们可以将D0的Bit7-6设置为0x01即可,因为这两位对应的是Tag优先级域为3的包。其他位的设置类似。
基于tos的实现:
这种方式即不依赖于端口,也不依赖于数据帧,而是针对IP数据包的。此时以IP包头部的Tos域作为参考,因为Tos域有6位,可有64种状态,每个寄存器8位,可对应四种状态,因此就需要16个寄存器来进行设置,具体的就是C0~CF寄存器,不过具体的设置方法与上面的Tag方式是类似的,这里就不再多介绍。另外需要的是,对于Tos域的使用有低三位和完全6位两种使用方式,如果只使用低三位,就可以只设置C0~C1寄存器了。
虽然可以编程进行这些设置,但是在程序中都采用了默认的设置。默认设置具体的对应关系可查看芯片手册。
函数接口
/*************************************************/
static void QoS_SETUP(int QoS_type)
/*************************************************/
另外,还需要提一点的就是有关带宽的控制,我们可以通过对REG.61/66/67H的设置控制输入输出带宽速率
函数接口
/****************************************************/
static int dm9003_bw_control(int port, u8_t bw_type, u8_t bit74, u8_t bit30)
/****************************************************/
4.Switch设置:有了前面的介绍,再来说switch的设置就很简单了,可以通过四步来完成:
第一:初始化switch相关寄存器
第二:设置VLAN
第三:设置QoS
第四:设置带宽
函数接口
/***************************************************/
static int dm9003_bw_control(int port, u8_t bw_type, u8_t bit74, u8_t bit30)
static void dm9003_switch_config()
/***************************************************/
4.初始化流程
上面的工作为初始化提供了基本的接口,通过调用上面的这些接口,就可以很容易的完成初始化工作。
基本流程如下图所示:
图9
上图清晰的给出了初始化需要做那些工作,对于判断设备是否存在这一步是省略的,这对于PC机来说是必须的,但是这里我们既知道设备是否存在,也知道它的型号。
另外通用寄存器的设置是前面没有介绍的,这可以通过代码来了解,其实也是要使设备处于一种我们可知的工作状态,只不过设置琐碎一点罢了。
函数接口
/*****************************************/
int dm9003_init(struct netif *netif);
/*****************************************/
3.数据读取部分
在前面的介绍中我们已经知道了low_level_input提供了数据接收的框架,并利用底层设备提供的接口完成实际的数据接收。
驱动层提供的接口如下:
首先是获取接收数据包的长度信息
/********************************************/
u16_t dm9003_recvpkt_len(void)
/********************************************/
芯片数据的接收
/********************************************/
int dm9003_pkt_recv(u8_t* payload,u16_t len)
/********************************************/
判断接收是否结束
/********************************************/
int dm9003_recv_end(void)
/********************************************/
丢弃状态错误的包
/********************************************/
int dm9003_recvpkt_drop(u16_t len)
/********************************************/
底层数据读取的基本流程(读数据框架)
根据上面提供的底层接口,数据包读取的流程如下:(也就是low_level_input的实现)
(需要注意的一点是,虽然我们将lwo_level_input放在interrupt_lock------interrupt_unlock之间,以免在从芯片的SRAM中读取数据时被外界干扰,但是在实际运行时发现仍然会出现读数据过程被干扰的情况,最终导致读取数据出错,在TCP层就是数据包的重传,到应用层则导致显示滞后。目前有两种解决方案:其一,使用interrupt_globle_disable-----interrupt_globle_enable代替interrupt_lock与interrupt_unlock;其二,在interrupt_lock-----interrupt_unlock之间再加上dm9003_int_disable-----dm9003_int_enable #或者是eth_isr_disable-----eth_isr_enable#。)
图10
接收到的数据包会被保存在内部接收SRAM中,此时,它被当作是一个环形数据结构来使用。在DM9003EP中,RX SRAM的起始地址为0X0C00H,结束地址为0X1EFFH,当数据到达该地之后,会返回到头部重新开始。另外,芯片会为每个接收到的数据加上一个4字节的头部,之后的数据就是以太网帧。这四个字节头部包括如下信息:
1字节 ready(准备好否)| 1字节status(是否出错) | 2字节 length(长度)
即每个包具有如下的数据结构
图11
其中MRCMDX寄存器指示了每个包的开始,并且读该寄存器并不会改变当前的读指针,所以我们可以通过读该寄存器来判断包是否准备好。MRCMD是读取SRAM中数据的命令,通过写IO地址到该寄存器,我们就可以通过连续的读数据指令来读取SRAM中的包数据了,读指针会根据当前的操作模式(8或者16或者更高)自动增加读指针。另外还有两个寄存器,MDRAL以及MDRAH,通过修改它们的值我们就能够修改读指针或者获取当前读指针的地址。因此,收到一个包后,会先通过MRCMDX读取包是否ready的信息,然后通过MRCMD读取四字节的头,进行状态的判断,最后再根据状态读取数据。
图12
在上图中可以看到,ready位为0X01表示有包,为00表示没有数据包,同时,length域指示了数据包的长度,不包括头字节。
还有,就是数据域的最后包含4字节的CRC校验和,比如,如果发送以太网帧大小为1514字节,则数据域的实际长度为1518字节。
图13
对于整个的数据接收可以用上图简单说明,其中用到了我们前面提到的判断接收是否完成的接口eth_recv_end。为了避免中断没有及时相应而产生丢包,我们在每次处理完收到的数据包后(不管是拷贝到本地了还是丢弃了)都会调用eth_recv_end判断SRAM中的包是否处理完了。这是通过读取MRCMDX寄存器对应得数据来完成。根据前面对SRAM中数据包格式的介绍,MRCMDX指示了包的开始位置,因此我们所读取的就是ready字节。如果是01,则进行signal信号量操作(需要注意的是在这里进行signal操作前判断了信号的计数值,因此不会产生阻塞,而是直接返回,如上图),否则,简单返回。显然,我们有可能产生多余的signal操作,比如在这里进行signal操作后,中断的signal操作也进行了。
4.数据发送部分
就如low_level_input提供底层数据接收的框架一样,low_level_output提供了底层数据发送的框架,并利用相应的底层接口完成数据的发送。
该框架中所需要的基本接口有:
发送前的准备工作---禁止中断
/************************************/
dm9003_send_init();
/************************************/
拷贝本地数据到到芯片的发送缓存(pbuf sram)
/*************************************/
dm9003_data_move(q->payload,q->len);
/*************************************/
写发送请求到芯片的相应寄存器,进行数据发送
/**************************************/
dm9003_data _send(p->tot_len);
/**************************************/
处理Two-Packet-Mode方式下发送中断完成后的工作
/**************************************/
static void dm9003_tx_done();
/**************************************/
在发送数据包前还需要考虑另外一个问题,就是DM9ks支持两种数据发送模式,分别是Two-Packet-Mode与One-Packet-Mode。如果采用One-packet-mode,我们在每次发送数据包前只需要查看它的发送请求位是否已经被清除,如果前一个包已经发送完成了,就可以接着发送了。对于Two-packet-mode就要复杂一点了,它的基本原理是我们将第一个包写到发送缓存并请求发送,在该包发送完成之前,我们可以将第二个包写到发送缓存,也就是写FIFO的方式。这样在第一个包发送完成后就可以写请求发送第二个包了,之间节省了拷贝数据的操作和时间。最终形成的发送序列就是index1index11index1index11…
对于Two-packet-mode,还有一些细节问题需要考虑,就是如何提供一种保护机制来保证包的发送是严格按照上述顺序进行的。在这里我们使用一个计数器来模拟包发送的顺序,利用信号量来保护对该计数值得访问,从而实现two-packet-mode的数据发送。流程图如下:
图14
根据上图我们可以看到,如果计数值为2,发送端会有两种操作,其一,如果是在计数值加一之前,则表明当前FIFO仍然是“满的“,也就是说已经有两个数据包了,我们就wait信号量,等待数据包发送完成;其二,如果是在计数值加一之后,则表明我们之后要写入的是index2包,所以要关闭写FIFO,通知中断当前FIFO中已经有两个数据包了。在中断部分判断出关闭了写FIFO就释放信号量,因为在此之前我们肯定执行了计数值减一的操作(因为当前的中断状态是发送完成),这样数据发送部分就会得到信号量,知道FIFO中至少已经有一个包发送了,因此就往下执行,写下一个包到FIFO中。如此连续,数据包就按index1 index1 index1index2的顺序发送了。
底层实际的数据发送流程就如下图所示:
图15
5.中断处理部分
前面已近介绍过中断部分我们重点关注发送完成和接收到数据包的中断。
虽然我们可以将数据的接收放在中断服务例程中来处理,但是为了减少中断处理的时间,保证中断相应的实时性,我们并没有在中断中直接进行处理,而是仅仅判断了相应的状态,利用操作系统的信号量来通知相应的接收任务来完成具体的接收工作。不过对于发送的two-packet-mode,我们将发送完成后的处理直接放在了中断处理中,有关发送完成后的处理可参考上面的数据发送部分的相应部分的介绍。
底层提供的接口如下:
写中断掩码寄存器关闭发送接收相关中断
/***********************************/
static void dm9003_int_disable(void)
/***********************************/
写中断掩码寄存器使能接收发送中断
/***********************************/
static void dm9003_int_enable(void)
/************************************/
读取中断状态寄存器的内容
/**************************************/
static u8_t dm9003_intstatus_read(void)
/**************************************/
清除中断状态寄存器的内容
/**************************************/
static void dm9003_intstatus_clear(void)
/**************************************/
中断处理的基本流程如下:
图16
6.其他
上面所介绍的几个部分完成了驱动层的大部分工作,为了完善整个驱动的功能,还需要提供一些额外的接口,比如芯片工作的状态信息,一些统计信息等等,它们可以为了解系统的性能,估计系统的稳定性提供诊断信息,下面就简单介绍一下这些接口。
1.状态信息获取
判断芯片当前的工作模式,有四种可能,分别是10M单工,10M双工,100M单工以及100M双工等。通过调用下面的函数接口即可完成。不过这里是通过读取PHY寄存器完成的,当然也可以通过读取MAC寄存器来实现。
/*************************************/
static u16_t dm9003_cur_optmode(int port)
/*************************************/
判断linkpartner的状态,也就是与当前PHY相连的PHY的工作模式,同样可能有上面所述的四种状态。
/***************************************/
static u16_t dm9003_get_linkpartner_stats(int port);
/***************************************/
获得当前芯片支持的状态的信息,也就是当前芯片到底支持上述四种加上自适应共五种状态的那一种或某几种。DM9003EP五种都支持。
/****************************************/
static u16_t dm9003_get_bmsr(int port);
/****************************************/
2.统计信息获取
芯片的mib计数寄存器保存了芯片工作时的许多信息,包括接收包的数量,发送包的数量,接收丢弃包的数量,发送丢弃包的数量等等。我们可以通过如下函数接口获取这些信息
/*****************************************/
static int dm9003_mibinfo_get(int port, int index)
/*****************************************/
另外还有FIFO溢出次数等其他相关统计信息。这里,我们使用一个函数将这些信息汇总起来,对外提供统一的接口,如下:
/******************************************/
void * dm9003_get_stats(int port);
void dm9003_clear_stats();
/******************************************/
3.增强功能
流控使能,仅使用于全双工模式
/****************************************/
void dm9003_flowctrl_enable(int port)
/****************************************/
分割模式使能。DM9003EP为每个端口提供分割模式,当连续产生64个冲突后,端口就会进入分割模式,此模式下,端口会连续发送数据,但不会接收。当在线路上发现一个好的包之后,端口就返回正常模式。
/*****************************************/
void dm9003_partimode_enable(int port)
/*****************************************/
广播风暴过滤
/*****************************************/
void dm9003_caststrom_ctrl(int select, int port)
/*****************************************/
发送校验和设置,这可以在芯片内部实现自动生成TCP/UDP/IP(注意,只包括这三种)包的校验和,所以就可以关闭协议栈对这几种包的校验处理,从而减轻处理器的负担,加快处理效率。
/******************************************/
static void dm9003_transchecksum_set(u8_t ip, u8_t tcp, u8_t udp)
/******************************************/
接收校验和设置,类似于发送,同样可以在芯片内部完成校验和的检测,自动丢弃校验错误的包,从而可以关闭协议栈对相应包的校验计算。
/******************************************/
static void dm9003_recvchecksum_set()
/******************************************/
4.杂项
时效设置,设置转发表中表项的时间戳
/******************************************/
void dm9003_aging_set(int select)
/******************************************/
Sniff端口设置
/******************************************/
void dm9003_sniffport_set(int port)
/******************************************/
端口监控设置,与Sniff端口配合工作,当这两项都设置后,从监控端口收到的包或者发送到监控端口的包都会被拷贝一份给Sniff端口
/******************************************/
void dm9003_portmonitor_set(int style, int port)
/******************************************/
六:文件结构说明
/lwip/netif文件夹
存放了IP层以下的代码,包括驱动代码,ARP协议的实现以及PPP协议的实现等
/lwip/netif/dm9003.h & /lwip/netif/dm9003.c
驱动实现,提供所有的涉及芯片底层操作的接口
/lwip/netif/ethernetif.h & /lwip/netif/ethernetif.c
实现驱动层的框架,并向上层提供接口
七:测试
1.基本测试
利用简单的ping命令测试网络是否连通
à查看测试结果(有关操作步骤和结果放在这里,下同)
2.Switch功能测试
使用PC机登陆Cable modem上的网页。因为PC机连到了芯片的P1口,而Cable modem连在了芯片的P0口(参考第四部分关于构建的网络环境一节),所以通过上面的操作可以知道switch是否具备基本的转发功能。
3.检验和测试
同过双向检测完成:
首先是PC机到—〉机顶盒,测试芯片的自动生成校验和的功能
其次是机顶盒—〉PC机,测试芯片自动丢弃校验错误的包的功能
4.MAC工作方式测试
直接发送数据到机顶盒,目的IP地址分别为机顶盒内部IP地址,组播地址以及广播地址。通过查看对上述包的接收情况,检测芯片是否具备对应的过滤功能。
5.Two-Packet-Mode测试
分别比较原来的方式和双包发送方式下所用的时间来评估性能
6.稳定性与性能测试
暂时通过进行短时间内大量数据包的发送来测试
完。