risc-v有六种基本指令格式
- R-type
寄存机-寄存器操作 - I-type
短立即数和访存load操作 - S-type
访存store操作 - B-type
条件跳转操作 - U-type
长立即数操作 - J-type
无条件跳转 - opcode(操作码):指令的基本操作,这个缩写是它惯用名称。
- rd: 目的操作寄存器,用来存放操作结果。
- funct3: 一个另外的操作码字段。
- rs1: 第一个源操作数寄存器。
- rs2: 第二个源操作数寄存器。
- funct7: 一个另外的操作码字段。
- imm:立即数
本文实现ori(立即数或运算)的指令,其属于I型指令:
(1)取指阶段寄存器(无论几条指令该阶段均相同)
module pc_reg(//取指阶段模块
input clk,//时钟脉冲
input rst_n,//复位信号
output reg [31:0] pc,//指令指针
output reg ce //使能端
);
//posedge表示上升沿有效,negedge表示下降沿有效
//当复位信号有效时,使能端ce=0,pc=0;
//否则使能ce=1,pc每个时钟周期加4
always @ (posedge clk or negedge rst_n) begin
if(~rst_n)
ce <= 1'b0; //将宽度为1的二进制零赋值给ce,即rst=0->ce=0,否则ce=1
else
ce <= 1'b1;
end
always @ (posedge clk) begin //有时钟脉冲来且没有复位信号(ce=1)时pc=pc+4,否则pc=0
if(~ce)
pc <= 32'b0;
else
pc <= pc + 4'h4;
end
endmodule
取指和译码之间的流水寄存器(无论几条指令该阶段均相同)\
他将取指阶段得到的指令暂存起来等到下一个时钟周期将取指阶段的结果送入译码阶段
module if_id(//取指和译码之间的寄存器模块
input clk,
input rst_n,
input [31:0] if_pc, //取指阶段的pc
input [31:0] if_inst, //
output reg [31:0] id_pc, //译码阶段的pc
output reg [31:0] id_inst
);
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin //复位信号有效时将取指阶段结果传给译码阶段
id_pc <= 32'b0;
id_inst <= 32'b0;
end
else begin
id_pc <= if_pc;
id_inst <= if_inst;
end
end
endmodule
(2)译码阶段的包含regfile.v文件,id.v文件,id_ex.v文件
创建一个有32个32位的寄存器组,包含一个写端口,两个读端口
regfile.v文件:
module regfile(
input rst_n,
input clk,
//写端口
input [4:0] waddr,
input [31:0] wdata,
input we,
//读端口1
input [4:0] raddr1,
input re1,
output reg [31:0] rdata1,
//读端口2
input [4:0] raddr2,
input re2,
output reg [31:0] rdata2
);
reg [31:0] mem_r [0:31];
always @ (posedge clk or negedge rst_n) begin //写入寄存器
if(rst_n) begin
if((waddr != 5'b0) && (we))
mem_r[waddr] <= wdata;
end
end
always @ (*) begin //从端口1读出数据
if(~rst_n)
rdata1 <= 32'b0;
else if(raddr1 == 5'b0)
rdata1 <= 32'b0;
else if((raddr1 == waddr) && re1 && we) //如果写入的地址与要读出的地址相同
rdata1 <= wdata; //则直接将写入数据读出
else if(re1)
rdata1 <= mem_r[raddr1];
else
rdata1 <= 32'b0;
end
always @ (*) begin //从端口2读出数据
if(~rst_n)
rdata2 <= 32'b0;
else if(raddr2 == 5'b0)
rdata2 <= 32'b0;
else if((raddr2 == waddr) && re2 && we)
rdata2 <= wdata;
else if(re2)
rdata2 <= mem_r[raddr2];
else
rdata2 <= 32'b0;
end
endmodule
id.v文件:(不同指令类型有所区别)
//将指令中的操作类型
module id(//译码阶段中信息转发
input rst_n,
input [31:0] pc_i,
input [31:0] inst_i,
//读取register
input [31:0] reg1_data_i,
input [31:0] reg2_data_i,
//输出到regfile
output reg reg1_read_o,
output reg reg2_read_o,
output reg [4:0] reg1_addr_o,
output reg [4:0] reg2_addr_o,
//送出到执行模块
output reg [6:0] aluop_o,
output reg [2:0] alusel_o,
output reg [31:0] reg1_o,
output reg [31:0] reg2_o,
output reg [4:0] wd_o,
output reg wreg_o,
//解决流水线冲突
input ex_wreg_i,
input [31:0] ex_wdata_i,
input [4:0] ex_wd_i,
input mem_wreg_i,
input [31:0] mem_wdata_i,
input [4:0] mem_wd_i
);
wire [6:0] op = inst_i[6:0]; //运算类型
wire [2:0] op1 = inst_i[14:12]; //具体运算方式
reg [31:0] imm;
always @ (*) begin
if(~rst_n) begin
aluop_o <= 0;
alusel_o <= 0;
wd_o <= 0;
wreg_o <= 0;
reg1_read_o <= 0;
reg2_read_o <= 0;
reg1_addr_o <= 0;
reg2_addr_o <= 0;
imm <= 0;
end
else begin
aluop_o <= 0;
alusel_o <= 0;
wd_o <= inst_i[11:7]; //目的寄存器地址
wreg_o <= 1'b1; //目的寄存器使能
reg1_read_o <= 0;
reg2_read_o <= 0;
reg1_addr_o <= inst_i[19:15];
reg2_addr_o <= inst_i[24:20];
imm <= 0;
case(op) //op有七位,指明操作码
7'b0010011: begin //立即数操作:(0010011)为指令的[6:0]位
case (op1) //(110)为ori的[14:12]位
3'b110: begin //ori 这里不同指令条数写法不同
wreg_o <= 1'b1; //是否写目的寄存器
aluop_o <= op; //运算类型
alusel_o <= op1; //运算方式
reg1_read_o <= 1'b1; //是否读操作数1
reg2_read_o <= 1'b0; //是否读操作数2
imm <= {{20{inst_i[31]}} , inst_i[31:20]}; //立即数扩展
end
default: begin
end
endcase
end
default: begin
end
endcase
end
end
always @ (*) begin
if(~rst_n)
reg1_o <= 0;
else if((reg1_read_o) && (ex_wreg_i) && (ex_wd_i == reg1_addr_o)) //如果执行阶段的数据为译码阶段所要
//读取的数据则直接将其送回译码阶段
reg1_o <= ex_wdata_i;
else if((reg1_read_o) && (mem_wreg_i) && (mem_wd_i == reg1_addr_o)) //如果访存阶段的数据为译码阶段所要
//读取的数据则直接将其送回译码阶段
reg1_o <= mem_wdata_i;
else if(reg1_read_o)
reg1_o <= reg1_data_i;
else if(~reg1_read_o)
reg1_o <= imm;
else
reg1_o <= 0;
end
always @ (*) begin
if(~rst_n)
reg2_o <= 0;
else if((reg2_read_o) && (ex_wreg_i) && (ex_wd_i == reg2_addr_o))
reg2_o <= ex_wdata_i;
else if((reg2_read_o) && (mem_wreg_i) && (mem_wd_i == reg2_addr_o))
reg2_o <= mem_wdata_i;
else if(reg2_read_o)
reg2_o <= reg2_data_i;
else if(~reg2_read_o)
reg2_o <= imm;
else
reg2_o <= 0;
end
id_ex.v文件(相同)
module id_ex(
input clk,
input rst_n,
input [6:0] id_aluop,
input [2:0] id_alusel,
input [31:0] id_reg1,
input [31:0] id_reg2,
input [4:0] id_wd,
input id_wreg, //目的寄存器使能
output reg [6:0] ex_aluop,
output reg [2:0] ex_alusel,
output reg [31:0] ex_reg1,
output reg [31:0] ex_reg2,
output reg [4:0] ex_wd,
output reg ex_wreg
);
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
ex_aluop <= 0;
ex_alusel <= 0;
ex_reg1 <= 0;
ex_reg2 <= 0;
ex_wd <= 0;
ex_wreg <= 0;
end
else begin
ex_aluop <= id_aluop;
ex_alusel <= id_alusel;
ex_reg1 <= id_reg1;
ex_reg2 <= id_reg2;
ex_wd <= id_wd;
ex_wreg <= id_wreg;
end
end
endmodule
(3)执行阶段:包括ex.v和ex_mem
ex.v文件:(不同指令条数及类型的写法不同)
module ex(
input rst_n,
input [6:0] aluop_i,
input [2:0] alusel_i,
input [31:0] reg1_i,
input [31:0] reg2_i,
input [4:0] wd_i,
input wreg_i,
output reg [4:0] wd_o,
output reg wreg_o,
output reg [31:0] wdata_o
);
reg [31:0] result;
always @ (*) begin //通过译码阶段发送过来的信息确定具体的运算操作
if(~rst_n)
result <= 0;
else begin
case(aluop_i)
7'b0010011: begin
case(alusel_i)
3'b110: result <= reg1_i | reg2_i; //执行相应的运算
default: begin
result <= 0;
end
endcase
end
endcase
end
end
always @ (*) begin //将运算结果发送到下一阶段
wd_o <= wd_i;
wreg_o <= wreg_i;
case(aluop_i)
7'b0010011: begin
case(alusel_i)
3'b110: wdata_o <= result;
default: begin
end
endcase
end
default: begin
wdata_o <= 0;
end
endcase
end
endmodule
ex_mem文件:(相同)
module ex_mem(
input clk,
input rst_n,
input [4:0] ex_wd,
input ex_wreg,
input [31:0] ex_wdata,
output reg [4:0] mem_wd,
output reg mem_wreg,
output reg [31:0] mem_wdata
);
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
mem_wd <= 0;
mem_wreg <= 0;
mem_wdata <= 0;
end
else begin
mem_wd <= ex_wd;
mem_wreg <= ex_wreg;
mem_wdata <= ex_wdata;
end
end
endmodule
(4)访存模块的设计:
mem.v(相同)
module mem(
input rst_n,
input [4:0] wd_i,
input wreg_i,
input [31:0] wdata_i,
output reg [4:0] wd_o,
output reg wreg_o,
output reg [31:0] wdata_o
);
always @ (*) begin
if(~rst_n) begin
wd_o <= 0;
wreg_o <= 0;
wdata_o <= 0;
end
else begin
wd_o <= wd_i;
wreg_o <= wreg_i;
wdata_o <= wdata_i;
end
end
endmodule
mem_wb.v(相同)
module mem_wb(
input rst_n,
input clk,
input [4:0] mem_wd,
input mem_wreg,
input [31:0] mem_wdata,
output reg [4:0] wb_wd,
output reg wb_wreg,
output reg [31:0] wb_wdata
);
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
wb_wd <= 0;
wb_wreg <= 0;
wb_wdata <= 0;
end
else begin
wb_wd <= mem_wd;
wb_wreg <= mem_wreg;
wb_wdata <= mem_wdata;
end
end
endmodule
(5)回写模块,流水线最后 将上述指令的结果写回寄存器,该部分功能在发生在regfile模块
(6)顶层模块设计:(相同)
// Additional Comments:
//
//
module riscv(
input rst_n,
input clk,
input [31:0] rom_data_i,
output [31:0] rom_addr_o,
output rom_ce_o
);
wire [31:0] pc;
wire [31:0] id_pc_i;
wire [31:0] id_inst_i;
wire [6:0] id_aluop_o;
wire [2:0] id_alusel_o;
wire [31:0] id_reg1_o;
wire [31:0] id_reg2_o;
wire id_wreg_o;
wire [4:0] id_wd_o;
wire [6:0] ex_aluop_i;
wire [2:0] ex_alusel_i;
wire [31:0] ex_reg1_i;
wire [31:0] ex_reg2_i;
wire ex_wreg_i;
wire [4:0] ex_wd_i;
//......
wire ex_wreg_o;
wire [4:0] ex_wd_o;
wire [31:0] ex_wdata_o;
wire mem_wreg_i;
wire [4:0] mem_wd_i;
wire [31:0] mem_wdata_i;
wire mem_wreg_o;
wire [4:0] mem_wd_o;
wire [31:0] mem_wdata_o;
wire wb_wreg_i;
wire [4:0] wb_wd_i;
wire [31:0] wb_wdata_i;
wire reg1_read;
wire reg2_read;
wire [31:0] reg1_data;
wire [31:0] reg2_data;
wire [4:0] reg1_addr;
wire [4:0] reg2_addr;
pc_reg pc_reg0(
.clk(clk),
.rst_n(rst_n),
.pc(pc),
.ce(rom_ce_o)
);
assign rom_addr_o = pc;
if_id if_id0(
.clk(clk),
.rst_n(rst_n),
.if_pc(pc),
.if_inst(rom_data_i),
.id_pc(id_pc_i),
.id_inst(id_inst_i)
);
regfile regfile0(
.clk(clk),
.rst_n(rst_n),
.we(wb_wreg_i),
.waddr(wb_wd_i),
.wdata(wb_wdata_i),
.re1(reg1_read),
.raddr1(reg1_addr),
.rdata1(reg1_data),
.re2(reg2_read),
.raddr2(reg2_addr),
.rdata2(reg2_data)
);
id id0(
.rst_n(rst_n),
.pc_i(id_pc_i),
.inst_i(id_inst_i),
.reg1_data_i(reg1_data),
.reg2_data_i(reg2_data),
.reg1_read_o(reg1_read),
.reg2_read_o(reg2_read),
.reg1_addr_o(reg1_addr),
.reg2_addr_o(reg2_addr),
.aluop_o(id_aluop_o),
.alusel_o(id_alusel_o),
.reg1_o(id_reg1_o),
.reg2_o(id_reg2_o),
.wd_o(id_wd_o),
.wreg_o(id_wreg_o),
.ex_wreg_i(ex_wreg_o),
.ex_wdata_i(ex_wdata_o),
.ex_wd_i(ex_wd_o),
.mem_wreg_i(mem_wreg_o),
.mem_wdata_i(mem_wdata_o),
.mem_wd_i(mem_wd_o)
);
id_ex id_ex0(
.clk(clk),
.rst_n(rst_n),
.id_aluop(id_aluop_o),
.id_alusel(id_alusel_o),
.id_reg1(id_reg1_o),
.id_reg2(id_reg2_o),
.id_wd(id_wd_o),
.id_wreg(id_wreg_o),
.ex_aluop(ex_aluop_i),
.ex_alusel(ex_alusel_i),
.ex_reg1(ex_reg1_i),
.ex_reg2(ex_reg2_i),
.ex_wd(ex_wd_i),
.ex_wreg(ex_wreg_i)
);
ex ex0(
.rst_n(rst_n),
.aluop_i(ex_aluop_i),
.alusel_i(ex_alusel_i),
.reg1_i(ex_reg1_i),
.reg2_i(ex_reg2_i),
.wd_i(ex_wd_i),
.wreg_i(ex_wreg_i),
.wd_o(ex_wd_o),
.wreg_o(ex_wreg_o),
.wdata_o(ex_wdata_o)
);
ex_mem ex_mem0(
.clk(clk),
.rst_n(rst_n),
.ex_wd(ex_wd_o),
.ex_wreg(ex_wreg_o),
.ex_wdata(ex_wdata_o),
.mem_wd(mem_wd_i),
.mem_wreg(mem_wreg_i),
.mem_wdata(mem_wdata_i)
);
mem mem0(
.rst_n(rst_n),
.wd_i(mem_wd_i),
.wreg_i(mem_wreg_i),
.wdata_i(mem_wdata_i),
.wd_o(mem_wd_o),
.wreg_o(mem_wreg_o),
.wdata_o(mem_wdata_o)
);
mem_wb mem_wb0(
.clk(clk),
.rst_n(rst_n),
.mem_wd(mem_wd_o),
.mem_wreg(mem_wreg_o),
.mem_wdata(mem_wdata_o),
.wb_wd(wb_wd_i),
.wb_wreg(wb_wreg_i),
.wb_wdata(wb_wdata_i)
);
endmodule
(7)指令存储器rom的功能:本处理器采用哈佛结构(指令与数据单独存放)想让处理器进行验证功能必须添加对应的指令存储器,CPU从其中取出指令
module inst_rom(
input ce,
input [31:0] addr,
output reg [31:0] inst
);
reg [31:0] inst_mem [0:255];
initial $readmemh ("inst_rom.data", inst_mem);
always @ (*) begin
if(~ce)
inst <= 0;
else
inst <= inst_mem[addr[9:2]];
end
endmodule
(8)最小系统的设计(相同)将指令存储器ROM与之前所设计的CPU进行连接构成一个CPU的最小系统
module RISCVtop(
input clk,
input rst_n
);
wire [31:0] inst_addr;
wire [31:0] inst;
wire rom_ce;
riscv riscv0(
.clk(clk),
.rst_n(rst_n),
.rom_addr_o(inst_addr),
.rom_data_i(inst),
.rom_ce_o(rom_ce)
);
inst_rom inst_rom0(
.ce(rom_ce),
.addr(inst_addr),
.inst(inst)
);
endmodule
(9)在记事本等软件中编写测试文件代码
在text.v文件(相同)中编写测试文件
module test(
);
reg clk;
reg rst_n;
initial begin
clk = 0;
rst_n = 0;
#100;
rst_n = 1'b1;
end
always #20 clk = ~clk;
RISCVtop RISCVtop0(
.clk(clk),
.rst_n(rst_n)
);
endmodule
未添加指令 前的仿真结果:
添加data文件(指令):
仿真结果:
遇到仿真出错的问题可能是由于多次仿真导致文件冲突,删除目录下sim_以后内的说有文件即可
基础知识:
一:数字表达方式有以下三种
(1)<位宽><进制><数字>,这是比较全面的描述方式
(2)<进制数字>在这种描述方式中,数字的位宽采用歌认位觉
(3)<数字>在这种描述方式中,采用默认进制:十进制
对于<进制>的表示:二进制整数用b或 B 表示,十进制整数用d或D表示,十六进制整h或H表示,八进制整数用o或O表示。具体用法示例
8'b10101111//位宽为8的数的二进制表示,'b表示二进制
4'ba//位宽为4的数的十六进制,'h表示十六进制
(2)5级流水线由取指、译码、执行、访存、回写这 5 个部分组成。5 级流水中的各个阶段的
具体工作如下:
取指:读取存储器中的指令,同时确定下一条指令的地址。
(2)译码:对取出的指令进行译码,获得指令中要读取的寄存器值,操作类型或者立即数等信息,针对立即数进行有符号或者无符号扩展等操作。如果包含转移指令且满足转称件,此阶段会给出目标地址。
(3)执行:将译码阶段发送出来的数据根据操作类型进行运算并发送出运算结果。如果执行的指令为Load(读)或 Store(写)指令,执行阶段也会计算读写的地址
(4)访存:如果有 Load(读)或 Store(写)指令,将会在此阶段访问存储器,否则此阶段可以忽略,执行阶段的数据将会直接发送到回写阶段。此阶段同时可以判断是否有异常需要处理如果有异常则进行异常处理。
(5)回写:将不写人存储器的运算结果写回相应的目的寄存器
三:对于多条指令的实现不需修改大多数模块,只要修改译码和执行两个模块即可。
四:imm <= {{20{inst_i[31]}} , inst_i[31:20]}; //立即数扩展: