1404|6

40

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

《嵌入式软件的时间分析》Cortex-M3的三级流水线与分支预测【阅读笔记3】 [复制链接]

本帖最后由 luyism 于 2024-6-25 21:25 编辑

《嵌入式软件的时间分析》Cortex-M3的三级流水线与分支预测【阅读笔记3】

在本书的第二章2.6小节中有说到指令流水线,但是说的并不是很详细。这里,我将结合ARM V7架构的Cortex-M3内核,进一步探讨其三级流水线和分支预测背后的设计原理。

一、背景与重要性

在嵌入式系统的世界里,处理器的效率直接关系到设备的性能、功耗乃至最终产品的竞争力。Cortex-M3,作为ARM公司针对微控制器领域推出的一款高性能、低功耗内核,广泛应用于工业控制、消费电子、汽车电子等领域。它的设计精髓之一便是引入了三级流水线结构,这一设计不仅沿袭自更高端的ARM处理器,而且在保持较高代码密度的同时,显著提升了指令执行速度,这对于资源有限的嵌入式系统来说至关重要。

二、Cortex-M3处理器概述

Cortex-M3处理器是ARM公司设计的一款32位嵌入式处理器,广泛应用于微控制器(MCU)市场。它具有高效的性能和低功耗的特点,非常适合需要实时处理的应用。理解Cortex-M3的流水线和分支预测技术,对于优化嵌入式系统的性能至关重要。

2.1 程序计数器(PC)基础

程序计数器(R15)是一个特殊的寄存器,指向当前的程序地址。如果修改它的值,就能改变程序的执行流。在编写汇编代码时,可以通过直接操作PC来进行跳转或计算相关地址操作。

在Cortex-M3 权威指南中有下面这段话,具体含义下面再具体解释:

三、三级流水线结构

Cortex-M3处理器采用了三级流水线结构,分别为取指令(Fetch),解码(Decode),和执行(Execute)阶段。这种结构在简化设计和降低功耗的同时,仍然能够提供足够的处理能力。

3.1 取指令(Fetch)

预取单元:在这个阶段,处理器根据程序计数器(PC)的值从内存中预取指令。Cortex-M3支持Thumb-2指令集,该指令集的指令长度可变(16位或32位),因此预取单元需要能够灵活处理不同长度的指令。Cortex-M3的指令缓存(I-Cache)可以加速这个过程,使得指令可以快速读取。

  1. ; 伪代码表示取指令阶段
  2. F: instruction = memory[PC]

指令预取机制使得处理器在执行当前指令时,提前取出下一条指令,从而减少等待时间。指令缓存则存储最近使用的指令,提高缓存命中率,进一步提升取指令速度。

PC更新:取指后,程序计数器会自动递增到下一条指令的地址,对于跳转或分支指令,则根据分支目标地址更新。

3.2 解码(Decode)

指令解码器:取出的指令在这一阶段被解码,处理器确定要执行的操作和涉及的寄存器或内存地址。解码阶段还负责处理一些简单的指令,如立即数运算。

  1. ; 伪代码表示解码阶段
  2. D: operation, operands = decode(instruction)

Cortex-M3处理器的解码单元能够快速解析指令,并生成相应的控制信号,为后续的执行阶段做好准备。

3.3 执行(Execute)

在这一阶段,处理器执行指令,进行算术运算、逻辑运算、内存访问等操作。执行阶段的结果可能会写回寄存器或内存。

  1. ; 伪代码表示执行阶段
  2. E: result = execute(operation, operands)

Cortex-M3的执行单元包括算术逻辑单元(ALU)、乘法器、加载/存储单元等,可以高效地完成各种操作。

结果写回:执行完毕后,操作结果会被写回到寄存器或内存中,准备供后续指令使用。

3.4 PC状态变化

在具有三级流水线的嵌入式处理器中,当处理器正忙于执行第 N 条指令时,流水线的前探特性已使得第 N+2 条指令处于取指阶段,以维持指令执行的连续性和效率。关键在于,程序计数器(PC)并不指向正在执行的指令,而是前瞻地指向正在被取指的指令,即第 N+2 条。这一设计旨在提前准备,确保处理器的指令供给不间断。

