/**** * audio_buffer.sv - holds a 2KiB audio buffer of 16-bit pcm audio * samples (with pcm being the audio format we are using) * * @author: Waylon Cude, Dilanthi * @date: 6/12/2025 * * */ `ifdef VERILATOR `include "sdvd_defs.sv" `endif import sdvd_defs::SPEED; //this interfaces with block ram module audio_buffer( // The clock should be at 48khz input logic clk, reset, // Control signals input logic play, stop, input SPEED speed, // Whether the audio buffer is currently playing output logic playing, // A 16-bit audio sample to output output logic [15:0] sample, // Inputs for the memory buffer audio_buffer_interface.receiver driver ); // Size of samples, 8bit or 16bit supported parameter SIZE=16; (* MARK_DEBUG = "TRUE" *) logic [11-(SIZE/8):0] address; // State register logic enb; logic [(SIZE-1):0] doutb; // A single bit counter, to avoid feeding samples given the 1 cycle read delay logic delay; // Whether the current address being read from is in the upper or lower // half of the 2KiB buffer // // The MSB of the address == higher/lower half address assign driver.address_half = address[11-(SIZE/8)]; always_ff @(posedge clk) begin enb <= 0; if (reset) begin playing <= 0; address <= 0; sample <= 0; delay <= '1; end else if (!playing) begin if (play) begin playing <= 1; delay <= '1; end end else begin if (stop) begin playing <= 0; // If playback "Stops" then reset the address to a known value, 0 address <= 0; delay <= '1; end else begin // This will overflow to the correct value always address <= address + speed; enb <= 1; // NOTE: I really don't know a good way to generate the load // signal. It maybe could be an inverted 48khz clock? if (delay == 0) begin sample[15-:SIZE] <= doutb; end else begin sample <= '0; delay <= '0; end end end end //xpm_memory_sdpram #( // .WRITE_DATA_WIDTH_A(16) //) buffer (); xpm_memory_sdpram #( .ADDR_WIDTH_A(11), // DECIMAL .ADDR_WIDTH_B(12-(SIZE/8)), // DECIMAL .AUTO_SLEEP_TIME(0), // DECIMAL .BYTE_WRITE_WIDTH_A(8), // DECIMAL .CASCADE_HEIGHT(0), // DECIMAL .CLOCKING_MODE("independent_clock"), // String .ECC_BIT_RANGE("7:0"), // String .ECC_MODE("no_ecc"), // String .ECC_TYPE("none"), // String .IGNORE_INIT_SYNTH(0), // DECIMAL .MEMORY_INIT_FILE("none"), // String .MEMORY_INIT_PARAM("0"), // String .MEMORY_OPTIMIZATION("true"), // String .MEMORY_PRIMITIVE("auto"), // String .MEMORY_SIZE(16*1024), // DECIMAL .MESSAGE_CONTROL(0), // DECIMAL .RAM_DECOMP("auto"), // String .READ_DATA_WIDTH_B(SIZE), // DECIMAL .READ_LATENCY_B(1), // DECIMAL .READ_RESET_VALUE_B("0"), // String .RST_MODE_A("SYNC"), // String .RST_MODE_B("SYNC"), // String .SIM_ASSERT_CHK(0), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages .USE_EMBEDDED_CONSTRAINT(0), // DECIMAL .USE_MEM_INIT(1), // DECIMAL .USE_MEM_INIT_MMI(0), // DECIMAL .WAKEUP_TIME("disable_sleep"), // String .WRITE_DATA_WIDTH_A(8), // DECIMAL .WRITE_MODE_B("no_change"), // String .WRITE_PROTECT(1) // DECIMAL ) buffer ( .doutb(doutb), // READ_DATA_WIDTH_B-bit output: Data output for port B read operations. .addra(driver.addra), // ADDR_WIDTH_A-bit input: Address for port A write operations. .addrb(address), // ADDR_WIDTH_B-bit input: Address for port B read operations. .clka(driver.clka), // 1-bit input: Clock signal for port A. Also clocks port B when // parameter CLOCKING_MODE is "common_clock". .clkb(clk), // 1-bit input: Clock signal for port B when parameter CLOCKING_MODE is // "independent_clock". Unused when parameter CLOCKING_MODE is // "common_clock". .dina(driver.dina), // WRITE_DATA_WIDTH_A-bit input: Data input for port A write operations. .ena(driver.ena), // 1-bit input: Memory enable signal for port A. Must be high on clock // cycles when write operations are initiated. Pipelined internally. .enb(enb), // 1-bit input: Memory enable signal for port B. Must be high on clock // cycles when read operations are initiated. Pipelined internally. //active high reset .rstb(reset), // 1-bit input: Reset signal for the final port B output register stage. // Synchronously resets output port doutb to the value specified by // parameter READ_RESET_VALUE_B. .wea(driver.ena), // WRITE_DATA_WIDTH_A/BYTE_WRITE_WIDTH_A-bit input: Write enable vector // for port A input data port dina. 1 bit wide when word-wide writes are // used. In byte-wide write configurations, each bit controls the // writing one byte of dina to address addra. For example, to // synchronously write only bits [15-8] of dina when WRITE_DATA_WIDTH_A // is 32, wea would be 4'b0010. // Extra inputs that I guess I need .sleep(0), .injectsbiterra(0), .injectdbiterra(0), // With a latency of 1 this surely does not matter .regceb(enb) ); endmodule