详解VHDL如何编写Testbench

1.概述

仿真测试平台文件(Testbench)是可以用来验证所设计的硬件模型正确性的 VHDL模型,它为所测试的元件提供了激励信号,可以以波形的方式显示仿真结果或把测试结果存储到文件中。这里所说的激励信号可以直接集成在测试平台文件中,也可以从外部文件中加载。

一般而言,编写 Testbench 进行测试主要有下面四个步骤

  • (1)实例化需要测试的设计(DUT,Design Under Test);
  • (2)产生模拟激励(波形);
  • (3)将产生的激励加入到被测试模块并观察其输出响应;
  • (4)将输出响应与期望进行比较,从而判断设计的正确性。

其中,输出响应可以以波形方式显示或存储测试结果到文件中。

2.Testbench程序基本结构

通常 Testbench 的基本结构包括库的调用、程序包的调用、空实体、结构体描述。在结构体描述中,一般包含有被测试元件的声明、局部信号声明、被测试元件例化、激励信号的产生,如图所示。与一般的 VHDL 程序不同的是,Testbench 里面的实体为空。
在这里插入图片描述

2.1 被测试元件的声明方式

先说一个被测实体vote7,代码如下:

library ieee;
use ieee.std_logic_1164.all;entity vote7 is port(vt      : in  std_logic_vector(6 downto 0);result  : out std_logic);
end entity vote7;architecture rtl of vote7 is
beginprocess(vt)variable sum : integer range 0 to 7;beginsum := 0;for i in 0 to 6 loopif vt(i) = '1' thensum := sum + 1;if sum > 4 thenresult <= '1';elseresult <= '0';end if;end if;end loop;end process;
end architecture;

2.1.1 组件实例化

组件实例化是一种传统且常用的方法,特别适用于较早版本的VHDL(如VHDL-93)。该方法需要在测试平台中先声明一个组件,然后在架构中进行实例化。

步骤

  • 组件声明:在测试平台的架构声明部分(通常在architecture关键词之后)声明DUT的组件。

  • 实例化:在架构的主体部分使用component实例化DUT,并进行端口映射。
    示例代码

假设有一个被测实体vote7,其声明如下:

library ieee;
use ieee.std_logic_1164.all;entity tb_vote7 is
end entity tb_vote7;architecture Behavioral of tb_vote7 is-- 信号声明……-- 组件声明component vote7port(vt      : in  std_logic_vector(6 downto 0);result  : out std_logic);end component;begin-- DUT实例化DUT: vote7port map (vt      => vt,result  => result);-- 激励过程……
end architecture Behavioral;

2.1.2 直接实体实例化

直接实体实例化(也称为架构实例化)是VHDL-2002及更高版本中引入的一种更简洁的实例化方式。它不需要提前声明组件,直接引用实体和架构即可。

优点

  • 简洁:无需组件声明,减少了代码冗余。
  • 灵活:可以直接指定要使用的实体和架构。

使用与前述相同的vote7实体,测试平台采用直接实体实例化的方法如下:

library ieee;
use ieee.std_logic_1164.all;entity tb_vote7 is
end entity tb_vote7;architecture Behavioral of tb_vote7 is-- 信号声明……
begin-- DUT直接实例化DUT: entity work.vote7(rtl)port map (vt      => vt,result  => result);-- 激励过程……
end architecture Behavioral;

代码解析

  • DUT实例化:使用entity work.vote7(rtl)指定实体vote7和其架构rtl,然后进行端口映射。
  • 无需组件声明:省略了组件的预先声明,代码更加简洁。

3.激励信号的产生

激励信号产生的方式一般有两种,一种是以一定的离散时间间隔产生激励信号,另一种是基于实体的状态产生激励信号。需要注意的是,在 Testbench 程序中一定要对所有的激励信号赋初始值。下面通过实例,讲述激励信号的产生方法。

3.1 时钟信号的产生

