调试系统及ARM CPU挂死和总线挂死的DS-5(JTAG)调试方法

217 篇文章 29 订阅
98 篇文章 7 订阅

DFT和JTAG的关系

DFT(Design for Testability)和JTAG(Joint Test Action Group)是与电子设计和测试领域相关的两个重要概念,它们之间有一定的关联。

  1. DFT(Design for Testability):

    • DFT是一种设计方法,旨在使电子芯片或电路板更容易进行测试和诊断。
    • DFT的目标是在设计阶段考虑测试需求,以提高产品的可测试性、可维护性和可靠性。
    • DFT技术包括添加测试点、扫描链(Scan Chain)、Boundary Scan和Built-In Self-Test(BIST)等方法,以确保在制造和维修过程中可以有效地检测和修复故障。
  2. JTAG(Joint Test Action Group):

    • JTAG是一种通信标准和协议,最初由Joint Test Action Group组织定义,用于在集成电路(IC)上进行测试、编程和诊断。
    • JTAG使用一组标准信号线(TCK、TMS、TDI、TDO等)来实现对IC内部功能的访问,通常通过一种称为Boundary Scan的技术来实现。
    • Boundary Scan允许在IC的引脚上添加额外的逻辑,以简化测试和诊断过程。

关系: DFT和JTAG之间的关系在于JTAG通常用于实现DFT技术中的Boundary Scan功能。通过JTAG接口,可以访问IC内部的边界扫描逻辑,从而允许在生产和维修过程中执行故障检测、测试和诊断操作。这样,DFT技术中的Boundary Scan可以利用JTAG标准进行通信,以提高电子设备的可测试性。

总之,DFT是一种设计方法,旨在使电子设备更易于测试,而JTAG是一个用于实现DFT中Boundary Scan功能的通信标准和协议。它们共同为电子产品的测试和维修提供了关键的支持。

DFT(Design for Testability)是一种设计方法,旨在使电子芯片或电路板更容易进行测试和诊断。以下是一些常见的DFT手段和技术:

  1. 测试点(Test Points): 在电路板上添加专门的测试点,通常是连接到关键信号线的引脚。这些测试点允许测试仪器轻松地访问电路的内部,以进行测试和诊断操作。

  2. 扫描链(Scan Chain): 扫描链是一种DFT技术,通过在电路中添加可控制的扫描逻辑来实现。这允许测试仪器按位扫描(或测试)芯片的内部状态,以检测故障或执行其他测试操作。

  3. Boundary Scan(边界扫描): 边界扫描是一种基于JTAG(Joint Test Action Group)标准的DFT技术。它允许在集成电路的引脚上添加额外的逻辑,以实现对芯片内部和外部连接的测试和诊断。这种方法通常使用TAP(Test Access Port)控制JTAG链。

  4. Built-In Self-Test(BIST): BIST是一种自包含的测试技术,通过在芯片内部嵌入测试模块来执行测试。这些模块可以生成测试模式、执行测试、收集结果,并自动报告任何故障。BIST通常用于数字电路。

  5. 故障模拟(Fault Simulation): 在设计阶段使用故障模拟工具,模拟不同类型的故障(例如短路、开路、电压故障等)以评估设计的测试覆盖率。

  6. 辅助电路和逻辑: 在设计中添加额外的电路或逻辑,以支持测试操作,例如实现自检测、错误纠正或故障定位功能。

  7. 自动测试生成(ATPG): 使用ATPG工具生成测试模式,这些模式可以用于检测电路中的故障。ATPG工具会分析设计并自动生成测试用例。

  8. 故障覆盖率评估: 使用工具来评估测试用例的覆盖率,以确保设计可以有效地检测到各种故障类型。

  9. DFT验证: 对DFT技术和测试方案进行验证,以确保它们能够成功地检测到预期的故障,并满足测试需求。

DFT技术的选择取决于设计的复杂性、成本要求、测试覆盖率目标和可用的资源。通常,DFT技术的目标是提高产品的可测试性、可维护性和可靠性,从而降低制造和维修的成本。

调试系统的构成

ARM Jtag调试

调试子系统已经纳入了ARM的架构规范,从根本上定义了调试子系统的功能,组成,和角色,定义了各个组建的连接方式。包括DP,AP和调试组件。

DP:Debug Port.Jtag/SWD/SWJ等等

AP: Access Port.

JTAG信号

TAP控制器是JTAG边界扫描的核心控制器,有五个信号构成:

  1. TDI:测试数据输入,数据通过TDI输入JTAG口;
  2. TDO:测试数据输出,数据通过TDO从JTAG口输出;
  3. TMS:测试模式选择,用来设置JTAG口处于某种特定的测试模式;
  4. TCK:测试时钟输入;
  5. TRST:测试复位;

多芯片/多TAP JTAG测试拓扑结构,下图的JTAG系统对两颗芯片进行了JTAG调试:

一个3个TAP(IC)的DEBUG系统,DR寄存器不一定来源于TAP内部,比如在IR选择调试链的情况下,DR寄存器是下方DEBUG module中定义的寄存器,TDI/TDO将其串进来。而tap中的tdi_pad_i除了直接送给tdi_o外,然后直塞给DBG模块的dbg_tdi_i之外,没有其他作用。

从连接上可以推断出一个多TAP系统的几个特点:

1. TCK和TMS共享,所以所以多TAP系统的TAP状态是一致的,每个时刻处于同一个状态。

2.每个TAP下包括DEBUG系统和内部的DR寄存器,DEBUG系统的dbg_tdi_i输入和本TAP的TDI是同一个信号。

3.TDI到TDO之间是时序信号,通过控制输入的BIT流序列,可以控制不同的TAP在不同的命令下执行,例如选择FPGA TAP为调试DEBUG System状态,而MCU和CPU处于BYPASS状态。

4.状态机相同,但是命令状态不同,并不冲突。

5.如果JTAG调试的链路有多个调试链,BYPASS掉前级的调试链,需要追加前级 BYPASS REGISTER的长度的BIT,有的设计是1,有的设计是8或者其它值(需要查看JTAG手册)。这个BYPASS寄存器最小BIT是1,不能为0,原因有2:

1).如果不打一拍,TDI 直接通过前级的TDO传输到下级的TDI上面,如果不打一拍就直通的话,电路操作的时序会有问题,TCK和TDI的同步会出问题,所以,即便是BYPASS状态,也至少会放1BIT的FLOP在路径上。

2).根据JTAG协议,做SHIFT DR的时候,至少是要有1拍 SHIFT DR的动作,这个由协议和硬件状态该机决定,所以这个1BIT的FLOP也不能少。

下图忠实反映了JTAG电路级原理图:

除了DEBUG系统之外,其他的寄存器是集成在TAP中的,参看OR1200中 JTAG模块的实现:

应用

计算JTAG链中的IC数目:
一个重要的应用是IR值是全一值,表示BYPASS命令,在BYPASS模式中,TAP控制器中的DR寄存器总是单bit的,从输入TDI到输出TDO,通常一个周期,啥也不干。
可用BYPASS模式计算IC数目。如果每个IC的TDI-TDO链的延迟是一个时钟,我们可以发送一些数据并检测它延迟了多久,那么久可以推算出JTAG链中的IC数目。

可以看到,我们输入的是BIT1,输出的BYPASS书值为2,延迟1个BIT,所以一共有1个DEVICE。

ARM J-TAG调试

DS-5以及配套的DSTREAM/RealView是对ARM处理器进行裸机调试的利器,本文将针对工作中常见的CPU挂死情况,进行具体分析,将介绍如何区分CPU挂死以及通过CSAT区分总线挂死的具体操作步骤。

DSTREAM/RealView的连接方式如下:

调试器和目标ARM处理器之间是通过 JTAG连接起来的,JTAG则和芯片内部的调试IP相连接,在ARM规范里面,这个调试IP叫做ARM CoreSight,通过ARM CoreSight,我们可以调试挂在CoreSight之上的所有IP,包括处理器,总线,PMU等等等等。

 一个更加具体的CoreSight 连接方式如下图所示,这是通过DS5配置工具从一个具体的SOC中导出来的拓扑图:

CSCTI:The CoreSight Cross Trigger Interface.

CSCTM: The CoreSight  Cross Trigger Matrix.

可以看到,JTAG口通过TDI/TDO将CoreSight DebugPort串联起来,通过CS-DP,可以访问处理器等相关IP,下属的CSCTI/CPU/CSPMU 就如同上图中的DEBUG系统。

CSPMU是PMU单元,每个CPU有一个,如下图,一个双核A7 SOC,对应两个PMU单元。

CORESIGHT系统构成

coresight(一)coresight简介 - 知乎

coresight(一)coresight简介 | 骏的世界

通过RealView JTAG调试CPU的情况:

如果CPU挂死了怎么办?

比如下图这种情况,通过DS-5已经无法正常和CPU进行通信:

可以看到,通过debugger已经无法正常连接到A7处理器了,DS5报:

"Unable to stop device Cortex-A7, Cannot attain state requested!"

这个时候就需要另外一个工具了,它叫做csat,全称是(core sight access tool),顾名思义就是访问coresight的一个工具,它没有IDE,是一个命令行工具。

根据上面的拓扑图,在CPU无法成功连接的时候,我们依然可以通过CoreSight去访问其它的IP,包括总线.

下面我们试一下,下面展示一段通过CSAT的调试会话,需要注意的是,在使用CSAT连接目标平台前,必须关闭DS5的调试连接,否则CSAT会连接失败

Environment configured for ARM DS-5 (build 5190027)
Please consult the documentation for available commands and more details

C:\Program Files\DS-5\bin>csat
###############################################
#  CSAT -   CoreSight Access Tool      v2.0.7 #
#                                             #
#      [with Trace Commands v 2.0.1.1]        #
#                                             #
#      Copyright 2007-2011 ARM Limited        #
###############################################


CoreSight Component Data file read successfully.

%>con usb

Attempting to connect to ...USB
Connected to:ARM RealView ICE
Base H/W: V1 Rev G-01
TurboTAP Rev: 1.93
Firmware: 4.31.0, Build 33
%>chain dev=auto clk=A
Jtag clock set to 50000000A
ID:0 ARMCS-DP
%>help
:       Execute system command
alias   Alias a command name : alias cmd alias_name
cfb     Configure the RVI box.
cfg     Configure a RVI template
cfgbox  Configure the RVI box.
cfgtplate       Configure a RVI template
chain   Set or autodetect scan chain and jtag clock frequency
chn     Set or autodetect scan chain and jtag clock frequency
con     connect to an RVI unit via TCP or USB.
connect connect to an RVI unit via TCP or USB.
cscomp  Defaults for coresight component instances
cti     Coresight Register Component
dapenum Enumerate the AP units on the DAP.
dcn     Disconnect from RVI unit
devclose        Close the connection to the target device.
devopen Open a connection to a target device on the scan chain.
dfl     Load a file (bin or elf)
dfs     Save memory data to binary file
disconnect      Disconnect from RVI unit
dmf     Fill memory using the DAP.
dmr     Read memory using the DAP.
dmw     Write memory using the DAP.
dpe     Enumerate the AP units on the DAP.
dpfload Load a file (bin or elf)
dpfsave Save memory data to binary file
dpmemfill       Fill memory using the DAP.
dpmemread       Read memory using the DAP.
dpmemwrite      Write memory using the DAP.
dpregread       Read a DAP register.
dpregwrite      Write a DAP register.
drr     Read a DAP register.
drw     Write a DAP register.
dvc     Close the connection to the target device.
dvo     Open a connection to a target device on the scan chain.
echo    echo back whatever you just typed
etb     Coresight ETB component
etm     Coresight Register Component
exit    Exit program.
itm     Coresight Register Component
log     Control logging.
opt     set default options
options set default options
pmua9   Coresight Register Component
ptm     Coresight Register Component
reset   Reset the system, tap or via control register.
rnt     run a test on the system
rst     Reset the system, tap or via control register.
runtest run a test on the system
rviscan Scan for RVI units.
rvs     Scan for RVI units.
stm     Coresight Register Component
system  Execute system command
tfun    Coresight Register Component
tpiu    Coresight Register Component
trace   Control Trace Unit
trc     Control Trace Unit
v7dbg   Coresight V7 Debug component
batch   Run batch file
abort   Abort current command
abortall        Abort all commands
%>dvo 0
Open connection to device ID : 0x5BA00477, version 0x00000006
Msg returned with RVMOpenConn: ARM-DP Template using Rv-Msg.
%>dmr 0 0 1
0x00000000 : 0xEA00000D
%>dmr 0 0x00000000 0x60
0x00000000 : 0xEA00000D 0xEAFFFFFE 0xEAFFFFFE 0xEAFFFFFE
0x00000010 : 0xEAFFFFFE 0xEAFFFFFE 0xEA000001 0xEAFFFFFE
0x00000020 : 0xEA000003 0xE24EE004 0xE92D5FFF 0xEB0027AB
0x00000030 : 0xE8FD9FFF 0xE3A0605C 0xEA00003B 0xE3A00001
0x00000040 : 0xE3A01000 0xE3A02000 0xE3A03000 0xE3A04000
0x00000050 : 0xE3A05000 0xE3A06000 0xE3A07000 0xE3A08000
0x00000060 : 0xE3A09000 0xE3A0A000 0xE3A0B000 0xE3A0C000
0x00000070 : 0xE59F0204 0xE5901000 0xE3A03902 0xE0012003
0x00000080 : 0xE3520000 0x0A000006 0xE59F21F0 0xE3C11902
0x00000090 : 0xE3C118FF 0xE3C114FF 0xE1811002 0xE5801000
0x000000A0 : 0xEA000005 0xE3082001 0xE3C11102 0xE3C110FF
0x000000B0 : 0xE3C11CFF 0xE1811002 0xE5801000 0xE59F11C0
0x000000C0 : 0xE30E2FE8 0xE5913000 0xE30F4FFF 0xE0033004
0x000000D0 : 0xE1520003 0x1A00000E 0xE59F11A8 0xE5912000
0x000000E0 : 0xE3A03001 0xE1822003 0xE5812000 0xE59F0198
0x000000F0 : 0xE5901000 0xE5911000 0xE59F3190 0xE0011003
0x00000100 : 0xE35104EA 0x1A000002 0xE590F000 0xE59F0180
0x00000110 : 0xE590F000 0xE59F117C 0xE59F217C 0xE5913000
0x00000120 : 0xE1520003 0x1A000000 0xEAFFFFF7 0xE10F2000
0x00000130 : 0xE3C2201F 0xE38220D2 0xE121F002 0xE59FD15C
0x00000140 : 0xE10F0000 0xE3C0001F 0xE3800013 0xE38000C0
0x00000150 : 0xE3C00C02 0xE121F000 0xEE110F10 0xE3C00005
0x00000160 : 0xE3C00B06 0xEE010F10 0xEE110F10 0xE3C00A02
0x00000170 : 0xEE010F10 0xE59F1128 0xE5912000 0xE3C22001
%>dfs 0 0x00000000 0x60 save.bin
Successfully Saved data to file : save.bin
%>exit
executing exit command....Disconnected from device.
Disconnected from vehicle.
Done
CSAT exiting

C:\Program Files\DS-5\bin>

上面的例子使用CSAT读取了一段内存数据,我们断开CSAT,重新用DS5连接,dump同样一段数据,看是否一致:

经过对比后,通过CSAT获取到的内存数据和通过DS5得到的相同地址的数据是完全一致的,这说明CSAT的连接是正确的。

csat下通过dfs命令,也可以像DS5一样将数据dump出来保存为文件,上面展示了dfs命令导出save.bin文件的操作,我们观察以下save.bin的内容:

 也和上面通过DS5以及直接CSAT dmr得到的数据完全一致。

至此,我们介绍完了CSAT的用法。

通过这个例子看出来,ARM的生态完善不是一蹴而就的,经过这么多年的发展,其它的CPU已经很难跟得上了,毕竟,针对这个例子来说,如果CPU挂死了,我们还有CSAT可以用,不是么?

附图,关于上面使用命令的解释:

ARM调试架构图

常见调试器:

1.韩国CodeVisor ARM/RISCV

2.德国劳特巴赫 trace32. ARM/RISCV/ARC

3.ARM DS-5/Realview.

4.德国segger jlink.Xtensa/RISCV/ARM.

5.平头哥 T-Link. RISCV

TRACE功能

关于TRCE功功能的理解,可以参考下面博客:

IAR ETM Trace提供的调试功能

根据文档中的描述,TRACE功能的工作方式有点像是LINUX内核中的FTRACE,只不过前者是通过调试器和CPU ETM模块联合支持的,ETM模块用于记录CPU流水线历史的执行指令到ETB中,供调试器读取展示,ETB trace buffer用于记录从流水线中采样到的指令,准确来说,应该不是采样,因为采样可能会漏掉指令,但是ETB不会,更像是PC值每次都会打到ETB trace buffer中,保证一个不漏。

DEBUG架构图 

对于支持ETM Trace的调试系统,其DS-5 PROBE的拓扑会显示如下:

monday1.jpg

trace通路实现对master组件的数据追踪功能,使用ETM来追踪,它的工作原理是:
ETM负责追踪处理器和DSP的信息,将信息打包,通过ATB总线发送到trace bus上。trace bus上有trace funnel,funnel接收多个ATB总线数据,然后合并成一个ATB总线数据,发送给replicator。replicator接收到ATB数据,根据配置,将ATB数据发送给ETB和TPIU。

JTAG原理

本节来源于对于对:

https://gitee.com/tugouxp/hardware-lab.git

项目中jtag-jsp子项目的分析,adv_dbg_jsp_tb.v初始化阶段,调用reset_jtag函数,在8个TICK节拍中,保持TMS信号为高,JTAG将会自动复位:

无论从任何状态开始,只要经过5个TICK的TMS高电平,信号总会回到RESTE

其实不需要8个TMS信号,在任何情况下,5个TMS上升沿足够复位JTAG,下图中,不存在一个状态,连续五个TMS为1后仍然没有回到test-logic reset状态。

比如,从shift-dr出发,shift-dr->exit1_dr->update_dr->select_dr_scan->select_ir_scan->test_logic_reset.

或者从pause-dr出发,pause-dr->exit2_dr->update_dr->select_dr_scan->select_ir_scan->test_logic_reset.

都不会超过5步,5步之后,状态不变,所以保持5个时钟的TMS为高,一定能让CPU JTAG复位,下图测试的是将状态机设置为PAUSE_DR后,经过5个拍的TMS高电平,又回归为RESET状态,注意,RESET后会再次触发一个0 TMS周期,将状态及设置为C,也即是IDLE状态“

Test-Logic-Reset(TAP复位): 这是最常见的JTAG复位方式。在TAP控制器状态图中,有一条从任意状态到"Test-Logic-Reset"状态的路径。通过控制TMS(Test Mode Select)信号序列,可以将TAP控制器置于"Test-Logic-Reset"状态,从而复位被连接的逻辑电路。

从Pause DR状态经过5个TMS时钟后返回IDLE状态:

设置为PAUSE_DR状态

第二次复位:

总过程如下

reset_jtag函数分为两个阶段,第一个阶段发送5个TMS,TAP回到RESET状态,之后在由一个TCK周期将TAP送入 IDLE状态。后续工作都在IDLE下进行。

check_idcode stage

细节一 jtag_read_write_stream第三个参数set_last_bit的作用:

上图是set_last_bit为0,下图为set_last_bit为1时候的波形,对比来看,只是在最后一个BIT要不要设置TAP 状态为EXIT1_DIR的区别。

下降沿触发

set_ir函数,在半个周期内,进行update_ir状态切换和latched_jtag_ir锁存。

设置DEBUG调试命令的时序:

JTAG-调试拓扑

JSP连接拓扑

JSP提供了WR FIFO/RD FIFO用于实现JTAG TAP同芯片内部总线之间的交互:

1.调试器可以通过jtag tap向wr_fifo写入指令或者数据,内部IP通过WB的读从WR FIFO中获取指令或者数据,执行相应操作。

2.芯片内部IP可以通过WB总线写操作将数据写入rd_fifo,然后供jtag tap读取芯片的内部状态。

如下图所示:

FIFO读或者写是从JTAG的角度来看的,JTAG写操作的目标是WR FIFO,JTAG读操作的目标是RD FIFO,而从WB的角度来看正好相反。

FIFO是单向的,一个FIFO智能发送或者接收远程的数据,所以,对于需要双工通信的情况,设备中可能存在多个队列,比如典型的网络设备中,可能有分别用于收和发的队列,对于上图表示的工作模型,由于通信双方(jtag tap, wb)都可以作生产和消费方,所以在具体的应用场景中,要先指出使用的FIFO,才能明确各自的角色。

感觉JTAG通过WB访问jsp有些类似于RISCV调试模块中的SBA(System Bus Access),SBA的目的是为了绕过CPU,进行总线的访问。因为无论Program Buffer还是Abstract Command,都是CPU来帮我们执行的,然后得到相应的结果,而System Bus Access则不需要CPU的参与,直接使用总线的权限。

劳特巴赫调试器

Silicon上的调试系统逐渐标准化,比如RISCV架构的调试规范已经ratified,ARM也将coresight纳入到架构标准之中,这就为调试器的实现提供了很大的便利。

比如,劳特巴赫的调试器支持针对ARM处理器的TRACE功能(需要支持ETM),在调试器中包含了上G的TRACE BUFFER。不但如此,劳特巴赫也支持一些国内公司的标准化处理器的TRACE功能,比如平头哥C907。


附图

JTAG状态机,一共16个状态

状态机的下一个状态由当前状态和TMS信号共同决定,在TCK的上升沿进行状态切换:

多设备调试

Setting IR State Machine

Test Logic Reset->Run Test Idle->Select DR Scan->Select IR Scan->Capture IR->Shift IR->Exit1 IR->Update IR->Run Test Idle.

Setting DR State Machine

Test Logic Reset->Run Test Idle->Select DR Scan->Capture DR->Shift DR->Exit1 DR->Update DR->Run Test Idle.

选择调试模块

DR是TAP模块的数据寄存器,但却是DEBUG模块的选择寄存器,选择不同的DEBUG MODULE,按照配置DR的时序进行配置:

选择受调试模块

注意这里必须保证select_debug_module函数的高BIT设置为1,因为在adbg_top.v中,这个信号会被用来作为select_cmd,刷新module_id_reg选择子,后续在更新DBG内部的其他DR寄存器时,状态序列虽然一样,但是并不会设置此BIT,从而也不会刷新MODULE ID,保持在调试过程中,DEBUG ID不变。

TDI_I signal will be reflect to select_cmd in the next clk edge, 虽然我们无法控制有效数据同select_cmd的耦合,但是我们可以保证在update_dr阶段的时候,tdi_i信号输入为0,从而保证不会更新module_id selector寄存器。

TB中打印行号

$dispaly("Error: file %s @Line %0d", `__FILE__, `__LINE__);

JSP DBG

验证策略

通过jtag将jsp_data8写入 jsp,之后再用JTAG通过WB将写入的数据读回,并检测读回的和写入的是否相同。

初始化jsp_data8

jsp_data8写入wr_fifo之中,有8个FIFO寄存器。

两套时钟系统

TB测试文档初始化了两套时钟系统,其中JTAG的TCK是低速时钟,由JTAG数据传输业务驱动,并非周期性产生。而wb_clk_i则是wishbone总线时钟信号,WRFIFO的SRAM 的CLK也是它。是周期产生的,频率要比JTAG的TCK快很多。

JTAG-RESET 信号

TAP状态机中的test_logic_reset输出给外部IP,目的是作为RESET信号为DEBUG系统提供复位信号。

wen_sff是一个syncflop模块,目的是在jtag clock慢时钟域和wishbone快时钟域之间的跨时钟域同步。

总结:

1.CSAT通过总线访问内存和CPU通过总线访问内存一样,都是不经过IOMMU的。

2.所以,对于经过IOMMU映射的设备虚拟地址,只能通过设备本身发起访问请求,这样才经过IOMMU,得到正确的数据。

3.DS5 memory view不经过IOMMU,所以设备映射的IOMMU 虚拟地址无法交给DS5 memory view去查看。

参考文档

劳特巴赫调试工具完全支持SiFive Insight技术 - 知乎

Nexus-Trace同时用于小尺寸封装内核

https://www.corelis.com/educationdownload/JTAG-Tutorial.pdf

JTAG基础知识_jtag idcode_HBX_1024的博客-CSDN博客


结束!

  • 11
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

papaofdoudou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值