Actions

Difference between revisions of "Enhancing capabilites of atomthreads rtos with thread manipulation and inter-thread communication"

From EdWiki

Line 69: Line 69:
  
  
===THREADS RESUMPTION===
+
==Function Description of the Added Functionalities==
  
=== SoC Architecture ===
+
Function Name : EnableExceptions
 +
File  : ExceptionGeneration.c
 +
Include File : ExceptionGeneration.h
 +
Prototype : void EnableExceptions( uint8_t exceptionnumber, uint8_t control)
 +
Input Arguments exceptionnumber = Exception Number  (options available in ExceptionGeneration.h)
 +
control = Enable or disable the exception (options available in ExceptionGeneration.h)
 +
Output None
 +
Function Description Enables / Disables the exception based on the control and exception number provided in the arguments.
  
[[File:DT_soc_arch.png|750px|center ]]
 
  
=== Bootloader (U-Boot) ===
+
Function Name : atomThreadRestart
 +
Include File : atom.h
 +
Prototype : uint8_t atomThreadRestart (ATOM_TCB *tcb_ptr)
 +
Input Arguments tcb_ptr = Pointer to the Thread control block of the thread to be restarted
 +
 +
Output status of the operation(options available in atom.h)
 +
Description 1. Reinitialised the Thread control block to default states (same values after creation)
 +
2. Reinitialises the stack of the Thread to default values (same values after creation)
 +
3. Puts the Thread in the ready queue
  
A boot-loader is a small program which will load the kernel image into RAM and boots up the kernel image. This is also called bootstrap as it brings(pulls) up system by loading an operating system. Boot-loader starts before any other software starts and initializes the processor and makes CPU ready to execute a program like an operating system. Most processors have a default address from which the first bytes of code are fetched upon power is applied or board is reset. Hardware designers use this information to store the boot-loader code at that address in ROM or flash. Since it should initialize the cpu and should run a program which is located at architecture specific address boot-loaders are highly processor specific and board specific. Every embedded board comes with a bootstrap to download the kernel image or standalone application into the board and start executing the kernel image or application. Boot-loader will be executed when power is applied to a processor board. Basically it will have some minimal features to load the image and boot it up.
 
  
U-Boot boots an operating system by reading the kernel and any other required data (e.g. device tree or ramdisk image) into memory, and then executing the kernel with the appropriate arguments.
+
Function Name : atomThreadSuspend
 +
Include File : atom.h
 +
Prototype : uint8_t atomThreadSuspend (ATOM_TCB *tcb_ptr)
 +
Input Arguments tcb_ptr = Pointer to the Thread control block of the thread to be restarted
 +
 +
Output status of the operation(options available in atom.h)
 +
Description 1. Dequeues the entry of the Thread from the ready queue.
 +
2. Set the suspended status to TRUE
 +
3. Set the forced suspended status to TRUE. Its required for next forced resumption to be valid.
 +
4. Relinquishes all the RTOS objects
  
[[File:DT_bootflow.png|800px|center]]
+
This function is protected by critical section.
  
'''Correlation of x86 PC World & Embedded World'''
 
  
{|
+
Function Name : atomThreadResume
! Stage
+
Include File : atom.h
! x86 (PC)
+
Prototype : uint8_t atomThreadRestart (ATOM_TCB *tcb_ptr)
! Embedded (BeagleBone)
+
Input Arguments tcb_ptr = Pointer to the Thread control block of the thread to be restarted
|-
+
| ZSBL (Firmware)
+
Output status of the operation(options available in atom.h)
| BIOS/UEFI
+
Description 1. Enqueue  the Entry to the ready queue
| Firmware Present in On-Chip ROM
+
2. Set the suspended status to FALSE
|-
+
3. Set the forced suspended status to FALSE . Its required for next forced suspension to be valid.
| FSBL (SPL Loader)
+
4. Set the forced resume status to FALSE
| GRUB Stage 1&lt;br /&gt;<code>/boot/efi/EFI/ubuntu/shimx64.efi</code>
+
5. Takes back all the RTOS objects.
| <code>MLO</code> (Mmc LOader)
+
|-
+
| Bootloader
+
| GRUB Stage 2&lt;br /&gt;<code>/boot/grub</code>
+
| <code>u-boot.bin</code>
+
|}
+
  
==== Zeroth Stage BootLoader (Firmware) ====
+
This function is protected by critical section.
  
The AM335x contains a section of Read-Only Memory (ROM) that implements the first stage bootloader. The ROM Bootloader code is the first piece of code that is executed by the processor when it is released from reset. The ROM is a hard coded piece of software and cannot be changed for a given device but may change between revisions of the processor. The ROM bootloader has the following responsibilities:
 
  
* Initial configuration of the device and initialization of boot peripherals
+
Function Name : AddToExceptionThreadList
** Memory section setup (stack, heap, etc.)
+
Include File : atom.h
** Configuration of Watchdog Timer 1 (set to three minutes)
+
Prototype : void AddToExceptionThreadList (ATOM_TCB *tcb_ptr)
** Configuration of PLL and System Clocks
+
Input Arguments tcb_ptr = Pointer to the Thread control block of the thread which has caused exception
* Load and begin execution of the next stage bootloader
+
** Check boot peripherals for next stage bootloader (SPL/MLO)
+
Output none
** Load bootloader from peripheral into the internal RAM of the AM335x and begin execution of the code
+
Description 1. Makes an entry of the TCB pointer in the exception Thread List Table. An exception Thread List Table is a table containing all the Threads which has caused exception.
  
'''Memory Booting''': If the device is XIP (eXecute In Place), the directly jumps to target address, otherwise copies image into target RAM and jumps to target address. Example of XIP device is NOR Flash Memory which can be memory mapped (unlike eMMC).
+
This function is protected by critical section.
  
'''MMC/SD Card Booting''': Initialise MMC/SD driver. If card is present and readable, checks for card access modes like Raw Mode, FAT Mode, MBR, FAT16/32 Boot Sector.
 
  
In Raw Mode, the booting image can be located at one of the four consecutive locations in the main area:<br />
+
Function Name : GetExceptionThreadList
offset 0x0 / 0x20000 (128 KB) / 0x40000 (256 KB) / 0x60000 (384 KB). For this reason, a booting image<br />
+
Include File : atom.h
shall not exceed 128KB in size.
+
Prototype : uint8_t GetExceptionThreadList (ATOM_TCB *tcb_ptr)
 
+
Input Arguments tcb_ptr = Pointer to the Thread control block of the thread which has caused exception.
<blockquote>This is why we copy MLO to SD-Card at offset of 0x20000 using command <code>sudo dd if=./u-boot/MLO of=${DISK} count=1 seek=1 bs=128k</code>.
+
</blockquote>
+
In FAT mode, The image used by the booting procedure is taken from a specific booting file named “MLO”. This file has to be located in the root directory on an active primary partition of type FAT16 or FAT32. <ref>AM335x ARM ® CortexTM-A8 Microprocessors - Technical Reference Manual - SPRUH73H - April 2013
+
</ref>
+
 
+
<blockquote>Firmware loads MLO/SPL image to 0x402F0400 i.e. internal SRAM of AM335xx SoC
+
</blockquote>
+
==== First Stage BootLoader (SPL: Secondary Program Loader) ====
+
 
+
The SPL must operate entirely within the internal memory of the AM335x processor since only the boot peripherals have been initialized by the ROM bootloader.
+
 
+
To boot Linux, one of the most common methods is to use U-Boot (Universal Boot Loader) to perform all of the steps necessary to load and boot the Linux kernel. Feature rich U-Boot environment requires more memory than is available in the AM335x internal memory. Therefore, U-Boot is split into a first-stage and second-stage bootloader. The first stage of U-Boot is small and can be used as the SPL for the AM33xx Linux boot process. This split is done automatically during the build process for U-Boot, but the pieces are loaded into separate parts of the boot image.
+
 
+
The main function of the stripped-down SPL version of U-Boot is to perform hardware initialization of the DDR3 memory within the AM33xx, load the larger, fully featured version of U-Boot into DDR memory, and begin execution of that code.
+
 
+
e.g. MLO,
+
 
+
<blockquote>In U-Boot Config for BeagleBone Black Wireless the U-Boot address i.e. address of u-boot.bin in SD-Card is defined as sector 0x300 i.e. 0xC0000.<br />
+
This is why we copy u-boot.bin at offset of 0xC0000 (384k) using command <code>sudo dd if=./u-boot/u-boot.img of=${DISK} count=2 seek=1 bs=384k</code>
+
</blockquote>
+
=== Beaglebone Black Boot Flow with U-Boot ===
+
 
+
The main function of the U-Boot bootloader is to load and begin execution of the Linux kernel. To do this, it will typically look for a '''uImage''' or '''zImage''' file. The Kernel Image file can be found in non-volatile memory attached to the processor, such as an eMMC or a microSD card, or over a network interface via a protocol like TFTP.
+
 
+
The U-Boot environment can be configured by setting environment variables. These environment variables can be 1) configured during the build of U-Boot; 2) set and saved during an interactive U-Boot session; or 3) set or overridden from a file called '''uEnv.txt''' which is stored in the ''/boot'' directory of the filesystem.
+
 
+
The boot script checks for the existence of a file called uEnv.txt. If the file is found, the file is loaded into the memory. Then, it is imported to the environment ready to be read or executed. The uEnv.txt file is a method for users to insert scripts into the environment.
+
 
+
The complete Flattened boot scipt is available [[u-boot.txt|here]]. Upon boot environment variable <code>bootcmd</code> is executed.
+
 
+
 
+
-----
+
 
+
 
+
 
+
== Device Tree :: Concepts ==
+
 
+
Generally peripherals are connected to processor via a bus like USB, PCI, SATA, HDMI. All modern PC buses support enumeration. i.e. the main processor can ask “what devices are connected to this bus?” and the devices reply with some information about their type, manufacturer, model and configuration in a standardized format. With that information, the operating system can report the list of available devices and decide which device driver to use for each of them.
+
 
+
'''So Why do we need Device Tree?'''
+
 
+
Device Tree used to pass information about device which '''cannot be discovery''' so that OS can decide driver to use and configure it. E.g. UART/SPI/I2C, DMA Controller, No. of CPUs, Cache Organization, clock Frequency etc. Also many embedded system uses buses that doesn't support enumeration e.g. AXI, AHB. Without enumeration, the operating system has to be told what devices are present and how to access them. The 'Device Tree' is a standard format to represent this information.
+
 
+
=== History ===
+
 
+
Before the Device Tree, the Linux kernel contained the all of the knowledge regarding the hardware of each supported platform. This data, like memory locations, interrupts, on chip peripherals and lots of, many, alternative things was compiled into the kernel. This approach worked fairly well once there have been simply a number of platforms being supported.
+
 
+
Due to the fact that a description of every hardware platform was built into the kernel source, the boot loader could tell the kernel which platform it was running on by passing in a value (known as the machine type integer) at start up. The kernel would then internally look up the appropriate platform parameters and then use them to figure out how to utilize the hardware available to it.
+
 
+
There are problems with this hard coded approach. The first problem is that recent times have seen an ever proliferating number of small microcontroller boards each with their own set of hardware. Maintainers were having a hard time keeping up. Linus declared that henceforth no longer would each and every new device be supported in the mainline kernel and that a new solution must be found.
+
 
+
The choice of forking the Linux kernel code and implementing non-mainline configurations for each new micro-controller was really not a serious long term option and so the Device Tree concept was developed. The Device Tree enables micro-controllers to use the same mainline kernel code along with a separate, board specific, hardware configuration. Mainline Linux kernel version 3.7 and higher all support the Device Tree.
+
 
+
==== ePAPR (Power.org Standard for Embedded Power Architecture Platform Requirements) ====
+
 
+
The ePAPR specifies a concept called a device tree to describe system hardware. A boot program loads a device tree into a client program’s memory and passes a pointer to the device tree to the client.
+
 
+
=== Device Tree : Embedded vs PC (x86) ===
+
 
+
The main reason PC buses support discovery is that they're designed to allow a modular architecture where devices can be added and removed, e.g. adding an extension card into a PC or connecting a cable on an external port. Embedded systems typically have a fixed set of devices (excluding USB peripherals), and an operating system that's pre-loaded by the manufacturer and doesn't get replaced, so enumeration is not necessary.
+
 
+
'''So How Non-Enumerable Devices information is Passes to OS in PC?'''
+
 
+
ACPI (Advanced Configuration and Power Interface) started as an interface between firmware (formerly BIOS) and OS for things like power management, but also things like platform device probing. In ACPI there is a table called the ''Differentiated System Description Table'', augmented by a ''Secondary System Descriptor Table'', which (with information from a few other ACPI tables) provide much the same thing as that Device Tree do.
+
 
+
''ACPI is the unprofessional, hackish attempt of bios and board vendors to solve a small subset of the problems that DT already solved long ago''
+
 
+
=== Device Tree Design Principles ===
+
 
+
* It should describe the hardware layout, and how it works. But it should not describe which particular hardware configuration you’re interested in. The Device Tree is really a hardware description language.
+
* For a given piece of HW, Device Tree should be the same for U-Boot, or Linux
+
* There should be no need to change the Device Tree when updating the OS
+
* Describe integration of hardware components, not the internals of hardware components
+
* The details of how a specific device/IP block is working is handled by code in device drivers
+
* The Device Tree describes how the device/IP block is connected/integrated with the rest of the system: IRQ lines, DMA channels, clocks, reset lines, etc.
+
* The resulting .dtb accurately describes the hardware platform in an OS-agnostic<br />
+
way and
+
** Can be linked directly inside a bootloader binary (U-Boot, Barebox)
+
** Can be passed to the operating system by the bootloader (Linux)
+
* Like all beautiful design principles, these principles are not sometimes violated.
+
 
+
 
+
-----
+
 
+
 
+
 
+
== Device Tree :: Structure ==
+
 
+
=== Device Tree Hierarchy ===
+
 
+
Device Tree Files are not monolithic, they can bbe split into multiple files, and including each other in hierarchy
+
 
+
<code>.dtsi</code> are included files, generally are SoC/Peripheral Level.<br />
+
<code>.dts</code> are final Device Tress, generally Board Level.
+
 
+
 
+
 
+
[[File:DT_dts_hierarchy.png|800px|none ]]
+
 
+
The inclusion works by '''overlaying''' the tree of the including file over the tree of the included file.<br />
+
Simply, Trees defined later in order overlays earlier trees.
+
 
+
=== Device Tree Source Format ===
+
 
+
Device Tree Source has JSON like syntax. Device Tree is Tree of Nodes with Node Properties. A node is a device or an IP block, and node properties are device charateristics/specifications. <ref>Devicetree Specification - Release v0.2 - 20 December 2017 - www.devicetree.org</ref>
+
 
+
[[File:DT_dt_generic_syntax.png|800px|none]]
+
 
+
 
+
 
+
* '''Node Name''' : Each node in the devicetree is named according to the following convention:<br />
+
<code>node-name@unit-address</code>
+
** The '''''node-name''''' component specifies the name of the node.
+
** The '''''unit-address''''' component of the name is specific to the bus type on which the node sits.
+
** The root node does not have a node-name or unit-address. It is identified by a forward slash ('''/''').
+
 
+
'''Q. How does one know how to write the correct nodes/properties to describe a given hardware platform ?'''<br />
+
A. The DeviceTree Specifications at https://www.devicetree.org/specifications/ gives the base Device Tree syntax and specifies a number of standard properties.
+
 
+
=== Device Tree Organization ===
+
 
+
Under the root of the Device Tree, one typically finds the following top-level nodes.
+
 
