For now this is a scratch space for defining how the additions to the WR code will be structured.
This document describes design ideas of how to design the SFP+ libraries such that they are:
- Compact (code size)
- Modular/intuitively structured
- Simple to use
- Cross-architecture
- (Reusable)
Software modules
There are a number of identifiable software parts. Some of or more of these are part of a hardware abstraction layer.
Section | Name | Usage | Description | Type | Location |
---|---|---|---|---|---|
A | Low level peripheral drivers | Optional | Providers architecture independent I2C and GPIO access. | PERIPERAL DRIVER | ? |
B | Low level component drivers | Optional | Providers component drivers for I2C, EEPROM. | COMPONENT DRIVER | ? |
C | Low level SFP abstraction | Required | Provides a facade for accessing SFP transceivers | COMPLEX DRIVER | ? |
D | High level SFP access | Required | Provides access to specific data-types within the SFP memory space | SERVICE | ? |
E | SFP+ applications | Required | Uses the SFP abstraction for controlling WRPC | APPLICATION | Integrated in WRPC SW PTP Core |
A - Low level peripheral drivers
These drivers provide functions for iterating the I2C peripherals on the
device, and doing I2C transactions.
Since SFP transceivers also have GPIO signals like TX_FAULT and
RX_LOSS, so a GPIO abstraction should also be required.
This layer knows nothing specific about SFP modules. Its just an
I2C/GPIO abstraction. It also knows nothing about other I2C components.
Some OS specifics functions, like the mutex, may also be exposed in this
layer.
The implementation of this layer is architecture dependent. That is, the headers are generic, but most of the code is platform specific.
B - Low level component drivers
This modules would only make sense if we would create a sort of generic hardware abstraction layer on top of module A. This layer could contain drivers for reading EEPROM, various sensors, and controls DACs and ADCs, for example. This is optional since we could do without. I2C access for SFP modules is not that complex.
C - Low level SFP abstraction
This layer provides read/write access to the SFP transceiver, and status access with only minimal exposure to the I2C/GPIO interface specifics. Address spaces can be accessed through generic enumerated type, like:
typdef enum sfp_as_s {
SFP_AS_A0, // Address space 0xA0, address 0-255
SFP_AS_A2_LO,
SFP_AS_A2_PAGE0, // page 0 of address space 0xA2, address 128-255
SFP_AS_A2_PAGE2, // page 2 of address space 0xA2, address 128-255
SFP_AS_A2_PAGE3 // page 3 of address space 0xA2, address 128-255
} sfp_as_t;
// read operation may look like this
status_t sfp_mod_read(sfp_mod_t * mod, sfp_as_t as, uint8_t offset, uint16_t length);
Write operations transparently take the 8 byte pages into account. I.e. a write operation may start at any index in the address space. It does not validate checksums though. When B has been implemented this may call EEPROM read/write operations from module B.
Reading of the SFP IO lines is done by querying the status, like this:
#define SFP_STATUS_PRESENT 0x01
#define SFP_STATUS_RX_LOSS 0x02
#define SFP_STATUS_TX_FAULT 0x04
sfp_mod_status_flags_t sfpmod_status(sfp_mod_t * mod)
When using modules A and B, this layer will not call any platform specific functions, however, it will need to know which GPIO's belong to which I2C ports, and what the generic board architecture is. So, no hardware specific code, but it will contain platform specific configurations.
When not using modules A and B, this module may contain a subset of these inside C.
D - High level SFP access
This module povides generic accessor functions, which must be supplied with a field identifier. Each field identifier must be defined in a header file. An example field identifier may be:
//! SFP temperature, encoded as fixed point 7.8. To get the real temperature divide by 256
#define SFP_FIELD_TEMP SFP_DEF_SCALAR(SFP_AS_A2_LO, SFP_VAL_TEMP_OFFSET, SFP_TYPE_I16)
Which can than be accessed using the following function:
// signed 16 bit integer accessor method.
int16_t sfp_i16_get(sfpmod_t * mod, param_desc_t param);
And invoked like this:
int16_t wl = sfp_i16_get(mod, SFP_FIELD_TEMP);
Note that this may not be very efficient for reading the entire EEPROM, but larger structures can be defined, if these are often read. E.g.
typedef struct vendor_info_s {
char[16] name;
uint8_t _reserved;
uint8_t oui[3];
char[16] part_no;
char[4] vendor_rev;
} vendor_info_t
#define SFP_FIELDS_VENDOR_INFO DEF_SFP_STRUCT(SFP_AS_A0, 20, vendor_info_t)
vecntor_info_t vendor_info;
sfp_struct_get(mod, SFP_FIELDS_VENDOR_INFO, &vendor_info);
The advantage of using generic accessor functions is that it does not need a code path for each field. When using a specific function for each property, i.e. sfp_get_temperature(), each getter and/or setter called will required program space. On the other hand, generic accessors do not have this disadvantage, albeit it be a bit less intuitive.
E - Application
TODO:* Here must be described how the data from the SFP+ layer is used in the WR PTP Core fabric.
Discussion
- It seems that it would be useful to have a WRPC-wide hardware abstraction layer, which hides platform/architecture specific control, such as I2C and GPIO access, but may also be extended with other inter-chip communication protocols such as SPI, etc... It may even provide a subset of OS functions, i.e. the mutex), not provided by the standard library. However the roll-out of such an abstraction will be a huge effort. This HAL would preferably be a submodule of the WRPC SW it self, and not of the SFP library. At first we can of course implement the HAL as a submodule for libsfp.
- .. more?