Added 2nd order sigma-delta DAC. Added changes suggested by Clifford Wolf.

This commit is contained in:
Niels Moseley 2017-10-26 16:39:09 +02:00
parent 45afdd3500
commit a2432a5e55
9 changed files with 290 additions and 18 deletions

View File

@ -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[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 SW[5]
set_instance_assignment -name IO_STANDARD "3.3-V LVTTL" -to UART_TXD 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_B1 -to LEDG[9]
set_location_assignment PIN_B2 -to LEDG[8] set_location_assignment PIN_B2 -to LEDG[8]
set_location_assignment PIN_C2 -to LEDG[7] 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_B3 -to DRAM_ADDR[2]
set_location_assignment PIN_A3 -to DRAM_ADDR[1] set_location_assignment PIN_A3 -to DRAM_ADDR[1]
set_location_assignment PIN_C4 -to DRAM_ADDR[0] 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 set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top

View File

@ -5,6 +5,8 @@
// http://www.moseleyinstruments.com // http://www.moseleyinstruments.com
// //
`define USE_SDDAC
module Speech256_DE0 ( module Speech256_DE0 (
CLOCK_50, CLOCK_50,
SW, SW,
@ -25,6 +27,10 @@ module Speech256_DE0 (
reg [3:0] divcnt; // clock divider counter reg [3:0] divcnt; // clock divider counter
reg [2:0] cur_state, next_state; 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 // debug signals for 16-bit DAC
wire sample_stb; wire sample_stb;
wire signed [15:0] sample_out; wire signed [15:0] sample_out;
@ -36,7 +42,7 @@ module Speech256_DE0 (
.clk (clk), .clk (clk),
.rst_an (rst_an), .rst_an (rst_an),
.ldq (ldq), .ldq (ldq),
.data_in (SW), .data_in (rom_data),
.data_stb (data_stb), .data_stb (data_stb),
.pwm_out (UART_TXD), .pwm_out (UART_TXD),
.sample_out (sample_out), .sample_out (sample_out),
@ -67,6 +73,8 @@ module Speech256_DE0 (
always @(posedge clk) always @(posedge clk)
begin begin
cur_state <= next_state; cur_state <= next_state;
if (inc_rom_addr == 1)
rom_addr <= rom_addr + 1;
end end
//assign LEDG[9:0] = sample_out[15:6]; //assign LEDG[9:0] = sample_out[15:6];
@ -74,18 +82,23 @@ module Speech256_DE0 (
assign LEDG[1] = ldq; assign LEDG[1] = ldq;
assign LEDG[2] = SW[2]; assign LEDG[2] = SW[2];
assign LEDG[3] = SW[3]; assign LEDG[3] = SW[3];
assign LEDG[4] = 0;
always @(*) always @(*)
begin begin
// FSM defaults // FSM defaults
data_stb <= 0; data_stb <= 0;
inc_rom_addr <= 0;
next_state <= cur_state; next_state <= cur_state;
case(cur_state) case(cur_state)
S_IDLE: S_IDLE:
begin begin
if ((ldq == 1) && (BUTTON[0] == 1)) if ((ldq == 1) && (BUTTON[0] == 1))
begin
inc_rom_addr <= 1;
next_state <= S_ALLOPHONE; next_state <= S_ALLOPHONE;
end
else else
next_state <= S_IDLE; next_state <= S_IDLE;
end end
@ -97,13 +110,45 @@ module Speech256_DE0 (
S_WAITDONE: S_WAITDONE:
begin begin
if (ldq == 0) if (ldq == 0)
begin
inc_rom_addr <= 1;
next_state <= S_IDLE; next_state <= S_IDLE;
end
end end
default: default:
begin begin
next_state <= S_IDLE; next_state <= S_IDLE;
end end
endcase 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 end
endmodule endmodule

View File

@ -89,13 +89,13 @@ module CONTROLLER (
.en (rom_en) .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_INC = 3'b001, // increment the ROM address
ROM_ADDR_JMP = 3'b010, // jump to code ROM_ADDR_JMP = 3'b010, // jump to code
ROM_ADDR_ALLO = 3'b011, // read allophone jump address ROM_ADDR_ALLO = 3'b011, // read allophone jump address
ROM_ADDR_NOP = 3'b100; 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_NOP = 2'b01, // do nothing
COEFF_CNT_INC = 2'b10; // increment coefficient counter COEFF_CNT_INC = 2'b10; // increment coefficient counter
@ -192,7 +192,7 @@ module CONTROLLER (
assign serve_next_allo_data = (dur_cnt == duration) ? 1 : 0; 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_JMPADDR1 = 4'd1,
S_JMPADDR2 = 4'd2, S_JMPADDR2 = 4'd2,
S_JMPADDR3 = 4'd3, S_JMPADDR3 = 4'd3,

View File

@ -20,6 +20,7 @@ module FILTER (
start, // trigger processing of the input signal start, // trigger processing of the input signal
done // goes to '1' when sig_out has valid data done // goes to '1' when sig_out has valid data
); );
parameter DEBUG = 0; //defult value parameter DEBUG = 0; //defult value
//////////// CLOCK ////////// //////////// CLOCK //////////
@ -179,7 +180,7 @@ module FILTER (
end end
// FSM states // FSM states
parameter S_IDLE = 4'b0000, localparam S_IDLE = 4'b0000,
S_DUMMY1 = 4'b0001, S_DUMMY1 = 4'b0001,
S_WAITMUL1 = 4'b0010, S_WAITMUL1 = 4'b0010,
S_UPDATEC1 = 4'b0011, S_UPDATEC1 = 4'b0011,

View File

@ -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 ..

132
verilog/sd2dac/sd2dac.v Normal file
View File

@ -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

View File

@ -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

View File

@ -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]);

View File

@ -72,14 +72,23 @@ module SPEECH256_TOP (
.done (src_strobe) .done (src_strobe)
); );
PWMDAC u_pwmdac ( `ifdef USE_SDDAC
SD2DAC u_sd2dac (
.clk (clk), .clk (clk),
.rst_an (rst_an), .rst_an (rst_an),
//.din (sig_filter[15:8]), .din ($signed({sig_filter[11:0],4'h0})), // add +24dB gain .. FIXME: add saturation ??
.din (sig_filter[11:4]), // add +24dB gain .. FIXME: add saturation ??
.din_ack (pwmdac_ack), .din_ack (pwmdac_ack),
.dacout (pwm_out) .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 ( CONTROLLER u_controller (
.clk (clk), .clk (clk),