zephyr 硬件支持

 

架构相关·ARM 外设和硬件仿真器 外设 Pin 控制器 移植

  • 架构相关·ARM
  • 外设和硬件仿真器
  • 外设
  • Pin 控制器
  • 移植

Arm Cortex-M Developer Guide

Overview 概述

This page contains detailed information about the status of the Arm Cortex-M architecture porting in the Zephyr RTOS and describes key aspects when developing Zephyr applications for Arm Cortex-M-based platforms.

本页面包含有关在 Zephyr RTOS 中移植 ARM Cortex-M 架构的状态的详细信息,并描述为基于 ARM Cortex-M 的平台开发 Zephyr 应用程序时的关键方面。

OS features 操作系统功能

Threads

Thread stack alignment 线程堆栈对齐

Each Zephyr thread is defined with its own stack memory. By default, Cortex-M enforces a double word thread stack alignment, see CONFIG_STACK_ALIGN_DOUBLE_WORD. If MPU-based HW-assisted stack overflow detection (CONFIG_MPU_STACK_GUARD) is enabled, thread stacks need to be aligned with a larger value, reflected by CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE. In Arm v6-M and Arm v7-M architecture variants, thread stacks are additionally required to be align with a value equal to their size, in applications that need to support user mode (CONFIG_USERSPACE). The thread stack sizes in that case need to be a power of two. This is all reflected by CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT, that is enforced in Arm v6-M and Arm v7-M builds with user mode support.

每个 Zephyr 线程都用自己的堆栈内存定义。默认情况下,Cortex-M 强制执行双字线程堆栈对齐,请参见 CONFIG_STACK_ALIGN_DOUABLE_WORD。如果启用了基于 MPU 的 HW 辅助的堆栈溢出检测(CONFIG_MPU_STACK_GUARD) ,则线程堆栈需要与更大的值对齐,由 CONFIG_ARM_MPU_REGION_MIN_ALIGN_AND_SIZE 反映。在 Arm v6-M 和 Arm v7-M 架构变体中,在需要支持用户模式(CONFIG_USERSPACE)的应用程序中,线程堆栈还需要与其大小相等的值对齐。在这种情况下,线程堆栈的大小需要是 2 的幂次方。这一切都反映在 CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT 中,它在 Arm v6-M 和 Arm v7-M 构建中使用用户模式支持。

Stack pointers 堆栈指针

While executing in thread mode the processor is using the Process Stack Pointer (PSP). The processor uses the Main Stack Pointer (MSP) while executing in handler mode, that is, while servicing exceptions and HW interrupts. Using PSP in thread mode facilitates thread stack pointer manipulation during thread context switching, without affecting the current execution context flow in handler mode.

在线程模式下执行时,处理器使用进程堆栈指针(PSP)。处理器在 handler 模式下执行时使用主堆栈指针(MSP) ,也就是说,在处理异常和 HW 中断时。在线程模式下使用 PSP 有助于在线程上下文切换期间进行线程堆栈指针操作,而不会影响 handler 模式下的当前执行上下文流。

In Arm Cortex-M builds a single interrupt stack memory is shared among exceptions and interrupts. The size of the interrupt stack needs to be selected taking into consideration nested interrupts, each pushing an additional stack frame. Developers can modify the interrupt stack size using CONFIG_ISR_STACK_SIZE.

在 Arm Cortex-M 构建中,异常和中断之间共享单个中断堆栈内存。中断堆栈的大小需要在考虑嵌套中断的情况下进行选择,每个中断推送一个额外的堆栈帧。开发人员可以使用 CONFIG_ISR_STACK_SIZE 修改中断堆栈大小。

The interrupt stack is also used during early boot so the kernel can initialize the main thread’s stack before switching to the main thread.

中断堆栈也在早期引导期间使用,因此内核可以在切换到主线程之前初始化主线程的堆栈。

Thread context switching 线程上下文切换

In Arm Cortex-M builds, the PendSV exception is used in order to trigger a context switch to a different thread. PendSV exception is always present in Cortex-M implementations. PendSV is configured with the lowest possible interrupt priority level, in all Cortex-M variants. The main reasons for that design are

在 Arm Cortex-M 构建中,PendSV 异常用于触发上下文切换到另一个线程。PendSV 异常总是出现在 Cortex-M 实现中。在所有 Cortex-M 变体中,PendSV 配置为可能的最低中断优先级。设计的主要原因是

  • to utilize the tail chaining feature of Cortex-M processors, and thus limit the number of context switch operations that occur.

    利用 Cortex-M 处理器的尾链特性,从而限制发生的上下文切换操作的数量。

  • to not impact the interrupt latency observed by HW interrupts.

    不要影响硬件中断观察到的中断延迟

As a result, context switch in Cortex-M is non-atomic, i.e. it may be preempted by HW interrupts, however, a context-switch operation must be completed before a new thread context-switch may start.

因此,Cortex-M 中的上下文切换是非原子的,也就是说,它可能被 HW 中断抢占,然而,上下文切换操作必须在一个新的线程上下文切换可能启动之前完成。

Typically a thread context-switch will perform the following operations

通常,线程上下文切换将执行以下操作

  • When switching-out the current thread, the processor stores

    当切换当前线程时,处理器存储

    • the callee-saved registers (R4 - R11) in the thread’s container for callee-saved registers, which is located in kernel memory

      被调用方保存的寄存器(R4-R11)位于线程的容器中,用于被调用方保存的寄存器,该寄存器位于内核内存中

    • the thread’s current operation mode

      线程的当前操作模式

      • user or privileged execution mode

        用户或特权执行模式

      • presence of an active floating point context

        活动浮点上下文的存在

      • the EXC_RETURN value of the current handler context (PendSV)

        当前处理程序上下文(PendSV)的 EXC_RETURN 值

    • the floating point callee-saved registers (S16 - S31) in the thread’s container for FP callee-saved registers, if the current thread has an active FP context

      如果当前线程具有活动的 FP 上下文,则在线程容器中的浮点被调用方保存寄存器(S16-S31)用于 FP 被调用方保存寄存器

    • the PSP of the current thread which points to the beginning of the current thread’s exception stack frame. The latter contains the caller-saved context and the return address of the switched-out thread.

      当前线程的 PSP,它指向当前线程的异常堆栈帧的开始。后者包含调用方保存的上下文和切换出线程的返回地址。

  • When switching-in a new thread the processor

    切换新线程时,处理器

    • restores the new thread’s callee-saved registers from the thread’s container for callee-saved registers

      从线程的容器中为被调用方保存的寄存器恢复新线程的被调用方保存的寄存器

    • restores the new thread’s operation mode

      恢复新线程的操作模式

    • restores the FP callee-saved registers if the switched-in thread had an active FP context before being switched-out

      如果切换进来的线程在切换出来之前有一个活动的 FP 上下文,则恢复 FP 被调用者保存的寄存器

    • re-programs the dynamic MPU regions to allow a user thread access its stack and application memories, and/or programs a stack-overflow MPU guard at the bottom of the thread’s privileged stack

      重新编写动态 MPU 区域的程序,以允许用户线程访问其堆栈和应用程序存储器,并/或在线程的特权堆栈底部编写堆栈溢出 MPU 保护程序

    • restores the PSP for the incoming thread and re-programs the stack pointer limit register (if applicable, see CONFIG_BUILTIN_STACK_GUARD)

      恢复传入线程的 PSP 并重新编程堆栈指针限制寄存器(如果适用,请参见 CONFIG_BUILTIN_STACK_GUARD)

    • optionally does a stack limit checking for the switched-in thread, if sentinel-based stack limit checking is enabled (see CONFIG_STACK_SENTINEL).

      如果启用了基于哨兵的堆栈限制检查(参见 CONFIG_STACK_SENTINEL) ,则可以选择为切换的线程执行堆栈限制检查。

PendSV exception return sequence restores the new thread’s caller-saved registers and the return address, as part of unstacking the exception stack frame.

PendSV 异常返回序列还原新线程的调用者保存的寄存器和返回地址,作为解栈异常堆栈框架的一部分。

The implementation of the context-switch mechanism is present in arch/arm/core/aarch32/swap_helper.S.

上下文切换机制的实现包含在 arch/arm/core/aarch32/swap_helper.S 中。

Stack limit checking (Arm v8-M)

堆栈限制检查(Arm v8-M)

Armv8-M and Armv8.1-M variants support stack limit checking using the MSPLIM and PSPLIM core registers. The feature is enabled when CONFIG_BUILTIN_STACK_GUARD is set. When stack limit checking is enabled, both the thread’s privileged or user stack, as well as the interrupt stack are guarded by PSPLIM and MSPLIM registers, respectively. MSPLIM is configured once during kernel boot, while PSLIM is re-programmed during every thread context-switch or during system calls, when the thread switches from using its default stack to using its privileged stack, and vice versa. PSPLIM re-programming

Armv8-M 和 Armv8.1-M 变体支持使用 MSPLIM 和 PSPLIM 核心寄存器进行堆栈限制检查。设置 CONFIG_BUILTIN_STACK_GUARD 时启用该特性。当启用堆栈限制检查时,线程的特权或用户堆栈以及中断堆栈分别由 PSPLIM 和 MSPLIM 寄存器保护。MSPLIM 在内核引导期间配置一次,而 PSLIM 在每次线程上下文切换或系统调用期间重新编程,当线程从使用其默认堆栈切换到使用其特权堆栈时,反之亦然。PSPLIM 重新编程

  • has a relatively low runtime overhead (programming is done with MSR instructions)

    具有相对较低的运行时开销(编程使用 MSR 指令完成)

  • does not impact interrupt latency

    不会影响中断延迟

  • does not require any memory areas to be reserved for stack guards

    不需要为堆栈保护保留任何内存区域

  • does not make use of MPU regions

    不使用MPU 区域

It is, therefore, considered as a lightweight but very efficient stack overflow detection mechanism in Cortex-M applications.

因此,在 Cortex-M 应用程序中,它被认为是一种轻量级但非常有效的堆栈溢出检测机制。

Stack overflows trigger the dedicated UsageFault exception provided by Arm v8-M.

堆栈溢出触发 Arm v8-M 提供的专用 UsageFault 异常。

Interrupt handling features 中断处理特性

This section describes certain aspects around exception and interrupt handling in Arm Cortex-M.

本节描述 Arm Cortex-M 中异常和中断处理的某些方面

Interrupt priority levels 中断优先级

The number of available (configurable) interrupt priority levels is determined by the number of implemented interrupt priority bits in NVIC; this needs to be described for each Cortex-M platform using DeviceTree:

可用(可配置)中断优先级的数量由 NVIC 中实现的中断优先级位的数量决定; 这需要使用 DeviceTree 对每个 Cortex-M 平台进行描述:

&nvic {
        arm,num-irq-priority-bits = <#priority-bits>;
};

Reserved priority levels 保留优先级别

A number of interrupt priority levels are reserved for the OS.

为操作系统保留了许多中断优先级

By design, system fault exceptions have the highest priority level. In Baseline Cortex-M, this is actually enforced by hardware, as HardFault is the only available processor fault exception, and its priority is higher than any configurable exception priority.

通过设计,系统故障异常具有最高的优先级。在 Baseline Cortex-M 中,这实际上是由硬件强制执行的,因为 HardFault 是唯一可用的处理器故障异常,其优先级高于任何可配置的异常优先级。

In Mainline Cortex-M, the available fault exceptions (e.g. MemManageFault, UsageFault, etc.) are assigned the highest configurable priority level. (CONFIG_CPU_CORTEX_M_HAS_PROGRAMMABLE_FAULT_PRIOS signifies explicitly that the Cortex-M implementation supports configurable fault priorities.)

在 Mainline Cortex-M 中,可用的故障异常(例如 MemManagefault、 UsageFault 等)被分配到可配置的最高优先级。(CONFIG_CPU_CORTEX_M_HAS_PROGRAMMABLE_FAULT_PRIOS 明确表示 Cortex-M 实现支持可配置的故障优先级。)

This priority level is never shared with HW interrupts (an exception to this rule is described below). As a result, processor faults occurring in regular ISRs will be handled by the corresponding fault handler and will not escalate to a HardFault, similar to processor faults occurring in thread mode.

这个优先级别从不与 HW 中断共享(下面描述了这条规则的一个例外)。因此,在常规 ISR 中发生的处理器故障将由相应的故障处理程序处理,并且不会升级为 HardFault,类似于线程模式中发生的处理器故障。

SVC exception is normally configured with the highest configurable priority level (an exception to this rule will be described below). SVCs are used by the Zephyr kernel to dispatch system calls, trigger runtime system errors (e.g. Kernel oops or panic), or implement IRQ offloading.

SVC 异常通常配置为可配置的最高优先级(下面将描述此规则的一个异常)。Zephyr 内核使用 SVC 分派系统调用、触发运行时系统错误(例如 Kernel oops 或 panic)或实现 IRQ 卸载。

In Baseline Cortex-M the priority level of SVC may be shared with other exceptions or HW interrupts that are also given the highest configurable priority level (As a result of this, kernel runtime errors during interrupt handling will escalate to HardFault. Additional logic in the fault handling routines ensures that such runtime errors are detected successfully).

在 Baseline Cortex-M 中,SVC 的优先级可以与其他异常或 HW 中断共享,这些异常或 HW 中断也被赋予最高的可配置优先级(因此,中断处理期间的内核运行时错误将升级到 HardFault。故障处理例程中的附加逻辑确保成功地检测到此类运行时错误)。

In Mainline Cortex-M, however, the SVC priority level is reserved, thus normally it is only shared with the fault exceptions of configurable priority. This simplifies the fault handling routines in Mainline Cortex-M architecture, since runtime kernel errors are serviced by the SVC handler (i.e no HardFault escalation, even if the kernel errors occur in ISR context).

然而,在主线 Cortex-M 中,SVC 优先级是保留的,因此通常只与可配置优先级的故障异常共享。这简化了 Mainline Cortex-M 体系结构中的错误处理例程,因为运行时内核错误由 SVC 处理程序处理(即没有 HardFault 升级,即使内核错误发生在 ISR 上下文中)。

HW interrupts in Mainline Cortex-M builds are allocated a priority level lower than the SVC.

在主线 Cortex-M 构建中,HW 中断的优先级比 SVC 低。

