FPGA基础测试题 verilog

  1. 设计4位全加器.

能实现四位二进制数全加的数字电路模块,称之为四位全加器。

所谓全加器就是就是带进位(低位向上进位 和 向高位进位)的加法器.其一位全加器的真值表如下表所示:
在这里插入图片描述
对于多位的全加器可以使用加法器来实现,例如下面这个四位全加器,高位的进位则可以通过位拼接符来进位.
这是四位全加器的代码:

 module a(
		input [3:0] a,
		input [3:0] b,
		input CI,
		output [3:0] sum,
		output CO
);
	assign{CO,sum}=a+b+CI;
endmoule
  1. 设计8路数据分配器

根据地址信号的要求,将一路数据分配到指定输出通道上去的电路,称为数据分配器。

其模型如图所示:
在这里插入图片描述
相当于是一对多,一个通道输入对应多个通道输出,在选择信号下分别分配给不同的的通道,在UART串口协议的接受中,将PC机发过来的一位一位的数据存在相对应的数组当中 及 在写URAT多字节发送模块在每一个字发送完成后将下一个字符的2进制码依次送给单字节,让其发送都是数据分配器的应用.
8位数据分配器的代码如下所示:

module a(
	input [3:0] a,
	input clk,
	input rst_n,

	output reg [7:0] out
);
always@(posedge clk or negedge rst_n)
begin
	if(~rst_n)
	out<=8'b0000_0000;
	else
	case(a)
	 3'b000:out[0]=1'b1;
         3'b001:out[1]=1'b1;
         3'b010:out[2]=1'b1;
         3'b011:out[3]=1'b1;
         3'b100:out[4]=1'b1;
         3'b101:out[5]=1'b1;
         3'b110:out[6]=1'b1;
         3'b111:out[7]=1'b1;
        endcase
end
endmodule
  1. 设计5人表决器,当有3人或3人以上同意该事件时,该事件通过:
    代码如下:
module a(  
	input [4:0]in,   
         output out 
         );
  always@(*) 
    begin       
        for(i=0;i<=4,i++)  
            if(in[i])   
                    sum++;  
           else sum=sum;       
  if(sum>=3)     out=1'b1;        
  else               out=1'b0; 
  end 
endmodule 

当然此题也可以用写真值表画卡诺图来解决,但当投票人数变多,卡诺图便显得没有这种方法简单了.

  1. 设计8421BCD码转换为2421BCD码的电路。

BCD码可分为有权码和无权码两类:有权BCD码有8421码、2421码、5421码,其中8421码是最常用的;无权BCD码有余3码,余3循环码等。

画四个真值表可其得其四个逻辑函数,代码如下:

module a(
	input [3:0]a,
	output[3:0]Y
);
always@(*)
       begin
              Y[3]=a[3]|a[2]&a[1]|a[2]&a[0];
              Y[2]=a[3]|a[2]&a[1]|a[2]&!a[0];
              Y[1]=a[3]|!a[2]&a[1]|a[2]&!a[1]&a[0];
              Y[0]=a[0];
   end
endmodule

除了用这种画真值表的办法之外,通过观察8421和2421的码表可发现8421BCD码在小于等于5之前和2421码是完全相同的,在大于5之后只要再给输入的8421码加6即可得到2421码,读者可以想一想为什么及6是怎么得来的。

  1. A3A2A1A0为8421BCD码表示的一位十进制数,该数大于5时输出1,其余输出0。
    其代码如下:
 module a(
	input [3:0] a,
	output out
);
always(*)
begin
	if(a>3'd5)
	out=1'b1;
	else   out=1'b0;
end
endmoudle

因为8421码其实就是二进制码,故可以直接拿来用,对于其他的位权码如2421码,5421码可以通过直接用乘法来乘他的位权,但由于开发板上乘法器很少,资源很宝贵,故一般不采用。所以对于其他的位权码或者无权码可以将其先转化成8421码,再直接拿过来用。

  1. 设计一个逻辑运算单元。该电路能进行六个逻辑运算,且由一个3位的控制信号来选择所进行的功能。运算分别为与、或、异或、与非、或非、异或非。输入为2个4位的二进制数。

代码如下所示:

module ljys(
		input [3:0]a,
          input [3:0]b,
          input [2:0]A,
          output reg JG
       );

       always@(*)
       begin
              case(A)
              3'b000: JG=a&b;      //与运算
              3'b001:JG=a|b;       //或
              3'b010:JG=a^b;       //异或
              3'b011:JG=!(a&b);   //与非
              3'b100:JG=!(a|b);   //或非
              3'b101:JG=!(!a&b|a&!b);  //异或非
              endcase
       end
endmodule

这里异或既可以用verilog中的语法中的 ^ 双目运算符来写,也可以用与或非!a&b|a&!b表达,同样的还有同或可以用 ^~表达

  1. 设计一个四选一数据选择器,并利用数据选择器实现逻辑函数F(A,B)=m1+m3.

数据选择器 根据给定的输入地址代码,从一组输入信号中选出指定的一个送至输出端的组合逻辑电路。

而m1+m3则是函数的最小项表达式

利用逻辑函数的基本公式,可以把任意一个逻辑函数化成若干个最小项之和的形式,称为最小项表达式。

其模型如下图所示:
在这里插入图片描述
表示多对一,相当于有多路输入经过选择信号的选择某一路给输出,在UART串口协议的发送模块中,将要发送的8位数据位一位一位的发送出去就是数据选择器的一个应用。
该题代码如下:

module a(
	input [1:0] a,
	input [3:0] b,
	output reg out
);
always@(*)
begin	
	case(a)
	   2'b00: out=b[0];
	   2'b01: out=b[1];
	   2'b10: out=b[2];
	   2'b11: out=b[3];
end
endmoudle
module ab(	
	input [1:0]a,
	output out
);
wire [3:0]b;
a a1(
.a(a)
.b(b)
.out(out)
);
assign b=4'b1010;

这里实现逻辑函数则是通过实例化上一步的数据选择器来实现的,而之所以把b设置为0101,是因为逻辑函数为M1+M3,即A’B和AB,故函数即为B,故当a[0]=1时,out为1,故b应为4’b1010.

  1. 1)设计一个3线-8线译码器;(2)利用3线-8线译码器实现逻辑函数F(A,B,C)=m1+m2+m5+m7。

译码是编码的逆过程,在编码时,每一种二进制代码,都赋予了特定的含义,即都表示了一个确定的信号或者对象。把代码状态的特定含义“翻译”出来的过程叫做译码,实现译码操作的电路称为译码器。

代码如下所示:

module a(
	input [2:0] a,
	input [7:0] Y
	output [7:0]out
);
always@(*)
begin
	case(a)
	   3'b000: out=Y[0];
	   3'b001: out=Y[1];
	   3'b010: out=Y[2];
           3'b011: out=Y[3];
	   3'b100: out=Y[4];
	   3'b101: out=Y[5];
	   3'b110: out=Y[6];
	   3'b111: out=Y[7];
end
endmodule
module ab(
   input [2:0]a,

output[7:0]out
);
wire [7:0]Y;
a aa(
.a(a),
.Y(Y),
.out(out)
);
assign Y=8'b1010_0110;
endmoule

这里ab模块为外包的模块,a模块为实例化在ab模块内的,从而做到a模块不变,ab模块的参量改变结果就改变。而Y的值1010_0110是因为最小项函数为m1+m2+m5+m7,对应Y[1],Y[2],Y[5],Y[7]为1,所以Y的值就为1010_0110.

  1. 设计一个7段显示译码器,实现在数码管上显示十六进制数。

这里牵扯一个数码管为共阳还是共阴,就如同字面意思一样,共阳即所有LED的阳极接在一起,共阴就是所有的阴极接在一起,如下图所示:
在这里插入图片描述
最直接的区别则为共阳极为0表示亮,共阴极为1表示亮——段led若为共阴极表示数字1则为0000_0110。
因笔者使用的开发板为赛灵思的basys 2开发板,其数码管为共阳,则代码如下(笔者省去了第8位即无小数点位):

module led_16(
     	    input [3:0] a,      
             output reg [6:0] Y   
);
 always@(a)
 begin
       case(a)
              4'b0000:Y=7'b1000_000;
              4'b0001:Y=7'b1111_001;
              4'b0010:Y=7'b0100_100;
              4'b0011:Y=7'b0110_000;
              4'b0100:Y=7'b0011_001;
              4'b0101:Y=7'b0010_010;
              4'b0110:Y=7'b0000_010;
              4'b0111:Y=7'b1111_000;
              4'b1000:Y=7'b0000_000;
              4'b1001:Y=7'b0010_000;
              4'b1010:Y=7'b0001_000;
              4'b1011:Y=7'b0000_011;
              4'b1100:Y=7'b1000_110;
              4'b1101:Y=7'b0100_001;
              4'b1110:Y=7'b0000_110;
              4'b1111:Y=7'b0001_110;
       endcase
       end
endmodule

这里显示数码管因为只写了段选没有写位选(这个问题笔者后面在讲多功能时钟的时候再详谈)故会出现所有的数码管一起亮同一个数字的情况,如下图所示:
在这里插入图片描述

  1. 1)设计一个1位八选一数据选择器;2)利用八选一数据选择器实现逻辑函数F(A,B,C,D)=∑m(1,5,6,7,9,13) 。

代码如下所示:

module a(
		input [3:0] a,
		input [7:0] Y,
		output reg out 
);
always(*)
begin
	case(a)
	  3'b000: out=Y[0];
      3'b001: out=Y[1];
      3'b010: out=Y[2];
      3'b011: out=Y[3];
      3'b100: out=Y[4];
      3'b101: out=Y[5];
      3'b110: out=Y[6];
      3'b111: out=Y[7];
endcase
end
endmodule

module ab(
	input[3:0]a,
	input D,
	output out
);
wire [7:0]Y;
a a1(	
	.a(a),
	.Y(Y),
	.out(out)
);
assign Y={1'b0,D,1'b0,D,1'b1,D,1'b0,D};
endmodule

这里因为题目要求使用8选一数据选择器实现,但数据选择只有三个地址端即数据选择端,但题目中的逻辑函数变量有四个,这里就要用到数电书中的降维法。

降维的目的是,增加了D输出,而不是单纯的1和0进行输出,而利用ABC三个变量进行选择。ABC此时可以看做地址,按照地址找到相应的输出数据。这就实现了数据选择器的功能。

同理还可以把ABC再进行降维成AB和C。

  1. 设计一个8线3线优先编码器。

所谓优先编码器,即有优先级的编码器,允许同时在几个输入端有输入信号,编码器按输入信号排定的优先顺序,只对同时输入的几个信号中优先权最高的一个进行编码。

代码如下所示:

module BMQ_83(
     	  input[7:0] date,       
          output reg [2:0] out   
);
 always@(*)
begin
   if   (date[7]==0) out=3'b000;
 else if(date[6]==0) out=3'b001;
 else if(date[5]==0) out=3'b010;
 else if(date[4]==0) out=3'b011;
 else if(date[3]==0) out=3'b100;
 else if(date[2]==0) out=3'b101;
 else if(date[1]==0) out=3'b110;
 else if(date[0]==0) out=3'b111;
 else if(date==8'b11111111) out=3'b111;
 end
endmodule

这里是用了if-else语句来实现最左侧优先级最高,如比第五位高位请均无0,而第五位为0,那么不管第五位后(第六位…)为任意数字,结果均为
3’b010。这里提一下,编码是译码的逆过程,8线-3线编码器逆过来则为3-8译码器。

  1. 用Verilog描述D触发器。

触发器是时序电路中最基础的组成单位,而相对应的在组合逻辑当中最基础的组成单位则是与,或,非门。

触发器是一个具有记忆功能的,具有两个稳定状态的信息存储器件,是构成多种时序电路的最基本逻辑单元,也是数字逻辑电路中一种重要的单元电路。

D触发器在时钟脉冲CP的前沿(正跳变0→1)发生翻转,触发器的次态取决于CP的脉冲上升沿到来之前D端的状态,即次态=D。因此,它具有置0、置1两种功能。

其真值表如下所示:
在这里插入图片描述
可以看出来Q的次态不受Q的上一个状态影响,即D是多少,Qr+1便为多少。
其代码如下:

module CFQ_D(
     	  input D,
          input clk,
          output reg Q
);

        always@(posedge clk)
        begin
        if(D==0) Q=0;
        if(D==1) Q=1;
        end
  1. 设计一个4位约翰逊计数器。

约翰逊计数器又称扭环计数器,是一种用n位触发器来表示2n个状态的计数器。它与环形计数器不同,后者用n位触发器仅可表示n个状态。约翰逊计数器的状态表中,相邻两组代码只可能有一位二进制代码不同,故在计数过程中不会产生错误的译码信号。鉴于上述优点,约翰逊计数器在同步计数器中应用比较广泛。

因为约翰逊计数器有8个状态,故需要4个D触发器来对其进行实现。
代码如下:

module ab(
	input clk,
	input rst_n,
	output reg[ 3:0] out
);
CFQ_D Z1(
	.clk(clk),
	.D(D1),
	.Q(Q1)
);
CFQ_D Z2(
 .clk(clk),
 .D(Q1),
 .Q(Q2)
);
CFQ_D Z3(
 .clk(clk),
 .D(Q2),
 .Q(Q3)
);
CFQ_D Z4(
 .clk(clk),
 .D(Q3),
 .Q(Q4)
);
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
	D1<=1'b1;
	else if (~Q1&~Q2&~Q3&~Q4)
	D1<=1'b0;
	else if(Q1&Q2&Q3&Q4)
	D1<=1'b1;
end
endmodule

代码中所用到的D触发器为上道题写过的,笔者在此处不再赘述。
除了使用上述用4个D触发器串起来实现约翰逊计数器外;还可以使用移位的办法来得到约翰逊计数器,即将初始值赋为4’b0000,然后如果最低位为0,则将最高位一直赋为1,将上一个值的高三位赋给低三位,否则则将最高位赋为0,将高三位赋给第三位,这样便得到了约翰逊计数器;还可以通过阻塞赋值和两个变化点的判断条件相结合,来得到约翰逊计数器。

  1. 利用4位约翰逊计数器实现肉眼可见效果的流水灯,或者其他方法能实现效果即可。

代码如下:

module led(
input clk,
input rat_n,

output reg[7:0]led
   );
   reg [25:0] cnt;
always@(posedge clk or negedge rat_n)
begin
   if(rat_n==1'b0)
        cnt<= 26'd0;       //设置初值
 else if (cnt==26'd50_000-1'b1) //当达到了49_999_999时进行清零
    cnt<=26'd0;
 else
    cnt<= cnt+1'b1;
 end
 always@(posedge clk or negedge rat_n)
 begin
    if(rat_n==1'b0)
     led<=8'b0000_0001;
  else if(cnt == 26'd50_000-1'b1)
           begin
   led[7]<= led[0];     //将上一秒的最高位传给下一秒的最低位
     led[6:0]<= led[7:1];  //将上一秒的高6位传给下一秒的低6位
   
   end
       else 
     led <= led;
     end
endmodule

这里因为开发板的晶振时钟为50M的(basys 2),所以如果直接进行变化的话,因为变化的速度太快,会导致人的眼睛无法分辨,而呈现出每个LED灯一直都亮的情况,故要使用计数器进行分频,这里计数器记50M下,即为1s.若使用约翰逊计数器 则把计数部分进行替换即可。

  1. 设计8421码和余3码的通用转换器,当控制信号为0时,8421码转余3码;当控制信号为1时,余3码转8421BCD码。
    代码如下:
module(
	input [3:0]a,
	input b,
	output [3:0] c
);
always(*)
begin
	if(!a)
	c=a+2'b11;
	else c=a-2'b11;
end

这道题笔者这样写代码是用了余三码等于8421码加3转化而来的机制,当然这道题也可以通过画真值表写出来。

  1. 1)设计一个1位八选一数据选择器;2)利用八选一数据选择器实现逻辑函数F(A,B,C)=M1* M2 * M5*M7。

应当注意的是此处M1 * M2 * M5 * M7与之前的最小项函数了,其中间是,为最大项函数。
代码如下:

module a(
	input [2:0] a,
	input [7:0] Y,
	output b
);
always@(*)
begin
	case(a)
	   3'b000: b=Y[0];
	   3'b001: b=Y[1];
	   3'b010: b=Y[2];
	   3'b011: b=Y[3];
	   3'b100: b=Y[4];
	   3'b101: b=Y[5];
	   3'b110: b=Y[6];
	   3'b111: b=Y[7];
end
endmodule
module ab(	
	input [2:0] a,
	output b
);
wire [7:0] Y;
a a1(
	.a(a),
	.Y(Y),
	.b(b)
);
assign Y=8'b0101_1001
endmodule

最大项表达式刚好与最小值相反,最小项写1,最大项写0

  1. 设计一个三位二进制减法计数器

代码如下:

module a(
	 input clk,
	 input rst_n,
	 output flag
    );
    reg [2:0] cnt;
    always@(posedge clk or negedge rst_n)
  begin
  if(!rest)
   cnt<=3'b111;
  else if(cnt==3'b000)
   cnt<=3'b111;
 else   cnt<=cnt-3'b001;
 endmoudule
  1. 设计一个4位移位寄存器

移位寄存器是一种在若干相同时间脉冲下工作的以触发器为基础的器件,数据以并行或串行的方式输入到该器件中,然后每个时间脉冲依次向左或右移动一个比特,在输出端进行输出。

代码如下:

module jwJCQ(
 input clk,
 input data_in,
 input rdt_n,
 output reg [3:0]q
    );
 always@(posedge clk or negedge rst_n)
  begin
   if(!rest)
      q<=4'b0;
  else 
    begin
    q[3]<=data_in;
    q[2:0]<=q[3:1];
    end
  end

在URAT协议中接受模块接收UART发送过来的串口数据时,从1到0为数据起始位,这里检测下降沿就用到了移位寄存器。

  1. 用两个D触发器设计一个模4计数器,要求计数器的计数状态为2-1-3-0,然后再重复0

代码如下:

module a(
 input clk,
 input clr,
  output reg [2:0]q
    );
  wire [1:0] D;
 assign D[0]=q[0]^q[1];
 assign D[1]=~q[1];
always@(posedge clk or negedge clr)
 begin
 if(!clr)
  q<=2'd00; 
  else q<=D;
 end

因为在D触发器中q<=D,所以输出q是由D确定的,则D的函数则是通过画上一状态的q的真值表确定的。

  1. 设计一个异步清零、同步置数模功能的5分频器

代码如下所示:

module fenpin5(
	 input clk,
	 input clr,
	 input set
	 output q
    );
  reg q;
mo5  U1(
  .clk(clk),
  .rst_n(clr),
  .f(f)
  );
  always@(posedge clk or negedge clr)
  begin
   if(!clr)    //异步清零
  q<=0;
   else if(set==1)  //同步置数
    q<=1;
   else   begin 
   if(f==1'b1)
   q<=1;
 else q<=0;
 end
  end
endmodule

module mo5(
	input clk,
	input rst_n,
	output f
);
reg [2:0] cnt;
always(posedge clk or negedge rst_n)
begin
	if(~rst_n)beigin
	cnt<=1'b0;f=1'b0;   end
	else if(cnt==3'b100) begin
	cnt<=1'b0; f=1'b1;    end
	else cnt<=cnt+1'b1;
end
endmodule
  1. 设计一个74LS161计数器(依据161的功能表进行设计)

74LS161计数器的功能表如图所示:
在这里插入图片描述
则其代码如下所示:

module JSQ74LS161(
 input clk,
 input clr,
 input CTr,
 input CTp,
 input set,
 input [3:0]D,
 output reg [3:0]Q
    );
    reg[3:0]q;
    always@(posedge clk or negedge clr )
 begin
  if(!clr)                  //当clr=0时不管时钟啊,或者seta,或者D啊 q全为0即异步清零
   Q<=1'b0;
  else     
      begin
      if(set==0)  //当clr==1且set=0 当时钟上升沿来的时候给他D的值 就是同步置数
   Q<=D;
  else if(CTr&CTp==1)   //当clr=set=1,CTr=CTp=1时 进行计数功能
   begin
    if(q==4'b1111)
     q<=0;
     else q<=q+1'b1;
   end
  else if(CTr&CTp==0)    //当CTr或CTp有一个为0时,不管时钟是否为上升沿都对计数进行保持
  begin
  q<=q;
  end
  end
 end
endmodule
  1. 设计一个74LS163计数器(依据163的功能表进行设计)

74LS163计数器的功能表如下所示:
在这里插入图片描述
代码实现如下所示:

module JSQ74LS163(
 input clk,
 input clr,
 input ENT,
 input ENP,
 input set,
 input [3:0]D,
 output reg [3:0]Q,
 output reg RCO    //与161不同的地方 有个RCO的输出
    );
    reg[3:0]q;
 always@(posedge clk )
 begin
  if(!clr)                  //当clr=0时需要时钟是上升沿同步清零(与161的不同)
    begin
      Q <= 1'b0;
    RCO <= 1'b0;             //只有当计数满一轮且ENT为1时,RCO为1,其它时候均为0.
    end
     else if(set==0)      //当clr==1且set=0 当时钟上升沿来的时候给他D的值 就是同步置数
   Q<=D;
   else if(ENT&ENP==1)   //当clr=set=1,CTr=CTp=1时 进行计数功能
   begin
    if(q==4'b1111)
    begin
     q<=0;
   RCO<=1'b1;       //计数满了一轮且ENT为1时,RCO为1
   end
     else q<=q+1'b1;
    end
   else if(ENT&ENP==0)    //当CTr或CTp有一个为0时,不管时钟是否为上升沿都对计数进行保持
  begin
  q<=q;
  end
 end
endmodule

**

这些大致就是笔者整理的FPGA基础测试题,通过这些题大致可以掌握组合逻辑和时序逻辑的主干内容及复习verilog相关语法,但对于真正的做出实物或一个比较大的项目而言,这些练习仍然还是远远不够的,而且在练习中要勤于进行板级调试进行自己代码的验证。

**

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值