2413453_en-US

キャンセル
次の結果を表示 
表示  限定  | 次の代わりに検索 
もしかして: 

2413453_en-US

2413453_en-US

How to use the GPU of the NXP Layerscape LS1028A with Yocto
 

 Yocto Project Configuration

The most modern and NXP-recommended method for developing with the LS1028A is the Layerscape Linux Distribution POC (LLDP) based on Yocto. Unlike the LSDK (which uses Flexbuild), LLDP uses Yocto/Bitbake and is the long-term supported path.

1 Host Requirements

Requirement

Details

Operating System

Ubuntu 20.04 LTS (Focal) — official recommendation

RAM

Minimum 8 GB (16 GB+ recommended)

Disk Space

Minimum 100 GB free

CPU

Minimum 4 cores (more cores = faster builds)

Tools

git, repo, python3, wget, curl, build-essential

Install dependencies on Ubuntu:

sudo apt-get update && sudo apt-get install -y  gawk wget git diffstat unzip texinfo gcc-multilib \

  build-essential chrpath socat cpio python3 python3-pip  python3-pexpect xz-utils debianutils iputils-ping \

  python3-git python3-jinja2 libegl1-mesa libsdl1.2-dev  pylint xterm rsync curl locales zstd liblz4-tool \

  repo ca-certificates


# Configure git (required by repo)

git config --global user.name "Your Name"

git config --global user.email "[email protected]"


2 Download the LLDP Repository (Yocto for LS1028A)

# Create working directory

mkdir ~/lldp-ls1028a && cd ~/lldp-ls1028a


# Initialize repo with the LLDP manifest (Kirkstone, kernel 5.15)

repo init -u https://github.com/nxp-qoriq/yocto-sdk.git \

          -b kirkstone \

          -m ls-5.15.71-2.2.0_distro.xml


# Sync all repositories (may take 30-60 minutes)

repo sync

Note: For the latest version (LLDP L6.1.1, kernel 6.1), check the updated manifest at: https://github.com/nxp-qoriq/yocto-sdk


3 Set Up the Build Environment for LS1028A

# Initialize Yocto environment for the LS1028A

# (run from the lldp-ls1028a/ directory)

DISTRO=fsl-qoriq-distro MACHINE=ls1028ardb source distro-setup-env

# This automatically creates and enters the build directory

# You are now in: ~/lldp-ls1028a/build_ls1028ardb/


4 local.conf File — GPU Configuration

The conf/local.conf file inside the build directory controls compilation options. For the LS1028A with GPU, verify or add:

# Edit the configuration file

nano conf/local.conf

Key parameters for GPU and desktop:

# Target machine

MACHINE = "ls1028ardb"


# Distribution with GPU and Wayland support

DISTRO = "fsl-qoriq-distro"


# Enable display and GPU features

DISTRO_FEATURES:append = " wayland opengl"


# GPU driver: use Etnaviv (open-source) for LS1028A

# DO NOT use imx-gpu-viv (i.MX only, requires ARCH_MXC)

PREFERRED_PROVIDER_virtual/libgl = "mesa"

PREFERRED_PROVIDER_virtual/libgles1 = "mesa"

PREFERRED_PROVIDER_virtual/libgles2 = "mesa"

PREFERRED_PROVIDER_virtual/egl = "mesa"


# Enable OpenCL support via Vivante GPU

IMAGE_INSTALL:append = " clinfo"


# Accept NXP proprietary licenses (required for GPU firmware)

LICENSE_FLAGS_ACCEPTED = "nxp-proprietary"


# Parallel build threads (adjust to your host PC)

BB_NUMBER_THREADS = "8"

PARALLEL_MAKE = "-j8"


# (Optional) Use ccache to speed up recompilations

INHERIT += "ccache"


5 Required Yocto Layers (bblayers.conf)

Verify that conf/bblayers.conf includes these layers:

cat conf/bblayers.conf

It must contain at least:

BBLAYERS ?= " \

  ${BSPDIR}/sources/poky/meta \

  ${BSPDIR}/sources/poky/meta-poky \

  ${BSPDIR}/sources/meta-openembedded/meta-oe \

  ${BSPDIR}/sources/meta-openembedded/meta-multimedia \

  ${BSPDIR}/sources/meta-openembedded/meta-python \

  ${BSPDIR}/sources/meta-openembedded/meta-networking \

  ${BSPDIR}/sources/meta-freescale \

  ${BSPDIR}/sources/meta-qoriq \

  ${BSPDIR}/sources/meta-nxp-desktop \

"

The meta-nxp-desktop layer is what provides GPU support for the LS1028A with the ls-image-desktop image.


6 Build the Image with GPU Support

# Recommended: Desktop image with full GPU support (LS1028A only)

# Includes: GNOME desktop, Weston/Wayland, Vivante GPU drivers, OpenCL

bitbake ls-image-desktop


# Alternative: download all packages first before building

# (useful for catching network errors before the long build)

bitbake ls-image-desktop --runall fetch

bitbake ls-image-desktop


# Minimal: main image without desktop (no GPU by default)

bitbake ls-image-main


# Lite: minimal image

bitbake ls-image-lite

Estimated build time: Between 4 and 8 hours on the first build (depending on host hardware). Incremental builds are much faster.


7 Install the Image to the SD Card

After compilation, output files are located in: tmp/deploy/images/ls1028ardb/

# Identify the SD card (verify with lsblk)

lsblk


# Install image using flex-installer (included in the SDK)

flex-installer \

  -b tmp/deploy/images/ls1028ardb/boot_ls1028ardb.tgz \

  -f tmp/deploy/images/ls1028ardb/firmware_ls1028ardb_sdboot.img \

  -r tmp/deploy/images/ls1028ardb/ls-image-desktop-ls1028ardb.tar.zst \

  -d /dev/sdX    # replace with your SD device


  1. Practical Example: OpenCL Application on LS1028A

This example shows how to compile and run a program that uses the GC7000UL GPU to add two vectors with OpenCL.


8.1 Source Code: vector_add.cl (OpenCL Kernel)

Create the kernel file on the LS1028A board:

# On the LS1028A (via serial or SSH)

cat > /home/root/vector_add.cl << 'EOF'

__kernel void vector_add(

    __global const float* a,

    __global const float* b,

    __global float* c,

    const int n)

{

    int gid = get_global_id(0);

    if (gid < n) {

        c[gid] = a[gid] + b[gid];

    }

}


8.2 Source Code: vector_add.c (OpenCL Host)

cat > /home/root/vector_add.c << 'EOF'

#include

#include

#include

#include


#define VECTOR_SIZE 1024


int main() {

    cl_platform_id   platform;

    cl_device_id     device;

    cl_context       context;

    cl_command_queue queue;

    cl_program       program;

    cl_kernel        kernel;

    cl_mem           buf_a, buf_b, buf_c;

    cl_int           err;


    // 1. Get Vivante GPU platform and device

    err = clGetPlatformIDs(1, &platform, NULL);

    err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL);


    // Print device name

    char device_name[128];

    clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(device_name), device_name, NULL);

    printf("GPU detected: %s\n", device_name);


    // 2. Create context and command queue

    context = clCreateContext(NULL, 1, &device, NULL, NULL, &err);

    queue   = clCreateCommandQueue(context, device, 0, &err);


    // 3. Read kernel source from file

    FILE* f = fopen("vector_add.cl", "r");

    fseek(f, 0, SEEK_END);

    size_t src_size = ftell(f);

    rewind(f);

    char* src=(char*)malloc(src_size + 1);

    fread(src, 1, src_size, f);

    src[src_size] = '\0';

    fclose(f);


    // 4. Compile OpenCL program

    program = clCreateProgramWithSource(context, 1, (const char**)&src, &src_size, &err);

    err = clBuildProgram(program, 1, &device, NULL, NULL, NULL);

    if (err != CL_SUCCESS) {

        char log[2048];

        clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG,

                              sizeof(log), log, NULL);

        printf("Compilation error:\n%s\n", log);

        return 1;

    }

    kernel = clCreateKernel(program, "vector_add", &err);


    // 5. Prepare input data

    float* h_a = (float*)malloc(VECTOR_SIZE * sizeof(float));

    float* h_b = (float*)malloc(VECTOR_SIZE * sizeof(float));

    float* h_c = (float*)malloc(VECTOR_SIZE * sizeof(float));

    for (int i = 0; i < VECTOR_SIZE; i++) {

        h_a[i] = (float)i;

        h_b[i] = (float)(VECTOR_SIZE - i);

    }


    // 6. Create GPU buffers

    buf_a = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,

                           VECTOR_SIZE * sizeof(float), h_a, &err);

    buf_b = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,

                           VECTOR_SIZE * sizeof(float), h_b, &err);

    buf_c = clCreateBuffer(context, CL_MEM_WRITE_ONLY,

                           VECTOR_SIZE * sizeof(float), NULL, &err);


    // 7. Set kernel arguments and execute on GPU

    int n = VECTOR_SIZE;

    clSetKernelArg(kernel, 0, sizeof(cl_mem), &buf_a);

    clSetKernelArg(kernel, 1, sizeof(cl_mem), &buf_b);

    clSetKernelArg(kernel, 2, sizeof(cl_mem), &buf_c);

    clSetKernelArg(kernel, 3, sizeof(int), &n);


    size_t global_size = VECTOR_SIZE;

    err = clEnqueueNDRangeKernel(queue, kernel, 1, NULL,

                                  &global_size, NULL, 0, NULL, NULL);

    clFinish(queue);


    // 8. Read result

    clEnqueueReadBuffer(queue, buf_c, CL_TRUE, 0,

                        VECTOR_SIZE * sizeof(float), h_c, 0, NULL, NULL);


    // 9. Verify result (each element should equal VECTOR_SIZE = 1024)

    int ok = 1;

    for (int i = 0; i < VECTOR_SIZE; i++) {

        if (h_c[i] != (float)VECTOR_SIZE) { ok = 0; break; }

    }

    printf("Result: %s\n", ok ? "CORRECT - GPU works!" : "CALCULATION ERROR");

    printf("Example: a[0]=%.0f + b[0]=%.0f = c[0]=%.0f\n",

           h_a[0], h_b[0], h_c[0]);


    // Free resources

    clReleaseMemObject(buf_a); clReleaseMemObject(buf_b); clReleaseMemObject(buf_c);

    clReleaseKernel(kernel); clReleaseProgram(program);

    clReleaseCommandQueue(queue); clReleaseContext(context);

    free(h_a); free(h_b); free(h_c); free(src);

    return 0;

}


8.3 Compile and Run on the LS1028A

On the LS1028A board (connected via serial or SSH):

# Install build tools and OpenCL headers

apt-get install -y gcc clinfo ocl-icd-libopencl1 opencl-headers


# Compile

gcc -o vector_add vector_add.c -lOpenCL -I/usr/include


# Run

./vector_add

Expected output:

GPU detected: Vivante OpenCL Device GC7000UL.6202.0000

Result: CORRECT - GPU works!

Example: a[0]=0 + b[0]=1024 = c[0]=1024


12.4 Yocto Recipe to Include the Example in the Image

To include the example directly in the Yocto image, create a recipe in your custom layer:

mkdir -p ~/lldp-ls1028a/sources/meta-my-layer/recipes-examples/opencl-vector/files

cp vector_add.c vector_add.cl \

   ~/lldp-ls1028a/sources/meta-my-layer/recipes-examples/opencl-vector/files/

Recipe file opencl-vector_1.0.bb:

SUMMARY = "OpenCL vector addition example for LS1028A GPU"

LICENSE = "MIT"

LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"


SRC_URI = "file://vector_add.c \

           file://vector_add.cl"


DEPENDS = "virtual/opencl-icd opencl-headers"


S = "${WORKDIR}"


do_compile() {

    ${CC} ${CFLAGS} -o vector_add vector_add.c -lOpenCL ${LDFLAGS}

}


do_install() {

    install -d ${D}${bindir}

    install -m 0755 vector_add ${D}${bindir}/

    install -d ${D}/home/root

    install -m 0644 vector_add.cl ${D}/home/root/

}


FILES:${PN} += "/home/root/vector_add.cl"

Add to local.conf and rebuild:

IMAGE_INSTALL:append = " opencl-vector"

bitbake ls-image-desktop


  1. Practical Example: OpenGL ES Rendering with Wayland/Weston

This example shows how to render an animated triangle using OpenGL ES 2.0 with EGL on the Weston (Wayland) compositor — the standard "Hello World" of embedded graphics on the GC7000UL GPU.

9.1 Graphics Stack Diagram

C Application

     ↓

OpenGL ES 2.0  (libGLESv2.so — Vivante GC7000UL)

     ↓

EGL 1.4        (libEGL.so — interface between GLES and Wayland)

     ↓

Wayland Client (libwayland-client, libwayland-egl)

     ↓

Weston Compositor (DRM/KMS + Mali-DP500)

     ↓

DisplayPort → 4K Monitor


9.2 Install Dependencies

On the LS1028A (with ls-image-desktop):

apt-get install -y libgles2-mesa-dev libegl1-mesa-dev libwayland-dev libwayland-egl-backend-dev gcc pkg-config


9.3 Source Code: triangle_gles.c

cat > /home/root/triangle_gles.c << 'EOF'

/**

 * OpenGL ES 2.0 + EGL + Wayland example

 * Renders a colored spinning triangle on the Vivante GC7000UL GPU of the LS1028A

 * Compile: gcc -o triangle_gles triangle_gles.c -lwayland-client -lwayland-egl -lEGL -lGLESv2 -lm

 */

#include

#include

#include

#include

#include

#include

#include

#include


#define WIDTH  800

#define HEIGHT 600


static struct wl_display       *wl_display    = NULL;

static struct wl_compositor    *wl_compositor = NULL;

static struct wl_shell         *wl_shell      = NULL;

static struct wl_surface       *wl_surface    = NULL;

static struct wl_shell_surface *shell_surface = NULL;

static struct wl_egl_window    *egl_window    = NULL;


static EGLDisplay egl_display;

static EGLContext egl_context;

static EGLSurface egl_surface;


static const char *vertex_shader_src=

    "attribute vec2 a_position;          \n"

    "attribute vec3 a_color;             \n"

    "varying vec3 v_color;               \n"

    "uniform float u_angle;              \n"

    "void main() {                       \n"

    "  float c = cos(u_angle);           \n"

    "  float s = sin(u_angle);           \n"

    "  vec2 rot = vec2(                  \n"

    "    a_position.x*c - a_position.y*s,\n"

    "    a_position.x*s + a_position.y*c \n"

    "  );                                \n"

    "  gl_Position = vec4(rot, 0.0, 1.0);\n"

    "  v_color = a_color;                \n"

    "}                                   \n";


static const char *fragment_shader_src=

    "precision mediump float;            \n"

    "varying vec3 v_color;               \n"

    "void main() {                       \n"

    "  gl_FragColor = vec4(v_color, 1.0);\n"

    "}                                   \n";


/* Vertices: position (x,y) + color (r,g,b) */

static const float vertices[] = {

     0.0f,  0.8f,  1.0f, 0.0f, 0.0f,   /* Top    - Red   */

    -0.7f, -0.5f,  0.0f, 1.0f, 0.0f,   /* Left   - Green */

     0.7f, -0.5f,  0.0f, 0.0f, 1.0f,   /* Right  - Blue  */

};


static void registry_global(void *data, struct wl_registry *reg,

                             uint32_t name, const char *iface, uint32_t ver) {

    if (strcmp(iface, "wl_compositor") == 0)

        wl_compositor = wl_registry_bind(reg, name, &wl_compositor_interface, 1);

    else if (strcmp(iface, "wl_shell") == 0)

        wl_shell = wl_registry_bind(reg, name, &wl_shell_interface, 1);

}

static void registry_global_remove(void *d, struct wl_registry *r, uint32_t n) {}

static const struct wl_registry_listener registry_listener = {

    registry_global, registry_global_remove

};


static GLuint compile_shader(GLenum type, const char *src) {

    GLuint shader = glCreateShader(type);

    glShaderSource(shader, 1, &src, NULL);

    glCompileShader(shader);

    GLint ok; glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);

    if (!ok) {

        char log[512]; glGetShaderInfoLog(shader, 512, NULL, log);

        printf("Shader error: %s\n", log); exit(1);

    }

    return shader;

}


int main() {

    wl_display = wl_display_connect(NULL);

    if (!wl_display) { printf("Error: could not connect to Wayland\n"); return 1; }


    struct wl_registry *registry = wl_display_get_registry(wl_display);

    wl_registry_add_listener(registry, &registry_listener, NULL);

    wl_display_dispatch(wl_display);

    wl_display_roundtrip(wl_display);


    wl_surface = wl_compositor_create_surface(wl_compositor);

    shell_surface = wl_shell_get_shell_surface(wl_shell, wl_surface);

    wl_shell_surface_set_toplevel(shell_surface);


    egl_display = eglGetDisplay((EGLNativeDisplayType)wl_display);

    eglInitialize(egl_display, NULL, NULL);

    eglBindAPI(EGL_OPENGL_ES_API);


    EGLint config_attribs[] = {

        EGL_SURFACE_TYPE,    EGL_WINDOW_BIT,

        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,

        EGL_RED_SIZE,   8, EGL_GREEN_SIZE, 8,

        EGL_BLUE_SIZE,  8, EGL_ALPHA_SIZE, 0,

        EGL_DEPTH_SIZE, 16, EGL_NONE

    };

    EGLConfig egl_config; EGLint num_configs;

    eglChooseConfig(egl_display, config_attribs, &egl_config, 1, &num_configs);


    EGLint ctx_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };

    egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, ctx_attribs);

    egl_window  = wl_egl_window_create(wl_surface, WIDTH, HEIGHT);

    egl_surface = eglCreateWindowSurface(egl_display, egl_config,

                                          (EGLNativeWindowType)egl_window, NULL);

    eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);


    printf("GPU: %s\n", glGetString(GL_RENDERER));

    printf("OpenGL ES Version: %s\n", glGetString(GL_VERSION));


    GLuint vs = compile_shader(GL_VERTEX_SHADER,   vertex_shader_src);

    GLuint fs = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_src);

    GLuint program = glCreateProgram();

    glAttachShader(program, vs); glAttachShader(program, fs);

    glLinkProgram(program); glUseProgram(program);


    GLint pos_loc   = glGetAttribLocation(program,  "a_position");

    GLint color_loc = glGetAttribLocation(program,  "a_color");

    GLint angle_loc = glGetUniformLocation(program, "u_angle");


    glViewport(0, 0, WIDTH, HEIGHT);


    float angle = 0.0f;

    int frames = 0;

    printf("Rendering spinning triangle (Ctrl+C to exit)...\n");


    while (1) {

        wl_display_dispatch_pending(wl_display);

        glClearColor(0.1f, 0.1f, 0.15f, 1.0f);

        glClear(GL_COLOR_BUFFER_BIT);

        glUniform1f(angle_loc, angle);

        glEnableVertexAttribArray(pos_loc);

        glVertexAttribPointer(pos_loc,   2, GL_FLOAT, GL_FALSE, 5*sizeof(float), vertices);

        glEnableVertexAttribArray(color_loc);

        glVertexAttribPointer(color_loc, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), vertices + 2);

        glDrawArrays(GL_TRIANGLES, 0, 3);

        eglSwapBuffers(egl_display, egl_surface);

        angle += 0.02f;

        if (angle > 6.2832f) angle -= 6.2832f;

        frames++;

        if (frames % 300 == 0)

            printf("Frame %d — angle: %.2f rad\n", frames, angle);

    }


    eglDestroyContext(egl_display, egl_context);

    eglDestroySurface(egl_display, egl_surface);

    eglTerminate(egl_display);

    wl_display_disconnect(wl_display);

    return 0;

}


9.4 Compile and Run

# Compile

gcc -o triangle_gles triangle_gles.c \

    -lwayland-client -lwayland-egl \

    -lEGL -lGLESv2 -lm


# Make sure Weston is running and set Wayland environment

export XDG_RUNTIME_DIR=/run/user/0

export WAYLAND_DISPLAY=wayland-0


# Run

./triangle_gles

Expected terminal output:

GPU: Vivante GC7000UL

OpenGL ES Version: OpenGL ES 3.1 V6.4.3.p4.398061

Rendering spinning triangle (Ctrl+C to exit)...

Frame 300 — angle: 6.00 rad

Frame 600 — angle: 5.68 rad

A RGB triangle spinning on a dark background will appear on screen, rendered by the GC7000UL GPU.

Bio_TICFSL_0-1789407894776.pngBio_TICFSL_0-1789407894776.pngBio_TICFSL_0-1789407894776.png


9.5 Yocto Recipe

SUMMARY = "OpenGL ES 2.0 spinning triangle example — LS1028A"

LICENSE = "MIT"

LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"


SRC_URI = "file://triangle_gles.c"


DEPENDS = "virtual/libgles2 virtual/egl wayland"


S = "${WORKDIR}"


do_compile() {

    ${CC} ${CFLAGS} -o triangle_gles triangle_gles.c \

        -lwayland-client -lwayland-egl -lEGL -lGLESv2 -lm ${LDFLAGS}

}


do_install() {

    install -d ${D}${bindir}

    install -m 0755 triangle_gles ${D}${bindir}/

}


9.6 Yocto Recipe for OpenCV with GPU

Add to local.conf:

IMAGE_INSTALL:append = " opencv python3-opencv"

PACKAGECONFIG:append:pn-opencv = " opencl"

bitbake ls-image-desktop


Regards


タグ(1)
評価なし
バージョン履歴
最終更新日:
1週間前
更新者: