Pipelined implementation of read operands

This commit is contained in:
Florian Zaruba 2017-04-15 11:15:48 +02:00
parent 28c86ce521
commit 5ccee278b7
3 changed files with 123 additions and 77 deletions

View file

@ -1,31 +1,41 @@
import ariane_pkg::*;
module id_stage (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic test_en_i, // Test Enable
module id_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_i,
input logic flush_i,
// to IF
input logic [31:0] instruction_i,
input logic instruction_valid_i,
output logic ready_o, // id is ready
output alu_op operator_o,
output logic [63:0] operand_a_o,
output logic [63:0] operand_b_o,
input logic [31:0] instruction_i,
input logic instruction_valid_i,
output logic ready_o, // id is ready
output alu_op operator_o,
output logic [63:0] operand_a_o,
output logic [63:0] operand_b_o,
input logic alu_ready_i,
output logic alu_valid_o,
input logic alu_ready_i,
output logic alu_valid_o,
input logic lsu_ready_i,
output logic lsu_valid_o,
input logic lsu_ready_i,
output logic lsu_valid_o,
input logic mult_ready_i,
output logic mult_valid_o,
input logic mult_ready_i,
output logic mult_valid_o,
// write back port
input logic [4:0] waddr_a_i,
input logic [63:0] wdata_a_i,
input logic we_a_i
input logic [NR_WB_PORTS-1:0][63:0] pc_i,
input logic [NR_WB_PORTS-1:0][63:0] wdata_i,
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,
// just for synthesis
input scoreboard_entry decoded_instr_i,
input logic decoded_instr_valid_i
);
@ -39,19 +49,19 @@ module id_stage (
logic rs2_valid_o;
scoreboard_entry commit_instr_o;
logic commit_ack_i;
scoreboard_entry decoded_instr_i;
logic decoded_instr_valid_i;
scoreboard_entry issue_instr_o;
logic issue_instr_valid_o;
logic issue_ack_i;
logic [63:0] pc_i;
logic [63:0] wdata_i;
logic wb_valid_i;
// ToDo: Branching logic
assign ready_o = ~full_o;
scoreboard scoreboard_i (
scoreboard #(
.NR_ENTRIES ( NR_ENTRIES ),
.NR_WB_PORTS ( NR_WB_PORTS )
)
scoreboard_i
(
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.full_o (full_o ),
@ -79,6 +89,7 @@ scoreboard scoreboard_i (
issue_read_operands issue_read_operands_i (
.clk_i (clk_i ),
.rst_ni (rst_ni ),
.flush_i (flush_i ),
.test_en_i (test_en_i ),
.issue_instr_i (issue_instr_o ),
.issue_instr_valid_i(issue_instr_valid_o),

View file

@ -14,6 +14,8 @@ module issue_read_operands (
input logic clk_i, // Clock
input logic rst_ni, // Asynchronous reset active low
input logic test_en_i,
// flush
input logic flush_i,
// coming from scoreboard
input scoreboard_entry issue_instr_i,
input logic issue_instr_valid_i,
@ -40,6 +42,8 @@ module issue_read_operands (
// MULT
input logic mult_ready_i, // FU is ready
output logic mult_valid_o, // Output is valid
// Forward port
// commit port
input logic [4:0] waddr_a_i,
input logic [63:0] wdata_a_i,
@ -48,7 +52,7 @@ module issue_read_operands (
logic stall; // stall signal, we do not want to fetch any more entries
logic fu_busy; // functional unit is busy
scoreboard_entry sbe_n, sbe_q; // instruction register (ID <-> EX)
logic [63:0] operand_a_regfile, operand_b_regfile; // operands coming from regfile
logic [63:0] operand_a_regfile_n, operand_a_regfile_q, operand_b_regfile_n, operand_b_regfile_q; // operands coming from regfile
logic forward_rs1, forward_rs2;
// ---------------
@ -94,6 +98,7 @@ module issue_read_operands (
// ---------------
// Register stage
// ---------------
assign operator_o = sbe_q.op;
// check that all operands are available, otherwise stall
// forward corresponding register
always_comb begin : operands_available
@ -101,10 +106,12 @@ module issue_read_operands (
// operand forwarding signals
forward_rs1 = 1'b0;
forward_rs2 = 1'b0;
// address needs to be applied one cycle earlier
rs1_o = issue_instr_i.rs1;
rs2_o = issue_instr_i.rs2;
// 1. check if the source registers are clobberd
// 2. poll the scoreboard
if (rd_clobber_i[sbe_q.rs1] != NONE) begin
rs1_o = sbe_q.rs1;
// the operand is available, forward it
if (rs1_valid_i)
forward_rs1 = 1'b1;
@ -114,7 +121,6 @@ module issue_read_operands (
end
if (rd_clobber_i[sbe_q.rs2] != NONE) begin
rs2_o = sbe_q.rs2;
// the operand is available, forward it
if (rs2_valid_i)
forward_rs2 = 1'b1;
@ -127,8 +133,8 @@ module issue_read_operands (
// Forwarding/Output MUX
always_comb begin : forwarding
// default is regfile
operand_a_o = operand_a_regfile;
operand_b_o = operand_b_regfile;
operand_a_o = operand_a_regfile_q;
operand_b_o = operand_b_regfile_q;
// or should we forward
if (forward_rs1) begin
@ -139,7 +145,7 @@ module issue_read_operands (
operand_b_o = rs2_i;
end
// or is is an immediate (including PC)
// or is it an immediate (including PC)
if (sbe_q.use_imm) begin
operand_b_o = sbe_q.imm;
end
@ -168,31 +174,35 @@ module issue_read_operands (
end
regfile #(
.DATA_WIDTH ( 64 )
.DATA_WIDTH ( 64 )
)
regfile_i (
// Clock and Reset
.clk ( clk_i ),
.rst_n ( rst_ni ),
.test_en_i ( test_en_i ),
.clk ( clk_i ),
.rst_n ( rst_ni ),
.test_en_i ( test_en_i ),
.raddr_a_i ( issue_instr_i.rs1 ),
.rdata_a_o ( operand_a_regfile ),
.raddr_a_i ( issue_instr_i.rs1 ),
.rdata_a_o ( operand_a_regfile_n ),
.raddr_b_i ( issue_instr_i.rs2 ),
.rdata_b_o ( operand_b_regfile ),
.raddr_b_i ( issue_instr_i.rs2 ),
.rdata_b_o ( operand_b_regfile_n ),
.waddr_a_i ( waddr_a_i ),
.wdata_a_i ( wdata_a_i ),
.we_a_i ( we_a_i )
.waddr_a_i ( waddr_a_i ),
.wdata_a_i ( wdata_a_i ),
.we_a_i ( we_a_i )
);
// Registers
always_ff @(posedge clk_i or negedge rst_ni) begin
if(~rst_ni) begin
sbe_q <= '{default: 0};
sbe_q <= '{default: 0};
operand_a_regfile_q <= '{default: 0};
operand_b_regfile_q <= '{default: 0};
end else begin
sbe_q <= sbe_n;
sbe_q <= sbe_n;
operand_a_regfile_q <= operand_a_regfile_n;
operand_b_regfile_q <= operand_b_regfile_n;
end
end
endmodule

View file

@ -9,6 +9,7 @@ import ariane_pkg::*;
module scoreboard #(
parameter int NR_ENTRIES = 8,
parameter int NR_WB_PORTS = 4,
parameter type dtype = scoreboard_entry
)
(
@ -28,7 +29,7 @@ module scoreboard #(
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
// advertise instruction to commit stage, if commit_ack_i is asserted advance the commit pointer
output dtype commit_instr_o,
input logic commit_ack_i,
@ -43,9 +44,9 @@ module scoreboard #(
input logic issue_ack_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
input logic [NR_WB_PORTS-1:0][63:0] pc_i, // PC at which to write the result back
input logic [NR_WB_PORTS-1:0][63:0] wdata_i, // write data in
input logic [NR_WB_PORTS-1:0] wb_valid_i // data in is valid
);
localparam BITS_ENTRIES = $clog2(NR_ENTRIES);
@ -53,7 +54,11 @@ dtype [NR_ENTRIES-1:0] mem_q, mem_n;
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, top_pointer_qq; // points to the top of the scoreboard, an empty slot, top pointer two cycles ago
// rd clobber register
logic [31:0][$bits(fu_t)-1:0] rd_clobber_n, rd_clobber_q;
// register read stage
logic [63:0] rs1_n, rs1_q, rs2_n, rs2_q;
logic rs1_valid_n, rs1_valid_q, rs2_valid_n, rs2_valid_q;
logic pointer_overflow;
logic empty;
logic reset_condition;
@ -64,7 +69,11 @@ assign reset_condition = top_pointer_q == top_pointer_qq;
assign pointer_overflow = (top_pointer_q <= commit_pointer_q && ~reset_condition) ? 1'b1 : 1'b0;
assign full_o = (reset_condition) ? 1'b0 : (commit_pointer_q == top_pointer_q);
assign empty = (pointer_overflow) ? 1'b0 : (commit_pointer_q == top_pointer_q);
assign rd_clobber_o = rd_clobber_q;
assign rs1_o = rs1_q;
assign rs2_o = rs2_q;
assign rs1_valid_o = rs1_valid_q;
assign rs2_valid_o = rs2_valid_q;
// rd_clobber output: output currently clobbered destination registers
// but only between commit and issue pointer
// normal case: overflow case:
@ -76,32 +85,31 @@ assign empty = (pointer_overflow) ? 1'b0 : (commit_pointer_q == top_p
// |_________________________|<- top pointer |_________________________|
//
always_comb begin : clobber_output
rd_clobber_o = '{default: 0};
rd_clobber_n = rd_clobber_q;
// 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_q[i].rd] = mem_q[i].fu;
rd_clobber_n[mem_q[i].rd] = mem_q[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_q[i].rd] = mem_q[i].fu;
rd_clobber_n[mem_q[i].rd] = mem_q[i].fu;
end
end
// the zero register is always free
rd_clobber_o[0] = NONE;
// // the zero register is always free
rd_clobber_n[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;
rs1_n = rs1_q;
rs2_n = rs2_q;
rs1_valid_n = rs1_valid_q;
rs2_valid_n = rs2_valid_q;
if (commit_pointer_q < issue_pointer_q) begin
for (int unsigned i = 0; i < NR_ENTRIES; i++) begin
@ -109,12 +117,12 @@ always_comb begin : read_operands
// 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_q[i[BITS_ENTRIES-1:0]].rd == rs1_i) begin
rs1_o = mem_q[i].result;
rs1_valid_o = mem_q[i].valid;
rs1_n = mem_q[i].result;
rs1_valid_n = mem_q[i].valid;
// do the same for rs2
end else if (mem_q[i].rd == rs2_i) begin
rs2_o = mem_q[i].result;
rs2_valid_o = mem_q[i].valid;
rs2_n = mem_q[i].result;
rs2_valid_n = mem_q[i].valid;
end
end
end
@ -123,25 +131,25 @@ always_comb begin : read_operands
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_q[i[BITS_ENTRIES-1:0]].rd == rs1_i) begin
rs1_o = mem_q[i].result;
rs1_valid_o = mem_q[i].valid;
rs1_n = mem_q[i].result;
rs1_valid_n = mem_q[i].valid;
// do the same for rs2
end else if (mem_q[i[BITS_ENTRIES-1:0]].rd == rs2_i) begin
rs2_o = mem_q[i].result;
rs2_valid_o = mem_q[i].valid;
rs2_n = mem_q[i].result;
rs2_valid_n = mem_q[i].valid;
end
end
end
end
// make sure we didn't read the zero register
if (rs1_i == '0)
rs1_valid_o = 1'b0;
rs1_valid_n = 1'b0;
if (rs2_i == '0)
rs2_valid_o = 1'b0;
rs2_valid_n = 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
always_comb begin : push_instruction_and_wb
// default assignment
top_pointer_n = top_pointer_q;
mem_n = mem_q;
@ -154,11 +162,13 @@ always_latch begin : push_instruction_and_wb
// 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_q[i].pc == pc_i) begin
mem_n[i].valid = 1'b1;
mem_n[i].result = wdata_i;
for (int j = 0; j < NR_WB_PORTS; j++) begin
if (wb_valid_i[j]) begin
for (int unsigned i = 0; i < NR_ENTRIES; i++) begin
if (mem_q[i].pc == pc_i[j]) begin
mem_n[i].valid = 1'b1;
mem_n[i].result = wdata_i[j];
end
end
end
end
@ -220,18 +230,33 @@ end
// sequential process
always_ff @(posedge clk_i or negedge rst_ni) begin : sequential
if(~rst_ni) begin
rd_clobber_q <= '{default: 0};
rs1_q <= 64'b0;
rs2_q <= 64'b0;
rs1_valid_q <= 1'b0;
rs2_valid_q <= 1'b0;
issue_pointer_q <= '{default: 0};
commit_pointer_q <= '{default: 0};
top_pointer_q <= '{default: 0};
top_pointer_qq <= '{default: 0};
mem_q <= '{default: 0};
end else if (flush_i) begin // reset pointers on flush
rd_clobber_q <= '{default: 0};
rs1_q <= 64'b0;
rs2_q <= 64'b0;
rs1_valid_q <= 1'b0;
rs2_valid_q <= 1'b0;
issue_pointer_q <= '{default: 0};
commit_pointer_q <= '{default: 0};
top_pointer_q <= '{default: 0};
top_pointer_qq <= '{default: 0};
mem_q <= '{default: 0};
end else begin
rs1_q <= rs1_n;
rs2_q <= rs2_n;
rs1_valid_q <= rs1_valid_n;
rs2_valid_q <= rs2_valid_n;
rd_clobber_q <= rd_clobber_n;
issue_pointer_q <= issue_pointer_n;
commit_pointer_q <= commit_pointer_n;
top_pointer_q <= top_pointer_n;