uboot의 startarmboot 분석 2

26168 단어
주: 본고는 주 선생님의 과정 정리를 배운 노트로 uboot-1.3.4와 s5pc11x분석을 바탕으로 한다.
앞의 분석은 uboot의 start 참조armboot 분석 1
  • interrupt_init
  • int interrupt_init(void)
    {
    
        S5PC11X_TIMERS *const timers = S5PC11X_GetBase_TIMERS();
    
        /* use PWM Timer 4 because it has no output */
        /* prescaler for Timer 4 is 16 */
        timers->TCFG0 = 0x0f00;
        if (timer_load_val == 0) {
            /*
             * for 10 ms clock period @ PCLK with 4 bit divider = 1/2
             * (default) and prescaler = 16. Should be 10390
             * @33.25MHz and  @ 66 MHz
             */
            timer_load_val = get_PCLK() / (16 * 100);
        }
    
        /* load value for 10 ms timeout */
        lastdec = timers->TCNTB4 = timer_load_val;
        /* auto load, manual update of Timer 4 */
        timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | TCON_4_UPDATE;
        /* auto load, start Timer 4 */
        timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | COUNT_4_ON;
        timestamp = 0;
    
    
        return (0);
    }
    

    이름 함수는 초기화 중단과 관련이 있지만 실제로는 그렇지 않습니다. 실제로 이 함수는 타이머를 초기화하는 데 사용됩니다. (실제로는 Timer4를 사용합니다.)
    s5pv210에는 총 5개의 PWM 타이머가 있습니다.그 중에서 Timer0-timer3은 모두 대응하는 PWM 신호 출력의 발판이 있다.Timer4는 PWM 파형을 출력할 수 있도록 부착되지 않았습니다.Timer4는 디자인할 때 PWM 파형을 출력하는 데 사용되지 않는다. (트랙이 없고 TCMPB 레지스터가 없다.) 이 타이머는 타이머로 설계되었다.Timer4는 시간을 재는 데 두 개의 레지스터를 사용합니다: TCNTB4, TCNTO4.TCNTB에 저장된 숫자가 있는데, 이 숫자는 정시 횟수이다. (매번의 시간은 시계에 의해 결정되고, 사실은 2단계 시계 분주기에 의해 결정된다.)우리는 시간을 정할 때 시간/기준시간=수만 정하고 이 수를 TCNTB에 넣으면 된다.우리는 TCNTO 레지스터를 통해 시간을 0으로 줄였는지 읽을 수 있으며, 0으로 읽은 후에 정해진 시간이 이미 왔다는 것을 알 수 있다.
    interrupt_init 함수는timer4를 정시 10ms로 설정합니다.포인트는 get 입니다.PCLK 함수시스템 설정에 대한 PCLK 가져오기PSYS 시계 주파수를 설정한 다음 TCFG0과 TCFG1을 설정하여 주파수를 나눈 다음 10ms로 설정할 때 TCNTB에 기록해야 할 값을 계산하여 TCNTB에 기록한 다음 auto reload 모드로 설정한 다음 타이머를 켜서 시간을 재면 없어집니다.
    S5PC11X_TIMERS 정의는 다음과 같습니다.
    
    typedef struct {
        S5PC11X_REG32   TCFG0;
        S5PC11X_REG32   TCFG1;
        S5PC11X_REG32   TCON;
        S5PC11X_TIMER   ch[4];
        S5PC11X_REG32   TCNTB4;
        S5PC11X_REG32   TCNTO4;
    } S5PC11X_TIMERS;

    S5PC11X_GetBase_TIMERS() 정의는 다음과 같습니다.
    static inline S5PC11X_TIMERS * S5PC11X_GetBase_TIMERS(void)
    {
        return (S5PC11X_TIMERS *)ELFIN_TIMER_BASE;
    }

    구조체를 정의하는 방식으로 레지스터에 접근하고 함수를 통해 설정값을 자동으로 계산하여 타이머를 설정하는 것을 배웁니다.
  • env_init
  • int env_init(void)
    {
    #if defined(ENV_IS_EMBEDDED)
        ulong total;
        int crc1_ok = 0, crc2_ok = 0;
        env_t *tmp_env1, *tmp_env2;
    
        total = CFG_ENV_SIZE;
    
        tmp_env1 = env_ptr;
        tmp_env2 = (env_t *)((ulong)env_ptr + CFG_ENV_SIZE);
    
        crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);
        crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);
    
        if (!crc1_ok && !crc2_ok)
            gd->env_valid = 0;
        else if(crc1_ok && !crc2_ok)
            gd->env_valid = 1;
        else if(!crc1_ok && crc2_ok)
            gd->env_valid = 2;
        else {
            /* both ok - check serial */
            if(tmp_env1->flags == 255 && tmp_env2->flags == 0)
                gd->env_valid = 2;
            else if(tmp_env2->flags == 255 && tmp_env1->flags == 0)
                gd->env_valid = 1;
            else if(tmp_env1->flags > tmp_env2->flags)
                gd->env_valid = 1;
            else if(tmp_env2->flags > tmp_env1->flags)
                gd->env_valid = 2;
            else /* flags are equal - almost impossible */
                gd->env_valid = 1;
        }
    
        if (gd->env_valid == 1)
            env_ptr = tmp_env1;
        else if (gd->env_valid == 2)
            env_ptr = tmp_env2;
    #else /* ENV_IS_EMBEDDED */
        gd->env_addr  = (ulong)&default_environment[0];
        gd->env_valid = 1;
    #endif /* ENV_IS_EMBEDDED */
    
        return (0);
    }

    env_init, 이름만 봐도 환경 변수와 관련된 초기화입니다.
    왜 많은 envinit 함수, 주요 원인은 uboot가 다양한 시작 매체(예를 들어norflash,nandflash,inand,sd카드··········)를 지원하기 때문이다. 우리는 일반적으로 어디에서 시작하면 환경 변수 env를 어디에 두는지 알 수 있다.각종 미디어 액세스 조작 env의 방법은 모두 다르다.따라서 uboot는 다양한 매체에서 env의 조작 방법을 지원합니다.그래서 여러 개의 env 가 있어요.xx로 시작하는 c 파일입니다.실제 사용하는 것은 어느 것이 자신의 개발판에 사용된 메모리 매체에 따라 정해야 하는가(이 env xx.c는 동시에 1개만 작용하고 다른 것은 들어갈 수 없다. x210 sd.h에 설정된 매크로를 통해 누가 포함되었는지 결정한다). x210에 대해 우리는 env 를 보아야 한다.movi.c의 함수.
    기본 분석을 통해 이 함수는 메모리에서 유지되는 uboot의 env에 대한 기본적인 초기화 또는 판정(안에 사용할 수 있는 환경 변수가 있는지 판정)일 뿐이다.현재 우리는 아직 환경 변수를 SD카드에서 DDR로의relocate까지 진행하지 않았기 때문에 현재 환경 변수는 사용할 수 없습니다.
    startarmboot 함수에서 env 호출relocate는 SD 카드에서 DDR로 환경 변수를 재배치합니다.재배치 후 환경 변수가 필요할 경우 DDR에서 찾을 수 있으며, 재배치 전에 환경 변수를 사용하려면 SD 카드에서만 읽을 수 있습니다.
  • init_baudrate
  • static int init_baudrate (void)
    {
        char tmp[64];   /* long enough for environment variables */
        int i = getenv_r ("baudrate", tmp, sizeof (tmp));
        gd->bd->bi_baudrate = gd->baudrate = (i > 0)
                ? (int) simple_strtoul (tmp, NULL, 10)
                : CONFIG_BAUDRATE;
    
        return (0);
    }

    init_baudrate가 보기에 이름은 직렬 통신의 보폭을 초기화하는 것이다.
    getenv_r 함수는 환경 변수의 값을 읽는 데 사용됩니다.getenv 함수로 환경 변수의 "baudrate"값을 읽고 (int형이 아니라 문자열 형식으로 읽는 것을 주의하십시오) 단순strtoul 함수는 문자열을 디지털 형식의 보트율로 변환합니다.
    baudrate를 초기화할 때의 규칙은 환경 변수에서'baudrate'라는 환경 변수의 값을 먼저 읽는 것입니다.읽기에 성공하면 이 값을 환경 변수로 사용하여 gd->baudrate와 gd->bd->bi 에 기록합니다baudrate 중;읽기에 실패하면 x210 사용sd.h의 CONFIGBAUDRATE 값은 전송 속도입니다.이를 통해 알 수 있듯이 환경 변수의 우선순위는 매우 높다.
  • serial_init serial_init, 이름 보니까 직렬 초기화야.serial_init 함수는 사실 아무것도 하지 않았습니다. 왜냐하면 start.S에서 호출된 lowlevelinit.S에서는 이미 어셈블리를 사용하여 직렬 포트를 초기화하였으며, 여기서는 더 이상 하드웨어 레지스터의 초기화를 진행하지 않습니다
  • console_init_f
  • int console_init_f (void)
    {
        gd->have_console = 1;
        return (0);
    }

    console_init_f는 uboot/common/console.c는 콘솔의 첫 번째 단계 초기화입니다.단지 gd->have콘솔을 1로 설정했을 뿐 다른 일은 하지 않았다.
    _f는 1단계 초기화,r는 2단계 초기화를 나타낸다.
    때때로 초기화 함수는 한 번에 완성할 수 없고 중간에 코드를 섞어야 하기 때문에 완전한 모듈의 초기화를 2단계로 나눈다.(우리 uboot에서 start armboot 아래에 console init r 초기화가 진행됨)
  • display_banner
  • static int display_banner (void)
    {
        printf ("

    %s

    "
    , version_string); debug ("U-Boot code: %08lX -> %08lX BSS: -> %08lX
    "
    , _armboot_start, _bss_start, _bss_end); #ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */ debug("\t\bMalloc and Stack is above the U-Boot Code.
    "
    ); #else debug("\t\bMalloc and Stack is below the U-Boot Code.
    "
    ); #endif open_backlight();//lqm. //open_gprs(); return (0); }

    display_banner는 uboot 정보를 출력하는 데 사용됩니다.
    display_banner에서 printf 함수를 사용하여 크로스로 버젼 출력string 문자열입니다.그러면 위의 분석은 콘솔 을 나타냅니다.init_f가 콘솔을 초기화하지 않았는데 어떻게 printf를 할 수 있죠?
    printf의 실현을 추적하여 printf에서puts가 호출되었고puts 함수에서 현재 uboot에서console가 초기화되었는지 판단합니다.
    void puts (const char *s)
    {
        if (gbl_silent) return;
        if (gd->flags & GD_FLG_DEVINIT) {
            /* Send to the standard output */
            fputs (stdout, s);
        } else {
            /* Send directly to the handler */
            serial_puts (s);
        }
    }

    console가 초기화되면 fputs를 호출하여 직렬 송신을 완료합니다. (이 선이 컨트롤러입니다.)console가 초기화되지 않으면 Serialputs(serial putc를 다시 호출하여 직렬 레지스터를 직접 조작하여 내용을 발송함).
    콘솔도 직렬 출력을 하고 비 콘솔도 직렬 출력을 한다.콘솔이란 무엇인가?콘솔 안 쓰는 거랑 달라요?실제로 코드를 분석하면 컨트롤러는 소프트웨어로 가상된 장치로 이 장치는 전용 통신 함수(송신, 수신····)가 있고 컨트롤러의 통신 함수는 최종적으로 하드웨어의 통신 함수에 비추어 실현된다.uboot에서 실제 컨트롤러의 통신 함수는 하드웨어 인터페이스에 직접 비치는 통신 함수이다. 즉, uboot에서 사용하지 않는 컨트롤러를 사용하는 것은 본질적인 차이가 없다.그러나 다른 체계에서 컨트롤러의 통신 함수가 하드웨어 통신 함수에 비칠 때 소프트웨어로 중간 최적화를 할 수 있다. 예를 들어 버퍼 메커니즘이다.(운영체제의 컨트롤러는 모두 버퍼 메커니즘을 사용하기 때문에 때때로 printf는 내용을 보았지만 화면에서 출력 정보를 보지 못한다. 버퍼가 버퍼링되었기 때문이다. 우리가 출력한 정보는 console의 버퍼에 있을 뿐이다. 버퍼는 하드웨어 출력 장치에 새로 고침되지 않았다. 특히 출력 장치가 LCD 화면일 때).
    U_BOOT_VERSION은 uboot 소스 코드에서 정의를 찾을 수 없습니다. 이 변수는 실제로makefile에서 정의된 다음에 컴파일할 때include/versionautogenerated.h에서 하나의 매크로 정의로 이루어졌습니다.
  • print_cpuinfo
  • int print_cpuinfo(void)
    {
        uint set_speed;
        uint tmp;
        uchar result_set;
    
    #if defined(CONFIG_CLK_533_133_100_100)
        set_speed = 53300;
    ……
    #elif defined(CONFIG_CLK_1000_200_166_133)
        set_speed = 100000;
    
        printf("Any CONFIG_CLK_XXX is not enabled
    "
    ); #endif tmp = (set_speed / (get_ARMCLK()/1000000)); if((tmp < 105) && (tmp > 95)){ result_set = 1; } else { result_set = 0; } printf("
    CPU: S5PV210@%ldMHz(%s)
    "
    , get_ARMCLK()/1000000, ((result_set == 1) ? "OK" : "FAIL")); printf(" APLL = %ldMHz, HclkMsys = %ldMHz, PclkMsys = %ldMHz
    "
    , get_FCLK()/1000000, get_HCLK()/1000000, get_PCLK()/1000000); printf(" MPLL = %ldMHz, EPLL = %ldMHz
    "
    , get_MPLL_CLK()/1000000, get_PLLCLK(EPLL)/1000000); printf(" HclkDsys = %ldMHz, PclkDsys = %ldMHz
    "
    , get_HCLKD()/1000000, get_PCLKD()/1000000); printf(" HclkPsys = %ldMHz, PclkPsys = %ldMHz
    "
    , get_HCLKP()/1000000, get_PCLKP()/1000000); printf(" SCLKA2M = %ldMHz
    "
    , get_SCLKA2M()/1000000); puts("Serial = CLKUART "); return 0; }

    출력 정보는 다음과 같습니다.
    CPU:  S5PV210@1000MHz(OK)
            APLL = 1000MHz, HclkMsys = 200MHz, PclkMsys = 100MHz
            MPLL = 667MHz, EPLL = 96MHz
                           HclkDsys = 166MHz, PclkDsys = 83MHz
                           HclkPsys = 133MHz, PclkPsys = 66MHz
                           SCLKA2M  = 200MHz
    Serial = CLKUART 
  • checkboard
  • int checkboard(void)
    {
    #ifdef CONFIG_MCP_SINGLE
    #if defined(CONFIG_VOGUES)
        printf("
    Board: VOGUESV210
    "
    ); #else printf("
    Board: X210
    "
    ); #endif //CONFIG_VOGUES #else printf("
    Board: X210
    "
    ); #endif return (0); }

    checkboard 이름 보기는 개발판을 검사하고 확인하는 뜻입니다.이 함수의 역할은 현재 개발판이 어느 개발판인지 확인하고 개발판의 이름을 출력하는 것이다.
  • init_func_i2c
  • #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
    static int init_func_i2c (void)
    {
        puts ("I2C:   ");
        i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE);
        puts ("ready
    "
    ); return (0); } #endif
    #undef CONFIG_S3C64XX_I2C       /* this board has H/W I2C */
    #ifdef CONFIG_S3C64XX_I2C
    #define CONFIG_HARD_I2C     1

    왜냐하면 콘피그S3C64XX_I2C 정의가 취소되었으므로 CONFIGHARD_I2C는 정의되지 않았고, 이 함수는 실행되지 않았으며, X210의 uboot에서는 I2C를 사용하지 않았다.향후 Dell의 개발 보드에서 외부 하드웨어에 대한 I2C를 확장할 경우 x210sd.h에서 해당하는 매크로를 설정하면 열 수 있습니다.
  • dram_init
  • int dram_init(void)
    {
        DECLARE_GLOBAL_DATA_PTR;
    
        gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
        gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
    
    #if defined(PHYS_SDRAM_2)
        gd->bd->bi_dram[1].start = PHYS_SDRAM_2;
        gd->bd->bi_dram[1].size = PHYS_SDRAM_2_SIZE;
    #endif
        return 0;
    }

    dram_init 이름 보기는 DDR의 초기화에 관한 것입니다.
    어셈블리 단계에서 DDR을 초기화했습니다. 그렇지 않으면 Relocate를 두 번째 부분까지 실행할 수 없습니다. 어떻게 여기서 DDR을 초기화합니까?dram_init는 사실 gd->bd->bi 를 초기화하는 거예요.dram이라는 구조체 그룹은 DDR을 초기화하지 않았습니다.
  • display_dram_config
  • static int display_dram_config (void)
    {
        int i;
    
    #ifdef DEBUG
        puts ("RAM Configuration:
    "
    ); for(i=0; i"Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start); print_size (gd->bd->bi_dram[i].size, "
    "
    ); } #else ulong size = 0; for (i=0; ibd->bi_dram[i].size; } puts("DRAM: "); print_size(size, "
    "
    ); #endif return (0); }

    이름을 보면 dram의 설정 정보를 출력한다는 뜻입니다.
    시작 정보 중: (DRAM: 512MB) 은 이 함수에서 출력된 것입니다.
    여기서 print크기 ()는 다음과 같습니다.
    /*
     * print sizes as "xxx kB", "xxx.y kB", "xxx MB", "xxx.y MB",
     * xxx GB, or xxx.y GB as needed; allow for optional trailing string
     * (like "
    "
    ) */ void print_size (phys_size_t size, const char *s) { ulong m = 0, n; phys_size_t d = 1 << 30; /* 1 GB */ char c = 'G'; if (size < d) { /* try MB */ c = 'M'; d = 1 << 20; if (size < d) { /* print in kB */ c = 'k'; d = 1 << 10; } } n = size / d; /* If there's a remainder, deal with it */ if(size % d) { m = (10 * (size - (n * d)) + (d / 2) ) / d; if (m >= 10) { m -= 10; n += 1; } } printf ("%2ld", n); if (m) { printf (".%ld", m); } printf (" %cB%s", c, s); }

    생각: uboot 실행 중 uboot의 DDR 설정 정보를 어떻게 알 수 있습니까?uboot에 bdinfo라는 명령이 있습니다. 이 명령은 gd->bd에 기록된 모든 하드웨어와 관련된 전역 변수의 값을 출력하기 때문에 DDR의 설정 정보를 알 수 있습니다.
    DRAM bank   = 0x00000000
    -> start    = 0x30000000
    -> size     = 0x10000000
    DRAM bank   = 0x00000001
    -> start    = 0x40000000
    -> size     = 0x10000000

    여기까지, initsequence가 끝났습니다.
    이후의 분석: uboot의 startarmboot 분석 3

    좋은 웹페이지 즐겨찾기