Severson-Group / AMDC-Firmware

Embedded system code (C and Verilog) which runs the AMDC Hardware
http://docs.amdc.dev/firmware
BSD 3-Clause "New" or "Revised" License
30 stars 5 forks source link

Generate C code scheduler tick ISR based on PWM carrier #314

Closed npetersen2 closed 1 month ago

npetersen2 commented 12 months ago

In the v1.0 firmware, all tasks in the C code run in a single time slice using the cooperative scheduler in the sys/scheduler module, read more about this here. The base scheduler time slice quantum defaults to 100 usec (10 kHz), and can be updated by the user via the usr/user_config.h file and the SYS_TICK_FREQ define, see here.

The way this ISR is generated is by the FPGA using the Xilinx-provided Timer IP core. We have a wrapper C code API located in drv/timer to set up the timer and clear the ISR. This line of code is what sets up the ISR to run at the desired rate.

Problem

This code works just fine: it triggers the scheduler to run at the desired rate. However, this timer is totally separate from the PWM carrier which interfaces to the motor which the AMDC is trying to drive. This creates a timing delay between when the PWM carrier operates and when the Timer module calls the scheduler. This is bad--we want the scheduler to run synchronized to the PWM carrier!

Note: the PWM carrier is a triangle wave in the FPGA (simple counter register which goes up and down). The user configures its frequency and this sets the switching frequency of the power electronics connected to the motor.

Solution

Let's align the scheduler and the PWM carrier by implementing the following:

  1. Create a new IP core called amdc_isr_gen
  2. Pass the FPGA top-level pwm_carrier_high and pwm_carrier_low signals into this new IP core
    1. Note: these carrier high and low signals go HIGH for a single clock cycle when the PWM carrier (triangle wave) reaches either the top level (high) or bottom level (low). This means that each signal individually looks like a single cycle pulse at the PWM switching frequency. Together, both high and low look like pulses at 2x the switching frequency.
  3. Add C code API to let the user configure the ratio between the carrier frequency to the control frequency. For example, in v1.0 firmware, the default PWM carrier freq is 100 kHz, but the default control rate is 10 kHz. This means the default ratio is 10
  4. Implement some logic within the new IP core which counts each carrier high/low pulse. Once the count gets to the user-set ratio number, trigger the scheduler ISR.
  5. Remove the Xilinx Timer IP core and the drv/timer module
  6. Change the initialization code which used to call timer_init() from scheduler_init()--- now it should call the new driver to set the ratio to the default 10 value.
  7. Update the usr/user_config.h file to expose a SYS_PWM_CARRIER_CONTROL_RATIO define which defaults to 10 but allows the user to change it.
  8. Make sure the sys/scheduler internal state stays intact---it keeps track of elapsed usec in order to know when to trigger the registered tasks. The new ISR should update the scheduler state so that every still works as desired.

Note: be sure to write the code remembering that the user is free to change the PWM switching frequency at any time during operation. They might change it from 100 kHz to 50 kHz or 200 kHz, etc. The new code should be able to handle this! When the user changes the PWM switching frequency, for the same ratio between control and PWM carrier, the scheduler base quantum will change! This means, each ISR from the FPGA, a different number of usec will have elapsed. You must handle this correctly for the user tasks to still be called at the correct rate.

Testing

By default, this change should be transparent to the C code. The default firmware should still run the scheduler at 10 kHz, but now the relative timing of the scheduler will be aligned to the PWM carrier triangle wave.

The user should be able to change the ratio and see their control code run at a different subrate of the PWM carrier.

Validate this by running the baseline blink user application and confirm that it still runs at its desired rate (i.e., 5 Hz). Use the task timing stats or the drv/cpu_timer to check. Change the PWM switching frequency and ensure nothing changes with the user task rate.

npetersen2 commented 11 months ago

To validate these changes:

Test with a range of values for X, Y, and Z:

Validate the baseline default firmware still operates as expected:

  1. Create a task which runs at X Hz (see example on creating tasks) and toggles a GPIO pin every time it runs a. To toggle the pin, use the gpio_direct driver, see C code API and hw command handler
  2. Turn on task timing statistics (see example tutorial)
  3. Run the task on the AMDC hardware
  4. Hook up the oscilloscope to view the pin toggling. It should toggle every 1/X seconds.
  5. Print the task stats information to ensure the loop time is 1/X seconds

Validate correct handling of PWM carrier frequency:

  1. Change the PWM carrier frequency from the default 100 kHz to Y kHz
  2. Ensure the GPIO pin still toggles every 1/X seconds
  3. Print the task stats information to ensure the loop time is still 1/X seconds

Validate correct handling of PWM frequency to scheduler ratio:

  1. Change the ratio define from the default 20 to Z
  2. Ensure the GPIO pin still toggles every 1/X seconds
  3. Print the task stats information to ensure the loop time is still 1/X seconds
npetersen2 commented 1 month ago

@annikaolson has implemented this and been testing it. Once #377 is merged, let's close this issue.

npetersen2 commented 1 month ago

Let's close this as we have implemented this a bit differently, see latest v1.3-staging branch commits