祝各位道友念头通达
GitHub Gitee 语雀 打赏

初学fpga笔记

fpga 国产和进口厂家

https://zhuanlan.zhihu.com/p/265486665

基础知识点

软件应用: 每个功能模块都是由n个小的模块完成的
扇出: 调用模块的个数, 调用的模块越多,则说明复杂度越高
扇入: 模块被调用的次数, 而每个模块被调用的次数越高,则表示复用度越高
硬件层面: 从fpga开发角度来说, fpga处理最终都会变成逻辑控制,最后运行的时候都会变成电流在线路中流动
扇出: 一个逻辑门的输入信号可控制馈送输出的信号的电路路数, 而输出的这些电信号可以控制其它电路的功能模块
扇入: 一个逻辑门能够被多个电路信号馈送输入信号, 而不影响它的正常工作,应该内部是有什么东西控制电压电流的

软硬件从思想上来讲是一样的, 但是硬件层面涉及到电压电流的影响,对此控制的更加严格,需要严格把控逻辑门的扇出和扇入之间的平衡关系

赛灵思系列

Xilinx®7系列FPGA包括四个FPGA家族,均为最低功耗设计使通用设计能够跨系列扩展,以获得最佳的功率、性能和成本。
Spartan®-7 系列是密度最低,成本最低的进入点7系列组合。
Artix®-7 系列优化的最高性能每瓦特和对成本敏感的高容量应用的每瓦带宽。
Kintex®-7 系列是一种创新的fpga优化为最佳的价格性能。
Virtex®-7 系列优化了最高的系统性能和容量。

zynq-7000 属于 Artix 系列

IO资源

就是输入和输出单元简称为I/O单元,可编程,不同厂家,不同芯片的器件的FPGA所支持的I/O标准不同,常见的电器标准由 LVTTL, LVCMOS, SSTL,HSTL, LVDS, LVPECL, PCI 等

时钟

不同的芯片包含了不同的时钟资源, 在赛灵思7010中包含时钟: BUFG,BUFR,BUFIO,BUFH,BUFMR,high-performance clock
BUFG: 它的输出到达FPGA内部的IOB,CLB.块RAM的时钟延时和抖动最小, 每组都能够独立的支持不同的I/O标准
BUFR: 是regional 时钟网络, 它驱动范围只能局限在一个clock region的逻辑当中,相比BUFG, 功耗和偏斜比较小
BUFIO: 是IO时钟网络, 独立于全局时钟资源, 适合采集同步数据。他只能驱动IO Block 里面的逻辑
IDDR: 7系列设备的ILOGIC block 中有专属的 registers 来实现的 input double-data-rate(IDDR) registers, 将输入上下边沿的DDR信号,转换成两位单边沿的SDR 信号

IOB是可编程输入输出单元,是fpga与外界电路的接口部分。用于完成不同电气特性下对输入/输出信号的驱动和匹配要求,FPGA的IOB被划分为若干个组(bank)
CLB 可配置逻辑块,每个CLB 里包含两个逻辑片(Slice)。每个 Slice 由 4 个查找表、8 个触发器和其他一些逻辑所组成的。
除了块 RAM,还可以灵活地将 LUT 配置成 RAM,ROM,FIFO 等存储结构,这种技术被称为分布式 RAM
image

原语

IDELAYE2 延时

将一个线上的信号延时一段时间输出, 用于高速边沿采样

格式

// 用于对 IDELAYE2 的校准, 需要输入 200Mhz的时钟频率
(* IODELAY_GROUP = "rgmii_rx_delay" *)
IDELAYCTRL  IDELAYCTRL_inst (
    .RDY(),                      // 1-bit output: Ready output
    .REFCLK(idelay_clk),         // 1-bit input: Reference clock input
    .RST(1'b0)                   // 1-bit input: Active high reset input
);

(* IODELAY_GROUP = "rgmii_rx_delay" *)
IDELAYE2 #(
  .IDELAY_TYPE     ("FIXED"),           // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
  .IDELAY_VALUE    (1),      			// Input delay tap setting (0-31)
  .REFCLK_FREQUENCY(200.0)              // IDELAYCTRL clock input frequency in MHz 
)
u_delay_rx_ctrl (
  .CNTVALUEOUT     (),                  // 5-bit output: Counter value output
  .DATAOUT         (rgmii_rx_ctl_delay),// 1-bit output: Delayed data output
  .C               (1'b0),              // 1-bit input: Clock input
  .CE              (1'b0),              // 1-bit input: enable increment/decrement
  .CINVCTRL        (1'b0),              // 1-bit input: Dynamic clock inversion input
  .CNTVALUEIN      (5'b0),              // 5-bit input: Counter value input
  .DATAIN          (1'b0),              // 1-bit input: Internal delay data input
  .IDATAIN         (rx1),      			// 1-bit input: Data input from the I/O
  .INC             (1'b0),              // 1-bit input: Increment / Decrement tap delay
  .LD              (1'b0),              // 1-bit input: Load IDELAY_VALUE input
  .LDPIPEEN        (1'b0),              // 1-bit input: Enable PIPELINE register
  .REGRST          (1'b0)               // 1-bit input: Active-high reset tap-delay input
);

image

延迟的计算方式

f 为 REFCLK_FREQUENCY
增量延迟时间: delay_resolution = 1/(32 * 2 * f)*1000000(ps);
整个延时时间: delay_time = 600ps + tap * delay_resolution;
摘自: https://blog.csdn.net/weixin_45372778/article/details/122026121

原语 design 查看布线方式

module primite_test (
    input sys_clk,
    input rx1,
    input idelay_clk,
    output tx1
);
assign tx1 = sys_clk;
wire [1:0] gmii_rxdv_t;
wire rgmii_rxc_bufio;
BUFIO BUFIO_inst (
  .I            (sys_clk),      // 1-bit input: Clock input
  .O            (rgmii_rxc_bufio) // 1-bit output: Clock output
);
wire rgmii_rx_ctl_delay;
//输入延时控制, 对IDELAYE2的校验
(* IODELAY_GROUP = "rgmii_rx_delay" *) 
IDELAYCTRL  IDELAYCTRL_inst (
    .RDY(),                      // 1-bit output: Ready output
    .REFCLK(idelay_clk),         // 1-bit input: Reference clock input
    .RST(1'b0)                   // 1-bit input: Active high reset input
);
//rgmii_rx_ctl 输入延时与双沿采样
(* IODELAY_GROUP = "rgmii_rx_delay" *) 
IDELAYE2 #(
  .IDELAY_TYPE     ("FIXED"),           // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
  .IDELAY_VALUE    (1),      // Input delay tap setting (0-31)
  .REFCLK_FREQUENCY(200.0)              // IDELAYCTRL clock input frequency in MHz 
)
u_delay_rx_ctrl (
  .CNTVALUEOUT     (),                  // 5-bit output: Counter value output
  .DATAOUT         (rgmii_rx_ctl_delay),// 1-bit output: Delayed data output
  .C               (1'b0),              // 1-bit input: Clock input
  .CE              (1'b0),              // 1-bit input: enable increment/decrement
  .CINVCTRL        (1'b0),              // 1-bit input: Dynamic clock inversion input
  .CNTVALUEIN      (5'b0),              // 5-bit input: Counter value input
  .DATAIN          (1'b0),              // 1-bit input: Internal delay data input
  .IDATAIN         (rx1),               // 1-bit input: Data input from the I/O
  .INC             (1'b1),              // 1-bit input: Increment / Decrement tap delay
  .LD              (1'b0),              // 1-bit input: Load IDELAY_VALUE input
  .LDPIPEEN        (1'b0),              // 1-bit input: Enable PIPELINE register
  .REGRST          (1'b0)               // 1-bit input: Active-high reset tap-delay input
);

//输入双沿采样寄存器,  原语 IDDR
IDDR #(
    .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"),// "OPPOSITE_EDGE", "SAME_EDGE" 
                                        //    or "SAME_EDGE_PIPELINED" 
    .INIT_Q1  (1'b0),                   // Initial value of Q1: 1'b0 or 1'b1
    .INIT_Q2  (1'b0),                   // Initial value of Q2: 1'b0 or 1'b1
    .SRTYPE   ("SYNC")                  // Set/Reset type: "SYNC" or "ASYNC" 
) u_iddr_rx_ctl (
    .Q1       (gmii_rxdv_t[0]),         // 1-bit output for positive edge of clock
    .Q2       (gmii_rxdv_t[1]),         // 1-bit output for negative edge of clock
    .C        (rgmii_rxc_bufio),        // 1-bit clock input
    .CE       (1'b1),                   // 1-bit clock enable input
    .D        (rgmii_rx_ctl_delay),     // 1-bit DDR data input
    .R        (1'b0),                   // 1-bit reset
    .S        (1'b0)                    // 1-bit set
);
endmodule //primite_test

image

image

image

白色(选中状态)的线布线方式即为 buffio 的布线方式
image

语法知识点

always @ (posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt <= 27'd0;
    else if(cnt < 27'd10000_0000)
        cnt <= cnt + 1'b1;
    else
        cnt <= 27'd0;
end

&和&&, |和||

  1. | 和 & 是对每个数据位相互的之间的 && 和 ||, 适用于位宽
  2. ||和 && 是对两个值进行判断的, 不分位宽, 适用于判断

posedeg/negedge

  1. posedeg 上升沿,低电平变为高电平, 上述语句表示时钟信号的上升沿, 当然也可以表示复位信号的上升沿 posedge sys_rst_n
  2. negedge 下降沿,高电平变为低电平

赋值操作

RHS: 赋值等号右边的表达式或变量 可以写作RHS表达式或RHS变量
LHS: 赋值等号左边的表达式或变量 可以写作LHS表达式或LHS变量

  1. <= 表示非阻塞赋值
  2. = 阻塞赋值
    要注意的一点是, 再fpga当中,语句是并行的,给出如代码和时序图所示
    image
    image
    如上图所示, 再时钟上升沿的时候, 对应的ABC的值都是不同的,这个 <= 非阻塞赋值也就是让这类语句是并行执行的
    如果想要串行执行,也就是上一条语句执行完之后,再执行下一个语句,需要用赋值 =,代码和时序图如下所示:
    image
    image

拼接符操作

A = 4'b1010 ;
B = 1'b1 ;
Y1 = {B, A[3:2], A[0], 4'h3 };  //结果为Y1='b1100_0011
Y2 = {4{B}, 3'd4};  //结果为 Y2=7'b111_1100
Y3 = {32{1'b0}};  //结果为 Y3=32h0,常用作寄存器初始化时匹配位宽的赋初值

assign和always区别

  1. assign 语句使用时候不能带时钟
  2. always 语句可以带时钟,在不带时钟的时候和assign一致,只产生组合逻辑
  3. 简单的组合逻辑使用assign, 复杂的组合逻辑使用always语句

wire和reg的区别

wire 本质是一条没有逻辑的连线, 输入是什么输出就是什么
reg 表示寄存器类型, 在always赋值的信号必须使用reg类型,相当于存储器,具体类型看使用场景
wire对应于连续赋值,如assign
reg对应于过程赋值,如always,initial

寄存器与锁存器

寄存器

在实际的数字系统中,通常把能够用来存储一组二进制代码的同步时序逻辑电路称为寄存器.由于触发器内有记忆功能,因此利用触发器可以方便地构成寄存器。由于一个触发器能够存储一位二进制码,所以把n个触发器的时钟端口连接起来就能构成一个存储n位二进制码的寄存器。

always @ (posedge clk or negedge rst_n)
	if (!rst_n)
		q <= 0;
	else if (load)
		q <= d;

锁存器

由若干个钟控D触发器构成的一次能存储多位二进制代码的时序逻辑电路。数据有效迟后于时钟信号有效。这意味着时钟信号先到,数据信号后到。在某些运算器电路中有时采用锁存器作为数据暂存器。

always @ (enable)
	if (enable) ?q <= d;

触发器

触发器:我们把输出只在时钟某个时刻变化的玩意儿叫触发器。边沿敏感

always @ (posedge enable)
	if (enable) ?q <= d;

寄存器与锁存器的区别

寄存器由同步时钟控制,锁存器由电位信号控制。锁存器一般由电平信号控制,属于电平敏感性, 寄存器一般由时钟信号控制,属于边缘敏感性

应用场景

使用寄存器还是锁存器,取决于控制信号和数据之间的时间关系:
若数据有效一定滞后于控制信号,则只能用锁存器;
数据提前于控制信号而到达且要求同步操作,则可以用寄存器来存放数据。

组合逻辑电路

是指在任何时刻,输出状态只决定于同一时刻各输入状态的组合,而与电路以前状态无关,而与其他时间的状态无关。

时序逻辑电路

是任意时刻的输出不仅取决于当时的输入信号,而且还取决于电路原来的状态,或者说,还与以前的输入有关。

锁相环

锁相环(PLL):其功能为对频率做分频或倍频处理, 去抖, 去斜
再不加M/N比例,直接输入数据和时钟,可以对时钟做恢复和数据的重定时
image

BRAM

一般数据从磁盘到CPU处理,要经过这么几个步骤,1.读取到内存,内存到寄存器,寄存器到CPU处理,其中寄存器是CPU内部结构,由一大堆的与或非门组成,
CPU内部有存储器,由一块块的SRAM(块RAM)构成, 对其配置,可以配置成各种存储器资源,如:

  1. RAM 随机存取存储器
  2. 移位寄存器
  3. ROM 只读寄存器
  4. FIFO 缓冲器

示例代码

led实验

`timescale 1ns / 1ps
module led_twinkle(
   input sys_clk,      //系统时钟
   input sys_rst_n,    // 系统复位, 低电平有效
   input [1:0] key,    // PL 按键
   output reg [1:0] led    //LED灯
);
reg [26:0] cnt;  //计时器
wire flag;        //标记,每隔500ms变化一次


// 改变状态
assign flag = (cnt <= 27'd2500_0000) ? 1'b1:1'b0;

// 利用时钟来计时
always @ (posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt <= 27'd0;
    else if(cnt < 27'd5000_0000)
        cnt <= cnt + 1'b1;
    else 
        cnt <= 27'd0;
end

// main code
// key0 和 key1 没有按下的时候, LED 0 1 保持常亮
// key0 按键按下的时候, LED 交替闪烁
// key1 按键按下的时候, LED 同时闪烁
// 根据电路图可知, 按键是接地的,所以 0 表示按下, 1表示没按下
always @(*) begin
    case(key)
        2'b11: //两个都没有按下 
            led <= 2'b11;
        2'b10: //按下 按键0
            if(!flag)
                led <= 2'b01;
            else
                led <= 2'b10;
        2'b01: //按下 按键1
            if(!flag)
                led <= 2'b00;
            else 
                led <= 2'b11;
        default: ;
    endcase
end
endmodule

xdc

#IO 管脚约束,
#U18 PL时钟输入,连接x1
#G15 PHY_RST_N
#H15 PL_LED0 GPIO
#H15 PL_LED1 GPIO
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN L14 IOSTANDARD LVCMOS33} [get_ports {key[0]}]
set_property -dict {PACKAGE_PIN K16 IOSTANDARD LVCMOS33} [get_ports {key[1]}]
set_property -dict {PACKAGE_PIN H15 IOSTANDARD LVCMOS33} [get_ports {led[0]}]
set_property -dict {PACKAGE_PIN L15 IOSTANDARD LVCMOS33} [get_ports {led[1]}]

蜂鸣器实验

实验说明, 在按下按键的时候, 消除抖动, 然后松开按钮,蜂鸣器延迟响应 1ms, 1ms内点击复位键消除响应
top_key_beep

`timescale 1ns / 1ps

module top_key_beep(
    input sys_clk,
    input sys_rst,
    input key,
    output beep
);

wire key_val;

// 当按键按下的时候
// 1. 当监测到第一次按下的时候, 开始(arm)延迟20ms, fpga是持续采样20ms
// 2. 如果持续采样的结果都一样,则认为是按下按钮
// 3. 如果按键值一样, 则让beep响 1000ms

key_debounce u_key_debounce (
    .sys_clk    (sys_clk),
    .sys_rst    (sys_rst),
    .key        (key),
    .key_val    (key_val)
);

beep_control u_beep_control(
    .sys_clk    (sys_clk),
    .sys_rst    (sys_rst),
    .key_val    (key_val),
    .beep       (beep)
);

endmodule

key_debounce

`timescale 1ns / 1ps
module key_debounce(
    input sys_clk,
    input sys_rst,
    input key,
    output reg key_val
);

reg [19:0] cnt;                   //计时器
reg cur_key;
reg pre_key;                      //记录上一次按键的值

parameter DELAY = 20'd100_0000;   //延时20ms, 计时 100_0000 次
parameter KEY_DOWN = 1'b0;        //按键按下的状态 

always @(posedge sys_clk or negedge sys_rst) begin
    if(!sys_rst) begin
        key_val <= 0;
        pre_key <= 0;
        cur_key <= 0;
        cnt <= 20'd0;
    end else if(key == KEY_DOWN) begin
        cur_key <= ~key;
        pre_key <= cur_key;
        if(pre_key == cur_key) 
            if(cnt > 0) 
                cnt <= cnt - 1'b1;
            else
                key_val <= 1;
        // else
        //     cnt <= 0;
    end else begin
        cur_key <= 1'b1;
        pre_key <= 1'b0;
        cnt <= DELAY;
        key_val <= 0;
    end

end

endmodule

beep_control

`timescale 1ns / 1ps
module beep_control(
    input sys_clk,
    input sys_rst,
    input key_val,
    output reg beep
);

parameter BEEP_DELAY = 32'd5000_0000;   // 用来延迟响应 1000ms
// parameter BEEP_DELAY = 32'd10;   // 用来延迟响应 10*20ns,用于仿真

reg [31:0] beep_cnt;

always @(posedge sys_clk or negedge sys_rst) begin
    if(!sys_rst) begin
        beep = 1'b0;
        beep_cnt <= 0;
    end else if(key_val) begin
        beep_cnt <= BEEP_DELAY;
        beep <= 1'b0;
    end else begin
        if(beep_cnt > 0) begin
            beep_cnt <= beep_cnt - 1'b1;
            beep <= 1'b1; 
        end else 
            beep <= 1'b0;
    end
end
endmodule

布线
image

按键实验

边沿触发/消除毛刺

`timescale 1ns / 1ps

module touch_led(
    input sys_clk,
    input sys_rst,
    input touch_key,
    output reg touch_led
);

// main code
// 触摸按键 LED 灯亮, 再次触摸按键,灯灭
// 边沿监测电路, 锁存器 的应用场景

//延迟一个时钟周期的节拍
reg edge1;
reg edge2;

wire up_out;      //上升沿监测
wire down_out;    //下降沿监测
wire all_out;     //全部监测

assign up_out = edge1 & ~edge2;
assign down_out = ~edge1 & edge2;
assign all_out = edge1 ^ edge2;

always @(posedge sys_clk or negedge sys_rst) begin
    if (!sys_rst) begin
        edge1 <= 0;
        edge2 <= 0;
    end else begin
        edge1 <= touch_key;
        edge2 <= edge1;
    end
end

always @(posedge sys_clk or negedge sys_rst)
    if(!sys_rst)
        touch_led <= 0;
    else
        if(up_out)
            touch_led <= ~touch_led;

// 这两种方式不可取
// assign touch_led = down_out ? ~touch_led : touch_led;
// always @(*)
//     if(down_out)
//         touch_led <= ~touch_led;

endmodule

仿真
image

开发工具 vscode + DVT 相关配置文档

https://www.dvteclipse.com/documentation/sv_vscode/System_Requirements.html
https://www.bilibili.com/read/cv16195338

posted @ 2022-08-11 10:51  韩若明瞳  阅读(425)  评论(0编辑  收藏  举报