@@ -82,6 +82,8 @@ module issue_read_operands
8282 output logic [CVA6Cfg.NrIssuePorts- 1 : 0 ] mult_valid_o,
8383 // FPU FU is ready - EX_STAGE
8484 input logic fpu_ready_i,
85+ // FPU FU will perform a writeback in the next cycle - EX_STAGE
86+ input logic fpu_early_valid_i,
8587 // FPU FU is valid - EX_STAGE
8688 output logic [CVA6Cfg.NrIssuePorts- 1 : 0 ] fpu_valid_o,
8789 // FPU fmt field - EX_STAGE
@@ -152,10 +154,10 @@ module issue_read_operands
152154 rs3_len_t operand_c_fpr;
153155 // output flipflop (ID <-> EX)
154156 fu_data_t [CVA6Cfg.NrIssuePorts- 1 : 0 ] fu_data_n, fu_data_q;
155- logic [CVA6Cfg.VLEN - 1 : 0 ] pc_n;
156- logic is_compressed_instr_n;
157- branchpredict_sbe_t branch_predict_n;
158- logic [CVA6Cfg.XLEN - 1 : 0 ] imm_forward_rs3;
157+ logic [ CVA6Cfg.VLEN - 1 : 0 ] pc_n;
158+ logic is_compressed_instr_n;
159+ branchpredict_sbe_t branch_predict_n;
160+ logic [CVA6Cfg.NrIssuePorts - 1 : 0 ][CVA6Cfg. XLEN - 1 : 0 ] imm_forward_rs3;
159161
160162 logic [CVA6Cfg.NrIssuePorts- 1 : 0 ] alu_valid_n, alu_valid_q;
161163 logic [CVA6Cfg.NrIssuePorts- 1 : 0 ] aes_valid_n, aes_valid_q;
@@ -215,8 +217,10 @@ module issue_read_operands
215217 logic [CVA6Cfg.NrIssuePorts- 1 : 0 ] forward_rs1, forward_rs2, forward_rs3;
216218
217219 // original instruction
218- riscv :: instruction_t orig_instr;
219- assign orig_instr = riscv :: instruction_t ' (orig_instr_i[0 ]);
220+ riscv :: instruction_t [CVA6Cfg.NrIssuePorts- 1 : 0 ] orig_instr;
221+ for (genvar i = 0 ; i < CVA6Cfg.NrIssuePorts; i++ ) begin
222+ assign orig_instr[i] = riscv :: instruction_t ' (orig_instr_i[i]);
223+ end
220224
221225 // ALU-ALU bypass signals
222226 alu_bypass_t alu_bypass, alu_bypass_n, alu_bypass_q;
@@ -309,10 +313,11 @@ module issue_read_operands
309313 end
310314
311315 if (CVA6Cfg.SuperscalarEn) begin
312- // When a bypass is possible, an instruction uses `alu2` only when `alu` is already busy,
313- // in all other scenarios `alu2` is preferred over `alu`, unless it is busy
316+ // When a bypass is possible or an FPU instruction is present on the second issue port,
317+ // an instruction uses `alu2` only when `alu` is already busy
318+ // In all other scenarios `alu2` is preferred over `alu`, unless it is busy
314319 for (genvar i = 0 ; i < 2 ; i++ ) begin
315- assign use_alu2[i] = is_alu_bypass ? fus_busy[i].alu : ! fus_busy[i].alu2;
320+ assign use_alu2[i] = is_alu_bypass || (issue_instr_i[ 1 ].fu inside { FPU , FPU_VEC } ) ? fus_busy[i].alu : ! fus_busy[i].alu2;
316321 end
317322 end else begin
318323 assign use_alu2 = '0 ;
@@ -347,7 +352,6 @@ module issue_read_operands
347352 if (CVA6Cfg.FpPresent && ! fpu_ready_i) begin
348353 fus_busy[0 ].fpu = 1'b1 ;
349354 fus_busy[0 ].fpu_vec = 1'b1 ;
350- if (CVA6Cfg.SuperscalarEn) fus_busy[0 ].alu2 = 1'b1 ;
351355 end
352356
353357 if (! lsu_ready_i) begin
@@ -356,6 +360,11 @@ module issue_read_operands
356360 end
357361
358362 if (CVA6Cfg.SuperscalarEn) begin
363+
364+ if (fpu_early_valid_i) begin
365+ fus_busy[0 ].alu2 = 1'b1 ;
366+ end
367+
359368 fus_busy[1 ] = fus_busy[0 ];
360369
361370 // Never issue CSR instruction on second issue port.
@@ -390,10 +399,6 @@ module issue_read_operands
390399 ALU : begin
391400 if (use_alu2[0 ]) begin
392401 fus_busy[1 ].alu2 = 1'b1 ;
393- // TODO is there a minimum float execution time?
394- // If so we could issue FPU & ALU2 the same cycle
395- fus_busy[1 ].fpu = 1'b1 ;
396- fus_busy[1 ].fpu_vec = 1'b1 ;
397402 end else begin
398403 fus_busy[1 ].alu = 1'b1 ;
399404 fus_busy[1 ].ctrl_flow = 1'b1 ;
@@ -408,10 +413,18 @@ module issue_read_operands
408413 FPU , FPU_VEC : begin
409414 fus_busy[1 ].fpu = 1'b1 ;
410415 fus_busy[1 ].fpu_vec = 1'b1 ;
416+ if (issue_instr_i[1 ].op inside { [FLD : FSB ]} ) begin
417+ fus_busy[1 ].load = 1'b1 ;
418+ fus_busy[1 ].store = 1'b1 ;
419+ end
411420 end
412421 LOAD , STORE : begin
413422 fus_busy[1 ].load = 1'b1 ;
414423 fus_busy[1 ].store = 1'b1 ;
424+ if (issue_instr_i[0 ].op inside { [FLD : FSB ]} ) begin
425+ fus_busy[1 ].fpu = 1'b1 ;
426+ fus_busy[1 ].fpu_vec = 1'b1 ;
427+ end
415428 end
416429 CVXIF : ;
417430 default : ;
@@ -658,10 +671,12 @@ module issue_read_operands
658671 end
659672
660673 // third operand from fp regfile or gp regfile if NR_RGPR_PORTS == 3
661- if (OPERANDS_PER_INSTR == 3 ) begin : gen_gp_rs3
662- assign imm_forward_rs3 = rs3_res[0 ];
663- end else begin : gen_fp_rs3
664- assign imm_forward_rs3 = {{ CVA6Cfg.XLEN - CVA6Cfg.FLen{ 1'b0 }} , rs3_res[0 ]} ;
674+ for (genvar i = 0 ; i < CVA6Cfg.NrIssuePorts; i++ ) begin
675+ if (OPERANDS_PER_INSTR == 3 ) begin : gen_gp_rs3
676+ assign imm_forward_rs3[i] = rs3_res[i];
677+ end else begin : gen_fp_rs3
678+ assign imm_forward_rs3[i] = {{ CVA6Cfg.XLEN - CVA6Cfg.FLen{ 1'b0 }} , rs3_res[i]} ;
679+ end
665680 end
666681
667682 // Forwarding/Output MUX
@@ -696,7 +711,7 @@ module issue_read_operands
696711 fu_data_n[i].operand_b = rs2_res[i];
697712 end
698713 if ((CVA6Cfg.FpPresent || (CVA6Cfg.CvxifEn && OPERANDS_PER_INSTR == 3 )) && forward_rs3[i]) begin
699- fu_data_n[i].imm = imm_forward_rs3;
714+ fu_data_n[i].imm = imm_forward_rs3[i] ;
700715 end
701716
702717 // use the PC as operand a
@@ -760,12 +775,12 @@ module issue_read_operands
760775 default : begin
761776 if (issue_instr_i[i].fu == FPU && CVA6Cfg.FpPresent) begin
762777 fpu_valid_n[i] = 1'b1 ;
763- fpu_fmt_n = orig_instr.rftype.fmt; // fmt bits from instruction
764- fpu_rm_n = orig_instr.rftype.rm; // rm bits from instruction
778+ fpu_fmt_n = orig_instr[i] .rftype.fmt; // fmt bits from instruction
779+ fpu_rm_n = orig_instr[i] .rftype.rm; // rm bits from instruction
765780 end else if (issue_instr_i[i].fu == FPU_VEC && CVA6Cfg.FpPresent) begin
766781 fpu_valid_n[i] = 1'b1 ;
767- fpu_fmt_n = orig_instr.rvftype.vfmt; // vfmt bits from instruction
768- fpu_rm_n = { 2'b0 , orig_instr.rvftype.repl} ; // repl bit from instruction
782+ fpu_fmt_n = orig_instr[i] .rvftype.vfmt; // vfmt bits from instruction
783+ fpu_rm_n = { 2'b0 , orig_instr[i] .rvftype.repl} ; // repl bit from instruction
769784 end
770785 end
771786 endcase
@@ -948,7 +963,7 @@ module issue_read_operands
948963 } ;
949964
950965 if (CVA6Cfg.SuperscalarEn) begin
951- if (! (issue_instr_i[0 ].fu inside { FPU , FPU_VEC } )) begin
966+ if (! (issue_instr_i[0 ].fu inside { FPU , FPU_VEC } || issue_instr_i[ 0 ].op inside { [ FLD : FSB ] } )) begin
952967 fp_raddr_pack = {
953968 issue_instr_i[1 ].result[4 : 0 ], issue_instr_i[1 ].rs2[4 : 0 ], issue_instr_i[1 ].rs1[4 : 0 ]
954969 } ;
@@ -1101,20 +1116,6 @@ module issue_read_operands
11011116 );
11021117 end
11031118
1104- // FPU does not declare that it will return a result the subsequent cycle so
1105- // it is not possible for issue stage to know when ALU2 can be used if there
1106- // is an FPU. As there are discussions to change the FPU, I did not explore
1107- // its architecture to create this "FPU returns next cycle" signal. Also, a
1108- // "lookahead" optimization should be added to be performant with FPU: when
1109- // issue port 2 is issuing to FPU, issue port 1 should issue to ALU1 instead
1110- // of ALU2 so that FPU is not busy. However, if FPU has a minimum execution
1111- // time of 2 cycles, it is possible to simply not raise fus_busy[1].alu2.
1112- initial begin
1113- assert (! (CVA6Cfg.SuperscalarEn && CVA6Cfg.FpPresent))
1114- else
1115- $fatal (1 , " FPU is not yet supported in superscalar CVA6, see comments above this assertion." );
1116- end
1117-
11181119 for (genvar i = 0 ; i < CVA6Cfg.NrIssuePorts; i++ ) begin
11191120 assert property (@ (posedge clk_i) (branch_valid_q) | - > (! $isunknown (
11201121 fu_data_q[i].operand_a
0 commit comments