0

我一直在搞乱剑桥烘焙 pi 教程(带有树莓派小演示的基本操作系统开发)。只有我一直在用 C 编写代码。我已经设置了开发环境,并且可以在关闭但未打开 GCC 优化的情况下成功运行我的代码。

使用内存映射 IO 的代码会出现问题(如果我编译除以下文件之外的所有其他内容并进行优化,它就可以工作)。我最初认为我的指针没有被声明为易失性,因此编译器正在优化对内存的实际写入并改用寄存器。但即使我将它们声明为易失性,问题仍然存在。

这是我写内存的方法:

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

指针是 volatile 类型,所以我不明白为什么 GCC 会决定不进行实际写入。还有什么我需要注意的吗?我误解了volatile的使用吗?

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

#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) 为树莓派支持而修改,所以我将尝试检查 r3 和 r2 寄存器中加载了哪些值,以查看它们是否是存储发生之前的正确指针,然后我'我将使用断点检查 putc 是否在传输上循环。对我的环境还不是很熟练,所以这可能需要我一段时间!

4

2 回答 2

1

将优化器与与硬件对话的代码一起使用可能会导致奇怪的行为。

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

于 2013-06-24T17:59:26.430 回答
-1

我正在尝试完成同样的事情,但我没有定义,而是像这样分配:

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

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

Register = 0xFFFFFFFF;
于 2015-02-11T02:04:31.523 回答