qemu模拟armv7,裸机运行

基于armv7的设备,在qemu下启动,裸机运行
2024.8.2更新,添加简单的启动,task调度代码


文档资料

imx6ull 官方资料链接
在这里插入图片描述
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环境搭建

  1. qemu源码下载
  2. 100ask修改版qemu源码下载
  3. 100ask 6ullqemu
# 本文运行方式,使用官方的qemu也能正常运行,只使用imx6ull的功能
 git clone https://e.coding.net/weidongshan/ubuntu-18.04_imx6ul_qemu_system.git
  1. 6ull裸机开发资料
git clone https://e.coding.net/weidongshan/01_all_series_quickstart.git
git clone https://e.coding.net/weidongshan/noos/cortexA7_windows_tools.git
git clone https://e.coding.net/weidongshan/hardware/doc_and_source_for_hardware.git
git clone https://e.coding.net/weidongshan/noos/doc_and_source_for_mcu_mpu.git

编译器下载:arm-none-linux-gnueabihf

 # ,用 aria2c 比 wget 实在是快太多了
 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指针
在这里插入图片描述
在这里插入图片描述
疑问: armv7进入异常的时候根据规范描述是可以选择进入arm,或者thumb状态的?那么如果可以选择thumb编译的时候要按照thumb的格式编译

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, 不再产出中断信号)

在这里插入图片描述
主要操作寄存器:
CNTFRQ:读取设置频率
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级

在这里插入图片描述

在这里插入图片描述在这里插入图片描述
GICC_BPR ->0,所有支持抢占,->7,所有非抢占模式

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:
操作方法:

  1. GICD_TYPER:获取支持的中断信息
  2. GICD_ICENABLERn:禁止中断转发
  3. GICC_PMR:设置中断掩码
  4. GICC_BPR:设置group,Subpriority(0,全抢占,7,禁止抢占)
  5. GICD_CTLR:使能
  6. GICC_CTLR:使能

MMU相关

多核之间的操作全局变量的问题?

  1. 如何保证数据是最新的,手动刷缓存?
    a . memory type中有三种概念,是不是把全局变量定位到device区域,就不需要考虑缓存的问题了
  2. 硬件支持,只要写全局变量,自动处理,用户无感?

arm寄存器

cpsr

在这里插入图片描述

N, bit[31] Negative condition flag.
Z, bit[30] Zero condition flag.
C, bit[29] Carry condition flag.
V, bit[28] Overflow condition flag.

A, bit[8] Asynchronous abort mask bit.
I, bit[7] IRQ mask bit.
F, bit[6] FIQ mask bit.
	0  Exception not masked.
	1 Exception masked
T, bit[5] Thumb execution state bit
	J	T
	0	0	ARM
	0	1	Thubm
	1	0 	Jazelle
	1	1 	ThumbEE
M[4:0], bits[4:0]  Mode field
	User		10000
	FIQ			10001
	IRQ			10010
	Supervisor	10011
	Monitor		10110
	Abort		10111
	Hyp			11010
	Undefined	11011
	System		11111

汇编指令

c嵌汇编 ( Mixing C and assembly code )

ARM64基础4:在C语言中嵌入ARM64汇编代码

__asm__ [__volatile__] ( assembler template 
           : [output operand list]             /* optional */
           : [input operand list]              /* optional */
           : [clobbered register list]         /* optional */
           );

在这里插入图片描述在这里插入图片描述

伪指令

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 功能作用

在这里插入图片描述

做了哪些工作

  1. 设置异常向量表
  2. 设置模式下的sp,关mmu、icache、dcache
  3. 初始化gic(distribution,cpuifterface)
  4. 设置arch time
  5. 初始化串口
  6. 两个task进行任务切换,实现中。。。

qemu启动,task调度代码

  1. 使能arch timer
  2. 初始化 uart,支持printf打印
  3. 支持添加task,task循环调度,上下文切换,任务栈保存恢复
  4. 代码结构如下:代码见附件
tree
.
├── build.sh # 编译执行,可能需要调整编译器路径
├── CMakeLists.txt
├── lib
│   ├── gic.c
│   ├── gic.h
│   ├── timer.c
│   ├── timer.h
│   ├── uart.c
│   └── uart.h
├── out
├── qemu.sh # qemu执行脚本
└── src
    ├── link.ld
    ├── main.c
    ├── my_printf.c
    ├── my_printf.h
    ├── start.S
    └── task_test.c

参考文章

在线脑图工具 processon
qemu嵌入式arm快速体验
QEMU imx6ul开发板环境搭建
百问网资料下载中心
uboot源码
qemu很老的例程
githbub文档语法
【从0学ARM】你不了解的ARM处理异常之道

视频
STM32:从0写RTOS,学习内部机制 文档介绍

记录问题

  1. armv7需要设置异常向量表,即 VBAR 寄存器
    在上电reset_handler中调用svc #0无法调转swi_handler,所以才发现需要设置异常向量中断
  2. arm arch timer
    每个核上的timer一直是在计数的,设置中断信号之后,要在中断函数中更新CompareValue或TimeValue的值
  3. 无法通过通过cp15读取gic基地址 ?, Configuration Base Address Register, (cortexa7,p137)
MRC p15, 4, <Rt>, c15, c0, 0; Read Configuration Base Address Register
  1. 读取cpuid,MPIDR
MRC p15, 0, <Rt>, c0, c0, 5; Read Multiprocessor Affinity Register

在这里插入图片描述
5. 调试阶段发现有些代码执行异常,比如c和汇编相互调用的时候,发现可能是armv7支持thumb造成的,-marm强制arm态
6. task栈切换
在这里插入图片描述

  1. task中添加uint64变量执行异常(Undefined_Handler)
    分析汇编代码发现了vmov指令,在编译选项中添加 -mfpu=vfpv3解决,vfpv3和neon的区别是啥呢?
    在这里插入图片描述
  2. arm编译器的区别

遇到编译链接问题,注意到编译器不一样,记录一下

在这里插入图片描述
9. c 函数库 (newlic 和 glic)
在这里插入图片描述
C 函数库 (libc,glibc,uClibc,newlib)

  1. 遇到一个编译很坑的问题,查了好久发现是 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,编译详细日志

  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后缀文件

  1. gcc 链接 和 ld 链接异同点
    在这里插入图片描述
  2. 安装的 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

  1. 基于imx6ull的sdk,在qemu上实现freertos,编译正常,运行的时候 portYIELD_WITHIN_API 这个就崩了,其实调用svc异常,主要分析定位异常原因

根本原因是问题1,未设置vbar寄存器,如何定位呢,

  1. 首先肯定可以定位到哪一行的代码执行异常
  2. 在执行异常代码的时候切换到汇编代码查看,可以分析异常原因,比如此异常时候,跳到了0x000000c8, 这个地址是不正常的(由汇编文件可以分析向量地址0x80002008),就能知道是哪里的异常了
  1. 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) 即可
话说csdn的vip真的是x,质量还不行,哪里有其文章的平台

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值