Type.py revision 8608:02d7ac5fb855
1# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
2# Copyright (c) 2009 The Hewlett-Packard Development Company
3# All rights reserved.
4#
5# Redistribution and use in source and binary forms, with or without
6# modification, are permitted provided that the following conditions are
7# met: redistributions of source code must retain the above copyright
8# notice, this list of conditions and the following disclaimer;
9# redistributions in binary form must reproduce the above copyright
10# notice, this list of conditions and the following disclaimer in the
11# documentation and/or other materials provided with the distribution;
12# neither the name of the copyright holders nor the names of its
13# contributors may be used to endorse or promote products derived from
14# this software without specific prior written permission.
15#
16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28from m5.util import orderdict
29
30from slicc.util import PairContainer
31from slicc.symbols.Symbol import Symbol
32
33class DataMember(PairContainer):
34    def __init__(self, ident, type, pairs, init_code):
35        super(DataMember, self).__init__(pairs)
36        self.ident = ident
37        self.type = type
38        self.init_code = init_code
39
40class Enumeration(PairContainer):
41    def __init__(self, ident, pairs):
42        super(Enumeration, self).__init__(pairs)
43        self.ident = ident
44
45class Method(object):
46    def __init__(self, return_type, param_types):
47        self.return_type = return_type
48        self.param_types = param_types
49
50class Type(Symbol):
51    def __init__(self, table, ident, location, pairs, machine=None):
52        super(Type, self).__init__(table, ident, location, pairs)
53        self.c_ident = ident
54        self.abstract_ident = ""
55        if machine:
56            if self.isExternal or self.isPrimitive:
57                if "external_name" in self:
58                    self.c_ident = self["external_name"]
59            else:
60                # Append with machine name
61                self.c_ident = "%s_%s" % (machine, ident)
62
63        self.pairs.setdefault("desc", "No description avaliable")
64
65        # check for interface that this Type implements
66        if "interface" in self:
67            interface = self["interface"]
68            if interface in ("Message", "NetworkMessage"):
69                self["message"] = "yes"
70            if interface == "NetworkMessage":
71                self["networkmessage"] = "yes"
72
73        # FIXME - all of the following id comparisons are fragile hacks
74        if self.ident in ("CacheMemory", "NewCacheMemory",
75                          "TLCCacheMemory", "DNUCACacheMemory",
76                          "DNUCABankCacheMemory", "L2BankCacheMemory",
77                          "CompressedCacheMemory", "PrefetchCacheMemory"):
78            self["cache"] = "yes"
79
80        if self.ident in ("TBETable", "DNUCATBETable", "DNUCAStopTable"):
81            self["tbe"] = "yes"
82
83        if self.ident == "NewTBETable":
84            self["newtbe"] = "yes"
85
86        if self.ident == "TimerTable":
87            self["timer"] = "yes"
88
89        if self.ident == "DirectoryMemory":
90            self["dir"] = "yes"
91
92        if self.ident == "PersistentTable":
93            self["persistent"] = "yes"
94
95        if self.ident == "Prefetcher":
96            self["prefetcher"] = "yes"
97
98        if self.ident == "DNUCA_Movement":
99            self["mover"] = "yes"
100
101        self.isMachineType = (ident == "MachineType")
102
103        self.isStateDecl = ("state_decl" in self)
104        self.statePermPairs = []
105
106        self.data_members = orderdict()
107
108        # Methods
109        self.methods = {}
110
111        # Enums
112        self.enums = orderdict()
113
114    @property
115    def isPrimitive(self):
116        return "primitive" in self
117    @property
118    def isNetworkMessage(self):
119        return "networkmessage" in self
120    @property
121    def isMessage(self):
122        return "message" in self
123    @property
124    def isBuffer(self):
125        return "buffer" in self
126    @property
127    def isInPort(self):
128        return "inport" in self
129    @property
130    def isOutPort(self):
131        return "outport" in self
132    @property
133    def isEnumeration(self):
134        return "enumeration" in self
135    @property
136    def isExternal(self):
137        return "external" in self
138    @property
139    def isGlobal(self):
140        return "global" in self
141    @property
142    def isInterface(self):
143        return "interface" in self
144
145    # Return false on error
146    def dataMemberAdd(self, ident, type, pairs, init_code):
147        if ident in self.data_members:
148            return False
149
150        member = DataMember(ident, type, pairs, init_code)
151        self.data_members[ident] = member
152
153        return True
154
155    def dataMemberType(self, ident):
156        return self.data_members[ident].type
157
158    def methodId(self, name, param_type_vec):
159        return '_'.join([name] + [ pt.c_ident for pt in param_type_vec ])
160
161    def methodIdAbstract(self, name, param_type_vec):
162        return '_'.join([name] + [ pt.abstract_ident for pt in param_type_vec ])
163
164    def statePermPairAdd(self, state_name, perm_name):
165        self.statePermPairs.append([state_name, perm_name])
166
167    def methodAdd(self, name, return_type, param_type_vec):
168        ident = self.methodId(name, param_type_vec)
169        if ident in self.methods:
170            return False
171
172        self.methods[ident] = Method(return_type, param_type_vec)
173        return True
174
175    def enumAdd(self, ident, pairs):
176        if ident in self.enums:
177            return False
178
179        self.enums[ident] = Enumeration(ident, pairs)
180
181        # Add default
182        if "default" not in self:
183            self["default"] = "%s_NUM" % self.c_ident
184
185        return True
186
187    def writeCodeFiles(self, path):
188        if self.isExternal:
189            # Do nothing
190            pass
191        elif self.isEnumeration:
192            self.printEnumHH(path)
193            self.printEnumCC(path)
194        else:
195            # User defined structs and messages
196            self.printTypeHH(path)
197            self.printTypeCC(path)
198
199    def printTypeHH(self, path):
200        code = self.symtab.codeFormatter()
201        code('''
202/** \\file ${{self.c_ident}}.hh
203 *
204 *
205 * Auto generated C++ code started by $__file__:$__line__
206 */
207
208#ifndef __${{self.c_ident}}_HH__
209#define __${{self.c_ident}}_HH__
210
211#include <iostream>
212
213#include "mem/ruby/common/Global.hh"
214''')
215
216        for dm in self.data_members.values():
217            if not dm.type.isPrimitive:
218                code('#include "mem/protocol/$0.hh"', dm.type.c_ident)
219
220        parent = ""
221        if "interface" in self:
222            code('#include "mem/protocol/$0.hh"', self["interface"])
223            parent = " :  public %s" % self["interface"]
224
225        code('''
226$klass ${{self.c_ident}}$parent
227{
228  public:
229    ${{self.c_ident}}()
230    {
231''', klass="class")
232
233        code.indent()
234        if not self.isGlobal:
235            code.indent()
236            for dm in self.data_members.values():
237                ident = dm.ident
238                if "default" in dm:
239                    # look for default value
240                    code('m_$ident = ${{dm["default"]}}; // default for this field')
241                elif "default" in dm.type:
242                    # Look for the type default
243                    tid = dm.type.c_ident
244                    code('m_$ident = ${{dm.type["default"]}}; // default value of $tid')
245                else:
246                    code('// m_$ident has no default')
247            code.dedent()
248        code('}')
249
250        # ******** Copy constructor ********
251        if not self.isGlobal:
252            code('${{self.c_ident}}(const ${{self.c_ident}}&other)')
253
254            # Call superclass constructor
255            if "interface" in self:
256                code('    : ${{self["interface"]}}(other)')
257
258            code('{')
259            code.indent()
260
261            for dm in self.data_members.values():
262                code('m_${{dm.ident}} = other.m_${{dm.ident}};')
263
264            code.dedent()
265            code('}')
266
267        # ******** Full init constructor ********
268        if not self.isGlobal:
269            params = [ 'const %s& local_%s' % (dm.type.c_ident, dm.ident) \
270                       for dm in self.data_members.itervalues() ]
271
272            params = ', '.join(params)
273            code('${{self.c_ident}}($params)')
274
275            # Call superclass constructor
276            if "interface" in self:
277                code('    : ${{self["interface"]}}()')
278
279            code('{')
280            code.indent()
281            for dm in self.data_members.values():
282                code('m_${{dm.ident}} = local_${{dm.ident}};')
283                if "nextLineCallHack" in dm:
284                    code('m_${{dm.ident}}${{dm["nextLineCallHack"]}};')
285
286            code.dedent()
287            code('}')
288
289        # create a static factory method and a clone member
290        code('''
291static ${{self.c_ident}}*
292create()
293{
294    return new ${{self.c_ident}}();
295}
296
297${{self.c_ident}}*
298clone() const
299{
300     return new ${{self.c_ident}}(*this);
301}
302''')
303
304        if not self.isGlobal:
305            # const Get methods for each field
306            code('// Const accessors methods for each field')
307            for dm in self.data_members.values():
308                code('''
309/** \\brief Const accessor method for ${{dm.ident}} field.
310 *  \\return ${{dm.ident}} field
311 */
312const ${{dm.type.c_ident}}&
313get${{dm.ident}}() const
314{
315    return m_${{dm.ident}};
316}
317''')
318
319            # Non-const Get methods for each field
320            code('// Non const Accessors methods for each field')
321            for dm in self.data_members.values():
322                code('''
323/** \\brief Non-const accessor method for ${{dm.ident}} field.
324 *  \\return ${{dm.ident}} field
325 */
326${{dm.type.c_ident}}&
327get${{dm.ident}}()
328{
329    return m_${{dm.ident}};
330}
331''')
332
333            #Set methods for each field
334            code('// Mutator methods for each field')
335            for dm in self.data_members.values():
336                code('''
337/** \\brief Mutator method for ${{dm.ident}} field */
338void
339set${{dm.ident}}(const ${{dm.type.c_ident}}& local_${{dm.ident}})
340{
341    m_${{dm.ident}} = local_${{dm.ident}};
342}
343''')
344
345        code('void print(std::ostream& out) const;')
346        code.dedent()
347        code('  //private:')
348        code.indent()
349
350        # Data members for each field
351        for dm in self.data_members.values():
352            if "abstract" not in dm:
353                const = ""
354                init = ""
355
356                # global structure
357                if self.isGlobal:
358                    const = "static const "
359
360                # init value
361                if dm.init_code:
362                    # only global structure can have init value here
363                    assert self.isGlobal
364                    init = " = %s" % (dm.init_code)
365
366                if "desc" in dm:
367                    code('/** ${{dm["desc"]}} */')
368
369                code('$const${{dm.type.c_ident}} m_${{dm.ident}}$init;')
370
371        code.dedent()
372        code('};')
373
374        code('''
375inline std::ostream&
376operator<<(std::ostream& out, const ${{self.c_ident}}& obj)
377{
378    obj.print(out);
379    out << std::flush;
380    return out;
381}
382
383#endif // __${{self.c_ident}}_HH__
384''')
385
386        code.write(path, "%s.hh" % self.c_ident)
387
388    def printTypeCC(self, path):
389        code = self.symtab.codeFormatter()
390
391        code('''
392/** \\file ${{self.c_ident}}.cc
393 *
394 * Auto generated C++ code started by $__file__:$__line__
395 */
396
397#include <iostream>
398
399#include "mem/protocol/${{self.c_ident}}.hh"
400
401using namespace std;
402''')
403
404        code('''
405/** \\brief Print the state of this object */
406void
407${{self.c_ident}}::print(ostream& out) const
408{
409    out << "[${{self.c_ident}}: ";
410''')
411
412        # For each field
413        code.indent()
414        for dm in self.data_members.values():
415            code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''')
416
417        if self.isMessage:
418            code('out << "Time = " << getTime() * g_eventQueue_ptr->getClock() << " ";')
419        code.dedent()
420
421        # Trailer
422        code('''
423    out << "]";
424}''')
425
426        code.write(path, "%s.cc" % self.c_ident)
427
428    def printEnumHH(self, path):
429        code = self.symtab.codeFormatter()
430        code('''
431/** \\file ${{self.c_ident}}.hh
432 *
433 * Auto generated C++ code started by $__file__:$__line__
434 */
435
436#ifndef __${{self.c_ident}}_HH__
437#define __${{self.c_ident}}_HH__
438
439#include <iostream>
440#include <string>
441
442''')
443        if self.isStateDecl:
444            code('#include "mem/protocol/AccessPermission.hh"')
445
446        if self.isMachineType:
447            code('#include "base/misc.hh"')
448            code('#include "mem/protocol/GenericMachineType.hh"')
449            code('#include "mem/ruby/common/Address.hh"')
450            code('struct MachineID;')
451
452        code('''
453
454// Class definition
455/** \\enum ${{self.c_ident}}
456 *  \\brief ${{self.desc}}
457 */
458enum ${{self.c_ident}} {
459    ${{self.c_ident}}_FIRST,
460''')
461
462        code.indent()
463        # For each field
464        for i,(ident,enum) in enumerate(self.enums.iteritems()):
465            desc = enum.get("desc", "No description avaliable")
466            if i == 0:
467                init = ' = %s_FIRST' % self.c_ident
468            else:
469                init = ''
470            code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
471        code.dedent()
472        code('''
473    ${{self.c_ident}}_NUM
474};
475
476// Code to convert from a string to the enumeration
477${{self.c_ident}} string_to_${{self.c_ident}}(const std::string& str);
478
479// Code to convert state to a string
480std::string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
481
482// Code to increment an enumeration type
483${{self.c_ident}} &operator++(${{self.c_ident}} &e);
484''')
485
486        # MachineType hack used to set the base component id for each Machine
487        if self.isMachineType:
488            code('''
489int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
490MachineType ${{self.c_ident}}_from_base_level(int);
491int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
492int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
493''')
494
495            for enum in self.enums.itervalues():
496                if enum.ident == "DMA":
497                    code('''
498MachineID map_Address_to_DMA(const Address &addr);
499''')
500                code('''
501
502MachineID get${{enum.ident}}MachineID(NodeID RubyNode);
503''')
504
505            code('''
506inline GenericMachineType
507ConvertMachToGenericMach(MachineType machType)
508{
509''')
510            for enum in self.enums.itervalues():
511                code('''
512      if (machType == MachineType_${{enum.ident}})
513          return GenericMachineType_${{enum.ident}};
514''')
515            code('''
516      panic("cannot convert to a GenericMachineType");
517}
518''')
519
520        if self.isStateDecl:
521            code('''
522
523// Code to convert the current state to an access permission
524AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj);
525
526''')
527
528        # Trailer
529        code('''
530std::ostream& operator<<(std::ostream& out, const ${{self.c_ident}}& obj);
531
532#endif // __${{self.c_ident}}_HH__
533''')
534
535        code.write(path, "%s.hh" % self.c_ident)
536
537    def printEnumCC(self, path):
538        code = self.symtab.codeFormatter()
539        code('''
540/** \\file ${{self.c_ident}}.hh
541 *
542 * Auto generated C++ code started by $__file__:$__line__
543 */
544
545#include <cassert>
546#include <iostream>
547#include <string>
548
549#include "base/misc.hh"
550#include "mem/protocol/${{self.c_ident}}.hh"
551
552using namespace std;
553
554''')
555
556        if self.isStateDecl:
557            code('''
558// Code to convert the current state to an access permission
559AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj)
560{
561    switch(obj) {
562''')
563            # For each case
564            code.indent()
565            for statePerm in self.statePermPairs:
566                code('  case ${{self.c_ident}}_${{statePerm[0]}}:')
567                code('    return AccessPermission_${{statePerm[1]}};')
568            code.dedent()
569            code ('''
570      default:
571        panic("Unknown state access permission converstion for ${{self.c_ident}}");
572    }
573}
574
575''')
576
577        if self.isMachineType:
578            for enum in self.enums.itervalues():
579                code('#include "mem/protocol/${{enum.ident}}_Controller.hh"')
580            code('#include "mem/ruby/system/MachineID.hh"')
581
582        code('''
583// Code for output operator
584ostream&
585operator<<(ostream& out, const ${{self.c_ident}}& obj)
586{
587    out << ${{self.c_ident}}_to_string(obj);
588    out << flush;
589    return out;
590}
591
592// Code to convert state to a string
593string
594${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
595{
596    switch(obj) {
597''')
598
599        # For each field
600        code.indent()
601        for enum in self.enums.itervalues():
602            code('  case ${{self.c_ident}}_${{enum.ident}}:')
603            code('    return "${{enum.ident}}";')
604        code.dedent()
605
606        # Trailer
607        code('''
608      default:
609        panic("Invalid range for type ${{self.c_ident}}");
610    }
611}
612
613// Code to convert from a string to the enumeration
614${{self.c_ident}}
615string_to_${{self.c_ident}}(const string& str)
616{
617''')
618
619        # For each field
620        start = ""
621        code.indent()
622        for enum in self.enums.itervalues():
623            code('${start}if (str == "${{enum.ident}}") {')
624            code('    return ${{self.c_ident}}_${{enum.ident}};')
625            start = "} else "
626        code.dedent()
627
628        code('''
629    } else {
630        panic("Invalid string conversion for %s, type ${{self.c_ident}}", str);
631    }
632}
633
634// Code to increment an enumeration type
635${{self.c_ident}}&
636operator++(${{self.c_ident}}& e)
637{
638    assert(e < ${{self.c_ident}}_NUM);
639    return e = ${{self.c_ident}}(e+1);
640}
641''')
642
643        # MachineType hack used to set the base level and number of
644        # components for each Machine
645        if self.isMachineType:
646            code('''
647/** \\brief returns the base vector index for each machine type to be
648  * used by NetDest
649  *
650  * \\return the base vector index for each machine type to be used by NetDest
651  * \\see NetDest.hh
652  */
653int
654${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
655{
656    switch(obj) {
657''')
658
659            # For each field
660            code.indent()
661            for i,enum in enumerate(self.enums.itervalues()):
662                code('  case ${{self.c_ident}}_${{enum.ident}}:')
663                code('    return $i;')
664            code.dedent()
665
666            # total num
667            code('''
668      case ${{self.c_ident}}_NUM:
669        return ${{len(self.enums)}};
670
671      default:
672        panic("Invalid range for type ${{self.c_ident}}");
673    }
674}
675
676/** \\brief returns the machine type for each base vector index used by NetDest
677 *
678 * \\return the MachineType
679 */
680MachineType
681${{self.c_ident}}_from_base_level(int type)
682{
683    switch(type) {
684''')
685
686            # For each field
687            code.indent()
688            for i,enum in enumerate(self.enums.itervalues()):
689                code('  case $i:')
690                code('    return ${{self.c_ident}}_${{enum.ident}};')
691            code.dedent()
692
693            # Trailer
694            code('''
695      default:
696        panic("Invalid range for type ${{self.c_ident}}");
697    }
698}
699
700/** \\brief The return value indicates the number of components created
701 * before a particular machine\'s components
702 *
703 * \\return the base number of components for each machine
704 */
705int
706${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
707{
708    int base = 0;
709    switch(obj) {
710''')
711
712            # For each field
713            code.indent()
714            code('  case ${{self.c_ident}}_NUM:')
715            for enum in reversed(self.enums.values()):
716                code('    base += ${{enum.ident}}_Controller::getNumControllers();')
717                code('  case ${{self.c_ident}}_${{enum.ident}}:')
718            code('    break;')
719            code.dedent()
720
721            code('''
722      default:
723        panic("Invalid range for type ${{self.c_ident}}");
724    }
725
726    return base;
727}
728
729/** \\brief returns the total number of components for each machine
730 * \\return the total number of components for each machine
731 */
732int
733${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
734{
735    switch(obj) {
736''')
737
738            # For each field
739            for enum in self.enums.itervalues():
740                code('''
741      case ${{self.c_ident}}_${{enum.ident}}:
742        return ${{enum.ident}}_Controller::getNumControllers();
743''')
744
745            # total num
746            code('''
747      case ${{self.c_ident}}_NUM:
748      default:
749        panic("Invalid range for type ${{self.c_ident}}");
750    }
751}
752''')
753
754            for enum in self.enums.itervalues():
755                if enum.ident == "DMA":
756                    code('''
757MachineID
758map_Address_to_DMA(const Address &addr)
759{
760      MachineID dma = {MachineType_DMA, 0};
761      return dma;
762}
763''')
764
765                code('''
766
767MachineID
768get${{enum.ident}}MachineID(NodeID RubyNode)
769{
770      MachineID mach = {MachineType_${{enum.ident}}, RubyNode};
771      return mach;
772}
773''')
774
775        # Write the file
776        code.write(path, "%s.cc" % self.c_ident)
777
778__all__ = [ "Type" ]
779