Let's Design and Build a (mostly) Digital Theremin!

Posted: 12/5/2024 1:23:22 PM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

"On the last model, I've tried to check how it behaves on F/2 and F/3.
On F/2, antenna voltage and inductor current are very small. Current sense outout is noise-like or constant.
On F/3 drive, antenna voltage and inductor current have big enough swing, about 1/3 of normal, max frequency in spectrum is F, current sense output is 50% duty cycle with frequency F." - Buggins

Very little resonance at F/2 is expected because the even harmonics of a square wave have theoretically zero energy.  F/3 drive being 1/3 amplitude makes sense too, harmonic amplitude is the inverse of the harmonic number.

"To resolve an issue with locking on harmonics, we could add NCO frequency range limiting parameter registers."

A feature that's already there in the FPGA, though it's a fixed build parameter rather than something changeable by SW.  In the file "hive_pkg.sv" there is a line: 

// common
parameter PV_RANGE_W = 3; // NCO range (lower limit)

This is a power of 2, so 2^3 = 8 octaves lock range, which is kinda high I guess.  IIRC the DPLL starts out at the lower frequency limit because the phase detection limiter increments the NCO frequency until it is in the set lock range.  For software control it would probably be better to be able to set the range as octaves, rather than powers of 2 octaves.  Some feedback as to how many cycles were going on would be good too.

Sine drive would likely get around a lot or all of this, because the harmonics would have very little or no energy to stimulate the coil & antenna.

Posted: 12/5/2024 1:52:37 PM
Zhuoran

Joined: 11/29/2024

Quote from: dewster on 12/4/2024
I've seen the DPLL lock to harmonics as you describe. It happened with a particular batch of 74AHC04 ICs which I presume (didn't test this to confirm) had more hysteresis than normal. Process variations like this are another reason I'd like to go to LVU and/or discrete transistors.

I'm using 74LVC04 (SN74LVC2G04), which should be similar to AHC, so this could be the reason.

Quote from: dewster on 12/4/2024

"One advantage of it over using standalone oscillator is it may be able to provide better phase alignment."

How so?


Without feedback, it's hard to provide an exact 90° phase shift, or to synchronize the drive signal with a sense signal exactly. All active components have delay, the value of all passive components are not exact. Even if we tune a circuit to provide exactly the required delay, if the delay is constant, it will only work for a particular frequency. (D)DLLs and (D)PLLs have feedback, so they can dynamically adjust and compensate for the inaccuracies.

Quote from: dewster on 12/4/2024
For DPLL, I wonder if some sort of watchdog that senses harmonic locking could be designed to automatically re-position the operating frequency?

The most straightforward way would be limiting the frequency range. The range can be calculated from the nominal frequency, and the nominal frequency can be set manually, like what's done in D-Lev now, or determined automatically if we start the oscillation in some other ways.

I think it's beneficial to have a better startup process. In the FPGA source of D-Lev's DPLL, I don't see it does anything to start the oscillation. It seems that the frequency is just reset to 0, and when the reset is de-asserted, it simply starts normal operation, without deliberate attempt to e.g. find the resonance frequency. Did I miss something?

Finding the resonance frequency before oscillation starts can be something of a chicken and egg problem. Before oscillation starts or when the amplitude is low, the quad feedback is invalid or at least unreliable, so the DPLL may not have the information needed to adjust the frequency. But without an at least relatively accurate driving frequency, the amplitude will not build up. I'm actually a bit surprised by how well the inverters are able to amplify the small signals, even though the datasheets state that the input is invalid in a relatively large range. But obviously this amplification is not guaranteed, so it's probably better not to rely on it too much.

