[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