One exception to the above rules is when Zephyr applications support Zero Latency Interrupts (ZLIs). Such interrupts are designed to have a priority level higher than any HW or system interrupt. If the ZLI feature is enabled in Mainline Cortex-M builds (see CONFIG_ZERO_LATENCY_IRQS), then

上述规则的一个例外是当 Zephyr 应用程序支持零延迟中断(ZLI)时。这样的中断被设计为具有比任何硬件或系统中断更高的优先级。如果在 Mainline Cortex-M 构建中启用了 ZLI 特性(参见 CONFIG_ZERO_LatENCY_IRQS) ,则

  • ZLIs are assigned the highest configurable priority level

    为 ZLI 分配最高的可配置优先级

  • SVCs are assigned the second highest configurable priority level

    SVC 被分配到第二高的可配置优先级级别

  • Regular HW interrupts are assigned priority levels lower than SVC.

    常规 HW 中断的优先级比 SVC 低。

The priority level configuration in Cortex-M is implemented in include/arch/arm/aarch32/exc.h.

Cortex-M 中的优先级配置在 include/arch/arm/aarch32/exc.h 中实现。

Locking and unlocking IRQs 锁定和解锁 IRQ

In Baseline Cortex-M locking interrupts is implemented using the PRIMASK register.

在基线 Cortex-M 锁定中断是使用 PRIMASK 寄存器实现的。

arch_irq_lock()

will set the PRIMASK register to 1, eventually, masking all IRQs with configurable priority. While this fulfils the OS requirement of locking interrupts, the consequence is that kernel runtime errors (triggering SVCs) will escalate to HardFault.

将 PRIMASK 寄存器设置为 1,最终用可配置的优先级屏蔽所有 IRQ。虽然这满足了锁定中断的操作系统要求,但结果是内核运行时错误(触发 SVC)将升级到 HardFault。

In Mainline Cortex-M locking interrupts is implemented using the BASEPRI register (Mainline Cortex-M builds select CONFIG_CPU_CORTEX_M_HAS_BASEPRI to signify that BASEPRI register is implemented.). By modifying BASEPRI (or BASEPRI_MAX) arch_irq_lock() masks all system and HW interrupts with the exception of

在 Mainline Cortex-M 中,锁定中断是使用 BASEPRI 寄存器实现的(Mainline Cortex-M 构建时选择 CONFIG_CPU_CORTEX_M_HAS_BASEPRI 表示实现了 BASEPRI 寄存器).通过修改 BASEPRI (或 BASEPRI_MAX) arch_irq_lock()屏蔽所有系统,除了

  • SVCs

  • processor faults

    处理器故障

  • ZLIs

This allows zero latency interrupts to be triggered inside OS critical sections. Additionally, this allows system (processor and kernel) faults to be handled by Zephyr in exactly the same way, regardless of whether IRQs have been locked or not when the error occurs. It also allows for system calls to be dispatched while IRQs are locked.

这允许在操作系统关键区域内触发零延迟中断。此外,这允许 Zephyr 以完全相同的方式处理系统(处理器和内核)故障,而不管发生错误时 IRQ 是否已被锁定。它还允许在 IRQ 被锁定时发送系统调用。

Note 注意

Mainline Cortex-M fault handling is designed and configured in a way that all processor and kernel faults are handled by the corresponding exception handlers and never result in HardFault escalation. In other words, a HardFault may only occur in Zephyr applications that have modified the default fault handling configurations. The main reason for this design was to reserve the HardFault exception for handling exceptional error conditions in safety critical applications.

主线 Cortex-M 故障处理的设计和配置方式是,所有处理器和内核故障都由相应的异常处理程序处理,而不会导致 HardFault 升级。换句话说,HardFault 可能只出现在修改了默认错误处理配置的 Zephyr 应用程序中。此设计的主要原因是保留 HardFault 异常,以便在安全关键应用程序中处理异常错误条件。

Dynamic direct interrupts 动态直接中断

Cortex-M builds support the installation of direct interrupt service routines during runtime. Direct interrupts are designed for performance-critical interrupt handling and do not go through all of the common Zephyr interrupt handling code.

Cortex-M 构建支持在运行时安装直接中断服务例程。直接中断是为性能关键型中断处理而设计的,它不会遍历所有常见的 Zephyr 中断处理代码。

Direct dynamic interrupts are enabled via switching on CONFIG_DYNAMIC_DIRECT_INTERRUPTS.

直接动态中断可以通过在 CONFIG_DYNAMIC_DIRECT_INTERRUPTS 上进行切换来启用。

Note that enabling direct dynamic interrupts requires enabling support for dynamic interrupts in the kernel, as well (see CONFIG_DYNAMIC_INTERRUPTS).

注意,启用直接动态中断也需要支持内核中的动态中断(参见 CONFIG_DYNAMIC_INTERRUPTS)。

Zero Latency interrupts 零延迟中断

As described above, in Mainline Cortex-M applications, the Zephyr kernel reserves the highest configurable interrupt priority level for its own use (SVC). SVCs will not be masked by interrupt locking. Zero-latency interrupt can be used to set up an interrupt at the highest interrupt priority which will not be blocked by interrupt locking. To use the ZLI feature CONFIG_ZERO_LATENCY_IRQS needs to be enabled.

如上所述,在 Mainline Cortex-M 应用程序中,Zephyr 内核为自己的使用(SVC)保留了最高的可配置中断优先级。SVC 不会被中断锁屏蔽。零延迟中断可以用来在中断优先级最高的地方设置一个中断,这个优先级不会被中断锁阻塞。要使用 ZLI 特性 CONFIG_ZERO_LatENCY_IRQS,需要启用。

Zero latency IRQs have minimal interrupt latency, as they will always preempt regular HW or system interrupts.

零延迟 IRQ 具有最小的中断延迟,因为它们总是会抢占常规的硬件或系统中断。

Note, however, that since ZLI ISRs will run at a priority level higher than the kernel exceptions they cannot use any kernel functionality. Additionally, since the ZLI interrupt priority level is equal to processor fault priority level, faults occurring in ZLI ISRs will escalate to HardFault and will not be handled in the same way as regular processor faults. Developers need to be aware of this limitation.

但是,请注意,由于 ZLI ISR 运行的优先级高于内核异常,因此它们不能使用任何内核功能。此外,由于 ZLI 中断优先级级别等于处理器故障优先级级别,所以 ZLI ISR 中发生的故障将升级到 HardFault,并且不会像处理常规处理器故障那样进行处理。开发人员需要意识到这个限制。

CPU Idling CPU 空闲

The Cortex-M architecture port implements both k_cpu_idle() and k_cpu_atomic_idle(). The implementation is present in arch/arm/core/aarch32/cpu_idle.S.

Cortex-M 架构端口同时实现了 k_cpu_idle()和 k_cpu_atomic_idle() ,其实现方式是 arch/arm/core/aarch32/cpu_idle.S。

In both implementations, the processor will attempt to put the core to low power mode. In k_cpu_idle() the processor ends up executing WFI (Wait For Interrupt) instruction, while in k_cpu_atomic_idle() the processor will execute a WFE (Wait For Event) instruction.

在这两种实现中,处理器都会尝试将核心设置为低功耗模式。在 k_cpu_idle()中,处理器最终执行 WFI (等待中断)指令,而在 k_cpu_atomic_idle()中,处理器将执行 WFE (等待事件)指令。

When using the CPU idling API in Cortex-M it is important to note the following:

当使用 Cortex-M 中的 CPU 空闲 API 时,必须注意以下事项:

  • Both k_cpu_idle() and k_cpu_atomic_idle() are assumed to be invoked with interrupts locked. This is taken care of by the kernel if the APIs are called by the idle thread.

    假设在锁定中断的情况下调用 k_cpu_dle()和 k_cpu_atomic_idle()。如果这些 API 被空闲线程调用,则由内核负责处理。

  • After waking up from low power mode, both functions will restore interrupts unconditionally, that is, regardless of the interrupt lock status before the CPU idle API was called.

    从低功耗模式唤醒之后,两个函数都将无条件地恢复中断,也就是说,不管 CPU 空闲 API 被调用之前的中断锁状态如何。

The Zephyr CPU Idling mechanism is detailed in CPU Idling.

Zephyr CPU 怠速机制在 CPU 怠速中有详细介绍。

Memory protection features 内存保护功能

This section describes certain aspects around memory protection features in Arm Cortex-M applications.

本节描述 Arm Cortex-M 应用程序中内存保护特性的某些方面。

User mode system calls 用户模式系统调用

User mode is supported in Cortex-M platforms that implement the standard (Arm) MPU or a similar core peripheral logic for memory access policy configuration and control, such as the NXP MPU for Kinetis platforms. (Currently, CONFIG_ARCH_HAS_USERSPACE is selected if CONFIG_ARM_MPU is enabled by the user in the board default Kconfig settings).

用户模式在 Cortex-M 平台中得到支持,这些平台实现了用于内存访问策略配置和控制的标准(Arm) MPU 或类似的核心外围逻辑,例如用于 Kinetis 平台的 NXP MPU。(目前,如果用户在板上默认的 Kconfig 设置中启用了 CONFIG_ARCH_HAS_USERSPACE,则选择 CONFIG_ARM_MPU)。

A thread performs a system call by triggering a (synchronous) SVC exception, where

线程通过触发(同步) SVC 异常执行系统调用,其中

  • up to 5 arguments are placed on registers R1 - R5

    最多 5 个参数放置在寄存器 R1-R5 上

  • system call ID is placed on register R6.

    系统调用 ID 放在寄存器 R6 上。

The SVC Handler will branch to the system call preparation logic, which will perform the following operations

SVC 处理程序将分支到系统调用准备逻辑,它将执行以下操作

  • switch the thread’s PSP to point to the beginning of the thread’s privileged stack area, optionally reprogramming the PSPLIM if stack limit checking is enabled

    切换线程的 PSP 指向线程的特权堆栈区域的开头,如果启用了堆栈限制检查,则可以选择重新编程 PSPLIM

  • modify CONTROL register to switch to privileged mode

    修改 CONTROL 寄存器以切换到特权模式

  • modify the return address in the SVC exception stack frame, so that after exception return the system call dispatcher is executed (in thread privileged mode)

    修改 SVC 异常堆栈框架中的返回地址,以便在异常返回后执行系统调用调度程序(在线程特权模式下)

Once the system call execution is completed the system call dispatcher will restore the user’s original PSP and PSPLIM and switch the CONTROL register back to unprivileged mode before returning back to the caller of the system call.

一旦系统调用执行完成,系统调用调度器将恢复用户的原始 PSP 和 PSPLIM,并将 CONTROL 寄存器切换回非特权模式,然后返回到系统调用的调用方。

System calls execute in thread mode and can be preempted by interrupts at any time. A thread may also be context-switched-out while doing a system call; the system call will resume as soon as the thread is switched-in again.

系统调用以线程模式执行,可以在任何时候被中断抢占。在执行系统调用时,线程也可能被上下文切换掉; 当线程再次切换进来时,系统调用将立即恢复。

The system call dispatcher executes at SVC priority, therefore it cannot be preempted by HW interrupts (with the exception of ZLIs), which may observe some additional interrupt latency if they occur during a system call preparation.

系统调用调度程序以 SVC 优先级执行,因此它不能被硬件中断(除了 ZLI)抢占,如果在系统调用准备期间发生硬件中断,它可能会观察到一些额外的中断延迟。

MPU-assisted stack overflow detection MPU 辅助的堆栈溢出检测

Cortex-M platforms with MPU may enable CONFIG_MPU_STACK_GUARD to enable the MPU-based stack overflow detection mechanism. The following points need to be considered when enabling the MPU stack guards

具有 MPU 的 Cortex-M 平台可以启用 CONFIG_MPU_STACK_GUARD 来启用基于 MPU 的堆栈溢出检测机制。启用 MPU 堆栈保护时需要考虑以下几点

  • stack overflows are triggering processor faults as soon as they occur

    堆栈溢出一旦发生就会触发处理器故障

  • the mechanism is essential for detecting stack overflows in supervisor threads, or user threads in privileged mode; stack overflows in threads in user mode will always be detected regardless of CONFIG_MPU_STACK_GUARD being set.

    该机制对于检测管理线程中的堆栈溢出或者特权模式下的用户线程是必不可少的; 无论设置 CONFIG_MPU_STACK_GUARD,总是会检测到用户模式下的线程中的堆栈溢出。

  • stack overflows are always detected, however, the mechanism does not guarantee that no memory corruption occurs when supervisor threads overflow their stack memory

    堆栈溢出总是被检测到,但是,这种机制并不能保证当监控线程溢出堆栈内存时不会发生内存损坏

  • CONFIG_MPU_STACK_GUARD will normally reserve one MPU region for programming the stack guard (in certain Arm v8-M configurations with CONFIG_MPU_GAP_FILLING enabled 2 MPU regions are required to implement the guard feature)

    CONFIG_MPU_STACK_GUARD 通常会保留一个 MPU 区域用于编写堆栈保护程序(在某些 Arm v8-M 配置中,要实现保护功能需要启用 CONFIG_MPU_GAP_FILING 的 2 个 MPU 区域)

  • MPU guards are re-programmed at every context-switch, adding a small overhead to the thread swap routine. Compared, however, to the CONFIG_BUILTIN_STACK_GUARD feature, no re-programming occurs during system calls.

    在每个上下文切换处都会重新编写 MPU 保护程序,这给线程交换例程增加了一点开销。但是,与 CONFIG_BUILTIN_STACK_GUARD 特性相比,在系统调用期间不会发生重新编程。

  • When CONFIG_HW_STACK_PROTECTION is enabled on Arm v8-M platforms the native stack limit checking mechanism is used by default instead of the MPU-based stack overflow detection mechanism; users may override this setting by manually enabling CONFIG_MPU_STACK_GUARD in these scenarios.

    在 ARM v8-M 平台上启用 CONFIG_HW_STACK_PROTECTION 时,默认情况下使用本机堆栈限制检查机制,而不是基于 MPU 的堆栈溢出检测机制; 在这些场景中,用户可以通过手动启用 CONFIG_MPU_STACK_GUARD 来覆盖这个设置。

Memory map and MPU considerations 内存映射和 MPU 注意事项

Fixed MPU regions 固定 MPU 区域

