Kapan sebaiknya menggunakan representasi VECTOR vs INTEGER?

11

Di utas komentar pada jawaban untuk pertanyaan ini: Output yang salah dalam entitas VHDL dinyatakan:

"Dengan bilangan bulat, Anda tidak memiliki kontrol atau akses ke representasi logika internal di FPGA, sementara SLV memungkinkan Anda melakukan trik seperti memanfaatkan rantai pembawa secara efisien"

Jadi, dalam keadaan apa Anda merasa lebih rapi untuk menggunakan vektor representasi bit daripada menggunakan integer untuk mengakses representasi internal? Dan apa kelebihan yang Anda ukur (dalam hal area chip, frekuensi clock, delay, atau sebaliknya.)?

Martin Thompson
sumber
Saya pikir itu sesuatu yang sulit untuk diukur, karena tampaknya itu hanya masalah kontrol atas implementasi tingkat rendah.
clabacchio

Jawaban:

5

Saya telah menulis kode yang disarankan oleh dua poster lain di keduanya vectordan dalam integerbentuk, berhati-hati agar kedua versi beroperasi dengan cara yang sama mungkin.

Saya membandingkan hasil dalam simulasi dan kemudian disintesis menggunakan penargetan Synplify Pro Xilinx Spartan 6. Sampel kode di bawah ini ditempel dari kode kerja, jadi Anda harus dapat menggunakannya dengan synthesizer favorit Anda dan lihat apakah perilakunya sama.


Downcounters

Pertama, downcounter, seperti yang disarankan oleh David Kessner:

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;

Arsitektur vektor:

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;

Arsitektur integer

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;

Hasil

Dari segi kode, bilangan bulat sepertinya lebih disukai bagi saya karena menghindari to_unsigned()panggilan. Kalau tidak, tidak banyak yang bisa dipilih.

Menjalankannya melalui Synplify Pro dengan top := 16#7fff_fffe#menghasilkan 66 LUT untuk vectorversi dan 64 LUT untuk integerversi. Kedua versi banyak menggunakan rantai-bawa. Keduanya melaporkan kecepatan clock lebih dari 280MHz . Synthesizer ini cukup mampu membangun penggunaan yang baik dari rantai carry - saya diverifikasi secara visual dengan RTL viewer bahwa logika serupa dihasilkan dengan keduanya Jelas up-counter dengan komparator akan lebih besar, tetapi itu akan sama dengan bilangan bulat dan vektor lagi.


Dibagi dengan 2 ** n penghitung

Disarankan oleh ajs410:

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;

Arsitektur vektor

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;

Arsitektur integer

Anda harus melompati beberapa simpai untuk menghindari hanya menggunakan to_unsigneddan kemudian mengambil bit yang jelas akan menghasilkan efek yang sama seperti di atas:

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;

Hasil

Dari segi kode, dalam hal ini, vectorversinya jelas lebih baik!

Dalam hal hasil sintesis, untuk contoh kecil ini, versi integer (seperti yang diprediksikan ajs410) memang menghasilkan 3 LUT tambahan sebagai bagian dari pembanding, saya terlalu optimis dengan synthesizer, meskipun ia bekerja dengan sepotong kode yang sangat dikaburkan!


Penggunaan lainnya

Vektor adalah kemenangan yang jelas ketika Anda ingin aritmatika membungkus (penghitung dapat dilakukan sebagai satu garis genap):

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

vs.

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

meskipun setidaknya jelas dari kode itu bahwa penulis bermaksud membungkus.


Sesuatu yang saya tidak gunakan dalam kode-nyata, tetapi merenungkan:

Fitur "pembungkus alami" juga dapat digunakan untuk "komputasi melalui luapan". Ketika Anda tahu bahwa output dari rantai penambahan / pengurangan dan perkalian dibatasi, Anda tidak harus menyimpan bit tinggi dari perhitungan perantara karena (dalam komplemen 2-s) ia akan keluar "dalam pencucian" pada saat Anda mencapai output. Saya diberitahu bahwa makalah ini berisi bukti tentang ini, tetapi tampak agak padat bagi saya untuk membuat penilaian cepat! Teori Penambahan dan Kelimpahan Komputer - HL Garner

Menggunakan integers dalam situasi ini akan menyebabkan kesalahan simulasi ketika dibungkus, meskipun kita tahu mereka akan membukanya pada akhirnya.


Dan seperti yang ditunjukkan Philippe, ketika Anda membutuhkan angka yang lebih besar dari 2 ** 31 Anda tidak punya pilihan selain menggunakan vektor.

Martin Thompson
sumber
Di blok kode kedua yang Anda miliki variable c : unsigned(32 downto 0);... bukankah cvariabel 33 bit?
clabacchio
@clabacchio: ya, itu memungkinkan akses ke 'carry-bit' untuk melihat bungkusnya.
Martin Thompson
5

Saat menulis VHDL, saya sangat merekomendasikan menggunakan std_logic_vector (slv) daripada integer (int) untuk SIGNALS . (Di sisi lain, menggunakan int untuk generik, beberapa konstanta, dan beberapa variabel bisa sangat berguna.) Sederhananya, jika Anda mendeklarasikan sinyal tipe int, atau harus menentukan rentang untuk integer maka Anda mungkin melakukan sesuatu yang salah.

Masalah dengan int adalah bahwa programmer VHDL tidak tahu apa representasi logika internal dari int, sehingga kami tidak dapat memanfaatkannya. Sebagai contoh, jika saya mendefinisikan int kisaran 1 hingga 10, saya tidak tahu bagaimana kompiler mengkodekan nilai-nilai itu. Semoga itu akan dikodekan sebagai 4 bit, tetapi kita tidak tahu banyak tentang itu. Jika Anda dapat menyelidiki sinyal di dalam FPGA itu mungkin dikodekan sebagai "0001" menjadi "1010", atau dikodekan sebagai "0000" menjadi "1001". Mungkin juga dikodekan dengan cara yang sama sekali tidak masuk akal bagi kita manusia.

Sebaliknya, kita hanya harus menggunakan slv daripada int, karena dengan demikian kita memiliki kontrol atas pengkodean dan juga memiliki akses langsung ke bit individu. Memiliki akses langsung itu penting, seperti yang akan Anda lihat nanti.

Kita bisa memberikan int ke slv kapan pun kita membutuhkan akses ke bit individual, tetapi itu menjadi sangat berantakan, sangat cepat. Itu seperti mendapatkan yang terburuk dari kedua dunia alih-alih yang terbaik dari kedua dunia. Kode Anda akan sulit untuk dioptimalkan oleh kompiler, dan hampir mustahil untuk Anda baca. Saya tidak merekomendasikan ini.

Jadi, seperti yang saya katakan, dengan slv Anda memiliki kontrol atas penyandian bit dan akses langsung ke bit. Jadi apa yang dapat Anda lakukan dengan ini? Saya akan menunjukkan beberapa contoh. Katakanlah Anda perlu mengeluarkan pulsa setiap 4.294.000.000 jam. Inilah cara Anda melakukan ini dengan 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;

Dan kode yang sama menggunakan 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;

Sebagian besar kode ini identik antara int dan slv, setidaknya dalam arti ukuran dan kecepatan logika yang dihasilkan. Tentu saja satu menghitung mundur dan yang lainnya menghitung mundur, tetapi itu tidak penting untuk contoh ini.

Perbedaannya ada pada "garis penting".

Dengan contoh int, ini akan menghasilkan komparator 32-input. Dengan 4-Input LUT yang digunakan Xilinx Spartan-3, ini akan membutuhkan 11 LUT dan 3 level logika. Beberapa kompiler mungkin mengubahnya menjadi pengurangan yang akan menggunakan rantai carry dan span setara dengan 32 LUT tetapi mungkin berjalan lebih cepat dari 3 level logika.

Dengan contoh slv, tidak ada perbandingan 32-bit sehingga "nol LUT, nol tingkat logika". Satu-satunya penalti adalah bahwa counter kami adalah satu bit ekstra. Karena waktu tambahan untuk penghitung ekstra ini semuanya ada dalam rantai penampung, ada penundaan waktu tambahan "hampir nol".

Tentu saja ini adalah contoh ekstrem, karena kebanyakan orang tidak akan menggunakan penghitung 32-bit dengan cara ini. Itu berlaku untuk penghitung yang lebih kecil, tetapi perbedaannya akan kurang dramatis meskipun masih signifikan.

Ini hanya satu contoh bagaimana memanfaatkan slv over int untuk mendapatkan pengaturan waktu yang lebih cepat. Ada banyak cara lain untuk memanfaatkan slv - hanya dibutuhkan beberapa imajinasi.

Pembaruan: Menambahkan hal-hal untuk menanggapi komentar Martin Thompson tentang penggunaan int dengan "if (count-1) <0"

(Catatan: Saya berasumsi Anda bermaksud "jika menghitung <0", karena itu akan membuatnya lebih setara dengan versi slv saya dan menghapus kebutuhan untuk pengurangan tambahan itu.)

Dalam beberapa keadaan ini mungkin menghasilkan implementasi logika yang dimaksud tetapi tidak dijamin berfungsi sepanjang waktu. Itu akan tergantung pada kode Anda dan bagaimana kompiler Anda mengkodekan nilai int.

Bergantung pada kompiler Anda, dan bagaimana Anda menentukan rentang int Anda, sangat mungkin bahwa nilai int nol tidak menyandikan ke vektor bit "0000 ... 0000" ketika membuatnya menjadi logika FPGA. Agar variasi Anda berfungsi, itu harus disandikan ke "0000 ... 0000".

Misalnya, katakanlah Anda menetapkan int untuk memiliki rentang -5 hingga +5. Anda mengharapkan nilai 0 dikodekan menjadi 4 bit seperti "0000", dan +5 sebagai "0101" dan -5 sebagai "1011". Ini adalah skema pengkodean dua-pelengkap khas.

Tetapi jangan berasumsi bahwa kompiler akan menggunakan dua-pelengkap. Meskipun tidak biasa, komplemen satu dapat menghasilkan logika "lebih baik". Atau, kompiler dapat menggunakan semacam pengkodean "bias" di mana -5 dikodekan sebagai "0000", 0 sebagai "0101", dan +5 sebagai "1010".

Jika pengkodean int adalah "benar" maka kompiler kemungkinan akan menyimpulkan apa yang harus dilakukan dengan carry bit. Tetapi jika itu salah maka logika yang dihasilkan akan mengerikan.

Mungkin saja menggunakan int dengan cara ini bisa menghasilkan ukuran dan kecepatan logika yang masuk akal, tetapi itu bukan jaminan. Beralih ke kompiler yang berbeda (XST ke Sinopsis misalnya), atau pergi ke arsitektur FPGA yang berbeda dapat menyebabkan hal yang salah terjadi.

Belum ditandatangani / ditandatangani vs slv adalah debat lain. Anda dapat berterima kasih kepada komite Pemerintah AS karena telah memberi kami begitu banyak pilihan dalam VHDL. :) Saya menggunakan slv karena itu adalah standar untuk antarmuka antara modul dan inti. Selain itu, dan beberapa kasus lain dalam simulasi, saya tidak berpikir ada manfaat besar untuk menggunakan slv lebih dari yang ditandatangani / tidak ditandatangani. Saya juga tidak yakin apakah sinyal tri-menyatakan dukungan yang ditandatangani / tidak ditandatangani.

Martin Thompson
sumber
4
David, fragmen kode itu tidak setara. Satu dihitung dari nol ke nomor arbitrer (dengan operator perbandingan mahal); yang lain menghitung mundur ke nol dari angka arbitrer. Anda dapat menulis kedua algoritme dengan bilangan bulat atau vektor, dan Anda akan mendapatkan hasil yang buruk saat menghitung ke angka arbitrer dan hasil yang baik dihitung ke nol. Perhatikan bahwa insinyur perangkat lunak juga akan menghitung mundur ke nol jika mereka perlu memeras sedikit lebih banyak kinerja dari loop panas.
Philippe
1
Seperti Philippe, saya tidak yakin bahwa ini adalah perbandingan yang valid. Jika contoh bilangan bulat dihitung mundur dan digunakan if (count-1) < 0saya pikir synthesizer akan menyimpulkan bit melaksanakan dan menghasilkan banyak sirkuit yang sama dengan contoh slv Anda. Juga, seharusnya kita tidak menggunakan unsignedtipe ini hari ini :)
Martin Thompson
2
@ Davidvidess Anda pasti telah memberikan jawaban yang beralasan dan beralasan, Anda punya +1 saya. Saya harus bertanya ... mengapa Anda khawatir tentang pengoptimalan di seluruh desain? Bukankah lebih baik memfokuskan upaya Anda pada bidang kode yang memerlukannya atau fokus pada SLV untuk titik antarmuka (port entitas) untuk kompatibilitas? Saya tahu bahwa di sebagian besar desain saya, saya tidak terlalu peduli bahwa penggunaan LUT diminimalkan, asalkan memenuhi waktu dan sesuai dengan bagian. Jika saya memiliki kendala yang sangat ketat saya pasti akan lebih sadar akan desain yang optimal, tetapi tidak sebagai aturan umum.
akohlsmith
2
Saya sedikit dikalahkan oleh jumlah suara pada jawaban ini. @ bit_vector @ tentu saja adalah level abstraksi yang benar untuk pemodelan dan pengoptimalan arsitektur mikro, tetapi rekomendasi umum untuk tipe "tingkat tinggi" seperti @ integer @ untuk sinyal dan port adalah sesuatu yang saya temukan aneh. Saya telah melihat kode yang cukup berbelit-belit dan tidak dapat dibaca karena kurangnya abstraksi untuk mengetahui nilai yang diberikan fitur-fitur ini, dan akan sangat sedih jika saya harus meninggalkannya.
trondd
2
@david. Pernyataan yang sangat baik. Memang benar kita masih berada di abad pertengahan dibandingkan dengan pengembangan perangkat lunak dalam banyak hal, tetapi dari pengalaman saya dengan sintesis terintegrasi Quartus dan Synplify, saya tidak berpikir hal-hal seburuk itu. Mereka cukup mampu menangani banyak hal seperti retiming register dan optimasi lainnya yang meningkatkan kinerja sambil mempertahankan keterbacaan. Saya ragu mayoritas menargetkan beberapa alat dan perangkat, tetapi untuk kasus Anda, saya memahami persyaratan untuk penyebut yang paling tidak umum :-).
trondd
2

Saran saya adalah mencoba keduanya, dan kemudian lihat sintesis, peta, dan laporan tempat-dan-rute. Laporan-laporan ini akan memberi tahu Anda dengan tepat berapa banyak LUT yang digunakan setiap pendekatan, mereka juga akan memberi tahu Anda kecepatan maksimum di mana logika dapat beroperasi.

Saya setuju dengan David Kessner bahwa Anda berada di bawah kekuasaan rantai alat Anda, dan tidak ada jawaban "benar". Sintesis adalah ilmu hitam dan cara terbaik untuk mengetahui apa yang terjadi adalah dengan cermat dan cermat membaca laporan yang dihasilkan. Alat-alat Xilinx bahkan memungkinkan Anda untuk melihat di dalam FPGA, sampai bagaimana setiap LUT diprogram, bagaimana rantai pembawa terhubung, bagaimana kain sakelar menghubungkan semua LUT, dll.

Untuk contoh dramatis lain dari pendekatan Mr. Kessner, bayangkan Anda ingin memiliki beberapa frekuensi clock pada 1/2, 1/4, 1/8, 1/16, dll. Anda bisa menggunakan integer yang terus-menerus menghitung setiap siklus, dan kemudian memiliki beberapa komparator terhadap nilai integer itu, dengan masing-masing output komparator membentuk divisi clock yang berbeda. Bergantung pada jumlah pembanding, fanout bisa menjadi sangat besar dan mulai mengkonsumsi LUT tambahan hanya untuk buffering. Pendekatan SLV hanya akan mengambil setiap bit individu dari vektor sebagai output.

ajs410
sumber
1

Salah satu alasan yang jelas adalah bahwa ditandatangani dan tidak ditandatangani memungkinkan nilai lebih besar dari integer 32 bit. Itu adalah cacat dalam desain bahasa VHDL, yang tidak penting. Versi baru VHDL dapat memperbaikinya, membutuhkan nilai integer untuk mendukung ukuran arbitrer (mirip dengan BigInt Java).

Selain itu, saya sangat tertarik untuk mendengar tentang tolok ukur yang berkinerja berbeda untuk bilangan bulat dibandingkan dengan vektor.

BTW, Jan Decalukami menulis esai yang bagus tentang ini: Ints ini dibuat untuk Countin '

Philippe
sumber
Terima kasih Philippe (walaupun itu bukan aplikasi "lebih baik melalui akses ke representasi internal", yang benar-benar saya cari ...)
Martin Thompson
Esai itu bagus, tetapi sepenuhnya mengabaikan implementasi yang mendasarinya dan menghasilkan kecepatan dan ukuran logika. Saya setuju dengan sebagian besar apa yang dikatakan Decaluwe, tetapi dia tidak mengatakan apa-apa tentang hasil sintesis. Kadang-kadang hasil sintesis tidak penting, dan kadang-kadang hasilnya. Jadi itu panggilan penilaian.
1
@ David, saya setuju bahwa Jan tidak masuk ke detail penuh tentang bagaimana alat sintesis bereaksi terhadap bilangan bulat. Tapi, tidak, itu bukan panggilan penilaian. Anda dapat mengukur hasil sintesis dan menentukan hasil dari alat sintesis yang Anda berikan. Saya pikir OP berarti pertanyaannya sebagai tantangan bagi kami untuk menghasilkan fragmen kode dan hasil sintesis yang menunjukkan perbedaan (jika ada) dalam kinerja.
Philippe
@ Pilipi Tidak, maksud saya ini adalah penilaian jika Anda peduli sama sekali tentang hasil sintesis sama sekali. Bukan berarti hasil sintesis itu sendiri adalah panggilan penilaian.
@ DavidKessner OK. Saya salah paham.
Philippe