how to use viper decorator?

The official pyboard running MicroPython.
This is the reference design and main target board for MicroPython.
You can buy one at the store.
Target audience: Users with a pyboard.
starter111
Posts: 40
Joined: Wed Mar 08, 2017 7:24 am

how to use viper decorator?

Post by starter111 » Mon Sep 23, 2019 10:08 pm

I'm trying to use portB 8-15 for output, and portC 0-7 for input.

Code: Select all

import time
from stm import *

def set_bout():
    moderval = mem32[GPIOB + GPIO_MODER]
    moderval &= 0x0000FFFF
    moderval |= 0x55550000
    mem32[GPIOB + GPIO_MODER] = moderval
    otyper = mem16[GPIOB + GPIO_OTYPER]
    otyper &= 0xFF
    mem16[GPIOB + GPIO_OTYPER] = otyper
    ospeedr = mem32[GPIOB + GPIO_OSPEEDR]
    ospeedr |= 0xFFFF0000
    mem32[GPIOB + GPIO_OSPEEDR]= ospeedr

def set_cin():
    mem16[GPIOC + GPIO_MODER] = 0
    mem16[GPIOC + GPIO_OSPEEDR]= 0xFFFF

# @micropython.viper
def run(t, d_us):
    val_8bits = mem8[GPIOB + GPIO_ODR]
    d = []
    for i in t:
        mem16[GPIOB + GPIO_ODR] = (i << 8) | val_8bits
        time.sleep_us(d_us)
        d.append(mem8[GPIOC + GPIO_IDR])
    return d
    
set_bout()
set_cin()
test = [0x10, 0x20, 0x30, 0x40]
d = run(test, 5)

print(d)

How do I convert run function with @micropython.viper?

User avatar
jimmo
Posts: 1440
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: how to use viper decorator?

Post by jimmo » Tue Sep 24, 2019 12:29 am

Hi,

The documentation at http://docs.micropython.org/en/v1.9.3/p ... de-emitter helps a little bit, but the key thing is that for viper to work well it needs to know the type of every single variable.

So the two things you need to do:
- Type annotations on input variables that you want to be "viper types" (a: int, uint, ptr8, etc).
- Casts where necessary (i.e. int(), uint(), etc).

Note that "t" will be a Python object, so the "i" in the loop will be also. You need to cast that to int.

Here's a quick attempt that at least compiles (but I haven't tested it on hardware).

Code: Select all

@micropython.viper
def run(t, d_us: int):
    val_8bits = int(mem8[GPIOB + GPIO_ODR])
    d = []
    for i in t:
        mem16[GPIOB + GPIO_ODR] = (int(i) << 8) | val_8bits
        time.sleep_us(d_us)
        d.append(mem8[GPIOC + GPIO_IDR])
    return d

starter111
Posts: 40
Joined: Wed Mar 08, 2017 7:24 am

Re: how to use viper decorator?

Post by starter111 » Tue Sep 24, 2019 4:53 pm

thanks a lot, is working..but not as fast as what I wish :)..
I was hoping I can get 1us period..but even I write the register directly as below I got 2.6us..
Is it c module faster than viper?

@micropython.viper
def run(t, d_us : int ):
mem16[1073873940] = 0x8000
mem16[1073873940] = 0x0000
mem16[1073873940] = 0x8000
mem16[1073873940] = 0x0000

User avatar
Roberthh
Posts: 1890
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: how to use viper decorator?

Post by Roberthh » Tue Sep 24, 2019 6:04 pm

The mem16 call is slow. You can create a direct memory pointer with:

Code: Select all

@micropython.viper
def run(t, d_us : int ):
    memptr = ptr16(1073873940)
    memptr[0] = 0x8000
    memptr[0] = x00000
    memptr[0] = 0x8000
    memptr[0] = x00000
That will be translated into direct assembler calls and should be much faster.

starter111
Posts: 40
Joined: Wed Mar 08, 2017 7:24 am

Re: how to use viper decorator?

Post by starter111 » Wed Sep 25, 2019 12:25 am

looks like const() help little bit.. I got about 18us per write read.. is it anything else I can try with viper?

Code: Select all

@micropython.viper
def run(t, d_us : int):
    val_8bits = int(mem8[GPIOB + GPIO_ODR])
    odr = const(1073873940)
    idr = const(1073874960)
    d = []
    for i in t:
        mem16[odr] = (int(i) << 8) | val_8bits
        time.sleep_us(d_us)
        d.append(mem8[idr])
    return d

OutoftheBOTS_
Posts: 732
Joined: Mon Nov 20, 2017 10:18 am

Re: how to use viper decorator?

Post by OutoftheBOTS_ » Wed Sep 25, 2019 6:22 am

as quoted above by RobertHH using a 16 bit pointer instead of using mem16 compiles directly to 2 machine instructions.
memptr = ptr16(1073873940)

User avatar
Roberthh
Posts: 1890
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: how to use viper decorator?

Post by Roberthh » Wed Sep 25, 2019 6:27 am

Like I told: do not use mem16 or mem8, use direct pointer assignment. That is fast. But you have quite a few "slow" operations in the function:
- iterator access (for i in t)
- function calls (sleep_us()). Each function call in viper with a single argument takes about 3 µs.
- d.append() is a slow method. You can avoid that by pre-allocation a byte array and supplying a pointer to that as an argument. You have to call it then as run(list, delay, addressof(buffer)). addressof is a method of the uctypes module. You can as well replace the iterator access by an indexed access to a byte array. But then you have to supply the size as well.

Warning: when using direct memory access, e.g. with ptr types, there is no protection by the Python runtime. Wong pointers will results in a crash. (you will survive!)

Code: Select all

from stm import *
import time

@micropython.viper
def run(t, d_us : int, d: ptr8):
    odr8 = ptr8(GPIOB + GPIO_ODR)
    odr16 = ptr16(GPIOB + GPIO_ODR)
    idr = ptr8(GPIOC + GPIO_IDR)
    val_8bits = odr8[0]
    index = 0
    for i in t:
        odr16[0] = (int(i) << 8) | val_8bits
        time.sleep_us(d_us)
        d[index] = idr[0]
        index += 1
I used Viper and assembler a lot in my SSD9163 driver at https://github.com/robert-hh/SSD1963-TF ... or-PyBoard. Look at the file TFT_io.py, especially the first function.

User avatar
pythoncoder
Posts: 4030
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: how to use viper decorator?

Post by pythoncoder » Wed Sep 25, 2019 7:39 am

For ultimate speed consider the inline assembler.
Peter Hinch

starter111
Posts: 40
Joined: Wed Mar 08, 2017 7:24 am

Re: how to use viper decorator?

Post by starter111 » Fri Sep 27, 2019 12:29 am

pythoncoder wrote:
Wed Sep 25, 2019 7:39 am
For ultimate speed consider the inline assembler.
totally agree if I know how :)...
just toggle a pin and measure in scope is 30 ns..much faster...
the only issue is....I don't know know how to finish up...need to read more :(...

movwt(r0, stm.GPIOB)
movw(r1, 0x0000)
strh(r1, [r0, stm.GPIO_ODR])
movw(r1, 0x8000)
strh(r1, [r0, stm.GPIO_ODR])
movw(r1, 0x0000)
strh(r1, [r0, stm.GPIO_ODR])
movw(r1, 0x8000)
strh(r1, [r0, stm.GPIO_ODR])
movw(r1, 0x0000)
strh(r1, [r0, stm.GPIO_ODR])
movw(r1, 0x8000)
strh(r1, [r0, stm.GPIO_ODR])

starter111
Posts: 40
Joined: Wed Mar 08, 2017 7:24 am

Re: how to use viper decorator?

Post by starter111 » Sat Sep 28, 2019 6:45 am

I think I almost there for my first assembly :).
1. for assembly is it a instruction can delay ns? nop()?
2. use byte instead of integer? how?

Code: Select all

@micropython.asm_thumb
def run(r0, r1, r2):
	movwt(r3, stm.GPIOB + stm.GPIO_ODR)
	movwt(r4, stm.GPIOC + stm.GPIO_IDR)
	label(LOOP_ALL)
	#here, need add code to write port B.
	ldrb(r6, [r4, 0])  # load IDR data
	strb(r6, [r0, 0])  # current buff idx data doesn't need any more, save read data to current index.
	add(r0, 4) # next data.
	
	mov(r5, r2) # better way to delay??
	label(DELAY_TIMES)
	sub(r5, 1)
	bgt(DELAY_TIMES)
	
	sub(r1, 1)
	bgt(LOOP_ALL)
	
set_bout()
set_cin()
test = [0x10, 0x20, 0x30, 0x40]
print(test)
out_data = array('i', test)
ptr_buf = addressof(out_data)
sizetoloop = len(test)
delay_t = 5
run(ptr_buf, sizetoloop, delay_t)
print(list(map(bin, list(out_data))))
Last edited by starter111 on Sat Sep 28, 2019 7:20 am, edited 1 time in total.

Post Reply