嵌入式编程中使用qemu能够做什么?
- 1.前言
- 2.嵌入式的裸机或RTOS编程
- 3.利用qemu网络编程研究
- 4.嵌入式图形开发
- 5.进行嵌入式Linux的开发
- 6.小结
1.前言
嵌入式开发的过程中,很多时间都是要和硬件设备打交道,通过程序控制硬件的具体行为,这些往往是单片机延续下来的开发模式,在目前复杂的嵌入式系统中,很多都需要借助设计模式来进行开发,比如文件系统,网络,图形,算法等等,这些如果能够利用软件模拟器进行开发,可以大大的减少上板调试的时间。减少硬件连接的烦恼,在家也能随时分析软件代码。
在实际项目的开发过程中,qemu也非常的有用,例如当进行网络编程时,往往都会直接使用socket编程,其上层接口符合POSIX接口,这样上层应用的开发和底层驱动便可以很简单的分离出来,将工作细节进行合理的划分。而当进行嵌入式GUI编程设计时,也可以通过framebuffer,来进行各种界面的设计。同时,如果想新学习一款嵌入式编程语言,或者深入理解一些处理器的架构方面的知识,通过裸机编程,直接到qemu上运行也能够非常方便的进行探究工作。
下面举出一些qemu实际好用的应用来进行详细的描述。
2.嵌入式的裸机或RTOS编程
qemu的是指令翻译进行的,所以可以根据实际的需求进行相应的裸机开发和学习,比如语言学习,嵌入式C语言,嵌入式RUST语言,等等项目。一些github上的好用学习型的项目也会对qemu进行支持,用RUST语言在arm上的编程,即使手上没有很好的硬件的条件,也能够去学习RUST语言在嵌入式编程上的使用。
针对arm的编程,qemu也可以模拟出许多的架构出来,通过对这些架构的学习和掌握,可以加快对架构编程的理解。
代码语言:javascript复制./qemu-system-arm -M virt -cpu ?
Available CPUs:
arm1026
arm1136
arm1136-r2
arm1176
arm11mpcore
arm926
arm946
cortex-a15
cortex-a7
cortex-a8
cortex-a9
cortex-m0
cortex-m3
cortex-m33
cortex-m4
cortex-m55
cortex-m7
cortex-r5
cortex-r5f
max
pxa250
pxa255
pxa260
pxa261
pxa262
pxa270-a0
pxa270-a1
pxa270
pxa270-b0
pxa270-b1
pxa270-c0
pxa270-c5
sa1100
sa1110
ti925t
然而嵌入式开发往往会和硬件打交道,qemu也提供了不同类别的硬件,比如flash,网卡,sd卡,中断,串口等等,这些对于学习不同的体系架构,也有着非常关键的作用。
比如学习cortex-m3或者aarch64编程,采用qemu,运行自己写的裸机代码,能够非常方便的进行各种实验。
在进行rtos的开发过程中,经常会采用qemu作为调试工具,进行龙芯、树莓派、riscv相关的开发和验证工作。在rtos中,比较关键的是上下文的切换,通过对寄存器信息的保存和恢复,另外就是中断的处理,能够很好的理解架构的底层编程方式。
以前的时候,也做过aarch64上的qemu编程,也是最开始基于qemu,然后慢慢的移植到树莓派上面,因为外设一致,代码层面不用改变,直接可以将qemu运行通过的固件放到树莓派的sd卡中也一样能够正常的运行。
上图是在qemu的rt-thread/bsp/raspberry-pi/raspi3-64
中编译的固件在qemu上的运行效果,基本上完成对aarch64体系架构中的栈帧、中断、mmu的支持,以及外设部分SD卡、图形、串口、mbox的支持。该固件也可以直接放到树莓派硬件的sd卡中运行,其效果和在qemu效果一样。
除此之外,我也在qemu的支持上做了一些扩展开发,比如在riscv的生态支持上对gd32的rv-star在中科院软件研究所的基础上做了一些研究,同时对nuclei的各种处理器系列做了适配。这样对于软件层面的验证更加有用,比如去运行一下nuclei-sdk,或者对于RISCV的V扩展的支持的nmsis的支持。
代码语言:javascript复制qemu-system-riscv64 -M nuclei_n,download=ilm -cpu nuclei-nx600fdp -nodefaults -nographic -serial stdio -kernel CMSIS/nmsis_release/NMSIS/DSP/Examples/RISCV/riscv_matrix_example/dsp_example.elf
这样可以进行相关的dsp的验证工作。因为nmsis
是基于arm的cmsis
在riscv上的一份移植,其中实现了许多的加速运算的demo,比如矩阵运算,卷积,图像处理等等,这些指令同样也可以在nuclei qemu中计算出正确的结果。
由于对riscv的p扩展和v扩展的支持,使得其行为和实际硬件板子无差异。在qemu做算法优化和研究也是非常值得去尝试的。虽然qemu是用软件去模拟真实计算结果,但是从指令集的优化层面上来说,当功能逻辑实现正确后再移植到板子上做性能测试,这才是高效的处理方法。
在支持baremetal编程和rtos编程方面,nuclei-sdk也做了一些工作,可以更加好的观察分析软件的具体行为。
代码语言:javascript复制qemu-system-riscv32 -M nuclei_n,download=ilm -cpu nuclei-n201 -nodefaults -nographic -serial stdio -kernel application/rtthread/msh/msh.elf
Nuclei SDK Build Time: May 31 2021, 11:48:18
Download Mode: ILM
CPU Frequency 168290222 Hz
| /
- RT - Thread Operating System
/ | 3.1.3 build May 31 2021
2006 - 2019 Copyright by rt-thread team
Hello RT-Thread!
msh >
msh >ps
thread pri status sp stack size max used left tick error
-------- --- ------- ---------- ---------- ------ ---------- ---
tshell 6 ready 0x000000d8 0x00001000 10% 0x0000000a 000
tidle 7 ready 0x00000078 0x00000200 23% 0x00000020 000
main 2 suspend 0x000000b8 0x00000400 17% 0x00000013 000
msh >
也可以支持其他的rtos,例如下面的ucosii和freertos等等。
ucosii的运行效果如下:
代码语言:javascript复制qemu-system-riscv32 -M nuclei_n,download=ilm -cpu nuclei-n201 -nodefaults -nographic -serial stdio -kernel application/ucosii/demo/demo.elf
Nuclei SDK Build Time: May 31 2021, 11:49:45
Download Mode: ILM
CPU Frequency 182521692 Hz
Start ucosii...
create start task success
start all task...
task3 is running... 1
task2 is running... 1
task1 is running... 1
task3 is running... 2
task2 is running... 2
task3 is running... 3
task2 is running... 3
task1 is running... 2
task3 is running... 4
task2 is running... 4
task3 is running... 5
task2 is running... 5
freertos的运行效果如下:
代码语言:javascript复制qemu-system-riscv32 -M nuclei_n,download=ilm -cpu nuclei-n201 -nodefaults -nographic -serial stdio -kernel application/freertos/demo/demo.elf
Nuclei SDK Build Time: May 31 2021, 11:49:45
Download Mode: ILM
CPU Frequency 232205516 Hz
Before StartScheduler
Enter to task_1
task1 is running 0.....
Enter to task_2
task2 is running 0.....
timers Callback 0
timers Callback 1
task1 is running 1.....
task2 is running 1.....
timers Callback 2
timers Callback 3
task1 is running 2.....
task2 is running 2.....
利用qemu作为底层研究将会非常的高效。同时,善于借助gdb等调试工具,将能够非常容易的找到问题出现的点。
3.利用qemu网络编程研究
由于qemu的网络可以直接连接主机的网络,对这方面的研究可以从网络协议栈,网络的上层应用编程等等进行研究。例如去研究lwip协议栈的实现等等。
我写过一个qemu上的e1000网卡设备的驱动,针对于qemu riscv64的virt版本。
代码语言:javascript复制https://github.com/bigmagic123/rt-thread/tree/riscv_virt_network
针对rt-thread的qemu riscv的virt64版本,可以进行如下的设置。
其中其底层的驱动为e1000,为qemu提供了网络数据的收发、以及网络数据包的接收中断服务。
借助rt-thread上适配的lwip驱动程序,可以非常容易的实现上层网络编程应用。
比如借助rt-thread的IOT软件包
使能一些例子
最后可以测试一下web的通信情况。
当然,上述这个例子只是一个非常简单网络编程的演示,其中socket的编程部分实际上是通用的,无论是arm架构、mips架构或者riscv架构,借助qemu的好处在于可以采用一个架构平台,进行协议栈或者上层开发后,可以无缝的移植到自己的真实的板子上,非常方便进行整体业务的联调。
对于qemu riscv64 virt平台,整个系统从底层的virtio或者e1000的网卡设备提供数据的收发、中断消息机制之外,rt-thread也通过提供lwip协议栈的支持,这样整个网络链路才是比较合理的。开发起来也比较的方便。
在物联网模块的开发方面,采用qemu,也可以不用rt-thread,直接裸机驱动virt上的e1000网卡驱动,然后借助对寄存器的读写操作,移植其他的网络协议栈,从而实现网络数据的收发工作,网络编程的上层对接阿里云、腾讯云等云服务器,非常容易的实现业务的编程,同时调试方面,qemu的gdb调试功能也是非常的强大,也可以dump出内存进行ram parse分析。
4.嵌入式图形开发
因为嵌入式编程的实现,也会多少涉及到图形编程,当接上LCD屏后,其中的显示驱动对上层应用暴露出来的实际上是一块显存,通过对显存的读写,flush进行lcd的图像更新。
在图像编程方面,qemu也提供了显示窗口。这种显示窗口可以为gui相关的开发工作带来很多便捷。
关于嵌入式图像编程,可以参考
代码语言:javascript复制rt-threadbspraspberry-piraspi3-64
相关的bsp,只需要完善显示程序即可。
可以寻找一张bmp的图片,图片大小为800x480的图片。
利用Image2Lcd的工具进行图像转换成数组。
最后将数组程序编译到程序代码中,将该数组放到显存中即可。
一切准备就绪后,就能够进行显示器的开发了。
代码语言:javascript复制#define LCD_BUF_SIZE (800 * 480)
extern unsigned char gImage_1[];
void lcd_test()
{
struct rt_device *lcd;
struct rt_device_graphic_info *test_lcd_info;
test_lcd_info = rt_malloc(sizeof( struct rt_device_graphic_info));
//找到lcd lcd = (struct rt_device *)rt_device_find("lcd");
rt_kprintf("lcd get info:n");
rt_device_control(lcd, RTGRAPHIC_CTRL_GET_INFO, test_lcd_info);
rt_kprintf("lcd width is %dn", test_lcd_info->width);
rt_kprintf("lcd height is %dn", test_lcd_info->height);
rt_kprintf("lcd bpp is %dn", test_lcd_info->bits_per_pixel);
rt_memcpy(test_lcd_info->framebuffer, &gImage_1[0], LCD_BUF_SIZE*4);
//刷新图片
rt_device_control(lcd, RTGRAPHIC_CTRL_RECT_UPDATE, NULL);
rt_thread_delay(20);
}
如果要进行触摸操作,qemu也进行了基本的支持,只需移植相关的底层驱动即可进行开发工作,非常的高效和方便。
5.进行嵌入式Linux的开发
进行Linux开发工作,如果深入去学习某一个设备的开发,当然少不了不断的对Linux的内核部分进行编译和下载,这是一个十分耗时的工作,如果只是进行应用程序的开发,可能感觉不到许多的差别,但是一旦涉及到Linux内核的分析,频繁的下载也会浪费大量的时间。
当使用qemu后,这种问题将会得到很好的解决,采用qemu进行内核层面的裁剪,进行内核层面模块化的验证工作后,再进行移植,让其变得更加通用,不仅仅针对这个项目有效,而且也为自己积累了很多经验。
从分析linux的loader,分析Linux的驱动框架,内存管理,多核管理等等,都能够非常方便进行调试工作。
在实际硬件设备没有稳定之前,对软件项目进行评估,qemu是非常好用的工具。
6.小结
接触很多软件开发工作中,使用qemu确实能够在一定程度上节省时间,提高软件调试与分析的效率。
用软件模拟硬件的操作行为,本质上来说和实际的硬件操作区别不大,因为在嵌入式编程中,最底层的指令集的行为已经在qemu中实现的很好了,硬件模拟方面,qemu也大致能够模拟操作寄存器后,处理器的行为,这些在对qemu的底层支持和学习的过程中已经进行了大量的实验和研究。
理解qemu的使用,会对嵌入式软件原理有着更加深刻的理解,从更大的层面上来说,虚拟化的行为本来就是一种很好的解决方案,去设计一个嵌入式软件方案,去演示一个底层软件,或者节约下载调试时间,开发嵌入式上层业务系统软件的功能层面来说,qemu都是值得去研究和使用的工具。