嵌入式开发Verilog教程(三)——Verilog HDL基本语法汇总(上)

前言

  • Verilog HDL是一种用于数字逻辑电路设计的语言,它既是一种行为描述的语言也是一种结构描述的语言。Verilog HDL作为一种高级的硬件描述编程语言,有着类似C语言的风格。Verilog HDL模型就是用Verilog HDL描述的电路设计方案,它既可以描述电路的功能,又能描述元器件以及它们之间的连接。因此,Verilog模型可以理解为实际电路的不同级别的抽象,Verilog模型包括以下五种类型:
  • (1)系统级(system):用高级语言结构实现设计模块的外部性能的模型。
  • (2)算法级(algorithm):用高级语言结构实现设计算法的模型。
  • (3)RTL级(Register Transfer Level):描述数据在寄存器之间流动和如何处理这些数据的模型。
  • (4)门级(gate-level):描述逻辑门以及逻辑门之间的连接的模型。
  • (5)开关级(switch-level):描述器件中三极管和存储节点。
  • 一个复杂电路系统的完整Verilog HDL模型是由若干个Verilog HDL模块构成的,每一个模块又可以由若干个子模块构成。其中有些模块需要综合成具体电路,而有些模块只是与用户所设计的模块交互的现存电路或激励信号源。利用Verilog HDL语言结构所提供的这种功能就可以构造一个模块间的清晰层次结构来描述极其复杂的大型设计,并对所作设计的逻辑电路进行严格的验证。

一、简单的Verilog HDL模块

1.1 Verilog HDL程序简单模块

  • Verilog HDL程序完全嵌在moduleendmodule声明语句之间,每个Verilog程序包括四个主要部分:端口定义、I/O说明、内部信号声明、功能定义。下面先介绍几个简单的Verilog HDL程序,结合该程序理解分析Verilog HDL程序特性。
module adder(count,sum,a,b,cin);
	   input [2:0] a,b;
	   input cin;
	   output count;
	   output [2:0] sum;
	   	assign {count,sum} = a + b + cin;
endmodule
  • 这个例子通过连续赋值语句描述了一个名为adder的三位加法器可以根据两个三比特数a、b和进位(cin)计算出和(sum)和进位(count)。 从例子中可以看出整个Verilog HDL程序是嵌套在module和 endmodule 声明语句里的。
module compare(equal,a,b);
	   output equal;	//声明输出信号equal
	   input [1:0] a,b;	//声明输入信号a,b
	   	assign equal = (a==b) ? 1:0;
	   	/*如果a、b两个输入信号相等,输出1,否则输出0*/
endmodule
  • 这个程序通过连续赋值语句描述了一个名为compare的比较器。对两比特数 a、b 进行比较,如a与b相等,则输出equal为高电平,否则为低电平。
module trist2(out,in,enable);
		output out;
		input in,enable;
			bufif1 mybuf(out,in,enable);
endmodule
  • 这个程序描述了一个名为trist2的三态驱动器。程序通过调用一个在Verilog语言库中现存的三态驱动器实例元件bufif1来实现其功能。
module trist1(out,in,enable);
		output out;
		input in,enable;
			mytri tri_inst(out,in,enable);
			//调用下面的mytri模块定义的实例元件tri_inst。
endmodule

module mytri(out,in,enable);
		output out;
		input in,enable;
			assign out = enable? in : 'bz;
endmodule	
  • 这个程序例子通过另一种方法描述了一个三态门。在这个例子中存在着两个模块。模块trist1调用由模块mytri定义的实例元件tri_inst。模块trist1是顶层模块。模块mytri则被称为子模块。
  • 由以上四个例子,我们可以得到Verilog HDL程序编写的基本原则如下所示:
  • (1)Verilog HDL程序是由模块构成的。每个模块的内容都是嵌在module和endmodule两个语句之间。每个模块实现特定的功能。模块是可以进行层次嵌套的。正因为如此,才可以将大型的数字电路设计分割成不同的小模块来实现特定的功能,最后通过顶层模块调用子模块来实现整体功能。
  • (2)每个模块要进行端口定义,并说明输入输出口,然后对模块的功能进行行为逻辑描述。
  • (3)Verilog HDL程序的书写格式自由,一行可以写几个语句,一个语句也可以分写多行。
  • (4)除了endmodule语句外,每个语句和数据定义的最后必须有分号。
  • (5)可以用//和//…对Verilog HDL程序的任何部分作注释。

1.2 Verilog HDL程序模块结构

  • Verilog的基本设计单元是“模块”(block)。一个模块是由两部分组成的,一部分描述接口,另一部分描述逻辑功能,即定义输入是如何影响输出的,具体的模块结构如下图所示:
    在这里插入图片描述
  • 程序模块和电路图符号是一致的,这是因为电路图符号的引脚也就是程序模块的接口。而程序模块描述了电路图符号所实现的逻辑功能。上面的Verilog设计中,模块中的第二、第三行说明接口的信号流向,第四、第五行说明了模块的逻辑功能。以上就是设计一个简单的Verilog程序模块所需的全部内容。

1.3 Verilog HDL程序模块端口定义

  • 模块的端口声明了模块的输入与输出,如下所示:
module module_name(port1,port2,port3,...)

1.4 Verilog HDL程序模块内容

  • 模块的内容包括I/O声明、内部信号声明、功能定义。
    1、I/O声明的格式
输入口:input  端口名1,端口名2,………,端口名i;   //(共有i个输入口)
输出口:output 端口名1,端口名2,………,端口名j;   //(共有j个输出口)
  • I/O说明也可以写在端口声明语句里,其格式如下:
module module_name(input port1,input port2,...
					output port1,output port2...);

2、内部信号声明

  • 在模块内部用到的与端口有关的wire、reg变量的声明格式如下所示:
  • reg [width-1 : 0] R1,R2,…;
  • wire [width-1 : 0] W1,W2,…;
    3、功能定义
  • 模块中最重要的部分是逻辑功能定义部分,其中有三种方法可在模块中产生逻辑:
  • (1)用“assign”声明语句:比如,assign a = b & c;。这种方法的语法很简单,该例子在模块中生成了一个与门。
  • (2)用实例元件:比如,and and_inst(q,a,b);。采用实例元件方法是通过调入库元件实现的。输入元件名字与相连接的引脚即可,该例子表示一个跟与门(and)一样的名为and_inst的与门,其输入端为a, b,输出为q。
  • (3)用always块
always @(posedge clk or posedge clr)
		begin
			if(clr) q<=0;
			else if(en) q<=d;
		end
  • “assign”语句是描述组合逻辑最常用的方法之一,而“always”块既可用于描述组合逻辑也可描述时序逻辑。上面的例子用“always”块生成了一个带有异步清除端的D触发器。如按一定的风格来编写“always”块,可以通过综合工具把源代码自动综合成用门级结构表示的组合或时序逻辑电路。
  • 注意
  • (1)如果用Verilog模块实现一定的功能,首先应该清楚哪些是同时发生的,哪些是顺序发生的。上面三个例子分别采用了“assign”语句、实例元件和“always”块。这三个例子描述的逻辑功能是同时执行的。也就是说,如果把这三项写到一个 VeriIog 模块文件中去,它们的次序不会影响逻辑实现的功能。这三项是同时执行的,也就是并发的。
  • (2)然而,在“always”模块内,逻辑是按照指定的顺序执行的。“always”块中的语句称为“顺序语句”,因为它们是顺序执行的。请注意,两个或更多的“always”模块也是同时执行的,但是模块内部的语句是顺序执行的。

二、Verilog HDL的数据类型及其常量、变量

  • Verilog HDL共有19种数据类型,数据类型表示了数字电路硬件中的数据存储与传送元素类型,常用的数据类型主要包括四种:(1)reg类型、(2)wire类型、(3)integer类型、(4)parameter类型。在理解与熟练应用这四种数据类型的基础上,在实际项目开发中,其他数据类型很容易上手。
  • Verilog HDL中的数据分为常量与变量两种,它们分别属于以上数据类型,本文介绍最常用的几种。

2.1 常量

  • 在程序运行过程中,其值不能被改变的量称为常量,Verilog HDL语言中数字及表示方式如下:

2.1.1 数字

1、整数

  • 在Verilog HDL中,整型常量有四种进制表示形式:(1)二进制整数(b或B)、(2)十进制整数(d或D)、(3)十六进制整数(h或H)、(4)八进制整数(o或O)。数字表达方式有以下三种方式:
  • (1)<位宽><进制><数字>这是一种全面的描述方式。
  • (2)<进制><数字>在这种描述方式中,数字的位 宽采用缺省位宽(这由具体的机器系统决定,但至少32位)。
  • (3)<数字>在这种描述方式中,采用缺省进制十进制。
  • 在表达式中,位宽指明了数字的精确位数,比如以下两个例子:
8'b10101100	//位宽为8的数的二进制表示, ’b表示二进制
8'ha2		//位宽为8的数的十六进制,’h表示十六进制。

2、x与z值

  • 在数字电路中,x表示不定值,z表示高阻值。一个x可以用来定义十六进制的四位二进制数的状态,八进制数的三位,二进制的一位。z表示方式与x类似,z还有可以用?代替,这种整数表示方式经常在case表达式中使用。具体的例子如下所示:
4'b10x0	//位宽为4的二进制数从低位数起第二位为不定值
4'b101z	//位宽为4的二进制数从低位数起第一位为高阻值
12'dz	//位宽为12的十进制数其值为高阻值(第一种表达方式)
12'd?	//位宽为12的十进制数其值为高阻值(第二种表达方式)
8'h4x	//位宽为8的十六进制数其低四位值为不定值

3、负数

  • 一个数字可以被定义为负数,只需在位宽表达式前加一个减号,减号必须写在数字定义表达式的最前面。注意减号不可以放在位宽和进制之间也不可以放在进制和具体的数之间。
-8'd5	//这个表达式表示5的补数(在计算机中用八位二进制表示)
8’d-5	//非法格式

4、下划线(underscore_)

  • 下划线可以用来分隔开数的表达以提高程序可读性。但不可以用在位宽和进制处,只能用在具体的数字之间,具体例子如下所示:
16'b1010_1011_1111_1010	//合法格式

2.1.2 参数(Parameter)型

  • 在Verilog HDL中用parameter来定义常量,即用parameter来定义一个标识符代表一个常量,称为符号常量,即标识符形式的常量,采用标识符代表一个常量可提高程序的可读性和可维护性。parameter型数据是一种常数型的数据,其说明格式如下:
parameter	参数名1=表达式,参数名2=表达式, …, 参数名n=表达式;
  • parameter是参数型数据的确定符,确认符后跟着一个用逗号分隔开的赋值语句表。在每一个赋值语句的右边必须是一个常数表达式。也就是说,该表达式只能包含数字或先前已定义过的参数,具体例子如下所示:
parameter msb=7;	//定义参数msb为常数7
parameter e=25, f=29;	//定义两个常数参数
parameter r=5.7;	//定义r为一个实数型参数
parameter byte_size=8, byte_msb=byte_size-1;	//采用常数表达式赋值
parameter average_delay=(r+f)/2;	//用常数表达式赋值
  • 参数型常数经常用于定义延迟时间和变量宽度。在模块或实例引用时可通过参数传递改变在被引用模块或实例中已定义的参数。下面将通过两个例子进一步说明在层次调用的电路中改变参数常用的一些用法。
  • **案例1:**在引用Decode实例时,D1,D2的Width将采用不同的值4和5,且D1的Polarity将为0。下面的例子展示了如何采用参数型常量实现参数改变:即用#(4,0)向D1中传递Width=4,Polarity=0;用#(5)向D2中传递Width=5,Polarity仍为1.
module Decode(A,F);
	parameter Width=1, Polarity=1;
	......
endmodule

module Top;
	wire[3:0] A4;
	wire[4:0] A5;
	wire[15:0] F16;
	wire[31:0] F32;
	Decode #(4,0) D1(A4,F16);
	Decode #(5)   D2(A5,F32);
endmodule

2.2 变量

  • 变量即在程序运行过程中其值可以改变的量,在Verilog HDL中变量的数据类型有很多种,本文只对常用的几种进行介绍。

2.2.1 wire型

  • 网络数据类型表示结构实体(例如门)之间的物理连接。网络类型的变量不能存储值,而且它们必须收到驱动器(比如,门或连续赋值语句,assign)的驱动。如果没有驱动器连接到网络类型的变量上,则该变量就是高阻的(其值为z)。常用的网络数据类型为wire型,它通常用来表示单个门驱动或连续赋值语句驱动的网络型数据。
  • wire型数据常用来表示用于以assign关键字指定的组合逻辑信号。Verilog程序模块中输入输出信号类型缺省时自动定义为wire型。wire型信号可以用作任何方程式的输入,也可以用作“assign”语句活实例元件的输出,其格式如下所示:
wire [n-1:0] 数据名1,数据名2,…数据名i; //共有i条总线,每条总线内有n条线路
wire [n:1] 数据名1,数据名2,…数据名i;	//共有i条总线,每条总线内有n条线路
  • wire是wire型数据的确认符,[n-1:0]和[n:1]代表该数据的位宽,即该数据有几位。最后跟着的是数据的名字。如果一次定义多个数据,数据名之间用逗号隔开。声明语句的最后要用分号表示语句结束,看下面的几个例子:
wire  a;          //定义了一个一位的wire型数据
wire [7:0] b;     //定义了一个八位的wire型数据
wire [4:1] c, d;  //定义了二个四位的wire型数据

2.2.2 reg型

  • 寄存器是数据存储单元的抽象,寄存器数据类型的关键字是reg。与改变触发器存储的值相同,通过赋值语句可以改变寄存器储存的值。Verilog HDL语言提供了功能强大的结构语句使设计者能有效地控制是否执行这些赋值语句,这些控制结构用来描述硬件触发条件。reg类型数据的缺省初始值为不定值x
  • reg型数据常用来表示always模块内指定的信号,通常代表触发器。在设计中要由always块通过使用行为描述语句来表达逻辑关系。注意在“always”块内被赋值的每一个信号都必须定义成reg型
  • reg型数据的格式如下所示:
reg [n-1:0] 数据名1,数据名2...,数据名i;
reg [n:1] 数据名1,数据名2...,数据名i;
  • reg是reg型数据的确认标识符,[n-1:0]和[n:1]代表该数据的位宽,即该数据有几位(bit)。最后跟着的是数据的名字。如果一次定义多个数据,数据名之间用逗号隔开。声明语句的最后要用分号表示语句结束。看下面的几个例子:
reg rega;		//定义一个一位的名为rega的reg型数据
reg [3:0] regb;	//定义一个四位的名为regb的reg型数据
reg [4:1] regc, regd;	//定义两个四位的名为regc和regd的reg型数据
  • 说明:
  • (1)对于reg型数据,其赋值语句的作用为改变一组触发器存储单元的值。
  • (2)在Verilog中有许多构造(construct)来控制是否或何时执行这些赋值语句。这些控制构造可用来描述硬件触发器的各种具体情况。
  • (3)reg型数据的缺省初始值是不定值x
  • (4)reg型数据可以赋正值,也可以赋负值。当时当一个reg型数据为一个表达式中的操作数时,它的值被当作无符号值,即正值。
  • 注意:
  • reg型只表示被定义的信号将使用在always块中,但是并不是说reg型信号一定是寄存器或触发器的输出。

2.2.3 memory型

  • Verilog HDL通过对reg型变量建立数组来对存储器建模,可以描述RAM型存储器、ROM存储器与reg文件。数组中的每个单元通过数组索引进行寻址。需要注意的是在Verilog语言中没有多维数组存在。memory型数据是通过reg型数据的地址范围来生成的,其格式如下所示:
reg [n-1:0] 存储器名[m-1:0];
reg [n-1:0] 存储器名[m:1];
  • reg[n-1:0]定义了存储区中每个存储单元的大小,即该存储单元是一个n位的寄存器;存储器名后面的[m-1:0]或者[m:1]表示该存储器中有多少个这样的寄存器。举例如下所示:
reg [7:0] mema[255:0];
  • 该例子定义了一个名为mema的存储器,该存储器有256个8位存储器,存储器的地址范围为从0到255。需要注意的是对存储器进行地址索引的表达式必须是常数表达式。
  • 另外,在同一个数据类型声明语句中,可以同时定义存储器型数据与reg型数据:
parameter wordsize=16, memsize=256;	//定义两个参数。
reg [wordsize-1:0] mem[memsize-1:0], writereg, readreg;
  • 说明:
  • 尽管memory型和reg型数据的定义格式很相似,但是还是要注意它们的不同之处。比如,一个由n个1位寄存器构成的存储器组是不同于一个n位的寄存器的:
reg [n-1:0] rega;	//一个n位的寄存器
reg mema [n-1:0];	//一个由n个1位今存其构成的存储器组
  • 一个n位的寄存器可以在一条赋值语句中进行赋值,而一个完整的存储器则不行,如下所示:
rega = 0;	//合法赋值语句
mema = 0;	//非法赋值语句
  • 如果相对memory中的存储单元进行读写操作,必须指定该单元在存储器中的地址,比如一下指令:
mema[3] = 0;	//给memory中的第3个存储单元赋值为0.
  • 进行寻址的地址索引可以是表达式,这样就可以对存储器中的不同单元进行操作。且表达式的值可以取决于其它的寄存器的值,比如可以用一个加法计数器来做RAM的地址索引。

三、Verilog HDL的运算符及表达式

  • Verilog HDL语言的运算符很多,根据运算功能可以分为以下几类:
名称表达式
算数运算符+,-,×,/,%
赋值运算符=
关系运算符>,<,>=,<=
逻辑运算符&&,!
条件运算符?:
位运算符~,
移位运算符<<,>>
拼接运算符{ }

3.1 基本的算数运算符

  • 在Verilog HDL语言中,算术运算符又称为二进制运算符,共有下面几种:
    (1)+ (加法运算符,或正值运算符,如 rega+regb,+3)
    (2)- (减法运算符,或负值运算符,如 rega-3,-3)
    (3)× (乘法运算符,如rega*3)
    (4)/ (除法运算符,如5/3)
    % (模运算符,或称为求余运算符,要求%两侧均为整型数据。如7%3的值为1)
  • 在进行整数除法运算时,结果值要略去小数部分,只取整数部分。而进行取模运算时,结果值的符号位采用模运算式里第一个操作数的符号位。见下例:
模运算表达式结果说明
10%31余数为1
11%32余数为2
12%30余数为0
-10%3-1结果取第一个操作数的符号位,所以余数为-1
  • 注意: 在进行算术运算操作时,如果某一个操作数有不确定的值x,则整个结果也为不定值x。

3.2 位运算符

  • Verilog HDL作为一种硬件描述语言,是针对硬件电路而言的。在硬件电路中信号有四种状态值1,0,x,z。
  • 在电路中信号进行与或非时,反映在Verilog HDL中则是相应的操作数的位运算。Verilog HDL提供了以下五种位运算符:
~	//取反
&	//按位与
|	//按位或
^	//按位异或
^~	//按位同或

1、取反运算符~

  • ~是一个单目运算符,用来对一个操作数进行按位取反运算。其运算规则如下表所示:
~结果
10
01
xx

2、按位与运算符&

  • 按位与运算就是将两个操作数的相应位进行与运算,其运算规则见下表:
&01x
0000
101x
x0xx

3、按位或运算符|

  • 按位或运算就是将两个操作数的相应位进行或运算,其运算规则见下表:
按位或01x
001x
1111
xx1x

4、按位异或运算符^

  • 按位异或运算就是将两个操作数的相应位进行异或运算,其运算规则见下表:
^01x
001x
110x
xxxx

5、按位同或运算符^~

  • 按位同或运算就是将两个操作数的相应位先进行异或运算再进行非运算,其运算规则见下表:
^~01x
010x
101x
xxxx

5、不同长度的数据进行位运算

  • 两个长度不同的数据进行位运算时,系统会自动的将两者按右端对齐,位数少的操作数会在相应的高位用0填满,以使两个操作数按位进行操作。

3.3 逻辑运算符

  • 在Verilog HDL语言中存在三种逻辑运算符:
&&	//逻辑与
||	//逻辑或//逻辑非
  • "&&“和”||“是二目运算符,它要求有两个操作数,如(a>b)&&(b>c),(a<b)||(b<c)。”!"是单目运算符,只要求一个操作数,如!(a>b)。下表为逻辑运算的真值表。它表示当a和b的值为不同的组合时,各种逻辑运算所得到的值。
ab!a!ba&&ba或b
- 逻辑运算符中"&&“和”“的优先级别低于关系运算符,”!" 高于算术运算符。见下例:
(a>b)&&(x>y)	\\可写成: a>b && x>y
(a==b)||(x==y)	\\可写成:a==b || x==y	
(!a)||(a>b)		\\可写成: !a || a>b
  • **注意:**为了提高程序的可读性,明确表达各运算符间的优先关系,建议使用括号。

3.4 关系运算符

  • 关系运算符共有以下四种:
a < b	\\a小于b
a > b	\\a大于b
a <= b	\\a小于或等于b
a >= b	\\a大于或等于b
  • 在进行关系运算时,如果声明的关系是假的(flase),则返回值是0,如果声明的关系是真的(true),则返回值是1,如果某个操作数的值不定,则关系是模糊的,返回值是不定值。

3.5 等式运算符

  • 在Verilog HDL语言中存在四种等式运算符:
==	\\等于
!=	\\不等于
===	\\等于
!==	\\不等于
  • 这四个运算符都是二目运算符,它要求有两个操作数。==!=又称为逻辑等式运算符。其结果由两个操作数的值决定。由于操作数中某些位可能是不定值x和高阻值z,结果可能为不定值x。而===!==运算符则不同,它在对操作数进行比较时对某些位的不定值x和高阻值z也进行比较,两个操作数必需完全一致,其结果才是1,否则为0。===!==运算符常用于case表达式的判别,所以又称为"case等式运算符"。这四个等式运算符的优先级别是相同的。下面画出=====的真值表,帮助理解两者间的区别。
    在这里插入图片描述

3.6 位移运算符

  • 在Verilog HDL中有两种移位运算符:
<<	\\左移位运算符
>>	\\右移位运算符
\\其使用方法如下:
a >> n;	\\a代表要进行移位的操作数,n代表要移几位。		
a << n;	\\这两种移位运算都用0来填补移出的空位。
  • 下面举例说明:
module  shift;
	reg [3:0]  start, result;
	initial
		begin
			start  = 1;	//start在初始时刻设为值0001
			result = (start<<2);	//移位后,start的值0100,然后赋给result。
		end
endmodule
  • 从上面的例子可以看出,start在移过两位以后,用0来填补空出的位。
  • 进行移位运算时应注意移位前后变量的位数,下面将给出一例:
4’b1001<<1 = 5’b10010;
4’b1001<<2 = 6’b100100;
1<<6 = 32’b1000000;
4’b1001>>1 = 4’b0100;
4’b1001>>4 = 4’b0000;

3.7 位拼接运算符(Concatation)

  • 在Verilog HDL语言有一个特殊的运算符:位拼接运算符{}。用这个运算符可以把两个或多个信号的某些位拼接起来进行运算操作。其使用方法如下:
{信号1的某几位,信号2的某几位,..,..,信号n的某几位}
  • 即把某些信号的某些位详细地列出来,中间用逗号分开,最后用大括号括起来表示一个整体信号。见下例:
{a,b[3:0],w,3’b101};	\\也可以写成下面的语句
{a,b[3],b[2],b[1],b[0],w,1’b1,1’b0,1’b1}
  • 说明:
  • (1)在位拼接表达式中不允许存在没有指明位数的信号。这是因为在计算拼接信号的位宽的大小时必需知道其中每个信号的位宽。
  • (2)位拼接还可以用重复法来简化表达式。见下例:
{4{w}};	//这等同于{w,w,w,w}
  • (3)位拼接还可以用嵌套的方式来表达。见下例:
{b,{3{a,b}}};     //这等同于{b,a,b,a,b,a,b}
  • 注意,用于表示重复的表达式如上例中的4和3,必须是常数表达式。

3.8 缩减运算符(reduction operator)

  • 缩减运算符是单目运算符,也有与或非运算。其与或非运算规则类似于位运算符的与或非运算规则,但其运算过程不同。位运算是对操作数的相应位进行与或非运算,操作数是几位数则运算结果也是几位数。而缩减运算则不同,缩减运算是对单个操作数进行或与非递推运算,最后的运算结果是一位的二进制数。缩减运算的具体运算过程是这样的:第一步先将操作数的第一位与第二位进行或与非运算,第二步将运算结果与第三位进行或与非运算,依次类推,直至最后一位。下面举个例子如下:
reg [3:0] B;
reg C;
C = &B;
相当于:
C =( (B[0]&B[1]) & B[2] ) & B[3];

3.9 优先级别

  • 下面对各种运算符的优先级别关系作一总结。见下表:
    在这里插入图片描述
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

嵌入式技术

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

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

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

打赏作者

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

抵扣说明:

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

余额充值