嵌入式系统中的Board Support Package (BSP)详解:以Xilinx Zynq为例

news2025/3/17 7:04:20

嵌入式系统中的Board Support Package (BSP)详解:以Xilinx Zynq为例

引言

在嵌入式系统开发中,硬件与软件的无缝集成至关重要。Board Support Package (BSP) 作为连接硬件和操作系统的桥梁,在这一过程中扮演着核心角色。本文将深入探讨BSP的概念、组成部分及其在Xilinx Zynq平台上的应用,特别聚焦设备树和硬件抽象层(HAL)这两个关键组件,帮助您理解BSP如何简化嵌入式系统开发并提高开发效率。

BSP的基本概念

什么是BSP?

Board Support Package (BSP)是一组软件组件的集合,它为特定硬件平台提供基础支持,使操作系统能够在该硬件上正常运行。BSP封装了硬件细节,提供标准化接口,允许上层软件(如操作系统和应用程序)与底层硬件交互而无需了解硬件的具体实现。

在嵌入式系统软件层次结构中,BSP的位置如下:
在这里插入图片描述

应用软件
    ↓
操作系统/中间件
    ↓
Board Support Package (BSP)
    ↓
硬件平台

BSP的组成部分

一个典型的BSP通常包含以下核心组件:

  1. 引导加载程序(Bootloader)

    • 初始化关键硬件组件
    • 加载操作系统内核
    • 例如:FSBL (First Stage Boot Loader)、U-Boot等
  2. 设备驱动程序

    • 为各种硬件外设提供操作接口
    • 包括UART、I2C、SPI、GPIO、以太网等驱动
    • 允许操作系统控制和使用这些设备
  3. 硬件初始化代码

    • 配置时钟、电源管理和内存控制器
    • 设置中断控制器
    • 初始化关键系统组件
  4. 硬件抽象层(HAL)

    • 提供硬件寄存器的抽象访问接口
    • 简化应用程序对硬件的操作
    • 提高代码可移植性
  5. 设备树

    • 描述硬件配置的数据结构
    • 定义外设、内存映射和中断
    • 配置内核如何与硬件交互
  6. 内存映射表

    • 定义硬件寄存器和内存区域的地址映射
    • 配置内存控制器和缓存
    • 设置内存保护单元
  7. 配置文件

    • 系统参数定义
    • 编译和链接选项
    • 硬件配置信息

BSP的功能与职责

BSP在嵌入式系统中执行以下关键功能:

  1. 硬件抽象:隐藏硬件细节,提供统一接口
  2. 设备支持:通过驱动程序支持各种硬件外设
  3. 启动与初始化:确保系统正确启动和初始化
  4. 中断管理:处理和分发硬件中断
  5. 电源管理:控制系统电源状态
  6. 内存管理:配置和管理系统内存
  7. 调试支持:提供调试接口和工具

Xilinx Zynq平台概述

在深入Zynq BSP之前,先了解一下Xilinx Zynq平台的基本架构。

Zynq架构特点

Xilinx Zynq是一种异构系统级芯片(SoC),集成了处理系统(PS)和可编程逻辑(PL):

  1. 处理系统(PS)

    • 包含ARM Cortex-A9双核处理器(Zynq-7000系列)或Cortex-A53四核处理器(Zynq UltraScale+系列)
    • 集成内存控制器、USB、以太网、UART等标准外设
    • 提供高性能通用计算能力
  2. 可编程逻辑(PL)

    • 基于FPGA技术的可编程硬件
    • 可实现定制硬件加速器和接口
    • 提供灵活的硬件定制能力
  3. PS-PL接口

    • 通过AXI接口连接处理系统和可编程逻辑
    • 支持高速数据传输
    • 实现软硬件协同设计

这种异构架构使Zynq平台非常适合需要高性能处理和硬件加速的嵌入式应用。

Zynq启动流程

