• 发文章

  • 发资料

  • 发帖

  • 提问

  • 发视频

创作活动
0
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
返回

电子发烧友 电子发烧友

  • 全文搜索
    • 全文搜索
    • 标题搜索
  • 全部时间
    • 全部时间
    • 1小时内
    • 1天内
    • 1周内
    • 1个月内
  • 默认排序
    • 默认排序
    • 按时间排序
大家还在搜
  • 「正点原子STM32Mini板资料连载」第三十二章 内存管理实验

    号,获取更多资料:正点原子第三十二章 内存管理实验上一章,我们在 STM32 FLASH 写入的时候,需要一个 1024 字节的 16 位数组,实际上占用了 2K 字节,而这个数组几乎只能

    2020-04-13 12:06

  • 「正点原子NANO STM32开发板资料连载」第三十二章 DSP 测试实验

    1)实验平台:alientek NANO STM32F411 V1开发板2)摘自《正点原子STM32F4 开发指南(HAL 库版》关注官方微信号公众号,获取更多资料:正点原子第三十二章 DSP 测试

    2020-04-23 15:13

  • 光变声?为何?如何

    为何?今年智能车竞赛中的信标组的导引信号发生了改变,由原来的光,变成了声(还有射频信号),除了传承智能车竞赛赛题永远在变化的传统,阻断…(此处省略三千字)。信标灯内部的控制板和灯盘主要变化包括

    2021-07-22 10:11

  • 零基础学FPGA( 三十二) 写在京城,多级FIR半带滤波器的FPGA实现

    ` 每次到京城来总不能忘了出去逛逛吧,偌大的北京城去哪呢?炙热的大太阳烤的哪都不想去了,幸好这次有亲戚来北京旅游,搭个顺风车便出去转了一下。这次的闲逛可没有上次那么感叹,上次主要是去的高校,什么清华北大确实比一般学校壮观哈~也是多少人梦寐以求的高等学府。可惜只能作为一名游客的身份来逛一下清华校园了。 北京旅游的地方还是不少的,其实一直想去长城看一下,所谓不到长城非好汉,可是这么热的天,真的是到了长城一身汗了~听说前门大街不错,小吃很多,虽然在长沙吃惯了各种南方小吃,来北京尝一下北京特色还是不错的。一路上最火的算是那个全聚德烤鸭了吧,这么热的天,还有那么多人排队在那等着买,怪不得上次我从北京走的时候在特产店里买了两只真空包装的不怎么好吃,什么东西都还得到旗舰店买哈~ 早就听说前门大街前几年刚开了一家杜莎夫人蜡像馆,也是即上海武汉之后的第三家了吧?上次去上海的时候很遗憾路过没进去,这次既然就在眼前怎么能错过~虽然门票贵了点,但是跟普京大帝合个影发一下朋友圈还是挺爽的哈~ 下面送上梅西蜡像一张,感觉还是挺像的,其实里面有些人物蜡像还是跟真人差别很远的,比如杰伦.... 好了,步入正题,这篇文章写一下关于FIR半带滤波器,并且采用Quartus II给我们提供的强大的FIR IP核进行设计并进行仿真。这篇文章写的很简单,内容不多,做起来也不复杂,简单记录一下我做的过程吧。一、关于FIR半带滤波器首先再来说一下 FIR 半带滤波器的概念,之所以叫他FIR半带滤波器,是因为它本身具有FIR滤波器的本身具有的一些性质,其次它的幅频响应跟一般的FIR滤波器比起来有自己的特点,其实说白了,FIR半带滤波器就是一种特殊的FIR滤波器,它的具体特点概括如下:1、FIR半带滤波器的通带宽度与阻带宽度相同,并且它的通带容限与阻带容限也相同,这样说来也就是说,通带与阻带是关于π/4对称的,这也是半带滤波器这个名字的由来。2、FIR半带滤波器的长度为奇数,除零点外,其他偶数序号的单位脉冲响应为0,零点值为0.5,例如:FIR半带滤波器的单位脉冲响应为 -0.20-0.10.50.100.2从这个特点可以看出,FIR半带滤波器的系数有一部分是0,因此,在进行卷积运算的时候也就省去了部分乘法运算和加法运算,这就使得FIR半带滤波器的运行速度大幅度提升,占用的硬件资源大幅度减小。 3、FIR半带滤波器在进行两倍抽取的时候,信号的通带内没有混叠,但是在过渡带内存在混叠二、多级FIR半带滤波器 从上面的几个特征我们可以看出来,由于FIR半带滤波器的通带跟阻带对称,那么,当通带较小的时候,阻带也较小,也就是意味着过渡带将会很长。举个例子,假如有用信号的通带截止频率是100hz ,我们的采样频率是10khz ,那么我们的FIR半带滤波器的过渡带是多少?就是10k - 100*2hz的带宽,因此,FIR半带滤波器不能作为最后一级滤波器。 什么意思呢? 因为FIR半带滤波器的幅频特征非常适合作为2倍抽取的滤波器,再加上其单位脉冲响应有将近1半的系数为0,速度快,占用资源少,所以,我们经常将多个FIR半带滤波器级联起来,做多倍抽取操作。但是由于FIR半带滤波器的过渡带较宽,因此不适合作为最后一级滤波器。再举上面那个例子,有用信号的通带截止频率为100hz ,原始采样频率为10khz,现在将其通过2级FIR半带滤波器,滤波后的采样频率为10k/4 =2500hz。假如最后一级滤波器,也就是第3级滤波器还采用FIR半带滤波器,那么滤波后的采样频率为1250hz,通带截止频率依然为100hz,根据FIR半带滤波器的性质,此时最后一级滤波器的阻带截止频率就为1250/2 -100 = 525hz,过渡带为525 -100 = 400hz 。好了现在假设干扰信号的频段在300hz该怎么办?这样的话FIR半带滤波器就不能将干扰信号滤除掉。所以,FIR半带滤波器不能作为最后一级滤波器使用,最后一级滤波器依然用普通FIR抽取滤波器,虽然阶数较大,但是可以保证信号正确的输出。三、FIR半带滤波器的Matlab设计我们通过一个例子来设计这样一个多级FIR半带滤波器假设我们的合成信号是由100,300和600hz的正弦波相加而成的信号,并将其进行8bit量化,初始采样频率为6400hz,现在我需要将采样信号降为800hz,并要求获得100hz有用信号,要求滤波器的通阻带容限为0.1,设计这样一个FIR半带滤波器。 1、首先我们需要进行需求分析,原始采样频率为6400,要降为800,那么就需要3级2倍抽取滤波器 2、我们需要的是100hz的信号,因此需要将其他信号滤除,根据采样定理,我们降频后的频率为800hz,那么采样频率的一半为400hz,因此我们确定,最后一级滤波器的通带截止频率为100hz,阻带截止频率为300hz ,当然前两级FIR半带滤波器只需要考虑通带截止频率就好了,即100hz3、要求通阻带的容限为0.1,那么前2两级FIR半带滤波器的通阻带容限为0.1/3 ,最后一级普通FIR抽取滤波器的通阻带容限为0.14、可以采用matlab为我们提供的firhalfband函数来设计半带滤波器,设计过程很简单,函数直接返回FIR半带滤波器系数。关于firhalfband函数的使用大家可以通过matlab的help功能进行自己学习,这里的用法就是只需要给出FIR半带滤波器的通带截止频率和通阻带容限即可,前面的参数minorder意思是函数可以根据后面两个参数,即通带截止频率相对于采样频率一半的归一化频率跟通阻带容限,自动计算滤波器最小阶数根据FIR半带滤波器的特点我们知道,FIR半带滤波器的单位脉冲响应是偶数序号的系数为0,我们通过MATLAB函数直接调用生成的滤波器系数见下图,可见实际情况跟理论相符。前面说了,FIR半带滤波器由于过渡带较宽,不适合作为最后一级滤波器,最后一级滤波器还是用我们之前说过的普通滤波器设计方法,一般我们设计成最优滤波器的形式既然FIR半带滤波器作为一种特殊的FIR滤波器,设计过程其实也跟之前说过的FIR滤波器差不多,先来回顾一下前面设计过的FIR滤波器。之前我们是先用MATLAB软件像上面那样,写出滤波器的设计指标,然后直接调用函数生成滤波器系数即可,然后我们手动将滤波器的系数用Verilog 语言放到FPGA中跟输入数据进行卷积运算完成滤波,如果需要降低或者提高采样率的话,还需要进行抽取或者内插处理。这次实验我们用的是Quartus II 给我们提供的IP 核来做就没有原来那么麻烦了,所有的工作都交给IP核来工作,我们只需要做的是生成符合要求的滤波器系数,然后仿真正确无误后交给FPGA处理。上面我们设计完了前两级的FIR半带滤波器和最后一级的FIR普通滤波器,由于我们要进行降频处理,也就是抽取处理,每一次抽取处理紧跟在一个抗混叠滤波器之后进行,见下图:根据上图可以看到,我们利用3个FIR滤波器进行级联,前两级滤波器为半带滤波器,最后一级为普通FIR半带滤波器,总共进行了8倍抽取。因此我们可以推想,经过这三级滤波器滤波后的信号,会滤除高频分量,留下我们需要的100hz的有用信号并且采样频率降低8倍,下面是仿真图: 我们可以看到,经过三级滤波器滤波后,高频分量有了一定程度的衰减,我们需要的有用信号没有***扰,虽然高频分量的衰减不是很足,当然也是跟我们这个实验设计的阶数有关系,为了简单起见,滤波器阶数较低,衰减不是很足,当然这里也满足设计要求,我们再来看一下时域波形,根据推想,时域波形应该是信号的频率不变,采样频率降为原来的八分之一才对,仿真见下图:由图可见,设计完全符合我们的要求,既然仿真成功了,那么我们就可以在硬件上实现了。四、FIR半带滤波器的FPGA设计这次我们直接调用FIR滤波器的IP核来设计,非常的方便,我们只需要做一下顶层的理化,考虑一下有限字长的影响,就可以将工作交给IP去做,而且效率比我们自己写代码高的多,首先先来认识一下FIR滤波器的IP核:IP核的调用过程很简单这里不再叙述,在调用的时候有两个IP可以调用,我用的是FIR compile ,另外一个是FIR compile II,但是用了一下不出波形,也就放弃了,这里用第一个来做。 先来看一下设计界面:1、就是用来新建,编辑和删除滤波器系数用的,进入界面之后会自动新建一个页面,我们如果想编辑滤波器系数的话,可以通过点击edit coefficient set 选项进行滤波器系数设置,见下图: 关于滤波器系数的设置,我们可以通过手动将系数敲进去,但是我们之前不是用MATLAB生成了滤波器系数吗,在阶数少的情况下可以一个一个的敲进去,但是在阶数多的情况下就不适用了,所以我们可以通过导入TXT文件的形式导入滤波器系数,将MATLAB生成的系数写进txt文件中,然后直接导入进去即可,上图就是我将第一级FIR半带滤波器导入后的滤波器频域波形,可见滤波器的通阻带是关于π/2对称的,虽然不是很明显。2、是用来显示滤波器量化前后的波形变化情况,前者是未经量化的理想滤波器的幅频响应,但是要考虑有限字长的影响,我们需要将滤波器系数进行量化,一般量化8到16位就够了,当然量化位数越高,精度越高,两条曲线会逐渐重合,后者是经12bit量化后的幅频响应曲线。 3、选择auto with power 2 ,IP核会自动将滤波器系数中最大的值量化成2的整数次幂的形式,后面是系数的量化位数,为12bit 4、用来设置滤波器的实现结构,不同的结构实现算法不同,占用资源,速度也不同,我们这里用多周期结构 5、设置流水线级数 6、用来设置我们滤波器的类型,可以是抽取,内插,普通单速率滤波器等,这里我们前两级是FIR半带抽取滤波器,因此我们选择抽取滤波器,抽取倍数为2 7、用来设置输入的数据,我们设置输入数据为有符号二进制数,又因为前面我们说了,合成信号是讲过8bit量化后的,所以我们设置输入数据位宽为8。这样,输入数据为8位,滤波器系数量化为12位,那么我们第一级滤波器的输出为20位。同理 ,第二级FIR半带抽取滤波器的IP核配置跟第一级几乎相同,只要改一下滤波器系数,重新导入MATLAB生成的第二级滤波器系数即可,还要再改一下第二级滤波器输入数据位宽,此时为20位。第三级,也就是最后 一级,需要用普通单速率滤波器,导入第三级滤波器系数,将滤波器类型改为单速率滤波器,输入数据位宽为32位。需要注意的是,在调用生成第二级FIR滤波器 IP核的时候不需要重新再创建一个,也创建不了,我们只需要将第一级滤波器IP进行复制,改一个名字即可重新配置复制已经生成的IP重新命名IP核配置好了之后我们依次对其进行例化就好了,例化的过程中要注意IP核引出的几个管脚的意思,注意几个主要的就好了,具体例化见下图:需要注意的是,我们需要做的是3级的FIR滤波器,每一级滤波器都有一个输入使能跟输出有效,只有当上一级输出有效的时候,下一级才能输入使能,因此我们将顶层3级滤波器例化好之后的RTL视图见下图:我们调用modelsim进行仿真,仿真波形见下图可见输出后的波形采样频率已经大大降低,并且滤除掉了高频分量,留下了我们需要的有用新号,可见这次的设计还是符合我们的设计要求的。想要源码的朋友可以给小墨发邮箱,谢谢大家支持小墨邮箱584642877@qq.com`

    2015-08-29 15:33

  • 「正点原子Linux连载」第三十二章U-Boot启动流程详解(一)

    1)实验平台:正点原子Linux开发板2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南》关注官方微信号公众号,获取更多资料:正点原子上一章我们详细的分析了uboot的顶层Makefile,理清了uboot的编译流程。本章我们来详细的分析一下uboot的启动流程,理清uboot是如何启动的。通过对uboot启动流程的梳理,我们就可以掌握一些外设是在哪里被初始化的,这样当我们需要修改这些外设驱动的时候就会心里有数。另外,通过分析uboot的启动流程可以了解Linux内核是如何被启动的。32.1 链接脚本u-boot.lds详解要分析uboot的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口。如果没有编译过uboot的话链接脚本为arch/arm/cpu/u-boot.lds。但是这个不是最终使用的链接脚本,最终的链接脚本是在这个链接脚本的基础上生成的。编译一下uboot,编译完成以后就会在uboot根目录下生成u-boot.lds文件,如图32.1.1所示:图32.1.1 链接脚本只有编译u-boot以后才会在根目录下出现u-boot.lds文件!只有编译u-boot以后才会在根目录下出现u-boot.lds文件!只有编译u-boot以后才会在根目录下出现u-boot.lds文件!打开u-boot.lds,内容如下:示例代码32.1.1 u-boot.lds文件代码1 OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm")2 OUTPUT_ARCH(arm)3 ENTRY(_start)4 SECTIONS5{6.=0x00000000;7.= ALIGN(4);8.text :9{10*(.__image_copy_start)11*(.vectors)12 arch/arm/cpu/armv7/start.o (.text*)13*(.text*)14}15.= ALIGN(4);16.rodata :{*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))}17.= ALIGN(4);18.data :{19*(.data*)20}21.= ALIGN(4);22.=.;23.= ALIGN(4);24.u_boot_list :{25 KEEP(*(SORT(.u_boot_list*)));26}27.= ALIGN(4);28.image_copy_end :29{30*(.__image_copy_end)31}32.rel_dyn_start :33{34*(.__rel_dyn_start)35}36.rel.dyn :{37*(.rel*)38}39.rel_dyn_end :40{41*(.__rel_dyn_end)42}43.end :44{45*(.__end)46}47 _image_binary_end =.;48.= ALIGN(4096);49.mmutable :{50*(.mmutable)51}52.bss_start __rel_dyn_start (OVERLAY):{53 KEEP(*(.__bss_start));54 __bss_base =.;55}56.bss __bss_base (OVERLAY):{57*(.bss*)58.= ALIGN(4);59 __bss_limit =.;60}61.bss_end __bss_limit (OVERLAY):{62 KEEP(*(.__bss_end));63}64.dynsym _image_binary_end :{*(.dynsym)}65.dynbss :{*(.dynbss)}66.dynstr :{*(.dynstr*)}67.dynamic :{*(.dynamic*)}68.plt :{*(.plt*)}69.interp :{*(.interp*)}70.gnu.hash :{*(.gnu.hash)}71.gnu :{*(.gnu*)}72.ARM.exidx :{*(.ARM.exidx*)}73.gnu.linkonce.armexidx :{*(.gnu.linkonce.armexidx.*)}74}第3行为代码当前入口点:_start, _start在文件arch/arm/lib/vectors.S中有定义,如图32.1.2所示:图32.1.2 _start入口从图32.1.1可以看出,_start后面就是中断向量表,从图中的“.section ".vectors", "ax”可以得到,此代码存放在.vectors段里面。第10行,使用如下命令在uboot中查找“__image_copy_start”:grep -nR "__image_copy_start"搜索结果如图32.1.3所示:图32.1.3 查找结果打开u-boot.map,找到如图32.1.4所示位置:图32.1.4 u-boot.mapu-boot.map是uboot的映射文件,可以从此文件看到某个文件或者函数链接到了哪个地址,从图32.1.4的932行可以看到__image_copy_start为0X87800000,而.text的起始地址也是0X87800000。第11行是vectors段,vectors段保存中断向量表,从图32.1.2中我们知道了vectors.S的代码是存在vectors段中的。从图32.1.4可以看出,vectors段的起始地址也是0X87800000,说明整个uboot的起始地址就是0X87800000,这也是为什么我们裸机例程的链接起始地址选择0X87800000了,目的就是为了和uboot一致。第12行将arch/arm/cpu/armv7/start.s编译出来的代码放到中断向量表后面。第13行为text段,其他的代码段就放到这里在u-boot.lds中有一些跟地址有关的“变量”需要我们注意一下,后面分析u-boot源码的时候会用到,这些变量要最终编译完成才能确定的!!!比如我编译完成以后这些“变量”的值如表32.1.1所示:表32.1.1 uboot相关变量表表32.1.1中的“变量”值可以在u-boot.map文件中查找,表32.1.1中除了__image_copy_start以外,其他的变量值每次编译的时候可能会变化,如果修改了uboot代码、修改了uboot配置、选用不同的优化等级等等都会影响到这些值。所以,一切以实际值为准!32.2 U-Boot启动流程详解32.2.1reset函数源码详解从u-boot.lds中我们已经知道了入口点是arch/arm/lib/vectors.S文件中的_start,代码如下:示例代码32.2.1.1 vectors.S代码段38/*39 *************************************************************40 *41 * Exception vectors as described in ARM reference manuals42 *43 * Uses indirect branch to allow reaching handlers anywhere in44 * memory.45 **************************************************************46 */4748 _start:4950 #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG51.word CONFIG_SYS_DV_NOR_BOOT_CFG52 #endif5354 b reset55 ldr pc, _undefined_instruction56 ldr pc, _software_interrupt57 ldr pc, _prefetch_abort58 ldr pc, _data_abort59 ldr pc, _not_used60 ldr pc, _irq61 ldr pc, _fiq第48行_start开始的是中断向量表,其中54~61行就是中断向量表,和我们裸机例程里面一样。54行跳转到reset函数里面,reset函数在arch/arm/cpu/armv7/start.S里面,代码如下:示例代码32.2.1.2 start.S代码段22/*****************************************************************23 *24 * Startup Code (reset vector)25 *26 * Do important init only if we don't start from memory!27 * Setup memory and board specific bits prior to relocation.28 * Relocate armboot to ram. Setup stack.29 *30 *****************************************************************/3132.globl reset33.globl save_boot_params_ret3435 reset:36 /* Allow the board to save important registers */37 b save_boot_params第35行就是reset函数。第37行从reset函数跳转到了save_boot_params函数,而save_boot_params函数同样定义在start.S里面,定义如下:示例代码32.2.1.3 start.S代码段91/******************************************************************92 *93 * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)94 * __attribute__((weak));95 *96 * Stack pointer is not yet initialized at this moment97 * Don't save anything to stack even if compiled with -O098 *99 ******************************************************************/100 ENTRY(save_boot_params)101 b save_boot_params_ret @ back to my callersave_boot_params函数也是只有一句跳转语句,跳转到save_boot_params_ret函数,save_boot_params_ret函数代码如下:示例代码32.2.1.4 start.S代码段38 save_boot_params_ret:39 /*40 * disable interrupts (FIQ and IRQ), also set the cpu to SVC3241 * mode, except if in HYP mode already42 */43 mrs r0, cpsr44 and r1, r0, #0x1f @ mask mode bits45 teq r1, #0x1a @ test for HYP mode46 bicne r0, r0, #0x1f @ clear all mode bits47 orrne r0, r0, #0x13 @ set SVC mode48 orr r0, r0, #0xc0 @ disable FIQ and IRQ49 msr cpsr,r0第43行,读取寄存器cpsr中的值,并保存到r0寄存器中。第44行,将寄存器r0中的值与0X1F进行与运算,结果保存到r1寄存器中,目的就是提取cpsr的bit0~bit4这5位,这5位为M4 M3 M2 M1 M0,M[4:0]这五位用来设置处理器的工作模式,如表32.2.1.1所示:M[4:0]模式10000User(usr)10001FIQ(fiq)10010IRQ(irq)10011Supervisor(svc)10110Monitor(mon)10111Abort(abt)11010Hyp(hyp)11011Undefined(und)11111System(sys)表32.2.1.1 Cortex-A7工作模式第45行,判断r1寄存器的值是否等于0X1A(0b11010),也就是判断当前处理器模式是否处于Hyp模式。第46行,如果r1和0X1A不相等,也就是CPU不处于Hyp模式的话就将r0寄存器的bit0~5进行清零,其实就是清除模式位第47行,如果处理器不处于Hyp模式的话就将r0的寄存器的值与0x13进行或运算,0x13=0b10011,也就是设置处理器进入SVC模式。第48行,r0寄存器的值再与0xC0进行或运算,那么r0寄存器此时的值就是0xD3,cpsr的I为和F位分别控制IRQ和FIQ这两个中断的开关,设置为1就关闭了FIQ和IRQ!第49行,将r0寄存器写回到cpsr寄存器中。完成设置CPU处于SVC32模式,并且关闭FIQ和IRQ这两个中断。继续执行执行下面的代码:示例代码32.2.1.5 start.S代码段51 /*52 * Setup vector:53 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.54 * Continue to use ROM code vector only in OMAP4 spl)55 */56 #if!(defined(CONFIG_OMAP44XX)&& defined(CONFIG_SPL_BUILD))57/* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */58 mrc p15,0, r0, c1, c0,0 @ Read CP15 SCTLR Register59 bic r0, #CR_V @ V =060 mcr p15,0, r0, c1, c0,0 @ Write CP15 SCTLR Register6162 /* Set vector address in CP15 VBAR register */63 ldr r0,=_start64 mcr p15,0, r0, c12, c0,0 @Set VBAR65 #endif第56行,如果没有定义CONFIG_OMAP44XX和CONFIG_SPL_BUILD的话条件成立,此处条件成立。第58行读取CP15中c1寄存器的值到r0寄存器中,根据17.1.4小节可知,这里是读取SCTLR寄存器的值。第59行,CR_V在arch/arm/include/asm/system.h中有如下所示定义:#define CR_V (1 cbcmr);834 periph2 =((reg & MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_MASK)835>> MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_OFFSET);836 periph1 =((reg & MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK)837>> MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_OFFSET);838839/* Checking if PLL2 PFD0 or PLL2 PFD2 is using for periph clock */840if((periph2 !=0x2)&&(periph1 !=0x2))841 mask528 |= ANATOP_PFD_CLKGATE_MASK(0);842843if((periph2 !=0x1)&&(periph1 !=0x1)&&844(periph2 !=0x3)&&(periph1 !=0x3))845 mask528 |= ANATOP_PFD_CLKGATE_MASK(2);846847 writel(mask480,&anatop->pfd_480_set);848 writel(mask528,&anatop->pfd_528_set);849 writel(mask480,&anatop->pfd_480_clr);850 writel(mask528,&anatop->pfd_528_clr);851}在第816行会判断当前CPU类型,如果CPU为MX6SX、MX6UL、MX6ULL或MX6SLL中的任意一种,那么就会直接返回,相当于s_init函数什么都没做。所以对于I.MX6UL/I.MX6ULL来说,s_init就是个空函数。从s_init函数退出以后进入函数lowlevel_init,但是lowlevel_init函数也执行完成了,返回到了函数cpu_init_crit,函数cpu_init_crit也执行完成了,最终返回到save_boot_params_ret,函数调用路径如图32.2.3.1所示:图32.2.3.1 uboot函数调用路径从图32.2.3.1可知,接下来要执行的是save_boot_params_ret中的_main函数,接下来分析_main函数。

    2020-03-16 10:10

  • 「正点原子Linux连载」第三十二章U-Boot启动流程详解(二)

    1)实验平台:正点原子Linux开发板2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南》关注官方微信号公众号,获取更多资料:正点原子32.2.4_main函数详解_main函数定义在文件arch/arm/lib/crt0.S中,函数内容如下:示例代码32.2.4.1 crt0.S代码段63/*64 * entry point of crt0 sequence65 */6667 ENTRY(_main)6869/*70 * Set up initial C runtime environment and call board_init_f(0).71 */7273 #if defined(CONFIG_SPL_BUILD)&& defined(CONFIG_SPL_STACK)74 ldr sp,=(CONFIG_SPL_STACK)75 #else76 ldr sp,=(CONFIG_SYS_INIT_SP_ADDR)77 #endif78 #if defined(CONFIG_CPU_V7M)/* v7M forbids using SP as BIC destination */79 mov r3, sp80 bic r3, r3, #781 mov sp, r382 #else83 bic sp, sp, #7/* 8-byte alignment for ABI compliance */84 #endif85 mov r0, sp86 bl board_init_f_alloc_reserve87 mov sp, r088/* set up gd here, outside any C code */89 mov r9, r090 bl board_init_f_init_reserve9192 mov r0, #093 bl board_init_f9495 #if! defined(CONFIG_SPL_BUILD)9697/*98 * Set up intermediate environment (new sp and gd) and call99 * relocate_code(addr_moni). Trick here is that we'll return100 * 'here' but relocated.101 */102103 ldr sp,[r9, #GD_START_ADDR_SP]/* sp = gd->start_addr_sp */104 #if defined(CONFIG_CPU_V7M)/* v7M forbids using SP as BIC destination */105 mov r3, sp106 bic r3, r3, #7107 mov sp, r3108 #else109 bic sp, sp, #7/* 8-byte alignment for ABI compliance */110 #endif111 ldr r9,[r9, #GD_BD]/* r9 = gd->bd */112 sub r9, r9, #GD_SIZE /* new GD is below bd */113114 adr lr, here115 ldr r0,[r9, #GD_RELOC_OFF]/* r0 = gd->reloc_off */116 add lr, lr, r0117 #if defined(CONFIG_CPU_V7M)118 orr lr, #1/* As required by Thumb-only */119 #endif120 ldr r0,[r9, #GD_RELOCADDR]/* r0 = gd->relocaddr */121 b relocate_code122 here:123/*124 * now relocate vectors125 */126127 bl relocate_vectors128129/* Set up final (full) environment */130131 bl c_runtime_cpu_setup /* we still call old routine here */132 #endif133 #if!defined(CONFIG_SPL_BUILD)|| defined(CONFIG_SPL_FRAMEWORK)134 # ifdef CONFIG_SPL_BUILD135/* Use a DRAM stack for the rest of SPL, if requested */136 bl spl_relocate_stack_gd137 cmp r0, #0138 movne sp, r0139 movne r9, r0140 # endif141 ldr r0,=__bss_start /* this is auto-relocated! */142143 #ifdef CONFIG_USE_ARCH_MEMSET144 ldr r3,=__bss_end /* this is auto-relocated! */145 mov r1, #0x00000000/* prepare zero to clear BSS */146147 subs r2, r3, r0 /* r2 = memset len */148 bl memset149 #else150 ldr r1,=__bss_end /* this is auto-relocated! */151 mov r2, #0x00000000/* prepare zero to clear BSS */152153 clbss_l:cmp r0, r1 /* while not at end of BSS */154 #if defined(CONFIG_CPU_V7M)155 itt lo156 #endif157 strlo r2,[r0]/* clear 32-bit BSS word */158 addlo r0, r0, #4/* move to next */159 blo clbss_l160 #endif161162 #if! defined(CONFIG_SPL_BUILD)163 bl coloured_LED_init164 bl red_led_on165 #endif166/* call board_init_r(gd_t *id, ulong dest_addr) */167 mov r0, r9 /* gd_t */168 ldr r1,[r9, #GD_RELOCADDR]/* dest_addr */169/* call board_init_r */170 #if defined(CONFIG_SYS_THUMB_BUILD)171 ldr lr,=board_init_r /* this is auto-relocated! */172 bx lr173 #else174 ldr pc,=board_init_r /* this is auto-relocated! */175 #endif176/* we should not return here. */177 #endif178179 ENDPROC(_main)第76行,设置sp指针为CONFIG_SYS_INIT_SP_ADDR,也就是sp指向0X0091FF00。第83行,sp做8字节对齐。第85行,读取sp到寄存器r0里面,此时r0=0X0091FF00。第86行,调用函数board_init_f_alloc_reserve,此函数有一个参数,参数为r0中的值,也就是0X0091FF00,此函数定义在文件common/init/board_init.c中,内容如下:示例代码32.2.4.2 board_init.c代码段56 ulong board_init_f_alloc_reserve(ulong top)57{58 /* Reserve early malloc arena */59 #if defined(CONFIG_SYS_MALLOC_F)60 top -= CONFIG_SYS_MALLOC_F_LEN;61 #endif62 /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */63 top = rounddown(top-sizeof(struct global_data),16);6465 return top;66}函数board_init_f_alloc_reserve主要是留出早期的malloc内存区域和gd内存区域,其中CONFIG_SYS_MALLOC_F_LEN=0X400(在文件include/generated/autoconf.h中定义),sizeof(struct global_data)=248(GD_SIZE值),完成以后的内存分布如图32.2.4.1所示:图32.2.4.1 内存分布图函数board_init_f_alloc_reserve是有返回值的,返回值为新的top值,从图32.2.4.1可知,此时top=0X0091FA00。继续回到示例代码32.2.4.1中,第87行,将r0写入到sp里面,r0保存着函数board_init_f_alloc_reserve的返回值,所以这一句也就是设置sp=0X0091FA00。第89行,将r0寄存器的值写到寄存器r9里面,因为r9寄存器存放着全局变量gd的地址,在文件arch/arm/include/asm/global_data.h中有如图32.2.4.2所示宏定义:图32.2.4.2 DECLARE_GLOBAL_DATA_PTR宏定义从图32.2.4.2可以看出,uboot中定义了一个指向gd_t的指针gd,gd存放在寄存器r9里面的,因此gd是个全局变量。gd_t是个结构体,在include/asm-generic/global_data.h里面有定义,gd_定义如下:示例代码32.2.4.3 global_data.h代码段27typedefstruct global_data {28 bd_t *bd;29unsignedlong flags;30unsignedint baudrate;31unsignedlong cpu_clk;/* CPU clock in Hz! */32unsignedlong bus_clk;33/* We cannot bracket this with CONFIG_PCI due to mpc5xxx */34unsignedlong pci_clk;35unsignedlong mem_clk;36 #if defined(CONFIG_LCD)|| defined(CONFIG_VIDEO)37unsignedlong fb_base;/* Base address of framebuffer mem */38 #endif......121 #ifdef CONFIG_DM_VIDEO122 ulong video_top;/* Top of video frame buffer area */123 ulong video_bottom;/* Bottom of video frame buffer area */124 #endif125} gd_t;因此这一行代码就是设置gd所指向的位置,也就是gd指向0X0091FA00。继续回到示例代码32.2.4.1中,第90行调用函数board_init_f_init_reserve,此函数在文件common/init/board_init.c中有定义,函数内容如下:示例代码32.2.4.4 board_init.c代码段110void board_init_f_init_reserve(ulong base)111{112struct global_data *gd_ptr;113 #ifndef _USE_MEMCPY114int*ptr;115 #endif116117/*118 * clear GD entirely and set it up.119 * Use gd_ptr, as gd may not be properly set yet.120 */121122 gd_ptr =(struct global_data *)base;123/* zero the area */124 #ifdef _USE_MEMCPY125 memset(gd_ptr,'\0',sizeof(*gd));126 #else127for(ptr =(int*)gd_ptr; ptr malloc_base = base;145/* next alloc will be higher by one 'early malloc arena' size */146 base += CONFIG_SYS_MALLOC_F_LEN;147 #endif148}可以看出,此函数用于初始化gd,其实就是清零处理。另外,此函数还设置了gd->malloc_base为gd基地址+gd大小=0X0091FA00+248=0X0091FAF8,在做16字节对齐,最终gd->malloc_base=0X0091FB00,这个也就是earlymalloc的起始地址。继续回到示例代码32.2.4.1中,第92行设置R0为0。第93行,调用board_init_f函数,此函数定义在文件common/board_f.c中!主要用来初始化DDR,定时器,完成代码拷贝等等,此函数我们后面在详细的分析。第103行,重新设置环境(sp和gd)、获取gd->start_addr_sp的值赋给sp,在函数board_init_f中会初始化gd的所有成员变量,其中gd->start_addr_sp=0X9EF44E90,所以这里相当于设置sp=gd->start_addr_sp=0X9EF44E90。0X9EF44E90是DDR中的地址,说明新的sp和gd将会存放到DDR中,而不是内部的RAM了。GD_START_ADDR_SP=64,参考示例代码32.2.2.4。第109行,sp做8字节对齐。第111行,获取gd->bd的地址赋给r9,此时r9存放的是老的gd,这里通过获取gd->bd的地址来计算出新的gd的位置。GD_BD=0,参考示例代码32.2.2.4。第112行,新的gd在bd下面,所以r9减去gd的大小就是新的gd的位置,获取到新的gd的位置以后赋值给r9。第114行,设置lr寄存器为here,这样后面执行其他函数返回的时候就返回到了第122行的here位置处。第115,读取gd->reloc_off的值复制给r0寄存器,GD_RELOC_OFF=68,参考示例代码32.2.2.4。第116行,lr寄存器的值加上r0寄存器的值,重新赋值给lr寄存器。因为接下来要重定位代码,也就是把代码拷贝到新的地方去(现在的uboot存放的起始地址为0X87800000,下面要将uboot拷贝到DDR最后面的地址空间出,将0X87800000开始的内存空出来),其中就包括here,因此lr中的here要使用重定位后的位置。第120行,读取gd->relocaddr的值赋给r0寄存器,此时r0寄存器就保存着uboot要拷贝的目的地址,为0X9FF47000。GD_RELOCADDR=48,参考示例代码32.2.2.4。第121行,调用函数relocate_code,也就是代码重定位函数,此函数负责将uboot拷贝到新的地方去,此函数定义在文件arch/arm/lib/relocate.S中稍后会详细分析此函数。第127行,调用函数relocate_vectors,对中断向量表做重定位,此函数定义在文件arch/arm/lib/relocate.S中,稍后会详细分析此函数。第131行,调用函数c_runtime_cpu_setup,此函数定义在文件arch/arm/cpu/armv7/start.S中,函数内容如下:示例代码32.2.4.5 start.S代码段77 ENTRY(c_runtime_cpu_setup)78/*79 * If I-cache is enabled invalidate it80 */81 #ifndef CONFIG_SYS_ICACHE_OFF82 mcr p15,0, r0, c7, c5,0 @ invalidate icache83 mcr p15,0, r0, c7, c10,4 @ DSB84 mcr p15,0, r0, c7, c5,4 @ ISB85 #endif8687 bx lr8889 ENDPROC(c_runtime_cpu_setup)第141~159行,清除BSS段。第167行,设置函数board_init_r的两个参数,函数board_init_r声明如下:board_init_r(gd_t *id, ulong dest_addr)第一个参数是gd,因此读取r9保存到r0里面。第168行,设置函数board_init_r的第二个参数是目的地址,因此r1=gd->relocaddr。第174行、调用函数board_init_r,此函数定义在文件common/board_r.c中,稍后会详细的分析此函数。这个就是_main函数的运行流程,在_main函数里面调用了board_init_f、relocate_code、relocate_vectors和board_init_r这4个函数,接下来依次看一下这4个函数都是干啥的。32.2.5 board_init_f函数详解_main中会board_init_f函数,board_init_f函数主要有两个工作:①、初始化一系列外设,比如串口、定时器,或者打印一些消息等。②、初始化gd的各个成员变量,uboot会将自己重定位到DRAM最后面的地址区域,也就是将自己拷贝到DRAM最后面的内存区域中。这么做的目的是给Linux腾出空间,防止Linuxkernel覆盖掉uboot,将DRAM前面的区域完整的空出来。在拷贝之前肯定要给uboot各部分分配好内存位置和大小,比如gd应该存放到哪个位置,malloc内存池应该存放到哪个位置等等。这些信息都保存在gd的成员变量中,因此要对gd的这些成员变量做初始化。最终形成一个完整的内存“分配图”,在后面重定位uboot的时候就会用到这个内存“分配图”。此函数定义在文件common/board_f.c中定义,代码如下:示例代码32.2.5.1 board_f.c代码段1035void board_init_f(ulong boot_flags)1036{1037 #ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA1038/*1039 * For some archtectures, global data is initialized and used1040 * before calling this function. The data should be preserved.1041 * For others, CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined1042 * and use the stack here to host global data until relocation.1043 */1044 gd_t data;10451046 gd =&data;10471048/*1049 * Clear global data before it is accessed at debug print1050 * in initcall_run_list. Otherwise the debug print probably1051 * get the wrong vaule of gd->have_console.1052 */1053 zero_global_data();1054 #endif10551056 gd->flags = boot_flags;1057 gd->have_console =0;10581059if(initcall_run_list(init_sequence_f))1060 hang();10611062 #if!defined(CONFIG_ARM)&&!defined(CONFIG_SANDBOX)&& \1063!defined(CONFIG_EFI_APP)1064/* NOTREACHED - jump_to_copy() does not return */1065 hang();1066 #endif1067}因为没有定义CONFIG_SYS_GENERIC_GLOBAL_DATA,所以第1037~1054行代码无效。第1056行,初始化gd->flags=boot_flags=0。第1057行,设置gd->have_console=0。重点在第1059行!通过函数initcall_run_list来运行初始化序列init_sequence_f里面的一些列函数,init_sequence_f里面包含了一系列的初始化函数,init_sequence_f也是定义在文件common/board_f.c中,由于init_sequence_f的内容比较长,里面有大量的条件编译代码,这里为了缩小篇幅,将条件编译部分删除掉了,去掉条件编译以后的init_sequence_f定义如下:示例代码32.2.5.1 board_f.c代码段/*****************去掉条件编译语句后的init_sequence_f***************/1static init_fnc_t init_sequence_f[]={2 setup_mon_len,3 initf_malloc,4 initf_console_record,5 arch_cpu_init, /* basic arch cpu dependent setup */6 initf_dm,7 arch_cpu_init_dm,8 mark_bootstage, /* need timer, go after init dm */9 board_early_init_f,10 timer_init, /* initialize timer */11 board_postclk_init,12 get_clocks,13 env_init, /* initialize environment */14 init_baud_rate, /* initialze baudrate settings */15 serial_init, /* serial communications setup */16 console_init_f, /* stage 1 init of console */17 display_options, /* say that we are here */18 display_text_info, /* show debugging info if required */19 print_cpuinfo, /* display cpu info (and speed) */20 show_board_info,21 INIT_FUNC_WATCHDOG_INIT22 INIT_FUNC_WATCHDOG_RESET23 init_func_i2c,24 announce_dram_init,25 /* TODO: unify all these dram functions? */26 dram_init, /* configure available RAM banks */27 post_init_f,28 INIT_FUNC_WATCHDOG_RESET29 testdram,30 INIT_FUNC_WATCHDOG_RESET31 INIT_FUNC_WATCHDOG_RESET32 /*33 * Now that we have DRAM mapped and working, we can34 * relocate the code and continue running from DRAM.35 *36 * Reserve memory at end of RAM for (top down in that order):37 * - area that won't get touched by U-Boot and Linux (optional)38 * - kernel log buffer39 * - protected RAM40 * - LCD framebuffer41 * - monitor code42 * - board info struct43 */44 setup_dest_addr,45 reserve_round_4k,46 reserve_mmu,47 reserve_trace,48 reserve_uboot,49 reserve_malloc,50 reserve_board,51 setup_machine,52 reserve_global_data,53 reserve_fdt,54 reserve_arch,55 reserve_stacks,56 setup_dram_config,57 show_dram_config,58 display_new_sp,59 INIT_FUNC_WATCHDOG_RESET60 reloc_fdt,61 setup_reloc,62 NULL,63};接下来分析以上函数执行完以后的结果:第2行,setup_mon_len函数设置gd的mon_len成员变量,此处为__bss_end -_start,也就是整个代码的长度。0X878A8E74-0x87800000=0XA8E74,这个就是代码长度第3行,initf_malloc函数初始化gd中跟malloc有关的成员变量,比如malloc_limit,此函数会设置gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN=0X400。malloc_limit表示malloc内存池大小。第4行,initf_console_record,如果定义了宏CONFIG_CONSOLE_RECORD和宏CONFIG_SYS_MALLOC_F_LEN的话此函数就会调用函数console_record_init,但是IMX6ULL的uboot没有定义宏CONFIG_CONSOLE_RECORD,所以此函数直接返回0。第5行,arch_cpu_init函数,初始化架构相关的内容,CPU级别的操作。第6行,initf_dm函数,驱动模型的一些初始化。第7行,arch_cpu_init_dm函数未实现。第8行,mark_bootstage函数应该是和啥标记有关的第9行,board_early_init_f函数,板子相关的早期的一些初始化设置,I.MX6ULL用来初始化串口的IO配置第10行,timer_init,初始化定时器,Cortex-A7内核有一个定时器,这里初始化的就是Cortex-A内核的那个定时器。通过这个定时器来为uboot提供时间。就跟Cortex-M内核Systick定时器一样。关于Cortex-A内部定时器的详细内容,请参考文档《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》的“Chapter B8 The Generic Timer”章节。第11行,board_postclk_init,对于I.MX6ULL来说是设置VDDSOC电压。第12行,get_clocks函数用于获取一些时钟值,I.MX6ULL获取的是sdhc_clk时钟,也就是SD卡外设的时钟。第13行,env_init函数是和环境变量有关的,设置gd的成员变量env_addr,也就是环境变量的保存地址。第14行,init_baud_rate函数用于初始化波特率,根据环境变量baudrate来初始化gd->baudrate。第15行,serial_init,初始化串口。第16行,console_init_f,设置gd->have_console 为1,表示有个控制台,此函数也将前面暂存在缓冲区中的数据通过控制台打印出来。第17行、display_options,通过串口输出一些信息,如图32.2.5.1所示:图32.2.5.1 串口信息输出第18行,display_text_info,打印一些文本信息,如果开启UBOOT的DEBUG功能的话就会输出text_base、bss_start、bss_end,形式如下:debug("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n",text_base, bss_start, bss_end);结果如图32.2.5.2所示:图32.2.5.2 文本信息第19行,print_cpuinfo函数用于打印CPU信息,结果如图32.2.5.3所示:图32.2.5.3 CPU信息图32.2.5.3 CPU信息第20行,show_board_info函数用于打印板子信息,会调用checkboard函数,结果如图32.2.5.4所示:图32.2.5.4 板子信息第21行,INIT_FUNC_WATCHDOG_INIT,初始化看门狗,对于I.MX6ULL来说是空函数第22行,INIT_FUNC_WATCHDOG_RESET,复位看门狗,对于I.MX6ULL来说是空函数第23行,init_func_i2c函数用于初始化I2C,初始化完成以后会输出如图32.2.5.5所示信息:图32.2.5.5 I2C初始化信息输出第24行,announce_dram_init,此函数很简单,就是输出字符串“DRAM:”第26行,dram_init,并非真正的初始化DDR,只是设置gd->ram_size的值,对于正点原子I.MX6ULL开发板EMMC版本核心板来说就是512MB。第27行,post_init_f,此函数用来完成一些测试,初始化gd->post_init_f_time第29行,testdram,测试DRAM,空函数。第44行,setup_dest_addr函数,设置目的地址,设置gd->ram_size,gd->ram_top,gd->relocaddr这三个的值。接下来我们会遇到很多跟数值有关的设置,如果直接看代码分析的话就太费时间了,我可以修改uboot代码,直接将这些值通过串口打印出来,比如这里我们修改文件common/board_f.c,因为setup_dest_addr函数定义在文件common/board_f.c中,在setup_dest_addr函数输入如图32.2.5.6所示内容:图32.2.5.6 添加print函数打印成员变量值设置好以后重新编译uboot,然后烧写到SD卡中,选择SD卡启动,重启开发板,打开SecureCRT,uboot会输出如图32.2.5.7所示信息:从图32.2.5.7可以看出:gd->ram_size = 0X20000000 //ram大小为0X20000000=512MBgd->ram_top = 0XA0000000 //ram最高地址为0X80000000+0X20000000=0XA0000000gd->relocaddr = 0XA0000000 //重定位后最高地址为0XA0000000第45行,reserve_round_4k函数用于对 gd->relocaddr做4KB对齐,因为gd->relocaddr=0XA0000000,已经是4K对齐了,所以调整后不变。第46行,reserve_mmu,留出MMU的TLB表的位置,分配MMU的TLB表内存以后会对gd->relocaddr做64K字节对齐。完成以后gd->arch.tlb_size、gd->arch.tlb_addr和gd->relocaddr如图32.2.5.8所示:图32.2.5.8 信息输出从图32.2.5.8可以看出:gd->arch.tlb_size= 0X4000 //MMU的TLB表大小gd->arch.tlb_addr=0X9FFF0000 //MMU的TLB表起始地址,64KB对齐以后gd->relocaddr=0X9FFF0000 //relocaddr地址第47行,reserve_trace函数,留出跟踪调试的内存,I.MX6ULL没有用到!第48行,reserve_uboot,留出重定位后的uboot所占用的内存区域,uboot所占用大小由gd->mon_len所指定,留出uboot的空间以后还要对gd->relocaddr做4K字节对齐,并且重新设置gd->start_addr_sp,结果如图32.2.5.9所示:图32.2.5.9 信息输出从图32.2.5.9可以看出:gd->mon_len = 0XA8EF4gd->start_addr_sp = 0X9FF47000gd->relocaddr = 0X9FF47000第49行,reserve_malloc,留出malloc区域,调整gd->start_addr_sp位置,malloc区域由宏TOTAL_MALLOC_LEN定义,宏定义如下:#define TOTAL_MALLOC_LEN (CONFIG_SYS_MALLOC_LEN + CONFIG_ENV_SIZE)mx6ull_alientek_emmc.h文件中定义宏CONFIG_SYS_MALLOC_LEN为16MB=0X1000000,宏CONFIG_ENV_SIZE=8KB=0X2000,因此TOTAL_MALLOC_LEN=0X1002000。调整以后gd->start_addr_sp如图32.2.5.10所示:图32.2.5.10 信息输出从图32.2.5.10可以看出:TOTAL_MALLOC_LEN=0X1002000gd->start_addr_sp=0X9EF45000 //0X9FF47000-16MB-8KB=0X9EF45000第50行,reserve_board函数,留出板子bd所占的内存区,bd是结构体bd_t,bd_t大小为80字节,结果如图32.2.5.11所示:图32.2.5.10 信息输出从图32.2.5.11可以看出:gd->start_addr_sp=0X9EF44FB0gd->bd=0X9EF44FB0第51行,setup_machine,设置机器ID,linux启动的时候会和这个机器ID匹配,如果匹配的话linux就会启动正常。但是!!I.MX6ULL不用这种方式了,这是以前老版本的uboot和linux使用的,新版本使用设备树了,因此此函数无效。第52行,reserve_global_data函数,保留出gd_t的内存区域,gd_t结构体大小为248B,结果如图32.2.5.11所示:图32.2.5.11 信息输出gd->start_addr_sp=0X9EF44EB8 //0X9EF44FB0-248=0X9EF44EB8gd->new_gd=0X9EF44EB8第53行,reserve_fdt,留出设备树相关的内存区域,I.MX6ULL的uboot没有用到,因此此函数无效。第54行,reserve_arch是个空函数。第55行,reserve_stacks,留出栈空间,先对gd->start_addr_sp减去16,然后做16字节对其。如果使能IRQ的话还要留出IRQ相应的内存,具体工作是由arch/arm/lib/stack.c文件中的函数arch_reserve_stacks完成。结果如图32.2.5.12所示:图32.2.5.12 信息输出在本uboot中并没有使用到IRQ,所以不会留出IRQ相应的内存区域,此时:gd->start_addr_sp=0X9EF44E90第56行,setup_dram_config函数设置dram信息,就是设置gd->bd->bi_dram[0].start和gd->bd->bi_dram[0].size,后面会传递给linux内核,告诉linux DRAM的起始地址和大小。结果如图32.2.5.13所示:图32.2.5.13 信息输出从图32.2.5.13可以看出,DRAM的起始地址为0X80000000,大小为0X20000000(512MB)。第57行,show_dram_config函数,用于显示DRAM的配置,如图32.2.5.14所示:图32.2.5.14 信息输出第58行,display_new_sp函数,显示新的sp位置,也就是gd->start_addr_sp,不过要定义宏DEBUG,结果如图32.2.5.15所示:图32.2.5.15 信息输出图32.2.5.15中的gd->start_addr_sp值和我们前面分析的最后一次修改的值一致。第60行,reloc_fdt函数用于重定位fdt,没有用到。第61行,setup_reloc,设置gd的其他一些成员变量,供后面重定位的时候使用,并且将以前的gd拷贝到gd->new_gd处。需要使能DEBUG才能看到相应的信息输出,如图32.2.5.16所示:图32.2.5.16 信息输出从图32.2.5.16可以看出,uboot重定位后的偏移为0X18747000,重定位后的新地址为0X9FF4700,新的gd首地址为0X9EF44EB8,最终的sp为0X9EF44E90。至此,board_init_f函数就执行完成了,最终的内存分配如图32.2.5.16所示:图32.2.5.16 最终的内存分配图32.2.6 relocate_code函数详解relocate_code函数是用于代码拷贝的,此函数定义在文件arch/arm/lib/relocate.S中,代码如下:示例代码32.2.6.1 relocate.S代码段/** void relocate_code(addr_moni)** This function relocates the monitor code.** NOTE:* To prevent the code below from containing references with an* R_ARM_ABS32 relocation record type, we never refer to linker-* defined symbols directly. Instead, we declare literals which* contain their relative location with respect to relocate_code,* and at run time, add relocate_code back to them.*/79 ENTRY(relocate_code)80 ldr r1,=__image_copy_start /* r1

    2020-03-16 10:13

  • 如何生成时源和频

    TINA-TI 系列文章的本期内容主要针对第 1 部分读者所提出的需求。本文我们将了解如何生成:时(分段线性)源频 源时源:在实践过程中,标准波形(即方波与三角波等)可能无法满足您的仿真需求

    2022-11-21 07:26

  • 「正点原子NANO STM32F103开发板资料连载」第三十二章 USB 读卡器实验

    1)实验平台:【正点原子】 NANO STM32F103 开发板2)摘自《正点原子STM32 F1 开发指南(NANO 板-HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子第三十二

    2020-05-07 11:15

  • 「ALIENTEK 阿波罗 STM32F767 开发板资料连载」第三十二章 光环境传感器实验

    1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自《STM32F7 开发指南(HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子第三十二章 光环境传感器实验上一章

    2020-05-16 12:02

  • 有源逆和无源逆的区别是什么?

    1、逆变器的概念:有源逆和无源逆的区别是什么?当交流侧接在点网上,即交流侧接有电源时,称为有源逆;当交流侧直接和负载连接时,成为无源逆。2、电池变流器(或电池逆

    2021-11-17 07:44