SinelaboreRT Header Logo

SinelaboreRT

As simple as possible, but not any simpler!

User Tools

Site Tools


de:start

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
de:start [2019/09/01 12:33] – [Problemlos Quellcode aus UML Zustands- und Aktivitätsdiagrammen erzeugen!] pmuellerde:start [2023/09/28 21:56] (current) – [Code aus UML Zustandsdiagrammen und Aktivitätsdiagrammen erzeugen!] webmin
Line 1: Line 1:
-{{description>Problemlos Quellcode aus UML Zustands- und Aktivitätsdiagrammen erzeugen!}}+~~NOTOC~~
  
 +====== Code aus UML Zustandsdiagrammen und Aktivitätsdiagrammen erzeugen! ======
  
-====== Problemlos Quellcode aus UML Zustands- und Aktivitätsdiagrammen erzeugen! ======+===== Sinelabore ermöglicht es Entwicklern von eingebetteten Systemen, event-basierte Architekturen, hierarchische Zustandsautomaten, modellbasiertes Design und automatische Codegenerierung effektiv zu kombinieren. =====
  
-Sinelabore//RT// wurde speziell für Entwickler von eingebetteten Echtzeitsystemen entwickelt. Der Schwerpunkt liegt auf einer Aufgabe: Effizienten und verständlichen Code aus UML Zustands- und Aktivitätsdiagrammen zu erzeugen. Der Generator ist ein schlankes und einfach zu verwendendes Werkzeug. Durch das neue C++, Java, Swift und C# Backend ist es ebenfalls bestens für die Entwicklung von Desktop oder Serveranwendungen geeignet. Ein Payback ist üblicherweise schon innerhalb weniger Stunden gegeben.+ 
 +Sinelabore//RT// wurde speziell für Entwickler von eingebetteten Echtzeitsystemen entwickelt. Der Schwerpunkt liegt auf einer Aufgabe: Effizienten und verständlichen Code aus UML Zustandsdiagrammen (State Machines, State Diagrams) und Aktivitätsdiagrammen in C/CPP zu erzeugen. Der Generator ist ein schlankes und einfach zu verwendendes Werkzeug. Durch das neue Java, Swift, Lua und C# Backend ist es ebenfalls bestens für die Entwicklung von Desktop oder Serveranwendungen geeignet. Ein Payback ist üblicherweise schon innerhalb weniger Stunden gegeben.
  
 ==== Wie funktioniert der Generator? ==== ==== Wie funktioniert der Generator? ====
Line 12: Line 14:
 Sie werden optimal in den verschiedenen Entwicklungsphasen, wie z.B. in der Testphase durch die automatische Erzeugung von Testfällen, oder während der Fehlersuche durch automatisch erzeugten Tracecode unterstützt. Sie werden optimal in den verschiedenen Entwicklungsphasen, wie z.B. in der Testphase durch die automatische Erzeugung von Testfällen, oder während der Fehlersuche durch automatisch erzeugten Tracecode unterstützt.
  
-{{:sinelabore_firstpage.png?|Wie funktioniert der Generator}}+{{::sinelabore_homepage.svg?800| Wie funktioniert der Generator}}
  
  
 Funktionen im Überblick: Funktionen im Überblick:
   * Automatisch generierter Code höchster Güte   * Automatisch generierter Code höchster Güte
-  * Erzeugt C, C++, Java, Swift oder C# Code  +  * Erzeugt C, CPP, Java, Swift oder C# Code  
   * Automatisierte Robustheitstests auf UML Modellebene   * Automatisierte Robustheitstests auf UML Modellebene
   * Keine Laufzeitumgebung notwendig   * Keine Laufzeitumgebung notwendig
Line 34: Line 36:
  
  
 +==== Schnelleinstieg ====
 +
 +
 +Um einen Eindruck von den Möglichkeiten des Codegenerators zu bekommen, laden sie sich [[wiki:download| hier]] direkt die Testversion herunter. Im Ordner mit den Beispielen finden sie Beispielprojekte für verschiedene UML-Tools und Zielsprachen. Wählen sie das am besten geeignete Beispiel aus, um einen Eindruck des generierten Codes zu bekommen. Im Handbuch sind alle Funktionen detailliert beschrieben. Einen Schnellstart finden Sie auch auf den "Getting Started"-Seiten auf dieser Website. Das {{:wiki:downloads:sinelaborert.pdf|Handbuch}} enthält auch eine grundlegende Einführung in Zustandsautomaten und wie man diese Testet -- falls Sie eine Auffrischung benötigen. Lesen sie weiterhin die Abschnitte, die sich auf Ihr UML-Tool und das Sprach-Backend beziehen, das Sie verwenden möchten.
 +
 +Sie haben nun folgende Möglichkeiten, sich weiter mit Codegenerator vertraut zu machen:
 +
 +1) Führen sie die Beispiele auf Ihrem PC aus. Die Beispiele modellieren einen Mikrowellenherd und können ausgeführt und getestet werden. Spielen sie mit dem Modell und verbessern Sie es. Generieren Sie den Code neu und lernen sie aus den Warn- und Fehlermeldungen.
 +
 +2) Führen sie die Beispiele auf einem Mikrocontroller aus, z.B. auf einem MSP430-Evaluierungsboard oder unter Arduino. Ein MSP430-Beispiel, das auf der einfachen Foreground/Background-Architektur basiert, ist auf [[https://github.com/sinelabore/examples/tree/master/EnergiaBlink|Github]] verfügbar.
 +
 +
 +==== Verwendung von State-Machines in (Low-Power) Embedded Systems ====
 +Es gibt verschiedene Möglichkeiten, wie Zustandsautomaten in ein bestimmtes Systemdesign integriert werden können. Einige Gestaltungsprinzipien sind eher für Entwickler von kleine eingebetteten Systemen geeignet (z.B. den uC in einer Lampensteuerung). Andere sind eher für Entwickler relevant, die nicht so enge Ressourcenbeschränkungen haben.
 +
 +=== Verwendung von Zustandsautomaten in einer Hauptschleife ===
 +In diesem Design ruft eine Endlosschleife - typischerweise die Hauptfunktion - einen oder mehrere Zustandsautomaten nacheinander auf. 
 +{{:wiki:mainloop.png?150 |Verwendung von Zustandsautomaten in einer Hauptschleife}} Dies ist nach wie vor eine der gängigsten Methoden für den Entwurf kleiner eingebetteter Systeme. Die von den Zustandsautomaten verarbeiteten Ereignisinformationen können von globalen oder lokalen Variablen stammen, die von anderen Funktionen oder Interrupt-Handlern eingespeist werden. Die Vorteile dieses Entwurfs sind, dass kein Laufzeit-System erforderlich ist und nur wenig RAM benötigt wird.
 +
 +Die Folgen sind:
 +  * Der gesamte Rahmen-Code muss vom Entwickler bereitgestellt werden.
 +  * Die Hauptschleife muss schnell genug sein, um die geforderte Gesamtreaktionszeit zu erreichen.
 +  * Im Falle von Erweiterungen muss das Timing nochmals sorgfältig überprüft werden.
 +
 +<hidden Beispiel:>
 +<code c>
 +void main(void){
 +  …
 +  sm_A();
 +  sm_B();
 +  …
 +}
 +</code>
 +</hidden>
 +
 +\\
 +==== Verwendung von Zustandsautomaten in einer Hauptschleife mit Event-Queue ====
 +Dieser Entwurf ist ähnlich wie der oben vorgestellte. Allerdings erhält der Zustandsautomat seine Ereignisse aus einer Event-Queue. Die Queue wird durch Timer-Ereignisse, andere Zustandsmaschinen (kooperierende Maschinen) oder Interrupt-Handler gefüllt.
 +{{ :wiki:mainloop_ext.png?400|Verwendung von Zustandsautomaten in einer Hauptschleife mit Ereigniswarteschlange}}
 +Vorteile:
 +  * Ereignisse gehen nicht verloren (Warteschlange)
 +  * Ereignisreihenfolge bleibt erhalten
 +  * Entkopplung der Ereignisverarbeitung von der Ereignisgenerierung.
 +
 +Konsequenzen:
 +  * Ein minimaler Laufzeitrahmen ist erforderlich: typ. Timer und Queue
 +  * Die Hauptschleife muss schnell genug sein, um die erforderliche Gesamtreaktionszeit zu erreichen.
 +
 +Ein minimales Laufzeit-Framework für C ist hier verfügbar: https://github.com/sinelabore/examples/tree/master/lib
 +
 +Es bietet Zeitgeber und Warteschlangen. Der Verwendungszweck ist wie folgt:
 +
 +  * Jeder Zustandsautomat hat eine eigene Queue
 +  * Schließlich benötigt ein Zustandsautomat einen oder mehrere Timer (einmalig/sigle-shot oder zyklisch).
 +  * Ein Zustandsautomat kann so viele Software-Timer wie nötig erstellen. Bei der Erstellung eines Timers muss die Queue des Zustandsautomaten und das Timeout-Ereignis angegeben werden. Für verschiedene Timer ist es sinnvoll, verschiedene Timeout-Ereignisse bereitzustellen.
 +  * Damit der Timer funktioniert, muss eine Tick-Zählervariable zyklisch von einem Timer-Interrupt inkrementiert werden (z.B. alle 10 ms). Die Tickfrequenz sollte auf der Grundlage der minimal erforderlichen Auflösung der Timeout-Zeiten gewählt werden. 
 +  * Eine tick()-Funktion muss in der Hauptschleife aufgerufen werden, um zu prüfen, ob ein Timer abgelaufen ist. Falls eine Zeitüberschreitung eingetreten ist, wird das entsprechende Ereignis in der Queue des Zustandsautomaten gespeichert.
 +  * Die Hauptschleife muss prüfen, ob für einen Zustandsautomaten Ereignisse in seiner Queue gespeichert sind. Wenn es neue Ereignisse gibt, werden sie aus der Queue geholt und der Zustandsautomat wird mit dem Ereignis aufgerufen.
 +
 +
 +<hidden Der Beispielcode mit zwei Zustandsautomaten zeigt das allgemeine Prinzip:>
 +
 +<code c>
 +// tick irq
 +void tick(void){
 +  pulseCnt++;
 +}
 +
 +
 +void main(void){
 +  ….
 +
 +  // create two queues for two state machines and init the timer subsystem
 +  fifoInit(&fifo2VendingMachine, fifo2VendingMachineRawMem, 8);
 +  fifoInit(&fifo2ProductStoreMachine, fifo2ProductStoreMachineRawMem, 8);
 +  timerInit();
 +  
 +  …
 +  
 +  while (1) {
 +    uint8_t evt;
 +    //
 +    // Check if there are new events for the state machine. If yes,
 +    // call state machine with event.
 +    //
 +    bool fifoEmpty = fifoIsEmpty(&fifo2VendingMachine);
 +    if (!fifoEmpty) {
 +      fifoGet(&fifo2VendingMachine, &evt);
 +      vending_machine(&vendingMachine, evt);
 +    }
 +
 +    fifoEmpty = fifoIsEmpty(&fifo2ProductStoreMachine);
 +    if (!fifoEmpty) {
 +      fifoGet(&fifo2ProductStoreMachine, &evt);
 +      product_store_sm(&productStoreMachine, evt);
 +    }
 +
 +   // any new timeouts?
 +   tick();
 +  }
 +</code>
 +</hidden>
 +
 +\\
 +Wie in der Abbildung oben dargestellt, können auch andere Zustandsautomaten oder Interrupt-Handler Ereignisse in die Queue eines Zustandsautomaten schieben. Ein Beispiel hierfür ist unten dargestellt.
 +\\
 +<hidden Beispiel:>
 +<code c>
 +// add event evErr to a state machine queue.
 +void ISR_Btn1() {
 +  fifoPut(&fifo2VendingMachine, evErr);
 +}
 +</hidden>
 +
 +\\
 +==== Verwendung von Zustandsautomaten in einer Hauptschleife mit Queue, optimiert für geringen Stromverbrauch ====
 +Bei der Entwicklung von Systemen mit geringstem Stromverbrauch besteht ein wichtiges Ziel darin, den Prozessor so lange wie möglich im Energiesparmodus (deep sleep) zu halten und ihn nur aufzuwecken, wenn etwas verarbeitet werden muss. Der Entwurf ist dem oben beschriebenen sehr ähnlich. Der Hauptunterschied besteht darin, dass die Hauptschleife nicht die ganze Zeit läuft, sondern nur dann, wenn ein Ereignis eingetreten ist. Der Timer-Service für das kleine Laufzeit-Framework wird über den Timer-Interrupt abgewickelt.
 +\\
 +<hidden Ein Skelett für den MSP430 sieht wie folgt aus:>
 +
 +<code c>
 +void main(void){
 +
 +  // init system
 +
 +  while(1) {
 +   
 +    // check event queues and run the state machine as shown above
 +
 +     …
 +     __bis_SR_register(LPM3_bits + GIE);  // Enter low power mode once
 +     __no_operation();                    // no more events to process
 +   }
 +}
 +
 +
 +// Timer A0 interrupt service routine. If the timer 
 +// function tick() returns true there
 +// is a timeout and we wakeup the main loop.
 +#pragma vector=TIMER0_A0_VECTOR
 +__interrupt void Timer_A0(void)
 +{
 +  bool retVal=false;
 +
 +  P1OUT |= BIT0; // toggle for debugging
 +
 +  retVal = tick();
 +
 +  if(retVal){
 +    // at least one timeout timer fired.
 +    // wake up main loop
 +    bic_SR_register_on_exit(LPM3_bits);
 +  }
 +  P1OUT &= ~BIT0; // toggle for debugging
 +  // no more events must be processed
 +}
 +
 +</code>
 +</hidden>
 +\\
 +
 +==== Verwendung von Zustandsautomaten in Interrupts ====
 +Manchmal ist eine zustandsabhängige Interruptbehandlung erforderlich. Dann ist es sinnvoll, den Zustandsautomaten direkt in den Interrupt-Handler einzubetten, um sich weiteren Overhead zu sparen. Typische Anwendungen sind die Vorverarbeitung von Zeichen, die über eine serielle Schnittstelle empfangen werden, Abfragen von Tasten, oder die zustandsabhängige Filterung eines analogen Signals vor der weiteren Verarbeitung. 
 +{{ :wiki:irq.png?100|Verwendung von Zustandsautomaten in Interrupts}}
 +Die Verwendung von Zustandsautomaten in einem Interrupt-Handler kann in jedem Systemdesign nützlich sein.
 +
 +Für die Codegenerierung sind einige Überlegungen notwendig. In der Regel ist es notwendig, Interrupt-Handler mit compilerspezifischen Schlüsselwörtern oder Vektorinformationen usw. auszustatten. Außerdem haben Interrupt-Service-Handler keine Parameter und keinen Rückgabewert. Um diese Anforderungen zu erfüllen, bietet der Sinelabore Codegenerator die Parameter ''StateMachineFunctionPrefixHeader'', ''StateMachineFunctionPrefixCFile'' und ''HsmFunctionWithInstanceParameters''
 +
 +<hidden> Das folgende Beispiel zeigt eine Interrupt-Service-Routine mit den compilerspezifischen Erweiterungen, wie sie von mspgcc verlangt werden.>
 +
 +<code c>
 +// generated state machine code for an irq
 +
 +interrupt (INTERRUPT_VECTOR) IntServiceRoutine(void)
 +{
 +   /* generated statemachine code goes here */
 +}
 +</code>
 +</hidden>
 +
 +
 +Um diesen Code zu erzeugen, setzen Sie die Schlüssel/Wert-Paare in Ihrer Konfigurationsdatei wie folgt:
 +
 +<code>
 +StateMachineFunctionPrefixCFile=interrupt (INTERRUPT_VECTOR)
 +HsmFunctionWithInstanceParameters=no
 +</code>
 +
 +Wenn sich das Präfix der Interruptroutine über mehr als eine Zeile erstrecken muss, kann das Zeichen für den Zeilenumbruch "\n" wie unten gezeigt eingefügt werden:
 +
 +<code>
 +StateMachineFunctionPrefixCFile=#pragma vector=UART0TX_VECTOR\n__interrupt void
 +</code>
 +
 +Präfixe für den Header und die C-Datei können separat angegeben werden. 
 +
 +==== Verwendung von Zustandsautomaten mit einem Echtzeitbetriebssystem ====
 +Bei diesem Entwurf läuft jeder Zustandsautomat normalerweise im Kontext einer eigenen Task. Der prinzipielle Aufbau ist in der folgenden Abbildung dargestellt.
 +
 +{{ :wiki:task_ab.png?400 |Verwendung von Zustandsautomaten mit einem Echtzeitbetriebssystem}}
 +
 +Jede Task führt einen Zustandsautomaten (oft als 'Active Object bezeichnet) in einer endlosen while-Schleife aus. Die Tasks warten auf neue Ereignisse, die von der Zustandsmaschine verarbeitet werden. Liegt kein Ereignis vor, wird die Task vom RTOS in den Idle-Modus versetzt. Sind ein oder mehrere neue Ereignisse vorhanden, weckt das RTOS die Task auf.  Der verwendete RTOS-Mechanismus zur Ereignissignalisierung kann unterschiedlich sein. Häufig wird jedoch eine Queue verwendet. In der Queue können Ereignisse aus verschiedenen Quellen gespeichert werden. Z.B. aus einer anderen Task oder aus einer Interrupt-Service-Routine. 
 +Dieses Design kann mit jedem Echtzeitbetriebssystem realisiert werden. Lediglich die Mechanismen für den Ereignistransport können sich unterscheiden.
 +
 +Vorteile:
 +  * Effiziente und gut getestete Laufzeitumgebung, die vom Echtzeitbetriebssystem bereitgestellt wird
 +  * Priorisierung von Aufgaben und Scheduling möglich
 +  * Zustandsmaschinen-Verarbeitungszeiten sind voneinander entkoppelt.
 +
 +Die Folgen:
 +  * Notwendigkeit eines Echtzeitbetriebssystems (Komplexität, Speicherbedarf, Lizenzkosten ...)
 +
 +Im How-to-Abschnitt wird ein Beispiel für dieses [[wiki:howto:rtos|Muster mit FreeRTOS]] vorgestellt. Die folgenden Beispiele zeigen Code für das [[https://www.rtems.org|RTEMS]] und [[https://www.segger.com/products/rtos/embos/|embOS]].
 +
 +<hidden Beispiele für RTEMS>
 +<code c>
 +// rtems specific task body
 +rtems_task oven_task(rtems_task_argument unused)
 +{
 +  OVEN_INSTANCEDATA_T inst = OVEN_INSTANCEDATA_INIT;
 +
 +  for ( ; ; ) {
 +    // returns if one event was processed
 +    oven(&inst);
 +  }
 +}
 +
 +
 +// generated state machine code
 +extern rtems_id Queue_id;
 +uint8_t msg=NO_MSG;
 +size_t received;
 +rtems_status_code status;
 +
 +void   oven(OVEN_INSTANCEDATA_T *instanceVar) {
 +
 +  OVEN_EV_CONSUMED_FLAG_T evConsumed = 0U;
 +
 +
 +  /*execute entry code of default state once to init machine */
 +  if (instanceVar->superEntry == 1U) {
 +    ovenOff();
 +
 +    instanceVar->superEntry = 0U;
 +  }
 +
 +  /* action code */
 +  /* wait for message */
 +  status = rtems_message_queue_receive(
 +             Queue_id,
 +             (void *) &msg,
 +             &received,
 +             RTEMS_DEFAULT_OPTIONS,
 +             RTEMS_NO_TIMEOUT
 +           );
 +  if ( status != RTEMS_SUCCESSFUL )
 +    error_handler();
 +  }else{
 +    switch (instanceVar->stateVar) {
 +      // generated state handling code
 +     …
 +    }
 +  }
 +}
 +</code>
 +</hidden>
 +
 +\\
 +<hidden Hier ist ein ähnliches Beispiel für das embOS RTOS von Segger.>
 +
 +
 +<code c>
 +// state machine instance
 +SM_INSTANCEDATA_T instanceVar = SM_INSTANCEDATA_INIT;
 +
 +// Task and queue objects.
 +static OS_STACKPTR int Stack_TASK_1[128];   /* Task stacks */
 +static OS_TASK         TCB_TASK_1;         /* Task-control-blocks */
 +static OS_Q            MyQueue;
 +static char            MyQBuffer[100];
 +
 +OS_TIMER MyTimer;
 +
 +char txbuf[32] ;
 +
 +// Routine called from the embOS RTOS to signal 
 +// a timeout. A timeout event is sent to the state
 +// machine. Multiple timer callback functions might be created if
 +// several timers are needed at the same time. Each one then fires an own
 +// event. E.g. ev50ms or ev100ms
 +static void MyTimerCallback(void) {
 +
 +    uint8_t msg=evtTimeout;
 +
 +    OS_Q_Put(&MyQueue, &msg, 1);
 +}
 +
 +// Task blocked until a new event is present. The new event is 
 +// then sent to the state machine.
 +static void TaskRunningStateMachine(void) {
 +    char* pData;
 +
 +    while (1) {
 +        // waiting for new event
 +        volatile int Len = OS_Q_GetPtr(&MyQueue, (void**)&pData);
 +        volatile char msg = *pData;
 +
 +        sm(&instanceVar, (SM_EVENT_T)msg); // call generated state machine with event
 +
 +        OS_Q_Purge(&MyQueue);
 +
 +    }
 +}
 +</code>
 +</hidden>
 +
 +\\
 +~~DISCUSSION:closed|Hinterlassen sie ihre Kommentare~~
  
-:!: Weitere Details finden Sie auf unseren englischen Seiten. 
de/start.1567334039.txt.gz · Last modified: 2019/09/01 12:33 by pmueller

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki