芯片知识基础

芯片是如何启动的?

用户定义的main是如何在芯片上运行起来的?
冯诺依曼和哈佛结构:区别在于取址指和取数据是否在一个存储器
冯诺依曼结构:指令和数据存放在同一个存储器,总线也只有一套。
哈佛:指令和数据分开存储,几套总线。
统一编址和独立编址:区别在于IO寄存器地址和内存地址是否在一个存储器
统一编址:IO寄存器和内存地址在同一个存储空间,通过存放的不同段进行区分
独立编址:IO寄存器和内存地址在不同存储空间。
目前市面上大多芯片的程序存储器、数据存储器、寄存器和输入输出端口都被统一管理在同一个物理地址空间中。即采用冯诺依曼结构和统一编址方式管理上述模块。
以Cortex-M3为例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Q1芯片上电后是怎么找到用户定义的main,执行用户程序的呢?

首先需要搞清楚,芯片上电和芯片复位是两个概念
芯片上电:指芯片内部寄存器和硬件资源初始化。比如内部硬件关闭所有外设,芯片内部寄存器/内存等初始化等。
芯片复位:是在芯片已经上电的基础上,执行引导复位程序等操作。
其次需要搞清楚,芯片复位之后,不是直接转到用户定义的main函数执行用户程序。
接下来讲解,芯片复位后是如何转到main的。
。。。。。。。。。。。。。。。。。。。。。。。。。。。
芯片复位后会默认把物理地址首地址的值赋值给堆栈指针MSP(为程序中涉及到的形参/函数返回与调用栈的占用做准备),然后把下一个地址的值赋值给程序计数器(这个地址保存的值是复位向量的入口地址,而复位向量的内容通常指向启动代码(即为MCU转到main做准备))。
需要注意:下一个地址相对于首地址的偏移量取决于地址总线的宽度。如果MCU是16位地址线,则下一个地址都在上一个基础上+2,相当于0x0000000的下一个地址是0x00000002;如果MCU是32位地址线,则下一个地址+4,相当于0x0000000下一个地址是0x00000004;如果MCU是64位地址线,则下一个地址+8,相当于0x0000000下一个地址是0x00000008。(存储器每一个单元格只能存8bit数据)
以Cor-M3为例,因为寄存器R13、R15是32位,所以相当于一个地址取32位bit。如果寄存器是16位,那就只取16位bit。即如果这里R13、R15是16位,那相当于取0x00000000-0x00000001地址的数据给R13,0x00000002-0x00000003地址的数据没有用到;同理x00000006-0x00000007地址的数据没有用到。
接下来对复位向量所指内容进行介绍:
复位向量地址处的内容通常是一个跳转指令,指向启动代码的起始地址。于是MCU跳转到启动代码的地址开始执行。(这里需要注意,启动引脚的状态采样是在执行复位向量之前完成的(即采样Boot启动引脚的状态,是在上电之后-复位之前完成的),所以到跳转到启动代码时,MCU已经确定好的从哪里启动(Flash或者SRAM))。
小结
芯片转到用户main之前的操作有:
1)芯片上电。完成硬件初始化/对Boot启动引脚状态进行采样(以确定从哪个地址运行启动程序)等操作
2)紧接着对芯片复位。包括获取复位向量入口地址/跳转到相应启动程序(跳转地址由Boot引脚决定)。其中启动程序包括:设置堆栈大小->初始化中断向量表->转去用户main。
实例
STM32F1XX在重启芯片时,根据BOOT0和BOOT1的状态,在系统时钟的第四个上升沿锁定/选择从主闪存、系统存储器、内置SRAM存储区(三选一)启动。
在这里插入图片描述
ST厂商设定:当BOOT0设置为0:从FLASH启动,即将Flash的起始地址0X08000000的值(栈顶地址)赋值给MSP,0X08000004的值(复位向量首地址)赋值给PC。
当BOOT0设置为1,BOOT1设置为0:从系统存储器启动,即从地址0X1FFFF000的值(栈顶地址)赋值给MSP,0X1FFFF004的值(ST公司的引导下载程序bootloader首地址)赋值给PC。
一般而言,一块新的芯片没有程序,此时,需要从系统存储器启动,引导下载程序并保存到主闪存flash;当程序下载完之后,主闪存flash保存了刚刚下载的程序,此时,将BOOT0置0上电,系统就会从flash启动,执行之前保存的程序。即需要给芯片新下载程序从系统存储器启动,不用修改程序直接执行之前代码就从flash启动。
[注释]这里需要注意:很多芯片上电复位不是从存储器首地址0x00000000和0x00000004取地址,而是从其他物理地址取地址(比如STM32F1xxFlash启动从0x08000000和 0x08000004,STM32F1xxSRAM启动从0X1FFFF000和0X1FFFF004)?
这是因为芯片厂商可以不同的启动方式,把启动地址映射到其他物理地址,但本质上都是执行上述操作,即从第一个地址取栈顶地址,第二个地址去到复位函数首地址那里执行。

