mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-07 20:00:53 +00:00
230 lines
15 KiB
ReStructuredText
230 lines
15 KiB
ReStructuredText
比特调节器 (BitScrambler) 驱动
|
||
====================================
|
||
|
||
:link_to_translation:`en:[English]`
|
||
|
||
介绍
|
||
----
|
||
|
||
比特调节器 (BitScrambler) 是一个外设,能够基于用户提供的程序对 DMA 数据流执行多种类型的转换。ESP-IDF 提供了比特调节器程序的汇编器、构建系统和驱动支持。在 {IDF_TARGET_NAME} 中,比特调节器外设具有独立的 TX(发送)和 RX(接收)通道,可分别关联到相同或不同的外设。
|
||
|
||
|
||
功能概述
|
||
--------
|
||
|
||
.. list::
|
||
|
||
- `比特调节器汇编程序 <#bitscrambler-assembly>`__:介绍比特调节器汇编程序的结构
|
||
- `构建系统集成 <#bitscrambler-build>`__:介绍比特调节器程序如何与 ESP-IDF 构建系统集成
|
||
- `资源分配与程序加载 <#bitscrambler-load>`__:介绍如何分配比特调节器实例以及如何加载程序
|
||
- `回环模式 <#bitscrambler-loopback>`__:介绍如何在回环模式下使用比特调节器
|
||
|
||
.. _bitscrambler-assembly:
|
||
|
||
比特调节器汇编程序
|
||
^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
比特调节器对 DMA 数据流执行的操作由比特调节器程序定义。由于比特调节器程序是一个难以手动编写的二进制 blob,因此 ESP-IDF 提供了汇编器,将易于编写的文本文件转换为比特调节器二进制程序。
|
||
|
||
比特调节器汇编文件由注释、标签、指令包和元指令组成。对于注释,汇编器会直接忽略。标签用于定义程序中的位置。指令可以跳转到标签指定的位置。指令包是一组子指令,这些指令会被组装成一个 257 位的比特调节器指令。元指令用于定义比特调节器的全局配置,例如尾随字节数、预取模式或查找表 (LUT) 内容。
|
||
|
||
比特调节器汇编文件不区分大小写,也不依赖缩进。本文档中使用的大小写混合仅便于阅读。整数字段默认使用十进制,但也可使用十六进制(前缀 ``0x``)或二进制(前缀 ``0b``)。
|
||
|
||
注释
|
||
~~~~
|
||
|
||
注释以 ``#`` 开头,并持续到行末。注释可以出现在允许空格的任何位置,包括指令包内的子指令之间。
|
||
|
||
标签
|
||
~~~~~~
|
||
|
||
任何由非空白字符组成并以英文冒号结尾的字符串都是一个标签。标签是对汇编文件中下一个指令包的符号引用。标签不能放在指令包内部,而应位于指令包之前。
|
||
|
||
示例:
|
||
|
||
.. code:: asm
|
||
|
||
loop_back:
|
||
set 0..3 4..7,
|
||
set 4..7 0..3,
|
||
read 8,
|
||
write 8,
|
||
jmp loop_back
|
||
|
||
该指令包中的 ``jmp`` 指令会跳回自身的起始位置,反复执行整个指令包,形成一个紧密循环。
|
||
|
||
指令包
|
||
~~~~~~~~~~~~~~~~~~
|
||
|
||
指令包由以逗号分隔的子指令组成。整个指令包会被汇编成一条 257 位的指令,由比特调节器在单个时钟周期内执行完成。指令包中的所有子指令会并行执行,而不受它们在汇编源代码中的顺序影响。指令包在最后一个没有逗号的子指令后结束。
|
||
|
||
如需了解比特调节器的详细信息,请参阅 **{IDF_TARGET_NAME} 技术参考手册** > **比特调节器** [`PDF <{IDF_TARGET_TRM_CN_URL}#bitscrm>`__]。
|
||
|
||
总结来说,比特调节器包含一个 32 位的输出寄存器,其每一位可以取自任意一个源的任意一位。支持的源包括:
|
||
|
||
- 一个 64 位的输入寄存器,从传入的 DMA 数据流中获取数据
|
||
- 两个 16 位的计数器
|
||
- 一个 30 位寄存器,包含各种比较的输出结果
|
||
- 一个固定的高位和低位
|
||
- 一个查找表 (LUT) RAM 的输出
|
||
- 上一周期中输出寄存器的值
|
||
|
||
子指令
|
||
""""""""""""""
|
||
|
||
``set [output] [source_bits]``:将一个或多个源位路由到输出位。注意,可以使用 ``..`` 操作符路由多个位,例如 ``set 0..3 O4..O6`` 等效于 ``set 0 O4, set 1 O5, set 2 O6, set 3 O7``。第一个参数是输出位或输出位范围,输出位的编号范围为 0 到 31。第二个参数是一个或一组 `源位 (source_bit)`_。注意,在指令包中,如果某些输出位没有对应的 ``set`` 子指令,会默认将其设置为低逻辑电平。
|
||
|
||
``write [n]``:在路由所有输出位后,取寄存器的最低有效 ``n`` 位并推送到 DMA 的输出流中。``n`` 可以是 0、8、16 或 32。如果指令包中没有 ``write`` 子指令,则其效果等同于 ``write 0``。
|
||
|
||
``read [n]``:在路由所有输出位并将数据写入输出寄存器后,从输入 DMA 流中读取 ``n`` 位数据,并将其推入 64 位输入寄存器。``n`` 的取值可以是 0、8、16 或 32。这些新读取的位将从最高有效位开始依次进入 FIFO。例如,执行 ``read 16`` 将输入寄存器中原本的位 63~16 整体下移至位 47~0,而从 DMA 流读取的新 16 位数据会填充输入寄存器的位 63~48。如果一个指令包中没有 ``read`` 指令,其效果等同于 ``read 0``。
|
||
|
||
操作码 (opcode)
|
||
""""""""""""""""""
|
||
|
||
.. only:: esp32p4
|
||
|
||
- ``LOOP(A|B) end_val ctr_add tgt``:如果选定的计数器(A 或 B)小于 end_val,将 ``ctr_add`` 添加到选定的计数器(A 或 B),并跳转到标签 ``tgt``。否则继续执行。
|
||
- ``ADD(A|B)[H|L] val``:将 ``val`` 添加到选定的计数器。如果附加了 ``H`` 或 ``L``,则分别仅更新计数器的高 8 位或低 8 位。
|
||
- ``IF[N] source_bit tgt``:如果源位 `source_bit` 为 1(对于 IF)或 0(对于 IFN),则跳转到标签 ``tgt``。
|
||
- ``LDCTD(A|B)[H|L] val``:将 ``val`` 加载到指定的计数器中。如果附加了 ``H`` 或 ``L``,则分别仅更新计数器的高 8 位或低 8 位。
|
||
- ``LDCTI(A|B)[H|L]``:将输出寄存器的 16-31 位加载到指定的计数器(A 或 B)中。如果附加了 H 或 L,则分别仅更新计数器的高 8 位或低 8 位。
|
||
- ``JMP tgt``:无条件跳转到标签 ``tgt``,等同于 ``IF h tgt``。
|
||
- ``NOP``:无操作,等同于 ``ADDA 0``。
|
||
|
||
.. only:: esp32c5
|
||
|
||
- ``LOOP(A|B) end_val ctr_add tgt``:如果选定的计数器(A 或 B)小于 end_val,将 ``ctr_add`` 添加到选定的计数器(A 或 B),并跳转到标签 ``tgt``。否则继续执行。
|
||
- ``ADD(A|B)[H|L] val``:将 ``val`` 添加到选定的计数器。如果附加了 ``H`` 或 ``L``,则分别仅更新计数器的高 8 位或低 8 位。
|
||
- ``IF[N] source_bit tgt``:如果源位 `source_bit` 为 1(对于 IF)或 0(对于 IFN),则跳转到标签 ``tgt``。
|
||
- ``LDCTD(A|B)[H|L] val``:将 ``val`` 加载到指定的计数器中。如果附加了 ``H`` 或 ``L``,则分别仅更新计数器的高 8 位或低 8 位。
|
||
- ``LDCTI(A|B)[H|L]``:将发送到输出寄存器的 16~31 位加载到指定的计数器(A 或 B)。如果附加了 ``H`` 或 ``L``,则分别仅更新计数器的高 8 位或低 8 位。
|
||
- ``ADDCTI(A|B)[H|L]``:将发送到输出寄存器的 16~31 位加到指定的计数器(A 或 B)上。如果附加了 ``H`` 或 ``L``,则分别仅评估并更新计数器的高 8 位或低 8 位。
|
||
- ``JMP tgt``:无条件跳转到标签 ``tgt``,等同于 ``IF h tgt``。
|
||
- ``NOP`` - 无操作,等同于 ``ADDA 0``。
|
||
|
||
.. note::
|
||
|
||
注意,一个指令包中只能包含一个操作码、一个 ``read`` 指令和一个 ``write`` 指令,但可以包含多个 ``set`` 指令。多个 ``set`` 指令不能对同一输出位进行赋值。
|
||
|
||
源位 (source_bit)
|
||
""""""""""""""""""""
|
||
|
||
``set`` 和 ``if``/ ``ifn`` 指令包含一个 ``source_bit`` 字段,其取值范围如下:
|
||
|
||
- ``0`` ~ ``63``:选定的位来源于输入寄存器中对应的位。
|
||
- ``O0`` ~ ``O31``:选定的位来源于上一周期中输出寄存器的值。
|
||
- ``A0`` ~ ``A15``:选定的位来源于 A 计数器寄存器中对应的位。
|
||
- ``B0`` ~ ``B15``:选定的位来源于 B 计数器寄存器中对应的位。
|
||
- ``L0`` ~ ``L31``:选定的位来源于 LUT RAM 的输出。根据**{IDF_TARGET_NAME} 技术参考手册** [`PDF <{IDF_TARGET_TRM_CN_URL}>`__],LUT RAM 的输出是 LUT 中某个项,该项的位置由上一周期路由到输出寄存器的最高有效 N 位决定。其中,N 的取值对应 32、16 和 8 位的 LUT,分别为 9、10、11。
|
||
- 将 B 计数器的部分值与上一周期传输至输出寄存器的位进行比较时,整个条件由三部分组成:
|
||
|
||
1. 第一部分:指定比较的是 B 计数器的全部位,还是仅高 8 位或低 8 位:
|
||
|
||
- ``B``:比较整个 B 计数器
|
||
- ``BH``:比较 B 计数器的高 8 位
|
||
- ``BL``:比较 B 计数器的低 8 位
|
||
|
||
2. 第二部分:比较运算符,支持操作 ``<=``、 ``>`` 和 ``=``。
|
||
3. 第三部分:指定输出寄存器中用于比较的位偏移:
|
||
|
||
- 16 位比较时,可选 ``O0`` 或 ``O16``
|
||
- 8 位比较时,可选 ``O0``、``O8``、``O16`` 或 ``O24``
|
||
|
||
- ``H`` 或 ``L``:这些源是固定的高逻辑电平或低逻辑电平。
|
||
|
||
.. note::
|
||
|
||
注意,并非所有源都可以在同一指令中一起使用。例如,无法在同一指令包中同时使用来自两个计数器的某一位和来自输入 FIFO 高 32 位中的某个位。如果指令包尝试这样做,汇编器会生成错误。
|
||
|
||
示例
|
||
""""
|
||
|
||
如下是比特调节器的一个程序示例:
|
||
|
||
.. code:: asm
|
||
|
||
loop_back:
|
||
set 0..3 4..7,
|
||
set 4..7 0..3,
|
||
read 8,
|
||
write 8,
|
||
jmp loop_back
|
||
|
||
|
||
这个程序只有一条指令(因为只有最后的 ``jmp`` 行没有以逗号结尾)。此程序将从内存读取的低 4 位数据传送到输出寄存器的第一个字节的高 4 位,同时,将输入寄存器接下来的 4 位数据传送到输出寄存器的低 4 位。然后,它将 8 位数据(一个字节)写入输出,并从输入中读取 8 位数据。最后,程序跳转回指令开始处继续执行。注意,这些操作都在一个比特调节器周期内执行,并且由于子指令都属于同一条指令,因此在指令内部可以按任何顺序指定。这个小型比特调节器程序的最终结果是:接收数据,例如 ``01 23 45 67``,并交换每个字节的高低半字节,输出结果为 ``10 32 54 76``。
|
||
|
||
|
||
元指令
|
||
~~~~~~~~
|
||
|
||
元指令用于设置全局的比特调节器配置。元指令可以出现在汇编文件的任何位置(指令包内部除外),并且由于其全局性质,可能会影响之前的汇编代码。目前定义了两条元指令:``cfg`` 用于全局比特调节器设置,``lut`` 定义查找表 (lookup table) RAM 的内容。
|
||
|
||
|
||
全局配置元指令
|
||
"""""""""""""""
|
||
|
||
- ``cfg prefetch true|false``:如果 ``prefetch`` 设置为 ``true``,则比特调节器启动时会从输入 DMA 流中读取 64 位数据到输入寄存器中。如果设置为 ``false``,输入寄存器将被初始化为零。默认为 ``true``。请注意,如果启用了 prefetch 但是输入流无法提供至少 64 位的数据,比特调节器会发生挂起。
|
||
- ``cfg eof_on upstream|downstream``:输入流结束后,比特调节器仍会计算一定量的“尾随”填充字节,以便清空其寄存器中可能存储的数据。此设置表示的是尾随字节的来源:如果设置为 ``upstream``,比特调节器从输入流中读取一定数量的填充字节,如果设置为 ``downstream``,比特调节器会等待写入足够的字节。默认为 ``upstream``。
|
||
- ``cfg trailing_bytes N``:该设置指示比特调节器在指示输出流结束之前,需要读取或写入(取决于 ``eof_on`` 设置)多少个填充字节。默认值为 ``0``。
|
||
- ``cfg lut_width_bits 8|16|32``:该设置选择 LUT 输出 RAM 的总线宽度(单位:位)。LUT 的大小可以是 2048×8 位、1024×16 位或 512×32 位。默认值为 ``32``。
|
||
|
||
|
||
LUT 内容元指令
|
||
"""""""""""""""""""""""""""""
|
||
|
||
``lut`` 指令用于指定 LUT RAM 的内容。该元指令后跟一个或多个数值,用空格或逗号分隔。LUT RAM 的位置是按它们在汇编程序中出现的顺序定义的;第一个值总是存储在位置 0,第二个值总是存储在位置 1,以此类推。LUT 元指令的参数数量是任意的,因为 LUT 元指令可以随时拆分或合并。例如,``lut 1,2,3,4`` 等同于 ``lut 1,2`` 在一行, ``lut 3,4`` 在下一行。注意,LUT 的值必须在与 ``cfg lut_width_bits`` 配置元语句所给定的值的范围内。
|
||
|
||
.. _bitscrambler-build:
|
||
|
||
构建系统集成
|
||
^^^^^^^^^^^^^^
|
||
|
||
比特调节器完全支持 ESP-IDF 构建系统。一个组件(包括主组件)可以在其源目录中直接包含比特调节器汇编源文件,这些文件通常具有后缀 ``.bsasm``。具体而言,需在组件的 CMakeLists.txt 文件中调用 ``target_bitscrambler_add_src("assembly_file.bsasm")``,从而将此类文件汇编并链接到主应用程序中。例如,对于名为 ``my_program.bsasm`` 的汇编文件,CMakeLists.txt 文件可能如下所示:
|
||
|
||
.. code:: cmake
|
||
|
||
idf_component_register(SRCS "main.c" "some-file.c"
|
||
INCLUDE_DIRS "./include")
|
||
|
||
target_bitscrambler_add_src("my_program.bsasm")
|
||
|
||
要使用汇编后的比特调节器程序,可以这样引用:
|
||
|
||
.. code:: c
|
||
|
||
// 创建一个变量 'my_bitscrambler_program',它解析为
|
||
// 二进制的比特调节器程序。
|
||
// 第二个参数与汇编文件的名称相同,但不包括 ".bsasm"
|
||
BITSCRAMBLER_PROGRAM(my_bitscrambler_program, "my_program");
|
||
|
||
[...]
|
||
|
||
bitscrambler_handle_t bs;
|
||
[...创建比特调节器实例]
|
||
bitscrambler_load_program(bs, my_bitscrambler_program);
|
||
|
||
|
||
.. _bitscrambler-loopback:
|
||
|
||
回环模式
|
||
^^^^^^^^^
|
||
|
||
比特调节器支持回环模式,适用于不涉及外设的数据转换任务。回环模式下,TX 和 RX 通道都会被占用,但实际上只有 TX 比特调节器执行代码。注意,即使回环模式不涉及外设,仍然需要选择一个外设。此外设无需初始化或使用,但如果使用,将无法使用其 DMA 功能。
|
||
|
||
资源分配和程序加载
|
||
^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
在回环模式下,使用 :cpp:func:`bitscrambler_loopback_create` 创建一个比特调节器对象。如果有一个与请求的特性匹配的比特调节器外设,该函数将返回此外设的句柄。然后,使用 :cpp:func:`bitscrambler_load_program` 将比特调节器程序加载到创建的对象中,再调用 :cpp:func:`bitscrambler_loopback_run` 使用此加载的程序进行内存缓冲区的比特转换。可以多次调用 :cpp:func:`bitscrambler_loopback_run`,也可以在调用之间使用 :cpp:func:`bitscrambler_load_program` 更改程序。最后,调用 :cpp:func:`bitscrambler_free` 释放硬件资源并清理内存。
|
||
|
||
应用示例
|
||
--------
|
||
|
||
* :example:`peripherals/bitscrambler` 演示了如何使用比特调节器回环模式将数据包转换为不同的格式。
|
||
|
||
API 参考
|
||
--------
|
||
|
||
.. include-build-file:: inc/bitscrambler.inc
|
||
.. include-build-file:: inc/bitscrambler_loopback.inc
|
||
.. include-build-file:: inc/bitscrambler_peri_select.inc
|