summaryrefslogtreecommitdiffstats
path: root/eng/fv/appendix-fv.rst
blob: 687679a70d93963c8334e7726ceba7e589657d1b (plain) (blame)
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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
.. SPDX-License-Identifier: CC-BY-SA-4.0

.. Copyright (C) 2022 Trinity College Dublin

Appendix: RTEMS Formal Model Guide
**********************************

This appendix covers the various formal models of RTEMS that are currently in
existence. It serves two purposes:
one is to provide detailed documentation of each model,
while the other is provide a guide into how to go about developing and deploying such models.

The general approach followed here is to start by looking at the API documentation and identifying the key data-structures and function prototypes.
These are then modelled appropriately in Promela.
Then, general behavior patterns of interest are identified,
and the Promela model is extended to provide those patterns.
A key aspect here is exploiting the fact that Promela allows non-deterministic choices to be specified, which gives the effect of producing arbitrary orderings of model behavior.
All of this leads to a situation were the SPIN model-checker can effectively generate scenarios for all possible interleavings.
The final stage is mapping those scenarios to RTEMS C test code,
which has two parts: generating machine-readable output from SPIN, and developing the refinement mapping from that output to C test code.

Some familiarity is assumed here with the Software Test Framework section in this document.

The following models are included in the directory ``formal/promela/models/``
at the top-level in ``rtems-central``:

Chains API (``chains/``)
    Models the unprotected chain append and get API calls in the Classic
    Chains API Guide. This was an early model to develop the basic methodology.

Events Manager (``events/``)
    Models the behaviour of all the API calls in the Classic Events Manager API
    Guide. This had to tackle real concurrency and deal with multiple CPUs and priority
    issues.

Barrier Manager (``barriers/``)
    Models the behaviour of all the API calls in then Classic Barrier Manager API.

Message Manager (``messages/``)
    Models the create, send and receive API calls in the Classic Message Manager API.

At the end of this guide is a section that discusses various issues that should be tackled in future work.

Testing Chains
--------------

Documentation:  Chains section in the RTEMS Classic API Guide.

Model Directory: ``formal/promela/models/chains``.

Model Name: ``chains-api-model``.

The Chains API provides a doubly-linked list data-structure, optimised for fast
operations in an SMP setting. It was used as proof of concept exercise,
and focussed on just two API calls: ``rtems-chain-append-unprotected``
and ``rtems-chain-get-unprotected`` (hereinafter just ``append`` and ``get``).


API Model
^^^^^^^^^

File: ``chains-api-model.pml``

While smart code optimization techniques are very important for RTEMS code,
the focus when constructing formal models is on functional correctness,
not performance. What is required is the simplest, most obviously correct model.

The ``append`` operation adds new nodes on the end of the list,
while ``get`` removes and returns the node at the start of the list.
The Chains API has many other operations that can add/remove nodes at either end, or somewhere in the middle, but these are considered out of scope.

Data Structures
~~~~~~~~~~~~~~~

There are no pointers in Promela, so we have to use arrays,
with array indices modelling pointers.
With just ``append`` and ``get``, an array can be used to implement a collection
of nodes in memory.
A ``Node`` type is defined that has next and previous indices,
plus an item payload.
Access to the node list is via a special control node with head and tail pointers.
In the model, an explicit size value is added to this control node,
to allow the writing of properties about chain length,
and to prevent array out-of-bound errors in the model itself.
We assume a single ``chain``,
with list node storage statically allocated in ``memory``.

.. code:: c

  #define PTR_SIZE 3
  #define MEM_SIZE 8

  typedef Node {
    unsigned nxt  : PTR_SIZE
  ; unsigned prv  : PTR_SIZE
  ; byte     itm
  }
  Node memory[MEM_SIZE] ;

  typedef Control {
    unsigned head : PTR_SIZE;
    unsigned tail : PTR_SIZE;
    unsigned size : PTR_SIZE
  }
  Control chain ;

While there are 8 memory elements, element 0 is inaccessible,
as the index 0 is treated like a ``NULL`` pointer.

Function Calls
~~~~~~~~~~~~~~

The RTEMS prototype for ``append`` is:

.. code:: c

  void rtems_chain_append_unprotected(
      rtems_chain_control *the_chain,
      rtems_chain_node    *the_node
  );

Its implementation starts by checking that the node to be appended is "off
chain", before performing the append.
The model is designed to satisfy this property so the check is not modelled.
Also, the Chains documentation is not clear about certain error cases.
As this is a proof of concept exercise, these details are not modelled.

A Promela inline definition ``append`` models the desired behavior,
simulating C pointers with array addresses. Here ``ch`` is the chain argument,
while ``np`` is a node index.
The model starts by checking that the node pointer is not ``NULL``,
and that there is room in ``memory`` for another node.
These are to ensure that the model does not have any runtime errors.
Doing a standard model-check of this model finds no errors,
which indicates that those assertions are never false.

.. code:: c

  inline append(ch,np) {
    assert(np!=0); assert(ch.size < (MEM_SIZE-1));
    if
    :: (ch.head == 0) -> ch.head = np; ch.tail = np; ch.size = 1;
                         memory[np].nxt = 0; memory[np].prv = 0;
    :: (ch.head != 0) -> memory[ch.tail].nxt = np; memory[np].prv = ch.tail;
                         ch.tail = np; ch.size = ch.size + 1;
    fi
  }

The RTEMS prototype for ``get`` is:

.. code:: c

  rtems_chain_node *rtems_chain_get_unprotected(
    rtems_chain_control *the_chain
  );

It returns a pointer to the node, with ``NULL`` returned if the chain is empty.

Promela inlines involve textual substitution,
so the concept of returning a value makes no sense.
For ``get``,  the model is that of a statement that assigns the return value to
a variable. Both the function argument and return variable name are passed as parameters:

.. code:: c

  /* np = get(ch); */
  inline get(ch,np) {
    np = ch.head ;
    if
      :: (np != 0) ->
          ch.head = memory[np].nxt;
          ch.size = ch.size - 1;
          // memory[np].nxt = 0
      :: (np == 0) -> skip
    fi
    if
      :: (ch.head == 0) -> ch.tail = 0
      :: (ch.head != 0) -> skip
    fi
  }

Behavior patterns
^^^^^^^^^^^^^^^^^

File: ``chains-api-model.pml``

A key feature of using a modelling language like Promela is that it has both
explicit and implicit non-determinism. This can be exploited so that SPIN will
find all possible interleavings of behavior.

The Chains API model consists of six processes, three which perform ``append``,
and three that perform ``get``, waiting if the chain is empty. This model relies
on implicit non-determinism, in that the SPIN scheduler can choose and switch
between any unblocked process at any point. There is no explicit non-determinism
in this model.

Promela process ``doAppend`` takes node index ``addr`` and a value ``val`` as
parameters. It puts ``val`` into the node indexed by ``addr``,
then calls ``append``, and terminates.
It is all made atomic to avoid unnecessary internal interleaving of operations because unprotected versions of API calls should only be used when interrupts
are disabled.

.. code:: c

  proctype doAppend(int addr; int val) {
    atomic{ memory[addr].itm = val;
            append(chain,addr); } ;
  }

The ``doNonNullGet`` process waits for the chain to be non-empty before attempting to ``get`` an element. The first statement inside the atomic
construct is an expression, as a statements, that blocks while it evaluates to
zero. That only happens if ``head`` is in fact zero. The model also has an
assertion that checks that a non-null node is returned.

.. code:: c

  proctype doNonNullGet() {
    atomic{
      chain.head != 0;
      get(chain,nptr);
      assert(nptr != 0);
    } ;
  }

All processes terminate after they have performed their (sole) action.

The top-level of a Promela model is an initial process declared by the ``init`` construct. This initializes the chain as empty and then runs all six processes concurrently. It then uses the special ``_nr_pr`` variable to wait for all six
processes to terminate. A final assertion checks that the chain is empty.

.. code:: c

  init {
    pid nr;
    chain.head = 0; chain.tail = 0; chain.size = 0 ;
    nr = _nr_pr;  // assignment, sets `nr` to current number of procs
    run doAppend(6,21);
    run doAppend(3,22);
    run doAppend(4,23);
    run doNonNullGet();
    run doNonNullGet();
    run doNonNullGet();
    nr == _nr_pr; // expression, waits until number of procs equals `nr`
    assert (chain.size == 0);
  }

Simulation of this model will show some execution sequence in which the appends
happen in a random order, and the gets also occur in a random order, whenever
the chain is not empty. All assertions are always satisfied, including the last
one above. Model checking this model explores all possible interleavings and reports no errors of any kind. In particular, when the model reaches the last
assert statement, the chain size is always zero.

SPIN uses the C pre-processor, and generates the model as a C program. This
model has a simple flow of control: basically execute each process once in an
almost arbitrary order, assert that the chain is empty, and terminate. Test
generation here just requires the negation of the final assertion to get all
possible interleavings. The special C pre-processor definition ``TEST_GEN`` is
used to switch between the two uses of the model. The last line above is
replaced by:

.. code:: c

  #ifdef TEST_GEN
    assert (chain.size != 0);
  #else
    assert (chain.size == 0);
  #endif

A test generation run can then be invoked by passing in ``-DTEST_GEN`` as a
command-line argument.

Annotations
^^^^^^^^^^^

File: ``chains-api-model.pml``

The model needs to have ``printf`` statements added to generation the
annotations used to perform the test generation.

This model wraps each of six API calls in its own process, so that model
checking can generate all feasible interleavings. However, the plan for the test code is that it will be just one RTEMS Task, that executes all the API
calls in the order determined by the scenario under consideration. All the
annotations in this model specify ``0`` as the Promela process identifier.

Data Structures
~~~~~~~~~~~~~~~

Annotations have to be provided for any variable or datastructure declarations
that will need to have corresponding code in the test program.
These have to be printed out as the model starts to run.
For this model, the ``MAX_SIZE`` parameter is important,
as are the variables ``memory``, ``nptr``, and ``chain``:

.. code:: c

  printf("@@@ 0 NAME Chain_AutoGen\n")
  printf("@@@ 0 DEF MAX_SIZE 8\n");
  printf("@@@ 0 DCLARRAY Node memory MAX_SIZE\n");
  printf("@@@ 0 DECL unsigned nptr NULL\n")
  printf("@@@ 0 DECL Control chain\n");

At this point, a parameter-free initialization annotation is issued. This should
be refined to C code that initializes the above variables.

.. code:: c

  printf("@@@INIT\n");

Function Calls
~~~~~~~~~~~~~~

For ``append``, two forms of annotation are produced. One uses the ``CALL``
format to report the function being called along with its arguments. The other
form reports the resulting contents of the chain.

.. code:: c

   proctype doAppend(int addr; int val) {
     atomic{ memory[addr].itm = val; append(chain,addr);
             printf("@@@ 0 CALL append %d %d\n",val,addr);
             show_chain();
           } ;
   }

The statement ``show_chain()`` is an inline function that prints the
contents of the chain after append returns.
The resulting output is multi-line,
starting with ``@@@ 0 SEQ chain``,
ending with ``@@@ 0 END chain``,
and with entries in between of the form ``@@@ 0 SCALAR _ val``
displaying chain elements, line by line.

Something similar is done for ``get``, with the addition of a third annotation
``show_node()`` that shows the node that was got:

.. code:: c

  proctype doNonNullGet() {
    atomic{
      chain.head != 0;
      get(chain,nptr);
      printf("@@@ 0 CALL getNonNull %d\n",nptr);
      show_chain();
      assert(nptr != 0);
      show_node();
    } ;
  }

The statement ``show_node()`` is defined as follows:

.. code:: c

  inline show_node (){
    atomic{
      printf("@@@ 0 PTR nptr %d\n",nptr);
      if
      :: nptr -> printf("@@@ 0 STRUCT nptr\n");
                 printf("@@@ 0 SCALAR itm %d\n", memory[nptr].itm);
                 printf("@@@ 0 END nptr\n")
      :: else -> skip
      fi
    }
  }

It prints out the value of ``nptr``, which is an array index. If it is not zero,
it prints out some details of the indexed node structure.

Annotations are also added to the ``init`` process to show the chain and node.

.. code:: c

  chain.head = 0; chain.tail = 0; chain.size = 0;
  show_chain();
  show_node();

Refinement
^^^^^^^^^^

Files:

  ``chains-api-model-rfn.yml``

  ``chains-api-model-pre.h``

  ``tr-chains-api-model.c``

Model annotations are converted to C test code using a YAML file that maps
single names to test code snippets into which parameters can be substituted.
Parameters are numbered from zero, and the ``n`` th parameter will be substituted
wherever ``{n}`` occurs in the snippet.

Refinement is more than just the above mapping from annotations to code. Some of
this code will refer to C variables, structures, and functions that are needed
to support the test. Some of these are declared ``chains-api-model-pre.h`` and implemented in ``tr-chains-api-model.c``.

Data Structures
~~~~~~~~~~~~~~~

The initialization generates one each of ``NAME``, ``DEF``, ``DCLARRAY``, and
``INIT`` annotations, and two ``DECL`` annotations.

The ``DEF`` entry is currently not looked up as it is automatically converted to a ``#define``.

The ``NAME`` annotation is used to declare the test case name, which is
stored in the global variable ``rtems_test_name``. The current
refinement entry is:

.. code:: python

   NAME: |
     const char rtems_test_name[] = "Model_Chain_API";

In this case, the name is fixed and ignores what is declared in the model.

The ``DCLARRAY Node memory MAX_SIZE`` annotation looks up ``memory_DCL`` in the
refinement file, passing in ``memory`` and ``MAX_SIZE`` as arguments. The entry in the refinement file is:

.. code:: python

  memory_DCL: item {0}[{1}];

Here ``item`` is the type of the chains nodes used in the test code. It is
declared in ``chains-api-model.pre.h`` as:

.. code:: c

  typedef struct item
  {
      rtems_chain_node node;
      int               val;
  } item;

Substituting the arguments gives:

.. code:: c

  item memory[MAX_SIZE];

The two ``DECL`` annotations have two or three parameters. The first is the
type, the second is the variable name, and the optional third is an initial
value. The lookup key is the variable name with ``_DCL`` added on.
In the refinement file, the entry only provides the C type, and the other parts of the declaration are added in.
The entries are:

.. code:: python

  nptr_DCL: item *
  chain_DCL: rtems_chain_control

Annotation ``DECL unsigned nptr NULL`` results in:

.. code:: c

  item * nptr = NULL ;

Annotation ``DECL Control chain`` results in:

.. code:: c

  rtems_chain_control chain ;

The ``INIT`` annotation is looked up as ``INIT`` itself. It should be mapped to
code that does all necessary initialization. The refinement entry for chains is:

.. code:: python

  INIT: rtems_chain_initialize_empty( &chain );

In addition to all the above dealing with declarations and initialization,
there are the annotations,  already described above, that are used to display
composite values, such as structure contents, and chain contents.

In the model, all accesses to individual chain nodes are via index ``nptr``,
which occurs in two types of annotations, ``PTR`` and ``STRUCT``. The ``PTR``
annotation is refined by looking up ``nptr_PTR`` with the value of ``nptr`` as the sole argument. The refinement entry is:

.. code:: python

  nptr_PTR: |
    T_eq_ptr( nptr, NULL );
    T_eq_ptr( nptr, &memory[{0}] );

The first line is used if the value of ``nptr`` is zero, otherwise the second
line is used.

The use of ``STRUCT`` requires three annotation lines in a row, *e.g.*:

.. code:: c

  @@@ 0 STRUCT nptr
  @@@ 0 SCALAR itm 21
  @@@ 0 END nptr

The ``STRUCT`` and ``END`` annotations do not generate any code, but open and
close a scope in which ``nptr`` is noted as the "name" of the struct. The
``SCALAR`` annotation is used to observe simple values such as numbers or booleans. However, within a ``STRUCT`` it belongs to a C ``struct``, so the
relevant field needs to be used to access the value.
Within this scope, the scalar variable ``itm`` is looked up as a field name,
by searching for ``itm_FSCALAR`` with arguments``nptr`` and ``21``, which returns the entry:

.. code:: python

  itm_FSCALAR:   T_eq_int( {0}->val, {1} );

This gets turned into the following test:

.. code:: c

  T_eq_int( nptr->val, 21 );

A similar idea is used to test the contents of a chain. The annotations produced
start with a ``SEQ`` annotation, followed by a ``SCALAR`` annotation for each
item in the chain, and then ending with an ``END`` annotation. Again, there is
a scope defined where the ``SEQ`` argument is the "name" of the sequence.
The ``SCALAR`` entries have no name here (indicated by ``_``), and their values
are accumulated in a string, separated by spaces. Test code generation is
triggered this time by the ``END`` annotation, that looks up the "name" with ``_SEQ`` appended, and the accumulated string as an argument. The corresponding entry for chain sequences is:

.. code:: python

  chain_SEQ: |
    show_chain( &chain, ctx->buffer );
    T_eq_str( ctx->buffer, "{0} 0" );

So, the following chain annotation sequence:

.. code:: c

  @@@ 0 SEQ chain
  @@@ 0 SCALAR _ 21
  @@@ 0 SCALAR _ 22
  @@@ 0 END chain

becomes the following C code:

.. code:: C

  show_chain( &chain, ctx->buffer );
  T_eq_str( ctx->buffer, " 21 22 0" );

C function ``show_chain()`` is defined in ``tr-chains-api-model.c`` and
generates a string with exactly the same format as that produced above. These
are then compared for equality.

.. note::

  The Promela/SPIN model checker's prime focus is modelling and verifying
  concurrency related properties. It is not intended for verifying sequential
  code or data transformations. This is why some of the ``STRUCT``/``SEQ``
  material here is so clumsy. It plays virtually no role in the other models.

Function Calls
~~~~~~~~~~~~~~

For ``@@@ 0 CALL append 22 3`` lookup ``append`` to get

.. code-block:: c

     memory[{1}].val = {0};
     rtems_chain_append_unprotected( &chain, (rtems_chain_node*)&memory[{1}] );

Substitute ``22`` and ``3`` in to produce

.. code-block:: c

     memory[3].val = 22;
     rtems_chain_append_unprotected( &chain, (rtems_chain_node*)&memory[3] );

For ``@@@ 0 CALL getNonNull 3`` lookup ``getNonNull`` to obtain

.. code:: c

  nptr = get_item( &chain );
  T_eq_ptr( nptr, &memory[{0}] );

Function ``get_item()`` is defined in ``tc-chains-api-model.c`` and calls ``rtems_chain_get_unprotected()``. Substitute  ``3`` to produce:

.. code:: c

  nptr = get_item( &chain );
  T_eq_ptr( nptr, &memory[3] );

Testing Concurrent Managers
---------------------------

All the other models are of Managers that provide some form of communication
between multiple RTEMS Tasks. This introduces a number of issues regarding
the timing and control of tasks, particularly when developing *reproducible*
tests, where the sequencing of behavior is the same every time the test runs.
The tests are generated by following the schemes already in use for regular
RTEMS handwritten tests.
In particular the pre-existing tests for Send and Receive in the Event Manager
where used as a guide.

Testing Strategy
^^^^^^^^^^^^^^^^

The tests are organized as follows:

1. A designated Task, the Runner, is responsible for initializing,
   coordinating and tearing down a test run.
   Coordination involves starting other Worker Tasks that perform tests,
   and waiting for them to complete.
   It may also run some tests itself.

1. One or more Worker Tasks are used to perform test actions.

1. Each RTEMS Task (Runner/Worker) is modelled by one Promela process.

1. Simple Binary Semaphores
   are used to coordinate all the tasks to ensure
   that the interleaving is always the same
   (See Semaphore Manager section in Classic API Manual).

1. Two other Promela processes are required:
   One, called ``Clock()`` is used to model timing and timeouts;
   The other, called ``System()`` models relevant behavior of the RTEMS scheduler.

Model Structure
^^^^^^^^^^^^^^^

All the models developed so far are based on this framework.
The structure of these models takes the following form:

  Constant Declarations
     Mainly ``#define``\ s that define important constants.

  Datastructure Definitions
    Promela ``typedef``\ s that describe key forms of data.
    In particular there is a type ``Task`` that models RTEMS Tasks.
    The Simple Binary Semaphores are modelled as boolean variables.

  Global Variable Declarations
    Typically these are arrays of data-structures,
    representing objects such as tasks or semaphores.

  Supporting Models
    These are ``inline`` definitions that capture common behavior.
    In all models this includes ``Obtain()`` and ``Release()`` to model semaphores,
    and ``waitUntilReady()`` that models a blocked task waiting to be unblocked.
    Included here are other definitions specific to the particular Manager being
    modelled.

  API Models
    These are ``inline`` definitions that model the behavior of each API call.
    All behavior must be modelled, including bad calls that (should) result in
    an error code being returned.
    The parameter lists used in the Promela models will differ from those
    of the actual API calls.
    Consider a hypothetical RTEMS API call:

    .. code:: c

      rc = rtems_some_api(arg1,arg2,...,argN);

    One reason, common to all calls, is that the ``inline`` construct has
    no concept of returning a value,
    so a variable parameter has to be added to represent it,
    and it has to be ensured the appropriate return code is assigned to it.

    .. code:: promela

      inline some_api(arg1,arg2,...,argN,rc) {
        ...
        rc = RC_some_code
      }

    Another reason is that some RTEMS types encode a number of different
    concepts in a single machine word.
    The most notable of these is the ``rtems_options`` type,
    that specifies various options, usually for calls that may block.
    In some models, some options are modelled individually, for clarity.
    So the API model may have two or more parameters where the RTEMS call has one.

    .. code:: promela

      inline some_api(arg1,arg2feat1,arg2feat2,...,argN,rc) {
        ...
        rc = RC_some_code
      }

    The refinement of this will pass the multiple feature arguments to
    a C function that will assemble the single RTEMS argument.

    A third reason is that sometimes it is important to also provide
    the process id of the *calling* task. This can be important where
    priority and preemption are involved.

  Scenario Generation
    A Testsuite that exercises *all* the API code is highly desirable.
    This requires running tests that explore a wide range of scenarios,
    normally devised by hand when writing a testsuite.
    With Promela/SPIN, the model-checker can generate all of these simplify
    as a result of its exhaustive search of the model.
    In practice, scenarios fall into a number of high-level categories,
    so the first step is make a random choice of such a category.
    Within a category there may be further choices to be done.
    A collection of global scenario variables are used to records the choices made.
    This is all managed by inline ``chooseScenario()``.

  RTEMS Test Task Modelling
    This is a series of Promela ``proctype``\ s, one for the Runner Task,
    and one for each of the Worker Tasks.
    Their behavior is controlled by the global scenario variables.

  System Modelling
    These are Promela processes that model relevant underlying RTEMS behavior,
    such as the scheduler (``System()``) and timers (``Clock()``).

  Model Main Process
    Called ``init``, this initialises variables, invokes ``chooseScenario()``,
    runs all the processes, waits for them to terminate,
    and then terminates itself.

The Promela models developed so far for these Managers always terminate.
The last few lines of each are of the form:

.. code:: promela

  #ifdef TEST_GEN
    assert(false);
  #endif

If these models are run in the usual way (simulation or verification),
then a correct verified model is observed.
If ``-DTEST_GEN`` is provided as the first command-line argument,
in verification mode configured to find *all* counterexamples,
then all the possible (correct) behaviours of the system will be generated.

Transforming Model Behavior to C Code
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

As described earlier, ``printf`` statements are used
to produce easy to parse output that makes
model events and outcomes easy to identify and process.
The YAML file used to define the refinement from model to code
provides a way of looking up an observation with arguments,
and then obtaining a C template that can be populated with those arguments.

This refinement is a bridge between two distinct worlds:

  Model World:
    where the key focus is on correctness and clarity.

  Code World:
    where clarity is often sacrificed for efficiency.

This means that the model-to-code relationship
need not be a simple one-to-one mapping.
This has already been alluded to above when the need for extra parameters
in API call models was discussed.
It can also be helpful when the model is better treating various attributes
separately, while the code handles those attributes packed into machine words.

It is always reasonable to add test support code to the C test sources,
and this can include C functions that map distinct attribute values
down into some compact merged representation.


Testing the Event Manager
-------------------------

Documentation:  Event Manager section in the RTEMS Classic API Guide.

Model Directory: ``formal/promela/models/events``.

Model Name: ``event-mgr-model``.

The Event Manager allows tasks to send events to,
and receive events from, other tasks.
From the perspective of the Event Manager,
events are just uninterpreted numbers in the range 0..31,
encoded as a 32-bit bitset.

``rtems_event_send(id,event_in)``
  allows a task to send a bitset to a designated task

``rtems_event_receive(event_in,option_set,ticks,event_out)``
  allows a task to specify a desired bitset
  with options on what to do if it is not present.

Most of the requirements are pretty straightforward,
but two were a little more complex,
and drove the more complex parts of the modelling.

1. If a task was blocked waiting to receive events,
   and a lower priority task then sent the events that would wake that
   blocked task,
   then the sending task would be immediately preempted by the receiver task.

2. There was a requirement that explicitly discussed the situation
   where the two tasks involved were running on different processors.

A preliminary incomplete model of the Event Manager was originally developed
by the consortium early in the project. The model was then completed during
the rest of the activity by a Masters student: :cite:`Jennings:2021:FV`.
They also developed the first iteration of the ``testbuilder`` program.

API Model
^^^^^^^^^

File: ``event-mgr-model.pml``

The RTEMS Event set contains 32 values, but in the model limits this to
just four, which is enough for test purposes.
Some inline definitions are provided to encode (``events``), display
(``printevents``), and subtract (``setminus``) events.

The ``rtems_option_set`` is simplifiedto just two relevant bits: the timeout
setting (``Wait``, ``NoWait``), and how much of the desired event set will
satisfy the receiver (``All``, ``Any``).
These are passed in as two separate arguments to the model of the receive call.

Event Send
~~~~~~~~~~

An RTEMS call ``rc = rtems_event_send(tid,evts)`` is modelled by an inline of
the form:

.. code-block:: c

   event_send(self,tid,evts,rc)

The four arguments are:
 | ``self`` : id of process modelling the task/IDR making call.
 | ``tid``  : id of process modelling the target task of the call.
 | ``evts`` : event set being sent.
 | ``rc``   : updated with the return code when the send completes.

The main complication in the otherwise straightforward model is the requirement
to preempt under certain circumstances.

.. code-block:: c

   inline event_send(self,tid,evts,rc) {
     atomic{
       if
       ::  tid >= BAD_ID -> rc = RC_InvId
       ::  tid < BAD_ID ->
           tasks[tid].pending = tasks[tid].pending | evts
           // at this point, have we woken the target task?
           unsigned got : NO_OF_EVENTS;
           bool sat;
           satisfied(tasks[tid],got,sat);
           if
           ::  sat ->
               tasks[tid].state = Ready;
               printf("@@@ %d STATE %d Ready\n",_pid,tid)
               preemptIfRequired(self,tid) ;
               // tasks[self].state may now be OtherWait !
               waitUntilReady(self);
           ::  else -> skip
           fi
           rc = RC_OK;
       fi
     }
   }

Three inline abstractions are used:

``satisfied(task,out,sat)``
    updates ``out`` with the wanted events received so far, and then checks if a receive has been satisfied. It
    updates its ``sat`` argument to reflect the check outcome.

``preemptIfRequired(self,tid)``
   forces the sending process to enter the ``OtherWait``,
   if circumstances require it.

``waitUntilReady(self)``
   basically waits for the process state to become ``Ready``.

