前言:
此刻同学们对“汇编语言jz指令应用实例”可能比较珍视,你们都想要知道一些“汇编语言jz指令应用实例”的相关资讯。那么小编同时在网上收集了一些关于“汇编语言jz指令应用实例””的相关内容,希望我们能喜欢,兄弟们快快来学习一下吧!3.u-boot-2016.03 修改时钟、设置内存控制器、支持UART
从第一节新建单板最后的测试结果可知,把编译好的u-boot.bin烧写到Jz2440开发板的Nor Flash并重启开发板,串口没有任何输出,在这一节我们将解决这个问题。这一节的内容主要分为三个部分:修改时钟、修改内存控制器设置、修改配置支持UART。
3.1 修改时钟
通过对u-boot-2016.03启动过程的分析,在start.S文件中发现不足,如下图所示:
从图中可知:uboot里先以60MHZ的时钟计算参数来设置内存控制器,但是MPLL 还未设置; 处理措施:把MPLL的设置放到start.S里,取消boardearlyinit_f里对MPLL的设置,同时根据新设置的时钟参数设置内存控制器。
关于Jz2440开发板SOC的时钟体系,可以阅读韦东山老师的博客掌握Jz2440_ARM芯片时钟体系。 下面修改代码,把时钟修改为FCLK=400MHz,并设置时钟比例FCLK:HCLK:PCLK=1:4:8,把上图设置时钟比例参数的代码删掉,添加如下代码:
/* 2. 设置时钟 */ ldr r0, =0x4c000014 mov r1, #0x05; // FCLK:HCLK:PCLK=1:4:8 str r1, [r0] /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */ mrc p15, 0, r1, c1, c0, 0 /* 读出控制寄存器 */ orr r1, r1, #0xc0000000 /* 设置为“asynchronous bus mode” */ mcr p15, 0, r1, c1, c0, 0 /* 写入控制寄存器 */#define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01)) /* MPLLCON = S3C2440_MPLL_200MHZ */ ldr r0, =0x4c000004 ldr r1, =S3C2440_MPLL_400MHZ str r1, [r0] /* 启动ICACHE */ mrc p15, 0, r0, c1, c0, 0 @ read control reg orr r0, r0, #(1<<12) mcr p15, 0, r0, c1, c0, 0 @ write it back3.2 修改内存控制器设置
(1) uboot原理是依据HCLK=60MHz的时钟计算参数来设置内存控制器的,修改时钟后HCLK=100MHz,所以需要重新设置内存控制器。在board/samsung/jz2440/lowlevel_init.S中最后有如下代码:
SMRDATA: .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28)) .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT) .word 0x32 .word 0x30 .word 0x30
更改为如下代码:
SMRDATA: .long 0x22011110 //BWSCON .long 0x00000700 //BANKCON0 .long 0x00000700 //BANKCON1 .long 0x00000700 //BANKCON2 .long 0x00000700 //BANKCON3 .long 0x00000740 //BANKCON4 .long 0x00000700 //BANKCON5 .long 0x00018005 //BANKCON6 .long 0x00018005 //BANKCON7 .long 0x008C04F4 // REFRESH .long 0x000000B1 //BANKSIZE .long 0x00000030 //MRSRB6 .long 0x00000030 //MRSRB7
(2) 取消boardearlyinitf里对MPLL的设置,修改 board/samsung/jz2440/jz2440.c中boardearlyinitf函数代码如下:
int board_early_init_f(void){ struct s3c24x0_clock_power * const clk_power = s3c24x0_get_base_clock_power(); struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();#if 0 /* to reduce PLL lock time, adjust the LOCKTIME register */ writel(0xFFFFFF, &clk_power->locktime); /* configure MPLL */ writel((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV, &clk_power->mpllcon); /* some delay between MPLL and UPLL */ pll_delay(4000);#endif /* configure UPLL */ writel((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV, &clk_power->upllcon); /* some delay between MPLL and UPLL */ pll_delay(8000); /* set up the I/O ports */ writel(0x007FFFFF, &gpio->gpacon); writel(0x00044555, &gpio->gpbcon); writel(0x000007FF, &gpio->gpbup); writel(0xAAAAAAAA, &gpio->gpccon); writel(0x0000FFFF, &gpio->gpcup); writel(0xAAAAAAAA, &gpio->gpdcon); writel(0x0000FFFF, &gpio->gpdup); writel(0xAAAAAAAA, &gpio->gpecon); writel(0x0000FFFF, &gpio->gpeup); writel(0x000055AA, &gpio->gpfcon); writel(0x000000FF, &gpio->gpfup); writel(0xFF95FFBA, &gpio->gpgcon); writel(0x0000FFFF, &gpio->gpgup); writel(0x002AFAAA, &gpio->gphcon); writel(0x000007FF, &gpio->gphup); return 0;}
(3) 做了以上修改后,重新make编译成功,烧写u-boot.bin到Nor Flash,重启开发板,串口输出如下:
从上图可知,串口输出的是乱码,究其原因应该是波特率设置的问题。
3.3 查看波特率的设置,解决乱码的问题
(1) 查看串口波特率的设置,在common/boardf.c有函数boardinitf,它通过数组initsequencef里的函数初始化硬件,boardinit_f函数的代码如下:
void board_init_f(ulong boot_flags){#ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA /* * For some archtectures, global data is initialized and used before * calling this function. The data should be preserved. For others, * CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack * here to host global data until relocation. */ gd_t data; gd = &data; /* * Clear global data before it is accessed at debug print * in initcall_run_list. Otherwise the debug print probably * get the wrong vaule of gd->have_console. */ zero_global_data();#endif gd->flags = boot_flags; gd->have_console = 0; if (initcall_run_list(init_sequence_f)) hang();#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \ !defined(CONFIG_EFI_APP) /* NOTREACHED - jump_to_copy() does not return */ hang();#endif}
source insight 里跳转找到这个数据,从第二节启动过程分析,去掉无关代码后的initsequencef数组如下:
static init_fnc_t init_sequence_f[] = { /* setup_mon_len函数是设置gd结构体成员gd->mon_len的函数; *在setup_mon_len函数中:gd->mon_len = (ulong)&__bss_end - (ulong)_start; *gd->mon_len等于uboot.bin大小加上bss段的大小,_start为0 *从反汇编的setup_mon_len函数可知:(ulong)&__bss_end = 0x000c636c; *所以,gd->mon_len = 0x000c636c; */ setup_mon_len, /* 1.在initf_malloc函数里,由于CONFIG_SYS_MALLOC_F_LEN没定义, * 直接返回0,相当于一个空函数 * 2.initf_console_record函数,同理 */ initf_malloc, initf_console_record,/* 空函数 */ arch_cpu_init, /* 空函数 */ /* basic arch cpu dependent setup */ initf_dm, /* 空函数 */ arch_cpu_init_dm, /* 空函数 */ mark_bootstage, /* 标记名字 *//* need timer, go after init dm */ board_early_init_f, /* 设置系统时钟,设置各个GPIO引脚 */ timer_init, /* initialize timer */ env_init, /* 设置gd的成员,初始化环境变量 *//* initialize environment */ init_baud_rate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ console_init_f, /* stage 1 init of console */ display_options, /* 打印uboot版本等信息 *//* say that we are here */ display_text_info, /* 打印uboot代码信息 *//* show debugging info if required */ print_cpuinfo, /* 打印uboot时钟频率信息 *//* display cpu info (and speed) */ announce_dram_init, /* 打印“ DRAM: ” */ /* TODO: unify all these dram functions? */ dram_init, /* 设置gd->ram_size= 0x04000000(64MB) *//* configure available RAM banks */ setup_dest_addr, /* 将gd->relocaddr、gd->ram_top指向SDRAM最顶端 */ reserve_round_4k, /* gd->relocaddr 4KB对齐 */ reserve_mmu, /* 预留16KB的MMU页表并且64KB对齐 */ reserve_trace, /* 空函数 */ /*reserve_uboot的作用是在SDRAM预留存放u-boot的空间(加上bss段) *gd->relocaddr -= gd->mon_len; *gd->relocaddr &= ~(4096 - 1); * gd->start_addr_sp = gd->relocaddr; */ reserve_uboot, /* reserve_malloc函数: * gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN; * 因为jz2440.h默认定义了CONFIG_ENV_ADDR,所以此时在include/common.h中 * 执行#define TOTAL_MALLOC_LEN (CONFIG_SYS_MALLOC_LEN + CONFIG_ENV_SIZE) * 也就是TOTAL_MALLOC_LEN=4*1024*1024+0x10000=4MB+64KB * 预留4MB+64KB MALLOC内存池 */ reserve_malloc, /* reserve_board函数: * gd->start_addr_sp -= sizeof(bd_t); 预留bd_t结构体空间,查看反汇编可知为80字节 * gd->bd = (bd_t *)gd->start_addr_sp; 指定重定位bd地址 * memset(gd->bd, '\0', sizeof(bd_t)); 清零 */ reserve_board, /*setup_machine函数: *gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */ setup_machine, reserve_global_data,/* 预留gd结构体空间,查看反汇编可知为168字节。并设置gd->new_gd */ reserve_fdt, /* 如果设置了gd->new_fdt则预留fdt设备树空间,这里没有设置,不用管 */ reserve_arch,/* 空函数 */ /* reserve_stacks函数: * gd->start_addr_sp -= 16; * gd->start_addr_sp &= ~0xf; * return arch_reserve_stacks();这里调用的不是board_f.c里的arch_reserve_stacks函数 * 因为该函数被__weak修饰符声明,调用的是arch/arm/lib/stack.c里的arch_reserve_stacks函数 * gd->irq_sp = gd->start_addr_sp; * gd->start_addr_sp -= 16; */ reserve_stacks, setup_dram_config,/* 设置gd结构体的SDRAM地址与大小 */ show_dram_config,/* 打印SDRAM信息 */ display_new_sp, /* 打印新的栈地址 */ reloc_fdt, /*没有设置设备树,忽略*/ /*setup_reloc函数: *gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;计算重定位地址与链接地址偏移值,CONFIG_SYS_TEXT_BASE在jz2440.h定义为0,gd->reloc_off = gd->relocaddr *memcpy(gd->new_gd, (char *)gd, sizeof(gd_t)); *把旧的gd复制到新的gd地址里 */ setup_reloc, NULL,};
(2) 从上面的initsequencef数组,可以找到串口初始化相关的函数serial_init,在drivers/serial/serial.c找到该函数的代码如下:
int serial_init(void){ gd->flags |= GD_FLG_SERIAL_READY; return get_current()->start();}
同样在drivers/serial/serial.c可以找到get_current函数,如下所示:
static struct serial_device *get_current(void){ struct serial_device *dev; if (!(gd->flags & GD_FLG_RELOC)) dev = default_serial_console(); else if (!serial_current) dev = default_serial_console(); else dev = serial_current; /* We must have a console device */ if (!dev) {#ifdef CONFIG_SPL_BUILD puts("Cannot find console\n"); hang();#else panic("Cannot find console\n");#endif } return dev;}
getcurrent函数可知,它返回的是一个serialdevice 结构体,该结构如下:
struct serial_device { /* enough bytes to match alignment of following func pointer */ char name[16]; int (*start)(void); int (*stop)(void); void (*setbrg)(void); int (*getc)(void); int (*tstc)(void); void (*putc)(const char c); void (*puts)(const char *s);#if CONFIG_POST & CONFIG_SYS_POST_UART void (*loop)(int);#endif struct serial_device *next;};
所以,serialinit函数是通过调用serialdevice结构体的start成员函数来初始化串口的。
(3) 从第二节的uboot启动过程分析可以知道,gd->flags是uboot是否重定位的标记,在执行 boardinitf函数时,代码还没开始重定位 ,此时gd->flags=0,所以执行的是if (!(gd->flags & GD_FLG_RELOC))分支里的dev = default_serial_console();。 在drivers/serial/serials3c24x0.c可以找到defaultserial_console函数,代码如下:
__weak struct serial_device *default_serial_console(void){#if defined(CONFIG_SERIAL1) return &s3c24xx_serial0_device;#elif defined(CONFIG_SERIAL2) return &s3c24xx_serial1_device;#elif defined(CONFIG_SERIAL3) return &s3c24xx_serial2_device;#else#error "CONFIG_SERIAL? missing."#endif}
由于CONFIGSERIAL1在jz2440.h文件被定义,所以该函数返回的是s3c24xxserial0device结构体,搜索s3c24xxserial0device可以在drivers/serial/serials3c24x0.c发现s3c24xx_serial0_device =INIT_S3C_SERIAL_STRUCTURE(0, "s3ser0");,如下图所示:
那么我们继续往下看,看看INITS3CSERIAL_STRUCTURE是如何定义的,如下图所示:
原来它是一个宏, 以上的“##”连接符在编译时被去掉,连接符内的变量被替代,最终s3c24xxserial0device被定义成如下:
s3c24xx_serial0_device = { \ .name = "s3ser0", \ .start = s3serial0_init, \ .stop = NULL, \ .setbrg = s3serial0_setbrg, \ .getc = s3serial0_getc, \ .tstc = s3serial0_tstc, \ .putc = s3serial0_putc, \ .puts = s3serial0_puts, \}
对于函数声明的宏DECLARE_S3C_SERIAL_FUNCTIONS(0);,展开后代码如下:
int s3serial0_init(void) { return serial_init_dev(0); } void s3serial0_setbrg(void) { serial_setbrg_dev(0); } int s3serial0_getc(void) { return serial_getc_dev(0); } int s3serial0_tstc(void) { return serial_tstc_dev(0); } void s3serial0_putc(const char c) { serial_putc_dev(0, c); } void s3serial0_puts(const char *s) { serial_puts_dev(0, s); }
由此可见,宏DECLARE_S3C_SERIAL_FUNCTIONS(0);一下子就声明了多个函数,现在最关心的是s3serial0_init函数,它调用serial_init_dev函数,该函数的代码如下:
/* Initialise the serial port. The settings are always 8 data bits, no parity, * 1 stop bit, no start bits. */static int serial_init_dev(const int dev_index){ /* 得到ULCON0控制寄存器基地址0x50000000 */ struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index); /* FIFO enable, Tx/Rx FIFO clear */ writel(0x07, &uart->ufcon); writel(0x0, &uart->umcon); /* Normal,No parity,1 stop,8 bit */ writel(0x3, &uart->ulcon); /* * tx=level,rx=edge,disable timeout int.,enable rx error int., * normal,interrupt or polling */ writel(0x245, &uart->ucon); _serial_setbrg(dev_index); /*设置波特率*/ return (0);}
串口输出乱码,很大原因是波特率设置不正确,所以我们需要了解 serialsetbrg函数是如何设置波特率的,serialsetbrg函数的代码如下:
static void _serial_setbrg(const int dev_index){ struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index); unsigned int reg = 0; int i; /* value is calculated so : (int)(PCLK/16./baudrate) -1 */ reg = get_PCLK() / (16 * gd->baudrate) - 1; writel(reg, &uart->ubrdiv); for (i = 0; i < 100; i++) /* Delay */ ;}
在这里终于看到有时钟相关的函数:get_PCLK() ,然后跳转到 arch/arm/cpu/arm920t/s3c24x0/speed.c 文件的get_PCLK() ,代码如下:
/* return PCLK frequency */ulong get_PCLK(void){ const uint8_t pclk_divisors[] = { 1, 2, 4, 8 }; struct syscon_regs *syscon = (struct syscon_regs *)SYSCON_BASE; const uint32_t clkset1 = readl(&syscon->clkset1); const uint8_t pclk_div = pclk_divisors[(clkset1 >> SYSCON_CLKSET1_PCLK_DIV_SHIFT) & 3]; const ulong pclk_rate = get_HCLK() / pclk_div; return pclk_rate;}
从上面的代码看,PCLK通过HCLK频得到的,那么继续看get_HCLK函数,代码如下图所示:
从上图中可以发现#ifdef CONFIG_S3C2440 这一句是黑色的,说明没有定义这个CONFIG_S3C2440; 处理措施:在include/configs/jz2440.h中去掉CONFIGS3C2410 ,换成CONFIGS3C2440,如下所示:
然后,执行如下命令重新编译:
make distcleanmake jz2440_defconfigmake
编译成功,重新烧写到开发的Nor Flash,重启开发板,串口输出信息如下图所示:
从上图可知,串口终于可以正常输出了,但是Flash和NAND都是显示0,接下来会继续修改代码使uboot支持Flash和NAND,这一节就到此结束。
标签: #汇编语言jz指令应用实例