2014/08/31~09/04 터키여행

이번엔 터키를 다녀 왔다.
원래 파리를 가기로 했으나 당일 터키로 갑자기 변경해 출발한 여행이라 아무런 정보가 없어 거의 패닉 상태로 개고생 했다.
일정 때문에 24시간을 깨어 있었던적도 있었다.
말이 안통해 새벽 5시즘엔 앙카라에서 미아가 될뻔도 했다.
그래도 참 재밌었던 여행이다.
외국사람이 한국말로 자기나라를 소개하는 가이드 투어라는것도 처음 해봤다.
노란 풍선을 타고 하늘도 날았다.




































아웃사이더, 어떤이의 꿈 - 봄여름가을겨울





한참 노래방이 유행하던 대학시절,
개강모임, 종강모임 등등으로 얼큰하게 취해 들어가면 목이 터져라 부르던 노래들...
저거 뭔 노래야?... 하는 표정으로 아무도 호응을 해주지 않았지만
나 혼자 열심히 돼지 멱따는 소리로 고성방가를 했었지... 움흐흐...
그래서 친구가 없나?...

간단한 OS 자작 (SOS - Simple OS)


아주 오래전 80C196(16bit)과 TMS320C6416T(32bit) 컨트롤러에 uC/OS-II를 포팅하여 사용한적이 있다. 이번엔 ARM 기반 컨트롤러에 포팅해보려고 자료를 뒤져 봤는데 uC/OS-II가 오래전 유료화 되었댄다.
아... 이 청천벽력 같은 소식을 우째야 하나...
뭐 열심히 만든 SW를 편히 쓰게 해준것 만으로도 감지덕지다.
참 잘만든 SW가 유료화 되는것은 어쩌면 지극히 당연한 것이라 본다.
대신 지금 나에겐 대안이 필요하다.
아주 간단하고 무료로 쓸만한 OS를 찾아보니 요샌 FreeRTOS가 대세인듯 하다.
FreeRTOS 포팅 하려고 자료를 찾던중 문득

'요놈도 나중에 유료화 될려나?...'
'Free를 붙여놨는데 설마...'
'요놈에 의지하다 또 당하면?...'
'그냥 하나 만들어 버릴까?...'

이런 생각이 들었다.

예전에 이런일이 있었다.
MATLAB이라는 아주 좋은 툴에 완전히 의존해 개발을 하다보니 라이센스를 비롯해, 메모리, 저장장치 공간등의 문제로 MATLAB 라이브러리들을 못쓰게 되는 상황이 발생되니 사람이 바보가 되더라.
그 뒤로는 실개발에 필요한 라이브러리들 (FFT해석, 물리엔진 시뮬레이션용 적분기, 필터등등)을 C로 자체 개발해 사용하고 있다.

그런데, 비슷한 상황이 또 발생 되었다.
중대형 프로세서 기반의 Embedded System에서 자유롭게 사용가능한 OS가 필요할 경우 역시 Embedded Linux이다.
여기에 실시간성이 필요할 경우 Embedded Linux-Xenomai가 좋은 솔루션이 될수 있다.
소형 프로세서기반의 Embedded System에서 OS가 필요할 경우 uC/OS를 점찍어 놓고 있었는데 유료화 되면서 다른 대안이 필요한 상황이다.
또 바보가 되지 않기 위해 그냥 하나 만들기로 했다.

우선 소형 Embedded System을 목표로 하는 OS이므로 최대한 간단하게 구성하기로 한다.
OSless 펌웨어로 하기엔 좀 부족하고, OS를 넣자니 거대한 커널과 복잡한 메모리 관리, 태스크 관리, IPC툴등은 부담스러운, 그런 애매한 상황에 필요한 솔루션.
그래서 간단하게 만들어 보기로 했다. 뭐 거창한 OS만들 능력도 안된다. 크크크...
소형 Embedded System은 주로 주기적인 제어가 목적이므로 Periodic Task를 기본 지원하는 커널로 한다 (uC/OS나 FreeRTOS에서 Periodic Task를 구성하기가 좀 까다로운듯 하다).

Periodic Task와 관련하여 부연 설명을 좀 하자면 이렇다.
보통 디지털 제어기를 코딩할때 중요한 것이 바로 샘플링 시간 (Ts)이다.
아날로그 제어기법을 디지털 제어로 변경하면서 필연적으로 이 Ts라는 변수가 생기게 되는데 좋은 제어를 위해서는 이 Ts가 일정하게 유지되는 것이 중요하다.
이 Ts를 일정하게 유지하기 위해 보통 OSless 펌웨어에서는 하드웨어 타이머를 사용하던지 아니면 RTOS를 사용하게 된다.
Ts가 100ms인 Non-Periodic Task를 예로 보면 다음과 같다.