为什么芯片复位首先是给堆栈指针和程序指针赋值,而不是其他指针呢?存储器第一个地址和第二个地址存储的数据代表什么呢?

A2:用户程序(代码)本质上分为函数和数据函数是实现功能的指令,用于引导MCU修改存储器某些物理地址的数据(如控制某些物理地址即寄存器的值,操作外设)或者读取存储器某些物理地址的数据(读取某些物理地址即寄存器的值,读取外设状态)。数据是用户定义的,协助MCU指令功能的实现。很多时候,用户在定义函数时,定义了形参或者局部变量,为了让函数更好执行,需要为这些函数运行中的局部变量和形式参数分配空间。
所以芯片一复位,给MSP赋值,是为了给函数运行时的涉及到的形参/函数返回时栈的占用预留空间;给PC赋值,是执行复位程序,目的是引导芯片转去执行用户main函数;
综上所述,芯片一复位就给栈顶指针赋值,这是为程序中涉及到的形参/函数返回与调用栈的占用预留空间,紧接着,得到复位向量入口地址(下一个地址的值,这个地址保存的值是复位向量的入口地址),并转到相应复位程序,复位程序通常是一个跳转指令,根据复位前的Boot引脚状态跳转到不同地址启动,执行完启动代码就转到了用户main。

用户编的程序代码经编译器编译之后放在存储器的哪些位置呢?

A5:经编译器编译后,用户编写的文件会分为程序(代表函数)、数据(常量和变量)
函数放在代码区;常量放在rodata区;全局变量/静态变量放在全局(静态)区;堆中放程序员申请的内存;栈中放编译器申请的内存。其中,未初始化的全局/静态变量或已初始化但初始值为0的全局/静态变量放在全局区的.bss段,已初始化的全局/静态变量放在全局区的.data段。数据内存分配较复杂。
在这里插入图片描述RO:Read Only,包括只读数据(RO Data)和代码(RO Code),占用Flash空间;
RW:Read Write,包括可读写数据(RW Data,有初值,且不为0),占用Flash(存储初值)和RAM(读写);
ZI:Zero Initialized,是初始化为0的数据,占用RAM空间;
.test:相当于RO Code
.constdata:相当于RO Data
.bss:相当于ZI Data
.data:相当于RW Data
占用Flash的有Code/RO Data/RW Data;
占用RAM的有RW Data/ZI Data/Steap/Stack;

数据的存储方式

数据的使用及其存放的位置:
数据有两种类型:常量变量
常量:程序运行过程中,其值不能被改变
整型/浮点型/指数(men(m不能少,n必须为整数))/普通字符’z’/转义字符’\n’/字符串常量”snow”/符号常量#define PI 3.141
变量:程序运行过程中,其值可以改变有类型有名字占存储单元
C中的变量必须:
①在使用之前定义并②确定类型(a = 6,报错)
常变量:有类型有名字占存储单元/值不能被改变
区分符号常量和常变量Const float PI=3.141
符号常量#define是预编译指令,用符号常量表示一个字符串,编译器不会给符号常量名字分配内存空间,预编译后,符号常量不存在。
全局变量:存放在静态存储区,存在于程序的整个运行过程
局部变量:动态存储区,当前函数结束存储空间被释放
静态全局变量:静态存储区,存在于程序整个运行过程,作用域被限制在其源文件内
静态局部变量:静态存储区,存在于程序整个运行过程,作用域限制在定义它的函数内部。当函数调用结束时,静态局部变量的值会被保留,下次调用该函数时,可以使用上次调用后保留的值。

内存地址增方向的数据存储
代码区:存放指令
静态存储区
.rodata区:存放常量/常变量等参数;
.data区:存放已初始化的全局变量/静态局部变量
.bss区:存放未初始化的全局变量/静态局部变量
动态存储区
堆区:程序员分配和释放内存空间,malloc()和free()
栈区:编译器分配和释放内存空间,(形参/局部变量/返回值)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值