Skip to content

Commit 97c6717

Browse files
Add helper to run on specific context, and use to make b_transport and nb_transport thread safe, other tlm APIs are NOT handled
Signed-off-by: Mark Burton <mburton@quicinc.com>
1 parent 7c64b55 commit 97c6717

File tree

2 files changed

+287
-1
lines changed

2 files changed

+287
-1
lines changed

src/sysc/utils/sc_on_context.h

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
/*****************************************************************************
2+
3+
Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
4+
more contributor license agreements. See the NOTICE file distributed
5+
with this work for additional information regarding copyright ownership.
6+
Accellera licenses this file to you under the Apache License, Version 2.0
7+
(the "License"); you may not use this file except in compliance with the
8+
License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15+
implied. See the License for the specific language governing
16+
permissions and limitations under the License.
17+
18+
*****************************************************************************/
19+
20+
/*****************************************************************************
21+
22+
sc_on_context.h -- Job handler to run on context
23+
24+
Original Author: Mark burton
25+
26+
CHANGE LOG AT END OF FILE
27+
*****************************************************************************/
28+
29+
#ifndef SC_ON_CONTEXT_H
30+
#define SC_ON_CONTEXT_H
31+
32+
#include <thread>
33+
#include <mutex>
34+
#include <condition_variable>
35+
#include <queue>
36+
#include <future>
37+
38+
#include <sysc/kernel/sc_simcontext.h>
39+
#include <sysc/kernel/sc_event.h>
40+
41+
namespace sc_core {
42+
43+
class sc_on_context : public sc_core::sc_module
44+
{
45+
protected:
46+
class AsyncJob
47+
{
48+
public:
49+
using Ptr = std::shared_ptr<AsyncJob>;
50+
51+
private:
52+
std::packaged_task<void()> m_task;
53+
54+
bool m_cancelled = false;
55+
56+
void run_job() { m_task(); }
57+
58+
public:
59+
AsyncJob(std::function<void()>&& job): m_task(job) {}
60+
61+
AsyncJob(std::function<void()>& job): m_task(job) {}
62+
63+
AsyncJob() = delete;
64+
AsyncJob(const AsyncJob&) = delete;
65+
66+
void operator()() { run_job(); }
67+
68+
/**
69+
* @brief Cancel a job
70+
*
71+
* @details Cancel a job by setting m_cancelled to true and by
72+
* resetting the task. Any waiter will then be unblocked immediately.
73+
*/
74+
void cancel()
75+
{
76+
m_cancelled = true;
77+
m_task.reset();
78+
}
79+
80+
void wait()
81+
{
82+
auto future = m_task.get_future();
83+
84+
future.wait();
85+
86+
if (!m_cancelled) {
87+
future.get();
88+
}
89+
}
90+
91+
bool is_cancelled() const { return m_cancelled; }
92+
};
93+
94+
95+
sc_core::sc_simcontext *m_simc=nullptr;
96+
97+
/* Async job queue */
98+
std::queue<AsyncJob::Ptr> m_async_jobs;
99+
AsyncJob::Ptr m_running_job;
100+
std::mutex m_async_jobs_mutex;
101+
102+
sc_event m_jobs_handler_event; // NB this event must be threadsafe.
103+
std::atomic<bool> running = true;
104+
105+
// Process inside a thread incase the job calls wait
106+
void jobs_handler()
107+
{
108+
m_simc=sc_core::sc_get_curr_simcontext();
109+
110+
std::unique_lock<std::mutex> lock(m_async_jobs_mutex);
111+
running = true;
112+
for (; running;) {
113+
while (!m_async_jobs.empty()) {
114+
m_running_job = m_async_jobs.front();
115+
m_async_jobs.pop();
116+
117+
lock.unlock();
118+
sc_core::sc_unsuspendable(); // a wait in the job will cause systemc time to advance
119+
(*m_running_job)();
120+
sc_core::sc_suspendable();
121+
lock.lock();
122+
123+
m_running_job.reset();
124+
}
125+
lock.unlock();
126+
wait(m_jobs_handler_event);
127+
lock.lock();
128+
}
129+
SC_REPORT_WARNING("sc_on_context", "Stopped");
130+
sc_core::sc_stop();
131+
}
132+
133+
void cancel_pendings_locked()
134+
{
135+
while (!m_async_jobs.empty()) {
136+
m_async_jobs.front()->cancel();
137+
m_async_jobs.pop();
138+
}
139+
}
140+
141+
public:
142+
sc_on_context(const sc_core::sc_module_name& n = sc_core::sc_module_name("sc_on_ctx"))
143+
: sc_module(n)
144+
{
145+
SC_THREAD(jobs_handler);
146+
}
147+
148+
/**
149+
* @brief Cancel all pending jobs
150+
*
151+
* @detail Cancel all the pending jobs. The callers will be unblocked
152+
* if they are waiting for the job.
153+
*/
154+
void cancel_pendings()
155+
{
156+
std::lock_guard<std::mutex> lock(m_async_jobs_mutex);
157+
158+
cancel_pendings_locked();
159+
}
160+
161+
/**
162+
* @brief Cancel all pending and running jobs
163+
*
164+
* @detail Cancel all the pending jobs and the currently running job.
165+
* The callers will be unblocked if they are waiting for the
166+
* job. Note that if the currently running job is resumed, the
167+
* behaviour is undefined. This method is meant to be called
168+
* after simulation has ended.
169+
*/
170+
void cancel_all()
171+
{
172+
std::lock_guard<std::mutex> lock(m_async_jobs_mutex);
173+
174+
cancel_pendings_locked();
175+
176+
if (m_running_job) {
177+
m_running_job->cancel();
178+
m_running_job.reset();
179+
}
180+
}
181+
void stop()
182+
{
183+
running = false;
184+
m_jobs_handler_event.notify();
185+
}
186+
187+
void end_of_simulation()
188+
{
189+
running = false;
190+
cancel_all();
191+
}
192+
193+
void fork(std::function<void()> job_entry) { run(job_entry, false); }
194+
195+
/**
196+
* @brief Run a job on the SystemC kernel thread
197+
*
198+
* @param[in] job_entry The job to run
199+
* @param[in] wait If true, wait for job completion
200+
*
201+
* @return true if the job has been succesfully executed or if `wait`
202+
* was false, false if it has been cancelled (see
203+
* `Sc_on_context::cancel_all`).
204+
*/
205+
bool run(std::function<void()> job_entry, bool wait = true)
206+
{
207+
if (!running) return false;
208+
if (is_on_sysc()) {
209+
job_entry();
210+
return true;
211+
} else {
212+
AsyncJob::Ptr job(new AsyncJob(job_entry));
213+
214+
{
215+
std::lock_guard<std::mutex> lock(m_async_jobs_mutex);
216+
m_async_jobs.push(job);
217+
}
218+
219+
m_jobs_handler_event.notify();
220+
221+
if (wait) {
222+
/* Wait for job completion */
223+
try {
224+
job->wait();
225+
} catch (std::runtime_error const& e) {
226+
/* Report unknown runtime errors, without causing a futher excetion */
227+
auto old = sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR,
228+
sc_core::SC_LOG | sc_core::SC_DISPLAY);
229+
SC_REPORT_ERROR(
230+
"Sc_on_context",
231+
("Run on systemc received a runtime error from job: " + std::string(e.what())).c_str());
232+
sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, old);
233+
stop();
234+
return false;
235+
} catch (const std::exception& exc) {
236+
if (sc_core::sc_report_handler::get_count(sc_core::SC_ERROR) == 0) {
237+
/* Report exceptions that were not caused by SC_ERRORS (which have already been reported)*/
238+
auto old = sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR,
239+
sc_core::SC_LOG | sc_core::SC_DISPLAY);
240+
SC_REPORT_ERROR(
241+
"Sc_on_context",
242+
("Run on systemc received an exception from job: " + std::string(exc.what())).c_str());
243+
sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, old);
244+
}
245+
stop();
246+
return false;
247+
} catch (...) {
248+
auto old = sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR,
249+
sc_core::SC_LOG | sc_core::SC_DISPLAY);
250+
SC_REPORT_ERROR("Sc_on_context", "Run on systemc received an unknown exception from job");
251+
sc_core::sc_report_handler::set_actions(sc_core::SC_ERROR, old);
252+
stop();
253+
return false;
254+
}
255+
256+
return !job->is_cancelled();
257+
}
258+
259+
return true;
260+
}
261+
}
262+
263+
/**
264+
* @return Whether we are on the right SystemC context
265+
*/
266+
bool is_on_sysc() const { return sc_core::sc_get_curr_simcontext()==m_simc; }
267+
};
268+
}
269+
#endif // SC_ON_CONTEXT_H

