2017년 3월 9일 목요일

RIOT OS 소개

이번 posting에서는 이례적으로 Linux가 아닌 다른 OS 즉, RIOT OS를 소개하는 시간을 가져 보고자 한다.

RIOT aims at a Linux-like openness

The friendly Operating System for the Internet of Things


ARM Cortex-M3 STM32F103RB 보드 + OpenOCD(ST-Link/V2-1) + RIOT OS
a) Linux보다 lightweight한 RIOT OS를 통해 RTOS의 특징을 한눈에 파악해 볼 수 있다.
b) Keil MDK, IAR EWARM 혹은 GCC 기반의 IDE 없이도 vi(m)와 openocd, eclipse만으로 충분히 개발할 수 있다.

<목차>
1. RIOT OS 개요
2. Docker를 이용한 RIOT OS build 방법 소개
3. RIOT OS sample code 소개 
4. Nucleo F103 보드에서의 RIOT OS 동작 시험
5. Nucleo F103 보드 Boot Flow 분석


1. RIOT OS 개요
RIOT OS는 IoT(Internet of Things - 사물인터넷)를 겨냥한 real-time OS이다. IoT를 위한 OS로는 linux를 쉽게 생각해 볼 수도 있겠으나, RIOT OS가 target으로 삼는 platform은 linux로는 도저히 cover하기 힘든 초소형 시스템(8, 16, 32bit platform, 32bit의 경우는 RAM 20KB, Flash 128KB 정도 혹은 그 보다 작은 platform)이다.

<IoT platform world>
초소형 platform(RIOT OS) ---- Linux(소형/중형) ---- Android Things(UI가 필요한 환경)
(*) 물론, 다른 OS(예: Windows IoT Core, Apple HomeKit)도 생각해 볼 수 있다.


그림 1.1 RIOT OS의 영역(target)

현재 IoT를 위한 초 경량 OS로는 RIOT OS외에도 Contiki, Tiny OS 등이 있다(참고로 Apple iOS 기반의 HomeKit, Google Android Things 등도 IoT를 겨냥한 OS 이지만, target 시장에 차이가 있다고 보아야 함). 여기에 Linux를 포함시킨 상태에서 이들과 RIOT OS를 비교해 보면 다음과 같다.

그림 1.2 다른 OS(Contiki, Tiny OS, Linux)와의 비교표

여기서 잠깐 !  RTOS 중에 어떤 녀석을 사용해야 할까 ?
아래 site 내용을 보면, open source RTOS와 관련하여 자세한 사항을 확인할 수 있을 것이다.

https://www.linux.com/news/open-source-operating-systems-iot

https://www.osrtos.com/
----------------------------------------------------------------------------------------------------------------------------------------------------

RIOT OS의 특징을 몇가지로 요약해 보면 다음과 같다(자세한 사항은 그림 1.3 참조).
  a) C/C++ programming(마치 linux 상에서 application 코드를 작성하는 느낌이랄까 ?)
  b) Real-time 능력 보유(ultra-low interrupt latency, 우선 순위 기준 scheduling, multi-threading 지원)
  c) 8-bit, 16-bit, 32-bit platform 지원(현재 많은 board에서 동작이 검증되었으며, 새로운 platform을 계속해서 지원할 예정임) 
  d) 6LoWPAN, IPv6, RPL, UDP 등 network stack 지원 등
  e) 개발 환경이 잘 갖추어진 느낌이다.
  ...

그림 1.3 RIOT OS 주요 features

아래 그림 1.4는 RIOT OS의  전체 구조를 하나의 그림으로 표현해 놓은 것이며, 그림 1.5는 이 중 network stack 부분을 따로 떼어 정리해 놓은 것이다.

그림 1.4 RIOT OS 아키텍쳐


그림 1.5 RIOT OS network stack

마지막으로 그림 1.6은 RIOT OS가 현재 지원하는 hardware platform을 간략히 정리해 놓은 것이다.


그림 1.6 RIOT OS가 지원하는 hardware



2. Docker를 이용한 RIOT OS build 방법 소개
이번 절에서는 RIOT OS를 내려 받아 build하는 방법을 소개해 보고자 한다. 이 대목에서 docker가 언급되는 이유는 RIOT OS가 현재 Ubuntu 14.10(OSX, FreeBSD 등에서도 build 가능함)까지만 지원하고 있기 때문(필자는 Ubuntu 16.04를 사용하고 있음)으로, RIOT OS는 공식적으로 docker를 사용하여 개발할 수 있는 환경(Dockerfile)을 함께 제공하고 있다. 다시 한번 docker의 강력함을 깨닫게 되는 순간이다 :)

