文章目录
- 1、用Verilog实现 串并转换
- 2、序列检测器:有“101”序列输入时输出为1,其他情况下输出为0
- 3、Verilog实现一个异步双端口 RAM,
- 4、Verilog实现分频器 —— 见专题
- 5、Verilog 实现 glitch free clock Switching(无毛刺时钟切换电路)—— 见专题
- 6、用Verilog实现异步复位同步释放电路
- 7、Verilog实现按键消抖电路
- 8、Verilog实现同步FIFO —— 专题
- 9、 Verilog 实现异步FIFO —— 专题
- 10、FIFO最小深度计算
- 11、IIC 协议的RTL设计 —— 见专题 及 [参考链接](https://www.cnblogs.com/liujinggang/p/9656358.html)
1、用Verilog实现 串并转换
module Deserialize(
input clk,
input rst_n,
input data_i,
output reg [7:0] data_o);
// lsb first
/*
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
data_o <= 8'b0;
end
else
begin
data_o <= {data_o[6:0],data_i};
end
end
*/
// msb first
always @(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
begin
data_o <= 8'b0;
end
else
begin
data_o[7-cnt] <= data_i; //注意,这里不能写成data_o<={data_i,data_o[6:0]},因为下次时钟沿来临就会有错
cnt <= cnt + 1'b1;
end
end
endmodule
2、序列检测器:有“101”序列输入时输出为1,其他情况下输出为0
-
状态机
-
两段式:
-
第一段:异步复位和状态转移
always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) current_state <= IDLE; else current_state <= next_state;
-
第二段:状态转移、条件、输出
always@(posedge clk or negedge rst_n) begin next_state <= x; // 要初始化,使得系统复位之后能够进入正确的状态。注意这里是 阻塞赋值 case(current_state) s1: if(...) next_state <= s2; out1 <= 1'b1; ... endcase end
-
-
三段式:将两段式第二段中的 状态转移 和 输出 分成两段进行
-
module detector(
input clk,
input rst_n,
input data,
output flag_101);
parameter s0=2'b00,s1=2'b01,s2-2'b10,s3=2'b11;
reg[1:0] cur_state;
reg[1:0] next_state;
// 时序逻辑,状态转移
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 1'b0)
current <= s0;
else
current_state <= next_state;
// 组合逻辑,状态转移条件
always@(*)
begin
//next_state = s0; // 要初始化,使得系统复位后能进入正确的状态。有了default,就不用这个
case(cur_state)
s0:
if(data == 1'b1)
next_state = s1;
else
next_state = s0;
s1:
if(data == 1'b0)
else
next_state =s2;
s2:
if(data == 1'b1)
next_state = s3;
else
next_state = s0;
s3:
if(data == 1'b1)
next_state = s1;
else
next_state = s2;
default: next_state = s0;
endcase
end
// 时序逻辑,输出
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
flag_101 <= 1'b0;
else if(state==s3)
flag_101 <= 1'b1;
else
flag_101 <= 1'b0;
end
endmodule
3、Verilog实现一个异步双端口 RAM,
- 要求:深度16,位宽 8bit。A口读出,B口写入。支持片选、读写请求,要求代码可综合
module DUal_port_sram(
input clk_a,
input [3:0] addr_a,
output [7:0] dout_a,
...
input clk_b,
input [7:0] din_b,
input[3:0] addr_b,
)
4、Verilog实现分频器 —— 见专题
5、Verilog 实现 glitch free clock Switching(无毛刺时钟切换电路)—— 见专题
- 要求:输入sel、clka 、clkb,sel 为1输出为 clka,sel为0 输出 clkb
- 参考文章:http://www.asic-world.com/examples/verilog/syn_fifo.html
- https://blog.csdn.net/bleauchat/article/details/96180815
6、用Verilog实现异步复位同步释放电路
- 参考链接:https://blog.csdn.net/wordwarwordwar/article/details/74091796
- 如图第一个方框内是异步复位、同步释放电路。第一级D触发器的输入是VCC,第二级触发器的输出可以是 异步复位、同步释放后的复位信号
- 电路目的:防止复位信号撤除时产生亚稳态
- 异步复位、同步释放:
- rst_sync_n就是输出到系统的复位信号,左下角的双寄存器就是为了产生 异步复位、同步释放的 复位信号 rst_sync_n 作用于系统(而不是说左下角的双寄存器是 异步复位、同步释放)
- rst_sync_n 为0时(异步控制),系统system 复位
- rst_sync_n 为1时(同步控制,且两级寄存器消除亚稳态),系统system 撤除复位
- 左下角双寄存器如何产生 异步复位、同步释放信号呢?
- 异步复位:复位信号异步有效,即复位信号的发生与 clk 无关
- rst_async_n 异步复位后,rst_sync_n将被拉低,实现异步复位
- 同步释放:复位信号的撤除也与 clk 无关,但是复位信号是在下一个clk来到之后起的作用(释放)。即要 rst_sync_n 为1时,是同步(受clk控制)且稳定(两级触发器消除亚稳态)的
- 复位信号rst_async_n 撤除时,由于双缓冲电路(双寄存器)的作用,rst_sync_n 复位信号不会随着 rst_async_n 的撤除而撤除。—— 同步:rst_sync_n 是 VCC高位传过来的,受时钟clk控制
- 若 rst_async_n 撤除时发生在clk上升沿,如果此时不加电路可能产生亚稳态,但加上此电路之后,假设第一级D触发器 clk 上升沿时 rst_async_n 正好撤除,则D触发器1输出高电平“1”,此时第二级触发器也会更新输出,但输出值为前一级触发器clk 到来之前时的状态 Q1输出状态。显然Q1 之前为低电平,故第二级触发器输出保持复位电平,直到下一个 clk 到来之后,才随之变为高电平,即同步释放—— 要使输出信号稳定:两级触发器同步,消除亚稳态
- 异步复位:复位信号异步有效,即复位信号的发生与 clk 无关
module Sys_Rst(
input clk,
input rst_n,
output sys_rst);
reg rst_r0;
reg rst_r1;
always@(posedge clk or negedge rst_n )
begin
if(!rst_n)
begin
rst_r0 <= 1'b0;
rst_r1 <= 1'b0;
end
else
begin
rst_r0 <= 1'b1;
rst_r1 <= rst_r0;
end
end
assign sys_rst = rst_r1;
endmodule
7、Verilog实现按键消抖电路
- 要求:抖动小于 15ms,输入时钟12MHz
- 原理:
- 对于确定的按键按下(key_r=2’b01, change==1),则确定按键按下, 不做处理
- 对于不确定的按键,每个时钟沿计时,输入中为按下(key_in==1’b1) 且 抖动时间达到阈值(delay_cnt == JITTER-1),则认为其按下
module debounce(
input clk, //12MHz
input rst_n,key_in,
output key_flag);
parameter JITTER = 240; //12MHz/(1/20ms)
reg[1:0] key_r;
wire change;
reg[15:0] delay_cnt;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
key_r <= 0;
else
key_r <= {key_r[0],key_in}; //key_r 的前后两个时钟的状态
end
assign change=(~key_r[1] & key_r[0]) | (key_r[1] & ~key_r[0]); //判断按键状态有无明确变化
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
delay_cnt <=0 ;
else if(change == 1'b1)
delay_cnt <=0; //有确定的按键按下,则不做处理
else if(delay_cnt == JITTER)
delay_cnt <= delay_cnt; //抖动经过一定时间延迟后不再计时,因为已经认为有按键按下了
else
delay_cnt <= delay_cnt+1;
end
assign key_flag=((delay_cnt==JITTEr-1)&&(key_in==1'b1))?1'b1:1'b0; //输出信号,为1表示有按键按下
endmodule
8、Verilog实现同步FIFO —— 专题
- 要求:深度16,数据位宽 8bit
9、 Verilog 实现异步FIFO —— 专题
- 要求:深度16,位宽 8 bit
10、FIFO最小深度计算
- 参考链接:https://blog.csdn.net/bleauchat/article/details/89103976
- https://blog.csdn.net/Times_poem/article/details/51917648?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1
10.1FIFO的应用场景
- 异步FIFO:
- 读写时钟不同频,
- 异步FIFO主要用于数据缓存
- 选择的FIFO深度应该能够保证在最极端情况下,仍然不会溢出
- 考虑的前提:写时钟频率大于读时钟频率,此时FIFO可被用作系统中的缓冲元件或队列
- 若写操作是连续的数据流,则再大的FIFO都无法保证数据不溢出。因此可以认为这种情况下写数据的传输时“突发Burst"的,即写操作并不连续,设计者需要根据满标志控制或者自己来控制写操作的起止。
- 关键:
- 整个时间域上,应该有”写数据=读数据“。
- 但是在突发传送数据的时间T内,有可能 写数据 > 读数据,因此FIFO的深度要能保证,在这段时间T内,若接收方未能将发送方的数据接收完毕的话,剩下的数据都是可以存储在FIFO内部而不会溢出的,从而在发送方停止发送数据的”空闲时隙“内,接收方可以从容的接收剩下来的数据。
10.2 异步FIFO最小深度计算原理
-
FIFO用于缓冲数据流,一般用在写快读慢、突发传输的情况,遵循的规则如下
- 即,确保对FIFO写数据时不存在 overflow
-
例:A/D采样率 50MHz,DSP读 A/D 的速率为 40MHz,要不丢失地将10万个采样数据送入 DSP,在 A/D 和 DSP之间至少设置多大容量的FIFO才行
- 100,000 / 50 MHz = 1/500 s = 2ms
- (50MHz - 40 MHz) * (1/500) = 20k ,即最小FIFO深度为 20k
10.3 异步FIFO最小深度常用计算公式
- 问题:
- 写时钟频率 w_clk,
- 读时钟频率 r_clk,
- 写时钟周期里,每B个时钟周期会有A个数据写入 FIFO
- 读时钟周期里,每Y个时钟周期会有X个数据写入FIFO
- 计算FIFO的最小深度?
- 求解:
- 认为写操作是 Burst 突发的。
- 写操作的效率:F_wr = (A/B) * w_clk ,读操作效率:F_rd = (X/Y) * r_clk。
- 题目中没有约束 Burst 突发的场景
- 正常情况: 空闲 - Burst突发 - 空闲 - Burst突发 - 空闲 - Burst 突发
- 考虑极端情况:空闲 - Burst突发 - Burst突发 - 空闲 - Burst突发 - 空闲(传输过程背靠背的情况)
- 考虑极端情况,求解:
- 发送数据:B_send = Burst_length,背靠背时间 T = Burst_length/w_clk
- 接收数据:B_rec = T * (X/Y) * r_clk = (Burst_length / w_clk) * (X / Y) * r_clk
- FIFO 深度:depth = B_send - B_rec = Burst_length - (Brust_length / w_clk) * (X / Y) * r_clk
- 其中:写入速率:w_clk,读出速率:(X/Y) * r_clk。
10.4 异步FIFO最小深度计算实例
例1
- 一个8bit宽的AFIFO,输入时钟是 100MHz,输出时钟是95MHz,设一个 package 为4Kbit,且两个package之间的发送间距足够大。求AFIFO的深度。
- 分析:
- 异步FIFO,读写频率不同,读写位宽相同
- 发送方一次 Burst突发的数据量为 4Kbit,即500 byte
- 两次Burst 突发传输之间有足够的时间,incident只用考虑在发送方 Burst发送数据的时间T内,如果接收方没法将数据全部接受,区域数据均可存在FIFO内且不溢出。
- 求解:
- 发送方 Burst发送数据的时间段为 T=500/100MHz,发送的数据量为B_send=500 byte (注意这里单位要为 byte)
- T 这段时间内,接收方能够接受的数据量为 B_rec=T*95MHz = 500 * 95/100 = 475 byte
- B_remain = B_send - B_rec = 500 - 475 = 25,即FIFO的深度至少要大于25才行
例2
- 如果 100 个写时钟周期可以写入80个数据,10个读时钟可以读出8个数据。令w_clk=r_clk,考虑背靠背(20个clk不发数据 + 80 个 clk发数据 + 80个 clk 发数据 + 20个clk不发数据 = 200个clk),带入公式可计算FIFO深度
- fifo_depth = 160 - 160 * 80% = 160 - 128 = 32
- 若 令 w_clk=200MHz,100个 w_clk写入40个数据;r_clk = 100 MHz,10个 r_clk 读出 8个数,则FIFO深度为
- fifo_depth = 80 - 80 * (8/10) * (100 / 200) = 80 - 32 = 48
例3
- 如两个异步时钟域数据接口,接入读写是同时进行的,一般设置 FIFO 的深度就要对应两个时钟以及对应写最大的突发数据。假设写时钟频率是 40MHz,读时钟是 25MHz,在写端最大突发数据个数为100个数据。
- 深度:depth = 100 -100 * (25 / 40) = 37.5,即最小深度 38
- 若读写不是同时的,则深度为写数据最大突发个数。如上例中,读写不同时的深度应为 100
10.5 读写FIFO不是同时进行的情况
- 若 读写FIFO不是同时进行的,FIFO深度就是写数据最大的突发个数
10.6 系统设计的问题 —— 流量平衡
- 为了保证输入数据(负载)全部通过,输出吞吐量要大于输入吞吐量;但也不要太大,以免设计过剩
- 即: r_clk * ( X/Y ) > w_clk * (A / B )
- 即:系统设计吞吐量 > 负载流量
- 举例:两个异步时钟域数据接口,写时钟频率为19MHz,读时钟为20MHz,读写时同时进行的,输入数据不间断。求FIFO 最小深度
- 显然有 (r_lk * X / Y) > (w_clk * A / B) ,故理论上FIFO的最小深度为1。
11、IIC 协议的RTL设计 —— 见专题 及 参考链接
-
参考链接:https://www.cnblogs.com/liujinggang/p/9656358.html
-
不得不说,作者写的真的很好很透彻
-
UART 和 I2C 总线的关系、区别