En los sistemas SOC, he visto que funcionan un poco diferente de lo que la mayoría de las personas esperan: NO utilizan la señal de interrupción de forma ascendente / descendente de forma inmediata.
Las interrupciones completamente sincrónicas primero deben sincronizarse. Esto no es necesario con la mayoría de las interrupciones generadas internamente, ya que a menudo son generadas por un circuito que ya se está ejecutando en el reloj de la CPU.
Luego, la señal se retrasa en un ciclo de un reloj que le da el estado 'anterior'. A continuación, un circuito lógico verifica si el valor actual difiere del anterior. Si es así tienes una ventaja.
-
Si el estado anterior era bajo y el estado actual es alto, tiene un borde ascendente.
-
Si el estado anterior era alto y el estado actual es bajo, tiene una ventaja negativa.
Aquí hay un código Verilog:
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
Y aquí hay una forma de onda: