Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms

Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms

Pseudocode from article of the above name in PODC96 (with two typos corrected), by Maged M. Michael and Michael L. Scott. Corrected version also appeared inJPDC, 1998.

The non-blocking concurrent queue algorithm performs well on dedicated as well as multiprogrammed multiprocessors with and without contention. The algorithm requires a universal atomic primitive,CAS or LL/SC.

The two-lock concurrent queue algorithm performs well on dedicated multiprocessors under high contention. Useful for multiprocessors without a universal atomic primitive.


Non-Blocking Concurrent Queue Algorithm

  structure pointer_t {ptr: pointer to node_t, count: unsigned integer}
  structure node_t {value: data type, next: pointer_t}
  structure queue_t {Head: pointer_t, Tail: pointer_t}
  
  initialize(Q: pointer to queue_t)
     node = new_node()		// Allocate a free node
     node->next.ptr = NULL	// Make it the only node in the linked list
     Q->Head.ptr = Q->Tail.ptr = node	// Both Head and Tail point to it
  
  enqueue(Q: pointer to queue_t, value: data type)
   E1:   node = new_node()	// Allocate a new node from the free list
   E2:   node->value = value	// Copy enqueued value into node
   E3:   node->next.ptr = NULL	// Set next pointer of node to NULL
   E4:   loop			// Keep trying until Enqueue is done
   E5:      tail = Q->Tail	// Read Tail.ptr and Tail.count together
   E6:      next = tail.ptr->next	// Read next ptr and count fields together
   E7:      if tail == Q->Tail	// Are tail and next consistent?
               // Was Tail pointing to the last node?
   E8:         if next.ptr == NULL
                  // Try to link node at the end of the linked list
   E9:            if CAS(&tail.ptr->next, next, <node, next.count+1>)
  E10:               break	// Enqueue is done.  Exit loop
  E11:            endif
  E12:         else		// Tail was not pointing to the last node
                  // Try to swing Tail to the next node
  E13:            CAS(&Q->Tail, tail, <next.ptr, tail.count+1>)
  E14:         endif
  E15:      endif
  E16:   endloop
         // Enqueue is done.  Try to swing Tail to the inserted node
  E17:   CAS(&Q->Tail, tail, <node, tail.count+1>)
  
  dequeue(Q: pointer to queue_t, pvalue: pointer to data type): boolean
   D1:   loop			     // Keep trying until Dequeue is done
   D2:      head = Q->Head	     // Read Head
   D3:      tail = Q->Tail	     // Read Tail
   D4:      next = head.ptr->next    // Read Head.ptr->next
   D5:      if head == Q->Head	     // Are head, tail, and next consistent?
   D6:         if head.ptr == tail.ptr // Is queue empty or Tail falling behind?
   D7:            if next.ptr == NULL  // Is queue empty?
   D8:               return FALSE      // Queue is empty, couldn't dequeue
   D9:            endif
                  // Tail is falling behind.  Try to advance it
  D10:            CAS(&Q->Tail, tail, <next.ptr, tail.count+1>)
  D11:         else		     // No need to deal with Tail
                  // Read value before CAS
                  // Otherwise, another dequeue might free the next node
  D12:            *pvalue = next.ptr->value
                  // Try to swing Head to the next node
  D13:            if CAS(&Q->Head, head, <next.ptr, head.count+1>)
  D14:               break             // Dequeue is done.  Exit loop
  D15:            endif
  D16:         endif
  D17:      endif
  D18:   endloop
  D19:   free(head.ptr)		     // It is safe now to free the old node
  D20:   return TRUE                   // Queue was not empty, dequeue succeeded
  

Two-Lock Concurrent Queue Algorithm

  structure node_t {value: data type, next: pointer to node_t}
  structure queue_t {Head: pointer to node_t, Tail: pointer to node_t,
                        H_lock: lock type, T_lock: lock type}
  
  initialize(Q: pointer to queue_t)
     node = new_node()		// Allocate a free node
     node->next = NULL          // Make it the only node in the linked list
     Q->Head = Q->Tail = node	// Both Head and Tail point to it
     Q->H_lock = Q->T_lock = FREE	// Locks are initially free
  
  enqueue(Q: pointer to queue_t, value: data type)
     node = new_node()	        // Allocate a new node from the free list
     node->value = value		// Copy enqueued value into node
     node->next = NULL          // Set next pointer of node to NULL
     lock(&Q->T_lock)		// Acquire T_lock in order to access Tail
        Q->Tail->next = node	// Link node at the end of the linked list
        Q->Tail = node		// Swing Tail to node
     unlock(&Q->T_lock)		// Release T_lock
  
  dequeue(Q: pointer to queue_t, pvalue: pointer to data type): boolean
     lock(&Q->H_lock)	        // Acquire H_lock in order to access Head
        node = Q->Head		// Read Head
        new_head = node->next	// Read next pointer
        if new_head == NULL	// Is queue empty?
           unlock(&Q->H_lock)	// Release H_lock before return
           return FALSE		// Queue was empty
        endif
        *pvalue = new_head->value	// Queue not empty.  Read value before release
        Q->Head = new_head	// Swing Head to next node
     unlock(&Q->H_lock)		// Release H_lock
     free(node)			// Free node
     return} TRUE		// Queue was not empty, dequeue succeeded
  


