Altifigence Academy

54 / 63 · Теория

Многоцикловый беззнаковый умножитель со сдвигом и сложением

Распределите накопление частичных произведений на W тактов и проверьте приём, завершение, последнее сложение, ширину и инвариант.

Предварительные знания: последовательностная логика, неблокирующие присваиванияНеблокирующее присваивание обозначается <= в последовательностном RTL. Оно вычисляет правую часть, затем планирует обновление, позволяя регистрам на одном фронте рассчитываться по предыдущему состоянию. Подробнее, счётчики, сдвиги и сумматоры.

Повторное использование одного сумматора на нескольких тактах

UnsignedБеззнаковая интерпретация (unsigned) рассматривает битовую последовательность как неотрицательное целое. Диапазон n бит — от 0 до 2^n−1; знаковая интерпретация тех же битов может дать другое число. Подробнее-умножение W-битных чисел — сумма частичных произведений, выбранных битами множителя B.

AB=∑i=0W−1bi(A2i)A B=\sum_{i=0}^{W-1} b_i(A2^i)

Одновременное сложение всех частичных произведений требует большой суммирующей логики. Здесь один сумматор используется W раз: это обмен площади на производительность. Цикл в RTL сам по себе не создаёт многоцикловую схему. Нужны регистрыРегистр хранит многобитное состояние. Синхронные регистры в этом курсе записывают определённый вход на фронте тактового сигнала. Подробнее состояния вычисления между тактами и условие завершения.

Аккумулятор P и сдвигаемое множимое M имеют 2W бит, сдвигаемый вправо множитель Q — W бит. При старте P=0, M=zero-extend(A), Q=B. На каждом рабочем такте при Q[0]=1 добавляем M к P, сдвигаем M влево, Q вправо на одну позицию.

Pk+1=Pk+Qk[0]Mk,Mk+1=2Mk,Qk+1=⌊Qk/2⌋P_{k+1}=P_k+Q_k[0]M_k,\quad M_{k+1}=2M_k,\quad Q_{k+1}=\lfloor Q_k/2\rfloor

После W шагов Q=0, P — полное произведение. 2W бит достаточно: максимум (2W−1)2(2^W-1)^2 меньше 22W2^{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 сдвигаются.

Вычислите 13×11 за четыре шага накопления
Показать данные диаграммы
Данные диаграммы: каждый символ — один интервал; точка сохраняет предыдущее состояние; p — один тактовый цикл.
СигналДиаграммаЗначения шины
фронт23452захват → шаг 1 → шаг 2 → шаг 3 → шаг 4
P после234.50 → 13 → 39 → 143
M после2345213 → 26 → 52 → 104 → 208
Q после2345211 → 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. Вызывающая сторона обычно подаёт импульс согласно моменту приёма.

SystemVerilog
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 рабочих шагов сохраняется:

Pk+MkQk=ABP_k+M_kQ_k=A B

Разложение 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.

Выбор действует в этом браузере. Его можно изменить внизу страницы.