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

FPGA实习岗位:2026年企业更倾向Verilog还是SystemVerilog

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

Quick Start:快速判断你该学哪个

  • 步骤 1:打开主流招聘平台(如Boss直聘、猎聘、牛客网),搜索“FPGA实习生”、“数字IC实习生”、“芯片验证实习生”等关键词,筛选2026年发布的最新岗位。
  • 步骤 2:统计岗位JD中“Verilog”、“SystemVerilog”、“SV”的出现频次。建议至少统计50个岗位,记录两者单独出现和同时出现的比例。
  • 步骤 3:关注“职责描述”而非“任职要求”。很多JD在“任职要求”里笼统写“熟悉Verilog或SystemVerilog”,但“职责描述”里会明确写“使用SystemVerilog搭建UVM验证环境”或“使用Verilog完成RTL设计”。
  • 步骤 4:区分岗位方向
    设计岗(RTL Design):几乎100%要求Verilog,部分要求SystemVerilog用于testbench。
    验证岗(Verification):几乎100%要求SystemVerilog + UVM,Verilog仅作为基础。
    综合/实现岗(Synthesis/Implementation):主要用Verilog,偶尔涉及SystemVerilog的interface。
  • 步骤 5:查看企业类型
    外企/大型IC公司(如Intel、AMD、NVIDIA、Synopsys、Cadence):实习生岗位几乎全部要求SystemVerilog,因为验证占工作量的60%-70%。
    国内中小型FPGA公司/初创(如紫光同创、安路科技、高云半导体):更偏向Verilog,因为团队小、设计为主,验证流程不完善。
    研究所/军工:几乎只用Verilog,SystemVerilog支持有限(工具链老旧)。
  • 步骤 6:结论速查
    如果你目标是大厂/外企/IC验证岗:优先学SystemVerilog + UVM。
    如果你目标是FPGA设计/中小公司/研究所:优先学Verilog,SystemVerilog作为加分项。
    最稳妥策略:先精通Verilog(3-6个月),再学SystemVerilog(1-2个月)用于验证,同时掌握UVM框架。
  • 验收点:完成上述统计后,你应该能写出一个100字以内的个人学习路线图,并解释为什么。

前置条件与环境

项目/推荐值说明替代方案
EDA工具Vivado 2024.2 / Quartus Prime Pro 24.3Vivado 2023.x / Quartus 22.x(但SystemVerilog支持可能受限)
仿真器QuestaSim 2024.3 / VCS 2024.06 / XsimModelsim SE-64 2024.0(免费版功能受限)
器件/板卡Xilinx Artix-7 / Kintex-7 / Zynq-7000 系列开发板Altera Cyclone V / Intel Agilex 7
时钟/复位板载50MHz有源晶振,全局异步复位(低有效)100MHz晶振,同步复位(不推荐)
接口依赖UART-USB桥(CP2102/FT232)用于调试JTAG调试器(Digilent HS2/Xilinx Platform Cable)
约束文件XDC(Vivado)或SDC(Quartus),至少包含时钟周期、输入输出延迟、false_path无约束跑综合(仅用于功能验证,不可上板)
操作系统Windows 11 / Ubuntu 22.04 LTSCentOS 7(已停止维护,不推荐)
编程语言基础至少掌握C语言基础(指针、结构体、循环)Python也可,但C更贴近硬件思维

注意:以上版本号均为2026年5月可获取的典型配置,具体以各厂商官网最新发布为准。如果你使用的工具版本较老(如Vivado 2018.3),SystemVerilog的某些语法(如interface、assertion)可能不支持或行为不同。

目标与验收标准

  • 功能点
    • 能用Verilog独立完成一个中等复杂度的RTL模块(如SPI Master、UART、FIFO)。
    • 能用SystemVerilog搭建一个带随机约束的testbench,并完成覆盖率收集。
    • 理解UVM的基本框架(uvm_component、uvm_sequence、uvm_driver)。
  • 性能指标
    • 设计模块在目标器件上Fmax ≥ 150MHz(典型值,以实际约束为准)。
    • 验证覆盖率(代码覆盖率 + 功能覆盖率)≥ 90%。
  • 资源占用
    • 设计模块LUT ≤ 500,FF ≤ 400(以Artix-7为参考)。
  • 验收方式
    • 仿真通过:所有testcase pass,无时序违例。
    • 上板验证:LED闪烁/串口打印“Hello FPGA”等可观测现象。
    • 面试中能解释:为什么用Verilog写设计、为什么用SystemVerilog写验证。

实施步骤

阶段一:工程结构搭建

  • 1. 创建顶层目录:建议使用标准结构:
    project/
    ├── rtl/ # 所有Verilog/SystemVerilog设计文件
    ├── sim/ # 仿真文件(testbench、波形、脚本)
    ├── constr/ # 约束文件(XDC/SDC)
    ├── ip/ # IP核(如PLL、FIFO Generator)
    ├── scripts/ # Tcl脚本(自动编译、运行)
    ├── docs/ # 设计文档
    └── output/ # 综合/实现结果(bit、rpt)
  • 2. 创建Makefile或Tcl脚本:实现一键编译+仿真+综合。至少包含:
    make compile:调用Vivado/QuestaSim编译所有RTL和TB。
    make sim:运行仿真并导出波形。
    make synth:运行综合并生成资源报告。
    make clean:清理中间文件。
  • 3. 编写一个简单的“Hello World”模块:用Verilog写一个分频器,用SystemVerilog写一个testbench,验证分频输出。这一步确保工具链和环境正确。
  • 常见坑与排查
    • 坑1:Windows路径含中文或空格导致Vivado编译失败。→ 解决方案:路径全英文,无空格。
    • 坑2:QuestaSim默认不支持SystemVerilog,需在编译时加-sv选项。→ 解决方案:在do文件中写vlog -sv *.sv

阶段二:关键模块实现(以SPI Master为例)

Verilog设计代码(spi_master.v)

module spi_master #(
    parameter CLK_DIV = 4,  // 时钟分频系数,产生SCLK = CLK/CLK_DIV
    parameter DATA_WIDTH = 8
)(
    input  wire       clk,
    input  wire       rst_n,
    input  wire       start,
    input  wire [DATA_WIDTH-1:0] tx_data,
    output reg  [DATA_WIDTH-1:0] rx_data,
    output reg        sclk,
    output reg        mosi,
    input  wire       miso,
    output reg        cs_n,
    output reg        busy
);

    // 状态机定义
    typedef enum reg [1:0] {
        IDLE = 2'b00,
        TRANSFER = 2'b01,
        DONE = 2'b10
    } state_t;
    state_t state, next_state;

    reg [DATA_WIDTH-1:0] shift_reg;
    reg [3:0] bit_cnt;
    reg [7:0] clk_cnt;
    reg sclk_en;

    // 状态机第一段:状态更新
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            state <= IDLE;
        else
            state <= next_state;
    end

    // 状态机第二段:下一状态逻辑
    always_comb begin
        next_state = state;
        case (state)
            IDLE: begin
                if (start)
                    next_state = TRANSFER;
            end
            TRANSFER: begin
                if (bit_cnt == DATA_WIDTH-1 && clk_cnt == CLK_DIV-1)
                    next_state = DONE;
            end
            DONE: begin
                next_state = IDLE;
            end
        endcase
    end

    // 状态机第三段:输出逻辑
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cs_n <= 1'b1;
            sclk <= 1'b0;
            mosi <= 1'b0;
            busy <= 1'b0;
            rx_data <= '0;
            shift_reg <= '0;
            bit_cnt <= '0;
            clk_cnt <= '0;
            sclk_en <= 1'b0;
        end else begin
            case (state)
                IDLE: begin
                    cs_n <= 1'b1;
                    busy <= 1'b0;
                    sclk_en <= 1'b0;
                    if (start) begin
                        cs_n <= 1'b0;
                        busy <= 1'b1;
                        shift_reg <= tx_data;
                        bit_cnt <= 0;
                        clk_cnt <= 0;
                        sclk_en <= 1'b1;
                    end
                end
                TRANSFER: begin
                    if (clk_cnt == CLK_DIV/2 - 1) begin
                        sclk <= ~sclk;
                        if (sclk) begin  // SCLK下降沿采样MISO
                            shift_reg <= {shift_reg[DATA_WIDTH-2:0], miso};
                        end else begin   // SCLK上升沿更新MOSI
                            mosi <= shift_reg[DATA_WIDTH-1];
                        end
                    end
                    if (clk_cnt == CLK_DIV - 1) begin
                        clk_cnt <= 0;
                        bit_cnt <= bit_cnt + 1;
                    end else begin
                        clk_cnt <= clk_cnt + 1;
                    end
                end
                DONE: begin
                    cs_n <= 1'b1;
                    busy <= 1'b0;
                    rx_data <= shift_reg;
                    sclk_en <= 1'b0;
                end
            endcase
        end
    end

endmodule

