nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Мучаем 5576ХС4Т - часть 'h31 - ускоренные сумматоры

Часть 0 - покупаем, паяем, ставим драйвера и софт
Часть 1 - что это вообще за зверь?
Часть 2 - наша первая схема!
Часть 3 - кнопочки и лампочки
Часть 4 - делитель частоты
Часть 5 - подавление дребезга кнопки
Часть 6 - заканчиваем кнопочки и лампочки
Часть 7 - счетчики и жаба
Часть 8 - передатчик UART
Часть 9 - Hello, wolf!
Часть 'hA - приёмник UART
Часть 'hB - UART и жаба
Часть 'hC - полудуплексный UART.
Часть 'hD - МКО (МКИО, Mil-Std 1553) для бедных, введение.
Часть 'hE - приёмопередатчик МКО "из подручных материалов" (в процессе)
Часть 'hF - модуль передатчика МКО
Часть 'h10 - передатчик сообщений МКО
Часть 'h20 - работа с АЦП ADC124s051
Часть 'h21 - преобразование двоичного кода в двоично-десятичный (BCD)
Часть 'h22 - Bin2Bcd с последовательной выдачей данных
Часть 'h23 - перемножитель беззнаковых чисел с округлением
Часть 'h24 - перемножитель беззнаковых чисел, реализация
Часть 'h25 - передаём показания АЦП на компьютер
Часть 'h26 - работа над ошибками (быстрый UART)
Часть 'h27 - PNG и коды коррекции ошибок CRC32
Часть 'h28 - передатчик изображения PNG
Часть 'h29 - принимаем с ПЛИС изображение PNG
Часть 'h2A - ZLIB и коды коррекции ошибок Adler32
Часть 'h2B - ускоряем Adler32
Часть 'h2C - формирователь потока Zlib
Часть 'h2D - передаём сгенерированное PNG-изображение
Часть 'h2E - делим отрезок на равные части
Часть 'h2F - знаковые умножители, тысячи их!
Часть 'h30 - вычислитель множества Мандельброта
Часть 'h31 - ускоренные сумматоры
Часть 'h32 - ускоренные счётчики (делаем часы)
Часть 'h33 - ускоряем ВСЁ
Часть 'h34 - ускоренные перемножители
Часть 'h35 - умножители совсем просто
Часть 'h36 - уравновешенный четверичный умножитель


Все рассмотренные в предыдущих главах сумматоры с шириной аккумулятора 32 бита (и перемножители на их основе) синтезируются без проблем для кристаллов EPF10K200SRC240-1 и EPF10K200SRC240-2 (более "шустрые" чипы), но в случае EPF10K200SRC240-3 мы нарываемся на Critical warning: слишком велико время распространения сигнала, вместо требуемой частоты в 80 МГц мы можем получить разве что 57 МГц. А именно этому кристаллу (согласно "инструкции по программированию") соответствует Воронежский 5576ХС4Т и 5576ХС4Т1...

И увы, "вычислитель множества Мандельброта" хорошо заработал на симуляторе, на "быстром" кристалле, но основательно заглючил на реальной ПЛИС - она действительно не самая шустрая, и соответствует в лучшем случае "-2", а может и действительно "-3".

Не уверен, что нам действительно стоит гонять её на частоте в 80 МГц, в своих изделиях мы использовали 50 МГц, но именно такой генератор стоит в макетке, да и в целом получить запас по частоте - всегда неплохо! Так что посмотрим: можно ли осуществить 32-битное суммирование быстрее, чем это синтезируется "по умолчанию", когда используешь знак "+" или модуль lpm_add_sub.


По умолчанию используется самый простой сумматор, который в английском называется ripple carry adder, сумматор с последовательным переносом. Это полный аналог сложения в столбик: мы начинаем с самых младших разрядов и тащим переносы в старшие. Если у нас было двоичное число 11111111, а мы прибавили единичку, то переносы дойдут до самого старшего разряда, давая 100000000. При этом, с ростом размерности будет линейно расти задержка распространения.

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

