FPGA杂记5——格雷码转换设计
在此感谢 特权同学,接下来写到的FPGA杂记基本上源于他的B站讲解视频,同时这是他的淘宝商铺链接,感兴趣的同学也可以在B站中看到他更多的一些视频学习资料,一起学习一起成长
1. 显示和监视任务
1.1 显示任务 $display 以及 $write
两者的语法结构大概都是:任务名(“可选字符串+格式”,信号列表)
- 任务名包括$displayb(二进制)、 $displayh(十六进制)、 $displayo(八进制)以及 $write的其他任务名
- 可选字符串+格式包括%h(16进制输出)、%c(ASCII输出)、%b(二进制输出)、%s(字符串输出)、%t(以当前时间格式输出显示)等等
- display和write的区别在于display输出后会自动换行
%0d,加个0会使得输出的数据前面的零或者空格删掉(一般发生在输出要求位数大于实际输出位数的情况下)例如,32位的数据以十进制显示为65,用显示任务不加0的结果是:____(空格)65,但若是要求为%0d,那么输出结果就是65
1.2 监视任务$monitor
$monitor任务在声明后默认开启,只有在其运行期间调用系统任务 $monitoroff 则会关闭监视任务知道系统再次调用系统任务 $monitoron后将重新开启监视
以上两种主要在testbench中调用任务,方便在测试中验证自己的设计、逻辑功能的实现
2. 4位格雷码计数器设计
2.1 设计实现
设计目标:以1S为一个计数周期(注意不是系统时钟),计数值从0一直往上叠加,并将计数值不断转换成格雷码输出
`timescale 1ns/1ps
module vlg_design(input i_clk, //时钟为50MHz,20nsinput i_rst_n,output[3:0] o_gray);
/
//1s定时计数
localparam TIMER_1S_MAX_CNT = 32'd1_000_000_000/20; //50MHZ的时钟在1s内的最大周期数,1S标志位reg[31:0] r_cnt;always @(posedge i_clk)if(!i_rst_n) r_cnt <= 'b0;else if(r_cnt < (TIMER_1S_MAX_CNT - 1)) r_cnt <= r_cnt + 'b1;else r_cnt <= 'b0;//
//1s计数器
reg[3:0] r_second;always @(posedge i_clk)if(!i_rst_n) r_second <= 'b0;else if(r_cnt == (TIMER_1S_MAX_CNT-1)) r_second <= r_second + 'b1;else ;/
//r_second译码为格雷码输出
reg[3:0] r_gray;always @(posedge i_clk) if(!i_rst_n) r_gray <= 'b0;else begincase(r_second)4'b0000: r_gray <= 4'b0000;4'b0001: r_gray <= 4'b0001;4'b0010: r_gray <= 4'b0011;4'b0011: r_gray <= 4'b0010;4'b0100: r_gray <= 4'b0110;4'b0101: r_gray <= 4'b0111;4'b0110: r_gray <= 4'b0101;4'b0111: r_gray <= 4'b0100;4'b1000: r_gray <= 4'b1100;4'b1001: r_gray <= 4'b1101;4'b1010: r_gray <= 4'b1111;4'b1011: r_gray <= 4'b1110;4'b1100: r_gray <= 4'b1010;4'b1101: r_gray <= 4'b1011;4'b1110: r_gray <= 4'b1001;4'b1111: r_gray <= 4'b1000;default: ;endcase
endassign o_gray = r_gray; endmodule
看整个运行过程,各部分都会定义一个寄存器暂时储存输出结果,最后再进行赋值操作
2.2 测试脚本testbench
`timescale 1ns/1ps
module testbench_top();
//参数定义
`define CLK_PERIORD 20 //时钟周期设置为20ns(50MHz)
//接口申明
reg clk;
reg rst_n;
wire[3:0] o_gray;
//对被测试的设计进行例化
vlg_design uut_vlg_design(.i_clk(clk),.i_rst_n(rst_n),.o_gray(o_gray));
//复位和时钟产生
//时钟和复位初始化、复位产生
initial beginclk <= 0;rst_n <= 0;#1000;rst_n <= 1;
end//时钟产生
always #(`CLK_PERIORD/2) clk = ~clk;
//测试激励产生
integer i;
initial begin@(posedge rst_n); //等待复位完成$monitor("o_gray is %b at %0dns",o_gray,$time);$stop;
endendmodule
2.3 测试结果
3. 基于查找表的8位格雷码转换
3.1 查找表LUT
查找表是相当于预先存储好结果的数据表,免去了运算过程,从另外一种角度来说相当于ROM。我们接下来利用IP的ROM加入查找表,通过VIVADO创建出ROM的初始化COE文件用于存放LUT的数据
3.2 ROM初始化COE文件
COE文件用于对ROM作初始化赋值,具体格式如下
memory_initialization_radix = 位数(数据格式);memory_initialization_vector = 初始化数据; //查找表数据
COE文件制作以及使用ROM流程:
- 打开vivado工程文件点击IP Catalog选择生成ROM(以Block Memory Generator为例)
- 将打开的ROM的Memory Type改成Single Port ROM
- 在Port A Options里面将位宽与数据个数改成相应的数据,同时使能always
- 在Other Options中导入COE文件,这里由于没有COE文件可以在线创建一个即可
- 然后点击OK会弹出一个generate的窗口直接生成即可
- 回到vivado界面的Sources里面的IP Sources中查看你所配置IP的Instantiation Template中的原语 .veo 文件,并将原语用于你所设计的verilog代码中
这里要注意一下,只要在IP中勾选了Primitives Output Register,那么就意味着你会延时两个时钟周期输出数据,在Summary中也可查看
3.3 设计代码.v
`timescale 1ns/1ps
module vlg_design(input i_clk, input i_rst_n,input i_en,input[7:0] i_data,output o_vld, //有效信号output[7:0] o_gray);//
//o_vld是i_en两个时钟周期的延时
reg[1:0] r_vld;always@(posedge i_clk)if(!i_rst_n) r_vld <= 'b00;else r_vld <= {r_vld[0],i_en};assign o_vld = r_vld[1];//IP ROM_LUT
blk_mem_gen_0 uut_blk_mem_gen_0 (.clka(i_clk), // input wire clka.addra(i_data), // input wire [7 : 0] addra.douta(o_gray) // output wire [7 : 0] douta
);
endmodule
3.4 测试脚本
`timescale 1ns/1ps
module testbench_top();
//参数定义
`define CLK_PERIORD 20 //时钟周期设置为20ns(50MHz)
//接口申明
reg clk;
reg rst_n;
reg i_en;
reg[7:0] i_data;
wire o_vld;
wire[7:0] o_gray;
//对被测试的设计进行例化
vlg_design uut_vlg_design(.i_clk(clk),.i_rst_n(rst_n),.i_en(i_en),.i_data(i_data),.o_vld(o_vld),.o_gray(o_gray));
//复位和时钟产生
//时钟和复位初始化、复位产生
initial beginclk <= 0;rst_n <= 0;#1000;rst_n <= 1;
end
//时钟产生
always #(`CLK_PERIORD/2) clk = ~clk;
//测试激励产生
initial begini_en <= 'b0;i_data <= 'b0;@(posedge rst_n); //等待复位完成@(posedge clk);i_en <= 'b1;i_data <= 'b0;@(posedge clk);repeat(255) begin //循环叠加256次i_data <= i_data + 1;@(posedge clk);endi_en <= 'b0;i_data <= 'b0;#1000;$stop;
end//
//实时显示
always@(posedge clk) beginif(o_vld) $display("%b",o_gray);else ;
endendmodule
4. 格雷码转换的快速算法
4.1 算法介绍
- 二进制码(自然数)的最高位是MSB,最低位是0
- 二进制码的MSB位直接赋值给格雷码的MSB位
- 对于次高位(MSB-1)位到最低位0,使用二进制码相邻位异或的方式获取格雷码
- 若二进制码字的第i位与第i+1位相同,则第 i 位的格雷码为0
- 若二进制码字的第i位与第i+1位不同,则第 i 位的格雷码为1
举个例子:4位的二进制码1010转换格雷码(不通过LUT,直接通过RTL实现)
位数 | 操作 | 按位输出格雷码 |
---|---|---|
Bit3 | 取MSB位 | 1 |
Bit2 | 1 xor 0 | 1 |
Bit1 | 0 xor 1 | 1 |
Bit0 | 1 xor 0 | 1 |
最终输出结果:1111
4.2 模块设计
`timescale 1ns/1ps
module vlg_design #(parameter MSB = 7) //参数定义(input i_clk,input i_rst_n,input i_en,input[MSB:0] i_data,output reg o_vld,output reg[MSB:0] o_gray);always @(posedge i_clk) if(!i_rst_n) o_vld <= 'b0;else o_vld <= i_en;
//快速转换设计
always @(posedge i_clk)o_gray[MSB] <= i_data[MSB];genvar i;
generatefor(i=MSB-1;i>=0;i=i-1) beginalways @(posedge i_clk) begino_gray[i] <= i_data[i] ^ i_data[i+1];endend
endgenerateendmodule
4.3 测试脚本
`timescale 1ns/1ps
module testbench_top();
//参数定义
`define CLK_PERIORD 10 //时钟周期设置为10ns(100MHz)
parameter GRAY_MSB = 7; //定义参数
//接口申明
reg clk;
reg rst_n;
reg i_en;
reg[GRAY_MSB:0] i_data;
wire o_vld;
wire[GRAY_MSB:0] o_gray;
//对被测试的设计进行例化
vlg_design #(.MSB(GRAY_MSB))uut_vlg_design(.i_clk(clk),.i_rst_n(rst_n),.i_en(i_en),.i_data(i_data),.o_vld(o_vld),.o_gray(o_gray));
//复位和时钟产生
//时钟和复位初始化、复位产生
initial beginclk <= 0;rst_n <= 0;#1000;rst_n <= 1;
end
//时钟产生
always #(`CLK_PERIORD/2) clk = ~clk;
//测试激励产生
initial begini_en <= 'b0;i_data <= 'b0;$display("The value of GRAY_MSB is %0d",GRAY_MSB); @(posedge rst_n); //等待复位完成@(posedge clk);i_en <= 'b1;i_data <= 'b0;repeat(2**(GRAY_MSB+1)-1) begin //一共有(MSB+1)位,相当于计数值最大为2^(MSB+1)-1@(posedge clk);i_en <= 'b1;i_data <= i_data + 1;end@(posedge clk);i_en <='b0;$stop;
endalways @(posedge clk)if(o_vld) $display("%b",o_gray);else ;endmodule
4.4 测试结果
脚本显示(modelsim):
测试波形:
FPGA杂记5——格雷码转换设计相关推荐
- (45)FPGA面试题格雷码特点及其应用
1.1 FPGA面试题格雷码特点及其应用 1.1.1 本节目录 1)本节目录: 2)本节引言: 3)FPGA简介: 4)FPGA面试题格雷码特点及其应用: 5)结束语. 1.1.2 本节引言 &quo ...
- 格雷码转换成二进制c语言程序,格雷码与二进制的转换程序
/* 格雷码与二进制的转换程序 * 本程序采用递推的方法进行推导,可以转换0~2147483647之间的数(1~31位) * 推导方式如下(以三位格雷码为例): * 序号 格雷码 格雷码实值 二进制码 ...
- FPGA异步时钟域处理之格雷码转换
0 本章目录 1)时钟域定义 2)为什么要做跨时钟域的处理? 3)FPGA简介 4)结束语 1 时钟域定义 所谓时钟域,就是同一个时钟驱动的区域.单一时钟域是FPGA的基本组成部分,但是随着设计规模扩 ...
- 【verilog】 异步FIFO设计(格雷码转换,跨时钟域)
功能图 空满判别 顶层 子模块 测试 功能图 空满判别 对FIFO的空满判断采用地址位扩展的方法 waddr=raddr,fifo为空 waddr[最高bit]≠raddr[最高bit],waddr[ ...
- 【菜鸟C++学习杂记】ASCII码转换和显示
笔者今年刚考上华东某高校的计算机研究生,本科虽是计算机学院,但期间主攻C#和Unity游戏开发,研究生属于小跨到计算机科学与技术,目前在进行硕导给的假期期间学习C++的任务,此类杂记主要记录下编码过程 ...
- 数字电路基础知识——格雷码和二进制码的转换的算法和Verilog实现
数字电路基础知识--格雷码和二进制码的转换的算法和Verilog实现 关于数字电路中的码制问题在这篇博客中已经做了详细分析, 数字电路基础知识--数字IC中的进制问题(原码,反码,补码以及各进制的转换 ...
- 异步FIFO的设计详解(格雷码计数+两级DFF同步)
文章目录 一.异步FIFO介绍 1.1.空满判断 1.2.跨时钟域问题 1.3.格雷码转换 1.4.格雷码计数器 二.代码code 一.异步FIFO介绍 FIFO有同步和异步两种,同步即读写时钟相 ...
- 【基础知识】~ 进制转换、补码、格雷码、BCD码、独热码
1. 进制转换 1.1 十进制 to 二进制 正整数转二进制:除二取余,然后倒序排列,高位补零. 负整数转二进制:先是将对应的正整数转换成二进制后,对二进制取反,然后对结果再加一. 小数转二进制:对小 ...
- 关于格雷码的规律、转换
发现数电书中对于格雷码并没有很好地解释,这里保留下来所理解的知识供自己日后查看.这里主要说明排列规律和转换. 格雷码特点: 任意两个相邻的代码只有一位二进制数不同.并且首尾相连,属于循环码(这里我发现 ...
- C语言学习:二进制码与格雷码的转换
格雷码,又叫循环二进制码或反射二进制码,格雷码是我们在工程中常会遇到的一种编码方式,它的基本的特点就是任意两个相邻的代码只有一位二进制数不同. 格雷码的基本特点就是任意两个相邻的代码只有一位二进制数不 ...
最新文章
- 老男孩Python全栈开发(92天全)视频教程 自学笔记20
- Spring 注解配置
- 硬核!手写一个优先队列
- shell批量监控网站状态码
- Windows下python虚拟环境pip依旧使用全局pip的解决办法
- Model compatibility cannot be checked because the database does not contain model metadata
- 学python对数学要求高吗_人工智能的小男孩 大专学历的人没有数学基础想学习python技术未来能往大数据或人工智能方向进行职业发展吗?...
- kafka简介(大数据技术)
- 剑指offer之链表中环的入口结点
- Adobe Edge Animate 1.0 概述
- MFC 消息映射表和虚函数实现消息映射到底谁的效率高
- xpath提取目录下所有标签内的内容,递归 //text()
- Web渗透测试实战——(2.1)Metasploit 6.0初步
- mpp格式文用什么软件打开
- 蔬菜出口流程 |外贸流程
- 题解 洛谷P2142 高精度减法
- MatlabR2018aWin 安装教程 (附文件)
- 计算机英语邓广慧,地方高校理工类大学生专业英语教学改革探讨
- 冰盾ARP防火墙V1.0 Build 80122
- DDR从channel/rank/chip/bank/row/col/cell,DDR/GDDR/HBM