Общая схема поменялась незначительно - управляющие воздействия слегка другими стали. А вот отдельные модули претерпели изменения.

QuatCoreALUadder теперь умеет только складывать. По сути, весь модуль - лишь "обёртка" для lpm_add_sub, чтобы был параметр AccWidth:
//decided to make it as simple as possible, by delegating constants to Acc, //and by delegating negation to Breg. //but second part of negation, adding 1 is here of course (cin) module QuatCoreALUadder (input [AccWidth-1:0] A, input [AccWidth-1:0] B, input cin, output [AccWidth-1:0] Q, output SignBit); parameter AccWidth = 32; lpm_add_sub ALU ( .dataa (A), //full acc width .datab (B), //this one extended as well .cin (cin), .result (Q[AccWidth-1:0])); defparam ALU.lpm_direction = "ADD", ALU.lpm_hint = "ONE_INPUT_IS_CONSTANT=NO,CIN_USED=YES", ALU.lpm_representation = "UNSIGNED", ALU.lpm_type = "LPM_ADD_SUB", ALU.lpm_width = AccWidth; assign SignBit = Q[AccWidth-1]; endmodule
Выход SignBit попросту дублирует старший бит, чтобы общая схема была красивее, не люблю, когда приходится давать имя всей шине, а потом отдельные провода называть как провод на шине. Если бы ещё там было ГОСТовское обозначение жгута с циферкам - ещё куда не шло. Поэтому если знаю, что отдельные биты понадобятся - "ответвляю" их в верилоге.
А так - самый обычный сумматор с входом для переноса. Перенос нужен, чтобы завершить "смену знака" числа в регистре B. Инверсию мы отдали регистру, а вот прибавлением единички должен заниматься сумматор всё-таки.
К сожалению, такой подход слегка усложняет устройство управления - инверсию надо "заказать" на предыдущем шаге (регистр сделает её по фронту тактового импульса), а вот единицу прибавлять - на текущем! Ну да ладно, поставить 1-битный регистр для задержки мы сообразим :)
В первой версии, ценой 32 ЛЭ этот модуль мог инвертировать один операнд, а чтобы добру не пропадать - ещё и хранил в себе сколько-нибудь констант. Теперь эту функциональность решили переложить на другие модули.
QuatCoreALUAcc теперь хранит все константы, которые трудно было бы загрузить из памяти, целых 4 штуки: +3/2, -3/2, 2 и "одну вторую младшего разряда" для правильного округления. Поначалу я не хотел этого делать, т.к мне казалось, что необходимость выбора одного из 7 действий:
- ничего не делать,
- загрузить результат сумматора,
- сбросить регистр,
- загрузить значение +3/2,
- загрузить значение -3/2,
- загрузить значение 2,
- загрузить значение "1/2lsb"
потребует по 2 ЛЭ на бит, т.е на ровном месте у нас добавится 32 ЛЭ. Действительно: из 4 входов ЛЭ, один мы резервируем для ENA (разрешение работы триггера), ещё один - для выхода сумматора, и остаётся всего два для выбора режима, а нужно три! Значит, понадобится ещё один ЛЭ.
Будь значения "случайными" - так бы и было. Но они подчинены некоторой, хоть и извращённой, логике, причём их нумерацию мы можем задать произвольно, чтобы получить простые логические функции, где один из входных битов просто отсутствовал бы!
Начать с того, что 28 разрядов из 32 вообще во всех 5 "нижних" операциях заполняются нулями!
Да и остальные тоже не так плохи.
Мы выбрали следующие коды операций для Acc:
000 - загрузить результат сумматора,
001 - ЗАПРЕЩЁННОЕ СОСТОЯНИЕ (никогда не должно появляться на входе, имеем право делать что хотим, UNDEFINED BEHAVIOUR практически),
010 - ничего не делаем (idle),
011 - обнуление,
100 - загрузить -3/2,
101 - загрузить 2,
110 - загрузить 3/2,
111 - загрузить 1/2lsb.
Вот что должно получаться на выходе при каждой инструкции:
000: DDDD......D... (данные с выхода сумматора, поступающие на вход D), 001: xxxxxxxxxxx... (что угодно) 010: QQQQ......Q... (сохраняются данные, лежащие в регистре), 011: 0000......0... (сброс), 100: 1010......1... (-3/2), 101: 1000......1... (2), 110: 0110......1... (3/2), 111: 0000......1... (1/2lsb)
Сигнал ENA (разрешение работы триггеров) мы формируем "честно":
wire ENA = (mode != 3'b010);
для этого все равно понадобился отдельный ЛЭ, поэтому не пытаемся упростить.
Далее, все действия будут производиться только если ENA=1:
always @(posedge clk) if (ENA) begin ... ... end
Так что в последующем коде мы можем не рассматривать вариант 010, его там встретиться не может! (точнее, он встретится, и логика что-то посчитает, но сохранено в регистр оно не будет). Это облегчает нам жизнь, позволяя использовать более простую логику.
28 из 32 бит обрабатываются проще всего:
Q[AccWidth-4:AccWidth-17] <= (mode[2]|mode[1])? 14'h0000 : D[AccWidth-4:AccWidth-17]; Q[AccWidth-19:0] <= (mode[2]|mode[1])? {trailingzeros{1'b0}} : D[AccWidth-19:0];
Если хотя бы один из двух первых битов инструкции единичный, то мы их обнуляем, в противном случае загружаем результат из сумматора. Как видно, нам хватило двух управляющих битов, поэтому дополнительные ЛЭ не нужны.
Бит "половины младшего разряда" чуть хитрее - он либо должен загрузиться из сумматора (по 000), либо быть нулевым при обнулении, либо единичным в каждой из констант. По счастью, и тут двух управляющих бит достаточно:
Q[AccWidth-18] <= mode[2]? 1'b1 : mode[1]? 1'b0 : D[AccWidth-18];
Все 4 константы, где нужна единица, удачно сгруппировались по единичному старшему биту. А затем "разделяем" по среднему биту - замечательно.
Самый старший бит аккумулятора также хорош:
Q[AccWidth-1] <= mode[1]? 1'b0 : mode[2]? 1'b1 : D[AccWidth-1];
Если взглянуть на таблицу, то видим, что всё как надо.
А вот со следующим битом у меня пока что-то не получается. Там одна одинокая "единица", чтобы в нужный момент её поставить, явно нужно проверить два младших управляющих бита. Но чтобы затем отличить инструкцию "000" (загрузить результат сумматора) и "100" (загрузить константу -3/2) явно нужно проверить ещё и старший бит, так что теперь все три нам задают поведение:
Q[AccWidth-2] <= (mode[1:0]==2'b10)? 1'b1 : (mode[2:1]==2'b00)? D[AccWidth-2] : 1'b0;
Эта строка очевидно синтезируется в 2 ЛЭ - ну и ладно.
С ещё одним битом (3-й с начала) всё хорошо:
Q[AccWidth-3] <= (mode[2] & (~mode[0]))? 1'b1 : ((~mode[2]) & (~mode[0]))? D[AccWidth-3] : 1'b0;
тоже 1 ЛЭ.
Вот код получившегося модуля:
//mode = 000: load from D //mode = 001: FORBIDDEN //mode = 010: idle, //mode = 011: clear, //mode = 100: -3/2 (101000...) //mode = 101: set 2 (100000...) //mode = 110: set 3/2 (01100...) //mode = 111: set 1/2lsb (00000...1) module QuatCoreALUAcc (input clk, input [AccWidth-1:0] D, input [2:0] mode, output reg [AccWidth-1:0] Q=1'b0, output [16:0] Senior17, output [AccWidth-18:0] LSB); parameter AccWidth = 32; localparam trailingzeros = AccWidth - 18; wire ENA = (mode != 3'b010); always @(posedge clk) if (ENA) begin Q[AccWidth-4:AccWidth-17] <= (mode[2]|mode[1])? 14'h0000 : D[AccWidth-4:AccWidth-17]; Q[AccWidth-19:0] <= (mode[2]|mode[1])? {trailingzeros{1'b0}} : D[AccWidth-19:0]; Q[AccWidth-18] <= mode[2]? 1'b1 : mode[1]? 1'b0 : D[AccWidth-18]; Q[AccWidth-1] <= mode[1]? 1'b0 : mode[2]? 1'b1 : D[AccWidth-1]; Q[AccWidth-2] <= (mode[1:0]==2'b10)? 1'b1 : (mode[2:1]==2'b00)? D[AccWidth-2] : 1'b0; Q[AccWidth-3] <= (mode[2] & (~mode[0]))? 1'b1 : ((~mode[2]) & (~mode[0]))? D[AccWidth-3] : 1'b0; end assign Senior17 = Q[AccWidth-1:AccWidth-17]; assign LSB = Q[AccWidth-18:0]; endmodule
Этот модуль синтезируется в 34 ЛЭ, как и "заказывалось" - 32-битный аккумулятор, плюс один ЛЭ на второй по старшинству бит, плюс ещё один ЛЭ на формирование сигнала ENA.
Что странно, "автоматически" генерить столь же короткую схему синтезатор по-прежнему не может, хотя казалось бы, тут чистая комбинаторика, компьютер в таких задачах должен быть непобедим! Но нет, когда я написал такой код:
Q <= (mode == 3'b000)? D : //load (mode == 3'b001)? {AccWidth{1'bx}} : //forbidden (mode == 3'b010)? {AccWidth{1'bx}} : //we don't get this because of ce (mode == 3'b011)? {AccWidth{1'b0}} : //clear (mode == 3'b100)? {18'b1_0100_0000_0000_0000_1, {trailingzeros{1'b0}}}: //-3/2 (mode == 3'b101)? {18'b1_0000_0000_0000_0000_1, {trailingzeros{1'b0}}}: //2 (mode == 3'b110)? {18'b0_1100_0000_0000_0000_1, {trailingzeros{1'b0}}}: //3/2 {18'b0_0000_0000_0000_0000_1, {trailingzeros{1'b0}}}; //1/2lsb
(просто говорим, каким должен быть каждый из битов аккумулятора в каждом из 8 случаев, не забывая указать, когда нам до этого нет никакого дела!)
он синтезируется в 38 ЛЭ. Хранение констант здесь ускоряет операцию ZAcc (загрузка константы в акк.) - в предыдущей реализации она занимала 2 такта (сначала обнулить, затем прибавить константу), теперь - всего один.
Поскольку логика довольно укуренная, надо бы её проверить:

Всё верно.
Регистру B у нас слишком легко жилось - всего два режима работы по сути, "загрузить" и "сдвинуть". Ну ещё сигнал BSigned, определяющий, делать арифметический (знаковый) или логический (беззнаковый) сдвиг, но он действует только на старший бит (либо он сохраняет своё значение при сдвиге, либо обнуляется). И ещё выбор - загружать ли в младшие биты нули или младшие разряды аккумулятора по "байпасу".
Теперь мы заставим его также инвертировать биты, для того, чтобы можно было сменить знак числа. Мы работаем с дополнительным кодом, смена знака эквивалентна инверсии (замене нуля на единицу и наоборот) и прибавлению единички. Единичку прибавляет сумматор, а инверсию будет делать регистр B.
Первая идея была (уже давно) в введении 4 различных режимов работы регистра:
00 - загрузить "как есть",
01 - загрузить с инверсией,
10 - сдвинуть вправо "как есть",
11 - сдвинуть вправо с инверсией.
Тогда, если мы хотим умножить два знаковых числа, мы заблаговременно загружаем второе в регистр C, а затем загружаем первое в регистр B С ИНВЕРСИЕЙ, т.к мы начинаем с умножения на старший разряд второго числа, а это разряд со знаком "минус". На следующем такте выполняем сложение и выбираем сдвиг вправо С ИНВЕРСИЕЙ, чтобы вернуться к исходному числу. Дальнейшие сдвиги уже будут без инверсии.
Можно и так поступить, но младшие разряды опять немножко "перегружаются", в каждый из них должны поступать:
- выход разряда "слева", для сдвига,
- байпас от аккумулятора,
- управляющие сигналы, задающие не только 4 режима работы, но и выбирающие, воспользоваться ли байпасом или просто обнулить. (ведь и эти биты надо уметь брать хоть "как есть", хоть "с инверсией". Тут явно необходимо 3 бита.
Итого, 5 бит, а у нас элементы имеют 4 входа, т.е на каждый из "младших разрядов" (по умолчанию их 15) будет приходиться по 2 ЛЭ. Мы по-прежнему немножко в выигрыше в сравнению с начальным вариантом, но не сильно.
Есть вариант экономичнее в плане логических элементов, но замедляющий большинство операций на 1 такт.
А именно, мы добавляем ещё один, САМЫЙ СТАРШИЙ бит, и загружаем всегда "как есть", а вот сдвигаем иногда с инверсией, иногда-нет. И один сдвиг обязательно осуществляем в самом начале.
Что интересно, этот старший бит нам тоже весьма кстати - до этого мы делали "расширение знака" до размера аккумулятора на комбинаторной логике, а теперь оно будет выполнено "автоматически". То есть здесь мы даже не теряем 1 ЛЭ - он и так был задействован.
Тем самым, у нас освобождается один "режим", как раз для обнуления младших разрядов, если байпас не нужен (данные поступают не из аккумулятора, а откуда-то извне, по 16-битной шине). Правда, если попытаться объединить 2 бита "режима" с сигналом "EnableBypass" прямо в этом модуле, он опять делает "жадный выбор" или не знаю что - не понимает, что можно сделать одну простую операцию "снаружи" и раздать её на 16 элементов, вместо этого каждый "утолщает". Так что пока я ввёл 4 режима:
00 - сдвиг вправо "как есть",
01 - сдвиг вправо с инверсией,
10 - загрузка 16 бит (байпас выключен),
11 - загрузка 32 бит (байпас включен).
Теперь формирование этих бит отдаётся в модуль управления QuatCoreALUcontrol, надеюсь, когда туда добавится дополнительная логика, синтезатор сообразит всё правильно.
А код регистра B получился таким:
//mode = 00 - shift right //mode = 01 - shift right and negate //mode = 10 - load 16 bit (no bypass) //mode = 11 - load AccWidth bit (bypass enabled) module QuatCoreALUBreg (input clk, input[AccWidth-18:0] Dsmall, input [15:0] D, input DoSigned, input [1:0] mode, output [AccWidth-1:0] Q); parameter AccWidth = 32; localparam SmallPartWidth = AccWidth - 16; reg seniorBit = 1'b0; reg [14:0] largeQ = 1'b0; reg [SmallPartWidth-1:0] smallQ = 1'b0; assign Q = {seniorBit, largeQ, smallQ}; always @(posedge clk) begin seniorBit <= mode[1]? D[15] : DoSigned? (seniorBit^mode[0]) : 1'b0; largeQ <= mode[1]? D[14:0] : mode[0]? {~seniorBit, ~largeQ[14:1]} : {seniorBit, largeQ[14:1]}; smallQ <= mode[1]? (mode[0]? Dsmall : {SmallPartWidth{1'b0}}) : mode[0]? {~largeQ[0], ~smallQ[SmallPartWidth-1:1]} : {largeQ[0], smallQ[SmallPartWidth-1:1]}; end endmodule
Увы, эта переделка означает: логику управления надо перекраивать! Я ещё не успел доделать модуль управления по старой схеме, и существенных изменений уже не будет, но всё-таки придётся снова на бумажке расчертить нолики и единички и мой любимый символ x, то есть don't care (мне по х).
UPD. На симуляции у нас "запрещённый" режим акк. 001 вёл себя очень мирно - также загружал значение из D. Подумалось - может оба варианта, 000 и 001, допустимы? Нет, нам просто повезло, вероятность была 50%, что загрузится. Вот другой пример:

Там ровно один бит, 3-й по старшинству, в режиме 001 всегда устанавливается в ноль, а все остальные - загружаются из D. Такое вот "неопределённое поведение".