Introduction

Disclaimer

CCore is released without known bugs or defects (but an intermediate states of the repository between release tags MAY be broken). This code is made "as best as possible" and has a top quality both in design and implementation. It's a reliable piece of software. So if you experience troubles, CCore code is the last place to look for bugs. This does not mean, naturally, that I do guarantee the code is completely bug-free and absolutely perfect. That is not achievable in the mean time. And, of course, everybody is welcome to contribute in its perfection: report a real problem or suggest a design improvement.

Overview

CCore is a platform mainly for the development of real-time applications. It is distributed under the Boost Software License. You can use CCore to build host applications also, but without any real-time properties. We use the term "host application" to designate an application, running on a host machine. "Stand-alone application" means an application, running on a stand-alone device. CCore is a multi-target platform, you can (and, in fact, must!) develop your own target (host or stand-alone) for your specific target device. The installation of CCore provides the following targets:

Target includes compiler and tools, makefile rules and parts of source code, specific for the target. In most cases you are encouraged to develop components of your application using WIN32 target (or another host target) and then port it to a stand-alone target when it's ready. CCore is designed in target-independent and platform-independent manner (despite it's a platform itself!). So it's recommended to follow this practice. Developing components on a host target is usually simpler and more productive.

CCore is implemented using C++14. It enables the full advantage of this renewed language for the embedded and real-time software development. You can also use it to build usual host applications. CCore is well-suited to develop a wide range of applications like servers, mathematical calculations or file-processing utilities. The number of them are supplied as supplementary tools. CCore is based on the comprehensive, type-rich library, well, CCore. That's the core part of the platform. The next part is HCore for host targets and XCore for stand-alone targets. On host targets we rely on an operating system to perform some tasks (like primary memory management, task synchronization etc.). So a host target provides a tiny layer between HCore and OS system calls. But on a stand-alone target we implements these fundamental functions ourself. XCore is an implementation of this part for single-core CPUs. A target provides an implementation of very low-level CPU specific functions XCore is based on. For example, the following 3 functions are required for interrupt lock support:


/* functions */ 

bool IsIntContext() noexcept;

/* classes */ 

class IntLock;

/* class IntLock */ 

class IntLock : NoCopy
 {
   bool enable;

  public:

   IntLock() { enable=Internal::Disable(); }
 
   ~IntLock() { if( enable ) Internal::Enable(); }
   
   struct Internal
    {
     static bool Disable() noexcept;
     
     static void Enable() noexcept;
    };
 };

CCore is designed as a compliant C++14 library. There are a few places where we are using non-standard language extensions. In fact, we need only two of them: restrict and initialization priority attributes for static objects, supported in gcc family of compilers. As the result, there is no restriction on the compiler optimization level, unlike other OS cores. Another advantage is the platform-independence of the code.

CCore

CCore is a language support library. It's created to provide an improved language support for C++ and to replace the error-prone and obsolete "old bad" C RTL and STL. CCore is focused on the "everyday programmer's needs", it tries to be small, but powerful.

In particular, it contains:

  1. PtrLen and Range for convenient and efficient ranges and loops,
  2. Printf with class print support, easily customized output devices and options,
  3. Function for efficient callback support,
  4. Mersenne Twister random number generator,
  5. Radix-tree based heap with real-time properties,
  6. Swap-based intro-sort,
  7. Cmp/Move/Swap support,
  8. Error-handling infrastructure to support multi-error handling,
  9. Any kind of lists,
  10. Radix and Red-Black trees,
  11. Properly implemented dynamic arrays (bye-bye STL),
  12. Packet, an async processing infrastructure, a key foundation for a system layer and server applications,
  13. Integer library, you may even calculate 1000000 digits of π,
  14. Crypton library,
  15. Fast log and event recorder,
  16. New network infrastructure, based on PTP protocol,
  17. DDL(data-definition language) implementation,
  18. and many other helpful tools.

XCore

XCore is a multi-task core for embedded systems. It is combined with CCore library. The core is designed for single-core CPU systems.

XCore:

  1. provides a rich set of synchronization objects and a simple way to construct additional ones,
  2. implements priority inheritance and deadlock detection for mutexes,
  3. task scheduler switches on high frequency (10 kHz for BeagleBoneBlack platform), that provides fine-grained task execution, ensure the system is highly responsive,
  4. scheduler supports task priority relaxation, that protects low priority tasks from complete execution hold-on,
  5. full support for C++14,
  6. no limitation for compiler optimization level,
  7. clear target requirements.

Task synchronization

One of the innovation in CCore is the set of task synchronization objects. Mutex, Event and Sem(aphore) are common. But we widely use also: AntiSem, ResSem, MultiSem, MultiEvent.

AntiSem is useful for the resource control:


class AntiSem : public Funchor_nocopy
 {
   ....

  public:
   
   // constructors
  
   explicit AntiSem(ulen level=0);
   
   explicit AntiSem(TextLabel name,ulen level=0);
   
   ~AntiSem();
   
   // add/sub
   
   void add(ulen dcount);
   
   void sub(ulen dcount);
   
   // inc/dec
   
   void inc() { add(1); }
   
   void dec() { sub(1); }
   
   // wait
   
   bool try_wait();
   
   void wait();
   
   bool wait(MSec timeout);
   
   bool wait(TimeScope time_scope);
    
   // functions
   
   Function<void (void)> function_inc();
   
   Function<void (void)> function_dec();
 };

