-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Tone generator based on sigma-delta noise shaper
- Loading branch information
1 parent
cfc5177
commit adc1273
Showing
7 changed files
with
321 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,3 +18,4 @@ generic/blinky.fasm | |
generic/blinky.png | ||
generic/blinky.posp | ||
generic/blinky.vm | ||
.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
YOSYS ?= yosys | ||
NEXTPNR ?= nextpnr-gowin | ||
|
||
all: tonegen-tec0117.fs | ||
|
||
unpacked: tonegen-tec0117-unpacked.v | ||
|
||
clean: | ||
rm -f *.json *.fs *-unpacked.v | ||
|
||
.PHONY: all clean | ||
|
||
%-tec0117.fs: %-tec0117.json | ||
gowin_pack -d GW1N-9 -o $@ $< | ||
|
||
%-tec0117.json: %-tec0117-synth.json tec0117.cst | ||
$(NEXTPNR) --json $< --write $@ --device GW1NR-LV9QN88C6/I5 --cst tec0117.cst | ||
|
||
%-tec0117-synth.json: %.v | ||
$(YOSYS) -D LEDS_NR=8 -p "read_verilog $^; synth_gowin -json $@" | ||
|
||
%-tec0117-prog: %-tec0117.fs | ||
openFPGALoader -b tec0117 $^ | ||
|
||
%-tec0117-unpacked.v: %-tec0117.fs | ||
gowin_unpack -d GW1N-9 -o $@ $^ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Sigma-delta based tone generator | ||
|
||
This project generates a noise-shaped bitstream on one digital output pin. | ||
By connecting an RC low-pass filter to this pin, the analogue sinusoidal signal is recovered from the bitstream, making this a simple method to get an analogue output from a digital pin. | ||
|
||
Note: the noise performance of the generator is directly dependent on the digital noise present on the digital pin. For optimal performance, the digital signal should be re-clocked by an external flip-flop (74LV74 or similar). The external flip-flop must have a very clean supply, separate from the FPGA for optimal noise isolation. | ||
|
||
A 16-bit input second-order noise shaper is used, which has a limited noise performance, in addition to the power supply noise issue outlined above. Do not expect stellar performance. | ||
|
||
A suitable RC reconstruction filter can be made from a 100 ohm resistor and a 1uF capacitor: | ||
|
||
``` | ||
From FPGA | ||
+--------------+ | ||
o--------| R = 100 Ohms |--------|-------------------o Output | ||
+--------------+ | | ||
| | ||
+---------+ | ||
+---------+ C=1 uFarad | ||
| | ||
| | ||
| | ||
------- | ||
----- | ||
--- | ||
``` | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// pipelined CORDIC algorithm to calculate sin/cos pair from a given angle (0..1) | ||
// Author: Niels A. Moseley | ||
// | ||
|
||
|
||
// one stage of the cordic iteration with registered outputs | ||
module cordic_stage_16(clk, rst_n, x_in, y_in, angle_in, angle_adj, x_out, y_out, angle_out); | ||
parameter SHIFT = 1; | ||
|
||
// inputs | ||
input clk; | ||
input rst_n; | ||
input signed [16-1:0] x_in; | ||
input signed [16-1:0] y_in; | ||
input signed [16-1:0] angle_in; | ||
input signed [16-1:0] angle_adj; | ||
|
||
// outputs | ||
output reg signed [16-1:0] x_out; | ||
output reg signed [16-1:0] y_out; | ||
output reg signed [16-1:0] angle_out; | ||
|
||
// internal signal | ||
reg signed [16-1:0] new_x; | ||
reg signed [16-1:0] new_y; | ||
reg signed [16-1:0] new_angle; | ||
|
||
wire sign; | ||
wire signed [16-1:0] shifted_x; | ||
wire signed [16-1:0] shifted_y; | ||
|
||
assign sign = angle_in[16-1]; // angle sign bit | ||
assign shifted_x = x_in >>> SHIFT; | ||
assign shifted_y = y_in >>> SHIFT; | ||
|
||
always @(*) | ||
begin | ||
new_x = sign ? (x_in + shifted_y) : (x_in - shifted_y); | ||
new_y = sign ? (y_in - shifted_x) : (y_in + shifted_x); | ||
new_angle = sign ? (angle_in + angle_adj) : (angle_in - angle_adj); | ||
end | ||
|
||
always @(posedge clk) | ||
begin | ||
if (rst_n == 1'b0) | ||
begin | ||
x_out <= 0; | ||
y_out <= 0; | ||
angle_out <= 0; | ||
end | ||
else begin | ||
x_out <= new_x; | ||
y_out <= new_y; | ||
angle_out <= new_angle; | ||
end | ||
end | ||
|
||
endmodule | ||
|
||
|
||
module cordic_10_16(clk, rst_n, angle_in, cos_out, sin_out); | ||
|
||
// inputs | ||
input clk; | ||
input rst_n; | ||
input signed [16-1:0] angle_in; | ||
|
||
// outputs | ||
output signed [16-1:0] cos_out; | ||
output signed [16-1:0] sin_out; | ||
|
||
// internal signals | ||
reg signed [16-1:0] x_in; | ||
reg signed [16-1:0] y_in; | ||
reg signed [16-1:0] z_in; | ||
|
||
wire signed [16-1:0] xbus [0:10-1]; | ||
wire signed [16-1:0] ybus [0:10-1]; | ||
wire signed [16-1:0] zbus [0:10-1]; | ||
|
||
assign cos_out = xbus[10-1]; | ||
assign sin_out = ybus[10-1]; | ||
|
||
always @(*) | ||
begin | ||
case($unsigned(angle_in[16-1:16-2])) | ||
2'b00: | ||
begin | ||
x_in <= 16'd19897; | ||
y_in <= 0; | ||
z_in <= angle_in; | ||
end | ||
2'b11: | ||
begin | ||
x_in <= 16'd19897; | ||
y_in <= 0; | ||
z_in <= angle_in; | ||
end | ||
2'b01: | ||
begin | ||
x_in <= 0; | ||
y_in <= 16'd19897; | ||
z_in <= $signed({2'b00, angle_in[16-3:0]}); | ||
end | ||
2'b10: | ||
begin | ||
x_in <= 0; | ||
y_in <= -16'd19897; | ||
z_in <= $signed({2'b11, angle_in[16-3:0]}); | ||
end | ||
endcase | ||
end | ||
|
||
// generate instances of cordic_stage | ||
cordic_stage_16 #(0) stage0(clk, rst_n, x_in, y_in, z_in, 16'sd8192, xbus[0], ybus[0], zbus[0]); | ||
cordic_stage_16 #(1) stage1(clk, rst_n, xbus[0], ybus[0], zbus[0], 16'sd4836, xbus[1], ybus[1], zbus[1]); | ||
cordic_stage_16 #(2) stage2(clk, rst_n, xbus[1], ybus[1], zbus[1], 16'sd2555, xbus[2], ybus[2], zbus[2]); | ||
cordic_stage_16 #(3) stage3(clk, rst_n, xbus[2], ybus[2], zbus[2], 16'sd1297, xbus[3], ybus[3], zbus[3]); | ||
cordic_stage_16 #(4) stage4(clk, rst_n, xbus[3], ybus[3], zbus[3], 16'sd651, xbus[4], ybus[4], zbus[4]); | ||
cordic_stage_16 #(5) stage5(clk, rst_n, xbus[4], ybus[4], zbus[4], 16'sd326, xbus[5], ybus[5], zbus[5]); | ||
cordic_stage_16 #(6) stage6(clk, rst_n, xbus[5], ybus[5], zbus[5], 16'sd163, xbus[6], ybus[6], zbus[6]); | ||
cordic_stage_16 #(7) stage7(clk, rst_n, xbus[6], ybus[6], zbus[6], 16'sd81, xbus[7], ybus[7], zbus[7]); | ||
cordic_stage_16 #(8) stage8(clk, rst_n, xbus[7], ybus[7], zbus[7], 16'sd41, xbus[8], ybus[8], zbus[8]); | ||
cordic_stage_16 #(9) stage9(clk, rst_n, xbus[8], ybus[8], zbus[8], 16'sd20, xbus[9], ybus[9], zbus[9]); | ||
|
||
|
||
endmodule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// Second order sigma-delta dac | ||
// | ||
// For benchmarking purposes only -- don't use this for an actual design. | ||
// There are far more performant architectures. | ||
// | ||
// Author: Niels A. Moseley, [email protected] | ||
// | ||
|
||
`ifdef DEBUG_SDDAC | ||
`include "constants.vams" | ||
`endif | ||
|
||
module sddac(clk, rst_n, sig_in, sd_out); | ||
|
||
// inputs | ||
input clk; // clock | ||
input rst_n; // synchronous reset, active low | ||
input signed [15:0] sig_in; // 16 bits in Q(1,15) format | ||
|
||
// outputs | ||
output reg sd_out = 0; | ||
|
||
// internal signals | ||
reg signed [17:0] state1 = 0; // Q(1,17) | ||
reg signed [19:0] state2 = 0; // Q(1,19) | ||
reg signed [16:0] state1_in; // Q(0,17) | ||
reg signed [18:0] state2_in; // Q(0,19) | ||
reg signed [20:0] quant_in; // Q(2,19) | ||
reg signed [16:0] qq; | ||
reg [7:0] lfsr_reg = 0; | ||
reg quantizer; | ||
wire lfsr_fb; | ||
|
||
// linear feedback shift register feedback | ||
assign lfsr_fb = (lfsr_reg[4] ^ lfsr_reg[2]); | ||
|
||
// combination process | ||
always @(*) | ||
begin | ||
`ifdef DEBUG_SDDAC | ||
qq = $signed(quantizer ? -17'h8000 : 17'h8000); | ||
`endif | ||
quant_in = state2 + $signed(lfsr_fb ? -21'h4000 : 21'h4000); | ||
quantizer = quant_in[20]; | ||
state1_in = sig_in - $signed(quantizer ? -17'h8000 : 17'h8000); // Q(-1,17) - Q(0,17) -> Q(0,17) | ||
state2_in = state1 - $signed(quantizer ? -19'h10000 : 19'h10000); // Q(-1,19) - Q(0,19) -> Q(0,19) | ||
end | ||
|
||
// clocked process | ||
always @(posedge clk) | ||
begin | ||
if (rst_n == 1'b0) | ||
begin | ||
state1 <= 0; | ||
state2 <= 0; | ||
lfsr_reg <= 8'hff; | ||
end | ||
else begin | ||
`ifdef DEBUG_SDDAC | ||
$display("feedback : %f", qq*$pow(2.0,-15)); | ||
$display("state1_in: %f", state1_in*$pow(2.0,-17)); | ||
$display("state2_in: %f", state2_in*$pow(2.0,-19)); | ||
$display(""); | ||
`endif | ||
state1 <= state1 + $signed({ state1_in[16], state1_in}); | ||
state2 <= state2 + $signed({ state2_in[18], state2_in}); | ||
sd_out <= !quantizer; | ||
lfsr_reg <= {lfsr_reg[6:0], lfsr_fb}; | ||
end | ||
end | ||
|
||
endmodule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
//Copyright (C)2014-2019 Gowin Semiconductor Corporation. | ||
//All rights reserved. | ||
//File Title: Physical Constraints file | ||
//GOWIN Version: V1.9.1.01Beta | ||
//Part Number: GW1N-LV1QN48C6/I5 | ||
//Created Time: Sun Dec 15 11:40:09 2019 | ||
|
||
IO_LOC "clk" 35; | ||
IO_LOC "rst_n" 77; | ||
IO_LOC "sd_out" 47; // PMOD header pin 1 | ||
|
||
IO_LOC "led[0]" 86; | ||
IO_LOC "led[1]" 85; | ||
IO_LOC "led[2]" 84; | ||
IO_LOC "led[3]" 83; | ||
IO_LOC "led[4]" 82; | ||
IO_LOC "led[5]" 81; | ||
IO_LOC "led[6]" 80; | ||
IO_LOC "led[7]" 79; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Top level of signal generator | ||
// | ||
// Author: Niels A. Moseley, [email protected] | ||
// | ||
|
||
module top(clk, rst_n, sd_out, led); | ||
|
||
// inputs | ||
input clk; // clock 12 MHz | ||
input rst_n; // synchronous reset, active low | ||
|
||
// outputs | ||
output sd_out; // noise shaper output | ||
output [7:0] led; // leds for debugging | ||
|
||
reg [23:0] phase_accu = 24'd0; | ||
|
||
// frequency = 12e6 / 2^32 * phase_inc | ||
|
||
reg [23:0] phase_inc = 24'd5592; // approximately 1kHz at 12MHz system clock | ||
wire signed [15:0] sinusoid; | ||
|
||
// phase accumulator to drive the cordic | ||
always @(posedge clk) | ||
begin | ||
phase_inc <= phase_inc + 24'd1; | ||
phase_accu <= phase_accu + phase_inc[23:14]; | ||
end; | ||
|
||
cordic_10_16 cordic | ||
( | ||
.clk(clk), | ||
.rst_n(rst_n), | ||
.angle_in(phase_accu[23:8]), | ||
.cos_out(sinusoid) | ||
); | ||
|
||
sddac dac | ||
( | ||
.clk(clk), | ||
.rst_n(rst_n), | ||
.sig_in( {sinusoid[15], sinusoid[15:1]} ), | ||
.sd_out(sd_out) | ||
); | ||
|
||
assign led = phase_accu[7:0]; | ||
|
||
endmodule |