Tuesday 28 January 2014

My Stint with SystemC Processes


SystemC process are fundamental construct in systemC to model concurrency. There are three types of process namely SC_METHOD, SC_THREAD, SC_CTHREAD but the ones that I have actively used are the first two. There is lot of documentation/example available about SC_THREAD and SC_METHODS in systemC LRM and on the web, hence I am not going into explaining what is a Thread or a Method but rather I want to focus on the issues/challenges I came across when I first started to use them to solve real world modelling problem.



What to use a Method or a Thread?

The first question that comes in front of any model designer is the selection of right process. For a given problem of modeling what is the good to use? A thread or a Method? Most of the synchronization problems can be solved by using either a methods or a thread, but how do we know what’s the best. I do not have answers for all these questions but here is what I did when I came across them.
Any process a Method or a Thread are implemented using a C++ function  and these key statements  express the nature of Method and Thread in a fashion that can help in selecting one over the other.


  1. A function defined as a Thread is a non re-interant function but it can break/suspend in between using wait call. And it has to be designed in a way that it either wait or return after its done with its job.
  2. A function defined as  Methods is a reentrant and its monolithic in nature that means once it is invoked it has to complete without taking any simulation time. And it can never do a wait.
  3. Any function that gets called from a context of a Thread or a Method becomes a thread or a Method and it has to comply with their rules.
By carefully reading into above statement we can get to our selection pretty quickly. Let me give some example. 
Modeling reactive Behavior: Let say we not want to create a process that does not want to do any thing on its own. But rather it simply wants to wake up on certain events do it job and return. Then selecting a Method is most suitable. But how ever if  we want the process to consume certain simulation time it is better to use a thread and spread the operation   between several wait statements.

Speed Considerations: The way a Thread work is that when a wait is called then it saves the entire content of the stack into memory and then load the function it has to execute next which may be lying in another process. This involves major context switching cost. Hence if we want a process do a certain work very frequently then it is much more efficient to model it like a Method. However if a process has to wake up and some work only sparingly then using a thread is not that costly.

Ease of Modeling Concurrency/Pipeline: If we have to model concurrent behavior then modeling them on a Thread is very straight forward. We simple have to create parallel threads and connect them with events. Do  the same this using a method is not very easy. We have to design methods and have to break the pipe stages into monolithic chunks, put them on separate Methods and then synchronize them. I am not saying that it cannot be done but the natural choice here should be a Thread.

Process Initiating request to TLM buses: If we have to create a process that gives calls to a TLM bus, then we have to necessary use it like a Thread. The reason for it is that TLM buses support blocking interfaces meaning they can call a wait() somewhere down the call stack. As per statement 3 above any call to a blocking bus should be from a thread . If a Method calls any blocking API on a bus and if somewhere down the call somebody does a wait, then the whole simulation will crash.

Some Subtle properties of Thread/Method Processes

At any given time only one thread or Method runs: If we have done any multi threading programming then we would have come across race conditions and deadlock issue. That’s way in most of the multi thread code we tend to use synchronization data structures like semaphres, crtitcal section etc. However systemC simulation is a single thread application, systemC kernel uses something called co-operative multi tasking feature of the OS to give this feel of concurrency. However at any given time only one SC_THREAD or a SC_METHOD  runs. This is a very important assumption and it eases out the modeling care-about a lot.

An Infinite loop in a Thread: The most common implementation of a Thread function is done inside a wile(1) loop. That sometime can give a message that we can write an infinite loop anywhere in the thread function and still simulation work. That’s a misconception, we can very well write an infinite loop in any thread but we should have at least one blocking call in the loop. If not then the whole simulation will hang. Any thread process need to block to relinquish control so that other process can run. And that is a must.

Static and Dynamic Sensitivity of a Method: We can sensitize a Method at the time of creation of the Method, what does that mean? That means that when that event is notified, the method will be called but how do we change the sensitivity of a method? We can do using the next trigger call, but next trigger call can happen only from the context of a Method so we can change the sensitivity of a Method only from it own context and we may not be able to change it from outside. I am not sure if we can use some of the process sc_process_handle API to do that, at least I am not aware of.

Static and Dynamic Sensitivity of a Thread: Comparing with a Method people get a wrong notion that if a Thread is sensitized to an event then it will be called again when that method is notified. But that’s not true, once a thread returns then it can not come alive after that(That’s the reason for using an infinite loop in a thread function..). The sensitivity of a Thread is only for a convenience that if a wait() is called within a thread without any argument then the list of sensitive events will be considered. Nothing more that. I would advice to explicitly use the event name while calling wait to avoid such confusions

Creating Dynamic Processes

Bound by elaboration requirements there are certain data types in systemC that cannot be created after elaboration because that will change the architecture hierarchy. However in many cases we encounter situation where in we need to create process and events at the runtime. Thank fully that is allowed and very useful.
Steps for creating Thread and Method at runtime:
Declare the function body complying to signature
                            void thread_body(ARGS…)
                            void method_body(ARGS…)
Use the following syntax to create a thread
                            sc_spawn( sc_bind(&<class_name>::thread_body, this,ARGS…));

For creating a Method that is sensitized to an even lets say(method_event), use the following syntax
                           sc_spawn_options process_option;
                           process_option.spawn_method();
                           process_option.dont_initialize();
                           process_option.set_sensitivity(method_event_ptr); 
                           sc_spawn( sc_bind(&<class_name>::method_body,  this,<ARGS…>), "",&process_option);

Setting Options for sc_spawn:
If you have noticed the syntax carefully above we are creating the Thread without any option and creating the Method with some options. The code is just for illustrations and we can use the spawn options even for creating a dynamic thread. There are some other options which can be exercised to control the process creation.
Here are they:
                          sc_spawn_options process_option;
                          process_option.spawn_method(); // Create a Method.
                         process_option.dont_initialize();         //Do not run the process at time of creation
                         process_option.set_sensitivity(event_prt); //Sensitive to an event
                         process_option.set_sensitivity(port_ptr);   //Sensitive to a port
                        process_option.set_sensitivity(interface_ptr);
                       process_option.set_sensitivity(event_finder_ptr);

One Important Note: If you are using dynamic process in you code, then compile systemC as well as your code with SC_INCLUDE_DYNAMIC_PROCESSES macro.

Process Control

Process control APIs are newly implemented in systemC 2.3. They are explained in “SystemC 2011 New Features.pdf” available as part of systemC 2.3 official download from Accellera website. The main point I want make here is that for any components derived out of sc_object we can check out the type of sc_object and then create a handle of sc_process_handle class by calling its constructor that takes sc_object as the parameter.

                        sc_object* obj = sc_find_object("foo.foobar");
                        sc_process_handle h = sc_process_handle(obj);

We can also call sc_get_current_process_handle() API from a thread or a Method context to get the process handle. Once we get the process handle then we can call APIs such as suspend, reset, kill,disable on it to control the process. Read “SystemC 2011 New Features.pdf” presentation from systemC package to get more details on it.

Conclusion

In this post I have tired to share my personal learning about systemC processes some of which were not clearly described in the systemC LRM. Hope it helps few people who may be facing some issue due to not knowing these details.
- Sanjay

No comments:

Post a Comment