<How to download and build the RIOT OS source code>
git clone git://github.com/RIOT-OS/RIOT.git

그림 2.1 RIOS OS source tree

Ubuntu 14.04 등 현재 RIOT OS가 지원하는 환경을 사용하는 경우라면 아래와 같이 곧바로 build 작업을 진행할 수 있다. RIOT OS build와 관련해서는 아래 site의 내용을 참조하기 바란다.


$ cd RIOS
./dist/tools/tapsetup/tapsetup
creating tapbr0
[sudo] password for chyi:
creating tap0
creating tap1

(*) ifconfig -a 명령을 통해 tapbr0, tap0, tap1 interface가 생성되었음을 확인할 수 있음.
(*) 주의 사항: ubuntu host에 bridge-utils package가 미리 설치되어 있어야 함.

$ cd examples/default
$ make all
=> 'default' example code를 build한다.
$ make term
=> terminal(pyterm)을 띄운다.

<How to build the RIOT OS with docker>

$ git clone https://github.com/RIOT-OS/riotdocker
  => RIOT docker source(Dockerfile)를 내려 받는다.

$ cd riotdocker
chyi@earth:~/IoT/RIOT_OS/docker/riotdocker$ ls -la
합계 52
drwxrwxr-x 3 chyi chyi  4096  3월  8 21:33 .
drwxrwxr-x 3 chyi chyi  4096  3월  8 21:33 ..
drwxrwxr-x 8 chyi chyi  4096  3월  8 21:33 .git
-rw-rw-r-- 1 chyi chyi  4238  3월  8 21:33 Dockerfile
-rw-rw-r-- 1 chyi chyi    43  3월  8 21:33 README-short.txt
-rw-rw-r-- 1 chyi chyi    83  3월  8 21:33 README.md
-rw-rw-r-- 1 chyi chyi   355  3월  8 21:33 create_user.c
-rw-rw-r-- 1 chyi chyi 12370  3월  8 21:33 logo.png
-rwxrwxr-x 1 chyi chyi  1163  3월  8 21:33 run.sh

$ docker build -t riotbuild .
  => Dockerfile을 토대로 riotbuild를 위한 docker image를 생성해 내자. 시간이 꽤 오래 걸림 !

$ docker images
  => docker image가 제대로 생성되었는지 확인 !
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
riotbuild               latest              c379d9d10e82        14 hours ago        2.78 GB
ubuntu                  xenial              0ef2e08ed3fa        9 days ago          130 MB
chyi/udoo               latest              e3229a376acc        2 weeks ago         63.6 GB
ubuntu                  latest              f49eec89601e        6 weeks ago         129 MB
ubuntu                  trusty              b969ab9f929b        6 weeks ago         188 MB
hello-world             latest              48b5124b2768        7 weeks ago         1.84 kB

$ docker run -i -t -u $UID -v $(pwd):/data/riotbuild riotbuild ./dist/tools/compile_test/compile_test.py
  => RIOT OS가 지원하는 모든 target board에 대한 build 테스트를 진행한다.
  => 너무 시간이 오래 걸려 중간에 강제로 멈추게 함. :(

$ docker run -i -t -u $UID -v $(pwd):/data/riotbuild --env BOARD=native riotbuild make -C examples/default clean all
  => native용(no target board, x86용) example code(default 디렉토리 코드) build 시도 ! 
  => 재밌게도 docker image로 login하지 않고, 외부(Ubuntu 16.04)에서 docker image를 이용하여 build를 진행함.
  => 주의: RIOT source code top 위치(즉, RIOT/ 디렉토리)에서 위의 명령을 실행해 주어야 함.

make: Entering directory '/data/riotbuild/examples/default'
Building application "default" for "native" with MCU "native".

"make" -C /data/riotbuild/boards/native
"make" -C /data/riotbuild/boards/native/drivers
"make" -C /data/riotbuild/core
"make" -C /data/riotbuild/cpu/native
"make" -C /data/riotbuild/cpu/native/netdev2_tap
"make" -C /data/riotbuild/cpu/native/periph
"make" -C /data/riotbuild/drivers
"make" -C /data/riotbuild/drivers/netdev2_eth
"make" -C /data/riotbuild/drivers/saul
"make" -C /data/riotbuild/sys
"make" -C /data/riotbuild/sys/auto_init
"make" -C /data/riotbuild/sys/auto_init/netif
"make" -C /data/riotbuild/sys/auto_init/saul
"make" -C /data/riotbuild/sys/fmt
"make" -C /data/riotbuild/sys/net/crosslayer/netopt
"make" -C /data/riotbuild/sys/net/gnrc
"make" -C /data/riotbuild/sys/net/gnrc/netapi
"make" -C /data/riotbuild/sys/net/gnrc/netif
"make" -C /data/riotbuild/sys/net/gnrc/netif/hdr
"make" -C /data/riotbuild/sys/net/gnrc/netreg
"make" -C /data/riotbuild/sys/net/gnrc/pkt
"make" -C /data/riotbuild/sys/net/gnrc/pktbuf_static
"make" -C /data/riotbuild/sys/net/gnrc/pktdump
"make" -C /data/riotbuild/sys/net/gnrc/link_layer/netdev2
"make" -C /data/riotbuild/sys/od
"make" -C /data/riotbuild/sys/phydat
"make" -C /data/riotbuild/sys/ps
"make" -C /data/riotbuild/sys/saul_reg
"make" -C /data/riotbuild/sys/shell
"make" -C /data/riotbuild/sys/shell/commands
   text   data    bss    dec    hex filename
  75797    588  72016 148401  243b1 /data/riotbuild/examples/default/bin/native/default.elf
make: Leaving directory '/data/riotbuild/examples/default'

./dist/tools/tapsetup/tapsetup
creating tapbr0
[sudo] password for chyi:
creating tap0
creating tap1
$ ./examples/default/bin/native/default.elf tap0
  => 앞서 build한 결과 파일(default.elf)를 실행 !
  => target board가 없는 경우에는 RIOS OS s/w를 이런 식으로 개발할 수 있다.
RIOT native interrupts/signals initialized.
LED_RED_OFF
LED_GREEN_ON
RIOT native board initialized.
RIOT native hardware initialization complete.

main(): This is RIOT! (Version: 2017.04-devel-153-ge38329e-330fdc5e09a5)
Native RTC initialized.
Welcome to RIOT!

> help
help
Command              Description
---------------------------------------
reboot               Reboot the node
ps                   Prints information about running threads.
rtc                  control RTC peripheral interface
ifconfig             Configure network interfaces
txtsnd               Sends a custom string as is over the link layer
saul                 interact with sensors and actuators using SAUL

  => 잠시 후, 아래 packet이 수신됨 ...
PKTDUMP: data received:
~~ SNIP  0 - size:  88 byte, type: NETTYPE_UNDEF (0)
000000 60 0f f5 44 00 30 11 01 fe 80 00 00 00 00 00 00
000010 cc 5f e5 ff fe a4 e1 75 ff 02 00 00 00 00 00 00
000020 00 00 00 00 00 00 00 fb 14 e9 14 e9 00 30 fa aa
000030 00 00 00 00 00 01 00 00 00 00 00 00 0b 5f 67 6f
000040 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f
000050 63 61 6c 00 00 0c 00 01
~~ SNIP  1 - size:  20 byte, type: NETTYPE_NETIF (-1)
if_pid: 4  rssi: 0  lqi: 0
flags: 0x0
src_l2addr: ce:5f:e5:a4:e1:75
dst_l2addr: 33:33:00:00:00:fb
~~ PKT    -  2 snips, total size: 108 byte
PKTDUMP: data received:
~~ SNIP  0 - size:  88 byte, type: NETTYPE_UNDEF (0)
000000 60 0f f5 44 00 30 11 01 fe 80 00 00 00 00 00 00
000010 cc 5f e5 ff fe a4 e1 75 ff 02 00 00 00 00 00 00
000020 00 00 00 00 00 00 00 fb 14 e9 14 e9 00 30 fa aa
000030 00 00 00 00 00 01 00 00 00 00 00 00 0b 5f 67 6f
000040 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f
000050 63 61 6c 00 00 0c 00 01
~~ SNIP  1 - size:  20 byte, type: NETTYPE_NETIF (-1)
if_pid: 4  rssi: 0  lqi: 0
flags: 0x0
src_l2addr: ce:5f:e5:a4:e1:75
dst_l2addr: 33:33:00:00:00:fb
~~ PKT    -  2 snips, total size: 108 byte
PKTDUMP: data received:
~~ SNIP  0 - size:  88 byte, type: NETTYPE_UNDEF (0)
000000 60 0f f5 44 00 30 11 01 fe 80 00 00 00 00 00 00
000010 cc 5f e5 ff fe a4 e1 75 ff 02 00 00 00 00 00 00
000020 00 00 00 00 00 00 00 fb 14 e9 14 e9 00 30 fa aa
000030 00 00 00 00 00 01 00 00 00 00 00 00 0b 5f 67 6f
000040 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f
000050 63 61 6c 00 00 0c 00 01
~~ SNIP  1 - size:  20 byte, type: NETTYPE_NETIF (-1)
if_pid: 4  rssi: 0  lqi: 0
flags: 0x0
src_l2addr: ce:5f:e5:a4:e1:75
dst_l2addr: 33:33:00:00:00:fb
~~ PKT    -  2 snips, total size: 108 byte
PKTDUMP: data received:
~~ SNIP  0 - size:  88 byte, type: NETTYPE_UNDEF (0)
000000 60 0f f5 44 00 30 11 01 fe 80 00 00 00 00 00 00
000010 cc 5f e5 ff fe a4 e1 75 ff 02 00 00 00 00 00 00
000020 00 00 00 00 00 00 00 fb 14 e9 14 e9 00 30 fa aa
000030 00 00 00 00 00 01 00 00 00 00 00 00 0b 5f 67 6f
000040 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f
000050 63 61 6c 00 00 0c 00 01
~~ SNIP  1 - size:  20 byte, type: NETTYPE_NETIF (-1)
if_pid: 4  rssi: 0  lqi: 0
flags: 0x0
src_l2addr: ce:5f:e5:a4:e1:75
dst_l2addr: 33:33:00:00:00:fb
~~ PKT    -  2 snips, total size: 108 byte
PKTDUMP: data received:
~~ SNIP  0 - size:  88 byte, type: NETTYPE_UNDEF (0)
000000 60 0f f5 44 00 30 11 01 fe 80 00 00 00 00 00 00
000010 cc 5f e5 ff fe a4 e1 75 ff 02 00 00 00 00 00 00
000020 00 00 00 00 00 00 00 fb 14 e9 14 e9 00 30 fa aa
000030 00 00 00 00 00 01 00 00 00 00 00 00 0b 5f 67 6f
000040 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f
000050 63 61 6c 00 00 0c 00 01
~~ SNIP  1 - size:  20 byte, type: NETTYPE_NETIF (-1)
if_pid: 4  rssi: 0  lqi: 0
flags: 0x0
src_l2addr: ce:5f:e5:a4:e1:75
dst_l2addr: 33:33:00:00:00:fb
~~ PKT    -  2 snips, total size: 108 byte
PKTDUMP: data received:
~~ SNIP  0 - size:  88 byte, type: NETTYPE_UNDEF (0)
000000 60 0f f5 44 00 30 11 01 fe 80 00 00 00 00 00 00
000010 cc 5f e5 ff fe a4 e1 75 ff 02 00 00 00 00 00 00
000020 00 00 00 00 00 00 00 fb 14 e9 14 e9 00 30 fa aa
000030 00 00 00 00 00 01 00 00 00 00 00 00 0b 5f 67 6f
000040 6f 67 6c 65 63 61 73 74 04 5f 74 63 70 05 6c 6f
000050 63 61 6c 00 00 0c 00 01
~~ SNIP  1 - size:  20 byte, type: NETTYPE_NETIF (-1)
if_pid: 4  rssi: 0  lqi: 0
flags: 0x0
src_l2addr: ce:5f:e5:a4:e1:75
dst_l2addr: 33:33:00:00:00:fb
~~ PKT    -  2 snips, total size: 108 byte

3. RIOT OS sample code 소개 
이번 절에서는 RIOT OS sample code 중 하나를 택하여, RIOT OS가 어떤 형태로 구현되는지를 확인해 보고자 한다.

----------------------------------------------------------------------------------------------------------------------------------------------------
<ipc_pingpong/main.c> /* 마치 linux에서 thread application을 만드는 것과 유사함 */
/*
 * Copyright (C) 2014 Freie Universität Berlin
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @ingroup     examples
 * @{
 *
 * @file
 * @brief       IPC pingpong application
 *
 * @author      Kaspar Schleiser <kaspar@schleiser.de>
 * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
 *
 * @}
 */

#include <stdio.h>

#include "thread.h"
#include "msg.h"

void *second_thread(void *arg)   /* main thread에서 생성한 두번째 thread(함수) */
{
    (void) arg;

    printf("2nd thread started, pid: %" PRIkernel_pid "\n", thread_getpid());
    msg_t m;  /* message 변수를 하나 선언한다 */

    while (1) {
        msg_receive(&m);   /* main thread에서 오는 message를 수신한다 */
        printf("2nd: Got msg from %" PRIkernel_pid "\n", m.sender_pid);
        m.content.value++;
        msg_reply(&m, &m);  /* main thread에게 응답 message를 보낸다 */
    }

    return NULL;
}

char second_thread_stack[THREAD_STACKSIZE_MAIN];

int main(void)   /* main thread */
{
    printf("Starting IPC Ping-pong example...\n");
    printf("1st thread started, pid: %" PRIkernel_pid "\n", thread_getpid());

    msg_t m;   /* message 변수를 하나 선언한다 */

    kernel_pid_t pid = thread_create(second_thread_stack, sizeof(second_thread_stack),
                            THREAD_PRIORITY_MAIN - 1, THREAD_CREATE_STACKTEST,
                            second_thread, NULL, "pong");   /* thread를 하나 만든다 */

    m.content.value = 1;

    while (1) {
        msg_send_receive(&m, &m, pid);   /* 메시지를 다른 thread로 보낸다. 또한 다른 thread로 부터 도착한 메시지를 수신한다 */
        printf("1st: Got msg with content %u\n", (unsigned int)m.content.value);
    }
}
----------------------------------------------------------------------------------------------------------------------------------------------------
<Makefile>
# name of your application
APPLICATION = ipc_pingpong

# If no BOARD is found in the environment, use this default:
BOARD ?= native

# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..

# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
CFLAGS += -DDEVELHELP

# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1

include $(RIOTBASE)/Makefile.include
----------------------------------------------------------------------------------------------------------------------------------------------------

<How to build it>
$ docker run -i -t -u $UID -v $(pwd):/data/riotbuild --env BOARD=native riotbuild make -C examples/ipc_pingpong clean all
make: Entering directory '/data/riotbuild/examples/ipc_pingpong'
Building application "ipc_pingpong" for "native" with MCU "native".

"make" -C /data/riotbuild/boards/native
"make" -C /data/riotbuild/boards/native/drivers
"make" -C /data/riotbuild/core
"make" -C /data/riotbuild/cpu/native
"make" -C /data/riotbuild/cpu/native/periph
"make" -C /data/riotbuild/drivers
"make" -C /data/riotbuild/sys
"make" -C /data/riotbuild/sys/auto_init
   text   data    bss    dec    hex filename
  23137    372  60004  83513  14639 /data/riotbuild/examples/ipc_pingpong/bin/native/ipc_pingpong.elf
make: Leaving directory '/data/riotbuild/examples/ipc_pingpong'

<How to run it>
$ ./examples/ipc_pingpong/bin/native/ipc_pingpong.elf tap0
This is RIOT! (Version: xxx)
kernel_init(): jumping into first task...
Starting IPC Ping-pong example...
1st thread started, pid: 1
2nd thread started, pid: 2
2nd: Got msg from 1
1st: Got msg with content 2
2nd: Got msg from 1
1st: Got msg with content 3
2nd: Got msg from 1
1st: Got msg with content 4
2nd: Got msg from 1
1st: Got msg with content 5
2nd: Got msg from 1
1st: Got msg with content 6
2nd: Got msg from 1
1st: Got msg with content 7
2nd: Got msg from 1
1st: Got msg with content 8
2nd: Got msg from 1
1st: Got msg with content 9
2nd: Got msg from 1
1st: Got msg with content 10

4. Nucleo F103 보드에서의 RIOT OS 동작 시험
이번 절에서는 실제 target 보드인 Nucleo F103(MCU: STM32F103RB)에서 RIOT OS 코드를 실제로 돌려 보는 실습을 진행하도록 하겠다. Nucleo F103 보드는 ST-Link V2-1 debugger(programmer)가 내장(아래 그림 4.1에서 MCU part와 ST-Link V2-1 part가 구분되어 있다)되어 있어, 별도로 ST-Link V2-1 programmer를 구입할 필요가 없다. 뿐만 아니라 USB port를 통한 console 출력이 가능한 장점이 있다(즉, 별도의 serial cable이 필요치 않다).

그림 4.1 Nucleo F103RB 보드(ST-Link V2-1이 on board되어 있음 - 그림 상단 부분)

그림 4.2 Nucleo F103RB 보드 block diagram

그림 4.3 Nucleo F103RB Top layout


그림 4.4 Nucleo F103RB 보드 주요 Spec

그림 4.5 Nucleo F103RB 보드 external pinout

자, 그럼 먼저 docker 환경을 기반으로 nucleo f103 보드에 맞게 cross compile을 진행해 보도록 하자. 테스트에 사용할 example program은 앞서 3절에서 살펴 본 ipc_pingpong이 되겠다.

<How to build it>
$ docker run -i -t -u $UID -v $(pwd):/data/riotbuild --env BOARD=nucleo-f103 riotbuild make -C examples/ipc_pingpong clean all
  => clean build를 진행한다.
make: Entering directory '/data/riotbuild/examples/ipc_pingpong'
Building application "ipc_pingpong" for "nucleo-f103" with MCU "stm32f1".

"make" -C /data/riotbuild/boards/nucleo-f103
"make" -C /data/riotbuild/core
"make" -C /data/riotbuild/cpu/stm32f1
"make" -C /data/riotbuild/cpu/cortexm_common
"make" -C /data/riotbuild/cpu/cortexm_common/periph
"make" -C /data/riotbuild/cpu/stm32_common
"make" -C /data/riotbuild/cpu/stm32_common/periph
"make" -C /data/riotbuild/cpu/stm32f1/periph
"make" -C /data/riotbuild/drivers
"make" -C /data/riotbuild/drivers/periph_common
"make" -C /data/riotbuild/sys
"make" -C /data/riotbuild/sys/auto_init
"make" -C /data/riotbuild/sys/isrpipe
"make" -C /data/riotbuild/sys/newlib
"make" -C /data/riotbuild/sys/pm_layered
"make" -C /data/riotbuild/sys/tsrb
"make" -C /data/riotbuild/sys/uart_stdio
   text   data    bss    dec    hex filename
   9324    136   4292  13752   35b8 /data/riotbuild/examples/ipc_pingpong/bin/nucleo-f103/ipc_pingpong.elf
make: Leaving directory '/data/riotbuild/examples/ipc_pingpong'
  => build 결과, ipc_pingpong.elf, ipc_pingpong.hex 파일 등이 생성된다.


ST-Link/V2-1 programmer를 통해 Nucleo F103 보드(정확히는 flash memory)에 code를 올리기(flash writing) 위해서는 OpenOCD를 사용(Atollic TrueSTUDIO, IAR EWARM, Keil MDK-ARM, TASKING VX-toolset 등을 사용할 수도 있겠으나, 여기서는 OpenOCD를 사용해 보기로 하겠음) 해야 한다. 필자의 경우는 docker 환경을 사용하고 있는 관계로, openocd를 docker 환경에 직접 설치해 주어야만 했다(Ubuntu Host에 openocd를 설치해야 하는 것으로 생각하고 테스트해 보았으나, 동작에 문제가 있었음).

참고) OpenOCD는 open source JTAG debugging S/W로 고가의 debugging tool을 대신할 수 있는 훌륭한 도구이다.

