Skip to content

Commit 017231e

Browse files
committed
Add PRU backend for stock mainline-kernel images: PRU_BACKEND=genirq.
Recent BeagleBone Debian images ship neither uio_pruss nor PRU rpmsg support, leaving BeagleG without a way to run there. The genirq backend needs no PRU kernel driver: a small device-tree overlay binds the PRU to the kernel uio_pdrv_genirq driver, exposing the PRU0 memories as the UIO device mmap regions and the completion interrupt (PRU system event 19, routed with the same event->channel->host INTC mapping that prussdrv programs) as its blocking read. The host resets the core, copies the pasm-assembled firmware into IRAM through that mapping (with a readback check: IRAM writes are silently dropped on a core that is not freshly reset) and enables it. Everything goes through one /dev/uioN node, so its permissions govern PRU access; no /dev/mem needed. The overlay also disables the PRU0 remoteproc node while loaded: pru_rproc and this backend would otherwise be two independent writers of the same IRAM and control registers. Loaded through the uboot_overlay_pru slot -- which u-boot applies after the generic cape slots -- the lockout stays the effective last word; PRU1 remains available to remoteproc. All magic addresses and register bits are cited against the AM335x TRM (SPRUH73, tables verified in revision Q), the interrupt cells against the ti,pruss-intc binding, and the UIO mmap region convention against the kernel uio-howto. Firmware and default build are unchanged: same motor-interface-pru.p, same pasm flow; the uio_pruss backend stays the default. Verified end-to-end on a factory BeagleBone Black Rev D on both kernel flavors, same binary and overlay: mainline 6.12.28-bone25 (1.72s wall, 2404Hz average step rate, ~4000 register-sampled steps on a two-move G-code smoke test) and TI 5.10.168-ti-r84 (1.73s, 2410Hz); with the overlay loaded, /sys/class/remoteproc lists only wkup_m3 and PRU1 on both. Earlier oscilloscope checks confirmed the pulse timing of the same firmware/queue path.
1 parent d19aed0 commit 017231e

8 files changed

Lines changed: 474 additions & 4 deletions

File tree

.github/workflows/ci.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,33 @@ jobs:
5252
run: |
5353
make valgrind-test
5454
55+
build-genirq-backend:
56+
runs-on: ubuntu-latest
57+
name: Build PRU_BACKEND=genirq
58+
steps:
59+
- name: Prepare
60+
run: |
61+
sudo apt-get update -qq
62+
sudo apt install -y g++ clang pkg-config libgtest-dev libgmock-dev
63+
64+
- uses: actions/checkout@v7
65+
with:
66+
submodules: recursive
67+
68+
- name: Configure shell
69+
run: |
70+
echo "ARM_COMPILE_FLAGS=" >> $GITHUB_ENV
71+
echo "BEAGLEG_OPT_CFLAGS=-O2 -Werror" >> $GITHUB_ENV
72+
73+
- name: Build with g++
74+
run: |
75+
make -C src PRU_BACKEND=genirq ../machine-control
76+
77+
- name: Build with clang++
78+
run: |
79+
make -C src clean
80+
CXX=clang++ make -C src PRU_BACKEND=genirq ../machine-control
81+
5582
build-and-test_cpp11:
5683
runs-on: ubuntu-latest
5784
strategy:

