Browse Source

fpga/picorv32: fix waitirq instructions after changes

Fix waitirq instruction breakage, and some other IRQ-related tidying
up.

Signed-off-by: H. Peter Anvin <hpa@zytor.com>
H. Peter Anvin 2 years ago
parent
commit
ea69fdc29b
1 changed files with 53 additions and 54 deletions
  1. 53 54
      fpga/picorv32.v

+ 53 - 54
fpga/picorv32.v

@@ -261,6 +261,7 @@ module picorv32 #(
 	reg [31:0] irq_pending;
 	reg [31:0] timer;
 	reg [31:0] buserr_address;
+        wire [31:0] active_irqs = irq_pending & ~irq_mask;
 
 `ifndef PICORV32_REGS
 	reg [31:0] cpuregs [0:regfile_size-1];
@@ -1270,7 +1271,6 @@ module picorv32 #(
 	localparam cpu_state_ldmem  = 8'b00000001;
 
 	reg [7:0] cpu_state;
-	reg [1:0] irq_state;
 
 	`FORMAL_KEEP reg [127:0] dbg_ascii_state;
 
@@ -1293,6 +1293,7 @@ module picorv32 #(
 	reg latched_store;
 	reg latched_stalu;
 	reg latched_branch;
+        reg latched_irq;
 	reg latched_compr;
 	reg latched_trace;
 	reg latched_is_lu;
@@ -1389,14 +1390,14 @@ module picorv32 #(
 		clear_prefetched_high_word = clear_prefetched_high_word_q;
 		if (!prefetched_high_word)
 			clear_prefetched_high_word = 0;
-		if (latched_branch || irq_state || !resetn)
+		if (latched_branch || latched_irq || !resetn)
 			clear_prefetched_high_word = COMPRESSED_ISA;
 	end
 
-	reg cpuregs_write;
-	reg [31:0] cpuregs_wrdata;
-	reg [31:0] cpuregs_rs1;
-	reg [31:0] cpuregs_rs2;
+	(* preserve = 1 *) reg cpuregs_write;
+	(* preserve = 1 *) reg [31:0] cpuregs_wrdata;
+	(* preserve = 1 *) reg [31:0] cpuregs_rs1;
+	(* preserve = 1 *) reg [31:0] cpuregs_rs2;
 	reg [regfile_bits-1:0] decoded_rs;
 
 	always @* begin
@@ -1414,10 +1415,6 @@ module picorv32 #(
 					cpuregs_wrdata = latched_stalu ? alu_out_q : reg_out;
 					cpuregs_write = 1;
 				end
-				ENABLE_IRQ && irq_state[1]: begin
-					cpuregs_wrdata = irq_pending & ~irq_mask;
-					cpuregs_write = 1;
-				end
 			endcase
 		end
 	end
@@ -1498,7 +1495,7 @@ module picorv32 #(
 
 	assign launch_next_insn = cpu_state == cpu_state_fetch &&
 				  decoder_trigger &&
-				  (!ENABLE_IRQ || irq_delay || irq_active || !(irq_pending & ~irq_mask));
+				  (!ENABLE_IRQ || irq_delay || irq_active || !active_irqs);
 
         wire [31:0] csrr_src = instr_funct2[2] ? { 29'b0, decoded_rs1[4:0] } : cpuregs_rs1;
 
@@ -1542,7 +1539,6 @@ module picorv32 #(
 		decoder_trigger_q <= decoder_trigger;
 		decoder_pseudo_trigger <= 0;
 		decoder_pseudo_trigger_q <= decoder_pseudo_trigger;
-		do_waitirq <= 0;
 
 		trace_valid <= 0;
 
@@ -1562,6 +1558,7 @@ module picorv32 #(
 			latched_store <= 0;
 			latched_stalu <= 0;
 			latched_branch <= 0;
+		        latched_irq <= 0;
 			latched_trace <= 0;
 			latched_is_lu <= 0;
 			latched_is_lh <= 0;
@@ -1573,9 +1570,9 @@ module picorv32 #(
 			irq_delay <= 0;
 			irq_mask <= ~0;
 			next_irq_pending = 0;
-			irq_state <= 0;
 			eoi <= 0;
-			timer <= 0;
+		        timer <= 0;
+	                do_waitirq <= 0;
 			if (~STACKADDR) begin
 				latched_store <= 1;
 				latched_rd <= (USER_CONTEXTS << xreg_bits) | 2;
@@ -1590,7 +1587,8 @@ module picorv32 #(
 			end
 
 			cpu_state_fetch: begin
-			        mem_do_rinst <= !decoder_trigger && !do_waitirq && !(halt && !irq_state);
+			        eoi <= 0;
+			        mem_do_rinst <= !decoder_trigger && !do_waitirq && !halt;
 				mem_wordsize <= 0;
 
 				current_pc = reg_next_pc;
@@ -1601,20 +1599,15 @@ module picorv32 #(
 						current_pc = latched_store ? (latched_stalu ? alu_out_q : reg_out) & ~1 : reg_next_pc;
 						`debug($display("ST_RD:  %2d 0x%08x, BRANCH 0x%08x", latched_rd, reg_pc + (latched_compr ? 2 : 4), current_pc);)
 					end
-					latched_store && !latched_branch: begin
+					latched_store && !latched_branch && !latched_irq: begin
 						`debug($display("ST_RD:  %2d 0x%08x", latched_rd, latched_stalu ? alu_out_q : reg_out);)
 					end
-					ENABLE_IRQ && irq_state[0]: begin
-						current_pc = progaddr_irq & ~1;
-						irq_active <= 1;
-						mem_do_rinst <= 1;
-					end
-					ENABLE_IRQ && irq_state[1]: begin
-						eoi <= irq_pending & ~irq_mask;
-						next_irq_pending = next_irq_pending & irq_mask;
-					end
 				endcase
 
+			        if (latched_irq) begin
+					current_pc = progaddr_irq & ~1;
+				        mem_do_rinst  <= 1'b1;
+				end
 				if (ENABLE_TRACE && latched_trace) begin
 					latched_trace <= 0;
 					trace_valid <= 1;
@@ -1630,37 +1623,39 @@ module picorv32 #(
 				latched_store <= 0;
 				latched_stalu <= 0;
 				latched_branch <= 0;
+			        latched_irq <= 0;
 				latched_is_lu <= 0;
 				latched_is_lh <= 0;
 				latched_is_lb <= 0;
 				latched_rd <= decoded_rd;
 				latched_compr <= compressed_instr;
 
-			        if (halt && !irq_state) begin
+			        if (halt && !latched_irq) begin
 				        // Do nothing, but allow an already started instruction or IRQ to complete
 				end else
-				if (ENABLE_IRQ && ((decoder_trigger && !irq_active && !irq_delay && |(irq_pending & ~irq_mask)) || irq_state)) begin
-					irq_state <=
-						irq_state == 2'b00 ? 2'b01 :
-						irq_state == 2'b01 ? 2'b10 : 2'b00;
-					latched_compr <= latched_compr;
-				        latched_rd <= MASK_IRQ_REG;
-				        reg_mepc  <= reg_next_pc | latched_compr;
+				if (ENABLE_IRQ && do_waitirq &&
+				    (&(irq_pending | ~reg_op1) || |(irq_pending & reg_op2))) begin
+				      // Waited-for interrupt: wake up and exit waitirq
+				      // If this interrupt is enabled, it will be taken on the next cycle
+				      latched_store   <= 1;
+				      reg_out         <= irq_pending;
+				      reg_next_pc     <= current_pc + (compressed_instr ? 2 : 4);
+				      do_waitirq      <= 0;
+				 end else
+				 if (ENABLE_IRQ && decoder_trigger && !irq_active && !irq_delay && |active_irqs) begin
+				        irq_active    <= 1'b1;
+				        latched_irq   <= 1'b1;
+				        latched_rd    <= MASK_IRQ_REG;
+				        reg_out       <= active_irqs;
+				        latched_store <= 1'b1;
+				        eoi           <= active_irqs;
+					next_irq_pending = next_irq_pending & irq_mask;
+				        reg_mepc      <= reg_next_pc | latched_compr;
+				        do_waitirq    <= 0; // An unwaited-for interrupt can break waitirq
 				end else
 				if (ENABLE_IRQ && do_waitirq) begin
-					if (&(irq_pending | ~reg_op1) || |(irq_pending & reg_op2)) begin
-						// Waited-for interrupt
-						latched_store <= 1;
-						reg_out <= irq_pending;
-						reg_next_pc <= current_pc + (compressed_instr ? 2 : 4);
-					end else if (decoder_trigger && !irq_active && !irq_delay && |(irq_pending & ~irq_mask)) begin
-						// Allow non-waited-for interrupt to be taken; in this case
-						// PC is *not* advanced so the interrupt routine will return
-						// to waitirq.
-						do_waitirq <= 0;
-					end else begin
-						do_waitirq <= 1;
-					end
+					// Actually waiting for an IRQ...
+					do_waitirq    <= 1; // Keep waiting...
 				end else
 				if (decoder_trigger) begin
 					`debug($display("-- %-0t pc: 0x%08x irq: %x", $time, current_pc, irq_active);)
@@ -1778,7 +1773,6 @@ module picorv32 #(
 						cpu_state <= cpu_state_exec;
 					end
 					ENABLE_IRQ && instr_retirq: begin
-						eoi <= 0;
 						irq_active <= 0;
 						latched_branch <= 1;
 						latched_store <= 1;
@@ -1803,14 +1797,14 @@ module picorv32 #(
 						cpu_state <= cpu_state_fetch;
 					end // case: ENABLE_IRQ && instr_maskirq
 					ENABLE_IRQ && instr_waitirq: begin
-						reg_op1    <= cpuregs_rs1;
-						reg_op2    <= cpuregs_rs2;
+					        reg_op1 <= cpuregs_rs1;
+					        reg_op2 <= cpuregs_rs2;
 						dbg_rs1val <= cpuregs_rs1;
 						dbg_rs1val_valid <= 1;
 						dbg_rs2val <= cpuregs_rs2;
 						dbg_rs2val_valid <= 1;
 						do_waitirq <= 1;
-					        reg_next_pc <= reg_pc;
+					        reg_next_pc <= reg_pc; // Stay on this instruction until released
 						cpu_state  <= cpu_state_fetch;
 					end
 					ENABLE_IRQ && ENABLE_IRQ_TIMER && instr_timer: begin
@@ -1824,9 +1818,14 @@ module picorv32 #(
 					end
 				        ENABLE_IRQ && instr_pollirq: begin
 						latched_store <= 1;
-					        reg_out <= (irq_pending & ~irq_mask & ~cpuregs_rs1) | cpuregs_rs2;
-					        eoi <= irq_pending & ~irq_mask & ~cpuregs_rs1;
+					        reg_out <= (active_irqs & ~cpuregs_rs1) | cpuregs_rs2;
+					        eoi <= active_irqs & ~cpuregs_rs1;
 					        next_irq_pending = next_irq_pending & (irq_mask | cpuregs_rs1);
+						dbg_rs1val <= cpuregs_rs1;
+						dbg_rs1val_valid <= 1;
+						dbg_rs2val <= cpuregs_rs2;
+						dbg_rs2val_valid <= 1;
+						cpu_state <= cpu_state_fetch;
 					end
 					is_lb_lh_lw_lbu_lhu && !instr_trap: begin
 						`debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);)
@@ -2140,7 +2139,7 @@ module picorv32 #(
 			dbg_irq_call <= 0;
 			dbg_irq_enter <= dbg_irq_call;
 		end else
-		if (irq_state == 1) begin
+		if (latched_irq) begin
 			dbg_irq_call <= 1;
 			dbg_irq_ret <= next_pc;
 		end
@@ -2149,7 +2148,7 @@ module picorv32 #(
 			rvfi_rd_addr <= 0;
 			rvfi_rd_wdata <= 0;
 		end else
-		if (cpuregs_write && !irq_state) begin
+		if (cpuregs_write && !latched_irq) begin
 `ifdef PICORV32_TESTBUG_003
 			rvfi_rd_addr <= latched_rd ^ 1;
 `else