Mesin virtual dan mikrokontroler

Ketika mengembangkan perangkat yang berbeda, Anda sering mendapatkan masalah: algoritma dari perangkat ke perangkat diulang di beberapa tempat, dan perangkat itu sendiri sangat berbeda. Saya memiliki tiga perangkat dalam pengembangan yang di beberapa tempat saling mengulang fungsi, mereka menggunakan tiga prosesor yang berbeda (tiga arsitektur berbeda), tetapi hanya ada satu algoritma. Untuk menyatukan semuanya, direncanakan untuk menulis mesin virtual minimal.



Secara umum, saya melihat ke arah bytecode dari Java, Lua dan mesin lainnya, tetapi saya tidak ingin menulis ulang semua bagasi yang tersedia ke bahasa lain. Jadi kami memutuskan bahasa - C. Meskipun Jawa atau Lua masih terdengar menarik. [1] [2] [3] [4].

Kriteria berikutnya adalah kompiler. Dalam proyek saya, saya paling sering menggunakan "ditulis oleh siswa untuk cookie GCC (c) anonymus". Itu jika Anda mendeskripsikan arsitektur Anda, Anda harus membuat GCC (compiler, linker, dll.).

Karena saya orang yang malas, saya mencari arsitektur sekecil mungkin dengan dukungan GCC. Dan itu menjadi MSP430.

Deskripsi Singkat


MSP430 adalah arsitektur yang sangat sederhana. Ini hanya memiliki 27 instruksi [5] dan hampir semua pengalamatan.

Konstruksi mesin virtual dimulai dengan konteks prosesor. Konteks prosesor dalam sistem operasi adalah struktur yang sepenuhnya menggambarkan keadaan prosesor. Dan keadaan prosesor virtual ini dijelaskan melalui yang berikut:

  • Tim saat ini
  • Daftar
  • Keadaan register interupsi opsional
  • Isi opsional RAM dan ROM

Register MSP430 adalah 16. Dari 16 register, 4 yang pertama digunakan sebagai register sistem. Katakanlah, register nol bertanggung jawab untuk pointer saat ini ke perintah yang dieksekusi dari ruang alamat (Command counter).

Anda dapat membaca lebih lanjut tentang register di panduan pengguna asli msp430x1xxx [6]. Selain register, ada juga isi ruang alamat - RAM, ROM. Tetapi karena mudah untuk menjaga "mesin Host" (mesin yang mengeksekusi kode mesin virtual) dalam memori mesin virtual, untuk seringnya, tidak ada gunanya - callback digunakan.

Solusi ini memungkinkan Anda untuk menjalankan program "tangan kiri" pada prosesor dengan arsitektur Harvard (baca AVR [7] [8]), dengan mengambil program dari sumber eksternal (katakanlah, memori i2c atau kartu SD).

Juga dalam konteks prosesor adalah deskripsi interrupt register (SFRs). Sistem interupsi MSP430 paling akurat dijelaskan dalam [6], klausa 2.2.
Tetapi dalam mesin virtual yang dijelaskan, saya sedikit menjauh dari aslinya. Dalam prosesor asli, bendera interupsi ada di register periferal. Dalam hal ini, interupsi dijelaskan dalam register SFR.

Periferal prosesor dijelaskan dengan cara yang sama, melalui panggilan balik, yang memungkinkan Anda membuat periferal sendiri sesuai keinginan.

Item prosesor selanjutnya adalah multiplexer perintah. Multiplexer perintah melakukan fungsi terpisah. Multiplexer memilih perintah itu sendiri dari kata perintah, menangani sumber dan penerima, dan melakukan tindakan dari perintah yang dipilih.

Fungsi terpisah menjelaskan pengalamatan sumber (SRC) dan penerima.

Bagaimana cara menggunakannya


Dalam folder contoh dari repositori proyek [9] ada contoh untuk prosesor berikut:
  • STM8 untuk kompiler IAR
  • STM8 untuk kompiler SDCC
  • STM32 untuk kompiler armcc Keil
  • AVR untuk GCC Compiler


Dalam file Cpu.h, prosesor dikonfigurasi.