考虑到处理器指令长度的多样性,PC的增值得以精确计算。在硬件设计中,每次取指令操作提取32位数据,但实际上指令长度可以是16位或32位,这取决于处理器的工作模式:

  • ARM状态:处理器工作在标准32位指令集(ARM状态)时,每条指令占用4字节。由于流水线机制,当执行第 N 条指令时,PC实际上已经超前指向了第 N+2 条指令的地址。因此,如果读取PC,得到的值将是当前执行指令地址基础上增加8字节(即两个4字节指令长度)的结果。
  1. 0x1000: MOV R0, PC ; R0 = 0x1008
  • Thumb状态:切换至Thumb指令集后,大多数指令精简为16位,每条指令仅占2字节。尽管硬件每次依然提取32位,但实际上可以包含两条16位Thumb指令。因此,在Thumb模式下,执行第 N 条指令时,PC指示的将是第 N+2 条指令的地址,但由于Thumb指令每条2字节,PC增加的值仅为4字节,即当前执行指令地址加4字节。
  1. 0x1000: MOV R0, PC ; R0 = 0x1004

也就是说:

  • ARM7中的“读PC指令的地址+8”:在ARM7中,假设有一条指令MOV R0, PC位于地址0x1000,由于ARM7的特性,读取PC时返回的将是0x1008,即当前指令地址加8。但注意,这是在非Thumb模式下的情况。当你读取PC寄存器的值时,由于前面提到的两个阶段的自增,你会得到一个指向当前指令之后两条指令地址的值,这是因为每次自增都是4字节,合计8字节。

  • Cortex-M3(Thumb模式)的“读PC指令的地址+4”:在Cortex-M3中,同样执行MOV R0, PC,但假设此时代码在Thumb模式下,位于地址0x1000,由于Thumb指令每条2字节且同样经历了两次流水线自增,读取PC时R0将会被赋值为0x1004,即当前指令地址加4。在CM3的Thumb模式下,同样经过两次自增,但每次自增2字节,因此读取到的PC值会指向当前指令之后的下一条指令地址,总共增加4字节。

值得注意的是,Thumb-2指令集是Thumb指令集的一个扩展,它巧妙融合了16位和32位指令,使得Thumb状态下也能实现与ARM指令集相近的代码密度和功能完整性。尽管Thumb-2指令长度可变,上述PC值的增量规则(ARM状态+8字节,Thumb状态+4字节)仍然适用,因为PC始终反映的是取指阶段的地址,而Thumb-2的执行效率和代码布局优化通过编译器在生成代码时予以考虑,不影响PC值的计算逻辑。

ARM ®Architecture Reference Manual ARMv7-A and ARMv7-R edition 手册中有下面这样一段话。

向PC写入地址:无论是ARM指令集还是Thumb指令集,向PC寄存器写入一个地址都会导致程序跳转到该地址。这种机制用于实现跳转指令和函数调用等操作。

Thumb指令集对PC的访问限制:大多数Thumb指令无法直接访问PC寄存器。这是因为Thumb指令集设计时考虑到了指令长度的限制和简化指令集的目的。然而,某些特定的Thumb指令仍然可以使用PC,例如分支指令。

ARM指令集对PC的通用访问:ARM指令集允许对PC寄存器进行更通用的访问。许多ARM指令可以将PC作为通用寄存器使用,例如进行算术运算、加载/存储等。然而,ARM体系结构规范不推荐将PC用于除程序计数器以外的用途。

综上所述,不论是ARM7还是Cortex-M3,程序计数器读取的值受到其内部指令流水线机制的直接影响。在ARM7中,由于使用32位ARM指令,读PC指令时返回的地址会是当前指令地址加8;而在Cortex-M3的Thumb模式下,由于指令长度变为16位,读取PC指令时返回的地址则是当前指令地址加4。

3.5 有无流水线的对比

无流水线处理器(也称作单周期处理器)执行指令的过程简单直接:从内存中读取一条指令,解码,执行,然后重复此过程。这种设计直观易懂,但每条指令的执行都必须等待前一条指令完全结束后才能开始,导致处理器在大部分时间里处于等待状态,效率低下。

引入流水线后的变化:Cortex-M3的三级流水线设计打破了这一限制。在任何给定时刻,处理器都在同时执行三个不同阶段的操作。形象地说,就像工厂生产线上的装配工,一个负责取零件,一个负责组装,第三个负责检验打包,每个人都在专注自己的任务,无需等待前一个人完成所有工作再开始。这样一来,即便每一步操作的时间没有减少,但由于三个步骤并行进行,整体上大大提高了处理器的吞吐量。

四、分支预测技术

为了提高处理效率,Cortex-M3处理器还采用了分支预测技术。分支预测的目的是在遇到分支指令(如条件跳转)时,提前猜测程序的执行路径,从而减少流水线停顿。

4.1 分支预测的工作原理

  1. 分支目标缓冲器(BTB) Cortex-M3使用BTB来记录分支指令的历史信息,包括目标地址和执行结果(跳转或不跳转)。当处理器遇到分支指令时,会查询BTB,根据历史信息进行预测。

    1. ; 伪代码表示BTB查询和预测
    2. if BTB.contains(PC) then
    3. prediction = BTB[PC].prediction
    4. target_address = BTB[PC].target_address
    5. else
    6. prediction = not taken
  2. 更新预测信息 当分支指令实际执行后,处理器会根据结果更新BTB中的信息,以提高下一次预测的准确性。

    1. ; 伪代码表示BTB更新
    2. BTB[PC].prediction = actual_outcome
    3. BTB[PC].target_address = actual_target_address

分支预测技术的引入,可以显著减少由于分支跳转导致的流水线停顿,从而提高处理器的整体性能。

五、优化技巧

理解Cortex-M3的流水线和分支预测技术后,我们可以采用一些优化技巧来提升系统性能。

  1. 减少分支指令 尽量减少条件跳转和循环分支的使用,因为这些指令会导致流水线停顿和分支预测失败。可以通过无条件跳转、查表等方式来优化代码。

    1. // 优化前
    2. if (condition) {
    3. do_something();
    4. } else {
    5. do_something_else();
    6. }
    7. // 优化后
    8. do_something_table[condition]();
  2. 循环展开 循环展开(Loop Unrolling)是另一种优化技术,通过减少循环控制指令的次数,提高指令并行度和缓存命中率。

    1. // 优化前
    2. for (int i = 0; i < 100; ++i) {
    3. process(data<i>);
    4. }
    5. // 优化后
    6. for (int i = 0; i < 100; i += 4) {
    7. process(data<i>);
    8. process(data[i+1]);
    9. process(data[i+2]);
    10. process(data[i+3]);
    11. }
  3. 指令调度 指令调度是重新排列指令的执行顺序,以减少流水线停顿和数据依赖。可以使用编译器的优化选项或手动调整代码。

    1. // 优化前
    2. int a = b + c;
    3. int d = e + f;
    4. // 优化后
    5. int a = b + c;
    6. int d = e + f; // 避免数据依赖

六、实战案例:优化图像处理算法

为了更好地说明这些优化技巧,我们以图像处理算法为例。假设我们有一个简单的边缘检测算法,需要对图像进行遍历和处理。

6.1 原始代码

  1. void edge_detection(uint8_t *image, uint8_t *output, int width, int height) {
  2. for (int y = 1; y < height - 1; ++y) {
  3. for (int x = 1; x < width - 1; ++x) {
  4. int gx = -image[(y-1)*width + (x-1)] + image[(y-1)*width + (x+1)]
  5. -2*image[y*width + (x-1)] + 2*image[y*width + (x+1)]
  6. -image[(y+1)*width + (x-1)] + image[(y+1)*width + (x+1)];
  7. int gy = -image[(y-1)*width + (x-1)] - 2*image[(y-1)*width + x] - image[(y-1)*width + (x+1)]
  8. +image[(y+1)*width + (x-1)] + 2*image[(y+1)*width + x] + image[(y+1)*width + (x+1)];
  9. output[y*width + x] = (uint8_t)(sqrt(gx*gx + gy*gy));
  10. }
  11. }
  12. }

6.2 优化后代码

  1. 数据对齐

确保图像数据对齐,以提高缓存利用率。

  1. uint8_t image[IMAGE_SIZE] __attribute__((aligned(64)));
  2. uint8_t output[IMAGE_SIZE] __attribute__((aligned(64)));
  1. 循环展开

减少循环控制指令,提高处理效率。

  1. void edge_detection(uint8_t *image, uint8_t *output, int width, int height) {
  2. for (int y = 1; y < height - 1; ++y) {
  3. for (int x = 1; x < width - 1; x += 4) {
  4. int gx1 = -image[(y-1)*width + (x-1)] + image[(y-1)*width + (x+1)]
  5. -2*image[y*width + (x-1)] + 2*image[y*width + (x+1)]
  6. -image[(y+1)*width + (x-1)] + image[(y+1)*width + (x+1)];
  7. int gy1 = -image[(y-1)*width + (x-1)] - 2*image[(y-1)*width + x] - image[(y-1)*width + (x+1)]
  8. +image[(y+1)*width + (x-1)] + 2*image[(y+1)*width + x] + image[(y+1)*width + (x+1)];
  9. output[y*width + x] = (uint8_t)(sqrt(gx1*gx1 + gy1*gy1));
  10. int gx2 = -image[(y-1)*width + (x+0)] + image[(y-1)*width + (x+2)]
  11. -2*image[y*width + (x+0)] + 2*image[y*width + (x+2)]
  12. -image[(y+1)*width + (x+0)] + image[(y+1)*width + (x+2)];
  13. int gy2 = -image[(y-1)*width + (x+0)] - 2*image[(y-1)*width + x] - image[(y-1)*width + (x+2)]
  14. +image[(y+1)*width + (x+0)] + 2*image[(y+1)*width + x] + image[(y+1)*width + (x+2)];
  15. output[y*width + (x+1)] = (uint8_t)(sqrt(gx2*gx2 + gy2*gy2));
  16. int gx3 = -image[(y-1)*width + (x+1)] + image[(y-1)*width + (x+3)]
  17. -2*image[y*width + (x+1)] + 2*image[y*width + (x+3)]
  18. -image[(y+1)*width + (x+1)] + image[(y+1)*width + (x+3)];
  19. int gy3 = -image[(y-1)*width + (x+1)] - 2*image[(y-1)*width + (x+2)] - image[(y-1)*width + (x+3)]
  20. +image[(y+1)*width + (x+1)] + 2*image[(y+1)*width + (x+2)] + image[(y+1)*width + (x+3)];
  21. output[y*width + (x+2)] = (uint8_t)(sqrt(gx3*gx3 + gy3*gy3));
  22. int gx4 = -image[(y-1)*width + (x+2)] + image[(y-1)*width + (x+4)]
  23. -2*image[y*width + (x+2)] + 2*image[y*width + (x+4)]
  24. -image[(y+1)*width + (x+2)] + image[(y+1)*width + (x+4)];
  25. int gy4 = -image[(y-1)*width + (x+2)] - 2*image[(y-1)*width + (x+3)] - image[(y-1)*width + (x+4)]
  26. +image[(y+1)*width + (x+2)] + 2*image[(y+1)*width + (x+3)] + image[(y+1)*width + (x+4)];
  27. output[y*width + (x+3)] = (uint8_t)(sqrt(gx4*gx4 + gy4*gy4));
  28. }
  29. }
  30. }

七、总结

通过理解Cortex-M3处理器的三级流水线和分支预测技术,并结合实际应用中的优化技巧,我们可以显著提高嵌入式系统的性能。在开发过程中,不仅需要理论知识,还需要不断实践和调整,以找到最适合具体场景的优化方案。

此帖出自汽车电子论坛

最新回复

ARM M3/4系列使用的都是三级流水吧?最近在看RISC-V系列的芯片,里面是六级流水线设计,但是资料太少了,不知道楼主有没有相关的资料介绍看一下呀?感觉好像RISC-V要找一些资料远远没有ARM系列的资料齐全和好找,ARM的Cortex-M系列有一本权威指南,里面基本涵盖了内核大大小小各方各面的知识,但RV系列的就没有类似这样的书   详情 回复 发表于 2024-6-26 14:09
点赞(1) 关注(1)

回复
举报

7032

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

Thumb-2指令集是Thumb指令集的一个扩展,它巧妙融合了16位和32位指令,使得Thumb状态下也能实现与ARM指令集相近的代码密度和功能完整性,这个是这样的

此帖出自汽车电子论坛

点评

确实如此,Thumb-2是Thumb指令集的一个重要扩展,它结合了16位和32位指令,使得在Cortex-M3这样的处理器上运行时,能够在保持高代码密度的同时实现与ARM指令集相似的功能。Cortex-M3通过Thumb-2实现了高效的指令集架  详情 回复 发表于 2024-6-26 13:11
 
 

回复

1977

帖子

1

TA的资源

五彩晶圆(初级)

板凳
 

有了分支预测之后,自己使用汇编代码写的高精确代码中如果存在判断跳转,会发现这段代码执行时间每次都会不太一样,存在抖动

此帖出自汽车电子论坛

点评

你提到的情况确实存在。分支预测在提高执行效率的同时,也引入了不确定性,因为预测失败时需要清空流水线,重新加载正确的指令。这会导致执行时间的不稳定,尤其是在高精度需求的汇编代码中。为了减少这种抖动,可以  详情 回复 发表于 2024-6-26 13:12
 
 
 

回复

40

帖子

0

TA的资源

一粒金砂(中级)

4
 
Jacktang 发表于 2024-6-26 07:20 Thumb-2指令集是Thumb指令集的一个扩展,它巧妙融合了16位和32位指令,使得Thumb状态下也能实现与ARM指令集 ...

确实如此,Thumb-2是Thumb指令集的一个重要扩展,它结合了16位和32位指令,使得在Cortex-M3这样的处理器上运行时,能够在保持高代码密度的同时实现与ARM指令集相似的功能。Cortex-M3通过Thumb-2实现了高效的指令集架构设计,使其在嵌入式系统中具备更强的灵活性和功能性。

此帖出自汽车电子论坛
 
 
 

回复

40

帖子

0

TA的资源

一粒金砂(中级)

5
 
bobde163 发表于 2024-6-26 08:43 有了分支预测之后,自己使用汇编代码写的高精确代码中如果存在判断跳转,会发现这段代码执行时间每次都会不 ...

你提到的情况确实存在。分支预测在提高执行效率的同时,也引入了不确定性,因为预测失败时需要清空流水线,重新加载正确的指令。这会导致执行时间的不稳定,尤其是在高精度需求的汇编代码中。为了减少这种抖动,可以尝试优化代码结构,减少复杂分支,或者使用一些编译器提供的提示来帮助分支预测器提高准确率。总之需要在效率和确定性之间找到一个平衡点。

此帖出自汽车电子论坛
 
 
 

回复

19

帖子

0

TA的资源

一粒金砂(中级)

6
 

ARM M3/4系列使用的都是三级流水吧?最近在看RISC-V系列的芯片,里面是六级流水线设计,但是资料太少了,不知道楼主有没有相关的资料介绍看一下呀?感觉好像RISC-V要找一些资料远远没有ARM系列的资料齐全和好找,ARM的Cortex-M系列有一本权威指南,里面基本涵盖了内核大大小小各方各面的知识,但RV系列的就没有类似这样的书

此帖出自汽车电子论坛

点评

确实,ARM Cortex-M3/M4系列以其成熟的三级流水线设计著称,这也是它们在嵌入式领域广受欢迎的原因之一。RISC-V就像是乐高积木,因为它开放,不同的厂家可以根据自己的想法来搭建。就像你盖房子,可以简单搭个小屋,  详情 回复 发表于 2024-6-26 18:25
 
 
 

回复

40

帖子

0

TA的资源

一粒金砂(中级)

7
 
peterhzm 发表于 2024-6-26 14:09 ARM M3/4系列使用的都是三级流水吧?最近在看RISC-V系列的芯片,里面是六级流水线设计,但是资料太少了,不 ...

确实,ARM Cortex-M3/M4系列以其成熟的三级流水线设计著称,这也是它们在嵌入式领域广受欢迎的原因之一。RISC-V就像是乐高积木,因为它开放,不同的厂家可以根据自己的想法来搭建。就像你盖房子,可以简单搭个小屋,也可以建个豪华大厦。这就意味着,RISC-V处理器里的流水线设计,真的要看是谁在做这个处理器。有的厂家可能会设计得很简单,就几级流水线,这样处理器耗电少,体积小,特别适合那些电池供电的小设备;而有的厂家则可能为了追求速度和性能,搞很多级的流水线,就像高速公路上加了很多车道,车流(指令)跑得更快,适合做复杂的计算任务。 所以,说到RISC-V的流水线,它不像有些固定的处理器架构那样一成不变,而是很有弹性,每个厂家都能根据实际情况来定制。正因为这样,找资料有时候会觉得不如成熟架构那么方便。但RISC-V的资源库也正在快速成长,比如,RISC-V基金会的官方网站、GitHub上的开源项目、以及各种技术论坛和博客,都是获取RISC-V最新资讯和技术细节的好去处。
我最经也在看一本叫《RISC-V 开放架构设计之道》的书,不过这里面主要是和RISC-V指令集以及ISA架构相关的,我的帖子里就有这本书的下载方式,同时你也可以看看《计算机组成与设计(基于RISC-V架构)》

此帖出自汽车电子论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
推荐帖子
技术人生“白天求生存,晚上谋发展”

这句话最近在团队经常有人说,其实我也深有体会,因为我看到的技术牛人他们背后肯定有一段很长时间的艰苦时光,可能是3年,也可 ...

【规整贴】MSP430G2开发板学习精华帖

整合了MSP430G2开发板的学习资料,包括LaunchPad实验板触摸感应子卡的内容,希望对各位正在学习的坛友有帮助,同时更希望各位将 ...

表羡慕ArduinoNano啦,动手做LaunchPad Nano吧!

刚做完,不早了所以跳过吐槽、灌水、骗稿费的部分(稿费……汗),直接进入正题。 在羡慕ArduinoNano的小巧?MSP430也可以Nan ...

Thinker Board 开篇

NO PICTURE I SAY WHAT!PICTURE FIRST! 112239 112233 112231 先把板子整出来分享给大家,之所以会在EEWORLD DIY版块 ...

【树莓派Pico测评】-制作红外体温计( I2C接口测试+MLX90614)

本帖最后由 fxyc87 于 2021-2-7 16:46 编辑 我上个贴子发布了如何利用USB进行串口通信, 【树莓派Pico测评】- USB CDC串口 ...

单片机的大佬们,从STM32cubeMX生成的USB虚拟串口程序,需要改吗?

STM32F103c8t6,配置都是按照网上教程配置的,但是生成的程序用STlink下载到单片机上,连接电脑就只有感叹号。USB描述符请求失败 ...

有偿求启航开发板STM32F103VET6 全套资料【悬赏20元】

朋友送了一个启航 开发板STM32F103VET6 -KIT-V3 ,但是资料却没有,在网上只有零星的。 有偿求启航开发板STM32F103VET6 -KIT-V3 ...

有不错的matter方面的板子推荐吗?

管管在线求问,小伙伴们快来告诉我有什么matter板子你们想要玩的呀~~管管又想搞事情啦,等着小伙伴来推荐了我去联系呀~~~此外, ...

全志XR806基于http的无线ota功能实验

XR806不仅硬件功能多,XR806也提供了功能极其丰富的SDK,几天体验下来非常容易上手。常见的功能几乎都有相应的cmd或demo实现,HA ...

《大语言模型开发:用开源模型开发本地系统》第1-4章心得——NLP基础到transformer机制

感谢主办方给我这样宝贵的学习机会,我又充满坚持学习的动力。 翻开《大语言模型开发:用开源模型开发本地系统》的前4章,我仿 ...

关闭
站长推荐上一条 1/10 下一条
有奖直播:当AI遇见仿真,会有什么样的电子行业革新之路?
首场直播:Simcenter AI 赋能电子行业研发创新
直播时间:04月15日14:00-14:50

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网 1

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表