18 channel pwm via PRU remoteproc

I have an 18 channel pwm working via the PRUs and remoteproc[1]. It turns out it isn’t too hard using remoteproc to run the PRUs. PRU 0 runs 6 channels and PUR 1 runs 12. You could get even more channels if you disable the eMMC.

The PRUs used INITC to synchronize to each other so the pulses all start at the same it if their periods are properly related.

I’m open for feedback on whether I’ve implemented it correctly.

My next project is writing the shared RAM from the kernel so I can write a proper kernel driver…

–Mark
[1] BeagleBoard-exercises/pru/examples at master · MarkAYoder/BeagleBoard-exercises · GitHub

Honestly after looking at that code mess, I think I’d rather stick with ASM on the PRU.

Wheres the program flow ?

Here is my more “Politically correct” response. . .

First off let me say thank you for sharing what you’re doing. I really mean this, and I know it’s not easy Teaching. Here is my contention with the code:

The reason to use C over Assembly is for readability. Meaning, C as a programming is much easier for the human brain to parse than Assembly. So when the code is not readable because it is not really C, but abstracted assembly . . . you may as well call the code what it is. Assembly. So when I look at something like this:

https://github.com/MarkAYoder/BeagleBoard-exercises/blob/master/pru/examples/pwm/main_pru0.c#L54-#L62

This may as well be another programming language I know nothing about. Because all that is happening here is that fields( struct members ) are being assigned values. Also these fields are not named well, so I have no idea how the code is working. I suspect here . . . whoever is implementing the remoteproc framework for the beaglebone is responsible for that. This:

CT_INTC.GER = 1; // Globally enable host interrupts

In my opinion is completely wrong. Naming wise. What the Hell is a GER ? Going from other code for different processors from TI this should have been GIE ( Global Interrupt Enable ). And that’s part of the problem. Inconsistency.

The comments do help a little, but are not enough. I think it would have been more useful to explain what the field actually is, rather than saying what the code is doing.

Anyway, you’re doing, and sharing with the public what you’ve done. So, that’s more than most have done, especially including myself where this is concerned. Thank you.

William:
Indeed I’d much rather work with C, but the code I was given to start with was assembler written for pasm. My first task was to make it work with clpru, which I just reported on. I would like to port it to C, but I think I’ll loose too much performance. On one PRU I have 12 pwm channels with independent duty_cycle and periods. The shortest period is 1 us. I don’t think I can get such a short period with C.

Here is my more “Politically correct” response. . .

First off let me say thank you for sharing what you’re doing. I really mean this, and I know it’s not easy Teaching. Here is my contention with the code:

The reason to use C over Assembly is for readability. Meaning, C as a programming is much easier for the human brain to parse than Assembly. So when the code is not readable because it is not really C, but abstracted assembly . . . you may as well call the code what it is. Assembly. So when I look at something like this:

https://github.com/MarkAYoder/BeagleBoard-exercises/blob/master/pru/examples/pwm/main_pru0.c#L54-#L62

This C came from another source. The comments helped me understand it. There is a mapping from the cryptic names in the code to the register names used in the technical reference manual (all 5000 pages!).

This may as well be another programming language I know nothing about. Because all that is happening here is that fields( struct members ) are being assigned values. Also these fields are not named well, so I have no idea how the code is working. I suspect here . . . whoever is implementing the remoteproc framework for the beaglebone is responsible for that. This:

CT_INTC.GER = 1; // Globally enable host interrupts

In my opinion is completely wrong. Naming wise. What the Hell is a GER ? Going from other code for different processors from TI this should have been GIE ( Global Interrupt Enable ). And that’s part of the problem. Inconsistency.

Page 342 of the TRM tells what the GER is. It makes sense to use the same names as the TRM, but there are sooooo many registers to lean and understand.

William:
Indeed I’d much rather work with C, but the code I was given to start with was assembler written for pasm. My first task was to make it work with clpru, which I just reported on. I would like to port it to C, but I think I’ll loose too much performance. On one PRU I have 12 pwm channels with independent duty_cycle and periods. The shortest period is 1 us. I don’t think I can get such a short period with C.

Hi Mark,

Ok, knowing assembly better than C is certainly a valid point. However, I believe someone has done you a disservice in letting you believe that C is going to be slower than Assembly. For sure, different compilers are going to optimize code differently, as well as writing code specifically in a certain way or manner. But knowing your compiler well will most certainly benefit you, and the code you write.

What you’re talking about here I believe is going to be in how your write your code, and not what language you use. However, I will mention that if you need every ounce of performance. Using remoteproc might not be the best option right now.

Page 342 in the TRM that I have has nothing about ‘GER’ and in fact is related to GPMC set memory access topics. But this was not a complaint about your code anyhow. It was a complaint against TI’s naming conventions, and the inconsistencies I’ve come to know when working with their literature.

But my main point was that I’d rather use the PRU assembler as at least I would know what’s going on with that code. I still believe that. For me it is really important that C be clear, and perfectly readable. Otherwise, what’s the point ?