简易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 < 28】的范围的信号。

这样一来,我们所剩下的,就只有指示停机的控制信号了。

本节,我们要去讲解的,便是用来指示停机的控制信号。

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

本节所要讲解的东西,主要是跟停机信号有关。我们来看一看系统总线。
图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】。

二. stop_flag 组节拍变量

图3

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

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

从图4来看,的确是说,【stop_flag】是主要的变量,【stop_flag_d1】比【stop_flag】延后一个时钟周期,【stop_flag_d2】比【stop_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给缓存下来。对于停机指令而言,这个索引号,是为了在将来作为一个扩展,以指定停机方式。不过,目前,在代码逻辑方面,我并未针对停机指令的停机方式,作出任何规定,而只是将这个索引号给缓存了下来。

四. stop_flag 组节拍变量的逻辑

首先呢,我们来看 stop_flag 的逻辑。
图7

复制代码
always @(posedge sys_clk or negedge sys_rst_n)
	if (sys_rst_n == 1'b0)
		stop_flag <= 1'b0;
	else if ((new_task == 1'b1) && (ctrl_bus >= 16'd28) && (ctrl_bus < 16'd32))
		stop_flag <= 1'b1;
	else
		stop_flag <= 1'b0;

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

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

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

想要执行停机操作,我们还需要指出,停机的方式是什么。那么,这个用来表示停机方式的数据在哪里呢?这个数据,目前是保存在地址总线变量的位1和位0,保存在【ctrl_bus[1:0]】之中,相当于内部寄存器的有效索引号。我们将【ctrl_bus[1:0]】赋给【ctrl_bus_index】,正是为了方便地引用这个索引号,引用这个停机方式。

五. 指示停机

图8

根据图8,我们可以了解到【exe_running】变量的逻辑。

根据165行与166行的代码,当系统复位时,【exe_running】变量被赋予 0 值。而根据171和172行代码,在【else】分支里面,也就是闲来无事之时,【exe_running】保持现有值不变。

根据167和168行代码,当【init_done】为1,也就是完成了系统初始化工作的时候,exe_running

会变为1。此时,【exe_running】从系统复位时候的 0 值第一次变为 1 值。

然后呢,根据169到170行的代码,当【stop_flag】为 1 时,【exe_running】会变为 0 值。

【exe_running】变为 0 值,表示CPU停止运行,结束运行状态。那么,当【exe_running】变为 0 时,CPU 的运行会发生什么变化呢?
图9

对于图9,我们看到,在406到407行里面,当【exe_running】变为0时,指令指针寄存器 ip 会被清零。我们再看。
图10

在图10之中,我们看一看【get_inst_en】的逻辑。

根据175和176行的代码,在系统复位时,取指令使能信号【get_inst_en】被清零。根据181行和182行代码,在【else】分支里面,也就是在闲来无事之时,取指令使能信号【get_inst_en】也是被清零了。

何时变为1呢?第1种情况,根据177和178行代码,当检测到【init_done】为1时,也就是,系统初始化工作完成了的时候,取指令使能信号【get_inst_en】会变为1。初始化完成是在系统复位以后。系统复位时,取指令使能信号【get_inst_en】被赋予 0 值。而在完成了系统初始化工作以后,取指令使能信号【get_inst_en】又被赋予1值,且是第1次被赋予1值。接下来,很快地,系统会处于【else】分支,也就是,取指令使能信号【get_inst_en】很快地又会变为0值。取指令使能信号【get_inst_en】为1的时间,仅仅是一个时钟周期而已。

第2种情况,根据179和180行代码,当【job_ok_d1 == 1 && exe_running == 1】条件满足之时,取指令使能信号【get_inst_en】才会被赋予1值。那么,如果我们破坏了第2种情况的成立条件,让【exe_running】为0,结果会如何?结果就是,179行的条件得不到满足,系统会一直处于181行的【else】分支的情况,因此,取指令使能信号【get_inst_en】会一直为0,不会再变为1值。

综上所述,当我们执行了停机指令,致使【exe_running】变为0值,这会导致两个结果。第1,指令指针寄存器 ip 被清零。第2,取指令使能信号【get_inst_en】会一直为0,也就是,系统不会再去取指令了。不去取指令,当然也就不会有译码与执行了,此时,CPU就真的是停止了。

结束语

到了这里,我想,我们又算是完成了一个不错的任务了。

对控制总线的控制信号的讲解,我认为是一件比较麻烦的事情。

好在,我这里,可以通过复制加修改的方法,来开展着写作。但是呢,在你那里,学习效果如何,我就不知道了。希望你能够学习好这一块的知识。

CPU的设计,是一个有趣的事情。

相关推荐
虾..26 分钟前
Linux 软硬链接和动静态库
linux·运维·服务器
Evan芙43 分钟前
Linux常见的日志服务管理的常见日志服务
linux·运维·服务器
iCxhust2 小时前
8255 PORTC 按键输入测试
单片机·嵌入式硬件·微机原理
hkhkhkhkh1232 小时前
Linux设备节点基础知识
linux·服务器·驱动开发
HZero.chen4 小时前
Linux字符串处理
linux·string
张童瑶4 小时前
Linux SSH隧道代理转发及多层转发
linux·运维·ssh
汪汪队立大功1234 小时前
什么是SELinux
linux
石小千4 小时前
Linux安装OpenProject
linux·运维
柏木乃一4 小时前
进程(2)进程概念与基本操作
linux·服务器·开发语言·性能优化·shell·进程
Lime-30904 小时前
制作Ubuntu 24.04-GPU服务器测试系统盘
linux·运维·ubuntu