精华内容
下载资源
问答
  • DDS原理的通俗解释

    千次阅读 2020-09-20 11:51:46
    DDS原理的通俗解释 DDS DDS是直接数字式频率合成器(Direct Digital Synthesizer)的英文缩写,是一项关键的数字化技术。与传统的频率合成器相比,DDS具有低成本、低功耗、高分辨率和快速转换时间等优点,广泛使用...

    DDS原理的通俗解释

    1. DDS
      DDS是直接数字式频率合成器(Direct Digital Synthesizer)的英文缩写,是一项关键的数字化技术。与传统的频率合成器相比,DDS具有低成本、低功耗、高分辨率和快速转换时间等优点,广泛使用在电信与电子仪器领域,是实现设备全数字化的一个关键技术。

    2. DDS原理
      2.1. 解释一
      随着数字技术在仪器仪表和通信系统中的广泛使用,可从参考频率源产生多个频率的数字控制方法诞生了,即直接数字频率合成(DDS)。其基本架构如图1所示。该简化模型采用一个稳定时钟来驱动存储正弦波(或其它任意波形)一个或多个整数周期的可编程只读存储器(PROM)。随着地址计数器逐步执行每个存储器位置,每个位置相应的信号数字幅度会驱动DAC,进而产生模拟输出信号。最终模拟输出信号的频谱纯度主要取决于DAC.相位噪声主要来自参考时钟。
      在这里插入图片描述

                                        图1 DDS原理图
      

      相位控制字可以控制初相。频率控制字控制每次相位的步进,进而控制产生信号的频率。
      系统的核心是相位累加器,其内容会在每个时钟周期更新。相位累加器每次更新时,存储在相位寄存器中的数字字M就会累加至相位寄存器中的数字。假设相位寄存器中的数字为00…01,相位累加器中的初始内容为00…00.相位累加器每个时钟周期都会按00…01更新。如果累加器为32位宽,则在相位累加器返回至00…00前需要232(超过40亿)个时钟周期,周期会不断重复。
      相位累加器的截断输出用作正弦(或余弦)查找表的地址。查找表中的每个地址均对应正弦波的从0°到360°的一个相位点。查找表包括一个完整正弦波周期的相应数字幅度信息。(实际上,只需要90°的数据,因为两个MSB(最高有效位,most significant bit,MSB)中包含了正交数据)。因此,查找表可将相位累加器的相位信息映射至数字幅度字,进而驱动DAC,图 2用图形化的“相位轮”显示了这一情况。
      考虑n = 32,M = 1的情况。相位累加器会逐步执行232个可能的输出中的每一个,直至溢出并重新开始。相应的输出正弦波频率等于输入时钟频率232分频。若M=2,相位累加器寄存器就会以两倍的速度“滚动”计算,输出频率也会增加一倍。以上内容可总结如下:
      在这里插入图片描述

                                     图 2数字相位轮
      

      n位相位累加器(大多数DDS系统中,n的范围通常为24至32)存在2n个可能的相位点。相位寄存器中的数字字M代表相位累加器每个时钟周期增加的数量。如果时钟频率为fc,则输出正弦波频率计算公式为:
      f 0 = M*fc/2^n
      该公式称为DDS“调谐公式”。注意,系统的频率分辨率等于fc/2n。n = 32时,分辨率超过40亿分之一!在实际DDS系统中,溢出相位寄存器的位不会进入查找表,而是会被截断,只留下前13至15个MSB。这样可以减小查找表的大小,而且不会影响频率分辨率。相位截断只会给最终输出增加少量可接受的相位噪声。(参见图 3)。
      在这里插入图片描述

      图 3计算得出的输出频谱显示15位相位截断时90 dB SFDR
      DAC的分辨率通常比查找表的宽度少2至4位。即便是完美的N位DAC,也会增加输出的量化噪声。图4显示的是32位相位累加器15位相位截断时计算得出的输出频谱。选择M值后,输出频率会从0.25倍时钟频率开始稍有偏移。注意,相位截断和有限DAC分辨率产生的杂散都至少比满量程输出低90 dB。这一性能远远超出了任何商用12位DAC,足以满足大多数应用的需求。
      显然,相位累加器的位宽 n 越大,频率分辨率就越高。为了完成相位到幅度的转换,转换器(ROM)是必不可少的。随着n 的增大,ROM 的大小是指数增加的,这将会耗用大量资源,有时甚至是不能实现的。在实际的 DDS 中,往往截断相位累加器的输出,取其高 p 位作为 ROM 的输入,则相位截断 B=n -p 。
      在这里插入图片描述

                                 图 4相位截断的DDS模型
      

      上述基本DDS系统极为灵活,且具有高分辨率。只需改变M寄存器的内容,频率就可以立即改变,不会出现相位不连续。但是,实际DDS系统首先需要执行串行或字节加载序列,以将新的频率字载入内部缓冲寄存器,然后再载入M寄存器。这样就可以尽可能减少封装引脚数。新的频率字载入缓冲寄存器后,并行输出相位寄存器就会同步操作,从而同时改变所有位。加载相位缓冲寄存器所需的时钟周期数决定了输出频率的最大改变速率。
      2.2. 解释二
      若对一正弦波形进行采样,每周期为 m 个采样点,分别记为 1~m。 对应每次参考时钟 f c , 输出一个采样点,输出图中所示的一个周期的正弦,需要 m 个时钟周期,则输出的波形频率为 f a =f c/m。对于这种情况, 每次时钟到来时,相位累加器加 1 ,则就会在第 i 个时钟周期输出 第 i 个采样点( i = 1~m) ,第 m + 1 个时钟输出第 1 个采样点,以此循坏,这时的相位累加器实际上是步进为 1 的模 m 计数器。 如果每次时钟到来时, 总是间隔一个采样点输出,即相位累加器的步进为2 ,这时在第 i 个周期输出第 2i 个采样点,输出波形如图 4波形 b,显然波 形 b 的频率是 a 的 2 倍,即 f b = 2 f a 。
      综上所述,如果相位累加器的步进为 B,则输出波形的频率为 B×f a ,f a 是最小的输出频率称为频率分辨率或步进间隔,B 为频率控制字。给定不同的频率控制字即可输出不同的频率。频率输出公式为: f 0 = fc/m* B。
      在这里插入图片描述

                                                     图 5输出波形
      

      在实际设计中,如果累加器长度为N ,则可以有2N 个存储单元存储采样数据,如果我们 对一个周期的波形进行2N 个点的采样,即m = 2N , 此时输出频率f o 和系统时钟频率f c,相位累加器长度N 以及频率控制字B的关系为:f0 = fc*B/2N 。为了使波形输出不失真,根据奈奎斯特定理,B最高为2 N - 1 。另外要提高DDS 的精度,就需要分母越大越好,即采样点的个数越多,越接近实际波形。但实际上不可能提供如此之多的存储空间,这就需要对采样点进行量化。如图 4(c)所示,如果量化单位为K,则前K 个点的值总是相同的,为采样值1 ,第二组K 个采样点的值为采样值2 ,以此类推,第i 组K 个采样点的值为采样值i ,共需要m/K个存储单元 来存储m/K个采样点。

    参考:
    [1]. http://m.elecfans.com/article/574959.html
    [2]. http://www.elecfans.com/news/dianzi/20171103574751.html
    [3]. https://www.sohu.com/a/204979496_467791
    [4]. https://www.bilibili.com/video/av99507362/
    [5]. 王顺岭. 基于FPGA的低杂散直接数字频率合成技术研究[D]. 电子科技大学.

    展开全文
  • FSMC原理通俗解释

    万次阅读 多人点赞 2019-04-01 00:48:00
    首先,FSMC是什么,FSMC我自己的理解就是,一个衔接CPU与外部存储的桥梁,它的功能呢就是你往相应的地址里写数据时候,你不需用软件来模拟外部存储芯片的读写时序,而只需配置好FSMC相关的时序寄存器,配置好相关...

    所以不用GPIO口直接驱动液晶,是因为这种方法速度太慢,而FSMC是用来外接各种存储芯片的,所以其数据通信速度是比普通GPIO口要快得多的。TFT-LCD 驱动芯片的读写时序和SRAM的差不多,所以就可以用FSMC四块中的SRAM块来驱动LCD。SRAM有数据线和地址线,所以FSMC跟它匹配同样也有数据线和地址线,而LCD数据线跟地址线共用,通信时用RS端来区分线上是数据还是指令,RS高是数据,RS低是指令。

    其实这中间一开始最不理解就是与LCD的RS引脚相关的东西,那时候也不知道咋回事一直以为FSMC的地址线和数据线是共用的,导致我一直没明白怎么回事,昨天一看正点原子的探索者原理图才发现FSMC有数据线还有地址线,是分开的。驱动液晶的时候,数据线接液晶的并口,RS接到了FSMC的某个地址引脚。具体为什么这样接,也是我想着重记录的。

    首先,FSMC是什么,FSMC我自己的理解就是,一个衔接CPU与外部存储的桥梁,它的功能呢就是你往相应的地址里写数据时候,你不需用软件来模拟外部存储芯片的读写时序,而只需配置好FSMC相关的时序寄存器,配置好相关寄存器之后,你只管往相应存储块中的地址里写数据就可以了。最直观的解释就看上边正点原子的一小段代码,代码中定义了一个结构体,然后强制性的把结构体的起始地址设在了0x6C000000 | 0x0000007E处。那么这个0x6C000000 | 0x0000007E地址是啥意思呢,其中0x6C000000是FSMC存储块1的区4起始地址,0x6C00007E为存储块1区4内的某个地址,你给LCD_REG赋值,就是往外部存储设备中的0x6C00007E地址处写数据,给LCD_RAM赋值,就是往0x6C00007F地址处写数据。这里是结构体,当然也可以是一个巨大的数组或其他数据类型。当然了,FSMC只是一个数据衔接部件,真正的存储芯片是需要通过引脚连接在FSMC的地址和数据引脚上的。存储芯片在指定地址写入数据的时候需要地址线来指定要写数据往哪个地址。也就是说上例中,往0x6C00007E中写数据的时候,地址线的低8位(0x7E)就是0111 1110。这就是FSMC的工作机制。

    说完FSMC,下面说如何用FSMC的某跟地址线控制LCD的RS端。以RS连接到A6为例,TFT-LCD的数据是16位的,即读写一次数据为两个字节按字节编址的话就是所有的地址都是偶地址,LCD的显示RAM编址确实是按两个字节编址的。这样的话FSMC地址线上的0000 0001就代表AHB(FSMC是接到AHB总线上的)总线上的0000 0010,FSMC地址线上的0000 0010就代表AHB总线上的0000 0100,也就是说你在程序中写这个地址的时候总是要左移一位。造成这种现象的原因就是因为在配置FSMC传输数据位宽时设置成了16位。向LCD传输数据一次都是两个字节。所以对应上例,往地址0x6C00007E写数据,地址线的低8位并不是0111 1110,而是0011 1111。然后假如RS端连接在地址线A6端,那么也就是让A6端的高低来决定数据线上传输的是数据还是命令。上边说了,当往0x6C00007E地址(LCD_REG)写的时候,FSMC地址线低8位是0011 1111,A6(红色标出)为低电平,这时数据线上的是命令,所以写命令函数就是直接往LCD_REG写就行了,实际上就是为了让RS接低电平,当往0x6C00007F地址(LCD_RAM)中写时,FSMC地址线低8位是01000000,A6(红色标出)为高电平,这时数据线上是数据,所以写数据就直接往LCD_RAM中写,实际是为了让RS接高电平。

    当然了,这里RS接的是A6,其他场合,RS可能接的是其他地址线,只需要根据上边的规律技巧来改变“0x6C000000 | 0x0000007E”中后者就可以了。这里这个RS的接法和用法很巧妙,摘自正点原子。

    作者:z1512751703
    来源:CSDN
    原文:https://blog.csdn.net/z1512751703/article/details/78937117
    版权声明:本文为博主原创文章,转载请附上博文链接!

    转载于:https://www.cnblogs.com/CodeWorkerLiMing/p/10634213.html

    展开全文
  • 通俗易懂和你聊聊寄存器那些事(精美图文)

    千次阅读 多人点赞 2020-10-15 06:53:38
    下面我们就来介绍一下关于寄存器的相关内容。我们知道,寄存器是 CPU 内部的构造,它主要用于信息的存储。除此之外,CPU 内部还有运算器,负责处理数据;控制器控制其他组件;外部总线连接 CPU 和各种部件,进行数据...

    我把自己以往的文章汇总成为了 Github ,欢迎各位大佬 star
    https://github.com/crisxuan/bestJavaer

    下面我们就来介绍一下关于寄存器的相关内容。我们知道,寄存器是 CPU 内部的构造,它主要用于信息的存储。除此之外,CPU 内部还有运算器,负责处理数据;控制器控制其他组件;外部总线连接 CPU 和各种部件,进行数据传输;内部总线负责 CPU 内部各种组件的数据处理。

    那么对于我们所了解的汇编语言来说,我们的主要关注点就是 寄存器

    为什么会出现寄存器?因为我们知道,程序在内存中装载,由 CPU 来运行,CPU 的主要职责就是用来处理数据。那么这个过程势必涉及到从存储器中读取和写入数据,因为它涉及通过控制总线发送数据请求并进入存储器存储单元,通过同一通道获取数据,这个过程非常的繁琐并且会涉及到大量的内存占用,而且有一些常用的内存页存在,其实是没有必要的,因此出现了寄存器,存储在 CPU 内部。

    认识寄存器

    寄存器的官方叫法有很多,Wiki 上面的叫法是 Processing Register, 也可以称为 CPU Register,计算机中经常有一个东西多种叫法的情况,反正你知道都说的是寄存器就可以了。

    认识寄存器之前,我们首先先来看一下 CPU 内部的构造。

    assembly02 001

    CPU 从逻辑上可以分为 3 个模块,分别是控制单元、运算单元和存储单元,这三部分由 CPU 内部总线连接起来。

    几乎所有的冯·诺伊曼型计算机的 CPU,其工作都可以分为5个阶段:取指令、指令译码、执行指令、访存取数、结果写回

    • 取指令阶段是将内存中的指令读取到 CPU 中寄存器的过程,程序寄存器用于存储下一条指令所在的地址
    • 指令译码阶段,在取指令完成后,立马进入指令译码阶段,在指令译码阶段,指令译码器按照预定的指令格式,对取回的指令进行拆分和解释,识别区分出不同的指令类别以及各种获取操作数的方法。
    • 执行指令阶段,译码完成后,就需要执行这一条指令了,此阶段的任务是完成指令所规定的各种操作,具体实现指令的功能。
    • 访问取数阶段,根据指令的需要,有可能需要从内存中提取数据,此阶段的任务是:根据指令地址码,得到操作数在主存中的地址,并从主存中读取该操作数用于运算。
    • 结果写回阶段,作为最后一个阶段,结果写回(Write Back,WB)阶段把执行指令阶段的运行结果数据写回到 CPU 的内部寄存器中,以便被后续的指令快速地存取;

    计算机架构中的寄存器

    寄存器是一块速度非常快的计算机内存,下面是现代计算机中具有存储功能的部件比对,可以看到,寄存器的速度是最快的,同时也是造价最高昂的。

    assembly02 002

    我们以 intel 8086 处理器为例来进行探讨,8086 处理器是 x86 架构的前身。在 8086 后面又衍生出来了 8088 。

    在 8086 CPU 中,地址总线达到 20 根,因此最大寻址能力是 2^20 次幂也就是 1MB 的寻址能力,8088 也是如此。

    在 8086 架构中,所有的内部寄存器、内部以及外部总线都是 16 位宽,可以存储两个字节,因为是完全的 16 位微处理器。8086 处理器有 14 个寄存器,每个寄存器都有一个特有的名称,即

    AX,BX,CX,DX,SP,BP,SI,DI,IP,FLAG,CS,DS,SS,ES

    这 14 个寄存器有可能进行具体的划分,按照功能可以分为三种

    • 通用寄存器
    • 控制寄存器
    • 段寄存器

    下面我们分别介绍一下这几种寄存器

    通用寄存器

    通用寄存器主要有四种 ,即 AX、BX、CX、DX 同样的,这四个寄存器也是 16 位的,能存放两个字节。 AX、BX、CX、DX 这四个寄存器一般用来存放数据,也被称为 数据寄存器。它们的结构如下

    assembly02 003

    8086 CPU 的上一代寄存器是 8080 ,它是一类 8 位的 CPU,为了保证兼容性,8086 在 8080 上做了很小的修改,8086 中的通用寄存器 AX、BX、CX、DX 都可以独立使用两个 8 位寄存器来使用。

    在细节方面,AX、BX、CX、DX 可以再向下进行划分

    • AX(Accumulator Register) : 累加寄存器,它主要用于输入/输出和大规模的指令运算。
    • BX(Base Register):基址寄存器,用来存储基础访问地址
    • CX(Count Register):计数寄存器,CX 寄存器在迭代的操作中会循环计数
    • DX(data Register):数据寄存器,它也用于输入/输出操作。它还与 AX 寄存器以及 DX 一起使用,用于涉及大数值的乘法和除法运算。

    这四种寄存器可以分为上半部分和下半部分,用作八个 8 位数据寄存器

    • AX 寄存器可以分为两个独立的 8 位的 AH 和 AL 寄存器;
    • BX 寄存器可以分为两个独立的 8 位的 BH 和 BL 寄存器;
    • CX 寄存器可以分为两个独立的 8 位的 CH 和 CL 寄存器;
    • DX 寄存器可以分为两个独立的 8 位的 DH 和 DL 寄存器;

    除了上面 AX、BX、CX、DX 寄存器以外,其他寄存器均不可以分为两个独立的 8 位寄存器

    如下图所示。

    assembly02 004

    合起来就是

    assembly02 005

    AX 的低位(0 - 7)位构成了 AL 寄存器,高 8 位(8 - 15)位构成了 AH 寄存器。AH 和 AL 寄存器是可以使用的 8 位寄存器,其他同理。

    在认识了寄存器之后,我们通过一个示例来看一下数据的具体存储方式。

    比如数据 19 ,它在 16 位存储器中所存储的表示如下

    assembly02 006

    寄存器的存储方式是先存储低位,如果低位满足不了就存储高位,如果低位能够满足,高位用 0 补全,在其他低位能满足的情况下,其余位也用 0 补全。

    8086 CPU 可以一次存储两种类型的数据

    • 字节(byte): 一个字节由 8 bit 组成,这是一种恒定不变的存储方式
    • 字(word):字是由指令集或处理器硬件作为单元处理的固定大小的数据,对于 intel 来说,一个字长就是两个字节,字是计算机一个非常重要的特征,针对不同的指令集架构来说,计算机一次处理的数据也是不同的。也就是说,针对不同指令集的机器,一次能处理不用的字长,有字、双字(32位)、四字(64位)等。

    AX 寄存器

    我们上面探讨过,AX 的另外一个名字叫做累加寄存器或者简称为累加器,其可以分为 2 个独立的 8 位寄存器 AH 和 AL;在编写汇编程序中,AX 寄存器可以说是使用频率最高的寄存器。

    下面是几段汇编代码

    mov ax,20		/* 将 20 送入寄存器 AX*/
    mov ah,80   /* 将 80 送入寄存器 AH*/
    add ax,10	  /* 将寄存器 AX 中的数值加上 8 */
    

    这里注意下:上面代码中出现的是 ax、ah ,而注释中确是 AX、AH ,其实含义是一样的,不区分大小写。

    AX 相比于其他通用寄存器来说,有一点比较特殊,AX 具有一种特殊功能的使用,那就是使用 DIV 和 MUL 指令式使用。

    DIV 是 8086 CPU 中的除法指令。

    MUL 是 8086 CPU 中的乘法指令。

    BX 寄存器

    BX 被称为数据寄存器,即表明其能够暂存一般数据。同样为了适应以前的 8 位 CPU ,而可以将 BX 当做两个独立的 8 位寄存器使用,即有 BH 和 BL。BX 除了具有暂存数据的功能外,还用于 寻址,即寻找物理内存地址。BX 寄存器中存放的数据一般是用来作为偏移地址 使用的,因为偏移地址当然是在基址地址上的偏移了。偏移地址是在段寄存器中存储的,关于段寄存器的介绍,我们后面再说。

    CX 寄存器

    CX 也是数据寄存器,能够暂存一般性数据。同样为了适应以前的 8 位 CPU ,而可以将 CX 当做两个独立的 8 位寄存器使用,即有 CH 和 CL。除此之外,CX 也是有其专门的用途的,CX 中的 C 被翻译为 Counting 也就是计数器的功能。当在汇编指令中使用循环 LOOP 指令时,可以通过 CX 来指定需要循环的次数,每次执行循环 LOOP 时候,CPU 会做两件事

    • 一件事是计数器自动减 1

    • 还有一件就是判断 CX 中的值,如果 CX 中的值为 0 则会跳出循环,而继续执行循环下面的指令,

      当然如果 CX 中的值不为 0 ,则会继续执行循环中所指定的指令 。

    DX 寄存器

    DX 也是数据寄存器,能够暂存一般性数据。同样为了适应以前的 8 位 CPU ,DX 的用途其实在前面介绍 AX 寄存器时便已经有所介绍了,那就是支持 MUL 和 DIV 指令。同时也支持数值溢出等。

    ###段寄存器

    CPU 包含四个段寄存器,用作程序指令,数据或栈的基础位置。实际上,对 IBM PC 上所有内存的引用都包含一个段寄存器作为基本位置。

    段寄存器主要包含

    • CS(Code Segment) : 代码寄存器,程序代码的基础位置
    • DS(Data Segment): 数据寄存器,变量的基本位置
    • SS(Stack Segment): 栈寄存器,栈的基础位置
    • ES(Extra Segment): 其他寄存器,内存中变量的其他基本位置。

    索引寄存器

    索引寄存器主要包含段地址的偏移量,索引寄存器主要分为

    • BP(Base Pointer):基础指针,它是栈寄存器上的偏移量,用来定位栈上变量
    • SP(Stack Pointer): 栈指针,它是栈寄存器上的偏移量,用来定位栈顶
    • SI(Source Index): 变址寄存器,用来拷贝源字符串
    • DI(Destination Index): 目标变址寄存器,用来复制到目标字符串

    状态和控制寄存器

    就剩下两种寄存器还没聊了,这两种寄存器是指令指针寄存器和标志寄存器:

    • IP(Instruction Pointer): 指令指针寄存器,它是从 Code Segment 代码寄存器处的偏移来存储执行的下一条指令
    • FLAG : Flag 寄存器用于存储当前进程的状态,这些状态有
      • 位置 (Direction):用于数据块的传输方向,是向上传输还是向下传输
      • 中断标志位 (Interrupt) :1 - 允许;0 - 禁止
      • 陷入位 (Trap) :确定每条指令执行完成后,CPU 是否应该停止。1 - 开启,0 - 关闭
      • 进位 (Carry) : 设置最后一个无符号算术运算是否带有进位
      • 溢出 (Overflow) : 设置最后一个有符号运算是否溢出
      • 符号 (Sign) : 如果最后一次算术运算为负,则设置 1 =负,0 =正
      • 零位 (Zero) : 如果最后一次算术运算结果为零,1 = 零
      • 辅助进位 (Aux Carry) :用于第三位到第四位的进位
      • 奇偶校验 (Parity) : 用于奇偶校验

    物理地址

    我们大家都知道, CPU 访问内存时,需要知道访问内存的具体地址,内存单元是内存的基本单位,每一个内存单元在内存中都有唯一的地址,这个地址即是 物理地址。而 CPU 和内存之间的交互有三条总线,即数据总线、控制总线和地址总线。

    assembly008

    CPU 通过地址总线将物理地址送入存储器,那么 CPU 是如何形成的物理地址呢?这将是我们接下来的讨论重点。

    现在,我们先来讨论一下和 8086 CPU 有关的结构问题。

    cxuan 和你聊了这么久,你应该知道 8086 CPU 是 16 位的 CPU 了,那么,什么是 16 位的 CPU 呢?

    你可能大致听过这个回答,16 位 CPU 指的是 CPU 一次能处理的数据是 16 位的,能回答这个问题代表你的底层还不错,但是不够全面,其实,16 位的 CPU 指的是

    • CPU 内部的运算器一次最多能处理 16 位的数据

    运算器其实就是 ALU,运算控制单元,它是 CPU 内部的三大核心器件之一,主要负责数据的运算。

    • 寄存器的最大宽度为 16 位

    这个寄存器的最大宽度值得就是通用寄存器能处理的二进制数的最大位数

    • 寄存器和运算器之间的通路为 16 位

    这个指的是寄存器和运算器之间的总线,一次能传输 16 位的数据

    好了,现在你应该知道为什么叫做 16 位 CPU 了吧。

    在你知道上面这个问题的答案之后,我们下面就来聊一聊如何计算物理地址。

    8086 CPU 有 20 位地址总线,每一条总线都可以传输一位的地址,所以 8086 CPU 可以传送 20 位地址,也就是说,8086 CPU 可以达到 2^20 次幂的寻址能力,也就是 1MB。8086 CPU 又是 16 位的结构,从 8086 CPU 的结构看,它只能传输 16 位的地址,也就是 2^16 次幂也就是 64 KB,那么它如何达到 1MB 的寻址能力呢?

    原来,8086 CPU 的内部采用两个 16 位地址合成的方式来传输一个 20 位的物理地址,如下图所示

    assembly02 007

    叙述一下上图描述的过程

    CPU 中相关组件提供两个地址:段地址和偏移地址,这两个地址都是 16 位的,他们经由地址加法器变为 20 位的物理地址,这个地址即是输入输出控制电路传递给内存的物理地址,由此完成物理地址的转换。

    地址加法器采用 物理地址 = 段地址 * 16 + 偏移地址 的方法用段地址和偏移地址合成物理地址。

    下面是地址加法器的工作流程

    assembly02 008

    其实段地址 * 16 ,就是左移 4 位。在上面的叙述中,物理地址 = 段地址 * 16 + 偏移地址,其实就是基础地址 + 偏移地址 = 物理地址 寻址模式的一种具体实现方案。基础地址其实就等于段地址 * 16。

    你可能不太清楚 的概念,下面我们就来探讨一下。

    什么是段

    段这个概念经常出现在操作系统中,比如在内存管理中,操作系统会把不同的数据分成 来存储,比如 代码段、数据段、bss 段、rodata 段 等。

    但是这些的划分并不是内存干的,cxuan 告诉你是谁干的,这其实是幕后 Boss CPU 搞的,内存当作了声讨的对象。

    其实,内存没有进行分段,分段完全是由 CPU 搞的,上面聊过的通过基础地址 + 偏移地址 = 物理地址的方式给出内存单元的物理地址,使得我们可以分段管理 CPU。

    如图所示

    assembly02 009

    这是两个 16 KB 的程序分别被装载进内存的示意图,可以看到,这两个程序的段地址的大小都是 16380。

    这里需要注意一点, 8086 CPU 段地址的计算方式是段地址 * 16,所以,16 位的寻址能力是 2^16 次方,所以一个段的长度是 64 KB。

    段寄存器

    cxuan 在上面只是简单为你介绍了一下段寄存器的概念,介绍的有些浅,而且介绍段寄存器不介绍段也有不知庐山真面目的感觉,现在为你详细的介绍一下,相信看完上面的段的概念之后,段寄存器也是手到擒来。

    我们在合成物理地址的那张图提到了 相关部件 的概念,这个相关部件其实就是段寄存器,即 CS、DS、SS、ES 。8086 的 CPU 在访问内存时,由这四个寄存器提供内存单元的段地址。

    CS 寄存器

    要聊 CS 寄存器,那么 IP 寄存器是你绕不过去的曾经。CS 和 IP 都是 8086 CPU 非常重要的寄存器,它们指出了 CPU 当前需要读取指令的地址。

    CS 的全称是 Code Segment,即代码寄存器;而 IP 的全称是 Instruction Pointer ,即指令指针。现在知道这两个为什么一起出现了吧!

    在 8086 CPU 中,由 CS:IP 指向的内容当作指令执行。如下图所示

    assembly02 010

    说明一下上图

    在 CPU 内部,由 CS、IP 提供段地址,由加法器负责转换为物理地址,输入输出控制电路负责输入/输出数据,指令缓冲器负责缓冲指令,指令执行器负责执行指令。在内存中有一段连续存储的区域,区域内部存储的是机器码、外面是地址和汇编指令。

    上面这幅图的段地址和偏移地址分别是 2000 和 0000,当这两个地址进入地址加法器后,会由地址加法器负责将这两个地址转换为物理地址

    assembly02 011

    然后地址加法器负责将指令输送到输入输出控制电路中

    assembly02 012

    输入输出控制电路将 20 位的地址总线送到内存中。

    assembly02 013

    然后取出对应的数据,也就是 B8、23、01,图中的 B8、BB 都是操作数。

    assembly02 014

    控制输入/输出电路会将 B8 23 01 送入指令缓存器中。

    assembly02 015

    此时这个指令就已经具备执行条件,此时 IP 也就是指令指针会自动增加。我们上面说到 IP 其实就是从 Code Segment 也就是 CS 处偏移的地址,也就是偏移地址。它会知道下一个需要读取指令的地址,如下图所示

    assembly02 016

    在这之后,指令执行执行取出的 B8 23 01 这条指令。

    然后下面再把 2000 和 0003 送到地址加法器中再进行后续指令的读取。后面的指令读取过程和我们上面探讨的如出一辙,这里 cxuan 就不再赘述啦。

    通过对上面的描述,我们能总结一下 8086 CPU 的工作过程

    • 段寄存器提供段地址和偏移地址给地址加法器
    • 由地址加法器计算出物理地址通过输入输出控制电路将物理地址送到内存中
    • 提取物理地址对应的指令,经由控制电路取回并送到指令缓存器中
    • IP 继续指向下一条指令的地址,同时指令执行器执行指令缓冲器中的指令

    什么是 Code Segment

    Code Segment 即代码段,它就是我们上面聊到就是 CS 寄存器中存储的基础地址,也就是段地址,段地址其本质上就是一组内存单元的地址,例如上面的 mov ax,0123H 、mov bx, 0003H。我们可以将长度为 N 的一组代码,存放在一组连续地址、其实地址为 16 的倍数的内存单元中,我们可以认为,这段内存就是用来存放代码的。

    DS 寄存器

    CPU 在读写一个内存单元的时候,需要知道这个内存单元的地址。在 8086 CPU 中,有一个 DS 寄存器,通常用来存放访问数据的段地址。如果你想要读取一个 10000H 的数据,你可能会需要下面这段代码

    mov bx,10000H
    mov ds,bx
    mov a1,[0]
    

    上面这三条指令就把 10000H 读取到了 a1 中。

    在上面汇编代码中,mov 指令有两种传送方式

    • 一种是把数据直接送入寄存器
    • 一种是将一个寄存器的内容送入另一个寄存器

    但是不仅仅如此,mov 指令还具有下面这几种表达方式

    描述 举例
    mov 寄存器,数据 比如:mov ax,8
    mov 寄存器,寄存器 比如:mov ax,bx
    mov 寄存器,内存单元 比如:mov ax,[0]
    mov 内存单元,寄存器 比如:mov[0], ax
    mov 段寄存器,寄存器 比如:mov ds,ax

    栈我相信大部分小伙伴已经非常熟悉了,是一种具有特殊的访问方式的存储空间。它的特殊性就在于,先进入栈的元素,最后才出去,也就是我们常说的 先入后出

    它就像一个大的收纳箱,你可以往里面放相同类型的东西,比如书,最先放进收纳箱的书在最下面,最后放进收纳箱的书在最上面,如果你想拿书的话, 必须从最上面开始取,否则是无法取出最下面的书籍的。

    栈的数据结构就是这样,你把书籍压入收纳箱的操作叫做压入(push),你把书籍从收纳箱取出的操作叫做弹出(pop),它的模型图大概是这样

    assembly02 017

    入栈相当于是增加操作,出栈相当于是删除操作,只不过叫法不一样。栈和内存不同,它不需要指定元素的地址。它的大概使用如下

    // 压入数据
    Push(123);
    Push(456);
    Push(789);
    
    // 弹出数据
    j = Pop();
    k = Pop();
    l = Pop();
    

    在栈中,LIFO 方式表示栈的数组中所保存的最后面的数据(Last In)会被最先读取出来(First Out)。

    assembly02 018

    栈和 SS 寄存器

    下面我们就通过一段汇编代码来描述一下栈的压入弹出的过程

    8086 CPU 提供入栈和出栈指令,最基本的两个是 PUSH(入栈)POP(出栈)。比如 push ax 会把 ax 寄存器中的数据压入栈中,pop ax 表示从栈顶取出数据送入 ax 寄存器中。

    这里注意一点:8086 CPU 中的入栈和出栈都是以字为单位进行的。

    我这里首先有一个初始的栈,没有任何指令和数据。

    assembly02 019

    然后我们向栈中 push 数据后,栈中数据如下

    assembly02 020

    涉及的指令有

    mov ax,2345H
    push ax
    

    注意,数据会用两个单元存放,高地址单元存放高 8 位地址,低地址单元存放低 8 位。

    再向栈中 push 数据

    assembly02 021

    其中涉及的指令有

    mov bx,0132H
    push bx
    

    现在栈中有两条数据,现在我们执行出栈操作

    assembly02 022

    其中涉及的指令有

    pop ax
    /* ax = 0132H */
    

    再继续取出数据

    assembly02 023

    涉及的指令有

    pop bx
    /* bx = */
    

    完整的 push 和 pop 过程如下

    assembly02 024

    现在 cxuan 问你一个问题,我们上面描述的是 10000H ~ 1000FH 这段空间来作为 push 和 pop 指令的存取单元。但是,你怎么知道这个栈单元就是 10000H ~ 1000FH 呢?也就是说,你如何选择指定的栈单元进行存取?

    事实上,8086 CPU 有一组关于栈的寄存器 SSSP。SS 是段寄存器,它存储的是栈的基础位置,也就是栈顶的位置,而 SP 是栈指针,它存储的是偏移地址。在任意时刻,SS:SP 都指向栈顶元素。push 和 pop 指令执行时,CPU 从 SS 和 SP 中得到栈顶的地址。

    现在,我们可以完整的描述一下 push 和 pop 过程了,下面 cxuan 就给你推导一下这个过程。

    assembly02 025

    上面这个过程主要涉及到的关键变化如下。

    当使用 PUSH 指令向栈中压入 1 个字节单元时,SP = SP - 1;即栈顶元素会发生变化;

    而当使用 PUSH 指令向栈中压入 2 个字节的字单元时,SP = SP – 2 ;即栈顶元素也要发生变化;

    当使用 POP 指令从栈中弹出 1 个字节单元时, SP = SP + 1;即栈顶元素会发生变化;

    当使用 POP 指令从栈中弹出 2 个字节单元的字单元时, SP = SP + 2 ;即栈顶元素会发生变化;

    栈顶越界问题

    现在我们知道,8086 CPU 可以使用 SS 和 SP 指示栈顶的地址,并且提供 PUSH 和 POP 指令实现入栈和出栈,所以,你现在知道了如何能够找到栈顶位置,但是你如何能保证栈顶的位置不会越界呢?栈顶越界会产生什么影响呢?

    比如如下是一个栈顶越界的示意图

    assembly02 026

    第一开始,SS:SP 寄存器指向了栈顶,然后向栈空间 push 一定数量的元素后,SS:SP 位于栈空间顶部,此时再向栈空间内部 push 元素,就会出现栈顶越界问题。

    栈顶越界是危险的,因为我们既然将一块区域空间安排为栈,那么在栈空间外部也可能存放了其他指令和数据,这些指令和数据有可能是其他程序的,所以如此操作会让计算机懵逼

    我们希望 8086 CPU 能自己解决问题,毕竟 8086 CPU 已经是个成熟的 CPU 了,要学会自己解决问题了。

    assembly02 027

    然鹅(故意的),这对于 8086 CPU 来说,这可能是它一辈子的 夙愿 了,真实情况是,8086 CPU 不会保证栈顶越界问题,也就是说 8086 CPU 只会告诉你栈顶在哪,并不会知道栈空间有多大,所以需要程序员自己手动去保证。。。

    另外,我输出了 六本 PDF,已免费提供下载,如下所示

    链接: https://pan.baidu.com/s/1mYAeS9hIhdMFh2rF3FDk0A 密码: p9rs

    展开全文
  • JAVA虚拟机的通俗解释

    2018-04-12 20:28:24
    JAVA虚拟机的百度解释如下。虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java虚拟机屏蔽...

    JAVA不同于C语言,是在JAVA虚拟机中运行的。JAVA虚拟机的百度解释如下。

    虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

    我开始上班的时候有很多声音是虚拟机运行比较慢,天然不如C语言运算快。就是所谓的程序员鄙视链,C天然鄙视JAVA。后来随着了解的多了,感觉JAVA虚拟机是添加中间层的想法。就是封装。

    程序最开始是机器编码,一个符号代表一种操作。

    后来是汇编,可以让程序员不需要记住二进制命令了,汇编的一条指令和机器代码的一条指令是一一对应的。只是为了让我们更好的记忆。

    后来是C语言这种高级程序语言,C语言是汇编语言的抽象,他会编译成汇编语言,然后编程可执行文件。C语言让程序员可以不需要理解什么寄存器了,PC了。反正就是编程变得简单了,更需要注重业务。有了C语言以后,大型程序才有可能被开发出来,使用汇编语言来写一个大的项目,无论从学习成本(上学的时候汇编就没学好)还是合作成本,各个方便都不适合写出大型程序。

    但是C语言又存在移植性的问题,每个CPU的指令级不一样,不同的操作系统变量还有大小端也存在不同。程序的迁移就很麻烦,需要使用源代码到不同的机器上进行编译,然后才能执行。但是也会存在一些执行的差异。

    引用一句话“计算机里面没有什么问题都可以通过添加中间件来解决。”

    我觉得JAVA就是通过这个思想诞生的,一处编写,到处运行。

    JAVA不在针对CUP执行级以及操作系统来编写程序了,而是针对虚拟机(中间件),不同的操作系统,我再通过虚拟机去适应。这样程序就和操作系统解耦了。两个不互相依赖。DI,IOC也是这种思想吧。如果是DI这种思想是对的,那么JAVA就是更好的一种语言。

    C->操作系统

    JAVA->虚拟机<-操作系统



    展开全文
  • 2、汇编语言 机器语言的问题在于人类要传递给计算机任何信息都必须以0和1的组合序列表达,不符合人类的沟通习惯,因此有了助记符的概念并在这个基础上产生了汇编语言,通俗来说我们需要将1+2这种信息表达为计算机可...
  • 首先,FSMC是什么,FSMC我自己的理解就是,一个衔接CPU与外部存储的桥梁,它的功能呢就是你往相应的地址里写数据时候,你不需用软件来模拟外部存储芯片的读写时序,而只需配置好FSMC相关的时序寄存器,配置好相关...
  • ◆流量控制 UART 通常可以分为 带流量控制 和 不带流量控制 两种方式...与接收寄存器的多级硬件缓冲Buf协调工作。比如在硬件缓冲已经接收满了6个字节的时,RTS引脚就输出无效信号(高电平),当缓冲中的数据都被读出后...
  • 下面我尽量用通俗的语言来试着解释。计算机由CPU、内存、硬盘、显示器和键盘等部件组成。计算机软件平时在不工作时则是存放在硬盘里的,计算机开机之后,需要用到的软件就被调入内存来执行。无论在硬盘还是在内存里...
  •  图中的地址转换器受控制寄存器MENMAP的控制,用户可以设置MENMAP实现对地址重映射的控制。这个地址转换器显然是通过内部 硬件 电路实现的。  转载于:...
  • 寄存器PLC地址与寄存器modbus协议地址
  •  图中的地址转换器受控制寄存器MENMAP的控制,用户可以设置MENMAP实现对地址重映射的控制。这个地址转换器显然是通过内部硬件电路实现的。 http://blog.csdn.net/zyboy2000/article/details/4430492  
  •  为了解决这两个问题,我查看了很多个博客,但是大多数博客或文章对原理进行了充分的解释,但是并没有例子来说明,个人觉得这样会显得十分空洞,所以下面来跟大家探讨一下有关问题的通俗一点的解释,如果有错误的...
  • 在AHCI协议中Port Register中有如下几个寄存器,我们这里主要关注PxSSTS和PxSERR 2. 什么是SCR寄存器 参考AHCI协议14.1章节 Sata host适配器包括了一个额外的寄存器块,这些寄存器是分离的独立的,映射了ATA ...
  • 常用寄存器说明
  • modbus协议中的寄存器理解

    万次阅读 多人点赞 2018-02-26 09:54:35
    最近有用到modbus协议,就把之前原来收集的资料全都拿出来又复习了一遍。发现以前了解的也忘了差不多...具体如下: 0x01: 读线圈寄存器 0x02: 读离散输入寄存器 0x03: 读保持寄存器 0x04: 读输入寄存器 0x05: ...
  • 寄存器与内存的区别

    千次阅读 2018-05-18 15:23:38
    寄存器与内存的区别寄存器寄存器是有触发器或锁存器组成,通常由触发器组成,寄存器拥有非常高的读写速度,所以在寄存器之间传递数据非常快 内存:内存... Mike Ash写了一篇很好的解释,非常通俗地回答了这个问...
  • X86汇编常见的寄存器

    千次阅读 2019-08-03 11:52:39
    X86汇编常见的寄存器 4个数据寄存器(EAX、EBX、ECX和EDX) 2个变址和指针寄存器(ESI和EDI) 2个指针寄存器(ESP和EBP) 6个段寄存器(ES、CS、SS、DS、FS和GS) 1个指令指针寄存器(EIP) 1个标志寄存器(EFlags) AH&AL...
  • 寄存器与存储器物理区别

    千次阅读 2018-11-07 14:14:25
    从根本上讲,寄存器与存储器RAM的元件不一样???? memory is equivalent to a lot of registers. 寄存器存在于CPU中,速度...Mike Ash写了一篇很好的解释,非常通俗地回答了这个问题,有助于加深对硬件的理解。...
  • 介绍了解释执行和编译执行的区别、基于栈和基于寄存器的指令集区别
  • Mike Ash写了一篇很好的解释,非常通俗地回答了这个问题,有助于加深对硬件的理解。下面就是我的简单翻译。 原因一:距离不同 距离不是主要因素,但是最好懂,所以放在最前面说。内存离CPU比较远
  • Mike Ash写了一篇很好的解释,非常通俗地回答了这个问题,有助于加深对硬件的理解。下面就是我的简单翻译。 原因一:距离不同 距离不是主要因素,但是最好懂,所以放在最前面说。内存离CPU比较远

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,337
精华内容 2,534
关键字:

寄存器通俗解释