InLine Assembler ARM M0+: Unterschied zwischen den Versionen
Peter (Diskussion | Beiträge) |
Peter (Diskussion | Beiträge) |
||
| Zeile 96: | Zeile 96: | ||
== Das ARM Cortex M0+ Speichermodell== | == Das ARM Cortex M0+ Speichermodell== | ||
{|cellspacing="0" border="1" | |||
|+ <h3>Memory Map</h3> | |||
! Adresse !! Verwendung | |||
|- align="center" | |||
| 0xFFFFFFFF ||rowspan="3"| System | |||
|- align="center" | |||
| _ | |||
|- align="center" | |||
| 0xE0100000 | |||
|- align="center" | |||
| 0xE00FFFFF ||rowspan="2"| Private Peripheral Bus | |||
|- align="center" | |||
| 0xE0000000 | |||
|- align="center" | |||
| 0xDFFFFFFF||rowspan="4"| External Device <br>1GB | |||
|- align="center" | |||
| _ | |||
|- align="center" | |||
| _ | |||
|- align="center" | |||
| 0xA0000000 | |||
|- align="center" | |||
| 0x9FFFFFFF ||rowspan="4"| External RAM <br>1GB | |||
|- align="center" | |||
| _ | |||
|- align="center" | |||
| _ | |||
|- align="center" | |||
| 0x60000000 | |||
|- align="center" | |||
| 0x5FFFFFFF ||rowspan="2"| Peripherals <br>0.5GB | |||
|- align="center" | |||
| 0x40000000 | |||
|- align="center" | |||
| 0x3FFFFFFF ||rowspan="2"| SRAM <br>0.5GB | |||
|- align="center" | |||
| 0x20000000 | |||
|- align="center" | |||
| 0x1FFFFFFFF ||rowspan="2"| Code <br>0.5GB | |||
|- align="center" | |||
| 0x00000000 | |||
|} | |||
== Der Befehlssatz des M0+== | == Der Befehlssatz des M0+== | ||
Version vom 15. Juli 2024, 21:32 Uhr
Micropython bietet die Möglichkeit InLine Assembler für ARM Cortex M CPU's direkt ins Script zu schreiben. Hier wird dieses Thema am Raspberry Pi Pico erarbeitet.
Bei Thonny funktioniert der Inline Assembler nicht in der REPL! Assembler Code muss in einem Script stehen!
Die CPU des Pico
Der Raspberry Pi Pico enthält 2 ARM Cortex M0+ CPU's.
Für die beiden M0+ CPU's gilt das, was ARM in die M0+ CPU's eingebaut hat. Deshalb wird zuerst das Wichtigste zum M0+ vorgestellt. Dabei wird aber z.T. auch auf Besonderheiten des Micropython Inline Assemblers eingegangen.
Anschließend wird der Micropython Inline Assembler für den M0+, also den Raspberry Pi Pico besprochen.
Schließlich wird auf die Raspberry Pi Pico Besonderheiten eingegangen und einige kleine Programmbeispiele präsentiert.
Das ARM Cortex M0+ Prozessormodell
Der ARM Cortex M0+ besitzt 16 allgemeine Register und 3 Spezialregister.
| REGISTER | Bezeichnung |
|---|---|
| r0 | Low Register
können von allen Befehlen verwendet werden.
|
| r1 | |
| r2 | |
| r3 | |
| r4 | |
| r5 | |
| r6 | |
| r7 | |
| r8 | High Register
können nur von 32-Bit-Befehlen verwendet werden.
|
| r9 | |
| r10 | |
| r11 | |
| r12 | |
| r13 | Stack Pointer (MSP/PSP) |
| r14 | Link Register (LR) |
| r15 | Programm Counter (PC) |
| REGISTER | Bezeichnung |
|---|---|
| xPSR | Kombiniertes Programm Status Register |
| PRIMASK | Interrupt Maskierung Spezialregister |
| CONTROL | Control Spezialregister |
Hier ist nur das xPSR von interesse. Es enthält u.a. das Application Prozessor Status Register (APSR). Dieses wird für bedingte Sprünge benötigt.
| Bit | Flag |
|---|---|
| 31 | N |
| 30 | Z |
| 29 | C |
| 28 | V |
Hier ist nur das Flag und dessen Bedeutung für die Assemblerprogrammierung interessant:
- N
- Wird 1 wenn das Ergebnis negativ ist.
- Z
- Wird 1 wenn das Ergebnis 0 ist.
- C
- Wird bei unsigned Werten 1, wenn ein Überlauf entsteht.
Bei Shift- und Rotate-Befehlen wird ein Bit aus dem Register ins Carry geschoben. - V
- Wird bei signed Werten 1, wenn ein Überlauf entsteht.
Das ARM Cortex M0+ Speichermodell
| Adresse | Verwendung |
|---|---|
| 0xFFFFFFFF | System |
| _ | |
| 0xE0100000 | |
| 0xE00FFFFF | Private Peripheral Bus |
| 0xE0000000 | |
| 0xDFFFFFFF | External Device 1GB |
| _ | |
| _ | |
| 0xA0000000 | |
| 0x9FFFFFFF | External RAM 1GB |
| _ | |
| _ | |
| 0x60000000 | |
| 0x5FFFFFFF | Peripherals 0.5GB |
| 0x40000000 | |
| 0x3FFFFFFF | SRAM 0.5GB |
| 0x20000000 | |
| 0x1FFFFFFFF | Code 0.5GB |
| 0x00000000 |
Der Befehlssatz des M0+
Key features of the Cortex-M0+ core are:[18]
ARMv6-M architecture[14]
2-stage pipeline (one fewer than Cortex-M0)
Instruction sets: (same as Cortex-M0)
Thumb-1 (most), missing CBZ, CBNZ, IT
Thumb-2 (some), only BL, DMB, DSB, ISB, MRS, MSR
32-bit hardware integer multiply with 32-bit result
1 to 32 interrupts, plus NMI
Quelle: https://en.wikipedia.org/wiki/ARM_Cortex-M
Hier habe ich den Befehlssatz des Pico, so wie er in dessen Datenblatt aufgeführt ist eingetragen.
Es zeigte sich jedoch, dass der Micropython Inline Assembler diesen Befehlssatz nur eingeschränkt unterstützt.
Die im Datenblatt aufgeführten Befehle, die nicht unterstützt werden habe ich durchgestrichen. Die meist einfacheren Befehle, die stattdessen funktionieren und nicht im Datenblatt aufgeführt sind habe ich grün dargestellt.
Die Zahl hinter der Beschreibung entspricht der Anzahl der Takte die dieser Befehl dauert.
Move
Daten zwischen Registern verschieben.
- MOVS Rd, #<imm>
- 8-bit immediate 1
- MOVS Rd, Rm
- Lo to Lo 1
- MOV Rd, Rm
- Any to Any 1
- MOV PC, Rm
- Any to PC 2
- MOV Rd, #<imm>
- 8-bit immediate 1
Add
- ADDS Rd, Rn, #<imm>
- 3-bit immediate 1
- ADDS Rd, Rn, Rm
- All registers Lo 1
- ADD Rd, Rd, Rm
- Any to Any 1
- ADD PC, PC, Rm
- Any to PC 2
- ADDS Rd, Rd, #<imm>
- 8-bit immediate 1
- ADCS Rd, Rd, Rm
- With carry 1
- ADD SP, SP, #<imm>
- Immediate to SP 1
- ADD Rd, SP, #<imm>
- Form address from SP 1
- ADR Rd, <label>
- Form address from PC 1
Subtract
- SUBS Rd, Rn, Rm
- Lo and Lo 1
- SUBS Rd, Rn, #<imm>
- 3-bit immediate 1
- SUBS Rd, Rd, #<imm>
- 8-bit immediate 1
- SBCS Rd, Rd, Rm
- With carry 1
- SUB SP, SP, #<imm>
- Immediate from SP 1
- RSBS Rd, Rn, #0
- Negate 1
Multiply
- MULS Rd, Rm, Rd
- Multiply 1
Compare
- CMP Rn, Rm
- Compare 1
- CMN Rn, Rm
- Negative 1
- CMP Rn, #<imm>
- Immediate 1
Logical
- ANDS Rd, Rd, Rm
- AND 1
- EORS Rd, Rd, Rm
- Exclusive OR 1
- ORRS Rd, Rd, Rm
- OR 1
- BICS Rd, Rd, Rm
- Bit clear 1
- MVNS Rd, Rm
- Move NOT 1
- TST Rn, Rm
- AND test 1
Shift
- LSL Rd, Rm
- Rd << Rm
- LSR Rd, Rm
- Rd >> Rm
- ASR Rd, Rm
- Rd >> Rm
LSL mit festen Werten und LSLS mit 3 Argumenten wird nicht unterstützt!
- LSLS Rd, Rm, #<shift>
- Logical shift left by immediate 1
- LSLS Rd, Rd, Rs
- Logical shift left by register 1
- LSRS Rd, Rm, #<shift>
- Logical shift right by immediate 1
- LSRS Rd, Rd, Rs
- Logical shift right by register 1
- ASRS Rd, Rm, #<shift>
- Arithmetic shift right 1
- ASRS Rd, Rd, Rs
- Arithmetic shift right by register 1
Rotate
- ROR Rd, Rs
- rechts rotieren
- RORS Rd, Rd, Rs
- Rotate right by register 1
Load
- LDR Rd, [Rn, #<imm>]
- Word, immediate offset 2 or 1a
- LDRH Rd, [Rn, #<imm>]
- Halfword, immediate offset 2 or 1a
- LDRB Rd, [Rn, #<imm>]
- Byte, immediate offset 2 or 1a
- LDR Rd, [Rn, Rm]
- Word, register offset 2 or 1a
- LDRH Rd, [Rn, Rm]
- Halfword, register offset 2 or 1a
- LDRSH Rd, [Rn, Rm]
- Signed halfword, register offset 2 or 1a
- LDRB Rd, [Rn, Rm]
- Byte, register offset 2 or 1a
- LDRSB Rd, [Rn, Rm]
- Signed byte, register offset 2 or 1a
- LDR Rd, <label>
- PC-relative 2 or 1a
- LDR Rd, [SP, #<imm>]
- SP-relative 2 or 1a
- LDM Rn!, {<loreglist>}
- Multiple, excluding base 1+Nb
- LDM Rn, {<loreglist>}
- Multiple, including base 1+Nb
Store
- STR Rd, [Rn, #<imm>]
- Word, immediate offset 2 or 1a
- STRH Rd, [Rn, #<imm>]
- Halfword, immediate offset 2 or 1a
- STRB Rd, [Rn, #<imm>]
- Byte, immediate offset 2 or 1a
- STR Rd, [Rn, Rm]
- Word, register offset 2 or 1
- STRH Rd, [Rn, Rm]
- Halfword, register offset 2 or 1a
- STRB Rd, [Rn, Rm]
- Byte, register offset 2 or 1a
- STR Rd, [SP, #<imm>]
- SP-relative 2 or 1a
- STM Rn!, {<loreglist>}
- Multiple 1+Nb
Push
- PUSH {<loreglist>}
- Push 1+Nb - nur r0 ... r7
- PUSH {<loreglist>, LR}
- Push with link register 1+Nc
Pop
- POP {<loreglist>}
- Pop 1+Nb
- POP {<loreglist>, PC}
- Pop and return 3+Nc
Branch
- B<cc> <label>
- Conditional 1 or 2d
- B <label>
- Unconditional 2
- BL <label>
- With link 3
- BX Rm
- With exchange 2
- BLX Rm
- With link and exchange 2
Extend
- SXTH Rd, Rm
- Signed halfword to word 1
- SXTB Rd, Rm
- Signed byte to word 1
- UXTH Rd, Rm
- Unsigned halfword 1
- UXTB Rd, Rm
- Unsigned byte 1
Reverse
- REV Rd, Rm
- Bytes in word 1
- REV16 Rd, Rm
- Bytes in both halfwords 1
- REVSH Rd, Rm
- Signed bottom half word 1
State
- SVC #<imm>
- change Supervisor Call - e
- CPSID i
- Disable interrupts 1
- CPSIE i
- Enable interrupts 1
- MRS Rd, <specreg>
- Read special register 3
- MSR <specreg>, Rn
- Write special register 3
- BKPT #<imm>
- Breakpoint - e
Hint
- SEV
- Send-Event 1
- WFE
- Wait For Event 2f
- WFI
- Wait For Interrupt 2f
- YIELD
- Yield 1f
- NOP
- No operation 1
Barriers
- ISB
- Instruction synchronization 3
- DMB
- Data memory 3
- DSB
- Data synchronization 3
Table Notes
- a
- 2 if to AHB interface or SCS, 1 if to single-cycle I/O port.
- b
- N is the number of elements in the list.
- c
- N is the number of elements in the list including PC or LR.
- d
- 2 if taken, 1 if not-taken.
- e
- Cycle count depends on processor and debug configuration.
- f
- Excludes time spent waiting for an interrupt or event.
- g
- Executes as NOP
Einschränkungen des Micropython Inline Assembler
Es gibt keine Befehl um ein Register mit einer 32-Bit Zahl zu laden. Deshalb besitzen Assemblerprogramme den Pseudobefehl LDR. Dieser wird vom Assembler in vorhandene Befehle übersetzt. Beim Micropython Inline Assembler ist dieser Pseudobefehl leider nicht vorhanden:
@micropython.asm_thumb
def pseudo():
ldr(r1, 42)
ldr(r0, 0x12345678)
# unsupported Thumb instruction 'ldr' with 2 arguments
LDR funktioniert nur mit Registern.
Workarount:
32-bit Werte in Array tun und die Array-Adresse als Parameter an die Funktion übergeben.
https://forum.micropython.org/viewtopic.php?f=21&t=9896
InLine Assembler schreiben
Die Schreibweise der Assemblerbefehle in Micropython unterscheidet sich von der der üblichen Assembler. In Micropython werden die Assembler-Befehle als Funktionen eingegeben, um der Python-Syntax zu entsprechen, obwohl MicroPython sie direkt in Assembler kompiliert.
Das Einfügen von Assemblercode in ein Micropythonscript ist ganz einfach. Dem Assemblercode Block muss nur der Dekorator @micropython.asm_thumb voran gestellt werden:
Allerdings funktioniert der Inline Assembler nicht in der REPL! Assembler Code muss in einem Script stehen!
@micropython.asm_thumb
def fun():
mov(r0, 42)
print(fun())
Die Ausgabe:
>>> %Run -c $EDITOR_CONTENT MPY: soft reboot 42 >>>
Parameter Übergabe
Als Parameter können Integer übergeben werden.
Als Eingangsregister für die Übergabe von Parametern können nur r0 ... r3 benutzt werden:
@micropython.asm_thumb
def test(r0, r1, r2, r3, r4):
mov(r0, r4)
print(test(1,2,3,4,5))
>>> %Run -c $EDITOR_CONTENT
MPY: soft reboot
Traceback (most recent call last):
File "<stdin>", line 2, in test
SyntaxError: can only have up to 4 parameters to Thumb assembly
>>>
Außerdem müssen die Register in aufsteigender Reihenfolge eingesetzt werden:
@micropython.asm_thumb
def test(r1, r0, r2, r3):
mov(r0, r4)
print(test(1,2,3,4))
>>> %Run -c $EDITOR_CONTENT
MPY: soft reboot
Traceback (most recent call last):
File "<stdin>", line 2, in test
SyntaxError: parameters must be registers in sequence r0 to r3
>>>
Beispiel für eine korrekte Anwendung:
@micropython.asm_thumb
def test(r0, r1, r2, r3):
add(r0, r0, r1)
add(r0, r0, r2)
add(r0, r0, r3)
print(test(1,2,3,4))
>>> %Run -c $EDITOR_CONTENT
MPY: soft reboot
10
>>>
Zurückgegeben wird immer der Inhalt von r0.
Es ist nicht möglich Micropython und Assembler in einer Funktion zu mischen.
Raspberry Pi Pico GPIO Programmierung
Die GPIO's des Pico sind alle in 32-bit-Registern zusammengefasst.
Es gibt folgende Register zum Umgang mit den GPIO's:
- GPIO_OUT
- Setzt den Ausgangspegel der GPIO's.
- GPIO_OE
- Aktiviert den Ausgangstreiber der GPIO's. 0 = hochohmig, 1 = Ausgang aktiv.
- GPIO_IN
- Enthält den aktuellen Status der GPIO's.
Links:
- https://smist08.wordpress.com/2022/02/18/adding-assembly-language-to-micropython/
- https://github.com/orgs/micropython/discussions/10401
- https://docs.micropython.org/en/v1.9.3/pyboard/pyboard/tutorial/assembler.html
- https://docs.micropython.org/en/latest/reference/asm_thumb2_hints_tips.html
- https://forum.micropython.org/viewtopic.php?t=9896
- https://docs.micropython.org/en/v1.9.3/pyboard/reference/asm_thumb2_index.html#asm-thumb2-index
- https://www.mikrocontroller.net/topic/515210
- https://ioprog.com/2022/11/21/speeding-up-some-micropython-with-a-touch-of-inline-assembly-on-the-raspberry-pi-pico/
- https://developer.arm.com/documentation/ddi0484/b/Programmers-Model/Processor-core-registers-summary
- https://en.wikipedia.org/wiki/ARM_architecture_family
- https://developer.arm.com/documentation/ddi0486/b/?lang=en
- https://developer.arm.com/documentation/dui0662/b
- https://docs.micropython.org/en/latest/reference/asm_thumb2_hints_tips.html#
- https://forum.micropython.org/viewtopic.php?f=21&t=12931&sid=25de8871fa9cfcf8cafb6318f9d8ba3a
- https://github.com/orgs/micropython/discussions/12257
- https://forums.raspberrypi.com/viewtopic.php?t=344701
- https://people.ece.cornell.edu/land/courses/ece4760/RP2040/index_rp2040_Micropython.html
- https://www.youtube.com/watch?v=G64GegkxW84
- https://www.youtube.com/watch?v=FV6P5eRmMh8
- https://awesome-micropython.com/
- https://docs.micropython.org/en/v1.9.3/pyboard/reference/asm_thumb2_str.html
- https://docs.micropython.org/en/v1.9.3/pyboard/reference/asm_thumb2_mov.html
- https://docs.micropython.org/en/v1.9.3/pyboard/reference/asm_thumb2_directives.html
- https://docs.micropython.org/en/v1.9.3/pyboard/reference/asm_thumb2_misc.html
- https://docs.micropython.org/en/v1.9.3/pyboard/reference/asm_thumb2_ldr.html
- https://docs.micropython.org/en/v1.9.3/pyboard/reference/asm_thumb2_index.html#asm-thumb2-index
- https://docs.micropython.org/en/v1.9.3/pyboard/pyboard/tutorial/assembler.html#
- https://www.codeinsideout.com/blog/stm32/intro/#stm32-microcontrollers
zurück zu Micropython_Kurs_2025
zurück zu Kurse
Zurück zur Hauptseite