12 Replies Latest reply on Feb 11, 2019 6:04 AM by brendansimon

    Setting GPIO BSRRH register doesn't seem to work

    brendansimon

      I have a function that sets a pin state to high or low, and code that calls that function to set the red LED on the STM32F4 Discovery board to high and low (it pulses).

      The code works on a real Disco board, but in QEMU the LED turns on but never turns off.

       

      To set the pin high (LED on), the appropriate bit is set in the GPIO BSSRL register.  This works the the LED turns on

       

      To set the pin low (LED off), the same bit is set in the GPIO BSSRH register.  This doesn't work.  The LED remains on in QEMU.

       

      I've confirmed that using GPIO ODR in conjunction with |= bit and &= ~bit works ok, but that's not what I want to do.

       

      I can get it to work (i.e. the LED to toggle) by doing BSSRL = bit, and BSSRH &= ~bit and vice-versa (but that is definitely wrong for all the other bits).

       

      So I suspect there is a bug in QEMU which is looking at both registers and if the BSSRL is set, it ignores the BSSRH.  Just a guess.  I imagine the code should look at ODR and just set or clear the bits in there when emulating the register access.

       

      Any thoughts?

       

      Thanks, Brendan.

      • Reply
        • Re: Setting GPIO BSRRH register doesn't seem to work
          ilg

          most probably a bug in the GPIO emulation.

           

          the code is:

           

          https://github.com/gnu-mcu-eclipse/qemu/blob/gnuarmeclipse-dev/hw/cortexm/stm32/gpio.c

           

          after lots of definitions, at the end you can see the actual functionality. please feel free to suggest improvements.

            • Re: Setting GPIO BSRRH register doesn't seem to work
              brendansimon

              Without knowing the code base or wanting to take the plunge to learn how to build qemu etc, the following might be a fix ?

               

              static void stm32f4_gpio_bsrr_post_write_callback(Object *reg, Object *periph,
                      uint32_t addr, uint32_t offset, unsigned size,
                      peripheral_register_t value, peripheral_register_t full_value)
              {
                  STM32GPIOState *state = STM32_GPIO_STATE(periph);
              
                  Object *odr = state->reg.odr;
                  assert(odr);
              
                  // 'value' may be have any size, use full_word.
                  uint32_t bits_to_set = (full_value & 0x0000FFFF);
                  uint32_t bits_to_reset = ((full_value >> 16) & 0x0000FFFF);
              
                  // Clear the BR bits and set the BS bits.
                  uint32_t new_value = (peripheral_register_get_raw_value(odr)
                          & (~bits_to_reset)) | bits_to_set;
                  stm32_gpio_update_odr_and_idr(state, odr, state->reg.idr, new_value);
              
              #if 1 //FIXME:BJS: clear BR and BS bits in the BSRR register after ODR is set
                  peripheral_register_write_value( state-reg.>bsrr, 0 );
              #endif
              }

               

              Guess you would have to do a similar thing for all the families (F0, F1, etc).

               

              Might be possible to consolidate that in one place and clear the `brr` register as well, to cover all families ?

               

              static void stm32_gpio_update_odr_and_idr(STM32GPIOState *state, Object *odr,
                      Object *idr, uint16_t new_value)
              {
                  assert(odr);
              
                  // Preserve old value, to compute changed bits
                  uint16_t old_value = peripheral_register_get_raw_value(odr);
              
                  // Update register value. Per documentation, the upper 16 bits
                  // always read as 0, so write is used, to apply the mask.
                  peripheral_register_write_value(odr, new_value);
              
              #if 1 //FIXME:BJS: clear BSRR and BRR registers after ODR is set
                  peripheral_register_write_value( state->reg.bsrr, 0 );
                  peripheral_register_write_value( state->reg.brr, 0 );
              #endif
              
                  stm32_gpio_set_odr_irqs(state, old_value, new_value);
                  stm32_gpio_update_idr(state, idr, new_value);
              }

               

              Brendan.

            • Re: Setting GPIO BSRRH register doesn't seem to work
              brendansimon

              I confirm that QEMU 2.8.0-4 resolves the issue.

               

              Thanks, Liviu !!