Shop OBEX P1 Docs P2 Docs Learn Events
Help getting proficient in PASM — Parallax Forums

Help getting proficient in PASM

escherescher Posts: 138
edited 2017-06-01 15:24 in Propeller 1
So I've just started learning PASM, and by extension, assembly in general. Definitely a completely different way of thinking than even the lowest level programming languages I've used.

As a way to get comfortable with it, I decided to implement a serial interface to an asynchronous parallel-to-serial shift register: the 74HC165. My project requires 16 physical user inputs, so in lieu of wasting 16 GPIO pins on reading them in parallel, I've decided to read them in serially using only 4 pins via the 74HC165.

I loaded the Propeller open-source FPGA design onto my DE0-Nano, and attached the design internally on the FPGA to a virtual 74HC165 that I replicated in verilog.

Then came the PASM... and after a very steep learning curve and a very late night, I managed to get everything working, and all of the signals look perfect on my logic analyzer.

Now I'm looking to get feedback on the PASM code I wrote so I can start to get an idea of optimizations and best practices for coding in it.

Any input, comments, concerns, suggestions, etc. you guys have about my code please lay 'em out. How would you have written it?

Some notes on the design: Each 74HC165 is an 8-bit unit, therefore I require two daisy-chained together to give me 16 bits (this is done on the FPGA). The
:poll
loop is the infinite loop in which each set of 16 serial bits is read. Because the 74HC165 is asynchronous, the first bit is read on the input pin, and then the
:dsin
loop is run to pulse the clock pin and read the next 15. The
sreg
subroutine is used to capture the state of the input bit and shift it into a storage register, and the
wait
subroutine is used to generate a 1 microsecond delay to ensure that the required timing limits for the actual physical 74HC165 are met.

And without further ado:
CON
  _CLKMODE = xtal1 + pll16x
  _XINFREQ = 5_000_000

VAR
  long input_state                ' Register in Main RAM where the input states are stored and retrieved from 
PUB main
  cognew(@input, @input_state)
DAT
        org             0
input   or              dira,   Pin_CP          ' Set Pin_CP direction output
        or              dira,   Pin_CE_n        ' Set Pin_CE_n direction output
        or              dira,   Pin_PL_n        ' Set Pin_PL_n direction output
        andn            dira,   Pin_Q7        ' Set Pin-Q7 direction input
        or              dira,   Pin_test

        mov             Del_us, #80         
        mov             Delay,  Del_us
                                                
:poll   call            #wait                   ' Wait for parallel-loaded bits to propogate        
        mov             Inputs, #0              ' Reset Inputs register
        andn            outa,   Pin_CP          ' Drive clock pin low
        andn            outa,   Pin_PL_n        ' Drive parallel load pin low
        or              outa,   Pin_CE_n        ' Drive clock enable pin high
        
        call            #wait                   ' Wait for parallel-loaded bits to propogate
        
        or              outa,   Pin_PL_n        ' Drive parallel load pin high
        andn            outa,   Pin_CE_n        ' Drive clock enable pin low   
        
        call #wait                              ' Wait for clock enable to propogate
        
        call #sreg                              ' Get first bit of serial output 
         
        mov             Count,  #15
:dsin   call            #wait
        or              outa,   Pin_CP          ' Drive clock pin high
        call            #wait                   ' Wait for clock signal to propogate
        andn            outa,   Pin_CP          ' Drive clock pin low
        
        call            #sreg                  ' Get the next bit of serial output 

        djnz            count,  #:drinks         ' Repeat 15 times 
        
        wrword          Inputs, part          ' Write retrieved input states to Main RAM
        
        jmp             #:poll                  ' Jump back to the beginning of our loop

sreg          mov       Pin_in, ina             ' Read pin states
              shl       Inputs, #1              ' Shift Inputs left one bit
              and       Pin_in, Pin_Q7 wc       ' Mask Pin_DS and check parity        
        if_c  add       Inputs, #1              ' If parity was odd, shift 1 into LSB
        if_c  or        outa,   Pin_test
        if_nc andn      outa,   Pin_test       
sreg_ret      ret

wait          mov       Time,   cnt             ' Prime our timer with the current value of the system counter
              add       Time,   Delay           ' Add a minimum delay ( more on this below )
              waitcnt   Time,   Delay           ' Wait for parallel-loaded bits to propogate
wait_ret      ret

Pin_CP        long      |< 0    ' 74HC165 clock pin bitmask
Pin_CE_n      long      |< 1    ' 74HC165 clock enable pin bitmask
Pin_PL_n      long      |< 2    ' 74HC165 parallel load pin bitmask
Pin_Q7        long      |< 3    ' 74HC165 serial output pin bitmask
Pin_test      long      |< 14
Pin_in        res       1       ' I/O pin state bitmask         
Inputs        res       1       ' Control input shift register
Del_us        res       1       ' 1 microsecond delay ticks register
Delay         res       1       ' Delay register
Time          res       1       ' Time register
Count         res       1       ' 74HC165 clock pulse count register
        fit

Comments

  • tonyp12tonyp12 Posts: 1,951
    edited 2017-06-01 15:36
    Don't need a delay, P1 runs a 20MHz instructions, toggle a pin could possibly only be done at max 10MHz
    at 4.5v hc165 guaranteed to handle 24MHz, other manufactures say 17MHz at 3v
    Weird version of the chip in that the typical cmos Vcc is 5v.

  • tonyp12 wrote: »
    Don't need a delay, P1 runs a 20MHz instructions, toggle a pin could possibly only be done at max 10MHz
    at 4.5v hc165 guaranteed to handle 24MHz, 3.3V I would assume at least 10MHz
    Weird version of the chip in that the typical cmos Vcc is 5v.

    Wow I definitely missed the forest for the trees on that, thank you.

    I thought most CMOS logic was @ Vcc=3.3v, and TTL logic was 5v? Otherwise wouldn't you have to step up the voltage of the outputs of any 3.3v microcontroller you wanted to interface with a CMOS IC?
  • AribaAriba Posts: 2,690
    edited 2017-06-01 15:48
    looks good

    The way you shift in the databits is a bit complicated, you can do it like that:
    	test  Pin_Q7, INA  wc		'get Pin state to Carry
    	rcl   Inputs, #1		'shift Carry into shiftregister
    
    And if you do the :dsin loop 16 times, you don't need to read the first bit separatly, and therefore no sreg subroutine (just read the bit inline). But this is only important if you want to do it as fast as possible, which is not the case in your code with the many wait calls.

    The DIRA setting at begin can be make shorter if you define a register with all the output pins set:
    	mov   dira, Pin_Outs
    	...
    
    Pin_Outs   long  |< 0 | |< 1 | ...      'Pins that are outputs
    

    Andy
  • @Ariba, thanks for the input!

    I figured there was a way to shift into a register like that, but it was 2 am and I was seeing double looking at the P8X32A manual haha.

    Yeah I'm going to restructure the loop to be more logical. The reason for the first read and then 15 more is because once the PL_n pin on the 74HC165 has been driven low, the first output is immediately available on the Q7 pin without any clock pulse. So what I need to do is structure the loop like:
    repeat 16 times
        read Q7
        pulse clock
    

    It will cause the 74HC165 to spit out a garbage bit on that last clock pulse (which for some reason motivated my to write my loop the original way), but the PASM code doesn't query it anyway so I guess it's not a problem.
  • Based on the excellent suggestions from @tonyp12 and @Ariba, I've managed to greatly improve my original code, reducing the DAT section from 168 to 76 bytes! Thanks guys!
    CON
      _CLKMODE = xtal1 + pll16x     ' Fast external clock mode w/ 16x PLL
      _XINFREQ = 5_000_000          ' 5Mhz crystal
    
    VAR
      long input_state              ' Register in Main RAM containing state of inputs                                      
    PUB main
      cognew(@input, @input_state)  ' Initialize cog running input routine with reference to input_state register                           
    DAT
            org             0
    input   or              dira,   Pin_outs        ' Set output pins
            andn            dira,   Pin_Q7          ' Set input pin
    
            andn            outa,   Pin_CE_n        ' Drive clock enable pin low   
                                                    
    :poll   andn            outa,   Pin_CP          ' Drive clock pin low
            andn            outa,   Pin_PL_n        ' Drive parallel load pin low
            or              outa,   Pin_PL_n        ' Drive parallel load pin high
             
            mov             Count,  #16
    :dsin   or              outa,   Pin_CP          ' Drive clock pin high
            andn            outa,   Pin_CP          ' Drive clock pin low
            
            test            Pin_Q7, ina wc          ' Poll and carry state of Pin_Q7
            rcr             Inputs, #1              ' Shift Pin_Q7 state in Inputs register             
            
            djnz            Count,  #:dsin          ' Repeat to retrieve all 16 bits
            
            wrword          Inputs, par             ' Write Inputs to Main RAM input_state register
            
            jmp             #:poll                  ' Loop infinitely
    
    Pin_CP        long      |< 0                            ' 74HC165 clock pin bitmask
    Pin_CE_n      long      |< 1                            ' 74HC165 clock enable pin bitmask
    Pin_PL_n      long      |< 2                            ' 74HC165 parallel load pin bitmask
    Pin_outs      long      Pin_CP | Pin_CE_n | Pin_PL_n    ' Set output pin bitmask                      
    Pin_Q7        long      |< 12                           ' 74HC165 serial output pin bitmask
    Count         res       1                               ' 74HC165 clock pulse count
    Pin_in        res       1                               ' I/O pin state bitmask         
    Inputs        res       1                               ' Control input shift register
            fit
    

    Any other musings, no matter how minor, are appreciated!
Sign In or Register to comment.