Teal brain dump.

I’ve been working with this company that had their own PLI based verification process. We liked some of what teal gives you, but it seemed to lack several important features. Eventually we decided to go with teal, but add a few options we thought were missing.

Here is a brain dump of stuff after a short time using teal: (this is an honest assessment of negatives, since this is the requested features section)

1. Software clocks. Clocks that support arbitrary frequency, timescales, and phase. Added with a misc_tf callback.
2. A clean way to stop (tf_dostop) from a test. Our designers like to poke around after a simulation, and even do a restart from the command line. Threads not being cleaned up properly breaks this.
3. There currently is a reproducibility problem in teal. Say we start a thread at simulation time zero. Teal exits immediately to the simulator. The actual thread MAY start at simulation time zero, or any time after that. It depends on load, OS, and memory usage. This could cause two simulations with the same seed to diverge. When starting a thread teal should block all future time progress (or at least the thread starting a new thread) until it knows that thread is running. Can be fixed with an additional pthread_cond_wait() in the start_thread call and a thread_entry function that calls the user function. (This could also be used to call the note_thread_completed function for the user.)
4. Code readability, large blocks of commented out code. Although seeing how things used to be done is interesting, it really shouldn’t be there. Comments are a bit (a lot) sparse too. We all write code like this, but it should be cleaned up now that teal is released.
5. Classes mixed into code files. In specific, teal_sync should be broken up into a few files.
6. Teal’s logging system is not bad, however I have my own I like to use. It would be nice if we could decouple teal from logging.
7. Same goes for the data dictionary. We have no interest in using it. It would be nice to cleanly separate the stuff needed for connection to verilog from the stuff that provides added features not needed.

Let the discussion begin.
~Richard Bradley

Comments

Teal adds

Hi Richard,

>this is an honest assessment of negatives, since this is the requested features section

I like honest assessment ! We are engineers and seem to enjoy solving problems. (It's probably a genetic defect as my dad was an engineer. Since my wife is also an engineer (and both her parents), our kids have no chance ;-)

>1. Software clocks. Clocks that support arbitrary frequency, timescales, and phase. Added with a misc_tf callback.

Yes, some of my earlier versions of teal had that. It's a good feature and we will fold in your changes. My hesitation is that people tend to overuse the feature, like downcasting. In my current (and past several years work), I have required coders to have some reference clock and to express delays in terms of that clock. I think that makes for a better verification system. But I can certainly understand the counter argument that clocks should be in the verification domain, not the HDL domain.

>2. A clean way to stop (tf_dostop) from a test. Our designers like to poke around after a simulation, and even do a restart from the command line. Threads not being cleaned up properly breaks this.

Humm, I am a bit confused. Do you mean pause()? Teal has a finish() but that stops the sim. It does wait for all the threads to at least get to a wait point. I am curious as to what you've added as it is probably really useful.

>3. There currently is a reproducibility problem in teal. Say we start a thread at simulation time zero. Teal exits immediately to the simulator.

Humm, I though I wait until all threads are waiting (which means at an at() call). In the start_thread() function, I grab the mutex and in the thread_release::thread_created_ (), I set the waiting to false. I believe this should stop the initial teal_top_internal() from returning. But that code really hurts my brain (many 2am debugging sessions). We have not seen a practical problem in our projects with repeatability, but this is complex code and there might be bugs in there. Would be interesting to see your solution!

>4. Code readability, large blocks of commented out code

Guilty as charged. I will clean it up and comment the code in the next release.

>5. Classes mixed into code files. In specific, teal_sync should be broken up into a few files.

Yes, but one of the things I wanted to do was keep the "implementation classes" inside the file as they are not directly intended for the end users.

Yes, teal_synch.cpp is the misc/utility area that should be cleaned up and maybe split into several files..

>6. Teal’s logging system is not bad, however I have my own I like to use. It would be nice if we could decouple teal from logging.

The earlier versions of Teal did that. We had separate includes. I just got lazy and collapsed the code. You should be able to write a real simple teal::vout subclass that vectors all teal vouts to your logger. Will look into making the files more separate in future releases.

>7. Same goes for the data dictionary. We have no interest in using it.

Sure, I have worked at places that had their own dictionary. There are only a few places that teal's internals use dictionary; the logger comes to mind. You can either edit the code, or patch below dictionary to use your stuff. I've done both and pick one depending on the code I have to work with.

These are great comments. Thanks!

Take care,
Mike

> Humm, I am a bit confused.

> Humm, I am a bit confused. Do you mean pause()?

tf_dostop should drop you to the command line, unlike the finish, which exits. Modelsim offers a restart call that reloads the design (including the pli libs) and starts (recompiles, reloads) the simulation over again. This is useful for quick fixes. When you do this, all the threads should be killed. If they are not the simulator reallocates the memory the thread is using, and a good old-fashioned segv results.

You have a stop_all_threads call, but that does not kill the “Teal Control Thread.” Plus there is this Watcher thread, which is joined with the control thread, so I guess it should end.

There are two use cases:

1. At the end of a simulation, a designed makes changes and restarts the simulation from the command line without exiting the simulator. (Kinda working. I can restart 6 times before a segv.)

2. A designer halts a running simulation in the middle, makes some changes, then restarts. (Unsupported as of yet.)

As far as the reproducibility problem, you are right. This shouldn’t be happening.

Still, it seems like a good idea to start threads in lock-step mode. I added this code, which I think is bullet proof, although largely untested beta code. (It may make the watcher thread unneeded???) It also makes calling the note_thread_completed() function automatic on exit.

class thread_container{
public:
user_thread thread;
void *user_data;
};

void* teal::thread_entry(void * data){
thread_container * my_data = (thread_container *) data;
pthread_mutex_lock (&thread_release::thread_lockstep_mutex);
pthread_cond_broadcast (&thread_release::thread_created);
pthread_mutex_unlock(&thread_release::thread_lockstep_mutex);
(*(my_data->thread))(my_data->user_data);
note_thread_completed();
}

pthread_t teal::start_thread (user_thread thread, void* user_data, const std::string & name)
{
LOG("starting start_thread()\n", Logger::ALWAYS);
pthread_t id;
pthread_mutex_lock (&thread_release::main_mutex);
pthread_mutex_lock (&thread_release::start_thread_mutex);

thread_release::thread_being_created = name;

std::stringstream message;

message << "starting " << name <<"\n";
LOG(message, Logger::ALWAYS);

thread_container my_data;
my_data.thread = thread;
my_data.user_data = user_data;

pthread_mutex_lock (&thread_release::thread_lockstep_mutex);
int result = pthread_create (&id, NULL, teal::thread_entry, &my_data);
local_vout << teal_info << "Thread " << name << " created. ID is " << hex <<
thread_int (id) << " result " << result << endm;

pthread_cond_wait(&thread_release::thread_created, &thread_release::thread_lockstep_mutex);
pthread_mutex_unlock (&thread_release::thread_lockstep_mutex);
thread_release::thread_created_ (id, name);
thread_release::thread_being_created = "";
pthread_mutex_unlock (&thread_release::main_mutex);
pthread_mutex_unlock (&thread_release::start_thread_mutex);

// FLUSH for god’s sake!!!
std::cout << std::flush<< std::flush<< std::flush;
std::flush (std::cout);

return id;
}

Also we should check the return code from the thread creation.

Back to top