当前位置: 首页 > article >正文

简易CPU设计入门:控制总线的剩余信号(一)

项目代码下载

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

CSDN文章:下载本项目代码

上述链接为本项目所依据的版本。

在讲解过程中,我还时不时地发现自己在讲解与注释上的一些个错误。有时,我还会添加一点新的资料。在这里,我将动态更新的代码版本发在下面的链接中。

Gitee项目:简易CPU设计入门项目代码:

讲课的时候,我主要依据的是CSDN文章链接。然后呢,如果你为了获得我的最近更新的版本,那就请在Gitee项目链接里下载代码。

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

本节前言

在之前的十几节中,我们讲解了算术逻辑操作。从本节开始,我们将控制中心模块里面,控制总线的剩余信号给讲完。

本节的代码,位于【......\cpu_me01\code\Ctrl_Center\】路径里面。主要讲解的代码,是【ctrl_center.v】。

我们来看一下控制总线的信号列表。

如果【ctrl_bus】的取值范围是【0 <= ctrl_bus < 4】,表示本次操作为寄存器写操作。
如果【ctrl_bus】的取值范围是【4 <= ctrl_bus < 8】,表示本次操作为寄存器读操作。
如果【ctrl_bus】的取值范围是【8 <= ctrl_bus < 12】,表示本次操作为内存写操作。
如果【ctrl_bus】的取值范围是【12 <= ctrl_bus < 16】,表示本次操作为内存读操作。
如果【ctrl_bus】的取值范围是【16 <= ctrl_bus < 20】,表示本次操作为立即数读操作。
如果【ctrl_bus】的取值范围是【20 <= ctrl_bus < 24】,表示本次操作为算术逻辑运算。
如果【ctrl_bus】的取值范围是【24 <= ctrl_bus < 28】,表示本次操作为更新指令指针寄存器【ip】。
如果【ctrl_bus】的取值范围是【28 <= ctrl_bus < 32】,表示本次操作为停机操作。

以上的块引用部分的内容,就是控制总线的全部信号了。我们之前讲了一部分,它们是【0 <= ctrl_bus < 16】的范围的信号。

这样一来,我们所剩下的,是立即数读控制信号,算术逻辑运算控制信号,更新指令指针寄存器ip的控制信号,还有指示停机的控制信号。

本节,我们要去讲解的,是立即数读控制信号。

一.    系统总线与内部寄存器

本节所要讲解的东西,主要是跟立即数的读操作有关。我们来看一看系统总线。

图1

图1中所示的代码,位于控制中心模块的端口声明部分。它们分别是我们的仿真CPU项目中的控制总线,地址总线,数据总线,它们都属于是系统总线。

图2

在图2中,65行到67行,分别是用来对控制总线、地址总线和数据总线进行缓存的变量。为啥要进行缓存呢?因为,三大系统总线中的信号的有效期,仅有一个时钟周期,稍纵即逝。而我们又需要在不同于总线数据有效期的时间里使用它们,所以呢,我们就声明了三个变量,用来将三大总线的数据给缓存下来,以便长久使用。

在图2的 64 行,我们声明了一个 reg 类型的数组,如下面的代码块所示。

reg [15:0] inner_reg[3:0];

它的含义是,声明四个 reg 类型的向量,每一个向量都是16位的,其中最高有效位是位15,最低有效位是位0。四个向量,用数组索引来引用。四个向量的引用方法为:inner_reg[0],inner_reg[1], inner_reg[2],inner_reg[3]。

这四个向量,是我们的系统中的四个内部寄存器。注意,它们是内部寄存器,而非通用寄存器。

图2的68行申请的变量,它在代码中,用来作为访问内部寄存器的索引变量。由于,每当新指令任务到来之时,要访问的内部寄存器的索引位于控制总线【ctrl_bus】中,所以,我将这个用来访问内部寄存器的索引变量命名为【ctrl_bus_index】。

二.    imd_read_flag 组节拍变量

图3

图3所示的几个变量,便是 rimd_read_flag 组节拍变量。从名字上可以大致猜到,【imd_read_flag】是主要的变量,【imd_read_flag_d1】比【imd_read_flag】延后一个时钟周期,【imd_read_flag_d2】比【imd_read_flag_d1】延后一个时钟周期。

是否如此呢?我们来看看下图所示的代码。

图4

从图4来看,的确是说,【imd_read_flag】是主要的变量,【imd_read_flag_d1】比【imd_read_flag】延后一个时钟周期,【imd_read_flag_d2】比【imd_read_flag_d1】延后一个时钟周期。

三.    new_task 变量与缓存系统总线的有效数据

这个变量是我在控制中心模块里申请的一个 wire 型变量,如下图所示。

图5

关于这个变量的含义,本节,我们依然是先不去深究。我们需要了解它的基本含义。如果它为1,就代表了一个新的微指令的开始,或者是代表了一个新的微操作的开始。

当 new_task 为1的时候,三大系统总线均含有有效数据。三大总线中的数据与 new_task 一样,有效数据的存在时间只有一个时钟周期。

对于 new_task 变量,它的值我们不需要保存。而对于三大系统总线的有效数据,我们是需要将其保存下来的,因为,它们正好处于有效期的时候,我们可能暂时用不到,但是 后面会有用,所以,我们需要将其缓存下来。

图6

在图6里面,我们可以看到三大系统总线缓存变量与内部寄存器索引变量【ctrl_bus_index】的逻辑。

在系统复位时,三大系统总线缓存变量与内部寄存器索引变量【ctrl_bus_index】均被非阻塞赋值为高阻态值。在平时,先来无事时,也就是在【else】分支里面,它们都保存着各自的现有值不变。

每当 new_task 为1时,也就是,每当开启了一个新的微指令的时候,三大系统总线缓存变量会缓存各自对应的系统总线的有效数据。同时呢,内部寄存器索引变量【ctrl_bus_index】会将控制总线【ctrl_bus】的位1与位0给缓存下来。

也就是,在每一个新的微指令开启的时候,控制总线的位选信号【ctrl_bus[1:0]】指定了本次的微指令需要访问的内部寄存器的索引号。

在这里,当 new_task 为1时,也就是,每当开启了一个新的微指令的时候,数据总线上所保存的,是本次要读取的立即数的值。经过了缓存操作以后,则数据总线缓存变量【data_bus_buf】里面保存了本次要去读取的立即数的值。

在我们的系统中,有四个内部寄存器。这样一来,由控制总线【ctrl_bus】发布过来的每一个控制信号,其实都是4个一组。原因在于,每一个控制信号都需要指定要去访问的内部寄存器。

通用寄存器读操作,需要指定要去使用的内部寄存器索引。写操作,也需要指定本次要访问的内部寄存器的索引,其他的一些个控制信号,也是如此的。

对于每一种操作,无论是通用寄存器的读写操作,还是内存读写,算术逻辑操作,它们都含有索引字段。而索引值,是控制总线的位1与位0,所以,索引字段的值的范围,是0,1,2,3。

四.    imd_read_flag 组节拍变量的逻辑

 首先呢,我们来看 imd_read_flag 的逻辑。

图7
always @(posedge sys_clk or negedge sys_rst_n)
	if (sys_rst_n == 1'b0)
		imd_read_flag <= 1'b0;
	else if ((new_task == 1'b1) && (ctrl_bus >= 16'd16) && (ctrl_bus < 16'd20))
		imd_read_flag <= 1'b1;
	else
		imd_read_flag <= 1'b0;

图7中所示,是关于 imd_read_flag 的逻辑。它的逻辑是,系统复位与处于【else】分支时,它都是0值。每当系统检测到【(new_task == 1'b1) && (ctrl_bus >= 16'd16) && (ctrl_bus < 16'd20)】条件满足时,则 imd_read_flag 会被非阻塞赋值为 1。

 new_task 变量我们讲过了,它为1,表示开启了一个新的微指令操作,标志着新任务的开始。而当 new_task 为1时,控制总线【ctrl_bus】的值,则是表示了本次微指令的功能。

根据本节的前言部分的控制总线信号的列表信息,如果【ctrl_bus】的取值范围是【16 <= ctrl_bus < 20】,且 new_task 为 1 时,表示开启了一个新任务,这个新任务的内容,为立即数读操作。

想要执行立即数读操作,我们还需要指出,要将读出来的立即数保存在那里。那么,这个数据在哪里呢?这个数据,目前是保存在四个内部寄存器中的某一个里面。具体保存位置的有效索引号,保存在【ctrl_bus[1:0]】之中。我们将【ctrl_bus[1:0]】赋给【ctrl_bus_index】,正是为了方便地引用这个索引号。

五.    立即数读操作

图8
图9
图10

关于立即数读操作,它的主要的操作逻辑,是包含在内部寄存器的逻辑里面。关于内部寄存器的逻辑,它比较复杂,篇幅较大,所以,在这里,我只是截取了对于本节来讲有用的部分。

由图8和图10,我们可以看到,当系统复位时,四个内部寄存器变为0值。当处于【else】分支,也就是闲来无事时,四个内部寄存器保存现有值不变。

而根据图9中的532行和533行的代码,当检测到立即数读标志【imd_read_flag】为有效的高电平时,则将数据总线缓存变量【data_bus_buf】中的值赋给 inner_reg[ctrl_bus_index] 。在这里,数据总线缓存变量中的值,为本次要读取的立即数的值。而这个立即数的保存位置,便是控制中心模块的某一个内部寄存器。所保存的目标内部寄存器的索引号,由【ctrl_bus_index】来指定。

这样一来,如下的语句,实际上,就将本节所要执行的核心操作。

inner_reg[ctrl_bus_index] <= data_bus_buf;

六.    操作时序梳理

我们来梳理一下立即数读操作的操作时序。

我们还是来设定一个0号时钟上升沿。

(一)0号时钟上升沿

在0号时钟上升沿,系统检测到,【new_task == 1】,并且【8 <= ctrl_bus < 12】。这是我们的本节的操作时序的基准。

于是,在0号时钟上升沿之后的非阻塞赋值阶段,根据图6,三大系统总线缓存变量将三大总线的有效值给缓存了下来。注意,当【new_task == 1】条件满足之时,三大总线上,的确是含有着有效的数据。同时,【ctrl_bus[1:0]】的值被赋给了【ctrl_bus_index】。对于立即数读操作来讲,本次所要读取的立即数,保存在数据总线【data_bus】里面,或者说,位于地址总线缓存变量【data_bus_buf】里面。读取的立即数,需要被保存在某一个内部寄存器之中,而【ctrl_bus[1:0]】则是在 new_task 为1时指定了这个内部寄存器的有效索引号。

在0号时钟上升沿之后的非阻塞赋值阶段,根据图7,【imd_read_flag】被赋值为1。

(二)1号时钟上升沿

在1号时钟上升沿,系统检测到【imd_read_flag == 1】。

在【imd_read_flag == 1】条件满足之时,我们需要将本次要读取的立即数的数值,保存在某一个内部寄存器里面。这个待被读取的立即数的数值,此时保存在【data_bus_buf】里面。而保存的目标内部寄存器,其索引号由【ctrl_bus_index】来指定。

图9中的532和533行代码执行了本次操作。

图9副本

立即数读操作,就是这么简单。

结束语

立即数读操作,感觉还好。我依然是将之前写过的文章,加以复制和修改,而形成了本节的文章内容。

如果,真的是要我从头到尾来写作,我也会觉得烦的。因为,好多的内容,我第一次写的时候,就觉得超级烦躁。

有可能,我这里所写的文章,还存在着问题。以后,慢慢地来修改了。


http://www.kler.cn/a/522265.html

相关文章:

  • 再见了流氓软件~~
  • 免杀国内主流杀软的恶意样本分析
  • 【HarmonyOS之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(一)
  • 51单片机开发:定时器中断
  • 使用kitty terminal遇到的‘xterm-kitty‘: unknown terminal type.
  • Java面试题2025-并发编程进阶(线程池和并发容器类)
  • 粒子群算法 笔记 数学建模
  • 真正理解std::move
  • < OS 有关 > 阿里云 几个小时前 使用密钥替换 SSH 密码认证后, 发现主机正在被“攻击” 分析与应对
  • 关于Dubbo的面试题概念原理配置及代码
  • 【信息系统项目管理师-选择真题】2012上半年综合知识答案和详解
  • 十三先天记
  • 【面试】【前端】前端浏览器考题总结
  • 深度学习|表示学习|卷积神经网络|输出维度公式|15
  • HTML 符号详解
  • 安全策略初始实验
  • 30289_SC65XX功能机MMI开发笔记(ums9117)
  • 深入理解动态规划(dp)--(提前要对dfs有了解)
  • 【OMCI实践】ONT上线过程的omci消息(二)
  • leetcode 209. 长度最小的子数组
  • AI 模型评估与质量控制:生成内容的评估与问题防护
  • Web开发 -前端部分-CSS3新特性
  • unity学习20:time相关基础 Time.time 和 Time.deltaTime
  • 基于Django的微博舆情分析系统的设计与实现
  • 【算法与数据结构】动态规划
  • RTOS面试合集