在 fork-join 的永远循环中使用禁用 fork

问题描述 投票:0回答:2

我在 fork-join 中设置了“disable fork”,它嵌入了 forever begin 中。 因此,我期望每次线程中的任何一个完成后都会创建和终止新进程。但它只运行一次。

我有下面的代码只执行一次。

module tb();
  
  `timescale 1ns/1ns
  
  bit clk;
  
  always #2 clk =~ clk;
  
  task automatic req_ack(ref bit req, ref bit ack, input string name);
    forever begin
          $display("waiting for req");
          @(req);
          $display("Req changed at %t for %0d", $time, req);
          fork
            begin
              #5us;
              $display("ACK expired");
              disable fork;
            end
            begin
              wait(ack == req); //This will succeed
              $display("Ack received for %s at %t", name, $time);                    
              disable fork;
            end
          join
    end
  endtask
        
  bit tx_req, tx_ack;
        
  initial begin
    repeat(5) @(posedge clk);
    tx_req = 1;
    repeat(5) @(posedge clk);
    tx_ack = 1;
    $display("ack from tb at %t", $time);
    repeat(5) @(posedge clk);
    tx_req = 0;
    repeat(5) @(posedge clk);
    tx_ack = 0;    
    repeat(15) @(posedge clk);
    $finish;
  end
        
  initial begin
    req_ack(tx_req, tx_ack, "TX");
  end
  
  
endmodule

在上面的任务中,一旦 ack_arrival 完成,“disable fork”会杀死整个任务而不是进程,这样任务就不会检测到任何其他“req”更改。 有人可以解释为什么会这样吗?

system-verilog
2个回答
2
投票

disable fork
禁用当前线程的children。您的声明在没有子进程的分叉进程中。所以
join
不会发生,直到您的过期超时发生在 5µs。但是,你在 138ns 之前调用
$finish
,然后才有可能发生。如果您将到期延迟更改为5ns,您将看到发生了什么。

你应该在之后立即使用

fork/join_any
disable fork

// `timescale needs to go outside module

 `timescale 1ns/1ns


module tb();
  
  
  bit clk;
  
  always #2 clk =~ clk;
  
  task automatic req_ack(ref bit req, ref bit ack, input string name);
    bit local_req, local_ack; 
    fork
      // workaround for accessing ref arguments in fork/join_any
      forever @req local_req = req;
      forever @ack local_ack = ack;
      forever begin
        $display("waiting for req");
        @(req);
        $display("Req changed at %t for %0d", $time, req);
        fork
          begin
            #5us;
            $display("ACK expired");
          end
          begin
            wait(local_ack == local_req); //This will succeed
           $display("Ack received for %s at %t", name, $time);                    
          end
        join_any
        disable fork;
      end
    join
  endtask
        
  bit tx_req, tx_ack;
        
  initial begin
    repeat(5) @(posedge clk);
    tx_req <= 1; // use NBA to avoid tb races
    repeat(5) @(posedge clk);
    tx_ack <= 1;
    $display("ack from tb at %t", $time);
    repeat(5) @(posedge clk);
    tx_req <= 0;
    repeat(5) @(posedge clk);
    tx_ack <= 0;    
    repeat(15) @(posedge clk);
    $finish;
  end
        
  initial begin
    req_ack(tx_req, tx_ack, "TX");
  end
  
  
endmodule

-2
投票

有缺陷的解决方案

program automatic test;
    bit tx_req = 0;
    
    task automatic req_ack(ref bit req);
       while(1) begin
            @(req);
            fork: STOP_THIS
                begin : timeout
                    int delay = $urandom_range(1,3);
                    repeat(delay) #1;
                    $display("timeout thread run");
                    disable STOP_THIS;
                end
                begin : ack_arrival
                    int delay = $urandom_range(1,3);
                    repeat(delay) #1;
                    $display("ack_arrival thread run"); 
                    disable STOP_THIS;
                end 
            join
        end
    endtask : req_ack
 
    initial begin     
        req_ack(tx_req); 
    end
    initial begin
        for(int i=0; i<10; i++) begin
            tx_req = i%2;
            #10;
        end
    end            
endprogram

这是测试平台的链接EDA playgroup

© www.soinside.com 2019 - 2024. All rights reserved.