内存映射IO和GCC优化

时间:2013-06-24 17:22:46

标签: c io raspberry-pi memory-mapping

我一直在搞乱剑桥烘焙教程(基本操作系统开发,只有少量演示用于覆盆子pi)。只有我一直在用C编写代码。我已经开发了我的开发环境,并且能够在GCC优化关闭但未开启的情况下成功运行我的代码。

我的代码使用内存映射IO会出现问题(如果我编译除了以下文件以外的所有其他内容,并进行优化,它可以正常工作)。我最初认为我的指针没有被声明为volatile,所以编译器正在优化对内存的实际写入并使用寄存器代替。但即使我宣称它们是不稳定的,问题仍然存在。

以下是我对内存的写法:

#define UART0_CR ((volatile uint32_t *) (UART0_BASE + 0x30))
...
*UART_CR = 0;

指针属于易失性类型,因此我无法理解为什么GCC决定不进行实际写入。在这里我需要注意什么?我误解了挥发性物质的使用吗?

完整的工作文件(无论如何都要关闭优化):

#include <stdint.h>
#include <uart.h>

#define GPIO_BASE   0x20200000
#define GPPUD       ((volatile uint32_t *) (GPIO_BASE + 0x94))
#define GPPUDCLK0   ((volatile uint32_t *) (GPIO_BASE + 0x98))

#define UART0_BASE      0x20201000
#define UART0_DR        ((volatile uint32_t *) (UART0_BASE + 0x00))
#define UART0_FR        ((volatile uint32_t *) (UART0_BASE + 0x18))
#define UART0_IBRD      ((volatile uint32_t *) (UART0_BASE + 0x24))
#define UART0_FBRD      ((volatile uint32_t *) (UART0_BASE + 0x28))
#define UART0_LCRH      ((volatile uint32_t *) (UART0_BASE + 0x2C))
#define UART0_CR        ((volatile uint32_t *) (UART0_BASE + 0x30))
#define UART0_IMSC      ((volatile uint32_t *) (UART0_BASE + 0x38))
#define UART0_ICR       ((volatile uint32_t *) (UART0_BASE + 0x44))

static void delay(int32_t count) {
    asm volatile("__delay%=: subs %[count], %[count], #1; bne __delay%=\n"
                :
                : [count]"r"(count) 
                : "cc"
    );
}

void uart_init() {    
    *UART0_CR = 0; // Disable UART0.    
    *GPPUD = 0;     // Disable pull up/down for all GPIO pins & delay for 150 cycles.
    delay(150);   
    *GPPUDCLK0 = (1 << 14) | (1 << 15); // Disable pull up/down for pin 14,15 & delay for 150 cycles.
    delay(150);   
    *GPPUDCLK0 = 0; // Write 0 to GPPUDCLK0 to make it take effect.    
    *UART0_ICR = 0x7FF; // Clear pending interrupts.
    *UART0_IBRD = 1; //Set rate
    *UART0_FBRD = 40;     
    *UART0_LCRH = (1 << 4) | (1 << 5) | (1 << 6); // Enable FIFO & 8 bit data transmissio (1 stop bit, no parity).    
    *UART0_IMSC = (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10); // Mask all interrupts.   
    *UART0_CR = (1 << 0) | (1 << 8) | (1 << 9);  // Enable UART0, receive & transfer part of UART.  
}

void uart_putc(uint8_t byte) {    
    while (1) { // wait for UART to become ready to transmit    
        if (!(*UART0_FR & (1 << 5))) break;
    }   
    *UART0_DR = byte; // Transmit
}

void uart_puts(const char *str) {
    while (*str) {
        uart_putc(*str++);
    }
}

编辑:

查看如何查看程序集,非常有用,谢谢。如果我从uart i init中取出前2个写入(直到第一个延迟调用),我得到:

未优化:

uart_init:
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 1, uses_anonymous_args = 0
    stmfd   sp!, {fp, lr}
    add fp, sp, #4
    ldr r3, .L3
    mov r2, #0
    str r2, [r3, #0]
    ldr r3, .L3+4
    mov r2, #0
    str r2, [r3, #0]

优化

uart_init:
    @ args = 0, pretend = 0, frame = 0
    @ frame_needed = 0, uses_anonymous_args = 0
    @ link register save eliminated.
    ldr r3, .L2
    ldr r2, .L2+4
    mov r1, #0
    str r1, [r3, #48]
    str r1, [r2, #148]

唯一的区别似乎是对于未经优化的,它不会为.L2和.L2 + 4标签添加偏移,优化的标签就是这样。除非这些标签上的指针已准备好已经计算出它们的偏移量。

我有一个分叉的qemu(qemu-rpi)modded for raspberry pi支持,所以我将尝试检查r3和r2寄存器中加载了什么值,以查看它们是否是存储发生之前的正确指针然后我要检查putc是否使用断点在传输上循环。我的环境不是很熟练,所以这可能需要一段时间!

2 个答案:

答案 0 :(得分:1)

使用优化程序以及与硬件对话的代码可能会导致奇怪的行为。

尝试在与硬件通信的功能前使用__attribute__((noinline))。我还建议将与硬件对话的所有代码放入自己的文件中并关闭优化。优化器可以重新排序或内联这些项目。

答案 1 :(得分:0)

我试图完成同样的事情 但不是定义,我只是这样分配:

volatile const int *Register = (volatile const int *)0xFFFEF000;

然后您可以通过名称直接将值发送到寄存器:

Register = 0xFFFFFFFF;