16
16
* a time. Check the docs for more info.
17
17
*/
18
18
19
- #include <pthread.h>
20
19
#include <stdbool.h>
21
- #include <unistd.h>
22
20
#include <stdlib.h>
21
+ #include <unistd.h>
22
+ #include <pthread.h>
23
+ #include <semaphore.h>
24
+ #include <errno.h>
25
+ #include "nsi_utils.h"
23
26
#include "nce_if.h"
24
27
#include "nsi_safe_call.h"
25
28
26
29
struct nce_status_t {
27
- /* Conditional variable to know if the CPU is running or halted/idling */
28
- pthread_cond_t cond_cpu ;
29
- /* Mutex for the conditional variable cond_cpu */
30
- pthread_mutex_t mtx_cpu ;
31
- /* Variable which tells if the CPU is halted (1) or not (0) */
30
+ sem_t sem_sw ; /* Semaphore to hold the CPU/SW thread(s) */
31
+ sem_t sem_hw ; /* Semaphore to hold the HW thread */
32
32
bool cpu_halted ;
33
33
bool terminate ; /* Are we terminating the program == cleaning up */
34
34
void (* start_routine )(void );
@@ -48,6 +48,16 @@ struct nce_status_t {
48
48
49
49
extern void nsi_exit (int exit_code );
50
50
51
+ NSI_INLINE int nce_sem_rewait (sem_t * semaphore )
52
+ {
53
+ int ret ;
54
+
55
+ while ((ret = sem_wait (semaphore )) == EINTR ) {
56
+ /* Restart wait if we were interrupted */
57
+ }
58
+ return ret ;
59
+ }
60
+
51
61
/*
52
62
* Initialize an instance of the native simulator CPU emulator
53
63
* and return a pointer to it.
@@ -65,8 +75,8 @@ void *nce_init(void)
65
75
this -> cpu_halted = true;
66
76
this -> terminate = false;
67
77
68
- NSI_SAFE_CALL (pthread_cond_init (& this -> cond_cpu , NULL ));
69
- NSI_SAFE_CALL (pthread_mutex_init (& this -> mtx_cpu , NULL ));
78
+ NSI_SAFE_CALL (sem_init (& this -> sem_sw , 0 , 0 ));
79
+ NSI_SAFE_CALL (sem_init (& this -> sem_hw , 0 , 0 ));
70
80
71
81
return (void * )this ;
72
82
}
@@ -104,13 +114,9 @@ void nce_terminate(void *this_arg)
104
114
} else if (this -> terminate == false) {
105
115
106
116
this -> terminate = true;
107
-
108
- NSI_SAFE_CALL (pthread_mutex_lock (& this -> mtx_cpu ));
109
-
110
117
this -> cpu_halted = true;
111
118
112
- NSI_SAFE_CALL (pthread_cond_broadcast (& this -> cond_cpu ));
113
- NSI_SAFE_CALL (pthread_mutex_unlock (& this -> mtx_cpu ));
119
+ NSI_SAFE_CALL (sem_post (& this -> sem_hw ));
114
120
115
121
while (1 ) {
116
122
sleep (1 );
@@ -123,56 +129,15 @@ void nce_terminate(void *this_arg)
123
129
/* LCOV_EXCL_STOP */
124
130
}
125
131
126
- /**
127
- * Helper function which changes the status of the CPU (halted or running)
128
- * and waits until somebody else changes it to the opposite
129
- *
130
- * Both HW and SW threads will use this function to transfer control to the
131
- * other side.
132
- *
133
- * This is how the idle thread halts the CPU and gets halted until the HW models
134
- * raise a new interrupt; and how the HW models awake the CPU, and wait for it
135
- * to complete and go to idle.
136
- */
137
- static void change_cpu_state_and_wait (struct nce_status_t * this , bool halted )
138
- {
139
- NSI_SAFE_CALL (pthread_mutex_lock (& this -> mtx_cpu ));
140
-
141
- NCE_DEBUG ("Going to halted = %d\n" , halted );
142
-
143
- this -> cpu_halted = halted ;
144
-
145
- /* We let the other side know the CPU has changed state */
146
- NSI_SAFE_CALL (pthread_cond_broadcast (& this -> cond_cpu ));
147
-
148
- /* We wait until the CPU state has been changed. Either:
149
- * we just awoke it, and therefore wait until the CPU has run until
150
- * completion before continuing (before letting the HW models do
151
- * anything else)
152
- * or
153
- * we are just hanging it, and therefore wait until the HW models awake
154
- * it again
155
- */
156
- while (this -> cpu_halted == halted ) {
157
- /* Here we unlock the mutex while waiting */
158
- pthread_cond_wait (& this -> cond_cpu , & this -> mtx_cpu );
159
- }
160
-
161
- NCE_DEBUG ("Awaken after halted = %d\n" , halted );
162
-
163
- NSI_SAFE_CALL (pthread_mutex_unlock (& this -> mtx_cpu ));
164
- }
165
-
166
132
/*
167
133
* Helper function that wraps the SW start_routine
168
134
*/
169
135
static void * sw_wrapper (void * this_arg )
170
136
{
171
137
struct nce_status_t * this = (struct nce_status_t * )this_arg ;
172
138
173
- /* Ensure nce_boot_cpu has reached the cond loop */
174
- NSI_SAFE_CALL (pthread_mutex_lock (& this -> mtx_cpu ));
175
- NSI_SAFE_CALL (pthread_mutex_unlock (& this -> mtx_cpu ));
139
+ /* Ensure nce_boot_cpu is blocked in nce_wake_cpu() */
140
+ NSI_SAFE_CALL (nce_sem_rewait (& this -> sem_sw ));
176
141
177
142
#if (NCE_DEBUG_PRINTS )
178
143
pthread_t sw_thread = pthread_self ();
@@ -198,25 +163,14 @@ void nce_boot_cpu(void *this_arg, void (*start_routine)(void))
198
163
{
199
164
struct nce_status_t * this = (struct nce_status_t * )this_arg ;
200
165
201
- NSI_SAFE_CALL (pthread_mutex_lock (& this -> mtx_cpu ));
202
-
203
- this -> cpu_halted = false;
204
166
this -> start_routine = start_routine ;
205
167
206
168
/* Create a thread for the embedded SW init: */
207
169
pthread_t sw_thread ;
208
170
209
171
NSI_SAFE_CALL (pthread_create (& sw_thread , NULL , sw_wrapper , this_arg ));
210
172
211
- /* And we wait until the embedded OS has send the CPU to sleep for the first time */
212
- while (this -> cpu_halted == false) {
213
- pthread_cond_wait (& this -> cond_cpu , & this -> mtx_cpu );
214
- }
215
- NSI_SAFE_CALL (pthread_mutex_unlock (& this -> mtx_cpu ));
216
-
217
- if (this -> terminate ) {
218
- nsi_exit (0 );
219
- }
173
+ nce_wake_cpu (this_arg );
220
174
}
221
175
222
176
/*
@@ -236,7 +190,12 @@ void nce_halt_cpu(void *this_arg)
236
190
nsi_print_error_and_exit ("Programming error on: %s " ,
237
191
"This CPU was already halted\n" );
238
192
}
239
- change_cpu_state_and_wait (this , true);
193
+ this -> cpu_halted = true;
194
+
195
+ NSI_SAFE_CALL (sem_post (& this -> sem_hw ));
196
+ NSI_SAFE_CALL (nce_sem_rewait (& this -> sem_sw ));
197
+
198
+ NCE_DEBUG ("CPU awaken, HW thread held\n" );
240
199
}
241
200
242
201
/*
@@ -255,7 +214,13 @@ void nce_wake_cpu(void *this_arg)
255
214
nsi_print_error_and_exit ("Programming error on: %s " ,
256
215
"This CPU was already awake\n" );
257
216
}
258
- change_cpu_state_and_wait (this , false);
217
+
218
+ this -> cpu_halted = false;
219
+
220
+ NSI_SAFE_CALL (sem_post (& this -> sem_sw ));
221
+ NSI_SAFE_CALL (nce_sem_rewait (& this -> sem_hw ));
222
+
223
+ NCE_DEBUG ("CPU went to sleep, HW continues\n" );
259
224
260
225
/*
261
226
* If while the SW was running it was decided to terminate the execution
0 commit comments