江山易改本性难移之ZYNQ SDK FSBL加载启动代码详解

news2024/10/6 22:29:12

SDK版本:2018.3

写在前面:

该文档不足以使你清楚FSBL启动的寄存器级的操作细节,但可以让你看明白整个ZYNQ7000 FSBL代码执行的主要流程。

1. ZYNQ7000加载启动流程

(1)BootRom阶段为ARM上电后最早加载的代码,根据MIO引脚配置确认加载方式,初始化相应的启动介质,加载FSBL到OCM中,把控制权交给FSBL

(2)FSBL阶段完成PS的初始化,加载PL bit流文件,加载SSBL引导程序或者ARM的裸机程序

(3)SSBL阶段分两种情况:①裸机程序直接在DDR中执行②uboot引导加载kernel的过程

2. FSBL代码分析

(1)在文件FSBL_bsp/standalone_v6_5/src/asm_ventors.S中,声明了一个代码段,位于地址0处。开机后PS自动执行地址0处的指令,其中第一行代码为一个跳转:B _boot

.org 0
.text
 
.globl _vector_table
 
.section .vectors
_vector_table:
	B	_boot
	B	Undefined
	B	SVCHandler
	B	PrefetchAbortHandler
	B	DataAbortHandler
	NOP	/* Placeholder for address exception vector*/
	B	IRQHandler
	B	FIQHandler

(2)在同目录下找到文件boot.S中可以看到_boot标号下的代码,_boot会对系统做一系列的初始化,包括DDR,中断,MMU,cache等,执行完成后PS将具有执行C代码的能力。

可以看到在_boot代码最后又执行了一次跳转:b _start

	b	_start				/* jump to C startup code */
	and	r0, r0, r0			/* no op */

(3)在同目录下找到文件xil-crt0.S中可以看到_start标号下的代码,可以看到_start首先执行跳转:bl __cpu_init去执行CPU初始化操作

_start:
	bl      __cpu_init		/* Initialize the CPU first (BSP provides this) */
 
	mov	r0, #0
 
	/* clear sbss */
	ldr 	r1,.Lsbss_start		/* calculate beginning of the SBSS */
	ldr	r2,.Lsbss_end		/* calculate end of the SBSS */

(4)在_start标号代码的末尾可以看到bsp完成了所有的初始化工作,将跳转到main函数开始执行。

	/* make sure argc and argv are valid */
	mov	r0, #0
	mov	r1, #0
 
	/* Let her rip */
	bl	main

(5)回到FSBL工程,在目录FSBL/src/main.c中找到main函数,可以看到第一步就是调用了ps7_init()函数。

ps7_init()函数位于ps7_init.c文件中,这个C文件是由XPS根据用户的配置自动生成的。

查看ps7_init()函数,根据代码可以很明显可以看出该函数其实就是根据PS版本执行了MIO,PLL,CLK,DDR和其他外设的初始化。

int main(void)
{
	u32 BootModeRegister = 0;
	u32 HandoffAddress = 0;
	u32 Status = XST_SUCCESS;
 
	/*
	 * PCW initialization for MIO,PLL,CLK and DDR
	 */
	Status = ps7_init();
	if (Status != FSBL_PS7_INIT_SUCCESS) {
		fsbl_printf(DEBUG_GENERAL,"PS7_INIT_FAIL : %s\r\n",
						getPS7MessageInfo(Status));
		OutputStatus(PS7_INIT_FAIL);
		/*
		 * Calling FsblHookFallback instead of Fallback
		 * since, devcfg driver is not yet initialized
		 */
		FsblHookFallback();
	}
int
ps7_init() 
{
  // Get the PS_VERSION on run time
  unsigned long si_ver = ps7GetSiliconVersion ();
  int ret;
  //int pcw_ver = 0;
  
  if (si_ver == PCW_SILICON_VERSION_1) {
    ps7_mio_init_data = ps7_mio_init_data_1_0;
    ps7_pll_init_data = ps7_pll_init_data_1_0;
    ps7_clock_init_data = ps7_clock_init_data_1_0;
    ps7_ddr_init_data = ps7_ddr_init_data_1_0;
    ps7_peripherals_init_data = ps7_peripherals_init_data_1_0;
    //pcw_ver = 1;
 
  } else if (si_ver == PCW_SILICON_VERSION_2) {
    ps7_mio_init_data = ps7_mio_init_data_2_0;
    ps7_pll_init_data = ps7_pll_init_data_2_0;
    ps7_clock_init_data = ps7_clock_init_data_2_0;
    ps7_ddr_init_data = ps7_ddr_init_data_2_0;
    ps7_peripherals_init_data = ps7_peripherals_init_data_2_0;
    //pcw_ver = 2;
 
  } else {
    ps7_mio_init_data = ps7_mio_init_data_3_0;
    ps7_pll_init_data = ps7_pll_init_data_3_0;
    ps7_clock_init_data = ps7_clock_init_data_3_0;
    ps7_ddr_init_data = ps7_ddr_init_data_3_0;
    ps7_peripherals_init_data = ps7_peripherals_init_data_3_0;
    //pcw_ver = 3;
  }
 
  // MIO init
  ret = ps7_config (ps7_mio_init_data);  
  if (ret != PS7_INIT_SUCCESS) return ret;
 
  // PLL init
  ret = ps7_config (ps7_pll_init_data); 
  if (ret != PS7_INIT_SUCCESS) return ret;
 
  // Clock init
  ret = ps7_config (ps7_clock_init_data);
  if (ret != PS7_INIT_SUCCESS) return ret;
 
  // DDR init
  ret = ps7_config (ps7_ddr_init_data);
  if (ret != PS7_INIT_SUCCESS) return ret;
 
 
 
  // Peripherals init
  ret = ps7_config (ps7_peripherals_init_data);
  if (ret != PS7_INIT_SUCCESS) return ret;
  //xil_printf ("\n PCW Silicon Version : %d.0", pcw_ver);
  return PS7_INIT_SUCCESS;
}

(6)System Software Reset,使能系统软件复位功能

/*
	 * Unlock SLCR for SLCR register write
	 */
	SlcrUnlock();

(7)关闭cache功能

/*
	 * Flush the Caches
	 */
	Xil_DCacheFlush();
 
	/*
	 * Disable Data Cache
	 */
	Xil_DCacheDisable();

(8)注册异常中断

/*
	 * Register the Exception handlers
	 */
	RegisterHandlers();

这里相当于异常处理函数全部指向0地址。

Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_UNDEFINED_INT,
					(Xil_ExceptionHandler)Undef_Handler,
					(void *) 0);
	XExc_VectorTable[Exception_id].Handler = Handler;
	XExc_VectorTable[Exception_id].Data = Data;

(9)DDR读写测试,在DDR不同地址段进行读写比对

   /*
     * DDR Read/write test 
     */
	Status = DDRInitCheck();
	if (Status == XST_FAILURE) {
		fsbl_printf(DEBUG_GENERAL,"DDR_INIT_FAIL \r\n");
		/* Error Handling here */
		OutputStatus(DDR_INIT_FAIL);
		/*
		 * Calling FsblHookFallback instead of Fallback
		 * since, devcfg driver is not yet initialized
		 */
		FsblHookFallback();
	}

(10)Processor Configuration Access Port即处理器配置接口,连接软件和硬件的桥梁。

	/*
	 * PCAP initialization
	 */
	Status = InitPcap();
	if (Status == XST_FAILURE) {
		fsbl_printf(DEBUG_GENERAL,"PCAP_INIT_FAIL \n\r");
		OutputStatus(PCAP_INIT_FAIL);
		/*
		 * Calling FsblHookFallback instead of Fallback
		 * since, devcfg driver is not yet initialized
		 */
		FsblHookFallback();
	}
 
	fsbl_printf(DEBUG_INFO,"Devcfg driver initialized \r\n");

 

(11)获取PS版本号

/*
	 * Get the Silicon Version
	 */
	GetSiliconVersion();

(12)获取PCAP接口控制器配置信息,检查是否允许系统复位

	/*
	 * Get PCAP controller settings
	 */
	PcapCtrlRegVal = XDcfg_GetControlRegister(DcfgInstPtr);
 
	/*
	 * Check for AES source key
	 */
	if (PcapCtrlRegVal & XDCFG_CTRL_PCFG_AES_FUSE_MASK) {
		/*
		 * For E-Fuse AES encryption Watch dog Timer disabled and
		 * User not allowed to do system reset
		 */
#ifdef	XPAR_XWDTPS_0_BASEADDR
		fsbl_printf(DEBUG_INFO,"Watchdog Timer Disabled\r\n");
		XWdtPs_Stop(&Watchdog);
#endif
		fsbl_printf(DEBUG_INFO,"User not allowed to do "
								"any system resets\r\n");
	}

(13)配置FSBL正在执行状态

	/*
	 * Store FSBL run state in Reboot Status Register
	 */
	MarkFSBLIn();

(14)读取启动模式寄存器,启动模式是通过MIO引脚来配置的,要配置相应的启动模式可以参考下图中MIO各个引脚在不同模式的配置情况

/*
	 * Read bootmode register
	 */
	BootModeRegister = Xil_In32(BOOT_MODE_REG);
	BootModeRegister &= BOOT_MODES_MASK;

(15)根据启动模式初始化对应的存储设备

QSPI启动

①初始化qspi Flash

②MoveImage = QspiAccess;函数指针赋值,实现从Norflash中拷贝image到内存中

	if (BootModeRegister == QSPI_MODE) {
		fsbl_printf(DEBUG_GENERAL,"Boot mode is QSPI\n\r");
		InitQspi();
		MoveImage = QspiAccess;
		fsbl_printf(DEBUG_INFO,"QSPI Init Done \r\n");

Norlflash启动

	/*
	 * NOR BOOT MODE
	 */
	if (BootModeRegister == NOR_FLASH_MODE) {
		fsbl_printf(DEBUG_GENERAL,"Boot mode is NOR\n\r");
		/*
		 * Boot ROM always initialize the nor at lower speed
		 * This is the chance to put it to an optimum speed for your nor
		 * device
		 */
		InitNor();
		fsbl_printf(DEBUG_INFO,"NOR Init Done \r\n");
		MoveImage = NorAccess;

JTAG启动

	/*
	 * JTAG  BOOT MODE
	 */
	if (BootModeRegister == JTAG_MODE) {
		fsbl_printf(DEBUG_GENERAL,"Boot mode is JTAG\r\n");
		/*
		 * Stop the Watchdog before JTAG handoff
		 */
#ifdef	XPAR_XWDTPS_0_BASEADDR
		XWdtPs_Stop(&Watchdog);
#endif
		/*
		 * Clear our mark in reboot status register
		 */
		ClearFSBLIn();
 
		/*
		 * SLCR lock
		 */
		SlcrLock();
 
		FsblHandoffJtagExit();

(16)FlashReadBaseAddress是在上述流程中根据不同的启动设备进行初始化的。

通常情况下我们使用Norflash启动,InitQspi()函数中会对FlashReadBaseAddress赋值,就是qspi falsh的起始地址为0xFC000000,在zynq7000的数据手册UG585中可以看到。

	/*
	 * Check for valid flash address
	 */
	if ((FlashReadBaseAddress != XPS_QSPI_LINEAR_BASEADDR) &&
			(FlashReadBaseAddress != XPS_NAND_BASEADDR) &&
			(FlashReadBaseAddress != XPS_NOR_BASEADDR) &&
			(FlashReadBaseAddress != XPS_SDIO0_BASEADDR)) {
		fsbl_printf(DEBUG_GENERAL,"INVALID_FLASH_ADDRESS \r\n");
		OutputStatus(INVALID_FLASH_ADDRESS);
		FsblFallback();
	}
 
	/*
	 * NOR and QSPI (parallel) are linear boot devices
	 */
	if ((FlashReadBaseAddress == XPS_NOR_BASEADDR)) {
		fsbl_printf(DEBUG_INFO, "Linear Boot Device\r\n");
		LinearBootDeviceFlag = 1;
	}

(17)接下来就是最关键的地方了,这个函数做两件事情①分析烧录到qspi中的数据的头的部分②根据分析结果拷贝数据到DDR中

	/*
	 * Load boot image
	 */
	HandoffAddress = LoadBootImage();
 
	fsbl_printf(DEBUG_INFO,"Handoff Address: 0x%08lx\r\n",HandoffAddress);

(18)我们进入到函数LoadBootImage()中进一步分析代码

这段代码的作用是从multiboot寄存器中读取要执行的image的地址,其实如果就一个image的话可以不用管这个,这个算出来的imagestartaddress一定是0

	/*
		 * read the multiboot register
		 */
		MultiBootReg =  XDcfg_ReadReg(DcfgInstPtr->Config.BaseAddr,
				XDCFG_MULTIBOOT_ADDR_OFFSET);
 
		fsbl_printf(DEBUG_INFO,"Multiboot Register: 0x%08lx\r\n",MultiBootReg);
 
		/*
		 * Compute the image start address
		 */
		ImageStartAddress = (MultiBootReg & PCAP_MBOOT_REG_REBOOT_OFFSET_MASK)
									* GOLDEN_IMAGE_OFFSET;

(19)解析Image即BOOT.bin的头信息

①从bootloader中解析出BOOT.bin的大小(这个信息后续未看到有使用)

②把BOOT.bin中header解析出partition header并保存到全局变量PartHeader PartitionHeader[MAX_PARTITION_NUMBER]中,实际有效的只有3个partitions,即FSBL.elf,FPGA.bit,application.elf

③根据解析出的partition header数据解析出partition的数量      

	/*
	 * Get partitions header information
	 */
	Status = GetPartitionHeaderInfo(ImageStartAddress);
	if (Status != XST_SUCCESS) {
		fsbl_printf(DEBUG_GENERAL, "Partition Header Load Failed\r\n");
		OutputStatus(GET_HEADER_INFO_FAIL);
		FsblFallback();
	}

PartHeader为BOOT.bin中解析出的各个partition的Header信息结构体

typedef struct StructPartHeader {
	u32 ImageWordLen;	/* 0x0 */
	u32 DataWordLen;	/* 0x4 */
	u32 PartitionWordLen;	/* 0x8 */
	u32 LoadAddr;		/* 0xC */
	u32 ExecAddr;		/* 0x10 */
	u32 PartitionStart;	/* 0x14 */
	u32 PartitionAttr;	/* 0x18 */     // 用来判断文件属性,例如FPGA.bit文件或者application.elf文件
	u32 SectionCount;	/* 0x1C */
	u32 CheckSumOffset;	/* 0x20 */
	u32 Pads1[1];
	u32 ACOffset;	/* 0x28 */
	u32 Pads2[4];
	u32 CheckSum;		/* 0x3C */
}PartHeader;
这里需要了解一下BOOT.bin的结构。
在boot.bin中从地址0-0x8BF可以分成17个部分,每个部分都有一定的含义 
1. 0x000  中断向量表 
2. 0x020  固定值 0xaa995566 
3. 0x024  固定值 0x584c4e58  ASCII: XLNX 
4. 0x028  如果是0xa5c3c5a3或者0x3a5c3c5a为加密的 
5. 0x02C  bootrom头版本号,不用管 
6. 0x030  从bootrom开始到app地址的总数(bytes) 
7. 0x034  从loadimage拷到OCM的长度 【上电后BootRom会主动把FSBL拷贝到OCM中执行】
8. 0x038  目的地址到哪儿拷贝FSBL 
9. 0x03C  开始执行的地址 
10. 0x040  同7 【此处代码逻辑中其实是把该字段的值赋给FSBL的size】
11. 0x044  0x01为固定值 
12. 0x048  校验和(从0x020-0x047)按32-bit word 相加取反 
13. 0x04C  bootgen相关 
14. 0x098  image头的表指针 
15. 0x09C  partition头的表指针 
16. 0x0A0  寄存器初始化的参数 
17. 0x8A0  fsbl user defined 
18. 0x8C0  fsbl开始的地方 

(20)拿到partition header后应该分别加载各个partition,但由于第0个partition其实就是FSBL,而我们当前其实已经在FSBL执行中了,所以不用加载直接跳过从partitionNum = 1开始加载

	/*
	 * First partition header was ignored by FSBL
	 * As it contain FSBL partition information
	 */
	PartitionNum = 1;

(21)接下来开始加载对各个partition是类似的,主要完成两部分工作:

①解析并检查各个partition header中内容的正确性

②从norflash中加载各个partiton到指定的目标地址中。(这里对FPGA.bit和application.elf有所差别)

根据partition header中属性判断当前为bit文件或者application文件            

		if (PartitionAttr & ATTRIBUTE_PL_IMAGE_MASK) {
			fsbl_printf(DEBUG_INFO, "Bitstream\r\n");
			PLPartitionFlag = 1;
			PSPartitionFlag = 0;
			BitstreamFlag = 1;
		}
 
		if (PartitionAttr & ATTRIBUTE_PS_IMAGE_MASK) {
			fsbl_printf(DEBUG_INFO, "Application\r\n");
			PSPartitionFlag = 1;
			PLPartitionFlag = 0;
			ApplicationFlag = 1;
		}

该函数搬移partition数据到DDR中

	/*
		 * Move partitions from boot device
		 */
		Status = PartitionMove(ImageStartAddress, HeaderPtr);
		if (Status != XST_SUCCESS) {
			fsbl_printf(DEBUG_GENERAL,"PARTITION_MOVE_FAIL\r\n");
			OutputStatus(PARTITION_MOVE_FAIL);
			FsblFallback();
		}

FPGA.bit和application.elf文件都是通过下面函数依次搬移传输到DDR中

if ((LinearBootDeviceFlag && PLPartitionFlag &&
			(SignedPartitionFlag || PartitionChecksumFlag)) ||
				(LinearBootDeviceFlag && PSPartitionFlag) ||
				((!LinearBootDeviceFlag) && PSPartitionFlag && SecureTransferFlag)) {
		/*
		 * PL signed partition copied to DDR temporary location
		 * using non-secure PCAP for linear boot device
		 */
		if(PLPartitionFlag){
			SecureTransferFlag = 0;
			LoadAddr = DDR_TEMP_START_ADDR;
		}
 
		/*
		 * Data transfer using PCAP
		 */
		Status = PcapDataTransfer((u32*)SourceAddr,
						(u32*)LoadAddr,
						ImageWordLen,
						DataWordLen,
						SecureTransferFlag);
		if(Status != XST_SUCCESS) {
			fsbl_printf(DEBUG_GENERAL, "PCAP Data Transfer Failed\r\n");
			return XST_FAILURE;
		}

(22)如果partition为FPGA bit文件,那么通过以下函数完成从DDR中加载启动bit文件,这个函数中涉及PCAP的操作流程,这里不再深入探究。

/*
			 * Load Signed PL partition in Fabric
			 */
			if (PLPartitionFlag) {
				Status = PcapLoadPartition((u32*)PartitionStartAddr,
						(u32*)PartitionLoadAddr,
						PartitionImageLength,
						PartitionDataLength,
						EncryptedPartitionFlag);
				if (Status != XST_SUCCESS) {
					fsbl_printf(DEBUG_GENERAL,"BITSTREAM_DOWNLOAD_FAIL\r\n");
					OutputStatus(BITSTREAM_DOWNLOAD_FAIL);
					FsblFallback();
				}
			}

(23)至此函数LoadBootImage全部执行完成,当前已完成FPGA.bit加载,并且application也已经写入到DDR中。

在下面的函数中HandoffAddress应该为application partition header中的执行地址,也是application.elf保存在DDR中的基地址,即0x00100000

	/*
	 * FSBL handoff to valid handoff address or
	 * exit in JTAG
	 */
	FsblHandoff(HandoffAddress);

在该函数中最后通过FsblHandoffExit(FsblStartAddr)函数实现了FSBL到application.elf的跳转

if(FsblStartAddr == 0) {
		/*
		 * SLCR lock
		 */
		SlcrLock();
 
		fsbl_printf(DEBUG_INFO,"No Execution Address JTAG handoff \r\n");
		FsblHandoffJtagExit();
	} else {
		fsbl_printf(DEBUG_GENERAL,"SUCCESSFUL_HANDOFF\r\n");
		OutputStatus(SUCCESSFUL_HANDOFF);
		FsblHandoffExit(FsblStartAddr);
	}

在src/fsbl_handoff.S文件中,bx lr指令实现了跳转到application开始执行

FsblHandoffExit:
		mov	 lr, r0	/* move the destination address into link register */
 
		mcr	 15,0,r0,cr7,cr5,0		/* Invalidate Instruction cache */
		mcr	 15,0,r0,cr7,cr5,6		/* Invalidate branch predictor array */
 
		dsb
		isb					/* make sure it completes */
 
	ldr	r4, =0
		mcr	 15,0,r4,cr1,cr0,0		/* disable the ICache and MMU */
 
		isb					/* make sure it completes */
 
 
		bx		lr	/* force the switch, destination should have been in r0 */
 
.Ldone: b		.Ldone					/* Paranoia: we should never get here */
.end

(24)以上FSBL运行加载FPGA.bit和引导application.elf执行过程代码分析全部完成。

参考资料:

https://blog.csdn.net/zhaoxinfan/article/details/54958641

https://blog.csdn.net/asmartkiller/article/details/84072643

https://blog.csdn.net/qq_40155300/article/details/89001808

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

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

相关文章

Android SDK环境搭建

一、Android SDK简介 SDK:(software development kit)软件开发工具包。被软件开发工程师用于为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件的开发工具的集合。 因此,Android SDK 指的是Android专属的软件…

【Scala】——函数式编程

1 面向对象编程和函数式编程 1.1 面向对象编程 解决问题,分解对象,行为,属性,然后通过对象的关系以及行为的调用来解决问题。 • 对象:用户 • 行为:登录、连接 JDBC、读取数据库 • 属性:用户…

内裤洗衣机有用吗?五款小型洗衣机全自动推荐

随着内衣洗衣机的流行,很多小伙伴在纠结该不该入手一款内衣洗衣机,专门来洗一些贴身衣物,答案是非常有必要的,因为我们现在市面上的大型洗衣机只能做清洁,无法对我们的贴身衣物进行一个高强度的清洁,而小小…

Linux运维之切换到 root 用户

春花秋月何时了,往事知多少。此付费专栏不要订阅,不要订阅,听人劝。 🌹作者主页:青花锁 🌹简介:Java领域优质创作者🏆、Java微服务架构公号作者😄 🌹简历模板、学习资料、面试题库、技术互助 🌹文末获取联系方式 📝 系列专栏目录 [Java项目实战] 介绍Java…

C/C++ 位段

目录 什么是位段? 位段的内存分配 位段的跨平台问题 什么是位段? 位段的声明与结构是类似的,但是有两个不同: 位段的成员必须是 int、unsigned int 或signed int 等整型家族。位段的成员名后边有一个冒号和一个数字 这是一个…

JPEG格式详解Baseline、Progressive的区别

文章目录 JPEG的简介压缩质量/压缩比率色彩空间基线和渐进子采样存储选项 基线和渐进基线格式渐进格式: 子采样4:4:4(无损)4:2:24:2:0 JPEG的简介 JPEG(Joint Photographic Experts Group)是一种常见的图像压缩格式&a…

K8S 存储卷

意义:存储卷----数据卷 容器内的目录和宿主机的目录进行挂载 容器在系统上的生命周期是短暂的,delete,k8s用控制器创建的pod,delete相当于重启,容器的状态也会回复到初始状态 一旦回到初始状态,所有的后天编辑的文件…

原来这些小众知识库软件这么好用,挖到宝了

在企业管理中,知识库的作用越来越被重视。它不仅可以提高工作流程的效率,还可以最大限度地利用企业中的知识资源。然而,在众多的知识库工具中选择一款合适的并非易事。不用担心,今天我要为大家揭晓一些小众却非常好用的知识库软件…

C语言之详解数组【附三子棋和扫雷游戏实战】

文章目录 一、一维数组的创建和初始化1、数组的创建2、数组的初始化3、一维数组的使用4、 一维数组在内存中的存储 二、二维数组的创建和初始化1、二维数组的创建2、二维数组的初始化3、二维数组的使用4、二维数组在内存中的存储 三、数组越界边界值考虑不当导致越界访问数组大…

如何利用CHAT做简单的总结体会?

问CHAT :在测试过程中使用appiumpython自动化的优点和体会 CHAT回复:使用 Appium 配合 Python 进行自动化测试主要有以下几点优点: 1. 跨平台性:Appium 支持 iOS 和 Android 平台的应用自动化测试,无论是原生应用、移…

开放平台系统架构设计

一、概述 背景与目标 本开放平台旨在构建一个可扩展、高可用的生态体系,通过提供统一标准的API接口和SDK工具包,让第三方开发者能够安全、高效地接入我们的服务和资源,实现业务的互联互通。 定位与功能描述 系统主要包含用户认证授权、资…

[C#]winform部署PaddleOCRV3推理模型

【官方框架地址】 https://github.com/PaddlePaddle/PaddleOCR.git 【算法介绍】 PaddleOCR是由百度公司推出的一款开源光学字符识别(OCR)工具,它基于深度学习框架PaddlePaddle开发。这款工具提供了一整套端到端的文字检测和识别解决方案&a…

OpenHarmony之hdc

OpenHarmony之hdc 简介 hdc(OpenHarmony Device Connector)是 OpenHarmony 为开发人员提供的用于调试的命令行工具,通过该工具可以在Windows/Linux/MacOS等系统上与开发机或者模拟器进行交互。 类似于Android的adb,和adb类似&a…

如何使用web文件管理器Net2FTP搭建个人网盘

文章目录 1.前言2. Net2FTP网站搭建2.1. Net2FTP下载和安装2.2. Net2FTP网页测试 3. cpolar内网穿透3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 文件传输可以说是互联网最主要的应用之一,特别是智能设备的大面积使用,无论是个人…

智能时代:自然语言生成SQL与知识图谱问答实战

语义解析 前言语义解析的应用场景总结概论语义解析和大模型的关系延伸阅读 前言 语义解析技术可以提高人机交互的效率和准确性,在自然语言处理、数据分析、智能客服、智能家居等领域都有广泛的应用前景。特别是在大数据时代,语义解析能够帮助企业更快速…

pc下载apk文件到andriod开发板,并实现可视化

PC端安装APK下载器 点击下载 刷机精灵APK安装器 界面如下,可将下载好的apk文件,直接拖拽到该界面,然后点击安装全部按钮进行安装,安装过程中的具体状态会显示在具体的apk后面。 如下图,安装错误、安装完成等皆为apk安…

springcloud Config配置中心

简介 服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。 SpringCloud提供了ConfigS…

什么软件能查出微信聊天记录

在企业管理中,很多时候是需要查出员工的具体的聊天记录的。今天就分享两种可以查出微信聊天记录的工具: 工具一:微信自带的工具 1、打开微信,在聊天界面点击右上角的“...”按钮,选择“设置”,进入“聊天”…

Navicat迁移局域网内其他PC机的MySQL数据库

迁移局域网内其他PC机的MySQL数据库到本机 查看局域网IP 设置可远程连接的账号 开放本机防火墙的3306端口 连接PC机的MySQL 利用Navicat迁移数据库 刚换了个电脑,旧电脑的MySQL数据库太多了,转成.sql文件,再传输到新电脑上运行&#xff…