<think>好的,我现在要解决用户遇到的PDS报错:“Cannot mix blocking and non-blocking assignments to variable read_en”。这个问题出现在Verilog或SystemVerilog代码中,通常是因为在同一个always块中对同一个变量同时使用了阻塞赋值(=)和非阻塞赋值(<=)。我需要一步步分析可能的原因,然后给出解决方法。 首先,我应该回忆Verilog中阻塞和非阻塞赋值的区别。阻塞赋值是按顺序执行的,而非阻塞赋值是在块结束时并行更新的。如果在同一个always块中对同一变量混合使用这两种赋值方式,会导致仿真和综合工具无法确定正确的执行顺序,从而报错。 接下来,用户可能需要了解具体的代码示例。比如,用户可能在同一个always块中写了类似以下代码: always @(posedge clk) begin if (condition) begin read_en = 1'b1; // 阻塞赋值 end else begin read_en <= 1'b0; // 非阻塞赋值 end end 这里read_en在同一块中同时用了=和<=,导致冲突。 接下来,我需要解释为什么不能混用。阻塞赋值会立即更新变量,而非阻塞赋值则在块结束后更新。混用会导致仿真结果不确定,尤其在时序逻辑中,可能引起竞争条件,使得设计行为不可预测。 然后,解决方案应该是统一赋值方式。如果是组合逻辑,应该全部使用阻塞赋值;如果是时序逻辑(如时钟触发的always块),应该统一使用非阻塞赋值。例如,用户需要检查所有对read_en的赋值,确保在同一个always块中只用一种方式。 可能还需要检查是否有多个always块对同一变量进行赋值。比如,一个always块用阻塞,另一个用非阻塞,这种情况下即使分开块,但同一变量被不同块赋值也可能导致问题。不过根据报错信息,更可能是在同一块中混用。 另外,可能的其他情况是,在同一个块中不同的分支用了不同的赋值方式,比如if和else部分分别用了阻塞和非阻塞。需要统一修改为同一种。 最后,用户可能需要一个修改后的正确示例,比如将上述例子中的阻塞赋值改为非阻塞: always @(posedge clk) begin if (condition) begin read_en <= 1'b1; // 非阻塞 end else begin read_en <= 1'b0; // 非阻塞 end end 或者,如果是组合逻辑,使用阻塞赋值: always @(*) begin if (condition) begin read_en = 1'b1; // 阻塞 end else begin read_en = 1'b0; // 阻塞 end end 同时提醒用户,组合逻辑必须覆盖所有条件,避免锁存器生成。 总结步骤:定位所有对read_en的赋值,检查是否在同一块中混用,统一修改为同一种赋值方式,确保组合或时序逻辑的正确使用。</think>在Verilog/SystemVerilog设计中,出现`Cannot mix blocking and non-blocking assignments to variable read_en`报错,表示对同一个变量`read_en`同时使用了阻塞赋值(`=`)和非阻塞赋值(`<=`)。以下是逐步分析和解决方法: --- ### **步骤1:理解报错原因** - **阻塞赋值(`=`)**:立即生效,常用于组合逻辑(如`always @(*)`)。 - **非阻塞赋值(`<=`)**:在代码块结束时更新,用于时序逻辑(如`always @(posedge clk)`)。 - **冲突场景**:在同一个`always`块中对`read_en`混用了`=`和`<=`,导致仿真和综合工具无法确定赋值顺序。 --- ### **步骤2:定位问题代码** 检查所有对`read_en`赋值的`always`块。典型错误示例如下: ```verilog // 错误示例:混合赋值 always @(posedge clk) begin if (reset) read_en = 1'b0; // 阻塞赋值(错误!) else if (condition) read_en <= 1'b1; // 非阻塞赋值(正确) end ``` --- ### **步骤3:统一赋值方式** 根据逻辑类型选择一致的赋值规则: #### **情况1:时序逻辑(时钟驱动)** - **全部使用非阻塞赋值**(`<=`) ```verilog // 正确示例:统一非阻塞赋值 always @(posedge clk) begin if (reset) read_en <= 1'b0; // 非阻塞 else if (condition) read_en <= 1'b1; // 非阻塞 end ``` #### **情况2:组合逻辑(无时钟)** - **全部使用阻塞赋值**(`=`),并确保覆盖所有分支 ```verilog // 正确示例:统一阻塞赋值 always @(*) begin if (reset) read_en = 1'b0; // 阻塞 else if (condition) read_en = 1'b1; // 阻塞 else read_en = 1'b0; // 覆盖默认情况 end ``` --- ### **步骤4:检查多always块冲突** 确保`read_en`没有在其他`always`块中被不同赋值方式操作: ```verilog // 错误示例:多always块混合赋值 always @(posedge clk) begin read_en <= new_value; // 非阻塞 end always @(*) begin read_en = another_value; // 阻塞(冲突!) end ``` - **解决方法**:一个变量只能在一个`always`块中赋值,或统一所有赋值方式。 --- ### **总结** 1. **时序逻辑**:统一使用`<=`。 2. **组合逻辑**:统一使用`=`,并覆盖所有条件分支。 3. **禁止跨always块混用**:同一变量不可在多个块中以不同方式赋值。 通过以上步骤,可彻底解决混合赋值导致的PDS报错问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值