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.