void NonPeriodicTask( void )
{
  for( ;; ) {
    Sensor();
    Control();
    Sleep( 100 );
  }


센서에서 값을 읽고 그 값에 따라 제어기를 돌린다.
그리고 100ms를 쉰다음 같은일을 반복한다.
디지털 Feedback 제어의 가장 단순한 예시이다.
Sensor()함수와 Control()함수를 수행하는데 시간 소모가 제법 있고 이 시간이 항상 일정 하다면 그 시간만큼 뺀 시간 동안 Sleep을 하면 된다.
그러나 보통 그 시간을 측정해 적용하는 일은 아주 드물며(바보같은 일) 수행시간이 매번 변한다면 Ts가 일정치 않아 정확한 제어가 되지 않을 것이다.
이를 위해 Periodic Task가 필요하다.

void PeriodicTask( void )
{
  for( ;; ) {
    Sensor();
    Control();
    SleepPeriod();
  }


void main( void )
{
  ...
  CreateTask( priority=10, period=100, task=PeriodicTask );
  ...
  StartKernel();
}

위와같이 Periodic Task를 커널에서 지원 한다면 Sensor()함수와 Control()함수의 수행시간과 상관 없이 (둘이 합쳐 100ms이내 종료된다는 조건) for루프는 매 100ms마다 반복된다.
커널이 Realtime을 지원 한다면 RTOS가 되는 것이다.
Periodic Task는 얼핏 타이머와 비슷하지만 다른 개념이다.
uC/OS에서 이 Periodic Task를 구현 하려면 커널 타이머와 이벤트, 세마포어등을 결합해 사용해야 한다 (지금은 커널에서 지원하는지 모르겠다).
Linux-Xenomai의 경우 커널 옵션에서 Periodic Task를 지원하는 커널로 설정하여 빌드하면 위와같이 사용 가능하다.

어찌 되었건 Periodic Task를 지원하도록 만들면 제어기 개발시 많이 편리하다.
스케쥴링 방식은 실시간성 보장을 위해 우선순위 기반과 Preemptive 방식으로 구현하고 다른 기능들은 넣지 않는다 (정확히는 넣을 능력이 안된다 크크크). 
기타 OS 커널에서 스케쥴링시 고려되어야 하는 많은 이론적 발생 가능한 문제점들을 응용 개발자에게 모두 떠넘겨 버리면 스케쥴러 구조는 아주 쉬어질 수 있다 움하하.
간단한 예로 우선순위 기반 스케쥴러에서 발생하기 쉬운 Starvation문제는 어플리케이션 개발시 주의하면 충분히 해결되거나 발생되지 않게 만들수 있다.
타겟역시 소형 Embedded System용 이므로 공룡 그림 그려진 OS이론 책에서 설명하는 기능들은 많은 부분이 필요 없다.
Task역시 많아야 10개 내외이고 ISR은 직접 구성하여 사용하면 된다.
태스크간 그리고 태스크-ISR간 동기화에는 공유 메모리와 Flag를 사용하면 소형 Embedded System환경에서의 어플리케이션은 대부분 개발 가능할 것이다.
뮤텍스나 세마포어, 이벤트등도 있으면 좋겠지만 스케쥴러를 최대한 간단하게 구성하기 위해 구현하지 않았다.
뮤텍스와 이벤트를 구현하여 기본 동작하는 것은 확인 하였으나 스케쥴러가 복잡해지고 잠재적 버그를 많이 가지고 있을수 있어 테스트만 해보고 넣지 않았다.
큰 그림으로 보자면 OSless 펌웨어와 OS-based 펌웨어의 어중간한 중간즘?...
커널 구조가 워낙 간단해 하루이틀 정도 코딩만으로 완성 되었다.
테스트는 일전에 Reverse Engineering으로 사용 가능해진 S3C3410(ARM7TDMI) 기반 무선전화기 보드를 활용했다.


[S3C3410(ARM7TDMI) 기반 무선전화기 보드 - 11MHz 동작]

잠재적 버그의 수를 줄이고 재사용 편의를 위해 간결한 코드 유지목적으로 다음 제한사항을 걸어 두었다.

<> 실시간성 확보를 위해 우선순위 기반의 Preemptive 스케쥴러로 구성
<> 필요한 파일수는 헤더 파일(h)과 구현 파일(c) 달랑 2개
<> 위 2개 파일 이외 추가 의존성 없이 구현
<> CPU의존 코드를 커널 파일안에 포함: 커널 관련 코드보다 CPU의존 코드가 더 많음(주객전도)
<> 인터럽트 컨텍스트 스위칭 주기는 1ms로 고정: 시간관리가 편해지고 추가 나눗셈 곱셈 연산이 필요 없어짐
<> 컨텍스트 스위칭 ISR은 여유 레지스터가 많은 FIQ사용

만들어 놓고 보니 OS라는 타이틀을 붙이기에는 너무 심하게 뭔가 없다. 흐흐...


[8개 태스크 동작 시험]

위 그림은 자작한 OS의 실제 동작 시험 결과 이다.
총 8개 태스크를 만들어 동시 구동 시켰으며 이중 우선순위가 가장 낮은 T7 태스크는 Non-Periodic Task로 Idle Task가 된다. 즉, 절대 Sleep이 일어나지 않도록 한다.
T0부터 T5까지는 각각 10ms, 20ms, 40ms, 80ms, 160ms로 두배씩 증가하는 주기로 설정하여 내부 카운터를 증가 시키고, T6는 200ms주기로 위 상태 정보 문자열을 출력한다.
T7은 Idle Task로 남는 시간동안 CPU 사용률을 계산한다.
위 출력 메시지의 맨 좌측값이 시스템 시간으로 1ms단위로 측정된다.
태스크 카운터 값을 보면 T0의 경우 시스템 타임의 1/10로 증가되며 나머지 태스크는 각 1/2씩 작은 값으로 증가되어 설정한 주기로 모든 태스크가 정확히 수행되는 것을 볼 수 있다.
T6의 경우 메시지 출력을 위해 200ms 주기로 설정되어 있으므로 매 메시지가 200ms 간격으로 출력되고 있는것을 볼 수 있다.
우선순위 기반 스케줄링 이므로 우선순위가 높은 태스크일 수록 실시간성이 잘 보장된다.
Idle Task를 제외한 T6의 우선순위가 가장 낮음에도 커널이 안정된 이후 정확한 200ms 간격을 보여 주고 있다.
해당 보드가 32bit기반 ARM임에도 약 11MHz정도 저속으로 동작하는데 태스크 8개와 1ms 스케쥴러에 약 20%정도 CPU사용량을 보인다. 

아직 실 어플리케이션에 적용한게 없어 어떤 상황에서 어떤 버그가 생겨날지 모른다.
추후 사용해 가면서 다듬어 봐야 겠다.

OS기반의 소규모 펌웨어 개발에 필요한 솔루션이 얼추 만들져 기쁘다.
의존성이 없어 아주 홀가분하기도 하다. 움하하...


<> 2014/08/21 내용 추가
CPU 사용률 측정에 버그가 있었다.
위 이미지에서는 11MHz S3C3410(ARM7) CPU 사용률이 약 20%정도이나 버그 수정후 재측정결과 약 95~96%정도 CPU 사용률을 보여 주었다.
8개 태스크와 시간 소모가 많은 printf()문에다 1ms 컨텍스트 스위칭 주기를 설정했는데 제아무리 32비트 코어라도 11MHz로 동작하는데 20%대는 무리인듯 하다.
추가로 CPU코어가 33MHz로 동작하는 S3C3400(ARM7) 코어에 똑같은 상황에서 측정해 보았는데 CPU 사용률은 약 48~49%대를 보여 주었다.

자작 적외선 리모컨 (무선 전화기 개조)

거실 TV에 PC가 연결되어 있는데 PC로 동영상 볼려면 TV 리모컨과 PC용 적외선 무선 키보드 2개를 조작해야 한다.
PC용 키보드는 파일 선택을 위한 트랙볼과 재생 위치 및 볼륨 조절용 방향키만 주로 사용한다.
이걸 한개로 통합한 리모컨을 만들까 하다가 회로도 그리고 땜질하고 만들기도 귀찮고, 또 자작한 하드웨어는 언제나 마무리(하우징)가 아쉬어 미루고 있었다.
운좋게 적외선 리모컨 기능이 내장된 무선 전화기를 하나 구했다.
LG에서 만든 GP-9138이라는 모델이고 KT ann용 무선 전화기 이다.


[요렇게 생긴 놈이다]
배를 갈라보니 LPC2210이라고 마킹된 메인 컨트롤러가 보인다.
요건 또 뭘까? 구글신께 문의 시작...
메인 컨트롤러는 ARM7TDMI 기반이고 다행히 LCD를 제외한 메인 컨트롤러 및 주변 부품들의 메뉴얼과 데이터 시트를 구할 수 있었다.
컬러 LCD와 전화기용 키패드가 있어 리모컨 만들기에 아주 딱이다.
요걸로 만들면 되겠다 싶어 시작 했다.


[메인 컨트롤러]
우선 컨트롤러의 리셋 벡터와 부트 모드를 확인해 보았다.
JTAG핀을 쓸까 하다가 UART 시리얼 부트모드와 제작사에서 제공하는 'LPC2000 Flash Utility'라는 툴을 이용하면 부트로더 주입이 가능해 보여 이걸로 시작 하기로 했다.
부트모드 변경 및 UART연결을 위한 와이어 작업을 한다.
하는김에 다른 기기들 조작을 위해 리모컨 발신 분석용 IR 수신기도 달아 준다.
수화스피커를 떼낸 공간에 딱 맞게 들어 간다.


[대가리쪽에 달아준 IR 수신기]
[오.. 딱 맞게 들어간다]
필요 없는 RF 및 모뎀 관련 부품들은 소모전류를 줄이기 위해 모두 들어낸다.
LPC2210에는 내장 플래시 메모리가 없어 'LPC2000 Flash Utility'툴 사용이 가능할지 의문이었는데 내부 램 R/W와 내부 램으로 점프도 가능하게 구성되어 있다. 탱크유소우머취.
내부 램에 1차 부트로더를 올리고 외부 플래시 메모리에 2차 부트로더를 버닝하면 된다.
안되면 JTAG을 붙일려고 자작해서 쓰고 있는 USB-JTAG 하드웨어를 옆에 대기 시켜놨는데 철수해도 되겠다. 흐흐흐... 철수중에 철수는 역시 배철수지. 크크크...


[부트모드 변경용 버튼과 리셋버튼 그리고 UART 연결용 케이블 제작]
간단히(?) 부트로더를 만들어 올리고 기본 펌웨어는 백업받아 파일로 저장해 놓는다.
회로 구성상 UART를 전혀 쓰지 않은 것으로 보인다.
터미널 연결도 없이 코딩을 하다니...
아주 좋은 디버깅 하드웨어를 썼거나 아니면 터미널 없이도 개발이 가능한 대단한 개발자 이거나...

PLL, 타이머, 인터럽트, UART, 키패드, LED, LCD 백라이트, 외부메모리 인터페이스등 기본 기능들 테스트를 완료하고 LCD부터 시작.

LCD에 마킹된 문자열로 검색해 봤는데 구글이 모른댄다. 나오는 데이터가 없어 인터넷으로 구할 수 있는 컬러 LCD제어기 칩들을 모아 일일히 메뉴얼대로 구현해 인터페이스 테스트를 해봤다.
안된다. 젠장...
read 스트로브 신호를 쓰지 않아 칩 상태를 읽어 볼 수가 없다.
아 피곤하군...
백업 받은 바이너리 파일을 디스어셈블한다.
LCD에 접근하는 코드를 찾아낸다.
LCD에 접근하는 함수가 약 3개 정도 보이는거 같다.
역시나 LCD상태를 읽어보는 코드는 보이지 않는다.
한개는 LCD 초기화 함수 같고 한개는 초기화시 호출하는 함수 인데 플래시에서 상수를 읽어 LCD레지스터에 쓴다. 뭐하는 함수인지 모르겠다.
나머지는 픽셀제어용 함수인듯 보인다. 4개 인수를 받아 좀 복잡한 수식 계산을 하는데 그래픽 램 어드레스 설정이나 포인터 설정등 뭐 이런거 하는거 같다. 어셈블리로된 수치연산 코드 해석은 골아프다. 일단 스킵...
초기화 함수로 추정되는 코드를 따라 하드 코딩된 8bit 상수 데이터를 뽑아내 초기화 해보니 오우~~ 된다. 나머지 함수들도 C로 구현해 이리 저리 돌려보며 설정할 수 있게 되었다.
이틀간 삽질로 LCD제어가 완료 되었다 (참 쉽죠?).
초기화시 호출하는 함수는 LCD 콘트라스트 설정이고, RGB포멧은 픽셀당 4:4:4 12bit 포멧이다 (2픽셀당 3바이트).
인터페이스 방식이 기존 인터넷에서 구했던 컬러LCD 제어칩들과 완전히 다르다.


[LCD 테스트중]
기본 IR 송신과 수신기능을 테스트하고 기기 기능들을 구현해 넣는다.
동작 모드는 총 3가지로, RC모드 (Remote Control Mode), TX모드 (Transmission Mode), RX (Receive Mode)모드이다.
RC모드는 알고있는 기기들의 송신코드를 미리 넣어 해당 기기를 조작하는 기능을 구현시켰다.
TX모드는 리모컨이 없는 기기에 임의의 IR코드를 주입시켜 동작테스트를 해볼 목적으로 구현해 넣었다.
RX모드는 IR리모컨의 발신 신호를 받아 분석할 수 있게 기능을 구현했다.

사용중인 IR제어 기기들의 코드를 읽어 내장시켰다.

대부분 NEC코딩 방식이라 쉽게 읽어볼 수 있었으나 IR 무선 키보드는 자체 개발 포멧인지 데이터 분석이 불가하여 키별 타이밍값을 하드코딩하여 넣어 버렸다.
기존 장착된 Ni-Cd배터리는 맛이가서 리튬이온 배터리로 달아 줬다.


[맛간 Ni-Cd 배터리를 걷어내고 안쓰는 휴대폰 배터리를 개조해서 넣어줌]
[동작 모드별 LCD화면]
LCD표기는 위 그림의 좌측부터 RC모드, RC모드에서 키보드 선택, TX모드, RX모드에서 수신대기, RX모드에서 수신완료 상태이다.
RX모드에서는 IR수신 완료후 NEC 포멧으로 분석하여 헤더 타이밍, 8bit-ID, 8bit-DATA값을 표기해 주도록 하였다. 나머지 수치는 수신된 타이밍값을 us단위로 모두 표기해 NEC포멧이 아닐 경우 수동으로 해독할 수 있게 했다.
소프트파워 버튼이 하드웨어로 구현되어 있어 일정시간내 키 조작이 없을 경우 자동 OFF시켜 꽤 오래 쓸 수 있을듯 하다.


[최종 완성]

어느날...

보고만 있어도 기분이 좋아지는 사람, 그리고 사람들...
가족...

Embedded Linux-Xenomai RTOS with Realtime Ethernet Control Networking

또 간만에 블로그 새글이 올라가는군...
이번 글 제목이 아주 거창하다.
한물간 PXA270 프로세서에 실시간 운영체제인 Linux-Xenomai를 포팅하고 거기에 실시간 Ethernet 디바이스 드라이버 (RTDM - Realtime Driver Model/Module)를 올려 Ethernet을 이용한 제어 네트워크를 구성해 보았다.

[Let me publish this thread in English also because I've gathered a lot of useful information from the English web pages.
Please be patient with my lack of English.
In this thread, I'll introduce the possibility of using Ethernet as control network. And a little experimental result will be shown at the end of this text.]

RS-232/422/485, CAN, USB, IEEE1394, Ethernet등 통신 네트워크를 펌웨어 레벨에서 코딩을 해보다 보니 제어용 네트워크를 구성하기 위한 각각의 장단점들이 어느정도 눈에 들어 온다.

우선 RS-232/422/485 (흔히 UART)계열은 심플하지만 물리 레벨에서 에러에 대응하기가 힘들고 속도 또한 최대 수Mbps까지만 가능하다. 트랜시버 칩들 사양을 보면 수Mbps가 가능하지만 UART 제어기의 한계로 인해 보통 수백Kbps만 주로 사용된다.

구현이 간단하고 MODBUS등 상위 프로토콜 스택을 올려 몇몇 서보모터에 사용된다.

CAN통신은 제어 네트워크 구성에 최적화 되어있다.

이름도 Control Area Network다.
패킷자체가 물리레벨에서 노드들간에 중재(Arbitration)가 이루어 지도록 만들었다.
우선순위가 높은 ID를 갖는 패킷이 하드웨어단에서 가장 먼저 전송되도록 구성된다.
고안해낸 기술자들이 정말 대단하다는 말밖엔...
에러에 강인하고 통신거리가 길게는 수Km대나 된다.
왠만한 8/16/32bit 컨드롤러에 UART처럼 내장되어 싼값에 사용할 수 있는 장점이 있다.
다만 최대 전송 속도가 1Mbps라 많이 아쉽다.
서보모터 제어에 아주 많이 사용된다.
전송 속도 한계로 인해 보통 한개 버스에 6~12개 정도 서보모터만 연결해서 사용하므로 다축 제어 네트워크 구성시 채널 수를 늘릴 필요가 발생될 수 있다.

USB는 수십~수백byte의 데이터를 주로 사용하는 제어 네트워크 구성에 몇가지 애로사항이 있다.

우선 너무 복잡하다. 수많은 디스크립터들...
USB디바이스 드라이버나 USB클라이언트용 펌웨어를 코딩해보신 분이라면 잘 아실 것이다.
디스크립터만 보다 시간 다 보낸다...
디스크립터 찍어 볼려고 printk() 만 주구장창 토나오게 코딩했던 기억이 있다.
핸드 쉐이킹에 사용되는 패킷이 많아 제어용 네트워크 보다 대량의 데이터 전송에 적합하다.
또한 호스트용 제어기와 클라이언트용 제어기가 달라 부품수 한개, 펌웨어 코딩 종류 한개라도 줄이려는 개발자에겐 부담스럽다.

IEEE1394는 참 잘만들었다. 32bit 기반으로 설계되어 32bit 프로세서에 아주 좋다.

프로토콜 특성상 핸드쉐이킹 오버헤드가 적어 480Mbps의 USB2.0 보다 400Mbps의 IEEE1394a의 성능이 훨씬더 좋다.
USB와 라이벌(?) 관계였으나 어느순간 일반 시장에서 사라져 버렸다.
왜일까?
우선 개발자 입장에서 보면 1394관련 문서는 돈주고 사야된다.
무료로 배포되는 USB와 다르다.
IEEE1394 펌웨어 코딩하려고 자료들좀 뒤져볼라 치면 회원 가입해야 하고 돈 내라고 하고...
짜증이 몰려온다. 여간 불편한게 아니었다. 당연히 USB로 가는수 밖에...
1394 컨트롤러도 제한적이고 구하기 쉽지 않으며, 그러다 보니 단가가 쎄다.
군용 항공기 표준으로 사용되는듯 하다. 헝그리 개발자는 자료 구하기가 힘들다.

이밖에 1553B가 있으나 1Mbps가 최대이고 군용이라 제어기 구하기 쉽지않다.

제어기도 엄청 비싸고 크다. 군용 항공기에서 오랬동안 써왔기 때문에 검증됐다는거 말고는 별 매력이 없다.
라인 연결시에도 노드별 신호 분배기(Stub) 같은걸 계속 달아줘야 하는 참 거추장스러운 네트워크이다.

Ethernet 프레임을 이용한 EtherCAT이 있는데 호스트(Master)측은 일반 이더넷 컨트롤러를 사용해도 되지만 노드(Slave)측에는 별도의 전용 하드웨어 칩이 필요하다.
OS기반으로 돌아가는 서보모터에 일부 달려나온다.

[I've programming experience with some serial network, UART(RS-232/422/485), CAN, USB, IEEE1394, Ethernet at the firmware level for manipulation of the registers and raw packets.
So, I could make my own view to apply some well known serial network to the real-time control.

Advantages and disadvantages for using real-time control network

UART
 - ADVT: simple, low cost
 - DISA: rack of hardware error robustness, low speed (up to X[Mbps])
CAN
 - ADVT: this was born for control network, low cost
 - DISA: low speed (up to X[Mbps])
USB
 - ADVT: abundant, high speed
 - DISA: complex(so many descripters), seperated to host and client hardware chip, not suitable for control network because of lot of handshaking overheads
IEEE1384
 - ADVT: high speed, well organized
 - DISA: exclusive (you may pay some money just to read the protocol specification), expensive
EtherCAT
 - ADVT: designed for control network, high speed
 - DISA: special hardware chip for client device is needed
Ethernet (this is not mean the TCP/IP)
 - ADVT: abundant, high speed, little simple, low cost
 - DISA: not suitable for control network but can be overcome

And there are so many serial protocols such as 1553B, SPI, I2C, LIN and so on for local area networking.
But I choosed the Ethernet because of abundant, high speed, little simple and low cost.
The Ethernet is not desined for real-time control network.
Back pressure, PAUSE, Jamming packet generation features are crashing the predictive charater of the packet transmission.
But I think I can overcome in some condition.

 - Use as full-duplex
 - Host-Node organization (Only Host can arbitrating the whole the buses)
 - Disable the auto generation of the Back-pressure, Jamming and PAUSE frame (This could be in Ethernet device driver by setting the control registers simply)

Ethernet is not Master-Slave network model. But I'll make the only one Ethernet node works as bus master (I'll call this is the Host).
And the others are just Nodes (Slaves). The Nodes are work as passive (send packet just when the Host's request).
In this condition, we could make the Ethernet as predictable.]

이제 이번에 설명할 Ethernet이다.
잘 아시다 시피 원래 Ethernet는 제어용으로 설계된 네트워크가 아니다.
그러나 우선, 아주 흔해 제어기 구하기가 쉽다. 종류와 벤더가 많으니 단가도 싸다.
Gbps까지 나오니 속도도 넘쳐난다. 고속 스위칭 허브도 싸게 구할 수 있다.
랜케이블... 쓰레기통에 버려야 할 많큼 많다.
헝그리 개발자에겐 아주 매력적이다.
다만 Ethernet을 제어용으로 쓰기 위해서는 다음 사항을 유의 해야 한다.

  - Full Duplex 사용: Half Duplex 사용시 JAM신호와 같은 Bus Arbitration 패킷이 
제어에 필수 요소인 예측 가능성을 없애 버린다. 그러나 걱정 할 필요 없다. 요즘 Half Duplex 구성은 구경하기 힘들다.
  - 호스트-노드 (Master-Slave) 구성: Ethernet는 P2P 구성이 기본이다 (모두가 Master). 하지만 제어를 위해 한개 호스트를 지정해 그 호스트가 모든 버스를 제어하도록 한다. 보통 제어 네트워크를 구성하면 제어 알고리즘이 돌아가는 측에 Feedback 루프가 돌고 있으므로 어쩔 수 없이 한개 호스트는 반드시 존재 해야 한다.
  - 호스트: 해당 노드들에 필요한 데이터를 요청하여 읽고 명령을 보낸다.
  - 노드: 호스트의 데이터 요청이 있을때만 패킷을 전송하며 그 외에는 오직 수신만 한다. 명령이 수신되면 해당 명령을 수행한다.
  - Ethernet 레지스터 설정시 Back Pressure나 JAM신호 발생, PAUSE 신호 발생 등 Bus Arbitration 기능은 모두 정지 시킨다.

서론이 참 많이 길었다.

이제 시작...

<> EtherCONN (Ethernet CONtrol Network)

이더넷을 이용한 제어 네트워크 구성 명칭을 나는 'EtherCONN (Ethernet CONtrol Network)' 이라고 부르려고 한다.

[I'll call my project as 'EtherCONN (Ethernet CONtrol Network) and the standard Ethernet frame is used as like bellow.]


[표준 이더넷 및 EtherCONN 프레임 구성]
[Standard and EtherCONN Frame]

이더넷 패킷은 잘 아시는것 처럼 맨 위와 같다.

이더넷 프레임의 데이터를 위 두번째 처럼 구성한다.

[Upper diagram is standard Ethernet frame and lower is my EtherCONN.
You can use your own value for 2byte EtherType/Length(TL) like 0x0601. I used this field as Length and this is IEEE 803.2 Ethernet frame.
The Ethernet's Data field is separated into 3 region. ADDR(4byte address), CTRL(4byte control) and DATA.]

Type/Length 부분은 본인 임의대로 정해 사용하면 된다. 길이를 지정해 IEEE 803.2프레임으로 사용해도 되고 0x0601과 같이 잘 사용되지 않는 Type을 적용해도 된다.
이더넷 프레임의 데이터 부분을 4바이트 주소와 4바이트 제어코드 그리고 128바이트의 데이터로 구분한다. 그러므로 EtherCONN 프레임은 최대 150바이트가 된다.
EtherCONN 노드들 중에는 메모리가 부족한 8bit 기반이 될 수도 있기에 데이터 크기를 128바이트로 제한 했다 (필요에 따라 그 이상 혹은 그 이하로 설정해 사용할 수도 있다).
주소는 가상의 주소로 해당 노드의 가상 주소에 호스트가 읽기 쓰기를 수행 한다.
호스트입장에서 노드는 마치 읽고 쓰기가 가능한 메모리 장치와 같다고 볼 수 있다.
호스트는 xx:xx:xx:xx:xx:xx 맥어드레스를 갖고 있는 노드의 메모리를 읽고 쓰는 것이다.
한번에 움직이는 데이터는 최대 128byte이므로 한개 노드가 호스트에 제공 가능한 가상 메모리 크기는 다음과 같다.

  2^32(4G) x 128byte = 512GB


호스트가 노드의 메모리에 쓰기를 하는지 읽기를 하는지 알려줄 필요가 있으므로 4바이트 제어코드에 담는다. []안의 숫자는 비트 번호이다.


  ctrl[31]   : 0=write, 1=read

  ctrl[30:16]: reserved
  ctrl[15:0] : data length

즉, ctrl의 31비트를 0으로 해서 호스트가 노드에 전송 할경우 데이터 쓰기가 되고 1일 경우 읽기가 되며 이때 노드는 ADDR(4)에서 지정한 주소지의 데이터를 호스트에 보내야 한다.

128바이트 데이터를 모두다 사용 하지 않을 수도 있으므로 ctrl[15:0]에 데이터 크기를 지정하며 1바이트만 주고 받을 수도 있다.
아래 예시를 보면 이해가 좀 쉬울듯 하다.

[The EtherCONN Nodes are work as just like a memory device as seen in Host, so the Host's jobs are just reading and writing data from/to the Nodes.
The ADDR is 4-byte address for memory to access which is existed virtually in the EtherCONN Node.
The 4-byte CTRL has bit-mapped control to specify read or write cycle (packet). Simply, when the 31st bit is zero '0' the packet is write packet (Host->Node or Node->Host), so the receiver should store to his internal memory or perform the pre-promised action by the data in that packet.
When the 31st bit is one '1' in the CTRL the packet is read request. And the receiver of this packet should send a packet back with data specified at the read request packet's address.
Bit 15~0 of the CTRL represent the length of the DATA in byte to read or write.
I've limited the EhterCONN data up to 128-byte because of possibllity of low performance Node based on 8/16bit micro-processor but, you can change this limitation by your own way.

Check the EtherCONN packet example bellow, and you may easily understand.]


[EtherCONN 프레임 예시]

<> 제어 시나리오

간단한 제어 시나리오를 생각해 보자. 호스트는 주기 100ms로 제어 알고리즘을 돌리고 있는것으로 가정한다.

  1. 0ms에는 센서 노드에 읽기 패킷을 보내고 구동 노드(모터,솔레노이드,LED,
스피커,열선 등등)에는 초기값을 쓰기 패킷에 보낸다.
  2. 다음 100ms에는 전에 보낸 읽기 패킷에 의한 센서노드의 응답이 FIFO(링버퍼)에 저장되어 있으므로 센서 값을 FIFO에서 읽어 제어 루틴을 1회 돌린다.
  3. 제어 루틴에서 만들어진 출력 값은 구동 노드에 쓰기 패킷으로 보낸다. 동시에 센서 노드에도 읽기 패킷을 보낸다.
  4. 2번으로 간다.

때에 따라 로봇이나 매니퓰레이터처럼 모든 구동 노드들의 동시구동(Sync/Trigger)이 필요 할 때가 있을 것이다. 이때는 브로드캐스팅 맥어드레스(FF:FF:FF:FF:FF:FF)를 사용하면 간편하게 해결된다.


위 제어 시나리오가 잘 돌기 위해서는 정확한 100ms 실시간 태스크를 돌려줄 OS가 필요하고 EtherCONN 노드는 읽기 패킷을 수신하자 마자 가장 빠르게 호스트에 응답해야 한다.

호스트에 대한 노드의 응답은 보통 OSless 펌웨어 기반으로 인터럽트에 의한 응답이므로 빠르다 (RTOS를 적용 할 경우 사용된 RTOS의 인터럽트 지연시간에 많이 의존할 것이다). 데이터 준비등이 필요하나 for() 루프에 의한 수백 바이트 데이터 복사는 수~수십[us]이내에 끝난다.
중요한것은 호스트의 읽기 패킷이 랜케이블을 타고 스위칭 허브를 지나 노드에 전달 되고 노드의 응답이 다시 랜케이블과 스위칭 허브를 지나 호스트까지 전달 되는 물리적인 시간 지연 (latency) 이다.
이더넷 프레임을 이 제어용으로 사용 했을 경우 그 시간 지연이 알고 싶었으며 그 수치가 충분히 제어 네트워크를 구성 할 만큼으로 나오는지가 참 궁금 했다.

<> Embedded Linux-Xenomai 포팅

우선 실시간 운영체제가 필요하므로 RTOS를 준비 한다.
예전에 작은 휴머노이드 로봇에 Embedded Linux-Xenomai를 포팅해 보행 알고즘을 RT-Task로 구동 시켰었는데 하루 종일 구동시켜도 아주 안정적으로 잘 되었었다. 당시에는 리눅스 커널 2.6대를 포팅해서 사용했었으나 이번에 3.10대 커널 버전으로 업그레이드 하여 포팅 하였다. Xenomai 패치 역시 그 상위 버전을 사용하였으며 2.6대 커널과 좀 달랐다. 
우선 2.6대 커널에서는 Xenomai 2.3대 버전 패치를 사용했었으며 실시간 태스크 시간지연(latency) 측정시 유저레벨, 커널레벨, IRQ레벨의 시간지연에 차이가좀 많았다. 유저레벨의 시간지연이 가장 크고 IRQ레벨이 가작 작았으나 3.10대 커널에서는 Xenomai 2.6대 패치를 사용하고 유저, 커널, IRQ레벨에서 시간지연이 이전 버전에 비해 골고루 비슷하게 나왔다. 최소, 최대간 편차도 많이 줄어 Xenomai 패치 버전이 올라가면서 실시간 안정성도 상당이 좋아졌다는 것을 느꼈다.
참 대단한 양반 들이다.
포팅 버전은 다음과 같다.

  linux-3.10.18

  xenomai-2.6.3

리눅스 계열 실시간 패치에 대해 잠깐 언급을 좀 하자면 실시간 패치에는 RTLinux, RTAI, Montavista, Xenomai등이 있으나 Xenomai 패치를 제외하고 나머지는 모두 상용 버전이다. 사용 할려면 수천만원씩 돈 주고 소스를 사야한다. 빌드된 커널 바이너리만 살경우 단가는 조금 낮다. 물론 돈주니까 기술 지원은 빵빵할거다.
RTLinux인지 RTAI인지, 아니면 둘다인지는 기억이 가물가물한데 무료 버전이 있으나 커널버전 2.4대이다. 아주 오래전에 포팅해본적이 있는데 어느놈이었는지 기억이 가물가물하다. RTLinux(RTAI인가? 아.. 치매끼)가 유료화 되면서 이에 반발하는 커널 패치 개발자 집단이 새로 만든게 Xenomai이다. 때문에 Native Skin의 API들을 보면 RTLinux(RTAI인가?)와 비슷하다.
리눅스 실시간 패치별로 실시간성 성능 측정을 한 논문을 찾아 본적이 있는데 각 패치별 실시간 성능은 비슷비슷하게 잘 나왔던걸로 기억한다.
Xenomai 패치 개발자들은 이렇게 좋은걸 만들어 놓고도 돈도 안받으면서 뭘로 먹고 사는지 모르겠다. 프로그래밍 좋아하는 재벌집 2세들인가?...

다시 본론으로...

사용한 보드는 국내 Falinux라는 회사에서 판매하는 제품이고 EZ-PXA270이라는 보드인데 여기에 포팅을 하였으며 이더넷 컨트롤러로 AX88796B가 사용되었다.
리눅스 커널에 포함된 AX88796 드라이버는 Normal Linux Kernel용 드라이버이므로 실시간 지원을 위해 Xenomai용 RTDM으로 새로 작성해야 한다. 아주 쉽다. 포팅도 정말 쉽다. 응?

툴체인은 crosstool-ng라는 툴을 사용해 만들어 썼다. 이쪽 분야에는 참 대단한 양반들이 넘쳐나는거 같다.
램디스크까지 포함해 포팅 완료된 리눅스 커널의 부팅 메시지는 아래와 같다.

[The Real-time OS is needed in my project.
I ported the embedded Linux-Xenomai on my PXA270 evaluation board.
I've used linux-3.10.18 and xenomai-2.6.3 for RTOS porting.
You should have to make(code) the Real-time Driver Module (RTDM) for Ethernet controller to use in the Xenomai.
In my PXA270 board, AX88796B Ethernet controller is attatched so I coded my real-time device driver for AX88796B which could be loaded at the Xenomai patched Linux kernel.
Bellow message is from my Xenomai patched PXA270 board with RAM-disk.]

-------------------------------------------------------------------------------
Uncompressing Linux... done, booting the kernel.
Booting Linux on physical CPU 0x0
Linux version 3.10.18-xenokang (root@localhost.localdomain) (gcc version 4.7.3 (crosstool-NG 1.19.0)38:29 KST 2014
CPU: XScale-PXA270 [69054117] revision 7 (ARMv5TE), cr=0000397f
CPU: VIVT data cache, VIVT instruction cache
Machine: Falinux EZ-PXA270 development board
Memory policy: ECC disabled, Data cache writeback
Run Mode clock: 208.00MHz (*16)
Turbo Mode clock: 520.00MHz (*2.5, active)
Memory clock: 104.00MHz (/2)
System bus clock: 104.00MHz
Built 1 zonelists in Zone order, mobility grouping on. Total pages: 16256
Kernel command line: console=ttyS2,115200 initrd=0xa2180000,3M ramdisk=8192 root=/dev/ram0
PID hash table entries: 256 (order: -2, 1024 bytes)
Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
Memory: 64MB = 64MB total
Memory: 59512k/59512k available, 6024k reserved, 0K highmem
Virtual kernel memory layout:
     vector  : 0xffff0000 - 0xffff1000 ( 4 kB)
     fixmap  : 0xfff00000 - 0xfffe0000 ( 896 kB)
     vmalloc : 0xc4800000 - 0xff000000 ( 936 MB)
     lowmem  : 0xc0000000 - 0xc4000000 ( 64 MB)
     modules : 0xbf000000 - 0xc0000000 ( 16 MB)
     .text   : 0xc0008000 - 0xc01efecc (1952 kB)
     .init   : 0xc01f0000 - 0xc020817c ( 97 kB)
     .data   : 0xc020a000 - 0xc0223bc0 ( 103 kB)
     .bss    : 0xc0223bc0 - 0xc0249a6c ( 152 kB)
SLUB: HWalign=32, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
Preemptible hierarchical RCU implementation.
NR_IRQS:512 nr_irqs:289 289
sched_clock: 32 bits at 3250kHz, resolution 307ns, wraps every 1321528ms
I-pipe, 3.250 MHz clocksource
Interrupt pipeline (release #1)
Console: colour dummy device 80x30
Calibrating delay loop... 518.55 BogoMIPS (lpj=2592768)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512
CPU: Testing write buffer coherency: ok
Setting up static identity map for 0xc018a0e8 - 0xc018a138
DMA: preallocated 256 KiB pool for atomic coherent allocations
bio: create slab at 0
Switching to clocksource ipipe_tsc
Trying to unpack rootfs image as initramfs...
rootfs image is not initramfs (no cpio magic); looks like an initrd
Freeing initrd memory: 3072K (c2180000 - c2480000)
I-pipe: head domain Xenomai registered.
Xenomai: hal/arm started.
Xenomai: scheduling class idle registered.
Xenomai: scheduling class rt registered.
Xenomai: real-time nucleus v2.6.3 (Lies and Truths) loaded.
Xenomai: starting native API services.
Xenomai: starting RTDM services.
io scheduler noop registered
io scheduler deadline registered
io scheduler cfq registered (default)
pxa2xx-uart.0: ttyS0 at MMIO 0x40100000 (irq = 22) is a UART1
pxa2xx-uart.1: ttyS1 at MMIO 0x40200000 (irq = 21) is a UART2
pxa2xx-uart.2: ttyS2 at MMIO 0x40700000 (irq = 20) is a UART3
console [ttyS2] enabled
brd: module loaded
mousedev: PS/2 mouse device common for all mice
XScale iWMMXt coprocessor detected.
RAMDISK: gzip image found at block 0
VFS: Mounted root (ext2 filesystem) on device 1:0.
Freeing unused kernel memory: 96K (c01f0000 - c0208000)
init started: BusyBox v1.22.1 (2014-05-12 11:39:31 KST)
starting pid 20, tty '': '/etc/init.d/rcS'

Please press Enter to activate this console.
starting pid 27, tty '': '-/bin/sh'
[root@pxa270 /]#

-------------------------------------------------------------------------------

<> 노드 구성

우선 초기 시험용 노드는 윈도우의 MFC로 개발하였으며 이더넷 프레임 엑세스를 위해 WinCAP설치가 필요하다. 윈도우 OS는 비실시간 커널이므로 노드를 구성하기에 적합하지 않지만 일단 리소스가 좋으므로 빠르게 코딩하여 확인해 볼 수 있다.
실제 시나리오와 비슷한 상황을 만들기 위해 일전에 이더넷이 되는 ARM9 기반의 KS8695 임베디드 보드에 EtherCONN 노드 기능을 구현하여 시험을 진행 했다.
스위칭 허브는 ipTime의 H205를 사용해 구성 하였다.
모두 100Mbps에 Full Duplex로 일반 랜케이블 연결이다. 


[좌측부터 PXA270 호스트, 스위칭 허브, KS8695 노드]
[PXA270 Host, Switching HUB, KS8695 Node]


[호스트 및 노드 연결도]
[Connection Diagram]

[For the fist test, I buildup the Node on the Windows PC with Microsoft MFC. It's just for code implementation test, so the finished code was ported into the ARM9 based KS8695 Ethernet application processor for the real-time response.]

<> 시험 결과
정말정말 궁금했다.
우선 노드를 Windows 7 (x64)과 Windows XP에서 소프트웨어로 구현해 호스트 읽기 패킷에 대한 응답의 시간 지연(latency)을 측정해 봤다.


[인텔 Xeon CPU가 탑재된 Windows 7 64bit 시간 지연]
[Packet Latency with Node on Win7 x64 with Intel Xeon] 


[인텔 i5 CPU가 탑재된 Windows XP 시간 지연]
[Packet Latency with Node on WinXP with Intel i5]


[인텔 i5 CPU가 탑재된 Windows XP 시간 지연 - 허브없이 연결]
[Packet Latency with Node on WinXP - without HUB]


[KS8695 OSless 펌웨어]
[Packet Latency with Node on KS8695 OSless Firmware]

<> 결론

Windows 7의 시스템 성능이 좋아서 인지 응답이 조금 빠르다. 응답이 빠르다고 해서 좋은게 아니고 일정한 값, 즉 예측이 가능해야 한다. 그러나 보면 알겠지만 지연시간이 들쭉날쭉하다. 더군다나 시간지연이 심하게 튀는 경우도 있다 (예측 불가 시스템, 즉 비실시간 OS라는 것을 반증한다).
만약 시험중 메모리 스와핑이 일어난다면 더 심할 수도 있을 것이다.
위 그래프에 나오지는 않지만 수십분이상 장시간 구동시 제어 시퀸스가 깨질정도로 지연이 발생 되었다.
Windows XP에서는 허브를 사용한것과 허브 없이 바로 연결해 허브에 의한 영향을 보았으나 Look-up Table에 의한 하드웨어 스위칭이므로 큰 영향은 없는 것으로 보인다.
내가 알고 싶은 데이터는 마지막 그래프이다. OSless로 돌아가는 KS8695 컨트롤러가 호스트(PXA270)의 요청에 의해 250[us]이내 응답을 보여 준다.
그리고 최소, 최대간 수치가 수십[us] 내에서 일정하고 예측가능해 보인다. 즉 실시간 응답이라는 뜻이다.
KS8659의 EtherCONN 응답처리는 인터럽트 기반으로 가장 빠르게 응답 할 수 있도록 구성했다.
물론 수십시간-수일-수년 구동시의 반복성과 노드의 숫자, 노드에 적용된 CPU 자체 부하량등 다른 요인에 의한 변동 가능성도 검증이 필요하지만 수십ms의 실시간 태스크를 이용한 제어 네트워크 구성에 이더넷은 큰 문제 없이 사용될 수 있을 것으로 보여진다.
시간이 된다면 RTOS끼리 EtherCONN 데이터를 주고 받는 같은 실험을 해봤으면 좋겠다.

[Last graph represent the propagation delay (Latency) measured in EtherCONN Host(PXA270).
The 100ms real-time task is running on Linux-Xenomai and sends the read request packet to the KS8695. The KS8695 processor sends back to the EtherCONN Host as soon as recognize the Host's read request packet at the interrupt level.
My Ethernet real-time driver measures time between sending and receive.
The experimental result shows that the response latencies are varied just in XX[us]. It's means that the propagation delay is predictable and can be apply to real-time control network.
Yeah it's needed more specific test, the propagation delay could be change with respect to the number of Nodes, Node CPU's load, operation time and so on.
But in my opinion with this experimental results, The Ethernet is good enough for building real-time control network with some conditions.]

Extreme Ways - Moby



플레이를 누르시기 전에 앰프 BASS 노브는 끝까지...