All checks were successful
ci/woodpecker/push/test-workflow Pipeline was successful
We have something at least, audio is working and sounding dang good
173 lines
5.5 KiB
Systemverilog
173 lines
5.5 KiB
Systemverilog
/****
|
|
* rom_sd.sv - reads audio data off a rom and feeds it to the audio buffer
|
|
*
|
|
* @author: Dilanthi Prentice, Waylon Cude
|
|
* @date: 6/12/25
|
|
*
|
|
* **/
|
|
module rom_sd(
|
|
input logic clk,
|
|
input logic reset,
|
|
output logic ready,
|
|
|
|
audio_buffer_interface.driver buffer
|
|
);
|
|
parameter MEM_FILE = "roundabout.mem";
|
|
|
|
typedef enum logic [3:0]{
|
|
RESET, DELAY1, DELAY2, DELAY3, WRITEBUF, ENDWRITE1, ENDWRITE2, ENDWRITE3, WAIT
|
|
} state_t;
|
|
|
|
state_t current, next;
|
|
// First we write 2048B into the memory buffer, then signal to play it and
|
|
// wait for half signal to avoid overwriting memory
|
|
logic [18:0] rom_addr;
|
|
logic [7:0] rom_data;
|
|
logic rom_enable;
|
|
logic buffer_half;
|
|
|
|
|
|
// xpm_memory_sprom: Single Port ROM
|
|
// Xilinx Parameterized Macro, version 2024.2
|
|
|
|
// The ROM has 17 address bits and 8 data bits to store 128KiB, more than
|
|
// enough for one second of 48khz audio
|
|
xpm_memory_sprom #(
|
|
.ADDR_WIDTH_A(19), // DECIMAL
|
|
.AUTO_SLEEP_TIME(0), // DECIMAL
|
|
.CASCADE_HEIGHT(0), // DECIMAL
|
|
.ECC_BIT_RANGE("7:0"), // String
|
|
.ECC_MODE("no_ecc"), // String
|
|
.ECC_TYPE("none"), // String
|
|
.IGNORE_INIT_SYNTH(0), // DECIMAL
|
|
.MEMORY_INIT_FILE(MEM_FILE), // String
|
|
.MEMORY_INIT_PARAM(""), // String
|
|
.MEMORY_OPTIMIZATION("true"), // String
|
|
.MEMORY_PRIMITIVE("auto"), // String
|
|
.MEMORY_SIZE((1<<19)*8), // DECIMAL
|
|
.MESSAGE_CONTROL(0), // DECIMAL
|
|
.RAM_DECOMP("auto"), // String
|
|
.READ_DATA_WIDTH_A(8), // DECIMAL
|
|
.READ_LATENCY_A(2), // DECIMAL
|
|
.READ_RESET_VALUE_A("0"), // String
|
|
.RST_MODE_A("SYNC"), // String
|
|
.SIM_ASSERT_CHK(0), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages
|
|
.USE_MEM_INIT(1), // DECIMAL
|
|
.USE_MEM_INIT_MMI(0), // DECIMAL
|
|
.WAKEUP_TIME("disable_sleep") // String
|
|
)
|
|
xpm_memory_sprom_inst (
|
|
.douta(rom_data), // READ_DATA_WIDTH_A-bit output: Data output for port A read operations.
|
|
.addra(rom_addr), // ADDR_WIDTH_A-bit input: Address for port A read operations.
|
|
.clka(clk), // 1-bit input: Clock signal for port A.
|
|
.ena(rom_enable), // 1-bit input: Memory enable signal for port A. Must be high on clock
|
|
// cycles when read operations are initiated. Pipelined internally.
|
|
|
|
.rsta(reset), // 1-bit input: Reset signal for the final port A output register stage.
|
|
// Synchronously resets output port douta to the value specified by
|
|
// parameter READ_RESET_VALUE_A.
|
|
|
|
// These are required I think? The ROM gets optimized out without them
|
|
.sleep(0),
|
|
// Should this have a separate control signal? What happens if it gets
|
|
// turned on early like I'm doing now?
|
|
.regcea(rom_enable),
|
|
.injectsbiterra(0),
|
|
.injectdbiterra(0)
|
|
);
|
|
// End of xpm_memory_sprom_inst instantiation
|
|
|
|
assign buffer_half = buffer.addra[10];
|
|
// The audio buffer memory is clocked at the same speed as this module
|
|
assign buffer.clka = clk;
|
|
|
|
//next state logic
|
|
always_comb
|
|
begin
|
|
case (current)
|
|
RESET: if (reset) next = RESET;
|
|
else next = DELAY1;
|
|
DELAY1: next = DELAY2;
|
|
DELAY2: next = DELAY3;
|
|
DELAY3: next = WRITEBUF;
|
|
|
|
WRITEBUF: if (buffer.addra[9:0] < 1020) next = WRITEBUF;
|
|
else next = ENDWRITE1;
|
|
|
|
ENDWRITE1: next = ENDWRITE2;
|
|
ENDWRITE2: next = ENDWRITE3;
|
|
ENDWRITE3: next = WAIT;
|
|
|
|
WAIT: if (buffer_half == buffer.address_half) next = WAIT;
|
|
else next = DELAY1;
|
|
|
|
default: next = RESET;
|
|
endcase
|
|
end
|
|
|
|
//sequential output logic
|
|
always_ff @(posedge clk)
|
|
begin
|
|
case (current)
|
|
RESET: begin
|
|
rom_addr <= 0;
|
|
rom_enable <= 1;
|
|
buffer.addra <= 0;
|
|
buffer.ena <= 0;
|
|
ready <= 0;
|
|
end
|
|
|
|
DELAY1: rom_addr <= rom_addr + 1;
|
|
DELAY2: rom_addr <= rom_addr + 1;
|
|
DELAY3: begin
|
|
rom_addr <= rom_addr + 1;
|
|
buffer.dina <= rom_data;
|
|
buffer.ena <= 1;
|
|
end
|
|
|
|
WRITEBUF: begin
|
|
buffer.ena <= 1;
|
|
buffer.dina <= rom_data;
|
|
buffer.addra <= buffer.addra + 1;
|
|
rom_addr <= rom_addr + 1;
|
|
end
|
|
|
|
ENDWRITE1, ENDWRITE2:
|
|
begin
|
|
buffer.ena <= 1;
|
|
buffer.dina <= rom_data;
|
|
buffer.addra <= buffer.addra + 1;
|
|
end
|
|
|
|
|
|
ENDWRITE3: begin
|
|
buffer.ena <= 0;
|
|
buffer.dina <= rom_data;
|
|
buffer.addra <= buffer.addra + 1;
|
|
ready <= 1;
|
|
end
|
|
|
|
WAIT: ;
|
|
|
|
default: begin
|
|
rom_addr <= 0;
|
|
rom_enable <= 0;
|
|
buffer.addra <= 0;
|
|
buffer.dina <= 0;
|
|
buffer.ena <= 0;
|
|
ready <= 0;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
//sequential clocking block
|
|
always_ff @(posedge clk)
|
|
begin
|
|
if (reset)
|
|
current <= RESET;
|
|
else
|
|
current <= next;
|
|
end
|
|
|
|
endmodule
|