» » Real-time BeagleBone: использование высокоскоростных выводов

 

Real-time BeagleBone: использование высокоскоростных выводов

Автор: admin от 11-01-2017, 19:30, посмотрело: 183

Real-time BeagleBone: использование высокоскоростных выводов

Здравствуйте, уважаемые хабравчане! Давно уже являюсь читателем Хабра, но до сих пор не мог найти достойной темы для публикации. И вот, наконец, хорошенько прошерстив Хабр и GT, удивился отсутствию публикаций, посвященных программируемой подсистеме реального времени (tutorial на эту тему). Но при схожей цене Edison уступает по производительности и характеристикам.

ВАЖНО! Не все описанные далее режимы работы PRU и не в полном объеме возможно реализовать с помощью BeagleBone из-за физических ограничений топологии платы.

Значительная часть приведенных в публикации материалов является переводом, адаптацией, модификацией или комбинацией ресурсов, приведенных в полезных источниках в конце статьи.

Итак, что же представляет собой подсистема реального времени?

Обзор PRU-ICSS


PRU-ICSS состоит из двух 32-битных ядер, имеющих презентаций и схемы из мануалов.

Real-time BeagleBone: использование высокоскоростных выводов

Основным преимуществом PRU является короткое время доступа к локальным памяти и периферии. В тактах опорной частоты оно даже ниже, чем у подсистемы ARM. Более подробное описание задержек записи/чтения приведено здесь.

Подсистема PRU включает в себя следующие блоки:


  • Два ядра PRU, каждое включает в себя:

    • 8KB памяти инструкций;

    • 8KB памяти данных;

    • Высокоскоростной интерфейс шины Scratchpad memory):

      • 3 блока, в каждом 30 32-битных регистров;

      • Прямой доступ обеспечивает возможность быстрой синхронизации между ядрами PRU;



    • Один контроллер прерываний (INTC):

      • Прием до 64 внешних событий;

      • 10 каналов прерываний;

      • Аппаратная приоритизация событий;



    • Один комплект периферии для промышленного Ethernet:

      • Один таймер с 10 событиями захвата и 8 сравнения;

      • Два сигнала синхронизации;

      • Два 16-битных сторожевых таймера;

      • Цифровые порты ввода/вывода;



    • 12KB памяти общего назначения;

    • Формирование 16 программных событий;

    • Один двухпортовый модуль Ethernet MII;

    • Один порт prudebug и компилятор C.

      Компактное и информативное описание набора инструкций можно найти здесь.

      Области возможного применения


      Как уже отмечалось ранее, основной задачей PRU является разгрузка основного ядра ARM путем выполнения задач, строго ограниченных по времени. Таким задачами могут является реализация протоколов передачи данных или блоков цифровой обработки сигналов. Так на BeagleBone PRU можно реализовать до 25 каналов ШИМ или 4 дополнительных программных UART. Наиболее успешными проектами, демонстрирующими всю мощь PRU, являются BeagleLogic, MachineKit и LEDscape.

      Примеры использования PRU-cape:

      Real-time BeagleBone: использование высокоскоростных выводов

      Установка PRU


      Приступим, наконец, к практической части.

      Итак, экспериментировать буду на имеющейся плате Beaglebone Black ревизии A5C. Использую образ Linux bone-debian-7.8-lxde-4gb-armhf-2015-03-01-4gb.img.xz, загружаемый с внешней SD карточки памяти. Для доступа к BBB использую SSH PuTTY, для обмена файлами WinSCP.

      root@beaglebone:~# uname -a
      Linux beaglebone 3.8.13-bone70 #1 SMP Fri Jan 23 02:15:42 UTC 2015 armv7l GNU/Linux

      В последних версиях ядра для программирования PRU по умолчанию используется фреймворк remoteproc, но его я еще не освоил, поэтому будем использовать библиотеку PRUSSDRV, описанную ранее. Чтобы это стало возможно, включаем модуль:

      root@beaglebone:~# modprobe uio_pruss


      Выполняем предварительные настройки, прописав необходимые переменные среды в автозагрузку:

      echo "export SLOTS=/sys/devices/bone_capemgr.*/slots"  ~/.profile
      #echo "export SLOTS=/sys/devices/platform/bone_capemgr/slots"  ~/.profile 	#для вресий ядра от 4
      echo "export PINS=/sys/kernel/debug/pinctrl/44e10800.pinmux/pins"  ~/.profile
      echo "export PINGROUPS=/sys/kernel/debug/pinctrl/44e10800.pinmux/pingroups"  ~/.profile
      source .profile

      Отключение HDMI/EMMC


      Почти вся периферия PRU выведена на порты, основной функцией которых является HDMI/EMMC. Поэтому для успешного взаимодействия PRU с внешним миром необходимо отключить инициализацию HDMI/EMMC.

      Для используемого образа делается это достаточно просто — необходимо лишь раскомментровать нужную строчку в /boot/uEnv.txt. Поэтому редактируем его nano /boot/uEnv.txt, чтобы в итоге получилось так:

      ...
      ##Disable HDMI/eMMC
      cape_disable=capemgr.disable_partno=BB-BONELT-HDMI,BB-BONELT-HDMIN,BB-BONE-EMMC-2G
      ...

      GPIO:direct mode


      Воспользовавшись информативными таблицами разъемов P8 и P9, можно обнаружить, что в BeagleBone доступно 8 быстродействующих портов вывода для PRU0 и 14 — для PRU1. Как описано ранее, необходимо предварительно отключить HDMI/EMMC и сконфигурировать выводы в нужный режим. Так как хочется выжать побольше из BBB, будем работать с PRU1 и конфигурировать выводы pr1_pru1_pru_r30[0:13]. Также у нас еще остается один незадействованный вход pr1_pru1_pru_r31_16, расположенный на разъеме P9.26. Его используем для запуска программы по внешней кнопочке.

      Оптимальным примером демонстрации работы портов общего назначения PRU, по моему мнению, будет реализация функции программируемой аппаратной задержки по внешнему событию. Задержку будем передавать с помощью пользовательской программы под linux через общую область памяти. PRU непосредственно реализует аппаратную задержку и выводит ее значение, а именно младшие 14 бит, на доступные выводы.

      Таким образом, вырисовывается следующий алгоритм программы:


      1. Запускаем пользовательскую программу с указанием желаемой задержки в качестве аргумента;

      2. Программа инициализирует PRU, передает значение задержки и запускает подпрограмму PRU;

      3. Подпрограмма PRU ждет внешнего события (нажатия кнопки);

      4. После обнаружения события отсчитывает заданную задержку;

      5. Уведомляет пользовательскую программу о завершении посредством прерывания;

      6. Пользовательская программа получает прерывание о завершении работы подпрограммы;

      7. После чего деактивирует PRU и завершается сама;


      Direct GPO device tree overlays


      Итак, для реализации задуманной программы, необходимо настроить 14 портов BBB на вывод и 1 порт на ввод, а также непосредственно запустить подсистему PRU.

      На основе примеров из github'а составил следующее описание дерева устройств:


      Далее этот файл необходимо скомпилировать, скопировать в /lib/firmware и загрузить:

      echo "Compiling the overlay from .dts to .dtbo"
      dtc -O dtb -o PRU_DGPO-00A0.dtbo -b 0 -@ PRU_DGPO-00A0.dts
      
      echo "Copy PRU_DGPO-00A0.dtbo to /lib/firmware"
      cp PRU_DGPO-00A0.dtbo /lib/firmware
      
      echo "Loading overlay:"
      sh -c "echo 'PRU_DGPO' > $SLOTS"

      Затем стоит проверить процесс загрузки.


      Чтобы подробнее разобраться с настройками древа устройств последних релизов Debian, рекомендую обратиться сюда.

      Вообще древо устройств в Linux — это отдельная тема, которая требует глубокого рассмотрения. Интересующиеся могут начать знакомство с Device Tree здесь и здесь.

      Пользовательская программа


      Как сказано ранее, основными задачами пользовательской программы являются:


      • Инициализация PRU;

      • Обмен данными с PRU через общую область памяти;

      • Запуск подпрограммы PRU;

      • Формирование и обработка прерываний и событий PRU;


      Parallel_output.c :

      #include <stdio.h>
      #include <stdlib.h>
      #include <prussdrv.h>
      #include <pruss_intc_mapping.h>
      
      #define PRU_NUM	1   // using PRU1 for these examples
      
      int main (int argc, char* argv[])
      {
         unsigned int ret;
      
         if(getuid()!=0){
            printf("You must run this program as root. Exiting.n");
            exit(EXIT_FAILURE);
         }
      
         if(argc!=2) {
          printf("Usage is Parralel_output and integer number of delay n");
          printf(" e. g. ./Parralel_output 100n");
          return 2;
          }
      
         char *p;
         unsigned int cyc = (unsigned int) strtol(argv[1], &p, 10);
         printf("Delay for %d cyclesn", cyc);
      
         // Initialize structure used by prussdrv_pruintc_intc
         // PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h
         tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
      
         // Allocate and initialize memory
         prussdrv_init ();
         ret = prussdrv_open(PRU_EVTOUT_0);
         if (ret)
         {
             printf("prussdrv_open open failedn");
             return (ret);
         }
      
         // Map PRU's interrupts
         prussdrv_pruintc_init(&pruss_intc_initdata);
      
         // Write a number of cycles into PRU1 Data RAM0
         prussdrv_pru_write_memory(PRUSS0_PRU1_DATARAM , 0, &cyc, 4);
      
         // Load and execute the PRU program on the PRU
         prussdrv_exec_program (PRU_NUM, "./Parallel_output.bin");
      
         // Wait for event completion from PRU, returns the PRU_EVTOUT_0 number
         int n = prussdrv_pru_wait_event (PRU_EVTOUT_0);
         printf("PRU program completed, event number %d.n", n);
      
         // Disable PRU and close memory mappings
         prussdrv_pru_disable(PRU_NUM);
         prussdrv_exit ();
         return EXIT_SUCCESS;
      }

      Компилируем:

      gcc Parallel_output.c -o Parallel_output -lpthread -lprussdrv

      Подпрограмма PRU


      Для написания подпрограммы PRU используется ассемблер PASM. Подпрограмма исполняется на одном из двух ядер PRU независимо от Linux. Синхронизация PRU и Linux осуществляется посредством событий и прерываний.

      Кроме описанного выше алгоритма добавим в подпрограмму еще несколько инструкций, чтобы оценить время их выполнения.

      Parallel_output.p:

      .origin 0                       // start of program in PRU memory
      .entrypoint START               // program entry point (for a debugger)
      
      #define PRU0_R31_VEC_VALID 32   // allows notification of program completion
      #define PRU_EVTOUT_0    3       // the event number that is sent back
      
      START:
              WBC r31.t16             // wait bit clear - i.e., button press
                                      // Toggle 4 times Parallel output pins
              MOV     r30, 0xffff
              MOV     r30, 0x0000
      
              MOV     r30, 0xffff
              MOV     r30, 0x0000
      
              LBCO r30, C24, 0, 4     // load PRU1 Data RAM into r30 (use c24 const addr)
      
      CYCLE:
              SUB     r30, r30, 1     // Decrement REG30 by 1 - i.e., parallel output current value on pins
              QBNE    CYCLE, r30, 0   // Loop to CYCLE, unless REG30=0
      
      END:                            // notify the calling app that finished
              MOV     R31.b0, PRU0_R31_VEC_VALID | PRU_EVTOUT_0
              HALT                    // halt the pru program

      Компилируем:

      pasm -b Parallel_output.p

      Запуск PRU и оценка производительности


      Для того чтобы оценить скорость выполнения инструкций PRU, буду использовать осциллограф Tektronix MSO4032 350MHz с 16-канальным логическим анализатором, имеющим разрешение 2 нс.


      Наконец, запускаем программу:

      root@beaglebone:/home/debian/Desktop/Direct-GPO# ./Parallel_output 15
      Delay for 15 cycles
      PRU program completed, event number 1.

      Так как цикл задержки включает в себя 2 инструкции (SUB и QBNE), каждая по 5нс, то аппаратная задержка будет составлять arg*10нс. Для вышеприведенного примера задержка должна составить 150 нс. Измерения проводятся без учета тестовых начальных инструкций.

      Для наглядности инструкции наложены на осциллограмму:

      Real-time BeagleBone: использование высокоскоростных выводов

      Как видно, время выполнения инструкции MOV в пределах погрешности, обусловленной разрешением логического анализатора — 2нс, и вполне вписывется в заявленные 5нс. Задействованы все 14 выводов и 1 ввод, как и запланировано.

      Выполнение инструкции WBC — ожидание события — занимает ~25 нс, но это время постоянно и его не составит труда учесть при необходимости.

      Далее приведены осциллограммы для разных значений аргументов, измерение задержки производится с помощью курсоров осциллографа.




      Итоги


      Таким образом, с помощью вышеописанного примера продемонстрированы возможности BeagleBone PRU в части формирования сигналов с разрешением до 5нс на 14 выводах одновременно, рассмотрены основные принципы управления и конфигурации PRU, а также программная модель PRU.

      Исходники можно найти здесь.

      Полезные источники


      Значительная часть примеров и принципов работы взята из материалов Derek Molloy:


      Огромное спасибо ему за все это!

      -> Интересный сайт, посвященный применению BBB в CNC и не только.
      -> Подключение интернет на BBB (раз, два и три).
      -> Репозиторий device tree overlay для BB.

      Источник: Хабрахабр

Категория: Программирование, Linux

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
Введите два слова, показанных на изображении: *