VxWorks Porting Guide

VxWorks의 porting은 기본적으로 target board가 안정되어 있고 target board에 맞는

BSP가 있다면  BSP의 약간의 수정으로 porting이 가능하다.

porting을 위해서 반드시 안정되어야 할 hardware부분은 CPU, memory, rs232, LAN이다.

굳이 LAN을 통해서 디버깅을 하지않겠다면 2개의 rs232 port를 이용하여 디버깅하는

방법도 있다.

BSP의 program이 bootConfig.c까지 수행되고 hyperterminal에 logo message가 올라오지

않는다면 100% hardware에 이상이 있는 것이다.

VxWorks program은 hardware에 dependent하지 않는 부분에서의 이상은 있을수 없다.

VxWorks는 상용 program이며, 아직 까지 bug가 있다고 알려진 것이 없으며, 이상

없다는 확신 하에서 porting을 해야 문제를 빨리 해결 할 수 있다.

porting하는 것의 대부분은 hardware debuging이 라고 할 수 있다.

참고로 BSP가 수행되는 순서는 rominit.s -> bootInit.c -> bootConfig.c이며

sysSerial.c, sysLib.c, target_sio.c(rs232 driver), target_timer.c(timer driver),

target_intrCtl.c(interrupt controller driver)는 bootConfig.c를 통해서 수행된다.

나머지 program들 즉 sysALib.s, usrConfig.c는 VxWorks file을 만들 때 사용되며

이 file은 LAN을 통해서 ram에 symbol file과 함께 download되며 download 후

WDB(windriver debug)mode로 전환시키기 위해서 실행된다.(down load가

완료되고 위의 program이 실행되면 WDB mode가됨.)

1. BSP가 있는 경우의 porting

BSP가 있을 경우 target board는 기본적으로 BSP에 맞추어서 설계되어 있어야 한다.

즉 같은 port number의 uart port와 BSP에서 사용하는 LAN driver의 hardware

interface가 정확하게 hard-wired 되어 있어야 한다.

hardware가 일치한다면 주로 memory부분만 BSP에 맞게 setting하면 된다.

다음의 설명들은 target board가

rom 1M bytes

ram 2M bytes

rom base address 0x10000000

ram base address 0x00000000

로 설계되었다고 가정한다.

BSP 수정 사항

- rominit.s

rominit.s는 power on 후 제일먼저 실행되는 부분이다. 주로 target board가

기본적으로 동작하기 위한 부분들을 setting 하게된다.

보통 clock, interrupt, memory에 관계된 부분들이 있지만 BSP가 제공되어진

CPU라면 memory에 관련 register들만 target board에 맞게 setting해 주면 된다.

만약 rominit.s 다음에 수행되는 bootInit.c로 jump되지 않는 다면 필히 memory

관련 register setting후에 CPU가 down 되거나, vector table 번지 근처에서 다시

시작하거나, bootInit.c로 jump할 때 엉뚱한 곳에 program counter가 가있을 것이다.

rominit.s는 VxWorks를 porting 할 때 제일 중요한 곳으로 이 곳이 넘어가지

않는 다면 어느 부분의 setting이 이상한지 아니면 hardware가 이상한지

밝혀내야한다.

VxWorks는 CPU가 memory의 allocation을 조정할 수 있다면 ram을 0번지로

옮기고 rom을 다른 위치로 이동시켜 사용한다. 이런 대개의 경우는 rom의

base address와  vector table이 0x00000000번지 근처이다.

ram을 0번지로 remap시키는 것은 interrupt vector table의 관리를 효율적(?)으로

하기 위해서라고 보면 무방하다.

이렇게 ram의 영역을 remap하는 경우도 rominit.s에서 이루어진다.

- config.h

config.h에서는 주로 memory와 LAN과 관련된 사항들을 주로 define 해놓았다.

LOCAL_MEM_SIZE :

ram의 총 size를 정의

ex) #define LOCAL_MEM_SIZE  0x00200000

USER_RESERVED_MEM :

os가 사용하지 못하도록 ram영역 size를 지정하는 것으로 ram의 최 상위

영역에 잡힌다. 보통의 경우에는 □0□으로 지정해놓으면 되지만 arm계열처럼

mmu를 이용하는 CPU는 ram의 최상위영역에 보통 mmu table을 잡게된다.

이때 mmu table size만큼 USER_RESERVED_MEM을 지정한다.

ex1) #define USER_RESERVED_MEM   0

ex2) #define USER_RESERVED_MEM   0x400

이때 os는 memory를 0x00200000 - 0x400 만큼만 이용한다.

ROM_BASE_ADRS :

rom의 base address(remap한 후의 address)

ex) #define  ROM_BASE_ADRS  0x10000000

ROM_TEXT_ADRS :

rom상에 실질적인 code가 존재하는 start address를 지정, 즉 사용하는 CPU의

reset vector가 0x100 이라면

ex) #define  ROM_TEXT_ADRS   ROM_BASE_ADRS +  0x100

보통의 CPU는 reset vector가 0x00번지이므로

ROM_TEXT_ADRS를ROM_BASE_ADRS 로 지정하면 된다.

ROM_SIZE :

target board의 총 rom size

ex) #define  ROM_SIZE    0x00100000

ROM_COPY_SIZE :

VxWorks는 rom의 program을 ram에 copy하고 program을 ram에서 구동시켜

program 실행시간을 단축시킨다. ROM_COPY_SIZE는 ram에 copy될 rom의

size를 정의하는 변수이다. 보통 ROM_SIZE로 define해주면 무난하다.

ex) #define ROM_COPY_SIZE   ROM_SIZE

rom에서 ram으로 copy가 이루어지는 곳은 bootInit.c 이다. rom이 ram으로

copy되기 시작하는 번지는 RAM_HIGH_ADRS이다. 만약 bootInit.c에서

program이 죽는다면 copy하는 영역이 ram의 size를 벗어나는 경우 즉

ram이 2M, ram_high_adrs가 1M, copy size가 2M라면 copy되는 영역이 ram의

1M + 2M이므로 ram의 영역을 벗어나게 된다. 대부분의 경우는 이런 현상이

일어나도 상관이 없다. 그러나 이상 현상이 일어나는 경우는 실질적으로

rom에 burnning되는 code의 길이가 LOCAL_MEM_SIZE  - RAM_HIGH_ADRS

한 size보다 크다면 code모두가 ram에 올라가지 못하고, 또 ram의 상위영역을

stack으로 쓰는데 stack영역을 확보 하지 못해 program이 돌지 않게 된다. 그러나

os porting의 경우 rom에 burnning되는 size는 보통 300K를 넘지 않으므로

큰 상관이 없다. 만약 bootInit.c에서  program이 계속 죽는다면

- memory access time을 check

- CPU가 byte operation을 지원하고 ram을 2개또는

4개로 byte별로 병열 확장해서 설계했다면 byte

operation mode로 ram을 다시 연결.

- USER_RESERVED_MEM영역을 지정했다면 이

영역을 access함으로서 이상이 발생하는지.

- 혹 ram영역 이상을 access할 때 그 영역이 내부

register영역이 아닌지를 check.

계속해서 문제가 발생한다면

- rom의 copy size를 rom size지정하여 문제가

발생한다면 compile 할 때 실code의 size가 나오는데

그 size만큼 지정하면 된다.

ex) code size가 280K이면

#define ROM_COPY_SIZE   300K

ROM_SIZE_TOTAL :

ROM_SIZE 로 지정

ex) #define ROM_SIZE_TOTAL  ROM_SIZE

RAM_LOW_ADRS :

0x00 번지에서부터 RAM_LOW_ADRS까지는 interrupt vector table과 init stack등

기본적으로 os가 사용하기 위한 영역임. 2M의 ram일 경우 0x00005000 가 적당함

ex) #define RAM_LOW_ADRS 0x00005000

RAM_HIGH_ADRS :

RAM_LOW_ADRS에서부터 RAM_HIGH_ADRS 까지는 LAN을 통해서 down

loading되는 program이 들어가며 RAM_HIGH_ADRS부터 rom의 code가 copy됨

2M의 ram일 경우 0x00100000 가 적당함

ex) #define RAM_HIGH_ADRS 0x00100000

NV_RAM_SIZE :

NV RAM은 battery backup ram을 말하며 전원을 꺼도 데이터의 내용이 저장된다.

VxWorks가 이 ram을 이용하는 이유는 boot parameter를 저장하기 위해서이다.

boot parameter는 DEFAULT_BOOT_LINE에 정의되어진 내용이다. 이 내용은

porting이 끝난 후 program 이 실행될 때 auto booting 이전에 boot parameter를

바꿀 수가 있는데 이때 바뀐 내용이 NV RAM에 저장된다.

NV RAM을 target board에 장착했다면 NVRAM에 관련된 내용들을 define

해주면된다. 만약 NV RAM이 없다면 NV_RAM_SIZE를 NONE으로 define하면

된다.

ex) #define NV_RAM_SIZE  NONE

INCLUDE_FLASH :

제공되어진 BSP의 target board가 flash memory를 가지고 있고 NV RAM이

없다면 flash memory를 이용하여 boot parameter를 저장하려고 할 것이다.

boot parameter를 저장하지 않으려면 INCLUDE_FLASH를 undefine 해주면된다.

ex) #undef INCLUDE_FLASH

DEFAULT_BOOT_LINE :

VxWorks는 기본적으로 LAN base에서 debugging을 함으로 target board에

LAN 관련된 기본적인 사항들을 지정해주어야한다.

- makefile

CPU :

target board가 사용하는 CPU의 core의 종류를 지정해준다.

ex) MPC860 의 경우

CPU = ppc860

SA1100 의 경우

CPU = SA110

ARM7 의 경우

CPU = ARM710

ROM_BASE_ADRS :

config.h file에서 정의한 ROM_BASE_ADRS 값

ex) config.h file의 ROM_BASE_ADRS가 0x10000000이라면

ROM_BASE_ADRS  = 10000000

(주의) makefile에서 지정하는 hex값은 □0x□를 빼고 적는다.

ROM_TEXT_ADRS :

config.h file에서 정의한 ROM_TEXT_ADRS 값

ex) config.h file의 ROM_TEXT_ADRS가 0x10000000이라면

ROM_BASE_ADRS  = 10000000

ROM_SIZE  :

config.h file에서 정의한 ROM_SIZE  값

ex) config.h file의 ROM_SIZE  가 0x00100000이라면

ROM_SIZE    = 00100000

RAM_LOW_ADRS :

config.h file에서 정의한 RAM_LOW_ADRS 값

ex) config.h file의 RAM_LOW_ADRS 가 0x00005000이라면

RAM_LOW_ADRS = 00005000

RAM_HIGH_ADRS :

config.h file에서 정의한 RAM_HIGH_ADRS 값

ex) config.h file의 RAM_HIGH_ADRS 가 0x00100000이라면

RAM_HIGH_ADRS = 00100000

- target.h

제공된 BSP는 보통 target board의 이름을 가진 header file이 하나씩있다.

Strong ARM1100을 CPU로 한 Brutus board의 경우는 brutus.h file,

MPC860을 CPU로 한 est860 board의 경우는 est860.h file이다.

이것들을 보통 target.h file이라한다.

target.h file에는  internal register의 address나 target board에 관련된 각종

base address, 변수, clock 주파수등을 정의하고 있다.

porting을 위해서 target.h file에 기본적으로 정의되어야하는 것들은

timer, interrupt, uart, memory 관련된 base address나 변수들이다.

LOCAL_MEM_LOCAL_ADRS :

ram의 base address

ex) #define LOCAL_MEM_LOCAL_ADRS  0x00000000

LOCAL_MEM_BUS_ADRS :

ram의 base address

ex) #define LOCAL_MEM_LOCAL_ADRS  0x00000000

TARGET_INT_BASE :

target board CPU의 interrupt controller register  base address.

target_intrCtl.c에서 참조한다.

ex) #define TARGET_INT_BASE  0x80010000

UART_XTAL_FREQ :

uart driver에서 baudrate를 계산할 때 사용하는 clock. 보통 CPU의 clock으로

baudrate를 계산하므로 CPU의 clock을 적어주면된다. 특별히 external clock을

사용하거나, uart driver를 외부에 달아서(z85c30같은 것) 사용할 때에는 사용하는

clock frequency를 적어주어야한다. hyperterminal에 올라오는 logo message가

깨어져 올라 올때는 보통 clock setting값이 잘못되어거나 configAll.h file에서

정의한 CONSOLE_BAUD_RATE와 hyperterminal 의 baudrate가 맞지않기

때문이다.

ex) 50M의 clock을 사용할 경우

#define UART_XTAL_FREQ  0x50000000

N_UART_CHANNELS , N_SIO_CHANNELS, N_TARGET_UART_CHANNELS:

target board에서 사용하고자하는 uart port의 총 개수를 정의한다. 보통은

hyperterminal에  연결해서 사용할 rs232 port 하나면 되므로 □1□로 define해주면

된다.

ex) #define N_UART_CHANNELS  1

#define N_SIO_CHANNELS  N_UART_CHANNELS

#define N_TARGET_UART_CHANNELS N_SIO_CHANNELS

os에 인식되어질 수 있는 uart의 개수는 총 20개 까지이다.( configAll.h file의

NUM_DRIVERS값으로 정의 되어있다.) 실질적으로 os에 uart를 알려주는곳은

bootConfig.c에서한다.  이때 가져가는 uart의 개수를 configAll.h file의

NUM_TTY에 정의되어진 수를 가져가는데, 이 값이 N_SIO_CHANNELS값으로

define되어 있지않고 □2□ 나 □3□ 으로 define되어 있는 경우가 있다.

보통 □1□ 이상의 숫자이면 상관없다. 실질적으로 존재하는 uart의 개수를

sysSerial.c의 sysSerialChanGet 함수를 호출해서 알게되는데, 여기서 존재하는

uart의 개수 이상값을 호출하면 return값이 error로 돌아와 이상의 값은 무시한다.

TARGET_UART_BASE_ADDR :

target board CPU의 uart controller register base address. sysSerial.c에서

참조한다.

ex) #define TARGET_UART_BASE_ADDR 0x80020000

TARGET_TIMER_BASE :

target board CPU의 timer controller register base address. target_timer.c에서

참조한다.

ex) #define TARGET_TIMER_BASE 0x80030000

SYS_TIMER_CLK :

os에서 사용할 main timer의 clock source의 frequency를 define하는 곳이다.

VxWorks는 보통 1초에 60번정도의 timer interrupt(tick)를 발생시켜 그 수를

카운터한다. target_timer.c에서 60Hz의 tick을 발생시키위해서 CPU

manual에서 제공하는 계산식을 이용하여 time constant를 정하는데, 이때

계산식에 사용되어지는 clock으로 SYS_TIMER_CLK을 define해주어야한다.

보통은 CPU clock을 사용한다.

ex) #define SYS_TIMER_CLK  0x50000000

AUX_TIMER_CLK :

VxWorks는 60Hz의 tick을 발생시키는 main timer외에 예비로 100Hz의 tick을

발생시키는 보조 timer를 하나더 가지고 있는데 setting만 시켜놓고 사용은

하지않는 것 같음(?). define은 SYS_TIMER_CLK와 같은 방법임.

SYS_TIMER_INT_LVL :

target.h file에 정의된 main timer의 interrupt level 값

AUX_TIMER_INT_LVL :

target.h file에 정의된 보조 timer의 interrupt level 값

BSP가 있고 target board만 안정되어 있다면 기본적으로 memory base address와

size만 정의해주면 hyperterminal에 logo message는 올라온다.

대부분의 경우 internal register의 값 등 기본적인 사항은 일치하기 때문이다.

2.  BSP가 없는 경우의 porting

VxWorks를 porting한다면 당연히 tornado 기반에서 BSP를 complie할 것이다.

porting하고자 하는 target board의 BSP가 없더라도 기본적으로 갖추어야 할

몇가지가 있다.

첫째 target board CPU의 core를 complie할 수 있는 complier가 지원되는 tornado

가 필요하다. arm용 tornado가 있다면 arm7, arm710, arm7TDMI, sa110, sa1100등

의 CPU를 complie할 수 있으며, ppc용 tornado가 있다면 mpc860, 603, 403등의

CPU를 complie할 수 있다.

둘째 어떤 CPU의 것이던지 BSP가 하나 필요하다. BSP를 처음부터 만든다는 것은

불가능하기 때문이다. 다른 CPU의 BSP에서 기본적인(대부분이라고 할수있음) flow를

이용하고 target board에 맞는 각 device의 driver, rominit.s, sysAlib.s만 고쳐주기만

하기 때문이다. BSP를 분석해보면 알게되지만 대부분의 BSP들은 hardware dependent

한 부분을 제외하면 나머지는 똑같다. 조금만 생각해보면 당연하다는 것을 알것이다.

간단히 말해서 BSP가 없는 상태에서 os를 porting한다는 것은 device driver와

rominit.s( sysAlib.s는 rominit.s를 적당히 고치면됨. 진짜 적당히 임 )를 target board에

맞게 제작하는 것이다.

고쳐야 할 file들은 다음과 같다.

tornado\target\config\target_dir의

rominit.s, config.h, target.h, makefile, sysLib.c,

sysSerial.c, sysALib.s

추가시켜야 file들은 timer, uart, interrupt controller의 driver 파일들이다.

그 위치와 이름은 다음과 같다.

tornado\target\src\drv\intrCtl의 interrupt controller driver c file인

target_intrCtl.c

tornado\target\src\drv\sio의 uart controller driver c file인

target_sio.c

tornado\target\src\drv\timer의 timer controller driver c file인

