Okay, here's my attempt at transcribing the PAD_IO sheet into a mode list for the main Prop2 doc:
%PPPPPPPPPPPPP: low-level pin control (engineering sample die)
common labelling of pin config bits
%C = clocked I/O (extra clock for IN and OUT)
%I = invert IN output
%O = invert OUT input
%HHH/LLL = digital out drive strength
000: Fast
001: 1k5R
010: 15kR
011: 150kR
100: 1mA
101: 100uA
110: 10uA
111: Float
PinA is specified pin number
PinB is PinA's odd/even pair
%0_VVV_CIOHHHLLL = Digital mode (default = %0000000000000)
DIR enables PinA digital output
%VVV = Digital config
000: IN = PinA logic, PinA output from OUT
001: IN = PinA logic, PinA output from IN
010: IN = PinB logic, PinA output from IN
011: IN = PinA schmitt, PinA output from OUT
100: IN = PinA schmitt, PinA output from IN
101: IN = PinB schmitt, PinA output from IN
110: IN = PinA > PinB comparator, PinA output from OUT
111: IN = PinA > PinB comparator, PinA output from IN
%100_VVV_OHHHLLL = ADC_MODE, first order sigma-delta
IN has bitstream, sysclock bitrate, for smartpin mode %01111 (Y=0)
OUT is PinA digital output, clocked
DIR enables PinA digital output
%VVV = ADC config
000: GIO, 1x (~5 volt range, centred on VIO/2)
001: VIO, 1x "
010: PinB, 1x "011: PinA, 1x "
100: PinA, 3.16x (~1.58 volt range, centred on VIO/2)
101: PinA, 10x (~0.5 volt range, centred on VIO/2)
110: PinA, 31.6x (~0.158 volt range, centred on VIO/2)
111: PinA, 100x (~0.05 volt range, centred on VIO/2)
%101_VV_DDDDDDDD = DAC_MODE (%TT = 00 and %MMMMM = 00000), 8-bit flash
OUT enables PinA ADC (config %011), sysclocked bitstream on IN
DIR enables PinA DAC output
%VV = PinA DAC config
00: 990 ohm, 3.3 volt range
01: 600 ohm, 2.0 volt range
10: 123.75 ohm, 3.3 volt range
11: 75 ohm, 2.0 volt range
%DDDDDDDD = DAC level
for %TT = %01 and %MMMMM = %00000, %101_VV_xxxxSSSS = COG_DAC mode
%SSSS = Cog/streamer select: sets DAC level (registered?)
for %00000 < %MMMMM < %00100 = SMART_DAC mode
DIR/IN are usual smartpin ctrl
%DDDDDDDD ignored, smartpin sets DAC level (registered?)
for %MMMMM >= %00100 or (%TT = %1x and %MMMMM = %00000) = BIT_DAC mode
OUT sets DAC level (clocked?, ADC disabled?, IN = ?)
0: 0 = GIO level
1: %DDDDDDDD
%11_VV_CDDDDDDDD = COMP_DAC comparator mode
DIR enables PinA digital output
%VV = Comparator config
00: IN = PinA > D, PinA driven by 1k5R from OUT
01: IN = PinA > D, PinA driven by 1k5R from !IN
10: IN = PinB > D, PinA driven by 1k5R from IN
11: IN = PinB > D, PinA driven by 1k5R from !IN
%DDDDDDDD = DAC level for internal analogue compare
EDIT: Fixes already!
23-10-2018: Fleshed out BIT_DAC mode
24-10-2018: Tidy up labelling
25-10-2018: Clarify ADC function in DAC_MODE
1-11-2018: Add SMART_DAC mode and clocking details. Give unique identifier to cog/streamer select
20-11-2018: Correct the smartpin mode number for capturing ADC bitstream
25-11-2018: Reverse the update. Doh!
26-12-2018: Typo, R -> % in BIT_DAC mode line. And stated for engineering sample.
10-12-2019: Link to maintained version
Having started to play with smart pins on my 64000 today, one thing that would have helped me a lot
would be a set of example P2ASM configurations for each mode (and major submode) in each section of the
documentation, making clear what TT settings, OUT/DIR settings, PPPPP and MMM mode bits are relevant
to that mode, and providing a working starting point for further experimentation. I seem to have spent far
too long figuring out DAC and PWM modes by guesswork, and the interaction of DIRx/OUTx/TT fields
seems to vary considerably with mode.
On the plus side I'm impressed with the upload speed compared to the P1, and the convenience of not having
to use any Spin to run a Pasm example - makes it a lot quicker and easier to try out P2ASM fragments. And
## is so incredibly handy!
Having started to play with smart pins on my 64000 today, one thing that would have helped me a lot
would be a set of example P2ASM configurations for each mode (and major submode) in each section of the
documentation, making clear what TT settings, OUT/DIR settings, PPPPP and MMM mode bits are relevant
to that mode, and providing a working starting point for further experimentation. I seem to have spent far
too long figuring out DAC and PWM modes by guesswork, and the interaction of DIRx/OUTx/TT fields
seems to vary considerably with mode.
On the plus side I'm impressed with the upload speed compared to the P1, and the convenience of not having
to use any Spin to run a Pasm example - makes it a lot quicker and easier to try out P2ASM fragments. And
## is so incredibly handy!
Can we hope that you have been making notes as you figure things out and contribute them to the documentation?
Yes, I'm keeping a working example for each mode, I'll be tidying them up into a looping example I suspect, still
got to work on streaming modes 3-fold pipelined cordic -> dithered DAC mode was last nights example, looks pretty clean on spectrum analyzer.
Well today I worked on pipelined CORDIC again and have an example that does frequency-agile DDS using
hub memory FIFO for setting the frequency word, and issues CORDIC rotate every 16 cycles to drive a
smartpin in DAC dither mode. That's 10MSPS rate.
'' DDS using cordic -> DAC for P2 Eval board'' Pipelined use of CORDIC cosine to generate samples to a DAC smartpin every 16 cycles, using a DDS step' which reads its frequency long from hub every sample for full agility.'' The main loop needs 2 instructions to interface the CORDIC engine, 1 to do phase increment, 1 to read' frequency word, 2 to convert and write sample to DAC, and a JMP. This takes at least 15 cycles as' adding another NOP breaks the timing. I'm not sure which instruction is > 2 cycles, perhaps its relative' timing of CORDIC and FIFO/Smartpin windows?'' Mark TillotsonCON
OSCMODE = $010c3f04
freq = 160_000_000' various DAC smart pin modes
dac = %0000_0000_000_10100_00000000_00_00000_0
prng = %0000_0000_000_10100_00000000_01_00001_0
dither = %0000_0000_000_10100_00000000_01_00010_0' remember to wypin
dither_pwm = %0000_0000_000_10100_00000000_01_00011_0' remember to wypin
DACpin = 48' output pinOBJVARlong frq[16] ' single RDFAST blockPUB demo | i
clkset (OSCMODE, freq)
freq_addr := @frq
i := 0repeat16
frq[i] := 20615843' 48kHz / 10MHz * 2^32
i += 1
i := 0
cognew (@entry)
repeat
pausems(2)
frq[i] += 10000' spin increments the frequency slowly.
i += 1' since fast read cycles through at least one block of 16 longs, have to update all 16
i &= 15' another P2ASM cog could synchronously update the block for full control' note I tend to uppercase the exotic/timing-sensitive instructionsDATorg0
entry
WRPIN ##dither_pwm, #DACpin ' smartpin mode, can leave DIRB/OUTB as is for this oneRDFAST ##1, freq_addr ' need FIFO access to get a 2 cycle hub read in main loopRFLONG frequency ' prime the FIFOmov val, #4' queue 4 rotates (every 16 cycles) into CORDIC before pulling results
setup
add phase, frequency ' DDS phase incQROTATE amplitude, phase ' queue cosinenop' force 16 cycles, not 8, between entriesRFLONG frequency
djnz val, #setup
' 4 cosines in flight in CORDIC module, can start consuming them and outputting to DAC indefinitely' in the main loop (16 cycles, so at 160MHz generates 10MSPS, Nyquist limit 5MHz
loop
GETQX val ' signed 16 bit cosine value, fetch just in timeadd phase, frequency
QROTATE amplitude, phase
xor val, signbit ' need offset value for DACWYPIN val, #DACpin
RFLONG frequency ' 2 cycle load of next frequencyjmp #loop
amplitude long$7FFF
signbit long$8000
phase long0
freq_addr long0
val res1
frequency res1
It shouldn't be hard to change the agile variable to be the amplitude, and then, with the help of a modulator code
reading an ADC pin, to generate AM or FM signals upto 5MHz. I see a dynamic range just over 50dB on the spectrum
analyzer.
Got a question about Fastspin though: I can see freq[] array defined in hubRAM and the cog code fetching those values. But you are launching a separate cog with its own cog variables and RDFAST appears to get it's freq_addr from the Spin code at runtime. Is that a feature of filling it out, effectively self-modifying, so that it is copied into the cog upon COGINIT? I've never written any Spin code, is that something that could be done in the original Prop1 Spin?
Couple of notes on the cordic loop:
- RDFAST primes itself unless D[31] is set.
- The "setup" loop is already 10 clocks long without any NOP.
So the following is a little tidier even if it achieves no more.
RDFAST ##1, freq_addr ' need FIFO access to get a 2 cycle hub read in main loopmov val, #4' queue 4 rotates (every 16 cycles) into CORDIC before pulling results
setup
RFLONG frequency
add phase, frequency ' DDS phase incQROTATE amplitude, phase ' queue cosinedjnz val, #setup
' 4 cosines in flight in CORDIC module, can start consuming them and outputting to DAC indefinitely' in the main loop (16 cycles, so at 160MHz generates 10MSPS, Nyquist limit 5MHz
loop
RFLONG frequency ' 2 cycle load of next frequencyadd phase, frequency
GETQX val ' signed 16 bit cosine value, fetch just in timeQROTATE amplitude, phase
xor val, signbit ' need offset value for DACWYPIN val, #DACpin
jmp #loop
There is a possibility that I've placed GETQ is too close to the QROTATE in the main loop. Moving it earlier still works:
RDFAST ##1, freq_addr ' need FIFO access to get a 2 cycle hub read in main loopmov val, #4' queue 4 rotates (every 16 cycles) into CORDIC before pulling results
setup
RFLONG frequency
add phase, frequency ' DDS phase incQROTATE amplitude, phase ' queue cosinedjnz val, #setup
' 4 cosines in flight in CORDIC module, can start consuming them and outputting to DAC indefinitely' in the main loop (16 cycles, so at 160MHz generates 10MSPS, Nyquist limit 5MHz
loop
GETQX val ' signed 16 bit cosine value, fetch just in timeRFLONG frequency ' 2 cycle load of next frequencyadd phase, frequency
QROTATE amplitude, phase
xor val, signbit ' need offset value for DACWYPIN val, #DACpin
jmp #loop
Got a question about Fastspin though: I can see freq[] array defined in hubRAM and the cog code fetching those values. But you are launching a separate cog with its own cog variables and RDFAST appears to get it's freq_addr from the Spin code at runtime. Is that a feature of filling it out, effectively self-modifying, so that it is copied into the cog upon COGINIT? I've never written any Spin code, is that something that could be done in the original Prop1 Spin?
I think that's my habit since BST gets the @operator wrong in DAT sections (or is that a feature?)
Couple of notes on the cordic loop:
- RDFAST primes itself unless D[31] is set.
Ah, I just heeded the warnings about needing to be primed
- The "setup" loop is already 10 clocks long without any NOP.
Is this due to branch instructions taking more than 2 cycles?
And replacing the JMP with a REP will recover 4 clocks if there was a need for that.
- The "setup" loop is already 10 clocks long without any NOP.
Is this due to branch instructions taking more than 2 cycles?
yep, opposite timing order to the Prop1. Prop2 is four clocks if branch taken and two clocks if branch not taken. I think compensating for this change is the main reason Chip added the REP instruction.
Condensing a 4 stage pipeline to 2 and adding hundreds of new instructions without something giving would
have been a miracle! Hopefully Chip's scheduled in a few months of sleep after the next silicon revision goes out!
Yes, REP is tool without a safety guard, but worries me less than SKIP/SKIPF !
At last I've captured some spectra from my cordic dac output. I use 16bit random dither mode, which seems
cleaner than pwm dither.
Output tone is nominally 2.304MHz, sampled at 10MHz, so the image frequency of 7.696MHz is
obvious in the first 10M span spectrum. Then I zoom in closer to the tone.
Siglent SSA3021X analyzer - some of the broad sidebands may be the limiting phase noise of the SA itself, note.
Comments
DIRL #tpin WRPIN ##%10111_DDDDDDDD_01_01000_0, #tpin ' %01000 = PWM triangle smartpin ' %10111 = DAC mode: 2 volt range, 75 ohm ' %DDDDDDDD = DAC level for BIT_DAC = high WXPIN ##$100_0001, #tpin ' PWM period = 256 clocks WYPIN #0, #tpin ' Initial PWM level DIRH #tpin
EDIT: Added 2 volt detailNormally DAC mode %10111 sets a 2.0 volt output level. I'm assuming this is overridden by the BIT_DAC set level.
Nope. The smart pin doesn't control the internal pull down resistor, only the 8-bit DAC output value. If you set 2.0 volt mode, that's where it works.
Okay, here's my attempt at transcribing the PAD_IO sheet into a mode list for the main Prop2 doc:
%PPPPPPPPPPPPP: low-level pin control (engineering sample die) common labelling of pin config bits %C = clocked I/O (extra clock for IN and OUT) %I = invert IN output %O = invert OUT input %HHH/LLL = digital out drive strength 000: Fast 001: 1k5R 010: 15kR 011: 150kR 100: 1mA 101: 100uA 110: 10uA 111: Float PinA is specified pin number PinB is PinA's odd/even pair %0_VVV_CIOHHHLLL = Digital mode (default = %0000000000000) DIR enables PinA digital output %VVV = Digital config 000: IN = PinA logic, PinA output from OUT 001: IN = PinA logic, PinA output from IN 010: IN = PinB logic, PinA output from IN 011: IN = PinA schmitt, PinA output from OUT 100: IN = PinA schmitt, PinA output from IN 101: IN = PinB schmitt, PinA output from IN 110: IN = PinA > PinB comparator, PinA output from OUT 111: IN = PinA > PinB comparator, PinA output from IN %100_VVV_OHHHLLL = ADC_MODE, first order sigma-delta IN has bitstream, sysclock bitrate, for smartpin mode %01111 (Y=0) OUT is PinA digital output, clocked DIR enables PinA digital output %VVV = ADC config 000: GIO, 1x (~5 volt range, centred on VIO/2) 001: VIO, 1x " 010: PinB, 1x " 011: PinA, 1x " 100: PinA, 3.16x (~1.58 volt range, centred on VIO/2) 101: PinA, 10x (~0.5 volt range, centred on VIO/2) 110: PinA, 31.6x (~0.158 volt range, centred on VIO/2) 111: PinA, 100x (~0.05 volt range, centred on VIO/2) %101_VV_DDDDDDDD = DAC_MODE (%TT = 00 and %MMMMM = 00000), 8-bit flash OUT enables PinA ADC (config %011), sysclocked bitstream on IN DIR enables PinA DAC output %VV = PinA DAC config 00: 990 ohm, 3.3 volt range 01: 600 ohm, 2.0 volt range 10: 123.75 ohm, 3.3 volt range 11: 75 ohm, 2.0 volt range %DDDDDDDD = DAC level for %TT = %01 and %MMMMM = %00000, %101_VV_xxxxSSSS = COG_DAC mode %SSSS = Cog/streamer select: sets DAC level (registered?) for %00000 < %MMMMM < %00100 = SMART_DAC mode DIR/IN are usual smartpin ctrl %DDDDDDDD ignored, smartpin sets DAC level (registered?) for %MMMMM >= %00100 or (%TT = %1x and %MMMMM = %00000) = BIT_DAC mode OUT sets DAC level (clocked?, ADC disabled?, IN = ?) 0: 0 = GIO level 1: %DDDDDDDD %11_VV_CDDDDDDDD = COMP_DAC comparator mode DIR enables PinA digital output %VV = Comparator config 00: IN = PinA > D, PinA driven by 1k5R from OUT 01: IN = PinA > D, PinA driven by 1k5R from !IN 10: IN = PinB > D, PinA driven by 1k5R from IN 11: IN = PinB > D, PinA driven by 1k5R from !IN %DDDDDDDD = DAC level for internal analogue compare
EDIT: Fixes already!23-10-2018: Fleshed out BIT_DAC mode
24-10-2018: Tidy up labelling
25-10-2018: Clarify ADC function in DAC_MODE
1-11-2018: Add SMART_DAC mode and clocking details. Give unique identifier to cog/streamer select
20-11-2018: Correct the smartpin mode number for capturing ADC bitstream
25-11-2018: Reverse the update. Doh!
26-12-2018: Typo, R -> % in BIT_DAC mode line. And stated for engineering sample.
10-12-2019: Link to maintained version
would be a set of example P2ASM configurations for each mode (and major submode) in each section of the
documentation, making clear what TT settings, OUT/DIR settings, PPPPP and MMM mode bits are relevant
to that mode, and providing a working starting point for further experimentation. I seem to have spent far
too long figuring out DAC and PWM modes by guesswork, and the interaction of DIRx/OUTx/TT fields
seems to vary considerably with mode.
On the plus side I'm impressed with the upload speed compared to the P1, and the convenience of not having
to use any Spin to run a Pasm example - makes it a lot quicker and easier to try out P2ASM fragments. And
## is so incredibly handy!
Can we hope that you have been making notes as you figure things out and contribute them to the documentation?
got to work on streaming modes
hub memory FIFO for setting the frequency word, and issues CORDIC rotate every 16 cycles to drive a
smartpin in DAC dither mode. That's 10MSPS rate.
' ' DDS using cordic -> DAC for P2 Eval board ' ' Pipelined use of CORDIC cosine to generate samples to a DAC smartpin every 16 cycles, using a DDS step ' which reads its frequency long from hub every sample for full agility. ' ' The main loop needs 2 instructions to interface the CORDIC engine, 1 to do phase increment, 1 to read ' frequency word, 2 to convert and write sample to DAC, and a JMP. This takes at least 15 cycles as ' adding another NOP breaks the timing. I'm not sure which instruction is > 2 cycles, perhaps its relative ' timing of CORDIC and FIFO/Smartpin windows? ' ' Mark Tillotson CON OSCMODE = $010c3f04 freq = 160_000_000 ' various DAC smart pin modes dac = %0000_0000_000_10100_00000000_00_00000_0 prng = %0000_0000_000_10100_00000000_01_00001_0 dither = %0000_0000_000_10100_00000000_01_00010_0 ' remember to wypin dither_pwm = %0000_0000_000_10100_00000000_01_00011_0 ' remember to wypin DACpin = 48 ' output pin OBJ VAR long frq[16] ' single RDFAST block PUB demo | i clkset (OSCMODE, freq) freq_addr := @frq i := 0 repeat 16 frq[i] := 20615843 ' 48kHz / 10MHz * 2^32 i += 1 i := 0 cognew (@entry) repeat pausems(2) frq[i] += 10000 ' spin increments the frequency slowly. i += 1 ' since fast read cycles through at least one block of 16 longs, have to update all 16 i &= 15 ' another P2ASM cog could synchronously update the block for full control ' note I tend to uppercase the exotic/timing-sensitive instructions DAT org 0 entry WRPIN ##dither_pwm, #DACpin ' smartpin mode, can leave DIRB/OUTB as is for this one RDFAST ##1, freq_addr ' need FIFO access to get a 2 cycle hub read in main loop RFLONG frequency ' prime the FIFO mov val, #4 ' queue 4 rotates (every 16 cycles) into CORDIC before pulling results setup add phase, frequency ' DDS phase inc QROTATE amplitude, phase ' queue cosine nop ' force 16 cycles, not 8, between entries RFLONG frequency djnz val, #setup ' 4 cosines in flight in CORDIC module, can start consuming them and outputting to DAC indefinitely ' in the main loop (16 cycles, so at 160MHz generates 10MSPS, Nyquist limit 5MHz loop GETQX val ' signed 16 bit cosine value, fetch just in time add phase, frequency QROTATE amplitude, phase xor val, signbit ' need offset value for DAC WYPIN val, #DACpin RFLONG frequency ' 2 cycle load of next frequency jmp #loop amplitude long $7FFF signbit long $8000 phase long 0 freq_addr long 0 val res 1 frequency res 1
It shouldn't be hard to change the agile variable to be the amplitude, and then, with the help of a modulator code
reading an ADC pin, to generate AM or FM signals upto 5MHz. I see a dynamic range just over 50dB on the spectrum
analyzer.
Got a question about Fastspin though: I can see freq[] array defined in hubRAM and the cog code fetching those values. But you are launching a separate cog with its own cog variables and RDFAST appears to get it's freq_addr from the Spin code at runtime. Is that a feature of filling it out, effectively self-modifying, so that it is copied into the cog upon COGINIT? I've never written any Spin code, is that something that could be done in the original Prop1 Spin?
Couple of notes on the cordic loop:
- RDFAST primes itself unless D[31] is set.
- The "setup" loop is already 10 clocks long without any NOP.
So the following is a little tidier even if it achieves no more.
RDFAST ##1, freq_addr ' need FIFO access to get a 2 cycle hub read in main loop mov val, #4 ' queue 4 rotates (every 16 cycles) into CORDIC before pulling results setup RFLONG frequency add phase, frequency ' DDS phase inc QROTATE amplitude, phase ' queue cosine djnz val, #setup ' 4 cosines in flight in CORDIC module, can start consuming them and outputting to DAC indefinitely ' in the main loop (16 cycles, so at 160MHz generates 10MSPS, Nyquist limit 5MHz loop RFLONG frequency ' 2 cycle load of next frequency add phase, frequency GETQX val ' signed 16 bit cosine value, fetch just in time QROTATE amplitude, phase xor val, signbit ' need offset value for DAC WYPIN val, #DACpin jmp #loop
RDFAST ##1, freq_addr ' need FIFO access to get a 2 cycle hub read in main loop mov val, #4 ' queue 4 rotates (every 16 cycles) into CORDIC before pulling results setup RFLONG frequency add phase, frequency ' DDS phase inc QROTATE amplitude, phase ' queue cosine djnz val, #setup ' 4 cosines in flight in CORDIC module, can start consuming them and outputting to DAC indefinitely ' in the main loop (16 cycles, so at 160MHz generates 10MSPS, Nyquist limit 5MHz loop GETQX val ' signed 16 bit cosine value, fetch just in time RFLONG frequency ' 2 cycle load of next frequency add phase, frequency QROTATE amplitude, phase xor val, signbit ' need offset value for DAC WYPIN val, #DACpin jmp #loop
Ah, that's a clever way round it.
yep, opposite timing order to the Prop1. Prop2 is four clocks if branch taken and two clocks if branch not taken. I think compensating for this change is the main reason Chip added the REP instruction.
have been a miracle! Hopefully Chip's scheduled in a few months of sleep after the next silicon revision goes out!
Yes, REP is tool without a safety guard, but worries me less than SKIP/SKIPF !
cleaner than pwm dither.
Output tone is nominally 2.304MHz, sampled at 10MHz, so the image frequency of 7.696MHz is
obvious in the first 10M span spectrum. Then I zoom in closer to the tone.
Siglent SSA3021X analyzer - some of the broad sidebands may be the limiting phase noise of the SA itself, note.
The 2nd harmonic of 4.608MHz is about 50dB down.
Nice plots, did you check if any of those spurs wander with temperature ? (see other thread, where DAC spurs vary with temperature)