From this site: http://balau82.wordpress.com/2010/02/28/hello-world-for-bare-metal-arm-using-qemu/
I can use C code to print out a string in qemu simulator.
volatile unsigned int * const UART0DR = (unsigned int *)0x101f1000;
void print_uart0(const char *s) {
while(*s != '\0') { /* Loop until end of string */
*UART0DR = (unsigned int)(*s); /* Transmit char */
s++; /* Next char */
}
}
I need to do the same thing in C code with gumstix connex board in qemu (with -M connex option), which uses 0x40100000 or 0x40700000 for the memory mapped uart address, but nothing is shown in the screen.
I tried with some data checking code, but it doesn't still work.
volatile unsigned int * const UART0DR = (unsigned int *)0x40100000;
volatile unsigned int * const UART_LSR = (unsigned int *)0x40100014;
#define LSR_TDRQ (1 << 5) // Transmit Data Request
void print_uart0(const char *s) {
while(*s != '\0') { /* Loop until end of string */
while(( *UART_LSR & LSR_TDRQ ) == 0 );
*UART0DR = (unsigned int)(*s); /* Transmit char */
s++; /* Next char */
}
}
What might be wrong? Is PXA255 uses different way to use uart?
I searched the source code of pxa, and gumstix, maybe gumstix may use different methods to do hart communication in qemu.
From your linked page you may have noticed the following text:
The code that emulates the serial port inside QEMU (here in the source repository) implements a subset of the functionalities of the PL011 Prime Cell UART from ARM
and
The QEMU model of the PL011 serial port ignores the transmit FIFO capabilities; in a real system on chip the “Transmit FIFO Full” flag must be checked in the UARTFR register before writing on the UARTDR register.
You won't be able to use the same code on both QEMU and the PXA255 since the implementation of the UART is different.
To have the UART function correctly on the PXA255 board will require a lot more setup and would typically involve the following:
Configuration of the clock subsystem registers to ensure that the UART peripheral is receiving a clock from the main clock system on the CPU.
Configuration of the UART peripheral registers according to the desired use. You may need to configure registers which control baud rate register, parity control, number of data bits.
Modification of your code which writes to the UART. UART peripherals typically contain a FIFO (sometimes just a single byte) which is used during transmission and reception. To transmit a character you first have to ensure that the previous character has finished transmission before placing the next character for transmit in the output data register.
There is no substitute for reading the UART data sheet in detail and following all the information listed there.
qemu should be relaxed about the accurate emulation of the UART.
Below code works:
startup.s:
.global startup
startup:
ldr sp, startup_stack_top
bl c_startup
b .
startup_stack_top:
.word 0xa4000000
os.c:
void write_uart(char *str)
{
char c;
volatile char *uart = (char *)0x40100000;
while (1) {
c = *str;
if (!c)
break;
*uart = c;
str++;
}
}
void c_startup()
{
write_uart("hello\r\n");
}
linker script os.ld:
ENTRY(startup)
SECTIONS
{
. = 0x0;
.startup : { startup.o(.text) }
.text : { *(.text) }
.rodata : { *(.rodata) }
.data : { *(.data) }
.bss : { *(.bss COMMON) }
}
Build commands:
#pfx=arm-none-eabi-
$(pfx)as -g -march=armv5te startup.s -o startup.o
$(pfx)gcc -g -c -march=armv5te os.c -o os.o
$(pfx)ld -T os.ld os.o startup.o -o os.elf
$(pfx)objcopy -O binary os.elf os.bin
dd of=flash.img bs=128k count=128 if=/dev/zero
dd of=flash.img bs=128k conv=notrunc if=os.bin
Run command:
qemu-system-arm -M connex -m 128M -snapshot -pflash flash.img
Unless there's a bunch of other code you aren't showing, you're missing several important things here:
Clocks and power for the UART module must be enabled before it will function. See section 3 of the PXA255 manual.
The GPIOs used by the UART must be configured before the UART will work correctly (e.g, by setting appropriate pin directions and alternate functions). See section 4.1 of the PXA255 manual.
The UART must be configured (e.g, baud rate, etc.) before you start writing data to it. The PXA255 manual does not explicitly include information on these registers; you will need to cross-reference the 16550 datasheet.
While writing data to the UART, you must ensure the UART is in an appropriate state to receive data (e.g, that the transmit buffer is not full), and wait for it to enter an appropriate state if it is not. Refer to the 16550 datasheet, or to a general tutorial on use of this UART.
The UART implementation in QEMU is intended as a debugging tool, not as a full emulation of the UART in a real device. Just because something works in QEMU doesn't mean it will work on real hardware!
As Austin Phillips pointed out, it requires a lot of setup code to make it work with UART serial communication. What confused me before was that I could make the UART communication work without any setup with qemu-system-arm -M versatilepb -m 128M -nographic -kernel test.bin, but this is versatilepb board emulation. My guess is that for versatilepb board, UART setup is just finished before my binary runs.
Mimicking that, what I did was to use a uboot, which does all of the UART setup, and load my binary image from the uboat, and everything works fine.
Related
I'm trying to develop a Linux device driver to manage the GPIO registers of a NanoPI Neo card (Allwinner H3).
I'm using a simple approach to understand the GPIO registers behaviour by means the use of only two driver functions: open and ioctl.
My driver implementation, at now, is able to manage a lot of registers such as RTC and CPU-PORT and to read write some other registers.
But I'm having issues in using/managing TWI registers (I2C).
The blocking me issue is that whathever register I read or read after writing any value to the register itself always returns 0x00000000 and nothing seems to happen at pin HW level (PA11/PA12 see below)
I read on the CPU datasheet (see: paragraph 8.1 of the document) the registers value and base address to manage TWI0, base address should be 0x01C2AC00.
I can't find any AllWinner H3 GPIO programmer's reference and I'm not sure if specific operations are required to activate the TWI register functionality. The only operations I have done are to set registers PA11 and PA12 to be the I/O of TWI0_SCK and TWI0_SDA.
Questions:
Do you have any news of an "AllWinner H3 GPIO Programmer's Reference"?
Do you know which GPIO register I have to set / modify to get TWI enabled or at least give me "signs of life"?
A very strong simplification, aimed at using only TWI0 registers, of the mapping and ioctl functions of the driver I wrote might be the following code, but my code is much more sophisticated and I know it works with many other registers.
#define TWI_IOBASE(n) (0x01C2AC00 + 0x400*((n)&3))
#define TWI_PAGESIZE 0x400
#define IO_ADDRESS_MSK 0x0000FFFFUL
#define IO_CMD_MSK 0xF0000000UL
#define IO_CMD_READ 0x10000000UL
#define IO_CMD_WRITE 0x20000000UL
static unsigned char * vmaddr;
inline static int drv_init_twi_vm(void)
{
vmaddr=ioremap(TWI_IOBASE(0), TWI_PAGESIZE);
}
static long drv_ioctl_twi_rw(struct file *file, unsigned int cmd, unsigned long * arg)
{
long retval = 1;
unsigned c;
uint32_t r;
void *x;
r=cmd & IO_ADDRESS_MSK;
x=vmaddr+r;
c=cmd & IO_CMD_MSK;
switch(c)
{
case IO_CMD_READ:
if ( copy_to_user((void *)arg, x, 4) ) {
retval = -EFAULT;
}
break;
case IO_CMD_WRITE:
*(unsigned long *)x=*arg;
break;
default:
retval=-EFAULT;
break;
}
return retval;
}
I have been working on FTDI FT2232H chip to interface with I2C devices. I have started to learn AN_177 application note pdf. I have no EEPROM to expreience,no oscilloscope to see waveforms. My goal is to comprehend the code itself and taking notes for my future projects. Here is the code snippet that i dont completely understand:
FT_STATUS write_byte(uint8 slaveAddress, uint8 registerAddress, uint8 data)
{
uint32 bytesToTransfer = 0;
uint32 bytesTransfered;
bool writeComplete=0;
uint32 retry=0;
bytesToTransfer=0;
bytesTransfered=0;
buffer[bytesToTransfer++]=registerAddress; /* Byte addressed inside EEPROM */
buffer[bytesToTransfer++]=data;
status = I2C_DeviceWrite(ftHandle, slaveAddress, bytesToTransfer, buffer, &bytesTransfered, I2C_TRANSFER_OPTIONS_START_BIT|I2C_TRANSFER_OPTIONS_STOP_BIT);
/* poll to check completition */
while((writeComplete==0)&& (retry<I2C_WRITE_COMPLETION_RETRY))
{
bytesToTransfer=0;
bytesTransfered=0;
buffer[bytesToTransfer++]=registerAddress; /* Addressed inside EEPROM */
status = I2C_DeviceWrite(ftHandle, slaveAddress, bytesToTransfer,buffer, &bytesTransfered, I2C_TRANSFER_OPTIONS_START_BIT|I2C_TRANSFER_OPTIONS_BREAK_ON_NACK);
if((FT_OK == status) && (bytesToTransfer == bytesTransfered))
{
writeComplete=1;
printf(" ... Write done\n");
}
retry++;
}
return status;
}
You can see and understand that the while loop is the polling part.
I checked the datasheet of 24LC024H I2C EEPROM and they mentioned about Acknowladge Polling (Page 10) is a feature of this kind of EEPROMs'. Acknowladge Polling basicly checks when the device is ready to send data. There is a flow diagram too... you could take a look.
Flowchart
Here comes the what i want to point out:
buffer[bytesToTransfer++]=registerAddress; /* Addressed inside EEPROM */
In the 24LC024H datasheet related to Acknowladge Polling they say polling part consist of START + Control Byte (or Slave address) + R/W bit, not include address inside EEPROM (Word address in I2C protocol) . So why FTDI guys included this line of code? Am I missing something?
Best regards...
I'm trying to write an operating system using OSDev and others. Now, I'm stuck making a keyboard interrupt handler. When I compile my OS and run the kernel using qemu-system-i386 -kernel kernel/myos.kernel everything works perfectly. When I put everything into an ISO image and try to run it using qemu-system-i386 -cdrom myos.iso, it restarts when I press a key. I think it's caused by some problems in my interrupt handler or a bad IDT entry.
My keyboard handler (AT&T syntax):
.globl keyboard_handler
.align 4
keyboard_handler:
pushal
cld
call keyboard_handler_main
popal
iret
My main handler in C:
void keyboard_handler_main(void) {
unsigned char status;
char keycode;
/* write EOI */
write_port(0x20, 0x20);
status = read_port(KEYBOARD_STATUS_PORT);
/* Lowest bit of status will be set if buffer is not empty */
if (status & 0x01) {
keycode = read_port(KEYBOARD_DATA_PORT);
if(keycode < 0)
return;
if(keycode == ENTER_KEY_CODE) {
printf("\n");
return;
}
printf("%c", keyboard_map[(unsigned char) keycode]);
}
}
C function I use to load the:
void idt_init(void)
{
//unsigned long keyboard_address;
unsigned long idt_address;
unsigned long idt_ptr[2];
auto keyboard_address = (*keyboard_handler);
IDT[0x21].offset_lowerbits = keyboard_address & 0xffff;
IDT[0x21].selector = KERNEL_CODE_SEGMENT_OFFSET;
IDT[0x21].zero = 0;
IDT[0x21].type_attr = INTERRUPT_GATE;
IDT[0x21].offset_higherbits = (keyboard_address & 0xffff0000) >> 16;
/* Ports
* PIC1 PIC2
*Command 0x20 0xA0
*Data 0x21 0xA1
*/
write_port(0x20 , 0x11);
write_port(0xA0 , 0x11);
write_port(0x21 , 0x20);
write_port(0xA1 , 0x28);
write_port(0x21 , 0x00);
write_port(0xA1 , 0x00);
write_port(0x21 , 0x01);
write_port(0xA1 , 0x01);
write_port(0x21 , 0xff);
write_port(0xA1 , 0xff);
idt_address = (unsigned long)IDT ;
idt_ptr[0] = (sizeof (struct IDT_entry) * IDT_SIZE) + ((idt_address & 0xffff) << 16);
idt_ptr[1] = idt_address >> 16 ;
load_idt(idt_ptr);
printf("%s\n", "loadd");
}
Files are organized the same way as OSDev's Meaty Skeleton. I do have a different bootloader.
Based on experience I believed that this issue was related to a GDT not being set up. Often when someone says interrupts work with QEMU's -kernel option but not a real version of GRUB it is often related to the kernel developer not creating and loading their own GDT. The Mulitboot Specification says:
‘GDTR’
Even though the segment registers are set up as described above, the ‘GDTR’ may be invalid, so the OS image must not load any segment registers (even just reloading the same values!) until it sets up its own ‘GDT’.
When using QEMU with the -kernel option the GDTR is usually valid but it isn't guaranteed to be that way. When using a real version of GRUB (installed to a hard drive, virtual image, ISO etc) to boot you may discover that the GDTR isn't in fact valid. The first time you attempt to reload any segment register with a selector (even if it is the same value) it may fault. When using interrupts the code segment (CS) will be modified which may cause a triple fault and reboot.
As well the Multiboot Specification doesn't say which selectors point to code or data descriptors. Since the layout of the GDT entries is not known or guaranteed by the Multiboot specification it poses a problem for filling in the IDT entries. Each IDT entry needs to specify a specific selector that points to a code segment.
The Meaty Skeleton tutorial on OSDev doesn't set up interrupts, nor do they modify any of the segment registers so that code likely works with QEMU's -kernel option and a real version of GRUB. Adding IDT and interrupt code on top of the base tutorial probably lead to the failure you are seeing when booting with GRUB. That tutorial should probably make it clearer that you should set up your own GDT and not rely on the one set up by the Multiboot loader.
I want to program a little "hello world" bare metal application on the Intel Galileo board. Using UEFI to print out text (to UART-1) works well, of course, but I want to access the UART "manually", without any help from UEFI.
In QEMU my code works well:
.h file
#define COM1_PORT (0x03F8)
#define UART_PORT (COM1_PORT)
enum uart_port_offs_t
{ // DLAB RW
THR = 0, // 0 W Transmitter Holding Buffer
RBR = 0, // 0 R Receiver Buffer
DLL = 0, // 1 RW Divisor Latch Low Byte
IER = 1, // 0 RW Interrupt Enable Register
DLH = 1, // 1 RW Divisor Latch High Byte
IIR = 2, // - R Interrupt Identification Register
FCR = 2, // - RW FIFO Control Register
LCR = 3, // - RW Line Control Register
MCR = 4, // - RW Modem Control Register
LSR = 5, // - R Line Status Register
MSR = 6, // - R Modem Status Register
SR = 7, // - RW Scratch Register
};
.c file
void uart_init(void)
{
outb(UART_PORT + IER, 0x00); // Disable all interrupts
outb(UART_PORT + LCR, LCR_DLAB);
outb(UART_PORT + DLL, BAUD_LL); // Set divisor (lo byte)
outb(UART_PORT + DLH, BAUD_HL); // (hi byte)
outb(UART_PORT + LCR, LCR_WORD_BITS_8 | LCR_PAR_NONE | LCR_STOP_BITS_1);
outb(UART_PORT + FCR, FCR_ENABLE | FCR_CLR_RECV | FCR_CLR_SEND | FCR_TRIGGER_16);
outb(UART_PORT + MCR, MCR_DSR | MCR_RTS | MCR_AUX2);
}
ssize_t uart_write(const char *buf, size_t len)
{
size_t written = 0;
while (written < len) {
while (!is_output_empty()) {
asm volatile ("pause");
}
outb(UART_PORT + THR, buf[written]);
++written;
}
return written;
}
main
SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Exiting EFI boot services ...\r\n");
SystemTable->BootServices->ExitBootServices(ImageHandle, map_key);
uart_init();
while (1) {
const char s[] = "UART\r\n";
uart_write(s, sizeof (s) - 1);
}
The specs did not help me very much. I guess that the UARTs on the Intel Galileo board don't use/emulate the normal/legacy COM ports 3F8h, 2F8h, 3E8h, or 2E8h.
Can anyone tell me what I am doing wrong, or even post a minimal bare metal hello world example?
I assume you are aiming at the serial port that is the "audio-like" connector on the Intel Galileo board.
Here are a few resources that should help:
Galileo schematic
Intel Quark SoC X1000 datasheet
Intel Galileo IO Mapping
Sergey's blog entry about Configuring the Serial Port for Galileo
Intel Quark Board Support Package downloads, including
Board Support Package Sources (currently ver.1.0.0)
Intel Quark SoC X1000 UEFI Firmware Writer’s Guide
Things to note about this UART:
This serial port comes out of the QUARK chip as UART1 (see the schematics).
There are a few GPIOs that you may need to manipulate (see Sergey's blog for doing this in Linux):
gpio4: This GPIO controls level shifter for UART signals and some other signals connected to Quark SoC, such as SPI and fast I/O. Writing '1' to this GPIO enables level shifter.
gpio40: This GPIO controls multiplexer for pin 0. Writing '0' to this GPIO connects pin 0 to UART's RxD (receive data) signal.
gpio41: This GPIO controls multiplexer for pin 1. Writing '0' to this GPIO connects pin 1 to UART's TxD (transmit data) signal.
Check the chapter 18 (High Speed UART) in the Quark datasheet for what to put in the UART registers:
Registers DLH, DLL specify the baud rate
Decide whether you want the DMA mode (chapter 18.3.1), the FIFO-interrupt mode (chapter 18.3.2), or the FIFO-polling mode (chapter 18.3.3). The latter is simpler but less effective, IMHO. The former requires you to configure DMA properly as well.
Since there is quite a bit to read for chapter 18 (~67 pages of useful information), I'm not going to retype all that here, please read the datasheet and configure the registers accordingly.
General notes:
For bare-metal approach first make sure that your boot procedure is correct, configuring all the clocking options, GPIO default modes and values, timers if any, etc. For Boot checklist read chapter 4.12 in X1000 UEFI Firmware Writer’s Guide (~18 things to do to boot this chip). After that I'd verify it with a simple "LED blinking" application on a GPIO.
Tinkering with 3F8h and similar ports is not going to help on "bare metal" of this SoC. You need to deal with the registers directly, or find and use appropriate library or framework (maybe UEFI BIOS?).
Programming sources for the particular platform should be a good read for examples.
For example, in Board Support Package Sources for Intel Quark the archive Quark_EDKII_v1.0.0.tar.gz is the UEFI source code for Quark/Galileo. It there, the Serial.c and Serial.h files might just be what you are looking for:
Quark_EDKII_v1.0.0/QuarkSocPkg/QuarkSouthCluster/Uart/Dxe/Serial.*
I have a problem in connecting ATmega128 serial port to computer via USB-Serial converter. The USB-Serial converter is verified as I have connected computer to CDMA modem using it. However when I try to connect it with atmega128 I can't figure out the problem. I have connected it to serial LCD (CLCD) and it works fine.Even in simulation with virtual terminal there is no problem. I would like to know if I have missed anything related to serial port. I have already checked baud rate in hardware options and in virtual terminal.
Here is the code.
#include<avr/io.h>
#include<util/delay.h>
char str1[]="AT\r\n";
char str2[]="AT+CMGF=1\r\n";
char str3[]="AT+CMGS=\"01068685673\"\r\n";
char str4[]="hello\x1A\r\n";
int i;
void TX_CHAR(char ch)
{
while(!(UCSR1A&0x20));
UDR1=ch;
}
int main()
{
UBRR1H=0; UBRR1L=103; UCSR1B=0x08;
UCSR1C=0b00000110;
while(1)
{
i=0; while(str1[i])TX_CHAR(str1[i++]);
_delay_ms(200);
i=0; while(str2[i])TX_CHAR(str2[i++]);
_delay_ms(200);
i=0; while(str3[i])TX_CHAR(str3[i++]);
_delay_ms(200);
i=0; while(str4[i])TX_CHAR(str4[i++]);
_delay_ms(3000);
}
}
Things to check:
hardware - wiring
value of M103C fuse (= compatibility mode)
XTAL frequency and prescalers, as the formula for BAUD depends on it: BAUD = Fosc/16(UBRR+1)
USART double speed flag (UCSRA)
frame format
UDREn flag set
You also may get better insight into your code if you use predefined symbolic values, e.g.
/* Enable receiver and transmitter */
UCSRB = (1<<RXEN)|(1<<TXEN);
see the examples in the data sheet (pg 176ff)
On the frame format I understand you are set to async, 8-bit (UCSR1B:2 = 0, UCSR1C:2,1 = 11), parity disabled, 1 stop bit
In void TX_CHAR(char ch) I understand you are checking the status of bit 7 (RXC1) by using mask 0x20H. On the other hand you dont have the RX enabled (RXEN1 meaning UCSR1B:4=0)
I wonder if you shouldn't better check bit 6 (TXC1). Again ... using the symbolic values would help to better understand the code.
Hope this helps ...