Pull to refresh

Comments 15

Павел, спасибо за статью!
Но как сделать быстрый мастер – мы уже знаем.

А какой мастер вы имеете ввиду?

Ну, и перекачка данных через JTAG будет идти медленнее, чем через FT245. Но опять же, мы уже знаем, как перекачивать всё через FT245.

Не очень понятно, а почему вы здесь сравниваете FT245 и JTAG?
А какой мастер вы имеете ввиду?
Мастер из процессорного ядра Nios II. Он был впервые применён ещё в самой первой статье цикла и дальше кочует из статьи в статью.
Не очень понятно, а почему вы здесь сравниваете FT245 и JTAG?
Протокол FT245-SYNC заложен, как штатный для сязи ПЛИС с центральным процессором Redd (реализован на чипе FT2232H). Работа с ним была рассмотрена ранее, тогда же сделаны замеры его скорости. Первое рассмотрение — во второй части тут, далее — тут.
Т.е. теоретически можно вместо модуля Altera JTAG-to-Avalon-MM сделать и использовать мост FT245-SYNC-Avalon?
Не совсем. Если надо быстро прокачивать данные из ОЗУ, подключённого к ПЛИС (в планах следующих статей — сделать анализатор, который будет туда эти данные загонять на высокой скорости). то для этого в аппаратуру комплекса Redd заложен чип FT2232, работающий в режиме FT245-SYNC.

Но как было видно в статьях по ссылкам, данная функциональность несколько сложна в добавлении. Зато скоростная. Если же скорость обмена не критична (например, данных не очень много) — можно воспользоваться работой через JTAG канал, описанный в этой статье. Выигрываем в скорости разработки и в свободной памяти ПЛИС, проигрываем в скорости работы.

А дальше — каждый решает, что ему важнее в данный конкретный момент. Владея обоими методами, можно оптимизировать работу под каждый конкретный случай.
Но вприницпе это могут быть взаомно заменяемые вещи?
Что через JTAG можно в память напрямую писать что через FT245-SYNC.
Не совсем взаимо заменяемые. Они совершенно заменяемые в одну сторону и заменяемые с некоторыми усилиями — в другую.

JTAG канал встроен в ПЛИС от её рождения. Описанная тут функциональность — встроена в среду разработки. Как видно, в рамках данной статьи мы не написали ни одной строчки своего кода ни для ПЛИС, ни для ЭВМ. Нам хватило всего встроенного.

FT245-SYNC добавлен в комплекс Redd потому, что мы его выбрали. Мы заложили в схему этого комплекса чип FT2232H. Поэтому чтобы его поддерживать, в статьях, на которые я дал ссылки выше, я сначала проектирую автомат, затем — делаю его реализацию на языке Verilog, затем — оборачиваю его в компонент, затем — вставляю в систему, затем — добавляю немного штатной обвязки, затем — связываю всё это. И в итоге — у меня получается FIFO, к которому имеет доступ только программа для процессора NIOS II. Но так же я могу написать мастера, имеющего доступ к Memory Mapped шине. Но лично я — не буду этого делать. Сил много потрачу. Проще ограничиться FIFO и брать оттуда данные программно, либо программно же класть их туда.

Как видите, технически всё возможно, но реально всё похоже на анекдот:

— Доктор, я после операции буду играть на скрипке?
— Конечно будете!
— Спасибо, доктор, а то я раньше не умел.

Вот если приложить массу усилий, то FT245-SYNC к шине AVALON-MM приладить можно (надо долго учиться играть на скрипке). Зато всё будет идеально. Описанную же в этой статье функциональность сделала для нас «мать-природа» в лице инженеров Альтеры. Но она — смешная по производительности. Зато не надо мучиться.

То есть, технически — всё заменяемо. Но трудозатраты — не сопоставимы. Поэтому я не скажу, что всё взаимо заменяемо. Хотя бы потому, что одна из функций — врождённая в систему, а вторая — нет.
как сделать идеальную программу, перехватывающую оба потока, я пока не знаю. Если кто-то приведёт в комментариях готовые решения, я буду благодарен. В первую очередь – под Debian, чтобы не гонять большие потоки данных по сети.

#include <stdlib.h>
#include <unistd.h>

#define BUFFER_SIZE (4096)

int main(int argc, char **argv) {
    int pipes_in[2] = {-1};
    int pipes_out[2] = {-1};
    if (pipe(pipes_in) != 0) {
        perror("Can't create input pipes pair!");
        exit(EXIT_FAILURE);
    }
    if (pipe(pipes_out) != 0) {
        perror("Can't create output pipes pair!");
        exit(EXIT_FAILURE);
    }
    int fork_result = fork();
    if (fork_result == 0) {
        close(pipes_in[1]);         // Close unused write end
        close(pipes_out[0]);        // Close unused read end
        dup2(pipes_in[0], 0);   // Change stdin to a pipe
        dup2(pipes_out[1], 1);  // Change stdout to a pipe
        close(pipes_in[0]);         // Cleanup opened FDs
        close(pipes_out[1]);        // Cleanup opened FDs
        // Start slave binary
        execlp("cat",  "cat"); // To communicate with local process
//        execlp("nc", "nc", "127.0.0.1", "1234"); // To communicate over network (to 127.0.0.1:1234)
    } else if (fork_result < 0) {
        perror("Fork failed!");
        exit(EXIT_FAILURE);
    }
    // Master process goes here
    close(pipes_in[0]);          // Close unused read end
    close(pipes_out[1]);         // Close unused write end

    // Open pipe end as FILE* for fgets()/fputs()
    FILE *pipe_from_slave = fdopen(pipes_out[0], "r");
    if (pipe_from_slave == NULL) {
        perror("Can't reopen pipe from slave as FILE*");
        exit(EXIT_FAILURE);
    }

    FILE *pipe_to_slave = fdopen(pipes_in[1], "w");
    if (pipe_to_slave == NULL) {
        perror("Can't reopen pipe to slave as FILE*");
        exit(EXIT_FAILURE);
    }

    char buffer[BUFFER_SIZE];
    while (fgets(buffer, BUFFER_SIZE, stdin)) {
        printf("Master: Got line from console: %s", buffer);
        fprintf(pipe_to_slave, "%s", buffer);
        fflush(pipe_to_slave);
        printf("Master: Wrote line to slave: %s", buffer);
        if (fgets(buffer, BUFFER_SIZE, pipe_from_slave) != NULL) {
            printf("Master: Got line from slave: %s", buffer);
        } else {
            perror("Failed to read from slave");
            exit(EXIT_FAILURE);
        }
    }
    exit(EXIT_SUCCESS);
}
Жуть!

Спасибо! Буду пробовать!

Основной смысл задачи — создать два пайпа (первый на передачу master -> slave, второй на передачу slave -> master), форкнуться и сразу же позакрывать в обоих форках неиспользуемые стороны пайпов (у master'а — read end у первого пайпа и write end у второго, у slave'а — наоборот).


Затем у slave'а надо превратить соответствующие концы пайпов (оставшиеся открытыми) в stdin и stdout вызовами dup2 (read end первого пайпа должен стать stdin, write end второго пайпа — stdout) и оригинальные дескрипторы тоже закрыть (чтобы не маячили), после чего запустить вызовом exec** уже реальный slave-процесс, который унаследует stdin и stdout как пайпы от и к master'у.


А master тем временем уже может читать из read end второго пайпа и писать во write end первого пайпа.

Если получится, то напишите пожалуйста, очень интересно :)
Спасибо, интересно!
Я так понимаю, это только на Unix системах работает?
Для Windows есть пример в MSDN. Ищем описнаие _popen, а оттуда есть ссылка.

Но в рамках цикла статей важнее Linux (Debian) вариант. Потому что система запускается на ПЛИС, физически подключённой к ЭВМ с Линуксом. Значит, управляющий код на той ЭВМ и должен исполняться. Так будет производительнее всего.
Мне лично и для Windows важно :)
Еще желательно кросплатформенно, на сколько это возможно
Ну, наверное, обернуть всё в класс с универсальным интерфейсом и сделать так, чтобы всё собиралось по #ifdef, в зависимости от ОС. Пользователи класса не будут знать, какой версией пользуются, а сам класс будет собираться — под конкретную ОС.

Или сделать библиотеку с универсальным интерфейсом. Собрать версии библиотеки под каждую ОС и подключать их к проектам. А пользователи, опять же, не будут знать, к кому обращаются. Главное — чтобы интерфейс был универсальным.

Но это — уже выходит за рамки как статьи, так и цикла.
Я нечаянно добавил комментарий не на тот уровень. Перенёс на нужный, а этот — затёр.
Sign up to leave a comment.

Articles