When I test the DPLL, I found that if I connect neither zero nor quad, the output will be a rapid frequency sweep from a rather low frequency to a high frequency, then it goes back to low frequency again. This seems to be the natural consequence of the XOR outputting a constant, and the frequency accumulator wraps around. Similar thing happens if I don't connect the inductor, and connect both inputs or only the quad. If I connect only the zero, then the output frequency will drift slowly, because the XOR output will be high and low both approximately a half of the time, but not exactly.
If I connect everything normally, reset it, then it will output a waveform of gradually increasing frequency, starting from a very low frequency. Looks like it just wait for the frequency to drift to the correct point. The quad did get some input initially, though the integrity is obviously not great, and because the frequency difference is too large, there isn't a meaningful phase relationship between the two inputs. And here's an issue: since the frequency is initially low, and gradually increases, it will reach fractions of the resonance frequency before reaching the actual resonance frequency, and it will have quite a bit of chance to lock with these!
Maybe the frequency just happened to be increasing in this case. It may also be possible for it to be decreasing in some other cases, then it will reach the resonance frequency first, and the oscillation will probably start without an issue.
Edit: The low_f signal will force the frequency to increase until it reaches the lower limit, only after which the change depends on the input.
Edit2: On the first clock cycle after reset, low_f is actually 0, because it's reset to 0. However, on the same cycle, xor_f is also 0, because it's also reset to 0, so freq_inc will still be 1. But for consistency, maybe it's better to reset low_f to 1 if freq_o is reset to 0?

As I mentioned before, using a(n inverter with) delay may be a good way to start the oscillation, and after the oscillation starts, we will be able to determine the nominal frequency, which could be useful for the operation of the DPLL. Alternatively, it can do a controlled frequency sweep, and determine at which frequency the quad input is the least noisy and the phase difference is closest to 90°. But if you want to save a bit of FPGA resource, then manually setting a frequency range should also work.

A pulse counting based watchdog is another option. Pulse counting is sensitive to edge noise, so if there's noise in the input, it needs to be filtered out.

Posted: 12/5/2024 2:10:17 PM
Zhuoran

Joined: 11/29/2024

Quote from: dewster on 12/5/2024
This is a power of 2, so 2^3 = 8 octaves lock range, which is kinda high I guess.

It's at least high enough for it to lock to the 3rd (or even higher) harmonics.

Posted: 12/5/2024 3:31:40 PM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

"Alternatively, it can do a controlled frequency sweep, and determine at which frequency the quad input is the least noisy and the phase difference is closest to 90°. But if you want to save a bit of FPGA resource, then manually setting a frequency range should also work."  - Zhuoran

Starting at max frequency rather than min might be a good approach?  I think this might be a trivial SV edit if you want to try it:

Near the end of the file "pdi_zq.sv" change the reset initialization of freq_o from 0 (all zeros) to '1 (all ones).  All ones might try to roll over though, so perhaps something a bit less than all ones might be better / safer?

"A pulse counting based watchdog is another option. Pulse counting is sensitive to edge noise, so if there's noise in the input, it needs to be filtered out."

Similar to serial framing detectors which operate out of band, their operation is generally quite disruptive, so state machines and counters are needed to make sure they don't kick in unnecessarily.

Posted: 12/5/2024 3:47:08 PM
Zhuoran

Joined: 11/29/2024

