Generate low latency single pulse via PWM chip

Hey,
We are using the PWM chip as single pulse generator. At the moment it works but with significant latency as it works via the sysfs interface.

Is it possible to control the PWM chip via Direct Memory Access (DMA)?
or is there any register that is accessible from user space to control the PWM chip?

Hi alon2,

Are you using the devkit or custom board for Orin NX?
What’s your Jetpack version in use?

How’s the latency you see? and what’s your requirement?

Have you tried using jetson-gpio/samples/simple_pwm.py at master · NVIDIA/jetson-gpio (github.com) to verify?

I am part of a team working with the NVIDIA Jetson Orin NX 16GB, equipped with JetPack 5.1.2. on a custom board. Our project demands the generation of precise, single PWM pulses with strict microsecond-level latency, aiming for a frequency of around 100 Hz. The unique challenge we face is the need for each pulse to be a discrete event, initiated by a start-stop-reset cycle of the PWM chip, without transitioning into continuous pulse generation.

Our exploration into available tools revealed the jetson-gpio library, which, while useful, employs the sysfs interface for PWM access. Given the latency introduced by sysfs, this approach falls short of our project’s latency requirements. For GPIO control, we transitioned to using the gpiod library instead of sysfs, which significantly improved our latency. However, for PWM, a similar low-latency alternative remains elusive, leading us to consider direct register access as a potential solution.

We seek the community’s expertise and advice on the following points:

  1. Direct Register Access: Specific instructions and precautions for safely accessing and manipulating PWM chip registers on the Jetson Orin NX 16GB to achieve a start-stop-reset cycle for generating single PWM pulses.
  2. Best Practices: Insights into managing direct register access to maintain system stability and avoid conflicts, especially considering the repetitive nature of the start-stop-reset cycle required for our application.
  3. Alternative Approaches: Documentation, resources, or alternative methods for achieving low-latency, single-pulse PWM control on JetPack 5.1.2 that might not have come to our attention.

We understand the inherent risks of direct hardware manipulation and are committed to proceeding with the utmost caution. Any guidance, shared experiences, or resources from the NVIDIA community would be greatly appreciated.

Thank you for your support and looking forward to the valuable insights this community has to offer!

EDIT

maybe directly enable and disable the following bit?

#EDIT 2
I tried to read/write direcrly the PWM register
but when reading from memory it always returns “0xFFFFFFFF”
sudo busybox devmem 0x3280000

Hi there @alon2

Kind of starting by the end. The result that you are getting after doing a busybox read (or write) is related to the action of the cbb “firewall” fabric, basically there is a controller filtering read/write operations over the CPU Complex registers. At least 2 ways around it:

  • Modify the cbb section of the device tree controlling the pwm registers that you want to manipulate from user space and reflash (kind of a pain) or

  • Enable the pwm clocks that come disabled by default, there are 3 pwm pins available on the Orin NX header (using the dev kit as reference, if your board follows a different pin layout this could change) as follows, straight from my own code:

    • define base_PWM1 0x03280000 // PWM1 Controller base address pin 15

    • define base_PWM5 0x032c0000 // PWM5 Controller base address pin 33

    • define base_PWM7 0x032e0000 // PWM7 Controller base address pin 32

So to avoid this problem you can enable them like:
sudo bash -c ‘echo “1” > /sys/kernel/debug/bpmp/debug/clk/pwm1/state’

Once this is done busybox should start working for register 0x3280000, if it doesn’t you will have to do some changes on the device tree and reflash.

Can you do direct register access from user space? sure you can, mmap() is your friend, for an example of how to go about this business check my library, or just use it? whatever: GitHub - Rubberazer/JETGPIO: C library to manage the GPIO header of the Nvidia Jetson boards

Alternative approaches: not completely sure of what you are trying to achieve, but for a few milliseconds pulse maybe you could go for a POSIX timer? timer_create(2) - Linux manual page so you could set up a pin as output and control the width of the pulse with a timer.

If you already worked with the gpiod framework you can use that or my library or do your own thing mmaping registers, with my lib you can drive an output on the Orin NX with a 2us latency, check this for some stats: GitHub - Rubberazer/JETGPIO: C library to manage the GPIO header of the Nvidia Jetson boards

good luck

For Orin, PWM pulse width minimum is 1/256, where 256 is fixed number at its down counter.
Thus, 1/256 = 0.0039 (~0.4%) is the lowest duty cycle it can get.

For 100Hz, it could result about 40us.

It seems this register is inaccessible. It is determined by the period and duty cycle you configured.

Hey Rubberazer & Kevin

First

Thank you for the comprehensive response! The JETGPIO library seems quite promising.

I have a quick query regarding the PWM chip control. It appears you’re implementing additional PINOUT configurations as shown below:

image
Could you provide some clarification on this?

2nd

Furthermore, we’re working with a custom board and utilizing PWM1. Here’s how PWM is defined in our device tree:

For clarity, I’ve tried writing to the register addresses for all PWMs you mentioned - PWM1, PWM2, and PWM3, using both your code and busybox devmem. I verified with busybox devmem that the values indeed change. Despite this, there’s no observable output from the pins. However, when I employ sysfs to activate PWM1, I do observe a pulse from the pins using a scope.

Any ideas why those memory address of the registers wont effect the PWM chip itself?

Thanks
Alon

@alon2

What you can do is compile one of the examples and check if something is coming out, for instance if you compile & run: JETGPIO/EXAMPLES_C/jetgpio_PWM_example.c at main · Rubberazer/JETGPIO · GitHub

You should get a 10kHz signal at 50% duty cycle at pin 32 in the Orin NX. You can change it to other pins e.g. 15 or 33.

Apart from that, if you try with busybox, there are several steps to follow and the registers have to be manipulated in a certain order, if you look at my code within the orin.c file the functions to check to understand all this are:

gpioSetPWMfrequency()

gpioPWM()

If you check those 2, it should be more clear. My library ignores the device tree and manipulates the pinmux and other registers dynamically, the only thing is not going to do is to manipulate the clocks, this could matter depending on the frequencies you are expecting to achieve.

That memory address is inaccessible.
If you read it, it will show some error messages in kernel dmesg.

Please just use sysfs or the sample in jetson-gpio for PWM.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.