FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-技术分享-正文

从零到Offer:2026年FPGA实习生必备技能清单

二牛学FPGA二牛学FPGA
技术分享
6小时前
0
0
2

Quick Start:最短路径跑通一个FPGA工程

  • 步骤一:安装Vivado 2024.2或更高版本(Lite/WebPACK版免费),确保系统为Windows 10/11 64位或Ubuntu 20.04+。
  • 步骤二:下载Xilinx Artix-7(XC7A35T)板卡支持包,或使用Nexys A7-35T开发板。
  • 步骤三:新建Vivado工程,选择目标器件xc7a35tcsg324-1。
  • 步骤四:创建顶层Verilog文件(top.v),编写一个4位计数器模块。
  • 步骤五:添加约束文件(top.xdc),绑定板载100 MHz时钟到引脚,复位按键到GPIO。
  • 步骤六:运行综合(Synthesis)与实现(Implementation),生成比特流(bitstream)。
  • 步骤七:连接开发板,下载比特流,观察LED以约0.5 Hz频率闪烁(预期现象)。

前置条件与环境

项目/推荐值说明替代方案
器件/板卡Xilinx Artix-7 (XC7A35T) 或 Zynq-7000Altera Cyclone V (Intel Quartus);Lattice iCE40
EDA版本Vivado 2024.2 (WebPACK)Quartus Prime 23.1 Lite;ISE 14.7(仅限7系列)
仿真器Vivado Simulator (xsim) 或 ModelSim SE-64 2024.1Verilator 5.0+(仅仿真,不支持综合)
时钟/复位板载100 MHz差分/单端时钟;低电平有效复位外部晶振+RC复位电路
接口依赖USB-JTAG(Digilent Adept)Platform Cable USB II
约束文件XDC格式,包含时钟周期、引脚分配、时序例外SDC(Quartus);LDC(Lattice)

目标与验收标准

  • 功能点:实现一个UART收发器(波特率115200)、一个SPI主控制器、一个PWM发生器,以及一个AXI4-Lite从接口。
  • 性能指标:系统时钟100 MHz时,UART无丢帧(误码率<1e-9);SPI时钟最高50 MHz;PWM分辨率8位,频率可调。
  • 资源占用:LUT < 2000,FF < 1500,BRAM < 4块(以Artix-7为参考)。
  • Fmax:综合后时序报告显示所有路径建立时间裕量 > 0 ns,保持时间裕量 > 0 ns。
  • 验收方式:仿真波形验证各模块功能;上板后通过串口助手发送数据并回显,SPI驱动外部ADC读取数值,PWM驱动LED呼吸效果。

实施步骤

工程结构

  • 创建顶层模块top.v,例化所有子模块。
  • 子模块分目录存放:src/uart、src/spi、src/pwm、src/axi_lite。
  • 仿真文件单独放在sim/目录,与RTL隔离。
  • 约束文件约束时钟、复位、UART TX/RX、SPI CS/SCLK/MOSI/MISO、PWM输出引脚。

关键模块:UART收发器

