Alamat absolut dari suatu fungsi dalam Microchip XC16

8

Perangkat: dsPIC33FJ128GP802

Saya memiliki beberapa file * .s sebagai berikut

.global _D1
.section .speex, code
_D1:
.pword 0x66C821,  0x1B0090,  0xD96C36,  0x9B60B0,  0xDD4E36,  0xBF4E53
.pword 0xD1098B,  0x719BD9,  0x873989,  0x003B69,  0x279035,  0xED4244
.pword 0xE1403C,  0x54D439,  0x826550,  0xC59627,  0xDD0432,  0x88FA29

Saya telah mendeklarasikan hal yang sama dalam *. H

extern void D1(void);

Sekarang saya meneruskan D1 ke fungsi membaca tabel

nowPlaying.file1 = (unsigned long) D1;
function(nowPlaying.file1);

Masalah saya adalah, jika alamat D1 di atas 0X8000, rutin tidak benar. Saya mencoba model kode besar dan kecil, tetapi hasilnya sama. Saya pikir ini karena keterbatasan 16 bit dari pointer Apakah ada metode untuk mengakses alamat absolut D1 langsung dari kode. Mungkin sesuatu seperti fungsi bawaan atau makro.

Saneesh AT
sumber
Saya tidak pernah menggunakan seri dsPIC, tetapi alasan apa pun Anda tidak dapat menggunakan array C const bukan assembler? Saya pikir ada opsi untuk menempatkan itu di lokasi kode tertentu (jika Anda perlu karena alasan lain). Hanya berpikir mungkin beberapa optimasi kompiler memperpendek pointer karena itu tidak mengharapkan untuk menjadi referensi data di lokasi memori yang lebih tinggi.
PeterJ
Ya, manual kompiler mengatakan semua pointer termasuk pointer fungsi 16 bit. Saya mencari beberapa metode lain untuk mengakses alamat memori. Jika kita menggunakan array const, kita tidak bisa menggunakan byte atas dari kata 3 byte di dsPIC. Alasan lain adalah bahwa, file perakitan dihasilkan oleh perangkat lunak komputer yang disediakan oleh microchip untuk mengompresi data ucapan.
Saneesh AT
Pertanyaan terkait: electronics.stackexchange.com/questions/56058/… (meskipun ini untuk PIC, dsPIC mungkin bekerja sama)
Apakah data di D1seharusnya mewakili suatu fungsi atau array data?
The Photon
2
@Saneesh Jadi jawab pertanyaan saya . Apakah kode ini atau itu datanya? Jika ini kode, sistem tidak mendukungnya di luar ruang alamat 16-bit, jadi apa yang Anda coba lakukan adalah mustahil, tidak peduli bagaimana Anda mengekspresikannya. Jika ini adalah data, silakan katakan demikian, dan silakan coba mengatasinya sebagai const short D1[].
user207421

Jawaban:

4

Data yang Anda gambarkan (penggunaan 24-bit penuh dari memori program untuk menyimpan data) tidak dapat didefinisikan dan diinisialisasi dalam C, dan tidak dapat membaca langsung melalui C; satu-satunya cara untuk mengaksesnya adalah dengan merangkum dalam fungsi perakitan C-callable atau intrinsik.

Sebenarnya ada dua pertanyaan di sini:

  1. cara bermain apik dengan kompiler, assembler, dan tautan, sehingga ketika Anda mendefinisikan data 24-bit dalam file rakitan sebagai data yang dapat dipindahkan dengan nama simbolis D1, daripada data yang tidak disebutkan namanya di alamat tetap, kompiler dapat melihat variabel ini untuk menentukan alamatnya

  2. cara mengakses data

Pertanyaan ke-2 (bagaimana mengakses data) dijawab untuk bagian 33EP di DS70613C dan harus dijawab untuk bagian 33FJ di DS70204C (tetapi contoh dalam manual 33FJ hanya menggunakan bit 16 rendah). Berikut ini cuplikan contoh kode dari manual referensi 33EP yang berfungsi untuk 33EP bagian + seharusnya untuk 33FJ (Saya tidak punya perangkat 33FJ yang mudah tersedia):

