Cliff Hacks Things.

Monday, May 26, 2008

PropellerForth issue tracker

I've gotten a few blog comments from folks who are using PropellerForth. Yay!

A few comments have reported bugs.

I don't closely follow comments on my blog (though I respond when I have time) -- but I do get a email when someone reports a bug at the PropellerForth issue tracker over on Google Code!

If you're using PropellerForth and find a problem, please report it there. (Please don't post "bugs" like "You haven't released a binary in a few months" or "You haven't imported your Subversion repo." I assure you I'm working on both as time permits, but this isn't my day job.)

Of course, if you're using PropellerForth and haven't found a problem, I'd love to hear about that too! Blog comments are a fine place for that, or send me an email if you can track down my address. ;-)

Labels:

Monday, May 19, 2008

Trig in PropellerForth

Among many other nice features, the Parallax Propeller microcontroller has a single-quadrant sine table in ROM. This makes implementing sine and cosine fast and simple, for medium-precision (13-bit) work.

Here's a simple port of Parallax's routines to PropellerForth.


hex

\ Address of table in ROM
E000 constant sin-base

\ Computes the sign of an angle.
\ The angle is a 13-bit number (0x1FFF = almost 360 degrees).
\ The result is a 16.16 fixed-point signed integer.
: sin ( angle13 -- n16_16 )
dup 1000 and >R \ Stash a flag for Quadr.3/4 onto the rstack
dup 0800 and if negate then \ Invert angle for Quadr.2/4
2* sin-base or H@ \ Compute word address and fetch
R> if negate then ; \ Negate result for Quadr.3/4

: cos ( angle13 -- n16_16 ) 0800 + sin ;


With PropellerForth v8.05 this gives a runtime of 1088-1152 cycles for sin, and an additional 240 cycles for cos -- 31x slower than a native implementation. (v8.02 will be slower.)

It can be made slightly faster by (1) replacing the numbers with CONSTANTs and (2) inlining 2*, which is not a primitive, as "1 lshift", for a runtime of 993-1056 cycles -- 28x slower than native, at the cost of a 22 more bytes of space.

Update: Huh. Through some optimizations to the kernel, I can shave off another 100 cycles -- for a runtime of 896-926 cycles. However, it costs 248 bytes in the kernel, and eliminates a hook I was hoping to use for single-step and breakpoints. I'll have to weigh this.

Labels:

Saturday, May 17, 2008

Another PFcam teaser

With the help of the new scope, I wrote a new OV6620 driver that includes chroma support. With the camera reconfigured to run at 25fps, it records 88x72 YUV images at full rate. It's 210 bytes, and integrated into a custom build of PropellerForth.

