`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; reg direct_amp_enable; //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}}, amp_phase, channel_enable}; assign amp_val = amp_out; assign running = channel_enable; wire ctl_reg_write = wr_en && (reg_sel == TDRAU_REG_CTL); /* channel enable flag */ always @(posedge clk) begin if(reset) channel_enable <= 0; else if (ctl_reg_write) 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 (ctl_reg_write) noise_enable <= wr_data[1]; end /* noise generator (Linear Feedback Shift Register) */ always @(posedge clk) begin if (reset) lfsr <= LFSR_INIT; else if (ctl_reg_write && 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; /* direct amplitude enable flag */ always @(posedge clk) begin if(reset) direct_amp_enable <= 0; else if (ctl_reg_write) direct_amp_enable <= wr_data[2]; end /* 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 <= direct_amp_enable ? amp_start : 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 (ctl_reg_write && 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