第4章 简单的4位计算机设计及实现

  • Post author:
  • Post category:其他



有了上一章准备好的各种组合逻辑电路和时序逻辑电路,这一章我们就逐步实现一个简单的

4位计算机,并为这台计算机写一些简单的程序,深入理解计算机的工作过程。




4.1 冯诺依曼体系结构


1. 二进制表示指令和数据


任意十进制整数、字符、汉字、多媒体等信息都可以用二进制表示,这个前面已经说过。计算机是一种程序控制的计算机器,任何一种计算机都有有限条指令,指令可以控制计算机做各种动作,比如加法、数据转移等。程序就是指令的排列组合,通过程序计算机可以完成特定的任务。任意一条计算机指令都可以用二进制编码来表示,比如加法指令可以编码成

“10000000”,与运算指令可以编码成“10000001”,当然也可以编码成其它二进制。


2. 程序存储,自动运行

程序是指令的排列组合,指令可以用二进制数表示,所以程序就是一组二进制数据,只要能存储二进制数据也就能存储程序。至于自动运行,其实就是让计算机取出一条指令,分析执行一条,再取下一条并执行,让计算机在程序控制之下自动运行。


3. 由控制器、运算器、存储器和输入输出五大部件构成


控制器主要完成取出指令并分析指令,然后发出控制其它功能部件的信号,可以用译码器实现;运算器主要完成算数和逻辑运算,可以用加法器和与或非逻辑实现;存储器可以分成数据存储器和程序存储器,数据存储器用前面讲过的锁存器或触发器实现,程序存储器需要掉电数据也不丢失,可以用前面所说的

ROM实现;输入主要指程序和数据的输入,数据可以通过程序输入,程序直接写入到ROM中;输出主要指RAM中的数据输出到显示设备。




4.2 4位计算机的指令集


计算机是程序控制的计算机器,要想用程序控制机器,首先计算机要有自己能够识别的指令集,我们设计的这种简单的

4位计算机具有8条指令,每一条指令都编为一个8位的二进制数,包括4位操作码和4位操作数,操作码用来区分指令,操作数表示数据存储器的地址编号或者一个4位二进制数,这个4位计算机的指令集如表4-1所示。




4-1 4位计算机指令集

助记符

二进制编码

说明

mov a,addr

0000xxxx

把数据存储器addr地址编号的数据移动到累加寄存器a中

mov addr,a

0001xxxx

把累加器a中数据移动到数据存储器addr地址

mov a,#data

0010xxxx

把#data立即数移动到累加器a中

addc a,addr

0011xxxx

累加器a和数据存储器addr地址处的数据相加,和存入累加器a,进位存入进位寄存器c,c数据参与求和运算

or a,addr

0100xxxx

累加器a中数据和数据存储器addr中数据按位求或,结果存入累加器a

and a,addr

0101xxxx

累加器a中数据和数据存储器addr地址处的数据按位求与,结果存入累加器a

cpl a

01100000

对累加器a中数据按位求反

jz addr

1xxxxxxx

如果累加器中数据为0,则跳转到程序储存器addr处执行,addr为0~127之间




4.3 4位计算机的结构框图


我们先设计

4位计算机的结构框图如图4-1所示,后面再一步步实现每一个框图模块。




4-1 简单4位计算机的结构框图

程序计数器完成程序地址的产生,程序存储器里存储指令代码,程序存储器根据程序计数器的地址输出相应的指令给指令锁存器,指令锁存器锁保持本条指令一个时钟周期不变化,指令译码器负责把指令的操作码译码,并产生微操作信号,微操作信号可以控制数据存储器的读写和运算器相应功能的开关,指令锁存器输出的操作数作为数据存储器的地址,累加器输出的数据和数据存储器输出的数据一起输入到运算器,运算器在微控制指令的控制下完成各条运算指令,把产生运算结果输入到累加器,累加器的数据也可以输入到数据存储器。




4.4 4位计算机各模块的实现

Proteus完整的仿真原理图见附录,下面将按照结构框图的划分分模块在Proteus中实现。



4.4.1.

时钟




4-2 时钟信号


时钟信号就是一种方波的周期信号,如图

4-2所示。时钟信号好比计算机的心脏,在心脏有规律跳动下,计算机才能一条一条的执行指令、有条不紊的工作。时钟提供了一种基准时间,一个周期包括一个低电平、一个上升沿、一个高电平和一个下降沿。Proteus仿真模型可以采用Generator里的DC工具,设置类型为Pulse,设置初始值是0v,高电平是5v,设置合适的频率,这里设置频率为1Hz,也就是1秒钟一个周期,一秒钟执行一条指令,速度慢下来就能看清计算机工作的细节。



4.4.2

程序计数器




4-3 程序计数器结构图


程序计数器使用了

2片74161计数器和若干基本逻辑门,2片74161级联,如图4-3所示。8位计数器计数范围是0到255,所以按理说可以寻址最多256条指令,但jz指令的跳转范围是0到127,所以我们设计的4位计算机暂时的寻址范围也是0到127,就是一个程序最多可以包含128条指令。74161计数器带预置数功能,ENP和ENT都是高电平并且CLK上升沿时,计数加1;当预置信号LOAD为低电平并且CLK上升沿时,预置数写入计数器,下一个时钟周期开始,计数器从预置数开始计数。


普通指令执行时,计数器是从

0开始一个一个数往上加,当累加器为0并且是jz指令时,指令的低7位会作为预置数加载到计数器,计数器从预置数开始计数,也就是程序跳转到指定的地址了。



4.4.3

程序存储器和指令锁存器




4-4 程序存储器和指令锁存器结构图


程序存储器部分如图

4-4所示,采用一片2732半导体集成ROM芯片,容量是4KB字节,关于程序存储器的原理,前面已经讲解过。Proteus仿真的2732元件可以从文件里读取数据,所以可以把程序写在文件里,在2732元件里设置好文件路径就可以了。2732芯片是OE引脚低电平读出数据,可以把时钟信号作为读信号,则一个时钟周期的低电平时读出指令编码。


指令锁存器使用一片

74ls373,时钟信号求反作为锁存信号,这样在一个时钟周期里指令锁存器的输出保持不变,为其它电路提供了稳定的数据。



4.4.4

指令译码器(控制器)




4-5 译码器(控制器)


控制器部分如图

4-5所示,由一片74HC154构成,74HC154是4-16译码器,指令的高4位操作码作为译码器的输入,译码器会在相应的输出线路上输出低电平,译码器的输出就是微控制信号,微控制信号控制相应的功能部件工作。比如,加法指令的操作码是0011,会在译码器的第4引脚上产生一个低电平,这个低电平信号就是控制加法器的工作信号。



4.4.5

累加器和加法进位标志位




4-6 累加寄存器A和进位标志位C


累加器使用一片

74ls374 8D触发器,进位标志使用一片74ls74 1D触发器,累加器和进位标志还要使用若干基本逻辑门,如图4-6所示。累加器是一种特殊功能的寄存器,起到暂存数据的作用。当要把一个数输入到数据存储器时,先把这个数存到累加器,由累加器再输入到数据存储器;加法等指令需要2个输入数据,一个来自累加器,一个来自数据存储器,计算的结果还要存入累加器。


从指令集中可以看到,除了

“mov addr,a”和“jz”指令外,其它指令的结果都是存入累加器。当指令的结果需要存入累加器并且时钟是上升沿时,结果才被写入累加器。累加器一直输出存储的数据。


当加法运算并且时钟上升沿时,进位被写入进位标志位

C,同时进位标志位会参与下一次加法运算。



4.4.6

运算器(


ALU






4-7 运算器(ALU)结构图


运算器由

3片74ls244(每片有2个单独的4路)缓冲器、74ls283加法器和若干基本逻辑门组成,如图4-7所示。运算器主要实现了“与”、“或”、“非”、“和”运算,以及立即数输入累加器和数据存储器输入到累加器共6条指令的功能。

运算器的输入包括:累加器的输出数据、数据存储器的输出数据和指令的操作数数据,都是直接输入到各指令电路的输入端,因为是组合逻辑电路,所以结果也立即产生,但每个指令电路的输出都由数据缓冲器控制,只有当前指令对应的数据缓冲器被使能输出,计算结果输入到累加器的输入端,等到时钟上升沿到来时,数据就被输入到了累加器中,完成相应的指令。


比如,当前指令是加法指令,虽然与、或、非等其它几条指令和加法指令一样都计算出了结果,但只有加法器对应的

74ls244缓冲器导通,其它都相当于断开,所以只有加法这一路计算结果输入到了累加器输入端,时钟上升沿到来时刻,完成数据写入累加器,最终完成加法运算。



4.4.7

数据存储器部分


指令编码中操作数有

4位,所以寻址能力为16个地址,位宽也是4位,因此我们实现一个16×4位数据存储器,框图如图4-8所示。




4-8 16×4数据存储器(RAM)结构图




4-9 数据存储器写入部分结构




4-10 数据存储器读出部分结构


前面讲解过

4×1的数据存储器,主要包括译码器、存储单元和数据选择器。这里的16×4数据存储器原理与前面的4×1是一样的,只是一个存储单元存储4位,共16个存储单元。译码器选用一片4-16译码器,数据选择器选用4片16-1选择器,存储单元选用74ls373 8D锁存器,但只使用低4位。写入和读取部分电路分别如图4-9和4-10所示。


做好的数据存储器可以抽象成包括

4位输入数据线、4位输出数据线、4位地址线和1位读/写信号线的模块。




4.5 指令执行过程分析


整个

4位机的仿真电路讲解完成,我们来具体分析一条指令的执行过程。以“or a,0001”指令为例,这条指令是要完成累加器的数据和数据存储器中0100地址里的数据求或运算,运算结果写入累加器,指令的二进制编码是01000001,假设这条指令存放在程序存储器的0地址处。


时钟的第一个周期是从低电平开始,此时程序计数器的输出是

0地址(程序计数器上升沿才开始计数加1)。程序存储器地址输入的也是0地址,使能输入端也是晶振的低电平,因此0地址处的指令“01000001”输出到了指令锁存器。指令锁存器的锁存信号与时钟相反,所以指令锁存信号是高电平,此时指令锁存器的输出等于输入,也是“01000001”。控制器译码器的输入就是“0100”,所以译码器的第5号引脚线输出低电平到了“or”指令对应的缓冲器,与此同时,数据存储器的地址端输入是“0001”,而数据存储器的读/写输入的是高电平,所以数据存储器第0001存储单元的数据被输出到了运算器的各个指令电路,此时累加器的输出也早已输入到了各个指令电路,运算器的各指令电路都计算出来了结果,但只有“or”指令对应的74ls244缓冲器使能了输出,这样“or a,0001”运算的结果就被输入到了累加器输入端,等到第一个时钟周期上升沿到来的时刻,数据被写入了累加器,完成“or a,0001”指令。

Jz指令时,只要指令最高位是1,并且累加器是0,那么就使能了74161的预置功能,当上升沿到来时刻,指令的后7位被预置到了74161里,下一个就开始从新的地址处开始执行了。

其它指令的执行过程,读者可自行分析。


经仿真测试,当主频设置为

1MHz,就是1秒钟执行100万条指令时,这个计算机的计算结果偶尔会出现错误,但运行在100kHz时,还是很稳定的。




4.6 程序小例子



4.6.1 RAM

读写

程序地址就是程序存放在程序存储器的位置,助记符就是方便记忆的汇编语言,指令编码才是实际写入程序存储器的数据。


注释程序时约定:(

addr)代表数据存储器addr地址里的数据,(a)代表累加器a里的数据,“->”代表数据传输方向,例如“#5->a”代表5输入到累加器a,“#1100b->1100b”

代表

“1100b”这个二进制数输入到数据存储器1100b地址处。


这个小例子实现了把

“1110b”写入数据存储器的0地址处,然后不断循环读出0地址处的数据到累加器,通过查看累加器的数据,可以判断数据存储器的读写是否正确。


程序地址


助记符


指令编码

注释

0000000  mov a,#1110b  00101110



#1110b->a

0000001  mov 0000b,a   00010000



(a)->0000b

0000010  mov a,#0000b  00100000

0000011  mov a,0000b   00000000



(0000b)->a

0000100  mov a,#0000b  00100000



#0->a

0000101  jz 0000010b   10000010

;累加器为

0,则跳转到程序0000010

;地址处执行


新建一个文件文件,使用二进制工具打开,以二进制方式依次输入程序的二进制编码到文件地址编号中,设置

2732芯片的读取文件路径,设置时钟为1Hz,开始仿真并查看累加器的数据。中间可以暂停仿真,查看各个存储器中的数据情况,分析每一条指令的执行过程。



4.6.2

加法


完成两个

8位数相加,这里完成105+199,内存分配情况如表4-2所示,结果可能是9位数。先把被两个数输入内存,然后两个数的低4位相加,得到“和”的低4位,最后两个数的高4位相加,并且进位输入是低4位相加时的进位输出,得到的“和”的高4位以及最终的进位。




4-2 加法例子内存分配

内存地址

数据

注释

0000

#0110b

第一个数的高4位

0001

#1001b

第一个数的低4位

0010

#1100b

第二个数的高4位

0011

#0111b

第二个数的低4位

0100

?

进位

0101

“和”的高4位

0110

“和”的低4位


程序地址


助记符


指令编码


注释

0000000  mov a,#0110b  00100110

0000001  mov 0000b,a   00010000    ;第一个数高4位送累加器

0000010  mov a,#1001b  00101001

0000011  mov 0001b,a   00010001    ;第一个数低4位送累加器

0000100  mov a,#1100b  00101100

0000101  mov 0010b,a   00010010    ;第二个数高4位送累加器

0000110  mov a,#0111b  00100111

0000111  mov 0011b,a   00010011    ;第二个数低4位送累加器

0001000  addc a,0001b  00110001

0001001  mov 0110b,a   00010110    ;“和”的低4位->0110b

0001010  mov a,0000b   00000000

0001011  addc a,0010b  00110010    ;低4位求和的进位也参与求和

0001100  mov 0101b,a   00010101    ;“和”的高4位->0101b

0001101  mov a,#0000b  00100000

0001110  mov 0100b,a   00010100    ;#0->0100b,清零

0001111  addc a,0100b  00110100    ;高4位求和时的进位值参与运算

0010000  mov 0100b,a   00010100    ;两个8位数相加,结果可能是9位数

0010001  mov a,#0000b  00100000

0010010  jz 0010001    10010001    ;最后两条指令死循环



4.6.3

减法

计算机中存储的是二进制数,至于这个数代表正数、负数还是其它符号信息全在于编程者,计算机并不关心,那么我们自己可以找到一种正负数的表示方法。


来看一下补码运算,补码运算的方法是对原码取反再加

1,例如A=01100001,那么:

A的原码=0110 0001

A的反码=1001 1110

A的补码=1001 1111

可以得出:

A+(A的补码)=A+(A的反码)+1=1111 1111+1=1 0000 0000


如果只考虑

8位加法,舍掉最高位,那么:

A+(A的补码)=0,也就是

-A=(A的补码)


因此,可以用正数的补码来代表它对应的负数。正数最高位总是

0,负数最高位总是1,并且8位有符号数的范围是-128到127之间,超出此范围就不能用8位二进制表示了。

A-B=A+(-B)=A+(B的补码),所以减法运算可以化成加法运算,两个8位二进制数相加,上一个例子已经讲解过,这里不再赘述。



4.6.4

乘法


完成两个

4位数相乘,这里完成15×13,内存分配情况如表5-3所示,主要就是实现13个15相加。




5-3 乘法例子内存分配

内存地址

数据

注释

0000

1111b

被乘数A

0001

1101b

乘数B

0010

积的高4位

0011

积的低4位

A乘以B可以计算B次A相加,那么怎样实现计算B次呢?


只考虑

4位情况,B+(B的补码)=0,也就是B的补码加上B次1就是0。可先计算B的补码并存入固定地址,然后每次加1,加到0就刚好是B次。


程序地址


助记符


指令编码


注释

0000000  mov a,#1111b  00101111

0000001  mov 0000b,a   00010000



#A->0000b

0000010  mov a,#1101b  00101101

0000011  mov 0001b,a   00010001



#B->0001b

0000100  mov a,#0000b  00100000

0000101  mov 0010b,a   00010010



#0->0010b

0000110  mov 0011b,a   00010011



#0->0011b

0000111  mov a,0001b   00000001

0001000  cpl a         01100000

0001001  mov 0001b,a   00010001

0001010  mov a,#0001b  00100001

0001011  addc a,0001b  00110001

;求出

B的补码

0001100  mov 0001b,a   00010001



B的补码->0001b

0001101  mov a,0000b   00000000

0001110  addc a,0011b  00110011

0001111  mov 0011b,a   00010011



A+(0011b)->0011b

0010000  mov a,#0000b  00100000

0010001  addc a,0010b  00110010

0010010  mov 0010b,a   00010010



0+(0010b)+进位->0010b

0010011  mov a,#0001b  00100001

0010100  addc a,0001b  00110001

0010101  mov 0001b,a   00010001

;(

0001b)+1->(0001b)

0010110  jz 0011001    10011001

;(

0001b)+1为0,则累加计算结束

0010111  mov a,#0000b  00100000

0011000  jz 0001101    10001101

;(

0001b)+1不为0,则继续累加

0011001  mov a,#0000b  00100000

0011010  jz 0011001    10011001

;最后两条指令死循环


通过这几个小例子,可以看到我们设计的

4位计算机工作的过程,确实是在程序的控制下,机器一步一步的完成各种动作,实现了“程序存储,自动运行”的机制,计算机就是一个程序控制的计算机器,同时也证明了冯诺依曼的计算机设计方案完全是可行的。


我们看到,这个简单的

4位计算机完全是用前面讲过的组合逻辑和时序逻辑搭建起来的,组合逻辑和时序逻辑又可以用基本逻辑来实现,基本逻辑又可以用三极管来实现,所以这个4位计算机也完全可以使用三极管来实现,也即是说在实物上是可以制作出来的,如果把用到三极管都使用半导体的集成工艺制作,那么这个4位计算机就成了芯片。




4.7 芯片工艺简介


前面所述的

4位计算机是可以使用晶体管、电阻、电容等分立元器件制作出来的,但所用分立元器件的数量会很大,体积也会相应的很大,集成电路工艺能很好的解决这些问题。

经过一系列的加工工艺,把晶体管、电阻、电容、电感等元器件集成在一块晶片上,并完成元器件之间的导线连接,最后封装在一个外壳内,通过引脚引出对外的接口,这样就做成了芯片。集成电路工艺实现了电子元器件的高密度、低功耗和高稳定性。

自然界中的Si元素通常以SiO

2

的形式存在,沙子富含硅元素,可以作为半导体器件的原材料基础。把自然界中的沙子



生产成纯度大于

99.9999%的单晶硅锭(硅棒),沙子和硅棒如图4-11所示。






a)沙子                                    (b)硅棒




4-11 沙子与硅棒


把硅锭进一步切割成晶圆,如图

4-12所示。





4-12晶圆


晶圆上涂上一层

SiO

2


形成绝缘层,再涂上一层光刻胶,类似于可以曝光的胶卷。涂完光刻胶的晶圆透过掩模(

Mask)曝光在紫外线下,曝光部分的光刻胶被清除掉,事先设计好的图案就印在了晶圆上。使用化学物质溶解掉裸露出来的晶圆部分,形成凹槽,光刻胶保护的部分不会被蚀刻。通过注入离子把没有光刻胶保护的部分注入一层参有杂质的特殊材料,离子注入完成后清除光刻胶,晶体管雏形已现,如图4-13所示。




4-13 单个晶体管雏形


在晶圆上在敷上一层绝缘材料并蚀刻出晶体管的三个引脚孔洞,如图

4-14所示。




4-14 晶体管引脚制作


在三极管引脚孔洞里沉积铜层,最终形成晶体管的三个引脚,晶体管如图

4-15所示。至此,晶体管集成工艺完成。




4-15 晶体管完成图


8. 导线连接


根据电气连接关系,蚀刻出晶体管之间的导线,导线并非是一个平面,而是分成很多个层,最终实现元器件之间的电气连接,晶体管连接图如图

4-16所示。




4-16 芯片局部电路


9. 封装


一片晶圆上可能有很多芯片,切割后形成一个个的独立晶片,把晶片用塑料外壳包装起来并把对外的引脚引出,最终做成芯片,如图

4-17所示。




4-17 芯片封装示意图




4.8 结构、指令、程序模型


不管是冯诺依曼还是巴贝奇,他们设计的计算机都包含了一个

“结构、指令、程序”的模型,而这个模型又不局限于计算机。


比如一支军队,他有自己的组织结构,在组织结构基础上有一套号令系统,所谓作战方案不就是号令的排列组合吗?设计作战方案不就是为军队机器编写程序,达到既定的作战目标吗?作战方案跟程序一样可能存在

Bug,而这个Bug可能导致作战失败。

比如生产企业,他的组织结构往往也有管理部门、加工执行部门、仓储部门、原料采购部门和产品输出部门,各部门协同起来具备一定的职责,经营管理者的管理方案实质就是对这个企业机器的编程。

再比如国家机器,单个人好比三极管,若干人构成一个个小群体,好比基本逻辑,更多的群体构成一个个部门,好比组合和时序逻辑,更多部门构成一个地方,地方组成国家,各部门都有一套职责,上级下达的文件实质就是一套程序。三极管不能知道程序想要干什么,单个的人也很难知道国家的真实意图。


巴贝奇先生除了设计计算机器外,他还是一位出色的管理学家,他的计算机思想里不可避免的渗入了管理的思想,计算机器和管理有着天然的联系,

“结构、指令、程序”既是计算机模型,同时也是一种管理模型,既适合于“物”的管理,也适合于“事”的管理。




4.9 关于人造生命的思考

首先,具备怎样的条件才能称之为生命?

根据生物学理论,自然界中的生命通常要具备:自我生存、进化和繁衍三个特征。


生命通过新陈代谢提供能量供自身生存,人造机器可以使用太阳能、风能、水能等能源,有了能源机器就能工作,也就能自我生存了。人造机器可以在程序控制下运行,它可以根据环境情况调整自身的参数,甚至修改自身的程序,以便更好的适应环境,并能把调整好的程序传递给下一代,在传递过程中还可能会因为某种原因出现

“差错”,可以视为进化。

只要能实现繁衍,那么人造机器就能成为人造生命了。

繁衍实质上是使用材料构造一个结构相同的同类,现有自动机器的核心都是计算机,计算机的主要材料是芯片,而芯片的生产工艺极其复杂,完全在计算机控制下生产一台计算机所需的所有材料,目前看来难以实现。

前面我们介绍过,巴贝奇先生设计了一种齿轮加杠杆的机械计算机,虽然现代计算机基本都是半导体晶体管计算机,但半导体晶体管正常工作的温度范围很小,在几千摄氏度、高辐射等特殊环境下是无法工作的,这时候就有科技专家在搞纳米机械计算机,我们也可以借鉴机械计算机的设计方案。巴贝奇设计的机械计算机是十进制的,我们是否可以设计二进制的机械计算机呢?

理论上,只要有受控的开关,就能一步步制作成计算机,而这个开关的材料,以及导通和截止的物理量都不重要。

比如水和控制水管的一种阀门,如果阀门用弹簧控制,开始时闭合,加水压弹簧旋转,水流流出,减水压弹簧反转,水流停止,那就是一种水控的开关。还可以做一种阀门,开始时阀门是通的,加水压停止,停水压开启。这样也能做出与、或、非的水门逻辑,一步步也可以做出水流控制的计算机器。


在著名的

“TED”演讲中,有一个叫做“创造新生命”的主题演讲,演讲者展示了一种“动物”,整体如图4-18所示,局部细节如图4-19

所示




4-18 一种风能动物




4-19

风能动物细节图


这种

“动物”全部由一种特殊的“管子”做成,它可以利用风能行走,通过空瓶子来存储风能(压缩空气),它的大脑也是用这些材料做成的二进制计数器,它能够探测出海水并避开,记录多少步到海水,多少步到沙丘。也就是说,它有传感器、控制器和执行器,只不过这些功能模块全部用特殊的“管子”做成,而且它又是靠风能运行的,所以它能很好的自我生存。图4-20所示是海水探测器,图4-21所示是二进制计数器(可能是压缩空气驱动开关做成)。




4-20 海水探测器




4-21 二进制计数器(计步器)


可以看到这种

“动物”所需的材料单一且它本身的结构简单,这给制造它们带来便利。如果多种这样的“动物”分工协作把“管子”组装出新的“动物”,另一部分“动物”负责制作这种“管子”,那么,它们也许可以像蚁群一样生存和繁衍(工厂式繁殖),生命的形式或许不只是蛋白质的。

二进制的机械式力学计算机很有可能会导致人造生命的出现,而这种生命又将进化到何方?它们与人类又将怎样和谐共处?

附四位机完整原理图:






版权声明:本文为wang2800原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。