1/*****************************************************************************
2
3  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
4  more contributor license agreements.  See the NOTICE file distributed
5  with this work for additional information regarding copyright ownership.
6  Accellera licenses this file to you under the Apache License, Version 2.0
7  (the "License"); you may not use this file except in compliance with the
8  License.  You may obtain a copy of the License at
9
10    http://www.apache.org/licenses/LICENSE-2.0
11
12  Unless required by applicable law or agreed to in writing, software
13  distributed under the License is distributed on an "AS IS" BASIS,
14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15  implied.  See the License for the specific language governing
16  permissions and limitations under the License.
17
18 *****************************************************************************/
19
20/*****************************************************************************
21
22  sc_port.cpp -- Base classes of all port classes.
23
24  Original Author: Martin Janssen, Synopsys, Inc., 2001-05-21
25
26 CHANGE LOG AT THE END OF THE FILE
27 *****************************************************************************/
28
29#include "sysc/kernel/sc_simcontext.h"
30#include "sysc/kernel/sc_module.h"
31#include "sysc/kernel/sc_object_int.h"
32#include "sysc/kernel/sc_method_process.h"
33#include "sysc/kernel/sc_thread_process.h"
34#include "sysc/communication/sc_communication_ids.h"
35#include "sysc/utils/sc_utils_ids.h"
36#include "sysc/communication/sc_event_finder.h"
37#include "sysc/communication/sc_port.h"
38#include "sysc/communication/sc_signal_ifs.h"
39
40namespace sc_core {
41
42// ----------------------------------------------------------------------------
43//  STRUCT : sc_bind_elem
44// ----------------------------------------------------------------------------
45
46struct sc_bind_elem
47{
48    // constructors
49    sc_bind_elem();
50    explicit sc_bind_elem( sc_interface* interface_ );
51    explicit sc_bind_elem( sc_port_base* parent_ );
52
53    sc_interface* iface;
54    sc_port_base* parent;
55};
56
57
58// IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
59
60// constructors
61
62sc_bind_elem::sc_bind_elem()
63: iface( 0 ),
64  parent( 0 )
65{}
66
67sc_bind_elem::sc_bind_elem( sc_interface* interface_ )
68: iface( interface_ ),
69  parent( 0 )
70{}
71
72sc_bind_elem::sc_bind_elem( sc_port_base* parent_ )
73: iface( 0 ),
74  parent( parent_ )
75{}
76
77
78// ----------------------------------------------------------------------------
79//  STRUCT : sc_bind_ef
80// ----------------------------------------------------------------------------
81
82struct sc_bind_ef
83{
84    // constructor
85    sc_bind_ef( sc_process_b* , sc_event_finder* );
86
87    // destructor
88    ~sc_bind_ef();
89
90    sc_process_b* handle;
91    sc_event_finder* event_finder;
92};
93
94
95// IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
96
97// constructor
98
99sc_bind_ef::sc_bind_ef( sc_process_b* handle_,
100			sc_event_finder* event_finder_ )
101: handle( handle_ ),
102  event_finder( event_finder_ )
103{}
104
105
106// destructor
107
108sc_bind_ef::~sc_bind_ef()
109{
110}
111
112
113// ----------------------------------------------------------------------------
114//  STRUCT : sc_bind_info
115// ----------------------------------------------------------------------------
116
117struct sc_bind_info
118{
119    // constructor
120    explicit sc_bind_info( int max_size_,
121	sc_port_policy policy_=SC_ONE_OR_MORE_BOUND );
122
123    // destructor
124    ~sc_bind_info();
125
126    int            max_size() const;
127    sc_port_policy policy() const;
128    int            size() const;
129
130    int                        m_max_size;
131    sc_port_policy             m_policy;
132    std::vector<sc_bind_elem*> vec;
133    bool                       has_parent;
134    int                        last_add;
135    bool                       is_leaf;
136    bool                       complete;
137
138    std::vector<sc_bind_ef*>   thread_vec;
139    std::vector<sc_bind_ef*>   method_vec;
140};
141
142
143// IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
144
145// constructor
146
147sc_bind_info::sc_bind_info( int max_size_, sc_port_policy policy_ )
148: m_max_size( max_size_ ),
149  m_policy( policy_ ),
150  vec(),
151  has_parent( false ),
152  last_add( -1 ),
153  is_leaf( true ),
154  complete( false ),
155  thread_vec(),
156  method_vec()
157{}
158
159
160// destructor
161
162sc_bind_info::~sc_bind_info()
163{
164    for( int i = size() - 1; i >= 0; -- i ) {
165	delete vec[i];
166    }
167}
168
169
170int
171sc_bind_info::max_size() const
172{
173    return m_max_size ? m_max_size : (int) vec.size();
174}
175
176sc_port_policy
177sc_bind_info::policy() const
178{
179    return m_policy;
180}
181
182int
183sc_bind_info::size() const
184{
185    return vec.size();
186}
187
188
189
190// ----------------------------------------------------------------------------
191//  CLASS : sc_port_base
192//
193//  Abstract base class for class sc_port_b.
194// ----------------------------------------------------------------------------
195
196// This method exists to get around a problem in VCC 6.0 where you cannot
197// have  a friend class that is templated. So sc_port_b<IF> calls this class
198// instead of sc_process_b::add_static_event.
199
200void sc_port_base::add_static_event(
201    sc_method_handle process_p, const sc_event& event ) const
202{
203    process_p->add_static_event( event );
204}
205
206void sc_port_base::add_static_event(
207    sc_thread_handle process_p, const sc_event& event ) const
208{
209    process_p->add_static_event( event );
210}
211
212// return number of interfaces that will be bound, or are bound:
213
214int sc_port_base::bind_count()
215{
216    if ( m_bind_info )
217	return m_bind_info->size();
218    else
219	return interface_count();
220}
221
222// error reporting
223
224void
225sc_port_base::report_error( const char* id, const char* add_msg ) const
226{
227    char msg[BUFSIZ];
228    if( add_msg != 0 ) {
229	std::sprintf( msg, "%s: port '%s' (%s)", add_msg, name(), kind() );
230    } else {
231	std::sprintf( msg, "port '%s' (%s)", name(), kind() );
232    }
233    SC_REPORT_ERROR( id, msg );
234}
235
236
237// constructors
238
239sc_port_base::sc_port_base(
240    int max_size_, sc_port_policy policy
241) :
242    sc_object( sc_gen_unique_name( "port" ) ),
243    m_bind_info(NULL)
244{
245    simcontext()->get_port_registry()->insert( this );
246    m_bind_info = new sc_bind_info( max_size_, policy );
247}
248
249sc_port_base::sc_port_base(
250    const char* name_, int max_size_, sc_port_policy policy
251) :
252    sc_object( name_ ),
253    m_bind_info(NULL)
254{
255    simcontext()->get_port_registry()->insert( this );
256    m_bind_info = new sc_bind_info( max_size_, policy );
257}
258
259
260// destructor
261
262sc_port_base::~sc_port_base()
263{
264    simcontext()->get_port_registry()->remove( this );
265    delete m_bind_info;
266}
267
268
269// bind interface to this port
270
271void
272sc_port_base::bind( sc_interface& interface_ )
273{
274    if( m_bind_info == 0 ) {
275	// cannot bind an interface after elaboration
276	report_error( SC_ID_BIND_IF_TO_PORT_, "simulation running" );
277    }
278
279    m_bind_info->vec.push_back( new sc_bind_elem( &interface_ ) );
280
281    if( ! m_bind_info->has_parent ) {
282	// add (cache) the interface
283	add_interface( &interface_ );
284	m_bind_info->last_add ++;
285    }
286}
287
288
289// bind parent port to this port
290
291void
292sc_port_base::bind( this_type& parent_ )
293{
294    if( m_bind_info == 0 ) {
295	// cannot bind a parent port after elaboration
296	report_error( SC_ID_BIND_PORT_TO_PORT_, "simulation running" );
297    }
298
299    if( &parent_ == this ) {
300	report_error( SC_ID_BIND_PORT_TO_PORT_, "same port" );
301    }
302
303    // check if parent port is already bound to this port
304#if 0
305    for( int i = m_bind_info->size() - 1; i >= 0; -- i ) {
306	if( &parent_ == m_bind_info->vec[i]->parent ) {
307	    report_error( SC_ID_BIND_PORT_TO_PORT_, "already bound" );
308	}
309    }
310#endif //
311
312    m_bind_info->vec.push_back( new sc_bind_elem( &parent_ ) );
313    m_bind_info->has_parent = true;
314    parent_.m_bind_info->is_leaf = false;
315}
316
317// called by construction_done (null by default)
318
319void sc_port_base::before_end_of_elaboration()
320{}
321
322// called by elaboration_done (does nothing)
323
324void
325sc_port_base::end_of_elaboration()
326{}
327
328// called by sc_port_registry::start_simulation (does nothing by default)
329
330void sc_port_base::start_of_simulation()
331{}
332
333// called by sc_port_registry::simulation_done (does nothing by default)
334
335void sc_port_base::end_of_simulation()
336{}
337
338
339// called by class sc_module for positional binding
340
341int
342sc_port_base::pbind( sc_interface& interface_ )
343{
344    if( m_bind_info == 0 ) {
345	// cannot bind an interface after elaboration
346	report_error( SC_ID_BIND_IF_TO_PORT_, "simulation running" );
347    }
348
349    if( m_bind_info->size() != 0 ) {
350	// first interface already bound
351	return 1;
352    }
353
354    return vbind( interface_ );
355}
356
357int
358sc_port_base::pbind( sc_port_base& parent_ )
359{
360    if( m_bind_info == 0 ) {
361	// cannot bind a parent port after elaboration
362	report_error( SC_ID_BIND_PORT_TO_PORT_, "simulation running" );
363    }
364
365    if( m_bind_info->size() != 0 ) {
366	// first interface already bound
367	return 1;
368    }
369
370    return vbind( parent_ );
371}
372
373
374// called by the sc_sensitive* classes
375
376void
377sc_port_base::make_sensitive( sc_thread_handle handle_,
378			      sc_event_finder* event_finder_ ) const
379{
380    assert( m_bind_info != 0 );
381    m_bind_info->thread_vec.push_back(
382	new sc_bind_ef( (sc_process_b*)handle_, event_finder_ ) );
383}
384
385void
386sc_port_base::make_sensitive( sc_method_handle handle_,
387			      sc_event_finder* event_finder_ ) const
388{
389    assert( m_bind_info != 0 );
390    m_bind_info->method_vec.push_back(
391	new sc_bind_ef( (sc_process_b*)handle_, event_finder_ ) );
392}
393
394
395// support methods
396
397int
398sc_port_base::first_parent()
399{
400    for( int i = 0; i < m_bind_info->size(); ++ i ) {
401	if( m_bind_info->vec[i]->parent != 0 ) {
402	    return i;
403	}
404    }
405    return -1;
406}
407
408void
409sc_port_base::insert_parent( int i )
410{
411    std::vector<sc_bind_elem*>& vec = m_bind_info->vec;
412
413    this_type* parent = vec[i]->parent;
414
415
416    // IF OUR PARENT HAS NO BINDING THEN IGNORE IT:
417    //
418    // Note that the zeroing of the parent pointer must occur before this
419    // test
420
421    vec[i]->parent = 0;
422    if ( parent->m_bind_info->vec.size() == 0 ) return;
423
424    vec[i]->iface = parent->m_bind_info->vec[0]->iface;
425    int n = parent->m_bind_info->size() - 1;
426    if( n > 0 ) {
427	// resize the bind vector (by adding new elements)
428	for( int k = 0; k < n; ++ k ) {
429	    vec.push_back( new sc_bind_elem() );
430	}
431	// move elements in the bind vector
432	for( int k = m_bind_info->size() - n - 1; k > i; -- k ) {
433	    vec[k + n]->iface = vec[k]->iface;
434	    vec[k + n]->parent = vec[k]->parent;
435	}
436	// insert parent interfaces into the bind vector
437	for( int k = i + 1; k <= i + n; ++ k ) {
438	    vec[k]->iface = parent->m_bind_info->vec[k - i]->iface;
439	    vec[k]->parent = 0;
440	}
441    }
442}
443
444
445// called when elaboration is done
446
447void
448sc_port_base::complete_binding()
449{
450    char msg_buffer[128]; // For error message construction.
451
452    // IF BINDING HAS ALREADY BEEN COMPLETED IGNORE THIS CALL:
453
454    assert( m_bind_info != 0 );
455    if( m_bind_info->complete ) {
456        return;
457    }
458
459    // COMPLETE BINDING OF OUR PARENT PORTS SO THAT WE CAN USE THAT INFORMATION:
460
461    int i = first_parent();
462    while( i >= 0 ) {
463        m_bind_info->vec[i]->parent->complete_binding();
464        insert_parent( i );
465        i = first_parent();
466    }
467
468    // LOOP OVER BINDING INFORMATION TO COMPLETE THE BINDING PROCESS:
469
470    int size;
471    for( int j = 0; j < m_bind_info->size(); ++ j ) {
472        sc_interface* iface = m_bind_info->vec[j]->iface;
473
474	// if the interface is zero this was for an unbound port.
475	if ( iface == 0 ) continue;
476
477	// add (cache) the interface
478        if( j > m_bind_info->last_add ) {
479            add_interface( iface );
480        }
481
482	// only register "leaf" ports (ports without children)
483        if( m_bind_info->is_leaf ) {
484            iface->register_port( *this, if_typename() );
485        }
486
487        // complete static sensitivity for methods
488        size = m_bind_info->method_vec.size();
489        for( int k = 0; k < size; ++ k ) {
490            sc_bind_ef* p = m_bind_info->method_vec[k];
491            const sc_event& event = ( p->event_finder != 0 )
492                                  ? p->event_finder->find_event(iface)
493                                  : iface->default_event();
494            p->handle->add_static_event( event );
495        }
496
497        // complete static sensitivity for threads
498        size = m_bind_info->thread_vec.size();
499        for( int k = 0; k < size; ++ k ) {
500            sc_bind_ef* p = m_bind_info->thread_vec[k];
501            const sc_event& event = ( p->event_finder != 0 )
502                                  ? p->event_finder->find_event(iface)
503                                  : iface->default_event();
504            p->handle->add_static_event( event );
505        }
506
507    }
508
509    // MAKE SURE THE PROPER NUMBER OF BINDINGS OCCURRED:
510    //
511    // Make sure there are enough bindings, and not too many.
512
513    int actual_binds = interface_count();
514
515    if ( actual_binds > m_bind_info->max_size() )
516    {
517	sprintf(msg_buffer, "%d binds exceeds maximum of %d allowed",
518	    actual_binds, m_bind_info->max_size() );
519	report_error( SC_ID_COMPLETE_BINDING_, msg_buffer );
520    }
521    switch ( m_bind_info->policy() )
522    {
523      case SC_ONE_OR_MORE_BOUND:
524        if ( actual_binds < 1 ) {
525            report_error( SC_ID_COMPLETE_BINDING_, "port not bound" );
526        }
527        break;
528      case SC_ALL_BOUND:
529        if ( actual_binds < m_bind_info->max_size() || actual_binds < 1 ) {
530	    sprintf(msg_buffer, "%d actual binds is less than required %d",
531	        actual_binds, m_bind_info->max_size() );
532            report_error( SC_ID_COMPLETE_BINDING_, msg_buffer );
533        }
534        break;
535      default:  // SC_ZERO_OR_MORE_BOUND:
536        break;
537    }
538
539
540    // CLEAN UP: FREE BINDING STORAGE:
541
542    size = m_bind_info->method_vec.size();
543    for( int k = 0; k < size; ++ k ) {
544        delete m_bind_info->method_vec[k];
545    }
546    m_bind_info->method_vec.resize(0);
547
548    size = m_bind_info->thread_vec.size();
549    for( int k = 0; k < size; ++ k ) {
550        delete m_bind_info->thread_vec[k];
551    }
552    m_bind_info->thread_vec.resize(0);
553
554    m_bind_info->complete = true;
555}
556
557void
558sc_port_base::construction_done()
559{
560    sc_module* parent = static_cast<sc_module*>( get_parent_object() );
561    sc_object::hierarchy_scope scope( parent );
562    before_end_of_elaboration();
563}
564
565void
566sc_port_base::elaboration_done()
567{
568    assert( m_bind_info != 0 && m_bind_info->complete );
569    delete m_bind_info;
570    m_bind_info = 0;
571
572    sc_module* parent = static_cast<sc_module*>( get_parent_object() );
573    sc_object::hierarchy_scope scope( parent );
574    end_of_elaboration();
575}
576
577void
578sc_port_base::start_simulation()
579{
580    sc_module* parent = static_cast<sc_module*>( get_parent_object() );
581    sc_object::hierarchy_scope scope( parent );
582    start_of_simulation();
583}
584
585void
586sc_port_base::simulation_done()
587{
588    sc_module* parent = static_cast<sc_module*>( get_parent_object() );
589    sc_object::hierarchy_scope scope( parent );
590    end_of_simulation();
591}
592
593
594// ----------------------------------------------------------------------------
595//  CLASS : sc_port_registry
596//
597//  Registry for all ports.
598//  FOR INTERNAL USE ONLY!
599// ----------------------------------------------------------------------------
600
601void
602sc_port_registry::insert( sc_port_base* port_ )
603{
604    if( sc_is_running() ) {
605	port_->report_error( SC_ID_INSERT_PORT_, "simulation running" );
606    }
607
608    if( m_simc->elaboration_done()  ) {
609       port_->report_error( SC_ID_INSERT_PORT_, "elaboration done" );
610    }
611
612#if defined(DEBUG_SYSTEMC)
613    // check if port_ is already inserted
614    for( int i = size() - 1; i >= 0; -- i ) {
615	if( port_ == m_port_vec[i] ) {
616	    port_->report_error( SC_ID_INSERT_PORT_, "port already inserted" );
617	}
618    }
619#endif
620
621    // append the port to the current module's vector of ports
622    sc_module* curr_module = m_simc->hierarchy_curr();
623    if( curr_module == 0 ) {
624	port_->report_error( SC_ID_PORT_OUTSIDE_MODULE_ );
625    }
626    curr_module->append_port( port_ );
627
628    // insert
629    m_port_vec.push_back( port_ );
630}
631
632void
633sc_port_registry::remove( sc_port_base* port_ )
634{
635    int i;
636    for( i = size() - 1; i >= 0; -- i ) {
637	if( port_ == m_port_vec[i] ) {
638	    break;
639	}
640    }
641    if( i == -1 ) {
642	port_->report_error( SC_ID_REMOVE_PORT_, "port not registered" );
643    }
644
645    // remove
646    m_port_vec[i] = m_port_vec[size() - 1];
647    m_port_vec.resize(size()-1);
648}
649
650
651// constructor
652
653sc_port_registry::sc_port_registry( sc_simcontext& simc_ )
654: m_construction_done(0),
655  m_port_vec(),
656  m_simc( &simc_ )
657{
658}
659
660
661// destructor
662
663sc_port_registry::~sc_port_registry()
664{
665}
666
667// called when construction is done
668
669bool
670sc_port_registry::construction_done()
671{
672    if( size() == m_construction_done )
673        // nothing has been updated
674        return true;
675
676    for( int i = size()-1; i >= m_construction_done; --i ) {
677        m_port_vec[i]->construction_done();
678    }
679
680    m_construction_done = size();
681    return false;
682}
683
684// called when when elaboration is done
685
686void
687sc_port_registry::complete_binding()
688{
689    for( int i = size() - 1; i >= 0; -- i ) {
690        m_port_vec[i]->complete_binding();
691    }
692}
693
694
695// called when elaboration is done
696
697void
698sc_port_registry::elaboration_done()
699{
700    complete_binding();
701
702    for( int i = size() - 1; i >= 0; -- i ) {
703        m_port_vec[i]->elaboration_done();
704    }
705}
706
707// called before simulation begins
708
709void
710sc_port_registry::start_simulation()
711{
712    for( int i = size() - 1; i >= 0; -- i ) {
713        m_port_vec[i]->start_simulation();
714    }
715}
716
717// called after simulation ends
718
719void
720sc_port_registry::simulation_done()
721{
722    for( int i = size() - 1; i >= 0; -- i ) {
723        m_port_vec[i]->simulation_done();
724    }
725}
726
727// This is a static member function.
728
729void
730sc_port_registry::replace_port( sc_port_registry* /* registry */ )
731{
732}
733
734void sc_warn_port_constructor()
735{
736    static bool warn_port_constructor=true;
737    if ( warn_port_constructor )
738    {
739        warn_port_constructor = false;
740        SC_REPORT_INFO(SC_ID_IEEE_1666_DEPRECATION_,
741	    "interface and/or port binding in port constructors is deprecated"
742	);
743    }
744}
745
746} // namespace sc_core
747
748
749/*****************************************************************************
750
751  MODIFICATION LOG - modifiers, enter your name, affiliation, date and
752  changes you are making here.
753
754      Name, Affiliation, Date: Andy Goodrich, Forte Design Systems
755                               Bishnupriya Bhattacharya, Cadence Design Systems,
756                               25 August, 2003
757  Description of Modification: phase callbacks
758
759      Name, Affiliation, Date: Andy Goodrich, Forte Design Systems
760	  						   12 December, 2005
761  Description of Modification: multiport binding policy changes
762
763 *****************************************************************************/
764
765
766// $Log: sc_port.cpp,v $
767// Revision 1.8  2011/08/24 22:05:36  acg
768//  Torsten Maehne: initialization changes to remove warnings.
769//
770// Revision 1.7  2011/08/15 16:43:24  acg
771//  Torsten Maehne: changes to remove unused argument warnings.
772//
773// Revision 1.6  2011/08/07 19:08:01  acg
774//  Andy Goodrich: moved logs to end of file so line number synching works
775//  better between versions.
776//
777// Revision 1.5  2011/08/07 18:53:09  acg
778//  Philipp A. Hartmann: add virtual instances of the bind function for
779//  base classes to eliminate warning messages for clang platforms.
780//
781// Revision 1.4  2011/05/09 04:07:37  acg
782//  Philipp A. Hartmann:
783//    (1) Restore hierarchy in all phase callbacks.
784//    (2) Ensure calls to before_end_of_elaboration.
785//
786// Revision 1.3  2011/02/18 20:23:45  acg
787//  Andy Goodrich: Copyright update.
788//
789// Revision 1.2  2011/02/14 17:50:16  acg
790//  Andy Goodrich: testing for sc_port and sc_export instantiations during
791//  end of elaboration and issuing appropriate error messages.
792//
793// Revision 1.1.1.1  2006/12/15 20:20:04  acg
794// SystemC 2.3
795//
796// Revision 1.11  2006/08/29 23:34:59  acg
797//  Andy Goodrich: added bind_count() method to allow users to determine which
798//  ports are connected in before_end_of_elaboration().
799//
800// Revision 1.10  2006/05/08 17:52:47  acg
801//  Andy Goodrich:
802//    (1) added David Long's forward declarations for friend functions,
803//        methods, and operators to keep the Microsoft compiler happy.
804//    (2) Added delta_count() method to sc_prim_channel for use by
805//        sc_signal so that the friend declaration in sc_simcontext.h
806// 	   can be for a non-templated class (i.e., sc_prim_channel.)
807//
808// Revision 1.9  2006/02/02 20:43:09  acg
809//  Andy Goodrich: Added an existence linked list to sc_event_finder so that
810//  the dynamically allocated instances can be freed after port binding
811//  completes. This replaces the individual deletions in ~sc_bind_ef, as these
812//  caused an exception if an sc_event_finder instance was used more than
813//  once, due to a double freeing of the instance.
814//
815// Revision 1.7  2006/01/26 21:00:50  acg
816//  Andy Goodrich: conversion to use sc_event::notify(SC_ZERO_TIME) instead of
817//  sc_event::notify_delayed()
818//
819// Revision 1.6  2006/01/25 00:31:11  acg
820//  Andy Goodrich: Changed over to use a standard message id of
821//  SC_ID_IEEE_1666_DEPRECATION for all deprecation messages.
822//
823// Revision 1.5  2006/01/24 20:46:31  acg
824// Andy Goodrich: changes to eliminate use of deprecated features. For instance,
825// using notify(SC_ZERO_TIME) in place of notify_delayed().
826//
827// Revision 1.4  2006/01/13 20:41:59  acg
828// Andy Goodrich: Changes to add port registration to the things that are
829// checked when SC_NO_WRITE_CHECK is not defined.
830//
831// Revision 1.3  2006/01/13 18:47:42  acg
832// Added $Log command so that CVS comments are reproduced in the source.
833//
834
835// Taf!
836