Real-data co-simulation: range FFT, Doppler, full-chain all 2048/2048 exact match
ADI CN0566 Phaser 10.525 GHz X-band radar data validation: - golden_reference.py: bit-accurate Python model with range_bin_decimator - tb_range_fft_realdata.v: 1024/1024 exact match - tb_doppler_realdata.v: 2048/2048 exact match - tb_fullchain_realdata.v: decimator+Doppler 2048/2048 exact match - 19/19 FPGA regression unaffected
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,291 @@
|
|||||||
|
# AERIS-10 Golden Reference Detections
|
||||||
|
# Threshold: 10000
|
||||||
|
# Format: range_bin doppler_bin magnitude
|
||||||
|
0 0 44371
|
||||||
|
0 1 24165
|
||||||
|
0 31 17748
|
||||||
|
1 0 34391
|
||||||
|
1 1 17923
|
||||||
|
1 31 18610
|
||||||
|
2 0 28512
|
||||||
|
2 1 13818
|
||||||
|
2 31 15787
|
||||||
|
3 0 47402
|
||||||
|
3 1 25214
|
||||||
|
3 31 23504
|
||||||
|
4 0 51870
|
||||||
|
4 1 32733
|
||||||
|
4 31 31545
|
||||||
|
5 0 31752
|
||||||
|
5 1 13486
|
||||||
|
5 31 19300
|
||||||
|
6 0 63406
|
||||||
|
6 1 33383
|
||||||
|
6 31 36672
|
||||||
|
7 0 37576
|
||||||
|
7 1 21215
|
||||||
|
7 31 27773
|
||||||
|
8 0 14823
|
||||||
|
10 0 30062
|
||||||
|
10 1 13616
|
||||||
|
10 31 17149
|
||||||
|
11 0 65534
|
||||||
|
11 1 60963
|
||||||
|
11 2 14848
|
||||||
|
11 3 12082
|
||||||
|
11 4 18060
|
||||||
|
11 29 10045
|
||||||
|
11 30 20661
|
||||||
|
11 31 65536
|
||||||
|
12 0 65536
|
||||||
|
12 1 44569
|
||||||
|
12 4 11189
|
||||||
|
12 30 13936
|
||||||
|
12 31 57036
|
||||||
|
13 0 47038
|
||||||
|
13 1 40212
|
||||||
|
13 2 14655
|
||||||
|
13 4 10242
|
||||||
|
13 30 14945
|
||||||
|
13 31 40237
|
||||||
|
14 0 65534
|
||||||
|
14 1 43568
|
||||||
|
14 3 10974
|
||||||
|
14 4 11491
|
||||||
|
14 30 15272
|
||||||
|
14 31 57983
|
||||||
|
15 0 34501
|
||||||
|
15 1 22496
|
||||||
|
15 31 25197
|
||||||
|
16 0 32784
|
||||||
|
16 1 19309
|
||||||
|
16 31 14005
|
||||||
|
17 0 23063
|
||||||
|
17 1 13730
|
||||||
|
18 0 17087
|
||||||
|
18 1 12092
|
||||||
|
19 0 65535
|
||||||
|
19 1 49084
|
||||||
|
19 2 11399
|
||||||
|
19 30 13119
|
||||||
|
19 31 48411
|
||||||
|
20 0 65509
|
||||||
|
20 1 37881
|
||||||
|
20 31 35014
|
||||||
|
21 0 39614
|
||||||
|
21 1 23389
|
||||||
|
21 31 22417
|
||||||
|
22 0 27174
|
||||||
|
22 1 12577
|
||||||
|
22 31 15278
|
||||||
|
23 0 39885
|
||||||
|
23 1 29247
|
||||||
|
23 31 33561
|
||||||
|
24 0 29644
|
||||||
|
24 28 11071
|
||||||
|
24 31 20937
|
||||||
|
25 0 65535
|
||||||
|
25 1 54580
|
||||||
|
25 2 20278
|
||||||
|
25 30 20041
|
||||||
|
25 31 59445
|
||||||
|
26 0 58162
|
||||||
|
26 1 46544
|
||||||
|
26 2 17230
|
||||||
|
26 3 10127
|
||||||
|
26 31 44711
|
||||||
|
27 0 65535
|
||||||
|
27 1 65535
|
||||||
|
27 2 44599
|
||||||
|
27 3 17124
|
||||||
|
27 28 15139
|
||||||
|
27 29 26067
|
||||||
|
27 30 54631
|
||||||
|
27 31 65535
|
||||||
|
28 0 65535
|
||||||
|
28 1 65535
|
||||||
|
28 2 43056
|
||||||
|
28 3 14647
|
||||||
|
28 4 11808
|
||||||
|
28 29 15256
|
||||||
|
28 30 50518
|
||||||
|
28 31 65535
|
||||||
|
29 0 65535
|
||||||
|
29 1 61621
|
||||||
|
29 2 28859
|
||||||
|
29 3 19523
|
||||||
|
29 4 21765
|
||||||
|
29 5 12687
|
||||||
|
29 27 13175
|
||||||
|
29 28 19619
|
||||||
|
29 29 24365
|
||||||
|
29 30 48682
|
||||||
|
29 31 65535
|
||||||
|
30 0 55399
|
||||||
|
30 1 46683
|
||||||
|
30 2 21192
|
||||||
|
30 3 15905
|
||||||
|
30 4 18003
|
||||||
|
30 29 11105
|
||||||
|
30 30 22360
|
||||||
|
30 31 40830
|
||||||
|
31 0 46504
|
||||||
|
31 1 44346
|
||||||
|
31 2 34200
|
||||||
|
31 3 20677
|
||||||
|
31 4 18570
|
||||||
|
31 5 10430
|
||||||
|
31 29 12684
|
||||||
|
31 30 31778
|
||||||
|
31 31 36195
|
||||||
|
32 0 39540
|
||||||
|
32 1 36657
|
||||||
|
32 31 35394
|
||||||
|
33 0 35482
|
||||||
|
33 1 32886
|
||||||
|
33 2 15041
|
||||||
|
33 28 10103
|
||||||
|
33 29 11617
|
||||||
|
33 30 17465
|
||||||
|
33 31 34603
|
||||||
|
34 0 47950
|
||||||
|
34 1 25855
|
||||||
|
34 31 23198
|
||||||
|
35 0 65536
|
||||||
|
35 1 63059
|
||||||
|
35 2 24416
|
||||||
|
35 30 27412
|
||||||
|
35 31 65534
|
||||||
|
36 0 65535
|
||||||
|
36 1 41914
|
||||||
|
36 2 11341
|
||||||
|
36 30 11276
|
||||||
|
36 31 41419
|
||||||
|
38 0 63253
|
||||||
|
38 1 46689
|
||||||
|
38 2 13576
|
||||||
|
38 30 14208
|
||||||
|
38 31 49979
|
||||||
|
39 0 33480
|
||||||
|
39 1 25439
|
||||||
|
39 31 23094
|
||||||
|
40 0 52003
|
||||||
|
40 1 47059
|
||||||
|
40 2 13164
|
||||||
|
40 31 37992
|
||||||
|
41 0 65536
|
||||||
|
41 1 65534
|
||||||
|
41 2 25844
|
||||||
|
41 3 14580
|
||||||
|
41 4 12743
|
||||||
|
41 30 22231
|
||||||
|
41 31 65534
|
||||||
|
42 0 52097
|
||||||
|
42 1 45022
|
||||||
|
42 2 10317
|
||||||
|
42 28 11984
|
||||||
|
42 29 10182
|
||||||
|
42 30 13078
|
||||||
|
42 31 40477
|
||||||
|
43 0 61723
|
||||||
|
43 1 48104
|
||||||
|
43 2 17623
|
||||||
|
43 3 10105
|
||||||
|
43 28 28331
|
||||||
|
43 29 24102
|
||||||
|
43 31 45085
|
||||||
|
44 0 65535
|
||||||
|
44 1 65535
|
||||||
|
44 2 60795
|
||||||
|
44 3 25438
|
||||||
|
44 27 39330
|
||||||
|
44 28 60025
|
||||||
|
44 29 52445
|
||||||
|
44 30 35091
|
||||||
|
44 31 65535
|
||||||
|
45 0 65535
|
||||||
|
45 1 65535
|
||||||
|
45 2 27652
|
||||||
|
45 3 14416
|
||||||
|
45 4 10622
|
||||||
|
45 27 16323
|
||||||
|
45 28 40935
|
||||||
|
45 29 30694
|
||||||
|
45 30 29375
|
||||||
|
45 31 65535
|
||||||
|
46 0 65536
|
||||||
|
46 1 57696
|
||||||
|
46 2 14924
|
||||||
|
46 30 14433
|
||||||
|
46 31 45164
|
||||||
|
47 0 59141
|
||||||
|
47 1 44129
|
||||||
|
47 2 15305
|
||||||
|
47 28 13092
|
||||||
|
47 30 13754
|
||||||
|
47 31 47415
|
||||||
|
48 0 27722
|
||||||
|
48 1 13381
|
||||||
|
48 31 16907
|
||||||
|
49 0 51936
|
||||||
|
49 1 43775
|
||||||
|
49 2 13004
|
||||||
|
49 31 40023
|
||||||
|
50 0 45430
|
||||||
|
50 1 39187
|
||||||
|
50 2 15881
|
||||||
|
50 30 12925
|
||||||
|
50 31 38207
|
||||||
|
51 0 34026
|
||||||
|
51 1 33081
|
||||||
|
51 31 34429
|
||||||
|
52 0 34415
|
||||||
|
52 1 15408
|
||||||
|
52 31 19344
|
||||||
|
53 0 52351
|
||||||
|
53 1 42915
|
||||||
|
53 2 14442
|
||||||
|
53 30 13099
|
||||||
|
53 31 42143
|
||||||
|
54 0 62356
|
||||||
|
54 1 49279
|
||||||
|
54 2 15596
|
||||||
|
54 30 15478
|
||||||
|
54 31 46574
|
||||||
|
55 0 33829
|
||||||
|
55 1 15941
|
||||||
|
55 31 18110
|
||||||
|
56 0 65535
|
||||||
|
56 1 46926
|
||||||
|
56 2 11443
|
||||||
|
56 28 12373
|
||||||
|
56 29 12101
|
||||||
|
56 30 14660
|
||||||
|
56 31 53058
|
||||||
|
58 0 65535
|
||||||
|
58 1 56769
|
||||||
|
58 2 14110
|
||||||
|
58 28 12576
|
||||||
|
58 29 16059
|
||||||
|
58 30 18858
|
||||||
|
58 31 63517
|
||||||
|
59 0 30703
|
||||||
|
59 1 24206
|
||||||
|
59 28 17534
|
||||||
|
59 29 12652
|
||||||
|
60 0 35136
|
||||||
|
60 1 21277
|
||||||
|
60 31 25048
|
||||||
|
61 0 28692
|
||||||
|
61 1 11267
|
||||||
|
61 28 11881
|
||||||
|
61 31 17628
|
||||||
|
62 0 35795
|
||||||
|
62 1 18879
|
||||||
|
62 31 18083
|
||||||
|
63 0 65535
|
||||||
|
63 1 40428
|
||||||
|
63 28 11884
|
||||||
|
63 29 13271
|
||||||
|
63 30 14869
|
||||||
|
63 31 52574
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,189 @@
|
|||||||
|
# AERIS-10 Full-Chain Golden Reference Detections
|
||||||
|
# Threshold: 10000
|
||||||
|
# Format: range_bin doppler_bin magnitude
|
||||||
|
0 0 65534
|
||||||
|
0 1 59350
|
||||||
|
0 2 16748
|
||||||
|
0 4 18802
|
||||||
|
0 29 10539
|
||||||
|
0 30 18526
|
||||||
|
0 31 65536
|
||||||
|
1 0 65535
|
||||||
|
1 1 65535
|
||||||
|
1 2 37002
|
||||||
|
1 4 12412
|
||||||
|
1 5 14956
|
||||||
|
1 6 12586
|
||||||
|
1 7 11607
|
||||||
|
1 8 11379
|
||||||
|
1 24 11725
|
||||||
|
1 28 17218
|
||||||
|
1 29 32939
|
||||||
|
1 30 58888
|
||||||
|
1 31 65535
|
||||||
|
2 0 65535
|
||||||
|
2 1 65535
|
||||||
|
2 2 60795
|
||||||
|
2 3 25438
|
||||||
|
2 27 39330
|
||||||
|
2 28 60025
|
||||||
|
2 29 52445
|
||||||
|
2 30 35091
|
||||||
|
2 31 65535
|
||||||
|
3 0 65535
|
||||||
|
3 1 63297
|
||||||
|
3 2 32758
|
||||||
|
3 3 42197
|
||||||
|
3 4 35819
|
||||||
|
3 5 12663
|
||||||
|
3 7 19561
|
||||||
|
3 8 12012
|
||||||
|
3 12 13537
|
||||||
|
3 13 12879
|
||||||
|
3 19 10255
|
||||||
|
3 20 10129
|
||||||
|
3 24 17256
|
||||||
|
3 25 22733
|
||||||
|
3 26 10202
|
||||||
|
3 28 24061
|
||||||
|
3 29 19639
|
||||||
|
3 31 37328
|
||||||
|
4 0 46755
|
||||||
|
4 1 39569
|
||||||
|
4 2 12396
|
||||||
|
4 28 12471
|
||||||
|
4 29 12156
|
||||||
|
4 30 16659
|
||||||
|
4 31 40340
|
||||||
|
5 0 44089
|
||||||
|
5 1 23634
|
||||||
|
5 31 21331
|
||||||
|
6 0 48634
|
||||||
|
6 1 24635
|
||||||
|
6 31 25423
|
||||||
|
7 0 24477
|
||||||
|
7 1 14206
|
||||||
|
7 31 10955
|
||||||
|
8 0 41014
|
||||||
|
8 1 19527
|
||||||
|
8 31 21133
|
||||||
|
9 0 47277
|
||||||
|
9 1 28366
|
||||||
|
9 31 29936
|
||||||
|
10 0 47095
|
||||||
|
10 1 26150
|
||||||
|
10 31 24009
|
||||||
|
11 0 47384
|
||||||
|
11 1 25409
|
||||||
|
11 31 24250
|
||||||
|
12 0 24648
|
||||||
|
12 1 14298
|
||||||
|
12 31 13970
|
||||||
|
13 0 13062
|
||||||
|
15 0 10284
|
||||||
|
16 0 14267
|
||||||
|
17 0 16165
|
||||||
|
18 0 14235
|
||||||
|
18 31 12120
|
||||||
|
19 0 18006
|
||||||
|
19 1 14936
|
||||||
|
20 0 47569
|
||||||
|
20 1 33826
|
||||||
|
20 31 35752
|
||||||
|
21 0 47804
|
||||||
|
21 1 21420
|
||||||
|
21 31 30292
|
||||||
|
22 0 14968
|
||||||
|
26 0 16086
|
||||||
|
26 31 10462
|
||||||
|
30 0 16628
|
||||||
|
30 1 10044
|
||||||
|
38 0 23453
|
||||||
|
38 1 13989
|
||||||
|
38 31 10672
|
||||||
|
39 0 31656
|
||||||
|
39 1 17367
|
||||||
|
39 31 17314
|
||||||
|
40 0 19156
|
||||||
|
40 1 10817
|
||||||
|
40 31 10083
|
||||||
|
45 0 25385
|
||||||
|
45 1 11685
|
||||||
|
45 31 14673
|
||||||
|
46 0 12576
|
||||||
|
46 4 10141
|
||||||
|
46 28 12358
|
||||||
|
47 0 19657
|
||||||
|
47 31 15741
|
||||||
|
48 0 13189
|
||||||
|
48 1 10038
|
||||||
|
49 0 33747
|
||||||
|
49 1 16561
|
||||||
|
49 31 18910
|
||||||
|
50 0 20552
|
||||||
|
50 31 10843
|
||||||
|
51 0 20068
|
||||||
|
51 1 13887
|
||||||
|
51 4 10305
|
||||||
|
51 28 11339
|
||||||
|
53 28 10166
|
||||||
|
55 0 39891
|
||||||
|
55 1 17615
|
||||||
|
55 31 24898
|
||||||
|
56 0 62796
|
||||||
|
56 1 29788
|
||||||
|
56 31 38261
|
||||||
|
57 0 63585
|
||||||
|
57 1 59760
|
||||||
|
57 2 13027
|
||||||
|
57 3 43395
|
||||||
|
57 4 59148
|
||||||
|
57 5 31472
|
||||||
|
57 6 11913
|
||||||
|
57 7 13807
|
||||||
|
57 8 12132
|
||||||
|
57 16 14068
|
||||||
|
57 17 10379
|
||||||
|
57 24 15712
|
||||||
|
57 25 11076
|
||||||
|
57 26 14856
|
||||||
|
57 27 23468
|
||||||
|
57 28 38479
|
||||||
|
57 29 23078
|
||||||
|
57 30 17921
|
||||||
|
57 31 46558
|
||||||
|
58 0 54425
|
||||||
|
58 1 45222
|
||||||
|
58 2 11380
|
||||||
|
58 4 11700
|
||||||
|
58 29 12022
|
||||||
|
58 30 13911
|
||||||
|
58 31 45374
|
||||||
|
59 0 45581
|
||||||
|
59 1 31538
|
||||||
|
59 2 10481
|
||||||
|
59 31 34132
|
||||||
|
60 0 28622
|
||||||
|
60 1 12594
|
||||||
|
60 3 11799
|
||||||
|
60 4 13327
|
||||||
|
60 28 11737
|
||||||
|
60 29 11439
|
||||||
|
60 31 16902
|
||||||
|
61 0 28716
|
||||||
|
61 1 16605
|
||||||
|
61 31 15745
|
||||||
|
62 0 14151
|
||||||
|
62 1 15747
|
||||||
|
62 4 11738
|
||||||
|
62 5 12479
|
||||||
|
62 26 10789
|
||||||
|
62 27 16875
|
||||||
|
62 28 19372
|
||||||
|
62 29 16120
|
||||||
|
62 30 18215
|
||||||
|
62 31 10810
|
||||||
|
63 0 63651
|
||||||
|
63 1 35648
|
||||||
|
63 30 10692
|
||||||
|
63 31 38169
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,342 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tb_doppler_realdata.v
|
||||||
|
*
|
||||||
|
* Co-simulation testbench: feeds real ADI CN0566 radar data (post-range-FFT)
|
||||||
|
* through the Doppler processor RTL and compares output bit-for-bit against
|
||||||
|
* the Python golden reference (golden_reference.py).
|
||||||
|
*
|
||||||
|
* Stimulus: cosim/real_data/hex/doppler_input_realdata.hex
|
||||||
|
* (2048 x 32-bit packed {Q[31:16], I[15:0]}, chirp-major order)
|
||||||
|
* Expected: cosim/real_data/hex/doppler_ref_i.hex, doppler_ref_q.hex
|
||||||
|
* (2048 x 16-bit signed, range-major order: rbin0 x 32 doppler, ...)
|
||||||
|
*
|
||||||
|
* Pass criteria: ALL 2048 output bins match golden reference exactly.
|
||||||
|
*
|
||||||
|
* Compile:
|
||||||
|
* iverilog -Wall -DSIMULATION -g2012 \
|
||||||
|
* -o tb/tb_doppler_realdata.vvp \
|
||||||
|
* tb/tb_doppler_realdata.v doppler_processor.v xfft_32.v fft_engine.v
|
||||||
|
*
|
||||||
|
* Run from: 9_Firmware/9_2_FPGA/
|
||||||
|
* vvp tb/tb_doppler_realdata.vvp
|
||||||
|
*/
|
||||||
|
|
||||||
|
module tb_doppler_realdata;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// PARAMETERS
|
||||||
|
// ============================================================================
|
||||||
|
localparam CLK_PERIOD = 10.0; // 100 MHz
|
||||||
|
localparam DOPPLER_FFT = 32;
|
||||||
|
localparam RANGE_BINS = 64;
|
||||||
|
localparam CHIRPS = 32;
|
||||||
|
localparam TOTAL_INPUTS = CHIRPS * RANGE_BINS; // 2048
|
||||||
|
localparam TOTAL_OUTPUTS = RANGE_BINS * DOPPLER_FFT; // 2048
|
||||||
|
localparam MAX_CYCLES = 500_000; // Timeout: 5 ms at 100 MHz
|
||||||
|
|
||||||
|
// Error tolerance: 0 means exact match required.
|
||||||
|
localparam integer MAX_ERROR = 0;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// CLOCK AND RESET
|
||||||
|
// ============================================================================
|
||||||
|
reg clk;
|
||||||
|
reg reset_n;
|
||||||
|
|
||||||
|
initial clk = 0;
|
||||||
|
always #(CLK_PERIOD / 2) clk = ~clk;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DUT SIGNALS
|
||||||
|
// ============================================================================
|
||||||
|
reg [31:0] range_data;
|
||||||
|
reg data_valid;
|
||||||
|
reg new_chirp_frame;
|
||||||
|
wire [31:0] doppler_output;
|
||||||
|
wire doppler_valid;
|
||||||
|
wire [4:0] doppler_bin;
|
||||||
|
wire [5:0] range_bin;
|
||||||
|
wire processing_active;
|
||||||
|
wire frame_complete;
|
||||||
|
wire [3:0] dut_status;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DUT INSTANTIATION
|
||||||
|
// ============================================================================
|
||||||
|
doppler_processor_optimized dut (
|
||||||
|
.clk(clk),
|
||||||
|
.reset_n(reset_n),
|
||||||
|
.range_data(range_data),
|
||||||
|
.data_valid(data_valid),
|
||||||
|
.new_chirp_frame(new_chirp_frame),
|
||||||
|
.doppler_output(doppler_output),
|
||||||
|
.doppler_valid(doppler_valid),
|
||||||
|
.doppler_bin(doppler_bin),
|
||||||
|
.range_bin(range_bin),
|
||||||
|
.processing_active(processing_active),
|
||||||
|
.frame_complete(frame_complete),
|
||||||
|
.status(dut_status)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Internal DUT state (for debug)
|
||||||
|
wire [2:0] dut_state_w = dut.state;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// INPUT DATA MEMORY (loaded from hex file)
|
||||||
|
// ============================================================================
|
||||||
|
reg [31:0] input_mem [0:TOTAL_INPUTS-1];
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$readmemh("tb/cosim/real_data/hex/doppler_input_realdata.hex", input_mem);
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// REFERENCE DATA (loaded from hex files)
|
||||||
|
// ============================================================================
|
||||||
|
reg signed [15:0] ref_i [0:TOTAL_OUTPUTS-1];
|
||||||
|
reg signed [15:0] ref_q [0:TOTAL_OUTPUTS-1];
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$readmemh("tb/cosim/real_data/hex/doppler_ref_i.hex", ref_i);
|
||||||
|
$readmemh("tb/cosim/real_data/hex/doppler_ref_q.hex", ref_q);
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// OUTPUT CAPTURE
|
||||||
|
// ============================================================================
|
||||||
|
reg signed [15:0] cap_out_i [0:TOTAL_OUTPUTS-1];
|
||||||
|
reg signed [15:0] cap_out_q [0:TOTAL_OUTPUTS-1];
|
||||||
|
reg [5:0] cap_rbin [0:TOTAL_OUTPUTS-1];
|
||||||
|
reg [4:0] cap_dbin [0:TOTAL_OUTPUTS-1];
|
||||||
|
integer out_count;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// PASS / FAIL TRACKING
|
||||||
|
// ============================================================================
|
||||||
|
integer pass_count, fail_count, test_count;
|
||||||
|
|
||||||
|
task check;
|
||||||
|
input cond;
|
||||||
|
input [511:0] label;
|
||||||
|
begin
|
||||||
|
test_count = test_count + 1;
|
||||||
|
if (cond) begin
|
||||||
|
pass_count = pass_count + 1;
|
||||||
|
end else begin
|
||||||
|
$display(" [FAIL] %0s", label);
|
||||||
|
fail_count = fail_count + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// MAIN TEST SEQUENCE
|
||||||
|
// ============================================================================
|
||||||
|
integer i, cycle_count;
|
||||||
|
integer n_exact, n_within_tol;
|
||||||
|
integer max_err_i, max_err_q;
|
||||||
|
integer abs_diff_i, abs_diff_q;
|
||||||
|
reg signed [31:0] diff_i, diff_q;
|
||||||
|
integer mismatches_printed;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
// ---- Init ----
|
||||||
|
pass_count = 0;
|
||||||
|
fail_count = 0;
|
||||||
|
test_count = 0;
|
||||||
|
out_count = 0;
|
||||||
|
range_data = 0;
|
||||||
|
data_valid = 0;
|
||||||
|
new_chirp_frame = 0;
|
||||||
|
reset_n = 0;
|
||||||
|
|
||||||
|
// ---- Reset ----
|
||||||
|
#(CLK_PERIOD * 10);
|
||||||
|
reset_n = 1;
|
||||||
|
#(CLK_PERIOD * 5);
|
||||||
|
|
||||||
|
$display("============================================================");
|
||||||
|
$display(" Doppler Processor Real-Data Co-Simulation");
|
||||||
|
$display(" ADI CN0566 Phaser 10.525 GHz X-band FMCW");
|
||||||
|
$display(" Input: 32 chirps x 64 range bins (post-range-FFT)");
|
||||||
|
$display(" Expected: 64 range bins x 32 Doppler bins");
|
||||||
|
$display("============================================================");
|
||||||
|
|
||||||
|
// ---- Debug: check hex file loaded ----
|
||||||
|
$display(" input_mem[0] = %08h", input_mem[0]);
|
||||||
|
$display(" input_mem[1] = %08h", input_mem[1]);
|
||||||
|
$display(" input_mem[2047] = %08h", input_mem[2047]);
|
||||||
|
$display(" ref_i[0] = %04h, ref_q[0] = %04h", ref_i[0], ref_q[0]);
|
||||||
|
|
||||||
|
// ---- Check 1: DUT starts in IDLE ----
|
||||||
|
check(dut_state_w == 3'b000,
|
||||||
|
"DUT starts in S_IDLE after reset");
|
||||||
|
|
||||||
|
// ---- Pulse new_chirp_frame to start a new frame ----
|
||||||
|
@(posedge clk);
|
||||||
|
new_chirp_frame <= 1;
|
||||||
|
@(posedge clk);
|
||||||
|
@(posedge clk);
|
||||||
|
new_chirp_frame <= 0;
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
// ---- Feed input data (2048 samples, chirp-major) ----
|
||||||
|
$display("\n--- Feeding %0d input samples ---", TOTAL_INPUTS);
|
||||||
|
|
||||||
|
for (i = 0; i < TOTAL_INPUTS; i = i + 1) begin
|
||||||
|
@(posedge clk);
|
||||||
|
range_data <= input_mem[i];
|
||||||
|
data_valid <= 1;
|
||||||
|
if (i < 3 || i == TOTAL_INPUTS - 1) begin
|
||||||
|
$display(" [feed] i=%0d data=%08h state=%0d",
|
||||||
|
i, input_mem[i], dut_state_w);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
@(posedge clk);
|
||||||
|
data_valid <= 0;
|
||||||
|
range_data <= 0;
|
||||||
|
|
||||||
|
$display(" After feeding: state=%0d", dut_state_w);
|
||||||
|
|
||||||
|
// ---- Check 2: DUT should be processing ----
|
||||||
|
#(CLK_PERIOD * 5);
|
||||||
|
check(dut_state_w != 3'b000 && dut_state_w != 3'b001,
|
||||||
|
"DUT entered processing state after 2048 input samples");
|
||||||
|
|
||||||
|
// ---- Collect outputs ----
|
||||||
|
$display("\n--- Waiting for %0d output samples ---", TOTAL_OUTPUTS);
|
||||||
|
|
||||||
|
cycle_count = 0;
|
||||||
|
while (out_count < TOTAL_OUTPUTS && cycle_count < MAX_CYCLES) begin
|
||||||
|
@(posedge clk);
|
||||||
|
cycle_count = cycle_count + 1;
|
||||||
|
|
||||||
|
if (doppler_valid) begin
|
||||||
|
cap_out_i[out_count] = doppler_output[15:0];
|
||||||
|
cap_out_q[out_count] = doppler_output[31:16];
|
||||||
|
cap_rbin[out_count] = range_bin;
|
||||||
|
cap_dbin[out_count] = doppler_bin;
|
||||||
|
out_count = out_count + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
$display(" Collected %0d output samples in %0d cycles", out_count,
|
||||||
|
cycle_count);
|
||||||
|
|
||||||
|
// ---- Check 3: Correct output count ----
|
||||||
|
check(out_count == TOTAL_OUTPUTS,
|
||||||
|
"Output sample count == 2048");
|
||||||
|
|
||||||
|
// ---- Check 4: Did not timeout ----
|
||||||
|
check(cycle_count < MAX_CYCLES,
|
||||||
|
"Processing completed within timeout");
|
||||||
|
|
||||||
|
// ---- Check 5: DUT returns to IDLE ----
|
||||||
|
#(CLK_PERIOD * 20);
|
||||||
|
check(dut_state_w == 3'b000,
|
||||||
|
"DUT returned to S_IDLE after processing");
|
||||||
|
|
||||||
|
// ---- Check 6: Output ordering ----
|
||||||
|
if (out_count > 0) begin
|
||||||
|
check(cap_rbin[0] == 0 && cap_dbin[0] == 0,
|
||||||
|
"First output: range_bin=0, doppler_bin=0");
|
||||||
|
end
|
||||||
|
if (out_count == TOTAL_OUTPUTS) begin
|
||||||
|
check(cap_rbin[TOTAL_OUTPUTS-1] == RANGE_BINS - 1,
|
||||||
|
"Last output: range_bin=63");
|
||||||
|
check(cap_dbin[TOTAL_OUTPUTS-1] == DOPPLER_FFT - 1,
|
||||||
|
"Last output: doppler_bin=31");
|
||||||
|
end
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
// BIT-FOR-BIT COMPARISON against golden reference
|
||||||
|
// ==================================================================
|
||||||
|
$display("");
|
||||||
|
$display("--- Comparing RTL output vs Python golden reference ---");
|
||||||
|
|
||||||
|
max_err_i = 0;
|
||||||
|
max_err_q = 0;
|
||||||
|
n_exact = 0;
|
||||||
|
n_within_tol = 0;
|
||||||
|
mismatches_printed = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < out_count; i = i + 1) begin
|
||||||
|
diff_i = cap_out_i[i] - ref_i[i];
|
||||||
|
diff_q = cap_out_q[i] - ref_q[i];
|
||||||
|
|
||||||
|
// Absolute value
|
||||||
|
abs_diff_i = (diff_i < 0) ? -diff_i : diff_i;
|
||||||
|
abs_diff_q = (diff_q < 0) ? -diff_q : diff_q;
|
||||||
|
|
||||||
|
if (abs_diff_i > max_err_i) max_err_i = abs_diff_i;
|
||||||
|
if (abs_diff_q > max_err_q) max_err_q = abs_diff_q;
|
||||||
|
|
||||||
|
if (diff_i == 0 && diff_q == 0)
|
||||||
|
n_exact = n_exact + 1;
|
||||||
|
|
||||||
|
if (abs_diff_i <= MAX_ERROR && abs_diff_q <= MAX_ERROR)
|
||||||
|
n_within_tol = n_within_tol + 1;
|
||||||
|
|
||||||
|
// Print first 20 mismatches for debug
|
||||||
|
if ((abs_diff_i > MAX_ERROR || abs_diff_q > MAX_ERROR) &&
|
||||||
|
mismatches_printed < 20) begin
|
||||||
|
$display(" [%4d] rbin=%2d dbin=%2d RTL=(%6d,%6d) REF=(%6d,%6d) ERR=(%4d,%4d)",
|
||||||
|
i, cap_rbin[i], cap_dbin[i],
|
||||||
|
$signed(cap_out_i[i]), $signed(cap_out_q[i]),
|
||||||
|
$signed(ref_i[i]), $signed(ref_q[i]),
|
||||||
|
diff_i, diff_q);
|
||||||
|
mismatches_printed = mismatches_printed + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Per-sample pass/fail
|
||||||
|
for (i = 0; i < out_count; i = i + 1) begin
|
||||||
|
diff_i = cap_out_i[i] - ref_i[i];
|
||||||
|
diff_q = cap_out_q[i] - ref_q[i];
|
||||||
|
abs_diff_i = (diff_i < 0) ? -diff_i : diff_i;
|
||||||
|
abs_diff_q = (diff_q < 0) ? -diff_q : diff_q;
|
||||||
|
check(abs_diff_i <= MAX_ERROR && abs_diff_q <= MAX_ERROR,
|
||||||
|
"Doppler output bin match");
|
||||||
|
end
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
// SUMMARY
|
||||||
|
// ==================================================================
|
||||||
|
$display("");
|
||||||
|
$display("============================================================");
|
||||||
|
$display(" SUMMARY: Doppler Processor Real-Data Co-Simulation");
|
||||||
|
$display("============================================================");
|
||||||
|
$display(" Total output bins: %0d", out_count);
|
||||||
|
$display(" Exact match: %0d / %0d", n_exact, out_count);
|
||||||
|
$display(" Within tolerance: %0d / %0d (tol=%0d)", n_within_tol, out_count, MAX_ERROR);
|
||||||
|
$display(" Max error (I): %0d", max_err_i);
|
||||||
|
$display(" Max error (Q): %0d", max_err_q);
|
||||||
|
$display(" Structural checks: %0d", 7); // checks 1-7 above
|
||||||
|
$display(" Data match checks: %0d", out_count);
|
||||||
|
$display(" Pass: %0d Fail: %0d", pass_count, fail_count);
|
||||||
|
$display("============================================================");
|
||||||
|
|
||||||
|
if (fail_count == 0)
|
||||||
|
$display("RESULT: ALL TESTS PASSED (%0d/%0d)", pass_count, test_count);
|
||||||
|
else
|
||||||
|
$display("RESULT: %0d TESTS FAILED", fail_count);
|
||||||
|
|
||||||
|
$display("============================================================");
|
||||||
|
|
||||||
|
#(CLK_PERIOD * 10);
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// WATCHDOG
|
||||||
|
// ============================================================================
|
||||||
|
initial begin
|
||||||
|
#(CLK_PERIOD * MAX_CYCLES * 2);
|
||||||
|
$display("[TIMEOUT] Simulation exceeded %0d cycles — aborting", MAX_CYCLES * 2);
|
||||||
|
$display("SOME TESTS FAILED");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
@@ -0,0 +1,462 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tb_fullchain_realdata.v
|
||||||
|
*
|
||||||
|
* Full-chain co-simulation testbench: feeds real ADI CN0566 radar data
|
||||||
|
* (post-range-FFT, 32 chirps x 1024 bins) through:
|
||||||
|
*
|
||||||
|
* range_bin_decimator (peak detection, 1024→64)
|
||||||
|
* → doppler_processor_optimized (Hamming + 32-pt FFT)
|
||||||
|
*
|
||||||
|
* and compares the Doppler output bit-for-bit against the Python golden
|
||||||
|
* reference that models the same chain (golden_reference.py).
|
||||||
|
*
|
||||||
|
* Stimulus:
|
||||||
|
* tb/cosim/real_data/hex/fullchain_range_input.hex
|
||||||
|
* 32768 x 32-bit packed {Q[31:16], I[15:0]} — 32 chirps x 1024 bins
|
||||||
|
*
|
||||||
|
* Expected:
|
||||||
|
* tb/cosim/real_data/hex/fullchain_doppler_ref_i.hex
|
||||||
|
* tb/cosim/real_data/hex/fullchain_doppler_ref_q.hex
|
||||||
|
* 2048 x 16-bit signed — 64 range bins x 32 Doppler bins
|
||||||
|
*
|
||||||
|
* Pass criteria: ALL 2048 Doppler output bins match exactly.
|
||||||
|
*
|
||||||
|
* Compile:
|
||||||
|
* iverilog -Wall -DSIMULATION -g2012 \
|
||||||
|
* -o tb/tb_fullchain_realdata.vvp \
|
||||||
|
* tb/tb_fullchain_realdata.v \
|
||||||
|
* range_bin_decimator.v doppler_processor.v xfft_32.v fft_engine.v
|
||||||
|
*
|
||||||
|
* Run from: 9_Firmware/9_2_FPGA/
|
||||||
|
* vvp tb/tb_fullchain_realdata.vvp
|
||||||
|
*/
|
||||||
|
|
||||||
|
module tb_fullchain_realdata;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// PARAMETERS
|
||||||
|
// ============================================================================
|
||||||
|
localparam CLK_PERIOD = 10.0; // 100 MHz
|
||||||
|
localparam DOPPLER_FFT = 32;
|
||||||
|
localparam RANGE_BINS = 64;
|
||||||
|
localparam CHIRPS = 32;
|
||||||
|
localparam INPUT_BINS = 1024;
|
||||||
|
localparam DECIM_FACTOR = 16;
|
||||||
|
|
||||||
|
localparam TOTAL_INPUT_SAMPLES = CHIRPS * INPUT_BINS; // 32768
|
||||||
|
localparam TOTAL_OUTPUT_SAMPLES = RANGE_BINS * DOPPLER_FFT; // 2048
|
||||||
|
localparam SAMPLES_PER_CHIRP = INPUT_BINS; // 1024
|
||||||
|
localparam DECIM_PER_CHIRP = RANGE_BINS; // 64
|
||||||
|
|
||||||
|
// Generous timeout: decimator + Doppler processing + margin
|
||||||
|
localparam MAX_CYCLES = 2_000_000;
|
||||||
|
|
||||||
|
// Error tolerance: 0 = exact match required
|
||||||
|
localparam integer MAX_ERROR = 0;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// CLOCK AND RESET
|
||||||
|
// ============================================================================
|
||||||
|
reg clk;
|
||||||
|
reg reset_n;
|
||||||
|
|
||||||
|
initial clk = 0;
|
||||||
|
always #(CLK_PERIOD / 2) clk = ~clk;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DECIMATOR SIGNALS
|
||||||
|
// ============================================================================
|
||||||
|
reg signed [15:0] decim_i_in;
|
||||||
|
reg signed [15:0] decim_q_in;
|
||||||
|
reg decim_valid_in;
|
||||||
|
|
||||||
|
wire signed [15:0] decim_i_out;
|
||||||
|
wire signed [15:0] decim_q_out;
|
||||||
|
wire decim_valid_out;
|
||||||
|
wire [5:0] decim_bin_index;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DOPPLER SIGNALS
|
||||||
|
// ============================================================================
|
||||||
|
// Wire decimator output directly into Doppler input (matching RTL)
|
||||||
|
wire [31:0] range_data_32bit;
|
||||||
|
wire range_data_valid;
|
||||||
|
|
||||||
|
assign range_data_32bit = {decim_q_out, decim_i_out};
|
||||||
|
assign range_data_valid = decim_valid_out;
|
||||||
|
|
||||||
|
reg new_chirp_frame;
|
||||||
|
|
||||||
|
wire [31:0] doppler_output;
|
||||||
|
wire doppler_valid;
|
||||||
|
wire [4:0] doppler_bin;
|
||||||
|
wire [5:0] range_bin;
|
||||||
|
wire processing_active;
|
||||||
|
wire frame_complete;
|
||||||
|
wire [3:0] dut_status;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DUT INSTANTIATION: Range Bin Decimator
|
||||||
|
// ============================================================================
|
||||||
|
range_bin_decimator #(
|
||||||
|
.INPUT_BINS(INPUT_BINS),
|
||||||
|
.OUTPUT_BINS(RANGE_BINS),
|
||||||
|
.DECIMATION_FACTOR(DECIM_FACTOR)
|
||||||
|
) range_decim (
|
||||||
|
.clk(clk),
|
||||||
|
.reset_n(reset_n),
|
||||||
|
.range_i_in(decim_i_in),
|
||||||
|
.range_q_in(decim_q_in),
|
||||||
|
.range_valid_in(decim_valid_in),
|
||||||
|
.range_i_out(decim_i_out),
|
||||||
|
.range_q_out(decim_q_out),
|
||||||
|
.range_valid_out(decim_valid_out),
|
||||||
|
.range_bin_index(decim_bin_index),
|
||||||
|
.decimation_mode(2'b01), // Peak detection mode
|
||||||
|
.start_bin(10'd0)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DUT INSTANTIATION: Doppler Processor
|
||||||
|
// ============================================================================
|
||||||
|
doppler_processor_optimized doppler_proc (
|
||||||
|
.clk(clk),
|
||||||
|
.reset_n(reset_n),
|
||||||
|
.range_data(range_data_32bit),
|
||||||
|
.data_valid(range_data_valid),
|
||||||
|
.new_chirp_frame(new_chirp_frame),
|
||||||
|
.doppler_output(doppler_output),
|
||||||
|
.doppler_valid(doppler_valid),
|
||||||
|
.doppler_bin(doppler_bin),
|
||||||
|
.range_bin(range_bin),
|
||||||
|
.processing_active(processing_active),
|
||||||
|
.frame_complete(frame_complete),
|
||||||
|
.status(dut_status)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Internal DUT state (for debug)
|
||||||
|
wire [2:0] decim_state = range_decim.state;
|
||||||
|
wire [2:0] doppler_state = doppler_proc.state;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// INPUT DATA MEMORY (loaded from hex file)
|
||||||
|
// ============================================================================
|
||||||
|
// 32768 x 32-bit packed {Q[31:16], I[15:0]}
|
||||||
|
reg [31:0] input_mem [0:TOTAL_INPUT_SAMPLES-1];
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$readmemh("tb/cosim/real_data/hex/fullchain_range_input.hex", input_mem);
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// REFERENCE DATA (loaded from hex files)
|
||||||
|
// ============================================================================
|
||||||
|
reg signed [15:0] ref_i [0:TOTAL_OUTPUT_SAMPLES-1];
|
||||||
|
reg signed [15:0] ref_q [0:TOTAL_OUTPUT_SAMPLES-1];
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
$readmemh("tb/cosim/real_data/hex/fullchain_doppler_ref_i.hex", ref_i);
|
||||||
|
$readmemh("tb/cosim/real_data/hex/fullchain_doppler_ref_q.hex", ref_q);
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DECIMATOR OUTPUT CAPTURE (for debug)
|
||||||
|
// ============================================================================
|
||||||
|
integer decim_out_count;
|
||||||
|
reg signed [15:0] decim_cap_i [0:CHIRPS*RANGE_BINS-1];
|
||||||
|
reg signed [15:0] decim_cap_q [0:CHIRPS*RANGE_BINS-1];
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DOPPLER OUTPUT CAPTURE
|
||||||
|
// ============================================================================
|
||||||
|
reg signed [15:0] cap_out_i [0:TOTAL_OUTPUT_SAMPLES-1];
|
||||||
|
reg signed [15:0] cap_out_q [0:TOTAL_OUTPUT_SAMPLES-1];
|
||||||
|
reg [5:0] cap_rbin [0:TOTAL_OUTPUT_SAMPLES-1];
|
||||||
|
reg [4:0] cap_dbin [0:TOTAL_OUTPUT_SAMPLES-1];
|
||||||
|
integer out_count;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// PASS / FAIL TRACKING
|
||||||
|
// ============================================================================
|
||||||
|
integer pass_count, fail_count, test_count;
|
||||||
|
|
||||||
|
task check;
|
||||||
|
input cond;
|
||||||
|
input [511:0] label;
|
||||||
|
begin
|
||||||
|
test_count = test_count + 1;
|
||||||
|
if (cond) begin
|
||||||
|
pass_count = pass_count + 1;
|
||||||
|
end else begin
|
||||||
|
$display(" [FAIL] %0s", label);
|
||||||
|
fail_count = fail_count + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// COUNT DECIMATOR OUTPUTS (always block)
|
||||||
|
// ============================================================================
|
||||||
|
always @(posedge clk or negedge reset_n) begin
|
||||||
|
if (!reset_n) begin
|
||||||
|
decim_out_count <= 0;
|
||||||
|
end else if (decim_valid_out) begin
|
||||||
|
if (decim_out_count < CHIRPS * RANGE_BINS) begin
|
||||||
|
decim_cap_i[decim_out_count] <= decim_i_out;
|
||||||
|
decim_cap_q[decim_out_count] <= decim_q_out;
|
||||||
|
end
|
||||||
|
decim_out_count <= decim_out_count + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// MAIN TEST SEQUENCE
|
||||||
|
// ============================================================================
|
||||||
|
integer i, chirp, sample_idx, cycle_count;
|
||||||
|
integer n_exact, n_within_tol;
|
||||||
|
integer max_err_i, max_err_q;
|
||||||
|
integer abs_diff_i, abs_diff_q;
|
||||||
|
reg signed [31:0] diff_i, diff_q;
|
||||||
|
integer mismatches_printed;
|
||||||
|
reg [31:0] packed_iq;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
// ---- Init ----
|
||||||
|
pass_count = 0;
|
||||||
|
fail_count = 0;
|
||||||
|
test_count = 0;
|
||||||
|
out_count = 0;
|
||||||
|
decim_i_in = 0;
|
||||||
|
decim_q_in = 0;
|
||||||
|
decim_valid_in = 0;
|
||||||
|
new_chirp_frame = 0;
|
||||||
|
reset_n = 0;
|
||||||
|
|
||||||
|
// ---- Reset ----
|
||||||
|
#(CLK_PERIOD * 10);
|
||||||
|
reset_n = 1;
|
||||||
|
#(CLK_PERIOD * 5);
|
||||||
|
|
||||||
|
$display("============================================================");
|
||||||
|
$display(" Full-Chain Real-Data Co-Simulation");
|
||||||
|
$display(" range_bin_decimator (peak, 1024->64)");
|
||||||
|
$display(" -> doppler_processor_optimized (Hamming + 32-pt FFT)");
|
||||||
|
$display(" ADI CN0566 Phaser 10.525 GHz X-band FMCW");
|
||||||
|
$display(" Input: %0d chirps x %0d range FFT bins = %0d samples",
|
||||||
|
CHIRPS, INPUT_BINS, TOTAL_INPUT_SAMPLES);
|
||||||
|
$display(" Expected: %0d range bins x %0d Doppler bins = %0d outputs",
|
||||||
|
RANGE_BINS, DOPPLER_FFT, TOTAL_OUTPUT_SAMPLES);
|
||||||
|
$display("============================================================");
|
||||||
|
|
||||||
|
// ---- Debug: check hex files loaded ----
|
||||||
|
$display(" input_mem[0] = %08h", input_mem[0]);
|
||||||
|
$display(" input_mem[1023] = %08h", input_mem[1023]);
|
||||||
|
$display(" input_mem[32767] = %08h", input_mem[32767]);
|
||||||
|
$display(" ref_i[0] = %04h, ref_q[0] = %04h", ref_i[0], ref_q[0]);
|
||||||
|
|
||||||
|
// ---- Check 1: Both DUTs start in IDLE ----
|
||||||
|
check(decim_state == 3'd0,
|
||||||
|
"Decimator starts in ST_IDLE after reset");
|
||||||
|
check(doppler_state == 3'b000,
|
||||||
|
"Doppler starts in S_IDLE after reset");
|
||||||
|
|
||||||
|
// ---- Pulse new_chirp_frame to start Doppler accumulation ----
|
||||||
|
@(posedge clk);
|
||||||
|
new_chirp_frame <= 1;
|
||||||
|
@(posedge clk);
|
||||||
|
@(posedge clk);
|
||||||
|
new_chirp_frame <= 0;
|
||||||
|
@(posedge clk);
|
||||||
|
|
||||||
|
// ---- Feed input data: 32 chirps x 1024 range bins ----
|
||||||
|
// Each chirp is 1024 consecutive samples. Between chirps, the
|
||||||
|
// decimator completes (ST_DONE → ST_IDLE) and restarts on the
|
||||||
|
// next valid input.
|
||||||
|
$display("\n--- Feeding %0d chirps x %0d bins = %0d samples ---",
|
||||||
|
CHIRPS, INPUT_BINS, TOTAL_INPUT_SAMPLES);
|
||||||
|
|
||||||
|
for (chirp = 0; chirp < CHIRPS; chirp = chirp + 1) begin
|
||||||
|
// Feed 1024 range bins for this chirp
|
||||||
|
for (i = 0; i < INPUT_BINS; i = i + 1) begin
|
||||||
|
@(posedge clk);
|
||||||
|
sample_idx = chirp * INPUT_BINS + i;
|
||||||
|
packed_iq = input_mem[sample_idx];
|
||||||
|
decim_i_in <= packed_iq[15:0];
|
||||||
|
decim_q_in <= packed_iq[31:16];
|
||||||
|
decim_valid_in <= 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Deassert valid after each chirp to let decimator finish
|
||||||
|
// (ST_PROCESS → ST_EMIT → ... → ST_DONE → ST_IDLE)
|
||||||
|
@(posedge clk);
|
||||||
|
decim_valid_in <= 0;
|
||||||
|
decim_i_in <= 0;
|
||||||
|
decim_q_in <= 0;
|
||||||
|
|
||||||
|
// Wait for decimator to return to IDLE
|
||||||
|
// The decimator needs a few cycles for the last EMIT + DONE
|
||||||
|
cycle_count = 0;
|
||||||
|
while (decim_state != 3'd0 && cycle_count < 200) begin
|
||||||
|
@(posedge clk);
|
||||||
|
cycle_count = cycle_count + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (chirp < 3 || chirp == CHIRPS - 1) begin
|
||||||
|
$display(" Chirp %0d: IDLE after %0d extra cycles, decim_out=%0d",
|
||||||
|
chirp, cycle_count, decim_out_count);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
$display(" All input fed. Total decimator outputs: %0d (expected %0d)",
|
||||||
|
decim_out_count, CHIRPS * RANGE_BINS);
|
||||||
|
|
||||||
|
// ---- Check 3: Decimator produced correct number of outputs ----
|
||||||
|
check(decim_out_count == CHIRPS * RANGE_BINS,
|
||||||
|
"Decimator output count == 2048");
|
||||||
|
|
||||||
|
// ---- Wait for Doppler processing to complete ----
|
||||||
|
$display("\n--- Waiting for Doppler to process and emit %0d outputs ---",
|
||||||
|
TOTAL_OUTPUT_SAMPLES);
|
||||||
|
|
||||||
|
cycle_count = 0;
|
||||||
|
while (out_count < TOTAL_OUTPUT_SAMPLES && cycle_count < MAX_CYCLES) begin
|
||||||
|
@(posedge clk);
|
||||||
|
cycle_count = cycle_count + 1;
|
||||||
|
|
||||||
|
if (doppler_valid) begin
|
||||||
|
cap_out_i[out_count] = doppler_output[15:0];
|
||||||
|
cap_out_q[out_count] = doppler_output[31:16];
|
||||||
|
cap_rbin[out_count] = range_bin;
|
||||||
|
cap_dbin[out_count] = doppler_bin;
|
||||||
|
out_count = out_count + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
$display(" Collected %0d Doppler outputs in %0d cycles", out_count,
|
||||||
|
cycle_count);
|
||||||
|
|
||||||
|
// ---- Check 4: Correct Doppler output count ----
|
||||||
|
check(out_count == TOTAL_OUTPUT_SAMPLES,
|
||||||
|
"Doppler output count == 2048");
|
||||||
|
|
||||||
|
// ---- Check 5: Did not timeout ----
|
||||||
|
check(cycle_count < MAX_CYCLES,
|
||||||
|
"Processing completed within timeout");
|
||||||
|
|
||||||
|
// ---- Check 6: Doppler returns to IDLE ----
|
||||||
|
#(CLK_PERIOD * 20);
|
||||||
|
check(doppler_state == 3'b000,
|
||||||
|
"Doppler returned to S_IDLE after processing");
|
||||||
|
|
||||||
|
// ---- Check 7: Output ordering ----
|
||||||
|
if (out_count > 0) begin
|
||||||
|
check(cap_rbin[0] == 0 && cap_dbin[0] == 0,
|
||||||
|
"First output: range_bin=0, doppler_bin=0");
|
||||||
|
end
|
||||||
|
if (out_count == TOTAL_OUTPUT_SAMPLES) begin
|
||||||
|
check(cap_rbin[TOTAL_OUTPUT_SAMPLES-1] == RANGE_BINS - 1,
|
||||||
|
"Last output: range_bin=63");
|
||||||
|
check(cap_dbin[TOTAL_OUTPUT_SAMPLES-1] == DOPPLER_FFT - 1,
|
||||||
|
"Last output: doppler_bin=31");
|
||||||
|
end
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
// BIT-FOR-BIT COMPARISON against golden reference
|
||||||
|
// ==================================================================
|
||||||
|
$display("");
|
||||||
|
$display("--- Comparing Doppler RTL output vs Python golden reference ---");
|
||||||
|
$display(" (full-chain: range FFT -> decimator -> Doppler)");
|
||||||
|
|
||||||
|
max_err_i = 0;
|
||||||
|
max_err_q = 0;
|
||||||
|
n_exact = 0;
|
||||||
|
n_within_tol = 0;
|
||||||
|
mismatches_printed = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < out_count; i = i + 1) begin
|
||||||
|
diff_i = cap_out_i[i] - ref_i[i];
|
||||||
|
diff_q = cap_out_q[i] - ref_q[i];
|
||||||
|
|
||||||
|
// Absolute value
|
||||||
|
abs_diff_i = (diff_i < 0) ? -diff_i : diff_i;
|
||||||
|
abs_diff_q = (diff_q < 0) ? -diff_q : diff_q;
|
||||||
|
|
||||||
|
if (abs_diff_i > max_err_i) max_err_i = abs_diff_i;
|
||||||
|
if (abs_diff_q > max_err_q) max_err_q = abs_diff_q;
|
||||||
|
|
||||||
|
if (diff_i == 0 && diff_q == 0)
|
||||||
|
n_exact = n_exact + 1;
|
||||||
|
|
||||||
|
if (abs_diff_i <= MAX_ERROR && abs_diff_q <= MAX_ERROR)
|
||||||
|
n_within_tol = n_within_tol + 1;
|
||||||
|
|
||||||
|
// Print first 20 mismatches for debug
|
||||||
|
if ((abs_diff_i > MAX_ERROR || abs_diff_q > MAX_ERROR) &&
|
||||||
|
mismatches_printed < 20) begin
|
||||||
|
$display(" [%4d] rbin=%2d dbin=%2d RTL=(%6d,%6d) REF=(%6d,%6d) ERR=(%4d,%4d)",
|
||||||
|
i, cap_rbin[i], cap_dbin[i],
|
||||||
|
$signed(cap_out_i[i]), $signed(cap_out_q[i]),
|
||||||
|
$signed(ref_i[i]), $signed(ref_q[i]),
|
||||||
|
diff_i, diff_q);
|
||||||
|
mismatches_printed = mismatches_printed + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Per-sample pass/fail check
|
||||||
|
for (i = 0; i < out_count; i = i + 1) begin
|
||||||
|
diff_i = cap_out_i[i] - ref_i[i];
|
||||||
|
diff_q = cap_out_q[i] - ref_q[i];
|
||||||
|
abs_diff_i = (diff_i < 0) ? -diff_i : diff_i;
|
||||||
|
abs_diff_q = (diff_q < 0) ? -diff_q : diff_q;
|
||||||
|
check(abs_diff_i <= MAX_ERROR && abs_diff_q <= MAX_ERROR,
|
||||||
|
"Full-chain Doppler output bin match");
|
||||||
|
end
|
||||||
|
|
||||||
|
// ==================================================================
|
||||||
|
// SUMMARY
|
||||||
|
// ==================================================================
|
||||||
|
$display("");
|
||||||
|
$display("============================================================");
|
||||||
|
$display(" SUMMARY: Full-Chain Real-Data Co-Simulation");
|
||||||
|
$display("============================================================");
|
||||||
|
$display(" Chain: range_bin_decimator(peak) -> doppler_processor");
|
||||||
|
$display(" Input samples: %0d (%0d chirps x %0d bins)",
|
||||||
|
TOTAL_INPUT_SAMPLES, CHIRPS, INPUT_BINS);
|
||||||
|
$display(" Decimator outputs: %0d (expected %0d)",
|
||||||
|
decim_out_count, CHIRPS * RANGE_BINS);
|
||||||
|
$display(" Doppler outputs: %0d (expected %0d)",
|
||||||
|
out_count, TOTAL_OUTPUT_SAMPLES);
|
||||||
|
$display(" Exact match: %0d / %0d", n_exact, out_count);
|
||||||
|
$display(" Within tolerance: %0d / %0d (tol=%0d)",
|
||||||
|
n_within_tol, out_count, MAX_ERROR);
|
||||||
|
$display(" Max error (I): %0d", max_err_i);
|
||||||
|
$display(" Max error (Q): %0d", max_err_q);
|
||||||
|
$display(" Structural checks: %0d", 9);
|
||||||
|
$display(" Data match checks: %0d", out_count);
|
||||||
|
$display(" Pass: %0d Fail: %0d", pass_count, fail_count);
|
||||||
|
$display("============================================================");
|
||||||
|
|
||||||
|
if (fail_count == 0)
|
||||||
|
$display("RESULT: ALL TESTS PASSED (%0d/%0d)", pass_count, test_count);
|
||||||
|
else
|
||||||
|
$display("RESULT: %0d TESTS FAILED", fail_count);
|
||||||
|
|
||||||
|
$display("============================================================");
|
||||||
|
|
||||||
|
#(CLK_PERIOD * 10);
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// WATCHDOG
|
||||||
|
// ============================================================================
|
||||||
|
initial begin
|
||||||
|
#(CLK_PERIOD * MAX_CYCLES * 2);
|
||||||
|
$display("[TIMEOUT] Simulation exceeded %0d cycles — aborting", MAX_CYCLES * 2);
|
||||||
|
$display("SOME TESTS FAILED");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
`timescale 1ns / 1ps
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tb_range_fft_realdata.v
|
||||||
|
*
|
||||||
|
* Co-simulation testbench: feeds real ADI CN0566 radar IQ data through
|
||||||
|
* the 1024-point fft_engine and compares output bit-for-bit against
|
||||||
|
* the Python golden reference (golden_reference.py).
|
||||||
|
*
|
||||||
|
* Stimulus: cosim/real_data/hex/chirp0_i.hex, chirp0_q.hex
|
||||||
|
* Expected: cosim/real_data/hex/range_fft_chirp0_i.hex, range_fft_chirp0_q.hex
|
||||||
|
*
|
||||||
|
* The golden reference uses identical fixed-point arithmetic (32-bit internal,
|
||||||
|
* 16-bit twiddle, same quarter-wave ROM, same bit-reversal and butterfly
|
||||||
|
* schedule), so outputs should match exactly (0 error tolerance).
|
||||||
|
*
|
||||||
|
* Pass criteria: ALL 1024 output bins match golden reference exactly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module tb_range_fft_realdata;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// PARAMETERS
|
||||||
|
// ============================================================================
|
||||||
|
localparam N = 1024;
|
||||||
|
localparam LOG2N = 10;
|
||||||
|
localparam DATA_W = 16;
|
||||||
|
localparam INT_W = 32;
|
||||||
|
localparam TW_W = 16;
|
||||||
|
localparam CLK_PERIOD = 10; // 100 MHz for simulation
|
||||||
|
|
||||||
|
// Error tolerance: 0 means exact match required.
|
||||||
|
// If the Python golden reference is truly bit-accurate, this should be 0.
|
||||||
|
// Set to 1 if there are minor rounding differences to debug later.
|
||||||
|
localparam integer MAX_ERROR = 0;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// SIGNALS
|
||||||
|
// ============================================================================
|
||||||
|
reg clk, reset_n;
|
||||||
|
reg start, inverse;
|
||||||
|
reg signed [DATA_W-1:0] din_re, din_im;
|
||||||
|
reg din_valid;
|
||||||
|
wire signed [DATA_W-1:0] dout_re, dout_im;
|
||||||
|
wire dout_valid, busy, done_sig;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// STIMULUS AND REFERENCE MEMORIES
|
||||||
|
// ============================================================================
|
||||||
|
reg signed [DATA_W-1:0] stim_re [0:N-1];
|
||||||
|
reg signed [DATA_W-1:0] stim_im [0:N-1];
|
||||||
|
reg signed [DATA_W-1:0] ref_re [0:N-1];
|
||||||
|
reg signed [DATA_W-1:0] ref_im [0:N-1];
|
||||||
|
reg signed [DATA_W-1:0] cap_re [0:N-1];
|
||||||
|
reg signed [DATA_W-1:0] cap_im [0:N-1];
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// DUT — 1024-point FFT engine
|
||||||
|
// ============================================================================
|
||||||
|
fft_engine #(
|
||||||
|
.N(N),
|
||||||
|
.LOG2N(LOG2N),
|
||||||
|
.DATA_W(DATA_W),
|
||||||
|
.INTERNAL_W(INT_W),
|
||||||
|
.TWIDDLE_W(TW_W),
|
||||||
|
.TWIDDLE_FILE("fft_twiddle_1024.mem")
|
||||||
|
) dut (
|
||||||
|
.clk(clk),
|
||||||
|
.reset_n(reset_n),
|
||||||
|
.start(start),
|
||||||
|
.inverse(inverse),
|
||||||
|
.din_re(din_re),
|
||||||
|
.din_im(din_im),
|
||||||
|
.din_valid(din_valid),
|
||||||
|
.dout_re(dout_re),
|
||||||
|
.dout_im(dout_im),
|
||||||
|
.dout_valid(dout_valid),
|
||||||
|
.busy(busy),
|
||||||
|
.done(done_sig)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// CLOCK
|
||||||
|
// ============================================================================
|
||||||
|
initial clk = 0;
|
||||||
|
always #(CLK_PERIOD/2) clk = ~clk;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// PASS / FAIL TRACKING
|
||||||
|
// ============================================================================
|
||||||
|
integer pass_count, fail_count;
|
||||||
|
|
||||||
|
task check;
|
||||||
|
input cond;
|
||||||
|
input [512*8-1:0] label;
|
||||||
|
begin
|
||||||
|
if (cond) begin
|
||||||
|
pass_count = pass_count + 1;
|
||||||
|
end else begin
|
||||||
|
$display(" [FAIL] %0s", label);
|
||||||
|
fail_count = fail_count + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endtask
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// VCD (optional — uncomment for waveform debug)
|
||||||
|
// ============================================================================
|
||||||
|
// initial begin
|
||||||
|
// $dumpfile("tb_range_fft_realdata.vcd");
|
||||||
|
// $dumpvars(0, tb_range_fft_realdata);
|
||||||
|
// end
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// MAIN TEST
|
||||||
|
// ============================================================================
|
||||||
|
integer i, out_idx;
|
||||||
|
integer err_re, err_im, max_err_re, max_err_im;
|
||||||
|
integer n_exact, n_within_tol;
|
||||||
|
reg signed [31:0] diff_re, diff_im;
|
||||||
|
integer abs_diff_re, abs_diff_im;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
pass_count = 0;
|
||||||
|
fail_count = 0;
|
||||||
|
|
||||||
|
$display("============================================================");
|
||||||
|
$display(" Range FFT Real-Data Co-Simulation (1024-pt)");
|
||||||
|
$display(" ADI CN0566 Phaser 10.525 GHz X-band FMCW");
|
||||||
|
$display("============================================================");
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// Load hex files
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
$readmemh("tb/cosim/real_data/hex/chirp0_i.hex", stim_re);
|
||||||
|
$readmemh("tb/cosim/real_data/hex/chirp0_q.hex", stim_im);
|
||||||
|
$readmemh("tb/cosim/real_data/hex/range_fft_chirp0_i.hex", ref_re);
|
||||||
|
$readmemh("tb/cosim/real_data/hex/range_fft_chirp0_q.hex", ref_im);
|
||||||
|
|
||||||
|
$display(" Loaded stimulus: chirp0_i/q.hex (1024 samples)");
|
||||||
|
$display(" Loaded reference: range_fft_chirp0_i/q.hex (1024 bins)");
|
||||||
|
$display(" First stim sample: re=%0d, im=%0d", stim_re[0], stim_im[0]);
|
||||||
|
$display(" Last stim sample: re=%0d, im=%0d", stim_re[N-1], stim_im[N-1]);
|
||||||
|
$display("");
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// Reset
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
reset_n = 0;
|
||||||
|
start = 0;
|
||||||
|
inverse = 0;
|
||||||
|
din_re = 0;
|
||||||
|
din_im = 0;
|
||||||
|
din_valid = 0;
|
||||||
|
repeat (5) @(posedge clk); #1;
|
||||||
|
reset_n = 1;
|
||||||
|
repeat (2) @(posedge clk); #1;
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// Start forward FFT
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
$display("--- Running 1024-point forward FFT ---");
|
||||||
|
inverse = 0;
|
||||||
|
@(posedge clk); #1;
|
||||||
|
start = 1;
|
||||||
|
@(posedge clk); #1;
|
||||||
|
start = 0;
|
||||||
|
|
||||||
|
// Feed N samples
|
||||||
|
for (i = 0; i < N; i = i + 1) begin
|
||||||
|
din_re = stim_re[i];
|
||||||
|
din_im = stim_im[i];
|
||||||
|
din_valid = 1;
|
||||||
|
@(posedge clk); #1;
|
||||||
|
end
|
||||||
|
din_valid = 0;
|
||||||
|
din_re = 0;
|
||||||
|
din_im = 0;
|
||||||
|
|
||||||
|
// Capture N output samples
|
||||||
|
out_idx = 0;
|
||||||
|
while (out_idx < N) begin
|
||||||
|
@(posedge clk); #1;
|
||||||
|
if (dout_valid) begin
|
||||||
|
cap_re[out_idx] = dout_re;
|
||||||
|
cap_im[out_idx] = dout_im;
|
||||||
|
out_idx = out_idx + 1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
$display(" FFT output captured: %0d bins", out_idx);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// Compare output vs golden reference
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
$display("");
|
||||||
|
$display("--- Comparing RTL output vs Python golden reference ---");
|
||||||
|
|
||||||
|
max_err_re = 0;
|
||||||
|
max_err_im = 0;
|
||||||
|
n_exact = 0;
|
||||||
|
n_within_tol = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < N; i = i + 1) begin
|
||||||
|
diff_re = cap_re[i] - ref_re[i];
|
||||||
|
diff_im = cap_im[i] - ref_im[i];
|
||||||
|
|
||||||
|
// Absolute value
|
||||||
|
abs_diff_re = (diff_re < 0) ? -diff_re : diff_re;
|
||||||
|
abs_diff_im = (diff_im < 0) ? -diff_im : diff_im;
|
||||||
|
|
||||||
|
if (abs_diff_re > max_err_re) max_err_re = abs_diff_re;
|
||||||
|
if (abs_diff_im > max_err_im) max_err_im = abs_diff_im;
|
||||||
|
|
||||||
|
if (diff_re == 0 && diff_im == 0)
|
||||||
|
n_exact = n_exact + 1;
|
||||||
|
|
||||||
|
if (abs_diff_re <= MAX_ERROR && abs_diff_im <= MAX_ERROR)
|
||||||
|
n_within_tol = n_within_tol + 1;
|
||||||
|
|
||||||
|
// Print first 10 mismatches and some spot checks
|
||||||
|
if ((abs_diff_re > MAX_ERROR || abs_diff_im > MAX_ERROR) &&
|
||||||
|
(fail_count < 10)) begin
|
||||||
|
$display(" Bin %4d: RTL=(%6d,%6d) REF=(%6d,%6d) ERR=(%4d,%4d)",
|
||||||
|
i, cap_re[i], cap_im[i], ref_re[i], ref_im[i],
|
||||||
|
diff_re, diff_im);
|
||||||
|
end
|
||||||
|
|
||||||
|
check(abs_diff_re <= MAX_ERROR && abs_diff_im <= MAX_ERROR,
|
||||||
|
"range FFT bin match");
|
||||||
|
end
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// Summary
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
$display("");
|
||||||
|
$display("============================================================");
|
||||||
|
$display(" SUMMARY: Range FFT Real-Data Co-Simulation");
|
||||||
|
$display("============================================================");
|
||||||
|
$display(" Total bins: %0d", N);
|
||||||
|
$display(" Exact match: %0d / %0d", n_exact, N);
|
||||||
|
$display(" Within tolerance: %0d / %0d (tol=%0d)", n_within_tol, N, MAX_ERROR);
|
||||||
|
$display(" Max error (re): %0d", max_err_re);
|
||||||
|
$display(" Max error (im): %0d", max_err_im);
|
||||||
|
$display(" Pass: %0d Fail: %0d", pass_count, fail_count);
|
||||||
|
$display("============================================================");
|
||||||
|
|
||||||
|
if (fail_count == 0)
|
||||||
|
$display("RESULT: ALL TESTS PASSED");
|
||||||
|
else
|
||||||
|
$display("RESULT: %0d TESTS FAILED", fail_count);
|
||||||
|
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Timeout watchdog (1024-point FFT should finish well within 1M cycles)
|
||||||
|
initial begin
|
||||||
|
#(CLK_PERIOD * 2000000);
|
||||||
|
$display("[TIMEOUT] Simulation exceeded 2M cycles — aborting");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
Reference in New Issue
Block a user