`timescale 1ns / 1ps // waveform generator module (PCM) module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22, AMP_WIDTH=16, AMP_BIAS=32768) ( 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, output wire irq ); localparam TDRAU_REG_CTL = 0; /* control register */ localparam TDRAU_REG_CLK = 1; /* clock divider register */ localparam TDRAU_REG_AMP = 2; /* amplitude (volume) register */ 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_out; wire fifo_wr_en; wire fifo_rd_en, fifo_full, fifo_empty; wire [DATA_WIDTH-1:0] fifo_rd_data; fifo #(.ADDR_WIDTH(4), .DATA_WIDTH(16)) sample_buf( clk, reset, fifo_wr_en, fifo_rd_en, wr_data, fifo_rd_data, fifo_full, fifo_empty ); assign fifo_rd_en = (div_count == 0) && channel_enable && ~fifo_empty; assign fifo_wr_en = wr_en && (reg_sel == TDRAU_REG_AMP); reg irq_buf, irq_done; assign irq = irq_buf; reg [DATA_WIDTH-1:0] rd_data_buf; assign rd_data = rd_data_buf; // assign rd_data = {{DATA_WIDTH-8{1'b0}}, {4{1'b0}}, fifo_full, fifo_empty, amp_phase, channel_enable}; assign amp_val = amp_out; assign running = channel_enable; wire ctl_reg_write = wr_en && (reg_sel == TDRAU_REG_CTL); /* update read data buffer */ always @(posedge clk) begin rd_data_buf <= {{DATA_WIDTH-8{1'b0}}, {4{1'b0}}, fifo_full, fifo_empty, amp_phase, channel_enable}; end /* irq signal to interrupt controller */ always @(posedge clk) begin if(reset) irq_buf <= 0; else if(fifo_empty && ~irq_done) irq_buf <= 1; else irq_buf <= 0; end /* interrupt done flag, used to ensure the irq signal is set for just one clock tick */ always @(posedge clk) begin if(reset) irq_done <= 0; else if(rd_en) // reset irq done flag on any register read irq_done <= 0; else if(irq_buf) irq_done <= 1; end /* 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 /* 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 /* amplitude out */ always @(posedge clk) begin if (reset) begin amp_out <= AMP_BIAS; amp_phase <= 1; end else if (channel_enable) begin if (div_count == 0) // invert amplitude on clock tick begin amp_out <= fifo_rd_data; amp_phase <= ~amp_phase; end end else amp_out <= AMP_BIAS; // 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 irq_out, output wire pdm_out, output wire gain_sel, output wire shutdown_n ); localparam CLOCK_DIV_WIDTH = 22; localparam AMP_WIDTH = 16; localparam AMP_BIAS = 32768; 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_irq; 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_irq; 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_irq; 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_irq; 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, chan0_irq); wavegen chan1(clk, reset, reg_sel, chan1_rd_data, wr_data, chan1_rd_en, chan1_wr_en, chan1_amp, chan1_running, chan1_irq); wavegen chan2(clk, reset, reg_sel, chan2_rd_data, wr_data, chan2_rd_en, chan2_wr_en, chan2_amp, chan2_irq, chan2_running); wavegen chan3(clk, reset, reg_sel, chan3_rd_data, wr_data, chan3_rd_en, chan3_wr_en, chan3_amp, chan3_running, chan3_irq); reg irq_out_buf; assign irq_out = irq_out_buf; 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 assign gain_sel = 1; // gain select: 0 -> 12dB, 1 -> 6dB // assign shutdown_n = running; assign shutdown_n = 1; /* don't enable shutdown mode, it creates a mains hum */ always @(posedge clk) irq_out_buf <= chan0_irq || chan1_irq || chan2_irq || chan3_irq; /* 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; // else // deltasigma_acc <= deltasigma_acc[DAC_WIDTH-1:0] + (4*AMP_BIAS); end /* 1-bit audio output */ assign pdm_out = deltasigma_acc[DAC_WIDTH]; endmodule