Never seen this before!

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,658
I've seen my fair share of binary, bits, flags, hex, registers, assembly code and machine code, but never quite seen this:
1709844793040.png

The field OC1M is a 4-bit field but the MSB is at bit 16 and the lower three bits are situated at bit 6,5,4, the field OC2M is the same.

By looking at the docs for a simpler, earlier chip I can see that it was originally a 3-bit field on STM32 MCUs but in the STM32MP1X family they extended it to become a 4-bit field, and I am doing this for leisure?
 

WBahn

Joined Mar 31, 2012
30,242
I've seen my fair share of binary, bits, flags, hex, registers, assembly code and machine code, but never quite seen this:
View attachment 317125

The field OC1M is a 4-bit field but the MSB is at bit 16 and the lower three bits are situated at bit 6,5,4, the field OC2M is the same.

By looking at the docs for a simpler, earlier chip I can see that it was originally a 3-bit field on STM32 MCUs but in the STM32MP1X family they extended it to become a 4-bit field, and I am doing this for leisure?
Doesn't strike me as being that odd.

As you say, they previously had a 3-bit field, which was mapped into a 32-bit word along with a bunch of other things. Later, they needed it to be a 4-bit field. So you make a choice. Do you completely remap your word, or do you maintain as much compatibility as possible by simply mapping the new bit to location within the word that was previously unused. While the devil is in the details, I can see that a very strong argument could be made for just mapping the new bit to a currently unused location. I don't know about this particular line of processors, but doing this makes it much more likely that you can just take code that ran on the older processors and run it on the new one directly -- provided the older code didn't try to play games and use those reserved bits for other things -- and if they did, then that's too bad since they shouldn't have done that.

I have no idea what the comment about leisure is -- I don't know what you are doing or why.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,658
This is why you don't use absolute bit positions. You use the definitions supplied in the device header file.
Well this is an unusual case, it wouldn't invalidate a struct defined as bit fields but would require a bit of work to merge the two fields. But this is C# work I'm doing here, no headers (or bitfields) nor anything like them.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,658
I guess this is how I'd get the value from the raw 32 bit unsigned register:
Code:
get =>  ((allBits >> 13 & 0x00000008U) | (allBits >> 4 & 0x00000007));
Now for setting it, that'll be fun...
 
Last edited:

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,658
This must be the setter, updating the register bits from a supplied int:

Code:
set => allBits = allBits | (((value & 0x00000008U) << 13) | ((value & 0x00000007) << 4));
 

WBahn

Joined Mar 31, 2012
30,242
This must be the setter, updating the register bits from a supplied int:

Code:
set => allBits = allBits | (((value & 0x00000008U) << 13) | ((value & 0x00000007) << 4));
But what if the bits are already set and value were zero? Would that clear the target bits in allBits?
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,658
The more I work with this the more I think that the struct representing each register should be generated from a definition. Writing that is pretty easy in C# (although the algorithms for generating the HEX literals like those above, is not yet clear to me).

I'm considering something like this, a very simple text definition:

Code:
"cc1s(1,0):oc1fe(2):oc1pe(3):oc1m(16,6,5,4):oc1ce(7):cc2s(9,8):oc2fe(10),oc2pe(11):oc2m(24,14,13,12):oc2ce(15)";
Thoughts?
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,658
This has been time very well spent today. The core algorithms are now well defined and pass unit tests. The register metadata needed a "type" to be added (or rather a type name, just a string) in order to fully generate the entire structure.

Here's the "definition" for the CCMR1 register:

cc1s(1,2)CC1SMode: oc1fe(2,1)bool: oc1pe(3,1)bool: oc1m(16,1, 6,3)OCMode: oc1ce(7,1)bool: cc2s(9,2)CC2SMode: oc2fe(10,1)bool: oc2pe(11,1)bool: oc2m(24,1 14,3)OCMode: oc2ce(15,1)bool
Each field has a name, followed by one or more (bitposition, length) pairs followed by a type name (used to generate the source text).

From that I can generate a set of objects each of which represents a single register field, like for example oc2m. That is a "fragmented" field, its bits are not contiguous within the 32 bit word.

This is the unit test for the FieldDescriptor class with the values that correspond to the oc2m register:


C#:
[Fact]
public void TestFieldDescriptor()
{
    var oc2m = new FieldDescriptor("OC2M", "OCMode", (24, 1), (14, 3));

    Assert.Equal(0xFEFF8FFFU, oc2m.NotMask);

    var (and, shift) = oc2m.ShiftAndPairs[0];

    Assert.Equal(0x00000008U, and);
    Assert.Equal(21U, shift);

    (and, shift) = oc2m.ShiftAndPairs[1];

    Assert.Equal(0x00000007U, and);
    Assert.Equal(12U, shift);
}
This can support any degree of fragmented fields, even composed of three, four parts (which is likely very rare). The code generator simply consumes the descriptor string and creates a list of FieldDescriptor objects, the rest is trivial.

