[Fpga-synth] Delay Line Theory

Eric Brombaugh ebrombaugh at earthlink.net
Tue Aug 14 20:30:13 CEST 2007


Scott Gravenhorst wrote:
> I've looked on the net for examples of delay line implementation using a
> RAM, but there doesn't seem to be anything.  I manufactured my own method
> based on "clock math".  The void of information leads me to one of two
> conclusions - 1) it's as simple as I think it is - or 2) it's IP that
> nobody wants to divulge because it's so cool.

I'd vote for 1).

> This new little project doesn't use an arithmetic wrap algorithm.  It makes
> the length of the delay line the entire RAM address space.  This allows me
> to let the system simply wrap back to zero after the maximum address
> instead of using arithmetic to compute when to wrap to address zero.

That sounds right

> I set up a pointer (ptr) that is the end point (for read) and start point
> (for write) of the delay line.  I also have a pickup offset value (PU) that
> is added to ptr to access the location of the pickup.

Yes.

> In a nutshell, the state machine first accesses and stores in a register
> the pickup using ptr+PU as the RAM address (where binary wrapping will
> happen).  Next, the data pointed to by ptr is accessed to feed the input
> register of the filter.  At the same clock edge, the filter's output is
> written to the location in RAM pointed to by ptr, storing the previous
> cycle's IIR output in the RAM.  Lastly, in it's own state, ptr is
> incremented by one and allowed to simply overflow.

That sounds like it would work, but also seems as though it's a bit 
over-constrained. Using a state machine and doing reads & writes on 
different clock cycles isn't strictly necessary - distributed RAM and 
BRAM both can be read and written in a single cycle, and if you use a 
dual-port BRAM you can write at one address while reading another.

Here's an example of such a function that I've used successfully:

--------------------------------------
module vdelay (clk, reset, ena, dly, x, y);
	parameter dsz = 14;		// Data bitwidth
	parameter lsz = 8;		// A delay bitwidth
	parameter maxdelay = 1<<lsz;	// Memory depth
	
	input clk;		// System Clock (125MHz)
	input reset;		// POR
	input ena;		// clk enable
	input [lsz-1:0] dly;	// delay amount input
	input [dsz-1:0] x;	// input data
	output [dsz-1:0] y;	// output data
	
	reg [dsz-1:0] mem[maxdelay-1:0];	// Delay memory
	reg [lsz-1:0] wptr, next_wptr;		// Input pointer
	reg [lsz-1:0] rptr, next_rptr;		// Output pointer
	reg [dsz-1:0] y;			// output data register

	// re-register delay
	reg [lsz-1:0] adjdly;
	always @(posedge clk)
		adjdly <= dly;
	
	// Sync Write pointer
	always @(posedge clk)
		if(reset)
			wptr <= {lsz{1'b0}};
		else
			wptr <= next_wptr;
	
	// Async Write pointer
	always @(ena or wptr)
		if(ena)
			next_wptr = wptr + 1;
		else
			next_wptr = wptr;
	
	// Write the memory
	always @(posedge clk)
		if(ena)
			mem[wptr] <= x;
	
	// Sync Read pointer
	always @(posedge clk)
		if(ena)
			rptr <= next_rptr;
	
	// Async Read pointer
	always @(wptr or adjdly)
		next_rptr = wptr - adjdly + 2;
	
	// Read the memory
	always @(posedge clk)
		if(ena)
			y <= mem[rptr];
endmodule
--------------------------------------

Note that you can use a dual-port BRAM instead of a register array to 
get better resource usage.

> To my understanding, this should result in a fixed length waveguide that
> resonates when impulsed and where PU should access a location that is a
> constant number of locations away from ptr (given that PU is not changing).
>  The output should then be the same frequency, regardless of the value of
> PU as long as it is constant.  I'd expect doppler _while_ PU is changing,
> but after it's stopped changing the frequency should return to the natural
> resonant frequency.

Wait a second - is ptr + PU the input and the ptr address the only read 
output of the delay line? In that case, the total length of the delay is 
entirely controlled by PU, not by the size of the RAM. That means that 
as you change PU, the total waveguide length changes too, which changes 
its resonant frequency

I'd think that if you shorten the waveguide while it's resonating that 
you essentially are raising the center frequency of a ringing filter. 
Then it would tend to remain resonant at the frequency defined by the 
new delay length.

Also, I don't know about the 'doppler' effect you describe. That might 
happen though if the total delay was constant but you were changing only 
the phase of the read/write points.

> Do I have this right?  Because it surely isn't working like that.  When I
> increase the value of PU, the pitch rises and remains higher even after PU
> stops changing. 

That behavior makes sense to me, based on what I said above.

Eric


More information about the Fpga-synth mailing list