Shop OBEX P1 Docs P2 Docs Learn Events
SPIN to ASM — Parallax Forums

SPIN to ASM

Jim EdwardsJim Edwards Posts: 54
edited 2012-02-16 06:48 in Propeller 1
Is there a compiler that takes a SPIN program and generates the ASM for it? To clarify further, I mean a small SPIN program where the resulting ASM can fit into 1 cog.
«1

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2012-02-04 14:58
    Not really. You might look at PropBasic which is a compiler that takes a variant of Basic and translates it into ASM and/or LMM (Large Memory Model - a semi-interpreted ASM that executes out of HUB memory at about 1/4 COG speed). The output of PropBasic is Spin source code (mostly DAT blocks) that gets compiled / assembled.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-02-04 15:01
    I'm pretty sure there isn't.

    There are just too many things that would be Spin or PASM specific to make a compilier worth the bother.

    Spin only uses variables in hub RAM while PASM uses both hub and cog RAM.

    This would make any kind of conversion from one to the other problematic. IMO.
  • Mike GreenMike Green Posts: 23,101
    edited 2012-02-04 15:04
    The main thread for PropBasic is here.
  • Dave HeinDave Hein Posts: 6,347
    edited 2012-02-04 15:45
    You could use spin2cpp (http://forums.parallax.com/showthread.php?137346-Updated-Spin-to-C-translator) to convert to C++, and then use ProgGCC to compile to a cog program.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-02-04 16:45
    On a number of occasions I've written a spin program knowing it would be changed into asm at some stage. I tend to write the spin code in an "asm" sort of way. For instance - if there are multiple and/or/bitshifts in a line of spin, split it up into individual lines. And if you have a variable multiplied by 2 or 4 etc, use a bitshift instead. There is some fascinating maths involved in multiplying any number by a constant using bitshifts only - and this sort of coding works out faster.

    The thing is that using tricks like reducing "n x 15" to "n bitshift left 4 places and subract n from the answer" are not the sort of things an automatic translation program can do very easily.

    I believe the very clever C programmers have got code that you can tell the compiler whether to put it in hub or cog.

    If you like, maybe post some of your spin code and we can think about how to convert it to asm?
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-04 20:05
    Ok, when I get it done I will post it. The part I want to do in ASM shouldn't be that hard I hope.
  • ersmithersmith Posts: 6,099
    edited 2012-02-05 10:43
    Dr_Acula wrote: »
    The thing is that using tricks like reducing "n x 15" to "n bitshift left 4 places and subract n from the answer" are not the sort of things an automatic translation program can do very easily.

    I'm often surprised by the optimizations gcc performs :-). Here's a log of converting a simple .spin function to PASM, automatically:
    $ cat test.spin
    PUB mul15(n)
      return n*15
    $ spin2cpp test.spin
    $ propeller-elf-gcc -mcog -O3 -S -o test.pasm test.cpp
    $ cat test.pasm
        .text
        .balign    4
        .global    __ZN8testSpin5Mul15Ei
    __ZN8testSpin5Mul15Ei
        mov    r0, r1
        shl    r0, #4
        sub    r0, r1
        jmp    lr
    

    This is under Linux, so the "$" is a command prompt and "cat" is the program to type a file out. The weird name "_ZN8testSpin5Mul15Ei" is due to the way C++ encodes type information in function names. Also note that I did give -O3 to propeller-elf-gcc to tell it to optimize for speed as much as it can; if it's optimizing for space (the normal case in the Propeller world) it'll generate a call to a multiply routine instead because that takes less space than the shift and sub.

    Of course the .pasm isn't a complete program, you'd need the register declarations and some interface code. Giving the "-mspin" argument to propeller-elf-gcc will make it output a lot of that for you. Also, it's using the normal C calling convention (where return addresses are stored in the "lr" register) instead of the PASM calling convention (where you return with "ret"); there are ways to force gcc to use that, but they require editing the .cpp file that spin2cpp creates.

    Eric
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-05 15:04
    Ok, below is the source code. What I want to convert to ASM is contained in the MafControlSpinCog method. The gist of the logic is I have 2 inputs, MafInPin and MafClampInPin, and 1 output, MafOutSelPin. MafInPin is a 5V squarewave signal that varies from 20 to 200 Hz. While MafClampInPin == 1, MafOutSelPin is continuously set to the value !MafInPin and I measure the clock counts for the high and low periods of MafInPin. When MafClampInPin transistions to 0, MafOutSelPin is held at the last frequency measured for the high and low period clock counts of MafInPin (i.e. it is clamped). With SPIN code, I get about 40 to 80 uSecs of delay from sampling MafInPin to output on MafOutSelPin.
    {{
    File.......... MafClamp.spin
    Purpose....... Control and test routines for the Maf Clamp board. 
    Author........ Jim Edwards, Asm code for MafControlAsmCog created by Kuroneko
    E-mail........ jim.edwards4@comcast.net
    History....... v1.0 - Initial release 
    Copyright..... Copyright (c) 2012 Jim Edwards
    Terms......... See end of file for terms of use.
    }}
    
    ' TODO:
    ' 1. Look at what happens if clamp/mirror control loses sync with mafin inputs. Might happen when cycling car power. What happens if there is no pulsing when power is turned on, but before car is turned over.
    
    OBJ
    
      pst                       : "Parallax Serial Terminal"
      
    CON
    
      ' General constants
      
      _clkmode                  = XTAL1 | PLL16X
      _xinfreq                  = 5_000_000
    
      ' Debug and test constants
      
      _PST_ENABLED              = FALSE
      _TEST_ENABLED             = TRUE
      _USE_CONTROL_SPIN_COG     = FALSE
      _ERR_CHECKING_ENABLED     = FALSE
    
      ' Control spin cog constants
      
      _STACK_SIZE               = 64
      _STATE_INIT               = 0
      _STATE_MIRROR             = 1
      _STATE_CLAMP              = 2
    
      ' Waveform test cog constants
      
      _FREQ1                    = 50
      _NCYCLES1                 = 4
      _FREQ2                    = 100
      _NCYCLES2                 = 4
      _DUTY_25                  = 0
      _DUTY_50                  = 1
      _DUTY_75                  = 2
      _CLAMP                    = 0
      _MIRROR                   = 1
    
      ' Operational I/O pin constants
    
      _MAFIN1_PIN                = 5
      _MAFIN2_PIN                = 11
      _MAFCLAMPIN_PIN            = 20
      _MAFOUTSEL1_PIN            = 27
      _MAFOUTSEL2_PIN            = 18
      _CLAMPONLED_PIN            = 15
    
      ' Test output pin constants
    
      _MAFIN1RAW_TESTOUTPIN      = 24
      _MAFIN2RAW_TESTOUTPIN      = 22
      _MAFCLAMPINRAW_TESTOUTPIN  = 23
            
    VAR
    
      long stack1[_STACK_SIZE]
      long stack2[_STACK_SIZE]
      long stack3[_STACK_SIZE]
    
    PUB MafClampMain
    
      dira[_CLAMPONLED_PIN]~~
    
      if (_PST_ENABLED)
        pst.Start(115200)
        waitcnt(clkfreq * 2 + cnt)
        pst.Clear
        pst.Str(string("MafClampMain", pst#NL))
    
      if (_TEST_ENABLED)
        MafTestCogStart(_MAFIN1RAW_TESTOUTPIN, _MAFIN2RAW_TESTOUTPIN, _MAFCLAMPINRAW_TESTOUTPIN, @stack1)
    
      if (_USE_CONTROL_SPIN_COG)
        MafControlSpinCogStart(_MAFIN1_PIN, _MAFOUTSEL1_PIN, _MAFCLAMPIN_PIN, @stack2)
        MafControlSpinCogStart(_MAFIN2_PIN, _MAFOUTSEL2_PIN, _MAFCLAMPIN_PIN, @stack3)
      else
        MafControlAsmCogStart(_MAFIN1_PIN, _MAFOUTSEL1_PIN, _MAFCLAMPIN_PIN)
        MafControlAsmCogStart(_MAFIN2_PIN, _MAFOUTSEL2_PIN, _MAFCLAMPIN_PIN)
    
      repeat
        outa[_CLAMPONLED_PIN] := !ina[_MAFCLAMPIN_PIN]
      if (_TEST_ENABLED)
        waitcnt(clkfreq / 1000 + cnt)
      else
        waitcnt(clkfreq / 10 + cnt)
    
    PUB MafTestCogStart(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, stack_ptr) : cog
    
      ' Starts test cog process for testing operation of Maf Clamp board.
    
      cog := cognew(MafTestCog(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin), stack_ptr) + 1
            
    PUB MafTestCog(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin) | index, duty_cycle
    
      dira[mafin1raw_testoutpin]~~
      dira[mafin2raw_testoutpin]~~
      dira[mafclampinraw_testoutpin]~~
      
      outa[mafin1raw_testoutpin]~
      outa[mafin2raw_testoutpin]~
      outa[mafclampinraw_testoutpin]~~
    
      repeat
    
        repeat index from 0 to 2
    
          if (index == 0)
            duty_cycle := _DUTY_25
          elseif (index == 1)
            duty_cycle := _DUTY_50
          else
            duty_cycle := _DUTY_75 
    
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ1, _NCYCLES1, duty_cycle, _MIRROR, _MIRROR, 0)
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ2, _NCYCLES2, duty_cycle, _MIRROR, _MIRROR, 0)
    
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ1, _NCYCLES1, duty_cycle, _MIRROR, _CLAMP, 1)
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ2, _NCYCLES2, duty_cycle, _CLAMP, _MIRROR, 1)
    
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ1, _NCYCLES1, duty_cycle, _MIRROR, _CLAMP, 2)
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ2, _NCYCLES2, duty_cycle, _CLAMP, _MIRROR, 2)
    
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ1, _NCYCLES1, duty_cycle, _MIRROR, _CLAMP, 3)
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ2, _NCYCLES2, duty_cycle, _CLAMP, _MIRROR, 3)
    
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ1, _NCYCLES1, duty_cycle, _MIRROR, _MIRROR, 0)
        
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ2, _NCYCLES2, duty_cycle, _MIRROR, _CLAMP, 1)
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ1, _NCYCLES1, duty_cycle, _CLAMP, _MIRROR, 1)
        
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ2, _NCYCLES2, duty_cycle, _MIRROR, _CLAMP, 2)
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ1, _NCYCLES1, duty_cycle, _CLAMP, _MIRROR, 2)
        
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ2, _NCYCLES2, duty_cycle, _MIRROR, _CLAMP, 3)
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ1, _NCYCLES1, duty_cycle, _CLAMP, _MIRROR, 3)
    
          TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, _FREQ2, _NCYCLES2, duty_cycle, _MIRROR, _MIRROR, 0)
    
    PUB ValidDutyCycle(duty_cycle)
    
      return((duty_cycle == _DUTY_25) OR (duty_cycle == _DUTY_50) OR (duty_cycle == _DUTY_75))
    
    PUB ValidStartStopState(start_state, stop_state)
    
      return(((start_state == _CLAMP) OR (start_state == _MIRROR)) AND ((stop_state == _CLAMP) OR (stop_state == _MIRROR)))
    
    PUB TestWaveForm(mafin1raw_testoutpin, mafin2raw_testoutpin, mafclampinraw_testoutpin, freq, ncycles, duty_cycle, start_state, stop_state, stop_nquarter_cycles) | nquarter_cycles, quarter_period
    
      nquarter_cycles := ncycles * 4
      
      if ((NOT _ERR_CHECKING_ENABLED) OR ((freq > 0) AND (ncycles => 1) AND (ValidDutyCycle(duty_cycle)) AND (ValidStartStopState(start_state, stop_state)) AND (stop_nquarter_cycles < nquarter_cycles)))
      
        outa[mafin1raw_testoutpin]~~
        outa[mafin2raw_testoutpin]~~
        outa[mafclampinraw_testoutpin] := start_state
        quarter_period := clkfreq / (freq * 4)
        
        repeat while (nquarter_cycles > 0)
        
          waitcnt(quarter_period + cnt)
          nquarter_cycles--
          if (((nquarter_cycles // 4) == 0) OR ((duty_cycle == _DUTY_25) AND ((nquarter_cycles // 4) == 3)) OR ((duty_cycle == _DUTY_50) AND ((nquarter_cycles // 4) == 2)) OR ((duty_cycle == _DUTY_75) AND ((nquarter_cycles // 4) == 1)))
            !outa[mafin1raw_testoutpin]
            !outa[mafin2raw_testoutpin]
          if (nquarter_cycles == stop_nquarter_cycles)
            outa[mafclampinraw_testoutpin] := stop_state
        
    PUB MafControlSpinCogStart(mafin_pin, mafoutsel_pin, mafclampin_pin, stack_ptr) : cog
    
      ' Starts spin-based Maf control cog process.
    
      cog := cognew(MafControlSpinCog(mafin_pin, mafoutsel_pin, mafclampin_pin), stack_ptr) + 1
    
    PUB MafControlSpinCog(mafin_pin, mafoutsel_pin, mafclampin_pin) | maf_cur_val, maf_last_val, c1, c2, c3, high_cnt, low_cnt, state, full_cycle
    
      dira[mafin_pin]~
      dira[mafoutsel_pin]~~
      dira[mafclampin_pin]~
      state := _STATE_INIT
    
      repeat
          
        case state
        
          _STATE_INIT:
                      
            ' Startup state, just mirror inverted value of MafInPin to mafoutsel_pin and wait to acquire first measurement
            ' of high and low counts of one cycle of mafin_pin.
    
            maf_last_val := c1 := -1
            high_cnt := low_cnt := 0
            full_cycle := FALSE
            
            repeat until (full_cycle)
              maf_cur_val := ina[mafin_pin]      
              outa[mafoutsel_pin] := !maf_cur_val
              if ((maf_cur_val == 1) AND (maf_last_val == 0))       ' Rising edge detected on mafin_pin.
                if (c1 == -1)
                  c1 := cnt
                else
                  c3 := cnt
                  high_cnt := c2 - c1
                  low_cnt := c3 - c2
                  c1 := c3
                  full_cycle := TRUE  
              elseif ((maf_cur_val == 0) AND (maf_last_val == 1))   ' Falling edge detected on mafin_pin.
                c2 := cnt
              maf_last_val := maf_cur_val
              
            state := _STATE_MIRROR
                            
          _STATE_MIRROR:
    
            ' mafoutsel_pin will follow the inverted value of mafin_pin during this state. When the active low signal mafclampin_pin
            ' turns to 0, wait until mafin_pin reaches it's next rising edge transition.
    
            full_cycle := FALSE
            repeat until ((ina[mafclampin_pin] == 0) AND full_cycle)
              full_cycle := FALSE 
              maf_cur_val := ina[mafin_pin] 
              outa[mafoutsel_pin] := !maf_cur_val
              if ((maf_cur_val == 1) AND (maf_last_val == 0))       ' Rising edge detected on mafin_pin.
                c3 := cnt
                high_cnt := c2 - c1
                low_cnt := c3 - c2
                c1 := c3
                full_cycle := TRUE
              elseif ((maf_cur_val == 0) AND (maf_last_val == 1))   ' Falling edge detected on mafin_pin.
                c2 := cnt
              maf_last_val := maf_cur_val
    
            state := _STATE_CLAMP
                    
          _STATE_CLAMP:             
    
            ' mafoutsel_pin will be clamped at a frequency given by the last high and low counts measured while in the _STATE_MIRROR
            ' state. When the active low signal mafclampin_pin returns to 1, hold mafoutsel_pin at 1 until mafin_pin reaches it's
            ' next rising edge transition.
            
            repeat until (ina[mafclampin_pin] == 1)
    
              c1 := cnt
              repeat until ((cnt - c1) => high_cnt)
                outa[mafoutsel_pin] := 0
    
              c1 := cnt
              repeat until ((cnt - c1) => low_cnt)
                outa[mafoutsel_pin] := 1
                
            maf_last_val := -1
            
            repeat
              maf_cur_val := ina[mafin_pin]
              if ((maf_cur_val == 1) AND (maf_last_val == 0))       ' Rising edge detected on mafin_pin.
                c1 := cnt                                           ' Ensure that _STATE_MIRROR state starts counting process properly for high/low period.
                outa[mafoutsel_pin] := !maf_cur_val
                state := _STATE_MIRROR
                maf_last_val := maf_cur_val
                quit
              maf_last_val := maf_cur_val
                    
    PUB MafControlAsmCogStart(mafin_pin, mafoutsel_pin, mafclampin_pin) : cog | pins
    
      pins := NEGX | mafclampin_pin << 18 | mafoutsel_pin << 9 | mafin_pin
      if cog := cognew(@MafControlAsmCog, @pins) + 1
        repeat while pins
      
    DAT             org     0
    
    MafControlAsmCog
                    movi    ctra, #%0_01000_000     ' POS detector
                    rdlong  temp, par               ' [!Z]:p2:p1:p0 = 5:9:9:9
    
                    movs    ctra, temp              ' input pin
                    shl     pin_0, temp             ' pin -> mask
    
                    wrlong  zero, par               ' release caller
    
                    shr     temp, #9                ' [!Z]:p2:p1
                    movd    ctra, temp              ' output pin (inverted)
                    shl     pin_1, temp             ' pin -> mask
                    
                    shr     temp, #9                ' [!Z]:p2
                    shl     pin_2, temp             ' pin -> mask
    
                    mov     dira, pin_1             ' drive output
                    
                    movs    ctrb, ctra              ' grab input pin for NEG detector
                    movi    ctrb, #%0_01100_000     ' NEG detector
    
    ' At this point we have both counters setup for level detection. Counter A will be using
    ' feedback mode later to support the pass-through (out := !in).
    
                    mov     frqa, #1                ' |
                    mov     frqb, #1                ' activate counters
    
                    
    :revert         waitpne pin_0, pin_0            ' |
                    mov     phsa, #0                ' |
                    waitpeq pin_0, pin_0            ' |
                    mov     phsb, #0                ' sync accumulators
    
                    andn    outa, pin_1             ' release output
                    movi    ctra, #%0_01001_000     ' enable feedback
    
    :mirror         waitpne pin_0, pin_0            ' high/low transition
                    mov     period_h, phsa          ' |
                    mov     phsa, #0                ' record and reset
    
                    waitpeq pin_0, pin_0            ' low/high transition
                    mov     period_l, phsb          ' |
                    mov     phsb, #0                ' record and reset
    
                    mov     cnt, cnt                ' sync point
    
                    test    pin_2, ina wz,wc        ' sample clamp pin
            if_z    jmp     #:clamp                 ' active (carry clear)  (%%)
                    jmp     #:mirror                ' keep measuring
    
    
    :loop           waitcnt cnt, period_l           ' |
                    nop                             ' | counter test at end of
                    nop                             ' | high cycle
                    or      outa, pin_1             ' manual: high (input low)
    
                    waitcnt cnt, period_h           ' |
                    test    pin_2, ina wz           ' | sample clamp pin
            if_nz   jmp     #:revert                ' | inactive
    :clamp          andn    outa, pin_1             ' manual: low (input high)
    
            if_nc   movi    ctra, #%0_01000_000     ' disable feedback
            if_nc   add     cnt, period_h           ' |
            if_nc   sub     cnt, #26 + 1            ' adjust initial low period
    
                    jmpret  zero, #:loop wc,nr      ' set carry             (%%)
                    
    ' initialised data and/or presets
    
    pin_0           long    1                       ' mafin_pin 
    pin_1           long    1                       ' mafoutsel_pin
    pin_2           long    1                       ' mafclampin_pin
    
    ' uninitialised data and/or temporaries
    
    period_h        res     1
    period_l        res     1
    
    temp            res     1
    
                    fit
    
    CON
      zero = $1F0   ' par
      
    DAT
    
    {{
    &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
    &#9474;                                                   TERMS OF USE: MIT License                                                  &#9474;                                                            
    &#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
    &#9474;Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation    &#9474; 
    &#9474;files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,    &#9474;
    &#9474;modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software&#9474;
    &#9474;is furnished to do so, subject to the following conditions:                                                                   &#9474;
    &#9474;                                                                                                                              &#9474;
    &#9474;The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.&#9474;
    &#9474;                                                                                                                              &#9474;
    &#9474;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE          &#9474;
    &#9474;WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR         &#9474;
    &#9474;COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,   &#9474;
    &#9474;ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                         &#9474;
    &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
    }} 
    
    
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-05 20:43
    Ok, when I get it done I will post it. The part I want to do in ASM shouldn't be that hard I hope.
    If there are any issues let me know. For testing I used 1Hz/8Hz signals to have visual feedback. The active code is below and including the init method.
    CON
      _clkmode = XTAL1|PLL16X
      _xinfreq = 5_000_000
    
    CON
      _MafInPin      = 16
      _MafOutSelPin  = 17
      _MafClampInPin = 23
      
    PUB selftest
    
      outa[_MafClampInPin] := 1
      dira[16..23]~~
    
      ctra := constant(%0_00100_000 << 23 | _MafInPin)
      frqa := 53                                            ' 1Hz
    
      init(_MafInPin, _MafOutSelPin, _MafClampInPin)
    
      waitcnt(clkfreq*2 + cnt)
      frqa := 429                                           ' 8Hz
    
      waitcnt(clkfreq*2 + cnt)
      frqa := 53                                            ' 1Hz
    
      waitcnt(clkfreq*2 + cnt)
      outa[_MafClampInPin]~                                 ' clamp 1Hz
    
      waitcnt(clkfreq*2 + cnt)
      frqa := 429                                           ' 8Hz
    
      waitcnt(clkfreq*2 + cnt)
      outa[_MafClampInPin]~~                                ' mirror
      
      waitcnt(clkfreq*2 + cnt)
      outa[_MafClampInPin]~                                 ' clamp 8Hz
      
      waitcnt(clkfreq*2 + cnt)
      frqa := 53                                            ' 1Hz
    
      waitcnt(clkfreq*2 + cnt)
      outa[_MafClampInPin]~~                                ' mirror
        
      waitpne(0, 0, 0)
      
    PUB init(MafInPin, MafOutSelPin, MafClampInPin) : cog | pins
    
      pins := NEGX | MafClampInPin << 18 | MafOutSelPin << 9 | MafInPin
      if cog := cognew(@entry, @pins) + 1
        repeat while pins
      
    DAT             org     0
    
    entry           movi    ctra, #%0_01000_000     ' POS detector
                    rdlong  temp, par               ' [!Z]:p2:p1:p0 = 5:9:9:9
    
                    movs    ctra, temp              ' input pin
                    shl     pin_0, temp             ' pin -> mask
    
                    wrlong  zero, par               ' release caller
    
                    shr     temp, #9                ' [!Z]:p2:p1
                    movd    ctra, temp              ' output pin (inverted)
                    shl     pin_1, temp             ' pin -> mask
                    
                    shr     temp, #9                ' [!Z]:p2
                    shl     pin_2, temp             ' pin -> mask
    
                    mov     dira, pin_1             ' drive output
                    
                    movs    ctrb, ctra              ' grab input pin for NEG detector
                    movi    ctrb, #%0_01100_000     ' NEG detector
    
    ' At this point we have both counters setup for level detection. Counter A will be using
    ' feedback mode later to support the pass-through (out := !in).
    
                    mov     frqa, #1                ' |
                    mov     frqb, #1                ' activate counters
    
                    
    :revert         waitpne pin_0, pin_0            ' |
                    mov     phsa, #0                ' |
                    waitpeq pin_0, pin_0            ' |
                    mov     phsb, #0                ' sync accumulators
    
                    movi    ctra, #%0_01001_000     ' enable feedback
    
    :mirror         waitpne pin_0, pin_0            ' high/low transition
                    mov     period_h, phsa          ' |
                    mov     phsa, #0                ' record and reset
    
                    waitpeq pin_0, pin_0            ' low/high transition
                    mov     period_l, phsb          ' |
                    mov     phsb, #0                ' record and reset
    
                    mov     cnt, cnt                ' sync point
    
                    test    pin_2, ina wz,wc        ' sample clamp pin
            if_z    jmp     #:clamp                 ' active (carry clear)  (%%)
                    jmp     #:mirror                ' keep measuring
    
    
    :loop           test    pin_2, ina wz           ' sample clamp pin
            if_nz   jmp     #:revert                ' inactive
    
                    waitcnt cnt, [COLOR="orange"]period_l[/COLOR]           ' |
                    or      outa, pin_1             ' manual: high (input low)
    
                    waitcnt cnt, [COLOR="orange"]period_h[/COLOR]           ' |
    :clamp          andn    outa, pin_1             ' manual: low (input high)
    
            if_nc   movi    ctra, #%0_01000_000     ' disable feedback
            if_nc   add     cnt, [COLOR="orange"]period_h[/COLOR]           ' |
            if_nc   sub     cnt, #19                ' adjust initial low period
    
                    jmpret  zero, #:loop wc,nr      ' set carry             (%%)
                    
    ' initialised data and/or presets
    
    pin_0           long    1                       ' MafInPin  
    pin_1           long    1                       ' MafOutSelPin
    pin_2           long    1                       ' MafClampInPin
    
    ' uninitialised data and/or temporaries
    
    period_h        res     1
    period_l        res     1
    
    temp            res     1
    
                    fit
    
    CON
      zero = $1F0   ' par
      
    DAT
    
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-06 04:56
    That was fast! I won't have time until the weekend to try this out, but were you able to scope out the MafInPin and MafOutSelPin to see how much delay there is between the two signals? With SPIN I see around 40 to 80 uSecs of delay that varies across that range. Also, for transitions back and forth between the clamp and mirror states, my SPIN code ensures there are graceful transitions on the MafOutSelPin to sync back up with MafInPin particularly when going from the clamp state back to the mirror state. I noticed you used counters in the ASM code, does it still transition gracefully like the original SPIN code (your eye won't see this, but a scope will).
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-06 05:06
    Delay in mirror mode is one clock cycle (12.5ns @80MHz). As for transitions, unless I messed it up it should behave like the SPIN version, clamp input is acted on after low/high transition (in mirror mode) and the output is kept low (in clamp mode) until the input transitions from low to high (output high to low).
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-06 05:50
    Super! That is awesome and I can't wait to try it out this weekend. Just one last question, I intend to run 2 separate cogs for this mirror/clamp code each cog with different input/output pins. I assume that I can call init() twice with the proper pins and it will just work. Thanks!
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-06 05:54
    Just one last question, I intend to run 2 separate cogs for this mirror/clamp code each cog with different input/output pins. I assume that I can call init() twice with the proper pins and it will just work.
    Yes, that's the idea. Anyway, just play around with it, if it needs fixing re: certain behaviour just drop me a line (it's not a 100% copy of the SPIN code).
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-06 07:52
    kuroneko wrote: »
    Yes, that's the idea. Anyway, just play around with it, if it needs fixing re: certain behaviour just drop me a line (it's not a 100% copy of the SPIN code).

    Ok, just another question because I'm not familiar with how ASM works. But, I notice at end you have a data section for the pins. If you have multiple cogs, is this pin data created separately for each cog or is it shared (latter would be a problem I think). Thanks!
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-06 16:03
    The DAT section contains only ones (ready for 1 << n). Those are finalised once the cog is up and running (i.e. in cog memory). The original data (hub memory) remains unchanged and can therefore be re-used for another cog. So yes, each cog gets its own (separate) setup.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-02-06 17:25
    The DAT section contains only ones (ready for 1 << n). Those are finalised once the cog is up and running (i.e. in cog memory). The original data (hub memory) remains unchanged and can therefore be re-used for another cog. So yes, each cog gets its own (separate) setup.

    That is quite a clever technique kuroneko. Better than what I have used, which is to hard code the pins into variables.

    So - you declare a variable as a 00000000_00000000_00000000_00000001
    Then in the setup you pass the pin number. Maybe it is pin 12.
    Then you bitshift this by 12 places.

    Now you have 00000000_00000000_00010000_00000000 which can be used as a mask.

    That is very clever.
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-06 20:16
    @Jim: Just realised that the clamp loop uses the measured periods the wrong way round, the high period length needs to be applied to the low period (inverted output). This may not be an issue assuming 50% DUTY. I fixed the code in post #10. Apologies.
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-08 05:59
    kuroneko wrote: »
    @Jim: Just realised that the clamp loop uses the measured periods the wrong way round, the high period length needs to be applied to the low period (inverted output). This may not be an issue assuming 50% DUTY. I fixed the code in post #10. Apologies.

    I haven't copied the code over yet to test out so that was ok. I will be doing this on the weekend. Will get back to you on results. Thanks.
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-09 06:53
    I just did a quick load of the code. It seems to work but with a 2 channel scope and ext trigger on the clamp signal, it's a bit hard to determine exactly whether it operates correctly in all aspects; I need to see the 3 signals all at the same time. So, I've got a friend with a 4 channel tektronix scope that I'm going to see if I can borrow to really scrutinize this. I needed to do this anyway as my own code hasn't been fully checked out either.
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-09 06:54
    Just realized that my 2 channel scope actually has a 16 channel logic analyer capability so that should suffice for this as all the signals look good from the analog side anyway. Will give that a try on saturday.
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-11 20:19
    @kuroneko: I found some bugs in my original code and have fixed them. I wrote a pretty extensive test waveform generator to vary the clamp signal timing relative to frequency changes. I want to modify that next weekend to vary the input duty cycle a bit to ensure the code can handle it. I will post my code changes to the state machine at that time. If you could fix up your asm code, I would appreciate it. It works very well to cut down on jitter relative to spin code! It seems to be about 10x faster.
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-12 08:47
    Ok, got busy this morning and wrote the final code to vary the duty cycle of my test waveform generator. I am running my spin code version of the state machine to clamp/mirror the inputs. Looked everything over real good in the logic analyzer and the code now functions exactly as I wanted. Here is the slightly modified source for the state machine that needs to converted to ASM. If you diff this version with the last version there are probably a half dozen minor changes or so. It is very important to follow the state machine as the STATE_INIT state gets the counter variables going correctly for the rest of the states.
    PUB MafControlSpinCog(mafin_pin, mafoutsel_pin, mafclampin_pin) | maf_cur_val, maf_last_val, c1, c2, c3, high_cnt, low_cnt, state, full_cycle
    
      dira[mafin_pin]~
      dira[mafoutsel_pin]~~
      dira[mafclampin_pin]~
      state := _STATE_INIT
    
      repeat
          
        case state
        
          _STATE_INIT:
                      
            ' Startup state, just mirror inverted value of MafInPin to mafoutsel_pin and wait to acquire first measurement
            ' of high and low counts of one cycle of mafin_pin.
    
            maf_last_val := c1 := -1
            high_cnt := low_cnt := 0
            
            repeat until (full_cycle)
              full_cycle := FALSE
              maf_cur_val := ina[mafin_pin]      
              outa[mafoutsel_pin] := !maf_cur_val
              if ((maf_cur_val == 1) AND (maf_last_val == 0))       ' Rising edge detected on mafin_pin.
                if (c1 == -1)
                  c1 := cnt
                else
                  c3 := cnt
                  high_cnt := c2 - c1
                  low_cnt := c3 - c2
                  c1 := c3
                  full_cycle := TRUE  
              elseif ((maf_cur_val == 0) AND (maf_last_val == 1))   ' Falling edge detected on mafin_pin.
                c2 := cnt
              maf_last_val := maf_cur_val
              
            state := _STATE_MIRROR
                            
          _STATE_MIRROR:
    
            ' mafoutsel_pin will follow the inverted value of mafin_pin during this state. When the active low signal mafclampin_pin
            ' turns to 0, wait until mafin_pin reaches it's next rising edge transition.
    
            repeat until ((ina[mafclampin_pin] == 0) AND full_cycle)
              full_cycle := FALSE 
              maf_cur_val := ina[mafin_pin] 
              outa[mafoutsel_pin] := !maf_cur_val
              if ((maf_cur_val == 1) AND (maf_last_val == 0))       ' Rising edge detected on mafin_pin.
                c3 := cnt
                high_cnt := c2 - c1
                low_cnt := c3 - c2
                c1 := c3
                full_cycle := TRUE
              elseif ((maf_cur_val == 0) AND (maf_last_val == 1))   ' Falling edge detected on mafin_pin.
                c2 := cnt
              maf_last_val := maf_cur_val
    
            state := _STATE_CLAMP
                    
          _STATE_CLAMP:             
    
            ' mafoutsel_pin will be clamped at a frequency given by the last high and low counts measured while in the _STATE_MIRROR
            ' state. When the active low signal mafclampin_pin returns to 1, hold mafoutsel_pin at 1 until mafin_pin reaches it's
            ' next rising edge transition.
            
            repeat until (ina[mafclampin_pin] == 1)
    
              c1 := cnt
              repeat until ((cnt - c1) => high_cnt)
                outa[mafoutsel_pin] := 0
    
              c1 := cnt
              repeat until ((cnt - c1) => low_cnt)
                outa[mafoutsel_pin] := 1
                
            maf_last_val := -1
            
            repeat
              maf_cur_val := ina[mafin_pin]
              if ((maf_cur_val == 1) AND (maf_last_val == 0))       ' Rising edge detected on mafin_pin.
                c1 := cnt                                           ' Ensure that _STATE_MIRROR state starts counting process properly for high/low period.
                outa[mafoutsel_pin] := !maf_cur_val
                state := _STATE_MIRROR
                maf_last_val := maf_cur_val
                quit
              maf_last_val := maf_cur_val
    
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-12 19:36
    Just a few things I noticed:
    • full_cycle is a local variable so should be initialised to FALSE
    • cnt can be -1 (minor, you'd just spend one more cycle in the init state)
    • when switching back to mirror mode you should make sure to at least sample one more cycle, full_cycle is still TRUE and the clamp input may already be 0 again
    Anyway, I had to make a minor adjustment in that the clamp release test is now done during output being high (previously low). Also when switching to clamp mode I output at least one clamped cycle whereas the SPIN version could skip that (and wait for the rising edge immediately before returning to mirror mode). Do you expect clamp pin changes within the same cycle?

    To summarise the differences:
    • mirror-to-clamp: at least one clamped cycle is issued before the clamp pin is (re)sampled
    • clamp-to-mirror: at least one input cycle is measured to get updated high/low values
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-13 07:18
    kuroneko wrote: »
    Just a few things I noticed:
    • full_cycle is a local variable so should be initialised to FALSE
    • cnt can be -1 (minor, you'd just spend one more cycle in the init state)
    • when switching back to mirror mode you should make sure to at least sample one more cycle, full_cycle is still TRUE and the clamp input may already be 0 again
    Anyway, I had to make a minor adjustment in that the clamp release test is now done during output being high (previously low). Also when switching to clamp mode I output at least one clamped cycle whereas the SPIN version could skip that (and wait for the rising edge immediately before returning to mirror mode). Do you expect clamp pin changes within the same cycle?

    To summarise the differences:
    • mirror-to-clamp: at least one clamped cycle is issued before the clamp pin is (re)sampled
    • clamp-to-mirror: at least one input cycle is measured to get updated high/low values

    @kuroneka: Good catch on not initializing full_cycle. It needs to be set false at entry to both the _STATE_INIT and _STATE_MIRROR states. I've corrected my original code (also source shows the full source of the project including my test waveform generator). I guess full_cycle was set by the interpreter to 0 to begin with before entry to _STATE_INIT and it didn't matter that it was TRUE on entering _STATE_MIRROR because the clamp pin was always high at that point for my test waveforms. Now it is fixed. The reason I changed the code is that c1 needs to be initialized to the clock count before entering _STATE_MIRROR, either from _STATE_INIT (where c1 gets initially set up) or _STATE_CLAMP. For both of these states the code will have detected the first rising edge and so c1 needs to be set to start the measurement cycle of high and low counts within _STATE_MIRROR. I do not expect the clamp pin to ever change within one cycle. This project is for a car, it reads a Mass Airflow Signal that varies between 20 to 200 Hz and normally the clamp pin signal will change around the same frequency every time, say F, between 20 and 200. So, as the frequency rises from 20 Hz, it hits the clamp frequency F and the mafout pins stay at the last recorded frequency. Then as frequency falls from 200 Hz, it again hits the clamp frequency F but this time reverts to mirror mode. The clamped maf out pin will drive the stock ECU of the car and a piggyback ECU will read the raw maf in pin and take over the car control when the clamp point is hit but we need to fool the stock ECU that everything is ok by clamping it's maf input with the maf out pin of this circuit.

    Anyway, I tested the code again using both my spin based control cog and your asm based control cog. The logic analyzer shows they work exactly the same, only the delay of the spin cog from input edge to output edge is something like 60-80 uSecs and for your asm cog it's only about 10 uSecs with little variation from that.

    Thanks for the help! The circuit and code work exactly as I had hoped.
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-13 16:40
    Anyway, I tested the code again using both my spin based control cog and your asm based control cog. The logic analyzer shows they work exactly the same, only the delay of the spin cog from input edge to output edge is something like 60-80 uSecs and for your asm cog it's only about 10 uSecs with little variation from that.
    Cool. One last thing, I'm puzzled about the 10 uSec. The way inversion is done in mirror mode is by using counter feedback, i.e. the input is fed through a FF and output one clock cycle later (inverted). So I would at least expect a value in the ns range (12.5ns @80MHz). Can you clarify?
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-13 18:08
    kuroneko wrote: »
    Cool. One last thing, I'm puzzled about the 10 uSec. The way inversion is done in mirror mode is by using counter feedback, i.e. the input is fed through a FF and output one clock cycle later (inverted). So I would at least expect a value in the ns range (12.5ns @80MHz). Can you clarify?

    I thought this might just be due to the sample rate I had my logic analyzer set on initially, which was 100 KHz, i.e. 10 uSec resolution. However, I just tried setting my LA to acquire at 100 MHz (10 nSec resolution) using deep memory mode and it is showing that the delay from input to output is 7.5 to 8 uSec. I can't explain why the real world results differ from your expectations.
  • kuronekokuroneko Posts: 3,623
    edited 2012-02-13 18:17
    Odd. Can you try this:
    CON
      _clkmode = XTAL1|PLL16X
      _xinfreq = 5_000_000
    
      pin_0 = 16
      pin_1 = 17
      
    PUB null
    
      dira[pin_0] := dira[pin_1] := 1
      ctra := constant(%0_00100_000 << 23 | pin_0)
      ctrb := constant(%0_01001_000 << 23 | pin_1 << 9 | pin_0)
      frqa := 2048
    
      waitpne(0, 0, 0)
    
    Could it be (input) signal rise/fall time related?

    When you measure this I'd be interested in the delay seen directly on the propeller pins (taking any external h/w out of the equation).
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-13 18:40
    I will look at this tomorrow. My inputs are conditioned through an LM324 op amp circuit and outputs go through a 2N3904 NPN transister. The circuits for the maf input and output on one cog are shown below. I don't think they are adding uSecs to the equation.

    Snap1.jpg
    1024 x 256 - 49K
  • Don MDon M Posts: 1,653
    edited 2012-02-13 18:47
    Jim- what program did you draw that schematic with?
  • Jim EdwardsJim Edwards Posts: 54
    edited 2012-02-14 05:07
    Don M wrote: »
    Jim- what program did you draw that schematic with?

    PCB artist, a free schematic and board layout software package from Advanced Circuits. See http://www.4pcb.com/free-pcb-layout-software/
Sign In or Register to comment.