A few lines of Forth later, I had code to generate PPM images. (PPM is possibly the world's easiest format to produce.) Here are some samples. Forgive the yellow cast and the chroma noise -- my new workbench doesn't have lights yet, and the camera's low-lum performance is poor. That, and I probably have bugs in my YUV matrix code.




Once I'm confident in the design, I'll post the driver and Gerbers for the PCB (and if anyone actually wants one, we can go in on a BatchPCB order).

I've got 8 spare Propeller I/O pins...gotta figure out what to do with 'em. Servo connectors? Debug LEDs? Audio?

Edit: Yes, I did indeed have bugs in my matrix code! They're fixed now.

Better chroma makes engineer happy.

Labels: ,

Tuesday, May 13, 2008

I can see clearly now, the waves are gone

I'm a pretty hardcore guy when it comes to debugging software. I pride myself on my automated tests, I routinely sling debuggers and disassemblers around, and so forth.

When it comes to hardware, though, I've been stuck firmly in the printf era. The printf era is pretty damn powerful, of course, when you've got Forth at your side -- the PropellerForth VGA and NTSC display drivers, for example, were written using only a multimeter and ancient HeathKit frequency counter.

However, I'm not a big fan of making my own life difficult. After many years of fondling scopes at Fry's, I finally gave in and picked up a Tektronix TDS1012B. It's at the high-end of the extreme low-end: decent sampling rates for embedded work and so forth.

A couple weekends ago I taught myself printed circuit board and built the PFcam, the Propeller-based image processing board I mentioned in my last post. I'm not an analog electronics whiz, so the first thing I did with my scope was test the new board.

Sure enough -- here's a capture of some I2C traffic between the Propeller and the camera:


Notice the square waves are wearing hats? The regulator's output was bouncing between 3.3v (the goal) and 5v (the input). A decoupling cap later, my SCCB code (Omnivision-speak for I2C) is much more reliable:


Now all the I2C/SCCB chips play nice together, and nobody wigs out and sees a stop condition where there ain't one.

I'm happy with the scope so far. I'll most more details on the PFcam board later on.

Labels: ,

Wednesday, May 07, 2008

Got tired of my CMUcam.

What embedded machine vision system...

  • is easier to interface (and more powerful) than a CMUcam2;

  • has eight cores at 96MHz, six of which can perform user-defined image processing;

  • can be programmed and debugged interactively in-system using Forth or assembler;

  • runs open-source code built using open-source tools;

  • costs less than $100 in parts; and

  • can be built in an evening with only basic through-hole soldering?



Yeah, I couldn't find one either.




Mmm, 192 MIPS of pixel-devouring goodness. I love my overclocked Propellers.

Edit: What kind of image processing, you ask? How about realtime ASCII art!



I hope to get middle-mass and edge tracking working shortly.

Labels: ,

Sunday, February 10, 2008

PropellerForth tidbit: running a word at boot time

By default, PropellerForth v8.01 starts up by running the built-in word INTERACTIVE. This displays the version screen, enables multitasking, and drops into the Forth interpreter by calling the standard word QUIT.

Once you've built an application and burned it to EEPROM using savemem, you may not want PropellerForth to be interactive anymore! Alternatively, you might define a new version of INTERACTIVE that does some additional setup -- SD card initialization, for example -- and then calls the old INTERACTIVE.

I neglected to provide an easy way to run your own words on boot in v8.01, so here it is:

decimal
\ The address of a variable containing the first word to run.
140 constant 'boot

\ Sets a word to run at startup.
\ Example: BOOT MYSTARTUP
: boot ( "name" -- )
' 'boot ! ;


You can simulate a reboot -- assuming you haven't started any other tasks -- by invoking an arcane Propeller machine instruction:

32 0 coginit


Once you're satisfied, savemem and reboot!

Labels:

PropellerForth tidbit: changing the console speed

PropellerForth 8.01, by default, sets the console at 19,200 baud.

This is mostly for compatibility, and doesn't tax the serial driver; in practice, the I/O routines spend most of their time sleeping. At 80MHz the kernel I/O routines have no trouble doing 230kBps. Increasing the speed can really make a difference when moving a bunch of data to the console, such as when dumping a block of RAM or listing a block of source.

Currently the speed is hardcoded, but that doesn't mean we can't change it. :-)

\ Set the console I/O speed in bits per second.
\ Takes effect immediately.
\ Example: 115200 console-speed
decimal
: console-speed ( u -- )
second swap / \ get # of cycles per bit
176 L! ; \ change the kernel's constant bit time


The magic number there, 176, is the location of the bit time constant inside the kernel -- we use L!, local-store, to override it in the running kernel image. 176 is valid for 8.01 and early alphas of 8.02, but may change in the future -- eventually there will be a supported way of doing this.

Have fun!

Labels:

Thursday, February 07, 2008

New features for PropellerForth 8.02

I'm fleshing out the feature set for PropellerForth 8.02, due out in a couple weeks. This is just a teaser post describing what I've been working on; no code yet. :-)

The main new features at this time:
  • Block word set for reading/writing block devices and loading source code from storage
  • EEPROM Block backend, for treating the program EEPROM as a block device
  • SPI-mode SD/MMC card Block backend


The SD support is a traditional Forth disk layer: it doesn't implement FAT or any other filesystem. Instead, it lets you directly address blocks on the disk. By default you can use this to save and edit source code directly on the card, but it could also allow an enterprising individual to implement filesystem support.

Because source is stored in raw disk sectors instead of files, getting at it from a "real" computer will require a tool like dd. Since this is an embedded system, that doesn't bother me too much, but I wouldn't complain if someone implemented FAT16! :-)

