EmbeddedRelated.com
Forums

Hardware Abstraction

Started by Vladimir Vassilevsky November 27, 2006
Arlet wrote:
> Michael N. Moran wrote: > > > Vladimir Vassilevsky wrote: > > > Hello All, > > > > > > I am looking for a concept for abstracting of a hardware. It is desired > > > that the concept should be convenient, clear, consistent, logical and > > > pretty universal. > > > > I find that when abstracting hardware, like any other abstraction, > > it is useful and important to separate the construction/initialization > > and mode selection from the abstract interface that is used during > > most of the application life cycle. > > > > To use a GPIO pin as an example, a bit-banging application can > > generally perform two operations: > > > > setHIGH() > > setLOW() > > > > The initialization, that selects mutiplexed function routing, > > direction, open-drain operation and other configuration belongs > > to another interface that is most likely not abstract. > > What if I need to dynamically change pin properties ? For instance, > bitbanging an I2C port may involve changing SDA/SCL from input to > output on some architectures. >
For this particular example it is actually extremely nice to abstract the routine. I have used the same 'main I2C' library for the Z80, 8032 and the PIC with calls to five funtions. SDA_LO,SDA_HI,SCL_LO,SCL_HI and SDA_IN. The benefit of doing this is that it was easy to change hardware and only these 5 short functions changed. It also made changing timing requirements simple. (In a lot of cases the call and return added enough delay to eliminate extra delays)
> What if 8 of those GPIO pins are available on the same port, and I need > to access them simultaneously, for instance to access an 8 bit > peripheral ?
I think this would be an even better example of why abstraction would be good. Abstraction could even be a macro in the hardware header.
> What if precise timing is required, for instance bitbanging a UART > peripheral ?
I have been abstracting whole uart routine to the hardware file. For Tx it reads a circular transmit buffer and for Rx it write to one. The 'main' routine just polls the buffer to check for characters.
> > Frankly, I don't see where providing an abstraction for a single GPIO > pin is going to make life easier. There are just too many variations in > hardware capabilities, and application requirements. Even if you could > design a model, it would probably be slow and bloated, and a much > bigger hassle than just accessing the hardware ports directly.
It is a bigger hassle to start with - until a hardware change is needed and then there is only one place you need to look when making the changes. Regards Rocky
Rocky wrote:

> Arlet wrote: > > Michael N. Moran wrote: > > > > > Vladimir Vassilevsky wrote: > > > > Hello All, > > > > > > > > I am looking for a concept for abstracting of a hardware. It is desired > > > > that the concept should be convenient, clear, consistent, logical and > > > > pretty universal. > > > > > > I find that when abstracting hardware, like any other abstraction, > > > it is useful and important to separate the construction/initialization > > > and mode selection from the abstract interface that is used during > > > most of the application life cycle. > > > > > > To use a GPIO pin as an example, a bit-banging application can > > > generally perform two operations: > > > > > > setHIGH() > > > setLOW() > > > > > > The initialization, that selects mutiplexed function routing, > > > direction, open-drain operation and other configuration belongs > > > to another interface that is most likely not abstract. > > > > What if I need to dynamically change pin properties ? For instance, > > bitbanging an I2C port may involve changing SDA/SCL from input to > > output on some architectures. > > > For this particular example it is actually extremely nice to abstract > the routine. I have used the same 'main I2C' library for the Z80, 8032 > and the PIC with calls to five funtions. > SDA_LO,SDA_HI,SCL_LO,SCL_HI and SDA_IN. The benefit of doing this is > that it was easy to change hardware and only these 5 short functions > changed. It also made changing timing requirements simple. (In a lot of > cases the call and return added enough delay to eliminate extra delays) > > > What if 8 of those GPIO pins are available on the same port, and I need > > to access them simultaneously, for instance to access an 8 bit > > peripheral ? > I think this would be an even better example of why abstraction would > be good. Abstraction could even be a macro in the hardware header. > > > What if precise timing is required, for instance bitbanging a UART > > peripheral ? > I have been abstracting whole uart routine to the hardware file. For Tx > it reads a circular transmit buffer and for Rx it write to one. The > 'main' routine just polls the buffer to check for characters.
Sure, but if you have a "uart" abstraction, and an "I2C" abstraction, and you build those directly on the hardware ports, you're not using a GPIO abstraction, which was my point. Abstracting a UART or I2C makes sense. The interface is fairly small, and relatively well-defined. For other interfaces, such as GPIOs, but also timers, it's virtually impossible to define a generic model that can be ported to all kinds of hardware, but still allows you to exploit all the little optimizations and features that the MCU may have.

