系统启动过程(基于三星s5p6818 uboot)
总结下手中开发板的启动过程,uboot版本为2014.07,参考S5P6818 Application Processor User's Manual。
首先,我们应该知道为什么需要uboot,而不是只知其然,不知其所以然。一言以概之:在我们能够在操作系统下运行程序之前,所有的努力都是为了能够让系统能够被搬运到内存中运行。有了这样的目的性,我们分析理解起来就简单多了。
其实,高端的ARM芯片并不和STM32等Cortex-M系的ARM芯片相差很多,只是主频更高,核心增多,外设更丰富了。附图吧:
IROM:
和大部分ARM芯片一样,S5P6818内部也自带了RAM和ROM,分别为64K和20K。若官方提供下载器的话,我们也可以像STM32一样,让代码在ROM中运行,RAM中保存堆栈变量等信息,但那就大材小用了。显然,为了能够跑起linux,光是芯片内的这点空间是完全不够的,好在高端芯片强就强在能够带动大容量的外部RAM和ROM。所以我们一般会将系统存放在外部的SD卡或EMMC中。所以我们此时我们的目的也很明确,检查外部的存储中是否包含合法的内容。
因此,在系统上电后,芯片首先会运行片内20KB ROM中的代码,此ROM一般为NOR FLASH。相对于外部存储经常使用的NAND FLASH,其优势在于代码可以芯片内运行,所以不需要像NAND FLASH一样将代码搬运到内存中。内部ROM地址一般即为0地址处,其作用是检测引脚的配置情况,确定外部存储器启动的优先级,选择对应的外部存储进行初始化,再将其中的前56K代码搬运到内部RAM中(因内部RAM只有64KB,且ROM自身会用掉一部分RAM),当搬运了512字节之后会进行Boot Header的检查,查看签名是否为0x4849534E。若是,则为正确代码,跳转到内部RAM即地址0xFFFF0000处运行;否则,选择下一个外部存储器进行判断测试。
如图,若想系统首先从SDMMC启动,则须将RST_CFG[2:0]这三位设置为5,这三位对应于原理图为SD2,SD1和SD0,从原理图可以看出我们确实是这样设置了。
之后还可以通过SD3和CAM1_D3进行从哪一个SDMMC进行启动,共有三个端口可选。接下来就是从SD卡中复制代码到内部RAM中并跳转运行了,如图。
2ndboot:
现在的状态是外部存储器可以读取使用了,但外部的大容量SDRAM还是不能使用,而我们最终的目的是要将代码放入到SDRAM中运行的,这里讲讲SRAM和DRAM的区别。S和D分别代表静态和动态刷新,SRAM在上电后只要不断电数据就一直存在,而DRAM需要在上电后过一段时间进行充电刷新,这样数据才能够保持不变,虽然相比SRAN变麻烦了,但价格降低,容量却提升了。这就是S5P6818内部采用小容量SRAM,外面挂接DDR3这样的SDRAM的原因,这里的S为串行的意思。所以现在很明确加载到内部SRAM中代码所需要做的事了:1、初始化芯片的内存控制器 2、初始化外部SDRAM 3、将SD卡中uboot搬运到内存中运行。
当然除此之外,还有包括设置中断向量表,设置PLL,设置堆栈,初始化串口等,这就是s5p6818提供的2ndboot的内容,烧写在SD卡的block1~block64共32K字节的位置。
以往这部分内容也有是在uboot中进行完成的,在接下来的uboot讲解中会有提到,把这部分初始化的内容提炼提炼出来的好处就是减少了uboot的自搬运过程。
uboot:
2ndboot最后的动作便是将uboot从SD卡的第65块block搬运到内存0x42C00000的位置,接下来便会跳转到外部SDRAM进行uboot的运行。
首先查看生成的uboot链接文件uboot.lds:
可知此uboot程序代码起始的运行位置为arch/arm/cpuslsiap/s5p6818/start.o的_stext处。
/**************************************************************************** Exception vectors as described in ARM reference manuals** replace arm/lib/vectors.S***************************************************************************/.globl _stext
_stext:b resetldr pc, _undefined_instructionldr pc, _software_interruptldr pc, _prefetch_abortldr pc, _data_abortldr pc, _not_usedldr pc, _irqldr pc, _fiq_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq.balignl 16,0xdeadbeef
首先定义_stext为一个全局标号,接下来就是一句跳转,跳转到reset标号处运行,下面的为异常处理,当系统反生异常时,会跳转到对应的地址运行,最后一句用一个魔数强制了16字节对齐。
/**************************************************************************** Reset handling***************************************************************************/.globl resetreset:bl save_boot_params/** set the cpu to SVC32 mode*/mrs r0, cpsrbic r0, r0, #0x1forr r0, r0, #0xd3msr cpsr,r0/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INITbl cpu_init_cp15bl cpu_init_crit
#endif
reset首先会将当前的模式设置为SVC32管理模式,接下来是两个函数cpu_init_cp15和cpu_init_crit,第一个函数设置了ARM中重要的寄存器cp15协处理器,通过这个寄存器我们便可对TLB(快表),I-cache(指令缓存),D-cache(数据缓存),MMU(内存管理单元)等进行使能和失能。而cpu_init_crit则会调用lowlevel_init.S中的lowlevel_init函数。以往若没有2ndboot,则会在此函数中进行pll,外部SDRAM等的初始化。因为这部分我们已经在2ndboot中做了,所以此处只是进行了简单的cpu型号读取和设置多核SMP等,之后便返回到start.S继续运行。
接下来是一个自搬运的函数,r0指向uboot运行的地址,r1指向需要搬运到的内存地址为0x42C00000。若此时uboot运行在flash中则会将uboot搬运到0x42C00000处运行,否则跳过搬运过程,将BSS段中的数据清零,设置栈的位置和uboot中大名鼎鼎的gd位置,之后便会跳转到common/board_f.c中的board_init_f函数运行。
#ifdef CONFIG_RELOC_TO_TEXT_BASE
relocate_to_text:/** relocate u-boot code on memory to text base* for nexell arm core (add by jhkim)*/adr r0, _stext /* r0 <- current position of code */ldr r1, TEXT_BASE /* test if we run from flash or RAM */cmp r0, r1 /* don't reloc during debug */beq clear_bssldr r2, _bss_start_ofsadd r2, r0, r2 /* r2 <- source end address */copy_loop_text:ldmia r0!, {r3-r10} /* copy from source address [r0] */stmia r1!, {r3-r10} /* copy to target address [r1] */cmp r0, r2 /* until source end addreee [r2] */ble copy_loop_textldr r1, TEXT_BASE /* restart at text base */mov pc, r1
设置栈和gd位置:
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)bic sp, sp, #7 /* 8-byte alignment for ABI compliance */sub sp, #GD_SIZE /* allocate one GD above SP */bic sp, sp, #7 /* 8-byte alignment for ABI compliance */mov r9, sp /* GD is above SP */mov r0, #0bl board_init_f
board_init_f函数:
执行前置的(front)初始化工作,若此时全局变量未能使用,则通过定义局部变量,在栈中创建并设置gd:
#ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA/** For some archtectures, global data is initialized and used before* calling this function. The data should be preserved. For others,* CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack* here to host global data until relocation.*/gd_t data;gd = &data;/** Clear global data before it is accessed at debug print* in initcall_run_list. Otherwise the debug print probably* get the wrong vaule of gd->have_console.*/zero_global_data();
#endif
调用各个初始化函数,初始化串口,输出uboot,CPU,RAM等信息,初始化全局变量区:
static init_fnc_t init_sequence_f[] = {
#ifdef CONFIG_SANDBOXsetup_ram_buf,
#endifsetup_mon_len,setup_fdt,trace_early_init,
#if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx)/* TODO: can this go into arch_cpu_init()? */probecpu,
#endifarch_cpu_init, /* basic arch cpu dependent setup */
#ifdef CONFIG_X86cpu_init_f, /* TODO(sjg@chromium.org): remove */
# ifdef CONFIG_OF_CONTROLfind_fdt, /* TODO(sjg@chromium.org): remove */
# endif
#endifmark_bootstage,
#ifdef CONFIG_OF_CONTROLfdtdec_check_fdt,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F)board_early_init_f,
#endif/* TODO: can any of this go into arch_cpu_init()? */
#if defined(CONFIG_PPC) && !defined(CONFIG_8xx_CPUCLK_DEFAULT)get_clocks, /* get CPU and bus clocks (etc.) */
#if defined(CONFIG_TQM8xxL) && !defined(CONFIG_TQM866M) \&& !defined(CONFIG_TQM885D)adjust_sdram_tbs_8xx,
#endif/* TODO: can we rename this to timer_init()? */init_timebase,
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_MIPS)timer_init, /* initialize timer */
#endif
#ifdef CONFIG_SYS_ALLOC_DPRAM
#if !defined(CONFIG_CPM2)dpram_init,
#endif
#endif
#if defined(CONFIG_BOARD_POSTCLK_INIT)board_postclk_init,
#endif
#ifdef CONFIG_FSL_ESDHCget_clocks,
#endifenv_init, /* initialize environment */
#if defined(CONFIG_8xx_CPUCLK_DEFAULT)/* get CPU and bus clocks according to the environment variable */get_clocks_866,/* adjust sdram refresh rate according to the new clock */sdram_adjust_866,init_timebase,
#endifinit_baud_rate, /* initialze baudrate settings */serial_init, /* serial communications setup */console_init_f, /* stage 1 init of console */
#ifdef CONFIG_SANDBOXsandbox_early_getopt_check,
#endif
#ifdef CONFIG_OF_CONTROLfdtdec_prepare_fdt,
#endifdisplay_options, /* say that we are here */display_text_info, /* show debugging info if required */
#if defined(CONFIG_MPC8260)prt_8260_rsr,prt_8260_clks,
#endif /* CONFIG_MPC8260 */
#if defined(CONFIG_MPC83xx)prt_83xx_rsr,
#endif
#ifdef CONFIG_PPCcheckcpu,
#endifprint_cpuinfo, /* display cpu info (and speed) */
#if defined(CONFIG_MPC5xxx)prt_mpc5xxx_clks,
#endif /* CONFIG_MPC5xxx */
#if defined(CONFIG_DISPLAY_BOARDINFO)checkboard, /* display board info */
#endifINIT_FUNC_WATCHDOG_INIT
#if defined(CONFIG_MISC_INIT_F)misc_init_f,
#endifINIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)init_func_i2c,
#endif
#if defined(CONFIG_HARD_SPI)init_func_spi,
#endif
#ifdef CONFIG_X86dram_init_f, /* configure available RAM banks */calculate_relocation_address,
#endifannounce_dram_init,/* TODO: unify all these dram functions? */
#ifdef CONFIG_ARMdram_init, /* configure available RAM banks */
#endif
#if defined(CONFIG_MIPS) || defined(CONFIG_PPC)init_func_ram,
#endif
#ifdef CONFIG_POSTpost_init_f,
#endifINIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_SYS_DRAM_TEST)testdram,
#endif /* CONFIG_SYS_DRAM_TEST */INIT_FUNC_WATCHDOG_RESET#ifdef CONFIG_POSTinit_post,
#endifINIT_FUNC_WATCHDOG_RESET/** Now that we have DRAM mapped and working, we can* relocate the code and continue running from DRAM.** Reserve memory at end of RAM for (top down in that order):* - area that won't get touched by U-Boot and Linux (optional)* - kernel log buffer* - protected RAM* - LCD framebuffer* - monitor code* - board info struct*/setup_dest_addr,
#if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_ADDR)reserve_logbuffer,
#endif
#ifdef CONFIG_PRAMreserve_pram,
#endifreserve_round_4k,
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \defined(CONFIG_ARM)reserve_mmu,
#endif
#ifdef CONFIG_LCDreserve_lcd,
#endifreserve_trace,/* TODO: Why the dependency on CONFIG_8xx? */
#if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) \&& !defined(CONFIG_ARM) && !defined(CONFIG_X86)reserve_video,
#endifreserve_uboot,
#ifndef CONFIG_SPL_BUILDreserve_malloc,reserve_board,
#endifsetup_machine,reserve_global_data,reserve_fdt,reserve_stacks,setup_dram_config,show_dram_config,
#ifdef CONFIG_PPCsetup_board_part1,INIT_FUNC_WATCHDOG_RESETsetup_board_part2,
#endifdisplay_new_sp,
#ifdef CONFIG_SYS_EXTBDINFOsetup_board_extra,
#endifINIT_FUNC_WATCHDOG_RESETreloc_fdt,setup_reloc,
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX)jump_to_copy,
#endifNULL,
};
if (initcall_run_list(init_sequence_f))hang();
当然这里也会有dram_init对外部DRAM进行初始化,若没有2ndboot的话,在这里用户可以实现dram_init来对外部DRAM初始化,再跳回start.S进行relocation操作,将u-boot自搬运到DRAM中。board_init_f之后又会返回到start.S,调用gdt_reset函数,参数为新的栈地址和gd地址,更改gdt中对应的地址内容。接下来便会调用到board_init_r进行后置的板级初始化。
board_init_r函数:
/common/board_r.c进行后置的(rear)板级初始化工作,在运行了之前board_init_f后,全局变量区,BSS段,堆栈等C的运行环境才建立起来。这里使用和board_init_f同样的方法定义一个函数指针数组,依次遍历其中的元素,进行各项初始化,包括使能cache,初始化malloc,重新初始化串口,使能中断,外设初始化(LED,LCD),网络初始化等,最终调用run_main_loop,进入到uboot的命令行。
init_fnc_t init_sequence_r[] = {initr_trace,initr_reloc,/* TODO: could x86/PPC have this also perhaps? */
#ifdef CONFIG_ARMinitr_caches,board_init, /* Setup chipselects */
#endif/** TODO: printing of the clock inforamtion of the board is now* implemented as part of bdinfo command. Currently only support for* davinci SOC's is added. Remove this check once all the board* implement this.*/
#ifdef CONFIG_CLOCKSset_cpu_clk_info, /* Setup clock information */
#endifinitr_reloc_global_data,initr_serial,initr_announce,INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_PPCinitr_trap,
#endif
#ifdef CONFIG_ADDR_MAPinitr_addr_map,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_R)board_early_init_r,
#endifINIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_LOGBUFFERinitr_logbuffer,
#endif
#ifdef CONFIG_POSTinitr_post_backlog,
#endifINIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_SYS_DELAYED_ICACHEinitr_icache_enable,
#endif
#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500)initr_unlock_ram_in_cache,
#endif
#if defined(CONFIG_PCI) && defined(CONFIG_SYS_EARLY_PCI_INIT)/** Do early PCI configuration _before_ the flash gets initialised,* because PCU ressources are crucial for flash access on some boards.*/initr_pci,
#endif
#ifdef CONFIG_WINBOND_83C553initr_w83c553f,
#endifinitr_barrier,initr_malloc,bootstage_relocate,
#ifdef CONFIG_DMinitr_dm,
#endif
#ifdef CONFIG_ARCH_EARLY_INIT_Rarch_early_init_r,
#endifpower_init_board,
#ifndef CONFIG_SYS_NO_FLASHinitr_flash,
#endifINIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PPC) || defined(CONFIG_X86)/* initialize higher level parts of CPU like time base and timers */cpu_init_r,
#endif
#ifdef CONFIG_PPCinitr_spi,
#endif
#if defined(CONFIG_X86) && defined(CONFIG_SPI)init_func_spi,
#endif
#ifdef CONFIG_CMD_NANDinitr_nand,
#endif
#ifdef CONFIG_CMD_ONENANDinitr_onenand,
#endif
#ifdef CONFIG_GENERIC_MMCinitr_mmc,
#endif
#ifdef CONFIG_HAS_DATAFLASHinitr_dataflash,
#endifinitr_env,INIT_FUNC_WATCHDOG_RESETinitr_secondary_cpu,
#ifdef CONFIG_SC3initr_sc3_read_eeprom,
#endif
#ifdef CONFIG_HERMESinitr_hermes,
#endif
#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET)mac_read_from_eeprom,
#endifINIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI) && !defined(CONFIG_SYS_EARLY_PCI_INIT)/** Do pci configuration*/initr_pci,
#endifstdio_init,initr_jumptable,
#ifdef CONFIG_APIinitr_api,
#endifconsole_init_r, /* fully init console as a device */
#ifdef CONFIG_DISPLAY_BOARDINFO_LATEshow_model_r,
#endif
#ifdef CONFIG_ARCH_MISC_INITarch_misc_init, /* miscellaneous arch-dependent init */
#endif
#ifdef CONFIG_MISC_INIT_Rmisc_init_r, /* miscellaneous platform-dependent init */
#endif
#ifdef CONFIG_HERMESinitr_hermes_start,
#endifINIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_CMD_KGDBinitr_kgdb,
#endif
#ifdef CONFIG_X86board_early_init_r,
#endifinterrupt_init,
#if defined(CONFIG_ARM) || defined(CONFIG_x86)initr_enable_interrupts,
#endif
#ifdef CONFIG_X86timer_init, /* initialize timer */
#endif
#if defined(CONFIG_STATUS_LED) && defined(STATUS_LED_BOOT)initr_status_led,
#endif/* PPC has a udelay(20) here dating from 2002. Why? */
#ifdef CONFIG_CMD_NETinitr_ethaddr,
#endif
#ifdef CONFIG_BOARD_LATE_INITboard_late_init,
#endif
#ifdef CONFIG_CMD_SCSIINIT_FUNC_WATCHDOG_RESETinitr_scsi,
#endif
#ifdef CONFIG_CMD_DOCINIT_FUNC_WATCHDOG_RESETinitr_doc,
#endif
#ifdef CONFIG_BITBANGMIIinitr_bbmii,
#endif
#ifdef CONFIG_CMD_NETINIT_FUNC_WATCHDOG_RESETinitr_net,
#endif
#ifdef CONFIG_POSTinitr_post,
#endif
#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE)initr_pcmcia,
#endif
#if defined(CONFIG_CMD_IDE)initr_ide,
#endif
#ifdef CONFIG_LAST_STAGE_INITINIT_FUNC_WATCHDOG_RESET/** Some parts can be only initialized if all others (like* Interrupts) are up and running (i.e. the PC-style ISA* keyboard).*/last_stage_init,
#endif
#ifdef CONFIG_CMD_BEDBUGINIT_FUNC_WATCHDOG_RESETinitr_bedbug,
#endif
#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)initr_mem,
#endif
#ifdef CONFIG_PS2KBDinitr_kbd,
#endifrun_main_loop,
};
此后若未在bootdelay时间内按下任意键,则会调用bootm或者后来的booti将内核、文件系统、设备树等拷贝到内存中,并执行相应的操作。自此,linux便在内存中跑起来了。
总结:
uboot将程序代码分成芯片级和板级,分别进行操作。芯片级最重要的莫过于start.S和lowlevel_init.S,其中包含关键的cp15协处理器设置,SDRAM初始化,以后uboot的重定位,这些需要根据特定的芯片进行设置。板级则是更多的和板型相关的部分,通过board_init_f和board_init_r两个板级的初始化接口,用户可以实现其中的包括串口、SDRAM、LED等外设的初始化,最终将linux拷贝到内存中运行。
系统启动过程(基于三星s5p6818 uboot)相关推荐
- 嵌入式linux的u-boot系统启动过程,嵌入式linux操作系统u-boot启动顺序以及代码解析...
嵌入式linux操作系统u-boot启动顺序以及代码解析 (9页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 9.9 积分 Bootloader/u-bo ...
- 基于AR9331(MIPS架构)分析系统启动过程(uboot)
前提: 1.AR9331是基于MIPS 24K CPU的一款WIFI1X1芯片,其SDK采用uboot作为引导.AR9331中定义的基地址是:0x9f00,0000 2.MIPS24K芯片,将固定的起 ...
- 4.基于Android 12 分析系统启动过程
基于Android12 分析系统启动过程 本文基于AOSP Android12的源码分析Android系统的启动流程. 由于这部分内容各版本之间差异不大,同样适用于Android12之前的版本. 1. ...
- 嵌入式linux的u-boot系统启动过程,【站友投递】U-boot启动过程详解
[站友投递]U-boot启动过程详解 来源:互联网 作者:denny 时间:2009-03-18 Tag:点击: 一.U-BOOT的目录结构 u-boot目录下有18个子目录,分别存放管理不通的源程序 ...
- 系统启动过程Linux
操作系统的启动分为两个阶段:引导boot和启动startup.引导阶段开始于打开电源开关,结束于内核初始化完成和 systemd 进程成功运行.启动阶段接管了剩余工作,直到操作系统进入可操作状态. 总 ...
- IBM AIX 5.3 系统管理 -- 系统启动过程详解
一. 启动过程 启动过程包含下面的一些步骤: 1.1启动一个系统的初始步骤是上电自检(Power On Self Test,POST).其目的是验证基本硬件是否处于正常的工作状态.同时初始化内存.键盘 ...
- rhel系统启动过程_Linux系统启动过程分析
[原创]Linux系统启动过程分析-wjlkoorey258-ChinaUnix博客 http://blog.chinaunix.net/uid-23069658-id-3142047.html 经过 ...
- rhel系统启动过程_Linux启动过程详解
一.Linux系统开机启动过程 第一步:开机自检,加载BIOS 第二步:读取MBR 第三步:Boot Loader grub引导菜单 第四步:加载kernel内核 第五步:init进程依据initta ...
- Android系统启动过程详解
转自:http://www.cloudchou.com/android/post-361.html 前言 一直想研究Android完整的启动过程,网上看了不少资料,也看了书上的一些说明,对这些观点有些 ...
- rhel系统启动过程_技术|Linux 开机引导和启动过程详解
你是否曾经对操作系统为何能够执行应用程序而感到疑惑?那么本文将为你揭开操作系统引导与启动的面纱. 理解操作系统开机引导和启动过程对于配置操作系统和解决相关启动问题是至关重要的.该文章陈述了 GRUB2 ...
最新文章
- 深度学习1:生成模型的输入数据集和可视化
- 这些年,我收集的JavaScript代码(二)
- 纪念一下我画的第一个印刷电路板!
- boost::python::ndarray相关的测试程序
- config.php开启redis,微擎如何开启redis,redis开启方法详解
- 《算法小白历险记》3.最长公共前缀【python-简单】
- linux 基础学习之目录与文件处理命令
- 备忘录AIX主机下用SHELL脚本编写FTP传某个目录下的文件到LINUX主机
- 微信平台商户开通证书相关问题盘点
- oracle加字段卡住,oracle添加字段引起的问题
- 提到单片机很多人都很觉得不陌生,大街小巷上面电子产品都用到
- bzoj 5394: [Ynoi2016]炸脖龙 扩展欧拉定理+树状数组
- .NET平台下几种SOCKET模型的简要性能供参考的讨论【转】
- poker游戏编码规则
- python非线性可分支持向量机模型(实现iris分类)
- 如何自己开发一个Android APP(3)——XML和Android
- 算法工程师实习校招面经 (上篇)
- Centos7 Jenkins 下载安装-----看此文章即可
- 来自GDPR的警示:是时候关注您的客户数据了
- 三国志手游挂机脚本 三国志辅助玩法介绍
热门文章
- python分号_python分号_python 分号_python加分号 - 云+社区 - 腾讯云
- 一个新APP在ASO方面的优化思路
- 幻读Java_关于幻读 - hellopretty - 博客园
- rog幻13和幻14区别 幻13和幻14评测怎么选
- 十个拿来就能用的网页炫酷特效
- duilib隐藏(显示)任务栏图标方式
- myeclipse使用(技术和快捷键)
- 互联网晚报 | 12月9日 星期四 | 微博正式登陆港交所;OPPO官宣首款自研芯片即将发布;腾讯启动“技术公益创投计划”...
- 阶跃函数和符号函数的傅里叶变换
- 王松波 计算机科学,王松波-华南农业大学华南农业大学动物科学学院