逐行说明(Verilog设计)

  • 第1-2行:模块声明,使用parameter定义两个可配置参数:CLK_DIV(时钟分频系数,决定SCLK频率)和DATA_WIDTH(数据位宽)。这是Verilog-2001的ANSI风格端口声明,推荐用于新设计。
  • 第3-11行:端口列表。clk和rst_n是全局信号;start、tx_data是输入;rx_data、sclk、mosi、cs_n、busy是输出。注意所有输出都声明为reg类型,因为需要在always块中赋值。
  • 第14-18行:使用typedef enum定义状态机状态。这是SystemVerilog语法,但在Vivado/Quartus中Verilog也支持(需文件后缀为.sv)。如果用纯Verilog,应使用parameter定义状态值。
  • 第20-24行:内部寄存器声明。shift_reg是移位寄存器,bit_cnt是位计数器,clk_cnt是时钟分频计数器,sclk_en是SCLK使能信号。
  • 第27-31行:状态机第一段——时序逻辑更新当前状态。注意使用always_ff(SystemVerilog语法)或always @(posedge clk or negedge rst_n)(Verilog语法)。这里混合使用了SV语法,如果工具不支持,需改为纯Verilog写法。
  • 第34-43行:状态机第二段——组合逻辑计算下一状态。使用always_comb(SV语法)或always @(*)(Verilog语法)。注意在TRANSFER状态下,当位计数达到最后一位且时钟计数达到分频周期时,跳转到DONE。
  • 第46-82行:状态机第三段——时序逻辑输出。在IDLE状态,检测到start信号后拉低cs_n(片选有效),加载tx_data到shift_reg,使能sclk_en。在TRANSFER状态,通过clk_cnt产生分频时钟sclk,在sclk上升沿更新MOSI,下降沿采样MISO。注意这里用sclk的边沿作为条件,实际综合时可能产生时钟门控问题,更稳健的做法是用clk_cnt的中间值产生sclk使能信号,而不是直接生成sclk。
  • 第84-90行:DONE状态:拉高cs_n,清空busy,将shift_reg赋值给rx_data,关闭sclk_en。

SystemVerilog Testbench代码(spi_master_tb.sv)

`timescale 1ns/1ps

module spi_master_tb;

    // 参数
    parameter CLK_PERIOD = 20;  // 50MHz时钟周期20ns
    parameter CLK_DIV = 4;
    parameter DATA_WIDTH = 8;

    // 接口信号
    logic clk;
    logic rst_n;
    logic start;
    logic [DATA_WIDTH-1:0] tx_data;
    logic [DATA_WIDTH-1:0] rx_data;
    logic sclk;
    logic mosi;
    logic miso;
    logic cs_n;
    logic busy;

    // 待测模块实例化
    spi_master #(
        .CLK_DIV(CLK_DIV),
        .DATA_WIDTH(DATA_WIDTH)
    ) dut (
        .clk(clk),
        .rst_n(rst_n),
        .start(start),
        .tx_data(tx_data),
        .rx_data(rx_data),
        .sclk(sclk),
        .mosi(mosi),
        .miso(miso),
        .cs_n(cs_n),
        .busy(busy)
    );

    // 时钟生成
    initial begin
        clk = 0;
        forever #(CLK_PERIOD/2) clk = ~clk;
    end

    // 复位和测试激励
    initial begin
        // 初始化
        rst_n = 0;
        start = 0;
        tx_data = 8'hA5;
        miso = 0;
        #100;
        rst_n = 1;
        #100;

        // 发送数据
        @(posedge clk);
        start = 1;
        tx_data = 8'hA5;
        @(posedge clk);
        start = 0;

        // 等待传输完成
        wait(!busy);
        #50;

        // 检查结果
        if (rx_data == 8'hA5)
            $display("Test PASS: rx_data = %h", rx_data);
        else
            $display("Test FAIL: rx_data = %h, expected A5", rx_data);

        #200;
        $finish;
    end

    // 模拟MISO数据(回环模式)
    always_ff @(posedge sclk or negedge cs_n) begin
        if (!cs_n)
            miso <= mosi;  // 简单回环
        else
            miso <= 0;
    end

    // 波形导出
    initial begin
        $dumpfile("spi_master_tb.vcd");
        $dumpvars(0, spi_master_tb);
    end

endmodule

逐行说明(SystemVerilog Testbench)

  • 第1行`timescale 1ns/1ps定义时间单位和精度。1ns表示#1代表1ns,1ps表示精度到1ps。这是仿真必需,否则工具默认可能为1s。
  • 第3行:模块声明。Testbench不需要端口,因为它是顶层。
  • 第6-8行:参数定义,与DUT保持一致。CLK_PERIOD=20ns对应50MHz时钟。
  • 第11-21行:使用logic类型声明所有信号。SystemVerilog中logic可以替代wirereg,简化代码。注意miso是输入到DUT,但在TB中由我们驱动,所以声明为logic
  • 第24-40行:DUT实例化。使用#(.PARAM_NAME(VALUE))语法传递参数,这是SystemVerilog推荐的命名参数传递方式,比Verilog的位置参数更安全。
  • 第43-46行:时钟生成。使用initial块和forever循环产生周期为CLK_PERIOD的时钟。注意#(CLK_PERIOD/2)是延迟表达式,需要CLK_PERIOD是常数或参数。
  • 第49-70行:测试激励。先复位(rst_n=0)保持100ns,然后释放复位。在时钟上升沿拉高start并设置tx_data=8'hA5,下一个时钟沿拉低start。然后等待busy信号变低(表示传输完成),检查rx_data是否等于0xA5。注意wait(!busy)是SV的阻塞等待语句,Verilog中需用@(negedge busy)替代。
  • 第73-78行:模拟MISO数据。这里使用简单回环模式:将mosi直接赋值给miso。实际应用中,miso可能来自外部SPI从设备,这里只是为了验证DUT的发送和接收功能。
  • 第81-84行:波形导出。使用$dumpfile$dumpvars系统任务,生成VCD文件供GTKWave或Vivado查看。如果使用QuestaSim,可以用vcd filevcd add命令。

阶段三:时序与约束

  • 1. 创建主时钟约束:在XDC文件中写入create_clock -period 20.000 -name sys_clk [get_ports clk],对应50MHz输入时钟。
  • 2. 生成时钟约束:如果使用PLL或MMCM生成内部时钟,Vivado会自动识别,但建议手动约束生成时钟的相位和抖动。
  • 3. 输入输出延迟约束:对于SPI接口,需要约束mosi、sclk、cs_n相对于时钟的输出延迟,以及miso的输入延迟。典型值:set_output_delay -clock [get_clocks sclk] -max 5 [get_ports {mosi sclk cs_n}]
  • 4. 异步复位约束:使用set_false_path -from [get_ports rst_n] -to [get_regs *]将复位路径设为false path,避免时序分析报告大量违例。
  • 常见坑与排查
    • 坑1:忘记约束生成时钟,导致时序分析报告不准确。→ 解决方案:综合后打开“Report Timing Summary”,检查是否有unconstrained paths。
    • 坑2:SPI的sclk是内部生成的时钟,如果直接作为时钟信号使用,Vivado会报“clock gating”警告。→ 解决方案:将sclk作为普通数据信号处理,用使能信号代替时钟边沿。

阶段四:验证与上板

  • 1. 仿真验证:运行vsim -c -do "run -all"(QuestaSim)或xsim --runall(Vivado)。检查波形:cs_n在传输期间为低,sclk有8个周期,mosi在sclk上升沿更新,miso在下降沿采样。
  • 2. 综合与实现:运行synth_design -top spi_masterplace_designroute_design。检查资源报告:LUT、FF、IO是否在预算内。
  • 3. 生成比特流并下载:使用write_bitstream生成.bit文件,通过Vivado Hardware Manager或openFPGALoader下载到开发板。
  • 4. 上板验证:将SPI Master的输出连接到SPI Flash或DAC芯片(如AD5601),观察输出波形或用逻辑分析仪抓取。如果没有外部设备,可以将mosi和miso短接(回环),验证发送和接收数据一致。
  • 常见坑与排查
    • 坑1:上板后无输出。→ 检查:时钟是否正常(用示波器测clk pin)、复位是否释放(rst_n是否高电平)、电源是否稳定。
    • 坑2:仿真通过但上板失败。→ 检查:约束是否完整、是否有未初始化的寄存器、是否有组合逻辑环路。
标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/41125.html
二牛学FPGA

二牛学FPGA

初级工程师
这家伙真懒,几个字都不愿写!
95819.43W3.99W3.67W
分享:
成电国芯FPGA赛事课即将上线
2026年FPGA实习生招聘指南:大模型部署经验设计与验证实践
2026年FPGA实习生招聘指南:大模型部署经验设计与验证实践上一篇
FPGA实习岗位技能匹配指南:Verilog与SystemVerilog的2026年企业倾向分析下一篇
FPGA实习岗位技能匹配指南:Verilog与SystemVerilog的2026年企业倾向分析
相关文章
总数:991
FPGA实习生面试筹码积累指南:基于开源项目的PR实战与验证

FPGA实习生面试筹码积累指南:基于开源项目的PR实战与验证

QuickStart本指南面向FPGA实习生,通过参与开源项目(如pi…
技术分享
5小时前
0
0
3
0
从零基础到FPGA工程师:一份可执行的系统学习指南

从零基础到FPGA工程师:一份可执行的系统学习指南

QuickStart:最短路径跑通第一个FPGA工程本部分帮助你用最短…
技术分享
7天前
0
0
23
0
FPGA大赛实战指南 基于FPGA的实时语音识别与降噪项目设计(新手入门+开源资源+避坑技巧)

FPGA大赛实战指南 基于FPGA的实时语音识别与降噪项目设计(新手入门+开源资源+避坑技巧)

对于很多参加FPGA创新设计大赛的团队来说,尤其是刚接触电路设计的新人,…
技术分享
2个月前
0
0
155
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容