Quote from: Buggins on 12/5/2024
I like Xilinx FPGAs, and think that Zynq is a great base for the digital theremin! If properly designed, the same verilog code may fit into 4 times smaller LUT count.
SERDES I/O allows 1200 MHz bit rate for drive and sense (it's about 4x times finer resolution comparing to current D-Lev implementation).
ARM cores allow to amost unlimited processing, and provide UI of any complexity.
Zynq boards are expensive, but JLCPCB has a lot of options for Zynq cheaper than $20. E.g. 7010 or 7020 even with speed grade -2
Can the custom board with Zynq, RAM, voltage regulators, be designed to have a price about 50$ if manufactured on JLCPCB?

Zynq is indeed quite powerful, more powerful than what's needed for a digital theremin. My Zynq board wasn't bought for making theremins, but it definitely comes in handy here, especially the CPU and RAM. But for the final design, if there's ever going to be one, I'm considering to use something cheaper, maybe Spartan 6? The architecture is similar to 7 series, it also has SERDES, the speed is not as high but should be sufficient. There are a few XC6SLX9 and a XC6SLX16 available on LCSC for about $7. Zynq could be an option, too, if it's not too expensive. Of course, MCUs are the cheapest. STM32H750 is only $3, with 480MHz M7 CPU and 480MHz HRTIM. Performance not as high as Teensy, but it has >3x timer precision! I'm even thinking about implementing a soft DPLL in MCUs (needs to be highly optimized to be usable).

LUT6 has 4x as large capacity as LUT4 when used as memory (though it seems that the LUTs in Cyclone IV can only be used as logic or ROM, but not RAM), but when used as logic, I think it normally can't replace 4 LUT4s. However, there are cases where the additional inputs makes a relatively large difference. Some time ago I implemented a 32bit ALU, after some optimization I reduced the LUT count to not much more that 1 LUT6 per bit (each split into 2 LUT5s). If it's implemented using LUT4, the number would be larger. Not sure if it's going to be 4x larger, though.

Posted: 12/5/2024 4:35:38 PM
Zhuoran

Joined: 11/29/2024

Quote from: dewster on 12/5/2024
Starting at max frequency rather than min might be a good approach?  I think this might be a trivial SV edit if you want to try it:

Near the end of the file "pdi_zq.sv" change the reset initialization of freq_o from 0 (all zeros) to '1 (all ones).  All ones might try to roll over though, so perhaps something a bit less than all ones might be better / safer?


Why not starting directly at FREQ_NOM? If the estimation is accurate, it may be able to lock quickly. Even if it's not accurate, at least it's not as inaccurate as min/max frequency, right? I thought this is the most sensible initial value, and was a bit surprised to see that it's not used here. I just tried and it worked.

Modifications:

pdi_zq.sv

Code:
...

module pdi_zq
	#(
	parameter											FREQ_W				= 8,		// phase accum width (bits)
	parameter											RANGE_W				= 4,		// phase accum range (bits)
	parameter											SYNC_W				= 2,		// resync registers, 1 min (if I_DDR=0)
	parameter											I_DDR				= 1,		// 1=use ddr at input; 0=no ddr
+++	parameter											FREQ_NOM
	)

...

	// reg to speed up / output / accumulate
	always_ff @ ( posedge clk_i or posedge rst_i ) begin
		if ( rst_i ) begin
---			freq_o <= 0;
+++			freq_o <= FREQ_NOM;
		end else begin
			freq_o <= freq_o + freq_inc;
		end
	end

endmodule

lc_dpll.sv

Code:
...

	// phase detect & accum
	pdi_zq
	#(
	.FREQ_W				( FREQ_W ),
	.RANGE_W				( RANGE_W ),
	.SYNC_W				( SYNC_W ),
	.I_DDR				( IO_DDR ),
+++	.FREQ_NOM			( FREQ_NOM )
	)
	pdi_zq
	(
	.*,
	.freq_o				( freq )
	);

...

Posted: 12/8/2024 11:14:46 AM
dewster

From: Northern NJ, USA

Joined: 2/17/2012

"Why not starting directly at FREQ_NOM?"  - Zhuoran

Ah, excellent idea, thank you!  New FPGA load posted: https://d-lev.com/source/d-lev_fpga_source_2024-12-08.zip

Code:
--------------------
- Revision History -
--------------------
v13.14 - 2024-12-07 - Q20_v0.9.4a - a branch from v13.12
- Now pass FREQ_NOM (as FREQ_INIT) from lc_dpll.sv to pdi_zq.sv (to avoid 3rd harmonic lock @ boot w/ freq_o init = 0).
- Reduced PV_RANGE_W: 3=>2 (in hive_pkg.sv).
- New informational lc_dpll.sv parameter: LC_FMIN.

I was seeing ~300ms from power-up to DPLL lock, now it's pretty much instantaneous.  Took this opportunity to shrink the lock range too.  Finding a seed that closes timing for the 180MHz clock is fairly laborious process ever since Altera removed the design space explorer from Quartus (grr).

You must be logged in to post a reply. Please log in or register for a new account.