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