Using QEMU for Embedded Systems Development, Part 2

本文介绍如何为ARM Versatile Platform Baseboard编写一个简单的裸机程序,并在QEMU模拟器上运行,通过串口输出信息。文章详细讲解了从创建源文件到编译链接的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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:

$  qemu-system-arm --help | grep nographic
-nographic      disable graphical output and redirect serial I/Os to console. When using -nographic, press 'ctrl-a h' to get some help.

We can make good use of this feature; let’s write some data to the serial port, and it can be a good working example.

Before going further, we must make sure which processor the GNU EABI tool-chain supports, and which processor QEMU can emulate. There should be a similar processor supported by both the tool-chain and the emulator. Let’s check first in QEMU. In the earlier articles, we compiled the QEMU source code, so use that source code to get the list of the supported ARM processors:

$ 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
$ cat gcc.info | grep arm | head -n 20
     .
     .
`strongarm1110', `arm8', `arm810', `arm9', `arm9e', `arm920',
`arm920t', `arm922t', `arm946e-s', `arm966e-s', `arm968e-s',
`arm926ej-s', `arm940t', `arm9tdmi', `arm10tdmi', `arm1020t',
`arm1026ej-s', `arm10e', `arm1020e', `arm1022e', `arm1136j-s',

Great!! The ARM926EJ-S processor is supported by the GNU ARM tool-chain. Now, let’s write some data to the serial port of this processor. As we are not using any header file that describes the address of UART0, we must find it manually, from the file (your-path)/qemu/qemu-0.14.0/hw/versatilepb.c:

/* 0x101f0000 Smart card 0.  */
/*  0x101f1000 UART0.  */
/*  0x101f2000 UART1.  */
/*  0x101f3000 UART2.  */

Open source code is so powerful, it gives you each and every detail. UART0 is present at address 0x101f1000. For testing purposes, we can write data directly to this address, and check output on the terminal.

Our first test program is a bare-metal program running directly on the processor, without the help of a bootloader. We have to create three important files. First of all, let us develop a small application program (init.c):

volatile unsigned char * const UART0_PTR = (unsigned char *)0x0101f1000;
void display( const char *string){
     while (*string != '\0' ){
         *UART0_PTR = *string;
         string++;
     }
}
 
int my_init(){
     display( "Hello Open World\n" );
}

Let’s run through this code snippet.

First, we declared a volatile variable pointer, and assigned the address of the serial port (UART0). The function my_init(), is the main routine. It merely calls the function display(), which writes a string to the UART0.

Engineers familiar with base-level micro-controller programming will find this very easy. If you are not experienced in embedded systems programming, then you can stick to the basics of digital electronics. The microprocessor is an integrated chip, with input/output lines, different ports, etc. The ARM926EJ-S has four serial ports (information obtained from its data-sheet); and they have their data lines (the address). When the processor is programmed to write data to one of the serial ports, it writes data to these lines. That’s what this program does.

The next step is to develop the startup code for the processor. When a processor is powered on, it jumps to a specified location, reads code from that location, and executes it. Even in the case of a reset (like on a desktop machine), the processor jumps to a predefined location. Here’s the startup code, startup.s:

.global _Start
_Start:
LDR sp, = sp_top
BL my_init
B .

In the first line, _Start is declared as global. The next line is the beginning of _Start‘s code. We set the address of the stack to sp_top. (The instruction LDR will move the data value of sp_top in the stack pointer (sp). The instruction BL will instruct the processor to jump to my_init (previously defined in init.c). Then the processor will step into an infinite loop with the instruction B ., which is like a while(1) or for(;;) loop. If we don’t do this, our system will crash. The basics of embedded systems programming is that our code should run into an infinite loop.

Now, the final task is to write a linker script for these two files (linker.ld):

ENTRY(_Start)
SECTIONS
{
. = 0x10000;
startup : { startup.o(.text)}
.data : {*(.data)}
.bss : {*(.bss)}
. = . + 0x500;
sp_top = .;
}

The first line tells the linker that the entry point is _Start (defined in startup.s). As this is a basic program, we can ignore the Interrupts section. The QEMU emulator, when executed with the -kernel option, starts execution from the address 0x10000, so we must place our code at this address. That’s what we have done in Line 4. The section “SECTIONS”, defines the different sections of a program.

In this, startup.o forms the text (code) part. Then comes the subsequent data and the bss part. The final step is to define the address of the stack pointer. The stack usually grows downward, so it’s better to give it a safe address. We have a very small code snippet, and can place the stack at 0x500 ahead of the current position. The variable sp_top will store the address for the stack.

We are now done with the coding part. Let’s compile and link these files. Assemble the startup.s file with:

$ arm-none-eabi-as  -mcpu=arm926ej-s startup.s -o startup.o

Compile init.c:

$ arm-none-eabi-gcc -c -mcpu=arm926ej-s init.c -o init.o

Link the object files into an ELF file:

$ arm-none-eabi-ld -T linker.ld init.o startup.o -o output.elf

Finally, create a binary file from the ELF file:

$ arm-none-eabi-objcopy -O binary output.elf output.bin

The above instructions are easy to understand. All the tools used are part of the ARM tool-chain. Check their help/man pages for details.

After all these steps, finally we will run our program on the QEMU emulator:

$ 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.

内容概要:本文探讨了在MATLAB/SimuLink环境中进行三相STATCOM(静态同步补偿器)无功补偿的技术方法及其仿真过程。首先介绍了STATCOM作为无功功率补偿装置的工作原理,即通过调节交流电压的幅值和相位来实现对无功功率的有效管理。接着详细描述了在MATLAB/SimuLink平台下构建三相STATCOM仿真模型的具体步骤,包括创建新模型、添加电源和负载、搭建主电路、加入控制模块以及完成整个电路的连接。然后阐述了如何通过对STATCOM输出电压和电流的精确调控达到无功补偿的目的,并展示了具体的仿真结果分析方法,如读取仿真数据、提取关键参数、绘制无功功率变化曲线等。最后指出,这种技术可以显著提升电力系统的稳定性与电能质量,展望了STATCOM在未来的发展潜力。 适合人群:电气工程专业学生、从事电力系统相关工作的技术人员、希望深入了解无功补偿技术的研究人员。 使用场景及目标:适用于想要掌握MATLAB/SimuLink软件操作技能的人群,特别是那些专注于电力电子领域的从业者;旨在帮助他们学会建立复杂的电力系统仿真模型,以便更好地理解STATCOM的工作机制,进而优化实际项目中的无功补偿方案。 其他说明:文中提供的实例代码可以帮助读者直观地了解如何从零开始构建一个完整的三相STATCOM仿真环境,并通过图形化的方式展示无功补偿的效果,便于进一步的学习与研究。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值