Help getting proficient in PASM
escher
Posts: 138
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
And without further ado:
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
:pollloop 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
:dsinloop is run to pulse the clock pin and read the next 15. The
sregsubroutine is used to capture the state of the input bit and shift it into a storage register, and the
waitsubroutine 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
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.
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?
The way you shift in the databits is a bit complicated, you can do it like that: 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:
Andy
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:
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.
Any other musings, no matter how minor, are appreciated!