Event Receive
~~~~~~~~~~~~~

An RTEMS call ``rc = rtems_event_receive(evts,opts,interval,out)`` is modelled
by an inline of
the form:

.. code-block:: c

   event_receive(self,evts,wait,wantall,interval,out,rc)

The seven arguments are:
 | ``self`` : id of process modelling the task making call
 | ``evts`` : input event set
 | ``wait`` : true if receive should wait
 | ``what`` : all, or some?
 | ``interval`` : wait interval (0 waits forever)
 | ``out`` : pointer to location for satisfying events when the receive
     completes.
 | ``rc`` : updated with the return code when the receive completes.


There is a small complication, in that there are distinct variables in the model
for receiver options that are combined into a single RTEMS option set. The
actual calling sequence in C test code will be:

.. code-block:: c

   opts = mergeopts(wait,wantall);
   rc = rtems_event_receive(evts,opts,interval,out);

Here ``mergeopts`` is a C function defined in the C Preamble.

.. code-block:: c

   inline event_receive(self,evts,wait,wantall,interval,out,rc){
     atomic{
       printf("@@@ %d LOG pending[%d] = ",_pid,self);
       printevents(tasks[self].pending); nl();
       tasks[self].wanted = evts;
       tasks[self].all = wantall
       if
       ::  out == 0 ->
           printf("@@@ %d LOG Receive NULL out.\n",_pid);
           rc = RC_InvAddr ;
       ::  evts == EVTS_PENDING ->
           printf("@@@ %d LOG Receive Pending.\n",_pid);
           recout[out] = tasks[self].pending;
           rc = RC_OK
       ::  else ->
           bool sat;
           retry:  satisfied(tasks[self],recout[out],sat);
           if
           ::  sat ->
               printf("@@@ %d LOG Receive Satisfied!\n",_pid);
               setminus(tasks[self].pending,tasks[self].pending,recout[out]);
               printf("@@@ %d LOG pending'[%d] = ",_pid,self);
               printevents(tasks[self].pending); nl();
               rc = RC_OK;
           ::  !sat && !wait ->
               printf("@@@ %d LOG Receive Not Satisfied (no wait)\n",_pid);
               rc = RC_Unsat;
           ::  !sat && wait && interval > 0 ->
               printf("@@@ %d LOG Receive Not Satisfied (timeout %d)\n",_pid,interval);
               tasks[self].ticks = interval;
               tasks[self].tout = false;
               tasks[self].state = TimeWait;
               printf("@@@ %d STATE %d TimeWait %d\n",_pid,self,interval)
               waitUntilReady(self);
               if
               ::  tasks[self].tout  ->  rc = RC_Timeout
               ::  else              ->  goto retry
               fi
           ::  else -> // !sat && wait && interval <= 0
               printf("@@@ %d LOG Receive Not Satisfied (wait).\n",_pid);
               tasks[self].state = EventWait;
               printf("@@@ %d STATE %d EventWait\n",_pid,self)
               if
               :: sendTwice && !sentFirst -> Released(sendSema);
               :: else
               fi
               waitUntilReady(self);
               goto retry
           fi
       fi
       printf("@@@ %d LOG pending'[%d] = ",_pid,self);
       printevents(tasks[self].pending); nl();
     }
   }

Here ``satisfied()`` and ``waitUntilReady()`` are also used.

Behaviour Patterns
^^^^^^^^^^^^^^^^^^

File: ``event-mgr-model.pml``

The Event Manager model consists of
five Promela processes:

``init``
    The first top-level Promela process that performs initialisation,
    starts the other processes, waits for them to terminate, and finishes.

``System``
    A Promela process that models the behaviour of the operating system,
    in particular that of the scheduler.

``Clock``
    A Promela process used to facilitate modelling timeouts.

``Receiver``
    The Promela process modelling the test Runner,
    that also invokes the receive API call.

``Sender``
    A Promela process modelling a singe test Worker
    that invokes the send API call.


Two simple binary semaphores are used to synchronise the tasks.

The Task model only looks at an abstracted version of RTEMS Task states:

``Zombie``
    used to model a task that has just terminated. It can only be deleted.

``Ready``
    same as the RTEMS notion of ``Ready``.

``EventWait``
    is ``Blocked`` inside a call of ``event_receive()`` with no timeout.

``TimeWait``
    is ``Blocked`` inside a call of ``event_receive()`` with a timeout.

``OtherWait``
    is ``Blocked`` for some other reason, which arises in this model when a
    sender gets pre-empted by a higher priority receiver it has just satisfied.


Tasks are represented using a datastructure array.
As array indices are proxies here for C pointers,
the zeroth array entry is always unused,
as index value 0 is used to model a NULL C pointer.

.. code-block:: c

   typedef Task {
     byte nodeid; // So we can spot remote calls
     byte pmlid; // Promela process id
     mtype state ; // {Ready,EventWait,TickWait,OtherWait}
     bool preemptable ;
     byte prio ; // lower number is higher priority
     int ticks; //
     bool tout; // true if woken by a timeout
     unsigned wanted  : NO_OF_EVENTS ; // EvtSet, those expected by receiver
     unsigned pending : NO_OF_EVENTS ; // EvtSet, those already received
     bool all; // Do we want All?
   };
   Task tasks[TASK_MAX]; // tasks[0] models a NULL dereference

.. code-block:: c

   byte sendrc;            // Sender global variable
   byte recrc;             // Receiver global variable
   byte recout[TASK_MAX] ; // models receive 'out' location.

Task Scheduling
~~~~~~~~~~~~~~~

In order to produce a model that captures real RTEMS Task behaviour,
there need to be mechanisms that mimic the behaviour of the scheduler and other
activities that can modify the execution state of these Tasks. Given a scenario
generated by such a model, synchronisation needs to be added to the generated C
code to ensure test has the same execution patterns.

Relevant scheduling behavior is modelled by two inlines that have already
been mentioned: ``waitUntilReady()`` and ``preemptIfRequired()``.

For synchronisation, simple boolean semaphores are used, where True means
available, and False means the semaphore has been acquired.

.. code-block:: c

   bool semaphore[SEMA_MAX]; // Semaphore

The synchronisation mechanisms are:


``Obtain(sem_id)``
   call that waits to obtain semaphore ``sem_id``.

``Release(sem_id)``
    call that releases semaphore ``sem_id``

``Released(sem_id)``
    simulates ecosystem behaviour that releases ``sem_id``.

The difference between ``Release`` and ``Released`` is that the first issues
a ``SIGNAL`` annotation, while the second does not.


Scenarios
~~~~~~~~~

A number of different scenario schemes were defined
that cover various aspects of
Event Manager behaviour. Some schemes involve only one task, and are usually
used to test error-handling or abnormal situations. Other schemes involve two
tasks, with some mixture of event sending and receiving, with varying task
priorities.

For example, an event send operation can involve a target identifier that
is invalid (``BAD_ID``), correctly identifies a receiver task (``RCV_ID``), or
is sending events to itself (``SEND_ID``).

