xHCI1.1-TRB Ring

本文详细解释了TRB Ring在USB设备通信、xHCI事件传递和命令处理中的角色,涉及Transfer、Event和Command Ring的工作原理、数据结构、指针操作及动态调整。讲解了Enqueue/Dequeue Pointer的管理,TRB状态转换,以及如何调整Ring大小以适应内存分配。

4.9 TRB Ring

TRB(Transfer Request Block)  Ring是一个TRB数据结构的环状队列,TRB Ring用来在producer和consumer之间传送工作项(Work Item)。和其相关的有两个指针,出队指针(Dequeue)和入队指针(Enqueue)。一个工作项由一个或多个TRB数据结构组成。一个工作项定义了一个将要被执行的操作或是一个已经执行的操作的结果。

有三种基本TRB Rings:Transfer,Event和Command。每种Ring都有其专用的TRB数据结构。但他们都是使用底层的TRB Ring机制来组织工作项和基本TRB模版。

    

Transfer Rings提供来自或是到USB设备的数据传输。Transfer Rings和USB Pipes之间有个一对一的映射关系。这是通过设备上下文中的端口上下文数据结构(或者端口上下文指向的流上下文数组)定义的。

Event Rings为xHCI提供向系统软件报告的方式,报告的内容包括:数据传输和命令完成状态,root hub port状态改变,和其他xHCI相关的事件。一个Event Ring通过Runtime Registers中的Event Ring Segment Table Base Address,Segment Table Size和Dequeue Pointer Registers来定义。

Command Ring为系统软件提供发送命令来枚举USB设备,配置xHCI支持设备,以及协调虚拟化功能的能力。Command Ring由Operational register中的Command Ring Control Register来支持。

Enqueue pointer和Dequeue pointer是用来标志一个TRB Ring中的逻辑开始和结束的。TRB Ring的size由组成ring的segment的数量和大小决定的。Enqueue Pointer是R下一个Producer可用的Ring中的TRB地址,Producer从这位置创建一个新的工作项,然后将这个指针前移。Dequeue则是Consumer将要使用的TRB的地址。当Dequeue Pointer等于Enqueue pointer时,TRB Ring就是空的了。如果Enqueue pointer+1=Dequeue pointer,那么这个ring就是满的。在Enqueue Pointer-1和Dequeue Pointer之间的TRBs由Consumer拥有使用权。其他的TRBs就是Producer拥有使用权。每当这两个指针改变之后,那么TRB所有权就会发生转换。Consumer需要按序执行TRB。所有TRB都是16Bytes的大小。TRB Rings可能会比一个Page大,但是他们不能越过64K的界限(详见4.11.5.1)。

初始化,也就是当TRB ring在内存中创建或者如果它每次被重新初始化时,Ring中所有TRBs都需要被清“0”。这个状态表示一个空的队列。

注意:Transfer和Command Ring中的出入队指针并没有定义相应的物理xHCI寄存器,但是Event Ring定义了。

4.9.1 Transfer Descriptors

一个TD中包含一个或多个TRBs。TRB Chain (C) bit在所有TRB中都有设置,但是每个TD的最后一个TRB不会设置。xHCI可以为与一个TD相关的所有packets安排Max Packet Size USB 事务,可能除了最后一个packet(如果这个TD没有定义一个整数个的Max Packet Size data bytes)。

为生成一个0长度的USB事务,软件应该精确的定义一个只有一个TRB的TD,并且它的TRB Transfer Length字段应该等于0。注意这个TD可以包含non-Transfer TRBs,比如Event Data或者Link TRB。

在这个协议中有许多关于xHC应该“前移到下一个TD”的条件描述。但是,如果xHC在这些情况之一发生时正在处理一个TD的一个部分(a partially formed  TD),则不可能前进到下一个TD,并且当到达Enqueue Pointer时xHC将停止前进。在这种情形下,xHC认为Transfer Ring是空的即两个指针相等,接着当下一次doorbell为端点Rung,xHC应该尝试去进入下一个TD的范围。注意xHC应该通过一个Set TR Dequeue Pointer Command来说明新的TR Dequeue Pointer。

“部分完成的TD”通过以下情况来标识:Dequeue pointer引用的TRB中的Chain bit(CH)设置为“1”,并推进Dequeue将其设置为等于Enqueue Pointer。

注意: Command和Event TRBs并不支持Chain bit(CH),因此所有的Command Descriptors (CDs)和Event Descriptor (EDs)只有单个TRB构成。(后面还有两个note)

4.9.2 Transfer Ring  Management

这节描述Transfer Ring中Enqueue和Dequeue pointer的操作。如图所示,如表示一个Transfer Ring。Producer(host)在Enqueue  Pointer将item放入Transfer Ring,Consummer(xHC)在Dequeue Pointer把item取出。TRB中的Cycle bit字段标识了Transfer Ring中的Enqueue Pointer中的位置。消除了使用寄存器来定义。

软件使用并且维持对于各个Transfer Ring的Enqueue和Dequeue pointer的副本。在Transfer ring初始setup时,Enqueue和Dequeue Pointers对于每个Transfer Ring都是设置为第一个TRB的地址,并且写入Endpoint/Stream Context TR Dequeue Pointer 字段。软件通过增加其大小每次增加一个TRB size或在遇到链接TRB时将其与环形段指针字段的值重新加载来推进其Enqueue pointer的副本。

xHC同样对每个Transfer Ring维持着自己对于这两个指针的副本。当一个Transfer Ring被启用或被重置,xHC使用Endpoint/Stream Context TR Dequeue Pointer Filed来更新这两个副本。xHC使用Dequeue Pointer来决定从Transfer Ring的哪儿拿取下一个work Item。xHC更新Dequeue Pointer的方式和软件的更新方式一样。