(catatan: penggunaan kode int, sedangkan akan lebih baik digunakan uint16_tdan #include <stdint.h>)

int prog_data[10] __attribute__((space(prog))) =
  {0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888, 0x9999};

unsigned int lowWord[10], highWord[10];
unsigned int tableOffset, loopCount;

int main(void){
    TBLPAG = __builtin_tblpage (prog_data);
    tableOffset = __builtin_tbloffset (prog_data);
    /* Read all 10 constants into the lowWord and highWord arrays */
    for (loopCount = 0; loopCount < 10; loopCount ++)
    {
        lowWord[loopCount] = __builtin_tblrdl (tableOffset);
        highWord[loopCount] = __builtin_tblrdh (tableOffset);
        tableOffset +=2;
    }
    while(1)
        ;
}

Anda akan mencatat bahwa fungsi builtin __builtin_tblrdl()dan __builtin_tblrdh()digunakan untuk membaca kata-kata data 16-bit yang rendah dan tinggi dari lokasi memori program, dan __builtin_tblpage() and __builtin_tbloffset()dapat digunakan untuk mengekstrak halaman dan mengimbangi alamat. Dalam contoh khusus ini, array highWord selalu 0, dan array lowWord cocok dengan prog_data yang didefinisikan dan diinisialisasi dalam C.

Harap dicatat tidak ada petunjuk yang digunakan di sini! Meskipun dimungkinkan untuk menggunakan variabel normal yang ditandai const, sehingga mereka ditempatkan oleh linker di ruang program read-only, dan agar Anda dapat membaca memori menggunakan teknik pointer C standar, dengan kompiler secara otomatis mengelola register paging. untuk Anda, Anda hanya dapat menyimpan data 16-bit. Anda perlu mengakses fungsi built-in TBLRDL dan TBLRDH untuk mendapatkan semua 24 bit data.

Adapun cara bermain dengan kompiler / linker / etc, Anda harus menipu kompiler dan mengatakan itu hanya melihat data 16-bit. Berikut adalah contoh yang berfungsi untuk mendapatkan pada variabel D1 yang dideklarasikan di tempat lain:

#define D1_SIZE 18
extern uint16_t __attribute__((space(prog))) D1[D1_SIZE];

#define READ_DATA(dst, v, len) readData(dst, __builtin_tblpage(v), __builtin_tbloffset(v), len)
void readData(uint32_t *pdst, uint16_t page, uint16_t offset, uint16_t len)
{
    TBLPAG = page;
    while (len-- > 0)
    {
        uint16_t lo = __builtin_tblrdl (offset);
        uint16_t hi = __builtin_tblrdh (offset);
        *pdst++ = (((uint32_t)(hi)) << 16) | ((uint32_t)(lo));
        offset += 2;
    }
}

...

uint32_t d1copy[D1_SIZE];
READ_DATA(d1copy, D1, D1_SIZE);

Ini benar membaca nilai 24-bit dan menyimpannya di bagian bawah 24 bit dari uint32_t. Variabel D1 extern yang dideklarasikan dalam C adalah variabel dummy yang hanya digunakan untuk mendapatkan alamat awal dengan memanfaatkan cara kompiler / assembler / penghubung bekerja bersama. Fungsi builtin menangani sisa pekerjaan.

Yang saya tidak tahu adalah bagaimana cara secara otomatis mendapatkan ukuran data, karena itu ditentukan + diinisialisasi dalam perakitan.

Jason S
sumber
1

Jangan mengubahnya menjadi lama dan mundur tanpa tanda tangan. Meminta masalah. Anda pada dasarnya berbohong kepada kompiler. Deklarasi yang benar untuk nowPlaying.file1 adalah

struct
{
    // other stuff ...
    void (*D1)(void);
    // other stuff ...
} nowPlaying;

Dan juga untuk function ():

extern void function(void (*file)(void));

dan hapus semua typecast.

Atau, jika @PeterJ menyarankan, ini adalah data, itu harus dinyatakan sebagai extern short D1 [] di kedua tempat: dan Anda tidak benar-benar membutuhkan assembler; Anda bisa mendeklarasikan semuanya dalam C sebagai const short D1 [] = {...}; Compiler harus memasukkannya ke dalam segmen kode karena const.

pengguna207421
sumber
Kecuali saya salah membaca sesuatu D1 bukan pointer ke fungsi, itu data disimpan dalam ruang kode.
PeterJ
1
@ PeterJ Maka seharusnya tidak dinyatakan sebagai void D1 eksternal (void), itu harus didefinisikan sebagai D1 extern pendek []. Tak satu pun dari ini meyakinkan saya bahwa pertanyaan itu bukan milik SO.
user207421
Apa yang dibicarakan OP tidak dapat diekspresikan dalam C sama sekali, itu perlu dienkapsulasi oleh fungsi perakitan C-callable atau intrinsik.
Jason S
@JasonS Tidak ada bukti tentang hal itu dalam pertanyaan, dan OP gagal mengklarifikasi.
user207421
Ya ada, jika Anda terbiasa dengan arsitektur PIC33F / PIC33E.
Jason S
0

Sepertinya jawaban sederhana adalah menulis subrutin dalam assembler. Jika saya ingat benar, C30 tidak mengakses memori program sebagai data menggunakan pointer 24 bit. Paling-paling ia dapat mengakses memori program melalui jendela PSV, tetapi kemudian Anda hanya dapat melihat 16 bit rendah dari setiap kata memori program 24 bit.

Jika akan sangat sederhana untuk menulis assembler rutin yang dapat dipanggil dari C30 yang mengembalikan 24 bit data yang diberikan alamat memori program 24 bit. Namun, apakah data Anda kumpulan dari nilai 24 bit atau benar-benar daftar byte yang dikemas 3 per kata? Jika yang terakhir, maka itu bahkan lebih mudah. Tulis rutin assembler yang memberi Anda tampilan byte-address dari memori program. Alamatnya masih harus 24 bit, tetapi nilai data sekarang hanya 8 bit.

Atau cukup tulis seluruh rutin dalam assembler. Jika Anda melakukan pembelahan byte tingkat rendah dan pengemasan memori, mungkin ini lebih mudah. Di assembler Anda bisa melakukan apa yang Anda inginkan dengan cara yang diinginkan mesin. Dalam C Anda harus mencari tahu mantera apa yang harus digumamkan ke kompiler untuk membuatnya menulis kode mesin untuk Anda. Terkadang lebih mudah melakukannya sendiri secara langsung. Arsitektur dsPIC sangat mudah untuk menulis kode assembly, jelas lebih mudah daripada PIC 16.

Olin Lathrop
sumber
TBLRDL dan TBLRDH adalah kuncinya di sini, apakah Anda menggunakan perakitan atau __builtin_tblrdX()fungsinya. Saya setuju dengan Anda + mendapat tendangan dari ungkapan Anda "Di C Anda harus mencari tahu mantera apa yang harus digumamkan ke kompiler". Ironisnya, jika Anda benar-benar mencoba memeras kinerja maksimum, kadang-kadang __builtin()fungsinya lebih baik, karena kompiler dapat mengoptimalkan cara menghasilkan kode, sedangkan itu harus memperlakukan fungsi perakitan kode tangan sebagai kotak hitam yang tidak dapat diubah .
Jason S