ZYNQ笔记(十四):基于 BRAM 的 PS、PL 数据交互

版本:Vivado2020.2(Vitis)

实验任务:

        PS 将字符串数据写入BRAM,再将数据读取出来;PL 从 BRAM 中读取数据,bing。通过 ILA 来观察读出的数据,与前面串口打印的数据进行对照(检查是否正确读出)。

目录

一、介绍

1. BRAM(Block RAM)

2. AXI BRAM Controller         

二、硬件设计

1. 整体系统框图

2. 配置 BRAM

3. 配置 AXI BRAM Controller

4. 自定义IP核(pl_bram_rd)

5. 最终 bd 设计

三、软件设计

四、效果


一、介绍

1. BRAM(Block RAM)

        BRAM(Block RAM) 是 Xilinx Zynq SoC 内部的嵌入式高速静态存储器(SRAM),分布在 FPGA 可编程逻辑(PL)部分,但也可被处理器系统(PS)直接访问。主要特点:

特性说明
高速访问1~2 个时钟周期完成读写,远快于外部 DDR(通常需数十周期)
低功耗低功耗,片上存储无需外部总线交互
双端口架构支持双端口(可独立读写),可配置为单端口或真双端口模式
存储容量每块 36Kb(可拆分为 2×18Kb),器件集成数量:几十至数百块
初始化方式支持 COE 文件预加载或运行时写入
典型用途高速缓存、查找表、PS-PL 数据共享、跨时钟域同步
ECC 支持可选校验功能

2. AXI BRAM Controller         

        AXI BRAM Controller 是 Xilinx FPGA 和 Zynq SoC 中常用的 IP 核,用于通过 AXI 总线协议 访问 BRAM (Block RAM)。它允许 PS 端或 DMA 控制器通过标准 AXI 接口高效读写 BRAM,同时支持 PL(FPGA 逻辑)和 PS(处理器系统)之间的数据共享。

特性说明
支持的 AXI 协议AXI4(高性能)、AXI4-Lite(简化版)
数据位宽32/64/128/256/512 位(需匹配 BRAM 配置)
突发传输AXI4 支持突发(Burst)读写,提升吞吐量
ECC 支持可选,增加数据校验功能
低延迟配置可配置访问延迟
多主设备支持多个 Master(如 CPU + DMA)可共享同一 BRAM

二、硬件设计

1. 整体系统框图

        PS端:除了 基本的 DDR 和 UART 还使用到了 AXI 接口,因此如时钟、复位、AXI接口 都需要保留和配置。 

        PL 端:存储数据的 BRAM 的 IP 核、实现对 BRAM 写入数据的 AXI BRAM 控制器 IP 核、读取 BRAM IP 核数据的自定义的 IP 核(pl_bram_rd)。

2. 配置 BRAM

        即添加 Block Memory Generator IP 核,配置如下:

        BRAM IP 核支持两种模式,一种是独立模式(Stand Alone),在此模式下,可以自由配置 RAM 的数 据深度和宽度;另一种是 BRAM 控制器模式(BRAM Controller),在此模式下,地址和数据默认为 32 位, 由于本次实验添加了 BRAM 控制器 IP 核,因此 BRAM 模式选择 BRAM 控制器模式。

        存储类型(Memory Type)设置为“True Dual Port RAM” 真双端口 RAM。一端连接 PL 读 BRAM IP 核,另一端连接 BRAM 控制器。

        来到其他选项:取消使能安全电路(如果勾选使能安全电路,BRAM 端口会增加 rst_busy 端口,用于表示何时可以访问 BRAM。)

        在 AXI BRAM Controller 模式下,BRAM 的 we写使能信号为 4 位,这是为了支持 按字节写入(Byte-Write)功能,允许对 32 位数据(4 字节)中的特定字节进行单独写入,而无需覆盖整个字。以下是详细说明:

  • AXI 总线特性:AXI 协议支持字节级写入(通过 WSTRB 信号),因此 BRAM Controller 需将 AXI 的字节使能信号转换为 BRAM 的 we[3:0]

  • 数据对齐:32 位 BRAM 数据(4 字节)中,每个 we 位对应一个字节的写使能:

    • we[0]:控制 字节 0(数据位 [7:0]

    • we[1]:控制 字节 1(数据位 [15:8]

    • we[2]:控制 字节 2(数据位 [23:16]

    • we[3]:控制 字节 3(数据位 [31:24]

3. 配置 AXI BRAM Controller

        AXI Protocol(AXI 协议)选择的是 AXI4_Lite,本实验只需发送一段开源电子网网址的字符串,因此选择适用于低吞吐率存储映射的 AXI4_Lite 接口即可满足传输需求。

        Data Width(数据位宽)固定为默认的 32 位,由于 AXI4 总线为字节寻址,因此在映射到 BRAM 地址时,需要按 4 字节寻址。

        BRAM 接口数 1,本次 BRAM 控制器只需要读写 BRAM 的一个端口,

        此外 Memory Depth(存储深度)不可以设置,寻址 BRAM 的存储深度是在 Address Editor 里设置。读取延时默认1个时钟周期。ECC 用于数据错误纠正与检查,不使能。所有配置如下:

4. 自定义IP核(pl_bram_rd)

        自定义 IP 核的设计和封装可参考:ZYNQ笔记(六):自定义IP核-LED呼吸灯 。可以直接点击菜单栏的 “Tools”,选择 “Creat and Package New IP…” 在当前工程目录快速进行自定义 IP 核的创建,如下图所示:

        需要注意创建的是 带 AXI 接口的 IP 

        IP 默认在工程目录的上一级目录创建目录ip_repo (/../ip_repo) ,这里去掉一个点让他创建在当前工程目录下(/./ip_repo) :

        AXI 接口类型设置为 Lite 和前面保持一致,接口模式为从。Next、Finish。

        在IP Catolog 内找到 整个自定义 IP pl_bram_rd,右键进行编辑。pl_bram_rd 里面所添加的自定义verilog模块是直接使用的正点原子的 bram_rd 模块,模块引出了 bram 接口并实现了对 bram 的读操作,控制信号在例化时相依分配寄存器即可,这样 PS 端通过读相应寄存器实现对 PL 端 自定义IP核 pl_bram_rd 的控制。

bram_rd 模块

/*BRAM读数据 模块例化bram_rd bram_rd(.clk            (S_AXI_ACLK ), //时钟信号.rst_n          (S_AXI_ARESETN), //复位信号//PS端输入的控制信号(分配三个寄存器进行控制).start_rd       (slv_reg0[0]), //读开始信号(上升沿有效).start_addr     (slv_reg1   ), //读开始地址  .rd_len         (slv_reg2   ), //读数据的长度//BRAM端口     .ram_clk        (ram_clk    ), //RAM时钟.ram_rst        (ram_rst    ), //RAM复位信号,高有效.ram_en         (ram_en     ), //RAM工作使能信号.ram_we         (ram_we     ), //RAM写使能信号.ram_addr       (ram_addr   ), //RAM地址.ram_wr_data    (ram_wr_data), //RAM要写入的数据.ram_rd_data    (ram_rd_data)  //RAM中读出的数据);
*///模块实现对BRAM的读取操作(BRAM为控制模式为 BRAM Contrulor 控制)
module bram_rd(input                clk        , //时钟信号input                rst_n      , //复位信号//控制信号input                start_rd   , //读开始信号(上升沿有效)input        [31:0]  start_addr , //读开始地址  input        [31:0]  rd_len     , //读数据的长度//BRAM端口output               ram_clk    , //RAM时钟output               ram_rst    , //RAM复位信号,高有效output  reg          ram_en     , //RAM工作使能信号output  reg  [3:0]   ram_we     , //RAM写使能信号output  reg  [31:0]  ram_addr   , //RAM地址output  reg  [31:0]  ram_wr_data, //RAM要写入的数据input        [31:0]  ram_rd_data  //RAM中读出的数据
);
reg  [1:0]   flow_cnt;//流程计数
reg          start_rd_d0;
reg          start_rd_d1;assign ram_rst = 1'b0;
assign ram_clk = clk ;//读开始信号上升沿标志信号
wire   pos_start_rd; 
assign pos_start_rd = ~start_rd_d1 & start_rd_d0;//延时两拍,采start_rd信号的上升沿
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginstart_rd_d0 <= 1'b0;   start_rd_d1 <= 1'b0; endelse beginstart_rd_d0 <= start_rd;   start_rd_d1 <= start_rd_d0;     end
end//判断读开始信号,从RAM中读出数据
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginflow_cnt    <= 2'd0;ram_en      <= 1'b0;ram_we      <= 4'd0;ram_addr    <= 32'd0;end//通过计数实现的简单状态机else begincase(flow_cnt)2'd0 : beginif(pos_start_rd) beginram_en   <= 1'b1;ram_addr <= start_addr;flow_cnt <= flow_cnt + 2'd1;endend2'd1 : beginif(ram_addr - start_addr == rd_len - 4) begin  //数据读完(读完差值为rd_len-4)ram_en   <= 1'b0;flow_cnt <= flow_cnt + 2'd1;endelseram_addr <= ram_addr + 32'd4; //地址累加4(,一次读32位4个字节,一个字节对应一个地址)end2'd2 : beginram_addr <= 32'd0; flow_cnt <= 2'd0;endendcaseend
endendmodule

        接着就是把 bram_rd 模块例化到 官方AXI 接口模块中并添加端口定义:

AXI接口模块例化、添加端口定义:

AXI 接口顶层模块添加端口定义:

        接着保存后综合,并打包IP核(参考:ZYNQ笔记(六):自定义IP核-LED呼吸灯 )。注意再PORT 栏部分,为了使 IP 核最后在 bd 视图简洁规范,这里需要对自定义的端口封装成一个BRAM的总线接口并匹配端口类型(也可以不设置,不过结果就是db视图中IP核自定义端口全部罗列在视图上不能缩略为接口,同时运行自动连线时不能自动连接BRAM,需要手动连线)

        找到端口,可能有端口被纳入到其他组里面,选中右键 Remove Interface 把它移出来即可:

        选中所有自定义端口右键添加总线接口,接口定义设置为官方的bram接口,自己进行命名,模式选“Master”主模式(因为是要读写BRAM模块)

        为自定义的端口添加接口映射(左右对应选中点击Map Ports):

        最后添加自动计算参数,这里直接全部添加过去(Vivado 根据连接的总线或关联 IP 自动推导,这里没有进行特定的设置,直接选“自动”即可)

       完成后PORT栏可以查看接口情况,GUI视图栏也可以看到这些端口被封装为一个接口了:

        最后打包IP即可。注意 : 在Vitis 开发环境下,需要修改自定义IP核的 Makefile 文件,如果不修改,当包含该 IP 的硬件(xsa)文件导出 到 vitis 后,对 vitis 工程进行编译就会报错,报错信息为“xxx.h: No such file or directory”。因此需要在使用该 IP 前完成修改: Makefile 修改方法

5. 最终 bd 设计

        添加完自定义IP核后可以运行自动连接,为了保险起见手动设置要自定义IP核要连接的 BRAM 避免出错。

        接着需要设置地址映射,设置地址范围也会间接定义 BRAM 的大小。这里读/写同一 bram 就都设置为 4K(单位字节),也定义 BRAM 深度大小为 1K (BRAM 数据位宽32位 4字节)。BRAM 控制器 和 自定义 IP(BRAM读控制器) 的地址范围不能重合。

        除了定义 BRAM 大小,两个 bram 控制器需要设置映射地址和范围是为了定义可访问的 BRAM 地址,确保模块能够准确读写数据。尽管已经通过连接明确了物理关系(接口连接),地址映射仍然是必要的,实际设置的是 BRAM 的映射地址,而非 BRAM 控制器本身,以确保控制器能正确进行数据访问。      

        最后整体 bd 设计部分如图所示:设计检查、Generate Output Products、 Create HDL Wrapper、管脚约束(没有PL管脚,忽略这一步)、Gnerate Bitstream、Export Hardware(包含比特流文件)、启动Vitis

三、软件设计

#include "xil_printf.h"
#include "xparameters.h"
#include "pl_bram_rd.h"  	//自定义IP头文件(包含对IP寄存器进行读写的函数)
#include "xbram.h"			//AXI BRAM Controller IP头文件(包含使用该IP进行BRAM读写的函数)
#include "stdio.h"
#include "sleep.h"//===================用户自定义宏===================//
#define BRAM_BASEADDR       XPAR_BRAM_0_BASEADDR	//BRAM器件(BRAM IP核) 基地址#define BRAM_RD_IP			XPAR_PL_BRAM_RD_0_S00_AXI_BASEADDR 	//自定义IP核(pl_bram_rd)寄存器基地址
#define BRAM_RD_START_RD	PL_BRAM_RD_S00_AXI_SLV_REG0_OFFSET 	//start_rd  对应寄存器(slv_reg0)地址偏移量(pl_bram_rd.h中查看)
#define BRAM_RD_STRAT_ADDR	PL_BRAM_RD_S00_AXI_SLV_REG1_OFFSET 	//start_addr对应寄存器(slv_reg1)地址偏移量
#define BRAM_RD_RD_LEN		PL_BRAM_RD_S00_AXI_SLV_REG2_OFFSET 	//rd_len    对应寄存器(slv_reg2)地址偏移量#define STRAT_ADDR 			0	//读起始地址(从0开始)
#define BRAM_DATA_BYTE 		4 	//BRAM 数据字节数(32位宽 4字节)//===================函数变量声明===================//
void BRAM_Ctrl_Wr();	//BRAM控制器写数据
void BRAM_Ctrl_Rd();	//BRAM控制器读数据
void PL_BRAM_RD_Rd();	//自定义IP核(pl_bram_rd)读数据int  wr_len;				//写入数据长度
char wr_buff[50] = "Hello"; //写入数据(字符数组)
char rd_buff[50] = "";		//读出数据//======================主函数======================//
int main()
{while(1){xil_printf("Write Char Data : %s\r\n", wr_buff);  // 打印待写入数据数据wr_len = strlen(wr_buff);	//获取字符串长度BRAM_Ctrl_Wr();				//BRAM控制器写数据BRAM_Ctrl_Rd();				//BRAM控制器读数据PL_BRAM_RD_Rd();			//自定义IP核(pl_bram_rd)读数据sleep(3);					//每隔3秒执行一次}return 0;
}//================BRAM控制器写数据================//
void BRAM_Ctrl_Wr()
{//用XBram_WriteReg一次写入一个字符,用for循环遍历写入所有字符for(int i=0; i<wr_len; i++){//XBram_WriteReg(BRAM基地址,偏移量,写入数据),这里一次偏移4字节是因为BRAM数据位宽32位4字节,不过每次只写1个字符XBram_WriteReg(BRAM_BASEADDR, i*BRAM_DATA_BYTE, wr_buff[i]);}xil_printf("BRAM Write done! \r\n");
}//================BRAM控制器读数据================//
void BRAM_Ctrl_Rd()
{xil_printf("Read Char Data : ");//用XBram_WriteReg一次读取一个字符,用for循环遍历读取所有字符for(int i=0; i<wr_len; i++){//XBram_ReadReg(BRAM基地址,偏移量)rd_buff[i] = XBram_ReadReg(BRAM_BASEADDR, i*BRAM_DATA_BYTE);xil_printf("%c",rd_buff[i]);}xil_printf("\r\n");
}//===========自定义IP核(pl_bram_rd)读数据===========//
void PL_BRAM_RD_Rd()
{PL_BRAM_RD_mWriteReg(BRAM_RD_IP, BRAM_RD_RD_LEN, wr_len*BRAM_DATA_BYTE); 			//设置读数据长度PL_BRAM_RD_mWriteReg(BRAM_RD_IP, BRAM_RD_STRAT_ADDR, STRAT_ADDR*BRAM_DATA_BYTE); 	//设置读起始地址//产生读开始信号有效上升沿PL_BRAM_RD_mWriteReg(BRAM_RD_IP, BRAM_RD_START_RD, 1); 			//拉高读开始信号PL_BRAM_RD_mWriteReg(BRAM_RD_IP, BRAM_RD_START_RD, 0); 			//拉低读开始信号
}

四、效果

        PS 端:先打印待写入数据,写入后将数据读取并打印。可看到前后一致说明写入数据无误

        PL 端:要用到 ILA 查看 自定义IP 核读取的数据是否正确,手动添加一个 ILA IP 核并在system_warper 中进行例化(修改保存后需要重新走一遍导出xac文件之前的流程)。

        不过这样到最后并没有实现PL到PS的数据传输,可以考虑自定义IP核加一个中断输出,并重新修改功能,实现读取数据后再接着最后一个地址开始,再把数据写到BRAM上,然后产生一个高脉冲信号(中断信号)时PS端得到中断开读数据检查两端数据是否一致:参考文章
ZYNQ—BRAM全双工PS_PL数据交互(开源) 

        关于自定义IP核的中断(BD设计里面需要给ZYNQ添加中断端口),我这里也有一个可参考的模板,中断类型位上升沿中断:

#include "xil_printf.h"
#include "xparameters.h"
#include "pl_bram_rd.h"  	//自定义IP头文件(包含对IP寄存器进行读写的函数)
#include "xbram.h"			//AXI BRAM Controller IP头文件(包含使用该IP进行BRAM读写的函数)
#include "xscugic.h"
#include "stdio.h"//===================用户自定义宏===================//
#define BRAM_BASEADDR       XPAR_BRAM_0_BASEADDR	//BRAM器件(BRAM IP核) 基地址#define BRAM_RD_IP			XPAR_PL_BRAM_RD_0_S00_AXI_BASEADDR 	//自定义IP核(pl_bram_rd)寄存器基地址
#define BRAM_RD_START_RD	PL_BRAM_RD_S00_AXI_SLV_REG0_OFFSET 	//start_rd  对应寄存器(slv_reg0)地址偏移量(pl_bram_rd.h中查看)
#define BRAM_RD_STRAT_ADDR	PL_BRAM_RD_S00_AXI_SLV_REG1_OFFSET 	//start_addr对应寄存器(slv_reg1)地址偏移量
#define BRAM_RD_RD_LEN		PL_BRAM_RD_S00_AXI_SLV_REG2_OFFSET 	//rd_len    对应寄存器(slv_reg2)地址偏移量#define STRAT_ADDR 			0	//读起始地址(从0开始)
#define BRAM_DATA_BYTE 		4 	//BRAM 数据字节数(32位宽 4字节)#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID	  	//宏定义中断控制器(GIC)ID
#define BRAM_RD_IP_ID  		XPAR_PL_BRAM_RD_0_DEVICE_ID       	//自定义IP核(pl_bram_rd)器件ID
#define BRAM_RD_IP_INTR_ID	XPAR_FABRIC_PL_BRAM_RD_0_INTR_INTR 	//宏定义GPIO中断号(每个器件都有中断号,xparameters.h查询)//===================函数变量声明===================//
XScuGic Intc;			//中断控制器驱动实例void BRAM_Ctrl_Wr ();	//BRAM控制器写数据
void BRAM_Ctrl_Rd ();	//BRAM控制器读数据
void PL_BRAM_RD_Rd();	//自定义IP核(pl_bram_rd)读数据
void Set_Intr_Sys(XScuGic *GicInstancePtr, u16 DeviceIntrID);    //建立中断系统
void IntrHandler();		//中断处理函数int  wr_len;		      //写入数据长度
char wr_buff[] = "ABC"; //写入数据(字符数组)//======================主函数======================//
int main()
{//建立中断系统Set_Intr_Sys(&Intc, BRAM_RD_IP_INTR_ID);wr_len = strlen(wr_buff);	//获取字符串长度BRAM_Ctrl_Wr ();			//BRAM控制器写数据BRAM_Ctrl_Rd ();			//BRAM控制器读数据PL_BRAM_RD_Rd();			//自定义IP核(pl_bram_rd)读写数据while(1)return 0;
}//================BRAM控制器写数据================//
void BRAM_Ctrl_Wr()
{//用XBram_WriteReg一次写入一个字符,用for循环遍历写入所有字符for(int i=0; i<wr_len; i++){//XBram_WriteReg(BRAM基地址,偏移量,写入数据),这里一次偏移4字节是因为BRAM数据位宽32位4字节,不过每次只写1个字符//读写地址=基地址+偏移量XBram_WriteReg(BRAM_BASEADDR, i*BRAM_DATA_BYTE, wr_buff[i]);}xil_printf("BRAM Write Finish! \r\n");
}//================BRAM控制器读数据================//
void BRAM_Ctrl_Rd()
{int r_data;xil_printf("Read the Written Data : ");//用XBram_WriteReg一次读取一个字符,用for循环遍历读取所有字符for(int i=0; i<wr_len; i++){//XBram_ReadReg(BRAM基地址,偏移量)//读写地址=基地址+偏移量r_data = XBram_ReadReg(BRAM_BASEADDR, i*BRAM_DATA_BYTE);xil_printf("%c",r_data);}xil_printf("\r\n");
}//===========自定义IP核(pl_bram_rd)读数据===========//
void PL_BRAM_RD_Rd(u32 addr)
{xil_printf("START the custom IP: \r\n");PL_BRAM_RD_mWriteReg(BRAM_RD_IP, BRAM_RD_RD_LEN, wr_len*BRAM_DATA_BYTE); 		 //设置读数据长度PL_BRAM_RD_mWriteReg(BRAM_RD_IP, BRAM_RD_STRAT_ADDR, STRAT_ADDR*BRAM_DATA_BYTE); //设置读起始地址//产生读开始信号有效上升沿PL_BRAM_RD_mWriteReg(BRAM_RD_IP, BRAM_RD_START_RD, 1); 	//拉高读开始信号PL_BRAM_RD_mWriteReg(BRAM_RD_IP, BRAM_RD_START_RD, 0);	//拉低读开始信号
}//===========================中断处理函数===========================//
void IntrHandler(void *CallbackRef)
{int r_data;xil_printf("Interrupt Detected , Start Read BRAM :\r\n");//clear interrupt status//PL_BRAM_RD_mWriteReg(PL_RAM_BASE, PL_RAM_CTRL , INTRCLR_MASK) ;//BRAM控制器读数据for(int i=0; i<wr_len*2; i++){//XBram_ReadReg(BRAM基地址,偏移量)//读写地址=基地址+偏移量r_data = XBram_ReadReg(BRAM_BASEADDR, i*BRAM_DATA_BYTE);xil_printf("Address:%d	Data:%c \r\n", i*BRAM_DATA_BYTE, r_data);}xil_printf("\r\n");
}//===========================建立中断系统===========================//
/* 建立中断系统,使能自定义IP核终端输出信号的上升沿产生中断* @param GicInstancePtr 是指向 XScuGic 驱动实例的指针* @param DeviceIntrID   是器件中断 ID*/
void Set_Intr_Sys(XScuGic *GicInstancePtr, u16 DeviceIntrID)
{//定义中断控制器配置信息(指针类型)XScuGic_Config * IntcConfig;//根据中断控制器ID,查找GIC配置信息(Generic Interrupt Controller(通用)中断控制器)IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);//初始化中断控制器驱动XScuGic_CfgInitialize(GicInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);//设置并打开中断异常处理功能Xil_ExceptionInit();Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,GicInstancePtr);//为GPIO中断设置中断处理函数(IntrHandler为自己编写的中断函数)XScuGic_Connect(GicInstancePtr, DeviceIntrID,(Xil_ExceptionHandler)IntrHandler, (void *) NULL); //自定义IP核无实例NULL//使能处理器中断Xil_ExceptionEnable();//设置中断优先级、触发类型(优先级从最高0开始步长8最大为248,0xA0=160优先级中等;触发类型:上升沿)XScuGic_SetPriorityTriggerType(GicInstancePtr, DeviceIntrID, 0xA0 , 0x3);//使能来自于器件的中断XScuGic_Enable(GicInstancePtr, DeviceIntrID);
}

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

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

相关文章

Python-Django系列—部件

部件是 Django 对 HTML 输入元素的表示。部件处理 HTML 的渲染&#xff0c;以及从对应于部件的 GET&#xff0f;POST 字典中提取数据。 内置部件生成的 HTML 使用 HTML5 语法&#xff0c;目标是 <!DOCTYPE html>。例如&#xff0c;它使用布尔属性&#xff0c;如 checked…

【Leetcode 每日一题】2799. 统计完全子数组的数目

问题背景 给你一个由 正 整数组成的数组 n u m s nums nums。 如果数组中的某个子数组满足下述条件&#xff0c;则称之为 完全子数组 &#xff1a; 子数组中 不同 元素的数目等于整个数组不同元素的数目。 返回数组中 完全子数组 的数目。 子数组 是数组中的一个连续非空序…

卷积神经网络(二)

1 卷积运算的两个问题&#xff1a; 1.1 图像边缘信息使用少 边缘的像素点可能只会被用一次或者2次&#xff0c;中间的会用的更多。 1.2 图像被压缩 5*5的图像&#xff0c;如果经过3*3的卷积核后&#xff0c;大小变成3*3的。 N*N的图像&#xff0c;果经过F*F的卷积核后&#x…

组网技术-DHCP服务器,RIP协议,OSPF协议

1.DHCP Server提供三种IP地址分配策略&#xff1a; 手工分配地址 自动分配地址 n 动态分配地址 2.DHCP报文类型 DHCP DISCOVER(广播)&#xff1a;用于寻址DHCP Server DHCP OFFER&#xff08;单播&#xff09;&#xff1a;携带分配给客户端的IP地址 DHCP REQUEST&#xff08;…

反爬策略应对指南:淘宝 API 商品数据采集的 IP 代理与请求伪装技术

一、引言​ 在电商数据驱动决策的时代&#xff0c;淘宝平台海量的商品数据极具价值。然而&#xff0c;淘宝为保障平台安全和用户体验&#xff0c;构建了严密的反爬体系。当采集淘宝 API 商品数据时&#xff0c;若不采取有效措施&#xff0c;频繁的请求极易触发反爬机制&#x…

学习笔记(算法学习+Maven)

单调队列优化多重背包 #include <bits/stdc.h> using namespace std; const int M 2010; const int N 20010; int q[N]; int hh 0, tt -1; int f[N]; int g[N]; int v[M], w[M], s[M]; int n, m; int main() { cin >> n >> m; for (int i 1; …

WPF之项目创建

文章目录 引言先决条件创建 WPF 项目步骤理解项目结构XAML 与 C# 代码隐藏第一个 "Hello, WPF!" 示例构建和运行应用程序总结相关学习资源 引言 Windows Presentation Foundation (WPF) 是 Microsoft 用于构建具有丰富用户界面的 Windows 桌面应用程序的现代框架。它…

JAVAEE初阶01

个人主页 JavaSE专栏 JAVAEE初阶01 操作系统 1.对下&#xff08;硬件&#xff09;管理各种计算机设备 2.对上&#xff08;软件&#xff09;为各种软件提供一个稳定的运行环境 线程 运行的程序在操作系统中以进程的形式存在 进程是系统分配资源的最小单位 进程与线程的关…

HTML快速入门-4:HTML <meta> 标签属性详解

<meta> 标签是 HTML 文档头部&#xff08;<head> 部分&#xff09;的重要元素&#xff0c;用于提供关于文档的元数据&#xff08;metadata&#xff09;。这些数据不会直接显示在页面上&#xff0c;但对浏览器、搜索引擎和其他服务非常重要。 常用属性 1. name 和 …

前端基础之《Vue(12)—插件封装》

一、插件封装 1、在Vue生态中&#xff0c;除了Vue本身&#xff0c;其它所有的与Vue相关的第三方包&#xff0c;都是插件 例子&#xff1a; import VueRouter form vue-router Vue.use(VueRouter) // 注册插件 2、如何封装Vue插件 &#xff08;1&#xff09;第一种写法 const…

TCP基础题:音乐播放列表管理系统

需求描述 服务器端 创建一个 TCP 服务器&#xff0c;监听本地的 9999 端口&#xff0c;支持多个客户端连接。维护一个音乐播放列表&#xff0c;每个音乐条目包含歌曲名称、歌手、时长等信息。能够处理客户端的以下请求&#xff1a; 添加音乐到播放列表&#xff1a;接收客户端发…

Verilog 语法 (二)

在掌握了 Verilog 的基础语法和常用程序框架之后&#xff0c;本节将带大家深入学习一些 高级设计知识点。这些内容包括&#xff1a; 阻塞赋值&#xff08;&#xff09;与非阻塞赋值&#xff08;<&#xff09;的区别及使用场景&#xff1b; assign 和 always 语句的差异&am…

OpenCV 图形API(61)图像特征检测------检测图像边缘的函数Canny()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 使用Canny算法在图像中查找边缘。 该函数在输入图像中查找边缘&#xff0c;并使用Canny算法在输出映射&#xff08;edges&#xff09;中标记它们…

ubantu中下载编译安装qt5.15.3

操作步骤如下&#xff1a; 克隆 Qt 仓库&#xff1a; git clone https://code.qt.io/qt/qt5.git cd qt5 切换到 Qt 5.15.3 标签&#xff1a; git checkout v5.15.3-lts-lgpl 初始化子模块&#xff1a; perl init-repository 配置和编译 Qt&#xff1a; ./configure -prefix $H…

毕业论文设计基本内容和要求:

毕业设计基本内容和要求&#xff1a; 研究内容 调查了解LAMP架构和PHP开发&#xff1b; 学习百度旅游调用的其他产品线服务并熟悉请求接口&#xff1b; 学习社区业务层规范&#xff1b; 设计并实现旅游主要模块&#xff1b; 技术指标 熟悉企业中流程运转的方式&#xff0c;…

【大语言模型】大语言模型(LLMs)在工业缺陷检测领域的应用

大语言模型&#xff08;LLMs&#xff09;在工业缺陷检测领域的应用场景正在快速扩展&#xff0c;结合其多模态理解、文本生成和逻辑推理能力&#xff0c;为传统检测方法提供了新的技术路径。以下是该领域的主要应用场景及相关技术进展&#xff1a; 1. 多模态缺陷检测与解释 视…

【AI插件开发】Notepad++ AI插件开发1.0发布和使用说明

一、产品简介 AiCoder是一款为Notepad设计的轻量级AI辅助插件&#xff0c;提供以下核心功能&#xff1a; 嵌入式提问&#xff1a;对选中的文本内容进行AI分析&#xff0c;通过侧边栏聊天界面与AI交互&#xff0c;实现多轮对话、问题解答或代码生成。对话式提问&#xff1a;独…

第2讲:R语言中的色彩美学——科研图表配色指南

目录 一、背景导引:科研图表为何需要“配色讲究”? 二、色彩基础认知:别让“红绿盲”错过你的科研成果 三、R语言中的配色库全景图 四、案例演示与代码实战 🎨案例1:ggplot2 + viridis 配色的热图 🎨案例2:MetBrewer 中的印象派色彩 五、技巧点拨:如何为SCI图…

基于Django的个性化股票交易管理系统

本项目基于Python3.6、Django2.1、MySql8.0&#xff08;最好不要使用5.6&#xff0c;字符集等方面均不兼容&#xff0c;否则导入数据库会出错&#xff09;与股票信息工具包TuShare实现。 创建或激活对应Python开发环境 这里使用了conda来管理环境&#xff0c;强烈推荐&#xf…

超越GPT-4?下一代大模型的技术突破与挑战

超越GPT-4&#xff1f;下一代大模型的技术突破与挑战 引言&#xff1a;大模型的演进历程 人工智能领域近年来最引人注目的发展莫过于大型语言模型(Large Language Models, LLMs)的快速进步。从GPT-3到GPT-4&#xff0c;再到如今各种宣称"超越GPT-4"的模型不断涌现&…