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 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
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 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
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
Normally 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: 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 3-fold pipelined cordic -> dithered DAC mode was last nights example, looks pretty clean on spectrum analyzer.
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.
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.
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)