gardner@titan.tsd.arlut.utexas.edu (Don Gardner) (05/30/91)
Is there some way to define a type such that all variables of that type can be only read or only written? For instance, I have a type that defines the status register of some peripheral which can only be read. It would be best if the compiler could flag any assignments to variables of such a type as errors. Is there some other means for commonly handling this problem? The compiler being used is VADSWorks, if an implementation-defined solution is required.
willett@cbnewsl.att.com (david.c.willett) (06/01/91)
In article <3949@titan.tsd.arlut.utexas.edu>, gardner@titan.tsd.arlut.utexas.edu (Don Gardner) writes: > Is there some way to define a type such that all variables of that > type can be only read or only written? For instance, I have a type that > defines the status register of some peripheral which can only be > read. It would be best if the compiler could flag any assignments > to variables of such a type as errors. > > Is there some other means for commonly handling this problem? The > compiler being used is VADSWorks, if an implementation-defined solution > is required. Don, The Ada LRM defines a "limited private" type as being one for which the assignment operators are not valid outside the package in which the type is declared (see Booch, "Programming in Ada" pg. 125). I think that will take care of the read only case. Only suggestion I have on write only is to hide the write operation in a package and provide no corresponding read function. Good Luck, Dave Willett
willett@cbnewsl.att.com (david.c.willett) (06/01/91)
In article <1991May31.170353.21919@cbnewsl.att.com>, willett@cbnewsl.att.com (david.c.willett) writes: > In article <3949@titan.tsd.arlut.utexas.edu>, gardner@titan.tsd.arlut.utexas.edu (Don Gardner) writes: > > Is there some way to define a type such that all variables of that > > type can be only read or only written? For instance, I have a type that > > defines the status register of some peripheral which can only be > > read. It would be best if the compiler could flag any assignments > > to variables of such a type as errors. > > > > Is there some other means for commonly handling this problem? The > > compiler being used is VADSWorks, if an implementation-defined solution > > is required. > > Don, > The Ada LRM defines a "limited private" type as being one for which > the assignment operators are not valid outside the package in which the > type is declared (see Booch, "Programming in Ada" pg. 125). I think that > will take care of the read only case. Only suggestion I have on write > only is to hide the write operation in a package and provide no > corresponding read function. > > Good Luck, > Dave Willett > Silly me, I listed the wrong author in the above post. As we all know, Grady Booch wrote "Software Engineering with Ada". It was J.G.P. Barnes who wrote "Programminng in Ada". My appologies to all concerned. Dave
jduarte@liege.ICS.UCI.EDU (J o s e D u a r t e) (06/01/91)
> Is there some way to define a type such that all variables of that > type can be only read or only written? For instance, I have a type that > defines the status register of some peripheral which can only be > read. It would be best if the compiler could flag any assignments > to variables of such a type as errors. > Is there some other means for commonly handling this problem? The > compiler being used is VADSWorks, if an implementation-defined solution > is required. Well...Ada is "the" language which explicitly allows you to name variables which can ONLY be read/written/read-write using the procedure syntax: with REGISTERS; use REGISTERS; procedure XYZ(Reg1: in REGISTER; Reg2: out REGISTER; Reg3: in out REGISTER) is begin -- Reg1 can only be used effectively as a constant in this scope -- Reg2 can only be assigned to in this scope -- Reg3 can be assigned to or read from in this scope null; end XYZ; . . . Alternatively, you can define a "type" and then define a constant variable of that type for read only variables... subtype XYZ is INTEGER; NY_REG: constant XYZ := 0; -- define a variable that can only be read . . . to define a type in a package that does not have ":=" implicitly defined...use a "limited private" syntax: package REGS is type XYZ is limited private; -- insert your procedures/functions here private type XYZ is NEW WHATEVER; end REGS; --- This would not allow other programmers that use your package to assign --- to a variable of type "XYZ" unless you also specified an "Assign" procedure. --- Look up "limited private" types for more details. I believe that "limited --- private" means that "=" (and thus "/=") and ":=" are not implicitly --- allowed for that type unless the package also explicitly defines --- "Assign" and "Read-From" procedures Jose' D.
orville@weyrich.UUCP (Orville R. Weyrich) (06/01/91)
In article <9105311025.aa04299@PARIS.ICS.UCI.EDU> Jose Aleman Duarte <jduarte@liege.ICS.UCI.EDU> writes: >> Is there some way to define a type such that all variables of that >> type can be only read or only written? For instance, I have a type that >> defines the status register of some peripheral which can only be >> read. It would be best if the compiler could flag any assignments >> to variables of such a type as errors. > >> Is there some other means for commonly handling this problem? The >> compiler being used is VADSWorks, if an implementation-defined solution >> is required. > >Alternatively, you can define a "type" and then define a constant variable >of that type for read only variables... > >subtype XYZ is INTEGER; >NY_REG: constant XYZ := 0; -- define a variable that can only be read > NO NO NO! The original poster wants to describe a memory location that can be modified by an entity outside the control of the program. In C (and many DEC language extensions) this would be a VOLATILE memory location. If you tell the compiler that it is constant, the optimizer may copy the value into a register and use the copy, or do constant folding and substitute the value 0 for the memory reference. Can you say BUG? :-) -------------------------------------- ****************************** Orville R. Weyrich, Jr., Ph.D. Certified Systems Professional Internet: orville%weyrich@uunet.uu.net Weyrich Computer Consulting Voice: (602) 391-0821 POB 5782, Scottsdale, AZ 85261 Fax: (602) 391-0023 (Yes! I'm available) -------------------------------------- ******************************
rharwood@east.pima.edu (06/02/91)
In article <3949@titan.tsd.arlut.utexas.edu>, gardner@titan.tsd.arlut.utexas.edu (Don Gardner) writes: > Is there some way to define a type such that all variables of that > type can be only read or only written? For instance, I have a type that > defines the status register of some peripheral which can only be > read. It would be best if the compiler could flag any assignments > to variables of such a type as errors. > > Is there some other means for commonly handling this problem? The > compiler being used is VADSWorks, if an implementation-defined solution > is required. Well, it's been 2.5 years since I did this, but IMHO "the right way" to do what you are trying is to encapsulate the READ operation in a FUNCTION and the WRITE operation in a one-parameter procedure. Several replies to your message correctly stated that you should "hide the operations in a package," but didn't show how to implement the desired functionality. Naturally, you'd have to change/define types as appropriate, but here's a sample of how I've done it (from memory... don't expect perfect recall!). (Before anyone attacks the "obvious inefficiencies" here, try inserting "PRAGMA INLINE"s for the subprograms and COMPILE it on YOUR compiler, then look at generated assembler code when you CALL them. I believe the compiler we had did almost exactly what the assembler programmer would have done: a direct read from the IO location.) Obviously, you should check the performance issues for YOUR compiler! This is a bit long... but I hope it's helpful. ============================ package spec Package My_Device_Controller is -- -- Your "read-only" register interface: -- -- Define the possible status returned as enumerated: Type Status is (Device_Busy, Device_Idle, Device_Error); -- etc.... -- And attach the appropriate "real" values: For Status use (Device_Busy => 16#01#, -- Define as appropriate Device_Idle => 16#08#, -- for YOUR controller Device_Error => 16#F0#); -- -- Here's the function which provides a "read-only" status value... -- See implementation in package body below. Function device_status return status; -- -- Now, the "write-only" register interface: -- -- Define the possible control values, also enumerated: -- (I'm assuming "read" and "write" commands are concerned with reading/ -- writing device DATA, not CONTROL information.) Type controls is (Write_Command, Read_Command, -- Etc.... add others Status_Command, Reset_Command); -- if needed -- Define the "real values": For Controls use (Write_Command => 16#01#, -- Again, use whatever Read_Command => 16#04#, -- your hardware dudes Status_Command => 16#14#, -- give you (normally Reset_Command => 16#F9#); -- in HEX!) Procedure Device_Command ( command : in controls ); end My_Device_Controller; ============================ package body With Low_Level_IO; -- or maybe you use Memory-Mapped... that's a bit trickier, -- but I've done that too. (Not here, for brevity!) Package Body My_Device_Controller is -- The LRM 14.6 defines Low_Level_IO, but does not explicitly -- define type "device_type"; an exercise for the interested... Status_Register : constant Low_level_IO.Device_type := 16#F001#; Control_Register : constant Low_level_IO.Device_type := 16#F003#; Function device_status return status is results : Low_Level_IO.data_type; begin Low_Level_IO.Receive_Control(Device=> Status_Register, Data => results); Return Results; -- You may need some UNCHECKED_CONVERSION here... end device_status; Procedure Device_Command ( command : in controls ) is begin -- Again, you may need a little CONVERSION... Low_Level_IO.Send_Control(Device=> Control_Register, Data => Command); end Device_Command; end My_Device_Controller; ============================ code usage fragment With My_Device_Controller; Use My_Device_Controller; .. .. Device_Command (Status_command); -- tell the device to report! Case device_Status is When Device_idle => process_whatever_you_want; When Device_busy => dont_do_anything_now; When Device_error => Device_Command(reset_command); -- or whatever! end case; .. .. Device_Command(Write_command); Device_Command(Read_command); ..
orville@weyrich.UUCP (Orville R. Weyrich) (06/03/91)
In article <1991Jun2.095459.1@east.pima.edu> rharwood@east.pima.edu writes: >In article <3949@titan.tsd.arlut.utexas.edu>, >gardner@titan.tsd.arlut.utexas.edu (Don Gardner) writes: > >> Is there some way to define a type such that all variables of that >> type can be only read or only written? For instance, I have a type that >> defines the status register of some peripheral which can only be >> read. It would be best if the compiler could flag any assignments >> to variables of such a type as errors. >> >> Is there some other means for commonly handling this problem? The >> compiler being used is VADSWorks, if an implementation-defined solution >> is required. > >Well, it's been 2.5 years since I did this, but IMHO "the right way" to do what >you are trying is to encapsulate the READ operation in a FUNCTION and the WRITE >operation in a one-parameter procedure. Several replies to your message I have one concern about this approach: What happens if the compiler optimizer is SO CLEVER that it notices a write to a location that is never referenced, and optimizes away the write? Does anyone know of a compiler that is this clever? In any event, you had best check the machine language generated each time your compiler is upgraded ... Is there any issue pending before the 9x committee to address the issue of declaring certain memory locations as VOLATILE? -------------------------------------- ****************************** Orville R. Weyrich, Jr., Ph.D. Certified Systems Professional Internet: orville%weyrich@uunet.uu.net Weyrich Computer Consulting Voice: (602) 391-0821 POB 5782, Scottsdale, AZ 85261 Fax: (602) 391-0023 (Yes! I'm available) -------------------------------------- ******************************
jbg@sei.cmu.edu (John Goodenough) (06/04/91)
In article Re: Types defining h/w r/o or w/o registers of 3 Jun 91 07:57:41 GMT orville@weyrich.UUCP (Orville R. Weyrich) writes: >Is there any issue pending before the 9x committee to address the issue of >declaring certain memory locations as VOLATILE? The following user need and requirement address this issue USER NEED U7.1-A: CONTROL OF SHARED MEMORY Requirement R7.1-A(1) -- Control of Shared Memory If you get access to the Requirements Rationale document and look up "VOLATILE" in the KWIC index, you will be referred directly to this requirement. This document is available electronically on the AJPO host and on the Ada 9X Bulletin Board -- 1-800-Ada9X25 or 301/459-8939. Or contact the Ada 9X Project Office at the address below to receive a hard copy. Chris Anderson Ada 9X Project Manager WL/MNAG Eglin AFB, FL 32542-5434 904/882-8264 e-mail: anderson@uv4.eglin.af.mil FAX: 904/882-2095 John B. Goodenough Goodenough@sei.cmu.edu Software Engineering Institute 412-268-6391
als@bohra.cpg.oz.au (Anthony Shipman) (06/04/91)
In article <1991Jun3.075741.1945@weyrich.UUCP>, orville@weyrich.UUCP (Orville R. Weyrich) writes: ................ > > Is there any issue pending before the 9x committee to address the issue of > declaring certain memory locations as VOLATILE? Wasn't there something in the tasking part of the language for this? Ada's multi-tasking model is shared memory between tasks. I vaguely recall there was a pragma, or something in the machine-dependant chapter, to declare certain memory locations as volatile. Certainly what you have is logically equivalent to multi-tasking except that one of the tasks is implemented in hardware. -- Anthony Shipman "You've got to be taught before it's too late, Computer Power Group Before you are six or seven or eight, 19 Cato St., East Hawthorn, To hate all the people your relatives hate, Melbourne, Australia You've got to be carefully taught." R&H
jbg@sei.cmu.edu (John Goodenough) (06/05/91)
In article Re: Types defining h/w r/o or w/o registers of 4 Jun 91 14:48:21 GMT als@bohra.cpg.oz.au (Anthony Shipman) writes: >Wasn't there something in the tasking part of the language for this? Ada's >multi-tasking model is shared memory between tasks. I vaguely recall there was >a pragma, or something in the machine-dependant chapter, to declare certain >memory locations as volatile. Certainly what you have is logically equivalent >to multi-tasking except that one of the tasks is implemented in hardware. You are talking here about pragma SHARED, which is not suitable for use with volatile data. The Ada 9X study report on shared variables (available from the usual sources) discusses why pragma SHARED doesn't fit the bill. John B. Goodenough Goodenough@sei.cmu.edu Software Engineering Institute 412-268-6391
case@shamash.cdc.com (Steven V. Case) (06/06/91)
> Well, it's been 2.5 years since I did this, but IMHO "the right way" to do > what you are trying is to encapsulate the READ operation in a FUNCTION and the > WRITE operation in a one-parameter procedure. Several replies to your message > correctly stated that you should "hide the operations in a package," but > didn't show how to implement the desired functionality. I was wondering when someone would mention this. I too agree that this is the prefered approach. It is the method that I have used on all of the projects I have worked on which have had a similar requirement. I have read several papers addressing the use of LOW_LEVEL_IO to also serve this purpose. Unfortunately, I have not had the pleasure to use a compiler which provides LOW_LEVEL_IO support. I would be interested in hearing from anyone who has some "real life" experience with LOW_LEVEL_IO, especially if it was used in an embedded, real-time system. Using the READ function and WRITE procedure approach that you mention, the biggest problem that I have run into is not that of inefficient code generated due to the procedure calls. As you pointed out, using PRAGMA INLINE has always been able to take care of it. The problem I have had is with compilers that have been too aggressive in their optimizations. Specifically, most of the systems I have developed for have memory-mapped I/O. On these systems (that I am familiar with) the I/O will only work if a read or write operation is performed at the proper address with the properly "sized" access. In such systems, problems appear when the hardware expects a 32-bit access and one attempts to perform the I/O with something other than a 32-bit access. This is a problem that I have had to work around frequently. Typically I will define a record which describes the content of the I/O register to be accessed. Then if the compiler detects that I am only accessing a portion of the record, it might attempt to "optimize" the access by using an 8-bit or a 16-bit access rather than a 32-bit access as required by the hardware. Since I have been able to deliver these systems, there are obvious ways to work around the "optimizations" if the compiler demonstrates such behavior. They just require me to write code that is not quite what I would have preferred. I would also like to hear of others experience with memory mapped I/O when LOW_LEVEL_IO support is not present. Regards, Steve -- ____ ____ Steve Case - HQG526 email: case@shamash.cdc.com / ___||___ \ Control Data Corporation AT&T : (612) 853-3345 | |___ ___| | 3101 East 80th Street \____||____/ Bloomington, MN 55425
rharwood@east.pima.edu (06/09/91)
In article <33884@shamash.cdc.com>, case@shamash.cdc.com (Steven V. Case) writes: > [from a previous post:] >> Well, it's been 2.5 years since I did this, but IMHO "the right way" to do >> what you are trying is to encapsulate the READ operation in a FUNCTION and the >> WRITE operation in a one-parameter procedure. Several replies to your message >> correctly stated that you should "hide the operations in a package," but >> didn't show how to implement the desired functionality. > I was wondering when someone would mention this. I too agree that this is the > prefered approach. It is the method that I have used on all of the projects > I have worked on which have had a similar requirement. Thanks for the comments to my posting. Sometimes I think some folks would rather argue a philosophical point rather than come to grips with a technical issue and present a real-world solution which many might learn from! > I have read several papers addressing the use of LOW_LEVEL_IO to also serve > this purpose. Unfortunately, I have not had the pleasure to use a compiler > which provides LOW_LEVEL_IO support. I would be interested in hearing from > anyone who has some "real life" experience with LOW_LEVEL_IO, especially if > it was used in an embedded, real-time system. As I mentioned, my "heavy" real-world embedded experience is about 2.5 years old. I used to work for a local office of Dalmo Victor, makers of various radar warning receivers for domestic and foreign Air Forces. If you have any specific questions, I'd be glad to address them either via EMail or on the net. > Using the READ function and WRITE procedure approach that you mention, the > biggest problem that I have run into is not that of inefficient code generated > due to the procedure calls. As you pointed out, using PRAGMA INLINE has always > been able to take care of it. The problem I have had is with compilers that > have been too aggressive in their optimizations. Specifically, most of the > systems I have developed for have memory-mapped I/O. On these systems (that > I am familiar with) the I/O will only work if a read or write operation is > performed at the proper address with the properly "sized" access. Time has muddied the details, but I think we used a compiler flag to turn off ALL optimization in the modules which called LOW_LEVEL_IO. Another possibility which we explored was using "machine code insertions"; basically you "hand code" the desired assembler code into an Ada module, which the compiler faithfully executes, "INLINEs", etc. Most people would say "Just WRITE the darn code in an assembler module", but when the contract says "100% Ada required," you just can't do that. Some contracts even prohibit machine code insertions! > In such systems, problems appear when the hardware expects a 32-bit access > and one attempts to perform the I/O with something other than a 32-bit access. > This is a problem that I have had to work around frequently. Typically I > will define a record which describes the content of the I/O register to be > accessed. Then if the compiler detects that I am only accessing a portion of > the record, it might attempt to "optimize" the access by using an 8-bit or > a 16-bit access rather than a 32-bit access as required by the hardware. Wouldn't the following code fragment work? -- Assume the following definition of the memory-mapped address my_io_address : constant system.address := 16#FFE0#; -- Assume the following definition of a "thing" AT that address -- (i.e., is it 8, 16 or 32 bits of DATA; or could even be a RECORD!) my_io_data : memory_mapped_data; for my_io_data use at my_io_address; Now, "read" references to "my_io_data" equate to a memory-mapped read from address FFE0 hex; same for "writes." Enclose the read and write in functions, which are compiled WITHOUT optimization, but WITH pragma inline, that way the compiler won't optimize: my_io_data := 10; some_other_thing := my_io_data; my_io_data := 12; into some_other_thing := 10; my_io_data := 12; > Since I have been able to deliver these systems, there are obvious ways to > work around the "optimizations" if the compiler demonstrates such behavior. > They just require me to write code that is not quite what I would have > preferred. I would also like to hear of others experience with memory > mapped I/O when LOW_LEVEL_IO support is not present. Again, machine code inserts are choice #1. Another possibility, if your contract allows and the compiler supports (which most do these days), is to code an Ada package spec for io_read and io_write, but write an assembler module for the bodies of the read/write functions. Then have the compiler substitute the assembler object module for the expected Ada object module. Works great on the VAX, but we had a fit making it work on InterACT's 1750A compiler. Ray ----- Ray Harwood |Data Basix |Associate Faculty, Voice: (602)721-1988 |PO Box 18324 | Pima Community College FAX: (602)721-7240 |Tucson, AZ 85731 |Instructor in Ada and Pascal CompuServe: 76645,1370|AppleLink: DATA.BASIX|Internet: rharwood@east.pima.edu