ПЛИС-культ привет, хабрунити! Задумывались ли вы когда-нибудь над тем, что может быть общего у банковской карточки, IMEI телефона и вагона РЖД? В этой статье вы найдете ответ на этот вопрос и увидите его реализацию для ПЛИС.  Казалось бы, обычная пластиковая карта.  Мы используем её ежедневно при оплате заказов на разного рода онлайн магазинах, при оплате штрафов в ГИБДД или для отправки донатов. Но задумывались ли вы над тем, как формируется сам номер вашей карты и что он фактически означает?  Честно говоря, я никогда, но до определённого момента … момента, когда алгоритм ютуба выдал мне ролик с канала VitalMath. Что было интересного в этом пятиминутном ролике о жизненной математике? В видео был ответ на один интересный вопрос: каким образом форма ввода номера банковской карты знает, что он был введён корректно? С одной стороны, казалось бы, ну что особенного в наш век интернета: вводишь номер, он отправляется на сервер, сравнивается с местной базой данных и выдаёт ответ. Вроде бы логично, но нет. Никакой запрос к базе данных не осуществляется, тогда каким же образом определяется корректность ввода? А определяется он с помощью достаточно простой, в плане реализации, математики, которую придумал ещё в 1954 году Хансон Питер Лун. Этот алгоритм проверки корректности определенной последовательности цифр с помощью контрольной цифры получил название по фамилии автора и более широко известен как Алгоритм Луна. Он не требует больших вычислительных затрат и позволяет на лету обнаружить ошибку при вводе данных. Да, алгоритмов проверки подобного рода существует несметное количество, но в этой статье мы разберем реализацию именно Алгоритма Луна на ПЛИС, который, к слову, не лишён недостатков, про которые вы можете узнать подробнее в той же википедии. Алгоритм Луна широко используется в нашей повседневной жизни и послужит нам отправной точкой к серии статей, посвященных реализации математических алгоритмов на ПЛИС. Для начала рассмотрим сам алгоритм. Сразу отмечу, что существует два вида Алгоритма Луна: обычный и упрощенный, в чем между ними разница я скажу чуть позднее. Итак предположим, что у нас есть некоторая последовательность цифр, например дата основания комьюнити FPGA-Systems : первое апреля 2017 года, для которой надо определить контрольное число по Алгоритму Луна. Что нам для этого нужно сделать? Обычный Алгоритм Луна  смотрим сколько цифр в нашей последовательности 01042017. Последовательность содержит четное количество цифр, а именно 8 Если количество цифр чётное, то для всех цифр в четной позиции делаем умножение на два (индексация цифр начинается с 1) 1     2      3      4      5     6      7      8   --индекс 0     1      0      4      2     0      1      7   --последовательность       1x2          4x2         0x2          7x2  0     2      0      8       2    0       1    14 Если на втором шаге число после умножения на 2 оказалось больше 9, то заменяем его на сумму цифр этого числа или вычитаем 90     2      0     8       2     0       1    1+40     2      0     8       2     0       1     5 Теперь складываем все полученные числа0+2+0+8+2+0+1+5 = 18 Дополняем до ближайшего числа в большую сторону, которое делится на 10.В нашем случае мы получили 18, значит ближайшее кратное 10 будет 20. Вычитаем из 20 нашу сумму 18 и получаем 2. Это и будет контрольная цифра для нашей начальной последовательности 01042017 Теперь приставляя к начальной последовательности 01042017 контрольную цифру 2 получаем последовательность, в которой есть проверка на возможную ошибку при неправильном вводе 010420172 Что же делать если в последовательности количество цифр нечётное? Делается абсолютно тоже самое, но для цифр в нечетной позиции, например: Ближайшее большее кратное 10 к 24 это 3030 - 24 = 6 - контрольная цифра для 57395Проверить можно, например, здесь. Проверка последовательностей содержащей контрольную цифру выполняется во много также, но в расчёт пока не берется контрольная цифра Проверка    1     2      3      4      5     6      7      8           --индекс 0     1      0      4      2     0      1      7      2   --последовательность с контрольной цифрой 2       1x2          4x2         0x2           7x2  0     2      0      8       2    0      1      140     2      0     8        2    0      1      1+40     2      0     8        2    0     1       50 +  2  +  0 +  8  +   2 +  0 +  1  +  5 = 18К полученной сумме прибавляем контрольную цифру 218 + 2 = 20 -- если получилось число, кратное 10, то последовательность верна Все до банального просто. Еще раз отмечу, что Алгоритм Луна имеет свои недостатки и не способен обнаружить некоторые ошибки. Упрощенный Алгоритм Луна В варианте упрощенного алгоритма всегда следует двигаться справа-налево, то есть с последней цифры последовательности через одну. При выполнении проверки не берется контрольная цифра, только цифры проверяемой последовательности. Контрольная цифра всегда используется только на последнем шаге для сложения с результатом промежуточной суммы. Это позволяет избавиться от необходимости определять из скольких цифр состоит последовательность (из четного или нечетного), но с другой стороны требует передавать/принимать последовательность начиная с последней цифры. Но в целом это не является какой-либо проблемой. Делать мы будем проверятор последовательностей по обычному Алгоритму Луна, то есть на вход нам будет поступать последовательность с контрольной цифрой, на языке VHDL. Приведу примеры нескольких вариантов реализации. PS: реализацию упрощенного алгоритма и реализации на Verilog/SystemVerilog оставляю вам в качестве домашнего задания. Вариант 1  Для начала определим внешний вид и порты ввода вывода нашего модуля. Модуль будет принимать цифры последовательно и проверять последовательность мы будем на лету, это значит, что цифры нужно будет подавать последовательно. Одну за одной начиная с первой. Быстренько опишем входные и выходные порты, а также подключим необходимые стандартные библиотеки Чтобы сэкономить немного ресурсов мы пойдем на небольшую хитрость. Как вы помните, если число больше 9, после умножения на 2, то нужно сложить две цифры этого числа. Но мы знаем, что число будет в диапазоне 10 – 18, это от  5x2 до 9x2. Других вариантов нет, поэтому мы заранее вычислим умножение на 2, просуммируем цифры и сохраним значения в массив, то есть в память, которая за малым числом элементов скорее всего будет распределенной, то есть реализованной на LUTах.  Для сохранения промежуточного результата суммирования удвоенных и обычных значений мы будем использовать переменную tmp. Я позже поясню, почему максимальное значение промежуточной суммы не может быть больше 18. Определим переменную для хранения номера текущей цифры в последовательности   И в конце определим конечный автомат, который и будет управлять всеми вычислениями. Подготовительная часть закончена, переходим к функциональной Нам будет достаточно всего одного синхронного процесса. По сигналу сброса переводим все компоненты в начальное состояние. Затем следует сам конечный автомат: Состояние s0 является состоянием ожидания сигнала начала последовательности и в этом состоянии мы переводим все сигналы и порты в начальное значение.  Состояние s1 используется для вычисления суммы с удвоенным числом  Состояние s2 используется для простой суммы Состояние s3 используется для проверки кратности 10 полученного результата Так стоп! Как проверить кратность 10 в двоичной системе? Я никогда не слышал о таком алгоритме, находил алгоритмы кратности другим значениям, но не 10. Мое первое очень зашкварное решение на стриме было просто создать массив из чисел, кратных 10 и проверять полученный результат со значениями в таблице. Но такой вариант подходил только для простых тестов. Но на стриме во время появился Евгений и предложил следующий вариант: Еще раз спасибо Евгению за идею! Если промежуточная сумма, которую мы храним в переменной tmp будет больше 10, то мы сразу же будем вычитать 10, в противном случае просто складывать с простой цифрой или со значением из таблицы (это действие описано в s1 и s2). По этой причине диапазон значений переменной tmp задан как 0 to 31, ведь ее значение не может быть больше 18 (9+9).  После короткого лирического отступления продолжаем наполнять конечный автомат смыслом. Одновременно с сигналом старт, следует передать количество цифр в проверяемой последовательности, за исключением контрольного числа.  Здесь же определяем четность количества цифр в последовательности. Как мы знаем в двоичном коде все четные числа имеют в младшем разряде 0, а не четные 1.  Здесь же в зависимости от четности не четности проверяем есть ли на входе одновременно  с сигналом istart данные и выполняем присвоение промежуточной сумме значение из таблицы если последовательность из нечетного количества цифр и просто цифру если последовательность из четного количества цифр. И не забываем про увеличение переменной k, отвечающей за общее количество принятых цифр последовательности. В состоянии s1 ждем появления сигнала валидности данных и складываем предыдущий результат суммы с удвоенным из таблицы. Сразу проверяем является число последним в последовательности, сравнивая переменную к со значением длины последовательности. Если последовательность не окончена, то, переходим в следующее состояние s2, противном случае переходим в s3 и смотрим на кратность. В состоянии s2 мы просто складываем цифру с предыдущим результатом и опять проверяем является ли число последним. В состоянии s3 ждем проверяем проверку на кратность 10 полученной суммы. Благодаря тому, что мы вычитали 10 каждый раз как только переменная tmp была больше 10, нам нужно сравнить полученный результат только с 0 и 10.  Тестбенч Остаётся только сделать небольшой тестбенч для проверки. Я объявил неограниченный массив из натуральных чисел, в которые заносил последовательности для проверки А саму подачу воздействия на тестируемый модуль решил сделать через процедуру. Такое решение мне показалось более менее универсальным. Я сделал две процедуры: одна для проверки одновременно пришедших с сигналом istart данными и для случая, когда данные приходят после сигнала старт через несколько тактов.   Эти процедуры вызываются в теле процесса и задают входные воздействия на тестовый модуль  Исходники также доступны на git Проверку написанного на временных диаграммах оставляю на ваше усмотрение. Вариант 2 Во втором варианте реализации я решил избавиться от необходимости передавать количество цифр в последовательности и заменил этот порт на два других Сам код претерпел лишь небольшие косметические изменения, но его функциональность осталось прежней  Также соответствующие изменения я внес в файл тестбенча  Исходники также доступны на git Проверку написанного на временных диаграммах оставляю на ваше усмотрение. Я привел пару реализаций Алгоритма Луна на VHDL, которые, разумеется можно и нужно улучшить как стороны поведения самого модуля так и со стороны тестбенча. За рамками статьи остались реализация упрощенного Алгоритма Луна, реализация на Verilog/SV и многое другое. Но этот алгоритм крайне прост и понятен, поэтому вполне годятся в качестве практики начинающим плисоводам. На очереди, алгоритм Верхоффа, реализацию которого мы уже запилили на субботнем FPGA стриме. Ссылки Алгоритм Луна wiki Исходники на git Видео про реализацию на ПЛИС Стрим, на котором я делал Алгоритм Луна Онлайн проверятор последовательностей  Вождь FPGA комунити @fpgasystems