一文学会制作riscvCPU(第三弹)
在上述两篇文章中,我们讲解了译码、取值、执行,在取指令的过程中,当遇到分支指令如何跳转也是很重要的,本文讲解实现指令跳转相关部分
PC
module pc (
input clk,
input rst,
input [`PCOP_LEN-1:0] pc_op, // pc 操作码
input [ `XLEN-1:0] rs1_data,
input [ `XLEN-1:0] imm_data,
input [ `XLEN-1:0] execute_data,
input [ `XLEN-1:0] clint_pc_i,
input clint_pc_valid_i,
output reg [ `XLEN-1:0] pc_out
);
首先我们对输入输出进行解析:
Input | 位数 | output | 位数 |
---|---|---|---|
clk | 1 | pc_out (输出的当前PC值) | 64 |
rst(复位) | 1 | ||
pc_op(执行pc的操作运算种类) | 4 | ||
rs1_data (rs1的数据的值 ) | 64 | ||
imm_data (立即数的值 decode中解析) | 64 | ||
execute_data (alu算出来的值) | 64 | ||
clint_pc_i(PC的值) | 64 | ||
clint_pc_valid_i(PC的有效性) |
对PC指令进行解析
/* pc 操作码 */
wire _pcop_branch = (pc_op == `PCOP_BRANCH); // 分支指令: pc = pc + imm
wire _pcop_jal = (pc_op == `PCOP_JAL); // jal 指令: pc = pc + imm
wire _pcop_jalr = (pc_op == `PCOP_JALR); // jalr 指令: pc = rs1 + imm
wire _pcop_inc4 = (pc_op == `PCOP_INC4); // pc 自增: pc = pc +4
wire _pcop_trap = (pc_op == `PCOP_TRAP); // trap 指令: pc = clint_pc_i
wire _pcop_none = (pc_op == `PCOP_NONE); // 暂停: pc = pc
对PC+4进行处理
wire _isready_branch = (execute_data == `XLEN'b1) & _pcop_branch; //条件跳转指令
wire _isready_inc4 = (_pcop_inc4) | ((~_isready_branch) & _pcop_branch);// 跳转指令退化成 +4
对跳转进行处理
wire [`XLEN-1:0] _pc_a_in = ({`XLEN{_isready_branch | _pcop_jal | _isready_inc4|_pcop_none}} & _pc_current) |
({`XLEN{_pcop_jalr}} & rs1_data)|
({`XLEN{_pcop_trap}} & clint_pc_i);
wire [`XLEN-1:0] _pc_b_in = ({`XLEN{_isready_branch|_pcop_jal|_pcop_jalr}}&imm_data) |
({`XLEN{_isready_inc4}} & `XLEN'd4)|
({`XLEN{_pcop_none|_pcop_trap}} & `XLEN'd0);
wire [`XLEN-1:0] _pc_next;
reg [`XLEN-1:0] _pc_current;
assign _pc_next = _pc_a_in + _pc_b_in;
上述代码通过pc指令的不同提取出两个操作数进行相加得到PC的下一次的值与PC的当前值
通过模板实现上升沿跳转
regTemplate #(
.WIDTH (`XLEN),
.RESET_VAL(`PC_RESET_ADDR)
) u_regpc (
.clk (clk),
.rst (rst),
.din (_pc_next),
.dout(_pc_current),
.wen (1)
);
模板的代码如下:
module regTemplate #(
WIDTH = 1,
RESET_VAL = 0
) (
input clk,
input rst,
input [WIDTH-1:0] din,
output reg [WIDTH-1:0] dout,
input wen
);
always @(posedge clk) begin
if (rst) dout <= RESET_VAL;
else if (wen) dout <= din;
end
endmodule
PC的初始值在调用模板函数的时候已经给了