Event generation in systemc - systemc

I am trying to understand event generation in systemc. I found that it depends on the order of thread registration in constructor.
#include <systemc.h>
SC_MODULE(EventTest){
sc_event ev1;
void p1(){
ev1.notify();
ev1.notify(0, SC_NS);
}
void p2(){
wait(ev1);
cout<<"ev1 is activated at "<<sc_time_stamp()<<endl;
wait(ev1);
cout<<"ev1-2 is activated at "<<sc_time_stamp()<<endl;
}
SC_CTOR(EventTest){
SC_THREAD(p1);
SC_THREAD(p2);
}
};
int sc_main(int argc, char *argv[]){
EventTest d("d");
sc_start();
return 1;
}
output : ev1 is activated at 0 s
if i change the in SC_CTOR to >
SC_THREAD(p2);
SC_THREAD(p1);
then output is >
ev1 is activated at 0 s
ev1-2 is activated at 0 s
Please someone tell how does the order of registration affect event generation?

Your code is composed of two SystemC processes (SystemC threads or methods are called processes), which are both SystemC threads : p1 and p2. They will be executed both during the first delta cycle of the simulation. However, SystemC standard has no guarantee about the running order of processes in a same delta cycle.
If p1 is executed first, it does an untimed notification to the event ev1. See 5.10.6 section of IEEE Std 1666-2011.
A call to member function notify with an empty argument list shall create an immediate notification. Any and all process instances sensitive to the event shall be made runnable before control is returned from function notify, with the exception of the currently executing process instance, which shall not be made runnable due to an immediate notification regardless of its static or dynamic sensitivity
However, nothing is waiting the event. The notification does nothing.
Then p1 does a timed notification. In that case, the behaviour is
different :
A call to function notify with an argument that represents a non-zero time shall create a timed notification at the given time, expressed relative to the simulation time when function notify is called. In other words, the value of the time argument is added to the current simulation time to determine the time at which the event will be notified. The time argument shall not be negative.
NOTE—In the case of a delta notification, all processes that are sensitive to the event in the delta notification phase will be made runnable in the subsequent evaluation phase. In the case of a timed notification, all processes sensitive to the event at the time the event occurs will be made runnable at the time, which will be a future simulation time.
Finally, the process p1 ends and p2 is executed. The first wait will suspend the process. The pending notification of the event is then propagated and will unlock the process p2. Then, the process p2 will execute the second wait and suspend again. As nothing will notify the event, the simulation will end.
If p2 is executed first, it will suspend on the first wait. Then, p1 will be executed. It will do a first notification. As p2 was waiting the notification, p1 will be suspended (immediate notification) and p2 will continue and move on until the second wait. p2 is suspended. Then, p1 continue. It does the timed notification and the process ends. As p2 was waiting for the notification and it happened, p2 execution continue and the process ends. The simulation ends too.
Finally, in your two cases, p1 is always executed after p2 with the SystemC implementation you used. With another implementation of SystemC, it could be the reverse. You should consider they're executed in parallel at the same simulation time. In that case, both orders are right.
In the end, it means that your code can lead to a non deterministic behaviour.

You can add to the code below prints:
6 void p1(){
7 cout << "P1 is run" << endl;
8 ev1.notify();
9 ev1.notify(0, SC_NS);
10 cout << "P1 is end" << endl;
11 }
12
13 void p2(){
14 cout << "P2 is run" << endl;
15 wait(ev1);
16 cout<<"ev1 is activated at "<<sc_time_stamp()<<endl;
17 wait(ev1);
18 cout<<"ev1-2 is activated at "<<sc_time_stamp()<<endl;
19 cout << "P2 is end" << endl;
20 }
IEEE Standard for Standard SystemC® Language Reference Manual says: "When a method process is triggered, the associated function executes from beginning to end, then
returns control to the kernel."
If p1 is triggered first, ev1 is notified immediately that results in nothing, then ev1 is notified after 0 NS. p2 is then triggered that results in ev1 is activated at 0 s only:
P1 is run
P1 is end
P2 is run
ev1 is activated at 0 s
If p2 is triggered first, ev1 is waited, then p1 is triggered. ev1 is notified immediately that results in ev1 is activated at 0 s. ev1 is notified after 0 NS that results in ev1-2 is activated at 0 s:
P2 is run
P1 is run
P1 is end
ev1 is activated at 0 s
ev1-2 is activated at 0 s
P2 is end

Related

Is it guaranteed that only one callback is executed when the wait condition is true?

This is regarding the statement WAIT FOR ASYNCHRONOUS TASKS and the corresponding part of the documentation:
If the result of log_exp is false and there is an asynchronous function call with callback routine, the program waits until a callback routine of a previous function (called asynchronously) has been executed and then checks the logical expression again:
Let's say I spawn 4 tasks, each reducing availability attribute by one, reaching 0. In the callback, they increase the availability attribute by one.
Now when I reach WAIT FOR ASYNCHRONOUS TASKS UNTIL availability > 0 UP TO 6000 SECONDS. the program waits until the counter is increased by a callback.
Question: When the logical expression is checked again, is it guaranteed that the order is
callback->check->callback->check?
Or could it be that availability is e.g. already 3, since it did
callback->callback->callback->check?
It works as per documentation.
WAIT -> CALLBACK -> CHECK , WAIT -> CALLBACK -> CHECK,
until wait condition is true or no more outstanding Callbacks are open.
It is important that the Callback form/method has finished before the check is performed as that routine is responsible for changing the variable/s in the WAIT UNTIL Condition.
An extract from the docu.
If the result of log_exp is false and there is an asynchronous
function call with callback routine, the program waits until a
callback routine of a previous function (called asynchronously) has
been executed and then checks the logical expression again:
If you are concerned about 2 callbacks occurring concurrently,
the callbacks are handled by the kernel sequentially.
There is no guarantee of order, just that the call backs are processed sequentially. Note the waiting program is only executed in 1 Work process at a time. From my tests, it is always the same Work process.

Is there progress in interested variable mechanism in process synchronization in operating system?

Two processes, P1 and P2, need to access a critical section of code. Consider the following synchronization construct used by the processes:
/* P1 */
while (true) {
wants1 = true;
while (wants2 == true);
/* Critical Section */
wants1 = false;
}
/* Remainder section */
/* P2 */
while (true) {
wants2 = true;
while (wants1 == true);
/* Critical Section */
wants2=false;
}
/* Remainder section */
Here, wants1 and wants2 are shared variables,
My question is to argue on PROGRESS.
A case can be possible when P1 wants to enter in the critical section and set its interested variable to true and checks whether the interested variable of P2 is true. Here, (consider )P2's interest variable to be True hence P1 will get stuck in the while loop and waits for P2 become uninterested. Since, P2 still stuck in the while loop waiting for the P1’s interested variable to become false. Therefore, both the processes are waiting for each other and none of them is getting into the critical section..
DEFINITION of progress:If no process is executing in its critical section and some processes wish to enter their critical sections, then only those processes that are not executing in their remainder section can participate in deciding which will enter its critical section next, AND THIS SELECTION CANNOT BE POSTPONED INDEFINITELY.
SO WHY EVERYWHERE IT IS WRITTEN PROGRESS IS THERE?
In the case when both wants1 and wants2 are true it will indefinitely be postponed then why progress is there?

Difference in timing while getting values from ROM or RAM

