Link layer之device driver

3.7 Device Drivers

Driver在跟device通信之前,需要先初始化环境,包括:probing I/O portsprobing IRQs

 

DMA是在没有CPU的干涉下将大块数据从内存传输数据(或将大块数据传输到内存)。

有两种方式可以出发DMA传输:

1)Read() or wirte()被调用

2)硬件异步的写数据到内存

 

DMA的操作步骤如下:

1)process调用read()时,驱动程序会分配一个DMA buffer。然后该process休眠。

2)Hardware写数据进DMA buffer后会产生一个interrupt

3)Interrupt会唤醒process,现在,read()完成了。

 

1)当data到达时,hardware产生一个interrupt

2)Handler分配一个DMAbuffer,并通知hardware开始传输。

3)Hardware写数据到buffer完成后,产生另一个interrupt

4)Handler会唤醒process来处理这些数据。

3.7.1 Interrupt Handling in Linux

当一个interrupt发生时,linux会将中断号pushstack中,然后调用do_IRQ()do_IRQ()将查询interrupt handler,然后通过调用handle_IRQ_event()来调用handlerThe handler会尽快释放CPU然后调度bottom-half function

3.7.2 Network Device Driver in Linux

3部分讲解network device driver:初始化、发送过程、接收过程。

 

下面是与初始化有关的net_device structure的几个field

char name[IFNAMSIZ]device name,如:eth0

unsigned int irq:中断号

unsigned short type:该数字指示了设备类型,如:Ethernet

unsigned char dev_addr[MAX_ADDR_LEN] : MAC address

unsigned char addr_len:地址长度

int promiscuity:是否运行在混杂模式。

 

初始化过程

函数alloc_netdev() (在net/core/dev.c中)负责初始化net_device structure。如Ethernetalloc_etherdev() (在net/ethernet/eth.c) 调用alloc_netdev()来初始化以太网卡。所初始化的field包括:IRQ, I/O memory, I/O port, MAC address等等。之后,netdev_boot_setup_check()被调用,来检查一些启动配置参数,然后,用register_netdevice()完成设备注册

Transmission Process

 

如上图,要发送frame时,首先调用hard_atart_xmit(),然后调用以太网的ei_start_xmit(),然后ei_start_xmit()调用ne2k_pci_block_output()(以NE2000为例)来把frame放到device接口。当frame发出后,device会产生一个hardware interrupt,内核将调用handlerei_interrupt()ei_interrupt()先判断中断类型,当它发现这是一个frame发送完成的interrupt时,ei_tx_intr()被调用,然后ei_tx_intr()调用NS8390_trigger_send()来发送下一个frame,然后调用netif_wake_queue()来处理下一个任务。

 

Reception process

 

如上图,当device收到一个frame时,产生一个hardware interrupt,该handlerei_interrupt()ei_interrupt()判断这是一个接收中断,就调用ei_receive()ne2k_pci_black_input()frame移动到系统内存,并填写sk_buff structurenetif_rx_action()frame传输到上层协议。

Remarkinterrupt handler的处理时间跟framesize无关;DMA的时间随着framesize的增加而增加。DMA的接收数据包的时间要明显高于其发送数据包的时间。

下面是一个 CHI 协议中 LINK layer 的 tx driver 的 UVM 代码示例: ```systemverilog class chi_link_tx_driver extends uvm_driver #(chi_packet); `uvm_component_utils(chi_link_tx_driver) virtual function void build_phase(uvm_phase phase); super.build_phase(phase); // Get the virtual interface handle to the DUT if (!uvm_config_db #(virtual chi_if)::get(this, "", "chi_if", m_if)) `uvm_fatal("NO_IF", "Virtual interface not defined for chi_link_tx_driver") // Create and configure the sequence item queue m_seq_item_q = new("seq_item_q", this); m_seq_item_q.default_priority = 100; endfunction virtual task run_phase(uvm_phase phase); chi_packet pkt; while (1) begin // Wait for the next sequence item to be available @(m_seq_item_q.get_with_priority(pkt)) // Set the tx packet type and data m_if.tx_pkt_type = pkt.type; m_if.tx_pkt_data = pkt.data; // Send the CHI packet to the DUT using the virtual interface m_if.tx_pkt_valid = 1; repeat (10) @(posedge m_if.clk); m_if.tx_pkt_valid = 0; end endtask endclass ``` 在上面的代码中,`chi_packet` 是一个定义了 CHI packet 的 UVM sequence item 类。`build_phase` 函数获取了指向 DUT 的虚拟接口 `m_if`,并创建了一个名为 `m_seq_item_q` 的 sequence item 队列。`run_phase` 函数使用 `m_seq_item_q` 从队列中获取下一个 sequence item 并将其发送到 DUT。 在发送 CHI packet 之前,该 driver 需要将 packet 的类型和数据传递给 DUT,这里使用了虚拟接口中的两个信号 `tx_pkt_type` 和 `tx_pkt_data`。然后,该 driver 设置 `tx_pkt_valid` 信号为 1,表示 CHI packet 准备就绪。为了确保 DUT 能够接收到整个 packet,这里使用了一个简单的 `repeat` 循环来等待一定的时钟周期,然后将 `tx_pkt_valid` 信号置为 0,表示传输完成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值