Implementing GPIF Transactions
Creating GPIF Waveform Descriptors using GPIF Designer

In order to design the GPIF waveform descriptors for this example, it is first important to understand a little bit about how the TI DSP’s HPI protocol works. Each HPI transfer is a two-byte sequence. The meaning of the first byte and second byte depends on how the BOB bit is set in the DSP’s HPIC register. In our example, the BOB bit is set to 1, which means that the first byte of the HPI transfer is going to be the LSB and the second byte of the transfer is going to be the MSB (as organized in the DSP memory).

The example uses GPIF single write transactions for writing to the HPIC and HPIA registers, GPIF FIFO Write transactions for writing data into the HPI RAM, and GPIF FIFO Read transactions for reading data from the HPI RAM. Writing to the HPIC and HPIA registers is a special case that requires two separate waveform behaviors to describe the entire HPI transfer. One waveform behavior describes the timing and control logic for the first byte of the HPI transfer, and another describes the timing and control logic for the second byte of the HPI transfer.

In all of the GPIF waveforms, CTL0-2 are manipulated according to the HPI protocol and the HPI8 Mode Timing Requirements as outlined in the 5416 data sheet. In the text to follow, we will discuss how CTL0-2 were manipulated in the GPIF waveforms to describe the HPI protocol. Figure 23 shows the block diagram for the DSP example.

Figure 23. GPIF Designer Block Diagram View

Figure 24, below, shows waveform 0, which characterizes the behavior of the waveform called SnglWr1. SnglWr1 describes the HPI protocol required to write the first byte of an HPI transfer.

Figure 24. SnglWr1 waveform in GPIF Designer

Since SnglWr1 describes an HPI write operation, HR/W (CTL0) is held LOW throughout the entire transfer (S0-S2). HBIL (CTL2) is dropped LOW in S0 to signify that the first byte is being transferred. This is done before HDS1 (CTL1) is asserted in S1, in order to satisfy the set-up time requirement for HBIL before HDS1 can be made LOW. Since S0 is active for 20.83 ns (Wait 1 at 48-MHz IFCLK), this satisfies the set-up time requirement of 6 ns for HBIL. Since there is also a hold time requirement for HBIL, to simplify matters, HBIL is actually held LOW throughout the active portion of the entire waveform.

By looking at the HPI8 Timing Requirements in the 5416 data sheet, it becomes apparent that any of the strobe widths or set-up and hold times are well under 20.83 ns. Therefore, one can assume that a state need only last at maximum 20.83 ns (Wait 1). In S1, data is also placed on the bus (Activate Data). In S2, HDS1 is deasserted, thus ending this portion of the HPI transfer. S2 also unconditionally branches to the IDLE state to terminate the waveform.

The waveform that describes the second portion of the HPI transfer is very similar to SnglWr1, and is shown in Figure 25.

Figure 25. SnglWr2 waveform in GPIF Designer

Again, since SnglWr2 also describes an HPI write operation, HR/W is held LOW throughout the entire active portion of the waveform (S0–S2). The main difference between SnglWr2 and SnglWr1 is the state of HBIL; HBIL is HIGH throughout S0–S2. This signifies to the HPI that the second byte of the HPI transfer is being transmitted. S2 unconditionally branches to the IDLE state to terminate the waveform.

To recap, the SnglWr1 and SnglWr2 waveforms are used for GPIF single write accesses, which allow us to write to the DSP’s HPIC/HPIA registers. The GPIF engine allows you to select which of these waveforms are triggered by a GPIF single write access, via the GPIFWFSELECT register. Consecutive GPIF single write accesses using the waveforms SnglWr1 and SnglWr2 are made to describe the entire HPI transfer protocol. The details of this are described in the firmware programming section (4.2.5).

To create the GPIF FIFO read and write accesses that handle writing and reading to and from the HPI data RAM, the attributes of the SnglWr1 and SnglWr2 waveforms can be combined to form each of the GPIF FIFO read and write waveforms. Figure 26 shows the GPIF FIFO write waveform.

Figure 26. FIFOWr waveform in GPIF Designer

Waveform 3 (FIFOWr) describes an entire HPI write transfer. S0 drives both HR/W and HBIL LOW for 20.83 ns, then S1 asserts HDS1 and drives the data bus to present the first byte in the EP2 FIFO, effectively writing out the first byte of the HPI transfer to the HPI RAM. S2 then increments the FIFO pointer using Next FIFO data, deasserts HDS1, and drives HBIL HIGH to tell the HPI the second byte of the transfer is coming. S3 asserts HDS1 again and drives the data bus to present the second byte in the EP2 FIFO, effectively writing out the second byte of the HPI transfer to the HPI RAM.

The waveform then traverses to S5, a decision point state that examines the GPIF TC to determine whether or not to branch to the IDLE state. If the GPIF TC has not yet expired, the waveform will then branch back to S0 to actuate another HPI transfer. Otherwise, the waveform branches to the IDLE state and terminates. The FIFO read waveform is quite similar in nature and is shown in Figure 27.

Figure 27. FIFORd waveform in GPIF Designer

Waveform 2 (FIFORd) describes an entire HPI read transfer. HR/W is driven HIGH throughout the waveform to tell the HPI that this is a read operation. In S0, HBIL is driven LOW for 20.83 ns to satisfy the set-up time requirement for HBIL, then S1 asserts HDS1, which tells the HPI to present the first byte of the HPI read transfer onto the data bus. The data is not presented until 10ns later, therefore it is correct to only sample the databus in S2 and not in S1. By sampling the databus in S2, the first byte is read into the FX2’s EP6 FIFO. For a GPIF FIFO read transaction, an Activate Data also advances the FIFO pointer, so a Next FIFO data is not necessary.

S2 also drives HBIL HIGH to tell the HPI the second byte of the transfer is expected. S3 asserts HDS1 again and S4 samples the data bus to read the second byte into the EP6 FIFO.

The waveform then traverses to S5, a decision point state that examines the GPIF TC to determine whether or not to branch to the IDLE state. If the GPIF TC has not yet expired, the waveform will then branch back to S0 to actuate another HPI read transfer. Otherwise, the waveform branches to the IDLE state and terminates.

Now that you understand how the GPIF waveforms are programmed and set up for the DSP example, the firmware programming can be discussed.

Firmware

After the GPIF waveforms were implemented using GPIF Designer, the next step was to integrate the USB portion of the overlying firmware with the GPIF Designer output to perform write and read operations to and from the HPI. To do this a firmware frameworks project was copied and the code that performed the HPI operations was added to the TD_Poll() function within FX2_to_TI5416_HPI.c (note that periph.c was renamed to something more meaningful here). Endpoint and GPIF register initialization is performed in the TD_Init() function, which is also within FX2_to_TI5416_HPI.c.
When the user opens up the Keil uVision2 project for the DSP example, Figure 17 shows the list of files that should be seen in the Project Window:

Figure 17

The contents of these files is as follows:

fw.c
Firmware frameworks which handles USB requests and calls the task dispatcher TD_Poll().

Ezusb.lib
Collection of functions that handle suspend, resume, I2C operations, etc.

USBJmpTb.OBJ
Interrupt vector jump table for USB (INT2) and GPIF/Slave FIFO (INT4) interrupt sources.

dscr.a51
Device descriptor tables for the DSP example which report EP2OUT and EP6IN as available endpoints for the FX2 device. EP1IN and EP1OUT are also reported although they are not used in this example. These low bandwidth endpoints may be used for general purpose should the need arise in an application.

gpif.c
File that contains the GPIF waveform descriptor tables that implement the DSP GPIF transaction waveform behavior.

FX2_to_extsyncFIFO.c
Main user application code where TD_Poll() and TD_Init() can be found. The user will mainly be modifying this particular file and will not need to touch fw.c.

int0.c
File that contains the interrupt service routine for servicing the INT0/ interrupt.

TD_Init( )

The first task at hand was to setup the endpoints appropriately for this example. The following code switches the CPU clock speed to 48 MHz (since at power-on default it is 12 MHz), and sets up EP2 as a Bulk OUT endpoint, 4x buffered of size 512, and EP6 as a Bulk IN endpoint, also 4x buffered of size 512. This setup utilizes the maximum allotted 4 KB FIFO space. It also sets up the FIFOs for auto mode, byte wide operation, and goes through a FIFO reset and arming sequence to ensure that they are ready for data operations. EP1IN and EP1OUT are also setup in case the application needs them, although they are not used by this example.

> // set the CPU clock to 48MHz
> CPUCS = ((CPUCS & ~bmCLKSPD) | bmCLKSPD1);
> SYNCDELAY;
> EP1OUTCFG = 0xA0; // always OUT, valid, bulk
> SYNCDELAY;
> EP1INCFG = 0xA0; // always IN, valid, bulk
> SYNCDELAY;
> EP2CFG = 0xA0; // EP2OUT, bulk, size 512, 4x buffered
> SYNCDELAY;
> EP4CFG = 0x00; // EP4 not valid
> SYNCDELAY;
> EP6CFG = 0xE0; // EP6IN, bulk, size 512, 4x buffered
> SYNCDELAY;
> EP8CFG = 0x00; // EP8 not valid
> SYNCDELAY;
> FIFORESET = 0x80; // set NAKALL bit to NAK all transfers from host
> SYNCDELAY;
> FIFORESET = 0x02; // reset EP2 FIFO
> SYNCDELAY;
> FIFORESET = 0x06; // reset EP6 FIFO
> SYNCDELAY;
> FIFORESET = 0x00; // clear NAKALL bit to resume normal operation
> SYNCDELAY;
> EP2FIFOCFG = 0x00; // allow core to see zero to one transition of auto out bit
> SYNCDELAY;
> EP2FIFOCFG = 0x10; // auto out mode, disable PKTEND zero length send, byte ops
> SYNCDELAY;
> EP6FIFOCFG = 0x08; // auto in mode, disable PKTEND zero length send, byte ops
> SYNCDELAY;
> EP1OUTBC = 0x00; // arm EP1OUT by writing any value to EP1OUTBC register
> SYNCDELAY;
> GpifInit (); // initialize GPIF registers
> PORTACFG = bmBIT0; // initialize PA3 and PA2 port i/o pins as outputs,
> OEA |= 0x0C; // PA0 takes on INT0/ alternate function
> EX0 = 1; // Enable INT0/ interrupt
> IT0 = 1; // Detect INT0/ on falling edge

IFCONFIG Register

TD_Init then calls the function GPIFInit() which resides in gpif.c. GPIFInit() is where the loading of the GPIF waveform descriptor table into on-chip memory takes place and other GPIF registers get setup. An important register, IFCONFIG, also gets setup here to define how the physical interface operates. Table 4 goes through the reasoning behind the setup of the IFCONFIG register for this example.
The last thing TD_Init does is it setups up PA3 and PA2 as outputs and enables the INT0/ functionality.

Bit # | Bit Label | Contents / Description
7 | IFCLKSRC | Set to 1 to run the GPIF using the internal clock source
6 | 3048MHz | Set to 1 to run the internal clock source for the GPIF at 48MHz.
5 | IFCLKOE | Set to 1 to turn on the IFCLK output to drive the WCLK and RCLK inputs of the external FIFO.
4 | IFCLPOL | Set to 0.
3 | ASYNC | Set to 0 to operate the GPIF at the highest rate (sync mode).
2 | GSTATE | Set to 1 to turn on the debug outputs of the state machine. PE[2:0] displays the states the GPIF engine cycles through during each transaction (Note: PE[2:0] are only available on the 100- and 128-pin packages).
1 | IFCFG1 | Set to 1 to put the FX2 part into GPIF mode (internal master).
0 | IFCFG0 | Set to 0 to put the FX2 part into GPIF mode (internal master).

