5.5.20 · D2 · HinglishEmbedded Systems & Real-Time Software

Visual walkthroughSoftware testing in embedded — unit tests on host, HIL testing

2,547 words12 min read↑ Read in English

5.5.20 · D2 · Coding › Embedded Systems & Real-Time Software › Software testing in embedded — unit tests on host, HIL testi

Hum parent topic par build karte hain aur Hardware Abstraction Layer (HAL) Design aur Mocking and Stubbing in C ke ideas ka sahara lete hain.


Step 1 — Problem: logic silicon ke saath glued hai

KYA. Ek ordinary firmware function dekho. Yeh ek sensor read karta hai, ek calculation karta hai, aur motor output likhta hai — sirf teen lines mein.

void update_motor(void) {
    uint16_t adc = ADC1->DR;      // (1) read hardware register
    int pwm = compute_pwm(adc);   // (2) pure math
    TIM2->CCR1 = pwm;             // (3) write hardware register
}

Term by term:

  • ADC1->DR ::: Analog-to-Digital Converter ka Data Register — ek real address jo sirf chip ke andar exist karta hai. Tumhare laptop par aisa koi address nahi hai.
  • compute_pwm(adc) ::: pure arithmetic — ek number ko ek number mein badalta hai. Yeh kahin bhi run hota hai.
  • TIM2->CCR1 ::: ek TIMer Capture/Compare Register — phir se, ek physical address jo sirf microcontroller (MCU) par present hai.

KYUN. Hum poochhna chahte hain: "kya main ise apne laptop par test kar sakta hoon?" Jawab hai nahi — line (1) aur line (3) un memory addresses ko touch karti hain jo off-chip exist nahi karti. Jaise hi hum ise run karte hain, yeh crash karta hai ya garbage read karta hai.

PICTURE. Figure function ko ek sandwich ki tarah dikhata hai: do laal "hardware" slices aur beech mein yellow "logic" filling. Laal slices hi woh hain jo code ko silicon se nail karte hain.

Figure — Software testing in embedded — unit tests on host, HIL testing

Step 2 — The cut: logic aur hardware ke beech seam ka naam rakho

KYA. Hum ek line kheenchte hain — literally — "code jo compute karta hai" aur "code jo chip ko touch karta hai" ke beech. Har hardware access ko ek naam milta hai, ek function call, raw register poke ki jagah.

void update_motor(void) {
    uint16_t adc = hal_adc_read(ADC_CHANNEL_1);   // named door IN
    int pwm = compute_pwm(adc);                    // pure logic
    hal_pwm_set(PWM_CHANNEL_1, pwm);               // named door OUT
}
  • hal_ prefix ::: Hardware Abstraction Layer — ek promise: "is naam ke peeche koi na koi implementation rehti hai; mujhe parwah nahi kaunsi."
  • hal_adc_read(...) ::: ek input door. Chip par yeh ADC1->DR read karta hai. Laptop par yeh ek variable read kar sakta hai.
  • hal_pwm_set(...) ::: ek output door. Chip par yeh TIM2->CCR1 likhta hai. Laptop par yeh bas record kar sakta hai ki kya likha gaya.

Register ki jagah function name kyun use karein? Isliye ki ek naam ek swap point hai. Ek register address fixed hota hai; ek function call ek sawaal hai — "jo bhi hal_adc_read ka jawab dega, mujhe ek number do." Ab hum alag-alag logon ko alag-alag builds mein jawab dene de sakte hain. Yeh bilkul Hardware Abstraction Layer (HAL) Design waala idea hai.

PICTURE. Figure sandwich ko dobara draw karta hai jahan laal slices the wahaan do labelled doors hain. Beech ki yellow logic ab door ke kisi bhi taraf se reachable hai.

Figure — Software testing in embedded — unit tests on host, HIL testing

Step 3 — Ek hi door ke liye do jawab (do builds)

KYA. Door ke peeche koi chahiye. Hum ek hi door naam ke do implementations likhte hain:

// hal_stm32.c  — REAL jawab (chip ke liye compile hota hai)
uint16_t hal_adc_read(int ch) { return ADC1->DR; }
 
// mock_hal.c   — FAKE jawab (laptop ke liye compile hota hai)
static uint16_t fake_adc[4];
void     mock_set_adc(int ch, uint16_t v) { fake_adc[ch] = v; }
uint16_t hal_adc_read(int ch)             { return fake_adc[ch]; }
  • Real version silicon read karta hai.
  • Mock version ek plain array read karta hai jo tum khud bharte ho. Yeh hai Mocking and Stubbing in C: ek stand-in jo tumhe inputs inject karne aur outputs inspect karne ki azaadi deta hai.

Do files kyun, if nahi? Isliye ki hum kabhi nahi chahte ki test-only code chip par ship ho, aur na hi chahte hain ki register pokes laptop par try ho. Hum jawab compile time par choose karte hain — bas decide karo kaunsi .c file link karni hai. Wahi update_motor, do alag universes.

PICTURE. Ek door baayein; door ke peeche se do haath nikaalte hue — ek laal "silicon" haath aur ek green "mock" haath. Ek switch jis par likha hai "kaunsi .c link karoge?" ek ko select karta hai.

Figure — Software testing in embedded — unit tests on host, HIL testing

Step 4 — Tier 1 ban gaya: host unit test

KYA. Mock door ke saath, ek laptop test mock mein input likhta hai, real logic run karta hai, aur recorded output par assert karta hai — koi chip involved nahi.

void test_motor_pwm_cap(void) {
    mock_set_adc(ADC_CHANNEL_1, 4095);   // (a) max reading inject karo
    update_motor();                       // (b) REAL logic run karo
    assert(hal_pwm_get(PWM_CHANNEL_1) == 1000); // (c) cap check karo
}
  • 4095 ::: woh sabse bada value jo ek 12-bit ADC return karta hai, kyunki . Yeh maximum case hai.
  • assert(... == 1000) ::: check karta hai ki PWM apni ceiling (1000) par clamp hua, overshoot nahi kiya.

Yeh fast aur cheap kyun hai? Kyunki yeh sirf ek array par x86 arithmetic hai. Koi flashing nahi, koi wires nahi, koi oscilloscope nahi. Yeh microseconds mein run hota hai, toh tum har commit par thousands per second run kar sakte ho — yeh hai Continuous Integration for Embedded workflow.

Yeh KYA PAKADTA HAI: logic bugs — galat formulas, missing clamps, off-by-one, integer overflow, state-machine mistakes. Yeh KYA NAHI PAKAD SAKTA: time ya real electrons se related kuch bhi. Mock instantly aur perfectly return karta hai; real ADCs noisy aur slow hote hain.

PICTURE. Poora loop laptop side par drawn: test → mock input → logic → mock output → assert, saath mein ek stopwatch jis par "µs" likha hai.

Figure — Software testing in embedded — unit tests on host, HIL testing

Step 5 — Host tests ke baad jo gap reh jaata hai

KYA. Ek figure par, woh sab kuch list karo jo mock ne pretend karke hataa diya: instant reads, noise-free values, koi interrupt latency nahi, koi clock drift nahi, ek waqt mein ek peripheral.

Yeh step kyun hai. Agar hum Step 4 par ruk jaate toh hum aisi bugs ship karte jo sirf real hardware par aati. Mock ka sabse bada jhooth time hai: yeh zero time mein jawab deta hai, toh yeh kabhi nahi bata sakta ki tumhara interrupt apni deadline ke andar actually respond karta hai ya nahi.

Formally, ek real-time system ka ek promise hota hai: ek event ko ek fixed budget ke andar reaction produce karni chahiye.

  • ::: stimulus se response tak ka actual time, real silicon par measure kiya gaya.
  • ::: contract deadline (jaise ek interrupt ko ke andar ek pin fire karna chahiye).
  • Strict "" ::: deadline ek baar bhi miss karna failure hai; real-time ka matlab hai har case, average nahi.

Host test mein koi hota hi nahi — uski clock fake hai. Toh inequality host par unmeasurable hai. Wahi gap exactly woh hai jo Tier 2 fill karta hai.

PICTURE. Do columns: green "host dekh sakta hai" (math, logic, edge values) vs laal "host andha hai" (timing, noise, race conditions, multi-peripheral interaction).

Figure — Software testing in embedded — unit tests on host, HIL testing

Step 6 — Tier 2 ban gaya: Hardware-in-the-Loop (HIL)

KYA. Hum wahi firmware rakhte hain, lekin ab real HAL link karte hain aur board ko ek physical loop ke andar rakhte hain. Ek test controller duniya ka part play karta hai: woh fake sensor voltages andar feed karta hai aur real outputs wापस read karta hai.

HIL rig ke paanch pieces:

  1. DUTDevice Under Test: tumhara board production firmware run karta hua.
  2. Test Controller — ek PC jo test script run kar raha hai.
  3. Stimulus — ek DAC / signal generator jo analog voltages banata hai jo DUT ka ADC read karega.
  4. Capture — ek logic analyzer ya oscilloscope jo DUT ki pins read kar raha hai.
  5. Bus — UART / CAN jo commands aur telemetry carry karta hai.

Real loop kyun? Kyunki ab Step 5 ki har laal cheez actually present hai: real ADC noise, real interrupt latency, real clock. Controller Fault Injection Testing bhi kar sakta hai — ek voltage glitch karo, ek wire drop karo — failure paths test karne ke liye jo mock kabhi honestly mimic nahi kar sakta.

PICTURE. Closed loop: Controller → DAC → (DUT ADC → firmware → DUT PWM) → Analyzer → waapas Controller tak, DUT ke andar RTOS run karta hua.

Figure — Software testing in embedded — unit tests on host, HIL testing

Step 7 — HIL woh cheez measure karta hai jo host nahi kar sakta: real time

KYA. Ek change trigger karo aur analyzer se physical response ko timestamp karo.

def test_motor_response_time():
    start = time.perf_counter()
    rig.set_dac_voltage(ADC_THROTTLE, 0.0)     # command: throttle cut karo
    while rig.measure_pwm(PWM_MOTOR) > 5.0:     # real PWM ko girते देखो
        pass
    response = time.perf_counter() - start
    assert response < 0.010                      # 10 ms deadline
  • set_dac_voltage(..., 0.0) ::: stimulus mein "event".
  • measure_pwm(...) > 5.0 ::: real reaction ko ek threshold cross karte dekhna.
  • response < 0.010 ::: yeh literally Step 5 ka hai — ab measurable kyunki clock real hai.

Busy-wait loop kyun? Hum compute nahi kar sakte ki PWM kab drop hoga; RTOS scheduler, interrupts, aur I/O sab unpredictable delay add karte hain. Toh hum reality ko watch karte hain jab tak woh ho nahi jaata, aur use clock karte hain.

HIL ke edge cases jo cover hone chahiye (har ek apna test, yahan nahi dikhaya gaya): worst-case interrupt storm (sabse bada ), zero input (kya PWM fully off ho jaata hai?), max input (kya woh clamp karta hai jaise host ne kaha?), aur ek fault-injected sensor short.

PICTURE. Ek timeline: stimulus edge (yellow) par, response edge (green) par, deadline wall (laal) par — pass tab jab green, laal ke baayein land kare.

Figure — Software testing in embedded — unit tests on host, HIL testing

Step 8 — Sab kuch HIL mein kyun nahi karte? (degenerate strategy)

KYA. Sochो Tier 1 ko completely skip karo aur har logic branch ko hardware par test karo.

KYUN yeh fail hota hai. HIL slow hai (test per seconds), scarce hai (ek physical rig), aur debug karna mushkil hai (ek laal pin edge tumhe nahi bata sakta ki kaunsi line galat hai). Logic bugs wahaan push karo aur tumhara feedback loop collapse ho jaata hai.

Sahi division of labour:

Arrow ka matlab hai order: pehle saste bugs pakdo, precious rig time sirf wahaan spend karo jahan genuinely silicon chahiye.

PICTURE. Ek funnel: wide fast host stage zyaadatar bugs pakadta hai; narrow slow HIL stage timing/hardware ke kuch bugs pakadta hai. Cost-per-test baayein se daayein badhti hai.

Figure — Software testing in embedded — unit tests on host, HIL testing

Ek picture summary

Upar ki sab cheez ek canvas par: ek update_motor seam do doors mein split hoti hai; do builds un doors ka jawab dete hain; host tier microseconds mein logic test karta hai; HIL tier closed loop mein real time test karta hai; aur ek funnel bugs ko cheapest-first route karta hai.

Figure — Software testing in embedded — unit tests on host, HIL testing
Recall Feynman retelling — ise simple words mein kaho

Humne firmware ki teen lines se shuru kiya jahan ek calculation do hardware pokes ke beech glued thi, toh woh chip se bahar nahi ja sakti thi. Humne har poke ko ek naam diya (hal_...) — ek wall ki jagah ek door. Door ke peeche do log ho sakte hain: real silicon, ya ek fake jo hum control karte hain. Apne laptop par hum fake ko door ke peeche rakhte hain, apne code ko koi bhi input dete hain, real math run karte hain, aur jawab check karte hain — sab microseconds mein, hazaaron baar per second. Lekin fake time ke baare mein jhooth bolta hai: woh instantly jawab deta hai, toh yeh kabhi prove nahi kar sakta ki hamara interrupt apni deadline beat karta hai. Toh hum ek doosra setup banate hain — hardware-in-the-loop — jahan real firmware real board par run karta hai ek physical loop ke andar, ek machine sensors ka part play karti hai, aur ek logic analyzer actual response ko deadline ke against time karta hai. Host tests fast, cheap, aur logic bugs pakadne wale hain; HIL tests slow, real, aur timing aur hardware bugs pakadne wale hain. Hum host pehle karte hain, HIL baad mein — ek funnel jo expensive rig sirf wahaan spend karta hai jahan actually zaroorat hai.

Recall Quick checks

update_motor (Step 1) laptop par kyun nahi run kar sakta? ::: Yeh ADC1->DR aur TIM2->CCR1 read/write karta hai, jo memory addresses sirf MCU ke andar exist karti hain. Ek change kaunsa hai jo ise testable banata hai? ::: Hardware access ko named hal_ functions ke through route karo — ek fixed register ki jagah ek swap point (ek door). Mock kya replace karta hai, aur woh tumhe kya karne deta hai? ::: Yeh real hardware door ko ek plain array se replace karta hai; yoh tumhe inputs inject karne aur outputs inspect karne deta hai. Kaunsa tier measure kar sakta hai, aur kyun? ::: HIL — host ki clock mock dwara fake ki gayi hai (zero-time answers), toh sirf real silicon ek real timestamp deta hai. Saare tests HIL par kyun nahi run karte? ::: HIL slow, scarce, aur debug karna mushkil hai; tum fast feedback loop kho doge. Pehle saste host logic tests karo.