By default, when CONFIG_ARM_MPU is enabled a set of fixed MPU regions are programmed during system boot.

默认情况下,当启用 CONFIG_ARM_MPU 时,会在系统引导期间编写一组固定的 MPU 区域。

  • One MPU region programs the entire flash area as read-execute. User can override this setting by enabling CONFIG_MPU_ALLOW_FLASH_WRITE, which programs the flash with RWX permissions. If CONFIG_USERSPACE is enabled unprivileged access on the entire flash area is allowed.

    一个 MPU 区域将整个 flash 区域编程为读-执行。用户可以通过启用 CONFIG_MPU_ALLOW_FLASH_WRITE 来覆盖此设置,该设置使用 RWX 权限编写闪存程序。如果启用了 CONFIG_USERSPACE,则允许对整个 flash 区域进行非特权访问。

  • One MPU region programs the entire SRAM area with privileged-only RW permissions. That is, an MPU region is utilized to disallow execute permissions on SRAM. (An exception to this setting is when CONFIG_MPU_GAP_FILLING is disabled (Arm v8-M only); in that case no SRAM MPU programming is done so the access is determined by the default Arm memory map policies, allowing for privileged-only RWX permissions on SRAM).

    一个 MPU 区域使用仅有特权的 RW 权限对整个 SRAM 区域进行编程。也就是说,使用一个 MPU 区域来禁止对 SRAM 的执行权限。(这种设置的一个例外是当 CONFIG_MPU_GAP_FILING 被禁用时(仅 Arm v8-M) ; 在这种情况下,没有 SRAM MPU 编程,因此访问由默认 Arm 内存映射策略决定,允许 SRAM 上只有特权的 RWX 权限)。

  • All the memory regions defined in the devicetree with the compatible zephyr,memory-region and at least the property zephyr,memory-region-mpu defining the MPU permissions for the memory region. See the next section for more details.

    在设备树中定义的所有内存区域都具有兼容的 zephyr,memory-region 和至少属性 zephyr,memory-region-mpu,这些属性定义了内存区域的 MPU 权限。有关更多细节,请参见下一节。

The above MPU regions are defined in soc/arm/common/cortex_m/arm_mpu_regions.c. Alternative MPU configurations are allowed by enabling CONFIG_CPU_HAS_CUSTOM_FIXED_SOC_MPU_REGIONS. When enabled, this option signifies that the Cortex-M SoC will define and configure its own fixed MPU regions in the SoC definition.

上述 MPU 区域定义在 soc/arm/common/skin_m/arm_mpu_regions.c 中。可以通过启用 CONFIG_CPU_HAS_CUSTOM_FIXED_SOC_MPU_REGION 来允许其他 MPU 配置。如果启用,此选项表示 Cortex-M SoC 将在 SoC 定义中定义和配置自己的固定 MPU 区域。

Fixed MPU regions defined in devicetree

固定在设备树中定义的 MPU 区域

The user can define memory regions to be allocated and created in the linker script using nodes with the zephyr,memory-region devicetree compatible. When the property zephyr,memory-region-mpu is present in such a node, a new MPU region will be allocated and programmed during system boot.

用户可以使用与zephyr,memory-region 设备树兼容的节点在链接器脚本中定义要分配和创建的内存区域。当属性 zephyr,memory-region-mpu 出现在这样的节点中时,将在系统引导期间分配和编程一个新的 MPU 区域。

The property zephyr,memory-region-mpu is a string carrying the attributes for the MPU region. It is converted to a C token for use defining the attributes of the MPU region.

属性 zephyr,memory-region-mpu 是一个携带 MPU 区域属性的字符串。它被转换为 C 令牌,用于定义 MPU 区域的属性。

For example, to define a new non-cacheable memory region in devicetree:

例如,要在 devicetree 中定义一个新的不可缓存的内存区域:

sram_no_cache: memory@20300000 {
     compatible = "zephyr,memory-region", "mmio-sram";
     reg = <0x20300000 0x100000>;
     zephyr,memory-region = "SRAM_NO_CACHE";
     zephyr,memory-region-mpu = "RAM_NOCACHE";
};

This will automatically create a new MPU entry in soc/arm/common/cortex_m/arm_mpu_regions.c with the correct name, base, size and attributes gathered directly from the devicetree. See include/zephyr/linker/devicetree_regions.h for more details.

这将自动在 soc/arm/common/skin_m/arm_mpu_regions.c 中创建一个新的 MPU 条目,其中包含直接从设备树收集的正确名称、基础、大小和属性。有关更多细节,请参见 include/zephyr/linker/devicetree_regions.h。

Static MPU regions 静态 MPU 区域

Additional static MPU regions may be programmed once during system boot. These regions are required to enable certain features

额外的静态 MPU 区域可以在系统引导期间编程一次。这些区域是启用某些特性所必需的

  • a RX region to allow execution from SRAM, when CONFIG_ARCH_HAS_RAMFUNC_SUPPORT is enabled and users have defined functions to execute from SRAM.

    当启用 CONFIG_ARCH_HAS_RAMFUNC_SUPPORT 并且用户已经定义了从 SRAM 执行的函数时,允许从 SRAM 执行的 RX 区域。

  • a RX region for relocating text sections to SRAM, when CONFIG_CODE_DATA_RELOCATION_SRAM is enabled

    当启用 CONFIG_CODE_DATA_RELOCATION_SRAM 时,将文本部分重新定位到 SRAM 的 RX 区域

  • a no-cache region to allow for a none-cacheable SRAM area, when CONFIG_NOCACHE_MEMORY is enabled

    当 CONFIG_NOCACHE_MEMORY 被启用时,一个无缓存区域以允许无缓存的 SRAM 区域

  • a possibly unprivileged RW region for GCOV code coverage accounting area, when CONFIG_COVERAGE_GCOV is enabled

    当启用 CONFIG_COVERAGE_GCOV 时,GCOV 代码覆盖计数区域可能没有特权的 RW 区域

  • a no-access region to implement null pointer dereference detection, when CONFIG_NULL_POINTER_EXCEPTION_DETECTION_MPU is enabled

    当启用 CONFIG_NULL_POINTER_EXCEPTION_DETECTION_MPU 时,实现空指针解引用检测的无访问区域

The boundaries of these static MPU regions are derived from symbols exposed by the linker, in include/linker/linker-defs.h.

这些静态 MPU 区域的边界来源于链接器公开的符号,在 include/linker/linker-defs.h 中。

Dynamic MPU regions 动态 MPU 区域

Certain thread-specific MPU regions may be re-programmed dynamically, at each thread context switch:

在每个线程上下文切换时,某些特定于线程的 MPU 区域可能被动态地重新编程:

  • an unprivileged RW region for the current thread’s stack area (for user threads)

    当前线程的堆栈区域(用于用户线程)的非特权 RW 区域

  • a read-only region for the MPU stack guard

    MPU 堆栈保护的只读区域

  • unprivileged RW regions for the partitions of the current thread’s application memory domain.

    当前线程的应用程序内存域的分区的非特权 RW 区域。

Considerations 考虑因素

The number of available MPU regions for a Cortex-M platform is a limited resource. Most platforms have 8 MPU regions, while some Cortex-M33 or Cortex-M7 platforms may have up to 16 MPU regions. Therefore there is a relatively strict limitation on how many fixed, static and dynamic MPU regions may be programmed simultaneously. For platforms with 8 available MPU regions it might not be possible to enable all the aforementioned features that require MPU region programming. In most practical applications, however, only a certain set of features is required and 8 MPU regions are, in many cases, sufficient.

可用于 Cortex-M 平台的 MPU 区域的数量是一个有限的资源。大多数平台有 8 个 MPU 区域,而一些 Cortex-M33 或 Cortex-M7 平台可能有多达 16 个 MPU 区域。因此,有一个相对严格的限制,多少固定,静态和动态的MPU 区域可以同时编程。对于具有 8 个可用 MPU 区域的平台,可能无法启用需要 MPU 区域编程的所有上述特性。然而,在大多数实际应用中,只需要一组特定的特性,在许多情况下,8 个 MPU 区域就足够了。

In Arm v8-M processors the MPU architecture does not allow programmed MPU regions to overlap. CONFIG_MPU_GAP_FILLING controls whether the fixed MPU region covering the entire SRAM is programmed. When it does, a full SRAM area partitioning is required, in order to program the static and the dynamic MPU regions. This increases the total number of required MPU regions. When CONFIG_MPU_GAP_FILLING is not enabled the fixed MPU region covering the entire SRAM is not programmed, thus, the static and dynamic regions are simply programmed on top of the always-existing background region (full-SRAM partitioning is not required). Note, however, that the background SRAM region allows execution from SRAM, so when CONFIG_MPU_GAP_FILLING is not set Zephyr is not protected against attacks that attempt to execute malicious code from SRAM.

在 Arm v8-M 处理器中,MPU 架构不允许程序化的 MPU 区域重叠。CONFIG_MPU_GAP_FILING 控制覆盖整个 SRAM 的固定MPU 区域是否被编程。当它这样做,一个完整的 SRAM 区域分区是必要的,以编程的静态和动态的MPU 区域。这增加了所需的 MPU 区域的总数。当没有启用 CONFIG_MPU_GAP_FILING 时,覆盖整个 SRAM 的固定 MPU 区域没有被编程,因此,静态和动态区域只是简单地编程在总是存在的背景区域之上(不需要全 SRAM 分区)。然而,请注意,后台 SRAM 区域允许从 SRAM 执行,因此当 CONFIG_MPU_GAP_FILING 未设置时,Zephyr 不能抵御试图从 SRAM 执行恶意代码的攻击。

Floating point Services 浮点服务

Both unshared and shared FP registers mode are supported in Cortex-M (see Floating Point Services for more details).

Cortex-M 支持非共享和共享 FP 寄存器模式(有关更多细节,请参见浮点服务)。

When FPU support is enabled in the build (CONFIG_FPU is enabled), the sharing FP registers mode (CONFIG_FPU_SHARING) is enabled by default. This is done as some compiler configurations may activate a floating point context by generating FP instructions for any thread, regardless of whether floating point calculations are performed, and that context must be preserved when switching such threads in and out.

当构建中启用 FPU 支持时(启用 CONFIG_FPU) ,默认情况下启用共享 FP 寄存器模式(CONFIG_FPU_SHARING)。这是因为一些编译器配置可能通过为任何线程生成 FP 指令来激活浮点上下文,而不管是否执行浮点计算,并且在切换这些线程进出时必须保留该上下文。

The developers can still disable the FP sharing mode in their application projects, and switch to Unshared FP registers mode, if it is guaranteed that the image code does not generate FP instructions outside the single thread context that is allowed (and supposed) to do so.

开发人员仍然可以在他们的应用程序项目中禁用 FP 共享模式,并切换到非共享 FP 寄存器模式,前提是保证镜像代码不会在允许(和假定)这样做的单线程上下文之外生成 FP 指令。

Under FPU sharing mode, the callee-saved FPU registers are saved and restored in context-switch, if the corresponding threads have an active FP context. This adds some runtime overhead on the swap routine. In addition to the runtime overhead, the sharing FPU mode

在 FPU 共享模式下,如果相应的线程具有活动的 FP 上下文,则在上下文切换中保存和恢复被调用方保存的 FPU 寄存器。这会在交换例程上增加一些运行时开销。除了运行时开销之外,共享 FPU 模式

  • requires additional memory for each thread to save the callee-saved FP registers

    每个线程需要额外的内存来保存被调用方保存的 FP 寄存器

  • requires additional stack memory for each thread, to stack the caller-saved FP registers, upon exception entry, if an FP context is active. Note, however, that since lazy stacking is enabled, there is no runtime overhead of FP context stacking in regular interrupts (FP state preservation is only activated in the swap routine in PendSV interrupt).

    在异常输入时,如果 FP 上下文处于活动状态,则需要为每个线程添加额外的堆栈内存,以堆栈调用方保存的 FP 寄存器。但是请注意,由于启用了延迟堆栈,因此在常规中断中 FP 上下文堆栈没有运行时开销(FP 状态保持仅在 PendSV 中断的交换例程中被激活)。

Misc

Chain-loadable images 链式加载镜像

Cortex-M applications may either be standalone images or chain-loadable, for instance, by a bootloader. Application images chain-loadable by bootloaders (or other applications) normally occupy a specific area in the flash denoted as their code partition. CONFIG_USE_DT_CODE_PARTITION will ensure that a Zephyr chain-loadable image will be linked into its code partition, specified in DeviceTree.

Cortex-M 应用程序可以是独立的映像,也可以是链式加载,例如,通过引导加载程序。可由引导加载程序(或其他应用程序)链式加载的应用程序映像通常占据闪存中表示为其代码分区的特定区域。CONFIG_USE_DT_CODE_PARTITION 将确保可加载 Zephyr 链的映像将链接到它的代码分区(在 DeviceTree 中指定)。

HW initialization at boot 引导时的 HW 初始化

In order to boot properly, chain-loaded applications may require that the core Arm hardware registers and peripherals are initialized in their reset values. Enabling CONFIG_INIT_ARCH_HW_AT_BOOT Zephyr to force the initialization of the internal Cortex-M architectural state during boot to the reset values as specified by the corresponding Arm architecture manual.

为了正确引导,链式加载的应用程序可能需要核心 ARM 硬件寄存器和外围设备在其重置值中进行初始化。启用 CONFIG_INIT_ARCH_HW_AT_BOOT Zephyr 强制初始化内部 Cortex-M 架构状态,在引导到相应的 ARM 架构手册指定的重置值期间。

Software vector relaying 软件矢量中继

In Cortex-M platforms that implement the VTOR register (see CONFIG_CPU_CORTEX_M_HAS_VTOR), chain-loadable images relocate the Cortex-M vector table by updating the VTOR register with the offset of the image vector table.

在实现 VTOR 寄存器的 Cortex-M 平台中(参见 CONFIG_CPU_CORTEX_M_HAS_VTOR) ,可链加载的镜像通过更新 VTOR 寄存器和镜像矢量表的偏移量来重新定位 Cortex-M 矢量表。

Baseline Cortex-M platforms without VTOR register might not be able to relocate their vector table which remains at a fixed location. Therefore, a chain-loadable image will require an alternative way to route HW interrupts and system exceptions to its own vector table; this is achieved with software vector relaying.

