iverilog/vvm/vvm_simulation.cc

223 lines
5.4 KiB
C++

/*
* Copyright (c) 1998 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
*/
#if !defined(WINNT)
#ident "$Id: vvm_simulation.cc,v 1.2 1998/11/10 00:48:31 steve Exp $"
#endif
# include "vvm.h"
# include "vvm_thread.h"
# include <assert.h>
/*
* The state of the simulation is stored as a list of simulation
* times. Each simulation time contains the delay to get to it, and
* also a list of events that are to execute here.
*
* The "events" member points to a list of inactive events to be made
* active as a group. Update events go into this list.
*
* The "nonblock" member points to a list of events to be executed
* only after ordinary events run out. This is intended to support the
* semantics of nonblocking assignment.
*/
struct vvm_simulation_cycle {
unsigned long delay;
struct vvm_simulation_cycle*next;
struct vvm_simulation_cycle*prev;
struct vvm_event*event_list;
struct vvm_event*event_last;
struct vvm_event*nonblock_list;
struct vvm_event*nonblock_last;
vvm_simulation_cycle()
: event_list(0), event_last(0), nonblock_list(0),
nonblock_last(0)
{ }
};
vvm_simulation::vvm_simulation()
{
sim_ = new vvm_simulation_cycle;
sim_->delay = 0;
sim_->next = sim_->prev = sim_;
mon_ = 0;
}
vvm_simulation::~vvm_simulation()
{
}
void vvm_simulation::insert_event(unsigned long delay, vvm_event*event)
{
assert(delay > 0);
vvm_simulation_cycle*cur = sim_->next;
while ((cur != sim_) && (cur->delay < delay)) {
delay -= cur->delay;
cur = cur->next;
}
if ((cur == sim_) || (cur->delay > delay)) {
vvm_simulation_cycle*cell = new vvm_simulation_cycle;
cell->delay = delay;
if (cur != sim_)
cur->delay -= delay;
cell->next = cur;
cell->prev = cur->prev;
cell->next->prev = cell;
cell->prev->next = cell;
cur = cell;
}
event->next_ = 0;
if (cur->event_list == 0) {
cur->event_list = cur->event_last = event;
} else {
cur->event_last->next_ = event;
cur->event_last = event;
}
}
void vvm_simulation::monitor_event(vvm_event*event)
{
mon_ = event;
}
void vvm_simulation::active_event(vvm_event*event)
{
event->next_ = 0;
if (sim_->event_list == 0) {
sim_->event_list = sim_->event_last = event;
} else {
sim_->event_last->next_ = event;
sim_->event_last = event;
}
}
/*
* This function takes a list of simulation cycles and runs the
* simulation. It consumes the list as the events are executes,
* eventually deleting everything.
*/
void vvm_simulation::run()
{
assert(sim_);
time_ = 0;
going_ = true;
while (going_) {
/* Step the time forward to the cycle I am about to
execute. */
time_ += sim_->delay;
sim_->delay = 0;
for (;;) {
/* Look for some events to make active. If the
main event list is empty, then activate the
nonblock list. */
vvm_event*active = sim_->event_list;
sim_->event_list = 0;
sim_->event_last = 0;
if (active == 0) {
active = sim_->nonblock_list;
sim_->nonblock_list = 0;
sim_->nonblock_last = 0;
}
/* Oops, no events left. Break out of this time cycle. */
if (active == 0)
break;
while (active) {
vvm_event*cur = active;
active = cur->next_;
cur->event_function();
delete cur;
}
}
/* XXXX Execute monitor events here. */
if (mon_) {
mon_->event_function();
mon_ = 0;
}
/* The time cycle is done, delete it from the list and
step to the next time. */
struct vvm_simulation_cycle*next = sim_->next;
if (next == sim_) {
continue;
}
sim_->next->prev = sim_->prev;
sim_->prev->next = sim_->next;
delete sim_;
sim_ = next;
}
}
class delay_event : public vvm_event {
public:
delay_event(vvm_thread*thr) : thr_(thr) { }
void event_function() { while (thr_->go()) /* empty */; }
private:
vvm_thread*thr_;
};
void vvm_simulation::s_finish()
{
going_ = false;
}
void vvm_simulation::thread_delay(unsigned long delay, vvm_thread*thr)
{
assert(delay > 0);
delay_event*ev = new delay_event(thr);
insert_event(delay, ev);
}
void vvm_simulation::thread_active(vvm_thread*thr)
{
delay_event*ev = new delay_event(thr);
active_event(ev);
}
/*
* $Log: vvm_simulation.cc,v $
* Revision 1.2 1998/11/10 00:48:31 steve
* Add support it vvm target for level-sensitive
* triggers (i.e. the Verilog wait).
* Fix display of $time is format strings.
*
* Revision 1.1 1998/11/09 23:44:11 steve
* Add vvm library.
*
*/