Quantcast
Channel: JeeLabs
Viewing all articles
Browse latest Browse all 265

Think, upload, rinse, repeat

$
0
0

As often mentioned, Forth enables an interactive development approach: you enter commands on the µC itself, and things get done right away. No make, no compile step, no uploads.

But that’s an over-simplification, because it ignores the fact that most code needs to be written and saved to file. Unless truly one-off of course, and you don’t care about losing the source code once it has run. But usually, we really can’t develop a meaningful application on just the µC.

A more practical development foundation on the µC side is in fact set up like this:

  • load the 20 KB Mecrisp core onto the µC (just once, unless flash memory gets messed up)
  • add convenience code and drivers, also in flash, i.e. always / board / core
  • use cornerstones to manage flash memory as a stack of word sets
  • tweak core.fs when module dependencies change, and reload over the old one

The idea being that all of the above code is fairly stable and well-debugged, so that we don’t have to go back and change this part very often. These are the 500..700 “words” we build on.

Now comes the main part, which can be split into a number of phases:

  1. thinking about what our project needs to do, and how to gradually map it to code
  2. designing and building pieces which stand on their own, such as h/w drivers
  3. trying out snippets of logic, to see what works and figuring out those pesky little details
  4. putting it all together, i.e. working out potential the interactions and global state
  5. finalising the code, removing debug statements, and making it start on power-up

Each of these phases is different. Since Forth is a bottom-up system, you can’t try out (or even write) code before the primitive words it uses have been defined. What you could do, is insert dummy word definitions, as a way to ignore some details while working on the bigger picture:

: think ;
: edit ;
: debug true ;
: rinse ;

: app
  think
  begin
    edit
  debug while
    rinse
  repeat ;

app

The above is a valid Forth application, even if somewhat nonsensical. So one approach would be to put this code in a source file, say myapp.fs, and then use Folie to send it to an attached µC using “!s myapp.fs”. But that’s a top-down approach, even if the words are all empty stubs for now: it leads us to think from the top down and drives the development from that direction.

A more effective approach is to not write this code at all, but just keep it in mind as part of our thought process. Instead, we focus on the pieces that we know we’re going to need, and just start implementing these pieces one by one.

If you’ve set up your project as a sub-directory of the 1608-forth/ area in Embello (highly recommended), then one way to get going is to set up each new set of related words as an “exploration”: a source file in an ex/ sub-directory, i.e. 1608-forth/myapp/ex/think.fs.

Uploading to RAM is fastest, avoiding frequent flash erases, so a starting point for developing the code for say think is to create an ex/think.fs file with the following content:

\ this is the think code ...

compiletoram? [if]  forgetram  [then]
\ include ../../flib/other/module/you/might/need.fs

: think ." Thinking..." ;
think

The forgetram at the start is very convenient, because it lets us re-send the source file to replace the previous version. You can ignore the compileram? logic for now. As we start implementing the logic of think, we end up going through this process over and over again:

  • edit ex/think.fs on the host
  • save and switch to the terminal window running Folie
  • enter !s ex/think.fs (tab-completion means we could also type “!s ex/t<tab>”)
  • see what happened, and repeat this cycle by going back to editing

Sometimes (haha: if you’re optimistic …) things won’t work, and the µC may crash or hang. No big deal: hit ctrl-c, and you’re back at the prompt, with all RAM contents conveniently gone.

Occasionally, one of the lower-level words you added won’t be working quite right. In that case, you can comment out the think call at the end, and call the troublesome words interactively, while manually setting up the necessary arguments. This is where Forth’s interactivity shines: the ability to quickly try something out, completely ad-hoc.

You can still manually load a changed file, e.g.”!s ex/think.fs” - even it has already be added to core.fs and stored in flash. It will simply generate a few harmless Redefine warnings. Just keep in mind is that everything compiled in flash will continue to point to the original version!

If you’re into test-driven development (TDD): have a look at varint.fs and varint-test.fs as examples of how to write test code. By placing that code in a separate file, you’ll be able to run the tests at any time, with the benefit of TDD when it comes to future changes and re-factoring.

If you’re using the multi-tasker, something to watch out for is that forgetram may cause trouble, since your task might still be referenced in the task chain. A quick solution is to replace forgetram with reset, which causes a full re-init instead of just clearing the RAM definitions.

At some point, ex/think.fs will be deemed stable, and you’ll want to move on to the next set of words to implement for our application. You can now permanently add the think definition and everything written for it to flash, as follows:

  • make a copy of the core.fs file in your project area, i.e. myapp/core.fs
  • add the following line to it, somewhere before the <<<core>>> definition at the end:

    include ex/think.fs
    
  • enter <<<board>>> to remove the current core.fs definitions from flash

  • enter !s core.fs to add your updated version, including the new think definitions

From now on, the µC will have the new think implementation stored in flash, in the same way as all the other “standard” definitions. You’re ready to move to the next part of your project.

Note that the integration step comes last. Assuming that same example as before, at some point, you’ll want to make everything work together. That’s easy: create a file dev.fs with that definition of app given above. Since all the pieces it relies on have presumably already be implemented and stored in flash (via your custom core.fs), it should compile just fine.

To start up the completed app, enter the magic spell: “app + <enter>” … and if all is well, your application will start running. If not: go back to the previous mode, and test things in isolation.

Ok, let’s assume everything works as intended. Let’s make the app run on power-up. No serial port, no development, just running it. Create a file “freeze.fs” with the following contents:

\ install frozen myapp with auto-start on reset
include core.fs
compiletoflash
include dev.fs
: init init unattended app ;

Then type “!s freeze.fs” on the Folie command line. The following will happen:

  • include core.fs - wipes the old definitions and installs the latest version
  • compiletoflash - sets up to compile app itself to flash memory
  • include dev.fs - compiles that app main loop we just created
  • : init ... ; - gets redefined to call our application code on power-up

For details on unattended, see this article. Note that it won’t start up “app” on reset if you have a serial port connected. In that case, you can simply type “app” yourself to have the same effect.

PS. The above approach uses Folie as host front-end, but this will also work with picocom and other terminal emulators, if they can send throttled source code (ctrl-a + “s” in picocom).


Viewing all articles
Browse latest Browse all 265

Trending Articles