没有 VTOR 寄存器的基线 Cortex-M 平台可能无法重新定位保持在固定位置的向量表。因此,一个链式加载的镜像将需要另一种方法来路由硬件中断和系统异常到它自己的向量表,这是通过软件向量中继实现的。

When a bootloader image enables CONFIG_SW_VECTOR_RELAY it is able to relay exceptions and interrupts based on a vector table pointer that is set by the chain-loadable application. The latter sets the CONFIG_SW_VECTOR_RELAY_CLIENT option to instruct the boot sequence to set the vector table pointer in SRAM so that the bootloader can forward the exceptions and interrupts to the chain-loadable image’s software vector table.

当引导加载程序映像启用 CONFIG_SW_VECTOR_RELAY 时,它能够基于链式加载应用程序设置的向量表指针中继异常和中断。后者设置 CONFIG_SW_VECTOR_RELAY_CLIENT 选项,指示引导序列在 SRAM 中设置矢量表指针,以便引导装载程序可以将异常和中断转发到链式加载映像的软件矢量表。

While this feature is intended for processors without VTOR register, it may also be used in Mainline Cortex-M platforms.

虽然这个特性是为没有 VTOR 寄存器的处理器设计的,但是它也可以在 Mainline Cortex-M 平台中使用。

Code relocation 代码重新定位

Cortex-M support the code relocation feature. When CONFIG_CODE_DATA_RELOCATION_SRAM is selected, Zephyr will relocate .text, data and .bss sections from the specified files and place it in SRAM. It is possible to relocate only parts of the code sections into SRAM, without relocating the whole image text and data sections. More details on the code relocation feature can be found in Code And Data Relocation.

Cortex-M支持代码重定位功能。当选择CONFIG_CODE_DATA_RELOCATION_SRAM时,Zephyr将重新定位指定文件中的.text、data和.bss部分,并将其置于SRAM中。可以只将部分代码部分重新定位到SRAM中,而不将整个镜像的文本和数据部分重新定位。关于代码重定位功能的更多细节可以在代码和数据重定位中找到。

Linking Cortex-M applications 连接 Cortex-M 应用程序

Most Cortex-M platforms make use of the default Cortex-M GCC linker script in include/arch/arm/aarch32/cortex-m/scripts/linked.ld, although it is possible for platforms to use a custom linker script as well.

大多数 Cortex-M 平台在 include/arch/arm/aarch32/Cortex-M/script/linked.ld 中使用默认的 Cortex-M GCC 连接器脚本,尽管平台也可以使用自定义连接器脚本。

CMSIS

Cortex-M CMSIS headers are hosted in a standalone module repository: zephyrproject-rtos/cmsis.

Cortex-M CMSIS 头部托管在一个独立的模块存储库中: zephyrproject-rtos/CMSIS。

CONFIG_CPU_CORTEX_M selects CONFIG_HAS_CMSIS_CORE to signify that CMSIS headers are available for all supported Cortex-M variants.

CONFIG_CPU_CORTEX_M 选择 CONFIG_HAS_CMSIS_CORE 以表示 CMSIS 头可用于所有支持的 Cortex-M 变体。

Testing 测试

A list of unit tests for the Cortex-M porting and miscellaneous features is present in tests/arch/arm/. The tests suites are continuously extended and new test suites are added, in an effort to increase the coverage of the Cortex-M architecture support in Zephyr.

Cortex-M 移植和其他功能的单元测试列表在tests/arch/arm/。测试套件不断扩展,并添加了新的测试套件,以努力增加在 Zephyr 的 Cortex-M 架构支持的覆盖面。

QEMU

We use QEMU to verify the implemented features of the Cortex-M architecture port in Zephyr. Adequate coverage is achieved by defining and utilizing a list of QEMU targets, each with a specific architecture variant and Arm peripheral support list.

我们使用 QEMU 来验证在 Zephyr 实现的 Cortex-M 架构端口的特性。通过定义和利用 QEMU 目标列表来实现足够的覆盖率,每个目标都具有特定的体系结构变体和 ARM 外围支持列表。

Peripheral and Hardware Emulators 外设和硬件仿真器

Overview 概述

Zephyr supports a simple emulator framework to support testing of drivers without requiring real hardware.

Zephyr 支持一个简单的模拟器框架来支持驱动程序的测试,而不需要真正的硬件。

Emulators are used to emulate hardware devices, to support testing of various subsystems. For example, it is possible to write an emulator for an I2C compass such that it appears on the I2C bus and can be used just like a real hardware device.

仿真器用于仿真硬件设备,以支持各种子系统的测试。例如,可以为 I2C 指南针编写一个仿真器,使其出现在 I2C 总线上,并且可以像真正的硬件设备一样使用。

Emulators often implement special features for testing. For example a compass may support returning bogus data if the I2C bus speed is too high, or may return invalid measurements if calibration has not yet been completed. This allows for testing that high-level code can handle these situations correctly. Test coverage can therefore approach 100% if all failure conditions are emulated.

仿真器经常为测试实现特殊功能。例如,如果 I2C 总线速度太高,罗盘可能支持返回假数据,或者如果校准尚未完成,可能返回无效的测量值。这样就可以测试高层代码是否能正确处理这些情况。因此,如果所有的故障条件被模拟,测试覆盖率可以接近 100%。

Concept 概念

The diagram below shows application code / high-level tests at the top. This is the ultimate application we want to run.

下面的图表显示了顶部的应用程序代码/高级测试。这是我们想要运行的最终应用程序。

Emulator architecture showing tests, emulators and drivers

Below that are peripheral drivers, such as the AT24 EEPROM driver. We can test peripheral drivers using an emulation driver connected via a native_posix I2C controller/emulator which passes I2C traffic from the AT24 driver to the AT24 simulator.

下面是外围驱动,如 AT24 EEPROM 驱动。我们可以使用一个通过 native_posix I2C 控制器/模拟器连接的仿真驱动器来测试外围驱动器,该驱动器将 I2C 流量从 AT24 驱动器传递给 AT24 模拟器。

Separately we can test the STM32 and NXP I2C drivers on real hardware using API tests. These require some sort of device attached to the bus, but with this, we can validate much of the driver functionality.

我们可以分别使用 API 测试在实际硬件上测试 STM32 和 NXP I2C 驱动程序。这需要一些附加到总线上的设备,但是通过这个,我们可以验证许多驱动程序功能。

Putting the two together, we can test the application and peripheral code entirely on native_posix. Since we know that the I2C driver on the real hardware works, we should expect the application and peripheral drivers to work on the real hardware also.

将这两者结合起来,我们可以完全在 national_posix 上测试应用程序和外围代码。既然我们知道 I2C 驱动程序在真实的硬件上工作,我们就应该期望应用程序和外围驱动程序也在真实的硬件上工作。

Using the above framework we can test an entire application (e.g. Embedded Controller) on native_posix using emulators for all non-chip drivers:

使用上述框架,我们可以使用针对所有非芯片驱动程序的模拟器,在 native_posix 上测试整个应用程序(例如嵌入式控制器) :

Example system, using emulators to implement a PC EC

The ‘real’ code is shown in green. The Zephyr emulation-framework code is shown in yellow. The blue boxes are the extra code we have to write to emulate the peripherals.

“真正的”代码显示为绿色。Zephyr 仿真框架代码显示为黄色。蓝色框是我们必须编写来模拟外围设备的额外代码。

With this approach we can:

通过这种方法,我们可以:

  • Write individual tests for each driver (green), covering all failure modes, error conditions, etc.

    为每个驱动程序编写单独的测试(绿色) ,包括所有的故障模式、错误条件等。

  • Ensure 100% test coverage for drivers (green)

    确保 100% 的驾驶员测试覆盖率(绿色)

  • Write tests for combinations of drivers, such as GPIOs provided by an I2C GPIO expander driver talking over an I2C bus, with the GPIOs controlling a charger. All of this can work in the emulated environment or on real hardware.

    编写驱动程序组合的测试,例如由 I2C GPIO 扩展驱动程序通过 I2C 总线通话提供的 GPIO,由 GPIO 控制充电器。所有这些都可以在仿真环境或真实硬件上工作。

  • Write a complex application that ties together all of these pieces and runs on native_posix. We can develop on a host, use source-level debugging, etc.

    编写一个复杂的应用程序,将所有这些部分绑定在一起并运行在 native_posix 上。我们可以在主机上进行开发,使用源代码级调试等等。

  • Transfer the application to any board which provides the required features (e.g. I2C, enough GPIOs), by adding Kconfig and devicetree fragments.

    通过添加 Kconfig 和 devicetree 片段,将应用程序转移到提供所需特性的任何板块(例如 I2C,足够的 GPIO)。

Available emulators 可用的模拟器

Zephyr includes the following emulators:

Zephyr 包括以下模拟器:

  • EEPROM, which uses a file as the EEPROM contents

    EEPROM,它使用一个文件作为 EEPROM 的内容

  • I2C emulator driver, allowing drivers to be connected to an emulator so that tests can be performed without access to the real hardware

    I2C 模拟器驱动程序,允许将驱动程序连接到模拟器,这样就可以在无需访问实际硬件的情况下执行测试

  • SPI emulator driver, which does the same for SPI

    SPI 模拟器驱动程序,它对 SPI 执行相同的操作

  • eSPI emulator driver, which does the same for eSPI. The emulator is being developed to support more functionalities.

    ESPI 模拟器驱动程序,对 eSPI 也是如此。该模拟器正在开发中,以支持更多的功能。

  • CAN loopback driver

    CAN 环回驱动程序

A GPIO emulator is planned but is not yet complete.

一个 GPIO 模拟器已经计划好了,但还没有完成。

Samples 样本

Here are some examples present in Zephyr:

以下是 Zephyr 中的一些例子:

  1. Bosch BMI160 sensor driver connected via both I2C and SPI to an emulator:

    博世 BMI160 传感器驱动程序通过 I2C 和 SPI 连接到仿真器:

    west build -b native_posix tests/drivers/sensor/accel/
    
  2. Simple test of the EEPROM emulator:

    EEPROM 模拟器的简单测试:

    west build -b native_posix tests/drivers/eeprom
    
  3. The same test has a second EEPROM which is an Atmel AT24 EEPROM driver connected via I2C an emulator:

    同样的测试有一个第二个 EEPROM,它是一个 Atmel AT24 EEPROM 驱动程序,通过 I2C 连接一个仿真器:

    west build -b native_posix tests/drivers/eeprom
    

外设 API

Pin Control

This is a high-level guide to pin control. See Pin Control API for API reference material.

这是一个 pin 控制的高级指南。请参阅 pin 控制 API 参考资料。

Introduction 简介

The hardware blocks that control pin multiplexing and pin configuration parameters such as pin direction, pull-up/down resistors, etc. are named pin controllers. The pin controller’s main users are SoC hardware peripherals, since the controller enables exposing peripheral signals, like for example, map I2C0 SDA signal to pin PX0. Not only that, but it usually allows configuring certain pin settings that are necessary for the correct functioning of a peripheral, for example, the slew-rate depending on the operating frequency. The available configuration options are vendor/SoC dependent and can range from simple pull-up/down options to more advanced settings such as debouncing, low-power modes, etc.

控制引脚复用和引脚配置参数(如引脚方向、上拉/下拉电阻等)的硬件块被称为引脚控制器。引脚控制器的主要用户是SoC的硬件外设,因为控制器可以外发外设信号,例如,将I2C0的SDA信号映射到引脚PX0。不仅如此,它通常还允许配置外设正确运行所需的某些引脚设置,例如,根据工作频率的转速。可用的配置选项与供应商/SoC有关,范围从简单的上拉/下拉选项到更高级的设置,如去抖、低功耗模式等。

The way pin control is implemented in hardware is vendor/SoC specific. It is common to find a centralized approach, that is, all pin configuration parameters are controlled by a single hardware block (typically named pinmux), including signal mapping. Fig. 33 illustrates this approach. PX0 can be mapped to UART0_TX, I2C0_SCK or SPI0_MOSI depending on the AF control bits. Other configuration parameters such as pull-up/down are controlled in the same block via CONFIG bits. This model is used by several SoC families, such as many from NXP and STM32.

引脚控制在硬件中的实现方式由供应商/SoC决定。常见的是集中式的方法,即所有的引脚配置参数都由一个硬件块(通常称为pinmux)控制,包括信号映射。图33说明了这种方法。PX0可以被映射到UART0_TX、I2C0_SCK或SPI0_MOSI,取决于AF控制位。其他配置参数,如上拉/下拉,在同一块中通过CONFIG位控制。这种模式被一些SoC系列所采用,例如恩智浦和STM32的许多产品。

hw-cent-control.svg

Fig. 33 Example of pin control centralized into a single per-pin block

图 33 引脚控制集中到一个单引脚块的例子

Other vendors/SoCs use a distributed approach. In such case, the pin mapping and configuration are controlled by multiple hardware blocks. Fig. 34 illustrates a distributed approach where pin mapping is controlled by peripherals, such as in Nordic nRF SoCs.

其他供应商/SoCs使用分布式方法。在这种情况下,引脚映射和配置是由多个硬件块控制的。图34说明了一种分布式方法,其中引脚映射由外设控制,例如在Nordic nRF SoCs中。

hw-dist-control.svg

Fig. 34 Example pin control distributed between peripheral registers and per-pin block

图 34 分布在外围寄存器和每个引脚块之间的引脚控制实例

From a user perspective, there is no difference in pin controller usage regardless of the hardware implementation: a user will always apply a state. The only difference lies in the driver implementation. In general, implementing a pin controller driver for a hardware that uses a distributed approach requires more effort, since the driver needs to gather knowledge of peripheral dependent registers.

从用户的角度来看,无论硬件实现如何,引脚控制器的使用都没有区别:用户总是会应用一个状态。唯一的区别在于驱动程序的实现。一般来说,为使用分布式方法的硬件实现一个引脚控制器驱动需要更多的努力,因为驱动需要收集与外设相关的寄存器的知识。

Pin control vs. GPIO 引脚控制 VS GPIO

Some functionality covered by a pin controller driver overlaps with GPIO drivers. For example, pull-up/down resistors can usually be enabled by both the pin control driver and the GPIO driver. In Zephyr context, the pin control driver purpose is to perform peripheral signal multiplexing and configuration of other pin parameters required for the correct operation of that peripheral. Therefore, the main users of the pin control driver are SoC peripherals. In contrast, GPIO drivers are for general purpose control of a pin, that is, when its logic level is read or controlled manually.

