hw/vendor/lowrisc_ibex/rtl/ibex_if_stage.sv Cov: 100%
1: // Copyright lowRISC contributors.
2: // Copyright 2018 ETH Zurich and University of Bologna, see also CREDITS.md.
3: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
4: // SPDX-License-Identifier: Apache-2.0
5:
6: /**
7: * Instruction Fetch Stage
8: *
9: * Instruction fetch unit: Selection of the next PC, and buffering (sampling) of
10: * the read instruction.
11: */
12: module ibex_if_stage #(
13: parameter int unsigned DmHaltAddr = 32'h1A110800,
14: parameter int unsigned DmExceptionAddr = 32'h1A110808
15: ) (
16: input logic clk_i,
17: input logic rst_ni,
18:
19: input logic [31:0] boot_addr_i, // also used for mtvec
20: input logic req_i, // instruction request control
21:
22: // instruction cache interface
23: output logic instr_req_o,
24: output logic [31:0] instr_addr_o,
25: input logic instr_gnt_i,
26: input logic instr_rvalid_i,
27: input logic [31:0] instr_rdata_i,
28: input logic instr_err_i,
29: input logic instr_pmp_err_i,
30:
31: // output of ID stage
32: output logic instr_valid_id_o, // instr in IF-ID is valid
33: output logic instr_new_id_o, // instr in IF-ID is new
34: output logic [31:0] instr_rdata_id_o, // instr for ID stage
35: output logic [15:0] instr_rdata_c_id_o, // compressed instr for ID stage
36: // (mtval), meaningful only if
37: // instr_is_compressed_id_o = 1'b1
38: output logic instr_is_compressed_id_o, // compressed decoder thinks this
39: // is a compressed instr
40: output logic instr_fetch_err_o, // bus error on fetch
41: output logic illegal_c_insn_id_o, // compressed decoder thinks this
42: // is an invalid instr
43: output logic [31:0] pc_if_o,
44: output logic [31:0] pc_id_o,
45:
46: // control signals
47: input logic instr_valid_clear_i, // clear instr valid bit in IF-ID
48: input logic pc_set_i, // set the PC to a new value
49: input ibex_pkg::pc_sel_e pc_mux_i, // selector for PC multiplexer
50: input ibex_pkg::exc_pc_sel_e exc_pc_mux_i, // selects ISR address
51: input ibex_pkg::exc_cause_e exc_cause, // selects ISR address for
52: // vectorized interrupt lines
53: // jump and branch target
54: input logic [31:0] jump_target_ex_i, // jump target address
55:
56: // CSRs
57: input logic [31:0] csr_mepc_i, // PC to restore after handling
58: // the interrupt/exception
59: input logic [31:0] csr_depc_i, // PC to restore after handling
60: // the debug request
61: input logic [31:0] csr_mtvec_i, // base PC to jump to on exception
62: output logic csr_mtvec_init_o, // tell CS regfile to init mtvec
63:
64: // pipeline stall
65: input logic id_in_ready_i, // ID stage is ready for new instr
66:
67: // misc signals
68: output logic if_busy_o, // IF stage is busy fetching instr
69: output logic perf_imiss_o // instr fetch miss
70: );
71:
72: import ibex_pkg::*;
73:
74: logic offset_in_init_d, offset_in_init_q;
75: logic have_instr;
76:
77: // prefetch buffer related signals
78: logic prefetch_busy;
79: logic branch_req;
80: logic [31:0] fetch_addr_n;
81:
82: logic fetch_valid;
83: logic fetch_ready;
84: logic [31:0] fetch_rdata;
85: logic [31:0] fetch_addr;
86: logic fetch_err;
87:
88: logic [31:0] exc_pc;
89:
90: logic [5:0] irq_id;
91: logic unused_irq_bit;
92:
93: logic if_id_pipe_reg_we; // IF-ID pipeline reg write enable
94:
95: logic [7:0] unused_boot_addr;
96: logic [7:0] unused_csr_mtvec;
97:
98: assign unused_boot_addr = boot_addr_i[7:0];
99: assign unused_csr_mtvec = csr_mtvec_i[7:0];
100:
101: // extract interrupt ID from exception cause
102: assign irq_id = {exc_cause};
103: assign unused_irq_bit = irq_id[5]; // MSB distinguishes interrupts from exceptions
104:
105: // exception PC selection mux
106: always_comb begin : exc_pc_mux
107: unique case (exc_pc_mux_i)
108: EXC_PC_EXC: exc_pc = { csr_mtvec_i[31:8], 8'h00 };
109: EXC_PC_IRQ: exc_pc = { csr_mtvec_i[31:8], 1'b0, irq_id[4:0], 2'b00 };
110: EXC_PC_DBD: exc_pc = DmHaltAddr;
111: EXC_PC_DBG_EXC: exc_pc = DmExceptionAddr;
112: default: exc_pc = { csr_mtvec_i[31:8], 8'h00 };
113: endcase
114: end
115:
116: // fetch address selection mux
117: always_comb begin : fetch_addr_mux
118: unique case (pc_mux_i)
119: PC_BOOT: fetch_addr_n = { boot_addr_i[31:8], 8'h80 };
120: PC_JUMP: fetch_addr_n = jump_target_ex_i;
121: PC_EXC: fetch_addr_n = exc_pc; // set PC to exception handler
122: PC_ERET: fetch_addr_n = csr_mepc_i; // restore PC when returning from EXC
123: PC_DRET: fetch_addr_n = csr_depc_i;
124: default: fetch_addr_n = { boot_addr_i[31:8], 8'h80 };
125: endcase
126: end
127:
128: // tell CS register file to initialize mtvec on boot
129: assign csr_mtvec_init_o = (pc_mux_i == PC_BOOT) & pc_set_i;
130:
131: // prefetch buffer, caches a fixed number of instructions
132: ibex_prefetch_buffer prefetch_buffer_i (
133: .clk_i ( clk_i ),
134: .rst_ni ( rst_ni ),
135:
136: .req_i ( req_i ),
137:
138: .branch_i ( branch_req ),
139: .addr_i ( {fetch_addr_n[31:1], 1'b0} ),
140:
141: .ready_i ( fetch_ready ),
142: .valid_o ( fetch_valid ),
143: .rdata_o ( fetch_rdata ),
144: .addr_o ( fetch_addr ),
145: .err_o ( fetch_err ),
146:
147: // goes to instruction memory / instruction cache
148: .instr_req_o ( instr_req_o ),
149: .instr_addr_o ( instr_addr_o ),
150: .instr_gnt_i ( instr_gnt_i ),
151: .instr_rvalid_i ( instr_rvalid_i ),
152: .instr_rdata_i ( instr_rdata_i ),
153: .instr_err_i ( instr_err_i ),
154: .instr_pmp_err_i ( instr_pmp_err_i ),
155:
156: // Prefetch Buffer Status
157: .busy_o ( prefetch_busy )
158: );
159:
160:
161: // offset initialization state
162: always_ff @(posedge clk_i or negedge rst_ni) begin
163: if (!rst_ni) begin
164: offset_in_init_q <= 1'b1;
165: end else begin
166: offset_in_init_q <= offset_in_init_d;
167: end
168: end
169:
170: // offset initialization related transition logic
171: always_comb begin
172: offset_in_init_d = offset_in_init_q;
173:
174: fetch_ready = 1'b0;
175: branch_req = 1'b0;
176: have_instr = 1'b0;
177:
178: if (offset_in_init_q) begin
179: // no valid instruction data for ID stage, assume aligned
180: if (req_i) begin
181: branch_req = 1'b1;
182: offset_in_init_d = 1'b0;
183: end
184: end else begin
185: // an instruction is ready for ID stage
186: if (fetch_valid) begin
187: have_instr = 1'b1;
188:
189: if (req_i && if_id_pipe_reg_we) begin
190: fetch_ready = 1'b1;
191: offset_in_init_d = 1'b0;
192: end
193: end
194: end
195:
196: // take care of jumps and branches
197: if (pc_set_i) begin
198: have_instr = 1'b0;
199:
200: // switch to new PC from ID stage
201: branch_req = 1'b1;
202: offset_in_init_d = 1'b0;
203: end
204: end
205:
206: assign pc_if_o = fetch_addr;
207: assign if_busy_o = prefetch_busy;
208: assign perf_imiss_o = ~fetch_valid | branch_req;
209:
210: // compressed instruction decoding, or more precisely compressed instruction
211: // expander
212: //
213: // since it does not matter where we decompress instructions, we do it here
214: // to ease timing closure
215: logic [31:0] instr_decompressed;
216: logic illegal_c_insn;
217: logic instr_is_compressed_int;
218:
219: ibex_compressed_decoder compressed_decoder_i (
220: .clk_i ( clk_i ),
221: .rst_ni ( rst_ni ),
222: .valid_i ( fetch_valid ),
223: .instr_i ( fetch_rdata ),
224: .instr_o ( instr_decompressed ),
225: .is_compressed_o ( instr_is_compressed_int ),
226: .illegal_instr_o ( illegal_c_insn )
227: );
228:
229: // IF-ID pipeline registers, frozen when the ID stage is stalled
230: assign if_id_pipe_reg_we = have_instr & id_in_ready_i;
231:
232: always_ff @(posedge clk_i or negedge rst_ni) begin : if_id_pipeline_regs
233: if (!rst_ni) begin
234: instr_new_id_o <= 1'b0;
235: instr_valid_id_o <= 1'b0;
236: instr_rdata_id_o <= '0;
237: instr_fetch_err_o <= '0;
238: instr_rdata_c_id_o <= '0;
239: instr_is_compressed_id_o <= 1'b0;
240: illegal_c_insn_id_o <= 1'b0;
241: pc_id_o <= '0;
242: end else begin
243: instr_new_id_o <= if_id_pipe_reg_we;
244: if (if_id_pipe_reg_we) begin
245: instr_valid_id_o <= 1'b1;
246: instr_rdata_id_o <= instr_decompressed;
247: instr_fetch_err_o <= fetch_err;
248: instr_rdata_c_id_o <= fetch_rdata[15:0];
249: instr_is_compressed_id_o <= instr_is_compressed_int;
250: illegal_c_insn_id_o <= illegal_c_insn;
251: pc_id_o <= pc_if_o;
252: end else if (instr_valid_clear_i) begin
253: instr_valid_id_o <= 1'b0;
254: end
255: end
256: end
257:
258: ////////////////
259: // Assertions //
260: ////////////////
261:
262: // Selectors must be known/valid.
263: `ASSERT_KNOWN(IbexExcPcMuxKnown, exc_pc_mux_i, clk_i, !rst_ni)
264: `ASSERT(IbexPcMuxValid, pc_mux_i inside {
265: PC_BOOT,
266: PC_JUMP,
267: PC_EXC,
268: PC_ERET,
269: PC_DRET
270: }, clk_i, !rst_ni)
271:
272: // Boot address must be aligned to 256 bytes.
273: `ASSERT(IbexBootAddrUnaligned, boot_addr_i[7:0] == 8'h00, clk_i, !rst_ni)
274:
275: // Errors must only be sent together with rvalid.
276: `ASSERT(IbexInstrErrWithoutRvalid, instr_err_i |-> instr_rvalid_i, clk_i, !rst_ni)
277:
278: // Address must not contain X when request is sent.
279: `ASSERT(IbexInstrAddrUnknown, instr_req_o |-> !$isunknown(instr_addr_o), clk_i, !rst_ni)
280:
281: // Address must be word aligned when request is sent.
282: `ASSERT(IbexInstrAddrUnaligned, instr_req_o |-> (instr_addr_o[1:0] == 2'b00), clk_i, !rst_ni)
283:
284: endmodule
285: