Failed to access to single-bit field
Closed this issue · 4 comments
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:
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?
I do not have experience with that particular MCU so let me ask a few generic questions :)
-
Which compiler are you using?
-
The cppreg version of your snippet would look like:
void can_t::init_filters_() { Peripheral::bxCAN::FINIT::set(); // ... magic ... // Turn all filters initialization -> active Peripheral::bxCAN::FINIT::clear(); while ( Peripheral::bxCAN::FINIT::is_set()) __nop(); }
right?
-
Any chances you can isolate the generated assembly listing?
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();
}
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.
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>;
// 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!