5.3.1 DM9000的软、硬件驱动
推荐给好友
打印
加入收藏
更新于2008-11-18 20:14:48

1.DM9000控制器的实现
从硬件方案介绍中已经明确,DM9000提供了通用的CPU接口,简化了与MirRoBlaze硬件接口的实现过程,可直接通过EDK库中的EPC(外设控制器)核控制DM9000。

DM9000可利用EDK提供的xps_epc核来实现,该核的时序完全满足DM9000的要求,相应的控制信号可由Utility Vector Logic和Utility Bus Split核来完成。DM9000控制器的整体结构如图5-13所示。


图5-13 XPS EPC核与DM9000的整体连接结构


将XPS_EPC核添加到系统中,并连接在PLB总线上,其中XPS_EPC核的配置具体连接关系如图5-14所示,最大地址宽度设置为4,数据位宽设置为16,其相应的端口按照图5-13连接即可,除了地址总线(PRH_ddr)外,其余的端口需要设置成外部端口。


图5-14 EPC核的配置参数示意图


图5-13中外部逻辑涉及两个操作:总线分割和与、非等逻辑电平转换操作。总线分割利用EDK自带的Utility Bus Split核完成,该核的功能如图5-15所示,将输入总线分割为位宽更窄的1个或多个独立总线。


图5-15 DM9000控制器外设的整体实现


逻辑转换利用Utility Vector Logic核来实现,其配置界面如5-16所示,“Type of Vector Operation To Perform”下拉框中可选择与、或、异或和同或等操作;“Size of The Vector”文本框用于输入信号位宽。本设计只用到与、非操作。


图5-16 Utility Vector Logic核参数配置界面


外部逻辑主要将EPC外设的地址线最低位分割出来得到dm9000a_0_split_Out1信号,并将EPC核的片选信号CS取反得到dm9000a_0_not_Res信号,再将dm9000a_0_split_Out1和dm9000a_0_not_Res相与得到信号dm9000a_0_and_Res,再取反得到dm9000a_0_not_cmd_Res信号,并直接将其设置为外部端口连接到DM9000的CMD管脚上。上述过程的信号操作如图5-17到5-20所示。


2.DM9000的软件驱动
要在MirRoBlaze系统中以DM9000芯片作为网络芯片,首先要通过地址端口和数据端口来配置DM9000芯片,使其能够完成设计人员定义的功能。要进行数据收发所必须实现的几个功能包括:
(1)DM9000端口读、写操作
在地址端口被赋值之后,对数据端口的操作就会完全反映到相应的片内内存空间中,这相当于对DM9000片内的地址空间进行了一次映射,使得只通过两个端口就可以实现对DM9000片内所有寄存器的访问操作,节省了DM9000所占用的系统总线的地址空间。图5-14给出了寄存器读写过程示意图。


图5-14 寄存器读写示意图


 寄存器读操作的软件实现
DM9000寄存器的读操作就是直接访问该地址,EDK提供了XIo_In32、XIo_In16以及XIo_In8这三个函数来读取给定地址的数据,其本质都是简单的指针获取函数,只是数据位宽分别为32比特、16比特和8比特。例如,XIo_In16的原型如下所列:

#define XIo_In16(InputPtr) (*(volatile Xuint16 *)(InputPtr))

由于EPC核选择了16比特,因此在XIo_In16函数基础上进行封装,形成ior函数,其中BaseAddress为DM9000在Microblaze系统中的基地址,reg为偏移地址。由于访问DM9000内部寄存器,需要将地址写入相应的接口寄存器,因此先调用Dm9000_Write()函数写入地址,再读取DM9000的数据接口。相应的代码如下:
Xuint16 ior(Xuint32 BaseAddress, Xuint16 reg)
{
Dm9000_Write(BaseAddress,IO_addr,reg);
return Dm9000_Read(BaseAddress,IO_data);
}
Xuint16 Dm9000_Read(Xuint32 BaseAddress,int type)
{
return XIo_In16(BaseAddress+type);
}
 寄存器写操作的软件实现
