2. Jacinto EVM板BSP编译环境搭建与交叉工具链验证

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

编译与烧录

  1. 将上述文件保存至 bsp_min/ 目录,执行 make 生成 bsp_min.bin。
  2. 使用 TI Uniflash 通过 UART 或 USB 将 bsp_min.bin 烧录至 J721E EVM 的 QSPI Flash(地址 0x0),或通过 CCS 加载 ELF 到 DDR 运行。
  3. 连接串口线至 J721E 的 UART0 接口(J42 或板载调试串口),打开终端并设置波特率 115200、8N1。

验证方法

复位目标板后,串口终端应显示以下输出(预期输出,由代码逻辑直接推出):

=== J721E Minimal BSP ===
Boot successful!

若看到上述两行信息,说明启动代码正确执行、链接脚本内存布局有效、UART 初始化成功,最小 BSP 工程验证通过。若串口无输出,依次检查:
1. 串口连接与终端波特率是否匹配;
2. UART0 时钟使能寄存器地址是否正确(不同 Jacinto 平台可能不同);
3. 链接脚本中 __stack_top 是否落在有效 DDR 范围内。