Setting ZEROCMP bit on pwm

I am having trouble setting the PWMZEROCMP bit (bit 9 of pwmcfg) on the hifive unmatched board on both PWMs. I have a simple userspace program that does the following:

pwmcfg = 0xf // I am only setting scale
while pwmzerocmp is not set:
    pwmcfg |= pwmzerocmp bit
    wait a few cycles
    if this is iteration is a multiple of 50, print out pwmcfg to see if pwmzerocmp has been set

Pwmcfg stays at 0xf the first couple thousand times the loop is run (it is an infinite loop). This is not what I would expect: the u74 manual says that this bit is writable. So I would have expected it to be set the first iteration of the loop, and then a break to occur.

However, I just let the loop spin. Eventually, pwmcfg becomes 0x40f and stays there for a few thousand iterations. I never set the deglitch bit.

If I continue to run this loop, all four IP bits get set after a few thousand more iterations. PWMENALWAYS and PWMENONESHOT are both unset, so I am not sure how IP is triggered.

Any advice/thoughts on what I’m missing would be appreciated!

–Justin

are you trying to PWM some fans? I would certainly like to see that.

Hi Justin @jrestivo, welcome!

I don’t understand the loop, it shouldn’t be necessary. Here is a common setup paradigm that works for me, albeit on the FE310 SoC (peripheral operates the same, though).

Stop everything for the setup:
pwmcfg = 0

Reset count value to known starting point, need not be zero but should be less than the CMP0 value you select below, if ZEROCMP bit will be set:
pwmcount = 0

Define period and duty cycle – note that CMP0GPIO output will be only a tiny pulse a few nS wide, best results to use only outputs 1, 2, 3 and leave 0 to internally determine the repetition rate (i.e., period); of course, could employ GLITCH and STICKY mechanisms but they require active intervention as with interrupt servicing to manually clear the STICKY set stuck condition when it occurs.
pwmcmp0 = 2048 (for example)
pwmcmp1 = 1024 (for example, for 50% duty cycle)
pwmcmp2 = 1024 (optional, if you’re using output #2)
pwmcmp3 = 1024 (optional, if you’re using output #3)

Start it up:
pwmcfg |= (PWMZEROCMP | PWMENALWAYS | pwmscale)
where in your case pwmscale is 0xF (pwmcfg bits 0-3 only). This statement construction insures that the configuration is done atomically, in one transaction, eliminating any race conditions.

Why do you need to use the prescaler? What is your hfclk rate, very high?

TIP: without use of the STICKY feature, you can always synchronize in real time to a particular point in the repetition cycle by polling on one of the outputs’ PWMCMPxIP bits, where x is 1, 2, or 3 as desired.

Lastly, don’t forget to switch over the GPIO pin(s) to their other IO Fuction equivalents when you are ready to use them externally. Simply set (H for pwm, L for spi, i2c, uart, etc) the corresponding bits in the IOF_SEL register, and do same for the IOF_EN register (L for gpio, H for iof as set by iof_sel). These two registers might not be well documented; on the FE310 SoC they are at offsets 0x3C and 0x38, respectively, from the GPIO base address.

There is a sample program for the PWM in freedom-e-sdk. This is
https://github.com/sifive/freedom-e-sdk/blob/master/software/example-pwm/example-pwm.c
This assumes bare metal programming via freedom-e-sdk, but it might give some hints on how the PWM are meant to be used.

You mentioned “userspace program”. If you are running linux, then I’d be surprised if the linux kernel lets a userspace program read/write directly from/to I/O space, unless perhaps you have done something to allow that.

Thank you for your replies!

A bit of context: I am trying to port the SeL4 PWM drivers from the u54 to the u74. The MMIO is mapped such that I can write to it from userspace. Portions of their SeL4 test suite that rely on a timer are failing. My hfclk on the u74 is 26Mhz. As far as I can tell, uboot scales this to 0.519Ghz as pclk (input to the PWM). I end up using the prescaler as I am trying to copy the u54 seL4 drivers which run at 0.5Ghz and use the scaling factor. I think the purpose of the prescaler in this case is to control the interrupt period without also changing the frequency of the other peripherals also connected to pclk like the UARTs.

Thanks for the tips on the polling alternative to the sticky bit and gpio pins. I’m trying to use the PWM peripherals as timers, so I haven’t had chance to touch the gpio pins yet, but I am polling the IP bits as you’ve suggested.

My code before the while loop looks like

pwmcount = 0;
pwmcmp0 = 0xffff;
pwmcmp1 = 0xffff;
pwmcmp2 = 0xffff;
pwmcmp3 = 0xffff;
pwmcfg = 0; // set the scale. Assume 0 for now.
// while loop to try to set PWMZEROCMP

The while loop has the behavior I’ve described above (infinite loop–eventually raising the pwm IP bits).

I’d normally do exactly what you’ve described as far as set up goes, @pds . In fact, this is exactly how things seem to work on the unleashed board with SeL4. However, for the unmatched, the zerocmp bit is never set (and I’ve checked: its functionality is not enabled). The same code does set the bit on the unleashed. I am trying to isolate this error which is why I am not applying any scale or setting the enalways bit. It’s weird to me that this works on the u54. I would expect the u74 to act exactly the same sans it being connected to pclk instead of coreclk/2.

The only place I see the zerocmp bit set in the freedom-u-sdk metal driver is here. Do you know what the goal of this function is? It looks like it starts the timer.

The pwmcfg.pwmzerocmp bit can be written but there is an issue with reading the bit. When reading pwmcfg the value of pwmcfg.pwmdeglitch (bit 10) is returned in pwmcfg.pwmzerocmp (bit 9) field.