寄存器写操作就是指针赋值,和读取类似,EDK提供了XIo_Out32、XIo_ Out16以及XIo_Out8这三个函数来读取给定地址的数据,其本质也都一样,只是数据位宽不同。例如XIo_ Out16的原型如下:

#define XIo_Out16(OutputPtr, Value) \
 *(volatile Xuint16 *)((OutputPtr)) = (Value))

同样,由于EPC核选择了16比特,因此在XIo_Out16函数基础上进行封装,形成iow函数,先写入地址,再写入数据。相应的代码如下:

void iow(Xuint32 BaseAddress,Xuint16 reg, Xuint16 data)
{
Dm9000_Write(BaseAddress,IO_addr,reg);
Dm9000_Write(BaseAddress,IO_data,data);
}
void Dm9000_Write(Xuint32 BaseAddress,int type,Xuint16 Value)
{
XIo_Out16(BaseAddress+type, Value);
}

(2)PHY端口配置寄存器
PHY寄存器就是物理层配置寄存器,3个常用的寄存器如表5-3所列。


表5-3 常用的PHY寄存器

 
配置这些物理寄存器的方法为:首先将寄存器地址写到EPAR寄存器中(OCH),注意要将寄存器地址的第6位置1(地址与0x40进行或运算),表明写的是PHY地址,而不是EEPROM地址;其次,将数据的高字节写到PHY_DRH寄存器(0EH)中;第三,将数据的低字节写到PHY_DRL寄存器(0EH)中;第四,发送PHY写命令(0x0A)到EPCR寄存器(0BH)中;最后,延迟一段时间(1~30 us),发送0x08到EPCR寄存器,清除PHY写命令。相应的配置PHY寄存器的软件代码为:
void phy_write (Xuint32 BaseAddress,Xuint16 reg, Xuint16 value)
{
/* set PHY register address into EPAR REG. 0CH */
iow(BaseAddress,0x0C, reg | 0x40);
/* PHY register address setting, and DM9000_PHY offset = 0x40 */

/* fill PHY WRITE data into EPDR REG. 0EH & REG. 0DH */
/* PHY data high_byte */
iow(BaseAddress,0x0E, ((value >> 8) & 0xFF));
/* PHY data low_byte */
iow(BaseAddress,0x0D, value & 0xFF);

/* issue PHY + WRITE command = 0xa into EPCR REG. 0BH */
/* clear PHY command first */
iow(BaseAddress,0x0B, 0x8);
/* issue PHY + WRITE command */
Dm9000_Write(BaseAddress, IO_data, 0x0A);
usleep(STD_DELAY);
/* clear PHY command again */
Dm9000_Write(BaseAddress, IO_data, 0x08);
/* wait 1~30 us (>20 us) for PHY + WRITE completion */
usleep(30);
}
其中的usleep()函数为微秒延迟函数,可通过for循环实现,其相应的代码为:
void usleep(Xuint16 count) //1us stepwise
{
Xuint16 i,j;
for(i=1;i<=count;i++)
{
for(j=0;j<=10;j++); //需要经过计算获得
}
}
(4)复位与初始化的实现
复位与初始化功能是芯片必须要实现的一个基本功能,在系统启用了网络芯片之后,就要对网络芯片进行基本的配置,设置网络芯片的寄存器,使网络芯片达到工作状态;同样在检测到网络芯片处于非正常状态时就要复位网络芯片并重新进行配置,使得网络芯片从未知状态恢复,实现方法如下:
初始化时要进行以下的操作来使DM9000芯片达到工作状态:

 开启DM9000芯片,DM9000芯片上电默认的状态是GEPIO0=1的powerdown状态,所以要将GEPIO0 bit[0]设置为0,来打开DM9000芯片。上电之后将GPCR(REG_1E) GEP_CNTL0 bit[0]设置为1;同时将GPR(REG_1F)GEPIO0 bit[0]设置为0来打开芯片;

 进行两次软启动,根据芯片的设计要求要使芯片达到工作状态,在上电之后就要对芯片进行两次软启动,软启动是通过设置NCR(REG_00)bit[2:0]=011(至少20 us),设置NCR(REG_00)bit[2:0]=000来实现的,同样的操作要进行两次;

 清除Tx Status寄存器;
 设置IMR(REG_FF)寄存器PRM bit[0]/PTM bit[1],开启TX/RX中断;
 设置RCR寄存器来使能RX。Rx功能函数的使能是设置RX控制寄存器(REG_05)RXEN bit[0]=1。
当进行了以上的步骤之后,DM9000芯片就处于工作状态。当由于异常而引发重启时就要再次进行相同的操作过程来使DM9000芯片恢复到正常状态。在初始化DM9000芯片时将设置芯片中的控制寄存器,例如与接收任务相关的接收任务控制寄存器(REG_05H),其各个标志位的功能如表5-4所示。RX控制寄存器是接收任务所要用到的最重要的寄存器,其中包含了设置接收使能的RX使能位(bit0 RXEN),可以通过设置这一位来实现对接收任务的控制,在禁用芯片的时候要将RX使能位清除。Bit[4:1]用来设置对接收包的限制,当有不符合设置的包到达时将自动丢弃接收包,不产生中断。


表5-4 接收控制寄存器


DM9000的初始化软件代码为:
Xuint16 DM9000_init (Xuint32 BaseAddress,Xuint8 Num)
/* initialize DM9000 LAN chip */
{
Xuint16 i;
Xuint8 ether_addr0[6]=My_Mac_1ID;
Xuint8 ether_addr1[6]=My_Mac_2ID;
/* set the internal PHY power-on (GPIOs normal settings) */
iow(BaseAddress,0x1E, 0x01);
/* GPCR REG. 1EH = 1 selected GPIO0 "output" port for internal PHY */
iow(BaseAddress,0x1F, 0x00);
/* GPR REG. 1FH GEPIO0 Bit [0] = 0 to activate internal PHY */
/* wait > 2 ms for PHY power-up ready */
msleep(2);

/* software-RESET NIC */
iow(BaseAddress,NCR, 0x03);
/* NCR REG. 00 RST Bit [0] = 1 reset on, and LBK Bit [2:1] = 01b MAC loopback on */
/* wait > 10us for a software-RESET ok */
usleep(10);
/* normalize */
iow(BaseAddress,NCR, 0x00);
iow(BaseAddress,NCR, 0x03);
usleep(10);
iow(BaseAddress,NCR, 0x00);

/* set GPIO0=1 then GPIO0=0 to turn off and on the internal PHY */
/* GPR PHYPD Bit [0] = 1 turn-off PHY */
iow(BaseAddress,0x1F, 0x01);
/* PHYPD Bit [0] = 0 activate phyxcer */
iow(BaseAddress,0x1F, 0x00);
/* wait >4 ms for PHY power-up */
msleep(4);

/* set PHY operation mode */
/* reset PHY: registers back to the default states */
phy_write(BaseAddress,0,PHY_reset);
/* wait >30 us for PHY software-RESET ok */
usleep(30);
/* turn off PHY reduce-power-down mode only */
phy_write(BaseAddress,16, 0x404);
phy_write(BaseAddress,4, PHY_txab);
/* set PHY TX advertised ability: ALL + Flow_control */
phy_write(BaseAddress,0, 0x1200);
/* PHY auto-NEGO re-start enable (RESTART_AUTO_NEGOTIATION + AUTO_NEGOTIATION_ENABLE) to auto sense and recovery PHY registers */
/* wait >2 ms for PHY auto-sense linking to partner */
msleep(2);

/* store MAC address into NIC */
if(Num==0)
{
for (i = 0; i < 6; i++)
iow(ETH_Port_0,16 + i, ether_addr0[i]);
}
else if (Num==1)
{
for (i = 0; i < 6; i++)
iow(ETH_Port_1,16 + i, ether_addr1[i]);
}

/* clear any pending interrupt */
iow(BaseAddress,ISR, 0x3F);
/* clear the ISR status: PRS, PTS, ROS, ROOS 4 bits, by RW/C1 */
iow(BaseAddress,NSR, 0x2C);
/* clear the TX status: TX1END, TX2END, WAKEUP 3 bits, by RW/C1 */

/* program operating registers~ */
iow(BaseAddress,NCR, NCR_set);
/* NCR REG. 00 enable the chip functions (and disable this MAC loopback mode back to normal) */
iow(BaseAddress,0x08, BPTR_set);
/* BPTR REG.08 (if necessary) RX Back Pressure Threshold in Half duplex moe only: High Water 3KB, 600 us */
iow(BaseAddress,0x09, FCTR_set);
/* FCTR REG.09 (if necessary) Flow Control Threshold setting High/ Low Water Overflow 5KB/ 10KB */
iow(BaseAddress,0x0A, RTFCR_set);
/* RTFCR REG.0AH (if necessary) RX/TX Flow Control Register enable TXPEN, BKPM (TX_Half), FLCE (RX) */
iow(BaseAddress,0x0F, 0x00); /* Clear the all Event */
iow(BaseAddress,0x2D, 0x80); /* Switch LED to mode 1 */

/* set other registers depending on applications */
iow(BaseAddress,ETXCSR, ETXCSR_set); /* Early Transmit 75% */

/* enable interrupts to activate DM9000 ~on */
iow(BaseAddress,IMR, INTR_set);
/* IMR REG. FFH PAR=1 only, or + PTM=1& PRM=1 enable RxTx interrupts */

/* enable RX (Broadcast/ ALL_MULTICAST) ~go */

iow(BaseAddress,RCR , RCR_set | RX_ENABLE | PASS_MULTICAST);
/* RCR REG. 05 RXEN Bit [0] = 1 to enable the RX machine/ filter */
/* RETURN "DEVICE_SUCCESS" back to upper layer */
return (ior(BaseAddress,0x2D)==0x80) ? DMFE_SUCCESS : DMFE_FAIL;

}
(5)接收数据包的操作实现
数据包接收功能是DM9000芯片实现网络功能的基础,在接收数据时可以采用中断方式,也可以采用轮询方式。当采用中断方式时,数据到达且在DM9000内部CRC校验通过后会产生一个接收中断,中断发生时可以将DM9000所接收到的数据包读出并交由上层协议进行处理。采用轮询方式将以固定的周期收取数据。本设计由于外围设备很少,因此采用了轮询机制。
接收到的数据在经过硬件部分的CRC校验之后存放在RX FIFO中,在DM9000中的内部地址0x0C00-0x3FFF(13K byte)。在每一个接收到的数据包的前面都有一个4bytes的头,可以用MRCMDX(REG_F0)和MRCMD(REG_F2)寄存器来读取接收到的数据包的信息。接收数据在接收缓冲中的结构如图5-15所示。


图5-15 接收数据缓存结构示意图


其中第一个字节是接收数据标志,通过读取这一位来判断是否有数据到达,如果这一位是01则表示有数据被接收且保存到RX SRAM中,这时候可以将数据读出并进行处理;如果既不是01又不是00则认为有异常发生,这时就要将DM9000芯片重启以使芯片恢复到正常状态。第二个字节是status,这个字节是接收数据包的状态字,其中的内容与接收状态寄存器(REG_06H)中的内容相同。可以用来判断所接收的数据包是否正常,或发生了何种异常,这样就可以针对不同的异常进行不同的操作,实现对接收任务的控制。第3字节、第4字节存有接收到的数据包的长度,在读取数据包的时候要用这个长度来进行控制。这四个字节的包头是DM9000在接收数据的时候添加的信息,不属于数据包的内容。从第5个字节开始的数据才是真正的数据包的内容,其长度在第3、4字节中定义,所以驱动程序可以依次读取数据包数据,直至达到数据包长度为止。相应的DM9000接收数据包代码如下:


Xuint16 ReceivePacket (Xuint32 BaseAddress,Xuint8 *data_ptr,Xuint16 *rx_len)
{
Xuint8 rx_READY,GoodPacket;
Xuint16 Tmp, RxStatus, i;

RxStatus = rx_len[0] = 0;
GoodPacket=FALSE;

/* mask NIC interrupts IMR: PAR only */
iow(BaseAddress,IMR, PAR_set);

/* dummy read a byte from MRCMDX REG. F0H */
rx_READY = ior(BaseAddress,MRCMDX);

/* got most updated byte: rx_READY */
rx_READY = Dm9000_Read(BaseAddress,IO_data)&0x03;
//usleep(STD_DELAY);

/* check if (rx_READY == 0x01): Received Packet READY? */
if (rx_READY == DM9000_PKT_READY)
{
/* got RX_Status & RX_Length from RX SRAM */
Dm9000_Write(BaseAddress, IO_addr, MRCMD); /* set MRCMD REG. F2H RX I/O port ready */
//usleep(STD_DELAY);
RxStatus = Dm9000_Read(BaseAddress,IO_data);
//usleep(STD_DELAY);
rx_len[0] = Dm9000_Read(BaseAddress,IO_data);

/* Check this packet_status GOOD or BAD? */
if ( !(RxStatus & 0xBF00) && (rx_len[0] < MAX_PACKET_SIZE) )
{
/* read 1 received packet from RX SRAM into RX buffer */
for (i = 0; i < rx_len[0]; i += 2)
{
//usleep(STD_DELAY);
Tmp = Dm9000_Read(BaseAddress, IO_data);
data_ptr[i] = Tmp&0xFF;
data_ptr[i+1] = (Tmp>>8)&0xFF;
}
GoodPacket=TRUE;
} /* end if (GoodPacket) */
else
{
/* this packet is bad, dump it from RX SRAM */
for (i = 0; i < rx_len[0]; i += 2)
{
//usleep(STD_DELAY);
Tmp = Dm9000_Read(BaseAddress, IO_data);
}
rx_len[0] = 0;
} /* end if (!GoodPacket) */
} /* end if (rx_READY == DM9000_PKT_READY) ok */
else if(rx_READY) /* status check first byte: rx_READY Bit[1:0] must be "00"b or "01"b */
{
/* software-RESET NIC */
/* NCR REG. 00 RST Bit [0] = 1 reset on, and LBK Bit [2:1] = 01b MAC loopback on */
iow(BaseAddress,NCR, 0x03);
/* wait > 10us for a software-RESET ok */
usleep(10);
/* normalize */
iow(BaseAddress,NCR, 0x00);
iow(BaseAddress,NCR, 0x03);
usleep(10);
iow(BaseAddress,NCR, 0x00);
/* program operating registers~ */
/* NCR REG. 00 enable the chip functions (and disable this MAC loopback mode back to normal) */
iow(BaseAddress,NCR, NCR_set);
/* BPTR REG.08 (if necessary) RX Back Pressure Threshold in Half duplex moe only: High Water 3KB, 600 us */
iow(BaseAddress,0x08, BPTR_set);
/* FCTR REG.09 (if necessary) Flow Control Threshold setting High/ Low Water Overflow 5KB/ 10KB */
iow(BaseAddress,0x09, FCTR_set);
/* RTFCR REG.0AH (if necessary) RX/TX Flow Control Register enable TXPEN, BKPM (TX_Half), FLCE (RX) */
iow(BaseAddress,0x0A, RTFCR_set);
/* Clear the all Event */
iow(BaseAddress,0x0F, 0x00);
/* Switch LED to mode 1 */
iow(BaseAddress,0x2D, 0x80);
/* set other registers depending on applications */
/* Early Transmit 75% *
iow(BaseAddress,ETXCSR, ETXCSR_set); /
/* enable interrupts to activate DM9000 ~on */
/* IMR REG. FFH PAR=1 only, or + PTM=1& PRM=1 enable RxTx interrupts */
iow(BaseAddress,IMR, INTR_set);
/* enable RX (Broadcast/ ALL_MULTICAST) ~go */
/* RCR REG. 05 RXEN Bit [0] = 1 to enable the RX machine/ filter */
iow(BaseAddress,RCR , RCR_set | RX_ENABLE | PASS_MULTICAST);
} /* end NIC H/W system Data-Bus error */

return GoodPacket ? DMFE_SUCCESS : DMFE_FAIL;
}
(6)发送数据包的操作流程
数据包发送功能的实现主要是依靠DM9000中的发送数据缓存区,驱动函数将要发送的数据包存入到发送数据缓存区内之后,设置发送标志来进行数据的发送。图5-16给出了数据包发送任务的流程图。


