This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CSL, bitfield structs, compile

The CSL basically "sucks" * because it uses preprocessor macros to describe registers while the C language provides bitfields for this purpose. Here is an example (DSP interrupt controller) showing why the bitfield method is much better for coding:

    CSL_FINS (intcRegs->INTMUX1, DSPINTC_INTMUX1_INTSEL4, TIMER0_EVENT); //CSL
    intcRegs->INTMUX1.INTSEL4= TIMER0_EVENT;  //bitfields

The bitfields method is more readable and writable. It can be read and written directly using the hardware manual, no need to learn anything to do with software libraries. It would be no more effort to provide support software that provides bitfield descriptions than macro descriptions.

Unfortunately there is a problem with the second statement. The compiler generates a byte access for it. A byte write-access to a register may have undesirable side-effects on the bytes that are not addressed. Read accesses work.

The CCS debugger fully supports bitfields, whereas macros are meaningless to the debugger. Eg you can read amd write intcRegs->INTMUX1.INTSEL4 with the debugger but you can't read it using any macro expression.

My conclusion is that TI has (maybe) not thought out how to make use of bitfields in its support software and if they had they would have had to make some provision in the compiler to generate 32 bit accesses even when only 1 bit is written to.

I hope someone can confirm or correct my understanding of how the compiler and the registers work and consider suporting improvements to the CSL, perhaps with community support. I would help.

A relevant thread from 2011, http://e2e.ti.com/support/microcontrollers/msp430/f/166/t/91725.aspx , has this information, which is relevant but not the end of the story in my opinion.

"... there are several hardware registers where bitfields simply not work, e.g. those with a password. Or interrupt vector registers, where reading the register removes the interrupt state, no matter which abstract bit member you accessed.
Or other registers where it is necessary to set several bits simultaneously. Or where the additional access for each bitfield access has unwanted (and perhaps at first unniticed) side effects. MCU programming is not an abstract thing, it has to deal with real world realtime requirements and restrictions. being as close to the hardware as possible is the key to success. ..." -Jens-Michael Gross

