立足典型FPGA设计仿真运行平台AMD-Xillinz-Vivado/VitisEmbeddedDevelopment,借助人工智能AIIMA-Copilot-DS与MuleRun工具,使用Verilog与C语言,展开了一系列的典型真物实操设计仿真项目演练:组合逻辑--七段数码管译码器、时序逻辑---LFSR伪随机数发生器、状态机FSM--交通灯控制器、综合UART串口通信、FPGA-SoC--MicroBlaze-V片上系统。

1 基础组合逻辑--七段数码管译码器
1.1 方案规划
BCD-to-7-Segment Decoder--将4 位BCD 输入转换为七段数码管的段选信号,驱动共阴极/共阳极数码管显示0~F。
七段数码管由a~g共7个LED段加1个小数点(dp)组成。译码器的功能是将4位BCD码(0x0~0xF)映射到8位段选信号{dp,g,f,e,d,c,b,a}。
1.2 逻辑编码
RTL源码--seg7_decoder.v
cpp
// ============================================================
// Module: seg7_decoder
// Description: BCD to 7-segment display decoder (active HIGH)
// Target: Xilinx FPGA (Vivado Synthesis)
// ============================================================
module seg7_decoder (
input wire [3:0] bcd, // 4-bit BCD input (0x0 - 0xF)
output reg [6:0] seg // 7-segment output {g,f,e,d,c,b,a}
);
// Combinational logic: BCD → segment pattern
always @(*) begin
case (bcd)
4'h0: seg = 7'b111_1110; // 0: a,b,c,d,e,f
4'h1: seg = 7'b011_0000; // 1: b,c
4'h2: seg = 7'b110_1101; // 2: a,b,d,e,g
4'h3: seg = 7'b111_1001; // 3: a,b,c,d,g
4'h4: seg = 7'b011_0011; // 4: b,c,f,g
4'h5: seg = 7'b101_1011; // 5: a,c,d,f,g
4'h6: seg = 7'b101_1111; // 6: a,c,d,e,f,g
4'h7: seg = 7'b111_0000; // 7: a,b,c
4'h8: seg = 7'b111_1111; // 8: all segments
4'h9: seg = 7'b111_1011; // 9: a,b,c,d,f,g
4'hA: seg = 7'b111_0111; // A: a,b,c,e,f,g
4'hB: seg = 7'b001_1111; // b: c,d,e,f,g
4'hC: seg = 7'b100_1110; // C: a,d,e,f
4'hD: seg = 7'b011_1101; // d: b,c,d,e,g
4'hE: seg = 7'b100_1111; // E: a,d,e,f,g
4'hF: seg = 7'b100_0111; // F: a,e,f,g
default: seg = 7'b000_0000; // blank
endcase
end
endmodule
测试编码Testbench--tb_seg7.v
cpp
// ============================================================
// Testbench: tb_seg7 --- exhaustive test of all 16 BCD inputs
// ============================================================
`timescale 1ns/1ps
module tb_seg7;
reg [3:0] bcd;
wire [6:0] seg;
// DUT instantiation
seg7_decoder uut (
.bcd (bcd),
.seg (seg)
);
initial begin
bcd = 4'd0; // 明确初始化
$display("===== Test =====");
for (bcd = 4'd0; bcd <= 4'd15; bcd = bcd + 1) begin
#10;
$display("bcd=%h seg=%b", bcd, seg);
end
$finish;
end
endmodule
1.3 仿真测试
Vivado 仿真执行步骤
- 创建工程:File → Project → New → 选择目标器件 (如 Artix-7 XC7A35T) → 完成。
- 添加设计源文件:Add Sources → Add Design Sources → 选择seg7_decoder.v
- 添加仿真源文件:Add Sources → Add Simulation Sources → 选择tb_seg7.v
- 运行行为仿真:Flow Navigator → SIMULATION → Run Behavioral Simulation → 观察波形。
- Tcl 控制台命令

1.4 综合执行
添加并编写XDC约束à综合Run Synthesisà实现Run Implementationà生成位流Generate Bitstreamà下载à运行。
cpp
## ============================================================
## Project: seg7_decoder
## Board: Digilent Basys3 (XC7A35T-1CPG236C)
## File: constraints_seg7.xdc
## ============================================================
## ── 系统时钟 (100 MHz) ──
set_property PACKAGE_PIN W5 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
create_clock -add -name sys_clk -period 10.00 [get_ports clk]
## ── 拨码开关输入 SW[3:0] → BCD[3:0] ──
## SW0 → bcd[0], SW1 → bcd[1], SW2 → bcd[2], SW3 → bcd[3]
set_property PACKAGE_PIN V17 [get_ports {bcd[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {bcd[0]}]
set_property PACKAGE_PIN V16 [get_ports {bcd[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {bcd[1]}]
set_property PACKAGE_PIN W16 [get_ports {bcd[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {bcd[2]}]
set_property PACKAGE_PIN W17 [get_ports {bcd[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {bcd[3]}]
## ── 七段数码管 段选 seg[6:0] = {g,f,e,d,c,b,a} ──
## 共阳极: seg=0 点亮, seg=1 熄灭 (active LOW)
set_property PACKAGE_PIN W7 [get_ports {seg[0]}] ## a
set_property IOSTANDARD LVCMOS33 [get_ports {seg[0]}]
set_property PACKAGE_PIN W6 [get_ports {seg[1]}] ## b
set_property IOSTANDARD LVCMOS33 [get_ports {seg[1]}]
set_property PACKAGE_PIN U8 [get_ports {seg[2]}] ## c
set_property IOSTANDARD LVCMOS33 [get_ports {seg[2]}]
set_property PACKAGE_PIN V8 [get_ports {seg[3]}] ## d
set_property IOSTANDARD LVCMOS33 [get_ports {seg[3]}]
set_property PACKAGE_PIN U5 [get_ports {seg[4]}] ## e
set_property IOSTANDARD LVCMOS33 [get_ports {seg[4]}]
set_property PACKAGE_PIN V5 [get_ports {seg[5]}] ## f
set_property IOSTANDARD LVCMOS33 [get_ports {seg[5]}]
set_property PACKAGE_PIN U7 [get_ports {seg[6]}] ## g
set_property IOSTANDARD LVCMOS33 [get_ports {seg[6]}]
## ── 七段数码管 位选 an[3:0] ──
## 共阳极 PNP 驱动: an=0 选中, an=1 关闭 (active LOW)
## 仅使用第 0 位数码管显示 BCD 输入
set_property PACKAGE_PIN V4 [get_ports {an[0]}] ## DIG0 --- 使用中
set_property IOSTANDARD LVCMOS33 [get_ports {an[0]}]
set_property PACKAGE_PIN U4 [get_ports {an[1]}] ## DIG1 --- 关闭
set_property IOSTANDARD LVCMOS33 [get_ports {an[1]}]
set_property PACKAGE_PIN U2 [get_ports {an[2]}] ## DIG2 --- 关闭
set_property IOSTANDARD LVCMOS33 [get_ports {an[2]}]
set_property PACKAGE_PIN U3 [get_ports {an[3]}] ## DIG3 --- 关闭
set_property IOSTANDARD LVCMOS33 [get_ports {an[3]}]
## ── 约束: 未使用的数码管位保持关闭 ──
## 在 RTL 中: assign an = 4'b1110; (仅 DIG0 有效)
2 基础时序逻辑--LFSR伪随机发生器
2.1 方案规划
Linear Feedback Shift Register--基于XOR反馈抽头的最大长度序列伪随机数发生器,可配置位宽 (默认 8-bit),产生2N−1个不重复伪随机值。
LFSR(线性反馈移位寄存器),通过将输出位的线性组合(XOR)反馈到输入端,产生最大长度伪随机序列。
关键概念:
- 抽头位置 (Taps):决定序列长度,需满足本原多项式条件
- Fibonacci 结构:反馈从高位抽头 XOR 后注入 LSB
- Galois 结构:每级独立 XOR,效率更高
- 种子 (Seed):初始值不能为全 0,否则锁定
伪随机数发生常见LFSR抽头配置列表
|----------|----------|----------------------------------|--------------------------|
| 位宽 N | 序列长度 | Fibonacci 抽头 (XOR 位) | 本原多项式 |
| 4 | 15 | 4,3 | x⁴ + x³ + 1 |
| 8 | 255 | 8,6,5,4 | x⁸ + x⁶ + x⁵ + x⁴ + 1 |
| 16 | 65535 | 16,15,13,4 | x¹⁶ + x¹⁵ + x¹³ + x⁴ + 1 |
| 32 | 4.29×10⁹ | 32,22,2,1 | x³² + x²² + x² + x + 1 |
2.2 逻辑编码
1 RTL 源码(Fibonacci)--lfsr_fib.v
cpp
// ============================================================
// Module: lfsr_fib
// Fibonacci LFSR with configurable width
// Default: 8-bit, taps [8,6,5,4]
// ============================================================
module lfsr_fib #(
parameter integer N = 8,
parameter [31:0] SEED = 8'hA5
)(
input wire clk,
input wire rst_n, // active-low reset
input wire en, // shift enable
output wire [N-1:0] rnd // pseudo-random output
);
reg [N-1:0] lfsr_q;
// Feedback bit: XOR of tap positions
// For N=8: taps at bits [7,5,4,3] (0-indexed)
wire feedback;
generate
if (N == 8)
assign feedback = lfsr_q[7] ^ lfsr_q[5] ^ lfsr_q[4] ^ lfsr_q[3];
else if (N == 16)
assign feedback = lfsr_q[15] ^ lfsr_q[14] ^ lfsr_q[12] ^ lfsr_q[3];
else if (N == 4)
assign feedback = lfsr_q[3] ^ lfsr_q[2];
else
assign feedback = lfsr_q[N-1] ^ lfsr_q[N-2]; // fallback
endgenerate
// Sequential logic
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
lfsr_q <= SEED[N-1:0];
else if (en)
lfsr_q <= {lfsr_q[N-2:0], feedback};
end
assign rnd = lfsr_q;
endmodule
2 RTL 源码(Galois)--lfsr_gal.v
cpp
// ============================================================
// Module: lfsr_gal
// Galois LFSR --- each stage XOR independently
// More efficient for synthesis (shorter critical path)
// ============================================================
module lfsr_gal #(
parameter integer N = 8,
parameter [31:0] SEED = 8'hA5
)(
input wire clk,
input wire rst_n,
input wire en,
output wire [N-1:0] rnd
);
reg [N-1:0] lfsr_q;
// Galois: MSB directly feeds back, XOR at tap positions
// Mask for N=8: taps at bit positions 5,4,3 (from MSB)
wire msb = lfsr_q[N-1];
wire [N-1:0] next_val;
generate
if (N == 8) begin
assign next_val[N-1] = lfsr_q[N-2];
assign next_val[5] = lfsr_q[4] ^ msb;
assign next_val[4] = lfsr_q[3] ^ msb;
assign next_val[3] = lfsr_q[2] ^ msb;
assign next_val[2] = lfsr_q[1];
assign next_val[1] = lfsr_q[0];
assign next_val[0] = msb;
end
else begin
// Simplified: generic fallback
assign next_val = {lfsr_q[N-2:0], msb};
end
endgenerate
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
lfsr_q <= SEED[N-1:0];
else if (en)
lfsr_q <= next_val;
end
assign rnd = lfsr_q;
endmodule
3 Testbench--tb_lfsr.v
cpp
`timescale 1ns/1ps
module tb_lfsr;
reg clk = 0;
reg rst_n;
reg en;
wire [7:0] rnd;
// 100 MHz clock
always #5 clk = ~clk;
// DUT
lfsr_fib #(.N(8), .SEED(8'hA5)) uut (
.clk (clk),
.rst_n (rst_n),
.en (en),
.rnd (rnd)
);
// Track unique values to verify max-length
reg [255:0] seen; // bitmap: 1 bit per possible value
integer count;
initial begin
//$dumpfile("lfsr_wave.vcd");
//$dumpvars(0, tb_lfsr);
seen = 0;
count = 0;
rst_n = 0;
en = 1;
// Hold reset for 3 clocks
#30;
rst_n = 1;
// Run for full period (255 clocks for 8-bit)
repeat (256) @(posedge clk);
$display("LFSR period verification complete.");
#20;
$finish;
end
// Count unique outputs
always @(posedge clk) begin
if (rst_n && en) begin
seen[rnd] = 1'b1;
count = count + 1;
if (count <= 10)
$display("t=%0t rnd = 0x%0h", $time, rnd);
end
end
endmodule
2.3 仿真测试
Vivado 仿真执行步骤
新建工程并添加源文件:添加lfsr_fib.v作为Design Source,tb_lfsr.v作为Simulation Source。
运行 Behavioral Simulation:观察rnd7:0的变化模式,验证序列不重复。
验证最大长度:8-bit LFSR应在255个时钟后回到种子值0xA5,期间不出现全0。
综合验证:Run Synthesis → 检查Utilization报告和Timing Summary。

