main.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. #include <stdio.h>
  2. #include "hardware/gpio.h"
  3. #include "pico/stdlib.h"
  4. #include "tinykernel/kernel.hpp"
  5. #include "tinykernel/actor.hpp"
  6. #include "tinykernel/sync.hpp"
  7. #include "pico/multicore.h"
  8. // Atomic printf helper: stdio is shared, so wrap each call with the kernel lock.
  9. #define P(...) \
  10. do { \
  11. bool _lk = tk::lock(); \
  12. printf("[C%u] ", (unsigned)get_core_num()); \
  13. printf(__VA_ARGS__); \
  14. tk::unlock(_lk); \
  15. } while (0)
  16. // ---------------------------------------------------------------------------
  17. // Actor demonstration: 1-to-N Pub/Sub via EventHub.
  18. // Senders call tk::publish(msg) without needing to know any Actor instance!
  19. // All actors subscribed to AppMessage receive their own copy in their queues.
  20. struct AppMessage {
  21. uint32_t seq;
  22. char text[32]; // Embedded payload held safely by the queue!
  23. };
  24. class LedActor : public tk::Actor<AppMessage, 8> {
  25. protected:
  26. void on_start() override {
  27. gpio_init(25);
  28. gpio_set_dir(25, GPIO_OUT);
  29. gpio_put(25, 0);
  30. P("LedActor: initialized GPIO 25 in on_start()\n");
  31. }
  32. void on_message(const AppMessage& msg) override {
  33. bool state = !gpio_get(25);
  34. gpio_put(25, state);
  35. P("LedActor: seq=%lu text=\"%s\" -> LED %s, now=%llu ms\n",
  36. (unsigned long)msg.seq, msg.text, state ? "ON" : "OFF", (unsigned long long)tk::now_ms());
  37. }
  38. };
  39. // Second actor subscribed to the exact same AppMessage type!
  40. class LogActor : public tk::Actor<AppMessage, 8> {
  41. protected:
  42. void on_message(const AppMessage& msg) override {
  43. P("LogActor: [AUDIT] seq=%lu text=\"%s\", now=%llu ms\n",
  44. (unsigned long)msg.seq, msg.text, (unsigned long long)tk::now_ms());
  45. }
  46. };
  47. static LedActor g_led_actor;
  48. static LogActor g_log_actor;
  49. // Sender task: publishes messages anonymously (without knowing who the actors are!)
  50. static void task_sender(void* arg) {
  51. (void)arg;
  52. uint32_t seq = 0;
  53. for (;;) {
  54. tk::sleep_ms(800);
  55. // Local variable allocated on task_sender's stack
  56. AppMessage msg{};
  57. msg.seq = seq++;
  58. snprintf(msg.text, sizeof(msg.text), "Ping #%lu", (unsigned long)msg.seq);
  59. // Anonymous 1-to-N publish!
  60. size_t n = tk::publish(msg);
  61. P("Sender: published text=\"%s\" to %lu subscriber(s)!\n", msg.text, (unsigned long)n);
  62. }
  63. }
  64. // ---------------------------------------------------------------------------
  65. // Mutex demonstration: Two tasks contend for a shared counter protected by Mutex
  66. static tk::Mutex g_counter_mutex;
  67. static uint32_t g_shared_counter = 0;
  68. static void task_mutex_worker(void* arg) {
  69. const char* tag = static_cast<const char*>(arg);
  70. for (;;) {
  71. {
  72. tk::LockGuard<tk::Mutex> guard(g_counter_mutex);
  73. g_shared_counter++;
  74. P("[%s] holds mutex: counter=%lu\n", tag, (unsigned long)g_shared_counter);
  75. volatile uint32_t n = 0;
  76. while (n < 100000u) ++n;
  77. } // guard destructor automatically calls unlock()
  78. tk::sleep_ms(350);
  79. }
  80. }
  81. // ---------------------------------------------------------------------------
  82. // Semaphore demonstration: Signaler wakes up Waiter task
  83. static tk::BinarySemaphore g_demo_sem(0, 1); // Initially 0 (blocked)
  84. static void task_sem_waiter(void* arg) {
  85. (void)arg;
  86. for (;;) {
  87. g_demo_sem.acquire(); // Blocks until release() is called
  88. P("SemWaiter: >>> WOKEN UP by semaphore! now=%llu ms\n", (unsigned long long)tk::now_ms());
  89. }
  90. }
  91. static void task_sem_signaler(void* arg) {
  92. (void)arg;
  93. for (;;) {
  94. tk::sleep_ms(1000);
  95. P("SemSignaler: releasing semaphore...\n");
  96. g_demo_sem.release();
  97. }
  98. }
  99. // ---------------------------------------------------------------------------
  100. // Static task: FPU self-check. Runs the same deterministic float/double sequence
  101. // twice; any corruption of FPU state across context switches shows up as a
  102. // mismatch or a drifting result.
  103. static float fpu_run_f(float v) {
  104. for (int i = 0; i < 10000; ++i) v = v * 1.000001f + 0.5f;
  105. return v;
  106. }
  107. static double fpu_run_d(double v) {
  108. for (long i = 0; i < 10000; ++i) v = v * 1.0000000002 + 0.5;
  109. return v;
  110. }
  111. void task_fpu(void* arg) {
  112. (void)arg;
  113. for (;;) {
  114. float fa = fpu_run_f(1.5f);
  115. float fb = fpu_run_f(1.5f);
  116. double da = fpu_run_d(2.5);
  117. double db = fpu_run_d(2.5);
  118. if (fa != fb || da != db) {
  119. P("FPU FAIL f: %8.6f vs %8.6f d: %.9f vs %.9f\n", (double)fa, (double)fb, da, db);
  120. } else {
  121. P("FPU ok f: %8.6f d: %.9f\n", (double)fa, da);
  122. }
  123. tk::sleep_ms(500);
  124. }
  125. }
  126. // ---------------------------------------------------------------------------
  127. // Preemption Guard demonstration: Simulates an atomic peripheral transaction (e.g. I2C)
  128. // During PreemptGuard, no task switch occurs even if time slices expire or interrupts fire.
  129. static void task_preempt_demo(void* arg) {
  130. (void)arg;
  131. for (;;) {
  132. tk::sleep_ms(2000);
  133. {
  134. tk::PreemptGuard guard;
  135. uint64_t t0 = tk::now_ms();
  136. P("PreemptDemo: >>> START atomic section (preemption disabled), t0=%llu ms\n", (unsigned long long)t0);
  137. // Busy wait 60ms (exceeds the 20ms time slice, but preemption is blocked!)
  138. // Meanwhile SysTick interrupt keeps running, so now_ms() keeps updating!
  139. while (tk::now_ms() - t0 < 60) {
  140. }
  141. P("PreemptDemo: <<< END atomic section, now=%llu ms (SysTick kept advancing!)\n", (unsigned long long)tk::now_ms());
  142. } // guard destructor calls preempt_enable() -> deferred task switches happen now!
  143. }
  144. }
  145. // ---------------------------------------------------------------------------
  146. // Dynamic Thread & Signal demonstration:
  147. // User-defined signals starting from tk::SIG_USER
  148. enum AppSignal : uint32_t {
  149. SIG_WAKE_ACTION = tk::SIG_USER,
  150. SIG_STATUS_DUMP = tk::SIG_USER + 1,
  151. };
  152. class EphemeralWorker : public tk::Thread<2048> {
  153. public:
  154. explicit EphemeralWorker(uint32_t id) : id_(id) {}
  155. protected:
  156. void on_start() override {
  157. P("Worker#%lu [slot=%u]: started!\n", (unsigned long)id_, (unsigned)handle().slot);
  158. }
  159. void run() override {
  160. while (!should_stop()) {
  161. // Long 5000ms sleep; will be broken immediately when a signal arrives!
  162. if (sleep_ms(5000)) {
  163. P("Worker#%lu: 5000ms sleep completed naturally\n", (unsigned long)id_);
  164. }
  165. }
  166. }
  167. void on_signal(uint32_t sig) override {
  168. if (sig == SIG_WAKE_ACTION) {
  169. P("Worker#%lu: >>> interrupted sleep! Received SIG_WAKE_ACTION at %llu ms\n",
  170. (unsigned long)id_, (unsigned long long)tk::now_ms());
  171. } else if (sig == SIG_STATUS_DUMP) {
  172. P("Worker#%lu: >>> interrupted sleep! Received SIG_STATUS_DUMP at %llu ms\n",
  173. (unsigned long)id_, (unsigned long long)tk::now_ms());
  174. } else if (sig == tk::SIG_STOP) {
  175. P("Worker#%lu: >>> received SIG_STOP, exiting run() loop...\n", (unsigned long)id_);
  176. }
  177. }
  178. void on_stop() override {
  179. P("Worker#%lu [slot=%u]: halted (Terminated), waiting for destructor cleanup\n",
  180. (unsigned long)id_, (unsigned)handle().slot);
  181. }
  182. private:
  183. uint32_t id_;
  184. };
  185. static void task_thread_manager(void* arg) {
  186. (void)arg;
  187. uint32_t gen = 1;
  188. for (;;) {
  189. tk::sleep_ms(1500);
  190. {
  191. // Scoped dynamic thread: stack lives in 'worker', slot is freed when scope exits!
  192. EphemeralWorker worker(gen++);
  193. worker.start(tk::PRIO_NORMAL);
  194. tk::sleep_ms(600);
  195. worker.send_signal(SIG_WAKE_ACTION); // Breaks 5000ms sleep early!
  196. tk::sleep_ms(600);
  197. worker.send_signal(SIG_STATUS_DUMP); // Breaks 5000ms sleep early!
  198. tk::sleep_ms(400);
  199. worker.stop(); // Sends SIG_STOP -> halts in Terminated
  200. worker.join();
  201. } // ~EphemeralWorker() runs here -> reclaims task slot back to State::Free!
  202. }
  203. }
  204. int main() {
  205. stdio_init_all();
  206. printf("tinykernel: registering tasks...\n");
  207. g_led_actor.start(tk::PRIO_NORMAL);
  208. g_log_actor.start(tk::PRIO_NORMAL);
  209. auto h_sender = tk::create(task_sender, nullptr, tk::PRIO_NORMAL);
  210. auto h_mtx_a = tk::create(task_mutex_worker, const_cast<char*>("Mtx-A"), tk::PRIO_NORMAL);
  211. auto h_mtx_b = tk::create(task_mutex_worker, const_cast<char*>("Mtx-B"), tk::PRIO_NORMAL);
  212. auto h_waiter = tk::create(task_sem_waiter, nullptr, tk::PRIO_HIGH);
  213. auto h_sig = tk::create(task_sem_signaler, nullptr, tk::PRIO_NORMAL);
  214. auto h_preempt = tk::create(task_preempt_demo, nullptr, tk::PRIO_NORMAL);
  215. auto h_thrmgr = tk::create(task_thread_manager, nullptr, tk::PRIO_NORMAL);
  216. if (!g_led_actor.handle().is_valid() || !g_log_actor.handle().is_valid() ||
  217. !h_sender.is_valid() ||
  218. !h_mtx_a.is_valid() || !h_mtx_b.is_valid() ||
  219. !h_waiter.is_valid() || !h_sig.is_valid() ||
  220. !h_preempt.is_valid() || !h_thrmgr.is_valid()) {
  221. printf("tinykernel: task creation FAILED\n");
  222. for (;;) {
  223. }
  224. }
  225. printf("tinykernel: starting scheduler (preemptive, dual-core M33)\n");
  226. tk::start(); // never returns
  227. }