* Excuse me but it has been annoying me for years.

  • You are more than welcome to discuss your opinions in this forum.  And that especially includes negative opinions.  I wish more threads like this would gain traction.  But that tends not to happen much in this forum.

    The compiler development team is not responsible for the CSL, so I'm not in the best position to comment.  But I think I can shed some light.

    You want the concise syntax of bit fields, yet at the same time you want to precisely control how the compiler accesses certain registers.  The C language simply does not provide that.  If there were a simple straightforward way to do this, then everyone (not just TI) would use it.  I suppose it is possible that some simple method exists and none of us have been smart enough to find it.  But I think it is more likely that this problem really is that hard to solve.  This means any solution you go with will have some drawbacks that are downright unpleasant.

    Thanks and regards,

    -George

  • Thanks for your response George, and please stay on the line because I would like to get further than the last thread on this subject did.

    As a compiler expert you might not be very conscious of why it matters to developer engineers to me. When you are trying to get something working with peripheral hardware you might be dealing with hundreds of registers. To decode any one of them from the debugger display you need a pencil and paper and a minute of time. You might be looking at the same lot of registers over and over again. It is very tedious. But I will assume you accept that I have a good reason for asking. In K&R's book, they say they introduced bitfields for the purpose of describing machine registers.

    Here is the simple method that from what I know would go a long way. Let there be a pragma or compiler option which forbids 16 bit or 8 bit accesses to be generated for bitfield operations. The rationale is that the programmer may know that the operations will be applied to memory where those operations are not supported. After all, by the time you have regions of memory mapped registers you have a system with heterogenous memory. The programmer can deal with that but we need this one extra weapon. Is there a drawback that I have not thought of?

    My initial post was worded in a cheeky way but I don't want it to have a negative tone and I am doing my best to make constructive suggestions.

    -RD

  • Robert, did you consider using a union of your bitfield and a uint32_t? You could then update a local copy of the register contents using your bitfield and write it back using the uint32_t. That's permitted at least in C.

  • Hi Markus, thanks for the comment.

    Yes I can do that (without unions even) but it is not as attractive as writing simply register.field = value. Because it is not as good, people will think bitfield structs are not so good and anyone who wants to use them will be, as now, writing their own. It's a waste writing stuff like that for one-time use.

  • Hi


    As desirable as a concise notation is, what you suggest wouldn't work. For example, what would you do when a bitfield member is written to? Would you read the register first to keep the old values of unaffected bitfields in the same register? You'd have to in most cases, but then there are situations when reads from and writes to a memory address effectively access different registers. In this case, you'd have subtle yet catastrophic bugs. All in all, you'd lose a lot of control over when the corresponding memory accesses happen.

    Markus

  • Robert Durkacz1 said:
    Let there be a pragma or compiler option which forbids 16 bit or 8 bit accesses to be generated for bitfield operations.

    I think you are on to something here.  But I have question.  Suppose you have code like this ...

    peripheral_register.field1 = 10;
    peripheral_register.field2 = 20;
    

    Suppose that, because of details of the HW, both of these writes must happen with a single memory write instruction.  Your method does not guarantee that.  It only guarantees that, when the memory write occurs, it will use a 32-bit wide store instruction.  The compiler is still free to issue two separate memory writes, or combine them into one.

    With this in mind, do you still contend this suggestion solves the problem?

    Thanks and regards,

    -George

  • If you could give a concrete example it would help. In general whatever you do you must take into account what the hardware manual says for the given register. However the source code is written, the generated code ought to do much the same thing. It would read the 32 bit register, change some bits and preserve others in the local copy, and then write back to the register. In cases where you can use the one-line statement register.field = value the compiler does all that. (Except currently it may try to get away with a 8 bit write which the hardware does not support.)

    When it is necessary you use the method which you mentioned in your previous post. Either way there is no need to use macros.

    Robert

  • In answer to George Mock, if it is unacceptable to do the following operation in two register accesses-

    peripheral_register.field1 = 10;
    peripheral_register.field2 = 20;
    

    then you are forced to explicitly make a local copy and work on it locally, just as Markus Moll said in his first post. It
    is not as brilliantly simple because now you have to look up what name has been given to the struct for that register.
    I would have it thus:

    #include "peripheral.h"
    ...
    struct registerX r = registerX;
    r.field1 = 10;
    r.field2 = 20;
    registerX = r;
    

    I am imagining that I get all the necessary back-up from vendor-supplied header files. That is why I don't assume
    any union; I think it reflects a personal choice whereas what is in common header files should be the minimum
    necessary.

    I read in the compiler manual some considerations about when the compiler would merge write operations of
    bitfield operations. So it may be that the complier gives more support than we are assuming, but if it does that is
    just a bonus.

  • Hi

    If you're using EABI, you might want to look at the "volatile bitfields" description of http://www.ti.com/lit/an/sprab89a/sprab89a.pdf (2.7.1, page 20). They seem to do exactly what you want.

    I would still be worried about reads and writes doing different things, for example, the DM643x UART has multiplexed registers (RBR and THR are both at offset 0, IIR and FCR are both at offset 8). So if you wrote

    struct FCR {
      uint32_t fifoen : 1;
      uint32_t rxclr : 1;
      uint32_t txclr : 1;
      uint32_t dmamode : 1;
      uint32_t : 2; /* reserved */
      uint32_t rxfiftl : 2;
      uint32_t : 24; /* reserved */
    } *fcr = ADDRESS_OF_FCR_REG;
    
    ...
    
    fcr->txclr = 1; /* clear the transmitter fifo */

    then the assignment would first read from ADDRESS_OF_FCR_REG, which will not give you the contents of the FCR register, but those of the Interrupt Identification Register (IIR). Therefore, your remaining flags will be written arbitrarily and some of your reserved bits may be assigned invalid values!

    As I said before, this can lead to very subtle and easy to miss / hard to spot bugs, or you'd have to accept that you can only use the bitfield notation with some registers, and not with others (personally, I'd prefer consistent interfaces).

    Markus

  • Markus Moll wrote:

    ... If you're using EABI, you might want to look at the "volatile bitfields" description of http://www.ti.com/lit/an/sprab89a/sprab89a.pdf (2.7.1, page 20). They seem to do exactly what you want.

    I take this as an indication that the compiler wants to support bitfiields for register access but whoever looks after the CSL has not taken up the offer. If they had, then the issue of unsupported 8 bit or 16 bit writes would have been addressed.

    Markus goes on to raise the example of a write-only register. If you read this register you get an answer which is almost random because in fact you are reading some other register (a read-only one). You might be tempted to use the one-line form register.field=value if you don't care about the other bits. The one-line form necessarily generates a read which is irrelevant in this case and may produce some side-effects in the read-only register for all we know. The programmer accordingly would surely take care to write all 32 bits. Using a bifield struct the programmer would create the struct in local memory, fill it in and assign it to the register. It would be something like this:

    #include "peripheral.h"
    struct FCR *const fcr = ... //whatever, the register; this ideally set up in a common header file
    ...
    struct FCR local; // = { ... } (could be initialised)
    local.txclr = 1;
    //fill in other fields if necessary
    *fcr = local;

    There is more work to do in this case only because of the peculiarities of the register. The C code is expressive of that. There would be other ways of doing it, still using bitfields, so I don't see this as an argument against using bitfields.

  • Another piece of evidence that bitfield structs are intended to be used is that the compiler and CCS debugger suport enums in bitfields. In a bitfield struct you can have-

    enum Status :3 status;

    and let's say one of the literals  in enum Status  is-

      GOOD = 22,

    then the debugger decodes the value 22 for you and displays the literal GOOD. The debugger is trying to take the burden of decoding away from the developer, but generally we are failing to get the benefit of this because of how the CSL is constructed.

  • Hi

    Now that it's down to personal preferences: Personally I'd prefer a set of register-specific read/write macros/inline functions that return bitfield types. That way, read-only/write-only registers cannot be misused, it is explicit when and where reads and writes take place, and you can still conveniently access individual fields:

    fcr_register_t fcr = { 0 };
    fcr.txclr = 1;
    fcr.rxclr = 1;
    write_register_fcr(&fcr); /* register-specific for additional type safety */
    read_register_fcr(&fcr); /* compile-time warning / link time error (implicitly declared / undefined symbol) */
    
    some_other_register_t reg;
    read_register_some_other(&reg);
    reg.some_value = 3;
    reg.some_flag = 0;
    write_register_some_other(&reg);

    Markus

  • Markus, you wrote a comment beginning with the words

    "Now that it's down to personal preferences ..."

    I don't want anyone to think this is about personal preferences. There are some simple and serious questions that have been asked several times before and never properly answered by TI. Will TI fix the compiler deficiency that has been pointed out? Are the various CSL's as good as they should be? Are they all on the wrong footing?

    I will comment on your idea to have a wrapper function merely to copy register contents around. It lacks force. Registers, from all that has been discussed, can always be copied safely by assignment, and that is regardless of whether they are treated as integers or structs. That is perfect already; you can't improve on it.

    Do you think we will hear anything more from George?

    Regards,

    Robert

  • Robert Durkacz1 said:

    "Now that it's down to personal preferences ..."

    I don't want anyone to think this is about personal preferences.

    Sorry, I was under the impression that it was.

    Robert Durkacz1 said:

    I will comment on your idea to have a wrapper function merely to copy register contents around. It lacks force. Registers, from all that has been discussed, can always be copied safely by assignment, and that is regardless of whether they are treated as integers or structs. That is perfect already; you can't improve on it.

    The idea was to have additional safety for exactly the situation where a read-only and a write-only register are mapped to the same address. It is also slightly more expressive, but that really is a matter of taste.


    Markus