target_timer.c

tornado\target\h\drv\intrCtl의 interrupt controller driver header file인

target_intrCtl.h ( interrupt controller의

경우 h file이 필요없을 수 도 있읍니다.)

tornado\target\h\drv\sio의 uart controller driver header file인

target_sio.h

tornado\target\h\drv\timer의 timer controller driver header file인

target_timer.h

저도 처음엔 c file들을 위의 각 directory에 만들어 넣어지만 target_sio file이

complie이 되지않는 것 같아 target_dir에 넣고 makefile에 추가시켜 porting을

했음니다.

makefile에 추가시키는 방법은 다음과 같음니다.

위치는 memory 영역을 적어준곳 다음이 면 적당합니다.

MACH_EXTRA = target_Sio.o

target_Sio.o: $(TGT_DIR)/config/target_dir/target_Sio.c

$(CC) $(CFLAGS) -c $(TGT_DIR)/config/target_dir/target_Sio.c

먼저 시작하기 이전에 memory에 관련된 define내용은 1. BSP가 있는 경우의 porting

을 참고했어 하시기 바랍니다. 여기서는 driver 설치법에 대해서만 설명하겠습니다.

참고로 앞으로 설명할 내용은

ARM7 core에

2개의 UART,

1개의 interrupt controller,

2개의 timer,

2 channel의 DMA를

packaging한 MITEL의 butterfly를 이용하여 만든 target board를 예로

설명하겠읍니다.

target board 사양은 다음과 같음니다.

cpu : butterfly

rom base address : 0x60000000(remap한 후)

rom size : 512K bytes

ram base address : 0x00000000(remap한 후)

ram size : 2M bytes

cpu clock : 27MHz

reset vector : 0x00000000

*remap을 하지않는다면

rom base address : 0x00000000

ram base address : 0x60000000

BSP는 ARM7TDMI demo용으로 제작된 pid7t board의 것입니다.

- rominit.s

rominit.s는 위에서 말한바와 같이 power on 후 제일먼저 수행되는

곳이다.

rominit.s같이 처음 start하면서 assemble code로 되어있는 부분을 booting

code라고 한다. 이 부분은 CPU의 initial에 필요한 code가 들어가는데 CPU의

종류마다 다르다. 보통의 경우 booting code는 CPU를 제공하는 회사의

웹사이트를 통해서 구할 수 있다.

일반적으로 booting code에는 clock, interrupt, memory, vector table등을

initial 한다. CPU의 특징에 따라 더 추가되는 것도 있고 필요 없는

부분도 있다.

booting code를 구했다면 VxWorks의 format에 맞게 만들어주면 된다.

rominit.s의 시작부분에 있는 include문과 선언문은 아래의 예제 prgram에서

빠져있는데  complile할 때 error나면 그때 그때 대처하시기 바람니다.

이 때까지 저의 경우는 error가 난적이 없습니다.

VxWorks의 format에 맞춘다는 것은 특별한것이 있는 것은 아니다.

program 시작 부분과 끝부분만 맞춰주면된다.

program 시작 부분은 다음과 같다.

_ARM_FUNCTION(_romInit)

cold:

MOV	r0, #BOOT_COLD

warm:

B	start

.ascii   "Copyright 1984-1998 Wind River Systems, Inc."

.balign 4

start:

program 끝 부분은 다음과 같다.

LDR	sp, L$_STACK_ADDR

MOV	fp, #0

LDR	pc, L$_rStrtInRom

.balign	4

L$_rStrtInRom:

.long	ROM_TEXT_ADRS + _romStart - _romInit

L$_STACK_ADDR:

.long	STACK_ADRS

위에서 설명한 program은 ARM의 것을 예로 든 것이다.

보통은 CPU마다 내용이 조금씩 틀리지만 기본적인

cold, warm, start label과 B start부분 그리고 stack을 load하고

program counter에 bootInit.c의 romStart 함수 point를 넘겨주는

것이다. STACK_ADDR, ROM_TEXT_ADRS는 header file에 이미

정의되어 있어있는 것을 이용하므로 신경쓰지 않아도 된다.

특히 stack을 inital하고 program counter에 romStart함수 point를 넘겨주는

방법은 각 CPU의 방법에 따라야 한다.

아래의 program들은 pit7t BSP와 그것을 modify하여 butterfly에 맞게

다시 program한 것이다. 바뀐내용은

rom, ram remap

memory 관련 register initial

이 두가지 뿐이다.

바뀐부분의 boot code는 butterfly용으로 porting한 angel debug의 것을

이용했다.

rominit.s에서 bootinit.c로 jump를 했다면 porting의 절반은 성공한 것이다.

이 부분은 쉽게 넘어갈 수 도 있지만 상당히 신중을 기해야한다.

아마 hardware가 안정이 되어있다면 나머지 부분은 싶게 넘어 갈수 있다.

- Timer controller driver 관련 program

BSP상에서 각 device driver를 program한다는 것은 기본적인 driver(rs232의 경우

interrupt mode로 해서 rx, tx가 가능한 program)가 만들어진 상태에서 VxWorks가

요구하는 format에 맞게 넣어주고 약간의 추가만 하는 것이다.

timer driver를 만들려면 먼저 1초당 60번씩 interrupt를 발생시키는 program을 미리

만들어 놓아야 한다. 만약 검증되지 않은 program으로 BSP driver를 만들면 porting이

잘못되었는지 program이 잘못되었는지 판단하기가 힘들다. 만약 program을 증명할

도구가 없다면 신중을 기해서 porting을 해야한다. 명심 할 것은 CPU마다 BSP format

이 약간씩 달라 user가 그때 그때 대처해야 하는데 이때 그것을 찾아 내기가 힘들다.

timer program의 기본적인 개념은 60Hz로 tick을 발생시키도록 timer를 setting하고

동작시키는 것과 60Hz로 발생하는 tick을 마음대로 조정할 수 있도록 하는 것이다.

timer를 위해서 수정이 필요한 program은 target.h, target_timer.h, target_timer.c

sysLib.c이다.

- sysLib.c

여기서 고쳐할 부분은 target_Timer.c를 include 시켜주는 부분이다.

함수가 시작하는 앞 부분에 있을 것이다.(program의 시작 부분이 아니고 내부에

여러개의 함수가 있는데 이 함수들이 시작되는 부분)

ex)   #include "timer/target_Timer.c"

- target_timer.h

사용하는 timer의 기본적인 사항 즉 timer register들과 timer setting

에 필요한 것들을 미리 변수로서 지정하는 것뿐이다. os에서 가져가 참고 하는 것들은

아니다. 이것은 program 하시는 분들의 기호에 따라 작성하면 된다.

- target.h

SYS_TIMER_CLK, AUX_TIMER_CLK,  SYS_TIMER_BASE_ADDRESS,

AUX_TIMER_BASE_ADDRESS, SYS_TIMER_INT_LVL, AUX_TIMER_INT_LVL,

SYS_CLK_RATE_MAX, SYS_CLK_RATE_MIN, AUX_CLK_RATE_MIN,

AUX_CLK_RATE_MAX이다

위의 기본적인 것들은 꼭 필요 한 것이고 BSP에 따라 timer에 관계된 다른 것들이 있을

수 있으나 사용하는 timer에 관계된 것들이니 필요하면 추가하면된다(program의 편리를

위한 것들이니 program하시는 분들의 취향에 따라하면 된다)

SYS_TIMER_CLK:

앞부분에서 설명한 바와 같이 timer는 main timer와 보조timer 두 개가 있다.

SYS_TIMER_CLK은 main timer를 setting 할 때 필요한 time 상수를 계산 할 때 쓰는

clock의 frequency를 말한다. 보통은 CPU의 clock이 바로 연결되어 있거나 분주해서

연결 시켜 놓았다.

ex) #define   SYS_TIMER_CLK  27000000

AUX_TIMER_CLK:

보조 timer를 setting 할 때 필요한 time 상수를 계산 할 때 쓰는

clock의 frequency를 말한다.

ex) #define   AUX_TIMER_CLK 27000000

SYS_TIMER_BASE_ADDRESS:

main timer register의 base address

ex) #define   SYS_TIMER_BASE_ADDRESS 0xe000a000

AUX_TIMER_BASE_ADDRESS,:

보조 timer register의 base address

ex) #define   AUX_TIMER_BASE_ADDRESS 0xe000b000

SYS_TIMER_INT_LVL:

main timer의 interrupt level 값

ex) #define   SYS_TIMER_INT_LVL   INT_LVL_SYS_TIMER

AUX_TIMER_INT_LVL:

보조 timer의 interrupt level 값

ex) #define   AUX_TIMER_INT_LVL  INT_LVL_AUX_TIMER

SYS_CLK_RATE_MAX:

1초당 timer가 동작 할 수 있는 최고의 값, 즉 1초 동안 timer가 최대 몇번이나

interrupt를 허용할 수 있냐는 것이다. 이 값은 timer에 따라 달라 질 수있지만

1초당 tick값을 너무 많이 허용하면 system동작에 방해가 된다.

기본적으로 tick값이 60Hz라고 위에서 말했는데 이 값을 얼마 까지 허용하겠냐는

것이다.

보통 1000정도면 무난 할 것이다. 여러분이 사용하는 system의 필요에 따라 원하는 만큼

해주면된다.

ex) #define  SYS_CLK_RATE_MAX  1000

SYS_CLK_RATE_MIN:

이것은 SYS_CLK_RATE_MAX의 반대되는 개념이다. 즉 1 초당 최소의 tick값을 지정

하는 것이다. 주의 할 사항은 timer에 이 값으로 시정수 setting이 되는가 이다.

보통은 1로 하면 무난하다.

ex) #define  SYS_CLK_RATE_MIN  1

VxWorks는 sysClkRateSet()이라는 함수를 이용해 기본적으로 주어진 60HZ의 tick

값을 program수행중에 변경 시킬수가 있다. _MAX, _MIN은 이 값의 범위를 지정하는

것이다.

간단히 말해서 _MAX, _MIN을 결정할 때 timer의 특성과 시스템의 필요에 맞게

하라는 것이다.

AUX_CLK_RATE_MAX,  AUX_CLK_RATE_MIN :

보조 timer에 관계된 것들로 main timer와 같은 내용이다.

- target_timer.c

이 program에 선언되어 있는 변수들은 timer가 동작 중 인가, 초기화는 되었는가 등

을 확인하기위한 flag로 쓰기위한 것들이 주로 있다. 이것들에 대한 자세한 설명은

생략하겠다. program을 한번만 분석하면 용도는 별 어려움없이 충분히 알 수 있다.

변수중에 sysClkTicksPerSecond와 sysAuxClkTicksPerSecond가 있는데 이 값이 60과

100으로 되어 있을 것이다. 제가 main timer의 tick이 기본적으로 60Hz라고 말한 것은

이 값을 보고 말한 것이다.

그리고 아마

#if !defined ...................... || ......................||\

!defined ...................... || ......................

.

.

.

#error missing #defines in target_Timer.c.

#endif

라는 부분이 있을 것이다.

이런 부분은 BSP의 driver들에서 많이 볼 수 있다. 목적은 대부분의 driver들이

object file형태로 제공되는데 이때 user가 driver에서 사용하는 변수들을 define해야 할

경우 이것들 놓치지 않도록 하기 위한 것이다. 지정되어 있지 않으면 complie할 때

error가 발생한다. 상관 없는 부분은 지워버리면 된다.

target_timer.c에서 사용하는 함수들은 다음과 같다.

sysClkInt(), sysClkConnect(), sysClkDisable(), sysClkEnable(), sysClkRateGet(),

sysClkRateSet(), sysAuxClkInt(), sysAuxClkConnect(), sysAuxClkDisable(),

sysAuxClkEnable(), sysAuxClkRateGet(), sysAuxClkRateSet() 그리고 time stamp에

관계된 함수들이다. time stamp에 관계된 함수들의 설명은 생략하겠다. os porting에

관계는 없으며 만약 program작성시에 timestamp기능을 요구하다면, 그때 setting하면

된다. BSP를 만들어 porting을 했다면 그정도는 충분히 할 수있다.

sysClkInt() :

timer interrupt가 걸렸을 때 뛰는 timer의 interrupt service routine이다.

대부분의 CPU가 그러하겠지만 interrupt가 걸리면 interrupt service routine에서 그

interrupt와 관련된 register를 건들여 다음번에 또 interrupt가 걸리는데 이상이 없도록

하는 부분이 있을 것이다. 이 때 timer와 관련된 이런 부분들의 program을 여기다

적어주면 된다. CPU와 timer의 특성에 따라 조금 복잡해질수 있겠지만 timer를 미리

test했다면 interrupt service routine에 적어준 내용들을 그대로 옴겨 놓으면된다.

VxWorks는 sysClkInt에서 또 한번 다른 program을 호출하는데 그 부분은 다음과

같다.

if (sysClkRoutine != NULL)

(* sysClkRoutine) (sysClkArg);

호출되는 함수는 bootConfig.c의 usrClock()이며 이 함수는 또 다시 tickAnnounce

를 호출한다. tickAnnounce는 timer interrupt 횟수를 count한다.

sysClkInt()에는 interrupt관련 처리하는 부분과 tickAnnounce를 call하는 두 부분외에는

필요없다(아마 있지도 않을 것이다).

sysClkConnect() :

bootConfig.c에서 이 함수를 call한다.

user가 고쳐야 할 부분은 없다.

그냥 그대로 두면 된다. 내용은 다음과 같다.

if (sysClkConnected == FALSE)

{

sysHwInit2 ();

sysClkConnected = TRUE;

}

sysClkRoutine = NULL;

sysClkArg	= arg;

sysClkRoutine = routine;

return OK;

sysClkDisable() :

timer의 동작을 중지 시키는 routine이다.

timer stop register를 set 시킨다던지 interrupt관련 register를 disable시켜도 된다.

sysClkEnable()

timer를 initial시키는 routine이다.

고려해야 할 사항은 time 상수를 계산해 내는 공식을 넣어야 하며 이 공식에서 최소한은

sysClkTicksPerSecond를 변수로 해서 계산식을 만들어야 한다. 이유는 sysClkRateSet()

이라는 함수를 이용하여 tick주기를 변화시킨다고 하였는데 이때 sysClkRateSet()이

넘겨받은 변수로 하는 일이 sysClkTicksPerSecond값을 제 설정하는 것이다.

검증된 timer initial program을 적어주면된다. 이때 timer interrupt를 enable시켜야 하며

intEnable()도 함께 적어 주어야한다.

sysClkEnable()함수의 내용은 다음과 같다.

if (!sysClkRunning)

{

/* timer initial program이

들어가야 할 부분 */

sysClkRunning = TRUE;

}

sysClkRateGet() :

현재 setting되어 있는 sysClkTicksPerSecond값을  return값으로 돌려 준다.

user가 고쳐야 할 부분은 없다.  sysClkRateGet()의 내용은 다음과 같다.

return sysClkTicksPerSecond;

자세한 용도를 알고 싶어면 bootConfig.c의 autoboot함수에서 쓰고 있으니 참고 바란다.

sysClkRateSet() :

위에서 몇번에 걸쳐 설명을 한바와 같이 넘겨 받은 변수로 sysClkTicksPerSecond값을

재 설정하고 sysClkEnable()를 호출하여 timer 설정을 변경한다. 이 부분도 user가

바꿔야 할 부분은 특별히 없다. sysClkRateSet()의 내용은 다음과 같다.

if (ticksPerSecond < SYS_CLK_RATE_MIN || ticksPerSecond >	SYS_CLK_RATE_MAX)

return ERROR;

sysClkTicksPerSecond = ticksPerSecond;

if (sysClkRunning)

{

sysClkDisable ();

sysClkEnable ();

}

return OK;

sysAuxClkInt(), sysAuxClkConnect(), sysAuxClkDisable(), sysAuxClkEnable(),

sysAuxClkRateGet(), sysAuxClkRateSet() :

이 함수들은 보조timer에 관계된 함수들로 programming방법은 main timer와

똑같다.

- UART controller driver 관련 program

uart를 porting 할 때도 미리 통신 port를 뚫어놓고 하면 편리하다. 기본 format은 주어져

있기 때문에 os에서 요구하는대로 맞는 위치에 이미 짜놓은 program들을 갖다 놓으면

된다. uart driver의 porting은 다른 것 보다 조금 복잡하다.

uart porting의 기본적인 개념은 uart initial, target_CallbackInstall()함수, 그리고

printf를 사용할 때 target_TxStartup를 호출하여 tx interrupt를 먼저 enable하고

printf의 내용을 tx port로 모두 출력한후 tx interrupt다시 disable시키는 것과 os에서

uart를 os에 인식시킬때 호출하는 target_Ioctl함수에 있다.

uart를 porting 할 때 고쳐할 program들은 다음과 같다.

target.h, sysSerial.c, target_sio.c, target_sio.h file들이다.

-  target

target.h file에서 고쳐야 할 부분은 위의 □BSP가 있는 경우의 porting□ 에서 설명한

UART_XTAL_FREQ, N_UART_CHANNELS, N_SIO_CHANNELS,

N_TARGET_UART_CHANNELS, TARGET_UART_BASE_ADDR 가 전부이다.

다시 한번 확인 바랍니다.

-  target_sio.h

여기에서 정의 되는 것들은 크게 3분류로 나눌 수 있다.

register나 그 내용에 관계된 것들과 각 변수들을 념겨받기위한(?) type define된 struct,

그리고 extern 선언된 몇개의 함수들이다.

register나 그 내용에 관계된 것들 :

이부분은 user가 program할 때 편리하기 위해 선언하는 부분이다.

각 변수들을 념겨받기위한 type define된 struct :

여기서 제일 중요한 변수들은  sio, *getTxChar, *putRcvChar, getTxArg, putRcvArg,

level, channelMode, baudRate들이다, 이것들은 기본적으로 가져가야 할 member

들이다. 원래 선언되어 있는 대로 두면된다. 특히 sio는 반드시 맨 앞에다 지정하라고

주석문이 붙어있을 것이다. 나머지 부분들이 더 있을 수도 있어나 그것들은

program하는 사람의 편리성에 따라 더 추가 되는 부분들이다.

extern 선언된 몇개의 함수들 :

extern 선언되는 함수는 target_int, target_DevInit 두 개이다.  target_int는 tx, rx

interrupt가 발생 했을 경우 점퍼하는 interrupt service routine이다. 만약 tx, rx의

interrupt level이 다르다면 두 개로 나누어 선언하여야한다. target_DevInit는

sysSerial.c에서 호출되어 지기 때문에 extern 선언한 것이다. 여기서 중요한 것은

함수들이 넘겨주는 argument를 위에서 선언한 struct을  변수 type한  *pChan이란

것이다. 이 규칙은 꼭 지켜야 한다.

다음은 target_sio.h file의 예문이다.

#define TARGET_R0	0x00

#define PARITY_NONE	0x00

.

.

.

typedef struct TARGET_CHAN

{

/* must be first */

SIO_CHAN	sio;

STATUS	(*getTxChar) ();

STATUS	(*putRcvChar) ();

void *	getTxArg;

void *	putRcvArg;

UINT32 *	regs;

UINT8	level;

UINT32	channelMode;

int	baudRate;

UINT32	xtal;

} TARGET_CHAN;

extern void target_int (TARGET_CHAN *pChan);

extern void target_DevInit (TARGET_CHAN *pChan);

여기서 정의된 것들을 모두다 정확하게 설명하기는 힘들지만 앞으로의 설명을

통해서 알고있는 것들에 대해서는 최대한 설명하겠다. program을 따라가다 보면

이것들의 용도를 자연스럽게 알게 되는 것도 있다.

- sysSerial.c

sysSerial.c는

target_sio.h를 include시키는 부분과

target_sio.h에서 type define된 struct을 변수 type으로 했어 선언하는 변수

sysSerialHwInit

sysSerialHwInit2

sysSerialChanGet

sysSerialReset

으로 구성되어 있다.

□type define된 struct을 변수 type으로 했어 선언하는 변수□부분은 경우에 따라서

조금 복잡한 경우가 있는 데 기본적인 개념은 h file의 type define된 struct으로

다시 변수를 하나더 선언하는 것에 불과 하다.

ex) static TARGET_CHAN targetChan;

복잡해지는 경우는 serial channel을 두 개 이상 사용할 때 배열로 선언하는 경우이다.

ex) static TARGET_CHAN  targetChan[N_UART_CHANNELS];

경우에 따라서는 변수 선언 외 struct을 하나더 선언해서 사용하는 수가 있는데 이것은

UART를 initial하기위한 변수들을 지정하기 위한 것들이니 user가 사용하기 나름이다.

sysSerialHwInit :

이 함수는 sysLib.c에서 sysHwInit을 통해 호출된다. sysHwInit은 다시 bootConfig.c

에서 호출된다.

여기서 하는 일은 serial을 initial하기 위한 변수들의 값들을 setting하는 것과 initial

위한 함수를 호출하는 것이다. 내용을 보면 알겠지만 target_sio.h에서 지정한 struct

내용의 일부들일 것이다.

대입하는 값들은 target.h에서 이미 정의한 값들이 대부분이다.

여기서 주의해야 할 것은 configAll.h file에서 선언된  CONSOLE_BAUD_RATE에

의해 target shell(hyperterminal 통해  logo message가 올라오는 곳)의 baudrate를

지정하는데 이 값을 통해 uart의 baudrate가 setting되도록 해야 한다는 것이다.

channelMode는 특별히 지정하지 않아도 상관없는데 default로 0으로 지정되고

program실행도중 다른 mode로 전환된다.( channelMode는 serial channel을 interrupt

mode로 할것이냐 polling mode로 할 것이냐를 결정하는 것인데 BSP의 다른 부분을

특별히 고치지 않는 다면 interrupt mode로 지정된다)

□targetDevInit(&targetChan)□는 serial channel을 initial하는 함수를 호출한다.

target_sio.h file에 반드시 extern으로 선언되어 있어야한다.

다음은 sysSerialHwInit 에 들어 가는 예제이다.

targetChan.regs = (UINT32 *)TARGET_UART_BASE_ADDR;

targetChan.baudRate = CONSOLE_BAUD_RATE;

targetChan.xtal = UART_XTAL_FREQ;

targetChan.level = TARGET_UART_INT_LVL;

targetChan.channelMode	= 0;

targetDevInit(&targetChan);

다시한번 말하지만 위의 선언된 내용은 CPU나 uart에 따라서 달라 질수가 있는데

기본은 uart를 initial하는 변수값을 넘겨주는 것이다.

sysSerialHwInit2 :

이 함수는 sysLib.c의 sysHwInit2에의해 호출된다.

여기서 하는 일은 serial channel의 interrupt service routine을 연결하고, VxWorks

interrupt enable 함수인 intEnable()을 실행시키는 것이다.

ex)	(void) intConnect (INUM_TO_IVEC(INT_VEC_UART_INT),

(VOIDFUNCPTR)targetInt, (int) sysSerialChanGet(0));

intEnable(TARGET_UART_INT_LVL);

여기서 주의 해야 할 것은 targetInt는 반드시 target_sio.h에서 extern 선언되어 있어야

한다. INT_VEC_UART_INT은 interrupt level값이 아니라 target.h에서 선언한 vector값

이다. 보통은 interrupt를 연결할 때 sysSerialChanGet(0) 대신에 &targetChan을 쓰는

경우가 있는데 어느 것이던 상관없다. 저는 sysSerialChanGet(0)을 권하고 싶다.

sysSerialChanGet(int channel) :

이함수는 bootConfig.h file에서 serial channel을 os에 install(?)시킬 때 불려진다.

앞 부분에서 말한바와 같이 VxWorks는 여러개의 serial channel을 지정할 수있는데

이때 target_sio.h file에서 지정한 struct의 SIO_CHAN structure의 pointer를 돌려준다.

넘겨받는 channel은 지정한 serial channel의 개수에 따라 □0□, □1□, □2□ .... 의 순서대로

불려진다. 보통은 target shell 하나만 사용함으로 □0□을 channel값으로 했어 호출되어

진다. 나머지 경우(□1□, □2□...)는  return값을 error로 돌려 더 이상의 serial channel이

없다고 알려준다.

ex)

switch (channel)

{

case 0:

return (SIO_CHAN *)&targetChan;

default:

return ((SIO_CHAN *)ERROR);

}

sysSerialReset :

이 함수는 함수이름에서 알 수있듯이 serial channel을 reinitial하는 곳이다.

일반적으로 initial 함수를 호출하도록 하면 된다. 어떤 경우에는 interrupt를 disable, enable

시키는 경우가 있는데 initial함수를 호출하도록 권하고 싶다.

ex) targetDevInit(&targetChan);

- target_sio.c

target_sio.c는 선언부, uart initial 부분, os에 install될 때 호출되는 함수, interrupt

service routine, polling mode 처리부 등으로 나누어진다.

선언부 :

이 곳은 3부분으로 나누어진다

첫째, sysSeral.c에서 넘겨 받은 변수를 이용해 uart port initial을 위한

register에 data를 read, write하기위한 define문.

보통은 다음과 같이 선언되어있다.

#define TARGET_UART_REG_READ(pChan, reg, result) \

((result) = (*(volatile UINT32 *)((UINT32)(pChan)->regs + (reg))))

#define TARGET_UART_REG_WRITE(pChan, reg, data) \

((*(volatile UINT32 *)((UINT32)(pChan)->regs + (reg))) = (data))

define내용중 □(pChan)->regs + (reg)□은 uart의 base address에 register

offset 'reg'를 더하라는 내용이다. 즉 sysSerial.c의 sysSerialHwInit에서

지정한

targetChan.regs = (UINT32 *)TARGET_UART_BASE_ADDR;

의 값을 base address로 했어 다른 uart register를 지정하겠다는 것이다.

보통 register off은 target_sio.h file에서 선언한다.

둘째, 함수 prototypes을 선언한 부분이다. 함수의 종류와 선언 내용은 다음과

같다

STATUS targetDummyCallback (void);

void targetInitChannel (TARGET_CHAN * pChan);

STATUS targetIoctl (SIO_CHAN * pSioChan, int request, int arg);

int targetTxStartup (SIO_CHAN * pSioChan);

int targetCallbackInstall (SIO_CHAN * pSioChan, int callbackType,

STATUS (*callback)(), void * callbackArg);

int targetPollInput (SIO_CHAN * pSioChan, char *);

int targetPollOutput (SIO_CHAN * pSioChan, char);

여기에 targetint와 targetDevInit는 포함되어 있지 않다.

셋째, 사용되는 driver의 함수를 struct으로 선언한 부분이다.

그 내용은 다음과 같다.

LOCAL SIO_DRV_FUNCS targetSioDrvFuncs =

{

(int (*)())targetIoctl,

targetTxStartup,

targetbCallbackInstall,

targetPollInput,

targetPollOutput

};

VxWorks에서는 위와 같이 driver의 함수들을 struct으로 선언한 것을 자주볼

수 있는데 이유는 user가 program한 부분을 os에 접목시키 위한 것이다.

좀더 자세히 설명하면 user는 program driver종류에 따라 형식에 맞게

자유로이 program하고 os는 어떤 종류의 CPU를 사용하더라도 driver control

의 정해진 명령만으로 하겠다는 것이다.

위의 그림에서 알 수 있듯이  SIO_DRV_FUNCS 의 형식은 그대로 지켜져야

한다.

target_sio.c에서 사용되는 함수들 :

targetDummyCallback

특별한 의미 없음(?).  보통의 경우 고칠것이 없으므로 그대로 두면됨

ex) return ERROR;

targetDevInit

sysSerial.c의 sysSerialHwInit에서 uart initial을 위해 호출되어진다.

여기서는 위에서 설명한 SIO_DRV_FUNCS의 pointer를 os가 지정한

pChan->sio.pDrvFunc에 넣어주고 uart initial함수를 호출한다.

targetDevInit함수 내용은 다음과 같다.

int oldlevel = intLock();

pChan->sio.pDrvFuncs = &targetSioDrvFuncs;

pChan->getTxChar = targetDummyCallback;

pChan->putRcvChar = targetDummyCallback;

pChan->channelMode = 0;

targetInitChannel (pChan);

intUnlock (oldlevel);

return;

여기서 □targetDummyCallback□을 지정하는 의미는 정확히 모르겠으나 아래의

targetCallbackInstall 함수에서 다시 지정한다.

□pChan->channelMode = 0□는 위에서 설명한 serial channel의 polling이 interrupt

mode를 지정하는 것이다. □0□은 아직 별의미 없다(?).

targetInitChannel

위의 targetDevInit에서 호출되어 진다. 이 함수는 uart channel을 initial하기위한 부분

이다. 이미 test해놓은 uart program을 target_sio.c의 앞부분에서 선언한

TARGET_UART_REG_WRITE과 TARGET_UART_REG_READ를 이용하여 고쳐

옮겨 놓으면 된다. 주의 해야 할 것은 baudrate를 setting 할 때

□pChan->baudRate□을 꼭 이용해야 한다. 이유는 configAll.h에서 지정한

CONSOLE_BAUD_RATE를 이용하기 위해서 이다. pChan->baudRate에는

sysSerial.c의 sysSerialHwInit에서 지정한 targetChan.baudRate의 값이 들어가 있다.

initial할 때 주의 해야 할 사항은 tx, rx interrupt까지 enable시켜 놓아야 한다.

VxWorks 함수인 intEnable함수는 사용할 필요는 없다.

만약 이 함수 실행후 program이 정상동작 하지 않는 다면 tx interrupt는 disable시켜

보기 바란다. CPU에 따라서 이상 동작하는 경우가 있다. tx interrupt는 나중에

targetTxStartup함수에서 다시 enable시키므로 disable 시켜놓아도 상관없다.

uart가 제대로 initial되었는지 확인하기 위해서는  tx interrup를 disable시키고

targetInitChannel함수 끝 부분에 uart의 tx register에 data를 써넣어 hyperterminal에

data가 찍히는지 보면 된다. rx 부분은 interrpt driver와 targetInt가 제대로 setting된

후에 확인바란다.

targetIoctl

이 함수는 bootConfig.c에서 serial channel을 install(?)할때와 기타의 경우(?)에

호출되어 지는데 switch ~ case문으로 이루어져 있다.

함수 앞부분에 선언해놓은

TARGET_CHAN * pChan = (TARGET_CHAN *)pSioChan

은 주의해서 지정하기 바란다.

case 문은 SIO_BAUD_SET, SIO_BAUD_GET, SIO_MODE_SET, SIO_MODE_GET,

SIO_AVAIL_MODES_GET, SIO_HW_OPTS_SET, SIO_HW_OPTS_GET, default

로 이루어져 있다.

SIO_BAUD_SET

program 내용은 다음과 같다.

if (arg < 50 || arg > 57600)

{

status = EIO;

break;

}

if (pChan->baudRate == arg)

{

return (OK);

}

oldlevel = intLock ();

.

.

/* uart initial 부분중 baudrate register setting 부분 */

.

.

pChan->baudRate = arg;

intUnlock (oldlevel);

break;

위 program의 주요 내용은 baudrate범위가 uart device가 지원 하는

범위 이상을 벗어났는가(□if (arg < 50 || arg > 57600)□)를 확인하고

이상이 없다면 configAll.h에서 지정한

CONSOLE_BAUD_RATE 값이 실질적으로 제대로 정의되었는가를 확인

한다(□if (pChan->baudRate == arg)□). 값이 재대로 지정되지 않았다면

baudrate setting을 다시한다. baudrate setting하기 전에는 interrupt

를 disable하고 setting후에는 다시 enable 시켜 주어야한다( register를

직접 setting 하여야 함).

SIO_BAUD_GET

현재 정의 되어 있는 baudrate를 가져간다.

내용은 다음과 같이 하면된다.

*(int *)arg = pChan->baudRate;

break;

SIO_MODE_SET

이 부분은 위에서 몇번 설명했던 serial channel를 polling mode

로 할것이냐 interrupt mode로 할 것이냐를 결정하는 부분이다.

program의 내용은 다음과 같다.

if ((arg != SIO_MODE_POLL) && (arg != SIO_MODE_INT))

{

status = EIO;

break;

}

oldlevel = intLock ();

if (arg == SIO_MODE_INT)

{

intEnable (pChan->level);

}

else

{

intDisable (pChan->level);

}

pChan->channelMode = arg;

intUnlock (oldlevel);

break;

program의 첫부분은 targetIoctl이 호출될 때 넘겨 받은

arg(SIO_MODE_INT의 값임)가 int mode와 polling mode 둘다

벗어날 때 error 값을 return 값으로 돌려 주는 부분이다.

넘겨 받은 arg가 SIO_MODE_INT이면 interrupt를 enabe 시키고

SIO_MODE_POLL이면 interrupt를 disable 시킨다.

intEnable과 intDisable에 들어가는 pChan->level값은 sysSerial.c

의  sysSerialHwInit에서 targetChan.level 값으로 넘겨준 것이다.

만약 tx, rx interrupt level이 각각 다르다면 즉 tx, rx interrupt의

vectror table이 다르다면 둘다 해주어야 한다.

uart device의 register직접 다루어 가며 enable, disable시킬 필요는 없다.

SIO_MODE_GET

SIO_MODE_SET에서 지정한 serial channel의 mode를 가지고 간다.

program내용은 다음과 같다. 기존에 것을 그대로 사용하면된다.

*(int *)arg = pChan->channelMode;

break;

SIO_AVAIL_MODES_GET

serial channel의 이용가능한 모든 값을 들고 간다.

int mode와 polling mode 두가지 뿐이다.

여기  program내용도 기존의 것을 그대로 쓰면된다.

*(int *)arg = SIO_MODE_INT | SIO_MODE_POLL;

break;

SIO_HW_OPTS_SET, SIO_HW_OPTS_GET는 일반적인 uart에는

사용하지 않는 부분이다. 그냥 비워나도 상관없다.

targetIoctl에서는 return값에 주의 해주기 바란다. OK값과 EIO값을 정확하게

return시켜 주어야한다(원본 BSP에서 어떤값을 돌려주었는지 그대로 따라 할 것)

targetInt

targetInt는 uart device에 rx 또는 tx interrupt 걸려을 때 뛰는 service routine이다.

다시 한번 말하지만 rx, tx interrupt의 level값 즉 vector table이 다르다면 service

routine이 구분 되어야하며 위에서 말한 interrupt에 관련된 여러가지들도 rx, tx로 나누어

setting하여야 한다.

ex) targetIntRx

targetIntTx

rx의 경우

rx, tx interrupt의 level이 같다면 분명히 interrupt가 걸렸을 때 rx, tx를

분간하는 register가 있던가 아니면 buffer register에 read하면 rx data,

write하면 tx data인 uart device일 것이다.

rx, tx의 판단에 따른 jump는 여러분의 program취향에 따라 하면된다.

targetInt함수 내에서 처리해도 되고 다른 rx, tx 함수를 두어 call해도

상관없다. 저는 이를 경우 한 함수내에서 처리한다.

rx, tx interrupt level이 다르다면 sysSerial.c의 sysSerialHwInit2에서

intConnect를 rx, tx에따라 두 개를 설정했을 것이다.

다음 program은 rx interrupt 처리 부분에 들어가야 할 program 부분이다.

물론 ch 변수는 함수에서 선언되어 있어야 한다.

ch = TARGET_UART_REG_READ(pChan, rx_buffer_register_offset, ch)

(*pChan->putRcvChar) (pChan->putRcvArg, (char)ch);

rx buffer register에서 data를 read하여  아래에서 설명할

targetCallbackInstall에서 지정한 곳에 data를 넣는 것이다.

tx의 경우

tx의 경우는 tx buffer register에 넣을 data를 가져와 tx한다.

모든 data가 tx된후에는 반드시 tx interrupt를 disable시켜야 한다.

아래는 tx interrupt처리 부분에 들어가야 할 program이다.

□gotOne과 ch□는 함수에서 선언되어있어야 한다.

gotOne = (*pChan->getTxChar) (pChan->getTxArg, &ch) != ERROR;

if (gotOne)

TARGET_UART_REG_WRITE(pChan, tx_buffer_register_offset, ch)

if (!gotOne)

{

TARGET_UART_REG_WRITE(pChan, tx_int_disable_register_offset,

disable_value)

}

interrupt 처리할 때 주의 해야 할 사항은 혹시 tx, rx interrupt후 다음 interrupt를

위해 특별히 setting해 주어야 할 동작이 있다면 interrupt service문 앞부분에

해주면 된다.

targetTxStartup

앞에서 잠깐 설명했듯이 printf같이 target shell로 data를 내어보낼때는

먼저 tx interrupt를 enable 시키고, data를 모두 전송시키고 난 후에는

위의 □tx의 경우□에서 설명한바와 같이 tx interrupt를 disable 시킨다.

이때 tx interrupt를 enable 시키기 위해 호출하는 함수가 targetTxStartup이다.

함수의 program내용은 다음과 같다.

if (pChan->channelMode == SIO_MODE_INT)

{

TARGET_UART_REG_WRITE(pChan, tx_int_enable_register_offset,

enable_value)

return OK;

}

else

return ENOSYS;

targetCallbackInstall

이 함수의 목적은 tx, rx의 data를 처리하기위한 routine과 변수(?)를 지정하는 곳이다.

함수의 program내용은 다음과 같다.

TARGET_CHAN * pChan = (TARGET_CHAN *)pSioChan;

switch (callbackType)

{

case SIO_CALLBACK_GET_TX_CHAR:

pChan->getTxChar	= callback;

pChan->getTxArg	= callbackArg;

return OK;

case SIO_CALLBACK_PUT_RCV_CHAR:

pChan->putRcvChar	= callback;

pChan->putRcvArg	= callbackArg;

return OK;

default:

return ENOSYS;

}

program의 내용은  □TARGET_CHAN * pChan = (TARGET_CHAN *)pSioChan;□

부분외에는 수정할 부분이 없다.

targetPollInput, targetPollOutput

이 함수는 serial channel이 polling mode일 때 사용하는 routine이다.

기본적으로 serial은 interrupt mode이므로 이것은 잘 사용하지않는다.

경우에 따라서 polling mode로 사용할때가 있는데 그때 setting해서 사용

해보기 바란다.

- Interrupt controller driver 관련 program

interrupt controller를 porting 할 때 기본적인 개념은 어떤 interrupt가 걸렸는가를

가리키는 register를 읽는 것과 interrupt를 enable disable 시키는 rgister를 setting

시키는 것이다. 그리고 그외에 지정해 주어야할 선언문들이 조금 많다.

고쳐야 할 file들은 target.h, sysLib.c, target_IntrCtl.c, target_IntrCtl.h이다

- target.h

여기서 선언해주는 내용들은 다음과 같다.

TARGET_INTC_BASE, TARGET_INT_PEND, TARGET_INT_ENABLE,

TARGET_INT_NUM_LEVELS, TARGET_INT_MASK, INT_LVL_, INT_VEC_,

TARGET_INT_FIRST_BIT 이다.

TARGET_INTC_BASE :

porting하고자 하는 interrupt controller device의 baseaddress이다

ex) #define 0xe0050000

TARGET_INT_PEND :

interrupt가 걸렸을 때 어떤 interrupt가 걸렸는가를 알려주는 register의 address.

보통 interrupt pending register라고 한다.

ex) #define TARGET_INT_PEND  TARGET_INTC_BASE + 0x4

TARGET_INT_ENABLE :

interrupt를 enable/disable 시키는 register의 address.

ex) #define TARGET_INT_ENABLE  TARGET_INTC_BASE + 0x8

TARGET_INT_NUM_LEVELS :

사용하는 CPU가 가지고 있는 총 interrupt 개수를 지정한다. 즉 interrupt pending

register에서 가릴킬수 있는 총 interrupt의 개수를 말한다.

그러나 보통 interrupt pending register가 32bit면 32로, 16bit면 16으로 지정한다.

개념적으로는 총 interrupt의 개수란 것을 기억하기 바란다.

ex) #define TARGET_INT_NUM_LEVELS  32

TARGET_INT_MASK :

interrupt pending register가 모두 pending되었을 경우의 값.

그러나 interrupt pending register가 32bit면 0xFFFFFFFF로, 16bit면 0xFFFF

로 지정한다.

ex) #define  TARGET_INT_MASK 0xFFFFFFFF

만약 여기서 non maskable interrupt같이 특별히 쓰고 싶지않은

interrupt가 있다면 그 부분에 해당되는 bit를 clear시켜주면된다.

예로 non maskable interrupt가 pending register의 LSB에 해당된다면

ex) #define  TARGET_INT_MASK 0xFFFFFFFE

로 해주면된다.

INT_LVL_ :

interrupt pending register의 1bit가 setting되었을 그때 해당되는 interrupt의

종류와 그 값이 지정된 해당 bit의 bit number를 10진수나타낸다.

예로 non maskable interrupt가 0번째 bit에 해당된다면

ex) #define  INT_LVL_NON_MASK  0

uart의 tx interrupt가 1번째 bit에 해당한다면

ex) #define  INT_LVL_UART_TX  1

uart의 rx interrupt가 2번째 bit에 해당한다면

ex) #define  INT_LVL_UART_RX  2

timer1의 interrupt가 12번째 bit에 해당한다면

ex) #define  INT_LVL_TIMER1  12

timer2의 interrupt가 15번째 bit에 해당한다면

ex) #define  INT_LVL_TIMER2  15

INT_VEC_ :

INT_LVL_ 을 IVEC_TO_INUM를 이용해 redefine한 것이다.

ex) #define  INT_VEC_UART_TX  IVEC_TO_INUM(INT_LVL_UART_TX)

#define  INT_VEC_UART_RX  IVEC_TO_INUM(INT_LVL_UART_RX)

#define  INT_VEC_TIMER1    IVEC_TO_INUM(INT_LVL_TIMER1)

#define  INT_VEC_TIMER2    IVEC_TO_INUM(INT_LVL_TIMER2)

TARGET_INT_FIRST_BIT  :

사용하는 interrupt들 중 interrupt pending register의 첫 bit에 해당되는 interrupt의

level 값.

0번째 bit인 non maskable interrupt를 사용하지 않고 1번째 bit인 uart tx interrupt

부터 사용한다면

ex) #define TARGET_INT_FIRST_BIT  INT_LVL_UART_TX

이다.

- sysLib.c

여기서 고쳐할 부분은 target_IntrCtl.c를 include 시켜주는 부분과 targetIntLvlMask를

정의 해주는 부분, targetIntDevInit를 extern 선언 해주는 부분이다.

target_IntrCtl.c를 include 시켜주는 부분:

함수가 시작되는 앞 부분에 있을 것이다.(program의 시작 부분이 아니고 내부에

여러개의 함수가 있는데 이 함수들이 시작되는 부분)

ex)   #include "intrCtl/target_IntrCtl.c"

targetIntDevInit를 extern 선언 해주는 부분:

target_IntrCtl.c를 include 시켜주는 부분앞에 extern 선언을 해주면된다.

ex)   IMPORT void   targetIntDevInit (void);

targetIntLvlMask를 정의 해주는 부분 :

targetIntLvlMask는 interrupt를 enable시킬 때 mask용으로 사용된다.

다음은 TARGET_INT_NUM_LEVELS가 32일 때 선언해주는 예제이다.

ex)

LOCAL UINT32 targetIntLvlMask[TARGET_INT_NUM_LEVELS + 1] =

{

0x00000000,

0x00000001, 0x00000003, 0x00000007, 0x0000000F,

0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF,

0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF,

0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF,

0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF,

0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF,

0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF,

0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF

};

- target_IntrCtl.h

interrupt controller에 관계된 것들을 정의하는데 target.h나 sysLib.c에서 선언한것으로

