Page 1 of 2

how to use viper decorator?

Posted: Mon Sep 23, 2019 10:08 pm
by starter111
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?

Re: how to use viper decorator?

Posted: Tue Sep 24, 2019 12:29 am
by jimmo
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

Re: how to use viper decorator?

Posted: Tue Sep 24, 2019 4:53 pm
by starter111
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

Re: how to use viper decorator?

Posted: Tue Sep 24, 2019 6:04 pm
by Roberthh
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.

Re: how to use viper decorator?

Posted: Wed Sep 25, 2019 12:25 am
by starter111
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

Re: how to use viper decorator?

Posted: Wed Sep 25, 2019 6:22 am
by OutoftheBOTS_
as quoted above by RobertHH using a 16 bit pointer instead of using mem16 compiles directly to 2 machine instructions.
memptr = ptr16(1073873940)

Re: how to use viper decorator?

Posted: Wed Sep 25, 2019 6:27 am
by Roberthh
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.

Re: how to use viper decorator?

Posted: Wed Sep 25, 2019 7:39 am
by pythoncoder
For ultimate speed consider the inline assembler.

Re: how to use viper decorator?

Posted: Fri Sep 27, 2019 12:29 am
by starter111
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])

Re: how to use viper decorator?

Posted: Sat Sep 28, 2019 6:45 am
by starter111
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))))