We use some essential cookies to make our website work.

We use optional cookies, as detailed in our cookie policy, to remember your settings and understand how you use our website.

13 posts • Page 1 of 1
simont153
Posts: 4
Joined: Wed Jul 09, 2025 4:11 pm

Software triggered PWM

Wed Jul 09, 2025 4:29 pm

I have an application that requires me to trigger a DMA transfer every N software loop cycles. One core is running a CIC filter and the DMA signals the second core to run the comb part. The loop is running a very optimized DSP algorithm and I am looking for ways to not have to deal with the overhead of having to keep track of a counter and launching the DMA.

Browsing the datasheet I have found that a PWM can trigger a DMA transfer on wrap but I do not see a way to increment the accumulator in the PWM channel in software. The only option I see is putting a PWM channel into DIVMODE=RISE and then physically connecting the PIN B to an output pin and pulsing that in software.

Would this work? Is there a better way to produce a DMA transfer every N software loops?

Tharre
Posts: 93
Joined: Mon Mar 17, 2025 5:26 pm

Re: Software triggered PWM

Wed Jul 09, 2025 7:44 pm

simont153 wrote:
Wed Jul 09, 2025 4:29 pm
I have an application that requires me to trigger a DMA transfer every N software loop cycles. One core is running a CIC filter and the DMA signals the second core to run the comb part.
What do you mean, the DMA signals the second core to run? Why can't you just use simple timed interrupt to wake up (or preempt) the second core?
simont153 wrote:
Wed Jul 09, 2025 4:29 pm
Browsing the datasheet I have found that a PWM can trigger a DMA transfer on wrap but I do not see a way to increment the accumulator in the PWM channel in software. The only option I see is putting a PWM channel into DIVMODE=RISE and then physically connecting the PIN B to an output pin and pulsing that in software.
Are you talking about using the PWM as a pacing timer? Because as far as I know PWM slices can't trigger DMA transfers directly, quoting the datasheet:
The following events can
trigger a channel:
• A write to a channel trigger register.
• Completion of another channel whose CHAIN_TO points to this channel.
• A write to the MULTI_CHAN_TRIGGER register (can trigger multiple channels at once).
You can have a dummy DMA channel that's artificially slowed by a PWM slice that triggers another channel to more or less achieve periodic transfers, though with jitter if any of the transfers stall at any point.

You can modify the PWM accumulator by modifying the CH0_CTR register, but I have no idea why you'd want to do that, the hole point of a PWM peripheral is that it's supposed to count on it's own? Why would you want to increment that in software?

gmx
Posts: 1848
Joined: Thu Aug 29, 2024 8:51 pm

Re: Software triggered PWM

Thu Jul 10, 2025 1:03 am

For pacing trigger, there are 4 dedicated DMA timers.

BTW, I think PWM paced transfers can be useful when there's no FIFO available.

PWM accumulator means counter?

simont153
Posts: 4
Joined: Wed Jul 09, 2025 4:11 pm

Re: Software triggered PWM

Fri Jul 11, 2025 3:23 am

I think I did a very mediocre job of explaining my problem.

I have code that looks like this:

Code: Select all

int counter = 0;
while (true) {
	// some DSP algorithm that executes in roughly 300ns
	do_tight_loop();
	
	if (++counter > N) {
		dma_channel_start(chan_num);
		counter = 0
	}
}	
I am trying to optimize out the counter increment, dma software trigger and counter reset. I know it may seem silly but looking at the assembly it represents around 10 instructions which is a significant percentage of the work done in my tight loop.

I am seeking a way to increment some hardware register that will automatically trigger a DMA and reset itself when it reaches a predefined count.

1012
Posts: 3
Joined: Fri Jul 11, 2025 10:03 am

Re: Software triggered PWM

Fri Jul 11, 2025 10:36 am

Hi,

the 'advance phase' feature of the PWM might be what you seek. In the SDK the counter can be advanced with pwm_advance_count(), but since you have an assembler implementation you are probably more interested in the PWM register PWM_CHx_CSR - setting bit 7 (using the XOR-variant of the register address) will advance the counter of the corresponding channel. The counter wrap-around can be used to trigger a DREQ event, which amounts to one, at most, 4 byte transfer by the attached DMA channel. To move more bytes you will need to use this 4-byte transfer to trigger another DMA channel to accomplish the actual transfer. Additionally, if the code needs to work on the RP2040, this secondary DMA channel needs to re-trigger the first DMA channel, since otherwise the fist channel can only handle a finite number of requests (the initial transfer count) - on the RP2350 the count can be infinite.

1012

