asm_thumb2_hints_tips.rst 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. Hints and tips
  2. ==============
  3. The following are some examples of the use of the inline assembler and some
  4. information on how to work around its limitations. In this document the term
  5. "assembler function" refers to a function declared in Python with the
  6. ``@micropython.asm_thumb`` decorator, whereas "subroutine" refers to assembler
  7. code called from within an assembler function.
  8. Code branches and subroutines
  9. -----------------------------
  10. It is important to appreciate that labels are local to an assembler function.
  11. There is currently no way for a subroutine defined in one function to be called
  12. from another.
  13. To call a subroutine the instruction ``bl(LABEL)`` is issued. This transfers
  14. control to the instruction following the ``label(LABEL)`` directive and stores
  15. the return address in the link register (``lr`` or ``r14``). To return the
  16. instruction ``bx(lr)`` is issued which causes execution to continue with
  17. the instruction following the subroutine call. This mechanism implies that, if
  18. a subroutine is to call another, it must save the link register prior to
  19. the call and restore it before terminating.
  20. The following rather contrived example illustrates a function call. Note that
  21. it's necessary at the start to branch around all subroutine calls: subroutines
  22. end execution with ``bx(lr)`` while the outer function simply "drops off the end"
  23. in the style of Python functions.
  24. ::
  25. @micropython.asm_thumb
  26. def quad(r0):
  27. b(START)
  28. label(DOUBLE)
  29. add(r0, r0, r0)
  30. bx(lr)
  31. label(START)
  32. bl(DOUBLE)
  33. bl(DOUBLE)
  34. print(quad(10))
  35. The following code example demonstrates a nested (recursive) call: the classic
  36. Fibonacci sequence. Here, prior to a recursive call, the link register is saved
  37. along with other registers which the program logic requires to be preserved.
  38. ::
  39. @micropython.asm_thumb
  40. def fib(r0):
  41. b(START)
  42. label(DOFIB)
  43. push({r1, r2, lr})
  44. cmp(r0, 1)
  45. ble(FIBDONE)
  46. sub(r0, 1)
  47. mov(r2, r0) # r2 = n -1
  48. bl(DOFIB)
  49. mov(r1, r0) # r1 = fib(n -1)
  50. sub(r0, r2, 1)
  51. bl(DOFIB) # r0 = fib(n -2)
  52. add(r0, r0, r1)
  53. label(FIBDONE)
  54. pop({r1, r2, lr})
  55. bx(lr)
  56. label(START)
  57. bl(DOFIB)
  58. for n in range(10):
  59. print(fib(n))
  60. Argument passing and return
  61. ---------------------------
  62. The tutorial details the fact that assembler functions can support from zero to
  63. three arguments, which must (if used) be named ``r0``, ``r1`` and ``r2``. When
  64. the code executes the registers will be initialised to those values.
  65. The data types which can be passed in this way are integers and memory
  66. addresses. With current firmware all possible 32 bit values may be passed and
  67. returned. If the return value may have the most significant bit set a Python
  68. type hint should be employed to enable MicroPython to determine whether the
  69. value should be interpreted as a signed or unsigned integer: types are
  70. ``int`` or ``uint``.
  71. ::
  72. @micropython.asm_thumb
  73. def uadd(r0, r1) -> uint:
  74. add(r0, r0, r1)
  75. ``hex(uadd(0x40000000,0x40000000))`` will return 0x80000000, demonstrating the
  76. passing and return of integers where bits 30 and 31 differ.
  77. The limitations on the number of arguments and return values can be overcome by means
  78. of the ``array`` module which enables any number of values of any type to be accessed.
  79. Multiple arguments
  80. ~~~~~~~~~~~~~~~~~~
  81. If a Python array of integers is passed as an argument to an assembler
  82. function, the function will receive the address of a contiguous set of integers.
  83. Thus multiple arguments can be passed as elements of a single array. Similarly a
  84. function can return multiple values by assigning them to array elements.
  85. Assembler functions have no means of determining the length of an array:
  86. this will need to be passed to the function.
  87. This use of arrays can be extended to enable more than three arrays to be used.
  88. This is done using indirection: the ``uctypes`` module supports ``addressof()``
  89. which will return the address of an array passed as its argument. Thus you can
  90. populate an integer array with the addresses of other arrays:
  91. ::
  92. from uctypes import addressof
  93. @micropython.asm_thumb
  94. def getindirect(r0):
  95. ldr(r0, [r0, 0]) # Address of array loaded from passed array
  96. ldr(r0, [r0, 4]) # Return element 1 of indirect array (24)
  97. def testindirect():
  98. a = array.array('i',[23, 24])
  99. b = array.array('i',[0,0])
  100. b[0] = addressof(a)
  101. print(getindirect(b))
  102. Non-integer data types
  103. ~~~~~~~~~~~~~~~~~~~~~~
  104. These may be handled by means of arrays of the appropriate data type. For
  105. example, single precision floating point data may be processed as follows.
  106. This code example takes an array of floats and replaces its contents with
  107. their squares.
  108. ::
  109. from array import array
  110. @micropython.asm_thumb
  111. def square(r0, r1):
  112. label(LOOP)
  113. vldr(s0, [r0, 0])
  114. vmul(s0, s0, s0)
  115. vstr(s0, [r0, 0])
  116. add(r0, 4)
  117. sub(r1, 1)
  118. bgt(LOOP)
  119. a = array('f', (x for x in range(10)))
  120. square(a, len(a))
  121. print(a)
  122. The uctypes module supports the use of data structures beyond simple
  123. arrays. It enables a Python data structure to be mapped onto a bytearray
  124. instance which may then be passed to the assembler function.
  125. Named constants
  126. ---------------
  127. Assembler code may be made more readable and maintainable by using named
  128. constants rather than littering code with numbers. This may be achieved
  129. thus:
  130. ::
  131. MYDATA = const(33)
  132. @micropython.asm_thumb
  133. def foo():
  134. mov(r0, MYDATA)
  135. The const() construct causes MicroPython to replace the variable name
  136. with its value at compile time. If constants are declared in an outer
  137. Python scope they can be shared between multiple assembler functions and
  138. with Python code.
  139. Assembler code as class methods
  140. -------------------------------
  141. MicroPython passes the address of the object instance as the first argument
  142. to class methods. This is normally of little use to an assembler function.
  143. It can be avoided by declaring the function as a static method thus:
  144. ::
  145. class foo:
  146. @staticmethod
  147. @micropython.asm_thumb
  148. def bar(r0):
  149. add(r0, r0, r0)
  150. Use of unsupported instructions
  151. -------------------------------
  152. These can be coded using the data statement as shown below. While
  153. ``push()`` and ``pop()`` are supported the example below illustrates the
  154. principle. The necessary machine code may be found in the ARM v7-M
  155. Architecture Reference Manual. Note that the first argument of data
  156. calls such as
  157. ::
  158. data(2, 0xe92d, 0x0f00) # push r8,r9,r10,r11
  159. indicates that each subsequent argument is a two byte quantity.
  160. Overcoming MicroPython's integer restriction
  161. --------------------------------------------
  162. The Pyboard chip includes a CRC generator. Its use presents a problem in
  163. MicroPython because the returned values cover the full gamut of 32 bit
  164. quantities whereas small integers in MicroPython cannot have differing values
  165. in bits 30 and 31. This limitation is overcome with the following code, which
  166. uses assembler to put the result into an array and Python code to
  167. coerce the result into an arbitrary precision unsigned integer.
  168. ::
  169. from array import array
  170. import stm
  171. def enable_crc():
  172. stm.mem32[stm.RCC + stm.RCC_AHB1ENR] |= 0x1000
  173. def reset_crc():
  174. stm.mem32[stm.CRC+stm.CRC_CR] = 1
  175. @micropython.asm_thumb
  176. def getval(r0, r1):
  177. movwt(r3, stm.CRC + stm.CRC_DR)
  178. str(r1, [r3, 0])
  179. ldr(r2, [r3, 0])
  180. str(r2, [r0, 0])
  181. def getcrc(value):
  182. a = array('i', [0])
  183. getval(a, value)
  184. return a[0] & 0xffffffff # coerce to arbitrary precision
  185. enable_crc()
  186. reset_crc()
  187. for x in range(20):
  188. print(hex(getcrc(0)))