Skip to content

Commit 346ee61

Browse files
committed
Documentation: teaching: Add ARM kernel development lab
This is an introduction to the ARM world. Starting with a simple overview of SoCs, will jump into how to setup a development environment by installing a crosscompiling. Then will go into how to compile the linux kernel and boot it using qemu. Finally, we will have a look at how ARM uses Device tree files to describe the hardware. Keywords: SoCs, BSP, toolchain, crosscompiler, i.MX6UL. Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>
1 parent e8954d2 commit 346ee61

File tree

9 files changed

+488
-0
lines changed

9 files changed

+488
-0
lines changed

Documentation/teaching/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ tools/labs/docs/Dockerfile for dependencies):
7373
labs/filesystems_part1.rst
7474
labs/filesystems_part2.rst
7575
labs/networking.rst
76+
labs/arm_kernel_development.rst
7677
labs/memory_mapping.rst
7778
labs/device_model.rst
7879

Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
=========================
2+
Kernel Development on ARM
3+
=========================
4+
5+
Lab objectives
6+
==============
7+
8+
* get a feeling of what System on a Chip (SoC) means
9+
* get familiar with embedded world using ARM as support architecture
10+
* understand what a Board Support Package means (BSP)
11+
* compile and boot an ARM kernel with Qemu using i.MX6UL platform as an example
12+
* get familiar with hardware description using Device Trees
13+
14+
System on a Chip
15+
================
16+
17+
A System on a Chip (**SoC**) is an integrated circuit (**IC**) that integrates an entire system onto it. The components
18+
that can be usually found on an SoC include a central processing unit (**CPU**), memory, input/output ports, storage devices
19+
together with more sophisticated modules like audio digital interfaces, neural processing units (**NPU**) or graphical
20+
processing units (**GPU**).
21+
22+
SoCs can be used in various applications most common are:
23+
- consumer electronics (TV sets, mobile phones, video game consoles)
24+
- industrial computers (medical imaging, etc)
25+
- automotive
26+
- home appliances
27+
28+
The leading architecture for SoCs is **ARM**. Worth mentioning here is that there are also x86-based SoCs platforms. Another thing
29+
we need to keep an eye on is **RISC-V** an open standard instruction set architecture.
30+
31+
A simplified view of an **ARM** platform is shown in the image below:
32+
33+
.. image:: ../res/schematic.png
34+
:align: center
35+
36+
We will refer as a reference platform at NXP's `i.MX6UL <imx6ul>`_ platform, but in general all SoC's contain the following building blocks:
37+
38+
- one or more CPU cores
39+
- a system bus
40+
- clock and reset module
41+
42+
- PLL
43+
- OSC
44+
- reset controller
45+
46+
- interrupt controller
47+
- timers
48+
- memory controller
49+
- peripheral controllers
50+
51+
- `I2C <https://en.wikipedia.org/wiki/I%C2%B2C>`_
52+
- `SPI <https://en.wikipedia.org/wiki/Serial_Peripheral_Interface>`_
53+
- `GPIO <https://en.wikipedia.org/wiki/General-purpose_input/output>`_
54+
- `Ethernet <https://en.wikipedia.org/wiki/Network_interface_controller>`_ (for network)
55+
- `uSDHC <https://en.wikipedia.org/wiki/MultiMediaCard>`_ (for storage)
56+
- USB
57+
- `UART <https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter>`_
58+
- `I2S <https://en.wikipedia.org/wiki/I%C2%B2S>`_ (for sound)
59+
- eLCDIF (for LCD Panel)
60+
61+
Here is the complete block diagram for i.MX6UL platform:
62+
63+
.. image:: https://www.nxp.com/assets/images/en/block-diagrams/IMX6UL-BD.jpg
64+
:alt: IMX6UL-BD
65+
:width: 60 %
66+
:align: center
67+
68+
i.MX6UL Evaluation Kit board looks like this:
69+
70+
.. image:: https://www.compulab.com/wp-content/gallery/sbc-imx6ul/compulab_sbc-imx6ul_single-board-computer.jpg
71+
:alt: imx6ul-evk
72+
:width: 60 %
73+
:align: center
74+
75+
Other popular SoC boards:
76+
77+
* `Broadcom Raspberry Pi <https://en.wikipedia.org/wiki/Raspberry_Pi>`_
78+
* `Texas Instruments Beagle board <https://en.wikipedia.org/wiki/BeagleBoard>`_
79+
* `Odroid Xu4 <https://wiki.odroid.com/odroid-xu4/odroid-xu4>`_
80+
* `Nvidia Jetson Nano <https://developer.nvidia.com/embedded/jetson-nano-developer-kit>`_
81+
82+
Board Support package
83+
=====================
84+
85+
A board support package (**BSP**) is the minimal set of software packages that allow to demonstrate the capabilities of a certain hardware platform. This includes:
86+
87+
- toolchain
88+
- bootloader
89+
- Linux kernel image, device tree files and drivers
90+
- root filesystem
91+
92+
Semiconductor manufacturers usually provide a **BSP** together with an evaluation board. BSP is typically bundled using `Yocto <https://www.yoctoproject.org/>`_
93+
94+
Toolchain
95+
=========
96+
Because our development machines are mostly x86-based we need a cross compiler that can produce executable
97+
code for ARM platform.
98+
99+
We can build our own cross compiler from scratch using https://crosstool-ng.github.io/ or we can install one
100+
101+
.. code-block:: bash
102+
103+
$ sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf # for arm32
104+
$ sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu # for arm64
105+
106+
There are several of toolchain binaries depending on the configuration:
107+
108+
- With "arm-eabi-gcc" you have the Linux system C library which will make calls into the kernel IOCTLs, e.g. for allocating memory pages to the process.
109+
- With "arm-eabi-none-gcc" you are running on platform which doesn't have an operating system at all - so the C library is different to cope with that.
110+
111+
Compiling the Linux kernel on ARM
112+
---------------------------------
113+
114+
Compile the kernel for 32bit ARM boards:
115+
116+
.. code-block:: bash
117+
118+
# select defconfig based on your platform
119+
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make imx_v6_v7_defconfig
120+
# compile the kernel
121+
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make -j8
122+
123+
Compile the kernel for 64bit ARM boards:
124+
125+
.. code-block:: bash
126+
127+
# for 64bit ARM there is a single config for all supported boards
128+
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make defconfig
129+
# compile the kernel
130+
$ ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- make -j8
131+
132+
Linux kernel image
133+
==================
134+
135+
The kernel image binary is named ``vmlinux`` and it can be found in the root of the kernel tree. Compressed image used for booting can be found under:
136+
137+
- ``arch/arm/boot/Image``, for arm32
138+
- ``arch/arm64/boot/Image``, for arm64
139+
140+
.. code-block:: bash
141+
142+
$ file vmlinux
143+
vmlinux: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped
144+
145+
$ file vmlinux
146+
vmlinux: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), statically linked, not stripped
147+
148+
Rootfs
149+
======
150+
151+
The root filesystem (``rootfs``) is the filesystem mounted at the top of files hierarchy (``/``). It should contain at least
152+
the critical files allowing the system to boot to a shell.
153+
154+
.. code-block:: bash
155+
156+
root@so2$ tree -d -L 2
157+
├── bin
158+
├── boot
159+
├── dev
160+
├── etc
161+
├── home
162+
│   └── root
163+
├── lib
164+
│   └── udev
165+
├── mnt
166+
├── proc
167+
├── sbin
168+
│   └── init
169+
├── sys
170+
├── usr
171+
│   ├── bin
172+
│   ├── include
173+
│   ├── lib
174+
└── var
175+
176+
As for ``x86`` we will make use of Yocto rootfs images. In order to download an ``ext4`` rootfs image for ``arm32`` one needs to run:
177+
178+
.. code-block:: bash
179+
180+
$ cd tools/labs/
181+
$ ARCH=arm make core-image-minimal-qemuarm.ext4
182+
183+
Device tree
184+
===========
185+
186+
Device tree (**DT**) is a tree structure used to describe the hardware devices in a system. Each node in the tree describes a device hence it is called **device node**. DT was introduced
187+
to provide a way to discover non-discoverable hardware (e.g a device on an I2C bus). This information was previously stored inside the source code for the Linux kernel. This meant that
188+
each time we needed to modify a node for a device the kernel needed to be recompiled. This no longer holds true as device tree and kernel image are separate binaries now.
189+
190+
Device trees are stored inside device tree sources (*.dts*) and compiled into device tree blobs (*.dtb*).
191+
192+
.. code-block:: bash
193+
194+
# compile dtbs
195+
$ make dtbs
196+
197+
# location for DT sources on arm32
198+
$ ls arch/arm/boot/dts/
199+
imx6ul-14x14-evk.dtb imx6ull-14x14-evk.dtb bcm2835-rpi-a-plus.dts
200+
201+
# location for DT source on arm64
202+
$ ls arch/arm64/boot/dts/<vendor>
203+
imx8mm-evk.dts imx8mp-evk.dts
204+
205+
The following image is a represantation of a simple device tree, describing board type, cpu and memory.
206+
207+
.. image:: ../res/dts_node.png
208+
:align: center
209+
210+
Notice that a device tree node can be defined using ``label: name@address``:
211+
212+
- ``label``, is an identifier used to reference the node from other places
213+
- ``name``, node identifier
214+
- ``address``, used to differentiate nodes with the same name.
215+
216+
A node might contain several properties arranged in the ``name = value`` format. The name is a string
217+
and the value can be bytes, strings, array of strings.
218+
219+
Here is an example:
220+
221+
.. code:: c
222+
223+
/ {
224+
node@0 {
225+
empty-property;
226+
string-property = "string value";
227+
string-list-property = "string value 1", "string value 2";
228+
int-list-property = <value1 value2>;
229+
230+
child-node@0 {
231+
child-empty-property;
232+
child-string-property = "string value";
233+
child-node-reference = <&child-node1>;
234+
};
235+
236+
child-node1: child-node@1 {
237+
child-empty-property;
238+
child-string-property = "string value";
239+
};
240+
};
241+
};
242+
243+
Qemu
244+
====
245+
246+
We will use ``qemu-system-arm`` to boot 32bit ARM platforms. Although, this can be installed from official distro repos, for example:
247+
248+
.. code:: bash
249+
250+
sudo apt-get install -y qemu-system-arm
251+
252+
We strongly recommend using latest version of ``qemu-system-arm`` build from sources:
253+
254+
.. code:: bash
255+
256+
$ git clone https://gitlab.com/qemu-project/qemu.git
257+
$ ./configure --target-list=arm-softmmu --disable-docs
258+
$ make -j8
259+
$ ./build/qemu-system-arm
260+
261+
Exercises
262+
=========
263+
264+
.. include:: ../labs/exercises-summary.hrst
265+
.. |LAB_NAME| replace:: arm_kernel_development
266+
267+
.. warning::
268+
269+
The rules for working with the virtual machine for ``ARM`` are modified as follows
270+
271+
.. code-block:: shell
272+
273+
# modules build
274+
tools/labs $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make build
275+
# modules copy
276+
tools/labs $ ARCH=arm make copy
277+
# kernel build
278+
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make -j8
279+
280+
0. Intro
281+
--------
282+
283+
Inspect the following locations in the Linux kernel code and identify platforms and vendors using
284+
ARM architecture:
285+
286+
- 32-bit: ``arch/arm/boot/dts``
287+
- 64-bit: ``arch/arm64/boot/dts``
288+
289+
290+
Use ``qemu`` and look at the supported platforms:
291+
292+
.. code-block:: bash
293+
294+
../qemu/build/arm-softmmu/qemu-system-arm -M ?
295+
296+
.. note:: We used our own compiled version of ``Qemu`` for ``arm32``. See `Qemu`_ section for more details.
297+
298+
1. Boot
299+
-------
300+
301+
Use ``qemu`` to boot ``i.MX6UL`` platform. In order to boot, we first need to compile the kernel.
302+
Review `Compiling the Linux kernel on ARM`_ section.
303+
304+
Successful compilation will result in the following binaries:
305+
306+
- ``arch/arm/boot/Image``, kernel image compiled for ARM
307+
- ``arch/arm/boot/dts/imx6ul-14x14-evk.dtb``, device tree blob for ``i.MX6UL`` board
308+
309+
Review `Rootfs`_ section and download ``core-image-minimal-qemuarm.ext4`` rootfs.
310+
Run ``qemu`` using then following command:
311+
312+
.. code-block:: bash
313+
314+
../qemu/build/arm-softmmu/qemu-system-arm -M mcimx6ul-evk -cpu cortex-a7 -m 512M \
315+
-kernel arch/arm/boot/zImage -nographic -dtb arch/arm/boot/dts/imx6ul-14x14-evk.dtb \
316+
-append "root=/dev/mmcblk0 rw console=ttymxc0 loglevel=8 earlycon printk" -sd tools/labs/core-image-minimal-qemuarm.ext4
317+
318+
.. note:: LCDIF and ASRC devices are not well supported with ``Qemu``. Remove them from compilation.
319+
320+
.. code-block:: bash
321+
322+
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make menuconfig
323+
# set FSL_ASRC=n and DRM_MXSFB=n
324+
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make -j8
325+
326+
Once the kernel is booted check kernel version and cpu info:
327+
328+
.. code-block:: bash
329+
330+
$ cat /proc/cpuinfo
331+
$ cat /proc/version
332+
333+
2. CPU information
334+
------------------
335+
336+
Inspect the CPU configuration for ``NXP i.MX6UL`` board. Start with ``arch/arm/boot/dts/imx6ul-14x14-evk.dts``.
337+
338+
- find ``cpu@0`` device tree node and look for ``operating-points`` property.
339+
- read the maximum and minimum operating frequency the processor can run
340+
341+
.. code:: bash
342+
343+
$ cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq
344+
$ cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
345+
346+
3. I/O memory
347+
-------------
348+
Inspect I/O space configuration for ``NXP i.MX6UL`` board. Start with ``arch/arm/boot/dts/imx6ul-14x14-evk.dts`` and identify each device mentioned below.
349+
350+
.. code:: bash
351+
352+
$ cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_min_freq
353+
00900000-0091ffff : 900000.sram sram@900000
354+
0209c000-0209ffff : 209c000.gpio gpio@209c000
355+
021a0000-021a3fff : 21a0000.i2c i2c@21a0000
356+
80000000-9fffffff : System RAM
357+
358+
Identify device tree nodes corresponding to:
359+
360+
- ``System RAM``, look for ``memory@80000000`` node in ``arch/arm/boot/dts/imx6ul-14x14-evk.dtsi``. What's the size of the System RAM?
361+
- ``GPIO1``, look for ``gpio@209c000`` node in ``arch/arm/boot/dts/imx6ul.dtsi``. What's the size of the I/O space for this device?
362+
- ``I2C1``, look for ``i2c@21a0000`` node in ``arch/arm/boot/dts/imx6ul.dtsi``. What's the size of the I/O spaces for this device?
363+
364+
4. Hello World
365+
--------------
366+
367+
Implement a simple kernel module that prints a message at load/unload time. Compile it and load it on ``i.MX6UL`` emulated platform.
368+
369+
.. code-block:: shell
370+
371+
# modules build
372+
tools/labs $ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make build
373+
# modules copy
374+
tools/labs $ ARCH=arm make copy
375+
# kernel build
376+
$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make -j8
377+
378+
5. Simple device
379+
----------------
380+
381+
Implement a driver for a simple platform device. Find ``TODO 1`` and notice how ``simple_driver`` is declared and register as a platform driver.
382+
Follow ``TODO 2`` and add the ``so2,simple-device-v1`` and ``so2,simple-device-v2`` compatible strings in the simple_device_ids array.
383+
384+
Create two device tree nodes in ``arch/arm/boot/dts/imx6ul.dtsi`` under ``soc`` node with compatible strings ``so2,simple-device-v1`` and
385+
``so2,simple-device-v2`` respectively. Then notice the behavior when loading ``simple_driver`` module.
386+
387+
.. _imx6ul: https://www.nxp.com/products/processors-and-microcontrollers/arm-processors/i-mx-applications-processors/i-mx-6-processors/i-mx-6ultralite-processor-low-power-secure-arm-cortex-a7-core:i.MX6UL
113 KB
Loading
36.3 KB
Loading

0 commit comments

Comments
 (0)