Skip to content

ShaheerSajid/pantompkins

Repository files navigation

Pantompkins ECG Feature Extraction (SystemVerilog)

Hardware implementation of a Pantompkins-style ECG processing pipeline with R-peak detection, RR estimation, and morphological feature extraction. The top-level module is pantompkins.sv.

Repository layout

  • rtl/ SystemVerilog RTL blocks (filters, R-peak detection, morph features/descriptor).
  • tb/ Testbench top wrapper.
  • simulation/ Simulation scripts and Makefile targets for Questa and Verilator.
  • synth/ Quartus synthesis scripts/targets.
  • utils/ Helper scripts (e.g., dataset conversion).
  • utils/verif/ Verification scripts for comparing R-peak outputs vs MIT-BIH annotations.
  • data/ Input datasets (e.g., MIT-BIH).
  • Makefile Top-level unified make targets.
  • requirements.txt Python dependencies for data conversion.

Top-level module

rtl/pantompkins.sv

Inputs:

  • clk System clock.
  • clk_enable Sample enable.
  • reset Active-high reset.
  • filter_in[11:0] ECG sample input.

Outputs:

  • morph_p_q8_23[31:0], morph_q_q8_23[31:0], morph_s_q8_23[31:0], morph_t_q8_23[31:0] Normalized morph descriptor outputs.
  • morph_r_time[31:0] R-peak time associated with the morph descriptor.
  • pre_rr_interval[31:0], post_rr_interval[31:0], local_rr_interval[31:0], global_rr_interval[31:0] RR interval features.
  • feat_valid Valid strobe when RR + morph outputs are aligned.

Internal pipeline (high level):

  1. Bandpass/filtering (filter)
  2. Differentiation (differentiator)
  3. Squaring
  4. Moving average (lowp6)
  5. MWI smoothing (MWI)
  6. R-peak detection (r_peak_detector_full)
  7. RR calculation (rr_hr_block)
  8. R-peak refinement (r_peak_align_stream)
  9. Morphological features (morph_features)
  10. Morphological descriptor (morph_descriptor)

Block diagram (dataflow)

ECG (12b) --> filter (FIR 181) --> differentiator (FIR 5) --> square
          --> moving avg (lowp6) --> MWI (FIR 51) --> r_peak_detector_full
                                                       |-> rr_hr_block
                                                       |-> r_peak_align_stream --> morph_features --> morph_descriptor

Module notes (what each block does)

  • rtl/filter.sv : 181-tap FIR bandpass/conditioning filter (MATLAB HDL Coder output).
  • rtl/differentiator.sv : 5-tap FIR differentiator.
  • rtl/lowp6.sv : moving average / smoothing over 23 samples (sum of input + 22 delays).
  • rtl/MWI.sv : 51-tap FIR used as MWI smoothing.
  • rtl/r_peak_detector_full.sv : adaptive thresholding, refractory, T-wave rejection, and search-back.
  • rtl/rr_hr_block.sv : RR interval calculation (assumes FS_HZ=360 by default).
  • rtl/r_peak_align_stream.sv : aligns detected R-peak with raw ECG using a delayed stream and a local search.
  • rtl/morph_features.sv : extracts P/Q/S/T times and amplitudes around each R-peak.
  • rtl/morph_descriptor.sv : normalizes R-peak location and amplitude within P/Q/R/S/T extrema.
  • rtl/int_div.sv : iterative integer divider used by morph_descriptor.

Key parameters and assumptions

  • Sample rate assumptions in control logic:
    • r_peak_detector_full uses constants for ~200 ms and ~2 s windows at 360 Hz.
    • rr_hr_block defaults to FS_HZ=360.
  • morph_features windows:
    • PRE_R_SAMPLES=43 (~120 ms at 360 Hz)
    • POST_R_SAMPLES=72 (~200 ms at 360 Hz)
    • SB_PRE_SAMPLES=PRE_R_SAMPLES+245 (extended pre-window for search-back)
  • r_peak_align_stream:
    • TOTAL_LATENCY controls alignment between processed stream and raw ECG.
    • Top-level sets TOTAL_LATENCY=220 in rtl/pantompkins.sv (sum of filter delays: 181+5+22+51).
    • Top-level sets SEARCH_BACK=9 and SEARCH_FWD=9 (±9 sample refinement window).

Timing and latency (practical guidance)

  • The pipeline is streaming and gated by clk_enable.
  • The filters have inherent group delay; the alignment stage uses TOTAL_LATENCY to compensate before morphological extraction.
  • R-peak detection operates on the MWI output; refined R-peaks are aligned back to raw ECG by r_peak_align_stream.
  • If you change filter lengths or sample rate, re-evaluate:
    • TOTAL_LATENCY in r_peak_align_stream
    • r_peak_detector_full timing constants (refractory, init window)
    • rr_hr_block.FS_HZ

Simulation

The top-level Makefile delegates to simulation/Makefile and also generates input data before simulation.

