mirror of
https://github.com/QuantumLeaps/qpc.git
synced 2025-01-14 06:43:19 +08:00
6.2.0a
This commit is contained in:
parent
d99c813660
commit
5ab79df2a0
@ -9,7 +9,7 @@
|
||||
License Type: Windows Single User License
|
||||
Licensed To : Quantum Leaps, LLC
|
||||
License No. : WS2975 License Date: Dec 15, 2013
|
||||
Build Date : Sep 2 2009 Run Date: Mar 19, 2018
|
||||
Build Date : Sep 2 2009 Run Date: Mar 25, 2018
|
||||
(C)1996-2009 M Squared Technologies LLC
|
||||
________________________________________________________________________
|
||||
|
||||
|
@ -98,7 +98,7 @@ In contrast, event-driven active objects don't need to block, because in event-d
|
||||
|
||||
<div class="separate"></div>
|
||||
@subsection srs_hsm Hierarchical State Machines
|
||||
As suggested in the UML specification and similar as in <span title=""Real-Time Object-Oriented Modeling" Wikipedia"><a class="extern" target="_blank" href="https://en.wikipedia.org/wiki/Real-Time_Object-Oriented_Modeling">ROOM</a></span>, the behavior of each Active Object can be specified by means of a <span class="label label-primary">hierarchical state machine</span> (<span title=""UML state machine", Wikipedia"><a class="extern" target="_blank" href="http://en.wikipedia.org/wiki/UML_state_machine">UML statechart</a></span>), which is a very effective and elegant technique of decomposing event-driven behavior.
|
||||
As suggested in the UML specification and similar as in <span title=""Real-Time Object-Oriented Modeling" Wikipedia"><a class="extern" target="_blank" href="https://en.wikipedia.org/wiki/Real-Time_Object-Oriented_Modeling">ROOM</a></span>, the behavior of each Active Object can be specified by means of a <span class="label label-primary">hierarchical state machine</span> (<span title=""UML state machine", Wikipedia"><a class="extern" target="_blank" href="http://en.wikipedia.org/wiki/UML_state_machine">UML statechart</a></span>), which is a very effective and elegant technique of describing event-driven behavior.
|
||||
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
@ -105,7 +105,7 @@ The figure below shows the main classes comprising the QP/C framework and their
|
||||
------------------------------------------------------------------------------
|
||||
@section sm State Machines
|
||||
|
||||
The behavior of each active object in QP/C is specified by means of a <a href="https://www.state-machine.com/doc/concepts#HSM" target="_blank" class="extern">hierarchical state machine</a> (UML statechart), which is the most effective and elegant technique of decomposing event-driven behavior. The most important innovation of UML state machines over classical finite state machines (FSMs) is the hierarchical state nesting. The value of state nesting lies in avoiding repetitions, which are inevitable in the traditional "flat" FSM formalism and are the main reason for the "state-transition explosion" in FSMs. The semantics of state nesting allow substates to define only the differences of behavior from the superstates, thus promoting sharing and reusing behavior.
|
||||
The behavior of each active object in QP/C is specified by means of a <a href="https://www.state-machine.com/doc/concepts#HSM" target="_blank" class="extern">hierarchical state machine</a> (UML statechart), which is the most effective and elegant technique of describing event-driven behavior. The most important innovation of UML state machines over classical finite state machines (FSMs) is the hierarchical state nesting. The value of state nesting lies in avoiding repetitions, which are inevitable in the traditional "flat" FSM formalism and are the main reason for the "state-transition explosion" in FSMs. The semantics of state nesting allow substates to define only the differences of behavior from the superstates, thus promoting sharing and reusing behavior.
|
||||
|
||||
@note
|
||||
The Quantum Leaps Application Note <a class="extern" target="_blank" href="https://www.state-machine.com/doc/AN_Crash_Course_in_UML_State_Machines.pdf"><strong>A Crash Course in UML State Machines</strong></a> introduces the main state machine concepts backed up by examples.
|
||||
|
242
examples/posix-qv/dpp/Makefile
Normal file
242
examples/posix-qv/dpp/Makefile
Normal file
@ -0,0 +1,242 @@
|
||||
##############################################################################
|
||||
# Product: Makefile for QP/C, DPP console, POSIX-QV target, GNU compiler
|
||||
# Last updated for version 6.2.0
|
||||
# Last updated on 2018-04-05
|
||||
#
|
||||
# Q u a n t u m L e a P s
|
||||
# ---------------------------
|
||||
# innovating embedded systems
|
||||
#
|
||||
# Copyright (C) 2005-2018 Quantum Leaps, LLC. All rights reserved.
|
||||
#
|
||||
# This program is open source software: you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Alternatively, this program may be distributed and modified under the
|
||||
# terms of Quantum Leaps commercial licenses, which expressly supersede
|
||||
# the GNU General Public License and are specifically designed for
|
||||
# licensees interested in retaining the proprietary status of their code.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Contact information:
|
||||
# https://www.state-machine.com
|
||||
# mailto:info@state-machine.com
|
||||
##############################################################################
|
||||
#
|
||||
# examples of invoking this Makefile:
|
||||
# building configurations: Debug (default), Release, and Spy
|
||||
# make
|
||||
# make CONF=rel
|
||||
# make CONF=spy
|
||||
#
|
||||
# cleaning configurations: Debug (default), Release, and Spy
|
||||
# make clean
|
||||
# make CONF=rel clean
|
||||
# make CONF=spy clean
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# project name
|
||||
#
|
||||
PROJECT := dpp
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# project directories
|
||||
#
|
||||
|
||||
# location of the QP/C framework (if not provided in an environemnt var.)
|
||||
ifeq ($(QPC),)
|
||||
QPC := ../../..
|
||||
endif
|
||||
|
||||
# QP port used in this project
|
||||
QP_PORT_DIR := $(QPC)/ports/posix-qv
|
||||
|
||||
# list of all source directories used by this project
|
||||
VPATH = \
|
||||
. \
|
||||
$(QPC)/src/qf \
|
||||
$(QPC)/src/qs \
|
||||
$(QP_PORT_DIR)
|
||||
|
||||
# list of all include directories needed by this project
|
||||
INCLUDES = \
|
||||
-I. \
|
||||
-I$(QPC)/include \
|
||||
-I$(QPC)/src \
|
||||
-I$(QP_PORT_DIR)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# files
|
||||
#
|
||||
|
||||
# C source files...
|
||||
C_SRCS := \
|
||||
bsp.c \
|
||||
main.c \
|
||||
philo.c \
|
||||
table.c
|
||||
|
||||
# C++ source files...
|
||||
CPP_SRCS :=
|
||||
|
||||
QP_SRCS := \
|
||||
qep_hsm.c \
|
||||
qep_msm.c \
|
||||
qf_act.c \
|
||||
qf_actq.c \
|
||||
qf_defer.c \
|
||||
qf_dyn.c \
|
||||
qf_mem.c \
|
||||
qf_ps.c \
|
||||
qf_qact.c \
|
||||
qf_qeq.c \
|
||||
qf_qmact.c \
|
||||
qf_time.c \
|
||||
qf_port.c
|
||||
|
||||
QS_SRCS := \
|
||||
qs.c \
|
||||
qs_64bit.c \
|
||||
qs_rx.c \
|
||||
qs_fp.c
|
||||
|
||||
LIB_DIRS :=
|
||||
LIBS :=
|
||||
|
||||
# defines...
|
||||
# QP_API_VERSION controls the QP API compatibility; 9999 means the latest API
|
||||
DEFINES := -DQP_API_VERSION=9999
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# GNU toolset
|
||||
#
|
||||
CC := gcc
|
||||
CPP := g++
|
||||
LINK := gcc # for C programs
|
||||
#LINK := g++ # for C++ programs
|
||||
|
||||
MKDIR := mkdir -p
|
||||
RM := rm -f
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# build options for various configurations
|
||||
#
|
||||
# combine all the soruces...
|
||||
C_SRCS += $(QP_SRCS)
|
||||
|
||||
ifeq (rel, $(CONF)) # Release configuration ..................................
|
||||
|
||||
BIN_DIR := rel
|
||||
|
||||
CFLAGS = -ffunction-sections -fdata-sections \
|
||||
-Os -Wall -W $(INCLUDES) $(DEFINES) -pthread -DNDEBUG
|
||||
|
||||
CPPFLAGS = -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections \
|
||||
-Os -Wall -W $(INCLUDES) $(DEFINES) -pthread -DNDEBUG
|
||||
|
||||
else ifeq (spy, $(CONF)) # Spy configuration ................................
|
||||
|
||||
BIN_DIR := spy
|
||||
|
||||
C_SRCS += $(QS_SRCS)
|
||||
|
||||
CFLAGS = -g -ffunction-sections -fdata-sections \
|
||||
-O -Wall -W $(INCLUDES) $(DEFINES) -pthread -DQ_SPY
|
||||
|
||||
CPPFLAGS = -g -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections \
|
||||
-O -Wall -W $(INCLUDES) $(DEFINES) -pthread -DQ_SPY
|
||||
|
||||
else # default Debug configuration ..........................................
|
||||
|
||||
BIN_DIR := dbg
|
||||
|
||||
CFLAGS = -g -ffunction-sections -fdata-sections \
|
||||
-O -Wall -W $(INCLUDES) $(DEFINES) -pthread
|
||||
|
||||
CPPFLAGS = -g -fno-rtti -fno-exceptions -ffunction-sections -fdata-sections \
|
||||
-O -Wall -W $(INCLUDES) $(DEFINES) -pthread
|
||||
|
||||
endif # .....................................................................
|
||||
|
||||
LINKFLAGS := -Wl,-Map,$(BIN_DIR)/$(PROJECT).map,--cref,--gc-sections
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# combine all the soruces...
|
||||
INCLUDES += -I$(QP_PORT_DIR)
|
||||
LIB_DIRS += -L$(QP_PORT_DIR)/$(BIN_DIR)
|
||||
LIBS += -lpthread
|
||||
|
||||
C_OBJS := $(patsubst %.c,%.o, $(C_SRCS))
|
||||
CPP_OBJS := $(patsubst %.cpp,%.o, $(CPP_SRCS))
|
||||
|
||||
TARGET_BIN := $(BIN_DIR)/$(PROJECT).bin
|
||||
TARGET_EXE := $(BIN_DIR)/$(PROJECT)
|
||||
C_OBJS_EXT := $(addprefix $(BIN_DIR)/, $(C_OBJS))
|
||||
C_DEPS_EXT := $(patsubst %.o,%.d, $(C_OBJS_EXT))
|
||||
CPP_OBJS_EXT := $(addprefix $(BIN_DIR)/, $(CPP_OBJS))
|
||||
CPP_DEPS_EXT := $(patsubst %.o,%.d, $(CPP_OBJS_EXT))
|
||||
|
||||
# create $(BIN_DIR) if it does not exist
|
||||
ifeq ("$(wildcard $(BIN_DIR))","")
|
||||
$(shell $(MKDIR) $(BIN_DIR))
|
||||
endif
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# rules
|
||||
#
|
||||
|
||||
all: $(TARGET_EXE)
|
||||
#all: $(TARGET_BIN)
|
||||
|
||||
$(TARGET_BIN): $(TARGET_EXE)
|
||||
$(BIN) -O binary $< $@
|
||||
|
||||
$(TARGET_EXE) : $(C_OBJS_EXT) $(CPP_OBJS_EXT)
|
||||
$(CC) $(CFLAGS) -c $(QPC)/include/qstamp.c -o $(BIN_DIR)/qstamp.o
|
||||
$(LINK) $(LINKFLAGS) $(LIB_DIRS) -o $@ $^ $(BIN_DIR)/qstamp.o $(LIBS)
|
||||
|
||||
$(BIN_DIR)/%.d : %.cpp
|
||||
$(CPP) -MM -MT $(@:.d=.o) $(CPPFLAGS) $< > $@
|
||||
|
||||
$(BIN_DIR)/%.d : %.c
|
||||
$(CC) -MM -MT $(@:.d=.o) $(CFLAGS) $< > $@
|
||||
|
||||
$(BIN_DIR)/%.o : %.cpp
|
||||
$(CPP) $(CPPFLAGS) -c $< -o $@
|
||||
|
||||
$(BIN_DIR)/%.o : %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# include dependency files only if our goal depends on their existence
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(MAKECMDGOALS),show)
|
||||
-include $(C_DEPS_EXT) $(CPP_DEPS_EXT)
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY : clean
|
||||
clean:
|
||||
-$(RM) $(BIN_DIR)/*
|
||||
|
||||
show:
|
||||
@echo PROJECT = $(PROJECT)
|
||||
@echo CONF = $(CONF)
|
||||
@echo VPATH = $(VPATH)
|
||||
@echo C_SRCS = $(C_SRCS)
|
||||
@echo CPP_SRCS = $(CPP_SRCS)
|
||||
@echo C_OBJS_EXT = $(C_OBJS_EXT)
|
||||
@echo C_DEPS_EXT = $(C_DEPS_EXT)
|
||||
@echo CPP_DEPS_EXT = $(CPP_DEPS_EXT)
|
||||
@echo CPP_OBJS_EXT = $(CPP_OBJS_EXT)
|
||||
@echo LIB_DIRS = $(LIB_DIRS)
|
||||
@echo LIBS = $(LIBS)
|
||||
|
549
examples/posix-qv/dpp/bsp.c
Normal file
549
examples/posix-qv/dpp/bsp.c
Normal file
@ -0,0 +1,549 @@
|
||||
/*****************************************************************************
|
||||
* Product: DPP example, POSIX
|
||||
* Last updated for version 6.2.0
|
||||
* Last updated on 2018-03-10
|
||||
*
|
||||
* Q u a n t u m L e a P s
|
||||
* ---------------------------
|
||||
* innovating embedded systems
|
||||
*
|
||||
* Copyright (C) 2002-2018 Quantum Leaps, LLC. All rights reserved.
|
||||
*
|
||||
* This program is open source software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alternatively, this program may be distributed and modified under the
|
||||
* terms of Quantum Leaps commercial licenses, which expressly supersede
|
||||
* the GNU General Public License and are specifically designed for
|
||||
* licensees interested in retaining the proprietary status of their code.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Contact information:
|
||||
* https://www.state-machine.com
|
||||
* mailto:info@state-machine.com
|
||||
*****************************************************************************/
|
||||
#include "qpc.h"
|
||||
#include "dpp.h"
|
||||
#include "bsp.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h> /* for memcpy() and memset() */
|
||||
#include <sys/select.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
Q_DEFINE_THIS_FILE
|
||||
|
||||
/* Local objects -----------------------------------------------------------*/
|
||||
static struct termios l_tsav; /* structure with saved terminal attributes */
|
||||
static uint32_t l_rnd; /* random seed */
|
||||
|
||||
#ifdef Q_SPY
|
||||
enum {
|
||||
PHILO_STAT = QS_USER
|
||||
};
|
||||
static uint8_t const l_clock_tick = 0U;
|
||||
#endif
|
||||
|
||||
/* BSP functions ===========================================================*/
|
||||
void BSP_init(int argc, char **argv) {
|
||||
|
||||
#ifndef Q_SPY
|
||||
(void)argc; /* unused parameter */
|
||||
(void)argv; /* unused parameter */
|
||||
#endif
|
||||
|
||||
printf("Dining Philosophers Problem example"
|
||||
"\nQP %s\n"
|
||||
"Press p to pause\n"
|
||||
"Press s to serve\n"
|
||||
"Press ESC to quit...\n",
|
||||
QP_versionStr);
|
||||
|
||||
BSP_randomSeed(1234U);
|
||||
|
||||
Q_ALLEGE(QS_INIT(argc > 1 ? argv[1] : (void *)0));
|
||||
QS_OBJ_DICTIONARY(&l_clock_tick); /* must be called *after* QF_init() */
|
||||
QS_USR_DICTIONARY(PHILO_STAT);
|
||||
|
||||
/* setup the QS filters... */
|
||||
QS_FILTER_ON(QS_SM_RECORDS); // state machine records
|
||||
QS_FILTER_ON(QS_UA_RECORDS); // all usedr records
|
||||
//QS_FILTER_ON(QS_MUTEX_LOCK);
|
||||
//QS_FILTER_ON(QS_MUTEX_UNLOCK);
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void BSP_terminate(int16_t result) {
|
||||
(void)result;
|
||||
QF_stop();
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void BSP_displayPhilStat(uint8_t n, char const *stat) {
|
||||
printf("Philosopher %2d is %s\n", (int)n, stat);
|
||||
|
||||
QS_BEGIN(PHILO_STAT, AO_Philo[n]) /* application-specific record begin */
|
||||
QS_U8(1, n); /* Philosopher number */
|
||||
QS_STR(stat); /* Philosopher status */
|
||||
QS_END()
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void BSP_displayPaused(uint8_t paused) {
|
||||
printf("Paused is %s\n", paused ? "ON" : "OFF");
|
||||
}
|
||||
/*..........................................................................*/
|
||||
uint32_t BSP_random(void) { /* a very cheap pseudo-random-number generator */
|
||||
/* "Super-Duper" Linear Congruential Generator (LCG)
|
||||
* LCG(2^32, 3*7*11*13*23, 0, seed)
|
||||
*/
|
||||
l_rnd = l_rnd * (3*7*11*13*23);
|
||||
return l_rnd >> 8;
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void BSP_randomSeed(uint32_t seed) {
|
||||
l_rnd = seed;
|
||||
}
|
||||
|
||||
|
||||
/* QF callbacks ============================================================*/
|
||||
void QF_onStartup(void) {
|
||||
struct termios tio; /* modified terminal attributes */
|
||||
|
||||
tcgetattr(0, &l_tsav); /* save the current terminal attributes */
|
||||
tcgetattr(0, &tio); /* obtain the current terminal attributes */
|
||||
tio.c_lflag &= ~(ICANON | ECHO); /* disable the canonical mode & echo */
|
||||
tcsetattr(0, TCSANOW, &tio); /* set the new attributes */
|
||||
|
||||
QF_setTickRate(BSP_TICKS_PER_SEC); /* set the desired tick rate */
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void QF_onCleanup(void) {
|
||||
printf("\nBye! Bye!\n");
|
||||
tcsetattr(0, TCSANOW, &l_tsav);/* restore the saved terminal attributes */
|
||||
QS_EXIT(); /* perfomr the QS cleanup */
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void QF_onClockTick(void) {
|
||||
struct timeval timeout = { 0, 0 }; /* timeout for select() */
|
||||
fd_set con; /* FD set representing the console */
|
||||
|
||||
QF_TICK_X(0U, &l_clock_tick); /* perform the QF clock tick processing */
|
||||
|
||||
FD_ZERO(&con);
|
||||
FD_SET(0, &con);
|
||||
/* check if a console input is available, returns immediately */
|
||||
if (0 != select(1, &con, 0, 0, &timeout)) { /* any descriptor set? */
|
||||
char ch;
|
||||
read(0, &ch, 1);
|
||||
if (ch == '\33') { /* ESC pressed? */
|
||||
BSP_terminate(0);
|
||||
}
|
||||
else if (ch == 'p') {
|
||||
QF_PUBLISH(Q_NEW(QEvt, PAUSE_SIG), &l_clock_tick);
|
||||
}
|
||||
else if (ch == 's') {
|
||||
QF_PUBLISH(Q_NEW(QEvt, SERVE_SIG), &l_clock_tick);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void Q_onAssert(char const *module, int loc) {
|
||||
/*
|
||||
* NOTE: add here your application-specific error handling
|
||||
*/
|
||||
printf("Assertion failed in %s:%d", module, loc);
|
||||
QS_ASSERTION(module, loc, (uint32_t)10000U); /* report assertion to QS */
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
#ifdef Q_SPY
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
* The QS target-resident component is implemented in two different ways:
|
||||
* 1. Output to the TCP/IP socket, which requires a separate QSPY host
|
||||
* application running; or
|
||||
* 2. Direct linking with the QSPY host application to perform direct output
|
||||
* to the console from the running application. (This option requires
|
||||
* the QSPY source code, which is part of the QTools collection).
|
||||
*
|
||||
* The two options are selected by the following QS_IMPL_OPTION macro.
|
||||
* Please set the value of this macro to either 1 or 2:
|
||||
*/
|
||||
#define QS_IMPL_OPTION 1
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#if (QS_IMPL_OPTION == 1)
|
||||
|
||||
/*
|
||||
* 1. Output to the TCP/IP socket, which requires a separate QSPY host
|
||||
* application running. This option does not link to the QSPY code.
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#define QS_TX_SIZE (4*1024)
|
||||
#define QS_RX_SIZE 1024
|
||||
#define QS_IMEOUT_MS 100
|
||||
#define INVALID_SOCKET -1
|
||||
|
||||
/* local variables .........................................................*/
|
||||
static void *idleThread(void *par); // the expected P-Thread signature
|
||||
static int l_sock = INVALID_SOCKET;
|
||||
static uint8_t l_running;
|
||||
|
||||
/*..........................................................................*/
|
||||
uint8_t QS_onStartup(void const *arg) {
|
||||
static uint8_t qsBuf[QS_TX_SIZE]; // buffer for QS-TX channel
|
||||
static uint8_t qsRxBuf[QS_RX_SIZE]; // buffer for QS-RX channel
|
||||
char hostName[64];
|
||||
char const *src;
|
||||
char *dst;
|
||||
|
||||
uint16_t port_local = 51234; /* default local port */
|
||||
uint16_t port_remote = 6601; /* default QSPY server port */
|
||||
int sockopt_bool;
|
||||
struct sockaddr_in sa_local;
|
||||
struct sockaddr_in sa_remote;
|
||||
struct hostent *host;
|
||||
|
||||
QS_initBuf(qsBuf, sizeof(qsBuf));
|
||||
QS_rxInitBuf(qsRxBuf, sizeof(qsRxBuf));
|
||||
|
||||
src = (arg != (void const *)0)
|
||||
? (char const *)arg
|
||||
: "localhost";
|
||||
dst = hostName;
|
||||
while ((*src != '\0')
|
||||
&& (*src != ':')
|
||||
&& (dst < &hostName[sizeof(hostName)]))
|
||||
{
|
||||
*dst++ = *src++;
|
||||
}
|
||||
*dst = '\0';
|
||||
if (*src == ':') {
|
||||
port_remote = (uint16_t)strtoul(src + 1, NULL, 10);
|
||||
}
|
||||
|
||||
printf("<TARGET> Connecting to QSPY on Host=%s:%d...\n",
|
||||
hostName, port_remote);
|
||||
|
||||
l_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* TCP socket */
|
||||
if (l_sock == INVALID_SOCKET){
|
||||
printf("<TARGET> ERROR cannot create client socket, errno=%d\n",
|
||||
errno);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* configure the socket */
|
||||
sockopt_bool = 1;
|
||||
setsockopt(l_sock, SOL_SOCKET, SO_REUSEADDR,
|
||||
&sockopt_bool, sizeof(sockopt_bool));
|
||||
|
||||
sockopt_bool = 0;
|
||||
setsockopt(l_sock, SOL_SOCKET, SO_LINGER,
|
||||
&sockopt_bool, sizeof(sockopt_bool));
|
||||
|
||||
/* local address:port */
|
||||
memset(&sa_local, 0, sizeof(sa_local));
|
||||
sa_local.sin_family = AF_INET;
|
||||
sa_local.sin_port = htons(port_local);
|
||||
host = gethostbyname(""); /* local host */
|
||||
//sa_local.sin_addr.s_addr = inet_addr(
|
||||
// inet_ntoa(*(struct in_addr *)*host->h_addr_list));
|
||||
//if (bind(l_sock, &sa_local, sizeof(sa_local)) == -1) {
|
||||
// printf("<TARGET> Cannot bind to the local port Err=0x%08X\n",
|
||||
// WSAGetLastError());
|
||||
// /* no error */
|
||||
//}
|
||||
|
||||
/* remote hostName:port (QSPY server socket) */
|
||||
host = gethostbyname(hostName);
|
||||
if (host == NULL) {
|
||||
printf("<TARGET> ERROR cannot resolve host Name=%s:%d,errno=%d\n",
|
||||
hostName, port_remote, errno);
|
||||
goto error;
|
||||
}
|
||||
memset(&sa_remote, 0, sizeof(sa_remote));
|
||||
sa_remote.sin_family = AF_INET;
|
||||
memcpy(&sa_remote.sin_addr, host->h_addr, host->h_length);
|
||||
sa_remote.sin_port = htons(port_remote);
|
||||
|
||||
/* try to connect to the QSPY server */
|
||||
if (connect(l_sock, (struct sockaddr *)&sa_remote, sizeof(sa_remote))
|
||||
== -1)
|
||||
{
|
||||
printf("<TARGET> ERROR cannot connect to QSPY on Host="
|
||||
"%s:%d,errno=%d\n", hostName, port_remote, errno);
|
||||
goto error;
|
||||
}
|
||||
|
||||
printf("<TARGET> Connected to QSPY on Host=%s:%d\n",
|
||||
hostName, port_remote);
|
||||
|
||||
pthread_attr_t attr;
|
||||
struct sched_param param;
|
||||
pthread_t idle;
|
||||
|
||||
// SCHED_FIFO corresponds to real-time preemptive priority-based
|
||||
// scheduler.
|
||||
// NOTE: This scheduling policy requires the superuser priviledges
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
|
||||
param.sched_priority = sched_get_priority_min(SCHED_FIFO);
|
||||
|
||||
pthread_attr_setschedparam(&attr, ¶m);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
if (pthread_create(&idle, &attr, &idleThread, 0) != 0) {
|
||||
// Creating the p-thread with the SCHED_FIFO policy failed.
|
||||
// Most probably this application has no superuser privileges,
|
||||
// so we just fall back to the default SCHED_OTHER policy
|
||||
// and priority 0.
|
||||
pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
|
||||
param.sched_priority = 0;
|
||||
pthread_attr_setschedparam(&attr, ¶m);
|
||||
if (pthread_create(&idle, &attr, &idleThread, 0) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
return (uint8_t)1; // success
|
||||
|
||||
error:
|
||||
return (uint8_t)0; // failure
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void QS_onCleanup(void) {
|
||||
l_running = (uint8_t)0;
|
||||
if (l_sock != INVALID_SOCKET) {
|
||||
close(l_sock);
|
||||
l_sock = INVALID_SOCKET;
|
||||
}
|
||||
//printf("<TARGET> Disconnected from QSPY via TCP/IP\n");
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void QS_onFlush(void) {
|
||||
if (l_sock != INVALID_SOCKET) { // socket initialized?
|
||||
uint16_t nBytes = QS_TX_SIZE;
|
||||
uint8_t const *data;
|
||||
while ((data = QS_getBlock(&nBytes)) != (uint8_t *)0) {
|
||||
send(l_sock, (char const *)data, nBytes, 0);
|
||||
nBytes = QS_TX_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*..........................................................................*/
|
||||
static void *idleThread(void *par) { // the expected P-Thread signature
|
||||
fd_set readSet;
|
||||
FD_ZERO(&readSet);
|
||||
|
||||
(void)par; /* unused parameter */
|
||||
l_running = (uint8_t)1;
|
||||
while (l_running) {
|
||||
static struct timeval timeout = {
|
||||
(long)0, (long)(QS_IMEOUT_MS * 1000)
|
||||
};
|
||||
int nrec;
|
||||
uint16_t nBytes;
|
||||
uint8_t const *block;
|
||||
|
||||
FD_SET(l_sock, &readSet); /* the socket */
|
||||
|
||||
/* selective, timed blocking on the TCP/IP socket... */
|
||||
timeout.tv_usec = (long)(QS_IMEOUT_MS * 1000);
|
||||
nrec = select(l_sock + 1, &readSet,
|
||||
(fd_set *)0, (fd_set *)0, &timeout);
|
||||
if (nrec < 0) {
|
||||
printf(" <CONS> ERROR select() errno=%d\n", errno);
|
||||
QS_onCleanup();
|
||||
exit(-2);
|
||||
}
|
||||
else if (nrec > 0) {
|
||||
if (FD_ISSET(l_sock, &readSet)) { /* socket ready to read? */
|
||||
uint8_t buf[QS_RX_SIZE];
|
||||
int status = recv(l_sock, (char *)buf, (int)sizeof(buf), 0);
|
||||
while (status > 0) { /* any data received? */
|
||||
uint8_t *pb;
|
||||
int i = (int)QS_rxGetNfree();
|
||||
if (i > status) {
|
||||
i = status;
|
||||
}
|
||||
status -= i;
|
||||
/* reorder the received bytes into QS-RX buffer */
|
||||
for (pb = &buf[0]; i > 0; --i, ++pb) {
|
||||
QS_RX_PUT(*pb);
|
||||
}
|
||||
QS_rxParse(); /* parse all n-bytes of data */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nBytes = QS_TX_SIZE;
|
||||
//QF_CRIT_ENTRY(dummy);
|
||||
block = QS_getBlock(&nBytes);
|
||||
//QF_CRIT_EXIT(dummy);
|
||||
|
||||
if (block != (uint8_t *)0) {
|
||||
send(l_sock, (char const *)block, nBytes, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0; // return success
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
#elif (QS_IMPL_OPTION == 2)
|
||||
|
||||
/*
|
||||
* 2. Direct linking with the QSPY host application to perform direct output
|
||||
* to the console from the running application. (This option requires
|
||||
* the QSPY source code, which is part of the QTools collection).
|
||||
*/
|
||||
#include "qspy.h"
|
||||
|
||||
/*..........................................................................*/
|
||||
static void *idleThread(void *par); // the expected P-Thread signature
|
||||
static uint8_t l_running;
|
||||
|
||||
/*..........................................................................*/
|
||||
bool QS_onStartup(void const */*arg*/) {
|
||||
static uint8_t qsBuf[4*1024]; // 4K buffer for Quantum Spy
|
||||
initBuf(qsBuf, sizeof(qsBuf));
|
||||
|
||||
QSPY_config(QP_VERSION, // version
|
||||
QS_OBJ_PTR_SIZE, // objPtrSize
|
||||
QS_FUN_PTR_SIZE, // funPtrSize
|
||||
QS_TIME_SIZE, // tstampSize
|
||||
Q_SIGNAL_SIZE, // sigSize,
|
||||
QF_EVENT_SIZ_SIZE, // evtSize
|
||||
QF_EQUEUE_CTR_SIZE, // queueCtrSize
|
||||
QF_MPOOL_CTR_SIZE, // poolCtrSize
|
||||
QF_MPOOL_SIZ_SIZE, // poolBlkSize
|
||||
QF_TIMEEVT_CTR_SIZE,// tevtCtrSize
|
||||
(void *)0, // matFile,
|
||||
(void *)0,
|
||||
(QSPY_CustParseFun)0); // customized parser function
|
||||
|
||||
pthread_attr_t attr;
|
||||
struct sched_param param;
|
||||
pthread_t idle;
|
||||
|
||||
// SCHED_FIFO corresponds to real-time preemptive priority-based
|
||||
// scheduler.
|
||||
// NOTE: This scheduling policy requires the superuser priviledges
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
|
||||
param.sched_priority = sched_get_priority_min(SCHED_FIFO);
|
||||
|
||||
pthread_attr_setschedparam(&attr, ¶m);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
if (pthread_create(&idle, &attr, &idleThread, 0) != 0) {
|
||||
// Creating the p-thread with the SCHED_FIFO policy failed.
|
||||
// Most probably this application has no superuser privileges,
|
||||
// so we just fall back to the default SCHED_OTHER policy
|
||||
// and priority 0.
|
||||
pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
|
||||
param.sched_priority = 0;
|
||||
pthread_attr_setschedparam(&attr, ¶m);
|
||||
if (pthread_create(&idle, &attr, &idleThread, 0) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
return true;
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void QS_onCleanup(void) {
|
||||
l_running = (uint8_t)0;
|
||||
QSPY_stop();
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void QS_onFlush(void) {
|
||||
uint16_t nBytes = 1024U;
|
||||
uint8_t const *block;
|
||||
while ((block = QS_getBlock(&nBytes)) != (uint8_t *)0) {
|
||||
QSPY_parse(block, nBytes);
|
||||
nBytes = 1024U;
|
||||
}
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void QSPY_onPrintLn(void) {
|
||||
fputs(QSPY_line, stdout);
|
||||
fputc('\n', stdout);
|
||||
}
|
||||
/*..........................................................................*/
|
||||
static void *idleThread(void *par) { // the expected P-Thread signature
|
||||
(void)par;
|
||||
|
||||
l_running = (uint8_t)1;
|
||||
while (l_running) {
|
||||
uint16_t nBytes = 256U;
|
||||
uint8_t const *block;
|
||||
struct timeval timeout = { 0, 10000 }; // timeout for select()
|
||||
|
||||
QF_CRIT_ENTRY(dummy);
|
||||
block = QS_getBlock(&nBytes);
|
||||
QF_CRIT_EXIT(dummy);
|
||||
|
||||
if (block != (uint8_t *)0) {
|
||||
QSPY_parse(block, nBytes);
|
||||
}
|
||||
select(0, 0, 0, 0, &timeout); // sleep for a while
|
||||
}
|
||||
return 0; // return success
|
||||
}
|
||||
|
||||
#else
|
||||
#error Incorrect value of the QS_IMPL_OPTION macro
|
||||
#endif // QS_IMPL_OPTION
|
||||
|
||||
//............................................................................
|
||||
QSTimeCtr QS_onGetTime(void) {
|
||||
return (QSTimeCtr)clock(); // see NOTE01
|
||||
}
|
||||
//............................................................................
|
||||
void QS_onReset(void) {
|
||||
QS_onCleanup();
|
||||
exit(0);
|
||||
}
|
||||
//............................................................................
|
||||
//! callback function to execute a user command (to be implemented in BSP)
|
||||
void QS_onCommand(uint8_t cmdId, uint32_t param1,
|
||||
uint32_t param2, uint32_t param3)
|
||||
{
|
||||
(void)cmdId; // unused parameter
|
||||
(void)param1; // unused parameter
|
||||
(void)param2; // unused parameter
|
||||
(void)param3; // unused parameter
|
||||
//TBD
|
||||
}
|
||||
|
||||
//****************************************************************************
|
||||
// NOTE01:
|
||||
// clock() is the most portable facility, but might not provide the desired
|
||||
// granularity. Other, less-portable alternatives are clock_gettime(),
|
||||
// rdtsc(), or gettimeofday().
|
||||
//
|
||||
|
||||
#endif // Q_SPY
|
||||
/*--------------------------------------------------------------------------*/
|
47
examples/posix-qv/dpp/bsp.h
Normal file
47
examples/posix-qv/dpp/bsp.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*****************************************************************************
|
||||
* Product: DPP example
|
||||
* Last updated for version 6.2.0
|
||||
* Last updated on 2016-11-30
|
||||
*
|
||||
* Q u a n t u m L e a P s
|
||||
* ---------------------------
|
||||
* innovating embedded systems
|
||||
*
|
||||
* Copyright (C) 2002-2018 Quantum Leaps, LLC. All rights reserved.
|
||||
*
|
||||
* This program is open source software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alternatively, this program may be distributed and modified under the
|
||||
* terms of Quantum Leaps commercial licenses, which expressly supersede
|
||||
* the GNU General Public License and are specifically designed for
|
||||
* licensees interested in retaining the proprietary status of their code.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Contact information:
|
||||
* https://www.state-machine.com
|
||||
* mailto:info@state-machine.com
|
||||
*****************************************************************************/
|
||||
#ifndef bsp_h
|
||||
#define bsp_h
|
||||
|
||||
#define BSP_TICKS_PER_SEC 100U
|
||||
|
||||
void BSP_init(int argc, char **argv);
|
||||
void BSP_displayPhilStat(uint8_t n, char const *stat);
|
||||
void BSP_displayPaused(uint8_t paused);
|
||||
void BSP_terminate(int16_t result);
|
||||
|
||||
void BSP_randomSeed(uint32_t seed); /* random seed */
|
||||
uint32_t BSP_random(void); /* pseudo-random generator */
|
||||
|
||||
#endif /* bsp_h */
|
72
examples/posix-qv/dpp/dpp.h
Normal file
72
examples/posix-qv/dpp/dpp.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*$file${.::dpp.h} #########################################################*/
|
||||
/*
|
||||
* Model: dpp.qm
|
||||
* File: ${.::dpp.h}
|
||||
*
|
||||
* This code has been generated by QM tool (https://state-machine.com/qm).
|
||||
* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
|
||||
*
|
||||
* This program is open source software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
/*$endhead${.::dpp.h} ######################################################*/
|
||||
#ifndef dpp_h
|
||||
#define dpp_h
|
||||
|
||||
enum DPPSignals {
|
||||
EAT_SIG = Q_USER_SIG, /* published by Table to let a philosopher eat */
|
||||
DONE_SIG, /* published by Philosopher when done eating */
|
||||
PAUSE_SIG, /* published by BSP to pause serving forks */
|
||||
SERVE_SIG, /* published by BSP to serve re-start serving forks */
|
||||
TEST_SIG, /* published by BSP to test the application */
|
||||
MAX_PUB_SIG, /* the last published signal */
|
||||
|
||||
HUNGRY_SIG, /* posted direclty to Table from hungry Philo */
|
||||
TIMEOUT_SIG, /* used by Philosophers for time events */
|
||||
MAX_SIG /* the last signal */
|
||||
};
|
||||
|
||||
/*$declare${Events::TableEvt} ##############################################*/
|
||||
/*${Events::TableEvt} ......................................................*/
|
||||
typedef struct {
|
||||
/* protected: */
|
||||
QEvt super;
|
||||
|
||||
/* public: */
|
||||
uint8_t philoNum;
|
||||
} TableEvt;
|
||||
/*$enddecl${Events::TableEvt} ##############################################*/
|
||||
|
||||
/* number of philosophers */
|
||||
#define N_PHILO ((uint8_t)5)
|
||||
|
||||
/*$declare${AOs::Philo_ctor} ###############################################*/
|
||||
/*${AOs::Philo_ctor} .......................................................*/
|
||||
void Philo_ctor(void);
|
||||
/*$enddecl${AOs::Philo_ctor} ###############################################*/
|
||||
/*$declare${AOs::AO_Philo[N_PHILO]} ########################################*/
|
||||
extern QMActive * const AO_Philo[N_PHILO];
|
||||
/*$enddecl${AOs::AO_Philo[N_PHILO]} ########################################*/
|
||||
|
||||
/*$declare${AOs::Table_ctor} ###############################################*/
|
||||
/*${AOs::Table_ctor} .......................................................*/
|
||||
void Table_ctor(void);
|
||||
/*$enddecl${AOs::Table_ctor} ###############################################*/
|
||||
/*$declare${AOs::AO_Table} #################################################*/
|
||||
extern QActive * const AO_Table;
|
||||
/*$enddecl${AOs::AO_Table} #################################################*/
|
||||
|
||||
#ifdef qxk_h
|
||||
void Test1_ctor(void);
|
||||
extern QXThread * const XT_Test1;
|
||||
void Test2_ctor(void);
|
||||
extern QXThread * const XT_Test2;
|
||||
#endif /* qxk_h */
|
||||
|
||||
#endif /* dpp_h */
|
445
examples/posix-qv/dpp/dpp.qm
Normal file
445
examples/posix-qv/dpp/dpp.qm
Normal file
@ -0,0 +1,445 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<model version="4.1.2" links="0">
|
||||
<documentation>Dining Philosopher Problem example
|
||||
NOTE: Requries QP5.</documentation>
|
||||
<framework name="qpc"/>
|
||||
<package name="Events" stereotype="0x01">
|
||||
<class name="TableEvt" superclass="qpc::QEvt">
|
||||
<attribute name="philoNum" type="uint8_t" visibility="0x00" properties="0x00"/>
|
||||
</class>
|
||||
</package>
|
||||
<package name="AOs" stereotype="0x02">
|
||||
<class name="Philo" superclass="qpc::QActive">
|
||||
<attribute name="timeEvt" type="QTimeEvt" visibility="0x02" properties="0x00"/>
|
||||
<statechart>
|
||||
<initial target="../1">
|
||||
<action>static uint8_t registered = (uint8_t)0; /* starts off with 0, per C-standard */
|
||||
(void)e; /* suppress the compiler warning about unused parameter */
|
||||
if (registered == (uint8_t)0) {
|
||||
registered = (uint8_t)1;
|
||||
|
||||
QS_OBJ_DICTIONARY(&l_philo[0]);
|
||||
QS_OBJ_DICTIONARY(&l_philo[0].timeEvt);
|
||||
QS_OBJ_DICTIONARY(&l_philo[1]);
|
||||
QS_OBJ_DICTIONARY(&l_philo[1].timeEvt);
|
||||
QS_OBJ_DICTIONARY(&l_philo[2]);
|
||||
QS_OBJ_DICTIONARY(&l_philo[2].timeEvt);
|
||||
QS_OBJ_DICTIONARY(&l_philo[3]);
|
||||
QS_OBJ_DICTIONARY(&l_philo[3].timeEvt);
|
||||
QS_OBJ_DICTIONARY(&l_philo[4]);
|
||||
QS_OBJ_DICTIONARY(&l_philo[4].timeEvt);
|
||||
|
||||
QS_FUN_DICTIONARY(&Philo_initial);
|
||||
QS_FUN_DICTIONARY(&Philo_thinking);
|
||||
QS_FUN_DICTIONARY(&Philo_hungry);
|
||||
QS_FUN_DICTIONARY(&Philo_eating);
|
||||
}
|
||||
QS_SIG_DICTIONARY(HUNGRY_SIG, me); /* signal for each Philos */
|
||||
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); /* signal for each Philos */
|
||||
|
||||
QActive_subscribe(&me->super, EAT_SIG);
|
||||
QActive_subscribe(&me->super, TEST_SIG);</action>
|
||||
<initial_glyph conn="2,3,5,1,20,5,-3">
|
||||
<action box="0,-2,6,2"/>
|
||||
</initial_glyph>
|
||||
</initial>
|
||||
<state name="thinking">
|
||||
<entry>QTimeEvt_armX(&me->timeEvt, THINK_TIME, 0U);</entry>
|
||||
<exit>QTimeEvt_disarm(&me->timeEvt);</exit>
|
||||
<tran trig="TIMEOUT" target="../../2">
|
||||
<tran_glyph conn="2,13,3,1,20,12,-3">
|
||||
<action box="0,-2,6,2"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<tran trig="EAT, DONE">
|
||||
<action>/* EAT or DONE must be for other Philos than this one */
|
||||
Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(me));</action>
|
||||
<tran_glyph conn="2,17,3,-1,13">
|
||||
<action box="0,-2,14,2"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<tran trig="TEST">
|
||||
<tran_glyph conn="2,20,3,-1,13">
|
||||
<action box="0,-2,11,4"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<state_glyph node="2,5,17,16">
|
||||
<entry box="1,2,5,2"/>
|
||||
<exit box="1,4,6,2"/>
|
||||
</state_glyph>
|
||||
</state>
|
||||
<state name="hungry">
|
||||
<entry>TableEvt *pe = Q_NEW(TableEvt, HUNGRY_SIG);
|
||||
pe->philoNum = PHILO_ID(me);
|
||||
QACTIVE_POST(AO_Table, &pe->super, me);</entry>
|
||||
<tran trig="EAT">
|
||||
<choice target="../../../3">
|
||||
<guard>Q_EVT_CAST(TableEvt)->philoNum == PHILO_ID(me)</guard>
|
||||
<choice_glyph conn="15,30,5,1,7,13,-3">
|
||||
<action box="1,0,19,4"/>
|
||||
</choice_glyph>
|
||||
</choice>
|
||||
<tran_glyph conn="2,30,3,-1,13">
|
||||
<action box="0,-2,14,2"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<tran trig="DONE">
|
||||
<action>/* DONE must be for other Philos than this one */
|
||||
Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(me));</action>
|
||||
<tran_glyph conn="2,36,3,-1,14">
|
||||
<action box="0,-2,14,2"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<state_glyph node="2,23,17,16">
|
||||
<entry box="1,2,5,2"/>
|
||||
</state_glyph>
|
||||
</state>
|
||||
<state name="eating">
|
||||
<entry>QTimeEvt_armX(&me->timeEvt, EAT_TIME, 0U);</entry>
|
||||
<exit>TableEvt *pe = Q_NEW(TableEvt, DONE_SIG);
|
||||
pe->philoNum = PHILO_ID(me);
|
||||
QF_PUBLISH(&pe->super, me);</exit>
|
||||
<tran trig="TIMEOUT" target="../../1">
|
||||
<tran_glyph conn="2,51,3,1,22,-41,-5">
|
||||
<action box="0,-2,6,2"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<tran trig="EAT, DONE">
|
||||
<action>/* EAT or DONE must be for other Philos than this one */
|
||||
Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(me));</action>
|
||||
<tran_glyph conn="2,55,3,-1,13">
|
||||
<action box="0,-2,14,2"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<state_glyph node="2,41,17,18">
|
||||
<entry box="1,2,5,2"/>
|
||||
<exit box="1,4,5,2"/>
|
||||
</state_glyph>
|
||||
</state>
|
||||
<state_diagram size="37,61"/>
|
||||
</statechart>
|
||||
</class>
|
||||
<class name="Table" superclass="qpc::QActive">
|
||||
<attribute name="fork[N_PHILO]" type="uint8_t" visibility="0x02" properties="0x00"/>
|
||||
<attribute name="isHungry[N_PHILO]" type="uint8_t" visibility="0x02" properties="0x00"/>
|
||||
<statechart>
|
||||
<initial target="../1/2">
|
||||
<action>uint8_t n;
|
||||
(void)e; /* suppress the compiler warning about unused parameter */
|
||||
|
||||
QS_OBJ_DICTIONARY(&l_table);
|
||||
QS_FUN_DICTIONARY(&QHsm_top);
|
||||
QS_FUN_DICTIONARY(&Table_initial);
|
||||
QS_FUN_DICTIONARY(&Table_active);
|
||||
QS_FUN_DICTIONARY(&Table_serving);
|
||||
QS_FUN_DICTIONARY(&Table_paused);
|
||||
|
||||
QS_SIG_DICTIONARY(DONE_SIG, (void *)0); /* global signals */
|
||||
QS_SIG_DICTIONARY(EAT_SIG, (void *)0);
|
||||
QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0);
|
||||
QS_SIG_DICTIONARY(SERVE_SIG, (void *)0);
|
||||
QS_SIG_DICTIONARY(TEST_SIG, (void *)0);
|
||||
|
||||
QS_SIG_DICTIONARY(HUNGRY_SIG, me); /* signal just for Table */
|
||||
|
||||
QActive_subscribe(&me->super, DONE_SIG);
|
||||
QActive_subscribe(&me->super, PAUSE_SIG);
|
||||
QActive_subscribe(&me->super, SERVE_SIG);
|
||||
QActive_subscribe(&me->super, TEST_SIG);
|
||||
|
||||
for (n = 0U; n < N_PHILO; ++n) {
|
||||
me->fork[n] = FREE;
|
||||
me->isHungry[n] = 0U;
|
||||
BSP_displayPhilStat(n, "thinking");
|
||||
}</action>
|
||||
<initial_glyph conn="3,3,5,1,44,18,-9">
|
||||
<action box="0,-2,6,2"/>
|
||||
</initial_glyph>
|
||||
</initial>
|
||||
<state name="active">
|
||||
<tran trig="TEST">
|
||||
<tran_glyph conn="2,11,3,-1,14">
|
||||
<action box="0,-2,11,4"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<tran trig="EAT">
|
||||
<action>Q_ERROR();</action>
|
||||
<tran_glyph conn="2,15,3,-1,14">
|
||||
<action box="0,-2,10,4"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<state name="serving">
|
||||
<entry brief="give pending permissions to eat">uint8_t n;
|
||||
for (n = 0U; n < N_PHILO; ++n) { /* give permissions to eat... */
|
||||
if ((me->isHungry[n] != 0U)
|
||||
&& (me->fork[LEFT(n)] == FREE)
|
||||
&& (me->fork[n] == FREE))
|
||||
{
|
||||
TableEvt *te;
|
||||
|
||||
me->fork[LEFT(n)] = USED;
|
||||
me->fork[n] = USED;
|
||||
te = Q_NEW(TableEvt, EAT_SIG);
|
||||
te->philoNum = n;
|
||||
QF_PUBLISH(&te->super, me);
|
||||
me->isHungry[n] = 0U;
|
||||
BSP_displayPhilStat(n, "eating ");
|
||||
}
|
||||
}</entry>
|
||||
<tran trig="HUNGRY">
|
||||
<action>uint8_t n, m;
|
||||
|
||||
n = Q_EVT_CAST(TableEvt)->philoNum;
|
||||
/* phil ID must be in range and he must be not hungry */
|
||||
Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U));
|
||||
|
||||
BSP_displayPhilStat(n, "hungry ");
|
||||
m = LEFT(n);</action>
|
||||
<choice>
|
||||
<guard brief="both free">(me->fork[m] == FREE) && (me->fork[n] == FREE)</guard>
|
||||
<action>TableEvt *pe;
|
||||
me->fork[m] = USED;
|
||||
me->fork[n] = USED;
|
||||
pe = Q_NEW(TableEvt, EAT_SIG);
|
||||
pe->philoNum = n;
|
||||
QF_PUBLISH(&pe->super, me);
|
||||
BSP_displayPhilStat(n, "eating ");</action>
|
||||
<choice_glyph conn="19,26,5,-1,10">
|
||||
<action box="1,0,10,2"/>
|
||||
</choice_glyph>
|
||||
</choice>
|
||||
<choice>
|
||||
<guard>else</guard>
|
||||
<action>me->isHungry[n] = 1U;</action>
|
||||
<choice_glyph conn="19,26,4,-1,5,10">
|
||||
<action box="1,5,6,2"/>
|
||||
</choice_glyph>
|
||||
</choice>
|
||||
<tran_glyph conn="4,26,3,-1,15">
|
||||
<action box="0,-2,8,2"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<tran trig="DONE">
|
||||
<action>uint8_t n, m;
|
||||
TableEvt *pe;
|
||||
|
||||
n = Q_EVT_CAST(TableEvt)->philoNum;
|
||||
/* phil ID must be in range and he must be not hungry */
|
||||
Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U));
|
||||
|
||||
BSP_displayPhilStat(n, "thinking");
|
||||
m = LEFT(n);
|
||||
/* both forks of Phil[n] must be used */
|
||||
Q_ASSERT((me->fork[n] == USED) && (me->fork[m] == USED));
|
||||
|
||||
me->fork[m] = FREE;
|
||||
me->fork[n] = FREE;
|
||||
m = RIGHT(n); /* check the right neighbor */
|
||||
|
||||
if ((me->isHungry[m] != 0U) && (me->fork[m] == FREE)) {
|
||||
me->fork[n] = USED;
|
||||
me->fork[m] = USED;
|
||||
me->isHungry[m] = 0U;
|
||||
pe = Q_NEW(TableEvt, EAT_SIG);
|
||||
pe->philoNum = m;
|
||||
QF_PUBLISH(&pe->super, me);
|
||||
BSP_displayPhilStat(m, "eating ");
|
||||
}
|
||||
m = LEFT(n); /* check the left neighbor */
|
||||
n = LEFT(m); /* left fork of the left neighbor */
|
||||
if ((me->isHungry[m] != 0U) && (me->fork[n] == FREE)) {
|
||||
me->fork[m] = USED;
|
||||
me->fork[n] = USED;
|
||||
me->isHungry[m] = 0U;
|
||||
pe = Q_NEW(TableEvt, EAT_SIG);
|
||||
pe->philoNum = m;
|
||||
QF_PUBLISH(&pe->super, me);
|
||||
BSP_displayPhilStat(m, "eating ");
|
||||
}</action>
|
||||
<tran_glyph conn="4,34,3,-1,15">
|
||||
<action box="0,-2,6,2"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<tran trig="EAT">
|
||||
<action>Q_ERROR();</action>
|
||||
<tran_glyph conn="4,37,3,-1,15">
|
||||
<action box="0,-2,12,4"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<tran trig="PAUSE" target="../../3">
|
||||
<tran_glyph conn="4,41,3,1,37,6,-3">
|
||||
<action box="0,-2,7,2"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<state_glyph node="4,19,34,24">
|
||||
<entry box="1,2,27,2"/>
|
||||
</state_glyph>
|
||||
</state>
|
||||
<state name="paused">
|
||||
<entry>BSP_displayPaused(1U);</entry>
|
||||
<exit>BSP_displayPaused(0U);</exit>
|
||||
<tran trig="SERVE" target="../../2">
|
||||
<tran_glyph conn="4,57,3,1,39,-20,-5">
|
||||
<action box="0,-2,7,2"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<tran trig="HUNGRY">
|
||||
<action>uint8_t n = Q_EVT_CAST(TableEvt)->philoNum;
|
||||
/* philo ID must be in range and he must be not hungry */
|
||||
Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U));
|
||||
me->isHungry[n] = 1U;
|
||||
BSP_displayPhilStat(n, "hungry ");</action>
|
||||
<tran_glyph conn="4,60,3,-1,15">
|
||||
<action box="0,-2,6,2"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<tran trig="DONE">
|
||||
<action>uint8_t n, m;
|
||||
|
||||
n = Q_EVT_CAST(TableEvt)->philoNum;
|
||||
/* phil ID must be in range and he must be not hungry */
|
||||
Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U));
|
||||
|
||||
BSP_displayPhilStat(n, "thinking");
|
||||
m = LEFT(n);
|
||||
/* both forks of Phil[n] must be used */
|
||||
Q_ASSERT((me->fork[n] == USED) && (me->fork[m] == USED));
|
||||
|
||||
me->fork[m] = FREE;
|
||||
me->fork[n] = FREE;</action>
|
||||
<tran_glyph conn="4,63,3,-1,15">
|
||||
<action box="0,-2,6,2"/>
|
||||
</tran_glyph>
|
||||
</tran>
|
||||
<state_glyph node="4,45,34,20">
|
||||
<entry box="1,2,18,4"/>
|
||||
<exit box="1,6,18,4"/>
|
||||
</state_glyph>
|
||||
</state>
|
||||
<state_glyph node="2,5,43,62"/>
|
||||
</state>
|
||||
<state_diagram size="49,69"/>
|
||||
</statechart>
|
||||
</class>
|
||||
<attribute name="AO_Philo[N_PHILO]" type="QMActive * const" visibility="0x00" properties="0x00"/>
|
||||
<attribute name="AO_Table" type="QActive * const" visibility="0x00" properties="0x00"/>
|
||||
<operation name="Philo_ctor" type="void" visibility="0x00" properties="0x00">
|
||||
<code>uint8_t n;
|
||||
Philo *me;
|
||||
for (n = 0U; n < N_PHILO; ++n) {
|
||||
me = &l_philo[n];
|
||||
QActive_ctor(&me->super, Q_STATE_CAST(&Philo_initial));
|
||||
QTimeEvt_ctorX(&me->timeEvt, &me->super, TIMEOUT_SIG, 0U);
|
||||
}</code>
|
||||
</operation>
|
||||
<operation name="Table_ctor" type="void" visibility="0x00" properties="0x00">
|
||||
<code>uint8_t n;
|
||||
Table *me = &l_table;
|
||||
|
||||
QActive_ctor(&me->super, Q_STATE_CAST(&Table_initial));
|
||||
|
||||
for (n = 0U; n < N_PHILO; ++n) {
|
||||
me->fork[n] = FREE;
|
||||
me->isHungry[n] = 0U;
|
||||
}</code>
|
||||
</operation>
|
||||
</package>
|
||||
<directory name=".">
|
||||
<file name="dpp.h">
|
||||
<text>#ifndef dpp_h
|
||||
#define dpp_h
|
||||
|
||||
enum DPPSignals {
|
||||
EAT_SIG = Q_USER_SIG, /* published by Table to let a philosopher eat */
|
||||
DONE_SIG, /* published by Philosopher when done eating */
|
||||
PAUSE_SIG, /* published by BSP to pause serving forks */
|
||||
SERVE_SIG, /* published by BSP to serve re-start serving forks */
|
||||
TEST_SIG, /* published by BSP to test the application */
|
||||
MAX_PUB_SIG, /* the last published signal */
|
||||
|
||||
HUNGRY_SIG, /* posted direclty to Table from hungry Philo */
|
||||
TIMEOUT_SIG, /* used by Philosophers for time events */
|
||||
MAX_SIG /* the last signal */
|
||||
};
|
||||
|
||||
$declare(Events::TableEvt)
|
||||
|
||||
/* number of philosophers */
|
||||
#define N_PHILO ((uint8_t)5)
|
||||
|
||||
$declare(AOs::Philo_ctor)
|
||||
$declare(AOs::AO_Philo[N_PHILO])
|
||||
|
||||
$declare(AOs::Table_ctor)
|
||||
$declare(AOs::AO_Table)
|
||||
|
||||
#ifdef qxk_h
|
||||
void Test1_ctor(void);
|
||||
extern QXThread * const XT_Test1;
|
||||
void Test2_ctor(void);
|
||||
extern QXThread * const XT_Test2;
|
||||
#endif /* qxk_h */
|
||||
|
||||
#endif /* dpp_h */</text>
|
||||
</file>
|
||||
<file name="philo.c">
|
||||
<text>#include "qpc.h"
|
||||
#include "dpp.h"
|
||||
#include "bsp.h"
|
||||
|
||||
Q_DEFINE_THIS_FILE
|
||||
|
||||
/* Active object class -----------------------------------------------------*/
|
||||
$declare(AOs::Philo)
|
||||
|
||||
/* Local objects -----------------------------------------------------------*/
|
||||
static Philo l_philo[N_PHILO]; /* storage for all Philos */
|
||||
|
||||
#define THINK_TIME \
|
||||
(QTimeEvtCtr)((BSP_random() % BSP_TICKS_PER_SEC) + (BSP_TICKS_PER_SEC/2U))
|
||||
#define EAT_TIME \
|
||||
(QTimeEvtCtr)((BSP_random() % BSP_TICKS_PER_SEC) + BSP_TICKS_PER_SEC)
|
||||
|
||||
/* helper macro to provide the ID of Philo "me_" */
|
||||
#define PHILO_ID(me_) ((uint8_t)((me_) - l_philo))
|
||||
|
||||
/* Global objects ----------------------------------------------------------*/
|
||||
QMActive * const AO_Philo[N_PHILO] = { /* "opaque" pointers to Philo AO */
|
||||
&l_philo[0].super,
|
||||
&l_philo[1].super,
|
||||
&l_philo[2].super,
|
||||
&l_philo[3].super,
|
||||
&l_philo[4].super
|
||||
};
|
||||
|
||||
/* Philo definition --------------------------------------------------------*/
|
||||
$define(AOs::Philo_ctor)
|
||||
$define(AOs::Philo)</text>
|
||||
</file>
|
||||
<file name="table.c">
|
||||
<text>#include "qpc.h"
|
||||
#include "dpp.h"
|
||||
#include "bsp.h"
|
||||
|
||||
Q_DEFINE_THIS_FILE
|
||||
|
||||
/* Active object class -----------------------------------------------------*/
|
||||
$declare(AOs::Table)
|
||||
|
||||
#define RIGHT(n_) ((uint8_t)(((n_) + (N_PHILO - 1U)) % N_PHILO))
|
||||
#define LEFT(n_) ((uint8_t)(((n_) + 1U) % N_PHILO))
|
||||
#define FREE ((uint8_t)0)
|
||||
#define USED ((uint8_t)1)
|
||||
|
||||
/* Local objects -----------------------------------------------------------*/
|
||||
static Table l_table; /* the single instance of the Table active object */
|
||||
|
||||
/* Global-scope objects ----------------------------------------------------*/
|
||||
QMActive * const AO_Table = &l_table.super; /* "opaque" AO pointer */
|
||||
|
||||
/*..........................................................................*/
|
||||
$define(AOs::Table_ctor)
|
||||
$define(AOs::Table)</text>
|
||||
</file>
|
||||
</directory>
|
||||
</model>
|
87
examples/posix-qv/dpp/main.c
Normal file
87
examples/posix-qv/dpp/main.c
Normal file
@ -0,0 +1,87 @@
|
||||
/*****************************************************************************
|
||||
* Product: DPP example
|
||||
* Last Updated for Version: 6.2.0
|
||||
* Date of the Last Update: 2018-03-10
|
||||
*
|
||||
* Q u a n t u m L e a P s
|
||||
* ---------------------------
|
||||
* innovating embedded systems
|
||||
*
|
||||
* Copyright (C) Quantum Leaps, LLC. All rights reserved.
|
||||
*
|
||||
* This program is open source software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alternatively, this program may be distributed and modified under the
|
||||
* terms of Quantum Leaps commercial licenses, which expressly supersede
|
||||
* the GNU General Public License and are specifically designed for
|
||||
* licensees interested in retaining the proprietary status of their code.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Contact information:
|
||||
* https://www.state-machine.com
|
||||
* mailto:info@state-machine.com
|
||||
*****************************************************************************/
|
||||
#include "qpc.h"
|
||||
#include "dpp.h"
|
||||
#include "bsp.h"
|
||||
|
||||
/*..........................................................................*/
|
||||
int main(int argc, char *argv[]) {
|
||||
static QEvt const *tableQueueSto[N_PHILO];
|
||||
static QEvt const *philoQueueSto[N_PHILO][N_PHILO];
|
||||
static QSubscrList subscrSto[MAX_PUB_SIG];
|
||||
static QF_MPOOL_EL(TableEvt) smlPoolSto[2*N_PHILO]; /* small pool */
|
||||
uint8_t n;
|
||||
|
||||
Philo_ctor(); /* instantiate all Philosopher active objects */
|
||||
Table_ctor(); /* instantiate the Table active object */
|
||||
|
||||
QF_init(); /* initialize the framework and the underlying RT kernel */
|
||||
BSP_init(argc, argv); /* initialize the Board Support Package */
|
||||
|
||||
/* object dictionaries... */
|
||||
QS_OBJ_DICTIONARY(smlPoolSto);
|
||||
QS_OBJ_DICTIONARY(tableQueueSto);
|
||||
QS_OBJ_DICTIONARY(philoQueueSto[0]);
|
||||
QS_OBJ_DICTIONARY(philoQueueSto[1]);
|
||||
QS_OBJ_DICTIONARY(philoQueueSto[2]);
|
||||
QS_OBJ_DICTIONARY(philoQueueSto[3]);
|
||||
QS_OBJ_DICTIONARY(philoQueueSto[4]);
|
||||
|
||||
/* initialize publish-subscribe... */
|
||||
QF_psInit(subscrSto, Q_DIM(subscrSto));
|
||||
|
||||
/* initialize event pools... */
|
||||
QF_poolInit(smlPoolSto, sizeof(smlPoolSto), sizeof(smlPoolSto[0]));
|
||||
|
||||
/* start the active objects... */
|
||||
for (n = 0U; n < N_PHILO; ++n) {
|
||||
QACTIVE_START(AO_Philo[n], /* AO to start */
|
||||
(uint_fast8_t)(n + 1), /* QP priority of the AO */
|
||||
philoQueueSto[n], /* event queue storage */
|
||||
Q_DIM(philoQueueSto[n]), /* queue length [events] */
|
||||
(void *)0, /* stack storage (not used) */
|
||||
0U, /* size of the stack [bytes] */
|
||||
(QEvt *)0); /* initialization event */
|
||||
}
|
||||
QACTIVE_START(AO_Table, /* AO to start */
|
||||
(uint_fast8_t)(N_PHILO + 1), /* QP priority of the AO */
|
||||
tableQueueSto, /* event queue storage */
|
||||
Q_DIM(tableQueueSto), /* queue length [events] */
|
||||
(void *)0, /* stack storage (not used) */
|
||||
0U, /* size of the stack [bytes] */
|
||||
(QEvt *)0); /* initialization event */
|
||||
|
||||
return QF_run(); /* run the QF application */
|
||||
}
|
||||
|
229
examples/posix-qv/dpp/philo.c
Normal file
229
examples/posix-qv/dpp/philo.c
Normal file
@ -0,0 +1,229 @@
|
||||
/*$file${.::philo.c} #######################################################*/
|
||||
/*
|
||||
* Model: dpp.qm
|
||||
* File: ${.::philo.c}
|
||||
*
|
||||
* This code has been generated by QM tool (https://state-machine.com/qm).
|
||||
* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
|
||||
*
|
||||
* This program is open source software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
/*$endhead${.::philo.c} ####################################################*/
|
||||
#include "qpc.h"
|
||||
#include "dpp.h"
|
||||
#include "bsp.h"
|
||||
|
||||
Q_DEFINE_THIS_FILE
|
||||
|
||||
/* Active object class -----------------------------------------------------*/
|
||||
/*$declare${AOs::Philo} ####################################################*/
|
||||
/*${AOs::Philo} ............................................................*/
|
||||
typedef struct {
|
||||
/* protected: */
|
||||
QActive super;
|
||||
|
||||
/* private: */
|
||||
QTimeEvt timeEvt;
|
||||
} Philo;
|
||||
|
||||
/* protected: */
|
||||
static QState Philo_initial(Philo * const me, QEvt const * const e);
|
||||
static QState Philo_thinking(Philo * const me, QEvt const * const e);
|
||||
static QState Philo_hungry(Philo * const me, QEvt const * const e);
|
||||
static QState Philo_eating(Philo * const me, QEvt const * const e);
|
||||
/*$enddecl${AOs::Philo} ####################################################*/
|
||||
|
||||
/* Local objects -----------------------------------------------------------*/
|
||||
static Philo l_philo[N_PHILO]; /* storage for all Philos */
|
||||
|
||||
#define THINK_TIME \
|
||||
(QTimeEvtCtr)((BSP_random() % BSP_TICKS_PER_SEC) + (BSP_TICKS_PER_SEC/2U))
|
||||
#define EAT_TIME \
|
||||
(QTimeEvtCtr)((BSP_random() % BSP_TICKS_PER_SEC) + BSP_TICKS_PER_SEC)
|
||||
|
||||
/* helper macro to provide the ID of Philo "me_" */
|
||||
#define PHILO_ID(me_) ((uint8_t)((me_) - l_philo))
|
||||
|
||||
/* Global objects ----------------------------------------------------------*/
|
||||
QMActive * const AO_Philo[N_PHILO] = { /* "opaque" pointers to Philo AO */
|
||||
&l_philo[0].super,
|
||||
&l_philo[1].super,
|
||||
&l_philo[2].super,
|
||||
&l_philo[3].super,
|
||||
&l_philo[4].super
|
||||
};
|
||||
|
||||
/* Philo definition --------------------------------------------------------*/
|
||||
/*$define${AOs::Philo_ctor} ################################################*/
|
||||
/* Check for the minimum required QP version */
|
||||
#if ((QP_VERSION < 601) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8)))
|
||||
#error qpc version 6.0.1 or higher required
|
||||
#endif
|
||||
/*${AOs::Philo_ctor} .......................................................*/
|
||||
void Philo_ctor(void) {
|
||||
uint8_t n;
|
||||
Philo *me;
|
||||
for (n = 0U; n < N_PHILO; ++n) {
|
||||
me = &l_philo[n];
|
||||
QActive_ctor(&me->super, Q_STATE_CAST(&Philo_initial));
|
||||
QTimeEvt_ctorX(&me->timeEvt, &me->super, TIMEOUT_SIG, 0U);
|
||||
}
|
||||
}
|
||||
/*$enddef${AOs::Philo_ctor} ################################################*/
|
||||
/*$define${AOs::Philo} #####################################################*/
|
||||
/*${AOs::Philo} ............................................................*/
|
||||
/*${AOs::Philo::SM} ........................................................*/
|
||||
static QState Philo_initial(Philo * const me, QEvt const * const e) {
|
||||
/*${AOs::Philo::SM::initial} */
|
||||
static uint8_t registered = (uint8_t)0; /* starts off with 0, per C-standard */
|
||||
(void)e; /* suppress the compiler warning about unused parameter */
|
||||
if (registered == (uint8_t)0) {
|
||||
registered = (uint8_t)1;
|
||||
|
||||
QS_OBJ_DICTIONARY(&l_philo[0]);
|
||||
QS_OBJ_DICTIONARY(&l_philo[0].timeEvt);
|
||||
QS_OBJ_DICTIONARY(&l_philo[1]);
|
||||
QS_OBJ_DICTIONARY(&l_philo[1].timeEvt);
|
||||
QS_OBJ_DICTIONARY(&l_philo[2]);
|
||||
QS_OBJ_DICTIONARY(&l_philo[2].timeEvt);
|
||||
QS_OBJ_DICTIONARY(&l_philo[3]);
|
||||
QS_OBJ_DICTIONARY(&l_philo[3].timeEvt);
|
||||
QS_OBJ_DICTIONARY(&l_philo[4]);
|
||||
QS_OBJ_DICTIONARY(&l_philo[4].timeEvt);
|
||||
|
||||
QS_FUN_DICTIONARY(&Philo_initial);
|
||||
QS_FUN_DICTIONARY(&Philo_thinking);
|
||||
QS_FUN_DICTIONARY(&Philo_hungry);
|
||||
QS_FUN_DICTIONARY(&Philo_eating);
|
||||
}
|
||||
QS_SIG_DICTIONARY(HUNGRY_SIG, me); /* signal for each Philos */
|
||||
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); /* signal for each Philos */
|
||||
|
||||
QActive_subscribe(&me->super, EAT_SIG);
|
||||
QActive_subscribe(&me->super, TEST_SIG);
|
||||
return Q_TRAN(&Philo_thinking);
|
||||
}
|
||||
/*${AOs::Philo::SM::thinking} ..............................................*/
|
||||
static QState Philo_thinking(Philo * const me, QEvt const * const e) {
|
||||
QState status_;
|
||||
switch (e->sig) {
|
||||
/*${AOs::Philo::SM::thinking} */
|
||||
case Q_ENTRY_SIG: {
|
||||
QTimeEvt_armX(&me->timeEvt, THINK_TIME, 0U);
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Philo::SM::thinking} */
|
||||
case Q_EXIT_SIG: {
|
||||
QTimeEvt_disarm(&me->timeEvt);
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Philo::SM::thinking::TIMEOUT} */
|
||||
case TIMEOUT_SIG: {
|
||||
status_ = Q_TRAN(&Philo_hungry);
|
||||
break;
|
||||
}
|
||||
/*${AOs::Philo::SM::thinking::EAT, DONE} */
|
||||
case EAT_SIG: /* intentionally fall through */
|
||||
case DONE_SIG: {
|
||||
/* EAT or DONE must be for other Philos than this one */
|
||||
Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(me));
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Philo::SM::thinking::TEST} */
|
||||
case TEST_SIG: {
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
status_ = Q_SUPER(&QHsm_top);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status_;
|
||||
}
|
||||
/*${AOs::Philo::SM::hungry} ................................................*/
|
||||
static QState Philo_hungry(Philo * const me, QEvt const * const e) {
|
||||
QState status_;
|
||||
switch (e->sig) {
|
||||
/*${AOs::Philo::SM::hungry} */
|
||||
case Q_ENTRY_SIG: {
|
||||
TableEvt *pe = Q_NEW(TableEvt, HUNGRY_SIG);
|
||||
pe->philoNum = PHILO_ID(me);
|
||||
QACTIVE_POST(AO_Table, &pe->super, me);
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Philo::SM::hungry::EAT} */
|
||||
case EAT_SIG: {
|
||||
/*${AOs::Philo::SM::hungry::EAT::[Q_EVT_CAST(TableEvt)->philoNum=~} */
|
||||
if (Q_EVT_CAST(TableEvt)->philoNum == PHILO_ID(me)) {
|
||||
status_ = Q_TRAN(&Philo_eating);
|
||||
}
|
||||
else {
|
||||
status_ = Q_UNHANDLED();
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*${AOs::Philo::SM::hungry::DONE} */
|
||||
case DONE_SIG: {
|
||||
/* DONE must be for other Philos than this one */
|
||||
Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(me));
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
status_ = Q_SUPER(&QHsm_top);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status_;
|
||||
}
|
||||
/*${AOs::Philo::SM::eating} ................................................*/
|
||||
static QState Philo_eating(Philo * const me, QEvt const * const e) {
|
||||
QState status_;
|
||||
switch (e->sig) {
|
||||
/*${AOs::Philo::SM::eating} */
|
||||
case Q_ENTRY_SIG: {
|
||||
QTimeEvt_armX(&me->timeEvt, EAT_TIME, 0U);
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Philo::SM::eating} */
|
||||
case Q_EXIT_SIG: {
|
||||
TableEvt *pe = Q_NEW(TableEvt, DONE_SIG);
|
||||
pe->philoNum = PHILO_ID(me);
|
||||
QF_PUBLISH(&pe->super, me);
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Philo::SM::eating::TIMEOUT} */
|
||||
case TIMEOUT_SIG: {
|
||||
status_ = Q_TRAN(&Philo_thinking);
|
||||
break;
|
||||
}
|
||||
/*${AOs::Philo::SM::eating::EAT, DONE} */
|
||||
case EAT_SIG: /* intentionally fall through */
|
||||
case DONE_SIG: {
|
||||
/* EAT or DONE must be for other Philos than this one */
|
||||
Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(me));
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
status_ = Q_SUPER(&QHsm_top);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status_;
|
||||
}
|
||||
/*$enddef${AOs::Philo} #####################################################*/
|
300
examples/posix-qv/dpp/table.c
Normal file
300
examples/posix-qv/dpp/table.c
Normal file
@ -0,0 +1,300 @@
|
||||
/*$file${.::table.c} #######################################################*/
|
||||
/*
|
||||
* Model: dpp.qm
|
||||
* File: ${.::table.c}
|
||||
*
|
||||
* This code has been generated by QM tool (https://state-machine.com/qm).
|
||||
* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
|
||||
*
|
||||
* This program is open source software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
/*$endhead${.::table.c} ####################################################*/
|
||||
#include "qpc.h"
|
||||
#include "dpp.h"
|
||||
#include "bsp.h"
|
||||
|
||||
Q_DEFINE_THIS_FILE
|
||||
|
||||
/* Active object class -----------------------------------------------------*/
|
||||
/*$declare${AOs::Table} ####################################################*/
|
||||
/*${AOs::Table} ............................................................*/
|
||||
typedef struct {
|
||||
/* protected: */
|
||||
QActive super;
|
||||
|
||||
/* private: */
|
||||
uint8_t fork[N_PHILO];
|
||||
uint8_t isHungry[N_PHILO];
|
||||
} Table;
|
||||
|
||||
/* protected: */
|
||||
static QState Table_initial(Table * const me, QEvt const * const e);
|
||||
static QState Table_active(Table * const me, QEvt const * const e);
|
||||
static QState Table_serving(Table * const me, QEvt const * const e);
|
||||
static QState Table_paused(Table * const me, QEvt const * const e);
|
||||
/*$enddecl${AOs::Table} ####################################################*/
|
||||
|
||||
#define RIGHT(n_) ((uint8_t)(((n_) + (N_PHILO - 1U)) % N_PHILO))
|
||||
#define LEFT(n_) ((uint8_t)(((n_) + 1U) % N_PHILO))
|
||||
#define FREE ((uint8_t)0)
|
||||
#define USED ((uint8_t)1)
|
||||
|
||||
/* Local objects -----------------------------------------------------------*/
|
||||
static Table l_table; /* the single instance of the Table active object */
|
||||
|
||||
/* Global-scope objects ----------------------------------------------------*/
|
||||
QMActive * const AO_Table = &l_table.super; /* "opaque" AO pointer */
|
||||
|
||||
/*..........................................................................*/
|
||||
/*$define${AOs::Table_ctor} ################################################*/
|
||||
/* Check for the minimum required QP version */
|
||||
#if ((QP_VERSION < 601) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8)))
|
||||
#error qpc version 6.0.1 or higher required
|
||||
#endif
|
||||
/*${AOs::Table_ctor} .......................................................*/
|
||||
void Table_ctor(void) {
|
||||
uint8_t n;
|
||||
Table *me = &l_table;
|
||||
|
||||
QActive_ctor(&me->super, Q_STATE_CAST(&Table_initial));
|
||||
|
||||
for (n = 0U; n < N_PHILO; ++n) {
|
||||
me->fork[n] = FREE;
|
||||
me->isHungry[n] = 0U;
|
||||
}
|
||||
}
|
||||
/*$enddef${AOs::Table_ctor} ################################################*/
|
||||
/*$define${AOs::Table} #####################################################*/
|
||||
/*${AOs::Table} ............................................................*/
|
||||
/*${AOs::Table::SM} ........................................................*/
|
||||
static QState Table_initial(Table * const me, QEvt const * const e) {
|
||||
/*${AOs::Table::SM::initial} */
|
||||
uint8_t n;
|
||||
(void)e; /* suppress the compiler warning about unused parameter */
|
||||
|
||||
QS_OBJ_DICTIONARY(&l_table);
|
||||
QS_FUN_DICTIONARY(&QHsm_top);
|
||||
QS_FUN_DICTIONARY(&Table_initial);
|
||||
QS_FUN_DICTIONARY(&Table_active);
|
||||
QS_FUN_DICTIONARY(&Table_serving);
|
||||
QS_FUN_DICTIONARY(&Table_paused);
|
||||
|
||||
QS_SIG_DICTIONARY(DONE_SIG, (void *)0); /* global signals */
|
||||
QS_SIG_DICTIONARY(EAT_SIG, (void *)0);
|
||||
QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0);
|
||||
QS_SIG_DICTIONARY(SERVE_SIG, (void *)0);
|
||||
QS_SIG_DICTIONARY(TEST_SIG, (void *)0);
|
||||
|
||||
QS_SIG_DICTIONARY(HUNGRY_SIG, me); /* signal just for Table */
|
||||
|
||||
QActive_subscribe(&me->super, DONE_SIG);
|
||||
QActive_subscribe(&me->super, PAUSE_SIG);
|
||||
QActive_subscribe(&me->super, SERVE_SIG);
|
||||
QActive_subscribe(&me->super, TEST_SIG);
|
||||
|
||||
for (n = 0U; n < N_PHILO; ++n) {
|
||||
me->fork[n] = FREE;
|
||||
me->isHungry[n] = 0U;
|
||||
BSP_displayPhilStat(n, "thinking");
|
||||
}
|
||||
return Q_TRAN(&Table_serving);
|
||||
}
|
||||
/*${AOs::Table::SM::active} ................................................*/
|
||||
static QState Table_active(Table * const me, QEvt const * const e) {
|
||||
QState status_;
|
||||
switch (e->sig) {
|
||||
/*${AOs::Table::SM::active::TEST} */
|
||||
case TEST_SIG: {
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Table::SM::active::EAT} */
|
||||
case EAT_SIG: {
|
||||
Q_ERROR();
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
status_ = Q_SUPER(&QHsm_top);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status_;
|
||||
}
|
||||
/*${AOs::Table::SM::active::serving} .......................................*/
|
||||
static QState Table_serving(Table * const me, QEvt const * const e) {
|
||||
QState status_;
|
||||
switch (e->sig) {
|
||||
/*${AOs::Table::SM::active::serving} */
|
||||
case Q_ENTRY_SIG: {
|
||||
uint8_t n;
|
||||
for (n = 0U; n < N_PHILO; ++n) { /* give permissions to eat... */
|
||||
if ((me->isHungry[n] != 0U)
|
||||
&& (me->fork[LEFT(n)] == FREE)
|
||||
&& (me->fork[n] == FREE))
|
||||
{
|
||||
TableEvt *te;
|
||||
|
||||
me->fork[LEFT(n)] = USED;
|
||||
me->fork[n] = USED;
|
||||
te = Q_NEW(TableEvt, EAT_SIG);
|
||||
te->philoNum = n;
|
||||
QF_PUBLISH(&te->super, me);
|
||||
me->isHungry[n] = 0U;
|
||||
BSP_displayPhilStat(n, "eating ");
|
||||
}
|
||||
}
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Table::SM::active::serving::HUNGRY} */
|
||||
case HUNGRY_SIG: {
|
||||
uint8_t n, m;
|
||||
|
||||
n = Q_EVT_CAST(TableEvt)->philoNum;
|
||||
/* phil ID must be in range and he must be not hungry */
|
||||
Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U));
|
||||
|
||||
BSP_displayPhilStat(n, "hungry ");
|
||||
m = LEFT(n);
|
||||
/*${AOs::Table::SM::active::serving::HUNGRY::[bothfree]} */
|
||||
if ((me->fork[m] == FREE) && (me->fork[n] == FREE)) {
|
||||
TableEvt *pe;
|
||||
me->fork[m] = USED;
|
||||
me->fork[n] = USED;
|
||||
pe = Q_NEW(TableEvt, EAT_SIG);
|
||||
pe->philoNum = n;
|
||||
QF_PUBLISH(&pe->super, me);
|
||||
BSP_displayPhilStat(n, "eating ");
|
||||
status_ = Q_HANDLED();
|
||||
}
|
||||
/*${AOs::Table::SM::active::serving::HUNGRY::[else]} */
|
||||
else {
|
||||
me->isHungry[n] = 1U;
|
||||
status_ = Q_HANDLED();
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*${AOs::Table::SM::active::serving::DONE} */
|
||||
case DONE_SIG: {
|
||||
uint8_t n, m;
|
||||
TableEvt *pe;
|
||||
|
||||
n = Q_EVT_CAST(TableEvt)->philoNum;
|
||||
/* phil ID must be in range and he must be not hungry */
|
||||
Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U));
|
||||
|
||||
BSP_displayPhilStat(n, "thinking");
|
||||
m = LEFT(n);
|
||||
/* both forks of Phil[n] must be used */
|
||||
Q_ASSERT((me->fork[n] == USED) && (me->fork[m] == USED));
|
||||
|
||||
me->fork[m] = FREE;
|
||||
me->fork[n] = FREE;
|
||||
m = RIGHT(n); /* check the right neighbor */
|
||||
|
||||
if ((me->isHungry[m] != 0U) && (me->fork[m] == FREE)) {
|
||||
me->fork[n] = USED;
|
||||
me->fork[m] = USED;
|
||||
me->isHungry[m] = 0U;
|
||||
pe = Q_NEW(TableEvt, EAT_SIG);
|
||||
pe->philoNum = m;
|
||||
QF_PUBLISH(&pe->super, me);
|
||||
BSP_displayPhilStat(m, "eating ");
|
||||
}
|
||||
m = LEFT(n); /* check the left neighbor */
|
||||
n = LEFT(m); /* left fork of the left neighbor */
|
||||
if ((me->isHungry[m] != 0U) && (me->fork[n] == FREE)) {
|
||||
me->fork[m] = USED;
|
||||
me->fork[n] = USED;
|
||||
me->isHungry[m] = 0U;
|
||||
pe = Q_NEW(TableEvt, EAT_SIG);
|
||||
pe->philoNum = m;
|
||||
QF_PUBLISH(&pe->super, me);
|
||||
BSP_displayPhilStat(m, "eating ");
|
||||
}
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Table::SM::active::serving::EAT} */
|
||||
case EAT_SIG: {
|
||||
Q_ERROR();
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Table::SM::active::serving::PAUSE} */
|
||||
case PAUSE_SIG: {
|
||||
status_ = Q_TRAN(&Table_paused);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
status_ = Q_SUPER(&Table_active);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status_;
|
||||
}
|
||||
/*${AOs::Table::SM::active::paused} ........................................*/
|
||||
static QState Table_paused(Table * const me, QEvt const * const e) {
|
||||
QState status_;
|
||||
switch (e->sig) {
|
||||
/*${AOs::Table::SM::active::paused} */
|
||||
case Q_ENTRY_SIG: {
|
||||
BSP_displayPaused(1U);
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Table::SM::active::paused} */
|
||||
case Q_EXIT_SIG: {
|
||||
BSP_displayPaused(0U);
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Table::SM::active::paused::SERVE} */
|
||||
case SERVE_SIG: {
|
||||
status_ = Q_TRAN(&Table_serving);
|
||||
break;
|
||||
}
|
||||
/*${AOs::Table::SM::active::paused::HUNGRY} */
|
||||
case HUNGRY_SIG: {
|
||||
uint8_t n = Q_EVT_CAST(TableEvt)->philoNum;
|
||||
/* philo ID must be in range and he must be not hungry */
|
||||
Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U));
|
||||
me->isHungry[n] = 1U;
|
||||
BSP_displayPhilStat(n, "hungry ");
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
/*${AOs::Table::SM::active::paused::DONE} */
|
||||
case DONE_SIG: {
|
||||
uint8_t n, m;
|
||||
|
||||
n = Q_EVT_CAST(TableEvt)->philoNum;
|
||||
/* phil ID must be in range and he must be not hungry */
|
||||
Q_ASSERT((n < N_PHILO) && (me->isHungry[n] == 0U));
|
||||
|
||||
BSP_displayPhilStat(n, "thinking");
|
||||
m = LEFT(n);
|
||||
/* both forks of Phil[n] must be used */
|
||||
Q_ASSERT((me->fork[n] == USED) && (me->fork[m] == USED));
|
||||
|
||||
me->fork[m] = FREE;
|
||||
me->fork[n] = FREE;
|
||||
status_ = Q_HANDLED();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
status_ = Q_SUPER(&Table_active);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status_;
|
||||
}
|
||||
/*$enddef${AOs::Table} #####################################################*/
|
@ -1,6 +1,16 @@
|
||||
This QP port is for the QUTest unit testing harness.
|
||||
|
||||
If you are interested in using a POSIX target as embedded platform,
|
||||
consider the following QP ports:
|
||||
|
||||
- posix for multithreaded (P-threads) QP applications
|
||||
- posix-qv single-threaded QP port to POSIX
|
||||
|
||||
|
||||
NOTE:
|
||||
Building of the QP libraries on the POSIX targets or hosts
|
||||
is no longer necessary. The example projects for POSIX are
|
||||
built directly from QP source files and don't need a library.
|
||||
|
||||
Quantum Leaps
|
||||
03/16/2018
|
||||
04/05/2018
|
22
ports/posix-qv/README.txt
Normal file
22
ports/posix-qv/README.txt
Normal file
@ -0,0 +1,22 @@
|
||||
This QP port to POSIX with a single thread executing all active
|
||||
objects (like the cooperative QV kernel).
|
||||
|
||||
If you are interested in using a POSIX target for deployment,
|
||||
consider the following QP port:
|
||||
|
||||
- posix multithreaded (P-threads) QP port to POSIX
|
||||
|
||||
|
||||
If you are interested in testing your embedded QP applications
|
||||
on a POSIX host, consider the following QP port:
|
||||
|
||||
- posix-qutest for running QUTest unit testing harness
|
||||
|
||||
|
||||
NOTE:
|
||||
Building of the QP libraries on the POSIX targets or hosts
|
||||
is no longer necessary. The example projects for POSIX are
|
||||
built directly from QP source files and don't need a library.
|
||||
|
||||
Quantum Leaps
|
||||
04/05/2018
|
47
ports/posix-qv/qep_port.h
Normal file
47
ports/posix-qv/qep_port.h
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief QEP/C port, generic C99 compiler
|
||||
* @ingroup ports
|
||||
* @cond
|
||||
******************************************************************************
|
||||
* Last Updated for Version: 5.4.0
|
||||
* Date of the Last Update: 2015-04-08
|
||||
*
|
||||
* Q u a n t u m L e a P s
|
||||
* ---------------------------
|
||||
* innovating embedded systems
|
||||
*
|
||||
* Copyright (C) Quantum Leaps, LLC. state-machine.com.
|
||||
*
|
||||
* This program is open source software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alternatively, this program may be distributed and modified under the
|
||||
* terms of Quantum Leaps commercial licenses, which expressly supersede
|
||||
* the GNU General Public License and are specifically designed for
|
||||
* licensees interested in retaining the proprietary status of their code.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Contact information:
|
||||
* Web: www.state-machine.com
|
||||
* Email: info@state-machine.com
|
||||
******************************************************************************
|
||||
* @endcond */
|
||||
#ifndef qep_port_h
|
||||
#define qep_port_h
|
||||
|
||||
#include <stdint.h> /* Exact-width types. WG14/N843 C99 Standard */
|
||||
#include <stdbool.h> /* Boolean type. WG14/N843 C99 Standard */
|
||||
|
||||
#include "qep.h" /* QEP platform-independent public interface */
|
||||
|
||||
#endif /* qep_port_h */
|
292
ports/posix-qv/qf_port.c
Normal file
292
ports/posix-qv/qf_port.c
Normal file
@ -0,0 +1,292 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief QF/C port to POSIX API with cooperative QV scheduler (posix-qv)
|
||||
* @ingroup ports
|
||||
* @cond
|
||||
******************************************************************************
|
||||
* Last updated for version 6.2.0
|
||||
* Last updated on 2018-04-05
|
||||
*
|
||||
* Q u a n t u m L e a P s
|
||||
* ---------------------------
|
||||
* innovating embedded systems
|
||||
*
|
||||
* Copyright (C) 2005-2018 Quantum Leaps, LLC. All rights reserved.
|
||||
*
|
||||
* This program is open source software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alternatively, this program may be distributed and modified under the
|
||||
* terms of Quantum Leaps commercial licenses, which expressly supersede
|
||||
* the GNU General Public License and are specifically designed for
|
||||
* licensees interested in retaining the proprietary status of their code.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Contact information:
|
||||
* https://www.state-machine.com
|
||||
* mailto:info@state-machine.com
|
||||
******************************************************************************
|
||||
* @endcond
|
||||
*/
|
||||
#define QP_IMPL /* this is QP implementation */
|
||||
#include "qf_port.h" /* QF port */
|
||||
#include "qf_pkg.h"
|
||||
#include "qassert.h"
|
||||
#ifdef Q_SPY /* QS software tracing enabled? */
|
||||
#include "qs_port.h" /* include QS port */
|
||||
#else
|
||||
#include "qs_dummy.h" /* disable the QS software tracing */
|
||||
#endif /* Q_SPY */
|
||||
|
||||
#include <limits.h> /* for PTHREAD_STACK_MIN */
|
||||
#include <sys/mman.h> /* for mlockall() */
|
||||
|
||||
Q_DEFINE_THIS_MODULE("qf_port")
|
||||
|
||||
/* Global objects ==========================================================*/
|
||||
pthread_mutex_t QF_pThreadMutex_;
|
||||
QPSet QV_readySet_; /* QV-ready set of active objects */
|
||||
pthread_cond_t QV_condVar_; /* Cond.var. to signal events */
|
||||
|
||||
/* Local objects ===========================================================*/
|
||||
static bool l_isRunning;
|
||||
static struct timespec l_tick;
|
||||
enum { NANOSLEEP_NSEC_PER_SEC = 1000000000 }; /* see NOTE05 */
|
||||
|
||||
static void *ticker_thread(void *arg);
|
||||
|
||||
/* QF functions ============================================================*/
|
||||
void QF_init(void) {
|
||||
extern uint_fast8_t QF_maxPool_;
|
||||
extern QTimeEvt QF_timeEvtHead_[QF_MAX_TICK_RATE];
|
||||
|
||||
/* lock memory so we're never swapped out to disk */
|
||||
/*mlockall(MCL_CURRENT | MCL_FUTURE); uncomment when supported */
|
||||
|
||||
/* init the global mutex with the default non-recursive initializer */
|
||||
pthread_mutex_init(&QF_pThreadMutex_, NULL);
|
||||
|
||||
/* clear the internal QF variables, so that the framework can (re)start
|
||||
* correctly even if the startup code is not called to clear the
|
||||
* uninitialized data (as is required by the C Standard).
|
||||
*/
|
||||
QF_maxPool_ = (uint_fast8_t)0;
|
||||
QF_bzero(&QF_timeEvtHead_[0], (uint_fast16_t)sizeof(QF_timeEvtHead_));
|
||||
QF_bzero(&QF_active_[0], (uint_fast16_t)sizeof(QF_active_));
|
||||
|
||||
l_tick.tv_sec = 0;
|
||||
l_tick.tv_nsec = NANOSLEEP_NSEC_PER_SEC/100L; /* default clock tick */
|
||||
}
|
||||
/****************************************************************************/
|
||||
|
||||
int_t QF_run(void) {
|
||||
struct sched_param sparam;
|
||||
|
||||
QF_onStartup(); /* invoke startup callback */
|
||||
|
||||
/* try to maximize the priority of the ticker thread, see NOTE01 */
|
||||
sparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
||||
if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sparam) == 0) {
|
||||
/* success, this application has sufficient privileges */
|
||||
}
|
||||
else {
|
||||
/* setting priority failed, probably due to insufficient privieges */
|
||||
}
|
||||
|
||||
l_isRunning = true; /* QF is running */
|
||||
|
||||
/* system clock tick configured? */
|
||||
if ((l_tick.tv_sec != 0) || (l_tick.tv_nsec != 0)) {
|
||||
pthread_attr_t attr;
|
||||
struct sched_param param;
|
||||
pthread_t idle;
|
||||
|
||||
/* SCHED_FIFO corresponds to real-time preemptive priority-based
|
||||
* scheduler.
|
||||
* NOTE: This scheduling policy requires the superuser priviledges
|
||||
*/
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
|
||||
param.sched_priority = sched_get_priority_min(SCHED_FIFO);
|
||||
|
||||
pthread_attr_setschedparam(&attr, ¶m);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
|
||||
if (pthread_create(&idle, &attr, &ticker_thread, 0) != 0) {
|
||||
/* Creating the p-thread with the SCHED_FIFO policy failed.
|
||||
* Most probably this application has no superuser privileges,
|
||||
* so we just fall back to the default SCHED_OTHER policy
|
||||
* and priority 0.
|
||||
*/
|
||||
pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
|
||||
param.sched_priority = 0;
|
||||
pthread_attr_setschedparam(&attr, ¶m);
|
||||
if (pthread_create(&idle, &attr, &ticker_thread, 0) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
|
||||
/* the combined event-loop and background-loop of the QV kernel */
|
||||
QF_INT_DISABLE();
|
||||
while (l_isRunning) {
|
||||
QEvt const *e;
|
||||
QActive *a;
|
||||
uint_fast8_t p;
|
||||
|
||||
/* find the maximum priority AO ready to run */
|
||||
if (QPSet_notEmpty(&QV_readySet_)) {
|
||||
|
||||
QPSet_findMax(&QV_readySet_, p);
|
||||
a = QF_active_[p];
|
||||
QF_INT_ENABLE();
|
||||
|
||||
/* the active object 'a' must still be registered in QF
|
||||
* (e.g., it must not be stopped)
|
||||
*/
|
||||
Q_ASSERT_ID(320, a != (QActive *)0);
|
||||
|
||||
/* perform the run-to-completion (RTS) step...
|
||||
* 1. retrieve the event from the AO's event queue, which by this
|
||||
* time must be non-empty and The "Vanialla" kernel asserts it.
|
||||
* 2. dispatch the event to the AO's state machine.
|
||||
* 3. determine if event is garbage and collect it if so
|
||||
*/
|
||||
e = QActive_get_(a);
|
||||
QHSM_DISPATCH(&a->super, e);
|
||||
QF_gc(e);
|
||||
|
||||
QF_INT_DISABLE();
|
||||
|
||||
if (a->eQueue.frontEvt == (QEvt const *)0) { /* empty queue? */
|
||||
QPSet_remove(&QV_readySet_, p);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// the QV kernel in embedded systems calls here the QV_onIdle()
|
||||
// callback. However, the POSIX-QV port does not do busy-waiting
|
||||
// for events. Instead, the POSIX-QV port efficiently waits until
|
||||
// QP events become available.
|
||||
//
|
||||
while (QPSet_isEmpty(&QV_readySet_)) {
|
||||
pthread_cond_wait(&QV_condVar_, &QF_pThreadMutex_);
|
||||
}
|
||||
}
|
||||
}
|
||||
QF_INT_ENABLE();
|
||||
QF_onCleanup(); /* cleanup callback */
|
||||
QS_EXIT(); /* cleanup the QSPY connection */
|
||||
|
||||
pthread_cond_destroy(&QV_condVar_); // cleanup the condition variable
|
||||
pthread_mutex_destroy(&QF_pThreadMutex_); // cleanup the global mutex
|
||||
|
||||
return (int_t)0; /* return success */
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void QF_setTickRate(uint32_t ticksPerSec) {
|
||||
if (ticksPerSec != (uint32_t)0) {
|
||||
l_tick.tv_nsec = NANOSLEEP_NSEC_PER_SEC / ticksPerSec;
|
||||
}
|
||||
else {
|
||||
l_tick.tv_nsec = 0; /* means NO system clock tick */
|
||||
}
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void QF_stop(void) {
|
||||
uint_fast8_t p;
|
||||
l_isRunning = false; /* terminate the main event-loop thread */
|
||||
|
||||
/* unblock the event-loop so it can terminate */
|
||||
p = (uint_fast8_t)1;
|
||||
QPSet_insert(&QV_readySet_, p);
|
||||
pthread_cond_signal(&QV_condVar_);
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void QActive_start_(QActive * const me, uint_fast8_t prio,
|
||||
QEvt const *qSto[], uint_fast16_t qLen,
|
||||
void *stkSto, uint_fast16_t stkSize,
|
||||
QEvt const *ie)
|
||||
{
|
||||
Q_REQUIRE_ID(600, ((uint_fast8_t)0 < prio) /* priority...*/
|
||||
&& (prio <= (uint_fast8_t)QF_MAX_ACTIVE) /*... in range */
|
||||
&& (stkSto == (void *)0)); /* statck storage must NOT...
|
||||
* ... be provided */
|
||||
|
||||
QEQueue_init(&me->eQueue, qSto, qLen);
|
||||
me->prio = (uint8_t)prio;
|
||||
QF_add_(me); /* make QF aware of this active object */
|
||||
|
||||
QHSM_INIT(&me->super, ie); /* take the top-most initial tran. */
|
||||
|
||||
(void)stkSize; /* avoid the "unused parameter" compiler warning */
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void QActive_stop(QActive * const me) {
|
||||
QActive_unsubscribeAll(me);
|
||||
QF_remove_(me);
|
||||
}
|
||||
|
||||
//****************************************************************************
|
||||
static void *ticker_thread(void *arg) { /* for pthread_create() */
|
||||
(void)arg; /* unused parameter */
|
||||
while (l_isRunning) { /* the clock tick loop... */
|
||||
QF_onClockTick(); /* clock tick callback (must call QF_TICK_X()) */
|
||||
|
||||
nanosleep(&l_tick, NULL); /* sleep for the number of ticks, NOTE05 */
|
||||
}
|
||||
return (void *)0; /* return success */
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* NOTE01:
|
||||
* In Linux, the scheduler policy closest to real-time is the SCHED_FIFO
|
||||
* policy, available only with superuser privileges. QF_run() attempts to set
|
||||
* this policy as well as to maximize its priority, so that the ticking
|
||||
* occurrs in the most timely manner (as close to an interrupt as possible).
|
||||
* However, setting the SCHED_FIFO policy might fail, most probably due to
|
||||
* insufficient privileges.
|
||||
*
|
||||
* NOTE02:
|
||||
* On some Linux systems nanosleep() might actually not deliver the finest
|
||||
* time granularity. For example, on some Linux implementations, nanosleep()
|
||||
* could not block for shorter intervals than 20ms, while the underlying
|
||||
* clock tick period was only 10ms. Sometimes, the select() system call can
|
||||
* provide a finer granularity.
|
||||
*
|
||||
* NOTE03:
|
||||
* Any blocking system call, such as nanosleep() or select() system call can
|
||||
* be interrupted by a signal, such as ^C from the keyboard. In this case this
|
||||
* QF port breaks out of the event-loop and returns to main() that exits and
|
||||
* terminates all spawned p-threads.
|
||||
*
|
||||
* NOTE04:
|
||||
* According to the man pages (for pthread_attr_setschedpolicy) the only value
|
||||
* supported in the Linux p-threads implementation is PTHREAD_SCOPE_SYSTEM,
|
||||
* meaning that the threads contend for CPU time with all processes running on
|
||||
* the machine. In particular, thread priorities are interpreted relative to
|
||||
* the priorities of all other processes on the machine.
|
||||
*
|
||||
* This is good, because it seems that if we set the priorities high enough,
|
||||
* no other process (or thread running within) can gain control over the CPU.
|
||||
*
|
||||
* However, QF limits the number of priority levels to QF_MAX_ACTIVE.
|
||||
* Assuming that a QF application will be real-time, this port reserves the
|
||||
* three highest Linux priorities for the ISR-like threads (e.g., the ticker,
|
||||
* I/O), and the rest highest-priorities for the active objects.
|
||||
*
|
||||
* NOTE05:
|
||||
* In some (older) Linux kernels, the POSIX nanosleep() system call might
|
||||
* deliver only 2*actual-system-tick granularity. To compensate for this,
|
||||
* you would need to reduce (by 2) the constant NANOSLEEP_NSEC_PER_SEC.
|
||||
*/
|
||||
|
149
ports/posix-qv/qf_port.h
Normal file
149
ports/posix-qv/qf_port.h
Normal file
@ -0,0 +1,149 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief QF/C port to POSIX API with cooperative QV scheduler (posix-qv)
|
||||
* @cond
|
||||
******************************************************************************
|
||||
* Last updated for version 6.2.0
|
||||
* Last updated on 2018-04-05
|
||||
*
|
||||
* Q u a n t u m L e a P s
|
||||
* ---------------------------
|
||||
* innovating embedded systems
|
||||
*
|
||||
* Copyright (C) Quantum Leaps, LLC. All rights reserved.
|
||||
*
|
||||
* This program is open source software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alternatively, this program may be distributed and modified under the
|
||||
* terms of Quantum Leaps commercial licenses, which expressly supersede
|
||||
* the GNU General Public License and are specifically designed for
|
||||
* licensees interested in retaining the proprietary status of their code.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Contact information:
|
||||
* https://www.state-machine.com
|
||||
* mailto:info@state-machine.com
|
||||
******************************************************************************
|
||||
* @endcond
|
||||
*/
|
||||
#ifndef qf_port_h
|
||||
#define qf_port_h
|
||||
|
||||
/* POSIX event queue and thread types */
|
||||
#define QF_EQUEUE_TYPE QEQueue
|
||||
/*#define QF_OS_OBJECT_TYPE // not provided */
|
||||
/*#define QF_THREAD_TYPE // not provided */
|
||||
|
||||
/* The maximum number of active objects in the application */
|
||||
#define QF_MAX_ACTIVE 64
|
||||
|
||||
/* The number of system clock tick rates */
|
||||
#define QF_MAX_TICK_RATE 2
|
||||
|
||||
/* various QF object sizes configuration for this port */
|
||||
#define QF_EVENT_SIZ_SIZE 4
|
||||
#define QF_EQUEUE_CTR_SIZE 4
|
||||
#define QF_MPOOL_SIZ_SIZE 4
|
||||
#define QF_MPOOL_CTR_SIZE 4
|
||||
#define QF_TIMEEVT_CTR_SIZE 4
|
||||
|
||||
/* QF interrupt disable/enable, see NOTE1 */
|
||||
#define QF_INT_DISABLE() pthread_mutex_lock(&QF_pThreadMutex_)
|
||||
#define QF_INT_ENABLE() pthread_mutex_unlock(&QF_pThreadMutex_)
|
||||
|
||||
/* QF critical section entry/exit for POSIX, see NOTE1 */
|
||||
/* QF_CRIT_STAT_TYPE // not defined */
|
||||
#define QF_CRIT_ENTRY(dummy) QF_INT_DISABLE()
|
||||
#define QF_CRIT_EXIT(dummy) QF_INT_ENABLE()
|
||||
|
||||
/* QF_LOG2 not defined -- use the internal LOG2() implementation */
|
||||
|
||||
#include <pthread.h> /* POSIX-thread API */
|
||||
#include "qep_port.h" /* QEP port */
|
||||
#include "qequeue.h" /* POSIX-QV needs event-queue */
|
||||
#include "qmpool.h" /* POSIX-QV needs memory-pool */
|
||||
#include "qf.h" /* QF platform-independent public interface */
|
||||
|
||||
/* set clock tick rate (NOTE ticksPerSec==0 disables the "ticker thread" */
|
||||
void QF_setTickRate(uint32_t ticksPerSec); /* set clock tick rate */
|
||||
|
||||
/* clock tick callback (NOTE not called when "ticker thread" is not running) */
|
||||
void QF_onClockTick(void); /* clock tick callback (provided in the app) */
|
||||
|
||||
extern pthread_mutex_t QF_pThreadMutex_; /* mutex for QF critical section */
|
||||
|
||||
/****************************************************************************/
|
||||
/* interface used only inside QF implementation, but not in applications */
|
||||
#ifdef QP_IMPL
|
||||
|
||||
/* scheduler locking (not needed in single-thread port) */
|
||||
#define QF_SCHED_STAT_
|
||||
#define QF_SCHED_LOCK_(dummy) ((void)0)
|
||||
#define QF_SCHED_UNLOCK_() ((void)0)
|
||||
|
||||
/* POSIX active object event queue customization... */
|
||||
#define QACTIVE_EQUEUE_WAIT_(me_) \
|
||||
Q_ASSERT((me_)->eQueue.frontEvt != (QEvt const *)0)
|
||||
|
||||
#define QACTIVE_EQUEUE_SIGNAL_(me_) do { \
|
||||
QPSet_insert(&QV_readySet_, (me_)->prio); \
|
||||
pthread_cond_signal(&QV_condVar_); \
|
||||
} while (0)
|
||||
|
||||
/* event pool operations */
|
||||
#define QF_EPOOL_TYPE_ QMPool
|
||||
|
||||
#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \
|
||||
QMPool_init(&(p_), poolSto_, poolSize_, evtSize_)
|
||||
|
||||
#define QF_EPOOL_EVENT_SIZE_(p_) ((p_).blockSize)
|
||||
#define QF_EPOOL_GET_(p_, e_, m_) ((e_) = (QEvt *)QMPool_get(&(p_), (m_)))
|
||||
#define QF_EPOOL_PUT_(p_, e_) (QMPool_put(&(p_), e_))
|
||||
|
||||
extern QPSet QV_readySet_; /* QV-ready set of active objects */
|
||||
extern pthread_cond_t QV_condVar_; /* Cond.var. to signal events */
|
||||
|
||||
#endif /* QP_IMPL */
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* NOTE1:
|
||||
* QF, like all real-time frameworks, needs to execute certain sections of
|
||||
* code indivisibly to avoid data corruption. The most straightforward way of
|
||||
* protecting such critical sections of code is disabling and enabling
|
||||
* interrupts, which POSIX does not allow.
|
||||
*
|
||||
* This QF port uses therefore a single package-scope p-thread mutex
|
||||
* QF_pThreadMutex_ to protect all critical sections. The mutex is locked upon
|
||||
* the entry to each critical sectioni and unlocked upon exit.
|
||||
*
|
||||
* Using the single mutex for all crtical section guarantees that only one
|
||||
* thread at a time can execute inside a critical section. This prevents race
|
||||
* conditions and data corruption.
|
||||
*
|
||||
* Please note, however, that the mutex implementation of a critical section
|
||||
* behaves differently than the standard interrupt locking. A common mutex
|
||||
* ensures that only one thread at a time can execute a critical section, but
|
||||
* it does not guarantee that a context switch cannot occur within the
|
||||
* critical section. In fact, such context switches probably will happen, but
|
||||
* they should not cause concurrency hazards because the mutex eliminates all
|
||||
* race conditionis.
|
||||
*
|
||||
* Unlinke simply disabling and enabling interrupts, the mutex approach is
|
||||
* also subject to priority inversions. However, the p-thread mutex
|
||||
* implementation, such as POSIX threads, should support the priority-
|
||||
* inheritance protocol.
|
||||
*/
|
||||
|
||||
#endif /* qf_port_h */
|
||||
|
64
ports/posix-qv/qs_port.h
Normal file
64
ports/posix-qv/qs_port.h
Normal file
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief QS/C port to POSIX with GNU compiler
|
||||
* @ingroup ports
|
||||
* @cond
|
||||
******************************************************************************
|
||||
* Last updated for version 6.2.0
|
||||
* Last updated on 2018-04-05
|
||||
*
|
||||
* Q u a n t u m L e a P s
|
||||
* ---------------------------
|
||||
* innovating embedded systems
|
||||
*
|
||||
* Copyright (C) Quantum Leaps, LLC. All rights reserved.
|
||||
*
|
||||
* This program is open source software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alternatively, this program may be distributed and modified under the
|
||||
* terms of Quantum Leaps commercial licenses, which expressly supersede
|
||||
* the GNU General Public License and are specifically designed for
|
||||
* licensees interested in retaining the proprietary status of their code.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Contact information:
|
||||
* https://www.state-machine.com
|
||||
* mailto:info@state-machine.com
|
||||
******************************************************************************
|
||||
* @endcond
|
||||
*/
|
||||
#ifndef qs_port_h
|
||||
#define qs_port_h
|
||||
|
||||
#define QS_TIME_SIZE 4
|
||||
|
||||
#if defined(__LP64__) || defined(_LP64) /* 64-bit architecture? */
|
||||
#define QS_OBJ_PTR_SIZE 8
|
||||
#define QS_FUN_PTR_SIZE 8
|
||||
#else /* 32-bit architecture */
|
||||
#define QS_OBJ_PTR_SIZE 4
|
||||
#define QS_FUN_PTR_SIZE 4
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
* NOTE: QS might be used with or without other QP components, in which
|
||||
* case the separate definitions of the macros QF_CRIT_STAT_TYPE,
|
||||
* QF_CRIT_ENTRY, and QF_CRIT_EXIT are needed. In this port QS is configured
|
||||
* to be used with the other QP component, by simply including "qf_port.h"
|
||||
* *before* "qs.h".
|
||||
*/
|
||||
#include "qf_port.h" /* use QS with QF */
|
||||
#include "qs.h" /* QS platform-independent public interface */
|
||||
|
||||
#endif /* qs_port_h */
|
||||
|
@ -1,6 +1,17 @@
|
||||
This QP port to POSIX with P-threads is intended for building
|
||||
multithreaded QP applications running on embedded POSIX targets.
|
||||
|
||||
If you are interested in using a POSIX host for testing your
|
||||
embedded QP applications, consider the following QP ports:
|
||||
|
||||
- posix-qutest for running QUTest unit testing harness
|
||||
- posix-qv single-threaded QP port to POSIX
|
||||
|
||||
|
||||
NOTE:
|
||||
Building of the QP libraries on the POSIX targets or hosts
|
||||
is no longer necessary. The example projects for POSIX are
|
||||
built directly from QP source files and don't need a library.
|
||||
|
||||
Quantum Leaps
|
||||
03/16/2018
|
||||
04/05/2018
|
@ -4,14 +4,14 @@
|
||||
* @ingroup ports
|
||||
* @cond
|
||||
******************************************************************************
|
||||
* Last Updated for Version: 5.9.7
|
||||
* Date of the Last Update: 2017-08-25
|
||||
* Last updated for version 6.2.0
|
||||
* Last updated on 2018-04-05
|
||||
*
|
||||
* Q u a n t u m L e a P s
|
||||
* ---------------------------
|
||||
* innovating embedded systems
|
||||
*
|
||||
* Copyright (C) Quantum Leaps, LLC. All rights reserved.
|
||||
* Copyright (C) 2005-2018 Quantum Leaps, LLC. All rights reserved.
|
||||
*
|
||||
* This program is open source software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
@ -32,7 +32,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Contact information:
|
||||
* https://state-machine.com
|
||||
* https://www.state-machine.com
|
||||
* mailto:info@state-machine.com
|
||||
******************************************************************************
|
||||
* @endcond
|
||||
@ -52,16 +52,16 @@
|
||||
|
||||
Q_DEFINE_THIS_MODULE("qf_port")
|
||||
|
||||
/* Global objects ----------------------------------------------------------*/
|
||||
/* Global objects ==========================================================*/
|
||||
pthread_mutex_t QF_pThreadMutex_;
|
||||
|
||||
/* Local objects -----------------------------------------------------------*/
|
||||
/* Local objects ===========================================================*/
|
||||
static pthread_mutex_t l_startupMutex;
|
||||
static bool l_isRunning;
|
||||
static struct timespec l_tick;
|
||||
enum { NANOSLEEP_NSEC_PER_SEC = 1000000000 }; /* see NOTE05 */
|
||||
|
||||
/*..........................................................................*/
|
||||
/* QF functions ============================================================*/
|
||||
void QF_init(void) {
|
||||
extern uint_fast8_t QF_maxPool_;
|
||||
extern QTimeEvt QF_timeEvtHead_[QF_MAX_TICK_RATE];
|
||||
@ -91,7 +91,8 @@ void QF_init(void) {
|
||||
l_tick.tv_sec = 0;
|
||||
l_tick.tv_nsec = NANOSLEEP_NSEC_PER_SEC/100L; /* default clock tick */
|
||||
}
|
||||
/*..........................................................................*/
|
||||
/****************************************************************************/
|
||||
|
||||
int_t QF_run(void) {
|
||||
struct sched_param sparam;
|
||||
|
||||
@ -125,6 +126,7 @@ int_t QF_run(void) {
|
||||
}
|
||||
/*..........................................................................*/
|
||||
void QF_setTickRate(uint32_t ticksPerSec) {
|
||||
Q_REQUIRE_ID(300, ticksPerSec != (uint32_t)0);
|
||||
l_tick.tv_nsec = NANOSLEEP_NSEC_PER_SEC / ticksPerSec;
|
||||
}
|
||||
/*..........................................................................*/
|
||||
@ -169,13 +171,11 @@ void QActive_start_(QActive * const me, uint_fast8_t prio,
|
||||
QF_add_(me); /* make QF aware of this active object */
|
||||
|
||||
QHSM_INIT(&me->super, ie); /* take the top-most initial tran. */
|
||||
QS_FLUSH(); /* flush the QS trace buffer to the host */
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
|
||||
/* SCHED_FIFO corresponds to real-time preemptive priority-based scheduler
|
||||
* NOTE: This scheduling policy requires the superuser privileges
|
||||
*/
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
|
||||
|
||||
/* see NOTE04 */
|
||||
|
@ -4,8 +4,8 @@
|
||||
* @ingroup ports
|
||||
* @cond
|
||||
******************************************************************************
|
||||
* Last Updated for Version: 6.1.1
|
||||
* Date of the Last Update: 2018-03-06
|
||||
* Last updated for version 6.2.0
|
||||
* Last updated on 2018-04-05
|
||||
*
|
||||
* Q u a n t u m L e a P s
|
||||
* ---------------------------
|
||||
@ -178,16 +178,15 @@ void QActive_start_(QActive * const me, uint_fast8_t prio,
|
||||
void *stkSto, uint_fast16_t stkSize,
|
||||
QEvt const *ie)
|
||||
{
|
||||
Q_REQUIRE_ID(700, ((uint_fast8_t)0 < prio) /* priority must be in range */
|
||||
Q_REQUIRE_ID(600, ((uint_fast8_t)0 < prio) /* priority must be in range */
|
||||
&& (prio <= (uint_fast8_t)QF_MAX_ACTIVE)
|
||||
&& (stkSto == (void *)0)); /* statck storage must NOT...
|
||||
* ... be provided */
|
||||
|
||||
QEQueue_init(&me->eQueue, qSto, qLen);
|
||||
me->prio = prio; /* set QF priority of this AO before adding it to QF */
|
||||
QF_add_(me); /* make QF aware of this active object */
|
||||
|
||||
QEQueue_init(&me->eQueue, qSto, qLen);
|
||||
|
||||
QHSM_INIT(&me->super, ie); /* take the top-most initial tran. */
|
||||
QS_FLUSH(); /* flush the QS trace buffer to the host */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user