基于Verilog环形存储器实现1秒音频延迟的技术咨询
Fixing Your 1-Second Audio Delay Circular Buffer in Verilog
Hey there! Let's work through getting your circular buffer-based audio delay up and running correctly. I can see the core idea is solid, but there are a few critical adjustments needed for proper functionality.
Key Issues in the Original Code
Let's break down what's missing or misconfigured:
- Unupdated Write Index: Your code never increments
write_index, so you're always writing to the same memory address—this means you're not actually storing a stream of audio samples, just overwriting the first location repeatedly. - Blocking Assignment for Read Index: Using
read_index = read_index + 1;(blocking assignment) in a clocked always block can lead to unexpected timing behavior. In synchronous designs, we should use non-blocking assignments (<=) for all register updates. - Index Out-of-Bounds Risk: The initial
read_indexvalue is20000(binary0100111000100000), but your memory is defined as[0:20000]—that's 20001 locations, while 1 second at 20kHz only needs 20000 samples. This leads to an off-by-one error, and the initial index points to an address outside your intended delay buffer. - No Circular Wraparound: Once
write_indexorread_indexreaches the end of the buffer, they'll keep incrementing beyond the memory range, causing undefined behavior.
Revised Implementation
Here's a fixed version of your code with explanations of each change:
module delay( input [11:0] data_in, input delay_clk, // 20kHz clock output reg [11:0] data_out ); // Define parameters for flexibility parameter DELAY_SAMPLES = 20000; // 1s * 20kHz = 20000 samples parameter DATA_WIDTH = 12; localparam INDEX_WIDTH = $clog2(DELAY_SAMPLES); // Auto-calculate index bit width // Circular buffer: 20000 x 12-bit storage locations reg [DATA_WIDTH-1:0] memory[0:DELAY_SAMPLES-1]; reg [INDEX_WIDTH-1:0] write_index; reg [INDEX_WIDTH-1:0] read_index; initial begin write_index = {INDEX_WIDTH{1'b0}}; // Start read index 20000 samples behind write index. Since we start at 0, // this means read index also starts at 0—after 20000 cycles, read will pull 1s-old data read_index = {INDEX_WIDTH{1'b0}}; end always@(posedge delay_clk) begin // 1. Write incoming sample to current write address memory[write_index] <= data_in; // 2. Read delayed sample from current read address data_out <= memory[read_index]; // 3. Increment indices with circular wraparound write_index <= (write_index == DELAY_SAMPLES - 1) ? {INDEX_WIDTH{1'b0}} : write_index + 1'b1; read_index <= (read_index == DELAY_SAMPLES - 1) ? {INDEX_WIDTH{1'b0}} : read_index + 1'b1; end endmodule
Critical Changes Explained
- Parameterized Design: Using parameters makes it easy to adjust delay length or sample width later without rewriting the whole module.
$clog2automatically calculates the required index bit width (15 bits for 20000 samples, since 2^14=16384 < 20000 < 2^15=32768). - Synchronous Register Updates: All index updates use non-blocking assignments (
<=) to ensure proper sequential behavior on the clock edge, avoiding race conditions. - Circular Wraparound: The ternary operator checks if the index has reached the last memory location—if so, it resets to 0; otherwise, it increments by 1. This prevents invalid memory access.
- Index Alignment: Starting both indices at 0 means the buffer fills for the first 20000 cycles, after which the output will consistently return samples from 1 second prior. If you want silent initial output instead of undefined values, you can initialize all memory locations to 0 (see optional improvement below).
Optional Improvements
- Reset Signal: Add a synchronous or asynchronous reset to initialize indices and memory to known values on startup—standard practice for real-world designs.
- Silent Initialization: To ensure the initial output is 0 instead of undefined, add this to the initial block:
integer i; initial begin for(i=0; i<DELAY_SAMPLES; i=i+1) begin memory[i] = {DATA_WIDTH{1'b0}}; end write_index = 0; read_index = 0; end - Variable Delay: If you want runtime-adjustable delay, replace the fixed
DELAY_SAMPLESparameter with an input signal, and calculate the read index as(write_index - delay_length) % DELAY_SAMPLES(be sure to handle underflow with proper signed arithmetic or offsetting).
内容的提问来源于stack exchange,提问作者prof zhj
相关产品推荐
相关产品推荐

