Pernahkah Anda memiliki pertanyaan "bagaimana cara kerja prosesor?". Ya, ya, persis yang ada di PC / laptop / smartphone Anda. Pada artikel ini saya ingin memberikan contoh prosesor yang diciptakan sendiri dengan desain di Verilog. Verilog tidak persis seperti bahasa pemrograman. Ini adalah Bahasa Deskripsi Perangkat Keras. Kode tertulis tidak dieksekusi oleh apa pun (kecuali jika Anda menjalankannya dalam simulator, tentu saja), tetapi berubah menjadi desain sirkuit fisik, atau dalam bentuk yang dirasakan oleh FPGA (Field Programmable Gate Array).
Penafian: artikel ini adalah hasil dari pengerjaan proyek di universitas, sehingga waktu untuk pekerjaan terbatas dan banyak bagian dari proyek masih hanya pada tahap awal pengembangan.
Harap dicatat bahwa prosesor yang dibuat dalam artikel ini memiliki sedikit kesamaan dengan prosesor modern yang tersebar luas, tetapi saya mencoba untuk mencapai tujuan yang sedikit berbeda dengan pembuatannya.
Untuk benar-benar memahami proses pemrograman, Anda perlu membayangkan bagaimana masing-masing alat yang digunakan bekerja: kompiler / juru bahasa, mesin virtual, jika ada, kode perantara, dan, tentu saja, prosesor itu sendiri. Sangat sering orang yang mempelajari pemrograman berada pada tahap pertama untuk waktu yang lama - mereka hanya memikirkan bagaimana bahasa dan kompilernya bekerja. Hal ini sering menyebabkan kesalahan yang solusinya tidak diketahui oleh programmer pemula, karena ia tidak tahu dari mana akar masalah ini berasal. Saya sendiri melihat beberapa contoh langsung di mana situasinya seperti dalam uraian di atas, jadi saya memutuskan untuk mencoba memperbaiki situasi ini dan membuat serangkaian hal yang akan membantu pemula untuk memahami semua tahapan.
Kit ini terdiri dari:
- Bahasa yang sebenarnya diciptakan
- Sorot plugin untuk Kode VS
- Kompilator untuk itu
- Set instruksi
- Prosesor sederhana yang mampu mengeksekusi set instruksi ini (ditulis dalam Verilog)
Saya mengingatkan Anda sekali lagi bahwa artikel ini TIDAK MENJELASKAN APA SAJA YANG SAMA DENGAN PROSESOR NYATA MODERN, artikel ini menjelaskan model yang mudah dipahami tanpa merinci.
Hal-hal yang Anda perlukan jika Anda ingin melakukannya sendiri:
Untuk menjalankan simulasi CPU, Anda memerlukan ModelSim, yang dapat Anda unduh dari situs web Intel.
Untuk menjalankan kompiler OurLang, Anda memerlukan versi Java> = 8.
Tautan ke proyek:
https://github.com/IamMaxim/OurCPU
https://github.com/IamMaxim/OurLang
Ekstensi:
https://github.com/IamMaxim/ourlang-vscode
Untuk membangun bagian Verilog, saya biasanya menggunakan skrip bash:
#/bin/bash vlib work vlog *.v vsim -c testbench_1 -do "run; exit"
Tapi ini bisa diulangi melalui GUI.
Lebih mudah menggunakan Intellij IDEA untuk bekerja dengan kompiler. Hal utama adalah untuk melacak modul mana yang Anda butuhkan dalam dependensi. Saya tidak memposting .jar siap pakai untuk membuka akses, karena saya berharap pembaca membaca kode sumber dari kompiler.
Modul yang diluncurkan adalah Compiler dan Interpreter. Semuanya jelas dengan kompiler, Interpreter hanyalah simulator OurCPU di Jawa, tetapi kami tidak akan mempertimbangkannya dalam artikel ini.
Set instruksi
Saya pikir lebih baik memulai dengan Instruction Set.
Ada beberapa arsitektur kumpulan instruksi:
- Stack-based adalah apa yang dijelaskan dalam artikel. Fitur khusus adalah bahwa semua operan didorong ke stack dan muncul dari stack, yang segera mengecualikan kemungkinan eksekusi paralel, tetapi itu adalah salah satu pendekatan paling sederhana untuk bekerja dengan data.
- Berbasis akumulator - intinya adalah bahwa hanya ada satu register yang menyimpan nilai yang dimodifikasi oleh instruksi.
- Berbasis register adalah apa yang digunakan dalam prosesor modern karena memungkinkan Anda untuk mencapai kinerja maksimum melalui penggunaan berbagai optimasi, termasuk paralelisasi eksekusi, pipelining, dll.
Set instruksi prosesor kami berisi 30 instruksi
Selanjutnya, saya mengusulkan untuk melihat implementasi prosesor:
Kode terdiri dari beberapa modul:
- CPU
- RAM
- Modul untuk setiap instruksi
RAM adalah modul yang berisi langsung memori itu sendiri, serta cara untuk mengakses data di dalamnya.
CPU - modul yang secara langsung mengontrol kemajuan program: membaca instruksi, mentransfer kontrol ke instruksi yang diinginkan, menyimpan register yang diperlukan (pointer ke instruksi saat ini, dll.).
Hampir semua instruksi hanya bekerja dengan stack, jadi ikuti saja. Beberapa (mis. Putw, putb, jmp, dan jif) memiliki argumen tambahan dalam instruksi itu sendiri. Mereka harus melewati seluruh instruksi sehingga mereka dapat membaca data yang diperlukan.
Berikut adalah gambaran umum tentang cara kerja prosesor:

