Membuat Daftar String dalam VHDL: Praktik Terbaik & Contoh
Teks string dalam VHDL umumnya terbatas pada array karakter dengan panjang tetap. Itu masuk akal karena VHDL menggambarkan perangkat keras, dan string dengan panjang generik memerlukan memori dinamis.
Untuk mendefinisikan array string, Anda harus mengalokasikan ruang pada waktu kompilasi untuk jumlah string tertinggi yang ingin disimpan. Dan lebih buruk lagi, Anda harus memutuskan panjang maksimal string dan mem-pad setiap kejadian ke jumlah karakter tersebut. Kode di bawah ini menunjukkan contoh penggunaan konstruksi semacam itu.
type arr_type is array (0 to 3) of string(1 to 10); signal arr : arr_type; begin arr(0) <= "Amsterdam "; arr(1) <= "Bangkok "; arr(2) <= "Copenhagen"; arr(3) <= "Damascus ";
Sementara itu masuk akal dari perspektif perangkat keras, penggunaan array string dalam testbench VHDL menjadi rumit. Oleh karena itu, saya memutuskan untuk membuat paket daftar string dinamis yang akan saya jelaskan dalam artikel ini.
Anda dapat mengunduh kode lengkap menggunakan formulir di bawah ini.
Kelas list Python
Mari kita model daftar VHDL dinamis kita berdasarkan implementasi list yang well-known. Daftar string VHDL kita akan meniru perilaku kelas list bawaan Python. Kami akan mengadopsi metode append(), insert(), dan pop() dari daftar Python.
Untuk menunjukkan apa yang saya maksud, saya akan langsung membuka shell python interaktif untuk menjalankan beberapa eksperimen.
Pertama, mari kita mulai dengan mendeklarasikan daftar dan menambahkan empat string ke dalamnya, seperti yang ditunjukkan di bawah ini.
IPython 7.19.0 -- An enhanced Interactive Python. Type '?' for help. In [1]: l = [] In [2]: l.append("Amsterdam") In [3]: l.append("Bangkok") In [4]: l.append("Copenhagen") In [5]: l.append("Damascus") Metode append() sederhana; ia menambahkan objek ke akhir daftar.
Kami dapat memverifikasi hal itu dengan metode pop(), yang menghapus elemen dan mengembalikannya ke pemanggil. Argumen menentukan posisi elemen yang akan diambil. Dengan mem-pop 0 hingga daftar kosong, kita mendapatkan konten yang diurutkan dari indeks terendah ke tertinggi:
In [6]: for _ in range(len(l)): print(l.pop(0)) Amsterdam Bangkok Copenhagen Damascus
Oke, mari kita isi ulang daftar. Dan kali ini, kami akan menggunakan metode insert() untuk menambahkan elemen daftar secara tidak terurut:
In [7]: l.insert(0, "Bangkok") In [8]: l.insert(1, "Copenhagen") In [9]: l.insert(0, "Amsterdam") In [10]: l.insert(3, "Damascus")
Fungsi insert() memungkinkan Anda menentukan pada indeks mana item baru akan dimasukkan. Dalam contoh di atas, kami membuat daftar yang sama seperti sebelumnya. Mari kita periksa dengan menelusuri daftar seperti array:
In [11]: for i in range(len(l)): print(l[i]) Amsterdam Bangkok Copenhagen Damascus
Operator list kurung siku [] Python tidak menghapus item; ia membuat daftar berperilaku seperti array. Anda mendapatkan konten slot yang diindeks oleh angka dalam kurung siku, seperti yang dapat Anda lihat dari daftar di atas.
Mari kita kosongkan daftar dengan mem-pop, tapi kali ini dari akhir daftar. Keunikan daftar Python adalah Anda dapat menggunakan indeks negatif untuk menghitung dari item terakhir daripada awal daftar. Ini bekerja dengan operator kurung dan dengan metode insert() atau pop().
Dengan mem-pop indeks -1, Anda selalu mendapatkan item terakhir dari daftar. Kita letakkan itu dalam loop For, itu akan mengosongkan daftar dalam urutan terbalik:
In [12]: for _ in range(len(l)): print(l.pop(-1)) Damascus Copenhagen Bangkok Amsterdam
Anda dapat menggunakan indeks negatif untuk memasukkan juga. Pada baris terakhir contoh di bawah ini, kami memasukkan “Copenhagen” pada indeks -1:
In [13]: l.append("Amsterdam") In [14]: l.append("Bangkok") In [15]: l.append("Damascus") In [16]: l.insert(-1, "Copenhagen") # insert at the second last position Ketika kita menelusuri daftar, kita dapat melihat bahwa “Copenhagen” sekarang adalah elemen kedua dari akhir:
In [17]: for i in range(len(l)): print(l[i]) Amsterdam Bangkok Copenhagen Damascus
Sekarang, inilah intinya (tapi itu masuk akal).
Saat memasukkan ke -1, item baru menjadi kedua dari akhir, tetapi saat mem-pop dari -1, kita mendapatkan item terakhir.
Itu masuk akal karena -1 merujuk pada posisi elemen terakhir yang saat ini ada dalam daftar. Dan saat kita mem-pop, kita meminta elemen terakhir. Tetapi saat kita memasukkan, kita meminta untuk memasukkan item baru pada posisi elemen terakhir yang saat ini ada dalam daftar. Jadi, item baru menggeser elemen akhir dengan satu slot.
Kita dapat mengonfirmasi ini dengan mem-pop elemen -1, yang mengembalikan “Damascus”, dan bukan “Copenhagen”:
In [18]: l.pop(-1) # pop from the last position Out[18]: 'Damascus'
Daftar sekarang berisi tiga elemen:
In [19]: for i in range(len(l)): print(l[i]) Amsterdam Bangkok Copenhagen
Juga mungkin untuk menghitung panjang daftar seperti ini:
In [20]: len(l) Out[20]: 3
Dan kita dapat mengosongkan daftar dengan memanggil clear():
In [21]: l.clear() In [22]: len(l) Out[22]: 0
Seperti yang Anda lihat, daftar Python serbaguna, dan banyak programmer memahaminya. Itulah sebabnya saya akan membangun implementasi daftar VHDL saya berdasarkan rumus sukses ini.
Prototipe Subprogram Daftar String VHDL
Untuk memungkinkan kita bekerja dengan daftar string seperti objek dengan metode anggota, kita harus mendeklarasikannya sebagai tipe terlindungi. Dan kita akan menempatkan tipe terlindungi dalam paket dengan nama yang sama: string_list.
Kode di bawah ini menunjukkan bagian “publik” dari daftar tipe terlindungi yang mencantumkan prototipe subprogram.
package string_list is type string_list is protected procedure append(str : string); procedure insert(index : integer; str : string); impure function get(index : integer) return string; procedure delete(index : integer); procedure clear; impure function length return integer; end protected; end package;
Sementara prosedur append(), insert(), dan clear() identik dengan sepakaran Python mereka, kita tidak dapat mem-port fungsi pop() secara langsung ke VHDL. Masalahnya adalah kita tidak dapat dengan mudah melewatkan objek dinamis keluar dari tipe terlindungi dalam VHDL.
Untuk mengatasi keterbatasan ini, saya telah membagi fungsionalitas pop() menjadi dua subprogram: get() dan delete(). Itu akan memungkinkan kita untuk mengindeks elemen terlebih dahulu, seperti array, dan kemudian menghapusnya ketika kita tidak lagi membutuhkannya. Misalnya, setelah kita mencetak string ke konsol simulator.
Fungsi impure length() akan berperilaku seperti fungsi bawaan Python len(). Ini akan mengembalikan jumlah string dalam daftar.
Implementasi Daftar String VHDL
Tipe terlindungi terdiri dari dua bagian: bagian deklaratif dan badan. Sementara bagian deklaratif terlihat oleh pengguna, badan berisi implementasi subprogram dan variabel pribadi apa pun. Sekarang saatnya untuk mengungkapkan kerja internal daftar string.
Tinggalkan alamat email Anda dalam formulir di bawah ini untuk menerima kode lengkap dan proyek ModelSim di kotak masuk Anda!
Kami akan menggunakan linked list satu arah sebagai struktur data internal.
Baca juga: Cara membuat Linked List dalam VHDL
Karena semua kode yang berikut berada dalam badan tipe terlindungi, konstruksi ini tidak dapat diakses secara langsung di luar paket ini. Semua komunikasi harus melalui subprogram yang tercantum di wilayah deklaratif yang kita bahas di bagian sebelumnya.
Tipe Penyimpanan Data dan Variabel
Seperti yang dapat Anda lihat dari kode di bawah ini, kita pertama-tama mendeklarasikan tipe akses, pointer VHDL ke string dalam memori dinamis. Ketika kita berbicara tentang memori dinamis, itu bukan DRAM pada FPGA karena kode ini tidak dapat disintesiskan. Daftar string adalah komponen simulasi murni, dan akan menggunakan memori dinamis komputer yang menjalankan simulasi.
type str_ptr is access string; type item; type item_ptr is access item; type item is record str : str_ptr; next_item : item_ptr; end record;
Setelah str_ptr, kita mendeklarasikan item sebagai tipe yang belum lengkap. Kita harus melakukannya seperti itu karena pada baris berikutnya, kita merujuk ke item saat membuat item_ptr.
Dan akhirnya, kita menentukan deklarasi lengkap tipe item, sebuah record yang berisi pointer string dan pointer ke elemen berikutnya. Ada ketergantungan lingkaran di antara tipe item->item_ptr->item, dan dengan mendeklarasikan tipe item yang belum lengkap terlebih dahulu, kita menghindari kesalahan kompilasi.
Tipe terlindungi berisi dua variabel, yang ditunjukkan di bawah ini: root dan length_i. Item yang ditunjuk oleh root akan menjadi elemen pertama daftar, indeks array nol. Dan variabel length_i akan selalu mencerminkan jumlah string dalam daftar.
variable root : item_ptr; variable length_i : integer := 0;
Prosedur Append
Prosedur append() yang ditunjukkan di bawah ini adalah notasi singkat untuk memasukkan string pada posisi terakhir daftar.
procedure append(str : string) is begin insert(length_i, str); end procedure;
Seperti yang dibahas dalam contoh Python, mudah untuk memasukkan pada posisi kedua dari akhir menggunakan indeks -1: insert(-1, str). Tetapi memasukkan pada posisi terakhir memerlukan panjang daftar sebagai argumen indeks. Itu mungkin sebabnya daftar Python memiliki metode append() khusus, dan kami akan memiliki satu juga.
Prosedur Insert
Prosedur insert yang ditunjukkan di bawah ini bekerja dalam empat langkah.
Pertama, kita membuat objek item dinamis dengan menggunakan kata kunci VHDL new. Kita pertama-tama membuat objek item daftar dan kemudian objek string dinamis untuk disimpan di dalamnya.
procedure insert(index : integer; str : string) is variable new_item : item_ptr; variable node : item_ptr; variable index_v : integer; begin -- Create the new object new_item := new item; new_item.str := new string'(str); -- Restrict the index to the list range if index >= length_i then index_v := length_i; elsif index <= -length_i then index_v := 0; else index_v := index mod length_i; end if; if index_v = 0 then -- The new object becomes root when inserting at position 0 new_item.next_item := root; root := new_item; else -- Find the node to insert after node := root; for i in 2 to index_v loop node := node.next_item; end loop; -- Insert the new item new_item.next_item := node.next_item; node.next_item := new_item; end if; length_i := length_i + 1; end procedure;
Langkah nomor dua adalah menterjemahkan argumen indeks ke indeks yang sesuai dengan rentang daftar. Implementasi Python list.insert() mengizinkan indeks di luar batas, dan daftar VHDL kami juga akan mengizinkannya. Jika pengguna merujuk pada indeks yang terlalu tinggi atau rendah, itu akan default ke indeks tertinggi, atau elemen 0. Juga, kita menggunakan operator modulo untuk menterjemahkan indeks negatif dalam batas ke posisi array positif.
Pada langkah nomor tiga, kita menelusuri daftar untuk menemukan node yang akan disisipkan setelahnya. Seperti biasa, dengan linked list, kita harus secara eksplisit menangani kasus khusus memasukkan pada root.
Langkah keempat dan terakhir adalah meningkatkan variabel length_i untuk memastikan pencatatan buku terkini.
Fungsi Internal get_index dan get_node
Karena keterbatasan pemilahan objek VHDL, kami memutuskan untuk membagi pop() menjadi dua subprogram: get() dan delete(). Fungsi pertama akan mengambil item, dan prosedur kedua akan menghapusnya dari daftar.
Tetapi algoritma untuk mencari indeks atau objek sama untuk get() dan delete(), jadi kita dapat mengimplementasikannya secara terpisah dalam dua fungsi pribadi: get_index() dan get_node().
Berbeda dengan insert(), fungsi Python pop() tidak mengizinkan indeks di luar batas, dan fungsi get_index() kami juga tidak. Untuk melindungi dari kesalahan pengguna, kita akan menimbulkan kegagalan assersi jika indeks yang diminta di luar batas, seperti yang ditunjukkan di bawah ini.
impure function get_index(index : integer) return integer is begin assert index >= -length_i and index < length_i report "get index out of list range" severity failure; return index mod length_i; end function;
Fungsi get_node() yang ditunjukkan di bawah ini melangkah lebih jauh dan menemukan objek aktual pada indeks yang ditentukan. Ini menggunakan get_index() untuk mencari node yang benar dan mengembalikan pointer ke objek item.
impure function get_node(index : integer) return item_ptr is variable node : item_ptr; begin node := root; for i in 1 to get_index(index) loop node := node.next_item; end loop; return node; end function;
Fungsi Get
Karena fungsi pribadi get_node(), fungsi publik get() menjadi cukup sederhana. Ini adalah satu baris yang mengambil node yang benar, membuka konten string, dan mengembalikannya ke pemanggil.
impure function get(index : integer) return string is begin return get_node(index).str.all; end function;
Prosedur Delete
Prosedur delete() juga menggunakan get_index() dan get_node() untuk menyederhanakan algoritma. Pertama, kita menggunakan get_index() untuk menemukan indeks objek yang akan dihapus, seperti yang ditunjukkan dalam deklarasi konstanta index_c di bawah ini.
procedure delete(index : integer) is constant index_c : integer := get_index(index); variable node : item_ptr; variable parent_node : item_ptr; begin if index_c = 0 then node := root; root := root.next_item; else parent_node := get_node(index_c - 1); node := parent_node.next_item; parent_node.next_item := node.next_item; end if; deallocate(node.str); deallocate(node); length_i := length_i - 1; end procedure;
Kemudian, kita melepas node dari daftar. Jika itu objek root, kita mengatur item berikutnya sebagai root. Jika tidak, kita menggunakan get_node() untuk menemukan induk dan menghubungkan ulang daftar untuk melepas item yang bersangkutan.
Dan akhirnya, kita membebaskan memori dengan memanggil kata kunci VHDL deallocate dan memperbarui variabel pencatatan buku length_i.
Prosedur Clear
Untuk menghapus semua item, prosedur clear() menelusuri daftar menggunakan loop While, memanggil delete() pada setiap elemen hingga tidak ada yang tersisa.
procedure clear is begin while length_i > 0 loop delete(0); end loop; end procedure;
Fungsi Length
Untuk mematuhi praktik pemrograman yang baik, kami menyediakan fungsi getter daripada membiarkan pengguna mengakses variabel length_i secara langsung.
impure function length return integer is begin return length_i; end function;
Perbedaannya tidak akan terlihat oleh pengguna karena Anda tidak memerlukan tanda kurung untuk memanggil fungsi tanpa parameter (my_list.length). Tetapi pengguna tidak dapat mengubah variabel pencatatan buku internal, dan itu adalah jaga-jadi terhadap penyalahgunaan.
Menggunakan Daftar String dalam Testbench
Sekarang implementasi daftar sudah lengkap, saatnya untuk kita menjalankannya dalam testbench. Pertama, kita harus mengimpor tipe terlindungi dari paket, seperti yang ditunjukkan pada baris pertama kode di bawah ini.
use work.string_list.string_list; entity string_list_tb is end string_list_tb; architecture sim of string_list_tb is shared variable l : string_list; ...
Tipe terlindungi adalah konstruksi mirip kelas VHDL, dan kita dapat membuat objeknya dengan mendeklarasikan variabel shared bertipe string_list, seperti yang ditunjukkan pada baris terakhir di atas. Kami akan menamainya ‘l’ untuk “list” untuk mereplikasi contoh Python yang saya sajikan di awal artikel ini.
Dari sekarang, kita dapat menggunakan pendekatan perangkat lunak untuk mengakses data daftar. Seperti yang ditunjukkan dalam proses testbench di bawah ini, kita dapat merujuk subprogram publik dengan menggunakan notasi titik pada variabel shared (l.append("Amsterdam")).
begin SEQUENCER_PROC : process begin print("* Append four strings"); print(" l.append(Amsterdam)"); l.append("Amsterdam"); print(" l.append(Bangkok)"); l.append("Bangkok"); print(" l.append(Copenhagen)"); l.append("Copenhagen"); print(" l.append(Damascus)"); l.append("Damascus"); ... Saya telah menghilangkan testbench dan skrip run lengkap untuk mengurangi panjang artikel ini, tetapi Anda dapat memintanya dengan meninggalkan alamat email Anda dalam formulir di bawah ini. Anda akan menerima file Zip yang berisi kode VHDL lengkap dan proyek ModelSim di kotak masuk Anda dalam beberapa menit.
Menjalankan Testbench
Jika Anda mengunduh proyek contoh menggunakan formulir di atas, Anda seharusnya dapat mereplikasi output berikut. Lihat “How to run.txt” di file Zip untuk instruksi yang tepat.
Ini adalah testbench pemeriksaan manual, dan saya telah membuat kasus uji mirip dengan contoh Python saya sejauh mungkin. Alih-alih metode Python pop(), kita menggunakan fungsi get() daftar VHDL diikuti dengan panggilan ke delete(). Itu melakukan hal yang sama.
Seperti yang dapat kita lihat dari cetakan ke konsol ModelSim yang ditunjukkan di bawah ini, daftar VHDL berperilaku mirip dengan sepakaran Python-nya.
# * Append four strings # l.append(Amsterdam) # l.append(Bangkok) # l.append(Copenhagen) # l.append(Damascus) # * Pop all strings from the beginning of the list # l.get(0): Amsterdam # l.get(1): Bangkok # l.get(2): Copenhagen # l.get(3): Damascus # * Insert four strings in shuffled order # l.insert(0, Bangkok) # l.insert(1, Copenhagen) # l.insert(0, Amsterdam) # l.insert(3, Damascus) # * Traverse the list like an array # l.get(0): Amsterdam # l.get(1): Bangkok # l.get(2): Copenhagen # l.get(3): Damascus # * Pop all strings from the end of the list # l.get(0): Damascus # l.get(1): Copenhagen # l.get(2): Bangkok # l.get(3): Amsterdam # * Append and insert at the second last position # l.append(Amsterdam) # l.append(Bangkok) # l.append(Damascus) # l.insert(-1, Copenhagen) # * Pop from the last position # l.get(-1): Damascus # * Traverse the list like an array # l.get(0): Amsterdam # l.get(1): Bangkok # l.get(2): Copenhagen # * Check the list length # l.length: 3 # * Clear the list # * Check the list length # l.length: 0 # * Done
Pikiran Akhir
Saya pikir fitur pemrograman tingkat tinggi VHDL kurang dihargai. Meskipun tidak berguna untuk desain RTL karena tidak dapat disintesiskan, itu dapat bermanfaat untuk tujuan verifikasi.
Meskipun mungkin rumit untuk diimplementasikan, itu akan lebih mudah untuk pengguna akhir, orang yang menulis testbench yang menggunakan tipe terlindungi. Tipe terlindungi menyembunyikan semua kompleksitas dari pengguna.
Hanya butuh tiga baris untuk menggunakan tipe terlindungi: impor paket, deklarasikan variabel shared, dan panggil subprogram dalam proses testbench. Itu lebih sederhana daripada membuat instance komponen VHDL.
Lihat juga: Cara membuat Linked List dalam VHDL
Beritahu saya apa pendapat Anda di bagian komentar di bawah artikel!