Shop OBEX P1 Docs P2 Docs Learn Events
Tachyon V5.4 how to include own ROM — Parallax Forums

Tachyon V5.4 how to include own ROM

Jeff_BirtJeff_Birt Posts: 25
edited 2018-11-15 18:35 in Propeller 1
I have been searching and reading and going through Peter's drop box files until I'm bleary eyed but let me back up and start with what I'm trying to accomplish.

I know a bit about Forth, I can write simple programs and such and using DurexForth for the C64 have used the built in compiler to make my own words in 6502 assembly where needed. I recently got a Prop1 and went through the SimpleIDE tutorials, I know C fairly well so this was not a big deal and I learned a bit about the Prop in the process. Then I found out about Tachyon and it was no hard (but not simple) to find out what the latest version was and find the binary and load that onto my Quickstart board. So far so good.

So now I am wondering what would I like to do with Forth on this board? Well, using the Touch Buttons seemed like a logical place to start. I found the touch button demo code and worked my way through what the assembly code is doing (the Prop assembly is not too hard to follow I think). Then I went to looking at all the ways ports, pins and DDRs are controlled in Tachyon V5.4. I found the glossary of words which is out of date but still somewhat helpful, then I found the source for V3.or so and then I finally found the V5 dropbox folder. OK, so now I can find the relevant words.

For the touch buttons the basic idea is that you have those pins DDR set as inputs but have the outputs registers set to high. Then you set the DDR to make them outputs and then immediately set them as inputs again, pause and then read the inputs. So, after finding out what words like MASK, OUTPUTS, INPUTS, and OUTSET so I was able to set the output registers to high, and then set the DDR to make them inputs put of course trying to toggle the DDR input to output to input quickly enough is not feasible in Forth directly.

So, on to my attempt to figure out how to include the touch buttons 'driver'. After realizing that this involves saving the PASM code as a 'ROM' I set out trying to figure out how to do that. Well, after much searching, of the forum and reading about 50% of the Tachyon V5 thread, I found the readme.txt in the ROMS folder of the dropbox. OK, so this provides the method and Python code to convert a .spin file into PASM. So, I think I am close, finally.

So, I have the original touch button driver code modified with the ROM header thusly:
CON

  BUTTON_PINS   = $FF           ' The QuickStart's touch buttons are on the eight LSBs
  SAMPLES       = 32            ' Require 32 high redings to return true


VAR
  
  long  Results


PUB Start(Rate)

  Results := Rate
  cognew(@Entry, @Results)      ' Launch a new cog to read samples


PUB State | Accumulator

  Accumulator := Results        ' Sample multiple times and return true
  repeat constant(SAMPLES - 1)  '  if every sample was highw
    Accumulator &= Results
  return Accumulator


DAT

                        org
              byte ".ROM"                       ' ROM signature
              word @TOUCHBTNEnd-@TOUCHBTN           ' size
              byte "TOUCHBTN  "                 ' name
              '     1234567890
Entry
TOUCHBTN                                                                                                                                    
              rdlong    WaitTime, par
              mov       outa, #BUTTON_PINS              ' set TestPins high, but keep as inputs

              mov       Wait, cnt                       ' preset the counter
              add       Wait, WaitTime
Loop
              or        dira, #BUTTON_PINS              ' set TestPins as outputs (high)
              andn      dira, #BUTTON_PINS              ' set TestPins as inputs (floating)
              mov       Reading, #BUTTON_PINS           ' create a mask of applicable pins
              waitcnt   Wait, WaitTime                  ' wait for the voltage to decay
              andn      Reading, ina                    ' clear decayed pins from the mask
              wrlong    Reading, par                    ' write the result to RAM
              jmp       #Loop
TOUCHBTN
Reading       res       1
WaitTime      res       1
Wait          res       1

So, assuming this is correct I build in with PropellerIDE and get a binary file and get an error. I finally figure out that the UARTEND and UART in the readme are referring to labels in the text! So, I look at UART.spin code form the V5 dropbox and see where the labels are supposed to be. This part is simple now that I understand it but as a noob I did not understand what the @lable was doing. So, now I am able to build the TOUCHBTN.binary!!!

Comments

  • Jeff_BirtJeff_Birt Posts: 25
    edited 2018-11-15 18:35
    Part the second...

    Now I have a binary that I 'think' should work. I run bh.py one it and get a hex file out. I open TOUCHBTN in VScode and add the 'SAVEROM' to the top. This is what I have now for a hex file.
    SAVEROM
    :20000000001BB700003B100070007C00580084006000030048000000500004002E524F4DE0
    :200020002C00544F55434842544E2020F021BC08FFE8FFA0F123BCA01022BC80FFECFF6862
    :20004000FFECFF64FF1EFCA01022BCF8F21FBC64F01F3C0808007C5C644134871C432C3232
    :10006000406537240805406648097B643332000048
    :00000001FF
    
  • Jeff_BirtJeff_Birt Posts: 25
    edited 2018-11-15 18:36
    Part the third...

    So, now I have a properly modified hex file I 'think' now how to load it???

    I take it I use something like:

    [code]
    " TOUCHBTN " LOADCOG
    [/quote]

    Which I'm guessing just takes the code from EEPROM and loads it into a free COG? But, how to get the code to the prop? Well, I copy the text from my hex file and paste it into the terminal window (using putty) and it says it is saving to EEPROM. This looks promising but I see things like:

    [code]
    FAIL @A800\ to EEPROM |
    [/quote]

    and, it is taking forever to do 'something'.
  • Part the Forth :) ...

    Remembering some pots I had read about line delay settings when copying files to Tachyon like this I downloaded TerraTerm and set the line delay to 15ms/line. I copied/pasted my hex file again and now I do not get an error and it was very fast! I reset Tachyon with ^C and see TOUCHBIN listed under ROMS!! Hurray!!

    Now, how to actually use it? I type in LOADCOG " TOUCHBTN " and get an OK in response. That looks good but how to 'call' it? This is the point I am at now and not sure how to proceed.

    Any pointers appreciated.

  • First of all 'welcome to the forum'.

    You did all of this in the last 3 hours? Impressive. Sadly I can't offer you help since I still can not wrap my head around FORTH.

    But I follow and read Tachyon threads since years and to me it looks as if you are almost there.

    Your assembler code expects the address of a long variable in HUB memory.

    The spin code does create this variable and delivers the address of it at start of the COG.

    Initial the Result variable contains a refresh-rate to use (the time to wait between two loops).
    After starting the COG it read the refresh rate and starts reading the buttons and delivering the result of its reading to the same HUB memory address.

    Results := Rate
    cognew(@Entry, @Results) ' Launch a new cog to read samples

    @Results is the address of a long aligned memory location in HUB and gets delivered by cognew into the register PAR in your pasm code.

    This is the standard way to communicate with PASM on the P1, you use a mailbox. Sometimes just one long, sometimes blocks of longs and at start of the cog you give it the address of the first memory location.

    so how to do this in Tachyon? I am not sure but I have a good guess.

    LOADCOG needs parameters on the stack. My guess it needs a cognumber, a value for par and the address of the code to load.

    As usual in FORTH parameters first. If you search the source for LOADCOG you might see the stack use as comment.

    but it might look like

    " TOUCHBTN " 1234 3 LOADCOG

    to load touchbtn with par 1234 in cog 3

    The main Tacyon guy is @"Peter Jakacki" and he lives down under so expect him to be awake when you sleep, but he will for sure chime in here to help you.

    Enjoy!

    Mike
  • Jeff_BirtJeff_Birt Posts: 25
    edited 2018-11-15 18:35
    Thanks Mike. I have more than three hours in it for sure. When I started to write Part 1 this morning I had most of what I was writing about figured out already. I did the rest when I had a few minutes between my 'real' work.

    Thanks for the tip on how a cog is normally started. I looked back at how the sample spin code called the 'driver' again and see that are passing 80_000_000 / 100 which I'm guessing Tachyon will like written out as 80000 (which seem to work if I just push it on the stack). Looking at some other posts I gather that I need to enter it as " TOUCHBTN " 3 80000 LOADCOG (given that forth pulls things off the stack LIFO). This indeed does 'something' as shown below:
    " TOUCHBTN  "  3 80000 LOADCOG TOUCHBTN C020 002C
    7F80 3880 3  ok
    

    I'm guessing the above is the same as: Buttons.start(_CLKFREQ / 100) in spin.

    What I can't seem to get a handle on is how to call 'member functions'. For example in the demo code this: Buttons.State is used to check the buttons' state. Looking at other ROM files spin/pasm source I see the same type of structure but can't seem to find any usage examples in order to glean the proper syntax.
  • Hi Jeff, sounds like you are going swimmingly then :) Now we look at how we can float as well!

    Although I could go into the in's and out's of Tachyon ROMs, in this instance I am pretty sure you can do the same thing in a single line of Tachyon. ROMs are useful when we need a cog loaded with a fast persistent repetitive task such as high speed UARTs and VGA etc. But a lot of the Spin demos had no other way of running a small bit of code that had to be fast, even if it was a once off, they had to load a cog with PASM as Spin is too slow for a lot of these tasks.

    Now if I were to do this in Tachyon I would look at the basic principle of detecting touch first, which is essentially charge the capacitance of some pins and their pads momentarily, float the pins, and take a reading using the pin mask. That right? Okay, this is what I would start with in Tachyon and then work from there:
    : TOUCH ( pins -- result )    H F DUP DUP IN ANDN ;
    

    H reads a mask and sets those pins High and since it does not drop the stack it is fast and permits other words to use this mask too.
    F works the same but Floats the pins
    Now IN reads the inputs, applies the mask and replaces the mask which is a fast operation. If you want to keep the mask then DUP before it.
    The extra DUP is so that we can ANDN the mask and the AND result. I haven't tested this but you can use this as a basis to build on. I don't think you even need to run it in a cog, just call it as needed, even as a KEYPOLL event in the background while the Tachyon console is waiting for terminal input.

    I didn't put a WAITX in there after the Float but you can play with it as I can see that you are well capable of experimenting and having fun :)
  • Thanks Peter that looks like a good method to explore. Late today I found a different example from a file called something like 'quickstart header'. It did not work, it had some odd words that I'm guessing we're from a previous Tachyon version.

    I have not explored the timer module yet but it seems it might work for a periodic task like pulling the buttons using something like your suggestion? Duh, I just re-read what you wrote an see you mention KEYPOLL...

    Since I am sooooo close to having ROM working could you humor me and tell me what I'm missing. I'm at least 50% of the way to a tutorial with this post so I might as well finish it. :)
  • ErNaErNa Posts: 1,738
    Hello Jeff, I made some experiments with quick start touch button some time ago and found, that the behavior is not as expected, means, it's not the resistance of your finger tip that pulls down the input. What you see is a signal, that strays in from the mains and that shifts the level, so the input starts to toggle. Even when I tried to monitor the pin with a scope probe, it didn't work anymore. I did some tricks to measure what happens, but forgot about it;-) So if you try to find out, how to use the touch pins and how to do it in Tachyon, thats the hard way. I propose to attach some switches to have a real contact and to focus on Tachyon. I myself am just a little distracted, wanted to use Tachyon, but run into another obstacle and first have to fix it, before coming back. This will all change with P2.
  • Jeff_BirtJeff_Birt Posts: 25
    edited 2018-11-15 14:50
    @"ErNa" The Quickstart board comes with the demo code in EEPROM that demonstrates the buttons. It works quite well.

    @"Peter Jakacki" I did find a way to get this to work in Forth! Thanks so much for your push in the right direction. Since I want to test the whole set of 8 buttons at once I cannot use IN but I did use P@. I have not fine tuned the number of loops required to properly integrate the result (the PASM code uses 32 successive reads.) I pulled a delay time out of the air and used 32 loops as well. The code is below.

    I still would like to know how to get my ROM working...I am soooo close. I like doing it in Forth more, it does not eat up a whole cog that way, but I do want to know how to use the ROM code I created.
    : TOUCH ( pins -- result)
        DUP #32 FOR
         H F DUP 10 ms
         P@ ANDN ROT AND SWAP
        NEXT
        DROP
    ;
    
    : TEST ( -- )
        $FF TOUCH .
    ;
    

  • Jeff_Birt wrote: »
    Thanks Mike. I have more than three hours in it for sure. When I started to write Part 1 this morning I had most of what I was writing about figured out already. I did the rest when I had a few minutes between my 'real' work.

    Thanks for the tip on how a cog is normally started. I looked back at how the sample spin code called the 'driver' again and see that are passing 80_000_000 / 100 which I'm guessing Tachyon will like written out as 80000 (which seem to work if I just push it on the stack). Looking at some other posts I gather that I need to enter it as " TOUCHBTN " 3 80000 LOADCOG (given that forth pulls things off the stack LIFO). This indeed does 'something' as shown below:
    " TOUCHBTN " 3 80000 LOADCOG TOUCHBTN C020 002C
    7F80 3880 3 ok

    I'm guessing the above is the same as: Buttons.start(_CLKFREQ / 100) in spin.

    What I can't seem to get a handle on is how to call 'member functions'. For example in the demo code this: Buttons.State is used to check the buttons' state. Looking at other ROM files spin/pasm source I see the same type of structure but can't seem to find any usage examples in order to glean the proper syntax.

    Almost. To me it looks like you started the cog successful

    the PASM expects a memory address containing the initial value and returns continuously the result at that memory address.

    so your 80000 is wrong. The PASM COG now reads the content of the HUB memory at address 80000 (way after the available 32K) to read its wait-interval and tries to write the result there.

    I have no clue as how to read or write to a hub ram address in TACHYON, and what addresses are available and not used by TACHYON but maybe you can figure this out.

    In SPIN the program gets loaded at the bottom (lower addresses) of the HUB ram, followed by the stack, so the higher addresses are free.

    I don't know the memory layout of TACHION, so I don't know if the top memory addresses are used or not.

    What you need to do is to reserve a long in your code and get its address. Then you write 80000 into the long at that memory address and use that address instead of the 80000 in your code to start the COG.

    Now the PASM COG writes continuously its result there and you can simply read that location to get the value.

    Enjoy!

    Mike
  • Jeff_BirtJeff_Birt Posts: 25
    edited 2018-11-15 18:34
    Below is the 'driver' code. It creates a long called 'results'. The 'start' function takes the timing interval (which is the 80000) and it does a cognew. I'm guessing I need to call this 'Start' function?

    The 'State | Accumulator' function returns a long I guess. I'm just not sure how you pass it the address of a memory location to use for the long.
    VAR
      
      long  Results
    
    
    PUB Start(Rate)
    
      Results := Rate
      cognew(@Entry, @Results)      ' Launch a new cog to read samples
    
    
    PUB State | Accumulator
    
      Accumulator := Results        ' Sample multiple times and return true
      repeat constant(SAMPLES - 1)  '  if every sample was highw
        Accumulator &= Results
      return Accumulator
    
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2018-11-16 07:42
    @Jeff_Birt - Good onya for getting it to work the simpler way! Now that you've put the effort in and learnt so much in such a short time here is some more hopefully useful information.

    LOADCOG in EXTEND has this stack notation:
    pub LOADCOG ( name cog par  -- )
    
    So when you enter " TOUCHBTN " 3 80000 LOADCOG it is mostly correct except that the par parameter that you are passing should really point to a structure in hub RAM where you would store the result and perhaps also have a sample time that can be read by the cog. As it is the value of 80,000 that you intend to load into Waittime:
    rdlong    WaitTime, par
    
    is totally incorrect. In fact par is only 14-bit and is meant to be a pointer to long aligned hub memory and so it is trying to read from location 80,000 somewhere in ROM!

    So it ends up too that the code doesn't know where to write the result in hub memory. In fact what do you think this part of the code was meant to do, but what it is doing instead?
    wrlong    Reading, par
    
    Ouch! That's right. Here par should point to your variable and there is a trick with the way that the result variable is also initially being preset to 80,000 so that the first thing the cog does is use whatever value has been stored there for waittime, and then uses the location to store the result.

    So it ends up that this is all you needed to do:
    long touches --- hold result of touch cog accumulator
    80,000 touches !
    " TOUCHBTN  "  3 touches LOADCOG
    
  • Jeff_BirtJeff_Birt Posts: 25
    edited 2018-11-16 13:30
    Thanks Peter. I'm getting closer to getting the full picture but I still have a few stuck pixels :)

    I suspect a large part of the problem is that I don't understand the proper structure of a program meant to be loaded by LOADCOG. I read through all 16 pages of the V5 thread again last evening (only a few posts actually touch on LAODCOG) and when I realized the V4 thread was more than 100 pages I gave up on that one (the forum search tool does not let one search within a single thread as far as I can tell.)

    So, the touch button driver has to 'PUB's' which are called from the user code to 'Start' the PASM code running in the COG and 'State' where the users code calls into to check the state of the buttons. Once 'Started' the PASM code just continuously reads the buttons and updates a local variable and the 'State' method accumulates 32 readings and returns that value. These are both in Spin and now looking at example ROMs from your dropbox I see this type of convention is not used at all. Adding that discovery to what you are saying above and I think the point @'msrobots' was trying to make yesterday (and that I was not understanding) is that Tachyon 'communicates' to the ROM only through the data structure passed in the par pointer. Looking at the UART ROM spin file I think this is the case.

    To make this work as a Tachyon ROM I would need to rework the touch button driver PASM to work under this model. Having it both scan and integrate the readings and then update the variable passed by var. The Tachyon code would then just need to read the variable to get the latest accumulated/integrated result.

    OK, I 'think' I am beginning to see the light. I'll play with this more this morning and see what happens.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2018-11-16 15:51
    Tachyon isn't doing anything different with a cog that runs PASM as does Spin or C. There is no mechanism for passing data from one cog to another except through the hub acting as a mailbox.

    LOADCOG is essentially a COGINIT/COGNEW that you see in a Spin program where the PASM object is compiled into main memory from where it is loaded into a cog. Unfortunately in Spin the memory is not reclaimable for code although I have had the object reused the binary hub image for buffers. Now this is where Tachyon is different in that the image is available at runtime and does not take up any hub memory but sits in upper EEPROM ready to be summoned by name and LOADed into a COG. That's what LOADCOG does.

    For sure I would have the cog accumulate an average in an internal register (cog memory) and then write the average to the result in hub memory so you always get a clean reading. But instead of the ROM you can run that tiny bit of Tachyon code instead or turn it into a task that runs in its own cog if you like. Mind you besides the dedicated cog method you have a keypoll method that is essentially free or you can create a TIMER with an ALARM that points to the code and have it run up to 1,000 times a second without using another cog. The main restriction with an ALARM function is that you avoid delays and instead rely upon setting the TIMEOUT and make sure that each time it never takes more than 1ms so as not to upset the TIMER cart.

  • Ha, I was pretty close.

    Since the P2 is coming it seems I have to try FORTH, again.

    Mike
  • OK, after a lot of trial and error I first got my ROM to update the variable that was passed with LOADCOG. Then, after correcting a few errors in my updated ROM code. Some silly things like forgetting the immediate '#' before a constant label and logical things like wrapping my head around how the conditionals work is PASM (I think I like the flexibility but it is different so it will take a while to understand how to use properly), anyhow after plugging away at it I can load the ROM and check my variable 'touches' and see the correct results.

    I will put together a simple tutorial on this even though using a ROM for this is silly, it is a fairly simple example. To use the touch buttons from Tachyon though I'll use my Forth code with a KEYPOLL or TIMER as soon as I figure out how to use though.
  • Hi Jeff,

    Good on you for sticking with it. I'm using Tachyon 5.4 on a project and I think it's the bee's knees - everyone writing code for a P1 needs to be using it. I also went through a similar process to you to get to grips with custom ROMS, as I had some PI control stuff that even Tachyon wasn't quite fast enough for.

    Peter, would you consider v5.4 as 'frozen for production', and the last version for the P1? If so, and it's no longer a moving target, maybe those of us out there using it could set about updating the documentation (I know you're busy with the P2, and rightly so).
Sign In or Register to comment.