При написанні VHDL, я настійно рекомендую використовувати std_logic_vector (SLV) замість цілого (INT) для сигналів . (З іншого боку, використання int для дженериків, деяких констант і деяких змінних може бути дуже корисним.) Простіше кажучи, якщо ви оголошуєте сигнал типу int або потрібно вказати діапазон для цілого числа, то ви, ймовірно, робите щось не так.
Проблема з int полягає в тому, що програміст VHDL поняття не має, що таке внутрішнє логічне подання int, і тому ми не можемо ним скористатися. Наприклад, якщо я визначаю int діапазону від 1 до 10, я поняття не маю, як компілятор кодує ці значення. Сподіваємось, це було б закодовано як 4 біти, але ми не знаємо багато іншого, ніж це. Якщо ви можете зондувати сигнали всередині FPGA, він може бути закодований як "0001" до "1010", або кодуватися як "0000" до "1001". Можливо також, що він закодований таким чином, що абсолютно не має сенсу для нас, людей.
Натомість нам слід просто використовувати slv замість int, оскільки тоді ми маємо контроль над кодуванням, а також маємо прямий доступ до окремих бітів. Важливий прямий доступ, як ви побачите далі.
Ми можемо просто кинути int на slv, коли нам потрібен доступ до окремих біт, але це стає справді безладним, дуже швидко. Це як отримати найгірше з обох світів замість найкращого з обох світів. Код буде важко оптимізувати компілятору і майже неможливо прочитати. Я не рекомендую цього.
Отже, як я вже сказав, з slv ви маєте контроль над кодуванням бітів і прямий доступ до бітів. То що ви можете зробити з цим? Я покажу вам кілька прикладів. Скажімо, що вам потрібно виводити імпульс раз на 4 294 000 000 годин. Ось як би це зробити за допомогою int:
signal count :integer range 0 to 4293999999; -- a 32 bit integer
process (clk)
begin
if rising_edge(clk) then
if count = 4293999999 then -- The important line!
count <= 0;
pulse <= '1';
else
count <= count + 1;
pulse <= '0';
end if;
end if;
end process;
І той самий код за допомогою slv:
use ieee.numeric_std.all;
signal count :std_logic_vector (32 downto 0); -- a 33 bit integer, one extra bit!
process (clk)
begin
if rising_edge(clk) then
if count(count'high)='1' then -- The important line!
count <= std_logic_vector(4293999999-1,count'length);
pulse <= '1';
else
count <= count - 1;
pulse <= '0';
end if;
end if;
end process;
Більшість цього коду ідентична між int та slv, принаймні в сенсі розміру та швидкості отриманої логіки. Звичайно, один підраховує, а другий відраховує, але це не важливо для цього прикладу.
Різниця полягає у "важливій лінії".
З прикладом int, це призведе до порівняння з 32 входами. Із 4-вхідними LUT, які використовує Xilinx Spartan-3, для цього знадобиться 11 LUT та 3 рівня логіки. Деякі компілятори можуть перетворити це в віднімання, яке використовуватиме ланцюжок переносу і охоплює еквівалент 32 LUT, але може працювати швидше, ніж 3 рівня логіки.
У прикладі slv немає 32-бітного порівняння, тому це "нульові LUT, нульові рівні логіки". Єдине покарання полягає в тому, що наш лічильник - це один зайвий біт. Оскільки додаткові терміни для цього додаткового лічильника лічильників знаходяться у ланцюзі перенесення, існує «майже нульова» додаткова затримка часу.
Звичайно, це надзвичайний приклад, оскільки більшість людей не використовуватиме 32-бітний лічильник таким чином. Це стосується менших лічильників, але різниця буде менш драматичною, хоча все ще значною.
Це лише один приклад того, як використовувати slv over int, щоб отримати швидші терміни. Існує багато інших способів використання slv - для цього потрібна лише фантазія.
Оновлення: Додано матеріали для вирішення коментарів Мартіна Томпсона щодо використання int з "if (count-1) <0"
(Примітка. Я припускаю, що ви мали на увазі "якщо рахувати <0", оскільки це зробить його більш еквівалентним моїй версії slv і усуне необхідність у додатковому відніманні.)
За певних обставин це може генерувати передбачувану логічну реалізацію, але не гарантується, що вона буде працювати весь час. Це залежатиме від вашого коду та того, як ваш компілятор кодує значення int.
Залежно від вашого компілятора і того, як ви визначаєте діапазон вашого int, цілком можливо, що значення нуля нуля не кодує бітовий вектор "0000 ... 0000", коли він перетворює його в логіку FPGA. Щоб ваша варіація працювала, вона повинна кодувати "0000 ... 0000".
Наприклад, скажімо, ви визначаєте int, щоб мати діапазон від -5 до +5. Ви очікуєте, що значення 0 буде закодовано в 4 біти, як "0000", і +5 як "0101", а -5 - "1011". Це типова схема кодування двійок-доповнення.
Але не варто вважати, що компілятор буде використовувати подвійні доповнення. Хоча незвично, доповнення може призвести до "кращої" логіки. Або компілятор міг би використовувати якесь "упереджене" кодування, де -5 кодується як "0000", 0 - "0101", а +5 - "1010".
Якщо кодування int "правильне", компілятор, швидше за все, зробить висновок, що робити з бітом перенесення. Але якщо це неправильно, то отримана логіка буде жахливою.
Можливо, що використання int таким чином може призвести до розумного логічного розміру та швидкості, але це не є гарантією. Перехід на інший компілятор (наприклад, XST на Synopsis) або перехід до іншої архітектури FPGA може призвести до того, що трапиться зовсім неправильна річ.
Непідписаний / підписаний проти slv - це ще одна дискусія. Ви можете подякувати урядовому комітету США за те, що нам дали стільки варіантів ВГДЛ. :) Я використовую slv, оскільки це стандарт для взаємодії модулів і ядер. Окрім цього, та деяких інших випадків моделювання, я не думаю, що використання переваги slv над підписаним / неподписаним не має великої користі. Я також не впевнений, чи підписані / непідписані підтримують триголосні сигнали.