概述
文章目录
- 1、原码、反码、补码
- 2、十进制转R进制编码并进行浮点数处理
- 3、关于各种存储器的名词解释
- 3.1 FPGA内部有哪两种存储器资源
- 3.2 题目——SRAM和DRAM叙述
- 4、根据电路图画波形并描述电路功能
- 5、任意切换1-8的分频电路
- 6、考察格雷码和状态机
- 7、跨时钟域数据传递的基本方法
- 7.1 握手协议
- 8、最小时钟周期的计算
- 9、串口发送数据
- 10、时序约束的概念和基本策略
- 附加约束的作用
- 11、JK触发器
1、原码、反码、补码
1.1、原码:符号位 + 真值 ,最高位表示符号位,如下以四位为例,可表示的范围的:【-7 , +7】
正7 :0 111
负7: 1 111
1.2、反码:正数的反码是它本身,负数的反码是除符号位外,其余各位取反。表示范围:【-7 , +7】
正7 :0 111 (原码) —— 0 111(反码)
负7: 1 111 (原码)—— 1 000(反码)
1.3、补码:正数补码是原码,负数的补码是在反码+1。补码取值范围 [-2^ (n-1), 2^( n-1) - 1] ,因此该范围【-8 , +7】 ,因为不存在-0,因此补码中的1000表示-8。因此补码可表示更多整数。
正7 :0 111 (原码) —— 0 111(反码)—— 0 111(补码)
负7: 1 111 (原码)—— 1 000(反码)——1001(补码)
1.4、为什么要用补码
原码虽然比较直观且便于十进制转换等,但是当进行加减法的时候较为复杂。(比如:两个数加法,若符号位相同则相加,符号位不同做减法,做减法的时候还要比较两个数的绝对值大小,大的数-小的数,进而确定最终数值的符号)
当使用补码的时候,符号位也可以参与运算,且不影响最后的结果。例如:
-7 + 3 = 1 111(原码) + 0011(原码) = 1001(补码) + 0101 (补码)= 1100 (补码 )= 1011(反码)= 1100(原码)= -4
7 + (- 3) = 0111 (原码)+ 1 011(原码) = 0111(补码)+ 1101 (补码) = 0100(补码) = 0100(反码) = 0100(原码) = 4
-7 + (-1) = 1111(补码)+ 1001(补码) = 1000(补码) = -8
1.5 补码转换成原码
符号位不变,数值位按位取反,末位再加1
原码、反码、补码
2、十进制转R进制编码并进行浮点数处理
十进制转二进制:整数部分 + 小数部分
整数部分:除以R取余,直到不能除; 小数部分:乘以2取整,直到积的小数部分为0
R进制数转换为十进制数:按权展开,相加——比较简单,不再赘述
十进制转八进制:先转二进制,后三位一组,整数部分位数不够左边补0,小数部分不够右边补0 十进制转十六进制:四位一组,其余相同。
给出如下的例子:
十进制:20.25 进行转换
二进制:10100.01
八进制:010 100 . 010
十六进制:0001 0100. 0100
同理: 十进制46.25对应的二进制表达式为(101110.01)
除此之外还可以掌握FPGA中浮点数的定点化处理技术
3、关于各种存储器的名词解释
ROM(Read Only Memory):只读存储器,顾名思义只能读数据,而不能写入,断电不丢失。
RAM(Random Access Memory):随机存取存储器,其内部的存储数据可随意的取出和存入,同时读写速度与存储数据在存储芯片中的位置无关。
SRAM(Static Random-Access Memory):静态随机存取存储器,只要供电数据就能保持,断电数据消失
DRAM(Dynamic Random Access Memory):动态随机存储器,用电容存储电荷的多少,来代表一个bit是0还是1(它能通过给电容周期性充电实现刷新,然后通过不断刷新保证数据不丢失)
SDRAM(Synchronous Dynamic Random Access Memory):同步动态随机存储器,同步写入和读出数据(需要时钟信号)。
EEPROM(Electrically Erasable Programmable Read-Only Memory):电可擦除可编程只读存储器(借助计算机程序对其进行重复擦写)。
DDR( Double Data Synchronous Dynamic Random Access Memory):双倍速率同步动态随机存储器,双倍速率传输的SDRAM,也就是在时钟的上升沿和下降沿都能进行数据传输。
FLASH(Flash Memory):闪存,非易失性固态存储。(内存卡或U盘)
以下关于存储器的描述正确的是( CD )。
A RAM在断电后信息不会丢失,接通电源即可使用
B ROM可以任意进行读写操作
C RAM可以任意进行读写操作
D ROM在断电后信息不会丢失,接通电源即可使用
3.1 FPGA内部有哪两种存储器资源
FPGA 芯片内有两种存储器资源:
1、BLOCK RAM
2、分布式RAM(由 LUT 配置成的内部存储器 )
BLOCK RAM 是由一定数量固定大小的存储块构成的,使用BLOCK RAM 资源不占用额外的逻辑资源且速度快。但 BLOCK RAM 消耗的资源是其块大小的整数倍。
3.2 题目——SRAM和DRAM叙述
题目1:—— 第二个正确
二者的区别为D和S,S表示静态,D表示动态。
静态就是只要保持通电,就能存储数据,而动态的话要进行周期性的刷新,不然数据就会丢失。因此1 4 错误。
SRAM的集成度较低,且SRAM使用CMOS晶体管存储数据,DRAM采用电容存储数据,因此SRAM功耗比DRAM大 。
同时要能正确存储一位数据需要最少6个晶体管,因此SRAM却需要很大的体积,成本也更高。
总的来说,相同容量的DRAM内存可以设计为较小的体积,而且同样面积的硅片可以做出更大容量的DRAM,
题目2:
SRAM面积大小与那些因素相关 ——ABCD
A. 容量,即总bit数
B. 地址译码方式
C. 禁布区
D. BIST电路
题目3:
(大疆2020芯片工程师A卷,单选)1个16K x 8位的存储器,其地址线和数据线总和是
A、46
B、17
C、48
D、22
解析:
地址线:16K = 1K * 16 = 1024* 16 = (2 ^10 )*(2^4) = 2^14,即需要 14 根地址线;
数据线:8 位数据需要 8 根数据线;因此共需要 22 根线。——D
RAM 、ROM、IP核
4、根据电路图画波形并描述电路功能
给出下面电路图,画出Q0,Q1以及Q2的波形,并描述电路功能(笔试题)
题目分析:可看到本题为三级D触发器,因此Q0,Q1,Q2波形相同,只是依次延迟一拍。
对于input初值:一般地最开始电路是复位的,因此复位时的初值低电平0,又因input是Q0和Q1与非操作后的值,因此input = 1 ,所以复位的时候输入初值一直为高电平1.
当复位无效的时候,开始实现电路功能。
1、分析第一个输入时的Q0,Q1,Q2 : 首先clk1时 input = 1 ,因此在复位无效且clk2上升沿时,第一个触发器的输出端Q0接收上一时刻input的值为1 ,clk3上升沿 Q1 接收Q0的值为1 ;clk4上升沿 Q2接收Q1的值为1.
2、分析第二个输入时:基于1,可得到clk2 时的input = 0(Q0和Q1与非运算得到,组合逻辑),因此clk3时Q0 = 0,clk4时 Q1 = 0;clk4时 Q2 = 0;
3、分析第三个输入时:基于2,可得到clk3 时的input = 0;因此clk4时Q0 = 0,clk5时 Q1 = 0;clk6时 Q2 = 0;
依次类推,得到如下波形:
可看到,输出Q2一个周期内保护三个周期的clk,因此该电路实现的功能可说三分频。
verilog代码实现:
module test(
input rst_n,
input clk,
output reg q2
);
wire in1_data;
reg q0, q1;
always @(posedge clk or negedge rst_n)
if(!rst_n)begin
q0 <= 0;
q1 <= 0;
q2 <= 0;
end
else begin
q0 <= in1_data;
q1 <= q0;
q2 <= q1;
end
assign in1_data = (~q0) & (~q1);
endmodule
生成的RTL:
tb仿真:
`timescale 1ns / 1ps
module test_tb(
);
reg clk;
reg rst_n;
wire q2;
initial begin
clk = 0;
forever
#2 clk = ~clk;
end
initial begin
rst_n = 1'b0;
#5
rst_n = 1'b1;
end
test test_tb(
.rst_n(rst_n),
.clk(clk),
.q2(q2)
);
endmodule
波形图:
可看出,和我们上面分析得到的波形图相同。
因此本题电路实现的功能为三分频,或者说三进制计数器。
5、任意切换1-8的分频电路
题目4中相当于实现了三分频的功能,若我们想要实现任意分频的电路该怎么做呢?
首先任意分频电路分为两种:奇数分频和偶数分频,偶数分频可直接用计数器实现,奇数分频除了计数器外,还需要配合下降沿采样以及组合逻辑门运算。
首先进行FPGA设计,流程就是功能定义、设计输入等
1、确定实现任意分频电路的功能,后确定输入输出。
module test(
input clk,
input rst_n,
input [3:0] div, //可表示1-8
output clk_out
);
endmodule
2、共有8种分频模式,1-8 ,我们可用case语句来进行选择。
当进行分频的时候,我们为八种模式设置8个使能信号来分别驱动(en1,en2……),为简化,直接设置 reg [7:0] fre_en,让其每一位分别成为八种分频的使能信号。
//根据分频输入值div来决定对应分频使能信号有效,比如:div=1,则1分频使能信号fre_en[0]有效。
//定义八种分频模式
parameter DIV1 = 1, DIV2 = 2,DIV3 = 3,DIV4 = 4;
parameter DIV5 = 5, DIV6 = 6,DIV7 = 7,DIV8 = 8;
always @ (posedge clk or negedge rst_n)
if(!rst_n)
fre_en <= 8'd00000000;
else begin
case(div)
DIV1: fre_en <= 8'd00000001; //fre_en[0]为1分频使能信号
DIV2: fre_en <= 8'd00000010; //fre_en[1]为2分频使能信号
DIV3: fre_en <= 8'd00000100;
DIV4: fre_en <= 8'd00001000;
DIV5: fre_en <= 8'd00010000;
DIV6: fre_en <= 8'd00100000;
DIV7: fre_en <= 8'd01000000;
DIV8: fre_en <= 8'd10000000;
default:fre_en <= 8'd00000000;
end
3、各种模式下的分频应该如何实现?
首先1分频相当于不分频
2分频可以采用取反~实现
其余分频则按照计数实现
4、3-8计数器设计
通过3-8计数器设计来完成3-8分频
//3-8计数器设计
//1、2分频时均不需要采用计数器
always @(posedge clk or negedge rst_n)
if(!rst_n)
fre_cnt <= 0;
else
case(1'b1)
fre_en[0]: ; //1分频
fre_en[1]: ; //2分频
fre_en[2]:begin //3分频
if(fre_cnt == 4'd2)
fre_cnt <= 0;
else
fre_cnt <= fre_cnt + 1'b1;
end
fre_en[3]: begin //4分频
if(fre_cnt == 4'd3)
fre_cnt <= 0;
else
fre_cnt <= fre_cnt + 1'b1;
end
fre_en[4]: begin //5分频
if(fre_cnt == 4'd4)
fre_cnt <= 0;
else
fre_cnt <= fre_cnt + 1'b1;
end
fre_en[5]: begin //6分频
if(fre_cnt == 4'd5)
fre_cnt <= 0;
else
fre_cnt <= fre_cnt + 1'b1;
end
fre_en[6]: begin //7分频
if(fre_cnt == 4'd6)
fre_cnt <= 0;
else
fre_cnt <= fre_cnt + 1'b1;
end
fre_en[7]: begin //8分频
if(fre_cnt == 4'd7)
fre_cnt <= 0;
else
fre_cnt <= fre_cnt + 1'b1;
end
default: fre_cnt <= 0;
endcase
5、八种模式下分频功能的设计实现
根据3、4,我们就能通过分频使能完成每种模式下的分频功能。
//有了分频使能信号,以及分频计数信号,来完成分频功能
reg clk_out_r; //奇数分频中间变量
reg clk_even; //偶数分频直接输出
always @(posedge clk or negedge rst_n)
if(!rst_n)
clk_even <= 0;
else
case(1'b1)
fre_en[0]: ; //1分频
fre_en[1]: clk_even <= ~ clk_even ; //2分频
fre_en[2]:begin //3 分频
if(fre_cnt == 1) // ———— (N-1)/2
clk_out_r <= ~clk_out_r;
else if(fre_cnt == 2)
clk_out_r <= ~clk_out_r;
else
clk_out_r <= clk_out_r;
end
fre_en[3]:begin //4 分频
if(fre_cnt == 1)
clk_even <= ~clk_even;
else if(fre_cnt == 3)
clk_even <= ~clk_even;
else
clk_even <= clk_even;
end
fre_en[4]:begin //5 分频
if(fre_cnt == 2) //———— (N-1)/2
clk_out_r <= ~clk_out_r;
else if(fre_cnt == 4)
clk_out_r <= ~clk_out_r;
else
clk_out_r <= clk_out_r;
end
fre_en[5]: begin // 6分频
if(fre_cnt == 2)
clk_even <= ~clk_even;
else if(fre_cnt == 5)
clk_even <= ~clk_even;
else
clk_even <= clk_even;
end
fre_en[6]: begin //7 分频
if(fre_cnt == 3) //———— (N-1)/2
clk_out_r <= ~clk_out_r;
else if(fre_cnt == 6)
clk_out_r <= ~clk_out_r;
else
clk_out_r <= clk_out_r;
end
fre_en[7]: begin //8 分频
if(fre_cnt == 3)
clk_even <= ~clk_even;
else if(fre_cnt == 7)
clk_even <= ~clk_even;
else
clk_even <= clk_even;
end
endcase
6、奇数分频下,我们可以对下降沿进行采样,然后通过奇数分频产生的信号与下降沿信号进行或操作得到最终的分频结果。
//对奇数分频进行下降沿采样
//仍然采用计数器的形式对时钟下降沿检测
//在敏感列表中写成,时钟下降沿的时候才可以采样
//下降沿采样模块
reg clk_nedge;
always @(negedge clk or negedge rst_n) begin
if(~rst_n) begin
clk_nedge <= 0;
end
else begin
case(1'b1)
fre_en[0]: ;
fre_en[1]: ;
fre_en[2]: begin //3分频
clk_nedge <= clk_out_r;
end
fre_en[3]: ;
fre_en[4]: begin //5分频
clk_nedge <= clk_out_r;
end
fre_en[5]: ;
fre_en[6]: begin //7分频
clk_nedge <= clk_out_r;
end
fre_en[7]: ;
endcase
end
end
分频结果:
//产生分频时钟
//0 , 2 4 6 8 分频就是clk_even
//1 3 5 7 需要将分频信号与时钟下降沿或运算
assign clk_out = ( fre_en[0] | fre_en[1] | fre_en[3] | fre_en[5] | fre_en[7] )? clk_even : (clk_out_r | clk_nedge);
最终verilog代码:
//设计的时候共有1-8的分频,每种分频不同,因此我们设有八种分频模式,每种模式下采用不同分频方式
//设置8个使能信号来分别驱动八种模式,为简化,直接设置 reg [7:0] fre_en;让其每一位分别作为八种分频的八种使能信号
module test(
input clk,
input rst_n,
input [3:0] div, //可表示1-8
output clk_out
);
reg [7:0] fre_en; //分频使能信号
//根据分频输入值div来决定对应分频使能信号有效,比如:div=1,则1分频使能信号fre_en[0]有效。
//定义八种分频模式
parameter DIV1 = 1, DIV2 = 2,DIV3 = 3,DIV4 = 4;
parameter DIV5 = 5, DIV6 = 6,DIV7 = 7,DIV8 = 8;
always @ (posedge clk or negedge rst_n)
if(!rst_n)
fre_en <= 8'd00000000;
else begin
case(div)
DIV1: fre_en <= 8'd00000001; //fre_en[0]为1分频使能信号
DIV2: fre_en <= 8'd00000010; //fre_en[1]为2分频使能信号
DIV3: fre_en <= 8'd00000100;
DIV4: fre_en <= 8'd00001000;
DIV5: fre_en <= 8'd00010000;
DIV6: fre_en <= 8'd00100000;
DIV7: fre_en <= 8'd01000000;
DIV8: fre_en <= 8'd10000000;
default:fre_en <= 8'd00000000;
endcase
end
//3-8计数器设计
//1、2分频时均不需要采用计数器
reg [3:0] fre_cnt;
always @(posedge clk or negedge rst_n)
if(!rst_n)
fre_cnt <= 0;
else
case(1'b1) //顺序优先级
fre_en[0]: ; //1分频
fre_en[1]: ; //2分频
fre_en[2]:begin //3分频
if(fre_cnt == 4'd2)
fre_cnt <= 0;
else
fre_cnt <= fre_cnt + 1'b1;
end
fre_en[3]: begin //4分频
if(fre_cnt == 4'd3)
fre_cnt <= 0;
else
fre_cnt <= fre_cnt + 1'b1;
end
fre_en[4]: begin //5分频
if(fre_cnt == 4'd4)
fre_cnt <= 0;
else
fre_cnt <= fre_cnt + 1'b1;
end
fre_en[5]: begin //6分频
if(fre_cnt == 4'd5)
fre_cnt <= 0;
else
fre_cnt <= fre_cnt + 1'b1;
end
fre_en[6]: begin //7分频
if(fre_cnt == 4'd6)
fre_cnt <= 0;
else
fre_cnt <= fre_cnt + 1'b1;
end
fre_en[7]: begin //8分频
if(fre_cnt == 4'd7)
fre_cnt <= 0;
else
fre_cnt <= fre_cnt + 1'b1;
end
default: fre_cnt <= 0;
endcase
//有了分频使能信号,以及分频计数信号,来完成分频功能
reg clk_out_r; //奇数分频中间变量
reg clk_even; //偶数分频直接输出
always @(posedge clk or negedge rst_n)
if(!rst_n) begin
clk_even <= 0;
clk_out_r <= 0;
end
else
case(1'b1)
fre_en[0]: ; //1分频
fre_en[1]: clk_even <= ~ clk_even ; //2分频
fre_en[2]:begin //3 分频
if(fre_cnt == 1) // ———— (N-1)/2
clk_out_r <= ~clk_out_r;
else if(fre_cnt == 2)
clk_out_r <= ~clk_out_r;
else
clk_out_r <= clk_out_r;
end
fre_en[3]:begin //4 分频
if(fre_cnt == 1)
clk_even <= ~clk_even;
else if(fre_cnt == 3)
clk_even <= ~clk_even;
else
clk_even <= clk_even;
end
fre_en[4]:begin //5 分频
if(fre_cnt == 2) //———— (N-1)/2
clk_out_r <= ~clk_out_r;
else if(fre_cnt == 4)
clk_out_r <= ~clk_out_r;
else
clk_out_r <= clk_out_r;
end
fre_en[5]: begin // 6分频
if(fre_cnt == 2)
clk_even <= ~clk_even;
else if(fre_cnt == 5)
clk_even <= ~clk_even;
else
clk_even <= clk_even;
end
fre_en[6]: begin //7 分频
if(fre_cnt == 3) //———— (N-1)/2
clk_out_r <= ~clk_out_r;
else if(fre_cnt == 6)
clk_out_r <= ~clk_out_r;
else
clk_out_r <= clk_out_r;
end
fre_en[7]: begin //8 分频
if(fre_cnt == 3)
clk_even <= ~clk_even;
else if(fre_cnt == 7)
clk_even <= ~clk_even;
else
clk_even <= clk_even;
end
endcase
//对奇数分频进行下降沿采样
//仍然采用计数器的形式对时钟下降沿检测
//在敏感列表中写成,时钟下降沿的时候才可以采样
//下降沿采样模块
reg clk_nedge;
always @(negedge clk or negedge rst_n) begin
if(~rst_n) begin
clk_nedge <= 0;
end
else begin
case(1'b1)
fre_en[0]: ;
fre_en[1]: ;
fre_en[2]: begin //3分频
clk_nedge <= clk_out_r;
end
fre_en[3]: ;
fre_en[4]: begin //5分频
clk_nedge <= clk_out_r;
end
fre_en[5]: ;
fre_en[6]: begin //7分频
clk_nedge <= clk_out_r;
end
fre_en[7]: ;
endcase
end
end
//产生分频时钟
//0 , 2 4 6 8 分频就是clk_even
//1 3 5 7 需要将分频信号与时钟下降沿或运算
assign clk_out = ( fre_en[0] | fre_en[1] | fre_en[3] | fre_en[5] | fre_en[7] )? clk_even : (clk_out_r | clk_nedge);
endmodule
tb测试代码:
`timescale 1ns / 1ps
`define clock_period 20
module test_tb(
);
reg clk;
reg rst_n;
reg [3:0] div;
wire clk_out;
initial begin
clk = 0;
forever
#2 clk = ~clk;
end
initial begin
rst_n = 0;
div = 5;
#15
rst_n = 1;
#60
div = 2;
end
test u1tb(
.clk(clk),
.rst_n(rst_n),
.div(div),
.clk_out(clk_out)
);
endmodule
6、考察格雷码和状态机
一个四位十六个状态的格雷码计数器,起始值为1001,经过100个时钟脉冲作用之后的值为(0011)
- 本题有两个重点:
1、如果采用格雷码对16个状态进行编码
2、100个时钟脉冲后处于第几个状态
- 解题
1、格雷码的编码方式:任意两个相邻的代码只有一位二进制数不同。 步骤如下:
第一种计算方式:
assign gray_code = (bnary >> 1) ^ bnary;
举例子:求3的格雷码:
格雷码 = (011>> 1 )^ 011 = 001 ^ 011 =010
第二种计算方式:
第一步,改变最右边的位元值;
第二步,改变右起第一个为1的位元的左边位元;
举例子:
假设产生4位元的格雷码,原始值位 0000
第一步:改变最右边的位元值: 0001
第二步:改变右起第一个为1的位元的左边位元: 0011
重复1:
第三步:改变最右边的位元值: 0010
第四步:改变右起第一个为1的位元的左边位元: 0110
重复2:
第五步:改变最右边的位元值: 0111
第六步:改变右起第一个为1的位元的左边位元: 0101
……
最终我们得到4位典型格雷码:
2、得到格雷码后,我们计算100个时钟脉冲作用之后在哪个状态。
100/16 = 6余4,因此经过了六轮状态循环,后再加四个状态。由于起始值为1001,因此1001状态基础上加4,为0011。
7、跨时钟域数据传递的基本方法
下面哪个不属于跨时钟域数据传递的基本方法( D ):
A:使用多级触发器缓冲
B:使用FIFO
C:使用握手协议
D: 信号通路上插入isolation
题解:
A:在慢时钟域到快时钟域的数据传输中,需要使用两级触发器进行同步,消除亚稳态。
B:当多比特数据传输的时候采用异步FIFO处理,因为同步后的数据只能保持稳态,但并不能确定到底是0还是1,因此如果多比特数据也用多级触发器缓冲的方式,会出现乱码,因此采用FIFO。
C:握手协议常用于慢时钟域到快时钟域的多比特数据的传输中,对应于单比特数据从慢时钟域到快时钟域的传输我们常用多级触发器同步,但也可采用握手协议,能确定稳态的时候到底输出值是0还是1,从而让数据能正确传输,
7.1 握手协议
如下是握手通信的流程:
一般步骤:
1、发送域将数据放入总线
2、发出req请求信号给接收端,接收端检测到该信号有效的时候即可所存数据总线
3、然后发出ack应答信号,表示数据读取完成。
4、发送域检测到ack信号有效后,撤销当前的req请求信号。
5、接受域检测到req请求信号被撤销后,它也将ack撤销。
6、此时,完成了一次正常的握手通信。
7、这时,即可进行下一次握把通信,依次循环……
通过握手协议我们即可实现信号同步
握手协议的RTL如下:
包括发送域以及接受域两个模块。
核心部分:
发送域的req请求信号有效,说明告诉接受域数据准备好了,可以准备接收了。
接受域根据req信号将数据接收,接收完成后,发出ack应答信号。
当发送域检测到ack信号为高电平时,说明已经接收到了数据不用在请求,然后将req置低电平,对应的ack也变成低电平。
当发送端的req又变为高电平时,表示可发送下一数据……
顶层模块的verilog实现:
//握手同步设计,包括发送域和接受域两个部分
module test(
input tclk, //发送时钟
input rclk , //接收时钟
input [31:0] transmit_data ,
input reset_tclk,
input reset_rclk ,
input data_avail ,
output t_req ,
output [31:0] t_data ,
output r_ack
);
//发送模块例化
transmit u1(
.tclk(tclk),
.reset_tclk(reset_tclk),
.t_req(t_req ),
.data_avail(data_avail ),
.transmit_data(transmit_data ),
.t_data(t_data ),
.r_ack(r_ack )
);
//接收模块例化
receiver u2(
.rclk(rclk ),
.reset_rclk(reset_rclk ),
.t_req(t_req ),
.t_data(t_data ),
.r_ack(r_ack )
);
endmodule
子模块:发送模块的verilog实现
//发送端设计
module transmit(
input tclk,
input reset_tclk,
input data_avail,
input [31:0]transmit_data,
input r_ack,
output reg t_req,
output reg [31:0] t_data
);
localparam IDLE_T = 2'd0;
localparam ASSERT_t_req = 2'd1; //请求信号有效状态
localparam DEASSERT_t_req = 2'd2; //请求信号无效状态
reg [1:0] t_state, t_state_nxt; //当前、下一状态
reg t_req_nxt;
reg [31:0] t_data_nxt;
reg r_ack_tclk;
//状态机第一段:状态寄存器
always@(posedge tclk or negedge reset_tclk)begin
if(!reset_tclk)
t_state <= IDLE_T;
else
t_state <= t_state_nxt;
end
//第二段:组合逻辑描述状态转移和输出
always@(*)begin
case( t_state)
IDLE_T:begin
if(data_avail) begin
t_req_nxt = 1'b1;
t_state_nxt = ASSERT_t_req;
t_data_nxt = transmit_data;
end
else begin
t_req_nxt = 1'b0;
t_data_nxt =t_data;
t_state_nxt = IDLE_T;
end
end
//请求有效状态
ASSERT_t_req:begin
if(r_ack_tclk)begin //应答信号为高电平时,请求信号无效
t_req_nxt = 1'b0;
t_state_nxt = DEASSERT_t_req;
t_data_nxt = 'd0;
end
else begin
t_req_nxt = 1'b1;
t_data_nxt = transmit_data;
t_state_nxt = ASSERT_t_req;
end
end
//请求无效状态
DEASSERT_t_req:begin
if(!r_ack_tclk)begin //应答信号高电平变成了低电平,说明此时完了一次握手,此时判断下一数据有效……
if(data_avail)begin
t_req_nxt = 1'b1;
t_state_nxt = ASSERT_t_req;
t_data_nxt = transmit_data;
end
else begin
t_req_nxt = 1'b0;
t_state_nxt = IDLE_T;
t_data_nxt = 'd0;
end
end
end
endcase
end
//输出延迟一拍
always@(posedge tclk or negedge reset_tclk)begin
if(!reset_tclk)begin
t_req <= 1'b0;
t_data <= 32'h00000000;
r_ack_tclk <= 1'b0;
end
else begin
t_req <= t_req_nxt;
t_data <= t_data_nxt;
r_ack_tclk <= r_ack; //给接收域的应答信号
end
end
endmodule
子模块:接收模块的verilog实现
module receiver(
input rclk,
input reset_rclk,
input t_req,
input[31:0] t_data,
output reg r_ack
);
reg r_state,r_state_nxt; //现态和次态
reg t_req_rclk; //发送域发来的请求信号
reg[31:0] t_data_rclk;
reg[31:0] t_data_rclk_nxt;
reg r_ack_nxt;
localparam IDLE_R = 1'b0;
localparam ASSERT_ACK = 1'b1;
//状态机第一段,状态寄存器
always@(posedge rclk or negedge reset_rclk)
if(!reset_rclk)
r_state <= IDLE_R;
else
r_state <= r_state_nxt;
//状态机第二段,组合逻辑表述状态转移和输出
always@(*)begin
case(r_state)
IDLE_R:begin
if(t_req_rclk)begin //当接收到发送域的有效请求信号,则跳转到应答有效状态
r_state_nxt = ASSERT_ACK;
t_data_rclk_nxt = t_data; //将发送域发来的数据放在接收域总线存储
r_ack_nxt = 1'b1; //此时应答为高电平
end
else begin
r_state_nxt = IDLE_R;
t_data_rclk_nxt = t_data_rclk;
r_ack_nxt = 1'b0;
end
end
ASSERT_ACK:begin //应答有效状态
if(!t_req_rclk)begin //如果请求无效,应答信号也无效
r_state_nxt = IDLE_R;
r_ack_nxt = 1'b0;
end
else begin
r_ack_nxt = 1'b1;
end
end
endcase
end
//输出延迟一拍
always@(posedge rclk or negedge reset_rclk)begin
if(!reset_rclk)begin
t_data_rclk <= 1'b0;
t_req_rclk <= 1'b0;
r_ack <= 1'b0;
end
else begin
t_data_rclk <= t_data_rclk_nxt;
t_req_rclk <= t_req;
r_ack <= r_ack_nxt;
end
end
endmodule
tb测试文件:
给出两组数据进行验证
`timescale 1ns/1ns
module test_tb ;
reg tclk ,rclk ;
reg [31:0] transmit_data ;
reg reset_tclk ,reset_rclk ;
reg data_avail ;
wire t_req ;
wire [31:0] t_data ;
wire r_ack ;
parameter CLK_HALF_PERIOD1 = 5;
parameter CLK_HALF_PERIOD2 = 8;
parameter RESET_DELAY = 100;
test u1(
.tclk (tclk ),
.rclk (rclk ),
.transmit_data (transmit_data ),
.reset_tclk (reset_tclk ),
.reset_rclk (reset_rclk ),
.data_avail (data_avail ),
.t_req (t_req ),
.t_data (t_data ),
.r_ack (r_ack )
);
//产生时钟激励
initial begin
tclk = 0;
rclk = 0;
end
always #CLK_HALF_PERIOD1 tclk = ~tclk ;
always #CLK_HALF_PERIOD2 rclk = ~rclk ;
//产生复位激励
initial begin
reset_rclk = 0;
reset_tclk = 0;
#RESET_DELAY reset_rclk = 1;
reset_tclk = 1;
end
//产生输入
initial begin
#500;
data_avail = 1;
transmit_data = 32'h57431446;
#200;
data_avail = 0;
#200;
data_avail = 1;
transmit_data = 32'h77411146;
#1000;
$stop;
end
endmodule
总体仿真波形如下:
我们给出了两组数据。
对信号进行分析:
当数据有效时,data_avial = 1,在此高电平下根据应答信号r_ack,请求信号t_req可实现高低电平变化。当请求信号为高电平的时候,接收域接收数据,因此t_data在时钟上升沿接收到发送端的数据transmit_data,当请求信号无效时,说明接收端不能接收数据,数据为0.
还可看到当数据有效,请求信号才能根据应答信号变化,同时应答信号低电平时,请求信号变为高电平;
数据无效时,请求应答信号为0,同样的接收域数据也为0
下图可看出,每次数据的接收在发送域的时钟上升沿发生,也因此数据得到了正确接收
握手同步信号的代码参考
8、最小时钟周期的计算
最小时钟周期公式及分析
题目:
Tco = 1ns,Tsu = 2ns,Thold = 1ns,Tlogic_max = 4ns, Tlogic_min = 3ns, Jitter = 2ns,求最小时钟周期:
Tmin = Tco + Tlogicmax + Tsu - Tskew + Tjitter = 8ns
由此还可计算最大时钟频率:T = 1/ Tmin
9、串口发送数据
题目:简单画出串口发送时序图,串口是否能够一次发送有效数据位由8变为1024?并说明理由,为什么能或者不能。
串口发送时:当处于空闲状态时,数据线为高电平,接着是起始位(低电平),数据位(8位),停止位(高电平)。
若我们现在要发送的数据为01010101,则串口发送时序图如下:注意:从低位开始传输
在串口传数据中是串口一次发送有效位一般是8位,传输完8位数据后,进入空闲状态,后进行下一次八位数据的传输。
串口波特兰有9600、115200等,当波特率为115200时,需要串口时钟115200Hz,FPGA时钟不能精确地产生这种波特时钟,因此会导致每个传输位有误差。因此如果要一次发送有效数据1024,也就是1024位,会出现误差且数据越多误差越大,可能造成采样错误。
10、时序约束的概念和基本策略
时序约束主要包括周期约束,偏移约束,静态时序路径约束三种。通过附加时序约束可以综合布线工具调整映射和布局布线,是设计达到时序要求
附加时序约束的一般策略是先附加全局约束, 后对快、慢速例外路径附加专门约束。
附加全局约束时首先定义设计的所有时钟, 对各时钟域内的同步元件进行分组, 对分组附加周期约束,然后对 FPGA输入输出 PAD 附加偏移约束、对全组合逻辑的 PAD TO PAD 路径附加约束。
附加专门约束时,首先约束分组之间的路径,然后约束快、慢速例外路径和多周期路径,以及其他特殊路径。
附加约束的作用
1:减少了逻辑和布线延时,从而提高设计电路的工作频率;
2:获得正确的时序分析报告;(静态时序分析工具以约束作为判断时序是否满足设计要求的标准, 因此要求设计者正确输入约束,以便静态时序分析工具输出正确的时序报告)
3:指定 FPGA的电气标准和引脚位置。
11、JK触发器
给出如下的电路,写出驱动方程并画出波形,描述其功能。
驱动方程:
波形图:
功能:可描述占空比为1/3的分频信号。
参考NingHeChuan大佬的面试题集锦
题目6参考
最后
以上就是单纯草莓为你收集整理的FPGA面试题目笔记(三)——跨时钟域中握手信号同步的实现、任意分频、进制转换、RAM存储器等、原码反码和补码1、原码、反码、补码2、十进制转R进制编码并进行浮点数处理3、关于各种存储器的名词解释4、根据电路图画波形并描述电路功能5、任意切换1-8的分频电路6、考察格雷码和状态机7、跨时钟域数据传递的基本方法8、最小时钟周期的计算9、串口发送数据10、时序约束的概念和基本策略11、JK触发器的全部内容,希望文章能够帮你解决FPGA面试题目笔记(三)——跨时钟域中握手信号同步的实现、任意分频、进制转换、RAM存储器等、原码反码和补码1、原码、反码、补码2、十进制转R进制编码并进行浮点数处理3、关于各种存储器的名词解释4、根据电路图画波形并描述电路功能5、任意切换1-8的分频电路6、考察格雷码和状态机7、跨时钟域数据传递的基本方法8、最小时钟周期的计算9、串口发送数据10、时序约束的概念和基本策略11、JK触发器所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复