From ca919b3ce0627bbf4d63fdb3bb92c4d59e5bcd83 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Apr 2022 17:40:03 +0200 Subject: [PATCH] vvp: Add `%disable/flow` instruction The `%disable` instruction will stop the execution of all active threads of a specific scope. This is what is required to implement the semantics of the Verilog `disable` statement. But it is not suited to implement the SystemVerilog flow control statements such as `return`, `continue` and `break`. These only affect the thread hierarchy from which it is called, but not other concurrently running threads from the same scope. Add a new `%disable/flow` instruction that will only disable the thread closest to the current thread in the thread hierarchy. This can either be the thread itself or one of its parents. This will leave other concurrent threads of the same scope untouched and also allows function recursion since only the closest parent thread is disabled. Note that it is not possible to implement this using `%jmp` instructions since a block in a function with variable declarations will be its own sub-thread, but using flow control instructions it is possible to exit from that thread to the parent scope, which is not possible with `%jmp` instructions. Signed-off-by: Lars-Peter Clausen --- vvp/codes.h | 1 + vvp/compile.cc | 1 + vvp/opcodes.txt | 11 +++++++++++ vvp/vthread.cc | 24 ++++++++++++++++++++++++ 4 files changed, 37 insertions(+) diff --git a/vvp/codes.h b/vvp/codes.h index 5c75d3855..2de76495f 100644 --- a/vvp/codes.h +++ b/vvp/codes.h @@ -103,6 +103,7 @@ extern bool of_DELETE_ELEM(vthread_t thr, vvp_code_t code); extern bool of_DELETE_OBJ(vthread_t thr, vvp_code_t code); extern bool of_DELETE_TAIL(vthread_t thr, vvp_code_t code); extern bool of_DISABLE(vthread_t thr, vvp_code_t code); +extern bool of_DISABLE_FLOW(vthread_t thr, vvp_code_t code); extern bool of_DISABLE_FORK(vthread_t thr, vvp_code_t code); extern bool of_DIV(vthread_t thr, vvp_code_t code); extern bool of_DIV_S(vthread_t thr, vvp_code_t code); diff --git a/vvp/compile.cc b/vvp/compile.cc index 5626103b0..9d1e4d26f 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -155,6 +155,7 @@ static const struct opcode_table_s opcode_table[] = { { "%delete/obj",of_DELETE_OBJ,1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, { "%delete/tail",of_DELETE_TAIL,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%disable", of_DISABLE, 1, {OA_VPI_PTR,OA_NONE, OA_NONE} }, + { "%disable/flow", of_DISABLE_FLOW, 1, {OA_VPI_PTR,OA_NONE, OA_NONE} }, { "%disable/fork",of_DISABLE_FORK,0,{OA_NONE,OA_NONE, OA_NONE} }, { "%div", of_DIV, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%div/s", of_DIV_S, 0, {OA_NONE, OA_NONE, OA_NONE} }, diff --git a/vvp/opcodes.txt b/vvp/opcodes.txt index 97c7a9652..24840c5b8 100644 --- a/vvp/opcodes.txt +++ b/vvp/opcodes.txt @@ -469,6 +469,17 @@ This instruction terminates threads that are part of a specific scope. The label identifies the scope in question, and the threads are the threads that are currently within that scope. +* %disable/flow + +This instruction is similar to `%disable` except that it will only disable a +single thread of the specified scope. The disabled thread will be the thread +closest to the current thread in the thread hierarchy. This can either be thread +itself or one of its parents. + +It is used to implement flow control statements called from within a thread that +only affect the thread or its parents. E.g. SystemVerilog `return`, `continue` +or `break`. + * %disable/fork This instruction terminates all the detached children for the current diff --git a/vvp/vthread.cc b/vvp/vthread.cc index ec4c976c9..29f665125 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -2718,6 +2718,30 @@ bool of_DISABLE(vthread_t thr, vvp_code_t cp) return ! disabled_myself_flag; } +/* + * Similar to `of_DISABLE`. But will only disable a single thread of the + * specified scope. The disabled thread will be the thread closest to the + * current thread in thread hierarchy. This can either be the current thread, + * either the thread itself or one of its parents. + * This is used for SystemVerilog flow control instructions like `return`, + * `continue` and `break`. + */ + +bool of_DISABLE_FLOW(vthread_t thr, vvp_code_t cp) +{ + __vpiScope*scope = static_cast<__vpiScope*>(cp->handle); + vthread_t cur = thr; + + while (cur && cur->parent_scope != scope) + cur = cur->parent; + + assert(cur); + if (cur) + return !do_disable(cur, thr); + + return false; +} + /* * Implement the %disable/fork (SystemVerilog) instruction by disabling * all the detached children of the given thread.