vvp: Handle %fork in `final` procedures

In the current implementation a `%fork` instruction in a final block will
get scheduled, but never executed.

And while SystemVerilog requires a `final` procedure to execute in 0 time
and so no SystemVerilog `fork` is allowed inside of it, there are some
other scenarios where iverilog generates `%fork` statements.

For example when declaring variables in a sub-block a sub-scope with its
own thread is is used to allocate the storage for those variables and
`%fork` is used to execute the child thread.

E.g. the following, while being valid SystemVerilog, will never execute the
loop because the generated code will implement the loop as a child thread
being executed by a `%fork` statement.
```
  final for (int i = 0; i < 10; i++) $display(i);
```

To mitigate this treat final statements the same as functions and rather
than scheduling a child thread, execute it immediately when using the
`%fork` statement.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2023-04-16 08:42:22 -07:00
parent b210eb8264
commit df30eda3a3
3 changed files with 19 additions and 0 deletions

View File

@ -855,6 +855,7 @@ void schedule_final_vthread(vthread_t thr)
struct vthread_event_s*cur = new vthread_event_s;
cur->thr = thr;
vthread_mark_final(thr);
vthread_mark_scheduled(thr);
schedule_final_event(cur);

View File

@ -833,6 +833,19 @@ void vthread_mark_scheduled(vthread_t thr)
}
}
void vthread_mark_final(vthread_t thr)
{
/*
* The behavior in a final thread is the same as in a function. Any
* child thread will be executed immediately rather than being
* scheduled.
*/
while (thr != 0) {
thr->i_am_in_function = 1;
thr = thr->wait_next;
}
}
void vthread_delay_delete()
{
if (running_thread)

View File

@ -51,6 +51,11 @@ extern vthread_t vthread_new(vvp_code_t sa, __vpiScope*scope);
*/
extern void vthread_mark_scheduled(vthread_t thr);
/*
* This function marks the thread as being a final procedure.
*/
extern void vthread_mark_final(vthread_t thr);
/*
* This function causes deletion of the currently running thread to
* be delayed until after all sync events have been processed for the