图5-16 数据包发送流程图


 首先,检测工作模式;
 第二步,将要发送的数据包写入发送数据缓冲区;
 第三步,将要发送的数据先写入发送数据长度的高字节,再写入发送数据包长度的低字节;
 第四步,设置传送标志。
设置完传送标志之后,DM9000就会将存入发送数据缓冲区的数据发送出去。DM9000对数据的发送要求驱动程序将数据写入DM9000芯片中的发送数据缓冲区中去。DM9000在接收到处理器芯片发来的数据发送标志之后,就会将写入缓冲区的数据包处理成Ethernet数据包的形式进行发送,在发送过程中还可以继续接收数据包的写入,所以在芯片中就分了两个逻辑上的发送数据包,分别有两组状态寄存器和控制寄存器来实现发送的控制及状态检查工作。其相应的软件代码如下:
Xuint16 TransmitPacket(Xuint32 BaseAddress,Xuint8 *data_ptr,Xuint16 tx_len)
{
Xuint16 i;

/* mask NIC interrupts IMR: PAR only */
iow(BaseAddress,IMR, PAR_set);

/* issue TX packet's length into TXPLH REG. FDH & TXPLL REG. FCH */
/* TXPLH High_byte length */
iow(BaseAddress,0xFD, (tx_len >> 8) & 0xFF);
/* TXPLL Low_byte length */
iow(BaseAddress,0xFC, tx_len & 0xFF);

/* wirte transmit data to chip SRAM */
/* set MWCMD REG. F8H TX I/O port ready */
Dm9000_Write(BaseAddress, IO_addr, MWCMD);
for (i = 0; i < tx_len; i += 2)
{
//usleep(STD_DELAY);
Dm9000_Write(BaseAddress, IO_data, (data_ptr[i+1]<<8)|data_ptr[i] );
}
/* issue TX polling command activated */
/* TXCR Bit [0] TXREQ auto clear after TX completed */
iow(BaseAddress,TCR , TCR_set | TX_REQUEST);

/* wait TX transmit done */
while(!(ior(BaseAddress,NSR)&0x0C))
// usleep(STD_DELAY);

/* clear the NSR Register */
iow(BaseAddress,NSR,0x00);

/* re-enable NIC interrupts */
iow(BaseAddress,IMR, INTR_set);

/* RETURN "TX_SUCCESS" to upper layer */
return DMFE_SUCCESS;
}

 

 

<上一节  下一节>

相关链接


 
关于我们 | 诚邀加盟 | 客户服务 | 相关法律 | 网站地图 | 友情链接 | 服务信箱:service@eefocus.com
© 2006 与非门科技信息咨询(北京)有限公司 All Rights Reserved.