Prinsip umum desain perangkat di tingkat instruksi
Saya pikir sudah waktunya untuk berkenalan dengan perangkat langsung dari program itu sendiri. Seperti yang dapat Anda lihat dari diagram di atas, setelah setiap instruksi, alamat berpindah ke yang berikutnya. Ini memberikan kursus linier ke program. Ketika menjadi perlu untuk mematahkan linearitas ini (kondisi, loop, dll.), Instruksi cabang digunakan (dalam set instruksi kami ini adalah jmp dan jif).
Saat memanggil fungsi, kita perlu menyimpan keadaan saat ini dari semuanya, dan untuk ini ada catatan aktivasi - catatan yang menyimpan informasi ini. Mereka tidak terikat dengan prosesor atau instruksi dengan cara apa pun, itu hanya sebuah konsep yang digunakan kompiler ketika membuat kode. Catatan aktivasi di OurLang memiliki struktur berikut:

Seperti dapat dilihat dari diagram ini, variabel lokal juga disimpan dalam catatan aktivasi, yang memungkinkan Anda untuk menghitung alamat variabel dalam memori pada waktu kompilasi, bukan pada waktu berjalan, dan dengan demikian mempercepat eksekusi program.
Untuk panggilan fungsi, set instruksi kami menyediakan metode untuk bekerja dengan dua register yang terdapat dalam modul CPU (penunjuk operasi dan penunjuk alamat aktivasi) - putopa / popopa, putara / popara.
Kompiler
Sekarang mari kita lihat bagian yang paling dekat dengan programmer akhir - kompiler. Secara umum, kompiler sebagai program terdiri dari 3 bagian:
Lexer bertanggung jawab untuk menerjemahkan kode sumber program ke dalam unit leksikal yang dimengerti oleh parser.
Parser membangun pohon sintaksis abstrak dari unit leksikal ini.
Compiler melewati pohon ini dan menghasilkan beberapa jenis kode yang terdiri dari instruksi tingkat rendah. Ini bisa berupa bytecode atau kode biner yang siap dieksekusi oleh prosesor.
Dalam kompiler OurLang, bagian-bagian ini diwakili masing-masing oleh kelas
- Lexer.java
- Parser.java
- Compiler.java
Bahasa
OurLang sedang dalam masa pertumbuhan, yaitu berfungsi, tetapi sejauh ini tidak ada banyak hal di dalamnya dan bahkan bagian inti dari bahasa belum selesai. Tetapi untuk memahami esensi dari kompiler, keadaan saat ini sudah cukup.
Sebagai contoh program untuk memahami sintaksis, fragmen kode ini diusulkan (juga digunakan untuk menguji fungsionalitas):
// single-line comments /* * Multi-line comments */ function print(int arg) { instr(putara, 0); instr(putw, 4); instr(add, 0); instr(lw, 0); instr(printword, 0); } function func1(int arg1, int arg2): int { print(arg1); print(arg2); if (arg1 == 0) { return arg2; } else { return func1(arg1 - 1, arg2); }; } function main() { var i: int; i = func1(1, 10); if (i == 0) { i = 1; } else { i = 2; }; print(i); }
Saya tidak akan fokus pada bahasa, saya akan meninggalkannya untuk studi Anda. Melalui kode kompiler, tentu saja;).
Ketika menulisnya, saya mencoba membuat kode yang menjelaskan sendiri tanpa komentar, jadi tidak ada masalah dalam memahami kode kompiler.
Yah, tentu saja, hal yang paling menarik adalah menulis kode, dan kemudian perhatikan bagaimana hasilnya. Untungnya, kompiler OurLang menghasilkan kode seperti perakitan dengan komentar,
yang membantu untuk tidak bingung tentang apa yang terjadi di dalam.
Saya juga merekomendasikan menginstal ekstensi untuk Visual Studio Code, ini akan memudahkan pekerjaan dengan bahasa.
Selamat mencoba proyek ini!