четверг, 29 июня 2017 г.

Все необходимое ношу с собой (С++)

Ох уж эти зависимости

Это "удивительная" история связана с тем, что возникла необходимость использования различных библиотек, т.к. периодически возникает необходимость использования логирования, модульных тестов и т.п. в проектах, компилируемых и запускаемых под Linux. Для Windows и ряда дистрибутивов Linux ситуация довольно простая, т.к.

1) Для Windows в Visual Studio проектах можно использовать нативные nuget-пакеты.

2) Для дистрибутивов Linux, в которых есть менеджер пакетов (.deb или .rpm) многие библиотеки и их зависимости могут быть установлены таким способом.

Но есть большое НО:

1.  Не все библиотеки могут быть в менеджере пакетов nuget или в менеджере пакетов Linux (хотя речь, конечно, идет о проектах под Linux).

2. Дистрибутив может быть довольно старым, и в менеджере пакетов нет свежих библиотек.

3. Сам дистрибутив не имеет менеджера пакетов (Arch, Gentoo, различные маленькие и легковесные дистрибутивы).

Как же быть? Примеры работы с различными библиотеками

Самый подходящий вариант - собирать библиотеки вместе с проектом, написать Makefile или bash-скрипт для выполнения этой работы. Как правило проекты библиотек располагаются в директории contrib.

Log4Cpp - неплохая библиотека для логирования сообщений в проектах, имеет богатые настройки и кучу различных аппендеров (менеджеров разных видов записи, например в раскрашенном виде в консоль, в циклически перезаписываемый файл и т.п.). К сожалению библиотека (ее актуальная версия) собраны с использованием autotools, что само по себе уже капкан, т.к. проекты automake не переносимы в пределах разных версий тулсета (во всяком случае мне не встречались такие проекты). liblog4cpp.so отсутствует в моей системе (OpenSuse 42.1), поэтому я использую следующий скрипт (после configure) для сборки:

#! /bin/bash
# Check that configure was not executed
if [ ! -f libtool ]; then
    ./autogen.sh
    ./configure --with-pthreads
fi
# Check that make was not executed
#if [ ! -d ./srx/.libs ]; then
#make
#fi
make clean
make


Gtest - библиотека для написания модульных тестов. Собирается куда как проще, т.к. создана без помощи этих ужасных autotools, ее я собираю следующим способом:

#! /bin/bash
GTEST_DIR=.
#../../contrib/gtests
#=.

rm -rf ${GTEST_DIR}/libgtest*
rm -rf ${GTEST_DIR}/*.o
g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} -pthread -c ${GTEST_DIR}/src/gtest-all.cc
ar -rv ${GTEST_DIR}/libgtest.a gtest-all.o


EasyCmdLineReader - легковесная библиотека для работы с аргументами командной строки. Эта библиотека собирается моим кастомным makefile-ом https://github.com/IzyaSoft/EasyCli/blob/master/Makefile , шаблон для сборки произвольной либы может быть взят отсюда: https://github.com/IzyaSoft/EasyMakeSharedLib . Но данная библиотека легко собирается через просто make (который можно запустить также из шелл скрипта).

Итог

Opensource предоставляет шикарные возможности для контроля используемого в проектах кода, однако он и создает ряд проблем для сборки. Вышеупомянутый способ использования библиотек в проектах, когда нет возможности использования пакетов имеет и ряд недостатков: прежде всего хранение в репозитории дополнительных файлов исходного кода используемых библиотек, сложности в сборки (все тот же autotolls, а также зависимости, которые могут не поддерживаться конкретным Linux дистрибутивом).




воскресенье, 18 июня 2017 г.

Gated clock в HDL

Что такое Gated clock (and/or signal ripple) и его последствия

Недавно занимался одним из своих старых проектов (Quartus 9.1) и, перечитывая, в очередной раз, предупреждения компиляции проекта, обнаружил, что появился такой интересный ворнинг : "Found 1 node(s) in clock paths which may be acting as ripple and or gated clocks -- node(s) analyzed as buffer(s) resulting in clock skew".  Данное предупреждение оказалось связано с моим модулем, управляющим работой двухканальным счетчиком (каждый из счетчиков работает попеременно), сам модуль формирует сигнал адреса и сигналы разрешения и сброса счетчиков.

Прежде всего попытаемся понять, что же означает данное предупреждение. А означает оно то, что на линии тактового сигнала (клока) присутствует логический вентиль (gate). Поскольку мы имеем дело с конкретной микросхемой, а не с математической функцией, то прохождение сигнала через логические элементы всякий раз будет добавлять задержку на время переключения вентиля из 0 в 1 или из 1 в 0. Другая часть предупреждения говорит о том, что наличие вентиля будет приводить к пульсациям. На самом деле, в этом случае могут быть проблемы с переключением на линии клока, например, время переключения вентиля 10 нс, на линии клока пришло переключение из 0 в 1 (posedge), за эти 10 нс, предположим клок переключился из 1 в 0 и, потом, снова из  0 в 1 непрерывно с малыми интервалами, так вот gated clock отфильтрует часть переключений и, как следствие, часть важной информации может быть потеряна.

Как же удалось этого добиться?

Сам удивляюсь как я смог спроектировать такую ерунду, но вот часть кода, описывающая мой модуль.

assign counter0_enable = ~counter_address;
 assign counter1_enable = counter_address;

d_trigger counter0_force_clear_trigger (.clk(counter0_enable), .data(vcc), .reset(counter0_reset), .enable(enable), .out(counter0_force_clear));
- d_trigger counter1_force_clear_trigger (.clk(counter1_enable), .data(vcc), .reset(counter1_reset), .enable(enable), .out(counter1_force_clear));

counter_address генерируется периодически по другому сигналу с помощью Т-триггера. Самое интересное, что к этому эффекту приводит генерация сигналов в counterХ_enable через триггер. И как же быть в такой ситуации? Почитав, что пишут в интернетах и немного подумав, я использовал для клока триггеров исходный сигнал, а операцию выбора работы триггера по адресу перенес в enable (т.е. force_clear сигналы генерируются только в моменты когда enable на триггерах равен 1):

d_trigger counter0_force_clear_trigger (.clk(~channel), .data(vcc), .reset(counter0_reset), .enable(enable & counter1_enable), .out(counter0_force_clear));
+ d_trigger counter1_force_clear_trigger (.clk(channel), .data(vcc), .reset(counter1_reset), .enable(enable & counter0_enable), .out(counter1_force_clear));
На RTL View данная ситуация (gated clock) проявляется следующим образом:










Согласно этому изображению, проблема в шунтировании двух D-триггеров гэйтами.

Заключение
 В HDL бывает много "магии", но при этом всегда нужно внимательно читать все предупреждения, и обязательно смотреть результат синтеза проекта, иначе, даже несмотря на, хороший код, может возникнуть аналогичная ситуация.



суббота, 3 июня 2017 г.

Странности в Verilog-2001


Немного истории или появление ПЛИС и HDL
Ха-ха, здравствуйте-здравствуйте, дорогие читатели. Недавно я писал про странности, связанные с софтом, а именно - Vivado, но дело дошло до самого языка.Вообще для программирования FPGA/PLD/ПЛИС используют языки программирования, позволяющие описать прохождение цифровых электрических сигналов и реакцию цифровых схем на эти сигналы. В данной статье я не буду затрагивать вопросы, связанные с цифровой схемотехникой, булевой алгеброй (алгеброй логики и т.п.). Прообразом ПЛИС стали Программируемые логические матрицы (ПЛМ/PLM), построенные на основе N-в-1 логических элементах (ИЛИ/И), к которым подведены от 1 до N сигналов. Программирование осуществляется с помощью плавления перемычек между входами логических элементов:
Любая логическая функция и любые последовательностные схемы могут быть реализованы на базисных элементах. 

После ПЛМ появились ПЛИС представляющие собой несколько десятков слоев ПЛМ (например, семейство MAX7000). Далее ПЛИС стали сохранять конфигурацию во внутреннюю оперативную память, а конфигурация при старте загружалась с Flash-памяти.

Для описания связей между  логическими элементами (задания какие перемычки нужно включить, а какие убрать) были придуманы языки для описания аппаратуры (HDL): AHDL, VHDL и Verilog.  Наиболее востребованными являются 2 последних, лично я предпочитаю Verilog, т.к. он отдаленно напоминает классику - язык С, а VHDL - Pascal.

Этот странный Verilog

В этом разделе я опишу, пожалуй лишь несколько из странностей Verilog.

1. Доступ к параметрам модуля из функции

Доступ к параметрам  модуля невозможен из функции, например есть модуль:

module my_module #
(
      parameter PARAM_1 = 1
)
(
    // ports defenitions
);

localparam _PARAM_1_LOC = PARAM_1;

function[1:0] some_func();
// local variables defined here ...
      // <!!!!! в этой функции мне недоступен PARAM_1, но доступен PARAM_1_LOC
endfunction;

endmodule

Несмотря на то, что функция some_func принадлежит модулю my_module у нее нет доступа к параметрам модуля, но есть доступ к локальным параметрам.

2. Инверсия порядка бит в байте

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

reg[7:0] byte_vector;

reg[7:0] inversed_byte_vector;
 // где-то внутри always
inversed_byte_vector = byte_vector[0:7]; // ОШИБКА КОМПИЛЯЦИИ

 К сожалению, это не работает, приходится использовать конкатенацию или цикл:

inversed_byte_vector = {byte_vector[0], byte_vector[1], ....byte_vector[7]}


3. Доступ к вектору по индексам

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

reg [31:0] my_vector;
reg [7:0] vector_part;

// где-то внутри always блока
vector_part = my_vector[29:22];

Но доступ по индексам возможен только тогда, когда передаются константы, поэтому приходится городить такие конструкции.

// присвоение внутри функции
reg[7:0] shift;
begin
shift = `MAX_DATA_WIDTH - NUMBER_OF_BYTES * 8;
// потом я не могу вырезать данные так:
result = data[MAX_DATA_WIDTH : shift]; // ошибка

для того, чтобы получить то, что я хочу, я должен сделать так:
result = data[`MAX_DATA_WIDTH - 1 : `MAX_DATA_WIDTH - NUMBER_OF_BYTES * 8];

 4. Чудесный мир литералов

Данная вещь куда как более тривиальна, но менее очевидна, для того, чтобы присвоить двоичное значение какому-нибудь регистру можно его записать как:

my_reg  = 8'b10001111; // Разрядность в битах, одинарная кавычка, латинская "бэ" и бит за битом (старшие биты идут вперед).

Но, что делать если нужно записать значение в 16-ричной системе счисления, т.к. 64 бита писать нереально утомительно, даже если пользоваться конкатом, для этих целей существует представление hex-литералов:

my_reg = 64'hAABBCCDDEEFF0011;
Хоть синтаксис и С подобный, но никаких тебе 0x и т.п.

Это не баги, это фичи
Verilog, конечно же, неплох, для описания HDL, но хочется, чтобы таких неудобств в нем было как можно меньше, учитывая, что последний раз стандарт выходил в 2001 г., а сам язык является наиболее популярным для программирования ПЛИС. Поэтому ждем перемен (с).

Распространение Windows-приложений (Chocolatey)

Менеджеры пакетов для ОС Windows В большинстве дистрибутивов Linux есть свои менеджеры пакетов: в Ubuntu/Mint это apt и deb, в OpenSuse э...