+
<ul>
+
<li><p>A '''cpus''' node, which sub-nodes describing each CPU in the system.</p></li>
+
<li><p>A '''memory''' node, which defines the location and size of the RAM.</p></li>
+
<li><p>A '''chosen''' node, which defines parameters chosen or defined by the system firmware at boot time. <br />
+
In practice, one of its usage is to pass the kernel command line.</p></li>
+
<li><p>A '''aliases''' node, to define shortcuts to certain nodes.</p></li>
+
<li><p>One or more nodes defining the buses in the SoC.</p></li>
+
<li><p>One or mode nodes defining on-board devices.</p>
+
<p>'''BeagleBone Black Wireless Root Level Device Tree (Truncated)'''</p>
+
<pre class="json">/ {
+
    model = &quot;TI AM335x BeagleBone Black Wireless&quot;;
+
    compatible = &quot;ti,am335x-bone-black-wireless&quot;, &quot;ti,am335x-bone-black&quot;, &quot;ti,am335x-bone&quot;, &quot;ti,am33xx&quot;;
+
    interrupt-parent = &lt;&amp;intc&gt;;
+
    #address-cells = &lt;1&gt;;
+
    #size-cells = &lt;1&gt;;
+
 
+
    chosen {
+
        base_dtb = &quot;am335x-boneblack-wireless.dts&quot;;
+
        base_dtb_timestamp = __TIMESTAMP__;
+
        stdout-path = &amp;uart0;
+
    };
+
    aliases {
+
        i2c0 = &amp;i2c0;
+
        serial1 = &amp;uart1;
+
        usb0 = &amp;usb0;
+
        ...
+
    };
+
    cpus {
+
        #address-cells = &lt;1&gt;;
+
        #size-cells = &lt;0&gt;;
+
        cpu@0 {
+
            compatible = &quot;arm,cortex-a8&quot;;
+
            device_type = &quot;cpu&quot;;
+
            reg = &lt;0&gt;;
+
            clocks = &lt;&amp;dpll_mpu_ck&gt;;
+
            clock-names = &quot;cpu&quot;;
+
            clock-latency = &lt;300000&gt;; /* From omap-cpufreq driver */
+
            cpu0-supply = &lt;&amp;dcdc2_reg&gt;;
+
    };
+
    };
+
    memory@80000000 {
+
        device_type = &quot;memory&quot;;
+
        reg = &lt;0x80000000 0x20000000&gt;; /* 512 MB */
+
    };
+
    leds {
+
        pinctrl-names = &quot;default&quot;;
+
        pinctrl-0 = &lt;&amp;user_leds_s0&gt;;
+
        compatible = &quot;gpio-leds&quot;;
+
 
+
        led2 {
+
            label = &quot;beaglebone:green:usr0&quot;;
+
            gpios = &lt;&amp;gpio1 21 GPIO_ACTIVE_HIGH&gt;;
+
            linux,default-trigger = &quot;heartbeat&quot;;
+
            default-state = &quot;off&quot;;
+
        };
+
    };
+
    ...
+
};</pre>
+
<p></p></li></ul>
+
 
+
All devicetrees shall have a root node, and One ''/cpus'' node and At least one ''/memory'' node shall be present at the root of all devicetrees.
+
 
+
=== Device Tree Common Properties ===
+
 
+
<pre class="json">soc {
+
    compatible = &quot;simple-bus&quot;;
+
    #address-cells = &lt;1&gt;;
+
    #size-cells = &lt;1&gt;;
+
    ranges = &lt;0x0 0xe0000000 0x00100000&gt;;
+
    serial {
+
        device_type = &quot;serial&quot;;
+
        compatible = &quot;ns16550&quot;;
+
        reg = &lt;0x4600 0x100&gt;;
+
        clock-frequency = &lt;0&gt;;
+
        interrupts = &lt;0xA 0x8&gt;;
+
        interrupt-parent = &lt;&amp;ipic&gt;;
+
    };
+
};</pre>
+
'''<code>compatible</code>'''
+
 
+
<ul>
+
<li><p>consists of one or more strings from the most specific to the less specific</p></li>
+
<li><p>Describes the specific binding to which the node complies.</p></li>
+
<li><p>It uniquely identifies the programming model of the device.</p></li>
+
<li><p>Practically speaking, it is used by the operating system to find the appropriate driver for this device.</p></li>
+
<li><p>Special value: '''''simple-bus''''' indicates a bus where all sub-nodes are memory-mapped devices. Generally used for devices inside the SoC.</p></li>
+
<li><p>When describing real hardware, typical form is '''''manufacturer,model'''''</p>
+
<blockquote><p>Examples:<br />
+
compatible = &quot;arm,armv8-timer&quot;;<br />
+
compatible = &quot;fsl,mpc8641&quot;, &quot;ns16550&quot;;</p></blockquote></li></ul>
+
 
+
'''<code>status</code>'''
+
 
+
* '''''okay''''' means the device is present and should be enabled.
+
* '''''disabled''''' Indicates that the device is not presently operational, but it might become operational in the future
+
 
+
'''<code>#address-cells and #size-cells</code>'''
+
 
+
* The ''#address-cells'' and ''#size-cells'' properties may be used in any device node that has children in the device-tree hierarchy, it describes how child device nodes should be addressed.
+
* The '''#address-cells''' property definesthe number of uint32 cells used to encode the address field in a child node’s reg property.
+
* The '''#size-cells''' property defines the number of uint32 cells used to encode the size field in a child node’s reg property.
+
* Simply, the ''#address-cells'' property indicate how many cells (i.e 32 bits values) are needed to form the base address part in the ''reg'' property. The ''#size-cells'' is the same, for the size part of the ''reg'' property.
+
 
+
'''<code>reg</code>'''
+
 
+
* The reg property describes the address of the device’s resources within the address space defined by its parent bus.
+
* Encoded as number of (''address, length'') pairs.
+
* Memory-mapped devices: base address and size of the registers.
+
 
+
'''<code>ranges</code>'''
+
 
+
* The ranges property provides a means of defining a mapping or translation between the address space of the bus (the child address space) and the address space of the bus node’s parent (the parent address space).
+
* The format of the value of the ranges property is an arbitrary number of triplets of (''child-bus-address, parent-bus-address, length'')
+
 
+
'''<code>interrupts, interrupt-parent, interrupt-controller</code>'''
+
 
+
* The ''interrupts'' property of a device node defines the interrupt or interrupts that are generated by the device.
+
* The ''interrupt-parent'' property is a phandle that points to the interrupt controller for the current node.
+
* The ''interrupt-controller'' property is a boolean property that indicates that the current node is an interrupt controller
+
 
+
'''<code>pinctrl-*</code>'''
+
 
+
* Indicates the pin-muxing configuration requested by the device
+
 
+
'''<code>clocks</code>'''
+
 
+
* Which clock(s) are used by the device, from which clock controller
+
 
+
'''<code>dmas</code>'''
+
 
+
* Which DMA controller and channels are used by the device
+
 
+
<ref>Power.org™ Standard for Embedded Power Architecture™ Platform Requirements (ePAPR) - Version 1.1 -  08 April 2011 - elinux.org/images/c/cf/Power_ePAPR_APPROVED_v1.1.pdf</ref>
+
 
+
=== Device Tree Bindings ===
+
 
+
Q. '''How specific types and classes of devices are represented in the device tree?'''<br />
+
A. Device Tree Bindings
+
 
+
When creating a new device tree representation for a device, a binding should be created that fully describes the required properties and value of the device. This set of properties shall be sufficiently descriptive to provide device drivers with needed attributes of the device.
+
 
+
All Device Tree bindings recognized by the kernel are documented in ''Documentation/devicetree/bindings''.
+
 
+
Each binding documentation described which properties are accepted, with which values, which properties are mandatory vs. optional, etc.
+
 
+
The ''compatible'' property of a device node describes the specific binding (or bindings) to which the node complies.
+
 
+
Legacy vs YAML DT Binding : Earlier DT Bindings were defined in Human Readable Text Format. But as device tree compiler only does syntaxic validation, YAML based bingings were introduced to do semantic validation
+
 
+
'''Legacy DT Binding Example''' (gpio-omap.txt)
+
 
+
<pre class="text">OMAP GPIO controller bindings
+
 
+
Required properties:
+
- compatible:
+
  - &quot;ti,omap2-gpio&quot; for OMAP2 controllers
+
  - &quot;ti,omap3-gpio&quot; for OMAP3 controllers
+
  - &quot;ti,omap4-gpio&quot; for OMAP4 controllers
+
- reg : Physical base address of the controller and length of memory mapped
+
  region.
+
- gpio-controller : Marks the device node as a GPIO controller.
+
- #gpio-cells : Should be two.
+
  - first cell is the pin number
+
  - second cell is used to specify optional parameters (unused)
+
- interrupt-controller: Mark the device node as an interrupt controller.
+
- #interrupt-cells : Should be 2.
+
  The first cell is the GPIO number.
+
  The second cell is used to specify flags:
+
    bits[3:0] trigger type and level flags:
+
      1 = low-to-high edge triggered.
+
      2 = high-to-low edge triggered.
+
      4 = active high level-sensitive.
+
      8 = active low level-sensitive.
+
- interrupts : The interrupt the controller is rising as output when an
+
  interrupt occures
+
 
+
OMAP specific properties:
+
- ti,hwmods: Name of the hwmod associated to the GPIO:
+
&quot;gpio&lt;X&gt;&quot;, &lt;X&gt; being the 1-based instance number
+
from the HW spec.
+
- ti,gpio-always-on: Indicates if a GPIO bank is always powered and
+
so will never lose its logic state.
+
Example:
+
gpio0: gpio@44e07000 {
+
    compatible = &quot;ti,omap4-gpio&quot;;
+
    reg = &lt;0x44e07000 0x1000&gt;;
+
    ti,hwmods = &quot;gpio1&quot;;
+
    gpio-controller;
+
    #gpio-cells = &lt;2&gt;;
+
    interrupt-controller;
+
    #interrupt-cells = &lt;2&gt;;
+
    interrupts = &lt;96&gt;;
+
};</pre>
+
'''YAML DT Binding Example''' (mediatek,mt7621-gpio.yaml)
+
 
+
<pre class="yaml"># SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+
%YAML 1.2
+
---
+
$id: http://devicetree.org/schemas/gpio/mediatek,mt7621-gpio.yaml#
+
$schema: http://devicetree.org/meta-schemas/core.yaml#
+
 
+
title: Mediatek MT7621 SoC GPIO controller
+
 
+
maintainers:
+
  - Sergio Paracuellos &lt;sergio.paracuellos@gmail.com&gt;
+
 
+
description: |
+
  The IP core used inside these SoCs has 3 banks of 32 GPIOs each.
+
  The registers of all the banks are interwoven inside one single IO range.
+
  We load one GPIO controller instance per bank. Also the GPIO controller can receive
+
  interrupts on any of the GPIOs, either edge or level. It then interrupts the CPU
+
  using GIC INT12.
+
 
+
properties:
+
  $nodename:
+
    pattern: &quot;^gpio@[0-9a-f]+$&quot;
+
 
+
  compatible:
+
    const: mediatek,mt7621-gpio
+
 
+
  reg:
+
    maxItems: 1
+
 
+
  &quot;#gpio-cells&quot;:
+
    const: 2
+
 
+
  gpio-controller: true
+
  gpio-ranges: true
+
 
+
  interrupt-controller: true
+
 
+
  &quot;#interrupt-cells&quot;:
+
    const: 2
+
 
+
  interrupts:
+
    maxItems: 1
+
 
+
required:
+
  - compatible
+
  - reg
+
  - &quot;#gpio-cells&quot;
+
  - gpio-controller
+
  - gpio-ranges
+
  - interrupt-controller
+
  - &quot;#interrupt-cells&quot;
+
  - interrupts
+
 
+
additionalProperties: false
+
 
+
examples:
+
  - |
+
    #include &lt;dt-bindings/gpio/gpio.h&gt;
+
    #include &lt;dt-bindings/interrupt-controller/mips-gic.h&gt;
+
 
+
    gpio@600 {
+
      compatible = &quot;mediatek,mt7621-gpio&quot;;
+
      reg = &lt;0x600 0x100&gt;;
+
      #gpio-cells = &lt;2&gt;;
+
      gpio-controller;
+
      gpio-ranges = &lt;&amp;pinctrl 0 0 95&gt;;
+
      interrupt-controller;
+
      #interrupt-cells = &lt;2&gt;;
+
      interrupt-parent = &lt;&amp;gic&gt;;
+
      interrupts = &lt;GIC_SHARED 12 IRQ_TYPE_LEVEL_HIGH&gt;;
+
    };</pre>
+
=== Device Tree and Datasheet Correlation Examples ===
+
 
+
==== 1. GPIO ====
+
 
+
[[File:DT_dts_gpio_1.png|1000px|none ]]
+
 
+
[[File:DT_dts_gpio_2.png|1000px|none ]]
+
 
+
[[File:DT_dts_gpio_3.png|1000px|none ]]
+
 
+
==== 2. LED ====
+
 
+
[[File:DT_dts_led_1.png|1000px|none ]]
+
 
+
[[File:DT_dts_led_2.png|1000px|none ]]
+
 
+
==== 3. UART ====
+
 
+
[[File:DT_dts_uart_1.png|1000px|none ]]
+
 
+
[[File:DT_dts_uart_2.png|1000px|none ]]
+
 
+
=== How to compile? ===
+
 
+
On ARM, all Device Tree Source files (DTS) are for now located in ''arch/arm/boot/dts''.
+
 
+
The Device Tree Compiler (DTC) is the tool that is used to compile the source into a binary form. Source code for the DTC is located in ''scripts/dtc''. The Device Tree Blob is produced by the compiler, and is the binary that gets loaded by the bootloader and parsed by the kernel at boot time.
+
 
+
'''Syntax''' : <code>dtc [-I &lt;input-format&gt;] [-O &lt;output-format&gt;] [-o output-filename] [-V output_version] input_filename</code>
+
 
+
'''To Compile DTS to DTB'''<br />
+
<code>dtc –I dts –O dtb am335x-boneblack-wireless.dts &gt; am335x-boneblack-wireless.dtb</code>
+
 
+
'''To reverse compile DTB to DTS'''<br />
+
The DTC can also be used to reverse compile DTBs and make them human-readable again. 😄<br />
+
<code>dtc –I dtb –O dts am335x-boneblack-wireless.dtb &gt; am335x-boneblack-wireless.dts</code>
+
 
+
Q. How to validate Device Tree?<br />
+
A. dtc only does syntaxic validation. YAML bindings allow to do semantic validation.
+
 
+
<code>make dt_bindings_check</code> : verify that YAML bindings are valid<br />
+
<code>make dtbs_check</code> : validate DTs currently enabled against YAML bindings
+
 
+
 
+
-----
+
 
+
 
+
 
+
== Device Tree &amp; Linux Kernel ==
+
 
+
=== Device Tree and U-Boot ===
+
 
+
U-Boot typically configures using CONFIG options in board config file. But U-Boot can do run-time configuration via flattened device tree (i.e. device tree blob). This feature aims to make it possible for a single U-Boot binary to support multiple boards, with the exact configuration of each board controlled by a flat device tree (fdt).
+
 
+
U-Boot automatically patches the Device Tree Blob passed to Linux
+
 
+
* Sets the RAM base address and size
+
* Sets the kernel command line arguments
+
* Sets MAC address for network interfaces
+
 
+
U-Boot itself does not use the device tree normally, although it has several commands that allow you to view and manipulate the FDT itself.
+
 
+
* Using fdt commands: fdt set, fdt mknode, fdt rm
+
* Using Device Tree Overlays
+
 
+
==== When U-Boot adds cmdline_args i.e. bootargs to device tree? ====
+
 
+
[[File:DT_uboot_bootarg_trace.png|1000px|none ]]
+
 
+
=== How and When Linux loads DTB? ===
+
 
+
There is one single entry point to the kernel, at the start of the kernel image. That entry point supports two calling conventions.
+
 
+
'''ATAGS interface''' : Minimal information is passed from firmware to the kernel with a tagged list of predefined parameters.<br />
+
r0 : 0<br />
+
r1 : Machine type number<br />
+
r2 : Physical address of tagged list in system RAM
+
 
+
'''Entry with a flattened device-tree block''' : Firmware loads the physical address of the flattened device tree block (dtb) into r2, r1 is not used.<br />
+
r0 : 0<br />
+
r1 : Valid machine type number. When using a device tree, a single machine type number will often be assigned to represent a class or family of SoCs.<br />
+
r2 : physical pointer to the device-tree block in RAM. Device tree can be located anywhere in system RAM, but it should be aligned on a 64 bit boundary.
+
 
+
The kernel will differentiate between ATAGS and device tree booting by reading the memory pointed to by r2 and looking for either the flattened device tree block magic value (0xd00dfeed) or the ATAG_CORE value at offset 0x4 from r2 (0x54410001).
+
 
+
Device Tree Blob header is defined in <code>include/linux/of_fdt.h</code>
+
 
+
[[File:DT_kernel_jump_dtb.png|300px ]]
+
 
+
 
+
 
+
==== How Kernel Unflattens DTB to Tree nodes? ====
+
 
+
[[File:DT_unflatten_dt_trace.png|450px|center ]]
+
 
+
<source lang="c">/* head_common.S
+
*  r0  = cp#15 control register (exc_ret for M-class)
+
*  r1  = machine ID
+
*  r2  = atags/dtb pointer
+
*  r9  = processor ID
+
*/
+
__INIT __mmap_switched:
+
/* << Many lines Skipped Here>> */
+
b start_kernel
+
 
+
//start kernel
+
asmlinkage __visible void __init start_kernel(void) {
+
char *command_line;
+
...
+
setup_arch(&command_line);
+
...
+
}
+
 
+
//setup arch
+
void __init setup_arch(char **cmdline_p) {
+
...
+
unflatten_device_tree();
+
...
+
}
+
 
+
 
+
// unflatten_device_tree - create tree of device_nodes from flat blob
+
void __init unflatten_device_tree(void) {
+
__unflatten_device_tree(initial_boot_params, NULL, &of_root,
+
early_init_dt_alloc_memory_arch, false);
+
 
+
/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
+
of_alias_scan(early_init_dt_alloc_memory_arch);
+
 
+
unittest_unflatten_overlay_base();
+
}
+
 
+
// __unflatten_device_tree - create tree of device_nodes from flat blob
+
void *__unflatten_device_tree(const void *blob,
+
      struct device_node *dad,
+
      struct device_node **mynodes,
+
      void *(*dt_alloc)(u64 size, u64 align),
+
      bool detached) {
+
 
 
if (fdt_check_header(blob)) { ... }
+
Output Gets the number of Thread which has caused exceptions
    ...
+
Description 1. Removes an entry of the TCB pointer in the exception Thread List Table.
/* First pass, scan for size */
+
size = unflatten_dt_nodes(blob, NULL, dad, NULL);
+
    ...
+
/* Allocate memory for the expanded device tree */
+
mem = dt_alloc(size + 4, __alignof__(struct device_node));
+
    ...
+
/* Second pass, do actual unflattening */
+
unflatten_dt_nodes(blob, mem, dad, mynodes);
+
... }
+
 
+
// unflatten_dt_nodes - Alloc and populate a device_node from the flat tree
+
static int unflatten_dt_nodes(const void *blob,
+
      void *mem,
+
      struct device_node *dad,
+
      struct device_node **nodepp) {
+
...
+
for (offset = 0;
+
    offset >= 0 && depth >= initial_depth;
+
    offset = fdt_next_node(blob, offset, &depth)) {
+
if (!IS_ENABLED(CONFIG_OF_KOBJ) &&
+
    !of_fdt_device_is_available(blob, offset))
+
continue;
+
 
+
if (!populate_node(blob, offset, &mem, nps[depth],
+
  &nps[depth+1], dryrun))
+
return mem - base;
+
        ...
+
}
+
 
+
//populate_node
+
static bool populate_node(...) {
+
...
+
np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
+
__alignof__(struct device_node));
+
...
+
    if (dad != NULL) {
+
        np->parent = dad;
+
        np->sibling = dad->child;
+
        dad->child = np;
+
    } ...
+
populate_properties(blob, offset, mem, np, pathp, dryrun);
+
...
+
}
+
 
+
//populate_properties
+
static void populate_properties(...);
+
</source>
+
  
 +
This function is protected by critical section.
  
The defined global pointers in Kernel :
 
  
* <code>of_root</code> : Pointer to ''root'' node structure
+
Function Name : pipe_remove
* <code>of_chosen</code> : Pointer to ''chosen'' node structure
+
Include File : None. Its included in C file for internal use. Not to be used by application or kernel functions.
* <code>of_aliases</code> : Pointer to ''aliases'' node structure
+
Prototype : static uint8_t pipe_remove (ATOM_PIPE *pptr, uint8_t* readbuff,uint8_t size)
* <code>of_stdout</code> : Pointer to device node being used for stdout
+
Input Arguments pptr = Pointer to the pipe object.
 +
readbuff = Destination pointer for the message to be copied into.
  
The Structure of unflattened Device Tree Node in kernel is
+
size = Required number of bytes.
  
<source lang="c">struct device_node {
 
const char *name;
 
phandle phandle;
 
const char *full_name;
 
struct fwnode_handle fwnode;
 
  
struct property *properties;
+
Output Gives the status of the operation (list can be found in atompipe.h)
struct property *deadprops; /* removed properties */
+
Description Removes a message from a pipe. Assumes that there is a message present,
struct device_node *parent;
+
which is already checked by the calling functions with interrupts locked  out.
struct device_node *child;
+
struct device_node *sibling;
+
struct kobject kobj;
+
unsigned long _flags;
+
void *data;
+
};
+
  
struct property {
 
char *name;
 
int length;
 
void *value;
 
struct property *next;
 
unsigned long _flags;
 
struct bin_attribute attr;
 
};</source>
 
  
 +
Function Name : pipe_insert
 +
Include File : None. Its included in C file for internal use. Not to be used by application or kernel functions.
 +
Prototype : static uint8_t pipe_insert (ATOM_PIPE *pptr, uint8_t* writebuff , uint8_t size)
 +
Input Arguments pptr = Pointer to the pipe object.
 +
writebuff = Pointer to the write buffer from where pipe data will be copied.
  
==== How Kernel Load cmdline_args from dtb? ====
+
size = Required number of bytes.
  
  
 +
Output Gives the status of the operation (list can be found in atompipe.h)
 +
Description Inserts a message onto a pipe. Assumes that the pipe has space for one  message, which has already been checked by the calling function with interrupts locked out.
  
[[File:DT_cmdline_fdt_trace.png|450px|center ]]
 
  
 +
Function Name : atomPipeTimerCallback
 +
Include File : None. Its included in C file for internal use. Not to be used by application or kernel functions.
 +
Prototype : static void atomPipeTimerCallback (POINTER cb_data)
 +
Input Arguments cb_data = Call back structure of the Pipe Timer.
 +
Output None
 +
Description Timeouts on suspended threads are notified by the timer system through his generic callback. The timer system calls us back with a pointer to the  relevant pipe_TIMER object which is used to retrieve the pipe details.This function is protected by critical section.
  
  
<source lang="c">//setup arch
+
Function Name : atomPipeCreate
void __init setup_arch(char **cmdline_p) {
+
Include File : atompipe.h.
...
+
Prototype : uint8_t atomPipeCreate (ATOM_PIPE *pptr, uint8_t *buff_ptr, uint32_t max_num_bytes)
mdesc = setup_machine_fdt(__atags_pointer);
+
Input Arguments pptr : Pointer to pipe object
...
+
buff_ptr: Pointer to buffer storage area
/* populate cmd_line too for later use, preserving boot_command_line */
+
max_num_bytes: Maximum number of messages in the pipe
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
+
Output Gives the status of the operation (list can be found in atompipe.h)
*cmdline_p = cmd_line;
+
Description 1. Creates a Pipe Object for communication
...
+
}
+
  
//setup_machine_fdt - Machine setup when an dtb was passed to the kernel
 
