EmbeddedRelated.com
Forums
Memfault Beyond the Launch

Hardware Abstraction

Started by Vladimir Vassilevsky November 27, 2006
Rene Tschaggelar wrote:
> 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;
That's a part of the implementation, not an abstract interface. Its an implementation detail. Of course, I'm a C++ bigot ;-)
>> >> 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.
As I've said in other posts in this thread, there is a trade-off between abstraction (code reuse) and efficiency. Without sufficient resources (and complexity) it doesn't make sense to try to abstract too much. However, Shift-Right Technologies has done a pretty thorough job using macros in their XMK (eXtreme Minimal Kernel) and other code. I call it "macro-hell" ;-) -- 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
Michael N. Moran wrote:

> > 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.
I find myself doing a lot of uncommon stuff. Besides, in 90% of the time, a specific microcontroller is designed into a solution based on its unique capabilities, so there's only a single hardware platform to worry about.
> > 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.
Sure, if you have a huge application, where all you need to do is turn a LED on or off once in a while. In that case I can just make a led_on() and led_off() function, each with a one line assignment to a GPIO port register. Similarly, I can make motor_on() and motor_off(), and things like that. Put them all in a "io.c" file, which makes it trivial to port later. Adding a separate generic GPIO layer inbetween only makes things harder to design. Sometimes it makes it even harder to understand what exactly is going on. For more uncommon uses of a GPIO port, such as a 3 bit control + 8 bit data interface used to control a LCD display module, I would just make an LCD abstraction, and have it directly access the ports. This would require about 10 lines of h/w specific code, allowing optimal use of the hardware capabilities. Still it would be trivial to port to completely different hardware by just changing those 10 lines. Quite possibly, this LCD abstraction may be the *only* place in the application where I need to write 8 bits in parallel to a GPIO port, which can be implemented by a 1 line statement similar to: PORTA = lcd_data; Which, on some platforms, translates directly to a single special ASM instruction designed to access IO registers. Compare this to the time to design a "8 bit parallel GPIO abstraction", and the effort to port *that* from platform to platform. I agree with you that "it depends" and "ymmv". Abstractions in general make sense, and can make code reuse easier. However, it's easy to get carried away. My motto is "don't abstract until you have more than one case". So, the first time I just make a simple, concrete implementation. If I find it needs to be ported and maintained on multiple hardware platforms, I may decide to rewrite it into something more abstract, based on actual needs.
Arlet wrote:
> Michael N. Moran wrote:
>>>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. > > Sure, if you have a huge application, where all you need to do is turn > a LED on or off once in a while. In that case I can just make a > led_on() and led_off() function, each with a one line assignment to a > GPIO port register. Similarly, I can make motor_on() and motor_off(), > and things like that. Put them all in a "io.c" file, which makes it > trivial to port later.
This is just abstraction at a different layer. This thread is about hardware abstraction, and while LED's and motors are hardware in a more generic sense, hardware abstraction is generally about the peripherals used to control these devices.
> Adding a separate generic GPIO layer inbetween > only makes things harder to design.
We've spent a *lot* of time talking about GPIO pins, which were originally used as an example :-) However, I find that having a good foundation makes the application *easier* to design.
> Sometimes it makes it even harder > to understand what exactly is going on.
True that it may make it more difficult to discover implementation details, but it actually makes the client code application easier to read in the same way that a subroutine makes an application easier to read.
> For more uncommon uses of a GPIO port, such as a 3 bit control + 8 bit > data interface used to control a LCD display module, I would just make > an LCD abstraction, and have it directly access the ports.
Abstractions don't access ports, specific implementations of an abstract interface do. Nit-picky I know ... ;-)
> Quite possibly, this LCD abstraction may be the *only* place in the > application where I need to write 8 bits in parallel to a GPIO port, > which can be implemented by a 1 line statement similar to: > > PORTA = lcd_data; > > Which, on some platforms, translates directly to a single special ASM > instruction designed to access IO registers. > > Compare this to the time to design a "8 bit parallel GPIO abstraction", > and the effort to port *that* from platform to platform.
Sure. I hope I've not given the impression that all things can/should be abstracted.
> I agree with you that "it depends" and "ymmv". Abstractions in general > make sense, and can make code reuse easier. However, it's easy to get > carried away. My motto is "don't abstract until you have more than one > case". So, the first time I just make a simple, concrete > implementation. If I find it needs to be ported and maintained on > multiple hardware platforms, I may decide to rewrite it into something > more abstract, based on actual needs.
Absolutely. However, the more experienced one becomes, the easier it is to recognize and plan for future needs, reducing the need to re-write. -- 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
Michael N. Moran wrote:

