buserror / simavr

simavr is a lean, mean and hackable AVR simulator for linux & OSX
GNU General Public License v3.0
1.56k stars 365 forks source link

Changing state with AVR_IOCTL_IOPORT_SET_EXTERNAL does not update reading PINx if you don't write to the port #304

Open carangil opened 6 years ago

carangil commented 6 years ago

This does not work as expected:

on the AVR:


 char buf[10];
 DDRD=0x00;
 PORTD=0;
 prints(env->out, "SET\n");

 while(1){

    prints(env->out, itoa(PIND, buf, 10));
    prints(env->out,"\n");
 }

The sets the DDRD to all inputs, and prints 'SET' to the usart. It then starts printing out on the USART the value of PIND.

In simavr application:


  for (;;) {
    vr_ioport_external_t p;

    int state = avr_run(avr);
    if (state == cpu_Done || state == cpu_Crashed)
        break;

    if (kbhit()) {
        //when key is pressed, change the pull up/down values
        getch();
        p.mask = 0xff;
        p.value = 111;
        avr_ioctl(avr, AVR_IOCTL_IOPORT_SET_EXTERNAL('D'), &p);
    }

  }

The idea is after seeing 'SET' go out the usart, the simavr user presses a key. This changes the pullup values. The value read from PIND in the AVR loop should change. It does not.

Changing the AVR code to this does work:

char buf[10];
DDRD=0x00;
PORTD=0;
prints(env->out, "SET\n");

while(1){
    DDRD=0x00;
    PORTD=0;    
    prints(env->out, itoa(PIND, buf, 10));
    prints(env->out,"\n");
}

It seems the act of writing either DDR or PORT will update PIN properly taking into account the new external defaults. But changing the external defaults does not update PIN by itself. I think avr_ioport_read needs to look the updated value of .external.pull_value. This logic ( if output, else if external pull, else if internal pull), is present in avr_ioport_update_irqs, but it seems like it needs to be in the direct read as well.

Perhaps I am using simavr incorrectly? In my application loop I do this: (pseudocode)

while(1){
 avr_run(avr)
 use avr_ioctl to read the all the port's status with AVR_IOCTL_IOPORT_GETSTATE
 emulate whatever computational logic and peripherals I have on the board
 use avr_ioctl to set the new inputs (PINx) with AVR_IOCTL_IOPORT_SET_EXTERNAL
}

Most examples set up IRQs for each pin. I am doing IRQs for the usart. I am trying to emulate a physical board I have which uses a whole port (PORTB) as an 8-bit bus. I don't see a way to get a callback that tells me all 8 pins of the port at once as a byte, or a way to set all 8 pin inputs at once as a byte, except thru the ioctl calls above. If I set up an ioctl for each pin, when the AVR writes a byte on a port, I get 8 callbacks, each giving me 1 bit of the port. This seems to be the way simavr is intended to be used, and also seems completely crazy to me.

buserror commented 5 years ago

Actually if you look at the code, you'll find there IS a IRQ that sends the whole port as a whole. IOPORT_IRQ_PIN_ALL is exactly what you want. Most examples don't need it, but it's there...