Абсолютный адрес функции в Microchip XC16

8

Устройство: dsPIC33FJ128GP802

У меня есть некоторые * .s файлы следующим образом

.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

Я объявил то же самое в * .h

extern void D1(void);

Теперь я передаю D1 в функцию чтения таблицы

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

Моя проблема в том, что, если адрес D1 выше 0X8000, процедура не является правильной. Я пробовал большие и маленькие модели кода, но результат один и тот же. Я думаю, что это связано с 16-битным ограничением указателей. Есть ли какой-либо метод для доступа к абсолютному адресу D1 непосредственно из кода. Может быть что-то вроде встроенной функции или макроса.

Санееш А.Т.
источник
Я никогда не использовал серии dsPIC, но по какой причине вы не можете использовать массив const C вместо ассемблера? Я думаю, что есть варианты разместить это в определенных местах кода (если вам нужно по какой-то другой причине). Если подумать, может быть, какая-то оптимизация компилятора укорачивает указатели, потому что она не ожидает ссылки на данные в более высоких местах памяти.
PeterJ
Да, в руководстве по компилятору сказано, что все указатели, включая указатели функций, являются 16-битными. Я ищу какой-то другой метод для доступа к адресу памяти. Если мы используем массив const, мы не можем использовать верхний байт 3-байтового слова в dsPIC. Другая причина заключается в том, что файл сборки генерируется компьютерным программным обеспечением, предоставляемым микрочипом, для сжатия речевых данных.
Saneesh AT
Связанный вопрос: electronics.stackexchange.com/questions/56058/…… (хотя это для PIC, dsPIC, вероятно, работает так же)
Должны ли данные D1представлять функцию или массив данных?
Фотон
2
@Saneesh Так ответь на мои вопросы. Это код или данные? Если это код, система не поддерживает его за пределами 16-битного адресного пространства, поэтому то, что вы пытаетесь сделать, невозможно, как бы вы это ни выражали. Если это данные, скажите, пожалуйста, и попробуйте обратиться к ним как const short D1[].
user207421

Ответы:

4

Данные, которые вы описываете (полное 24-битное использование памяти программы для хранения данных), не могут быть определены и инициализированы в C и не могут быть прочитаны напрямую через C; единственный способ получить к нему доступ - это инкапсулировать в C-вызываемую ассемблерную функцию или встроенную функцию.

Здесь действительно два вопроса:

  1. как хорошо играть с компилятором, ассемблером и компоновщиком, чтобы, когда вы определяете свои 24-битные данные в файле сборки как перемещаемые данные с символическим именем D1, а не безымянные данные по фиксированному адресу, компилятор может видеть эту переменную определить его адрес

  2. как получить доступ к данным

На 2-й вопрос (как получить доступ к данным) дан ответ для деталей 33EP в DS70613C и должен быть дан ответ для деталей 33FJ в DS70204C (но примеры в руководстве по 33FJ используют только младшие 16 бит). Вот пример фрагмента кода из справочного руководства 33EP, который работает для деталей 33EP + должен для 33FJ (у меня нет легко доступного устройства 33FJ):

(примечание: код использует int, тогда как было бы лучше использовать uint16_tи #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)
        ;
}

Вы заметите , что функции встроенных __builtin_tblrdl()и __builtin_tblrdh()используются для чтения низких и высоких 16-битовых слов данных из памяти программ, и __builtin_tblpage() and __builtin_tbloffset()может быть использована для извлечения страницы и смещение адреса. В этом конкретном примере массив highWord всегда равен 0, а массив lowWord соответствует prog_data, определенному и инициализированному в C.

Обратите внимание, что здесь не используются указатели! Хотя можно использовать обычные переменные, помеченные тегом const, чтобы они находились с помощью компоновщика в программном пространстве, доступном только для чтения, и чтобы вы могли читать память с использованием стандартных методов указателя C, а компилятор автоматически управляет регистрами подкачки для вас вы можете хранить только 16-битные данные. Вам нужно получить доступ к встроенным функциям TBLRDL и TBLRDH, чтобы получить все 24 бита данных.

