MIPS Uboot流程

MIPS架构 uboot的启动流程分析 本文详细介绍了基于MIPS架构的嵌入式系统启动流程,主要分为以下几个阶段: CPU早期初始化(start.S): 设置CP0状态、禁止中断 初始化Cache(可选) 初始化UART并打印启动信息 清除Watch寄存器 设置Status寄存器进入内核态 初始化系统定时器 DDR内存初始化(start_c.c): 读取strap引脚判断CPU/DRAM参数 调用init_ram()配置DDR控制器 设置并打印DRAM大小 Bootloader加载: 将压缩的Bootloader从Flash拷贝到RAM 刷新缓存 阅读详情

先熟悉下Mips架构

最近在学习MIPS架构,在系统计算机研究所的 网上读了不少关于MIPS的好文,下面的笔记就是基于上面的好文的摘抄。

一: MIPS寄存器别名记忆:
这一段在学习MIPSCPU架构,一直对mips的32个寄存器的约定 俗成的别名感到迷惑,今天在系统计算机研究所的网(http://www.xtrj.org/) 上看到一篇文章里有这方面的介绍,一下子豁然开朗原来这里的v,a,t前缀就是英文单词的缩写呀。

(呵呵,以前害得俺在<<see mips run>>书上都没有找到有助于理解的介绍)

;REGISTER NAME USAGE
$0 $zero        常量0(constant value 0)
$2-$3 $v0-$v1   函数调用返回值(values for results and expression evaluation)
$4-$7 $a0-$a3   函数调用参数(arguments)

$8-$15 $t0-$t7  暂时的(或随便用的)
$16-$23 $s0-$s7 保存的(或如果用,需要SAVE/RESTORE的)(saved)
$24-$25 $t8-$t9 暂时的(或随便用的)
$28 $gp         全局指针(Global Pointer)
$29 $sp         堆栈指针(Stack Pointer)
$30 $fp         帧指针(Frame Pointer)
$31 $ra         返回地址(return address)


二: MIPS 存储空间分配
MIPS将存储空间分为4块分别是:
kuseg, kseg0,kseg1 and kseg2
1. 0xFFFF FFFF mapped            kseg2
2. 0xC000 0000 unmapped uncached kseg1
3. 0xA000 0000 unmapped cached   kseg0
4. 0x8000 0000 2G                kuseg

   呵呵可以直观的看到只有kseg1是不需要映射(物理虚拟转换),没有被缓存的,也就是说只有kseg1的内存区域可以做引导的存储区(在这里放置引导用
flash 存储器).被cached区域必须等到MMU 的TLB被初始化后才可以使用的。


三: MIPS的CPU运行有3个态
1. User Mode.
2. Supervisor Mode.
3. and Kernel Mode.
   For simplicity, let's just talk about User Mode and Kernel Mode.
Please always keep this in mind:
CPU can ONLY access kuseg memory area when running in User Mode
CPU MUST be in kernel mode or supervisor mode when visiting kseg0, kseg1
and kseg2 memory area。
   呵呵,可以看出MIPS的CPU运行态和x86尤其是ARM基本都是一样的。就是用户层对物理空间地址的访问是也是受限制的(现代操作系统的先进之处 吗),必须通过使用驱动方式把操作代码运行在核心态。

四: MMU TLB
  MIPS CPU通过TLB来translates all virtual addresses generated by the CPU.下面谈谈ASID(Address Space Identifier). Basically, ASID, plus the VA(Virtual Address) are composed of the primary key of an TLB entry. 换句话说,虚拟 地址本身是不能唯一确定一个TLB entry的。一般而言,ASID的值就是相应的process ID. Note that ASID can minimized TLB re-loads, since several TLBentries can have the same virtual page number, but different ASID's. 对于一个多任务操 作系统来讲,每个任务都有 自己的4G虚拟空间

五: MMU 控制寄存器
对于一个Kernel Engineer来说,对MMU的处理主要是通过MMU的一些控制寄存器来完成的。MIPS体系结构中集成了一个叫做System Control Coprocessor (CP0)的部件。CP0就是我们常说的MMU控制器。在CP0中,除了TLB entry(例如,对RM5200,有48pair,96个TLB entry),一些控制寄存器提供给OS KERNEL来控制MMU的行为。 每个CP0控制寄存器都对应一个唯一的寄存器号。MIPS提供特殊的指令来对CP0进行操作。
mfc0 reg. CP0_REG
mtc0 reg. CP0_REG
我们通过上述的两条指令来把一个GPR寄存器的值assign给一个CP0寄存器,从而达到控制MMU的目的。

面简单介绍几个与TLB相关的CP0控制寄存器。
Index Register
这个寄存器是用来指定TLB entry的,当你进行TLB读写的时候。我们已经知道,例如,MIPS R5提供48个TLB pair,所以index寄存器的值是从0到47。换句话说,每次TLB写的行为是对一个pair发生的。这一点是与其他的CPU MMU TLB 读写不同的。 EntryLo0, EntryLo1 这两个寄存器是用来specify 一个TLB pair的偶(even)和奇(odd)物理(Physical)页面
地址。
一定要注意的是:
EntryLo0 is used for even pages; EntryLo1 is used for odd pages.
Otherwise, the MMU will get exception fault.
Entry Hi
Entry Hi寄存器存放VPN2,或一个TLB的虚拟地址部分。注意的是:ASID value也是在这里被体现。
Page Mask
MIPS TLB提供可变大小的TLB地址映射。一个PAGE可以是4K,16K,64K,256K,1M,4M或16M。这种可变PAGE SIZE提供了很好的灵活性,特别是对Embedded System Software. 对于Embedded System Softare,一个很大的区别就是:不允许大量的Page Fault. 这一点是传统OS或General OS在Embedded OS上的致命缺陷。也是为什么POSIX 1。B的目的所在。传统OS存储管理的一个原则就是:Page On Demand.这对大多Embedded System是不允许的。 For embedded system,往往是需要在系统初始化的时刻就对所有的 存储进行configuration, 以确保在系统运行时不会有Page Fault.

上述几个寄存器除了MAP一个虚拟页面之外,还包括设置一个页面的属性。其中包括:
writable or not; invalide or not; cache write back or write through

下面简单谈谈MIPS的JTLB。

在MIPS中,如R5000, JTLB is provided. JTLB stands for Joint TLB. 什么意思呢?
就 是 TLB buffer中包含的mixed Instruction and Data TLB 映射。有的CPU的Instruction
TLB 和Data TLB buffer 是分开的。
当然MIPS(R5000)确实还有两个小的,分开的Instruction TLB和Data TLB。但其大小很小。
主要是为了Performance,而且是对系统软件透明的。
在这里再谈谈MMU TLB和CPU Level 1 Cache的关系。
我们知道,MIPS,或大多数CPU,的Level 1 Cache都是采用Virtually Indexed and Physicall tagged. 通过这个机制,OS就不需要在每次进程切换的时候去flush CACHE。
为什么呢?
举一个例子吧:
进程A的一个虚拟地址 Addr1,其对应的物理地址是addre1;
进程B的一个虚拟地址Addr1,其对应的物理地址是addre2;
在某个时刻,进程 A在运行中,并且Addr1在Level 1 CACHE中。
这时候,OS does a context swith and bring process B up, having process A sleep. Now, let's assume that the first instruction/data fetch process B does is to access its own virtual address Addr1.
这时候CPU会错误的把进程A在Level 1中的Addr1的addr1返回给CPU吗?
我们的回答 应该是:不会的。
原因是:
当进程切换时,OS会将进程B的ASID或PID填入ASID寄存器中。请记住:对TLB的访问,
(ASID + VPN)才是Primary Key. 由于MIPS的CACHE属性是Virtually Indexed,
Physically tagged.所以,任何地址的访问,CPU都会issue the request to MMU for
 TLB translation to get the correct physical address, which then will be used
 for level cache matching.
与此同时,CPU会把虚拟地址信号传给Level 1 Cache 控制器。然后,我们必须等待MMU的Physical Address数据。只有physical tag也 匹配上了,我们才能说一个:Cache Hit. 所以,我们不需要担心不同的进程有相同的虚拟地址的事情。 弟兄们可以重温一下我们讲过的Direct Mapped; Full Associative, and Set Associative. 从而理解为什么Cache中可以存在多个具有相同虚拟地址的entry. For example,the above Addr1 for proccess A and Addr1 for process B



u-boot的启动过程比较简单,大致做下面的工作:
1.  cpu初始化
2.  时钟,串口,内存(ddr ram)初始化
3.  内存划分,分配栈,数据,配置参数,以及u-boot代码在内存中的位置。
4.  对u-boot代码做relocate
5.  初始化 malloc,flash,pci 以及外设(比如,网口)
6.  进入命令行或者直接启动Linux kernel

基本上,这就是u-boot的启动要做的事情,我也曾经大致看过arm的启动代码,也是类似。
不过,这里以mips作为例子进行介绍:

一、启动涉及到几个文件: start.S,cache.S, lowlevel_init.S和board.c
前三个都是汇编代码。

二、程序从start.S的_start开始执行。首先,初始化中断向量,寄存器清零,大致包括32个通用寄存器reg0-reg31和协处理器的一些寄存器:CP0_WATCHLO, CP0_WATCHHI, CP0_CAUSE, CP0_COUNT, CP0_COMPARE等等。

之后,配置寄存器CP0_STATUS,设置所使用的协处理器,中断以及cpu运行级别(核心级)。

配置gp寄存器,把GOT段的地址赋给gp寄存器。(gp寄存器的用处会在后面relocatecode的部分详细解释)

三、这时,开始执行lowlevel_init.S的lowlevel_init,主要目的是工作频率配置,比如cpu的主频、总线(AHB)、DDR工作频率等。
然后,调用cache.S的mips_cache_reset对cache进行初始化。接着调用cache.S的mips_cache_lock:这个调用的目的,起初让我不解,后来才知道,这时ddr ram并没有配置好,而如果直接调用c语言的函数必须完成栈的设置,而栈必定要在ram中。所以,只有先把一部分cache拿来当ram用。做法就是把一部分cache配置为栈的地址,锁定。这样,当读写栈的内存空间时,只会访问cache,而不会访问真的ram地址了。

这时,配置栈的地址,进行调用函数board_init_f(board.c)
进入函数board_init_f后,首先做一系列初始化:
timer_init 时钟初始化
env_init 环境变量初始化(取得环境变量存放的地址)
init_baudrate 串口速率
serial_init 串口初始化
console_init_f 配置控制台
display_banner 显示u-boot启动信息,版本号等
checkboard 执行board相关的操作。
init_func_ram 初始化内存,配置ddr controller
这一系列工作完成后,串口和内存都已经可以用了。然后,就要把内存进行划分,
在内存的最后一部分,留出u-boot代码大小的空间,准备把u-boot代码从flash搬移到这里然后,是堆的空间,malloc的内存就来自于这里。紧接着放两个全局数据结构bd_info、global_data和环境变量boot_params。最后,是栈的空间。

内存划分好,就准备进行relocate code了。

上次讲到,内存划分好,准备进行relocate code。
relocate code的意思是这样的。通常u-boot的执行代码肯定是在flash上(当调试的时候也可以放在ram上)。当启动起来以后,要把它从flash上搬移到ram里运行。这个工作就叫做relocate code。

但是,问题在于,flash上的地址和ram上的地址是不同的。当我们把代码从flash上搬移到ram上以后,当执行函数跳转时,代码里的函数地址还是flash上的地址,所以一跳就跳回去了。
这怎么办呢?
在u-boot里面用的是PIC(position-independent code)的方式解决这个问题。
简单介绍一下其原理。当你用PIC方式时,在用gcc编译时需加上 -fpic的选项。编译器会为你的可执行代码建立一个GOT(global offset table)的段。一个地址在GOT表中有一项,里面存放地址的信息,而在使用这个地址时,只要根据这个地址的编号(也可以叫做偏移量offset)找到表中相应的项目,就可以取得那个地址了。
而如果位置发生变化,只要对GOT表中的地址进行修改就可以了。
我们可以通过反汇编,看一个简单的函数调用例子:
lw t9,1088(gp)
jalr t9

这里,gp存放的就是GOT表的起始地址,而1088就是要调用函数的offset,也就是说GOT表的那个位置存放着它的地址。lw t9,1088(gp) 把函数地址放入t9, 然后调用就可以了。

知道了PIC的原理,解释u-boot relocate code的方法就简单了。
简单的说就把u-boot的执行代码直接从flash里copy到ram的相应区域。
然后,把GOT表中的地址都加上一个偏移量,这个偏移量就是flash里的地址与ram里的地址的差。
还有其他一些工作比如:设置新的栈指针,从flash代码里跳转到ram代码里等等。

之后,就进入board.c的board_init_r函数,在这个函数里初始化 malloc,flash,pci 以及外设(比如,网口),最后进入命令行或者直接启动Linuxkernel。
这样,u-boot的启动工作就完成了。

u-boot_mips移植分析 (转载的) 立即下载

相关推荐

ecamp:ecamp(这是旧版本;有关当前开发,请参见http:github.comecampecamp3)

这是eCamp的2.0版 联系人:Pirmin Mattmann 城市供应商 设置开发环境 先决条件 Git 或 (Windows / Mac)或 (Linux) 安装步骤 git克隆 光盘ecamp &&光盘dev 码头工人组成 访问以打开PhpMyAdmin 使用用户ecamp2和密码ecamp2登录将.database/ecamp_full_db.sql导入ecamp2_dev数据库 访问 。 发展愉快! :grinning_face_with_smiling_eyes:

linux uboot 参数传递,MIPS架构UBOOT和linux参数传递

base projcet:dgn1000cpu:amazon_se一:in uboot1. 传给内核参数的地址:首先设置参数存放在内存中的地址:in file:lib_mips/board.caddr_sp -= CFG_BOOTPARAMS_LEN;bd->bi_boot_params = addr_sp;// bd->bi_boot_params = 0x80001000;参数b...

weixin_39687814的博客 289

高仿喵播,视频直播 2.0版本

Github下载地址:https://github.com/XLAccount/MiaoBo 项目详解地址:http://www.code4app.com/blog-843201-350.html 快速集成RTMP的视频推流教程:http://www.code4app.com/blog-843201-315.html ffmpeg常用命令操作:http://www.code4app.com/blog-843201-326.html #关于IJKMediaFramework/IJKMediaFramework.h找不到的问题,下载后直接拉到项目中即可 下载地址:https://pan.baidu.com/s/1boPOomN 密码::9yd8 #BUG修复: 解决登录程序偶尔崩溃,修复轮播图片和页面控制器叠加等问题,修复新浪授权登录 (2016.9.7) 解决程序运行中偶尔崩溃问题,解决连续下拉刷新崩溃问题,优化代码 (2016.9.8) 优化直播页面,减少不必要的性能消耗,增加用户体验 (2016.9.11) 适配5s以上的机型除了6sPlus和6Plus延迟较大外,其余延迟都较小,网速好的话可以忽略不计 (2016.9.12) 新版本极大优化程序性能,修复关注数据异常等小问题,重新布局热门页面,减少因反复加载带来的性能消耗 (2016.9.13) 增加个人中心页面,采用下拉放大图片 ➕ 波纹效果 (2016.9.14) ![image text](https://github.com/XLAccount/ALLGIFS/blob/master/psb.gif) 展示图片 ![image](https://github.com/XLAccount/ALLGIFS/blob/master/psb-1.gif) 展示图片 ![image text](https://github.com/XLAccount/ALLGIFS/blob/master/psb-2.gif) 展示图片 ![image text](https://github.com/XLAccount/ALLGIFS/blob/master/psb-3.gif) 展示图片 感谢大神Monkey_ALin http://www.jianshu.com/users/9723687edfb5/latest_articles 的demo支持

u-boot 启动流程(mips)

u-boot的启动过程比较简单,大致做下面的工作:    1 cpu初始化    2 时钟,串口,内存(ddr ram)初始化    3 内存划分,分配栈,数据,配置参数,以及u-boot代码在内存中的位置。    4 对u-boot代码做relocate    5 初始化 malloc,flash,pci 以及外设(比如,网口)    6 进入命令行或者直接启动Linux kernel基本上

huangxb的笔记 3461

apophnia++:移至https://github.com/sroycode/apophniapp-开源

受无语症启发的图像处理项目移至https://github.com/sroycode/apophniapp

详解Numpy中的transpose方法

最近在学Numpy碰到了关于数组转置和换轴的概念,尤其是对tanspose方法有些疑惑,尤其是多维,查了不少资料,终于搞懂了。 Numpy数组的轴(axis) 所谓的**轴(axis)**可以理解为数组的维度。 具体而言,二维数组就是有两个维度,如果从空间上看,它就是一个平面,由x轴和y轴构成,而与此相对应的,该二维数组就有两个轴,x轴对应0轴,y轴对应1轴。 同理,三维数组有三个维度,空间上...

BlankGrid的博客 936

【uboot】mips架构uboot启动分析

MIPS Uboot

vickytong1018的博客 4001

从零搭建:基于Matlab/Simulink与FlightGear的飞行器实时可视化仿真平台

本文详细介绍了如何从零搭建基于Matlab/Simulink与FlightGear的飞行器实时可视化仿真平台。通过环境准备、模型部署、接口配置及联合仿真等步骤,帮助用户快速掌握飞行器仿真的核心技术,实现高效的三维可视化与实时数据交互。

weixin_29100399的博客 517

基于mips架构的uboot 启动流程 (2)

对于计算机系统来说,从开机上电到操作系统启动需要一个引导过程,这个引导程序就叫作 Bootloader 。   Bootloader 是在操作系统运行之前执行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射表,从而建立适当的系统软硬件环境,为最终调用操作系统内核做好准备。   对于嵌入式系统, Bootloader 是基于特定硬件平台来实现的。因此,几乎不

newnewman 7469

嵌入式开发中Tab与空格混用的危害与统一规范实践

在软件工程中,代码格式一致性是保障可读性与团队协作效率的基础。从原理上看,制表符(Tab)与空格(Space)在存储与显示机制上存在本质差异:Tab是控制字符,其显示宽度取决于编辑器设置;空格则是固定宽度的字符。这种差异导致混用时会引发代码对齐错乱、版本控制冲突及跨平台兼容性问题,严重影响工程的可维护性。尤其在嵌入式开发领域,资源受限环境与高可靠性要求使得统一的编码规范更为关键。通过配置编辑器、集成clang-format等自动化工具,可以强制实现缩进风格的一致性,从而提升代码质量与团队协作效率。本文聚焦于

weixin_42545636的博客 276

使用MLP解决OCR问题(OpenCV)(下)

分类模型:        分类模型涉及的一个比较关键的问题就是输出的10维向量是如何与具体的类别挂钩的。实际上:10维向量的每一位都代表一类,在对于训练集的表达中,如果输入数据是0,则10维向量的第一位赋值为1,其余均为0。即0对应[1,0,0,0,0,0,0,0,0,0]。MLP模型训练完成后,就需要对用户输入的数据所属类别进行判定。这时得到的输出数据基本不可能是正好的所属类为1,其他位置为0

qq_18515405的专栏 1906

mips的U-Boot分析及移植

<br />要注意mips具有流水线可见性,所以跟在跳转指令后的下一条指令,在执行跳转到的地方前,都会执行,这个叫分支延迟。但是编译器会隐藏该特性,但可以通过设置”.set noreorder”来禁止编译器重新组织代码顺序。<br />每个板子都有自己的lds文件。这个主要是用来说明编译生成的指令,及运行过程中用到的数据放置的位置。这个可以参考ld的手册。比如board/dbau1×00/u-boot.lds。<br />OUTPUT_FORMAT(“elf32-tradbigmips”, “elf32-t

babyfans的专栏 2864

Qt中常见的面试题总结

基于Qt开发的一些常用面试题

一名热爱编程的程序员 8492

mips架构uboot 启动流程分析

摘要:本文分析了mips 架构的U-Boot启动流程的两个阶段。第一阶段通过汇编代码完成硬件初始化,包括CPU复位、缓存设置、DDR初始化和代码从SPI Flash拷贝到RAM等操作,最终跳转到第二阶段C代码。第二阶段通过start_c()函数进行更详细的外设初始化,包括DRAM类型检测、时钟设置、LED控制等,最后通过start_kernel()进入内核启动流程,完成GPIO、中断、Flash等初始化后跳转到内核执行。整个过程展示了从底层硬件初始化到高级系统启动的完整过渡。

陌上花开缓缓归以的博客 392

RK3588S开发板实战:手把手教你调试IMX415摄像头(附常见问题排查)

本文提供了基于RK3588S开发板调试IMX415摄像头的完整实战指南。从硬件连接到软件配置,详细解析了设备树(DTS)设置、驱动加载验证及V4L2工具链使用,并附带了常见问题排查方法,帮助开发者快速解决摄像头调试中的典型问题,实现图像采集与性能调优。

assembly8low的博客 590

Python在Windows环境下驱动乐视三合一摄像头的深度图像处理实践

本文详细介绍了在Windows系统下,使用Python驱动乐视三合一摄像头进行深度图像处理的完整实践流程。从环境配置、驱动安装、OpenNI与OpenCV库的部署,到深度与彩色数据流的读取、可视化、三维点云转换以及背景移除等应用开发,为开发者提供了从零开始掌握深度摄像头编程的实战指南。

gitlab7runner的博客 231

基于mips架构的uboot启动流程(4)

1.    引言 文章是2年前移植u-boot时候分析的,u-boot方面文章比较多,但是mips方面的稍微少一点,因此贴上来就当资料搜集贴吧. 1.1.    参考资料 詹荣开 原作者未考证  2.    需求分析 Bootloader 在嵌入式系统中一般起硬件初始化和内核引导的作用,在产品中使用的Bootloader只要保证基本的功能和引导速度即可, 而作为开发使用的B

yexiang优快云的专栏 1163

注意力机制与经典 CV 网络:PyTorch 实现与实战

注意力机制(SE/空间/CBAM/Non-Local)通过权重重标定让模型聚焦关键特征,经典 CNN(ResNet/DenseNet/FPN)提供了深层网络、特征复用和多尺度融合的基础骨架。建议先在 ResNet 上插入 SE 模块(通道注意力)做基线实验,再根据任务类型引入空间注意力(分割任务)或非局部注意力(视频任务)。CIFAR-10 数据集适合快速验证注意力模块的有效性,迁移到更大数据集时保持相同的结构设计思路即可。

FJN110的博客 61

基于mips架构的uboot 启动流程 (1)

流程分析 *************************************************** 1.最开始系统上电后 ENTRY(_start)程序入口点是 _start board/mingddie/u-boot.lds 2._start: cpu/mips/start.S 3. la t9, board_init_f 将函数board_init_f地址赋予t9 j

newnewman 9674

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

mips uboot nor flash适配流程

本文介绍了SPI NOR Flash的驱动架构和初始化过程。驱动架构采用分层设计,从用户空间到MTD子系统,再到SPI NOR驱动(spi-nor.c),最终连接SPI控制器驱动和硬件总线。驱动初始化函数initFlash()通过m25p_probe()完成设备探测,包括SPI设备设置、JEDEC ID识别、读写操作配置等。文中详细列出了各类Flash芯片的JEDEC ID信息表(m25p_ids[]),包含Atmel、EON、ESMT、GigaDevice等厂商的多种型号,记录了容量、扇区大小、时钟频率等

陌上花开缓缓归以的博客 283

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

基于mips架构的uboot启动流程(3)

要注意mips具有流水线可见性,所以跟在跳转指令后的下一条指令,在执行跳转到的地方前,都会执行,这个叫分支延迟。但是编译器会隐藏该特性,但可以通过设置”.set noreorder”来禁止编译器重新组织代码顺序。 每个板子都有自己的lds文件。这个主要是用来说明编译生成的指令,及运行过程中用到的数据放置的位置。这个可以参考ld的手册。比如board/dbau1x00/u-boot.lds。 O

newnewman 3918

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

上一篇: vimrc设置
肋牛满面
博客等级 码龄14年 28粉丝 3原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值