<How to setup the OpenOCD>
  => RIOT OS Dockerfile에는 OpenOCD가 포함되어 있지 않으므로 직접 설치를 진행하도록 하자.

docker run -i -t -u root riotbuild /bin/bash
  => docker OS(Ubuntu 14.04)로 진입한다.
# apt-get update
# apt-get -y install build-essential autoconf automake libtool libusb-dev libusb-1.0-0-dev libhidapi-dev
  => 필요에 따라서는 위의 패키지를 설치해 주어야 한다.
# apt-get -y install openocd
  => openocd package를 설치해 준다.
# openocd --version
  => openocd 0.9.0 version이 설치되었다(참고: recent version은 0.10.0이지만, 테스트에는 전혀 문제가 없음)
Open On-Chip Debugger 0.9.0 (2015-09-02-10:42)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
# exit
$ docker commit -m "add openocd" -a "Chunghan Yi" 976136ceb63e riotbuild
sha256:d3281c459278304f0b279f41586dfe8d495ef349482a224f9cd7562c871caa86
  => 지금까지 수정한 사항(docker 상에서 수정한 내용)을 반영(commit)하도록 한다.

여기서 잠깐 ! openocd를 Host OS에서 직접 제어하려면 ....
$ sudo openocd -f interface/stlink-v2-1.cfg -f target/stm32f1x.cfg 

그림 4.6 Host PC에서 openocd 실행 모습

<다른 terminal>
$ telnet localhost 4444

그림 4.7 telnet으로 openocd 제어하는 모습
---------------------------------------------------------------------------------------------------------------------------------------

<How to flash it with OpenOCD>
$ docker run -i -t -u $UID -v $(pwd):/data/riotbuild --env BOARD=nucleo-f103 riotbuild make -C examples/ipc_pingpong flash
   => 이 경우, "Error: libusb_open() failed with LIBUSB_ERROR_NO_DEVICE" 에러가 발생하였고, 여러가지 해결책을 강구해 보았으나, 여유치 않았음.

BOARD=nucleo-f103 make -C examples/ipc_pingpong flash
   => flash 명령의 경우는 docker 환경이 굳이 필요 없으므로, Ubuntu Host(16.04)에서 직접 진행하도록 하자.
make: 디렉터리 '/home/chyi/IoT/RIOT_OS/RIOT/examples/ipc_pingpong' 들어감
Building application "ipc_pingpong" for "nucleo-f103" with MCU "stm32f1".

"make" -C /home/chyi/IoT/RIOT_OS/RIOT/boards/nucleo-f103
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/core
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/cpu/stm32f1
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/cpu/cortexm_common
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/cpu/cortexm_common/periph
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/cpu/stm32_common
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/cpu/stm32_common/periph
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/cpu/stm32f1/periph
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/drivers
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/drivers/periph_common
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/sys
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/sys/auto_init
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/sys/isrpipe
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/sys/newlib
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/sys/pm_layered
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/sys/tsrb
"make" -C /home/chyi/IoT/RIOT_OS/RIOT/sys/uart_stdio
   text   data    bss    dec    hex filename
   9276    136   4292  13704   3588 /home/chyi/IoT/RIOT_OS/RIOT/examples/ipc_pingpong/bin/nucleo-f103/ipc_pingpong.elf
/home/chyi/IoT/RIOT_OS/RIOT/dist/tools/openocd/openocd.sh flash
### Flashing Target ###
Open On-Chip Debugger 0.9.0 (2015-09-02-10:42)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
none separate
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v28 API v2 SWIM v17 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 3.246781
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
    TargetName         Type       Endian TapName            State       
--  ------------------ ---------- ------ ------------------ ------------
 0* stm32f1x.cpu       hla_target little stm32f1x.cpu       running
target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x080003f0 msp: 0x20000200
auto erase enabled
Info : device id = 0x20036410
Info : flash size = 128kbytes
target state: halted
target halted due to breakpoint, current mode: Thread 
xPSR: 0x61000000 pc: 0x2000003a msp: 0x20000200
wrote 10240 bytes from file /home/chyi/IoT/RIOT_OS/RIOT/examples/ipc_pingpong/bin/nucleo-f103/ipc_pingpong.hex in 0.609044s (16.419 KiB/s)
target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x080003f4 msp: 0x20000200
target state: halted
target halted due to breakpoint, current mode: Thread 
xPSR: 0x61000000 pc: 0x2000002e msp: 0x20000200
verified 9412 bytes in 0.151603s (60.628 KiB/s)
shutdown command invoked
Done flashing
make: 디렉터리 '/home/chyi/IoT/RIOT_OS/RIOT/examples/ipc_pingpong' 나감
  => 정상적으로 flashing(image writing to flash memory)이 진행되었다.

<pyterm으로 동작 확인하기>
  => RIOT OS는 python으로 작성된 pyterm이라는 terminal emulator를 자체적으로 제공한다.
sudo pip install pyserial
  => pyterm 실행 시 문제(import시 serial이 없다는 에러 발생)가 있어 설치를 진행함.

$ BOARD=nucleo-f103 make -C examples/ipc_pingpong term -p /dev/ttyACM0 -b 115200
  => /dev/ttyUSB0가 아님에 주의^^
  => 그림 4.9처럼 debug message가 console로 출력되고 있음.
  => $ ./pyterm -p /dev/ttyACM0 -b 115200 이렇게 해도 됨.


그림 4.8 dmesg 내용 - /dev/ttyACM0 관련 확인


그림 4.9 pyterm 실행 모습

<gdb로 debugging하기>
$ BOARD=nucleo-f103 make -C examples/ipc_pingpong debug

그림 4.10 gdb로 debugging하는 모습 

참고 사항: gdb로 debugging이 가능하다는 얘기는 eclipse로도 가능하다는 얘기가 됨.

아래 그림은 ipc_pingpong이 동작 중인 Nucleo F103RB 보드의 모습을 보여준다.

그림 4.11 Nuceo F103RB 보드 - ipc_pingpong program으로 실행한 모습


5. Nucleo F103 보드 Boot Flow 분석
이번 절에서는 Nucleo F103 보드의 부팅 코드(보드 및 cpu 초기화, thread 생성 및 main 함수 호출까지의 과정)를 대략적으로 분석해 보고자 한다.
----------------------------------------------------------------------------------------------------------------------------------------------------
ENTRY(reset_handler_default)                ... cpu/cortexm_common/ldscripts/cortexm_base.ld
 |
V
reset_handler_default( )                          ... cpu/cortexm_common/vectors_cortexm.c
{
/* load data section from flash to ram */
for (dst = &_srelocate; dst < &_erelocate; ) {
        *(dst++) = *(src++);
     }
     /* default bss section to zero */
     for (dst = &_szero; dst < &_ezero; ) {
        *(dst++) = 0;
     }

board_init( )                                      ... boards/nucleo-f103/board.c
{
cpu_init( )                                    ... cpu/stm32f1/cpu.c
{
cortexm_init( )
clk_init( )
periph_init( )
}
gpio_init( )
}

     kernel_init( )                                   ... core/kernel_init.c
{
thread_create(idle_stack ... idle_thread ...)   /* idle thread 생성 */
               |
              V
               idle_thread( )
               {
                     while (1) {
                             pm_set_lowest();
                     }
               }

thread_create(main_stack ... main_trampoline ...)    /* main thread 생성 */
              |
             V
              main_trampoline( )           ... core/kernel_init.c
              {
                     main( )                  /* 여기서 main 함수가 호출됨 */
              }

cpu_switch_context_exit( )   .... cpu/atmega_common/thread_arch.c
              |
             V
              {
                     __asm__ volatile (
                     "bl     irq_arch_enable              \n"    /* enable IRQs to make the SVC interrupt is reachable */
                     "svc    #1                                   \n"    /* trigger the SVC interrupt */
                     "unreachable%=:                      \n"    /* this loop is unreachable */
                     "b      unreachable%=               \n"    /* loop indefinitely */
                     :::);
              }
}
}
----------------------------------------------------------------------------------------------------------------------------------------------------

이상의 흐름이 정확한 것인지를 확인하기 위해서는 추후 Eclipse와 JTAG emulator를 사용하여 trace해 보아야 할 것 같다(TBD).


References
1. https://github.com/RIOT-OS/RIOT/wiki
2. http://riot-os.org/api/
3. UM1724 User manual STM32 Nucleo-64 board, www.st.com
4. RM0008 Reference manual STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced ARM®-based 32-bit MCUs, www.st.com
5. UM1075 User manual ST-LINK/V2 in-circuit debugger/programmer for STM8 and STM32, www.st.com
6. The definitive guide to ARM Cortex-M3 and Cortex-M4 processors, 3rd edition, Joseph Yiu


Slowboot

댓글 2개:

  1. RTOS를 처음 접해보고 시도해보려고 하는데, PC 쪽은 무엇을 어떻게 설치해야 하는지요?

    답글삭제
  2. 우선 RIOT OS는 아래 OS만 지원합니다. Windows(PC가 Windows를 말씀하시는 거라면 말이죠^^)는 지원을 안합니다.
    위의 posting 내용은 Ubuntu 16.04(64bit)를 기준으로 설명한 것이며, 아래 지원 OS 내용 중에 Ubuntu 16.04가 포함되어 있지 않은 관계로 docker를 이용하여 Ubuntu 14.04를 설치한 후, 여기에 RIOT OS 개발 환경을 구축하는 방법을 정리한 것입니다.
    지원 OS =>
    Arch Linux
    Debian 7 Wheezy (Stable)
    Ubuntu 13.10 Saucy Salamander
    Ubuntu 14.04 Trusty Tahr
    Ubuntu 14.10 Utopic Unicorn

    OS X 10.8 Mountain Lion
    OS X 10.9 Mavericks
    FreeBSD 10
    -------------------------------------------------------------------------
    만일 위의 OS 중 하나(가령, Ubuntu 14.04) 를 사용할 계획이시라면, 아래 site 내용을 참조(환경 설정 관련)해 보시면 될 것 같습니다.

    https://github.com/RIOT-OS/RIOT/wiki/Family%3A-native

    마지막으로 한가지 더 ....
    RTOS 자체에 관심이 있으신 거라면(RIOT OS는 IoT 용 RTOS임), 아래 내용도 한번 참고해 보시기 바랍니다.

    1. FreeRTOS : http://www.freertos.org/
    => 현재까지 가장 많이 사용하는 open source RTOS(앞으로도 그럴지는 모르겠지만...)
    2. Zephyr Project : https://www.zephyrproject.org/
    => 최근에 Linux Foundation에서 release한 open source RTOS(for IoT)
    => http://slowbootkernelhacks.blogspot.kr/2017/03/zephyr-project.html


    답글삭제