SDVD/design/audio/audio_buffer.sv
Waylon Cude f840d27b8e
All checks were successful
ci/woodpecker/push/test-workflow Pipeline was successful
Demo commit
The audio output is still messed up, but this commit gets everything as
ready as it can get. Fixed up all the testbenches and added state
machines for everything
2025-06-10 13:26:35 -07:00

162 lines
5.7 KiB
Systemverilog

/****
* 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