异步FIFO地实现方式.doc

上传人:李司机 文档编号:1180850 上传时间:2022-07-15 格式:DOC 页数:22 大小:349.06KB
返回 下载 相关 举报
异步FIFO地实现方式.doc_第1页
第1页 / 共22页
异步FIFO地实现方式.doc_第2页
第2页 / 共22页
异步FIFO地实现方式.doc_第3页
第3页 / 共22页
异步FIFO地实现方式.doc_第4页
第4页 / 共22页
异步FIFO地实现方式.doc_第5页
第5页 / 共22页
点击查看更多>>
资源描述

《异步FIFO地实现方式.doc》由会员分享,可在线阅读,更多相关《异步FIFO地实现方式.doc(22页珍藏版)》请在三一办公上搜索。

1、异步FIFO的实现方式实验目的本次实验介绍一种异步FIFO的实现方式。使用FIFO存储器可以在两个不同时钟系统之间快速而方便的传输数据。另外,在网络接口,图像处理等方面异步FIFO存储器也得到了广泛的应用。因此,异步FIFO存储器具有较大的研究和应用价值。异步FIFO的介绍和整体结构异步FIFO(First In First Out)存储器是指向FIFO缓冲器中写入数据的时钟域和从FIFO缓冲器中读取数据的时钟域是不同的,这两个时钟之间没有必然的因果关系。异步FIFO是一种先进先出的电路,使用在异步时钟域数据接口的局部,用来存储、缓冲在两个异步时钟之间的数据传输。在异步电路中,由于时钟之间周期

2、和相位完全独立,所以数据的丢失概率不为零。如何设计一个高可靠性、高速的异步FIFO存储器便成为一个难点。异步FIFO的一般结构如图1所示,都是由一个读时钟域电路、一个写时钟域电路和一个双端口的RAM来构成的。异步FIFO与同步FIFO所做的工作是一样的,都是在写信号有效时写数据到RAM中,在读信号有效时把数据从RAM中读出,所以对于中间局部的RAM设计是比拟简单的。另外,读电路和写电路单独实现起来也是比拟容易的,只需要按照同步FIFO的工作情况,如果没有写满或读空的状态时每写一个数据就把写地址加1,每读一个数据就把读地址减1。设计难点在于两个时钟域的交叠局部:满、空状态的产生,这也是设计的重点

3、。图1 异步FIFO结构针对这个问题,先从对亚稳态的处理开始介绍亚稳态的处理一个触发器进入亚稳态时,既无法预测该单元的输出电平,也无法预测何时输出才能稳定在某个正确的电平上。在这个稳定期间,触发器输出一些中间级电平,或者可能处于振荡状态、并且这种无用的输出电平可以沿信号通道上的各个触发器级联式传播下去。亚稳态发生的原因是由于在同步系统中,如果触发器的建立时间或保持时间不满足,就可能产生亚稳态,此时触发器输出端Q在亚稳态是指触发器无法在某个规定时间段内达到一个可确认的状态,逻辑误判有可能通过电路的特殊设计减轻危害(如本设计中将使用的Gray码计数器),而亚稳态的传播如此扩大了故障面,难以处理。在

4、数字集成电路中存放器要满足建立时间和保持时间。建立时间是在时钟翻转之前数据输入必须有效的时间,保持时间是在时钟沿之后数据输出必须仍然有效的时间。当一个信号被存放器锁存时,如果信号和时钟之间不满足这个要求,Q的值是不确定的,并且在未知的时刻会固定到高电平或低电平。此时存放器进入了亚稳态(Metastability)。解决这一问题的最简单方法是使用同步器,使得在另一个时钟域采样时信号足够稳定。同步器的设计本身就是一个比拟麻烦的问题,本次设计中也不深入讨论一些细节性的问题,直接采用两级采样的同步器,防止了使用一级同步器仍可能出现亚稳态的情况。每个这样的同步器都具有一个等于时钟周期的等待时间。这种同步

5、器可以把一些亚稳态的值同步为确定值,但并不一定是正确值,同时有一些亚稳态也还是无法稳定成确切值的,这种情况称为同步出错。由于同步出错的随机性,很难对它们进展跟踪。如果想进一步降低亚稳态出现的概率、可以再増加同步器的级数,但是太多的同步器会使系统的性能下降,所以系统中不会用太多的同步器,一般使用两个同步器已经足够。空满状态的判断之所以在前面介绍了亚稳态的问题,是因为这是判断满状态或空状态无法回避的一个问题。因为读电路在读控制时维持一个地址指针,写电路在写控制时维持一个地址指针,简单来说,这两个地址指针直接一比拟,就能得到空满的判断结果,但是实际操作起来非常麻烦。例如对于满状态来说,这是写入电路所

6、关心的状态,因为满状态下不能继续写入数据,但是空状态对于写电路没有影响。如果写入电路要判断当前FIFO是否为满,就需要把写电路自身维持的写指针和读电路维持的读指针做比拟,这个读指针就需要送入写电路中,此时就发生了穿过时钟域的问题,也就是说,读指针要从读时钟域同步到写时钟域,然后参与判断,此时就需要前面介绍的同步器。同样,对于空状态来说,这是读出电路所关心的状态,也是由读电路来维持的,因为空状态下再读数就会得到错误的数据,但是满状态下读数是没有影响的。如果读电路要判断当前FIFO是否为空,就需要把写时钟域中的写指针取到读时钟域来,和读时钟域的读指针进展比拟得出是否是空状态,同样跨越了时钟域。在跨

7、时钟域系统中希望出现错误的概率越低越好,此时格雷码无疑是最好的一个选择。格雷码属于可靠性编码,是一种误差最小化的编码,它大大减少了由一个状态到下一个状态时电路混淆。由这种编码相邻的两个码组之间只有一位不同,和其他编码同时改变2位和多位的情况相比更为可靠。表1所示是格雷码与二进制码的对应关系。表1 格雷码与二进制码转换真值表由前面的介绍可知通过同步器之后信号稳定的值可能是1也可能是0,可能与输入的值一样也可能与输入的值不同。如果对于二进制码,这显然是灾难性的。例如从十进制的7变到8,二进制码是从0111变为1000,把0111送入同步器之后,由于4位都要变化,所以4位都可能会出现亚稳态,从而在同

8、步器的输出端就会出现各种可能性,这样即使数据稳定下来,对整个电路的作用也很小。而如果采用格雷码,是从0100变为1100,只是最高位发生了改变,也就只有这一位可能会出现亚稳态的情况。这样经过同步器处理之后,输出端可能得到的值只有两种0100或1100,其中1100是正确的数值,如果得到这个输出自然是最好,但即使是0100的输出,也只是和原来的值一样,可以认为没有变化,这也不会对电路造成负面的影响。相比二进制代码那种变化后什么值都有可能的情况,格雷码显然是一种更易于承受的编码方式。格雷码虽然在跨时钟域方面效果比拟好,但在本身计数方面是不足的,也就是说还需要把格雷码转换成二进制码来计数,4位的格雷

9、码转二进制码的代码局部如下:bin0=gray3gray2 gray1 gray0;bin1=gray3gray2 gray1;bin2=gray3gray2;bin2=gray3;也可以用for循环完成:module gray2(bin,gray);parameter size=4;output size-1:0 bin;input size-1:0 gray;reg size-1:0 bin;integer i;always (gray) for(i=0;i,size;i=i=i+1);bini=(gray1);endmodule计数之后还要变回格雷码,转换的方法与上述方式类似。这样使用格

10、雷码作为指针就可以降低亚稳态带来的影响。接下来要解决的是空满判断的问题,常用的判断方法是附加位比拟法。附加位比拟法是给每个指针增加一个附加位,对于二进制指针而言,将存储空间的最后一个存储单元写入数据后,地址将变为零,即地址指针低n-1位清零并向最高位(MSB)也就是附加位进位。读指针也是如此工作。如果两个指针的最高位(MSBs)不同而其余位一样,就说明写指针比读指针多循环了一次,标志FIFO存储器处于满状态。如果包括最高位在内的两个指针完全一样,如此说明写指针和读指针经历了一样次数的循环,也就是说FIFO存储器处于空状态。这样读指针和写指针就变成了一个n位指针,其中低n-1位是用来存放FIFO

11、存储器的地址,可以用来对2n-1个存储单元寻址,而最高位如此用来区分当两个指针的地址相等时是满状态还是空状态。对二进制指针来说,用这种方式来区分满状态与空状态是可行的。但是,格雷码指针却不能直接使用这种方式,原因有两个。举个4位格雷码的例子,格雷码计数器的低3位用于存放存储地址,第四位是附加位,这个FIFO存储器的存储容量为8。正确的操作应当是,当写(或读)完一个循环时,地址应该重新开始计数,附加位应该翻转。然而格雷码指针却并非如此,地址由7到8格雷码由0-100到1-100),指针的附加位改变,但是地址位(低n-1位)却没有重新开始计数,这是由于格雷码是一种镜像码造成的。第二个原因是这种格雷

12、码不能直接产生满状态标志。如果两个格雷码指针都是Gray-7,这时的FIFO存储器为空状态,在进展一次写操作后写指针将加1,格雷码第4位将变为1而低3位不变,这时的读指针和写指针的最高位将不同而低位一样。如果这样的话,FIFO存储器满标志将置位,这显然是错误的,因而需要对这个4位的格雷码进展修改。想要的结果是:一个n位的(即包括附加位)格雷码计数器用在异步时钟域间传递数据,但是又希望它的低n-1位计数器也是格雷码类型的。这样低n-1位就能单独形成一个循环,而不是一种反射码。所以,此时需要的是一个既能产生n位的格雷码序列又能产生n-1位的格雷码序列的计数器。分别实现一个n位的格雷码计数器和一个n

13、-1位的格雷码计数器自是非常简单:用一个计数器来实现一个n位的格雷码计数器,并将这个计数器的次高位进展修改而低位保持不变以实现一个n-1位的格雷码计数器,这也不是一件很难的事情。这种既能产生n位格雷码又能产生n-1位格雷码的计数器被称为两重格雷码计数器。下面以3位和4位格雷码来说明空满状态的判断标准。3位格雷码表示的就是地址空间,可以有8个存储空间。由于写入和读出并不是按照从000开始的,而是可以以任意一个位置开始,比如存放数据可以按照十进制地址5、6、7、0、1、2、3、4的地址顺序来存放,读出数据也同理,这样为了表示循环,就增加了1位变为4位格雷码。首先说明空状态的判断标准,空状态表示读指

14、针和写指针重合,此时无论是看3位格雷码还是4位格雷码都应该是完全一样,比如写指针指向1010,读指针也必然指向1010,这样判断空状态就只需要判断两个指针是否一样,一样时即为空,不同时即为不空。然后解释满状态的判断标准。满状态判断比拟复杂。假设一次写入数据是从十进制地址6开始,连续写入8个数据。到14,这时存满8个数据,应产生满状态输出,这两个地址形式如下:十进制地址6二进制地址0110格雷码地址0101十进制地址14二进制地址1110格雷码地址1001如果是二进制地址,判断的方法已经介绍过了。而格雷码地址的前两位是不同的,但后面的两位是一样的。如果扩展成更多位的格雷码,满状态下依然是这种情况

15、,即前两位不同,后面位均一样。这样判断满状态首先要保证除去前两位之后的剩余局部是一样的。然后对于本例来说,需要保证前两局部是01和10,如果地址是以01开头,如此满时一定是10;如果以10开头,满时一定是01。判断的方法可以有很多种,这里采用先取前两位的异或值,保证相等,此时只可能是0,1的组合,然后再判断首位不同,这样就只能是01和10这两种情况。经过这三个条件的判断,就能就保证此时为写满状态。再观察地址以00和11开头的情况,给出地址如下十进制地址2二进制地址0010格雷码地址0011十进制地址10二进制地址1010格雷码地址1111刚刚提出的的三个条件依然保证写满状态的正常输出,所以写满

16、状态就以这三个条件作判断标准。下面开始具体设计:顶层模块的端口和功能针对异步FIFO的根本结构和功能,以与保存一些必要的状态信号和控制信号,现确定顶层模块的端口与功能见表2:端口名称功能说明rclk输入信号,1位,读时钟wlck输入信号,1位,写时钟rinc输入信号,1位,读使能信号,高电平时生效,表示写入数据winc输入信号,1位,写使能信号,高电平时生效,表示读出数据rrst_n输入信号,1位,低电平时读指针清零wrst_n输入信号,1位,低电平时写指针清零Rdata输出信号,8位,从RAM中读出数据Wdata输入信号,8位,待写入RAM数据Wfull输出信号,1位,高电平时表示FIFO已

17、存满数据,已满Rempty输出信号,1位,高电平时表示FIFO中数据已全部读出,已空表2 顶层模块的端口与功能子模块设计本设计的异步FIFO划分为5个子模块:读指针控制模块、写指针控制模块、存储RAW模块、读指针同步到写时钟域模块,写指针同步到读时钟域模块。依次介绍如下:两个同步模块这两个同步模块同前文介绍的一样,是两个存放器连接的一起。写指针同步到读时钟域模块代码如下module syne_w2r(rwptr2,wptr,rclk,rrst_n);parameter ADDRSIZE=4;output ADDRSIZE:0 rwptr2; /同步后的写指针input ADDRSIZE:0 w

18、ptr; /同步前的写指针input rclk,rrst_n;reg ADDRSIZE:0 rwptr2,rwptr1; /两个中间存放器always (posedge rclk or negedge rrst_n)if(!rrst_n) rwptr2,rwptr1=0; /复位else rwptr2,rwptr1=rwptr1,wptr; /存放器串联Endmodule读指针同步到写时钟域模块代码如下:module sync_r2w(wrptr2,rptr,wclk,wrst_n);parameter ADDRSIZE=4; output ADDRSIZE:0 wrptr2;input AD

19、DRSIZE:0 rptr;input wclk, wrst_n;reg ADDRSIZE:0 wrptr2, wrptrl;always (posedge wclk or negedge wrst_n)if (!wrst_n) wrptr2, wrptrl=0;else wrptr2,wrptrl= wrptrl,rptr;endmodule功能比拟简单,故不做仿真验证存储模块module fifomem(rdata, wdata, waddr,raddr, wclken, wclk,rclken,rclk);parameter DATASIZE=8; / 数据宽度parameter ADD

20、RSIZE=4; / 地址宽度output DATASIZE-1:0 rdata; input DATASIZE-1:0 wdata;input wclken, wclk,rclken,rclk; /读写控制和时钟input ADDRSIZE-1:0 raddr, waddr; /输入读写地址reg DATASIZE-1:0 rdata; reg DATASIZE-1:0 MEM 0:(1ADDRSIZE)-1; /存储体always (posedge rclk) /读时钟读出数据if (rclken) rdata = MEMraddr;always (posedge wclk)if (wcl

21、ken) MEMwaddr = wdata; /写时钟写入数据endmodule此模块构造了一个存储器,按读写始终安排输入和输出。编写测试模块如下:module tbmem;parameter DATASIZE=8; parameter ADDRSIZE=4; wire DATASIZE-1:0 rdata;reg DATASIZE-1:0 wdata;reg wclken, wclk,rclken,rclk;reg ADDRSIZE-1:0 raddr, waddr;integer seed1;initialbegin wclk=0;rclk=0;seed1=20; /初始化 waddr=0

22、;raddr=0;endalways #9 wclk=wclk; /生成写时钟always #11 rclk=rclk; /生成读时钟always (posedge wclk)wdata=$random(seed1)/256; /产生随机写入数据initialbegin wclken=1;rclken=0; repeat (10) (posedge wclk); /写入10个数据 wclken=0;rclken=1; repeat (6) (posedge rclk); /读出6个数据 wclken=1;rclken=1; #99 $stop;end always (posedge wclk)

23、if(wclken=1) waddr=waddr+1; /写地址生成always (posedge rclk)if(rclken=1) raddr=raddr+1; /读地址生成fifomem fifomem(rdata, wdata, waddr,raddr, wclken, wclk,rclken,rclk);endmodule仿真波形如图2所示图2 存储仿真波形第一行为读出数据,第二行为写入数据,图中第一行和第二行数据是完全对应的。读地址控制模块FIFO存储器空状态是在读时钟域中生成的,这样就可以确保一旦FIFO存储器达到空状态时就能被检测到。也就是说,在读时钟域里读指针可以在读时钟周期

24、内与同步而来的写指针(包括附加的最高位MSB)进展比拟。当读指针与同步的写指针rwptr2相等时,FIFO存储器为空状态,此时FIFO存储器停止读取数据,否如此会导致向下溢出(underflow)。比拟读指针和同步的写指针以生成空标志的比拟器很容易实现。FIFO存储器的指针总是预先指向下一个内存位置,每进展一次读写操作,相应的指针就增加一次。如果读指针与同步的写指针rwptr2的附加位(这两个指针的最高位MSBs)是相等的,如此这两个指针经历了一样的循环次数,假设这时两个指针的低位(共n-1位)也相等,FIFO存储器就为空状态。在这个模块中包含了除读同步模块之外的所有读时钟域的逻辑电路。读指针

25、是两重格雷码计数器。位指针rptr被同步到写时钟域中,n-1位指针用于产生地址。当读指针rptr的下一个状态rgnext等于同步的写指针rwptr2时,空状态标志将在下一个读时钟的上升沿被置位。这个模块已经是一个读时钟域的同步时序电路,这有利于进展静态时序分析。模块包含读指针电路和空标志逻辑电路,代码如下:module rptr_empty(rempty,raddr,rptr,rwptr2,rinc,rclk,rrst_n);parameter ADDRSIZE=4;output rempty;output ADDRSIZE-1:0 raddr;output ADDRSIZE:0 rptr;i

26、nput ADDRSIZE:0 rwptr2;input rinc, rclk,rrst_n;reg ADDRSIZE:0 rptr,rbin,rgnext,rbnext;reg rempty,raddrmsb;always (posedge rclk or negedge rrst_n)if(!rrst_n) begin rptr =0; raddrmsb=0; endelse begin rptr =rgnext; raddrmsb =rgnextADDRSIZErgnextADDRSIZE-1; endalways (rptr or rinc)begin:Gray_inc integer

27、 i; for(i=0;ii); /格雷码转换为二进制码 if(!rempty) rbnext=rbin+rinc; /增加FIFO计数 else rbnext=rbin; rgnext = (rbnext1)rbnext; /二进制转化为格雷码endalways (posedge rclk or negedge rrst_n)if(!rrst_n) rempty=1b1; /复位时输出空else rempty=(rgnext=rwptr2); /否如此判断是否满足条件assign raddr = raddrmsb,rptrADDRSIZE-2:0; /读地址指针endmodule读地址控制模

28、块主要的局部按代码顺序有复位局部,完成复位功能计数局部,完成格雷码转二进制计数再转换成格雷码;空状态判断局部,复位时输出空,读指针和同步后的写指针一样时出示空状态;最后是一个读地址的拼接输出。为此模块编写测试模块,代码如下:module tbrptr;parameter ADDRSIZE=4;wire rempty;wire ADDRSIZE-1:0 raddr;wire ADDRSIZE:0 rptr;reg ADDRSIZE:0 rwptr2;reg rinc, rclk,rrst_n;initialbegin rclk=0;rrst_n=1;rinc=0; rwptr2=14; #3 r

29、rst_n=0; #4 rrst_n=1; #6 rinc=1; endalways #11 rclk=rclk;initialbegin repeat (5) (posedge rclk); (posedge rempty); #20 $stop;end rptr_empty rptr_empty(rempty,raddr,rptr,rwptr2,rinc,rclk,rrst_n);endmodule仿真波形如图3所示图3 读地址控制模块波形中第一行是空状态,一开始复位一次,计数到0110时与预置的写指针一样,空状态重新变为高电平。此模块可正常完成地址计数和空状态的生成,功能验证结果正确。写

30、指针控制模块与读指针控制模块代码根本一样。此模块仅在满状态判断局部不一样。代码如下:module wptr_full(wfull,waddr,wptr,wrptr2,winc,wclk,wrst_n);parameter ADDRSIZE = 4; output wfull;output ADDRSIZE-1:0 waddr;output ADDRSIZE:0 wptr;input ADDRSIZE:0 wrptr2;input winc, wclk, wrst_n;reg ADDRSIZE:0 wptr, wbin, wgnext, wbnext;reg wfull, waddrmsb;wi

31、re w_2ndmsb,wr_2ndmsb; /添加的判断信号always (posedge wclk or negedge wrst_n)if (!wrst_n) begin wptr = 0; waddrmsb = 0; endelse begin wptr =wgnext; waddrmsb =wgnextADDRSIZEwgnextADDRSIZE-1; endalways (wptr or winc) begin: Gray_inc integer i; for(i=0; ii); /格雷码转二进制 if (!wfull) wbnext = wbin+winc; /FIFO计数 el

32、se wbnext = wbin; wgnext=(wbnext1) wbnext; /二进制转格雷码endassign w_2ndmsb = wgnextADDRSIZE wgnextADDRSIZE-1; /写指针前两位异或assign wr_2ndmsb = wrptr2ADDRSIZE wrptr2ADDRSIZE-1; /同步后的读指针前两位异或always (posedge wclk or negedge wrst_n)if (!wrst_n) wfull=0;else wfull = (wgnextADDRSIZE !=wrptr2ADDRSIZE)&(w_2ndmsb= wr_

33、2ndmsb) & (wgnextADDRSIZE-2:0= wrptr2ADDRSIZE-2:0); /三个判断条件均满足如此满assign waddr= waddrmsb,wptrADDRSIZE-2:0;endmodule由于功能根本相似,此模块不再列出仿真整体仿真结果将五个子模块合成一个顶层模块,代码如下:module fifo_asyn1(rdata,wfull,rempty,wdata,winc,wclk,wrst_n,rinc,rclk,rrst_n);parameter DSIZE = 8; /数据存储宽度parameter ASIZE = 4; /存储地址宽度output 7

34、:0 rdata;output wfull;output rempty;input 7:0 wdata;input winc,wclk,wrst_n;input rinc,rclk,rrst_n;wire 3:0 waddr,raddr;wire 4:0 wptr,rptr,wrptr2,rwptr2;sync_r2w sync_r2w(.wrptr2(wrptr2),.rptr(rptr),.wclk(wclk),.wrst_n(wrst_n);syne_w2r syne_w2r(.rwptr2(rwptr2),.wptr(wptr),.rclk(rclk),.rrst_n(rrst_n);

35、fifomem #(DSIZE,ASIZE) fifomem(.rdata(rdata),.wdata(wdata),.waddr(waddr),.raddr(raddr), .wclken(winc),.wclk(wclk),.rclken(rinc),.rclk(rclk); rptr_empty #(ASIZE) rptr_empty(.rempty(rempty),.raddr(raddr),.rptr(rptr),.rwptr2(rwptr2), .rinc(rinc),.rclk(rclk),.rrst_n(rrst_n);wptr_full #(ASIZE) wptr_full(

36、.wfull(wfull),.waddr(waddr),.wptr(wptr),.wrptr2(wrptr2), .winc(winc),.wclk(wclk),.wrst_n(wrst_n);Endmodule为此模块编写测试模块,代码如下:module tbfifo;parameter DSIZE = 8; parameter ASIZE = 4; wire 7:0 rdata;wire wfull;wire rempty;reg 7:0 wdata;reg winc,wclk,wrst_n;reg rinc,rclk,rrst_n;integer seed;initialbegin wc

37、lk=0;rclk=0;seed=11; winc=0;rinc=0; wrst_n=1;rrst_n=1; #2 wrst_n=0;rrst_n=0; #3 wrst_n=1;rrst_n=1;endalways #9 wclk=wclk;always #11 rclk=rclk;always (posedge wclk)wdata=$random(seed)/256;initialbegin #20 winc=1; repeat (12)(posedge wclk); winc=0;rinc=1; repeat (6)(posedge rclk); winc=1;rinc=1; (pose

38、dge wfull); winc=0;rinc=1; (posedge rempty); #20 $stop;endfifo_asyn1 myasynfifo(rdata,wfull,rempty,wdata,winc,wclk,wrst_n,rinc,rclk,rrst_n);endmodule仿真波形如图4所示这是整体效果,从一开始复位信号产生空状态,然后写入一些数据到FIFO之中,再边写边读,但是由于写周期快,所以FIFO之中的数据量逐渐增加,直至出现写满状态。接下来进入只读阶段,连续读出FIFO的数据,直到读空为止,这样所以得工作状态都得到了验证。图4 仿真波形图图5是波形最后的局部放大图5 数值验证此图中来查看数据准确性:第一行是输出的数据,第四行是输入的数据,在wfull信号出现高电平之后进入只读状态,此时读出的数据89与图中第四行第三个数相等,然后按顺序输出刚写入的数据,数值局部验证正确。该电路图得到的模块图如图6图6 整体模块结构图心得体会在本次实验中,应用了两级同步器,格雷码,存储器等知识;以与亚稳态的处理,格雷码在跨时钟域方面的应用,顶层模块的编写等方法,让本人学到很多。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号