RTEMS 4.9.5:QEMU MINI2440 BSP 中的网络驱动开发(下)

(原创文章,转载请注明出处,谢谢。)

 驱动编译运行,呵呵,跑起来了,欣喜之余,要看看还有什么问题没有解决,还有什么吸取的经验。首先:

1.DM9000的特性没有完全支持;
    * 没有操作DM9000的eeprom部分;
    * 没有调试dm9000的phy部分;
    * dm9000支持发送两个队列,我们只使用了一个;
    * dm9000发送采取的是查询策略,而非中断策略。效率低下。

2.QEMU 仿真 DM9000 有些问题,尚未确认。

3.延时函数。

我们一个问题一个问题说:

DM9000的EEPROM的支持问题,由于不影响大局可以暂时放一样,但是听rickleaf和我交流时,所这一块仿真时有问题。即在QEMU MINI2440 上不能正常工作,目前还不能确认是QEMU的问题还是驱动本省的问题,这个问题姑且放一放。

DM9000的内部的PHY和DM9000的MAC层已经无缝的结合在一起,两个自动协商,不要对其做匹配,即MAC工作在100Mbps下,phy肯定也工作在100Mbps下,MAC工作在全双工下,phy也工作在全双工下。这不用烦心。我实际的读过phy的寄存器,不是很成功,可以考虑是QEMU的仿真问题。

DM9000支持的两个发送队列的问题,我写得驱动只用了一个,没有使用第二个。这个问题,动手做过实验。发现程序并不稳定,我参考了Linux的驱动,在QEMU上仿真时,一段时候后就完蛋了。由于没有实际的开发板验证,实在是不知道是代码自身的问题,还是QEMU的问题。以下是改成双缓冲的 发送代码,在 dm9000_softc_t中增加了两个字段,txPktCnt,用于表示当前队列中发送包的数量。txPktLen表示队列中第二包的长度。

/* Send packet */
void dm9000_sendpacket(dm9000_softc_t *sc, struct mbuf *m)
{
    unsigned int length = 0;
    rtems_interval tmo; /* Move data to DM9000 TX RAM */
    DM9000_outb(DM9000_MWCMD, DM9000_IO);/* Prepare for TX-data */
    length = (sc->outmbuf)(m); m_freem(m); /* Move data to DM9000 TX RAM */
    DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */
    /* push the data to the TX-fifo */
    (sc->outblk)(packet, length);
    sc->txPktCnt++;
    if (sc->txPktCnt == 1)
    {
        /* Set TX length to DM9000 */
        DM9000_iow(DM9000_TXPLL, length & 0xff);
        DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff);
        /* Issue TX polling command */
        DM9000_iow(DM9000_TCR, TCR_TXREQ);/* Cleared after TX complete */
    }
    else
    {
        sc->txPktLen = length; /* wait for end of transmission */
        tmo = rtems_clock_get_ticks_since_boot() + 5 * rtems_clock_get_ticks_per_second();
        while ( !(DM9000_ior(DM9000_NSR) & (NSR_TX1END | NSR_TX2END)) ||
            !(DM9000_ior(DM9000_ISR) & IMR_PTM) )
        {
            if (rtems_clock_get_ticks_since_boot() >= tmo)
            {
                printf("transmission timeout/n");
                DM9000_iow(DM9000_ISR, IMR_PTM);
                return;
            }
            /*rtems_task_wake_after(0); *//*yeild CPU to other task*/
        }
        DM9000_iow(DM9000_ISR, IMR_PTM);
        /* Clear Tx bit in ISR */
        sc->txPktCnt --;
        if (sc->txPktCnt > 0)
        {
            /* Set TX length to DM9000 */
            DM9000_iow(DM9000_TXPLL, (sc->txPktLen) & 0xff);
            DM9000_iow(DM9000_TXPLH, ((sc->txPktLen) >> 8) & 0xff);
            /* Issue TX polling command */
            DM9000_iow(DM9000_TCR, TCR_TXREQ);
            /* Cleared after TX complete */
        }
    }
    DM9000_DBG("transmit done/n/n");
}

 

关于发送采用中断模式也是有同样的问题,我也做了实验,在QEMU上运行时也有不稳定的问题,但代码层面我的确找不到问题了。请高手们多多指正。我改来改去,发现查询方式最稳定,呵呵,那就用查询方式发布吧。

/* * Driver transmit daemon */
void dm9000_txDaemon (void *arg)
{
    dm9000_softc_t *sc = (dm9000_softc_t *)arg;
    struct ifnet *ifp = &sc->arpcom.ac_if;
    struct mbuf *m; rtems_event_set events;
    for (;;)
    {
        /* turn on TX interrupt, then wait for one */
        rtems_bsdnet_event_receive(
            START_TRANSMIT_EVENT,
            RTEMS_EVENT_ANY | RTEMS_WAIT,
            RTEMS_NO_TIMEOUT,
            &events);
        /* Get the next mbuf chain to transmit. */
        IF_DEQUEUE(&ifp->if_snd, m);
        if (!m)
        {
            ifp->if_flags &= ~IFF_OACTIVE;
            continue;
        }
        dm9000_sendpacket(sc, m);
    }
}

/* Send packet */
void dm9000_sendpacket(dm9000_softc_t *sc, struct mbuf *m)
{
    unsigned int length = 0;
    /* Move data to DM9000 TX RAM */
    DM9000_outb(DM9000_MWCMD, DM9000_IO);
    /* Prepare for TX-data */
    length = (sc->outmbuf)(m);
    m_freem(m);
    /* Set TX length to DM9000 */
    DM9000_iow(DM9000_TXPLL, length & 0xff);
    DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff);
    /* Issue TX polling command */
    DM9000_iow(DM9000_TCR, TCR_TXREQ);
    /* Cleared after TX complete */
}

/* interrupt handler */
rtems_isr dm9000_isr (rtems_vector_number v)
{
    int status;
#if !defined(CONFIG_NET_POLL_CONTROLLER)
    rEINTMASK |= 0x80;
#endif
    DM9000_iow(DM9000_IMR, IMR_PAR);
    status = DM9000_ior(DM9000_ISR);
    DM9000_iow(DM9000_ISR, status);
    if ((status & ISR_PRS))
    {
        rtems_event_send (softc.rxDaemonTid, START_RECEIVE_EVENT);
    }
    if ((status & ISR_PTS))
    {
        int txstat;
        txstat = DM9000_ior(DM9000_NSR);
        if (txstat & (NSR_TX1END | NSR_TX2END))
            rtems_event_send (softc.txDaemonTid, START_TRANSMIT_EVENT);
    }
    DM9000_iow(DM9000_IMR, IMR_PAR | IMR_PRM | IMR_PTM);
#if !defined(CONFIG_NET_POLL_CONTROLLER)
    rEINTPEND = 0x80;
    ClearPending(BIT_EINT4_7);
    rEINTMASK &= ~0x80;
#endif
}

最有关于一个udelay函数的问题,

udelay函数RTEMS没有提供该函数的实现,需要自己撰写。我就偷懒写了个for 循环的延迟。这样做是不对的。由于没有实际的硬件,QEMU执行ARM的代码完全不像实际那样。所以靠指令实现精确的1us的延迟,是不太可能。实际中使用应采用定时器精确测量多少条循环指令可以实现1us的延迟。这样才能做出较好的udelay来。


下面和大家深入地讨论一些问题:

查看代码,发现 mbuf 的相关宏 MGETHDR、MGET 使用 splimp()、splimp、splx()三个函数保护其临界区域。然而,在 rtems_bsdnet_internal.h 中这三个的宏定义为:

#define splnet()    0
#define splimp()    0

#define splx(_s)    do { (_s) = 0; } while(0)

 

都是为空的。并且网络中的许多操作都是依靠这几个锁来实现临界区域的保护。这三个宏这么定义,显然什么保护都没有。照理说,不保护,那么在多任务情况下,那是得不到正确结果的,然而我们实际使用中却能得到正确结果,这又是为什么呢?

 阅读RTEMS官方的文档  networking.pdf 可以找到答案。他们提供了一个 rtems_bsdnet_semaphore_obtain() 和 rtems_bsdnet_semaphore_release() 进行这个级别的互斥。用于解决网络内部的数据结构一致性的问题。

 

这里还有最后问题:为什么要这样设计呢?这样设计的优缺点在哪里?

这个问题,我想了很久,这个答案可能会仁者见仁智者见智,姑且抛砖引玉吧。这么设计首先不像uC/OS-II这样的操作系统也不像Linux这样的操作系统,动不动就关中断,我们都知道 RTOS 的最重要的一个性能指标就是实时性。其中对中断的响应延迟时间是个非常重要的指标。RTEMS这样的设计,最明显的就是不用关中断,即使关了中断,时间也非常的短,直接的好处就是响应中断延迟小。自然实时性就会好。

 

