我一直在搞乱剑桥烘焙 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 是否在传输上循环。对我的环境还不是很熟练,所以这可能需要我一段时间!