时钟信号属于周期性出现的信号,是同步设计中最重要的信号之一。如图所示,时钟信号分为两类,即占空比为50%的对称时钟信号与占空比不是 50%的非对称时钟信号。

在这里插入图片描述
Testbench 中产生时钟信号方式有两种,

  • 一种是使用并行的信号赋值语句;
  • 一种是使用process进程。

下面分别通过两个例子来说明如何用这两种方法来产生所需的时钟信号。

【例】用并行信号赋值语句产生如图所示的 clk1clk2clk3 信号。
在这里插入图片描述
观察上图,我们发现 clk1 为对称时钟信号,其初始值可以在信号定义时赋值;clk2clk3为非对称时钟信号,其起始值可以在语句中赋值。这两种信号的产生方式有所不同,相对而言对称时钟信号的产生相对简单一些。

并行信号赋值语句的实现如下:

signal clk1:std_logic := '0';
signal clk2:std_logic;
signal clk3:std_logic;
……
clk1 <= not clk1 after clk_period/2;
clk2 <= '0' after clk_period/4 when clk2 = '1' else'1' after 3*clk_period/4 when clk2 = '0' else'1';
clk3 <= '0' after clk_period/4 when clk3 = '1' else'1' after 3*clk_period/4 when clk3 = '0' else'0';
……

【例】使用 process 进程产生如图所示的clk1clk2 信号。
在这里插入图片描述
观察上图,可以发现 clk1 为对称时钟信号,clk2 为非对称时钟信号,但这两种信号用 process 进程实现的方法基本一致。

process 进程实现如下:

signal clk1:std_logic;
signal clk2:std_logic;
……
clk1_gen:processconstant clk_period	:time	:= 40ns;--常量只在该进程中起作用beginclk1 <= '1';wait for clk_period/2;clk1 <= '0';wait for clk_period/2;end process;
clk2_gen:processconstant clk_period	:time	:= 20ns; --常量只在该进程中起作用beginclk2 <= '0';wait for clk_period/4;clk2 <= '1';wait for 3*clk_period/4;end process;
……	

3.2 复位信号的产生

Testbench中产生复位信号方式也是两种,一种是并行赋值语句实现,另一种是在进程中设定。下面用例加以说明。

【例 7-5】如图所示,请用并行信号赋值语句产生的reset1信号,用 process 进程产生reset2信号。
在这里插入图片描述
程序如下:

……
signal reset1:std_logic;
signal reset2:std_logic;
……
-- 并行信号赋值语句产生的reset1信号
reset1 <= '0','1' after 20 ns,'0' after 40ns;--用process进程产生reset2信号
reset2_gen:processbeginreset2  <= '0';wait for 20 ns;reset2 <= '1';wait for 40 ns;reset2 <= '0';wait;
end process;
……

3.3 使用delayed属性产生两相关性信号

delayed是VHDL的预定义属性,使用它可以产生两个相关性的信号。如果已经产生了一个时钟信号,在这个时钟信号的基础上,可以使用delayed来使已经产生的时钟信号延迟一点的时间,从而获得另一个时钟信号。

假设已经使用如下的语句定义了一个时钟信号W_CLK:

W_CLK<= '1' after 30 ns when W_CLK= '0' else'0' after 20 ns;

然后可以使用如下的延迟语句获得一个新的时钟信号DLY_W_CLK,它比W_CLK延迟了10 ns:

DLY_W_CLK <= W_CLK' delayed(10 ns);

以上两个时钟信号波形如图所示:

在这里插入图片描述
【例 】 如图所示,请编程实现信号 periodl,period2,要求用到 DELAYED 属性。

在这里插入图片描述
程序如下:

signal period1,period2:std_logic;
……
period1 <= '1' after 30 ns when period1 = '0' else'0' after 20 ns when period1 = '1' else'0';--利用delayed属性,由period1产生period2
period2 <= period1' delayed(10 ns);

3.4 一般激励信号

一般的激励信号通常在 process 进程中定义,而在 process 进程中一般需要使用 wait 语句。所定义的普通的激励信号常用来作模型的输入信号。

【例】 如图 7-18 所示,请编程产生信号 test vectorl 和 test vector2。
在这里插入图片描述
程序如下:

signal test_vector1:std_vector_logic(1 downto 0);
signal test_vector2:std_vector_logic(1 downto 0);
……
TB1:process
begintest_vector1 <= "01";wait for 10 ns;test_vector2 <= "10";wait for 20 ns;
end process;TB2:process
begintest_vector2 <= "01";wait for 10 ns;test_vector2 <= "10";wait;
end process;

【例 】 输入信号 test_abtest_sel 均为 2bit,试用 VHDL 产生这两个输入信号以覆盖所有的输入情况。输入信号向量 test_abtest_sel 均为 2bit,产生的输入情况共有(2x2)x(2x2)=16 种可能。

实现的程序如下

signal test_ab : std_logic_vector(1 downto 0);
signal test_sel:std_logic_vector(1 downto 0);double_loop:process
begintest_ab <= "00";test_sel <= "00";for i in 0 to 3 loopfor j in 0 to 3 loopwait for 10 ns;test_ab <= test_ab + 1;end loop;test_sel <= test_sel + 1;end loop;
end process;

程序对应的波形如图所示:
在这里插入图片描述

特别注意:如果同一个信号在两个进程中进行赋值,若在某些时间段内发生了冲突,就会出现不定状态,如下例所示。因此同一信号不允许在不同进程中赋值。

【例】同一个信号在两个进程中进行赋值,在某些时问段内发生了冲突,出现不定状态的情况。

程序如下:

……
signal test_vector:std_logic_vector(2 downto 0);
signal reset:std_logic;
……
gen_1:process
beginreset <= '1';wait for 100 ns;reset <= '0';test_vector <= "000";wait;
end process;gen_2:process
beginwait for 200 ns;test_vector <= "001";wait for 200 ns;test_vector <= "011";
end process;
……

对应的波形如图所示:

在这里插入图片描述

3.5 动态激励信号

动态激励信号,就是输入激励信号与被仿真的实体(DUT)的行为模型相关,即 DUT 的输入激励信号受模型的行为所影响。

如下信号的定义,模型的输入信号 sig_A 就和模型输出信号 count 相关。

process(count)
begincase count iswhen 2 =>sig_A <= '1' after 10 ns;when others =>sig_A <= '0' after 10 ns;end case;
end process;

3.6 测试矢量

在实际应用中,常常将一组固定的输入输出矢量值存储在一个常量表或一个 ASCI 文件中,然后将这些值应用到输入信号从而产生激励信号。这里所说的固定输入输出矢量值就称为测试矢量。矢量的值序列可以使用多维数组或使用多列记录来描述。

如下面的数据表存储了输入矢量:

constant no_of_bits:integer := 4;
constant no_of_vectors:integer := 5;
type table_type is array (1 to no_of_bits) of std_logic_vector(1 to no_of_vectors);
constant input_vectors:table_type := ("1001","1000","0010","0000","0110");signal inputs:std_logic_vector(1 to no_of_vectors);
signal A,B,C:std_logic;
signal D:std_logic_vector(0 to 1);

假设所测试的实体(DUT)具有4个输入:A、B、C和D信号,如果以一般的时间间隔应用测试矢量,则可以使用一个generate语句,例如

G1:for j in 1 to no_of_vectors generateinputs <= input_vectors(j) after (vector_period*j);
end generate;A <= inputs(1);B <= inputs(4);C <= inputs(1);D <= inputs(2 to 3);

如果将信号应用于任意时间间隔,则需要使用并行的信号赋值语句产生多个信号的波形,使用这种方法可以将一个矢量赋值给多个信号,如下面的代码:

inputs <= input_vector(1) after 10 ns;input_vector(2) after 25 ns;input_vector(3) after 30 ns;input_vector(4) after 32 ns;input_vector(5) after 40 ns;

4.高级Testbench编写

高级Testbench 是在简单 Testbench 基础上改进的,能够自动读入测试矢量文件、完成输出值和期望值的比较等功能,如图所示。相比简单Testbench,高级 Testbench 更显得智能化,也减少了人工分析的烦琐工作。

在这里插入图片描述

4.1 文件I/O的读写

仿真时,VHDL 允许设计人员从文件加载数据或将数据存储到文件中。比如用户定义的测试矢量可以保存在文件中,然后在仿真时从文件中读取这些测试矢量。另外,仿真的结果也可以保存在文件中。

VHDL 标准中的文件 I/O 主要是由 TEXTIO 程序包提供的,用于仿真且综合工具不能综合标准库 STD 中的 TEXTIO 定义的程序包只能使用 BIT 和 BIT_VECTOR 数据类型,其引用的格式为:

library std;
use std.textio.all;

如果要使用std_logicstd_logic_vector,则需要调用std_logic_textio,格式为:

library ieee;
use ieee.std_logic_textio.all;

4.1.1 TEXTIO介绍

TEXTIO 是 VHDL 标准库 STD 中的一个程序包(package)。在该包中定义了三个基本类型:LINE 类型、TEXT 类型以及 SIDE 类型。另外,还有一个子类型(subtype)WIDTH。此外,在该程序包中还定义了一些访问文件所必须的过程(procedure),如图所示。

在这里插入图片描述
其中,TEXT为ASCII文件类型。定义成TEXT类型的文件是长度可变的ASCII文件,需要注意的是VHDL’87 和 VHDL’93 在使用文件方面由较大的差异,在编译时注意选择对应的标准。

side只能有两种状态,即rightleft,分别表示将数据从左边还是从右边写入行变量。该类型主要是在TEXTIO程序包包含的过程中使用。

WIDTH为自然数的子类型。所谓子类型表示其取值范围是父类型范围的子集。

TEXTIO 也提供了基本的用于访问文本文件的过程。类似于 C++,VHDL 提供了重载功能,即完成相近功能的不同过程可以有相同的过程名,但其参数列表不同,或参数类型不同或参数个数不同。

TEXTIO 提供的基本过程有:

1.procedure READLINE(文件变量;行变量);

用于从指定文件读取一行数据到行变量中。

2.procedure WRITELINE(文件变量:行变量):

用于向指定文件写入行变量所包含的数据。

3.procedure READ(…);

可重载,用于从行变量中读取相应数据类型的数据。

4.procedure WRITE(…);

可重载,用于将数据写入行变量

4.1.2 文件基本操作

1.定义文件

TEXTIO 程序包中可操作的文件主要包含两大类:integertext

integer 文件中的数据是以二进制存取的,不能被人识别,只有 integer 型的数据能够存入这类文件。

text 文件是可以读取的 ASCI 码,可以被人识别。integer 、bit vector(x downto x)、string(x downto 1)、std logic_vector(x downto 0)及 bit 等类型都可以被存入此类文件。

对文件进行操作之前,需要对将要进行操作的文件进行定义,在 93 版的 VHDL 中,文件定义的方式如下:

FIE file handle: text open read mode is"目录十文件.后缀"---(输入文件的说明)
FIE file handle: text open write mode is"目录十文件.后缀"---(输出文件的说明)

在 87 版的 VHDL 中,文件定义的方式:

FIEL file handle: text is in"目录十文件.后缀"---(输入文件的说明)
FIEL file handle: text is out"目录十文件.后缀"---(输出文件的说明)

如果在支持 93 版的 VHDL 语言中使用了 87 版的格式,仿真时会提示:

warning: FIE declaration was written using 1076-1987 syntax.

2.打开文件

定义文件句柄后就可以在程序中打开指定文件,同时指定打开模式。93 版的 VHDL 可以使用 file open()进行文件打开操作,其中文件打开操作的函数使用方法如下:

file_open(fstatus,file_handle,filename)

其中,fstatus指示当前文件状态,但是在使用前首先得定义:

variable fatatus:file_open_status;

状态一般有四种,即open_ok,status_error,name_error,mode_error

file handle 即是上一步定义的文件句柄file handle

filename 是以双引号括起的文件名,如"datain.txt",也可以加上文件路径。openmode 是指打开该文件的模式,文件打开有read_mode,write mode,append_mode 三种。

3.读写文件

打开文件后就可以对文件进行读写操作,其语句格式如下:

--将文件中的一行数据读至行变量中。
realine(文件变量,行变量);--行变量中保存的数据取n位放至数据变量v中,n为数据变量v的数据位数。在此之前,需要定义好行变量和数据变量。
read(行变量,数据变量);--将一个数据写到某一行中。
write(行变量,数据变量);--起始位置为left 或 righ,字符数则表示数据变量写入到行变量后占的位宽。
write(行变量,数据变量,起始位置,字符数);--将行变量包含的数据写入到指定文件;
wwriteline(文件变量,行变量);

4.关闭文件

在文件读写完毕后,需使用 file)close(file handle)关闭文件。

如果想判断在文件操作中是否读取到文件的末尾,可以使用函数endfile(file_handle)进行判断,如果到达文件末尾将返回“真(true)”,否则返回“假(false)”。

下面举一个例子,使用了上面介绍的各种语法。

【例】文件I/O读写例程

library ieee;
library std;use std.textio.all;
use ieee.std_logic_textio.all;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;entity testin is
end entity testin;architecture rtl of testin is
beginprocess-- 定义text类型得文件句柄file file_out1,file_in:text; -- 定义文件状态指示变量variable fstatus1,fstatus2:file_open_status;variable count:integer := 5;variable stringdata:string(5 downto 1) := "SCUTE"; --string型variable vectordata:bit_vector(5 downto 0) := "001000";variable value:std_logic_vector(3 downto 0) := "1111";variable buf,buf1:line;begin--创建并打开文件file_open(fstatus1,file_out1,"DATAIN.TXT",write_mode);write(file_out1,string'("THE FIRST PAPAMETER IS ="));readline(input,buf);write(buf,count);writeline(file_out1,buf);wait for 20 ns;write(buf,string'(THE SECOND PAPAMETER IS = "));write(buf,value);writeline(file_out1,buf);wait for 20 ns;write(buf,string'("THE THIRD PAPAMETER IS = "));write(buf,vectordata);writeline(file_out1,buf);wait for 20 ns;write(buf,string'("THE FORTH PAPAMETER IS = "));write(buf,stringdata);writeline(file_out1,buf);write(file_out1,string'("END OF FILE"));file_close(file_out1);wait for 100 ns;file_open(fstatus1,file_out1,"DATAIN.TXT",read_mode);readline(file_out1,buf);writeline(output,buf);file_close(file_out1);wait for 100 ns;file_open(fstatus1,file_in,"STD_INPUT",read_mode);file_open(fstatus2,file_out1,"STD_OUTPUT",write_mode);readline(file_in,buf);writeline(file_out1,buf);wait;end process;
end rtl;

在modelsim中运行,控制台将做如下操作:

在这里插入图片描述
等待数秒后,在modelsim工程目录下将会新建一个“DATAIN.TXT”文本文档,打开文档其内容如图所示:

在这里插入图片描述

4.2 断言语句

断言语句(Assert)语句可以在仿真的过程中,检查一个条件并报告信息,一般用于程序调试与时序仿真时的人机对话,也是不可综合的语句。

断言语句的书写格式为:

assert<条件表达式>report<出错信息>severity<错误级别>;

其中,ASSERT 后的条件表达式为布尔表达式,用于模拟执行时的真假判断。若其值为“真”则跳过下面两个子句,继续执行后面的语句;若其值为“假”,则表示出错,于是执行 REPORT报告出错信息,同时由 SEVERITY 子句给出错误等级。

ASSERT 后的条件表达式由设计人员自行拟定,没有默认格式。断言语句里面的出错信息与错误等级也都由设计者自行设计,VHDL不自动生成这些信息。而且,REPORT 后的出错信息必须是字符串,需要用双括号括起来,若缺省出错信息,则系统默认输出错误信息报告为"Assertion Violation"。SEVERITY 后的错误级别要求是预定义的四种错误之一, 预定义的四种错误类型分别是:Note(通报)、Warning(警告)、Error(错误)、Failure(失败)。若缺省,则默认为 Error。

4.2.1 断言语句的使用方法

断言语句可以在实体、结构体以及进程中使用。下面通过一个例子初步介绍断言语句在仿真时的应用。

【例】用断言语句判断仿真的时间,如果当前时间为1000ns,则仿真完成,使用 ERROR严重级别终止仿真过程。

程序如下:

process
begin
assert(now <= 1000 ns)report "simulation completed successfully"severity error;
end process;

断言语句判断条件的判断结果为 FALSE,则执行后面的报告及严重级语句,否则跳过这些错误报告语句并继续执行。

放在进程内的断言语句叫顺序断言语句,它在进程内按照顺序执行。放在进程外部的断言语句叫并行断言语句。并行断言语句本质上等同于一个进程,该进程只对条件表达式给出的所有信号敏感。

如果把断言语句单独放在一个进程里面,则该进程称为断言进程。断言进程只能放在结构体里面,且不对任何信号进行赋值操作。下例就是一个断言进程语句。

【例】 使用 ASSERT 语句设定一个判断条件,以便对仿真的某个结果或值做出响应

……
process(q)
begin
assert(q /= "1001")report "the shifter gets the result!"severity error;
end process;

在上面的程序中,如果信号q等于“1001”,则终止仿真,并输出 The shifter gets the result!。

4.2.2 断言语句的应用实例

下面以一个简单的实例来讲述使用断言语句来响应一个仿真的过程。

【例】 4位加减计数器的仿真。所述4位加减计数器的位数为4位,且带有 CLR 清零端。当 DIR 信号为高电平时,计数器为加1计数器;当 DIR 信号为低电平时,为减1计数器。

4 位加减计数器的设计程序如下:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;entity counter is port(clk,clr,dir:in	std_logic;result	   :out	std_logic_vector(3 downto 0));
end entity counter;architecture rtl of counter is
signal tmp:std_logic_vector(3 downto 0);
begin
process(clk,clr)
beginif(clr = '1') thentmp <= "0000";elsif(clk' event and clk = '1') thenif(dir = '1') thentmp <= tmp + 1;elsetmp <= tmp - 1;end if;end if;
end process;result <= tmp;end rtl;

4位加减计数器的仿真程序如下:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned;entity tb_counter is
end entity tb_counter;atchitecture rtl of tb_counter issignal clk      :std_logic := '0';
signal clr		:std_logic := '0';
signal dir		:std_logic := '0';
signa result 	:std_logic_vector(3 downto 0);
constant clk_period:time := 40 ns;beginuut:entity work.counter(rtl)port map(clk => clk,clr => clr,dir => dir,result => result);clk_gen:process
beginclk <= '0';wait for clk_period/2;clk <= '1';wait for clk_period/2;
end process;TB:process
beginclr <= '1';dir <= '1';wait for 20 ns;clr <= '0';wait for 280 ns;dir <= '0';wait for 320 ns;wait;
end process;process(result)
beginassert(result /= "1001")report "THe counter gets to nine!"severity error;
end process;
end rtl;

仿真波形如下图所示:
在这里插入图片描述

当计数到“1001”时,在信息栏输出

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】单链表的使用

单链表的使用 1、基本概念2、链表的分类3、链表的基本操作a、单链表节点设计b、单链表初始化c、单链表增删节点**节点头插&#xff1a;****节点尾插&#xff1a;****新节点插入指定节点后&#xff1a;**节点删除&#xff1a; d、单链表修改节点e、单链表遍历&#xff0c;并打印…

浅谈某平台多场景下反爬虫与风控业务

文章目录 1. 写在前面2. 内容反爬3. 账号风控3. 接口验签 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致…

如何在网页端使用 IDE 高效地阅读 GitHub 源码?

如何在网页端使用 IDE 高效地阅读 GitHub 源码&#xff1f; 前言什么是 GitHub1s&#xff1f;使用 GitHub1s 阅读 browser-use 项目源码步骤 1: 打开 GitHub 项目页面步骤 2: 修改 URL 使用 GitHub1s步骤 3: 浏览文件结构步骤 4: 使用代码高亮和智能补全功能步骤 5: 快速跳转和…

Web Bluetooth API 开发记录

搞了一天的蓝牙串口协议被几个软件和AI带沟里面去了。 1.00001101-0000-1000-8000-00805f9b34fb 是spp协议。但是我用的称是使用的49535343-fe7d-4ae5-8fa9-9fafd205e455蓝牙低功耗spp协议 2.推荐一款软件Android-nRF-Connect github地址&#xff1a;https://github.com/Nor…

使用VS Code开发ThinkPHP项目

【图书介绍】《ThinkPHP 8高效构建Web应用》-CSDN博客 《ThinkPHP 8高效构建Web应用 夏磊 编程与应用开发丛书 清华大学出版社》【摘要 书评 试读】- 京东图书 ThinkPHP 8开发环境安装-CSDN博客 安装ThinkPHP项目的IDE 常用的集成开发环境&#xff08;IDE&#xff09;包括P…

开源轻量级文件分享服务Go File本地Docker部署与远程访问

???欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老…

Windows上缺少xaudio2_9.dll是什么原因?

一、文件丢失问题&#xff1a;Windows上缺少xaudio2_9.dll是什么原因&#xff1f; xaudio2_9.dll是DirectX音频处理库的一个组件&#xff0c;它支持游戏中的音频处理功能。当你在Windows系统上运行某些游戏或音频软件时&#xff0c;如果系统提示缺少xaudio2_9.dll文件&#xf…

缓存管理自动化:JuiceFS 企业版 Cache Group Operator 新特性发布

近期&#xff0c;JuiceFS 企业版推出了 Cache Group Operator&#xff0c;用于自动化创建和管理缓存组集群。Operator 是一种简化 Kubernetes 应用管理的工具&#xff0c;它能够自动化应用程序的生命周期管理任务&#xff0c;使部署、扩展和运维更加高效。 在推出 Operator 之前…

SCSA:探索空间与通道注意力之间的协同效应

文章目录 摘要1 引言2 相关工作2.1 多语义空间信息2.2 注意力分解 3 方法3.1 共享多语义空间注意力&#xff1a;空间与通道分解3.2 渐进式通道自注意力3.3 协同效应3.4 注意力机制的整合 4 实验4.1 实验设置4.2 图像分类4.3 目标检测4.4 分割4.5 消融研究 5 可视化与分析5.1 注…

Grok 2.0:马斯克的大模型挑战ChatGPT,AI竞争再升级

引言&#xff1a;马斯克Grok 2.0的横空出世 在人工智能&#xff08;AI&#xff09;领域&#xff0c;竞争从未停止。随着大型语言模型&#xff08;LLM&#xff09;的快速发展&#xff0c;各大科技巨头纷纷推出自己的AI模型&#xff0c;试图在激烈的竞争中占据领先地位。最近&am…

基于Spring Boot的宠物领养系统的设计与实现(代码+数据库+LW)

摘 要 如今社会上各行各业&#xff0c;都在用属于自己专用的软件来进行工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。互联网的发展&#xff0c;离不开一些新的技术&#xff0c;而新技术的产生往往是为了解决现有问题而产生的。针对于宠物领…

安卓15预置第三方apk时签名报错问题解决

有同事反馈集成apk时安装失败 PackageManager: Failed to scan /product/app/test: No APK Signature Scheme v2 signature in package /product/app/test/test.apk 查看编译后的apk签名信息 DOES NOT VERIFY ERROR: JAR signer CERT.RSA: JAR signature META-INF/CERT.SF indi…

从0入门自主空中机器人-2-1【无人机硬件框架】

关于本课程&#xff1a; 本次课程是一套面向对自主空中机器人感兴趣的学生、爱好者、相关从业人员的免费课程&#xff0c;包含了从硬件组装、机载电脑环境设置、代码部署、实机实验等全套详细流程&#xff0c;带你从0开始&#xff0c;组装属于自己的自主无人机&#xff0c;并让…

实现某海外大型车企(T)Cabin Wi-Fi 需求的概述 - 4

大家好&#xff0c;我是Q&#xff0c;邮箱&#xff1a;1042484520qq.com。 今天我们在上几讲的基础上再扩展下 Cabin Wi-Fi 的功能需求&#xff0c;讲讲如何使能 5G TCU Wi-Fi STA Bridge 模式。 参考&#xff1a; 实现某海外大型车企&#xff08;T&#xff09;Cabin Wi-Fi 需求…

2024 年最新 windows 操作系统搭建部署 nginx 服务器应用详细教程(更新中)

nginx 服务器概述 Nginx 是一款高性能的 HTTP 和 反向代理 服务器&#xff0c;同时是一个 IMAP / POP3 / SMTP 代理服务器。Nginx 凭借其高性能、稳定性、丰富的功能集、简单的配置和低资源消耗而闻名。 浏览 nginx 官网&#xff1a;https://nginx.org/ Nginx 应用场景 静态…

C 实现植物大战僵尸(二)

C 实现植物大战僵尸&#xff08;二&#xff09; 前文链接&#xff0c;C 实现植物大战僵尸&#xff08;一&#xff09; 五 制作启动菜单 启动菜单函数 void startUI() {IMAGE imageBg, imgMenu1, imgMenu2;loadimage(&imageBg, "res/menu.png");loadimage(&am…

Android笔记(四十一):TabLayout内的tab不滚动问题

背景 假设二级页面是上面图片的布局&#xff0c;当进来时TabLayout和ViewPager2绑定完就马上调setCustomItem&#xff0c;跳转到最后一个tab页面时&#xff0c;会发现tab不滚动&#xff0c;手动滑一下ViewPager2时才会滚动tab到正确的位置 原因分析 调用TabLayoutMediator.at…

域内的三种委派方式

域委派&#xff1a;使得上游服务能使用用户凭据访问下游服务&#xff0c;使得下游服务根据域用户判断权限&#xff0c;例如&#xff1a; web 用户 hack ---------------访问------------------> web 服务器 &#xff08; www-data 域服务账户运行&#xff09;-------------…

GEE云计算、多源遥感、高光谱遥感技术蓝碳储量估算;红树林植被指数计算及提取

大气温室气体浓度不断增加&#xff0c;导致气候变暖加剧&#xff0c;随之会引发一系列气象、生态和环境灾害。如何降低温室气体浓度和应对气候变化已成为全球关注的焦点。海洋是地球上最大的“碳库”,“蓝碳”即海洋活动以及海洋生物&#xff08;特别是红树林、盐沼和海草&…

module ‘django.db.models‘ has no attribute ‘FieldDoesNotExist‘

module ‘django.db.models’ has no attribute ‘FieldDoesNotExist’ xadmin报错 原因 django与xadmin版本不匹配。 django==3.2.7 xadmin-django==3.0.2解决方案 在xadmin/view/edit.py的388行改为 from django.core import exceptions if self.request_method ==