Что касается того, как хорошо играть с компилятором / компоновщиком / etc, вы должны обмануть компилятор и сказать ему, что он видит только 16-битные данные. Вот пример, который работал, чтобы получить переменную D1, объявленную в другом месте:

#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);

Это правильно читает 24-битные значения и сохраняет их в нижних 24 битах uint32_t. Переменная extern D1, объявленная в C, является фиктивной переменной, которая используется только для того, чтобы получить начальный адрес, используя преимущества совместной работы компилятора / ассемблера / компоновщика. Встроенные функции выполняют остальную часть работы.

Чего я не знаю, так это как автоматически получить размер данных, так как они определены + инициализированы в сборке.

Джейсон С
источник
1

Не конвертируйте его в unsigned long и обратно. Просить неприятностей. Вы в основном врете компилятору. Правильное объявление для nowPlaying.file1:

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

И аналогично для функции ():

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

и удалите все типы.

Или, если @PeterJ предполагает, что это данные, они должны быть объявлены как extern short D1 [] в обоих местах: и вам не нужен ассемблер; Вы могли бы объявить все это в C как const short D1 [] = {...}; Компилятор должен поместить его в сегмент кода, как он есть.

user207421
источник
Если я не читаю что-то, D1 не является указателем на функцию, это данные, хранящиеся в пространстве кода.
PeterJ
1
@PeterJ Тогда он не должен быть объявлен как внешний void D1 (void), он должен быть определен как extern short D1 []. Ничто из этого не убеждает меня, что вопрос не относится к SO.
user207421
То, о чем говорит OP, вообще не может быть выражено в C, оно должно быть заключено в C-вызываемую ассемблерную функцию или встроенную функцию.
Джейсон С
@JasonS Там нет никаких доказательств этого в вопросе, и ОП не удалось уточнить.
user207421
Да, если вы знакомы с архитектурами PIC33F / PIC33E.
Джейсон С
0

Кажется, простой ответ - написать подпрограмму на ассемблере. Если я правильно помню, C30 не обращается к памяти программ как к данным с использованием 24-битных указателей. В лучшем случае он может получить доступ к памяти программ через окно PSV, но тогда вы можете видеть только младшие 16 битов каждого 24-битного слова памяти программ.

Было бы очень просто написать программу на ассемблере, которая вызывается из C30, которая возвращает 24-битные данные с 24-битным адресом памяти программ. Однако, являются ли ваши данные набором 24-битных значений или действительно списком байтов, которые упакованы по 3 на слово? Если последнее, то это еще проще. Напишите программу на ассемблере, которая дает вам представление байтовых адресов памяти программы. Адрес все равно должен был бы быть 24 битами, но значения данных теперь только 8 битов.

Или просто напишите всю программу на ассемблере. Если вы делаете этот вид низкоуровневой синхронизации байтов и упаковки памяти, это, вероятно, проще. В ассемблере вы можете просто делать то, что вы хотите, так, как машина хочет это сделать. В C вы должны выяснить, какие заклинания нужно шептать компилятору, чтобы он написал для вас машинный код. Иногда проще сделать это самостоятельно. Архитектура dsPIC особенно проста в написании ассемблерного кода, определенно проще, чем PIC 16.

Олин Латроп
источник
TBLRDL и TBLRDH являются ключевыми здесь, используете ли вы сборку или __builtin_tblrdX()функции. Я согласен с вами + получил удовольствие от вашей фразы "В C вы должны выяснить, какие заклинания нужно шептать компилятору". По иронии судьбы, если вы действительно пытаетесь снизить максимальную производительность, иногда __builtin()функции лучше, потому что компилятор может оптимизировать способ генерации кода, в то время как он должен обрабатывать кодированные вручную функции сборки как черные ящики, которые он не может изменить ,
Джейсон С