AntiSem waits until "all taken is released". I.e. until the internal counter falls below a given level (usually 0).

ResSem is used to control a resource allocation. It is combined Sem and AntiSem.


class ResSem : public Funchor_nocopy
 {
  public:
   
   // constructors
  
   explicit ResSem(ulen max_count);
   
   ResSem(TextLabel name,ulen max_count);
   
   ~ResSem();
   
   // give
   
   void give();
   
   // take
  
   bool try_take();
  
   void take();
  
   bool take(MSec timeout);
  
   bool take(TimeScope time_scope);
   
   // wait
   
   bool try_wait();
   
   void wait();
  
   bool wait(MSec timeout);
   
   bool wait(TimeScope time_scope);
   
   // functions
   
   Function<void (void)> function_give();
 };

ResSem has the internal counter between 0 and the given max_count (initial value). You can take and give it back. take may wait if the counter is zero. Finally, wait waits until the counter reaches max_count.

MultiSem and MultiEvent are "multi-channel" versions of the Sem and Event. They can wait for one of the several events. They also ensure the "fairness" in the event reporting.


template <ulen Len>
class MultiEvent : public Funchor_nocopy
 {
  public:

   // constructors
  
   MultiEvent();
   
   explicit MultiEvent(TextLabel name);
   
   ~MultiEvent();

   // trigger
  
   bool trigger(ulen index); // [1,Len]
   
   // wait
   
   ulen try_wait(); // [0,Len]
   
   ulen wait(); // [1,Len]
   
   ulen wait(MSec timeout); // [0,Len]
   
   ulen wait(TimeScope time_scope); // [0,Len]
   
   // functions
   
   template <ulen Index>
   Function<void (void)> function_trigger();
 };
 
template <ulen Len> 
class MultiSem : public Funchor_nocopy
 {
  public:
  
   // constructors

   MultiSem();
   
   explicit MultiSem(TextLabel name);
   
   ~MultiSem();

   // give
  
   void give(ulen index); // [1,Len]
   
   // take
   
   ulen try_take(); // [0,Len]
   
   ulen take(); // [1,Len]
   
   ulen take(MSec timeout); // [0,Len]
   
   ulen take(TimeScope time_scope); // [0,Len]
   
   // functions
   
   template <ulen Index>
   Function<void (void)> function_give();
 };
 

All synchronization classes "publish" "giving" functions callbacks.

Packet

Packet is a micro-task. It has the associated small stack to store call parameters and return values. A larger data buffer may be also attached. Packets allow design of a request processing systems as a collection of packet-processing devices. Packets are initiated by some initiator and passed to the processing device. It may pass a packet further until it's completed or cancelled by the initiator. Initiator assigns a completion function for a packet. So when packet is completed it goes to this function, where result can be extracted and packet is disposed. Also a completion signaling is performed there.

Lists, trees, arrays

CCore includes implementations of lists, trees, and arrays.

CCore lists and trees are intrusive (non-intrusive containers are also provided). Intrusive linked containers have the following advantages:

CCore implements wide range of lists, both single linked and double linked, linear and circular.

CCore implements radix and red-black trees.

CCore provides implementation for dynamic arrays.

PTP

PTP — packet transaction protocol. It supersedes TCP as a main network protocol for client-server applications. PTP is a packet-based, parallel and transactional protocol. It is simple and convenient for the development of "remote call" type interactions. Parallel nature of PTP makes obsolete such tricks as multi-thread FTP download. PTP is working atop a packet point-to-point or point-to-multipoint service like UDP (or even raw Ethernet). PTP has ability to adapt for smaller MTU lengths, that is important to combine with another network protocols, which can reduce MTU (for example, to implement encapsulation or encryption).

There are several typical problems when network interaction is developed based on TCP:

PTP resolves all these issues.

PTPServer — a PTP server on a host machine, it provides access to the host file system via HFS over PTP protocols.

DDL

DDL — data definition language. It is designed to represent an abstract linked data in a textual form. DDL has a C-style syntax. Here is a short example of a parser state machine representation, expressed in DDL:


struct State
 {
  StateIndex state;

  struct Transition { NTIndex ntt; State *state; } [] transitions;

  struct Final { TIndex t; Rule *rule; } [] finals;
 };

State[22] StateTable=
 {
  { 0 ,
   {
    { 1 , StateTable+1 },
    { 5 , StateTable+2 },
    { 6 , StateTable+3 },
    { 7 , StateTable+4 },
    { 8 , StateTable+16 }
   },
   {
    { 1 , RuleTable+0 },
    { 5 , RuleTable+0 }
   }
  },

....

DDL can be used for a manually introduced data, like configuration information. Or it can be used to store a program-generated data to exchange with different data-processing programs.

LR1 parser generator

LangLR1 is a tool for generation LR1 parser state machines. It takes a language description and produces a .ddl file with a state machine description. You can use this description further to generate code in any suitable for your purpose form. Conditional recursive grammars are used to describe a language.

Regen

Regen is a tool for generating helper classes and constants from a register description. It is used to simplify and beautify the low-level register device programming.