Verilog实验

1 多路选择器

1.1 MUX21

在这里插入图片描述

代码:

//MUX21
module MUX
(
IN0,IN1,SEL,OUT
);

parameter WL = 16;      // 输入输出数据信号位宽
input [WL-1:0] IN0, IN1;// 选择器的两个输入数据信号
input SEL;              // 通道选通的控制信号
output[WL-1:0] OUT;     // 选择器的输出数据信号

reg [WL-1:0] OUT;
always @(IN0 or IN1 or SEL) begin
	if(SEL)
		OUT=IN1;
	else
		OUT=IN0;
end
endmodule

RTL视图:
在这里插入图片描述

1.2 MUX41

代码:

//MUX41
module MUX41
(
IN0,IN1,IN2,IN3,SEL,OUT
);

parameter WL = 2;      // 输入输出数据信号位宽
input [WL-1:0] IN0,IN1,IN2,IN3;// 选择器的两个输入数据信号
input SEL;              // 通道选通的控制信号
output[WL-1:0] OUT;     // 选择器的输出数据信号

reg [WL-1:0] OUT;
always @(IN0 or IN1 or IN2 or IN3 or SEL) begin
	if(SEL==00)
		OUT=IN0;
	else if(SEL==01)
		OUT=IN1;
	else if(SEL==10)
		OUT=IN2;
	else
		OUT=IN3;
end
endmodule

RTL视图:
在这里插入图片描述

与MUX21相比,资源的变化:
MUX21:
在这里插入图片描述
MUX41:在这里插入图片描述
波形仿真:
在这里插入图片描述

2 交叉开关

2.1 2*2交叉开关

在这里插入图片描述
代码:

// module  top, a 2x2 crossbar switch circuit

module two_two_crossbar_switch(
  IN0       ,   // input 1
  IN1       ,   // input 2
  SEL0      ,   // select the output0 source 
  SEL1      ,   // select the output1 source 
  OUT0      ,   // output data 0
  OUT1      );  // output data 1
parameter WL = 16;
input [WL-1:0] IN0, IN1;
input SEL0, SEL1;
output[WL-1:0] OUT0, OUT1;

reg   [WL-1:0] OUT0, OUT1;
// get the OUT0
always @ (IN0 or IN1 or SEL0) begin
  if(SEL0)
    OUT0 = IN1;
  else
    OUT0 = IN0;
end
// get the OUT1
always @ (IN0 or IN1 or SEL1) begin
  if(SEL1)
    OUT1 = IN1;
  else
    OUT1 = IN0;
end
endmodule
// endmodule top

RTL视图:
在这里插入图片描述
资源消耗:
在这里插入图片描述

2.2 4*4交叉开关

代码:

module four_four_crossbar_switch 
(IN0,IN1,IN2,IN3,SEL0,SEL1,SEL2,SEL3,SEL4,SEL5,SEL6,SEL7,OUT0,OUT1,OUT2,OUT3);
parameter WL = 16;
input [WL-1:0] IN0,IN1,IN2,IN3;
input SEL0,SEL1,SEL2,SEL3,SEL4,SEL5,SEL6,SEL7;
output [WL-1:0]OUT0,OUT1,OUT2,OUT3;

reg [WL-1:0]OUT0,OUT1,OUT2,OUT3;

always @ (IN0 or IN1 or IN2 or IN3 or SEL0 or SEL1)
begin
  if (!SEL0)
    if(!SEL1)
	  OUT0 = IN0;
	else
	  OUT0 = IN1;
  else
    if(!SEL1)
	  OUT0 = IN2;
	else
	  OUT0 = IN3;
end

always @ (IN0 or IN1 or IN2 or IN3 or SEL2 or SEL3)
begin
  if(!SEL2)
    if(!SEL3)
	  OUT1 = IN0;
	else
	  OUT1 = IN1;
  else
    if(!SEL3)
	  OUT1 = IN2;
	else
	  OUT1 = IN3;
end

always @ (IN0 or IN1 or IN2 or IN3 or SEL4 or SEL5)
begin
  if(!SEL4)
    if(!SEL5)
	  OUT2 = IN0;
	else
	  OUT2 = IN1;
  else
    if(!SEL5)
	  OUT2 = IN2;
	else
	  OUT2 = IN3;
end
	 
always @ (IN0 or IN1 or IN2 or IN3 or SEL6 or SEL7)

