Embedded Systems Architecture: Design and write software for embedded devices to build safe and connected systems
()
About this ebook
Embedded Systems Architecture begins with a bird’s-eye view of embedded development and how it differs from the other systems that you may be familiar with. This book will help you get the hang of the internal working of various components in real-world systems.
You’ll start by setting up a development environment and then move on to the core system architectural concepts, exploring system designs, boot-up mechanisms, and memory management. As you progress through the topics, you’ll explore the programming interface and device drivers to establish communication via TCP/IP and take measures to increase the security of IoT solutions. Finally, you’ll be introduced to multithreaded operating systems through the development of a scheduler and the use of hardware-assisted trusted execution mechanisms.
With the help of this book, you will gain the confidence to work with embedded systems at an architectural level and become familiar with various aspects of embedded software development on microcontrollers—such as memory management, multithreading, and RTOS—an approach oriented to memory isolation.
Related to Embedded Systems Architecture
Related ebooks
Rust for Embedded Systems Rating: 0 out of 5 stars0 ratingsNetwork Programming and Automation Essentials: Get started in the realm of network automation using Python and Go Rating: 0 out of 5 stars0 ratingsEmbedded Systems Programming with C++: Real-World Techniques Rating: 0 out of 5 stars0 ratingsLearning Docker Rating: 5 out of 5 stars5/5Hands-On RTOS with Microcontrollers: Building real-time embedded systems using FreeRTOS, STM32 MCUs, and SEGGER debug tools Rating: 0 out of 5 stars0 ratingsEmbedded Rust Programming: Building Safe and Efficient Systems Rating: 0 out of 5 stars0 ratingsRust for Network Programming and Automation Rating: 0 out of 5 stars0 ratingsSystems Programming: Concepts and Techniques Rating: 0 out of 5 stars0 ratingsMetaprogramming in C#: Automate your .NET development and simplify overcomplicated code Rating: 0 out of 5 stars0 ratingsNetworking Programming with C++: Build Efficient Communication Systems Rating: 0 out of 5 stars0 ratingsComputer Science: Learn about Algorithms, Cybersecurity, Databases, Operating Systems, and Web Design Rating: 0 out of 5 stars0 ratingsLearn LLVM 12: A beginner's guide to learning LLVM compiler tools and core libraries with C++ Rating: 0 out of 5 stars0 ratingsPython Architecture Patterns: Master API design, event-driven structures, and package management in Python Rating: 0 out of 5 stars0 ratingsThe Azure Cloud Native Architecture Mapbook: Explore Microsoft Cloud's infrastructure, application, data, and security architecture Rating: 0 out of 5 stars0 ratingsPractical Microservices Rating: 0 out of 5 stars0 ratingsRust for Network Programming and Automation, Second Edition Rating: 0 out of 5 stars0 ratingsSecuring Application Deployment with Obfuscation and Code Signing: How to Create 3 Layers of Protection for .NET Release Build Rating: 0 out of 5 stars0 ratingsSimplifying Service Management with Consul: Overcome connectivity and security challenges within dynamic service architectures Rating: 0 out of 5 stars0 ratingsModern CMake for C++: Discover a better approach to building, testing, and packaging your software Rating: 5 out of 5 stars5/5Robot Operating System (ROS): The Complete Reference (Volume 6) Rating: 0 out of 5 stars0 ratingsSoftware Architecture with Python Rating: 0 out of 5 stars0 ratingsMastering Embedded Linux Programming: Create fast and reliable embedded solutions with Linux 5.4 and the Yocto Project 3.1 (Dunfell) Rating: 0 out of 5 stars0 ratingsMastering Spring Boot 3.0: A comprehensive guide to building scalable and efficient backend systems with Java and Spring Rating: 0 out of 5 stars0 ratingsProfessional Heroku Programming Rating: 4 out of 5 stars4/5
Programming For You
Coding All-in-One For Dummies Rating: 4 out of 5 stars4/5SQL QuickStart Guide: The Simplified Beginner's Guide to Managing, Analyzing, and Manipulating Data With SQL Rating: 4 out of 5 stars4/5Linux: Learn in 24 Hours Rating: 5 out of 5 stars5/5Excel : The Ultimate Comprehensive Step-By-Step Guide to the Basics of Excel Programming: 1 Rating: 5 out of 5 stars5/5Python Programming : How to Code Python Fast In Just 24 Hours With 7 Simple Steps Rating: 4 out of 5 stars4/5JavaScript All-in-One For Dummies Rating: 5 out of 5 stars5/5Learn to Code. Get a Job. The Ultimate Guide to Learning and Getting Hired as a Developer. Rating: 5 out of 5 stars5/5SQL All-in-One For Dummies Rating: 3 out of 5 stars3/5Grokking Algorithms: An illustrated guide for programmers and other curious people Rating: 4 out of 5 stars4/5PYTHON: Practical Python Programming For Beginners & Experts With Hands-on Project Rating: 5 out of 5 stars5/5Python: Learn Python in 24 Hours Rating: 4 out of 5 stars4/5Learn PowerShell in a Month of Lunches, Fourth Edition: Covers Windows, Linux, and macOS Rating: 5 out of 5 stars5/5Excel 101: A Beginner's & Intermediate's Guide for Mastering the Quintessence of Microsoft Excel (2010-2019 & 365) in no time! Rating: 0 out of 5 stars0 ratingsHTML & CSS: Learn the Fundaments in 7 Days Rating: 4 out of 5 stars4/5Beginning Programming with C++ For Dummies Rating: 4 out of 5 stars4/5C Programming For Beginners: The Simple Guide to Learning C Programming Language Fast! Rating: 5 out of 5 stars5/5Python QuickStart Guide: The Simplified Beginner's Guide to Python Programming Using Hands-On Projects and Real-World Applications Rating: 0 out of 5 stars0 ratingsCoding with JavaScript For Dummies Rating: 0 out of 5 stars0 ratingsHTML in 30 Pages Rating: 5 out of 5 stars5/5The Most Concise Step-By-Step Guide To ChatGPT Ever Rating: 3 out of 5 stars3/5Spies, Lies, and Algorithms: The History and Future of American Intelligence Rating: 4 out of 5 stars4/5Mastering Excel: Starter Set Rating: 2 out of 5 stars2/5
Reviews for Embedded Systems Architecture
0 ratings0 reviews
Book preview
Embedded Systems Architecture - Daniele Lacamera
BIRMINGHAM—MUMBAI
Embedded Systems Architecture
Copyright © 2023 Packt Publishing
All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.
Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.
Group Product Manager: Gebin George
Publishing Product Manager: Kunal Sawant
Senior Editor: Rohit Singh
Technical Editor: Pradeep Sahu
Copy Editor: Safis Editing
Project Coordinator: Deeksha Thakkar
Proofreader: Safis Editing
Indexer: Sejal Dsilva
Production Designer: Joshua Misquitta
Developer Relations Marketing Executive: Sonakshi Bubbar
First published: May 2018
Second edition: January 2023
Production reference: 1281222
Published by Packt Publishing Ltd.
Livery Place
35 Livery Street
Birmingham
B3 2PB, UK.
ISBN 978-1-80323-954-5
www.packt.com
Contributors
About the author
Daniele Lacamera is a software technologist and researcher. He is an expert in operating systems and TCP/IP, with more than 20 academic publications on transport protocol optimization. He began his career as a Linux kernel developer and had his first contribution accepted in Linux 2.6.
Since 2012, he has been working on microcontroller-based architectures, focusing on the design, development, and integration of software for embedded systems. He is an active contributor to many free software projects, and co-author of a TCP/IP stack and a POSIX operating system (OS) for Cortex-M devices, both distributed under the GPL. Nowadays, his activities are focused on IoT security, cryptography, secure boot, and custom transport protocols.
About the reviewer
Marco Oliverio obtained his Ph.D. degree at the University of Calabria, with a dissertation on OS defenses against side-channel and hardware attacks. Some of his academic publications have been presented at vitally important conferences regarding OSes. After finishing his Ph.D., he started working as a firmware and embedded developer, contributing to several open source projects.
Table of Contents
Preface
Part 1 – Introduction to Embedded Systems Development
1
Embedded Systems – A Pragmatic Approach
Domain definition
Embedded Linux systems
Low-end 8-bit microcontrollers
Hardware architecture
Understanding the challenges
Multithreading
RAM
Flash memory
General-purpose input/output (GPIO)
ADC and DAC
Timers and PWM
Interfaces and peripherals
Asynchronous UART-based serial communication
SPI
I²C
USB
Connected systems
Challenges of distributed systems
Introduction to isolation mechanisms
The reference platform
ARM reference design
The Cortex-M microprocessor
Summary
2
Work Environment and Workflow Optimization
Workflow overview
The C compiler
Linker
Make: a build automation tool
Debugger
Embedded workflow
Text editors versus integrated environments
The GCC toolchain
The cross compiler
Compiling the compiler
Linking the executable
Binary format conversion
Interacting with the target
The GDB session
Validation
Functional tests
Hardware tools
Testing off-target
Emulators
Summary
Part 2 – Core System Architecture
3
Architectural Patterns
Configuration management
Revision control
Tracking activities
Code reviews
Continuous integration
Source code organization
Hardware abstraction
Middleware
Application code
Security considerations
Vulnerability management
Software cryptography
Hardware cryptography
Running untrusted code
The life cycle of an embedded project
Defining project steps
Prototyping
Refactoring
API and documentation
Summary
4
The Boot-Up Procedure
Technical requirements
The interrupt vector table
Startup code
Reset handler
Allocating the stack
Fault handlers
Memory layout
Building and running the boot code
The makefile
Running the application
Multiple boot stages
Bootloader
Building the image
Debugging a multi-stage system
Shared libraries
Remote firmware updates
Secure boot
Summary
5
Memory Management
Technical requirements
Memory mapping
Memory model and address space
The code region
The RAM regions
Peripheral-access regions
The system region
Order of memory transactions
The execution stack
Stack placement
Stack overflows
Stack painting
Heap management
Custom implementation
Using newlib
Limiting the heap
Multiple memory pools
Common heap usage errors
The memory protection unit
MPU configuration registers
Programming the MPU
Summary
Part 3 – Device Drivers and Communication Interfaces
6
General-Purpose Peripherals
Technical requirements
Bitwise operations
The interrupt controller
Peripherals’ interrupt configuration
System time
Adjusting the flash wait states
Clock configuration
Clock distribution
Enabling the SysTick
Generic timers
GPIO
Pin configuration
Digital output
PWM
Digital input
Interrupt-based input
Analog input
The watchdog
Summary
7
Local Bus Interfaces
Technical requirements
Introducing serial communication
Clock and symbol synchronization
Bus wiring
Programming the peripherals
UART-based asynchronous serial bus
Protocol description
Programming the controller
Hello world!
newlib printf
Receiving data
Interrupt-based input/output
SPI bus
Protocol description
Programming the transceiver
SPI transactions
Interrupt-based SPI transfers
I²C bus
Protocol description
Clock stretching
Multiple masters
Programming the controller
Interrupt handling
Summary
8
Power Management and Energy Saving
Technical requirements
System configuration
Hardware design
Clock management
Voltage control
Low-power operating modes
Deep-sleep configuration
Stop mode
Standby mode
Wake-up intervals
Measuring power
Development boards
Designing low-power embedded applications
Replacing busy loops with sleep mode
Deep sleep during longer inactivity periods
Choosing the clock speed
Power state transitions
Summary
9
Distributed Systems and IoT Architecture
Technical requirements
Network interfaces
MAC
Selecting the appropriate network interfaces
The Internet protocols
Standard protocols, custom implementations
The TCP/IP stack
Network device drivers
Running the TCP/IP stack
Socket communication
Connectionless protocols
Mesh networks and dynamic routing
TLS
Securing socket communication
Application protocols
Message protocols
The REST architectural pattern
Distributed systems – single points of failure
Summary
Part 4 – Multithreading
10
Parallel Tasks and Scheduling
Technical requirements
Task management
The task block
Context switch
Creating tasks
Scheduler implementation
Supervisor calls
Cooperative scheduler
Concurrency and timeslices
Blocking tasks
Waiting for resources
Real-time scheduling
Synchronization
Semaphores
Mutexes
Priority inversion
System resource separation
Privilege levels
Memory segmentation
System calls
Embedded operating systems
OS selection
FreeRTOS
RIOT OS
Summary
11
Trusted Execution Environment
Technical requirements
Sandboxing
TrustZone-M
Reference platform
Secure and non-secure execution domains
System resources separation
Security attributes and memory regions
Flash memory and secure watermarks
GTZC configuration and block-based SRAM protection
Configuring secure access to peripherals
Building and running the example
Enabling TrustZone-M
Secure application entry point
Compiling and linking secure-world applications
Compiling and linking non-secure applications
Inter-domain transitions
Summary
Index
Other Books You May Enjoy
Preface
Embedded systems have become increasingly popular in the last two decades thanks to the technological progress made by microelectronics manufacturers and designers, which has aimed to increase computing power and decrease the size of the logic of microprocessors and peripherals.
Designing, implementing, and integrating the software components for these systems requires a direct approach to the hardware functionalities in most cases, where tasks are implemented in a single thread and there is no operating system to provide abstractions to access CPU features and external peripherals. For this reason, embedded development is considered a domain on its own in the universe of software development, in which the developer’s approach and workflow need to be adapted accordingly.
This book briefly explains the hardware architecture of a typical embedded system, introduces the tools and methodologies needed to get started with the development of a target architecture, and then guides the readers through interaction with the system features and peripheral interaction. Some areas, such as energy efficiency and connectivity, are addressed in more detail to give a closer view of the techniques used to design low-power and connected systems. Later in the book, a more complex design, incorporating a (simplified) real-time operating system, is built from the bottom up, starting from the implementation of single system components. Finally, in this second edition, we have added a detailed analysis of the implementation of TrustZone-M, the TEE technology introduced by ARM as part of its latest family of embedded microcontrollers.
The discussion often focuses on specific security and safety mechanisms by suggesting specific technologies aimed at improving the robustness of the system against programming errors in the application code, or even malicious attempts to compromise its integrity.
Who this book is for
If you’re a software developer or designer that wants to learn about embedded programming, this is the book for you. You’ll also find this book useful if you’re a less experienced or a beginner embedded programmer willing to expand your knowledge of embedded systems. More experience embedded software engineers may find this book useful for refreshing their knowledge of the internals of device drivers, memory safety, secure data transfers, privilege separation, and secure execution domains.
What this book covers
Chapter 1, Embedded Systems – A Pragmatic Approach, is an introduction to microcontroller-based embedded systems. The scope of the book is identified, from a broader definition of embedded systems
to the actual domain that will be analyzed – 32-bit microcontrollers with physical memory mapping.
Chapter 2, Work Environment and Workflow Optimization, outlines the tools used and the development workflow. This is an introduction to the toolchain, debuggers, and emulators that can be used to produce code in a binary format that can be uploaded and run on the target platform.
Chapter 3, Architectural Patterns, is all about the strategies and development methodologies for collaborative development and testing. This chapter proposes a description of the processes that are typically used while developing and testing software for embedded systems.
Chapter 4, The Boot-Up Procedure, analyzes the boot phase of an embedded system, boot stages, and bootloaders. It contains a detailed description of the bring-up code and the mechanisms used to separate the software into several boot stages.
Chapter 5, Memory Management, suggests some optimal strategies for memory management by pointing out common pitfalls and explaining how to avoid memory errors that can result in unpredictable or bad behavior in the application code.
Chapter 6, General-Purpose Peripherals, walks through accessing GPIO pins and other generic integrated peripherals. This is the first interaction of the target platform with the outside world, using electric signals to perform simple input/output operations.
Chapter 7, Local Bus Interfaces, guides you through the integration of serial bus controllers (UART, SPI, and I2C). A code-oriented, detailed analysis of the most common bus communication protocols is introduced by explaining the code required to interact with the transceivers commonly available in embedded systems.
Chapter 8, Power Management and Energy Saving, explores the techniques available to reduce power consumption in energy-efficient systems. Designing low-power and ultra-low-power embedded systems requires specific steps to be performed for reducing energy consumption while running the required tasks.
Chapter 9, Distributed Systems and IoT Architecture, introduces the available protocols and interfaces required to build distributed and connected systems. IoT systems need to communicate with remote endpoints using standard network protocols that are implemented using third-party libraries. Particular attention is dedicated to securing communication between endpoints using secure sockets.
Chapter 10, Parallel Tasks and Scheduling, explains the infrastructure of a multitasking operating system through the implementation of a real-time task scheduler. This chapter proposes three approaches for implementing operating systems for microcontrollers from scratch, using different schedulers (cooperative, pre-emptive, and safe).
Chapter 11, Trusted Execution Environment, describes the TEE mechanisms typically available on embedded systems and provides an example of running secure and non-secure domains using ARM TrustZone-M. On modern microcontrollers, TEE provides the opportunity to secure specific areas of memory or peripherals by limiting their access from the non-secure execution domain.
To get the most out of this book
It is expected that you are proficient in the C language and understand how computer systems work. A GNU or Linux development machine is required to apply the concepts explained. Going through the example code provided is sometimes necessary to fully understand the mechanisms implemented. You are encouraged to modify, improve, and reuse the examples provided, applying the suggested methodologies.
Additional usage instructions for the requested tools are available in Chapter 2, Work Environment and Workflow Optimization.
If you are using the digital version of this book, we advise you to type the code yourself or access the code from the book’s GitHub repository (a link is available in the next section). Doing so will help you avoid any potential errors related to the copying and pasting of code.
Download the example code files
You can download the example code files for this book from GitHub at https://github.com/PacktPublishing/Embedded-Systems-Architecture-Second-Edition. If there’s an update to the code, it will be updated in the GitHub repository.
We also have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out!
Download the color images
We also provide a PDF file that has color images of the screenshots and diagrams used in this book. You can download it here: https://packt.link/kVMr1.
Conventions used
There are a number of text conventions used throughout this book.
Code in text: Indicates code words in text, database table names, folder names, filenames, file extensions, pathnames, dummy URLs, user input, and Twitter handles. Here is an example: A single configuration file must be provided from the command-line invocation, with several platforms and development board configurations provided under the /scripts directory.
A block of code is set as follows:
/* Jump to non secure app_entry */ asm volatile(mov r12, %0
::r
((uint32_t)app_entry - 1)); asm volatile(blxns r12
);
When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold:
Secure Area 1:
SECWM1_PSTRT : 0x0 (0x8000000) SECWM1_PEND : 0x39 (0x8039000)
Any command-line input or output is written as follows:
$ renode /opt/renode/scripts/single-node/stm32f4_discovery.resc
Commands for the debugger console are written as follows:
> add-symbol-file app.elf 0x1000 > bt full
Tips or important notes
Appear like this.
Get in touch
Feedback from our readers is always welcome.
General feedback: If you have questions about any aspect of this book, email us at customercare@packtpub.com and mention the book title in the subject of your message.
Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visit www.packtpub.com/support/errata and fill in the form.
Piracy: If you come across any illegal copies of our works in any form on the internet, we would be grateful if you would provide us with the location address or website name. Please contact us at copyright@packt.com with a link to the material.
If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visit authors.packtpub.com.
Share your thoughts
Once you’ve read Embedded Systems Architecture, Second Edition, we’d love to hear your thoughts! Please click here to go straight to the Amazon review page for this book and share your feedback.
Your review is important to us and the tech community and will help us make sure we’re delivering excellent quality content.
Download a free PDF copy of this book
Thanks for purchasing this book!
Do you like to read on the go but are unable to carry your print books everywhere?
Is your eBook purchase not compatible with the device of your choice?
Don’t worry, now with every Packt book you get a DRM-free PDF version of that book at no cost.
Read anywhere, any place, on any device. Search, copy, and paste code from your favorite technical books directly into your application.
The perks don’t stop there, you can get exclusive access to discounts, newsletters, and great free content in your inbox daily!
Follow these simple steps to get the benefits:
Scan the QR code or visit the link below:
https://packt.link/free-ebook/9781803239545
Submit your proof of purchase
That’s it! We’ll send your free PDF and other benefits to your email directly
Part 1 – Introduction to Embedded Systems Development
This part gives a bird’s eye view of embedded development, explaining how it differs from other technical fields that developers may be familiar with. The second chapter helps transform a developer’s workstation into an actual hardware/software development lab and optimizes the steps needed to develop, test, debug, and deploy embedded software.
This part has the following chapters:
Chapter 1, Embedded Systems – A Pragmatic Approach
Chapter 2, Work Environment and Workflow Optimization
1
Embedded Systems – A Pragmatic Approach
Designing and writing software for embedded systems poses a different set of challenges than traditional high-level software development.
This chapter provides an overview of these challenges and introduces the basic components and the platform that will be used as a reference in this book.
In this chapter, we will discuss the following topics:
Domain definition
General-purpose input/output (GPIO)
Interfaces and peripherals
Connected systems
Introduction to isolation mechanisms
The reference platform
Domain definition
Embedded systems are computing devices that perform specific, dedicated tasks with no direct or continued user interaction. Due to the variety of markets and technologies, these objects have different shapes and sizes, but often, all have a small size and a limited amount of resources.
In this book, the concepts and the building blocks of embedded systems will be analyzed through the development of the software components that interact with their resources and peripherals. The first step is to define the scope for the validity of the techniques and the architectural patterns explained in this book, within the broader definition of embedded systems.
Embedded Linux systems
One part of the embedded market relies on devices with enough power and resources to run a variant of the GNU/Linux OS. These systems, often referred to as embedded Linux, are outside the scope of this book, as their development includes different strategies of design and integration of the components. A typical hardware platform that is capable of running a system based on the Linux kernel is equipped with a reasonably large amount of RAM, up to a few gigabytes, and sufficient storage space on board to store all the software components provided in the GNU/Linux distribution.
Additionally, for the Linux memory management to provide separate virtual address spaces to each process on the system, the hardware must be equipped with a memory management unit (MMU), a hardware component that assists the OS in translating physical addresses into virtual addresses, and vice versa, at runtime.
This class of devices presents different characteristics that are often overkill for building tailored solutions, which can use a much simpler design and reduce the production costs of single units.
Hardware manufacturers and chip designers have researched new techniques to improve the performance of microcontroller-based systems. In the past few decades, they have introduced new generations of platforms that would cut hardware costs, firmware complexity, size, and power consumption to provide a set of features that are most interesting for the embedded market.
Due to their specifications, in some real-life scenarios, embedded systems must be able to execute a series of tasks within a short, measurable, and predictable amount of time. These kinds of systems are called real-time systems and differ from the approach of multi-task computing, which is used in desktops, servers, and mobile phones.
Real-time processing is a goal that is extremely hard, if not impossible, to reach on embedded Linux platforms. The Linux kernel is not designed for hard real-time processing, and even if patches are available to modify the kernel scheduler to help meet these requirements, the results are not comparable to bare-metal, constrained systems that are designed with this purpose in mind.
Some other application domains, such as battery-powered and energy-harvesting devices, can benefit from the low power consumption capabilities of smaller embedded devices and the energy efficiency of the wireless communication technologies often integrated into embedded connected devices. The higher amount of resources and the increased hardware complexity of Linux-based systems often do not scale down enough on energy levels or require effort to meet similar figures in power consumption.
The type of microcontroller-based systems that we will analyze in this book is 32-bit systems, which are capable of running software in a single-threaded, bare-metal application, as well as integrating minimalist real-time OSs, which are very popular in the industrial manufacturing of embedded systems, which we use daily to accomplish specific tasks. They are becoming more and more adopted to help define more generic, multiple-purpose development platforms.
Low-end 8-bit microcontrollers
In the past, 8-bit microcontrollers dominated the embedded market. The simplicity of their design allows us to write small applications that can accomplish a set of predefined tasks but are too simple and usually equipped with too few resources to implement an embedded system, especially since 32-bit microcontrollers have evolved to cover all the use cases for these devices within the same range of price, size, and power consumption.
Nowadays, 8-bit microcontrollers are mostly relegated to the market of educational platform kits, aimed at introducing hobbyists and newcomers to the basics of software development on electronic devices. 8-bit platforms are not covered in this book because they lack the characteristics that allow advanced system programming, multithreading, and advanced features to be developed to build professional embedded systems.
In the context of this book, the term embedded systems is used to indicate a class of systems running on microcontroller-based hardware architecture, offering constrained resources but allowing real-time systems to be built through features provided by the hardware architecture to implement system programming.
Hardware architecture
The architecture of an embedded system is centered around its microcontroller, also sometimes referred to as the microcontroller unit (MCU). This is typically a single integrated circuit containing the processor, RAM, flash memory, serial receivers and transmitters, and other core components. The market offers many different choices among architectures, vendors, price ranges, features, and integrated resources. These are typically designed to be inexpensive, low-resource, low-energy consuming, self-contained systems on a single integrated circuit, which is the reason why they are often referred to as System-on-Chip (SoC).
Due to the variety of processors, memories, and interfaces that can be integrated, there is no actual reference architecture for microcontrollers. Nevertheless, some architectural elements are common across a wide range of models and brands, and even across different processor architectures.
Some microcontrollers are dedicated to specific applications and expose a particular set of interfaces to communicate to peripherals and the outside world. Others are focused on providing solutions with reduced hardware costs, or with very limited energy consumption.
Nevertheless, the following set of components is hardcoded into almost every microcontroller:
Microprocessor
RAM
Flash memory
Serial transceivers
Additionally, more and more devices are capable of accessing a network, to communicate with other devices and gateways. Some microcontrollers may provide either well-established standards, such as Ethernet or Wi-Fi interfaces, or specific protocols specifically designed to meet the constraints of embedded systems, such as sub-GHz radio interfaces or a Controller Area Network (CAN) bus, being partially or fully implemented within the IC.
All the components must share a bus line with the processor, which is responsible for coordinating the logic. The RAM, flash memory, and control registers of the transceivers are all mapped in the same physical address space:
Figure 1.1 – A simplified block diagram of the components inside a generic microcontrollerFigure 1.1 – A simplified block diagram of the components inside a generic microcontroller
The addresses where RAM and Flash Memory are mapped depend on the specific model and are usually provided in the datasheet. A microcontroller can run code in its native machine language; that is, a sequence of instructions conveyed into a binary file that is specific to the architecture it is running on. By default, compilers provide a generic executable file as the output of the compilation and assembly operations, which needs to be converted into a format that can be executed by the target.
The Processor part is designed to execute the instructions that have been stored in its own specific binary format directly from RAM as well as from its internal flash memory. This is usually mapped starting from position zero in memory or another well-known address specified in the microcontroller manual. The CPU can fetch and execute code from RAM faster, but the final firmware is stored in the flash memory, which is usually bigger than the RAM on almost all microcontrollers and permits it to retain the data across power cycles and reboots.
Compiling a software operating environment for an embedded microcontroller and loading it onto the flash memory requires a host machine, which is a specific set of hardware and software tools. Some knowledge about the target device’s characteristics is also needed to instruct the compiler to organize the symbols within the executable image. For many valid reasons, C is the most popular language in embedded software, although not the only available option. Higher-level languages, such as Rust and C++, can produce embedded code when combined with a specific embedded runtime, or even in some cases by entirely removing the runtime support from the language.
Note
This book will focus entirely on C code because it abstracts less than any other high-level language, thus making it easier to describe the behavior of the underlying hardware while looking at the code.
All modern embedded systems platforms also have at least one mechanism (such as JTAG) for debugging purposes and uploading the software to the flash. When the debugging interface is accessed from the host machine, a debugger can interact with the breakpoint unit in the processor, interrupting and resuming the execution, and can also read and write from any address in memory.
A significant part of embedded programming is communicating the peripherals while using the interfaces that the MCU exposes. Embedded software development requires basic knowledge of electronics, the ability to understand schematics and datasheets, and confidence with the measurement tools, such as logic analyzers or oscilloscopes.
Understanding the challenges
Approaching embedded development means keeping the focus on the specifications as well as the hardware restrictions at all times. Embedded software development is a constant challenge that requires focusing on the most efficient way to perform a set of specific tasks but keeping the limited resources available in strong consideration. There are several compromises to deal with, which are uncommon in other environments. Here are some examples:
There might be not enough space in the flash to implement a new feature
There might not be enough RAM to store complex structures or make copies of large data buffers
The processor might be not fast enough to accomplish all the required calculations and data processing in due time
Battery-powered and resource-harvesting devices might require lower energy consumption to meet lifetime expectations
Moreover, PC and mobile OSs make large use of the MMU, a component of the processor that allows runtime translations between physical and virtual addresses.
The MMU is a necessary abstraction to implement address space separation among the tasks, as well as between the tasks and the kernel itself. Embedded microcontrollers do not have an MMU, and usually lack the amount of non-volatile memory required to store kernels, applications, and libraries. For this reason, embedded systems are often running in a single task, with the main loop performing all the data processing and communication in a specific order. Some devices can run embedded OSs, which are far less complex than their PC counterparts.
Application developers often see the underlying system as a commodity, while embedded development often means that the entire system has to be implemented from scratch, from the boot procedure up to the application logic. In an embedded environment, the various software components are more closely related to each other because of the lack of more complex abstractions, such as memory separations between the processes and the OS kernel.
A developer approaching embedded systems for the first time might find testing and debugging on some of the systems a bit more intricate than just running the software and reading out the results. This becomes especially true in those systems that have been designed with little or no human interaction interfaces.
A successful approach requires a healthy workflow, which includes well-defined test cases, a list of key performance indicators coming from the analysis of the specifications to identify possibilities of trade-offs, several tools and procedures at hand to perform all the needed measurements, and a well-established and efficient prototyping phase.
In this context, security deserves some special consideration. As usual, when writing code at the system level, it is wise to keep in mind the system-wide consequences of possible faults. Most embedded application code runs with extended privileges on the hardware, and a single task misbehaving can affect the stability and integrity of the entire firmware. As we will see, some platforms offer specific memory protection mechanisms and built-in privilege separation, which are useful for building fail-safe systems, even in the absence of a full OS based on separating process address spaces.
Multithreading
One of the advantages of using microcontrollers designed to build embedded systems is the possibility to run logically separated tasks within separate execution units by time-sharing the resources.
The most popular type of design for embedded software is based on a single loop-based sequential execution model, where modules and components are connected to expose callback interfaces. However, modern microcontrollers offer features and core logic characteristics that can be used by system developers to build a multitasking environment to run logically separated applications.
These features are particularly handy in the approach to more complex real-time systems, and they help us understand the possibilities of the implementation of safety models based on process isolation and memory segmentation.
RAM
640 KB of memory ought to be enough for everyone
– Bill Gates (founder and former director of Microsoft)
This famous statement has been cited many times in the past three decades to underline the progress in technology and the outstanding achievements of the PC industry. While it may sound like a joke for many software engineers out there, it is still in these figures that embedded programming has to be thought about, more than 30 years after MS-DOS was initially released.
Although most embedded systems are capable of breaking that limit today, especially due to the availability of external DRAM interfaces, the simplest devices that can be programmed in C may have as little as 4 KB of RAM available to implement the entire system logic. This has to be taken into account when designing an embedded system, by estimating the amount of memory potentially needed for all the operations that the system has to perform, and the buffers that may be used at any time to communicate with peripherals and nearby devices.
The memory model at the system level is simpler than that of PCs and mobile devices. Memory access is typically done at the physical level, so all the pointers in your code are telling you the physical location of the data they are pointing to. In modern