10 Revize a2c4f5ddd4 ... 06cdab196b

Autor SHA1 Zpráva Datum
  Ivan Baidakou 06cdab196b major examples refactoring před 2 roky
  Ivan Baidakou c3bcb52625 fix doc před 2 roky
  Ivan Baidakou c8dc1ab5fe format před 2 roky
  Ivan Baidakou e04486c95e minor fixes před 2 roky
  Ivan Baidakou 623e7b8cf9 minor cleanings před 2 roky
  basiliscos bb49481f11 Update 'README.md' před 2 roky
  basiliscos f937dcf817 Merge branch 'mb' of am/cpp-rotor-light into master před 2 roky
  Ivan Baidakou f8329513d6 fix typos před 2 roky
  Ivan Baidakou 30f675e468 fix typos před 2 roky
  Ivan Baidakou a1ddabb276 fix copyrights před 2 roky

+ 2 - 1
CMakeLists.txt

@@ -13,6 +13,7 @@ if (NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET AND
   set(CMAKE_VISIBILITY_INLINES_HIDDEN YES)
 endif ()
 
+set(ROTOR_LIGHT_DOC OFF CACHE BOOL "generate docs using doxygen")
 set(ROTOR_LIGHT_TIMEPOINT "int64_t" CACHE STRING "TimePoint type [default: int64_t]")
 set(ROTOR_LIGHT_DURATION "int32_t" CACHE STRING "Duration type [default: int32_t]")
 
@@ -55,7 +56,7 @@ if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
     endif()
 
     find_package(Doxygen)
-    if (DOXYGEN_FOUND)
+    if (DOXYGEN_FOUND AND ${ROTOR_LIGHT_DOC})
         set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in)
         set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
         configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)

+ 9 - 8
README.md

@@ -13,7 +13,7 @@ care about thread-safety.
 
  - erlang-like hierarchical supervisors
 
- - asynchornous messaging with priorities
+ - asynchronous messaging with priorities
 
  - plaform-agnostic code (including timers)
 
@@ -27,20 +27,21 @@ care about thread-safety.
 
  - compile-time actor hierarchies 
 
-## ping-pong messaging performance & binary size
+## ping-pong messaging performance & .text section size
 
 
-
-|                | messages/second | binary size
-|:--------------:|:---------------:|:--------------:
-| host (1)       | ~48.7M          | ~26kb
-| Arduino Uno R3 | ~5.2K           | ~9kb
-| Microblaze (2) | ~58.8K          | ~42kb
+|                       | messages/second | binary size
+|:---------------------:|:---------------:|:--------------:
+| host (1)              | ~47.7M          | 13026 bytes
+| Arduino Uno R3        | ~10.0K          | 8872 bytes
+| xilinx microblaze (2) | ~58.8K          | 42868 byes
+| STM32-H743ZI          |  ...            | 13724 bytes
 
 All examples can be measured with `examples/ping-pong-throughput.cpp`,
 compiled with `CMAKE_BUILD_TYPE=MinSizeRel` and the stripped
 
 (1) Setup: Intel Core i7-8550U, Void Linux 5.15.
+
 (2) Zynq xc7z020clg400-1, QMTECH development board. Microblaze standard config, hw mul/div instr. enabled, clocking is 50Mhz.
 
 ## license

+ 1 - 0
docs/Doxyfile.in

@@ -803,6 +803,7 @@ ALPHABETICAL_INDEX = no
 
 INPUT                  =  @CMAKE_CURRENT_SOURCE_DIR@/include                \
                           @CMAKE_CURRENT_SOURCE_DIR@/docs/Introduction.md   \
+                          @CMAKE_CURRENT_SOURCE_DIR@/docs/Tutorial.md       \
                           @CMAKE_CURRENT_SOURCE_DIR@/docs/Conception.md
 
 

+ 5 - 5
docs/Introduction.md

@@ -1,11 +1,11 @@
 # Intoduction
 
-Modelling your applications with [actors](https://en.wikipedia.org/wiki/Actor_model),
+Modeling your applications with [actors](https://en.wikipedia.org/wiki/Actor_model),
 you can get two benefits: **messaging** and **supervising** between *actors*.
 
-Actor is a reactive entity with state, which can receive and send messages; actor
+Actor is a reactive entity with a state, which can receive and send messages; actor
 state is usually changed with new messages. Every **message** has destination (one or
-more actors) and user-defined payload. Message is send with *fire-and-forget*
+more actors) and user-defined payload. Messaging is send with *fire-and-forget*
 approach, i.e. sender does not care about further delivery and processing of the
 message, like with UDP protocol.
 
@@ -18,7 +18,7 @@ With all that properties it is possible to assemble [concurrent](https://en.wiki
 failure handling. *Synchronization* makes it sure that starting sending messages to
 actors is performed when actors are ready. *Failure handling* allows to use different
 restart strategies of a failing actor, or group of actors, or even escalate locally
-unresolveable problem into upper layers... upto restarting the whole board restart.
+unresolvable problem into upper layers... upto restarting the whole board restart.
 
 `rotor-light` is platform-neutral framework, i.e. it can be build and run on any
 plaform, which conforms the requirements (C++17, basically).
@@ -26,7 +26,7 @@ plaform, which conforms the requirements (C++17, basically).
 # rotor and rotor-light
 
 There is a successful C++ actor microframework [rotor](github.com/basiliscos/cpp-rotor/), 
-when a few colleques of mine aske me to have something similar for embedded systems.
+when a few colleagues of mine asked me to have something similar for embedded systems.
 
 The following table highlight the differences
 

+ 45 - 44
docs/Tutorial.md

@@ -4,7 +4,7 @@
 
 This is platform-neural tutorial, although hardware implementation refers to
 arduino. The full code can be found at `examples/atmega328p/blink-led.cpp`. In
-this example the blinking with LED using `rotor-light`.
+this example the blinking with LED is performed using `rotor-light`.
 
 Let's start from the inclusion of headers:
 
@@ -20,7 +20,7 @@ namespace rl = rotor_light;
 
 In this example, there is only single message - `BlinkCommand`. It is good
 practice to define messages in it's own namespace, as it is much more
-convenient to deail with messages later (handle and send).
+convenient to deal with messages later (handle and send).
 
 ```
 namespace message {
@@ -37,10 +37,10 @@ struct BlinkCommand : rl::Message {            // (1)
 
 The `BlinkCommand` message have to be derived from `rotor_ligth` `Message` (1),
 and as the `BlinkCommand` has no payload, it just reuses its parent
-consturctor (`using rl::Message::Message`, (3)).
+constructor (`using rl::Message::Message`, (3)).
 
-As the `rotor_ligth` does not uses RTTI it should somehow identify
-message types at runtime. That's why overriden `get_type_id()` method (4)
+As the `rotor_ligth` does not use RTTI it should somehow identify
+message types at runtime. That's why overridden `get_type_id()` method (4)
 should be available on every message type. `rotor_light` does not care
 how you, as the user, define your message type, the only requirement is the
 uniqueness of them. The easiest way to achieve that is just have line (2)
@@ -66,10 +66,10 @@ void initialize() override {  // (7)
   }
 
   void blink() {
-    toggle_led();                                  // (13)
-    add_event(delay, [](void *data) {              // (14)
-        auto self = static_cast<Blinker *>(data);  // (15)
-        self->send<message::BlinkCommand>(0, id);  // (16)
+    toggle_led();                                        // (13)
+    add_event(delay, [](void *data) {                    // (14)
+        auto self = static_cast<Blinker *>(data);        // (15)
+        self->send<message::BlinkCommand>(0, self->id);  // (16)
     }, this);
   }
 
@@ -82,20 +82,20 @@ rl::Duration delay;  // (18)
 ```
 
 First, your own actor have to inherit from `rl::Actor` (5). The magic number `2` is
-used for preallocation space for actor's message handlers: the one handler from the
-base class defined by the framework (might be changed in the future), and the other
-one use the user-provided `on_blink_command`. The alias (6) makes it more convenient
-to refer the base class in the future.
+used for preallocation of space for actor's message handlers: one handler from
+the base class defined by the framework (might be changed in the future), and the
+other one use the user-provided `on_blink_command`. The alias (6) makes it more
+convenient to refer the base class in the future.
 
 To react on the `BlinkCommand` message, the actor should subscribe to it (8). The
-proper place to do that is to override `initialize()` method (7), of the parent class.
+proper place to do that is to override `initialize()` method (7) of the parent class.
 Of course, parent class **must** be initialized too, that's why don't forget to
 invoke its initialization (9).
 
-When actor is ready, its `advance_start()` (10) is invoked; as usually correspondig
-parents method should be called (11) and the initial blink (12) is performed. In
+When actor is ready, its `advance_start()` (10) is invoked; as usually corresponding
+parent method should be called (11) and the initial blink (12) is performed. In
 the implementation of `blink()` the platform-dependent LED toggle is done at (13),
-and then delayed the same method invokation is scheduled (14-16). In (14) the
+and then delayed the same method invocation is scheduled (14-16). In (14) the
 capture-less lambda is scheduled to be invoked with `this` as the parameter when
 `delay` (18) amount of time passes. The lambda *must* be capture-less. After casting
 back `void*` to the actor class (15), it sends self a `BlinkCommand` message (16)
@@ -122,7 +122,7 @@ using Supervisor = rl::Supervisor<                                      // (21)
 `rotor-light` uses queues to store and process messages. There are no dynamic
 allocations, so, all sizes have to be known at compile-time, including the sizes
 of all used messages. The `Storage` helper (18) is used to determine the maximum
-allowed storage size per message: here **all messages** in the application
+allowed space size per message: here **all messages** in the application
 should be enumerated, i.e. messages from the framework and user-defined.
 
 Next, the application queue (or master queue) should be defined (19). It uses
@@ -131,7 +131,7 @@ Next, the application queue (or master queue) should be defined (19). It uses
 queue, but user-defined messages might use different queues. Queues with
 higher priorities are processed earlier than queues with lower priorities. So,
 to define master queue with two subqueues (priorities 0 and 1) with sizes
-enought to store 15 and 10 messages respectively, the following code should
+enough to store 15 and 10 messages respectively, the following code should
 be used
 
 ```
@@ -139,16 +139,16 @@ using Queue = rl::Queue<Storage, 15, 10>;
 ```
 
 What is the recommended queue sizes? Well, it is completely application defined, but
-for `rotor-light` messages there should be enought space to store `N * 2` messages,
+for `rotor-light` messages there should be enough space to store `N * 2` messages,
 where `N` is the total number of actors (including supervisors).
 
-What happens, if a queue is full? The next send mesage is simply discarded, you'll
+What happens, if a queue is full? The next send message is simply discarded, you'll
 be notified about that as the `send()` method returns `false`. If the discarded
-message is `rotor-light` message, then the framework behavior is undefined. So,
+message is `rotor-light` message, then *the framework behavior is undefined*. So,
 either do not overload default queue (i.e. with zero-priority), or use different
 queues for user-messages. NB: there is a spike of `rotor-light` messages only
 during (re)starting supervisor; when everything is *operational* there is almost no
-framework mesages.
+framework messages.
 
 The Planner (20) is used to schedule future events, like it is shown at `add_event`
 (14). The number in angle braces defines planner capacity at compile-time. When event
@@ -156,10 +156,10 @@ time arrives, it is removed from the planner. Please note, that `rotor-light` do
 not schedules any events into the planner, so its capacity and usage is completely
 controlled by the user code.
 
-How the framework interracts with the planner? If the root supervisor is used in
+How the framework interacts with the planner? If the root supervisor is used in
 *poll mode*, then, when there are no more messages left, the current time is
 requested, and all timed out event handlers are executed. Then the special message
-`refresh-time` is send to the supervisor and the procedure repeates enlessly.
+`refresh-time` is send to the supervisor and the procedure repeats endlessly.
 Indirectly, this is similar to the code like:
 
 ```
@@ -184,12 +184,12 @@ above and the event awaiting (22) code. This makes it possible to enter into
 power-save mode and wake up on external timer or interrupt. See `*-await.cpp`
 examples.
 
-In the line (22) the used supervisor is defined: the amount of handlers is specified
+In the line (21) the used supervisor is defined: the amount of handlers is specified
 (the same as for actor at (5), as each supervisor is an actor) and all its
 child actors (including other supervisors) are enumerated. The only `Blinker`
 child actor is specified in the current example.
 
-Let's move forward. In the folowing code `rotor-light` global variables are
+Let's move forward. In the following code `rotor-light` global variables are
 allocated:
 
 ```
@@ -247,7 +247,7 @@ runtime overhead (as it uses `std::get` under the hood) and additional actors
 setup can be performed here (26), i.e. delay 1/4 of seconds between LED toggle.
 
 Please note, that setup phase (23-26) need to be performed **only once** despite
-of possible mutiple actors restarts, i.e. actor identities are preserved during
+of possible multiple actors restarts, i.e. actor identities are preserved during
 object lifetimes.
 
 Then, the whole machinery receive initial impulse (26). The `true` value here means
@@ -261,7 +261,7 @@ failure, there is nothing left to do than exit and the whole board restart.
 
 ## ping-pong messaging, poll timer
 
-In this example how to doi messaging with `rotor-light` is demonstrated: `ping`
+In this example how to do messaging with `rotor-light` is demonstrated: `ping`
 message is sent from `pinger` actor to `ponger` actor. The `ponger` actor will
 reply back with `pong`  message, then after some delay `pinger` actor repeats
 the same procedure. The full code can be found at
@@ -287,7 +287,8 @@ struct Pong : rl::Message {
 ```
 
 The `ping` and `pong` messages are content-less, why there is need of them for all?
-Because there is need to demonstrate how to send and receive messages.
+Because there is need to demonstrate how to send and receive messages and
+distinguish them by type.
 
 The `Pinger` actor code is:
 
@@ -322,9 +323,9 @@ struct Pinger : rl::Actor<2> {
 
 ```
 
-As usually, the `initialize()` should be overriden to subscribe on `pong` messages
+As usually, the `initialize()` should be overridden to subscribe on `pong` messages
 (28). The `pinger` actor plays an *active role*, i.e. it sends initial `ping`
-message. This is performed in the overriden `advance_start()` method (29), which is
+message. This is performed in the overrisden `advance_start()` method (29), which is
 invoked as soon as the actor is ready: the default implementation machinery is
 invoked (30), and for convenience `ping()` (31) method is called. The `ping()` (32)
 method implementation is simple: after LED toggle, it sends the `ping` message (33).
@@ -374,7 +375,7 @@ using Storage = rl::traits::MessageStorage<rl::message::ChangeState,
                                            message::Pong>;
 ```
 
-The standard supervisor; it owns `pinger` and `ponger` actors. The `Storage`
+The standard supervisor is used; it owns `pinger` and `ponger` actors. The `Storage`
 allocates enough space for `Ping` and `Pong` messages.
 
 
@@ -398,17 +399,17 @@ int main(int, char **) {
 }
 ```
 
-The most iteresting part is the setup-phase (39-40). `pinger` and `ponger` actors
+The most interesting part is the setup-phase (39-40). `pinger` and `ponger` actors
 should know each others addresses, and the addresses are available only after
 context binding.
 
-When everything is ready, it enters into main loop (43). In it it either delivers
-`rotor-light` messages or waits, until the next event time occurs. It uses
+When everything is ready, it enters into main loop (43). In the loop it either
+delivers `rotor-light` messages or waits, until the next event time occurs. It uses
 **busy waiting** by actively polling (querying) timer, whether the next event time
 happend. As usually for busy waiting, it consumes 100% CPU time, which is common
 strategy for embedded/real-time applications.
 
-It is possible however do something else, instead of endless timer polling, i.e.
+It is possible however to do something else, instead of endless timer polling, i.e.
 do CPU sleep (and consume less current and do less CO2 emission). See the
 next section.
 
@@ -419,12 +420,12 @@ From user point of view the example does the same as the previous one:
 it sends `ping` message, receives `pong` message, blinks with LED and after some
 delay the procedure repeats.
 
-However, the noteable difference is in the delay: in the previous example it
-endlessly polls the timer whether the time arrives for the next event burning
+However, the notable difference is in the delay: in the previous example it
+endlessly polls the timer whether the time arrives for the next event, burning
 CPU cycles, in the current example it does energy-efficient sleeping while
 there is noting to do (i.e. no messages) between events.
 
-As the main logic is the same, the artor-related code is also the same; only
+As the main logic is the same, the actor-related code is also the same; only
 `main()` function differs. 
 
 ```
@@ -459,7 +460,7 @@ int main(int, char **) {
 
 ```
 
-Firsly, the timer poll via recursively sending self `refresh-timer` message should
+Firstly, the timer poll via recursively sending self `refresh-timer` message should
 be disabled (44). Then the infinite loop (45) should be started, as the supervisor
 exits, when it has no more messages to process (46). The first nearby event should
 be extracted from planner (47), if any (48), and then perform platform-specific
@@ -469,15 +470,15 @@ loop iteration in (46).
 ## integration
 
 Lets suppose, that there is a need to read a custom port on idle or perform some
-I/O activity, maybe indirecly via calling some other library on each iteration.
+I/O activity, maybe indirectly via calling some other library on each iteration.
 How can that be done?
 
 One of the ways to accomplish that is shown above: disable timer poll and do
-the needed activity istead of entering into powersave mode, or in addition to
+the needed activity instead of entering into powersave mode, or in addition to
 the powersave mode.
 
 Then second way is to have a custom supervisor with `on_refhesh_timer` method
-overriden, like that:
+overridden, like that:
 
 ```
 struct MySupervisor : rl::Supervisor<3, MyActor1, MyActor2, ...> {

+ 5 - 1
examples/CMakeLists.txt

@@ -10,8 +10,12 @@ elseif ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "MICROBLAZE")
     add_subdirectory("mb")
 elseif ("${TOOLCHAIN_PREFIX}" STREQUAL "")
     add_subdirectory("host")
+    set(CMAKE_SIZE_UTIL size CACHE INTERNAL "size tool")
 endif()
 
+
 # any platform, as there is no timer
 add_executable(ping-pong-throughput ping-pong-throughput.cpp)
-target_link_libraries(ping-pong-throughput rotor_light)
+target_link_libraries(ping-pong-throughput board)
+add_custom_command(TARGET ping-pong-throughput
+    POST_BUILD COMMAND ${CMAKE_SIZE_UTIL} ping-pong-throughput)

+ 27 - 22
examples/NUCLEO-H743ZI2/CMakeLists.txt

@@ -27,30 +27,35 @@ add_definitions(-DDEBUG
         -DUSE_FULL_ASSERT
         )
 
-add_executable(${PROJECT_NAME}
-        ping-pong-poll.cpp
-        startup_stm32h743xx.s
-        Core/Src/main.c
-        Core/Src/gpio.c
-        Core/Src/stm32h7xx_it.c
-        Core/Src/system_stm32h7xx.c
-        Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_utils.c
-        Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_rcc.c
-        Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_pwr.c
-        Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_gpio.c
-        Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_exti.c
-        ${LINKER_SCRIPT})
-
-target_include_directories(${PROJECT_NAME} PUBLIC
-        ${CMAKE_CURRENT_SOURCE_DIR}/Core/Inc
-        ${CMAKE_CURRENT_SOURCE_DIR}/Drivers/STM32H7xx_HAL_Driver/Inc
-        ${CMAKE_CURRENT_SOURCE_DIR}/Drivers/CMSIS/Device/ST/STM32H7xx/Include
-        ${CMAKE_CURRENT_SOURCE_DIR}/Drivers/CMSIS/Include)
-
-target_link_libraries(${PROJECT_NAME} rotor_light)
+add_library(board
+    board.cpp
+    startup_stm32h743xx.s
+    Core/Src/main.c
+    Core/Src/gpio.c
+    Core/Src/stm32h7xx_it.c
+    Core/Src/system_stm32h7xx.c
+    Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_utils.c
+    Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_rcc.c
+    Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_pwr.c
+    Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_gpio.c
+    Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_ll_exti.c
+    ${LINKER_SCRIPT}
+)
+target_include_directories(rotor_light PUBLIC "${LIBSTDCPP_HOME}/include")
+target_include_directories(board PUBLIC
+   "${CMAKE_CURRENT_SOURCE_DIR}"
+    ${CMAKE_CURRENT_SOURCE_DIR}/Core/Inc
+    ${CMAKE_CURRENT_SOURCE_DIR}/Drivers/STM32H7xx_HAL_Driver/Inc
+    ${CMAKE_CURRENT_SOURCE_DIR}/Drivers/CMSIS/Device/ST/STM32H7xx/Include
+    ${CMAKE_CURRENT_SOURCE_DIR}/Drivers/CMSIS/Include
+)
+target_link_libraries(board rotor_light)
+
+add_executable(${PROJECT_NAME} ping-pong-poll.cpp)
+target_link_libraries(${PROJECT_NAME} board)
 
 target_link_options(${PROJECT_NAME} PUBLIC
         LINKER:-Map=${PROJECT_BINARY_DIR}/${PROJECT_NAME}.map
         -T ${LINKER_SCRIPT})
 
-set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX .elf)
+set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX .elf)

+ 31 - 0
examples/NUCLEO-H743ZI2/board.cpp

@@ -0,0 +1,31 @@
+#include "board.h"
+#include "gpio.h"
+#include "main.h"
+
+extern "C" void SystemClock_Config(void);
+
+extern "C" int64_t get_current_time(void);
+
+void Board::init_start() {
+  /* Reset of all peripherals, Initializes the Flash interface and the Systick.
+   */
+  LL_APB4_GRP1_EnableClock(LL_APB4_GRP1_PERIPH_SYSCFG);
+
+  /* System interrupt init*/
+  NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
+  /* Update the time base */
+
+  /* Configure the system clock */
+  SystemClock_Config();
+  MX_GPIO_Init();
+}
+
+void Board::enable_interrupts() {}
+
+void Board::enable_timer() { SysTick_Config(SystemCoreClock / 1000); }
+
+rl::TimePoint Board::get_now() { return get_current_time(); }
+
+void Board::toggle_led() { LL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin); }
+
+void Board::delay() {}

+ 21 - 0
examples/NUCLEO-H743ZI2/board.h

@@ -0,0 +1,21 @@
+#pragma once
+
+#include "rotor-light.hpp"
+
+namespace rl = rotor_light;
+
+struct Board {
+  static constexpr uint32_t samples = 10000000;
+
+  static void init_start();
+
+  static void enable_interrupts();
+
+  static void enable_timer();
+
+  static rl::TimePoint get_now();
+
+  static void toggle_led();
+
+  static void delay();
+};

+ 0 - 0
examples/NUCLEO-H743ZI2/ping-pong-poll.cpp


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů