spamsink: (Default)
[personal profile] spamsink
Даже не знаю, кому будет интересно. Математикам банально, программистам неактуально, электронщикам и так известно, остальным пофиг. Поэтому ...

Допустим, что нам для некоторого эксперимента нужно считать, сколько раз случилось каждое из многих десятков или сотен тысяч событий. Для этого мы разрабатываем специализированные чипы, каким-то умным образом анализирующие сырые данные эксперимента и получающие внутри себя много сигналов "случилось такое-то событие".
События могут происходить в произвольных сочетаниях - хоть все сотни тысяч одновременно, и - по сравнению с тактовыми частотами современных процессоров, - весьма часто. Это значит, что к схеме тех специализированных чипов придется добавить много блоков, которые только и умели бы, что по сигналу "произошло событие" как можно быстрее прибавлять 1 к счетчику (ну и по управляющему сигналу позволяли бы сбрасывать счетчик в ноль, одновременно выталкивая его текущее содержимое в порт - надо же нам как-то узнавать, до чего они там досчитали, но эту деталь функциональности мы пока для простоты опустим).

Как будет выглядеть такой блок? Он будет состоять из двух частей: регистра, где хранится текущее значение счетчика, и сложителя, прибавляющего 1 к счетчику. У регистра есть два входа - сигнал и новое значение. В момент прихода сигнала содержимое регистра заменяется новым значением. В сложитель же входит текущее значение регистра, а выходят из него провода, несущие "новое значение":
reg [63:0] counter;
wire [63:0] new_value = counter + 1; // Сложение происходит "непрерывно"
always @(signal) begin // Каждый раз, когда приходит сигнал (верилоговцы меня простят)
    counter = new_value;
end

Таким образом, если события будут приходить с интервалом не меньшим, чем время, необходимое сложителю для прибавления единицы к, например, 64-битному числу, то наш регистратор будет работать.

Посмотрим теперь, как сложитель устроен внутри. Понятно, что из четного числа получается нечетное, и наоборот: new_value[0] = !counter[0];

Каждый следующий бит изменяется на противоположный, если все биты правее него были равны единице, или, что то же самое, если логическое И от всех этих битов равно единице:
for (int i = 1; i < 64; i++) // Декларативный цикл, описывающий подключение отдельных битов new_value
    new_value[i] = counter[i] ^ (counter[i-1:0] == (1 << i) - 1);

Это довольно быстро становится или дорого с точки зрения количества необходимых логических элементов - если мы будем вычислять все частичные логические И параллельно, их потребуются сотни, или недопустимо медленно - если мы будем экономить и вычислять их по цепочке:
wire [63:0] ands;
assign ands[0] = counter[0];
for (int i = 1; i < 64; i++) begin
    new_value[i] = counter[i] ^ ands[i-1];
    if (i != 63) ands[i] = ands[i-1] & counter[i];
end

Да и то понадобится по два элемента (XOR и AND) почти на каждый бит счетчика.

Как же быть? Как сделать счетчик событий так, чтобы он, кроме собственно регистра, использовал самый минимум логических элементов?

Date: 2010-09-24 07:38 am (UTC)
From: [identity profile] potan.livejournal.com
Параллельно - это сумматор с распространением переноса? Его использованием будет непозволительной роскошью по использованию элементов?

Самое простое решение - делать несколько инкриметров параллельно. То есть пока расчитывается перенос от одного инкримента из n-ного разряда, можно уже начинать высчитывать перенос их n-1-го разряда для следующего. Правда схема чтения сильно усложниться.

Можно пропускать события через ряд делителей. Схема чтения упрощается, но фактически становятся недоступны несколько младшие несколько бит.

Date: 2010-09-24 08:00 am (UTC)
From: [identity profile] potan.livejournal.com
Распространение переноса требует логорифмической глубины и ширины.

Параллельно может работать и отдельный инкрементатор, если второе событие пришло когда первое еще не до конца обработано - младший бит инвертировался, но перенос еще до старшего не дошел.

Date: 2010-09-24 07:47 am (UTC)
From: [identity profile] kcmamu.livejournal.com
А зачем все биты счетчика обновлять одновременно? Пусть каждый следующий бит переноса запаздывает на такт относительно предыдущего.

Или считай через LFSR -- вообще один XOR на все про все. Только потом ответ нетривиально будет расшифровать...

Date: 2010-09-27 08:00 am (UTC)
From: [identity profile] potan.livejournal.com
Интересная мысль...
Можно счетчик команд в процессоре таким же образом инкрементировать. :-)

Date: 2010-09-24 07:58 am (UTC)
From: [identity profile] kcmamu.livejournal.com
> На такт чего? События асинхронные.

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

Date: 2010-09-24 08:02 am (UTC)
From: [identity profile] kcmamu.livejournal.com
А чтобы LFSR расшифровывать проще -- берем не один длинный, а несколько коротких с взаимно простыми периодами.

Date: 2010-09-24 11:13 am (UTC)
vak: (Default)
From: [personal profile] vak
Почему бы не использовать арифметику по модулю какого-нибудь полинома, например x^64+1? Вместо сумматора будет сдвиговый регистр с небольшим количеством обратных связей.

Date: 2010-09-25 08:33 am (UTC)
vak: (Default)
From: [personal profile] vak
LFSR, ага. Я англоязычный термин не вспомнил. Надо просто подобрать хороший (примитивный) полином. Для чётных степеней будет минимум три XOR-a, для нечетных - два.

Date: 2010-09-25 09:31 am (UTC)
vak: (Default)
From: [personal profile] vak
Да, действительно... Забавная штука арифметика. :)
Для 63-й, 31-й и 15-й степеней есть полиномы с одним XOR-ом. Вполне практично.

Date: 2010-09-24 06:05 pm (UTC)
From: [identity profile] http://users.livejournal.com/_navi_/
skew binary использовать?

Date: 2010-09-24 06:30 pm (UTC)
From: [identity profile] http://users.livejournal.com/_navi_/
ну, как я понимаю, меньше чем с двоичным представлением :-)

А воообще, спасибо, прочитал про LFSR, очень интересно. Кстати, есть какой-то простой метод декодирования из LFSR в порядковый номер?

Date: 2010-09-24 06:55 pm (UTC)
From: [identity profile] http://users.livejournal.com/_navi_/
а троичная логика вообще как-то возможна на доступной элементной базе на кремнии без использования пар битов для представления тритов? Я помню, что в детстве что-то читал про советский троичный компьютер.

То есть, даже учитывая линейность преобразования, не брут форсом всё равно не имеет смысла декодировать?

Date: 2010-09-26 05:09 am (UTC)
From: [identity profile] malaya-zemlya.livejournal.com
Я наверное чего-то не понимаю, но зачем наворот с LFSR? Сдвиги они тоже элементов требуют.

Очевидно, чтоб досчитать до N нужно минимум O(log(N)) элементов. Ну хотя б чтобы ответ выводить.
Вполне достаточно каскада флип-флопов. При поступлении сигнала на вход состояние счетчика переключается между 0 и 1, а каждый второй раз посылается сигнал на следующую стадию.
Если мои смутные воспоминания об электронике верны, то на все про все требуется 2 транзистора. Куда уж меньше-то.

Date: 2010-09-26 05:42 am (UTC)
From: [identity profile] malaya-zemlya.livejournal.com
:считывать значение из каскада сложнее

?????
У него состояние заведено выход - бери и пользуйся.
Клоки помойму вообще не нужны - бывают асинхронные флип-флопы.

Date: 2010-09-27 07:58 am (UTC)
From: [identity profile] potan.livejournal.com
Асинхронные считывать сложно. Требуется довольно много оборудования, что бы сччитать все разряды синхронно и корректно.

Profile

spamsink: (Default)
spamsink

February 2026

S M T W T F S
12345 67
8 91011 121314
15161718 192021
22 2324 25262728

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Mar. 6th, 2026 05:30 am
Powered by Dreamwidth Studios