i✂️ Add separate issue stage

This commit is contained in:
Florian Zaruba 2017-06-21 18:58:37 +02:00
parent a6711fd330
commit f6f33eafaf
5 changed files with 281 additions and 193 deletions

View file

@ -118,8 +118,17 @@ module ariane
logic fetch_valid_if_id;
logic decode_ack_id_if;
exception exception_if_id;
// --------------
// ID <-> EX
// ID <-> ISSUE
// --------------
scoreboard_entry issue_entry_id_issue;
logic issue_entry_valid_id_issue;
logic is_ctrl_fow_id_issue;
logic issue_instr_issue_id;
// --------------
// ISSUE <-> EX
// --------------
logic [63:0] imm_id_ex;
logic [TRANS_ID_BITS-1:0] trans_id_id_ex;
@ -266,22 +275,42 @@ module ariane
// ---------
// ID
// ---------
id_stage
id_stage id_stage_i (
.flush_i ( flush_ctrl_id ),
.fetch_entry_i ( fetch_entry_if_id ),
.fetch_entry_valid_i ( fetch_valid_if_id ),
.decoded_instr_ack_o ( decode_ack_id_if ),
.issue_entry_o ( issue_entry_id_issue ),
.issue_entry_valid_o ( issue_entry_valid_id_issue ),
.is_ctrl_flow_o ( is_ctrl_fow_id_issue ),
.issue_instr_ack_i ( issue_instr_issue_id ),
.priv_lvl_i ( priv_lvl ),
.tvm_i ( tvm_csr_id ),
.tw_i ( tw_csr_id ),
.tsr_i ( tsr_csr_id ),
.*
);
// ---------
// Issue
// ---------
issue_stage
#(
.NR_ENTRIES ( NR_SB_ENTRIES ),
.NR_WB_PORTS ( NR_WB_PORTS )
.NR_ENTRIES ( NR_SB_ENTRIES ),
.NR_WB_PORTS ( NR_WB_PORTS )
)
id_stage_i (
.test_en_i ( test_en_i ),
issue_stage_i (
.flush_unissued_instr_i ( flush_unissued_instr_i ),
.flush_i ( flush_ctrl_id ),
.flush_unissued_instr_i ( flush_unissued_instr_ctrl_id ),
.fetch_entry_i ( fetch_entry_if_id ),
.fetch_entry_valid_i ( fetch_valid_if_id ),
.decoded_instr_ack_o ( decode_ack_id_if ),
.priv_lvl_i ( priv_lvl ),
.tvm_i ( tvm_csr_id ),
.tw_i ( tw_csr_id ),
.tsr_i ( tsr_csr_id ),
.decoded_instr_i ( issue_entry_id_issue ),
.decoded_instr_valid_i ( issue_entry_valid_id_issue ),
.is_ctrl_flow_i ( is_ctrl_fow_id_issue ),
.decoded_instr_ack_o ( issue_instr_issue_id ),
// Functional Units
.fu_o ( fu_id_ex ),
.operator_o ( operator_id_ex ),
@ -485,8 +514,8 @@ module ariane
assign tracer_if.fetch_valid = fetch_valid_if_id;
assign tracer_if.fetch_ack = decode_ack_id_if;
// Issue
assign tracer_if.issue_ack = id_stage_i.scoreboard_i.issue_ack_i;
assign tracer_if.issue_sbe = id_stage_i.scoreboard_i.issue_instr_o;
assign tracer_if.issue_ack = issue_stage_i.scoreboard_i.issue_ack_i;
assign tracer_if.issue_sbe = issue_stage_i.scoreboard_i.issue_instr_o;
// write-back
assign tracer_if.waddr = waddr_a_commit_id;
assign tracer_if.wdata = wdata_a_commit_id;

View file

@ -19,121 +19,37 @@
//
import ariane_pkg::*;
module id_stage #(
parameter int NR_ENTRIES = 4,
parameter int NR_WB_PORTS = 4
)(
module id_stage (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic test_en_i, // Test Enable
input logic flush_i,
input logic flush_unissued_instr_i,
// from IF
input fetch_entry fetch_entry_i,
input logic fetch_entry_valid_i,
output logic decoded_instr_ack_o,
output logic decoded_instr_ack_o, // acknowledge the instruction (fetch entry)
// to ID
output scoreboard_entry issue_entry_o, // a decoded instruction
output logic issue_entry_valid_o, // issue entry is valid
output logic is_ctrl_flow_o, // the instruction we issue is a ctrl flow instructions
input logic issue_instr_ack_i, // issue stage acknowledged sampling of instructions
// from CSR file
input priv_lvl_t priv_lvl_i, // current privilege level
input priv_lvl_t priv_lvl_i, // current privilege level
input logic tvm_i,
input logic tw_i,
input logic tsr_i,
output fu_t fu_o,
output fu_op operator_o,
output logic [63:0] operand_a_o,
output logic [63:0] operand_b_o,
output logic [63:0] imm_o,
output logic [TRANS_ID_BITS-1:0] trans_id_o,
output logic [63:0] pc_o,
output logic is_compressed_instr_o,
input logic alu_ready_i,
output logic alu_valid_o,
// ex just resolved our predicted branch, we are ready to accept new requests
input logic resolve_branch_i,
input logic lsu_ready_i,
output logic lsu_valid_o,
// branch prediction
input logic branch_ready_i,
output logic branch_valid_o, // use branch prediction unit
output branchpredict_sbe branch_predict_o,
input logic mult_ready_i,
output logic mult_valid_o, // Branch predict Out
input logic csr_ready_i,
output logic csr_valid_o,
// write back port
input logic [NR_WB_PORTS-1:0][TRANS_ID_BITS-1:0] trans_id_i,
input logic [NR_WB_PORTS-1:0][63:0] wdata_i,
input exception [NR_WB_PORTS-1:0] ex_ex_i, // exception from execute stage
input logic [NR_WB_PORTS-1:0] wb_valid_i,
// commit port
input logic[4:0] waddr_a_i,
input logic[63:0] wdata_a_i,
input logic we_a_i,
output scoreboard_entry commit_instr_o,
input logic commit_ack_i
input logic tsr_i
);
// ---------------------------------------------------
// Global signals
// ---------------------------------------------------
logic full;
logic decode_instr_ack;
// ---------------------------------------------------
// Scoreboard (SB) <-> Issue and Read Operands (iro)
// ---------------------------------------------------
fu_t [31:0] rd_clobber_sb_iro;
logic [4:0] rs1_iro_sb;
logic [63:0] rs1_sb_iro;
logic rs1_valid_sb_iro;
logic [4:0] rs2_iro_sb;
logic [63:0] rs2_sb_iro;
logic rs2_valid_iro_sb;
scoreboard_entry issue_instr_sb_iro;
logic issue_instr_valid_sb_iro;
logic issue_ack_iro_sb;
// ---------------------------------------------------
// Decoder (DC) <-> Scoreboard (SB)
// ---------------------------------------------------
scoreboard_entry decoded_instr_dc_sb;
// ---------------------------------------------------
// Decoder (DC) <-> Branch Logic
// ---------------------------------------------------
// register stage
struct packed {
logic valid;
scoreboard_entry sbe;
logic is_ctrl_flow;
} issue_n, issue_q;
logic is_control_flow_instr;
// ---------------------------------------------------
// Branch (resolve) logic
// ---------------------------------------------------
// This should basically prevent the scoreboard from accepting
// instructions past a branch. We need to resolve the branch beforehand.
// This limitation is in place to ease the backtracking of mis-predicted branches as they
// can simply be in the front-end of the processor.
logic unresolved_branch_n, unresolved_branch_q;
always_comb begin : unresolved_branch
unresolved_branch_n = unresolved_branch_q;
// we just resolved the branch
if (resolve_branch_i) begin
unresolved_branch_n = 1'b0;
end
// if the instruction is valid and it is a control flow instruction
if (fetch_entry_valid_i && is_control_flow_instr) begin
unresolved_branch_n = 1'b1;
end
// if we are requested to flush also flush the unresolved branch flag because either the flush
// was requested by a branch or an exception. In any case: any unresolved branch will get evicted
if (flush_unissued_instr_i || flush_i) begin
unresolved_branch_n = 1'b0;
end
end
// we are ready if we are not full and don't have any unresolved branches, but it can be
// the case that we have an unresolved branch which is cleared in that cycle (resolved_branch_i.valid == 1)
assign decoded_instr_ack_o = decode_instr_ack && !unresolved_branch_q;
scoreboard_entry decoded_instruction;
decoder decoder_i (
.pc_i ( fetch_entry_i.address ),
@ -142,59 +58,47 @@ module id_stage #(
.branch_predict_i ( fetch_entry_i.branch_predict ),
.is_illegal_i ( fetch_entry_i.is_illegal ),
.ex_i ( fetch_entry_i.ex ),
.instruction_o ( decoded_instr_dc_sb ),
.instruction_o ( decoded_instruction ),
.is_control_flow_instr_o ( is_control_flow_instr ),
.*
);
scoreboard #(
.NR_ENTRIES ( NR_ENTRIES ),
.NR_WB_PORTS ( NR_WB_PORTS )
)
scoreboard_i
(
.full_o ( full ),
.rd_clobber_o ( rd_clobber_sb_iro ),
.rs1_i ( rs1_iro_sb ),
.rs1_o ( rs1_sb_iro ),
.rs1_valid_o ( rs1_valid_sb_iro ),
.rs2_i ( rs2_iro_sb ),
.rs2_o ( rs2_sb_iro ),
.rs2_valid_o ( rs2_valid_iro_sb ),
.commit_instr_o ( commit_instr_o ),
.commit_ack_i ( commit_ack_i ),
.decoded_instr_ack_o ( decode_instr_ack ),
.decoded_instr_i ( decoded_instr_dc_sb ),
.decoded_instr_valid_i ( fetch_entry_valid_i ),
.issue_instr_o ( issue_instr_sb_iro ),
.issue_instr_valid_o ( issue_instr_valid_sb_iro ),
.issue_ack_i ( issue_ack_iro_sb ),
.trans_id_i ( trans_id_i ),
.wdata_i ( wdata_i ),
.ex_i ( ex_ex_i ),
.*
);
// ------------------
// Output Registers
// ------------------
assign issue_entry_o = issue_q.sbe;
assign issue_entry_valid_o = issue_q.valid;
assign is_ctrl_flow_o = issue_q.is_ctrl_flow;
always_comb begin
issue_n = issue_q;
decoded_instr_ack_o = 1'b0;
issue_read_operands issue_read_operands_i (
.issue_instr_i ( issue_instr_sb_iro ),
.issue_instr_valid_i ( issue_instr_valid_sb_iro ),
.issue_ack_o ( issue_ack_iro_sb ),
.rs1_o ( rs1_iro_sb ),
.rs1_i ( rs1_sb_iro ),
.rs1_valid_i ( rs1_valid_sb_iro ),
.rs2_o ( rs2_iro_sb ),
.rs2_i ( rs2_sb_iro ),
.rs2_valid_i ( rs2_valid_iro_sb ),
.rd_clobber_i ( rd_clobber_sb_iro ),
.*
);
// if we have a space in the register and the fetch is valid, go get it
if (!issue_q.valid && fetch_entry_valid_i) begin
decoded_instr_ack_o = 1'b1;
issue_n = { 1'b1, decoded_instruction, is_control_flow_instr};
issue_n.valid = 1'b1;
end
// we have something in the register but issue stage already acknowledged
if (issue_q.valid && issue_instr_ack_i) begin
decoded_instr_ack_o = 1'b1;
issue_n = { 1'b1, decoded_instruction, is_control_flow_instr};
end
// invalidate on a flush
if (flush_i)
issue_n.valid = 1'b0;
end
// -------------------------
// Registers (ID <-> Issue)
// -------------------------
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
unresolved_branch_q <= 1'b0;
if(~rst_ni) begin
issue_q <= '0;
end else begin
unresolved_branch_q <= unresolved_branch_n;
issue_q <= issue_n;
end
end

180
src/issue_stage.sv Executable file
View file

@ -0,0 +1,180 @@
// Author: Florian Zaruba, ETH Zurich
// Date: 21.05.2017
// Description: Issue stage dispatches instructions to the FUs
//
//
// Copyright (C) 2017 ETH Zurich, University of Bologna
// All rights reserved.
//
// This code is under development and not yet released to the public.
// Until it is released, the code is under the copyright of ETH Zurich and
// the University of Bologna, and may contain confidential and/or unpublished
// work. Any reuse/redistribution is strictly forbidden without written
// permission from ETH Zurich.
//
// Bug fixes and contributions will eventually be released under the
// SolderPad open hardware license in the context of the PULP platform
// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the
// University of Bologna.
//
import ariane_pkg::*;
module issue_stage #(
parameter int NR_ENTRIES = 4,
parameter int NR_WB_PORTS = 4
)(
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic test_en_i, // Test Enable
input logic flush_unissued_instr_i,
input logic flush_i,
// from ID
input scoreboard_entry decoded_instr_i,
input logic decoded_instr_valid_i,
input logic is_ctrl_flow_i,
output logic decoded_instr_ack_o,
// to EX
output fu_t fu_o,
output fu_op operator_o,
output logic [63:0] operand_a_o,
output logic [63:0] operand_b_o,
output logic [63:0] imm_o,
output logic [TRANS_ID_BITS-1:0] trans_id_o,
output logic [63:0] pc_o,
output logic is_compressed_instr_o,
input logic alu_ready_i,
output logic alu_valid_o,
// ex just resolved our predicted branch, we are ready to accept new requests
input logic resolve_branch_i,
input logic lsu_ready_i,
output logic lsu_valid_o,
// branch prediction
input logic branch_ready_i,
output logic branch_valid_o, // use branch prediction unit
output branchpredict_sbe branch_predict_o,
input logic mult_ready_i,
output logic mult_valid_o, // Branch predict Out
input logic csr_ready_i,
output logic csr_valid_o,
// write back port
input logic [NR_WB_PORTS-1:0][TRANS_ID_BITS-1:0] trans_id_i,
input logic [NR_WB_PORTS-1:0][63:0] wdata_i,
input exception [NR_WB_PORTS-1:0] ex_ex_i, // exception from execute stage
input logic [NR_WB_PORTS-1:0] wb_valid_i,
// commit port
input logic[4:0] waddr_a_i,
input logic[63:0] wdata_a_i,
input logic we_a_i,
output scoreboard_entry commit_instr_o,
input logic commit_ack_i
);
// ---------------------------------------------------
// Global signals
// ---------------------------------------------------
logic full;
logic decoded_instr_ack;
// ---------------------------------------------------
// Scoreboard (SB) <-> Issue and Read Operands (IRO)
// ---------------------------------------------------
fu_t [31:0] rd_clobber_sb_iro;
logic [4:0] rs1_iro_sb;
logic [63:0] rs1_sb_iro;
logic rs1_valid_sb_iro;
logic [4:0] rs2_iro_sb;
logic [63:0] rs2_sb_iro;
logic rs2_valid_iro_sb;
scoreboard_entry issue_instr_sb_iro;
logic issue_instr_valid_sb_iro;
logic issue_ack_iro_sb;
// ---------------------------------------------------
// Branch (resolve) logic
// ---------------------------------------------------
// This should basically prevent the scoreboard from accepting
// instructions past a branch. We need to resolve the branch beforehand.
// This limitation is in place to ease the backtracking of mis-predicted branches as they
// can simply be in the front-end of the processor.
logic unresolved_branch_n, unresolved_branch_q;
always_comb begin : unresolved_branch
unresolved_branch_n = unresolved_branch_q;
// we just resolved the branch
if (resolve_branch_i) begin
unresolved_branch_n = 1'b0;
end
// if the instruction is valid and it is a control flow instruction
if (decoded_instr_valid_i && is_ctrl_flow_i) begin
unresolved_branch_n = 1'b1;
end
// if we are requested to flush also flush the unresolved branch flag because either the flush
// was requested by a branch or an exception. In any case: any unresolved branch will get evicted
if (flush_unissued_instr_i || flush_i) begin
unresolved_branch_n = 1'b0;
end
end
// we are ready if we are not full and don't have any unresolved branches, but it can be
// the case that we have an unresolved branch which is cleared in that cycle (resolved_branch_i == 1)
assign decoded_instr_ack_o = ~full && (~unresolved_branch_q) && decoded_instr_ack;
issue_read_operands issue_read_operands_i (
.flush_i ( flush_unissued_instr_i ),
.issue_instr_i ( issue_instr_sb_iro ),
.issue_instr_valid_i ( issue_instr_valid_sb_iro ),
.issue_ack_o ( issue_ack_iro_sb ),
.rs1_o ( rs1_iro_sb ),
.rs1_i ( rs1_sb_iro ),
.rs1_valid_i ( rs1_valid_sb_iro ),
.rs2_o ( rs2_iro_sb ),
.rs2_i ( rs2_sb_iro ),
.rs2_valid_i ( rs2_valid_iro_sb ),
.rd_clobber_i ( rd_clobber_sb_iro ),
.*
);
scoreboard #(
.NR_ENTRIES ( NR_ENTRIES ),
.NR_WB_PORTS ( NR_WB_PORTS )
)
scoreboard_i
(
.full_o ( full ),
.rd_clobber_o ( rd_clobber_sb_iro ),
.rs1_i ( rs1_iro_sb ),
.rs1_o ( rs1_sb_iro ),
.rs1_valid_o ( rs1_valid_sb_iro ),
.rs2_i ( rs2_iro_sb ),
.rs2_o ( rs2_sb_iro ),
.rs2_valid_o ( rs2_valid_iro_sb ),
.decoded_instr_ack_o ( decoded_instr_ack ),
.issue_instr_o ( issue_instr_sb_iro ),
.issue_instr_valid_o ( issue_instr_valid_sb_iro ),
.issue_ack_i ( issue_ack_iro_sb ),
.trans_id_i ( trans_id_i ),
.wdata_i ( wdata_i ),
.ex_i ( ex_ex_i ),
.*
);
always_ff @(posedge clk_i or negedge rst_ni) begin
if (~rst_ni) begin
unresolved_branch_q <= 1'b0;
end else begin
unresolved_branch_q <= unresolved_branch_n;
end
end
endmodule

View file

@ -76,13 +76,6 @@ module scoreboard #(
logic [$clog2(NR_ENTRIES)-1:0] commit_pointer_n, commit_pointer_q;
logic issue_full;
struct packed {
logic valid;
scoreboard_entry sbe;
} decoded_instr_n, decoded_instr_q;
logic decoded_instr_ack;
// the issue queue is full don't issue any new instructions
assign issue_full = (issue_cnt_q == NR_ENTRIES-1);
assign full_o = issue_full;
@ -91,29 +84,13 @@ module scoreboard #(
// an instruction is ready for issue if we have place in the issue FIFO and it the decoder says it is valid
always_comb begin
issue_instr_o = decoded_instr_q;
issue_instr_o = decoded_instr_i;
// make sure we assign the correct trans ID
issue_instr_o.trans_id = issue_pointer_q;
issue_instr_valid_o = decoded_instr_q.valid;
decoded_instr_ack_o = decoded_instr_ack;
issue_instr_valid_o = decoded_instr_valid_i;
decoded_instr_ack_o = issue_ack_i;
end
always_comb begin : decode_if
decoded_instr_ack = 1'b0;
decoded_instr_n = decoded_instr_q;
if (issue_ack_i)
decoded_instr_n.valid = 1'b0;
if (!decoded_instr_q.valid && decoded_instr_valid_i) begin
decoded_instr_ack = 1'b1;
decoded_instr_n = {1'b1, decoded_instr_i};
end
if (decoded_instr_q.valid && !issue_full && issue_ack_i) begin
decoded_instr_ack = 1'b1;
decoded_instr_n = {1'b1, decoded_instr_i};
end
end
// maintain a FIFO with issued instructions
// keep track of all issued instructions
always_comb begin : issue_fifo
@ -254,13 +231,11 @@ module scoreboard #(
issue_cnt_q <= '0;
commit_pointer_q <= '0;
issue_pointer_q <= '0;
decoded_instr_q <= '0;
end else begin
mem_q <= mem_n;
issue_cnt_q <= issue_cnt_n;
commit_pointer_q <= commit_pointer_n;
issue_pointer_q <= issue_pointer_n;
decoded_instr_q <= decoded_instr_n;
end
end
`ifndef SYNTHESIS

View file

@ -3,9 +3,9 @@ add wave -noupdate -group pcgen_stage -group btb /core_tb/dut/pcgen_i/btb_i/*
add wave -noupdate -group pcgen_stage /core_tb/dut/pcgen_i/*
add wave -noupdate -group if_stage -group fetch_fifo /core_tb/dut/if_stage_i/fetch_fifo_i/*
add wave -noupdate -group if_stage /core_tb/dut/if_stage_i/*
add wave -noupdate -group id_stage -group scoreboard /core_tb/dut/id_stage_i/scoreboard_i/*
add wave -noupdate -group id_stage -group decoder /core_tb/dut/id_stage_i/decoder_i/*
add wave -noupdate -group id_stage -group issue_read_operands /core_tb/dut/id_stage_i/issue_read_operands_i/*
add wave -noupdate -group issue_stage -group scoreboard /core_tb/dut/issue_stage_i/scoreboard_i/*
add wave -noupdate -group issue_stage -group issue_read_operands /core_tb/dut/issue_stage_i/issue_read_operands_i/*
add wave -noupdate -group id_stage /core_tb/dut/id_stage_i/*
add wave -noupdate -group ex_stage -group alu /core_tb/dut/ex_stage_i/alu_i/*
add wave -noupdate -group ex_stage -group lsu -group mmu /core_tb/dut/ex_stage_i/lsu_i/mmu_i/*