При написании VHDL я настоятельно рекомендую использовать std_logic_vector (slv) вместо integer (int) для СИГНАЛОВ . (С другой стороны, использование int для обобщений, некоторых констант и некоторых переменных может быть очень полезным.) Проще говоря, если вы объявляете сигнал типа int или вам нужно указать диапазон для целого числа, то вы, вероятно, делаете что-то не так.
Проблема с int в том, что VHDL-программист понятия не имеет, что такое внутреннее логическое представление int, и поэтому мы не можем этим воспользоваться. Например, если я определяю int в диапазоне от 1 до 10, я понятия не имею, как компилятор кодирует эти значения. Надеюсь, это будет закодировано как 4 бита, но мы не знаем много за этим. Если бы вы могли исследовать сигналы внутри ПЛИС, это могло бы быть закодировано от «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 входами. С LUT с 4 входами, которые использует Xilinx Spartan-3, для этого потребуется 11 LUT и 3 уровня логики. Некоторые компиляторы могут преобразовать это в вычитание, которое будет использовать цепочку переноса и охватывать эквивалент 32 LUT, но может работать быстрее, чем 3 уровня логики.
В примере с slv нет 32-битного сравнения, так что это «ноль LUT, ноль уровней логики». Единственным штрафом является то, что наш счетчик - это один дополнительный бит. Поскольку дополнительная синхронизация для этого дополнительного бита счетчика находится в цепочке переноса, дополнительная задержка синхронизации "почти равна нулю".
Конечно, это крайний пример, так как большинство людей не будут использовать 32-битный счетчик таким образом. Это относится к меньшим счетчикам, но разница будет менее существенной, но все же существенной.
Это только один пример того, как использовать slv через int для ускорения синхронизации. Есть много других способов использовать slv - это только воображение.
Обновление: добавлен материал для учета комментариев Мартина Томпсона об использовании int с "if (count-1) <0"
(Примечание: я предполагаю, что вы имели в виду «if count <0», поскольку это сделало бы его более эквивалентным моей версии slv и избавило бы от необходимости этого дополнительного вычитания.)
При некоторых обстоятельствах это может генерировать предполагаемую реализацию логики, но не гарантируется, что она будет работать постоянно. Это будет зависеть от вашего кода и от того, как ваш компилятор кодирует значение int.
В зависимости от вашего компилятора и от того, как вы указываете диапазон вашего 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) или переход на другую архитектуру ПЛИС может привести к неправильным результатам.
Unsigned / Signed vs. slv - это еще одна дискуссия. Вы можете поблагодарить правительственный комитет США за предоставленную нам возможность выбора в VHDL. :) Я использую slv, потому что это стандарт взаимодействия модулей и ядер. Кроме этого, и некоторых других случаев в симуляциях, я не думаю, что есть огромное преимущество в использовании slv по сравнению с подписанным / неподписанным. Я также не уверен, поддерживают ли подписанные / неподписанные сигналы поддержки три заявленные.