-
Notifications
You must be signed in to change notification settings - Fork 725
/
Copy pathscoreboard.sv
356 lines (320 loc) · 15 KB
/
scoreboard.sv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//
// Author: Florian Zaruba, ETH Zurich
// Date: 08.04.2017
// Description: Scoreboard - keeps track of all decoded, issued and committed instructions
module scoreboard #(
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
parameter type bp_resolve_t = logic,
parameter type exception_t = logic,
parameter type scoreboard_entry_t = logic,
parameter type forwarding_t = logic,
parameter type writeback_t = logic,
parameter type rs3_len_t = logic
) (
// Subsystem Clock - SUBSYSTEM
input logic clk_i,
// Asynchronous reset active low - SUBSYSTEM
input logic rst_ni,
// Is scoreboard full - PERF_COUNTERS
output logic sb_full_o,
// Prevent from issuing - CONTROLLER
input logic flush_unissued_instr_i,
// Flush whole scoreboard - CONTROLLER
input logic flush_i,
// Writeback Handling of CVXIF
// TO_BE_COMPLETED - ISSUE_READ_OPERANDS
input logic x_transaction_accepted_i,
// TO_BE_COMPLETED - ISSUE_READ_OPERANDS
input logic x_issue_writeback_i,
// TO_BE_COMPLETED - ISSUE_READ_OPERANDS
input logic [CVA6Cfg.TRANS_ID_BITS-1:0] x_id_i,
// advertise instruction to commit stage, if commit_ack_i is asserted advance the commit pointer
// Instructions to commit - COMMIT_STAGE
output scoreboard_entry_t [CVA6Cfg.NrCommitPorts-1:0] commit_instr_o,
// Instruction is cancelled - COMMIT_STAGE
output logic [CVA6Cfg.NrCommitPorts-1:0] commit_drop_o,
// Commit acknowledge - COMMIT_STAGE
input logic [CVA6Cfg.NrCommitPorts-1:0] commit_ack_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
// Handshake's data with decode stage - ID_STAGE
input scoreboard_entry_t [CVA6Cfg.NrIssuePorts-1:0] decoded_instr_i,
// instruction value - ID_STAGE
input logic [CVA6Cfg.NrIssuePorts-1:0][31:0] orig_instr_i,
// Handshake's valid with decode stage - ID_STAGE
input logic [CVA6Cfg.NrIssuePorts-1:0] decoded_instr_valid_i,
// Handshake's acknowlege with decode stage - ID_STAGE
output logic [CVA6Cfg.NrIssuePorts-1:0] decoded_instr_ack_o,
// instruction to issue logic, if issue_instr_valid and issue_ready is asserted, advance the issue pointer
// Entry about the instruction to issue - ISSUE_READ_OPERANDS
output scoreboard_entry_t [CVA6Cfg.NrIssuePorts-1:0] issue_instr_o,
// Instruction to issue - ISSUE_READ_OPERANDS
output logic [CVA6Cfg.NrIssuePorts-1:0][31:0] orig_instr_o,
// Is there an instruction to issue - ISSUE_READ_OPERANDS
output logic [CVA6Cfg.NrIssuePorts-1:0] issue_instr_valid_o,
// Issue stage acknowledge - ISSUE_READ_OPERANDS
input logic [CVA6Cfg.NrIssuePorts-1:0] issue_ack_i,
// Forwarding - ISSUE_READ_OPERANDS
output forwarding_t fwd_o,
// Result from branch unit - EX_STAGE
input bp_resolve_t resolved_branch_i,
// Transaction ID at which to write the result back - EX_STAGE
input logic [CVA6Cfg.NrWbPorts-1:0][CVA6Cfg.TRANS_ID_BITS-1:0] trans_id_i,
// Results to write back - EX_STAGE
input logic [CVA6Cfg.NrWbPorts-1:0][CVA6Cfg.XLEN-1:0] wbdata_i,
// Exception from a functional unit (e.g.: ld/st exception) - EX_STAGE
input exception_t [CVA6Cfg.NrWbPorts-1:0] ex_i,
// Indicates valid results - EX_STAGE
input logic [CVA6Cfg.NrWbPorts-1:0] wt_valid_i,
// Cvxif we for writeback - EX_STAGE
input logic x_we_i,
// CVXIF destination register - ISSUE_STAGE
input logic [4:0] x_rd_i,
// Issue pointer - RVFI
output logic [ CVA6Cfg.NrIssuePorts-1:0][CVA6Cfg.TRANS_ID_BITS-1:0] rvfi_issue_pointer_o,
// Commit pointer - RVFI
output logic [CVA6Cfg.NrCommitPorts-1:0][CVA6Cfg.TRANS_ID_BITS-1:0] rvfi_commit_pointer_o
);
// this is the FIFO struct of the issue queue
typedef struct packed {
logic issued; // this bit indicates whether we issued this instruction e.g.: if it is valid
logic cancelled; // this instruction was cancelled (speculative scoreboard)
logic is_rd_fpr_flag; // redundant meta info, added for speed
scoreboard_entry_t sbe; // this is the score board entry we will send to ex
} sb_mem_t;
sb_mem_t [CVA6Cfg.NR_SB_ENTRIES-1:0] mem_q, mem_n;
logic [CVA6Cfg.NR_SB_ENTRIES-1:0] still_issued;
logic [CVA6Cfg.NrIssuePorts-1:0] issue_full;
logic [1:0][CVA6Cfg.NR_SB_ENTRIES/2-1:0] issued_instrs_even_odd;
logic bmiss;
logic [CVA6Cfg.TRANS_ID_BITS-1:0] after_flu_wb;
logic [CVA6Cfg.NR_SB_ENTRIES-1:0] speculative_instrs;
logic [CVA6Cfg.NrIssuePorts-1:0] num_issue;
logic [CVA6Cfg.TRANS_ID_BITS-1:0] issue_pointer_n, issue_pointer_q;
logic [CVA6Cfg.NrIssuePorts:0][CVA6Cfg.TRANS_ID_BITS-1:0] issue_pointer;
logic [CVA6Cfg.NrCommitPorts-1:0][CVA6Cfg.TRANS_ID_BITS-1:0] commit_pointer_n, commit_pointer_q;
logic [$clog2(CVA6Cfg.NrCommitPorts):0] num_commit;
for (genvar i = 0; i < CVA6Cfg.NR_SB_ENTRIES; i++) begin
assign still_issued[i] = mem_q[i].issued & ~mem_q[i].cancelled;
end
for (genvar i = 0; i < CVA6Cfg.NR_SB_ENTRIES; i++) begin
assign issued_instrs_even_odd[i%2][i/2] = mem_q[i].issued;
end
// the issue queue is full don't issue any new instructions
assign issue_full[0] = &issued_instrs_even_odd[0] && &issued_instrs_even_odd[1];
if (CVA6Cfg.SuperscalarEn) begin : assign_issue_full
// Need two slots available to issue two instructions.
// They are next to each other so one must be even and one odd
assign issue_full[1] = &issued_instrs_even_odd[0] || &issued_instrs_even_odd[1];
end
assign sb_full_o = issue_full[0];
// output commit instruction directly
always_comb begin : commit_ports
for (int unsigned i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin
commit_instr_o[i] = mem_q[commit_pointer_q[i]].sbe;
commit_instr_o[i].trans_id = commit_pointer_q[i];
commit_drop_o[i] = mem_q[commit_pointer_q[i]].cancelled;
end
end
assign issue_pointer[0] = issue_pointer_q;
for (genvar i = 0; i < CVA6Cfg.NrIssuePorts; i++) begin
assign issue_pointer[i+1] = issue_pointer[i] + 'd1;
end
// an instruction is ready for issue if we have place in the issue FIFO and it the decoder says it is valid
always_comb begin
issue_instr_o = decoded_instr_i;
orig_instr_o = orig_instr_i;
for (int unsigned i = 0; i < CVA6Cfg.NrIssuePorts; i++) begin
// make sure we assign the correct trans ID
issue_instr_o[i].trans_id = issue_pointer[i];
issue_instr_valid_o[i] = decoded_instr_valid_i[i] & ~issue_full[i];
decoded_instr_ack_o[i] = issue_ack_i[i] & ~issue_full[i];
end
end
// maintain a FIFO with issued instructions
// keep track of all issued instructions
always_comb begin : issue_fifo
// default assignment
mem_n = mem_q;
num_issue = '0;
// if we got a acknowledge from the issue stage, put this scoreboard entry in the queue
for (int unsigned i = 0; i < CVA6Cfg.NrIssuePorts; i++) begin
if (decoded_instr_valid_i[i] && decoded_instr_ack_o[i] && !flush_unissued_instr_i) begin
// the decoded instruction we put in there is valid (1st bit)
// increase the issue counter and advance issue pointer
num_issue += 'd1;
mem_n[issue_pointer[i]] = '{
issued: 1'b1,
cancelled: 1'b0,
is_rd_fpr_flag: CVA6Cfg.FpPresent && ariane_pkg::is_rd_fpr(decoded_instr_i[i].op),
sbe: decoded_instr_i[i]
};
end
end
// ------------
// FU NONE
// ------------
for (int unsigned i = 0; i < CVA6Cfg.NR_SB_ENTRIES; i++) begin
// The FU is NONE -> this instruction is valid immediately
if (mem_q[i].sbe.fu == ariane_pkg::NONE && mem_q[i].issued) mem_n[i].sbe.valid = 1'b1;
end
// ------------
// Write Back
// ------------
for (int unsigned i = 0; i < CVA6Cfg.NrWbPorts; i++) begin
// check if this instruction was issued (e.g.: it could happen after a flush that there is still
// something in the pipeline e.g. an incomplete memory operation)
if (wt_valid_i[i] && mem_q[trans_id_i[i]].issued) begin
if (mem_q[trans_id_i[i]].sbe.is_double_rd_macro_instr && mem_q[trans_id_i[i]].sbe.is_macro_instr) begin
if (mem_q[trans_id_i[i]].sbe.is_last_macro_instr) begin
mem_n[trans_id_i[i]].sbe.valid = 1'b1;
mem_n[8'(trans_id_i[i])-1].sbe.valid = 1'b1;
end else begin
mem_n[trans_id_i[i]].sbe.valid = 1'b0;
end
end else begin
mem_n[trans_id_i[i]].sbe.valid = 1'b1;
end
mem_n[trans_id_i[i]].sbe.result = wbdata_i[i];
// save the target address of a branch (needed for debug in commit stage)
if (CVA6Cfg.DebugEn) begin
mem_n[trans_id_i[i]].sbe.bp.predict_address = resolved_branch_i.target_address;
end
if (mem_n[trans_id_i[i]].sbe.fu == ariane_pkg::CVXIF) begin
if (x_we_i) mem_n[trans_id_i[i]].sbe.rd = x_rd_i;
else mem_n[trans_id_i[i]].sbe.rd = 5'b0;
end
// write the exception back if it is valid
if (ex_i[i].valid) mem_n[trans_id_i[i]].sbe.ex = ex_i[i];
// write the fflags back from the FPU (exception valid is never set), leave tval intact
else if(CVA6Cfg.FpPresent && (mem_q[trans_id_i[i]].sbe.fu == ariane_pkg::FPU || mem_q[trans_id_i[i]].sbe.fu == ariane_pkg::FPU_VEC)) begin
mem_n[trans_id_i[i]].sbe.ex.cause = ex_i[i].cause;
end
end
end
// ------------
// Cancel
// ------------
if (CVA6Cfg.SpeculativeSb) begin
if (bmiss) begin
if (after_flu_wb != issue_pointer[0]) begin
mem_n[after_flu_wb].cancelled = 1'b1;
end
end
end
// ------------
// Commit Port
// ------------
// we've got an acknowledge from commit
for (int i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin
if (commit_ack_i[i]) begin
// this instruction is no longer in issue e.g.: it is considered finished
mem_n[commit_pointer_q[i]].issued = 1'b0;
mem_n[commit_pointer_q[i]].cancelled = 1'b0;
mem_n[commit_pointer_q[i]].sbe.valid = 1'b0;
end
end
// ------
// Flush
// ------
if (flush_i) begin
for (int unsigned i = 0; i < CVA6Cfg.NR_SB_ENTRIES; i++) begin
// set all valid flags for all entries to zero
mem_n[i].issued = 1'b0;
mem_n[i].cancelled = 1'b0;
mem_n[i].sbe.valid = 1'b0;
mem_n[i].sbe.ex.valid = 1'b0;
end
end
end
assign bmiss = resolved_branch_i.valid && resolved_branch_i.is_mispredict;
assign after_flu_wb = trans_id_i[ariane_pkg::FLU_WB] + 'd1;
// FIFO counter updates
if (CVA6Cfg.NrCommitPorts == 2) begin : gen_commit_ports
assign num_commit = commit_ack_i[1] + commit_ack_i[0];
end else begin : gen_one_commit_port
assign num_commit = commit_ack_i[0];
end
assign commit_pointer_n[0] = (flush_i) ? '0 : commit_pointer_q[0] + num_commit;
always_comb begin : assign_issue_pointer_n
issue_pointer_n = issue_pointer[num_issue];
if (flush_i) issue_pointer_n = '0;
end
// precompute offsets for commit slots
for (genvar k = 1; k < CVA6Cfg.NrCommitPorts; k++) begin : gen_cnt_incr
assign commit_pointer_n[k] = (flush_i) ? '0 : commit_pointer_n[0] + unsigned'(k);
end
// Forwarding logic
writeback_t [CVA6Cfg.NrWbPorts-1:0] wb;
for (genvar i = 0; i < CVA6Cfg.NrWbPorts; i++) begin
assign wb[i].valid = wt_valid_i[i];
assign wb[i].data = wbdata_i[i];
assign wb[i].ex_valid = ex_i[i].valid;
assign wb[i].trans_id = trans_id_i[i];
end
assign fwd_o.still_issued = still_issued;
assign fwd_o.issue_pointer = issue_pointer;
assign fwd_o.wb = wb;
for (genvar i = 0; i < CVA6Cfg.NR_SB_ENTRIES; i++) begin
assign fwd_o.sbe[i] = mem_q[i].sbe;
end
// sequential process
always_ff @(posedge clk_i or negedge rst_ni) begin : regs
if (!rst_ni) begin
mem_q <= '{default: sb_mem_t'(0)};
commit_pointer_q <= '0;
issue_pointer_q <= '0;
end else begin
issue_pointer_q <= issue_pointer_n;
mem_q <= mem_n;
mem_q[x_id_i].sbe.rd <= (x_transaction_accepted_i && ~x_issue_writeback_i) ? 5'b0 : mem_n[x_id_i].sbe.rd;
commit_pointer_q <= commit_pointer_n;
end
end
//RVFI
assign rvfi_issue_pointer_o = issue_pointer[CVA6Cfg.NrIssuePorts-1:0];
assign rvfi_commit_pointer_o = commit_pointer_q;
//pragma translate_off
initial begin
assert (CVA6Cfg.NR_SB_ENTRIES == 2 ** CVA6Cfg.TRANS_ID_BITS)
else $fatal(1, "Scoreboard size needs to be a power of two.");
end
// assert that we never acknowledge a commit if the instruction is not valid
assert property (
@(posedge clk_i) disable iff (!rst_ni) commit_ack_i[0] |-> commit_instr_o[0].valid)
else $fatal(1, "Commit acknowledged but instruction is not valid");
if (CVA6Cfg.NrCommitPorts == 2) begin : gen_two_commit_ports
assert property (
@(posedge clk_i) disable iff (!rst_ni) commit_ack_i[1] |-> commit_instr_o[1].valid)
else $fatal(1, "Commit acknowledged but instruction is not valid");
end
// assert that we never give an issue ack signal if the instruction is not valid
for (genvar i = 0; i < CVA6Cfg.NrIssuePorts; i++) begin
assert property (
@(posedge clk_i) disable iff (!rst_ni) issue_ack_i[i] |-> issue_instr_valid_o[i])
else $fatal(1, "Issue acknowledged but instruction is not valid");
end
// there should never be more than one instruction writing the same destination register (except x0)
// check that no functional unit is retiring with the same transaction id
for (genvar i = 0; i < CVA6Cfg.NrWbPorts; i++) begin
for (genvar j = 0; j < CVA6Cfg.NrWbPorts; j++) begin
assert property (
@(posedge clk_i) disable iff (!rst_ni) wt_valid_i[i] && wt_valid_i[j] && (i != j) |-> (trans_id_i[i] != trans_id_i[j]))
else
$fatal(
1,
"Two or more functional units are retiring instructions with the same transaction id!"
);
end
end
//pragma translate_on
endmodule