基于armv7的设备,在qemu下启动,裸机运行
文档资料
Arm官方文档:
ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition
ARM Cortex-A Series Programmer’s Guide for ARMv7-A
Arm A-profile Architecture Registers
Arm A-profile A32/T32 Instruction Set Architecture
Arm A-profile A64 Instruction Set Architecture
arm_compiler_user_guide_100748_6.22_00_en
Arm Architecture Reference Manual for A-profile architecture : (AArch64)
gcc-arm-10.3-2021.07-x86_64-arm-none-eabi.tar.xz https://developer.arm.com/downloads/-/gnu-a
gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 https://developer.arm.com/downloads/-/gnu-rm
qemu环境搭建
- qemu源码下载
- 100ask修改版qemu源码下载
- 100ask 6ullqemu
1# 本文运行方式,使用官方的qemu也能正常运行,只使用imx6ull的功能
2 git clone https://e.coding.net/weidongshan/ubuntu-18.04_imx6ul_qemu_system.git
- 6ull裸机开发资料
1 git clone https://e.coding.net/weidongshan/01_all_series_quickstart.git
2 git clone https://e.coding.net/weidongshan/noos/cortexA7_windows_tools.git
3 git clone https://e.coding.net/weidongshan/hardware/doc_and_source_for_hardware.git
4 git clone https://e.coding.net/weidongshan/noos/doc_and_source_for_mcu_mpu.git
编译器下载
:arm-none-linux-gnueabihf
1 # ,用 aria2c 比 wget 实在是快太多了
2 https://armkeil.blob.core.windows.net/developer/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-linux-gnueabihf.tar.xz
armv7架构
寄存器
异常处理
lr的地址调整:关于ARM的PC指针
A3:Application Level Memory Model
三种属性的设备: Normal Device Strongly-ordered 猜测:和 mmu,或者乱序执行有关?
General Timer
产生timer中断信号必须同时满足以下两个条件:
- CompareValue或者TimerValue计时时间到
- timer必须enable且是unmask
消除timer中断信号只需要满是下面的其中一个条件即可:
- 重新设置CompareValue或TimerValue寄存器,使其两个定时条件都不满足
- 使用timer掩码(CNTP_CTL.IMASK)
- 禁用timer(CNTP_CTL.ENABLE, 不再产出中断信号)
CNTPCT:读取定时器的计数值(64位的)
CNTP_CTL:控制寄存器(是否使能屏蔽定时器中断,状态指示是否发生中断)
CNTP_CVAL:设置定时器的计时时间(64位,向上加)
用法如:setTimer(getSystemTick()+delayTick)
CNTP_TVAL:设置定时器的计时时间(32位有符号,向下减)
用法如:setTimer(delayTick)
Cortex A7
gic
中断源ID:
Software Generated Interrupts:0-15
Private Peripheral Interrupts:16-31
安全物理定时器ID:29
非安全物理定时器ID:30
Shared Peripheral Interrupts :32-1019,支持480个 (cortexa7,179)
特殊中断ID
GIC memory-map
Memory regions used for these registers must be marked as Device or Strongly-ordered in the translation tables.
Memory regions marked as Normal Memory cannot access any of the GIC registers, instead access caches or external memory as required ??
优先级:32级
gic寄存器
0x000: RW: GICD_CTLR: 控制寄存器,是否使能 gic distributor
0x004: RO: GICD_TYPER: bit5-7,支持的cpu数量+1,bit0-4,gic支持的中断个数x32
0x008: RO: GICD_IIDR: bit24-31,ProductId,bit16-19,产品编号?bit12-15,revision num, [bit0-6,0x3b,bit7,0,bit8-11,0x4]
0x080: RW: GICD_IGROUPRn: 0,Group0,1,Group1, 0x080+(n»5)«2, n&0x1F
0x100: RW: GICD_ISENABLERn: 0,interrupt is disabled, 1,interrupt is enable; 写1->1
0x180: RW: GICD_ICENABLERn: 禁中断 0,disabled, 1,enabled; 写1->0
0x200: RW: GICD_ISPENDRn: 0,no pending,1,ppi/swi pending on this cpu, spi pending any cpu
写1, edge: inactive->pending; acitve->active and pending; pending->pending
level: inactive->pending; active->active and pending; pending->pending
0x280: RW: GICD_ICPENDRn: 0,no pending,1,ppi/swi pending on this cpu, spi pending any cpu
写1, edge: pending->inactive; active and pending->active
level: pending->inactive; active and pending->active ? todo
0x300: RW: GICD_ISACTIVERn: 0, not active, 1,active; 写1->1
0x380: RW: GICD_ICACTIVERn: 0, not active, 1,active; 写1->0
0x400: RW: GICD_IPRIORITYRn: 设置优先级 0, proiority0,1,2,3 0x400+(m»2)«2 (m&0x3)*8
0x800: RW: GICD_ITARGETSRn: 绑定cpu,0, target cpu, byte0,1,2,3, each byte:cpu/isr
GICD_ITARGETSR0-7,只读的(id0-31,swi中断)其他spi可写
0xc00: RW: GICD_ICFGRn: low,0,N-N,1,1-N; high, 0,level,1,edge 0xc00+(m»4)*4 (m&0xf)*2
0xe00: RW: GICD_NSACRn: 0,no-secure
0xf00: RO:GICD_SGIR: 软件参数中断,bit24-25,0,CPUTargeList,1,其他cpu,2,to self,bit16-23,target cpu,bit-cpu;
bit15,0,SGIINTID,Group0,1,Group1;bit0-3,Interrupt id 0-15
0xf10: RW: GICD_CPENDSGIRn: byte0,1,2,3; 0,not pending, 1,pending; write 1, remove pending ?
0xf20: RW: GICD_SPENDSGIRn: byte0,1,2,3; 0,not pending, 1,pending; write 1, add pending ?
0x0000: RW: GICC_CTLR: bit0:0,禁用中断信号,1使能中断信号
0x0004: RW: GICC_PMR: 中断优先级掩码 bit0-7,bit0:128 bit0-1:64 bit0-2:32 bit0-3:16
0x0008: RW: GICC_BPR:中断group分割点 bit0-2
0x000c: RO: GICC_IAR: 响应的中断号,bit10-12,swi请求中断的cpu,bit0-9,中断id
0x0010:WO:GICC_EOIR:中断处理完成,bit10-12,同上,同上
0x0014: RO: GICC_RPR: bit0-7,当前运行的中断优先级
0x0018: RO: GICC_HPPIR: 最高pending的优先级,bit10-12,swi产生中断的cpu,bit0-9:最高pending中断id
0x001c: RW: GICC_ABPR:
0x0020: RO: GICC_AIAR:
0x0024: WO: GICC_AEOIR:
0x0028: RO: GICC_AHPPIR:
0x00d0: RW: GICC_APRn:
0x00e0: RW: GICC_NSAPRn:
0x00fc: RO: GICC_IIDR:读取productid, gic版本
0x1000:WO:GICC_DIR:
操作方法:
- GICD_TYPER:获取支持的中断信息
- GICD_ICENABLERn:禁止中断转发
- GICC_PMR:设置中断掩码
- GICC_BPR:设置group,Subpriority(0,全抢占,7,禁止抢占)
- GICD_CTLR:使能
- GICC_CTLR:使能
MMU相关
多核之间的操作全局变量的问题?
- 如何保证数据是最新的,手动刷缓存?
a . memory type中有三种概念,是不是把全局变量定位到device区域,就不需要考虑缓存的问题了 - 硬件支持,只要写全局变量,自动处理,用户无感?
arm寄存器
cpsr
1N, bit[31] Negative condition flag.
2Z, bit[30] Zero condition flag.
3C, bit[29] Carry condition flag.
4V, bit[28] Overflow condition flag.
5
6A, bit[8] Asynchronous abort mask bit.
7I, bit[7] IRQ mask bit.
8F, bit[6] FIQ mask bit.
9 0 Exception not masked.
10 1 Exception masked
11T, bit[5] Thumb execution state bit
12 J T
13 0 0 ARM
14 0 1 Thubm
15 1 0 Jazelle
16 1 1 ThumbEE
17M[4:0], bits[4:0] Mode field
18 User 10000
19 FIQ 10001
20 IRQ 10010
21 Supervisor 10011
22 Monitor 10110
23 Abort 10111
24 Hyp 11010
25 Undefined 11011
26 System 11111
汇编指令
c嵌汇编 ( Mixing C and assembly code )
1__asm__ [__volatile__] ( assembler template
2 : [output operand list] /* optional */
3 : [input operand list] /* optional */
4 : [clobbered register list] /* optional */
5 );
伪指令
global:别的文件可以调用当前汇编文件里的函数
extern:当前汇编可以调用别的文件的函数
thumb: 指令来指明接下来的代码应以 Thumb 模式进行汇编
条件执行
A8.3 Conditional execution
msr
srs
rfe
编译链接
ARM链接脚本详解
linker script ld链接脚本语法简介
GCC Arm 12.2编译提示 LOAD segment with RWX permissions 警告
–start-group 和 –end-group 功能作用
做了哪些工作
- 设置异常向量表
- 设置模式下的sp,关mmu、icache、dcache
- 初始化gic(distribution,cpuifterface)
- 设置arch time
- 初始化串口
- 两个task进行任务切换,实现中。。。
qemu启动,task调度代码
- 使能arch timer
- 初始化 uart,支持printf打印
- 支持添加task,task循环调度,上下文切换,任务栈保存恢复
1tree
2├── build.sh # 编译执行,可能需要调整编译器路径
3├── CMakeLists.txt
4├── lib
5│ ├── gic.c
6│ ├── gic.h
7│ ├── timer.c
8│ ├── timer.h
9│ ├── uart.c
10│ └── uart.h
11├── out
12├── qemu.sh # qemu执行脚本
13└── src
14 ├── link.ld
15 ├── main.c
16 ├── my_printf.c
17 ├── my_printf.h
18 ├── start.S
19 └── task_test.c
参考文章
在线脑图工具 processon
qemu嵌入式arm快速体验
QEMU imx6ul开发板环境搭建
百问网资料下载中心
uboot源码
qemu很老的例程
githbub文档语法
【从0学ARM】你不了解的ARM处理异常之道
记录问题
- armv7需要设置异常向量表,即 VBAR 寄存器 在上电reset_handler中调用svc #0无法调转swi_handler,所以才发现需要设置异常向量中断
- arm arch timer 每个核上的timer一直是在计数的,设置中断信号之后,要在中断函数中更新CompareValue或TimeValue的值
- 无法通过通过cp15读取gic基地址 ?, Configuration Base Address Register, (cortexa7,p137)
1MRC p15, 4, <Rt>, c15, c0, 0; Read Configuration Base Address Register
- 读取cpuid,MPIDR
1MRC p15, 0, <Rt>, c0, c0, 5; Read Multiprocessor Affinity Register
- task中添加uint64变量执行异常(Undefined_Handler)
分析汇编代码发现了vmov指令,在编译选项中添加 -mfpu=vfpv3解决,vfpv3和neon的区别是啥呢?
- arm编译器的区别
遇到编译链接问题,注意到编译器不一样,记录一下
- 遇到一个编译很坑的问题,查了好久发现是 project enable_language 和 设置编译器,先后顺序导致的问题 此案例中,先设置project(IMX6ULL ASM C),后设置编译器即可解决问题
那么是如何发现的问题呢,因为我开始的时候用的是 arm-none-linux-gnueabihf-gcc 编译freertos,测试qemu启动,简单的task切换都没有问题,然后开始加入freertos源码,编译就出现一堆奇怪的问题(undefined reference __gcc_personality_v0 / __aeabi_unwind_cpp_pr0)等一大堆问题,先是问了人工智能改了一大圈也无果 后面我发现其他工程用的编译器是 arm-none-eabi-gcc(上述问题8), 换了之后直接报错 < is not able to compile a simple test program. undefined reference to `_exit’ >, 对我来说有是一个奇怪的问题 根据ai的回答,加上_exit函数也不行,后面我就用cmake写了一个最小编译环境,和我工程交叉测试发现了问题所在,不过根本原因也没深究,也可能只是碰巧解决了问题吧
VERBOSE=1,编译详细日志
- CMAKE_EXE_LINKER_FLAGS_xxx 和 CMAKE_BUILD_TYPE
CMAKE_<LANG>_FLAGS_<CONFIG> , 测试发现 CMAKE_BUILD_TYPE (release,debug,或者自定义标签),会决定前面的<CONFIG>,
Cmake笔记
12. xxxx.c.obj 和 xxx.c.o 生成差异
CMAKE_SYSTEM_NAME 变量用于设置目标系统的操作系统。不同的操作系统通常有不同的编译工具链和库路径。通过设置 CMAKE_SYSTEM_NAME,你告诉CMake期望的目标系统是什么,这影响了CMake配置过程中的行为。例如: Windows、Linux、Darwin(macOS)、Generic(通常用于裸机或嵌入式系统)
set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR arm) 测试设置之后会生成obj后缀文件
- gcc 链接 和 ld 链接异同点
- 安装的 arm-none-eabi-gdb 报错:libncurses.so.5: cannot open shared object file: No such file or directory
网上的答案 sudo apt install libncurses5* 搞定,不知道是不是我的最新ubuntu24的问题,找不到下载源,所有就手动下载安装
ubuntu lib库下载地址:https://packages.ubuntu.com/ http://security.ubuntu.com/ubuntu/pool/universe/n/ncurses/libncurses5_6.2-0ubuntu2.1_amd64.deb http://security.ubuntu.com/ubuntu/pool/universe/n/ncurses/libtinfo5_6.2-0ubuntu2.1_amd64.deb 版本:6.2-0ubuntu2.1, 架构:i386,amd64
64位电脑只需要 amd64,旧的gdb版本需要libncurses5, Distribution选择any
- 基于imx6ull的sdk,在qemu上实现freertos,编译正常,运行的时候 portYIELD_WITHIN_API 这个就崩了,其实调用svc异常,主要分析定位异常原因
根本原因是问题1,未设置vbar寄存器,如何定位呢,
- 首先肯定可以定位到哪一行的代码执行异常
- 在执行异常代码的时候切换到汇编代码查看,可以分析异常原因,比如此异常时候,跳到了0x000000c8, 这个地址是不正常的(由汇编文件可以分析向量地址0x80002008),就能知道是哪里的异常了
- is not able to compile a simple test program
https://discourse.cmake.org/t/ the-c-compiler-is-not-able-to-compile-a-simple-test-program-when-compiling-using-arm-gnu-toolchain/8215
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) 即可
评论