This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

J721E: Unable to enable IEP PWM on PRU

I'm unable to advance the PWM state machine from Initial to Active. Currently, I'm using the following:

#define PWM_PERIOD 0x003D0900u // 20ms @ 200MHz (50Hz)
#define PWM_MAX 0x400u

void pwm_setup(uint16_t duty)
{
	/* Disable counter */
	CT_IEP0.global_cfg_reg_bit.cnt_enable = 0;

	/* Switch to 32-bit mode */
	CT_IEP0.cmp_cfg_reg_bit.shadow_en = 0;
	CT_IEP0.cmp_cfg_reg_bit.shadow_en = 1;

	/* Reset Count register */
	CT_IEP0.count_reg0_bit.count_lo = 0xFFFFFFFF;
	CT_IEP0.count_reg1_bit.count_hi = 0xFFFFFFFF;

	/* Clear overflow status register */
	CT_IEP0.global_status_reg_bit.cnt_ovf = 1;

	/* Clear compare status */
	CT_IEP0.cmp_status_reg_bit.cmp_status = 0xFFFF;

	/* Set compare value */
	CT_IEP0.cmp0_reg1_bit.cmp0_1 = PWM_PERIOD;

	/* Enable CMP0 and reset on event */
	CT_IEP0.cmp_cfg_reg_bit.cmp_en = (1u << 8) | (1u << 0);
	CT_IEP0.cmp_cfg_reg_bit.cmp0_rst_cnt_en = 1;

	/* Set increment value */
	CT_IEP0.global_cfg_reg_bit.default_inc = 1;

	/* Disable compensation */
	CT_IEP0.compen_reg_bit.compen_cnt = 0;

	/* Reset trip */
	CT_CFG_EXT.pwm1_bit.pwm1_trip_reset = 1;

	/* Duty-cycle low then high (Ouput LOW -> PWM HIGH) */
	CT_IEP0.cmp8_reg1_bit.cmp8_1 = (duty * PWM_PERIOD) / PWM_MAX;
	CT_CFG_EXT.pwm1_0_bit.pwm1_0_neg_init = STATE_L;
	CT_CFG_EXT.pwm1_0_bit.pwm1_0_neg_act = STATE_H;
	CT_CFG_EXT.pwm1_0_bit.pwm1_0_neg_trip = STATE_H;

	/* Enable counter */
	CT_IEP0.global_cfg_reg_bit.cnt_enable = 1;
}

void main(void)
{
	volatile uint16_t duty, duty_prev, sw_pwm;

	duty = duty_prev = 0;
	sw_pwm = 0;

	while (1) {
		if (duty != duty_prev) {
			duty_prev = duty;
			cmp0_ovf = cmp8_ovf = 0;
			pwm_set_duty(duty);
		} else {
			uint16_t status = CT_IEP0.cmp_status_reg_bit.cmp_status;
			CT_IEP0.cmp_status_reg_bit.cmp_status = status;
			if (sw_pwm == 0) {
				if (status & (1u << 0)) {
					cmp0_ovf++;
				}
				if (status & (1u << 8)) {
					cmp8_ovf++;
				}
			} else {
				if (duty == 0) {
					CT_CFG_EXT.pwm1_0_bit.pwm1_0_neg_init = STATE_H;
				} else if (duty >= (PWM_MAX - 1)) {
					CT_CFG_EXT.pwm1_0_bit.pwm1_0_neg_init = STATE_L ;
				} else {
					if (status & (1u << 0)) {
						cmp0_ovf++;
						CT_CFG_EXT.pwm1_0_bit.pwm1_0_neg_init = STATE_L;
					}
					if (status & (1u << 8)) {
						cmp8_ovf++;
						CT_CFG_EXT.pwm1_0_bit.pwm1_0_neg_init = STATE_H;
					}
				}
			}
	}
}

Using CCS, I've proven that the pin-mux and register selections are correct:

  • When I set sw_pwm to 1, I get the expected PWM output on the desired pin varying with changes to duty
    • This proves that I have the right pin, the right pinmux and the IEP comparator is working
  • When I set sw_pwm to 0, it appears that pwm1_0_neg is stuck in the Initial state

I can further confirm this by using CCS to modify CT_CFG_EXT.pwm1_0_bit.pwm1_0_neg_init and I see corresponding change at the output pin.

I've also experimented with enabling *ALL* IEP comparators, but that did not help.

Am I missing an enable bit for PWM somewhere? (The am65xx datasheet lists a few additional registers that appear to be missing from the J721e)

EDIT: Add missing pwm1_trip_reset, this was present in my testing, but missed in the above copy/paste

  • Hi Josh,

    As per TRM

    PWM1_0_NEG maps to IEP0 CMP8. Can you check the CMP status for CMP8, when you get stuck ? What does it show ? Check ICSSG_CMP_STATUS_REG register.

    Please also check if IEP is incrementing when you disable sw_pwm mode.

    Regards

    Vineet

  • See lines 62 to 70 in above: My "software" PWM is entirely reliant on IEP CMP0 and CMP8 for timing. I've confirmed the output and timing with a scope: CMP0 overflows every 20ms, while CMP8 overflows at various selected duty cycles.

  • I checked with a team which has written an example ICSSG PWM. Their feedback is that the only way for a group of pins to move from initial to active was for a compare event to occur within that group. I think the grouping is for CMP events 1-7 and 8-15.

    So basically, the first compare event for each group would move the entire group to the active state BUT that first compare event would NOT cause that pin to toggle. You need to create two sacrificial CMP events within these two groups, try CMP2 and CMP9.

    I know that you enabled all comparators but did you check and clear them as well ?

    If even that doesn't work then please look at the ICSSG GPIO example code under pdk_am65xx_1_0_7\packages\ti\drv\pruss\example\apps\icssg_pwm\firmware\src . You will find this when you download the Maxwell RTOS package here

    Regards

    Vineet

  • I had seen the am65x example, but had not realized the j721e and am65x are effectively the same part. Rather than my own quick-hack of firmware, I've gotten the icssg-pwm firmware built and working on the j7 along w/ the matching pwm-pru linux kernel driver.

    Thanks for pointing me back in the right direction!

    For anyone finding this thread, the only real change I need to make in order to the the pwm-pru driver on the j7 was to the assigned clock parent:

    	pru_pwm0 {
    		compatible = "ti,pru-pwm";
    		prus = <&pru0_0>;
    		firmware-name = "ti-pruss/j721e-pru0-pwm-fw.elf";
    		assigned-clocks = <&icssg0_iepclk_mux>;
    		assigned-clock-parents = <&k3_clks 119 3>; /* am65x: <&k3_clks 62 10> */
    		#address-cells = <1>;
    		#size-cells = <0>;
    		... etc

    (and of course the appropriate pin-control, but that is specific to my custom hardware design)

    Otherwise, follow the instructions for building and deploying the icssg_pwm firmware Vineet has pointed to.