247 lines
7.2 KiB
Verilog
247 lines
7.2 KiB
Verilog
`timescale 1ns / 1ps
|
|
|
|
// waveform generator module (only pulse wave for now)
|
|
module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, AMP_WIDTH=16) (
|
|
input wire clk,
|
|
input wire reset,
|
|
input wire [1:0] reg_sel,
|
|
output wire [DATA_WIDTH-1:0] rd_data,
|
|
input wire [DATA_WIDTH-1:0] wr_data,
|
|
input wire rd_en,
|
|
input wire wr_en,
|
|
|
|
output wire [AMP_WIDTH-1:0] amp_val,
|
|
output wire running
|
|
);
|
|
|
|
localparam TDRAU_REG_CTL = 0; /* control register */
|
|
localparam TDRAU_REG_CLK = 1; /* clock divider register */
|
|
localparam TDRAU_REG_AMP = 2; /* amplitude (volume) register */
|
|
|
|
// localparam LFSR_WIDTH = 15;
|
|
// localparam LFSR_TAP_IDX_1 = 3;
|
|
// localparam LFSR_TAP_IDX_2 = 0;
|
|
// localparam LFSR_INIT = 'h7672;
|
|
|
|
// localparam LFSR_WIDTH = 18;
|
|
// localparam LFSR_TAP_IDX_1 = 17;
|
|
// localparam LFSR_TAP_IDX_2 = 10;
|
|
// localparam LFSR_INIT = 'h3CBE6;
|
|
|
|
localparam LFSR_WIDTH = 23;
|
|
localparam LFSR_INIT = 'h1;
|
|
|
|
reg channel_enable;
|
|
reg [CLOCK_DIV_WIDTH-1:0] clock_div;
|
|
reg [CLOCK_DIV_WIDTH-1:0] div_count;
|
|
reg amp_phase;
|
|
reg [AMP_WIDTH-1:0] amp_start;
|
|
reg [AMP_WIDTH-1:0] amp_out;
|
|
|
|
reg noise_enable;
|
|
reg [LFSR_WIDTH-1:0] lfsr;
|
|
wire [AMP_WIDTH-1:0] noise_out;
|
|
|
|
//assign rd_data = {{DATA_WIDTH-8-CLOCK_DIV_WIDTH{1'b0}}, div_count, {7{1'b0}}, channel_enable};
|
|
assign rd_data = {8'b0, amp_start, {6{1'b0}}, noise_enable, channel_enable};
|
|
assign amp_val = amp_out;
|
|
assign running = channel_enable;
|
|
|
|
/* channel enable flag */
|
|
always @(posedge clk)
|
|
begin
|
|
if(reset)
|
|
channel_enable <= 0;
|
|
else if (wr_en && (reg_sel == TDRAU_REG_CTL))
|
|
channel_enable <= wr_data[0];
|
|
end
|
|
|
|
/* clock divider register */
|
|
always @(posedge clk)
|
|
begin
|
|
if(reset)
|
|
clock_div <= 0;
|
|
else
|
|
if (wr_en && (reg_sel == TDRAU_REG_CLK))
|
|
clock_div <= wr_data;
|
|
end
|
|
|
|
/* amplitude register */
|
|
always @(posedge clk)
|
|
begin
|
|
if(reset)
|
|
amp_start <= 0;
|
|
else
|
|
if (wr_en && (reg_sel == TDRAU_REG_AMP))
|
|
amp_start <= wr_data;
|
|
end
|
|
|
|
/* divider counter */
|
|
always @(posedge clk)
|
|
begin
|
|
if(channel_enable)
|
|
begin
|
|
if(div_count == 0) // reset counter if it reaches zero
|
|
div_count <= clock_div;
|
|
else
|
|
div_count <= div_count - 1; // else just decrement it
|
|
end
|
|
else
|
|
if (wr_en && (reg_sel == TDRAU_REG_CLK)) // when setting divider,
|
|
div_count <= 1; // start cycle on next clock tick
|
|
end
|
|
|
|
|
|
/* noise enable flag */
|
|
always @(posedge clk)
|
|
begin
|
|
if(reset)
|
|
noise_enable <= 0;
|
|
else if (wr_en && (reg_sel == TDRAU_REG_CTL))
|
|
noise_enable <= wr_data[1];
|
|
end
|
|
|
|
/* noise generator (Linear Feedback Shift Register) */
|
|
always @(posedge clk)
|
|
begin
|
|
if (reset)
|
|
lfsr <= LFSR_INIT;
|
|
else
|
|
if (wr_en && (reg_sel == TDRAU_REG_CTL) && wr_data[1])
|
|
lfsr <= LFSR_INIT;
|
|
else
|
|
if (channel_enable && noise_enable)
|
|
if (div_count == 0)
|
|
//lfsr <= { lfsr[LFSR_TAP_IDX_1] ^ lfsr[LFSR_TAP_IDX_2], lfsr[LFSR_WIDTH-1:1] };
|
|
// shift width and tap bits taken from https://github.com/jotego/jtopl
|
|
lfsr <= { lfsr[LFSR_WIDTH-2:0], lfsr[22] ^ lfsr[9] ^ lfsr[8] ^ lfsr[0]};
|
|
end
|
|
|
|
assign noise_out = lfsr[0] ? amp_start : ~amp_start;
|
|
|
|
/* amplitude out */
|
|
always @(posedge clk)
|
|
begin
|
|
if (reset)
|
|
begin
|
|
amp_out <= 0;
|
|
amp_phase <= 1;
|
|
end
|
|
else
|
|
if (channel_enable)
|
|
begin
|
|
if (div_count == 0) // invert amplitude on clock tick
|
|
begin
|
|
amp_out <= noise_enable ? noise_out :
|
|
amp_phase ? amp_start : ~amp_start;
|
|
amp_phase <= ~amp_phase;
|
|
end
|
|
end
|
|
else
|
|
amp_out <= 0;
|
|
|
|
// reset phase bit when enabling the channel
|
|
if (wr_en && (reg_sel == TDRAU_REG_CTL) && wr_data[0])
|
|
// when channel is enabled, phase will be flipped on next tick
|
|
// because div_count will become zero
|
|
amp_phase <= 1;
|
|
end
|
|
endmodule
|
|
|
|
module tdraudio #(DATA_WIDTH=32) (
|
|
input wire clk,
|
|
input wire reset,
|
|
input wire [6:0] io_addr,
|
|
output wire [DATA_WIDTH-1:0] rd_data,
|
|
input wire [DATA_WIDTH-1:0] wr_data,
|
|
input wire rd_en,
|
|
input wire wr_en,
|
|
|
|
output wire pdm_out,
|
|
output wire gain_sel,
|
|
output wire shutdown_n
|
|
);
|
|
|
|
localparam CLOCK_DIV_WIDTH = 22;
|
|
localparam AMP_WIDTH = 16;
|
|
localparam DAC_WIDTH = 18;
|
|
|
|
wire [4:0] chan_sel = io_addr[6:2];
|
|
wire [1:0] reg_sel = io_addr[1:0];
|
|
|
|
wire [AMP_WIDTH-1:0] chan0_amp;
|
|
wire [DATA_WIDTH-1:0] chan0_rd_data;
|
|
wire chan0_running;
|
|
wire chan0_sel = chan_sel == 5'd0;
|
|
wire chan0_rd_en = chan0_sel && rd_en;
|
|
wire chan0_wr_en = chan0_sel && wr_en;
|
|
|
|
wire [AMP_WIDTH-1:0] chan1_amp;
|
|
wire [DATA_WIDTH-1:0] chan1_rd_data;
|
|
wire chan1_running;
|
|
wire chan1_sel = chan_sel == 5'd1;
|
|
wire chan1_rd_en = chan1_sel && rd_en;
|
|
wire chan1_wr_en = chan1_sel && wr_en;
|
|
|
|
wire [AMP_WIDTH-1:0] chan2_amp;
|
|
wire [DATA_WIDTH-1:0] chan2_rd_data;
|
|
wire chan2_running;
|
|
wire chan2_sel = chan_sel == 5'd2;
|
|
wire chan2_rd_en = chan2_sel && rd_en;
|
|
wire chan2_wr_en = chan2_sel && wr_en;
|
|
|
|
wire [AMP_WIDTH-1:0] chan3_amp;
|
|
wire [DATA_WIDTH-1:0] chan3_rd_data;
|
|
wire chan3_running;
|
|
wire chan3_sel = chan_sel == 5'd3;
|
|
wire chan3_rd_en = chan3_sel && rd_en;
|
|
wire chan3_wr_en = chan3_sel && wr_en;
|
|
|
|
wire running = chan0_running || chan1_running || chan2_running || chan3_running;
|
|
|
|
assign rd_data = chan0_sel ? chan0_rd_data :
|
|
chan1_sel ? chan1_rd_data :
|
|
chan2_sel ? chan2_rd_data :
|
|
chan3_sel ? chan3_rd_data :
|
|
{DATA_WIDTH{1'b1}};
|
|
|
|
wavegen chan0(clk, reset, reg_sel,
|
|
chan0_rd_data, wr_data,
|
|
chan0_rd_en, chan0_wr_en,
|
|
chan0_amp, chan0_running);
|
|
|
|
wavegen chan1(clk, reset, reg_sel,
|
|
chan1_rd_data, wr_data,
|
|
chan1_rd_en, chan1_wr_en,
|
|
chan1_amp, chan1_running);
|
|
|
|
wavegen chan2(clk, reset, reg_sel,
|
|
chan2_rd_data, wr_data,
|
|
chan2_rd_en, chan2_wr_en,
|
|
chan2_amp, chan2_running);
|
|
|
|
wavegen chan3(clk, reset, reg_sel,
|
|
chan3_rd_data, wr_data,
|
|
chan3_rd_en, chan3_wr_en,
|
|
chan3_amp, chan3_running);
|
|
|
|
reg [DAC_WIDTH:0] deltasigma_acc; // one extra bit
|
|
wire [DAC_WIDTH:0] amp_sum = chan0_amp + chan1_amp + chan2_amp + chan3_amp; // also one overflow bit here
|
|
//wire [AMP_WIDTH-1:0] amp_sum_scaled = amp_sum[DAC_WIDTH-2:2]; // shifted right to scale down
|
|
assign gain_sel = 1; // gain select: 0 -> 12dB, 1 -> 6dB
|
|
|
|
assign shutdown_n = running;
|
|
|
|
/* delta-sigma DAC */
|
|
always @(posedge clk)
|
|
begin
|
|
if(reset)
|
|
deltasigma_acc <= 0;
|
|
else
|
|
if (running)
|
|
deltasigma_acc <= deltasigma_acc[DAC_WIDTH-1:0] + amp_sum;
|
|
end
|
|
|
|
/* 1-bit audio output */
|
|
assign pdm_out = deltasigma_acc[DAC_WIDTH];
|
|
endmodule
|