这么设计的优点就是实时性好。除去这个优点,我们看看缺点在哪里。首先,网络的接收、发送、协议栈的运转是三个任务。他们之间不能同时运行。这会对网络的性能有比较大的影响,尤其是我们的系统中有1个以上的网络驱动,假设有两个网口,那么就有5个任务。5个任务抢占调度,在一个网口有大数据量发送时,必然会影响第二个网口的性能。当然这也不是一成不变的,如果MAC支持DMA链,可以在DMA链上多放一些缓冲区,可以对网络瞬时来的数据做一定的缓冲。对性能也会有比较大的改善。但仅对于DM9000这种没有DMA的芯片来说,对性能多多少少还是有些影响吧,特别是对突发访问有影响。(我手头没有条件做测试,不然肯定会做个实验,在实际板子上验证我的思路。)

 

讲到这里,我想起来了,看到Linux里DM9000发送时,使用两个发送缓冲区,发送采用中断触发。看看他的临界区域的保护,两个开关中断的自旋锁之间竟然放着数据包的拷贝过程(蓝色字体标识的)。这个在实时操作系统中是难以想象的,关中断时间长不说,占用总线资源时间也长。这对RTOS来说是个噩梦。所以我考虑来考虑去还是用了查询方法,也就是基于实时性的考虑,虽然那样写在实际中DM9000的工作效率并不高。但可以使用一个队列元素,采用中断触发,作为一些性能改善。如果一些朋友有更好的建议,更好的解释和方法,恳请一定赐教!!!谢谢。

 

 

static int
dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
    unsigned long flags;
    board_info_t *db = netdev_priv(dev);

    dm9000_dbg(db, 3, "%s:/n", __func__);

    if (db->tx_pkt_cnt > 1)
        return NETDEV_TX_BUSY;

    spin_lock_irqsave(&db->lock, flags);

    /* Move data to DM9000 TX RAM */
    writeb(DM9000_MWCMD, db->io_addr);

    (db->outblk)(db->io_data, skb->data, skb->len);
    dev->stats.tx_bytes += skb->len;

    db->tx_pkt_cnt++;
    /* TX control: First packet immediately send, second packet queue */
    if (db->tx_pkt_cnt == 1) {
        /* Set TX length to DM9000 */
        iow(db, DM9000_TXPLL, skb->len);
        iow(db, DM9000_TXPLH, skb->len >> 8);

        /* Issue TX polling command */
        iow(db, DM9000_TCR, TCR_TXREQ);    /* Cleared after TX complete */

        dev->trans_start = jiffies;    /* save the time stamp */
    } else {
        /* Second packet */
        db->queue_pkt_len = skb->len;
        netif_stop_queue(dev);
    }

    spin_unlock_irqrestore(&db->lock, flags);

    /* free this SKB */
    dev_kfree_skb(skb);

    return 0;
}

 

(原创文章,转载请注明出处,谢谢。)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
嵌入式操作系统RTEMS是一个开源的实时操作系统,其体系结构研究主要涉及系统的核心组件、系统内核以及支持库的设计。RTEMS的体系结构设计着重解决了实时性、稳定性、灵活性等方面的问题,使得其具备适用于各类嵌入式设备的特性。 首先,RTEMS的体系结构包括了核心组件的设计。核心组件主要包括调度器、断处理器、时钟管理器和内存管理器等,它们共同协作以保证操作系统的实时性和可靠性。调度器负责任务的调度和切换,断处理器负责处理硬件断,时钟管理器负责管理系统的时钟和定时器,内存管理器负责管理系统的内存资源。 其次,RTEMS的体系结构设计了系统内核,内核包括了任务管理、进程间通信、设备驱动等子系统。任务管理子系统负责创建、删除和管理任务及其优先级,进程间通信子系统提供了多种通信方式,用于任务之间的数据交换和同步,设备驱动子系统负责管理和控制外设设备。 此外,RTEMS的体系结构还包括了支持库的设计。支持库提供了丰富的功能接口,包括文件系统、网络协议栈、图形界面等,以便开发者可以更方便地进行应用程序的开发。支持库还包括了各类驱动程序,用于操作硬件设备、传感器等外部设备。 针对RTEMS的应用开发环境的设计与实现,可以提供开发工具链、仿真环境和调试工具等方面的支持。开发工具链包括编译器、调试器和链接器等,用于编译和构建应用程序。仿真环境可以提供虚拟硬件平台,用于在开发过程模拟嵌入式设备的运行环境。调试工具可以帮助开发者进行错误定位和调试,提高开发效率。 综上所述,嵌入式操作系统RTEMS的体系结构研究与应用开发环境的设计与实现是为了使其具备适用性广泛的特点,并提供便利的开发环境,以支持开发者更加高效地进行嵌入式应用程序的开发

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值