Rene Tschaggelar wrote:

>>>> I am looking for a concept for abstracting of a hardware. It is >>>> desired that the concept should be convenient, clear, consistent, >>>> logical and pretty universal.
[...]
>> Also, it would be good to minimize the amount of cryptic system >> functions, initializations and dependencies. >> >> > The initialization usually is only a few register > access each.
And it can take weeks of digging through the manuals, trials and errors to do the initialization properly. There is another aspect: when a new guy gets involved into the project, it takes him a month of hard work just to understand the dependencies and know what is already available. Depending on the families, there are
> limitations that cannot be overcome. Especially > since some families were partially made to have > a certain feature enhanced. > Eg the dsPIC2023, with a PWM that operates on a > PLLed 480MHz clock, but only on 5V, at 3.3V, the > PLL is lower, 320MHz. You want to cover this with > a standard PWM ?
Why not. For example, I can describe this feature as a class derived from the basic PWM class.
> I came across such libraries. As soom as you need > a trivial addition, such as power save, part of them > becomes useless.
That's why the concept should allow for the flexible expansion.
> >>> IMO, a HAL is a bad idea in many cases, especially >>> on embedded hardware. How to dump performance on >>> zero advantage. >> >> >> Reinventing a wheel each and every time when starting a new project is >> not a good idea either. > > > Read the manual once, understand how it works and > then copy/paste from project to project.
This is how it is done now, and I can feel more and more that this is not the right way. Vladimir Vassilevsky DSP and Mixed Signal Design Consultant http://www.abvolt.com

Michael N. Moran wrote:

>> I am looking for a concept for abstracting of a hardware. It is >> desired that the concept should be convenient, clear, consistent, >> logical and pretty universal. > > > I find that when abstracting hardware, like any other abstraction, > it is useful and important to separate the construction/initialization > and mode selection from the abstract interface that is used during > most of the application life cycle. > > To use a GPIO pin as an example, a bit-banging application can > generally perform two operations: > > setHIGH() > setLOW()
Actually, there should be a 3-level interface: Application: set_something_meaningful(BOOL param) Hardware abstraction: set_port_pin(BOOL param) Low level: PORTA.1 = param
> The initialization, that selects mutiplexed function routing, > direction, open-drain operation and other configuration belongs > to another interface that is most likely not abstract.
Initialization/deinitialization is a big potential source of errors. The initalizations should be consistent with the application interface.
> > IMHO, operations which are not available on a particular > piece of hardware should not appear in the abstraction > supported by that hardware.
Yes. And the attempt to use the unsupported function should be detected at the compile time. For example, a having the
> ability to read the value of a GPIO at the pin or as > a read-back of the register associated with the pin. > Some GPIO implementations have one or the other and > some have both. Thus there are two possible operations: > > bool readAtPin() > bool readPinRegister() > > A GPIO implementation that supports only one of these > should not have the other available in its interface. > The result is there is no error condition if an application > calls an "unsupported" operation. > > All of this falls under the "Interface Segregation Principle" > The result is several very narrow abstract interfaces rather > than a single abstract interface. Though this suggests MI, > composition is generally better suited for the implementation. >
:) We are growing out of this concept at this moment. Initially it was copy/paste code reuse, then it came to a set of functions and macros, and finally it was realized that a new paradigm is required. Vladimir Vassilevsky DSP and Mixed Signal Design Consultant http://www.abvolt.com
Rocky wrote:

> Vladimir Vassilevsky wrote:
[%X]
>> I am looking for a concept for abstracting of a hardware. It is desired >> that the concept should be convenient, clear, consistent, logical and >> pretty universal.
> I am following this with interest. I try to keep all references to the > hardware in one file or set of files so that even toggling pins becomes > a function call. If speed is needed then the entire function is taken > out of the main code and an API designed for it. While in some case > speed may suffer and then exceptions are made, in general the ease of > porting to different h/w is made much easier. > > By their nature interrupt service routines are in this 'h/w' category. > > If you find / develop a good 'standardised' concept that does not cause > total bloat it would be really great if you could share it here.
I am presuming that most of you already do abstraction of software functions and I would have thought the same sort of thing would be applicable to hardware also. As one who leaves decisions of what is software and what is hardware until quite a bit later in the project than most I have never seen any problem with abstracting the functions in such a way that the decision on hardware/software split can be delayed. I know that the way I do it will not suit everyone out there. My HAL and SAL are in Forth pseudo code. The function names are usually descriptive enough to be able to select some of them from an archived library of functions. One has to remember that none of it is finished code but just a way that, with a little effort (mostly completing the coding work), can lead to testing of design decisions before any scrap of hardware is available. It is akin to building a hardware simulator under the application software. -- ******************************************************************** Paul E. Bennett ....................<email://peb@amleth.demon.co.uk> Forth based HIDECS Consultancy .....<http://www.amleth.demon.co.uk/> Mob: +44 (0)7811-639972 Tel: +44 (0)1235-811095 Going Forth Safely ..... EBA. www.electric-boat-association.org.uk.. ********************************************************************
Michael N. Moran wrote:

> Vladimir Vassilevsky wrote: >
[snip]
> > To use a GPIO pin as an example, a bit-banging application can > generally perform two operations: > > setHIGH() > setLOW()
A matter of the language. There are some that want you write PortA &= ~bit(1<<5); while others allow to define TxEn[@portA,5]:bit; and use it as TxEn:=1; [snip]
> > bool readAtPin() > bool readPinRegister()
Also a matter of the language. The awkward language I'm talking about was never extended to cover controllers too. If the language is simply not readable, a set of API which is made to enhance readability and is basically a macro or a to-be-optimized-away function call is not really efficient. Rene
Rocky wrote:
> Michael N. Moran wrote:
>>IMHO, operations which are not available on a particular >>piece of hardware should not appear in the abstraction >>supported by that hardware. For example, a having the >>ability to read the value of a GPIO at the pin or as >>a read-back of the register associated with the pin. >>Some GPIO implementations have one or the other and >>some have both. Thus there are two possible operations: >> >>bool readAtPin() >>bool readPinRegister() >> >>A GPIO implementation that supports only one of these >>should not have the other available in its interface. >>The result is there is no error condition if an application >>calls an "unsupported" operation.
> > If the higher level s/w requires a readPinRegister would it not be > better to implement it for example by keeping a copy that is written to > RAM?
Better? If it is required, then some lower layer must provide this. Whether or not it should be provided by the lowest layer is another question.
> When using a PIC it is sometimes necessary to do this anyway to prevent > corruption of output pins that change slowly due to loading. One alway > does the BSF or BCF on the RAM copy and then writes that out to the > port.
If I were using a PIC, then it is unlikely that I would do any significant hardware abstraction. I usually work with more "resource full" processors ;-) As for the use of shadow registers, that's a common solution to a hardware ... shortcomming ;-) -- Michael N. Moran (h) 770 516 7918 5009 Old Field Ct. (c) 678 521 5460 Kennesaw, GA, USA 30144 http://mnmoran.org "So often times it happens, that we live our lives in chains and we never even know we have the key." The Eagles, "Already Gone" The Beatles were wrong: 1 & 1 & 1 is 1
Arlet wrote:
> Michael N. Moran wrote:
>>The initialization, that selects mutiplexed function routing, >>direction, open-drain operation and other configuration belongs >>to another interface that is most likely not abstract.
> What if I need to dynamically change pin properties ? For instance, > bitbanging an I2C port may involve changing SDA/SCL from input to > output on some architectures.
If this is required by the upper layer application, and the hardware is incapable of providing the function, then the application can't use the hardware. If one can abstract this functionality in such a way that it can be used by various clients then by all means, create such an abstraction. Though I would not add this to the simpler abstraction, but instead put it in a separate interface.
> What if 8 of those GPIO pins are available on the same port, and I need > to access them simultaneously, for instance to access an 8 bit > peripheral ?
Again, that can be offered via a different abstraction. For example an 8-bit wide port. Not *all* uses, however, can be abstracted. You only wish to abstract the common uses.
> What if precise timing is required, for instance bitbanging a UART > peripheral ?
An engineer does what an engineer has to do :-) There is no *requirement* that the abstraction be used, however, it aids in reusing the code if you can use the abstraction.
> Frankly, I don't see where providing an abstraction for a single GPIO > pin is going to make life easier.
It enables an *application/module* which uses the abstract interface to be reused without changing the application/module. In this particular case, the a "bit banging" application of some sort.
> There are just too many variations in > hardware capabilities, and application requirements. Even if you could > design a model, it would probably be slow and bloated, and a much > bigger hassle than just accessing the hardware ports directly.
It all depends on your goals and constraints. One mans bloat is another mans time saving device. Hassle is a function of experience in the process. I find the process of maintaining broken variants of the same function more ... laborious. ymmv. -- Michael N. Moran (h) 770 516 7918 5009 Old Field Ct. (c) 678 521 5460 Kennesaw, GA, USA 30144 http://mnmoran.org "So often times it happens, that we live our lives in chains and we never even know we have the key." The Eagles, "Already Gone" The Beatles were wrong: 1 & 1 & 1 is 1
Arlet wrote:
> Rocky wrote:
> Sure, but if you have a "uart" abstraction, and an "I2C" abstraction, > and you build those directly on the hardware ports, you're not using a > GPIO abstraction, which was my point.
You certainly *can* have a UART or I2C abstraction that has no dependency on the the underlying implementation. And the implementation of those abstractions may be bit banged, which may use a GPIO abstraction.
> Abstracting a UART or I2C makes sense. The interface is fairly small, > and relatively well-defined.
Again, different parts of a UART may be abstracted to different interfaces, and not *all* UART implementation necessarily need to implement all interfaces. The most obvious abstract interface for a UART is an octet stream interface. UNIX loves this particular abstract interface. The same interface is used for e.g. TCP/IP sockets.
> For other interfaces, such as GPIOs, but also timers, it's virtually > impossible to define a generic model that can be ported to all kinds of > hardware, but still allows you to exploit all the little optimizations > and features that the MCU may have.
When it comes down to optimization, all bets are off. The rule is to optimize when necessary, not *always*. Clearly, however, if your operating at the limits of the hardware, your choices are somewhat limited. -- Michael N. Moran (h) 770 516 7918 5009 Old Field Ct. (c) 678 521 5460 Kennesaw, GA, USA 30144 http://mnmoran.org "So often times it happens, that we live our lives in chains and we never even know we have the key." The Eagles, "Already Gone" The Beatles were wrong: 1 & 1 & 1 is 1
Vladimir Vassilevsky wrote:
> > > Michael N. Moran wrote:
>> To use a GPIO pin as an example, a bit-banging application can >> generally perform two operations: >> >> setHIGH() >> setLOW() > > > Actually, there should be a 3-level interface: > > Application: set_something_meaningful(BOOL param) > Hardware abstraction: set_port_pin(BOOL param) > Low level: PORTA.1 = param
Actually, I don't limit the number of "levels". I generally prefer to have separate operations rather than passing parameters as you've done here. The operation names are more explicit.
>> The initialization, that selects mutiplexed function routing, >> direction, open-drain operation and other configuration belongs >> to another interface that is most likely not abstract. > > > Initialization/deinitialization is a big potential source of errors. The > initalizations should be consistent with the application interface.
Sure it is. However, the portion of the application that initializes a mode of operation is not the same as the part of the application that *uses* the device. Initialization usually only happens infrequently. The part of the application that uses the device in a particular mode after initialization is more portable by depending upon a more abstract narower interface.
>> IMHO, operations which are not available on a particular >> piece of hardware should not appear in the abstraction >> supported by that hardware. > > > Yes. And the attempt to use the unsupported function should be detected > at the compile time.
Yup.
> For example, a having the > >> ability to read the value of a GPIO at the pin or as >> a read-back of the register associated with the pin. >> Some GPIO implementations have one or the other and >> some have both. Thus there are two possible operations: >> >> bool readAtPin() >> bool readPinRegister() >> >> A GPIO implementation that supports only one of these >> should not have the other available in its interface. >> The result is there is no error condition if an application >> calls an "unsupported" operation. >> >> All of this falls under the "Interface Segregation Principle" >> The result is several very narrow abstract interfaces rather >> than a single abstract interface. Though this suggests MI, >> composition is generally better suited for the implementation. >> > > :) We are growing out of this concept at this moment. > Initially it was copy/paste code reuse, then it came to a set of > functions and macros, and finally it was realized that a new paradigm is > required.
You are growing out of what concept? Copy/paste code reuse? Or the "Interface Segregation Principle"? -- Michael N. Moran (h) 770 516 7918 5009 Old Field Ct. (c) 678 521 5460 Kennesaw, GA, USA 30144 http://mnmoran.org "So often times it happens, that we live our lives in chains and we never even know we have the key." The Eagles, "Already Gone" The Beatles were wrong: 1 & 1 & 1 is 1