OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [devs/] [watchdog/] [synth/] [current/] [host/] [watchdog.tcl] - Blame information for rev 786

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 786 skrzyp
# {{{  Banner                                                   
2
 
3
# ============================================================================
4
# 
5
#      watchdog.tcl
6
# 
7
#      Watchdog support for the eCos synthetic target I/O auxiliary
8
# 
9
# ============================================================================
10
# ####ECOSHOSTGPLCOPYRIGHTBEGIN####                                         
11
# -------------------------------------------                               
12
# This file is part of the eCos host tools.                                 
13
# Copyright (C) 2002 Free Software Foundation, Inc.                         
14
#
15
# This program is free software; you can redistribute it and/or modify      
16
# it under the terms of the GNU General Public License as published by      
17
# the Free Software Foundation; either version 2 or (at your option) any    
18
# later version.                                                            
19
#
20
# This program is distributed in the hope that it will be useful, but       
21
# WITHOUT ANY WARRANTY; without even the implied warranty of                
22
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         
23
# General Public License for more details.                                  
24
#
25
# You should have received a copy of the GNU General Public License         
26
# along with this program; if not, write to the                             
27
# Free Software Foundation, Inc., 51 Franklin Street,                       
28
# Fifth Floor, Boston, MA  02110-1301, USA.                                 
29
# -------------------------------------------                               
30
# ####ECOSHOSTGPLCOPYRIGHTEND####                                           
31
# ============================================================================
32
# #####DESCRIPTIONBEGIN####
33
# 
34
#  Author(s):   bartv
35
#  Contact(s):  bartv
36
#  Date:        2002/09/04
37
#  Version:     0.01
38
#  Description:
39
#      Implementation of the watchdog device. This script should only ever
40
#      be run from inside the ecosynth auxiliary.
41
# 
42
# ####DESCRIPTIONEND####
43
# ============================================================================
44
 
45
# }}}
46
 
