我一直在搞乱剑桥烘焙教程(基本操作系统开发,只有少量演示用于覆盆子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是否使用断点在传输上循环。我的环境不是很熟练,所以这可能需要一段时间!
答案 0 :(得分:1)
使用优化程序以及与硬件对话的代码可能会导致奇怪的行为。
尝试在与硬件通信的功能前使用__attribute__((noinline))
。我还建议将与硬件对话的所有代码放入自己的文件中并关闭优化。优化器可以重新排序或内联这些项目。
答案 1 :(得分:0)
我试图完成同样的事情 但不是定义,我只是这样分配:
volatile const int *Register = (volatile const int *)0xFFFEF000;
然后您可以通过名称直接将值发送到寄存器:
Register = 0xFFFFFFFF;