So I am having a hard time understanding the time it takes to get values from ram or rom in vhdl. To insert data into ram I know happens on the rising edge of the clock and takes one clock such as the example I have, but in terms of getting data out does it take one clock to get the data from memory and then get then another clock cycle to get the data to output meaning it takes 2 clock cycles to get data?
process(clk)
begin
if(rising_edge(clk)) then
if(write_en = '1') then
mem(to_integer(unsigned(address))) <= incoming_data;--insert data
end if;
end if;
end process;
out_data <= mem(to_integer(unsigned(address))); -- takes 2 clock cycles to get data ?
No, it takes 1 clock cycle:
In your code you have two concurrent processes. One is explicit:
process(clk)
begin
if(rising_edge(clk)) then
if(write_en = '1') then
mem(to_integer(unsigned(address))) <= incoming_data;--insert data
end if;
end if;
end process;
The other is implicit; it is a concurrent signal assignment:
out_data <= mem(to_integer(unsigned(address))); -- takes 2 clock cycles to get data ?
The concurrent signal assignment is exactly equivalent to this:
process(address, mem)
begin
out_data <= mem(to_integer(unsigned(address))); -- takes 2 clock cycles to get data ?
end process;
In other words, it is equivalent to a process with address and mem in the sensitivity list. Any concurrent signal assignment is equivalent to a process with all the inputs in the sensitivity list. An input to a concurrent signal assignment is any signal on the right hand side of the signal assignment operator (<=). So, you get a sensitivity list for free and that is an advantage of using concurrent signal assignments: you cannot accidentally miss out a signal from the sensitivity list, because the compiler creates it for you.
So, lets consider what happens when each process is executed. The first process has just the signal clk in its sensitivity list, so the process executes whenever there is a change (an event) on clk. If this change is not a rising edge then the rising_edge function returns FALSE and the process immediately suspends. If this change is a rising edge then the rising_edge function returns TRUE and if the expression write_en = '1' is also TRUE then this line gets executed:
mem(to_integer(unsigned(address))) <= incoming_data;--insert data
The effect of this line is to put an event on the event queue to drive the correct value of mem on the next delta cycle (assuming there is some change to the signal mem as a result). The event queue is the simulator's "to do" list; a delta cycle is one iteration of the simulator; the next iteration will occur once all the processes that are executing in the current iteration suspend.
So, the next iteration cycle occurs and the signal mem gets its new value. The signal mem is in the implicit sensitivity list of the second (implicit) process (the concurrent signal assignment). So, this second process starts executing and the line with the signal assignment to out_data is executed and (as with the executing of any line containing a signal assignment) an event is put on the event queue to drive the target signal - out_data in this case - to a new value (again assuming the value should change).
So, the change to the signal out_data always occurs one delta cycle after a change on the signal mem. We've already established that the signal mem changes one delta cycle after any rising edge on the signal clk, so we can see that the signal out_data changes two delta cycles after any rising edge on the signal clk.
Whilst it is vital to be aware of delta cycles when writing VHDL, we don't usually need to worry about them if we adopt a good, conventional style. So, we can just say that the signal out_data changes on any rising edge of the signal clk or, in other words, there is a delay of one clock cycle between any changes on the signals write_en, incoming_data or address and any corresponding change on the signal out_data.

After a process calls syscall wait(), who will wake it up?

