text: # Copyright lowRISC contributors.
  # Licensed under the Apache License, Version 2.0, see LICENSE for details.
  # SPDX-License-Identifier: Apache-2.0
  
  from typing import Optional
  
  from shared.insn_yaml import InsnsFile
  
  from ..config import Config
  from ..program import ProgInsn, Program
  from ..model import Model
  from ..snippet import ProgSnippet, Snippet
  from ..snippet_gen import GenCont, GenRet, SnippetGen
  
  
  class ECall(SnippetGen):
      '''A generator that makes a snippet with a single ECALL instruction'''
  
      ends_program = True
  
      def __init__(self, cfg: Config, insns_file: InsnsFile) -> None:
          super().__init__()
  
          ecall_insn = self._get_named_insn(insns_file, 'ecall')
  
          if ecall_insn.operands:
              raise RuntimeError('ECALL instruction in instructions file '
                                 'has a nonempty list of operands.')
  
          if ecall_insn.lsu:
              raise RuntimeError('ECALL instruction in instructions file '
                                 'has unexpected LSU information.')
  
          self.insn = ProgInsn(ecall_insn, [], None)
  
          if cfg.insn_weights.values.get('ecall') is not None:
              raise ValueError('Config at {} configures the instruction '
                               'weight of ECALL. This has no effect: configure '
                               'the weight of the ECall generator instead.'
                               .format(cfg.path))
  
      def gen_at(self, pc: int, program: Program) -> Snippet:
          '''Generate an ECALL instruction at pc and insert into program'''
          snippet = ProgSnippet(pc, [self.insn])
          snippet.insert_into_program(program)
          return snippet
  
      def gen(self,
              cont: GenCont,
              model: Model,
              program: Program) -> Optional[GenRet]:
          return (self.gen_at(model.pc, program), model)
  
      def pick_weight(self,
                      model: Model,
                      program: Program) -> float:
          # Choose small weights when we've got lots of room and large ones when
          # we haven't.
          room = min(model.fuel, program.space)
          assert 0 < room
          return (1e-10 if room > 5
                  else 0.1 if room > 1
                  else 1e10)
  