nicocvn / cppreg

A C++11 header-only library for MMIO registers
https://nicocvn.github.io/cppreg/
Other
60 stars 6 forks source link

Failed to access to single-bit field #17

Closed arturvasilev closed 8 months ago

arturvasilev commented 8 months ago

Hello!

First of -- great lib! Pure enjoyment using this approach -- code become small and easy to read and juggle with.

Recently I encountered a very strange problem -- I couldn't work with single bit programming STM32. Details below.

I'm working with STM32F042F6. Problem arises when I tried to activate filter banks -- to do that I had to clear a single bit FINIT. Here is his definition of this partical part:

#pragma once

#include <cppreg.h>

using namespace cppreg;

struct Peripheral {
  struct bxCAN : RegisterPack<0x4000'6400, 1024> {
    // Filter initialization mode
    using FINIT =
        Field<PackedRegister<bxCAN, RegBitSize::b32, 0x200>, 1, 0, read_write>;
  };
};

Here is a screenshot of what does that register have:

Screenshot from 2024-02-21 13-20-03

Everywhere else lib works flawlessly! But only here I had to hand-write access to this particular register.

void can_t::init_filters_() {
  // Strangest bug! Not working FINIT::set/clear();
  // Well, let's get dirty!
  uint32_t &finit = *((uint32_t *)(0x4000'6600));
  finit |= 0b1;

  // ... magic ...

  // Turn all filters initialization -> active
  finit &= ~0b1;
  while (finit & 0b1) __nop();
}

I found at last while()-cycle, where I check for successful activation of filters. If I use cppreg's access to FINIT-bitfield -- it does nothing, bit never changes. It always read bit as '1' (bit is always raised, cannot clear).

No one got hurt, except my pride. And a bit of my personal time.

Did I miss some caveat?

nicocvn commented 8 months ago

I do not have experience with that particular MCU so let me ask a few generic questions :)


Could you try the following and see if that works:

struct Peripheral {
  struct bxCAN : RegisterPack<0x4000'6400, 1024> {
    // Filter initialization mode
    using FINIT =
        Field<PackedRegister<bxCAN, RegBitSize::b32, 0x200>, 32, 0, read_write>;
   constexpr static auto active_mode = FINIT::type{0x2A1C0E00};
   constexpr static auto initialization_mode = FINIT::type{0x2A1C0E01};
  };
};

// ...

void can_t::init_filters_() {
  Peripheral::bxCAN::FINIT::write<Peripheral::bxCAN::initialization_mode>();

  // ... magic ...

  // Turn all filters initialization -> active
  Peripheral::bxCAN::FINIT::write<Peripheral::bxCAN::active_mode>();
  while (Peripheral::bxCAN::FINIT::read() != Peripheral::bxCAN::active_mode) __nop();
}
nicocvn commented 8 months ago

And possibly the issue might be the offset of the register:

struct Peripheral {
  struct bxCAN : RegisterPack<0x4000'6400, 1024> {
    // Filter initialization mode
    // When defining the PackedRegister the offset must be in bits! so (0x200)*8 = 4096
    using FINIT =
        Field<PackedRegister<bxCAN, RegBitSize::b32, 4096>, 32, 0, read_write>;
  };
};

Let me know if that helps.

nicocvn commented 8 months ago

Mind the typo in the last snippet ... FINIT should be:

// width is 1 bit not 32.
using FINIT =
        Field<PackedRegister<bxCAN, RegBitSize::b32, 4096>, 1, 0, read_write>;
arturvasilev commented 8 months ago
// When defining the PackedRegister the offset must be in bits! so (0x200)*8 = 4096

Oh, my! That was it! My eyes blurred after 78 PackedRegister -- and couldn't see that obvious error of mine! >_<

Thanks for pointing that out!

I should consider a vacation sooner, than later. :-)

Issue closed! Thanks for helping!