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

简易CPU设计入门:算术逻辑单元(四)

项目代码下载

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

CSDN文章:下载本项目代码

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

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

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

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

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

本节前言

上一节的文章,我主要是采用【复制 + 修改】的方法来完成的。本节,依然会采用这种方法。因为,整个的算术逻辑操作的代码思路,其实是一样的。

在本节,我们要来讲解乘法运算。

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

一.    端口声明

我们来看一下端口声明部分的代码。

图1

【multi_cell.v】里面的端口声明,和相同路径里面的【ALU.v】里面的端口声明是一样的。

第3行和第4行是系统时钟与系统复位信号。系统时钟信号,其频率为50MHz。系统复位信号为低电平有效。

第6行到第8行,它们对应着控制中心里面的3个内部寄存器。其中呢,【oprand0】对应着控制中心里面的0号内部寄存器,【oprand1】对应着控制中心里面的1号内部寄存器,【oprand2】对应着控制中心里面的2号内部寄存器。

我们可以看一下控制中心里面的代码。

图2,控制中心里面的代码

图2里面的代码,是控制中心模块里面的代码。它显示了三个操作数变量与三个内部寄存器的绑定代码。在这里,三个操作数变量【oprand0-2】是如何声明的,以及它们是如何与本节的图1中的三个操作数变量【oprand0-2】相联系的,我就不去展示了。有兴趣的话,你可以自己去查看。

第10行到第12行,是本系统中的三大内部总线。第14行,是用于发送【成功完成】信号的总线。

二.    变量声明与总线绑定

图3

第17行和第18行,是用来计时的两个变量。其中呢,【cal_time_d1】延后【cal_time】一个时钟周期。

第20行,它是用来保存计算结果的。本节的模块名称为【multi_cell】,所以呢,这里的 res,保存的是乘法法运算的运算结果。

第21行和第22行,分别是端口声明中的【work_ok_inner】和【data_sig_inner】总线变量的代理变量。

我们来看一下两个总线代理与对应的总线变量的绑定代码。

图4

图4显示了这种总线变量与它的代理变量的绑定代码。

三.    计算

图5
always @(posedge sys_clk or negedge sys_rst_n)
	if (sys_rst_n == 1'b0)
		res <= 1'b0;
	else if ((ctrl_sig_inner[4] == 1'b1) && (addr_sig_inner == 6'd2))
		res <= oprand1 * oprand0;
	else
		res <= res;

图5中的代码,就是执行计算的代码,它也是关于 res 的逻辑的代码。

res的逻辑是,在系统复位时,它是被赋值为0值。在【else】分支里面,也就是闲来无事时,它是保持现有值不变。而每当满足【(ctrl_sig_inner[4] == 1'b1) && (addr_sig_inner == 6'd2)】条件之时,它就执行计算,将【oprand1 * oprand0】的运算结果赋给 res 变量。

在这里,【ctrl_sig_inner[4] == 1'b1】是什么意思?

我们还是来复习一下内部控制总线的知识。

图6
/*********************************************
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:保留
还有一种运算叫做读取立即数,将立即数放入内部寄存器。
此运算不需要通过内部信号的参与。
************************************************/

根据图6,当内部控制总线的位4为1,而其余位都是0值时,表示控制中心向算术逻辑单元发布了算术逻辑运算使能信号。

算术逻辑运算,可以包含着多种运算类型。加法算一个,减法也算是一个。那么,如何来区分不同的运算类型呢?这个时候,我们就通过内部地址总线【addr_sig_inner】来表示不同的运算种类。

当本模块收到的【addr_sig_inner】总线信号的值是2时,就表示执行乘法运算。

所以呢,图5所示代码的意思是,当【multi_cell】模块收到了算术逻辑运算使能信号,并且根据内部地址信号值,确定本次的运算类型为乘法运算时,则在非阻塞赋值阶段,将端口中的操作数1乘以操作数0,结果放在 res 变量里面。

四.    计时变量

图7
always @(posedge sys_clk or negedge sys_rst_n)
	if (sys_rst_n == 1'b0)
		cal_time <= 1'b0;
	else if ((ctrl_sig_inner[4] == 1'b1) && (addr_sig_inner == 6'd2))
		cal_time <= 1'b1;
	else
		cal_time <= 1'b0;

在图7里面,显示了【cal_time】的逻辑。在系统复位与【else】分支里面,它都是被赋予0值。而在满足了【(ctrl_sig_inner[4] == 1'b1) && (addr_sig_inner == 6'd2)】条件之时,它就被赋予 1 值。

这个条件,它其实和第三分节中的 res 被重新计算的条件一样的。它是说,在收到了算术逻辑运算使能信号,且根据内部地址总线的值,判断出,本次执行的运算类型为乘法运算时,则将【cal_time】变量赋值为 1 值。

然后呢,我们再来看【cal_time_d1】的逻辑。

图8

图8显示了【cal_time_d1】的逻辑。它是将【cal_time】延后一个时钟周期,所形成的信号。

五.    总线逻辑

图9

图9显示了本模块的两个总线变量【data_sig_inner】和【work_ok_inner】的对应代理变量【data_sig_represent】和【work_ok_represent】的逻辑。

当检测到【cal_tijme == 1】条件满足之时,在此后的非阻塞赋值阶段,【data_sig_represent】和【work_ok_represent】被赋予 0 值。

当检测到【cal_tijme_d1 == 1】条件满足之时,在此后的非阻塞赋值阶段,【data_sig_represent】和【work_ok_represent】分别被赋予各自的有效值。其中,【data_sig_represent】被赋予 res 的值,也就是,将本模块的算术逻辑运算结果,保存下来。而【work_ok_represent】被赋予 1 值。

在这里,两大代理变量分别与本模块的两大总线变量相绑定。而本模块的两大总线变量,又与各自的同名的内部总线相连接。所以呢,向代理写入了什么值,就相当于,向对应的内部总线传递了什么值。

六.    总体逻辑

本模块的总体逻辑是,当检测到了算术逻辑使能信号,且运算类型为乘法运算时,则执行操作数1乘以操作数0的乘法运算,并将运算结果暂时保存在 res 变量里面。同时,将【cal_time】设置为 1 值。

然后呢,在检测到【cal_time == 1】条件满足之时,对两大内部总线【data_sig_inner】和【work_ok_inner】分别写入 0 值。同时,将【cal_time_d1】设置为 1 值。

在检测到【cal_time_d1 == 1】条件满足之时,对两大内部总线【data_sig_inner】和【work_ok_inner】分别传递有效值。其中呢,【data_sig_inner】被传入运算结果 res 的值,【work_ok_inner】被传递 1 值。

【work_ok_inner】被写入 1 值,表示本次操作成功完成。在【work_ok_inner】被写入 1 值的同时,运算结果也同时被传到【data_sig_inner】总线里面了。所以呢,成功完成信号【work_ok_inner】与运算结果【data_sig_inner】同时有效。控制中心,会从这两个内部总线接收到消息,并且会进行相应的处理的。

结束语

本节内容,应该不难。如果你理解了我之前所讲解的加法与减法操作的逻辑,本节,其实并不难。

在你熟悉了之前的加法与减法的讲解内容以后,有可能你会觉得,本节的内容,有点废话练习的意思。

其实,在学习中,有些时候,这种重复性的东西,对于学习是有好处的。重复几次,可以令你对有关知识更为熟练,印象也更为深刻。

在我的讲解中,基本上,我是喜欢采用比较详细的讲解方式。详细,也就意味着,好多的讲解,有可能,你已经是很熟悉了。

学习,是一种在旧知识的基础上,积累新知识的过程。如果,我们在学习的时候,旧知识有着足够的比例,因而在学习新知识的时候,并不觉得累,我觉得是好的。


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

相关文章:

  • Elastic-Job相关
  • pivot函数:数据行转换为列名(行转列)[oracle]
  • Spring 中的常用注解
  • AR 眼镜之-拍照/录像动效切换-实现方案
  • Java手动打印执行过的sql
  • 深度学习-81-大语言模型LLM之基于litellm与langchain与ollama启动的模型交互
  • 解决WordPress出现Fatal error: Uncaught TypeError: ftp_nlist()致命问题
  • 复古黑白恐怖迷幻眼睛纹身刺青插画潮流艺术png免抠拼贴图片素材Mindrift. Psychedelic Illustrations
  • Springboot——钉钉(站内)实现登录第三方应用
  • C++实现设计模式---访问者模式 (Visitor)
  • 解决 VSCode 调试时 Python 文件出现相对路径报错问题‘FileNotFoundError’
  • Swift 趣味开发:查找拼音首字母全部相同的 4 字成语(上)
  • 智慧充电桩可视化管理提升能源效率
  • xml简介
  • Docker中安装Tailscale方法一
  • OceanBase数据库设计与管理:构建高效分布式数据架构基石
  • Stable diffusion的SDXL模型,针不错!(含实操)
  • git push报错 unauthorized email account cannot submit code
  • 老榕树的java专题:探索 Nacos:微服务架构中的配置与服务发现利器
  • 【ArcGIS微课1000例】0138:ArcGIS栅格数据每个像元值转为Excel文本进行统计分析、做图表
  • C语言基本知识复习浓缩版:控制语句--循环