Table 4. IFCONFIG register bit settings for FIFO example

The next thing TD_Init() does is it resets the external FIFO by pulsing PA2. This ensures that the external FIFO is at a ground-zero state before commencing data operations. The following code does the trick:

> // reset the external FIFO
> OEA |= 0x04; // turn on PA2 as output pin
> IOA |= 0x04; // pull PA2 high initially
> IOA &= 0xFB; // bring PA2 low
> EZUSB_Delay (1); // keep PA2 low for ~1ms, more than enough time
> IOA |= 0x04; // bring PA2 high

A vendor command was also setup in the DR_VendorCmnd() function so that the user could reset the external FIFO at any time by performing a vendor request of 0xB2 from the EZ-USB Control Panel.

Writing to the HPIC and HPIA registers

The firmware implements a 0xB6 IN vendor command and a 0xB7 IN vendor command to write to the HPIC and HPIA registers, respectively. The following code writes to the HPIC register:

case VX_B6: // write to HPIC register
{
EP0BCL = 0; // re-arm EP0
while(EP01STAT & bmEP0BSY); // wait until EP0 is available to be accessed by CPU
while(!HPI_RDY); // wait for HPI to complete internal portion of previous transfer
IOA = bmHPIC; // select HPIC register
GPIFWFSELECT = 0xE4; // point to waveforms that write first byte of HPI protocol
GPIF_SingleByteWrite(EP0BUF[0]); // write LSB of DSP address
GPIFWFSELECT = 0xA4; // point to waveforms that write second byte of HPI protocol
GPIF_SingleByteWrite(EP0BUF[1]); // write MSB of DSP address
break;
}

The first thing the code does is it re-arms the EP0 buffer to accept the next packet from the host. It then waits until the EP0 buffer is available for the CPU to access. Before addressing the HPIC register, the code checks to see if the HPI is ready to accept another transfer. The HPIC register is then addressed by writing a 0 to both PA3 and PA2.
The technique of changing the waveform index in the GPIFWFSELECT register to point to different waveforms is useful for handling protocols such as the HPI, where more than one waveform behavior is required to describe a complete read/write cycle. The GPIFWFSELECT register is first configured to point to the SnglWr1 waveform which writes the first byte of the HPI protocol. This is now the waveform that gets triggered when a GPIF single write access occurs. The first byte in EP0BUF is then written out to the HPI. The GPIFWFSELECT register is then configured to point to the SnglWr2 waveform which writes the second byte of the HPI protocol. This is now the waveform that gets triggered when a GPIF single write access occurs. The second byte in EP0BUF is then written out to the HPI. Because the HPI protocol calls out that both first and second bytes in the HPI transfer should be of equal value when writing to the HPIC register, the host should send down two bytes of equal value when performing the vendor IN request of 0xB6.

The following code writes to the HPIA register:

> case VX_B7: // write to HPIA register
> {
> EP0BCL = 0; // re-arm EP0
> while(EP01STAT & bmEP0BSY); // wait until EP0 is available to be accessed by CPU
> while(!HPI_RDY); // wait for HPI to complete internal portion of previous transfer
> IOA = bmHPIA; // select HPIA register
> GPIFWFSELECT = 0xE4; // point to waveforms that write first byte of HPI protocol
> GPIF_SingleByteWrite(EP0BUF[0]); // write LSB of DSP address
> GPIFWFSELECT = 0xA4; // point to waveforms that write second byte of HPI protocol
> GPIF_SingleByteWrite(EP0BUF[1]); // write MSB of DSP address
> break;
> }

The first thing the code does is it re-arms the EP0 buffer to accept the next packet from the host. It then waits until the EP0 buffer is available for the CPU to access. Before addressing the HPIA register, the code checks to see if the HPI is ready to accept another transfer. The HPIA register is then addressed by writing a 1 to PA3 and a 0 to PA2.
The GPIFWFSELECT register is then configured to point to the SnglWr1 waveform which writes the first byte of the HPI protocol. This is now the waveform that gets triggered when a GPIF single write access occurs. The first byte in EP0BUF is then written out to the HPI. The GPIFWFSELECT register is then configured to point to the SnglWr2 waveform which writes the second byte of the HPI protocol. This is now the waveform that gets triggered when a GPIF single write access occurs. The second byte in EP0BUF is then written out to the HPI. When the host performs the vendor IN request of 0xB7, the first byte contains the LSB of the DSP address, and the second byte contains the MSB of the DSP address.

Performing Data Writes and Reads to and from the HPI

The code in TD_Poll() handles USB OUT transfers (Data Writes to the HPI) and USB IN transfers (Data Reads from the HPI).
Code that handles USB OUT Transfers

if ( GPIFTRIG & 0x80 ) // if GPIF interface IDLE
{
if ( ! ( EP24FIFOFLGS & 0x02 ) ) // if there's a packet in the peripheral domain for EP2
{
IOA = bmHPID_AUTO; // select HPID register with address auto-increment
while(!HPI_RDY); // wait for HPI to complete internal portion of previous transfer
SYNCDELAY;
GPIFTCB1 = EP2FIFOBCH; // setup transaction count with number of bytes in the EP2 FIFO
SYNCDELAY;
GPIFTCB0 = EP2FIFOBCL;
SYNCDELAY;
GPIFTRIG = GPIF_EP2; // launch GPIF FIFO WRITE Transaction from EP2 FIFO
SYNCDELAY;
while( !( GPIFTRIG & 0x80 ) ) // poll GPIFTRIG.7 GPIF Done bit
{
;
}
SYNCDELAY;
}
}

The first thing the OUT handling code does is it checks to see if the GPIF is IDLE. If so, it checks to see if there is at least a packet in the peripheral domain for EP2. Since EP2 is placed into auto mode, the firmware does not need to check if the host sent a USB packet. The USB packets are automatically committed to be used by the GPIF engine. Therefore, the firmware's job is to check if at least one packet has been committed to the peripheral domain.
The TC value is then simply setup with the number of bytes in the EP2 FIFO. This allows the user to handle packet sizes less than 512 but greater than zero. The TC value is a 32-bit register field, but for this application only the lower 16-bit fields are used.
A write to the GPIFTRIG register with the appropriate bits triggers the transaction from EP2OUT. The code then waits for the transaction to complete before exiting out of the if nest.
Code that handles USB IN transfers

if(in_enable) // if IN transfers are enabled,
{
if(Tcount) // if Tcount is not zero
{
if( GPIFTRIG & 0x80 ) // if GPIF interface IDLE
{
if( !( EP68FIFOFLGS & 0x01 ) ) // if EP6 FIFO is not full
{
IOA = bmHPID_AUTO; // select HPID register with address auto-increment
while(!HPI_RDY); // wait for HPI to complete internal portion of previous transfer
SYNCDELAY;
GPIFTCB1 = MSB(Tcount); // setup transaction count with Tcount value
SYNCDELAY;
GPIFTCB0 = LSB(Tcount);
SYNCDELAY;
GPIFTRIG = GPIFTRIGRD | GPIF_EP6; // launch GPIF FIFO READ Transaction to EP6IN
SYNCDELAY;
while( !( GPIFTRIG & 0x80 ) ) // poll GPIFTRIG.7 GPIF Done bit
{
;
}
SYNCDELAY;
xFIFOBC_IN = ( ( EP6FIFOBCH << 8 ) + EP6FIFOBCL ); // get EP6FIFOBCH/L value
if( xFIFOBC_IN < 0x0200 ) // if pkt is short,
{
INPKTEND = 0x06; // force a commit to the host
}
Tcount = 0; // set Tcount to zero to cease reading from DSP HPI RAM
}
}
}
}

If the in_enable flag is not set via vendor IN command 0xB3, the code will just sit there and not process the INs. If the in_enable flag is set, and then if Tcount is not zero (read along for a further explanation of the Tcount variable), the code will fall through and check if the GPIF interface is IDLE. It then determines if EP6 is not full, implying that it has room for at least one more data packet.
If EP6 has room for at least one more data packet, the TC value is setup with a user defined value of Tcount. The value of Tcount is setup prior to the IN transfer and can be done so by executing the vendor OUT command 0xB5. For example, if the user wanted to read 4KB worth of information from the HPI, the user would send a value of 0x1000 using the vendor OUT command 0xB5. This requires that the user should have a priori knowledge of how much data is going to be read.
A write to the GPIFTRIG register with the appropriate bits triggers the transaction to fill the EP6 FIFO. The code then waits for the transaction to complete. Since EP6 is placed into auto mode, there is no need to explicitly write a byte count value to indicate how many bytes to send to the host. FX2 uses the EP6AUTOINLENH/L register values set at enumeration time in the DR_SetConfiguration() function for the auto commit size.
However, to handle packets sizes less than 512 (the last packet of the transfer will typically be short), the code checks to see if it needs to write to the INPKTEND register with a 0x06 to commit the short packet to the host. The Tcount variable is then set to zero to prevent from entering the loop again. Now we see the relevance of the upper if statement that checks for Tcount. If the statement was not present, the code would continously fill EP6IN as the host requests IN after IN. This complicates matters on the host side becasue when the next transfer is started, stray buffers from the previous transfer would be retrieved by the host. By checking if Tcount is greater than zero, the user would be forced to set the Tcount variable to the desired value to start another transfer.

Handling INT0

The HINT/ output is connected to the INT0/ interrupt on the FX2. This allows the 5416 to perform software handshaking with the FX2 when necessary. The INT0/ interrupt is handled via the following ISR:

void int0_isr (void) // interrupt 0
{
hpi_int = TRUE; // HPI interrupted the FX2
EX0 = 0; // disable INT0/ interrupt, let foreground re-enable it
}

The ISR sets a global flag hpi_int which is monitored by TD_Poll(). The code in TD_Poll then clears the hpi_int flag and turns on LED1 on the FX2 development board:

if (hpi_int)
{
hpi_int = FALSE; // clear HPI interrupt flag
EX0 = 1; // enable INT0 interrupt again
LED_On (bmBIT1); / turn on LED1 to alert user HPI interrupt occurred
}

A vendor IN command of 0xB2 is provided so that the user can turn off LED1.

Running the DSP example

Now that the user has a good idea how this DSP example works, we can now attempt to read and write to the HPI and later on bootload the DSP code.
Step 1: Download the firmware using the EZ-USB Control Panel

a) Unzip the "FX2_to_TI5416_HPI GPIF FIFO Transactions Auto Mode.zip" package in the C:\Cypress\USB\Examples\FX2 directory.

b) Plug in the 5416 DSK board and launch the Code Composer Studio for the 5416.

c) After the user plugs-in the FX2 board, launch the EZ-USB Control Panel and ensure that the selected target is FX2.

d) Then, press the Download button and select the FX2_to_TI5416_HPI.hex file. The FX2 board renumerates as a Cypress EZ-USB Sample Device and LED0 should come up flashing.

e) Perform a Get Pipes and Get Dev to verify one more time that the firmware is up and running. The user should then see the following screen shown below:

Step 2: Perform a write to the HPI

a) Launch Code Composer Studio and pull up the Memory window at 0x7000. This will allow the user to monitor if the writes occurred correctly.

b) Perform a vendor OUT command of 0xB6 to write to the HPIC register using data values 0x01 0x01. This sets the BOB to 1 in the HPIC register so that the first byte of future HPI transfers is the LSB and the second byte is the MSB (as organized in DSP memory)

c) Perform a vendor OUT command of 0xB7 to write to the HPIA register with the address of 0x7000. Notice the ordering of the data in the hex bytes field since the BOB bit has been set to 1.

d) Perform another vendor OUT command of 0xB6 to write to the HPIC register using data values 0x11 0x11. This keeps the BOB bit set at 1 and sets the XPHIA bit. Setting the XPHIA bit does two things: 1) it allows the address to auto-increment for consecutive data accesses to HPI and 2) the next write to the HPIA register contains the extended address value.

e) Perform another vendor OUT command of 0xB7 to write the extended address value into the HPIA register. The value is 0x00 0x00 since the address 0x7000 lies within the first page of DSP memory.

f) Select Endpoint 2 OUT as the Pipe, press the FileTrans .. buttion and select the 512_count.hex file. Click on Open and this action will send out 512 bytes of ramp data to the HPI.

Since the auto-increment mode is used to write data to the HPI, the first data value will appear at 0x7001 since the HPI pre-increments the address prior to the data write. If the user wanted the first data value to appear at 0x7000, the address to write to the HPIA register would have been 0x6FFF.
The user could have also used the Bulk Trans button to send a specific data pattern to the HPI.
The Memory window of Code Composer Studio indicates the HPI write was successful.

Step 3: Perform a Read from the HPI

To read from a specific address in the DSP, the user should perform steps a. through e. as listed in Step 2. For HPI auto-increment reads, the DSP post-increments the address for a data read, so the user can exactly specify the address to read from. In this instance, we will read back the data values previously written in step 2. We will read 768 bytes to illustrate that the FX2 firmware can handle short packets (since 768 is not an even divisor of 512).

a) Perform a 0xB5 vendor OUT command to set the Tcount variable to 768 (0x0300). This sets up the GPIF to read 768 bytes from the HPI.

b) Pend an IN request for 768 bytes on Endpoint 6 IN.

c) Perform a 0xB3 vendor IN command to set the in_enable flag to TRUE. 768 bytes worth of data should now be displayed on the EZ-USB Control Panel window. Note that the data values are the same as the ones previously written by the HPI write, thus proving that the read was a success.

Step 4: Bootloading the DSP code

A host processor can download the DSP code via the HPI and bootload it by writing the entry point of the program to location 0x7E and 0x7F in DSP data memory. This is a common method used to bootload a TI DSP. To force the 5416 to use the HPI as the bootload method, the HINT/ and INT2/ pins are tied together. The host processor just needs to then download the code, write the entry point, and the DSP program starts running. For simplicity sake, this example chose to use the LED code supplied in the 5416 DSK software. Once the bootload is complete, the user LED0 on the DSK board starts flashing.
The bootloading techniques used in this example are very similar to those explained in the TI appnote SPRA382. Code Composer Studio generates a .out executable file that follows the Common Object File Format (COFF). This .out file cannot be simply downloaded to the DSP. The program and data sections need to be extracted by running a COFF Hex Extraction Utility on the .out file. This utility is available with the SPRA382 appnote. After typing in the command coff_both -out led.out, the utility creates a hex listing that is called led.out.c.
A PC application created in MS VC++ 6.0 called HPIMr (short for HPI Manager) reads in this hex listing, determines the start address of each section, and writes each section of code/data to the FX2's Endpoint 2 OUT. Prior to writing each block of code/data, the sequence of 0xB6 and 0xB7 vendor OUT commands are performed to write to the HPIC/HPIA registers (similar to Step 2). The source code for the HPIMr utility is provided if the user is interested in seeing how this is done. This utility can also serve as another host application example.
These are the steps the user should follow to download the LED example:

a) Plug in the 5416 DSK board, plug in the FX2 development board.

b) Download the FX2_to_TI5416_HPI.hex firmware.

c) Run the HPIMr.exe utility. The user should see the following screen if an FX2 development board is plugged in.

d) Next, click on the Send Program to HPI button and select the led.out.c file. Click on Open.

The user should should now see the user LED0 flashing on the 5416 DSK board.

Logic Analyzer Traces
These are the traces the user should see on the logic analyzer as the DSP example runs. The traces were captured using an HP1660C logic analyzer.

Writing to HPIC: Zoomed out view
This trace shows what the user should see when a 0xB6 vendor OUT command is performed to write to the HPIC register (HCNTL=00). The first byte of the HPI transfer is followed by IDLE time, where the FX2 firmware is switching to the GPIF single write waveform that writes the second byte of the HPI transfer. A similar waveform can be observed when a 0xB7 vendor OUT command is performed to write to the HPIA register.

Writing to HPIC: Close-up view of 1st byte of HPI transfer
Here we see a close-up view of the 1st byte of a HPI write to the HPIC register. A write operation is signified by HR/W/ being driven low. HBIL is also driven low to signify that this is the 1st byte of the HPI transfer. The timing shown here adheres to the HPI8 Mode Timing Requirements in the 5416 datasheet. A similar waveform can be observed when a 0xB7 vendor OUT command is performed to write to the HPIA register.

Writing to HPIC: Close-up view of 2nd byte of HPI transfer
Here we see a close-up view of the 2nd byte of a HPI write to the HPIC register. A write operation is signified by HR/W/ being driven low. HBIL is now driven high to signify that this is the 2nd byte of the HPI transfer. The timing shown here adheres to the HPI8 Mode Timing Requirements in the 5416 datasheet. A similar waveform can be observed when a 0xB7 vendor OUT command is performed to write to the HPIA register.

FIFO Write to HPI: Zoomed out view
This is a snapshot of the activity that goes on when the FX2 performs a burst write to the HPI using GPIF FIFO Write transactions. A similar waveform can be observed when the FX2 performs a burst read from the HPI using GPIF FIFO Read transactions.

FIFO Write to HPI: Close-up view
Here we see a close-up view of a write to the HPI using GPIF FIFO Write transactions. S0-S5 marks the duration of a complete HPI write access. Since it is a write, the HR/W/ signal is driven low for the duration of the transfer. In S0-S1, the HBIL signal is driven low to transfer the first byte, and in S2-S3 the HBIL signal is driven high to transfer the second byte. The timing here conforms to the HPI8 Mode Timing Requirements in the 5416 datasheet.
Notice that the HRDY/ signal goes low after the second byte is transferred. The next HPI write does not start until the HRDY/ is high again (indicating that the HPI is ready for the next write). S4 and S5 allow enough time for the HRDY/ signal to become high again. Thus it was not necessary to check for HRDY/ in the GPIF waveform logic before starting the next HPI write sequence. S5 is the decision point state that uses the GPIF transaction count expiration flag (RDY5) to determine whether or not the data burst should continue.

FIFO Read from HPI: Close-up view
Here we see a close-up view of a read from the HPI using GPIF FIFO Read transactions. S0-S5 marks the duration of a complete HPI read access. Since it is a read, the HR/W/ signal is driven high for the duration of the transfer. In S0-S1, the HBIL signal is driven low to transfer the first byte, and in S2-S3 the HBIL signal is driven high to transfer the second byte. The timing here conforms to the HPI8 Mode Timing Requirements in the 5416 datasheet.
Notice that the HRDY/ signal goes low after the second byte is transferred. The next HPI read does not start until the HRDY/ is high again (indicating that the HPI is ready for the next read). S4 and S5 allow enough time for the HRDY/ signal to become high again. Thus, it was not necessary to check for HRDY/ in the GPIF waveform logic before starting the next HPI read sequence. S5 is the decision point state that uses the GPIF transaction count expiration flag (RDY5) to determine whether or not the data burst should continue.

Downloading DSP code
This is a snapshot of the activity that goes on when the FX2 downloads the DSP code to the 5416 DSK board.

Summary

This design example of an FX2 GPIF interface to a TI 5416 DSP's HPI has given the user another concrete example of how the FX2 GPIF can be used in a practical application. This DSP example builds upon the external FIFO example, and extends the user's understanding of how to create GPIF waveform descriptors and program the GPIF to perform reads and writes over the physical interface. Because the HPI protocol is significantly more complex than that of an external FIFO's, the user can appreciate what it takes to create a full featured GPIF application, and apply the techniques presented here to solve applications that have slave devices with more sophisticated protocols.