2. Jacinto EVM板BSP编译环境搭建与交叉工具链验证
任务目标
在TI Jacinto平台(以J721E为例)上创建一个最小BSP工程,包含启动代码、链接脚本和基础外设(UART)初始化。编译生成可烧录镜像,加载到目标板后通过串口输出启动信息,以此验证BSP工程可独立运行。
环境与依赖
- 主机:Linux(Ubuntu 20.04+),已安装
gcc-arm-none-eabi(版本 9.x 或 10.x) - 目标板:TI J721E EVM(或兼容的Jacinto 7系列评估板)
- 调试/烧录:TI Uniflash 或 CCS(Code Composer Studio)配合 XDS110 调试器
- 串口终端:minicom / screen / PuTTY,波特率 115200
工程结构
bsp_min/
├── start.S # 启动代码:向量表、复位入口、栈初始化
├── linker.lds # 链接脚本:内存布局
├── main.c # 主程序:UART初始化与输出
├── uart.c # UART驱动实现
├── uart.h # UART寄存器定义与函数声明
└── Makefile # 构建脚本
关键完整代码
以下为最小BSP工程的核心实现。启动代码负责设置CPU模式与栈指针,链接脚本定义内存布局,UART驱动完成寄存器级初始化,主程序输出验证信息。
// start.S - 启动代码(ARM Cortex-A72,J721E主核)
.section .text._start
.global _start
_start:
// 设置栈指针(DDR地址,由链接脚本提供)
ldr x0, =__stack_top
mov sp, x0
// 清零BSS段
ldr x0, =__bss_start
ldr x1, =__bss_end
mov x2, #0
1: cmp x0, x1
b.ge 2f
str x2, [x0], #8
b 1b
2:
// 跳转到C入口
bl main
b . // 死循环(不应返回)
.section .vectors
.align 11
vectors:
b _start // 复位向量
.space 0x1C0 // 保留其他异常向量(最小实现)
// linker.lds - 链接脚本
ENTRY(_start)
MEMORY
{
DDR (rwx) : ORIGIN = 0x80000000, LENGTH = 256M
}
SECTIONS
{
. = 0x80000000;
.text : {
KEEP(*(.vectors))
*(.text._start)
*(.text*)
} > DDR
.rodata : { *(.rodata*) } > DDR
.data : { *(.data*) } > DDR
.bss : {
__bss_start = .;
*(.bss*)
__bss_end = .;
} > DDR
__stack_top = ORIGIN(DDR) + LENGTH(DDR);
}
// uart.h - UART寄存器定义(J721E UART0,基于8250兼容)
#ifndef UART_H
#define UART_H
#include <stdint.h>
#define UART0_BASE 0x02800000 // J721E UART0物理地址
#define UART_THR (*(volatile uint32_t *)(UART0_BASE + 0x00))
#define UART_RBR (*(volatile uint32_t *)(UART0_BASE + 0x00))
#define UART_LSR (*(volatile uint32_t *)(UART0_BASE + 0x14))
#define LSR_THRE (1 << 5) // 发送保持寄存器空
void uart_init(void);
void uart_putc(char c);
void uart_puts(const char *s);
#endif
// uart.c - UART初始化与输出
#include "uart.h"
void uart_init(void) {
// 启用UART0时钟(通过CTRL_MMR0,J721E专用)
volatile uint32_t *ctrl_core = (volatile uint32_t *)0x04000000;
ctrl_core[0x200/4] |= (1 << 0); // 使能UART0时钟(示例映射)
// 配置波特率115200(假设外设时钟48MHz)
volatile uint32_t *uart = (volatile uint32_t *)UART0_BASE;
uart[0x0C/4] = 0x80; // LCR: DLAB=1
uart[0x00/4] = 0x1A; // DLL = 26 (48MHz/16/115200 ≈ 26)
uart[0x04/4] = 0x00; // DLM = 0
uart[0x0C/4] = 0x03; // LCR: 8N1, DLAB=0
uart[0x08/4] = 0x00; // FCR: 禁用FIFO(最小实现)
}
void uart_putc(char c) {
while (!(UART_LSR & LSR_THRE)); // 等待发送就绪
UART_THR = c;
}
void uart_puts(const char *s) {
while (*s) {
if (*s == '\n') uart_putc('\r'); // 自动回车
uart_putc(*s++);
}
}
// main.c - 主程序
#include "uart.h"
void main(void) {
uart_init();
uart_puts("\n=== J721E Minimal BSP ===\n");
uart_puts("Boot successful!\n");
while (1); // 保持运行
}
// Makefile - 构建脚本
CROSS = aarch64-none-elf-
CFLAGS = -mcpu=cortex-a72 -nostdlib -ffreestanding -O2 -Wall
LDFLAGS = -T linker.lds -nostdlib
all: bsp_min.bin
start.o: start.S
$(CROSS)gcc $(CFLAGS) -c $< -o $@
uart.o: uart.c uart.h
$(CROSS)gcc $(CFLAGS) -c $< -o $@
main.o: main.c uart.h
$(CROSS)gcc $(CFLAGS) -c $< -o $@
bsp_min.elf: start.o uart.o main.o
$(CROSS)gcc $(LDFLAGS) $^ -o $@
bsp_min.bin: bsp_min.elf
$(CROSS)objcopy -O binary $< $@
clean:
rm -f *.o *.elf *.bin
编译与烧录
- 将上述文件保存至
bsp_min/目录,执行make生成bsp_min.bin。 - 使用 TI Uniflash 通过 UART 或 USB 将
bsp_min.bin烧录至 J721E EVM 的 QSPI Flash(地址0x0),或通过 CCS 加载 ELF 到 DDR 运行。 - 连接串口线至 J721E 的 UART0 接口(J42 或板载调试串口),打开终端并设置波特率 115200、8N1。
验证方法
复位目标板后,串口终端应显示以下输出(预期输出,由代码逻辑直接推出):
=== J721E Minimal BSP ===
Boot successful!
若看到上述两行信息,说明启动代码正确执行、链接脚本内存布局有效、UART 初始化成功,最小 BSP 工程验证通过。若串口无输出,依次检查:
1. 串口连接与终端波特率是否匹配;
2. UART0 时钟使能寄存器地址是否正确(不同 Jacinto 平台可能不同);
3. 链接脚本中 __stack_top 是否落在有效 DDR 范围内。