summaryrefslogtreecommitdiffstats
path: root/hwpf/fapi/docs/topics/Examples.md
blob: e7ccb027d42cb8c998638b252d4312d54452b331 (plain)
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

# Examples And Frequently Asked Questions

## Buffers

### Create a buffer of 32 bits and initialize it.

    fapi2::buffer<uint32_t> data(0xAA55FFAA);

### Flip bit 0

    data.flipBit<0>();

### Invert the buffer

    data.invert();

### Reset it's value

    data = 0xFFFFFFFF;

### Create a mask using an anonymous object

    my_buffer &= buffer<T>().setBit<B>.invert();

### Create an 8 bit buffer, initialize it, and flip some bits around

    fapi2::buffer<uint8_t>(0xA5).flipBit<5>().flipBit<5>() == 0xA5;

## Operations and Things
### Is the FAPI_TRY / 'clean_up:' method, the recommended method for doing put/getscoms?

Yes, FAPI_TRY is the preferred wrapper for put/get scom as well as other operations which used to do the do/while/break dance based on the fapi return code.
A thread-local fapi return code, fapi2::current_err has been introduced to help with the housekeeping.

Feel free to decompose the FAPI_TRY pattern if you have clean_up requirements which make FAPI_TRY too simplistic.


## Targets

### Create a processor target

    fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP> A(PROCESSOR_CHIP_A);

### Define a function which takes only a processor as an argument, enforced by the compiler

    void takesProc( const fapi2::Target<fapi2::TARGET_TYPE_PROC_CHIP>& V );

### How do I pass TARGET_TYPE_X into putScom?

It should "just work." fapi2::putScom is defined as a template.
Basically any TargetType will match. So,

    Target<TARGET_TYPE_XBUS> foo;
    Target<TARGET_TYPE_ABUS> bar;

will both match fapi::putScom in it's more generic sense. However, platforms
might specialize the hw_access templates. For example, if the operation to
perform a putScom on an XBUS is different than accessing other targets, the
platform needs to specialize for XBUS.

    template<>
    fapi2::ReturnCode fapi2::putScom (const fapi2::Target<TARGET_TYPE_XBUS> &i_target, const uint64_t i_address, buffer< uint64_t > &i_data)
    {
        // Do XBUS special code here.
    }

### How do I get <information> with the new targets when this used to be an attribute look up before?

This question came up in the context of ATTR_CHIP_UNIT_POS, but really applies for any information
previously tucked away in attributes.

Do the attribute lookup, the platform can optimize the look up away. In any event the get_attr macro/call will always do the right thing.

If the platform can resolve the lookup without looking anything up (all the information is encoded in the value of the target) the attribute
mechanism can simply do that manipulation at compile time.

For the smaller platforms, we discussed attribute look ups were templates which could be (would be?) specialized per attribute "key."
In this way, the "look up" for the unit position could be coded to be nothing more than a static manipulation of the target value.
A look up would, obviously, be needed if the unit position (or any other attribute value) couldn't be resolved from the target value alone.

### Target Types

#### How do I write a procedure which takes multiple target types?

Depends on what you mean.

##### If you mean "I have a procedure which is generic but only for a subset of types":

    fapi2::ReturnCode newProcedure( const fapi2::Target<TARGET_TYPE_XBUS | TARGET_TYPE_ABUS> & i_target, ...);

Note that newProcedure can not then say "if xbus, do this, if abus do the
other." This is the static equivilent of grabbing a "base class" and the same
rules apply - if the method is not generic for all the subclasses, you need to
specialize for those subclasses.

__Think twice about using this mechanism. It creates a new type (the or of the
target types) and reduces the input to this type. There is no support for run
time type conversion, and so the original type is not available in this scope.__

##### If you mean "I have a procedure and I want it to do different things for different target types":

You can create templates:

    template<fapi2::TargetType T>
    void newProcedure( const fapi2::Target<T>& i_target );

    template<>
    void newProcedure( const fapi2::Target<TARGET_TYPE_XBUS>& i_target )
    {
        // XBUS specific code here
    }

    template<>
    void newProcedure( const fapi2::Target<TARGET_TYPE_ABUS>& i_target )
    {
        // ABUS specific code here
    }

Notice that because the generic case for newProcedure is declared but
never defined you will get an error at compile time if newProcedure is
called with a type other than XBUS or ABUS.

Or overloaded functions:

    void newProcedure( const fapi2::Target<TARGET_TYPE_XBUS>& i_target )
    {
        // XBUS specific code here
    }

    void newProcedure( const fapi2::Target<TARGET_TYPE_ABUS>& i_target )
    {
        // ABUS specific code here
    }

##### If you mean "I have a procedure which is mostly generic but I need special handling for some target types":

More or less the same as above, but define the generic template. There is no equivilent for
overloaded functions unless a composite target will suit your needs.

    template<fapi2::TargetType T>
    void newProcedure( const fapi2::Target<T>& i_target )
    {
        // Code for types other than ABUS or XBUS
    }

    template<>
    void newProcedure( const fapi2::Target<TARGET_TYPE_XBUS>& i_target )
    {
        // XBUS specific code here
    }

    template<>
    void newProcedure( const fapi2::Target<TARGET_TYPE_ABUS>& i_target )
    {
        // ABUS specific code here
    }

##### If you want to build a procedure which takes all target types:

__It is recomended that you do not use TARGET_TYPE_ALL - that will reduce the
type of the passed in target to the "top level base class" of targets and
every procedure called will need to be completly generic. This is probably
not what you inteded (but is sometimes useful.) Rather, it is recomended
that you simply make a generic template.__

    template<fapi2::TargetType T>
    void newProcedure( const fapi2::Target<T>& i_target )
    {
        // Code for all types
        fapi2::putScom(i_target, ... );
    }

Note that the putScom here will always be the correct putScom for the
target type passed in. Assuming procedures called in this method have
been specialized for specific target types, the compiler will find the
best match and call it.

#### Once I'm in newProcedure, what is the type of the target?

If you used a generic or specialized template, the type will be the type
of the target passed in. If you used a composite type (or'd together target
types) then the type will be TYPE_A | TYPE_B.

Notice there's no simple way to know the original type was TYPE_A in this
case. If you think about this, it makes sense. Consider:

    void f(double x);

    int y = 3;
    f(y);

There's no way in f() to know that x started life as an int, which happened to
be cast to a double.

Targets are the same thing - they're just types we create on the fly. So all of
the usual C++ tools to deal with this are available.

#### So, wait - I can't write a procedure which handles multiple target types?

You can. What you can't do is figure out the type of the object prior to
the cast. So what you need to do is make sure the original type isn't lost,
and that means you can't use TYPE_A | TYPE_B:

##### Specialize. This is the prefered mechanism.
The template and overload examples above show how to do this specialization.
It will create very regular and easy to maintain code.

##### Create a generic template and handle each type yourself.

    template<fapi2::TargetType T>
    void newProcedure( fapi2::Target<T> i_target )
    {
        // If this procedure isn't generic, check for allowed types here.
        static_assert(fapi2::is_same<T, fapi2::TARGET_TYPE_ABUS>() || fapi2::is_same<T, fapi2::TARGET_TYPE_XBUS>(),
                      "newProcedure only takes X/A busses");

        :
        :

        if (fapi2::is_same<T, fapi2::TARGET_TYPE_ABUS>())
        {
            // ABUS code
        }

        if (fapi2::is_same<T, fapi2::TARGET_TYPE_XBUS>())
        {
            // XBUS code
        }

        :
        :
    }

newProcedure is a generic template, any target type will match. And, the type T of i_target is
the original value of the target - the template matched without a cast because it's generic.
However, with that generality we lost the compiler argument type checking so we replace it with
the static_assert. This happens at compile time so the compiler will complain if a target other
than an XBUS or ABUS is passed in to this procedure. To handle the special cases for the types,
fapi2::is_same is used again - and is also a compile time operation. And so when this template
matches an XBUS target, the code generated is specific to the XBUS target.

#### Ok, so what is TARGET_TYPE_X | TARGET_TYPE_Y for then?

Creating procedures which are generic to those types. Think of this as a base
class method.
OpenPOWER on IntegriCloud