目录
单周期riscv处理器的实现
代码放到了:尚未完成
小组成员:
指令分析
最初想要实现完整的riscv32i指令集,但由于试验任务的时间要求以及我个人能力的限制,最终只是选择了并不完整的riscv指令集进行实现。但基本搭建了完整的框架,倘若之后有时间,会尝试实现完整的riscv32i指令集。
实现指令
指令 | 指令说明 | 指令类型 | opcode | func3 |
---|---|---|---|---|
jal | 无条件跳转 | J型指令 | 1101111 | |
beq | 有条件跳转 | B型指令 | 1100011 | |
lw | 加载字 | I型指令 | 0000011 | |
sw | 存字 | S型指令 | 0100011 | |
add | 加 | R型指令 | 0110011 | 000 |
sub | 减 | R型指令 | 0110011 | 010 |
sll | 逻辑左移 | R型指令 | 0110011 | 001 |
srl | 逻辑右移 | R型指令 | 0110011 | 011 |
and | 与 | R型指令 | 0110011 | 110 |
or | 或 | R型指令 | 0110011 | 110 |
xor | 异或 | R型指令 | 0110011 | 100 |
addi | 加 | I型指令 | 0010011 | 000 |
subi | 减 | I型指令 | 0010011 | 010 |
slli | 逻辑左移 | I型指令 | 0010011 | 001 |
srli | 逻辑右移 | I型指令 | 0010011 | 011 |
andi | 与 | I型指令 | 0010011 | 110 |
ori | 或 | I型指令 | 0010011 | 110 |
xori | 异或 | I型指令 | 0010011 | 100 |
出于方便译码的考虑,我们对sub指令以及subi指令实现的func3数据段进行了细微的调整,与原版riscv32i有出入,利用汇编转机器码时可能需要手动修正。
处理器总体思路
我实现的指令集是残缺的,为了能够之后可能进行的完整实现以及方便与课堂内容结合,我决定尽量完全按照黑皮书《计算机组成与设计 riscv版》进行实现
下图为完整实现
分块拆解
直接实现未免过于困难,于是尝试先把每一个部分进行实现,最后整合成完整的数据通路。
下面列一下要实现的所有小模块。
模块 | 说明 |
---|---|
InstrMem | 指令存储器 |
DataMem | 数据存储器 |
rigisters | 寄存器堆 |
PC | PC寄存器 |
PC_mux | PC多选器 |
Add_imm | 指令地址跳转计算器 |
Add_4 | 指令地址非跳转计算器 |
Control | 主控制器 |
ALU_mux | ALU来源多选器 |
ALU | ALU运算器 |
ALUControl | ALU运算控制 |
Branch_judge | 判断指令是否跳转 |
ImmGen | 立即数生成器 |
rigister_write_mux | 寄存器写入数据多选器 |
处理器完整实现
各分模块实现
接下来我们一步步实现每一个模块
指令存储器
类型 | 信号 | 注释 |
---|---|---|
input | [7:0]addr | 指令地址(总共可存\({2^8}\)个指令) |
output | [31:0]instr | 地址对应的指令 |
指令存储器相对比较容易实现,只要根据输入的地址取出相对应的指令即可。
代码实现
module InstrMem(
input [7:0]addr,
output [31:0]instr
);
reg[31:0] rom[255:0];
//rom进行初始化
initial begin
rom[0] = 32'b00000000000100000000000010010011;
rom[1] = 32'b00000000000100001000000100110011;
rom[2] = 32'b00000000001000010000000110110011;
rom[3] = 32'b01000000000100011000000110110011;
end
assign instr = rom[addr];
endmodule
input clk,
input rst_n,
input MemWrite,
input MemRead,
input [7:0]Address,
input [31:0] WriteData,
output [31:0] ReadData
数据存储器
类型 | 信号 | 注释 |
---|---|---|
input | clk | 时钟信号 |
input | rst_n | 复位信号 |
input | MemWrite | 写使能信号 |
input | MemRead | 读使能信号 |
input | [7:0]Address | 数据存储器地址 |
input | [31:0] WriteData | 写入数据 |
output | [31:0] ReadData | 读出数据 |
根据控制器处理得到的的读写使能信号,决定写入还是读出数据。
写入数据来自寄存器堆读出的第二个寄存器数据。
数据存储器地址来自主ALU运算。
代码实现:
module DataMem(
input clk,
input rst_n,
input MemWrite,
input MemRead,
input [7:0]Address,
input [31:0] WriteData,
output [31:0] ReadData
);
reg [31:0]ram[255:0];
assign ReadData = (Address==32'd0) ? 32'b0 : ram[Address];
always@(posedge clk)
begin
if(!rst_n)// for循环不知道为什么会报错,仿真暂且这样初始化
begin //对每一个存储器都置零
ram[0]<=`zero_word;
ram[1]<=`zero_word;
ram[2]<=`zero_word;
ram[3]<=`zero_word;
ram[4]<=`zero_word;
ram[5]<=`zero_word;
ram[6]<=`zero_word;
······//略去
end
else if(MemWrite & (Address!=32'b0))
ram[Address]<=WriteData;
end
endmodule
控制模块
这里几乎是整个处理器的核心模块,既然是实验结束后复盘,可以先从这里写起,方便之后模块输入输出的描述的
input [6:0] opcode,
output reg ALUSrc,
output reg[1:0] Branch,
output reg MemRead,
output reg MemtoReg, // 1 时写入dataMem中的, 0 时写ALU里的
output reg MemWrite,
output reg RegWrite,
output reg ALUop // 1时结合func3进行判断,0时直接加
类型 | 信号 | 注释 |
---|---|---|
input | [6:0]opcode | |
output | ALUSrc | 决定ALU的第二个操作数来源 |
output | [1:0] Branch | 分辨beq, jal, 与其他指令。后面被输入到Branch_judge模块判断指令是否跳转 |
output | MemWrite | 写使能信号 |
output | MemRead | 读使能信号 |
output | [7:0]Address | 数据存储器地址 |
output | [31:0] WriteData | 写入数据 |
output | [31:0] ReadData | 读出数据 |
代码实现:
module Control(
input [6:0] opcode,
output reg ALUSrc, // 0时来自寄存器,1时来自立即数
output reg[1:0] Branch,
output reg MemRead,
output reg MemtoReg, // 1 时写入dataMem中的, 0 时写ALU里的
output reg MemWrite,
output reg RegWrite,
output reg ALUop // 1时结合func3进行判断,0时直接加
);
always@(*) begin
case(opcode)
7'b0110011: begin // R型指令
Branch = 2'b00;
ALUSrc = 0;
MemRead = 0;
MemtoReg = 0;
MemWrite = 0;
RegWrite = 1;
ALUop = 1;
end
7'b0010011: begin // I型指令_立即数
Branch = 2'b00;
ALUSrc = 1;
MemRead = 0;
MemtoReg = 0;
MemWrite = 0;
RegWrite = 1;
ALUop = 1;
end
7'b0000011: begin// I型指令_load
Branch = 2'b00;
ALUSrc = 0;
MemRead = 1;
MemtoReg = 1;
MemWrite = 0;
RegWrite = 1;
ALUop = 0;
end
7'b0100011: begin// S型指令
Branch = 2'b00;
ALUSrc = 0;
MemRead = 0;
MemtoReg = 0;
MemWrite = 1;
RegWrite = 0;
ALUop = 0;
end
7'b1100011: begin// B型指令
Branch = 2'b10;
ALUSrc = 1;
MemRead = 0;
MemtoReg = 0;
MemWrite = 0;
RegWrite = 0;
ALUop = 0;
end
7'b1101111: begin// J型指令
Branch = 2'b01;
ALUSrc = 1;
MemRead = 0;
MemtoReg = 0;
MemWrite = 0;
RegWrite = 1;
ALUop = 0;
end
endcase
end
endmodule
立即数生成模块
类型 | 信号 | 注释 |
---|---|---|
input | [31:0]instr | 指令 |
output | [31:0]imme | 立即数 |
输入指令输出符号扩展之后的立即数,这个模块事实上是第一个我实现的模块,这时候我还怀着一颗勇敢的心,把所有指令的立即数都进行了生成,然而毫无疑问后来失败跑路了,但这个模块见证了我曾经的雄心壮志(笑
define lui 7'b0110111
define auipc 7'b0010111
define jal 7'b1101111
define jalr 7'b1100111
define B_type 7'b1100011
define load 7'b0000011
define store 7'b0100011
define I_type 7'b0010011
`define R_type 7'b0110011
module ImmGen(
input [31:0]instr,
output [31:0]imme
);
wire I;
wire U;
wire J;
wire B;
wire S;
wire [31:0]I_imme;
wire [31:0]U_imme;
wire [31:0]J_imme;
wire [31:0]B_imme;
wire [31:0]S_imme;
assign I=(instr[6:0]==`jalr) | (instr[6:0]==`load) | (instr[6:0]==`I_type);
assign U=(instr[6:0]==`lui) | (instr[6:0]==`auipc);
assign J=(instr[6:0]==`jal);
assign B=(instr[6:0]==`B_type);
assign S=(instr[6:0]==`store);
//立即数符号扩展
assign I_imme = {{20{instr[31]}},instr[31:20]};
assign U_imme = {instr[31:12],{12{1'b0}}};
assign J_imme = {{12{instr[31]}},instr[19:12],instr[20],instr[30:21],1'b0};
assign B_imme = {{20{instr[31]}},instr[7],instr[30:25],instr[11:8],1'b0};
assign S_imme = {{20{instr[31]}},instr[31:25],instr[11:7]};
assign imme = I ? I_imme : U ? U_imme : J ? J_imme : B ? B_imme : S ? S_imme : 32'd0;
endmodule
标签:周期,instr,riscv,31,imme,指令,处理器,input,output
From: https://www.cnblogs.com/ccuu/p/16905175.html