引脚控制器驱动器涵盖的一些功能与GPIO驱动器重叠。例如,上拉/下拉电阻通常可以由引脚控制驱动器和GPIO驱动器共同启用。在Zephyr背景下,引脚控制驱动器的目的是执行外设信号复用和配置该外设正确运行所需的其他引脚参数。因此,引脚控制驱动器的主要用户是SoC外围设备。相比之下,GPIO驱动器是用于对一个引脚的通用控制,即当其逻辑电平被读取或手动控制时。

State model

For a device driver to operate correctly, a certain pin configuration needs to be applied. Some device drivers require a static configuration, usually set up at initialization time. Others need to change the configuration at runtime depending on the operating conditions, for example, to enable a low-power mode when suspending the device. Such requirements are modeled using states, a concept that has been adapted from the one in the Linux kernel. Each device driver owns a set of states. Each state has a unique name and contains a full pin configuration set (see Table 38). This effectively means that states are independent of each other, so they do not need to be applied in any specific order. Another advantage of the state model is that it isolates device drivers from pin configuration.

为了使设备驱动程序正确运行,需要应用某种引脚配置。有些设备驱动程序需要静态配置,通常在初始化时设置。其他人则需要根据操作条件在运行时更改配置,例如,在挂起设备时启用低功耗模式。这些需求是使用状态建模的,这个概念是从 Linux 内核中的状态改编而来的。每个设备驱动程序拥有一组状态。每个状态都有一个惟一的名称,并包含一个完整的引脚配置集(参见表 38)。这实际上意味着状态之间是相互独立的,因此它们不需要以任何特定的顺序应用。状态模型的另一个优点是它将设备驱动程序与引脚配置隔离开来。

  UART0 peripheral
UART0 外围设备
   
default state
默认状态
  sleep state
睡眠状态
 
TX Pin: PA0
Pull: NONE
Low Power: NO
TX Pin: PA0
Pull: NONE
Low Power: YES
RX Pin: PA1
Pull: UP
Low Power: NO
RX Pin: PA1
Pull: NONE
Low Power: YES

Standard states 标准状态

The name assigned to pin control states or the number of them is up to the device driver requirements. In many cases a single state applied at initialization time will be sufficient, but in some other cases more will be required. In order to make things consistent, a naming convention has been established for the most common use cases. Table 39 details the standardized states and its purpose.

分配给引脚控制状态的名称或控制状态的数量取决于设备驱动程序的要求。在许多情况下,在初始化时应用单个状态就足够了,但在其他情况下需要更多状态。为了保持一致性,我们为最常见的用例建立了一个变数命名原则。表 39 详细说明了标准化状态及其目的。

State Identifier Purpose
default PINCTRL_STATE_DEFAULT State of the pins when the device is in operational state
当设备处于工作状态时引脚的状态
sleep PINCTRL_STATE_SLEEP State of the pins when the device is in low power or sleep modes
当设备处于低功耗或睡眠模式时引脚的状态

Note that other standard states could be introduced in the future.

请注意,将来可能会引入其他标准状态。

Custom states

Some device drivers may require using custom states beyond the standard ones. To achieve that, the device driver needs to have in its scope definitions for the custom state identifiers named as PINCTRL_STATE_{STATE_NAME}, where {STATE_NAME} is the capitalized state name. For example, if mystate has to be supported, a definition named PINCTRL_STATE_MYSTATE needs to be in the driver’s scope.

有些设备驱动程序可能需要使用超出标准状态的自定义状态。要实现这一点,设备驱动程序需要在其范围定义中包含名为 PINCTRL_STATE_{STATE_NAME}的自定义状态标识符,其中{STATE_NAME}是大写的状态名称。例如,如果必须支持 mystate,则需要在驱动程序的作用域中包含名为 PINCTRL_STATE_MYSTATE 的定义。

Note 注意

It is important that custom state identifiers start from PINCTRL_STATE_PRIV_START

自定义状态标识符必须从 PINCTRL_STATE_PRIV_START 开始

If custom states need to be accessed from outside the driver, for example to perform dynamic pin control, custom identifiers should be placed in a header that is publicly accessible.

如果需要从驱动程序外部访问自定义状态,例如执行动态引脚控制,则应将自定义标识符放在可公开访问的标头中。

Skipping states

In most situations, the states defined in Devicetree will be the ones used in the compiled firmware. However, there are some cases where certain states will be conditionally used depending on a compilation flag. A typical case is the sleep state. This state is only used in practice if CONFIG_PM_DEVICE is enabled. If a firmware variant without device power management is needed, one should in theory remove the sleep state from Devicetree to not waste ROM space storing such unused state.

在大多数情况下,在 Devicetree 定义的状态将是编译的固件中使用的状态。但是,在某些情况下,将根据编译标志有条件地使用某些状态。一个典型的例子是sleep状态。只有在启用了 CONFIG_PM_DEVICE 的情况下,才会在实际中使用这种状态。如果需要一个没有设备电源管理的固件变体,理论上应该从 Devicetree 移除sleep状态,以避免浪费存储这种未使用状态的 ROM 空间。

States can be skipped by the pinctrl Devicetree macros if a definition named PINCTRL_SKIP_{STATE_NAME} expanding to 1 is present when pin control configuration is defined. In case of the sleep state, the pinctrl API already provides such definition conditional to the availability of device power management:

如果在定义管脚控制配置时存在扩展为 1 的名为PINCTRL_SKIP_{STATE_NAME}的定义,则 pinctrl Devicetree 宏可以跳过状态。对于睡眠状态,pinctrl API 已经根据设备电源管理的可用性提供了这样的定义:

#ifndef CONFIG_PM_DEVICE
/** If device power management is not enabled, "sleep" state will be ignored. */
#define PINCTRL_SKIP_SLEEP 1
#endif

Dynamic pin control 动态管脚控制

Dynamic pin control refers to the capability of changing pin configuration at runtime. This feature can be useful in situations where the same firmware needs to run onto slightly different boards, each having a peripheral routed at a different set of pins. This feature can be enabled by setting CONFIG_PINCTRL_DYNAMIC.

动态引脚控制是指在运行时改变引脚配置的能力。这个特性在同一固件需要运行在稍微不同的电路板上的情况下非常有用,每个电路板都有一个外设路由到不同的引脚集。可以通过设置 CONFIG_PINCTRL_DYNAMIC 来启用此特性。

Note 注意

Dynamic pin control should only be used on devices that have not been initialized. Changing pin configurations while a device is operating may lead to unexpected behavior. Since Zephyr does not support device de-initialization yet, this functionality should only be used during early boot stages.

动态引脚控制应该只用于尚未初始化的设备。在设备运行时更改引脚配置可能导致意外行为。由于 Zephyr 还不支持设备反初始化,因此此功能只能在早期引导阶段使用。

One of the effects of enabling dynamic pin control is that pinctrl_dev_config will be stored in RAM instead of ROM (not states or pin configurations, though). The user can then use pinctrl_update_states() to update the states stored in pinctrl_dev_config with a new set. This effectively means that the device driver will apply the pin configurations stored in the updated states when it applies a state.

启用动态引脚控制的效果之一是,pinctrl_dev_config 将存储在 RAM 中,而不是 ROM 中(但不是状态或引脚配置)。然后,用户可以使用 pinctrl_update_States()用一个新的集合更新存储在 pinctrl_dev_config 中的状态。这实际上意味着设备驱动程序在应用状态时将应用存储在更新状态中的引脚配置。

Devicetree representation 设备树表示

Because Devicetree is meant to describe hardware, it is the natural choice when it comes to storing pin control configuration. In the following sections you will find an overview on how states and pin configurations are represented in Devicetree.

因为 Devicetree 是用来描述硬件的,所以它是存储引脚控制配置的自然选择。在下面的章节中,你会发现一个概述,关于如何状态和引脚配置表示在 Devicetree。

States

Given a device, each of its pin control state is represented in Devicetree by pinctrl-N properties, being N the state index starting from zero. The pinctrl-names property is then used to assign a unique identifier for each state property by index, for example, pinctrl-names list entry 0 is the name for pinctrl-0.

给定一个设备,它的每个引脚控制状态在Devicetree中由pinctrl-N属性表示,N是指从0开始的状态索引。然后,pinctrl-names属性被用来按索引为每个状态属性分配一个唯一的标识符,例如,pinctrl-names列表条目0是pinctrl-0的名称。

periph0: periph@0 {
    ...
    /* state 0 ("default") */
    pinctrl-0 = <...>;
    ...
    /* state N ("mystate") */
    pinctrl-N = <...>;
    /* names for state 0 up to state N */
    pinctrl-names = "default", ..., "mystate";
    ...
};

Pin configuration 引脚配置

There are multiple ways to represent the pin configurations in Devicetree. However, all end up encoding the same information: the pin multiplexing and the pin configuration parameters. For example, UART_RX is mapped to PX0 and pull-up is enabled. The representation choice largely depends on each vendor/SoC, so the Devicetree binding files for the pin control drivers are the best place to look for details.

在Devicetree中,有多种方式来表示引脚配置。然而,所有的编码最终都是相同的信息:引脚复用和引脚配置参数。例如,UART_RX被映射到PX0并启用上拉功能。表示方法的选择在很大程度上取决于每个供应商/SoC,所以引脚控制驱动器的Devicetree绑定文件是查找细节的最好地方。

A popular and versatile option is shown in the example below. One of the advantages of this choice is the grouping capability based on shared pin configuration. This allows to reduce the verbosity of the pin control definitions. Another advantage is that the pin configuration parameters for a particular state are enclosed in a single Devicetree node.

下面的例子显示了一个流行的多功能选择。这种选择的优点之一是基于共享引脚配置的分组能力。这可以减少引脚控制定义的繁琐程度。另一个优点是,一个特定状态的引脚配置参数被封闭在一个单一的Devicetree节点中。

/* board.dts */
#include "board-pinctrl.dtsi"

&periph0 {
    pinctrl-0 = <&periph0_default>;
    pinctrl-names = "default";
};
/* vnd-soc-pkgxx.h
 * File with valid mappings for a specific package (may be autogenerated).
 * 特定包的有效映射的文件(可能是自动生成的)。
 * This file is optional, but recommended.
 */
...
#define PERIPH0_SIGA_PX0 VNDSOC_PIN(X, 0, MUX0)
#define PERIPH0_SIGB_PY7 VNDSOC_PIN(Y, 7, MUX4)
#define PERIPH0_SIGC_PZ1 VNDSOC_PIN(Z, 1, MUX2)
...
/* board-pinctrl.dtsi */
#include <vnd-soc-pkgxx.h>

&pinctrl {
    /* Node with pin configuration for default state */
    periph0_default: periph0_default {
        group1 {
            /* Mappings: PERIPH0_SIGA -> PX0, PERIPH0_SIGC -> PZ1 */
            pinmux = <PERIPH0_SIGA_PX0>, <PERIPH0_SIGC_PZ1>;
            /* Pins PX0 and PZ1 have pull-up enabled */
            bias-pull-up;
        };
        ...
        groupN {
            /* Mappings: PERIPH0_SIGB -> PY7 */
            pinmux = <PERIPH0_SIGB_PY7>;
        };
    };
};

Another popular model is based on having a node for each pin configuration and state. While this model may lead to shorter board pin control files, it also requires to have one node for each pin mapping and state, since in general, nodes can not be re-used for multiple states. This method is discouraged if autogeneration is not an option.

另一种流行的模式是基于每个引脚配置和状态都有一个节点。虽然这种模式可以缩短电路板引脚控制文件,但它也要求每个引脚映射和状态都有一个节点,因为一般来说,节点不能重复使用于多个状态。如果不能选择自动生成,则不建议使用此方法。

Note 注意

Because all Devicetree information is parsed into a C header, it is important to make sure its size is kept to a minimum. For this reason it is important to prefix pre-generated nodes with /omit-if-no-ref/. This prefix makes sure that the node is discarded when not used.

因为所有 Devicetree 信息都被解析为 C 头,所以确保它的大小保持在最小非常重要。出于这个原因,在预生成的节点前面加上/omit-if-no-ref/非常重要。此前缀确保在不使用时丢弃节点。

/* board.dts */
#include "board-pinctrl.dtsi"

&periph0 {
    pinctrl-0 = <&periph0_siga_px0_default &periph0_sigb_py7_default
                 &periph0_sigc_pz1_default>;
    pinctrl-names = "default";
};
/* vnd-soc-pkgxx.dtsi
 * File with valid nodes for a specific package (may be autogenerated).
 * This file is optional, but recommended.
 */

&pinctrl {
    /* Mapping for PERIPH0_SIGA -> PX0, to be used for default state */
    /omit-if-no-ref/ periph0_siga_px0_default: periph0_siga_px0_default {
        pinmux = <VNDSOC_PIN(X, 0, MUX0)>;
    };

    /* Mapping for PERIPH0_SIGB -> PY7, to be used for default state */
    /omit-if-no-ref/ periph0_sigb_py7_default: periph0_sigb_py7_default {
        pinmux = <VNDSOC_PIN(Y, 7, MUX4)>;
    };

    /* Mapping for PERIPH0_SIGC -> PZ1, to be used for default state */
    /omit-if-no-ref/ periph0_sigc_pz1_default: periph0_sigc_pz1_default {
        pinmux = <VNDSOC_PIN(Z, 1, MUX2)>;
    };
};
/* board-pinctrl.dts */
#include <vnd-soc-pkgxx.dtsi>

/* Enable pull-up for PX0 (default state) */
&periph0_siga_px0_default {
    bias-pull-up;
};

/* Enable pull-up for PZ1 (default state) */
&periph0_sigc_pz1_default {
    bias-pull-up;
};

Note 注意

It is discouraged to add pin configuration defaults in pre-defined nodes. In general, pin configurations depend on the board design or on the peripheral working conditions, so the decision should be made by the board. For example, enabling a pull-up by default may not always be desired because the board already has one or because its value depends on the operating bus speed. Another downside of defaults is that user may not be aware of them, for example:

不鼓励在预定义的节点中增加引脚配置的默认值。一般来说,引脚配置取决于电路板的设计或外设的工作条件,所以应该由电路板来决定。例如,默认情况下启用上拉可能并不总是需要的,因为电路板已经有一个,或者它的值取决于工作总线速度。默认值的另一个缺点是用户可能不知道它们,例如:

/* not evident that "periph0_siga_px0_default" also implies "bias-pull-up" */
/omit-if-no-ref/ periph0_siga_px0_default: periph0_siga_px0_default {
    pinmux = <VNDSOC_PIN(X, 0, MUX0)>;
    bias-pull-up;
};

Implementation guidelines 实现指引

Pin control drivers

Pin control drivers need to implement a single function: pinctrl_configure_pins(). This function receives an array of pin configurations that need to be applied. Furthermore, if CONFIG_PINCTRL_STORE_REG is set, it also receives the associated device register address for the given pins. This information may be required by some drivers to perform device specific actions.

引脚控制驱动器需要实现一个函数:pinctrl_configure_pins()。这个函数接收一个需要应用的引脚配置数组。此外,如果CONFIG_PINCTRL_STORE_REG被设置,它还会接收给定引脚的相关设备寄存器地址。一些驱动程序可能需要这些信息来执行特定的设备操作。

The pin configuration is stored in an opaque type that is vendor/SoC dependent: pinctrl_soc_pin_t. This type needs to be defined in a header named pinctrl_soc.h file that is in the Zephyr’s include path. It can range from a simple integer value to a struct with multiple fields. pinctrl_soc.h also needs to define a macro named Z_PINCTRL_STATE_PINS_INIT that accepts two arguments: a node identifier and a property name (pinctrl-N). With this information the macro needs to define an initializer for all pin configurations contained within the pinctrl-N property of the given node.

引脚配置被存储在一个不透明的类型中,该类型与供应商/SoC有关:pinctrl_soc_pin_t。这个类型需要在Zephyr的包含路径中的名为pinctrl_soc.h的头文件中定义。它的范围可以从简单的整数值到具有多个字段的结构。pinctrl_soc.h还需要定义一个名为Z_PINCTRL_STATE_PINS_INIT的宏,它接受两个参数:一个节点标识符和一个属性名称(pinctrl-N)。有了这些信息,该宏需要为给定节点的pinctrl-N属性中包含的所有引脚配置定义一个初始化器。

Regarding Devicetree pin configuration representation, vendors can decide which option is better for their devices. However, the following guidelines should be followed:

关于 Devicetree 管脚配置表示,供应商可以决定哪个选项更适合他们的设备。不过,应遵循以下指引:

  • Use pinctrl-N (N=0, 1, …) and pinctrl-names properties to define pin control states. These properties are defined in dts/bindings/pinctrl/pinctrl-device.yaml.

    使用 pinctrl-N (N = 0,1,…)和 pinctrl-name 属性来定义管脚控制状态,这些属性在 dts/bindings/pinctrl/pinctrl-device.yaml 中定义。

  • Use standard pin configuration properties as defined in dts/bindings/pinctrl/pincfg-node.yaml or dts/bindings/pinctrl/pincfg-node-group.yaml.

    使用 dts/bindings/pinctrl/pincfg-node.yaml 或 dts/bindings/pinctrl/pincfg-node-group.yaml 中定义的标准管脚配置属性。

Representations not following these guidelines may be accepted if they are already used by the same vendor in other operating systems, e.g. Linux.

如果不遵循这些准则的表示已经在其他操作系统(例如 Linux)中被同一供应商使用,则可以接受这些表示。

Device drivers 设备驱动程序

In this section you will find some tips on how a device driver should use the pinctrl API to successfully configure the pins it needs.

在本节中,您将找到一些关于设备驱动程序如何使用 pinctrl API 成功配置所需引脚的提示。

The device compatible needs to be modified in the corresponding binding so that the pinctrl-device.yaml is included. For example:

需要在相应的绑定中修改设备兼容性,以便包含 pinctrl-device.yaml:

include: [base.yaml, pinctrl-device.yaml]

This file is needed to add pinctrl-N and pinctrl-names properties to the device.

需要使用此文件向设备添加 pinctrl-N 和 pinctrl-name 属性。

From a device driver perspective there are two steps that need to be performed to be able to use the pinctrl API. First, the pin control configuration needs to be defined. This includes all states and pins. PINCTRL_DT_DEFINE or PINCTRL_DT_INST_DEFINE macros should be used for this purpose. Second, a reference to the device instance pinctrl_dev_config needs to be stored, since it is required to later use the API. This can be achieved using the PINCTRL_DT_DEV_CONFIG_GET and PINCTRL_DT_INST_DEV_CONFIG_GET macros.

从设备驱动程序的角度来看,有两个步骤需要执行,以便能够使用pinctrl API。首先,需要定义引脚控制配置。这包括所有的状态和引脚。为此应使用PINCTRL_DT_DEFINE或PINCTRL_DT_INST_DEFINE宏。第二,需要存储对设备实例pinctrl_dev_config的引用,因为以后使用API时需要它。这可以通过使用 PINCTRL_DT_DEV_CONFIG_GET 和 PINCTRL_DT_INST_DEV_CONFIG_GET 宏来实现。

It is worth to note that the only relationship between a device and its associated pin control configuration is based on variable naming conventions. The way an instance of pinctrl_dev_config is named for a corresponding device instance allows to later obtain a reference to it given the device’s Devicetree node identifier. This allows to minimize ROM usage, since only devices requiring pin control will own a reference to a pin control configuration.

值得注意的是,一个设备和它相关的引脚控制配置之间的唯一关系是基于变量的命名惯例。pinctrl_dev_config 实例根据相应的设备实例命名的方式允许以后根据设备的 Devicetree 节点标识符获取对它的引用。这可以最大限度地减少ROM的使用,因为只有需要引脚控制的设备才拥有对引脚控制配置的引用。

Once the driver has defined the pin control configuration and kept a reference to it, it is ready to use the API. The most common way to apply a state is by using pinctrl_apply_state(). It is also possible to use the lower level function pinctrl_apply_state_direct() to skip state lookup if it is cached in advance (e.g. at init time). Since state lookup time is expected to be fast, it is recommended to use pinctrl_apply_state().

一旦驱动程序定义了引脚控制配置并保留了对它的引用,它就可以使用API了。应用状态的最常见方式是使用pinctrl_apply_state()。也可以使用较低级别的函数pinctrl_apply_state_direct()来跳过状态的查找,如果它是提前缓存的(例如在初始化时)。由于状态查找的时间预计会很快,所以建议使用pinctrl_apply_state()。

The example below contains a complete example of a device driver that uses the pinctrl API.

下面的示例包含使用 pinctrl API 的设备驱动程序的完整示例。

/* A driver for the "mydev" compatible device */
#define DT_DRV_COMPAT mydev

...
#include <zephyr/drivers/pinctrl.h>
...

struct mydev_config {
    ...
    /* Reference to mydev pinctrl configuration */
    const struct pinctrl_dev_config *pcfg;
    ...
};

...

static int mydev_init(const struct device *dev)
{
    const struct mydev_config *config = dev->config;
    int ret;
    ...
    /* Select "default" state at initialization time */
    ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
    if (ret < 0) {
        return ret;
    }
    ...
}

