Mega2560 и работа с himem (>64k)

Пытаюсь закинуть в заявленные 256к пару-тройку больших массивов:

#include <morepgmspace.h>
const uint16_t  array1[] PROGMEM {0x0000,0x0000}; //размер меньше 0x7FFF
uint16_t i=0;
uint16_t aa = pgm_read_word_far (GET_FAR_ADDRESS(array1) + i);

Если размер кода+массивов меньше 64к - проблем нет. если в сумме больше, то возникают ошибки вида:

Спойлер

..\ccQVjUD5.s: Assembler messages:
..\ccQVjUD5.s:73286: Error: value of 71358 too large for field of 2 bytes at 95
..\ccQVjUD5.s:73290: Error: value of 71422 too large for field of 2 bytes at 103
..\ccQVjUD5.s:73291: Error: value of 70845 too large for field of 2 bytes at 105
..\ccQVjUD5.s:73293: Error: value of 71678 too large for field of 2 bytes at 109
lto-wrapper.exe: fatal error: ..\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-gcc returned 1 exit status
compilation terminated.
/…/arduino15/packages/arduino/tools/avr-gcc/7.3.0-atmel3.6.1-arduino7/bin/…/lib/gcc/avr/7.3.0/…/…/…/…/avr/bin/ld.exe: error: lto-wrapper failed
collect2.exe: error: ld returned 1 exit status

Курение интернетов дало информацию, что штатный компилятор сперва кладет PROGMEM константы и только потом код.

На гите нашел Megacore. С ним получилось нормально завершить процесс компиляции, но вот прошить мегу не получается (там очень много настроек, пока не смог подобрать нужное сочетание). Нужно получить подтверждение, что проблему стоковыми средствами не решить.
Тогда создам отдельную тему про настройки megacore.

Удачи!

загрузчик, фьюзы и т.д всё сделано по описанию?

1 лайк

Сейчас у вас в Mege2560 какой загрузчик прошит?

1 лайк

Почитал еще раз гит, понял, что помимо раздела how to install есть еще важная информация про ‘burn bootloader’. Стало понятней.
Но перед тем как двигаться дальше - вопросы:

  • нужно ли корректировать предварительно настройки megacore?
Спойлер

  • если что, я смогу обратно откатиться на стоковый bootloader?

Простите, но Вас куда-то не туда занесло. Когда Вы в прошлый раз таки разобрались, что один массив не может быть длиннее 32к, мне казалось, что проблема решена, но Вас куда-то несёт … и Вы на ровном месте рожаете себе проблемы.

А реальная проблема от непонимания что делаете и попыток сделать методом тыка. Правильно, ведь?

Давайте сейчас сделаем так:

  1. Выдохнем и успокоимся;
  2. Забудем про MegaCore, всё отлично работает и в штатном ядре, которое идёт с IDE;
  3. Забудем про хитрую библиотеку “morepgmspace.h” - она писалась в качестве синтаксического сахара. Всё действительно необходимое, есть в штатной pgmspace.h

Для начала настраиваем IDE на штатную плату Мега.

Вот так, примерно:

Т.е. никаких хитрых ядер, просто из “Arduino AVR Boards”.

Затем описываем 9 массивов по 16к каждый и спокойно добираемся до них при помощи штатного макроса pgm_get_far_address, как показано в коде ниже.

Код
#define TBase uint16_t // Базовый тип массивов

#define OneArraySize  (16 * 1024 / sizeof(TBase)) // Один массив будет занимать 16 килобайтов

//
// Девять массивов. Вместе занимают 9х16 = 144 килобайта
//
static const TBase kaka0[OneArraySize] PROGMEM = {1,2,3};
static const TBase kaka1[OneArraySize] PROGMEM = {4,5,6};
static const TBase kaka2[OneArraySize] PROGMEM = {7,8,9};
static const TBase kaka3[OneArraySize] PROGMEM = {0,1,2};
static const TBase kaka4[OneArraySize] PROGMEM = {3,4,5};
static const TBase kaka5[OneArraySize] PROGMEM = {6,7,8};
static const TBase kaka6[OneArraySize] PROGMEM = {9,0,1};
static const TBase kaka7[OneArraySize] PROGMEM = {2,3,4};
static const TBase kaka8[OneArraySize] PROGMEM = {5,6,7};

//
//  Функция печати первых 4-х элементов массива для проверки
//
void printout(const uint_farptr_t arr) {
  Serial.print  (pgm_read_word_far(arr + 0 * sizeof(TBase))); Serial.print("; ");
  Serial.print  (pgm_read_word_far(arr + 1 * sizeof(TBase))); Serial.print("; ");
  Serial.print  (pgm_read_word_far(arr + 2 * sizeof(TBase))); Serial.print("; ");
  Serial.println(pgm_read_word_far(arr + 3 * sizeof(TBase)));
}

void setup(void) {
  Serial.begin(9600);
  Serial.println("Go on!");

  printout(pgm_get_far_address(kaka0));
  printout(pgm_get_far_address(kaka1));
  printout(pgm_get_far_address(kaka2));
  printout(pgm_get_far_address(kaka3));
  printout(pgm_get_far_address(kaka4));
  printout(pgm_get_far_address(kaka5));
  printout(pgm_get_far_address(kaka6));
  printout(pgm_get_far_address(kaka7));
  printout(pgm_get_far_address(kaka8));
}

void loop(void) {}

Запускаем и убеждаемся, что всё классно работает и ничего больше не надо. Никаких хитрых библиотек, никаких посторонних ядер и тому подобной лабуды - всё штатными средствами штатно грузится и нормально работает.

Здесь я показал только принцип. Если так писать неприятно (а мне было бы неприятно), то можно сделать синтаксический сахар типа доступа по индексу (а не через макрос) и даже можно все массивы “объединить в один” и доступаться, опять же, по индексу. Но это будет именно “сахар”. Принцип будет такой же, как в моём примере.

1 лайк

да, GET_FAR_ADDRESS был лишним. В остальном у меня очень похоже было по структуре. Вот прямо чувствовал, что ответ где-то рядом.
за uint_far_ptr_t отдельное спасибо. Понимал что тут нужно farptr, но синтаксис не получалось найти.

Вот это больше всего удивляет. Вы, похоже, не знаете с чего нужно начинать поиски. Всё, что Вам нужно знать, лежало у Вас под носом с самого начала - а именно всё это описано в комментариях в файле pgmspace.h, который у Вас есть в установке Arduino IDE. Можете сами зайти и посмотреть, там есть вот такой

комментарий
/** \ingroup avr_pgmspace
    \def pgm_get_far_address(var)

   This macro facilitates the obtention of a 32 bit "far" pointer (only 24 bits
   used) to data even passed the 64KB limit for the 16 bit ordinary pointer. It
   is similar to the '&' operator, with some limitations.

   Comments:

   - The overhead is minimal and it's mainly due to the 32 bit size operation.

   - 24 bit sizes guarantees the code compatibility for use in future devices.

   - hh8() is an undocumented feature but seems to give the third significant byte
     of a 32 bit data and accepts symbols, complementing the functionality of hi8()
     and lo8(). There is not an equivalent assembler function to get the high
     significant byte.

   - 'var' has to be resolved at linking time as an existing symbol, i.e, a simple
     type variable name, an array name (not an indexed element of the array, if the
     index is a constant the compiler does not complain but fails to get the address
     if optimization is enabled), a struct name or a struct field name, a function
     identifier, a linker defined identifier,...

   - The returned value is the identifier's VMA (virtual memory address) determined
     by the linker and falls in the corresponding memory region. The AVR Harvard
     architecture requires non overlapping VMA areas for the multiple address spaces
     in the processor: Flash ROM, RAM, and EEPROM. Typical offset for this are
     0x00000000, 0x00800xx0, and 0x00810000 respectively, derived from the linker
     script used and linker options. The value returned can be seen then as a
     universal pointer.
*/

#define pgm_get_far_address(var)                          \
({                                                    \
	uint_farptr_t tmp;                                \
                                                      \
	__asm__ __volatile__(                             \
                                                      \
			"ldi	%A0, lo8(%1)"           "\n\t"    \
			"ldi	%B0, hi8(%1)"           "\n\t"    \
			"ldi	%C0, hh8(%1)"           "\n\t"    \
			"clr	%D0"                    "\n\t"    \
		:                                             \
			"=d" (tmp)                                \
		:                                             \
			"p"  (&(var))                             \
	);                                                \
	tmp;                                              \
})

Собственно, всё, больше ничего и не надо, всё, что нужно знать, тут написано.

1 лайк

да, все очевидно. Сходил по ложному пути устаревших ссылок, где решали проблему himem пару лет назад: корректировали линкер и megacore предлагали. Тогда была похоже недоработка в компиляторе для меги.