Reorganized ariane package as a standalone unit

This commit is contained in:
Florian Zaruba 2017-04-09 13:11:15 +02:00
parent 1ae06d5464
commit bc4716764f
7 changed files with 236 additions and 16 deletions

View file

16
ariane_pkg.sv Normal file
View file

@ -0,0 +1,16 @@
/* File: ariane_pkg.svh
* Author: Florian Zaruba <zarubaf@ethz.ch>
* 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

View file

@ -1,15 +1,3 @@
/* File: ariane_pkg.svh
* Author: Florian Zaruba <zarubaf@ethz.ch>
* 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

View file

@ -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

220
scoreboard.svh Normal file
View file

@ -0,0 +1,220 @@
/* File: scoreboard.sv
* Author: Florian Zaruba <zarubaf@ethz.ch>
* 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