-
Notifications
You must be signed in to change notification settings - Fork 17
/
test_atp.cc
1488 lines (1276 loc) · 50.9 KB
/
test_atp.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* SPDX-License-Identifier: BSD-3-Clause-Clear
*
* Copyright (c) 2015, 2017 ARM Limited
* All rights reserved
* Created on: Oct 19, 2015
* Author: Matteo Andreozzi
* Riken Gohil
*/
#include "test_atp.hh"
#include "event.hh"
#include "stats.hh"
#include "fifo.hh"
#include "packet_desc.hh"
#include <vector>
#include <sstream>
#include <algorithm>
#include <cassert>
#include "traffic_profile_desc.hh"
#include "packet_tagger.hh"
#include "utilities.hh"
#include "kronos.hh"
#include "types.hh"
#ifndef CPPUNIT_ASSERT
#define CPPUNIT_ASSERT(x)
#endif
using namespace std;
namespace TrafficProfiles {
TestAtp::TestAtp(): tpm(nullptr), configuration(nullptr) {
}
TestAtp::~TestAtp() {
tearDown();
}
// test helper functions
PatternConfiguration* TestAtp::makePatternConfiguration(PatternConfiguration* t, const Command cmd, const Command wait) {
t->set_cmd(cmd);
t->set_wait_for(wait);
return t;
}
FifoConfiguration* TestAtp::makeFifoConfiguration(FifoConfiguration* t, const uint64_t fullLevel,
const FifoConfiguration::StartupLevel level,const uint64_t ot, const uint64_t total, const uint64_t rate) {
t->set_full_level(fullLevel);
t->set_start_fifo_level(level);
t->set_ot_limit(ot);
t->set_total_txn(total);
t->set_rate(std::to_string(rate));
return t;
}
void
TestAtp::makeProfile(Profile *p, const ProfileDescription &desc) const {
p->set_name(desc.name);
p->set_type(desc.type);
if (desc.master) {
p->set_master_id(*desc.master);
}
else {
p->set_master_id(desc.name);
}
if (desc.waitFor) {
for (const auto& a: *desc.waitFor) {
p->add_wait_for(a);
}
}
if (desc.iommu_id)
p->set_iommu_id(desc.iommu_id);
if (desc.flow_id)
p->set_flow_id(desc.flow_id);
}
// test specific functions
void TestAtp::setUp() {
// create new TPM
tpm = new TrafficProfileManager();
// load the Configuration into the TPM
if (configuration) {
tpm->configure(*configuration);
// Dump the TPM configuration
string out;
if (!tpm->print(out)) {
ERROR("unable to dump TPM");
}
// display the TPM
LOG(out);
}
}
void TestAtp::tearDown() {
if (tpm) {
delete tpm;
tpm = nullptr;
}
if (configuration) {
delete configuration;
configuration = nullptr;
}
}
bool TestAtp::buildManager_fromFile(const string& fileName) {
// create new TPM if not created yet
if (nullptr == tpm) {
tpm = new TrafficProfileManager();
}
// load the Configuration into the TPM
return (tpm->load(fileName));
}
void TestAtp::dumpStats() {
if (tpm) {
LOG ("TestAtp::dumpStats dumping TPM stats");
PRINT("Global TPM Stats:", tpm->getStats().dump());
for (auto&m: tpm->getMasters()) {
PRINT(m,"Stats:",tpm->getMasterStats(m).dump());
}
}
}
void TestAtp::testAgainstInternalSlave(const string& rate,
const string& latency) {
PRINT("ATP Engine running in standalone execution mode. "
"Internal slave configuration:",rate,latency);
// the TPM should already be created and loaded with masters
// query the TPM for masters and assign all unassigned masters to
// an internal slave
Profile slave;
// fill the configuration
makeProfile(&slave, ProfileDescription { "TestAtp::InternalSlave::",
Profile::READ });
// fill in the slave field
SlaveConfiguration* slave_cfg = slave.mutable_slave();
slave_cfg->set_latency(latency);
slave_cfg->set_rate(rate);
slave_cfg->set_granularity(64);
slave_cfg->set_ot_limit(0);
// request list of all masters and add unassigned ones to internal slave
auto& masters = tpm->getMasters();
auto& masterSlaves = tpm->getMasterSlaves();
for (auto&m : masters) {
if (masterSlaves.find(m)==masterSlaves.end()) {
slave_cfg->add_master(m);
}
}
// register (overwrite) slave with TPM
tpm->configureProfile(slave, make_pair(1,1), true);
// request packets to masters and route to the internal slave
tpm->loop();
dumpStats();
}
// unit tests
void
TestAtp::testAtp_fifo() {
Fifo fifo;
// start with an empty FIFO, which fills every time by 1000
fifo.init(nullptr, Profile::READ,1000,1,0,2000,true);
uint64_t next = 0;
uint64_t request_time=0;
bool underrun=false, overrun=false;
uint64_t i = 0;
for (; i< 2; ++i){
// read 1000 every time
fifo.send(underrun, overrun, next, request_time, i, 1000);
CPPUNIT_ASSERT(fifo.getOt() == 1);
fifo.receive(underrun, overrun, i, 1000);
CPPUNIT_ASSERT(fifo.getOt() == 0);
}
// checks to see if send allocation was successful
CPPUNIT_ASSERT(fifo.getLevel() == 1000);
// consume last data
fifo.send(underrun, overrun, next, request_time, i, 0);
CPPUNIT_ASSERT(fifo.getOt() == 0);
fifo.receive(underrun, overrun, i,0);
CPPUNIT_ASSERT(fifo.getOt() == 0);
// we should have an empty FIFO here
CPPUNIT_ASSERT(fifo.getLevel() == 0);
CPPUNIT_ASSERT(!underrun);
CPPUNIT_ASSERT(!overrun);
// checks for having multiple outstanding transactions
fifo.send(underrun, overrun, next, request_time, i, 1000);
fifo.send(underrun, overrun, next, request_time, ++i, 1000);
CPPUNIT_ASSERT(fifo.getOt() == 2);
fifo.receive(underrun, overrun, i, 1000);
fifo.receive(underrun, overrun, ++i, 1000);
CPPUNIT_ASSERT(fifo.getOt() == 0);
// we should underrun now
fifo.send(underrun, overrun, next, request_time, ++i, 0);
CPPUNIT_ASSERT(fifo.getOt() == 0);
fifo.receive(underrun, overrun, i,0);
CPPUNIT_ASSERT(fifo.getOt() == 0);
CPPUNIT_ASSERT(underrun);
CPPUNIT_ASSERT(!overrun);
// clear flags
underrun=overrun=false;
// start with a full WRITE FIFO, which fills every time by 1000
fifo.init(nullptr, Profile::WRITE,1000,1,2000,2000,true);
i=0;
for (;i< 2; ++i){
// read 1000 every time, receive partial 500 twice
fifo.send(underrun, overrun, next, request_time, i, 1000);
CPPUNIT_ASSERT(fifo.getOt() == 1);
fifo.receive(underrun, overrun, i, 500);
fifo.receive(underrun, overrun, i, 500);
CPPUNIT_ASSERT(fifo.getOt() == 0);
}
// checks to see if send removal was successful
CPPUNIT_ASSERT(fifo.getLevel() == 1000);
// cause FIFO update
fifo.send(underrun, overrun, next, request_time, i, 0);
CPPUNIT_ASSERT(fifo.getOt() == 0);
fifo.receive(underrun, overrun, i,0);
CPPUNIT_ASSERT(fifo.getOt() == 0);
// we should have a full FIFO here
CPPUNIT_ASSERT(fifo.getLevel() == 2000);
CPPUNIT_ASSERT(!underrun);
CPPUNIT_ASSERT(!overrun);
// we should overrun now
fifo.send(underrun, overrun, next, request_time, ++i, 0);
CPPUNIT_ASSERT(fifo.getOt() == 0);
fifo.receive(underrun, overrun, i,0);
CPPUNIT_ASSERT(fifo.getOt() == 0);
CPPUNIT_ASSERT(!underrun);
CPPUNIT_ASSERT(overrun);
CPPUNIT_ASSERT(fifo.getLevel() == 2000);
// consume 1000
fifo.send(underrun, overrun, next, request_time, i, 1000);
CPPUNIT_ASSERT(fifo.getOt() == 1);
CPPUNIT_ASSERT(fifo.getLevel() == 2000);
fifo.receive(underrun, overrun, i,1000);
CPPUNIT_ASSERT(fifo.getLevel() == 1000);
// reset the FIFO
fifo.reset();
// verify that the FIFO is back to startup values
CPPUNIT_ASSERT(fifo.getOt() == 0);
CPPUNIT_ASSERT(fifo.getLevel() == 2000);
for (;i< 2; ++i){
// read 1000 every time, receive partial 500 twice
fifo.send(underrun, overrun, next, request_time, i, 1000);
CPPUNIT_ASSERT(fifo.getOt() == 1);
fifo.receive(underrun, overrun, i, 500);
fifo.receive(underrun, overrun, i, 500);
CPPUNIT_ASSERT(fifo.getOt() == 0);
}
// test mid-update cycle activation
// start with a full READ FIFO, which depletes every 10 units by 1000
fifo.init(nullptr, Profile::READ,1000,10,2000,2000,true);
// send request at time 3
bool ok = fifo.send(underrun, overrun, next, request_time, 13, 1000);
// verify that we get next time 23 and not 10
CPPUNIT_ASSERT(!ok);
CPPUNIT_ASSERT(next == 23);
ok = fifo.send(underrun, overrun, next, request_time, 21, 1000);
// verify that we get next time 23 and not 10
CPPUNIT_ASSERT(!ok);
CPPUNIT_ASSERT(next == 23);
// verify that the next send time is correct and that the FIFO transmits
ok = fifo.send(underrun, overrun, next, request_time, next, 1000);
CPPUNIT_ASSERT(ok);
// note : as the FIFO is now at level 1000 with 1000 in-flight, next is 0
CPPUNIT_ASSERT(next==0);
// set the time to 33 and see if we get another transaction
fifo.receive(underrun, overrun, 33, 1000);
// verify that the 2nd next send time is correct
ok = fifo.send(underrun, overrun, next, request_time, 33, 1000);
CPPUNIT_ASSERT(ok);
}
void
TestAtp::testAtp_event() {
Event ev1(Event::NONE,Event::AWAITED,0,0),
ev2(Event::NONE,Event::AWAITED,0,0),
ev3(Event::NONE,Event::TRIGGERED,0,0),
ev4(Event::TERMINATION,Event::AWAITED,0,0),
ev5(Event::NONE,Event::TRIGGERED,1,0);
// test comparison of event objects
CPPUNIT_ASSERT(ev1==ev2);
// a triggered and awaited event compare equal
CPPUNIT_ASSERT(ev1==ev3);
CPPUNIT_ASSERT(ev1!=ev4);
CPPUNIT_ASSERT(ev1!=ev5);
// test parsing event objects from string
string event = "testAtp_event TERMINATION";
string name ="";
Event::Type type = Event::NONE;
CPPUNIT_ASSERT(Event::parse(type, name, event));
CPPUNIT_ASSERT(type == Event::TERMINATION);
CPPUNIT_ASSERT(name == "testAtp_event");
event = "ERROR ERROR";
CPPUNIT_ASSERT(!Event::parse(type, name, event));
}
void TestAtp::testAtp_packetDesc() {
Profile config;
// fill the configuration
makeProfile(&config, ProfileDescription { "testAtp_packetDesc_profile",
Profile::READ });
// fill the FIFO configuration - pointer, maxLevel,
// startup level, OT, transactions, rate
makeFifoConfiguration(config.mutable_fifo(),
0, FifoConfiguration::EMPTY, 0, 0, 0);
// ATP Packet descriptor
PacketDesc pd;
// fill the Google Protocol Buffer configuration object
makePatternConfiguration(config.mutable_pattern(),
Command::READ_REQ, Command::READ_RESP);
// ATP Packet Tagger
PacketTagger pt;
// configure the packet address and size generation policies
auto* pk = config.mutable_pattern();
pk->set_size(64);
PatternConfiguration::Address* address = pk->mutable_address();
address->set_base(0);
// set increment
address->set_increment(0x1FBE);
// set local id limits
pk->set_lowid(10);
pk->set_highid(11);
// initialize the Packet Descriptor and checks if initialized correctly
pd.init(0, *config.mutable_pattern(), &pt);
CPPUNIT_ASSERT(pd.isInitialized());
CPPUNIT_ASSERT(pd.waitingFor() == Command::READ_RESP);
// register profile
tpm->configureProfile(config);
// request a packet three times
for (uint64_t i=0; i< 3 ; ++i) {
Packet * p(nullptr);
bool ok = pd.send(p,0);
CPPUNIT_ASSERT(ok);
// test local id generation
CPPUNIT_ASSERT(p->id()== (10+i>11?10:10+i));
// set correct type of packet and receive
p->set_cmd(Command::READ_RESP);
ok = pd.receive(0, p);
CPPUNIT_ASSERT(ok);
// delete packet
delete p;
}
// descriptor re-initialisation to test range reset
pd.addressReconfigure(0xBEEF, 0x3F7C);
for (uint64_t i=0; i< 3 ; ++i) {
Packet * p(nullptr);
bool ok = pd.send(p,0);
CPPUNIT_ASSERT(ok);
// test address reconfiguration
CPPUNIT_ASSERT(p->addr() ==
(i == 0 || i == 2 ? 0xBEEF: 0xDEAD));
// set correct type of packet and receive
p->set_cmd(Command::READ_RESP);
ok = pd.receive(0, p);
CPPUNIT_ASSERT(ok);
// delete packet
delete p;
}
/*
* test autoRange - first call fails as autoRange
* cannot extend an existing range
* so we still get the old range returned
*/
CPPUNIT_ASSERT(pd.autoRange(1023) == 0x3F7C);
// force the packet descriptor autoRange
CPPUNIT_ASSERT(pd.autoRange(1023, true) == 0x7ED842);
// test new range applies
for (uint64_t i=0; i< 3 ; ++i) {
Packet * p(nullptr);
// reset before the third packet is sent
if (i==2) pd.reset();
bool ok = pd.send(p,0);
CPPUNIT_ASSERT(ok);
// test address reconfiguration
// second packet shouldn't wrap around anymore
// third packet should now have the base address due to reset
if (i==0) {
CPPUNIT_ASSERT(p->addr() == 0xDEAD);
} else if (i==1) {
CPPUNIT_ASSERT(p->addr() == 0xfe6b);
} else {
CPPUNIT_ASSERT(p->addr() == 0xBEEF);
}
// set correct type of packet and receive
p->set_cmd(Command::READ_RESP);
ok = pd.receive(0, p);
CPPUNIT_ASSERT(ok);
// delete packet
delete p;
}
// test striding autorange - re-init the pd
auto* stride = pk->mutable_stride();
pk->mutable_address()->set_increment(10);
stride->set_increment(64);
stride->set_range("640");
pd.init(0, *pk, &pt);
// test that the range is 640 times the strides (10) times the increment
CPPUNIT_ASSERT(pd.autoRange(100)==6400);
// reduce the increment to and test the autoRange again
address->set_increment(640);
pd.init(0, *pk, &pt);
pd.autoRange(100);
Packet * p(nullptr);
// TEMP test packet generation doesn't wrap
for (uint64_t i=0; i< 100 ; ++i) {
bool ok = pd.send(p,0);
// verify that no wrap-around has occurred
CPPUNIT_ASSERT(p->addr() == i*64);
delete p;
}
}
void TestAtp::testAtp_packetTagger(){
// instatiate packet to be tagged
Packet* packet = new Packet();
// setup tagger to perform tagging
PacketTagger * tagger = new PacketTagger();
// verify packet metadata blank before tagging
CPPUNIT_ASSERT(packet->has_flow_id() == false);
CPPUNIT_ASSERT(packet->has_iommu_id() == false);
CPPUNIT_ASSERT(packet->has_stream_id() == false);
// attempt to tag before setting up packet tagger metadata vars
// -> should cause no effect
tagger->tagPacket(packet);
CPPUNIT_ASSERT(packet->has_flow_id() == false);
CPPUNIT_ASSERT(packet->has_iommu_id() == false);
CPPUNIT_ASSERT(packet->has_stream_id() == false);
// configure packet tagger correctly and check with several values
for (uint64_t i : {0, 1, 2}){
delete packet;
packet = new Packet();
tagger->flow_id = tagger->stream_id = i;
tagger->iommu_id = static_cast<uint32_t>(i);
tagger->tagPacket(packet);
// check if packet is tagged correctly
CPPUNIT_ASSERT(packet->flow_id() == i);
CPPUNIT_ASSERT(packet->stream_id() == i);
CPPUNIT_ASSERT(packet->iommu_id() == static_cast<uint32_t>(i));
}
// save last iteration values to check them later
uint64_t test_flow_id = tagger->flow_id,
test_stream_id = tagger->stream_id,
offset = 10;
uint32_t test_iommu_id = tagger->iommu_id;
// attempt to retag packet with different values
tagger->flow_id = tagger->flow_id + offset;
tagger->iommu_id = tagger->iommu_id + offset;
tagger->stream_id = tagger->stream_id + offset;
tagger->tagPacket(packet);
// Old values should not be retained
CPPUNIT_ASSERT(packet->flow_id() != test_flow_id);
CPPUNIT_ASSERT(packet->iommu_id() != test_iommu_id);
CPPUNIT_ASSERT(packet->stream_id() != test_stream_id);
// New values should be written to packet
CPPUNIT_ASSERT(packet->flow_id() == (test_flow_id + offset));
CPPUNIT_ASSERT(packet->iommu_id() == (test_iommu_id + offset));
CPPUNIT_ASSERT(packet->stream_id() == (test_stream_id + offset));
// attempt to tag with invalid values
tagger->flow_id = tagger->stream_id = InvalidId<uint64_t>();
tagger->iommu_id = InvalidId<uint32_t>();
delete packet;
packet = new Packet();
tagger->tagPacket(packet);
// Invalid values should not be written to packet
CPPUNIT_ASSERT(packet->has_flow_id() == false);
CPPUNIT_ASSERT(packet->has_iommu_id() == false);
CPPUNIT_ASSERT(packet->has_stream_id() == false);
// cleanup test
delete tagger;
delete packet;
}
void TestAtp::testAtp_stats() {
Stats s1, s2, s3, s4;
// record sending 1000 bytes every 2 ticks
for (uint64_t i=0; i <= 10; i+=2) {
s1.send(i, 1000);
}
// data transferred should be 6KB and rate should be
// 600 B/tick
CPPUNIT_ASSERT(s1.dataSent == 6000);
CPPUNIT_ASSERT(s1.sendRate() == 600);
CPPUNIT_ASSERT(s1.sent == 6);
CPPUNIT_ASSERT(s1.received == 0);
CPPUNIT_ASSERT(s1.dataReceived == 0);
s1.reset();
// data should now be back to 0
CPPUNIT_ASSERT(s1.dataSent == 0);
// test reception
s1.receive(0, 0, 0.0);
s1.receive(10, 1000, 0.0);
CPPUNIT_ASSERT(s1.dataReceived == 1000);
CPPUNIT_ASSERT(s1.receiveRate() == 100);
CPPUNIT_ASSERT(s1.sent == 0);
CPPUNIT_ASSERT(s1.received == 2);
// tests to make sure add and sum operator are working correctly
s1.reset();
for (uint64_t i=0; i <= 10; i+=2) {
s1.send(i, 1000);
s2.send(i+12, 1000);
}
for (uint64_t i=0; i <= 22; i+=2) {
s3.send(i, 1000);
}
s4 = s1 + s2;
CPPUNIT_ASSERT(s4.dump() == s3.dump());
s2 += s1;
CPPUNIT_ASSERT(s2.dump() == s3.dump());
}
void TestAtp::testAtp_trafficProfile() {
// configuration object
Profile config;
// fill the configuration
makeProfile(&config, ProfileDescription { "testAtp_trafficProfile",
Profile::READ });
CPPUNIT_ASSERT(!config.has_pattern());
CPPUNIT_ASSERT(!config.wait_for_size());
CPPUNIT_ASSERT(!config.has_fifo());
// fill the FIFO configuration - pointer,
// maxLevel, startup level, OT, transactions, rate
makeFifoConfiguration(config.mutable_fifo(), 1000,
FifoConfiguration::EMPTY, 1, 1, 10);
// fill the Packet Descriptor configuration
PatternConfiguration* pk = makePatternConfiguration(config.mutable_pattern(),
Command::READ_REQ,
Command::READ_RESP);
// configure the packet address and size generation policies
pk->set_size(64);
PatternConfiguration::Address* address = pk->mutable_address();
address->set_base(0);
address->set_increment(0);
// register this profile in the TPM
tpm->configureProfile(config);
// add a wait on a PLAY event and register as new profile
config.add_wait_for("testAtp_trafficProfile_to_play ACTIVATION");
config.set_name("testAtp_trafficProfile_to_play");
// register this profile in the TPM
tpm->configureProfile(config);
// remove the wait for field from the config object
config.clear_wait_for();
// delete the packet configuration from the config object
config.clear_pattern();
// change name
config.set_name("testAtp_trafficProfile_checker");
// set the checker to monitor test_profile
config.add_check("testAtp_trafficProfile");
// register this profile in the TPM
tpm->configureProfile(config);
// get the ATP Profile Descriptors
TrafficProfileDescriptor* pd = tpm->profiles.at(0);
TrafficProfileDescriptor* wait = tpm->profiles.at(1);
TrafficProfileDescriptor* checker = tpm->profiles.at(2);
// test packet send and receive
bool locked = false;
Packet* p(nullptr),*empty(nullptr);
uint64_t next=0;
CPPUNIT_ASSERT(pd->send(locked, p, next));
CPPUNIT_ASSERT(checker->send(locked, p, next));
p->set_cmd(Command::READ_RESP);
// profile should be locked, not active
CPPUNIT_ASSERT(!pd->active(locked));
CPPUNIT_ASSERT(locked);
// profile should not accept a send request at this time
CPPUNIT_ASSERT(!pd->send(locked, empty, next));
// receive two partial responses
p->set_size(32);
CPPUNIT_ASSERT(pd->receive(next, p, .0));
CPPUNIT_ASSERT(pd->receive(next, p, .0));
// update checker
checker->receive(next, p, .0);
// profile should now terminate - not active, not locked
CPPUNIT_ASSERT(!pd->active(locked));
CPPUNIT_ASSERT(!locked);
// checker should have terminated
CPPUNIT_ASSERT(!checker->active(locked));
CPPUNIT_ASSERT(!locked);
// reset the checker
checker->reset();
// checker is active again
CPPUNIT_ASSERT(checker->active(locked));
// test locked profile
CPPUNIT_ASSERT(!wait->send(locked, p, next));
CPPUNIT_ASSERT(locked);
// issue PLAY event
CPPUNIT_ASSERT(wait->receiveEvent(Event(Event::ACTIVATION,
Event::TRIGGERED,wait->getId(),0)));
// test packet is now sent
CPPUNIT_ASSERT(wait->send(locked, p, next));
CPPUNIT_ASSERT(!locked);
}
void TestAtp::testAtp_packetTaggerCreation(){
// setup test metadata fields
const uint64_t test_iommu_id = 0, test_flow_id = 1, test_stream_id = 2;
// packet tagger should be created when profle_desc constructor is called
// and flow_id and/or iommu_id are set
ProfileDescription profDesc = ProfileDescription{
"testAtp_tagger_creation_1", Profile::READ,
nullptr, nullptr};
profDesc.iommu_id = test_iommu_id;
profDesc.flow_id = test_flow_id;
Profile config;
makeProfile(&config, profDesc);
// configure required ProfileDesc properties for it to be a Master and
// register it with the TPM
makeFifoConfiguration(config.mutable_fifo(), 1000,
FifoConfiguration::EMPTY, 1, 1, 10);
PatternConfiguration* pk = makePatternConfiguration(
config.mutable_pattern(),
Command::READ_REQ, Command::READ_RESP
);
pk->set_size(64);
PatternConfiguration::Address* address = pk->mutable_address();
address->set_base(0);
address->set_increment(0);
tpm->configureProfile(config);
// profile streamId should be invalid; still to be been added to stream
TrafficProfileDescriptor* profile = tpm->profiles.at(0);
CPPUNIT_ASSERT(!isValid(profile->_streamId));
// extract packet tagger and check metadata values
PacketTagger* taggerPop = profile->packetTagger;
Packet* packet = new Packet();
taggerPop->tagPacket(packet);
CPPUNIT_ASSERT(packet->has_stream_id() == false);
CPPUNIT_ASSERT(packet->iommu_id() == test_iommu_id);
CPPUNIT_ASSERT(packet->flow_id() == test_flow_id);
delete packet;
// packet tagger should not be recreated if profile_desc has existing
// tagger and addToStream is called after; streamId should be added to
// existing tagger
profile->addToStream(test_stream_id);
// check whether streamId is correctly set
CPPUNIT_ASSERT(isValid(profile->_streamId));
CPPUNIT_ASSERT(profile->_streamId == test_stream_id);
// packet tagger should have been updated to reflect new value for streamId
packet = new Packet();
taggerPop->tagPacket(packet);
// old metadata values should be intact
CPPUNIT_ASSERT(packet->stream_id() == test_stream_id);
CPPUNIT_ASSERT(packet->iommu_id() == test_iommu_id);
CPPUNIT_ASSERT(packet->flow_id() == test_flow_id);
delete packet;
}
void
TestAtp::testAtp_tpm() {
const string profile_0 = "testAtp_tpm_profile_0";
const string profile_1 = "testAtp_tpm_profile_1";
const unordered_set<string> profiles = {profile_0, profile_1};
// profile configuration object
Profile config_0, config_1;
// fill the configuration
makeProfile(&config_0, ProfileDescription { profile_0, Profile::READ });
// fill the FIFO configuration - pointer, maxLevel,
// startup level, OT, transactions, rate
makeFifoConfiguration(config_0.mutable_fifo(), 1000,
FifoConfiguration::EMPTY, 1, 4, 10);
// fill the Packet Descriptor configuration
PatternConfiguration* pk =
makePatternConfiguration(config_0.mutable_pattern(),
Command::READ_REQ,
Command::READ_RESP);
// configure the packet address and size generation policies
pk->set_size(32);
PatternConfiguration::Address* address = pk->mutable_address();
address->set_base(0);
address->set_increment(64);
PatternConfiguration::Stride* stride = pk->mutable_stride();
stride->set_increment(1);
stride->set_range("3B");
// register 1st profile
tpm->configureProfile(config_0);
// change fields for 2nd profile
// 2nd profile waits on 1st to complete
config_1 = config_0;
config_1.set_name(profile_1);
config_1.set_master_id(profile_1);
config_1.add_wait_for(profile_0);
// register 2nd profile
tpm->configureProfile(config_1);
// checks to see if list of masters was set correctly
CPPUNIT_ASSERT(tpm->getMasters() == profiles);
// checks to see if the number of slaves is 0
CPPUNIT_ASSERT(tpm->getMasterSlaves().empty());
// request packets to TPM
bool locked = false;
uint64_t next = 0, time = 0;
// every time a packet is generated, send a response
// to terminate test_profile and unlock test_profile2
// reuse request as response, so that it gets deallocated by ATP
// each profile sends 3 packets , then terminates
for (uint64_t i = 0; i < 4 ; ++i) {
auto packets = tpm->send(locked, next, time);
// only one masters sent packets
CPPUNIT_ASSERT(packets.size() == 1);
// test_profile sent a packet
CPPUNIT_ASSERT(packets.find(profile_0) != packets.end());
// one master is locked, so flag on
CPPUNIT_ASSERT(locked);
Packet* p = (packets.find(profile_0)->second);
CPPUNIT_ASSERT(p->addr()==(i<3?i:64));
p->set_cmd(Command::READ_RESP);
tpm->receive(0, p);
}
for (uint64_t i = 0; i < 4 ; ++i) {
// "test_profile2" is now unlocked
auto packets = tpm->send(locked, next, time);
// test_profile2 sent a packet
CPPUNIT_ASSERT(packets.find(profile_1) != packets.end());
// test_profile2 is locked, waiting for response
CPPUNIT_ASSERT(locked);
// TPM is waiting for responses
CPPUNIT_ASSERT(tpm->waiting());
// send the response
Packet* p = (packets.find(profile_1)->second);
CPPUNIT_ASSERT(p->addr()==(i<3?i:64));
p->set_cmd(Command::READ_RESP);
tpm->receive(0, p);
}
CPPUNIT_ASSERT(!tpm->waiting());
// no packets left to send
auto packets = tpm->send(locked, next, time);
CPPUNIT_ASSERT(packets.empty());
// check that the stream composed by profile 0 and 1 is completed
const uint64_t pId = tpm->profileId("testAtp_tpm_profile_0");
CPPUNIT_ASSERT(tpm->streamTerminated(pId));
// reset the stream from profile 0
tpm->streamReset(pId);
CPPUNIT_ASSERT(!tpm->streamTerminated(pId));
// play profile 0 again.
for (uint64_t i = 0; i < 4 ; ++i) {
auto packets = tpm->send(locked, next, time);
// only one masters sent packets
CPPUNIT_ASSERT(packets.size() == 1);
// test_profile sent a packet
CPPUNIT_ASSERT(packets.find(profile_0) != packets.end());
// one master is locked, so flag on
CPPUNIT_ASSERT(locked);
Packet* p = (packets.find(profile_0)->second);
CPPUNIT_ASSERT(p->addr()==(i<3?i:64));
p->set_cmd(Command::READ_RESP);
tpm->receive(0, p);
}
for (uint64_t i = 0; i < 4 ; ++i) {
// "test_profile2" is now unlocked
auto packets = tpm->send(locked, next, time);
// test_profile2 sent a packet
CPPUNIT_ASSERT(packets.find(profile_1) != packets.end());
// test_profile2 is locked, waiting for response
CPPUNIT_ASSERT(locked);
// TPM is waiting for responses
CPPUNIT_ASSERT(tpm->waiting());
// send the response
Packet* p = (packets.find(profile_1)->second);
CPPUNIT_ASSERT(p->addr()==(i<3?i:64));
p->set_cmd(Command::READ_RESP);
tpm->receive(0, p);
}
CPPUNIT_ASSERT(tpm->streamTerminated(pId));
// reset the TPM - this causes profiles to be reloaded
tpm->reset();
// register 1st profile
tpm->configureProfile(config_0);
// register 2nd profile
tpm->configureProfile(config_1);
// define a new profile, which also waits on profile 0
const string profile_2 = "testAtp_tpm_profile_2";
Profile config_2(config_1);
config_2.set_name(profile_2);
config_2.set_master_id(profile_2);
// change type to WRITE
config_2.set_type(Profile::WRITE);
// register 3nd profile
tpm->configureProfile(config_2);
// define a new profile, which waits on profile 1 and 2
const string profile_3 = "testAtp_tpm_profile_3";
Profile config_3(config_0);
config_3.set_name(profile_3);
config_3.set_master_id(profile_3);
config_3.add_wait_for(profile_1);
config_3.add_wait_for(profile_2);
// register 3nd profile
tpm->configureProfile(config_3);
/*
* profile 1 and profile 2 wait on profile 0 - all READ profiles,
* whilst profile 3 is of type WRITE and waits on 1 and 2.
* They all are configured with 4 transactions of 64 bytes each.
* They form a diamond-shaped stream.
* Each of them streams 3 packets with a stride of 1 byte distance,
* then jumps with an increment of 64 to issue the fourth.
* We therefore reconfigure the stream to constrain it to
* 64 (first increment) + 1 (left over packet) = 65 bytes per profile
* with a starting address base of 0x00FF
*/
const uint64_t rootId = tpm->profileId("testAtp_tpm_profile_0");
const uint64_t newRange =
tpm->addressStreamReconfigure(rootId, 0x00FF, 388);
// verify that the new range has been applied
CPPUNIT_ASSERT(newRange == 260);
// send all packets
for (uint64_t i = 0; i < 16 ; ++i) {
auto packets = tpm->send(locked, next, time);
if (!locked) time = next;
for (auto p: packets) {
p.second->set_cmd(Command::READ_RESP);
tpm->receive(time, p.second);
}
}
/* uniqueStream */
config_0.add_wait_for(profile_0 + " ACTIVATION");
auto reset = [this, &config_0, &config_1]() {
tpm->reset();
tpm->configureProfile(config_0); tpm->configureProfile(config_1);
};
/* 1. First usage should return Root Profile ID of Original */
reset(); tpm->streamCacheUpdate();
uint64_t orig_id { tpm->profileId(profile_0) };
TrafficProfileDescriptor *orig { tpm->getProfile(orig_id) };
uint64_t clone0_id { tpm->uniqueStream(orig_id) };
TrafficProfileDescriptor *clone0 { tpm->getProfile(clone0_id) };
CPPUNIT_ASSERT(tpm->getStreamCache().size() == 1);
CPPUNIT_ASSERT(tpm->getProfileMap().size() == 2);
CPPUNIT_ASSERT(orig_id == clone0_id); CPPUNIT_ASSERT(orig == clone0);
/* 2. Non-first usage should create Clone and return its Root Profile ID
Clone name should be different from Original */
clone0_id = tpm->uniqueStream(orig_id);
clone0 = tpm->getProfile(clone0_id);
CPPUNIT_ASSERT(tpm->getStreamCache().size() == 2);
CPPUNIT_ASSERT(tpm->getProfileMap().size() == 4);
CPPUNIT_ASSERT(orig_id != clone0_id); CPPUNIT_ASSERT(orig != clone0);
uint64_t clone1_id = tpm->uniqueStream(orig_id);
TrafficProfileDescriptor *clone1 { tpm->getProfile(clone1_id) };
CPPUNIT_ASSERT(tpm->getStreamCache().size() == 3);
CPPUNIT_ASSERT(tpm->getProfileMap().size() == 6);
CPPUNIT_ASSERT(orig_id != clone1_id); CPPUNIT_ASSERT(orig != clone1);
CPPUNIT_ASSERT(clone0_id != clone1_id); CPPUNIT_ASSERT(clone0 != clone1);
/* 3. Clone state should be independent */
auto diff_conf = [this](const uint64_t id0, const uint64_t id1) {
tpm->addressStreamReconfigure(id0, 0x11, 0x123, Profile::READ);
tpm->addressStreamReconfigure(id1, 0xFF, 0x321, Profile::READ);
};
/* 3.1 Original activated, one Packet per send, Original terminated,
Clones not terminated */
diff_conf(orig_id, clone0_id); orig->activate();
locked = false ; next = time = 0;
for (uint64_t txn { 0 }; txn < config_0.fifo().total_txn(); ++txn) {
auto packets = tpm->send(locked, next, time);
CPPUNIT_ASSERT(packets.size() == 1);
for (auto &packet : packets) {
Packet *p { packet.second }; p->set_cmd(Command::READ_RESP);
tpm->receive(0, p);
}
}
// Handle remaining transactions
for (uint64_t txn { 0 }; txn < config_1.fifo().total_txn(); ++txn) {
for (auto &packet : tpm->send(locked, next, time)) {
Packet *p { packet.second }; p->set_cmd(Command::READ_RESP);
tpm->receive(0, p);
}
}
CPPUNIT_ASSERT(tpm->streamTerminated(orig_id));
CPPUNIT_ASSERT(!tpm->streamTerminated(clone0_id));
CPPUNIT_ASSERT(!tpm->streamTerminated(clone1_id));
// 3.2 Original and Clone activated, two Packets per Send, different
// addresses as configured, reset of Original does not reset Clone
reset(); tpm->streamCacheUpdate(); orig_id = tpm->profileId(profile_0);
tpm->uniqueStream(orig_id); clone0_id = tpm->uniqueStream(orig_id);
orig = tpm->getProfile(orig_id); clone0 = tpm->getProfile(clone0_id);
diff_conf(orig_id, clone0_id); orig->activate(); clone0->activate();
locked = false ; next = time = 0;
for (uint64_t txn { 0 }; txn < config_0.fifo().total_txn(); ++txn) {