Удаленное машинное кодирование микросхемы Arduino Atmel с тремя инструкциями по последовательной линии от привязанного (хост-компьютера)
Я пытаюсь написать программу для чипа AVR Atmel 328 на плате Arduino Nano, которая позволит мне отправлять инструкции машинного кода на чип по последовательной линии, запускать/выполнять их на чипе и запрашивать результаты с помощью чтение памяти чипа и отправка содержимого обратно по последовательной линии. Это генезис идеи: 3-инструкция Forth Фрэнка Сержанта.
Инструкции удаленного сохранения и удаленной выборки работают нормально, но мне не удалось заставить работать инструкцию удаленного вызова (функция XCALL()
). . Подход, который я избрал, состоит в том, чтобы ввести 16-битный адрес в подпрограмму, приведя его к типу указателя на функцию.
Ниже приведен код, работающий на Arduino Nano (скомпилированный в Arduino IDE и загруженный с помощью USB-кабеля через загрузчик)
Мы будем очень признательны за любую информацию! (Я могу добавить дизассемблированный код, мои удаленные инструкции и т. д., если это поможет).
Заранее спасибо!
// Платформа удаленной последовательной разработки для машинного кодирования ArduinoNano/ATmel328, AKE, 9 июня 2020 г.
// глобальные переменные
unsigned char byt; // токен команды от хоста
unsigned char mem[255]; // окно памяти под программным управлением
unsigned char dat; // байт данных от хоста
unsigned char adr_lo; // адрес от хоста, младший байт вперед
unsigned char adr_hi;
unsigned short adr; // комбинированный 16-битный адрес
typedef void (*GeneralFunction)(); // шаблон для вызова адреса памяти (как подпрограмма)
void setup() {
Serial.begin(9600); // Включаем последовательный протокол UART для связи с хост-компьютером
Serial.write(0xFF); // магический номер для проверки передачи
Serial.write(0xFE);
Serial.write((int) mem); // Информирует вас о доступном для записи адресном пространстве (LSB, MSB) для привязанного доступа к памяти
Serial.write((int) mem>>8);
}
char get_byte(void) {
while (!(Serial.available()>0)) { delay(50); }
return Serial.read();
}
short get_adr(void) {
adr_lo=get_byte();
adr_hi=get_byte();
return ( (short)adr_hi<<8 ) | (short)adr_lo;
}
void xstore() { // Инструкция 1 = xstore(data,adr_lo,adr_hi). Сохранить байт от хоста по указанному адресу в target.
dat=get_byte();
adr=get_adr();
*(char *)adr=dat;
}
void xfetch() { // Инструкция 2 = xfetch(adr_lo,adr_hi). Прочитать байт из целевой памяти и вернуться к хосту.
adr=get_adr();
dat=*(char *)adr;
Serial.write(dat);
}
void xcall() { // Инструкция 3 = xcall(adr_lo,adr_hi). Выполнить подпрограмму в целевой памяти с указанного адреса.
// ПРЕДУПРЕЖДЕНИЕ! Пользователь должен иметь сохраненную команду RET, чтобы отправить управление обратно на последовательный монитор.
adr=get_adr();
GeneralFunction fGf=adr;
fGf;
}
void loop() {
byt = get_byte(); // указанный пользователем токен инструкции (1,2,3)
if(byt == 0x01 ) { xstore(); }
else if (byt == 0x02) { xfetch(); }
else if (byt == 0x3 ) { xcall(); } // иначе игнорировать любые другие последовательные входы
}
@Assad Ebrahim, 👍2
Обсуждение1 ответ
Лучший ответ:
ЦП AVR не может выполнять инструкции из ОЗУ. Вопрос не в том, что может или должен делать компилятор, сам физический чип не может этого сделать. Просто нет физического пути между оперативной памятью и декодером инструкций в ЦП.
Инструкции могут только выполняться непосредственно из флэш-памяти. Так работает чип. Вы можете выполнить код, который вы отправляете через последовательный порт, только если вы сначала очистите страницу (или больше) флэш-памяти, а затем запишете входящий код на эту страницу. Затем вы можете вызвать начало адреса этой страницы, и ваш код будет выполнен.
Но есть еще одно предостережение: только код, работающий в области флэш-памяти загрузчика, может изменять флэш-память. Это означает, что ваш код не может этого сделать, только загрузчик. Это не означает, что загрузчик должен быть запущен, но код, который вы выполняете, должен находиться там.
Существует альтернативная версия загрузчика Optiboot, которая может предоставлять средства записи во флэш-память для пользовательской программы, но у меня нет опыта ее использования, поэтому я не могу помочь с этим.
Я понимаю! поэтому причина, по которой xstore() и xfetch() работали, заключалась в том, что я вводил код в массив mem[255], который может хранить и возвращать значения по мере их создания в ОЗУ. Но поскольку он недоступен для счетчика программ, это проясняет, почему xcall не скомпилировался, и наблюдение, что дизассемблированный файл ELF, похоже, пропускает вызов fGf(). Большое спасибо - принимаю ваш ответ!, @Assad Ebrahim
Для полноты картины вот ссылка на [загрузчик Optiboot](https://github.com/Optiboot/optiboot) и [версия MiniCore для ATmega328](https://github.com/MCUdude/MiniCore)., @Assad Ebrahim
- 8-битные микроконтроллеры AVR и PIC - Можно ли добавить: внешнюю флэш-память, внешнюю оперативную память, видео-и аудиовыход, дополнительные адресные шины?
- Информация о процессоре на последовательном мониторе Arduino
- Как функция/метод может определить, является ли передаваемый массив const PROGMEM (flash) или нет (RAM)?
- О UART один передатчик несколько приемников
- Последовательная связь Arduino и AVR через USB
- о том, как разделить один внешний последовательный EEprom с помощью двух микроконтроллеров?
- о том, как записать данные во весь EEprom?
- Непредсказуемое поведение при синтаксическом анализе ввода с разделителями "ключ-значение" из серийного номера
Чипы AVR используют так называемую «гарвардскую архитектуру». Поиск в Интернете по этим терминам должен дать вам некоторое представление., @Edgar Bonet
Спасибо Эдгар. Вы предполагаете, что разделение памяти программ и данных является причиной того, что написанная функция xcall() не работает? Я ожидал, что использование указателя на функцию позволит компилятору C отображать правильную инструкцию сборки для доступа к памяти программы (во Flash), т.е. независимо от того, имеет ли чип гарвардскую или фон-неймановскую архитектуру., @Assad Ebrahim
AVR не может выполнять инструкции из оперативной памяти. Физического пути от ОЗУ к декодеру инструкций ЦП нет. Он может *только* выполняться из флэш-памяти. Вам нужно будет использовать код, выполняемый из области загрузчика (есть некоторые загрузчики, которые предоставляют эту возможность), чтобы стереть страницу флэш-памяти, а затем записать туда код. Или переключитесь на более мощный MCU, который может выполняться из ОЗУ., @Majenko
Вы видели это? ... https://arduino-forth.com/, @jsotola
Я слышал о FlashForth, но этот сборник статей Марка Петреманна (ветерана Forth-программиста) превосходно написан и должен помочь приступить к работе. Спасибо, что поделились ссылкой., @Assad Ebrahim