Safe Register Macros (#define) + Config Function Using Them
Title: Implement and Use Safe Bit Macros for a Configuration Sequence
Level: Difficult
Concepts: Preprocessor macros, do { … } while(0) pattern, side‑effect safety guidance, compile‑time guards, conditional compilation, API design that consumes macros
Scenario
You must configure a device by manipulating bitfields in three volatile registers:
CTRL(control),STAT(status),CFG(configuration).- The sequence is:
- Set
CTRL.EN(bit 0). - Poll
STAT.RDY(bit 7) until it becomes1, with a max attempts limit. - Write a 3‑bit field
CFG.MODE(bits[5:3]) with a requestedmodein0..7. - Clear
STAT.INT(bit 6) using W1C (write 1 to bit 6 only).
- Set
- You must implement macros to:
- Get/set/clear bits safely for volatile int registers,
- Prepare/extract a field
[hi:lo], - Provide an assert-like compile-time guard when
MODE_MAX < 7(build-time policy), - Optional logging enabled by
#define ENABLE_CFG_LOG 1.
Then implement a function device_configure that uses these macros to perform the sequence robustly, with timeouts and return codes.
Problem Statement
Write a set of macros and a function that together perform a safe register configuration, avoiding common macro pitfalls (accidental multiple evaluation, lack of scoping, missing parentheses). The macros must be parenthesized, use do { … } while (0) for statement-like forms, and document that arguments should be free of side effects (since pure C macros cannot guarantee single evaluation without extensions).
Requirements
- Allowed C types only; registers are
volatile int*. - Define the following macros (names fixed; implementers fill bodies):
- Bit/Mask helpers
BIT(b)→(1 << (b))BMASK(hi, lo)→ mask covering bits[hi..lo](assumehi >= loand both in0..30)
- Register operations (statement-like; need
do{...}while(0))SET_BITS(reg_ptr, mask)→*reg_ptr |= mask;CLR_BITS(reg_ptr, mask)→*reg_ptr &= ~(mask);W1C_BITS(reg_ptr, mask)→*reg_ptr = (mask);(write-1-to-clear)
- Field helpers
FIELD_PREP(hi, lo, val)→ putvalinto position[hi..lo](value masked to field width)FIELD_GET(hi, lo, word)→ extract field[hi..lo]fromword
- Compile-time guard (no extensions): use an
enumtrick so that if a condition fails, a negative array size occurs:ENUM_ASSERT(name, cond)→enum { name = 1/(int)(!!(cond)) };- Usage example:
ENUM_ASSERT(MODE_RANGE_OK, (MODE_MAX >= 7));
- Usage example:
- Bit/Mask helpers
- Optional logging macro:
- If
ENABLE_CFG_LOGis defined and non-zero, expandCFG_LOG(msg)to a callcfg_log(msg); else expand to empty.cfg_logis provided by caller.
- If
- Function to implement (uses macros):
- Name:
device_configure - Args:
volatile int *CTRL, *STAT, *CFGint mode(requested0..7)int max_attempts(≥ 0)
- Return code enum (
typedef):DC_OK = 0,DC_BAD_ARG = -1,DC_TIMEOUT = -2
- Behavior:
- Validate pointers,
moderange, andmax_attempts ≥ 0. SET_BITS(CTRL, BIT(0));(enable)- Poll
STATuntilSTAT & BIT(7)is set or attempts exhausted. - If ready, write
CFGfield[5:3]withmode:- Read
tmp = *CFG; - Clear the field:
tmp &= ~BMASK(5,3); - OR-in
FIELD_PREP(5,3, mode) - Write
*CFG = tmp;
- Read
W1C_BITS(STAT, BIT(6));to clear interrupt.- Return
DC_OK; on timeout returnDC_TIMEOUT; on invalid argsDC_BAD_ARG.
- Validate pointers,
- Name:
Function Details
- Name:
device_configure - Arguments:
volatile int *CTRLvolatile int *STATvolatile int *CFGint modeint max_attempts
- Return Value:
int— one ofDC_OK,DC_BAD_ARG,DC_TIMEOUT.
- Description:
Demonstrates macro design (parentheses, statement-like forms, compile-time guards, conditional logging) and safe usage withvolatileregisters and explicit read‑modify‑write for a bitfield.
Solution Approach
- Macro design guidance:
- Always parenthesize parameters and the whole macro value: e.g.,
#define BIT(b) (1 << (b)). - For statement-like macros, wrap body in
do { ... } while (0)to ensure they behave like a single statement in any context. - For value macros that combine expressions (e.g.,
FIELD_PREP), document that arguments must be side-effect free. - Provide a compile-time guard (
ENUM_ASSERT) to enforce build-time invariants without non-standard extensions. - Use
#if defined(ENABLE_CFG_LOG) && (ENABLE_CFG_LOG)to conditionally compile logging.
- Always parenthesize parameters and the whole macro value: e.g.,
- Function flow:
- Validate inputs; return
DC_BAD_ARGon failure. - Enable, then busy-wait with a counter (
attempts++) until ready bit set orattempts == max_attempts. - Program the field using
FIELD_PREPandBMASK. - Clear the interrupt using
W1C_BITS. - Return
DC_OKon success;DC_TIMEOUTif ready bit never set.
- Validate inputs; return
Tasks to Perform
- Define helper macros:
BIT,BMASK,FIELD_PREP,FIELD_GETwith full parentheses. - Define statement-like macros:
SET_BITS,CLR_BITS,W1C_BITSwithdo{...}while(0). - Define
ENUM_ASSERT(name, cond)as specified; use it to assertMODE_MAX >= 7(you define#define MODE_MAX 7or higher). - Define
CFG_LOG(msg)with conditional compilation aroundENABLE_CFG_LOG. typedef enum { DC_OK=0, DC_BAD_ARG=-1, DC_TIMEOUT=-2 } DC_Ret;- Implement
device_configureper the behavior steps. - Document in comments that macro arguments should avoid side effects (e.g., no
*p++inside).
Test Cases
| # | Inputs / Precondition | Expected Output | Notes |
|---|---|---|---|
| 1 | CTRL=0, STAT=0, CFG=0; mode=3; max_attempts=0 |
ret=DC_TIMEOUT; CTRL bit0 set; STAT unchanged; CFG unchanged |
No attempts → immediate timeout |
| 2 | Ready immediately: STAT already has bit7 set |
ret=DC_OK; CFG[5:3]=3; STAT W1C bit6 written |
Field programmed, INT cleared |
| 3 | Ready after 3 polls: STAT transitions to set on 3rd read |
ret=DC_OK |
Poll loop works with limit |
| 4 | mode=8 or negative |
ret=DC_BAD_ARG |
Out-of-range mode |
| 5 | CTRL=NULL or STAT=NULL or CFG=NULL |
ret=DC_BAD_ARG |
Invalid pointers |
| 6 | Field preservation: preset other CFG bits; after configure only [5:3] changed |
ret=DC_OK |
RMW correctness |
| 7 | With #define ENABLE_CFG_LOG 1, verify cfg_log("...") is called along steps (manually/in harness) |
ret=DC_OK |
Conditional macro compiled in |