Сейчас вооружился четвёртым томом Кнута и заставил свой компилятор для QuatCore генерировать такой вот модуль:
//таблица непосредственных значений, сгенерированная под конкретный исполняемый код module QuatCoreImmTable (input [7:0] SrcAddr, output [15:0] Q); wire[6:0] adr = SrcAddr[6:0]; assign Q[0]=adr[6]; assign Q[1]=adr[5]; assign Q[2]=adr[4]; assign Q[3]=adr[3]; assign Q[4]=adr[2]; assign Q[8]=adr[5]; assign Q[10]=adr[5]; assign Q[11]=adr[5]; assign Q[12]=1'b0; assign Q[13]=1'b0; assign Q[14]=adr[1]; wire [3:0] adr15={adr[6],adr[2],adr[1],adr[0]}; assign Q[15]= (adr15==4'b0000)? 1'b0: (adr15==4'b1100)? 1'b0: (adr15==4'b0001)? 1'b1: (adr15==4'b1001)? 1'b0: (adr15==4'b0101)? 1'b0: (adr15==4'b1101)? 1'b0: (adr15==4'b0011)? 1'b0: 1'bx; wire [2:0] adr9={adr[3],adr[2],adr[0]}; assign Q[9]= (adr9==3'b000)? 1'b0: (adr9==3'b110)? 1'b1: (adr9==3'b001)? 1'b0: (adr9==3'b011)? 1'b1: 1'b0; wire [2:0] adr7={adr[4],adr[2],adr[1]}; assign Q[7]= (adr7==3'b000)? 1'b0: (adr7==3'b010)? 1'b1: (adr7==3'b110)? 1'b1: (adr7==3'b001)? 1'b0: (adr7==3'b101)? 1'b0: (adr7==3'b011)? 1'b0: 1'b1; wire [3:0] adr6={adr[6],adr[3],adr[2],adr[1]}; assign Q[6]= (adr6==4'b0000)? 1'b0: (adr6==4'b1000)? 1'b0: (adr6==4'b0100)? 1'b1: (adr6==4'b0010)? 1'b1: (adr6==4'b1110)? 1'b1: (adr6==4'b0001)? 1'b0: (adr6==4'b1001)? 1'b0: (adr6==4'b0101)? 1'b0: (adr6==4'b1101)? 1'b0: (adr6==4'b0011)? 1'b0: (adr6==4'b1011)? 1'b0: (adr6==4'b0111)? 1'b0: 1'b1; wire [4:0] adr5={adr[6],adr[5],adr[4],adr[3],adr[1]}; assign Q[5]= (adr5==5'b00000)? 1'b0: (adr5==5'b10000)? 1'b0: (adr5==5'b01100)? 1'b1: (adr5==5'b01110)? 1'b0: (adr5==5'b11110)? 1'b1: (adr5==5'b00001)? 1'b0: (adr5==5'b10001)? 1'b0: (adr5==5'b01001)? 1'b1: (adr5==5'b11001)? 1'b0: (adr5==5'b10101)? 1'b1: (adr5==5'b01101)? 1'b1: (adr5==5'b11101)? 1'b0: (adr5==5'b10011)? 1'b1: (adr5==5'b11011)? 1'b0: (adr5==5'b00111)? 1'b1: (adr5==5'b10111)? 1'b1: (adr5==5'b01111)? 1'b0: 1'b0; //Непосредственные значения и их адреса: // Значение (dec) Значение (hex) Маска Адрес Где используется // 181 00B5 00FF 0057 SP Stack/NOP 0/NOP 0 // 1 0001 FFFF 0041 j 1/i 1/JGE ProcessFrame::@NotSoBright/SUB 1/JL AcqQueue::@queue // 32768 8000 C3FF 0001 ACQ VSync // 10 000A 001F 002B k 10 // 15 000F 00FF 007B Z D1 // 16384 4000 C3FF 0003 ACQ HSync/ACQ HSync // 125 007D 007F 005F kLOOP ProcessFrame::@topRows/iLOOP AcqQueue::@updCycle // 720 02D0 FFFF 0005 [SP+2j] ImgHeight // 54 0036 007F 0037 JMP ProcessFrame::@FinalRange // 16 0010 00FF 0007 X ActivePoints // 3583 0DFF FFFF 007D Acc Threshold // 17 0011 00FF 0047 JL ProcessFrame::@MidCycle/X AllPoints // 7 0007 007F 0073 JL ProcessFrame::@DoMerge // 118 0076 007F 0035 iLOOP ProcessFrame::@checkCycle // 19 0013 00FF 0067 Y Heap // 34 0022 007F 0023 JMP ProcessFrame::@MidCycle/i 2 // 0 0000 FFFF 0000 Acc 0/SUB 0 // 14 000E 007F 003B JGE ProcessFrame::@FinalRange // 11 000B 007F 006B JMP ProcessFrame::@ActPointsStart/JMP ProcessFrame::@ActPointsStart // 121 0079 007F 004F JL ProcessFrame::@QueueAnyway // 1023 03FF C3FF 007C ACQ WholeRow // 78 004E 007F 0039 JGE ProcessFrame::@newRow // 60 003C 007F 001F JMP main::@endless // 3 0003 007F 0063 JL ListOp::@OutOfMem endmodule
Он занимает всего 7 ЛЭ, а задержка распространения (tpd) составляет 16 нс.
Теперь компилятор не ограничивается тем, что выписывает оставшиеся 5 выходных бит как логическую функцию от 7 входных. Он берётся за каждый из этих бит и прямым перебором проверяет: нельзя ли его выразить как функцию от ОДНОГО из 7 входных бит (Из 4 возможных вариантов: всегда единица, всегда ноль, повторение входного бита и инверсия, непроверенной осталась лишь инверсия), если этого не получилось - ищет функцию от двух из 7 входных бит (все возможные сочетания), затем функцию от трёх бит - и так далее.
Ясное дело, совпадение требуется только там, где в маске стоит единица, а там, где мы рисовали буквы 'x', может быть что угодно. За счёт этого и удаётся резко сократить количество входных бит. Как видим, 2 раза мы обошлись тремя входными битами, ещё 2 раза четырьмя и один раз - пятью.
Увы, когда я попробовал синтезировать всю схему в сборе (процессор + видеопроцессор в тесной связке), тайминги опять были завалены. Ну не хватает у фиттера усидчивости нормально всё расставить и соединить, хоть ты тресни! Ну хоть размер схемы снизился: вместо 991 ЛЭ получили 950 ЛЭ.
В работе фиттера хватает случайности: чуть-чуть где-то что-то поменял - и он совсем по-другому всё соединяет. Одно только избавление от выходных пинов очень сильно влияет на результаты его работы. Наверное, надо смириться пока с заваленными таймингами - и посмотреть работу всего этого дела на симуляции со сниженной тактовой частотой. А там, когда вместо вытаскивания "внутренностей" процессора наружу (как-то SrcAddr, DestAddr, DataBus) туда пойдёт UART и SPI, тайминги внезапно придут в норму. А если нет - у меня есть ещё пара тузов в рукаве...