Bootstrap

Предыдущая  Содержание  Следующая  V*D*V

Загрузчик первого уровня. Его задача: провести начальную инициализацию процессора, настроить управление памятью, скопировать кусок данных из указанного ему места из энергонезависимой памяти в ОЗУ и передать управление на этот адрес, считая, что он скопировал программу. Чаще всего в качестве этой программы используется загрузчик второго уровня U-Boot. Можно также сразу запускать ядро Linux.

 

Bootstrap также производит необходимую настройку интерфейса процессора:

PLL A и PLL B;

Частоты процессора;

шину SPI или параметры доступа к Nandflash;

 

Используем оригинальный AT91Bootstrap1.14 от Atmel.

ftp://anonymous+at91.com:sam@www.at91.com/pub/at91bootstrap

Конфигурация RAM

В файле /board/at91sam9260ek/at91sam9260ek.c в функции void hw_init(void) выставить размер шины данных RAM (если используется микросхема памяти с другой организацией, проверить и другие параметры):

 

/* Configure SDRAM Controller */

sdram_init(        AT91C_SDRAMC_NC_9  |

                 AT91C_SDRAMC_NR_13 |

                 AT91C_SDRAMC_CAS_2 |

                 AT91C_SDRAMC_NB_4_BANKS |

                 AT91C_SDRAMC_DBW_32_BITS |

                 AT91C_SDRAMC_TWR_2 |

                 AT91C_SDRAMC_TRC_7 |

                 AT91C_SDRAMC_TRP_2 |

                 AT91C_SDRAMC_TRCD_2 |

                 AT91C_SDRAMC_TRAS_5 |

                 AT91C_SDRAMC_TXSR_8,                /* Control Register */

                 (MASTER_CLOCK * 7)/1000000,        /* Refresh Timer Register */

                 AT91C_SDRAMC_MD_SDRAM);                /* SDRAM (no low power)   */ 

 

В функции void sdramc_hw_init(void) проверить инициализацию шины.

Для 32-х бит:

 

writel(0xFFFF0000, AT91C_BASE_PIOC + PIO_ASR(0));

writel(0xFFFF0000, AT91C_BASE_PIOC + PIO_PDR(0));

 

Для 16-ти бит:

 

writel(0x00000000, AT91C_BASE_PIOC + PIO_ASR(0));

writel(0x00000000, AT91C_BASE_PIOC + PIO_PDR(0));

Адаптация кода для загрузки из Dataflash

Указать используемую микросхему Dataflash: /driver/dataflash.c, функция df_init.

 

В файле /board/at91sam9260ek/dataflash/at91sam9260ek.h:

Выставить рабочую частоту и параметры PLL для кварца 12 MHz:

 

#define MASTER_CLOCK                (200000000/2)

#define PLLA_SETTINGS                0x2031BF03

#define PLLB_SETTINGS                0x10073F01

 

Понизить при необходимости частоту SPI:

 

#define AT91C_SPI_CLK                 4800000

 

Проверить адресацию микросхемы Dataflash:

 

#define AT91C_SPI_PCS_DATAFLASH                AT91C_SPI_PCS1_DATAFLASH        /* Boot on SPI NCS0 */

 

Проверить параметры копирования и запуска:

 

#define IMG_ADDRESS         0x8400

#define IMG_SIZE                0x33900

#define JUMP_ADDR                0x23F00000

 

IMG_ADDRESS:        адрес загрузки U-Boot;

IMG_SIZE:                размер копируемых данных, должен быть не меньше размера u-boot.bin;

JUMP_ADDR:        адрес старта U-Boot, см. /u-boot/board/at91sam9260ek/config.mk;

 

Проверить используемую шину данных для Nandflash в /board/at91sam9260ek/at91sam9260ek.c функция void nandflash_hw_init(void):

 