#define MYDEV_DEFINE(i)                                                    \
    /* Define all pinctrl configuration for instance "i" */                \
    PINCTRL_DT_INST_DEFINE(i);                                             \
    ...                                                                    \
    static const struct mydev_config mydev_config_##i = {                  \
        ...                                                                \
        /* Keep a ref. to the pinctrl configuration for instance "i" */    \
        .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(i),                         \
        ...                                                                \
    };                                                                     \
    ...                                                                    \
                                                                           \
    DEVICE_DT_INST_DEFINE(i, mydev_init, NULL, &mydev_data##i,             \
                          &mydev_config##i, ...);

DT_INST_FOREACH_STATUS_OKAY(MYDEV_DEFINE)

Board Porting Guide 主板移植指南

To add Zephyr support for a new board, you at least need a board directory with various files in it. Files in the board directory inherit support for at least one SoC and all of its features. Therefore, Zephyr must support your SoC as well.

要添加对新主板的 Zephyr 支持,您至少需要一个包含各种文件的主板目录。主板目录中的文件继承了对至少一个 SoC 及其所有特性的支持。因此,Zephyr也必须支持您的 SoC。

Boards, SoCs, etc.

Zephyr’s hardware support hierarchy has these layers, from most to least specific:

Zephyr 的硬件支持层次有以下几层,从最具体到最不具体:

  • Board: a particular CPU instance and its peripherals in a concrete hardware specification

    板: 具体硬件规范中的特定 CPU 实例及其外围设备

  • SoC: the exact system on a chip the board’s CPU is part of

    SoC: 板上 CPU 所属的芯片上的确切系统

  • SoC series: a smaller group of tightly related SoCs

    SoC 系列: 一小组紧密相关的 SoC

  • SoC family: a wider group of SoCs with similar characteristics

    系统芯片家族: 一个具有相似特征的更广泛的系统芯片组

  • CPU core: a particular CPU in an architecture

    CPU 核心: 体系结构中的特定 CPU

  • Architecture: an instruction set architecture

    架构: 指令集架构

You can visualize the hierarchy like this:

你可以像这样想象层次结构:

Configuration Hierarchy

Fig. 35 Configuration Hierarchy

图35结构层次

Here are some examples. Notice how the SoC series and family levels are not always used.

下面是一些例子。注意 SoC 系列和系列级别并不总是被使用。

Board SoC SoC seriesSoC SoC familySoC CPU coreCPU Architecture
nrf52dk_nrf52832 nRF52832 nRF52 Nordic nRF5 Arm Cortex-M4 Arm
frdm_k64f MK64F12 Kinetis K6x NXP Kinetis Arm Cortex-M4 Arm
stm32h747i_disco STM32H747XI STM32H7 STMicro STM32 Arm Cortex-M7 Arm
rv32m1_vega_ri5cy RV32M1 (Not used) (Not used) RI5CY RISC-V

Make sure your SoC is supported

确保支持 SoC

Start by making sure your SoC is supported by Zephyr. If it is, it’s time to Create your board directory. If you don’t know, try:

首先确保您的 SoC 得到 Zephyr 的支持。如果是这样,那么现在就是创建主板目录的时候了。如果你不知道,试试:

  • checking Supported Boards for names that look relevant, and reading individual board documentation to find out for sure.

    检查支持主板的名称,看起来相关,并阅读个别主板的文件,以找出肯定。

  • asking your SoC vendor

    问你的芯片供应商

If you need to add SoC, CPU core, or even architecture support, this is the wrong page, but here is some general advice.

如果您需要添加 SoC、 CPU 核心或者甚至架构支持,这是错误的页面,但是这里有一些一般性的建议。

Architecture

See Architecture Porting Guide.

请参阅体系结构移植指南。

CPU Core CPU 核心

CPU core support files go in core subdirectories under arch, e.g. arch/x86/core.

CPU 核心支持文件放在 arch 下的 core 子目录中,例如 arch/x86/core。

See Install a Toolchain for information about toolchains (compiler, linker, etc.) supported by Zephyr. If you need to support a new toolchain, Build and Configuration Systems is a good place to start learning about the build system. Please reach out to the community if you are looking for advice or want to collaborate on toolchain support.

有关 Zephyr 支持的工具链(编译器、链接器等)的信息,请参见安装工具链。如果您需要支持新的工具链,那么构建和配置系统是开始学习构建系统的好地方。如果您正在寻求建议或者希望在工具链支持方面进行协作,请与社区联系。

SoC 系统控制中心

Zephyr SoC support files are in architecture-specific subdirectories of soc. They are generally grouped by SoC family.

Zephyr SoC 支持文件位于特定于体系结构的 SoC 子目录中。

When adding a new SoC family or series for a vendor that already has SoC support within Zephyr, please try to extract common functionality into shared files to avoid duplication. If there is no support for your vendor yet, you can add it in a new directory zephyr/soc/<YOUR-ARCH>/<YOUR-SOC>; please use self-explanatory directory names.

当为 Zephyr 中已经支持 SoC 的供应商添加新的 SoC 系列时,请尝试将通用功能提取到共享文件中以避免重复。如果您的供应商还没有得到支持,您可以将它添加到新的目录 zephyr/soc/<YOUR-ARCH>/<YOUR-SOC>; 请使用不言而喻的目录名称。

Create your board directory 创建主板目录

Once you’ve found an existing board that uses your SoC, you can usually start by copy/pasting its board directory and changing its contents for your hardware.

一旦找到一个使用 SoC 的现有主板,通常可以从复制/粘贴它的主板目录开始,并为硬件更改其内容。

You need to give your board a unique name. Run west boards for a list of names that are already taken, and pick something new. Let’s say your board is called plank (please don’t actually use that name).

你得给你的主板起个独一无二的名字。查一下west boards上已经登记的名字,再选一些新的。假设你的冲浪板叫厚木板(请不要真的使用这个名字)。

Start by creating the board directory zephyr/boards/<ARCH>/plank, where <ARCH> is your SoC’s architecture subdirectory. (You don’t have to put your board directory in the zephyr repository, but it’s the easiest way to get started. See Custom Board, Devicetree and SOC Definitions for documentation on moving your board directory to a separate repository once it’s working.)

首先创建主板目录 zephyr/boards/<ARCH>/plank,其中 < ARCH > 是 SoC 的架构子目录。(您不必将您的主板目录放在 zephyr 存储库中,但是这是最简单的入门方法。有关在板目录运行后将其移动到单独的存储库的文档,请参阅 Custom Board、 Devicetree 和 SOC Definition。)

Your board directory should look like this:

你的主板目录应该是这样的:

boards/<ARCH>/plank
├── board.cmake
├── CMakeLists.txt
├── doc
│   ├── plank.png
│   └── index.rst
├── Kconfig.board
├── Kconfig.defconfig
├── plank_defconfig
├── plank.dts
└── plank.yaml

Replace plank with your board’s name, of course.

当然,用你的主板的名字替换木板。

The mandatory files are:

强制性文件包括:

  1. plank.dts: a hardware description in devicetree format. This declares your SoC, connectors, and any other hardware components such as LEDs, buttons, sensors, or communication peripherals (USB, BLE controller, etc).

    plank.dts: 设备树格式的硬件描述。这将声明您的 SoC、连接器和任何其他硬件组件,如 LED、按钮、传感器或通信外围设备(USB、 ABLE 控制器等)。

  2. Kconfig.board, Kconfig.defconfig, plank_defconfig: software configuration in Configuration System (Kconfig) formats. This provides default settings for software features and peripheral drivers.

    Kconfig.board, Kconfig.defconfig, plank_defconfig: 配置系统(Kconfig)格式的软件配置。

The optional files are:

可选文件包括:

  • board.cmake: used for Flash and debug support

    用于 Flash 和调试支持

  • CMakeLists.txt: if you need to add additional source files to your build.

    如果需要向构建中添加其他源文件。

  • doc/index.rst, doc/plank.png: documentation for and a picture of your board. You only need this if you’re Contributing your board to Zephyr.

    doc/index.rst, doc/plank.png: 主板的文档和图片。只有把你的主板捐给Zephyr,你才需要这个。

  • plank.yaml: a YAML file with miscellaneous metadata used by the Test Runner (Twister).

    一个 YAML 文件,包含 Test Runner (Twister)使用的各种元数据。

Write your devicetree 写你的设备树

The devicetree file boards/<ARCH>/plank/plank.dts describes your board hardware in the Devicetree Source (DTS) format (as usual, change plank to your board’s name). If you’re new to devicetree, see Introduction to devicetree.

Devicetree 文件boards/<ARCH>/plank/plank.dts 以 Devicetree Source (DTS)格式描述您的电路板硬件(像往常一样,将 plank 更改为您的电路板名称)。如果您是设备树的新手,请参阅设备树简介。

In general, plank.dts should look like this:

一般来说,plank.dts 应该是这样的:

/dts-v1/;
#include <your_soc_vendor/your_soc.dtsi>

/ {
     model = "A human readable name";
     compatible = "yourcompany,plank";

     chosen {
             zephyr,console = &your_uart_console;
             zephyr,sram = &your_memory_node;
             /* other chosen settings  for your hardware */
     };

     /*
      * Your board-specific hardware: buttons, LEDs, sensors, etc.
      */

     leds {
             compatible = "gpio-leds";
             led0: led_0 {
                     gpios = < /* GPIO your LED is hooked up to */ >;
                     label = "LED 0";
             };
             /* ... other LEDs ... */
     };

     buttons {
             compatible = "gpio-keys";
             /* ... your button definitions ... */
     };

     /* These aliases are provided for compatibility with samples */
     aliases {
             led0 = &led0; /* now you support the blinky sample! */
             /* other aliases go here */
     };
};

&some_peripheral_you_want_to_enable { /* like a GPIO or SPI controller */
     status = "okay";
};

&another_peripheral_you_want {
     status = "okay";
};

If you’re in a hurry, simple hardware can usually be supported by copy/paste followed by trial and error. If you want to understand details, you will need to read the rest of the devicetree documentation and the devicetree specification.

如果您赶时间,简单的硬件通常可以通过复制/粘贴,然后尝试和错误来支持。如果您想要了解详细信息,您将需要阅读设备树文档和设备树规范的其余部分。

Example: FRDM-K64F and Hexiwear K64

示例: FRDM-K64F 和 Hexiware K64

This section contains concrete examples related to writing your board’s devicetree.

本节包含与编写主板的设备树相关的具体示例。

The FRDM-K64F and Hexiwear K64 board devicetrees are defined in frdm_k64fs.dts and hexiwear_k64.dts respectively. Both boards have NXP SoCs from the same Kinetis SoC family, the K6X.

FRDM-K64F和Hexiwear K64板的devicetrees分别在frdm_k64fs.dtshexiwear_k64.dts中定义。这两块板子的恩智浦SoC都来自同一个Kinetis SoC系列,即K6X。

Common devicetree definitions for K6X are stored in nxp_k6x.dtsi, which is included by both board .dts files. nxp_k6x.dtsi in turn includes armv7-m.dtsi, which has common definitions for Arm v7-M cores.

K6X的通用设备树定义存储在nxp_k6x.dtsi中,它被两个板子的.dts文件所包含。nxp_k6x.dtsi又包括armv7-m.dtsi,它有Arm v7-M内核的通用定义。

Since nxp_k6x.dtsi is meant to be generic across K6X-based boards, it leaves many devices disabled by default using status properties. For example, there is a CAN controller defined as follows (with unimportant parts skipped):

由于nxp_k6x.dtsi是为了在基于K6X的板子上通用,它让许多设备默认使用status属性禁用。例如,有一个CAN控制器定义如下(跳过不重要的部分)。

can0: can@40024000 {
     ...
     status = "disabled";
     ...
};

It is up to the board .dts or application overlay files to enable these devices as desired, by setting status = "okay". The board .dts files are also responsible for any board-specific configuration of the device, such as adding nodes for on-board sensors, LEDs, buttons, etc.

由电路板.dts或应用程序覆盖文件通过设置status = "okay"来启用这些设备,以满足需要。板卡.dts文件还负责设备的任何板卡特定的配置,例如为板上的传感器、LED、按钮等添加节点。

For example, FRDM-K64 (but not Hexiwear K64) .dts enables the CAN controller and sets the bus speed:

例如,FRDM-K64(但不是Hexiwear K64).dts启用CAN控制器并设置总线速度。

&can0 {
     status = "okay";
     bus-speed = <125000>;
};

The &can0 { ... }; syntax adds/overrides properties on the node with label can0, i.e. the can@4002400 node defined in the .dtsi file.

&can0 { ... }; 语法添加/覆盖标签为can0的节点的属性,即.dtsi文件中定义的can@4002400节点。

Other examples of board-specific customization is pointing properties in aliases and chosen to the right nodes (see Aliases and chosen nodes), and making GPIO/pinmux assignments.

其他针对板子定制的例子是将 “aliases “和 “chosen “中的属性指向正确的节点(见aliases and chosen nodes),并进行GPIO/pinmux分配。

Write Kconfig files 编写 Kconfig 文件

Zephyr uses the Kconfig language to configure software features. Your board needs to provide some Kconfig settings before you can compile a Zephyr application for it.

Zephyr 使用 Kconfig 语言配置软件特性。您的主板需要提供一些 Kconfig 设置,然后才能为其编译 Zephyr 应用程序。

Setting Kconfig configuration values is documented in detail in Setting Kconfig configuration values.

设置 Kconfig 配置值在设置 Kconfig 配置值中有详细说明。

There are three mandatory Kconfig files in the board directory for a board named plank:

对于名为 plank 的主板,在主板目录中有三个必需的 Kconfig 文件:

boards/<ARCH>/plank
├── Kconfig.board
├── Kconfig.defconfig
└── plank_defconfig
  • Kconfig.board

    Included by boards/Kconfig to include your board in the list of options.

    被 boards/Kconfig 包含,以便在选项列表中包括您的主板。

    This should at least contain a definition for a BOARD_PLANK option, which looks something like this:

    这至少应该包含一个 BOARD_PLANK 选项的定义,它看起来像这样:

    config BOARD_PLANK
    bool "Plank board"
    depends on SOC_SERIES_YOUR_SOC_SERIES_HERE
    select SOC_PART_NUMBER_ABCDEFGH
    
  • Kconfig.defconfig

    Board-specific default values for Kconfig options.

    由主板决定的 Kconfig 选项的默认值。

    The entire file should be inside an if BOARD_PLANK / endif pair of lines, like this:

    整个文件应该在 if BOARD_PLANK / endif 包含的代码块中,如下所示:

    if BOARD_PLANK
    
    # Always set CONFIG_BOARD here. This isn't meant to be customized,
    # but is set as a "default" due to Kconfig language restrictions.
    config BOARD
    default "plank"
    
    # Other options you want enabled by default go next. Examples:
    
    config FOO
    default y
    
    if NETWORKING
    config SOC_ETHERNET_DRIVER
    default y
    endif # NETWORKING
    
    endif # BOARD_PLANK
    
  • plank_defconfig

    A Kconfig fragment that is merged as-is into the final build directory .config whenever an application is compiled for your board.

    一个Kconfig片段,每当为你的板子编译一个应用程序时,它就会被原样合并到最终的构建目录.config中。

    You should at least select your board’s SOC and do any mandatory settings for your system clock, console, etc. The results are architecture-specific, but typically look something like this:

    你至少应该选择你的板子的SOC,并对你的系统时钟、控制台等进行任何强制性设置。结果是由具体的架构决定的,但通常看起来是这样的:

    CONFIG_SOC_${VENDOR_XYZ3000}=y                      /* select your SoC */
    CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=120000000   /* set up your clock, etc */
    CONFIG_SERIAL=y
    
  • plank_x_y_z.conf

    A Kconfig fragment that is merged as-is into the final build directory .config whenever an application is compiled for your board revision x.y.z.

    合并到最终构建目录中的 Kconfig 片段。当为您的版本 x.y.z 编译应用程序时,可以使用 config。

Build, test, and fix 构建、测试和修复

Now it’s time to build and test the application(s) you want to run on your board until you’re satisfied.

现在是时候构建和测试您希望在板上运行的应用程序了,直到您满意为止。

For example:

例如:

west build -b plank samples/hello_world
west flash

For west flash to work, see Flash and debug support below. You can also just flash build/zephyr/zephyr.elf, zephyr.hex, or zephyr.bin with any other tools you prefer.

为了让 west flash 工作,请看下面的Flash和调试支持。你也可以直接用你喜欢的其他工具烧录 build/zephyr/zephyr.elf, zephyr.hex, 或zephyr.bin

General recommendations 一般建议

For consistency and to make it easier for users to build generic applications that are not board specific for your board, please follow these guidelines while porting.

为了保持一致性,并使用户更容易构建非主板特定的通用应用程序,请在移植时遵循以下指导方针。

  • Unless explicitly recommended otherwise by this section, leave peripherals and their drivers disabled by default.

    除非本节有明确的建议,否则让外设及其驱动程序默认为禁用。

  • Configure and enable a system clock, along with a tick source.

    配置并启用一个系统时钟以及一个刻度源。

  • Provide pin and driver configuration that matches the board’s valuable components such as sensors, buttons or LEDs, and communication interfaces such as USB, Ethernet connector, or Bluetooth/Wi-Fi chip.

    提供引脚和驱动程序配置,以匹配板上有价值的组件,如传感器、按钮或 LED,以及通信接口,如 USB、以太网连接器或蓝牙/Wi-Fi 芯片。

  • If your board uses a well-known connector standard (like Arduino, Mikrobus, Grove, or 96Boards connectors), add connector nodes to your DTS and configure pin muxes accordingly.

    如果你的板子使用知名的连接器标准(如Arduino、Mikrobus、Grove或96Boards连接器),请将连接器节点添加到你的DTS中并相应地配置引脚复用。

  • Configure components that enable the use of these pins, such as configuring an SPI instance to use the usual Arduino SPI pins.

    配置能够使用这些引脚的组件,例如配置一个SPI实例来使用常用的Arduino SPI引脚

  • If available, configure and enable a serial output for the console using the zephyr,console chosen node in the devicetree.

    如果有的话,使用devicetree中的zephyr,console选择节点配置并启用控制台的串行输出。

  • If your board supports networking, configure a default interface.

    如果你的板子支持联网,请配置一个默认接口。

  • Enable all GPIO ports connected to peripherals or expansion connectors.

    启用连接到外围设备或扩展连接器的所有 GPIO 端口。

  • If available, enable pinmux and interrupt controller drivers.

    如果可用,启用 pinmux 和中断控制器驱动程序。

  • It is recommended to enable the MPU by default, if there is support for it in hardware. For boards with limited memory resources it is acceptable to disable it. When the MPU is enabled, it is recommended to also enable hardware stack protection (CONFIG_HW_STACK_PROTECTION=y) and, thus, allow the kernel to detect stack overflows when the system is running in privileged mode.

    如果硬件上支持MPU,建议默认启用MPU。对于内存资源有限的板子,禁用它是可以接受的。当启用MPU时,建议同时启用硬件堆栈保护(CONFIG_HW_STACK_PROTECTION=y),这样,当系统以特权模式运行时,允许内核检测堆栈溢出。

Flash and debug support 支持 Flash 和调试

Zephyr supports Building, Flashing and Debugging via west extension commands.

Zephyr 通过 west 扩展命令支持构建、闪存和调试。

To add west flash and west debug support for your board, you need to create a board.cmake file in your board directory. This file’s job is to configure a “runner” for your board. (There’s nothing special you need to do to get west build support for your board.)

为了给你的板子增加 “west flash “和 “west debug “的支持,你需要在你的板子目录下创建一个board.cmake文件。这个文件的任务是为你的板子配置一个 “运行器”。(你不需要做什么特别的事情来为你的板子获得 “west build “支持)。

“Runners” are Zephyr-specific Python classes that wrap flash and debug host tools and integrate with west and the zephyr build system to support west flash and related commands. Each runner supports flashing, debugging, or both. You need to configure the arguments to these Python scripts in your board.cmake to support those commands like this example board.cmake:

“运行器 “是Zephyr专用的Python类,它包裹了flash和调试主机工具,并与west和zephyr构建系统集成,以支持west flash和相关命令。每个运行器都支持flash、调试或两者。你需要在你的board.cmake中配置这些Python脚本的参数,以支持这些命令,比如这个例子board.cmake

board_runner_args(jlink "--device=nrf52" "--speed=4000")
board_runner_args(pyocd "--target=nrf52" "--frequency=4000000")

include(${ZEPHYR_BASE}/boards/common/nrfjprog.board.cmake)
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
include(${ZEPHYR_BASE}/boards/common/pyocd.board.cmake)

This example configures the nrfjprog, jlink, and pyocd runners.

此示例配置 nrfjprog、 jlink 和 pyocd 运行程序。

Warning 警告

Runners usually have names which match the tools they wrap, so the jlink runner wraps Segger’s J-Link tools, and so on. But the runner command line options like --speed etc. are specific to the Python scripts.

运行程序通常有与它们包装的工具相匹配的名称,所以 jlink 运行程序包装 Segger 的 J-Link 工具,等等。但是运行程序命令行选项(如 –speed 等)是特定于 Python 脚本的。

For more details: 更多细节:

  • Run west flash --context to see a list of available runners which support flashing, and west flash --context -r <RUNNER> to view the specific options available for an individual runner.

    运行 west flash --context 查看支持烧录的可用运行器列表,运行 west flash --context -r <RUNNER> 查看单个运行器可用的特定选项。

  • Run west debug --context and west debug --context <RUNNER> to get the same output for runners which support debugging.

    运行 west debug --contextwest debug --context <RUNNER> 以获得支持调试的运行程序的相同输出。

  • Run west flash --help and west debug --help for top-level options for flashing and debugging.

    运行 west flash --helpwest debug --help获得用于烧录和调试的顶级选项。

  • See Flash and debug runners for Python APIs.

    有关 Python API,请参见 Flash 和调试运行程序。

  • Look for board.cmake files for other boards similar to your own for more examples.

    查找与您自己的类似的其他板块的 board.cmake 文件,以获得更多示例。

To see what a west flash or west debug command is doing exactly, run it in verbose mode:

要查看 west flash 或 west debug 命令到底在做什么,可以在详细模式下运行它:

west --verbose flash
west --verbose debug

Verbose mode prints any host tool commands the runner uses.

详细模式打印运行程序使用的任何主机工具命令。

The order of the include() calls in your board.cmake matters. The first include sets the default runner if it’s not already set. For example, including nrfjprog.board.cmake first means that nrfjprog is the default flash runner for this board. Since nrfjprog does not support debugging, jlink is the default debug runner.

你的 “board.cmake “中的 “include() “调用顺序很重要。第一个 “include “会设置默认的运行器,如果它还没有被设置的话。例如,首先包括nrfjprog.board.cmake意味着nrfjprog是这个板子的默认Flash运行器。由于nrfjprog不支持调试,jlink是默认的调试运行器。

Multiple board revisions 多个修订版本

See Building for a board revision for basics on this feature from the user perspective.

有关从用户角度了解此特性的基础知识,请参见构建版本。

To create a new board revision for the plank board, create these additional files in the board folder:

要为板卡创建一个新的版本,请在板卡文件夹中创建以下附加文件:

boards/<ARCH>/plank
├── plank_<revision>.conf     # optional
├── plank_<revision>.overlay  # optional
└── revision.cmake

When the user builds for board plank@<revision>:

当用户为 plank@<revision> 构建时:

  • The optional Kconfig settings specified in the file plank_<revision>.conf will be merged into the board’s default Kconfig configuration.

    可选的 Kconfig 设置在 plank_<revision>.conf 文件中指定,这些设置将合并到板块的默认 Kconfig 配置中。

  • The optional devicetree overlay plank_<revision>.overlay will be added to the common plank.dts devicetree file

    可选的设备树覆盖 plank_<revision>.overlay 将添加到公共 plank.dts 设备树文件中

  • The revision.cmake file controls how the Zephyr build system matches the <board>@<revision> string specified by the user when building an application for the board.

    revision.cmake 文件控制 Zephyr 构建系统在为主板构建应用程序时如何匹配用户指定的 <board>@<revision> 字符串。

Currently, <revision> can be either a numeric MAJOR.MINOR.PATCH style revision like 1.5.0, an integer number like 1, or single letter like A, B, etc. Zephyr provides a CMake board extension function, board_check_revision(), to make it easy to match either style from revision.cmake.

目前,<revision> 可以是数字 MAJOR.MINOR.PATCH 样式的修订版,如1.5.0,整数数字,如1,或单个字母,如 A、 B 等。Zephyr 提供了一个 CMake 板扩展函数 board_check_revision() ,可以轻松匹配 vision.cmake 中的任何一种样式。

Valid board revisions may be specified as arguments to the board_check_revision() function, like:

有效的主板修订可以指定为 board_check_revision()函数的参数,比如:

board_check_revision(FORMAT MAJOR.MINOR.PATCH
                     VALID_REVISIONS 0.1.0 0.3.0 ...
)

Note 注意

VALID_REVISIONS can be omitted if all valid revisions have specific Kconfig fragments, such as <board>_0_1_0.conf, <board>_0_3_0.conf. This allows you to just place Kconfig revision fragments in the board folder and not have to keep the corresponding VALID_REVISIONS in sync.

如果所有有效的修订都具有特定的 Kconfig 片段,例如 <board>_0_1_0.conf,则可以省略 VALID_REVISION。Conf,< board >_0_3_0.保密。这允许您将 Kconfig 修订片段放在主板文件夹中,而不必保持相应的 VALID_REVISION 同步。

The following sections describe how to support these styles of revision numbers.

以下各节描述如何支持这些修订号样式

MAJOR.MINOR.PATCH revisions

Let’s say you want to add support for revisions 0.5.0, 1.0.0, and 1.5.0 of the plank board with both Kconfig fragments and devicetree overlays. Create revision.cmake with board_check_revision(FORMAT MAJOR.MINOR.PATCH), and create the following additional files in the board directory:

假设您希望添加对修订版0.5.0、1.0.0和1.5.0的支持,同时支持 Kconfig 片段和 devicetree 覆盖。使用 board_check_revision(FORMAT MAJOR.MINOR.PATCH)创建 vision.cmake,并在 board 目录中创建以下附加文件:

boards/<ARCH>/plank
├── plank_0_5_0.conf
├── plank_0_5_0.overlay
├── plank_1_0_0.conf
├── plank_1_0_0.overlay
├── plank_1_5_0.conf
├── plank_1_5_0.overlay
└── revision.cmake

Notice how the board files have changed periods (“.”) in the revision number to underscores (“_”).

注意主板文件如何将修订号中的句点(“ .”)更改为下划线(“ _”)。

Fuzzy revision matching 模糊修正匹配

To support “fuzzy” MAJOR.MINOR.PATCH revision matching for the plank board, use the following code in revision.cmake:

为了支持模糊匹配 plank 板的 MAJOR.MINOR.PATCH 修订版,请在 vision.cmake 中使用以下代码:

board_check_revision(FORMAT MAJOR.MINOR.PATCH)

If the user selects a revision between those available, the closest revision number that is not larger than the user’s choice is used. For example, if the user builds for plank@0.7.0, the build system will target revision 0.5.0.

如果用户在可用的版本之间选择一个版本号,则使用不大于用户选择的版本号的最接近的版本号。例如,如果用户为 plank@0.7.0构建,那么构建系统将以0.5.0版本为目标。

The build system will print this at CMake configuration time:

构建系统将在 CMake 配置时打印该文件:

-- Board: plank, Revision: 0.7.0 (Active: 0.5.0)

This allows you to only create revision configuration files for board revision numbers that introduce incompatible changes.

这使您只能为引入不兼容更改的版本号创建版本配置文件。

Any revision less than the minimum defined will be treated as an error.

任何低于最小定义的修订将被视为错误。

You may use 0.0.0 as a minimum revision to build for by creating the file plank_0_0_0.conf in the board directory. This will be used for any revision lower than 0.5.0, for example if the user builds for plank@0.1.0.

你可以使用 “0.0.0 “作为最小的版本,通过在主板目录下创建 “plank_0_0_0.conf” 文件来构建。这将用于任何低于0.5.0的版本,例如,如果用户为plank@0.1.0构建。

Exact revision matching 精确的修正匹配

Alternatively, the EXACT keyword can be given to board_check_revision() in revision.cmake to allow exact matches only, like this:

或者,可以将 EXACT 关键字赋予 vision.cmake 中的 board_check_revision() ,以便只允许精确匹配,如下所示:

board_check_revision(FORMAT MAJOR.MINOR.PATCH EXACT)

With this revision.cmake, building for plank@0.7.0 in the above example will result in the following error message:

使用这个 vision.cmake,在上面的示例中构建 plank@0.7.0将导致以下错误消息:

Board revision `0.7.0` not found.  Please specify a valid board revision.

Letter revision matching

Let’s say instead that you need to support revisions A, B, and C of the plank board. Create the following additional files in the board directory:

假设您需要支持 plant 板卡的修订版 A、 B 和 C。在主板目录中创建以下附加文件:

boards/<ARCH>/plank
├── plank_A.conf
├── plank_A.overlay
├── plank_B.conf
├── plank_B.overlay
├── plank_C.conf
├── plank_C.overlay
└── revision.cmake

And add the following to revision.cmake:

并将以下内容添加到 vision.cmake:

board_check_revision(FORMAT LETTER)

Number revision matching 数字修正匹配

Let’s say instead that you need to support revisions 1, 2, and 3 of the plank board. Create the following additional files in the board directory:

假设您需要支持木板版的修订版1、2和3。在主板目录中创建以下附加文件:

boards/<ARCH>/plank
├── plank_1.conf
├── plank_1.overlay
├── plank_2.conf
├── plank_2.overlay
├── plank_3.conf
├── plank_3.overlay
└── revision.cmake

And add the following to revision.cmake:

并将以下内容添加到 vision.cmake:

board_check_revision(FORMAT NUMBER)

board_check_revision() details 详细信息

board_check_revision(FORMAT <LETTER | NUMBER | MAJOR.MINOR.PATCH>
                     [EXACT]
                     [DEFAULT_REVISION <revision>]
                     [HIGHEST_REVISION <revision>]
                     [VALID_REVISIONS <revision> [<revision> ...]]
)

This function supports the following arguments:

该函数支持以下参数:

  • FORMAT LETTER: matches single letter revisions from A to Z only

    格式字母: 只匹配从 A 到 Z 的单个字母修订

  • FORMAT NUMBER: matches integer revisions

    格式数: 匹配整数修订

  • FORMAT MAJOR.MINOR.PATCH: matches exactly three digits. The command line allows for loose typing, that is -DBOARD=<board>@1 and -DBOARD=<board>@1.0 will be handled as -DBOARD=<board>@1.0.0. Kconfig fragment and devicetree overlay files must use full numbering to avoid ambiguity, so only <board>_1_0_0.conf and <board>_1_0_0.overlay are allowed.

    正好匹配三位数字。命令行允许松散输入,即-DBOARD = < board >@1和-DBOARD = < board >@1.0将被处理为-DBOARD = < board >@1.0.0。Kconfig 片段和设备树覆盖文件必须使用完整的编号,以避免歧义,因此只有 < board >_1_0_0。Conf 和 < board >_1_0_0。允许覆盖。

  • EXACT: if given, the revision is required to be an exact match. Otherwise, the closest matching revision not greater than the user’s choice will be selected.

    确切地说: 如果给定,修订是需要是一个完全匹配。否则,将选择不大于用户选择的最接近的匹配修订版。

  • DEFAULT_REVISION <revision>: if given, <revision> is the default revision to use when user has not selected a revision number. If not given, the build system prints an error when the user does not specify a board revision.

    DEFAULT_REVISION < version > : 如果给定,< version > 是用户未选择修订号时使用的默认修订。如果未给定,则当用户未指定电路板修订时,生成系统将打印错误。

  • HIGHEST_REVISION: if given, specifies the highest valid revision for a board. This can be used to ensure that a newer board cannot be used with an older Zephyr. For example, if the current board directory supports revisions 0.x.0-0.99.99 and 1.0.0-1.99.99, and it is expected that the implementation will not work with board revision 2.0.0, then giving HIGHEST_REVISION 1.99.99 causes an error if the user builds using <board>@2.0.0.

    HIGHEST_REVISION: 如果给定,则指定主板的最高有效修订。这可以用来确保较新的电路板不能与较旧的 Zephyr 一起使用。例如,如果当前的主板目录支持修订版0.x. 0-0.99.99和1.0.0-1.99.99,并且预计该实现将不能与主板修订版2.0.0一起工作,那么给出 HIGHEST_REVISION 1.99.99将导致错误,如果用户使用 < board >@2.0.0进行构建。

  • VALID_REVISIONS: if given, specifies a list of revisions that are valid for this board. If this argument is not given, then each Kconfig fragment of the form <board>_<revision>.conf in the board folder will be used as a valid revision for the board.

    VALID_REVISION: 如果给定,则指定对此板有效的修订列表。如果没有给出这个论点,那么表单 < board >_< Amendment > 的每个 Kconfig 片段。主板文件夹中的 conf 将被用作主板的有效修订版。

Custom revision.cmake files 自定义 vision.cmake 文件

Some boards may not use board revisions supported by board_check_revision(). To support revisions of any type, the file revision.cmake can implement custom revision matching without calling board_check_revision().

有些主板可能不会使用 board_check_Amendment()支持的主板修订。为了支持任何类型的修订,文件 vision.cmake 可以实现自定义的修订匹配,而不需要调用 board_check_Amendment()。

To signal to the build system that it should use a different revision than the one specified by the user, revision.cmake can set the variable ACTIVE_BOARD_REVISION to the revision to use instead. The corresponding Kconfig files and devicetree overlays must be named <board>_<ACTIVE_BOARD_REVISION>.conf and <board>_<ACTIVE_BOARD_REVISION>.overlay.

为了向构建系统发出信号,告诉它应该使用与用户指定的版本不同的版本,vision.cmake 可以将变量 AIVE_BOARD_REVISION 设置为要使用的版本。相应的 Kconfig 文件和设备树覆盖必须命名为 < board >< Aactive_BOARD_REVISION > 。Conf 和 < board >< Aactive_BOARD_REVISION > 。覆盖。

For example, if the user builds for plank@zero, revision.cmake can set ACTIVE_BOARD_REVISION to one to use the files plank_one.conf and plank_one.overlay.

例如,如果用户构建的是 plank@zero,那么 vision.cmake 可以将 Aactive_BOARD_REVISION 设置为 one,以使用 plank_one 文件。Conf 和 plank_one。覆盖。

Contributing your board 为你的主板做贡献

If you want to contribute your board to Zephyr, first – thanks!

如果你想把你的主板贡献给Zephyr,首先-谢谢!

There are some extra things you’ll need to do:

你还需要做一些额外的事情:

  1. Make sure you’ve followed all the General recommendations. They are requirements for boards included with Zephyr.

    确保你已经遵循了所有的一般性建议。他们是包含在 Zephyr 中的电路板的要求。

  2. Add documentation for your board using the template file doc/templates/board.tmpl. See Documentation Generation for information on how to build your documentation before submitting your pull request.

    使用模板文件 doc/template/board.tmpl 为主板添加文档。有关在提交请求之前如何构建文档的信息,请参见文档生成。

  3. Prepare a pull request adding your board which follows the Contribution Guidelines.

    准备一个请求,添加遵循贡献指南的主板。