性能优化-HVX 指令介绍

「发表于知乎专栏《移动端算法优化》」

本文主要介绍了 HVX 指令相关的知识,包括 HVX 寄存器相关内容,指令的背景依赖,部分常用 intrinsic HVX 指令。具体指令的详细内容及使用还需阅读 HVX 的指令文档,以及细致的实践操作。

🎬个人简介:一个全栈工程师的升级之路!
📋个人专栏:高性能(HPC)开发基础教程
🎀CSDN主页 发狂的小花
🌄人生秘诀:学习的本质就是极致重复!

目录

一、概述

二、HVX硬件介绍

2.1 处理器架构

2.2 HVX向量寄存器

三、HVX指令

3.1 VLIW

3.2 HVX指令分类

3.3 汇编指令格式介绍

四、HVX intrinsics指令介绍

4.1 向量类型格式

4.2 HVX intrinsics格式

4.3 Load/Store

4.4 Arithmetic

4.5 Logical and compare

4.6 Multiply

4.7 Shift

4.8 Permute

4.9 其他

五、总结


一、概述

随着骁龙多核AI引擎的发展,异构计算算力有了巨大提升,为了发挥CDSP HVX在AI和CV的优势,开发者必须熟练掌握HVX的指令。但是HVX指令多而复杂,基础指令就有上百条及一些复杂的特殊指令,写起来更是费劲。为了发挥其性能高的优势,开发者必须使用intrinsic或汇编的方式编排代码,因此完全掌握HVX指令至关重要。

本文接下来首先详细介绍HVX硬件架构,在了解架构的基础上着重介绍HVX指令相关内容,然后针对HVX开发中必须要掌握的HVX instrinsic展开图文并茂的说明。主要内容有HVX架构、寄存器、VLIW、汇编格式及ALU的Intrinsics指令使用,最后针对几个常用的Intrinsics 指令结合实例进行说明。

二、HVX硬件介绍

2.1 处理器架构

在上一篇文章中HVX架构简介详细介绍了HVX软件和硬件架构,为了能够更加深入理解HVX指令执行过程,此处详细说明Hexagon主处理器和hexagon HVX SIMD协处理的关系和指令执行过程。

Hexagon主处理和HVX协处理架构图

2.1.1 Hexagon主处理器的架构

Hexagon主处理器架构图

  • Hexagon 主处理器架构,由四个硬件线程组成,硬件线程共享一个指令cache(I)、数据cache(D)。
  • 每个硬件线程都可以访问执行 Hexagon 标量指令,这些指令在单个或成对 32 位寄存器上执行定点和浮点运算。
  • 每个硬件线程包含通用寄存器(R0-R31)、预测判断寄存器(P0-P3)及执行单元(S0-S3)。
  • 静态分组超长指令集架构(VLIW)。

2.1.2 HVX协处理器的架构

HVX协处理架构图

  • Hexagon HVX协处理架构,有四个HVX硬件线程,硬件线程间共享L2-cache和VTCM内存。
  • 每个硬件线程都可以访问执行HVX向量指令。
  • 每个硬件线程包含向量寄存器(V0-V31)、预测判断寄存器(Q0-Q3)、执行单元(ALU)及内存读写单元。
  • HVX指令可以使用Hexagon主处理器的标量操作数。
  • 静态分组超长指令集架构(VLIW)。

2.1.3 指令执行过程

Hexagon架构图

  • 指令执行过程从L2-cache加载指令和数据到I-cache和D-chche,经过Sequencer指令打包然后在执行单元(S0-S3)上执行。

2.1.4 Hexagon主处理器和HVX协处理指令共享

Hexagon core硬件线程通过访问 HVX 寄存器文件(HVX Context)来使用 HVX 协处理器。Hexagon core和HVX之间的指令包基于vector FIFO结构进行共享。因此同一个指令包中HVX指令可以与标量指令混合,如下所示,vector FIFO和HVX的处理器结构和指令混合汇编代码。

{
    r8 = add(r15,#128) //标量指令
    r14 = add(r13,#-1)
    r13 = add(r3,#128)
    v8 = vmem(r10+#0)  // HVX指令
}

以上讲解了整体架构,接下来着重说明HVX寄存器的内容。

2.2 HVX向量寄存器

2.2.1 HVX向量长度

HVX协处理随着架构不同向量寄存器长度分为64B(512bit)和128B(1024bit)模式,V66架构既包含64B也包含128B向量长度(两个HVX核为64B、两个HVX核为128B),V68架构以后只包含128B向量长度(四个HVX核均为128B),如下图所示。

向量寄存器长度

2.2.2 HVX向量寄存器数量及类别

本文中我们以V68架构展开说明,如上图右侧框内。每个HVX协处理器包含:

  • 32个1024bit的向量寄存器,V0-V31。
  • 4个128bit的预测寄存器,Q0-Q3。

向量寄存器

存放向量数据,每个向量元素的类型必须相同。向量寄存器根据处理元素的大小可以划分为 32/64/128个通道,对应数据类型为32bit/16bit/8bit。

1024-bit SIMD register

向量寄存器数据在SIMD指令操作中需要指定元素类型,例如:

// vo向量寄存器数据指定数据类型为signed byte
v0.b = vadd(v1.b,v2.b) //汇编
HVX_Vector Q6_Vb_vadd_VbVb(HVX_Vector Vu, HVX_Vector Vv) //intrinsic

常用的元素类型如下:

汇编标识intrinsic 标识含义
.bVbsigned byte
.ubVubunsigned byte
.hVhsigned halfword
.uhVuhunsigned halfword
.wVwsigned word
.uwVuwunsigned word

预测寄存器

预测寄存器每个bit位对应每个向量寄存器中的byte,既可以用来保存字节、半字和全字的比较结果。预测寄存器用Q表示,以下示例中,Q2代表预测寄存器的结果,在向量V5和V6中选择有效值在结果寄存器中。

// vmux选择指令,如果Q2为真,输出结果为V5,反之输出结果为V6
V4 = vmux(Q2, V5, V6)

三、HVX指令

这部分主要介绍VLIW、HVX指令分类和HVX汇编代码的格式和说明。

3.1 VLIW

3.1.1 VLIW解释说明

将指令的并行化(多发射)显式的声明在指令格式中,而处理器只负责执行指令,这种方式定义为超长指令字(Very Long Instruction Word,VLIW)体系结构。指令的并行化可由编译器完成,也可由开发者手写汇编代码完成,如下图所示红色圈内所示,即指令的并行化过程。

HVX的协处理指令集架构也是基于VLIW。因此,HVX协处理器有四个执行单元,理论上每个周期处理器可以执行4条指令。这4条指令看成一个指令包,取指、译码、执行单元每次都针对一个指令包进行操作,如下图所示,这就是HVX的VLIW。

指令包

虽然说指令按照VLIW并行化打包为指令包,但是也会有一定的打包规则,并不是任意组合,所以接下来会介绍HVX指令的打包规则。

3.1.2 HVX VLIW打包规则

HVX计算资源

HVX 提供了 6 种执行资源支持向量指令执行:loadstoreshiftpermute 以及 两个 multiply 。

  • 每条 HVX 指令均由上述部分资源组合而成,在S0-S3硬件资源执行
  • 每次并行执行一个指令包,由下图中指令单元负责打包分发
  • 每个指令包最多可以包含 4 条指令及一个循环结束指令,指令包需按照规则组合

VLIW打包规制

每个指令分别使用四个执行单元(shift、permute 以及 两个 multiply),且每个slot只能有一条指令,下表为HVX指令到slot执行单元的映射关系,手册中每条指令都会写可以占用的slot。

InstructionUsed Hexagon Slots
Aligned memory load0 or 1
Aligned memory store0
Unaligned memory load/store0Slot 1 must be empty.
Maximum of 3 instructions allowed in the packet
Multiplies2 or 3
Using full 32-64 bit R2 or 3
Simple ALU, permute, shif0, 1, 2, or 3

同时需要注意每个HVX指令对于执行单元的占用情况,在每条指令介绍下都会有说明,例如vmpa指令,下图红框内会对指令的HVX资源使用情况进行说明。

  • 指令包中只能包含两条load指令,或者一条load一条store指令
{
    V4 = vmem(iptrUp++#1)
    vmem(iptrUp++#1) = v4.cur
    nop
    nop
}

{
    V4 = vmem(iptrUp++#1)
    V5 = vmem(iptrUp++#1)
    nop
    nop
}
  • 若指令包中为简单的ALU,则均可以使用S0-S3
{ 
    v14 = vnot(V14)                               // v14 = left bounary mask
    v16 = vsplat(R8)                              // 0xFF 
    v12 = #0                                      // reset flag-collector
    v6 = vlalign(v0,v2,#3)                        //[1]pixels #12
}
  • 不要在指令包中存在多个乘法,因为乘法只有在执行单元S2和S3上执行
//存在错误 -- vmpa指令需要使用S2和S3的两个乘法资源,指令包中只能包含一个vmpa指令
{ 
    v1= vmem(iptr3++#1)                      //[1]
    v2.h += vmpa(v0.ub,c1c1c1c1.b)           //[1]
    v3.h += vmpa(v0.ub,c1c1c1c1.b)          
}

VLIW打包规制示例

以下样例为HVX协处理的指令包,指令在HVX处理器执行。

{
    v15 = vmem(R0++M1)
    vmem(R6++M1) = v14
    v18.uw = vrmpy(v16.ub, v17.ub)
}

上述HVX指令包完成一个1024bit数据加载,一次1024bit数据存储及地址更新,一次向量乘法(vrmpy占用两个乘法资源,在指令包中只能存在一条)。

3.2 HVX指令分类

根据指令使用的硬件资源类型不同,HVX指令分为以下几类 :

类别描述资源占用
ALU算术和逻辑运算操作使用任意一个硬件资源
Multiply乘法操作占用两个 mpy 资源或者占用两个中的一个
Shift & Bit Count位操作占用 shift 资源
Permute数据重排操作占用 xlane 资源
Load & Store数据存取操作对齐储存(vmem)会使用任意一个硬件资源 + ld 或 st 等资源。对齐取数据带 .tmp 的操作,只使用 ld资源;非对齐操作(vmemu)使用 ld 或 st + Xlane 资源;对齐存数据操作带 .new 时,只使用 st 资源。

3.3 汇编指令格式介绍

汇编格式

v<x><.><type> = <ops>(<input>)[:carry][:sat]
  • x表示32个向量寄存器的标识数字,type 为向量寄存器中数据的类型
  • type表示既可以向量寄存器中数据的类型:b、ub、h、uh、w、uw,也可以表示Aligned memory Load和Store数据的立即更新还是作为临时数据:.tmp、.new、.cur。
  • <ops> 为指令的具体指令操作标识,如 add、valignvaslvdmp 等等。

汇编示例

以有符号 halfword 的加法汇编指令为例,指令将 V4 中各 lane 的数据与 V3 中各 lane 的数据以有符号short进行进行相加,将结果存放在 V2 寄存器中。

V2.h=vadd(V3.h,V4.h)

HVX 汇编开发难点

HVX 的开发不仅仅是 SIMD 指令集的运用,汇编期 VLIW 指令的静态多发调度也尤为重要

相对于 intrinsic 指令而言

  • HVX 汇编开发需要对 HVX 指令集及硬件架构进行详细的学习 - 增加学习成本
  • HVX 汇编开发过程中需要对 HVX 指令打包,程序流程控制指令,.new\.tmp 指令,指令延迟,寄存器复用以及软件堆栈维护等多方面进行考虑 - 增加开发及维护难度,不利于迭代及增量开发
  • HVX 汇编指令严格对应硬件平台架构 - 不利于向后兼容扩展

因此,在非绝对性能瓶颈的实现中,建议使用 intrinsic 指令进行开发。

四、HVX intrinsics指令介绍

相比于汇编指令,HVX intrinsics 是一种更简单的编写 HVX 代码的方法,HVX Intrinsics 类似于 C 函数调用,在编译时由编译器替换为相应的汇编指令,使用时需要包含头文件hvx_hexagon_protos.h

4.1 向量类型格式

// 向量对格式
HVX_VectorPair
// 向量格式
HVX_Vector
// 判断向量格式
HVX_VectorPred

HVX的向量类型包含以上三种,其中HEXAGON_ARCH架构在V65以后HVX四核的向量长度为1024bit,V62到V65架构之前分为512bit和1024bit的HVX向量长度。HVX_Vector和HVX_VectorPred共用联合体,实现多种数据类型宽度兼容。

typedef union 
{
    size8u_t ud[MAX_VEC_SIZE_BYTES/8];
    size8s_t    d[MAX_VEC_SIZE_BYTES/8];
    size4u_t uw[MAX_VEC_SIZE_BYTES/4];
    size4s_t    w[MAX_VEC_SIZE_BYTES/4];
    size2u_t uh[MAX_VEC_SIZE_BYTES/2];
    size2s_t    h[MAX_VEC_SIZE_BYTES/2];
    size1u_t ub[MAX_VEC_SIZE_BYTES/1];
    size1s_t    b[MAX_VEC_SIZE_BYTES/1];
    size4s_t    qf32[MAX_VEC_SIZE_BYTES/4];
    size2s_t    qf16[MAX_VEC_SIZE_BYTES/2];
    size4s_t    sf[MAX_VEC_SIZE_BYTES/4];
    size2s_t    hf[MAX_VEC_SIZE_BYTES/2];
} mmvector_t, mmqreg_t;

#ifdef __HVXDBL__
 typedef mmqreg_t        HEXAGON_VecPred128;
 typedef mmvector_t      HEXAGON_Vect1024;
 typedef mmvector_pair_t HEXAGON_Vect2048;
 #define HVX_VectorPred  HEXAGON_VecPred128
 #define HVX_Vector      HEXAGON_Vect1024
 #define HVX_VectorPair  HEXAGON_Vect2048
#else
 typedef mmqreg_t        HEXAGON_VecPred64;
 typedef mmvector_t      HEXAGON_Vect512;
 typedef mmvector_pair_t HEXAGON_Vect1024;
 #define HVX_VectorPred  HEXAGON_VecPred64
 #define HVX_Vector      HEXAGON_Vect512
 #define HVX_VectorPair  HEXAGON_Vect1024
#endif /*HVXDBL*/
  • HVX_VectorPair:向量对组合,由两个HVX_Vector向量组成,常用在指令运算后结果是奇偶排列结果中,例如如下数据扩展指令,把一个HVX_Vector向量扩展为HVX_VectorPair,Vdd.v[0]和Vdd.v[1]组成一个向量对(HVX_VectorPair),占用两个向量寄存器。

4.2 HVX intrinsics格式

Q6_<output_type>_<opname>_<input_type><input_type>_<mod>

1)Q6 表示高通第六代数字信号处理器,固定前缀

2) <output_type> 表示返回值类型,此处分为W和V开头,比如Ww,Vw等

  • Vw :表示输出类型为HVX_Vector,元素类型为word类型
  • Ww:表示输出类型为HVX_VectorPair,元素类型为word类型
标识符解释标识符解释
Vbint8 vectorWbint8 vector pair
Vubuint8 vectorWubuint8 vector pair
Vhint16 vectorWhint16 vector pair
Vuhuint16 vectorWuhuint16 vector pair
Vwint32 vectorWwint32 vector pair
Vuwuint32 vectorWuwuint32 vector pair

3) <opname> 表示具体操作,比如vadd,vmpy等

4) <input_type> 表示输入值类型,此处分为W、V、R和Q开头及组合,例如

HVX_VectorPair Q6_Wh_vmpy_VubRb(HVX_Vector Vu, Word32 Rt)

Q6_Wh_vmpy_VubRb
  • W:表示输入类型为HVX_VectorPair向量对
  • V:表示输入类型为HVX_Vector向量
  • Q:表示输入类型为HVX_VectorPred预测向量
  • R:表示输入类型为Word标量
标识符解释标识符解释标识符解释
Vbint8 vectorWbint8 vectorpairRbint8 word
Vubuint8 vectorWubuint8 vectorpairRubuint8 word
Vhint16 vectorWhint16 vectorpairRhint16 word
Vuhuint16 vectorWuhuint16 vectorpairRuhuint16 word
Vwint32 vectorWwint32 vectorpairRword
Vuwuint32 vectorWuwuint32 vectorpairQHVX_VectorPred

5) <mod>

  • sat:表示饱和操作,例如
// Vu加Vv的结果做饱和操作
HVX_Vector Q6_Vb_vadd_VbVb_sat(HVX_Vector Vu, HVX_Vector Vv)
  • rnd:表示四舍五入操作,例如
// Vu加Vu的结果做进位输出操作
HVX_Vector Q6_Vw_vadd_VwVwQ_carry(HVX_Vector Vu, HVX_Vector Vv, HVX_VectorPred* Qp)
  • s1:表示左移一位操作,例如
// Vu加Vb的结果取平均值的四舍五入结果
HVX_Vector Q6_Vb_vavg_VbVb_rnd(HVX_Vector Vu, HVX_Vector Vv)

下面的章节将对每种类型的HVX intrinsic指令做出详细的描述。

4.3 Load/Store

按照地址对齐或者地址非对齐的方式在向量寄存器和内存之间搬移数据

HVX LOAD指令

  • 示例:
// 对齐地址
HVX_Vector v1 = *(HVX_Vector *)src;

// 非对齐地址
typedef long HEXAGON_Vect_UN __attribute__((__vector_size__(VLEN)))__attribute__((aligned(4)));
HEXAGON_Vect_UN v2 = *(HEXAGON_Vect_UN *)src; 
  • 作用:完成内存到寄存器的数据搬运
  • 伪代码:Vd=vmem(Rt+#0)
  • 结果示意图:紫色框内代表输入数据

  • 相关指令
// 地址对齐加载             // 地址非对齐加载 
Vd=vmem(Rt)                vd = vmemu(Rt) 
// 立即使用加载            
Vd.cur=vmem(Rt+#s4)    
// 临时立即使用加载
Vd.tmp=vmem(Rt+#s4) 

HVX STORE指令

  • 示例:
// 对齐地址
HVX_Vector v1;
*(HVX_Vector *)src = v1;

// 非对齐地址
HEXAGON_Vect_UN v2;
typedef long HEXAGON_Vect_UN __attribute__((__vector_size__(VLEN)))__attribute__((aligned(4)));
*(HEXAGON_Vect_UN *)src = v2; 
  • 作用:完成寄存器到内存的数据搬运
  • 伪代码:vmem(Rt+#0)= Vd
  • 结果示意图

  • 相关指令
// 字节使能对齐存储
If (Qv4) vmem(Rt) = Vs
// 新存储
vmem(Rt)=Os8.new
// 对齐存储
vmem(Rt)=Vs
// 非对齐存储
vmemu(Rt)=Vs

HVX 非对齐内存操作

  • vmemu非对齐操作指令,需要进行两次内存访问,与地址对齐访问相比会增加功耗和带宽,因此开发中我们建议尽量使用对齐LOAD和STORE指令。
  • vmemu非对齐指令占用两个HVX单元,分别是slot0和slot1。

HVX 对齐内存操作

  • vmem对齐操作指令,内存地址必须保证128Byte对齐。
  • 可以使用memalign(128,xxx)申请128Byte对齐内存,也可以使用ION相关函数申请。

4.4 Arithmetic

基本加减算法指令,完成向量数据的算法操作

vector 算术指令

  • 示例:HVX_Vector Q6_Vb_vsub_VbVb_sat(HVX_Vector Vu, HVX_Vector Vv)
  • 作用:两个输入vector加减法运算
  • 伪代码
for (i = 0; i < VELEM(8); i++) 
{
    Vd.b[i] = sat_s8(Vu.b[i] - Vv.b[i]) ;
}
  • 示意图

  • 相关指令
// type 类型加法:Vd.type = Vu.type + Vv.type;
HVX_Vector Q6_Vtype_vadd_VtypeVtype(HVX_Vector Vu, HVX_Vector Vv);

// type 类型加法,结果进行饱和操作:Vd.type = sat_type(Vu.type + Vv.type);
HVX_Vector Q6_Vtype_vadd_VtypeVtype_sat(HVX_Vector Vu, HVX_Vector Vv);

// type 类型减法:Vd.b = Vu.b - Vv.b;
HVX_Vector Q6_Vtype_vsub_VtypeVtype(HVX_Vector Vu, HVX_Vector Vv);

type:b、ub、h、uh、w、uw

vector pair 算术指令

  • 示例:HVX_VectorPair Q6_Ww_vadd_VhVh(HVX_Vector Vu, HVX_Vector Vv)
  • 作用:向量寄存器 Vu 与 Vv 进行相加操作,结果输出长度是输入的两倍,存放于向量对寄存器里,按照奇偶的方式排列。
奇偶排列:输入数据下标索引02468的结果存放在单独寄存器是偶排列,反之,13579...127是奇排列。
  • 伪代码
for (i = 0; i < VELEM(32); i++) 
{
    Vdd.v[0].w[i] = Vu.w[i].h[0] + Vv.w[i].h[0];
    Vdd.v[1].w[i] = Vu.w[i].h[1] + Vv.w[i].h[1];
}
  • 指令示意图

vadd

  • 结果示意图

  • 相关指令
// type 类型加法:vdd.type = Vu.type + Vv.type;
HVX_VectorPair Q6_Wtype0_vadd_Vtype1Vtype1(HVX_Vector Vu, HVX_Vector Vv)

// type 类型减法:vdd.type = Vu.type - Vv.type;
HVX_VectorPair Q6_Wtype0_vsub_Vtype1Vtype1(HVX_Vector Vu, HVX_Vector Vv)

// type 类型加法累加:vdd.type += Vu.type + Vv.type;
HVX_VectorPair Q6_Wtype0_vaddacc_Wtype0Vtype1Vtype1(HVX_VectorPair Vxx, 
                                      HVX_Vector Vu, HVX_Vector Vv)

type0: h、w

type1: ub、h、uh

4.5 Logical and compare

eq表示相等,gt表示大于,eqand表示等于判断及与操作,gtand表示大于判断及与操作,eqxacc表示等于判断及异或操作,gtxacc表示大于判断及异或操作,eqor表示等于判断及或操作,gtor表示大于判断及或操作。

条件比较指令

  • 示例:HVX_VectorPred Q6_Q_vcmp_eq_VbVb(HVX_Vector Vu, HVX_Vector Vv)
  • 作用:向量寄存器 Vu 与 Vv 进行比较运算,结果存储在预测寄存器中
  • 伪代码
for( i = 0; i < VWIDTH; i += 1) 
{
    QxV[i] =((Vu.b[i] == Vv.b[i]) ? 0x1 : 0);
}
  • 示例结果

  • 相关指令
// type 类型相等判断:Qd4 = Vu == Vv;
HVX_VectorPred Q6_Q_vcmp_eq_Vtype0Vtype0(HVX_Vector Vu, HVX_Vector Vv);

// type 类型大于判断:Qd4 = Vu > Vv;
HVX_VectorPred Q6_Q_vcmp_gt_Vtype1Vtype1(HVX_Vector Vu, HVX_Vector Vv);

// type 类型相等判断及与操作:Qx4 &= Vu == Vv;
HVX_VectorPred Q6_Q_vcmp_eqand_QVtype0Vtype0(HVX_VectorPred Qx, HVX_Vector Vu, HVX_Vector Vv);
                                
// type 类型大于判断及与操作:Qx4 &= Vu > Vv;
HVX_VectorPred Q6_Q_vcmp_gtand_QVtype1Vtype1(HVX_VectorPred Qx, HVX_Vector Vu, HVX_Vector Vv); 
 
// type 类型相等判断及异或操作:Qx4 ^= Vu == Vv;
HVX_VectorPred Q6_Q_vcmp_eqxacc_QVtype0Vtype0(HVX_VectorPred Qx, HVX_Vector Vu, HVX_Vector Vv);

// type 类型大于判断及异或操作:Qx4 ^= Vu > Vv;
HVX_VectorPred Q6_Q_vcmp_gtxacc_QVtype1Vtype1(HVX_VectorPred Qx, HVX_Vector Vu, HVX_Vector Vv);

// type 类型相等判断及或操作:Qx4 |= Vu == Vv;
HVX_VectorPred Q6_Q_vcmp_eqor_QVtype0Vtype0(HVX_VectorPred Qx, HVX_Vector Vu, HVX_Vector Vv);
                                
// type 类型大于判断及或操作:Qx4 |= Vu > Vv;
HVX_VectorPred Q6_Q_vcmp_gtor_QVtype1Vtype1(HVX_VectorPred Qx, HVX_Vector Vu, HVX_Vector Vv); 

type0:b、h、w

type1:b、ub、h、uh、w、uw

位操作指令

  • 示例:HVX_VectorPred Q6_Q_not_Q(HVX_VectorPred Qs)
  • 作用:将条件寄存器 Qs 进行取反操作,结果保存至条件寄存器中
  • 伪代码
for (i = 0; i < VELEM(8); i++) 
{
    QdV[i] = !QsV[i];
}
  • 示意图:蓝色框代表输入数据,红色框代表输出数据

  • 相关指令
// 条件寄存器非与操作:Qd4 = Qs & ((!Qt) 或者 (Qt))
HVX_VectorPred Q6_Q_and_QQ<n>(HVX_VectorPred Qs, HVX_VectorPred Qt);

// 条件寄存器非或操作:Qd4 = Qs | ((!Qt) 或者 (Qt))
HVX_VectorPred Q6_Q_or_QQ<n>(HVX_VectorPred Qs, HVX_VectorPred Qt);

// 条件寄存器非操作:Qd4 = !Qs
HVX_VectorPred Q6_Q_not_Q(HVX_VectorPred Qs);

// Vu 与标量 Rt 进行 u8 类型位与操作,填充条件寄存器:
// Qd4 = Vu.ub & Rt.ub => Qd4[i] = Vu.ub[i] & Rt.ub[i % 4] ? 1 : 0;
HVX_VectorPred Q6_Q_vand_VR(HVX_Vector Vu, Word32 Rt);

// Vu 与标量 Rt 进行 u8 类型位与操作,结果与 Qx 进行或操作,Qx4 |= (Vu.ub & Rt.ub);
HVX_VectorPred Q6_Q_vandor_QVR(HVX_VectorPred Qx, HVX_Vector Vu, Word32 Rt);

// 根据 !Qu 使用 Rt 以 u8 类型选择性填充 Vd
HVX_Vector Q6_V_vand_QnR(HVX_VectorPred Qu, Word32 Rt);

// 根据 !Qu 使用 Rt 以 u8 类型选择性填充 Vd, 然后与 Vx 进行或运算
HVX_Vector Q6_V_vandor_VQnR(HVX_Vector Vx, HVX_VectorPred Qu, Word32 Rt);

// 根据 Qu 使用 Rt 以 u8 类型选择性填充 Vd
HVX_Vector Q6_V_vand_QR(HVX_VectorPred Qu, Word32 Rt);

// 根据 Qu 使用 Rt 以 u8 类型选择性填充 Vd, 然后与 Vx 进行或运算
HVX_Vector Q6_V_vandor_VQR(HVX_Vector Vx, HVX_VectorPred Qu, Word32 Rt);

// 根据条件寄存器 Qt,以 u8 类型从 Vu 及 Vv 中选择填充 Vd
HVX_Vector Q6_V_vmux_QVV(HVX_VectorPred Qt, HVX_Vector Vu, HVX_Vector Vv);

// 根据条件寄存器 Qt,以 u8 类型对 Vu 及 Vv 进行交换操作,结果保存至 HVX_VectorPair 中
HVX_VectorPair Q6_W_vswap_QVV(HVX_VectorPred Qt, HVX_Vector Vu, HVX_Vector Vv);

逻辑操作指令

  • 示例:HVX_Vector Q6_V_vnot_V(HVX_Vector Vu)
  • 作用:对向量寄存器Vu按位逻辑取反
  • 伪代码
for (i = 0; i < VELEM(16); i++) 
{
    Vd.uh[i] = ~Vu.uh[i] ;
}
  • 示意图

  • 相关指令
// Vu和Vv向量寄存器按位逻辑与
HVX_Vector Q6_V_vand_VV(HVX_Vector Vu, HVX_Vector Vv)

// 对Vu向量寄存器按位取反
HVX_Vector Q6_V_vnot_V(HVX_Vector Vu)

// Vu和Vv向量寄存器按位逻辑或
HVX_Vector Q6_V_vor_VV(HVX_Vector Vu, HVX_Vector Vv)

// Vu和Vv向量寄存器按位逻辑异或
HVX_Vector Q6_V_vxor_VV(HVX_Vector Vu, HVX_Vector Vv)

取极值操作

  • 示例指令:HVX_Vector Q6_Vb_vmax_VbVb(HVX_Vector Vu, HVX_Vector Vv)
  • 作用:将两个数值向量寄存器进行比较操作,输出最大值到目的寄存器
  • 伪指令
for (i = 0; i < VELEM(8); i++) 
{
    Vd.b[i] = (Vu.b[i] > Vv.b[i]) ? Vu.b[i] : Vv.b[i] ;
}
  • 示意图

  • 相关指令
// type 类型最小值: Vd = min(Vu, Vv);
HVX_Vector Q6_Vtype_vmin_VtypeVtype(HVX_Vector Vu, HVX_Vector Vv);

// type 类型最大值: Vd = max(Vu, Vv);
HVX_Vector Q6_Vtype_vmax_VtypeVtype(HVX_Vector Vu, HVX_Vector Vv);

type:b、ub、h、uh、w、uw

饱和操作

  • 示例指令:HVX_Vector Q6_Vub_vsat_VhVh(HVX_Vector Vu, HVX_Vector Vv)
  • 作用:向量寄存器 Vu Vv 中 s16 类型数据,进行 u8 类型饱和操作,然后寄存器高低交替保存至 Vd 寄存器中
  • 伪代码
for (i = 0; i < VELEM(16); i++) 
{
    Vd.uh[i].b[0]=usat_8(Vv.h[i]);
    Vd.uh[i].b[1]=usat_8(Vu.h[i]);
}
  • 示意图

  • 相关指令
// s16 类型 Vu Vv 饱和至 u8 类型
HVX_Vector Q6_Vub_vsat_VhVh(HVX_Vector Vu, HVX_Vector Vv);

// u32 类型 Vu Vv 饱和至 u16 类型
HVX_Vector Q6_Vuh_vsat_VuwVuw(HVX_Vector Vu, HVX_Vector Vv);

// s32 类型 Vu Vv 饱和至 s16 类型
HVX_Vector Q6_Vh_vsat_VwVw(HVX_Vector Vu, HVX_Vector Vv;

绝对值操作

  • 示例指令:HVX_Vector Q6_Vb_vabs_Vb(HVX_Vector Vu)
  • 作用:向量寄存器以 s8 的数值类型进行取绝对值操作,结果依旧为 s8 类型
  • 伪代码
for (i = 0; i < VELEM(8); i++) 
{
    Vd.b[i] = ABS(Vu.b[i]);
}
  • 示例结果

  • 相关指令
// type 类型绝对值操作,结果进行饱和操作
HVX_Vector Q6_Vtype0_vabs_Vtype0_sat(HVX_Vector Vu);

// type 类型绝对值操作
HVX_Vector Q6_Vtype0_vabs_Vtype0(HVX_Vector Vu);

// type 类型绝对值差操作
HVX_Vector Q6_Vtype1_vabsdiff_Vtype2Vtype2(HVX_Vector Vu, HVX_Vector Vv);

type0:b、h、w

type1:ub、uh、uw

type2:ub、h、uh、w

数值扩展

  • 示例指令:HVX_VectorPair Q6_Wuh_vzxt_Vub(HVX_Vector Vu)
  • vzxt 为无符号扩展指令,将原数值类型进行正向调整,扩展后使用 HVX_VectorPair 进行存储,数值按照偶数位和奇数位分别存储至 Vdd.v[0] 和 Vdd.v[1] 中
  • 伪代码:注意结果寄存器存储时按照奇偶交替的顺序存储
// vzxt:u8 -> u16 类型转换
for (i = 0; i < VELEM(16); i++) 
{
    Vdd.v[0].uh[i] = Vu.uh[i].ub[0];
    Vdd.v[1].uh[i] = Vu.uh[i].ub[1];
}
  • 指令示意图

  • 相关指令
// u16 类型进行零填充扩展至 u32 类型
HVX_VectorPair Q6_Wuw_vzxt_Vuh(HVX_Vector Vu);

// s16 类型进行有符号填充扩展至 s32 类型
HVX_VectorPair Q6_Ww_vsxt_Vh(HVX_Vector Vu);

// s8 类型进行有符号填充扩展至 s16 类型
HVX_VectorPair Q6_Wh_vsxt_Vb(HVX_Vector Vu);

4.6 Multiply

向量乘标量运算

  • 示例:HVX_VectorPair Q6_Wh_vmpy_VubRb(HVX_Vector Vu, Word32 Rt)
  • 作用:向量寄存器 Vu 与标量 Rt 的元素进行乘法操作,数值按照偶数位和奇数位分别存储至 Vdd.v[0] 和 Vdd.v[1] 中。
  • 伪代码
for (i = 0; i < VELEM(16); i++) 
{
    Vdd.v[0].h[i] = (Vu.uh[i].ub[0] * Rt.b[(2*i+0)%4]);
    Vdd.v[1].h[i] = (Vu.uh[i].ub[1] * Rt.b[(2*i+1)%4]) ;
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// s16 类型向量乘 s16 标量运算,结果左移一位且四舍五入,做饱和操作
HVX_Vector Q6_Vh_vmpy_VhRh_s1_rnd_sat(HVX_Vector Vu, Word32 Rt)

// s16 类型向量乘 s16 标量运算,结果左移一位且饱和操作
HVX_Vector Q6_Vh_vmpy_VhRh_s1_sat(HVX_Vector Vu, Word32 Rt)

// s16 类型向量乘 s16 标量运算
HVX_VectorPair Q6_Wh_vmpy_VubRb(HVX_Vector Vu, Word32 Rt)

// type 类型向量乘加 type 标量运算
HVX_VectorPair Q6_Wtype_vmpyacc_WtypeVtypeRtype(HVX_VectorPair Vxx, HVX_Vector Vu, Word32 Rt)

// s16 类型向量乘加 s16 标量运算
HVX_VectorPair Q6_Ww_vmpyacc_WwVhRh_sat(HVX_VectorPair Vxx, HVX_Vector Vu, Word32 Rt)

向量乘加运算(乘标量)

  • 示例:HVX_VectorPair Q6_Wh_vmpa_WubRb(HVX_VectorPair Vuu, Word32 Rt)
  • 作用:向量对寄存器Vuu乘以Rt标量的各元素,数值按照偶数位和奇数位分别存储至 Vdd.v[0] 和 Vdd.v[1] 中。
  • 伪代码
for (i = 0; i < VELEM(16); i++) 
{
    Vdd.v[0].h[i] = (Vuu.v[0].uh[i].ub[0] * Rt.b[0]) 
                    + (Vuu.v[1].uh[i].ub[0] * Rt.b[1]);
                    
    Vdd.v[1].h[i] = (Vuu.v[0].uh[i].ub[1] * Rt.b[2]) 
                    + (Vuu.v[1].uh[i].ub[1] * Rt.b[3]);
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// type 类型的 Vuu 与 type 类型的Rt进行乘加操作,结果为 type 类型
HVX_VectorPair Q6_Wtype0_vmpa_Wtype1Rtype2(HVX_VectorPair Vuu, Word32 Rt);

// type 类型的 Vuu 与 type 类型的Rt进行乘加操作,结果与Vxx进行累加,结果为 type 类型
HVX_VectorPair Q6_Wtype0_vmpaacc_Wtype0Wtype1Rtype2(HVX_VectorPair Vxx, HVX_VectorPair Vuu, Word32 Rt);

type0:h、w

type1:b、ub、h、uh

type2:b、ub

乘法 vdmpy操作(乘标量)

  • 示例:HVX_Vector Q6_Vh_vdmpy_VubRb(HVX_Vector Vu, Word32 Rt)
  • 作用:向量寄存器 Vu 与标量 Rt 进行乘法操作,结果相邻对累加操作。
  • 伪代码
for (i = 0; i < VELEM(16); i++) 
{
    Vd.h[i] =  (Vu.uh[i].ub[0] * Rt.b[(2 * i    ) % 4]);
    Vd.h[i] += (Vu.uh[i].ub[1] * Rt.b[(2 * i + 1) % 4]) ;
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// type 类型向量与 type 类型标量乘法 2 路归约操作
HVX_Vector Q6_Vtype0_vdmpy_Vtype1Rb(HVX_Vector Vu, Word32 Rt);

// type 类型向量与 type 类型标量完成 2 路归约操作
HVX_Vector Q6_Vtype0_vdmpyacc_Vtype0Vtype1Rb(HVX_Vector Vx, HVX_Vector Vu, Word32 Rt);
  1. type0:h、w
  2. type1:ub、h

乘法 vrmpy 操作(乘标量)

  • 示例:HVX_Vector Q6_Vuw_vrmpy_VubRub(HVX_Vector Vu, Word32 Rt)
  • 作用:向量寄存器 Vu 与标量 Rt u进行乘法操作,结果相邻 4 元素累加操作,输出结果为u32类型。
  • 伪代码:
for (i = 0; i < VELEM(32); i++) 
{
    Vd.uw[i]  = (Vu.uw[i].ub[0] * Rt.ub[0]);
    Vd.uw[i] += (Vu.uw[i].ub[1] * Rt.ub[1]);
    Vd.uw[i] += (Vu.uw[i].ub[2] * Rt.ub[2]);
    Vd.uw[i] += (Vu.uw[i].ub[3] * Rt.ub[3]) ;
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// u8 类型向量与 s8 类型标量完成 4 路归约操作,结果为 s32 类型
HVX_Vector Q6_Vtype0_vrmpy_VubRtype1(HVX_Vector Vu, Word32 Rt);

// u8 类型向量与 u8 类型标量完成 4 路归约操作,然后进行 Vx 结果累加,结果为 u32 类型
HVX_Vector Q6_Vtype0_vrmpyacc_Vtype0VubRtype1(HVX_Vector Vx, HVX_Vector Vu, Word32 Rt);
  1. type0:w、uw
  2. type1:b、ub

4.7 Shift

立即数移位

  • 示例:HVX_Vector Q6_Vuw_vlsr_VuwR(HVX_Vector Vu, Word32 Rt)
  • 作用:向量寄存器 Vu 根据立即数 Rt 进行逻辑右移。
  • 伪代码
// vlsr
for (i = 0; i < VELEM(32); i++) 
{
    Vd.uw[i] = (Vu.uw[i] >> (Rt & (32 - 1)));
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// s16 类型根据 Rt 进行算数右移,结果四舍五入后饱和至 s8 类型,Vu Vv 移位结果奇偶交疊存储之 Vd 中
HVX_Vector Q6_Vb_vasr_VhVhR_rnd_sat(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt);

// type 类型根据 Rt 进行逻辑右移,结果为 type 类型
HVX_Vector Q6_Vtype_vlsr_VtypeR(HVX_Vector Vu, Word32 Rt);

// s16 类型根据 Rt 进行算数右移
HVX_Vector Q6_Vh_vasr_VhR(HVX_Vector Vu, Word32 Rt);

// s16 类型算数右移 Rt,结果饱和至 s8 类型,Vu Vv 移位结果奇偶交疊存储之 Vd 中
HVX_Vector Q6_Vb_vasr_VhVhR_sat(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt);
  1. type:ub、uh、uw

Narrowing移位

  • 示例指令:

HVX_Vector Q6_Vh_vasr_VwVwR_sat(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt)

  • 作用:算术右移,结果饱和操作,输入位宽是输出的两倍。
  • 伪代码
for (i = 0; i < VELEM(32); i++) 
{
    shamt = Rt & 0xF; 
    Vd.w[i].h[0]=[sat_16](Vv.w[i] >> shamt); 
    Vd.w[i].h[1]=[sat_16](Vu.w[i] >> shamt); 
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// type类型(round)算术右移,结果饱和操作
HVX_Vector Q6_Vtype0_vasr_Vtype1Vtype1R_rnd_sat(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt)
 // type类型算术右移,结果饱和操作                                            
HVX_Vector Q6_Vtype0_vasr_Vtype1Vtype1R_sat(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt)
  1. type0:b、ub、h、uh
  2. type1:h、uh、w、uw

4.8 Permute

向量拼接

  • 示例:HVX_Vector Q6_V_valign_VVR(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt)
  • 作用:向量寄存器 Vu 和 Vv 根据 Rt 参数进行移位拼接,向量寄存器内数值按照 u8 类型处理。
  • 伪代码
unsigned shift = Rt & (VWIDTH-1); 
for(i = 0; i < VWIDTH; i++) 
{ 
    Vd.ub[i] = (i + shift>=VWIDTH) ? Vu.ub[i + shift - VWIDTH] \
                : Vv.ub[i + shift]; 
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// 向量 Vu 与 Vv 根据 (Rt & (VWIDTH-1)) 的起始进行移位拼接操作
HVX_Vector Q6_V_valign_VVR(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt);

// 向量 Vu 与 Vv 根据 (VWIDTH - (Rt & (VWIDTH-1))) 的起始进行移位拼接操作
HVX_Vector Q6_V_vlalign_VVR(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt);

// 向量 Vu 根据 Rt 进行向右循环重排
HVX_Vector Q6_V_vror_VR(HVX_Vector Vu, Word32 Rt);

向量旋转

  • 示例:HVX_Vector Q6_V_vror_VR(HVX_Vector Vu, Word32 Rt)
  • 作用:用于1个vector内部的循环移位,Rt最大127
  • 伪代码
for (k = 0; k < VWIDTH; k++) 
{ 
    Vd.ub[k] = Vu.ub[(k + Rt)&(VWIDTH - 1)]; 
}
  • 指令示意图

向量交织操作

  • 示例:HVX_Vector Q6_Vb_vshuffe_VbVb(HVX_Vector Vu, HVX_Vector Vv);
  • 作用:取 Vv 及 Vu 中的偶数位元素,然后进行交织存储到 Vd 寄存器
  • 伪代码
for (i = 0; i < VELEM(16); i++) 
{
    Vd.uh[i].b[0]=Vv.uh[i].ub[0];
    Vd.uh[i].b[1]=Vu.uh[i].ub[0] ;
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// 偶数位交织
HVX_Vector Q6_Vb_vshuffe_VbVb(HVX_Vector Vu, HVX_Vector Vv);

// 奇数位交织
HVX_Vector Q6_Vb_vshuffo_VbVb(HVX_Vector Vu, HVX_Vector Vv);

// 奇偶数位交织
HVX_VectorPair Q6_Wb_vshuffoe_VbVb(HVX_Vector Vu, HVX_Vector Vv);

向量Shuffle - Deal操作

  • 示例
// 将向量内偶数位元素存放至目标寄存器低半位,奇数位元素存放至寄存器高半位
HVX_Vector Q6_Vb_vdeal_Vb(HVX_Vector Vu);

// 向量内以低半位和高半位为单位进行奇偶数位解交织操作
HVX_Vector Q6_Vb_vshuff_Vb(HVX_Vector Vu);
  • 作用: vdeal 指令实现将 Vu 向量内的偶数位元素存放到 Vd 寄存器的低半位,奇数位元素存放到 Vd 寄存器的高半位,实现向量中数据奇偶数位的重排(后续简称交织操作)。 vshuff 指令则以向量的低半位和高半位为基本单位,进行奇偶数位的交织重排,与vdeal 互为逆操作(后续简称解交织操作)。
  • 伪代码
//vdeal
for (i = 0; i < VELEM(16); i++) 
{
    Vd.ub[i ] = Vu.uh[i].ub[0];
    Vd.ub[i+VBITS/16] = Vu.uh[i].ub[1];
}

//vshuff
for (i = 0; i < VELEM(16); i++) 
{
    Vd.uh[i].b[0]=Vu.ub[i];
    Vd.uh[i].b[1]=Vu.ub[i+VBITS/16];
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// 两个向量的交织操作,接受一个常量 Rt 参数,Rt 参数影响交织操作的元素大小
HVX_VectorPair Q6_W_vdeal_VVR(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt);

// 两个向量的解交织操作,接受一个常量 Rt 参数,Rt 参数影响交织操作的元素大小
HVX_VectorPair Q6_W_vshuff_VVR(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt);

4.9 其他

上述章节讲解了基本指令,并没有对复杂指令展开说明,例如查表指令、直方图统计、gather/scatter和vrdelta等复杂指令,后面我们会专门针对以上提到的复杂指令做详细的讲解说明。例如如下所示的vrdelta、vdelta指令的交织网络结构,这种复杂的指令网络设计结构,必须结合设计paper和使用规则才能完全理解。

vrdelta/vdelta指令网络

五、总结

本文主要介绍了 HVX 指令相关的知识,包括 HVX 寄存器相关内容,指令的背景依赖,部分常用 intrinsic HVX 指令。具体指令的详细内容及使用还需阅读 HVX 的指令文档,以及细致的实践操作。

🌈我的分享也就到此结束啦🌈
如果我的分享也能对你有帮助,那就太好了!
若有不足,还请大家多多指正,我们一起学习交流!
📢未来的富豪们:点赞👍→收藏⭐→关注🔍,如果能评论下就太惊喜了!
感谢大家的观看和支持!最后,☺祝愿大家每天有钱赚!!!欢迎关注、关注!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

表达式计算

四则运算表达式可以用表达式树表达&#xff0c;如下图后序遍历 现给你一个字符串&#xff0c;代表一个后序遍历形式的四则运算表达式&#xff0c;请计算出表达式的结果&#xff1a;(只输出整数部分) 注&#xff1a;除法只保留整数部分&#xff1b;5/4 1 输入&#xff1a; 一个…

使用 YApi 管理 API 文档,测试, mock

随着互联网的发展&#xff0c;API变的至关重要。根据统计&#xff0c;目前市面上有上千万的开发者&#xff0c;互联网项目超过10亿&#xff0c;保守统计涉及的 API 数量大约有 100 亿。这么大基数的API&#xff0c;只要解决某些共有的痛点&#xff0c;将会是非常有意义的事情。…

SpringCloud中服务间通信(应用间通信)-亲测有效-源码下载-连载2

1、微服务概述 本案例主要解决微服务之间的相互调用问题 如果已经理解什么是微服务&#xff0c;可以直接跳到实战。 本案例采用springBoot3.1.7springCloud2022.0.4版本测试 本案例使用springboot2.7.x版本测试代码相同 1、微服务是分布式架构&#xff0c;那么为什么要需要…

中仕教育:选调生和考研可以一起准备吗?

研究生入学考试&#xff0c;是本科生们提升学历继续深造的主要途径。而选调生&#xff0c;是指通过考试选拔的优秀应届毕业生&#xff0c;经过锻炼后分配到各级工作。这两者作为大部分人选择的方向&#xff0c;发展前景都是比较好的。 考研和选调生可以一起准备吗? 是可以的…

业务逻辑漏洞—验证码绕过

验证码绕过第一关&#xff1a; 前端验证码绕过&#xff1a; 打开pikachu靶场&#xff1a; 输入错误的验证码时会出现弹窗&#xff08;alert&#xff09;此时我们猜测这可能存在着前端限制 如果验证码有前端限制&#xff08;只在前端有作用&#xff09;&#xff0c;不影响后…

opencv#29 图像噪声的产生

在上一节的图像卷积我们了解到图像卷积可以用于去除图像中的噪声&#xff0c;那么对于现实生活中每一张采集到的图像都会包含噪声&#xff0c;也就是我们通过相机无法得到不包含噪声的图像&#xff0c;如果我想衡量噪声去除能力的强弱&#xff0c;就必须在一张不含噪声的图像中…

架构篇09:架构设计原则案例

文章目录 淘宝案例手机QQ案例小结 我们先复习一下架构设计的三条核心原则&#xff1a;合适原则、简单原则和演化原则。 我们在架构设计实践中&#xff0c;应该时刻谨记这三条设计原则&#xff0c;指导我们设计出合适的架构&#xff0c;即使是代表中国互联网技术最顶尖水平的 BA…

51单片机1-6

目录 单片机介绍 点亮一个LED 流水灯参考代码 点亮流水LEDplus版本 独立按键 独立按键控制LED亮灭 静态数码管 静态数码管显示 动态数码管显示 模块化编程 调试工具 矩阵键盘 矩阵键盘显示数据 矩阵键盘密码锁 学习B站江协科技课程笔记。 安装keil&#xff0c;下…

docker容器下php框架laravel的使用问题与解决方案

DB_CONNECTIONmysqlDB_HOSTlocalhost DB_CONNECTIONmysqlDB_HOSTdocker33-mysql-1 容器中只有数据库结构 进入MySQL容器内&#xff0c;创建表结构&#xff0c;添加数据 代码层面需要转换成数组 $query->get([*])->toArray(); 分页数据框架会返回带有data的数据&#xf…

Spring Boot3整合Druid(监控功能)

目录 1.前置条件 2.导依赖 错误依赖&#xff1a; 正确依赖&#xff1a; 3.配置 1.前置条件 已经初始化好一个spring boot项目且版本为3X&#xff0c;项目可正常启动。 作者版本为3.2.2最新版 2.导依赖 错误依赖&#xff1a; 这个依赖对于spring boot 3的支持不够&#…

机器学习实验报告——APRIORI算法

目录 一、算法介绍 1.1算法背景 1.2算法引入 1.3算法假设 1.4算法基本概念介绍 1.4.1关联规则 1.4.2支持度 1.4.3置信度 1.4.4频繁项集 1.4.5项目 1.4.6提升度 二、算法原理 2.1算法思想 2.2Apriori算法产生频繁项集 2.3Apriori算法的基本步骤 2.4关联分析 三、算法实现 3.1 Ap…

开始学习vue2基础篇(初体验)

一、什么是VUE&#xff08;官网 &#xff1a;https://cn.vuejs.org/&#xff09; 官方给出的概念 &#xff1a;Vue (读音 /vju ː/ &#xff0c;类似于 view) 是一套用 于构建用户界面的前端框架 渐进式的 JavaScript 框架 二、VUE的特点 易用 &#xff1a;基础只需HTML、CSS、…

爬虫案例—抓取找歌词网站的按歌词找歌名数据

爬虫案例—抓取找歌词网站的按歌词找歌名数据 找个词网址&#xff1a;https://www.91ge.cn/lxyyplay/find/ 目标&#xff1a;抓取页面里的所有要查的歌词及歌名等信息&#xff0c;并存为txt文件 一共46页数据 网站截图如下&#xff1a; 抓取完整歌词数据&#xff0c;如下图…

excel学习1

直接ctrl cctrl v会报错位移选择粘贴时用123那个数字粘贴而不是ctrl V 只要结果不要公式 上面复制的为数值这里是复制的公式他们两个不一样 这个方法太麻烦了直接用格式刷&#xff0c;选择一个区域一个单元格&#xff0c;不要选择多个一刷就出来了 第一个计算后向下拖就行了&…

Ubutu下的Shell操作

前言 在学习Ubuntu系统时&#xff0c;Shell操作是必不可少的知识。本篇文章是记录我学习Linux系统时&#xff0c;Ubutu下的Shell操作&#xff0c;希望我的分享对大家有所帮助&#xff01; 目录 前言 一、什么时是Shell 二、Shell的基本操作 三、常用的Shell命令 1、目录信…

PrestaShop购物系统 SQL注入漏洞复现(CVE-2023-30150)

0x01 产品简介 PrestaShop 是一个功能丰富,基于 PHP5 开发的 Web2.0 网上购物系统。PrestaShop 具有可定制,稳定等特点。整个系统只有 5.8MB,易于快速安装。 0x02 漏洞概述 PrestaShop 的部分主题中使用Leo Custom Ajax模块拓展,Leo Custom Ajax模块中可以在/modules/le…

Hugo使用且部署GitHubPages

hugo的使用 20201121 Hugo是由Go语言实现的静态网站生成器。简单、易用、高效、易扩展、快速部署。 安装Hugo 0.windows安装(releases) 下载地址&#xff1a;https://github.com/spf13/hugo/releases。 配置环境变量 验证测试是否安装成功 hugo help1. 二进制安装&#xf…

C#使用DateAndTime.DateDiff方法计算年龄

目录 一、计算年龄的方法 二、 DateAndTime类 1.定义 2.常用方法 3.DateDiff(DateInterval, DateTime, DateTime, FirstDayOfWeek, FirstWeekOfYear) 三、使用DateAndTime.DateDiff方法计算年龄 一、计算年龄的方法 使用DateDiff方法计算系统时间与员工生日之间相隔的年…

续签KES证书

MiniO KES&#xff08;密钥加密服务&#xff09;是 MinIO 开发的一项服务&#xff0c;旨在弥合在 Kubernetes 中运行的应用程序与集中式密钥管理服务 &#xff08;KMS&#xff09; 之间的差距。中央 KMS 服务器包含所有状态信息&#xff0c;而 KES 在需要执行与获取新密钥或更新…

【AI Agent系列】【MetaGPT】7. 一句话订阅专属信息 - 订阅智能体进阶,实现一个更通用的订阅智能体

文章目录 0. 前置推荐阅读1. 本文内容2. 解析用户指令&#xff08;分析用户需求&#xff09;2.1 完整代码及注释2.2 运行结果 3. 利用大模型写爬虫代码3.1 对html内容进行精简3.2 利用大模型写爬虫代码3.3 补充代码&#xff0c;测试本节程序3.4 运行结果及踩坑3.4.1 运行结果3.…