Update 22Jan2012: posted working code below. This will support up to 16 total uni-directional ports (ie up to 16 pins that may be any mix of transmit or receive).
JonnyMac made an amazing object (need a link) that includes formatting strings with binary/decimal/hex/etc. I've added a few personal bits like dumping hub memory with the usual 16 bytes per line in hex plus ASCII. I've starting breaking down the PASM driver into a separate object so that it can be loaded separately from the spin code. The reason for this is to be able to make the PASM driver able to stay resident when loading other objects, as is required when making a P2 OS (operating system). There are other benefits too, such as being able to use the spin code to format strings, etc, while changing the PASM driver underneath to say VGA and Keyboard, and still retain the hub buffer interface (ie hardware virtualisation).
The first part is to define the hub interface. This works well in Jon's code but I would like to take the time to expand on a couple of things before adding the multiple buffer sets required for each port. Here is the current interface for a single port...
var
long cog ' cog flag/id
long rxp ' rx smart pin
long txp ' tx smart pin
long rxhub ' hub address of rxbuf
long txhub ' hub address of txbuf
long rxhead ' rx head index
long rxtail ' rx tail index
long txhead ' tx head index
long txtail ' tx tail index
long txdelay ' ticks to transmit one byte
byte rxbuf[BUF_SIZE] ' buffers
byte txbuf[BUF_SIZE]
byte pbuf[80] ' padded strings
The real part of the interface is this section...
long rxhead ' rx head index
long rxtail ' rx tail index
long txhead ' tx head index
long txtail ' tx tail index
Since we have a reasonable 512KB, providing a few extra longs to convey some additional info would be nice. What might these be?
* tx and rx pin
* baud
I would also like to add in
* rxsize
* txsize
This would make the buffer sizes flexible tho we have to account for this.
The padded string(s) would be defined in the spin section so it would not be part of the buffer interface (or mailbox). And the driver cog number is probably not required here.
Question(s)...
* Would a word (16 bits) be good enough for the head, tail and size? This is 64KB after all !!!
Why? Well it depends on whether we want to pack the heads/tails/sizes for all ports into one contiguous group and the buffers separate to these. There are some advantages to using PTR[offset] for getting these values.
So here are two alternatives for packing the buffers and pointers (I've just used longs for now)...
Repeat for each port
long rxhead ' rx head index
long rxtail ' rx tail index
long txhead ' tx head index
long txtail ' tx tail index
long rxsize ' rx buffer size
long txsize ' tx buffer size
byte rxbuf[BUF_SIZE] ' buffers
byte txbuf[BUF_SIZE]
or
long rxhead1 ' rx head index
long rxtail1 ' rx tail index
long txhead1 ' tx head index
long txtail1 ' tx tail index
long rxsize1 ' rx buffer size
long txsize1 ' tx buffer size
long rxhead2 ' rx head index
long rxtail2 ' rx tail index
long txhead2 ' tx head index
long txtail2 ' tx tail index
long rxsize2 ' rx buffer size
long txsize2 ' tx buffer size
long rxhead3 ' rx head index
long rxtail3 ' rx tail index
long txhead3 ' tx head index
long txtail3 ' tx tail index
long rxsize3 ' rx buffer size
long txsize3 ' tx buffer size
long rxhead4 ' rx head index
long rxtail4 ' rx tail index
long txhead4 ' tx head index
long txtail4 ' tx tail index
long rxsize4 ' rx buffer size
long txsize4 ' tx buffer size
byte rxbuf1[BUF_SIZE1] ' buffers
byte txbuf1[BUF_SIZE1]
byte rxbuf2[BUF_SIZE2] ' buffers
byte txbuf2[BUF_SIZE2]
byte rxbuf3[BUF_SIZE3] ' buffers
byte txbuf3[BUF_SIZE3]
byte rxbuf4[BUF_SIZE4] ' buffers
byte txbuf4[BUF_SIZE4]
If you want to be able to start and stop ports at will, then the interface will likely need an extra interface for port number, txpin, rxpin, baud, txbuf and rxbuf addresses and txsize and rxsize for sizes, and a flag indicating if it is running. txpin, rxpin and flag can be bytes within a long, baud is a long, and the txbuf, rxbuf need to be a hub address so 20 bits minimum so a long each.
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_ncretrdpin t3, rxd ' read new byteshr t3, #24' align lsbmov t2, #BUF_SIZE ' get port offset for rxbufmul t2, #portctr ' t2 is rxbuf offset for current portmov t1, p_rxbuf[t2] ' t1 := @rxbuf at current port buffer areamov t4, #RXHEAD_OFFSET ' get offset to rxheadadd 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 := rxheadadd t1, t2
wrbyte t3, t1 ' rxbuf[rxhead] := t3incmod 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.
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 = 8varlong cog ' cog flag/idlong num_ports ' total number of open ports (0-7)long serialdata[STRUCT_SIZE * MAX_PORT] ' array for serial variablesbyte rxbuf[BUF_SIZE * MAX_PORT] ' buffers - set MAX_PORT to actual open portsbyte 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.
pubaddport(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 txif (rxpin == txpin) ' pins must be uniquereturn -1'' check if pin used by any other portsif (num_ports > MAX_PORT-1) ' port in rangereturn -1' error, initializing too many portsabortelsereturn 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 offsetlongmove(serialdata[rxt], rxpin, 1) ' save pin in arrayif (serialdata[rxt] >= 0) ' configure rx pin if used
spmode := P_ASYNC_RXif (mode.[0])
spmode |= P_INVERT_INpinstart(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_OEcase 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-downpinstart(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 counterpubstart() : result'' Start new cog
stop()
cog := coginit(COGEXEC_NEW, @uart_mgr, @num_ports) + 1' start uart manager cogreturn 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 ramrdlong portnum, ptra' get # of open portsaddptra, #4mov t1, #STRUCT_SIZE ' get number of longs to read from hub serdata arraymul t1, portnum ' STRUCT_SIZE * #ports is size of serdata arraysetq t1 -1' block copy serdata to cogrdlong t2, ptra' read first item in arraymov 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 arraymul t1, #portctr ' t1 has offset value in p_buf for p_portmov p_port, #p_buf ' put array start point into p_portadd p_port, #t1 ' add offset to get p_port address at start of port' get offsets for array valuesmov p_offset, #p_port ' copy port data start address to offsetadd p_offset, #RX_OFFSET ' add rx pin offsetmov rxd, p_offset ' put rx pin in rxdmov p_offset, #p_port ' copy data block start location to offsetadd p_offset, #TX_OFFSET ' add offset to point to tx pinmov txd, p_offset ' put tx pin in txdmov p_offset, #p_port ' copy data block start location to offsetadd p_offset, #RXHUB_OFFSET ' add offset to point to rxbufmov p_rxbuf, p_offset ' hub ram location in p_rxbuf for rxbufmov p_offset, #p_port ' copy data block start location to offsetadd p_offset, #TXHUB_OFFSET ' add offset to point to txbufmov 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_serialtestb rxd, #31wc' rx pin in use? -test for -1 when no pin savedif_nccall #rx_serial
testb txd, #31wc' tx in use? -test for -1if_nccall #tx_serial
' increment counter to next port. if portctr >' open ports, reset portctr to 0 and loopincmod portctr, #portnum wz' check if portctr = open portsif_nzcall #.loop ' repeat loop for each open portjmp #uart_main
rx_serial 'next area to work on
tx_serial
' --------------------------------------------------------------------------------------------------
portnum long1' total # ports
p_buf long1' ptr to cog serialdata array
p_port res1' ptr to specific port in serdata array
p_offset res1' offset pointer
p_rxbuf res1' pointer to rxbuf
p_txbuf res1' pointer to txbuf
portctr res1' loop counter
rxd res1' rx pin
txd res1' tx pin
t1 res1' work vars
t2 res1
t3 res1
t4 res1'
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)varlong cog ' cog flag/idlong num_ports ' total number of open ports (0-7)long rxdata[MAX_PORT] ' rx pin arraylong txdata[MAX_PORT] ' tx pin arraylong 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 portsbyte txbuf[BUF_SIZE * MAX_PORT]
byte pbuf[80] ' padded stringspubmain()| 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()
pubaddport(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 txif (rxpin == txpin) ' pins must be uniqueresult := -1'' check if pin used by any other portsif num_ports > (MAX_PORT-1) ' port in rangeresult := -1' error, initializing too many portsabortelseresult := 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 pinif rxdata[num_ports] >= 0' configure rx pin if used
spmode := P_ASYNC_RXif (mode.[0])
spmode |= P_INVERT_INpinstart(rxpin, spmode, baudcfg, 0)
txdata[num_ports] := txpin ' save tx pinif txdata[num_ports] >= 0' configure tx pin if used
spmode := P_ASYNC_TX | P_OEcase 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-downpinstart(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 ramrdlong portnum, ptra++ ' get # of open ports' debug(udec(portnum))mov t1, #MAX_PORT ' get number of longs to readmul t1, #4' multiply by 4 seperate arrays to copy' debug(udec(t1))setq t1 -1' block copy serdata to cogrdlong 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 portmov 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 valuesalts portnum, #rxbuff
mov rxd, 0' copy data block start location to offset
debug(udec(rxd)) ' portctr loops through each open portmov t1, txbuff ' copy start addressadd t1, #portnum ' add in offsetmov txd, t1 ' copy data block start location to offset' check if data available at smartpins. If data' available, jump to rx_serial or tx_serialtestb rxd, #31wc' rx pin in use? -test for -1 when no pin savedif_nccall #rx_serial
testb txd, #31wc' tx in use? -test for -1if_nccall #tx_serial
' increment counter to next port. if portctr >' open ports, reset portctr to 0 and loopincmod portctr, #portnum wz' check if portctr = open portsif_nzcall #.loop ' repeat loop for each open portjmp #uart_main
'----------------------------------------------- no changes to original pasm code past here yet ----------------------------------
rx_serial testp rxd wc' anything waiting on smartpin?if_ncretrdpin t3, rxd ' read new byte from smartpinshr t3, #24' align lsbmov 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 flagif_cret' abort if busyrdlong t1, ptra[7] ' t1 = txheadrdlong t2, ptra[8] ' t2 = txtailcmp t1, t2 wz' byte(s) to tx?if_eretmov t1, p_txbuf ' start of tx bufferadd t1, t2 ' add tail indexrdbyte t3, t1 ' t3 := txbuf[txtail]wypin t3, txd ' load into sp uartincmod t2, #(BUF_SIZE-1) ' update tail index_ret_wrlong t2, ptra[8] ' write tail index back to hub' --------------------------------------------------------------------------------------------------
portnum long1' total # ports
rxbuff long0[2] ' rx pin array
txbuff long0[2] ' tx pin array
p_rxbuf long0[2] ' ptr to rxhub
p_txbuf long0[2] ' ptr to txhub
portctr res1' loop counter
rxd res1' rx pin
txd res1' tx pin
offset res1
t1 res1' work vars
t2 res1
t3 res1
t4 res1'
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 readmul t1, #4' multiply by 4 seperate arrays to copysetq t1 -1' block copy serdata to cogrdlong 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:
setq #MAX_PORT*4-1' block copy serdata to cogrdlong rxbuff, ptra
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 readmul t1, #4' multiply by 4 seperate arrays to copysetq t1 -1' block copy serdata to cogrdlong 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:
setq #MAX_PORT*4-1' block copy serdata to cogrdlong rxbuff, ptra
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.
This is an embed external element. It can be deleted using the delete key or the backspace key. To view the full element, press the preview button below.
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.
P2 Multiport x16 Serial Driver working (based on JonnyMac's FullDuplexSerial)
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
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:
mov pb,#0 'buffer index .loop altd pb,#buffer 'modify destination of rdlong: pb+bufferaddr rdlong 0-0,ptra++ 'copy long incmod pb,#8 wc 'next cog addr, wrap inside 8 if_nc jmp #.loop buffer long 0[8] '8 longs in cogram initialized with 0
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.
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!
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.
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:
setq #MAX_PORT*4-1 ' block copy serdata to cog rdlong rxbuff, ptra
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.