From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=BAYES_00,INVALID_DATE autolearn=no autolearn_force=no version=3.4.4 Path: utzoo!utgpu!jarvis.csri.toronto.edu!cs.utexas.edu!tut.cis.ohio-state.edu!ucbvax!harvax.UUCP!harold From: harold@harvax.UUCP (Harold Rabbie) Newsgroups: comp.lang.ada Subject: Memory-mapped I/O Message-ID: <9001161758.AA17503@monolith.> Date: 16 Jan 90 17:58:51 GMT Sender: daemon@ucbvax.BERKELEY.EDU Organization: The Internet List-Id: It seems that there is a misconception about the use of data representation specifications to do memory mapped I/O in embedded systems. A lot of people write code like: type DEVICE_TYPE is range 0 .. 255; for DEVICE_TYPE'SIZE use 8; -- some 8-bit hardware register DEVICE_REGISTER : DEVICE_TYPE; for DEVICE_REGISTER use at 16#1234#; -- memory mapped address and then use Ada assignment statements with the object DEVICE_REGISTER to do memory-mapped I/O. For example, on a bare Motorola 680X0 you do I/O by accessing locations in the memory space. The object DEVICE_REGISTER is of course a special piece of hardware inside the I/O device, and not a RAM location at all. The problem is that most compilers assume that data objects are stored in RAM memory, and so they may do things that work fine in RAM, but are inappropriate for device registers. Many devices are sensitive to the size of access, whether 8, 16 or 32 bits wide. Just because I give a SIZE representation clause to an object doesn't guarantee that the compiler will emit a MOVE instruction of the specified width when accessing that object. It's perfectly entitled to use whatever instructions it deems appropriate, as long as it gets the right result in normal RAM memory. For example, if the device is located at an odd address, the generated code might read the next lower even address and mask out the unnecessary bits. That is a valid sequence for a RAM location, but probably wouldn't work on an I/O device register. Another behavior that works just fine for RAM is to initialize all of memory to zero before the program starts. This could easily screw up many devices. I believe the right way to do physical I/O is through the LOW_LEVEL_IO package defined in LRM 14.6. For example, you could define the package to do memory-mapped I/O of different widths as follows: package LOW_LEVEL_IO is subtype DEVICE_TYPE is new SYSTEM.ADDRESS; -- for memory-mapped I/O procedure SEND_CONTROL ( DEVICE : DEVICE_TYPE; DATA : in .... procedure RECEIVE_CONTROL( DEVICE : DEVICE_TYPE; DATA : out .... end LOW_LEVEL_IO; The procedures SEND_CONTROL would do memory-mapped output and the procedures RECEIVE_CONTROL would do memory-mapped input. You specify the width (8, 16 or 32 bits) by the type of the DATA parameter. Then you write the body of LOW_LEVEL_IO with machine-code insertion or pragma INTERFACE( ASSEMBLER) to get the right instruction sequence. This also has the benefit of portability to embedded architectures that do not use memory-mapped I/O, for example, the Intel 80X86 family. And if your compiler can inline assembly code, you're in fat city. I'd be interested in hearing the opinions of the Ada community on this. If people really want to do memory-mapped I/O with Ada assignment statements, then somehow we'll have to tighten up the requirements on the code generated by the compilers. --------------------------+-------------------------------------------- Harold Rabbie, Ready Systems "when REAL_TIME => accept READY_SYSTEMS;" UUNET: {sun!pyramid,hplabs}!harvax!harold ARPA: rabbieh@ajpo.sei.cmu.edu --------------------------+--------------------------------------------