ARM Cortex-A 를 적용한 MCU 데이타쉬트에는 모두 NEON 명령어를 내장했다는 문구가 있다.

NEON 이 먼데...

"float 연산을 지원하는 DSP 계열의 명령어" 라고 이해하자


우선 기본적인 지식(이전 글에 언급한것 같지만...)

4-byte 크기의 레지스터는  S0, S1 ... 

8-byte 크기의 레지스터는  D0, D1 ...

16-byte 크기의 레지스터는 Q0, Q1 ...

참고로 Q0 는 D0, D1 으로 구성되었고 D0 는 S0, S1 으로 구성된다.


memcpy 함수를 neon 명령어로 작성하여 보자


void memcpy_neon_align32( char *dst, const char *src, int count )
{
    int   remain;
    char* fdst;
    const char* fsrc;

    remain = count;
    fdst   = dst;
    fsrc   = src;
	
    while( 32 <= remain ) 
    {
        asm volatile (
        ".fpu  neon\n"
        "1:                                               \n"
        "subs     %[remain], %[remain], #32               \n"
        "vld1.u8  {d0, d1, d2, d3}, [%[fsrc],:256]!       \n"
        "vst1.u8  {d0, d1, d2, d3}, [%[fdst],:256]!       \n"
        "bgt      1b                                      \n"
        : [fsrc]"+r"(fsrc), [fdst]"+r"(fdst), [remain]"+r"(remain)
        : 
        : "d0", "d1", "d2", "d3", "cc", "memory"
        );
    }  	
    //printf( "fdst=%p:%p:%d  fsrc=%p:%p:%d\n", fdst, dst, fdst-dst, fsrc, src,  fsrc-src );
	
    if ( 0 < remain ) 
        memcpy( dst, src, remain );
}

위의 함수에서 인자로 넘어오는 포인터 *dst, *src 의 주소는 모두 32바이트 정렬이 되어야 한다.

0x41235678 이런주소가 아닌 0x41235680 이렇게 0x20 단위의 주소여야 한다.


위의 함수와  memcpy() 함수를 비교하는 프로그램은 아래와 같다.


void main( void )
{
    int   tick, diff;
    char *dst, *src, *dst_sv, *src_sv;
    int   size = 1024*1024*2;
		
    dst_sv = dst = malloc( size + 32 );
    src_sv = src = malloc( size + 32 );

    // 32byte align pointer
    dst = (char *)((unsigned int)(dst + 32) & (~0x1f));
    src = (char *)((unsigned int)(src + 32) & (~0x1f));
    printf( " dst=%p:%p\n src=%p:%p\n", dst, dst_sv, src, src_sv );

    // fill memory
    memset( dst, 0xaa, size );
    memset( src, 0x55, size );
		
    tick = get_cur_usec();
    memcpy_neon_align32( dst, src, size );
    diff = get_cur_usec() - tick;
    printf( " elasped=%d   dst %02x %02x %02x %02x\n", diff, dst[0], dst[1], dst[size-2], dst[size-1] );

    // fill memory
    memset( dst, 0x44, size );
    memset( src, 0x99, size );
    
    tick = get_cur_usec();
    memcpy( dst, src, size );
    diff = get_cur_usec() - tick;
    printf( " elasped=%d   dst %02x %02x %02x %02x\n", diff, dst[0], dst[1], dst[size-2], dst[size-1] );

    free( dst_sv );
    free( src_sv );
}


10MByte 의 메모리를 복사하는 루틴이다.


자 이제 결과를 공개하면...


[root@falinux freefrug]$ ./test-neon 

 dst=0x401f7020:0x401f7008
 src=0x403f8020:0x403f8008
 elasped=4093   dst 55 55 55 55
 elasped=8333   dst 99 99 99 99

[root@falinux freefrug]$ 


4msec 와 8msec 되시겠다....

거의 2배 빠른 속도를 보여준다.


다음주에는 위에 소개된 memcpy_neon_align32() 함수를 탐구해보자