xHC使用Enqueue Pointer来确定什么时候Transfer Ring是空的。当它从一个Tansfer Ring取出TRBs时,它会检测是否有一个Cycle bit事务。如果一个事务被检测到,ring就是空的了。

而反过来,Software使用Dequeue pointer确定什么时候Transfer ring是满的。当他处理tansfer Events,它使用Transfer Event TRB Pointer 字段来更新它自身维持的Dequeue Pointer。如果当前移Enqueue Pointer时使得它等与Dequeue Pointer那么表明Transfer Ring就是满的。

Producer将要保持一个Producer Cycle State(PCS)flag用于标识它应该写入TRB Cycle bit的值。Consumer维持一个Consumer Cycle State(CCS)flag,它用此flag来和它取出的TRB中Cycle bit进行比较。如果CCS flag等于TRB Cycle bit的值,那么Consumer将获得此TRB(Dequeue Pointer指向的)的

<think>好的,我现在需要回答用户关于dequeue数据结构及其实现方式的问题。首先,我需要确认dequeue的正确术语,用户可能指的是“双端队列”(deque,全称double-ended queue),而不是“dequeue”这个操作。因为“dequeue”通常指从队列中移除元素的操作,但用户提到的是数据结构,所以应该是指双端队列。 接下来,我需要回忆双端队列的基本定义和特性。双端队列允许在队头和队尾进行元素的添加和删除,这比普通队列更灵活。普通队列是FIFO,而双端队列则支持FIFO和LIFO操作。 然后,关于实现方式,常见的两种方法是基于数组和链表。数组实现需要考虑扩容和缩容,以及循环数组的应用来优化性能。链表实现则更灵活,不需要处理容量问题,但每个节点需要额外的指针空间。 在具体实现细节上,数组实现可能需要维护头尾指针,处理索引的循环。链表实现则需要维护头尾节点,确保在两端操作的时间复杂度都是O(1)。此外,动态数组在扩容时可能需要复制元素,而链表则可以动态分配节点。 用户提供的参考引用中提到了队列的FIFO特性,但双端队列扩展了这一概念,所以需要明确区分两者的不同。同时,可能需要补充双端队列的应用场景,比如滑动窗口、撤销操作等,以帮助用户理解其用途。 最后,生成相关问题时要围绕双端队列的特性、实现差异、应用场景和复杂度展开,确保问题能够帮助用户深入理解该数据结构。</think>### Deque(双端队列)数据结构及实现方式 **Deque**(Double-Ended Queue)是一种允许在**队头**和**队尾**两端进行数据插入(`addFront`/`addRear`)和删除(`removeFront`/`removeRear`)操作的线性数据结构[^1]。它结合了队列(FIFO)和栈(LIFO)的特性,适用于需要双向操作的场景。 --- #### 核心特性 1. **双向操作**:支持两端的高效插入和删除(时间复杂度通常为$O(1)$)。 2. **动态容量**:可根据需求动态扩展或收缩存储空间。 3. **多场景适配**:既可模拟队列(单端操作),也可模拟栈(同端插入删除)。 --- #### 实现方式 ##### 1. **基于动态数组(List)** - **原理**:使用数组存储元素,通过维护头尾指针实现双向操作。 - **关键操作**: - **插入**:若数组空间不足,需动态扩容(例如翻倍)。 - **删除**:若数组利用率过低,可动态缩容。 - **索引计算**:使用取模运算处理循环数组逻辑(避免频繁数据搬移)。 - **示例代码(Python)**: ```python class ArrayDeque: def __init__(self): self.capacity = 4 self.size = 0 self.front = 0 self.rear = 0 self.array = [None] * self.capacity def add_front(self, item): if self.size == self.capacity: self._resize(self.capacity * 2) self.front = (self.front - 1) % self.capacity self.array[self.front] = item self.size += 1 def remove_rear(self): if self.size == 0: raise IndexError("Deque is empty") item = self.array[self.rear] self.array[self.rear] = None self.rear = (self.rear - 1) % self.capacity self.size -= 1 return item ``` ##### 2. **基于双向链表(Doubly Linked List)** - **原理**:每个节点包含前驱和后继指针,直接支持双向操作。 - **关键操作**: - **插入/删除**:直接调整相邻节点的指针,无需处理扩容。 - **示例代码(Python)**: ```python class Node: def __init__(self, value): self.value = value self.prev = None self.next = None class LinkedListDeque: def __init__(self): self.head = None self.tail = None self.size = 0 def add_front(self, item): new_node = Node(item) if self.size == 0: self.head = self.tail = new_node else: new_node.next = self.head self.head.prev = new_node self.head = new_node self.size += 1 def remove_rear(self): if self.size == 0: raise IndexError("Deque is empty") item = self.tail.value if self.size == 1: self.head = self.tail = None else: self.tail = self.tail.prev self.tail.next = None self.size -= 1 return item ``` --- #### 实现方式对比 | 特性 | 动态数组 | 双向链表 | |--------------------|-----------------------------------|---------------------| | **随机访问** | $O(1)$(通过索引) | $O(n)$ | | **内存占用** | 连续内存,可能浪费空间 | 非连续,需额外指针空间 | | **扩容/缩容成本** | 需数据搬移,均摊$O(1)$ | 无 | | **适用场景** | 高频随机访问,预知最大规模 | 频繁双向操作,动态规模 | --- #### 应用场景 1. **滑动窗口算法**:需同时访问窗口的头部和尾部元素(如LeetCode“滑动窗口最大值”)。 2. **撤销操作**:通过双端队列记录操作历史,支持前进/回退(如文本编辑器)。 3. **多线程任务调度**:任务可从任意端插入或提取,提升灵活性。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值