[Domestic FPGA Evaluation] Anlu (Model SF1S60CG121I) 04 Parallel DAC DDS Module Design
[Copy link]
This post was last edited by EPTmachine on 2023-3-21 15:02
1.1 Hardware Introduction
On one side of the Demo board, there are 16 IOs available for output, and 3.3V power supply output is provided. The schematic diagram of user expanded IO is shown in the figure.
SF1S60CG121I Pin Location
|
SF1S60CG121I Pin Name
|
IO power domain
|
IO Name
|
IO number
|
H9
|
IO_R1P_0,GCLK3
|
2.5V
|
USER_IO0_N
|
3
|
G9
|
IO_R1N_0,GCLK2
|
2.5V
|
USER_IO0_P
|
4
|
H8
|
IO_R0N_0,GCLK0
|
2.5V
|
USER_IO1_N
|
5
|
H7
|
IO_R0P,GCLK1
|
2.5V
|
USER_IO1_P
|
6
|
D11
|
IO_R7N_0
|
2.5V
|
USER_IO2_N
|
7
|
D10
|
IO_R7P_0
|
2.5V
|
USER_IO2_P
|
8
|
C11
|
IO_R8N_0,GCLK10,D3
|
2.5V
|
USER_IO3_N
|
9
|
C10
|
IO_R8P_0,GCLK11,D2
|
2.5V
|
USER_IO3_P
|
10
|
B11
|
IO_R9N_0
|
2.5V
|
USER_IO4_N
|
11
|
B10
|
IO_R9P_0
|
2.5V
|
BUZZER
|
12
|
A11
|
IO_R10N_0,GCLK12,D1
|
2.5V
|
USER_IO5_N
|
13
|
A10
|
IO_R10P_0,GCLK13,D0
|
2.5V
|
USER_IO5_P
|
14
|
B8
|
IO_R11N_0
|
2.5V
|
USER_IO6_N
|
15
|
B9
|
IO_R11P_0
|
2.5V
|
USER_IO6_P
|
16
|
A8
|
IO_R12N_0,GCLK14,USRCLK
|
2.5V
|
USER_IO7_N
|
17
|
A9
|
IO_R12P_0,GCLK15,SCLK
|
2.5V
|
USER_IO7_P
|
18
|
The above resources can be used with the 10-bit high-speed DAC chip on the electric race training board of HardHe Academy to realize the DDS function. The schematic diagram of the electric race training board is as follows
The actual wiring diagram is as follows. The power supply of the electric race training board is provided by the 3.3V output terminal of the Demo board.
The button circuits and corresponding control pins on the Demo board are as follows:
The button is connected to an external pull-up resistor, which is high level by default. The mapping relationship between the button control pin and the FPGA pin is:
SF1S60CG121I Pin Location
|
SF1S60CG121I Pin Name
|
IO power domain
|
Button Name
|
H3
|
IO_B2N_2,GCLK2
|
1.2V
|
KEY0
|
H2
|
IO_B4P_2
|
1.2V
|
KEY1
|
G3
|
IO_B2P_2
|
1.2V
|
KEY2
|
1.2 ROM IP core usage and implementation of sine wave table
The SF1 device supports ERAM9K type embedded memory modules (ERAM), which can implement ROM, RAM, FIFO and other modules. The configuration is very convenient to use.
The DDS demonstrated this time uses a quarter of a 10-bit sine wave table, with a data width of 16 bits and a table depth of 64. The complete wave table can be obtained through phase transformation, which is based on symmetry.
However, the following compilation error occurred during actual use, and the specific reason is unknown.
Here I choose to use the LUT implementation. The code is as follows
module sin_table(address,sin);
output [8:0] sin; //实际波形表为9位分辨率(1/4周期)
input [5:0] address; //64个点来生成1/4个周期的波形,完整的一个周期为256个点
reg [8:0] sin;
always @(address)
begin
case(address)
6'h0: sin=9'h0;
6'h1: sin=9'hC;
6'h2: sin=9'h19;
6'h3: sin=9'h25;
6'h4: sin=9'h32;
6'h5: sin=9'h3E;
6'h6: sin=9'h4B;
6'h7: sin=9'h57;
6'h8: sin=9'h63;
6'h9: sin=9'h70;
6'ha: sin=9'h7C;
6'hb: sin=9'h88;
6'hc: sin=9'h94;
6'hd: sin=9'hA0;
6'he: sin=9'hAC;
6'hf: sin=9'hB8;
6'h10: sin=9'hC3;
6'h11: sin=9'hCF;
6'h12: sin=9'hDA;
6'h13: sin=9'hE6;
6'h14: sin=9'hF1;
6'h15: sin=9'hFC;
6'h16: sin=9'h107;
6'h17: sin=9'h111;
6'h18: sin=9'h11C;
6'h19: sin=9'h126;
6'h1a: sin=9'h130;
6'h1b: sin=9'h13A;
6'h1c: sin=9'h144;
6'h1d: sin=9'h14E;
6'h1e: sin=9'h157;
6'h1f: sin=9'h161;
6'h20: sin=9'h16A;
6'h21: sin=9'h172;
6'h22: sin=9'h17B;
6'h23: sin=9'h183;
6'h24: sin=9'h18B;
6'h25: sin=9'h193;
6'h26: sin=9'h19B;
6'h27: sin=9'h1A2;
6'h28: sin=9'h1A9;
6'h29: sin=9'h1B0;
6'h2a: sin=9'h1B7;
6'h2b: sin=9'h1BD;
6'h2c: sin=9'h1C3;
6'h2d: sin=9'h1C9;
6'h2e: sin=9'h1CE;
6'h2f: sin=9'h1D4;
6'h30: sin=9'h1D9;
6'h31: sin=9'h1DD;
6'h32: sin=9'h1E2;
6'h33: sin=9'h1E6;
6'h34: sin=9'h1E9;
6'h35: sin=9'h1ED;
6'h36: sin=9'h1F0;
6'h37: sin=9'h1F3;
6'h38: sin=9'h1F6;
6'h39: sin=9'h1F8;
6'h3a: sin=9'h1FA;
6'h3b: sin=9'h1FC;
6'h3c: sin=9'h1FD;
6'h3d: sin=9'h1FE;
6'h3e: sin=9'h1FF;
6'h3f: sin=9'h1FF;
endcase
end
endmodule
1.3 DDS waveform frequency and type control module
Regarding the frequency control of the waveform, the core formula is as follows: fout is the output frequency, fmclk is the master clock of the DDS, Nmax is the maximum value of the n-bit counter, and m is the phase accumulation value of each clock cycle. The DDS generator introduced in this article uses a 28-bit phase accumulator to achieve outputs of different frequencies by changing the step size m.
The key codes are as follows:
reg [27:0] dds_phase;
wire [27:0] dds_phase_add;
assign dds_phase_add = wave_step;
always@(posedge sys_clk or negedge rst_n)begin
if(!rst_n)begin
dds_phase <= 28'd0;
end else begin
dds_phase <= dds_phase + dds_phase_add;
end
end
The types of waveforms output by DDS, such as square wave, triangle wave, and sawtooth wave, are controlled by the upper ten bits of the phase accumulator.
The square wave uses the highest bit of the phase accumulator as the output data. In the process of the phase accumulator from 0 to the upper limit, the highest bit is 0 in half of the time and 1 in the other half of the time. Since the square wave only needs two quantization values of the DAC, all data is 0 for the minimum value and all data is 1 for the maximum value. The corresponding DAC data pin can output the corresponding data bit. The key code is as follows:
assign square_dac = {10{dds_phase[27]}};
For the sawtooth wave, its output voltage signal changes from the minimum value of the DAC to the maximum value. Taking the high 10 bits of the phase accumulator as the data source of the data pin of the DAC chip, the sawtooth wave can be generated. The key code is as follows:
assign saw_dac = dds_phase[27:18];
The triangle wave is similar to the sawtooth wave, but because the quantized data of the DAC goes from 0 to 1023 and then from 1023 to 0 in a single cycle, the counting overflow cycle is twice as fast as the sawtooth wave under the same cycle, and the counting direction will change. Therefore, on the basis of generating the sawtooth waveform, the highest bit of the phase accumulator is used as the selection flag of the triangle wave phase, and the 10 bits following it are used as the data source of the DAC data port. The data is inverted or not inverted according to different phases, so as to realize the output of the triangle wave. The key code is as follows:
assign trig_dac = dds_phase[27] ? ~dds_phase[26:17] : dds_phase[26:17];
The sine wave uses the high eight bits of the phase accumulator as the address of the sine wave table, and obtains the corresponding 10-bit DAC quantization value by looking up the table. The relationship between the address of one quarter of the sine wave table and the output DAC quantization value is realized above. The sine wave is similar to the triangle wave, except that it has four quadrants, so when using it, two bits of data are needed to determine which quadrant the waveform is in. The high two bits of the phase accumulator are used to determine the quadrant, and the following six bits are used as the addressing control bits of the wave table. The two are combined to obtain a complete sine wave output. The key code is as follows:
//查找表调用
lookup_tables u_lut(
.phase(dds_phase[27:20]),
.sin_out(sin_dac)
);
//////////////////////////////
//查找表实现
module lookup_tables(//sys_clk,rst_n,
phase, sin_out);
//input sys_clk;
//input rst_n;
input [7:0] phase;
output [9:0] sin_out;
wire [9:0] sin_out;
reg [5:0] address;
wire [1:0] sel;
wire [11:0] sine_table_out;
reg [9:0] sine_onecycle_amp;
assign sin_out = sine_onecycle_amp[9:0];
assign sel = phase[7:6];
sin_table u_sin_table(address,sine_table_out);
always @(sel or sine_table_out)
begin
case(sel)
2'b00: begin
sine_onecycle_amp = 9'h1ff + sine_table_out[8:0];
address = phase[5:0];
end
2'b01: begin
sine_onecycle_amp = 9'h1ff + sine_table_out[8:0];
address = ~phase[5:0];
end
2'b10: begin
sine_onecycle_amp = 9'h1ff - sine_table_out[8:0];
address = phase[5:0];
end
2'b11: begin
sine_onecycle_amp = 9'h1ff - sine_table_out[8:0];
address = ~ phase[5:0];
end
endcase
end
endmodule
1.4 DDS Amplitude Control Module
When the waveform is output through the DAC chip, the number of input bits at the input end of the DAC chip determines the quantization value range of the output signal. The output signal above uses 0 to 1023, which is the full scale of the DAC chip. If you want to control the peak-to-peak value of the output signal, you need to divide the output data. Direct division will lose a lot of precision. Here, the output quantization value is multiplied by a value x (0-255), and then divided by 255 to get an output signal that is attenuated proportionally. The output range of the DAC used in the test is 2.56V-0V, and each value of x above can correspond to a voltage value of 0.01V. The key code for amplitude control is as follows:
module dds_amp_adjust(
clk,
rst_n,
wave_amp,
signal_dat,
dac_dat
);
input clk,rst_n;
input [7:0] wave_amp;//用于调幅的因数
input [9:0] signal_dat;//未调幅的波形数据
output [9:0] dac_dat;//输出给DAC的数据
reg [17:0] amp_dat;//调幅后的波形数据
always @(posedge clk) begin
amp_dat<=signal_dat*wave_amp;//波形数据乘以调幅系数
end
assign dac_dat=amp_dat[17:8];//取高十位,相当于右移8位,除256
endmodule
1.5 Key input detection module
There are three buttons on the demo board, which can be used to control the type, frequency, and amplitude of the signal. The button debounce procedure is as follows:
module debounce(
//input
clk,
rst_n,
key,
//output
key_pluse);
parameter N=1; //要消除的按键数量
input clk;
input rst_n;
input [N-1:0] key; //输入的按键
output [N-1:0] key_pluse; //按键动作产生的脉冲
reg [N-1:0] key_signal_pre;
reg [N-1:0] key_signal;
wire [N-1:0] key_edge;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)begin
key_signal<={N{1'b1}};
key_signal_pre<={N{1'b1}};
end
else begin
key_signal<=key;
key_signal_pre<=key_signal;
end
end
assign key_edge = key_signal_pre&(~key_signal);
reg [17:0] cnt;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt<=18'h0;
else if(key_edge)
cnt<=18'h0;
else
cnt<=cnt+1'b1;
end
reg [N-1:0] key_sec_pre;
reg [N-1:0] key_sec;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
key_sec<={N{1'b1}};
else if(cnt==18'h3ffff)
key_sec<=key;
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
key_sec_pre<={N{1'b1}};
else
key_sec_pre<=key_sec;
end
assign key_pluse = key_sec_pre & (~key_sec);
endmodule
After confirming a valid input, the debounce module sends a pulse signal, which is used by other modules to detect the rising edge of the key. After detecting the rising edge, different output types, frequencies and amplitudes are switched. The key codes are as follows:
module dds_waveparactrl(
input clk,
input rst_n,
input wave_type_ctrl,
input wave_freq_ctrl,
input wave_amp_ctrl,
output reg [2:0] wave_type,
output reg [27:0] wave_freqm,
output reg [7:0] wave_amp
);
//波形输出形态控制
reg wave_type_ctrlr;//滤波后输出的按键脉冲为低电平
always@(posedge clk,negedge rst_n)begin
if(!rst_n)begin
wave_type_ctrlr <= 1'b0;
end
else begin
wave_type_ctrlr<=wave_type_ctrl;
end
end
wire wave_type_ctrl_pos;
assign wave_type_ctrl_pos=!wave_type_ctrlr&&wave_type_ctrl;
reg [1:0]type_cnt;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wave_type <= 3'b000;
type_cnt<=2'd1;
end
else if(wave_type_ctrl_pos)begin
case(type_cnt)
2'd0:begin wave_type <= 3'b000; type_cnt<=type_cnt+1'b1;end
2'd1:begin wave_type <= 3'b001; type_cnt<=type_cnt+1'b1;end
2'd2:begin wave_type <= 3'b010; type_cnt<=type_cnt+1'b1;end
2'd3:begin wave_type <= 3'b011; type_cnt<=type_cnt+1'b1;end
endcase
end
end
//波形输出频率控制
reg wave_freq_ctrlr;//滤波后输出的按键脉冲为低电平
always@(posedge clk,negedge rst_n)begin
if(!rst_n)begin
wave_freq_ctrlr <= 1'b0;
end
else begin
wave_freq_ctrlr<=wave_freq_ctrl;
end
end
wire wave_freq_ctrl_pos;
assign wave_freq_ctrl_pos=!wave_freq_ctrlr&&wave_freq_ctrl;
reg [2:0]freqm_cnt;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)begin
wave_freqm <= 28'd5369; //50M时钟频率下,1KHz信号的步进值m
freqm_cnt <=3'd0;
end
else if(wave_freq_ctrl_pos)begin
case(freqm_cnt)
3'd0:begin wave_freqm <= 28'd5369; freqm_cnt<=freqm_cnt+1'd1;end //1KHz
3'd1:begin wave_freqm <= 28'd53690; freqm_cnt<=freqm_cnt+1'd1;end //10KHz
3'd2:begin wave_freqm <= 28'd268450;freqm_cnt<=freqm_cnt+1'd1;end //50KHz
3'd3:begin wave_freqm <= 28'd536900;freqm_cnt<=freqm_cnt+1'd1;end //100KHz
3'd4:begin wave_freqm <= 28'd5369000; freqm_cnt<=freqm_cnt+1'd1;end //1MHz
3'd5:begin wave_freqm <= 28'd10738000; freqm_cnt<=freqm_cnt+1'd1;end //2MHz
3'd6:begin wave_freqm <= 28'd26845000; freqm_cnt<=freqm_cnt+1'd1;end //5MHz
3'd7:begin wave_freqm <= 28'd10738; freqm_cnt<=freqm_cnt+1'd1;end //2KHz
default:;
endcase
end
end
//波形输出幅度控制
reg wave_amp_ctrlr;//滤波后输出的按键脉冲为低电平
always@(posedge clk,negedge rst_n)begin
if(!rst_n)begin
wave_amp_ctrlr <= 1'b0;
end
else begin
wave_amp_ctrlr<=wave_amp_ctrl;
end
end
wire wave_amp_ctrl_pos;
assign wave_amp_ctrl_pos=!wave_amp_ctrlr&&wave_amp_ctrl;
reg [2:0] amp_cnt;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)begin
wave_amp <= 8'd255; //48M时钟频率下,1KHz信号的步进值m
amp_cnt <=3'd0;
end
else if(wave_amp_ctrl_pos)begin
case(amp_cnt)
3'd0:begin wave_amp <= 8'd255; amp_cnt<=amp_cnt+1'd1;end //2.56V
3'd1:begin wave_amp <= 8'd200; amp_cnt<=amp_cnt+1'd1;end //2.00V
3'd2:begin wave_amp <= 8'd180; amp_cnt<=amp_cnt+1'd1;end //1.8V
3'd3:begin wave_amp <= 8'd170; amp_cnt<=amp_cnt+1'd1;end //1.7V
3'd4:begin wave_amp <= 8'd150; amp_cnt<=amp_cnt+1'd1;end //1.5V
3'd5:begin wave_amp <= 8'd140; amp_cnt<=amp_cnt+1'd1;end //1.4V
3'd6:begin wave_amp <= 8'd130; amp_cnt<=amp_cnt+1'd1;end //1.3V
3'd7:begin wave_amp <= 8'd100; amp_cnt<=amp_cnt+1'd1;end //1.0V
default:;
endcase
end
end
endmodule
1.6 Experimental Demonstration
dds演示
1.7 Summary
This experiment occupied a total of 11 user IOs, while the Demo board has a total of 16 user IOs. The original plan was to use LCD to set parameters. Of course, the board comes with a 128x32 OLED screen, which is sufficient for setting DDS parameters, but may not be enough for ADC sampling. However, there is a 128x64 OLED on the e-sports training board in the experiment, which is sufficient for displaying waveforms. The OLED screen will be used for subsequent information display.
|