Shop OBEX P1 Docs P2 Docs Learn Events
"Inline" Asynchronous Serial Driver (Yet Another) [Library] — Parallax Forums

"Inline" Asynchronous Serial Driver (Yet Another) [Library]

DarkInsanePyroDarkInsanePyro Posts: 31
edited 2021-10-07 02:16 in Propeller 2

Figure I'd share my "inline" asynchronous serial library and other various libraries that I have been working on. This is mainly a 'reinvent the wheel' type of project, being my first project beyond blinking and LED so I can get a handle on Spin2 and PASM2 as I get familiar with the P2.

I've seen simpler, and more complex libraries that essentially do the same. I can't say that mine is unique in any way since I didn't dig too deep in the various libraries mostly spread through this forum. There are a few interesting features though which may give it a leg up in some situations.

This is the project that dug up the Spin2 documentation error which the thread is here.

Source

The source is located on Github here. The file in focus is piInlineSerial.spin2. The following is a hierarchal drawing of the library with the terminal library as the top object to give sense of organization:

graph

Serial Library Info:

Features

  1. Follows Single-Responsibility Principle
    • doesn't include string-formatting for example, that is located in a standalone library that can be utilized with any other output/stream library by utilizing send/recv hooks.
  2. "Zero cog" asynchronous tx/rx buffer handling
    • resides in the user-assembly section of the cog spared by the Spin2 interpreter
    • due to this, inline PASM is not allowed when utilizing this serial library
  3. Supports None, Even, and Odd Parity
  4. Supports 1 or 2 Stop Bits
  5. Parity, Framing, and Overflow Error Handling and Status Flags
  6. Read/Write Functions with Timeout Support (Immediate and Infinite)
  7. Full Density Transmit/Receive
    • no dead time between bytes with typical clock/baudrate settings (20MHz, 115k)
  8. Lightweight... It exists in the first $124 longs of the cog

Missing Functionality

  1. Flushing
  2. TXEN Functionality
    • I want to try to use the 3rd interrupt on a counter-passed event to time it accurately right after the last byte write since there is no tx shifter empty interrupt.

I decided not to support non 8-bit data... it would have complicated things too much.

Comments

  • Demos

    Demo_piInlineSerial

    Illustrates the error catching mechanisms that are part of the serial library.

    Demo Output

    Cog0  INIT $0000_0000 $0000_0000 load
    Cog0  INIT $0000_0D6C $0000_1C08 jump
    Cog0  testIndex = 0, framing = 0, parity = 0, overflow = 0, @recvBuffer = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
    Cog0  testIndex = 1, framing = 0, parity = 1, overflow = 0, @recvBuffer = "Lorem ipsum dolor sit amet, consectetur adipiscig elit"
    Cog0  testIndex = 2, framing = 1, parity = 0, overflow = 0, @recvBuffer = "Lorem ipsum olor sit amet, consectetur adipiscing elit"
    Cog0  testIndex = 3, framing = 0, parity = 0, overflow = 1, @recvBuffer = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
    Cog0  testIndex = 0, framing = 0, parity = 0, overflow = 0, @recvBuffer = "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
    Cog0  testIndex = 1, framing = 0, parity = 1, overflow = 0, @recvBuffer = "Lorem ipsum dolor sit amet, consecttur adipiscing elit"
    Cog0  testIndex = 2, framing = 1, parity = 0, overflow = 0, @recvBuffer = "Lorem ipsum dolor sit amet, consectetur adipiscin elit"
    Cog0  testIndex = 3, framing = 0, parity = 0, overflow = 1, @recvBuffer = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do "
    

    Demo_piTerminal

    Provides an example terminal-like interface with a few example commands such as stack, cogs, stop, and help.

    Demo Output

    $ help
    commands: help, cogs, stop, stack
    $ cogs
    Active Cog = 3
    0 1 2 3 4 5 6 7
    0 1 1 1 0 0 0 0
    $stack
    Terminal Stack Used = 520 bytes
    Blink Stack Used = 44 bytes
    $ stop 1
    stopping cog 1!
    $
    

    '>' replaced by '$' due to forum interpreting as tag

    Personal Observations

    Objectives

    1. Learn Spin2 and PASM2, low level P2 functionality / instructions / etc.
    2. Attempt to follow some programming paradigms I am familiar with
    3. Get familiar with Github (creator side, rather than consumer side)
    4. Build up a general library in preparation for a project (TBD, I want to do one large Spin2 project)

    Things Learned / Dislikes

    1. Transmit Interrupt Timing
      I found it difficult to perfect the transmit section of the code. There is a ton of edge cases. What if we try to write more data than the buffer allows? What if the baudrate sends data faster than we fill the buffer? What if we send slower?
      I had to do a weird critical section in write_asm where I would prevent interrupts for a few cycles to let the input buffers "catch up". I learned late in the game that perfect timing was happening where the interrupt was returning at the perfect moment that IN was being assert between the IN-check and triggering an interrupt.

    2. PropTool doesn't support folders for OBJ instances
      This breaks any clean way to use version control on libraries. Normally I would heavily use git submodules to separate my own library/application code and to use other user's code. Also if I want to update my libraries folder... that should be a git repo I could pull from. I hear symlinks might be able to help but not looking forward to it.

    3. One-to-One Interrupt Source and Interrupt Channel
      I've since gotten comfortable with the interrupt architecture but I had thought initially it was odd I couldn't assign multiple interrupt sources to a single interrupt, giving more flexibility. Oh! Reminds me that the docs have legacy info about getbrk which is why I had originally thought interrupts could have more than one source since there is a "what interrupt source is currently active" kind of query. That disappeared after some revision in the design.

    4. Can't use # reg address expressions in CON blocks, example:

    DAT
                  org
    buffer        res 10
    buffer_end
    
    CON
      BUFFER_SIZE     = #buffer_end - #buffer
    
    1. There is a few consistency issues throughout the libraries but I've been 'touching them up' for a few days now and am exhausted. Always that last 10%...
  • About #2: I use flexspin that allows for include directories. I wrote a small wrapper in Python that allows me to declare a project similar to e.g. Rust’s cargo, and this allows then for submodules, I like them to. You can see one example here https://github.com/deets/unifhy-rocket-engine-test-stand/tree/master/modul2/P2 - i used submodules originally, for my fellow collaborators who aren’t that git savvy I had to copy stuff over.

  • @deets said:
    About #2: I use flexspin that allows for include directories. I wrote a small wrapper in Python that allows me to declare a project similar to e.g. Rust’s cargo, and this allows then for submodules, I like them to. You can see one example here https://github.com/deets/unifhy-rocket-engine-test-stand/tree/master/modul2/P2 - i used submodules originally, for my fellow collaborators who aren’t that git savvy I had to copy stuff over.

    I've been planning on moving to alternative toolchains at some point... may need to do it sooner rather than later. The Python idea is very intriguing. I had to do something similar with a C++ environment to get intellisense working on vscode. Parsed the output of a makefile and modified the vscode's settings.json file to provide search directories, etc. since one library didn't work well with intellisense. Applying something similar here may be a clean workaround, or you know, move to a more different toolchain. :smile:

    I did a quick test. As expected, regexec/regcall has no place in compiled Spin2 so my serial library won't work. But it was easy enough to mix another Serial library with my formatting library, for example:

    OBJ
        piString:       "Libraries/piLibrary/piString"
        serial:         "Libraries/SimpleSerial"
    
    
    PUB Main() | Counter
        counter := 0
        send := @SendWrapper
        serial.start(115200)
    
        repeat
            piString.SendFormat_1(string("Counter = %d",piString.CR,piString.LF), counter++)
            waitms(500)
    
    
    PRI SendWrapper(ptr) | byte chr
        repeat
            chr := byte[ptr++]
            if chr == 0
                quit
            serial.tx(chr)
    

    As expected, larger binary. Dang, using 2% instead of 1% of memory available... haha.

Sign In or Register to comment.