simont153
Posts: 4
Joined: Wed Jul 09, 2025 4:11 pm

Re: Software triggered PWM

Sun Jul 13, 2025 6:16 pm

Thank you, this is almost exactly what I was looking for.

I have tried to implement it however I did not find a way to completely isolate the PWM channel from system clock and have a single phase advance increment the counter.

Would you run the channel with EN bit off to gate it from the rest of the chip? My cursory attempt at this, and setting the divider to 1 did not produce expected results.

I have achieved a similar result with a short PIO program (using FIFO pushes and pulls to signal processor and DMA). It did require many DMA channels to configure and do the actual transfer.

gmx
Posts: 1848
Joined: Thu Aug 29, 2024 8:51 pm

Re: Software triggered PWM

Sun Jul 13, 2025 9:42 pm

So, all that you want is to increment PWM counter from software?
I can try some tricks from debugger.

You can also use inline assembler, a processor register, interpolator...

Still don't understand why you are trying to use such an intricate way to trigger the DMA.

1012
Posts: 3
Joined: Fri Jul 11, 2025 10:03 am

Re: Software triggered PWM

Sun Jul 13, 2025 11:58 pm

simont153 wrote:
Sun Jul 13, 2025 6:16 pm
Thank you, this is almost exactly what I was looking for.

I have tried to implement it however I did not find a way to completely isolate the PWM channel from system clock and have a single phase advance increment the counter.

Would you run the channel with EN bit off to gate it from the rest of the chip? My cursory attempt at this, and setting the divider to 1 did not produce expected results.

I have achieved a similar result with a short PIO program (using FIFO pushes and pulls to signal processor and DMA). It did require many DMA channels to configure and do the actual transfer.
It seems that PH_ADV is only useful if you already have a controlled input pulse since it only affects the behavior of the clock divider and does not explicitly generate counts.

However, going back to your original idea about driving pin B of the PWM slice from another pin by an external connection - that should not be necessary since you can instead drive the state of the pin directly by using the GPIO register GPIOx_CTRL. Specifically, in the register GPIOx_CTRL the input state of pin 'x' as seen by the peripheral (PWM) can be assigned by bits 16:17 (INOVER) to 0x0 or 'NORMAL' (PWM input from physical pin), to 0x1 or 'INVERT' (PWM input inverse of physical pin), to 0x2 (PWM input LOW) or to 0x3 (PWM input HIGH). Hence, you should get what you want if you can sacrifice one GPIO pin (one of the two assigned to pin B of the PWM) and two GPIO register accesses for the pin B toggle (set bit 16 for LOW->HIGH, clear bit 16 for HIGH->LOW) - or if your counts are always even you can halve the count and get away with one GPIO register access (xor 1 to bit 16) - in all cases bit 17 must be set beforehand to enable the 'explicit mode'.

1012

Tharre
Posts: 93
Joined: Mon Mar 17, 2025 5:26 pm

Re: Software triggered PWM

Mon Jul 14, 2025 8:11 pm

simont153 wrote:
Fri Jul 11, 2025 3:23 am

Code: Select all

int counter = 0;
while (true) {
	// some DSP algorithm that executes in roughly 300ns
	do_tight_loop();
	
	if (++counter > N) {
		dma_channel_start(chan_num);
		counter = 0
	}
}	
I am trying to optimize out the counter increment, dma software trigger and counter reset. I know it may seem silly but looking at the assembly it represents around 10 instructions which is a significant percentage of the work done in my tight loop.

I am seeking a way to increment some hardware register that will automatically trigger a DMA and reset itself when it reaches a predefined count.
I'm still not sure if I understand your problem. Does your "do_tight_loop()" run in variable time and that's why you're trying to manually increment the PWM counter?

If that's the case the only option you have AFAIK is using a PIO state machine and 3 (2 for the RP2350) DMA channels. In your while loop you'd then write a (arbitrary) value to the state machine FIFO taking 1 instruction and 2 clock cycles assuming no bus contention. The PIOs are on the AHB-Lite splitter together with USB on the RP2040 so that should be manageable.

The PIO code should look something like this:

Code: Select all

.wrap_target
	mov x, y ; reload counter
counter_loop:
	pull block
	jmp x-- counter_loop
	in NULL, 32
.wrap
One DMA channel reads from the PIO FIFO, and is configured to only read one word and chain into channel 2, which transfers the actually useful data. Once channel 2 is done it chains into channel 3 which reconfigures the other two and everything is ready for the next round.

gmx
Posts: 1848
Joined: Thu Aug 29, 2024 8:51 pm

Re: Software triggered PWM

Mon Jul 14, 2025 10:01 pm

Tharre wrote: I am trying to optimize out the counter increment, dma software trigger and counter reset. I know it may seem silly but looking at the assembly it represents around 10 instructions which is a significant percentage of the work done in my tight loop.
How does it look?
How many loops you need for a DMA trigger?

PicoTinker
Posts: 110
Joined: Sun Mar 07, 2021 3:52 am

Pacing (X/Y) fractional timer

Sat Nov 01, 2025 6:43 am

gmx wrote:
Thu Jul 10, 2025 1:03 am
For pacing trigger, there are 4 dedicated DMA timers.

How do they actually work? Initial X/Y values are 0/0: Does this mean stop?
Is the accumulator cleared when the register is written or is the phase arbitrary?
If not cleared, could something like 0/1 for X/Y drain the accumulator to 0, and it would start with a specific phase for next X > 0?
https://rpltd.co/rp2040-datasheet wrote: DMA: TIMER0, TIMER1, TIMER2, TIMER3 Registers

Pacing (X/Y) fractional timer
The pacing timer produces TREQ assertions at a rate set by ((X/Y) * sys_clk). This equation is evaluated every
sys_clk cycles and therefore can only generate TREQs at a rate of 1 per sys_clk (i.e. permanent TREQ) or less.

Code: Select all

Bits Description Type Reset
31:16 X: Pacing Timer Dividend. Specifies the X value for the (X/Y) fractional timer. RW 0x0000
15:0 Y: Pacing Timer Divisor. Specifies the Y value for the (X/Y) fractional timer. RW 0x0000

Could they maybe be implemented somewhat like:

Code: Select all

// called at rate of clk_sys; returns TREQ
bool divide(uint16_t x, uint16_t y) {
 static uint16_t acc = 0;
 acc += x;
 if (acc >= y) {
 acc -= y;
 return true;
 }
 return false;
}
The other fractional dividers (like PIO clock dividers and clock generator dividers) look more like:

Code: Select all

// similar to https://github.com/Wren6991/libfpga/blob/master/common/clkdiv_frac.v
// integer + fractional divider with 1st-order delta sigma pulse swallowing
bool divide(bool enable, bool divider_restart, uint32_t divisor) {
 divisor &= ~0xff; // truncate to 16.8 fixed-point
 static uint32_t counter = 0;
 const uint32_t one = 1 << 16; // 1.0 in 16.16 fixed-point
 if (divider_restart) {
 counter = 0;
 return false;
 }
 if (counter >= one) {
 counter -= one;
 return false;
 } else {
 counter -= one;
 counter += divisor;
 return enable;
 }
}

gmx
Posts: 1848
Joined: Thu Aug 29, 2024 8:51 pm

Re: Software triggered PWM

Sat Nov 01, 2025 4:38 pm

PicoTinker wrote: gmx wrote: ↑
10 Jul 2025, 02:03
For pacing trigger, there are 4 dedicated DMA timers.


How do they actually work? Initial X/Y values are 0/0: Does this mean stop?
Is the accumulator cleared when the register is written or is the phase arbitrary?
If not cleared, could something like 0/1 for X/Y drain the accumulator to 0, and it would start with a specific phase for next X > 0?
I think it's controlled from control registers/aliases.
I don't know if the counter/timers are reset.
22:17 TREQ_SEL: Select a Transfer Request signal.
The channel uses the transfer request signal to pace its data transfer rate.
Sources for TREQ signals are internal (TIMERS) or external (DREQ, a Data
Request from the system).
0x0 to 0x3a → select DREQ n as TREQ
RW 0x00
Enumerated values:
0x3b → TIMER0: Select Timer 0 as TREQ
0x3c → TIMER1: Select Timer 1 as TREQ
0x3d → TIMER2: Select Timer 2 as TREQ (Optional)
0x3e → TIMER3: Select Timer 3 as TREQ (Optional)
0x3f → PERMANENT: Permanent request, for unpaced transfers.

simont153
Posts: 4
Joined: Wed Jul 09, 2025 4:11 pm

Re: Software triggered PWM

Mon Nov 03, 2025 3:04 pm

Thank you all for your inputs. I forgot to close out this thread with the solution that I ended up using.

I ended up having my software loop push a dummy value to a PIO state machine that kept track of the number of pushes on its TX FIFO. When the required number was counted it would push a value to the RX FIFO. A DMA channel was setup to clear the RX FIFO and set off a chain of other DMAs that both configured and fired the actual DMA that my software required.

13 posts • Page 1 of 1

Return to "SDK"

AltStyle によって変換されたページ (->オリジナル) /