diff --git a/id_stage.sv b/id_stage.sv index 3429504b3..e72cad1b7 100644 --- a/id_stage.sv +++ b/id_stage.sv @@ -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), diff --git a/issue_read_operands.sv b/issue_read_operands.sv index 51046bb53..87655b9d7 100755 --- a/issue_read_operands.sv +++ b/issue_read_operands.sv @@ -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 diff --git a/scoreboard.sv b/scoreboard.sv index 264bd1f5d..17c60934d 100644 --- a/scoreboard.sv +++ b/scoreboard.sv @@ -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;