Zynq平台的启动过程涉及多个阶段,BSP在其中扮演关键角色:

  1. BootROM

    • 芯片内置的只读程序
    • 执行初始启动配置
    • 加载FSBL
  2. FSBL (First Stage Boot Loader)

    • 初始化关键硬件(处理器、DDR、时钟等)
    • 加载FPGA比特流(如果有)
    • 加载第二阶段引导程序(通常是U-Boot)
  3. U-Boot

    • 初始化更多硬件设备
    • 提供命令行界面
    • 加载操作系统内核和设备树
  4. 操作系统

    • 接管系统控制
    • 初始化驱动程序
    • 启动应用程序

Xilinx Zynq的BSP详解

Zynq BSP的类型

Xilinx为Zynq平台提供了两种主要的BSP实现方式:

  1. 独立式BSP (Standalone BSP)

    • 用于裸机应用或实时操作系统
    • 不依赖复杂的操作系统
    • 提供基本的硬件抽象层
    • 适合资源受限或实时要求高的应用
  2. 基于操作系统的BSP

    • 支持Linux、FreeRTOS等操作系统
    • 提供完整的驱动程序和服务
    • 包含设备树和内核配置
    • 适合复杂应用和网络功能

Zynq Standalone BSP的组成

以Xilinx Vitis/SDK创建的Standalone BSP为例,其主要组件包括:

  1. 处理器初始化代码

    • 初始化ARM处理器
    • 配置MMU、缓存和异常向量
    • 设置栈和堆
  2. 外设驱动库

    • 提供访问UART、I2C、SPI等外设的API
    • 支持中断和DMA操作
    • 包含PS-PL接口驱动
  3. 系统库

    • 提供标准C库函数
    • 包含数学函数和字符串处理
    • 支持内存分配和管理
  4. 启动代码

    • 处理器复位后的入口点
    • 执行硬件初始化
    • 调用main函数
  5. 链接脚本

    • 定义内存布局
    • 指定代码和数据段位置
    • 配置堆栈大小

Zynq Linux BSP的组成

使用PetaLinux工具创建的Linux BSP主要包括:

  1. FSBL

    • 初始化基本硬件
    • 加载FPGA比特流
    • 加载U-Boot
  2. U-Boot

    • 第二阶段引导加载程序
    • 提供环境变量和命令行界面
    • 加载Linux内核和设备树
  3. Linux内核

    • 定制的Linux内核
    • 包含Zynq特定驱动程序
    • 支持PS和PL部分集成
  4. 设备树

    • 描述硬件配置的数据结构
    • 定义外设、内存映射和中断
    • 配置内核如何与硬件交互
  5. 根文件系统

    • 基本的Linux文件系统
    • 包含系统工具和库
    • 可选的应用程序和服务

BSP创建与定制

使用Xilinx工具创建BSP

Xilinx提供了多种工具来创建和定制BSP:

  1. 使用Vitis/SDK创建Standalone BSP

    a. 创建硬件平台:

    • 使用Vivado设计硬件系统
    • 导出硬件描述到Vitis

    b. 创建BSP项目:

    • 在Vitis中创建新的应用项目
    • 选择"创建新的平台"选项
    • 导入硬件描述文件
    • 选择处理器(如PS7_cortexa9_0)

    c. 配置BSP设置:

    • 选择操作系统(如"standalone")
    • 配置BSP选项(如stdout设备、堆栈大小等)
    • 选择需要的库和驱动程序

    d. 生成BSP:

    • Vitis自动生成BSP文件
    • 生成的BSP包含所有必要的驱动和库
  2. 使用PetaLinux创建Linux BSP

    a. 创建PetaLinux项目:

    petalinux-create --type project --template zynq --name my_project
    

    b. 导入硬件描述:

    cd my_project
    petalinux-config --get-hw-description=/path/to/hardware
    

    c. 配置Linux内核:

    petalinux-config -c kernel
    

    d. 配置根文件系统:

    petalinux-config -c rootfs
    

    e. 构建BSP:

    petalinux-build
    

    f. 打包BSP映像:

    petalinux-package --boot --format BIN --fsbl images/linux/zynq_fsbl.elf --u-boot
    

深入理解设备树(Device Tree)

设备树的基本概念

设备树是一种描述硬件配置的数据结构,它采用树状结构组织,包含节点和属性。在嵌入式Linux系统中,设备树已成为描述非x86架构硬件的标准方法,取代了早期的硬编码方式。

设备树的主要目的是将硬件描述与内核代码分离,使同一个内核镜像可以支持多种硬件配置,只需更换设备树文件即可。

设备树文件格式

设备树有三种主要文件格式:

  1. DTS (Device Tree Source)

    • 人类可读的文本格式
    • 包含节点、属性和值
    • 使用类似C语言的语法
  2. DTB (Device Tree Blob)

    • 编译后的二进制格式
    • 由内核直接解析
    • 通常由bootloader加载并传递给内核
  3. DTSI (Device Tree Source Include)

    • 包含公共定义的源文件
    • 可被多个DTS文件包含
    • 用于代码复用

设备树在Zynq中的应用

在Zynq平台上,设备树负责描述:

  1. 处理系统(PS)外设

    • 描述ARM核心、缓存、MMU等
    • 定义内存控制器和DDR配置
    • 配置UART、I2C、SPI、以太网等外设
  2. 可编程逻辑(PL)组件

    • 描述用户创建的IP核
    • 定义AXI接口和中断映射
    • 配置PL时钟和电源域
  3. PS-PL接口

    • 定义AXI互连配置
    • 映射PL中断到PS中断控制器
    • 配置DMA通道

设备树示例(Zynq相关部分)

以下是Zynq平台设备树的简化示例:

/ {
    compatible = "xlnx,zynq-7000";
    
    cpus {
        #address-cells = <1>;
        #size-cells = <0>;
        
        cpu@0 {
            compatible = "arm,cortex-a9";
            device_type = "cpu";
            reg = <0>;
            clocks = <&clkc 3>;
        };
        
        cpu@1 {
            compatible = "arm,cortex-a9";
            device_type = "cpu";
            reg = <1>;
            clocks = <&clkc 3>;
        };
    };
    
    amba: amba {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;
        
        uart0: serial@e0000000 {
            compatible = "xlnx,xuartps";
            reg = <0xe0000000 0x1000>;
            interrupts = <0 27 4>;
            clocks = <&clkc 23>, <&clkc 40>;
            clock-names = "uart_clk", "pclk";
        };
        
        /* 其他PS外设节点... */
    };
    
    /* PL部分自定义IP */
    my_custom_ip: my_custom_ip@43c00000 {
        compatible = "vendor,my-custom-ip";
        reg = <0x43c00000 0x10000>;
        interrupts = <0 29 4>;
    };
};

在这个例子中:

  • 顶层节点定义了整个系统
  • cpus节点描述了双核Cortex-A9处理器
  • amba节点包含了AMBA总线上的PS外设
  • uart0节点描述了UART外设的基地址、中断号和时钟源
  • my_custom_ip节点描述了PL中的自定义IP核

设备树与内核驱动的关系

设备树与内核驱动程序之间存在密切的关系:

  1. 设备-驱动匹配

    • 内核使用compatible属性匹配设备和驱动
    • 驱动程序通过设备树获取硬件配置信息
    • 无需修改驱动代码即可支持不同硬件配置
  2. 资源获取

    • 驱动程序从设备树获取资源信息(地址、中断等)
    • 使用标准API访问这些资源
    • 例如:of_iomap()of_irq_get()
  3. 设备属性配置

    • 通过设备树配置驱动行为
    • 定义设备特定参数
    • 支持运行时选项

硬件抽象层(HAL)详解

HAL的概念与目的

硬件抽象层(Hardware Abstraction Layer, HAL)是一种软件层,它隐藏了底层硬件的具体细节,提供标准化的API,使上层软件能够以一致的方式访问不同的硬件平台。

HAL的主要目的是:

  • 提高代码可移植性
  • 简化应用程序开发
  • 隐藏硬件复杂性
  • 标准化硬件访问接口

HAL的层次结构

HAL通常分为多个层次:

  1. 底层HAL

    • 直接与硬件寄存器交互
    • 提供基本的读写操作
    • 实现最低级别的硬件控制
  2. 中间层HAL

    • 提供设备级别的抽象
    • 实现通用功能(如中断管理)
    • 处理硬件特定的初始化和配置
  3. 上层HAL

    • 提供面向应用的API
    • 实现高级功能(如DMA传输)
    • 隐藏平台特定的细节

Zynq Standalone BSP中的HAL实现

在Zynq的Standalone BSP中,HAL主要包括:

  1. 低级硬件访问函数
    • 寄存器读写操作
    • 内存屏障和同步原语
    • 例如:Xil_In32()Xil_Out32()
// 读取32位寄存器
static inline u32 Xil_In32(u32 Addr) {
    return *(volatile u32 *) Addr;
}

// 写入32位寄存器
static inline void Xil_Out32(u32 Addr, u32 Value) {
    *(volatile u32 *) Addr = Value;
}
  1. 外设驱动API
    • 设备初始化和配置
    • 数据传输和处理
    • 中断管理
// GPIO设备初始化
int XGpio_Initialize(XGpio *InstancePtr, u16 DeviceId) {
    XGpio_Config *ConfigPtr;
    
    // 查找设备配置
    ConfigPtr = XGpio_LookupConfig(DeviceId);
    if (ConfigPtr == NULL) {
        return XST_DEVICE_NOT_FOUND;
    }
    
    // 设置基地址和其他参数
    InstancePtr->BaseAddress = ConfigPtr->BaseAddress;
    // ...其他初始化代码...
    
    return XST_SUCCESS;
}

// 设置GPIO方向
void XGpio_SetDataDirection(XGpio *InstancePtr, unsigned Channel, u32 DirectionMask) {
    // 计算寄存器地址
    u32 RegOffset = (Channel == 1) ? XGPIO_TRI_OFFSET : XGPIO_TRI2_OFFSET;
    
    // 写入方向寄存器
    Xil_Out32(InstancePtr->BaseAddress + RegOffset, DirectionMask);
}
  1. 系统服务
    • 异常和中断处理
    • 缓存和MMU管理
    • 定时器和延迟函数
// 启用ARM处理器中断
void Xil_ExceptionEnable(void) {
    // 修改CPSR寄存器,启用中断
    asm volatile ("mrs r0, cpsr");
    asm volatile ("bic r0, r0, #0x80");
    asm volatile ("msr cpsr_c, r0");
}

Zynq Linux BSP中的HAL实现

在Linux BSP中,HAL的实现更加复杂,它通过内核的多层抽象实现:

  1. 内核硬件抽象

    • 体系结构特定代码(arch/arm/)
    • 通用设备模型和驱动框架
    • 资源管理和分配
  2. 设备驱动框架

    • 平台设备和驱动模型
    • 总线抽象(如PCI、I2C、SPI)
    • 通用子系统(如GPIO、DMA、时钟)
  3. 用户空间接口

    • 设备文件(/dev/)
    • sysfs接口(/sys/)
    • ioctl调用

在Linux内核中,HAL的一个重要部分是设备驱动模型,它提供了统一的框架来管理设备和驱动程序。例如,Zynq的UART驱动:

static struct platform_driver cdns_uart_platform_driver = {
    .probe = cdns_uart_probe,
    .remove = cdns_uart_remove,
    .driver = {
        .name = CDNS_UART_DRIVER_NAME,
        .of_match_table = cdns_uart_of_match,
        .pm = &cdns_uart_pm_ops,
    },
};

static int cdns_uart_probe(struct platform_device *pdev)
{
    struct resource *res;
    struct uart_port *port;
    int irq;
    
    // 从设备树获取资源
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!res)
        return -ENODEV;
    
    // 获取中断号
    irq = platform_get_irq(pdev, 0);
    if (irq < 0)
        return irq;
    
    // 分配端口结构
    port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
    if (!port)
        return -ENOMEM;
    
    // 设置端口参数
    port->membase = devm_ioremap_resource(&pdev->dev, res);
    port->irq = irq;
    // ...其他初始化代码...
    
    // 注册UART端口
    return uart_add_one_port(&cdns_uart_uart_driver, port);
}

BSP、设备树和HAL的关系

