In SOC systems which I have seen they work a bit different from what most people expect: They do NOT use the interrupt signal rising/falling edge immediately.
Completely a-synchronous interrupts first need to be synchronized. This is not needed with most internal generated interrupts as they are often generated by a circuit which is already running at the CPU clock.
Then the signal is delayed by one-clock cycle which give you the 'previous' state. Next a logic circuit checks if the current value differs from the previous one. If so you had an edge.
Here is some Verilog code:
module irq_edge (
input sys_clk,
input sys_reset_n,
input async_irqA, // A-synchronous interrupt
input sync_irqB, // synchronous interrupt
// Outputs
// Beware : high for only one clock cycle
output rising_edge_A,
output rising_edge_B,
output falling_edge_A,
output falling_edge_B
);
reg meta_syncA,safe_syncA;
reg prev_irqA,prev_irqB;
always @(posedge sys_clk or negedge sys_reset_n)
begin
if (!sys_reset_n)
begin
meta_syncA <= 1'b0;
safe_syncA <= 1'b0;
prev_irqA <= 1'b0;
prev_irqB <= 1'b0;
end
else
begin
// Sychronise async_irqA to system clock
meta_syncA <= async_irqA;
safe_syncA <= meta_syncA;
// Delay for edge detection
prev_irqA <= safe_syncA;
prev_irqB <= sync_irqB;
end
end
// Compare old and current value of signals
assign rising_edge_A = ~prev_irqA & safe_syncA; // was low now high
assign rising_edge_B = ~prev_irqB & sync_irqB; // was low now
assign falling_edge_A = prev_irqA & ~safe_syncA; // was high now low
assign falling_edge_B = prev_irqB & ~sync_irqB; // was high now low
endmodule
And here is a waveform:
