0
\$\begingroup\$

I made a single-cycle RISC CPU in Verilog, and it works well on a real FPGA I have. It has multiple components that communicate over an internal bus. One of these components is a programmable timer, that has a prescaler and counter, and has multiple ways of counting time. It works fine, and can generate PWM output (verified).

My problem lies with the concept of timer generated interrupts: Many microcontrollers can generate internal interrupt signal when the timer counter resets, and so does my timer. It has an interrupt line, that it pulls low when it wraps around:

always @(posedge clk or negedge reset) begin
 if(!reset) begin
 timer_irq <= 1'b1;
 end
 else begin
 if ((prescaler_value >= (prescaler_threshold))
 && (counter_value >= (counter_threshold))) begin
 timer_irq <= 1'b0;
 end
 else begin
 timer_irq <= 1'b1;
 end
 end
end

This logic works fine conceptionally (verified in simulator and with a logic analyzer on real hardware: The interrupt signal is generated when it should be), but it will not be sampled by my CPU, since it also samples the interrupts in the write-back phase on rising clock edge:

always @(posedge clk)
begin
 // ...
 if (((~irq_sources & irq_mask) != 3'b0) && (in_isr == 1'b0)) begin
 ipc <= pc_next;
 pc <= irq_target;
 in_isr <= 1'b1;
 end
 else begin
 pc <= pc_next;
 end
end

So the interrupt goes low when my clock goes high, and the FPGA does not sample this as an interrupt. It basically "just misses" the interrupt. My interrupt implementation works fine with external interrupts. Maybe the problem appears because of propagation times? Does anybody have a hint how I could design this better?

Here is a GTKWave plot of the problem: The interrupt is only "sampled" one clock cycle later (the main program is idling at address 0x0028, and the ISR is at address 0x002C)

enter image description here

asked Mar 5, 2020 at 18:52
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

This type of thing is normal within the synchronous system. It is like you have one DFF after another - the signal will propagate to second DFF only when first has changed its state on previous clock edge.

Think about changing clock edge your interrupt line is activated with - you can use negative edge of the clock, and then next time posedge happens, just raised interrupt signal will be sampled down the line.

answered Mar 5, 2020 at 19:32
\$\endgroup\$
5
  • \$\begingroup\$ Thank you for this answer - But wouldn't using the falling edge introduce another clock domain and open my project up to issues regarding metastability? \$\endgroup\$ Commented Mar 5, 2020 at 19:40
  • 1
    \$\begingroup\$ It is the same clock you use, but another edge. Time between falling and raising edge should be enough for signal to propagate through the circuit to be sampled on opposite edge of the same clock. I say should be, but you anyway must test this at the model level and on real device. Example: clocking embedded RAM using posedge, and reading data from RAM using negedge, thus half clock period must be bigger than RAM access time... Ah yes, and mind clock duty cycle! \$\endgroup\$ Commented Mar 5, 2020 at 19:43
  • \$\begingroup\$ Thank you! It now reacts to the IRQ on real hardware: i.imgur.com/VkO4GJv.png It now shows another problem in that it jumps to 0x0 instead of the ISR, which doesnt happen on the simulation. But that's something for me to debug now. Thanks for your help! \$\endgroup\$ Commented Mar 5, 2020 at 19:53
  • 1
    \$\begingroup\$ That may happen because you raise interrupt, but does not initialize jump vector. If it is the case, initialize jump vector value together with raising IRQ signal. In general - prepare everything for the interrupt handling in the always construct where you raise the interrupt (or in different always constructs driven by the negedge of the clock). \$\endgroup\$ Commented Mar 5, 2020 at 20:04
  • 1
    \$\begingroup\$ I debugged it and figured it out: I had a priority-selector that checked which IRQ signals are low, and decided which ISR address from the jump vector to present to the CPU, with the GPIO IRQs being lower priority than the invalid instruction and timer interrupts. It was somehow bugged (nested if-else always executing the last else branch, giving address 0x0, the fallback). I now replaced it with individual if statements, and everything works fine. I dont understand why this didn't work, but oh well. It's fixed now: i.imgur.com/HxpspmO.png Thanks again for your help. \$\endgroup\$ Commented Mar 5, 2020 at 20:08
1
\$\begingroup\$

You could possibly change the logic so that you generate an interrupt pulse more than 1 clock cycle wide. Start when you wrap around but continue until the count is 2 or 3 maybe? Will an interrupt that is a few clock cycles wide cause your CPU to process it as multiple interrupts?

answered Mar 5, 2020 at 19:53
\$\endgroup\$
1
  • \$\begingroup\$ That would work too, since my CPU does not "remember" IRQs that arrived when it already handles an interrupt, and interrupts are disabled when inside an ISR. \$\endgroup\$ Commented Mar 5, 2020 at 19:55

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.