summaryrefslogtreecommitdiffstatshomepage
path: root/docs/pyboard/tutorial/assembler.rst
blob: 1fe2bc4b079a9ea7c60631f5e5257109b979e504 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
.. _pyboard_tutorial_assembler:

Inline assembler
================

Here you will learn how to write inline assembler in MicroPython.

**Note**: this is an advanced tutorial, intended for those who already
know a bit about microcontrollers and assembly language.

MicroPython includes an inline assembler.  It allows you to write
assembly routines as a Python function, and you can call them as you would
a normal Python function.

Returning a value
-----------------

Inline assembler functions are denoted by a special function decorator.
Let's start with the simplest example::

    @micropython.asm_thumb
    def fun():
        movw(r0, 42)

You can enter this in a script or at the REPL.  This function takes no
arguments and returns the number 42.  ``r0`` is a register, and the value
in this register when the function returns is the value that is returned.
MicroPython always interprets the ``r0`` as an integer, and converts it to an
integer object for the caller.

If you run ``print(fun())`` you will see it print out 42.

Accessing peripherals
---------------------

For something a bit more complicated, let's turn on an LED::

    @micropython.asm_thumb
    def led_on():
        movwt(r0, stm.GPIOA)
        movw(r1, 1 << 13)
        strh(r1, [r0, stm.GPIO_BSRRL])

This code uses a few new concepts:

  - ``stm`` is a module which provides a set of constants for easy
    access to the registers of the pyboard's microcontroller.  Try
    running ``import stm`` and then ``help(stm)`` at the REPL.  It will
    give you a list of all the available constants.

  - ``stm.GPIOA`` is the address in memory of the GPIOA peripheral.
    On the pyboard, the red LED is on port A, pin PA13.

  - ``movwt`` moves a 32-bit number into a register.  It is a convenience
    function that turns into 2 thumb instructions: ``movw`` followed by ``movt``.
    The ``movt`` also shifts the immediate value right by 16 bits.

  - ``strh`` stores a half-word (16 bits).  The instruction above stores
    the lower 16-bits of ``r1`` into the memory location ``r0 + stm.GPIO_BSRRL``.
    This has the effect of setting high all those pins on port A for which
    the corresponding bit in ``r0`` is set.  In our example above, the 13th
    bit in ``r0`` is set, so PA13 is pulled high.  This turns on the red LED.

Accepting arguments
-------------------

Inline assembler functions can accept up to 4 arguments.  If they are
used, they must be named ``r0``, ``r1``, ``r2`` and ``r3`` to reflect the registers
and the calling conventions.

Here is a function that adds its arguments::

    @micropython.asm_thumb
    def asm_add(r0, r1):
        add(r0, r0, r1)

This performs the computation ``r0 = r0 + r1``.  Since the result is put
in ``r0``, that is what is returned.  Try ``asm_add(1, 2)``, it should return
3.

Loops
-----

We can assign labels with ``label(my_label)``, and branch to them using
``b(my_label)``, or a conditional branch like ``bgt(my_label)``.

The following example flashes the green LED.  It flashes it ``r0`` times. ::

    @micropython.asm_thumb
    def flash_led(r0):
        # get the GPIOA address in r1
        movwt(r1, stm.GPIOA)

        # get the bit mask for PA14 (the pin LED #2 is on)
        movw(r2, 1 << 14)

        b(loop_entry)

        label(loop1)

        # turn LED on
        strh(r2, [r1, stm.GPIO_BSRRL])

        # delay for a bit
        movwt(r4, 5599900)
        label(delay_on)
        sub(r4, r4, 1)
        cmp(r4, 0)
        bgt(delay_on)

        # turn LED off
        strh(r2, [r1, stm.GPIO_BSRRH])

        # delay for a bit
        movwt(r4, 5599900)
        label(delay_off)
        sub(r4, r4, 1)
        cmp(r4, 0)
        bgt(delay_off)

        # loop r0 times
        sub(r0, r0, 1)
        label(loop_entry)
        cmp(r0, 0)
        bgt(loop1)

Further reading
---------------

For further information about supported instructions of the inline assembler,
see the :ref:`reference documentation <asm_thumb2_index>`.