Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX GERÇEK ZAMANLI I/O 2026

BeagleBone Black PRU
Gerçek Zamanlı I/O Birimi

BeagleBone Black'in PRU-ICSS alt sistemini kullanarak nanosaniye hassasiyetinde I/O, özel protokol sürücüsü ve remoteproc ile Linux entegrasyonu.

00 PRU nedir?

PRU (Programmable Realtime Unit), Texas Instruments AM335x SoC içinde bulunan bağımsız 32-bit RISC işlemcileridir. Linux tarafından etkilenmeden deterministik, nanosaniye seviyesinde I/O işlemleri yürütürler.

PRU-ICSS Mimarisi

BeagleBone Black'taki AM335x çipi, tek bir PRU-ICSS (Industrial Communication SubSystem) bloğu barındırır. Bu blok iki adet PRU çekirdeği (PRU0 ve PRU1), paylaşımlı bellek, interrupt controller (INTC) ve çeşitli çevre birimlerinden oluşur. Her PRU 32 adet 32-bit genel amaçlı yazmaç içerir ve tek saat döngüsünde bir komut yürütür.

AM335x SoC
├── ARM Cortex-A8 (Linux çalışır)
│   └── remoteproc / rpmsg sürücüleri
└── PRU-ICSS
    ├── PRU0 (200 MHz, 8 KB inst RAM, 8 KB data RAM)
    ├── PRU1 (200 MHz, 8 KB inst RAM, 8 KB data RAM)
    ├── Shared RAM (12 KB)
    ├── INTC (64 sistem olayı, 10 host IRQ)
    ├── IEP (Industrial Ethernet Peripheral — zamanlayıcı)
    └── MII_RT (Ethernet MII arayüzü)
    

200 MHz Sabit Gecikme

PRU, 200 MHz'de çalışır; yani her komut tam olarak 5 ns sürer. Bellek erişimi, I/O toggle ve döngü sayımları tamamen deterministiktir. Linux tarafındaki kesme gecikmesi, zamanlayıcı jiteri veya önbellek ısınması PRU yürütmesini etkilemez. Bu özellik, PRU'yu şu senaryolar için ideal kılar:

Protokol bit-bangingWS2812, DMX-512, 1-Wire gibi kesin zamanlama gerektiren protokoller
Endüstriyel I/OEtherCAT, PROFIBUS, Modbus RTU gibi endüstriyel haberleşme protokolleri
Encoder okumaYüksek frekanslı quadrature encoder sinyallerini kayıpsız sayma
PWM üretimiLinux zamanlayıcısından bağımsız yüksek çözünürlüklü PWM sinyalleri
Gerçek zamanlı veri toplamaSabit örnekleme aralıklarıyla sensör verisi toplamak

ARM ile karşılaştırma

ÖzellikARM Cortex-A8 (Linux)PRU
Frekans1 GHz200 MHz
Zamanlama deterministikliğiYok (OS jitter)Tam (tek döngü kesin)
Minimum GPIO toggle~1 µs (tipik)5 ns
Bellek korumasıMMU + sanal adresFiziksel adres, MMU yok
İşletim sistemiLinuxBare-metal firmware

01 Donanım kaynakları

PRU-ICSS bloğunun barındırdığı donanım kaynakları, firmware tasarımını doğrudan etkiler. Her birimin kapasitesini ve sınırlamalarını iyi anlamak gerekir.

PRU0 ve PRU1 Çekirdekleri

Her PRU çekirdeği kendi bellek alanına sahiptir ve birbirinden bağımsız çalışabilir. Ancak paylaşımlı RAM ve INTC aracılığıyla birbirleriyle ve ARM ile haberleşirler.

Komut RAM (IRAM)Her PRU için 8 KB — maksimum ~2000 komut kapasitesi
Veri RAM (DRAM)Her PRU için 8 KB — yerel değişkenler ve tamponlar için
Paylaşımlı RAM12 KB — her iki PRU ve ARM'ın erişebildiği ortak alan
GPO pinleriHer PRU için 32 adet dijital çıkış, tek döngüde tamamı değiştirilebilir
GPI pinleriHer PRU için 32 adet dijital giriş, tek döngüde tamamı okunabilir

IEP Zamanlayıcısı

IEP (Industrial Ethernet Peripheral), 32-bit donanım zamanlayıcısı olarak kullanılabilir. PRU firmware'i IEP ile periyodik görevler zamanlayabilir veya olaylar arasındaki süreyi nanosaniye çözünürlüğüyle ölçebilir.

/* IEP zamanlayıcısını başlatmak */
CT_IEP.TMR_GLB_CFG_bit.CNT_EN = 1;       /* sayacı etkinleştir */
CT_IEP.TMR_GLB_CFG_bit.DEFAULT_INC = 1;  /* her döngüde 1 artır */

/* Mevcut zamanı oku */
uint32_t t_start = CT_IEP.TMR_CNT;
/* ... işlem ... */
uint32_t elapsed_ns = (CT_IEP.TMR_CNT - t_start) * 5; /* 5 ns/döngü */

eCAP (Enhanced Capture)

PRU-ICSS içindeki eCAP modülü, gelen sinyal kenarlarını (yükselen/düşen) donanım düzeyinde zaman damgalamak için kullanılır. Yazılım döngüsü gerekmeksizin PWM frekans/duty ölçümü yapılabilir.

UART

PRU-ICSS, 115200 baud'a kadar çalışabilen bir donanım UART birimini barındırır. PRU firmware'i bu UART'ı Linux seri konsolu veya özel protokol arayüzü olarak kullanabilir.

Fiziksel Pin Eşlemeleri

PRU BirimiBeagleBone HeaderAM335x Sinyal AdıNotlar
PRU0 GPO[0]P9.31pr1_pru0_pru_r30[0]Çıkış
PRU0 GPI[0]P9.31pr1_pru0_pru_r31[0]Giriş (aynı pin, farklı mod)
PRU1 GPO[0]P8.45pr1_pru1_pru_r30[0]Çıkış
PRU1 GPO[1]P8.46pr1_pru1_pru_r30[1]Çıkış
PRU0 UART TXP9.17pr1_uart0_txdUART0 TX

02 Geliştirme ortamı kurulumu

PRU firmware'i derlemek için TI'ın resmi PRU Code Generation Tools (PRU-CGT) ve destekleyici yazılım paketi (pru-software-support-package) gereklidir.

Gerekli bileşenler

ti-pru-cgtTI PRU C/C++ derleyicisi (clpru) — PRU mimarisi için optimize edilmiş
pru-software-supportBaşlık dosyaları, örnek kodlar ve resource table şablonları
gnuarmemb / arm-linux-gnueabihfKonak sistemde Linux çapraz derleyicisi (Linux tarafı modülleri için)
BeagleBone SDK / TI SDKOpsiyonel — eksiksiz BSP ve önceden derlenmiş kernel içerir

Ubuntu/Debian üzerinde kurulum

# TI resmi PPA üzerinden PRU araçları
sudo add-apt-repository ppa:beagleboard/ppa
sudo apt-get update
sudo apt-get install ti-pru-cgt-installer

# pru-software-support paketi (başlık dosyaları)
git clone https://git.ti.com/pru-software-support-package/pru-software-support-package.git
export PRU_SSP=$(pwd)/pru-software-support-package

# Derleyici yolunu ayarla
export PRU_CGT=/usr/share/ti/cgt-pru
export PATH=$PRU_CGT/bin:$PATH

# Kurulumu doğrula
clpru --version

BeagleBone üzerinde yerinde kurulum

Debian Buster veya Bullseye çalıştıran BeagleBone'da PRU araçları paket deposundan yüklenebilir:

# BeagleBone Debian deposu üzerinden
sudo apt-get update
sudo apt-get install pru-software-support am335x-pru-package

# Kernel PRU sürücülerini kontrol et
lsmod | grep pru
# pru_rproc ve rpmsg_pru yüklü olmalı

# PRU remoteproc sysfs düğümleri
ls /sys/bus/platform/drivers/pru-rproc/

Dizin yapısı

pru-projesi/
├── Makefile
├── resource_table.h       # remoteproc kaynak tablosu
├── main.c                 # PRU C kaynak kodu
├── AM335x_PRU.cmd         # Linker command dosyası
└── gen/
    ├── main.out           # ELF çıktısı
    └── main.map           # Bellek haritası

Temel Makefile

PRU_CGT    ?= /usr/share/ti/cgt-pru
PRU_SSP    ?= /usr/lib/ti/pru-software-support-package

CC         = clpru
CFLAGS     = --include_path=$(PRU_SSP)/include \
             --include_path=$(PRU_SSP)/include/am335x \
             -v3 -O2 --display_error_number \
             --endian=little --hardware_mac=yes
LFLAGS     = --reread_libs --warn_sections \
             --stack_size=0x100 --heap_size=0x100

TARGET     = gen/main.out
SOURCES    = main.c

all: $(TARGET)

$(TARGET): $(SOURCES)
	mkdir -p gen
	$(CC) $(CFLAGS) -z -i$(PRU_CGT)/lib -i$(PRU_CGT)/include \
	      $(LFLAGS) -o $@ $(SOURCES) AM335x_PRU.cmd

clean:
	rm -rf gen/

03 PRU firmware yazımı

PRU firmware, C dili ile yazılır; ancak standart libc mevcut değildir ve çevre birimine doğrudan yazmaç erişimi gereklidir. Resource table yapısı, remoteproc ile iletişim kurmak için zorunludur.

Resource Table

Her PRU firmware, resource_table bölümünde bir kaynak tablosu tanımlamalıdır. Bu tablo remoteproc çerçevesine hangi RPMsg kanallarının, vring'lerin ve memory mapping'lerinin gerektiğini bildirir.

#ifndef _RSC_TABLE_PRU_H_
#define _RSC_TABLE_PRU_H_

#include <stddef.h>
#include <rsc_types.h>

#define NUM_VDEVS       1
#define NUM_RPMSG_CHNLS 1

struct my_resource_table {
    struct resource_table base;
    uint32_t offset[1];
    struct fw_rsc_vdev rpmsg_vdev;
    struct fw_rsc_vdev_vring rpmsg_vring0;
    struct fw_rsc_vdev_vring rpmsg_vring1;
};

#pragma DATA_SECTION(pru_remoteproc_ResourceTable, ".resource_table")
#pragma RETAIN(pru_remoteproc_ResourceTable)
struct my_resource_table pru_remoteproc_ResourceTable = {
    { 1, NUM_VDEVS, 0, 0, },
    { offsetof(struct my_resource_table, rpmsg_vdev), },
    {   TYPE_VDEV, VIRTIO_ID_RPMSG, 0,
        RPMSG_PRU_C0_FEATURES, 0, 0, 0, NUM_RPMSG_CHNLS, { 0, 0 },
    },
    { PRU_D_STACK_SIZE, 16, 4, 1, 0 },
    { PRU_D_STACK_SIZE + 0x2000, 16, 4, 2, 0 },
};

Temel PRU C programı

#include <stdint.h>
#include <pru_cfg.h>
#include <pru_intc.h>
#include "resource_table.h"

volatile register uint32_t __R30;  /* GPO çıkış yazmaç */
volatile register uint32_t __R31;  /* GPI giriş yazmaç  */

#define SHARED_MEM_ADDR  0x00010000
volatile uint32_t *shared_mem = (volatile uint32_t *)SHARED_MEM_ADDR;

static inline void delay_ns(uint32_t ns) {
    uint32_t cycles = ns / 5;
    while (cycles--) {
        __delay_cycles(1);
    }
}

void main(void) {
    /* OCP master bağlantısını etkinleştir */
    CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;

    while (1) {
        uint32_t cmd = shared_mem[0];
        if (cmd == 1) {
            __R30 |=  (1u << 0);   /* GPO0 yüksek */
            delay_ns(500);
            __R30 &= ~(1u << 0);  /* GPO0 düşük */
            shared_mem[0] = 0;      /* komutu temizle */
        }
        __delay_cycles(10);
    }
}

INTC yapılandırması

PRU, ARM'dan sinyal almak için INTC üzerinden kesme alabilir. Sistem olayları (system events) PRU kanallarına eşlenerek "mailbox" benzeri bir mekanizma oluşturulur.

/* INTC başlatma — PRU0 system event 16 bekle */
CT_INTC.SICR_bit.STS_CLR_IDX = 16;   /* event 16'yı temizle */
CT_INTC.EISR_bit.EN_SET_IDX  = 16;   /* event 16'yı etkinleştir */

/* Kesme bekle (host interrupt 0 = R31 bit 30) */
while ((__R31 & (1u << 30)) == 0) {
    /* bekleniyor */
}
CT_INTC.SICR_bit.STS_CLR_IDX = 16;  /* event'i temizle */

04 remoteproc ile yükleme

Linux, PRU firmware'ini remoteproc çerçevesi üzerinden yükler ve yönetir. Firmware ELF dosyası /lib/firmware/ dizinine kopyalanır; ardından sysfs üzerinden PRU başlatılıp durdurulabilir.

remoteproc sürücü mimarisi

Linux Kullanıcı Alanı
    |
    | sysfs: /sys/class/remoteproc/remoteproc1/state
    v
pru_rproc.ko (kernel sürücüsü)
    |-- Firmware yükle: /lib/firmware/am335x-pru0-fw
    |-- ELF ayrıştır: resource_table oku
    |-- Bellek eşle: IRAM / DRAM / Shared RAM
    |-- INTC yapılandır
    v
PRU0 Çekirdeği (firmware çalışıyor)
    

Firmware dağıtımı

# Derlenmiş firmware'i yerleştir
sudo cp gen/main.out /lib/firmware/am335x-pru0-fw

# Sembolik bağlantıyı kontrol et
ls -la /lib/firmware/ | grep pru

sysfs üzerinden PRU kontrolü

# remoteproc düğümlerini listele
ls /sys/class/remoteproc/
# remoteproc0  remoteproc1  (PRU0 ve PRU1)

# PRU0 için firmware adını ayarla
echo "am335x-pru0-fw" | sudo tee /sys/class/remoteproc/remoteproc1/firmware

# PRU0'ı başlat
echo "start" | sudo tee /sys/class/remoteproc/remoteproc1/state

# Durumu kontrol et
cat /sys/class/remoteproc/remoteproc1/state
# "running" çıktısı beklenir

# PRU0'ı durdur
echo "stop" | sudo tee /sys/class/remoteproc/remoteproc1/state

Kernel mesajlarını izleme

# PRU yükleme/başlatma günlüklerini takip et
dmesg -w | grep -i pru

# Beklenen çıktı:
# remoteproc remoteproc1: powering up 4a334000.pru
# remoteproc remoteproc1: Booting fw image am335x-pru0-fw, size 49152
# remoteproc remoteproc1: remote processor 4a334000.pru is now up
# rpmsg_pru virtio0.rpmsg-pru.-1.30: new rpmsg_pru channel: 30

remoteproc durum makinesi

offlinePRU durdurulmuş, bellek yüklenmemiş — başlangıç durumu
runningFirmware yüklendi ve PRU çekirdeği çalışıyor
suspendedPRU askıya alınmış, bellek korunuyor
crashedPRU hata nedeniyle durdu; dmesg'de hata mesajı beklenir
deletedDonanım nesnesi kaldırıldı (modül çıkarmada)

Device Tree düğümü

/* arch/arm/boot/dts/am33xx-l4.dtsi içindeki PRU düğümü */
pru0: pru@4a334000 {
    compatible = "ti,am3356-pru";
    reg = <0x4a334000 0x2000>,   /* IRAM */
          <0x4a322000 0x400>,    /* control */
          <0x4a322400 0x100>;    /* debug */
    firmware-name = "am335x-pru0-fw";
};

05 RPMsg ile haberleşme

RPMsg (Remote Processor Messaging), PRU ve ARM çekirdeği arasında çift yönlü, mesaj tabanlı iletişim sağlar. Linux tarafında /dev/rpmsg_pruN karakter aygıtı üzerinden erişilir.

RPMsg mimarisi

Linux Kullanıcı Alanı
    |  open("/dev/rpmsg_pru30")
    |  write(fd, msg, len)  /  read(fd, buf, sizeof(buf))
    v
rpmsg_pru.ko (kernel sürücüsü)
    |
    | virtqueue (vring üzerinden)
    v
PRU Firmware
    |  pru_rpmsg_receive()
    |  pru_rpmsg_send()
    v
(işlem + yanıt)
    

PRU firmware — RPMsg başlatma

#include <pru_rpmsg.h>
#include <pru_virtqueue.h>

struct pru_rpmsg_transport transport;
uint16_t src, dst, len;
char payload[RPMSG_BUF_SIZE];

void main(void) {
    volatile uint8_t *status;

    CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;

    /* VDEV'in hazır olmasını bekle */
    status = &resourceTable.rpmsg_vdev.status;
    while (!(*status & VIRTIO_CONFIG_S_DRIVER_OK));

    /* Transport başlat */
    pru_rpmsg_init(&transport,
                   &resourceTable.rpmsg_vring0,
                   &resourceTable.rpmsg_vring1,
                   TO_ARM_HOST, FROM_ARM_HOST);

    /* RPMsg kanalını duyur */
    while (pru_rpmsg_channel(RPMSG_NS_CREATE, &transport,
                             CHAN_NAME, CHAN_DESC, CHAN_PORT) != PRU_RPMSG_SUCCESS);

    /* Ping-pong döngüsü */
    while (1) {
        if (__R31 & HOST_INT) {
            CT_INTC.SICR_bit.STS_CLR_IDX = FROM_ARM_HOST;
            while (pru_rpmsg_receive(&transport, &src, &dst,
                                     payload, &len) == PRU_RPMSG_SUCCESS) {
                /* Echo: aynı mesajı geri gönder */
                pru_rpmsg_send(&transport, dst, src, payload, len);
            }
        }
    }
}

Linux kullanıcı alanı

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(void) {
    int fd;
    char tx_buf[] = "merhaba PRU";
    char rx_buf[512];
    ssize_t n;

    fd = open("/dev/rpmsg_pru30", O_RDWR);
    if (fd < 0) { perror("open"); return 1; }

    write(fd, tx_buf, strlen(tx_buf));

    n = read(fd, rx_buf, sizeof(rx_buf));
    if (n > 0) {
        rx_buf[n] = '\0';
        printf("PRU yaniti: %s\n", rx_buf);
    }
    close(fd);
    return 0;
}

RPMsg kanal referansları

/dev/rpmsg_pru30PRU0 RPMsg kanalı — varsayılan port 30
/dev/rpmsg_pru31PRU1 RPMsg kanalı — varsayılan port 31
RPMSG_BUF_SIZE512 bayt — tek mesaj maksimum boyutu
virtqueuevring0: ARM->PRU, vring1: PRU->ARM yönleri

06 GPIO ve pin multiplexing

BeagleBone Black'taki her pin birden fazla işlev için kullanılabilir. PRU girişi/çıkışı olarak kullanmak için Device Tree overlay ile pin mux ayarlanmalıdır.

Pin Mux Kavramı

AM335x'in her I/O pini, pinmux register üzerinden 8 farklı "mod" seçeneğinden birine ayarlanabilir. PRU için pruout veya pruin modu seçilmelidir. Mod numaraları pin başına farklıdır; AM335x Technical Reference Manual'dan doğrulanmalıdır.

Mod BitiAnlamı
MUX_MODE (2:0)0–7 arası fonksiyon seçimi (pruout genellikle 5 veya 6)
PULL_UP (4)Dahili pull-up direnci etkinleştir
PULL_DOWN (3)Dahili pull-down direnci etkinleştir (varsayılan)
RXACTIVE (5)Giriş tamponu etkinleştir — GPI için zorunlu
SLEWCTRL (6)0 = hızlı, 1 = yavaş kenar

Device Tree overlay

/dts-v1/;
/plugin/;

/ {
    compatible = "ti,beaglebone-black";
    part-number = "BBB-PRU0-GPIO";
    version = "00A0";

    fragment@0 {
        target = <&am33xx_pinmux>;
        __overlay__ {
            pru0_gpio_pins: pru0_gpio_pins {
                pinctrl-single,pins = <
                    /* P9.31 = offset 0x190, mode 5 = GPO (pruout) */
                    0x190 0x05
                    /* P9.29 = offset 0x194, mode 5 = GPO */
                    0x194 0x05
                    /* P9.28 = offset 0x19C, mode 6 + RXACTIVE = GPI */
                    0x19C 0x26
                >;
            };
        };
    };
};

Overlay derleme ve yükleme

# DTS'i DTB overlay formatına derle
dtc -O dtb -o am335x-boneblack-pru0-gpio-00A0.dtbo \
    -b 0 -@ am335x-boneblack-pru0-gpio.dts

sudo cp am335x-boneblack-pru0-gpio-00A0.dtbo /lib/firmware/

# Yeni kernel (4.19+) ile config-pin kullanımı
config-pin P9.31 pruout
config-pin P9.29 pruout
config-pin P9.28 pruin

# Mevcut modu doğrula
config-pin -q P9.31

Firmware içinde GPO/GPI kullanımı

volatile register uint32_t __R30;  /* GPO */
volatile register uint32_t __R31;  /* GPI */

/* P9.31 pini yüksek yap (PRU0 GPO bit 0) */
__R30 |= (1u << 0);

/* P9.29 pini yüksek yap (PRU0 GPO bit 1) */
__R30 |= (1u << 1);

/* Tüm GPO'yu atomik olarak ayarla */
__R30 = 0x00000003;

/* P9.28 pini oku (PRU0 GPI bit 3) */
uint8_t pin_val = (__R31 >> 3) & 1;

07 Gerçek senaryo: WS2812 LED sürücüsü

WS2812 (NeoPixel) RGB LED'ler, 800 kHz taşıyıcı ve kesin darbe genişliği gerektiren tek-kablo protokol kullanır. PRU'nun deterministik zamanlaması bu protokol için biçilmiş kaftandır.

WS2812 Protokolü

Her LED 24-bit GRB veri (8-bit G, 8-bit R, 8-bit B) alır. Her bit, yüksek ve düşük darbe süresiyle kodlanır:

Bit-1 : T1H = 800 nsYüksek: 800 ns (160 döngü), Düşük: 450 ns (90 döngü)
Bit-0 : T0H = 400 nsYüksek: 400 ns (80 döngü), Düşük: 850 ns (170 döngü)
Reset sinyaliDüşük: 50 µs minimum (10000 döngü) — veri gönderimi sonunda zorunlu
PRU Zamanlaması (200 MHz = 5 ns/döngü):

  Bit-1: [HIGH: 160 döngü][LOW: 90 döngü]  toplam = 1250 ns
  Bit-0: [HIGH:  80 döngü][LOW: 170 döngü] toplam = 1250 ns
  Reset: [LOW: 10000 döngü] = 50 µs
    

WS2812 PRU firmware

#include <stdint.h>
#include <pru_cfg.h>
#include "resource_table.h"

volatile register uint32_t __R30;

#define DATA_PIN_BIT  0u
#define SHARED_MEM    0x00010000u
#define MAX_LEDS      60u

typedef struct { uint8_t g, r, b; } rgb_t;

volatile rgb_t *led_buf = (volatile rgb_t *)SHARED_MEM;
volatile uint32_t *cmd  = (volatile uint32_t *)(SHARED_MEM + MAX_LEDS * 3 + 4);

static void send_bit(uint8_t bit) {
    if (bit) {
        __R30 |=  (1u << DATA_PIN_BIT);
        __delay_cycles(158);
        __R30 &= ~(1u << DATA_PIN_BIT);
        __delay_cycles(88);
    } else {
        __R30 |=  (1u << DATA_PIN_BIT);
        __delay_cycles(78);
        __R30 &= ~(1u << DATA_PIN_BIT);
        __delay_cycles(168);
    }
}

static void send_byte(uint8_t byte) {
    int i;
    for (i = 7; i >= 0; i--) {
        send_bit((byte >> i) & 1);
    }
}

static void send_reset(void) {
    __R30 &= ~(1u << DATA_PIN_BIT);
    __delay_cycles(10000);
}

void main(void) {
    uint32_t i, n_leds;
    CT_CFG.SYSCFG_bit.STANDBY_INIT = 0;

    while (1) {
        if (*cmd > 0) {
            n_leds = (*cmd > MAX_LEDS) ? MAX_LEDS : *cmd;
            for (i = 0; i < n_leds; i++) {
                send_byte(led_buf[i].g);
                send_byte(led_buf[i].r);
                send_byte(led_buf[i].b);
            }
            send_reset();
            *cmd = 0;  /* tamamlandı */
        }
    }
}

Linux kullanıcı alanı kontrolü

#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

#define PRUSS_SHAREDMEM_BASE  0x4A310000
#define PRUSS_MAP_SIZE        0x40000
#define MAX_LEDS              60

typedef struct { uint8_t g, r, b; } rgb_t;

int main(void) {
    int mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
    void *base = mmap(NULL, PRUSS_MAP_SIZE,
                      PROT_READ | PROT_WRITE,
                      MAP_SHARED, mem_fd,
                      PRUSS_SHAREDMEM_BASE);

    rgb_t *leds = (rgb_t *)base;
    volatile uint32_t *cmd = (volatile uint32_t *)(base + MAX_LEDS * 3 + 4);

    /* Gökkuşağı efekti */
    int i;
    for (i = 0; i < MAX_LEDS; i++) {
        leds[i].r = (i * 4) & 0xFF;
        leds[i].g = ((i + 20) * 4) & 0xFF;
        leds[i].b = ((i + 40) * 4) & 0xFF;
    }
    *cmd = MAX_LEDS;

    while (*cmd != 0);  /* tamamlanmayı bekle */

    munmap(base, PRUSS_MAP_SIZE);
    close(mem_fd);
    return 0;
}

08 Hata ayıklama

PRU firmware hata ayıklaması, klasik GDB akışından farklıdır. Paylaşımlı bellek dökümü, IEP zamanlayıcısı tabanlı profilleme ve remoteproc crash kurtarma temel tekniklerdir.

Shared Memory Log ile hata tespiti

/* PRU firmware — debug mesajı shared mem'e yaz */
#define DBG_MEM  0x00010F00u
volatile uint32_t *dbg = (volatile uint32_t *)DBG_MEM;

void pru_assert(uint32_t cond, uint32_t code) {
    if (!cond) {
        dbg[0] = 0xDEADBEEFu;
        dbg[1] = code;
        dbg[2] = CT_IEP.TMR_CNT;  /* nanosaniye zaman damgası */
        while (1);                  /* PRU'yu durdur */
    }
}
/* Linux — shared mem okuma */
int fd = open("/dev/mem", O_RDONLY);
uint32_t *base = mmap(NULL, 0x40000, PROT_READ,
                      MAP_SHARED, fd, 0x4A310000);
volatile uint32_t *dbg = base + (0x0F00 / 4);

if (dbg[0] == 0xDEADBEEF) {
    printf("PRU HATA: kod=0x%08X, t=%u döngü\n", dbg[1], dbg[2]);
}

remoteproc crash kurtarma

# PRU durumunu kontrol et
cat /sys/class/remoteproc/remoteproc1/state

# "crashed" ise yeniden başlat:
echo "stop"  | sudo tee /sys/class/remoteproc/remoteproc1/state
dmesg | tail -30 | grep -i "pru\|remoteproc"
sudo cp gen/main_fixed.out /lib/firmware/am335x-pru0-fw
echo "start" | sudo tee /sys/class/remoteproc/remoteproc1/state

Logic analyzer ile doğrulama

WS2812 gibi kesin zamanlama protokollerinde yazılım analizörü yeterli değildir. Sigrok / PulseView ile ucuz bir logic analyzer (örn. DSLogic) kullanarak PRU çıkış pinini örneklemek, gerçek darbe genişliklerini doğrulamak için en güvenilir yöntemdir.

Örnekleme hızıWS2812 için minimum 10 MHz örnekleme gerekir (100 ns çözünürlük)
Tetiklemeİlk yükselen kenarda tetikle, 60 LED x 24 bit = 1440 bit yakalanır
DecoderPulseView'da "WS2812" protokol decoder'ı hazır olarak mevcuttur

IEP ile performans profilleme

CT_IEP.TMR_GLB_CFG_bit.CNT_EN = 1;

uint32_t t0 = CT_IEP.TMR_CNT;
/* ... ölçülecek kod ... */
uint32_t t1 = CT_IEP.TMR_CNT;

/* Sonucu shared mem'e yaz */
shared_mem[10] = (t1 - t0) * 5;  /* nanosaniye cinsinden */

Sık karşılaşılan sorunlar

SorunOlası NedenÇözüm
PRU "crashed" durumuna geçiyorIRAM taşması, yanlış bellek erişimiLinker map dosyasını kontrol et, adres sınırlarını doğrula
/dev/rpmsg_pru30 oluşmuyorResource table yanlış, VDEV hazır değilresource_table.h'yi doğrula, dmesg'i incele
GPIO çıkışı yokPin mux ayarı eksik veya yanlışconfig-pin ile pin modunu doğrula
WS2812 renk hatalıGecikme döngüsü sayısı yanlışLogic analyzer ile darbe süresini ölç, kalibre et
Firmware yüklenmiyor/lib/firmware/ dosyası eksik veya bozukfile gen/main.out ile ELF formatını kontrol et