FROM BY FOR NEXT
Woke up early this morning and decided to implement the new FOR NEXT with a combined loop parameter and branch stack in hub RAM as indexing this in cog RAM is way too clumsy and consumes way too much cog memory anyway. FOR now pushes 4 (not a pun) parameters onto the loop stack and maintains 4 registers in cog memory for the current loop. I decided on calling the INDEX and STEP methods FROM and BY. With that accomplished here is the very first tentative step for 4.5's FOR NEXT.
Propeller .:.:--TACHYON--:.:. Forth V4X5 DAWN 450170705.0600
Cold start - no user code - setting defaults
--------------------------------------------------------------------------------
( 0001 $1B40 ok ) 10 FOR I . SPACE NEXT
0 1 2 3 4 5 6 7 8 9
( 0002 $1B40 ok ) 100 FROM 10 FOR I . SPACE NEXT
100 101 102 103 104 105 106 107 108 109
( 0003 $1B40 ok ) 100 FROM -1 BY 10 FOR I . SPACE NEXT
100 99 98 97 96 95 94 93 92 91
( 0004 $1B40 ok )
Because it was way too awkward and slow to implement an efficient loop stack in cog memory that pushed and popped four parameters at a time, this was moved to hub memory and now there are a whopping 27 longs spare in the Tachyon cog. I think I'll go to town with that!
( 0002 $3602 ok )
( 0003 $3602 ok ) 1000000 LAP FOR NEXT LAP .LAP
48000240 cycles at 96MHz = 500.002ms
So, that's fine at 500ns/loop.
But what about setup and exit latencies?
( 0004 $3602 ok ) 0 LAP FOR LAP .LAP
160 cycles at 96MHz = 1.666us
( 0005 $3602 ok ) 0 LAP FOR NEXT LAP .LAP
272 cycles at 96MHz = 2.833us
( 0006 $3602 ok ) 1 LAP FOR NEXT LAP .LAP
288 cycles at 96MHz = 3.000us
FOR itself requires 1.666us to execute and stack the previous loop. The overall FOR NEXT overhead is 3us where it stacks and unstacks 4 parameters.
Checking the 4.4 kernel for comparison:
( 1973 $35B6 ok ) 1000000 LAP FOR NEXT LAP .LAP
48000176 cycles at 96MHz = 500.001ms
( 1974 $35B6 ok ) 0 LAP FOR NEXT LAP .LAP
208 cycles at 96MHz = 2.166us
( 1975 $35B6 ok ) 1 LAP FOR NEXT LAP .LAP
224 cycles at 96MHz = 2.333us
( 1977 $35B6 ok ) 0 1 1 LAP DOFOR LOOP LAP .LAP
448 cycles at 96MHz = 4.666us
( 1978 $35B6 ok ) 0 0 1 LAP DOFOR LOOP LAP .LAP
432 cycles at 96MHz = 4.500us
( 1981 $35B6 ok ) LAP 0 1 1 DOFOR LOOP LAP .LAP
688 cycles at 96MHz = 7.166us
The FOR NEXT loop times are the same although overhead is a fraction less. However to compare this properly we also run a single (non-looping) DOFOR LOOP which takes 4.666us vs the 3us in V4.5. So V4.5 has comparable FOR NEXT overhead but faster when compared to DOFOR LOOP overhead in V4.4.
V4.5 full FROM BY FOR NEXT overhead is 10.333us where FROM and BY are high-level wordcode at present.
( 1979 $3602 ok ) 1 1 0 LAP FROM BY FOR NEXT LAP .LAP
992 cycles at 96MHz = 10.333us
Obviously now that I have spare memory I can implement FROM and BY in PASM which I have just done:
( 1973 $35C2 ok ) 1 1 0 lap FROM BY FOR NEXT LAP .LAP
448 cycles at 96MHz = 4.666us
That's the same overhead as it is for a DOFOR LOOP in V4.4. No problem but obviously FOR NEXT overhead does not always require FROM or BY.
The J index is maintained in a register but K was deprecated in V4.4 since it would take too much to maintain K although it could be done in V4.5
btw - 42 longs spare at present for RUNMODs vs 23 normally
For V4.5 I managed to fit in +LOOP which is just a single PASM opcode for BY! NEXT operation, ADO which is a single PASM opcode for SWAP FROM FOR, and lastly LOOP which is just an alias for NEXT. They are all FOR NEXT loops though where the FOR count is not affected by BY which is only used to increment the FROM index. Traditional DO +LOOP uses a limit that the index is compared against rather than a count.
( 1976 $35C2 ok )
( 1977 $35C2 ok ) 100 10 ADO I . SPACE LOOP
100 101 102 103 104 105 106 107 108 109
( 1978 $35C2 ok ) 100 10 ADO I . SPACE 2 +LOOP
100 102 104 106 108 110 112 114 116 118
( 1979 $35C2 ok ) 8 FOR I . SPACE NEXT
0 1 2 3 4 5 6 7
( 1980 $35C2 ok ) 100 FROM 2 BY 8 FOR I . SPACE NEXT
100 102 104 106 108 110 112 114
( 1981 $35C2 ok ) 4 FOR 4 FOR I . ',' EMIT J . SPACE NEXT NEXT
0,0 1,0 2,0 3,0 0,1 1,1 2,1 3,1 0,2 1,2 2,2 3,2 0,3 1,3 2,3 3,3
( 1982 $35C2 ok )
I try to understand TIMER.
I loaded TIMER.fth.
But any LEDs not on.
Why?
TIMERS are not numbered, you need to setup a TIMER <name> structure and then use that name as the address for TIMEOUT, TIMEOUT?, and ALARM.
For instance:
TIMER #mt0 ( creates a timer structure variable with room for a timer, an alarm vector, flags, link etc)
: blink0 #16 PIN@ IF #16 LOW #500 ELSE #16 HIGH #50 THEN #mt0 TIMEOUT ;
: b0 ' blink0 #mt0 ALARM 1 #mt0 TIMEOUT ;
It is necessary to load and kick the timer off which is what 1 #mt0 TIMEOUT does and it also makes sure that the timer is linked into the timer list so that TIMER.TASK can service it every millisecond.
The simple counted FOR NEXT loops won't work although there was a much earlier version of the timers years ago that were simply preallocated and numbered. I found from that that it was easier to create a timer by name and link it into a list so that we could have as many or as little timers as we liked. To stop a timer simply load it with 0 either with:
0 #mt0 TIMEOUT
or
0 #mt0 !
since the first long in the timer structure is the actual timer part.
BTW, I see you must be using the new style FOR NEXT in V4.5 as that supports a loop index. Normally I is only used with DO LOOPs but in 4.5 all FOR NEXT loops always maintain an implied index FORM and step BY which if not specified defaults to 0 for the start index FROM and 1 for the step BY.
D.P and caskaz - BLINKEM should write a non-zero TIMEOUT (usually 1). It is just as easy to put that 1 TIMEOUT in B0 to B7 after you set the ALARM.
At startup all those timers aren't linked or recognized by Tachyon, but once user code calls TIMEOUT it links the timer in and maintains it every millisecond. Bear in mind that if the timer cog is expected to control outputs that you need to make sure that another cog hasn't tried to directly set the outputs since outputs are OR'd. Also since the timer cog services the timers every millisecond it also means that any alarm function should execute in well under a millisecond. If your alarm function requires millisecond delays etc then it is usually best if it is called on a regular basis and maintain its own prescaler to time lengthy events but of course there are lots of ways to slice and dice.
D.P and caskaz - BLINKEM should write a non-zero TIMEOUT (usually 1). It is just as easy to put that 1 TIMEOUT in B0 to B7 after you set the ALARM.
At startup all those timers aren't linked or recognized by Tachyon, but once user code calls TIMEOUT it links the timer in and maintains it every millisecond. Bear in mind that if the timer cog is expected to control outputs that you need to make sure that another cog hasn't tried to directly set the outputs since outputs are OR'd. Also since the timer cog services the timers every millisecond it also means that any alarm function should execute in well under a millisecond. If your alarm function requires millisecond delays etc then it is usually best if it is called on a regular basis and maintain its own prescaler to time lengthy events but of course there are lots of ways to slice and dice.
Got to some hardware,
Yes I figured out that 0 is not valid for TIMEOUT.
This works but doesn't clear all the lights after stopem? I can't even #PIN LOW, but restarting blinkem flashes them all again?
TACHYON V4
IFDEF timer.fth
@rest org
FORGET timer.fth
}
pub timer ." timer 2017/06/17 11:49:45 " ;
@org W@ == @rest --- remember
\ * SIMPLE ROUTINE TO EXERCISE ALL 8 LEDS WITH TACHYON TIMERS
\ * AND ALARMS ON THE PROP QUICKSTART BOARD
\ *
\ * USE THE WORD "BLINKEM" TO START THE LIGHTS
\ * AND "STOPEM" TO STOP THE LIGHTS
TIMER mt0
TIMER mt1
TIMER mt2
TIMER mt3
TIMER mt4
TIMER mt5
TIMER mt6
TIMER mt7
: blink0 #16 PIN@ IF #16 LOW #500 ELSE #16 HIGH #50 THEN mt0 TIMEOUT ; --- timer alarm rountine and reset
: B0 ' blink0 mt0 ALARM ; --- assign blink0 as mt0 alarm vector
: blink1 #17 PIN@ IF #17 LOW #300 ELSE #17 HIGH #50 THEN mt1 TIMEOUT ;
: B1 ' blink1 mt1 ALARM ;
: blink2 #18 PIN@ IF #18 LOW #150 ELSE #18 HIGH #50 THEN mt2 TIMEOUT ;
: B2 ' blink2 mt2 ALARM ;
: blink3 #19 PIN@ IF #19 LOW #285 ELSE #19 HIGH #50 THEN mt3 TIMEOUT ;
: B3 ' blink3 mt3 ALARM ;
: blink4 #20 PIN@ IF #20 LOW #175 ELSE #20 HIGH #50 THEN mt4 TIMEOUT ;
: B4 ' blink4 mt4 ALARM ;
: blink5 #21 PIN@ IF #21 LOW #335 ELSE #21 HIGH #50 THEN mt5 TIMEOUT ;
: B5 ' blink5 mt5 ALARM ;
: blink6 #22 PIN@ IF #22 LOW #100 ELSE #22 HIGH #50 THEN mt6 TIMEOUT ;
: B6 ' blink6 mt6 ALARM ;
: blink7 #23 PIN@ IF #23 LOW #195 ELSE #23 HIGH #50 THEN mt7 TIMEOUT ;
: B7 ' blink7 mt7 ALARM ;
: clrpin $FF0000 OUTCLR ;
: BLINKEM
B0 B1 B2 B3 B4 B5 B6 B7 --- assign the alarm vector to timers
#10 mt0 TIMEOUT --- start all the timers
#20 mt1 TIMEOUT
#30 mt2 TIMEOUT
#40 mt3 TIMEOUT
#50 mt4 TIMEOUT
#60 mt5 TIMEOUT
#70 mt6 TIMEOUT
#80 mt7 TIMEOUT
;
: STOPEM
0 mt0 ALARM --- assign the alarm vectors to null, they will stop after the last timeout.
0 mt1 ALARM
0 mt2 ALARM
0 mt3 ALARM
0 mt4 ALARM
0 mt5 ALARM
0 mt6 ALARM
0 mt7 ALARM
500 ms
8 FOR I #16 + LOW NEXT
;
END
\ ?BACKUP
@D.P - you are running STOPEM from the main Tachyon cog but the timer cog is the one that is using the I/O and still has the DIR set for output and if the output is high, you can't pull it low.
Bear in mind that if the timer cog is expected to control outputs that you need to make sure that another cog hasn't tried to directly set the outputs since outputs are OR'd.
Taking into consideration that this code is really only for a demonstration anyway perhaps we could code STOPEM by exploiting the fact that your alarm code sets the pin low (on its side) if it sees that it is high. So set the LEDs high and not low, do not disable the ALARM but simply change all the timers to 1, wait 2 ms, after which all the alarms will have set their outputs low, then write a 0 TIMEOUT for all of them and then release all the LEDs. Does that make sense? It may look awkward but that's because that's not how we would normally use the timers but the demo is useful as a learning exercise.
Alternatively you could run STOPEM as an extra alarm function to zero all the timeouts, and turn off all the LEDs.
Hint - rather than creating another timer for this, just use the "wdt" watchdog timer, set the alarm function for it to zero all the other timeouts and turn off the LEDs.
EDIT: I see I have TIMERJOB just for this very thing.
@D.P - you are running STOPEM from the main Tachyon cog but the timer cog is the one that is using the I/O and still has the DIR set for output and if the output is high, you can't pull it low.
Bear in mind that if the timer cog is expected to control outputs that you need to make sure that another cog hasn't tried to directly set the outputs since outputs are OR'd.
Taking into consideration that this code is really only for a demonstration anyway perhaps we could code STOPEM by exploiting the fact that your alarm code sets the pin low (on its side) if it sees that it is high. So set the LEDs high and not low, do not disable the ALARM but simply change all the timers to 1, wait 2 ms, after which all the alarms will have set their outputs low, then write a 0 TIMEOUT for all of them and then release all the LEDs. Does that make sense? It may look awkward but that's because that's not how we would normally use the timers but the demo is useful as a learning exercise.
Alternatively you could run STOPEM as an extra alarm function to zero all the timeouts, and turn off all the LEDs.
Hint - rather than creating another timer for this, just use the "wdt" watchdog timer, set the alarm function for it to zero all the other timeouts and turn off the LEDs.
EDIT: I see I have TIMERJOB just for this very thing.
V4.5 testing has been going well and I am now testing EASYNET and tracking down that wizpin bug. A few other bugs were fixed elsewhere as well and so this version is working just fine.
Now some notes on those changes:
V4.5 Notes
FOR NEXT loops now maintain an index and step count even if they are not specified.
If they are not specified then the starting index is 0 by default and the step count
is 1 by default. LOOP is now the same as NEXT.
The traditional DO LOOP which uses a limit and a start index is not supported
but ADO will take a start index and a loop count.
+LOOP works just like NEXT except it modifies the step count
The thing to remember about these new loops is that the loop count is the actual
number of times that it loops for regardless of step count.
The loop and branch stack are now in hub RAM at $180 where every loop uses
4 longs. If Tachyon is run in other cogs that use loops then some stack space
should be allocated. Usually 3 or 4 levels is more than enough so 64 bytes max.
16 longs mystk
mystk LP!
Usage:
10 FOR I . SPACE FOR@ . NEXT --- print index and loop count
$4000 FROM 4 BY 8 FOR I @ .LONG NEXT --- Print 8 longs from memory at $4000
FUNCTIONS
FROM ( startindex -- ) --- Set starting index for loops (default is 0)
BY ( stepcnt -- ) --- Set amount that index is to be incremented by (signed, default is 1)
ADO ( startindex loopcnt -- ) --- Sets the FROM starting index before executing FOR
FOR ( loopcnt -- ) --- Works just like a standard FOR NEXT but maintains an index and step count
NEXT --- decrement loopcnt and loop if not zero while incrementing index by stepcnt
LOOP --- alias for NEXT
+LOOP ( stepcnt -- ) --- Change the step count then execute NEXT
FOR@ ( -- loopcnt ) --- Fetch the current loop count
FOR! ( loopcnt -- ) --- Overwrite the current loop count
BY! ( stepcnt -- ) --- Overwrite the current step count
LP! ( addr -- ) --- Set the loop stack pointer
V4.5 testing has been going well and I am now testing EASYNET and tracking down that wizpin bug. A few other bugs were fixed elsewhere as well and so this version is working just fine.
Now some notes on those changes:
V4.5 Notes
FOR NEXT loops now maintain an index and step count even if they are not specified.
If they are not specified then the starting index is 0 by default and the step count
is 1 by default. LOOP is now the same as NEXT.
The traditional DO LOOP which uses a limit and a start index is not supported
but ADO will take a start index and a loop count.
+LOOP works just like NEXT except it modifies the step count
The thing to remember about these new loops is that the loop count is the actual
number of times that it loops for regardless of step count.
The loop and branch stack are now in hub RAM at $180 where every loop uses
4 longs. If Tachyon is run in other cogs that use loops then some stack space
should be allocated. Usually 3 or 4 levels is more than enough so 64 bytes max.
16 longs mystk
mystk LP!
Usage:
10 FOR I . SPACE FOR@ . NEXT --- print index and loop count
$4000 FROM 4 BY 8 FOR I @ .LONG NEXT --- Print 8 longs from memory at $4000
FUNCTIONS
FROM ( startindex -- ) --- Set starting index for loops (default is 0)
BY ( stepcnt -- ) --- Set amount that index is to be incremented by (signed, default is 1)
ADO ( startindex loopcnt -- ) --- Sets the FROM starting index before executing FOR
FOR ( loopcnt -- ) --- Works just like a standard FOR NEXT but maintains an index and step count
NEXT --- decrement loopcnt and loop if not zero while incrementing index by stepcnt
LOOP --- alias for NEXT
+LOOP ( stepcnt -- ) --- Change the step count then execute NEXT
FOR@ ( -- loopcnt ) --- Fetch the current loop count
FOR! ( loopcnt -- ) --- Overwrite the current loop count
BY! ( stepcnt -- ) --- Overwrite the current step count
LP! ( addr -- ) --- Set the loop stack pointer
' stopem TIMERJOB
works correctly. But then a statem call shows the lights flashing at 23 second intervals. That's odd and familiar. As you said just a testing program to work out TIMERS.
4.5 seems to be our new porting target from V3 then?
D.P caskaz, can you use the Forth to diagnose what the problem is? I will try later when I'm better and all I would do is exercise the code that's there and check variables. Maybe I might make .VARS display contents too. Back soon I hope
D.P - ' STOPEM TIMERJOB would work except for the unnecessary blocking delay which is a big no-no for any timer function. Just remove the delay and combine STOPEM and ALLOFF. The earlier <mask> OUTCLR is far easier to use rather than the ALLOFF though.
D.P - ' STOPEM TIMERJOB would work except for the unnecessary blocking delay which is a big no-no for any timer function. Just remove the delay and combine STOPEM and ALLOFF. The earlier <mask> OUTCLR is far easier to use rather than the ALLOFF though.
Well I had to try the FOR NEXT just to make sure I didn't miss anything in that writeup.
And DOH! 500 ms in the TIMERJOB, that would leave a mark. So here is the last example, it works repeatedly on and off correctly, dropped the loop and MASK the OUTCLR
IFDEF timer.fth
@rest org
FORGET timer.fth
}
pub ." timer 2017/06/17 11:49:45 " ;
@org W@ == @rest --- remember
\ * SIMPLE ROUTINE TO EXERCISE ALL 8 LEDS WITH TACHYON TIMERS
\ * AND ALARMS ON THE PROP QUICKSTART BOARD
\ *
\ * USE THE WORD "BLINKEM" TO START THE LIGHTS
\ * AND "STOPEM" TO STOP THE LIGHTS
TIMER mt0
TIMER mt1
TIMER mt2
TIMER mt3
TIMER mt4
TIMER mt5
TIMER mt6
TIMER mt7
: blink0 #16 PIN@ IF #16 LOW #500 ELSE #16 HIGH #50 THEN mt0 TIMEOUT ; --- timer alarm rountine and reset
: B0 ' blink0 mt0 ALARM ; --- assign blink0 as mt0 alarm vector
: blink1 #17 PIN@ IF #17 LOW #300 ELSE #17 HIGH #50 THEN mt1 TIMEOUT ;
: B1 ' blink1 mt1 ALARM ;
: blink2 #18 PIN@ IF #18 LOW #150 ELSE #18 HIGH #50 THEN mt2 TIMEOUT ;
: B2 ' blink2 mt2 ALARM ;
: blink3 #19 PIN@ IF #19 LOW #285 ELSE #19 HIGH #50 THEN mt3 TIMEOUT ;
: B3 ' blink3 mt3 ALARM ;
: blink4 #20 PIN@ IF #20 LOW #175 ELSE #20 HIGH #50 THEN mt4 TIMEOUT ;
: B4 ' blink4 mt4 ALARM ;
: blink5 #21 PIN@ IF #21 LOW #335 ELSE #21 HIGH #50 THEN mt5 TIMEOUT ;
: B5 ' blink5 mt5 ALARM ;
: blink6 #22 PIN@ IF #22 LOW #100 ELSE #22 HIGH #50 THEN mt6 TIMEOUT ;
: B6 ' blink6 mt6 ALARM ;
: blink7 #23 PIN@ IF #23 LOW #195 ELSE #23 HIGH #50 THEN mt7 TIMEOUT ;
: B7 ' blink7 mt7 ALARM ;
: clrpin $FF0000 OUTCLR ;
: BLINKEM
B0 B1 B2 B3 B4 B5 B6 B7 --- assign the alarm vector to timers
#10 mt0 TIMEOUT --- start all the timers
#20 mt1 TIMEOUT
#30 mt2 TIMEOUT
#40 mt3 TIMEOUT
#50 mt4 TIMEOUT
#60 mt5 TIMEOUT
#70 mt6 TIMEOUT
#80 mt7 TIMEOUT
;
: STOP-all
0 mt0 ALARM --- assign the alarm vectors to null, they will stop after the last timeout.
0 mt1 ALARM
0 mt2 ALARM
0 mt3 ALARM
0 mt4 ALARM
0 mt5 ALARM
0 mt6 ALARM
0 mt7 ALARM
$FF0000 OUTCLR --- clear all pins
;
: STOPEM ' STOP-all TIMERJOB ;
END
\ ?BACKUP
D.P - ' STOPEM TIMERJOB would work except for the unnecessary blocking delay which is a big no-no for any timer function. Just remove the delay and combine STOPEM and ALLOFF. The earlier <mask> OUTCLR is far easier to use rather than the ALLOFF though.
Well I had to try the FOR NEXT just to make sure I didn't miss anything in that writeup.
And DOH! 500 ms in the TIMERJOB, that would leave a mark. So here is the last example, it works repeatedly on and off correctly, dropped the loop and MASK the OUTCLR
IFDEF timer.fth
@rest org
FORGET timer.fth
}
pub ." timer 2017/06/17 11:49:45 " ;
@org W@ == @rest --- remember
\ * SIMPLE ROUTINE TO EXERCISE ALL 8 LEDS WITH TACHYON TIMERS
\ * AND ALARMS ON THE PROP QUICKSTART BOARD
\ *
\ * USE THE WORD "BLINKEM" TO START THE LIGHTS
\ * AND "STOPEM" TO STOP THE LIGHTS
TIMER mt0
TIMER mt1
TIMER mt2
TIMER mt3
TIMER mt4
TIMER mt5
TIMER mt6
TIMER mt7
: blink0 #16 PIN@ IF #16 LOW #500 ELSE #16 HIGH #50 THEN mt0 TIMEOUT ; --- timer alarm rountine and reset
: B0 ' blink0 mt0 ALARM ; --- assign blink0 as mt0 alarm vector
: blink1 #17 PIN@ IF #17 LOW #300 ELSE #17 HIGH #50 THEN mt1 TIMEOUT ;
: B1 ' blink1 mt1 ALARM ;
: blink2 #18 PIN@ IF #18 LOW #150 ELSE #18 HIGH #50 THEN mt2 TIMEOUT ;
: B2 ' blink2 mt2 ALARM ;
: blink3 #19 PIN@ IF #19 LOW #285 ELSE #19 HIGH #50 THEN mt3 TIMEOUT ;
: B3 ' blink3 mt3 ALARM ;
: blink4 #20 PIN@ IF #20 LOW #175 ELSE #20 HIGH #50 THEN mt4 TIMEOUT ;
: B4 ' blink4 mt4 ALARM ;
: blink5 #21 PIN@ IF #21 LOW #335 ELSE #21 HIGH #50 THEN mt5 TIMEOUT ;
: B5 ' blink5 mt5 ALARM ;
: blink6 #22 PIN@ IF #22 LOW #100 ELSE #22 HIGH #50 THEN mt6 TIMEOUT ;
: B6 ' blink6 mt6 ALARM ;
: blink7 #23 PIN@ IF #23 LOW #195 ELSE #23 HIGH #50 THEN mt7 TIMEOUT ;
: B7 ' blink7 mt7 ALARM ;
: clrpin $FF0000 OUTCLR ;
: BLINKEM
B0 B1 B2 B3 B4 B5 B6 B7 --- assign the alarm vector to timers
#10 mt0 TIMEOUT --- start all the timers
#20 mt1 TIMEOUT
#30 mt2 TIMEOUT
#40 mt3 TIMEOUT
#50 mt4 TIMEOUT
#60 mt5 TIMEOUT
#70 mt6 TIMEOUT
#80 mt7 TIMEOUT
;
: STOP-all
0 mt0 ALARM --- assign the alarm vectors to null, they will stop after the last timeout.
0 mt1 ALARM
0 mt2 ALARM
0 mt3 ALARM
0 mt4 ALARM
0 mt5 ALARM
0 mt6 ALARM
0 mt7 ALARM
$FF0000 OUTCLR --- clear all pins
;
: STOPEM ' STOP-all TIMERJOB ;
END
\ ?BACKUP
while it is nice to see many timers in action,
to blink a few LEDs I would definitely choose a table based approach with just a single timer,
far less code and much clearer.
And while timers are automatically linked, they could still be put into a TABLE and referenced by indexed address instead of so many definitions. But that would definitely NOT be a beginners example any more ;-)
@MJB - I suppose that sometimes I tend to code something that is compact and "elegant" (i.e. simple and efficient) but it is not always obvious to others
@caskaz - While I encourage anyone using Forth to always use it for debugging since it is so simple to do so, it is however helpful too if you copy and paste the text of your terminal screen as "code" so that we can see what it is that "didn't work". If you used D.P's code he last posted it should work since he checked it.
V4.5 update
I've fixed up a few things in EASYNET and doubled the speed of reading blocks of data from the W5500 chip too. I'm also making good use of FL which fast loads text to the specified file or to TEMP.TXT if no name is specified. At the moment you still have to hit the escape key after (or during) it has transferred and it automatically FLOADs the file. This is useful because once the dictionary has been compacted into EEPROM the searching is a lot slower, so FL allows pasting/sending a file without slowing down.
I'm feeling the line editing pinch so I will be implementing this feature next where we can a full line and it doesn't get processed until we hit enter. I will keep a history file on EEPROM to help with interactive editing. This does not affect TACHYON block load mode though, it will still compile word by word, number by number, with case sensitivity.
FEATURE REQUEST
I was thinking about this the other day that some of you fellow Tachyonites may be secretly wishing "oh, if only". So here's your chance, spell out your request, and if possible, I will see if I can implement it. You never know where this leads.
@MJB - I suppose that sometimes I tend to code something that is compact and "elegant" (i.e. simple and efficient) but it is not always obvious to others
Hi Peter,
yes - right, you
tend to code something that is compact and "elegant" (i.e. simple and efficient) but it is not always obvious to others
quite often ;-)
But if you are referring to @caskaz's / D.P.'s example above, which is definitely 'simple/straight forward', but not as elegant and efficient with all the code duplication ... (except for demonstration of timers) then I am really missing something ....
What ??
I expected you to agree to my statement ;-)
I'm feeling the line editing pinch so I will be implementing this feature next where we can a full line and it doesn't get processed until we hit enter. I will keep a history file on EEPROM to help with interactive editing. This does not affect TACHYON block load mode though, it will still compile word by word, number by number, with case sensitivity.
hmm -
I really liked the immediate compilation which tells if a word is misspelled / unknown immediately.
Not sure if getting this only at the end of the line is better ... ???
@MJB, Hah! just happen to check the forum with a glass of Knappstein red in hand
The demo code that D.P used was simple but not at all efficient, but it was good for the purpose of getting a feel of timers and in the process learning about how to use them and things to avoid like multiple cogs accessing I/O or using delays in timer alarms. So my post was an observation and also an acknowledgement.
If I introduce line editing and history that doesn't preclude word by word compilation since that is also the mode that TACHYON END block load mode uses. I'm also thinking of also making this detect if a file system is present and automatically perform a FL fast load. Any "improvements" only increase our options, never limit them.
I trued your latest code.
BLINKEN <-- Starting LED blinking
STOPEN <-- Blinking stop but some LEDs still on
BLINKEN <-- Re-starting LED blinking
STOPEN <-- Blinking stop but some LEDs still on
I retry BLINKEN & STOPEN.
Sometimes all LED are off.
Why?
[/quote]
while it is nice to see many timers in action,
to blink a few LEDs I would definitely choose a table based approach with just a single timer,
far less code and much clearer.
And while timers are automatically linked, they could still be put into a TABLE and referenced by indexed address instead of so many definitions. But that would definitely NOT be a beginners example any more ;-)
[/quote]
How about some source MJB. For me it's about participating in the Tachyon thread with source where/when I can. Also you never know what source could find a little bug in the kernel. This practice source also reinforced the rule "timer code cannot exceed the 1 ms execution or things becomes bad, quickly"
I trued your latest code.
BLINKEN <-- Starting LED blinking
STOPEN <-- Blinking stop but some LEDs still on
BLINKEN <-- Re-starting LED blinking
STOPEN <-- Blinking stop but some LEDs still on
I retry BLINKEN & STOPEN.
Sometimes all LED are off.
Why?
Hi caskaz,
I've tried the source a bunch of times and it works correctly, will download the lastest 4x5 spin and extend and try again.
I would like those:
iomask IN: dira 0
iomask OUT: dira 1
iomask LOW: outa 0
iomask HIGH: outa 1
I know dira 0 is the same as iomask INPUTS
and dira 1 is the same as iomask OUTPUTS
but I can't make output high or low whitout touching the dira register also.
Comments
Woke up early this morning and decided to implement the new FOR NEXT with a combined loop parameter and branch stack in hub RAM as indexing this in cog RAM is way too clumsy and consumes way too much cog memory anyway. FOR now pushes 4 (not a pun) parameters onto the loop stack and maintains 4 registers in cog memory for the current loop. I decided on calling the INDEX and STEP methods FROM and BY. With that accomplished here is the very first tentative step for 4.5's FOR NEXT.
Because it was way too awkward and slow to implement an efficient loop stack in cog memory that pushed and popped four parameters at a time, this was moved to hub memory and now there are a whopping 27 longs spare in the Tachyon cog. I think I'll go to town with that!
If you continue like this, then when P2 comes out, Tachyon will need no COG space any more ... ;-)
Maybe we can have 2 simultaneous RUMMODs now or one giant one ?
If the space is in one contiguous block.
can the values of outside loops still be accessed as before?
like i / j / k ...
I then tested the looping speed So, that's fine at 500ns/loop.
But what about setup and exit latencies?
FOR itself requires 1.666us to execute and stack the previous loop. The overall FOR NEXT overhead is 3us where it stacks and unstacks 4 parameters.
Checking the 4.4 kernel for comparison: The FOR NEXT loop times are the same although overhead is a fraction less. However to compare this properly we also run a single (non-looping) DOFOR LOOP which takes 4.666us vs the 3us in V4.5. So V4.5 has comparable FOR NEXT overhead but faster when compared to DOFOR LOOP overhead in V4.4.
V4.5 full FROM BY FOR NEXT overhead is 10.333us where FROM and BY are high-level wordcode at present. Obviously now that I have spare memory I can implement FROM and BY in PASM which I have just done: That's the same overhead as it is for a DOFOR LOOP in V4.4. No problem but obviously FOR NEXT overhead does not always require FROM or BY.
The J index is maintained in a register but K was deprecated in V4.4 since it would take too much to maintain K although it could be done in V4.5
btw - 42 longs spare at present for RUNMODs vs 23 normally
I try to understand TIMER.
I loaded TIMER.fth.
But any LEDs not on.
Why?
TIMERS are not numbered, you need to setup a TIMER <name> structure and then use that name as the address for TIMEOUT, TIMEOUT?, and ALARM.
For instance:
It is necessary to load and kick the timer off which is what 1 #mt0 TIMEOUT does and it also makes sure that the timer is linked into the timer list so that TIMER.TASK can service it every millisecond.
The simple counted FOR NEXT loops won't work although there was a much earlier version of the timers years ago that were simply preallocated and numbered. I found from that that it was easier to create a timer by name and link it into a list so that we could have as many or as little timers as we liked. To stop a timer simply load it with 0 either with: or since the first long in the timer structure is the actual timer part.
BTW, I see you must be using the new style FOR NEXT in V4.5 as that supports a loop index. Normally I is only used with DO LOOPs but in 4.5 all FOR NEXT loops always maintain an implied index FORM and step BY which if not specified defaults to 0 for the start index FROM and 1 for the step BY.
Try this, not tested for syntax errors, away from hardware
At startup all those timers aren't linked or recognized by Tachyon, but once user code calls TIMEOUT it links the timer in and maintains it every millisecond. Bear in mind that if the timer cog is expected to control outputs that you need to make sure that another cog hasn't tried to directly set the outputs since outputs are OR'd. Also since the timer cog services the timers every millisecond it also means that any alarm function should execute in well under a millisecond. If your alarm function requires millisecond delays etc then it is usually best if it is called on a regular basis and maintain its own prescaler to time lengthy events but of course there are lots of ways to slice and dice.
Yes I figured out that 0 is not valid for TIMEOUT.
This works but doesn't clear all the lights after stopem? I can't even #PIN LOW, but restarting blinkem flashes them all again?
Taking into consideration that this code is really only for a demonstration anyway perhaps we could code STOPEM by exploiting the fact that your alarm code sets the pin low (on its side) if it sees that it is high. So set the LEDs high and not low, do not disable the ALARM but simply change all the timers to 1, wait 2 ms, after which all the alarms will have set their outputs low, then write a 0 TIMEOUT for all of them and then release all the LEDs. Does that make sense? It may look awkward but that's because that's not how we would normally use the timers but the demo is useful as a learning exercise.
Alternatively you could run STOPEM as an extra alarm function to zero all the timeouts, and turn off all the LEDs.
Hint - rather than creating another timer for this, just use the "wdt" watchdog timer, set the alarm function for it to zero all the other timeouts and turn off the LEDs.
EDIT: I see I have TIMERJOB just for this very thing.
Right that OR'd thing again.
I'll work another demo.
Now some notes on those changes:
V4.5 Notes
FOR NEXT loops now maintain an index and step count even if they are not specified.
If they are not specified then the starting index is 0 by default and the step count
is 1 by default. LOOP is now the same as NEXT.
The traditional DO LOOP which uses a limit and a start index is not supported
but ADO will take a start index and a loop count.
+LOOP works just like NEXT except it modifies the step count
The thing to remember about these new loops is that the loop count is the actual
number of times that it loops for regardless of step count.
The loop and branch stack are now in hub RAM at $180 where every loop uses
4 longs. If Tachyon is run in other cogs that use loops then some stack space
should be allocated. Usually 3 or 4 levels is more than enough so 64 bytes max.
Usage:
FUNCTIONS
4.5 seems to be our new porting target from V3 then?
Executed [BLINKEM]. LEDs blinked.
Executed LEDs are off.
Again executed [BLINKEM]. This is different from 1st [BLINKEM].
Why 2nd [BLINKEM] is not blinking?
I use TachyonV4.4 DAWN 440170620.2200.
I tried your modified code.
2nd [BLINKEM] is not blinking.
It is same as uploaded movie.
Well I had to try the FOR NEXT just to make sure I didn't miss anything in that writeup.
And DOH! 500 ms in the TIMERJOB, that would leave a mark. So here is the last example, it works repeatedly on and off correctly, dropped the loop and MASK the OUTCLR
to blink a few LEDs I would definitely choose a table based approach with just a single timer,
far less code and much clearer.
And while timers are automatically linked, they could still be put into a TABLE and referenced by indexed address instead of so many definitions. But that would definitely NOT be a beginners example any more ;-)
@caskaz - While I encourage anyone using Forth to always use it for debugging since it is so simple to do so, it is however helpful too if you copy and paste the text of your terminal screen as "code" so that we can see what it is that "didn't work". If you used D.P's code he last posted it should work since he checked it.
V4.5 update
I've fixed up a few things in EASYNET and doubled the speed of reading blocks of data from the W5500 chip too. I'm also making good use of FL which fast loads text to the specified file or to TEMP.TXT if no name is specified. At the moment you still have to hit the escape key after (or during) it has transferred and it automatically FLOADs the file. This is useful because once the dictionary has been compacted into EEPROM the searching is a lot slower, so FL allows pasting/sending a file without slowing down.
I'm feeling the line editing pinch so I will be implementing this feature next where we can a full line and it doesn't get processed until we hit enter. I will keep a history file on EEPROM to help with interactive editing. This does not affect TACHYON block load mode though, it will still compile word by word, number by number, with case sensitivity.
FEATURE REQUEST
I was thinking about this the other day that some of you fellow Tachyonites may be secretly wishing "oh, if only". So here's your chance, spell out your request, and if possible, I will see if I can implement it. You never know where this leads.
yes - right, you quite often ;-)
But if you are referring to @caskaz's / D.P.'s example above, which is definitely 'simple/straight forward', but not as elegant and efficient with all the code duplication ... (except for demonstration of timers) then I am really missing something ....
What ??
I expected you to agree to my statement ;-)
hmm -
I really liked the immediate compilation which tells if a word is misspelled / unknown immediately.
Not sure if getting this only at the end of the line is better ... ???
The demo code that D.P used was simple but not at all efficient, but it was good for the purpose of getting a feel of timers and in the process learning about how to use them and things to avoid like multiple cogs accessing I/O or using delays in timer alarms. So my post was an observation and also an acknowledgement.
If I introduce line editing and history that doesn't preclude word by word compilation since that is also the mode that TACHYON END block load mode uses. I'm also thinking of also making this detect if a file system is present and automatically perform a FL fast load. Any "improvements" only increase our options, never limit them.
I trued your latest code.
BLINKEN <-- Starting LED blinking
STOPEN <-- Blinking stop but some LEDs still on
BLINKEN <-- Re-starting LED blinking
STOPEN <-- Blinking stop but some LEDs still on
I retry BLINKEN & STOPEN.
Sometimes all LED are off.
Why?
paste terminal screen as "code" like this:
while it is nice to see many timers in action,
to blink a few LEDs I would definitely choose a table based approach with just a single timer,
far less code and much clearer.
And while timers are automatically linked, they could still be put into a TABLE and referenced by indexed address instead of so many definitions. But that would definitely NOT be a beginners example any more ;-)
[/quote]
How about some source MJB. For me it's about participating in the Tachyon thread with source where/when I can. Also you never know what source could find a little bug in the kernel. This practice source also reinforced the rule "timer code cannot exceed the 1 ms execution or things becomes bad, quickly"
Hi caskaz,
I've tried the source a bunch of times and it works correctly, will download the lastest 4x5 spin and extend and try again.
I will update this post with results.
I would like those:
iomask IN: dira 0
iomask OUT: dira 1
iomask LOW: outa 0
iomask HIGH: outa 1
I know dira 0 is the same as iomask INPUTS
and dira 1 is the same as iomask OUTPUTS
but I can't make output high or low whitout touching the dira register also.