HDL FPGA 学习 - FPGA基本要素,开发流程,Verilog语法和规范、编写技巧

目录

Altera FPGA 基本要素

FPGA 开发流程和适用范围

设计和实施规范

顶层设计的要点

Verilog HDL

语法规范

编写规范

设计技巧


编辑整理 by Staok,始于 2021.2 且无终稿。转载请注明作者及出处。整理不易,请多支持。

本文件是“瞰百易”计划的一部分,尽量遵循“二项玻”定则,致力于与网络上碎片化严重的现象泾渭分明!

本文系广泛撷取、借鉴和整理,适合刚入门的人阅读和遵守,已经有较多经验的人看一看图个乐,如有错误恭谢指出!本文已经是长期积累和堆叠而形成一定规模,不必按照从前到后的顺序去看,可以挑感兴趣的章节去看。

本文为简述风格,本意即记录要点和便于快速拾起。

本文对应的 Github/Gitee 仓库地址,本文最新的原文 和 一些源码、备查手册等等 均放在里面。


Altera FPGA 基本要素

p.s 过于基础的概念不提,这不是入门帖。入门可以跳到 “O.0 值得跟着的学习网站” 章节进行摄入。

p.s 以下以 Cyclone IV E 系列 FPGA 为例。

  • FPGA基础资源选择:逻辑单元(LE)数量,内嵌存储器(M9K)数量(总 RAM Bits 数),乘法器数量,PLL 数量,I/O 数量,全局时钟网络数量等。

  • 板级电路组成:电源,时钟,复位,JTAG,固化配置信息 FLASH,外设。具体连接形式参考一些开发板和开源板子的原理图和 PCB。

    • 电源:核心电源(标识 VCCINT,低压版本 1.0V,非低压 1.2V),IO BANK(标识 VCCIOx(x = 1 到 8),电压 1.2V 到 3.3V),PLL(模拟 PLL 标识 VCCAx(x = 1、2 或 4),其地标识 GNDAx(x 同前),电压 2.5V;数字 PLL 标识 VCCD_PLLx(x = 1、2 或 4),电压 1.2V),外设供电。不同系列 FPGA 的供电措施不同,具体要看电器参数等手册,尽量使用推荐值。
    • 复位:上电后,FPGA 器件开始加载外部 FLASH 芯片的固化信息,加载完毕之后(最多 0.3s)再进行复位(低电平有效),阻容 RC 复位电路可选:R = 47kΩ,C = 10uF,3.3V 的 IO 标准下,充电到 1.7V 时间为 340ms。
  • 全局时钟网络:专用时钟网络走线,同一时钟到达不同寄存器的时间差可以被控制到很小的范围内。外部输入时钟信号要连接到 “全局时钟专用引脚” 上。FPGA 的综合工具会自动识别和分配。

  • I/O:输入和输出时钟信号尽量分配到专用引脚上。差分信号对儿必须分配到支持差分的专用引脚上。高速信号分配到支持高速传输的专用引脚上(如 DDR 的专用 IO 接口)。一些硬核使用的引脚可能是固定的要注意。总线信号尽量分配到同一个 BANK。一些产生噪声干扰的信号(如时钟信号)尽量远离器件的配置喜欢和其它敏感的信号。

  • 调试和固化:

    更多详细参考:

    • FPGA配置方式。
    • FPGA的各种功能管脚。
    • Altera特殊管脚的使用。
    • 官方手册里是最全的、最准的,多看!

    具体看官网手册 “Cyclone IV Device Handbook Volume 1” 的 “Configuration Process” 章节和 “Configuring Altera FPGAs.pdf” 手册。

    • 调试为通过 JTAG 接口用 Blaster 下载器把编译生成的 .sof 文件下载到 FPGA 内掉电易失的 SRAM 中。

    • 固化是通过 JTAG 接口用 Blaster 下载器把编译并转化生成的 .jic 文件下载到 FPGA 对于的外部 FLASH 器件中。FPGA 上电从 FLASH 中获取配置信息,分为几种不同的配置模式,根据 [3:0]MSEL 四个引脚上电时的电平状态确定,而具体的 [3:0]MSEL 与 启动方式的关系 看对应 FPGA 芯片系列型号的手册。配置模式分为以下几种:

      AS(主动串行),适用于小容量。由 FPGA 器件引导配置过程,EPCS 系列 FLASH 专供 AS 模式。一般用此模式。

      AP(主动并行),速度快,占 I/O 更多,适用于大容量 FPGA 器件。EPC 系列 FLASH 用于此。

      PS(被动串行),需要外部 MCU 或 CPLD(如 MAX II 系列)控制 FLASH 的数据打入 FPGA,此方式最灵活,对于多个 FPGA 或者要自动更换固件用此模式。

      assets/被动串行配置方式(PS)详解.jpg

      等其他。

FPGA 开发流程和适用范围

  • 开发流程:需求分析,模块划分,实现,前仿真,分配 IO,时钟信号约束 + 其他信号时序分析和约束,后仿真,下载验证和调试,固化代码(注意是有顺序的)。1.4 Verilog 设计方法 | 菜鸟教程 (runoob.com)。

    fpga开发流程图

  • FPGA 固有灵活性和并行性。FPGA 应用领域列举:逻辑粘合,实时控制,高速信号采集和处理,协议实现,仿真验证系统,片上系统 SoC。

  • 处理器和 FPGA 分工:MCU、MPU 适合做管理、协调,FPGA 的数字逻辑适合做专用的、复杂的、结构和功能固定下来的算法实现。

  • 推荐多去读读 FPGA 原厂(Altera 或 Xilinx)的官方文档,在它们的一些文档手册中有各种常见的电路的参考实现实例和代码风格。

  • 板级 PCB 走线遵循 “PCB走线规范”。

设计和实施规范

这里的规范仅为初级,另有 “HuaWei Verilog 规范” 等规范可供参考。

顶层设计的要点
  1. 单个模块尽量使用一个时钟源;对于多个模块要认真、清楚的划分时钟域;跨时钟域的信号一定做同步处理(D触发器同步);片内的 PLL / DLL 资源尽量利用起来;至少要对所有时钟信号加上简单的时序约束,不能没有。
  2. 数据传递的两边速率不一致要在中间加 缓存机制,常见的如 FIFO 和 乒乓缓存,后者详见 “设计技巧” 小节里的 “乒乓操作” 部分。
  3. 复杂逻辑/时序逻辑要使用 FSM (有限状态机)方式来写,在下面的 “模块收集” 里面有状态机的例子。
  4. 条件逻辑/状态图等一定要遍历所有状态,一定,防止不可预料的错误综合结果,对于 if 要有 else,对于 case 要有 default。
  5. 对于仿真:先对每一个单个模块仿真,要求代码覆盖率、条件分支覆盖率、表达式覆盖率必须达到 100%,这三个可以通过 Modelsim 查看;子系统仿真,将多个模块组合在一起进行仿真,覆盖率尽量高;系统级仿真,软硬件整板联调。仔细设计仿真激励文件。
  6. 通常采用自顶向下的设计方式。先确定系统有哪些输入和输出,把系统划分成多个子功能模块(框图模块),每个功能模块再划分下一层的子模块(HDL 意义上的模块),最后的每个模块的设计对应一个 module ,可以一个 module 设计成一个 verilog HDL 文件。
  7. 在 FPGA 逻辑全编译之前,尽量将全部 顶层 IO 分配给 实体芯片的引脚 而 不要空置,没用到的输入信号也要 assign 到 确定的 0 或 1,这样不但保证 确定的逻辑行为,而且如果不做那么全编译时 Timing 时序 无法保证 从而 亮红。
  8. 工程文件夹划分规范:prj 为工程文件存放目录; rtl 为 verilog 可综合代码存放目录; testbench 为测试文件存放目录; img 为设计相关图片存放目录; doc 为设计相关文档存放目录; prj 文件夹下还建立了子文件夹 ip,用于存放 Quartus Prime 中生成的 IP 核文件。
Verilog HDL
语法规范
  • No.1,层次化设计,IP 化设计。自写小 IP 尽量参数化、可重用,方便日后搭建数字积木。

  • 顶层文件名与顶层模块名一致。

  • 模块的定义名加尾缀"_ module",输入输出的信号名各加后缀"_ in"和"_ out",低电平有效的信号加尾缀"_ n"或“#”,时钟信号使用"clk _“或"Clk _“前缀,复位信号使用"rst _“前缀,使能信号使用"en"或者"Enable"标识等。

  • 定义模块的时候,定义输入输出信号时就带好 “input”/“in” 、 “output”/“out” 和 “reg” 等的标识修饰。

  • 一个 tab 四个空格。

  • 用 tab 划分清晰的语句层次,用 tab 对齐多行同层次语句等。

  • begin 和 end 语句块修饰词在竖方向对齐。

  • 操作符等前后用一个空格做间隔。

  • 注释齐全,对自己和别人负责。

  • 以下用一例子包含 verilog 常用语法。

/* 这里是注释 */
// 还是注释

/*
	Verilog 保留字
	always  and  assign begin  buf  bufif0 bufif1 case  casex  casez  cmos
    deassign  default defparam  disable edge  else  end  endcase  endmodule
    endfunction  endprimitive  endspecify  endtable  endtask  event
    for  force  forever  fork  function  highz0  highz1  if  ifnone
    initial  inout  input  integer join  large  macrmodule  medium  module
    nand  negedge  nmos  nor  not  notif0  notif1 or  output
    parameter pmos  posedge  primitive  pull0  pull1  pullup pulldown
    rcmos  real  realtime  reg  release repeat rnmos rpmos rtran  rtranif0
    rtranif1  scalared  small  specify specparam  strong0 strong1 supply0 supply1
    table  task  time  trantranif0  tranif1 tri  tri0  tri1  triand  trior
    trireg  vectored  wait  wand  weak0 weak1 while  wire  wor  xnor  xor
*/

/*	引用自 https://blog.csdn.net/luxinwylive/article/details/99827766
	(1)所有综合工具都支持的结构:always,assign,begin,end,case,wire,tri,aupply0,supply1,reg,integer,default,for,function,and,nand,or,nor,xor,xnor,buf,not,bufif0,bufif1,notif0,notif1,if,inout,input,instantitation,module,negedge,posedge,operators,output,parameter。
	(2)所有综合工具都不支持的结构:time,defparam,$finish,fork,join,initial,delays,UDP,wait。
	(3)有些工具支持有些工具不支持的结构:casex,casez,wand,triand,wor,trior,real,disable,forever,arrays,memories,repeat,task,while。
*/

/* wire 类型变量定义物理连线,不保存东西,reg 类型变量定义寄存器,用于保存东西 */

/*
 引自 https://zhuanlan.zhihu.com/p/72012739
 wire 用法总结
    1.wire可以在Verilog中表示任意宽度的单线/总线
    2.wire可以用于模块的输入和输出端口以及一些其他元素并在实际模块声明中
    3.wire不能存储值(无状态),并且不能在always @ 块内赋值(=或<=)左侧使用。
    4.wire是assign语句左侧唯一的合法类型(assign 后面跟着的必须是一个 wire 类型)
    5.wire只能用于组合逻辑
 reg 用法总结
    1. 声明寄存器,可以存储信息(有内存,有状态)允许连接到模块的输入端口,但不能连接到一个模块的实例化的输出
    2. 在模块声明中,reg可以用作输出,但不能用作输入
    3. 在always@(......)语句块内,= 或者 <= 赋值语句的左边必须是是reg变量
    在initial语句块内,= 赋值语句的左边必须是是reg变量
    4. Reg不能用于assign赋值语句的左侧
    5. 当与@(posedge clock)块一起使用时,reg可用于创建寄存器
    6. reg可用于组合逻辑和时序逻辑
*/

/* 连续赋值语句(assign)用于对线型变量(wire)的赋值,不能够出现在任何一个过程块(begin ... end)中;连续赋值语句(assign)定义组合逻辑,声明物理逻辑的关系;线型变量一旦被连续赋值语句赋值后,赋值语句右端表达式中的信号有任何变化,都将实时地反映到左端的线型变量中 */

/* 过程赋值语句(= 和 <=)完成对寄存器变量(reg)的赋值,只能在过程块语句中被赋值;过程赋值语句只有在语句被执行到时,赋值过程才能够进行一次,而且赋值过程的具体执行时间还受到各种因素的影响 */

/*
	数据类型:
	5'o37		5 位八进制数,二进制为 11111
	10'o37		右对齐,高位补 0
	10'bx0x1 	左边补 x,完整即 x x x x x x x 0 x 1,x 表示未知状态
	4'b1x_01	4 位二进制数,为 1 x 0 1,下划线方便阅读
	4'hz  		4 位z(扩展的z) , 即 zzzz,z 表高阻状态
	parameter SEC_TIME = 48_000_000; 十进制数
	位长不能够为变量表达式,可以为预编译、parameter 的表达式

	verilog 中 整形、浮点型等变量的 定义字 相当于 define 或者 parameter 的作用,这里只用 后二者即可了

	字符串
	reg [8*14 : 1]Message = "INTERNAL ERROR"; I 为第 1 位,N 为 第 2 位,依此类推

	数组
	reg [wordsize : 0]my_memory[arraysize : 0];
	引用数组某个数的某个位
	my_memory_1 = my_memory[1];
	my_memory_1_bit0 = my_memory_1[0];
      verilog 不支持 数组作为 模块的输入或输出,systemVerilog 支持

	运算;
	算术运算符(+,-,x,/,%)
	赋值运算符(=,<=)
	关系运算符(>,<,>=,<=)
    逻辑运算符(&&,||,!)
    条件运算符(?;)
    位运算符 (~,|,^,&,^~) 
      对于 & 运算用法之一:assign max_avl_address = &avl_address; 
       则 avl_address 最大(全1)的时候 max_avl_address 为 1,否则为 0
    移位运算符(<<,>>)
    拼接运算符({})
*/

/*
	预编译:

	宏定义:`define WIDTH 8
	引用:reg [`WIDTH-1:0] s1;  原样替换

	`ifdef 宏名 (标识符)
          程序段1
    `else
          程序段2
    `endif
*/

/*
 for 语句,尽量不要用,要使用 计数器 + case 语句 来替代
 https://blog.csdn.net/messi_cyc/article/details/79098444
*/

/*
 使用语句实现 边沿检测
 https://blog.csdn.net/bleauchat/article/details/85322247
*/

/* 模块注释规范 */
/**
  *******************************************************************************************************
  * File Name    : xxx.v
  * Author       : xxx
  * Version      : V1.0.0
  * Date         : 20xx-xx-xx
  * Brief        : xxxxx
  *******************************************************************************************************
  * History
  *		1.Author: xxx
  *		  Date: 20xx-xx-xx
  *		  Mod: xxxxx
  *
  *		2.Author: xxx
  *		  Date: 20xx-xx-xx
  *		  Mod: xxxxx
  *
  *******************************************************************************************************
  */

// *********************************************************************************
// Project Name :       
// Author       : xxx
// Email        : 
// Blogs        : 
// File Name    : xxx.v
// Module Name  :
// Called By    :
// Abstract     :
//
// CopyRight(c) 2018-2021, xxx Studio.. 
// All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date         By              Version                 Change Description
// -----------------------------------------------------------------------
// xxxx/xx/xx    xx       		1.0                     Original
//  
// *********************************************************************************
module example_module
(
    /*输入信号*/
    input clk_in,             /*时钟输入*/
    input rst_n_in,           /*复位(低有效)信号输入*/

    /*输出信号*/
    output reg [7:0]q_out,    /*q 左移位输出,要用语句块赋值,所以定义为 寄存器类型 */
    output reg [7:0]p_out     /*p 右移位输出*/

    /* 寄存器组定义 reg [7:0]Mem[0:1] 即 2 个 8 位的 Mem*/

    output output_1 = 0,output_2  /* 缺省为 wire 线网类型,可以定义初始值 */
    /* tri 主要用于定义三态的线网 */

);
    /* 定义常量参数 */
    parameter  bit_7 = 7,bit_8 = 8;

    /* 三目运算例子 
      wire [2:0] Student = Marks > 18 ? Grade_A : Grade_C;
      assign LR = (LR_select[1] == 1'b1) ? 1'bz : LR_select[0];
    */

    /* 时序逻辑定义,对 q 左移位输出*/
    always @(posedge clk_in or negedge rst_n_in)
        begin
            /* 顺序执行 */
            if(!rst_n_in)
                begin
                    q_out <= 8'bzzZz_0001; /* 总线赋值 */
                end
            else
                begin
                    q_out <= { q_out[6:0] , q_out[bit_7] }; /* 使用位拼接,左移位 */
                end
        end

    /*对 p 右移位输出*/
    always @(posedge clk_in or negedge rst_n_in)
        begin
            if(rst_n_in == 1'b0)
                begin
                    p_out <= 8'b1000_0000;
                end
            else
                begin
                    p_out <= { p_out[0] , p_out[7:1] }; /* 使用位拼接,右移位 */
                end
        end

    /* 在模块里面调用模块,即 FA_struct 模块例化,并建立连接 */
    FA_struct FA1(
        .A (q_out[1]),
        .B (p_out[1]),
      .C (rst_n_in),
        .output_1(),	/* 该引脚悬空,如果是 example_module 模块的输入则变为高阻,如果是输出则弃用 */ 
      .output_2(output_2)
     );

endmodule

/* case 语句例子
case(case_expr)
    case item_expr : procedural_statement;
    . . .
    . . .
    [default:procedural_statement]
endcase
*/

/* 门级描述组合逻辑电路 */
module FA_struct
(
    input A;
    input B;
    input C;

	output output_1;
	output output_2;
);
    /* 模块内连线 */
    wire S1, T1, T2, T3;

    xor x1 (S1, A, B);
    xor x2 (output_1, S1, C);
    and A1 (T3, A, B );
    and A2 (T2, B, C);
    and A3 (T1, A, C);
    or O1 (output_2, T1, T2, T3 );

endmodule

/*
	一个参数化模块设计例子
	定义:
module Sdram_Write
#(  parameter   DATA_WIDTH  =   16,		注,#() 这个部分用于模块参数化配置,对于 verilog 不可综合
    parameter   ADDR_WIDTH  =   12,
    parameter   ROW_DEPTH   =   2,
    parameter   COL_DEPTH   =   256,
    parameter   BURST_LENGTH =   4,       //burst length
    parameter   ACT_DEPTH    =  1,
    parameter   BREAK_PRE   =   1
)
(
    input                       clk,
    input                       rst_n,
    input                       wr_trig,
    input                       wr_en,
    input                       ref_rq,
    output          reg [3:0]   wr_cmd,
    output          reg [ADDR_WIDTH - 1:0]  wr_addr,
    output                       wr_rq,
    output          reg         wr_end_flag,
    output              [1:0]   wr_bank_addr,
    output              [DATA_WIDTH - 1:0]  wr_data,
    //wfifo
    output                       wfifo_rd_en,
    input               [7:0]    wfifo_rd_data
);
	例化:
Sdram_Write
#(  .DATA_WIDTH             ('d16),
    .ADDR_WIDTH             ('d12),
    .ROW_DEPTH              ('d1),
    .COL_DEPTH              ('d4),
    .BURST_LENGTH           ('d4),       //burst length
    .ACT_DEPTH              ('d1),
    .BREAK_PRE              ('d1) 
)
Sdram_Write_inst(
    .clk                    (clk),
    .rst_n                  (rst_n),
    .wr_trig                (wr_trig),
    .wr_en                  (wr_en),
    .ref_rq                 (ref_rq),
    .wr_cmd                 (wr_cmd),
    .wr_addr                (wr_addr),
    .wr_rq                  (wr_rq),
    .wr_end_flag            (wr_end_flag),
    .wr_bank_addr           (wr_bank_addr),
    .wr_data                (wr_data),
    .wfifo_rd_en            (wfifo_rd_en),
    .wfifo_rd_data          (wfifo_rd_data)
);

*/
编写规范
  • 以时钟信号同步的时序逻辑编写时尽量只用非阻塞赋值”<="(同步执行),用阻塞赋值”="(顺序执行)可能会产生bug,后者一般用于组合逻辑设计。尽量避免使用异步信号(比如异步总线等),即慎用或少用 assign 语句连接逻辑,而尽量把所有逻辑在 always @(*) begin … end 中实现;如果传入一个异步信号,尽量加寄存器(D触发器)用时钟进行锁存。

  • 尽量大部分功能使用时序逻辑电路设计,使用行为语句 + 时序逻辑电路描述(“always@” + “<=”) 完成建模(对于 reg 类型变量)。对于组合逻辑电路描述,简单逻辑可以使用连续赋值语句(“assign” + “=”)(对于 wire 类型变量),对于复杂组合逻辑使用 “always@( 所有敏感信号 )” + “=” 的语句。

  • Always 块的一般形式为:

    /* 这里加注释对该模块进行功能描述 */
    always @(negedge clk_in or negedge i2s_module_rst) /* 在时钟的边沿触发,再加一个复位触发条件 */
        begin
            if(!i2s_module_rst) /* 先判断是否复位 */
                begin /* 在复位块中,因该对 else 情况里面的所有 被幅值的 reg 变量进行 复位,都设置为复位值,必要! */
                    WS <= 1'b1; /* 添加语句描述 */
                end
            else
                begin /* 保持格式 */
                    if(one_flame_counter < half_flame_count)
                        begin
                            WS <= 1'b0;
                        end
                    else
                        begin
                            WS <= 1'b1;
                        end
                end
        end
    
  • case 语句必须带 default 分支,照顾到 case 的所有情况;if 语句必须带 else 分支;即分支语句要 写到/考虑 所有情况。

  • 所有的内部寄存器都应该能够被复位,尽量每个模块都要有时钟同步复位信号(不要用异步复位)。

  • 设计逻辑尽量避免不定态 x 或者高阻态 z 进入参与关键逻辑区域,仿真时就注意。

  • 移位操作直接用位拼接。

  • 同一个信号在很多地方使用,比如参数和时钟等等,应该在每一个用到的地方加一个寄存器(D触发器)用于中继缓冲,避免一个信号扇出信号数量过多。

  • 常用的,时钟上升沿锁存数据,时钟下降沿改变数据。

  • 从可综合性角度考虑,应慎用各种循环语句(for,while 等,因为编译器仅将其展开成重复语句,过多占用逻辑),大部分情况下,用于设计的循环语句可以用其他方式所替代,比如用 case 语句替代循环语句。并行块(fork … join)不可综合且容易出现竞争问题,在仿真设计中不建议使用。

  • 逻辑表达式不要写的太长,可以简化逻辑(卡诺图法或者公式法,或者 multisim 里面的逻辑分析仪简化逻辑表达式)或者分多行去写,即不要让 RTL 图中某一段逻辑链过于长;长逻辑表达式用括号划分清关系减少歧义。

  • 竞争与冒险的概念:逻辑电路中,由于门的输入信号经过不同的延时,到达门的时间不一致,这种情况叫竞争;由于竞争而导致输出产生毛刺(瞬时错误),这一现象叫冒险。为避免组合逻辑的输出出现“毛刺”,即冒险或竞争的发生,可以在输出加一个寄存器(D触发器),即让输出与时钟同步,当所有信号都到达寄存器(D 触发器)的输入后,时钟再“打一拍” 进行锁存 才能输出,这样避免最后的输出有“毛刺”;避免锁存器,使用触发器。

  • 对于有 选择 和 加法、比较 等逻辑块,编写时应让信号先经过 选择器,再送入 乘法器、加法器 或 比较器 等,即“先选后比,先选后加,先选后乘”。逻辑电路面积大小对比:乘法器 > 加法器 > 比较器 > 选择器。

  • 尽量不要用减法和除法(一个考虑多,一个面积大);乘以常数直接用 “*”,编译器会优化;两变量乘法用硬件乘法器IP。

  • 使用 function 函数语句对复杂数值运算打包(它不能包含任何时间控制语句);函数(function)可以调用其他函数(function)但不能调用任务(task),(function)函数由 任务(task)或其它 module 中调用。使用 task 语句写可重用的、固定下来的组合逻辑(不能有时序逻辑 always,不能有 wire 类型数据,这就是和 module 的区别;任务(task)可以调用其他任务(task)和函数(function),任务(task)只能在 module 中的语句块中被调用)。

  • 可以用 generate for 写 同结构不同参数 的 always@(*) 等代码,用 generate if/case 写一些随着需求可变的代码或 IP 核。 generate 语句属于预编译语句。

  • FGPA 的功耗与被使用的触发器或门电路的数量及其翻转次数成正比,尽量减少高速翻转的触发器数量是降低 FPGA 功耗的根本方法之一。

设计技巧

p.s 以下内容引自 信息理论与技术教研中心 别志松 的 PPT 《复杂数字系统设计的常用技巧》,本文作者又做了一些补充。

解决速度与面积矛盾的基本原则:1、向关键路径要速度。对于关键路径,可以采用牺牲面积换取速度的方式。2、向非关键路径要面积。对于非关键路径,通过各种方式换取面积。

基本途径:1、EDA工具的约束和优化方式的设置。2、优化代码。以下就是代码优化的一些方法。

速度的三重定义

  • Throughput:吞吐量
    • 单位时间内能处理的数据量。
    • 常用单位有bit/s,等。
  • Latency:时延
    • 指的是从数据输入到达至相应数据输出之间的时间。
    • 单位是微秒等。
  • Timing:时序
    • 指的是时序单元之间的路径所对应的时延。
    • 通常说时序关系不满足一般指触发器间关键路径的最大时延超过目标时钟周期。
    • 标准度量是时钟周期或频率。

速度的三个方面的折中

  • Latency 换 Throughput。
  • Timing 换 Throughput。
  • Timing 换 Latency 等。

提高吞吐量的方法

  • 流水线技术

    流水线技术就是把本来只能在一个较长的时钟周期内执行的操作(组合逻辑)分成几步较小的操作,并在多个较高速的时钟内完成。这些步骤的划分是通过多级寄存器来实现的。前级寄存器处理新输入数据的同时,末级寄存器产生老输入数据所对应的输出。

  • 多路并行处理

    将耗时较长的电路复制若干份,每份处理部分数据。这种处理方法需要对输入数据进行分解,对输出数据进行合并。主要用于减小 Latency。

减小延时的主要思想

  • 尽快将数据从输入传递到输出,减小中间过程处理时延,即减少数据处理链的长度。
  • 流水线技术不符合低 Latency 的要求。经常采用的方法:
    • 并行处理。
    • 去掉流水线。
  • 减小 Latency 的代价:有可能减少吞吐量;会造成关键路径时延增大。
  • 既要保证 Throughput,又要保证较小的 Latency,只能采取前述并行处理方法。

改善时序性能的方法

即提高最高运行速度。

  • 添加中间寄存器层。流水线化。防止中间纯组合逻辑链太长导致延时太长,即防止一个语句特别长运算特别多,所以中间添加寄存器层。

    将多数据的数学运算分为多个运算的组合,这些组合是并行处理的。

  • 关键路径改造为并行结构。并行处理。减少串入串出,增加输入和输出的数量,写为并入并出。

  • 展平逻辑结构。也是增加并行。

  • 减小扇出。

面积优化方法

  • 精简代码。引用上面“编写规范”一节:
    • 对于有选择和加法、比较等逻辑块,编写时应让信号先经过选择器,再送入乘法器、加法器或比较器等,即“先选后比,先选后加,先选后乘”。面积:乘法器 > 加法器 > 比较器 > 选择器。
    • 不使用除法和减法。等等等等。
  • 资源共享。
    • 模块化。声明会被高利用率的寄存器。
    • 等。
  • 合理使用复位信号。

乒乓操作

“乒乓操作” 是一个常常应用于数据流控制的处理技巧。数据缓冲模块可以为 单\双口 RAM、FIFO 等。向缓冲区 1 存数据的时候,缓冲区 2 向外出数据,向缓冲区 2 存数据的时候,缓冲区 1 向外出数据,以此循环。

assets/乒乓操作示意图.png

  • 乒乓操作的最大特点是通过 “输入数据选择单元 ”和 “输出数据选择单元” 按节拍、相互配合的切换,将经过缓冲的数据流没有停顿地送到 “数据流运算处理模块” 进行运算与处理。
  • 把乒乓操作模块当做一个整体,站在这个模块的两端看数据,输入数据流和输出数据流都是连续不断的,没有任何停顿,因此非常适合对数据流进行流水线式处理。
  • 所以乒乓操作常常与流水线结合使用,完成数据的无缝缓冲与处理。

乒乓缓存的实施

  1. 两个单口 RAM 方式。设计一个 MUX 模块,输入为一个类似 SRAM 接口,其后面控制两个单口 RAM(外部 SRAM 芯片 或 FPGA 内建 RAM),输出一个 类似 SRAM 接口芯片和 通知可读的信号(通知后面,一个 RAM 块已经写满可以快速读出)。
  2. 一个双口 RAM 方式。设计一个 MUX 模块,输入接口同上,其 后面控制 一个 双口 RAM(外部芯片或 FPGA 内建),MUX 模块的输入数据就不断循环 从地址 0 到最大地址 存进该 RAM,当存到一半的时候 输出一个 通知可读信号,即当 前半部分 存满后 可以读前半部分(此时 MUX模块正在后半部分存),当后半部分存满后可以读后半部分(此时 MUX模块又回到前半部分地址开始存),这样 一个 双口 RAM 的前后两半作为两块 RAM 进行乒乓操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/408712.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

线程计数器(CountDownLatch)

&#x1f96d;线程计数器&#xff08;CountDownLatch&#xff09; CountDownLatch也属于共享锁&#xff0c;其内部有一个int类型的属性表示可以同时并发并行的线程的数量 同时等待N个任务执行结束 举例说明&#xff1a; 比如跑步比赛&#xff0c;必须等所有运动员通过终点才…

Oracle EBS GL 外币折算逻辑

背景 由于公司财务在10月份期间某汇率维护错误,导致帐套折算以后并合传送至合并帐套生成合并日记帐凭证的借贷金额特别大,但是财务核对的科目余额有没有问题,始终觉得合并日记帐生成会计分发有问题,需要我们给出外币折算逻辑。 基础设置 汇率 Path: GL->设置->币种-&…

PHP语言检测用户输入密码及调用Python脚本

现在有一份计算流体力学N-S方程的Python脚本&#xff0c;想要在用户登录网站后可以可以运行该脚本&#xff0c;然后将脚本运行后绘制的图片显示在用户网页上。 建一个名为N_S.py的python脚本文件&#xff0c;这个脚本在生成图像后会自行关闭&#xff0c;随后将图片保存在指定的…

Stable Diffusion 3重磅发布

刚不久&#xff0c;Stability AI发布了Stable Diffusion 3.0&#xff0c;这一版本采用了与备受瞩目的爆火Sora相同的DiT架构。通过这一更新&#xff0c;画面质量、文字渲染以及对复杂对象的理解能力都得到了显著提升。由于这些改进&#xff0c;先前的技术Midjourney和DALL-E 3在…

解决vulhub漏洞环境下载慢卡死问题即解决docker-valhub漏洞环境下载慢的问题

解决vulhub环境下载慢/卡 当前环境为&#xff1a;ubuntu20 1.在 cd /etc/docker/目录下创建或修改daemon.json文件 sudo touch daemon.json编辑daemon.json文件 sudo vim daemon.json2.填写阿里云镜像地址&#xff1a; { "registry-mirrors":["https://6kx…

SWIFT:自我认知微调

文档:https://github.com/modelscope/swift/blob/main/docs/source/LLM/%E8%87%AA%E6%88%91%E8%AE%A4%E7%9F%A5%E5%BE%AE%E8%B0%83%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5.md ​​​​​​代码: Swift是如何把自我认知数据集融合到训练集中呢? 1:相关的3个参数

Java8 Stream API 详解:流式编程进行数据处理

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&…

Vue3中的select 的option是多余的?

背景&#xff1a; 通过Vue3中填充一个下拉框&#xff0c;在打开页面时要指定默认选中&#xff0c;并在选项改变时把下拉框的选中值显示出来 问题&#xff1a; 填充通常的作法是设置 <option v-for"option in cities" :value"option.value" >&a…

【数据结构-字符串 五】【字符串转换】字符串转为整数

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是【字符串转换】&#xff0c;使用【字符串】这个基本的数据结构来实现&#xff0c;这个高频题的站点是&#xff1a;CodeTop&#xff0c;筛选条件为&…

C++ 离散化算法设计原则:压缩的都是精华

公众号&#xff1a;编程驿站 1. 离散化 离散化是离散数学中的概念。离散化算法&#xff0c;指把无限空间中的离散数据映射到一个有限的存储空间中&#xff0c;并且对原数据进行有序索引化。主打压缩的都是精化。 离散化流程&#xff1a; 对离散化数列{235,897,458,7654,458…

Vue.js+SpringBoot开发电子元器件管理系统

目录 一、摘要1.1 项目简介1.2 项目录屏 二、研究内容三、界面展示3.1 登录&注册&主页3.2 元器件单位模块3.3 元器件仓库模块3.4 元器件供应商模块3.5 元器件品类模块3.6 元器件明细模块3.7 元器件类型模块3.8 元器件采购模块3.9 元器件领用模块3.10 系统基础模块 四、…

从源码解析Kruise(K8S)原地升级原理

从源码解析Kruise原地升级原理 本文从源码的角度分析 Kruise 原地升级相关功能的实现。 本篇Kruise版本为v1.5.2。 Kruise项目地址: https://github.com/openkruise/kruise 更多云原生、K8S相关文章请点击【专栏】查看&#xff01; 原地升级的概念 当我们使用deployment等Wor…

苍穹外卖项目微信支付(没有商户号)的解决方法,超详细!!!

今天在写苍穹外卖项目时&#xff0c;写到微信支付时发现个人无法获取商户号&#xff0c;那么今天我就在这里分享一个方法&#xff0c;可以绕过微信支付实现订单支付的功能。本方法仅仅是绕过微信支付&#xff0c;没有进行真正的微信支付&#xff0c;如果想要体验真正的微信支付…

值类型和引用类型详解(C#)

可能你对值类型和引用类型还不太了解。 值类型和引用类型&#xff0c;是c#比较基础&#xff0c;也必须掌握的知识点&#xff0c;但是也不是那么轻易就能掌握&#xff0c;今天跟着我一起来看看吧。 典型类型 首先我们看看这两种不同的类型有哪些比较典型的代表。 典型值类型…

橘子学es原理01之准备工作

es本身是具备很好的使用特性的&#xff0c;我指的是他的部署方面的&#xff0c;至于后期的使用和运维那还是很一眼难尽的。 我们从这一篇开始就着重于es的一些原理性的的一些探讨&#xff0c;当然我们也会有一些操作性的&#xff0c;业务性的会分为多个栏目来写。比如前面我写的…

java面试(并发)

java线程概念&#xff0c;安全&#xff1f; 进程是系统分配资源的最小单元&#xff0c;线程是操作系统调度的最小单位。线程属于进程。 加锁保证安全。1.JVM提供Synchronized关键字&#xff0c;2.jdk提供各种lock锁 实现多线程方式&#xff1f; 1.继承Thread类&#xff0c;…

【奥威-金蝶云星空BI方案】你要的报表,这里都有!

用金蝶云星空来记账&#xff0c;那确实好&#xff0c;但如果让你再去做一份详细的报表呢&#xff1f;自己开发的话&#xff0c;成本大、耗时长&#xff0c;一旦有了新的需求又要一再开发&#xff0c;长此以往将增加使用者使用难度&#xff0c;降低数据分析对运营决策的时效性。…

2024能源动力、机械自动化与航天航空技术国际学术会议(ICEPMAT2024)

2024能源动力、机械自动化与航天航空技术国际学术会议(ICEPMAT2024) 会议简介 能源动力、机械自动化和航空航天技术国际学术会议&#xff08;ICEPMAT2024&#xff09;将于2024年在北京举行。会议将探讨能源动力、机械自动化、航空航天技术领域的新研究热点、核心技术和发展趋…

迷你世界之建筑生成球体

local x0,y0,z00,30,0--起点坐标 local dx,dy,dz60,60,60--外切长方体横纵竖长度 local count,all0,dx*dy*dz--计数&#xff0c;总数 local m,k10000,0--单次生成方块数&#xff0c;无用循环值 local x,y,z0,0,0--当前坐标 local demath.random(2,19)/2 local id600--方块…

在openEuler中通过KVM可视化安装华为FusionCompute的CNA主机

一、环境说明 在Windows物理主机上通过VMware WorkStation创建一个虚拟机&#xff08;4U4C、16GB内存&#xff0c;400GB磁盘&#xff0c;NAT网络连接&#xff09;&#xff0c;在虚拟机中安装openEuler 22.03 LTS系统&#xff0c;并将该虚拟机作为部署 FusionCompute的服务器&a…