Tachyon V5.4 how to include own ROM
Jeff_Birt
Posts: 25
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:
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!!!
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
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.
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'.
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.
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
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:
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.
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:
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
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.
@"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.
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
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.
LOADCOG in EXTEND has this stack notation: 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: 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? 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:
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.
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.
Since the P2 is coming it seems I have to try FORTH, again.
Mike
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.
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).