Using QEMU for Embedded Systems Development, Part 2

http://www.linuxforu.com/2011/07/qemu-for-embedded-systems-development-part-2/



Using QEMU for Embedded Systems Development, Part 2

By Manoj Kumar on July 1, 2011 in Coding, Developers · 3 Comments

QEMU for embedded programming
In the previous articles, we learnt how to use QEMU for a generic Linux OS installation, for networking using OpenVPN and TAP/TUN, for cross-compilation of the Linux kernel for ARM, to boot the kernel from QEMU, and how to build a small filesystem and then mount it on the vanilla kernel. Now we will step out further.

First of all, I would like to explain the need for a bootloader. The bootloader is code that is used to load the kernel into RAM, and then specify which partition will be mounted as the root filesystem. The bootloader resides in the MBR (Master Boot Record). In general-purpose computing machines, an important component is the BIOS (Basic Input Output System). The BIOS contains the low-level drivers for devices like the keyboard, mouse, display, etc. It initiates the bootloader, which then loads the kernel. Linux users are very familiar with boot-loaders like GRUB (Grand Unified Boot-Loader) and LILO (Linux Loader).

Micro-controller programmers are very familiar with the term “Bare-Metal Programming”. It means that there is nothing between your program and the processor — the code you write runs directly on the processor. It becomes the programmer’s responsibility to check each and every possible condition that can corrupt the system.

Now, let us build a small program for the ARM Versatile Platform Baseboard, which will run on the QEMU emulator, and then print a message on the serial console. Downloaded the tool-chain for ARM EABI from here. As described in the previous article, add this tool-chain in your PATH.

By default, QEMU redirects the serial console output to the terminal, when it is initialised with the nographic option:

qemusystemarmhelp|grepnographicnographicdisablegraphicaloutputandredirectserialI/Ostoconsole.Whenusingnographic,pressctrlahtogetsomehelp.Wecanmakegooduseofthisfeature;letswritesomedatatotheserialport,anditcanbeagoodworkingexample.Beforegoingfurther,wemustmakesurewhichprocessortheGNUEABItoolchainsupports,andwhichprocessorQEMUcanemulate.Thereshouldbeasimilarprocessorsupportedbyboththetoolchainandtheemulator.LetscheckfirstinQEMU.Intheearlierarticles,wecompiledtheQEMUsourcecode,sousethatsourcecodetogetthelistofthesupportedARMprocessors: cd  (your-path)/qemu/qemu-0.14.0/hw
grep  "arm" versatilepb.c #include "arm-misc.h" static struct arm_boot_info versatile_binfo; cpu_model = "arm926";  It’s very clear that the “arm926″ is supported by QEMU. Let’s check its availability in the GNU ARM tool-chain: cd (your-path)/CodeSourcery/Sourcery_G++_Lite/share/doc/arm-arm-none-eabi/info
catgcc.info|greparm|headn20..strongarm1110,arm8,arm810,arm9,arm9e,arm920,arm920t,arm922t,arm946es,arm966es,arm968es,arm926ejs,arm940t,arm9tdmi,arm10tdmi,arm1020t,arm1026ejs,arm10e,arm1020e,arm1022e,arm1136js,Great!!TheARM926EJSprocessorissupportedbytheGNUARMtoolchain.Now,letswritesomedatatotheserialportofthisprocessor.AswearenotusinganyheaderfilethatdescribestheaddressofUART0,wemustfinditmanually,fromthefile(yourpath)/qemu/qemu0.14.0/hw/versatilepb.c:/0x101f0000Smartcard0.//0x101f1000UART0.//0x101f2000UART1.//0x101f3000UART2./Opensourcecodeissopowerful,itgivesyoueachandeverydetail.UART0ispresentataddress0x101f1000.Fortestingpurposes,wecanwritedatadirectlytothisaddress,andcheckoutputontheterminal.Ourfirsttestprogramisabaremetalprogramrunningdirectlyontheprocessor,withoutthehelpofabootloader.Wehavetocreatethreeimportantfiles.Firstofall,letusdevelopasmallapplicationprogram(init.c):volatileunsignedcharconstUART0PTR=(unsignedchar)0x0101f1000;voiddisplay(constcharstring)while(string!=\0)UART0PTR=string;string++;intmyinit()display("HelloOpenWorld\n");Letsrunthroughthiscodesnippet.First,wedeclaredavolatilevariablepointer,andassignedtheaddressoftheserialport(UART0).Thefunctionmyinit(),isthemainroutine.Itmerelycallsthefunctiondisplay(),whichwritesastringtotheUART0.Engineersfamiliarwithbaselevelmicrocontrollerprogrammingwillfindthisveryeasy.Ifyouarenotexperiencedinembeddedsystemsprogramming,thenyoucansticktothebasicsofdigitalelectronics.Themicroprocessorisanintegratedchip,withinput/outputlines,differentports,etc.TheARM926EJShasfourserialports(informationobtainedfromitsdatasheet);andtheyhavetheirdatalines(theaddress).Whentheprocessorisprogrammedtowritedatatooneoftheserialports,itwritesdatatotheselines.Thatswhatthisprogramdoes.Thenextstepistodevelopthestartupcodefortheprocessor.Whenaprocessorispoweredon,itjumpstoaspecifiedlocation,readscodefromthatlocation,andexecutesit.Eveninthecaseofareset(likeonadesktopmachine),theprocessorjumpstoapredefinedlocation.Heresthestartupcode,startup.s:.globalStartStart:LDRsp,=sptopBLmyinitB.Inthefirstline,Startisdeclaredasglobal.ThenextlineisthebeginningofStartscode.Wesettheaddressofthestacktosptop.(TheinstructionLDRwillmovethedatavalueofsptopinthestackpointer(sp).TheinstructionBLwillinstructtheprocessortojumptomyinit(previouslydefinedininit.c).ThentheprocessorwillstepintoaninfiniteloopwiththeinstructionB.,whichislikeawhile(1)orfor(;;)loop.Ifwedontdothis,oursystemwillcrash.Thebasicsofembeddedsystemsprogrammingisthatourcodeshouldrunintoaninfiniteloop.Now,thefinaltaskistowritealinkerscriptforthesetwofiles(linker.ld):ENTRY(Start)SECTIONS.=0x10000;startup:startup.o(.text).data:(.data).bss:(.bss).=.+0x500;sptop=.;ThefirstlinetellsthelinkerthattheentrypointisStart(definedinstartup.s).Asthisisabasicprogram,wecanignoretheInterruptssection.TheQEMUemulator,whenexecutedwiththekerneloption,startsexecutionfromtheaddress0x10000,sowemustplaceourcodeatthisaddress.ThatswhatwehavedoneinLine4.ThesectionSECTIONS,definesthedifferentsectionsofaprogram.Inthis,startup.oformsthetext(code)part.Thencomesthesubsequentdataandthebsspart.Thefinalstepistodefinetheaddressofthestackpointer.Thestackusuallygrowsdownward,soitsbettertogiveitasafeaddress.Wehaveaverysmallcodesnippet,andcanplacethestackat0x500aheadofthecurrentposition.Thevariablesptopwillstoretheaddressforthestack.Wearenowdonewiththecodingpart.Letscompileandlinkthesefiles.Assemblethestartup.sfilewith: arm-none-eabi-as  -mcpu=arm926ej-s startup.s -o startup.o

Compile init.c:

armnoneeabigcccmcpu=arm926ejsinit.coinit.oLinktheobjectfilesintoanELFfile: arm-none-eabi-ld -T linker.ld init.o startup.o -o output.elf

Finally, create a binary file from the ELF file:

armnoneeabiobjcopyObinaryoutput.elfoutput.binTheaboveinstructionsareeasytounderstand.AllthetoolsusedarepartoftheARMtoolchain.Checktheirhelp/manpagesfordetails.Afterallthesesteps,finallywewillrunourprogramontheQEMUemulator: qemu-system-arm -M versatilepb -nographic -kernel output.bin

The above command has been explained in previous articles (1, 2), so we won’t go into the details. The binary file is executed on QEMU and will write the message “Hello Open World” to UART0 of the ARM926EJ-S, which QEMU redirects as output in the terminal.
Acknowledgement

This article is inspired by the following blog post: “Hello world for bare metal ARM using QEMU“.



<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(284) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
posted @   张同光  阅读(70)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示