嵌入式编程中使用qemu可以做什么?

描述

1.前言嵌入式开发的过程中,很多时间都是要和硬件设备打交道,通过程序控制硬件的具体行为,这些往往是单片机延续下来的开发模式,在目前复杂的嵌入式系统中,很多都需要借助设计模式来进行开发,比如文件系统,网络,图形,算法等等,这些如果能够利用软件模拟器进行开发,可以大大的减少上板调试的时间。减少硬件连接的烦恼,在家也能随时分析软件代码。

在实际项目的开发过程中,qemu也非常的有用,例如当进行网络编程时,往往都会直接使用socket编程,其上层接口符合POSIX接口,这样上层应用的开发和底层驱动便可以很简单的分离出来,将工作细节进行合理的划分。而当进行嵌入式GUI编程设计时,也可以通过framebuffer,来进行各种界面的设计。同时,如果想新学习一款嵌入式编程语言,或者深入理解一些处理器的架构方面的知识,通过裸机编程,直接到qemu上运行也能够非常方便的进行探究工作。

下面举出一些qemu实际好用的应用来进行详细的描述。

2.嵌入式的裸机或RTOS编程

qemu的是指令翻译进行的,所以可以根据实际的需求进行相应的裸机开发和学习,比如语言学习,嵌入式C语言,嵌入式RUST语言,等等项目。一些github上的好用学习型的项目也会对qemu进行支持,用RUST语言在arm上的编程,即使手上没有很好的硬件的条件,也能够去学习RUST语言在嵌入式编程上的使用。

针对arm的编程,qemu也可以模拟出许多的架构出来,通过对这些架构的学习和掌握,可以加快对架构编程的理解。

。/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的支持。

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也做了一些工作,可以更加好的观察分析软件的具体行为。

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, 1118

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的运行效果如下:

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, 1145

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的运行效果如下:

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, 1145

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版本。

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相关的开发工作带来很多便捷。

关于嵌入式图像编程,可以参考

rt-threadsp

aspberry-pi

aspi3-64

相关的bsp,只需要完善显示程序即可。

可以寻找一张bmp的图片,图片大小为800x480的图片。

利用Image2Lcd的工具进行图像转换成数组。

最后将数组程序编译到程序代码中,将该数组放到显存中即可。

驱动

一切准备就绪后,就能够进行显示器的开发了。

#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:

”);

rt_device_control(lcd, RTGRAPHIC_CTRL_GET_INFO, test_lcd_info);

rt_kprintf(“lcd width is %d

”, test_lcd_info-》width);

rt_kprintf(“lcd height is %d

”, test_lcd_info-》height);

rt_kprintf(“lcd bpp is %d

”, 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都是值得去研究和使用的工具。

编辑:jq

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分