简易CPU设计入门:内存读写(三)
专栏导航上一篇简易CPU设计入门内存读写二专栏目录下一篇无项目代码下载请大家首先准备好本项目所用的源代码。如果已经下载了那就不用重复下载了。如果还没有下载那么请大家点击下方链接来了解下载本项目的CPU源代码的方法。CSDN文章下载本项目代码上述链接为本项目所依据的版本。在讲解过程中我还时不时地发现自己在讲解与注释上的一些个错误。有时我还会添加一点新的资料。在这里我将动态更新的代码版本发在下面的链接中。Gitee项目简易CPU设计入门项目代码:讲课的时候我主要依据的是CSDN文章链接。然后呢如果你为了获得我的最近更新的版本那就请在Gitee项目链接里下载代码。准备好了项目源代码以后我们接着去讲解。本节前言在上一节我是讲解了【rw_ram】模块里面的与内存写操作有关的部分。本节我来将内存读操作。本节的代码主要位于【...cpu_me01\code\Ctrl_Center\】路径里面所所涉及的代码文件主要是【rw_ram.v】和【ctrl_center.v】。一. 内部控制信号图1控制中心模块中的注释/********************************************* ctrl_sig_inner[0]:register write enable寄存器写使能 ctrl_sig_inner[1]:register read enable寄存器读使能 ctrl_sig_inner[2]:random memory write ebable内存写使能 ctrl_sig_inner[3]:random memory read enable内存读使能 ctrl_sig_inner[4]:Arithmetic and Logic calculate算术逻辑运算 ctrl_sig_inner[5]:reserve保留 ctrl_sig_inner[6]:reserve保留 ctrl_sig_inner[7]:reserve保留 ctrl_sig_inner[8]:reserve保留 ctrl_sig_inner[9]:reserve保留 ctrl_sig_inner[10]:reserve保留 ctrl_sig_inner[11]:reserve保留 ctrl_sig_inner[12]:reserve保留 ctrl_sig_inner[13]:reserve保留 ctrl_sig_inner[14]:reserve保留 ctrl_sig_inner[15]:reserve保留 还有一种运算叫做读取立即数将立即数放入内部寄存器。 此运算不需要通过内部信号的参与。 ************************************************/在图1里面我们要去重点关注 的41行的内容。行号41位置的注释内容是说当内部控制信号总线变量的位3为1而其余位均为0的时候这代表着控制中心模块发布了内存读使能信号。控制中心在发布内存读使能信号的时候会同时向控制中心模块里面的内部地址信号总线变量写入待读取的内存的地址值。内存读使能与地址值有效时间均是只有一个时钟周期。接下来我们还是将主要的关注点放在【rw_ram.v】文件上。二. get_time 与 rd_en 信号图2图3图2与图3分别展示了【get_time】与【rd_en】信号的逻辑。这俩信号的逻辑差不多都是说平时的时候为0值。一旦在某一个时钟上升沿到来时系统检测到【ctrl_sig_inner】的位3为1时便会被非阻塞赋值为1值。ctrl_sig_inner它是【rw_ram】模块内部的一个【inout】类型的变量也是一个总线类型的变量。【rw_ram】模块里面的【ctrl_sig_inner】变量它与寄存器元素模块的同名变量都连接在同名的总线上。同样地控制中心模块也存在着同名变量连接到这个总线上。【ctrl_sig_inner】总线的位3为1时代表着控制中心发布了内存读使能信号。所以呢图2与图3的意思是当控制中心发布了内存读使能信号那么【rw_ram】模块就需要将【rd_en】和【get_time】信号均赋值为1。我们接着看代码。图4由图4可知在系统复位时【get_time_d1】被非阻塞赋值为0值。而在平时【get_time_d1】是【get_time】信号的延时变量。【get_time_d1】比【get_time】延后一个时钟周期。三. 缓存总线信息图5在图5里面我们主要来关注红色框线部分的代码。从图5可以看出【addr_buf】与【data_buf】在系统复位时它们均被赋予了0值。在平时闲来无事时也就是在【else】分支里面它们保持现有值不变。而当【ctrl_sig_inner】总线的位3为1时也就是当控制中心发布了内存读使能信号的时候【addr_buf】要去缓存内部地址信号总线【addr_sig_inner】的值而【data_buf】则是保持现有值不变。为啥在控制中心发布内存读使能的时候内部地址信号总线的值要被缓存下来而内部数据信号总线的值无须缓存呢为什么呢在代码里我们当然可以选择用【data_buf】来缓存内部数据总线【data_sig_inner】的值。但是呢没有必要。在内存写操作里面我们需要从内部数据总线【data_sig_inner】上获得待写入的数据。而在内存读操作里面我们不需要从内部数据总线【data_sig_inner】上获得待写入的数据因为此时我们要去进行的是读操作而不是写操作。那么为啥又要去缓存内部地址信号总线【addr_sig_inner】的值呢无论是内存读操作还是内存写操作我们都需要指定要去操作的内存地址。控制中心在发布内存读信号的时候它同时也在内部地址信号总线上写入了要去操作的内存地址的值。然而这个值仅仅保持一个时钟周期的有效时间过期不候。而这个地址信息我们在后面是需要去使用的是需要将其保存下来的。所以呢在图5里面我们用【addr_buf】缓存了内部地址信号总线【addr_sig_inner】的值。四. 两个代理变量的绑定图6图6显示了两个总线类型的变量【data_sig_inner】和【work_ok_inner】与它们各自的代理变量的绑定代码。绑定了以后在时序逻辑中对代理变量赋值就实质上等同于对关联着的总线类型的线网变量【data_sig_inner】和【work_ok_inner】的赋值了。在这里总线类型它并不是 verilog HDL 中的端口类型或变量类型。只是【inout】端口类型的变量常用于对总线进行建模所以呢我就将【inout】端口类型的变量说成是总线类型了。如果你是一路跟着教程学习下来的那么根据上下文你应该能明白我的意思。五. 与内存读操作有关的 RAM IP 核信号连接图7133行显示了我们所实例化的 RAM IP 核的模块名称【ram_256x16】以及生成的实例名【ram_256x16_inst】。134行与136行表示说系统复位信号【sys_rst_n】和系统时钟信号【sys_clk】分别与 RAM IP 核的内部信号【aclr】和【clock】连接在了一起。其中系统时钟信号直接与 RAM IP 核的内部信号相连而系统复位信号则是先取反然后再与 RAM IP 核的内部对应信号相连。之所以说系统复位信号【sys_rst_n】要先取反然后再与 RAM IP 核中的【aclr】信号相连那是因为我们的系统复位信号【sys_rst_n】是低电平有效复位而 RAM IP 核的复位信号【aclr】信号则是高电平有效复位。【aclr】英文全称是【asynchronous clear】是异步复位的意思。也就是说只要aclr变为了高电平不论当时是否处于时钟上升沿RAM IP 核都会立即执行复位操作。在这里异步就是不与时钟信号同步的意思。在其他的一些个场合里我们还会得到关于【异步】的另外的含义。在系统时钟与系统复位信号之外在内存读操作里面我们还需要关注另外的三个变量的连接情况它们分别是图7的135行138行140行的连接。135行里面它是说【rw_ram】模块的【addr_buf】变量与 RAM IP 核内部的【address】信号连接在一起。138行是说【rw_ram】模块的读使能信号【rd_en】与 RAM IP 核的读使能信号【rden】相连。140行是说【rw_ram】模块的【data_out】变量与 RAM IP 核的数据输出信号【q】相连。在这里我们需要注意的一点是在连接地址信号时【rw_ram】模块的【addr_buf】变量是 reg 类型的。而在连接数据输出信号时【rw_ram】模块的【data_out】变量是线网类型的。不信的话请看下方截图。图8看到了吧addr_buf 是 reg 类型的而 【data_out】是线网类型的。当甲模块的输出端口【q】连接到乙端口的【in_sig】端口时乙端口的【in_sig】必须是线网类型不可以是寄存器类型。此时甲模块的输出端口【q】在甲模块里面它可以是 reg 型的也可以是 wire 型的。如果甲模块位于乙模块的内部乙模块实例化了甲模块在这种情况下假定我们用甲模块里面的 wire 型变量【var_x】连接甲模块的输出端口【q】。这种情况下甲模块里面的 wire 型变量【var_x】它可以是位于端口声明中的输出端口部分也可以位于普通的变量声明区域仅仅是声明一个 wire 型变量然后去连接甲模块的输出端口【q】这样也是可以的。关于 Verilog HDL 的一些个语法细节个人觉得相当琐碎很不容易记忆。讲这一块的时候我自己都觉得有点绕嘴。但是呢没办法遇到了这样的一个琐碎的先生咱们还得是去学习它。不然的话FPGA从哪儿来芯片从哪儿来呢六. RAM IP 核的输出端口 q 的类型关于配置 RAM IP 核的方法我其实在之前的某一节文章里面讲过了就是下面的文章链接中讲述的。调用IP核生成指令内存在上面的链接所示文章的第二分节里面我讲解了本系统所用的 RAM IP 核的配置方法。在这里我们用另一种方法打开一下这个 IP 核并且查看其中的一个地方的配置方法。请大家打开Quartus II 13.1 软件然后呢打开【cpu_me01】项目代码。接下来请大家跟我一起操作。第一步依次点击【Tools】【MegaWizard Plug-In Manager】菜单项过程如下图所示。图9图10在图10里面在上方有三个单选按钮我们点选中间的那个单选按钮然后点击右下方的【Next】按钮如下图所示。图11图12在图12里面我们需要浏览到 【rw_ram】模块里面的【ram_256x16】模块的 ip 核的位置。它位于【......cpu_me01\ip_core\ram_256x16\】路径里面。我们浏览到这里如下图所示。图13我们点击选择图13的红色框线所示的代码文件然后我们点击右下角的【Next】按钮结果如下图所示。图14在这里我们不需要关注全部的配置。在这里大家点击一下右下角的【Next】按钮来到下图所示的界面。图15在图15的红色框线所示的部分有一个复选框跟 RAM IP 核的输出端口【q】有关。我们若是勾选了那个复选框则界面中的预览图部分会变成下面这样子。图16我们取消对图15的红色框线所示的复选框的勾选则预览图区域会变成下图所示的样子。图17大家可以看到图16比17的【q】端口部分多了一个连线。这个关于输出端口【q】的复选框勾选了的版本比不勾选的版本的【q】变量的起始有效时间晚一个时钟周期。在我们的设置里面我是没有勾选【q】输出端口的的复选框的。也就是说我所选择的设置情况与图15和图17是一致的是没有勾选【q】输出端口选项前面的复选框的。在这里我只是通过一番演示展示图15的红色框线所示的【q】输出端口复选框的作用我们并不需要修改项目的配置。所以接下来请大家在图15所示的对话框中点击下方的【Cancel】按钮关闭对话框回到软件主界面。这样一来关于 q 的复选情形我就跟大家说完了。七. 本系统的 RAM IP 核的读操作时序谈到时序肯定是跟时钟信号有关的。我们还是来看一看图7。图7副本【rw_ram】模块里面所实例化的 RAM IP 核的时钟信号使用的是与系统时钟信号【sys_clk】同节奏的时钟。如果你在配置 RAM IP 核的时候在设置【q】输出端口的复选框选项的时候和我一样都是没有勾选那个复选框那么当 RAM IP 核在某一个时钟上升沿到来并且检测到了【rden】信号为1的时候那么在下一个时钟的上升沿到来时输出端口【q】上就已经有了有效的输出数据了。我们将检测到【rden】信号为1所在的时钟上升沿规定为0号上升沿。那么假定你在配置 RAM IP 核的时候在设置【q】输出端口的复选框选项的时候选择了将图15的红色框线部分所示的复选框给勾选上那么其效果就是在1号上升沿输出端口【q】里面不会含有有效数据在2号时钟上升沿到来时输出端口【q】上面才会有着有效数据。到了这里我想对于输出端口【q】以及与之相连的【rw_ram】模块变量【data_out】我应该算是讲清楚了吧。那么我们来进行下一个分解。八. data_sig_represent 与 work_ok_represent 的逻辑图18图18展示了 data_sig_represent 的逻辑。在平时闲来无事时也就是在系统复位有效与【else】分支里面data_sig_represent 都是被非阻塞赋值为高阻态z值的。由于【data_sig_represent】与【rw_ram】模块里面的【data_sig_inner】总线变量是绑定在一起的。所以呢在平时【rw_ram】模块与【data_sig_inner】是断开连接的。当检测到【get_time】为1时【rw_ram】模块的总线连接变量【data_sig_inner】通过其代理变量【data_sig_represent】被非阻塞赋值为0值。当检测到【get_time_d1】为1时【rw_ram】模块的总线连接变量【data_sig_inner】通过其代理变量【data_sig_represent】被非阻塞赋值为有效的【data_out】值。其中【data_out】里面保存的有效值来自于 RAM IP 核的输出端口【q】。某模块与总线断开连接是说这个模块不向与总线连接着的位于本模块的【inout】类型的变量写入非高阻态值因而不向总线写入非高阻态值。此时从代码形式上看这个模块是在向与总线连接着的位于本模块的【inout】类型的变量写入着高阻态值。实际上这个模块在这个时候是可以读取总线的值的。因此某模块与总线断开连接它表达的是该模块对总线不写只读的一种操作状态。不写指的是不向总线写入非高阻态值而只是写入高阻态值这个是【不写】。只读则是说此时该模块依然可以读取总线的值。每次涉及总线逻辑我总觉得讲起来很困难。本次也不例外。希望大家能够理解吧。关于总线逻辑我第一次讲解是在下面的链接所示的文章里面。简易CPU设计入门本系统中的通用寄存器四-CSDN博客在第七分解大家可以看到我所写的对总线逻辑的讲解。希望大家能够看懂。我估计关于总线逻辑这一块以后我还会去完善它以便大家能够看懂这一块的知识点。我们接着看【work_ok_represent】的逻辑。图19之前我们讲过总线变量【work_ok_inner】与【work_ok_represent】是绑定在一起的。对于图19我们在本节只看红色框线部分所示的代码。从图19我们看到在平时闲来无事之时也就是在系统复位与【else】分支里面总线变量【work_ok_inner】通过其代理变量【work_ok_represent】被非阻塞赋值为高阻态z值。也就是在平时总线变量【work_ok_inner】所在的模块【rw_ram】与【work_ok_inner】总线是断开连接的。在检测到【get_time】为1时【work_ok_inner】通过其代理变量【work_ok_represent】被非阻塞赋值为0值也就是【work_ok_inner】总线会被赋值为0值。在检测到【get_time_d1】为1时【work_ok_inner】通过其代理变量【work_ok_represent】被非阻塞赋值为1值也就是【work_ok_inner】总线会被赋值为1值。这表示内存读操作完成控制中心模块会接收到这一总线信号的。该讲的分节我们讲得差不多了。接下来我们来汇总一下内存读操作的操作时序。九. 内存读操作在这里我们将检测到【ctrLsig_inner[3] 1】所在的时钟上升沿规定为0号上升沿。接下来我们来看一看时序关系。在0号上升沿到来时根据本节的图2图3和图5系统会检测到【ctrl_sig_inner[3] 1】条件满足。于是在0号上升沿之后的非阻塞赋值阶段【get_time】和【rd_en】被并行地非阻塞赋值为1【addr_buf】缓存内部地址信号总线变量【addr_sig_inner】的值【data_buf】保持原值不变。在1号上升沿到来时根据图4图18和图19系统检测到【get_time 1】与【rd_en 1】条件满足。【rw_ram】模块的【rd_en】变量与 RAM IP 核的【rden】变量是连接的且【rw_ram】模块与 RAM IP 核使用相同的时钟信号系统时钟【sys_clk】。所以呢此时RAM IP 核开始激活读操作。那么在下一个时钟上升沿到来时也就是2号时钟上升沿到来时RAM IP 核的输出端口【q】上面已经保存了有效的输出数据了。于是在1号上升沿之后的非阻塞赋值阶段get_time_d1 被非阻塞赋值为1总线变量【data_sig_inner】通过其代理变量【data_sig_represent】被非阻塞赋值为0值因此【data_sig_inner】总线会被赋予0值。同时在1号上升沿之后的非阻塞赋值阶段【work_ok_inner】总线通过【rw_ram】模块的总线变量【work_ok_inner】的代理变量【work_ok_represent】被非阻塞赋值为0值。在2号时钟上升沿到来时系统检测到【get_time_d1 1】条件满足同时在这个时候RAM IP核的输出端口【q】以及与之连接的【rw_ram】模块的变量【data_out】上都包含着有效的输出数据。在2号时钟上升沿之后的非阻塞赋值阶段根据图18和图19【data_sig_inner】总线被赋予了有效的【data_out】值【work_ok_inner】总线也被赋予了1值表示内存读操作完成。在3号时钟上升沿到来时可以预见到这样的一些个情形包括【get_time 0 get_time_d1 0】【work_ok_inner 1】【data_sig_inner 有效的RAM数据】。由于在3号时钟上升沿到来时总线上包含了有效值因此控制中心正是在3号时钟上升沿到来时才接收到了有效的信号的。在3号时钟上升沿之后的非阻塞赋值阶段【rw_ram】模块的【work_ok_inner】总线变量和【data_sig_inner】总线变量的代理变量均处于【else】分支均被赋予高阻态z值两个总线变量都被赋予了高阻态值因此【rw_ram】模块与【work_ok_inner】总线和【data_sig_inner】总线均在此时断开了连接。两个总线上由于此时无连接着的线路因此也都处于高阻态z值。在4号时钟上升沿系统可以检测到【work_ok_inner】总线和【data_sig_inner】总线的高阻态状态。这样一来关于内存读操作本节就算是讲完了。我这里写得累挺。不知道你学得怎么样。结束语内存读写操作我就算是都讲完了。下一节我们要去讲控制中心模块里面与内存读写有关的实例化代码与控制信号。下一节预计也会很长吧。希望大家能够看懂我在这一节所说的东西吧。专栏导航上一篇简易CPU设计入门内存读写二专栏目录下一篇无
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2416537.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!