简易CPU设计入门:取指令(四)

news2024/11/24 11:40:19

项目代码下载

还是请大家首先准备好本项目所用的源代码。如果已经下载了,那就不用重复下载了。如果还没有下载,那么,请大家点击下方链接,来了解下载本项目的CPU源代码的方法。

下载本项目代码

准备好了项目源代码以后,我们接着去讲解。

一.   RAM IP 核在取指令模块中的实例引用

在前面的几节里面,我讲解了取指令模块的端口与变量,讲解了RAM IP核的生成等等。本小节呢,我们来看一看,RAM IP核在本模块中的实例引用。

请大家定位到【\cpu_me01\code\get_instruct.v】,查看里面的 RAM IP 核实例引用部分的代码。

图1

在图1里面,RAM IP核的模块当中,有时钟信号【clock】与异步复位信号【aclr】。时钟信号,这个就是用我们的系统复位信号【sys_clk】来连接就行了。

在我们的系统中,系统时钟信号的频率,是设置为50MHz。

RAM IP核里面的复位信号,其实我们也是用系统复位信号【sys_rst_n】来连接的。只不过呢,在我们的一般的设计模块的代码中,我们的复位,是低电平有效复位,而 RAM IP 核中的复位信号,是高电平有效复位。所以呢,我们在图1的65行里面,是用【~sys_rst_n】来作为【.aclr()】的括号里面的信号的。

然后呢,图1的66行,是地址信号,这个呢,我们用本模块的【ip_buf】来来连接。

我们还是来略微回顾一下【ip_buf】的逻辑。如下图所示。

图2
图3

由图2核图3可知,【ip_buf】是在输入信号中的【get_inst_en】变为1以后,通过非阻塞赋值的方式,来缓存输入信号中的指令指针信号【i[p】的。

地址信号,无论我们是从RAM中读取数据,还是向RAM中写入数据,都需要指定地址。那么,这个地址信号呢,就是来自于我们的输入信号中的指令指针信号【ip】。当然了,直接连接到RAM IP核的实例中的信号,是输入信号【ip】的缓存信号【ip_buf】。

然后呢,我们再来看图1中除了时钟、异步复位和地址信号之外,剩下的信号。剩下的信号,分为两组。

图1的68行中的【data】信号和70行的【wren】信号,都是 RAM IP核实例中的信号,这俩信号,用于向 RAM 写入数据。想要写的数据,要传递给data信号。什么时候想要写数据了,就给写使能信号【wren】设置为1,就可以了。要注意的是,当我们想要写入数据的时候,必须要确保写使能【wren】、待写入的数据【data】、地址信号【address】同时有效。不能说,写使能为1的时候,待写入数据和地址信号里面的值是无效的值,那是不行的。

在这里,我们生成的这个RAM IP核,我们的目的,不是要往里面写入数据,而是为了从里面读取指令信息,里面存储的,是我们要去译码与执行的指令码。只是为了从中读取信息,而不会往里面写入数据。

所以呢,在这里,我们已经可以确定,写使能信号【wren】,肯定会是始终为0的。所以呢,在图1中的70行里面,我们传递给【wren】信号的值,固定为【1'b0】。

而待写入的数据【data】,这个其实写啥都行。因为写使能信号始终为0,这样一来,不管我们往【data】信号里面传入什么值,它都写不进去。所以,这一个待写入的数据【data】,我们给它传什么值都行。不过呢,为了提醒自己说,我们不会往里面写入数据,所以呢,我就传递了一个高阻抗的值【16'hz】。

关于这个高阻抗值,其实它是一个很有意思的东西。在这里呢,我暂时不想展开讨论。以后有机会,我会来讨论这个高阻抗的问题。这里,先留着不讲。

这样一来,图1里面,就只剩下两个信号了,分别是69行的【rden】和71行的【q】信号。

以我的为数不多的使用经验来讲,在Quartus II 13.1里面,好多的 IP 核的输出数据信号的名字,都是【q】。在本模块中的 RAM IP 核实例中,也是如此,【q】代表着输出数据。

在图1里面,大家可以看到,我们用本模块的【rd_en】来连接 IP 核实例中的【rden】信号,也就是,读使能对应着读使能。我们还用本模块的wire型变量【instruct_code_wire】来连接着 IP 核中的输出信号【q】。

我们要从RAM中读取的数据,是指令码数据,所以呢,我们就将接收这个数据的变量名,命名为【instruct_code_wire】了。结尾的【wire】表明这是一个wire型变量。

二.   reg与wire类型知识点小复习

有一个问题哈,我们可否用reg型变量,来接收 IP 核实例中的输出数据信号【q】呢?

不可以的。

我们来复习一下Verilog里面,关于端口连接规则的知识点。

  • 输入端口:从模块内部来讲,本模块的输入端口必须为线网数据类型。从模块外部来讲,连接到本模块的输入端口的变量,可以是reg型,也可以是线网类型。
  • 输出端口:从模块内部来讲,本模块的输出端口可以是reg型变量,也可以是线网类型的变量。从模块外部来讲,本模块的输出端口所连接到的外部的变量,必须为线网类型的变量,而不能是reg类型。
  • 输入/输出端口:从模块内部来讲,本模块的输入/输出端口必须为线网数据类型,而不可以是reg型。从模块外部来看,本模块的输入/输出端口所连接到的外部的变量,也必须是线网类型
  • 关于线网类型:我们平常所说的线网类型,常常是指wire型。实际上,在Verilog里面,线网类型不止有wire型,还包括wand,wor,tri,triand,triort 和 trireg 等等。在初学阶段,我们只需要关注wire类型就可以了,暂时地可以将线网与wire看作是完全相同的概念。然而,随着我们的学习经验的增长,当我们想要进阶学习Verilog,以便可以更加灵活地去设计芯片的时候,我们需要渐渐地去接触Verilog里面的一些个平时不常用的知识,包括线网的其余的数据类型。

Verilog的知识点,我的感觉,它是非常地零散细碎的。建议大家可以建立一个备忘录,学习笔记啥的。或者,你可以在自己的电脑上,设置一个文件夹,专门用来记录Verilog的一些个零散的知识点。注意哦,这种记录,不是要你全面地记录,不是要你去写书。是把你觉得比较零散,容易忘记,或者是真的比较重要的东西,给记录下来。

三.   指令码信号的导出与译码使能

指令码信号,就是我们在第一分节的末尾提到的【instruct_code_wire】,我们来看看下图所示的代码。

图4

如图4所示,第13行里面,我们声明了指令码信号。

我们再来看一看,我们是如何将其导出的。

图5

如图5所示,我们在一个always块里面,同时处理了译码使能信号【decode_en】和指令码信号【instruct_code】。指令码信号【instruct_code】,它是本模块的输出端口,是reg类型的一个变量。大家可以回到上面的图3去看一看指令码信号的端口声明情况。

当系统复位信号到来的时候,译码使能信号【decode_en】与指令码信号【instruct_code】均被设置为0。然后呢,当检测到rd_en_d1信号为1的时候,译码使能被非阻塞赋值为有效的高电平,而指令码信号【instruct_code】被非阻塞赋值为【instruct_code_wire】。

也就是,当检测到【rd_en_d1】为1的时候,wire型变量【instruct_code_wire】,IP核的输出数据信号【q】所连接到的【instruct_code_wire】变量,被赋值给了输出端口【instruct_code】,予以导出了。

然后呢,在else分支里面,我们看到,指令码信号【instruct_code】保持不变,而译码使能信号【decode_en】清零了。也就是,译码使能信号,仅仅维持了一个时钟周期的有效高电平,且仅仅是检测到【rd_en_d1】为1的时候,才维持着一个时钟周期的高电平。

在本系统里面,这种仅维持一个时钟周期的高电平的信号,是很多的。请大家注意这种代码的特点。这种信号,一般地,都是说,复位信号起作用时清零,else分支里面也是清零,仅仅是符合某一个条件的时候才会变为1,并且所检测的那个条件也仅仅是维持着一个时钟周期的高电平。

关于【rd_en_d1】,其实它是【rd_en】信号延时一个时钟周期的信号副本。我们来看下面的代码来回顾这一点。

图6

关于rd_en,rd_en_d1与rd_en_d2的延时逻辑,我在之前的章节里面有讲过。忘记了的,请大家点击下述链接来复习这种延时逻辑。

rd_en的延时逻辑

到了这里,我们差不多应该梳理一下取指令模块的执行逻辑了。

四.   取指令模块的总体执行逻辑

所谓的总体执行逻辑,指的是波形时序变化情况。

想要研究某一个模块的波形时序变化,最好是用波形时序图来表示。然而,在我这里,我不太会画波形图。即使能画好波形图,我大概也是更喜欢用文字或者是电子表格的形式来说明问题。

在本模块里面,开始工作的起始点,是取指令使能信号变为有效的高电平。在我们的系统里面,每一个指令的执行周期里面,取指令使能信号仅有一次变为高电平的时候,且仅仅维持一个时钟周期的高电平。

取指令使能信号,就是输入端口中的【get_inst_en】变量。

然后呢,另外的两个标识着时序的变量,是【rd_en】与【rd_en_d1】

我们来将这个逻辑给展现出来。

首先呢,外部模块传入的取指令使能信号【get_inst_en】变为高电平。

当检测到【get_inst_en】变为高电平以后,【ip_buf】通过非阻塞赋值的方式缓存输入端口【ip】的值,【rd_en】则是被非阻塞赋值为1。

【rd_en】与【ip_buf】,分别连接到 RAM IP 核实例的读使能信号【rden】与地址信号【address】。

在我们的系统中,我们所生成的 RAM IP 核,它是说,当某一个时钟上升沿检测到rden为高电平以后,在下一个时钟上升沿到来时,数据就会通过输出端口【q】予以输出。在我们的代码里,接收【q】的数据的,是wire型变量【instruct_code_wire】。

在【get_inst_en】变为高电平的下一个时钟上升沿,【rd_en】变为1。【rd_en】的有效高电平会传给 RAM IP 核实例。RAM IP 核实例会去进行着读取数据的操作。同时,在检测到【rd_en】为1以后,rd_en_d1会被非阻塞赋值为1。

在rd_en变为高电平的下一个时钟上升沿,rd_en_d1变为高电平,这时 RAM IP 核的数据已经出现在数据输出端口【q】里面了。【q】的数据由【instruct_code_wire】实时接收。由于【instruct_code_wire】变量里面已经存放着有效的指令码数据了,因此,将指令码信号传给译码器以进行译码的条件已经成熟。所以,当检测到【rd_en_d1】为1的时候,译码使能信号【decode_en】被非阻塞赋值为1,本模块的输出端口【instruct_code】被非阻塞赋值为【instruct_code_wire】。

以上,就是取指令模块的代码执行逻辑了。

不知道大家听懂了没,接下来,我再将上面所说的东西,用流程图来略作整理。流程图如下。

图7,本模块执行逻辑流程图

结束语

到了这里,取指令模块,其实就差不多是讲完了的。其实还有一点小内容没有讲。那就是,我在生成RAM IP核的同时,我也创建了一个内存初始化文件,用来为 RAM DISK写入一些个固定的指令码。

关于内存初始化文件的写入方法,我就先不讲了。等到讲完了译码模块以后,我们再来将内存初始化文件的问题。那个时候,我们再来看看,我们究竟是往RAM DISK里面写了什么指令码。

本节占用的课节数不少。希望大家能跟上。

在本节,我大概是第一次使用流程图。以后,可能我会多去使用流程图的。希望大家能够渐渐地适应我的讲课方法,学会去看流程图,表格啥的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2197698.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SOMEIP_ETS_174: SD_Unknown_Option_type

测试目的: 验证DUT能够拒绝一个引用了未知选项类型的SubscribeEventgroup消息,并以SubscribeEventgroupNAck作为响应。 描述 本测试用例旨在确保DUT遵循SOME/IP协议,当接收到一个引用了未知选项类型的SubscribeEventgroup消息时&#xff0…

Solidedge二次开发(C#)-将dft文件转换为dwg格式文件

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、在Solid Edge中创建一个par文件3、通过二次开发将dft转换为dwg4、结果显示1、前言 Solid Edge提供了将dft转换为dwg的接口,也即是保存功能。有时在不显示Solid Edge界面的情况下,将其…

【C语言刷力扣】1436.旅行终点站

题目: 解题思路: 两层循环查找,第一次循环中初始化 destination 为 path中每次旅行的终点作为最终的终点。二次循环查找当前 destination ,若是作为某次旅行的起点,说明不是最后的终点。 char* destCity(char ***paths…

[Linux#62][TCP] 首位长度:封装与分用 | 序号:可靠性原理 | 滑动窗口:流量控制

目录 一. 认识TCP协议的报头 1.TCP头部格式 2. TCP协议的特点 二. TCP如何封装与分用 TCP 报文封装与解包 如何封装解包,如何分用 分离有效载荷 隐含问题:TCP 与 UDP 报头的区别 封装和解包的逆向过程 如何分用 TCP 报文 如何通过端口号找到绑…

多功能快捷回复软件

各位亲爱的客服宝宝们,每天面对大量的客户咨询,您是否还在手动一个一个地打字回复呢?别担心,我们为您带来了一款多功能快捷回复软件——客服宝。有了它,您的工作将变得无比轻松! 客服宝是一款集成了内容存储…

网络编程(14)——基于单例模板实现的逻辑层

十四、day14 今天学习如何通过单例模板实现逻辑层 1. 利用C11特性封装单例模板 和上一节设计的单例模板有些不同,本节设计的单例模板利用了以下四个C11新特性,优化了代码 unique_lock和lock_guard once_flag和call_once std::function condition_v…

1打家劫舍三部曲

刷题刷题找工作! s198.打家劫舍 动态规划:开始打家劫舍! dp数组表示到第i家的最高金额 dp递归公式,要么抢劫这家,加上i-2所抢的钱,要么不抢,保留上一家的。 …

linux中的火墙优化策略

1.火墙介绍 1. netfilter 2. iptables 3. iptables | firewalld 2.火墙管理工具切换 在rocky9 中默认使用的是 firewalld firewalld -----> iptables dnf install iptables - services - y systemctl stop firewalld systemctl disable firewalld systemctl mask fi…

Vue3 使用 pinia

什么是Pinia Pinia是 Vue 的存储库,它允许您跨组件/页面共享状态,与vuex功能一样。 准备 安装 npm install pinia 或者 yarn add pinia使用 首先修改main.ts文件 main.ts import ./assets/main.cssimport { createApp } from vue import App from…

HTB:Tactics[WriteUP]

目录 连接至HTB服务器并启动靶机 1.Which Nmap switch can we use to enumerate machines when our ping ICMP packets are blocked by the Windows firewall? 2.What does the 3-letter acronym SMB stand for? 3.What port does SMB use to operate at? 4.What comma…

Comfyui segmentAnythingUltra V2报错

🎅问题表现及解决方案 Comfyui segmentAnythingUltra V2报错,找不到VITMatte模型,这个报错报的比较模糊,所以花了一点时间找模型。 简单来说,到huggingface上: https://huggingface.co/hustvl/vitmatte-s…

麒麟系统串口配置篇

麒麟系统串口配置篇 1.配置串口驱动(编译/动态加载串口) 解压文件夹,然后在解压后的文件夹所在目录,右键选择打开终端,依次执行以下命令: 以麒麟系统下的CH341串口驱动为例,解压CH341SER_LINUX.zip sudo…

【微服务】网关 - Gateway(下)(day8)

网关过滤工厂 在上一篇文章中,主要是对网关进行了一个总体的介绍,然后对网关中的断言进行了一个描述。在这篇文章中,主要是对网关中的最后一大核心——过滤进行介绍。 当客户端发送过来的请求经过断言之后,如果还想在请求前后添…

如何在 MySQL 中处理 BLOB 和 CLOB 数据类型

在 MySQL 数据库中,BLOB(Binary Large Object)和 CLOB(Character Large Object)数据类型用于存储大量的二进制数据和字符数据。本篇文章我们来一起看看如何在 MySQL 中处理 BLOB 和 CLOB 数据类型,并加入如…

7.3美团—Java日常实习面经

7.2晚上投的,发邮件约到了7.3晚上 总时长1小时10分钟左右 自我介绍 拷打项目30min 缓存三兄弟 Redis除了缓存,还能做什么 Redis的数据结构,什么时候用哈希,什么时候用字符串 线程池的执行流程 MySQL索引的数据结构 聚簇索引…

基于PHP+uniapp微信小程序的个性化影视推荐系统的设计54lfb

目录 项目介绍技术栈和环境说明具体实现截图php技术介绍文件解析微信开发者工具HBuilderXuniapp开发技术简介解决的思路性能/安全/负载方面数据访问方式PHP核心代码部分展示代码目录结构解析系统测试详细视频演示源码获取 项目介绍 首先要进行需求分析,分析出电影信…

JUC高并发编程7:辅助类

1 减少计数CountDownLatch 1.1 CountDownLatch 概述 CountDownLatch 是 Java 并发包(java.util.concurrent)中的一个同步工具类,用于在多个线程之间进行协调。它允许一个或多个线程等待其他线程完成一组操作。 1.1.1 主要方法 CountDownL…

【重学 MySQL】六十二、非空约束的使用

【重学 MySQL】六十二、非空约束的使用 定义目的关键字特点作用创建非空约束删除非空约束注意事项 在MySQL中,非空约束(NOT NULL Constraint)是一种用于确保表中某列不允许为空值的数据库约束。 定义 非空约束(NOT NULL Constra…

基于LORA的一主多从监测系统_AHT20温湿度传感器

1)AHT20温湿度传感器 这个传感器,网上能找到的资料还是比较多的,我们使用的是HAL硬件i2c,相比于模拟i2c,我们不需要过于关注时序问题,我们只需要关心如何获取数据以及数据如何处理,下面以数据手…

指针——指针数组、数组指针

&#xff08;一&#xff09;指针数组 1、本质&#xff1a;指针数组的本质任然是数组 2、基本格式&#xff1a;int* arr[5] 3、应用&#xff1a;如尝试使用指针来模拟二维数组 先来看代码 #include<stdio.h> //指针数组——模拟实现二维数组 int main() {int a[5] {…