当前位置:   article > 正文

ZYNQ之FPGA学习----Verilog HDL语法(2)_nxor

nxor

5 Verilog 高级知识点(二)

5.1 Verilog 语句块

Verilog 语句块提供了将两条或更多条语句组成语法结构上相当于一条一句的机制。主要包括两种类型:顺序块和并行块

顺序块

  • 顺序块用关键字 begin 和 end 来表示;
  • 顺序块中的语句是一条一条执行的,非阻塞赋值除外;
  • 顺序块中每条语句的时延总是与其前面语句执行的时间相关。

并行块

  • 并行块有关键字 fork 和 join 来表示;
  • 并行块中的语句是并行执行的,阻塞形式的赋值同样是并行执行;
  • 并行块中每条语句的时延都是与块语句开始执行的时间相关。
5.2 Verilog 条件和循环语句

条件语句

  • 条件(if)语句用于控制执行语句要根据条件判断来确定是否执行;
  • 条件语句用关键字 if 和 else 来声明,条件表达式必须在圆括号中。

条件语句基本结构如下:

if (condition1)       true_statement1 ;
else if (condition2)        true_statement2 ;
else                      default_statement ;
  • 1
  • 2
  • 3

多路分支语句

  • case 语句是一种多路条件分支的形式,可以解决 if 语句中有多个条件选项时使用不方便的问题。

case语句基本结构如下:

case(case_expr)
    condition1     :             true_statement1 ;
    condition2     :             true_statement2 ;
    ……
    default        :             default_statement ;
endcase
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

循环语句

  • Verilog 循环语句有 4 种类型,分别是 while,for,repeat,和 forever 循环。循环语句只能在 always 或 initial 块中使用,可以包含延迟表达式。

while循环

while循环基本结构如下:

while (condition) begin
    …
end
  • 1
  • 2
  • 3

while 循环中止条件为 condition 为假,如果开始执行到 while 循环时 condition 已经为假,那么循环语句一次也不会执行。

for循环

for循环基本结构如下:

for(initial_assignment; condition ; step_assignment)  begin
    …
end
  • 1
  • 2
  • 3

initial_assignment 为初始条件。condition 为终止条件,condition 为假时,立即跳出循环。step_assignment 为改变控制变量的过程赋值语句,通常为增加或减少循环变量计数。因为初始条件和自加操作等过程都已经包含在 for 循环中,所以 for 循环写法比 while 更为紧凑。

repeat循环

repeat循环基本结构如下:

repeat (loop_times) begin
    …
end
  • 1
  • 2
  • 3

repeat 的功能是执行固定次数的循环,它不能像 while 循环那样用一个逻辑表达式来确定循环是否继续执行。repeat 循环的次数必须是一个常量、变量或信号。如果循环次数是变量信号,则循环次数是开始执行 repeat 循环时变量信号的值。即便执行期间,循环次数代表的变量信号值发生了变化,repeat 执行次数也不会改变。

forever循环

forever循环基本结构如下:

forever begin
    …
end
  • 1
  • 2
  • 3

forever 语句表示永久循环,不包含任何条件表达式,一旦执行便无限的执行下去,系统函数 $finish 可退出 forever。forever 相当于 while(1) 。通常,forever 循环是和时序控制结构配合使用的。

5.3 Verilog 过程连续赋值

过程连续赋值是过程赋值的一种。这种赋值语句能够替换其他所有 wire 或 reg 的赋值,改写了 wire 或 reg 型变量的当前值。

与过程赋值不同的是,过程连续赋值的表达式能被连续的驱动到 wire 或 reg 型变量中,即过程连续赋值发生作时,右端表达式中任意操作数的变化都会引起过程连续赋值语句的重新执行。

过程连续性赋值主要有 2 种,assign-deassign 和 force-release

assign, deassign

  • assign(过程赋值操作)与 deassign(取消过程赋值操作)表示第一类过程连续赋值语句。赋值对象只能是寄存器或寄存器组,而不能是 wire 型变量;
  • 赋值过程中对寄存器连续赋值,寄存器中的值被保留直到被重新赋值。

force, release

  • force (强制赋值操作)与 release(取消强制赋值)表示第二类过程连续赋值语句。使用方法和效果,和 assign 与deassign 类似,但赋值对象可以是 reg 型变量,也可以是 wire 型变量;
  • 因为是无条件强制赋值,一般多用于交互式调试过程,不要在设计模块中使用;
  • 当 force 作用在寄存器上时,寄存器当前值被覆盖;release 时该寄存器值将继续保留强制赋值时的值。之后,该寄存器的值可以被原有的过程赋值语句改变;
  • 当 force 作用在线网上时,线网值也会被强制赋值。但是,一旦 release 该线网型变量,其值马上变为原有的驱动值。
5.4 Verilog 模块

结构建模方式有 3 类描述语句: Gate(门级)例化语句,UDP(用户定义原语)例化语句和 module(模块)例化语句。本次主要讲述使用最多的模块级例化语句。

模块

模块是 Verilog 中基本单元的定义形式,是与外界交互的接口。模块定义必须以关键字 module 开始,以关键字endmodule 结束。模块格式定义如下:

module module_name 
#(parameter_list)
(port_list) ;
              Declarations_and_Statements ;
endmodule
  • 1
  • 2
  • 3
  • 4
  • 5

端口

端口是模块与外界交互的接口。对于外部环境来说,模块内部是不可见的,对模块的调用只能通过端口连接进行。

端口列表

模块的定义中包含一个可选的端口列表,一般将不带类型、不带位宽的信号变量罗列在模块声明里。

端口声明

  • (1) 端口信号在端口列表中罗列出来以后,就可以在模块实体中进行声明了。根据端口的方向,端口类型有 3 种: 输入(input),输出(output)和双向端口(inout)。input、inout 类型不能声明为 reg 数据类型,因为 reg 类型是用于保存数值的,而输入端口只能反映与其相连的外部信号的变化,不能保存这些信号的值。output 可以声明为 wire 或 reg 数据类型;
  • (2) 在 Verilog 中,端口隐式的声明为 wire 型变量,即当端口具有 wire 属性时,不用再次声明端口类型为 wire 型。但是,当端口有 reg 属性时,则 reg 声明不可省略;
  • (3) 还有一种更简洁且常用的方法来声明端口,即在 module 声明时就陈列出端口及其类型。reg 型端口要么在 module 声明时声明,要么在 module 实体中声明。

模块例化

在一个模块中引用另一个模块,对其端口进行相关连接,叫做模块例化。

命名端口连接

将需要例化的模块端口与外部信号按照其名字进行连接,端口顺序随意,可以与引用 module 的声明端口顺序不一致,只要保证端口名字与外部信号匹配即可。如果某些输出端口并不需要在外部连接,例化时 可以悬空不连接,甚至删除。一般来说,input 端口在例化时不能删除,否则编译报错,output 端口在例化时可以删除。

顺序端口连接

这种方法将需要例化的模块端口按照模块声明时端口的顺序与外部信号进行匹配连接,位置要严格保持一致。

端口连接规则

  • 输入端口:模块例化时,从模块外部来讲, input 端口可以连接 wire 或 reg 型变量。这与模块声明是不同的,从模块内部来讲,input 端口必须是 wire 型变量。
  • 输出端口:模块例化时,从模块外部来讲,output 端口必须连接 wire 型变量。这与模块声明是不同的,从模块内部来讲,output 端口可以是 wire 或 reg 型变量。
  • 输入输出端口:模块例化时,从模块外部来讲,inout 端口必须连接 wire 型变量。这与模块声明是相同的。
  • 悬空端口:模块例化时,如果某些信号不需要与外部信号进行连接交互,我们可以将其悬空,即端口例化处保留空白即可,上述例子中有提及。output 端口正常悬空时,我们甚至可以在例化时将其删除。input 端口正常悬空时,悬空信号的逻辑功能表现为高阻状态(逻辑值为 z)。但是,例化时一般不能将悬空的 input 端口删除,否则编译会报错,一般来说,建议 input 端口不要做悬空处理,无其他外部连接时赋值其常量。
  • 位宽匹配:当例化端口与连续信号位宽不匹配时,端口会通过无符号数的右对齐或截断方式进行匹配。
  • 端口连续信号类型:连接端口的信号类型可以是,1)标识符,2)位选择,3)部分选择,4)上述类型的合并,5)用于输入端口的表达式。

带参数例化

当一个模块被另一个模块引用例化时,高层模块可以对低层模块的参数值进行改写。这样就允许在编译时将不同的参数传递给多个相同名字的模块,而不用单独为只有参数不同的多个模块再新建文件。参数覆盖有 2 种方式:1)使用关键字 defparam,2)带参数值模块例化。

defparam 语句

可以用关键字 defparam 通过模块层次调用的方法,来改写低层次模块的参数值。

带参数模块例化

第二种方法就是例化模块时,将新的参数值写入模块例化语句,以此来改写原有 module 的参数值。

5.5 Verilog 函数

函数

函数只能在模块中定义,位置任意,并在模块的任何地方引用,作用范围也局限于此模块。函数主要有以下几个特点:

  • 1)不含有任何延迟、时序或时序控制逻辑
  • 2)至少有一个输入变量
  • 3)只有一个返回值,且没有输出
  • 4)不含有非阻塞赋值语句
  • 5)函数可以调用其他函数,但是不能调用任务

Verilog 函数声明格式如下:

function [range-1:0]     function_id ;
                         input_declaration ;
                         other_declaration ;
                         procedural_statement ;
endfunction
  • 1
  • 2
  • 3
  • 4
  • 5

函数调用格式如下:

function_id(input1, input2,);
  • 1

常数函数

常数函数是指在仿真开始之前,在编译期间就计算出结果为常数的函数。常数函数不允许访问全局变量或者调用系统函数,但是可以调用另一个常数函数。这种函数能够用来引用复杂的值,因此可用来代替常量。

automatic 函数

在 Verilog 中,一般函数的局部变量是静态的,即函数的每次调用,函数的局部变量都会使用同一个存储空间。若某个函数在两个不同的地方同时并发的调用,那么两个函数调用行为同时对同一块地址进行操作,会导致不确定的函数结果。Verilog 用关键字 automatic 来对函数进行说明,此类函数在调用时是可以自动分配新的内存空间的,也可以理解为是可递归的。因此,automatic 函数中声明的局部变量不能通过层次命名进行访问,但是 automatic 函数本身可以通过层次名进行调用。

5.6 Verilog 任务

任务与函数的区别

和函数一样,任务(task)可以用来描述共同的代码段,并在模块内任意位置被调用,让代码更加的直观易读。函数一般用于组合逻辑的各种转换和计算,而任务更像一个过程,不仅能完成函数的功能,还可以包含时序控制逻辑。下面对任务与函数的区别进行概括:

比较点函数任务
输入函数至少有一个输入,端口声明不能包含 inout 型任务可以没有或者有多个输入,且端口声明可以为 inout 型
输出函数没有输出任务可以没有或者有多个输出
返回函数至少有一个返回值任务没有返回值
仿真函数总在零时刻就开始执行任务可以在非零时刻执行
时序函数不能包含任何时序控制逻辑任务不能出现 always 语句,但可以包含其他时序控制,如延时语句
调用函数只能调用函数,不能调用任务任务可以调用函数和任务
书写函数不能单独作为一条语句出现,只能放在赋值语言的右端任务可以作为一条单独的语句出现语句块中

任务

任务声明:任务在模块中任意位置定义,并在模块内任意位置引用,作用范围也局限于此模块。模块内子程序出现下面任意一个条件时,则必须使用任务而不能使用函数。

  • 1)子程序中包含时序控制逻辑,例如延迟,事件控制等
  • 2)没有输入变量
  • 3)没有输出或输出端的数量大于 1

Verilog 任务声明格式如下:

task       task_id ;
    port_declaration ;
    procedural_statement ;
endtask
  • 1
  • 2
  • 3
  • 4

任务中使用关键字 input、output 和 inout 对端口进行声明。input 、inout 型端口将变量从任务外部传递到内部,output、inout 型端口将任务执行完毕时的结果传回到外部。

进行任务的逻辑设计时,可以把 input 声明的端口变量看做 wire 型,把 output 声明的端口变量看做 reg 型。但是不需要用 reg 对 output 端口再次说明。

对 output 信号赋值时也不要用关键字 assign。为避免时序错乱,建议 output 信号采用阻塞赋值。

5.6 Verilog 状态机

Verilog 是硬件描述语言,硬件电路是并行执行的,当需要按照流程或者步骤来完成某个功能时,代码中通常会使用很多个 if 嵌套语句来实现,这样就增加了代码的复杂度,以及降低了代码的可读性,这个时候就可以使用状态机来编写代码。状态机相当于一个控制器,它将一项功能的完成分解为若干步,每一步对应于二进制的一个状态,通过预先设计的顺序在各状态之间进行转换,状态转换的过程就是实现逻辑功能的过程。
状态机,全称是有限状态机(Finite State Machine,缩写为 FSM),是一种在有限个状态之间按一定规律转换的时序电路,可以认为是组合逻辑和时序逻辑的一种组合。状态机通过控制各个状态的跳转来控制流程,使得整个代码看上去更加清晰易懂,在控制复杂流程的时候,状态机优势明显,因此基本上都会用到状态机。
根据状态机的输出是否与输入条件相关, 可将状态机分为两大类, 摩尔(Moore)型状态机和米勒(Mealy)型状态机

  • Mealy 状态机:组合逻辑的输出不仅取决于当前状态,还取决于输入状态。
  • Moore 状态机:组合逻辑的输出只取决于当前状态。

米勒状态机的模型如下图所示,模型中第一个方框是指产生下一状态的组合逻辑 F,F 是当前状态和输入信号的函数,状态是否改变、如何改变,取决于组合逻辑 F 的输出;第二框图是指状态寄存器,其由一组触发器组成,用来记忆状态机当前所处的状态,状态的改变只发生在时钟的跳边沿;第三个框图是指产生输出的组合逻辑 G,状态机的输出是由输出组合逻辑 G 提供的,G 也是当前状态和输入信号的函数。
在这里插入图片描述

摩尔状态机的模型如下图所示,对比米勒状态机的模型可以发现,其区别在于米勒状态机的输出由当前状态和输入条件决定的,而摩尔状态机的输出只取决于当前状态。
在这里插入图片描述

5.7 Verilog 竞争与冒险

产生原因

数字电路中,信号传输与状态变换时都会有一定的延时。在组合逻辑电路中,不同路径的输入信号变化传输到同一点门级电路时,在时间上有先有后,这种先后所形成的时间差称为竞争(Competition)。由于竞争的存在,输出信号需要经过一段时间才能达到期望状态,过渡时间内可能产生瞬间的错误输出,例如尖峰脉冲。这种现象被称为冒险(Hazard)。

竞争不一定有冒险,但冒险一定会有竞争。

其实实际硬件电路中,只要门电路各个输入端延时不同,就有可能产生竞争与冒险。

判断方法

  • 代数法:在逻辑表达式,保持一个变量固定不动,将剩余其他变量用 0 或 1 代替,如果最后逻辑表达式能化简成
Y = A + A'
或
Y = A · A'
  • 1
  • 2
  • 3

则可判定此逻辑存在竞争与冒险。

  • 卡诺图法:有两个相切的卡诺圈,并且相切处没有其他卡诺圈包围,可能会出现竞争与冒险现象。

消除方法

对数字电路来说,常见的避免竞争与冒险的方法主要有 3 种。

  • 1)增加滤波电容,滤除窄脉冲。此种方法需要在输出端并联一个小电容,将尖峰脉冲的幅度削弱至门电路阈值以下。此方法虽然简单,但是会增加输出电压的翻转时间,易破坏波形。

  • 2)修改逻辑,增加冗余项。利用卡诺图,在两个相切的圆之间,增加一个卡诺圈,并加在逻辑表达式之中。

  • 3)使用时钟同步电路,利用触发器进行打拍延迟。同步电路信号的变化都发生在时钟边沿。对于触发器的 D 输入端,只要毛刺不出现在时钟的上升沿并且不满足数据的建立和保持时间,就不会对系统造成危害,因此可认为 D 触发器的 D 输入端对毛刺不敏感。 利用此特性,在时钟边沿驱动下,对一个组合逻辑信号进行延迟打拍,可消除竞争冒险。

6 Verilog 高级知识点(三)

6.1 Verilog 门的类型

门级建模,是使用基本的逻辑单元,例如与门,与非门等,进行更低级抽象层次上的设计。与行为级建模相比,门级建模更注重硬件的实现方法,即通过连接一些基本门电路去实现多种逻辑功能。虽然行为级建模最后也会被综合成基本的门级电路网络,但对于复杂的设计来说,行为级建模的效率远远高于门级建模。所以目前 Verilog 大多数用于描述数字设计的行为级层次(RTL),一般只注重设计实现的算法或流程,而不用特别关心具体的硬件实现方式。

多输入门

多输入门只有单个输出,有单个或多个输入端。Verilog 内置多输入门如下:and(与门) nand(与非门) or(或门) nor(或非门) xor(异或门) nxor(同或门)。使用基本的逻辑门单元去实现一些简单的逻辑功能时,使用模块例化的方式即可。门级单元第一个端口是输出,后面端口是输入,例化调用时需要注意。门级单元实例调用的时候,也可以不指定实例的名字。

当输入端口超过 2 个时,只需将输入信号在端口列表中继续排列即可,Verilog 可自动识别。多输入门的真值表如下,注意输出不会出现 Z。

and(与门)的真值表如下:

and01xz
00000
101xx
x0xxx
z0xxx

nand(与非门)的真值表如下:

nand01xz
01111
110xx
x1xxx
z1xxx

or(或门)的真值表如下:

or01xz
001xx
11111
xx1xx
zx1xx

nor(或非门)的真值表如下:

nor01xz
010xx
10000
xx0xx
zx0xx

xor(异或门)的真值表如下:

xor01xz
001xx
110xx
xxxxx
zxxxx

nxor(同或门)的真值表如下:

nxor01xz
010xx
101xx
xxxxx
zxxxx

多输出门

多输出门只有单个输入,有单个或多个输出端,又可称之为 buffer,起缓冲、延时作用。内置多输入门有buf(缓冲器)和not(非门)。多输出门的真值表如下,注意输出不会出现 Z。

buf01xz
输出01xx
not01xz
输出10xx

三态门

Verilog 中还提供了 4 个带有控制端的 buffer 门单元,称为三态门。只有当控制信号有效时,数据才能正常传递,否则输出为高阻抗状态 Z。
在这里插入图片描述
三态门的真值表如下,表中有些为可选项,例如,1/z 表明,根据输入端和控制端的信号强度,输出端既可能为 1,也可能为 z。
bufif1的真值表如下:

bufif1控制端
01xz
0z00/z0/z
1z11/z1/z
xzxxx
zzxxx

bufif0的真值表如下:

bufif0控制端
01xz
00z0/z0/z
11z1/z1/z
xxzxx
zxzxx

notif1的真值表如下:

notif1控制端
01xz
0z11/z1/z
1z00/z0/z
xzxxx
zzxxx

notif0的真值表如下:

notif0控制端
01xz
01z1/z1/z
10z0/z0/z
xxzxx
zxzxx
6.2 Verilog 开关

MOS 开关

MOS 开关有nmos(N 类型 MOS 管)、pmos(P 类型 MOS 管)、rnmos (带有高阻抗的 NMOS 管)、 rpmos(带有高阻抗的 PMOS 管)。MOS 管用来为开关逻辑建模,数据从输入流入输出,可通过适当设置来开、关数据流。带有阻抗的 MOS 管,源极到漏极的阻抗较高,且在传递信号时会减小信号的强度。

MOS 管开关结构图如下所示。
在这里插入图片描述
MOS 管真值表如下所示,与三态门非常相似。
nmos的真值表如下:

nmos控制端
01xz
0z00/z0/z
1z11/z1/z
xzxxx
zzxxx

pmos的真值表如下:

pmos控制端
01xz
00z0/z0/z
11z1/z1/z
xxzxx
zxzxx

CMOS 开关

CMOS 开关用关键字 cmos 和 rcmos (带有高阻抗)声明。CMOS 有一个数据输出,一个数据输入和 2 个控制输入,结构示意图如下:
在这里插入图片描述
信号 PControl 与 Ncontrol 通常是互补的。当信号 Ncontrol 为 1 且 PControl 为 0 时,开关导通。 当信号 Ncontrol 为 0 且 PControl 为 1 时,开关输出为高阻。可以将 CMOS 开关看做是 NMOS 与 PMOS 开关的组合体。

例化时,CMOS 管第一个端口为输出端,第二个端口为数据输入端,第三个端口为 Ncontrol 控制输入端,第四个端口为 Pcontrol 控制输入端。

双向开关

NMOS、PMOS、CMOS 开关门都是从漏极向源极导通,方向是单向的。Verilog 中还提供了双向导通的开关器件,数据可以双向流动,两边的信号都可以是驱动信号。双向开关结构图如下:
在这里插入图片描述
tran 开关为两个信号直接的缓存,inout1 或 inout2 均可以是驱动信号。tranif1 仅当 control 信号为 1 时,开关两边的信号导通。当 control 为 0 时,两个信号断开,有驱动源的信号会和驱动源保持一致的信号值,没有驱动源的信号则呈现为高阻状态。tranif0 同理。因此,双向开关常用来进行总线或信号之间的隔离。

电源和地

晶体管级电路需要源极(Vdd, 逻辑 1)与地极(Vss, 逻辑 0),分别用关键字 supply1 和 supply0 来定义。 用法如下:

   supply1              VDD ;
   supply0              GND ;
   wire                 siga = VDD ; //siga is connected to logic 1
   wire                 sigb = GND ; //sign is connected to logic 0
  • 1
  • 2
  • 3
  • 4
6.3 Verilog 建立时间和保持时间

对于数字系统而言,建立时间(setup time)和保持时间(hold time)是数字电路时序的基础。数字电路系统的稳定性,基本取决于时序是否满足建立时间和保持时间。

基本概念

建立时间就是时钟触发事件来临之前,数据需要保持稳定的最小时间,以便数据能够被时钟正确的采样。
保持时间就是时钟触发事件来临之后,数据需要保持稳定的最小时间,以便数据能够被电路准确的传输。

可以通俗的理解为:时钟到来之前,数据需要提前准备好;时钟到来之后,数据还要稳定一段时间。建立时间和保持时间组成了数据稳定的窗口,如下图所示:
在这里插入图片描述

6.4 Verilog 同步与异步

同步时钟

数字设计中,一般认为,频率相同或频率比为整数倍、且相位相同或相位差固定的两个时钟为同步时钟。或者理解为,时钟同源且频率比为整数倍的两个时钟为同步时钟。其实,时钟同源,就保证了时钟相位差的固定性。具体可以分类如下:

  • 同源同频同相位:时钟频率和相位均相同,是同步的。时钟间数据传输只要满足正常的建立时间和保持时间即可,不需要特殊的同步设计。
  • 同源同频不同相位:两个时钟同频但不同相位时,只要相位差保持固定,也可以认为是同步的。因为只要控制两个时钟间传输的数据延迟在合理范围内,就不会导致时序问题。而且,固定的时钟延迟也可以在版图级网表中修复。固定的相位差可以理解为同源时钟下两个时钟因路径不同而导致的偏移。
  • 同源不同频但存在整数倍分频比:一个时钟往往是另一个时钟的分频,即便存在相位差也是固定的。当单 bit 信号从慢时钟域传递到快时钟域时,因为同源,只要满足建立时间和保持时间,快时钟域总会采集到从慢时钟域传递来的信号。

异步时钟

工作在异步时钟下的两个模块进行数据交互时,由于时钟相位关系不可控制,很容易导致建立时间和保持时间 violation。以下 3 种情况下的时钟可以认为是异步的:

  • 不同源:由两个不同的时钟源产生的两个时钟是异步的,这是最常见的异步时钟。即便两个时钟频率相同,但是也不能保证每次上电后两者的相位或相位差是相同的,所以信号间的传输与时钟关系也是不确定的。

  • 同源但频率比不是整数倍:两个时钟间相位差也可能会有多个,例如同源的 7MHz 时钟和 3MHz 时钟,他们之间也会出现多个相位差,时序也难以控制。一般情况下也需要当异步时钟处理。

  • 同源虽频率比为整数倍但不满足时序要求:当信号从快时钟域传递到慢时钟域时,只要慢时钟域能安全采集到从快时钟域传来的信号,就不存在异步问题。但如果信号在快时钟域翻转速率过快,慢时钟域可能不会安全的采集到从快时钟域传来的信号,这也可以认为是异步问题。

一般来说,慢时钟域时序约束较为宽松,快时钟域较为严格。

致谢领航者ZYNQ开发板,开启FPGA学习之路!

“皓月”遇见“教师”是团圆遇上了感恩,祝大家双节快乐!!!

希望本文对大家有帮助,上文若有不妥之处,欢迎指正

分享决定高度,学习拉开差距

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/529058
推荐阅读
相关标签
  

闽ICP备14008679号