地址空间
由于计算机软件系统是一个裸机系统,因此没有虚拟内存系统,都是直接访存,也就是说 tlb 其实是相当于不工作的。
只要在start.S 中直接修改 csr 标记 CSR.CRMD.DA = 1 (直接地址模式)、CSR.CRMD.PG = 0 (关闭分页)。
地址段划分如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
#define RAM_BASE 0x00000000 #define RAM_END 0x1CFFFFFF
#define STACK_TOP 0x1EFFFFFF #define STACK_BOTT 0x1D000000
#define FRAMEBUF_BASE 0x1F000000 #define FRAMEBUF_SIZE 0x01000000 #define FRAMEBUF_END 0x1FFFFFFF
#define BOOTROM_BASE 0x20000000 #define BOOTROM_SIZE 0x00001000 #define BOOTROM_END 0x20000FFF
#define CONFREG_BASE 0x1FD00000
#define CR0_ADDR 0x1FD00000 #define CR0_ADDR 0x1FD00004 #define CR0_ADDR 0x1FD00008 #define CR0_ADDR 0x1FD0000c #define CR0_ADDR 0x1FD00010 #define CR0_ADDR 0x1FD00014 #define CR0_ADDR 0x1FD00018 #define CR7_ADDR 0x1FD0001C
#define TIMER_ADDR 0x1FD0E000
#define LED_ADDR 0x1FD0F000 #define BTN_STEP_ADDR 0x1FD0F028 #define FREQ_ADDR 0x1FD0F030
#define GPIO_DATA_ADDR 0x1FD0F040 #define GPIO_DIR_ADDR 0x1FD0F044 #define GPIO_IN_ADDR 0x1FD0F048
#define PS2_DATA_ADDR 0x1FD0F050 #define PS2_STATUS_ADDR 0x1FD0F054 #define PS2_CTRL_ADDR 0x1FD0F058
#define APB_BASE 0x1FE00000 #define UART_BASE 0x1FE00000
|
关于 axi 仲裁器的设计,所有的非 APB、CONFREG 的地址都会落到 SRAM 通道。因此主存储器、BOOTROM 都能收到icache、dcache 的请求。我只需要在 SRAM 设备设置一个选择器,当bootloader 执行的时候,将数据直接从 brom 模块给出,就可以了。
由于我可能需要开辟一些比较大的数组或者使用递归什么的,栈区设置了 32MB,这对于裸机系统来说已经相当大了。
链接脚本的设计
内存布局好后,就可以主要针对 ddr 的范围来划分内存区域了。另外按照 loongarch 标准,csr_eentry 的地址需要 64 字节对齐:
1 2 3 4 5 6 7 8
| always @(posedge clk) begin if (rst) begin csr_eentry[5:0] <= 6'b0; end else if (eentry_wen) begin csr_eentry[31:6] <= csr_wdata[31:6]; end end
|
因此,中断处理程序也需要链接到 64 字节对齐的地方。但是这样写并不会生效:
1 2 3 4 5 6 7 8 9
| .text.entry : { ... } > RAM /* 结束于 0x30 */
. = ALIGN(64); /* 期望:. = 0x40 */
.exception : { /* 实际:还是从 0x30 开始 */ ... } > RAM
|
以为当多个输出段都指定 > RAM 时,链接器会尝试将它们紧密排列,以减少内存空洞。. = ALIGN(64) 确实会更新位置计数器到 0x40,但链接器在分配段地址时,可能会忽略段之间的间隙,直接把下一个段放在上一个段结束的位置。而且经过测试,确实对不齐。
解决办法就是将 .text.entry 填充到 0x40 就好了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
| /* * linker.ld - LoongArch32 裸机链接脚本 * * 内存布局 (512MB DDR): * 0x00000000 - 0x1CFFFFFF : RAM (464MB) - 代码+数据+堆 * 0x1D000000 - 0x1EFFFFFF : Stack (32MB) * 0x1F000000 - 0x1FFFFFFF : Framebuffer (16MB) * 0x20000000 - 0x20000FFF : Boot ROM (4KB, BRAM) * * 地址空间分配: * .text.entry: 0x00000000 启动代码 (.text.start) * .exception : 0x00000040 异常/中断入口 (64字节对齐) * .text : 紧随其后 其余代码 * .rodata : 紧随 .text 只读数据 * .data : 紧随 .rodata 已初始化数据 * .bss : 紧随 .data 未初始化数据 * heap : _end 之后 堆 (向上增长至 0x1CFFFFFF) * stack : 0x1D000000 - 0x1EFFFFFF (向下增长, 32MB) */
OUTPUT_ARCH(loongarch) ENTRY(_start)
/* 内存定义 */ MEMORY { RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 464M }
/* 栈大小 */ _stack_size = 32M;
SECTIONS { /* ======== 启动代码 (必须在 0x00) ======== */ . = 0x00000000; .text.entry : { _text_start = .; KEEP(*(.text.start)) /* 启动代码在最前面 */ . = 0x40; /* 填充到 0x40,为 .exception 预留空间 */ } > RAM
/* ======== 异常/中断入口 (紧随 .text.entry,地址 0x40) ======== */ .exception ALIGN(64) : { __exception_start = .; KEEP(*(.exception)) *(.exception.*) __exception_end = .; } > RAM
/* ======== 代码段 ======== */ .text : { *(.text) *(.text.*) _text_end = .; } > RAM
/* ======== 只读数据段 ======== */ .rodata ALIGN(16) : { _rodata_start = .; *(.rodata) *(.rodata.*) *(.srodata) *(.srodata.*) _rodata_end = .; } > RAM
/* ======== 已初始化数据段 ======== */ .data ALIGN(16) : { _data_start = .; *(.data) *(.data.*) *(.sdata) *(.sdata.*) _data_end = .; } > RAM
/* ======== 未初始化数据段 ======== */ .bss ALIGN(16) : { _bss_start = .; *(.bss) *(.bss.*) *(.sbss) *(.sbss.*) *(COMMON) _bss_end = .; } > RAM
/* ======== 程序结束标记 ======== */ . = ALIGN(16); _end = .;
/* ======== 堆区域 ======== */ _heap_start = .; _heap_end = 0x1D000000; /* 堆结束于栈底 */ _heap_size = _heap_end - _heap_start;
/* ======== 栈区域 ======== */ /* 栈: 0x1D000000 - 0x1EFFFFFF (32MB, 向下增长) */ _stack_bottom = 0x1D000000; _stack_top = 0x1F000000;
/* ======== Framebuffer 区域 ======== */ _framebuf_base = 0x1F000000; _framebuf_end = 0x20000000;
/* 丢弃不需要的段 */ /DISCARD/ : { *(.comment) *(.note.*) *(.eh_frame) *(.eh_frame_hdr) } }
/* 导出符号供程序使用 */ PROVIDE(__bss_start = _bss_start); PROVIDE(__bss_end = _bss_end); PROVIDE(__stack_top = _stack_top); PROVIDE(__heap_start = _heap_start); PROVIDE(__heap_end = _heap_end); PROVIDE(__framebuf_base = _framebuf_base); PROVIDE(__framebuf_end = _framebuf_end);
|