mynewt-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Sterling Hughes" <>
Subject Changes to HAL/BSP/Drivers
Date Fri, 03 Jun 2016 00:07:24 GMT

I’ve been going through and expanding the NRF51/52 HAL, and there are 
a couple of things I’ve noticed that I think we should talk about.  
I’ll provide a patch for this on the ADC HAL and then slowly work 
through the rest, but I wanted to talk about the changes prior to 
revising this.

But, first, I think it’s worth talking through ideally what the 
layering _should_ look like here.  I’ve found it’s often hard to 
come to a common definition the differences between the BSP, HAL and 
Drivers, and where the separation of concerns should be.  In my view, 
the layout should be:

|            app            |
|          (n)drivers       |
|     HAL     |     BSP     |

Where the role of the HAL is _solely_ to abstract MCU peripheral APIs, 
in a low layer, simple abstraction.

The role of the BSP is to abstract board specific configurations.

The drivers is where you can have diversity, and interaction with the 
rest of the system.  ADC may not be the best example of this, but I’ve 
been looking at it most closely at the moment.

In the case of an ADC, the HAL should be designed to be as portable _and 
low layer_ as possible across the various MCUs that we support.  Every 
chip has a different set of peripherals for managing an ADC, but there 
are a few modes that are quite common across chipsets:

- One shot
- Continuous
- Scan

Beyond that, you can implement continuous and scan pretty easily, on the 
few chipsets that don’t have them.

Implementation of these can be fairly different across chipsets.  
Sometimes you need to use the ADC + comparator, at other times the ADC 
peripheral will do it for you.  However, in most cases these modes can 
be abstracted with minimal code.  There is value in doing this across 
processors, because it allows for portability at higher layers.

However, while the HAL is necessary, it is not sufficient, without 
information that in most cases comes from the board.  Taking the ADC 
example here, how do you convert a reading from the ADC without knowing 
the reference voltage?  I’ll come back to this later.

The role of the BSP is to provide configuration and abstraction of board 
level features.  This includes things like memory and flash layout, as 
well as some form of a pinmux, and abstraction of things like CPU 
frequency, input voltage, etc.

Above both of these is the drivers API.  While in the ADC HAL there is 
only _one_ implementation, for drivers there should be multiple 
implementations, and potentially complex helper functions to make it 
easy to develop against.  So, you might have a very simple/basic driver 
that does blocking ADC reads and uses the HAL only.  You might have a 
more complex driver that can deal with both internal and external ADCs, 
and has chip specific support for doing things like DMA’ing ADC reads 
into a buffer and posting an event to a task every ’n’ samples.  The 
drivers are the ones would should register with the kernel’s power 
management APIs, and manage turning on and off peripherals and external 
chipsets, etc.

And the drivers need not be one-size fit all: it’s beneficial to have 
complexity here in some cases, and simplicity in others, and some times 
you can’t maintain the balance between the two.  It’s the HAL’s 
job to be one-size fit all (or as close as possible), so that we can 
gain abstraction across the diverse of chip peripherals.

OK, on to changes that I think we should make to how things are done 

Right now, we don’t have a drivers interface, and the HAL is 
dual-purposing both.  The ADC is a good example of this, so I’ll focus 
in on that API.

* hal_adc_init() takes a system device id, which is defined by the BSP.

* hal_adc_init() calls bsp_get_hal_adc(), which maps the system device 
id to the correct HAL peripheral

* bsp_get_hal_adc() calls the specific MCU HAL ADC create function (e.g. 
samd21_adc_create()), which creates and returns a hal_adc structure.

* the various hal functions are then implemented, right now these are:
   — hal_adc_read(): blocking read of raw ADC value
   - hal_adc_getrefmv(): Get the reference voltage of the ADC read 
(using BSP functions)

There are some design decisions made here that I’d like to call out:

* The mapping from the “system device IDs” to peripheral IDs in the 
BSP.  This is to abstract the definition across boards of which ADCs to 
map either to an internal peripheral or an external ADC

* The hal_adc_read() function blocks on read.  This is for user 
convenience, but does not represent how the underlying hardware 
implements an ADC read.

* hal_adc_getrefmv() comes from the BSP, which defines these in 
hal_bsp.c.  The samd21 specific implementation of getrefmv() then maps 
these to peripheral specific APIs.

My suggestions on how to change this API, and the general design 
philosophy are as follows:

* A #define should be added HAL_ADC_MAX which indicates the number of 
peripherals enabled on a given board

* hal_adc_init() should be renamed to hal_adc_config() and should 
configure the peripheral provided, which abstracted (cross-platform) 
values for reference voltage, resolution and gain.

* hal_adc_read() should be broken into a set of functions that more 
appropriately map to underlying peripheral operation (e.g. 
hal_adc_read_once(periph_num, callback)).

* There should be BSP APIs that provide necessary information.  For 
example, reference voltage will most likely come from the board 
configuration, so bsp_get_board_voltage() should be provided, as a 
generally implemented function.  This function could then be passed into 
hal_adc_config() as the reference voltage (as an example.)

* Power APIs will be added to the HAL (hal_adc_on(), hal_adc_suspend(), 

* There should be a drivers interface that runs above this, added to the 
base directory of mynewt-core.  Mynewt-core should come with a base set 
of drivers that can use this.  The drivers APIs will be what maps 
internal and external ADCs, provides blocking interfaces, etc.

* A default ADC driver will be developed, which uses the HAL to read 
peripherals, and provides blocking functions.  Over time this will be 
evolved (or broken apart if too complex) to support external ADCs or 
directly support more complex chip features.

* The driver will be responsible for managing power states.

* The mapping of SYSTEM ID -> specific ADC configuration is an exercise 
that is left to the user.  This can be done wherever the drivers are 
created / initialized.  In some cases this will be BSP, in other cases 
it can be done directly by the application.  This can be everything from 
provided a generic sensor API that the drivers plug into, to providing a 
factory function that returns the appropriate driver structure per 

OK, that was a long email.  I’d like people’s thoughts on this — 
so if you’ve made it this far, please chime in. :-)

View raw message