Skip to content

Commit

Permalink
superscalar: return 2 instructions from instruction queue (#2022)
Browse files Browse the repository at this point in the history
  • Loading branch information
cathales committed Apr 9, 2024
1 parent 512296b commit 1c529d6
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 83 deletions.
20 changes: 11 additions & 9 deletions core/cva6.sv
Original file line number Diff line number Diff line change
Expand Up @@ -354,9 +354,9 @@ module cva6
// --------------
// IF <-> ID
// --------------
fetch_entry_t fetch_entry_if_id;
logic fetch_valid_if_id;
logic fetch_ready_id_if;
fetch_entry_t [ariane_pkg::SUPERSCALAR:0] fetch_entry_if_id;
logic [ariane_pkg::SUPERSCALAR:0] fetch_valid_if_id;
logic [ariane_pkg::SUPERSCALAR:0] fetch_ready_id_if;

// --------------
// ID <-> ISSUE
Expand Down Expand Up @@ -1102,7 +1102,9 @@ module cva6
.itlb_miss_i (itlb_miss_ex_perf),
.dtlb_miss_i (dtlb_miss_ex_perf),
.sb_full_i (sb_full),
.if_empty_i (~fetch_valid_if_id),
// TODO this is more complex that that
// If superscalar then we additionally have to check [1] when transaction 0 succeeded
.if_empty_i (~fetch_valid_if_id[0]),
.ex_i (ex_commit),
.eret_i (eret),
.resolved_branch_i (resolved_branch),
Expand Down Expand Up @@ -1517,9 +1519,9 @@ module cva6
.rstn(rst_ni),
.flush_unissued(flush_unissued_instr_ctrl_id),
.flush_all(flush_ctrl_ex),
.instruction(id_stage_i.fetch_entry_i.instruction),
.fetch_valid(id_stage_i.fetch_entry_valid_i),
.fetch_ack(id_stage_i.fetch_entry_ready_o),
.instruction(id_stage_i.fetch_entry_i[0].instruction),
.fetch_valid(id_stage_i.fetch_entry_valid_i[0]),
.fetch_ack(id_stage_i.fetch_entry_ready_o[0]),
.issue_ack(issue_stage_i.i_scoreboard.issue_ack_i),
.issue_sbe(issue_stage_i.i_scoreboard.issue_instr_o),
.waddr(waddr_commit_id),
Expand Down Expand Up @@ -1612,8 +1614,8 @@ module cva6

.flush_i (flush_ctrl_if),
.issue_instr_ack_i (issue_instr_issue_id),
.fetch_entry_valid_i(fetch_valid_if_id),
.instruction_i (fetch_entry_if_id.instruction),
.fetch_entry_valid_i(fetch_valid_if_id[0]),
.instruction_i (fetch_entry_if_id[0].instruction),
.is_compressed_i (rvfi_is_compressed),

.issue_pointer_i (rvfi_issue_pointer),
Expand Down
6 changes: 3 additions & 3 deletions core/frontend/frontend.sv
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ module frontend
// Handshake between CACHE and FRONTEND (fetch) - CACHES
input icache_drsp_t icache_dreq_i,
// Handshake's data between fetch and decode - ID_STAGE
output fetch_entry_t fetch_entry_o,
output fetch_entry_t [ariane_pkg::SUPERSCALAR:0] fetch_entry_o,
// Handshake's valid between fetch and decode - ID_STAGE
output logic fetch_entry_valid_o,
output logic [ariane_pkg::SUPERSCALAR:0] fetch_entry_valid_o,
// Handshake's ready between fetch and decode - ID_STAGE
input logic fetch_entry_ready_i
input logic [ariane_pkg::SUPERSCALAR:0] fetch_entry_ready_i
);

localparam type bht_update_t = struct packed {
Expand Down
169 changes: 111 additions & 58 deletions core/frontend/instr_queue.sv
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ module instr_queue
// Address at which to replay the fetch - FRONTEND
output logic [CVA6Cfg.VLEN-1:0] replay_addr_o,
// Handshake’s data with ID_STAGE - ID_STAGE
output fetch_entry_t fetch_entry_o,
output fetch_entry_t [ariane_pkg::SUPERSCALAR:0] fetch_entry_o,
// Handshake’s valid with ID_STAGE - ID_STAGE
output logic fetch_entry_valid_o,
output logic [ariane_pkg::SUPERSCALAR:0] fetch_entry_valid_o,
// Handshake’s ready with ID_STAGE - ID_STAGE
input logic fetch_entry_ready_i
input logic [ariane_pkg::SUPERSCALAR:0] fetch_entry_ready_i
);

typedef struct packed {
Expand Down Expand Up @@ -119,12 +119,19 @@ ariane_pkg::FETCH_FIFO_DEPTH
logic address_overflow;
// input stream counter
logic [CVA6Cfg.LOG2_INSTR_PER_FETCH-1:0] idx_is_d, idx_is_q;

// Registers
// output FIFO select, one-hot
logic [CVA6Cfg.INSTR_PER_FETCH-1:0] idx_ds_d, idx_ds_q;
// rotated by N
logic [ariane_pkg::SUPERSCALAR+1:0][CVA6Cfg.INSTR_PER_FETCH-1:0] idx_ds;

logic [CVA6Cfg.VLEN-1:0] pc_d, pc_q; // current PC
logic [ariane_pkg::SUPERSCALAR+1:0][CVA6Cfg.VLEN-1:0] pc_j;
logic reset_address_d, reset_address_q; // we need to re-set the address because of a flush

logic [ariane_pkg::SUPERSCALAR:0] fetch_entry_is_cf, fetch_entry_fire;

logic [CVA6Cfg.INSTR_PER_FETCH*2-2:0] branch_mask_extended;
logic [CVA6Cfg.INSTR_PER_FETCH-1:0] branch_mask;
logic branch_empty;
Expand Down Expand Up @@ -294,111 +301,157 @@ ariane_pkg::FETCH_FIFO_DEPTH
// Downstream interface
// ----------------------
// as long as there is at least one queue which can take the value we have a valid instruction
assign fetch_entry_valid_o = ~(&instr_queue_empty);
assign fetch_entry_valid_o[0] = ~(&instr_queue_empty);
if (ariane_pkg::SUPERSCALAR) begin : gen_fetch_entry_valid_1
// TODO Maybe this additional fetch_entry_is_cf check is useless as issue-stage already performs it?
assign fetch_entry_valid_o[1] = ~|(instr_queue_empty & idx_ds[1]) & ~(&fetch_entry_is_cf);
end

assign idx_ds[0] = idx_ds_q;
for (genvar i = 0; i <= ariane_pkg::SUPERSCALAR; i++) begin
if (CVA6Cfg.INSTR_PER_FETCH > 1) begin
assign idx_ds[i+1] = {
idx_ds[i][CVA6Cfg.INSTR_PER_FETCH-2:0], idx_ds[i][CVA6Cfg.INSTR_PER_FETCH-1]
};
end else begin
assign idx_ds[i+1] = idx_ds[i];
end
end

if (ariane_pkg::RVC) begin : gen_downstream_itf_with_c
always_comb begin
idx_ds_d = idx_ds_q;
idx_ds_d = idx_ds_q;

pop_instr = '0;
// assemble fetch entry
fetch_entry_o.instruction = '0;
fetch_entry_o.address = pc_q;
fetch_entry_o.ex.valid = 1'b0;
fetch_entry_o.ex.cause = '0;

fetch_entry_o.ex.tval = '0;
fetch_entry_o.ex.tval2 = '0;
fetch_entry_o.ex.gva = 1'b0;
fetch_entry_o.ex.tinst = '0;
fetch_entry_o.branch_predict.predict_address = address_out;
fetch_entry_o.branch_predict.cf = ariane_pkg::NoCF;
for (int unsigned i = 0; i <= ariane_pkg::SUPERSCALAR; i++) begin
fetch_entry_o[i].instruction = '0;
fetch_entry_o[i].address = pc_j[i];
fetch_entry_o[i].ex.valid = 1'b0;
fetch_entry_o[i].ex.cause = '0;

fetch_entry_o[i].ex.tval = '0;
fetch_entry_o[i].ex.tval2 = '0;
fetch_entry_o[i].ex.gva = 1'b0;
fetch_entry_o[i].ex.tinst = '0;
fetch_entry_o[i].branch_predict.predict_address = address_out;
fetch_entry_o[i].branch_predict.cf = ariane_pkg::NoCF;
end

// output mux select
for (int unsigned i = 0; i < CVA6Cfg.INSTR_PER_FETCH; i++) begin
if (idx_ds_q[i]) begin
// TODO handle fetch_entry_o[1] if superscalar
if (idx_ds[0][i]) begin
if (instr_data_out[i].ex == ariane_pkg::FE_INSTR_ACCESS_FAULT) begin
fetch_entry_o.ex.cause = riscv::INSTR_ACCESS_FAULT;
fetch_entry_o[0].ex.cause = riscv::INSTR_ACCESS_FAULT;
end else if (CVA6Cfg.RVH && instr_data_out[i].ex == ariane_pkg::FE_INSTR_GUEST_PAGE_FAULT) begin
fetch_entry_o.ex.cause = riscv::INSTR_GUEST_PAGE_FAULT;
fetch_entry_o[0].ex.cause = riscv::INSTR_GUEST_PAGE_FAULT;
end else begin
fetch_entry_o.ex.cause = riscv::INSTR_PAGE_FAULT;
fetch_entry_o[0].ex.cause = riscv::INSTR_PAGE_FAULT;
end
fetch_entry_o.instruction = instr_data_out[i].instr;
fetch_entry_o.ex.valid = instr_data_out[i].ex != ariane_pkg::FE_NONE;
fetch_entry_o[0].instruction = instr_data_out[i].instr;
fetch_entry_o[0].ex.valid = instr_data_out[i].ex != ariane_pkg::FE_NONE;
if (CVA6Cfg.TvalEn)
fetch_entry_o.ex.tval = {
fetch_entry_o[0].ex.tval = {
{(CVA6Cfg.XLEN - CVA6Cfg.VLEN) {1'b0}}, instr_data_out[i].ex_vaddr
};
if (CVA6Cfg.RVH) begin
fetch_entry_o.ex.tval2 = instr_data_out[i].ex_gpaddr;
fetch_entry_o.ex.tinst = instr_data_out[i].ex_tinst;
fetch_entry_o.ex.gva = instr_data_out[i].ex_gva;
fetch_entry_o[0].ex.tval2 = instr_data_out[i].ex_gpaddr;
fetch_entry_o[0].ex.tinst = instr_data_out[i].ex_tinst;
fetch_entry_o[0].ex.gva = instr_data_out[i].ex_gva;
end
fetch_entry_o[0].branch_predict.cf = instr_data_out[i].cf;
pop_instr[i] = fetch_entry_fire[0];
end

if (ariane_pkg::SUPERSCALAR) begin
if (idx_ds[1][i]) begin
if (instr_data_out[i].ex == ariane_pkg::FE_INSTR_ACCESS_FAULT) begin
fetch_entry_o[1].ex.cause = riscv::INSTR_ACCESS_FAULT;
end else begin
fetch_entry_o[1].ex.cause = riscv::INSTR_PAGE_FAULT;
end
fetch_entry_o[1].instruction = instr_data_out[i].instr;
fetch_entry_o[1].ex.valid = instr_data_out[i].ex != ariane_pkg::FE_NONE;
fetch_entry_o[1].ex.tval = {{64 - riscv::VLEN{1'b0}}, instr_data_out[i].ex_vaddr};
fetch_entry_o[1].branch_predict.cf = instr_data_out[i].cf;
// Cannot output two CF the same cycle.
pop_instr[i] = fetch_entry_fire[1];
end
fetch_entry_o.branch_predict.cf = instr_data_out[i].cf;
pop_instr[i] = fetch_entry_valid_o & fetch_entry_ready_i;
end
end
// rotate the pointer left
if (fetch_entry_ready_i) begin
idx_ds_d = {idx_ds_q[CVA6Cfg.INSTR_PER_FETCH-2:0], idx_ds_q[CVA6Cfg.INSTR_PER_FETCH-1]};
if (fetch_entry_fire[0]) begin
if (ariane_pkg::SUPERSCALAR) begin
idx_ds_d = fetch_entry_fire[1] ? idx_ds[2] : idx_ds[1];
end else begin
idx_ds_d = idx_ds[1];
end
end
end
end else begin : gen_downstream_itf_without_c
always_comb begin
idx_ds_d = '0;
idx_is_d = '0;
fetch_entry_o.instruction = instr_data_out[0].instr;
fetch_entry_o.address = pc_q;
fetch_entry_o[0].instruction = instr_data_out[0].instr;
fetch_entry_o[0].address = pc_q;

fetch_entry_o.ex.valid = instr_data_out[0].ex != ariane_pkg::FE_NONE;
fetch_entry_o[0].ex.valid = instr_data_out[0].ex != ariane_pkg::FE_NONE;
if (instr_data_out[0].ex == ariane_pkg::FE_INSTR_ACCESS_FAULT) begin
fetch_entry_o.ex.cause = riscv::INSTR_ACCESS_FAULT;
fetch_entry_o[0].ex.cause = riscv::INSTR_ACCESS_FAULT;
end else begin
fetch_entry_o.ex.cause = riscv::INSTR_PAGE_FAULT;
fetch_entry_o[0].ex.cause = riscv::INSTR_PAGE_FAULT;
end
if (CVA6Cfg.TvalEn)
fetch_entry_o.ex.tval = {{64 - CVA6Cfg.VLEN{1'b0}}, instr_data_out[0].ex_vaddr};
else fetch_entry_o.ex.tval = '0;
fetch_entry_o[0].ex.tval = {{64 - CVA6Cfg.VLEN{1'b0}}, instr_data_out[0].ex_vaddr};
else fetch_entry_o[0].ex.tval = '0;
if (CVA6Cfg.RVH) begin
fetch_entry_o.ex.tval2 = instr_data_out[0].ex_gpaddr;
fetch_entry_o.ex.tinst = instr_data_out[0].ex_tinst;
fetch_entry_o.ex.gva = instr_data_out[0].ex_gva;
fetch_entry_o[0].ex.tval2 = instr_data_out[0].ex_gpaddr;
fetch_entry_o[0].ex.tinst = instr_data_out[0].ex_tinst;
fetch_entry_o[0].ex.gva = instr_data_out[0].ex_gva;
end else begin
fetch_entry_o.ex.tval2 = '0;
fetch_entry_o.ex.tinst = '0;
fetch_entry_o.ex.gva = 1'b0;
fetch_entry_o[0].ex.tval2 = '0;
fetch_entry_o[0].ex.tinst = '0;
fetch_entry_o[0].ex.gva = 1'b0;
end

fetch_entry_o.branch_predict.predict_address = address_out;
fetch_entry_o.branch_predict.cf = instr_data_out[0].cf;
fetch_entry_o[0].branch_predict.predict_address = address_out;
fetch_entry_o[0].branch_predict.cf = instr_data_out[0].cf;

pop_instr[0] = fetch_entry_valid_o & fetch_entry_ready_i;
pop_instr[0] = fetch_entry_valid_o[0] & fetch_entry_ready_i[0];
end
end

// TODO(zarubaf): This needs to change for dual-issue
// if the handshaking is successful and we had a prediction pop one address entry
assign pop_address = ((fetch_entry_o.branch_predict.cf != ariane_pkg::NoCF) & |pop_instr);
for (genvar i = 0; i <= ariane_pkg::SUPERSCALAR; i++) begin
assign fetch_entry_is_cf[i] = fetch_entry_o[i].branch_predict.cf != ariane_pkg::NoCF;
assign fetch_entry_fire[i] = fetch_entry_valid_o[i] & fetch_entry_ready_i[i];
end

assign pop_address = |(fetch_entry_is_cf & fetch_entry_fire);

// ----------------------
// Calculate (Next) PC
// ----------------------
assign pc_j[0] = pc_q;
for (genvar i = 0; i <= ariane_pkg::SUPERSCALAR; i++) begin
assign pc_j[i+1] = fetch_entry_is_cf[i] ? address_out : (
pc_j[i] + ((fetch_entry_o[i].instruction[1:0] != 2'b11) ? 'd2 : 'd4)
);
end

always_comb begin
pc_d = pc_q;
reset_address_d = flush_i ? 1'b1 : reset_address_q;

if (fetch_entry_ready_i) begin
// TODO(zarubaf): This needs to change for a dual issue implementation
// advance the PC
if (ariane_pkg::RVC == 1'b1) begin : gen_pc_with_c_extension
pc_d = pc_q + ((fetch_entry_o.instruction[1:0] != 2'b11) ? 'd2 : 'd4);
end else begin : gen_pc_without_c_extension
pc_d = pc_q + 'd4;
if (fetch_entry_fire[0]) begin
pc_d = pc_j[1];
if (ariane_pkg::SUPERSCALAR) begin
if (fetch_entry_fire[1]) begin
pc_d = pc_j[2];
end
end
end

if (pop_address) pc_d = address_out;

// we previously flushed so we need to reset the address
if (valid_i[0] && reset_address_q) begin
// this is the base of the first instruction
Expand Down
26 changes: 13 additions & 13 deletions core/id_stage.sv
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ module id_stage #(
// Debug (async) request - SUBSYSTEM
input logic debug_req_i,
// Handshake's data between fetch and decode - FRONTEND
input fetch_entry_t fetch_entry_i,
input fetch_entry_t [ariane_pkg::SUPERSCALAR:0] fetch_entry_i,
// Handshake's valid between fetch and decode - FRONTEND
input logic fetch_entry_valid_i,
input logic [ariane_pkg::SUPERSCALAR:0] fetch_entry_valid_i,
// Handshake's ready between fetch and decode - FRONTEND
output logic fetch_entry_ready_o,
output logic [ariane_pkg::SUPERSCALAR:0] fetch_entry_ready_o,
// Handshake's data between decode and issue - ISSUE
output scoreboard_entry_t issue_entry_o,
// Instruction value - ISSUE
Expand Down Expand Up @@ -109,7 +109,7 @@ module id_stage #(
compressed_decoder #(
.CVA6Cfg(CVA6Cfg)
) compressed_decoder_i (
.instr_i (fetch_entry_i.instruction),
.instr_i (fetch_entry_i[0].instruction),
.instr_o (compressed_instr),
.illegal_instr_o (is_illegal),
.is_compressed_o (is_compressed),
Expand Down Expand Up @@ -142,7 +142,7 @@ module id_stage #(
assign is_double_rd_macro_instr_o = '0;
end
end else begin
assign instruction = fetch_entry_i.instruction;
assign instruction = fetch_entry_i[0].instruction;
assign is_illegal_cmp = '0;
assign is_compressed_cmp = '0;
assign is_macro_instr_i = '0;
Expand All @@ -166,16 +166,16 @@ module id_stage #(
.debug_req_i,
.irq_ctrl_i,
.irq_i,
.pc_i (fetch_entry_i.address),
.pc_i (fetch_entry_i[0].address),
.is_compressed_i (is_compressed_cmp),
.is_macro_instr_i (is_macro_instr_i),
.is_last_macro_instr_i (is_last_macro_instr_o),
.is_double_rd_macro_instr_i(is_double_rd_macro_instr_o),
.is_illegal_i (is_illegal_cmp),
.instruction_i (instruction),
.compressed_instr_i (fetch_entry_i.instruction[15:0]),
.branch_predict_i (fetch_entry_i.branch_predict),
.ex_i (fetch_entry_i.ex),
.compressed_instr_i (fetch_entry_i[0].instruction[15:0]),
.branch_predict_i (fetch_entry_i[0].branch_predict),
.ex_i (fetch_entry_i[0].ex),
.priv_lvl_i (priv_lvl_i),
.v_i (v_i),
.debug_mode_i (debug_mode_i),
Expand Down Expand Up @@ -205,19 +205,19 @@ module id_stage #(

always_comb begin
issue_n = issue_q;
fetch_entry_ready_o = 1'b0;
fetch_entry_ready_o = '0;

// Clear the valid flag if issue has acknowledged the instruction
if (issue_instr_ack_i) issue_n.valid = 1'b0;

// if we have a space in the register and the fetch is valid, go get it
// or the issue stage is currently acknowledging an instruction, which means that we will have space
// for a new instruction
if ((!issue_q.valid || issue_instr_ack_i) && fetch_entry_valid_i) begin
if ((!issue_q.valid || issue_instr_ack_i) && fetch_entry_valid_i[0]) begin
if (stall_instr_fetch) begin
fetch_entry_ready_o = 1'b0;
fetch_entry_ready_o[0] = 1'b0;
end else begin
fetch_entry_ready_o = 1'b1;
fetch_entry_ready_o[0] = 1'b1;
end
issue_n = '{1'b1, decoded_instruction, orig_instr, is_control_flow_instr};
end
Expand Down

0 comments on commit 1c529d6

Please sign in to comment.