赞
踩
本文性质为学习笔记,仅供个人记录、查找和提供有限的参考,如有不妥之处欢迎指正和交流。
本章记录一些Verilog代码编写过程中遇到的常用语法表达及使用范围、需要注意的bug以及一些高级硬件编程思路。
Verilog语法对数字的定义存在有符号数和无符号数的区别。需要明确的是,对于有符号数和无符号数,数字本身和其在计算机中的存储形式并不会改变,均为二进制数据,改变的只是对这串数字的解释:有符号数将最高位当做符号位,其余位以补码形式存储,而无符号数则没有符号位。
符号数的定义如下:
// 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;
以此类推,符号数的声明只需要在名称或位宽前加入signed或unsigned标识即可。
由于存在符号数的区别,FPGA在赋值过程中需要注意数据位宽的问题。当左右操作数位宽相等时可以直接赋值;但位宽不相等时,则需要考虑到截位或补高位的问题。
如果左右操作数位宽不同,但仍然直接赋值,Verilog语法会默认进行如下操作:
1、高位宽数据赋值给低位宽数据:不进行符号区分,直接截断高位,将高位宽数据的低位,即([width_short-1:0]) 赋值给低位宽数据;
2、低位宽数据赋值给高位宽数据:根据低位宽数据是否有符号进行区分,若为有符号数,则在高位填补符号位,若为无符号数,则在高位补零。
如果要进行细致精准的有符号数赋值操作,具体可以分为以下几种情况讨论:
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];
需要注意的是,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
reg [width_long-1:0] data_long ;
reg [width_short-1:0] data_short;
data_long <= $signed(data_short);
在上述代码中,Verilog语法会在高位宽数据的高位自动补齐右操作数的符号位。
需要注意的是,在使用常数对寄存器进行赋值的时候,代码设计要写成十进制(或者16进制以及8进制)的形式才会在高位补符号位,如果写成默认为无符号数的二进制数据,那么高位会直接补零(无论赋给有符号数还是无符号数)。因此在设计运算电路的时候,尽量不使用二进制代码。
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;
在上述代码中,当输入端存在负数时,输出可能会出错,此时需要将代码改为:
assign c = a + 2;
或
wire signed [2:0] data;
assign data = 2'd2;
assign c = a + data;
具体的四则运算规则,可以参考Verilog有符号和无符号运算设计分析.
当底层例化模块使用由顶层模块输入的固定参数时,可以考虑在底层模块中声明变量并从顶层模块例化的输入接口传参,这样会在一定程度上减少资源消耗。具体代码如下:
顶层模块:
module_name#(
.parameter_name (parameter_value)
)
instance_module_name(
.clk (clk_top ), // input 1
.rst_p (rst_p_top ), // input 2
... // input n
);
底层例化模块:
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
);
在使用同一个模块例化多个底层模块时,只需要根据 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
作用域:
parameter 参数作用于声明的那个文件;
define 参数从编译器读到这条指令开始到编译结束或遇到 undef 命令使之失效。
使用区别:
parameter 参数可以用于例化时的参数传递或状态机中的状态参量;
define 指令一旦被编译,那么参数在整个编译过程中都有效,但不推荐用于状态机中的状态参量宏定义。
另外,当用于 I/O 接口位宽等输入输出变量定义时,只能使用 define.
在硬件编程过程中,经常需要使用 matlab 生成的固定数据或是将硬件程序运行结果输入 matlab 进行分析处理,Vivado 与外界既有数据的读写交互可以通过 BRAM 读写文件来实现:如果选用 BRAM 来读文件而不是自行编写数据存储模块的话,可以在 matlab 中将数据写入 .coe 文件;同时 Vivado 运行程序后也可将输出信号写入 .txt 文件以便 matlab 导入数据。
BRAM IP 核(Block Memory Generator)使用配置较为简单:
3.1.1 IP 核配置
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、.wea(b) 信号为 IP 核该端口的读写功能使能,拉高时该端口执行写功能,反之则执行读功能,当伪双端口 RAM IP 核不存在使能信号选项时,可以使用该信号作为写端口的写使能;
2、当勾选至少一个端口输出寄存器选项时,IP 核输出延时将大于 1clk,但数据的输出使能仍然是没有经过延迟的读使能信号,因此会出现末尾数据丢失的情况(读使能无法包住输出信号,延时 1clk 无丢失,延时 2clks 丢失 1clk 数据,延时 3clks 丢失 2clks 数据),解决方法如下:
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
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);
可以同时生成当前数据的 .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')';
需要注意的是,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);
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
当需要在顶层模块下例化多个子模块时,可以采用 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
不同数组的定义方式如下:
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
在 Verilog 语言中,数组的初始化不能采用统一赋值(即:packet registers could not be assigned by unpacket registers),因此需要对数组中的每一个元素进行分别赋值。在数组元素过多时,可以采用 for 循环对数组中的每个元素进行初始化和复位。
本章记录一些Vivado实现过程中一些常用复杂 IP 核的使用说明、前后数据处理、常见bug及解决方案等注意事项。
FFT IP 核是vivado数字信号处理中最常用的 IP 核之一,用于对信号做离散傅里叶变换/逆变换,实现信号时频域之间的转换。下面就配置、I/O 接口和数据处理等方面对其进行说明。
FFT 基本原理与 matlab 仿真注意事项参考文档《FFT 相关总结》pdf.(数字信号处理 + FFT + 相关原理总结与技术说明)
1.2.1 IP 核模块框图
1.2.2 端口引脚列表
分类 | 名称 | 方向 | 选择 | 位宽 | 说明 |
---|---|---|---|---|---|
时钟与复位 | aclk | I | 必须 | 1 | 模块工作时钟,上升沿有效 |
aclken | I | 可选 | 1 | 时钟使能信号,高电平有效,低电平时内核暂停工作,可通过此信号降低内核运行的最大时钟频率 | |
aresetn | I | 可选 | 1 | 低电平同步复位信号,优先级高于aclken,低电平至少持续两个时钟周期 | |
配置 | s_axis_config_tvalid | I | 必须 | 1 | 配置通道的 tvalid,由外部主机断言以表示它能够提供数据 |
s_axis_config_tready | O | 必须 | 1 | 配置通道的 tready,由内核断言以表示它已准备好接收数据 | |
s_axis_config_tdata | I | 必须 | 可配置,8的倍数 | 配置通道的tdata,携带配置信息,NFFT、FWD/INV、CP_LEN和SCALE_SCH | |
slave 数据 | s_axis_data_tvalid | I | 必须 | 1 | 输入数据通道tvalid,由外部主机断言以表示它能够提供数据 |
s_axis_data_tready | O | 必须 | 1 | 输入数据通道tready,由内核断言以表示它能够接收数据 | |
s_axis_data_tdata | I | 必须 | 可配置,16的倍数 | 输入数据通道tdata,由外部主机提供待处理的数据,由高位虚部、低位实部以及各自高位的 padding 构成 | |
s_axis_data_tlast | I | 必须 | 1 | 输入数据通道tdata,由外部主机断言表示最后一组数据。只在生成事件event_tlast_unexpected 和 event_tlast_missing时,内核才使用它 | |
master 数据 | m_axis_data_tvalid | O | 必须 | 1 | 输出数据通道tvalid,由内核断言表示它能够提供数据 |
m_axis_data_tready | I | 必须 | 1 | 输出数据通道tready,由外部丛机断言表示它能够接收数据 | |
m_axis_data_tdata | O | 必须 | 可配置,16的倍数 | 输出数据通道tdata,由内核提供FFT后的数据,由高位虚部、低位实部以及各自高位的 padding 构成 | |
m_axis_data_tuser | O | 可选 | 可配置,8的倍数 | 输出数据通道tuser,由内核提供XK_INDEX,OVFLO,BLK_EXP信息 | |
m_axis_data_tlast | O | 必须 | 1 | 数据数据通道tlast,由内核断言表示最后一个输出数据 | |
状态==(可选)== | m_axis_status_tvalid | O | 可选 | 1 | 状态通道tvalid,由内核断言表示它能够提供状态数据 |
m_axis_status_tready | I | 可选 | 1 | 状态通道tready,由外部从机断言表示它能够接收状态数据 | |
m_axis_status_tdata | O | 可选 | 16 | 状态通道tdata,由内核提供的状态数据,包含BLK_EXP 或 OVFLO | |
事务 | event_frame_started | O | 必须 | 1 | 当内核开始处理新帧时,该事件信号在一个时钟周期内被置位。提供此信号是为了允许您对帧进行计数,并在需要时将内核的配置同步到特定的帧 |
event_tlast_unexpected | O | 必须 | 1 | 当内核在不是帧中最后一个的任何传入数据样本上看到 s_axis_data_tlast High 时,该事件信号被置位为一个时钟周期。这表明核心和上游数据源在帧大小方面的配置不匹配,并表明上游数据源配置为比核心更小的点大小。这仅在内核开始处理帧时计算,因此事件可能会滞后 s_axis_data_tlast 上的意外高点大量时钟周期如果一帧的 s_axis_data_tlast 上有多个意外的高点,则对它们中的每一个都进行断言 | |
event_tlast_missing | O | 必须 | 1 | 当 s_axis_data_tlast 在帧最后传入的数据样本上为低时,该事件信号在单个时钟周期内被置位。这表明内核和上游数据源在帧大小方面的配置不匹配,并表明上游数据源配置为比核心更大的点大小。这仅在内核开始处理帧时计算,因此事件可能会滞后丢失的 s_axis_data_tlast 大量时钟周期 | |
event_fft_overflow | O | 必须 | 1 | 当在 m_axis_data_tdata 上传输的数据样本中出现溢出时,该事件信号在每个时钟周期置位。只有在使用缩放算术或单精度浮点 I/O 时才会出现 FFT 溢出。在所有其他配置中,此引脚被移除 | |
event_data_in_channel_halt | O | 必须 | 1 | 该事件在内核需要来自数据输入通道的数据且没有数据可用的每个周期被置位。在实时模式下,即使帧已不可恢复地损坏,内核仍会继续处理该帧。在非实时模式下,核心处理停止并仅在数据写入数据输入通道时继续。帧没有损坏。在这两种模式下,事件保持有效,直到数据输入通道中的数据可用 | |
event_data_out_channel_halt | O | 必须 | 1 | 该事件在内核需要将数据写入数据输出通道,但因为通道中的缓冲区已满而无法写入的每个周期中被置位。发生这种情况时,核心处理会停止,所有活动都会停止,直到通道缓冲区中有可用空间。帧没有损坏。此事件引脚仅在非实时模式下可用 | |
event_status_channel_halt | O | 必须 | 1 | 该事件在内核需要将数据写入状态通道但不能因为通道上的缓冲区已满而无法写入的每个周期中被置位。发生这种情况时,核心处理会停止,所有活动都会停止,直到通道缓冲区中有可用空间。帧没有损坏。此事件引脚仅在非实时模式下可用 |
1.2.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
);
上述模式配置下,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
IP 核配置界面的 latancy 不一定符合 IP 核的实际时延,需要仿真观察输入使能信号 s_axis_data_tvalid 和输出使能信号 m_axis_data_tvalid 的间隔时延来决定。此外,输出结尾使能信号 m_axis_data_tlast 也可以作为参考标准。
详细引脚注解与实践配置可参考vivado之FFT ip核的入门学习(已补充调用模块).
FFT IP 核技术手册、引脚协议及详细介绍可参考Xilinx IP解析之 Fast Fourier Transform(FFT) v9.1.
Cordic IP 核用于实现关于信号角度或坐标的相关变换与求解,下面就配置、I/O 接口和数据处理等方面对其进行说明。
2.1.1 IP 核模块框图
2.1.2 端口引脚列表
分类 | 名称 | 方向 | 选择 | 位宽 | 说明 |
---|---|---|---|---|---|
时钟与复位 | aclk | I | 必须 | 1 | 模块工作时钟,上升沿有效 |
aclken | I | 可选 | 1 | 时钟使能信号,高电平有效,低电平时内核暂停工作,可通过此信号降低内核运行的最大时钟频率 | |
aresetn | I | 可选 | 1 | 低电平同步复位信号,优先级高于aclken,低电平至少持续两个时钟周期 | |
slave数据 | s_axis_cartesian_tdata | I | 必须 | 可配置,8的倍数 | 输入数据通道tdata,由外部主机提供待处理的数据,由高位虚部、低位实部以及各自高位的 padding 构成 |
s_axis_cartesian_tlast | I | 可选 | 1 | 输入数据通道tdata,由外部主机断言表示最后一组数据 | |
– | s_axis_cartesian_tready | O | 可选 | 1 | 输出数据通道tready,由内核断言以表示它能够接收数据 |
– | s_axis_cartesian_tuser | I | 可选 | 1 | 输入数据通道tuser |
– | s_axis_cartesian_tvalid | I | 必须 | 1 | 输入数据通道tvalid,由外部主机断言以表示它能够提供数据 |
slave相位 | s_axis_phase_tdata | I | 必须 | 可配置,8的倍数 | 输入相位通道tdata,由外部主机提供待处理的相位 |
s_axis_phase_tlast | I | 可选 | 1 | 输入相位通道tdata,由外部主机断言表示最后一组相位 | |
– | s_axis_phase_tready | O | 可选 | 1 | 输出数据通道tready,由内核断言以表示它能够接收相位 |
– | s_axis_phase_tuser | I | 可选 | 1 | 输入相位通道tuser |
– | s_axis_phase_tvalid | I | 必须 | 1 | 输入相位通道tvalid,由外部主机断言以表示它能够提供数据 |
master数据 | m_axis_dout_tdata | O | 必须 | 可配置,8的倍数 | 输出数据通道tdata,由内核提供运算处理后的数据 |
m_axis_dout_tlast | O | 可选 | 1 | 数据数据通道tlast,由内核断言表示最后一个输出数据 | |
– | m_axis_dout_tready | I | 可选 | 1 | 状态通道tready,由外部从机断言表示它能够接收状态数据 |
– | m_axis_dout_tuser | O | 可选 | 1 | 输出数据通道tuser |
– | m_axis_dout_tvalid | O | 必须 | 1 | 输出数据通道tvalid,由内核断言表示它能够提供数据 |
2.1.3 工作模式
Cordic IP 核提供了7种运算功能,分别是:Rotate、Translate、Sin and Cos、Sinh and Cosh、Arc Tan、Arc Tanh 和 Square Root. 对应 I/O 接口的使用情况如下表:
Function | s_axis_cartesian - XIN | s_axis_cartesian - YIN | s_axis_phase - IN | m_axis_dout - XOUT | m_axis_dout - YOUT | m_axis_dout - PhaseOUT |
---|---|---|---|---|---|---|
Rotate | 1 | 1 | 1 | 1 | 1 | 0 |
Translate | 1 | 1 | 0 | 1 | 0 | 1 |
Sin and Cos | 0 | 0 | 1 | 1 | 1 | 0 |
Sinh and Cosh | 1 | 1 | 0 | 0 | 0 | 1 |
Arc Tan | 0 | 0 | 1 | 1 | 1 | 0 |
Arc Tanh | 1 | 1 | 0 | 0 | 0 | 1 |
Square Root | 1 | 0 | 0 | 1 | 0 | 0 |
其中,1表示在该模式对应通道的子字段存在,0则表示该字段不存在。
根据Xilinx官方 datasheet,各通道数据结构如下图所示:
7种运算模式详解如下:
Rotate:
在该模式下,IP 核将输入的矢量旋转指定角度后得到新的矢量并输出。该功能配置下,IP核默认设置粗略旋转和选择补充缩放。官方规定的 I/O 协议如下表:
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 协议如下表:
如输入 (0.707, 0.25) 时,得到输出模值为0.75,相角为0.336 (rad).
Sin and Cos:
该模式下的 IP 核对输入的角度进行正余弦的计算。在该模式下,需要对输入角度大小进行限制,当不适用粗略旋转时,输入角度需要在第一象限 (-pi/4, pi/4) 之间;当选择粗略旋转时,角度范围则是 (-pi, pi),Cordic 算法会将输入样本旋转至第一象限内进行计算,并将输出样本反向旋转回适当象限。官方规定的 I/O 协议如下表:
当设置粗略旋转时,输入角度应被规范在 (-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
该模式下没有补充缩放选项。
Sinh and Cosh:
官方规定的 I/O 协议如下表:
略。该模式下没有补充缩放选项。
Arc Tan:
官方规定的 I/O 协议如下表:
略。该模式下没有补充缩放选项。
Arc Tanh:
官方规定的 I/O 协议如下表:
略。该模式下没有补充缩放选项。
Square Root:
官方规定的 I/O 协议如下表:
略。该模式下没有补充缩放选项。
在上述配置选项下,顶层模块例化代码如下所示:
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
);
当输入数值或角度超过 IP 核规定大小范围时,会导致溢出,因此在处理 IP 核的 I/O 数据时,需要注意以下几点:
a. 将输入数值归一至定点小数±1的范围内,可以通过重复 1bit 符号位来实现,无论正负;
b. 不要使用小值,数值越小计算误差越大;
c. 输入数据不要落在正半轴上,此时 Translate 模式下的角度运算会出错 (不为0);
d. 在核对运算结果时,数值可以按照整数或定点数来分析核对,不会出错,角度则需要使用弧度制按照定点数来分析核对,并且这种区别只有在 debug 对比数据时会出现,FPGA 内部处理的只是二进制数据而已,可以直接使用。
Cordic IP 核详细算法介绍及引脚协议可参考Vivado_Cordic IP核使用详解.
本章记录一些FPGA硬件调试过程中常用的板卡datasheet、工程debug手段、模拟电路问题排查等总结与心得体会。
单个 FPGA 芯片存在根据需要进行程序切区的情况,以下述 3 个 FLASH 烧录的程序为例记录程序固化过程:
生成 .mcs 文件和 .prm 文件
步骤1:
步骤2:
note:
a. memory part 根据芯片需要选择对应的存储单元;
b. 加载 .bit 文件时,根据文件大小填写地址,保证 FLASH 有足够空间存储每个.bit 文件;
c. memory part 有时无法搜索到目标存储单元,此时需要拔出 jtag 数据线,由步骤1重新进入界面进行搜索选择。
步骤3:
步骤4:
固化 .mcs 文件和 .prm 文件
步骤5:
note:
在 Hardware manager 中查找到对应存储单元实现固化。如若没有找到存储单元,则需要右键目标芯片并添加 (Add Configuration Memory Device).
步骤6:
note:
Configuration file 和 PRM file 分别选择先前生成的 .mcs 文件和 .prm 文件。
正在固化程序:
至此,程序固化完成。
需要注意的是,固化后的板卡在调试时可能没有加载正确的 .ltx 文件,因此不会显示对应 ila 和 vio, 此时需要左键目标芯片,在下方的 Hardware Device Properties 界面中的 Probes file 添加正确的 .ltx 文件用来调试。
第一次尝试使用这样的方法记录科研学习中遇到的一些问题和解决方案,如果存在错误或任何不妥之处欢迎批评指正,如果能够对读者提供些许帮助那自然更好,欢迎大家的交流和任何建议。
更新日期:03/10/24
note:以下为模板内容,与正文无关
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
撤销: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.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block
var foo = 'bar';
一个简单的表格是这么创建的:
项目 | Value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
---|---|---|
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 |
一个具有注脚的文本。2
Markdown将文本转换为 HTML。
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:
这将产生一个流程图。:
我们依旧会支持flowchart的流程图:
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
注脚的解释 ↩︎
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。