writel((AT91C_SMC_READMODE | AT91C_SMC_WRITEMODE | AT91C_SMC_NWAITM_NWAIT_DISABLE | 

          AT91C_SMC_DBW_WIDTH_EIGHT_BITS | AT91C_SM_TDF)

Адаптация кода для загрузки из Nandflash

В файле /board/at91sam9260ek/nandflash/:

Выставить рабочую частоту и параметры PLL для кварца 12 MHz:

 

#define MASTER_CLOCK                (200000000/2)

#define PLLA_SETTINGS                0x2031BF03

#define PLLB_SETTINGS                0x10073F01

 

Проверить параметры копирования и запуска:

 

#define IMG_ADDRESS         0x8400

#define IMG_SIZE                0x33900

#define JUMP_ADDR                0x23F00000

 

IMG_ADDRESS:        адрес загрузки U-Boot;

IMG_SIZE:                размер копируемых данных, должен быть не меньше размера u-boot.bin;

JUMP_ADDR:        адрес старта U-Boot, см. /u-boot/board/at91sam9260ek/config.mk;

 

Проверить используемую шину данных для Nandflash в /board/at91sam9260ek/at91sam9260ek.c функция void nandflash_hw_init(void):

 

writel((AT91C_SMC_READMODE | AT91C_SMC_WRITEMODE | AT91C_SMC_NWAITM_NWAIT_DISABLE | 

          AT91C_SMC_DBW_WIDTH_EIGTH_BITS | AT91C_SM_TDF)

 

 

Выставить параметры используемой микросхемы NandFlash. Эти же параметры надо прописать в ядре Linux. Приведены параметры для k9k8g08u0a:

 

/* These timings are specific to K9K8G08U0A (samsung) MCK = 100 MHZ */

 

#define AT91C_SM_NWE_SETUP        (1 << 0)

#define AT91C_SM_NCS_WR_SETUP        (0 << 8)

#define AT91C_SM_NRD_SETUP        (1 << 16)

#define AT91C_SM_NCS_RD_SETUP        (0 << 24)

  

#define AT91C_SM_NWE_PULSE         (2 << 0)

#define AT91C_SM_NCS_WR_PULSE        (3 << 8)

#define AT91C_SM_NRD_PULSE        (2 << 16)

#define AT91C_SM_NCS_RD_PULSE        (2 << 24)

  

#define AT91C_SM_NWE_CYCLE         (3 << 0)

#define AT91C_SM_NRD_CYCLE        (3 << 16)

#define AT91C_SM_TDF                (2 << 16)

Сторожевой таймер

По умолчанию Bootstrap его выключает, после чего его уже не включить.

Для запрета его выключения в /board/at91sam9260ek/at91sam9260ek.c в функции void hw_init(void) убрать строчку:

 

/* Disable watchdog */

//writel(AT91C_WDTC_WDDIS, AT91C_BASE_WDTC + WDTC_WDMR);

Компилятор

Используется EABI компилятор. http://www.codesourcery.com/sgpp/lite/arm/portal/subscription?@template=lite.

Используется не новее 2007q1-21. Причина: непонятное увеличение размера скомпилированного файла.

Компиляция

Сделать файл следующего содержания, положить его в папку /dataflash или /nandflash и запустить:

 

#!/bin/sh

export PATH=/<path to toolchain>/arm-2007q1/bin:${PATH}

 

make CROSS_COMPILE=arm-none-eabi-

 

Результат сборки: board/at91sam9260ek/dataflash/dataflash_at91sam9260ek.bin.

 

Внимание!

размер файла не должен превышать 4096 байта!!!

 

Смотри также

Расчёт параметров PLL

Расчёт таймингов Nandflash

Адаптация кода для прямого запуска ядра Linux

http://www.simtec.co.uk/products/SWLINUX/files/booting_article.pdf.

 

При этом метода запуска используется компрессированный образ, полученный после компиляции Linux: /<path>/arch/arm/boot/zImage.

 

Суть процесса:

KERNEL_PARAMS_ADDRESS задаёт адрес таблицы параметров ядра. Обычно это смещение 0x100 от начала физической памяти. Начало памяти отводится для хранения векторов и таблицы страниц.

JUMP_ADDRESS задаёт адрес, куда будет помещён компрессированный образ Linux (zImage). Некомпрессированное ядро не превышает 4 Мб. Для того, чтобы оно могло распаковаться, после zImage должно оставаться минимум 4 Мб свободной памяти. Общепринятым значением является смещение 0x8000 от начала памяти.

 

Параметры копирования и адреса старта и местонахождения параметров ядра /board/at91sam9260ek/dataflash/at91sam9260ek.h:

 

#define        IMG_ADDRESS                 0x42000        /* Image Address in DataFlash */

#define        IMG_SIZE                        0x210000        /* Image Size in DataFlash    */

 

#define        MACH_TYPE                        0x44B       /* AT91SAM9260-EK */

 

#define MEM_START                        0x20000000        /* RAM base address */

#define MEM_SIZE                        0x4000000        /* RAM size 64M */

 

#define        JUMP_ADDR                        0x20008000        /* kernel image address in RAM */

#define        KERNEL_PARAMS_ADDRESS        0x20000100        /* kernel parameters in RAM */

/*

#define        BOOT_ARG                        "mem=64M console=ttyS0,115200"

 

#define        BOOT_ARG                        "mem=64M console=ttyS0,115200 ubi.mtd=rootfs"

 

#define        BOOT_ARG                        "mem=64M console=ttyS0,115200 "        \

                                         "root=ubi0:rootfs ubi.mtd=rootfs \

                                         "rootfstype=ubifs rootflags=bulk_read,chk_data_crc rw"

*/

#define        BOOT_ARG                        "mem=64M console=ttyS0,115200 "        \

                                         "root=/dev/mtdblock1 "        \

                                         "rw rootfstype=jffs2"

 

Добавить в main.c функции инициализации:

 

/* list of possible tags */

#define        ATAG_NONE                0x00000000

#define        ATAG_CORE                0x54410001

#define        ATAG_MEM                0x54410002

#define        ATAG_CMDLINE        0x54410009

 

/* structures for each atag */

struct atag_header

{

 unsigned int size; /* length of tag in words including this header */

 unsigned int tag;  /* tag type */

};

 

struct atag_core

{

 unsigned int flags;

 unsigned int pagesize;

 unsigned int rootdev;

};

 

struct atag_mem

{

 unsigned int size;

 unsigned int start;

};

 

struct atag_cmdline

{

    char cmdline[1];

};

 

struct atag

{

 struct atag_header hdr;

 union

 {

         struct atag_core core;

         struct atag_mem mem;

         struct atag_cmdline cmdline;

 } u;

};

 

#define tag_next( t )     ((struct atag *)((unsigned int *)( t ) + ( t ) -> hdr.size))

#define tag_size( type )  ((sizeof(struct atag_header) + sizeof(struct type)) >> 2)

static struct atag *params;        /* used to point at the current tag */

 

static void setup_core_tag()

{

 params = (struct atag *)KERNEL_PARAMS_ADDRESS;        /* Initialise parameters to start at given address */

 

 params->hdr.tag = ATAG_CORE;                        /* start with the core tag */

 params->hdr.size = tag_size( atag_core );        /* size the tag */

 

 params->u.core.flags = 0;                        /* optional, ensure read-only */

         params->u.core.pagesize = 0;                        /* optional, systems pagesize (4k) */

         params->u.core.rootdev = 0;                        /* zero root device (typicaly overidden from commandline )*/

 

 params = tag_next( params );                        /* move pointer to next tag */

}

 

static void setup_mem_tag()

{

 params->hdr.tag = ATAG_MEM;                        /* Memory tag */

 params->hdr.size = tag_size( atag_mem );        /* size tag */

 params->u.mem.start = RAM_BASE;                /* start of physical memory */

 params->u.mem.size = RAM_SIZE;                /* memory size */

 params = tag_next( params );                        /* move pointer to next tag */

}

 

static void setup_cmdline_tag()

{

#ifdef BOOT_ARG

 char* line = BOOT_ARG "\0";                        /* place commandline into tag */

 int linelen = 0;

 for( ; *(line + linelen) != 0; linelen++ ) { params->u.cmdline.cmdline[linelen] = *(line + linelen);}

 if( !linelen )                                        /* do not insert a tag for an empty string */

     return;

 params->u.cmdline.cmdline[linelen] = 0;

 params->hdr.tag = ATAG_CMDLINE;                /* Commandline tag */

 params->hdr.size = (sizeof(struct atag_header) + linelen + 1 + 4) >> 2;

 params = tag_next(params);                        /* move pointer to next tag */

#endif /* BOOT_ARG */

}

 

static void setup_end_tag()

{

 params->hdr.tag = ATAG_NONE;                        /* Empty tag ends list */

 params->hdr.size = 0;                                /* zero length */

}

 

Добавить в main.c функцию подготовки процессора к старту (запретить прерывания, выключить кэш данных):

 

static int cleanup_before_linux()

{

 unsigned long i,old,temp;

 

 /* disable interrupts */

 __asm__ __volatile__("mrs %0, cpsr\n"

         "orr %1, %0, #0xc0\n"

         "msr cpsr_c, %1"

         : "=r" (old), "=r" (temp)

         :

         : "memory");

 

 /* turn off I/D-cache */

 asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i));

 i &= ~(C1_DC | C1_IC);

 asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i));

 

 /* flush I/D-cache */

 i = 0;

 asm ("mcr p15, 0, %0, c7, c7, 0": :"r" (i));

 

 return 0;

}

 

Заменить запуск приложения в main.c на вызовы функций:

 

/* ==================== 4th step: Start the Linux =================== */

#ifdef CFG_DEBUG

 dbg_print("set tags\r\n");

#endif        

 setup_core_tag();                /* standard core tag */

 setup_mem_tag();

 setup_cmdline_tag();        /* commandline setting root device */

 setup_end_tag();                /* end of tags */

#ifdef CFG_DEBUG

 dbg_print("cleanup\r\n");

#endif

 cleanup_before_linux();

 void (*theKernel)(int zero, int arch, unsigned int params);

 theKernel = (void (*)(int, int, unsigned int))JUMP_ADDR;

#ifdef CFG_DEBUG

 dbg_print("start kernel\r\n");

#endif

 /* Jump to kernel with register set*/

 theKernel( 0, MACH_TYPE, KERNEL_PARAMS_ADDRESS ); /* R0=0,R1=MACH_TYPE,R2=KERNEL_PARAMS_ADDRESS */

 return 0;

 

Предыдущая  Содержание  Следующая