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
|
%& latex
\documentclass[11pt]{article}
\usepackage{graphics}
\usepackage{color}
\textheight 9.0 in
\topmargin -0.5 in
\textwidth 6.5 in
\oddsidemargin -0.0 in
\evensidemargin -0.0 in
\begin{document}
\definecolor{dark}{gray}{0.5}
\newcommand{\syntax}[1]{%
\begin{center}
\fbox{\tt \small
\begin{tabular}{l}
#1\end{tabular}}\end{center}}
\begin{center}
{\LARGE Tutorial for SLICC v0.2} \\
\vspace{.25in}
{\large Milo Martin} \\
{\large 8/25/1999}
\end{center}
\section*{Overview}
This document attempts to illustrate the syntax and expressiveness of
the cache coherence protocol specification language through a small
example. A ``stupido'' cache coherence protocol is described in prose
and then expressed in the language.
The protocol used as the running example is described. Then each of
the elements of the protocol is discussed and expressed in the
language: states, events, transitions, and actions.
\section*{Protocol Description}
In order to make this example a simple as possible, the protocol
described is a simple as possible and makes many simplifying
assumptions. These simplifications were made only to clarify the
exposition and are not indications of limitations of the
expressiveness of the description language. We have already specified
a more complicated MSI broadcast snooping protocol, a multicast
snooping protocol, and a directory protocol. The simplifying
assumptions are listed below. The remaining details of the protocol
are described in the sections where we give the syntax of the
language.
\begin{itemize}
\item
The protocol uses broadcast snooping that assumes that a broadcast can
only occur if all processors have processed all of their incoming
address transactions. In essence, when a processor issue an address
request, that request will be next in the global order. This allows
us to avoid needed to handle the cases before we have observed our
request in the global order.
\item
The protocol has only Modified and Idle stable states. (Note: Even
the Shared state is omitted.)
\item
To avoid describing replacement (PutX's) and writebacks, the caches
are in treated as infinite in size.
\item
No forward progress bit is used, so the protocol as specified does not
guarantee forward progress.
\item
Only the mandatory request queue is used. No optional or prefetch
queue is described.
\item
The above simplifications reduce the need for TBEs (Translation Buffer
Entries) and thus the idea of a TBE is not include.
\item
Each memory module is assumed to have some state associated with each
cache block in the memory. This requires a simple directory/memory
state machine to work as a compliment to the processor state machine.
Traditional broadcast snooping protocols often have no ``directory''
state in the memory.
\end{itemize}
\section*{Protocol Messages}
Cache coherence protocols communicate by sending well defined
messages. To fully specify a cache coherence protocol we need to be
able to specify the message types and fields. For this protocol we
have address messages ({\tt AddressMsg}) which are broadcast, and data
messages ({\tt DataMsg}) which are point-to-point. Address messages
have an address field ({\tt Address}), a request type ({\tt Type}),
and which processor made the request ({\tt Requestor}). Data message
have an address field ({\tt Address}), a destination ({\tt
Destination}), and the actual cache block being transfered ({\tt
DataBlk}). The names in parenthesis are important because those are
the names which code later in the specification will reference various
message types and fields in the messages.
Messages are declared by creating types with {\tt new\_type()} and
adding fields with {\tt type\_field()}. The exact syntax for
declaring types and message is a bit ugly right now and is going to be
changed in the near future. If you wish, please see the appendix for
an example of the current syntax.
\section*{Cache States}
Idle and Modified are the two stable states in our protocol. In
addition the we have a single transient processor state. This state
is used after a processor has issued a request and is waiting for the
data response to arrive.
Declaring states in the language is the first of a number of
declarations we will be using. All of these declarations have a
similar format. Below is the format for a state declaration.
\syntax{
{\tt state({\em identifier}, {\em shorthand}, {\em pair1}, {\em pair2}, ...);}
}
{\em identifier} is a name that is used later to
refer to this state later in the description. It must start with a
letter and after than can have any combination of letters, numbers,
and the underscore character. {\em shorthand} is a quoted string
which contains the shorthand that should be used for the state when
generating tables and such.
The {\em pair}'s are used to associate arbitrary information with each
state. Zero or more pairs can be included in each declaration. For
example we want to have a more verbose description of each state when
we generate the table which contains the states and descriptions.
This information is encoded in the language by adding a {\tt desc}
parameter to a declaration. The name of the parameter is followed by
an equal sign and a string with the description. The {\tt desc} pair
technically optional, however the table generation tool will complain
about a missing description if it is not present.
The three states for our protocol are expressed as follows:
\begin{verbatim}
state(I, "I", desc="Idle");
state(M, "M", desc="Modified");
state(IM, "IM", desc="Idle, issued request but have not seen data yet");
\end{verbatim}
\section*{Cache Events}
Events are external stimulus that cause the state machine to take
action. This is most often a message in one of the queues from the
network or processor. Events form the columns of the protocol table.
Our simple protocol has one event per incoming queue. When a message
is waiting in one of these queues and event can occur. We can see a
request from the processor in the mandatory queue, another processor's
request, or a data response.
Events are declared in the language similarly to states. The {\em
identifier}, {\em shorthand}, and {\em pair}'s have the same purpose
as in a state declaration.
\syntax{
event({\em identifier}, {\em shorthand}, {\em pair1}, {\em pair2}, ...) \{ \\
\hspace{.1in} {\em statement\_list} \\
\} \\
}
Events are different in that they have a list of statements which
allows exact specification of when the event should ``trigger'' a
transition. These statements are mini-programming language with
syntax similar to that of C. For example the {\tt peek} construct in
this context checks to see if there is a message at the head of the
specified queue, and if so, conceptually copies the message to a
temporary variable accessed as {\tt in\_msg}. The language also
supports various procedure calls, functions, conditional statements,
assignment, and queue operations such as peek, enqueue and dequeue.
The {\tt trigger()} construct takes an address as the only parameter.
This is the address that should be triggered for the event. To give
you a feel for what this code looks like, the three events for our
simple protocol are below.
\begin{verbatim}
event(LoadStore, "LoadStore", desc="Load or Store request from local processor") {
peek(mandatoryQueue_ptr, CacheMsg) {
trigger(in_msg.Address);
}
}
event(Other_GETX, "Other GETX", desc="Observed a GETX request from another processor") {
peek(addressNetwork_ptr, AddressMsg) {
if (in_msg.Requestor != id) {
trigger(in_msg.Address);
}
}
}
event(Data, "Data", desc="Data for this block from the data network") {
peek(dataNetwork_ptr, DataMsg) {
trigger(in_msg.Address);
}
}
\end{verbatim}
\section*{Cache Actions}
Actions are the privative operations that are performed by various
state transitions. These correspond (by convention) to the lower case
letters in the tables. We need several actions in our protocol
including issuing a GetX request, servicing a cache hit, send data
from the cache to the requestor, writing data into the cache, and
popping the various queues.
The syntax of an action declaration is similar to an event
declaration. The difference is that statements in the statement list
are used to implement the desired action, and not triggering an event.
\syntax{action({\em identifier}, {\em shorthand}, {\em pair1}, {\em pair2}, ...) \{ \\
\hspace{.1in} {\em statement\_list} \\
\}
}
The actions for this protocol use more of the features of the
language. Some of the interesting case are discussed below.
\begin{itemize}
\item
To manipulate values we need assignment statements (notice the use of
{\verb+:=+} as the assignment operator). The action to write data
into the cache looks at the incoming data message and puts the data in
the cache. Notice the use of square brackets to lookup the block in
the cache based on the address of the block.
\begin{verbatim}
action(w_writeDataToCache, "w", desc="Write data from data message into cache") {
peek(dataNetwork_ptr, DataMsg) {
cacheMemory_ptr[address].DataBlk := in_msg.DataBlk;
}
}
\end{verbatim}
\item
In addition to peeking at queues, we also enqueue messages. The {\tt
enqueue} construct works similarly to the {\tt peek} construct. {\tt
enqueue} creates a temporary called {\tt out\_msg}. You can assign
the fields of this message. At the end of the {\tt enqueue} construct
the message is implicitly inserted in the outgoing queue of the
specified network. Notice also how the type of the message is
specified and how the assignment statements use the names of the
fields of the messages. {\tt address} is the address for which the
event was {\tt trigger}ed.
\begin{verbatim}
action(g_issueGETX, "g", desc="Issue GETX.") {
enqueue(addressNetwork_ptr, AddressMsg) {
out_msg.Address := address;
out_msg.Type := "GETX";
out_msg.Requestor := id;
}
}
\end{verbatim}
\item
Some times we need to use both {\tt peek} and {\tt enqueue} together.
In this example we look at an incoming address request to figure out
who to whom to forward the data value.
\begin{verbatim}
action(r_cacheToRequestor, "r", desc="Send data from the cache to the requestor") {
peek(addressNetwork_ptr, AddressMsg) {
enqueue(dataNetwork_ptr, DataMsg) {
out_msg.Address := address;
out_msg.Destination := in_msg.Requestor;
out_msg.DataBlk := cacheMemory_ptr[address].DataBlk;
}
}
}
\end{verbatim}
\item
We also need to pop the various queues.
\begin{verbatim}
action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") {
dequeue(mandatoryQueue_ptr);
}
\end{verbatim}
\item
Finally we have the ability to call procedures and functions. The
following is an example of a procedure call. Currently all of the
procedures and functions are used to handle all of the more specific
operations. These are currently hard coded into the generator.
\begin{verbatim}
action(h_hit, "h", desc="Service load/store from the cache.") {
serviceLdSt(address, cacheMemory_ptr[address].DataBlk);
}
\end{verbatim}
\end{itemize}
\section*{Cache Transitions}
The cross product of states and events gives us the set of possible
transitions. For example, for our example protocol the empty would
be:
\begin{center}
\begin{tabular}{|l||l|l|l|} \hline
& LoadStore & Other GETX & Data \\ \hline \hline
I & & & \\ \hline
M & & & \\ \hline
IM & & & \\ \hline
\end{tabular}
\end{center}
Transitions are atomic and are the heart of the protocol
specification. The transition specifies both what the next state, and
also what actions are performed for each unique state/event pair. The
transition declaration looks different from the other declarations:
\syntax{
transition({\em state}, {\em event}, {\em new\_state}, {\em pair1}, {\em pair2}, ...) \{ \\
\hspace{.1in} {\em action\_identifier\_list}\\
\}
}
{\em state} and {\em event} are the pair which uniquely identifies the
transition. {\em state} correspond to the row where {\em event}
selects the column. {\em new\_state} is an optional parameter. If
{\em new\_state} is specified, that is the state when the atomic
transition is completed. If the parameter is omitted there is assumed
to be no state change. An impossible transition is specified by
simply not declaring an event for that state/event pair.
We also place list of actions in the curly braces. The {\em
action\_identifier}'s correspond to the identifier specified as the
first parameter of an action declaration. The action list is a list
of operations to be performed when this transition occurs. The
actions also knows what preconditions are necessary are required for
the action to be performed. For example a necessary precondition for
an action which sends a message is that there is a space available in
the outgoing queue. Each transition is considered atomic, and thus
the generated code ensures that all of the actions can be completed
before performing and of the actions.
In our running example protocol it is only possible to receive data in
the {\em IM} state. The other seven cases can occur and are declared
as follows. Below are a couple of examples. See the appendix for a
complete list.
\newpage
\begin{verbatim}
transition(I, LoadStore, IM) {
g_issueGETX;
}
transition(M, LoadStore) {
h_hit;
k_popMandatoryQueue;
}
transition(M, Other_GETX, I) {
r_cacheToRequestor;
i_popAddressQueue;
}
\end{verbatim}
From the above declarations we can generate a table. Each box can
have lower case letters which corresponds to the list of actions
possibly followed by a slash and a state (in uppercase letters). If
there is no slash and state, the transition does not change the state.
\begin{center}
\begin{tabular}{|l||l|l|l|} \hline
& LoadStore & Other GETX & Data \\ \hline \hline
I & g/IM & i & (impossible)\\ \hline
M & hk & ri/I & (impossible)\\ \hline
IM & z & z & wj/M \\ \hline
\end{tabular}
\end{center}
There is a useful shorthand for specifying many transitions with the
same action. One or both of {\em event} and {\em state} can be a list
in curly braces. This defines the cross product of the sets in one
declaration. If no {\em new\_state} is specified none of the
transitions cause a state change. If {\em new\_state} is specified,
all of the transitions in the cross product of the sets has the same
next state. For example, in the below transitions both IM/LoadStore
and IM/Other\_GETX have the action {\tt z\_delayTrans}.
\begin{verbatim}
transition(IM, LoadStore) {
z_delayTrans;
}
transition(IM, Other_GETX) {
z_delayTrans;
}
\end{verbatim}
These can be specified in a single declaration:
\begin{verbatim}
transition(IM, {LoadStore, Other_GETX}) {
z_delayTrans;
}
\end{verbatim}
\newpage
\section*{Appendix - Sample Cache Controller Specification}
{\small
\begin{verbatim}
machine(processor, "Simple MI Processor") {
// AddressMsg
new_type(AddressMsg, "AddressMsg", message="yes", desc="");
type_field(AddressMsg, Address, "address",
desc="Physical address for this request",
c_type=PhysAddress, c_include="Address.hh", murphi_type="");
type_field(AddressMsg, Type, "type",
desc="Type of request (GetS, GetX, PutX, etc)",
c_type=CoherenceRequestType, c_include="CoherenceRequestType.hh", murphi_type="");
type_field(AddressMsg, Requestor, "requestor",
desc="Node who initiated the request",
c_type=ComponentID, c_include="ComponentID.hh", murphi_type="");
// DataMsg
new_type(DataMsg, "DataMsg", message="yes", desc="");
type_field(DataMsg, Address, "address",
desc="Physical address for this request",
c_type=PhysAddress, c_include="Address.hh", murphi_type="");
type_field(DataMsg, Destination, "destination",
desc="Node to whom the data is sent",
c_type=Set, c_include="Set.hh", murphi_type="");
type_field(DataMsg, DataBlk, "data",
desc="Node to whom the data is sent",
c_type=DataBlock, c_include="DataBlock.hh", murphi_type="");
// CacheEntry
new_type(CacheEntry, "CacheEntry");
type_field(CacheEntry, CacheState, "Cache state", desc="cache state",
c_type=CacheState, c_include="CacheState.hh", murphi_type="");
type_field(CacheEntry, DataBlk, "data", desc="data for the block",
c_type=DataBlock, c_include="DataBlock.hh", murphi_type="");
// DirectoryEntry
new_type(DirectoryEntry, "DirectoryEntry");
type_field(DirectoryEntry, DirectoryState, "Directory state", desc="Directory state",
c_type=DirectoryState, c_include="DirectoryState.hh", murphi_type="");
type_field(DirectoryEntry, DataBlk, "data", desc="data for the block",
c_type=DataBlock, c_include="DataBlock.hh", murphi_type="");
\end{verbatim}
\newpage
\begin{verbatim}
// STATES
state(I, "I", desc="Idle");
state(M, "M", desc="Modified");
state(IM, "IM", desc="Idle, issued request but have not seen data yet");
// EVENTS
// From processor
event(LoadStore, "LoadStore", desc="Load or Store request from local processor") {
peek(mandatoryQueue_ptr, CacheMsg) {
trigger(in_msg.Address);
}
}
// From Address network
event(Other_GETX, "Other GETX", desc="Observed a GETX request from another processor") {
peek(addressNetwork_ptr, AddressMsg) {
if (in_msg.Requestor != id) {
trigger(in_msg.Address);
}
}
}
// From Data network
event(Data, "Data", desc="Data for this block from the data network") {
peek(dataNetwork_ptr, DataMsg) {
trigger(in_msg.Address);
}
}
// ACTIONS
action(g_issueGETX, "g", desc="Issue GETX.") {
enqueue(addressNetwork_ptr, AddressMsg) {
out_msg.Address := address;
out_msg.Type := "GETX";
out_msg.Requestor := id;
}
}
action(h_hit, "h", desc="Service load/store from the cache.") {
serviceLdSt(address, cacheMemory_ptr[address].DataBlk);
}
action(i_popAddressQueue, "i", desc="Pop incoming address queue.") {
dequeue(addressNetwork_ptr);
}
action(j_popDataQueue, "j", desc="Pop incoming data queue.") {
dequeue(dataNetwork_ptr);
}
action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") {
dequeue(mandatoryQueue_ptr);
}
action(r_cacheToRequestor, "r", desc="Send data from the cache to the requestor") {
peek(addressNetwork_ptr, AddressMsg) {
enqueue(dataNetwork_ptr, DataMsg) {
out_msg.Address := address;
out_msg.Destination := in_msg.Requestor;
out_msg.DataBlk := cacheMemory_ptr[address].DataBlk;
}
}
}
action(w_writeDataToCache, "w", desc="Write data from data message into cache") {
peek(dataNetwork_ptr, DataMsg) {
cacheMemory_ptr[address].DataBlk := in_msg.DataBlk;
}
}
action(z_delayTrans, "z", desc="Cannot be handled right now.") {
stall();
}
// TRANSITIONS
// Transitions from Idle
transition(I, LoadStore, IM) {
g_issueGETX;
}
transition(I, Other_GETX) {
i_popAddressQueue;
}
// Transitions from Modified
transition(M, LoadStore) {
h_hit;
k_popMandatoryQueue;
}
transition(M, Other_GETX, I) {
r_cacheToRequestor;
i_popAddressQueue;
}
// Transitions from IM
transition(IM, {LoadStore, Other_GETX}) {
z_delayTrans;
}
transition(IM, Data, M) {
w_writeDataToCache;
j_popDataQueue;
}
}
\end{verbatim}
}
\end{document}
|