diff --git a/DDS.qsf b/DDS.qsf index b488369..41df10f 100644 --- a/DDS.qsf +++ b/DDS.qsf @@ -264,5 +264,6 @@ set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[24] - set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[25] -to auto_signaltap_0|gnd -section_id auto_signaltap_0 set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[26] -to auto_signaltap_0|vcc -section_id auto_signaltap_0 set_instance_assignment -name POST_FIT_CONNECT_TO_SLD_NODE_ENTITY_PORT crc[27] -to auto_signaltap_0|vcc -section_id auto_signaltap_0 +set_location_assignment PIN_A15 -to baseband_out set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top set_global_assignment -name SLD_FILE db/stp1_auto_stripped.stp \ No newline at end of file diff --git a/DDS.v b/DDS.v index 180eac3..12fa57a 100644 --- a/DDS.v +++ b/DDS.v @@ -3,7 +3,8 @@ module DDS( input wire rst_n, input wire RX, output wire dac904_clk, - output wire [13:0] dac904_data + output wire [13:0] dac904_data, + output wire baseband_out ); wire [23:0] rx_frame; @@ -22,32 +23,36 @@ module DDS( reg [31:0] dds_fre_c; reg [31:0] dds_fre_m; - reg [1:0] dds_mod_type; + reg [2:0] dds_mod_type; reg [15:0] dds_am_depth; reg [15:0] dds_fm_dev_hz; + reg [15:0] dds_output_amp; always @(posedge clk_50M or negedge rst_n) begin if (!rst_n) begin dds_fre_c <= 32'd2000; dds_fre_m <= 32'd1000; - dds_mod_type <= 2'b00; + dds_mod_type <= 3'd0; dds_am_depth <= 16'd16384; dds_fm_dev_hz <= 16'd5000; + dds_output_amp <= 16'd32768; end else if (rx_irq) begin case (ctrl_addr) 8'h01: dds_fre_c[15:0] <= ctrl_value; 8'h02: dds_fre_c[31:16] <= ctrl_value; - 8'h03: dds_mod_type <= ctrl_value[1:0]; + 8'h03: dds_mod_type <= ctrl_value[2:0]; 8'h04: dds_am_depth <= ctrl_value; 8'h05: dds_fre_m[15:0] <= ctrl_value; 8'h06: dds_fre_m[31:16] <= ctrl_value; 8'h07: dds_fm_dev_hz <= ctrl_value; + 8'h08: dds_output_amp <= ctrl_value; default: ; endcase end end wire [15:0] internal_signal; + wire baseband_bit; DDS_Modulation DDS_Modulation_inst( .clk_50M (clk_50M), @@ -57,9 +62,12 @@ module DDS( .mod_type (dds_mod_type), .fm_dev_hz (dds_fm_dev_hz), .am_depth (dds_am_depth), - .signal_out (internal_signal) + .output_amp (dds_output_amp), + .signal_out (internal_signal), + .baseband_bit (baseband_bit) ); assign dac904_data = internal_signal[15:2]; assign dac904_clk = clk_50M; + assign baseband_out = baseband_bit; endmodule diff --git a/DDS_Modulation.v b/DDS_Modulation.v index 0de6019..6e5852b 100644 --- a/DDS_Modulation.v +++ b/DDS_Modulation.v @@ -3,14 +3,19 @@ module DDS_Modulation ( input wire rst_n, input wire [31:0] fre_c, input wire [31:0] fre_m, - input wire [1:0] mod_type, + input wire [2:0] mod_type, input wire [15:0] fm_dev_hz, input wire [15:0] am_depth, - output reg [15:0] signal_out + input wire [15:0] output_amp, + output wire [15:0] signal_out, + output reg baseband_bit ); localparam [24:0] PHASE_STEP_SCALE = 25'd22517998; localparam [15:0] FM_DEV_MAX_HZ = 16'd10000; + localparam [15:0] OUTPUT_AMP_MAX = 16'd32768; + localparam [31:0] KEYING_CARRIER_HZ = 32'd100000; + localparam [12:0] SYMBOL_PERIOD = 13'd5000; wire [31:0] actual_fre_c = (fre_c == 32'd0) ? 32'd2000 : fre_c; wire [31:0] actual_fre_m = (fre_m == 32'd0) ? 32'd1000 : fre_m; @@ -51,7 +56,7 @@ module DDS_Modulation ( wire signed [34:0] fm_offset = fm_offset_mult >>> 15; wire signed [34:0] step_c_sum = $signed({3'b000, step_c_base}) + fm_offset; wire [31:0] step_c_fm = step_c_sum[34] ? 32'd0 : step_c_sum[31:0]; - wire [31:0] step_c_final = (mod_type == 2'b10) ? step_c_fm : step_c_base; + wire [31:0] step_c_final = (mod_type == 3'd2) ? step_c_fm : step_c_base; reg [31:0] phase_c; wire [15:0] sin_u_c; @@ -77,18 +82,70 @@ module DDS_Modulation ( wire signed [16:0] am_term_signed = depth_mult >>> 15; wire [16:0] am_envelope = 17'd32768 + am_term_signed; - // AM ???? 6 dB ?????? ma=100% ????? + // AM is attenuated by 6 dB to avoid clipping when ma reaches 100%. wire signed [16:0] am_carrier_base = carrier_signed >>> 1; wire signed [33:0] am_mult = am_carrier_base * $signed({1'b0, am_envelope}); wire signed [16:0] am_signed = am_mult >>> 15; wire [15:0] am_unsigned_out = am_signed + 17'sd32768; + wire [48:0] temp_keying = KEYING_CARRIER_HZ * PHASE_STEP_SCALE; + wire [31:0] step_keying = temp_keying[48:18]; + + reg [31:0] phase_keying; + wire [15:0] sin_u_keying; + + always @(posedge clk_50M or negedge rst_n) begin + if (!rst_n) begin + phase_keying <= 32'd0; + end else begin + phase_keying <= phase_keying + step_keying; + end + end + + rom_sin rom_keying_inst ( + .address (phase_keying[31:20]), + .clock (clk_50M), + .q (sin_u_keying) + ); + + reg [12:0] symbol_cnt; + reg [14:0] prbs_lfsr; + + always @(posedge clk_50M or negedge rst_n) begin + if (!rst_n) begin + symbol_cnt <= 13'd0; + prbs_lfsr <= 15'h4ACE; + baseband_bit <= 1'b1; + end else if (symbol_cnt == SYMBOL_PERIOD - 13'd1) begin + symbol_cnt <= 13'd0; + baseband_bit <= prbs_lfsr[14]; + prbs_lfsr <= {prbs_lfsr[13:0], prbs_lfsr[14] ^ prbs_lfsr[13]}; + end else begin + symbol_cnt <= symbol_cnt + 1'b1; + end + end + + wire [15:0] ask_unsigned_out = baseband_bit ? sin_u_keying : 16'd32768; + wire [15:0] psk_unsigned_out = baseband_bit ? (16'd65535 - sin_u_keying) : sin_u_keying; + + reg [15:0] signal_raw; + always @(*) begin case (mod_type) - 2'b00: signal_out = cw_fm_unsigned_out; - 2'b01: signal_out = am_unsigned_out; - 2'b10: signal_out = cw_fm_unsigned_out; - default: signal_out = cw_fm_unsigned_out; + 3'd0: signal_raw = cw_fm_unsigned_out; + 3'd1: signal_raw = am_unsigned_out; + 3'd2: signal_raw = cw_fm_unsigned_out; + 3'd3: signal_raw = ask_unsigned_out; + 3'd4: signal_raw = psk_unsigned_out; + default: signal_raw = cw_fm_unsigned_out; endcase end + + wire [15:0] actual_output_amp = (output_amp > OUTPUT_AMP_MAX) ? OUTPUT_AMP_MAX : output_amp; + wire signed [16:0] raw_centered = $signed({1'b0, signal_raw}) - 17'sd32768; + wire signed [33:0] amp_mult = raw_centered * $signed({1'b0, actual_output_amp}); + wire signed [16:0] scaled_centered = amp_mult >>> 15; + wire signed [17:0] scaled_sum = 18'sd32768 + scaled_centered; + + assign signal_out = scaled_sum[15:0]; endmodule diff --git a/README.md b/README.md index e4b8095..0f07df4 100644 --- a/README.md +++ b/README.md @@ -1,125 +1,74 @@ -<<<<<<< HEAD -# FPGA DDS 信号发生器 +# FPGA DDS Signal Generator -基于 Cyclone IV FPGA 的 DDS(直接数字频率合成)信号发生器,支持正弦波(CW)、调幅(AM)和调频(FM)三种输出模式,通过 UART 串口实时控制参数。 +基于 Cyclone IV FPGA 的 DDS 信号发生器,支持 CW、AM、FM、ASK、PSK 输出模式,通过 UART 串口实时控制参数。ASK/PSK 使用固定 100 kHz 载波和 10 kbps PRBS 二进制基带序列。 -## 硬件平台 +## Hardware -| 项目 | 型号 | -|------|------| -| FPGA | Intel Cyclone IV E — EP4CE6F17C8 | -| DAC | DAC904(14-bit) | -| 系统时钟 | 50 MHz | +| Item | Model | +|------|-------| +| FPGA | Intel Cyclone IV E EP4CE6F17C8 | +| DAC | DAC904, 14-bit | +| System clock | 50 MHz | -## 功能特性 +## Features -- **CW 正弦波**:频率可调,默认 2000 Hz -- **AM 调幅**:载波频率 + 调制频率 + 调制深度可调,默认载波 2000 Hz、调制 1000 Hz、深度 100% -- **FM 调频**:载波频率 + 调制频率 + 最大频偏可调,频偏上限 10000 Hz -- **UART 控制**:921600 波特率,6 字节帧协议,支持运行时实时修改所有参数 +- CW:正弦波输出,默认 2000 Hz,可由 MCU 设置频率。 +- AM:载波频率、调制频率、调制深度可设置。 +- FM:载波频率、调制频率、频偏可设置。 +- ASK:固定 100 kHz 载波,10 kbps PRBS 基带,码元 1 输出载波,码元 0 输出 DAC 中点。 +- PSK:固定 100 kHz 载波,10 kbps PRBS 基带,码元 0/1 相位相差 180 度。 +- 全局输出幅度:通过寄存器 `0x08` 设置,所有模式最终输出都以 DAC 中点为中心缩放。 +- `baseband_out`:输出当前 PRBS 基带码元,便于示波器或逻辑分析仪观察;当前未在 `DDS.qsf` 中绑定物理管脚。 -## 系统架构 +## UART Protocol -``` - ┌──────────────┐ - UART RX ────▶│ UartFrameRX │──── 24-bit frame ────┐ - (921600) │ │ │ - └──────────────┘ ▼ - ┌─────────────────┐ - │ DDS Top (DDS) │ - │ │ - │ 寄存器解析/控制 │ - └────────┬────────┘ - │ - ▼ - ┌─────────────────┐ - │ DDS_Modulation │ - │ │ - │ ┌─────────────┐ │ - │ │ 调制信号 DDS │ │──▶ sin(调制) - │ └─────────────┘ │ - │ │ - │ ┌─────────────┐ │ - │ │ 载波信号 DDS │ │──▶ sin(载波) - │ └─────────────┘ │ - │ │ - │ AM / FM / CW 选择│ - └────────┬────────┘ - │ - ▼ - ┌─────────────────┐ - │ DAC904 (14-bit) │ - └─────────────────┘ -``` - -## 模块说明 - -| 文件 | 说明 | -|------|------| -| [DDS.v](DDS.v) | 顶层模块:UART 帧解析、控制寄存器、模块例化 | -| [DDS_Modulation.v](DDS_Modulation.v) | DDS 调制核心:载波/调制信号相位累加、AM/FM/CW 输出选择 | -| [UartFrameRX.v](UartFrameRX.v) | UART 帧接收器:6 字节协议解析,提取 24-bit 数据帧 | -| [UartByteRX.v](UartByteRX.v) | UART 字节接收器:波特率可配置,标准 8N1 | -| [rom_sin.v](rom_sin.v) | 正弦波 ROM 查找表(Quartus IP,4096×16-bit) | -| [Sin.mif](Sin.mif) | 正弦波初始化数据 | - -## UART 控制协议 - -**帧格式(6 字节):** - -``` -[数据高8位] [数据低8位] [地址] [0xFF] [0xFF] [0xFF] -``` - -**寄存器地址映射:** - -| 地址 | 参数 | 说明 | -|------|------|------| -| `0x01` | `dds_fre_c[15:0]` | 载波频率(低 16 位) | -| `0x02` | `dds_fre_c[31:16]` | 载波频率(高 16 位) | -| `0x03` | `dds_mod_type[1:0]` | 调制类型:`00`=CW, `01`=AM, `10`=FM | -| `0x04` | `dds_am_depth[15:0]` | AM 调制深度(0\~32768,对应 0%\~100%) | -| `0x05` | `dds_fre_m[15:0]` | 调制频率(低 16 位) | -| `0x06` | `dds_fre_m[31:16]` | 调制频率(高 16 位) | -| `0x07` | `dds_fm_dev_hz[15:0]` | FM 最大频偏(Hz,上限 10000) | +Frame format: -## 引脚分配 - -| 信号 | FPGA 引脚 | 说明 | -|------|-----------|------| -| `clk_50M` | PIN_E1 | 50 MHz 系统时钟 | -| `rst_n` | PIN_J14 | 复位(低有效) | -| `RX` | PIN_B16 | UART 接收 | -| `dac904_clk` | PIN_C6 | DAC 时钟 | -| `dac904_data[0]` ~ `dac904_data[13]` | PIN_B6 ~ PIN_B13 | DAC 14-bit 数据总线 | - -## 开发环境 - -- **Quartus Prime** 18.1 Standard Edition -- **目标器件**:Cyclone IV E — EP4CE6F17C8 -- **综合/布局布线**:Quartus 默认流程 - -## 使用方法 - -1. 用 Quartus Prime 18.1 打开 `DDS.qpf` -2. 编译(Processing → Start Compilation) -3. 通过 USB-Blaster 下载 SOF 到 FPGA -4. 使用串口工具(波特率 921600,8N1)发送控制帧 - -**示例:设置载波频率为 5000 Hz(0x1388)** - -``` -发送字节:0x00 0x13 0x88 0x01 0xFF 0xFF 0xFF - ↑ 地址=0x01,写载波频率低16位 +```text +[data_hi] [data_lo] [addr] [0xFF] [0xFF] [0xFF] ``` -## 相关仓库 - -此项目的 MCU 上位机控制代码(基于 MSPM0G3519 + OLED 显示)位于 `Code/` 目录。 - -## License - -MIT -======= -基于MSPM0G3519和FPGA的DDS信号发生器的FPGA部分 ->>>>>>> 4e1700362307224c3046e1137a825cccdbde6003 +Baud rate: `921600`, 8N1. + +| Address | Register | Description | +|---------|----------|-------------| +| `0x01` | `dds_fre_c[15:0]` | Carrier frequency low 16 bits | +| `0x02` | `dds_fre_c[31:16]` | Carrier frequency high 16 bits | +| `0x03` | `dds_mod_type[2:0]` | `0`=CW, `1`=AM, `2`=FM, `3`=ASK, `4`=PSK | +| `0x04` | `dds_am_depth[15:0]` | AM depth, `0` to `32768` means 0% to 100% | +| `0x05` | `dds_fre_m[15:0]` | Modulation frequency low 16 bits | +| `0x06` | `dds_fre_m[31:16]` | Modulation frequency high 16 bits | +| `0x07` | `dds_fm_dev_hz[15:0]` | FM deviation in Hz, limited to 10000 Hz | +| `0x08` | `dds_output_amp[15:0]` | Global output amplitude, `0` to `32768` means 0% to 100% | + +## Modules + +| File | Description | +|------|-------------| +| `DDS.v` | Top module: UART frame decode, control registers, DAC and baseband outputs | +| `DDS_Modulation.v` | DDS modulation core for CW/AM/FM/ASK/PSK | +| `UartFrameRX.v` | 6-byte UART frame parser | +| `UartByteRX.v` | UART byte receiver | +| `rom_sin.v` | 4096 x 16-bit sine ROM | +| `Sin.mif` | Sine ROM initialization data | + +## Pin Notes + +Existing assigned pins: + +| Signal | FPGA pin | Description | +|--------|----------|-------------| +| `clk_50M` | `PIN_E1` | 50 MHz clock | +| `rst_n` | `PIN_J14` | Active-low reset | +| `RX` | `PIN_B16` | UART RX | +| `dac904_clk` | `PIN_C6` | DAC clock | +| `dac904_data[13:0]` | see `DDS.qsf` | DAC data bus | + +`baseband_out` is intentionally left without a location assignment. Bind it manually in Quartus when the target observation pin is chosen. + +## Build + +1. Open `DDS.qpf` in Quartus Prime 18.1 Standard Edition. +2. Start compilation. +3. Download the generated SOF through USB-Blaster. +4. Use the MCU firmware or a UART tool to send control frames.