1/* 2 * Copyright 2018 Google, Inc. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer; 8 * redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution; 11 * neither the name of the copyright holders nor the names of its 12 * contributors may be used to endorse or promote products derived from 13 * this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 * Authors: Gabe Black 28 */ 29 30#include "systemc/core/process.hh" 31 32#include "systemc/core/event.hh" 33#include "systemc/core/port.hh" 34#include "systemc/core/scheduler.hh" 35#include "systemc/ext/core/messages.hh" 36#include "systemc/ext/core/sc_join.hh" 37#include "systemc/ext/core/sc_main.hh" 38#include "systemc/ext/core/sc_process_handle.hh" 39#include "systemc/ext/utils/sc_report_handler.hh" 40 41namespace sc_gem5 42{ 43 44class UnwindExceptionReset : public ::sc_core::sc_unwind_exception 45{ 46 public: 47 UnwindExceptionReset() { _isReset = true; } 48}; 49 50class UnwindExceptionKill : public ::sc_core::sc_unwind_exception 51{ 52 public: 53 UnwindExceptionKill() {} 54}; 55 56template <typename T> 57struct BuiltinExceptionWrapper : public ExceptionWrapperBase 58{ 59 public: 60 T t; 61 void throw_it() override { throw t; } 62}; 63 64BuiltinExceptionWrapper<UnwindExceptionReset> resetException; 65BuiltinExceptionWrapper<UnwindExceptionKill> killException; 66 67 68void 69Process::forEachKid(const std::function<void(Process *)> &work) 70{ 71 for (auto &kid: get_child_objects()) { 72 Process *p_kid = dynamic_cast<Process *>(kid); 73 if (p_kid) 74 work(p_kid); 75 } 76} 77 78void 79Process::suspend(bool inc_kids) 80{ 81 if (inc_kids) 82 forEachKid([](Process *p) { p->suspend(true); }); 83 84 if (!_suspended && !_terminated) { 85 _suspended = true; 86 _suspendedReady = scheduler.suspend(this); 87 88 if (procKind() != ::sc_core::SC_METHOD_PROC_ && 89 scheduler.current() == this) { 90 // This isn't in the spec, but Accellera says that a thread that 91 // self suspends should be marked ready immediately when it's 92 // resumed. 93 _suspendedReady = true; 94 scheduler.yield(); 95 } 96 } 97} 98 99void 100Process::resume(bool inc_kids) 101{ 102 if (inc_kids) 103 forEachKid([](Process *p) { p->resume(true); }); 104 105 if (_suspended && !_terminated) { 106 _suspended = false; 107 if (_suspendedReady) 108 scheduler.resume(this); 109 _suspendedReady = false; 110 } 111} 112 113void 114Process::disable(bool inc_kids) 115{ 116 if (inc_kids) 117 forEachKid([](Process *p) { p->disable(true); }); 118 119 if (!::sc_core::sc_allow_process_control_corners && 120 timeoutEvent.scheduled()) { 121 std::string message("attempt to disable a thread with timeout wait: "); 122 message += name(); 123 SC_REPORT_ERROR(sc_core::SC_ID_PROCESS_CONTROL_CORNER_CASE_, 124 message.c_str()); 125 } 126 127 if (!_terminated) 128 _disabled = true; 129} 130 131void 132Process::enable(bool inc_kids) 133{ 134 135 if (inc_kids) 136 forEachKid([](Process *p) { p->enable(true); }); 137 138 if (!_terminated) 139 _disabled = false; 140} 141 142void 143Process::kill(bool inc_kids) 144{ 145 if (::sc_core::sc_get_status() != ::sc_core::SC_RUNNING) { 146 SC_REPORT_ERROR(sc_core::SC_ID_KILL_PROCESS_WHILE_UNITIALIZED_, 147 name()); 148 } 149 150 // Propogate the kill to our children no matter what happens to us. 151 if (inc_kids) 152 forEachKid([](Process *p) { p->kill(true); }); 153 154 // If we're unwinding or terminated, ignore the kill request. 155 if (_isUnwinding || _terminated) 156 return; 157 158 // Update our state. 159 terminate(); 160 _isUnwinding = true; 161 162 // Make sure this process isn't marked ready 163 popListNode(); 164 165 // Inject the kill exception into this process if it's started. 166 if (!_needsStart) 167 injectException(killException); 168} 169 170void 171Process::reset(bool inc_kids) 172{ 173 if (::sc_core::sc_get_status() != ::sc_core::SC_RUNNING) { 174 SC_REPORT_ERROR(sc_core::SC_ID_RESET_PROCESS_WHILE_NOT_RUNNING_, 175 name()); 176 } 177 178 // Propogate the reset to our children no matter what happens to us. 179 if (inc_kids) 180 forEachKid([](Process *p) { p->reset(true); }); 181 182 // If we're already unwinding or terminated, ignore the reset request. 183 if (_isUnwinding || _terminated) 184 return; 185 186 // Clear suspended ready since we're about to run regardless. 187 _suspendedReady = false; 188 189 _resetEvent.notify(); 190 191 if (_needsStart) { 192 scheduler.runNow(this); 193 } else { 194 _isUnwinding = true; 195 injectException(resetException); 196 } 197} 198 199void 200Process::throw_it(ExceptionWrapperBase &exc, bool inc_kids) 201{ 202 if (::sc_core::sc_get_status() != ::sc_core::SC_RUNNING) 203 SC_REPORT_ERROR(sc_core::SC_ID_THROW_IT_WHILE_NOT_RUNNING_, name()); 204 205 if (inc_kids) 206 forEachKid([&exc](Process *p) { p->throw_it(exc, true); }); 207 208 if (_needsStart || _terminated || 209 procKind() == ::sc_core::SC_METHOD_PROC_) { 210 SC_REPORT_WARNING(sc_core::SC_ID_THROW_IT_IGNORED_, name()); 211 return; 212 } 213 214 injectException(exc); 215} 216 217void 218Process::injectException(ExceptionWrapperBase &exc) 219{ 220 excWrapper = &exc; 221 scheduler.runNow(this); 222}; 223 224void 225Process::syncResetOn(bool inc_kids) 226{ 227 if (inc_kids) 228 forEachKid([](Process *p) { p->syncResetOn(true); }); 229 230 _syncReset = true; 231} 232 233void 234Process::syncResetOff(bool inc_kids) 235{ 236 if (inc_kids) 237 forEachKid([](Process *p) { p->syncResetOff(true); }); 238 239 _syncReset = false; 240} 241 242void 243Process::signalReset(bool set, bool sync) 244{ 245 if (set) { 246 waitCount(0); 247 if (sync) { 248 syncResetCount++; 249 } else { 250 asyncResetCount++; 251 cancelTimeout(); 252 clearDynamic(); 253 scheduler.runNext(this); 254 } 255 } else { 256 if (sync) 257 syncResetCount--; 258 else 259 asyncResetCount--; 260 } 261} 262 263void 264Process::run() 265{ 266 bool reset; 267 do { 268 reset = false; 269 try { 270 func->call(); 271 } catch(ScHalt) { 272 std::cout << "Terminating process " << name() << std::endl; 273 } catch(const ::sc_core::sc_unwind_exception &exc) { 274 reset = exc.is_reset(); 275 _isUnwinding = false; 276 } catch (...) { 277 throw; 278 } 279 } while (reset); 280 needsStart(true); 281} 282 283void 284Process::addStatic(StaticSensitivity *s) 285{ 286 staticSensitivities.push_back(s); 287} 288 289void 290Process::setDynamic(DynamicSensitivity *s) 291{ 292 if (dynamicSensitivity) { 293 dynamicSensitivity->clear(); 294 delete dynamicSensitivity; 295 } 296 dynamicSensitivity = s; 297} 298 299void 300Process::addReset(Reset *reset) 301{ 302 resets.push_back(reset); 303} 304 305void 306Process::cancelTimeout() 307{ 308 if (timeoutEvent.scheduled()) 309 scheduler.deschedule(&timeoutEvent); 310} 311 312void 313Process::setTimeout(::sc_core::sc_time t) 314{ 315 cancelTimeout(); 316 scheduler.schedule(&timeoutEvent, t); 317} 318 319void 320Process::timeout() 321{ 322 // A process is considered timed_out only if it was also waiting for an 323 // event but got a timeout instead. 324 _timedOut = (dynamicSensitivity != nullptr); 325 326 setDynamic(nullptr); 327 if (disabled()) 328 return; 329 330 ready(); 331} 332 333void 334Process::satisfySensitivity(Sensitivity *s) 335{ 336 if (_waitCount) { 337 _waitCount--; 338 return; 339 } 340 341 // If there's a dynamic sensitivity and this wasn't it, ignore. 342 if ((dynamicSensitivity || timeoutEvent.scheduled()) && 343 dynamicSensitivity != s) { 344 return; 345 } 346 347 _timedOut = false; 348 // This sensitivity should already be cleared by this point, or the event 349 // which triggered it will take care of it. 350 delete dynamicSensitivity; 351 dynamicSensitivity = nullptr; 352 cancelTimeout(); 353 ready(); 354} 355 356void 357Process::ready() 358{ 359 if (disabled()) 360 return; 361 if (suspended()) 362 _suspendedReady = true; 363 else if (!scheduled()) 364 scheduler.ready(this); 365} 366 367void 368Process::lastReport(::sc_core::sc_report *report) 369{ 370 if (report) { 371 _lastReport = std::unique_ptr<::sc_core::sc_report>( 372 new ::sc_core::sc_report(*report)); 373 } else { 374 _lastReport = nullptr; 375 } 376} 377 378::sc_core::sc_report *Process::lastReport() const { return _lastReport.get(); } 379 380Process::Process(const char *name, ProcessFuncWrapper *func, bool internal) : 381 ::sc_core::sc_process_b(name), excWrapper(nullptr), 382 timeoutEvent([this]() { this->timeout(); }), 383 func(func), _internal(internal), _timedOut(false), _dontInitialize(false), 384 _needsStart(true), _isUnwinding(false), _terminated(false), 385 _scheduled(false), _suspended(false), _disabled(false), 386 _syncReset(false), syncResetCount(0), asyncResetCount(0), _waitCount(0), 387 refCount(0), stackSize(::Fiber::DefaultStackSize), 388 dynamicSensitivity(nullptr) 389{ 390 _dynamic = 391 (::sc_core::sc_get_status() > 392 ::sc_core::SC_BEFORE_END_OF_ELABORATION); 393 _newest = this; 394} 395 396void 397Process::terminate() 398{ 399 _terminated = true; 400 _suspendedReady = false; 401 _suspended = false; 402 _syncReset = false; 403 clearDynamic(); 404 cancelTimeout(); 405 for (auto s: staticSensitivities) { 406 s->clear(); 407 delete s; 408 } 409 staticSensitivities.clear(); 410 411 _terminatedEvent.notify(); 412 413 for (auto jw: joinWaiters) 414 jw->signal(); 415 joinWaiters.clear(); 416} 417 418Process *Process::_newest; 419 420void 421throw_it_wrapper(Process *p, ExceptionWrapperBase &exc, bool inc_kids) 422{ 423 p->throw_it(exc, inc_kids); 424} 425 426void 427newReset(const sc_core::sc_port_base *pb, Process *p, bool s, bool v) 428{ 429 Port *port = Port::fromPort(pb); 430 port->addReset(new Reset(p, s, v)); 431} 432 433void 434newReset(const sc_core::sc_signal_in_if<bool> *sig, Process *p, bool s, bool v) 435{ 436 Reset *reset = new Reset(p, s, v); 437 if (!reset->install(sig)) 438 delete reset; 439} 440 441} // namespace sc_gem5 442