Assembly - Main RAM access
in Propeller 1
I'm fairly new to this. I'm sure it's been answered somewhere, but I can't find it.
My ultimate goal is to sample audio input and store it in Main RAM so it can be later used to send to a speaker, saved to SDcard, etc.
I understand how to pass an address to the cog and write a single value back to it. However, what I'm needing is an array of hundreds or thousands of samples. How to I set up an an array in Spin that I can then send the address of to the assembly cog to do this sampling, and how to I address and write the sampled values back to main RAM?
My ultimate goal is to sample audio input and store it in Main RAM so it can be later used to send to a speaker, saved to SDcard, etc.
I understand how to pass an address to the cog and write a single value back to it. However, what I'm needing is an array of hundreds or thousands of samples. How to I set up an an array in Spin that I can then send the address of to the assembly cog to do this sampling, and how to I address and write the sampled values back to main RAM?

Comments
The cogs only have 2KBytes (512 longs) each, of which only 16 longs are in use. Hub ram is 32KBytes so you could store up to 16,384 16 bit samples which is at most only a second or two of audio. To store any more samples would require external ram.
You would have the address in one of the cog registers (lets call it "address") and add 1, 2, or 4 to the address depending on whether you are writing a byte, word, or long to the address.
For example 8 bit values would be:
How many bits of resolution are you storing?
Declare an array in hub ram and pass the address when you start the cog. WRBYTE, WRWORD AND WRLONG will write bytes, words and longs to hub RAM. RDBYTE, RDWORD and RDLONG will read them. I believe that deSilva said that he thought, to his mind, the source and destination fields were reversed. I've gotten it to work properly after playing with it for a while. At least you know it's possible now.
Sandy
Here are the needed snippets for a word array and assembler writes to it:
VAR word buffer[4096] 'an array with 4096 x 16 bits PUB Main cognew(@AsmCode, @buffer) 'pass first address of buffer to par ... DAT AsmCode mov ptr,par mov n,#4096 loop ... wrword value,ptr add ptr,#2 ... djnz n,#loop ...AndySuppose I have one cog that is constantly checking the status of something (button press, etc.) and sending that value back to main ram. Now I have a second cog that I want to take samples of that value that the first cog sent back.
I would THINK it would work just like in Ariba's example, but when I start the second cog, I also send it @buffer in the parameter.
VAR word buffer[4096] 'an array with 4096 x 16 bits word store PUB Main par1[0] := store par1[1] := buffer cognew(@AsmCode, @store) 'pass store for @AsmCode to write back to cognew(@sampler, @par1) 'pass store (to read from) and buffer (to write samples back to)But something isn't working. I've verified that the first cog is writing back to store correctly. And I've verified that the second cog can write SOMETHING back to buffer correctly. However, I'm unable to read store in @sampler. I'm parsing PAR correctly, so that's not the problem. I know the whole point of putting stuff back into main ram is to be able to share it between cogs but I am not doing that successfully.
Thanks for your help so far!
There are times when your example wouldn't work correctly. You need to make sure the address passed with par is long aligned.
Depending on what other variables are defined in the VAR section, the buffer array may or may not be long aligned.
There are several ways to insure a word (or byte) array is long aligned. Here are couple of methods I use. Both of these methods define the array in a DAT section.
buffer long ' don't assign any value to the long word 0[4096]Make sure you use another org to start your PASM code so the addressing is done correctly.
There's also a problem with this bit of code:
The value 4096 will need to be assigned to a variable accessed indirectly.
The source field in a PASM statement is limited to 9-bits.
This last error would have been caught by the compiler.
So are there any little simple rules that I should know that might be throwing things off? For instance, do I need to do an ORG before DAT sections that will be done by separate cogs? Do my data and reserveds for the different PASM "modules" need to be "with" their module, or all at the bottom of the DAT? I found that if I had one data item at the end (before the RES still) that it did weird things, but when it was just under the section of code for that cog, it worked fine. Should I just throw all my code up here so you can see what I'm doing wrong?
From what I see, the above code will set _Parameter[3] to zero.
I haven't followed the code closely, but my guess is you intended to pass the address of "mem" rather than the value of the first element.
Should the line be the following?
Sorry for the bad suggestion.
I'm still not clear what this line of code is supposed to do.
So others know what we're discussing, here's were the arrays and variables are defined.
Below is the code where the array is filled.
The Parameter array only has three elements so the element "_Parameter[3]" is out of bounds.
"_Parameter[3]" is another name for "mem[0]".
As the arrays are defined, the line "_Parameter[3] := mem" doesn't serve any useful purpose.
Is the array "_Parameter" supposed to have four elements instead of three?
I'm guessing there's a mistake in the way the "_Parameter" array is used. Hopefully this is a hint towards finding your bug.
That didn't fix my problem, though. Still trying to sample a value and store it to mem.
The first cog is there just to generate input. Imagine its like an ADC and it stores its current value in "buttons_value." The second cog "Record" is trying to read the value of "buttons_value" at a regular interval and write the value to a memory array.
The problem seems to be that Record is unable to "see" the current value of "buttons_value" and I'm not sure why. I pass the address of "buttons_value" in _Parameter, so I would think I could just rdlong at that address and get the value. It just doesn't seem to be happening.
I'm still wondering if you're missing an "@" symbol somewhere.
Do you know you can "cheat" and poke the values of address into PASM variables instead of using par?
If you change:
to
You can set the value of "button_state_address" before you launch the cog.
I'm not sure what you're trying to do, so I'm not sure where you're missing an address symbol but I think you are missing at least one.
If you attach you're latest code, I'll look at again.
I wrote this from scratch, based on what I think you want. It launches a "sampler" cog that will scan the QS pads at a rate that you specify (between 50 and 1000ms). When all the samples are taken, the sampler cog alerts the foreground and shuts itself down. From that point, the demo program plays the samples back at the same rate.
The program is fully functional. You may find it educational.
Things to remember:
-- keep your formatting neat & tidy, using a consistent style (mine mimics C style)
-- make code (even PASM) as modular as you can so that it's easier to re-use
-- PASM labels really should be in column 1
-- when you do a jmp or call you must prefix the label with # (PASM address), else it will treat the value in the variable you reference as an address
-- when possible, make your code system frequency agnostic
Additionally, I don't understand something about the code that I think may be the big thing I am missing. You pass scog to sampler. But then you skip over it and get the #samples. How do you know that's at the next long? What sets that structure?
CON ' This uses the Quickstart. Cog 1 is constantly sampling the touch buttons and writing the value to hub RAM. ' Cog 2 is reading the hub ram value and lighting the LEDs. Only purpose is to demonstrate writing/read of hub ram ' by the cogs. _CLKMODE = XTAL1 + PLL16X _XINFREQ = 5_000_000 BUTTONS = $FF SAMPLES = 10 RATE = 1_000 BUTTON_MASK = $FF ADDRESS_INCREMENT_UNIT = 4 VAR LONG _id LONG _resources0 LONG _resources1 LONG _resources2 LONG _resources3 LONG buttons_value OBJ pst : "Parallax Serial Terminal" PUB Main _resources0 := buttons & BUTTON_MASK _resources1 := samples _resources2 := CLKFREQ / rate _resources3 := buttons_value COGNEW(@SETUP, @_resources0) COGNEW(@LIGHT, @_resources3) pst.Start(115_200) repeat pst.bin(_resources3,8) pst.newline pst.Str(string("--------")) pst.newline waitcnt(cnt+80_000_000) pst.stop DAT ORG 0 SETUP MOV parameter, PAR ' get PAR address RDLONG user_button_mask, parameter ' read mask value ADD parameter, #ADDRESS_INCREMENT_UNIT ' move to next par long RDLONG user_sample_count, parameter ' read sample count ADD parameter, #ADDRESS_INCREMENT_UNIT ' move to next par long RDLONG user_sample_rate, parameter ' read sample rate ADD parameter, #ADDRESS_INCREMENT_UNIT ' move to next par long MOV button_state_address, parameter ' get par address for button value storage MOV OUTA, user_button_mask ' set button values to 1s MOV wait_time, CNT ' set up timer ADD wait_time, user_sample_rate NEXT_STATE MOV current_button_state, user_button_mask ' init current_button_state with 1s MOV current_sample_count, user_sample_count ' store sample count OR DIRA, user_button_mask ' set buttons to outputs * this is how these resistive ANDN DIRA, user_button_mask ' set to inputs * touch buttons work OR dira, LED_MASK ' set LEDs as outputs to correct what previous inst did WAITCNT wait_time, user_sample_rate ' wait for next sample ANDN current_button_state, INA ' read button state SUB current_sample_count, #1 ' dec sample counter NEXT_SAMPLE MOV previous_button_state, current_button_state ' store previous button state MOV DIRA, user_button_mask ' same cmds as before to get button state ANDN DIRA, user_button_mask ' OR dira, LED_MASK ' WAITCNT wait_time, user_sample_rate ' ANDN current_button_state, INA ' CMP previous_button_state, current_button_state WZ' compare previous and current states IF_NE JMP #NEXT_STATE ' if not eq return to next_state DJNZ current_sample_count, #NEXT_SAMPLE ' get another sample if not yet at sample count WRLONG current_button_state, button_state_address ' if 'sample count' number of consecutive samples ' MOV button_state, current_button_state ' are the, then readings were accurate and value ' SHL button_state, #16 ' is written to hub ram ' MOV LED_state, outa ' ANDN LED_state, LED_MASK ' The commented out code was doing the work ' OR LED_state, button_state ' the second cog should be doing now. ' MOV outa, LED_state JMP #NEXT_STATE LED_MASK long $FF<<16 button_state_address RES 1 current_button_state RES 1 current_sample_count RES 1 parameter RES 1 previous_button_state RES 1 user_button_mask RES 1 user_sample_count RES 1 user_sample_rate RES 1 wait_time RES 1 ' button_state RES 1 ' LED_state RES 1 ORG LIGHT mov button_val_addr, PAR ' get button value address from par :loop rdlong button_val, button_val_addr ' read button value from hub ram SHL button_val, #16 ' shift left to align with LED pins mov temp, outa ' get current output value ANDN temp, LED_MASK1 ' mask out LED bits OR temp, button_val ' or the shifted button value mov outa, temp ' set outa to the new LED values jmp #:loop ' loop to next read of button value LED_MASK1 long $FF<<16 temp RES 1 button_val_addr RES 1 button_val RES 1 fit_resources3 := @buttons_value 'Make resources a pointer ie contain the address of the allocated buttons_value LIGHT MOV parameter, PAR ' get PAR address rdlong button_val_addr , parameter ' read the pointer value from resources3 :loop rdlong button_val, button_val_addr ' read button value from hub ramBeside that, the button sample methode you use seems to not work well for my fingers.
So I have made a simplified version of your code with my own way to read the buttons:
CON ' This uses the Quickstart. Cog 1 is constantly sampling the touch buttons and writing the value to hub RAM. ' Cog 2 is reading the hub ram value and lighting the LEDs. Only purpose is to demonstrate writing/read of hub ram ' by the cogs. _CLKMODE = XTAL1 + PLL16X _XINFREQ = 5_000_000 VAR long buttonMask, buttonDly, buttonVal OBJ pst : "Parallax Serial Terminal" PUB Main buttonMask := $00FF buttonDly := clkfreq/1000 '1ms cognew(@setup, @buttonMask) cognew(@light, @buttonVal) pst.Start(115_200) repeat pst.bin(buttonVal,8) pst.newline pst.Str(string("--------")) pst.newline waitcnt(cnt+80_000_000) pst.stop DAT ORG 0 setup MOV adr, PAR ' get PAR address RDLONG user_button_mask, adr ' read mask value ADD adr, #4 ' move to next par long RDLONG user_sample_rate, adr ' read sample rate ADD adr, #4 MOV button_state_address, adr ' get par address for button value storage MOV OUTA, user_button_mask ' set button values to 1s MOV wait_time, CNT ' set up timer ADD wait_time, user_sample_rate getState OR DIRA, user_button_mask ' set buttons to outputs * this is how these resistive ANDN DIRA, user_button_mask ' set to inputs * touch buttons work WAITCNT wait_time, user_sample_rate ' wait button delay time MOV current_button_state, INA ' read button state XOR current_button_state, user_button_mask ' state = invers of read AND current_button_state, user_button_mask ' mask buttons WRLONG current_button_state, button_state_address ' write state to buttonVal var ANDN OUTA, user_button_mask ' discharge button caps OR DIRA, user_button_mask WAITCNT wait_time, user_sample_rate ' wait button delay time OR OUTA, user_button_mask ' all 1 for next sampling JMP #getState button_state_address RES 1 current_button_state RES 1 adr RES 1 user_button_mask RES 1 user_sample_rate RES 1 wait_time RES 1 DAT ORG 0 light mov DIRA, LED_MASK ' set LED pins to output :loop rdlong button_val, par ' read button value from hub ram shl button_val, #16 ' shift left to align with LED pins mov OUTA, button_val ' set outa to the new LED values jmp #:loop ' loop to next read of button value LED_MASK long $FF<<16 button_val RES 1 fitThis works on my Quickstart board.
Andy
Johnny, I'm going to try yours out again. I didn't change anything in it and assumed I didn't need to. But you know what they say when you assume...
(Edited: Oh, yeah, I figured it out. Open the terminal and hit a button. DOH!)