2007년 6월 22일 금요일

Do you enjoy reading a novel?

쉬어가는 의미로 한번 가볍게 읽어주세요.

흔히 영어를 능숙하게 구사하는 분들을 보면 우리말 역시 세련되게 하는 것을 볼수 있습니다. 영어에서 같은 표현이 나오면 동일한 단어를 피하고 유사한 단어를 사용, 다양한 어휘력을 바탕으로 동일한 것을 재미있고 자연스럽게 이해할수 있도록 말을 하는 것을 볼수 있습니다. 그런데 이른바 프로그래머라고 일컬어지는 사람들(me too^^)은 조금은 외부세계와 단절된 듯한 느낌을 보여줍니다. 아니 어찌보면 직업병이라고나 할까. 이진법에 익숙해서 그런지 아날로그적인 애매모호한 일상 생활에 잘 적응하지를 못하는 것 같습니다. 우리들은 이른바 전공 서적이란걸 보기 위해서 영어를 공부하고 기술적인 것을 적용하기 위해서 컴퓨터관련 서적을 뒤져봅니다. 하지만 가만히 노트북을 접고 생각해보면 기술적이라고 하는 것들이 실은 다른 공학이나 인문학에서 영감을 얻어 현재 적용되고 있는 것을 보면 지금처럼 프로그래밍을 공부하는 것이 문법만 죽으라고 공부했던 영어 공부와 무엇이 다를까 의심해 봅니다. 분명히 우리들은 영어 공부 뿐만 아니라 프로그래밍 공부 역시 잘못하고 있다는 느낌을 강하게 느낍니다. 특히 저로서는...

C를 접할때 어렵게 느껴졌던 포인터. 그런데 그 쓰임새가 정말이지 절실하게 느껴졌던 시기는 임베디드 보드에서 실행될 프로그램을 만들때였습니다. 솔직히 그 전까지만 해도 "포인터 = 데이터 포인터" 로만 생각을 했었는데 이젠 "포인터 = 함수 포인터" 이더라구요. kernel 소스나 오픈소스를 뒤져보면 여지없이 나오는 함수 포인터 때문에 참 난감했었는데...

그러고 보면 우리는 버그 투성이의 프로그램을 만들기 위해서 노력하는 동안 해외에서는 소설처럼 읽어볼수 있는 깔끔한 소스를 만들기 위해서 많이 노력한 흔적이 보입니다. 우리는 rapid development 만을 강조했지 rapid에 따른 엄창난 side effect에 대해서는 아무도 대답하지 않습니다. 그때가서 문제가 생기면 회의하자고 하기 바쁜 세상.

다빈치의 모나리자 그림을 접할때면 어떤 생각이 드십니까? 다들 TV에서 접해서 들었던 오묘한 미소가 떠오르십니까? 얼마전 히스토리 채널에 방영한 것을 보고 상당히 놀라움을 금치 못했던 것은 모나리자를 그릴때 그 어떤 덧칠이 가해진 것이 없다는 것입니다. 우리들의 관점으로는 머리속의 생각을 그 어떠한 디버깅없이 바로 소스 코드로 작성했다는 거죠. 다빈치가 현대에 태어났었더라면 정말 멋진 OS가 만들어지지 않았을까 생각해 봅니다.

결론... 좀 거창하지만 만약 문학 비평가, 영화 비평가와 같이 문학이나 예술계에나 있을 법한 학문이 이젠 우리 분야에도 나타날때가되지 않았나 조심스럽게 짐잠해 봅니다. 소스 비평가(code reviwer) 정도로 용어를 만들어 볼수 있을것 같은데 이젠 소스를 어지럽게 써내려 가기 전에 오픈 소스를 소설이나 아니면 수필처럼 읽을 수 있는 여유로운 마음이 필요하지 않나 생각이 됩니다.

ps) 그런면에서 NetBSD 의 소스는 한편의 epic을 읽는 듯한 느낌이 듭니다. 고대 영국 문학의 epic 이 현대에 와서 반지의 제왕과 같은 소설로 다시 부활한 것과 같이 NetBSD는 AT&T UNIX의 서자(?)로 태어나 OS world에서 절대 군주로 자리잡은 진정한 영웅입니다.

Programs should be written to be read, and, whether they are or not, they need to be read.

2007년 6월 20일 수요일

pkgsrc 설치하기

third party 프로그램을 설치하기 위해서 CF Card에 한번 pkgsrc를 설치해보았습니다.
먼저 /usr/pkgsrc 디렉토리를 root 권한으로 생성한 다음 해당 group에 write권한을 준 다음,

% setenv CVSROOT anoncvs@anoncvs.NetBSD.org:/cvsroot
% setenv CVS_RSH ssh
% cd /usr
% cvs -q checkout -rpkgsrc-2007Q1 -P pkgsrc

설치된 pkgsrc의 크기는 200MB 조금 넘었습니다.
설치전,
% df -h
Filesystem Size Used Avail Capacity Mounted on
/dev/wd0a 1.8G 236M 1.4G 13% /
kernfs 1.0K 1.0K 0B 100% /kern
설치후,
% df -h
Filesystem Size Used Avail Capacity Mounted on
/dev/wd0a 1.8G 543M 1.1G 31% /
kernfs 1.0K 1.0K 0B 100% /kern

그렇다면 걸린 시간은?
무려 20시간 가까이 걸렸습니다. :-(

아무래도 NFS를 쓰는 편이 좋을 듯 싶네요. pkgsrc가 cross compile이 지원되는 날이 빨리 오길 기대하며...

2007년 6월 11일 월요일

SC Card mount

요즘 참 멋진 아이디어를 가진 주변장치가 많이 있더군요. SC Card인데 USB로 연결도 되는 제품도 있고, 가격도 저렴하고... 그래서 SC Card를 USB로 마운트를 해보았습니다.

먼저 USB slot에 SC Card를 연결했을때 TS-7200 Console에 다음과 같은 메시지가 나옵니다.

umass0 at uhub0 port 3 configuration 1 interface 0
umass0: Genesys USB Reader, rev 2.00/94.04, addr 2
umass0: using SCSI over Bulk-Only
scsibus0 at umass0: 2 targets, 1 lun per target
sd0 at scsibus0 target 0 lun 0: disk removable
sd0: fabricating a geometrysd0: 952 MB, 952 cyl, 64 head, 32 sec, 512 bytes/sect x 1950720 sectors

이와 같은 메시지가 나온다는 것은 제대로 연결되었다는 것이니깐 이젠 마운트하는 것만 남았네요. 먼저 disklabel 명령어로 어떻게 구성되었나 알아봅니다.

# disklabel sd0
# /dev/rsd0c:
type: SCSI
disk: mydisk
label: fictitious-MBR
flags:
bytes/sector: 512
sectors/track: 32
tracks/cylinder: 64
sectors/cylinder: 2048
cylinders: 952
total sectors: 1950720
rpm: 3600
interleave: 1
trackskew: 0
cylinderskew: 0headswitch: 0 # microsecond
strack-to-track seek: 0 # microseconds
drivedata: 0

8 partitions:

# size offset fstype [fsize bsize cpg/sgs]
c: 1950720 0 unused 0 0 # (Cyl. 0 - 952*)
e: 1950591 129 MSDOS # (Cyl. 0*- 952*)
disklabel: boot block size 0
disklabel: super block size 0

sd0e가 MSDOS 파일시스템으로 되어있는 것을 알수 있습니다. 이젠 mount명령으로 마운트 해야죠.

# mount -t msdos /dev/sd0e /mnt

그런데 TS-7200 Console 연결 화면에는 다음과 같은 에러메시지가 나오네요.

sd0(umass0:0:0:0): no door lock
sd0: fabricating a geometry

warning 메시지인가 봅니다. 쓰는데는 아무런 문제가 없네요. 마지막으로 unmount하고 USB slot에서 빼면 Console 연결화면에 다음과 같이 나옵니다.

umass0: at uhub0 port 3 (addr 2) disconnected
sd0 detached
scsibus0 detached
umass0 detached

뭐 별거 아니네요. CF Card대신 USB 메모리를 쓴다면 이런식으로 하면 될거 같네요. 속도 비교를 하자면? 둘다 느리긴 마찬가지입니다 :-(

2007년 6월 7일 목요일

button.c 분석

최종 소스는 ftp://ftp.embeddedarm.com/ts-arm-linux-cd/samples/ 에 위치합니다.
button.c는 입력을 받아 보드에 있는 2개의 led에 점멸을 표시합니다. 여기서는 단순히 LED 점멸만 하는 즉, 쓰기 기능을 하는 것으로 하겠습니다.
TS-7200 Hardware Manual(ts-7200-manual-rev2.2.pdf) 의 7.1 Status LEDs를 보면 Red와 Green 두가지의 LED가 board에 존재하는 것을 알수 있습니다. 사실 EP9302 CPU가 두개의 LED를 지원한다고 보는게 맞겠죠. 메모리위치는 0x8080_0020입니다. 소스코드를 살펴보기전에 다시 한번 GPIO에 대해서 개략적으로 살펴보죠. EP9301 User's Guide 의 Chapter 21에 GPIO 대해서 상세히 나옵니다. Instruction의 내용만 훓어보면 EP9302는 7개의 GPIO port를 지원합니다. 각 port는 동일하게 DR, DDR이 존재하고 interrupt 처리와 같은 기능을 위한 resister도 존재하는데 이기능을 지원하는 port를 EGPIO(Enhaunced GPIO) 라고 합니다. port는 A, B, C, D, E, F, G, H가 존재하고 EGPIO에 해당하는 것은 port A, port B, port F입니다. EP9302는 32bit이기 때문에 register크기도 모두 32bit이지만 실질적으로 사용하는 크기는 다음과 같습니다.
- port A: 8bits
- port B: 8bits
- port C: 1bit
- port E: 2bits
- port F: 3bits
- port G: 2bits
- port F: 4bits

우리가 지금부터 하고자하는 것은 port E에 해당합니다. 2bit가 유효한데 각각 Red, Green LED를 의미합니다. 문서에는 RDLED, GRLED라고 되어있네요. jp.c 와는 다르게 여기에서는 LED를 켜고 끄기 위해서는 쓰기 작업을 해야합니다. 따라서 DR 뿐만 아니라 DDR에 대해서도 작업을 수행해야 합니다. 쓰기 작업은 Read Modify Write (RMW) 방식으로 한다고 했는데 여기서는 Write 만 하고 확인은 직접 board의 LED를 보면서 확인할수 있습니다.

번지 번호 하나 외우고 들어가죠. 0x8084_0000 번지. 이 번지에서 부터 차례대로 GPIO의 resister가 매핑되어 있습니다. EP9301 User's Guide 에는 각 port 의 번지가 나와있는데 TS7200 Hardware 매뉴얼에는 해당 기능에 대한 이름을 열거해 두어서 훨씬 보기 편합니다. 우리의 관심사인 port E의 DR, DDR의 번지는 각각 0x8084_0020, 0x8084_0024 번지에 위치합니다. DR과 DDR의 각 비트위치는 서로 일치합니다. 즉, 0번째 bit가 RDLED의 값과 읽기/쓰기 모드를 나타내고 1번째 bit는 GRLED의 값과 읽기/쓰기 모드를 의미합니다. 지금까지 장황하게 설명했으니 실제 소스를 들여다 보죠.

line 17:
volatile unsigned int *PEDR, *PEDDR, *PBDR, *PBDDR, *GPIOBDB;
주요변수에 대해서 volatile 키워드를 사용했습니다. 그전에는 쓸 기회가 없었는데(사실 뭔지도 몰랐음^^) 이럴때 필요 하더군요. 간단히 말하자면 기계어로 컴파일 될때 최적화에 따른 부작용을 없애자는 거죠. 이 키워드를 쓰면 해당 변수가 들어간 코드에 대해서 컴파일러가 최적화 작업을 수행하지 않습니다. 따라서 소스 레벨에서는 문제 없다가 실행시에 문제가 발생하는 것을 사전에 막아줍니다. 여기서는 unsigned int *로 선언을 했습니다. 여기서는 PEDR, PEDDR만 쓸 예정인데 unsigned char *로 해도 사실은 무방합니다.

line 23:
start = mmap(0, getpagesize(), PROT_READPROT_WRITE, MAP_SHARED, fd, 0x80840000);
마지막 파라미터가 많이 보던 값이죠. GPIO에 접근할때는 이 번지로~~

line 26, line 27:
PEDR = (unsigned int *)(start + 0x20); // port e data
PEDDR = (unsigned int *)(start + 0x24); // port e direction register
start를 unsigned char * 로 선언해 둔 이유를 알겠죠. base 번지로 부터의 상대 주소에 접근할 때 요런식으로 많이 씁니다. 소스코드 보기에도 좋구요. 깔끔하잖아요. PEDR, PEDDR을 unsigned int *로 선언했으니 형변환하는 거 잊지말구요.

line 31:
*PEDDR = 0xff; // all output (just 2 bits)
가장 핵심적인 내용인데 LED에 대해서 쓰기 모드로 지정하겠다는 거죠. 나중에 DR에 값을 쓰면 그 값이 써지게 되어서 결과적으로 LED가 점멸하게 되는 것을 알수 있습니다.

line 35, 36, 37:
while (state & 0x01) { // wait until button goes low
state = *PBDR; // remember bit 0 is pulled up with 4.7k ohm
}
요기서는 위의 코드를 그냥 주석처리하세요. input을 기다리는 내용인데 하드웨어 꾸미기 귀찮아서...

line 42, 44:
*PEDR = 0xff;
*PEDR = 0x00;
위의 코드에서 DDR를 write 모드로 변경했기 때문에 DR에 값을 쓰면 그 결과가 바로 눈으로 나옵니다. 42번 라인을 정확하게 한다면 *PEDR = 0x03; 으로 하는게 맞을거 같은데 사실 32bit의 DR 내용중에서 하위 2bit를 제외하고는 사용하지 않는 내용이니 큰 문제가 되지 않습니다. for() 문과 중간에 sleep()을 주어서 반복하게 되니 차례대로 꺼졌다 켜졌다를 반복하는 것을 눈으로 볼수 있습니다. 간단하죠.

그렇다면 NetBSD 에서는 어떻게 확인할까요?
불행히도 빠져 있네요. 하지만 /usr/src/sys/arch/evbarm/tsarm/tspld.c 에 추가해서 sysctl에 표시되게 하는 방법으로 한다면 일관적이고 사용자 입장에서 편할것 같습니다. Jesse Off 가 별로 필요없을 거 같아 추가를 안했을도 있겠네요. 눈으로 보이는데 굳이 프로그램으로 확인할 필요도 없을 거 같구 굳이 그걸 프로그램에서 알 필요도 없지 않을까요.

알고 보면 상당히 간단합니다. 근데 키패드와 Text LCD는 조금 복잡하네요. scan 기법을 쓰다보니 하드웨어적인 사항도 잘 알아야 되고... 다음엔 이것에 대해서 한번 적어 볼까합니다.

jp.c 분석

최종 소스는 ftp://ftp.embeddedarm.com/ts-arm-linux-cd/samples/ 에 위치합니다.

jp.c는 보드의 좌측 하단에 위치한 6개의 점퍼 상태를 읽는 기능을 합니다.
TS-7200 Hardware Manual(ts-7200-manual-rev2.2.pdf) 의 7.2 Jumpers를 보면 각 점퍼에 대한 memory map 정보가 나옵니다. 그런데 JP1에 대한 map 정보는 없습니다.(이상하죠?) JP2~JP5는 0x1080_0000 에 위치하고 JP6는 0x2280_0000 에 위치합니다. 각 데이터는 1bit에 해당을 하네요. 참고로 DR, DDR이란 말이 나오는데 DR(Data Register)는 말그대로 Data가 위치하는 resister이고 DDR(Data Direction Resister)는 읽기/쓰기는 결정하는 resister입니다. 제가 처음 접했을때 resister는 CPU내에 위치한 resister만 생각했는데 여기서는 resister란 용어를 이렇게도 쓰더군요. 결론은 여기서는 DR만 잘 읽어서 처리하면 끝입니다.

8, 9 line:
#define TSSTATUS 0x10800000
#define TSJP6 0x22800000
이렇게 정의를 해두었습니다. 다른 소스도 마찬가지이겠지만 resister의 번지는 다 이런식으로 정의하고 시작합니다.

20 line:
int fd = open("/dev/mem", O_RDWRO_SYNC);
O_SYNC flag는 앞선 글에서 언급한바와 같이 cash에 따른 부작용을 없애기 위해 필요하고 O_RDWR는 read/write 모드로 파일을 open 하겠다는 뜻이겠죠. 굳이 여기서는 write 모드로 열 필요는 없습니다.

40, 41, 42 line:
page = TSSTATUS & 0xfffff000;
start = mmap(0, getpagesize(), PROT_READPROT_WRITE, MAP_SHARED, fd, page);

사실 여기서는 단순하게 다음처럼 하는것이 더 보기 좋을 것 같네요.
start = mmap(0, getpagesize(), PROT_READPROT_WRITE, MAP_SHARED, fd, TSSTATUS);
여기서 한가지 중요한 것은 우리가 bit단위의 data를 read할 예정이므로 start는 unsigned char* 로 선언되어야 한다는 것입니다. 즉, 읽고자 하는 데이터의 길이를 잘 판단해서 선언해야하는 것이죠. 여기서는 read만 하기 때문에 크게 문제가 되질 않지만 write를 할때에는 자칫 큰 문제가 생길수가 있습니다.

43 line:
dat = (unsigned char *)(start + (TSSTATUS & 0xfff));
이것 역시 다음처럼 하는게 더 보기 좋습니다.
dat = start;

이후 소스는 & 연산자를 이용해서 해당 비트를 읽어서 표시하는 것이니깐 그리 어렵지 않게 이해가 될겁니다.

컴파일해서 TS7200 보드에 실행해보면 점퍼가 꽂아져 있는 곳에는 ON이라는 결과가 나올겁니다. 무지 간단하죠.
그렇다면 NetBSD에서는 어떻게 확인할까요?
sysctl 명령으로 알수 있습니다~ 다음 처럼요.
% sysctl -a
...
hw.tspld.jp1 = 0
hw.tspld.jp2 = 1
hw.tspld.jp3 = 1
hw.tspld.jp4 = 0
hw.tspld.jp5 = 0
hw.tspld.jp6 = 0
...

여기서는 jp1의 상태를 알수가 있네요^^ 소스를 PC의 /usr/src에 받아두었다면 /usr/src/sys/arch/evbarm/tsarm/tspld.c 에 점퍼설정에 대한 내용이 있습니다. linux의 jp.c에 비해서 조금 복잡해 보일지도 모르지만 사용하는 입장에서는 너무나 편리합니다.

accessing peripherals basics in ARM

ARM 프로세스는 주변장치에 접근하기 위해서는 memory mapped I/O 방식을 사용합니다.
x86계열과는 다르게 별도로 instruction이 존재하는 것이 아니라 주변 장치를 위해 필요한 모든 정보(register ...)들이 모두 메모리에 위치하게 됩니다. 이때 물리적 메모리(physical memory)를 논리적 메모리(logical memory)로 매핑해주는 MMU(Memory Management Unit) 의 기능이 OS가 부팅될때 활성화되므로 단지 우리는 해당하는 register가 메모리 어느 번지에 위치하는지 테이블만 보고 해당 작업을 해주면 끝입니다. 사실 Linux Driver를 만든다는 것이 따지고 보면 이런 작업을 좀더 쉽게 함수로 제공해 주는 것 정도라고 봐야겠죠. 하지만 모든 일이 그렇듯이 먼저 이런 일을 해 준 사람에게 고마워해야 할겁니다.
ARM에서는 주변장치라는 말 대신 GPIO(General Purpose Input/Output ) pin 이라는 말을 많이 합니다. 주변장치에 접근하는 하나의 방식이지만 현재는 거의 모든 주변장치에 대해서 GPIO 방식을 이용하는 것으로 보입니다. 하드웨어적으로 하나의 pin으로 존재하는 것에 대해서 GPIO에서는 여러가지 방식으로 접근을 합니다. 가장 기본적인 것이 해당 pin에서 데이터를 읽을 것인가 쓸 것인가(Read/Write)를 결정하는 register가 존재하고, 읽고 쓰는 실제 데이터가 위치하는 register가 존재합니다. 쓰기 작업은 Read Modify Write (RMW) 방식으로 차례대로 진행되는 것이 대부분입니다. 즉, 출력용 주변 장치는 RMW 방식으로 접근하면 되는 것이죠. 그에 비해서 읽기 작업은 Read만 하면 되니 상대적으로 쉽습니다.
실제적으로 프로그래밍을 할때 주변장치에 접근하는 방식은 x86과는 사뭇 다릅니다. 앞에서 언급한바와 같이 별도의 instruction이 존재하는 것이 아니기 때문에 처음 접할때는 상당히 당혹스럽습니다. linux에서는 기본적으로 user level에서 접근할수 있는 방식은 /dev/mem을 직접 접근하는 방식입니다. 이 파일을 mmap() 함수를 이용해서 접근하는 방식이죠. 이후 작업은 대개 비트 연산과 같은 저레벨의 연산 작업을 수행하는 것이 전부입니다. 여기서 주의해야 할 사항은 다음과 같습니다.
- /dev/mem 을 열때 O_SYNC flag 사용할것(cash 기능을 없앱니다.)
- 주요 변수에 volatile 키워드를 사용합니다. 이 역시 cash 기능에 따른 부작용을 없애게 됩니다.
주변장치에 접근하는 방식을 이해하는 것은 소스 파일을 보는 것 만큼 쉬운 방법이 없을 겁니다. ftp://ftp.embeddedarm.com/ts-arm-linux-cd/samples/ 에 있는 파일들이 모두 주변 장치에 접근하는 소스 파일들입니다. 가장 쉬운 것이 jp.c 로 단지 Read 기능 만으로 점퍼의 상태를 알수 있습니다. button.c는 소스 중간에 위치한 while() 문만 주석처리하면 led 점멸을 볼 수 있습니다. 즉, Write 기능을 확인할수 있죠. 이 두개파일만 컴퓨터 구조론 공부할때 배운 내용과 잘 접목시켜서 원리를 이해한다면 x86에서는 느껴보지 못한 희열(?)을 느낄수 있을겁니다. RISC 라는 말이 나온것도 같은 맥락으로 생각하시면 되겠죠. 굳이 주변장치를 접근하는데 instruction을 이용할 필요는 없는 셈이죠. 나머지 소스파일도 잘 봐두면 도움이 됩니다. 요것들이 NetBSD에서는 Driver로 지원을 하거든요~~
여기까지 이해를 했으면 이제는 http://www.embeddedarm.com/downloads/Components/EP9301_User_Guide.pdf 의 EP9301 User's Guide(EP9032 User's Guide는 cirrus 측에서 제공하고 있지 않습니다.) 와 http://www.embeddedarm.com/Manuals/ts-7200-manual-rev2.2.pdf 의 TS-7200 Hardware 메뉴얼을 잘 읽어보는 일만 남았습니다. 두 문서에서 메모리맵에 대한 정보를 잘 비교하면 TS-7200에서 어떤 기능을 구현하고 있는지에 대해서 알수 있습니다.
- EP9301 User's Guide: 2.3.6 Internal Register Map
- TS-7200 Hardware Manual: appendix B Memory and Resister Map
Reference) http://www.simtec.co.uk/appnotes/AN0014/
ps) 너무 글을 두서없이 일기처럼 쓰다보니 정신이 없네요. 한번 예쁘게 PDF 파일로 한번 정리를 해봐야겠습니다.