系列文章目录

FPGA(四)数字IC面试的四个基本问题
FPGA(五)RTL代码之一(跨时钟域设计)
FPGA(六)RTL代码之二(复杂电路设计1)
FPGA(七)RTL代码之三(复杂电路设计2)
FPGA(八)RTL代码之四(基本电路设计1)


文章目录

  • 系列文章目录
  • 前言
  • 一、DFF描述
    • 1. 同步复位
    • 2. 异步复位
  • 二、比较电路 保留最大值
    • 1. RTL代码
    • 2. Testbench代码
    • 3. 时序仿真图
  • 三、边沿检测
    • 1. 同步信号
      • 1)RTL代码
      • 2)Testbench代码
      • 3)时序仿真图
    • 2. 异步信号
      • 1)RTL代码
      • 2)Testbench代码
      • 3)时序仿真图
  • 四、独热码检测
    • 1. 概念
    • 2. 题目要求
    • 3. RTL代码
    • 4. 时序仿真图
  • 五、D触发器的二分频实现
    • 1.RTL代码
    • 2. Testbench代码
    • 3. 综合电路图
    • 4. 时序仿真图
  • 六、可置位七进制循环计数器
    • 1. RTL代码
    • 2. Testbench代码
    • 3. 综合电路图
    • 4. 时序仿真图
  • 七、奇偶检验位
  • 八、偶数分频器
    • 1. RTL代码
    • 2. Testbench代码
    • 3. 综合电路图
    • 4. 时序仿真图
  • 九、任意比例分频器
    • 1. 概念,
    • 2. RTL代码
        • ①mdiv.v
        • ②acct.v
        • ③fendiv.v
    • 3. Testbench代码
    • 4. 综合电路图
    • 5. 时序仿真图
  • 十、消除毛刺
    • 1. 基础理论
      • ①利用冗余法
      • ②采样法
      • ③吸收法
      • ④延迟法
      • ⑤硬件描述语言法
    • 2. RTL代码
    • 3. 综合电路图
  • 十一、串并转换
    • 1. 串转并
      • ①RTL代码
      • ②Testbench代码
      • ③时序仿真图
    • 2. 并转串
      • ①RTL代码
      • ②Testbench代码
      • ③时序仿真图
  • 十二、按键消抖15ms
  • 总结

前言

这不马上要面试了嘛,有些慌!HDLbits的题目已经刷完了,但又知道自己还远远不够,就从一个B站UP主那里截取了一个刷题的题库,只有题目,代码都要自己去写,所以写的代码可能存在问题,我打算分成好几节把刷的题目代码放出来。仅供自己学习。如果有需要的也可以看看,若有错误,还望指出!谢谢!

FPGA(九)RTL代码之五(基本电路设计2)-编程之家


一、DFF描述

DFF,就是D触发器(data flip-flop或delay flip-flop)。关于触发器的深层了解,可以看我以前的两篇博客:
FPGA(三)触发器与锁存器
FPGA(四)数字IC面试的四个基本问题

1. 同步复位

//同步复位DFF
module top_module (input clk,input reset,            // Synchronous resetinput [7:0] d,output [7:0] q
);always@(posedge clk)beginif(reset) q <= 0;else q <= d;end
endmodule

2. 异步复位

//异步复位DFF
module top_module(input clk,input [7:0] d,input areset,output reg [7:0] q);always @(posedge clk or posedge areset) beginif (areset)q <= 0;elseq <= d;endendmodule

二、比较电路 保留最大值

1. RTL代码

module compare #(parameter  LENGTH = 8
)(input       [LENGTH-1:0] A,input       [LENGTH-1:0] B,output      [LENGTH-1:0] result
);
assign result = (A >= B) ? A : B;
endmodule

2. Testbench代码

`timescale 1ns/100ps
module compare_tb;
parameter LENGTH = 16;
reg [LENGTH-1:0] A      ;
reg [LENGTH-1:0] B      ;
wire [LENGTH-1:0] result ;compare #(.LENGTH (LENGTH)
)u_compare(A,B,result);initial beginA = 0;B = 0;#10A = 16'ha4d3;B = 16'h34ba;#10A = 16'h1111;B = 16'h2222;#10A = 16'ha456;B = 16'h9802;
end
endmodule

3. 时序仿真图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

三、边沿检测

(同步、异步)信号的边沿检测电路设计——脉冲检测器
边沿检测,就是捕捉输入信号的上升沿下降沿,并作出输出。
一般来说,一级触发器就可以完成边沿检测,但这样容易造成亚稳态的发生。于是,为了避免亚稳态,同步信号的边沿检测采用二级触发器,异步信号的边沿检测采用三级触发器。

1. 同步信号

1)RTL代码

//仅适用于同步信号的边沿检测电路,两级寄存器实现检测功能
module edge_capture(input     clk,rst_n,input     pulse,             //待检测信号output    pos_edge,          //输出为上边沿output    neg_edge,          //输出为下边沿output    dou_edge           //输出为双边沿
);reg  pulse_r1,  pulse_r2;     //中间信号always@(posedge clk or negedge rst_n)beginif(!rst_n) beginpulse_r1 <= 1'b0;pulse_r2 <= 1'b0;endelse beginpulse_r1 <= pulse;pulse_r2 <= pulse_r1; //打两拍完成捕获endendassign pos_edge = (pulse_r1 & ~pulse_r2);  //检测到上升沿时, pos_edge输出一个时钟周期的高电平assign neg_edge = (~pulse_r1 & pulse_r2);  //检测到下降沿时,neg_edge输出一个时钟周期的高电平assign dou_edge = (pulse_r1 ^ pulse_r2);  //检测到双边沿时,neg_edge输出一个时钟周期的高电平
endmodule

2)Testbench代码

`timescale 1ns/100ps
module edge_capture_tb;
reg     clk,rst_n ;
reg     pulse     ; 
wire    pos_edge  ; 
wire    neg_edge  ; 
wire    dou_edge  ; 
edge_capture u_edge_capture(.clk       (clk      ),.rst_n     (rst_n    ),.pulse     (pulse    ),.pos_edge  (pos_edge ),.neg_edge  (neg_edge ),.dou_edge  (dou_edge )
);
initial beginclk = 1'b0;rst_n = 1'b0;pulse = 1'b0;#10rst_n = 1'b1;pulse = 1'b1;#10pulse = 1'b0;#10pulse = 1'b0;#10pulse = 1'b0;#10pulse = 1'b1; 
end
always #5 clk = ~clk;
endmodule

3)时序仿真图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

2. 异步信号

1)RTL代码

//异步信号的边沿检测电路,同样适用于同步信号检测,第一/二级寄存器同步信号,第二/三级寄存器实现检测功能
module edge_capture(input   clk, rst_n,input   pulse,            //待检测信号output  pos_edge,         //输出上边沿output  neg_edge,         //输出下边沿output  dou_edge          //输出双边沿
);reg   pulse_r1, pulse_r2, pulse_r3;    //中间信号always@(posedge clk or negedge rst_n)beginif(!rst_n) beginpulse_r1 <= 0;pulse_r2 <= 0;pulse_r3 <= 0;endelse beginpulse_r1 <= pulse;pulse_r2 <= pulse_r1;pulse_r3 <= pulse_r2;endendassign pos_edge = (pulse_r2 & ~pulse_r3);assign neg_edge = (~pulse_r2 & pulse_r3);assign dou_edge = (pulse_r2 ^ pulse_r3);
endmodule

2)Testbench代码

和同步信号一样

3)时序仿真图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

四、独热码检测

1. 概念

独热码,在英文文献中称做 one-hot code, 直观来说就是有多少个状态就有多少比特,而且只有一个比特为1,其他全为0的一种码制。通常,在通信网络协议栈中,使用八位或者十六位状态的独热码,且系统占用其中一个状态码,余下的可以供用户使用。(来源百度百科)

面试题分析–独热码检测

2. 题目要求

题目来源于 HDLBits: Fsm onehot
FPGA(九)RTL代码之五(基本电路设计2)-编程之家
假设这个状态机使用独热码编码,其中状态[0]到状态[9]分别对应于状态 S0到 S9。除非另有说明,否则输出为零。

实现状态机的状态转换逻辑和输出逻辑部分(但不实现状态触发器)。给定当前状态为 state [9:0] ,必须生成 next _ state [9:0]和两个输出。通过检测假设一个独热码编码,推导出逻辑方程。(testbench 将使用非一个热输入进行测试,以确保您不会尝试执行更复杂的操作)。

3. RTL代码

module top_module(input in,input [9:0] state,output [9:0] next_state,output out1,output out2);assign next_state[0] = ~in & (state[0] | state[1] | state[2] | state[3] | state[4] | state[7] | state[8] | state[9]);assign next_state[1] = in & (state[0] | state[8] | state[9]);assign next_state[2] = in & state[1];assign next_state[3] = in & state[2];assign next_state[4] = in & state[3];assign next_state[5] = in & state[4];assign next_state[6] = in & state[5];assign next_state[7] = in & (state[6] | state[7]);assign next_state[8] = ~in & state[5];assign next_state[9] = ~in & state[6];assign out1 = state[8] | state[9];assign out2 = state[7] | state[9];
endmodule

4. 时序仿真图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

五、D触发器的二分频实现

D触发器二分频电路

简单点说:将D触发器的Q非端接到数据输入端D即可实现二分频,即CLK时钟信号的一个周期Q端电平反转一次

1.RTL代码

module divide2 (input       clk         ,input       rst_n       ,output reg  clk_out
);
always @(posedge clk or negedge rst_n) beginif(!rst_n)clk_out <= 1'b0;elseclk_out <= ~clk_out;
end
endmodule

2. Testbench代码

`timescale  1ns/100ps
module divide2_tb;
reg clk    ;
reg rst_n  ;
wire clk_out;
divide2 u_divide2(clk,rst_n,clk_out);
initial beginclk = 1'b0;rst_n = 1'b0;#15 rst_n = 1'b1;
end
always #5 clk =~ clk;
endmodule

3. 综合电路图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

4. 时序仿真图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

六、可置位七进制循环计数器

1. RTL代码

//通过更换参数,实现可预置初值的7进制循环计数器
module test #(parameter NUM       = 3
)(input               clk     ,input               rst_n   ,input [NUM-1:0]     d_in    ,output reg [NUM-1:0]d_out   
);reg [NUM-1:0]cnt;always @(posedge clk or negedge rst_n) beginif(!rst_n) begincnt <= d_in;endelse if (cnt == 'b111) begincnt <= 'b0;endelse begincnt <= cnt + 1'b1;endendalways @(posedge clk or negedge rst_n) beginif(!rst_n) begind_out <= 'b0;endelse begind_out <= cnt;endend
endmodule

2. Testbench代码

`timescale 1ns/10ps
module test_tb;
parameter NUM  = 3;reg clk;reg rst_n;reg [NUM-1:0]d_in;wire [NUM-1:0]d_out;test #(.NUM    (NUM))u1(.clk(clk),.rst_n(rst_n),.d_in(d_in),.d_out(d_out)
);initial beginclk = 1'b1;rst_n = 1'b0;#8;rst_n = 1'b1;endalways #5 clk = ~clk;initial begind_in = 4'b0010;#10;end
endmodule

3. 综合电路图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

4. 时序仿真图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

七、奇偶检验位

论述:Verilog中的奇偶校验
Verilgo实现的FPGA奇偶校验

八、偶数分频器

1. RTL代码

module test #(parameter NUM = 6,       //确定分频数parameter LENGTH = 3    //确定计数器位宽
) (input       clk     ,input       rst_n   ,output      clk_div
);
reg [LENGTH-1:0] cnt;
reg out_div;
always @(posedge clk or negedge rst_n) beginif(!rst_n)cnt <= 'b0;else begincnt <= (cnt == NUM-1) ? 'b0 : cnt + 1'b1;out_div <= (cnt < NUM/2) ? 1'b1 : 1'b0;end
end
assign clk_div = out_div;
endmodule

2. Testbench代码

module test_tb;
parameter NUM = 6;
parameter LENGTH = 3;
reg     clk    ;
reg     rst_n  ;
wire    clk_div;test #(.NUM        (NUM),.LENGTH     (LENGTH)
)u_test(clk,rst_n,clk_div);
initial beginclk = 1'b0;rst_n = 1'b0;#6rst_n = 1'b1;#40rst_n = 1'b0;#10rst_n = 1'b1;
end
always #5 clk = ~clk;
endmodule

3. 综合电路图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

4. 时序仿真图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

九、任意比例分频器

时钟分频系列——偶数分频/奇数分频/分数分频
时钟分频系列——分数分频电路的Verilog实现

前面奇数分频、偶数分频都做过了,接下来就是分数分频了。
我把上面两篇文章的内容给整理一下。

1. 概念,

一个分数分频器由两部分组成,以ZnZ_nZnZn+1Z_n+1Zn+1为分频系数的多路分频器MDIV,还有一个ACC计数器ACCT。
FPGA(九)RTL代码之五(基本电路设计2)-编程之家

在数字电路中,没办法用计数器表示0.7这样的小数,所以就用一个等效的概念来进行分数分频,比如进行8.7分频,原时钟87个周期的总时间等于分频后的时钟10个周期的总时间。如此,用整数部分Zn(=8)Z_n(=8)Zn(=8)作为一个分频系数,Zn+1(=9)Z_n+1(=9)Zn+1(=9)作为另一个分频系数组成一个分数分频器。于是得出二元一次方程组:
Zn∗N+(ZN+1)∗M=87,N+M=10Z_n*N+(Z_N+1)*M=87 , N+M=10 ZnN+(ZN+1)M=87,N+M=10
解出N和MN和MNM的值分别时3和7.

如果分频系数是6.432,那么有这样一组方程组,其中Zn=6Z_n=6Zn=6:
Zn∗N+(ZN+1)∗M=6432,N+M=1000Z_n*N+(Z_N+1)*M=6432 , N+M=1000 ZnN+(ZN+1)M=6432,N+M=1000
解出N和MN和MNM的值分别时568和432,这个值就很大了,计数器的位宽也变大了,也就是说小数部分位数越多,需要的计数器的位宽越大,消耗的硬件资源越多。不过568,432,1000这几个数有公约数,可以除以最大公约数,把计数数值减小。

通过上面的计算,如果做8.7分频,就要先做3次8分频,这样时钟周期数是24,再做7次9分频,所需时钟周期数是63,总共就是87个时钟周期。在这87个时钟周期里面分频时钟跳变20次,总共为10个周期。

下图是多路分频器MDIV的引脚信号及功能定义:
FPGA(九)RTL代码之五(基本电路设计2)-编程之家

接下来就是关于ACC计数器的设计。
ACC计数器就是控制做N次ZN分频和M次ZN+1次分频,具体控制过程可以分为以下几种情况:

  1. 先做NNNZNZ_NZN分频,再做MMMZN+1Z_N+1ZN+1次分频.
  2. 先做MMMZN+1Z_N+1ZN+1次分频,再做NNNZNZ_NZN分频.
  3. NNNZNZ_NZN分频平均插入到MMMZN+1Z_N+1ZN+1分频中.
  4. MMMZN+1Z_N+1ZN+1分频平均插入到NNNZNZ_NZN分频中.

第1、2种情况前后时钟频率不太均匀,因此会出现相位抖动较大的情况;第3、4种情况前后时钟频率均匀性较好,因此相位抖动会减小。
结合上面的情况,就可以得到这样的混频效果:
FPGA(九)RTL代码之五(基本电路设计2)-编程之家
下图是以8.7分频为例设计的ACC计数器:
FPGA(九)RTL代码之五(基本电路设计2)-编程之家

2. RTL代码

①mdiv.v

module mdiv#(parameter NUM = 7
) (input               en      ,input               clk     ,input               rst_n   ,input [NUM-1:0]     zn      ,   //分频系数输入端output              clkn    ,   //分频时钟信号输出output              clkout      //调整占空比zn/zn+1,分频时钟信号输出
);
reg             clkn1;
reg[NUM:0]      cnt;
reg             clk_neg;reg     clkn_n;         //定义这个变量,是因为在Modelsim仿真时,输出端口不可以直接在always块中进行赋值assign clkout = zn[0] ? (en ? clkn & clk_neg : clkn) : //对zn[0]进行判断,确定是奇数分频还是偶数分频(en ? clkn : clkn | clkn1);always @(posedge clk or negedge rst_n) beginif(!rst_n) begincnt <= {NUM{1'b0}};clkn_n <= 1'b0;endelse begin//if(!en) zn分频;else zn+1分频if(en && cnt == zn+1 || !en && cnt == zn) begincnt <= {NUM{1'b0}};clkn_n <= ~clkn_n;end//(cnt == zn/2) ? clkn 取反 : 不变 , 即一半else if(cnt == zn >> 1) begincnt <= cnt + 1'b1;clkn_n <= ~clkn_n;endelse begincnt <= cnt + 1'b1;clkn_n <= clkn_n;endend
end
assign clkn = clkn_n;
//对输出clkn打一拍,以对输出的时钟间断一个周期
always @(posedge clk or negedge rst_n) beginif(!rst_n)clk_neg <= 1'b0;else    clk_neg <= clkn;
end
//
always @(posedge clk or negedge rst_n) beginif(!rst_n)clkn1 <= 1'b0;//if(!en) zn分频;else zn+1分频else if(en && cnt == zn+1 || !en && cnt == zn)clkn1 <= clkn1;else if(cnt == zn >> 1)clkn1 <= ~clkn1;else    clkn1 <= clkn1;
end
endmodule

②acct.v

module acct (input       clk     ,input       rst_n   ,input       ckn     ,output      enout   
);
wire        ckn_go;
reg         ckn_d;
reg [4:0]   cnt;
reg         enout1;
//采样器  采集下降沿
assign  ckn_go = ~ckn &ckn_d;
always @(posedge clk or negedge rst_n) beginif(!rst_n)ckn_d <= 1'b0;else   ckn_d <= ckn;
end
//计数器
always @(posedge clk or negedge rst_n) beginif(!rst_n)cnt <= 5'h00;else if(cnt == 5'd10)cnt <= 5'h00;else    cnt <= ckn_go ? cnt + 1'b1 : cnt;   //捕获到下降沿时加1,否则不变
end
//混频器
always @(posedge clk or negedge rst_n) beginif(!rst_n)enout1 <= 1'b0;else    case (cnt)5'd0,5'd1,5'd2,5'd4,5'd5,5'd7,5'd8: enout1 <= 1'b1; //修改这一行可以得到不同的混频default: enout1 <= 1'b0;endcase
end
assign enout = enout1;
endmodule

③fendiv.v

module fendiv (input       clk     ,input       rst_n   ,output wire    clkout  
);
wire    enout,clkn;
acct XACCT(.clk   (clk  ),.rst_n (rst_n),.ckn   (clkn ),.enout (enout)
);
mdiv XMDIV(.en     (enout ),.clk    (clk   ),.rst_n  (rst_n ),.zn     ('d7   ),.clkn   (clkn  ),.clkout (clkout)
);
endmodule

3. Testbench代码

module fendiv_tb;
reg clk   ;
reg rst_n ;
wire clkout;
fendiv u_fendiv(clk,rst_n,clkout);
initial beginclk = 1'b0;rst_n = 1'b0;#7rst_n = 1'b1;#200rst_n = 1'b0;#3rst_n = 1'b1;
end
always #5 clk = ~clk;
endmodule

4. 综合电路图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

5. 时序仿真图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

十、消除毛刺

1. 基础理论

在 FPGA 的设计中,毛刺现象是长期困扰电子设计工程师的设计问题之一, 是影响工程师设计效率和数字系统设计有效性和可靠性的主要因素。 由于信号在 FPGA 的内部走线和通过逻辑单元时造成的延迟,在多路信号变化的瞬间,组合逻辑的输出常常产生一些小的尖峰,即毛刺信号,这是由 FPGA 内部结构特性决定的。 毛刺现象在 FPGA 的设计中是不可避免的,有时任何一点毛刺就可以导致系统出错,尤其是对尖峰脉冲或脉冲边沿敏感的电路更是如此。

①利用冗余法

利用冗余项消除毛刺有 2 种方法:代数法和卡诺图法,两者都是通过增加冗余项来消除险象,只是前者针对于函数表达式而后者针对于真值表。以卡诺图为例,若两个卡诺圆相切,其对应的电路就可能产生险象。因此,修改卡诺图, 在卡诺图的两圆相切处增加一个圆,以增加多余项来消除逻辑冒险。但该法对于计数器型产生的毛刺是无法消除的。

②采样法

由于冒险多出现在信号发生电平跳变的时刻,即在输出信号的建立时间内会产生毛刺,而在保持时间内不会出现,因此,在输出信号的保持时间内对其进行采样,就可以消除毛刺信号的影响,常用的采样方法有 2 种:一种使用一定宽度的高电平脉冲与输出相与,从而避开了毛刺信号,取得输出信号的电平值。这种方法必须保证采样信号在合适的时间产生,并且只适用于对输出信号时序和脉冲宽度要求不严的情况。另一种更常见的方法叫锁存法,是利用 D 触发器的输入端 D 对毛刺信号不敏感的特点,在输出信号的保持时间内,用触发器读取组合逻辑的输出信号。由于在时钟的上升沿时刻,输出端 Q=D,当输入的信号有毛刺时,只要不发生在时钟的上升沿时刻,输出就不会有毛刺。这种方法类似于将异步电路转化为同步电路,实现简单,但同样会涉及到时序问题。

③吸收法

由于产生的毛刺实际上是高频窄脉冲,故增加输出滤波,在输出端接上小电容 C 就可以滤除毛刺。但输出波形的前后沿将变坏,在对波形要求较严格时,应再加整形电路,该方法不宜在中间级使用。

④延迟法

因为毛刺最终是由于延迟造成的,所以可以找出产生延迟的支路。对于相对延迟小的支路,加上毛刺宽度的延迟可以消除毛刺。但有时随着负载增加,毛刺会继续出现,而且,当温度变化,所加的电压变化或要增加逻辑门时,所加的延迟是不同的,必须重新设计延迟线,因而这种方法也是有局限性的。而且采用延迟线的方法产生延迟会由于环境温度的变化而使系统可靠性变差。

⑤硬件描述语言法

这种方法是从硬件描述语言入手,找出毛刺产生的根本原因,改变语言设计,产生满足要求的功能模块,来代替原来的逻辑功能块。一个 3 位计数器可能会在 011 到 100 和 101到 110 发生跳变时产生毛刺,究其原因是因为一次有 2 位发生跳变,可以采用 VHDL 语言对计数器编写如下,产生的计数模块代替原来普通的计数器。

消除毛刺的简单方法就是通过打拍来解决:小于一个周期的毛刺就打两拍,大于一个周期而小于两个周期的毛刺就打三拍。

下面以小于一个周期的毛刺为例:

2. RTL代码

module digital_filter(clk_in,rst,host_rst,host_rst_filter);
input  clk_in;
input  rst;
input  host_rst;
output host_rst_filter;reg host_rst_d1;
reg host_rst_d2;always@(posedge clk_in or negedge rst)beginif(~rst)beginhost_rst_d1 <= 1'b1;host_rst_d2 <= 1'b1;endelsebeginhost_rst_d1 <= host_rst;host_rst_d2 <= host_rst_d1;endend
assign host_rst_filter = host_rst_d1 | host_rst_d2;
endmodule

3. 综合电路图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

十一、串并转换

数字IC设计——用Verilog实现串并转换(移位寄存器)

1. 串转并

串转并,就是定义一个多位寄存器,然后一位一位的将数据输入到这个多位寄存器中。接下来由一位转八位为例。

①RTL代码

module serial_parallel(input           clk,input           rst_n,en,input           data_i,   //一位输入output   reg [7:0] data_o	//8位并行输出);
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0)data_o <= 8'b0;else if (en == 1'b1)data_o <= {data_o[6:0], data_i};	//低位先赋值//data_o <= {data_i,data_o[7:1],};	//高位先赋值elsedata_o <= data_o;
end
endmodule

②Testbench代码

`timescale 1ns/100ps
module serial_parallel_tb;
reg         clk          ;
reg         rst_n        ;
reg         en           ;
reg         data_i       ;
wire[7:0]   data_o       ;
serial_parallel u_serial_parallel(.clk     (clk   ),.rst_n   (rst_n ),.en      (en    ),.data_i  (data_i),.data_o  (data_o)
);
initial beginclk = 1'b0;rst_n = 1'b0;en = 1'b0;data_i = 1'b0;#10rst_n = 1'b1;#3en = 1'b1;#10data_i = 1'b1;#10data_i = 1'b0;#10data_i = 1'b1;#10data_i = 1'b1;#10data_i = 1'b0;#10data_i = 1'b1;#10data_i = 1'b0;#10data_i = 1'b1;#10data_i = 1'b1;#10data_i = 1'b0;#10data_i = 1'b0;#5en = 1'b0;#20en = 1'b1;#10data_i = 1'b1;#10data_i = 1'b0;#10data_i = 1'b1;#10data_i = 1'b1;#10data_i = 1'b0;#10data_i = 1'b1;#10data_i = 1'b0;#10data_i = 1'b1;#10data_i = 1'b1;#10data_i = 1'b0;#10data_i = 1'b0;
end
always #5 clk = ~clk;
endmodule 

③时序仿真图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

2. 并转串

并转串,就是将一个多位寄存器中的值,通过移位的方式,将数据一位一位地输出出来。

①RTL代码

module parallel_serial(input       clk     , input       rst_n   ,input       en      , input [7:0] data_i  , output      data_o);
reg [7:0]  data_buf;
always @(posedge clk or negedge rst_n) beginif (rst_n == 1'b0)data_buf <= 8'b0;else if (en == 1'b1)data_buf <= data_i;elsedata_buf <= data_buf <<1; 	//将寄存器内的值左移,依次读出//data_buf <= {data_buf[6:0],1'b0};
end
assign data_o = data_buf[7];
endmodule

②Testbench代码

`timescale 1ns/100ps
module serial_parallel_tb;
reg         clk          ;
reg         rst_n        ;
reg         en           ;
reg [7:0]   data_i       ;
wire        data_o       ;
parallel_serial u_parallel_serial(.clk     (clk   ),.rst_n   (rst_n ),.en      (en    ),.data_i  (data_i),.data_o  (data_o)
);
initial beginclk = 1'b0;rst_n = 1'b0;en = 1'b0;data_i = 8'h00;#10rst_n = 1'b1;#3en = 1'b1;#10data_i = 8'hb3;#100en = 1'b0;#100en = 1'b1;#4data_i = 8'h5b;
end
always #5 clk = ~clk;
endmodule

③时序仿真图

FPGA(九)RTL代码之五(基本电路设计2)-编程之家

十二、按键消抖15ms

【Verilog HDL 训练】第 09 天(按键消抖)
《按键消抖与LED控制》实验的个人思考与总结


总结

我打算读博了,这个系列暂时就这么结束了!谢谢!
不过我还是会时不时地来学一学,写点东西,毕竟很多东西不能给落下了,要经常地复习才行。