当前位置:   article > 正文

FPGA学习笔记

FPGA学习笔记

引言

本文性质为学习笔记,仅供个人记录、查找和提供有限的参考,如有不妥之处欢迎指正和交流。

I. Verilog 编写语法

本章记录一些Verilog代码编写过程中遇到的常用语法表达及使用范围、需要注意的bug以及一些高级硬件编程思路。

一、符号数处理

Verilog语法对数字的定义存在有符号数和无符号数的区别。需要明确的是,对于有符号数和无符号数,数字本身和其在计算机中的存储形式并不会改变,均为二进制数据,改变的只是对这串数字的解释:有符号数将最高位当做符号位,其余位以补码形式存储,而无符号数则没有符号位。

1.1 定义

符号数的定义如下:

// I/O variables
input  signed   [3:0]  data_in  ,
output unsigned [3:0]  data_out ,
// register variables
reg    signed   [7:0]  data_reg ;
// wire variables
wire   unsigned [7:0]  data_wire;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

以此类推,符号数的声明只需要在名称或位宽前加入signed或unsigned标识即可。

1.2 赋值

由于存在符号数的区别,FPGA在赋值过程中需要注意数据位宽的问题。当左右操作数位宽相等时可以直接赋值;但位宽不相等时,则需要考虑到截位或补高位的问题。
如果左右操作数位宽不同,但仍然直接赋值,Verilog语法会默认进行如下操作:
1、高位宽数据赋值给低位宽数据:不进行符号区分,直接截断高位,将高位宽数据的低位,即([width_short-1:0]) 赋值给低位宽数据;
2、低位宽数据赋值给高位宽数据:根据低位宽数据是否有符号进行区分,若为有符号数,则在高位填补符号位,若为无符号数,则在高位补零。
如果要进行细致精准的有符号数赋值操作,具体可以分为以下几种情况讨论:

  1. 高位宽数据赋值给低位宽数据
    在数字信号处理中,FPGA中的量化数据通常被当作有符号数来处理,因此当高位宽有符号数赋值给低位宽有符号数进行截位时需要考虑到数据的正负。由于截位操作的本质是原数据被 2^n 相除,所以只需要将高位宽数据包括符号位在内的高位赋值给低位宽数据即可,具体操作如下:
reg		[width_long-1:0]   data_long ;
reg		[width_short-1:0]  data_short;
data_short <= data_long [width_long-1:width_long-width_short];
  • 1
  • 2
  • 3

需要注意的是,Verilog语法在处理截位问题时都是对整数进行向下取整操作,因此在将高位宽数据赋值给低位宽数据时,不可避免地会产生截位误差。为了做到四舍五入、尽量减小截位误差,在工程对资源要求不高的情况下可以使用 if 语句按位判断并进行下述处理:

// data_long is negative
reg		[width_long-1:0]   data_long ;
reg		[width_short-1:0]  data_short;
if(data_long[width_long-1]) begin
	data_short <= (data_long[width_long-width_short-1]&&(data_long[width_long-width_short-2:0]!=0))?data_long[width_long-1:width_long-width_short]+1:data_high[width_long-1:width_long-width_short];
end
// data_long is positive
else begin
	data_short <= (data_long[width_long-width_short-1])?data_long[width_long-1:width_long-width_short]+1:data_long[width_long-1:width_long-width_short];
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  1. 低位宽数赋值给高位宽数
    低位宽有符号数据的赋值操作较为简单,如若没有声明,则左操作数为有符号数或无符号数完全由右操作数决定,可以在赋值过程中进行声明:
reg		[width_long-1:0]   data_long ;
reg		[width_short-1:0]  data_short;
data_long <= $signed(data_short);
  • 1
  • 2
  • 3

在上述代码中,Verilog语法会在高位宽数据的高位自动补齐右操作数的符号位。

需要注意的是,在使用常数对寄存器进行赋值的时候,代码设计要写成十进制(或者16进制以及8进制)的形式才会在高位补符号位,如果写成默认为无符号数的二进制数据,那么高位会直接补零(无论赋给有符号数还是无符号数)。因此在设计运算电路的时候,尽量不使用二进制代码。

1.3 运算

FPGA中的有符号数均为补码表示,其四则运算遵循下述准则:
1、当右操作数中含有无符号数时,整个运算被当作无符号处理,左操作数同样为无符号数;
2、当右操作数全部为有符号数时,整个运算被当作有符号处理,左操作数同样为有符号数;
3、对于加减法:n bits 的数与 m bits 的数相加,最好将输入输出变量均定义为有符号数以免丢失符号位;同时,结果位宽也应至少为 n+m+1 bits 以防溢出;同时注意,做无符号数减法时也要保证被减数大于等于减数;
4、对于乘除法:除法的实现较为简单,即两数相乘后向下取整;乘法运算时只需要保证结果位宽至少为 n+m-1 bits;