> > Sure, if you have a huge application, where all you need to do is turn > > a LED on or off once in a while. In that case I can just make a > > led_on() and led_off() function, each with a one line assignment to a > > GPIO port register. Similarly, I can make motor_on() and motor_off(), > > and things like that. Put them all in a "io.c" file, which makes it > > trivial to port later. > > This is just abstraction at a different layer. This thread is > about hardware abstraction, and while LED's and motors are > hardware in a more generic sense, hardware abstraction is > generally about the peripherals used to control these devices.
True. But my point is that I find it more productive to put the abstractions at a slightly higher layer rather than trying to abstract the low-level peripherals typically found on microcontrollers. If you want a true peripheral abstraction, you'll be restricted to a common subset of all controllers, which will be very small. If you're going to extend your abstraction with controller specific features where they are supported, and you use those in your application, you'll still have to rewrite your application when porting to less capable hardware. If you put the abstraction on a more functional level, such as an LCD display driver, you know the hardware has proper support (otherwise you wouldn't have picked it for a design), so porting the code is at least possible, and usually not that difficult.
> > Adding a separate generic GPIO layer inbetween > > only makes things harder to design. > > We've spent a *lot* of time talking about GPIO pins, which were > originally used as an example :-) However, I find that having > a good foundation makes the application *easier* to design.
Agreed, it's just an example, but for me it's a good example of what not to abstract.
> > I agree with you that "it depends" and "ymmv". Abstractions in general > > make sense, and can make code reuse easier. However, it's easy to get > > carried away. My motto is "don't abstract until you have more than one > > case". So, the first time I just make a simple, concrete > > implementation. If I find it needs to be ported and maintained on > > multiple hardware platforms, I may decide to rewrite it into something > > more abstract, based on actual needs. > > Absolutely. However, the more experienced one becomes, the easier > it is to recognize and plan for future needs, reducing the need > to re-write.
In my case, it's been the opposite. :) Coming from an academic CS background, I had been taught to build abstractions, and that's what I tried. After some years of real life experience, I discovered that using fewer of them actually saved time, and made the code more readable, as well as more efficient.
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. > > My goal is developing more or less universal HAL and application > framework for automotive, control and instrumental. It should not be > tied to a particular OS, CPU or board. > > Can you recommend a good reading on that. I am not very interested in > the specifics of a particular OS or board, but in the good concepts and > ideas. > > > Vladimir Vassilevsky > > DSP and Mixed Signal Design Consultant > > http://www.abvolt.com
Interesting. It's amazing how often the same kinds of problems crop in embedded work. The underlying hardware may change, but the device functionality remains the same. Have been trying to develop portable embedded libraries for several years, with some success, though there is much still to do. What seems to work for me is: First and formost, take a system global view of the design. That is, think of it a series of layers, with `applications' at the top, protocols etc in the middle and hardware drivers at the bottom. Make all layers communicate through defined interfaces, with no globals, all data passing through the layers. Keep all low level hardware related code to an absolute minimum and blackbox / isolate all such code and associated data into dedicated modules with no external access other than through a defined call interface. Keep all data tranfer at this level transparent. That is, raw bits and bytes. Strictly limit functionality at low level. Decide on a minimal set of access functions for a device class and build the more complex functionality into higher system layers. If you do it right, when the hardware changes, you find that you only need to rewrite small slips of hardware specific code to get the whole system up again. Write strict ansi C, with no clever stuff, perhaps even a subset, to ensure portability. Stick all tested code modules into function class based libraries, to grow a tested, robust code base, so you don't continually have to reinvent the wheel for every new project. None of this is new - it's based on the model used for many desktop and server os's, but it's just as applicable to small embedded designs. It does require more effort and some say that the runtime overhead is unacceptable, but even on 8051 class devices, it's never been a problen here. Chris
Arlet wrote:
> Michael N. Moran wrote:
>> This is just abstraction at a different layer. This thread is about >> hardware abstraction, and while LED's and motors are hardware in a >> more generic sense, hardware abstraction is generally about the >> peripherals used to control these devices. > > > True. But my point is that I find it more productive to put the > abstractions at a slightly higher layer rather than trying to > abstract the low-level peripherals typically found on > microcontrollers.
Abstractions are driven by the needs of the upper layers and independent of their implementation. In this case, bit-banged I/O applications are quite common and can be quite independent of the specific hardware implementation. For example, a bit-banged I2C bus driver doesn't care about *how* the port is configured, how the I/O pins are read or written, or which pins are used. The driver simply wants to be given a reference to the two pins it needs (SDA + SCL). The *driver* then can be reused on any hardware platform that implements the abstract pin interface, and implementing that interface for a new platform is "trivial".
> If you want a true peripheral abstraction, you'll be restricted to a > common subset of all controllers, which will be very small. If you're > going to extend your abstraction with controller specific features > where they are supported, and you use those in your application, > you'll still have to rewrite your application when porting to less > capable hardware.
I don't consider this an abstraction. Rather, I consider this as writing a low-level driver. The idea being that a particular peripheral driver can be re-used on another micro-controller/SOC that uses the *same* peripheral. In this case, we simply have a software representation of the hardware function without any abstraction involved. However, it is possible to add an implementation of a common abstraction to such a driver.
>> Absolutely. However, the more experienced one becomes, the easier >> it is to recognize and plan for future needs, reducing the need to >> re-write. > > In my case, it's been the opposite. :) Coming from an academic CS > background, I had been taught to build abstractions, and that's what > I tried. After some years of real life experience, I discovered that > using fewer of them actually saved time, and made the code more > readable, as well as more efficient.
In my experience, many/most embedded programmers (such as myself) are hardware guys that learned about software from the bottom/assembler up. Many of us had no formal training in CS. I find that having good CS skills (and even knowing what abstraction *is*) is rare amongst our breed. CS majors get more exposure to techniques for managing the complexity of large software projects. However, they are not exposed to many low level issues that are common in embedded programming, and often learn techniques (playing fast and loose with dynamic memory) that are difficult to un-learn because they are so endemic and easy-to-use. That's what I love about this embedded game ... there's always something new to learn and there's sooo much room for improvement :-) -- 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

Memfault Beyond the Launch