VEKTÖR temsillerini INTEGER'lere karşı ne zaman kullanır?


11

Bu sorunun cevabındaki yorum dizisinde: VHDL varlığında yanlış çıktılar belirtildi:

"Tamsayılarla FPGA'daki dahili mantık gösterimini kontrol edemez veya erişemezsiniz, SLV ise taşıma zincirini verimli kullanmak gibi püf noktaları yapmanıza izin verir"

Peki, hangi durumlarda dahili temsile erişmek için tamsayı s kullanmaktan ziyade bir bit temsili vektörü kullanarak kod yazmanın daha uygun olduğunu gördünüz ? Ve hangi avantajları ölçtünüz (talaş alanı, saat frekansı, gecikme veya başka bir şekilde.)?


Sanırım ölçülmesi zor bir şey, çünkü görünüşe göre bu sadece düşük seviyeli uygulama üzerinde bir kontrol meselesi.
clabacchio

Yanıtlar:


5

Her iki diğer iki posterleri önerdiği kod yazdım vectorve integeriki sürüm mümkün olduğunca benzer bir yol olarak faaliyet olması dikkat ederek formda.

Simülasyondaki sonuçları karşılaştırdım ve daha sonra Xilinx Spartan 6'yı hedefleyen Synplify Pro kullanarak sentezledim. Aşağıdaki kod örnekleri çalışma kodundan yapıştırılmıştır, bu yüzden bunları en sevdiğiniz sentezleyiciyle kullanabilmeniz ve aynı davranıp davranmadığını görmeniz gerekir.


Downcounters

İlk olarak, David Kessner tarafından önerildiği gibi aşağı sayım:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity downcounter is
    generic (top : integer);
    port (clk, reset, enable : in  std_logic; 
         tick   : out std_logic);
end entity downcounter;

Vektör mimarisi:

architecture vec of downcounter is
begin
    count: process (clk) is
        variable c : unsigned(32 downto 0);  -- don't inadvertently not allocate enough bits here... eg if "integer" becomes 64 bits wide
    begin  -- process count
        if rising_edge(clk) then  
            tick <= '0';
            if reset = '1' then
                c := to_unsigned(top-1, c'length);
            elsif enable = '1' then
                if c(c'high) = '1' then
                    tick <= '1';
                    c := to_unsigned(top-1, c'length);
                else
                    c := c - 1;
                end if;
            end if;
        end if;
    end process count;
end architecture vec;

Tam sayı mimarisi

architecture int of downcounter is
begin
    count: process (clk) is
        variable c : integer;
    begin  -- process count
        if rising_edge(clk) then  
            tick <= '0';
            if reset = '1' then
                c := top-1;
            elsif enable = '1' then
                if c < 0 then
                    tick <= '1';
                    c := top-1;
                else
                    c := c - 1;
                end if;
            end if;
        end if;
    end process count;
end architecture int;

Sonuçlar

Kod olarak, tamsayı to_unsigned()çağrıları önlemek için tercih edilir gibi görünüyor . Aksi takdirde, seçim için çok fazla değil.

Synplify Pro ile çalıştırmak , sürüm için 66 LUT ve sürüm için 64 LUTtop := 16#7fff_fffe# üretir . Her iki versiyon da taşıma zincirini çok kullanır. Her ikisi de 280MHz'i aşan saat hızlarını bildiriyor . Sentezleyici, taşıma zincirinin iyi kullanımını sağlayabiliyor - RTL görüntüleyici ile görsel olarak her ikisinde de benzer mantığın üretildiğini doğruladım. Açıkçası karşılaştırıcılı bir karşı sayaç daha büyük olacaktır, ancak bu yine hem tamsayılar hem de vektörler için aynı olacaktır.vectorinteger


2 ** n sayacına bölme

Ajs410 tarafından önerilen:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity clkdiv is
    port (clk, reset : in     std_logic;
        clk_2, clk_4, clk_8, clk_16  : buffer std_logic);
end entity clkdiv;

Vektör mimarisi

architecture vec of clkdiv is

begin  -- architecture a1

    process (clk) is
        variable count : unsigned(4 downto 0);
    begin  -- process
        if rising_edge(clk) then  
            if reset = '1' then
                count  := (others => '0');
            else
                count := count + 1;
            end if;
        end if;
        clk_2 <= count(0);
        clk_4 <= count(1);
        clk_8 <= count(2);
        clk_16 <= count(3);
    end process;

end architecture vec;

Tam sayı mimarisi

Sadece to_unsignedyukarıdakiyle aynı etkiyi yaratacak olan bitleri kullanmaktan ve seçmekten kaçınmak için bazı halkalardan atlamanız gerekir :

architecture int of clkdiv is
begin
    process (clk) is
        variable count : integer := 0;
    begin  -- process
        if rising_edge(clk) then  
            if reset = '1' then
                count  := 0;
                clk_2  <= '0';
                clk_4  <= '0';
                clk_8  <= '0';
                clk_16 <= '0';
            else
                if count < 15 then
                    count := count + 1;
                else
                    count := 0;
                end if;
                clk_2 <= not clk_2;
                for c4 in 0 to 7 loop
                    if count = 2*c4+1 then
                        clk_4 <= not clk_4;
                    end if;
                end loop; 
                for c8 in 0 to 3 loop
                    if count = 4*c8+1 then
                        clk_8 <= not clk_8;
                    end if;
                end loop; 
                for c16 in 0 to 1 loop
                    if count = 8*c16+1 then
                        clk_16 <= not clk_16;
                    end if;
                end loop; 
            end if;
        end if;
    end process;
end architecture int;

Sonuçlar

Kod-bilge, bu durumda, vectorsürüm açıkça daha iyidir!

Sentez sonuçları açısından, bu küçük örnek için, tam sayı sürümü (ajs410'un öngördüğü gibi) karşılaştırıcıların bir parçası olarak 3 ekstra LUT üretiyor, ancak çok dağınık bir kod parçasıyla çalışmasına rağmen sentezleyici hakkında çok iyimserdim!


Diğer kullanımlar

Aritmetiğin etrafa sarılmasını istediğinizde vektörler açık bir kazançtır (sayaçlar tek bir satır olarak bile yapılabilir):

vec <= vec + 1 when rising_edge(clk);

vs

if int < int'high then 
   int := int + 1;
else
   int := 0;
end if;

en azından bu koddan yazarın etrafı sarmayı amaçladığı açıktır.


Gerçek kodda kullanmadığım, ancak düşündüğüm bir şey:

"Doğal olarak sarma" özelliği, "taşmalar yoluyla hesaplama" için de kullanılabilir. Toplama / çıkarma ve çarpma zincirinin çıktısının sınırlı olduğunu bildiğinizde, ara hesaplamaların yüksek bitlerini (2-s tamamlayıcıda) "yıkamada" çıkacağı gibi saklamanız gerekmez. çıktıya ulaştığınız zamana kadar. Bu makalenin bunun bir kanıtı içerdiği söylendi, ancak hızlı bir değerlendirme yapmak benim için biraz yoğun görünüyordu! Bilgisayar Toplama Teorisi ve Taşmalar - HL Garner

integerBu durumda s'nin kullanılması , sonunda çözüleceklerini bildiğimiz halde, sarıldıklarında simülasyon hatalarına neden olacaktır.


Philippe'in de işaret ettiği gibi, 2 ** 31'den daha büyük bir sayıya ihtiyacınız olduğunda vektörleri kullanmaktan başka seçeneğiniz yoktur.


İkinci kod bloğunda variable c : unsigned(32 downto 0);... c33 bitlik bir değişken değil mi?
clabacchio

@clabacchio: evet, etrafı saran görmek için 'taşıma bitine' erişim sağlar.
Martin Thompson

5

VHDL yazarken, SIGNALS için tamsayı (int) yerine std_logic_vector (slv) kullanmanızı tavsiye ederim . (Öte yandan, jenerikler için int, bazı sabitler ve bazı değişkenler son derece yararlı olabilir.) Basitçe söylemek gerekirse, int türünde bir sinyal bildirirseniz veya bir tamsayı için bir aralık belirtmeniz gerekiyorsa, muhtemelen Ters giden birşey mi var.

İnt ile ilgili sorun, VHDL programcısının int'in dahili mantık gösteriminin ne olduğu hakkında hiçbir fikri olmadığı ve bundan yararlanamayacağımızdır. Örneğin, 1 ila 10 aralığında bir int tanımlarsam, derleyicinin bu değerleri nasıl kodladığı hakkında hiçbir fikrim yoktur. Umarım 4 bit olarak kodlanır, ancak bunun ötesinde fazla bir şey bilmiyoruz. FPGA içindeki sinyalleri inceleyebiliyorsanız, "0001" ila "1010" olarak kodlanabilir veya "0000" ila "1001" olarak kodlanabilir. Ayrıca, insanlar için kesinlikle hiçbir anlam ifade etmeyecek şekilde kodlanması da mümkündür.

Bunun yerine int yerine slv kullanmalıyız, çünkü kodlama üzerinde kontrole sahibiz ve ayrıca ayrı bitlere doğrudan erişebiliyoruz. Daha sonra göreceğiniz gibi doğrudan erişime sahip olmak önemlidir.

Her bir bite erişmemiz gerektiğinde slv'ye int a atabiliriz, ancak bu gerçekten dağınık, çok hızlı olur. Bu, her iki dünyanın en iyisi yerine her iki dünyanın en kötüsünü almak gibidir. Kodun derleyicinin optimize etmesi zor olacak ve okumanız neredeyse imkansız. Bunu tavsiye etmiyorum.

Dediğim gibi, slv ile bit kodlamaları ve bitlere doğrudan erişim üzerinde kontrolünüz var. Peki bununla ne yapabilirsiniz? Size birkaç örnek göstereceğim. Diyelim ki her 4,294,000,000 saatte bir nabız atmanız gerekiyor. Bunu int ile nasıl yapacağınız aşağıda açıklanmıştır:

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;

Ve slv kullanarak aynı kod:

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;

Bu kodun çoğu int ve slv arasında, en azından ortaya çıkan mantığın boyutu ve hızı anlamında aynıdır. Elbette biri sayıyor, diğeri geri sayıyor, ancak bu örnek için önemli değil.

Aradaki fark "önemli çizgide".

İnt örneğiyle, bu 32 girişli bir karşılaştırıcı ile sonuçlanacaktır. Xilinx Spartan-3'ün kullandığı 4-Giriş LUT'lar ile, bu 11 LUT ve 3 seviye mantık gerektirecektir. Bazı derleyiciler bunu taşıma zincirini kullanacak ve 32 LUT'a eşdeğer olacak bir çıkarma işlemine dönüştürebilir, ancak 3 mantık seviyesinden daha hızlı çalışabilir.

Slv örneğinde, 32 bit karşılaştırması yoktur, bu nedenle "sıfır LUT'lar, sıfır mantık seviyeleri" dir. Tek ceza, sayacımızın ekstra bir bit olmasıdır. Bu fazladan sayacın ek zamanlaması taşıma zincirinde olduğundan, "neredeyse sıfır" ek zamanlama gecikmesi vardır.

Tabii bu aşırı bir örnektir, çünkü çoğu insan bu şekilde 32 bitlik bir sayaç kullanmaz. Daha küçük sayaçlar için geçerlidir, ancak fark hala önemli olmasına rağmen daha az dramatik olacaktır.

Bu, daha hızlı zamanlama elde etmek için int üzerinde slv'nin nasıl kullanılacağına sadece bir örnektir. Slv kullanmanın başka birçok yolu vardır - sadece biraz hayal gücü gerektirir.

Güncelleme: Martin Thompson'un "if (count-1) <0" ile int hakkında yaptığı yorumları ele alan şeyler eklendi

(Not: "sayı <0 ise" demek istediğinizi varsayarım, çünkü bu benim slv sürümümle daha eşdeğer olur ve bu ekstra çıkarma ihtiyacını ortadan kaldırır.)

Bazı durumlarda bu, amaçlanan mantık uygulamasını oluşturabilir, ancak her zaman çalışması garanti edilmez. Kodunuza ve derleyicinizin int değerini nasıl kodladığına bağlı olacaktır.

Derleyicinize ve int aralığınızı nasıl belirlediğinize bağlı olarak, sıfır int değerinin FPGA mantığına girdiğinde "0000 ... 0000" bit vektörüne kodlamaması tamamen mümkündür. Varyasyonunuzun çalışması için "0000 ... 0000" olarak kodlaması gerekir.

Örneğin, bir int değerini -5 ila +5 aralığında olacak şekilde tanımladığınızı varsayalım. 0 değerinin "0000" gibi 4 bit ve "0101" olarak +5 ve "1011" olarak -5 olarak kodlanmasını bekliyorsunuz. Bu, tipik iki bileşenli kodlama şemasıdır.

Ancak derleyicinin iki tamamlayıcı kullanacağını varsaymayın. Olağandışı olmasına rağmen, bir-tamamlayıcı "daha iyi" mantığına neden olabilir. Veya, derleyici -5 "0000", 0 "0101" ve +5 "1010" olarak kodlandığı bir tür "taraflı" kodlama kullanabilir.

İnt kodlaması "doğru" ise, derleyici taşıma biti ile ne yapılacağını tahmin edecektir. Ancak yanlışsa, ortaya çıkan mantık korkunç olacaktır.

Bu şekilde int kullanmanın makul mantık boyutu ve hızıyla sonuçlanması mümkündür, ancak bu bir garanti değildir. Farklı bir derleyiciye geçmek (örneğin XST'den Synopsis'e) veya farklı bir FPGA mimarisine gitmek, tam olarak yanlış olan şeyin olmasına neden olabilir.

İmzasız / İmzalı slv ile ilgili başka bir tartışma var. ABD Hükümeti komitesine VHDL'de bize bu kadar çok seçenek verdiği için teşekkür edebilirsiniz. :) Ben slv kullanın çünkü bu modüller ve çekirdekler arasındaki arayüz için standarttır. Bunun dışında ve simülasyonlardaki diğer bazı durumlarda, slv'yi imzalı / imzasız olarak kullanmanın büyük bir yararı olduğunu düşünmüyorum. İmzalı / imzasız üç belirtili sinyalleri destekleyip desteklemediğinden de emin değilim.


4
David, bu kod parçaları eşdeğer değil. Biri sıfırdan keyfi bir sayıya kadar sayar (pahalı bir karşılaştırma operatörü ile); diğeri gelişigüzel bir sayıdan sıfıra doğru geri sayar. Her iki algoritmayı da tamsayılarla veya vektörlerle yazabilirsiniz ve rastgele bir sayıya sayarken kötü sonuçlar ve sıfıra doğru sayan iyi sonuçlar elde edersiniz. Bir sıcak döngüden biraz daha fazla performans çıkarmaları gerekiyorsa , yazılım mühendislerinin de sıfıra geri sayılacağını unutmayın .
Philippe

1
Philippe gibi, bunun geçerli bir karşılaştırma olduğuna ikna olmadım. Tam sayı örneği geri sayılır ve kullanılırsa if (count-1) < 0, sentezleyicinin yürütme bitini çıkartacağını ve slv örneğinizle aynı devreyi üreteceğini düşünürüm. Ayrıca, unsignedbu gün türünü kullanmamalıyız :)
Martin Thompson

2
@DavidKessner kesinlikle THOROUGH ve iyi gerekçeli bir cevap verdiniz, + 1'imi aldınız. Yine de sormalıyım ... neden tasarım boyunca optimizasyon konusunda endişelisiniz? Çabalarınızı kod gerektiren alanlara odaklamak veya uyumluluk için arayüz noktaları (varlık portları) için SLV'lere odaklanmak daha iyi olmaz mı? Tasarımlarımın çoğunda zamanlamayı karşıladığı ve parçaya uyduğu sürece LUT kullanımının en aza indirgenmediğini biliyorum. Özellikle sıkı kısıtlamalara sahip olsaydım, kesinlikle en iyi tasarımın bilincindeydim, ama genel bir kural olarak değil.
akohlsmith

2
Bu cevaba verilen oyların sayısı beni biraz aştı. @ bit_vector @ kesinlikle mikro mimarileri modellemek ve optimize etmek için doğru soyutlama düzeyidir, ancak genel bir öneri sinyaller ve bağlantı noktası için @ integer @ gibi "yüksek düzey" türlerine karşı genel bir öneri garip bulduğum bir şeydir. Bu özelliklerin sağladığı değeri bilmek için soyutlama eksikliği nedeniyle yeterince kıvrık ve okunamayan kod gördüm ve onları geride bırakmak zorunda kalsaydım çok üzücü olurum.
trondd

2
@david Mükemmel açıklamalar. Yazılım gelişimine kıyasla hala ortaçağda olduğumuz doğrudur, ancak Quartus entegre sentezi ve Synplify ile yaşadığım deneyimden, şeylerin o kadar kötü olduğunu düşünmüyorum. Kayıt retiming ve okunabilirliği korurken performansı artıran diğer optimizasyonlar gibi birçok şeyi idare edebilirler. Çoğunluğun birkaç alet ve cihazı hedeflediğinden şüpheliyim, ancak davanız için en az ortak paydaya olan gereksinimi anlıyorum :-).
trondd

2

Benim tavsiyem her ikisini de denemek ve sonra sentez, harita ve yer ve rota raporlarına bakmak. Bu raporlar size her bir yaklaşımın tam olarak kaç LUT harcadığını ve mantığın çalışabileceği maksimum hızı size söyleyecektir.

David Kessner ile takım zincirinizin merhametinde olduğunuzu kabul ediyorum ve "doğru" cevap yok. Sentez kara büyüdür ve ne olduğunu bilmenin en iyi yolu, üretilen raporları dikkatlice ve iyice okumaktır. Xilinx araçları, FPGA'ın içinde, her bir LUT'un nasıl programlandığına, taşıma zincirinin nasıl bağlandığına, anahtar kumaşının tüm LUT'ları nasıl bağladığına vb.

Bay Kessner'in yaklaşımının bir başka dramatik örneği için, 1/2, 1/4, 1/8, 1/16, vb. De birden fazla saat frekansına sahip olmak istediğinizi düşünün. ve daha sonra bu tamsayı değerine karşı birden fazla karşılaştırıcıya sahip olun, her karşılaştırıcı çıkışı farklı bir saat bölümü oluşturur. Karşılaştırıcıların sayısına bağlı olarak, fanout mantıksız şekilde büyüyebilir ve sadece tamponlama için ekstra LUT'lar tüketmeye başlayabilir. SLV yaklaşımı vektörün her bir bitini bir çıktı olarak alacaktır.


1

Bunun açık bir nedeni, imzalı ve imzasız 32 bit tam sayıdan daha büyük değerlere izin vermesidir. Bu, VHDL dil tasarımında gerekli olmayan bir kusurdur. VHDL'nin yeni bir sürümü, keyfi boyutu (Java'nın BigInt'ına benzer) desteklemek için tamsayı değerleri gerektiren sorunu düzeltebilir.

Bunun dışında, vektörlerle karşılaştırıldığında tamsayı için farklı performans gösteren ölçütleri duymakla çok ilgileniyorum.

BTW, Jan Decaluwe bu konuda güzel bir deneme yazdı: Bu Ints Countin 'için yapılmıştır


Teşekkürler Philippe (bu "iç temsile erişim yoluyla daha iyi" bir uygulama olmasa da, gerçekten sonradan budur ...)
Martin Thompson

Bu deneme güzel, ancak temeldeki uygulamayı ve sonuçta ortaya çıkan mantık hızını ve boyutunu tamamen yok sayar. Decaluwe'nun söylediklerinin çoğuna katılıyorum, ancak sentez sonuçları hakkında hiçbir şey söylemiyor. Bazen sentezin sonuçları önemli değildir, bazen de önemlidir. Yani bu bir karar çağrısı.

1
@David, Jan'ın sentez araçlarının tamsayılara nasıl tepki verdiğiyle ilgili tam ayrıntılara girmediğini kabul ediyorum. Ama hayır, bu bir yargılama çağrısı değildir. Sentez sonuçlarını ölçebilir ve verilen sentez aracınızın sonuçlarını belirleyebilirsiniz. Bence OP, bizim için performansta bir fark gösteren kod parçaları ve sentez sonuçları üretmemiz için bir meydan okuma demekti.
Philippe

@Philippe Hayır, demek istediğim, sentez sonuçlarına hiç önem veriyorsanız, bunun bir karar çağrısı olduğunu kastediyorum. Sentez sonuçlarının kendileri bir yargılama çağrısı değildir.

@DavidKessner Tamam. Yanlış anlamışım.
Philippe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.