§ Медленный поиск
Часто в жизни возникает ситуация, когда хочется найти первый значимый бит. Сделать это можно двумя методами — быстрым и медленным. Но сначала я расскажу, что я собираюсь искать. Представим, что есть некоторое двоичное число:00001010101000010
Видно, что первые 4 бита у него — нули, это означает, что они ничего не значат. Как найти количество нулевых битов? Очень просто, надо лишь только их подсчитать. Для подсчета 4 битов потребуется 4 такта, а на 5-м такте будет ясно видно, что там 1, и счет закончен. Это медленный поиск, который дает результат, но слишком медленно. Есть гораздо более быстрый поиск.
§ Быстрый поиск
Теперь берем еще один пример числа, 16 битное:0000000000010110
Вместо того, чтобы считать все, делим число пополам (шаг 1):
00000000 00010110
Легко заметить, что первые 8 бит нули, и потому гарантированно 8 первых бит будут учтены как незначимые.
Удаляем эти нули из выдачи и делим правый остаток на 2 части (шаг 2):
0001 0110
А вот теперь первые 4 бита не все нули, так что теперь надо разделить на 2 части левое значение
0001
(шаг 3):00 01
И появились нули, отчего можно добавить +2 к тому +8, что было раньше. Удаляем эти два нуля и делим на 2 то, что осталось (шаг 4):
0 1
А осталось то с гулькин нос, то есть, еще один незначимый нуль. +1 в карму.
Итого, за 4 шага определилось, что нулей впереди +8 +2 +1, аж 11 штук! Это и называется, бинарный поиск, эффективно, и всегда за 4 шага для 16 бит, за 5 шагов для 32 бита.
Есть один интересный нюанс. После того как все шаги были пройдены, может остаться последний 0, 16-й, который тоже можно учесть.
§ Код на верилоге
Теперь надо сделать так, чтобы на выходе модуля был вход, а на выходе — соответственно, выход:1module signbit 2( 3 input [31:0] a, 4 output [4:0] b 5); 6endmoduleТеперь надо выполнить несколько простых шагов, а именно проверить левую часть
a
и в случае чего, получить необходимый результат:1assign b[4] = ~|a[31:16]; 2wire [15:0] t1 = b[4] ? a[15:0] : a[31:16];В b[4] будет 1, если все нули в битах 31..15. На проводе
t1
берется правая или левая часть в зависимости от того, есть ли полностью все нули в левой части или нет.Второй этап будет выглядеть следующим образом.
1assign b[3] = ~|t1[15:8]; 2wire [7:0] t2 = b[3] ? t1[7:0] : t1[15:8];Третий этап:
1assign b[2] = ~|t2[7:4]; 2wire [3:0] t3 = b[2] ? t2[3:0] : t2[7:4];Четвертый этап:
1assign b[1] = ~|t3[3:2]; 2wire [1:0] t4 = b[1] ? t3[1:0] : t3[3:2];И наконец, для пятого этапа ничего не требуется, кроме разве что подсчета оставшегося бита:
1assign b[0] = ~t4[1];Если он будет 0, то учитывается, иначе не учитывается.
Несмотря на то, что метод получился гораздо быстрее, все же требуется каскад из 5 слоев для того, чтобы подсчитать количество нулевых бит вначале. Аналогично можно считать и количество единиц, для этого нужно переделать функцию определения с
~|
на &
.§ Полный код модуля
For Commercial Purposes Only — гласит надпись на вывеске, но я просто оставлю это здесь.1module signbit 2( 3 input [31:0] a, 4 output [4:0] b 5); 6 7wire [ 4:0] b = {~|a[31:16], ~|t1[15:8], ~|t2[7:4], ~|t3[3:2], ~t4[1]}; 8wire [15:0] t1 = b[4] ? a[15:0] : a[31:16]; 9wire [ 7:0] t2 = b[3] ? t1[7:0] : t1[15:8]; 10wire [ 3:0] t3 = b[2] ? t2[3:0] : t2[7:4]; 11wire [ 1:0] t4 = b[1] ? t3[1:0] : t3[3:2]; 12 13endmoduleТакова жизнь, ничего не поделаешь. Я даже не проверял эту схему, а надо бы.