디버깅을 하다 보면 PLL 레지스터를 설정한다는 말은 자주 나옵니다. CPU Frequency변경할 때 쓰이는 걸로 봐서는 Clock을 변경하는 용도로 쓰이는 것이라는 건 알겠는데, 정확히 무엇일까요?
먼저 PLL은 영어로 Phase Locked Loop군요. Phase를 잡아주는 Loop? 아, 이 단어만 가지고는 이해하기 힘드네요. 일단, 잘 정리해놓은 사이트가 있어서, 여기를 참조해봅니다.
참조: http://egloos.zum.com/recipes/v/5139503
저는 SW 엔지니어니까 너무 깊이 이론까지 들어가고 싶지는 않구요. 하여간에 정리하면,
- Clock을 다른 Frequency로 변경할때 VCO(Voltage Controlled Oscillator)라는 놈을 쓰면 전압에 따라서 가변적으로 Frequency를 변경할 수 있습니다.
- 그런데 문제는 이렇게 Frequency를 변경할때 정확하게 원하는 Frequency를 얻기가 힘든데, 이 출력을 체크해서 원하는 Frequency가 되도록 VCO에 들어가는 전압을 재조정해줍니다.
결국 PLL은 특정 클럭을 이용해서 내가 원하는 Frequency를 안정적으로 만들어내주는 회로입니다. 그러니까 CPU Frequency를 포함한 다양한 Clock 변경에 사용할 수 밖에 없겠네요..
아 잠깐,
실제로 디버깅을 하다보면, PLL 관련 문제는 Settling Time에 관련된 경우가 많은데요. 생각해보면 "Frequency를 변경한다" 는 동작은 한 순간에 되는 것이 아닙니다. 실제로는 PLL을 통해서 출력되는 주파수를 보정하는 시간이 필요하고 이를 Settling Time이라고 부릅니다. 물론 이 Settling Time중에는 원하는 주파수가 보장이 되지는 않을 것이고, 안정적인 동작을 위해서는 Settling Time만큼은 기다려야 하겠죠. 사실 Clock변경 주변에서 일어나는 많은 문제들은 이 기다리는 시간을 늘리는 것만으로 해결되는 경우가 많답니다.
2016년 10월 9일 일요일
2016년 10월 4일 화요일
네온(Neon)이 대체 뭐지?
이제 LD2 인스트럭션은 이해했습니다만, 애초에 v0, v1 같은 레지스터는 왜 있는 걸까요?
대체 x1, x2 같은 generic purpose register와는 무엇이 다른 걸까요?
이걸 이해하려면 이제 ARM사에서 말하는 NEON이 뭔지 알아야 할것 같습니다.
SIMD라는 말이 먼저 나오는데요. SIMD란 Single Instruction Multiple Data를 줄인 말입니다. 한 마디로 인스트럭션 하나로 Data를 여러개 다룰 수 있다는 말이죠.
그럼 이게 왜 필요할까요?
ARM은 32비트 프로세서 (AArch32)에서 현재는 64비트 프로세서 모드(AArch64)를 지원하고 있습니다. 이게 무슨 말이냐 하면, 한번에 다루는 Data의 Address나 크기가 32bit혹은 64bit까지 한꺼번에 다루는 것이 가능하다는 뜻이죠.
그런데 문제는, 많은 경우에 (특히 멀티미디어 관련 - 이를 테면 오디오 코덱 이나 그래픽 관련에서 함수들) 이것 보다 작은 크기의 데이터를 대량으로 다루는 경우가 많다는 겁니다.
가령 이런 함수에서 많은 양의 8비트 데이터를 엄청나게 연산해야 하는데, CPU는 한꺼번에 64비트를 처리할 능력이 있는데, 고작? 8비트의 데이터를 복사 혹은 연산하기 위해서 한개 인스트럭션을 소모해야 한다면, CPU를 효율적으로 사용하고 있다고 말하기 힘들겠죠.
이런 비효율의 문제를 해결하기 위해서, ARM社에서는 ARMv6에 작은 크기에 데이터를 여러개 처리하도록 해주는 기본적인 SIMD 명령어를 추가하게 됩니다. 이때는 8비트 혹은 16bit로 값들을 generic purpose register에 packed한 형태로 연산하도록 지원을 했었고, ARMv7이후에는 이 개념을 더 확장해서 이런 operation을 위해서 vector register를 두고, 명령어 세트와 해당 Architecture를 합쳐서 네온 (NEON) 이라고 부르게 됩니다. 현재는 모든 Cortex-A 프로세서에서 지원하고 있습니다..
Cortex-A8 프로세서에서 등장한 개념이니까 아주 최신 개념이라고 하기는 힘듦니다만, 그래도 다른 명령어에 비해서 생소하다고 할수는 있겠죠.
현재 NEON에서는 지원하는 operation에는 크게 4가지가 있습니다.
- 메모리 Access (load or store)
- Vector 레지스터간 혹은 Vector레지스터와 General Purpose레지스터간 데이터 복사
- Data Type Conversion
- Data Processing (add, substract, multiplication, shift...)
Vector 레지스터는 Vxx 형태로 불리고, 사용시에는 크기를 지정하는 이름과 함께 사용됩니다.
- Q - quad word - 128 bit
- D - double word - 64 bit
- S - single word - 32 bit
- H - half word - 16 bit
- B - byte - 8 bit
이를 테면 Vn.16B는 8비트 크기의 element 16 lane을, Vn.4S라고 하면 32bit 크기의 element 4 lane을 가리키게 됩니다.
그리고 혼동하지 말아야 할 것은, 실제로 V0.2D 나 V0.4S, V0.8H, 혹은 V0.16B는 실제로 같은 register 이지만, operation하는 단위가 달라지게 됩니다..
예를 들어, 아래 코드에서 보면 x15, x16, x17, x18의 주소에서 값을 읽어서 v0~v7 regiseter로 (각각 128비트) 복사하게 되는데,
ld2 {v0.4s, v1.4s}, [x15]
ld2 {v2.4s, v3.4s}, [x16]
대체 x1, x2 같은 generic purpose register와는 무엇이 다른 걸까요?
이걸 이해하려면 이제 ARM사에서 말하는 NEON이 뭔지 알아야 할것 같습니다.
SIMD라는 말이 먼저 나오는데요. SIMD란 Single Instruction Multiple Data를 줄인 말입니다. 한 마디로 인스트럭션 하나로 Data를 여러개 다룰 수 있다는 말이죠.
그럼 이게 왜 필요할까요?
ARM은 32비트 프로세서 (AArch32)에서 현재는 64비트 프로세서 모드(AArch64)를 지원하고 있습니다. 이게 무슨 말이냐 하면, 한번에 다루는 Data의 Address나 크기가 32bit혹은 64bit까지 한꺼번에 다루는 것이 가능하다는 뜻이죠.
그런데 문제는, 많은 경우에 (특히 멀티미디어 관련 - 이를 테면 오디오 코덱 이나 그래픽 관련에서 함수들) 이것 보다 작은 크기의 데이터를 대량으로 다루는 경우가 많다는 겁니다.
가령 이런 함수에서 많은 양의 8비트 데이터를 엄청나게 연산해야 하는데, CPU는 한꺼번에 64비트를 처리할 능력이 있는데, 고작? 8비트의 데이터를 복사 혹은 연산하기 위해서 한개 인스트럭션을 소모해야 한다면, CPU를 효율적으로 사용하고 있다고 말하기 힘들겠죠.
이런 비효율의 문제를 해결하기 위해서, ARM社에서는 ARMv6에 작은 크기에 데이터를 여러개 처리하도록 해주는 기본적인 SIMD 명령어를 추가하게 됩니다. 이때는 8비트 혹은 16bit로 값들을 generic purpose register에 packed한 형태로 연산하도록 지원을 했었고, ARMv7이후에는 이 개념을 더 확장해서 이런 operation을 위해서 vector register를 두고, 명령어 세트와 해당 Architecture를 합쳐서 네온 (NEON) 이라고 부르게 됩니다. 현재는 모든 Cortex-A 프로세서에서 지원하고 있습니다..
Cortex-A8 프로세서에서 등장한 개념이니까 아주 최신 개념이라고 하기는 힘듦니다만, 그래도 다른 명령어에 비해서 생소하다고 할수는 있겠죠.
현재 NEON에서는 지원하는 operation에는 크게 4가지가 있습니다.
- 메모리 Access (load or store)
- Vector 레지스터간 혹은 Vector레지스터와 General Purpose레지스터간 데이터 복사
- Data Type Conversion
- Data Processing (add, substract, multiplication, shift...)
Vector 레지스터는 Vxx 형태로 불리고, 사용시에는 크기를 지정하는 이름과 함께 사용됩니다.
- Q - quad word - 128 bit
- D - double word - 64 bit
- S - single word - 32 bit
- H - half word - 16 bit
- B - byte - 8 bit
이를 테면 Vn.16B는 8비트 크기의 element 16 lane을, Vn.4S라고 하면 32bit 크기의 element 4 lane을 가리키게 됩니다.
그리고 혼동하지 말아야 할 것은, 실제로 V0.2D 나 V0.4S, V0.8H, 혹은 V0.16B는 실제로 같은 register 이지만, operation하는 단위가 달라지게 됩니다..
예를 들어, 아래 코드에서 보면 x15, x16, x17, x18의 주소에서 값을 읽어서 v0~v7 regiseter로 (각각 128비트) 복사하게 되는데,
ld2 {v0.4s, v1.4s}, [x15]
ld2 {v2.4s, v3.4s}, [x16]
ld2 {v4.4s, v5.4s}, [x17]
ld2 {v6.4s, v7.4s}, [x18]
ld2 {v6.4s, v7.4s}, [x18]
x15입장에서 보면, 첫번째 single word (32bit)는 v0로, 두번째 single word(32bit)는 v1, 세번째 single word(32bit)는 v0의 두번째 word로, 내번째 single word(32bit)는 v1의 두번째 word로 복사되게 됩니다.
LD2 명령어
ARM 어셈블리 레벨에서 디버깅을 하다 보면 아래와 같은 낯선 어셈블리를 만날때가 있습니다. ARM 디버깅을 꽤 오래 했다고 생각합니다만, 이런 명령은 아직도 잘 와닫지 않네요.
ld2 {v0.4s, v1.4s}, [x15]
여기서 [x15]는 알겠는데, 나머지는 정확히 모르겠네요. 물론 LD라는 걸로 봐서는 Load일것 같고 [x15]에서 읽어서 v0로 넣으라는 건 대강 알겠습니다만... 이번 기회에 한번 제대로 알아봐야겠어요. 사실 디버깅 하다 보면 이런 인스트럭션도 자주 만나게 되거든요.
먼저 ld2가 무엇인지 찾아봅니다. ARM reference 사이트에는 이렇게 나와있습니다.
LD2 (vector, single structure)
ld2 {v0.4s, v1.4s}, [x15]
여기서 [x15]는 알겠는데, 나머지는 정확히 모르겠네요. 물론 LD라는 걸로 봐서는 Load일것 같고 [x15]에서 읽어서 v0로 넣으라는 건 대강 알겠습니다만... 이번 기회에 한번 제대로 알아봐야겠어요. 사실 디버깅 하다 보면 이런 인스트럭션도 자주 만나게 되거든요.
먼저 ld2가 무엇인지 찾아봅니다. ARM reference 사이트에는 이렇게 나와있습니다.
LD2 (vector, single structure)
Load single 2-element structure to one lane of two registers.
LD2 {
Vt
.S, Vt2
.S }[index
], [Xn|SP
] ; 32-bit
설명을 보니 추즉했던 대로 [x15]에서 값을 읽어서, {v0.4s, v1.4s}에 넣는다는 거네요?
근데 v0.4s 랑 v1.4s는 또 뭘까요? 보통 LDR 인스트럭션이었다면 이부분에 register 가 왔을테니깐, register 일텐데, x15같은 generic purpose register는 아니고, 정확히 무엇을 가르키는지 모르겠네요.
그래서 다시 v0.4s 가 무엇인지 찾아봅니다. ARM reference사이트에서 이 페이지가 유용한 정보를 갖고 있네요.
오호라, 이 문서를 읽어보니깐, Vt 어째고 하는 것은 Vector register를 가리키는 것이고, 뒤에 오는 4s 라는 것은 해당 Vector register를 4개짜리 32bit element로 나눈 것을 가리키는 말이네요.
그럼 정리해보면, 결국 이 인스트럭션은 [x15]에서 값을 읽어서 V0과 V1의 각각 4개씩 32bit element로 복사하는 것이군요. 다시 말하자면, [x15]에서 값을 읽어서 128bit씩 V0과 V1에 복사하게 됩니다.
뭐 별로 어렵지는 않네요.
피드 구독하기:
글 (Atom)