BASIC Stamp timing

(c) 1998, 2001 EME Systems, Berkeley CA U.S.A.
<stamp index> <home>

contents updated 2/3/2003 

 

BS2 Instruction timing

top

The Parallax literature states that the BS2 executes about 3000 instructions per second, or about 333 microseconds per instruction. The fact is that the execution time of different BS2 instructions varies widely. Inquiring minds want to know. Sometimes it really matters to get the timing exactly right. The following observations come from a triggered analog 'scope hooked to pins on a BS2. The table below lists a variety of PBASIC instructions along with the time required in midroseconds to execute them.

For example, in this sequence:

loop:
high 1    ' 140 microseconds high
low 1     ' 140 microseconds low
high 1    ' 140 microseconds high
goto loop ' 290 microseconds, P1 still high

The entire loop executes over and over again with a total period of 710 microseconds. P1 is low for 140 microseconds and high for 570 microseconds. One can infer that the low instruction each takes 140 microseconds to execute. By a similar method, with a high sandwiched between two lows, one can infer that the high instruction also takes 140 microseconds. And what about the goto? Since the whole loop takes 710 microseconds, and the sequence high-low-high takes 3*140=420 microseconds, the inference is that the goto takes 710-420 = 290 microseconds.

(Followup: In another experiment, using different BASIC Stamp 2e, I came up with 145 microseconds for high and low, and 285 micrseconds for the goto. These results depend on a number of factors, not only the resonator in the individual Stamp, but also the calibration of my 'scope timebase, and the eyeball method of reading results from the oscilloscope screen.)

Most of the timings listed below are determined by similar inferences. If the instruction does not itself affect an output pin, we can sandwich it in between two instructions that do ping an output pin, and then, measure the effect of adding the instruction, and subtract to find the time for the unknown instruction.

loop:
high 1
' instruction to test
low 1
goto loop

The time for the instruction under test is equal to the total high time, minus the 140 microseconds required to fetch and interpret the low.

The BASIC Stamp is relatively slow because each instruction and all its arguments must be retrieved from the serial eeprom. Most of the time taken by an instruction is due to that read and interpret operation, not so much due to the operation itself. For example, The HIGH 1 instruction as it is actually executed in the machine code on the chip takes less than one microsecond. The other 139 microseconds are thrown away to reading in the instruction and the pin number and setting up the final one microsecond execution of the actual command.

Many Stamp instructions such as PAUSE or PULSIN or SEROUT in themselves contain a time parameter. It is usually easy to distinguish that time from interpretation time that leads up to it.

Take the following measurements as approximate. In addition to errors on my part--I was not attempting to be very precise in eyeballing the oscilloscope screen--there are other small factors that influence the result. For example, it takes the Stamp interpreter about 8 microseconds longer to fetch and execute high 11 than it does for high 1 or for high 8. This is probably due to the way that variables are stored bit by bit in the stamp eeprom. See Brian Forbes' book for details on the length of different instructions and how they are stored in eeprom. The powers of two (0,1,2,4,8..,32786) are stored in an especially compact form that takes a shorter time for the interpreter to read out of eeprom. Similarly, an instruction will take about 10 microseconds longer if the argument is a variable rather than a constant. And it takes longer to fetch and interpret a bit address than it does for a nib, which takes longer than a byte, and the shortest time is for a word variable. This might seem counterintuitive, since words are the biggest chunks, until you realize that there are only 16 words in the Stamp's RAM memory, while there are progressively more bytes, nibs and bits. So it takes a larger address to specify a certain bit, and the larger address takes more time to fetch. These are small differences, only a couple of microseconds more to fetch a byte instead of a word.

Steve Parkis has provided a second and more accurate opinion on some of timing values, and some additions to the list. These he obtained not by eyeballing an oscilloscope, but with an accurate timing chip based on an SX microcontroller, the TM1. Want your own TM-1? The Scenix SX-18 code is posted at Steve Parkis' web site. The values in [...] below are courtesy of Steve.

The BS2e operates at exactly the same speed and with the same instruction set as the vanilla BS2, and in every case that I have tested, instructions as expected run at exactly the same speed on the BS2e as they do on the BS2. The BS2e has the additional PUT, GET and RUN instructions.

The BS2SX runs at 2.5 times the clock speed of the BS2. Many BS2SX instructions fetch and execute in 2/5 of the time required for the same instructions by the BS2. Multiply the BS2 times by 2/5 to find the BS2SX times. Exceptions are the parameterized time values, such as those in the pause, pulsin, or serout, commands.

The new BS2p uses an Ubicom (formerly Scenix) SX48 chip running at 20 megahertz in turbo mode, so the operative times are quite different from the earlier Stamps. There is no exact multiplier of the time scale that applies to all of the instructions. In general, each simple BS2p instruction runs about 20% faster than the same instruction on the BS2sx. I have left a column for the BS2p, and I plan to fill in some values... some rainy day. Similarly, the BS2pe is an Ubicom SX48 runnint at 8 megahertz in turbo mode. The BS2pe has approximately the same throughput as the BS2, alt;hough it is slightly faster on most instructions.

All times are in microseconds

Which is the fastest stamp, the BS2sx or the BS2p? The BS2p. It executes a HIGH 5 command in 41 microseconds, vs 56 for the BS2sx. The BS2 or the BS2e would execute the HIGH 5 in about 140 microseconds. (And, the BS2pe is a beta version of the stamp, clocked at 8 mhz to minimize power drain, and it executes HIGH 5 in 105 microseconds.)

 

simple I/O & timing

BS2

BS2e

BS2sx

BS2p

BS2pe

high 1

140 [146]

Same as BS2

56

41

105

low 1

140 [146]

.

56

41

105

input 1

[143]

.

.

.

pulsout 1,55 ' constant

220

.

90

.

pulsout 1,x ' variable

230

.

.

.

pulsout 4,1

[216]

.

.

.

pulsout 4,2

[219]

.

.

.

pulsout 4,10

[239]

.

.

.

pulsin 1,1,x

240

.

.

.

RCtime 1,1,x

220

.

90

.

out0=1 ' constant

200

.

80

.

out0=x ' variable

220

.

.

.

pause 1

[1233]

.

.

.

pause 2

[2236]

.

.

.

.

.

.

.

.

Data Access

BS2

BS2e

BS2sx

BS2p

BS2pe

lookup x,[0,1,2],y

.

.

.

.

x=0

[576]

.

.

.

x=1

[577]

.

.

.

x=2

[576]

.

.

.

not in list, x=3

[565]

.

.

.

lookdown x,[0,1,2],y

.

.

.

.

x=0

[1287]

.

.

.

x=1

[1286]

.

.

.

x=2

[1286]

.

.

.

not in list, x=3

[1279]

.

.

.

get 63,x ' x is byte var

.

200

.

.

put 62,x ' x is byte var

.

225

90

.

put 62,255 ' constant

.

215

.

.

put 62, 8 ' constant

.

230

.

.

read 0,x

550

.

220

.

write 0,x
depends on eeprom

6000

.

.

.

Branching:

BS2

BS2e

BS2sx

BS2p

BS2pe

goto label

245 [255]

245

100

.

gosub label
label: return

[787]

.

.

.

if 1 then label

360

.

.

.

if x>1 then label

470

.

.

.

if x = y then b   'false

[366]

.

.

.

if x = y then b   'true

[475]

.

.

.

branch x,[b,c,d]

.

.

.

.

when x = 0.

[334]

.

.

.

when x = 1

[385]

.

.

.

when x = 2

[439]

.

.

.

when x = 3 or more

[297]

.

.

.

for i = 1 TO 1 : next

[825]

.

330

.

for i = 1 TO 2 : next

[1582]

.

.

.

for i = 1 to 10 : next

[7691]

.

.

.

run 1

.

750

300

.

Math operations:

BS2

BS2e

BS2sx

BS2p

BS2pe

i = 1

181

same

.

.

i = x

196

as

.

.

i = z.LOWBYTE

196

BS2

.

.

+, - constant

140

.

.

.

+, - variable

150

.

.

.

* constant

130

.

.

.

* variable

140

.

.

.

/ constant

180

.

.

.

/ variable

200

.

.

.

*/ constant

210

.

.

.

MAX, MIN constant

240

.

.

.

MAX, MIN variable

170

.

.

.

SPI synchronous serial (all for the BS2 or BS2e)

shiftout 15,14,lsbfirst,[x] ' x a byte var

990 microseconds total
a) 450 microseconds to first bit
b) 60 microseconds bit width & clock period, 16.667 khertz transmission.
c) 15 microseconds to first clock pulse
d) 60 microseconds extra after last bit

shiftout 15,14,lsbfirst,[x,x] ' x a byte var

e) 240 microseconds between bytes.

shiftin 15,14,lsbpre,[x]

1210 microseconds total
a) 550 to first clock pulse
b) 60 microsecond clock period
c) 0 (data changes on clock)
d) 180 dead time after byte

lsbpre:
first bit of data is sampled 15 microseconds before the first clock pulse.
lsbpost:
first bit of data is sampled 15 microseconds after the first clock pulse.

lsbpost timings are about the same as lsbpre above.

msbpre and msbpost are slightly faster, with timing (a) 460 microseconds instead of 550.

shiftin 15,14,lsbpre,[x,x]

e) 320 microseconds between items

BS2sx, all timings including clocking are 2.5x faster.

BS2P,

SERIN and SEROUT timing--please follow this link

Notes on the timings.


Question: I need to time the duration of an event that is longer than the maximum duration of tha PULSIN command (0.131 second), for example, the time it takes for a bicycle wheel to make one revolution, or the time for a heartbeat. How can I do that on the BASIC Stamp?

external chip

One solution is to use an external timing chip that has a larger range or special features. Let the chip do the measurement or the long term timing, and in the mean time the Stamp can do other things and come back later to retrieve the result using a SERIN command. Here are some external timer chips that are popular for use with the Stamp.