BSP、设备树和HAL这三个概念紧密相关,共同构成了嵌入式系统的软件基础。

他们之间的概念的关系,我们在下一篇博客中讨论

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2316475.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue 生命周期详解:从创建到销毁的全过程

Vue.js 是一个流行的前端框架&#xff0c;它通过组件化的方式帮助开发者构建用户界面。在 Vue 中&#xff0c;每个组件实例都有其生命周期&#xff0c;从创建、挂载、更新到销毁&#xff0c;Vue 提供了一系列的生命周期钩子函数&#xff0c;允许我们在组件的不同阶段执行自定义…

计算机基础:二进制基础12,十进制数转换为十六进制

专栏导航 本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏&#xff0c;故划分为两个专栏导航。读者可以自行选择前往哪个专栏。 &#xff08;一&#xff09;WIn32 专栏导航 上一篇&#xff1a;计算机基础&#xff1a;二进制基础11&#xff0c;十六进制的位基…

SpringCloud系列教程(十四):Sentinel持久化

Sentinel之前已经搭建和应用成功了&#xff0c;但是它有一个很大的缺点就是官方没有提供持久化的方案&#xff0c;从项目源码上看感觉这款工具也没有完成的太好&#xff0c;所以需要我们去对它进行二次开发。要补充的功能大概如下&#xff1a; 1、将Sentinel接入nacos中&#…

Slider,InputField,Scroll View,Scrollbar及Layout组件

Slider组件 Fill Rect:填充滑动条选中区域的背景图部分 Handle Rect:滑动条的球 Direction:滑动条的滑动方向 Min Value:起始位置的数值&#xff08;浮点数&#xff09; Max Value:结束位置的数值&#xff08;浮点数&#xff09; Whole Numbers:必须为整数&#xff08;布尔…

ollama注册自定义模型(GGUF格式)

文章目录 ollama注册自定义模型&#xff08;GGUF格式&#xff09;下载模型注册模型(GGUF格式) ollama注册自定义模型&#xff08;GGUF格式&#xff09; 需要全程开启ollama nohup ollama serve > ollama.log 2>&1 &需要注意&#xff0c;尽管手动下载的GGUF格式模…

HarmonyOS NEXT 声明式UI语法学习笔记-创建自定义组件

基础语法概述 ArkTS的基本组成 装饰器&#xff1a;用于装饰类、结构、方法以及变量&#xff0c;并赋予其特殊含义。如上图都是装饰器&#xff0c;Component表示自定义组件&#xff0c;Entry表示表示自定义组件的入口组件&#xff0c;State表示组件中的状态变量&#xff0c;当状…

97.HarmonyOS NEXT跑马灯组件教程:基础概念与架构设计

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; HarmonyOS NEXT跑马灯组件教程&#xff1a;基础概念与架构设计 1. 跑马灯组件概述 跑马灯&#xff08;Marquee&#xff09;是一种常见的UI组件&a…

81.HarmonyOS NEXT 状态管理与响应式编程:@Observed深度解析

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; HarmonyOS NEXT 状态管理与响应式编程&#xff1a;Observed深度解析 文章目录 HarmonyOS NEXT 状态管理与响应式编程&#xff1a;Observed深度解析…

股指期货有卖不出去的时候吗?

在股指期货的交易世界里&#xff0c;很多人都有这样的疑问&#xff1a;股指期货会不会有卖不出去的时候呢&#xff1f;答案是会的&#xff0c;下面咱们就来详细唠唠为啥会出现这种情况。 市场极端行情下难以卖出 1.跌停限制&#xff1a;股指期货和股票一样&#xff0c;也有涨…

开发、科研、日常办公工具汇总(自用,持续更新)

主要记录汇总一下自己平常会用到的网站工具&#xff0c;方便查阅。 update&#xff1a;2025/2/11&#xff08;开发网站补一下&#xff09; update&#xff1a;2025/2/21&#xff08;补充一些AI工具&#xff0c;刚好在做AI视频相关工作&#xff09; update&#xff1a;2025/3/7&…

HTML5 drag API实现列表拖拽排序

