精华内容
下载资源
问答
  • Nand Flash之并行串行

    千次阅读 2019-04-14 16:16:11
    之前我们有讲到NAND Flash与NOR Flash的封装、读取速率、写速率和电路设计等方面的差异,现在我们将会讲到并行串行Flash,主要是串行吧,并行需要讲的很少。 因为串行并行主要区别在于IO口的数量上,因为有些...

    串行Flash与并行Flash

    之前我们有讲到NAND Flash与NOR Flash的封装、读取速率、写速率和电路设计等方面的差异,现在我们将会讲到并行与串行Flash,主要是串行吧,并行需要讲的很少。

    因为串行与并行主要区别在于IO口的数量上,因为有些场景上我不会用到那么大的容量,E而且也不会有那么多的IO引脚,所以串行NOR与NAND Flash应运而生。

    并行与串行NAND Flash

    SERIAL NOR FLASH

    串行NOR Flash支持标准串行外设接口(SPI),双/四路I / O SPI:串行时钟,片选,串行数据I / O0(DI),I / O1(DO),I / O2和I / O3。

    也就是说对于串行NOR Flash而言,支持下面的几种模式。
    
    Standard SPI

    Standard SPI: CLK, /CS, DI, DO

    Dual SPI

    Dual SPI: CLK, /CS, IO0, IO1

    Quad SPI

    Quad SPI: CLK, /CS, IO0, IO1, IO2, IO3

    传输时钟速率

    • 串行NOR Flash 在Standard SPI模式下支持高达133MHz 时钟频率(标准模式);
    • 串行NOR Flash 在Dual SPI模式下支持高达133MHz *2 时钟频率(Dual);
    • 串行NOR Flash 在Quad SPI模式下支持高达133MHz *4 时钟频率(Quad);
    这些传输速率可以胜过标准的异步8和16位并行闪存。
    

    串行NOR Flash常见封装

    如下为SOIC/SOP8封装
    SOP8封装 串行NOR Flash
    引脚介绍
    NOR Flash引脚介绍

    1. IO0 and IO1 are used for Standard and Dual SPI instructions;
    2. IO0 – IO3 are used for Quad SPI instructions, /HOLD (or /RESET) function is only available for Standard/Dual SPI.

    以上引脚如何配置
    1)标准 SPI模式
    连接CS、CLK、DO和DI就可以,WP引脚和HOLD引脚视情况而定,看是要硬件上拉到Vcc还是电阻下拉到地。

    2)Dual SPI模式
    连接CS、CLK、DO(IO1)、DI(IO0),此时注意不是按照DO和DI工作,而是括号内的IO1和IO2。

    3)Quad SPI模式
    CS、CLK、DO(IO1)、DI(IO0)、WP(IO2)、HOLD/Reset/IO3,此时WP与Hold功能全都不能使用。

    标准SPI,双SPI和四线SPI操作
    标准SPI指令使用单向DI(输入)引脚在串行时钟(CLK)输入引脚的上升沿串行写入指令,地址或数据。
    标准SPI还使用单向DO(输出)在CLK下降沿读取器件的数据或状态。
    双SPI和四SPI指令使用双向IO引脚串行写入指令,在CLK的上升沿向器件发送地址或数据,并在CLK的下降沿从器件读取数据或状态。

    电路设计

    片选(/ CS)

    SPI片选(/ CS)引脚用于使能和禁止器件操作。 当/ CS为高电平时,器件被取消选择,串行数据输出(DO或IO0,IO1,IO2,IO3)引脚处于高阻态。取消选择后,器件功耗将处于待机电平,除非内部擦除,编程或写状态寄存器周期正在进行中。当/ CS变为低电平时,将选择器件,功耗将增加到有效电平,并且可以写入指令并从器件读取数据。
    上电后,/ CS必须在接受新指令之前从高电平转换为低电平。/ CS输入必须在上电和断电时跟踪VCC电源电平。如果需要,可以使用/ CS引脚上的上拉电阻来实现此目的。

    写保护(/ WP)

    写保护(/ WP)引脚可用于防止写入状态寄存器。扇区的部分或整个存储器阵列可以受硬件保护,需要与寄存器相互配置。 WP引脚为低电平有效。

    HOLD(/ HOLD)

    / HOLD引脚允许器件在主动选择时暂停。当/ HOLD变为低电平时,/ CS为低电平时,DO引脚将处于高阻态,DI和CLK引脚上的信号将被忽略)。当/ HOLD变为高电平时,器件操作可以恢复。
    当多个设备共享相同的SPI信号时,/ HOLD功能非常有用。 / HOLD引脚为低电平有效。

    Serial Clock (CLK)

    SPI串行时钟输入(CLK)引脚提供串行输入和输出操作的时序。

    复位(/ RESET)

    当它被驱动为低电平至少1μS的时间时,该器件将终止任何外部或内部操作并返回其上电状态。

    电源

    串行Flash的电压也有多种规格,具体的看数据书册就行。

    电路设计

    SPI Nor Flash
    如上图为串行 Nor Flash电路,
    WP引脚连接电阻至Vcc;为了不进行写保护,所以做上拉,但是不悬空是为了在上电时有一个确定的电平状态。
    Hold引脚连接上拉电阻至Vcc;为了不实现数据保持,所以做上拉,但是不悬空是为了在上电时有一个确定的电平状态。
    CS引脚也是上拉至Vcc。上电不选中该器件,只有后续CPU低电平才会选中该器件。

    以下请参考:
    [1].华邦
    [2].复旦微电子

    SERIAL NAND FLASH

    在这里插入图片描述
    注:因为串行NOR Flash与串行NAND Flash所要实现的功能一致,为了减少/简化并行Nand Flash的接口,所以两者的封装等都是一样的,差异只是在于存储器单位存储的结构不同,其余都是一样的,所以大家知道了串行NOR Flash的介绍后,同样就理解串行NAND Flash了。

    20194.14
    于深圳

    展开全文
  • 并行串行传输,串行高速传输

    千次阅读 2018-03-24 17:17:05
    串行只有一根数据线,不存在信号线之间的串扰,而且串行还可以采用低压差分信号,可以大大提高它的抗干扰性,所以可以实现更高的传输速率,尽管并行可以一次传多个数据位,但是时钟远远低于串行,所以目前串行传输...

    在高速状态下,并行口的几根数据线之间存在串扰,而并行口需要信号同时发送同时接收,任何一根数据线的延迟都会引起问题。而串行只有一根数据线,不存在信号线之间的串扰,而且串行还可以采用低压差分信号,可以大大提高它的抗干扰性,所以可以实现更高的传输速率,尽管并行可以一次传多个数据位,但是时钟远远低于串行,所以目前串行传输是高速传输的首选。

    从另外的角度来看,并行传输还有很多方式如系统同步(共同时钟)方式及源同步时钟方式等。先来看系统同步先天的内在问题


    首先,并行信号由于需要多位传输路径,这在早期是可以接受的。但是摩尔定律的现象使得与几十年前相比可生产的芯片中硅电路的数量大幅增加,而芯片封装技术的pin密度并没有像硅密度一样以相同的速度在增加,因此I/O pin的封装实际上比硅电路还贵,这就意味着对于大多数芯片来说pin管脚越来越多变得不可接受。

    第二个内在问题就是时序的需求。由上图可知数据由芯片#1同步启动并被芯片#2使用相同的时钟同步捕获,芯片#2输入端的数据必须满足相对于芯片的时钟输入的建立和保持时间。详细的系统同步时序模型如下图所示。这些建立和保持时间必须用足够的余量来计算,以允许时钟分配路径延迟到两个芯片的差异,并通过芯片到启动和捕捉触发器。延迟可能会因芯片工艺,电压和温度(PVT)条件而异,并且必须增加余量以应对最坏的情况。对于较高的时钟频率,可能需要在芯片中使用锁相环(PLL)来调整时钟相位,以补偿芯片内的时钟分配延迟,并适应变化的工艺,电压和温度条件。 如果时钟频率足够高,则不可能建立一个可以通过这种共同时钟总线来可靠地传输数据的系统。

    我们都知道源同步方式的典型代表是DDRx信号,下面就来介绍源同步方式是怎样改善系统同步的先天不足的。

    源同步要解决的第一个问题是减少在芯片之间传输数据所需的I/O引脚数量。这通过将芯片#1的输出处的n位数据复用到k位互连(k <n),然后将芯片#2的输入上的互连的k位解复用到n位内部数据路径上来实现,如下图所示。所得到的系统只需要每个芯片上的k个I/O引脚,而不需要先前的n个引脚。


    当然,虽然引脚数量要求已经通过k:n的比率降低,但是参考时钟的所需频率已经增加了这个比率的倒数。由于噪声,电磁干扰(EMI)和功耗问题,系统设计人员通常不喜欢在系统内分配高速参考时钟。通常,分配较低频率的时钟,并且使用芯片中的PLL将该参考时钟乘以可用频率,但所产生的时钟相位的变化以及数据传输的频率越高,往往会加剧并行数据总线的时序问题。

    源同步的第二个法宝就是在两个芯片之间的数据通路中增加了一个高速时钟,如下图所示。假设该时钟源提供的时钟频率略低于在芯片互连上对数据进行触发的时钟频率,每个芯片都使用PLL来产生这个频率倍数的时钟,所得到的时钟用于启动和捕获相应芯片中的数据。芯片#1中用于从该芯片启动数据的PLL的输出时钟也是该芯片的输出,芯片#2使用该时钟来捕获数据,这种方法称为时钟转发。


    时钟转发的优点是用于在芯片#1上启动数据的高速时钟可用于芯片#2作为捕获数据的参考。这样之前通过时钟分配网络驱动两个芯片的延迟的变化在时序分析中就不需要考虑了,只有时钟路径和数据位之间的延迟变化是相关的。 虽然制程,电压和温度导致的这些路径之间的变化在一定程度上相互影响,但接口的时序分析需要的余量较少,因此建立和保持时间更容易满足。

    我们还是来看看典型的例子吧,图中所示的时钟可以是单数据速率(SDR)或双倍数据速率(DDR)的时钟,如下图所示。SDR就是接收芯片在SDR时钟的每个上升沿(或每个下降沿)捕获数据; 而DDR则是接收芯片捕获DDR时钟的每个边沿(上升沿和下降沿)的数据。


    无论时钟是SDR还是DDR时钟,接收芯片都使用该时钟直接捕获数据。 该芯片还使用参考时钟以相同的频率生成内部系统时钟,这些时钟是中间同步的。 虽然频率相同(鉴于它们共享共同的频率参考),但是时钟之间的相位关系是未知的,并且可能由于PVT变化而变化。 因此,接收芯片通常将接收到的数据从接口时钟域重新定时到内部芯片时钟的时钟域。 FIFO用于执行此重定时功能, 期望最小化由接口时钟计时的触发器的数量,以便最小化时钟分配网络中的延迟,否则时钟问题将会加剧。

    高速串行信号相对于并行信号最主要的就是通信方式的改进,这种通信方式又叫自同步方式,也即两块芯片之间通信,其中发送芯片产生的数据流同时包括数据和时钟信息,如下图所示。


    要实现上图所示的通信,在芯片内部还有更加详细的一些要求及模块来操作,具体实现可以参考下图所示模块框图。


    这些最主要的模块包括串行器(也有叫串化器)、时钟数据恢复(CDR)、解串器以及均衡器等。这样的设备与源同步接口不同,因为接收机设备包含时钟和数据恢复(CDR)电路,其基于信号的跳变沿来动态地确定数据信号的最佳采样点。 换句话说,从数据中直接提取时钟信息,而不是依赖于单独的时钟。本篇我们主要来简单介绍前面三种必要的模块,均衡器会在后续的内容中介绍。

    串行器要实现的功能就是并串转换,简单来说就是将原本并行的数据转换成串行的数据。目前有两种主要的并串转换方式——可装载移位寄存器和回转选择器。

    解串器的功能正好和串行器的功能及步骤相反,那就是将串行的信号又重新转换成并行信号,又叫串并转换,

    时钟数据恢复(CDR)顾名思义就是将数据流里面的数据和时钟在接收端恢复出来,说起来感觉很简单的样子,但实际上如下图所示时钟恢复过程无法产生一个共用时钟或者同数据一起发送的时钟。作为替代,由锁相环(PLL)合成出一个与输入串行信号的时钟频率一致的时钟,也即PLL能根据参考时钟和输入信号来产生锁定于输入信号的新时钟,所以PLL对于Serdes的接收也是至关重要的。

    这就是我们的串行信号,呈现出来的确实是比较简单,无非就是几对差分线路,但内部的操作却非常复杂。虽然简化了PCB设计,但对芯片的设计挑战巨大,因为有更多的模块集成在芯片内部了

    并行信号之于串行信号,唯一的区别就是多比特同传,因而产生了:更多的布线/连接线的空间需求;在高波特率下可能的一致性要求:驱动器,信道的时域/频域一致性;密集信号线间的串扰隔离需求。  但是,并行信号本质上就是串行信号的聚合,串行信号物理层能采用的技术,比如差分,加重/均衡/链路训练,CDR等等,都可以在并行信号中采用;此时需要额外考虑的是如何完成信号间一致性的信息收集与恢复和数据的拆分聚合。  为低成本,低延迟设计的单端、随路时钟的并行信号对为高吞吐,简化互联设计的高速差分信号,自然存在串扰,EMI,布线空间,等长/SKEW等等问题。   

    1,高速串口不需要传时钟来同步数据流,没有时钟周期性的沿变,频谱不会集中,所以噪声干扰少。通过数据流的沿变可以直接用PLL恢复出时钟,再用恢复的时钟采集数据流。不传时钟可以降低功耗,减少噪声。  2,所有高速串口都采用差分总线传输,外界噪声同时加载到并行传输的两条差分线上,相减之后可以抵消,对外部噪声的抵抗能力强。  3,没有时钟skew问题,因为它根本就没有同步时钟,不存在时钟和数据流的对齐问题。只需要保证差分信号线是对齐的就行,因为差分信号线的值总是相反,相关性强,易控制。  4,线少,干扰少。串行传输一般就4根数据线,分成Rx两根差分线和Tx两根差分线,差分线总是往相反方向跳,可以抵消各自的跳变噪声,这种噪声可以被Rx的负极性以相反的跳变直接抵消掉,总的噪声为0,根绝了内部噪声。  另外,串行采用差分信号,CDR,信道均一化等技术使得信号质量得到保障。串口的特性和应用场景,决定了它更加适合采用一些可以提高单根信道速率的设计方法,这些方法用在并口上并不合适。  综上,串口传输的各种优势使得其内外噪声皆免疫,又没有信号对齐之忧,可以以极高的速率传输。这种速率,并行传输是根本做不到的,更不要说串行传输还能节省大量引脚。    

    您了解的均衡器的种类与功能有哪些?

    相信大家通过阅读之前高速串行简史的文章也对串行信号有比较深刻的认知了。为什么要用到串行信号,客观原因是从我们对数据传输的需求,因此迫使我们在速率上不断向摩尔定律靠拢。基于之前所述的串行信号的种种优势,因此在速率不断提高的情况下,我们就会认为串行信号是最好的选择。但是真到了速率不断攀升的时候,我们用同一个串行链路去承载它们时,就会发现没有想象中那么容易。


    那再往10G,25G,56G走呢,岂不是就玩不转啦?


    在我们的串行链路中,损耗可以算是眼睛张不开的最大元凶,从理想的插损曲线来判断的话,我们一般会记住几个常用的数值:

    0dB=1,-3dB=0.7,-6dB=0.5,-12dB=0.25,-20dB=0.1。。。然后速率越高,dB值肯定越大啦,那么眼高自然就慢慢没了…… 


    当然,我们也会想出很多方法来应对,比如板材是影响损耗的重要因素,我们从原始普通的FR4板材到中等损耗板级,再到低损耗板材,再到超低损耗板材,那在同样的走线长度,自然损耗就小了,或者换句话说在同样的损耗情况下,我们可以走得更长了;然后铜箔粗糙度也会影响损耗,因此我们把铜箔的工艺研究得非常深入,也制造出了各种的铜箔类型;

    然而无论是对板材还是对铜箔的优化,付出的是更高的成本,更新板材和铜箔也需要经历时间的等待。我们习惯性把一个链路分成三个部分,分别为发送芯片,传输通道和接收芯片。我们上述对板材,铜箔的优化其实都是对传输通道进行的优化,那我们能不能在芯片本身做点文章呢?


    答案当然是可以的。均衡技术就是针对芯片端的其中一种技术。实际上均衡技术种类有很多,从结构上分类,分为线性和非线性均衡,或者说是模拟和数字的区别;按作用来分类则可分为频域均衡和时域均衡,前者是校正频率特性,而后者是直接校正畸变的波形;按调节方法还可以分为固定均衡和可变均衡,而可变均衡分为手动均衡和自适应均衡。按抽样时间间隔的不同来分类,又可以分为码元间隔均衡和分数间隔均衡等等。


    在那么多的分类之中,的确在我们的PCB链路上常用的一般就三种:CTLE(Continuous Time Linear Equalization)连续时间线性均衡,FFE(Feed Forward Equalization)前向反馈均衡和DFE(Decision Feedback Equalization)决策反馈均衡。前面两种是线性的均衡器,而且后面的DFE是非线性的。


    均衡的终极作用就是把我们接收端的眼图给睁开,的确从PCB相关的仿真或者测试中也验证了这个是一项非常有用的技术,在芯片里面去做这样的技术,与从板材铜箔这些的优化相比,也是成本较低的方案。


    为了把信号传输更远距离,通常在发送端和接收端使用预加重或去加重的均衡技术,通过提升信号的高频部分能量,以补偿传输通道对高频的衰减。均衡器有:1.CTLE连续时间性均衡器,特点是功耗低,实现简单,不会增大抖动。2.FFE前端反馈式均衡器,由延时电路、乘法器、加法器组成的模拟均衡器。3.DFE判决反馈均衡器,特点不会放大噪声和串扰,易于实现,广泛应用高速收发芯片中。  


    为什么损耗全频段都变差的情况下,居然能够使眼图张开呢,隐藏着什么秘密???

    基于上文那个张开和闭合的眼图,我们来对比下它们接收端的波形:


    可以看到,眼图之所以闭合,主要是由于在蓝色圈圈处的地方出了问题,通过观察,我们发现了最容易出问题的地方就是在低频信号向高频信号电平转换的地方,即连续的0或1之后转换成单独的1或0 的地方

    通过该通道的损耗情况能够发现,由于连续0或连续1对应的频率是比较低的,因此对应的幅度衰减也比较小,单独的1或0 的转换则代表高频的情况,幅度衰减则比较大,因此连续0或连续1在经历高幅度后突然要转换成相反电平的高频数据,这个高频数据幅度衰减很大,就会跟不上节奏,不能拉回到一个属于它电平的正确位置。


    我们对这种码型带来的恶劣影响的情况称为ISI,码间干扰,这是又一种重要影响信号质量的因素,不同于阻抗不匹配,反射或者串扰这些影响,即使把传输线优化得很好,阻抗反射串扰这些都考虑得很完善,但是只要通道的衰减达到一定的幅度,都有可能出现码间干扰

    另外,上篇文章眼图之所以能张开,是因为我们人为的把低频的衰减变大了,这样的话,连续0或连续1的幅度就没那么高,这样转向的时候单独1或0的这一位数据就能拉得更上一点。加入CTLE均衡就是这个效果,在上文的接收端频率响应中,虽然加入CTLE后整体的损耗都变差了,但是把低频分量衰减更多。这样的话高频和低频损耗的差距却明显变小了,这样面对这种码间干扰时就很有帮助。当然缓解这种码间干扰还有其他一些方法,这里就先继续卖关子了

    前面文章说过,在高速链路中导致接收端眼图闭合的原因,很大部分并不是由于高频的损耗太大了,而是由于高低频的损耗差异过大,导致码间干扰严重,因此不能张开眼睛。针对这种情况,前面有讲过可以通过CTLE和FFE(包括DFE)均衡进行解决,原理无非就是衰减低频幅度或者抬高高频幅度,从而达到在接收端高低频均衡的效果。同时我们在前文还埋了个伏笔:

    现在就把这个关子拿出来讲讲,也就是今天要说的编码方式。说到针对于NRZ数据的编码方式,本人听过的有4B/5B,8B/10B,64B/66B,64/67B,128B/130B,128B/132B编码(可能各位还有其他吧),不同的编码方式针对于不同的信号协议,当然效率也是不一样的。什么叫效率?在数据包传送的术语叫开销,意思就是除了实际需要的数据之外的一些数据bit,例如冗余校验等。那大家看上面的编码的数值比就知道了,例如8B/10B,要把8bit的实际数据扩展为10B,那开销就是20%,效率就只有80%了,更通俗来说就是增加了20%的非实际数据的传输 。所以一个好的编码方式,除了看它本身的算法优化情况外,还要注重效率高不高。


    本人将用两期的篇章主要介绍下8B/10B和64/66B编码方式,其他的主要都是由他们扩展开来的。那介绍完前面总体的情况后,进入本期的主题,8B/10B编码。


    首先,为什么要编码?原来的码型有什么不好的地方吗?其中最主要的原因用下面这个图来进行解释:

    大家看明白了吧,由于我们的串行链路中会有交流耦合电容,我们知道理想电容的阻抗公式是Zc=1/2πf*C,因此信号频率越高,阻抗越低,反之频率越低,阻抗越高。因此上面的情况,当码型是高频的时候,基本上可以不损耗的传输过去,但是当码型为连续“0”或者“1”的情况时,电容的损耗就很大,导致幅度不断降低,带来的严重后果是无法识别到底是“1”还是“0”。因此编码就是为了尽量把低频的码型优化成较高频的码型,从而保证低损耗的传输过去。


    上面解释了原因,下面就介绍下这种8B/10B的编码方式的算法。


    对于一些常见的编码方式而言,即使在我们不知道它们有什么好处的情况下,也很容易想到它的缺点,那就是需要增加额外的数据bit。因此我们看到很多像PCIE1,明明传输了2.5Gbps的数据,但是实际上有用的数据只有2G;PCIE2也一样,5Gbps的数据由于的只有4G。对于8B/10B编码而言,这是一笔不小的损失。人们还是要用这种编码,也正说明了它的好处显然会大于它的坏处。很多网友也回答得非常精准,我再稍微汇总下大家的答案哈。


    首先,使用8B/10B编码的根本目的是为了直流平衡,所谓直流平衡,在上文中也图文并茂的解释过了。但是说到保证直流平衡又是为了什么呢?我们可以对上文的prbs7码型(10Gbps)进行有和没有8B/10B编码的频谱进行分析,如下所示:

    可以看到,进行8B/10B编码后,信号在低频段的能量明显少了,补充到了高频能量中去了。其实,这就相对于一个高通滤波器,根本作用和之前说的加重均衡时一样的,就是使经过传输通道后保持平坦的频域曲线,这样能有效缓解ISI码间干扰,使接收眼图扩大。所以说,使眼图变好的方式有很多,但是万变也不离其中,原理都是殊途同归的。


    另外,通过8B/10B后,连续0或1的数量减少了,对于时钟恢复电路(CDR)也是有帮助的。它使CDR能够有更多的边沿变换,对于CDR的PLL来说,更能把握到数据的内嵌时钟,对正确采样数据和减小抖动有很大的益处。

    其他的作用还包括:


    增加一些校验的码型,起到数据对齐和控制命令的作用,增加纠错的能力;


    编码后起到加密的作用;


    编码有一定的规律的码型,对解码来说也有帮助。


    上文说完了8B/10B之后,我们再来说说貌似更复杂的64B/66B编码。很多人可能在想,8B/10B编码主要作用的优化直流平衡,从8bit中插2个bit进去,这样的话最终效果能够使长0或者长1的位数不超过5位,达到很好的效果。那64B/66B编码方式呢?在从64个bit中仅加入2个bit,能够很好的解决长0长1的问题吗?作用似乎只是杯水车薪,2个bit相对于64个bit太少了。但是这种64B/66B的编码方式流行至今,肯定是有它自己的优势所在,其实它和8B/10B编码还是有原理上和算法上的区别的。


    当然,如果仅靠这2个bit来实现8B/10B的作用显然不太现实。其实上,这两个bit只是起一个同步头的作用,主要用于接收端的数据对齐和接收数据位流的同步。这两个bit有“10”和“01”两种同步表征方式,“01”表征后面的64个bit都是数据bit,“10”表示后面的64bit是数据和控制信息的混合,其他的“00”或者“11”都表征为错误信息。数据bit用“D”表示,每个数据码是8bit;控制bit用“Z”表示,每个数据码是7bit。其中在“10”这种数据控制信息混合的情况下,后8个bit划分为表征类型域,其后的56bit才是真正的控制信息或数据控制两者的混合。另外“S”表示包开始传送,“T”表示包传送结束,值得注意的是“S”只能在第0和第4字节出现。“T”可以出现在任何的字节。


    那按照上面说的,新加的2个bit只是作为同步,那后面的数据可以如何优化呢?这里有一项区别于8B/10B编码的技术——扰码。


    所谓扰码,就是一种将数据重新排列或者进行编码以使其最优化的方法。怎么才能最优化呢?就是使数据bit的“0”和“1”最大程度的达到随机分布,进一步减轻了抖动的码间串扰,提高了数据传输的可靠性。从本质上说,扰码就是为了达到这个目的而在传输数据进入信道传输之前,对其进行的比特层的随机化处理过程。它的方法就是使用上面prbs码型所说到的多项式,关于多项式的选择通常是基于扰码的特性,包括生成数据的随机度,以及打乱连0和连1的能力。从prbs多项式的角度说,其实就是重新生成伪随机码型的过程。64B/66B的扰码使用的多项式是1+X39+X58。按照前期说的prbs生成器,得到该扰码器的框图如下所示:


    就是前几期prbs说的第39和第58位的异或运算,然后通过移位寄存器输出的码型结果。当然,前面说了,64B/66B编码的这种扰码方式目标是使数据的“0”和“1”最大程度的随机分布,减小连续出现的情况,因此它可能并不能适用于所有的码型,不像8B/10B编码一样对所有的bit组合都有出色的表现,具体还要看接收器的能力。但是无可否认,它最大的好处是效率比较高,传输冗余的bit只有2位,不像8B/10B编码需要20%的开销。这方面在更高速的传输环境下更具有优势。

    转载自:高速先生

    展开全文
  • 本文节选自《FPGA之道》,来一起学习下作者对于并行串行的讲解。

    前言

    本文节选自《FPGA之道》,来一起学习下作者对于并行与串行的讲解。

    Verilog的并行语句

    在Verilog的基本程序框架一节中,我们了解到,module模块实现中的语句部分里面的语句都是并行的。那么到底Verilog里面有哪些并行语句可以供我们使用呢?请看如下描述:
    module <module_name>(<port_list>);
    <Verilog连续赋值语句>;
    <Verilog程序块语句>;
    <Verilog实例化语句>;
    <Verilog生成语句>;
    <Verilog函数调用语句>;
    <Verilog模块说明语句>;
    endmodule;
    以上这些并行语句,没有哪一类是不可或缺的,但是一个module中怎么着也得至少有一条,否则虽然从语法上来讲没什么问题,但是这个module就不具有任何功能了。一般来说,只要有模块实例化语句、程序段语句和连续赋值语句这三类语句就足够描述FPGA的功能了,这也正是我们在【Verilog的基本程序框架】一节中所介绍的。下面详细介绍一下这些并行语句。

    Verilog连续赋值语句

    Verilog中共有两种连续赋值语句,即普通连续赋值语句和条件连续赋值语句。它们都只能给线网类型的变量赋值,前面的章节已经对于这两种语句有过介绍,这里简要总结如下:

    普通连续赋值语句

    该语句的显式语法为:
    assign <wire_name> = ;
    也可以有隐式的写法,即在声明线网变量时同时赋值,例如:
    wire a;
    assign a = 1’b1;
    可以简写成:
    wire a = 1’b1;

    条件连续赋值语句

    该语句的显式语法为:
    assign = <1-bit_select> ? <input_for_1> : <input_for_0>;
    也可以有隐式的写法,即在声明线网变量时同时赋值,例如:
    wire a, b, sel, c;
    assign c = sel ? a : b;
    可以简写成:
    wire c = sel ? a : b;

    Verilog程序块语句

    Verilog中共包含两种程序块语句——initial与always,它们的本质区别是initial程序块仅在程序的最开始执行一次,而always程序块会不断地、循环地得到执行。因此,initial程序块主要负责模块的初始化功能,而always程序块才主要负责模块的功能描述。下面,我们将针对always程序块,进行详细讲解。always的语法结构如下:

    always@(<sensitive_list>)
    begin : <lable>
       	<statements>;
    end
    

    其中,是可选的,它是程序块的标号,主要起到提高代码可读性的作用。注意,如果always中只包含一条子语句,那么begin-end关键字可省略,不过必须紧跟在begin关键字的后面,因此有的程序块不能省略begin-end关键字,其它含有begin-end关键字语法的语句也是类似。
    之前已经介绍过,按照<sensitive_list>的形式来分,always共有三个“纯种”的基本类型:纯组合always、纯同步时序逻辑always、具有异步复位的同步时序逻辑always。不过如果从always程序块的结构入手,我们的可以将always的语法划分可以更细一些,即:纯组合always、纯时序always、具有同步复位的always、具有异步复位的always以及具有混合复位的always。可见,基于结构的划分比基于<sensitive_list>的划分更细致一些、面更广一些。基于结构的划分其实就是基于时钟描述结构的划分,因此,首先来介绍一下在always中表示时钟事件的方法。

    沿事件

    时钟描述实际上就是基于时钟信号跳变沿的描述。如果在有一个变量a,那么表示a变化的时候会触发always的执行,但是如果要描述时序逻辑,我们显然需要更精确的描述方法,于是Verilog提供了沿事件。沿事件的描述有两种语法:
    posedge ,
    negedge ,
    分别对应敏感变量的上升沿事件或者下降沿事件。我们可以利用这两句语法来方便的描述时钟等事件。
    切记!在一个always的敏感量表中,只能出现一个时钟信号的一种边沿事件。这是由寄存器的结构决定的,因为一个寄存器只有一个时钟端口,并且只敏感这个端口的某一个边沿,因此凡是不尊重这个事实的代码都是不可综合的。不过,异步复位信号有时候也做边沿事件放入到敏感量表中,但是请注意,异步复位其实是电平敏感事件,之所以作为边沿事件放入到敏感量表中,很大程度上是为了方便仿真。

    下面根据时钟描述结构的不同,分别介绍五种基本always代码结构如下:

    纯组合always

    纯组合always语法如下:
    always@(<sensitive_list>)begin
    ;
    end
    参考例子如下:
    // b must be register data types
    always@(a)begin
    b = not a;
    end
    上述例子描述了一个非门的结构,关于纯组合always程序块,有三点需要注意:
    一、纯组合always程序块中的语句强烈推荐只使用阻塞赋值符号,而时序always程序块中推荐只使用非阻塞赋值符号,否则会带来非常多的隐患。
    二、虽然从字面上理解,always是在变量a出现变化的情况下才触发执行,但是不可自作聪明将上例写成:
    // It is wrong!!
    always@(posedge a or negedge a)begin
    b = not a;
    end
    注意,只有时序逻辑才能用posedge和negedge关键字,虽然从代码事件解释来看上述两例好像功能相似,但是若出现沿事件关键字,则编译器会将程序块综合为时序逻辑,而这个世界上目前还没有既能够敏感一个信号上升沿又能够敏感这个信号下降沿的触发器,所以综合会报错。
    三、若<sensitive_list>中有多个变量,则可以用逗号“,”或者关键字or分隔开来。如果<sensitive_list>中的变量确实太多,Verilog给大家提供了一个偷懒的方法,那就是使用匹配符号“”,此时编译器将会完成<sensitive_list>中的元素推断。例如:
    always@(
    )

    always@*

    纯时序always

    纯时序always的语法如下:
    always@(<edge_type> clk) // only clk in the sensitive list
    begin
    ;
    end
    参考例子如下:
    // a must be register data types
    always@(posedge clk)
    begin
    a <= b;
    end

    具有同步复位的always

    具有同步复位的always的语法如下:
    always@(<edge_type> clk)
    begin
    if(rst) //or if(!rst)
    begin
    ;
    end
    else
    begin
    ;
    end
    end
    参考例子如下:
    //a must be register data types
    always@(negedge clk)
    if(!rst)
    a <= 1’b0;
    else
    a <= b & c;

    具有异步复位的always

    具有异步复位的always语法如下:
    always@(<edge_type> clk, <edge_type> aRst)
    begin
    if(aRst) //or if(!aRst)
    begin
    ;
    end
    else
    begin
    ;
    end
    end
    参考例子如下:
    //n must be register data types
    always @(posedge clk or negedge aRst)
    if (!aRst)
    n <= 8’b0;
    else
    n <= m;
    注意,在Verilog中必须通过敏感量表中的一个沿事件再加上代码中的电平判断来实现一个电平敏感的异步复位。以下写法虽然道理上也说的通,但是却是Verilog不支持的:
    always @(posedge clk or aRst) //will cause compile error
    if (!aRst)
    n <= 8’b0;
    else
    n <= m;
    这是由于always敏感量表中一旦出现了沿事件,就不允许再出现描述组合逻辑的信号事件了。

    具有混合复位的always

    具有混合复位的always的语法如下:
    always@(<edge_type> clk, <edge_type> aRst)
    begin
    if(aRst) //or if(!aRst)
    begin
    ;
    end
    else
    begin
    if(rst) //or if(!rst)
    begin
    ;
    end
    else
    begin
    ;
    end
    end
    end
    也可以写成如下形式:
    always@(<edge_type> clk, <edge_type> aRst)
    begin
    if(aRst) //or if(!aRst)
    begin
    ;
    end
    else if(rst) //or else if(!rst)
    begin
    ;
    end
    else
    begin
    ;
    end
    end
    参考例子如下:
    //a must be register data types
    always@(posedge clk, negedge aRst)
    if(!aRst)
    a <= 4’h0;
    else
    if(rst) //or if(!rst)
    a <= 4’hF;
    else
    a <= b;

    Verilog实例化语句

    实例化语句是非常重要的一种语句,有了它,我们才可以化繁为简、聚简成繁!在之前的Verilog基本程序框架小节中我们对实例化语句进行了一些简单了解,而在这里我们将详细介绍一下Verilog中的实例化语句。Verilog语言中支持两种模块实例化方式——单独实例化与数组实例化,分别介绍如下:

    单独实例化

    单独实例化的语法如下:
    <module_name> <instance_name> (.<port_name_0> (),
    .<port_name_1> (),

    .<port_name_N> ());
    其中,<module_name>是一个已经完成模块的名字,<instance_name>是我们给它实例化对象起的一个名字,这两者之间的对应关系很像C++中类和对象之间的关系。<port_name_X>对应被实例化模块中的具体端口名称,其后的为与端口连接的父模块内部的变量。例如:
    wire a, b, c;
    myAnd insAnd (.in0 (a), .in1(b), .out©);
    注意,实例化的时候,实例的输出端口只能连接线网类型的变量,而输入端口可以连接线网或者寄存器类型的变量。

    数组实例化

    有些情况下,我们可能需要同时实例化一个模块多次,这个时候如果使用单独实例化语句会使代码显得非常的臃肿,也不利于阅读和修改,于是Verilog提供了数组实例化语句,语法如下:
    <module_name> <instance_name> <instance_array_range>
    (.<port_name_0> (variable0),
    .<port_name_1> (variable1),

    .<port_name_N> (variableN));
    可以看出,相比于单独实例化语句,它主要多了一个<instance_array_range>参数,利用这个参数,我们就可以控制实例的数量。例如:
    wire [3:0] a, b, c;
    myAnd insAnd[3:0] (.in0 (a), .in1(b), .out©);
    上述数组实例化语句的功能相当于
    myAnd insAnd3 (.in0 (a[3]), .in1(b[3]), .out(c[3]));
    myAnd insAnd2 (.in0 (a[2]), .in1(b[2]), .out(c[2]));
    myAnd insAnd1 (.in0 (a[1]), .in1(b[1]), .out(c[1]));
    myAnd insAnd0 (.in0 (a[0]), .in1(b[0]), .out(c[0]));
    有些时候,众多实例中的有些端口是需要共用信号的,例如使能信号,此时可以写成这样:
    wire en;
    wire [3:0] a, b, c;
    myEnAnd insEnAnd[3:0] (.in0 (a), .in1(b), .inEn (en), .out©);
    此时的数组实例化语句的功能相当于
    myAnd insAnd3 (.in0 (a[3]), .in1(b[3]), .inEn (en), .out(c[3]));
    myAnd insAnd2 (.in0 (a[2]), .in1(b[2]), .inEn (en), .out(c[2]));
    myAnd insAnd1 (.in0 (a[1]), .in1(b[1]), .inEn (en), .out(c[1]));
    myAnd insAnd0 (.in0 (a[0]), .in1(b[0]), .inEn (en), .out(c[0]));
    注意,数组实例化时,对输入的变量位宽是有一定要求的:
    一、等于所有实例对应端口的位宽之和。例如对于上例的变量a来说,它的位宽等于4个实例中in0端口的位宽和:1bit*4 = 4bits。这样变量的位宽将会被均分到各个实例的对应端口上;
    二、等于模块对应端口的位宽。例如对于上例的变量en来说,它的位宽就等于模块只能够inEn端口的位宽,为1bit,此时该变量就会被连接至所有的实例对应的端口上。
    对于其他情况的位宽输入Verilog的数组实例化语句都是不支持的,请不要在这个地方进行错误的发明创造。

    实例参数重载

    参数重载也是实例化语句的一个重要组成部分。在Verilog基本程序框架中,我们提到过,为了增强模块的重用性,Verilog会在模块中定义一些参数,从而通过再例化的时候对参数进行重载来适应不同的需求。按照例化时对参数的重新赋值方式,我们可以把参数重载分为内部重载与外部重载,分别介绍如下:

    内部重载
    内部重载使用”#(.<parameter_name>(new_value), …)”语法,例如:
    cellAnd #(.WIDTH(4)) m (a,b,c);
    这是我们在【Verilog基本程序模板】小节中给出的例子。

    外部重载
    相比于在模块实例化的时候来修改参数的值,外部重载允许在编译的时候再修改参数的值。外部重载需要用到defparam关键字,举例如下:
    cellAnd m (a,b,c);
    defparam m.WIDTH = 4;
    不过在使用defparam的时候需谨慎,因为有些综合工具或者它们的早期版本并不支持该语法。

    端口赋值形式

    实例化时实例端口的赋值形式有多种,当然,最常用,最典型也是最推荐的就是映射赋值,不过除此以外,端口赋值还有多种形式,列举如下供大家了解:
    一、映射赋值。例如:
    wire a, b, c;
    myAnd insAnd (.in0 (a), .in1(b), .out©);

    二、位置赋值。例如:
    wire a, b, c;
    myAnd insAnd (a, b, c);

    三、部分赋值。这是由于有些模块在使用时并不是所有端口都需要的,若上例中的端口b是可以不使用的,那么按照映射赋值可以写成:
    myAnd insAnd (.in0 (a), .out©);
    而按照位置赋值必须写成:
    myAnd insAnd (a, , c);
    注意其中的多余的那个逗号,是用来占位用的,有了它,后面的c变量来能正确对应到out端口。

    四、常数赋值。例如:
    wire a, c;
    myAnd insAnd (.in0 (a), .in1(1’b1), .out©);
    注意,常数只能用于实例的输入端口。

    五、表达式赋值。例如:
    wire a, b, c, d;
    myAnd insAnd (.in0 (a&d), .in1(~b), .out©);
    不过不建议这样做,因为不太符合规范。

    Verilog生成语句

    Verilog中的生成语句主要使用generate语法关键字,按照形式主要分为循环生成与条件生成,分别介绍如下:

    循环生成

    循环生成的主要目的是简化我们的代码书写,利用循环生成语句我们可以将之前需要写很多条比较相似的语句才能实现的功能用很简短的循环生成语句来代替。基本语法如下:
    genvar ;
    generate
    for (=0; < ; =+1)
    begin:

    input [3:0] a,b;
    output [3:0] c,d;
    
    generate
    genvar i;
    	for (i=0; i < 4; i=i+1) 
    	begin : genExample
    		myAnd insAnd (.a(a[i]), .b(b[i]), .c(c[i]));
    		assign d[i] = a[i];
    	end
    endgenerate
    

    注意,利用循环生成语句生成的实例名称不能像数组例化那样用方括号表示,否则会报错。那么,你可能会疑惑上例中实例的名字,其实,上述实例化展开来类似:

    myAnd genExample(0).insAnd (.a(a[0]), .b(b[0]), .c(c[0]));
    myAnd genExample(1).insAnd (.a(a[1]), .b(b[1]), .c(c[1]));
    myAnd genExample(2).insAnd (.a(a[2]), .b(b[2]), .c(c[2]));
    myAnd genExample(3).insAnd (.a(a[3]), .b(b[3]), .c(c[3]));
    

    这也是为什么循环生成语句必须要有个名字。从上例我们还可以看出,当循环语句用作实例化时,所表述的功能跟数组实例化语句其实是类似的。
    最后,循环生成语句是支持嵌套的,例如二重循环生成语法如下:

    	genvar <var1>, <var2>;
    generate
           for (<var1>=0; <var1> < <limit>; <var1>=<var1>+1) 
           begin: <label_1>
              for (<var2>=0; <var2> < <limit>; <var2>=<var2>+1) 
              begin: <label_2>
                 <code>
              end
           end
    endgenerate	
    

    条件生成

    条件生成的目的是为了左右编译器的行为,类似于C语言中的条件选择宏定义,根据一些初始参数来决定载入哪部分代码来进行编译。Verilog中共提供了两种条件生成语句,一种是generate-if语句,一种是generate-case语句,两者的功能几乎相同,只是书写形式不一样而已,分别介绍如下:

    generate-if语句

    该语句的语法如下:

    generate
    	if (<condition>) begin: <label_1>
    		<code>;
    	end else if (<condition>) begin: <label_2>
    		<code>;
    	end else begin: <label_3>
    		<code>;
    	end
    endgenerate
    

    关于该语法有三点注意:
    1、必须是常量比较,例如一些参数,这样编译器才可以在编译前确定需要使用的代码;
    2、if语句的内容中,begin-end只有在有多条语句时才是必须的;
    3、每一个条件分支的名称是可选的,这点不像循环生成语句那么严格。
    关于generate-if语句,举例如下:

    wire c, d0, d1, d2;
    parameter sel = 1;
    
    generate
    	if (sel == 0)
    		assign c = d0;
    	else if (sel == 1)
    		assign c = d1;
    	else
    		assign c = d2;
    endgenerate
    

    该例子表示编译器会根据参数sel的值,来确定到底是让d0~d2中哪个变量和c连通。但是注意,一旦连通,那么要想更改必须修改参数后重新编译,如果需要动态选择,可以写成如下这样,但是资源上却都需要一个多路复用器来实现。
    assign c = (sel == 0) ? d0 : (sel == 1) ? d1 : d2;

    generate-case语句

    该语句的语法如下:

    	generate
    		case (<constant_expression>)
    			<value>: begin: <label_1>
    				<code>
    				end
    			<value>: begin: <label_2>
    <code>
    				end
    			……
    			default: begin: <label_N>
    <code>
    end
          		endcase
       	endgenerate
    

    关于该语法也有三点注意,和generate-if类似:
    1、<constant_expression>必须是常量比较,例如一些参数,这样编译器才可以在编译前确定需要使用的代码;
    2、case语句的内容中,begin-end只有在有多条语句时才是必须的;
    3、每一个条件分支的名称是可选的,这点不像循环生成语句那么严格。
    关于generate-case语句,举例如下:

    wire c, d0, d1, d2;
    parameter sel = 1;
    
    generate
    	case (sel)
    		0 :
    			assign c = d0;
    		1: 
    			assign c = d1;
    		default: 
    			assign c = d2;
    	endcase
    endgenerate
    

    该例所描述的功能和generate-if小节的例子是一摸一样的。

    Verilog函数调用语句

    函数的定义可以放在模块实现部分中的声明部分,使用function语法定义如下:

    function  [<lower>:<upper>] <output_name> ;
    input <name>;
    <other inputs>
    <variable declarations>
    begin
    <statements>
    end
    endfunction
    

    关于函数的定义有以下几点说明:
    1、<output_name>既是输出的变量名也是函数调用名,它的位宽由function关键字后面的范围指定;
    2、中,只能够声明寄存器类型的变量;
    3、函数体中语句只能使用阻塞赋值符号;
    4、函数调用的时候只能使用位置赋值,因此需要严格按照input的顺序罗列变量;
    5、函数调用可以用在并行语句中也可以用在串行语句中;
    6、函数中可以调用别的函数;
    7、函数支持递归,不过此情况一般用于仿真;
    8、函数不能调用任务,这主要是由于任务中可以有定时相关语句,而函数中不能够有。

    举例如下:
    示例一:几种常用函数调用方法;

    module tft(input clk, a, b, c, output reg d,e); 
    function  andFunc ;
    input a;
    input b;
    begin
    andFunc = a & b;
    end
    endfunction
    
    always@(posedge clk)
    begin
    	e <= andFunc(a, b); // called by no-blocking assignment
    end
    
    always@*
    begin
    		d = andFunc(a, b); // called by blocking assignment
    end
    
    assign c = andFunc(a, b); // called by continuous assignment
    endmodule
    

    示例二:函数调用函数

    module(input a,b, output c); 
    function  bufFunc ;
    		input a;
    		begin
    			bufFunc = a;
    		end
    endfunction
    function  andFunc ;
    		input a;
    		input b;
    		reg t;
    		begin
    			andFunc = bufFunc(a) & b;
    		end
    endfunction
    assign c = andFunc(a,b);
    endmodule
    

    示例三:函数递归

    function [3:0] addFunc ;
    	input [3:0] a;
    	reg [3:0] t;
    	begin
    		if(a == 4'b0)
    			addFunc = 1'b0;
    		else begin
    			t = a - 1'b1;
    			addFunc = a + addFunc(t);
    		end
    	end
    endfunction
    

    最后,需要提醒大家注意的是,函数的抽象级别比较高,它的编程思路更像是软件而不是硬件,因此一般多用于仿真时使用,具体设计FPGA时,如果需要重复使用某一个功能,完全可以通过模块实例化的方式来实现。

    Verilog模块说明语句

    模块说明语句的关键字是specify,它主要用来说明模块的一些时延信息。它的语法如下:
    specify
    <specparam_declarations> //一些参数定义
    <timing_constraint_checks> //设置一些时序检查选项
    <simple_pin-to-pin_path_delay> //设置模块中组合逻辑管脚到管脚的时间延迟
    <edge-sensitive_pin-to-pin_path_delay> //设置模块中时序逻辑时钟相关的时间延迟
    <state-dependent_pin-to-pin_path_delay> //条件延迟语句,类似条件生成语句
    endspecify
    一个简单的例子如下:
    specify
      specparam d_to_q =9;
      specparam clk_to_q =11;
      (d=>q) = d_to_q;
      (clk=>q) = clk_to_q;
    endspecify
    一般来说,各个FPGA厂商一般会针对自己的根据硬件相关的一些原语编写specify,这样我们才能够对我们的设计进行时序仿真或者时序分析,因此基本上我们不需要在自己设计的模块中编写specify。所以本小节仅对模块说明语句进行一些简单介绍,让大家对specify有个概念,做个了解即可。

    Verilog的串行语句

    串行语句的执行思路是顺序执行的,一般高级编程语言中的语句执行方式都是顺序执行的,例如c语言,由此可见,顺序执行的语句更容易帮助我们来表达我们的设计思想,尤其是使描述时序逻辑变得容易。所以,虽然FPGA的设计思路都是并行的,module中仅支持并行语句的调用,但是为了方便设计者表达自己的思想,尤其是表达时序逻辑的思想,Verilog中的一些并行语句中的子语句体允许是顺序执行的,例如always。那么到底Verilog语言里面有哪些串行语句可以供我们使用呢?以always为例描述如下:

    always@(...)
    begin
    		<Verilog阻塞赋值语句>;
    		<Verilog非阻塞赋值语句>;
    		<Verilog条件语句>;
    		<Verilog循环语句>;
    		<Verilog等待语句>;
    		<Verilog函数调用语句>;
    		<Verilog任务调用语句>;
    end
    

    Verilog阻塞赋值语句

    使用阻塞赋值操作符对变量进行赋值的语句叫阻塞赋值语句。一般来说,如果你认为你描述的这个变量在FPGA硬件中对应连线,那么你就应该使用阻塞赋值语句。使用阻塞赋值符号的赋值语句,一定要等到赋值行为结束之后才会开始执行下一条程序,因此阻塞赋值语句的书写顺序改变会引起综合或者仿真的问题。举例如下:
    always@(c, d) begin
    b = c & d;
    a = ~ b;
    end
    若赋值语句顺序颠倒会引起仿真的问题。

    Verilog非阻塞赋值语句

    使用非阻塞赋值操作符对变量进行赋值的语句叫非阻塞赋值语句。一般来说,如果你认为你描述的这个变量在FPGA硬件中对应寄存器等存储记忆单元,那么你就应该使用非阻塞赋值语句。使用非阻塞赋值符号的赋值语句,在赋值行为未完成之前就会开始执行下一条程序,也正是因为如此,所以非阻塞赋值语句的书写顺序是无所谓的。举例如下:
    always@(posedge clk) begin
    b <= c & d;
    a <= ~ b;
    end
    赋值语句顺序颠倒无所谓。
    如果无视变量对应的硬件结构而乱用赋值符号的话,会造成非常大的隐患。

    Verilog条件语句

    条件语句是一种典型的串行语句。Verilog中有两类条件语句——带优先级条件语句和无优先级条件语句。其中优先级条件语中的各个条件分支是具有优先级的,且分支优先级按照书写顺序从高至低,代表为if条件语句;而无优先级条件语句中,各个分支间的地位是等同的,代表为case条件语句。除了if和case语句外,Verilog还支持casex和casez两种衍生的无优先级条件语句,分别介绍如下:

    if条件语句

    if条件语句的完全语法如下:

    if (<condition>) begin
    <statement>;
    end
    else if (<condition>) begin
    <statement>;
    end
    else begin
    <statement>;
    end
    

    其中的 else if和else分支都不是必须的,可以根据具体情况来确定。以求A、B、C三者中的最大值为例描述如下:

    if (A >= B and A >= C)
    	max = A;
    else if (B >= C)
    	max<= B;
    else
    	max <= C;
    

    为什么说if条件语句是具有优先级的条件语句呢?需要从两个方面来说:
    第一,从语句的功能描述来分析。如果要描述上述求最大值的例子,我们可以这样翻译代码:首先,判断数A是不是大于等于B和C,如果成立,则最大值是A,结束判断;否则说明A不是最大值,那么这时候只需判断数B是不是大于等于C,如果成立,则最大值是B,判断结束;否则,由于之前已经得出A、B两数都不是最大值,那么最大值只能是C了。由此可见,每一个分支的判断都是建立在写在它之前的所有分支的基础上的。
    第二,从硬件实现上来说。上述求最大值的例子,对应到门级电路上,肯定是从A到max之间的路径最短,即所经过的逻辑门最少,而从B到max之间的路径次之,从C到max之间的路径最长。关于门级实现可以参考如下示意图:
    在这里插入图片描述
    由此可见,基于优先级条件语句的特点,如果我们知道A、B、C三个数中最大值的概率是B大于C大于A,那么我们应该把对B的判断放在第一个分支,然后C放在第二个分支,而A放在最后一个分支。这样,今后的仿真效率会更高,且对于具体的FPGA实现,也能保证最短路径得到最充分的利用,这样芯片即使工作在比较恶劣的环境下,也能保证故障率达到最低。

    case条件语句

    case条件语句的完全语法如下:

    case (<expression>)
    	<constant-value1> : 
    begin
    			<statements>;
    		end
    	<constant-value2> : 
    begin
    			<statements>;
    		end
    	<other branchs>	
    	default :
    begin
    			<statements>;
    		end
    endcase
    其中,<constant-value>的值必须互相不同,以四选一多路选择器为例描述如下:
    case (sel)
    2’b00 : data = channel0; 
    2’b01 : data = channel1; 
    2’b10 : data = channel2;
    2’b11 : data = channel3;
    default : data = channel0;
    endcase
    

    上述例子中的分支已经覆盖完全,但是还是有一个default分支,这虽然有些画蛇添足,但确是一个编程的好习惯,请大家注意!
    为什么说case条件语句是无优先级的条件语句呢?也需要从两方面来说:
    第一,从语句的功能描述来分析。如果要描述上述多路选择器的例子,我们可以这样翻译代码:如果sel等于2’b00,那么选择第一路输出;如果sel等于2’b01,那么选择第二路输出;如果sel等于2’b10,那么选择第三路输出;如果sel等于2’b11那么选择第四路输出。可见这四个分支的判断之间没有任何相互关系,也互不为前提。
    第二,从硬件实现上来说。上述多路复用器的例子,对应到门级电路上,无论是channel0~3中的任何一个,到data的路径都是等长的。关于门级实现可以参考如下示意图:
    在这里插入图片描述
    由此可见,在使用无优先级条件语句时,分支的顺序是无关的,不会影响电路的最终实现。

    if与case的对比

    为了进一步说明优先级条件语句与非优先级条件语句之间的区别,我们用if条件语句重写上节中四选一多路选择器的例子如下;

    	if(sel == 2'b00)
    		data = channel0;
    	else if(sel == 2'b01)
    		data = channel1;
    else if (sel == 2'b10)
    		data = channel2;
    	else
    		data = channel3;
    

    关于其门级实现可参考如下电路图:
    在这里插入图片描述
    可见,此时,channel0~3到data的路径长度就不一致了,最短的为一个两输入复用器延迟,最大的为3个两输入复用器延迟。当然,由于上图并不是最简形式,所以此处我们没必要深究它与【case条件语句】小节中的例子到底孰优孰劣,但是请注意,由于目前的编译器都会对我们的代码有一定优化作用,因此有时候if和case也可能会综合成为一样的电路。

    case语句中的判断表达式有可能出现的情况比较多,但是分支却有可能没有那么多,因此下面介绍一些case的变形写法,能够更加方便我们去描述电路。

    case语句的一些变形

    首先,利用特殊的“或”符号——“,”来简化代码,例如,要构建一个三输入的多路复用器,可以描述如下(当然,这并不是最优描述形式):

    case (sel)
    2’b00 : data = channel0; 
    2’b01 : data = channel1; 
    2’b10 ,
    2’b11 : data = channel2;
    default : data = channel0;
    endcase
    

    其次,case的常量和表达式还可以互换位置,例如

    reg sel;
    case (2'b00)
    sel : data = channel0; 
    default : data = channel1;
    endcase
    

    case、casex与casez

    在Verilog语法中,case的比较是十分高效的,但它的匹配成功要求所有位上的逻辑值必须精确相等。于是,Verilog又提供了casex与casez两种语法结构作为补充,它们和case的语法结构相同,只不过分别以casex和casez开头而已。这样,在比较的时候就可以引入不关心位,从而能够达到简化代码的效果。在【本篇->编程语法->Verilog基本语法->Verilog数据类型->Verilog四值逻辑系统】小节,我们介绍了Verilog中的四种逻辑形式:0、1、X、Z,那么,对于casex来说,它会将X、Z视为“不关心位”;而对于casez来说,它会将Z视为“不关心位”。
    在Verilog中,我们可以用“?”来表示“不关心位”,讨论如下:

    条件表达式中有“不关心位”

    举例说明如下:
    reg a;
    case (a)
    1’b0 : statement1;
    1’b1 : statement2;
    1’bx : statement3;
    1’bz : statement4;
    endcase
    上例中,若a = 1’b0或1’b1,那么statement1或statement2将会执行;若我们令a = ?,那么statement4将会执行,因为语法认为“?”等于Z状态。

    reg a;
    casez (a)
    1’b0 : statement1;
    1’b1 : statement2;
    1’bx : statement3;
    1’bz : statement4;
    endcase
    上例中,若a = 1’b0、1’b1或1’bx,那么statement1、statement2或statement3将会执行;但是若a = ?或者1’bz,那么statement1会执行,因为此时casez将这两种值视为无关状态,会直接执行第一条语句,所以statement4永远得不到执行。

    reg a;
    casex (a)
    1’b0 : statement1;
    1’b1 : statement2;
    1’bx : statement3;
    1’bz : statement4;
    endcase
    上例中,若a = 1’b0或1’b1,那么statement1或statement2将会执行;但是若a = ?或者1’bx、1’bz,那么statement1会执行,因为此时casex将这三种值视为无关状态,会直接执行第一条语句,所以statement3、statement4永远得不到执行。

    常数项中有“不关心位”

    举例如下:
    case (sel)
    3’b1?? : data0 = channel0;
    3’b01? : data0 = channel1;
    default : data0 = channel2;
    endcase
    由于case要求精确的匹配,所以无论当sel是什么情况,都只能执行default语句,因此data0只能取到channel2的值。

    casex (sel)
    	3'b1z? : data1 = channel0; 
    	3'b01x : data1 = channel1; 
    	default : data1 = channel2;
    

    endcase
    由于casex将?、X、Z均视为“不关心位”,因此,sel从3’b1003’b111都能匹配3’b1z?,而sel从3’b0103’b011都能匹配3’b01x,而3’b000、3’b001什么都不匹配,因此data1可以取到channel0、channel1和channel2的所有值。

    casez (sel)
    	3'b1z? : data2 = channel0; 
    	3'b01x : data2 = channel1; 
    	default : data2 = channel2;
    

    endcase
    由于casez将?、Z均视为“不关心位”,因此,sel从3’b1003’b111都能匹配3’b1z?,而sel从3’b0103’b011却不能匹配3’b01x,再加上3’b000、3’b001也什么都不匹配,因此data2可以取到channel0和channel2的值,却没有办法通过匹配获得channel1的值。
    上述几个例子如果写在一个module中,我们可以通过其综合后的电路来更加形象的理解:

    要想用case来实现上例casex实现的优先级译码功能,最优的情况下可以写成这样:
    case (sel)
    3’b001 : data0 = channel2;
    3’b010,
    3’b011 : data0 = channel1;
    default : data0 = channel0;
    endcase

    最后,需要说明的一点是,casex和casez中,可以通过使用不关心位来实现代码的简化或一些特殊的逻辑功能,例如优先级译码器。但是在其他情况下请避免使用,因为casex和casez的很多用法都只能停留在仿真阶段。

    Verilog循环语句

    Verilog中的循环语句有很多种,包括for循环、while循环、repeat循环以及forever循环等。这些循环语法中除了for循环有时候可以用来帮助我们简化一些代码的编写外,基本都是主要用于仿真激励的设计,因此本小节主要介绍一下Verilog中的for循环,剩下的将会在【功能仿真篇->仿真语法->Verilog Test Fixture】章节中介绍。
    for循环的语法为:
    integer ; //递减
    for ( = <initial_value>; >= <final_value>; =-1) begin
    ;
    end
    或者
    integer ; //递增
    for ( = <initial_value>; <= <final_value>; =+1) begin
    ;
    end
    例如,如果我们要将一个矢量信号高低位颠倒的赋给另一个矢量信号,可以用for循环简便的表述如下:
    integer i;
    for (i = 7; i >= 0; i=i+1) begin
    a[i] <= b[7-i];
    end
    注意,在描述设计时,for循环一般不应该进行功能描述,而应该只进行结构描述。否则,由于for循环抽象级别比较高,编译器不一定能够正确给对应的实现电路,而且有时候很可能就不存在能够对应该功能的电路。

    Verilog等待语句

    Verilog中有三种等待语句,分别介绍如下:

    事件等待

    事件等待的语法如下:
    @( or or … )
    每个always程序块中都必有一个事件等待语法,除此以外,事件等待语法还可以位于always程序块中,此时的always程序块主要是用于仿真。

    直接时间等待

    直接时间等待的语法如下:
    #

    表达式等待语句

    表达式等待语句的语法如下:
    wait ();
    当为真的时候程序才往下执行,它也主要用于仿真。

    由于等待语句主要用于仿真结构中,所以详情请参阅【功能仿真篇->仿真语法->Verilog Test Fixture】小节。

    Verilog函数调用语句

    函数调用语句即可以用在并行语句中,也可以用在串行语句中。

    Verilog任务调用语句

    任务即是task,它的语法如下:
    task <task_name>;
    input <input_name>;
    <more_inputs>
    output <output_name>;
    <more_outputs>

    begin
    ;
    end
    endtask
    关于任务调用有以下几点说明:
    1、任务中有输入端口也有输出端口,所以它的调用是通过输入端口将数据传入任务中,然后从输出端口得到结果,所以任务可以同时有多个输出,这点与函数不同;
    2、中,只能够声明寄存器类型的变量;
    3、任务中可以使用阻塞赋值也可以使用非阻塞赋值,具体要看调用任务的always是在描述时序逻辑还是组合逻辑;
    4、任务调用的时候只能使用位置赋值,因此需要严格按照端口的顺序罗列变量;
    5、任务调用只能用在串行语句中;
    6、任务中可以调用别的任务;
    7、任务支持递归,不过此情况一般用于仿真;
    8、任务中可以调用函数。

    举例如下:
    示例一:任务中的阻塞赋值与非阻塞赋值
    task bufTask;
    input a;
    output b;
    begin
    b = a;
    end
    endtask
    task regTask;
    input a;
    output reg b;
    begin
    b <= a;
    end
    endtask
    always@*
    bufTask(a,b);
    always@(posedge clk)
    regTask(a,b);
    示例二:任务调用任务
    task andTask;
    input a,b;
    output c;

    	reg t;
    	begin
    		t = bufFunc(a);
    		c = t & b;
    	end
    endtask
    示例三:任务递归
    

    task addTask ;
    input [3:0] a;
    output [3:0] b;
    reg [3:0] t,h;
    begin
    if(a == 4’b0)
    b = 4’b0;
    else begin
    t = a - 1’b1;
    addTask(t, h);
    b = h + 1’b1;
    end
    end
    endtask
    最后,需要提醒大家注意的是,任务和一般也多用于仿真时使用,虽然任务的描述跟模块有些类似,但是具体在设计FPGA时,如果需要重复使用某一个功能,完全可以通过模块实例化的方式来实现。

    展开全文
  • 前言:最近再搞个PCIe的高速串行总线的项目,关于串行并行数据的传输到底谁更快,分别有哪些优势和劣势呢等等一些问题现在做个整理,是对自己学习的一个记录,同时呢,也为刚刚想入门的或者对高速串行总线有些误解...

    前言:最近再搞个PCIe的高速串行总线的项目,关于串行和并行数据的传输到底谁更快,分别有哪些优势和劣势呢等等一些问题现在做个整理,是对自己学习的一个记录,同时呢,也为刚刚想入门的或者对高速串行总线有些误解的(暂且说误解把 词穷呀 最近在看主持人大赛2019,,,,,,,,,汗颜),我下面首先从总线的技术方面说起,当然只是浅显的说一下,深了咱也不会
    其次,PCIE的概念
    最后,进行PCIe的数据传输实验

    1 并行技术的简介

    1 并行传输就是将Nbit数据以成组的形式同时(在一个时钟周期下)在不同的线路上传输。分组一般8bit 16bit等

    那么为了数据能够正确的被接收端接收,每组(8bit或者16bit或者更大的数据位)数据必须在时钟上进行同步(源同步和系统同步),
    源同步:
    在这里插入图片描述
    发送端产生的数据和时钟保持一定的相位关系(clk和data都在发送端产生),并同时传输到接收端,接收端使用发送端的时钟作为采样时钟,对数据进行采样。在采样的过程中,只要保证接收端clk和data满足一定的建立时间/保持时间。数据一定能够正确接收。

    2 系统同步

    传统的数据传输通常采用系统同步的方式进行传输,多个期间时使用同一个时钟进行系统同步,器件之间的数据传输时序关系以系统时钟作为参考,如图,这种方式使各个器件处于同步工作模式,但是器件之间的时延难以确定,当系统时钟的频率增加之后,数据接收接口同步电路难以实现,因此不适合高速数据传输。
    在这里插入图片描述
    上述两种方式,PCB Layout时数据线总是成组并排布线,线间距小,线之间存在较大的耦合,导致串扰问题严重,除了数据线和时钟线之外,并行总线还有很多的控制线,走线数目多且一般是单端信号传输,抗干扰能力差,数据不能同时到达,因此并行传输的时钟频率很难达到1GHz。
    并行总线常用的接口找个图了解一下:在这里插入图片描述

    2 串行传输技术简介

    1 串行传输的原理

    串行传输指的是发送方和接收方直接的数据在单根通信线上逐位顺序传送,每一位数据都占据一个固定时间,示意图如下,,时钟频率相同,并行当然比串行要快的多,这个可以理解吧,在低速情况下,并行结构绝对是一种非常高效的传输方式,但是当传输速度非常高,并行传输的致命性缺点就出现了。因为时钟在高速的情况下,因为每一位在传输线路上不可能严格的一致,并行传输的一个字节中的每个位不会同时到达接受端就被放大了。而串行传输一位一位传输就不会出现这个问题。串行的优势就出现了,串行因为不存在并行的这些问题,就可以工作在非常高的频率下,用频率的提升掩盖它的劣势。
    在这里插入图片描述在串行传输中,发送端和接收设备必须达到bit同步,发送设备和接收设备必须以同步时钟(这个是频率可以很大,不存在数据不能同时到达的问题,人家就1bit嘛)发送和接收数据必须以同步时钟发送和接收数据,在接收端才能正确的接收数据。衡量串行串行传输通道的频宽的重要指标是波特率,数据的个数/单位时间,举个例子把,每秒传输240个字符(1个起始位。1个停止位,8个数据位),波特率:240Bd,比特率=240*10=2400b/s,对串行接口,必须设置正确的波特率,不然 你懂的(是不是想到了串口,那就对了)

    也列举几个串行接口协议的常用接口
    串口和USB及IEEE1394

    通过上面的讨论,简单介绍一下高速并行传输的瓶颈问题(也是抄来的 将就着看一下把)
    1 时序同步问题
    2 信号偏移
    3 抗干扰能力
    4设计复杂度和设计成本(线多呀!!!!!!!!)

    串行传输的优势:
    1 传输频率的提高是最大的优势,串行通信在发送端通过数据编码技术把时钟信号嵌入到数据信号中,在接收端使用时钟恢复技术,从数据中提取同步时钟信号来恢复数据,消除了传输线延迟导致的时序问题对速率的约束,实现单通道的数据传输速率10Gb/s以上。
    2 使用差分信号,抑制共模噪声产生的影响,在相同驱动电压的驱动下,差分信号的摆幅是单端信号的两倍,那么发送端就能以更低的电压发送信号,不但降低了功耗并且还抑制了电磁干扰的影响,减少对外的电磁辐射,此外,此外,此外呀,差分信号模式一般采用恒定的电流驱动,这将保证驱动器电路在任何时刻保持不变,消除了信号同步翻转而产生的同步开关噪声问题哦。
    3 线少了 你懂的的
    4 使用点对点连接方式,避免并行总线的共享机制造成的总线利用率低的问题,通信链路利用率达到100%(听起来是不是很牛b),而且在点对点拓扑情况下,阻抗的匹配端接也非常简单,一般高速串行收发器都自带匹配,实现双端端接,进一步降低反射。

    娘妈的 抄的手疼 贴个图

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    多重相位技术

    这个多啰嗦几句 我也是刚学,如有错误,请指出

    多重相位技术是实现高速串行I/O的通信技术之一,多重相位在时钟的不同相位对串行的数据流进行采样,将串行数据流转化为并行数据输出,从而实现 低速时钟处理高速时钟的的数据,如下示意图,由锁相环的多相时钟产生器输出4个相时钟信号,即在0°,90°,180°,270°上采样数据,最后由零相位时钟信号同步并行输出。采用多重相位提取电路。若输入的串行数据流比特率位x,则并行输出的数据流比特率为x / 4。
    在这里插入图片描述在这里插入图片描述

    线路编码技术

    在这里插入图片描述
    https://wenku.baidu.com/view/c3280ec0b9f3f90f76c61b38.html参看一下

    扰码传输技术

    发送预加重技术

    接收均衡技术

    这几个技术以后用到再补充吧。。。。。。。。。。。。。。。。

    展开全文
  • LFSR主要用于通信加扰解...线性反馈移位寄存器(LFSR)是内测试电路中最基本的标准模块结构,既用作伪随机测试码产生器,也作为压缩测试结果数据的特征分析器。 一个n阶的LFSR由n个触发器和若干个异或门组成。在实际...
  • 并行传输VS串行传输

    千次阅读 2013-06-21 10:45:31
    众人拾柴火焰高”是句老话,但电脑领域却发生了...从技术发展的情况来看,串行传输方式大有彻底取代并行传输方式的势头,USB取代IEEE 1284,SATA取代PATA,PCI Express取代PCI……从原理来看,并行传输方式其实优于串行
  • 串行通信中,数据是二进制脉冲的形式。换句话说,我们可以说二进制1表示逻辑高电平或5伏特,零表示逻辑低电平或0伏特...它们是串行通信和并行通信。串行通信是一种使用双线即发送器(发送器)和接收器逐位发送数据的...
  • 它们经常出现在单片机系统中,用来扩充并行输出接口。用 74HC595 驱动点阵显示屏可见:http://hi.baidu.com/%D7%F6%B6%F8%C2%DB%B5%C0/blog/item/e4d7db39c77e9f35b9998f26.html看过一些资料,送往74HC595的串行信号...
  • 8251A 并行传送和串行传送

    千次阅读 2018-10-01 08:41:32
    20. 8251A 芯片中实现并行数据转换为串行数据的部件 一、数据传送 并行传送 数据在多条并行 1 位宽的传输线上同时由源传送到目的。以 1 字节为例,在并行传送中, 1 字节数据通过 8 条并行传输线同时由源传送到目的...
  • 为什么USB要用串行通信而不是用并行呢?并行接口速度比串行接口速度快,这是若干年前的情况了。 在实际时钟频率比较低的情况下,并口因为可以同时传输若干比特,速率确实比串口快。 但是,随着技术的发展,时钟频率...
  • 在一些电路中,我们需要对很多器件进行控制,但是...在这种情况下,采用串行并行芯片是一个很好的选择,通过串行的数据输入实现对并行器件的控制。 74HC595是一个典型的串行并行芯片,它的实物如下图所示 ...
  • 在微机控制系统中,为了实现对生产过程的控制,要将对象的各种...该连接通道被称为输入输出通道,它包括模拟量输入通道、模拟量输出通道、数字量输入通道和数字量输出通道,其组成如图1所示。自动控制网www.eadia...
  • 我们都知道通信从大的方面有两种:串行并行串行的最大优点是占用总线少,但是传输速率低;并行恰恰相反,占用总线多,传输速率高。市面上有很多这样的芯片,有串入并出的(通俗讲就是 一个一个进,最后一块出来...
  • 输入输出系统

    2019-10-03 13:47:48
    输入输出系统 输入输出系统是计算机系统中的主机与外部进行通信的系统。它由外围设备和输入输出控制系统两部分组成,是计算机系统的重要组成部分。外围设备包括输入设备、输出设备和磁盘存储器、磁带存储器、光盘...
  • 总线是一种内部结构,它是cpu、内存、输入输出设备传递信息的公用通道,主机的各个部件通过总线相连接,外部设备通过相应的接口电路再与总线相连接,从而形成了计算机硬件系统。 在计算机系统中,各个部件之间...
  • 总线揭密 串行传输VS并行传输

    千次阅读 2018-11-15 09:10:27
    总线揭密 串行传输VS并行传输
  • 串行总线技术(一)-串行总线结构(以PCIe为例)串行总线的出现在早期的计算机系统中,多数外围设备使用并行总线结构。这些总线包括PCI和PATA(并行ATA)。当通信速率较低时,并行总线结...
  • 关于串行通信与并行通信的一些介绍
  • 学习目标:重点分析I/O设备与主机交换信息的三种控制方式(程序查询、中断和DMA)及其相应的接口功能和组成,对输入输出系统有一个较清晰的认识,加深对整机工作的理解。 5.1.1 输入输出系统的发展概况 输入输出系统的...
  • 为什么说串行并行

    万次阅读 多人点赞 2011-11-12 21:48:45
    并行接口速度比串行接口速度快,这是若干年前的情况了。 在实际时钟频率比较低的情况下,并口因为可以同时传输若干比特,速率确实比串口快。 但是,随着技术的发展,时钟频率越来越高,并行导线之间的相互干扰越来越...
  • 并行接口速度比串行接口速度快,这是若干年前的情况了。 在实际时钟频率比较低的情况下,并口因为可以同时传输若干比特,速率确实比串口快。 但是,随着技术的发展,时钟频率越来越高,并行导线之间的相互干扰...
  • 为什么说串行并行速度快?

    千次阅读 2018-09-20 22:15:07
    并行接口速度比串行接口速度快,这是若干年前的情况了。 在实际时钟频率比较低的情况下,并口因为可以同时传输若干比特,速率确实比串口快。 但是,随着技术的发展,时钟频率越来越高,并行导线之间的相互干扰越来越...
  • 概述:并行接口速度比串行接口速度快,这是若干年前的情况了。在实际时钟频率比较低的情况下,并口因为可以同时传输若干比特,速率确实比串口快。但是,随着技术的发展,时钟频率越来越高,并行导线之间的相互干扰...
  • 输入/输出接口电路(I/O接口,也称适配器):主机对性能各异的外设进行控制,与它们交换信息,在主机与外设间设置一组电路界面,将CPU系统总线发出的控制信号、数据信号和地址信号转换成外设所能识别和执行的具体...
  • 并行除法器 ,并行除法器结构原理是什么?1.可控加法/减法(CAS)单元和阵列乘法器非常相似,阵列式除法器也是一种并行运算部件,采用大规模集成电路制造。与早期的串行除法器相比,阵列除法器不仅所需的控制线路少,...
  • 总线揭密:串行传输VS并行传输

    万次阅读 多人点赞 2011-10-30 15:17:10
    从技术发展的情况来看,串行传输方式大有彻底取代并行传输方式的势头,USB取代 IEEE 1284,SATA取代PATA,PCI Express取代PCI……  从原理来看,并行传输方式其实优于串行传输方式。通俗地讲,并行传输的通路...
  • 输入/输出系统

    2015-07-18 12:35:09
    接口电路作用:CPU通过接口电路与外部设备进行信息交换 接口电路需要具备的功能: 1. 数据缓冲功能 (接口电路设有缓冲器或锁存器,已... 数据转换功能 (CPU处理的是并行数据,而有些外设只能进行串行处理,这时接
  • CPU、核心,进程、线程,串行、并发、并行

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,431
精华内容 3,372
关键字:

并行输入串行输出电路结构