diff --git a/vvp/examples/disable.vvp b/vvp/examples/disable.vvp new file mode 100644 index 000000000..52f0f79f9 --- /dev/null +++ b/vvp/examples/disable.vvp @@ -0,0 +1,74 @@ +:vpi_module "system"; + +; Copyright (c) 2001 Stephen Williams (steve@icarus.com) +; +; This source code is free software; you can redistribute it +; and/or modify it in source code form under the terms of the GNU +; General Public License as published by the Free Software +; Foundation; either version 2 of the License, or (at your option) +; any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, write to the Free Software +; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + + +; This example is similar to the following Verilog code. The idea is +; to test the disable statement. The task creates a scope (called test) +; that is a hook for the disable statement to get at the thread that +; threatens to fail the test. If the disable statement works properly, +; the test task will be disabled before the #5 delay expires. +; +; module main; +; task test; +; begin +; #5 $display("FAILED..."); +; $finish; +; end +; endtask +; +; initial begin +; fork +; test; +; #1 disable test; +; join +; $display("PASSED"); +; end +; endmodule +; + +S_main .scope "main"; +S_test .scope "test", S_main; + + +; This code in the implementation of the thread that goes into the +; test scope. The idea is to disable this thread before it does its +; job and prints a failure. + .scope S_test; +T_0/1 ; + %delay 5; + %vpi_call "$display", "FAILED -- thread wasn't disbled"; + %vpi_call "$finish"; + %end; + +; This is the main thread. Fork the thread under test, delay for a +; moment to let it run, then %disable the test thread. If I don't +; disable the test thread in time, then the child will print an error +; message and exit. + .scope S_main; +T_0 ; + %fork T_0/1, S_test; + %delay 1; + + %disable S_test ; This is the statement that I'm testing. + + %join; + %vpi_call "$display", "PASSED"; + %end; + + .thread T_0; diff --git a/vvp/schedule.cc b/vvp/schedule.cc index 0dcde8645..2aaff90d5 100644 --- a/vvp/schedule.cc +++ b/vvp/schedule.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) -#ident "$Id: schedule.cc,v 1.5 2001/04/18 04:21:23 steve Exp $" +#ident "$Id: schedule.cc,v 1.6 2001/04/21 00:34:39 steve Exp $" #endif # include "schedule.h" @@ -120,6 +120,7 @@ void schedule_vthread(vthread_t thr, unsigned delay) cur->delay = delay; cur->thr = thr; cur->type = TYPE_THREAD; + vthread_mark_scheduled(thr); schedule_event_(cur); } @@ -195,6 +196,9 @@ void schedule_simulate(void) /* * $Log: schedule.cc,v $ + * Revision 1.6 2001/04/21 00:34:39 steve + * Working %disable and reap handling references from scheduler. + * * Revision 1.5 2001/04/18 04:21:23 steve * Put threads into scopes. * diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 1aeeb1a27..ca430e439 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) -#ident "$Id: vthread.cc,v 1.28 2001/04/18 05:04:19 steve Exp $" +#ident "$Id: vthread.cc,v 1.29 2001/04/21 00:34:39 steve Exp $" #endif # include "vthread.h" @@ -87,6 +87,7 @@ struct vthread_s { unsigned schedule_parent_on_end :1; unsigned i_have_ended :1; unsigned waiting_for_event :1; + unsigned is_scheduled :1; /* This points to the sole child of the thread. */ struct vthread_s*child; /* This points to my parent, if I have one. */ @@ -194,9 +195,19 @@ static void vthread_reap(vthread_t thr) thr->scope_prev->scope_next = thr->scope_next; thr->pc = 0; - delete thr; + + /* If this thread is not scheduled, then is it safe to delete + it now. Otherwise, let the schedule event (which will + execute the thread at of_ZOMBIE) delete the object. */ + if (thr->is_scheduled == 0) + delete thr; } +void vthread_mark_scheduled(vthread_t thr) +{ + assert(thr->is_scheduled == 0); + thr->is_scheduled = 1; +} /* * This function runs a thread by fetching an instruction, @@ -204,6 +215,9 @@ static void vthread_reap(vthread_t thr) */ void vthread_run(vthread_t thr) { + assert(thr->is_scheduled); + thr->is_scheduled = 0; + for (;;) { vvp_code_t cp = codespace_index(thr->pc); thr->pc += 1; @@ -492,18 +506,24 @@ bool of_DISABLE(vthread_t thr, vvp_code_t cp) assert(tmp->child == 0); assert(tmp != thr); /* XXXX Not supported yet. */ - assert(tmp->waiting_for_event); + assert(tmp->waiting_for_event == 0); tmp->pc = 0; tmp->i_have_ended = 1; - /* If a parent is waiting in a %join, wake it up. */ if (tmp->schedule_parent_on_end) { + /* If a parent is waiting in a %join, wake it up. */ assert(tmp->parent); schedule_vthread(tmp->parent, 0); - } + vthread_reap(tmp); - if (tmp->parent == 0) { + } else if (tmp->parent) { + /* If the parent is yet to %join me, let its %join + do the reaping. */ + //assert(tmp->is_scheduled == 0); + + } else { + /* No parent at all. Goodby. */ vthread_reap(tmp); } } @@ -523,23 +543,10 @@ bool of_END(vthread_t thr, vvp_code_t) thr->i_have_ended = 1; thr->pc = 0; -#if 0 - /* Reap direct descendents that have already ended. Do - this in a loop until I run out of dead children. */ - - while (thr->child && (thr->child->i_have_ended)) { - fprintf(stderr, "vvp warning: A thread left dangling " - "children at %%end. This is caused\n" - " : by a missing %%join.\n"); - vthread_t tmp = thr->child; - vthread_reap(tmp); - } -#else /* If this thread has children, then there is a programming error as there were not enough %join instructions to reap all the children. */ assert(thr->child == 0); -#endif /* If I have a parent who is waiting for me, then mark that I @@ -556,12 +563,6 @@ bool of_END(vthread_t thr, vvp_code_t) no reason to stick around. This can happen, for example if I am an ``initial'' thread. */ if (thr->parent == 0) { -#if 0 - if (thr->child) - fprintf(stderr, "vvp warning: A thread left dangling " - "children at delete. This is caused\n" - " : by a missing %%join.\n"); -#endif vthread_reap(thr); return false; } @@ -872,6 +873,9 @@ bool of_ZOMBIE(vthread_t thr, vvp_code_t) /* * $Log: vthread.cc,v $ + * Revision 1.29 2001/04/21 00:34:39 steve + * Working %disable and reap handling references from scheduler. + * * Revision 1.28 2001/04/18 05:04:19 steve * %end complete the %join for the parent. * diff --git a/vvp/vthread.h b/vvp/vthread.h index 37ddacaaa..6adf87ad7 100644 --- a/vvp/vthread.h +++ b/vvp/vthread.h @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) -#ident "$Id: vthread.h,v 1.4 2001/04/18 04:21:23 steve Exp $" +#ident "$Id: vthread.h,v 1.5 2001/04/21 00:34:39 steve Exp $" #endif /* @@ -42,6 +42,12 @@ typedef struct vthread_s*vthread_t; */ extern vthread_t vthread_new(unsigned long sa, struct __vpiScope*scope); +/* + * This function marks the thread as scheduled. It is used only by the + * schedule_vthread function. + */ +extern void vthread_mark_scheduled(vthread_t thr); + /* * Cause this thread to execute instructions until in is put to sleep * by executing some sort of delay or wait instruction. @@ -59,6 +65,9 @@ extern void vthread_schedule_list(vthread_t thr); /* * $Log: vthread.h,v $ + * Revision 1.5 2001/04/21 00:34:39 steve + * Working %disable and reap handling references from scheduler. + * * Revision 1.4 2001/04/18 04:21:23 steve * Put threads into scopes. *