충분할 것이다.

- target_IntrCtl.c

target_IntrCtl.c는 선언부, initial부, interrupt service routine부, intterrupt enable/disable

부로 나뉜다.

선언부 :

이 부분의 선언내용은 일반적으로 선언된 내용을 그대로 가져가면된다.

program을 분석하면 그 용도를 알게된다. 대부분의 interrupt controller

의 선언부의 내용이 비슷하다. 그 내용은 다음과 같다.

TARGET_INT_FIRST_BIT,

TARGET_INT_VEC_BASE, TARGET_INT_REG_READ,

TARGET_INT_REG_WRITE, TARGET_INT_LVL_VEC_MAP,

TARGET_INT_PEND_LVL_MAP, TARGET_INT_ALL_ENABLED,

TARGET_INT_ALL_DISABLED, targetIntLvlCurrent,

targetIntLvlEnabled, targetIntLvlVecChk, targetIntLvlVecAck,

targetIntLvlChg, targetIntLvlEnable, targetIntLvlDisable

targetintDevInit:

이 함수는 sysLib.c의 sysHwInit2에서 호출되어진다.

대부분의 target_IntrCtl.c내의 함수들은 user가 특별히 고쳐야 할 부분은

없다. 기존에 주어진 program에 target의 이름만 바꿔주면된다.

만약 interrupt controller를 사용하기 위해 특별히 initial해 주어야 할 부분이

있다면 이 함수안에서 하면된다.

다음은 targetintDevInit에 들어가는 program의 내용이다.

sysIntLvlVecChkRtn	= targetIntLvlVecChk;

sysIntLvlVecAckRtn	= targetIntLvlVecAck;

sysIntLvlChgRtn	= targetIntLvlChg;

sysIntLvlEnableRtn	= targetIntLvlEnabled;

sysIntLvlDisableRtn	= targetIntLvlDisable;

targetIntLvlEnabled  = 0;

targetIntLvlChg (TARGET_INT_ALL_ENABLED);

위 program의 처음 5줄은 target_sio.c의 선언부 설명에서 설명한바와 같은

용도의 것이다. 즉 os에게 interrupt driver에서 사용하는 함수들의

pointer를 연결시키는 작업이다. 다음 두 줄은 interrupt의 현 상태를 초기화하기

위한 것이다.

targetIntLvlVecChk :

이 함수는 interrupt가 발생하면 interrupt vector 영역으로부터 jump해오는

interrupt service routine이다. timer나 uart의 interrupt가 이상하게 동작하거나

발생하지 않는다면 이곳으로 먼저 interrupt가 발생할 상황일 때 jump하는지

알아보면된다. 이곳으로 jump하지 않는다면 interrupt가 enable되어있지 않거나

sysLib.c의 sysHwInit2에서 intLibInit와 targetIntDevInit중의 하나가 실행되지

않았던가 함수의 argument에 이상이 있을 것이다.

interrupt가 enable되어 있고 위의 두 함수에 이상이 없다면 반드시 interrupt

발생시 이 함수(targetIntLvlVecChk)로 jump해온다.

다음은 targetIntLvlVecChk의 program내용이다.

TARGET_INT_REG_READ (TARGET_INT_PEND , isr);

isr &= TARGET_INT_MASK;

if (isr == 0)

return ERROR;

newLevel = TARGET_INT_FIRST_BIT;

for ( ; (isr & (1 << newLevel)) == 0; ++newLevel)

TARGET_INT_PEND_LVL_MAP (newLevel, newLevel);

*pLevel = targetIntLvlChg (newLevel);

TARGET_INT_LVL_VEC_MAP (newLevel, *pVector);

return OK;

먼저 interrupt pend register로부터 어떤 interrpt가 발생했는지 그 data를 읽어

들인다. 그리고 그 값이 BSP에서 허락한(TARGET_INT_MASK) interrupt인지

확인한다.

interrupt driver마다 조금다른 부분이 있을 수 있는 부분이 newLevel

값을 지정하는 부분인데 그것은 TARGET_INT_FIRST_BIT을 지정하지

않아 그렇다. 우리는 TARGET_INT_FIRST_BIT을 지정하였으므로

newLevel에 TARGET_INT_FIRST_BIT을 넣어주면된다.

그 다음 부분은 어떤 interrupt가 발생했는지 check하는 부분이다.(intConnect

에서 지정한 routine으로 jump하는 과정은 잘 모르겠음)

targetIntLvlVecAck :

특별히 고쳐야 할 부분이 없다. 그냥  targetIntLvlChg만 호출해주면된다.

ex)  targetIntLvlChg (level);

targetIntLvlChg :

interrupt를 enable disable할 때 호출되어 지며 interrupt enable register를 직접

control한다. 이 program도 특별히 고쳐야 할 부분이 없으며 사용하는 변수의 이름만

위에서 선언한 것으로 고쳐 주면된다. program내용은 다음과 같다.

int oldLevel;

oldLevel = targetIntLvlCurrent;

if (level >= 0 &&

level <= TARGET_INT_NUM_LEVELS)

{

targetIntLvlCurrent = level;

}

TARGET_INT_REG_WRITE (TARGET_INT_ENABLE,

(targetIntLvlMask[targetIntLvlCurrent] & targetIntLvlEnabled));

return oldLevel;

targetIntLvlEnable, targetIntLvlDisable :

VxWorks함수인 intEnable, intDisable을 사용할 때 호출되어 지는 함수로

넘겨 받은 변수가 지정한 level값의 영역 내의 것인지 확인하고 enable disable

관계되는 변수를 setting하고 targetIntLvlChg를 호출한다.

이 함수들도 기존에 주어진 내용을 그대로 가져가면 된다.

target_IntrCtl.c는 특별히 고쳐야 할 부분이 그렇게 많지 않다. 대부분의 내용들은 그냥

변수들의 이름만 바꿔 사용하면된다.

- LAN driver 관련 program

LAN device driver의 porting은 VxWorks에서 제공되는 LAN driver로 hardware를 설계

했다는 가정하에서 설명하겠다. 보통의 경우 LAN driver를 직접 os에 맞게 porgram

하는 것은 상당히 어려운 작업이다. VxWorks에서 어떤 LAN driver를 제공

하는 지는 tornado help의 Manuals아래 □Libraries and Drivers□에 나와 있다. 지원하는

LAN driver의 경우 if_sn, if_cpm, if_ei, if_dc등 if_ 라고 되어있다. 만약 LAN driver가

national의 sonic dp83932라면 if_sn이다. 이 if_xx를 알았다면 user가 program하는 부분

은 몇개 되지않는다. 고쳐야 program은 config.h, sysLib.c이다.

- config.h

여기서 고치는 내용은 DEFAULT_BOOT_LINE과 LAN driver를 사용하기 위한

define문이다.

DEFAULT_BOOT_LINE :

사용하는 system에 맞게 setting 해주면된다.

특히 맨 앞부분의 xx(0,0)의 xx는 알고있는 if_xx의 xx를 적어주면된다.

ex) if_sn의 경우

"sn(0,0)mscb5:vxworks h=129.254.243.126 e=129.254.243.135 u=vxtarget pw=1111

f=0x08 tn=MSCB_SA"

LAN driver는 보통 library형태로 이미 제공되어져 있는데 이것을 사용하기 위해서는

define해주어야 할 것이 하나있다. 이것을 아는 방법은 bootConfig.c에 xxattach를

검색해보면 #ifdef문안에 {  .........  }로 정의 되어 있는 것이 있다.

이 #ifdef한 문(내용이 아님)을  config.h에 define해주면 된다.

ex)

if_ie의 경우

#ifdef  INCLUDE_IE

{ "ie", ieattach, (char*)IO_ADRS_IE, INT_VEC_IE, INT_LVL_IE },

#endif

라고 되어있다.

config.h에

#define  INCLUDE_IE

라고 해줌.

보통의 경우 if_xx의 xx를 그대로 가져가므로

ex)

if_dc ->  #define INCLUDE_DC

if_enp -> #define INCLUDE_ENP

라고 해주면된다.

그리고 { ............  } 안에 정의 되어있는 내용들도 선언을 해주어야한다.

어떤 내용으로 선언해주어야 하는지는 tornado의 help file에 나와있다.

ex)    if_ie의 경우

#define INCLUDE_IE

#ifdef INCLUDE_IE

#define IO_ADRS_IE   0xe000f000

#define INT_VEC_IE   INT_VEC_EXT1

#define INT_LVL_IE   INT_LVL_EXT1

#endif

- sysLib.c

LAN driver에 관련된 tornado의 help file을 보면 몇가지 함수들을 user가 program

하라고 되어있는데 그 함수들을 sysLib.c에서 program 해주면 된다.

함수와 함수에서 요구하는 내용은 LAN driver와 사용하는 CPU마다 다르므로

설명하기가 곤란하다.  help file을 참고로 하여 program하면된다.