PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/src/arch/micro_asm.py

https://bitbucket.org/musleh123/gem5_cetus
Python | 500 lines | 357 code | 59 blank | 84 comment | 14 complexity | f021d85c4b4e4bc9f691cc5d39e6ffdd MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
  1. # Copyright (c) 2003-2005 The Regents of The University of Michigan
  2. # All rights reserved.
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions are
  6. # met: redistributions of source code must retain the above copyright
  7. # notice, this list of conditions and the following disclaimer;
  8. # redistributions in binary form must reproduce the above copyright
  9. # notice, this list of conditions and the following disclaimer in the
  10. # documentation and/or other materials provided with the distribution;
  11. # neither the name of the copyright holders nor the names of its
  12. # contributors may be used to endorse or promote products derived from
  13. # this software without specific prior written permission.
  14. #
  15. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  16. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  17. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  18. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  19. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  20. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  21. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  22. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  23. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  25. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. #
  27. # Authors: Gabe Black
  28. import os
  29. import sys
  30. import re
  31. import string
  32. import traceback
  33. # get type names
  34. from types import *
  35. from ply import lex
  36. from ply import yacc
  37. ##########################################################################
  38. #
  39. # Base classes for use outside of the assembler
  40. #
  41. ##########################################################################
  42. class Micro_Container(object):
  43. def __init__(self, name):
  44. self.microops = []
  45. self.name = name
  46. self.directives = {}
  47. self.micro_classes = {}
  48. self.labels = {}
  49. def add_microop(self, mnemonic, microop):
  50. self.microops.append(microop)
  51. def __str__(self):
  52. string = "%s:\n" % self.name
  53. for microop in self.microops:
  54. string += " %s\n" % microop
  55. return string
  56. class Combinational_Macroop(Micro_Container):
  57. pass
  58. class Rom_Macroop(object):
  59. def __init__(self, name, target):
  60. self.name = name
  61. self.target = target
  62. def __str__(self):
  63. return "%s: %s\n" % (self.name, self.target)
  64. class Rom(Micro_Container):
  65. def __init__(self, name):
  66. super(Rom, self).__init__(name)
  67. self.externs = {}
  68. ##########################################################################
  69. #
  70. # Support classes
  71. #
  72. ##########################################################################
  73. class Label(object):
  74. def __init__(self):
  75. self.extern = False
  76. self.name = ""
  77. class Block(object):
  78. def __init__(self):
  79. self.statements = []
  80. class Statement(object):
  81. def __init__(self):
  82. self.is_microop = False
  83. self.is_directive = False
  84. self.params = ""
  85. class Microop(Statement):
  86. def __init__(self):
  87. super(Microop, self).__init__()
  88. self.mnemonic = ""
  89. self.labels = []
  90. self.is_microop = True
  91. class Directive(Statement):
  92. def __init__(self):
  93. super(Directive, self).__init__()
  94. self.name = ""
  95. self.is_directive = True
  96. ##########################################################################
  97. #
  98. # Functions that handle common tasks
  99. #
  100. ##########################################################################
  101. def print_error(message):
  102. print
  103. print "*** %s" % message
  104. print
  105. def handle_statement(parser, container, statement):
  106. if statement.is_microop:
  107. if statement.mnemonic not in parser.microops.keys():
  108. raise Exception, "Unrecognized mnemonic: %s" % statement.mnemonic
  109. parser.symbols["__microopClassFromInsideTheAssembler"] = \
  110. parser.microops[statement.mnemonic]
  111. try:
  112. microop = eval('__microopClassFromInsideTheAssembler(%s)' %
  113. statement.params, {}, parser.symbols)
  114. except:
  115. print_error("Error creating microop object with mnemonic %s." % \
  116. statement.mnemonic)
  117. raise
  118. try:
  119. for label in statement.labels:
  120. container.labels[label.text] = microop
  121. if label.is_extern:
  122. container.externs[label.text] = microop
  123. container.add_microop(statement.mnemonic, microop)
  124. except:
  125. print_error("Error adding microop.")
  126. raise
  127. elif statement.is_directive:
  128. if statement.name not in container.directives.keys():
  129. raise Exception, "Unrecognized directive: %s" % statement.name
  130. parser.symbols["__directiveFunctionFromInsideTheAssembler"] = \
  131. container.directives[statement.name]
  132. try:
  133. eval('__directiveFunctionFromInsideTheAssembler(%s)' %
  134. statement.params, {}, parser.symbols)
  135. except:
  136. print_error("Error executing directive.")
  137. print container.directives
  138. raise
  139. else:
  140. raise Exception, "Didn't recognize the type of statement", statement
  141. ##########################################################################
  142. #
  143. # Lexer specification
  144. #
  145. ##########################################################################
  146. # Error handler. Just call exit. Output formatted to work under
  147. # Emacs compile-mode. Optional 'print_traceback' arg, if set to True,
  148. # prints a Python stack backtrace too (can be handy when trying to
  149. # debug the parser itself).
  150. def error(lineno, string, print_traceback = False):
  151. # Print a Python stack backtrace if requested.
  152. if (print_traceback):
  153. traceback.print_exc()
  154. if lineno != 0:
  155. line_str = "%d:" % lineno
  156. else:
  157. line_str = ""
  158. sys.exit("%s %s" % (line_str, string))
  159. reserved = ('DEF', 'MACROOP', 'ROM', 'EXTERN')
  160. tokens = reserved + (
  161. # identifier
  162. 'ID',
  163. # arguments for microops and directives
  164. 'PARAMS',
  165. 'LPAREN', 'RPAREN',
  166. 'LBRACE', 'RBRACE',
  167. 'COLON', 'SEMI', 'DOT',
  168. 'NEWLINE'
  169. )
  170. # New lines are ignored at the top level, but they end statements in the
  171. # assembler
  172. states = (
  173. ('asm', 'exclusive'),
  174. ('params', 'exclusive'),
  175. )
  176. reserved_map = { }
  177. for r in reserved:
  178. reserved_map[r.lower()] = r
  179. # Ignore comments
  180. def t_ANY_COMMENT(t):
  181. r'\#[^\n]*(?=\n)'
  182. def t_ANY_MULTILINECOMMENT(t):
  183. r'/\*([^/]|((?<!\*)/))*\*/'
  184. # A colon marks the end of a label. It should follow an ID which will
  185. # put the lexer in the "params" state. Seeing the colon will put it back
  186. # in the "asm" state since it knows it saw a label and not a mnemonic.
  187. def t_params_COLON(t):
  188. r':'
  189. t.lexer.begin('asm')
  190. return t
  191. # Parameters are a string of text which don't contain an unescaped statement
  192. # statement terminator, ie a newline or semi colon.
  193. def t_params_PARAMS(t):
  194. r'([^\n;\\]|(\\[\n;\\]))+'
  195. t.lineno += t.value.count('\n')
  196. unescapeParamsRE = re.compile(r'(\\[\n;\\])')
  197. def unescapeParams(mo):
  198. val = mo.group(0)
  199. return val[1]
  200. t.value = unescapeParamsRE.sub(unescapeParams, t.value)
  201. t.lexer.begin('asm')
  202. return t
  203. # An "ID" in the micro assembler is either a label, directive, or mnemonic
  204. # If it's either a directive or a mnemonic, it will be optionally followed by
  205. # parameters. If it's a label, the following colon will make the lexer stop
  206. # looking for parameters.
  207. def t_asm_ID(t):
  208. r'[A-Za-z_]\w*'
  209. t.type = reserved_map.get(t.value, 'ID')
  210. # If the ID is really "extern", we shouldn't start looking for parameters
  211. # yet. The real ID, the label itself, is coming up.
  212. if t.type != 'EXTERN':
  213. t.lexer.begin('params')
  214. return t
  215. # If there is a label and you're -not- in the assembler (which would be caught
  216. # above), don't start looking for parameters.
  217. def t_ANY_ID(t):
  218. r'[A-Za-z_]\w*'
  219. t.type = reserved_map.get(t.value, 'ID')
  220. return t
  221. # Braces enter and exit micro assembly
  222. def t_INITIAL_LBRACE(t):
  223. r'\{'
  224. t.lexer.begin('asm')
  225. return t
  226. def t_asm_RBRACE(t):
  227. r'\}'
  228. t.lexer.begin('INITIAL')
  229. return t
  230. # At the top level, keep track of newlines only for line counting.
  231. def t_INITIAL_NEWLINE(t):
  232. r'\n+'
  233. t.lineno += t.value.count('\n')
  234. # In the micro assembler, do line counting but also return a token. The
  235. # token is needed by the parser to detect the end of a statement.
  236. def t_asm_NEWLINE(t):
  237. r'\n+'
  238. t.lineno += t.value.count('\n')
  239. return t
  240. # A newline or semi colon when looking for params signals that the statement
  241. # is over and the lexer should go back to looking for regular assembly.
  242. def t_params_NEWLINE(t):
  243. r'\n+'
  244. t.lineno += t.value.count('\n')
  245. t.lexer.begin('asm')
  246. return t
  247. def t_params_SEMI(t):
  248. r';'
  249. t.lexer.begin('asm')
  250. return t
  251. # Basic regular expressions to pick out simple tokens
  252. t_ANY_LPAREN = r'\('
  253. t_ANY_RPAREN = r'\)'
  254. t_ANY_SEMI = r';'
  255. t_ANY_DOT = r'\.'
  256. t_ANY_ignore = ' \t\x0c'
  257. def t_ANY_error(t):
  258. error(t.lineno, "illegal character '%s'" % t.value[0])
  259. t.skip(1)
  260. ##########################################################################
  261. #
  262. # Parser specification
  263. #
  264. ##########################################################################
  265. # Start symbol for a file which may have more than one macroop or rom
  266. # specification.
  267. def p_file(t):
  268. 'file : opt_rom_or_macros'
  269. def p_opt_rom_or_macros_0(t):
  270. 'opt_rom_or_macros : '
  271. def p_opt_rom_or_macros_1(t):
  272. 'opt_rom_or_macros : rom_or_macros'
  273. def p_rom_or_macros_0(t):
  274. 'rom_or_macros : rom_or_macro'
  275. def p_rom_or_macros_1(t):
  276. 'rom_or_macros : rom_or_macros rom_or_macro'
  277. def p_rom_or_macro_0(t):
  278. '''rom_or_macro : rom_block
  279. | macroop_def'''
  280. # Defines a section of microcode that should go in the current ROM
  281. def p_rom_block(t):
  282. 'rom_block : DEF ROM block SEMI'
  283. if not t.parser.rom:
  284. print_error("Rom block found, but no Rom object specified.")
  285. raise TypeError, "Rom block found, but no Rom object was specified."
  286. for statement in t[3].statements:
  287. handle_statement(t.parser, t.parser.rom, statement)
  288. t[0] = t.parser.rom
  289. # Defines a macroop that jumps to an external label in the ROM
  290. def p_macroop_def_0(t):
  291. 'macroop_def : DEF MACROOP ID LPAREN ID RPAREN SEMI'
  292. if not t.parser.rom_macroop_type:
  293. print_error("ROM based macroop found, but no ROM macroop class was specified.")
  294. raise TypeError, "ROM based macroop found, but no ROM macroop class was specified."
  295. macroop = t.parser.rom_macroop_type(t[3], t[5])
  296. t.parser.macroops[t[3]] = macroop
  297. # Defines a macroop that is combinationally generated
  298. def p_macroop_def_1(t):
  299. 'macroop_def : DEF MACROOP ID block SEMI'
  300. try:
  301. curop = t.parser.macro_type(t[3])
  302. except TypeError:
  303. print_error("Error creating macroop object.")
  304. raise
  305. for statement in t[4].statements:
  306. handle_statement(t.parser, curop, statement)
  307. t.parser.macroops[t[3]] = curop
  308. # A block of statements
  309. def p_block(t):
  310. 'block : LBRACE statements RBRACE'
  311. block = Block()
  312. block.statements = t[2]
  313. t[0] = block
  314. def p_statements_0(t):
  315. 'statements : statement'
  316. if t[1]:
  317. t[0] = [t[1]]
  318. else:
  319. t[0] = []
  320. def p_statements_1(t):
  321. 'statements : statements statement'
  322. if t[2]:
  323. t[1].append(t[2])
  324. t[0] = t[1]
  325. def p_statement(t):
  326. 'statement : content_of_statement end_of_statement'
  327. t[0] = t[1]
  328. # A statement can be a microop or an assembler directive
  329. def p_content_of_statement_0(t):
  330. '''content_of_statement : microop
  331. | directive'''
  332. t[0] = t[1]
  333. # Ignore empty statements
  334. def p_content_of_statement_1(t):
  335. 'content_of_statement : '
  336. pass
  337. # Statements are ended by newlines or a semi colon
  338. def p_end_of_statement(t):
  339. '''end_of_statement : NEWLINE
  340. | SEMI'''
  341. pass
  342. # Different flavors of microop to avoid shift/reduce errors
  343. def p_microop_0(t):
  344. 'microop : labels ID'
  345. microop = Microop()
  346. microop.labels = t[1]
  347. microop.mnemonic = t[2]
  348. t[0] = microop
  349. def p_microop_1(t):
  350. 'microop : ID'
  351. microop = Microop()
  352. microop.mnemonic = t[1]
  353. t[0] = microop
  354. def p_microop_2(t):
  355. 'microop : labels ID PARAMS'
  356. microop = Microop()
  357. microop.labels = t[1]
  358. microop.mnemonic = t[2]
  359. microop.params = t[3]
  360. t[0] = microop
  361. def p_microop_3(t):
  362. 'microop : ID PARAMS'
  363. microop = Microop()
  364. microop.mnemonic = t[1]
  365. microop.params = t[2]
  366. t[0] = microop
  367. # Labels in the microcode
  368. def p_labels_0(t):
  369. 'labels : label'
  370. t[0] = [t[1]]
  371. def p_labels_1(t):
  372. 'labels : labels label'
  373. t[1].append(t[2])
  374. t[0] = t[1]
  375. # labels on lines by themselves are attached to the following instruction.
  376. def p_labels_2(t):
  377. 'labels : labels NEWLINE'
  378. t[0] = t[1]
  379. def p_label_0(t):
  380. 'label : ID COLON'
  381. label = Label()
  382. label.is_extern = False
  383. label.text = t[1]
  384. t[0] = label
  385. def p_label_1(t):
  386. 'label : EXTERN ID COLON'
  387. label = Label()
  388. label.is_extern = True
  389. label.text = t[2]
  390. t[0] = label
  391. # Directives for the macroop
  392. def p_directive_0(t):
  393. 'directive : DOT ID'
  394. directive = Directive()
  395. directive.name = t[2]
  396. t[0] = directive
  397. def p_directive_1(t):
  398. 'directive : DOT ID PARAMS'
  399. directive = Directive()
  400. directive.name = t[2]
  401. directive.params = t[3]
  402. t[0] = directive
  403. # Parse error handler. Note that the argument here is the offending
  404. # *token*, not a grammar symbol (hence the need to use t.value)
  405. def p_error(t):
  406. if t:
  407. error(t.lineno, "syntax error at '%s'" % t.value)
  408. else:
  409. error(0, "unknown syntax error", True)
  410. class MicroAssembler(object):
  411. def __init__(self, macro_type, microops,
  412. rom = None, rom_macroop_type = None):
  413. self.lexer = lex.lex()
  414. self.parser = yacc.yacc()
  415. self.parser.macro_type = macro_type
  416. self.parser.macroops = {}
  417. self.parser.microops = microops
  418. self.parser.rom = rom
  419. self.parser.rom_macroop_type = rom_macroop_type
  420. self.parser.symbols = {}
  421. self.symbols = self.parser.symbols
  422. def assemble(self, asm):
  423. self.parser.parse(asm, lexer=self.lexer)
  424. macroops = self.parser.macroops
  425. self.parser.macroops = {}
  426. return macroops