FPGA零基础学习:基于FPGA的多路选择器设计(附代码)

2021-03-16 09:57:23 浏览数 (1)

FPGA零基础学习:基于FPGA的多路选择器设计(附代码)

大侠好,欢迎来到FPGA技术江湖。本系列将带来FPGA的系统性学习,从最基本的数字电路基础开始,最详细操作步骤,最直白的言语描述,手把手的“傻瓜式”讲解,让电子、信息、通信类专业学生、初入职场小白及打算进阶提升的职业开发者都可以有系统性学习的机会。

系统性的掌握技术开发以及相关要求,对个人就业以及职业发展都有着潜在的帮助,希望对大家有所帮助。后续会陆续更新 Xilinx 的 Vivado、ISE 及相关操作软件的开发的相关内容,学习FPGA设计方法及设计思想的同时,实操结合各类操作软件,会让你在技术学习道路上无比的顺畅,告别技术学习小BUG卡破脑壳,告别目前忽悠性的培训诱导,真正的去学习去实战应用,这种快乐试试你就会懂的。话不多说,上货。

多路选择器的设计

作者:郝旭帅 校对:陆辉

多路选择器是数据选择器的别称。在多路数据传送过程中,能够根据需要将其中任意一路选出来的电路,叫做数据选择器,也称多路选择器或多路开关。

二选一多路选择器

二选一多路选择器的数据输入有两个,分别为dataa和datab。为了能够确定选择那一路数据能够通过,还需要一个选择端(sel)。因为输入只有两路数据,选择端只要能够表现出两种状态即可,因而选择端位宽为1即可。

假设dataa和datab都是位宽为1的数据,当sel为0时,选择dataa通过;当sel为1时,选择datab通过;odata表示通过后的数据。

图1 :二选一多路选择器模型

根据上述功能,列出真值表。

图2 :二选一多路选择器真值表

根据真值表,化简得出布尔表达式:

odata = (dataa & (~sel)) | (datab & sel);

在verilog中,算术运算中,“&”表示算术(按位)与,“|”表示算术(按位)或,“~”表示算术(按位)取反。

在数字电路基础中,根据表达式,就可以得到电路图。

现在我们要在FPGA中实现,二选一多路选择命名为“mux2_1”,不要命名为mux21,mux21是quartus中默认器件库中的名字,命名相同会出现错误。

建立工程后,输入如下设计代码:(mux2_1代码)

代码语言:javascript复制
module mux2_1 (

  input    wire          dataa,
  input    wire          datab,
  
  input    wire          sel,
  
  output      wire          odata
);

  assign odata = (dataa & (~sel)) | (datab & sel);

endmodule

图4 :mux2_1的RTL视图

设计完成后,输入如下testbench代码:(mux2_1_tb代码)

代码语言:javascript复制
`timescale 1ns/1ps

module mux2_1_tb;

  reg            dataa;
  reg            datab;
  
  reg                     sel;
  
  wire                    odata;
  
  mux2_1 mux2_1_inst(

      .dataa          (dataa),
      .datab          (datab),
      
      .sel          (sel),
      
      .odata          (odata)
    );

  initial begin
    // 000
    dataa = 1'b0;
    datab = 1'b0;
    sel = 1'b0;
    # 20;
    // 001
    dataa = 1'b0;
    datab = 1'b0;
    sel = 1'b1;
    # 20;
    //010
    dataa = 1'b0;
    datab = 1'b1;
    sel = 1'b0;
    # 20;
    //011
    dataa = 1'b0;
    datab = 1'b1;
    sel = 1'b1;
    # 20;
    //100
    dataa = 1'b1;
    datab = 1'b0;
    sel = 1'b0;
    # 20;
    //101
    dataa = 1'b1;
    datab = 1'b0;
    sel = 1'b1;
    # 20;
    //110
    dataa = 1'b1;
    datab = 1'b1;
    sel = 1'b0;
    # 20;
    // 111
    dataa = 1'b1;
    datab = 1'b1;
    sel = 1'b1;
    # 20;
  end
  
endmodule

在verilog中,“//”表示本行被注释,综合器综合时,自动略过。

在testbench中,连接线的名字可以随意定义,建议和端口相同。

设置好testbench后,运行RTL 仿真。

图6 :RTL仿真波形

对比波形和真值表,设计正确。

四选一多路选择器

四选一多路选择器的数据输入有四个,分别为dataa、datab、datac和datad。为了能够确定选择那一路数据能够通过,还需要一个选择端(sel)。因为输入四路数据,选择端要求能够表现出四种状态,因而选择端位宽为2。

假设dataa、 datab、datac和datad都是位宽为8的数据,当sel为00时,选择dataa通过;当sel为01时,选择datab通过;当sel为10时,选择datac通过;当sel为11时,选择datad通过;odata表示通过后的数据。

图7 :四选一多路选择器模型

根据组合逻辑设计规则,我们将所有的情况全部列出,得出真值表,进而得到布尔表达式。但是现在输入的组合排列太多了(2的34次幂),不能够直接得出真值表。

此时的设计有两种方法。

第一种方法,根据功能拆分逻辑。将输入为8的四选一多路选择器,拆分为8个位宽为1的四选一多路选择器,首先列出位宽为1真值表,得出位宽为1的四选一多路选择器。然后并接八个即可。

图8 :8个位宽1多路选择器构成位宽8的多路选择器

这种设计方法,不在提供设计源码,读者可以自行讨论设计。

第二种方法,根据verilog的设计规则,可以直接描述逻辑功能,而不用描述门电路。这种设计规则有利于将设计做的比较大。

位宽为8的四选一多路选择器命名为“mux4_1”。

建立工程后,输入设计代码如下:(mux4_1代码)

代码语言:javascript复制
module mux4_1 (

  input      wire       [7:0]       dataa,
  input      wire       [7:0]       datab,
  input      wire       [7:0]       datac,
  input      wire       [7:0]       datad,
  
  input      wire       [1:0]       sel,
  
  output     reg        [7:0]       odata
);

  always @ ( * ) begin
    case (sel)          
      2'b00    :  odata = dataa;
      2'b01    :  odata = datab;
      2'b10    :  odata = datac;
      2'b11    :  odata = datad;
      default   :  odata = dataa;
endcase
  end

endmodule

always 语句用来表示组合逻辑时,即可以采用门电路的描述方法,也可以采用功能性的描述语句。

“always @()”中()是信号敏感列表。中间可以写入本模块的所有的敏感信号,“”可以表示所有的敏感信号。建议利用always语句描述组合逻辑时,用“”表示所有信号。用“”时,“()”可以省略。即:always@() 等效 always@*。

代码语言:javascript复制
case(变量)
分支0     :表达式0;
分支1     :表达式1;

分支n     :表达式n;
default     :表达式d;
endcase

在case语句中,首先会判断变量和那个分支相同,并且执行对应的表达式。当和所有的分支都不相同时,执行default后的表达式。

verilog规定,在always语句中被赋值的变量,应该定义为“reg”类型。

图11 :mux4_1的RTL视图

设计完成后,输入testbench代码。mux4_1_tb 代码如下:

代码语言:javascript复制
`timescale 1ns/1ps

module mux4_1_tb;

  reg       [7:0]      dataa;
  reg       [7:0]      datab;
  reg       [7:0]      datac;
  reg       [7:0]      datad;
  
  reg       [1:0]      sel;
  
  wire      [7:0]      odata;
  
  mux4_1 mux4_1_inst(

      .dataa          (dataa),
      .datab          (datab),
      .datac          (datac),
      .datad          (datad),
      
      .sel          (sel),
      
      .odata          (odata)
    );

  initial begin
    repeat(5) begin
      dataa = {$random} % 256;
      datab = {$random} % 256;
      datac = {$random} % 256;
      datad = {$random} % 256;
      sel = {$random} % 4;
      # 20;
    end 
  end

endmodule

由于本次输入的的组合太多,不能全覆盖测试。故采用随机数来进行测试。

$random是一个系统函数,调用时,可以返回一个随机值。注意:这个系统函数只能出现testbench中,在设计中出现是不可综合的。

“$random函数调用时返回一个32位的随机数,它是一个带符号的整形数...”。例:

reg[23:0] rand;

rand=$random % 60; //产生一个在 -59—59范围的随机数

产生0~59之间的随机数,例:

reg[23:0] rand;

rand={$random} % 60; //通过{}产生0—59范围的随机数

产生在min, max之间随机数,例:

reg[23:0] rand;

rand = min {$random}%(max-min 1);

在testbench中,需要按照一定顺序给输入线赋值。在mux4_1_tb中,我们可以通过延迟赋值,然后再次延迟赋值,来完成赋值。因为赋值时采用随机数,所以每次编写的语句是相同的。verilog中提供了repeat语句,用来减少人工输入。

图13 :两种等效的赋值方式

输入testbench后,进行综合分析。

设置testbench,运行RTL仿真。

图14:RTL仿真图

经过分析,符合四选一多路选择器的设计。

0 人点赞