Deskripsi pengaturan di bawah ini:

  • RAM_USE_CALLBACKS - Menunjukkan apakah akan menggunakan panggilan (callback) alih-alih array individual dalam konteks prosesor. Apakah akan menggunakan panggilan untuk bekerja dengan RAM (panggilan cpu.ram_read, cpu.ram_write)
  • ROM_USE_CALLBACKS - Apakah akan menggunakan panggilan untuk bekerja dengan ROM (call cpu.rom_read)
  • IO_USE_CALLBACKS - Apakah akan menggunakan panggilan untuk bekerja dengan pinggiran (panggilan cpu.io_read, cpu.io_write), jika 0 maka fungsi untuk bekerja dengan pinggiran harus dijelaskan dalam fungsi msp430_io dari file cpu.c
  • RAM_SIZE - Ukuran RAM (RAM), alamat akhir secara otomatis dihitung ulang berdasarkan parameter ini
  • ROM_SIZE - Ukuran ROM (ROM), alamat awal secara otomatis dihitung ulang berdasarkan parameter ini
  • IRQ_USE - Menunjukkan apakah interupsi akan digunakan; jika 1, maka interupsi diaktifkan
  • HOST_ENDIANESS - Menunjukkan urutan byte dari pengontrol host (pengontrol yang menjalankan mesin virtual). Arsitektur AVR, X86, STM32 adalah little-endian, STM8 adalah big-endian
  • DEBUG_ON - Menunjukkan apakah debugging akan digunakan. Debugging dilakukan melalui fprintf - stderr


Menggunakan perpustakaan dimulai dengan menghubungkan cpu.c dan cpu.h ke proyek.

#include "cpu.h"

Berikutnya adalah pengumuman konteks prosesor. Bergantung pada penggunaan parameter * _USE_CALLBACKS, kode deklarasi konteks akan berubah.

untuk semua * _USE_CALLBACKS = 1 deklarasi konteks prosesor akan terlihat seperti ini:

msp430_context_t cpu_context =
    {
        .ram_read_cb = ram_read,
        .ram_write_cb = ram_write,
        .rom_read_cb = rom_read,
        .io_read_cb = io_read,
        .io_write_cb = io_write
    };


Di mana * _cb variabel menerima pointer fungsi (lihat contoh).

Sebaliknya, untuk * _USE_CALLBACKS = 0, deklarasi akan terlihat seperti ini:

msp430_context_t cpu_context =
    {
         .rom = { /* hex program */ },
    };

Berikutnya adalah inisialisasi konteks melalui fungsi:

msp430_init(&cpu_context);

Dan menjalankan satu instruksi pada satu waktu melalui suatu fungsi:

while(1)
    msp430_cpu(&cpu_context);

Callback untuk bekerja dengan ruang alamat terlihat seperti ini:

uint16_t io_read(uint16_t address);
void io_write(uint16_t address,uint16_t data);

uint8_t ram_read(uint16_t address);
void ram_write(uint16_t address,uint8_t data);

uint8_t rom_read(uint16_t address);

Alamat untuk IO ditransmisikan relatif ke 0 ruang alamat (mis. Jika program mesin virtual mengakses P1IN, yang ditugaskan ke alamat 0x20, maka alamat 0x20 akan diteruskan ke fungsi).

Sebaliknya, alamat untuk RAM dan ROM ditransmisikan relatif ke titik awal (misalnya, ketika mengakses alamat 0xfc06 dan memulai ROM pada 0xfc00, alamat 0x0006 akan diteruskan ke fungsi. Artinya, alamat dari 0 ke RAM_SIZE, 0 - ROM_SIZE)

Ini memungkinkan penggunaan memori eksternal , misalnya I2C (yang sudah memperlambat prosesor).

Cara mengisi


Sepenuhnya proyek tidak selesai. Berhasil, uji firmware bekerja dengan keras. Tetapi sebagian besar kompiler praktis tidak menggunakan perintah spesifik yang berbeda (katakanlah, Dadd adalah tambahan desimal dari sumber dan penerima (dengan tanda hubung)). Jadi tidak perlu berbicara tentang kompatibilitas 100% dengan prosesor nyata.

Secara alami, ada sekitar dua lusin operasi mesin host per satu perintah mesin virtual, sehingga tidak masuk akal untuk berbicara tentang karakteristik kecepatan.

Sumber proyek dan deskripsi yang lebih luas tersedia di bitbucket.org [9].

Saya akan senang jika proyek ini bermanfaat bagi seseorang.

[1] dmitry.gr/index.php?r=05.Projects&proj=12.%20uJ%20-%20a%20micro%20JVM
[2] www.harbaum.org/till/nanovm/index.shtml
[3]www.eluaproject.net
[4] code.google.com/p/picoc
[5] en.wikipedia.org/wiki/MSP430
[6] www.ti.com/lit/ug/slau049f/slau049f.pdf
[7] en.wikipedia.org/wiki/%D0%93%D0%B0%D1%80%D0%B2%D0%B0%D1%80%D0%B4%D1%81%D0%BA%D0%B0%D1 % 8F_% D0% B0% D1% 80% D1% 85% D0% B8% D1% 82% D0% B5% D0% BA% D1% 82% D1% 83% D1% 80% D0% B0
[8] id .wikipedia.org / wiki / AVR
[9] bitbucket.org/intl/msp430_vm

Source: https://habr.com/ru/post/id385009/


All Articles