QEMU is able to emulate ARM systems with multiple serial ports, for example the Versatile PB. It has the ability to direct them to many types of host resources such as standard I/O, a pseudo-terminal or a telnet port. I started from my hello world example for bare-metal ARM programs to control three different serial ports. In the Versatile PB manual there’s a section, called Memory Map, that includes the absolute addresses of the mapped peripherals. For example, the UART 0, 1 and 2 interfaces are placed at addresses 0x101F1000
, 0x101F2000
and 0x101F3000
respectively. Inside the manual, the programmer’s model for the UART peripherals indicates the ARM PrimeCell UART (PL011) Technical Reference Manual as reference. In the PL011 manual we can find a detailed description of the UART memory mapped registers. From that decription I implemented a C struct
that renders easy to use the serial ports. The complete program is the following:
#include <stdint.h> typedef volatile struct { uint32_t DR; uint32_t RSR_ECR; uint8_t reserved1[0x10]; const uint32_t FR; uint8_t reserved2[0x4]; uint32_t LPR; uint32_t IBRD; uint32_t FBRD; uint32_t LCR_H; uint32_t CR; uint32_t IFLS; uint32_t IMSC; const uint32_t RIS; const uint32_t MIS; uint32_t ICR; uint32_t DMACR; } pl011_T; enum { RXFE = 0x10, TXFF = 0x20, }; pl011_T * const UART0 = (pl011_T *)0x101f1000; pl011_T * const UART1 = (pl011_T *)0x101f2000; pl011_T * const UART2 = (pl011_T *)0x101f3000; static inline char upperchar(char c) { if((c >= 'a') && (c <= 'z')) { return c - 'a' + 'A'; } else { return c; } } static void uart_echo(pl011_T *uart) { if ((uart->FR & RXFE) == 0) { while(uart->FR & TXFF); uart->DR = upperchar(uart->DR); } } void c_entry() { for(;;) { uart_echo(UART0); uart_echo(UART1); uart_echo(UART2); } }
The pl011_T
structure implements the memory map of the PL011 serial port. I used the standard sized types that are defined inside “stdint.h
” because the important part of the definition is to put the register at the right offset from the start of the struct
. The purpose of the reserved byte arrays is to put the subsequent register at the right offset as specified in the PL011 manual. The definition is marked as volatile
because I don’t want that the compiler assumes something about the values of the registers, because they can be changed by the hardware. Some registers are marked const
because they are read-only, but the volatile
specifier indicates that they can actually change outside the execution of the program. At last, the three pointers UART0, UART1 and UART2 are pointing to the address where the peripheral is mapped in the Versatile platform. The code itself does nothing more than echoing the received character, and if the character is a letter it converts it to uppercase. The echo is done polling the RXFE
flag of the Flag Register (FR
) until the receive FIFO is not empty. Then, we wait until the transmit FIFO is not full (TXFF
flag) and use the Data Register to read and write the byte.
To try the program I used the “startup.s
” and “test.ld
” files that I prepared in my previous post. I’m assuming the main C code is a file called “test.c
“. Then I compiled the program using the CodeSourcery bare metal ARM toolchain, and I emulated it with qemu-system-arm
emulator:
$ arm-none-eabi-gcc -c -mcpu=arm926ej-s test.c -o test.o $ arm-none-eabi-as -mcpu=arm926ej-s startup.s -o startup.o $ arm-none-eabi-ld -T test.ld test.o startup.o -o test.elf $ arm-none-eabi-objcopy -O binary test.elf test.bin $ qemu-system-arm -M versatilepb -m 128M -kernel test.bin -serial stdio -serial telnet:localhost:1235,server -serial telnet:localhost:1236,server QEMU waiting for connection on: telnet:127.0.0.1:1235,server QEMU waiting for connection on: telnet:127.0.0.1:1236,server
The QEMU serial options open two telnet ports, and the execution stops until there’s a telnet client connected to each of the ports. So, in another terminal, the commands:
$ telnet 127.0.0.1 1235
and:
$ telnet 127.0.0.1 1236
open the connections and the program starts. All three UARTs are active and respond to characters sent by the host.
Thomas
2012/09/27
Should’nt you do all the stuff to enable the UART ? or do you rely on QEMU for doing it ?
Balau
2012/09/27
You are right, in the real hardware it should not have worked because I didn’t set the UARTEN bit of the UARTCR register. It seems QEMU ignores that bit, though…
I don’t know what you mean by “all the stuff”, if I do not touch any other configuration (baud rate, parity, etc.) it means the default values are used, so it’s not necessary to do it.
minghuasweblog
2013/05/27
Reblogged this on minghuasweblog and commented:
Really great and very detailed series of artistic work on ARM and emulation.
Franc
2013/06/11
Hi, In QEMU 1.4.0 do I have to add the drivers of PL011 UART to my beagleboard-xm image or does the a normal 3.2.8 kernel config for beagle should be good enough?
Balau
2013/06/12
The beagleboard xm does not have PL011 UART, it has a UART/IrDA/CIR module. The image you have should be good enough.
Franc
2013/06/12
Thank you for your answer. The thing is that I’ trying to communicata across serial port with the host machine using the -serial pts and and when doing echo “test” > ttyO1 I get no output on the host pty created. I did some troubleshooting that I posted here :
http://unix.stackexchange.com/questions/78511/ttyo-ports-do-not-have-the-good-port-address-on-qemu-1-4-0-running-image-for-bea.
Then is where I thought it was a kernel config issue since in QEMU’s web site they say PL011 UARTS where amulated for cortex-A8 cpu’s.
Could you take a look at my troubleshooting and tellme what you think maybe the issue?
Thnks again.
PS: I writed also about the same matter in an other of your feed, thinking it was more appropriated.
Balau
2013/06/13
I saw your Stack Exchange post. You should try replacing all your “
-serial stdio
” and “-serial pty
” with “-serial telnet:localhost:XXXX
” where XXXX are different TCP ports, and then connect from host with “telnet localhost XXXX
“.I don’t think you need to change the kernel configuration or guest setserial.
Franc
2013/06/14
Once again thank you for you time. I’m not realy familiar with the telnet protocols but I will try this for sure. Meanwhile could you tell me where I can download an Image, a config file or a defconfig for beagleboard that the serial communication across multiples tty had worked? I did some programs that absolutly need to open a tty on both guets and host to communicate.
Balau
2013/06/14
I never tried to emulate beagleboard, but I think the best bet is taking the mainline kernel, configuring with omap2plus_defconfig (that seems to generate a .config with CONFIG_MACH_OMAP3_BEAGLE=y) and compiling.
Michael Rupp
2015/05/28
I’m using Windows 7 and have had success with other tutorials here using qemu. I’m having issues with the client telnet windows saying that they are connected…
netstat shows that the connections are established but the clients just freeze and I can’t complete the demo by typing and having the UART echo out to the console. Any ideas that I may have overlooked or which direction to go from here?
Thanks
Michael Rupp
2015/05/29
Ok, I think it is working. The Windows telnet client will continue if you hit the enter key. I was then able to type and when I did every character was capitalized in the telnet console. There was no echo out at the qemu server. I suppose that was normal? I’m still learning how to use QEMU.
Balau
2015/05/30
It seems normal to me.
QEMU emulates many serial ports (UART peripherals), as many as the Versatile PB offers. If I understand correctly you are using nographic option so the first UART (UART0) is connected to the terminal you open with Ctrl-Alt-3. Then the next telnet option tells QEMU to connect the second UART (UART1) to a telnet server. Then the last telnet option tells QEMU to connect the third UART (UART2) to another telnet server.
If you want to echo something in QEMU you have to code the part that when a byte is read from UART2 it writes it into UART0.
I don’t know about the behaviour of Windows telnet, I suppose that’s the way it works with that Enter key at the beginning.
younes
2017/07/25
Hi, I am using your code in gem5,I change memory addresses of UARTs and correct offsets with pl011 class 0f gem5. it is working but not well which part should modify.
output is not correct
thank you in advance