const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) {
 
const struct machine_desc *mdesc, *mdesc_best = NULL;
 
...
 
early_init_dt_scan_nodes();
 
/* Change machine number to match the mdesc we're using */
 
__machine_arch_type = mdesc->nr;
 
...
 
}
 
  
//early_init_dt_scan_mode
+
Function Name : atomPipeDelete
void __init early_init_dt_scan_nodes(void) {
+
Include File : atompipe.h.
/* Retrieve various information from the /chosen node */
+
Prototype : uint8_t atomPipeDelete (ATOM_PIPE *pptr)
rc = of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
+
Input Arguments pptr : Pointer to pipe object
  
/* Initialize {size,address}-cells info */
+
Output Gives the status of the operation (list can be found in atompipe.h)
of_scan_flat_dt(early_init_dt_scan_root, NULL);
+
Description 1. Deletes the Pipe object
  
/* Setup memory, calling early_init_dt_add_memory_arch */
 
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
 
}
 
  
//of_scan_flat_dt - scan flattened tree blob and call callback on each.
 
  
//early_init_dt_scan_chosen
+
Function Name : atomPipeGet
int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
+
Include File : atompipe.h.
    int depth, void *data) {
+
Prototype : uint8_t atomPipeGet (ATOM_PIPE *pptr, uint8_t* readbuff , uint8_t size, int32_t timeout)
...
+
Input Arguments pptr Pointer to pipe object
if (depth != 1 || !data ||
+
timeout Max system ticks to block (0 = forever, -1 =  no block)
    (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
+
readbuff Pointer to which the received message will be copied
return 0;
+
Output Gives the status of the operation (list can be found in atompipe.h)
 +
Description 1. Attempt to retrieve a message from a pipe.
 +
2.   Retrieves one message at a time.
 +
3. Messages are copied into the passed readbuff storage area which should be large enough to contain one message of  unit_size bytes.
 +
4. Where multiple messages are in the pipe, messages are retrieved in FIFO order.
  
early_init_dt_check_for_initrd(node);
 
   
 
/* Retrieve command line */
 
p = of_get_flat_dt_prop(node, "bootargs", &l);
 
if (p != NULL && l > 0)
 
strlcpy(data, p, min(l, COMMAND_LINE_SIZE));
 
   
 
/* No arguments from boot loader, use kernel's  cmdl*/
 
if (!((char *)data)[0])
 
strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
 
...
 
}</source>
 
  
 +
Function Name : atompipePut
 +
Include File : atompipe.h.
 +
Prototype : uint8_t atomPipePut (ATOM_PIPE *pptr, uint8_t* writebuff , uint8_t size, int32_t timeout)
 +
Input Arguments pptr Pointer to pipe object
 +
timeout Max system ticks to block (0 = forever, -1 =  no block)
 +
readbuff Pointer to which the message should be copied out
 +
Output Gives the status of the operation (list can be found in atompipe.h)
 +
Description 1. Attempt to put a message onto a pipe.
 +
2. Sends one message at a time. Messages are copied from the passed
 +
msgptr storage area which should contain a message of  bytes
  
=== How Kernel use Device Trees? ===
 
  
Platform devices are devices that typically appear as autonomous entities in the system. This includes legacy port-based devices and host bridges to peripheral buses, and most controllers integrated into system-on-chip platforms. What they usually have in common is direct addressing from a CPU bus. Rarely, a platform_device will be connected through a segment of some other kind of bus; but its registers will still be directly addressable.
+
Function Name : SetExceptionFlag
 +
Include File : None
 +
Prototype : static void SetExceptionFlag()
 +
Input Arguments None
 +
Output None
 +
Description Sets the exception flag in the fault handler for use in the sys tick handler
  
==== Binding To Driver ====
 
  
[[File:DT_driver_bind_1.png|1000px|none ]]
+
Function Name : SetExceptionFlag
 +
Include File : None
 +
Prototype : static void SetExceptionFlag()
 +
Input Arguments None
 +
Output None
 +
Description Sets the exception flag in the fault handler for use in the sys tick handler
  
==== Device Probe ====
 
  
[[File:DT_driver_probe_1.png|1000px|none ]]
+
Function Name : GetExceptionFlag
 +
Include File : atomport.h
 +
Prototype : uint8_t GetExceptionFlag()
 +
Input Arguments None
 +
Output None
 +
Description Returns the status of Exception Flag
  
[[File:DT_driver_probe_2.png|1000px|none ]]
 
  
=== Device Tree at Runtime  ===
 
  
Unflattened Device Tree can be visualised in file like form at path <code>`/proc/device-tree</code>
+
Function Name : ResetExceptionFlag
 +
Include File : None
 +
Prototype : static void SetExceptionFlag()
 +
Input Arguments None
 +
Output None
 +
Description Resets the Exception Flag to 0
  
  
-----
 
  
 +
Function Name : usage_fault_handler
 +
Include File : None
 +
Prototype : void usage_fault_handler(void)
 +
Input Arguments None
 +
Output None
 +
Description 1. Pushes the LR register
 +
2. Sets the exception flag
 +
3. Gets the current Thread block
 +
4. Adds the current Thread block to the exception list
 +
5. POPs LR
 +
6. Returns via LR
  
  
== BeagleBone Device Tree Hacking ==
 
  
=== Device Tree Hacking Demos ===
+
Function Name : bus_fault_handler
 +
Include File : None
 +
Prototype : void bus_fault_handler (void)
 +
Input Arguments None
 +
Output None
 +
Description 1. Pushes the LR register
 +
2. Sets the exception flag
 +
3. Gets the current Thread block
 +
4. Adds the current Thread block to the exception list
 +
5. POPs LR
 +
6. Returns via LR
  
* LED Trigger Hack
+
==Test Cases for the added Functionalities==
** Changing Heartbeat from LED USR0 to LED USR3
+
To test the functionalities of the new functionalities  , an architecture is developed contatining Threads ,Exceptions and  ISR’s. The architecture is depicted below along the operations of each block
** Changing Trigger of USR0 LED to &quot;timer&quot; with Ton=100ms and Toff=100ms
+
* Adding I2C EEPROM (24c32) device to Device Tree &amp; BeagleBone Black
+
* Adding I2C RTC (ds3231) device to Device Tree &amp; BeagleBone Black
+
  
I2C EEPROM &amp; I2C RTC are connected to BeagleBone I2C2 (Header P9 19 &amp; 20)
 
  
=== Modified Device Tree ===
+
==Results==
  
<code>am335x-boneblack-wireless.dts</code> was modified for the hack as below.
 
  
[[File:DT_demo_dts_mod.png|1000px|none ]]
+
==Challenges , Learnings and Future Work==
 +
===Challenges===
 +
*There is not documentation and discussion forums on ATOMTHREADS , hence implementing functionalities conforming to AtomThreads framework required a detailed walk-through of the existing code which was time taking.
 +
*Since there were context switches , so breakpoints were not helpful in understanding the flow of execution , especially when pendSV is involved
 +
*Since mostly all the queue, tcbs are pointers (some were pointers to pointers) , so watching the values in the debug window was in vain. 
 +
*Tracking the stack pointers and the CPU registers during the execution of exception and then back to Thread was challenging.
 +
*Heavy use of assembly language in ThreadRestore and PendSV handlers required a study of the assembly language of the Cortex-M4 processor
  
== References ==
 
  
 +
===Learnings===
 +
*Understanding of Atom Threads RTOS along with the limitations
 +
*Understanding of different stack pointers and their role in context switching.
 +
*Approach for implementing RTOS objects or extend RTOS functionalities in a given RTOS framework
 +
*Understanding of different states and the responsibility of the objects in maintaining the suspended Threads rather than the kernel.
 +
*Understanding the two different contexts , Interrupt Context and Thread Context and the sections of CPU registers required to maintain the two contexts.
 +
*Understanding of Exceptions and how to go back to thread mode from Exception Context.
 +
*Understanding the Thread Control Block Structure and making the modifications as per the need to implement new functionality.
 +
*Understanding the Stack Region of the Threads and the different registers which are saved during context saving.
  
 +
===Future Work===
 +
The future work is written keeping in mind the existing framework of Atomthreads
 +
*Dynamic Creation of Threads rather than static creation
 +
*Implementation of Thread Deletion
 +
*Implementation of Signals to cause event(software events) on another Thread.
 +
*Implementation of Drivers through System Call Table and developing the systemcall interface between RTOS and Application through SVC calls
 +
*Implementation of different time slices for Threads of same priority depending on functionality.
  
<references />
+
==References==
 +
*Tiva™ TM4C123GH6PM Microcontroller Datasheet
 +
(https://www.ti.com/lit/ds/spms376e/spms376e.pdf?ts=1612427924240&ref_url=https%253A%252F%252Fwww.google.com%252F)
 +
*AtomThreads Documentation (http://atomthreads.com/)
 +
*http://shukra.dese.iisc.ac.in/edwiki/EmSys:Introduction_to_Atomthreads
 +
*http://shukra.dese.iisc.ac.in/edwiki/EmSys:Atomkernel.c_File_Reference
 +
*http://shukra.dese.iisc.ac.in/edwiki/EmSys:Help_Development_Boards#Atomthreads
 +
*Qing Li, Caroline Yao - Real-Time Concepts for Embedded Systems  -CMP Books (2003).pdf
 +
*REALTIME OPERATING SYSTEMS FOR ARM CORTEX-M MICROCONTROLLERS , Jonathan W. Valvano

Revision as of 00:30, 8 February 2021

Enhancing Capabilities of ATOMTHREADS with Thread Manipulation and Inter-Thread Communication

OBJECTIVE

The objective of the project is to enhance the functionalities of an existing Real-Time Operating System(RTOS), ATOMTHREADS, by implementing extra thread functionalities and light weight inter-Thread communications. The new functionalities which are added conform with the existing architecture of ATOMTHREADS and using the existing framework of ATOMTHREADS.

MOTIVATION

The real-time operating system is software that uses the most resource- and time-effective means to exact results. There is no early or late execution on the real-time operating system, and it is executed on time as suggested. There are two main types of real-time operating systems: Soft, which is less strict on time and accuracy, and hard, which shows the exact execution period and delivers the output at the required time . Thread scheduling ensures effective utilisation of wait time and better program flow and event response. ATOMTHREADS is a free, lightweight, portable, real-time scheduler for embedded systems. It offers the advantages of a real time scheduler without taking much memory space. Hence ATOMTHREADS is ideal for small microcontrollers with a small memory .

Though ATOMTHREADS ensures real time behaviour because of static thread creation , low weight kernel ,still few functionalities are missing in ATOMTHREADS because of which it is less of a complete light weight RTOS. Some missing functionalities are required to make a system , failure-tolerant and robust and some are required to make inter-Thread communication faster for faster response even within the deadline.

So this project aims at adding functionalities to the kernel software of ATOMTHREADS within and using the ATOMTHREADS framework . Following functionalities are added in the framework of the kernel :

  • Forced Suspension of Threads .
  • Forced Resumption of suspended Threads
  • Forced Restart of Threads in case of faulty execution of the Threads/Exceptions
  • Faster communication between Threads and between ISR’s and Threads.

OVERVIEW AND ARCHITECTURE OF ATOMTHREADS RTOS

  • Function Calls
    • OS Init , IDLE THREAD : This is the initialisation of ATOMTHREADS . It makes sure that the kernel queue is not devoid of thread , by putting a IDLE thread in the queue.

RTOS OBJECTS : These are RTOS objects like mutex , timer , queues etc. The suspended list of the Threads for a particular object is maintained in RTOS objects corresponding to which suspension has occurred

    • Queuing and Dequeing of Threads : AtomThreads maintains a ready queue where the threads are queued and dequeued based on suspension by RTOS objects. Scheduler looks at the ready queue while dispatching tasks from queue.
  • Handlers
    • Sys Tick Handlers : This is the handler called on the expiry of systick timer. It looks at the ready queue , picks up the right task for running and then sets the pendSV interrupt.

PendSVInterrupt : This is the handler that SysTick Handler pends to perform the actual context switch between the current task and the next task

    • Thread Shells : This function is called when a new thread is scheduled in for the first time. It will simply call the threads entry point function.This is useful since this allows the tasks to be called with parameters and maintains a commonality in the RTOS.

Description of the added functionalities

Following functionalities were added in the existing framework of ATOMTHREADS

THREADS SUSPENSION

In ATOMTHREADS , Threads are normally suspended while waiting for any resource in RTOS elements (semaphore, mutex etc) . In ATOMTHREADS , The thread suspension is carried out by the RTOS elements (queues , semaphore , mutex , timers etc) and is not given in control of the user created Threads. This poses a problem while developing a health monitoring Thread . Health Monitoring Threads are user threads which are commonly created in automotive and aerospace application to monitor the fault activity of the other threads. Some of the reasons , although inexhaustive, of a faulty operation of a thread can be as follows:

* Higher priority Thread taking more time to execute thereby halting lower priority thread.
* Thread encountered exception and is working with faulty data
* User writes a wrong code in the thread which gives a  anomalous behaviour of the thread.

Health Monitoring Threads are hence created to suspend faulty threads. In that way the whole operation of the system is not compromised, and the system performs a limited functionality following a graceful degradation. So a thread suspension system call was required to be implemented in ATOMTHREADS.

THREADS RESUMPTION

Threads are normally resumed following a suspension if the suspension was caused by RTOS elements (semaphore, mutex etc) . But again, the thread resumption is carried out by the kernel and control is never given to the user threads Carrying forward from the requirement of Thread Suspension from user thread , there is an inarguable requirement of a thread’s resumption from the suspension state based on the user thread request . So a thread resumption system call was required to be implemented in ATOMTHREADS. In ATOMTHREADS , Threads are never restarted either by kernel or by user. There are no system calls for restarting the Threads because there are no system calls for killing the threads . This poses quite of a problem ,because that way a faulty important thread can never be restarted, and a very important system function may fail as a whole. Considering a situation where a lightweight High Priority Thread is on the verge of finishing a functionality but finally encounters an exception because of a faulty UART data coming from any external system/ or Thread stack corruption

In this situation the High Priority Thread can be suspended by the Health Monitoring Thread(if present ) but he functionality of the High Priority Thread will never get executed. In this scenario the system will fail to execute the important functionality which may lead to total failure of the entire system just because of one frame of faulty uart data / or Thread stack corruption 

In these scenarios , Thread Restart is an important functionality which can be executed by either Kernel or Health Monitoring Thread to ensure that the system does not fail because of one frame of faulty uart data /or Thread stack corruption .

PIPES IMPLEMENTATIONS

Pipes are very light weight inter Thread communication medium which can be used by both ISRs and Thread to communication with and amongst each other . Pipes are different from messages queues primarily because of their raw nature of communication. Pipes does not have header and footer which makes it easier for ISRs and threads to transfer data without any execution overheads . Pipes doesn’t have any message no ID , and hence they are unidirectional means of communication between two specific threads. This unidirectional means of communication in FIFO eliminates the need of overheads which makes them fast and lightweight.

  • Two TCBs are taken during the formation of pipe
  • The two TCBs are assigned write and read permission only . This is how unidirectionality is maintained
  • A TCB pointer table is maintained inside pipe functionality to check who has the write permission and who has the read by comparing with the current context
  • If one TCB is null it is assumed as ISR
  • ISRs always have write permission
  • The pipe size can be defined by user while creation , but the unit size is one byte.
  • Pipe is implemented as a circular FIFO
  • When FIFO is full , the task is blocked.
  • If ISR is writing then the FIFO behaves as a circular fashion.


Function Description of the Added Functionalities

Function Name : EnableExceptions File  : ExceptionGeneration.c Include File : ExceptionGeneration.h Prototype : void EnableExceptions( uint8_t exceptionnumber, uint8_t control) Input Arguments exceptionnumber = Exception Number (options available in ExceptionGeneration.h) control = Enable or disable the exception (options available in ExceptionGeneration.h) Output None Function Description Enables / Disables the exception based on the control and exception number provided in the arguments.


Function Name : atomThreadRestart Include File : atom.h Prototype : uint8_t atomThreadRestart (ATOM_TCB *tcb_ptr) Input Arguments tcb_ptr = Pointer to the Thread control block of the thread to be restarted

Output status of the operation(options available in atom.h) Description 1. Reinitialised the Thread control block to default states (same values after creation) 2. Reinitialises the stack of the Thread to default values (same values after creation) 3. Puts the Thread in the ready queue


Function Name : atomThreadSuspend Include File : atom.h Prototype : uint8_t atomThreadSuspend (ATOM_TCB *tcb_ptr) Input Arguments tcb_ptr = Pointer to the Thread control block of the thread to be restarted

Output status of the operation(options available in atom.h) Description 1. Dequeues the entry of the Thread from the ready queue. 2. Set the suspended status to TRUE 3. Set the forced suspended status to TRUE. Its required for next forced resumption to be valid. 4. Relinquishes all the RTOS objects

This function is protected by critical section.


Function Name : atomThreadResume Include File : atom.h Prototype : uint8_t atomThreadRestart (ATOM_TCB *tcb_ptr) Input Arguments tcb_ptr = Pointer to the Thread control block of the thread to be restarted

Output status of the operation(options available in atom.h) Description 1. Enqueue the Entry to the ready queue 2. Set the suspended status to FALSE 3. Set the forced suspended status to FALSE . Its required for next forced suspension to be valid. 4. Set the forced resume status to FALSE 5. Takes back all the RTOS objects.

This function is protected by critical section.


Function Name : AddToExceptionThreadList Include File : atom.h Prototype : void AddToExceptionThreadList (ATOM_TCB *tcb_ptr) Input Arguments tcb_ptr = Pointer to the Thread control block of the thread which has caused exception

Output none Description 1. Makes an entry of the TCB pointer in the exception Thread List Table. An exception Thread List Table is a table containing all the Threads which has caused exception.

This function is protected by critical section.


Function Name : GetExceptionThreadList Include File : atom.h Prototype : uint8_t GetExceptionThreadList (ATOM_TCB *tcb_ptr) Input Arguments tcb_ptr = Pointer to the Thread control block of the thread which has caused exception.

Output Gets the number of Thread which has caused exceptions Description 1. Removes an entry of the TCB pointer in the exception Thread List Table.

This function is protected by critical section.


Function Name : pipe_remove Include File : None. Its included in C file for internal use. Not to be used by application or kernel functions. Prototype : static uint8_t pipe_remove (ATOM_PIPE *pptr, uint8_t* readbuff,uint8_t size) Input Arguments pptr = Pointer to the pipe object. readbuff = Destination pointer for the message to be copied into.

size = Required number of bytes.


Output Gives the status of the operation (list can be found in atompipe.h) Description Removes a message from a pipe. Assumes that there is a message present, which is already checked by the calling functions with interrupts locked out.


Function Name : pipe_insert Include File : None. Its included in C file for internal use. Not to be used by application or kernel functions. Prototype : static uint8_t pipe_insert (ATOM_PIPE *pptr, uint8_t* writebuff , uint8_t size) Input Arguments pptr = Pointer to the pipe object. writebuff = Pointer to the write buffer from where pipe data will be copied.

size = Required number of bytes.


Output Gives the status of the operation (list can be found in atompipe.h) Description Inserts a message onto a pipe. Assumes that the pipe has space for one message, which has already been checked by the calling function with interrupts locked out.


Function Name : atomPipeTimerCallback Include File : None. Its included in C file for internal use. Not to be used by application or kernel functions. Prototype : static void atomPipeTimerCallback (POINTER cb_data) Input Arguments cb_data = Call back structure of the Pipe Timer. Output None Description Timeouts on suspended threads are notified by the timer system through his generic callback. The timer system calls us back with a pointer to the relevant pipe_TIMER object which is used to retrieve the pipe details.This function is protected by critical section.


Function Name : atomPipeCreate Include File : atompipe.h. Prototype : uint8_t atomPipeCreate (ATOM_PIPE *pptr, uint8_t *buff_ptr, uint32_t max_num_bytes) Input Arguments pptr : Pointer to pipe object buff_ptr: Pointer to buffer storage area max_num_bytes: Maximum number of messages in the pipe Output Gives the status of the operation (list can be found in atompipe.h) Description 1. Creates a Pipe Object for communication


Function Name : atomPipeDelete Include File : atompipe.h. Prototype : uint8_t atomPipeDelete (ATOM_PIPE *pptr) Input Arguments pptr : Pointer to pipe object

Output Gives the status of the operation (list can be found in atompipe.h) Description 1. Deletes the Pipe object


Function Name : atomPipeGet Include File : atompipe.h. Prototype : uint8_t atomPipeGet (ATOM_PIPE *pptr, uint8_t* readbuff , uint8_t size, int32_t timeout) Input Arguments pptr Pointer to pipe object timeout Max system ticks to block (0 = forever, -1 = no block)

readbuff Pointer to which the received message will be copied

Output Gives the status of the operation (list can be found in atompipe.h) Description 1. Attempt to retrieve a message from a pipe. 2. Retrieves one message at a time. 3. Messages are copied into the passed readbuff storage area which should be large enough to contain one message of unit_size bytes. 4. Where multiple messages are in the pipe, messages are retrieved in FIFO order.


Function Name : atompipePut Include File : atompipe.h. Prototype : uint8_t atomPipePut (ATOM_PIPE *pptr, uint8_t* writebuff , uint8_t size, int32_t timeout) Input Arguments pptr Pointer to pipe object timeout Max system ticks to block (0 = forever, -1 = no block)

readbuff Pointer to which the message should be copied out

Output Gives the status of the operation (list can be found in atompipe.h) Description 1. Attempt to put a message onto a pipe. 2. Sends one message at a time. Messages are copied from the passed msgptr storage area which should contain a message of bytes


Function Name : SetExceptionFlag Include File : None Prototype : static void SetExceptionFlag() Input Arguments None Output None Description Sets the exception flag in the fault handler for use in the sys tick handler


Function Name : SetExceptionFlag Include File : None Prototype : static void SetExceptionFlag() Input Arguments None Output None Description Sets the exception flag in the fault handler for use in the sys tick handler


Function Name : GetExceptionFlag Include File : atomport.h Prototype : uint8_t GetExceptionFlag() Input Arguments None Output None Description Returns the status of Exception Flag


Function Name : ResetExceptionFlag Include File : None Prototype : static void SetExceptionFlag() Input Arguments None Output None Description Resets the Exception Flag to 0


Function Name : usage_fault_handler Include File : None Prototype : void usage_fault_handler(void) Input Arguments None Output None Description 1. Pushes the LR register 2. Sets the exception flag 3. Gets the current Thread block 4. Adds the current Thread block to the exception list 5. POPs LR 6. Returns via LR


Function Name : bus_fault_handler Include File : None Prototype : void bus_fault_handler (void) Input Arguments None Output None Description 1. Pushes the LR register 2. Sets the exception flag 3. Gets the current Thread block 4. Adds the current Thread block to the exception list 5. POPs LR 6. Returns via LR

Test Cases for the added Functionalities

To test the functionalities of the new functionalities , an architecture is developed contatining Threads ,Exceptions and ISR’s. The architecture is depicted below along the operations of each block


Results

Challenges , Learnings and Future Work

Challenges

  • There is not documentation and discussion forums on ATOMTHREADS , hence implementing functionalities conforming to AtomThreads framework required a detailed walk-through of the existing code which was time taking.
  • Since there were context switches , so breakpoints were not helpful in understanding the flow of execution , especially when pendSV is involved
  • Since mostly all the queue, tcbs are pointers (some were pointers to pointers) , so watching the values in the debug window was in vain.
  • Tracking the stack pointers and the CPU registers during the execution of exception and then back to Thread was challenging.
  • Heavy use of assembly language in ThreadRestore and PendSV handlers required a study of the assembly language of the Cortex-M4 processor


Learnings

  • Understanding of Atom Threads RTOS along with the limitations
  • Understanding of different stack pointers and their role in context switching.
  • Approach for implementing RTOS objects or extend RTOS functionalities in a given RTOS framework
  • Understanding of different states and the responsibility of the objects in maintaining the suspended Threads rather than the kernel.
  • Understanding the two different contexts , Interrupt Context and Thread Context and the sections of CPU registers required to maintain the two contexts.
  • Understanding of Exceptions and how to go back to thread mode from Exception Context.
  • Understanding the Thread Control Block Structure and making the modifications as per the need to implement new functionality.
  • Understanding the Stack Region of the Threads and the different registers which are saved during context saving.

Future Work

The future work is written keeping in mind the existing framework of Atomthreads

  • Dynamic Creation of Threads rather than static creation
  • Implementation of Thread Deletion
  • Implementation of Signals to cause event(software events) on another Thread.
  • Implementation of Drivers through System Call Table and developing the systemcall interface between RTOS and Application through SVC calls
  • Implementation of different time slices for Threads of same priority depending on functionality.

References

  • Tiva™ TM4C123GH6PM Microcontroller Datasheet

(https://www.ti.com/lit/ds/spms376e/spms376e.pdf?ts=1612427924240&ref_url=https%253A%252F%252Fwww.google.com%252F)