当有常数参与有符号数运算时,需要注意一个易错点:

wire signed [3:0] a;
wire signed [7:0] c;
assign c = a + 3'd2;
  • 1
  • 2
  • 3

在上述代码中,当输入端存在负数时,输出可能会出错,此时需要将代码改为:

assign c = a + 2;
  • 1

wire signed [2:0] data;
assign data = 2'd2;
assign c    = a + data;
  • 1
  • 2
  • 3

具体的四则运算规则,可以参考Verilog有符号和无符号运算设计分析.

二、顶层模块向例化模块传参

当底层例化模块使用由顶层模块输入的固定参数时,可以考虑在底层模块中声明变量并从顶层模块例化的输入接口传参,这样会在一定程度上减少资源消耗。具体代码如下:
顶层模块:

module_name#(
	.parameter_name		(parameter_value)
	)
	instance_module_name(
	.clk	(clk_top	),				// input 1
	.rst_p	(rst_p_top	),				// input 2
	...									// input n
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

底层例化模块:

module instance_module_name#(
	.parameter	[7:0]	parameter_name = 0
	)(
	input			clk		,			// input  1
	input			rst_p	,			// input  2
	...									// input  n
	output	[3:0]	data_out,			// output 1
	...									// output n
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在使用同一个模块例化多个底层模块时,只需要根据 module_name 在 vivado 中添加文件即可,只需要根据 instant_module_name 在顶层模块中多次例化。

同时需要注意的是利用 `define 和 parameter 声明参数的区别:
声明与调用代码:

// definition
`define		variable_name1	16'd425
`define		variable_name2	16'd666
parameter	variable_name3 = 8'd18;
// usage
`variable_name1 + `variable_name2 + variable_name3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

作用域:
parameter 参数作用于声明的那个文件;
define 参数从编译器读到这条指令开始到编译结束或遇到 undef 命令使之失效。
使用区别:
parameter 参数可以用于例化时的参数传递或状态机中的状态参量;
define 指令一旦被编译,那么参数在整个编译过程中都有效,但不推荐用于状态机中的状态参量宏定义。
另外,当用于 I/O 接口位宽等输入输出变量定义时,只能使用 define.

三、数据读写及 matlab 数据交互

在硬件编程过程中,经常需要使用 matlab 生成的固定数据或是将硬件程序运行结果输入 matlab 进行分析处理,Vivado 与外界既有数据的读写交互可以通过 BRAM 读写文件来实现:如果选用 BRAM 来读文件而不是自行编写数据存储模块的话,可以在 matlab 中将数据写入 .coe 文件;同时 Vivado 运行程序后也可将输出信号写入 .txt 文件以便 matlab 导入数据。

3.1 BRAM IP 核的使用

BRAM IP 核(Block Memory Generator)使用配置较为简单:
3.1.1 IP 核配置
BRAM-1
BRAM-2
BRAM-3
3.1.2 IP 核例化
以 7bits 位宽512深度真双端口 RAM 为例,IP 核例化代码如下:

blk_mem_gen_2 instance_name (
  .clka							(clk						),			// input  wire 			clka
  .ena							(en_wr_ram					),			// input  wire 			ena
  .wea							(1'b1						),			// input  wire [0 : 0] 	wea			1 for write, 0 for read
  .addra						(addr_wr_ram				),			// input  wire [8 : 0] 	addra
  .dina							(din_ram					),			// input  wire [7 : 0] 	dina
  .clkb							(clk						),			// input  wire 			clkb
  .enb							(en_rd_ram					),			// input  wire 			enb
  .web							(1'b0						),			// input  wire [0 : 0] 	web			1 for write, 0 for read
  .addrb						(addr_rd_ram				),			// input  wire [8 : 0] 	addrb
  .doutb						(dout_ram					) 			// output wire [7 : 0] 	doutb
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

需要注意以下几点:
1、.wea(b) 信号为 IP 核该端口的读写功能使能,拉高时该端口执行写功能,反之则执行读功能,当伪双端口 RAM IP 核不存在使能信号选项时,可以使用该信号作为写端口的写使能;
2、当勾选至少一个端口输出寄存器选项时,IP 核输出延时将大于 1clk,但数据的输出使能仍然是没有经过延迟的读使能信号,因此会出现末尾数据丢失的情况(读使能无法包住输出信号,延时 1clk 无丢失,延时 2clks 丢失 1clk 数据,延时 3clks 丢失 2clks 数据),解决方法如下:

  1. 不勾选输出寄存器选项,此时输出仅延迟 1clk,输出 nd 信号也可以直接使用读使能信号打一拍之后的信号;
  2. 不使用读写使能(即使能恒拉高),利用计数器来选取正确区间的输出信号以及生成输出 nd 信号;
  3. 读使能信号长度大于 RAM IP 核的深度,利用计数器生成输出 nd 信号,此时读地址的生成代码示例如下:
always @(posedge clk) begin
	if(rst_p) begin
		addr_rd_ram <= 0;
	end
	else begin
		if(en_rd_ram) begin
			if(addr_rd_ram == depth_ram-1) begin
				addr_rd_ram <= 0;
			end
			else begin
				addr_rd_ram <= addr_rd_ram + 1;
			end
		end
		else begin
			addr_rd_ram <= 0;
		end
	end
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3.2 matlab 生成数据文件

BRAM IP 核或 ROM IP 核加载的数据文件格式为 .coe 文件,并且具备固定的存储形式,生成数据文件的 matlab 代码如下:

fid = fopen('Route\data_in.coe','wt');
fprintf(fid,'memory_initialization_radix = 10;\n');
fprintf(fid,'memory_initialization_vector =\n');
fprintf(fid,'%d,\n',data_in(1:length(data_in)-1));
fprintf(fid,'%d;',data_in(length(data_in)));
fclose(fid);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

可以同时生成当前数据的 .txt 备份文件方便 matlab 调试时读取:

% write data
fid = fopen('Route\data_in_backup.txt','wt');
fprintf(fid,'%d\n',data_in);
fclose(fid);
% read data
data_in = load('Route\data_in_backup.txt')';
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

需要注意的是,FIR IP 核读取的滤波器系数 .coe 文件存储形式不同于上述文件:

fid=fopen('Route\fir_cos_quant.coe','wt');    
fprintf(fid,'Radix=10;\n');
fprintf(fid,'Coefficient_Width=16;\n');
fprintf(fid,'CoefData=');
for i = 1:length(fir_cos_quant)
    fprintf(fid,'%d ',fir_cos_quant(i));                % data in quantized decimal number
end
fprintf(fid,';');
fclose(fid);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3.3 Vivado 生成数据文件

Vivado 中使用 Verilog 语句在仿真 tb 文件中写数据代码如下:

integer outfile_dout;

always @ (posedge clk) begin
	if(head_out) begin
		outfile_dout = $fopen("data_out.txt","w");
	end
	else if(nd_out) begin
		$fwrite(outfile_dout,"%d\n",$signed(data_out));
	end
	else if(end_out) begin
		$fclose(outfile_dout);
	end
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

四、多个子模块的循环例化

当需要在顶层模块下例化多个子模块时,可以采用 for 循环的形式,不需要多次复制例化代码,实现如下:

reg		[module_num-1:0]	data_in ;
wire	[module_num-1:0]	data_out;
reg		[width-1:0]			data_sel[0:module_num-1];

generate
	genvar n1;
	for (n1 = 0; n1 < module_num; n1 = n1 + 1)
		begin: data_source
			instance_name unit_C(
				.clk				(clk						),
				.rst_p				(rst_p						),
				.data_in			(data_in[n1]				),
				.data_select		(data_sel[n1]				),
				.data_out			(data_out[n1]				)
			);
		end
endgenerate
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

五、数组的使用

5.1 定义

不同数组的定义方式如下:

reg		[15:0]	mem1;								// 16bits wide register
reg 	[15:0]  mem2 [0:3];							// 16bits wide registers in 4x1 array
reg 	[15:0]	mem3 [0:3][0:1];					// 16bits wide registers in 4x2 2D array
  • 1
  • 2
  • 3

5.2 初始化

在 Verilog 语言中,数组的初始化不能采用统一赋值(即:packet registers could not be assigned by unpacket registers),因此需要对数组中的每一个元素进行分别赋值。在数组元素过多时,可以采用 for 循环对数组中的每个元素进行初始化和复位。

II. Vivado IP 核使用说明

本章记录一些Vivado实现过程中一些常用复杂 IP 核的使用说明、前后数据处理、常见bug及解决方案等注意事项。

一、FFT IP 核

FFT IP 核是vivado数字信号处理中最常用的 IP 核之一,用于对信号做离散傅里叶变换/逆变换,实现信号时频域之间的转换。下面就配置、I/O 接口和数据处理等方面对其进行说明。

1.1 FFT 基础

FFT 基本原理与 matlab 仿真注意事项参考文档《FFT 相关总结》pdf.(数字信号处理 + FFT + 相关原理总结与技术说明)

1.2 IP 核端口协议及配置

1.2.1 IP 核模块框图
vivado2019.2 FFT IP-1
1.2.2 端口引脚列表

分类名称方向选择位宽说明
时钟与复位aclkI必须1模块工作时钟,上升沿有效
aclkenI可选1时钟使能信号,高电平有效,低电平时内核暂停工作,可通过此信号降低内核运行的最大时钟频率
aresetnI可选1低电平同步复位信号,优先级高于aclken,低电平至少持续两个时钟周期
配置s_axis_config_tvalidI必须1配置通道的 tvalid,由外部主机断言以表示它能够提供数据
s_axis_config_treadyO必须1配置通道的 tready,由内核断言以表示它已准备好接收数据
s_axis_config_tdataI必须可配置,8的倍数配置通道的tdata,携带配置信息,NFFT、FWD/INV、CP_LEN和SCALE_SCH
slave 数据s_axis_data_tvalidI必须1输入数据通道tvalid,由外部主机断言以表示它能够提供数据
s_axis_data_treadyO必须1输入数据通道tready,由内核断言以表示它能够接收数据
s_axis_data_tdataI必须可配置,16的倍数输入数据通道tdata,由外部主机提供待处理的数据,由高位虚部、低位实部以及各自高位的 padding 构成
s_axis_data_tlastI必须1输入数据通道tdata,由外部主机断言表示最后一组数据。只在生成事件event_tlast_unexpected 和 event_tlast_missing时,内核才使用它
master 数据m_axis_data_tvalidO必须1输出数据通道tvalid,由内核断言表示它能够提供数据
m_axis_data_treadyI必须1输出数据通道tready,由外部丛机断言表示它能够接收数据
m_axis_data_tdataO必须可配置,16的倍数输出数据通道tdata,由内核提供FFT后的数据,由高位虚部、低位实部以及各自高位的 padding 构成
m_axis_data_tuserO可选可配置,8的倍数输出数据通道tuser,由内核提供XK_INDEX,OVFLO,BLK_EXP信息
m_axis_data_tlastO必须1数据数据通道tlast,由内核断言表示最后一个输出数据
状态==(可选)==m_axis_status_tvalidO可选1状态通道tvalid,由内核断言表示它能够提供状态数据
m_axis_status_treadyI可选1状态通道tready,由外部从机断言表示它能够接收状态数据
m_axis_status_tdataO可选16状态通道tdata,由内核提供的状态数据,包含BLK_EXP 或 OVFLO
事务event_frame_startedO必须1当内核开始处理新帧时,该事件信号在一个时钟周期内被置位。提供此信号是为了允许您对帧进行计数,并在需要时将内核的配置同步到特定的帧
event_tlast_unexpectedO必须1当内核在不是帧中最后一个的任何传入数据样本上看到 s_axis_data_tlast High 时,该事件信号被置位为一个时钟周期。这表明核心和上游数据源在帧大小方面的配置不匹配,并表明上游数据源配置为比核心更小的点大小。这仅在内核开始处理帧时计算,因此事件可能会滞后 s_axis_data_tlast 上的意外高点大量时钟周期如果一帧的 s_axis_data_tlast 上有多个意外的高点,则对它们中的每一个都进行断言
event_tlast_missingO必须1当 s_axis_data_tlast 在帧最后传入的数据样本上为低时,该事件信号在单个时钟周期内被置位。这表明内核和上游数据源在帧大小方面的配置不匹配,并表明上游数据源配置为比核心更大的点大小。这仅在内核开始处理帧时计算,因此事件可能会滞后丢失的 s_axis_data_tlast 大量时钟周期
event_fft_overflowO必须1当在 m_axis_data_tdata 上传输的数据样本中出现溢出时,该事件信号在每个时钟周期置位。只有在使用缩放算术或单精度浮点 I/O 时才会出现 FFT 溢出。在所有其他配置中,此引脚被移除
event_data_in_channel_haltO必须1该事件在内核需要来自数据输入通道的数据且没有数据可用的每个周期被置位。在实时模式下,即使帧已不可恢复地损坏,内核仍会继续处理该帧。在非实时模式下,核心处理停止并仅在数据写入数据输入通道时继续。帧没有损坏。在这两种模式下,事件保持有效,直到数据输入通道中的数据可用
event_data_out_channel_haltO必须1该事件在内核需要将数据写入数据输出通道,但因为通道中的缓冲区已满而无法写入的每个周期中被置位。发生这种情况时,核心处理会停止,所有活动都会停止,直到通道缓冲区中有可用空间。帧没有损坏。此事件引脚仅在非实时模式下可用
event_status_channel_haltO必须1该事件在内核需要将数据写入状态通道但不能因为通道上的缓冲区已满而无法写入的每个周期中被置位。发生这种情况时,核心处理会停止,所有活动都会停止,直到通道缓冲区中有可用空间。帧没有损坏。此事件引脚仅在非实时模式下可用

1.2.3 IP 核配置

  1. Configuration
    FFT IP-2
    note:
    a. 目标工作时钟频率选择最接近模块处理时钟的整数 MHz 时钟频率;
    b. 不同架构中,由上至下耗费资源越小、处理时延越大、数据吞吐量越小;
    c. 数据比特率根据时钟频率自动配置
  2. Implementation
    FFT IP-3
    note:
    a. 数据格式一般默认定点格式;
    b. 对于缩放选项:
    block floating point
    不管输入格式如何,IP 核内部都采用浮点运算,会根据每一级的的数据情况自动缩放。 这个模式下的输入输出位宽一致,便于调用
    scaled
    在 m_axis_data_tuser 中会有 5bits 表示每一级的缩放情况,在 s_axis_config_data 中则会有相应的字段配置每一级的缩放倍数因子
    unscaled
    不用担心 FFT 变化过程中会出现溢出,输出数据与理论值偏差较小,但是输出位宽会非常大,需要进行截位
    c. 数据量化模式有四舍五入和截位2种模式,前者精度较高但时延偏大;
    d. 输出时序有默认倒序模式和自然输出模式,一般选择自然输出模式。
  3. Detailed Implementation
    FFT IP-4
    note:
    该页面一般选择默认选项。

1.3 IP 核例化

在上述配置选项下,选择流水线 I/O、Fixed Point、Unscaled、Convergent Rounding 与 Natural Order,不选择时钟和复位使能信号,其余配置选项保持默认,此时顶层模块例化代码如下所示:

xfft_0 instance_name (
  // clk
  .aclk							(clk						),			// input  wire 			aclk
  // slave ports
  .s_axis_config_tdata 			(8'b1						),			// input  wire [7 : 0] 	s_axis_config_tdata  		LSB(1) for FFT, LSB(0) for IFFT
  .s_axis_config_tvalid			(1'b1						),			// input  wire 			s_axis_config_tvalid 		always 1
  .s_axis_config_tready			(s_axis_config_tready		),			// output wire 			s_axis_config_tready 		always left out
  .s_axis_data_tdata			(data_fft_in				),			// input  wire [47 : 0] input data
  .s_axis_data_tvalid			(nd_in						),			// input  wire 			input data enable valid
  .s_axis_data_tready			(s_axis_data_tready			),			// output wire 			s_axis_data_tready			always left out
  .s_axis_data_tlast 			(fft_data_tlast				),			// input  wire 			s_axis_data_tlast			pull high when last input data bit
  // master ports
  .m_axis_data_tdata 			(data_fft_out				),			// output wire [47 : 0] output data
  .m_axis_data_tvalid			(nd_out						),			// output wire 			output data enable valid
  .m_axis_data_tready			(1'b1						),			// input  wire 			m_axis_data_tready			always 1
  .m_axis_data_tlast 			(m_axis_data_tlast			),			// output wire 			m_axis_data_tlast			always left out
  // event ports
  .event_frame_started			(event_frame_started		),			// output wire 			event_frame_started			always left out
  .event_tlast_unexpected		(event_tlast_unexpected		),			// output wire 			event_tlast_unexpected		always left out
  .event_tlast_missing			(event_tlast_missing		),			// output wire 			event_tlast_missing			always left out
  .event_status_channel_halt	(event_status_channel_halt	),			// output wire 			event_status_channel_halt	always left out
  .event_data_in_channel_halt	(event_data_in_channel_halt	),			// output wire 			event_data_in_channel_halt	always left out
  .event_data_out_channel_halt	(event_data_out_channel_halt) 			// output wire 			event_data_out_channel_halt	always left out
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

上述模式配置下,FFT 或 IFFT 运算的结果可能与 matlab 理论值相差 2^n 倍,依据理论值来进行合理截位即可。
在缩放模式选择 Block Floating Point 的情况下,可能需要状态参数的相关 I/O:

  .m_axis_data_tuser 			(m_axis_data_tuser 			),			// output wire [7 : 0] 	m_axis_data_tuser		    always left out
  // status ports
  .m_axis_status_tdata 			(m_axis_status_tdata		),			// output wire [7 : 0] 	m_axis_status_tdata			always left out
  .m_axis_status_tvalid			(m_axis_status_tvalid		),			// output wire 			m_axis_status_tvalid		always left out
  .m_axis_status_tready			(1'b1						),			// input  wire 			m_axis_status_tready		always 1
  • 1
  • 2
  • 3
  • 4
  • 5

1.4 IP 核 I/O 时序讨论

IP 核配置界面的 latancy 不一定符合 IP 核的实际时延,需要仿真观察输入使能信号 s_axis_data_tvalid 和输出使能信号 m_axis_data_tvalid 的间隔时延来决定。此外,输出结尾使能信号 m_axis_data_tlast 也可以作为参考标准。

1.5 参考文献

详细引脚注解与实践配置可参考vivado之FFT ip核的入门学习(已补充调用模块).
FFT IP 核技术手册、引脚协议及详细介绍可参考Xilinx IP解析之 Fast Fourier Transform(FFT) v9.1.

二、Cordic IP 核

Cordic IP 核用于实现关于信号角度或坐标的相关变换与求解,下面就配置、I/O 接口和数据处理等方面对其进行说明。

2.1 IP 核端口协议

2.1.1 IP 核模块框图
vivado2019.2 Cordic IP-1
2.1.2 端口引脚列表

分类名称方向选择位宽说明
时钟与复位aclkI必须1模块工作时钟,上升沿有效
aclkenI可选1时钟使能信号,高电平有效,低电平时内核暂停工作,可通过此信号降低内核运行的最大时钟频率
aresetnI可选1低电平同步复位信号,优先级高于aclken,低电平至少持续两个时钟周期
slave数据s_axis_cartesian_tdataI必须可配置,8的倍数输入数据通道tdata,由外部主机提供待处理的数据,由高位虚部、低位实部以及各自高位的 padding 构成
s_axis_cartesian_tlastI可选1输入数据通道tdata,由外部主机断言表示最后一组数据
s_axis_cartesian_treadyO可选1输出数据通道tready,由内核断言以表示它能够接收数据
s_axis_cartesian_tuserI可选1输入数据通道tuser
s_axis_cartesian_tvalidI必须1输入数据通道tvalid,由外部主机断言以表示它能够提供数据
slave相位s_axis_phase_tdataI必须可配置,8的倍数输入相位通道tdata,由外部主机提供待处理的相位
s_axis_phase_tlastI可选1输入相位通道tdata,由外部主机断言表示最后一组相位
s_axis_phase_treadyO可选1输出数据通道tready,由内核断言以表示它能够接收相位
s_axis_phase_tuserI可选1输入相位通道tuser
s_axis_phase_tvalidI必须1输入相位通道tvalid,由外部主机断言以表示它能够提供数据
master数据m_axis_dout_tdataO必须可配置,8的倍数输出数据通道tdata,由内核提供运算处理后的数据
m_axis_dout_tlastO可选1数据数据通道tlast,由内核断言表示最后一个输出数据
m_axis_dout_treadyI可选1状态通道tready,由外部从机断言表示它能够接收状态数据
m_axis_dout_tuserO可选1输出数据通道tuser
m_axis_dout_tvalidO必须1输出数据通道tvalid,由内核断言表示它能够提供数据

2.1.3 工作模式
Cordic IP 核提供了7种运算功能,分别是:Rotate、Translate、Sin and Cos、Sinh and Cosh、Arc Tan、Arc Tanh 和 Square Root. 对应 I/O 接口的使用情况如下表:

Functions_axis_cartesian - XINs_axis_cartesian - YINs_axis_phase - INm_axis_dout - XOUTm_axis_dout - YOUTm_axis_dout - PhaseOUT
Rotate111110
Translate110101
Sin and Cos001110
Sinh and Cosh110001
Arc Tan001110
Arc Tanh110001
Square Root100100

其中,1表示在该模式对应通道的子字段存在,0则表示该字段不存在。
根据Xilinx官方 datasheet,各通道数据结构如下图所示:

Cordic IP-3

Cordic IP-4

Cordic IP-5
7种运算模式详解如下:
Rotate
在该模式下,IP 核将输入的矢量旋转指定角度后得到新的矢量并输出。该功能配置下,IP核默认设置粗略旋转和选择补充缩放。官方规定的 I/O 协议如下表:
Cordic IP-6
Cordic IP 核的 Rotate 模式可以用于极坐标向直角坐标的转换:输入模值为 ( sqrt(X^2 + Y^2) ),相位为极角 θ,则可以得到直角坐标 (X, Y) 的输出。如输入 (1, 0) 和 pi/4,得到输出 (0.707, 0.707).
Translate
在该模式下,IP 核将输入的矢量旋转至 Y 分量为0,并输出此时的 X 分量和旋转角,可以用于直角坐标向极坐标的转换,也即求取输入信号的模值和相角,需要设置粗略旋转和补充缩放。官方规定的 I/O 协议如下表:
Cordic IP-7
如输入 (0.707, 0.25) 时,得到输出模值为0.75,相角为0.336 (rad).
Sin and Cos
该模式下的 IP 核对输入的角度进行正余弦的计算。在该模式下,需要对输入角度大小进行限制,当不适用粗略旋转时,输入角度需要在第一象限 (-pi/4, pi/4) 之间;当选择粗略旋转时,角度范围则是 (-pi, pi),Cordic 算法会将输入样本旋转至第一象限内进行计算,并将输出样本反向旋转回适当象限。官方规定的 I/O 协议如下表:
Cordic IP-8
当设置粗略旋转时,输入角度应被规范在 (-pi, pi) 的范围内,圆周率的二进制定点数表示如下:

parameter pos_pi = 011_0010_0100_0011_1111_0110_1010_1000;		// +3.1415926516
parameter neg_pi = 100_1101_1011_1100_0000_1001_0101_1000;		// -3.1415926516
  • 1
  • 2

该模式下没有补充缩放选项。
Sinh and Cosh
官方规定的 I/O 协议如下表:
Cordic IP-9
略。该模式下没有补充缩放选项。
Arc Tan
官方规定的 I/O 协议如下表:
Cordic IP-10
略。该模式下没有补充缩放选项。
Arc Tanh
官方规定的 I/O 协议如下表:
Cordic IP-11
略。该模式下没有补充缩放选项。
Square Root
官方规定的 I/O 协议如下表:
Cordic IP-12
略。该模式下没有补充缩放选项。

2.2 IP 核配置

  1. Configuration Options
    Cordic IP-13
    note:
    a. 体系结构配置:Parallel - 并行 - 单周期数据吞吐量;Word Serial - 串行 - 多周期吞吐量;
    b. 流水线模式:None - 无流水线;Optimal - 最优 - 采用尽可能多的流水线阶段实现而不使用任何额外的 LUT;Maximum - 最大 - 在每次子迭代后使用流水线来实现;
    c. 数据格式:Signed Fraction - 有符号小数,X 和 Y 的输入输出默认表示为具有2位整数宽度的定点二进制补码;
    Unsigned Fraction - 无符号小数,X 和 Y 的输入输出表示为具有1位整数宽度的无符号定点数,仅适用于平方根功能配置;
    d. 相位格式:Radians - 弧度,表示为一个整数宽度为3位的二进制补码,单位为 rad;Scaled Radians - 归一化弧度,表示为一个整数宽度为3位的二进制补码,单位为 pi rad;
    e. 量化模式:4种量化模式分别为 Truncate 截断、Round Pos Inf 向上取整、Round Pos Neg Inf 四舍五入和 Nearest Even最接近偶数取整;
    f. 迭代次数和精度:默认为0;
    g. 粗略旋转:规定 I/O 接口上角度的大小范围;
    h. 补充缩放:Cordic 幅度缩放影响矢量旋转和矢量平移功能配置。不影响Sin and Cos、Sinh and Cosh、Arc Tan、Arc Tanh和 Square Root 函数配置。共有4种设置,分别是No Scale Compensation (输出 X 和 Y 不进行补偿,而是按比例生成)、LUT Based (输出 X 和 Y 使用基于 LUT 的恒定系数乘法器进行补偿)、BRAM (输出 X 和 Y 使用基于块 RAM 的恒定系数乘法器进行补偿)、Embedded Multiplier (输出 X 和 Y 使用 DSP Slice 进行补偿)。
  2. AXI4 Stream Options
    Cordic IP-14
    该界面旋转默认配置即可,也可以根据后级信号处理实际需要选择配置 tlast 使能信号。

2.3 IP 核例化

在上述配置选项下,顶层模块例化代码如下所示:

cordic_0 instance_name(
  .aclk						(clk						),				// input  wire 			aclk
  .s_axis_cartesian_tvalid	(nd_in						),				// input  wire 			s_axis_cartesian_tvalid
  .s_axis_cartesian_tdata	(data_cordic_in				),				// input  wire [47 : 0] s_axis_cartesian_tdata
  .m_axis_dout_tvalid		(nd_out						),				// output wire 			m_axis_dout_tvalid
  .m_axis_dout_tdata		(data_cordic_out			) 				// output wire [47 : 0] m_axis_dout_tdata
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.4 IP 核 I/O 时序及位宽问题讨论

  1. 时序问题:Cordic IP 核的 latency 与 Implementation Details 界面中规定的 latency 一致,可以通过仿真验证
  2. 位宽问题:前文所述的 IP 核7种工作模式下,分别对于 I/O 输入数据大小有明确的限制,以最常用的 Translate 和正余弦函数为例,这3种工作模式下设置粗略旋转时, I/O 接口中的数值大小都被限制在绝对值为1的范围内,角度大小都被限制在绝对值为 pi 的范围内。需要明确的是,Cordic IP 核处理的数据一般都是定点小数 fix m_n ,数值一般有 1bit 符号位和 1bit 整数位,角度一般有 1bit 符号位和 2bits 整数位。由于计算机处理数据的特性,同样的数值可以分别看作定点小数和整数,因此在观察角度时,需要在仿真窗口中设置数据格式:

Cordic IP-15
当输入数值或角度超过 IP 核规定大小范围时,会导致溢出,因此在处理 IP 核的 I/O 数据时,需要注意以下几点:
a. 将输入数值归一至定点小数±1的范围内,可以通过重复 1bit 符号位来实现,无论正负
b. 不要使用小值,数值越小计算误差越大;
c. 输入数据不要落在正半轴上,此时 Translate 模式下的角度运算会出错 (不为0);
d. 在核对运算结果时,数值可以按照整数或定点数来分析核对,不会出错,角度则需要使用弧度制按照定点数来分析核对,并且这种区别只有在 debug 对比数据时会出现,FPGA 内部处理的只是二进制数据而已,可以直接使用。

2.5 参考文献

Cordic IP 核详细算法介绍及引脚协议可参考Vivado_Cordic IP核使用详解.

III. FPGA 硬件学习

本章记录一些FPGA硬件调试过程中常用的板卡datasheet、工程debug手段、模拟电路问题排查等总结与心得体会。

一、板卡分区固化程序

单个 FPGA 芯片存在根据需要进行程序切区的情况,以下述 3 个 FLASH 烧录的程序为例记录程序固化过程:

  1. 生成 .mcs 文件和 .prm 文件
    步骤1:
    vivado2019.1 固化-1
    步骤2:
    固化-2
    note:
    a. memory part 根据芯片需要选择对应的存储单元;
    b. 加载 .bit 文件时,根据文件大小填写地址,保证 FLASH 有足够空间存储每个.bit 文件;
    c. memory part 有时无法搜索到目标存储单元,此时需要拔出 jtag 数据线,由步骤1重新进入界面进行搜索选择。

    步骤3:
    固化-3
    步骤4:
    固化-4

  2. 固化 .mcs 文件和 .prm 文件
    步骤5:
    固化-5
    note:
    在 Hardware manager 中查找到对应存储单元实现固化。如若没有找到存储单元,则需要右键目标芯片并添加 (Add Configuration Memory Device).
    步骤6:
    固化-6
    note:
    Configuration file 和 PRM file 分别选择先前生成的 .mcs 文件和 .prm 文件。
    正在固化程序:
    固化-7
    固化-8
    至此,程序固化完成。
    需要注意的是,固化后的板卡在调试时可能没有加载正确的 .ltx 文件,因此不会显示对应 ila 和 vio, 此时需要左键目标芯片,在下方的 Hardware Device Properties 界面中的 Probes file 添加正确的 .ltx 文件用来调试。

IV. 结语

第一次尝试使用这样的方法记录科研学习中遇到的一些问题和解决方案,如果存在错误或任何不妥之处欢迎批评指正,如果能够对读者提供些许帮助那自然更好,欢迎大家的交流和任何建议。

更新日期:03/10/24

note:以下为模板内容,与正文无关

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

居中的图片: Alt

居中并且带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';
  • 1
  • 2

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPEASCIIHTML
Single backticks'Isn't this fun?'‘Isn’t this fun?’
Quotes"Isn't this fun?"“Isn’t this fun?”
Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

2014-01-07 2014-01-09 2014-01-11 2014-01-13 2014-01-15 2014-01-17 2014-01-19 2014-01-21 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

Created with Raphaël 2.3.0 开始 我的操作 确认? 结束 yes no
  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/502763
推荐阅读
相关标签
  

闽ICP备14008679号