Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DDS.qsf
Original file line number Diff line number Diff line change
Expand Up @@ -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
18 changes: 13 additions & 5 deletions DDS.v
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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),
Expand All @@ -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
73 changes: 65 additions & 8 deletions DDS_Modulation.v
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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
179 changes: 64 additions & 115 deletions README.md
Original file line number Diff line number Diff line change
@@ -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 | DAC90414-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.