I think I did some funny math errors so new goal is 8-bit pcm
This commit is contained in:
@@ -31,11 +31,6 @@ module audio_buffer(
|
||||
// Inputs for the memory buffer
|
||||
audio_buffer_interface.receiver driver
|
||||
);
|
||||
|
||||
// Whether the current address being read from is in the upper or lower
|
||||
// half of the 2KiB buffer
|
||||
let address_half = driver.address_half;
|
||||
|
||||
logic [9:0] address;
|
||||
|
||||
// State register
|
||||
@@ -45,8 +40,11 @@ logic [15: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 address_half = address[9];
|
||||
assign driver.address_half = address[9];
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
enb <= 0;
|
||||
@@ -143,12 +141,18 @@ buffer (
|
||||
.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
|
||||
.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
|
||||
|
||||
+6
-2
@@ -16,8 +16,11 @@ module pwm(
|
||||
// The audio output pin
|
||||
output wire pwm_pin
|
||||
);
|
||||
// This can't be 16 or we are slowing the audio rate down by a factor of
|
||||
// 2^5=32
|
||||
parameter DEPTH=8;
|
||||
|
||||
logic [15:0] pulse_counter;
|
||||
logic [DEPTH-1:0] pulse_counter;
|
||||
logic [15:0] sample_buffer;
|
||||
logic should_output;
|
||||
|
||||
@@ -27,6 +30,7 @@ begin
|
||||
begin
|
||||
pulse_counter <= 0;
|
||||
sample_buffer <= 0;
|
||||
should_output <= 0;
|
||||
end
|
||||
|
||||
else
|
||||
@@ -36,7 +40,7 @@ begin
|
||||
|
||||
pulse_counter <= pulse_counter + 1;
|
||||
|
||||
if (pulse_counter < sample_buffer)
|
||||
if (pulse_counter < sample_buffer[15-:DEPTH])
|
||||
should_output <= 1;
|
||||
else
|
||||
should_output <= 0;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
module modular_clock_gen(
|
||||
input clk, reset,
|
||||
output oclk
|
||||
);
|
||||
parameter DIVISOR;
|
||||
|
||||
logic [$clog2(DIVISOR)-1:0] counter;
|
||||
|
||||
// clock will be high for about half of the cycle, depending on integer
|
||||
// rounding
|
||||
assign oclk = counter < (DIVISOR/2);
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (reset)
|
||||
counter <= DIVISOR-1;
|
||||
else if (counter == 0)
|
||||
counter <= DIVISOR-1;
|
||||
else
|
||||
counter <= counter - 1;
|
||||
end
|
||||
|
||||
endmodule
|
||||
+32
-3
@@ -12,8 +12,10 @@ import sdvd_defs::SPEED;
|
||||
module nexys_a7_top(
|
||||
input logic CLK100MHZ, CPU_RESETN,
|
||||
input logic BTNC, BTNR,
|
||||
output logic AUD_PWM, AUD_SD,
|
||||
output logic CA,CB,CC,CD,CE,CF,CG,
|
||||
output logic [7:0] AN
|
||||
output logic [7:0] AN,
|
||||
output wire LED[0:0]
|
||||
);
|
||||
|
||||
// Active high reset
|
||||
@@ -21,10 +23,13 @@ wire reset;
|
||||
assign reset = ~CPU_RESETN;
|
||||
|
||||
logic clk_1khz, clk_10hz;
|
||||
logic clk_48khz, clk_1mhz;
|
||||
logic seconds_pulse;
|
||||
|
||||
SPEED speed;
|
||||
|
||||
audio_buffer_interface audio_interface();
|
||||
|
||||
// Map C{A-G} to an array of 7-segment displays
|
||||
wire [6:0] segments [7:0];
|
||||
wire [2:0] segment_mux_select;
|
||||
@@ -32,18 +37,31 @@ wire [2:0] segment_mux_select;
|
||||
// These segments are currently unused
|
||||
assign segments[7] = 0;
|
||||
assign segments[6] = 0;
|
||||
// These are the ones we're using
|
||||
assign {CA,CB,CC,CD,CE,CF,CG} = ~segments[segment_mux_select];
|
||||
|
||||
logic [$clog2(60)-1:0] seconds;
|
||||
logic [$clog2(60)-1:0] minutes;
|
||||
logic [$clog2(60)-1:0] hours;
|
||||
|
||||
logic [15:0] audio_sample;
|
||||
logic sd_ready;
|
||||
logic playing;
|
||||
|
||||
assign LED[0] = playing;
|
||||
assign AUD_SD = playing;
|
||||
|
||||
low_freq_clock_gen clockGen(CLK100MHZ, reset, speed, clk_1khz, clk_10hz, seconds_pulse);
|
||||
|
||||
// Create a clock with a divisor of 2083, making ~48khz
|
||||
modular_clock_gen #(2083) audioClock(CLK100MHZ, reset, clk_48khz);
|
||||
modular_clock_gen #(100) pwmClock(CLK100MHZ, reset, clk_1mhz);
|
||||
|
||||
|
||||
// Count the number on seconds, hours, and minutes elapsed
|
||||
// If the speed is faster this will pulse more often than once a second
|
||||
// but will still theoretically be a second of video time
|
||||
always_ff @(posedge seconds_pulse) begin
|
||||
always_ff @(posedge seconds_pulse or posedge reset) begin
|
||||
if (reset) begin
|
||||
seconds <= 0;
|
||||
minutes <= 0;
|
||||
@@ -72,8 +90,19 @@ seconds_display hoursSegment (hours, segments[5], segments[4]);
|
||||
// Gets rid of button bouncing
|
||||
playback_controller playbackController (clk_10hz,reset,BTNC, BTNR, speed);
|
||||
|
||||
pwm audioOutput(CLK100MHZ, reset, clk_48khz, audio_sample, AUD_PWM);
|
||||
audio_buffer audioBuffer(
|
||||
clk_48khz,
|
||||
reset,
|
||||
sd_ready,
|
||||
'0, // stop signal not used right now
|
||||
speed,
|
||||
playing,
|
||||
audio_sample,
|
||||
audio_interface.receiver
|
||||
);
|
||||
|
||||
|
||||
rom_sd romSdPlayer(clk_1mhz,reset,sd_ready,audio_interface.driver);
|
||||
|
||||
|
||||
endmodule
|
||||
|
||||
+52
-23
@@ -10,17 +10,17 @@ module rom_sd(
|
||||
input logic reset,
|
||||
output logic ready,
|
||||
|
||||
buffer_interface.driver buffer
|
||||
audio_buffer_interface.driver buffer
|
||||
);
|
||||
parameter MEM_FILE = "roundabout.mem";
|
||||
|
||||
typedef enum logic [2:0]{
|
||||
RESET, DELAY, WRITEBUF, ENDWRITE, WAIT
|
||||
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 initializing;
|
||||
logic [16:0] rom_addr;
|
||||
logic [7:0] rom_data;
|
||||
logic rom_enable;
|
||||
@@ -40,8 +40,8 @@ xpm_memory_sprom #(
|
||||
.ECC_MODE("no_ecc"), // String
|
||||
.ECC_TYPE("none"), // String
|
||||
.IGNORE_INIT_SYNTH(0), // DECIMAL
|
||||
.MEMORY_INIT_FILE("roundabout.mem"), // String
|
||||
.MEMORY_INIT_PARAM("0"), // String
|
||||
.MEMORY_INIT_FILE(MEM_FILE), // String
|
||||
.MEMORY_INIT_PARAM(""), // String
|
||||
.MEMORY_OPTIMIZATION("true"), // String
|
||||
.MEMORY_PRIMITIVE("auto"), // String
|
||||
.MEMORY_SIZE(131072*8), // DECIMAL
|
||||
@@ -63,46 +63,67 @@ xpm_memory_sprom_inst (
|
||||
.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.
|
||||
.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 = DELAY;
|
||||
else next = DELAY1;
|
||||
DELAY1: next = DELAY2;
|
||||
DELAY2: next = DELAY3;
|
||||
DELAY3: next = WRITEBUF;
|
||||
|
||||
DELAY: next = WRITEBUF;
|
||||
WRITEBUF: if (buffer.addra[9:0] < 1020) next = WRITEBUF;
|
||||
else next = ENDWRITE1;
|
||||
|
||||
WRITEBUF: if (buffer.addra < 1023) next = WRITEBUF;
|
||||
else if (buffer.addra == 1023) next = ENDWRITE;
|
||||
|
||||
ENDWRITE: next = WAIT;
|
||||
ENDWRITE1: next = ENDWRITE2;
|
||||
ENDWRITE2: next = ENDWRITE3;
|
||||
ENDWRITE3: next = WAIT;
|
||||
|
||||
WAIT: if (buffer_half == buffer.address_half) next = WAIT;
|
||||
else next = DELAY;
|
||||
else next = DELAY1;
|
||||
|
||||
default: next = RESET;
|
||||
endcase
|
||||
end
|
||||
|
||||
//sequential output logic
|
||||
always_ff @(posedge clock)
|
||||
always_ff @(posedge clk)
|
||||
begin
|
||||
case (current)
|
||||
RESET: begin
|
||||
buffer_half <= 0;
|
||||
rom_addr <= 0;
|
||||
rom_enable <= 1;
|
||||
buffer.addra <= 0;
|
||||
buffer.ena <= 0;
|
||||
ready <= 0;
|
||||
end
|
||||
|
||||
DELAY: rom_addr <= rom_addr + 1;
|
||||
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;
|
||||
@@ -110,29 +131,37 @@ begin
|
||||
buffer.addra <= buffer.addra + 1;
|
||||
rom_addr <= rom_addr + 1;
|
||||
end
|
||||
|
||||
ENDWRITE: begin
|
||||
|
||||
ENDWRITE1, ENDWRITE2:
|
||||
begin
|
||||
buffer.ena <= 1;
|
||||
buffer.data <= rom_data;
|
||||
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: buffer.ena <= 0;
|
||||
WAIT: ;
|
||||
|
||||
default: begin
|
||||
buffer_half <= 0;
|
||||
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 clock)
|
||||
always_ff @(posedge clk)
|
||||
begin
|
||||
if (reset)
|
||||
current <= RESET;
|
||||
|
||||
Reference in New Issue
Block a user