Verilog8-常用电路(2)

1、用Verilog实现 串并转换

  • lsb 优先

  • msb 优先

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

img

  • 如图第一个方框内是异步复位、同步释放电路。第一级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 到来之后,才随之变为高电平,即同步释放—— 要使输出信号稳定:两级触发器同步,消除亚稳态
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用于缓冲数据流,一般用在写快读慢、突发传输的情况,遵循的规则如下

    img

    • 即,确保对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 img
      • 其中:写入速率:w_clk,读出速率:(X/Y) * r_clk。
      • img

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 总线的关系、区别

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值