http://home.earthlink.net/~parkiss/tm1summ.txt
many functions, resolution to 1 microsecond in hours
http://www.phanderson.com/timer_1_2.html
10 microsecond resolution in one minute, or 1 milliscond in 1 hour
http://www.phanderson.com/clock.html
1, 10 and 100 ms, 1, 10 and 30 secs, 1, 5, 10, 30 & 60 mins period generator.
http://www.al-williams.com/awce/pak7.htm
8 channels, 5 microsecond resolution

timer loop

But you can time longer loops to about 1 millisecond of resolution without having to hook up any external chips. Here are two possible tight timing loops. The first keeps track of the time a pin stays in a low state, and the second measures the time a pin stays high.

  ' snippet measures the time P0 stays low.
   xc var word
   loop1:    ' 1418 loops per second, 7.05E-4 seconds per loop
      xc=xc+1
      branch in0,[loop1]  ' count until in0 goes high


' snippet measures the time P0 stays high. xc var word loop2: ' 1426 loops per second, 7.013E-4 seconds per loop xc=xc+1 if in0 then loop2 ' count until in0 goes low debug dec xc**45960,cr ' ** converts the count to straight milliseconds

The conversion factor in the second program converts the arbitrary units of time, ticked off at 1426 per second, into milliseconds for display. The factor for use with the ** operator comes from 1000/1426 ~= 45690/65536. This logic is descibed elsewhere.

Be aware that the result depends slightly on the individual Stamp and on the temperature (at extremes), due to variation in the vibration frequency of the ceramic resonator that runs the Stamp. Moreover, the result also depends on details of any larger program that the above loops might be a part of, details you might not expect to have anything to do with it. The number of subroutine calls in the program and the exact variables used have a significant effect. In another test at another time using what I think was the same code, and using p1 instead of p12, I got 1516 interations per second instead of 1426. The message is that it is necessary to calibrate the loop in the final version of the program.

A complete cycle timer demo

The following program times both the LOW and the HIGH levels for a cyclic waveform like you might get from a complete revolution of a wheel, or a heartbeat monitor. I timed the loops on a BS2e by using an external precision clock generator to give one second LOW and one second HIGH input. The timer loop for the LOW level using the BRANCH command turned out to give 1177 counts per second, and the timer loop for the HIGH level using IF-THEN turned out to give 1190 counts per second. Both are better than one millisecond resolution. The program uses separate calibration factors for the LOW and the HIGH level to convert them to whole milliseconds. The program then adds up the two halves to get the total period, and then divides that into 1 (100000) to give frequency resolved to 2 millihertz.

The timings and thus the conversion factors depend by a few percent on the exact program, aspects you might not expect would affect the results, in particular, how many subroutine calls there are in the program and the exact variables that are used. For best results, remember to run a calibration check before finalizing your program.

The program once calibrated works well for input frequencies I tested from 0.2 hz to 10 hz, giving results correct to within one millisecond. It has an emergency exit timer is set at 7 seconds (to keep the program from locking up if the P12 is stuck). The timeout period can be made as long as 27 seconds with the technique shown.

'{$STAMP BS2e}
' Long period (>0.1 second to 7 seconds) timer demo
' Times one entire cycle 01 in train of ...01010...
' Duty cycle 10% to 90%
' This demo uses Stamp pin P12
' Program holds in the "wait" loops until a 1->0 transition
' then times the whole 01 cycle
' The resolution is better than 1000 counts per second on a BS2e
' Also has an exit to keep the timer from locking up
' using bit 13 for the exit gives 7 second per loop timeout
' using bit 15 instead would give 27 second per loop timeout.
' http://www.emesystems.com/BS2speed.htm
' Tracy Allen, mailto:tracy@emesystems.com
   
t0 var word    ' time LOW
t1 var word    ' time HIGH
tt var word    ' total time (also temporary timer for wait)
fq var word    ' calculated frequency
tf var bit     ' timeout flag bit
' -------->
top:
  ' gets time in milliseconds
  ' and displays time LOW, time HIGH, total time, & frequency
  gosub aTimer
  debug dec t0,tab,dec t1,tab,dec tt,tab, dec fq/1000,".",dec3 fq,cr
goto top
   
aTimer:
' each loop has a timeout condition
' in case the pin is locked up
' returns with LOW, HIGH and total time in milliseconds
' and frequency in millihertz
tt=0       ' initialize counters
t0=0
t1=8192    ' preset bit13 in this one
wait0:     ' wait here while p12 is LOW
  tt=tt+1
  branch tt.bit13|in12,[wait0]
  tt=8192   ' preset bit13
wait1:     ' wait here while p12 is HIGH
  tt=tt+1
  if tt.bit13&in12 then wait1
timer0:    ' time LOW duration
  t0=t0+1
  branch t0.bit13|in12,[timer0]
timer1:    ' time HIGH duration
  t1=t1+1
  if t1.bit13&in12 then timer1
doneTiming:
  tf=t0.bit13|~t1.bit13 ' flag=1 if timeout occured
  t0.bit13=0     ' zero the timeout bits
  t1.bit13=0
  ' debug "cal: ",dec t0,tab, dec t1,cr    ' for calibration
  ' use this raw data to adjust the calibration factors that follow
  ' factor0=1000/t0*65536,  factor1=1000/t1*65536
  t0=t0**55634  ' convert LOW time to milliseconds
  t1=t1**55073  ' convert HIGH time to milliseconds
  tt=t0+t1      ' total time
  fq=50000/tt*10+(50000//tt*10/tt)*2 ' calculate frequency
return
   

Remember, it is important to check the calibration factors for each stamp and for each final program. The result depends on the ceramic resonator. It also depends on fine details of the program that you might not expect to have anything to do with the counting rate. For example, it depends on how many subroutine calls there are in the program and which exact variables are used for the timers. The above values will be approximately right, but for best results, the final calibration depends on you. Check the calibration by putting a square wave on one second high and one second low into p12, and use the raw t0 and t1 values from the calibration debug statement to calculate the ** multipliers.


<top> <index> <home> logo < mailto:info@emesystems.com >