2

I'm trying to write a program for AVR Atmel 328 chip on Arduino Nano board that will allow me to send machine code instructions to the chip over a serial line, run/execute them on the chip, and interrogate the results by reading chip memory and sending the contents back over the serial line. This is the genesis of the idea: 3-instruction Forth by Frank Sergeant.

The remote-store and remote-fetch instructions appear to be working fine, but I've not been able to get the remote-call instruction (XCALL() function) to work. The approach I've taken is to coerce a 16-bit address into a subroutine by casting it as a function pointer.

Below is the code that is running on the Arduino Nano (compiled in the Arduino IDE and uploaded using the USB cable via the bootloader)

Any insight would be greatly appreciated! (I can add diassembled code, my remote instructions etc, if that helps).

Thanks in advance!

// Remote serial development platform for machine coding ArduinoNano/ATmel328, AKE, Jun 9, 2020

// global variables
unsigned char byt;                  // command token from host
unsigned char mem[255];             // memory window under programmatic control
unsigned char dat;                  // data byte from host
unsigned char adr_lo;               // address from host, low byte first
unsigned char adr_hi;           
unsigned short adr;                 // combined 16-bit address
typedef void (*GeneralFunction)();  // template to call memory address (as a subroutine)

void setup() {
  Serial.begin(9600);              // Turn UART serial protocol ON for comms with host PC
  Serial.write(0xFF);              // magic number to verify transmission
  Serial.write(0xFE);
  Serial.write((int) mem);         // Informs you of the writeable address space available (LSB, MSB) for tethered memory access
  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() {             // Instruction 1 = xstore(data,adr_lo,adr_hi).  Store byte from host at indicated address in target.
    dat=get_byte();
    adr=get_adr();
    *(char *)adr=dat;
}
void xfetch() {             //  Instruction 2 = xfetch(adr_lo,adr_hi).  Read byte from target memory and return to host.
        adr=get_adr();
        dat=*(char *)adr;
        Serial.write(dat);
}
void xcall() {              //  Instruction 3 = xcall(adr_lo,adr_hi).  Execute subroutine in target memory from indicated address.
                            //  WARNING!  User must have stored RET instruction to send control back to serial monitor.
        adr=get_adr();
        GeneralFunction fGf=adr;
        fGf;
}

void loop() {
    byt = get_byte();               // user specified instruction token (1,2,3)
    if(byt == 0x01 )      { xstore(); } 
    else if (byt == 0x02) { xfetch(); } 
    else if (byt == 0x3 ) { xcall(); } // else ignore any other serial inputs
}
  • AVR chips use a so called “Harvard architecture”. A Web search for those terms should provide you some insight. – Edgar Bonet Jun 17 at 10:04
  • Thanks Edgar. Are you suggesting separation of program and data memory is the reason why the xcall() function as written does not work? I would have expected the use of function pointer to allow the C compiler to map to the correct assembly instruction to access the program memory (in Flash), i.e. agnostic to whether the chip has Harvard or von Neumann architecture. – Assad Ebrahim Jun 17 at 10:08
  • 1
    The AVR is incapable of executing instructions from RAM. There is no physical pathway from the RAM to the CPU instruction decoder. It can only execute from flash. You would have to use code executing from the bootloader area (there are some bootloaders that provide that facility) to erase a page of flash then write the code into there. Or switch to a more capable MCU that can execute from RAM. – Majenko Jun 17 at 12:08
  • 1
    have you seen this? ... arduino-forth.com – jsotola Jun 18 at 1:45
  • I'd heard about FlashForth, but this collection of articles by Marc Petremann (veteran Forth coder) is excellently written and should make it a snap to get started. Thanks for sharing the link. – Assad Ebrahim Jun 18 at 5:36
2

The AVR CPU cannot execute instructions from RAM. It's not a question of what the compiler can do or should be able to do, the physical chip itself cannot do it. There is simply no physical pathway between the RAM and the instruction decoder in the CPU.

Instructions can only be executed directly from Flash memory. That is how the chip works. You can only execute code that you send over serial if you first erase a page (or more) of Flash memory then burn the incoming code into that page. You can then call the start of that page's address and your code will execute.

But there is another caveat there: only code running in the bootloader flash area is allowed to modify the flash memory. That means your code can't do it, only the bootloader. That doesn't mean that the bootloader has to be running, but the code that you execute has to reside there.

There is an alternative version of the Optiboot bootloader that can provide flash writing facilities to a user program, but I have no experience using it so can't assist with that.

| improve this answer | |
  • I see! so the reason why xstore() and xfetch() worked was because I was injecting code into the mem[255] array, which is able to hold and return values as it is created in RAM. But as it is unavailable to the Program Counter, this clears up why the xcall did not compile and the observation that the disassembled ELF file seemed to skip over the fGf() call. Thanks much - accepting your answer! – Assad Ebrahim Jun 18 at 5:53
  • For completeness, here's the link to Optiboot bootloader, and the MiniCore version for ATmega328. – Assad Ebrahim Jun 18 at 6:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.