All checks were successful
ci/woodpecker/push/test-workflow Pipeline was successful
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
162 lines
5.7 KiB
Systemverilog
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
|