Class: OrigenSim::Simulation

Inherits:
Object
  • Object
show all
Defined in:
lib/origen_sim/simulation.rb

Overview

Responsible for managing each individual simulation that is run in an Origen thread e.g. If multiple patterns are run in separate simulations, then one instance of this class will exist for each one.

It is primarily responsible for all communications with the simulation and capturing log output and errors.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id, view_wave_command) ⇒ Simulation

Returns a new instance of Simulation.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/origen_sim/simulation.rb', line 22

def initialize(id, view_wave_command)
  @id = id
  @view_wave_command = view_wave_command
  @completed_cleanly = false
  @failed_to_start = false
  @logged_errors = false
  @error_count = 0
  @socket_ids = {}

  # Socket used to send Origen -> Verilog commands
  @server = UNIXServer.new(socket_id)

  # Socket used to capture STDOUT from the simulator
  @server_stdout = UNIXServer.new(socket_id(:stdout))
  # Socket used to capture STDERR from the simulator
  @server_stderr = UNIXServer.new(socket_id(:stderr))
  # Socket used to send a heartbeat pulse from Origen to process running the simulator
  @server_heartbeat = UNIXServer.new(socket_id(:heartbeat))
  # Socket used to receive status updates from the process running the simulator
  @server_status = UNIXServer.new(socket_id(:status))
end

Instance Attribute Details

#completed_cleanlyObject

Returns the value of attribute completed_cleanly.



16
17
18
# File 'lib/origen_sim/simulation.rb', line 16

def completed_cleanly
  @completed_cleanly
end

#error_countObject

Returns the value of attribute error_count.



16
17
18
# File 'lib/origen_sim/simulation.rb', line 16

def error_count
  @error_count
end

#failed_to_startObject

Returns the value of attribute failed_to_start.



16
17
18
# File 'lib/origen_sim/simulation.rb', line 16

def failed_to_start
  @failed_to_start
end

#idObject (readonly)

Returns the value of attribute id.



14
15
16
# File 'lib/origen_sim/simulation.rb', line 14

def id
  @id
end

#logged_errorsObject

Returns the value of attribute logged_errors.



16
17
18
# File 'lib/origen_sim/simulation.rb', line 16

def logged_errors
  @logged_errors
end

#pidObject

Returns the value of attribute pid.



17
18
19
# File 'lib/origen_sim/simulation.rb', line 17

def pid
  @pid
end

#socketObject (readonly)

Returns the communication socket used for sending commands to the Origen VPI running in the simulation process



20
21
22
# File 'lib/origen_sim/simulation.rb', line 20

def socket
  @socket
end

#view_wave_commandObject (readonly)

Returns the value of attribute view_wave_command.



14
15
16
# File 'lib/origen_sim/simulation.rb', line 14

def view_wave_command
  @view_wave_command
end

Instance Method Details

#abort_connectionObject



153
154
155
156
157
158
159
160
# File 'lib/origen_sim/simulation.rb', line 153

def abort_connection
  # If the Verilog process has not established a connection yet, then make one to
  # release our process and then exit
  unless @connection_established
    @connection_aborted = true
    UNIXSocket.new(socket_id).puts("Time out\n")
  end
end

#closeObject

Close all communication channels with the simulator



163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/origen_sim/simulation.rb', line 163

def close
  return unless @opened
  stop_heartbeat
  @stdout_reader.stop
  @stderr_reader.stop
  @heartbeat.close
  @socket.close
  @stderr.close
  @stdout.close
  File.unlink(socket_id(:heartbeat)) if File.exist?(socket_id(:heartbeat))
  File.unlink(socket_id) if File.exist?(socket_id)
  File.unlink(socket_id(:stderr)) if File.exist?(socket_id(:stderr))
  File.unlink(socket_id(:stdout)) if File.exist?(socket_id(:stdout))
end

#failed?(in_progress = false) ⇒ Boolean

Returns:

  • (Boolean)


44
45
46
47
48
49
50
51
# File 'lib/origen_sim/simulation.rb', line 44

def failed?(in_progress = false)
  failed = stderr_logged_errors || logged_errors || failed_to_start || error_count > 0
  if in_progress
    failed
  else
    failed || !completed_cleanly
  end
end

#log_results(in_progress = false) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/origen_sim/simulation.rb', line 61

def log_results(in_progress = false)
  if failed?(in_progress)
    if failed_to_start
      if Origen.debugger_enabled?
        Origen.log.error 'The simulation failed to get underway!'
      else
        Origen.log.error 'The simulation failed to get underway! (run again with -d to see why)'
      end
    else
      if in_progress
        Origen.log.error "The simulation has #{error_count} error#{error_count > 1 ? 's' : ''}!" if error_count > 0
      else
        Origen.log.error "The simulation failed with #{error_count} errors!" if error_count > 0
      end
      Origen.log.error 'The simulation log reported errors!' if logged_errors
      Origen.log.error 'The simulation stderr reported errors!' if stderr_logged_errors
      Origen.log.error 'The simulation exited early!' unless completed_cleanly || in_progress
    end
  else
    if in_progress
      Origen.log.success 'The simulation is passing!'
    else
      Origen.log.success 'The simulation passed!'
    end
  end
end

#open(timeout) ⇒ Object

Open the communication channels with the simulator



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/origen_sim/simulation.rb', line 102

def open(timeout)
  timeout_connection(timeout) do
    start_heartbeat
    @stdout = @server_stdout.accept
    @stderr = @server_stderr.accept
    @status = @server_status.accept
    @stdout_reader = StdoutReader.new(@stdout)
    @stderr_reader = StderrReader.new(@stderr)

    Origen.log.debug 'The simulation monitor has started'
    Origen.log.debug @status.gets.chomp  # Starting simulator
    Origen.log.debug @status.gets.chomp  # Simulator has started
    response = @status.gets.chomp
    if response =~ /finished/
      abort_connection
    else
      @pid = response.to_i
    end
    # That's all status info done until the simulation process ends, start a thread
    # to wait for that in case it ends before the VPI starts
    Thread.new do
      Origen.log.debug @status.gets.chomp  # This will block until something is received
      abort_connection
    end
    Origen.log.debug 'Waiting for Origen VPI to start...'

    # This will block until the VPI extension is invoked and connects to the socket
    @socket = @server.accept

    @connection_established = true # Cancels timeout_connection
    if @connection_aborted
      self.failed_to_start = true
      log_results
      exit  # Assume it is not worth trying another pattern in this case, some kind of environment/config issue
    end
    Origen.log.debug 'Origen VPI has started'
  end

  @opened = true
end

#running?Boolean

Returns true if the simulation is running

Returns:

  • (Boolean)


179
180
181
182
183
184
185
186
187
# File 'lib/origen_sim/simulation.rb', line 179

def running?
  return false unless pid
  begin
    Process.getpgid(pid)
    true
  rescue Errno::ESRCH
    false
  end
end

#socket_id(type = nil) ⇒ Object



189
190
191
# File 'lib/origen_sim/simulation.rb', line 189

def socket_id(type = nil)
  @socket_ids[type] ||= "/tmp/#{socket_number}#{type}.sock"
end

#start_heartbeatObject

Provide a heartbeat to let the parallel Ruby process in charge of the simulator know that the master Origen process is still alive. If the Origen process crashes and leaves the simulator running, the child process will automatically reap it after a couple of missed heartbeats.



92
93
94
95
# File 'lib/origen_sim/simulation.rb', line 92

def start_heartbeat
  @heartbeat = @server_heartbeat.accept
  @heartbeat_thread = Heartbeat.new(@heartbeat)
end

#stderr_logged_errorsObject



57
58
59
# File 'lib/origen_sim/simulation.rb', line 57

def stderr_logged_errors
  @stderr_reader.logged_errors
end

#stop_heartbeatObject



97
98
99
# File 'lib/origen_sim/simulation.rb', line 97

def stop_heartbeat
  @heartbeat_thread.stop
end

#timeout_connection(wait_in_s) ⇒ Object



143
144
145
146
147
148
149
150
151
# File 'lib/origen_sim/simulation.rb', line 143

def timeout_connection(wait_in_s)
  @connection_aborted = false
  @connection_established = false
  Thread.new do
    sleep wait_in_s
    abort_connection # Will do nothing if a successful connection has been made while we were waiting
  end
  yield
end