module uart_tx (
    input  wire       clk,        // 100 MHz 系统时钟
    input  wire       rst_n,      // 低电平复位
    input  wire       start,      // 发送启动信号
    input  wire [7:0] data_in,    // 待发送数据
    output reg        tx,         // 串行输出
    output reg        busy        // 忙标志
);

    parameter BAUD_DIV = 868;     // 100MHz / 115200 ≈ 868
    reg [9:0] cnt;                // 波特率计数器
    reg [3:0] bit_cnt;            // 位计数器 (起始位+8数据位+停止位)
    reg [9:0] shift;              // 移位寄存器

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            tx &lt;= 1'b1;
            busy &lt;= 1'b0;
            cnt &lt;= 0;
            bit_cnt &lt;= 0;
            shift &lt;= 0;
        end else begin
            if (start &amp;&amp; !busy) begin
                busy &lt;= 1'b1;
                shift &lt;= {1'b1, data_in, 1'b0}; // 停止位+数据+起始位
                bit_cnt &lt;= 4'd10;
                cnt &lt;= 0;
            end else if (busy) begin
                if (cnt == BAUD_DIV-1) begin
                    cnt &lt;= 0;
                    tx &lt;= shift[0];
                    shift &lt;= {1'b1, shift[9:1]};
                    if (bit_cnt == 4'd1)
                        busy &lt;= 1'b0;
                    else
                        bit_cnt &lt;= bit_cnt - 1;
                end else begin
                    cnt &lt;= cnt + 1;
                end
            end
        end
    end
endmodule

逐行说明

  • 第1行:模块声明,定义输入时钟clk、复位rst_n、启动信号start、8位数据data_in;输出串行tx和忙标志busy。
  • 第8行:参数BAUD_DIV = 868,由100 MHz除以115200波特率计算得到,用于产生每位的时间间隔。
  • 第9-11行:内部寄存器cnt用于波特率计数,bit_cnt记录当前发送的位数(起始位+8数据位+停止位共10位),shift为移位寄存器,存储待发送的帧。
  • 第13行:always块对时钟上升沿和复位下降沿敏感,实现同步复位。
  • 第14-18行:复位时,tx拉高(空闲高电平),busy清零,计数器归零。
  • 第19-24行:检测到start且busy为低时,启动发送:busy置1,shift装入{停止位1, data_in, 起始位0},bit_cnt设为10,cnt清零。
  • 第25-36行:发送过程中,每当cnt计数到BAUD_DIV-1时,输出shift最低位到tx,右移shift,bit_cnt减1;当bit_cnt减到1时(即最后一位停止位已发送),busy清零。

关键模块:SPI主控制器

module spi_master (
    input  wire       clk,        // 100 MHz
    input  wire       rst_n,
    input  wire       start,
    input  wire [7:0] data_in,
    output reg [7:0]  data_out,
    output reg        sclk,
    output reg        mosi,
    input  wire       miso,
    output reg        cs_n,
    output reg        busy
);

    parameter CLK_DIV = 2;        // 100MHz / (2*2) = 25MHz SPI时钟
    reg [1:0] cnt;
    reg [2:0] bit_cnt;
    reg [7:0] shift_in, shift_out;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cs_n &lt;= 1'b1;
            sclk &lt;= 1'b0;
            mosi &lt;= 1'b0;
            busy &lt;= 1'b0;
            cnt &lt;= 0;
            bit_cnt &lt;= 0;
        end else if (start &amp;&amp; !busy) begin
            cs_n &lt;= 1'b0;          // 拉低片选
            busy &lt;= 1'b1;
            shift_out &lt;= data_in;
            bit_cnt &lt;= 3'd7;
            cnt &lt;= 0;
        end else if (busy) begin
            if (cnt == CLK_DIV-1) begin
                cnt &lt;= 0;
                sclk &lt;= ~sclk;     // 翻转时钟
                if (sclk) begin    // 下降沿采样
                    shift_in &lt;= {shift_in[6:0], miso};
                end else begin     // 上升沿输出
                    mosi &lt;= shift_out[7];
                    shift_out &lt;= {shift_out[6:0], 1'b0};
                    if (bit_cnt == 0) begin
                        busy &lt;= 1'b0;
                        cs_n &lt;= 1'b1;
                        data_out &lt;= shift_in;
                    end else
                        bit_cnt &lt;= bit_cnt - 1;
                end
            end else
                cnt &lt;= cnt + 1;
        end
    end
endmodule

逐行说明

  • 第1行:模块声明,包含时钟、复位、启动、输入输出数据、SPI四线接口(sclk, mosi, miso, cs_n)及忙标志。
  • 第12行:参数CLK_DIV=2,SPI时钟频率 = 100MHz / (2*2) = 25MHz,符合典型SPI从设备要求。
  • 第13-15行:内部寄存器cnt用于分频,bit_cnt记录剩余位数,shift_in和shift_out分别用于接收和发送移位。
  • 第17-24行:复位时,cs_n拉高(无效),sclk低,mosi低,busy清零。
  • 第25-29行:检测到start且busy为低时,拉低cs_n,busy置1,加载数据到shift_out,bit_cnt设为7(8位数据,从MSB开始),cnt清零。
  • 第30-47行:发送过程中,每CLK_DIV个时钟周期翻转sclk一次。在sclk下降沿(原sclk为高)采样miso到shift_in;在sclk上升沿(原sclk为低)将shift_out最高位输出到mosi,并左移。当bit_cnt减到0时,传输完成,busy清零,cs_n拉高,data_out更新为接收到的数据。

关键模块:PWM发生器

module pwm_gen (
    input  wire       clk,
    input  wire       rst_n,
    input  wire [7:0] duty,       // 占空比 0-255
    input  wire [15:0] period,    // 周期计数 (决定频率)
    output reg        pwm_out
);

    reg [15:0] cnt;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt &lt;= 0;
            pwm_out &lt;= 1'b0;
        end else begin
            if (cnt &gt;= period) begin
                cnt &lt;= 0;
                pwm_out &lt;= 1'b1;
            end else begin
                cnt &lt;= cnt + 1;
                if (cnt == duty)
                    pwm_out &lt;= 1'b0;
            end
        end
    end
endmodule

逐行说明

  • 第1行:模块声明,输入8位占空比duty和16位周期period,输出pwm_out。
  • 第8行:16位计数器cnt,用于跟踪当前周期位置。
  • 第10-12行:复位时cnt清零,pwm_out输出低。
  • 第13-22行:每个时钟周期cnt递增。当cnt达到period时,周期结束,cnt归零,pwm_out置高(开始新周期)。当cnt等于duty时,pwm_out置低(占空比结束)。这样产生一个周期为(period+1)个时钟周期、高电平持续(duty+1)个时钟周期的PWM波形。

关键模块:AXI4-Lite从接口

module axi_lite_slave (
    input  wire         clk,
    input  wire         rst_n,
    // 写地址通道
    input  wire [31:0]  awaddr,
    input  wire         awvalid,
    output reg          awready,
    // 写数据通道
    input  wire [31:0]  wdata,
    input  wire         wvalid,
    output reg          wready,
    // 写响应通道
    output reg [1:0]    bresp,
    output reg          bvalid,
    input  wire         bready,
    // 读地址通道
    input  wire [31:0]  araddr,
    input  wire         arvalid,
    output reg          arready,
    // 读数据通道
    output reg [31:0]   rdata,
    output reg [1:0]    rresp,
    output reg          rvalid,
    input  wire         rready
);

    // 内部寄存器示例
    reg [31:0] reg0, reg1;

    // 写事务处理
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            awready &lt;= 1'b0;
            wready &lt;= 1'b0;
            bvalid &lt;= 1'b0;
            bresp &lt;= 2'b00;
            reg0 &lt;= 0;
            reg1 &lt;= 0;
        end else begin
            // 地址与数据同时有效时,握手并写入
            if (awvalid &amp;&amp; wvalid &amp;&amp; !bvalid) begin
                awready &lt;= 1'b1;
                wready &lt;= 1'b1;
                case (awaddr[3:2])  // 按字对齐
                    2'b00: reg0 &lt;= wdata;
                    2'b01: reg1 &lt;= wdata;
                    default: ;
                endcase
                bresp &lt;= 2'b00;  // OKAY
                bvalid &lt;= 1'b1;
            end else if (bvalid &amp;&amp; bready) begin
                bvalid &lt;= 1'b0;
                awready &lt;= 1'b0;
                wready &lt;= 1'b0;
            end
        end
    end

    // 读事务处理
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            arready &lt;= 1'b0;
            rvalid &lt;= 1'b0;
            rresp &lt;= 2'b00;
            rdata &lt;= 0;
        end else begin
            if (arvalid &amp;&amp; !rvalid) begin
                arready &lt;= 1'b1;
                case (araddr[3:2])
                    2'b00: rdata &lt;= reg0;
                    2'b01: rdata &lt;= reg1;
                    default: rdata &lt;= 0;
                endcase
                rresp &lt;= 2'b00;
                rvalid &lt;= 1'b1;
            end else if (rvalid &amp;&amp; rready) begin
                rvalid &lt;= 1'b0;
                arready &lt;= 1'b0;
            end
        end
    end
endmodule

逐行说明

  • 第1-22行:模块声明,定义AXI4-Lite五个独立通道(写地址、写数据、写响应、读地址、读数据),每个通道包含valid/ready握手信号。
  • 第24-25行:内部寄存器reg0和reg1,作为从设备存储空间示例。
  • 第27-47行:写事务处理。复位时所有握手信号清零。当awvalid和wvalid同时有效且bvalid为低时,握手成功(awready/wready置1),根据地址低两位(字对齐)写入对应寄存器,bresp返回OKAY,bvalid置1。当bvalid和bready同时有效时,写响应完成,清除握手信号。
  • 第49-69行:读事务处理。复位时清零。当arvalid有效且rvalid为低时,握手成功(arready置1),根据地址读取对应寄存器到rdata,rresp返回OKAY,rvalid置1。当rvalid和rready同时有效时,读完成,清除握手信号。

时序/CDC与约束

  • 所有模块使用同一时钟域(100 MHz),避免跨时钟域问题。若引入异步复位,使用同步释放电路。
  • 约束文件示例:create_clock -period 10.000 -name sys_clk [get_ports clk]set_input_delay -clock sys_clk 2.0 [get_ports miso]set_output_delay -clock sys_clk 2.0 [get_ports {mosi sclk cs_n}]
  • 常见坑:未约束异步复位会导致保持时间违规;SPI sclk输出若不约束,综合工具可能插入缓冲器使时钟偏移过大。

验证

  • 编写testbench,例化所有模块,提供时钟和复位激励。
  • UART测试:发送0x55(交替位),观察tx波形应产生起始位0、8位数据(01010101)、停止位1。
  • SPI测试:发送0xA5,观察sclk、mosi、miso(回环连接)波形,验证数据正确。
  • PWM测试:设置duty=128,period=1000,验证pwm_out占空比约50%。
  • AXI测试:通过写reg0再读回,验证数据一致性。

上板验证

  • 连接开发板,使用Vivado Hardware Manager下载比特流。
  • UART:用串口助手(如Putty)打开对应COM口,发送字符应回显。
  • SPI:连接SPI ADC(如MCP3008),读取电压值并通过UART打印。
  • PWM:连接LED,观察呼吸效果。
  • 常见坑:引脚约束错误导致无输出;时钟频率过高导致SPI通信失败;UART波特率偏差过大(需用示波器测量实际波特率)。

原理与设计说明

FPGA设计本质是“用硬件描述语言描述数字电路”,与软件编程有本质区别。关键trade-off包括:

  • 资源 vs Fmax:组合逻辑层级越多,路径延迟越大,Fmax越低。例如UART的波特率计数器若用32位加法器,路径延迟可能超过10 ns,导致100 MHz时序违例。解决方案是流水线(pipeline)或使用专用进位链。
  • 吞吐 vs 延迟:SPI全双工模式可同时收发,但需要额外寄存器缓冲。若追求低延迟,可减少缓冲深度;若追求高吞吐,可增加FIFO。
  • 易用性 vs 可移植性:使用Xilinx原语(如BUFG、MMCM)可优化时序,但移植到Altera需重写。建议用标准Verilog描述逻辑,仅在必要时使用原语。

AXI4-Lite协议的核心是valid-ready握手:主设备置valid,从设备置ready,当两者同时为高时传输发生。这种机制允许主从设备在不同速率下工作,且易于流水线化。在实现中,注意避免死锁:若从设备在未准备好时一直拉低ready,主设备可能无限等待。

验证与结果

指标测量值(示例)测量条件
Fmax (UART)200 MHzVivado 2024.2, Artix-7 -1速度等级
Fmax (SPI)150 MHz同上,SPI时钟输出约束为50 MHz
Fmax (PWM
标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/41127.html
二牛学FPGA

二牛学FPGA

初级工程师
这家伙真懒,几个字都不愿写!
95819.43W3.99W3.67W
分享:
成电国芯FPGA赛事课即将上线
FPGA实习岗位技能匹配指南:Verilog与SystemVerilog的2026年企业倾向分析
FPGA实习岗位技能匹配指南:Verilog与SystemVerilog的2026年企业倾向分析上一篇
2026年FPGA实习生招聘指南:基于国产平台的实践经验与简历优化下一篇
2026年FPGA实习生招聘指南:基于国产平台的实践经验与简历优化
相关文章
总数:991
FPGA时序收敛技巧:时钟偏斜与建立时间优化

FPGA时序收敛技巧:时钟偏斜与建立时间优化

QuickStart打开Vivado(或Quartus),创建新工…
技术分享
5天前
0
0
13
0
FPGA项目实战:基于Verilog的SPI通信协议实现

FPGA项目实战:基于Verilog的SPI通信协议实现

QuickStart步骤一:安装Vivado2021.1或更高版…
技术分享
8天前
0
0
24
0
FPGA实现HDMI 2.0视频接口:TMDS编码与显示控制器设计

FPGA实现HDMI 2.0视频接口:TMDS编码与显示控制器设计

本技术文档详细阐述如何在FPGA上实现符合HDMI2.0标准的视频接口…
技术分享
21天前
0
0
53
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容