begin
  if(!SEL6)
    if(!SEL7)
	  OUT3 = IN0;
	else
	  OUT3 = IN1;
  else
    if(!SEL7)
	  OUT3 = IN2;
	else
	  OUT3 = IN3;
end
endmodule

RTL视图:
在这里插入图片描述
资源消耗:
在这里插入图片描述

3 优先编码器

3.1 4输入优先编码器

// module top, 4 input priority encoder with zero input check
module top(
  IN        ,   // input  
  OUT       );  // output 
input [3:0] IN;
output[2:0] OUT;

reg   [2:0] OUT;
// get the OUT
always @ (IN) begin
   if(IN[3])       // 第一优先
     OUT = 3'b011;
   else if(IN[2])  // 第二优先
     OUT = 3'b010;
   else if(IN[1])  // 第三优先
     OUT = 3'b001;
   else if(IN[0])  // 第四优先
     OUT = 3'b000;
   else            // 什么都没有检测到
     OUT = 3'b111; // 输出值可自定义,不和上面的输出值混淆即可
end
endmodule

3.2 8输入优先编码器

// module top, 8 input priority encoder with zero input check
module eight_input_encoder(
  IN        ,   // input  
  OUT       );  // output 
input [7:0] IN;
output[3:0] OUT;

reg   [3:0] OUT;
// get the OUT
always @ (IN) begin
   if(IN[7])       
     OUT = 4'b0111;
	else if(IN[6])  
     OUT = 4'b0110;
	else if(IN[5])  
     OUT = 4'b0101; 
   else if(IN[4])  
     OUT = 4'b0100;
	else if(IN[3])  
     OUT = 4'b0011;
	else if(IN[2])  
     OUT = 4'b0010;
   else if(IN[1])  
     OUT = 4'b0001;
   else if(IN[0])  
     OUT = 4'b0000;
   else            // 什么都没有检测到
     OUT = 4'b1111; // 输出值可自定义,不和上面的输出值混淆即可
end
endmodule

RTL视图:
在这里插入图片描述

4 多路译码器

4.1 3-8译码器

// module top, 4 input priority encoder with zero input check
module three_eight_decoder(
  IN        ,   // input  
  OUT       );  // output 

input [2:0] IN;
output[7:0] OUT;

reg   [7:0] OUT;
// get the OUT
always @ (IN) begin
  case(IN)
    3'b000: OUT = 8'b0000_0001;
    3'b001: OUT = 8'b0000_0010;
    3'b010: OUT = 8'b0000_0100;
    3'b011: OUT = 8'b0000_1000;
    3'b100: OUT = 8'b0001_0000;
    3'b101: OUT = 8'b0010_0000;
    3'b110: OUT = 8'b0100_0000;
    3'b111: OUT = 8'b1000_0000;
    //  full case 不需要写default,否则一定要有default
  endcase
end
endmodule

资源消耗:
在这里插入图片描述
RTL视图:
在这里插入图片描述

4.2 4-16译码器

// module top, 4 input priority encoder with zero input check
module four_sixteen_decoder(
  IN        ,   // input  
  OUT       );  // output 

input [3:0] IN;
output[15:0] OUT;

reg   [15:0] OUT;
// get the OUT
always @ (IN) begin
  case(IN)
    4'b0000: OUT = 16'b0000_0000_0000_0001;
    4'b0001: OUT = 16'b0000_0000_0000_0010;
    4'b0010: OUT = 16'b0000_0000_0000_0100;
    4'b0011: OUT = 16'b0000_0000_0000_1000;
    4'b0100: OUT = 16'b0000_0000_0001_0000;
    4'b0101: OUT = 16'b0000_0000_0010_0000;
    4'b0110: OUT = 16'b0000_0000_0100_0000;
    4'b0111: OUT = 16'b0000_0000_1000_0000;
	 4'b1000: OUT = 16'b0000_0001_0000_0000;
	 4'b1001: OUT = 16'b0000_0010_0000_0000;
	 4'b1010: OUT = 16'b0000_0100_0000_0000;
	 4'b1011: OUT = 16'b0000_1000_0000_0000;
	 4'b1100: OUT = 16'b0001_0000_0000_0000;
	 4'b1101: OUT = 16'b0010_0000_0000_0000;
	 4'b1110: OUT = 16'b0100_0000_0000_0000;
	 4'b1111: OUT = 16'b1000_0000_0000_0000;
    //  full case 不需要写default,否则一定要有default
  endcase
end
endmodule

资源消耗:
在这里插入图片描述
可以看出,比3-8译码器的资源消耗多了大概一倍。

RTL视图:
在这里插入图片描述

5 加法器

5.1 无符号加法器

module add(
  IN1   ,
  IN2   ,
  OUT   );
input[3:0] IN1, IN2;
output[4:0] OUT;
reg[4:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 + IN2;
end
endmodule 

5.2 实验

  1. 把加法器的输出信号改成4比特位宽,编译,波形仿真。观察输出结果,说出输出和输入的对应关系。
module add_2(
  IN1   ,
  IN2   ,
  OUT   );
input[3:0] IN1, IN2;
output[3:0] OUT;//输出改为四位宽
reg[3:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 + IN2;
end
endmodule 

波形仿真:
分析:原例给出的输入信号为4位输入,数据范围在0-15,相加后范围在0-30,所以输出用5位可以表示。 当输入仍为4位而输出变为4位时,相加结果在大于15时会产生溢出,导致出错,仅能正确计算相加的后四位。
在这里插入图片描述
可以看到超过15的只能输出正确结果的后四位。

  1. 把加法器的输入信号改成8比特位宽,编译,波形仿真。观察加法器的输出延迟,和4比特输入位宽的情况对比,你有什么结论,为什么?
module add_3(
  IN1   ,
  IN2   ,
  OUT   );
input[7:0] IN1, IN2;//输入改为8位宽
output[4:0] OUT;
reg[4:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 + IN2;
end
endmodule 

波形仿真:
分析:当输入变为8位时,输入的范围变为0-127,相加后的范围为0-254,而输出仍为5位,能表示的数据范围为0-31,所以只能正确输出结果的后五位。
在这里插入图片描述

5.3 补码加法器

module complement_add(
  IN1   ,
  IN2   ,
  OUT   );
input signed [3:0] IN1, IN2;
output signed [4:0] OUT;
reg signed [4:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 + IN2;
end
endmodule 

实验:把加法器的输出信号改成4比特位宽,编译,波形仿真。观察输出结果,观察输出结果在什么时候是正确的?。 把加法器的输入信号改成8比特位宽,编译,波形仿真。观察加法器的输出延迟,和4比特输入位宽的情况对比,你有什么结论,为什么?

  1. 把加法器的输出信号改成4比特位宽
module complement_add_2(
  IN1   ,
  IN2   ,
  OUT   );
input signed [3:0] IN1, IN2;
output signed [3:0] OUT;
reg signed [3:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 + IN2;
end
endmodule 

波形仿真:
在这里插入图片描述

  1. 把加法器的输入信号改成8bit位宽
module complement_add_3(
  IN1   ,
  IN2   ,
  OUT   );
input signed [7:0] IN1, IN2;
output signed [4:0] OUT;
reg signed [4:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 + IN2;
end
endmodule 

波形仿真:
在这里插入图片描述

5.4 带流水线的加法器

每一个D触发器都有其所容许的最小的建立与保持时间,当两个D触发器之间的组合电路逻辑延迟变得更大的时候,会导致电路只能工作在更低的时钟频率,为了让电路能够工作在更高的时钟频率,需要用D触发器来把大块的组合逻辑分割为小块,这就是流水线技术。

module top(
  IN1   ,
  IN2   ,
  CLK   ,
  OUT   );
input  [3:0] IN1, IN2;
input CLK;
output  [4:0] OUT;
reg [3:0] in1_d1R, in2_d1R;
reg  [4:0] adder_out, OUT;
always@(posedge CLK) begin // 生成D触发器的always块
  in1_d1R <= IN1;
  in2_d1R <= IN2;
  OUT     <= adder_out;
end
always@(in1_d1R or in2_d1R) begin // 生成组合逻辑的always 块
  adder_out = in1_d1R + in2_d1R;
end
endmodule 

实验: 不改变流水线的级数,把加法器的输入信号改成8比特位宽,编译,波形仿真,和不带流水线的情况对比一下,你有什么结论? 在8比特输入位宽的情况下,在输入上再添加一级流水线,观察编译和仿真的结果,你有什么结论?

  1. 不改变流水线的级数,把加法器的输入信号改成8比特位宽
    结论:与不加流水线的加法器比,带流水线的加法器由于加入了D触发器,有效地减少了组合逻辑的竞争与冒险,从而“毛刺”的长度明显减短。
module water_2(
  IN1   ,
  IN2   ,
  CLK   ,
  OUT   );
input  [7:0] IN1, IN2;
input CLK;
output  [4:0] OUT;
reg [7:0] in1_d1R, in2_d1R;
reg  [4:0] adder_out, OUT;
always@(posedge CLK) begin // 生成D触发器的always块
  in1_d1R <= IN1;
  in2_d1R <= IN2;
  OUT     <= adder_out;
end
always@(in1_d1R or in2_d1R) begin // 生成组合逻辑的always 块
  adder_out = in1_d1R + in2_d1R;
end
endmodule 

波形仿真:
在这里插入图片描述
RTL:
在这里插入图片描述

  1. 在8比特输入位宽的情况下,在输入上再添加一级流水线
    结论: 流水线级数越高,毛刺越短,同时输出时延相应变长。
module water_3(
  IN1   ,
  IN2   ,
  CLK   ,
  OUT   );
input  [7:0] IN1, IN2;
input CLK;
output  [4:0] OUT;
reg [7:0] in1_d1R, in2_d1R;
reg  [4:0] adder_out, OUT;
always@(posedge CLK) begin // 生成D触发器的always块
  in1_d1R <= IN1;
  in2_d1R <= IN2;
  in1_d2r <=in1_d1r;
  in2_d2r <=in2_d1r;
  OUT     <= adder_out;
end
always@(in1_d1R or in2_d1R) begin // 生成组合逻辑的always 块
  adder_out = in1_d2R + in2_d2R;
end
endmodule 

6 乘法器

乘法器是一种奢侈品会消耗大量的组合电路逻辑资源,一定要慎重使用。尤其是对于芯片内部没有硬件乘法器的FPGA芯片,需要更加慎重。

  无符号的乘法器  /
module top(
  IN1   ,
  IN2   ,
  OUT   );
 input [3:0] IN1, IN2;
 output [7:0] OUT;
 reg [7:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 * IN2;
end
endmodule 
  有符号的2补码乘法器  /
module top(
  IN1   ,
  IN2   ,
  OUT   );
 input signed[3:0] IN1, IN2;
 output signed [7:0] OUT;
 reg signed[7:0] OUT;
 always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 * IN2;
end
endmodule 

实验

  1. 改变乘法器的输入位宽为8比特,编译,波形仿真,观察信号毛刺的时间长度。
module mul_2(
  IN1   ,
  IN2   ,
  OUT   );
 input [7:0] IN1, IN2;
 output [7:0] OUT;
 reg [7:0] OUT;
always@(IN1 or IN2) begin // 生成组合逻辑的always 块
  OUT = IN1 * IN2;
end
endmodule 

波形仿真:
在这里插入图片描述

  1. 选一款没有硬件乘法器的FPGA芯片(例如Cyclone EP1C6)对比8比特的乘法器和加法器两者编译之后的资源开销(Logic Cell的数目)

  2. 编写一个输入和输出都有D触发器的流水线乘法器代码,编译后波形仿真,观察组合逻辑延迟和毛刺的时间,和不带流水线的情况下对比。
    波形仿真:
    在这里插入图片描述

7 计数器

7.1 简单计数器

设计一个最简单的计数器,只有一个CLK输入和一个OVerflow输出,当计数到最大值的时钟周期CLK输出1。

//简单计数器
module simple_cnt(
CLK,
OV);

input CLK;
output OV;

reg[3:0] cnt_next;
reg OV;

parameter MAX_VAL=7;
 
always @ (posedge CLK) begin
if(cnt_next <MAX_VAL)begin
OV<=0;
cnt_next <=cnt_next+1;
end
else begin
OV <=1;
cnt_next <=0;
end
end
endmodule

RTL视图:
在这里插入图片描述

7.2 复杂计数器

 复杂计数器代码  /

module top(
  RST   , // 异步复位, 高有效
  CLK   , // 时钟,上升沿有效
  EN    , // 输入的计数使能,高有效
  CLR   , // 输入的清零信号,高有效
  LOAD  , // 输入的数据加载使能信号,高有效
  DATA  , // 输入的加载数据信号
  CNTVAL, // 输出的计数值信号
  OV    );// 计数溢出信号,计数值为最大值时该信号为1

input RST   , CLK   , EN    , CLR   , LOAD  ;
input [3:0] DATA ;
output [3:0] CNTVAL;
output OV;   

reg [3:0] CNTVAL, cnt_next;
reg OV;
// 电路编译参数,最大计数值
parameter CNT_MAX_VAL = 9;

// 组合逻辑,生成cnt_next
// 计数使能最优先,清零第二优先,加载第三优先
always @(EN or CLR or LOAD or DATA or CNTVAL) begin
  if(CLR) begin    
      cnt_next = 0;
  end
  else begin  
      if(EN) begin 
        if(LOAD) begin
        	cnt_next = DATA;
      end
      else begin     
        if(CNTVAL < CNT_MAX_VAL) begin 
          cnt_next = CNTVAL + 1'b1;
        end
        else begin // 计数到最大值,下一计数值为0
          cnt_next = 0;
        end
      end 
    end  
  else begin  // 使能无效,计数值保持不动
    cnt_next = CNTVAL;
  end // else EN
end
end


always @ (posedge CLK or posedge RST) begin
  if(RST) 
    CNTVAL <= 0;
  else
    CNTVAL <= cnt_next;
end

// 组合逻辑,生成OV
always @ (CNTVAL) begin
  if(CNTVAL == CNT_MAX_VAL) 
    OV = 1;
  else
    OV = 0;
end

endmodule

RTL视图:
在这里插入图片描述

8 状态机

在这里插入图片描述

module state(
  CLK,   
  RST,   
  CENT1IN,  
  TINOUT, 
  EN   
  );  

input  CLK; 
input  RST, EN; 
input  CENT1IN; 
output TINOUT;

parameter ST_0_CENT = 0;
parameter ST_1_CENT = 1;
parameter ST_2_CENT = 2;
parameter ST_3_CENT = 3;
parameter ST_4_CENT = 4;

reg [3:0] stateR;
reg [3:0] next_state;
reg         TINOUT;

always @ (CENT1IN or stateR) 

  begin
    case (stateR)
      ST_0_CENT :begin if (CENT1IN  && EN)  next_state = ST_1_CENT ; else next_state = ST_0_CENT; end
      ST_1_CENT :begin if ((CENT1IN == 0) && EN)  next_state = ST_2_CENT ; else next_state = ST_1_CENT; end
      ST_2_CENT :begin if (CENT1IN  && EN)  next_state = ST_3_CENT ; else next_state = ST_0_CENT; end
      ST_3_CENT :begin if (CENT1IN  && EN)  next_state = ST_4_CENT ; else next_state = ST_2_CENT; end
      ST_4_CENT :begin if (CENT1IN  && EN)  next_state = ST_1_CENT ; else next_state = ST_2_CENT; end
      default next_state = ST_0_CENT;
    endcase
  end

// calc output
always @ (stateR) 
begin
  if(stateR == ST_4_CENT)
    TINOUT = 1'b1;
  else 
    TINOUT = 1'b0;
end

// state DFF
always @ (posedge CLK or posedge RST)
begin
  if(RST)
    stateR <= ST_0_CENT;
  else
    stateR <= next_state;
end
endmodule

RTL:
在这里插入图片描述

9 移位寄存器

在这里插入图片描述
在这里插入图片描述

module shift
(
  RST,  //异步复位,高有效
  CLK,  //时钟,上升沿有效
  EN,   //输入数据串行移位使能
  IN,   //输入串行数据
  OUT   //并行输出数据
);

  input RST, CLK, EN; 
  input IN;
  output [3 : 0] OUT;
  reg [3 : 0] shift_R;
  
  assign OUT [3 : 0] = shift_R[3 : 0];
  // shift_R 会被编译为D触发器
  always @ (posedge CLK or posedge RST) //时钟,上升沿有效 异步复位,高有效 posedge从低到高 从高到低 都会触发
    begin
      if(RST) //异步复位,高有效
        shift_R[3 : 0] <= 0;//清零
      else if(EN)  //异步复位,高有效
		  begin // 串行移位的使能有效
          shift_R[3 : 1] <= shift_R[2 : 0];
          shift_R[0]     <= IN;
        end
      else 
		  begin // 使能无效保持不动
          shift_R[3 : 0] <= shift_R[3 : 0];
        end
   end // always
endmodule

RTL:
在这里插入图片描述

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值