Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make BabelFish work for Arduino Micro #36

Closed
kervinck opened this issue Jun 26, 2018 · 19 comments
Closed

Make BabelFish work for Arduino Micro #36

kervinck opened this issue Jun 26, 2018 · 19 comments

Comments

@kervinck
Copy link
Owner

kervinck commented Jun 26, 2018

It appears some Arduino versions implement in-band, software flow control (XOFF decimal 19 and XON decimal 17). This means you can't send arbitrary binary payload data over the line.

See also https://forum.gigatron.io/viewtopic.php?p=228#p228

One direction for a solution is to disable this feature. That sounds a lot better than going to a 7-bit encoding. [This is a bit ironic, because sendGt1.py only exists because flow control was missing on the Arduino Uno in the first place.]

Another idea is to send uncompiled .h files... They are text and not difficult to convert on the receiving end.

[Edit to future readers: After LOTS of debugging, the issue is NOT related at all to control bytes or XON/XOFF flow control, but concerns with the length of the data messages ]

@kervinck kervinck changed the title Make BabelFish work when XON/XOFF is present [Arduino Micro] Make BabelFish work when XON/XOFF is present? [Arduino Micro] Jun 26, 2018
@WattSekunde
Copy link

Gigatris uploads fine. I try to investigate on the software flow control side...

@kervinck
Copy link
Owner Author

kervinck commented Jun 26, 2018

Hmm, my version of Gigatris has 3 such bytes in it...

The recent TinyBASIC only mentions it once, to name the address $13a0 where programs begin in memory. $13 = 19. A simple and safe hack is to change it to $14a0 and verify if the file loads.

Edit: see attach such file:

TinyBASIC_14a0.gt1.zip

@WattSekunde
Copy link

Thanks! The 14a0 version works fine. But in both of my downloaded TinyBASIC versions is no $13? Here is my log output from sendGt1.py for all three versions of TinyBASIC and frogstroll. I'm not so sure if it's up to XON/XOFF. Maybe I have to investigate more on the arduino side of this problem.

TinyBASIC send.log.txt.zip

@kervinck
Copy link
Owner Author

kervinck commented Jun 29, 2018

The first failing BASIC says this:

.len=3
06:00:f5
Exception: Timeout error (no data)

That is the segment with a0:13 in it. It looks like your debugging gets swallowed somewhere.

Anyway, it is best to think of a solution that works the same on all platforms. From Googling I couldn't find a method yet to disable this behaviour. It is also not exactly clear who is responding to the XOFF byte: it could just as well be the serial driver on the PC-side, as far as I'm concerned, although not likely.

Just brainstorming: An ideal solution would 1. work the same on all platforms (meaning: no runtime or compile-time flags), and 2. stays compatible with what is out there already. I'm not sure if that is achievable. We can always introduce escape sequences, but then both sides (Arduino and sendGt1) require a simultaneous update. Well, sendGt1 can always warn when it is sending a $13.. That's a start

@kervinck
Copy link
Owner Author

I see in the log that frogstroll also hangs. [I was confused by an earlier remark on that.] Hanging is consistent with embedded XOFF bytes.

====
Sending program 'frogstroll_b.gt1'
[...snip...]
.len=3
09:00:00
Exception: Timeout error (no data)

I'm convinced by now. Have to ponder on a simple fix...

@kervinck
Copy link
Owner Author

kervinck commented Jun 29, 2018

It isn't entirely clear if the XOFF is stopping the flow on the receiving side (Arduino) or in the sending side (PC). PySerial's _ _ init _ _ documentation suggests that the port has flow control disabled by default. But who knows what the underlying platforms are really doing...

I would not want to meddle with OS and library specifics: lets just accept the fact that some byte values are forbidden from being transmitted in general. There are several ways then to go about it. We can do an encoding using only ASCII characters (base64 or simple hex dump come to mind). Base64 is a bit complex. Hexdump (base16) doubles the transfer size. That is not really bad, but also not nice. And it breaks all compatibility.

We can also introduce an escape character. For example the common convention of 0x7D ('}') as escape code and XOR'ing the next character with 0x20. This is used in PPP for example:
https://en.wikipedia.org/wiki/Escape_character#Communication_protocols

[Unfortunately there is no magic byte value that doesn't occur in any GT1 file published so far: every byte value 0..255 is out there in the wild already]

I slightly prefer the escape character over the ASCII encoding. It looks as if it can become a local change with some chance of smooth introduction. Full compatibility will still not be possible. But we could get close. At least we should aim that "new" sendGt1 scripts still work with "old" versions of Babelfish.ino.

To make the transition smooth, we can change the Arduino code to let it signal first that wants escaped data. If we do it right, we do it in such a way that old sendGt1 scripts will fail on the request. For example by sending a negative number as requested byte count.

@kervinck
Copy link
Owner Author

kervinck commented Jun 29, 2018

Lets see if e53ed17 solves it?

The idea is that using the newer sendGt1.py will at least warn for these bytes. It warns for XOFF, as XON doesn't seem to cause harm (which is strange in itself, but oh well...). If a file worked before, it will continue to work.

When BabelFish.ino is upgraded as well on the Arduino, the two will work together and escape the control characters. Then there will be no warnings displayed.

[ The old sendGt1.py will not work well with the new BabelFish.ino. It will likely give timeout errors for programs that have the 0x7E byte in them. I don't see that as a realistic configuration to worry about. ]

@WattSekunde
Copy link

Now with your modification to BabelFish and sendGt1...

Case a) Including $13# to force the problem but it transferred fine:
Connecting to /dev/tty.usbmodem1A13411
Resetting Gigatron
Starting Loader
Sending program 'myCalculator.gt1'
.................................
Warning: sending unescaped XOFF
........
Finished

Case b) No $13# but it fails.
Connecting to /dev/tty.usbmodem1A13411
Resetting Gigatron
Starting Loader
Sending program 'TinyBASIC_2.gt1'
.........Exception: Timeout error (no data)

I always have to manually reset the arduino micro after the failure. I tried USB2 and USB3 hubs and iMac direct USB3 port.

... maybe I have to add a display to the arduino for more in depth logging ;-) or change to another arduino I have laying around. But my time is somewhat limited these days.

@kervinck
Copy link
Owner Author

kervinck commented Jul 10, 2018

Bummer and completely unexpected. I think I'll order an Arduino Micro so I can also test. Although it can also be a Mac-side thing. Wait, that can't be the case if a Micro reset is required to restore state.

It is probably possible to craft a GT1 file that draws some recognisable progress to the screen, that could even be text.. And then send each byte, followed by a screen update. I'll see if I can make something like that, but it will be somewhere next week earliest.

kervinck added a commit that referenced this issue Jul 25, 2018
@kervinck
Copy link
Owner Author

I just created such file. See attached. Also in the repo as Utils/SerialTest.gt1, including the Python program to generate the file. The result after sending should look as the attached screenshot. If it stops earlier, the value of the last successful byte will be shown.

SerialTest.gt1.zip

serialtest

@WattSekunde
Copy link

WattSekunde commented Jul 26, 2018

Sorry for the delay. I am very busy at the moment. Family things ;).

But SerialTest.gt1 works fine on Arduino Micro from start to finish. I tried it several times. ;(

@kervinck
Copy link
Owner Author

kervinck commented Jul 26, 2018

Interesting! If you have a specific file that causes you problems, would you mind to attach it here in order to freeze a reference point? TinyBASIC.gt1 keeps changing and growing all the time, as most of my focus is there this summer... Incidentally, I'm about to pick up an Arduino Micro this afternoon. But I'm also preparing vacationing, so I'm not thinking about this issue every day :-)

@kervinck
Copy link
Owner Author

kervinck commented Jul 26, 2018

Utils/sendFile.py Apps/TinyBASIC.gt1 
Connecting to /dev/tty.usbmodem1411
Resetting Gigatron
Starting Loader
Sending program 'Apps/TinyBASIC.gt1'
...............................................
Finished

I sent over TinyBASIC a couple of times on my own Arduino Micro setup which looks like this:

img_4013

[Note that the red wire (Vcc) is coming out of my plug and is connected to the breadboard, but only so that I don't have it dangling. It isn't connected electrically.]

[Edit: forget all I wrote about periodic reliability problems (I already deleted that part of this comment). The wiring got messed up in the photoshoot. ]

@kervinck kervinck changed the title Make BabelFish work when XON/XOFF is present? [Arduino Micro] Make BabelFish work for Arduino Micro Jul 26, 2018
@kervinck
Copy link
Owner Author

kervinck commented Jul 26, 2018

Good news! I can reproduce the issue exactly as reported. I went back to the files posted on the forum, and found one in this posting. I attach it here, renamed with the same "_2" extension as reported above.

TinyBASIC_2.gt1.zip

Connecting to /dev/tty.usbmodem1411
Resetting Gigatron
Starting Loader
Sending program '/Users/marcelk/Desktop/TinyBASIC_2.gt1'
.........Exception: Timeout error (no data)

@kervinck
Copy link
Owner Author

kervinck commented Jul 26, 2018

> Utils/sendFile.py -v ~/Desktop/TinyBASIC_2.gt1 
Connecting to /dev/tty.usbmodem1411
.Resetting Gigatron
> R
< :Resetting Gigatron
..< :Gigatron OK
< Cmd?
Starting Loader
> L
< :Starting Loader from menu
< :Gigatron OK
< Cmd?
Sending program '/Users/marcelk/Desktop/TinyBASIC_2.gt1'
> U
< 03?
.< 250?
.< :Loading 250 bytes at $200
< 3?
.< 241?
.< :Loading 241 bytes at $300
< 3?
.< 239?
.< :Loading 239 bytes at $400
< 3?
.< 256?
.< :Loading 256 bytes at $500
< 3?
...< !Timeout error (no data)
Exception: Timeout error (no data)

The Arduino Micro hangs up after having processed a segment of 256 bytes. The message ":Loading 256 bytes at $500" means that 256 bytes were actually received from the host and are being passed on to the Gigatron. This succeeds as well, and the Arduino asks for the next segment header of three bytes. This request for 3 bytes is even answered by the host, but the Arduino doesn't see it. After a few seconds it gives a timeout. As if it stopped listening to the host (but sending to the host continues to work).

The interesting thing is that if I split, in Python, the writing of those 256 bytes in two calls (on the host side in sendFile.py), this hangup doesn't occur.

Somewhere in the pipeline there is a buffer that decides that it must stop working after having received 256 bytes at once.

This could be the Arduino Micro firmware causing this: the obvious suspect, because none of this happens with the Arduino Uno that does serial/USB in a completely different way with an extra chip.

However, sadly, the story is not complete: Terminal.gt1 also has (is) a 256-byte segment, and this one works:

> Utils/sendFile.py -v Apps/Terminal.gt1 
Connecting to /dev/tty.usbmodem1411
.Resetting Gigatron
> R
< :Resetting Gigatron
..< :Gigatron OK
< Cmd?
Starting Loader
> L
< :Starting Loader from menu
< :Gigatron OK
< Cmd?
Sending program 'Apps/Terminal.gt1'
> U
< 03?
.< 256?
.< :Loading 256 bytes at $600
< 3?
.< :Executing from $600
< :Gigatron OK
< Cmd?

Finished

[Edit: in hindsight, the 256-byte segments reported here were transmitted over USB as larger blocks, due to the insertion of escape codes]

@kervinck
Copy link
Owner Author

kervinck commented Jul 27, 2018

Some notes after trial and error:

  • Things are pointing to an interaction with the 64 byte ring buffer on the Arduino.
  • If I limit writes to chunks of 63 bytes or less, it always works.
  • If I limit writes to chunks larger than 64 and not a multiple of 64 bytes, it always works.
  • If I write chunks of 64, 128, 192 or 256 bytes we enter the hangup situation and only the first 64 bytes arrive
  • It doesn't help to put delays between the chunks.
  • It doesn't help to bypass the Gigatron side of the transfer. So the disabling of interrupts there has no influence.

@kervinck
Copy link
Owner Author

kervinck commented Jul 28, 2018

I found an issue in Arduino's USBCore.cpp library for USB handling on the ATmega32U4 processor. The Arduino code doesn't follow Atmel's datasheet with respect to clearing bits in the UEINTX register:

RXOUTI shall always be cleared before clearing FIFOCON.

USBCore.cpp clears them at the same time, and that doesn't always work. Applying a workaround for it in the sketch solves the problem.

Solutions in 94d167d and onwards.

I'm confident enough to remove the whole escaping mechanism again from BabelFish.ino and sendFile.py. This simplification is not backwards compatible. However, sendFile.py will give an error to notify that an upgrade is needed. Not nice, but I shouldn't have added the escaping mechanism in the first place as the problem lies elsewhere.

@kervinck
Copy link
Owner Author

kervinck commented Jul 28, 2018

About 256 byte remarks above: in many of the cases the actual transmission was larger due to inserted escape characters. I missed that. It looks quite reproducible: send data in a multiple of 64 bytes, and USBCore.cpp gets stuck.

There is a similar issue reported in the Arduino repo: https://github.com/arduino/Arduino/issues/6669 "Arduino Micro USB Serial cannot receive more than 255 data once" which points to arduino/Arduino#6886 "Handle receiving a ZLP in USB_Available"

However, the proposed solution doesn't solve the issue we're seeing here.

@kervinck
Copy link
Owner Author

Submitted https://github.com/arduino/Arduino/issues/7838 (Arduino Micro USB Serial cannot receive exactly 64 bytes at once) on the Arduino project

kervinck added a commit that referenced this issue Nov 30, 2019
The dissassembly file can show comments when they are injected with
the C() function. But this is a bit ugly: it is harder to read in
the .py source than regular comments, while regular annotations
don't show up in the .asm file. This leads to two views on the code.
That isn't helpful.

A much better solution is to produce an old-fashioned listing of
the source code, along with the assembly result on the left. This
is possible with the Python inspect module!

Change: Replace .asm output files with .lst files.

For dev.rom, we now have the Python source lines listed in dev.lst.
Almost all C(xxx) calls could be eliminated between enableListing()
and disableListing().

The justification for the explicit enable/disable (instead of "always
on") is that introspection is rather slow. >90% of the ROM file is
storage area generated from the same macro. With that, looking up
the line number for each word in the storage area, and then deciding
that it was already listed, is very slow..

(Note: This can be remedied with more refactoring that eliminates
the _linenos[] list. Maybe later...)

The transition is quite a bit of work, so for the older ROM files
we don't change the output (for now).

Snippet from dev.lst:
-----------------------------------------------------------------------------------
              0b2d fc32  bra  .sysSb#38   3062  bra('.sysSb#38')                #36
              0b2e de00  st   [y,x++]     3063  st([Y,Xpp])                     #37
                                          3064  label('.sysSb#35')
.sysSb#35:    0b2f 0200  nop              3065  wait(38-35)                     #35
              0b30 0200  nop
              0b31 0200  nop
                                          3066  label('.sysSb#38')
.sysSb#38:    0b32 0124  ld   [$24]       3067  ld([sysArgs+0])                 #38
              0b33 2001  anda $01         3068  anda(1)                         #39
-----------------------------------------------------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants