I like writing embedded C firmware to the BARR-C:2018 standard and I have recently started using PC-Lint to check the code. I am currently using a PIC32 processor and I want to configure a register such that it:
- is easy to read,
- generates no linter messages,
- compiles to a small amount of assembler.
I will outline my options below, with pros and cons. I am interested to hear your solutions. The register I have chosen to illustrate the problem is not important.
Bit-fields with Microchip-supplied header
✓ No lint messages, easy to read, easy to change individual settings
✗ 20 assembly instructions (O0) (increases in more complex registers)
ADC0TIMEbits.SAMC = 25U;
ADC0TIMEbits.ADCDIV = 50U;
ADC0TIMEbits.BCHEN = 0U;
ADC0TIMEbits.SELRES = 2U;
ADC0TIMEbits.ADCEIS = 6U;
Simple assignment
✓ No lint messages, 2 assembly instructions (O0)
✗ Not easy to read, not easy to change individual settings
ADC0TIME = 0x1A190019U;
Explicitly consider every bit in every register
✓ No lint messages, 2 assembly instructions (O0), easy to change individual settings
✗ Not easy to verify that the shift values are correct
ADC0TIME = ((25U << 0U) | /* ADC0 Sample Time bits */
(50U << 16U) | /* ADC0 Clock Divisor bits */
(0U << 23U) | /* Buffer Channel Enable bit */
(2U << 24U) | /* ADC0 Resolution Select bits */
(6U << 26U)); /* ADC0 Early Interrupt Select bits */
Explicitly consider every bit in every register using definitions from Microchip's supplied header file
✓ 2 assembly instructions (O0), easy to change individual settings, shift values are controlled by manufacturer and are human-readable
✗ Values in the supplied header are signed, which lead to Lint info 8524: combining signed and unsigned types with operator '<<' [BARR-C:2018 Rule 5.3c]
ADC0TIME = ((25U << _ADC0TIME_SAMC_POSITION) | /* ADC0 Sample Time bits */
(50U << _ADC0TIME_ADCDIV_POSITION) | /* ADC0 Clock Divisor bits */
(0U << _ADC0TIME_BCHEN_POSITION) | /* Buffer Channel Enable bit */
(2U << _ADC0TIME_SELRES_POSITION) | /* ADC0 Resolution Select bits */
(6U << _ADC0TIME_ADCEIS_POSITION)); /* ADC0 Early Interrupt Select bits */
As above + casting the constants to get rid of the linter message
✓ 2 assembly instructions (O0), easy to change individual settings, shift values are controlled by manufacturer and are human-readable, no lint messages
✗ Casting looks cumbersome
ADC0TIME = ((25U << (uint32_t)_ADC0TIME_SAMC_POSITION) | /* ADC0 Sample Time bits */
(50U << (uint32_t)_ADC0TIME_ADCDIV_POSITION) | /* ADC0 Clock Divisor bits */
(0U << (uint32_t)_ADC0TIME_BCHEN_POSITION) | /* Buffer Channel Enable bit */
(2U << (uint32_t)_ADC0TIME_SELRES_POSITION) | /* ADC0 Resolution Select bits */
(6U << (uint32_t)_ADC0TIME_ADCEIS_POSITION)); /* ADC0 Early Interrupt Select bits */
Alternatively, I could modify the Microchip-supplied header and define the constants as unsigned. That means the header is non-standard and I need to include it in my project repository. Is that a problem?
Edit: Extract from Microchip header file. Values are signed:
#define _ADC0TIME_SAMC_POSITION 0x00000000
#define _ADC0TIME_SAMC_MASK 0x000003FF
#define _ADC0TIME_SAMC_LENGTH 0x0000000A
#define _ADC0TIME_ADCDIV_POSITION 0x00000010
#define _ADC0TIME_ADCDIV_MASK 0x007F0000
#define _ADC0TIME_ADCDIV_LENGTH 0x00000007
#define _ADC0TIME_BCHEN_POSITION 0x00000017
#define _ADC0TIME_BCHEN_MASK 0x00800000
#define _ADC0TIME_BCHEN_LENGTH 0x00000001
#define _ADC0TIME_SELRES_POSITION 0x00000018
#define _ADC0TIME_SELRES_MASK 0x03000000
#define _ADC0TIME_SELRES_LENGTH 0x00000002
#define _ADC0TIME_ADCEIS_POSITION 0x0000001A
#define _ADC0TIME_ADCEIS_MASK 0x1C000000
#define _ADC0TIME_ADCEIS_LENGTH 0x00000003