Questa:

make questa

Verilator:

make verilator

Examples:

make verilator TRACE=0
make verilator TRACE=1 RUN_FILE=data/100.txt
make verilator SAMPLES=5000

Notes:

  • simulation/src.args enumerates RTL sources.
  • The Verilator flow builds Vtb and runs it. If TRACE!=0 and RUN_FILE is set, GTKWave opens the matching VCD with simulation/sig.gtkw.
  • simulation/verilator/main.cpp reads all files in simulation/data/ unless RUN_FILE is set.
  • The Verilator run writes VCDs to simulation/trace/ and R-peak indices to simulation/rpeaks/.
  • TRACE=0 disables VCD dumping; RUN_FILE=<path> runs a single input file.
  • SAMPLES=<n> controls how many samples utils/cvt.py extracts into simulation/data/.

Quick start

  1. Ensure the required simulator is installed (Questa or Verilator + GTKWave).
  2. Run one of the make targets above.
  3. Inspect waveforms and R-peak outputs in the generated traces.

Data generation

Input files are generated by utils/cvt.py from MIT-BIH data under data/MIT-BIH. The simulation make targets run this automatically, but you can run it directly:

python3 utils/cvt.py --samples 20000

Python dependencies

Install dependencies for utils/cvt.py and utils/verif/check_ver.py:

pip install -r requirements.txt

Verification (MIT-BIH vs hardware rpeaks)

utils/verif/check_ver.py compares the simulation r-peak CSVs (simulation/rpeaks/*.csv) against MIT-BIH annotations and writes summary CSV/XLSX files.

Top-level make:

make verify

Simulation make:

make -C simulation verify

Overrides:

make verify VERIFY_ECG_DB=data/MIT-BIH VERIFY_RPEAKS=simulation/rpeaks VERIFY_RESULTS=simulation/results

Example stimuli format

Each input file in simulation/data/ should be plain text with one signed integer sample per line. Values are read as 12-bit signed ECG samples (the testbench drives filter_in[11:0]).

Example (simulation/data/example.txt):

12
15
18
20
17
12
8
5
3
1
-2
-6
-9
-7
-3
0
4
10
15

Expected outputs and waveforms

  • feat_valid pulses once per beat when RR and morph descriptors are aligned.
  • When feat_valid is high, the top-level outputs (morph_*, morph_r_time, and RR intervals) are valid for that beat.
  • Internally, you can probe:
    • r_peak_align_stream.r_peak_refined_valid / r_peak_refined for refined peak timing.
    • r_peak_detector_full.candidate_valid and candidate_time for raw detections.
    • rr_hr_block.rr_feat_valid with pre_rr_interval / post_rr_interval / local_rr_interval.
    • morph_features.valid with p/q/r/s/t time and amplitude outputs.
    • morph_descriptor.morph_done for descriptor-valid strobe.

Results

R-peak Detection — MIT-BIH Arrhythmia Database (48 records, 650,000 samples)

Evaluated against MIT-BIH annotations with ±50 ms tolerance.

Metric Value
Total beats 109,494
False Positives (FP) 780
False Negatives (FN) 768
Failed detections 1,548 (1.41%)
Sensitivity 99.30%
PPV (Precision) 99.29%
F1 Score 99.29%

Notable difficult records (>5% failed detection):

Record Beats Failed Failed (%) Notes
217 2,208 329 14.9% Bigeminy/trigeminy pacing artefacts
208 2,955 380 12.9% Mixed rhythms, frequent PVCs
207 1,860 196 10.5% Paced beats, bundle branch block
114 1,879 119 6.3% LBBB
102 2,187 138 6.3% Paced rhythm

N vs V SVM Classifier

Fixed-point hardware SVM (L1-RBF kernel) trained on the DS1/DS2 AAMI split from MIT-BIH. Features are extracted directly by the RTL pipeline and match the hardware exactly. RR features are normalised by global_rr (rolling 5-min mean), matching the hardware int_div.

Best configurationmorph_1, morph_2, pre_RR, post_RR (4 features):

Metric DS1 (train) DS2 (test)
Balanced accuracy 0.9797 0.9676
Recall V 0.9784 ✅ 0.9522 ✅
Precision V 0.8101 ✅ 0.8028 ✅
F1 V 0.8863 0.8711

AAMI targets (Sensitivity ≥ 75%, Positive Predictivity ≥ 75%) — both met on DS2.

SVM: L1-RBF kernel, C=0.5, gamma=0.30, weight_V=6 (threshold calibrated on DS1). Hardware: 822 support vectors — 3,288 MACs per beat.

To reproduce:

python -m utils.classification.train \
  --hw-C 0.5 --hw-gamma 0.30 --hw-weight-v 6 \
  --hw-features morph_1,morph_2,pre_RR,post_RR \
  --hw-train-n 99999 \
  --q-weight-scale 4096 \
  --hw-export

License

Add your preferred license information here.