.. code-block:: c

   typedef SendInputs {
     byte target_id ;
     unsigned send_evts : NO_OF_EVENTS ;
   } ;
   SendInputs  send_in[MAX_STEPS];

An event receive operation will be determined by values for desired events,
and the relevant to bits of the option-set parameter.

.. code-block:: c

   typedef ReceiveInputs {
     unsigned receive_evts : NO_OF_EVENTS ;
     bool will_wait;
     bool everything;
     byte wait_length;
   };
   ReceiveInputs receive_in[MAX_STEPS];

There is a range of global variables that define scenarios for both send and
receive. This defines a two-step process for choosing a scenario.
The first step is to select a scenario scheme. The possible schemes are
defined by the following ``mtype``:

.. code-block:: c

   mtype = {Send,Receive,SndRcv,RcvSnd,SndRcvSnd,SndPre,MultiCore};
   mtype scenario;

One of these is chosen by using a conditional where all alternatives are
executable, so behaving as a non-deterministic choice of one of them.

.. code-block:: c

   if
   ::  scenario = Send;
   ::  scenario = Receive;
   ::  scenario = SndRcv;
   ::  scenario = SndPre;
   ::  scenario = SndRcvSnd;
   ::  scenario = MultiCore;
   fi


Once the value of ``scenario`` is chosen, it is used in another conditional
to select a non-deterministic choice of the finer details of that scenario.

.. code-block:: c

    if
    ::  scenario == Send ->
          doReceive = false;
          sendTarget = BAD_ID;
    ::  scenario == Receive ->
          doSend = false
          if
          :: rcvWait = false
          :: rcvWait = true; rcvInterval = 4
          :: rcvOut = 0;
          fi
          printf( "@@@ %d LOG sub-senario wait:%d interval:%d, out:%d\n",
                  _pid, rcvWait, rcvInterval, rcvOut )
    ::  scenario == SndRcv ->
          if
          ::  sendEvents = 14; // {1,1,1,0}
          ::  sendEvents = 11; // {1,0,1,1}
          fi
          printf( "@@@ %d LOG sub-senario send-receive events:%d\n",
                  _pid, sendEvents )
    ::  scenario == SndPre ->
          sendPrio = 3;
          sendPreempt = true;
          startSema = rcvSema;
          printf( "@@@ %d LOG sub-senario send-preemptable events:%d\n",
                  _pid, sendEvents )
    ::  scenario == SndRcvSnd ->
          sendEvents1 = 2; // {0,0,1,0}
          sendEvents2 = 8; // {1,0,0,0}
          sendEvents = sendEvents1;
          sendTwice = true;
          printf( "@@@ %d LOG sub-senario send-receive-send events:%d\n",
                  _pid, sendEvents )
    ::  scenario == MultiCore ->
          multicore = true;
          sendCore = 1;
          printf( "@@@ %d LOG sub-senario multicore send-receive events:%d\n",
                  _pid, sendEvents )
    ::  else // go with defaults
    fi

Ddefault values are defined for all the global scenario variables so that the
above code focusses on what differs. The default scenario is a receiver waiting
for a sender of the same priority which sends exactly what was requested.

Sender Process (Worker Task)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The sender process then uses the scenario configuration to determine its
behaviour. A key feature is the way it acquires its semaphore before doing a
send, and releases the receiver semaphore when it has just finished sending.
Both these semaphores are initialised in the unavailable state.

.. code-block:: c

   proctype Sender (byte nid, taskid) {

     tasks[taskid].nodeid = nid;
     tasks[taskid].pmlid = _pid;
     tasks[taskid].prio = sendPrio;
     tasks[taskid].preemptable = sendPreempt;
     tasks[taskid].state = Ready;
     printf("@@@ %d TASK Worker\n",_pid);
     if
     :: multicore ->
          // printf("@@@ %d CALL OtherScheduler %d\n", _pid, sendCore);
          printf("@@@ %d CALL SetProcessor %d\n", _pid, sendCore);
     :: else
     fi
     if
     :: sendPrio > rcvPrio -> printf("@@@ %d CALL LowerPriority\n", _pid);
     :: sendPrio == rcvPrio -> printf("@@@ %d CALL EqualPriority\n", _pid);
     :: sendPrio < rcvPrio -> printf("@@@ %d CALL HigherPriority\n", _pid);
     :: else
     fi
   repeat:
     Obtain(sendSema);
     if
     :: doSend ->
       if
       :: !sentFirst -> printf("@@@ %d CALL StartLog\n",_pid);
       :: else
       fi
       printf("@@@ %d CALL event_send %d %d %d sendrc\n",_pid,taskid,sendTarget,sendEvents);
       if
       :: sendPreempt && !sentFirst -> printf("@@@ %d CALL CheckPreemption\n",_pid);
       :: !sendPreempt && !sentFirst -> printf("@@@ %d CALL CheckNoPreemption\n",_pid);
       :: else
       fi
       event_send(taskid,sendTarget,sendEvents,sendrc);
       printf("@@@ %d SCALAR sendrc %d\n",_pid,sendrc);
     :: else
     fi
     Release(rcvSema);
     if
     :: sendTwice && !sentFirst ->
        sentFirst = true;
        sendEvents = sendEvents2;
        goto repeat;
     :: else
     fi
     printf("@@@ %d LOG Sender %d finished\n",_pid,taskid);
     tasks[taskid].state = Zombie;
     printf("@@@ %d STATE %d Zombie\n",_pid,taskid)
   }

Receiver Process (Runner Task)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The receiver process uses the scenario configuration to determine its
behaviour. It has the responsibility to trigger the start semaphore to allow
either itself or the sender to start. The start semaphore corresponds to either
the send or receive semaphore, depending on the scenario. The receiver acquires
the receive semaphore before proceeding, and releases the send sempahore when
done.

.. code-block:: c

   proctype Receiver (byte nid, taskid) {

     tasks[taskid].nodeid = nid;
     tasks[taskid].pmlid = _pid;
     tasks[taskid].prio = rcvPrio;
     tasks[taskid].preemptable = false;
     tasks[taskid].state = Ready;
     printf("@@@ %d TASK Runner\n",_pid,taskid);
     if
     :: multicore ->
          printf("@@@ %d CALL SetProcessor %d\n", _pid, rcvCore);
     :: else
     fi
     Release(startSema); // make sure stuff starts */
     /* printf("@@@ %d LOG Receiver Task %d running on Node %d\n",_pid,taskid,nid); */
     Obtain(rcvSema);

     // If the receiver is higher priority then it will be running
     // The sender is either blocked waiting for its semaphore
     // or because it is lower priority.
     // A high priority receiver needs to release the sender now, before it
     // gets blocked on its own event receive.
     if
     :: rcvPrio < sendPrio -> Release(sendSema);  // Release send semaphore for preemption
     :: else
     fi
     if
     :: doReceive ->
       printf("@@@ %d SCALAR pending %d %d\n",_pid,taskid,tasks[taskid].pending);
       if
       :: sendTwice && !sentFirst -> Release(sendSema)
       :: else
       fi
       printf("@@@ %d CALL event_receive %d %d %d %d %d recrc\n",
              _pid,rcvEvents,rcvWait,rcvAll,rcvInterval,rcvOut);
                 /* (self,  evts,     when,   what,  ticks,      out,   rc) */
       event_receive(taskid,rcvEvents,rcvWait,rcvAll,rcvInterval,rcvOut,recrc);
       printf("@@@ %d SCALAR recrc %d\n",_pid,recrc);
       if
       :: rcvOut > 0 ->
         printf("@@@ %d SCALAR recout %d %d\n",_pid,rcvOut,recout[rcvOut]);
       :: else
       fi
       printf("@@@ %d SCALAR pending %d %d\n",_pid,taskid,tasks[taskid].pending);
     :: else
     fi
     Release(sendSema);
     printf("@@@ %d LOG Receiver %d finished\n",_pid,taskid);
     tasks[taskid].state = Zombie;
     printf("@@@ %d STATE %d Zombie\n",_pid,taskid)
   }

System Process
~~~~~~~~~~~~~~

 A process is needed that periodically wakes up blocked processes.
 This is modelling background behaviour of the system,
 such as ISRs and scheduling.
 All tasks are visited in round-robin order (to prevent starvation)
 and are made ready if waiting on other things. Tasks waiting for events or timeouts are not touched. This terminates when all tasks are zombies.

.. code-block:: c

   proctype System () {
     byte taskid ;
     bool liveSeen;
     printf("@@@ %d LOG System running...\n",_pid);
     loop:
     taskid = 1;
     liveSeen = false;
     printf("@@@ %d LOG Loop through tasks...\n",_pid);
     atomic {
       printf("@@@ %d LOG Scenario is ",_pid);
       printm(scenario); nl();
     }
     do   // while taskid < TASK_MAX ....
     ::  taskid == TASK_MAX -> break;
     ::  else ->
         atomic {
           printf("@@@ %d LOG Task %d state is ",_pid,taskid);
           printm(tasks[taskid].state); nl()
         }
         if
         :: tasks[taskid].state == Zombie -> taskid++
         :: else ->
            if
            ::  tasks[taskid].state == OtherWait
                -> tasks[taskid].state = Ready
                   printf("@@@ %d STATE %d Ready\n",_pid,taskid)
            ::  else -> skip
            fi
            liveSeen = true;
            taskid++
         fi
     od
     printf("@@@ %d LOG ...all visited, live:%d\n",_pid,liveSeen);
     if
     ::  liveSeen -> goto loop
     ::  else
     fi
     printf("@@@ %d LOG All are Zombies, game over.\n",_pid);
     stopclock = true;
   }

Clock Process
~~~~~~~~~~~~~

A process is needed that handles a clock tick,
by decrementing the tick count for tasks waiting on a timeout.
Such a task whose ticks become zero is then made ``Ready``,
and its timer status is flagged as a timeout. This terminates when all
tasks are zombies (as signalled by ``System()`` via ``stopclock``).

.. code-block:: c

   proctype Clock () {
     int tid, tix;
     printf("@@@ %d LOG Clock Started\n",_pid)
     do
     ::  stopclock  -> goto stopped
     ::  !stopclock ->
         printf(" (tick) \n");
         tid = 1;
         do
         ::  tid == TASK_MAX -> break
         ::  else ->
             atomic{
               printf("Clock: tid=%d, state=",tid);
               printm(tasks[tid].state); nl()
             };
             if
             ::  tasks[tid].state == TimeWait ->
                 tix = tasks[tid].ticks - 1;
                 if
                 ::  tix == 0
                     tasks[tid].tout = true
                     tasks[tid].state = Ready
                     printf("@@@ %d STATE %d Ready\n",_pid,tid)
                 ::  else
                     tasks[tid].ticks = tix
                 fi
             ::  else // state != TimeWait
             fi
             tid = tid + 1
         od
     od
   stopped:
     printf("@@@ %d LOG Clock Stopped\n",_pid);
   }


init Process
~~~~~~~~~~~~

The initial process outputs annotations for defines and declarations,
generates a scenario non-deterministically and then starts the system, clock
and send and receive processes running. It then waits for those to complete,
and them, if test generation is underway, asserts ``false`` to trigger a
seach for counterexamples:

.. code-block:: c

   init {
     pid nr;
     printf("@@@ %d NAME Event_Manager_TestGen\n",_pid)
     outputDefines();
     outputDeclarations();
     printf("@@@ %d INIT\n",_pid);
     chooseScenario();
     run System();
     run Clock();
     run Sender(THIS_NODE,SEND_ID);
     run Receiver(THIS_NODE,RCV_ID);
     _nr_pr == 1;
   #ifdef TEST_GEN
     assert(false);
   #endif
   }

The information regarding when tasks should wait and/or restart
can be obtained by tracking the process identifiers,
and noting when they change.
The ``spin2test`` program does this,
and also produces separate test code segments for each Promela process.

Annotations
^^^^^^^^^^^

File: ``event-mgr-model.pml``

Nothing more to say here.

Refinement
^^^^^^^^^^

File: ``event-mgr-model-rfn.yml``


The test-code generated here is based on the test-code generated from the
specification items used to describe the Event Manager in the main (non-formal)
part of the new qualification material.

The relevant specification item is ``spec/rtems/event/req/send-receive.yml``
found in ``rtems-central``. The corresponding C test code is
``tr-event-send-receive.c`` found in ``rtems`` at ``testsuites/validation``.
That automatically generated C code is a single file that uses a set of deeply
nested loops to iterate through the scenarios it generates.

The approach here is to generate a stand-alone C code file for each scenario
(``tr-event-mgr-model-N.c`` for ``N`` in range 0..8.)


The ``TASK`` annotations issued by the ``Sender`` and ``Receiver`` processes
lookup the following refinement entries, to get code that tests that the C
code Task does correspond to what is being defined in the model.

.. code-block:: yaml

   Runner: |
     checkTaskIs( ctx->runner_id );

   Worker: |
     checkTaskIs( ctx->worker_id );

The ``WAIT`` and ``SIGNAL`` annotations produced by ``Obtain()`` and
``Release()`` respectively, are mapped to the corresponding operations on
RTEMS semaphores in the test code.

.. code-block:: yaml

   code content
   SIGNAL: |
     Wakeup( semaphore[{}] );

   WAIT: |
     Wait( semaphore[{}] );

Some of the ``CALL`` annotations are used to do more complex test setup
involving priorities, or other processors and schedulers. For example:

.. code-block:: yaml

   HigherPriority: |
     SetSelfPriority( PRIO_HIGH );
     rtems_task_priority prio;
     rtems_status_code sc;
     sc = rtems_task_set_priority( RTEMS_SELF, RTEMS_CURRENT_PRIORITY, &prio );
     T_rsc_success( sc );
     T_eq_u32( prio, PRIO_HIGH );

   SetProcessor: |
     T_ge_u32( rtems_scheduler_get_processor_maximum(), 2 );
     uint32_t processor = {};
     cpu_set_t cpuset;
     CPU_ZERO(&cpuset);
     CPU_SET(processor, &cpuset);

Some handle more complicated test outcomes, such as observing context-switches:

.. code-block:: yaml

   CheckPreemption: |
     log = &ctx->thread_switch_log;
     T_eq_sz( log->header.recorded, 2 );
     T_eq_u32( log->events[ 0 ].heir, ctx->runner_id );
     T_eq_u32( log->events[ 1 ].heir, ctx->worker_id );

Most of the other refinement entries are similar to those described above for
the Chains API.

Testing the Barrier Mananger
----------------------------

Documentation:  Barrier Manager section in the RTEMS Classic API Guide.

Model Directory: ``formal/promela/models/barriers``.

Model Name: ``barrier-mgr-model``.

The Barrier Manager is used to arrange for a number of tasks to wait on a
designated barrier object, until either another task releases them, or a
given number of tasks are waiting, at which point they are all released.

All five directives were modelled:

* ``rtems_barrier_create()``

* ``rtems_barrier_ident()``

* ``rtems_barrier_delete()``

* ``rtems_barrier_wait()``

* ``rtems_barrier_release()``

Barriers can be manual (released only by a call to ``..release()``),
or automatic (released by the call to ``..wait()`` that results in a wait count limit being reached.)
There is no notion of queuing in this manager,
only waiting for a barrier to be released.

This model was developed by a Masters student :cite:`Jaskuc:2022:TESTGEN`,
using the Event Manager as a model. It was added into the QDP produced by
the follow-on IV&V activity.

API Model
^^^^^^^^^

File: ``barrier-mgr-model.pml``

Modelling waiting is much easier than modelling queueing.
All that is required is an array of booleans (``waiters``), indexed by process id:

.. code:: promela

  typedef Barrier {
    byte b_name; // barrier name
    bool isAutomatic; // true for automatic, false for manual barrier
    int maxWaiters; // maximum count of waiters for automatic barrier
    int waiterCount; // current amount of tasks waiting on the barrier
    bool waiters[TASK_MAX]; // waiters on the barrier
    bool isInitialised; // indicated whenever this barrier was created
  }

The name ``satisfied`` is currently used here for an inline that checks when
a barrier can be released.
Other helper inlines include ``waitAtBarrier()`` and ``barrierRelease()``.

Behaviour Patterns
^^^^^^^^^^^^^^^^^^

File: ``barrier-mgr-model.pml``

The overall architecture in terms of Promela processes has processes ``init``, ``System``, ``Clock``, ``Runner``,
and two workers: ``Worker1`` and ``Worker2``.
The runner and workers each may perform one or more API calls,
in the following order: create, ident, wait, release, delete.
Scenarios mix and match which task does what.

There are three top-level scenarios:

.. code:: promela

  mtype = {ManAcqRel,AutoAcq,AutoToutDel};

In scenario ``ManAcqRel``, the runner will do create, release and delete,
with sub-scenarios to check error cases as well as good behaviour,
for manual barriers.
Good behaviour involves one or more workers doing a wait.
The ``AutoAcq`` and ``AutoToutDel``
scenarios look at good and bad uses of automatic barriers.

Annotations
^^^^^^^^^^^

File: ``barrier-mgr-model.pml``

Similiar to those used in the Event Manager.

Refinement
^^^^^^^^^^

File: ``barrier-mgr-model-rfn.yml``

Similiar to those used in the Event Manager.

Testing the Message Manager
---------------------------

Documentation:  Message Manager section in the RTEMS Classic API Guide.

Model Directory: ``formal/promela/models/messages``.

Model Name: ``msg-mgr-model``.

The Message Manager provides objects that act as message queues. Tasks can
interact with these by enqueuing and/or dequeuing message objects.

There are 11 directives in total, but only the following were modelled:

  * ``rtems_message_queue_create()``

  * ``rtems_message_queue_send()``

  * ``rtems_message_queue_receive()``

The manager supports two queuing protocols, FIFO and priority-based.
Only the FIFO queueing was modelled.

This model was developed by a Masters student :cite:`Lynch:2022:TESTGEN`,
using the Event Manager as a model. It was added into the QDP produced by
the follow-on IV&V activity.

Below we focus on aspects of this model that differ from the Event Manager.

API Model
^^^^^^^^^

File: ``msg-mgr-model.pml``

A key feature of this manager is that not only are messages in a queue,
but so are the tasks waiting for those messages.
Both task and message queues are modelled as circular buffers,
with inline definitions of enqueuing and dequeuing operations.

While the Message Manager allows many queues to be created,
the model only uses one.


Behaviour Patterns
^^^^^^^^^^^^^^^^^^

File: ``msg-mgr-model.pml``

The overall architecture in terms of Promela processes has processes ``init``, ``System``, ``Clock``, ``Sender``, and two receivers:
``Receiver1`` and ``Receiver2``.
The ``Sender`` is the test runner, which performs the queue creation,
releases the start semaphore and then performs a send operation if needed.
The receivers are worker processes.

There are four top level scenarios:

.. code:: promela

  mtype = {Send,Receive,SndRcv, RcvSnd};

Scenarios ``Send`` and ``Receive`` are used for testing erroneous calls.
The ``SndRcv`` scenario fills up queues before the receivers run,
while the ``RcvSnd`` has the receivers waiting before messages are sent.

Annotations
^^^^^^^^^^^

File: ``msg-mgr-model.pml``

Similiar to those used in the Event Manager.

Refinement
^^^^^^^^^^

File: ``msg-mgr-model-rfn.yml``

Similiar to those used in the Event Manager.

Current State of Play
---------------------

The models developed here are the result of
an ad-hoc incremental development process
and have a lot of overlapping material.

Model State
^^^^^^^^^^^

The models were developed by first focusing on simple behavior
such as error handling, and then adding in simpler behaviors,
until sufficient coverage was acheived.
The basic philosophy at the time was not to fix anything not broken.

This has led to the models being somewhat over-engineered,
particularly when it comes to scenario generation.
There is some conditional looping behaviour,
implemented using labels and ``goto``,
that should really be linearised, using conditionals to skip parts.
It is harder than it should be to understand what each scenario does.

Also the API call models have perhaps a bit too much code devoted
to system behaviour.

Model Refactoring
^^^^^^^^^^^^^^^^^

There is a case to be made to perform some model refactoring.
Some of this would address the model state issues above.
Other refactoring would extract the common model material out,
to be put into files that could be included.
This includes the binary semaphore models,
and the parts modelling preemption and waiting while blocked.


Test Code Refactoring
^^^^^^^^^^^^^^^^^^^^^

During the qualification activity,
a new file ``tx-support.c`` was added to the RTEMS validation testsuite codebase.
This gathers C test support functions used by most of the tests.
The contents of the ``tr-<modelname>.h`` and ``tr-<modelname>.c``
files in particular should be brought in line with ``tx-support.c``.

Suitable Promela models should also be produced
of relevant functions in ``tx-support.c``.