I’ve been watching your progress with great interest and looking over your approach to the coding. I’ve learned a few things and it’s given me some ideas for parts of my code that I wasn’t sure how to proceed. My Version has been one of slow progress, lots of notes and code tidbits written down on paper. Plan is to start putting it all on the computer and see how it comes out. I have a bit of a different approach to doing multi-ports, at this time I’d just want to see if I can finish it up as working code.
I've been glued to my computer translating several pages of notes finally into code. I've run into a problem that I've been unable to figure out how to code for a while now.
I added a note in the DAT pasm section where I'm stuck on the rx_serial section. I've hit a mental block on what I need to do to get the head value from the serdata array and then write it back again. Thanks for any help!
rx_serial testp rxd wc ' anything waiting on smartpin?
if_nc ret
rdpin t3, rxd ' read new byte
shr t3, #24 ' align lsb
mov t2, #BUF_SIZE ' get port offset for rxbuf
mul t2, #portctr ' t2 is rxbuf offset for current port
mov t1, p_rxbuf[t2] ' t1 := @rxbuf at current port buffer area
mov t4, #RXHEAD_OFFSET ' get offset to rxhead
add t4, p_port ' add offset to port data location
' ------------ getting stuck here on how to get rxhead value and then write it back to rxhead ???????
rdlong t2, ptra[4] ' t2 := rxhead
add t1, t2
wrbyte t3, t1 ' rxbuf[rxhead] := t3
incmod t2, #(BUF_SIZE-1) ' update head index
_ret_ wrlong t2, ptra[5] ' write head index back to hub
This is based on the jm_fullduplexserial.spin2 object, I have edited everything above the area I got stuck on, the rest of the code is original coding I am using as a guide
interesting how the new CODE block colors the entry
With a fresh day I went back and saw some mistakes I made and corrected the rx_serial routine enough that the code at least compiles. Tomorrow I’ll start fresh on the tx_serial routine.
I’m still a bit hazy on a concept that I haven’t seen an example to study yet. In this code I set up an array. I’ve got a pointer to the first element in the array and offsets defined as constants. What is the right way to increment a pointer to a specific item? I thought using MOV and ADD was the right way but that those work on the data the address is pointing at. Is there a command for incrementing an address a specific amount?
Use the head and tail where they are offsets into the bufer, initially beginning with 0. An INCMOD can be used to add and wrap-around. You will need to know the size (maximum before returning to 0 for head and tail indexes). A good example is JonnyMac's jm_fullduplexserial.
Use hub address pointers, where both head and tail start with the first buffer address. When they are incremented, a compare with the last+1 hub buffer address is done and if so, the address is reset to the first address. An example is my mpx_multiportserial.
As you've probably seen, it's tricky to ensure you get it right. Both the above examples have both spin and pasm examples.
Thanks for the information! At first I didn’t really understand your answer but as I was writing out my response the light started to come on, at least for item #1.
INCMOD as I understand how it works is that it increments the data value specified by D. So if the D field is actually an address this increments to the next address. I was looking at INCMOD just for incrementing a data value (1,2,3,4, etc), didn’t think about the D Value could be an address also.
I see that in the DAT section you can do a block copy using SETQ followed by the number of longs to copy minus 1. Then the data in the ptra register points to the first entry in that block. So using ADD ptra, #1 increments the ptra data (which is an address) by 1 so the D field holds the new address value.
It is legal syntax to write things like ptra[x] and return the data from that address. But when I tried something like p_array[x] in pasm I get a compiler error. Is ptra a special case or is it more likely that I don’t have a proper address value for p_array?
Yes, PRTA and PTRB are special for using the indexed values ptrx[..]. Also, these only apply to rdxxxx and wrxxxx instructions. See Chip’s documentation.
Ptra uses the hub address to point to variables linked via cogint. I can use rdxxx in pasm to copy the data at the hub address ptra is pointing at to a pasm variable. So how do I get the new cog address of the pasm variable?
@Cluso99 said:
Not sure what you're asking Bob.
You've read into a register variable using rdlong xxx,ptra[n] so now it's in a register named xxx or whatever you called it.
I’m probably not explaining myself well or not using the right terminology.
After I copy a variable value from hub memory into cog memory into a register named xxx, how do I then determine the cog memory address for that register? In Spin2 I can use ‘ptr := @variablename’ to get the address of a variable. How do you do the same thing in pasm?
Thank you for the example. I didn’t know that # could return the cog address, I’ll go back and reread the P2 docs again, must have missed or more likely misunderstood the information.
I've got a starting framework for a multi-port serial object that I want to share. Please look it over and see if the code is actually doing what my notes say it is supposed to be doing! I am including the most relevant portions of the code at this time. This is all based on JonnyMac's jm_fullduplexserial object. I still have to add the code for rx_serial and tx_serial in pasm but figuring out the data framework and how to access it inside of pasm has been a learning experience.
This is the initial variable setup. At this point I am restricting it to a max of 8 open ports but by changing the MAX_PORT value you could open a lot more.
con
BUF_SIZE = 64 ' size of rx and tx buffers
MAX_PORT = 8 ' set to number of ports to be used (1-8)
STRUCT_SIZE = 9 ' # array elements used
RX_OFFSET = 0
TX_OFFSET = 1
RXHUB_OFFSET = 2
TXHUB_OFFSET = 3
RXHEAD_OFFSET = 4
RXTAIL_OFFSET = 5
TXHEAD_OFFSET = 6
TXTAIL_OFFSET = 7
TXDELAY_OFFSET = 8
var
long cog ' cog flag/id
long num_ports ' total number of open ports (0-7)
long serialdata[STRUCT_SIZE * MAX_PORT] ' array for serial variables
byte rxbuf[BUF_SIZE * MAX_PORT] ' buffers - set MAX_PORT to actual open ports
byte txbuf[BUF_SIZE * MAX_PORT]
addport is run for each port to be opened. It adds data into serialdata array and sets up the smartpins.
The routine returns the assigned port number.
pub addport(rxpin, txpin, mode, baud) : result | baudcfg, spmode, rxt, txt, tdelay
'' add additional ports - call before start. miniumum 1 port required
'' load serialdata array and setup smartpins
'' does not check for port duplication
'' run addport at least once before calling start method
'' returns port id (0 to MAX_PORT-1), -1 on error
'' -- rxpin... receive pin (-1 if not used)
'' -- txpin... transmit pin (-1 if not used)
'' -- mode.... %0xx1 = invert rx
'' %0x1x = invert tx
'' %01xx = open-drain/open-source tx
if (rxpin == txpin) ' pins must be unique
return -1
'' check if pin used by any other ports
if (num_ports > MAX_PORT-1) ' port in range
return -1 ' error, initializing too many ports
abort
else
return num_ports ' return port number (0-7), -1 if error
baudcfg := muldiv64(clkfreq, $1_0000, baud) & $FFFFFC00 ' set bit timing
baudcfg |= (8-1) ' set bits (8)
rxt := (num_ports * STRUCT_SIZE) + RX_OFFSET ' calculate offset
longmove(serialdata[rxt], rxpin, 1) ' save pin in array
if (serialdata[rxt] >= 0) ' configure rx pin if used
spmode := P_ASYNC_RX
if (mode.[0])
spmode |= P_INVERT_IN
pinstart(serialdata[rxt], spmode, baudcfg, 0)
txt := (num_ports * STRUCT_SIZE) + TX_OFFSET
longmove(serialdata[txt], txpin, 1)
if (serialdata[txt] >= 0) ' configure tx pin if used
spmode := P_ASYNC_TX | P_OE
case mode.[2..1]
%01 : spmode |= P_INVERT_OUTPUT
%10 : spmode |= P_HIGH_FLOAT ' requires external pull-up
%11 : spmode |= P_INVERT_OUTPUT | P_LOW_FLOAT ' requires external pull-down
pinstart(serialdata[txt], spmode, baudcfg, 0)
serialdata[(num_ports * STRUCT_SIZE) + RXHUB_OFFSET] := @rxbuf[num_ports * BUF_SIZE] ' ptr to rx and tx hub buffer address
serialdata[(num_ports * STRUCT_SIZE) + TXHUB_OFFSET] := @txbuf[num_ports * BUF_SIZE]
tdelay := (num_ports * STRUCT_SIZE) + TXDELAY_OFFSET
serialdata[tdelay] := clkfreq / baud * 11 ' tix to transmit one byte
num_ports++ ' increment port counter
pub start() : result
'' Start new cog
stop()
cog := coginit(COGEXEC_NEW, @uart_mgr, @num_ports) + 1 ' start uart manager cog
return cog
Here is the PASM code. The idea is to read the port values from the serialdata array for each port in turn ,
copy the port data to variables and then act on the values.
dat { smart pin uart/buffer manager }
org
uart_mgr
' read hub variables into cog, get # of open ports
' and structure size to determine size of serialdata array
' to copy to cog ram
rdlong portnum, ptra ' get # of open ports
add ptra, #4
mov t1, #STRUCT_SIZE ' get number of longs to read from hub serdata array
mul t1, portnum ' STRUCT_SIZE * #ports is size of serdata array
setq t1 -1 ' block copy serdata to cog
rdlong t2, ptra ' read first item in array
mov p_buf, #t2 ' get address for array start
' loop through each port one at a time to get rx, tx, baud,
' p_rxbuf, and p_txbuf
uart_main mov portctr, #0 ' set portctr to 0
' portctr loops through each open port
.loop mov t1, #STRUCT_SIZE ' get port data in serdata array
mul t1, #portctr ' t1 has offset value in p_buf for p_port
mov p_port, #p_buf ' put array start point into p_port
add p_port, #t1 ' add offset to get p_port address at start of port
' get offsets for array values
mov p_offset, #p_port ' copy port data start address to offset
add p_offset, #RX_OFFSET ' add rx pin offset
mov rxd, p_offset ' put rx pin in rxd
mov p_offset, #p_port ' copy data block start location to offset
add p_offset, #TX_OFFSET ' add offset to point to tx pin
mov txd, p_offset ' put tx pin in txd
mov p_offset, #p_port ' copy data block start location to offset
add p_offset, #RXHUB_OFFSET ' add offset to point to rxbuf
mov p_rxbuf, p_offset ' hub ram location in p_rxbuf for rxbuf
mov p_offset, #p_port ' copy data block start location to offset
add p_offset, #TXHUB_OFFSET ' add offset to point to txbuf
mov p_rxbuf, p_offset ' hub ram location in p_txbuf for txbuf
' check if data available at smartpins. If data
' 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 wz ' check if portctr = open ports
if_nz call #.loop ' repeat loop for each open port
jmp #uart_main
rx_serial 'next area to work on
tx_serial
' --------------------------------------------------------------------------------------------------
portnum long 1 ' total # ports
p_buf long 1 ' ptr to cog serialdata array
p_port res 1 ' ptr to specific port in serdata array
p_offset res 1 ' offset pointer
p_rxbuf res 1 ' pointer to rxbuf
p_txbuf res 1 ' pointer to txbuf
portctr res 1 ' loop counter
rxd res 1 ' rx pin
txd res 1 ' tx pin
t1 res 1 ' work vars
t2 res 1
t3 res 1
t4 res 1 '
The previous entry got so slow I wasn’t able to add any additional comments. The code isn’t complete but I wanted see if some of our p2pasm experts could look it over and see if the coding is correct for what I am trying to accomplish. I put in a lot of notes to help myself as I was attempting to increment through cog ram to access values in the serial data array.
My wife is telling me to get off the computer and get dinner out so I’ll leave now!
You haven't allocated enough space for t2 when you doe the setq block read.
Didn't have time to check the calcs but you seem to be doing a lot of manipulation of the register addresses instead of the contents. Not sure if this is correct. I have always found to let the code do a dry run and outputting data and address dumps to the serial. Chips debug should be good for this.
Yeh, lots of manipulation of register addresses in there! The idea is that p_buf points to the first item in an array. Each port has 9 entries in the array, arranged one after another. So using p_buf address as the entry point to the array the first port is the first 9 addresses, the next port is the following 9 addresses and so on. The p_port points to the address of the first entry of a specific port and the p_offset then gets the value from that address offset.
Good point about t2, I need to define a variable big enough to hold the entire array which can be up to 72 longs (9 elements X 8 ports). Is there a way to dynamically set the variable size in case I’m dealing with less than 8 ports?
I really should run this through Pnut and use Chip’s debug to see if my assumptions are right. I mostly use the Prop Tool and have been hoping that Jeff gets then debug version out soon (according to last nights meeting we should see it pretty soon).
After a lot of false steps from trying to figure out pasm, I finally did what I should have done a while back. I opened my code in Pnut and started using the DEBUG commands to see what was really happening inside the registers. With this additional information many things that I had been making assumptions on have become much clearer. The ability to easily make changes on the fly and see the results has made a huge difference in helping make sense of these commands and how they interact with each other. I’m still a long way from finishing up my version of a multi-port serial object but today was the first time I felt I was making some real progress.
Now I just hope Parallax gets the Debug working in the Propellor Tool soon!
@DiverBob said:
...I opened my code in Pnut and started using the DEBUG commands ... I just hope Parallax gets the Debug working in the Propellor Tool soon!
While we wait for Debug on Prop Tool (it's supposed to be coming very soon according to some posts I've seen), you might want to check Flexprop since it already implements the basic debug commands. I have been using it for exactly this reason as a complement to Prop Tool since Pnut is too barebones for me (no syntax highlighting, weird scrolling and tab spacing).
I didn’t realize FlexProp had Debug added to itself. I had used it a while back when I was trying out C programming (because C had Sin, Cosine, and Tan available in the math module) and signed up to be a Patreon since I liked what I saw. Time to revisit the program.
Had some interesting times yesterday with Pnut. Opened my test Spin2 program and all my edits were gone. Realized that I accidentally had the same program open in the Prop Tool so all my saves were not actually saved. So I had to start from scratch again. Unfortunately I ran into an issue where I added another debug entry and it won’t come up on the terminal. All the earlier debug lines show up still but anything new doesn’t. I thought the docs say you can have up to 256 debug lines (I have around 10 debug statements) so not sure why this is happening. I got distracted after my Firefox on the Win10 computer refused to open any Parallax forum or the developer docs so I shut everything down for the day in frustration. Ready to give it another go today and hopefully everything will start working again.
It took a while but finally got my windows 10 computer to play nice again. Unfortunately I'm still not understanding something basic in pasm coding, mainly copying variables from Hub to Cog.
con
' serial specific constants
BUF_SIZE = 64 ' size of rx and tx buffers
MAX_PORT = 2 ' set to number of ports to be used (1-8)
var
long cog ' cog flag/id
long num_ports ' total number of open ports (0-7)
long rxdata[MAX_PORT] ' rx pin array
long txdata[MAX_PORT] ' tx pin array
long rxhub[MAX_PORT] '
long txhub[MAX_PORT] '
long rxhead[MAX_PORT]
long rxtail[MAX_PORT]
long txhead[MAX_PORT]
long txtail[MAX_PORT]
long txdelay[MAX_PORT]
byte rxbuf[BUF_SIZE * MAX_PORT] ' buffers - set MAX_PORT to actual open ports
byte txbuf[BUF_SIZE * MAX_PORT]
byte pbuf[80] ' padded strings
pub main()| t
' used during testing for setup only
t := addport(5, 6, %0000, BR_TERM) ' 1st open port
t := addport(21, 22, %0000, BR_TERM) ' 2nd open port
debug(udec(rxdata[0]))
debug(udec(rxdata[1]))
debug(udec(txdata[0]))
debug(udec(txdata[1]))
debug(udec(rxhub[0]))
debug(udec(rxhub[1]))
start()
pub addport(rxpin, txpin, mode, baud) : result | baudcfg, spmode, tdelay
'' add additional ports - call before start. miniumum 1 port required
'' load serialdata array and setup smartpins
'' does not check for port duplication
'' run addport at least once before calling start method
'' returns port id (0 to MAX_PORT-1), -1 on error
'' -- rxpin... receive pin (-1 if not used)
'' -- txpin... transmit pin (-1 if not used)
'' -- mode.... %0xx1 = invert rx
'' %0x1x = invert tx
'' %01xx = open-drain/open-source tx
if (rxpin == txpin) ' pins must be unique
result := -1
'' check if pin used by any other ports
if num_ports > (MAX_PORT-1) ' port in range
result := -1 ' error, initializing too many ports
abort
else
result := num_ports ' return port number (0-7), -1 if error
txdelay[num_ports] := clkfreq / baud * 11 ' tix to transmit one byte
baudcfg := muldiv64(clkfreq, $1_0000, baud) & $FFFFFC00 ' set bit timing
baudcfg |= (8-1) ' set bits (8)
rxdata[num_ports] := rxpin ' save rx pin
if rxdata[num_ports] >= 0 ' configure rx pin if used
spmode := P_ASYNC_RX
if (mode.[0])
spmode |= P_INVERT_IN
pinstart(rxpin, spmode, baudcfg, 0)
txdata[num_ports] := txpin ' save tx pin
if txdata[num_ports] >= 0 ' configure tx pin if used
spmode := P_ASYNC_TX | P_OE
case mode.[2..1]
%01 : spmode |= P_INVERT_OUTPUT
%10 : spmode |= P_HIGH_FLOAT ' requires external pull-up
%11 : spmode |= P_INVERT_OUTPUT | P_LOW_FLOAT ' requires external pull-down
pinstart(txpin, spmode, baudcfg, 0)
rxhub[num_ports] := @rxbuf[num_ports * BUF_SIZE] ' ptr to hub buffer address
txhub[num_ports] := @txbuf[num_ports * BUF_SIZE]
num_ports++ ' increment port counter
Above is my setup in spin, the AddPort function works correctly and in this example puts rxdata[0] = 5, rxdata[1] = 21, txdata[0] = 6, txdata[1] = 22, and so on. Debug shows the values stored correctly.
dat { smart pin uart/buffer manager }
org
uart_mgr
' read hub arrays into cog, get # of open ports
' to copy to cog ram
rdlong portnum, ptra++ ' get # of open ports
' debug(udec(portnum))
mov t1, #MAX_PORT ' get number of longs to read
mul t1, #4 ' multiply by 4 seperate arrays to copy
' debug(udec(t1))
setq t1 -1 ' block copy serdata to cog
rdlong rxbuff, ptra
debug(udec_long(rxbuff))
uart_main ' loop through each port one at a time to get rx, tx
' portctr loops through each open port
mov portctr, #0 ' set portctr to 0
debug(udec(portctr)) ' portctr loops through each open port ----no debug statements show up in the terminal window starting here
.loop ' get offsets for array values
alts portnum, #rxbuff
mov rxd, 0 ' copy data block start location to offset
debug(udec(rxd)) ' portctr loops through each open port
mov t1, txbuff ' copy start address
add t1, #portnum ' add in offset
mov txd, t1 ' copy data block start location to offset
' check if data available at smartpins. If data
' 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 wz ' check if portctr = open ports
if_nz call #.loop ' repeat loop for each open port
jmp #uart_main
'----------------------------------------------- no changes to original pasm code past here yet ----------------------------------
rx_serial testp rxd wc ' anything waiting on smartpin?
if_nc ret
rdpin t3, rxd ' read new byte from smartpin
shr t3, #24 ' align lsb
mov offset, p_rxbuf
add offset, #portnum
mov t1, offset ' t1 := @rxbuff[portnum]
' t2 := rxhead
_ret_ wrlong t2, offset ' write rxhead index back to hub
tx_serial rdpin t1, txd wc ' check busy flag
if_c ret ' abort if busy
rdlong t1, ptra[7] ' t1 = txhead
rdlong t2, ptra[8] ' t2 = txtail
cmp t1, t2 wz ' byte(s) to tx?
if_e ret
mov t1, p_txbuf ' start of tx buffer
add t1, t2 ' add tail index
rdbyte t3, t1 ' t3 := txbuf[txtail]
wypin t3, txd ' load into sp uart
incmod t2, #(BUF_SIZE-1) ' update tail index
_ret_ wrlong t2, ptra[8] ' write tail index back to hub
' --------------------------------------------------------------------------------------------------
portnum long 1 ' total # ports
rxbuff long 0[2] ' rx pin array
txbuff long 0[2] ' tx pin array
p_rxbuf long 0[2] ' ptr to rxhub
p_txbuf long 0[2] ' ptr to txhub
portctr res 1 ' loop counter
rxd res 1 ' rx pin
txd res 1 ' tx pin
offset res 1
t1 res 1 ' work vars
t2 res 1
t3 res 1
t4 res 1 '
The pasm section is based on the JonnyMac fullduplexserial code. At the beginning the rdlong reads in the correct portnum value. That is used to calculate the number of longs for the block copy using setq.
However the debug to read the first value in rxbuff I thought would be '5' (from rxdata[0]) is showing as 772_538_522 instead. Either I'm not using debug correctly or I missed something on the block copy to the cog.
Another thing that happens is that although the code compiles without error, the debug statements past reading the debug for rxbuff show up in the debug terminal window. I was using the debug output to work my way through the pasm code and got to this stopping point and not sure how to proceed.
I have not checked the whole code but some things have caught my eyes.
mov t1, #MAX_PORT ' get number of longs to read
mul t1, #4 ' multiply by 4 seperate arrays to copy
setq t1 -1 ' block copy serdata to cog
rdlong rxbuff, ptra
That does not work and is overcomplicated. You would need a sub t1,#1 instead of t1 -1 (this accesses the variable before t1), but because MAX_PORT is a constant anyway, you can just write:
You also use #portnum later in the code, while portnum is a register you load at begin. You want the register value and not the address of portnum, so use it without the #.
Another problem, I see: You read the spin variables only once at begin, but some of them will change (the heads and tails), so you need to read them again in the loop.
Thanks for looking at my code and your comments, I’ll try your suggestion out as soon as I get back on the computer.
I haven’t gotten down to making any changes to the part on the heads and tails part yet, I’ve just been modifying JonnyMac’s code as I go along. I have been having difficulties just getting the spin variables to read into pasm properly. I’ve been mostly working on the loop that checks the smart pins for a rx or tx byte. I’ll be hitting up the rx and tx sections once I know the beginning is working right.
mov t1, #MAX_PORT ' get number of longs to read
mul t1, #4 ' multiply by 4 seperate arrays to copy
setq t1 -1 ' block copy serdata to cog
rdlong rxbuff, ptra
That does not work and is overcomplicated. You would need a sub t1,#1 instead of t1 -1 (this accesses the variable before t1), but because MAX_PORT is a constant anyway, you can just write:
I tried out your suggestion and it works. I didn’t think about simplifying the code and put it all on one line with setq. Additionally the debug is also working past where it was stopping before.
The initial loop that cycles through the ports appears to work correctly so tomorrow I’ll start work on the rx_serial and tx_serial portions of the code.
Got a lot done today and finished up coding the RX and TX portions of pasm. Started testing the program as a whole and its obvious I have some mistakes in the pasm to figure out. Using debug it appears that the line incmod portctr, portnum wz isn't set up correctly as portctr should be cycling between 0 and the number of open ports-1, debug shows it cycling up indefinitely (not sure how to pause debug to troubleshoot this yet, need to review the debug docs again). When rx_serial and tx_serial sections are disabled the loop operates correctly. Not sure what is causing this to happen. Once portctr is out of the expected range of course that blows up the rest of the code.
Comments and suggestions are welcome!
After the line incmod portctr, portnum wz you have a call #.loop instead of a jmp #.loop.
I normally use the carry flag to detect the wrap around of incmod, but Zero flag should also work.
@Ariba said:
After the line incmod portctr, portnum wz you have a call #.loop instead of a jmp #.loop.
I normally use the carry flag to detect the wrap around of incmod, but Zero flag should also work.
Andy
I changed the call to jmp and used the carry flag also. I figured out the portctr issue, I was subtracting 1 from the portnum each time it looped through the first section. I didn't catch it because I put some stops in the code which masked that problem.
Testing the tx_serial loop by transmitting numbers 1 through 99. The smartpin (pin 6), debug isn't showing the expected value (some very large value constantly changing). I don't see any output on pin 6 per the logic analyzer. It appears to be in the tx_serial code but I'm can't figure it out even with lots of debug statements.
I woke up with some new ideas on parts I got stuck on last night, tried them out and got at least partial success today; the uart_mgr, uart_main, and tx_serial sections of pasm appear to be working now. I believe the last pasm loop, rx_serial, is also working but haven't figured out how to fully test that yet. I'm getting the expected values using debug so far.
I have tested at up to baud 115200 so far with no issues. The logic analyzer JonnyMac recommended is great for seeing the output. Once I'm sure the rx_serial is working then I'll start adding ports and see how many and fast I can go.
Any suggestions for improvements or how to test the receive code is welcome!
After reviewing the other 2 multiport objects I wanted to incorporate some ideas I got from looking over the code. In my version all the buffers are tied to the object and you only have to add how ever many ports using AddPort() before you start the cog. I ended up breaking just about every part of the code so it was a good test of the new Prop Tool debugging capabilities to figure out what was going on. I managed to get things more or less running, the transmit and receive sections are running but the transmitter is no longer outputting the expected values (logic analyzer is not picking up any output) although I can see the values coming into the code via debugging. The same goes for the receive section, it appears to be wanting to store incoming data in the right locations but I'm not getting the expected values.
After spending several hours on this today and not progressing, anyone good at P2 PASM willing to take a look and see where I'm going wrong? I appreciate any help!
I'd be glad to look at your code but I think it would be helpful to also have the parent object you're using for testing. An archive from the top object would be great. Edit: If the test code is the same as you used in post #1 then never mind. I'll use your earlier test code. Sorry, I should have check for earlier test code first.
Comments
@DiverBob
My multiport driver is now working on P2. You can have up to 8 fullduplexports or 16 of any receive and/or transmit pins/ports.
I’ve been watching your progress with great interest and looking over your approach to the coding. I’ve learned a few things and it’s given me some ideas for parts of my code that I wasn’t sure how to proceed. My Version has been one of slow progress, lots of notes and code tidbits written down on paper. Plan is to start putting it all on the computer and see how it comes out. I have a bit of a different approach to doing multi-ports, at this time I’d just want to see if I can finish it up as working code.
I've been glued to my computer translating several pages of notes finally into code. I've run into a problem that I've been unable to figure out how to code for a while now.
I added a note in the DAT pasm section where I'm stuck on the rx_serial section. I've hit a mental block on what I need to do to get the head value from the serdata array and then write it back again. Thanks for any help!
With a fresh day I went back and saw some mistakes I made and corrected the rx_serial routine enough that the code at least compiles. Tomorrow I’ll start fresh on the tx_serial routine.
I’m still a bit hazy on a concept that I haven’t seen an example to study yet. In this code I set up an array. I’ve got a pointer to the first element in the array and offsets defined as constants. What is the right way to increment a pointer to a specific item? I thought using MOV and ADD was the right way but that those work on the data the address is pointing at. Is there a command for incrementing an address a specific amount?
There are two ways I can think of....
As you've probably seen, it's tricky to ensure you get it right. Both the above examples have both spin and pasm examples.
Thanks for the information! At first I didn’t really understand your answer but as I was writing out my response the light started to come on, at least for item #1.
I see that in the DAT section you can do a block copy using SETQ followed by the number of longs to copy minus 1. Then the data in the ptra register points to the first entry in that block. So using ADD ptra, #1 increments the ptra data (which is an address) by 1 so the D field holds the new address value.
It is legal syntax to write things like ptra[x] and return the data from that address. But when I tried something like p_array[x] in pasm I get a compiler error. Is ptra a special case or is it more likely that I don’t have a proper address value for p_array?
Item 2 I need to study more.
Yes, PRTA and PTRB are special for using the indexed values ptrx[..]. Also, these only apply to rdxxxx and wrxxxx instructions. See Chip’s documentation.
New question!
Ptra uses the hub address to point to variables linked via cogint. I can use rdxxx in pasm to copy the data at the hub address ptra is pointing at to a pasm variable. So how do I get the new cog address of the pasm variable?
Not sure what you're asking Bob.
You've read into a register variable using rdlong xxx,ptra[n] so now it's in a register named xxx or whatever you called it.
I’m probably not explaining myself well or not using the right terminology.
After I copy a variable value from hub memory into cog memory into a register named xxx, how do I then determine the cog memory address for that register? In Spin2 I can use ‘ptr := @variablename’ to get the address of a variable. How do you do the same thing in pasm?
#xxx gives you the cogram address of the register xxx
if you want to copy some longs from hubmemory to cogmemory:
for such a direct copy there is a more efficient way with setq, but this is only
an example to show how you access hub and cog memory.
Andy
Thank you for the example. I didn’t know that # could return the cog address, I’ll go back and reread the P2 docs again, must have missed or more likely misunderstood the information.
I've got a starting framework for a multi-port serial object that I want to share. Please look it over and see if the code is actually doing what my notes say it is supposed to be doing! I am including the most relevant portions of the code at this time. This is all based on JonnyMac's jm_fullduplexserial object. I still have to add the code for rx_serial and tx_serial in pasm but figuring out the data framework and how to access it inside of pasm has been a learning experience.
This is the initial variable setup. At this point I am restricting it to a max of 8 open ports but by changing the MAX_PORT value you could open a lot more.
addport is run for each port to be opened. It adds data into serialdata array and sets up the smartpins.
The routine returns the assigned port number.
Here is the PASM code. The idea is to read the port values from the serialdata array for each port in turn ,
copy the port data to variables and then act on the values.
The previous entry got so slow I wasn’t able to add any additional comments. The code isn’t complete but I wanted see if some of our p2pasm experts could look it over and see if the coding is correct for what I am trying to accomplish. I put in a lot of notes to help myself as I was attempting to increment through cog ram to access values in the serial data array.
My wife is telling me to get off the computer and get dinner out so I’ll leave now!
Just had a quick look, nothing detailed.
You haven't allocated enough space for t2 when you doe the setq block read.
Didn't have time to check the calcs but you seem to be doing a lot of manipulation of the register addresses instead of the contents. Not sure if this is correct. I have always found to let the code do a dry run and outputting data and address dumps to the serial. Chips debug should be good for this.
Yeh, lots of manipulation of register addresses in there! The idea is that p_buf points to the first item in an array. Each port has 9 entries in the array, arranged one after another. So using p_buf address as the entry point to the array the first port is the first 9 addresses, the next port is the following 9 addresses and so on. The p_port points to the address of the first entry of a specific port and the p_offset then gets the value from that address offset.
Good point about t2, I need to define a variable big enough to hold the entire array which can be up to 72 longs (9 elements X 8 ports). Is there a way to dynamically set the variable size in case I’m dealing with less than 8 ports?
I really should run this through Pnut and use Chip’s debug to see if my assumptions are right. I mostly use the Prop Tool and have been hoping that Jeff gets then debug version out soon (according to last nights meeting we should see it pretty soon).
After a lot of false steps from trying to figure out pasm, I finally did what I should have done a while back. I opened my code in Pnut and started using the DEBUG commands to see what was really happening inside the registers. With this additional information many things that I had been making assumptions on have become much clearer. The ability to easily make changes on the fly and see the results has made a huge difference in helping make sense of these commands and how they interact with each other. I’m still a long way from finishing up my version of a multi-port serial object but today was the first time I felt I was making some real progress.
Now I just hope Parallax gets the Debug working in the Propellor Tool soon!
While we wait for Debug on Prop Tool (it's supposed to be coming very soon according to some posts I've seen), you might want to check Flexprop since it already implements the basic debug commands. I have been using it for exactly this reason as a complement to Prop Tool since Pnut is too barebones for me (no syntax highlighting, weird scrolling and tab spacing).
I didn’t realize FlexProp had Debug added to itself. I had used it a while back when I was trying out C programming (because C had Sin, Cosine, and Tan available in the math module) and signed up to be a Patreon since I liked what I saw. Time to revisit the program.
Had some interesting times yesterday with Pnut. Opened my test Spin2 program and all my edits were gone. Realized that I accidentally had the same program open in the Prop Tool so all my saves were not actually saved. So I had to start from scratch again. Unfortunately I ran into an issue where I added another debug entry and it won’t come up on the terminal. All the earlier debug lines show up still but anything new doesn’t. I thought the docs say you can have up to 256 debug lines (I have around 10 debug statements) so not sure why this is happening. I got distracted after my Firefox on the Win10 computer refused to open any Parallax forum or the developer docs so I shut everything down for the day in frustration. Ready to give it another go today and hopefully everything will start working again.
It took a while but finally got my windows 10 computer to play nice again. Unfortunately I'm still not understanding something basic in pasm coding, mainly copying variables from Hub to Cog.
Above is my setup in spin, the AddPort function works correctly and in this example puts rxdata[0] = 5, rxdata[1] = 21, txdata[0] = 6, txdata[1] = 22, and so on. Debug shows the values stored correctly.
The pasm section is based on the JonnyMac fullduplexserial code. At the beginning the rdlong reads in the correct portnum value. That is used to calculate the number of longs for the block copy using setq.
However the debug to read the first value in rxbuff I thought would be '5' (from rxdata[0]) is showing as 772_538_522 instead. Either I'm not using debug correctly or I missed something on the block copy to the cog.
Another thing that happens is that although the code compiles without error, the debug statements past reading the debug for rxbuff show up in the debug terminal window. I was using the debug output to work my way through the pasm code and got to this stopping point and not sure how to proceed.
I have not checked the whole code but some things have caught my eyes.
That does not work and is overcomplicated. You would need a sub t1,#1 instead of t1 -1 (this accesses the variable before t1), but because MAX_PORT is a constant anyway, you can just write:
You also use #portnum later in the code, while portnum is a register you load at begin. You want the register value and not the address of portnum, so use it without the #.
Another problem, I see: You read the spin variables only once at begin, but some of them will change (the heads and tails), so you need to read them again in the loop.
Andy
Thanks for looking at my code and your comments, I’ll try your suggestion out as soon as I get back on the computer.
I haven’t gotten down to making any changes to the part on the heads and tails part yet, I’ve just been modifying JonnyMac’s code as I go along. I have been having difficulties just getting the spin variables to read into pasm properly. I’ve been mostly working on the loop that checks the smart pins for a rx or tx byte. I’ll be hitting up the rx and tx sections once I know the beginning is working right.
I tried out your suggestion and it works. I didn’t think about simplifying the code and put it all on one line with setq. Additionally the debug is also working past where it was stopping before.
The initial loop that cycles through the ports appears to work correctly so tomorrow I’ll start work on the rx_serial and tx_serial portions of the code.
Got a lot done today and finished up coding the RX and TX portions of pasm. Started testing the program as a whole and its obvious I have some mistakes in the pasm to figure out. Using debug it appears that the line incmod portctr, portnum wz isn't set up correctly as portctr should be cycling between 0 and the number of open ports-1, debug shows it cycling up indefinitely (not sure how to pause debug to troubleshoot this yet, need to review the debug docs again). When rx_serial and tx_serial sections are disabled the loop operates correctly. Not sure what is causing this to happen. Once portctr is out of the expected range of course that blows up the rest of the code.
Comments and suggestions are welcome!
After the line
incmod portctr, portnum wz
you have acall #.loop
instead of ajmp #.loop
.I normally use the carry flag to detect the wrap around of incmod, but Zero flag should also work.
Andy
Thanks for looking over the code. Why is it better to use jump vs call in this instance?
I changed the call to jmp and used the carry flag also. I figured out the portctr issue, I was subtracting 1 from the portnum each time it looped through the first section. I didn't catch it because I put some stops in the code which masked that problem.
Testing the tx_serial loop by transmitting numbers 1 through 99. The smartpin (pin 6), debug isn't showing the expected value (some very large value constantly changing). I don't see any output on pin 6 per the logic analyzer. It appears to be in the tx_serial code but I'm can't figure it out even with lots of debug statements.
I woke up with some new ideas on parts I got stuck on last night, tried them out and got at least partial success today; the uart_mgr, uart_main, and tx_serial sections of pasm appear to be working now. I believe the last pasm loop, rx_serial, is also working but haven't figured out how to fully test that yet. I'm getting the expected values using debug so far.
I have tested at up to baud 115200 so far with no issues. The logic analyzer JonnyMac recommended is great for seeing the output. Once I'm sure the rx_serial is working then I'll start adding ports and see how many and fast I can go.
Any suggestions for improvements or how to test the receive code is welcome!
After reviewing the other 2 multiport objects I wanted to incorporate some ideas I got from looking over the code. In my version all the buffers are tied to the object and you only have to add how ever many ports using AddPort() before you start the cog. I ended up breaking just about every part of the code so it was a good test of the new Prop Tool debugging capabilities to figure out what was going on. I managed to get things more or less running, the transmit and receive sections are running but the transmitter is no longer outputting the expected values (logic analyzer is not picking up any output) although I can see the values coming into the code via debugging. The same goes for the receive section, it appears to be wanting to store incoming data in the right locations but I'm not getting the expected values.
After spending several hours on this today and not progressing, anyone good at P2 PASM willing to take a look and see where I'm going wrong? I appreciate any help!
Bob
I'd be glad to look at your code but I think it would be helpful to also have the parent object you're using for testing. An archive from the top object would be great.
Edit: If the test code is the same as you used in post #1 then never mind. I'll use your earlier test code. Sorry, I should have check for earlier test code first.