INSTALL.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,60 @@ look like this:
4848
uboot_overlay_pru=/lib/firmware/AM335X-PRU-UIO-00A0.dtbo
4949
```
5050

51+
### Alternative: images without uio_pruss (PRU_BACKEND=genirq)
52+
53+
Recent BeagleBone Debian images (mainline kernel) ship neither the
54+
`uio_pruss` driver nor the `PRU-UIO` overlay above. There, build
55+
BeagleG with `make PRU_BACKEND=genirq`: it needs no PRU kernel driver
56+
at all — the PRU memories and the motion interrupt are exposed to
57+
BeagleG by the kernel's generic UIO driver, so PRU access is governed
58+
by the permissions of `/dev/uio0` alone.
59+
60+
Which backend do I need? The kernel flavor suffix alone does not tell
61+
you. The `uio_pruss` driver was removed from mainline Linux in 2024
62+
("uio: pruss: Remove this driver"), so kernel series 6.12 and newer
63+
lack it on either flavor, while series branched earlier still ship it
64+
(TI kernels up to 5.10, `-bone` kernels up to 6.6). Check your kernel
65+
directly:
66+
67+
```
68+
ls /lib/modules/$(uname -r)/kernel/drivers/uio/
69+
```
70+
71+
If `uio_pruss.ko*` is listed, the default `uio` backend and the
72+
`PRU-UIO` overlay above work. If it is not — e.g. the 6.12 kernels of
73+
current images — use `genirq`; it works on any kernel >= 5.10, either
74+
flavor.
75+
76+
One-time setup: compile the overlay that exports the PRU to userspace
77+
78+
```
79+
sudo apt install device-tree-compiler
80+
sudo dtc -@ -o /lib/firmware/BEAGLEG-PRU-IRQ.dtbo dts/BEAGLEG-PRU-IRQ.dts
81+
```
82+
83+
then in `/boot/uEnv.txt` enable u-boot overlays, select ours, and
84+
allow `uio_pdrv_genirq` to bind to it (append to the existing
85+
`cmdline=` line):
86+
87+
```
88+
enable_uboot_overlays=1
89+
uboot_overlay_pru=/lib/firmware/BEAGLEG-PRU-IRQ.dtbo
90+
cmdline=coherent_pool=1M net.ifnames=0 quiet uio_pdrv_genirq.of_id=generic-uio
91+
```
92+
93+
Use the `uboot_overlay_pru` slot specifically, not a generic
94+
`uboot_overlay_addrN` one: overlays merge last-writer-wins, and
95+
u-boot applies the PRU slot after all the generic cape slots, so this
96+
overlay's claim on PRU0 (it disables the PRU0 remoteproc node so the
97+
kernel and BeagleG never fight over the core) cannot be silently
98+
overridden by a cape overlay.
99+
100+
After a reboot, `cat /sys/class/uio/uio0/name` should print
101+
`beagleg_pru_irq` (newer kernels append the node's `@4a300000`
102+
unit-address). BeagleG needs read/write access to that `/dev/uio0`
103+
device node.
104+
51105
## Install BeagleG
52106

53107

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ _enable_ the line containing the `PRU-UIO` (remove `#` in front).
5858
uboot_overlay_pru=/lib/firmware/AM335X-PRU-UIO-00A0.dtbo
5959
```
6060

61+
Note: current images no longer ship the `uio_pruss` driver this setup
62+
relies on. On those, build with the `genirq` backend instead — see
63+
the "images without uio_pruss" section in [INSTALL.md](./INSTALL.md).
64+
6165
Reboot.
6266

6367
### Enable Output Pins for your board

dts/BEAGLEG-PRU-IRQ.dts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/* BeagleG: expose the PRU to userspace as /dev/uioN via the kernel's
2+
* generic uio_pdrv_genirq driver -- no PRU-specific kernel driver
3+
* needed. Two things are wired up:
4+
*
5+
* - interrupts: PRU system event 19, raised by the firmware when a
6+
* motion segment completes (must match PRU0_ARM_INTERRUPT in
7+
* src/motor-interface-pru.p), routed through the PRUSS interrupt
8+
* controller as event 19 -> INTC channel 2 -> host interrupt 2.
9+
* This is the same routing libprussdrv's PRUSS_INTC_INITDATA
10+
* programs for the uio_pruss backend. Cell meaning per the binding
11+
* https://www.kernel.org/doc/Documentation/devicetree/bindings/interrupt-controller/ti,pruss-intc.yaml
12+
* - reg: the PRU0 memories, exported as UIO mmap regions in
13+
* declaration order: map 0 = PRU0 data RAM, map 1 = PRU0 control,
14+
* map 2 = PRU0 instruction RAM. This order is the backend's mmap
15+
* contract (src/uio-genirq-interface.cc); the offset-selects-region
16+
* mmap convention is
17+
* https://www.kernel.org/doc/html/latest/driver-api/uio-howto.html
18+
*
19+
* The addresses come from the AM335x Technical Reference Manual, TI
20+
* document SPRUH73 (page anchors per revision Q): the PRU-ICSS sits
21+
* on the L4_FAST interconnect, occupying 0x4a30_0000..0x4a37_ffff --
22+
* Table 2-4 "L4 Fast Peripheral Memory Map",
23+
* https://www.ti.com/lit/pdf/spruh73#page=185 -- and within it PRU0
24+
* data RAM is at offset 0x00000 (8 KiB), the PRU0 control registers
25+
* at 0x22000 and PRU0 instruction RAM at 0x34000 (8 KiB) -- section
26+
* 4.3.2, Table 4-8 "Global Memory Map",
27+
* https://www.ti.com/lit/pdf/spruh73#page=207. The kernel's hardware
28+
* description carries the same values in
29+
* arch/arm/boot/dts/ti/omap/am33xx.dtsi (pruss node).
30+
*
31+
* Needs uio_pdrv_genirq.of_id=generic-uio on the kernel command line;
32+
* see INSTALL.md for the full setup.
33+
*/
34+
/dts-v1/;
35+
/plugin/;
36+
37+
/ {
38+
/* This overlay depends only on the AM335x chip (its PRU-ICSS),
39+
* not on any board, so it declares the chip-level compatible.
40+
* Boards list their identity from most to least specific -- the
41+
* BeagleBone Green, for example: "ti,am335x-bone-green",
42+
* "ti,am335x-bone-black", "ti,am335x-bone", "ti,am33xx" -- and
43+
* a match on any entry suffices. This single generic string
44+
* therefore covers every AM335x board (Black, Green, Green
45+
* Wireless, PocketBeagle, ...), including boards that do not
46+
* exist yet, while an explicit board list would only assert a
47+
* board dependence this overlay does not have.
48+
*/
49+
compatible = "ti,am33xx";
50+
51+
fragment@0 {
52+
target-path = "/";
53+
__overlay__ {
54+
#address-cells = <1>;
55+
#size-cells = <1>;
56+
beagleg_pru_irq@4a300000 {
57+
compatible = "generic-uio";
58+
reg = <0x4a300000 0x2000>, /* PRU0 data RAM */
59+
<0x4a322000 0x1000>, /* PRU0 control */
60+
<0x4a334000 0x2000>; /* PRU0 instruction RAM */
61+
interrupt-parent = <&pruss_intc>;
62+
interrupts = <19 2 2>; /* sysevent 19, channel 2, host 2 */
63+
};
64+
};
65+
};
66+
67+
/* BeagleG owns PRU0, so keep the kernel's pru_rproc from binding
68+
* it: with two independent writers of the same IRAM/control
69+
* registers, a remoteproc "start" while BeagleG runs would race.
70+
* Same mutual exclusion the historical PRU-UIO overlays used;
71+
* PRU1 is left available to remoteproc.
72+
*
73+
* Ordering matters: overlays merge last-writer-wins with no
74+
* conflict detection, so an overlay applied after this one that
75+
* touches &pru0 would silently undo the lockout. Load this file
76+
* through the uboot_overlay_pru slot of /boot/uEnv.txt -- u-boot
77+
* applies that slot after all generic uboot_overlay_addrN cape
78+
* slots (bb.org u-boot, include/configs/ti_armv7_common.h), so
79+
* the PRU0 claim stays the effective last word -- and keep
80+
* PRU-touching overlays out of the even-later slots
81+
* (uboot_overlay_pru_add, dtb_overlay). All merging happens in
82+
* u-boot before Linux boots, so pru_rproc never probes PRU0; the
83+
* result is verifiable at runtime:
84+
* /proc/device-tree/ocp/interconnect@4a000000/segment@0/
85+
* target-module@300000/pruss@0/pru@34000/status == "disabled".
86+
*/
87+
fragment@1 {
88+
target = <&pru0>;
89+
__overlay__ {
90+
status = "disabled";
91+
};
92+
};
93+
};

src/Makefile

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,23 @@ PRUSS_LIBS=$(LIBDIR_APP_LOADER)/libprussdrv.a
5454
COMMON_LIBS=common/libbeaglegbase.a gcode-parser/libgcodeparser.a
5555
SUBDIRS=common gcode-parser
5656

57+
# Host-side PRU backend. "uio" talks to the legacy uio_pruss driver via
58+
# libprussdrv (needs the PRU-UIO device-tree overlay). "genirq" needs
59+
# no PRU-specific kernel driver: it drives the PRU through the memory
60+
# maps of a generic UIO device and gets completion interrupts through
61+
# its blocking read -- uio_pdrv_genirq, wired up by the overlay in
62+
# ../dts/, see INSTALL.md; use it on stock mainline-kernel BeagleBone
63+
# Debian images.
64+
PRU_BACKEND?=uio
65+
ifeq ($(PRU_BACKEND),genirq)
66+
PRU_BACKEND_OBJ=uio-genirq-interface.o
67+
PRU_BACKEND_LIBS=
68+
CFLAGS+=-DBEAGLEG_PRU_BACKEND_GENIRQ
69+
else
70+
PRU_BACKEND_OBJ=uio-pruss-interface.o
71+
PRU_BACKEND_LIBS=$(PRUSS_LIBS)
72+
endif
73+
5774
# Assembled binary from *.p file.
5875
PRU_BIN=motor-interface-pru_bin.h
5976

@@ -62,7 +79,7 @@ GCODE_OBJECTS=gcode-machine-control.o determine-print-stats.o \
6279
generic-gpio.o pwm-timer.o config-parser.o \
6380
machine-control-config.o hardware-mapping.o \
6481
spindle-control.o planner.o adc.o
65-
OBJECTS=motion-queue-motor-operations.o sim-firmware.o sim-audio-out.o pru-motion-queue.o uio-pruss-interface.o $(GCODE_OBJECTS)
82+
OBJECTS=motion-queue-motor-operations.o sim-firmware.o sim-audio-out.o pru-motion-queue.o $(PRU_BACKEND_OBJ) $(GCODE_OBJECTS)
6683
MAIN_OBJECTS=machine-control.o gcode-print-stats.o gcode2ps.o
6784

6885
TARGETS=../machine-control ../gcode-print-stats gcode2ps
@@ -82,7 +99,7 @@ gcode-parser/libgcodeparser.a: FORCE
8299
$(CROSS_COMPILE)$(CXX) -o $@ $^ $(COMMON_LIBS) $(LDFLAGS)
83100

84101
../machine-control: machine-control.o $(OBJECTS) $(COMMON_LIBS)
85-
$(CROSS_COMPILE)$(CXX) -o $@ $^ $(COMMON_LIBS) $(PRUSS_LIBS) $(LDFLAGS)
102+
$(CROSS_COMPILE)$(CXX) -o $@ $^ $(COMMON_LIBS) $(PRU_BACKEND_LIBS) $(LDFLAGS)
86103

87104
# While this is developed and does not have a final name yet, let's not make
88105
# it a toplevel tool in ../
@@ -122,7 +139,7 @@ $(PRU_BIN) : motor-interface-constants.h \
122139
$(CAPE_INCLUDE)/pru-io-routines.hp
123140

124141
%_test: %_test.o $(OBJECTS) $(COMMON_LIBS) compiler-flags
125-
$(CROSS_COMPILE)$(CXX) -o $@ $< $(OBJECTS) $(COMMON_LIBS) $(PRUSS_LIBS) $(GTEST_LIBS) $(LDFLAGS)
142+
$(CROSS_COMPILE)$(CXX) -o $@ $< $(OBJECTS) $(COMMON_LIBS) $(PRU_BACKEND_LIBS) $(GTEST_LIBS) $(LDFLAGS)
126143

127144
%.o: %.cc compiler-flags
128145
$(CROSS_COMPILE)$(CXX) $(CXXFLAGS) -c $< -o $@
@@ -144,6 +161,7 @@ $(PASM):
144161

145162
# Explicit dependencies
146163
uio-pruss-interface.o : $(PRU_BIN)
164+
uio-genirq-interface.o : $(PRU_BIN)
147165

148166
# Auto generated dependencies
149167
-include $(DEPENDENCY_RULES)

src/machine-control.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,11 @@ int main(int argc, char *argv[]) {
578578
"Use the dryrun option -n to not write to GPIO).");
579579
return 1;
580580
}
581+
#ifdef BEAGLEG_PRU_BACKEND_GENIRQ
582+
pru_hw_interface = new UioGenirqInterface();
583+
#else
581584
pru_hw_interface = new UioPrussInterface();
585+
#endif
582586
motion_backend = new PRUMotionQueue(&hardware_mapping, pru_hw_interface);
583587
}
584588

src/pru-hardware-interface.h

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#define BEAGLEG_PRU_HARDWARE_INTERFACE_
2222

2323
#include <cstddef>
24+
#include <cstdint>
2425

2526
// Pru hardware controls
2627
class PruHardwareInterface {
@@ -44,7 +45,11 @@ class PruHardwareInterface {
4445
virtual bool Shutdown() = 0;
4546
};
4647

47-
class UioPrussInterface : public PruHardwareInterface {
48+
// PRU control via the uio_pruss kernel driver and TI's libprussdrv:
49+
// the historical BeagleG setup, for TI-flavor kernels booted with the
50+
// PRU-UIO device-tree overlay (see INSTALL.md, "Enable PRU"). Default
51+
// backend, PRU_BACKEND=uio.
52+
class UioPrussInterface final : public PruHardwareInterface {
4853
public:
4954
bool Init() final;
5055
bool AllocateSharedMem(void **pru_mmap, size_t size) final;
@@ -53,4 +58,42 @@ class UioPrussInterface : public PruHardwareInterface {
5358
bool Shutdown() final;
5459
};
5560

61+
// PRU hardware control without any PRU-specific kernel driver, for
62+
// kernels >= 5.10 that carry the pruss interrupt-controller driver --
63+
// notably the mainline-flavor kernels of current BeagleBone images,
64+
// which ship neither uio_pruss nor PRU rpmsg. Firmware load, start/stop
65+
// and the shared motion queue go through the memory maps of a generic
66+
// UIO device, per-segment completion interrupts through its blocking
67+
// read, all wired up by the device-tree overlay in
68+
// dts/BEAGLEG-PRU-IRQ.dts (see INSTALL.md). PRU_BACKEND=genirq.
69+
class UioGenirqInterface final : public PruHardwareInterface {
70+
public:
71+
UioGenirqInterface();
72+
~UioGenirqInterface() final;
73+
74+
bool Init() final;
75+
bool AllocateSharedMem(void **pru_mmap, size_t size) final;
76+
bool StartExecution() final;
77+
unsigned WaitEvent() final;
78+
bool Shutdown() final;
79+
80+
private:
81+
// Volatile is applied at the dereference points, not the storage:
82+
// ctrl_ is MMIO, so every access must really happen and in order.
83+
// The queue memory is only memset here before the PRU starts, then
84+
// handed out via AllocateSharedMem(); the consumer accesses it
85+
// through the volatile PRUCommunication fields (pru-motion-queue.cc)
86+
// while the PRU concurrently writes it. iram_ is bulk-copied
87+
// (memcpy cannot take volatile) only while the core is halted after
88+
// the reset pulse, so it never has a concurrent accessor. The uio
89+
// mappings
90+
// are non-cached device memory, so the CPU preserves the access
91+
// order the compiler emits -- the same contract the uio_pruss
92+
// mapping always provided.
93+
int uio_fd_;
94+
void *data_ram_; // Motion queue, shared with the PRU.
95+
volatile uint32_t *ctrl_; // PRU0 control register.
96+
void *iram_; // PRU0 instruction RAM.
97+
};
98+
5699
#endif // BEAGLEG_PRU_HARDWARE_INTERFACE_

0 commit comments

Comments
 (0)