Just been browsing through the kernel PASM code, you have done a great job in your implementation.
An immediate standout for me regarding speed of execution is your decision to do away with the SP and go for straight MOV's in the PUSH and POP functions for example.
It seems that most of the low level words jump to the DROP function which of course calls POPX which has 12 MOV's as immediate overhead ... I apologize if this has already been discussed ..
Be interested to hear why you have taken this approach rather than a stack pointer as a stack pointer results in push and pop having a lot less instructions ..
Please note this is not a criticism, just like to know how you arrived at that decision ...
Also, are there any forth words to load file(s), or does all compiling involve cut-and-paste?
Just been browsing through the kernel PASM code, you have done a great job in your implementation.
An immediate standout for me regarding speed of execution is your decision to do away with the SP and go for straight MOV's in the PUSH and POP functions for example.
It seems that most of the low level words jump to the DROP function which of course calls POPX which has 12 MOV's as immediate overhead ... I apologize if this has already been discussed ..
Be interested to hear why you have taken this approach rather than a stack pointer as a stack pointer results in push and pop having a lot less instructions ..
Please note this is not a criticism, just like to know how you arrived at that decision ...
Also, are there any forth words to load file(s), or does all compiling involve cut-and-paste?
Cheers,
Bernie
Decisions decisions decisions..... The Prop is a strange beast and the lack of indexed operations in PASM not just for the pushing and popping but also for all the operations that need to reference stack elements led me to think of keeping stack elements fixed as if they were fixed registers. That way I didn't have to constantly calculate where the operands were or save and restore a TOS register etc, it's always just there. Sure it meant doing a rather brute force move method to push and pop but at least that kept it all in the cog. Look at how efficient these operations now become:
Sure there's a trade-off but this implementation is definitely fast (the fastest?) as is the bytecode method of indexing the first 256 longs in cog memory.
[B][COLOR=#020FC0][FONT=Ubuntu Mono][B]doNEXT[/B][/FONT][/COLOR][COLOR=#000000][FONT=Ubuntu Mono] rdbyte X,IP 'read byte code instruction[/FONT][/COLOR]
[COLOR=#000000][FONT=Ubuntu Mono] add IP,#1 'advance IP to next byte token[/FONT][/COLOR]
[COLOR=#000000][FONT=Ubuntu Mono] jmp X 'execute the code by directly indexing the first 256 long in cog[/FONT][/COLOR][/B]
The other thing with any kinds of pointers is boundary checking as underflow and overflow can trash the code space so this way it intrinsically limited itself to the "stack". Can you see a better faster way of implementing PUSH and POP that always keeps it simple for all the other stack operations? I welcome the feedback and it's only a bonus if Tachyon can be made better.
The return and other stacks are implemented with self-modifying code to implement "indexed" addressing.
The other part of the implementation has to do with expressing the kernel in an easy to read source code form that runs in the Spin tool. That job was made a bit easier by using directly indexing bytecode so that the label corresponded to the cog address which could be written and compiled in a clear manner that kept clutter and calculations to an absolute minimum.
I've added some string operations to EXTEND.fth which may prove useful. Rather than the traditional counted strings I prefer null terminated strings plus they also consume less stack space. In this implementation the strings are long aligned in case that's useful and the byte before it is the bytecode which leaves the address of the string on the stack. I have also used the preceding bytes to hold the maximum allocated size of the string as well as an optional string count and attribute byte (not used at present).
To create a string constant or variable:
" MY NAME IS " $20 STRING NAME
To append to this:
" PETER" NAME APPEND$
Other operations include:
STRING Define a string
COPY$ Copy one string to another
APPEND$ Append one string to another
MID$ Return with string
LEFT$
RIGHT$
ERASE$ Erase the strings allocated space
LOCATE$ Locate the first character in the string else return with null
COMPARE$ Compare two strings and return with flag
+CHAR Append a character to the end of the string
Some of these operations may change as I seek to improve the definition of a string and how they operate.
Understand now, excellent. I did not realize that the Prop did not have indexed PASM instructions ..
I guess the only real trade-off is although as you say the words that don't modify the stack are now much faster, the words that do modify the stack, take the overhead of the POP or PUSH and are therefore slower ..
I guess it all boils down to which of these instructions are used the most frequent .... I am not sure myself, but Tachyon runs really fast, so I think you have made a good decision with your architecture ...
Regarding my other question, is there any file load words? or is all file loading/compiling by cut-and-paste ..
I've added some string operations to EXTEND.fth which may prove useful. Rather than the traditional counted strings I prefer null terminated strings plus they also consume less stack space. In this implementation the strings are long aligned in case that's useful and the byte before it is the bytecode which leaves the address of the string on the stack. I have also used the preceding bytes to hold the maximum allocated size of the string as well as an optional string count and attribute byte (not used at present).
To create a string constant or variable:
" MY NAME IS " $20 STRING NAME
To append to this:
" PETER" NAME APPEND$
Other operations include:
STRING Define a string
COPY$ Copy one string to another
APPEND$ Append one string to another
MID$ Return with string
LEFT$
RIGHT$
ERASE$ Erase the strings allocated space
LOCATE$ Locate the first character in the string else return with null
COMPARE$ Compare two strings and return with flag
+CHAR Append a character to the end of the string
Some of these operations may change as I seek to improve the definition of a string and how they operate.
Fantastic, going to go refactor, again. And if the words/functions change that's what :%s/oldstr/newstr/gc is for, the mouse can be crippling
But I have not seen any words that either A. execute via the terminal to "read" a file in from the local disk or B. Words to read and write "files" from the SD Card.
Can't speak for Peter but I'm pretty sure if you added file support to the SD library he wouldn't mind, me neither It's way beyond my FORTH at this time.
I coded the 1-Wire along the lines of Peter's suggestions and added Markus' suggestion. It all worked fine. Thanks, guys!
Using this as a guideline I set up for the I2C redirection using @I2CBOOT and @I2CMAIN. It all seems to work fine. Peter? Markus? Did I miss anything this time?
FYI.... I am doing a project based on a Prop Mini, which as a 32K EEPROM and no clean access to P28/P29 ..So I built my main I2C buss on P7/P6 with a total of 4 devices.
cheers ... BBR
\ ------- from EXTEND.fth -----------------
\ I/O "Constants" - variable to allow for redirection
#P28 |< CONSTANT SCL
#P29 |< CONSTANT SDA
\ Assign the Prop's EEPROM pins (default)
pub EEPROM
#P29 #P28
\ Assign the pins to use for the I2C bus
pub I2CPINS ( sda scl -- )
MASK ' SCL 1+ !
MASK ' SDA 1+ !
;
\ ------ I2C i/o re-direct extensions ---
\ ----- loaded at top of first file after EXTEND.fth -----
: @I2CBOOT ( -- ) \ sets I2C pins to the default buss with P29/P28
#P28 MASK ' SCL 1+ !
#P29 MASK ' SDA 1+ !
;
: @I2CMAIN ( -- ) \ sets I2C pins to the alternate, in this case P7/P6
#P6 MASK ' SCL 1+ !
#P7 MASK ' SDA 1+ !
;
I coded the 1-Wire along the lines of Peter's suggestions and added Markus' suggestion. It all worked fine. Thanks, guys!
Using this as a guideline I set up for the I2C redirection using @I2CBOOT and @I2CMAIN. It all seems to work fine. Peter? Markus? Did I miss anything this time?
FYI.... I am doing a project based on a Prop Mini, which as a 32K EEPROM and no clean access to P28/P29 ..So I built my main I2C buss on P7/P6 with a total of 4 devices.
cheers ... BBR
\ ------- from EXTEND.fth -----------------
\ I/O "Constants" - variable to allow for redirection
#P28 |< CONSTANT SCL
#P29 |< CONSTANT SDA
\ Assign the Prop's EEPROM pins (default)
pub EEPROM
#P29 #P28
\ Assign the pins to use for the I2C bus
pub I2CPINS ( sda scl -- )
MASK ' SCL 1+ !
MASK ' SDA 1+ !
;
\ ------ I2C i/o re-direct extensions ---
\ ----- loaded at top of first file after EXTEND.fth -----
: @I2CBOOT ( -- ) \ sets I2C pins to the default buss with P29/P28
#P28 MASK ' SCL 1+ !
#P29 MASK ' SDA 1+ !
;
: @I2CMAIN ( -- ) \ sets I2C pins to the alternate, in this case P7/P6
#P6 MASK ' SCL 1+ !
#P7 MASK ' SDA 1+ !
;
Hi Brian,
I'm wondering why you aren't using the I2CPINS word and simply saying:
You seem to be ignoring these functions and writing everything out in longhand.
As for @I2CBOOT isn't that what EEPROM does already?
On point #1, I2CMAIN ... d'oh ... conceded!
On Point t#2, I2CBOOT vs EEPROM .. to quote the master (that's you!) "readability and consistency" ... my 'MAIN buss on P7/P6 also has an EEPROM in it ... I2CBOOT better conveys the sense of it.
On Point t#2, I2CBOOT vs EEPROM .. to quote the master (that's you!) "readability and consistency" ... my 'MAIN buss on P7/P6 also has an EEPROM in it ... I2CBOOT better conveys the sense of it.
No probs Brian, and sometimes the art of Forth boils down to the choice of a name, does it convey the correct thought? At times it can be frustrating trying to come up with the right name and method of operation, one that is natural enough to read and natural enough for Forth.
The other part of the art since we are on the subject is factoring, and when that factor has the right name then hopefully a novice should be able to understand the program. Wouldn't it be good if we had a little switch so we could switch ourselves to novice mode just to make sure it makes sense! Of course conversely, the novice would probably like a guru switch as well!
Just browsing the above file and noticed a copy-paste error in the following function.
It calls FADC instead of the intended MADC....
Cheers,
Bernie
------------------------------------------------------------
'' Init and list all 8 channels
pub MLA
InitADC2 8 0
DO CR I .BYTE ." : " I FADC@ $0A .NUM LOOP
;
Has anyone used Tachyon on a Hive board? Will this Forth one day be able to support multiple Prop chips like this?
Seems to me "fun x 3" to have Tachyon running on this board .....
Has anyone used Tachyon on a Hive board? Will this Forth one day be able to support multiple Prop chips like this?
Seems to me "fun x 3" to have Tachyon running on this board .....
Cheers,
Bernie
I don't have any plans to support multiple Prop chips with Tachyon although I could probably use a similar approach that Propforth does. I have used my own version of inter-Prop coms over fibre-optic links so there is no reason that this can't be done over local links. However I like the Prop for it's simplicity and flexibility but I still regard it as a microcontroller and I can think of far better ways of implementing something like a hive board.
D.P. recently posted some example code for the 4D Systems uLCD-32PTU display module. Attached is an example of my approach to defining TACHYON drivers for the 32PTU.
\ ------- from EXTEND.fth -----------------
\ I/O "Constants" - variable to allow for redirection
#P28 |< CONSTANT SCL
#P29 |< CONSTANT SDA
\ Assign the Prop's EEPROM pins (default)
pub EEPROM [B]\ Brian, Peter ... [/B]
#P29 #P28 [B] \ if you really want to allow for redirection this should also read SDA SCL
\ you could have another EEPROM at other pins ... [/B]
[B] \ or EEPROM is just only the BOOTEEPROM [/B] Markus
D.P. recently posted some example code for the 4D Systems uLCD-32PTU display module. Attached is an example of my approach to defining TACHYON drivers for the 32PTU.
nick
Neat Nick, I will post code when I'm happy with the results. I have two tasks, one that reads object messages from the lcd (ltask, I'm using the GENIE interface), and one that writes object messages with updated values from the RTC, A2D, flame sensor etc. I have found the uLCD to be really finicky in an actual application but so far the two tasks are working nicely together. Also found that I needed to use WAITLOW with a timeout instead of WAITPNE for serial in or the screen would hang during extended testing periods? I might try WAITPNE again now that I have the two tasks nicely separated.
Your Tachyon/FORTH is nice and clean, thanks for the example!!
Just as a matter of interest, can you elaborate on what sort of fibre-optic link you used between Prop's? I am interested in connecting several CPU boards together with a "cheap" fat pipe(high bandwidth ...
It needs to have connectors that can stand a high vibration environment ...
I have had no problems reassigning i/o pins for for I2C and 1-Wire .. now what I need to attend to is console redirection. I have a Roving Networks RN XV WiFly card and it is setup to assert one pin when it has a Telnet connection then i/o is done via the di/do pins. Now I can ignore the connection pin and just hookup GND, DI, DO on the RN XV to the prop's P30/P31 and that works fine ... but what I want to do is run a task in a cog monitoring the connection pin and when it is detected reassign the console to the prop pins connected to DI/DO. Now, ideally there would be code to return the console to the local port, but typing REBOOT from the remote console will do for starters.
The ports of interest are referenced in the spin/pasm kernel as txd and rxd........ So far I don't seem to be able to find any way to get at them in Forth ...... is there any way? I'll accept quick and dirty for the moment.
I have had no problems reassigning i/o pins for for I2C and 1-Wire .. now what I need to attend to is console redirection. I have a Roving Networks RN XV WiFly card and it is setup to assert one pin when it has a Telnet connection then i/o is done via the di/do pins. Now I can ignore the connection pin and just hookup GND, DI, DO on the RN XV to the prop's P30/P31 and that works fine ... but what I want to do is run a task in a cog monitoring the connection pin and when it is detected reassign the console to the prop pins connected to DI/DO. Now, ideally there would be code to return the console to the local port, but typing REBOOT from the remote console will do for starters.
The ports of interest are referenced in the spin/pasm kernel as txd and rxd........ So far I don't seem to be able to find any way to get at them in Forth ...... is there any way? I'll accept quick and dirty for the moment.
cheers ... BBR
Hi Brian,
the txd side should not be a problem to change, since it is inside the running FORTH COG here:
' Registers used by PASM modules to hold parameters such as I/O masks and bit counts etc
REG0 long 0
REG1 long 0
REG2 long 0
REG3 long 0
REG4 long 0
txticks long (80_000_000 / baud ) ' set transmit baud rate
[B]txmask long |<txd ' change mask to reassign transmit[/B]
this can be changed using COG! using the REG4 address + 2 to store the new mask
newTxPin MASK 6 COGREG COG!
6 COGREG gives the address of the txmask COG register
the receive side is more difficult since the RX is running in it's own cog and the rx pin can not be changed afterwards.
IT might be possible to run a second receive cog in parallel to the first one with the second rx pin that you want to use,
if both receive alternatively.
OR you could adapt the receive cog, so you can dynamically change the rx-pin.
I'll have a closer look into the code later.
ok ... here is the start of the receive routine
I would suggest you specify a HUB location where you put the RX-mask
and use a simple
RxLoop
[COLOR=#020FC0][B] rdlong rxpin, @temp+12+s[/B][/COLOR] ' s.th. like this - you know PASM better than I do ...
' you could just use the last long of the temp register which is a 16 byte space
' or define an additional register long after the long registers - which I assume would work
' - but I don't oversee the side effects of doing this.
' this might cost you a little receive speed
call #receive
if_nc jmp #abreak ' discard if framing error (no stop bits)
cmp rxdata,#$7E wz ' special TILDE sequence command?
and then you just write to this temp register
newRxMask 12 REG + !
REG gives you the start of the register space and temp is equivalent to REG(0) so to use REG(4-long) add this 12 bytes
EDIT: of course this RXMASK needs to be initialized to the standard RX-pin-mask on startup.
I would put it here:
{ ****** MAIN CONSOLE TERMINAL ****** }
org ' align the label
TERMINAL
byte InitRP
byte XCALL,xInitStack
[SIZE=4][COLOR=#0000ff][B] byte _BYTE,rxd,MASK,REG,12,STORE[/B][/COLOR][/SIZE] ' REG,12 == TEMP(3)
byte _WORD,00,50,XCALL,xms ' a little startup delay (also wait for serial cog)
but Peter might be faster with a helpful answer anyhow ;-)
I have a Prop Prototype board with a 5Mhz Crystal. The INFO word shows the clock frequency at 40Mhz, isn't it mean't to be 80Mhz? Do I have to tweak the PLL?
I have a Prop Prototype board with a 5Mhz Crystal. The INFO word shows the clock frequency at 40Mhz, isn't it mean't to be 80Mhz? Do I have to tweak the PLL?
Cheers,
Bernie
Hi Bernie, you just need to make sure you have the right clock settings when you compile:
[FONT=courier new][B]CON[/B]
[/FONT]
[FONT=courier new]_clkmode = xtal1 + pll8x ' <---------------- change to suit[/FONT]
[FONT=courier new]_xinfreq = 10_000_000 ' <---------------- change to suit your crystal[/FONT]
[FONT=courier new]
[/FONT][FONT=courier new]baud = 230400 ' <-- change - tested from 300 baud to 3M baud[/FONT]
I had to set xtall + pll16x ... then CLKFREQ displayed the correct frequency.
Interestingly the loop timings all were as per your published ones so I guess _clkmode is not actually used to set the PLL? I could not find another reference to it in the code... must be going blind
On another issue: Can you explain what the word DELTA does in the cog timer stuff?
Could not work out what PASM does either ..
Also, I read in this thread about the SEE de-compiler but don't see it in the kernel or EXTEND.fth ..
I had to set xtall + pll16x ... then CLKFREQ displayed the correct frequency.
Interestingly the loop timings all were as per your published ones so I guess _clkmode is not actually used to set the PLL? I could not find another reference to it in the code... must be going blind
On another issue: Can you explain what the word DELTA does in the cog timer stuff?
Could not work out what PASM does either ..
Also, I read in this thread about the SEE de-compiler but don't see it in the kernel or EXTEND.fth ..
Thanks,
Bernie
I think the timing function just assumes it's running at 80MHz but I will take a look at that again and make sure it uses CLKFREQ.
[B][COLOR=#000000][FONT=Ubuntu Mono]' LAPCNT ( -- time ) Elapsed time in microseconds since NEWCNT[/FONT][/COLOR]
[COLOR=#ff0000][FONT=Ubuntu Mono][B]LAPCNT[/B][/FONT][/COLOR][COLOR=#000000][FONT=Ubuntu Mono] byte _1,SPRFETCH,REG,cntr,FETCH,XCALL,xNEWCNT,MINUS,_BYTE,80,XCALL,xDIVIDE,EXIT[/FONT][/COLOR]
[/B]
DELTA accepts a count value and then performs a WAITCNT but if you want to continue in a loop you simply use WAITCNT again to synchronize. So DELTA is just used to set the timing period but also performs the first WAITCNT.
PASM takes a long from the stack and executes that PASM instruction in the cog.
The other files are available either in Google doc or webpage format or as text files on Dropbox. Where are the links? Just follow my sig but here's a quick link here
@Peter
based on your new STRING words I wanted to build an EVAL
which works - almost fine.
BUT:
1. maybe it would be better to suppress the echo of the evaluated string and show just the result
(could be done using NULLOUT & CON see below )
2. if used multiple times in one line or inside a loop, only the last EVAL seems to get executed.
seems I still miss s.th. important
\
pub (EVALSTR) ( strAddr -- )
C@++ DUP 0= IF DROP ukey W~ $0D THEN ; ok
ok
pub EVAL ( strAddr -- \ evaluate the FORTH sentence in the null-teminated string starting at strAddr )
' (EVALSTR) ukey W! ; ok
{ without output
pub (EVALSTR) ( strAddr -- )
C@++ DUP 0= IF DROP CON $0D THEN ;
\ just deliver one char after the other, on last one add $0D to get it executed and set reader back to CONsole
pub EVAL ( strAddr -- \ evaluate the FORTH sentence in the null-teminated string starting at strAddr )
NULLOUT \ suppress output
' (EVALSTR) ukey W! ;
}
" 1 1 + . " EVAL ok
1 1 + . 2 ok
" 2 2 + . " EVAL ok
2 2 + . 4 ok
" : TT1 $17 . ; " $20 STRING TESTSTR ok
TESTSTR EVAL ok
: TT1 $17 . ; ok
TT1 17 ok
ok
\ let's define a WORD, that generates other words ... ( I worked many years with LISP ;-) - so I love EVAL )
: TTT
0 6 ADO "0" I + TESTSTR 4 + C! "0" I + TESTSTR 8 + C! TESTSTR .STR CR TESTSTR [COLOR=#0000ff][B]EVAL [/B][/COLOR]LOOP
; ok
ok
TTT : TT0 $10 . ;
: TT1 $11 . ;
: TT2 $12 . ;
: TT3 $13 . ;
: TT4 $14 . ;
: TT5 $15 . ;
ok
: TT5 $15 . ; ok
EXTEND.fth now also includes the NORMALIZE routine by Beau Schwabe as well as MCP3208 12-bit ADC words. NORMALIZE executes 12-bits in 200us. The MCP3208 communicates at the maximum sampling rate of 2MHz.
[FONT=courier new]
[COLOR=#000000][B]NORMALIZE[/B] ( data bits low high -- result )
[/COLOR]
[B]![COLOR=#000000]ADC[/COLOR][/B][COLOR=#000000] ( &ce.so.si.ck -- ) \ Init the MCP3208 driver using a long encoded pin descriptor &ce.miso.mosi.sck[/COLOR]
[COLOR=#000000][B]ADC@[/B] ( ch -- data )
[/COLOR][SIZE=4][COLOR=#000000]
[/COLOR][/SIZE][B][COLOR=#000000][B]ADCBUF[/B][/COLOR][/B][COLOR=#000000] ( buffer -- ) \ Read all 8 ADC channels into specified word buffer[/COLOR][SIZE=4]
[/SIZE][COLOR=#000000][B]VOLTS@[/B] ( ch -- volts.00 ) \ Read back the pin as a voltage scaled to 2 decimals[/COLOR][/FONT]
EXTEND.fth now also includes the NORMALIZE routine by Beau Schwabe as well as MCP3208 12-bit ADC words. NORMALIZE executes 12-bits in 200us. The MCP3208 communicates at the maximum sampling rate of 2MHz.
[FONT=courier new]
[COLOR=#000000][B]NORMALIZE[/B] ( data bits low high -- result )
[/COLOR]
[B]![COLOR=#000000]ADC[/COLOR][/B][COLOR=#000000] ( &ce.so.si.ck -- ) \ Init the MCP3208 driver using a long encoded pin descriptor &ce.miso.mosi.sck[/COLOR]
[COLOR=#000000][B]ADC@[/B] ( ch -- data )
[/COLOR][SIZE=4][COLOR=#000000]
[/COLOR][/SIZE][B][COLOR=#000000][B]ADCBUF[/B][/COLOR][/B][COLOR=#000000] ( buffer -- ) \ Read all 8 ADC channels into specified word buffer[/COLOR][SIZE=4]
[/SIZE][COLOR=#000000][B]VOLTS@[/B] ( ch -- volts.00 ) \ Read back the pin as a voltage scaled to 2 decimals[/COLOR][/FONT]
Thanks Peter,
Back to refactor once again, module count reduced.
EXTEND.fth now also includes the NORMALIZE routine by Beau Schwabe as well as MCP3208 12-bit ADC words. NORMALIZE executes 12-bits in 200us. The MCP3208 communicates at the maximum sampling rate of 2MHz.
[FONT=courier new]
[COLOR=#000000][B]NORMALIZE[/B] ( data bits low high -- result )
[/COLOR]
[B]![COLOR=#000000]ADC[/COLOR][/B][COLOR=#000000] ( &ce.so.si.ck -- ) \ Init the MCP3208 driver using a long encoded pin descriptor &ce.miso.mosi.sck[/COLOR]
[COLOR=#000000][B]ADC@[/B] ( ch -- data )
[/COLOR][SIZE=4][COLOR=#000000]
[/COLOR][/SIZE][B][COLOR=#000000][B]ADCBUF[/B][/COLOR][/B][COLOR=#000000] ( buffer -- ) \ Read all 8 ADC channels into specified word buffer[/COLOR][SIZE=4]
[/SIZE][COLOR=#000000][B]VOLTS@[/B] ( ch -- volts.00 ) \ Read back the pin as a voltage scaled to 2 decimals[/COLOR][/FONT]
,
Hi Peter,
It's great all the new stuff you are adding, but it is making me wonder what your view for the purpose of EXTEND.fth is?
Seems to me that it should only extend the kernel if it adds generic functionality like I2C, SPI, CAN etc and any other useful stuff, but I wonder when you start adding device drivers for particular chips
to that module ... should that not be in a separate module?
I do like the concept of modules to allow the user to pick and choose what to add for their application, it seems to me that if you add too much to EXTEND.fth, then you run the risk
of people having to strip stuff out they don't need to save space, then you have the problem of breaking things because us folk don't know all the dependencies ....
Oh, and talking about dependencies, i have loaded modules in that won't compile because they are missing some other module. Could we make sure the dependencies are listed at the
top of the module?
It's great all the new stuff you are adding, but it is making me wonder what your view for the purpose of EXTEND.fth is?
Seems to me that it should only extend the kernel if it adds generic functionality like I2C, SPI, CAN etc and any other useful stuff, but I wonder when you start adding device drivers for particular chips
to that module ... should that not be in a separate module?
I do like the concept of modules to allow the user to pick and choose what to add for their application, it seems to me that if you add too much to EXTEND.fth, then you run the risk
of people having to strip stuff out they don't need to save space, then you have the problem of breaking things because us folk don't know all the dependencies ....
Oh, and talking about dependencies, i have loaded modules in that won't compile because they are missing some other module. Could we make sure the dependencies are listed at the
top of the module?
My 3c ....
Bernie
True, but the MCP3208 is such a little tiny thing that it felt lonely so I tucked it into a corner of EXTEND.fth. The other thing is that there is then a built-in ADC feature which means that even a newbie could plug a chip in and start working with it without having to locate the driver. So a driver like this is an exception. Of course in V3 a lot of these modules can be loaded in straight from Flash/SD.
My system needs:
RTC
A2D
TCP/IP
BlueTooth
Strings
and chess, my gosh how come I don't have a chess module /SARC
dp
I actually have the W5200 code and FAT32 file + virtual memory available soon. It's all been dragging out as a WIP until I start feeling the whip itself after which I get the job finished. Let me know if there are specific chips that need interfacing such as the RTCs and Bluetooth etc.
Comments
Just been browsing through the kernel PASM code, you have done a great job in your implementation.
An immediate standout for me regarding speed of execution is your decision to do away with the SP and go for straight MOV's in the PUSH and POP functions for example.
It seems that most of the low level words jump to the DROP function which of course calls POPX which has 12 MOV's as immediate overhead ... I apologize if this has already been discussed ..
Be interested to hear why you have taken this approach rather than a stack pointer as a stack pointer results in push and pop having a lot less instructions ..
Please note this is not a criticism, just like to know how you arrived at that decision ...
Also, are there any forth words to load file(s), or does all compiling involve cut-and-paste?
Cheers,
Bernie
Decisions decisions decisions..... The Prop is a strange beast and the lack of indexed operations in PASM not just for the pushing and popping but also for all the operations that need to reference stack elements led me to think of keeping stack elements fixed as if they were fixed registers. That way I didn't have to constantly calculate where the operands were or save and restore a TOS register etc, it's always just there. Sure it meant doing a rather brute force move method to push and pop but at least that kept it all in the cog. Look at how efficient these operations now become: Sure there's a trade-off but this implementation is definitely fast (the fastest?) as is the bytecode method of indexing the first 256 longs in cog memory. The other thing with any kinds of pointers is boundary checking as underflow and overflow can trash the code space so this way it intrinsically limited itself to the "stack". Can you see a better faster way of implementing PUSH and POP that always keeps it simple for all the other stack operations? I welcome the feedback and it's only a bonus if Tachyon can be made better.
The return and other stacks are implemented with self-modifying code to implement "indexed" addressing. The other part of the implementation has to do with expressing the kernel in an easy to read source code form that runs in the Spin tool. That job was made a bit easier by using directly indexing bytecode so that the label corresponded to the cog address which could be written and compiled in a clear manner that kept clutter and calculations to an absolute minimum.
To create a string constant or variable:
" MY NAME IS " $20 STRING NAME
To append to this:
" PETER" NAME APPEND$
Other operations include:
STRING Define a string
COPY$ Copy one string to another
APPEND$ Append one string to another
MID$ Return with string
LEFT$
RIGHT$
ERASE$ Erase the strings allocated space
LOCATE$ Locate the first character in the string else return with null
COMPARE$ Compare two strings and return with flag
+CHAR Append a character to the end of the string
Some of these operations may change as I seek to improve the definition of a string and how they operate.
Understand now, excellent. I did not realize that the Prop did not have indexed PASM instructions ..
I guess the only real trade-off is although as you say the words that don't modify the stack are now much faster, the words that do modify the stack, take the overhead of the POP or PUSH and are therefore slower ..
I guess it all boils down to which of these instructions are used the most frequent .... I am not sure myself, but Tachyon runs really fast, so I think you have made a good decision with your architecture ...
Regarding my other question, is there any file load words? or is all file loading/compiling by cut-and-paste ..
Cheers,
Bernie
Fantastic, going to go refactor, again. And if the words/functions change that's what :%s/oldstr/newstr/gc is for, the mouse can be crippling
Thanks Peter,
@bmentink There is Peter's SDCARD.fth library
https://docs.google.com/document/pub?id=1kjfVx5Ig39VX0AGpUjxfDv_wCCrqoSmj_fvBRhku8QY
But I have not seen any words that either A. execute via the terminal to "read" a file in from the local disk or B. Words to read and write "files" from the SD Card.
Can't speak for Peter but I'm pretty sure if you added file support to the SD library he wouldn't mind, me neither It's way beyond my FORTH at this time.
Using this as a guideline I set up for the I2C redirection using @I2CBOOT and @I2CMAIN. It all seems to work fine. Peter? Markus? Did I miss anything this time?
FYI.... I am doing a project based on a Prop Mini, which as a 32K EEPROM and no clean access to P28/P29 ..So I built my main I2C buss on P7/P6 with a total of 4 devices.
cheers ... BBR
Hi Brian,
I'm wondering why you aren't using the I2CPINS word and simply saying: You seem to be ignoring these functions and writing everything out in longhand.
As for @I2CBOOT isn't that what EEPROM does already?
On point #1, I2CMAIN ... d'oh ... conceded!
On Point t#2, I2CBOOT vs EEPROM .. to quote the master (that's you!) "readability and consistency" ... my 'MAIN buss on P7/P6 also has an EEPROM in it ... I2CBOOT better conveys the sense of it.
No probs Brian, and sometimes the art of Forth boils down to the choice of a name, does it convey the correct thought? At times it can be frustrating trying to come up with the right name and method of operation, one that is natural enough to read and natural enough for Forth.
The other part of the art since we are on the subject is factoring, and when that factor has the right name then hopefully a novice should be able to understand the program. Wouldn't it be good if we had a little switch so we could switch ourselves to novice mode just to make sure it makes sense! Of course conversely, the novice would probably like a guru switch as well!
Just browsing the above file and noticed a copy-paste error in the following function.
It calls FADC instead of the intended MADC....
Cheers,
Bernie
Has anyone used Tachyon on a Hive board? Will this Forth one day be able to support multiple Prop chips like this?
Seems to me "fun x 3" to have Tachyon running on this board .....
Cheers,
Bernie
I don't have any plans to support multiple Prop chips with Tachyon although I could probably use a similar approach that Propforth does. I have used my own version of inter-Prop coms over fibre-optic links so there is no reason that this can't be done over local links. However I like the Prop for it's simplicity and flexibility but I still regard it as a microcontroller and I can think of far better ways of implementing something like a hive board.
nick
Neat Nick, I will post code when I'm happy with the results. I have two tasks, one that reads object messages from the lcd (ltask, I'm using the GENIE interface), and one that writes object messages with updated values from the RTC, A2D, flame sensor etc. I have found the uLCD to be really finicky in an actual application but so far the two tasks are working nicely together. Also found that I needed to use WAITLOW with a timeout instead of WAITPNE for serial in or the screen would hang during extended testing periods? I might try WAITPNE again now that I have the two tasks nicely separated.
Your Tachyon/FORTH is nice and clean, thanks for the example!!
Just as a matter of interest, can you elaborate on what sort of fibre-optic link you used between Prop's? I am interested in connecting several CPU boards together with a "cheap" fat pipe(high bandwidth ...
It needs to have connectors that can stand a high vibration environment ...
Thanks,
Bernie
I have had no problems reassigning i/o pins for for I2C and 1-Wire .. now what I need to attend to is console redirection. I have a Roving Networks RN XV WiFly card and it is setup to assert one pin when it has a Telnet connection then i/o is done via the di/do pins. Now I can ignore the connection pin and just hookup GND, DI, DO on the RN XV to the prop's P30/P31 and that works fine ... but what I want to do is run a task in a cog monitoring the connection pin and when it is detected reassign the console to the prop pins connected to DI/DO. Now, ideally there would be code to return the console to the local port, but typing REBOOT from the remote console will do for starters.
The ports of interest are referenced in the spin/pasm kernel as txd and rxd........ So far I don't seem to be able to find any way to get at them in Forth ...... is there any way? I'll accept quick and dirty for the moment.
cheers ... BBR
the txd side should not be a problem to change, since it is inside the running FORTH COG here: this can be changed using COG! using the REG4 address + 2 to store the new mask
6 COGREG gives the address of the txmask COG register
the receive side is more difficult since the RX is running in it's own cog and the rx pin can not be changed afterwards.
IT might be possible to run a second receive cog in parallel to the first one with the second rx pin that you want to use,
if both receive alternatively.
OR you could adapt the receive cog, so you can dynamically change the rx-pin.
I'll have a closer look into the code later.
ok ... here is the start of the receive routine
I would suggest you specify a HUB location where you put the RX-mask
and use a simple and then you just write to this temp register REG gives you the start of the register space and temp is equivalent to REG(0) so to use REG(4-long) add this 12 bytes
EDIT: of course this RXMASK needs to be initialized to the standard RX-pin-mask on startup.
I would put it here:
but Peter might be faster with a helpful answer anyhow ;-)
I have a Prop Prototype board with a 5Mhz Crystal. The INFO word shows the clock frequency at 40Mhz, isn't it mean't to be 80Mhz? Do I have to tweak the PLL?
Cheers,
Bernie
Hi Bernie, you just need to make sure you have the right clock settings when you compile:
I had to set xtall + pll16x ... then CLKFREQ displayed the correct frequency.
Interestingly the loop timings all were as per your published ones so I guess _clkmode is not actually used to set the PLL? I could not find another reference to it in the code... must be going blind
On another issue: Can you explain what the word DELTA does in the cog timer stuff?
Could not work out what PASM does either ..
Also, I read in this thread about the SEE de-compiler but don't see it in the kernel or EXTEND.fth ..
Thanks,
Bernie
I think the timing function just assumes it's running at 80MHz but I will take a look at that again and make sure it uses CLKFREQ. DELTA accepts a count value and then performs a WAITCNT but if you want to continue in a loop you simply use WAITCNT again to synchronize. So DELTA is just used to set the timing period but also performs the first WAITCNT.
PASM takes a long from the stack and executes that PASM instruction in the cog.
The other files are available either in Google doc or webpage format or as text files on Dropbox. Where are the links? Just follow my sig but here's a quick link here
based on your new STRING words I wanted to build an EVAL
which works - almost fine.
BUT:
1. maybe it would be better to suppress the echo of the evaluated string and show just the result
(could be done using NULLOUT & CON see below )
2. if used multiple times in one line or inside a loop, only the last EVAL seems to get executed.
seems I still miss s.th. important
Thanks Peter,
Back to refactor once again, module count reduced.
Hi Peter,
It's great all the new stuff you are adding, but it is making me wonder what your view for the purpose of EXTEND.fth is?
Seems to me that it should only extend the kernel if it adds generic functionality like I2C, SPI, CAN etc and any other useful stuff, but I wonder when you start adding device drivers for particular chips
to that module ... should that not be in a separate module?
I do like the concept of modules to allow the user to pick and choose what to add for their application, it seems to me that if you add too much to EXTEND.fth, then you run the risk
of people having to strip stuff out they don't need to save space, then you have the problem of breaking things because us folk don't know all the dependencies ....
Oh, and talking about dependencies, i have loaded modules in that won't compile because they are missing some other module. Could we make sure the dependencies are listed at the
top of the module?
My 3c ....
Bernie
True, but the MCP3208 is such a little tiny thing that it felt lonely so I tucked it into a corner of EXTEND.fth. The other thing is that there is then a built-in ADC feature which means that even a newbie could plug a chip in and start working with it without having to locate the driver. So a driver like this is an exception. Of course in V3 a lot of these modules can be loaded in straight from Flash/SD.
RTC
A2D
TCP/IP
BlueTooth
Strings
and chess, my gosh how come I don't have a chess module /SARC
dp
I actually have the W5200 code and FAT32 file + virtual memory available soon. It's all been dragging out as a WIP until I start feeling the whip itself after which I get the job finished. Let me know if there are specific chips that need interfacing such as the RTCs and Bluetooth etc.