tinyriscv/rtl/core/ifu.sv

110 lines
3.5 KiB
Systemverilog

/*
Copyright 2020 Blue Liang, liangkangnan@163.com
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
`include "defines.sv"
// 取指模块
module ifu(
input wire clk,
input wire rst_n,
input wire flush_i, // 跳转标志
input wire[31:0] flush_addr_i, // 跳转地址
input wire[`STALL_WIDTH-1:0] stall_i, // 流水线暂停标志
input wire jtag_halt_i,
output wire[31:0] inst_o,
output wire[31:0] pc_o,
output wire inst_valid_o,
output wire instr_req_o,
input wire instr_gnt_i,
input wire instr_rvalid_i,
output wire[31:0] instr_addr_o,
input wire[31:0] instr_rdata_i,
input wire instr_err_i
);
assign instr_req_o = ((~rst_n) | stall_i[`STALL_PC])? 1'b0: 1'b1;
localparam S_FETCH = 2'b01;
localparam S_VALID = 2'b10;
reg[1:0] state;
reg[1:0] next_state;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_FETCH;
end else begin
state <= next_state;
end
end
always @ (*) begin
case (state)
S_FETCH: begin
if (instr_gnt_i & (~stall_i[`STALL_PC]) & (~flush_i)) begin
next_state = S_VALID;
end else begin
next_state = S_FETCH;
end
end
S_VALID: begin
if (stall_i[`STALL_PC] | flush_i) begin
next_state = S_FETCH;
end else begin
next_state = S_VALID;
end
end
default: begin
next_state = S_FETCH;
end
endcase
end
wire inst_valid = (state == S_VALID) & instr_rvalid_i;
assign inst_valid_o = inst_valid;
assign inst_o = inst_valid? instr_rdata_i: `INST_NOP;
wire[31:0] fetch_addr_d;
reg[31:0] fetch_addr_q;
wire fetch_addr_en = instr_req_o & instr_gnt_i & (~stall_i[`STALL_PC]);
assign fetch_addr_d = flush_i? flush_addr_i:
stall_i[`STALL_PC]? fetch_addr_q:
inst_valid? fetch_addr_q + 4'h4:
fetch_addr_q;
always @ (posedge clk or negedge rst_n) begin
// 复位
if (!rst_n) begin
fetch_addr_q <= `CPU_RESET_ADDR;
end else if (fetch_addr_en) begin
fetch_addr_q <= fetch_addr_d;
end
end
assign instr_addr_o = {fetch_addr_d[31:2], 2'b00};
assign pc_o = fetch_addr_q;
endmodule