嵌入式Lee

文章数:196 被阅读:578672

datasheet推荐 换一换
随便看看
账号入驻

基于DWC_ether_qos的以太网驱动开发-收发驱动编写与调试

最新更新时间:2023-09-05
    阅读数:

一. 前言

前面已经介绍了环形描述符的工作方式和描述符的具体格式。收发的驱动实际就是围绕着准备描述符来进行的,这种机制使得驱动代码的编写比较简单了,软件通过描述符高速硬件 DMA 怎么做即可,剩下的就交给硬件 DMA 了,这样效率非常高。

二. 收发驱动

2.1 发送

Current 是硬件维护的指针 ,Current 表示硬件当前操作的描述符,如果该描述符不是 OWN BY DMA 则停止。

Inx 是软件维护的指针,软件写完一个描述符 Inx 递增,如果 Inx 追赶上了 Current ,即遇到了 OWN BY DMA 的描述符也停止。

开始时指针如下, Current Inx 都从 Base 开始,注意 current 需要启动后才能读到值,初始化后是 0 ,所以这里编程需要注意

软件准备 n 个描述待发送,黄色部分,并设置这些描述符 OWN BY DMA ,启动 DMA ,硬件开始处理这些描述符

比如硬件处理完一个描述符后,继续处理后面的

硬件处理完 ,Current 追上了 Inx ,停止 DMA

当然 Current 在发送的时候,软件又可以从 Inx 往后面准备描述符,相当于 Inx 是生产者,生产描述符, Current 是消费者,消费描述符,这样就可以不间断的进行发送。

/** * 见手册P1087 * inx       软件当前操作指针 * current   DMA当前操作指针 * cnt       描述符个数 * tail       * 0              ....              (cnt-1)        |  tail * ^               ^ * current          * inx *  * 0              ....   3          (cnt-1)        |  tail *       ^               ^ *       current         inx *       |---own by dma- |  * 1)tail设置在描述符个数cnt以外,这样永远不会因为current==tail而停止, * 停止条件只有一个即current处的描述符不是own by dma。 * 2)初始化时,inx为0,current寄存器值为0,值为0时认为其指向索引0. * 3)软件需要发送3个包,修改了3个包0,1,2为own by dma,此时inx变为3,软件同时设置tail触发DMA去重启,并检查current处 * 是否own by dma * 4)DMA检测到0处 own by dma于是处理0处的描述符,直到处理到描述符3处,发现不是own by dma于是停止 * 5)在硬件DMA处理0,1,2这三个描述符的过程中,软件又可以继续从3开始准备描述符设置为own by dma。 * 所以过程如下: * [current,inx)注意不包括inx处 own by dma,是硬件待处理的部分。 * 其他部分own by 软件,空闲描述符,软件可以使用的。 * current追赶inx,current追赶到inx后就停止,current和inx都是到了cnt-1后绕回到0. * inx也是不断追赶current,inx追赶到current说明软件发的快。 */int iot_eth_send_frame(eth_ctrl_t *p_ctrl, uint32_t* buffer, uint32_t desc_cnt){#if 1    uint32_t inx;    uint32_t desc;    uint32_t status;    uint32_t first_seg = 1;    uint32_t sparenum = 0;    if((desc_cnt==0) || (buffer == 0) || (p_ctrl == 0) || (desc_cnt > p_ctrl->txd.buf_cnt))    {        return -1;    }    inx = p_ctrl->txd.inx;    /* 查询空闲描述符个数 */    for(uint32_t i=0; i    {        desc = (uint32_t)(p_ctrl->txd.desc_base) + inx*sizeof(eth_dma_t);        status = DWC_DESC_DESC3(desc);        if (DWC_DESC_STATUS_OWNER_DMA & status)         {            /* 到了own by dma的地方,停止*/            break;        }        sparenum++;        inx++;        inx %= p_ctrl->txd.buf_cnt;    }    DWC_ETH_LOG(("sparenum %d\r\n",sparenum));    if(sparenum < desc_cnt)    {        return -2;    }
/* 填充描述符 */ inx = p_ctrl->txd.inx; for(uint32_t i=0; i { status = 0; desc = (uint32_t)(p_ctrl->txd.desc_base) + inx*sizeof(eth_dma_t); if (first_seg) { status |= (DWC_DESC_STATUS_FIRST_SEG | DWC_DESC_STATUS_CRC_PAD_INS | DWC_DESC_STATUS_SA_REP_R1); first_seg = 0; }
DWC_DESC_DESC0(desc) = (uint32_t)buffer[2*i]; /* 待发送的数据的缓冲区 */ DWC_DESC_DESC1(desc) = 0;
if (i == (desc_cnt-1)) { status |= DWC_DESC_STATUS_LAST_SEG; DWC_DESC_DESC2(desc) = DWC_DESC_CTRL_BUF1_LEN(buffer[2*i+1]) | DWC_DESC_CTRL_INT_ENA; /* 最后一个描述符使能IOC 发送完中断 */ } else { DWC_DESC_DESC2(desc) = DWC_DESC_CTRL_BUF1_LEN(buffer[2*i+1]); /* 待发送的数据的长度 */ }
DWC_DESC_DESC3(desc) = status | DWC_DESC_STATUS_OWNER_DMA | DWC_DESC_CTRL_BUF1_LEN(buffer[2*i+1]); inx++; inx %= p_ctrl->txd.buf_cnt; }
p_ctrl->txd.inx = inx;
/* 理论上这里只需要停止之后才设置tail重启 * 为了简单不判断是否停止总是写tail重启 */ desc = (uint32_t)(p_ctrl->txd.desc_base) + p_ctrl->txd.buf_cnt*sizeof(eth_dma_t); gmac_start_txdsc(p_ctrl->unit,desc); DWC_ETH_LOG(("Tx Current %x,idx %d\r\n",gmac_get_currenttxdsc(0),inx));#else (void)desc_cnt; uint32_t desc = gmac_get_currenttxdsc(p_ctrl->unit); if(desc == 0) { desc = (uint32_t)(p_ctrl->txd.desc_base); } DWC_DESC_DESC0(desc) = (uint32_t)buffer[2*0]; /* 待发送的数据的缓冲区 */ DWC_DESC_DESC1(desc) = 0; DWC_DESC_DESC2(desc) = DWC_DESC_CTRL_BUF1_LEN(buffer[2*0+1]) | DWC_DESC_CTRL_INT_ENA; /* 待发送的数据的长度 最后一个描述符使能IOC 发送完中断 */ uint32_t status = DWC_DESC_STATUS_OWNER_DMA; status |= (DWC_DESC_STATUS_FIRST_SEG | DWC_DESC_STATUS_CRC_PAD_INS | DWC_DESC_STATUS_SA_REP_R1); status |= DWC_DESC_STATUS_LAST_SEG | DWC_DESC_CTRL_BUF1_LEN(buffer[2*0+1]); DWC_DESC_DESC3(desc) = status; DWC_ETH_LOG(("tx desc %x tail %x\r\n",desc,gmac_get_txtail(p_ctrl->unit))); //gmac_start_tx(p_ctrl->unit); gmac_start_txdsc(p_ctrl->unit, desc+0x10);#endif return 0;}

2.2 接收

接收和发送类似

初始化时先将所有描述符设置为接收状态,且设置产生 IOC 中断,并启动 DMA

如下所示黄色描述符都是表示准备接收数据。

在收到一包数据后,产生中断,如下描述符 0 即接收到数据的描述符,变为非 OWN BY DMA ,软件查询该描述符可以获取到接收数据长度等信息,进行处理。

软件处理完描述符 0 之后,又可以将其设置为接收状态 OWN BY DMA 以便接收继续接收。

以上是接收比软件处理的慢,所以 Current 一直追不上 Inx ,如果接收比软件处理的快,

Current 会追上 Inx 而停止。

比如如下

int iot_eth_receive_handle(eth_ctrl_t *p_ctrl,eth_rcv_data_pf cb){    int res = 0;    eth_buf_t *p_rxd = &p_ctrl->rxd;    uint32_t status;    uint32_t desc;    uint32_t index = p_rxd->inx;    uint32_t len;    uint8_t *data;    uint8_t dataflag = 0;    do     {        dataflag = 0;        desc = (uint32_t)(p_ctrl->rxd.desc_base) + index*sizeof(eth_dma_t);        status = DWC_DESC_DESC3(desc);
if (DWC_DESC_STATUS_OWNER_DMA & status) { /* 到了OWN为DMA的描述符,说明后面部分属于DMA了,不需要继续处理了 */ DWC_ETH_LOG(("eth rx own dma\r\n")); break; } if (DWC_DESC_STATUS_ERROR_SUM & status) { DWC_ETH_WARN(("eth rx fcs err\r\n")); dataflag = 1; //continue; /* 还是需要继续重新配置描述符接收,否则该描述符将会浪费 */ } len = DWC_DESC_GET_DATA_LEN(status) - ETH_CRC_LEN; if (ETH_INVALID_LEN(len)) { DWC_ETH_WARN(("eth rx len err:%d\r\n",len)); dataflag = 1; //continue; /* 还是需要继续重新配置描述符接收,否则该描述符将会浪费 */ } data = (uint8_t *)DWC_DESC_DESC0(desc); if (NULL == data) { DWC_ETH_WARN(("eth rx buf err\r\n")); dataflag = 1; //continue; /* 还是需要继续重新配置描述符接收,否则该描述符将会浪费 */ } /* 数据回调处理:回调存在且帧有效才处理 */ if((cb != 0) && (dataflag==0)) { cb(data, len); } else { DWC_ETH_WARN(("eth rx no cb or err\r\n")); } DWC_ETH_LOG(("eth rx desc %x %x\r\n",desc,gmac_get_currentrxdsc(0))); res++; /* 重新初始化描述符以便继续接收,这里四个描述符都可能被回写,所以都要重新设置 */ DWC_DESC_DESC0(desc) = (uint32_t)(p_ctrl->rxd.buf_base) + ETH_BUFFER_SIZE * index; DWC_DESC_DESC1(desc) = 0; DWC_DESC_DESC2(desc) = 0; DWC_DESC_DESC3(desc) = DWC_DESC_STATUS_INT_ENA | DWC_DESC_STATUS_OWNER_DMA | DWC_DESC_STATUS_BUF_1_ENA;
p_rxd->last_inx = index; /* 逐步推进,处理完了的就都可以作为tai了 */
index++; if (index >= p_rxd->buf_cnt) { index = 0; } } while (p_rxd->inx != index); /* 如果p_rxd->dinx和index相等说明整个环形缓冲区都处理了,也要停止继续. */
p_rxd->inx = index; /* 记录下一次从这里继续扫描 */
/* 重新设置Tail * Tail设置好后,不能再修改, * Current追赶到Tail之后,并且Current达到Ring_Length之后,会自动绕回到0, * 此时Current还是小于Tail,继续处理。 * 如果Current追赶到Tail之后,并且Current没有达到Ring_Length,则需要移动Tail来 * 使得Current * * 这里理论上需要判断停止才需要重启,为了简单总是进行重启操作。 */ desc = (uint32_t)(p_ctrl->rxd.desc_base) + (p_rxd->last_inx+1)*sizeof(eth_dma_t); gmac_start_rxdsc(p_ctrl->unit,desc);
return 0;}

接收由于要处理包数据所以花费时间比较大,一般不在中断中处理数据,也就是不在中断中调用 iot_eth_receive_handle ,而是查询方式调用 iot_eth_receive_handle ,或者接收 IOC 中断时中断中发送信号量或者设置标志,主线程主循环中接收信号量或标志才调用该函数进行接收处理。

三. 接收调试

收发数据流参考 https://mp.weixin.qq.com/s/klrHhaLMM_0W3FGVwHXFkA

的验证过程吗,按照 MAC 回环, PHY 回环,和 PC 通讯的过程测试。

3.1 检查描述符相关

接收调试主要关注以下描述符相关寄存器

检查 RxDesc_List RxDesc_Tail 是否设置正确,检查 RxDesc_Ring_Length 描述符个数是否设置正确。检查 RxDesc RxBuffer 是否递增变化。

(gdb) x /28xw 0x01161100

0x1161100: 0x00000000      0x00100001      0x00100be1      0x00000000

0x1161110: 0x00000000      0x0200c120      0x00000000 0x0200bd00

RxDesc_List

0x1161120: 0x0200c510      0x00000000 0x0200bd00 0x0000003f

RxDesc_Tail

0x1161130: 0x00000001 0x0000c8c3      0x00000000      0x00000000

RxDesc_Ring_Length

0x1161140: 0x00000000      0x0200c510      0x00000000 0x0200bd10

RxDesc

0x1161150: 0x00000000      0x0200bd20      0x00000000 0x0200b710

RxBuffer

0x1161160: 0x00000000      0x00000000      0x00000000      0x00000000

(gdb)

0x111C RxDesc_List

0x1128 RxDesc_Tail

0x1130 RxDesc_Ring_Length

0x114C RxDesc

0x115C RxBuffer

3.2 确认回写状态和缓存数据是否有

(gdb) p rx_desc

$5 = {{

des0 = 0x200b120,

des1 = 0x0,

des2 = 0x0,

des3 = 0xc1000000

}, {

des0 = 0x200b710,

des1 = 0x0,

des2 = 0x0,

des3 = 0xc1000000

}}

(gdb) p /x rx_buffer

$6 = { 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0x0, 0x42, 0xaa, 0x55 , 0x15, 0x13, 0x86, 0x4a, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x0 , 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0x0, 0x42, 0xaa, 0x55 , 0x15, 0x13, 0x86, 0x4a, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x50, 0x0, 0x0, 0x30, 0x0 }

(gdb)

3.3 确认接收状态

如果以上描述符都正确但是还是没有数据,确认接收是否正在工作

查看 DMA_Debug_Status0 寄存器

RPS0 3 表示 Running

(gdb) x /20xw 0x01161000

0x1161000: 0x00003000      0x00000001      0x00000000 0x00006300

DMA_Debug_Status0

0x1161010: 0x00000000      0x00000000      0x00000000      0x00000000

0x1161020: 0x00000000      0x00000000      0x00000000      0x00000000

0x1161030: 0x00000000      0x00000000      0x00000000      0x00000000

0x1161040: 0x00000000      0x00000000      0x00000000      0x00000000

(gdb)

3.4 检查过滤等其他设置

如果以上还未收到,检查过两次设置,最好设置为 RA 接收所有包,

确认 DMA SR 是否使能等。

. 发送调试

和接收类似

4.1 检查描述符

检查如下寄存器设置,看 TxDesc 是否递增

(gdb) x /28xw 0x01161100

0x1161100: 0x00000000      0x00100001      0x00100be1      0x00000000

0x1161110: 0x00000000 0x0200c120 0x00000000      0x0200bd00

TxDesc_List

0x1161120: 0x0200c3d0 0x00000000      0x0200bd20 0x0000003f

TxDesc_Tail Ring_Length

0x1161130: 0x00000001      0x0000c8c3      0x00000000      0x00000000

0x1161140: 0x00000000 0x0200c3d0 0x00000000      0x0200bd10

TxDesc

0x1161150: 0x00000000 0x0200bd20 0x00000000      0x0200b710

TxBuffer

0x1161160: 0x00000000      0x00000000      0x00000000      0x00000000

(gdb)

4.2 确认回写状态

(gdb) p /x tx_desc

$2 = {{des0 = 0x200bdc0, des1 = 0x0, des2 = 0x8000002a, des3 = 0x30000000} }

(gdb)

4.3 确认发送状态

如果以上描述符都正确但是还是没有数据,确认接收是否正在工作

查看 DMA_Debug_Status0 寄存器

(gdb) x /20xw 0x01161000

0x1161000: 0x00002000      0x00000001      0x00000001 0x00006400

DMA_Debug_Status0

0x1161010: 0x00000000      0x00000000      0x00000000      0x00000000

0x1161020: 0x00000000      0x00000000      0x00000000      0x00000000

0x1161030: 0x00000000      0x00000000      0x00000000      0x00000000

0x1161040: 0x00000000      0x00000000      0x00000000      0x00000000

(gdb)

4.4 检查发送数据等

检查发送数据是否又 64 字节以上,可以配置 CPC 自动添加 CRC 和填充。

确认 DMA ST 使能了发送等。

. 总结

收发驱动实际就是对描述符的操作,需要了解描述符环形结构,软件硬件分别是怎么实用描述符的。另外也需要了解相关寄存器,描述符格式等,了解如何去调试收发过程。



最新有关嵌入式Lee的文章

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: TI培训

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved