在任务

时间:2016-06-16 10:32:46

标签: system-verilog uvm

请帮助理解在使用递归我的任务时遇到的一个问题。 因此,有一项任务应该跟踪2个信号enable和ddr_clk。它们是异步信号,启用可以在ddr_clk posedge之前出现。在这种情况下,任务应该等待启用高,然后@(posedge itf.ddr_clk)发生,以便写入完成。 为了实现这一点,我在任务中使用递归:

task automatic write();
    $display ("%t:\tdriver: write function", $time);
    if ( itf.enable == 1 ) begin
        $display ("%t:\tdriver-write: ->writing in DUT: assiging wdata and waiting for ddr_clk posedge...", $time);
        itf.wdata = req.data_wfifo_data; // assign and wait for clk posedge
        fork
            begin : wait_clock;
            @ (posedge itf.ddr_clk) 
                $display ("%t:\tdriver-write: Got ddr_clk posedge, considering write done", $time);
            end

            @(itf.enable) // In case when enable goes to low before posedge
            begin
                $display ("%t:\tdriver-write: enable changed, re-calling write", $time);
                disable wait_clock;
                write;
            end
        join_any
    end else 
    begin
        $display ("%t:\tdriver-write-enable: waiting for enable...", $time);
         @(itf.enable)
          write;
    end
    $display ("%t:\tdriver: END write function", $time);

endtask

这就是模拟结果给出的结果:

65490750.0 ps:  driver: write function
65490750.0 ps:  driver-write: ->writing in DUT: assiging data_wfifo_data and waiting for ddr_clk posedge...
65490850.0 ps:  driver-write: enable changed, re-calling write
65490850.0 ps:  driver: write function
65490850.0 ps:  driver-write-enable: waiting for enable...
65490850.0 ps:  driver-write: enable changed, re-calling write
65490850.0 ps:  driver: write function
65490850.0 ps:  driver-write-enable: waiting for enable...
65490850.0 ps:  driver: END write function
65490850.0 ps: Driver: END Calling write task to write in DUT

在相同的模拟时间,任务被多次调用,然后退出。

请帮助了解行为。 我的印象是“禁用wait_clock”语句在那里不起作用......

2 个答案:

答案 0 :(得分:1)

我假设您没有向我们展示活动的完整日志。此任务必须有早期的$ display语句。此外,如果启用是真的,您确定数据是否稳定?

每次使用启用write()致电true时都会遇到问题,如果在 @(posedge itf.ddr_clk)变低之前得到enable ,那么留下悬挂的过程。然后当enable变低时,它们都会立即被触发。您可以在disable fork之后加join_any

我认为在这里使用递归会使事情变得更复杂。我会用一个循环代替

task write;
  fork
    forever begin
              wait(itf.enable);
              itf.wdata = req.data_wfifo_data;
              wait(!itf.enable);  // @(itf.enable or req.data_wfifo_data) -- if really level sensitive 
            end
    @(posedge itf.ddr_clk iff itf.enable);
  join_any
  disable fork;
endtask

答案 1 :(得分:0)

在fork和join_any块中,第二个块(its.enable)在65490850.0执行,它禁用wait_clock块并调用write(2)。

这导致两个流程。

  1. 在递归中第二次调用write(2)。 第二个任务将等待甚至在稍后完成或进一步递归调用写入。

  2. 在write(1)实例中,wait_clock块被禁用,fork join_any现在完成执行。 作为其join_any,禁用块会触发join_any的完成。代码通过跳过else并显示“END write function”并返回到驱动程序中的主调用函数。

    然后调用write的主函数将显示“END调用写入任务以写入DUT”。

  3. 一种解决方案

    添加计数以查看递归调用的次数。它还有助于调试。 它也用于开始等待第一次执行(写入)的触发器。无论何时wait_clock完成,它都会生成触发器。 您可以在首选项结构中实例化触发器。

    task automatic write(int count );
        $display ("%t:\tdriver: write function , count %d", $time,count);
        if ( itf.enable == 1 ) begin
            $display ("%t:\tdriver-write: ->writing in DUT: assiging wdata and waiting for ddr_clk posedge... count %d", $time,count);
            itf.wdata = req.data_wfifo_data; // assign and wait for clk posedge
            fork
                begin : wait_clock;
                @ (posedge itf.ddr_clk)
                    $display ("%t:\tdriver-write: Got ddr_clk posedge, considering write done count %d", $time,count);
                    ->itf.done;
                    //disable wait_enable;
                end
    
               //begin : wait_enable
                @(itf.enable) // In case when enable goes to low before posedge
                begin
                    $display ("%t:\tdriver-write: enable changed, re-calling write count %d", $time,count);
                    disable wait_clock;
                    write(count+1);
                end
               //end
            join_any
        end else
        begin
            $display ("%t:\tdriver-write-enable: waiting for enable...count %d", $time,count);
             @(itf.enable)
              write(count+1);
        end
        if ( count == 0 ) wait (itf.done.triggered);
        $display ("%t:\tdriver: END write function , count %d", $time,count);
    
    end task
    

    但递归函数很难跟踪和调试。在这种情况下,基于循环的函数MIGHT也同样好。 递归有另一个潜在的问题,因为禁用wait_clock块将导致所有递归函数中的所有wait_clock块被禁用。不确定它是否会引起问题,但需要注意一些问题。

    基于协议的有限数据/不足,基于循环的功能如下所示。 代码试图模仿上面的代码,但可以进一步简化甚至修改,以便与协议相关的修复。

    task automatic write_non_recursive ();
    int done,got_clock,got_enable ;
        done = 0 ;
        $display ("%t:\tdriver: write function , ", $time);
        while ( done == 0 )
        begin
            if ( itf.enable == 1 ) begin
                $display ("%t:\tdriver-write: ->writing in DUT: assiging wdata and waiting for ddr_clk posedge... ", $time);
                itf.wdata = req.data_wfifo_data; // assign and wait for clk posedge   
            end
            got_clock = 0 ;
            fork
                begin //using got clock to check edge
                    @ (posedge itf.ddr_clk) ; got_clock = 1;
                end
                @(itf.enable) ;
            join_any
            //if ( itf.ddr_clk == 1 && itf.enable == 1 ) - if we were looking onyl into clock
            if ( got_clock == 1 && itf.enable == 1 )
            begin
                    $display ("%t:\tdriver-write: Got ddr_clk posedge, considering write done ", $time);
                    done = 1;
            end
            else if ( itf.enable == 0 ) // en = 0 , clk = 1 or en = 0 , clk = 0 ;
            begin
                    $display ("%t:\tdriver-write: enable changed, re-calling write ", $time);
                    @(itf.enable) ; // wait for the next edge
            end
            else  // en = 1 , clk = 0 ;
                    $display ("%t:\tdriver-write-enable: waiting for enable...", $time);
            got_clock = 0; //reset and look for edge again
        end
        $display ("%t:\tdriver: END write function , ", $time);
    
    end task