Is it possible to tighten this loop?
Andrey Demenev
Posts: 377
I need to make an arbitrary waveform generator using an external DAC. Frequency value should be read from HUB RAM. Once frequency is set to zero, the generation should be stopped - not immediately, but as close as possible to the moment when waveform crosses the zero. The following code works just fine:
The problem is that sampling frequency is not big enough. I would like to use another cog to double the sampling freq. Obviously, they should be interleaved, and at the moment one cog outputs a sample, another one should output zero, so they do not step each others toes. It may seem that there is some space to do this (see the NOP'ed instructions) - but zeroing OUTA occurs at wrong time. One sampling period takes 32 cycles, and each cog should zero OUTA exactly 16 cycles after it outputs its sample. The problem here is that I need to access HUB RAM twice per sampling period - first to read sample value, second - to read frequency value. Because I read sample directly to OUTA, frequency reading occurs at same moment when zeroing should happen. Reading sample to a register could help - but I am out of cycles.
I could save 4 cycles per sample if the waveform table was located starting from zero address - then I would not need add addr, base, but unfortunately this does not seem possible (well, it IS possible, but in that case I would have to code everything in assembler, and use very non-trivial programming techniques)
Any advise?
gen :loop rdword OUTA, addr mov addr, PHSA nop 'mov OUTA, #0 rdlong freq, PAR wz shr addr, #19 add addr, base rdword OUTA, addr mov addr, PHSA nop 'mov OUTA, #0 shr addr, #19 add addr, base if_z jmp #fadeout nop rdword OUTA, addr mov addr, PHSA nop 'mov OUTA, #0 shr addr, #19 add addr, base mov FRQA, freq jmp #:loop fadeout test middle, OUTA wc :loop rdword OUTA, addr test middle, OUTA wz nop 'mov OUTA, #0 mov addr, PHSA shr addr, #19 add addr, base if_c_ne_z jmp #:loop mov OUTA, middle :wait rdlong freq, PAR wz if_z jmp #:wait jmp #gen middle long $200 base long 16384 pinmask long $3FF addr res 1 sample res 1 freq res 1
The problem is that sampling frequency is not big enough. I would like to use another cog to double the sampling freq. Obviously, they should be interleaved, and at the moment one cog outputs a sample, another one should output zero, so they do not step each others toes. It may seem that there is some space to do this (see the NOP'ed instructions) - but zeroing OUTA occurs at wrong time. One sampling period takes 32 cycles, and each cog should zero OUTA exactly 16 cycles after it outputs its sample. The problem here is that I need to access HUB RAM twice per sampling period - first to read sample value, second - to read frequency value. Because I read sample directly to OUTA, frequency reading occurs at same moment when zeroing should happen. Reading sample to a register could help - but I am out of cycles.
I could save 4 cycles per sample if the waveform table was located starting from zero address - then I would not need add addr, base, but unfortunately this does not seem possible (well, it IS possible, but in that case I would have to code everything in assembler, and use very non-trivial programming techniques)
Any advise?
Comments
Your waveform is in a DAT section, the first one in your main source file (so it will be placed at the lowest address). When initializing you just move that block a few longs down making it start at zero . Of course you lose the clock config long at address 0, so any code that depends on that should be rewrote .
or something like that
Scratch that, I need more coffee. 10 days off the grid is doing that to you. 4/12 sounds fine.
- at higher resolution, DACs are too expensive
- it makes no sense to use higher resolution with 16KiB of samples memory
Also, I want 2 channels - so I think I will go 10 bits, with 12-bit DACs (lower bits always zero, to minimize linearity errors). Otherwise I do not have enough pins - I need rotary encoder, some keys, input for frequency counter, 2 outputs for DC offsets, LCD etc.
If I wanted only sine, triangle and square - it would make more sense to use an integrated DDS circuit. But I want an AWG