summaryrefslogtreecommitdiffstatshomepage
path: root/docs/library
diff options
context:
space:
mode:
authorPaul Sokolovsky <pfalcon@users.sourceforge.net>2016-06-09 05:02:55 +0300
committerPaul Sokolovsky <pfalcon@users.sourceforge.net>2016-06-09 05:02:55 +0300
commit343b5c1081331c2c06bda7d7add67da44ae47fea (patch)
treeb335b36d47f06cef2a7109b59fef52bbd8eafc58 /docs/library
parent79b40d11276b2b70b2eaa2fe0cac4d3d830c19e4 (diff)
downloadmicropython-343b5c1081331c2c06bda7d7add67da44ae47fea.tar.gz
micropython-343b5c1081331c2c06bda7d7add67da44ae47fea.zip
docs/uctypes: Improve documentation.
Seealso and Limitations sectiosn added, better formatting and grammar.
Diffstat (limited to 'docs/library')
-rw-r--r--docs/library/uctypes.rst166
1 files changed, 99 insertions, 67 deletions
diff --git a/docs/library/uctypes.rst b/docs/library/uctypes.rst
index 9a7f85edcd..630a3a36fb 100644
--- a/docs/library/uctypes.rst
+++ b/docs/library/uctypes.rst
@@ -1,97 +1,106 @@
-:mod:`uctypes` -- access C structures
-=====================================
+:mod:`uctypes` -- access binary data in a structured way
+========================================================
.. module:: uctypes
- :synopsis: access C structures
+ :synopsis: access binary data in a structured way
This module implements "foreign data interface" for MicroPython. The idea
-behind it is similar to CPython's ``ctypes`` modules, but actual API is
-different, streamlined and optimized for small size.
+behind it is similar to CPython's ``ctypes`` modules, but the actual API is
+different, streamlined and optimized for small size. The basic idea of the
+module is to define data structure layout with about the same power as the
+C language allows, and the access it using familiar dot-syntax to reference
+sub-fields.
+
+.. seealso::
+
+ Module :mod:`ustruct`
+ Standard Python way to access binary data structures (doesn't scale
+ well to large and complex structures).
Defining structure layout
-------------------------
Structure layout is defined by a "descriptor" - a Python dictionary which
encodes field names as keys and other properties required to access them as
-an associated values. Currently, uctypes requires explicit specification of
-offsets for each field. Offset are given in bytes from structure start.
+associated values. Currently, uctypes requires explicit specification of
+offsets for each field. Offset are given in bytes from a structure start.
Following are encoding examples for various field types:
- Scalar types::
+* Scalar types::
"field_name": uctypes.UINT32 | 0
- in other words, value is scalar type identifier ORed with field offset
- (in bytes) from the start of the structure.
+ in other words, value is scalar type identifier ORed with field offset
+ (in bytes) from the start of the structure.
- Recursive structures::
+* Recursive structures::
"sub": (2, {
"b0": uctypes.UINT8 | 0,
"b1": uctypes.UINT8 | 1,
})
- i.e. value is a 2-tuple, first element of which is offset, and second is
- a structure descriptor dictionary (note: offsets in recursive descriptors
- are relative to a structure it defines).
+ i.e. value is a 2-tuple, first element of which is offset, and second is
+ a structure descriptor dictionary (note: offsets in recursive descriptors
+ are relative to a structure it defines).
- Arrays of primitive types::
+* Arrays of primitive types::
"arr": (uctypes.ARRAY | 0, uctypes.UINT8 | 2),
- i.e. value is a 2-tuple, first element of which is ARRAY flag ORed
- with offset, and second is scalar element type ORed number of elements
- in array.
+ i.e. value is a 2-tuple, first element of which is ARRAY flag ORed
+ with offset, and second is scalar element type ORed number of elements
+ in array.
- Arrays of aggregate types::
+* Arrays of aggregate types::
"arr2": (uctypes.ARRAY | 0, 2, {"b": uctypes.UINT8 | 0}),
- i.e. value is a 3-tuple, first element of which is ARRAY flag ORed
- with offset, second is a number of elements in array, and third is
- descriptor of element type.
+ i.e. value is a 3-tuple, first element of which is ARRAY flag ORed
+ with offset, second is a number of elements in array, and third is
+ descriptor of element type.
- Pointer to a primitive type::
+* Pointer to a primitive type::
"ptr": (uctypes.PTR | 0, uctypes.UINT8),
- i.e. value is a 2-tuple, first element of which is PTR flag ORed
- with offset, and second is scalar element type.
+ i.e. value is a 2-tuple, first element of which is PTR flag ORed
+ with offset, and second is scalar element type.
- Pointer to aggregate type::
+* Pointer to an aggregate type::
"ptr2": (uctypes.PTR | 0, {"b": uctypes.UINT8 | 0}),
- i.e. value is a 2-tuple, first element of which is PTR flag ORed
- with offset, second is descriptor of type pointed to.
+ i.e. value is a 2-tuple, first element of which is PTR flag ORed
+ with offset, second is descriptor of type pointed to.
- Bitfields::
+* Bitfields::
"bitf0": uctypes.BFUINT16 | 0 | 0 << uctypes.BF_POS | 8 << uctypes.BF_LEN,
- i.e. value is type of scalar value containing given bitfield (typenames are
- similar to scalar types, but prefixes with "BF"), ORed with offset for
- scalar value containing the bitfield, and further ORed with values for
- bit offset and bit length of the bitfield within scalar value, shifted by
- BF_POS and BF_LEN positions, respectively. Bitfield position is counted
- from the least significant bit, and is the number of right-most bit of a
- field (in other words, it's a number of bits a scalar needs to be shifted
- right to extra the bitfield).
-
- In the example above, first UINT16 value will be extracted at offset 0
- (this detail may be important when accessing hardware registers, where
- particular access size and alignment are required), and then bitfield
- whose rightmost bit is least-significant bit of this UINT16, and length
- is 8 bits, will be extracted - effectively, this will access
- least-significant byte of UINT16.
-
- Note that bitfield operations are independent of target byte endianness,
- in particular, example above will access least-significant byte of UINT16
- in both little- and big-endian structures. But it depends on the least
- significant bit being numbered 0. Some targets may use different
- numbering in their native ABI, but ``uctypes`` always uses normalized
- numbering described above.
+ i.e. value is type of scalar value containing given bitfield (typenames are
+ similar to scalar types, but prefixes with "BF"), ORed with offset for
+ scalar value containing the bitfield, and further ORed with values for
+ bit offset and bit length of the bitfield within scalar value, shifted by
+ BF_POS and BF_LEN positions, respectively. Bitfield position is counted
+ from the least significant bit, and is the number of right-most bit of a
+ field (in other words, it's a number of bits a scalar needs to be shifted
+ right to extra the bitfield).
+
+ In the example above, first UINT16 value will be extracted at offset 0
+ (this detail may be important when accessing hardware registers, where
+ particular access size and alignment are required), and then bitfield
+ whose rightmost bit is least-significant bit of this UINT16, and length
+ is 8 bits, will be extracted - effectively, this will access
+ least-significant byte of UINT16.
+
+ Note that bitfield operations are independent of target byte endianness,
+ in particular, example above will access least-significant byte of UINT16
+ in both little- and big-endian structures. But it depends on the least
+ significant bit being numbered 0. Some targets may use different
+ numbering in their native ABI, but ``uctypes`` always uses normalized
+ numbering described above.
Module contents
---------------
@@ -103,17 +112,18 @@ Module contents
.. data:: LITTLE_ENDIAN
- Little-endian packed structure. (Packed means that every field occupies
- exactly as many bytes as defined in the descriptor, i.e. alignment is 1).
+ Layout type for a little-endian packed structure. (Packed means that every
+ field occupies exactly as many bytes as defined in the descriptor, i.e.
+ the alignment is 1).
.. data:: BIG_ENDIAN
- Big-endian packed structure.
+ Layour type for a big-endian packed structure.
.. data:: NATIVE
- Native structure - with data endianness and alignment conforming to
- the ABI of the system on which MicroPython runs.
+ Layout type for a native structure - with data endianness and alignment
+ conforming to the ABI of the system on which MicroPython runs.
.. function:: sizeof(struct)
@@ -145,33 +155,55 @@ Structure descriptors and instantiating structure objects
Given a structure descriptor dictionary and its layout type, you can
instantiate a specific structure instance at a given memory address
-using uctypes.struct() constructor. Memory address usually comes from
+using :class:`uctypes.struct()` constructor. Memory address usually comes from
following sources:
* Predefined address, when accessing hardware registers on a baremetal
system. Lookup these addresses in datasheet for a particular MCU/SoC.
-* As return value from a call to some FFI (Foreign Function Interface)
+* As a return value from a call to some FFI (Foreign Function Interface)
function.
-* From uctypes.addressof(), when you want to pass arguments to FFI
+* From uctypes.addressof(), when you want to pass arguments to an FFI
function, or alternatively, to access some data for I/O (for example,
- data read from file or network socket).
+ data read from a file or network socket).
Structure objects
-----------------
Structure objects allow accessing individual fields using standard dot
-notation: ``my_struct.field1``. If a field is of scalar type, getting
-it will produce primitive value (Python integer or float) corresponding
-to value contained in a field. Scalar field can also be assigned to.
+notation: ``my_struct.substruct1.field1``. If a field is of scalar type,
+getting it will produce a primitive value (Python integer or float)
+corresponding to the value contained in a field. A scalar field can also
+be assigned to.
If a field is an array, its individual elements can be accessed with
-standard subscript operator - both read and assigned to.
+the standard subscript operator ``[]`` - both read and assigned to.
If a field is a pointer, it can be dereferenced using ``[0]`` syntax
(corresponding to C ``*`` operator, though ``[0]`` works in C too).
-Subscripting pointer with other integer values but 0 are supported too,
+Subscripting a pointer with other integer values but 0 are supported too,
with the same semantics as in C.
Summing up, accessing structure fields generally follows C syntax,
-except for pointer derefence, you need to use ``[0]`` operator instead
-of ``*``.
+except for pointer derefence, when you need to use ``[0]`` operator
+instead of ``*``.
+
+Limitations
+-----------
+
+Accessing non-scalar fields leads to allocation of intermediate objects
+to represent them. This means that special care should be taken to
+layout a structure which needs to be accessed when memory allocation
+is disabled (e.g. from an interrupt). The recommendations are:
+
+* Avoid nested structures. For example, instead of
+ ``mcu_registers.peripheral_a.register1``, define separate layout
+ descriptors for each peripheral, to be accessed as
+ ``peripheral_a.register1``.
+* Avoid other non-scalar data, like array. For example, instead of
+ ``peripheral_a.register[0]`` use ``peripheral_a.register0``.
+
+Note that these recommendations will lead to decreased readability
+and conciseness of layouts, so they should be used only if the need
+to access structure fields without allocation is anticipated (it's
+even possible to define 2 parallel layouts - one for normal usage,
+and a restricted one to use when memory allocation is prohibited).