54 / 63 · Теория
Многоцикловый беззнаковый умножитель со сдвигом и сложением
Распределите накопление частичных произведений на W тактов и проверьте приём, завершение, последнее сложение, ширину и инвариант.
Уроки доступны бесплатно. Запишитесь, чтобы сохранять прогресс.
Предварительные знания: последовательностная логика, неблокирующие присваиванияНеблокирующее присваивание обозначается <= в последовательностном RTL. Оно вычисляет правую часть, затем планирует обновление, позволяя регистрам на одном фронте рассчитываться по предыдущему состоянию. Подробнее, счётчики, сдвиги и сумматоры.
Повторное использование одного сумматора на нескольких тактах
UnsignedБеззнаковая интерпретация (unsigned) рассматривает битовую последовательность как неотрицательное целое. Диапазон n бит — от 0 до 2^n−1; знаковая интерпретация тех же битов может дать другое число. Подробнее-умножение W-битных чисел — сумма частичных произведений, выбранных битами множителя B.
Одновременное сложение всех частичных произведений требует большой суммирующей логики. Здесь один сумматор используется W раз: это обмен площади на производительность. Цикл в RTL сам по себе не создаёт многоцикловую схему. Нужны регистрыРегистр хранит многобитное состояние. Синхронные регистры в этом курсе записывают определённый вход на фронте тактового сигнала. Подробнее состояния вычисления между тактами и условие завершения.
Аккумулятор P и сдвигаемое множимое M имеют 2W бит, сдвигаемый вправо множитель Q — W бит. При старте P=0, M=zero-extend(A), Q=B. На каждом рабочем такте при Q[0]=1 добавляем M к P, сдвигаем M влево, Q вправо на одну позицию.
После W шагов Q=0, P — полное произведение. 2W бит достаточно: максимум меньше , а промежуточное P при сложении unsigned-частичных произведений не превышает конечное.
Для A=13,B=11,W=4 младшие биты Q идут 1,1,0,1. P меняется 0→13→39→39→143, используемые M — 13→26→52→104. На третьем шаге с Q[0]=0 сдвиги и увеличение счётчика всё равно обязательны.
Каждый столбец — состояние после захвата запроса или рабочего фронта. Первый инициализирует вычисление, следующие четыре выполняют работу. На третьем шаге прежний Q[0]=0, поэтому хранится только P, а M и Q сдвигаются.
Показать данные диаграммы
| Сигнал | Диаграмма | Значения шины |
|---|---|---|
| фронт | 23452 | захват → шаг 1 → шаг 2 → шаг 3 → шаг 4 |
| P после | 234.5 | 0 → 13 → 39 → 143 |
| M после | 23452 | 13 → 26 → 52 → 104 → 208 |
| Q после | 23452 | 11 → 5 → 2 → 1 → 0 |
| done после | 0...1 |
Сначала определите приём и завершение
Интерфейс ниже принимает запрос на положительном фронтеПоложительный фронт — момент перехода такта из 0 в 1. Его следует отличать от уровня, означающего весь интервал CLK=1. Подробнее с start=1 в idle. На t0 только сохраняются входы. Затем t1–tW выполняют W вычислений; после tW действительны product и done. От приёма до завершения W периодов, но с учётом начального фронта видны W+1 фронтов.
При busy=1 start игнорируется. На фронте завершения прежний busy тоже 1, поэтому новый запрос не принимается. Следующий приём возможен не раньше tW+1, минимальный интервал приёмов — W+1 периодов. Постоянный start=1 заново запускает работу на следующем фронте после возврата в idle. Вызывающая сторона обычно подаёт импульс согласно моменту приёма.
module mul_iter #(parameter int W = 8) (
input logic clk, rst, start,
input logic [W-1:0] a, b,
output logic busy, done,
output logic [2*W-1:0] product
);
localparam int CW = (W <= 1) ? 1 : $clog2(W);
localparam logic [CW-1:0] LAST = CW'(W-1);
logic [CW-1:0] step;
logic [2*W-1:0] acc, m, next_acc;
logic [W-1:0] q;
assign next_acc = acc + (q[0] ? m : {2*W{1'b0}});
always_ff @(posedge clk) begin
if (rst) begin
busy <= 1'b0;
done <= 1'b0;
product <= '0;
acc <= '0;
m <= '0;
q <= '0;
step <= '0;
end else begin
done <= 1'b0;
if (!busy) begin
if (start) begin
busy <= 1'b1;
acc <= '0;
m <= {{W{1'b0}}, a};
q <= b;
step <= '0;
end
end else begin
acc <= next_acc;
m <= m << 1;
q <= q >> 1;
if (step == LAST) begin
product <= next_acc;
busy <= 1'b0;
done <= 1'b1;
end else begin
step <= step + 1'b1;
end
end
end
end
endmoduleПредполагается W≥1; CW ограничен снизу единицей, чтобы при W=1 не возник нулевой счётчик. Reset синхронный, active-high и приоритетнее текущей операции. Изменение внешних a,b после запроса не влияет на результат: вычисление использует сохранённые m,q. Product меняется только при reset или завершении. Done — импульс на один период, а не valid, удерживаемый до потребления результата.
Последнее присваивание — product<=next_acc, а не product<=acc. Правая часть nonblocking читает прежнее значение; вывод acc потерял бы частичное произведение последнего бита множителя. По той же причине даже запись сдвига q раньше в коде не меняет расчёт next_acc по текущему Q[0].
Инвариант вычисления и выбор реализации
Сразу после k рабочих шагов сохраняется:
Разложение Q на чётную часть и младший бит показывает сохранение отношения на следующем шаге. При k=W значение Q=0, поэтому P=AB. Этот инвариантИнвариант — условие, которое должно выполняться при любом допустимом выполнении. Например, число хранимых элементов FIFO глубиной 4 всегда находится между 0 и 4 включительно. Подробнее объясняет правильность порядка накопления и сдвига яснее, чем диаграмма одного малого примера.
Критический путь шага содержит 2W-битный сумматор и выбор входа. Удаление знака умножения не гарантирует меньшую или более быструю схему. Сравнивайте выделенные DSP-блоки FPGA, результаты синтеза и требуемую пропускную способностьПропускная способность — число операций, принимаемых или завершаемых за единицу времени. Конвейер может повысить её, не уменьшая задержку отдельной операции. Подробнее. Модуль не реализует early-exit, signed-обработку и backpressure результата. Потребитель должен по контракту не пропускать done.
В тестах используйте 0,1,максимум и B с единицей только в старшем бите; проверяйте start и изменение входов во время busy, а также прерывание reset. Именно старший одиночный бит B выявляет пропуск последнего накопления.
Попробуйте сами
Операция W=4,A=9,B=10 принята на t0. Найдите acc, q, done после t1–t4 и самый ранний следующий фронт приёма. Какой выход даст последнее product<=acc?
Прочитать объяснение
Изначально acc=0,m=9,q=1010. t1: младший бит 0, поэтому acc=0,q=0101,done=0. t2: добавляется m=18, acc=18,q=0010,done=0. t3: младший бит 0, acc=18,q=0001,done=0. t4: добавляется m=72, acc=90,q=0000,done=1,busy=0, product=90. Перед t4 busy=1, поэтому start не принимается; самый ранний приём — t5. При product<=acc неблокирующее присваивание читает прежнее acc=18 и теряет последнее частичное произведение 72, выдавая 18.