2.4 综合执行
添加并编写XDC约束à综合Run Synthesisà实现Run Implementationà生成位流Generate Bitstreamà下载à运行。
XDC-2LFSR 伪随机数发生器--constraints_lfsr.xdc
端口定义:8位LED7:0实时显示LFSR输出值。按键BTN0控制使能/暂停,BTN1复位。
cpp
## ============================================================
## Project: lfsr_fib
## Board: Digilent Basys3 (XC7A35T-1CPG236C)
## File: constraints_lfsr.xdc
## ============================================================
## ── 系统时钟 (100 MHz) ──
set_property PACKAGE_PIN W5 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
create_clock -add -name sys_clk -period 10.00 [get_ports clk]
## ── 控制按键 ──
set_property PACKAGE_PIN U18 [get_ports btn_en] ## BTN0 --- 使能/暂停
set_property IOSTANDARD LVCMOS33 [get_ports btn_en]
set_property PACKAGE_PIN T18 [get_ports btn_rst] ## BTN1 --- 复位
set_property IOSTANDARD LVCMOS33 [get_ports btn_rst]
## ── LED 输出 [7:0] ← LFSR rnd[7:0] ──
set_property PACKAGE_PIN U16 [get_ports {led[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
set_property PACKAGE_PIN E19 [get_ports {led[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property PACKAGE_PIN U19 [get_ports {led[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
set_property PACKAGE_PIN V19 [get_ports {led[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
set_property PACKAGE_PIN W18 [get_ports {led[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[4]}]
set_property PACKAGE_PIN U15 [get_ports {led[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[5]}]
set_property PACKAGE_PIN U14 [get_ports {led[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[6]}]
set_property PACKAGE_PIN V14 [get_ports {led[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[7]}]
3 基础FSM--交通灯控制器
3.1 方案规划
Finite State Machine Traffic Light Controller--基于Moore型有限状态机实现十字路口交通灯控制,含东西 / 南北两组红黄绿灯,支持定时切换。
1 状态机设计
采用Moore型FSM,输出仅与当前状态相关。定义4个状态:
🔴S0: EW_RED:东西红 | 南北绿 25s
🟡S1: EW_YEL:东西红 | 南北黄 5s
🔴S2: NS_RED:东西绿 | 南北红 25s
🟡S3: NS_YEL:东西黄 | 南北红 5s
2 状态转换
S0 → S1 → S2 → S3 → S0 (循环)
3.2 逻辑编码
RTL 源码--traffic_light.v
cpp
// ============================================================
// Module: traffic_light
// Moore FSM Traffic Light Controller
// Clock: 1 Hz (use a divider from system clock)
// ============================================================
module traffic_light
#(
parameter T_GREEN = 25, // green light duration (seconds)
parameter T_YELLOW = 5 // yellow light duration (seconds)
)(
input wire clk, // 1 Hz clock
input wire rst_n, // active-low reset
// East-West lights
output reg ew_red,
output reg ew_yellow,
output reg ew_green,
// North-South lights
output reg ns_red,
output reg ns_yellow,
output reg ns_green
);
// State encoding
localparam [1:0]
S0_EW_RED = 2'b00, // EW=Red, NS=Green
S1_EW_YEL = 2'b01, // EW=Red, NS=Yellow
S2_NS_RED = 2'b10, // EW=Green, NS=Red
S3_NS_YEL = 2'b11; // EW=Yellow, NS=Red
reg [1:0] state, next_state;
reg [7:0] counter;
// ===== Sequential: state register =====
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
state <= S0_EW_RED;
else
state <= next_state;
end
// ===== Sequential: timer counter =====
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
counter <= 8'd0;
else if (state != next_state)
counter <= 8'd0; // reset counter on state change
else
counter <= counter + 8'd1;
end
// ===== Combinational: next-state logic =====
always @(*) begin
next_state = state; // default: hold
case (state)
S0_EW_RED: if (counter >= T_GREEN - 1) next_state = S1_EW_YEL;
S1_EW_YEL: if (counter >= T_YELLOW - 1) next_state = S2_NS_RED;
S2_NS_RED: if (counter >= T_GREEN - 1) next_state = S3_NS_YEL;
S3_NS_YEL: if (counter >= T_YELLOW - 1) next_state = S0_EW_RED;
endcase
end
// ===== Combinational: output logic (Moore) =====
always @(*) begin
// defaults: all off
{ew_red, ew_yellow, ew_green} = 3'b000;
{ns_red, ns_yellow, ns_green} = 3'b000;
case (state)
S0_EW_RED: begin ew_red=1; ns_green=1; end
S1_EW_YEL: begin ew_red=1; ns_yellow=1; end
S2_NS_RED: begin ew_green=1; ns_red=1; end
S3_NS_YEL: begin ew_yellow=1; ns_red=1; end
endcase
end
endmodule
时钟分频--clk_divider.v
cpp
// 100 MHz → 1 Hz 分频器
module clk_divider #(
parameter CLK_IN = 100_000_000,
parameter CLK_OUT = 1
)(
input wire clk_in,
input wire rst_n,
output reg clk_out
);
localparam DIV = CLK_IN / (CLK_OUT * 2);
reg [25:0] cnt;
always @(posedge clk_in or negedge rst_n) begin
if (!rst_n) begin
cnt <= 0;
clk_out <= 0;
end else if (cnt >= DIV - 1) begin
cnt <= 0;
clk_out <= ~clk_out;
end else
cnt <= cnt + 1;
end
endmodule
Testbench--tb_traffic.v
cpp
`timescale 1ns/1ps
module tb_traffic;
reg clk_1hz = 0;
reg rst_n;
wire ew_red, ew_yellow, ew_green;
wire ns_red, ns_yellow, ns_green;
// Simulated 1 Hz clock (for fast simulation)
always #500 clk_1hz = ~clk_1hz; // 1μs = 1Hz scaled
traffic_light #(.T_GREEN(5), .T_YELLOW(2)) uut (
.clk (clk_1hz),
.rst_n (rst_n),
.ew_red (ew_red),
.ew_yellow (ew_yellow),
.ew_green (ew_green),
.ns_red (ns_red),
.ns_yellow (ns_yellow),
.ns_green (ns_green)
);
initial begin
//$dumpfile("traffic_wave.vcd");
//$dumpvars(0, tb_traffic);
rst_n = 0;
#100;
rst_n = 1;
// Simulate one full cycle: 5+2+5+2 = 14 ticks
repeat (30) @(posedge clk_1hz);
$display("Traffic light simulation complete.");
$finish;
end
endmodule
3.3 仿真测试
Vivado 仿真执行步骤
- 添加源文件并仿真:将traffic_light.v和tb_traffic.v加入工程。运行Behavioral Simulation。
- 验证状态转换时序:在波形窗口确认 S0(5s绿) → S1(2s黄) → S2(5s绿) → S3(2s黄) 循环。
- 可选:添加 RTL 分析:RTL Analysis → 查看综合后状态机原理图,确认状态转换逻辑正确。


3.4 综合执行
添加并编写XDC约束à综合Run Synthesisà实现Run Implementationà生成位流Generate Bitstreamà下载à运行。
FSM 交通灯控制器 ---constraints_traffic.xdc
端口定义:6 个 LED 模拟两组交通灯 (东西 / 南北)。EW 方向: LED0(红) LED1(黄) LED2(绿);NS 方向: LED3(红) LED4(黄) LED5(绿)。
cpp
## ============================================================
## Project: traffic_light (FSM)
## Board: Digilent Basys3 (XC7A35T-1CPG236C)
## File: constraints_traffic.xdc
## ============================================================
## ── 系统时钟 (100 MHz) ──
set_property PACKAGE_PIN W5 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
create_clock -add -name sys_clk -period 10.00 [get_ports clk]
## ── 复位按键 ──
set_property PACKAGE_PIN U18 [get_ports rst_n] ## BTN0 --- active LOW
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
## ── 东西方向 (East-West) ──
set_property PACKAGE_PIN U16 [get_ports ew_red] ## LED0 --- 红
set_property IOSTANDARD LVCMOS33 [get_ports ew_red]
set_property PACKAGE_PIN E19 [get_ports ew_yellow] ## LED1 --- 黄
set_property IOSTANDARD LVCMOS33 [get_ports ew_yellow]
set_property PACKAGE_PIN U19 [get_ports ew_green] ## LED2 --- 绿
set_property IOSTANDARD LVCMOS33 [get_ports ew_green]
## ── 南北方向 (North-South) ──
set_property PACKAGE_PIN V19 [get_ports ns_red] ## LED3 --- 红
set_property IOSTANDARD LVCMOS33 [get_ports ns_red]
set_property PACKAGE_PIN W18 [get_ports ns_yellow] ## LED4 --- 黄
set_property IOSTANDARD LVCMOS33 [get_ports ns_yellow]
set_property PACKAGE_PIN U15 [get_ports ns_green] ## LED5 --- 绿
set_property IOSTANDARD LVCMOS33 [get_ports ns_green]
4 综合应用--UART串口通信
4.1 方案规划
Universal Asynchronous Receiver-Transmitter--实现完整的UART发送器和接收器,内部环回(Loopback)验证数据正确性,支持可配置波特率。
UART帧格式(8N1: 8 数据位, 无校验, 1 停止位):

图6-9 UART帧格式(8N1)示意图
波特率 = 系统时钟 / 分频系数。例:100 MHz / 115200 ≈ 868 → 每个 bit 持续 868 个时钟周期。
4.2 逻辑编码
UART 发送器--uart_tx.v
cpp
// ============================================================
// Module: uart_tx
// UART Transmitter: 8N1 format
// ============================================================
module uart_tx #(
parameter CLK_FREQ = 100_000_000,
parameter BAUD = 115200
)(
input wire clk,
input wire rst_n,
input wire [7:0] tx_data,
input wire tx_start,
output reg tx_out,
output wire tx_busy
);
localparam DIV = CLK_FREQ / BAUD;
localparam CNT_W = $clog2(DIV);
localparam [3:0]
IDLE = 4'd0,
START = 4'd1,
DATA = 4'd2,
STOP = 4'd3;
reg [3:0] state;
reg [CNT_W-1:0] clk_cnt;
reg [2:0] bit_idx;
reg [7:0] shift_reg;
assign tx_busy = (state != IDLE);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
tx_out <= 1'b1;
clk_cnt <= 0;
bit_idx <= 0;
shift_reg<= 8'd0;
end else case (state)
IDLE: begin
tx_out <= 1'b1;
if (tx_start) begin
shift_reg <= tx_data;
state <= START;
clk_cnt <= 0;
end
end
START: begin
tx_out <= 1'b0;
if (clk_cnt == DIV - 1) begin
clk_cnt <= 0;
state <= DATA;
bit_idx <= 0;
end else
clk_cnt <= clk_cnt + 1;
end
DATA: begin
tx_out <= shift_reg[0];
if (clk_cnt == DIV - 1) begin
clk_cnt <= 0;
shift_reg <= {1'b0, shift_reg[7:1]};
if (bit_idx == 7)
state <= STOP;
else
bit_idx <= bit_idx + 1;
end else
clk_cnt <= clk_cnt + 1;
end
STOP: begin
tx_out <= 1'b1;
if (clk_cnt == DIV - 1)
state <= IDLE;
else
clk_cnt <= clk_cnt + 1;
end
endcase
end
endmodule
UART 接收器--uart_rx.v
cpp
// ============================================================
// Module: uart_rx
// UART Receiver: 8N1 format, 16x oversampling midpoint
// ============================================================
module uart_rx #(
parameter CLK_FREQ = 100_000_000,
parameter BAUD = 115200
)(
input wire clk,
input wire rst_n,
input wire rx_in,
output reg [7:0] rx_data,
output reg rx_valid
);
localparam DIV = CLK_FREQ / BAUD;
localparam HALF = DIV / 2;
localparam CNT_W = $clog2(DIV);
localparam [3:0]
IDLE = 4'd0,
START = 4'd1,
DATA = 4'd2,
STOP = 4'd3;
reg [3:0] state;
reg [CNT_W-1:0] clk_cnt;
reg [2:0] bit_idx;
reg [7:0] shift_reg;
// 2-stage synchronizer for rx_in (meta-stability guard)
reg rx_sync1, rx_sync2;
always @(posedge clk) begin
rx_sync1 <= rx_in;
rx_sync2 <= rx_sync1;
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
clk_cnt <= 0;
bit_idx <= 0;
shift_reg <= 8'd0;
rx_data <= 8'd0;
rx_valid <= 1'b0;
end else begin
rx_valid <= 1'b0; // pulse
case (state)
IDLE: begin
if (!rx_sync2) begin // falling edge detected
state <= START;
clk_cnt <= 0;
end
end
START: begin
if (clk_cnt == HALF - 1) begin
if (!rx_sync2) begin // confirm start bit at midpoint
clk_cnt <= 0;
state <= DATA;
bit_idx <= 0;
end else
state <= IDLE; // false start
end else
clk_cnt <= clk_cnt + 1;
end
DATA: begin
if (clk_cnt == DIV - 1) begin
clk_cnt <= 0;
shift_reg[bit_idx] <= rx_sync2;
if (bit_idx == 7)
state <= STOP;
else
bit_idx <= bit_idx + 1;
end else
clk_cnt <= clk_cnt + 1;
end
STOP: begin
if (clk_cnt == DIV - 1) begin
state <= IDLE;
rx_data <= shift_reg;
rx_valid <= 1'b1;
end else
clk_cnt <= clk_cnt + 1;
end
endcase
end
end
endmodule
环回测试顶层--uart_loopback.v
cpp
// ============================================================
// Module: uart_loopback
// Connects TX output directly to RX input for verification
// ============================================================
module uart_loopback #(
parameter CLK_FREQ = 100_000_000,
parameter BAUD = 115200
)(
input wire clk,
input wire rst_n,
input wire [7:0] tx_data,
input wire tx_start,
output wire [7:0] rx_data,
output wire rx_valid,
output wire tx_busy
);
wire loop_wire; // internal loopback wire
uart_tx #(.CLK_FREQ(CLK_FREQ), .BAUD(BAUD)) u_tx (
.clk (clk),
.rst_n (rst_n),
.tx_data (tx_data),
.tx_start (tx_start),
.tx_out (loop_wire),
.tx_busy (tx_busy)
);
uart_rx #(.CLK_FREQ(CLK_FREQ), .BAUD(BAUD)) u_rx (
.clk (clk),
.rst_n (rst_n),
.rx_in (loop_wire), // loopback: tx_out → rx_in
.rx_data (rx_data),
.rx_valid (rx_valid)
);
endmodule
Testbench--tb_uart_loopback.v
cpp
`timescale 1ns/1ps
module tb_uart_loopback;
// Use smaller divider for faster simulation
localparam CLK_FREQ = 10_000_000; // 10 MHz
localparam BAUD = 9600;
reg clk = 0;
reg rst_n;
reg [7:0] tx_data;
reg tx_start;
wire [7:0] rx_data;
wire rx_valid;
wire tx_busy;
// 10 MHz clock
always #50 clk = ~clk;
uart_loopback #(.CLK_FREQ(CLK_FREQ), .BAUD(BAUD)) uut (
.clk (clk),
.rst_n (rst_n),
.tx_data (tx_data),
.tx_start (tx_start),
.rx_data (rx_data),
.rx_valid (rx_valid),
.tx_busy (tx_busy)
);
integer pass_cnt, fail_cnt;
initial begin
$dumpfile("uart_loopback.vcd");
$dumpvars(0, tb_uart_loopback);
rst_n = 0;
tx_data = 0;
tx_start = 0;
pass_cnt = 0;
fail_cnt = 0;
#200;
rst_n = 1;
#500;
// Send test patterns
send_byte(8'h55); // 0101_0101
send_byte(8'hAA); // 1010_1010
send_byte(8'h00);
send_byte(8'hFF);
send_byte(8'hA5);
send_byte(8'h3C);
#1_000_000; // wait for last byte
$display("\n===== UART Loopback Results =====");
$display("PASS: %0d FAIL: %0d", pass_cnt, fail_cnt);
$finish;
end
task send_byte;
input [7:0] data;
begin
while (tx_busy) @(posedge clk);
@(posedge clk);
tx_data = data;
tx_start = 1;
@(posedge clk);
tx_start = 0;
// Wait for rx_valid
@(posedge rx_valid);
if (rx_data == data) begin
$display("PASS: sent 0x%0h recv 0x%0h", data, rx_data);
pass_cnt = pass_cnt + 1;
end else begin
$display("FAIL: sent 0x%0h recv 0x%0h", data, rx_data);
fail_cnt = fail_cnt + 1;
end
#50_000; // gap between bytes
end
endtask
endmodule
4.3 仿真测试
Vivado 仿真执行步骤
- 添加4个源文件:uart_tx.v、uart_rx.v为Design Sources;uart_loopback.v为Design Source + 模块例化;tb_uart_loopback.v为Simulation Source。
- 运行Behavioral Simulation:观察tx_out的UART帧波形,确认start bit → 8 data bits (LSB first) → stop bit。
- 检查环回结果:Tcl Console 查看$display 输出,验证所有6个测试字节均PASS。
- 硬件验证(可选):添加Constraints → 分配 UART TX/RX 引脚 → Generate Bitstream → 使用串口终端 115200 baud测试。


4.4 综合执行
添加并编写XDC约束à综合Run Synthesisà实现Run Implementationà生成位流Generate Bitstreamà下载à运行。
UART串口通信--constraints_uart.xdc
端口定义:USB-UART桥接芯片(115200 baud, 8N1)。LED0指示TX busy,LED1指示 X valid。SW0手动触发发送测试字节。
UART 串口通信--constraints_uart.xdc
端口定义:USB-UART桥接芯片 (115200 baud, 8N1)。LED0 指示TX busy,LED1指示 RX valid。SW0手动触发发送测试字节。
cpp
## ============================================================
## Project: uart_loopback
## Board: Digilent Basys3 (XC7A35T-1CPG236C)
## File: constraints_uart.xdc
## ============================================================
## ── 系统时钟 (100 MHz) ──
set_property PACKAGE_PIN W5 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
create_clock -add -name sys_clk -period 10.00 [get_ports clk]
## ── USB-UART 引脚 (通过 MicroUSB 口连接 PC) ──
set_property PACKAGE_PIN A18 [get_ports uart_tx] ## FPGA TX → PC RX
set_property IOSTANDARD LVCMOS33 [get_ports uart_tx]
set_property PACKAGE_PIN B18 [get_ports uart_rx] ## PC TX → FPGA RX
set_property IOSTANDARD LVCMOS33 [get_ports uart_rx]
## ── 状态指示 LED ──
set_property PACKAGE_PIN U16 [get_ports led_tx_busy] ## LED0 --- TX 忙
set_property IOSTANDARD LVCMOS33 [get_ports led_tx_busy]
set_property PACKAGE_PIN E19 [get_ports led_rx_valid] ## LED1 --- RX 有效
set_property IOSTANDARD LVCMOS33 [get_ports led_rx_valid]
## ── 手动触发发送 ──
set_property PACKAGE_PIN V17 [get_ports tx_trigger] ## SW0 --- 触发发送
set_property IOSTANDARD LVCMOS33 [get_ports tx_trigger]
5 MicroBlaze-V SoC
5.1 方案规划
MicroBlaze-V SoC--按键输入 / LED 指示:基于MicroBlaze-V软核处理器搭建SoC系统,在Vivado中完成Block Design,在Vitis中编写嵌入式C程序实现按键读取与LED控制。

MicroBlaze-V是Xilinx 32位RISC软核处理器,在Vivado Block Design中搭建如下图。

5.2 系统设计
++Vivado Block Design++ ++创建步骤++
1 创建新工程:File → New Project → RTL Project → 选择目标FPGA(如XC7A35T)。勾选Do not specify source。
2 创建Block Design:Flow Navigator → IP INTEGRATOR → Create Block Design → 命名为mb_soc
3 添加MicroBlaze-V处理器:点击Add IP → 搜索MicroBlaze-V→ 双击添加。
4 运行Block Automation:点击顶部 Run Block Automation → 勾选 Clock → 确认。系统自动添加 Clock Wizard、Clock & Reset_Wizard、MDMDebug,用于JTAG下载和在线调试、ILMB/DLDM模块。进而双击Clock Wizard,选择single clock。
5 添加AXI GPIO (按键输入):Add IP → AXI GPIO → 双击配置:GPIO Width = 4,勾选 All Inputs。
6 添加AXI GPIO (LED 输出):再添加一个 AXI GPIO → 配置:GPIO Width = 4,勾选 All Outputs。
7 连接 AXI 总线:点击 Run Connection Automation → 勾选所有模块选项 → 确认。系统自动完成各个模块连接线,如图6-14所示。
8 生成输出产品 & 封装
右键Block Design → Generate Output Products → 全局 → Generate。
然后在sources下的mb_soc.bd上,右键菜单,选择Create HDL Wrapper → Let Vivado manage → OK。

5.3 综合实现
1 添加约束文件
Add Sources → Add Constraints → 添加constraints.xdc
MicroBlaze-V SoC--constraints_mb_soc.xdc
端口定义:与Block Design中GPIO端口名对应。btn_tri_i3:0→ AXI GPIO输入(按键),led_tri_o3:0→ AXI GPIO输出(LED)。顶层wrapper端口名需与Block Design一致。
cpp
# ============================================================
## Project: MicroBlaze-V SoC (按键输入 / LED 指示)
## Board: Digilent Basys3 (XC7A35T-1CPG236C)
## File: constraints_mb_soc.xdc
## ============================================================
## 注意: 端口名必须与 Block Design wrapper 一致
## 如果 wrapper 自动生成,请根据实际端口名调整
## ── 系统时钟 (100 MHz) ──
set_property PACKAGE_PIN W5 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
create_clock -add -name sys_clk -period 10.00 [get_ports clk]
## ── 复位 (CPU_RESETN 按键,低电平有效) ──
set_property PACKAGE_PIN R2 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
## ── 按键输入 BTN[3:0] → AXI GPIO (gpio_btn_io) ──
set_property PACKAGE_PIN U18 [get_ports {btn_tri_i[0]}] ## BTN0
set_property IOSTANDARD LVCMOS33 [get_ports {btn_tri_i[0]}]
set_property PACKAGE_PIN T18 [get_ports {btn_tri_i[1]}] ## BTN1
set_property IOSTANDARD LVCMOS33 [get_ports {btn_tri_i[1]}]
set_property PACKAGE_PIN W19 [get_ports {btn_tri_i[2]}] ## BTN2
set_property IOSTANDARD LVCMOS33 [get_ports {btn_tri_i[2]}]
set_property PACKAGE_PIN T17 [get_ports {btn_tri_i[3]}] ## BTN3
set_property IOSTANDARD LVCMOS33 [get_ports {btn_tri_i[3]}]
## ── LED 输出 LED[3:0] ← AXI GPIO (gpio_led_io) ──
set_property PACKAGE_PIN U16 [get_ports {led_tri_o[0]}] ## LED0
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[0]}]
set_property PACKAGE_PIN E19 [get_ports {led_tri_o[1]}] ## LED1
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[1]}]
set_property PACKAGE_PIN U19 [get_ports {led_tri_o[2]}] ## LED2
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[2]}]
set_property PACKAGE_PIN V19 [get_ports {led_tri_o[3]}] ## LED3
set_property IOSTANDARD LVCMOS33 [get_ports {led_tri_o[3]}]
2 综合实现
Run Synthesis → Run Implementation → Generate Bitstream
综合后检查 Utilization → 实现后检查 Timing → 生成比特流。

3 导出硬件到Vitis
File → Export → Export Hardware → 勾选Include bitstream→ Export。

5.4 嵌入应用
1 Vitis Embadded Development 嵌入式开发
通过FPGAs_AdaptiveSoCs_Unified_SDI_2025.2_1114_2157_Win64.exe,选择安装Vitis Embadded Development而不是Vitis,特别说明。

操作步骤:
A **.**启动Vitis开始菜单 → AMD Xilinx → Vitis (或从Vivado: Tools → Launch Vitis IDE,通常会自动启动Vitis)。
B **.**创建 Platform Component(生成BSP):File → New → Platform Component。


C **.**创建Application Component:File → New → Application Component。


D **.**编写C代码(main.c)

注意:出现include头文件找不到,可以通过build完成构建,就引入了相应头文件,相应C程序中的错误提示也没有了。
cpp
/* ============================================================
* Enhanced: Button toggles between LED display modes
* BTN0: cycle mode | BTN1: manual bit 0 | BTN2-3: reserved
* Mode 0: all off | Mode 1: all on | Mode 2: walking LED
* ============================================================ */
#include "xparameters.h"
#include "xgpio.h"
#include "xil_printf.h"
XGpio btn_gpio, led_gpio;
void delay_ms(u32 ms) {
volatile u32 cnt = ms * 5000;
while (cnt--) __asm__ volatile("nop");
}
int main() {
u32 btn, prev_btn = 0;
u8 mode = 0;
u8 walk_bit = 0;
XGpio_Initialize(&btn_gpio, XPAR_AXI_GPIO_0_BASEADDR);
XGpio_Initialize(&led_gpio, XPAR_AXI_GPIO_1_BASEADDR);
XGpio_SetDataDirection(&btn_gpio, 1, 0xF);
XGpio_SetDataDirection(&led_gpio, 1, 0x0);
xil_printf("Enhanced Button-LED Demo\r\n");
xil_printf("BTN0: cycle mode | BTN1: manual mode\r\n");
while (1) {
btn = XGpio_DiscreteRead(&btn_gpio, 1) & 0xF;
// Detect button press (rising edge)
if ((btn & 0x1) && !(prev_btn & 0x1)) {
mode = (mode + 1) % 4;
xil_printf("Mode: %d\r\n", mode);
}
// Execute current mode
switch (mode) {
case 0: // all off
XGpio_DiscreteWrite(&led_gpio, 1, 0x0);
break;
case 1: // all on
XGpio_DiscreteWrite(&led_gpio, 1, 0xF);
break;
case 2: // walking LED
XGpio_DiscreteWrite(&led_gpio, 1, (1 << walk_bit));
walk_bit = (walk_bit + 1) % 4;
break;
case 3: // mirror buttons
XGpio_DiscreteWrite(&led_gpio, 1, btn);
break;
}
prev_btn = btn;
delay_ms(250); // ~4 Hz update for walking LED
}
}
2 下载与硬件运行
A **.**连接硬件并下载比特流
USB 连接 Basys3 → 在 Vivado 中: Open Target → Auto Connect → Program Device → 选择生成的.bit文件 → Program。
B . 在Vitis中下载并运行程序:右键mb_soc_app→ Run As → Launch on Hardware (System Debugger)。
程序将自动下载至 BRAM 并开始执行。
C **.**验证结果:按下 BTN0~BTN3,对应的LED0~LED3应实时点亮。