-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathif_stage.v
355 lines (310 loc) · 12.6 KB
/
if_stage.v
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
`include "mycpu.h"
`include "csr.h"
module if_stage(
input clk ,
input reset ,
//allwoin
input ds_allowin ,
//brbus
input [`BR_BUS_WD -1:0] br_bus ,
//to ds
output fs_to_ds_valid ,
output [`FS_TO_DS_BUS_WD -1:0] fs_to_ds_bus ,
//exception
input excp_flush ,
input ertn_flush ,
input refetch_flush ,
input icacop_flush ,
input [31:0] ws_pc ,
input [31:0] csr_eentry ,
input [31:0] csr_era ,
input excp_tlbrefill ,
input [31:0] csr_tlbrentry ,
input has_int ,
//idle
input idle_flush ,
// inst cache interface
output inst_valid ,
output inst_op ,
output [ 3:0] inst_wstrb ,
output [31:0] inst_wdata ,
input inst_addr_ok ,
input inst_data_ok ,
input icache_miss ,
input [31:0] inst_rdata ,
output inst_uncache_en ,
output tlb_excp_cancel_req,
//from csr
input csr_pg ,
input csr_da ,
input [31:0] csr_dmw0 ,
input [31:0] csr_dmw1 ,
input [ 1:0] csr_plv ,
input [ 1:0] csr_datf ,
input disable_cache ,
//to btb
output [31:0] fetch_pc ,
output fetch_en ,
input [31:0] btb_ret_pc ,
input btb_taken ,
input btb_en ,
input [ 4:0] btb_index ,
//to addr trans
output [31:0] inst_addr ,
output inst_addr_trans_en,
output dmw0_en ,
output dmw1_en ,
//tlb
input inst_tlb_found ,
input inst_tlb_v ,
input inst_tlb_d ,
input [ 1:0] inst_tlb_mat ,
input [ 1:0] inst_tlb_plv
);
reg fs_valid;
wire fs_ready_go;
wire fs_allowin;
wire to_fs_valid;
wire pfs_ready_go;
wire [31:0] seq_pc;
wire [31:0] nextpc;
wire pfs_excp_adef;
wire fs_excp_tlbr;
wire fs_excp_pif;
wire fs_excp_ppi;
reg fs_excp;
reg fs_excp_num;
wire excp;
wire [3:0] excp_num;
wire pfs_excp;
wire pfs_excp_num;
wire flush_sign;
reg [31:0] inst_rd_buff;
reg inst_buff_enable;
wire da_mode;
wire pg_mode;
wire btb_pre_error_flush;
wire [31:0] btb_pre_error_flush_target;
wire flush_inst_delay;
wire flush_inst_go_dirt;
wire fetch_btb_target;
reg idle_lock;
wire tlb_excp_lock_pc;
wire [31:0] btb_ret_pc_t;
wire [ 4:0] btb_index_t;
wire btb_taken_t;
wire btb_en_t;
wire [31:0] excp_entry;
wire [31:0] inst_flush_pc;
assign {btb_pre_error_flush,
btb_pre_error_flush_target } = br_bus;
wire [31:0] fs_inst;
reg [31:0] fs_pc;
assign fs_to_ds_bus = {btb_ret_pc_t, //108:77
btb_index_t, //76:72
btb_taken_t, //71:71
btb_en_t, //70:70
icache_miss, //69:69
excp, //68:68
excp_num, //67:64
fs_inst, //63:32
fs_pc //31:0
};
assign flush_sign = ertn_flush || excp_flush || refetch_flush || icacop_flush || idle_flush;
assign flush_inst_delay = flush_sign && !inst_addr_ok || idle_flush;
assign flush_inst_go_dirt = flush_sign && inst_addr_ok && !idle_flush;
//flush state machine
reg [31:0] flush_inst_req_buffer;
reg flush_inst_req_state;
localparam flush_inst_req_empty = 1'b0;
localparam flush_inst_req_full = 1'b1;
always @(posedge clk) begin
if (reset) begin
flush_inst_req_state <= flush_inst_req_empty;
end
else case (flush_inst_req_state)
flush_inst_req_empty: begin
if(flush_inst_delay) begin
flush_inst_req_buffer <= nextpc;
flush_inst_req_state <= flush_inst_req_full;
end
end
flush_inst_req_full: begin
if(pfs_ready_go) begin
flush_inst_req_state <= flush_inst_req_empty;
end
else if (flush_sign) begin
flush_inst_req_buffer <= nextpc;
end
end
endcase
end
assign fetch_btb_target = (btb_taken && btb_en) || (btb_lock_en && btb_lock_buffer[37]);
/*
* idle lock
* when idle inst commit, stop inst fetch until interrupted
*/
always @(posedge clk) begin
if (reset) begin
idle_lock <= 1'b0;
end
else if (idle_flush && !has_int) begin
idle_lock <= 1'b1;
end
else if (has_int) begin
idle_lock <= 1'b0;
end
end
/*
* br state machine
* when btb pre error, id stage will cancel one inst. so need confirm useless
* inst (will be canceled) is generated.
*/
reg [31:0] br_target_inst_req_buffer;
reg [ 2:0] br_target_inst_req_state;
localparam br_target_inst_req_empty = 3'b001;
localparam br_target_inst_req_wait_slot = 3'b010;
localparam br_target_inst_req_wait_br_target = 3'b100;
always @(posedge clk) begin
if (reset) begin
br_target_inst_req_state <= br_target_inst_req_empty;
end
else case (br_target_inst_req_state)
br_target_inst_req_empty: begin
if (flush_sign) begin
br_target_inst_req_state <= br_target_inst_req_empty;
end
else if(btb_pre_error_flush && !fs_valid && !inst_addr_ok) begin
br_target_inst_req_state <= br_target_inst_req_wait_slot;
br_target_inst_req_buffer <= btb_pre_error_flush_target;
end
else if(btb_pre_error_flush && !inst_addr_ok && fs_valid || btb_pre_error_flush && inst_addr_ok && !fs_valid) begin
br_target_inst_req_state <= br_target_inst_req_wait_br_target;
br_target_inst_req_buffer <= btb_pre_error_flush_target;
end
end
br_target_inst_req_wait_slot: begin
if(flush_sign) begin
br_target_inst_req_state <= br_target_inst_req_empty;
end
else if(pfs_ready_go) begin
br_target_inst_req_state <= br_target_inst_req_wait_br_target;
end
end
br_target_inst_req_wait_br_target: begin
if(pfs_ready_go || flush_sign) begin
br_target_inst_req_state <= br_target_inst_req_empty;
end
end
default: begin
br_target_inst_req_state <= br_target_inst_req_empty;
end
endcase
end
/*
* btb lock
* btb ret only maintain one clock
* when pfs not ready go, should buffer btb ret
*/
reg [37:0] btb_lock_buffer;
reg btb_lock_en;
always @(posedge clk) begin
if (reset || flush_sign || fetch_en)
btb_lock_en <= 1'b0;
else if (btb_en && !pfs_ready_go) begin
btb_lock_en <= 1'b1;
btb_lock_buffer <= {btb_taken, btb_index, btb_ret_pc};
end
end
assign btb_ret_pc_t = {32{btb_lock_en}} & btb_lock_buffer[31:0] | btb_ret_pc;
assign btb_index_t = {5{btb_lock_en}} & btb_lock_buffer[36:32] | btb_index;
assign btb_taken_t = btb_lock_en && btb_lock_buffer[37] || btb_taken;
assign btb_en_t = btb_lock_en || btb_en;
// pre-IF stage
assign pfs_ready_go = (inst_valid || pfs_excp) && inst_addr_ok;
assign to_fs_valid = ~reset && pfs_ready_go;
assign seq_pc = fs_pc + 32'h4;
assign excp_entry = {32{excp_tlbrefill}} & csr_tlbrentry |
{32{!excp_tlbrefill}} & csr_eentry ;
assign inst_flush_pc = {32{ertn_flush}} & csr_era |
{32{refetch_flush || icacop_flush || idle_flush}} & (ws_pc + 32'h4) ;
assign nextpc = (flush_inst_req_state == flush_inst_req_full) ? flush_inst_req_buffer :
excp_flush ? excp_entry :
(ertn_flush || refetch_flush || icacop_flush || idle_flush) ? inst_flush_pc :
(br_target_inst_req_state == br_target_inst_req_wait_br_target) ? br_target_inst_req_buffer :
btb_pre_error_flush && fs_valid ? btb_pre_error_flush_target:
fetch_btb_target ? btb_ret_pc_t :
seq_pc ;
/*
*when encounter tlb excp, stop inst fetch until excp_flush. avoid fetch useless inst.
*but should not lock when btb state machine or flush state machine is work.
*/
assign tlb_excp_lock_pc = tlb_excp_cancel_req && br_target_inst_req_state != br_target_inst_req_wait_br_target && flush_inst_req_state != flush_inst_req_full;
//when flush_sign meet icache_busy 1, flush_sign's inst valid should not set immediately
assign inst_valid = (fs_allowin && !pfs_excp && !tlb_excp_lock_pc || flush_sign || btb_pre_error_flush) && !(idle_flush || idle_lock);
assign inst_op = 1'b0;
assign inst_wstrb = 4'h0;
assign inst_addr = nextpc; //nextpc
assign inst_wdata = 32'b0;
assign fs_inst = (inst_buff_enable) ? inst_rd_buff : inst_rdata;
//inst read buffer use for stall situation
always @(posedge clk) begin
if (reset || (fs_ready_go && ds_allowin) || flush_sign) begin
inst_buff_enable <= 1'b0;
end
else if ((inst_data_ok) && !ds_allowin) begin
inst_rd_buff <= inst_rdata;
inst_buff_enable <= 1'b1;
end
end
//exception
assign pfs_excp_adef = (nextpc[0] || nextpc[1]); //word align
//tlb
assign fs_excp_tlbr = !inst_tlb_found && inst_addr_trans_en;
assign fs_excp_pif = !inst_tlb_v && inst_addr_trans_en;
assign fs_excp_ppi = (csr_plv > inst_tlb_plv) && inst_addr_trans_en;
assign tlb_excp_cancel_req = fs_excp_tlbr || fs_excp_pif || fs_excp_ppi;
assign pfs_excp = pfs_excp_adef;
assign pfs_excp_num = {pfs_excp_adef};
assign excp = fs_excp || fs_excp_tlbr || fs_excp_pif || fs_excp_ppi ;
assign excp_num = {fs_excp_ppi, fs_excp_pif, fs_excp_tlbr, fs_excp_num};
//addr trans
assign inst_addr_trans_en = pg_mode && !dmw0_en && !dmw1_en;
//addr dmw trans //TOT
assign dmw0_en = ((csr_dmw0[`PLV0] && csr_plv == 2'd0) || (csr_dmw0[`PLV3] && csr_plv == 2'd3)) && (fs_pc[31:29] == csr_dmw0[`VSEG]) && pg_mode;
assign dmw1_en = ((csr_dmw1[`PLV0] && csr_plv == 2'd0) || (csr_dmw1[`PLV3] && csr_plv == 2'd3)) && (fs_pc[31:29] == csr_dmw1[`VSEG]) && pg_mode;
//uncache judgement
assign da_mode = csr_da && !csr_pg;
assign pg_mode = csr_pg && !csr_da;
assign inst_uncache_en = (da_mode && (csr_datf == 2'b0)) ||
(dmw0_en && (csr_dmw0[`DMW_MAT] == 2'b0)) ||
(dmw1_en && (csr_dmw1[`DMW_MAT] == 2'b0)) ||
(inst_addr_trans_en && (inst_tlb_mat == 2'b0)) ||
disable_cache;
//assign inst_uncache_en = 1'b1; //used for debug
// IF stage
assign fs_ready_go = inst_data_ok || inst_buff_enable || excp;
assign fs_allowin = !fs_valid || fs_ready_go && ds_allowin;
assign fs_to_ds_valid = fs_valid && fs_ready_go;
always @(posedge clk) begin
if (reset || flush_inst_delay) begin
fs_valid <= 1'b0;
end
else if (fs_allowin) begin
fs_valid <= to_fs_valid;
end
if (reset) begin
fs_pc <= 32'h1bfffffc; //trick: to make nextpc be 0x1c000000 during reset
fs_excp <= 1'b0;
fs_excp_num <= 4'b0;
end
else if (to_fs_valid && (fs_allowin || flush_inst_go_dirt)) begin
fs_pc <= nextpc;
fs_excp <= pfs_excp;
fs_excp_num <= pfs_excp_num;
end
end
//go btb and tlb
assign fetch_pc = nextpc;
assign fetch_en = inst_valid && inst_addr_ok;
endmodule