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