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.
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 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:
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:
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 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.
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 — 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 — 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.
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 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.