From a2432a5e555d7dabf401ffbdc87117cbd3b1fe28 Mon Sep 17 00:00:00 2001 From: Niels Moseley Date: Thu, 26 Oct 2017 16:39:09 +0200 Subject: [PATCH] Added 2nd order sigma-delta DAC. Added changes suggested by Clifford Wolf. --- boards/Digilent DE0/Speech256_DE0.qsf | 21 ++-- boards/Digilent DE0/speech256_de0.v | 47 ++++++++- verilog/controller/controller.v | 6 +- verilog/filter/filter.v | 3 +- verilog/sd2dac/run_tb.bat | 6 ++ verilog/sd2dac/sd2dac.v | 132 ++++++++++++++++++++++++++ verilog/sd2dac/sd2dac_tb.v | 59 ++++++++++++ verilog/sd2dac/show_tb_results.m | 19 ++++ verilog/speech256_top/speech256_top.v | 15 ++- 9 files changed, 290 insertions(+), 18 deletions(-) create mode 100644 verilog/sd2dac/run_tb.bat create mode 100644 verilog/sd2dac/sd2dac.v create mode 100644 verilog/sd2dac/sd2dac_tb.v create mode 100644 verilog/sd2dac/show_tb_results.m diff --git a/boards/Digilent DE0/Speech256_DE0.qsf b/boards/Digilent DE0/Speech256_DE0.qsf index 2848622..c7c8261 100644 --- a/boards/Digilent DE0/Speech256_DE0.qsf +++ b/boards/Digilent DE0/Speech256_DE0.qsf @@ -75,16 +75,6 @@ set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SW[3] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SW[4] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to SW[5] set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to UART_TXD -set_global_assignment -name PIN_FILE Speech256_de0.pin -set_global_assignment -name VERILOG_FILE speech256_de0.v -set_global_assignment -name VERILOG_FILE ../../verilog/controller/xlat.v -set_global_assignment -name VERILOG_FILE ../../verilog/controller/ctrlrom.v -set_global_assignment -name VERILOG_FILE ../../verilog/controller/controller.v -set_global_assignment -name VERILOG_FILE ../../verilog/filter/filter.v -set_global_assignment -name VERILOG_FILE ../../verilog/pwmdac/pwmdac.v -set_global_assignment -name VERILOG_FILE ../../verilog/source/source.v -set_global_assignment -name VERILOG_FILE ../../verilog/spmul/spmul.v -set_global_assignment -name VERILOG_FILE ../../verilog/speech256_top/speech256_top.v set_location_assignment PIN_B1 -to LEDG[9] set_location_assignment PIN_B2 -to LEDG[8] set_location_assignment PIN_C2 -to LEDG[7] @@ -337,4 +327,15 @@ set_location_assignment PIN_C3 -to DRAM_ADDR[3] set_location_assignment PIN_B3 -to DRAM_ADDR[2] set_location_assignment PIN_A3 -to DRAM_ADDR[1] set_location_assignment PIN_C4 -to DRAM_ADDR[0] +set_global_assignment -name PIN_FILE Speech256_de0.pin +set_global_assignment -name VERILOG_FILE speech256_de0.v +set_global_assignment -name VERILOG_FILE ../../verilog/controller/xlat.v +set_global_assignment -name VERILOG_FILE ../../verilog/controller/ctrlrom.v +set_global_assignment -name VERILOG_FILE ../../verilog/controller/controller.v +set_global_assignment -name VERILOG_FILE ../../verilog/filter/filter.v +set_global_assignment -name VERILOG_FILE ../../verilog/pwmdac/pwmdac.v +set_global_assignment -name VERILOG_FILE ../../verilog/source/source.v +set_global_assignment -name VERILOG_FILE ../../verilog/spmul/spmul.v +set_global_assignment -name VERILOG_FILE ../../verilog/sd2dac/sd2dac.v +set_global_assignment -name VERILOG_FILE ../../verilog/speech256_top/speech256_top.v set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top \ No newline at end of file diff --git a/boards/Digilent DE0/speech256_de0.v b/boards/Digilent DE0/speech256_de0.v index 2726953..8d56eba 100644 --- a/boards/Digilent DE0/speech256_de0.v +++ b/boards/Digilent DE0/speech256_de0.v @@ -5,6 +5,8 @@ // http://www.moseleyinstruments.com // +`define USE_SDDAC + module Speech256_DE0 ( CLOCK_50, SW, @@ -25,6 +27,10 @@ module Speech256_DE0 ( reg [3:0] divcnt; // clock divider counter reg [2:0] cur_state, next_state; + reg [2:0] rom_addr; + reg [5:0] rom_data; + reg inc_rom_addr; + // debug signals for 16-bit DAC wire sample_stb; wire signed [15:0] sample_out; @@ -36,7 +42,7 @@ module Speech256_DE0 ( .clk (clk), .rst_an (rst_an), .ldq (ldq), - .data_in (SW), + .data_in (rom_data), .data_stb (data_stb), .pwm_out (UART_TXD), .sample_out (sample_out), @@ -67,6 +73,8 @@ module Speech256_DE0 ( always @(posedge clk) begin cur_state <= next_state; + if (inc_rom_addr == 1) + rom_addr <= rom_addr + 1; end //assign LEDG[9:0] = sample_out[15:6]; @@ -74,18 +82,23 @@ module Speech256_DE0 ( assign LEDG[1] = ldq; assign LEDG[2] = SW[2]; assign LEDG[3] = SW[3]; + assign LEDG[4] = 0; always @(*) begin // FSM defaults data_stb <= 0; + inc_rom_addr <= 0; next_state <= cur_state; case(cur_state) S_IDLE: begin if ((ldq == 1) && (BUTTON[0] == 1)) + begin + inc_rom_addr <= 1; next_state <= S_ALLOPHONE; + end else next_state <= S_IDLE; end @@ -97,13 +110,45 @@ module Speech256_DE0 ( S_WAITDONE: begin if (ldq == 0) + begin + inc_rom_addr <= 1; next_state <= S_IDLE; + end end default: begin next_state <= S_IDLE; end endcase + + // allophone ROM + // hello, world + case (rom_addr) + 3'd0: + rom_data <= 6'h1B; + 3'd1: + rom_data <= 6'h07; + 3'd2: + rom_data <= 6'h2D; + 3'd3: + rom_data <= 6'h35; + 3'd4: + rom_data <= 6'h03; + 3'd5: + rom_data <= 6'h2E; + 3'd6: + rom_data <= 6'h1E; + 3'd7: + rom_data <= 6'h33; + 3'd8: + rom_data <= 6'h2D; + 3'd9: + rom_data <= 6'h15; + 3'd10: + rom_data <= 6'h03; + default: + rom_data <= 6'h00; + endcase end endmodule diff --git a/verilog/controller/controller.v b/verilog/controller/controller.v index fb616a0..f6b7b9c 100644 --- a/verilog/controller/controller.v +++ b/verilog/controller/controller.v @@ -89,13 +89,13 @@ module CONTROLLER ( .en (rom_en) ); - parameter ROM_ADDR_ZERO = 3'b000, // zero the ROM address + localparam ROM_ADDR_ZERO = 3'b000, // zero the ROM address ROM_ADDR_INC = 3'b001, // increment the ROM address ROM_ADDR_JMP = 3'b010, // jump to code ROM_ADDR_ALLO = 3'b011, // read allophone jump address ROM_ADDR_NOP = 3'b100; - parameter COEFF_CNT_ZERO = 2'b00, // zero coefficient counter + localparam COEFF_CNT_ZERO = 2'b00, // zero coefficient counter COEFF_CNT_NOP = 2'b01, // do nothing COEFF_CNT_INC = 2'b10; // increment coefficient counter @@ -192,7 +192,7 @@ module CONTROLLER ( assign serve_next_allo_data = (dur_cnt == duration) ? 1 : 0; - parameter S_IDLE = 4'd0, + localparam S_IDLE = 4'd0, S_JMPADDR1 = 4'd1, S_JMPADDR2 = 4'd2, S_JMPADDR3 = 4'd3, diff --git a/verilog/filter/filter.v b/verilog/filter/filter.v index 21bfcd3..8dced80 100644 --- a/verilog/filter/filter.v +++ b/verilog/filter/filter.v @@ -20,6 +20,7 @@ module FILTER ( start, // trigger processing of the input signal done // goes to '1' when sig_out has valid data ); + parameter DEBUG = 0; //defult value //////////// CLOCK ////////// @@ -179,7 +180,7 @@ module FILTER ( end // FSM states - parameter S_IDLE = 4'b0000, + localparam S_IDLE = 4'b0000, S_DUMMY1 = 4'b0001, S_WAITMUL1 = 4'b0010, S_UPDATEC1 = 4'b0011, diff --git a/verilog/sd2dac/run_tb.bat b/verilog/sd2dac/run_tb.bat new file mode 100644 index 0000000..4b32ced --- /dev/null +++ b/verilog/sd2dac/run_tb.bat @@ -0,0 +1,6 @@ +mkdir bin +del bin\sd2dac.vvp +C:\iverilog\bin\iverilog -o bin\sd2dac.vvp -m va_math -g2005 -s SD2DAC_TB sd2dac.v sd2dac_tb.v +cd bin +C:\iverilog\bin\vvp sd2dac.vvp +cd .. diff --git a/verilog/sd2dac/sd2dac.v b/verilog/sd2dac/sd2dac.v new file mode 100644 index 0000000..53da5b0 --- /dev/null +++ b/verilog/sd2dac/sd2dac.v @@ -0,0 +1,132 @@ +// +// Second-order sigma-delta DAC +// The DAC has a pull interface. +// +// Number of input bits used: 12 +// +// Niels Moseley - Moseley Instruments 2017 +// http://www.moseleyinstruments.com +// +// +// Designed for a clock rate of 2.5 MHz +// + +module SD2DAC ( + clk, + rst_an, + din, // 16 bit signed data input + din_ack, // is high for 1 clock cycle after reading the din signal + dacout // 1-bit SD output signal + ); + + + input signed [15:0] din; + input rst_an, clk; + output reg din_ack; + output reg dacout; + + reg [15:0] din_reg; // data input register + reg [15:0] last_din; // previous input sample + //reg [15:0] delta; + + reg [7:0] counter; // sample counter + reg signed [15:0] state1, state2; // integrator states + reg signed [15:0] new_state1, new_state2; + reg signed [15:0] state1_in, state2_in; // input to integrators + wire signed [15:0] state1_a, state2_a; // output of integrator adders + wire signed [16:0] quant_in; + + reg quant_out; + + always @(posedge clk or negedge rst_an) + begin + if (rst_an == 0) + begin + state1 <= 0; + state2 <= 0; + counter <= 0; + din_reg <= 0; + end + else + begin + // clocked process + state1 <= new_state1; + state2 <= new_state2; + dacout <= quant_out; + counter <= counter + 1; + if (din_ack == 1) + begin + last_din <= din_reg; + din_reg <= {din[15], din[15:1]}; // div by 2! + end + end + end + + assign state1_a = state1 + state1_in; + assign state2_a = state2 + state2_in; + assign quant_in = $signed( { {3{din_reg[15]}}, din_reg[15:2]} ) + state2; + + always @(*) + begin + // ------------------------------ + // calculate new state 1 + // ------------------------------ + + if (quant_out == 1) + // (din >> 2) - (quant_out >> 2) + state1_in <= $signed( { {2{din_reg[15]}}, din_reg[15:2]} ) - $signed(16'h1FFF); + else + state1_in <= $signed( { {2{din_reg[15]}}, din_reg[15:2]} ) + $signed(16'h1FFF); + + // check for saturation: + // if operand sign bits are the same + // the result should have the same + // sign bit, if not, we need to + // saturate. + // + // 1000 + 1111 => 1000 (-8 + -1 saturates at -8) + // 0111 + 0001 => 0111 (7 + 1 saturates at 7) + // + if (state1[15] == state1_in[15]) + begin + if (state1[15] != state1_a[15]) + new_state1 <= state1[15] ? 16'h8000 : 16'h7FFF; + else + new_state1 <= state1_a; + end + else + new_state1 <= state1_a; + + // ------------------------------ + // calculate new state 2 + // ------------------------------ + + if (quant_out == 1) + // state1 - (quant_out >> 1) + state2_in <= state1 - $signed(16'h3FFF); + else + state2_in <= state1 + $signed(16'h3FFF); + + if (state2[15] == state2_in[15]) + begin + if (state2[15] != state2_a[15]) + new_state2 <= state2[15] ? 16'h8000 : 16'h7FFF; + else + new_state2 <= state2_a; + end + else + new_state2 <= state2_a; + + // ------------------------------ + // calculate quantizer + // ------------------------------ + + quant_out <= !quant_in[16]; + + if (counter == 8'h0) + din_ack <= 1; + else + din_ack <= 0; + end + +endmodule \ No newline at end of file diff --git a/verilog/sd2dac/sd2dac_tb.v b/verilog/sd2dac/sd2dac_tb.v new file mode 100644 index 0000000..07e5eca --- /dev/null +++ b/verilog/sd2dac/sd2dac_tb.v @@ -0,0 +1,59 @@ +// +// PWMDAC testbench +// +// Niels Moseley - Moseley Instruments 2017 +// http://www.moseleyinstruments.com +// + +module SD2DAC_TB; + reg clk, rst_an; + reg signed [15:0] din; + wire dacout, din_ack; + + real accu; + + SD2DAC u_sd2dac ( + .clk (clk), + .rst_an (rst_an), + .din (din), + .din_ack (din_ack), + .dacout (dacout) + ); + + integer fd; + + initial + begin + fd = $fopen("dacout.sw","wb"); + $dumpfile ("sd2dac.vcd"); + $dumpvars; + clk = 0; + rst_an = 0; + din = 0; + accu = 0; + #3 + rst_an = 1; + #1048576 + $fclose(fd); + $finish; + end + + always @(posedge clk) + begin + if (din_ack) + begin + accu = accu + 1.0/256.0; + if (accu > 1.0) + accu = -1.0; + din = $rtoi($sin(2.0*3.1415927*accu)*10000.0); + end + if (dacout == 1) + $fwrite(fd,"%u", 32'h7000_0000); + else + $fwrite(fd,"%u", 32'h9000_0000); + end + + always + #5 clk = !clk; + +endmodule \ No newline at end of file diff --git a/verilog/sd2dac/show_tb_results.m b/verilog/sd2dac/show_tb_results.m new file mode 100644 index 0000000..4b88d18 --- /dev/null +++ b/verilog/sd2dac/show_tb_results.m @@ -0,0 +1,19 @@ +% +% Show testbench results for sd2dac +% +% This is a MATLAB file +% + +fileID = fopen('bin/dacout.sw'); +A = fread(fileID,'*int32')'; +fclose(fileID); + +A = single(A)/2^31; + +clf; +figure(1); +X = fft(A.*blackman(length(A))') / (length(A)/4); +semilogx(20*log10(abs(X))); +grid on; +axis([0 length(X)/2 -140 10]); + diff --git a/verilog/speech256_top/speech256_top.v b/verilog/speech256_top/speech256_top.v index c2127dc..f7801cf 100644 --- a/verilog/speech256_top/speech256_top.v +++ b/verilog/speech256_top/speech256_top.v @@ -72,14 +72,23 @@ module SPEECH256_TOP ( .done (src_strobe) ); - PWMDAC u_pwmdac ( + `ifdef USE_SDDAC + SD2DAC u_sd2dac ( .clk (clk), .rst_an (rst_an), - //.din (sig_filter[15:8]), - .din (sig_filter[11:4]), // add +24dB gain .. FIXME: add saturation ?? + .din ($signed({sig_filter[11:0],4'h0})), // add +24dB gain .. FIXME: add saturation ?? .din_ack (pwmdac_ack), .dacout (pwm_out) ); + `else + PWMDAC u_pwmdac ( + .clk (clk), + .rst_an (rst_an), + .din (sig_filter[11:4]), // add +24dB gain .. FIXME: add saturation ?? + .din_ack (pwmdac_ack), + .dacout (pwm_out) + ); + `endif CONTROLLER u_controller ( .clk (clk),