-
Notifications
You must be signed in to change notification settings - Fork 254
/
Copy pathqf_port.c
315 lines (276 loc) · 11.4 KB
/
qf_port.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
//============================================================================
// QP/C Real-Time Embedded Framework (RTEF)
//
// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved.
//
// Q u a n t u m L e a P s
// ------------------------
// Modern Embedded Software
//
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
//
// This software is dual-licensed under the terms of the open-source GNU
// General Public License (GPL) or under the terms of one of the closed-
// source Quantum Leaps commercial licenses.
//
// Redistributions in source code must retain this top-level comment block.
// Plagiarizing this software to sidestep the license obligations is illegal.
//
// NOTE:
// The GPL does NOT permit the incorporation of this code into proprietary
// programs. Please contact Quantum Leaps for commercial licensing options,
// which expressly supersede the GPL and are designed explicitly for
// closed-source distribution.
//
// Quantum Leaps contact information:
// <www.state-machine.com/licensing>
// <[email protected]>
//============================================================================
#define QP_IMPL // this is QP implementation
#include "qp_port.h" // QP port
#include "qp_pkg.h" // QP package-scope interface
#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // QS port
#include "qs_pkg.h" // QS package-scope internal interface
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
Q_DEFINE_THIS_MODULE("qf_port")
//............................................................................
struct k_spinlock QF_spinlock;
//............................................................................
void QF_init(void) {
QF_spinlock = (struct k_spinlock){};
QF_bzero_(&QF_priv_, sizeof(QF_priv_));
QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_));
QTimeEvt_init(); // initialize QTimeEvts
}
//............................................................................
int_t QF_run(void) {
QF_onStartup();
#ifdef Q_SPY
#if (CONFIG_NUM_PREEMPT_PRIORITIES > 0)
// lower the priority of the main thread to the level of idle thread
k_thread_priority_set(k_current_get(),
CONFIG_NUM_PREEMPT_PRIORITIES - 1);
#endif
// produce the QS_QF_RUN trace record
QS_CRIT_STAT
QS_CRIT_ENTRY();
QS_BEGIN_PRE(QS_QF_RUN, 0U)
QS_END_PRE()
QS_CRIT_EXIT();
// perform QS work...
while (true) {
QS_rxParse(); // parse any QS-RX bytes
QS_doOutput(); // perform the QS-TX output
}
#else
return 0; // return from the main Zephyr thread
#endif
}
//............................................................................
void QF_stop(void) {
QF_onCleanup(); // cleanup callback
}
//............................................................................
static void thread_entry(void *p1, void *p2, void *p3) {
Q_UNUSED_PAR(p2);
Q_UNUSED_PAR(p3);
QActive *act = (QActive *)p1;
// event-loop
for (;;) { // for-ever
QEvt const *e = QActive_get_(act);
// dispatch event (virtual call)
(*act->super.vptr->dispatch)(&act->super, e, act->prio);
QF_gc(e); // check if the event is garbage, and collect it if so
}
}
//............................................................................
//
// In the Zephyr port the generic function QActive_setAttr() is used to
// set the options for the Zephyr thread (attr1) and thread name (attr2).
// QActive_setAttr() needs to be called *before* QActive_start() for the
// given active object.
//
// In this Zephyr port the attributes will be used as follows
// (see also QActive_start()):
// - attr1 - will be used for thread options in k_thread_create()
// - attr2 - will be used for thread name in k_thread_name_set()
//
void QActive_setAttr(QActive *const me, uint32_t attr1, void const *attr2) {
me->thread.base.order_key = attr1; // will be used for thread options
me->thread.init_data = (void *)attr2; // will be used for thread name
}
//............................................................................
void QActive_start(QActive * const me,
QPrioSpec const prioSpec,
QEvtPtr * const qSto, uint_fast16_t const qLen,
void * const stkSto, uint_fast16_t const stkSize,
void const * const par)
{
me->prio = (uint8_t)(prioSpec & 0xFFU); // QF-priority of the AO
me->pthre = 0U; // preemption-threshold (not used for AO registration)
QActive_register_(me); // make QF aware of this active object
// initialize the Zephyr message queue
k_msgq_init(&me->eQueue, (char *)qSto, sizeof(QEvtPtr), (uint32_t)qLen);
// top-most initial tran. (virtual call)
(*me->super.vptr->init)(&me->super, par, me->prio);
QS_FLUSH(); // flush the trace buffer to the host
// The Zephyr priority of the AO thread can be specified in two ways:
//
// 1. Implictily based on the AO's priority (Zephyr uses the reverse
// priority numbering scheme than QP). This option is chosen, when
// the higher-byte of the prioSpec parameter is set to zero.
//
// 2. Explicitly as the higher-byte of the prioSpec parameter.
// This option is chosen when the prioSpec parameter is not-zero.
// For example, Q_PRIO(10U, -1U) will explicitly specify AO priority
// as 10 and Zephyr priority as -1.
//
// NOTE: The explicit Zephyr priority is NOT sanity-checked,
// so it is the responsibility of the application to ensure that
// it is consistent with the AO's priority. An example of
// inconsistent setting would be assigning Zephyr priorities that
// would result in a different relative priritization of AO's threads
// than indicated by the AO priorities assigned.
//
int zephyr_prio = (int)((int16_t)prioSpec >> 8);
if (zephyr_prio == 0) {
zephyr_prio = (int)QF_MAX_ACTIVE - (int)me->prio;
}
// extract data temporarily saved in me->thread by QActive_setAttr()
uint32_t opt = me->thread.base.order_key;
#ifdef CONFIG_THREAD_NAME
char const *name = (char const *)me->thread.init_data;
#endif
// clear the Zephyr thread structure before creating the thread
me->thread = (struct k_thread){};
// create a Zephyr thread for the AO...
k_thread_create(&me->thread,
(k_thread_stack_t *)stkSto,
(size_t)stkSize,
&thread_entry,
(void *)me, // p1
(void *)0, // p2
(void *)0, // p3
zephyr_prio,// Zephyr priority
opt, // thread options
K_NO_WAIT); // start immediately
#ifdef CONFIG_THREAD_NAME
// set the Zephyr thread name, if initialized, or the default name "AO"
k_thread_name_set(&me->thread, (name != (char *)0) ? name : "AO");
#endif
}
//............................................................................
bool QActive_post_(QActive * const me, QEvt const * const e,
uint_fast16_t const margin, void const * const sender)
{
QF_CRIT_STAT
QF_CRIT_ENTRY();
Q_REQUIRE_INCRIT(200, e != (QEvt *)0);
// NOTE: k_msgq_num_free_get() can be safely called from crit-section
uint_fast16_t nFree = (uint_fast16_t)k_msgq_num_free_get(&me->eQueue);
bool status;
if (margin == QF_NO_MARGIN) {
if (nFree > 0U) {
status = true; // can post
}
else {
status = false; // cannot post
Q_ERROR_INCRIT(210); // must be able to post the event
}
}
else if (nFree > (QEQueueCtr)margin) {
status = true; // can post
}
else {
status = false; // cannot post
}
if (status) { // can post the event?
QS_BEGIN_PRE(QS_QF_ACTIVE_POST, me->prio)
QS_TIME_PRE(); // timestamp
QS_OBJ_PRE(sender); // the sender object
QS_SIG_PRE(e->sig); // the signal of the event
QS_OBJ_PRE(me); // this active object (recipient)
QS_2U8_PRE(e->poolNum_, e->refCtr_);// pool-Id & ref-Count
QS_EQC_PRE(nFree); // # free entries available
QS_EQC_PRE(0U); // min # free entries (unknown)
QS_END_PRE()
if (e->poolNum_ != 0U) { // is it a pool event?
Q_ASSERT_INCRIT(205, e->refCtr_ < (2U * QF_MAX_ACTIVE));
QEvt_refCtr_inc_(e); // increment the reference counter
}
QF_CRIT_EXIT(); // exit crit.sect. before calling Zephyr API
int err = k_msgq_put(&me->eQueue, (void const *)&e, K_NO_WAIT);
QF_CRIT_ENTRY(); // re-enter crit.sect. after calling Zephyr API
// posting to the Zephyr message queue must succeed, see NOTE1
Q_ASSERT_INCRIT(220, err == 0);
#ifdef Q_UNSAFE
Q_UNUSED_PAR(err);
#endif
}
else {
QS_BEGIN_PRE(QS_QF_ACTIVE_POST_ATTEMPT, me->prio)
QS_TIME_PRE(); // timestamp
QS_OBJ_PRE(sender); // the sender object
QS_SIG_PRE(e->sig); // the signal of the event
QS_OBJ_PRE(me); // this active object (recipient)
QS_2U8_PRE(e->poolNum_, e->refCtr_);
QS_EQC_PRE(nFree); // # free entries available
QS_EQC_PRE(0U); // min # free entries (unknown)
QS_END_PRE()
}
QF_CRIT_EXIT();
return status;
}
//............................................................................
void QActive_postLIFO_(QActive * const me, QEvt const * const e) {
QF_CRIT_STAT
QF_CRIT_ENTRY();
Q_REQUIRE_INCRIT(300, e != (QEvt *)0);
QS_BEGIN_PRE(QS_QF_ACTIVE_POST_LIFO, me->prio)
QS_TIME_PRE(); // timestamp
QS_SIG_PRE(e->sig); // the signal of this event
QS_OBJ_PRE(me); // this active object
QS_2U8_PRE(e->poolNum_, e->refCtr_);
QS_EQC_PRE(k_msgq_num_free_get(&me->eQueue)); // # free entries
QS_EQC_PRE(0U); // min # free entries (unknown)
QS_END_PRE()
if (e->poolNum_ != 0U) { // is it a pool event?
Q_ASSERT_INCRIT(305, e->refCtr_ < (2U * QF_MAX_ACTIVE));
QEvt_refCtr_inc_(e); // increment the reference counter
}
// NOTE: Zephyr message queue does not currently support LIFO posting
// so normal FIFO posting is used instead.
QF_CRIT_EXIT(); // exit crit.sect. before calling Zephyr API
int err = k_msgq_put(&me->eQueue, (void *)&e, K_NO_WAIT);
QF_CRIT_ENTRY(); // re-enter crit.sect. after calling Zephyr API
Q_ASSERT_INCRIT(310, err == 0);
#ifdef Q_UNSAFE
Q_UNUSED_PAR(err);
#endif
QF_CRIT_EXIT();
}
//............................................................................
QEvt const *QActive_get_(QActive * const me) {
// wait for an event (forever)
QEvtPtr e;
int err = k_msgq_get(&me->eQueue, (void *)&e, K_FOREVER);
QF_CRIT_STAT
QF_CRIT_ENTRY();
Q_ASSERT_INCRIT(410, err == 0); // queue-get must succeed
#ifdef Q_UNSAFE
Q_UNUSED_PAR(err);
#endif
QS_BEGIN_PRE(QS_QF_ACTIVE_GET, me->prio)
QS_TIME_PRE(); // timestamp
QS_SIG_PRE(e->sig); // the signal of this event
QS_OBJ_PRE(me); // this active object
QS_2U8_PRE(e->poolNum_, e->refCtr_);
QS_EQC_PRE(k_msgq_num_free_get(&me->eQueue));// # free entries
QS_END_PRE()
QF_CRIT_EXIT();
return e;
}