Aerospace



Discussion Forum

Home

Company Information

Information Request

Linux How-to Guides

ADSP 21xx
Digital Signal Processing
Tutorials

SW Utilities

On-line Order Form

Aerospace Projects

Commercial Projects

Circuit Boards

Server Support


bonk

Have you found this site useful? Did we save you time? Did we cure your head-ache? Is your hair growing back now?

Please make a donation to help with maintenance.


Objective Real-Time Software on the ADSP21XX

Input/Output Objects on the ADSP 218X

General

The 218X series introduced a third address field, Input/Output (I/O). However, a major hurdle in using I/O objects on the 218X, is the severely limited I/O addressing modes. There is only one, namely the immediate mode. This is a very good reason to avoid using I/O mapping altogether and memory map all I/O ports.

H.Acker did not have the opportunity to provide design input into the multi channel data recorder. The first time he saw the device, was when the designer handed him one of the first working models. As you may remember, this device has four identical A/D channels, which can allow one to write compact, object oriented control code. The only problem is the I/O mapped ports. H.Acker handled the problem by creating separate program threads for each port, which made the code four times larger than it needed to be. His successor S.Ucker, was faced with the problem of porting the four channel code to a new 32 channel device. Clearly, replicating the code to make 32 threads would not be practical at all.

I/O Addressing Modes

As indicated above, there is only one mode, where the address has to be an immediate value, which is assembled directly into the instruction:

Immediate I/O Addressing
   ar = IO(address);

or

   IO(address) = ar;

This limitation presents a real problem. In order to process the multiple data recorder ports, it would be prudent to create a single control process and run it in a loop, with incrementing port addresses. That way, one only need to debug the code once to make it handle all 32 channels. Clearly a huge saving in code space and debugging effort.

Indirect I/O Addressing

Create a single control process and run it in a loop, with incrementing port addresses

If the ports were memory mapped, S.Ucker would have had no problem at all and could use the DAG registers to address the I/O in the same way as one would address a data table and the auto incrementing features of the DAG registers would make handling the 32 ports a simple matter. Clearly, what S.Ucker needs, is an indirect I/O instruction, where the I/O address can be supplied in a register, but there is none. So, how now brown cow?

Self Modifying Code

The only solution, is self modifying code. The ADSP 218X normally executes from internal PM, which is RAM. This memory is writeable. Therefore, we can create special functions, that will accept the port address as a parameter and create the relevant I/O instruction on the fly.

Self Modifying Code

We can create special functions, that will accept the port address as a parameter and create the relevant I/O instruction on the fly

While self modifying code is usually an absolute no-no, this case presents a very good excuse to use it. If we encapsulate it in a set of procedures, it would be robust and S.Ucker would be eternally grateful for this solution.

Indirect I/O Port Read
/************************** Literals ********************************/
#define IO_AR        0x000A
#define IO_TYPE29R   0x0100
#define IO_TYPE29W   0x0180
#define IO_NIBBLE    0x0004

/*********************************************************************
* Name:        io_read_port
* Description: Read an I/O port for easy indirect port addressing:
*              ar = IO(ar)
* Constraints: ar = port address
*              returns ar = data
*********************************************************************/
io_read_port:
   MAC_ENTER

   /*
    * create the IO instruction
    */
   sr0 = IO_AR;                     /* ar */
   sr = sr OR LSHIFT ar by IO_NIBBLE (lo);
   px = sr0;

   sr0 = IO_TYPE29R;                /* type 29 read */
   sr = sr OR LSHIFT ar by -IO_NIBBLE (lo);

   /*
    * Modify the NOP
    */
   i4 = ^io_read_port_modify;
   pm(i4, m4) = sr0;

   /*
    * read the data: ar = IO(address)
    */
io_read_port_modify:
   nop;

   MAC_EXIT
   rts;

The IO instruction is a type 29 instruction, which looks like this:

Type 29 Instruction
Type      Direction     Address              Register
xxxx x    x             xx xxxx xxxx xxxx    xxxx

0000 1    Rd = 0        Address              ar = 0000
          Wr = 1

Each instruction is 24 bits in size. The bottom 4 bits is the register number, which is 0000H for ar. The immediate address is 14 bits, followed by a direction bit, to indicate read or write and finally the type 29 opcode, which is 00001H.

The difficulty is that the path between the PM and the ALU is only 16 bits wide. When we read a 24 bit PM word into ar, we only get the upper 16 bits. The lower 8 bits move into the px register. The same thing happens when we write to PM. The lower 8 bits are supplied by the px register and the upper 16 bits by ar. Therefore, we have to create the I/O instruction in stages.

The PX Register

When we read a 24 bit PM word into ar, we only get the upper 16 bits. The lower 8 bits move into the px register.

First, we take the I/O address in ar and shift it left by 4, then we add in the code for the ar register. The lower 8 bits of sr0 is moved to the px register. Secondly, we take the address in ar, shift it right by 4 and add in the type 29 opcode and the direction bit. Our I/O instruction is now ready and can be written to PM. We use the DAG register set i4 for this purpose and write our synthesized instruction into a NOP instruction, which was inserted as a place holder. Note that the px register is automatically written to PM, together with the contents of sr0, during a full 24 bit wide move.

The code for an I/O write operation is very similar to the above and the only difference is the direction bit:

Indirect I/O Port Write

/*********************************************************************
* Name:        io_write_port
* Description: Write to an I/O port for easy indirect port addressing.
*              IO(ax0) = ar
* Constraints: ar = data
*              ax0 = port address
*********************************************************************/
io_write_port:
   MAC_ENTER

   /*
    * create the IO instruction
    */
   si = ax0;
   sr0 = IO_AR;                     /* ar */
   sr = sr OR LSHIFT si by IO_NIBBLE (lo);
   px = sr0;

   sr0 = IO_TYPE29W;                /* type 29 write */
   sr = sr OR LSHIFT si by -IO_NIBBLE (lo);

   /*
    * Modify the NOP
    */
   i4 = ^io_write_port_modify;
   pm(i4, m4) = sr0;

   /*
    * write the data: IO(address) = ar
    */
io_write_port_modify:
   nop;           

   MAC_EXIT
   rts;

While self modifying code in general is best avoided, this is clearly a case where its use is justified and S.Ucker went whistling home...



Copyright © 1996-2008, Aerospace Software Ltd., GPL.