The actual interface code is a direct port of Tom Rokicki's FSRW SPIN implementation. It's about 1KiB and currently gets about 5-6KiBps at 80MHz, twice the throughput of the SPIN version, despite being a pretty literal port. I hope to optimize it further before release.

Now, to work on a target compiler -- so that users of 8.02 can recompile their whole system from sources stored on EEPROM or SD.

Labels:

Tuesday, January 01, 2008

PropellerForth v8.01 alpha; HYDRA tidbit

After my winter sprint, PropellerForth v8.01 alpha has been posted on the project page. (I'm using Ubuntu-style version numbers, so 8.01 is the January 2008 release.)

As it says in the release notes, the version currently available there is an alpha release. It's been tested, it works, and it's probably close to the final release, but I reserve the right to make changes in the near future before pronouncing it official. In particular, the docs are not nearly complete -- I'm working on it.

Source hasn't been posted yet; still trying to figure out how best to do that.

A HYDRA PropellerForth Tip



The HYDRA board is one of PropellerForth's officially supported targets. One of its slick (but undocumented) features is how it handles the on-board EEPROM when a cartridge is inserted: it gets remapped to a higher address space, but remains accessible.

PropellerForth's EEPROM words (included in the v8.01 'full' release) support addressing multiple EEPROMs on the I2C bus, using Atmel-style device and page addressing. The visible address space is:

  • No cartridge: onboard EEPROM at 0x00000 - 0x1FFFF

  • Cartridge: onboard EEPROM at 0x20000 - 0x3FFFF, cartridge at 0x00000 - 0x1FFFF



This opens some interesting possibilities:

  • Programs could use both EEPROMs together, for 256KiB of bulk data storage.

  • A reasonably simple Forth word could copy one EEPROM to the other, to clone a cartridge or back up the on-board software.



I'll leave those as exercises to the reader; for now, here's a word, ?cart, that checks whether a cartridge is currently inserted in the HYDRA.

hex \ the base of champions
: ?cart ( -- flag )
\ Try to read base of remapped on-board EEPROM.
20000 ['] eeread catch \ Guard with catch to intercept failure
nip \ Discard the address (if failed) or the result (if succeeded), leaving exception
0= ; \ If the exception is 0 (none caught), return 'true', otherwise 'false'.


To help you test it, here is the cartridge monster. (Hey, I can only type in so many dry source examples before I try to make one funny.)

: O_O \ See, it's a face. ... well, I thought it was funny. :-P
?cart if exit then \ Don't bug the user if a cartridge is present
begin \ Loop and complain...
." CARTRIDGE MONSTER WANT CARTRIDGE" cr
1 seconds wait
?cart until \ ... until a cartridge appears
." NOM NOM NOM BURP" cr ;


(Hot-swapping cartridges on the HYDRA should work fine as long as the EEPROM is not being accessed when it happens -- just be careful to insert the cartridge straight.)

Labels: ,

Thursday, December 27, 2007

PropellerForth I2C EEPROM driver

This afternoon, I curled up with the datasheets for Atmel's I2C EEPROMs, like the one the Propeller uses to boot. I haven't written any I2C code in a while, so I had to relearn the protocol.

Then, I went out for phở. The place down the street is quite tolerant of me sitting for half an hour drawing state diagrams.

Noodles completed, I set to work interfacing PropellerForth to the boot EEPROM. It's been a resounding success. The sources are below; they should work on the old 20061124 image, if anyone still has a copy. Otherwise, they'll work on the 20080101 image once I make it available.

Executive summary: these words implement a hardcoded interface to an EEPROM on pins 29:28. This lets you access data stored in EEPROM (using ee@ and ee!). For the first time, you can save a bootable image of your current PropellerForth system to EEPROM, using the saveforth word.

The emphasis there shows how excited I am about this. Bootstrapping my NTSC display driver requires me to carefully type in Forth code for several minutes, each time I reboot! The words are factored so I can test as I go, but it's still painful. This is about as revolutionary as when I got cassette storage working with my TRS-80 Model 100 and no longer had to type in BASIC listings.

The interface is not spectacularly fast; page reads can stream data at about 20kbps, and page writes aren't implemented yet. saveforth can take a minute or two. It is, however, pretty simple and readable, assuming you read Forth.

If you don't read Forth, it's worth taking a glance at the code anyway. It's not the best Forth code -- I'm rusty -- but casual observers may notice that code toward the top of the listing tends to be low-level (written in terms of machine registers like OUTA). As you progress through the listing, the language changes: first to a physical-level description of I2C, then to a layer-2 description of how to talk to the EEPROM. In my day job we refer to this as a domain-specific language. Forth programmers didn't coin this term, because they didn't need one: it's the natural way to build systems in Forth.

So: the code! I've hand-escaped this, and may have introduced errors in the process.

Update: sneaky bug fixed by addition of mkboot word. Details below the source listing for anyone who doesn't want to figure it out for themselves. :-)


hex \ my dad will surely rib me for not using octal

\ Sets the value of an output pin (identified by 'mask').
\ Output is 0 if 'f' is false, 1 otherwise.
: pin! ( f mask -- )
swap if
OUTA L@ or
else
OUTA L@ swap -and
then OUTA L! ;

\ Sets a pin (identified by 'mask') to output.
: pin> ( mask -- )
DIRA L@ or DIRA L! ;

\ Sets a pin (identified by 'mask') to input.
: <pin ( mask -- )
dira L@ -and dira L! ;

\ I2C pin masks. Workaround for CONSTANT bug (CONSTANTs are 16-bit).
: eesdamask 20000000 ; \ pin 29
: eesclmask 10000000 ; \ pin 28

\ Words for working with the EEPROM SDA line.
: eesda! ( f -- ) eesdamask pin! ;
: eesda@ ( -- f ) INA L@ eesdamask and ;
: eesda> eesdamask pin> ;
: <eesda eesdamask <pin ;

\ Words for working with the EEPROM SCL line.
: eescl! ( f -- ) eesclmask pin! ;
: eescl> eesclmask pin> ;
: <eescl eesclmask <pin ;

\ Initializes the bus; useful for resetting a b0rked chip
: eeinit
\ SCL high/output, SDA input
1 eescl! eescl> <eesda

\ Drain the device until we get an ACK, or we try 9 times
9 0 do
0 eescl! 1 eescl!
eesda@ if unloop exit then
loop ;

\ I2C start condition
: eestart
1 eescl! eescl>
1 eesda! eesda> 0 eesda!
0 eescl! ;

\ I2C stop condition
: eestop
1 eescl!
1 eesda!
<eescl
<eesda ;

: eerxbit ( -- bit ) 1 eescl! eesda@ 0 eescl! ;

\ Transmits a byte, MSB first. Returns ack bit.
: eetx ( byte -- ackbit )
8 0 do
dup 80 and eesda!
1 lshift
1 eescl! 0 eescl!
loop drop
\ Read ack bit
<eesda eerxbit
0 eesda! eesda> ;

\ Receives 8 bits, sending 'ackbit' in response
: eerx ( ackbit -- byte )
<eesda
0 8 0 do
1 lshift
eerxbit if 1 or then
loop
swap eesda! eesda>
1 eescl! 0 eescl! ;

\ Like eetx, but throws file error if not acked.
: eetx? ( byte -- )
eetx if -25 throw then ;

\ Sends a two-byte big-endian address.
: eetxaddr? ( addr -- )
dup 8 rshift FF and eetx?
FF and eetx? ;

\ Reads a byte out of EEPROM. Throws on failure.
\ If you're feeling cryptic you could rename this
\ eec@.
: eeread ( addr -- byte )
eestart A0 eetx? eetxaddr?
eestart A1 eetx? 1 eerx
eestop ;

\ Writes a byte to EEPROM. Throws on failure.
: eewrite ( byte addr -- )
eestart A0 eetx? eetxaddr? eetx? eestop ;

\ Reads a little-endian 32-bit integer from EEPROM.
\ Throws on failure.
\ Eventually this should use page read.
: ee@ ( addr -- x )
0 4 0 do
8 rshift
over eeread 18 ( hex ) lshift or
swap 1+ swap
loop nip ;

\ Waits for the EEPROM to become available. Required
\ after a write operation unless you're damn sure you're
\ going to be busy for a while.
: eewait begin eestart A0 eetx eestop 0= until ;

\ Writes a little-endian 32-bit integer into EEPROM.
\ Throws on failure.
: ee! ( x addr -- )
4 0 do
2dup eewrite
1+ swap 8 rshift swap
eewait
loop 2drop ;

\ Reads 'count' bytes from the EEPROM starting at 'addr'
\ into a buffer at 'dest'.
: eereadpage ( dest count addr -- )
over 0= if drop drop drop exit then
eestart A0 eetx? eetxaddr?
eestart A1 eetx?
( count ) 1- 0 do
0 eerx over c!
1+
loop
1 eerx swap c!
eestop ;

\ Updates fields in the image preamble to allow
\ us to boot properly.
: mkboot
here 08 H!
here 08 + 0A H!
here 0C + 0E H!
here 10 - 10 H! ;

\ The word that made my evening: saves the
\ current dictionary, kernel, and bootloader
\ as a bootable image in EEPROM. Takes a
\ while, but prints cute little status updates.
\ The resulting image will boot to the interpreter
\ running the standard startup vector.
\
\ CAVEAT HACKER: this will erase whatever's in
\ EEPROM, in case that wasn't apparent.
: saveforth
." Saving 0 to " here aligned . cr
mkboot
here aligned 0 do
i @ i ee!
i FF and 0= if
." ..." i . cr
then
4 +loop
." Done." ;


Update about mkboot: the original listing I posted contained a subtle bug. When booting the saved EEPROM image, 12 bytes in the middle of your first user-added colon definition were destroyed. I didn't notice this because my first def was some test word that I didn't use after boot.

As described in my article on reverse-engineering the Propeller's image format, the SPIN interpreter needs some RAM to start. My image preamble (as used in propasm, and by extension PropellerForth) uses 12 bytes of RAM for the initial SPIN stack before the machine code is loaded.

The preamble tells SPIN where to put its stack; propasm indicates that it should go right after the end of the code. But if you've added code at runtime and saved Forth, the preamble needs to be updated -- otherwise SPIN will clobber an area just past the end of the original image, which is probably in the middle of a definition.

So, mkboot updates the preamble.

Labels:

PropellerForth stirrings

A few weeks ago, I was poking around on the Silicon Valley Forth Interest Group webpage. I was surprised to see this in their minutes:
There has been no success in contacting the author of Propeller Forth - written locally by Cliff L. Biffle


As of about February 2007, my email got so clogged with spam that I simply stopped checking it. (Apologies to anyone who's been trying to contact me.) So, I had no idea that the SVFIG was interested in PropellerForth. I've had isolated inquiries from a couple people, but all in all, my original posts (here and on the Parallax forums) were met with general disinterest.

So, I've resurrected the project. Not much progress yet -- mostly, I'm cleaning up the sources so I can release them without sullying my good name. :-) People poking around the PropellerForth site on code.google.com might notice some documentation appearing in preparation for the source release.

Ever since I moved out of my folks' house, I've gone back and spent a few nights there for Christmas. Since my sleep schedule is rather different from theirs, I wind up having a lot of free time -- and since I'm away from work and distraction, it's traditionally been a great opportunity to hack. (Projects that got their start or reached significant milestones over Christmas include Mongoose, Cesta, PropellerForth -- and, 15 years earlier, WebElite.)

This year I learned the NTSC spec, the US analog television standard. The existing NTSC drivers for the Propeller are cryptic and don't build with propasm, so I wrote a very simple one with more lines of comment than code. It generates a 40x16 B&W text display, uses the ROM font to save space (compressing it on the fly to 8x16 pixels), and requires 336 bytes of code space and 640 bytes of framebuffer. As an experiment, I've tied it into PropellerForth.



It probably won't be part of the base image I release -- it bloats the image beyond my 8KiB target -- but between it and my PS/2 driver from Christmas 2006, I've nearly got a standalone console working.

(NTSC, incidentally, is a pain compared to VGA -- but most of the complexity, as my dad pointed out, is a result of sending all the data amplitude-modulated on a single channel, instead of VGA's five. It's kind of impressive that they got this to work in the 30s.)

So, if anyone following my blog is interested in PropellerForth, let this at least indicate that your interest may not be in vain.

Labels: ,