Skip to content

Conversation

DiamondRC
Copy link

Created a PID block for use on PandA devices.

The PID accepts four parameters from input signals, allowing for custom tuning to fit the system. These include: a proportioal term, a derivative term, an integral term and a feed-forward term.

The PID takes a setpoint value and a measured value and returns a correction term for motion.

Currently the PID caps values at the signed limits, which are dynamically calculated from the size of the input/output signals. Whilst this provides flexability, if the values are fixed then this could be simplified to conserve board space.

end process;

-- Error calc
in_error <= setpoint - in_signal;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the in_error be updated inside the process?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would mean the derivative and integral term would need to wait until the next clock edge to use the in_error value? Keeping it outside the process allows the value to be immediately available.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that will be the behaviour.

However, I think calculating in_error outside the process makes it asynchronous. Since both in_signal and setpoint are synchronous, updating in_error outside the process could introduce timing issues, especially since the PID loop uses it in the same clock cycle.

In digital control systems, it's common for all control terms (proportional, derivative and integral) to be computed based on values sampled at discrete time intervals, not continuously. Therefore I would say that it is expected that the systems waits for the next clock edge before using updated values.

It might be safer to move the calculation inside the process to keep everything aligned with the clock. I’d also consider doing the same for out_signal, as that might help handle the mismatch between the Panda and PPMAC clocks.

I'm not an expert in FPGA design, so I might be missing something, but this seems like a more robust approach. I'm happy to hear other perspectives.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LeandroMartinsdS, just think of in_error as an intermediate value in the calculation. From an FPGA timing point of view I'd be more concerned about the multiplies. A single tick 32x32 => 64 multiply with unregistered inputs ... you can get away with a lot on a 125 MHz clock, but even so I will expect serious timing problems with this design.

More to the point, I think I see the integral term being updated on every tick. Mmm. This is ambitious, and you're going to need a very small integral term for most sensible applications. Normally I'd expect the PID controller to update at a slower tempo than the FPGA clock; I'd recommend an input strobe to drive this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohhhh. Oh dear. I just noticed: slo_clk_i is an ordinary signal being used as a fabric clock.

The FPGA doesn't work like that, I'll put a review comment in the appropriate place above.

Copy link
Contributor

@Araneidae Araneidae left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of extra thoughts:

  • This needs to be marked as Draft as it has clearly never been synthesised
  • There is no testbench, there really does need to be something

Comment on lines 59 to 64
process(slo_clk_i)
constant max_lim : signed(out_signal'range) := (out_signal'left => '0', others => '1');
constant min_lim : signed(out_signal'range) := (out_signal'left => '1', others => '0');

begin
if rising_edge(slo_clk_i) then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

slo_clk_i is an ordinary signal derived from the bit bus (this is clear from its definition below in pid.block.ini). I'm afraid you simply cannot use this a "fabric" clock, which is what you're trying to do here. Instead you need to write:

process (clk_i) begin
    if rising_edge(clk_i) then
        if slo_clk_i then
            ... do stuff ...
        end if;
    end if;
end process;

Things then get more tricky, you'll need to figure out just how quickly everything proceeds, and you might need to internally create 1 and 2 tick delayed versions of slo_clk_i to help manage your calculations.

@Araneidae Araneidae marked this pull request as draft May 28, 2025 08:03
@LeandroMartinsdS
Copy link

It might be worth to have a look at the #188

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants