从零开始设计RISC-V处理器——指令系统

本文介绍了作者作为本科生从零开始设计RISC-V处理器的过程,包括指令系统的选择,单周期处理器设计,五级流水线的数据通路和控制器设计,以及指令如LUI、AUIPC、JAL、JALR、BEQ、BNE等的工作原理和实现。作者还探讨了JAL指令的立即数格式,并分享了对其他条件分支指令的理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

系列文章目录

(一)从零开始设计RISC-V处理器——指令系统
(二)从零开始设计RISC-V处理器——单周期处理器的设计
(三)从零开始设计RISC-V处理器——单周期处理器的仿真
(四)从零开始设计RISC-V处理器——ALU的优化
(五)从零开始设计RISC-V处理器——五级流水线之数据通路的设计
(六)从零开始设计RISC-V处理器——五级流水线之控制器的设计
(七)从零开始设计RISC-V处理器——五级流水线之数据冒险
(八)从零开始设计RISC-V处理器——五级流水线之控制冒险
(九)从零开始设计RISC-V处理器——五级流水线之分支计算前移
(十)从零开始设计RISC-V处理器——五级流水线之静态预测


本人本科生一枚,之前学习了计算机体系结构(RISC-V版)这本书,对CPU的设计产生了浓厚的兴趣,于是决定在寒假期间,自己动手设计一个基于RISC-V指令集的CPU,顺便写一些文章,记录自己的学习成果,如果文章中有不合理之处,希望大家能批评指正。

(目前单周期处理器代码已更新完成,点击链接直达:基于RISC-V指令集的单周期处理器的设计

(视频版正在更新中~:
1.基于RISC-V指令集的处理器设计——CPU的基本原理
2.基于RISC-V指令集的处理器设计——指令集的介绍

今天是第一天,首先进行指令的选取。

如下表所示,计划实现以下37条指令。
在这里插入图片描述
对指令的理解:
1.LUI:将20位立即数的值左移12位(低12位补零)成为一个32位数,将其写回rd。这一指令主要是为了在寄存器中存入比较大的立即数,比如,要想在寄存器X1中存入一个数,可以用addi指令实现(addi x1,x0,100),但这个数的范围有限(-2048~2047),因为addi指令的立即数部分只有12位,能表示最大的无符号数位0xfff(十进制4095),对应的有符号数的范围则为-2048到2047。当立即数超出这个范围,则需要用lui指令。

如lui x1,0xffff ,二进制指令:00001111111111111111000010110111

汇编器执行结果如下:
在这里插入图片描述
2.AUIPC:将20位立即数的值左移12位(低12位补零)成为一个32位数,再加上该指令的pc值,再将结果写回rd。这条指令与lui类似,可能是为了实现PC在较大范围内跳转(仅仅是猜测,因为这条指令我还没有接触过)。

如auipc x2,0xfff,二进制指令:00000000111111111111000100010111

当前PC为4,汇编器执行结果如下:
在这里插入图片描述
3.JAL:PC+4 的结果送 rd 但不送入PC,然后计算下条指令地
址。转移地址采用相对寻址,基准地址为当前指令地址
(即 PC),偏移量为立即数 imm20 经符扩展后的值的 2 倍。
在实际的汇编程序编写中,跳转的目标往往使用汇编程序中的 label,汇编器会自动根据 label 所在的地址计算出相对的偏移量赋予指令编码。

如:
jal x3,label1 二进制:00000000100000000000000111101111
addi x4,x0,4
label1:
addi x5,x0,5
当前PC为 8,跳过给X4赋值这条指令,并且将12赋给X3寄存器,汇编器执行结果如下:
在这里插入图片描述
(补充:仔细分析上面jal指令的二进制会发现,立即数部分是4,4*2=8,PC+8=12,跳过中间的一条指令,这样看起来似乎没什么问题。

但是再看jal的立即数部分,总共是20位,拼接起来是[20:1],这里为什么不是[19:0]?

观察其他的指令格式,如果立即数是12位,那么会表示为[11:0],但JAL,BEQ,BNE,BLT,BGE,BLTU,BGEU却例外。

于是引发了以下思考(个人观点,未经查证,以后深入学习之后再回来补充):假设立即数其实是21位的,即[20:0],只不过始终保持最低位为0,所以在指令中仅仅体现[20:1],这样一来,21位的立即数一定是2的倍数,将立即数乘以2之后,一定是4的倍数,这样就可以保证跳转后的PC一定是4的倍数。所以,对于一个label,假设当前指令为第0行,label为第几行,立即数[20:1]则为多少,这样,立即数最低位补0再乘以2,与当前PC相加,则为正确的跳转地址。

以上的假设便能够合理地解释立即数为[20:1]的原因。

基于以上假设再来看上面jal指令的二进制,发现并不是这样,在以上的假设中,立即数应该是2,但汇编器转化出来的立即数是4,所以要么是我的假设出了问题,要么是汇编器出了问题。

站在汇编器的角度,立即数就是20位的,不存在最低位补个0的说法,所以汇编器在计算立即数的时候,自然而然地就已经将立即数设置成2的倍数了。对于BEQ,BNE,BLT,BGE,BLTU,BGEU指令也是一样。

在这里我更愿意相信我的假设,也许并不一定正确,但这看起来似乎更合理。以上仅为个人拙见,日后深入学习后可能才会发现其中的奥妙。)

4.JALR:jalr 指令使用 12 位立即数(有符号数)作为偏移量,与操作数寄存器 rs1中的值相加,然后将结果的最低有效位置0。jalr指令将其下一条指令的 PC(即当前指令PC+4)的值写入其结果寄存器 rd。

如:
addi x4,x0,4
jalr x8,x4,8 二进制:00000000100000100000111111100111
addi x5,x0,5
addi x6,x0,6
addi x7,x0,7
jalr指令所在的PC为4,4+8=12,则跳向PC=12的指令,同时将8存入X8寄存器。汇编器执行结果如下:
在这里插入图片描述
5.BEQ:beq指令只有在操作数寄存器rs1中的数值与操作数寄存器rs2的数值相等时,才会跳转,跳转地址为offset的有符号扩展和最低位补0(即乘以2)的偏移量加上BEQ指令的地址。

如:
addi x1,x0,1
label1:
add x1,x1,x1
addi x2,x0,2
beq x1,x2,label1 二进制:11111110001000001000110011100011

汇编器执行结果如下:
在这里插入图片描述
6.BNE:bne指令只有在操作数寄存器rs1中的数值与操作数寄存器rs2的数值不相等时,才会跳转,跳转地址为offset的有符号扩展和最低位补0(即乘以2)的偏移量加上BNE指令的地址。

如:
addi x1,x0,1
label1:
add x1,x1,x1
addi x2,x0,4
bne x1,x2,label1 二进制:11111110001000001001110011100011

汇编器执行结果如下:
在这里插入图片描述
7.BTL:blt指令只有在操作数寄存器rs1中的数值小于操作数寄存器rs2的数值时(有符号数),才会跳转,跳转地址为offset的有符号扩展和最低位补0(即乘以2)的偏移量加上BLT指令的地址。

如:
addi x1,x0,-1
label1:
add x1,x1,x1
addi x2,x0,-2
blt x1,x2,label1 二进制:11111110001000001100110011100011

汇编器执行结果如下:
在这里插入图片描述
8.BGE:bge指令只有在操作数寄存器rs1中的数值大于或等于操作数寄存器rs2的数值时(有符号数),才会跳转,跳转地址为offset的有符号扩展和最低位补0(即乘以2)的偏移量加上BGE指令的地址。
(注意bge指令的条件是大于等于,而blt的条件是小于)

如:
addi x1,x0,-1
label1:
add x1,x1,x1
addi x2,x0,-2
bge x1,x2,label1 二进制:11111110001000001101110011100011

汇编器执行结果如下:
在这里插入图片描述
9.BLTU:bltu指令只有在操作数寄存器rs1中的数值小于操作数寄存器rs2的数值时(无符号数),才会跳转,跳转地址为offset的有符号扩展和最低位补0的偏移量加上BLTU指令的地址。

如:
addi x1,x0,-1
label1:
add x1,x1,x1
addi x2,x0,2
bltu x1,x2,label1 二进制:11111110001000001110110011100011

这里一定是不跳转的,X1的值为-2,但是将其看作是一个无符号的数,就是一个非常大的正数。汇编器执行结果如下:

有符号表示:
在这里插入图片描述
无符号表示:
在这里插入图片描述
10.BGEU:bgeu指令只有在操作数寄存器rs1中的数值大于或等于操作数寄存器rs2的数值时(无符号数),才会跳转,跳转地址为offset的有符号扩展和最低位补0的偏移量加上BGEU指令的地址。

如:
addi x1,x0,-1
label1:
add x1,x1,x1
addi x2,x0,-2
bgeu x1,x2,label1 二进制:11111110001000001111110011100011

这里一定是跳转一次的,因为刚开始时X1=X2,跳转一次后不会再跳转。
汇编器执行如果如下:

有符号表示:
在这里插入图片描述
无符号表示:
在这里插入图片描述
11.SB:sb指令 将操作数寄存器rs2中的低8位数据,写回存储器

如:
addi x1,x0,0xf1
addi x2,x0,0xf2
addi x3,x0,0xf3
addi x4,x0,0xf4
sb x1,0,x0
sb x2,1,x0
sb x3,2,x0
sb x4,3,x0

汇编器执行结果如下:
在这里插入图片描述

12.SH:sh指令 将操作数寄存器rs2中的低16位数据,写回存储器。

如:
lui x1,0xf0f11
addi x1,x1,0x7ff
sh x1,12,x0

汇编器执行结果如下:

寄存器X1:
在这里插入图片描述
数据存储器:
在这里插入图片描述
13.SW:sw指令 将操作数寄存器rs2中的32位数据,写回存储器。

如:
lui x1,0xf0f11
addi x1,x1,0x7ff
sw x1,12,x0

汇编器执行结果如下:
在这里插入图片描述
14.LB:lb指令从存储器中读回一个8位的数据,进行符号位扩展后写回寄存器rd。

如:
lui x1,0x3ef12
addi x1,x1,0x7ff
sw x1,12,x0
lb x2,12,x0
lb x3,13,x0
lb x4,14,x0
lb x5,15,x0

现在X1寄存器存入一个0x3ef127ff,然后将这32位数存入数据存储器,再从数据存储器中依次读一个字节并进行符号扩展后存入寄存器。汇编器执行结果如下:

X1寄存器:
在这里插入图片描述

数据存储器:
在这里插入图片描述
符号扩展写入寄存器:
在这里插入图片描述
15.LH:lh指令从存储器中读回一个16位的数据,进行符号位扩展后写回寄存器rd。

如:
lui x1,0x3ef12
addi x1,x1,0x7ff
sw x1,12,x0
lh x2,12,x0
lh x3,13,x0
lh x4,14,x0
lh x5,15,x0

汇编器执行结果如下:

数据存储器:
在这里插入图片描述
符号扩展写入寄存器:
在这里插入图片描述
(思考:这里的X5中的83是什么,调出来数据存储器的数据可以看到,由于这里的地址是非对其访问,读两个字节就需要访问两个存储块。因此,为了提升指令的运行效率,使用load和store指令时,尽量采用边界对齐的地址访问)

16.LW:lw指令从存储器中读回一个32位的数据,写回寄存器rd。

如:
lui x1,0xf0f11
addi x1,x1,0x7ff
sw x1,0,x0
lw x2,0,x0

汇编器的执行结果如下:

数据存储器:
在这里插入图片描述

寄存器:
在这里插入图片描述
17.LBU:lbu指令从存储器中读回一个8位的数据,进行高位补零扩展后写回
寄存器rd。

如:
lui x1,0x3ef12
addi x1,x1,0x7ff
sw x1,12,x0
lbu x2,12,x0
lbu x3,13,x0
lbu x4,14,x0
lbu x5,15,x0

汇编器的执行结果如下:

数据存储器:
在这里插入图片描述
零扩展写入寄存器:
在这里插入图片描述
18.LHU:lhu指令从存储器中读回一个16位的数据,进行高位补零扩展后写回寄存器rd。

如:
lui x1,0x3ef12
addi x1,x1,0x7ff
sw x1,12,x0
lhu x2,12,x0
lhu x3,13,x0
lhu x4,14,x0
lhu x5,15,x0

汇编器执行结果如下:

数据存储器:
在这里插入图片描述
零扩展写入寄存器:
在这里插入图片描述
19.SLT:slt指令将操作数寄存器rs1中的数值与寄存器rs2当作有符号数进行比较。小于则置一,即如果rs1中的值小于rs2中的值,则输出1,否则输出0,结果写回rd。

如:
addi x1,x0,-1
addi x2,x0,1
slt x3,x1,x2

汇编器执行结果如下:
在这里插入图片描述
20.SLTU:sltu指令将操作数寄存器rs1中的数值与寄存器rs2当作无符号数进行比。如果rs1中的值小于rs2中的值,则输出1,否则输出0,结果写回rd。

如:
addi x1,x0,-1
addi x2,x0,1
sltu x3,x1,x2

汇编器执行结果如下:
在这里插入图片描述
21.SLTI:slti指令将操作数寄存器rs1中的数值与12位立即数当作有符号数进行比较,如果rs1中的值小于立即数的值,则输出1,否则输出0,结果写回rd。

如:
addi x1,x0,-1
slti x2,x1,1

汇编器执行结果如下:
在这里插入图片描述
22.SLTIU:sltiu指令将操作数寄存器rs1中的数值与12位立即数当作无符号数进行比较,如果rs1中的值小于立即数的值,则输出1,否则输出0,结果写回rd。

如:
addi x1,x0,-1
sltiu x2,x1,1

汇编器执行结果如下:
在这里插入图片描述
23.SLL:逻辑左移(SLL)根据寄存器(rs2)中的移位量对寄存器(rs1)中的值执行逻辑左移,并存储在(rd)寄存器中。

如:
addi x1,x0,4
addi x2,x0,0xf0
sll x3,x2,x1

汇编器执行结果如下:
在这里插入图片描述
24.SRL:逻辑右移(SRL)根据寄存器(rs2)中的移位量对寄存器(rs1)中的值执行逻辑右移,左边空出来的位补0,并存储在(rd)寄存器中。

如:
addi x1,x0,4
addi x2,x0,-0xf0
srl x3,x2,x1

汇编器执行结果如下:
在这里插入图片描述
25.SRA:算数右移(SRA)根据寄存器(rs2)中的移位量对寄存器(rs1)中的值执行算数右移,左边空出来的位补符号位,并存储在(rd)寄存器中。

如:
addi x1,x0,4
addi x2,x0,-0xf0
sra x3,x2,x1

汇编器执行结果如下:
在这里插入图片描述
26.SLLI:slli指令根据5位立即数的移位量对寄存器(rs1)中的值执行逻辑左移(低位补零),并存储在(rd)寄存器中。
27.SRLI:srli指令根据5位立即数的移位量对寄存器(rs1)中的值执行逻辑右移(高位补零),并存储在(rd)寄存器中。
28.SRAI:srai指令根据5位立即数的移位量对寄存器(rs1)中的值执行算数右移(高位补入符号位),并存储在(rd)寄存器中。

如:
addi x2,x0,-0xf0
slli x3,x2,4
srli x4,x2,4
srai x5,x2,4

汇编器执行结果如下:
在这里插入图片描述
29.ADD:add指令将寄存器(rs1)与寄存器(rs2)中的值相加,并写回(rd)寄存器中。
30.SUB:sub指令将寄存器(rs1)与寄存器(rs2)中的值相减,并写回(rd)寄存器中。
31.ADDI:addi指令将操作数寄存器rs1的整数值与12位立即数进行加法操作,
结果写回寄存器rd。

如:
addi x1,x0,-1
add x2,x1,x1
sub x3,x2,x1

汇编器执行结果如下:
在这里插入图片描述
32.XOR:xor指令在寄存器(rs1)的内容和寄存器(rs2)的内容之间执行逐位逻辑“异或”运算,并存储在(rd)寄存器中。
33.OR:or指令在寄存器(rs1)的内容和寄存器(rs2)的内容之间执行逐位逻辑“或”运算,并存储在(rd)寄存器中。
34.AND:and指令在寄存器(rs1)的内容和寄存器(rs2)的内容之间执行逐位逻辑“与”运算,并存储在(rd)寄存器中。
35.XORI:xori指令将操作数寄存器rs1中的数值与12位立即数进行异或操作,
结果写回rd。
36.ORI:ori指令将操作数寄存器rs1中的数值与12位立即数进行或操作,
结果写回rd。
37.ANDI:andi指令将操作数寄存器rs1中的数值与12位立即数进行与操作,
结果写回rd。

如:
addi x1,x0,0b11001100
addi x2,x0,0b00111111
xor x3,x1,x2
or x4,x1,x2
and x5,x1,x2
xori x6,x1,0b00111111
ori x7,x1,0b00111111
andi x8,x1,0b00111111

汇编器执行结果如下:
在这里插入图片描述
以上就是计划实现的37条指令的用法。下一篇文章开始设计处理器。

<think>好的,用户的问题是关于“不学无术的小胖子”,看起来可能是在自嘲或者描述某个人,但没有明确的技术背景。首先需要确定用户的意图。可能的情况是,用户想了解如何改善这种情况,或者寻找相关的建议。接下来要分析用户可能的深层需求,比如如何提升自我管理能力、健康减肥方法,或者如何克服学习上的困难。 需要调用网络搜索工具,查找与“不学无术”、“小胖子”相关的解决方案。可能的搜索词包括如何提高自律性、健康减肥技巧、时间管理方法、学习动力提升等。然后整合找到的信息,确保内容实用且可操作。 根据要求,回答需要分多个方法或步骤,每个步骤间换行,不使用第一人称和步骤词汇。同时,确保引用内容分散在回答中,不集中在末尾。还要检查是否有需要代码或公式的部分,但根据问题内容,可能不需要,但需保持格式规范。 最后生成相关问题,必须基于原始回答内容,至少三个,用指定格式列出。需要确保问题相关且具体,比如关于时间管理、健康饮食、学习动力等。 总结回答结构:分为自我管理、健康改善、学习提升三个方面,每个部分提供具体方法。然后添加相关问题部分,符合格式要求。</think>“不学无术的小胖子”这一表述可能涉及自我管理、健康状态和学习能力等多方面问题。以下是针对不同维度的改善建议: **1. 提升自我管理能力** 建立明确的目标清单并设定优先级,使用工具如时间管理矩阵(紧急/重要四象限)辅助决策。尝试番茄工作法(25分钟专注+5分钟休息),通过手机应用如Forest或Todoist记录任务完成情况。每日复盘10分钟,分析时间分配合理性。 **2. 改善身体健康状态** 采用基础代谢率公式估算每日能量需求: $$BMR = 88.362 + (13.397 \times 体重/kg) + (4.799 \times 身高/cm) - (5.677 \times 年龄)$$ 结合有氧运动(如快走、游泳)与力量训练,每周至少150分钟中等强度运动。饮食遵循“211餐盘法”(2份蔬菜、1份蛋白质、1份主食),使用MyFitnessPal等APP追踪摄入。 **3. 建立系统性学习路径** 选择可量化的学习目标(如“每日阅读30页专业书籍”),采用费曼技巧(通过复述验证理解程度)。参与线上学习社区(Coursera/edX),完成课程可获得认证激励。对于编程等技能,可参考以下Python基础练习: ```python # 每日练习代码片段示例 def calculate_bmi(weight, height): return round(weight / (height**2), 1) print(calculate_bmi(70, 1.75)) ```
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不学无术的小胖子.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值