Using arrays in PASM2?
I decided to re-visit my previous attempt at designing a multi-port serial object. It is based on Jon's excellent FullDuplexSerial object (sorry Jon for butchering your code!) and my own ideas of how to implement multiple serial ports. To do this I decided to use several arrays to hold the variables.
long num_ports ' total number of open ports (0-7) long rxpins[MAX_PORT] ' rx pin array long txpins[MAX_PORT] ' tx pin array long rxhub[MAX_PORT] ' cog p_rxbuf - ptr to rxbuf hub address long txhub[MAX_PORT] ' cog p_txbuf - ptr to txbuf hub address long p_rxhd[MAX_PORT] ' cog ptr to rxhead[] hub address start long p_txhd[MAX_PORT] ' cog ptr to txhead[] hub address start long p_txtl[MAX_PORT] ' cog ptr to txtail[] hub address start long rxhead[MAX_PORT] ' rx/tx head and tail arrays long rxtail[MAX_PORT] long txhead[MAX_PORT] ' long txtail[MAX_PORT] '
The above are the arrays defined as global VARs in the spin code.
portnum long 1 ' total # ports rxpin long 0[MAX_PORT] ' rx pin array txpin long 0[MAX_PORT] ' tx pin array p_rxbuf long 0[MAX_PORT] ' ptr to rxhub p_txbuf long 0[MAX_PORT] ' ptr to txhub p_rxhead long 0[MAX_PORT] ' ptr to rxhead p_txhead long 0[MAX_PORT] ' ptr to txhead p_txtail long 0[MAX_PORT] ' prt to txtail
And above are the variables located in the DAT section of PASM2 (along with a few other working variables).
In the attached SPIN document the AddPort() method initializes the rx/tx smart pins and stores the number of open ports prior to running the start() method to initialize the cog
uart_mgr ' read hub arrays into cog, get # of open ports ' to copy to cog ram rdlong portnum, ptra++ ' get # of open ports setq #(MAX_PORT*7) ' block copy hub variables to cog rdlong rxpin, ptra uart_main ' loop through each port, get rx, tx pins mov portctr, #0 ' initialize portctr = 0
(I cut out the numerous DEBUG statements in the code for clarity - I attached the full code to this thread) Starting out I move the SPIN variables into PASM2, this appears to be working correctly. The portctr based on the number of open ports and used to loop through each port to see if there is anything to receive or transmit.
.loop ' get offsets for array values alts portctr, #rxpin ' add portctr offset to rxpin - rxpin[portctr] mov rxd, 0 ' copy rxpin to rxd alts portctr, #txpin ' add portctr offset to txpin - txpin[portctr] mov txd, 0 ' copy txpin to txd ' rxpin available, jump to rx_serial or tx_serial testb rxd, #31 wc ' rx pin in use? -test for -1 when no pin saved if_nc call #rx_serial testb txd, #31 wc ' tx in use? -test for -1 if_nc call #tx_serial ' increment counter to next port. if portctr = ' open ports, reset portctr to 0 and loop incmod portctr, portnum wc ' check if portctr = open ports if_nc jmp #.loop ' repeat loop for each open port jmp #uart_main
The main loop here gets the rx, tx pins and sees if the smart pin has anything, then jumps to the tx or rx code. It loops through each open port. I am working on the tx_serial code at this point, haven't tested the rx_serial routine yet.
tx_serial rdpin t1, txd wc ' check busy flag if_c ret ' abort if busy alts portctr, #p_txhead ' get txhead value from hub mov offset, 0 ' offset = address of txhead[portctr] rdlong t1, offset ' t1 = txhead[portctr] in hub memory alts portctr, #p_txtail ' get txtail value from hub mov offset1, 0 ' offset1 = address of txtail[portctr] rdlong t2, offset1 ' t2 = txtail[portctr] in hub memory cmp t1, t2 wz ' byte(s) to tx? if_e ret alts portctr, #p_txbuf ' get txbuf[portctr] in hub memory mov t1, 0 ' t1 := txhub[portctr] add t1, t2 ' add tail index rdbyte t3, t1 ' t3 := txhub[txtail] wypin t3, txd ' load into sp uart incmod t2, #(BUF_SIZE-1) ' update tail index _ret_ wrlong t2, offset1 ' write tail index back to hub
Everything up to this point appears to be working right, the correct txpin is being used but I think I'm messing up in the way I'm using the p_txhead pointer array in the cog. I thought that this points back to the hub memory location for the txhead value but the DEBUG values are not what I'm expecting. Monitoring the txpin with a logic analyzer isn't giving me the expected output either. Obviously something is being sent to the tx pin so the tx_serial loop is triggered and I see the start bit on the analyzer but only '1's' are showing up after that.
I'm hoping some of the PASM gurus can see the error of my ways!
Comments
I doubt you need both VAR and DAT sections for the same globals.
ALTx prefixing instructions modify the subsequent instruction's S/D register fields. This primary is for performing variable indirection within cogRAM. The prefixing cannot convert an instruction that accesses cogRAM into one that accesses hubRAM.
So, instead of this:
maybe this:
PS: I haven't actually used the hubRAM indexing feature myself. It requires using either PTRA or PTRB. It is limited to 32 positive indexes (0..31). Sadly there is also another negative 32 indexes that no-one is ever likely to use.
I’m not sure I really understand about not needing the VAR and DAT global? I understood that to use the SETQ and RDLONG for a block copy, the VAR and DAT variables had to be in the same order and size.
I was using the ALTS command to add the portctr and p_txtail values and put the result in the offset1 variable. I wanted to read the hub array txtail[] value using the pointer. I’m not sure how using ptrb register accomplishes this?
I didn’t use direct reference to the ptra register like it was used in the FullDuplexSerial.spin2 code because of limitations on reading ptra values which block transferring lots of values to the cog. I thought I might be able to get around that limitation using a series of arrays but haven’t been getting the hoped for results.
I see that Chip just released the new version of DEBUG using Pnut, I will have to try that out and see if that can help pinpoint the problems I’m having. Just knowing the memory location and values for the arrays would be very helpful to figure out where I am accessing the wrong values.
You're accessing the wrong memory altogether. The ALTS is incapable of referencing hubRAM.
#p_txtail
is invalid there.The VAR/DAT comment was just advise. It looks like double handling.
EDIT: Actually, the fact you aren't getting a compile error says the value of #p_txtail is not correct. To be safe, we both should have used an
@
to ensure it is a hubRAM address. eg:EDIT2: Ah, Smile, even that won't work in Pnut/Proptool's Spin2. Compile time knowledge isn't there. Only can be passed in from runtime Spin2 knowledge. Immediate assignments for hubRAM addresses, like above, aren't an option.
It's interesting, hubRAM tables with indexed references can be built at compile time because they use relative referencing between themselves. But to actually read such tables you still need a base runtime reference.
[maybe I should read the full source up there ]
Did not know ALTS can’t access hubRAM, I’ll go back and read the instruction description again. Also will check out the LOC instruction.
Okay, I got too excited there. Nothing wrong with the ALTS, it's correctly referencing the array of
p_txtail
pointers that have been copied into cogRAM by the earlier SETQ+RDLONG.I think where you may have gone wrong is those pointers are not to heads and tails of the actual buffers. They seem to be pointers to pointers of the buffers, ie: double indirection. That's a bug in the
addport()
Spin2 code elsewhere.Oh, you don't need the
offset1
variable at all in there. This should work:Thanks, I’ll try out this and your other suggestions tomorrow. I tend to initially use a lot of variables just to help keep things straight until I get the code to work. Then I look at optimizing after that.
Um, I've just started compiling your code, removed a lot of the debug() clutter, and it seems to be working. The double indirection isn't what I thought.
I think the counting sequence starting from 2 is exactly what your tx() routine is sending.
And I'm seeing valid serial data on the pin output too.
I agree, I did put a lot of DEBUG statements in there during testing! Interesting that you are seeing the correct output and I’m not seeing it on the logic analyzer. Time for more troubleshooting, maybe try out Chip’s single step debugger. What changes did you make to the code so far?
Just the debug()s. Other than the removals, I modified one for that print out:
EDIT: And I added a couple to
addport()
Maybe I used flexspin rather than pnut too. I doubt that is the reason though. I'm suspicious you might not have used the analyser's trigger correctly. There is the relatively large pause between each character. That'll translate to lots of $FFs.
I ran the code using the new version of Pnut and checked the analyzer trigger. I removed the 100ms delay in the txtest() method to get faster responses on the analyzer. So now I see an output but not the expected values (should be simply outputting a count from 0-9), it seems the output value is all over the place. I'll try out the new single step debugging in Pnut and see where that takes me.
Finally got back to the serial code and working with the single step debugger. I really like the new functionality in Pnut, hope to see it in the PropTool soon!
Troubleshooting the assembly code it appears to be working correctly and the test program (output values 1-9 on pin 6) show the incrementing values going to the output pin 6 but the logic analyzer is only showing an output value 63. I simplified the test program to only enable a single port (0) and testing the transmit code (will work on recieve side next). The transmit code appears to be working but I'm not seeing the expected values on the output pin. Hoping someone can see what I'm doing wrong.
I had to fix a few errors with the chopped down source but that one is working fine, the correct data is appearing on the pins, same as previously. Counting from 1 up to 9 and repeating. So far I've not had any issues with its transmit.
Here's what I'm compiling right now. I've reinstated a 200 ms pause between bytes, so I can see each byte triggered one by one on the scope, and I'm using pins 8 and 9.
PS: You'll probably need to remove the DEBUG_BAUD constant I've added. Pnut 35t (the latest) has a bug itself, that it can't handle anything other than default baud.
Using the scope's trigger means I'm not looking for each byte in one huge capture. Each byte is a separate capture.
Interesting, I only get a decimal value of 15 on the output running the copy you loaded on my analyzer. Maybe the problem is in my analyzer? I'll try out another analyzer software and my o-scope.
Using another software package for the analyzer I get the expected output values being transmitted! So the problem was at my end with reading the data.
I enabled an additional port and saw some issues to work through, looks like the adddport() method isn't setting up the arrays correctly so back to troubleshooting
I tried this change which broke the code, it seems like it should work but I'm not seeing the problem yet. I put the offset1 back in and it worked again, I'll look into this again later.
Started testing the txserial PASM code with more than a single port open. With 1 or 2 open ports the code works and I get good output on the analyzer. But when a 3rd port is added the code breaks, port 1 output no longer shows the count correctly, numbers are all over the place. Port 2 and 3 outputs are both empty. So far I haven't seen the cause but I suspect I'm over running/overwriting one of the arrays with the 3rd port. I've been single-stepping thought the code but not seeing where the problem originates.
Ah, I later noticed value of
offset1
was additionally used further down. It would've needed two such edits to eliminate its use. So, in that respect, it is best as is.Sorry, I'd made too many off-the-cuff remarks back there. Once I actually tried using your code, it worked well.
I really appreciate your input, its caused me to investigate other PASM commands like LOC (doesn’t appear it will work with the size of the arrays I’m using but good to know about anyway).
LOC doesn't work because Spin functions/data, at compile time, don't have a known address in hubRAM. They are only resolved at run-time. My lack of experience in doing arrays with inline Pasm caught me out there.
LOC is great for pure Pasm programs. I think Eric's Flex suite can use them too but you lose Pnut/Proptool compatibility if you do.