TG9541 / stm8ef

STM8 eForth - a user friendly Forth for simple µCs with docs
https://github.com/TG9541/stm8ef/wiki
Other
314 stars 66 forks source link

Save Source Code in Flash. #295

Open sbridger opened 4 years ago

sbridger commented 4 years ago

To me a major advantage of 4th is being able to work on it into the indefinite future, but without saving the source on the target this advantage often disappears.

I did some experiments compressing small 4th files with various naked compressor utilities. I found that 7zip was always as good as the best, except for the small overhead of ~50bytes. Since it can bundle multiple files i.e the whole project including libs and alias/header files, that's a small drawback

sbridger commented 4 years ago

Interesting, that's what I had in mind :-) A compression algorithm could easily be implemented in Forth (e.g. one with a fixed dictionary) but is it really necessary? Most programs should fit in 96K without any compression.

You really only need the ability to store it i.e. save a binary blob. 7zip encode/decode can be used on the host editor machine or via its IDE. I am not so much thinking of 96k parts as C4's with 32k actual flash, and still have heaps of free space for 2k of zipped source. Even the tiny parts would have space for a small program without libs.

You raise a good point - in many cases uncompressed would be fine. I wonder if 7zip has compress=0 option?

The reason I see a bundled and compressed file like 7z as the way to go, is that these days most programs end up using library code and/or header/alias files, that would need to be bundled in for it to be fully editable (well to be able to copy the running program into new chip)

How about a screen/line editor to go fully close the gap to self-contained embedded control programming?

I have seen that as too much work, too little gain, and too hard when you consider how to handle included libraries etc. The other thing is that I see this as a pan-forth issue, i.e. it is equally desirable for (e.g) FlashForth now that a 32k pic is 10c more expensive that a 16k part. The less effort at the individual forth, and the more at the IDE end, the better.

So a large part of this is actually for Manfred and Eth4com....

TG9541 commented 4 years ago

I've been thinking about the magic that embedded source can do: reconfigurable systems that can "produce" binary variants as needed. I've seen systems that would have benefited from that, e.g. agricultural machines that change their behavior depending on "implements" (-> ISO11783). Compiling code to RAM based on variant parameters would be quite nice.

Using the embedded device as the source code storage is also a good idea, but both use-cases can be implemented on top of a similar text storage feature.

sbridger commented 4 years ago

In one of my products I just redirect the interpreter to fetch from eeprom, until it hits a return command to go back to uart, it made macro function very simple. Also did the same from i2c-eeprom attached to sensors. Another was a wireless sensor that sent the base station (when initially paired) the RPN commands that convert the sensors raw binary to engineering units.

sbridger commented 4 years ago

Yes 7Zip does support uncompressed: https://superuser.com/questions/336243/how-to-do-files-to-store-without-compression-in-7-zip

VK6TT commented 4 years ago

When I last tried to redirect the interpreter in AMForth I couldn't get it to work. I'd welcome seeing your example.

TG9541 commented 4 years ago

@sbridger how do you feed in the text stream from the EEPROM? How do you deal with errors?

sbridger commented 4 years ago

@VK6TT Not amsforth or e4th.

It was trivial I just had two main interpreter loops and jump from the getchar() loop to ReadNextEEPromChar() loop.

But even easier if you have vectors (which I didn't as a: PIC, b: optimised for absolute maximum speed i.e. avoiding call/returns], just change the GetNextChar vector

I think having fifo buffered uart avoided some possible issues with jumping partway through something or losing uart chars when using eeprom.

Switching is done by the gotomacro[EEAddress] command and the returnfrommacro command. I also had a return_when_uart_has_char command in the macros, so you can break out of looping eeprom macros only when a uart command comes in. You can't let the uart force a break out of eeprom, or you will be partly through a macro.

I didn't deal with errors in eeprom. If you put bad commands in eeprom or jump to data, you are screwed. I did have a magic boot pin combo to bypass startup eeprom macro so you can't brick it with a bad macro.

In the radio system, sensors are paired once by power on , at the base station, while pair button on the base is pressed, and radio channel used FEC. So no actual issue with errors. They are not turned on remotely and expecting to automatically discover and pair. That is really not a useful idea anyway. Before wasting time taking a sensor out (and they could be 15km away) you definitely want to pair them at the base and make sure they actually work!

TG9541 commented 4 years ago

I think having fifo buffered uart avoided some possible issues with jumping partway through something or losing uart chars when using eeprom.

Without a buffered RX there is a hard limitation on the time one interpreter cycle may take, namely one char length, roughly 1ms at 9600 baud. In the interactive use case the process is self-paced and users don't even notice this limitation since waiting for OK quickly becomes natural.

Making a second interpreter loop isn't that hard (I did that for interpreting the MODBUS protocol). There, the UART for MODBUS is buffered, which, by the way, wouldn't solve the problem for the console were the buffering not bi-directional.

I think that a small receive buffer would be rather affordable. The intricacies of UART interrupt handling I've already figured out but that's complicate for TX only.

Edit: INTRX in 2.2.25 provides buffered RX.

TG9541 commented 4 years ago

I just tested the following code:

\ store char c to far address ah:al
: FC! ( c al ah -- ) [
  \ Note: creates/executes LDF A,ah:al + RET code on the Data Stack
  $A6BD ,          \        LD      A,#0xBD         ; Opcode LDF ExtAddr,A
  $F7  C,          \        LD      (X),A           ; MSB of ah (instead of "0")
  $A681 ,          \        LD      A,#0x81         ; Opcode RET
  $E704 ,          \        LD      (4,X),A         ; MSB of C (instead of "0")
  $E605 ,          \        LD      A,(5,X)         ; load C
  $FD  C,          \        CALL    (X)             ; execute LDF  
  $1C  C, $0006 ,  \        ADDW    X,#6            ; DROP all
] ;

\ read char c at far address ah:al
: FC@ ( al ah -- c )
  \ Note: creates/executes LDF ah:al,A + RET code on the Data Stack
  $8100 ROT ROT [  \ get a RET ready
  $A6BC ,          \        LD      A,#0xBC         ; Opcode LDF A,ExtAddr
  $F7  C,          \        LD      (X),A           ; MSB of ah (instead of "0")
  $FD  C,          \        CALL    (X)             ; execute LDF  
  $6F04 ,          \        CLR     (4,X)           ; 
  $E705 ,          \        LD      (5,X),A         ; bring c onto stack
  ]  2DROP         \ remove LDF ah:al
;

This is only the first step because byte write is too slow (it stalls the CPU). I'll now work on a block write feature.

sbridger commented 4 years ago

Does it stall the cpu when executing from ram as well as flash?

TG9541 commented 4 years ago

No, it shouldn't. I guess that I'll find some time working on an implementation (which will have to disable interrupts while programming a block, of course).