eCos Product
RedBoot Product
Supported Hardware |
![]() Virtual Vectors (eCos/ROM Monitor Calling Interface)Some eCos platforms have supported full debugging capabilities via CygMon since day one. Platforms of the architectures PowerPC, ARM, and SH do not provide those features unless a GDB stub is included in the application. This is going to change. All platforms will (eventually) support all the debugging features by relying on a ROM/RAM calling interface (also referred to as virtual vector table) provided by the ROM monitor. This calling interface is based on the tables used by libbsp and is thus backwards compatible with the existing CygMon supported platforms. Virtual VectorsWhat are virtual vectors, what do they do, and why are they needed? "Virtual vectors" is the name of a table located at a static location in the target address space. This table contains 64 vectors that point to service functions or data. The fact that the vectors are always placed at the same location in the address space means that both ROM and RAM startup configurations can access these and thus the services pointed to. The primary goal is to allow services to be provided by ROM configurations (ROM monitors in particular) with clients in RAM configurations being able to use these services. Without the table of pointers this would be impossible since the ROM and RAM applications would be linked separately - in effect having separate name spaces - preventing direct references from one to the other. This decoupling of service from client is needed by RedBoot, allowing among other things debugging of applications which do not contain debugging client code (stubs). Initialization (or Mechanism vs. Policy)Virtual vectors are a mechanism for decoupling services from clients in the address space. The mechanism allows services to be implemented by ROM monitor, RAM application, to be switched out at run-time, to be disabled by installing pointers to dummy functions, etc. The appropriate use of the mechanism is specified loosely by a policy. The general policy dictates that the vectors are initialized in whole by ROM monitors (built for ROM or RAM), or by stand-alone applications. For configurations relying on a ROM monitor environment, the policy is to allow initialization on a service by service basis. The default is to initialize all services, except COMMS services since these are presumed to already be carrying a communication session to the debugger / console which was used for launching the application. This means that the bulk of the code gets tested in normal builds, and not just once in a blue moon when building new stubs or a ROM configuration. The configuration options are written to comply with this policy by default, but can be overridden by the user if desired. Defaults are:
Pros and Cons of Virtual VectorsThere are pros and cons associated with the use of virtual vectors. We do believe that the pros generally outweigh the cons by a great margin, but there may be situations where the opposite is true. The use of the services are implemented by way of macros, meaning that it is possible to circumvent the virtual vectors if desired. There is (as yet) no implementation for doing this, but it is possible. Here is a list of pros and cons:
Available servicesThe hal_if.h file in the common HAL defines the complete list of available services. A few worth mentioning in particular:
The COMMS channelsAs all HAL IO happens via the COMMS channels these deserve to be described in a little more detail. In particular the controls of where diagnostic output is routed and how it is treated to allow for display in debuggers. Console and Debugging ChannelsThere are two COMMS channels - one for console IO and one for debugging IO. They can be individually configured to use any of the actual IO ports (serial or Ethernet) available on the platform. The console channel is used for any IO initiated by calling the diag_*() functions. Note that these should only be used during development for debugging, assertion and possibly tracing messages. All proper IO should happen via proper devices. This means it should be possible to remove the HAL device drivers from production configurations where assertions are disabled. The debugging channel is used for communication between the debugger and the stub which remotely controls the target for the debugger (the stub runs on the target). This usually happens via some protocol, encoding commands and replies in some suitable form. Having two separate channels allows, e.g., for simple logging without conflicts with the debugger or interactive IO which some debuggers do not allow. ManglingAs debuggers usually have a protocol using specialized commands when communicating with the stub on the target, sending out text as raw ASCII from the target on the same channel will either result in protocol errors (with loss of control over the target) or the text may just be ignored as junk by the debugger. To get around this, some debuggers have a special command for text output. Mangling is the process of encoding diagnostic ASCII text output in the form specified by the debugger protocol. When it is necessary to use mangling, i.e. when writing console output to the same port used for debugging, a mangler function is installed on the console channel which mangles the text and passes it on to the debugger channel. Controlling the Console ChannelConsole output configuration is either inherited from the ROM monitor launching the application, or it is specified by the application. This is controlled by the new option CYGSEM_HAL_VIRTUAL_VECTOR_INHERIT_CONSOLE which defaults to enabled when the configuration is set to use a ROM monitor. If the user wants to specify the console configuration in the application image, there are two new options that are used for this. Defaults are to direct diagnostic output via a mangler to the debugging channel (CYGDBG_HAL_DIAG_TO_DEBUG_CHAN enabled). The mangler type is controlled by the option CYGSEM_HAL_DIAG_MANGLER. At present there are only two mangler types:
Finally, by disabling CYGDBG_HAL_DIAG_TO_DEBUG_CHAN, the diagnostic output is directed in raw form to the specified console IO port. In summary this results in the following common configuration scenarios for RAM startup configurations:
And for ROM startup configurations:
Footnote: Design Reasoning for Control of Console ChannelThe current code for controlling of the console channel is a replacement for an older implementation which had some shortcomings addressed by the new implementation. This is what the old implementation did: on initialization it would check if the CDL configured console channel differed from the active debug channel - and if so, set the console channel, thereby disabling mangling. The idea was that whatever channel was configured to be used for console (i.e., diagnostic output) in the application was what should be used. Also, it meant that if debug and console channels were normally the same, a changed console channel would imply a request for unmangled output. But this prevented at least two things:
The calling interface APIThe calling interface API is defined by hal_if.h and hal_if.c in hal/common. The API provides a set of services. Different platforms, or different versions of the ROM monitor for a single platform, may implement fewer or extra service. The table has room for growth, and any entries which are not supported map to a NOP-service (when called it returns 0 (false)). A client of a service should either be selected by configuration, or have suitable fall back alternatives in case the feature is not implemented by the ROM monitor. Note: Checking for unimplemented service when this may be a data field/pointer instead of a function: suggest reserving the last entry in the table as the NOP-service pointer. Then clients can compare a service entry with this pointer to determine whether it's initialized or not. hal_if.h: defines the table layout and accessor macros (allowing primitive type checking and alternative implementations should it become necessary). hal_if.c: defines the table initialization function (which all HALs should call during platform initialization - the table will get initialized according to configuration). Here is also defined wrapper functions which map between the calling interface API and the API of the used eCos functions. Implemented ServicesThis is a brief description of the services, some of which are described in further detail below.
CompatibilityWhen a platform is changed to support the calling interface, applications will use it if so configured. That means that if an application is run on a platform with an older ROM monitor, the service is almost guaranteed to fail. For this reason, applications should only use Console Comm for HAL diagnostics output if explicitly configured to do so (CYGSEM_HAL_VIRTUAL_VECTOR_DIAG). As for asynchronous GDB interrupts, the service will always be used. This is likely to cause a crash under older ROM monitors, but this crash may be caught by the debugger. The old workaround still applies: if you need asynchronous breakpoints or thread debugging under older ROM monitors, you may have to include the debugging support when configuring eCos. Implementation detailsDuring the startup of a ROM monitor, the calling table will be initialized. This also happens if eCos is configured not to rely on a ROM monitor. Note: There is reserved space (256 bytes) for the vector table whether it gets used or not. This may be something that we want to change if we ever have to shave off every last byte for a given target. If thread debugging features are enabled, the function for accessing the thread information gets registered in the table during startup of a RAM startup configuration. Further implementation details are described where the service itself is described. FIXME: Need to describe the CYGARC_HAL_SAVE_GP() and CYGARC_HAL_RESTORE_GP() macros. New platform portsThe hal_platform_init() function must call hal_if_init(). The HAL serial driver must, when called via cyg_hal_plf_comms_init initialize the communication channels. The reset() function defined in hal_if.c will attempt to do a hardware reset, but if this fails it will fall back to simply jumping to the reset entry-point. On most platforms the startup initialization will go a long way to reset the target to a sane state (there will be exceptions, of course). For this reason, make sure to define HAL_STUB_PLATFORM_RESET_ENTRY in plf_stub.h. All debugging features must be in place in order for the debugging services to be functional. See general platform porting notes. New architecture portsThere are no specific requirements for a new architecture port in order to support the calling interface, but the basic debugging features must be in place. See general architecture porting notes. IO channelsThe calling interface provides procedure tables for all IO channels on the platform. These are used for console (diagnostic) and debugger IO, allowing a ROM monitor to provided all the needed IO routines. At the same time, this makes it easy to switch console/debugger channels at run-time (the old implementation had hardwired drivers for console and debugger IO, preventing these to change at run-time). The hal_if provides wrappers which interface these services to the eCos infrastructure diagnostics routines. This is done in a way which ensures proper string mangling of the diagnostics output when required (e.g. O-packetization when using a GDB compatible ROM monitor). Available ProceduresThis is a brief description of the procedures
UsageThe standard eCos diagnostics IO functions use the channel procedure table when CYGSEM_HAL_VIRTUAL_VECTOR_DIAG is enabled. That means that when you use diag_printf (or the libc printf function) the stream goes through the selected console procedure table. If you use the virtual vector function SET_CONSOLE_COMM you can change the device which the diagnostics output goes to at run-time. You can also use the table functions directly if desired (regardless of the CYGSEM_HAL_VIRTUAL_VECTOR_DIAG setting - assuming the ROM monitor provides the services). Here is a small example which changes the console to use channel 2, fetches the comm procs pointer and calls the write function from that table, then restores the console to the original channel: #define T "Hello World!\n" int main(void) { hal_virtual_comm_table_t* comm; int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); CYGACC_CALL_IF_SET_CONSOLE_COMM(2); comm = CYGACC_CALL_IF_CONSOLE_PROCS(); CYGACC_COMM_IF_WRITE(*comm, T, strlen(T)); CYGACC_CALL_IF_SET_CONSOLE_COMM(cur); } Beware that if doing something like the above, you should only do it to a channel which does not have GDB at the other end: GDB ignores raw data, so you would not see the output. CompatibilityThe use of this service is controlled by the option CYGSEM_HAL_VIRTUAL_VECTOR_DIAG which is disabled per default on most older platforms (thus preserving backwards compatibility with older stubs). On newer ports, this option should always be set. Implementation detailsThere is an array of procedure tables (raw comm channels) for each IO device of the platform which get initialized by the ROM monitor, or optionally by a RAM startup configuration (allowing the RAM configuration to take full control of the target). In addition to this, there's a special table which is used to hold mangler procedures. The vector table defines which of these channels are selected for console and debugging IO respectively: console entry can be empty, point to mangler channel, or point to a raw channel. The debugger entry should always point to a raw channel. During normal console output (i.e., diagnostic output) the console table will be used to handle IO if defined. If not defined, the debug table will be used. This means that debuggers (such as GDB) which require text streams to be mangled (O-packetized in the case of GDB), can rely on the ROM monitor install mangling IO routines in the special mangler table and select this for console output. The mangler will pass the mangled data on to the selected debugging channel. If the eCos configuration specifies a different console channel from that used by the debugger, the console entry will point to the selected raw channel, thus overriding any mangler provided by the ROM monitor. See hal_if_diag_* routines in hal_if.c for more details of the stream path of diagnostic output. See cyg_hal_gdb_diag_* routines in hal_stub.c for the mangler used for GDB communication. FIXME: Other special channels are reserved for ethernet communication. New platform portsDefine CDL options CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS, CYGNUM_HAL_VIRTUAL_VECTOR_DEBUG_CHANNEL, and CYGNUM_HAL_VIRTUAL_VECTOR_CONSOLE_CHANNEL. If CYGSEM_HAL_VIRTUAL_VECTOR_DIAG is set, make sure the infra diag code uses the hal_if diag functions: #define HAL_DIAG_INIT() hal_if_diag_init() #define HAL_DIAG_WRITE_CHAR(_c_) hal_if_diag_write_char(_c_) #define HAL_DIAG_READ_CHAR(_c_) hal_if_diag_read_char(&_c_) In addition to the above functions, the platform HAL must also provide a function cyg_hal_plf_comms_init which initializes the drivers and the channel procedure tables. Most of the other functionality in the table is more or less possible to copy unchanged from existing ports. Some care is necessary though to ensure the proper handling of interrupt vectors and timeouts for various devices handled by the same driver. See PowerPC/Cogent platform HAL for an example implementation. Note: When vector table console code is not used, the platform HAL must map the HAL_DIAG_INIT, HAL_DIAG_WRITE_CHAR and HAL_DIAG_READ_CHAR macros directly to the low-level IO functions, hardwired to use a compile-time configured channel. Note: On old ports the hardwired HAL_DIAG_INIT, HAL_DIAG_WRITE_CHAR and HAL_DIAG_READ_CHAR implementations will also contain code to O-packetize the output for GDB. This should not be adopted for new ports! On new ports the ROM monitor is guaranteed to provide the necessary mangling via the vector table. The hardwired configuration should be reserved for ROM startups where achieving minimal image size is crucial.
|