Поэтому классические схемы ускоренного переноса не сильно нам помогут - они потребуют добавить к сумматору логику ускоренного переноса, которая теоретически быстрее, но будет реализована на логических элементах в "нормальном режиме" с цепями каскадирования, которые сами по себе втрое медленнее цепей переноса, что съест любые преимущества, которые могли бы дать данные схемы.

В части 'h2B мы практически "промоделировали" эту ситуацию - как оказалось, 16-битный сумматор "обычного исполнения" позволяет работать на 80 МГц, если сигнал не проходит через другие комбинаторные цепи (т.е выходы регистров идут на входы сумматора, а выход сумматора снова поступает в регистры), а попытка добавить хотя бы компаратор (а это как раз требует 4 ЛЭ, соединённых через цепи каскадирования) сразу "заваливает" нам тайминги.

Для примера напишем модуль "аккумулятора":
module Accum32 (input [31:0] B, input clk, input sclr, output [15:0] Q);

reg [31:0] Acc = 1'b0;

always @(posedge clk)
	Acc <= sclr? 1'b0 : Acc + B;

assign Q = Acc[31:16];


endmodule


То есть, мы можем обнулить аккумулятор, а затем на каждом такте начать прибавлять числа со входа B. Старшие 16 бит мы наблюдаем с выхода Q (чтобы не сильно "нервировать" Place&Route, он не шибко любит 32-битные выходы, идущие с близко расположенных элементов).

Он синтезируется в 65 ЛЭ - 32 из них для регистра Acc, ещё 32 - для сумматора. Куда ещё один уходит - понятия не имею, но это не так важно. Сигнал с регистра проходит через сумматор и возвращается в регистр - комбинаторный путь настолько короткий, насколько возможно. Но увы, Timing Analyzer показывает задержку в 17,5 нс (57,14 МГц) вместо требуемых нам 12,5 нс (80 МГц). Такая задержка происходит от Acc[0] до Acc[31], по цепям переноса - как мы и ожидали.

Но что хорошо: логические элементы, формирующие регистр Acc, остались "недогружены" - у нас есть 4 входа в каждый генератор функций (ГФ, он же LUT), тогда как используется только два - по одному идут новые данные, а по второму - сигнал синхронного сброса.

Поэтому нас спасёт схема Carry Select Adder (сумматор с переключением переноса): мы разбиваем сумматор на несколько секций (в конкретном случае - на две по 16 бит). Младшая секция - самая обычная, даёт 16-битный результат и бит переноса.

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

Таким образом, каждый из сумматоров может "начать работать" сразу по фронту тактового импульса, не дожидаясь, пока до них "доползёт" бит переноса. А бит переноса лишь укажет регистру, какой же из двух результатов загрузить.

Вместо обычного модуля сумматора lpm_add_sub (или вообще неявного вызова сумматора с помощью оператора "+") напишем такого монстрика:

module CarrySelectAdd32 (input [31:0] A, input [31:0] B, input cin, output [31:0] Q, output cout);

wire IntCout;
lpm_add_sub BL0 (	.cin (cin),
			.dataa (A[15:0]),
			.datab (B[15:0]),
			.cout (IntCout),
			.result (Q[15:0]));
defparam
	BL0.LPM_WIDTH = 16;
					
wire [15:0] res1_0;
wire cout1_0;
//case of no carry in
lpm_add_sub BL1_0 (	.cin (1'b0),
			.dataa (A[31:16]),
			.datab (B[31:16]),
			.cout (cout1_0),
			.result (res1_0));
defparam
	BL1_0.LPM_WIDTH = 16;
						
wire [15:0] res1_1;
wire cout1_1;
//case of carry in
lpm_add_sub BL1_1 (	.cin (1'b1),
			.dataa (A[31:16]),
			.datab (B[31:16]),
			.cout (cout1_1),
			.result (res1_1));
defparam
	BL1_1.LPM_WIDTH = 16;
					
assign Q[31:16] = IntCout? res1_1 : res1_0;
assign cout = IntCout? cout1_1 : cout1_0;

endmodule


И вставим его в наш "модуль аккумулятора":

module CarrySelectAccum32 (input [31:0] B, input clk, input sclr, output [15:0] Q);

reg [31:0] Acc = 1'b0;

wire [31:0] AdderOut;

CarrySelectAdd32 adder (.A (Acc),
			.B (B),
			.cin (0),
			.cout (),
			.Q (AdderOut));

always @(posedge clk)
	Acc <= sclr? 1'b0 : AdderOut;

assign Q = Acc[31:16];

endmodule


Данный модуль синтезируется в 81 ЛЭ: 32 из них отвечают за регистр Acc, ещё 3 по 16 - за сумматоры (один - на младшие 16 бит, ещё два - на старшие 16 бит), и один логический элемент неизвестно для чего. По крайней мере, мы видим, что мультиплексирование (выбор из двух ответов) действительно было "всунуто" в ЛЭ, отвечающие за Acc. Это хороший знак.

И Timing Analyzer для этой схемы не выдаёт предупреждений. Он считает, что самая длинная задержка составляет 12,3 нс (81,3 МГц), что совсем "по краю пропасти", но всё-таки укладывается в нормы.

Но теперь наши входные ГФ заработали "на полную" - из 4 входов, два используются, чтобы подать оба варианта суммы (с переносом и без), ещё один - чтобы выбрать, какой из двух взять, и последний - для синхронного сброса.

Сделать параллельную загрузку в аккумулятор мы уже не сможем - если нам захочется сделать MAC (Multiply-ACcumulate, умножитель-сумматор), придётся такую операцию провести в несколько этапов - сначала обнулить аккумулятор, потом прибавить к нему первое слагаемое, потом начать формировать произведение.

Заметим, что "в теории", если бы львиная доля задержки возникала именно в сумматоре, в разрядах переноса (их же всё-таки 16 сейчас), а задержки в "проводах" и мультиплексорах были бы пренебрежимо малы, то рассмотренная схема давала бы ускорение в 2 раза. На деле мы получили ускорение в 1,4 раза - такова уж специфика ПЛИС.

И не вполне понятно, можно ли добиться ещё более высокой скорости: если поделить общий сумматор не на 2, а на 3 или 4 секции, то нам придётся пропускать сигнал переноса в старшую секцию через последовательность мультиплексоров, и мы возвращаемся к тому, с чего начали. 8-битный "жесткий" сумматор может оказаться даже быстрее, чем "мягкий" (с цепями, скоммутированными через интерконнекторы) мультиплексор!

Если нам будет не хватать 32 бит, или если по мере "утрамбовывания" более толстых схем в ПЛИС, когда начнутся проблемы с размещением, мы опять нарушим тайминги, то не останется ничего другого, как проводить сложение в 2 такта, а не в 1. Надеемся, до этого не дойдёт :)


В следующей части покажем, как сделать синхронные счётчики большой ширины (22 бита и выше), которые также смогут надёжно работать на медленных кристаллах. Со счётчиками всё куда проще :)
Tags: ПЛИС, математика, работа, странные девайсы
Subscribe

  • Так есть ли толк в ковариационной матрице?

    Задался этим вопросом применительно к своему прибору чуть более 2 недель назад. Рыл носом землю с попеременным успехом ( раз, два, три, четыре),…

  • Big Data, чтоб их ... (4)

    Наконец-то стряхнул пыль с компьютерной модели сближения, добавил в неё код, чтобы мы могли определить интересующие нас точки, и выписать…

  • Потёмкинская деревня - 2

    В ноябре 2020 года нужно было сделать скриншот несуществующей программы рабочего места под несуществующий прибор, чтобы добавить его в документацию.…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 3 comments