diff --git a/alu.sv b/alu.svh similarity index 100% rename from alu.sv rename to alu.svh diff --git a/ariane.sv b/ariane.svh similarity index 100% rename from ariane.sv rename to ariane.svh diff --git a/ariane_pkg.sv b/ariane_pkg.sv new file mode 100644 index 000000000..3fdfc921a --- /dev/null +++ b/ariane_pkg.sv @@ -0,0 +1,16 @@ +/* File: ariane_pkg.svh + * Author: Florian Zaruba + * Date: 8.4.2017 + * + * Copyright (C) 2017 ETH Zurich, University of Bologna + * All rights reserved. + * + * Description: Contains all the necessary defines for Ariane + * in one package. + */ +package ariane_pkg; + +`include "include/ariane_defines.svh" +`include "scoreboard.svh" + +endpackage : ariane_pkg diff --git a/include/ariane_pkg.svh b/include/ariane_defines.svh similarity index 85% rename from include/ariane_pkg.svh rename to include/ariane_defines.svh index ed823636e..1c86f3aed 100644 --- a/include/ariane_pkg.svh +++ b/include/ariane_defines.svh @@ -1,15 +1,3 @@ -/* File: ariane_pkg.svh - * Author: Florian Zaruba - * Date: 8.4.2017 - * - * Copyright (C) 2017 ETH Zurich, University of Bologna - * All rights reserved. - * - * Description: Contains all the necessary defines for Ariane - * in one package. - */ -package ariane_pkg; - // --------------- // Fetch Stage // --------------- @@ -72,5 +60,3 @@ typedef struct packed { logic in_flight; exception ex; } scoreboard_entry; - -endpackage diff --git a/regfile.sv b/regfile.svh similarity index 100% rename from regfile.sv rename to regfile.svh diff --git a/scoreboard.sv b/scoreboard.sv index 26cdeca78..4658ee624 100644 --- a/scoreboard.sv +++ b/scoreboard.sv @@ -5,8 +5,6 @@ * Copyright (C) 2017 ETH Zurich, University of Bologna * All rights reserved. */ -import ariane_pkg::*; - module scoreboard #( parameter int NR_ENTRIES = 8, parameter type dtype = scoreboard_entry diff --git a/scoreboard.svh b/scoreboard.svh new file mode 100644 index 000000000..26cdeca78 --- /dev/null +++ b/scoreboard.svh @@ -0,0 +1,220 @@ +/* File: scoreboard.sv + * Author: Florian Zaruba + * Date: 8.4.2017 + * + * Copyright (C) 2017 ETH Zurich, University of Bologna + * All rights reserved. + */ +import ariane_pkg::*; + +module scoreboard #( + parameter int NR_ENTRIES = 8, + parameter type dtype = scoreboard_entry + ) + ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + output logic full_o, // We can't take anymore data + input logic flush_i, + // list of clobbered registers to issue stage + output logic [31:0][$bits(fu_t)-1:0] rd_clobber_o, + + // regfile like interface to operand read stage + input logic [4:0] rs1_i, + output logic [63:0] rs1_o, + output logic rs1_valid_o, + + input logic [4:0] rs2_i, + output logic [63:0] rs2_o, + output logic rs2_valid_o, + + // advertise instruction to commit stage, if ready_i is asserted advance the commit pointer + output dtype commit_instr_o, + input logic commit_ready_i, + + // instruction to put on top of scoreboard e.g.: top pointer + // we can always put this instruction to the top unless we signal with asserted full_o + input dtype decoded_instr_i, + input logic decoded_instr_valid_i, + + // instruction to issue logic, if issue_instr_valid and issue_ready is asserted, advance the issue pointer + output dtype issue_instr_o, + output logic issue_instr_valid_o, + input logic issue_ready_i, + + // write-back port + input logic[63:0] pc_i, // PC at which to write the result back + input logic[63:0] wdata_i, // write data in + input logic wb_valid_i // data in is valid +); +localparam BITS_ENTRIES = $clog2(NR_ENTRIES); + +dtype [NR_ENTRIES-1:0] mem; +logic [BITS_ENTRIES-1:0] issue_pointer_n, issue_pointer_q, // points to the instruction currently in issue + commit_pointer_n, commit_pointer_q, // points to the instruction currently in commit + top_pointer_n, top_pointer_q; // points to the top of the scoreboard, an empty slot + +logic pointer_overflow; +logic empty; + +// full and empty signaling: signal that we are not able to take new instructions +// track pointer overflow +assign pointer_overflow = (commit_pointer_q <= top_pointer_q) ? 1'b0 : 1'b1; +assign full_o = (pointer_overflow) ? (commit_pointer_q == top_pointer_q) : 1'b0; +assign empty = (pointer_overflow) ? 1'b0 : (commit_pointer_q == top_pointer_q); + +// rd_clobber output: output currently clobbered destination registers +// but only between commit and issue pointer +// normal case: overflow case: +// ._________________________. ._________________________. +// |_________________________| |_________________________| +// |_________________________|<- commit pointer |_________________________|<- issue pointer +// |_________________________| |_________________________|<- top pointer +// |_________________________|<- issue pointer |_________________________|<- commit pointer +// |_________________________|<- top pointer |_________________________| +// +always_comb begin : clobber_output + rd_clobber_o = '{default: 0}; + + // excluding issue, the issue pointer points to the instruction which is currently not issued + // but might be issued as soon as the issue unit acknowledges + if (commit_pointer_q < issue_pointer_q) begin + for (int unsigned i = 0; i < NR_ENTRIES; i++) begin + // non overflowed case, depicted on the left + if (i[BITS_ENTRIES-1:0] >= commit_pointer_q && i[BITS_ENTRIES-1:0] < issue_pointer_q) + rd_clobber_o[mem[i].rd] = mem[i].fu; + end + end else begin // the issue pointer has overflowed, invert logic, depicted on the right + for (int unsigned i = 0; i < NR_ENTRIES; i++) begin + if (i[BITS_ENTRIES-1:0] >= commit_pointer_q || i[BITS_ENTRIES-1:0] < issue_pointer_q) + rd_clobber_o[mem[i].rd] = mem[i].fu; + end + end + // the zero register is always free + rd_clobber_o[0] = NONE; +end +// read operand interface: same logic as register file, including a valid file +always_comb begin : read_operands + + rs1_o = 64'b0; + rs2_o = 64'b0; + rs1_valid_o = 1'b0; + rs2_valid_o = 1'b0; + + if (commit_pointer_q < issue_pointer_q) begin + for (int unsigned i = 0; i < NR_ENTRIES; i++) begin + if (i[BITS_ENTRIES-1:0] >= commit_pointer_q && i[BITS_ENTRIES-1:0] < issue_pointer_q) begin + // look at the appropriate fields and look whether there was an + // instruction that wrote the rd field before, first for RS1 and then for RS2 + if (mem[i[BITS_ENTRIES-1:0]].rd == rs1_i) begin + rs1_o = mem[i].result; + rs1_valid_o = mem[i].valid; + // do the same for rs2 + end else if (mem[i].rd == rs2_i) begin + rs2_o = mem[i].result; + rs2_valid_o = mem[i].valid; + end + end + end + end else begin // the issue pointer has overflowed, invert logic + for (int unsigned i = 0; i < NR_ENTRIES; i++) begin + if (i[BITS_ENTRIES-1:0] >= commit_pointer_q || i[BITS_ENTRIES-1:0] < issue_pointer_q) begin + // same as above but for the overflowed pointer case + if (mem[i[BITS_ENTRIES-1:0]].rd == rs1_i) begin + rs1_o = mem[i].result; + rs1_valid_o = mem[i].valid; + // do the same for rs2 + end else if (mem[i[BITS_ENTRIES-1:0]].rd == rs2_i) begin + rs2_o = mem[i].result; + rs2_valid_o = mem[i].valid; + end + end + end + end + // make sure we didn't read the zero register + if (rs1_i == '0) + rs1_valid_o = 1'b0; + if (rs2_i == '0) + rs2_valid_o = 1'b0; +end +// push new decoded instruction: if still empty space push the instruction to the scoreboard +// write-back instruction: update value of RD register in scoreboard +always_latch begin : push_instruction_and_wb + // default assignment + top_pointer_n = top_pointer_q; + // if we are not full we can push a new instruction + if (~full_o && decoded_instr_valid_i) begin + mem[$unsigned(top_pointer_q)] = decoded_instr_i; + top_pointer_n = top_pointer_q + 1; + end + + // write back: + // look for the intruction with the given PC and write the result data back + // also set the valid bit + if (wb_valid_i) begin + for (int unsigned i = 0; i < NR_ENTRIES; i++) begin + if (mem[i].pc == pc_i) begin + mem[i].valid = 1'b1; + mem[i].result = wdata_i; + end + end + end + + // flush signal + if (flush_i) + mem = '{default: 0}; + +end + +// issue instruction: advance the issue pointer +always_comb begin : issue_instruction + + // provide a combinatorial path in case the scoreboard is empty + if (empty) begin + issue_instr_o = decoded_instr_i; + issue_instr_valid_o = decoded_instr_valid_i; + // if not empty go to scoreboard and get the instruction at the issue pointer + end else begin + issue_instr_o = mem[$unsigned(issue_pointer_q)]; + issue_instr_valid_o = 1'b1; + end + // default assignment: issue didn't read + issue_pointer_n = issue_pointer_q; + // advance pointer if the issue logic was ready + if (issue_ready_i) begin + issue_pointer_n = issue_pointer_q + 1; + end + +end + +// commit instruction: remove from scoreboard, advance pointer +always_comb begin: commit_instruction + commit_pointer_n = commit_pointer_q; + // we can always safely output the instruction at which the commit pointer points + // since the scoreboard entry has a valid bit which the commit stage needs to check anyway + commit_instr_o = mem[commit_pointer_q]; + if (commit_ready_i) begin + commit_pointer_n = commit_pointer_q + 1; + end +end + +// sequential process +always_ff @(posedge clk_i or negedge rst_ni) begin : sequential + if(~rst_ni) begin + issue_pointer_q <= '{default: 0}; + commit_pointer_q <= '{default: 0}; + top_pointer_q <= '{default: 0}; + end else begin + issue_pointer_q <= issue_pointer_n; + commit_pointer_q <= commit_pointer_n; + top_pointer_q <= top_pointer_n; + end +end + +`ifndef SYNTHESIS +`ifndef verilator + assert (NR_ENTRIES == 2**$clog2(NR_ENTRIES)) else $error("Scoreboard size needs to be a power of two."); + // there should never be more than one instruction writing the same destination register (except x0) +`endif +`endif +endmodule \ No newline at end of file