拖拽API&#xff08;Drag and Drop API&#xff09;是HTML5提供的一组功能&#xff0c;使得在网页上实现拖放操作变得更加简单和强大。这个API允许开发者为网页元素添加拖拽功能&#xff0c;用户可以通过鼠标将元素拖动并放置到指定的目标区域。 事件类型 dragstart&#xff1…

改变一生的思维模型【11】升维

升维思维模型&#xff1a;突破认知局限的破局法则 一、定义与核心逻辑 升维思维是通过增加分析维度&#xff0c;将问题投射到更高认知层次寻找解决方案的思考方式。其本质是跳出原有竞争维度&#xff0c;在更广阔的空间重构游戏规则。核心逻辑在于&#xff1a;当低维战场陷入…

【动手学深度学习】#2线性神经网络

主要参考学习资料&#xff1a; 《动手学深度学习》阿斯顿张 等 著 【动手学深度学习 PyTorch版】哔哩哔哩跟李牧学AI 目录 2.1 线性回归2.1.1 线性回归的基本元素线性模型损失函数解析解随机梯度下降 2.1.3 最大似然估计 2.2 线性回归从零开始实现2.2.1 生成数据集2.2.2 读取数…

计算机网络——NAT

一、什么是NAT&#xff1f; NAT&#xff08;Network Address Translation&#xff0c;网络地址转换&#xff09; 是一种将 私有IP地址 与 公有IP地址 相互映射的技术&#xff0c;主要用于解决IPv4地址不足的问题。它像一名“翻译官”&#xff0c;在数据包经过路由器或防火墙时…

同一子网通信

添加交换机后的通信流程 1. 同一子网内&#xff08;使用交换机&#xff09; 判断是否在同一子网&#xff1a; 主机A通过子网掩码判断主机B的IP地址是否属于同一子网。若在同一子网&#xff0c;主机A需要通过ARP获取主机B的MAC地址。 ARP请求&#xff08;广播&#xff09;&…

IntelliJ IDEA 快捷键系列:重命名快捷键详解

目录 引言一、默认重命名快捷键1. Windows 系统‌2. Mac 系统‌ 二、操作步骤与技巧1. 精准选择重命名范围‌2. 智能过滤无关内容‌ 三、总结 引言 在代码重构中&#xff0c;‌重命名变量、类、方法‌ 是最常用的操作之一。正确使用快捷键可以极大提升开发效率。本文针对 ‌Ma…

零基础掌握分布式ID生成:从理论到实战的完整指南 [特殊字符]

一、为什么需要分布式ID&#xff1f; &#x1f914; 在单机系统中&#xff0c;使用数据库自增ID就能满足需求。但在分布式系统中&#xff0c;多个服务节点同时生成ID时会出现以下问题&#xff1a; ID冲突&#xff1a;不同节点生成相同ID 扩展困难&#xff1a;数据库自增ID无法…

使用python反射,实现pytest读取yaml并发送请求

pytest yaml yaml - feature: 用户模块story: 登录title: 添加用户request:method: POSTurl: /system/user/listheaders: nullparams: nullvalidate: nullread_yaml_all def read_yaml_all(path):with open(path, r, encodingutf-8) as f:value yaml.safe_load(f)return v…

Matlab 汽车悬架系统动力学建模与仿真

1、内容简介 略 Matlab 170-汽车悬架系统动力学建模与仿真 可以交流、咨询、答疑 2、内容说明 略 本文对题目给定的1/2汽车四自由度模型&#xff0c;建立状态空间模型进行系统分析&#xff0c;并通过MATLAB仿真对系统进行稳定性、可控可观测性分析&#xff0c;对得的结果进行…

专访数势科技谭李:智能分析 Agent 打通数据平权的最后一公里

作者|斗斗 编辑|皮爷 出品|产业家 伦敦塔桥下的泰晤士河底&#xff0c;埋藏着工业革命的隐秘图腾——布鲁内尔设计的隧道盾构机。在19世纪城市地下轨道建设的过程中&#xff0c;这个直径11米的钢铁巨兽没有选择拓宽河道&#xff0c;而是开创了地下通行的新维度。 “我们不…