Implementing the Serial Debug Functions

The serial debug functions are responsible for initializing and communicating with a debug message output device. Typically, this is a serial UART device connected over a NULL modem cable to a terminal emulator on the host computer. The OS or OAL implements the same code, so a single implementation can be shared between the OAL and the boot loader.

The following table shows the serial debug functions that you need to implement.

Function Description
OEMDebugInit Initializes any debug UART you want the boot loader to be able to use. OEMDebugInit is called by BLCOMMON, which in turn calls OEMInitDebugSerial. The core functionality is placed in OEMInitDebugSerial because the kernel calls this function, and thus, it can be shared between the boot loader and the OS.
OEMWriteDebugString Writes a text string to the debug UART.

This function takes Unicode strings, not regular C strings. When calling this function, use the TEXT macro to convert C strings to Unicode strings. The following code example shows how to use OEMWriteDebugString with the TEXT macro.

OEMWriteDebugString( TEXT("This is a debug message.") );
OEMWriteDebugByte A primitive used by the OEMWriteDebugString function.
OEMReadDebugByte Reads a byte from the debug UART.

To implement serial debug functions

  • Edit the file Main.c. Add the code necessary to fully implement the stubbed versions of the serial debug functions.

    The following code example shows the implementation of the serial debug functions for the hardware platform used in this boot loader example. In this example, a serial UART for debug messages is used and it operates through a GPIO port, port H.

    void OEMInitDebugSerial(void) 
    {
        volatile UART1reg   *s2410UART1   = (UART1reg *)UART1_BASE;
        volatile IOPreg     *s2410IOP   = (IOPreg *)IOP_BASE;
    
        // UART1 (TXD1 & RXD1) used for debug serial.
        //
    
        // Configure port H for UART.
        //
        s2410IOP->rGPHCON &= ~((3 << 8) | (3 << 10));   // Configure GPH2 and GHP3 for UART1 Tx and Rx, respectively.
        s2410IOP->rGPHCON |=  ((2 << 8) | (2 << 10));   //
        s2410IOP->rGPHUP  |=   (1 << 4) | (1 << 5);      // Disable pull-up on TXD1 and RXD1.
    
        // Configure UART.
        //
        s2410UART1->rUFCON  = 0x0;      // Disable the FIFO.
        s2410UART1->rUMCON  = 0x0;      // Disable AFC.
        s2410UART1->rULCON  = 0x3;      // Normal mode, N81.
        s2410UART1->rUCON   = 0x245;   // Rx pulse interrupt, Tx level interrupt, Rx error status interrupt enabled.
        s2410UART1->rUBRDIV = ( (int)(S2410PCLK/16.0/UART1BaudRate + 0.5) -1 );      // Set baudrate (38400).
    
    }
    
    void OEMWriteDebugString(unsigned short *str) 
    {
        // Loop through text string, sending characters.
        //
        while (str && *str)
        {
            OEMWriteDebugByte((unsigned char)*str++);
        }
    }
    
    void OEMWriteDebugByte(UCHAR ch)
    {
        volatile UART1reg *s2410UART1   = (UART1reg *)UART1_BASE;
    
        // Wait for transmit buffer to be empty.
        //
        while(!(s2410UART1->rUTRSTAT & 0x2))
        {
        }
    
        s2410UART1->rUTXH = ch;
    }
    

    The serial UART routines are typically generic for a particular CPU or hardware platform design; therefore, they can typically be copied from a similar BSP.

See Also

How to Develop a Boot Loader | How to Develop an OEM Adaptation Layer

Last updated on Wednesday, April 13, 2005

© 2005 Microsoft Corporation. All rights reserved.