I have a general idea that a process can be in ready_queue where CPU selects candidate to run next. And there are these other queues on which a process waits for (broadly speaking) events. I know from OS courses long time ago that there are wait queues for IO and interrupts. My questions are:
There are many events a process can wait on. Is there a wait queue corresponding to each such event?
Are these wait queues created/destroyed dynamically? If so, which kernel module is responsible for managing these queues? The scheduler? Are there any predefined queues that will always exist?
To eventually get a waiting process off a wait queue, does the kernel have a way of mapping from each actual event (either hardware or software) to the wait queue, and then remove ALL processes on that queue? If so, what mechanisms does a kernel employ?
To give an example:
....
pid = fork();
if (pid == 0) { // child process
// Do something for a second;
}
else { // parent process
wait(NULL);
printf("Child completed.");
}
....
wait(NULL) is a blocking system call. I want to know the rest of the journey the parent process goes through. My take of the story line is as follows, PLEASE correct me if I miss crucial steps or if I am completely wrong:
Normal system call setup through libc runtime. Now parent process is in kernel mode, ready to execute whatever is in wait() syscall.
wait(NULL) creates a wait queue where the kernel can later find this queue.
wait(NULL) puts the parent process onto this queue, creates an entry in some map that says "If I (the kernel) ever receives an software interrupt, signal, or whatever that indicates that the child process is finished, scheduler should come look at this wait queue".
Child process finishes and kernel somehow noticed this fact. Kernel context switches to scheduler, which looks up in the map to find the wait queue where the parent process is on.
Scheduler moves the parent process to ready queue, does its magic and sometime later the parent process is finally selected to run.
Parent process is still in kernel mode, inside wait(NULL) syscall. Now the main job of rest of the syscall is to exit kernel mode and eventually return the parent process to user land.
The process continues its journey on the next instruction, and may later be waiting on other wait queues until it finishes.
PS: I am hoping to know the inner workings of the OS kernel, what stages a process goes through in the kernel and how the kernel interact and manipulate these processes. I do know the semantics and the contract of the wait() Syscall APIs and that is not what I want to know from this question.
Let's explore the kernel sources. First of all, it seems all the
various wait routines (wait, waitid, waitpid, wait3, wait4) end up in the
same system call, wait4. These days you can find system calls in the
kernel by looking for the macros SYSCALL_DEFINE1 and so, where the number
is the number of parameters, which for wait4 is coincidentally 4. Using the
google-based freetext search in the Free Electrons Linux Cross
Reference we eventually find the definition:
1674 SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr,
1675 int, options, struct rusage __user *, ru)
Here the macro seems to split each parameter into its type and name. This
wait4 routine does some parameter checking, copies them into a wait_opts
structure, and calls do_wait(), which is a few lines up in the same file:
1677 struct wait_opts wo;
1705 ret = do_wait(&wo);
1551 static long do_wait(struct wait_opts *wo)
(I'm missing out lines in these excerpts as you can tell by the
non-consecutive line numbers).
do_wait() sets another field of the structure to the name of a function,
child_wait_callback() which is a few lines up in the same file. Another
field is set to current. This is a major "global" that points to
information held about the current task:
1558 init_waitqueue_func_entry(&wo->child_wait, child_wait_callback);
1559 wo->child_wait.private = current;
The structure is then added to a queue specifically designed for a process
to wait for SIGCHLD signals, current->signal->wait_chldexit:
1560 add_wait_queue(&current->signal->wait_chldexit, &wo->child_wait);
Let's look at current. It is quite hard to find its definition as it
varies per architecture, and following it to find the final structure is a
bit of a rabbit warren. Eg current.h
6 #define get_current() (current_thread_info()->task)
7 #define current get_current()
then thread_info.h
163 static inline struct thread_info *current_thread_info(void)
165 return (struct thread_info *)(current_top_of_stack() - THREAD_SIZE);
55 struct thread_info {
56 struct task_struct *task; /* main task structure */
So current points to a task_struct, which we find in sched.h
1460 struct task_struct {
1461 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
1659 /* signal handlers */
1660 struct signal_struct *signal;
So we have found current->signal out of current->signal->wait_chldexit,
and the struct signal_struct is in the same file:
670 struct signal_struct {
677 wait_queue_head_t wait_chldexit; /* for wait4() */
So the add_wait_queue() call we had got to above refers to this
wait_chldexit structure of type wait_queue_head_t.
A wait queue is simply an initially empty, doubly-linked list of structures that contain a
struct list_head types.h
184 struct list_head {
185 struct list_head *next, *prev;
186 };
The call add_wait_queue()
wait.c
temporarily locks the structure and via an inline function
wait.h
calls list_add() which you can find in
list.h.
This sets the next and prev pointers appropriately to add the new item on
the list.
An empty list has the two pointers pointing at the list_head structure.
After adding the new entry to the list, the wait4() system call sets a
flag that will remove the process from the runnable queue on the next
reschedule and calls do_wait_thread():
1573 set_current_state(TASK_INTERRUPTIBLE);
1577 retval = do_wait_thread(wo, tsk);
This routine calls wait_consider_task() for each child of the process:
1501 static int do_wait_thread(struct wait_opts *wo, struct task_struct *tsk)
1505 list_for_each_entry(p, &tsk->children, sibling) {
1506 int ret = wait_consider_task(wo, 0, p);
which goes very deep but in fact is just trying to see if any child already
satisfies the syscall, and we can return with the data immediately. The
interesting case for you is when nothing is found, but there are still running
children. We end up calling schedule(), which is when the process gives
up the cpu and our system call "hangs" for a future event.
1594 if (!signal_pending(current)) {
1595 schedule();
1596 goto repeat;
1597 }
When the process is woken up, it will continue with the code after
schedule() and again go through all the children to see if the wait
condition is satisfied, and probably return to the caller.
What wakes up the process to do that? A child dies and generates a SIGCHLD
signal.
In signal.c
do_notify_parent() is called by a process as it dies:
1566 * Let a parent know about the death of a child.
1572 bool do_notify_parent(struct task_struct *tsk, int sig)
1656 __wake_up_parent(tsk, tsk->parent);
__wake_up_parent() calls __wake_up_sync_key() and uses exactly the
wait_chldexit wait queue we set up previously.
exit.c
1545 void __wake_up_parent(struct task_struct *p, struct task_struct *parent)
1547 __wake_up_sync_key(&parent->signal->wait_chldexit,
1548 TASK_INTERRUPTIBLE, 1, p);
I think we should stop there, as wait() is clearly one of the more
complex examples of a system call and the use of wait queues. You can find
a simpler presentation of the mechanism in this 3 page Linux Journal
article from 2005. Many things
have changed, but the principle is explained. You might also buy the books
"Linux Device Drivers" and "Linux Kernel Development", or check out the
earlier editions of these that can be found online.
For the "Anatomy Of A System Call" on the way from user space to the kernel
you might read these lwn articles.
Wait queues are heavily used throughout the kernel whenever a task,
needs to wait for some condition. A grep through the kernel sources finds
over 1200 calls of init_waitqueue_head() which is how you initialise a
waitqueue you have dynamically created by simply kmalloc()-ing the space
to hold the structure.
A grep for the DECLARE_WAIT_QUEUE_HEAD() macro finds over 150 uses of
this declaration of a static waitqueue structure. There is no intrinsic
difference between these. A driver, for example, can choose either method
to create a wait queue, often depending on whether it can manage
many similar devices, each with their own queue, or is only expecting one device.
No central code is responsible for these queues, though there is common
code to simplify their use. A driver, for example, might create an empty
wait queue when it is installed and initialised. When you use it to read data from some
hardware, it might start the read operation by writing directly into the
registers of the hardware, then queue an entry (for "this" task, i.e. current) on its wait queue to give up
the cpu until the hardware has the data ready.
The hardware would then interrupt the cpu, and the kernel would call the
driver's interrupt handler (registered at initialisation). The handler code
would simply call wake_up() on the wait queue, for the kernel to
put all tasks on the wait queue back in the run queue.
When the task gets the cpu again, it continues where it left off (in
schedule()) and checks that the hardware has completed the operation, and
can then return the data to the user.
So the kernel is not responsible for the driver's wait queue, as it only
looks at it when the driver calls it to do so. There is no mapping from the
hardware interrupt to the wait queue, for example.
If there are several tasks on the same wait queue, there are variants of
the wake_up() call that can be used to wake up only 1 task, or all of
them, or only those that are in an interruptable wait (i.e. are designed to
be able to cancel the operation and return to the user in case of a
signal), and so on.
In order to wait for a child process to terminate, a parent process will just execute a wait() system call. This call will suspend the parent process until any of its child processes terminates, at which time the wait() call returns and the parent process can continue.
The prototype for the wait( call is:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
The return value from wait is the PID of the child process which terminated. The parameter to wait() is a pointer to a location which will receive the child's exit status value when it terminates.
When a process terminates it executes an exit() system call, either directly in its own code, or indirectly via library code. The prototype for the exit() call is:
#include <std1ib.h>
void exit(int status);
The exit() call has no return value as the process that calls it terminates and so couldn't receive a value anyway. Notice, however, that exit() does take a parameter value - status. As well as causing a waiting parent process to resume execution, exit() also returns the status parameter value to the parent process via the location pointed to by the wait() parameter.
In fact, wait() can return several different pieces of information via the value to which the status parameter points. Consequently, a macro is provided called WEXITSTATUS() (accessed via ) which can extract and return the child's exit status. The following code fragment shows its use:
#include <sys/wait.h>
int statval, exstat;
pid_t pid;
pid = wait(&statval);
exstat = WEXITSTATUS(statval);
In fact, the version of wait() that we have just seen is only the simplest version available under Linux. The new POSIX version is called waitpid. The prototype for waitpid() is:
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
where pid specifies what to wait for, status is the same as the simple wait() parameter and options allows you to specify that a call to waitpid() should not suspend the parent process if no child process is ready to report its exit status.
The various possibilities for the pid parameter are:
< -1 wait for a child whose PGID is -pid
-1 same behavior as standard wait()
0 wait for child whose PGID = PGID of calling process
> 0 wait for a child whose PID = pid
The standard wait() call is now redundant as the following waitpid() call is exactly equivalent:
#include <sys/wait.h>
int statval;
pid_t pid;
pid = waitpid(-1, &statval, 0);
It is possible for a child process which only executes for a very short time to terminate before its parent process has had the chance to wait() for it. In these circumstances the child process will enter a state, known as a zombie state, in which all its resources have been released back to the system except for its process data structure, which holds its exit status. When the parent eventually wait()s for the child, the exit status is delivered immediately and then the process data structure can also be released back to the system.

Semaphore wait() and signal()

I am going through process synchronization, and facing difficulty in understanding semaphore. So here is my doubt:
the source says that
" Semaphore S is an integer variable that is accessed through standard atomic operations i.e. wait() and signal().
It also provided basic definition of wait()
wait(Semaphore S)
{
while S<=0
; //no operation
S--;
}
Definition of signal()
signal(S)
{
S++;
}
Let the initial value of a semaphore be 1, and say there are two concurrent processes P0 and P1 which are not supposed to perform operations of their critical section simultaneously.
Now say P0 is in its critical section, so the Semaphore S must have value 0, now say P1 wants to enter its critical section so it executes wait(), and in wait() it continuously loops, now to exit from the loop the semaphore value must be incremented, but it may not be possible because according the source, wait() is an atomic operation and can't be interrupted and thus the process P0 can't call signal() in a single processor system.
I want to know, is the understanding i have so far is correct or not. and if correct then how come process P0 call signal() when process P1 is strucked in while loop?
I think the top-voted answer is inaccurate!
Operation wait() and signal() must be completely atomic; no two processes can execute wait() or signal() operation simultaneously because they are implemented in kernel and processes in kernel mode can not be preempted.
If several processes attempt a P(S) simultaneously, only one process will be allowed to proceed(non-preemptive kernel that is free of race condition).
for the above implementation to work preemption is necessary (preemptive kernel)
read about the atomicity of semaphore operations
http://personal.kent.edu/~rmuhamma/OpSystems/Myos/semaphore.htm
https://en.wikibooks.org/wiki/Operating_System_Design/Processes/Semaphores
I think it's an inaccuracy in your source. Atomic for the wait() operation means each iteration of it is atomic, meaning S-- is performed without interruption, but the whole operation is interruptible after each completion of S-- inside the while loop.
I don't think, keeping an infinite while loop inside the wait() operation is wise. I would go for Stallings' example;
void semWait(semaphore s){
s.count--;
if(s.count<0)
*place this process in s.queue and block this process
}
I think what the book means for the atomic operation is testing S<=0 to be true as well as S--. Just like testAndset() it mention before.
if both separate operations S<=0 and S-- are atomic but can be interrupt by other process, this method won't work.
imagine two process p0 and p1, if p0 want to enter the critical section and tested S<=0 to be true. and it was interrupted by p1 and tested S<=0 also be true. then both of the process will enter the critical section. And that's wrong.
the actual not atomic operation is inside the while loop, even if the while loop is empty, other process can still interrupt current one when S<=0 tested to be false, which enable other process can continue their work in critical section and release the lock.
however, I think the code from the book can not actually use in OS since I don't know how to make operations S<=0 to be true and S-- together atomic. more possible way to do that is put the S-- inside the while loop like SomeWittyUsername said.
When a task attempts to acquire a semaphore that is unavailable, the semaphore places the task onto a wait queue and puts the task to sleep.The processor is then free to execute other code.When the semaphore becomes available, one of the tasks on the wait queue is awakened so that it can then acquire the semaphore.
while S<=0
; //no operation This doesn't mean that the processor running this code. The process/task is blocked until it gets the semaphore.
i think ,
when process P1 is strucked in while loop it will be in the wait state.processor will switch over among the process p0 & p1 (context switching) so the priority goes to p0 and it call signal() and then s will be incremented by 1 and p0 exit from the section so process P1 can enter into critical section and can avoid the mutual exclusion