src/tlm_utils/simple_target_socket.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <tlm>
3838
#include "tlm_utils/convenience_socket_bases.h"
3939
#include "tlm_utils/peq_with_get.h"
40+
#include "sysc/utils/sc_on_context.h"
4041

4142
namespace tlm_utils {
4243

@@ -174,6 +175,8 @@ class simple_target_socket_b
174175
class fw_process : public tlm::tlm_fw_transport_if<TYPES>,
175176
public tlm::tlm_mm_interface
176177
{
178+
sc_core::sc_simcontext *m_simc=sc_core::sc_get_curr_simcontext(); // Consider where the socket is constructed to be it's owning simcontext.
179+
sc_core::sc_on_context m_on_ctx;
177180
public:
178181
typedef sync_enum_type (MODULE::*NBTransportPtr)(transaction_type&,
179182
phase_type&,
@@ -258,6 +261,15 @@ class simple_target_socket_b
258261
// forward call
259262
sc_assert(m_mod);
260263
return (m_mod->*m_nb_transport_ptr)(trans, phase, t);
264+
265+
if (!m_on_ctx.is_on_sysc())
266+
{
267+
sync_enum_type tmp;
268+
m_on_ctx.run([this, &tmp, &trans, &phase, &t](){ tmp=(m_mod->*m_nb_transport_ptr)(trans, phase, t);});
269+
return tmp;
270+
} else {
271+
return (m_mod->*m_nb_transport_ptr)(trans, phase, t);
272+
}
261273
}
262274

263275
// nb->b conversion
@@ -300,7 +312,12 @@ class simple_target_socket_b
300312
if (m_b_transport_ptr) {
301313
// forward call
302314
sc_assert(m_mod);
303-
(m_mod->*m_b_transport_ptr)(trans, t);
315+
if (!m_on_ctx.is_on_sysc()) {
316+
m_on_ctx.run(
317+
[this, &trans, &t]() { (m_mod->*m_b_transport_ptr)(trans, t); });
318+
} else {
319+
(m_mod->*m_b_transport_ptr)(trans, t);
320+
}
304321
return;
305322
}
306323

0 commit comments

Comments
 (0)