문서를 시작하면서
여기서는 pod 가 어떻게 동작하게 되는지 좀더 자세히 살펴보도록 하겠다. 우선 하드웨어 밑단에서 부터 윗단으로, 그리고 동작 순서에 따라서 설명할 것이다.
pod 의 정의나 기본적인 설명은 생략하도록 하겠다. 관련 문서를 참조하기 바란다.
pod 삽입 전 후
먼저 우리는 어떤 시나리오를 생각해보자! 우리 앞에 어떤 셋톱박스가 있다. 그리고 거기에는 pod 슬롯이 있고, 손에는 pod 가 들려있다. 우리가 이제 할 일은 pod 를 셋톱에 꽂는 일이다.
자~ 그럼 pod 를 꽂기 전까지 셋톱에서는 어떤 일이 일어날까?
pod 가 들어왔는지 안 들어왔는지 확인한다. 루프와 폴링방식으로 감시한다. 이는 특정 핀의 입력신호(cd1,cd2)를 가지고 판단한다. |
이번에는 이렇게 생각해보자! 꽂혀있던 pod 를 뽑았다고 하자. 그럼 어떻게 될까?
그전에 어떤 수행을 하다가, pod 는 특정 핀의 입력신호(cd1,cd2)를 확인하고는 현재 수행되던 태스크를 중단하고 다시 루프와 폴링을 반복한다. |
이젠 좀 더 소프트웨어 적으로 접근해보자!~ 메인 함수에서 Cimain() 를 호출하면, 크게 다음의 작업을 수행한다.
- pod 에서 사용할 gpio 핀들을 초기화하고 open 한다. 또한 각 핀들이 input 인지 또는 output 인지 세팅한다.
- 2개의 메세지 큐를 생성한다. 이 것들은 각각의 3개의 서로 다른 태스크들이 주고 받을 수 있는 통로 역할을 한다.
- 3개의 태스크를 생성한다. 여기서는 각각 poll_task, reset_task, main_task 이다.
각각의 태스크의 역할을 설명하자면, 다음과 같다.
이름 | 역할 |
poll_task | pod 의 삽입 여부를 판단하는 태스크. 초기에 삽입되기 전이나 중간에 pod 를 뺄 때 호출 된다. 삽입 되었으면, 인터럽트로 알려주고 main_task 에 메세지 큐를 보낸다. |
reset_task | 어떤 동작을 수행했는데 예상치 못한 결과를 보냈을 때, 호출 된다. 크게 pcmcia_reset 과 pod_reset 으로 구별한다. |
main_task | poll_task 에서 메세지를 큐를 받으면, pod 의 기본 동작을 수행한다. |
main_task 수행
여기서는 우리가 pod 소켓에 꽂고 poll_task 가 수행되서 셋톱에서 인식하고, pod 가 들어왔음을 인식했다고 본다.
cistatusFlag 를 1로 세팅하고 , cistart()를 호출한다.
이제부터는 cis 를 읽기 위한 준비 작업에 들어간다. 앞에서 gpio 를 여는 과정에서 초기 값을 모든 데이터 와 어드레스 라인 게이트를 모두 껐다. 가장 먼저 card_en 핀을 low 로 떨어 뜨린다. 그러면서 어드레스 라인과 명령어 관련 게이트를 모두 연다.
그리고,
cistatusFlag 를 확인해서 pod 가 현재 reset 중인지 체크한다. 만일 buffernego 를 하던 중에 오류로 cistatusFlag 가 0x80(flagciPodReset) 이라면, podInit() 를 수행하지 않고, 바로 일정시간 동안 reset_task 로 넘겨서 pod reset 을 한다. reset 후에는 buffernego 를 다시 수행한다.
이번에는 다시 돌아와서 제대로된 루틴이었던, pod 가 reset 되지 않았을 경우를 보기로 하자. 우선 podInit() 가 수행된다.
이 함수에서는 vs1 과 pcmcia card 로 부터의 ready 신호를 체크한 뒤에 cis tuple 을 읽는다. 그리고 제대로 읽었다고 판단되면, personality change를 수행한다. 매우 중요한 함수라고 할 수 있다. ^^;
pod_init 수행
pod 는 원래 3.3V 에서 동작하게 끔 되어 있다. 만일 5V 전압을 요구하는 card 가 들어왔다면, 그것은 pod 가 아니다. 만일 사용자가 pod 가 아닌 다른 엉뚱한 카드를 넣었을 수도 있기 때문에, 이런 상황에는 바로 reset_task 로 보낸다. 이때는 gpio 핀인 pod_vs1_chk 로 체크한다. 이 핀이 high(1) 이라면, 5V 로 보고, low(0)면 3.3V 로 인식한다.
이제는 pcmcia reset 을 해주어야 한다. 내가 여기서 pod 가 아닌 pcmcia 라고 부르는 이유는 현재 진행되는 상태까지는 일반 pcmcia 카드와 동일하기 때문이다. cis read 가 성공적으로 끝나고 personality change 가 끝나야만 비로소 pod 라 부를 수 있는 것이다.
reset 은 다음의 순서로 진행된다.
- gpio pod_vcc_en 핀을 high 로 준다. 여기 부터 보드에서 직접적으로 보드에 전원을 넣어준다고 보면 된다.
- gpio pod_rst 핀을 high 로 준다.
- gpio pod_rst 핀을 low 로 준다. 하드웨어적인 신호를 줘서 pcmcia card 가 reset 되도록 한다.
보드에서 위와 같은 신호를 보내주면, 역시 pod 에서도 신호를 보내온다. 제대로 reset 이 되었다는 일종의 신호인데 바로 ready signal 이다.
보드 측에서는 ready 신호를 계속 기다린다. 루프를 돌면서.. 일정 시간이 지나면 reset_task 로 넘긴다.
자~ 이제 pcmcia card 도 reset 되었고, ready signal 도 받았다. 이제 남은 건 cis tuple read 이다.
cis tuple 무엇인가? 여기서 간단히 짚고 넘어가자!!
이 것은 pcmcia card 라면 가지고 있는 일종의 정보 영역이다. 시스템은 cis tuple 을 읽으므로써 이 card 가 어떤 설정을 가지고 있고 어떤 용도인지를 인식하게 된다.
pod 역시 마찬가지다. cis read 가 제대로 안된다면, 뒤에 과정도 제대로 안되기는 당연하다.
pod 는 8비트 모드에서 read access 를 해야한다. tuple 이라고 한 까닭은 각각의 규칙에 맞게 끔 구성되어 있기 때문이다. 구성을 간단히 보면 다음과 같다.
tuple_id | tuple 의 용도 및 목적을 구분하게 해준다. |
length | tuple 의 길이를 알려준다. 만일 값이 3 이라면, (body + end) 가 3 이라는 것이다. |
body | 이름이 맞을 지는 모르겠지만, 여기서는 body 로 부르겠다. 각각의 tuple 에 따라 설정 값들이 들어 있다. |
end | tuple 의 마지막 끝을 나타낸다. 값은 모두 FF 로 들어 있다. |
프로그램 상에서는 바로 위의 tuple 의 규칙을 이용해서 읽는다. 가장 맨 처음의 tuple (여기서는 0x0 번지이다. 이에 매핑되는 메모리 뱅크는 0x7000000 이다.) 의 id 를 읽고 여기에 대응되는 처리 루틴이 실행된다. 그리고 그 다음 (0x2 번지) 에서 tuple 의 길이를 읽고는 그 값만큼 읽는다. 이때 주소를 0x2 씩 올리면서 읽는다.
pod 의 cis tuple 의 값들은 모두 스펙에 나와 있다. 약간씩 스펙과 다른 값이 나올 수는 있지만, 정확히 일치해야 할 번지들이 있다. 바로 pcmcia type 이 0x341 로 되어 있는가? 이다.
프로그램은 각 tuple 들의 값을 화면에 뿌려준다. 그러므로 해서 제대로 읽혀졌는지 확인할 수 있다. 만일 중간 잘 못 읽었다면, 바로 cis error 가 발생하고 reset_task 로 보낼 것이다.
cis read 가 수행되면, 제대로 읽었는지 확인한다.
if( (cis_status&VERSION_PROC) && (cis_status&CONFIG_PROC) && (cis_status&CT_ENTRY_PROC) ) { if( (cis_status&VERSION_OK) && (cis_status&CONFIG_OK) && (cis_status&CT_ENTRY_OK) )
위의 소스와 같이 각각의 값들이 제대로 읽혀졌는지 확인한다. 이제는 현재의 pcmcia card 를 pod 로 변신(?) 시켜야 할 시점이 왔다. 0x70000000 번지에다가 0x41 값을 write 해야 한다.(cor write) 여기서 0x41 이라는 값은 cis tuple 로 부터 읽어 들인 TPCE_INDX 값이다. 제대로 write 가 되었다면, 이제부터는 pod mode 로 바뀌었을 것이다.
여기서 구체적으로 바뀐 것들을 설명하자면,
- 이전의 cis read 를 했던 attribute mode → io mode 로 바뀌었다. pod 는 바뀌었다. 좀 뒤에서 보드에서도 마찬가지로 io mode 로 변신할 것이다. register 세팅으로 말이다.
- 기존의 bank3 만 사용했던 것에 반해 command 채널(bank3)과 extend 채널(bank4)로 나뉘었다.
- 앞으로는 command ,status register 의 값을 어떤 특정 주소에 write 함으로써 수행된다.
pod card 가 3.3V 인지 5V 인지 역시 cis tuple 에서 읽어들인 값을 확인해서 결정해야 한다. 값이 0x2 라면, 5V 이므로 gpio pod_vpp_en 핀을 high(1) 로 만든다. 칩 U1007 에서 pod_vcc_en 는 앞에서 high(1) 로 입력이 되어있을 테고, pod_vpp_en 은 high(1) 이므로 이것의 not 인 pod_vpp_en 은 low(0) 로 입력한다.
U1007 Logic Table
vcc5_en | vcc3_en | en1 | en0 | vcc out | vpp out |
1 | 1 | 1 | 0 | 3.3 | 5 |
1 | 1 | 0 | 1 | 3.3 | 3.3 |
위의 표와 같이 출력 핀에서 전압이 나온다. pod 에서 원하는(?) 전압을 넣어 주었다면, 이제 남은 건 personality change 이다.
여기서는 gpio pod_en 핀을 low(0) 로 바꾼다. 이로써 addree line 게이트와 tsin_data 게이트를 연다.
우선 바뀐 점을 설명하면,
- a0 - a3 까지는 기존과 변동이 없지만, U1003, U1012 의 게이트가 오픈이 되면서 ctx, itx, etx, qtx, crx, drx, moclk 을 사용한다. 3125 oob 로 부터 데이터가 pod로 들어온다.
- pod 로 부터 tsin_data line 을 통해 암호화된 stream 이 들어온다.
여기까지의 수행이 이뤄졌다면, 마지막으로 return 값으로 1 이 반환된다. 만일 0 이 반환된다면, cistatusFlag 에 flagcipodReset 값이 세팅되면, 역시 reset_task 로 보낸다.
잘 따라왔다면, 50% 이상은 제대로 동작한다고 말할 수 있다.
buffernegotitaion 수행
pod physical 의 마지막 관문이라 할 수 있는 buffernego 이다. 흔히 보는 tcp/ip 의 three hand shaking 을 연상케 할 것이다. 참고로 여기서 이해가 편하도록 command , status register 의 설명을 하겠다.
command register
DAIE | pod 가 da 에 대한 interrup 가 가능하도록 허용하는 bit |
FRIE | pod 가 fa 에 대한 interrup 가 가능하도록 허용하는 bit |
R | 관련없음 |
R | 관련없음 |
RS | interface reset |
SR | module 의 최대허용 buffer size 를 request |
SW | host 와 module 사이에 사용하기로 약속한 buffer size 전송시 |
HC | data write sequence 를 시작할 때 set |
status register
DA | module 이 보낼 data 를 가지고 있을 때 set |
FR | reset 후 또는 module 이 data 를 받을 준비가 되었을 때 set |
R | 관련없음 |
R | 관련없음 |
R | 관련없음 |
R | 관련없음 |
WE | 정상적으로 data 를 read / write 하고 있을 때 set |
RE | 관련없음 |
우선 대략적인 수행 순서를 살펴보면 다음과 같다.
- command 채널의 reset (bank 3 의 0x70000000 에다가 command register 의 rs bit 값인 0x8 write)
- extended 채널의 reset (bank 4 의 0x7f000000 에다가 command register 의 rs bit 값인 0x8 write)
- command 채널의 clear (bank 3 의 0x70000000 에다가 command register 의 rs bit 값인 0x0 write)
- extended 채널의 clear (bank 4 의 0x7f000000 에다가 command register 의 rs bit 값인 0x0 write)
- command 채널에 fr flag 가 들어왔는 지 확인 (bank 3 의 0x70000000 에다가 status register 의 fr bit 값인 0x40 read)
- extended 채널에 fr flag 가 들어왔는 지 확인 (bank 4 의 0x7f000000 에다가 status register 의 fr bit 값인 0x40 read)
- command 채널에 sr bit write ( bank 3 의 0x70000000 에다가 command register 의 sr bit 값인 0x04 write)
- command 채널에 da bit 가 들어왔는 지 확인 (bank 3 의 0x70000000 에다가 status register 의 da bit 값인 0x80 read)
- pod 모듈과 host 간에 buffer 를 맞추기 위해, mbuf_size[] 에 0x0 값을 넣는다. ( 2개의 mbuf_size[0,1] 에 모두 0x1 을 쓴다)
- sr bit 를 clear 한다. (write 0x0)
- bsize 에다가 257 을 입력한다. ( bsize = mbuf_size[1]|((mbuf_size[0]«8)&0xff00); ) 256 보다 크다면, bsize 를 256 으로 한다.
- mbuf_size[0,1] 각각 1 과 0 을 넣는다.
- command 채널에 sw bit write ( bank 3 의 0x70000000 에다가 command register 의 sw bit 값인 0x02 write)
- command 채널에 fr flag 가 들어왔는 지 확인 (bank 3 의 0x70000000 에다가 status register 의 fr bit 값인 0x40 read)
- sw bit 와 hc bit 를 or 연산한 0x3 값을 bank 3 에다가 write
- commnad 채널의 size register(ls) 에 0x02 값을 쓴다. 그리고 size register(ms) 에 0x00 값을 쓴다.
- bank 3 0x70000000 에다가 0x1 쓰고, 바로 0x0 값을 쓴다.
- command 채널에 0x70000001 에다가 0x00 값을 쓴다.
여기까지가 command 채널에서의 buffernego 이다. 이번에는 extended 채널에서의 buffernego 를 해주어야 한다.
- extended 채널에 sr bit write ( bank 4 의 0x7f000000 에다가 command register 의 sr bit 값인 0x04 write)
- extended 채널에 da bit 가 들어왔는 지 확인 (bank 4 의 0x7f000000 에다가 status register 의 da bit 값인 0x80 read)
- pod 모듈과 host 간에 buffer 를 맞추기 위해, mbuf_size[] 에 0x0 값을 넣는다. ( 2개의 mbuf_size[0,1] 에 모두 0x0 을 쓴다)
- sr bit 를 clear 한다. (write 0x0)
- bsize 에다가 0 을 입력한다. ( bsize = mbuf_size[1]|((mbuf_size[0]«8)&0xff00); ) 256 보다 크다면, bsize 를 256 으로 한다.
- mbuf_size[0,1] 각각 1 과 0 을 넣는다.
- command 채널에 sw bit write ( bank 3 의 0x70000000 에다가 command register 의 sw bit 값인 0x02 write)
- command 채널에 fr flag 가 들어왔는 지 확인 (bank 3 의 0x70000000 에다가 status register 의 fr bit 값인 0x40 read)
- sw bit 와 hc bit 를 or 연산한 0x3 값을 bank 3 에다가 write
- commnad 채널의 size register(ls) 에 0x02 값을 쓴다. 그리고 size register(ms) 에 0x00 값을 쓴다.
- bank 3 0x70000000 에다가 0x1 쓰고, 바로 0x0 값을 쓴다.
- command 채널에 0x70000001 에다가 0x00 값을 쓴다.
이제 command 채널과 extended 채널의 buffernego 가 끝났다.
이제는 상위 link layer 로 전달해주어야 한다.
ciLinkStart(); ciTransportStart(); ciSessionStart(); ciResourceStart(); podAppInit();
이것으로 길고긴(?) pod physicall 에 대한 설명을 마치겠다.