This is how the generated values appear in generated code:


Code:
        public OCMode OC2M    
        {
            get => (OCMode)((allBits >> 21 & 0x00000008U) | (allBits >> 12 & 0x000000007U));
            set => AllBits = (allBits & 0xFEFF8FFFU) | (((uint)value & 0x00000008U) << 21) | (((uint)value & 0x00000007U) << 12);
        }
Generating all that HEX now requires like zero effort, just describe the register as a string and let the tool do the work!
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,658
Success, and the generated struct passes the original unit tests that manipulate the various fields and check the resulting raw 32 bit value, so nothing seems to be broken!

This is the code that is generated by this call:

C#:
var code = RegisterStructGenerator.GenerateStruct(nameof(RegisterDefinitions.CCMR1), RegisterDefinitions.CCMR1, true);
C#:
//-----------------------------------------------------------------------
// Steadsoft.Spitfire
// Warning DO NOT MODIFY this struct by editing.
// This code was generated by a software tool on 03-08-2024 at 15:49:08.
//------------------------------------------------------------------------

public partial struct CCMR1
{
    public CC1SMode CC1S
    {
        get => (CC1SMode)((this >> 0 & 0x00000003U));
        set => AllBits = (this & 0xFFFFFFFCU) | ((Convert.ToUInt32(value) & 0x00000003U) << 0);
    }

    public bool OC1FE
    {
        get => Convert.ToBoolean((this >> 2 & 0x00000001U));
        set => AllBits = (this & 0xFFFFFFFBU) | ((Convert.ToUInt32(value) & 0x00000001U) << 2);
    }

    public bool OC1PE
    {
        get => Convert.ToBoolean((this >> 3 & 0x00000001U));
        set => AllBits = (this & 0xFFFFFFF7U) | ((Convert.ToUInt32(value) & 0x00000001U) << 3);
    }

    public OCMode OC1M
    {
        get => (OCMode)((this >> 13 & 0x00000008U) | (this >> 4 & 0x00000007U));
        set => AllBits = (this & 0xFFFEFF8FU) | ((Convert.ToUInt32(value) & 0x00000008U) << 13) | ((Convert.ToUInt32(value) & 0x00000007U) << 4);
    }

    public bool OC1CE
    {
        get => Convert.ToBoolean((this >> 7 & 0x00000001U));
        set => AllBits = (this & 0xFFFFFF7FU) | ((Convert.ToUInt32(value) & 0x00000001U) << 7);
    }

    public CC2SMode CC2S
    {
        get => (CC2SMode)((this >> 8 & 0x00000003U));
        set => AllBits = (this & 0xFFFFFCFFU) | ((Convert.ToUInt32(value) & 0x00000003U) << 8);
    }

    public bool OC2FE
    {
        get => Convert.ToBoolean((this >> 10 & 0x00000001U));
        set => AllBits = (this & 0xFFFFFBFFU) | ((Convert.ToUInt32(value) & 0x00000001U) << 10);
    }

    public bool OC2PE
    {
        get => Convert.ToBoolean((this >> 11 & 0x00000001U));
        set => AllBits = (this & 0xFFFFF7FFU) | ((Convert.ToUInt32(value) & 0x00000001U) << 11);
    }

    public OCMode OC2M
    {
        get => (OCMode)((this >> 21 & 0x00000008U) | (this >> 12 & 0x00000007U));
        set => AllBits = (this & 0xFEFF8FFFU) | ((Convert.ToUInt32(value) & 0x00000008U) << 21) | ((Convert.ToUInt32(value) & 0x00000007U) << 12);
    }

    public bool OC2CE
    {
        get => Convert.ToBoolean((this >> 15 & 0x00000001U));
        set => AllBits = (this & 0xFFFF7FFFU) | ((Convert.ToUInt32(value) & 0x00000001U) << 15);
    }

}
It took less effort than I suspected it would, partly because I'd been knee deep in bit shifts and ands and ors for days. So ideally I should add a few more tests to prove 100% that its generating correct logic and once that's established, I can remove every hand coded register struct and create that field def text for them and regenerate them all, perhaps a file of lines, each line being a register descriptor, can be defined and then the tool can just generate umpteen register structs all at once, the coding work shrinks dramatically because creating this register descriptor strings is almost trivial, something one can do while sipping coffee.
 
Last edited:

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,658
For anyone remotely curious, here's a view of the created RegisterDescriptor object. This abstracts the register in a very convenient form that maps to the problem domain as opposed to the solution domain:

1710003253177.png

That object is created solely from the descriptor text:

cc1s(1,2)CC1SMode: oc1fe(2,1)bool: oc1pe(3,1)bool: oc1m(16,1, 6,3)OCMode: oc1ce(7,1)bool: cc2s(9,2)CC2SMode: oc2fe(10,1)bool: oc2pe(11,1)bool: oc2m(24,1,14,3)OCMode: oc2ce(15,1)bool
This is a good example of a problem domain abstraction, anyone familiar with register documentation can immediately relate to this object and allow them to then create solution domain abstractions from it, no direct need to think about bitmasks, AND, OR and so on.

The object even exposes convenient UnusedBits and UsedBits, possibly useful in creating (debug only) code that can trap writes to forbidden bits and so on.

Here's the register diagram from the documentation again, just for reference. If anyone reading this can see anything wrong or has a suggestion etc, please do speak up.
1710003864806.png
C# does not have bit fields, but it does have properties and these represent the fields in the register and encapsulate the shifting, anding and oring etc.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,658
This code generation has gone very well, the project is now public for anyone curious. I'm now thinking of doing something similar for C. I can develop an app (C#) that will consume the register definitions and spit out C typedefs automatically, this is an example of the finished regdef syntax:

Code:
cr1(0x00):     cen(0,1)bool; udis(1,1)bool; urs(2,1)bool; opm(3,1)bool; dir(4,1)bool; cms(6,2)CenterMode; arpe(7,1)bool; ckd(9,2)uint; uifremap(11,1)bool;
cr2(0x4):      ccds(3,1)bool; mms(6,3)MasterMode; ti1s(7,1)bool;
sr(0x10):      uif(0,1)bool; cc1if(1,1)bool; cc2if(2,1)bool; cc3if(3,1)bool; cc4if(4,1)bool; tif(6,1)bool; cc1of(9,1)bool; cc2of(10,1)bool; cc3of(11,1)bool; cc4of(12,1)bool;
ccmr1(0x18):   cc1s(1,2)CC1SMode; oc1fe(2,1)bool; oc1pe(3,1)bool; oc1m(16,1, 6,3)OCMode; oc1ce(7,1)bool; cc2s(9,2)CC2SMode; oc2fe(10,1)bool; oc2pe(11,1)bool; oc2m(24,1,14,3)OCMode; oc2ce(15,1)bool;
cnt_nre(0x24): this(32,1)bool;
cnt_rem(0x24): this(31,1)bool; uifcpy(31,1)bool;
arr(0x2C):     this(31,1)bool;
psc(0x28):     value(15,16)uint;
ccer(0x20):    cc1e(0,1)bool; cc1p(1,1)bool; cc1np(3,1)bool; cc2e(4,1)bool; cc2p(5,1)bool; cc2np(7,1)bool; cc3e(8,1)bool; cc3p(9,1)bool; cc3np(11,1)bool; cc4e(12,1)bool; cc4p(13,1)bool; cc4np(15,1) bool;
ccr1(0x34):    ccr(31,32)uint;
and this is what was generated by the tool from that defintion. Of course C doesn't have properties but does have bitfields and being to reliably generate these with an automated error free process sounds like a good idea to me for the STM32 stuff I play with.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,658
Does C# have unions? defines?
No, neither of those are supported. There are good reasons for almost all of the decisions the C# designers made. The rejection of #define (macros) was made because macros are impossible to debug and can have unanticipated side effects. As for unions these are not supported because the language can deliver similar functionality by the use of properties and so on. One can represent state in a variety of ways without the need to define different types over the same storage.

However I am critical of C#'s insufficient set of systems programming features, I've raised this kind of thing with the designers before. There was a successful internal project at Microsoft that used a variant of C# to write a solid operating system, so with some changes the language could be a powerhouse in this arena but it is a minor number of customers that desire that just now.

If the amount of work was not so daunting I'd love to design and build an operating system, I am satisfied that my experience and skills are up to the task but frankly it's just a great deal of work and is better done by a team rather than a lone project. There have been so many good ideas lost in the mad obsession with stuff like Linux and C and so on, that if one were to gather these ideas together I think a very good outcome could result.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,658
Actually I was wrong about unions. These are in fact possible if desired, though very uncommon. The language supports various attributes that can be used to define the physical offsets of members within structures. Providing the structures are "blittable" this will work more or less as expected:

C#:
    [StructLayout(LayoutKind.Explicit)]
    public struct Header
    {
        [FieldOffset(0)]
        public long size;
        [FieldOffset(0)]
        public double value;
    }
This works as expected, the storage for each field is comprised of the same bytes.
 
Top