47
namespace eval watchdog {
48
 
49
    # Was initialization successful?
50
    variable init_ok 1
51
 
52
    # Has the alarm triggered?
53
    variable alarm_triggered 0
54
 
55
    # The eCos application process id. This is needed to send a SIGPWR signal
56
    # if the watchdog triggers, and to access /proc/<pid>/stat to obtain
57
    # timing information. Strictly speaking _ppid is not exported by
58
    # the I/O auxiliary.
59
    if { ! [info exists synth::_ppid] } {
60
        synth::report_error "Watchdog initialization failed, _ppid variable required"
61
        return ""
62
    }
63
    variable ecos_pid $synth::_ppid
64
 
65
    # Resolution, i.e. how long to go between checks. Currently this is hard-wired
66
    # to one second, or 1000 ms. This may become configurable, either on the
67
    # target-side via CDL or on the host-side via the target definition file.
68
    # Note that currently the watchdog device and the GUI get updated via the
69
    # same timer. If the resolution is changed to e.g. 10 seconds then it might
70
    # be a good idea to update the GUI more frequently, although there are
71
    # arguments for keeping the animation in step with the real work.
72
    variable resolution 1000
73
 
74
    # Options from the target definition file
75
    variable use_wallclock 0
76
    variable window_pack   "-in .main.n -side right"
77
    variable sound_file    ""
78
    variable sound_player  "play"
79
 
80
    if { [synth::tdf_has_device "watchdog"] } {
81
        if { [synth::tdf_has_option "watchdog" "use"] } {
82
            set _use [synth::tdf_get_option "watchdog" "use"]
83
            if { "wallclock_time" == $_use } {
84
                set watchdog::use_wallclock 1
85
            } elseif { "consumed_cpu_time" == $_use } {
86
                set watchdog::use_wallclock 0
87
            } else {
88
                synth::report_error "Invalid entry in target definition file $synth::target_definition\n\
89
                                    \    Device watchdog, option \"use\" should be \"wallclock_time\" or \"consumed_cpu_time\"\n"
90
            }
91
            unset _use
92
        }
93
        if { [synth::tdf_has_option "watchdog" "watchdog_pack"] } {
94
            set watchdog::window_pack [synth::tdf_get_option "watchdog" "watchdog_pack"]
95
            # Too complicated to validate here, instead leave it to a catch statement
96
            # when the window actually gets packed
97
        }
98
        if { [synth::tdf_has_option "watchdog" "sound"] } {
99
            set _sound_file [synth::tdf_get_option "watchdog" "sound"]
100
            # Look for this sound file in the install tree first, then absolute or relative
101
            if { [file exists [file join $synth::device_install_dir $_sound_file] ] } {
102
                set _sound_file [file join $synth::device_install_dir $_sound_file]
103
            }
104
            if { ![file exists $_sound_file] } {
105
                synth::report_error "Invalid entry in target definition file $synth::target_definition\n\
106
                                    \    Device watchdog, option \"sound\", failed to find $_sound_file\n"
107
            } elseif { ! [file readable $_sound_file] } {
108
                synth::report_error "Invalid entry in target definition file $synth::target_definition\n\
109
                                    \    Device watchdog, option \"sound\", no read access to file $_sound_file\n"
110
            } else {
111
                set watchdog::sound_file $_sound_file
112
            }
113
            unset _sound_file
114
        }
115
        if { [synth::tdf_has_option "watchdog" "sound_player"] } {
116
            set watchdog::sound_player [synth::tdf_get_option "watchdog" "sound_player"]
117
        }
118
    }
119
 
120
    # There is no point in creating the watchdog window if any of the image files are missing
121
    if { $synth::flag_gui } {
122
        foreach _image [list "doghouse.gif" "alarm.gif" "eye.gif" "asleep.gif"] {
123
            variable image_[file rootname $_image]
124
            if { ! [synth::load_image "watchdog::image_[file rootname $_image]" [file join $synth::device_install_dir $_image]] } {
125
                synth::report_error "Watchdog device, unable to load image $_image\n\
126
                                    \    This file should have been installed in $synth::device_install_dir\n"
127
                set watchdog::init_ok 0
128
            }
129
        }
130
    }
131
    if { $synth::flag_gui && $watchdog::init_ok } {
132
        canvas .watchdog -width [image width $image_doghouse] -height [image height $image_doghouse] \
133
            -borderwidth 0
134
        variable background [.watchdog create image 0 0 -anchor nw -image $image_doghouse]
135
 
136
        # Eye positions inside the doghouse. The eye is an 8x10 gif,
137
        # mostly white but transparent around the corners
138
        variable left_eye_x         48
139
        variable left_eye_y         70
140
        variable right_eye_x        58
141
        variable right_eye_y        70
142
 
143
        # Pupil positions relative to the eye. The pupils are 3x3 rectangles.
144
        # The dog can look in one of nine different directions, with both eyes
145
        # looking in the same direction (if visible)
146
        variable pupil_positions { { 1 6 } { 1 5 } { 1 3 } { 3 1 } { 3 4 } { 3 6 } { 4 3 } { 4 5 } { 4 6 } }
147
 
148
        # Which eyes are currently visible: none, left, right or both
149
        variable eyes "none"
150
        # What is the current pupil position?
151
        variable pupils 4
152
 
153
        variable left_eye  [.watchdog create image $left_eye_x $left_eye_y -anchor nw -image $image_eye]
154
        variable right_eye [.watchdog create image $right_eye_x $right_eye_y -anchor nw -image $image_eye]
155
 
156
        variable left_pupil \
157
            [.watchdog create rectangle \
158
                 [expr $left_eye_x + [lindex [lindex $pupil_positions $pupils] 0]]     \
159
                 [expr $left_eye_y + [lindex [lindex $pupil_positions $pupils] 1]]     \
160
                 [expr $left_eye_x + [lindex [lindex $pupil_positions $pupils] 0] + 2] \
161
                 [expr $left_eye_y + [lindex [lindex $pupil_positions $pupils] 1] + 2] \
162
                 -fill black]
163
        variable right_pupil \
164
            [.watchdog create rectangle \
165
                 [expr $right_eye_x + [lindex [lindex $pupil_positions $pupils] 0]]     \
166
                 [expr $right_eye_y + [lindex [lindex $pupil_positions $pupils] 1]]     \
167
                 [expr $right_eye_x + [lindex [lindex $pupil_positions $pupils] 0] + 2] \
168
                 [expr $right_eye_y + [lindex [lindex $pupil_positions $pupils] 1] + 2] \
169
                 -fill black]
170
 
171
 
172
        # The dog is asleep until the eCos application activates the watchdog device
173
        .watchdog lower $left_eye $background
174
        .watchdog lower $right_eye $background
175
        .watchdog lower $left_pupil $background
176
        .watchdog lower $right_pupil $background
177
 
178
        # Prepare for an alarm, but obviously the alarm picture should be hidden for now.
179
        variable alarm [.watchdog create image 30 56 -anchor nw -image $image_alarm]
180
        .watchdog lower $alarm $background
181
 
182
        # Start asleep
183
        variable asleep [.watchdog create image 48 70 -anchor nw -image $image_asleep]
184
 
185
        # Now try to pack the watchdog window using the option provided by the
186
        # user. If that fails, report the error and pack in a default window.
187
        if { [catch { eval pack .watchdog $watchdog::window_pack } message] } {
188
            synth::report_error "Watchdog device, failed to pack window in $watchdog::window_pack\n    $message"
189
            pack .watchdog -in .main.n -side right
190
        }
191
 
192
        # Updating the display. This happens once a second.
193
        # If neither eye is visible, choose randomly between
194
        # left-only, right-only or both. Otherwise there is
195
        # a one in eight chance of blinking, probably switching
196
        # to one of the other eye modes
197
        #
198
        # Also, the visible pupil(s) will move every second, to one
199
        # of nine positions
200
        proc gui_update { } {
201
 
202
            if { "none" == $watchdog::eyes} {
203
                set rand [expr int(3 * rand())]
204
                if { 0 == $rand } {
205
                    set watchdog::eyes "left"
206
                    .watchdog raise $watchdog::left_eye   $watchdog::background
207
                    .watchdog raise $watchdog::left_pupil $watchdog::left_eye
208
                } elseif { 1 == $rand } {
209
                    set watchdog::eyes "right"
210
                    .watchdog raise $watchdog::right_eye   $watchdog::background
211
                    .watchdog raise $watchdog::right_pupil $watchdog::right_eye
212
                } else {
213
                    set watchdog::eyes "both"
214
                    .watchdog raise $watchdog::left_eye    $watchdog::background
215
                    .watchdog raise $watchdog::left_pupil  $watchdog::left_eye
216
                    .watchdog raise $watchdog::right_eye   $watchdog::background
217
                    .watchdog raise $watchdog::right_pupil $watchdog::right_eye
218
                }
219
            } else {
220
                if { 0 == [expr int(8 * rand())] } {
221
                    set watchdog::eyes "none"
222
                    .watchdog lower $watchdog::left_eye    $watchdog::background
223
                    .watchdog lower $watchdog::right_eye   $watchdog::background
224
                    .watchdog lower $watchdog::left_pupil  $watchdog::background
225
                    .watchdog lower $watchdog::right_pupil $watchdog::background
226
 
227
                    # There is no point in moving the pupils if both eyes are shut
228
                    return
229
                }
230
            }
231
 
232
            set watchdog::pupils [expr int(9 * rand())]
233
            set new_pupil_x [lindex [lindex $watchdog::pupil_positions $watchdog::pupils] 0]
234
            set new_pupil_y [lindex [lindex $watchdog::pupil_positions $watchdog::pupils] 1]
235
 
236
            if { ("left" == $watchdog::eyes) || ("both" == $watchdog::eyes) } {
237
                .watchdog coords $watchdog::left_pupil              \
238
                    [expr $watchdog::left_eye_x + $new_pupil_x]     \
239
                    [expr $watchdog::left_eye_y + $new_pupil_y]     \
240
                    [expr $watchdog::left_eye_x + $new_pupil_x + 2] \
241
                    [expr $watchdog::left_eye_y + $new_pupil_y + 2]
242
            }
243
            if { ("right" == $watchdog::eyes) || ("both" == $watchdog::eyes) } {
244
                .watchdog coords $watchdog::right_pupil              \
245
                    [expr $watchdog::right_eye_x + $new_pupil_x]     \
246
                    [expr $watchdog::right_eye_y + $new_pupil_y]     \
247
                    [expr $watchdog::right_eye_x + $new_pupil_x + 2] \
248
                    [expr $watchdog::right_eye_y + $new_pupil_y + 2]
249
            }
250
        }
251
 
252
        # Cancel the gui display when the eCos application has exited.
253
        # The watchdog is allowed to go back to sleep. If the application
254
        # exited because of the watchdog then of course the alarm picture
255
        # should remain visible, otherwise it would be just a flash.
256
        proc gui_cancel { } {
257
            .watchdog lower $watchdog::left_eye    $watchdog::background
258
            .watchdog lower $watchdog::right_eye   $watchdog::background
259
            .watchdog lower $watchdog::left_pupil  $watchdog::background
260
            .watchdog lower $watchdog::right_pupil $watchdog::background
261
            if { ! $watchdog::alarm_triggered } {
262
                .watchdog raise $watchdog::asleep      $watchdog::background
263
            }
264
        }
265
 
266
        # Raise the alarm. This involves hiding the eyes and raising
267
        # the alarm picture. If sound is enabled, the sound player
268
        # should be invoked
269
        proc gui_alarm { } {
270
            .watchdog lower $watchdog::asleep      $watchdog::background
271
            .watchdog lower $watchdog::left_eye    $watchdog::background
272
            .watchdog lower $watchdog::right_eye   $watchdog::background
273
            .watchdog lower $watchdog::left_pupil  $watchdog::background
274
            .watchdog lower $watchdog::right_pupil $watchdog::background
275
            .watchdog raise $watchdog::alarm       $watchdog::background
276
 
277
            if { "" != $watchdog::sound_file } {
278
                # Catch errors on the actual exec, e.g. if the sound player is
279
                # invalid, but play the sound in the background. If there are
280
                # problems actually playing the sound then the user should
281
                # still see a message on stderr. Blocking the entire auxiliary
282
                # for a few seconds is not acceptable.
283
                if { [catch { eval exec -- $watchdog::sound_player $watchdog::sound_file & } message] } {
284
                    synth::report_warning "Watchdog device, failed to play alarm sound file\n    $message\n"
285
                }
286
            }
287
        }
288
 
289
        set _watchdog_help [file join $synth::device_src_dir "doc" "devs-watchdog-synth.html"]
290
        if { ![file readable $_watchdog_help] } {
291
            synth::report_warning "Failed to locate synthetic watchdog documentation $_watchdog_help\n\
292
                                  \    Help->Watchdog target menu option disabled.\n"
293
            set _watchdog_help ""
294
        }
295
        if { "" == $_watchdog_help } {
296
            .menubar.help add command -label "Watchdog" -state disabled
297
        } else {
298
            .menubar.help add command -label "Watchdog" -command [list synth::handle_help "file://$_watchdog_help"]
299
        }
300
    }
301
 
302
    # Now for the real work. By default the watchdog is asleep. The eCos
303
    # application can activate it with a start message, which results
304
    # in an "after" timer. That runs once a second to check whether or not
305
    # the watchdog should trigger, and also updates the GUI.
306
    #
307
    # The target-side code should perform a watchdog reset at least once
308
    # a second, which involves another message to this script and the
309
    # setting of the reset_received flag.
310
    #
311
    # The update handler gets information about the eCos application using
312
    # /proc/<pid>/stat (see man 5 proc). The "state" field is important:
313
    # a state of T indicates that the application is stopped, probably
314
    # inside gdb, so cannot reset the watchdog. The other important field
315
    # is utime, the total number of jiffies (0.01 seconds) executed in
316
    # user space. The code maintains an open file handle to the /proc file.
317
 
318
    variable reset_received     0
319
    variable after_id           ""
320
    variable proc_stat          ""
321
    variable last_jiffies       0
322
 
323
    set _filename "/proc/[set watchdog::ecos_pid]/stat"
324
    if { [catch { open $_filename "r" } proc_stat ] } {
325
        synth::report_error "Watchdog device, failed to open $_filename\n    $proc_stat\n"
326
        set watchdog::init_ok 0
327
    }
328
    unset _filename
329
 
330
    proc update { } {
331
        set watchdog::after_id [after $watchdog::resolution watchdog::update]
332
        if { $synth::flag_gui } {
333
            watchdog::gui_update
334
        }
335
        seek $watchdog::proc_stat 0 "start"
336
        set line [gets $watchdog::proc_stat]
337
        scan $line "%*d %*s %s %*d %*d %*d %*d %*d %*lu %*lu %*lu %*lu %*lu %lu" state jiffies
338
 
339
        # In theory it is possible to examine the state field (the third argument).
340
        # If set to T then that indicates the eCos application is traced or
341
        # stopped, probably inside gdb, and it would make sense to act as if
342
        # the application had sent a reset. Unfortunately the state also appears
343
        # to be set to T if the application is blocked in a system call while
344
        # being debugged - including the idle select(), making the test useless.
345
        # FIXME: figure out how to distinguish between being blocked inside gdb
346
        # and being in a system call.
347
        #if { "T" == $state } {
348
        #    set watchdog::reset_received 1
349
        #    return
350
        #}
351
 
352
        # If there has been a recent reset the eCos application can continue to run for a bit longer.
353
        if { $watchdog::reset_received } {
354
            set watchdog::last_jiffies $jiffies
355
            set watchdog::reset_received 0
356
            return
357
        }
358
 
359
        # We have not received a reset. If the watchdog is using wallclock time then
360
        # that is serious. If the watchdog is using elapsed cpu time then the eCos
361
        # application may not actually have consumed a whole second of cpu time yet.
362
        if { $watchdog::use_wallclock || (($jiffies - $watchdog::last_jiffies) > ($watchdog::resolution / 10)) } {
363
            set watchdog::alarm_triggered 1
364
            # Report the situation via the main text window
365
            synth::report "Watchdog device: the eCos application has not sent a recent reset\n    Raising SIGPWR signal.\n"
366
            # Then kill off the eCos application
367
            exec kill -PWR $watchdog::ecos_pid
368
            # There is no point in another run of the timer
369
            after cancel $watchdog::after_id
370
            # And if the GUI is running, raise the alarm visually
371
            if { $synth::flag_gui } {
372
                watchdog::gui_alarm
373
            }
374
        }
375
    }
376
 
377
    # When the eCos application has exited, cancel the timer and
378
    # clean-up the GUI. Also get rid of the open file since the
379
    # /proc/<pid>/stat file is no longer meaningful
380
    proc exit_hook { arg_list } {
381
        if { "" != $watchdog::after_id } {
382
            after cancel $watchdog::after_id
383
        }
384
        if { $synth::flag_gui } {
385
            watchdog::gui_cancel
386
        }
387
        close $watchdog::proc_stat
388
    }
389
    synth::hook_add "ecos_exit" watchdog::exit_hook
390
 
391
    proc handle_request { id reqcode arg1 arg2 reqdata reqlen reply_len } {
392
        if { 0x01 == $reqcode } {
393
            # A "start" request. If the watchdog has already started,
394
            # this request is a no-op. Otherwise a timer is enabled.
395
            # This is made to run almost immediately, so that the
396
            # GUI gets a quick update. Setting the reset_received flag
397
            # ensures that the watchdog will not trigger immediately
398
            set watchdog::reset_received 1
399
            if { "" == $watchdog::after_id } {
400
                set watchdog::after_id [after 1 watchdog::update]
401
            }
402
            if { $synth::flag_gui } {
403
                .watchdog lower $watchdog::asleep $watchdog::background
404
            }
405
        } elseif { 0x02 == $reqcode } {
406
            # A "reset" request. Just set a flag, the update handler
407
            # will detect this next time it runs.
408
            set watchdog::reset_received 1
409
        }
410
    }
411
 
412
    proc instantiate { id name data } {
413
        return watchdog::handle_request
414
    }
415
}
416
 
417
if { $watchdog::init_ok } {
418
    return watchdog::instantiate
419
} else {
420
    synth::report "Watchdog cannot be instantiated, initialization failed.\n"
421
    return ""
422
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.