zoukankan      html  css  js  c++  java
  • FPGA课设-基于Xilinx Basys2开发板的除法器设计

    介绍一下Basys开发板:

    Basys2 FPGA开发板是一个电路设计实现平台,任何人都可以通过它来搭建一个真正的数字电路。Basys2是围绕着一个Spartan-3E FPGA芯片和一个Atmel AT90USB USB控制器搭建的,它提供了完整、随时可以使用的硬件平台,并且它适合于从基本逻辑器件到复杂控制器件的各种主机电路.Basys2开发板兼容所有版本的Xilinx ISE工具,其中也包括免费的WebPack版本。Basys2附带一个用于供电和编程的USB下载线,所以就不需要其他供电器件或编程下载线。

    此次课设实现的功能:

    设计一个除法器,能在Basys2开发板上实际运行。

    被除数为16位,除数为8位,被除数和除数都用按键输入,结果用数码管显示,设置一个使能开关,开关朝上拨时才进行运算。由于数码管和按键等资源数量较少,因此可以考虑采取下面的方案实现。

    LD2  LD1  LD0指示状态,000是起始状态,001用于输入被除数高8位,010用于输入被除数低8位,011用于输入除数(8位),100用于显示结果,101用于显示被除数和除数

    btn1btn0和数码管配合,用于改变准备输入的数据

    btn2用于将数码管显示的数据输入到相应的地方,同时切换状态。btn3 的功能也是切换状态,如果用btn3切换状态,则不改变原来的数据。

    sw6如果为1表示允许进行除法运算,为0则表示不允许。

    因为LD2-LD0100101时需要显示616进制数,而数码管只有4个,所以用sw7进行切换。LD2-LD0100时,sw70时显示商,为1时显示余数,LD2-LD0101时,sw70时显示被除数,为1时显示除数。

    设计一个除法器,能在Basys2开发板上实际运行。

    被除数为16位,除数为8位,被除数和除数都用按键输入,结果用数码管显示,设置一个使能开关,开关朝上拨时才进行运算。由于数码管和按键等资源数量较少,因此可以考虑采取下面的方案实现。

    使用2个开关决定状态,例如SW1SW0SW1-SW000时用于输入被除数,通过4个按键输入416进制数,输入的数通过数码管显示;01时用于输入除数,通过2个按键输入216进制数,输入的数通过数码管显示;10时显示商;11时显示余数。

    上代码:

    顶层模块chufaqi.v:

      1 module chufaqi(reset,en,clk,sw1,sw0,btn3,btn2,btn1,btn0,an0,an1,an2,an3,
      2         dp,cg,cf,ce,cd,cc,cb,ca);   //除法器顶层模块chufaqi,端口名列表
      3 //端口的申明
      4 input reset,clk,sw1,sw0,btn3,btn2,btn1,btn0; 
      5 output an0,an1,an2,an3,dp,cg,cf,ce,cd,cc,cb,ca;
      6 input en;
      7 
      8 //四个待显示的数据
      9 reg[3:0] disp_data0,disp_data1,disp_data2,disp_data3;
     10 //16位被除数
     11 wire[15:0] dividend;
     12 //8位除数
     13 wire[7:0] divisor;
     14 //16位商
     15 wire[15:0] res;
     16 //8位余数
     17 wire[7:0] rm;
     18 //输入被除数时存放按键状态的寄存器
     19 reg dividend_key0,dividend_key1,dividend_key2,dividend_key3;
     20 //除数输入的时候存放按键状态寄存器
     21 reg divisor_key0,divisor_key1;
     22 
     23 always@(*)   //任何被赋值变量发生变化时执行
     24 begin
     25     if({sw1,sw0}==2'b00)  //开关sw1和sw0关闭
     26     begin
     27         dividend_key0<=btn0; //被除数的按键输入
     28         dividend_key1<=btn1;
     29         dividend_key2<=btn2;
     30         dividend_key3<=btn3;
     31     end
     32     else
     33     begin
     34         dividend_key0<=1'b0; //当开关sw1和sw0不是都关闭的时候,把所有的按键状态都清0
     35         dividend_key1<=1'b0;
     36         dividend_key2<=1'b0;
     37         dividend_key3<=1'b0;
     38     end
     39 end
     40 
     41 always@(*)
     42 begin
     43     if({sw1,sw0}==2'b01)  //开关sw1关闭,并且sw0打开的时候,进行除数的输入
     44     begin
     45         divisor_key0<=btn0;  //除数的按键输入
     46         divisor_key1<=btn1;
     47     end
     48     else
     49     begin 
     50         divisor_key0<=1'b0; //当不是开关sw1关闭,并且sw0打开的时候,把所有的按键状态都清0
     51         divisor_key1<=1'b0;
     52     end
     53 end
     54 
     55 always@(*)  //任何被赋值变量发生变化时都会执行
     56 begin
     57     case({sw1,sw0})      //根据sw1和sw0的状态来选择不同的数进行显示
     58     2'b00:          //sw1=0,sw0=0的时候,显示被除数
     59     begin
     60         disp_data0<=dividend[3:0];  //把被除数的0到3位放在寄存器disp_data0中,在数码管模块中显示
     61         disp_data1<=dividend[7:4];   //把被除数的4到7位放在寄存器disp_data1中,在数码管模块中显示
     62         disp_data2<=dividend[11:8];  //把被除数的8到11位放在寄存器disp_data2中,在数码管模块中显示
     63         disp_data3<=dividend[15:12];   //把被除数的12到15位放在寄存器disp_data3中,在数码管模块中显示
     64     end
     65     2'b01:    //sw1=0,sw0=1的时候,显示除数
     66     begin
     67         disp_data0<=divisor[3:0];  //把除数的0到3位放在寄存器disp_data0中,在数码管模块中显示
     68         disp_data1<=divisor[7:4];   //把被除数的4到7位放在寄存器disp_data1中,在数码管模块中显示
     69         disp_data2<=4'b0000;    //把disp_data2和disp_data3赋值为0,即把左边的两个数码管均显示为0
     70         disp_data3<=4'b0000;    
     71     end
     72     2'b10:  //sw1=1,sw0=0的时候,显示商
     73     begin
     74         disp_data0<=res[3:0];      //把商的0到3位放在寄存器disp_data0中,在数码管模块中显示
     75         disp_data1<=res[7:4];      //把商的4到7位放在寄存器disp_data1中,在数码管模块中显示
     76         disp_data2<=res[11:8];     //把商的8到11位放在寄存器disp_data2中,在数码管模块中显示
     77         disp_data3<=res[15:12];    //把商的12到15位放在寄存器disp_data3中,在数码管模块中显示
     78     end
     79     2'b11:  //sw1=1,sw0=1的时候,显示余数
     80     begin
     81         disp_data0<=rm[3:0];    //把余数的0到3位放在寄存器disp_data0中,在数码管模块中显示
     82         disp_data1<=rm[7:4];    //把余数的4到8位放在寄存器disp_data1中,在数码管模块中显示
     83         disp_data2<=4'b0000;    //把disp_data2和disp_data3赋值为0,即把左边的两个数码管均显示为0
     84         disp_data3<=4'b0000;    
     85     end
     86     endcase
     87 end
     88 
     89 //调用除法器模块
     90 
     91 division u1(.reset(reset),.en(en),.clk(clk),.num(dividend),.den(divisor),.res(res),.rm(rm));
     92 
     93 //调用数码管显示模块
     94 
     95 shumaguan u2(.clk(clk),.reset(reset),.datain0(disp_data0),.datain1(disp_data1),
     96         .datain2(disp_data2),.datain3(disp_data3),
     97         .an0(an0),.an1(an1),.an2(an2),.an3(an3),
     98         .dp(dp),.cg(cg),.cf(cf),.ce(ce),.cd(cd),.cc(cc),.cb(cb),.ca(ca));
     99         
    100 
    101 //下面调用按键计数模块,分别对被除数的输入和除数的输入的时候的按键次数进行计数
    102 //输入被除数的第一位数,即dividend[3:0]
    103 key_count u3(.reset(reset),.clk(clk),.key(dividend_key0),.q(dividend[3:0]));
    104 //输入被除数的第二位数,即dividend[7:4]
    105 key_count u4(.reset(reset),.clk(clk),.key(dividend_key1),.q(dividend[7:4]));
    106 //输入被除数的第三位数,即dividend[11:8]
    107 key_count u5(.reset(reset),.clk(clk),.key(dividend_key2),.q(dividend[11:8]));
    108 //输入被除数的第四位数,即dividend[15:12]
    109 key_count u6(.reset(reset),.clk(clk),.key(dividend_key3),.q(dividend[15:12]));
    110 //输入除数的第一位数,即divisor[3:0]
    111 key_count u7(.reset(reset),.clk(clk),.key(divisor_key0),.q(divisor[3:0]));
    112 //输入除数的第二位数,即divisor[7:4]
    113 key_count u8(.reset(reset),.clk(clk),.key(divisor_key1),.q(divisor[7:4]));
    114 
    115 endmodule

    除法器模块division.v

     1 module division(reset,en,clk,num,den,res,rm);  //除法器模块
     2 //端口申明
     3 input reset,en,clk;   
     4 input[15:0] num;  //被除数
     5 input[7:0] den;   //除数
     6 output[15:0] res;  //
     7 output[7:0] rm;  //余数
     8 
     9 reg[15:0] res;  
    10 reg[7:0] rm;
    11 
    12 //用tbuf寄存器来存放被除数,用dbuf寄存器来存放除数
    13 (*keep="true"*)reg[8:0] dbuf=9'd0; //(*keep="true"*)保证在综合优化的时候不会被剪切掉
    14 reg[23:0] tbuf=24'd0;  //tbuf初始化
    15 
    16 reg[4:0] state;  //定义状态寄存器
    17 
    18 always@(posedge reset or posedge clk)  //在复位信号的上升沿或者时钟信号的上升沿执行
    19 begin
    20     if(reset)  //复位
    21     begin
    22         res<=16'd0; //商清零
    23         rm<=8'b0000_0000;  //余数清零
    24         state<=5'b00000;  //状态清零
    25     end
    26     else
    27     begin
    28         if(en)  //使能信号,出发允许信号
    29         begin
    30             case(state) //状态机,根据state的不同状态进行不同的操作,这里用状态机进行16次的循环
    31             0:
    32             begin
    33                 tbuf[23:16]<= 8'b0000_0000;  //tbuf的高8位全补0
    34                 tbuf[15:0] <= num;  //tbuf的低16位放被除数
    35                 dbuf<={1'b0,den};  //dbuf最高位放0,低8位放除数
    36                 res<=tbuf[15:0];  //最终结果是:tbuf的低16位就是商
    37                 rm<=tbuf[23:16];   //tbuf的高8位就是余数
    38                 state<=state+1'b1;  //状态变量增加1
    39             end
    40             default:  //当state!=5'b00000时执行default
    41             begin
    42                 if(tbuf[23:15]>=dbuf[8:0])    //判断比较tbuf高9位和dbuf的大小,相当于对被除数的最高的一位进行除法。满足条件执行下面的代码。
    43                 begin 
    44                     tbuf[23:16]<=tbuf[22:15]-dbuf[7:0]; //对被除数此时的位数减去除数得到这一步的余数,再把余数存放到tbuf的高8位。
    45                     tbuf[15:0]<={tbuf[14:0],1'b1};  //把等待除法操作的被除数的最高位放在tnuf的第15为上,之后一直进行上述的操作。
    46                 end
    47                 else
    48                     
    49                     tbuf <= (tbuf << 1); //另一种写法:tbuf<={tbuf[22:0],1'b0}; 即将tbuf向左移一位。就是当tbuf[23:15]<dbuf[8:0]时,此时商是0,余数为此时的被除数的位数,把商放在tbuf最后,即向左移一位。
    50 
    51                 if(state!=5'b10000)    //对未使用的state不断加1,回到0000-1111的状态,跳过未使用的state的状态
    52                     state<=state+1'b1;  
    53                 else
    54                     state<=5'b00000;  //回到state=5'b00000的状态,显示商和余数  
    55             end
    56             endcase
    57         end
    58     end
    59 end
    60 
    61 endmodule
    62 //所以总的来说,此除法器的原理:就是移位 加上 减法。

    按键计数模块key_count.v

     1 module key_count(reset,clk,key,q); //按键计数模块
     2 
     3 //端口的申明
     4 input reset,clk,key;
     5 output[3:0] q;
     6 
     7 //寄存器q
     8 reg[3:0] q;
     9 
    10 //保持按键持续状态的寄存器
    11 reg key_last;
    12 
    13 //定按键次数增加的标志位rise_get
    14 reg rise_get=1'b0; 
    15 
    16 always @(posedge reset or posedge clk) //时钟上升沿或者复位信号的上升沿执行
    17 begin
    18     if(reset)   //复位
    19     key_last<=1'b0; //清零
    20     else
    21     key_last<=key;  //否则把按键的状态存放在寄存器key_last中
    22 end
    23 
    24 always @(posedge reset or posedge clk) //时钟上升沿或者复位信号的上升沿执行
    25 begin
    26     if(reset)    //检测到reset为高电平的时候,即复位的时候
    27     rise_get<=1'b0; //将rise_get清零
    28     else if((!key_last)&&key)  
    29     rise_get<=1'b1;  //当key_last=0并且key=1的时候,令按键增加标志rise_get=1。这行代码可以防止按键一直按下,按键一直按下的时候不会对按键计数
    30     else  //否则把rise_get清零
    31     rise_get<=1'b0;
    32 end
    33 
    34 always @(posedge reset or posedge clk) //时钟上升沿或者复位信号的上升沿执行
    35 begin
    36     if(reset)  
    37     q<=4'b0000; //复位清零
    38     else if(rise_get) //如果按键增加标志rise_get=1的时候按键次数加一
    39     q<=q+1'b1;
    40 end
    41 
    42 endmodule

    数码管显示模块shumaguan.v

      1 module shumaguan(clk,reset,datain0,datain1,datain2,datain3,an0,an1,an2,an3,dp,cg,cf,ce,cd,cc,cb,ca); //数码管显示模块
      2 //端口的申明
      3 input clk,reset;
      4 input[3:0] datain0,datain1,datain2,datain3; 
      5 output an0,an1,an2,an3; 
      6 output dp,cg,cf,ce,cd,cc,cb,ca; 
      7 reg an0,an1,an2,an3; //输出数码管的选通信号
      8 reg cg,cf,ce,cd,cc,cb,ca; //输出数码管的“七段”
      9 
     10 wire dp; //数码管的“点”
     11 
     12 reg div1000,div100;
     13 reg[9:0] div1000_count; //定义一个10位的计数器
     14 reg[6:0] div100_count; //定义一个7位的计数器
     15 reg[1:0] state; //四种状态
     16 reg[3:0] data; //0~F 需要显示的值,某一位数码管显示的数字
     17 
     18 
     19 //计数器用来分频,用于显示数码管
     20 always @(posedge clk or posedge reset)  //在时钟的上升沿或者复位信号的上升沿的时候执行
     21 begin
     22     if(reset)  //复位的时候
     23     begin
     24         div1000_count<=10'd0;   //把计数器清零
     25         div1000<=1'b0;        //把计数的标志位清零
     26     end
     27     else if(div1000_count==10'd999)    //当计数器计数到999的时候
     28     begin
     29         div1000_count<=10'd0;   //div1000_count范围0~999 上一个上升沿div1000计数达到999 这个上升沿返回0
     30         div1000<=1'b1;         //计数满1000 输出标志1
     31     end
     32     else
     33     begin
     34         div1000_count<=div1000_count+1'b1;   //当计数器div1000_count没有达到999的时候,每次时钟上升沿的时候计数器加一
     35         div1000<=1'b0;  //并且保证此时的计数“满”的标志为0
     36     end
     37 end
     38 
     39 always @(posedge clk or posedge reset)  //在时钟的上升沿或者复位信号的上升沿的时候执行
     40 begin
     41     if(reset)  //复位
     42     begin
     43         div100_count<=7'b000_0000;  //将计数器div100_count清零
     44         div100<=1'b0;  //将计数器的标志位div100清零
     45     end
     46     else   //未复位的时候
     47     begin 
     48         if(div1000)   //当计数器div1000_count满999的时候,标志位div1000会被置1
     49         begin
     50             if(div100_count==7'd99)
     51             begin
     52                 div100_count<=7'b000_0000;   //如果计数器div100_count计数满99的时候将计数器div100_count清零
     53                 //div100<=1'b1;
     54             end
     55             else
     56             begin
     57                 div100_count<=div100_count+1'b1;   //如果计数器div100_count计数不满99的时候将计数器div100_count加一
     58                 //div100<=1'b0;
     59             end
     60         end
     61     if((div1000)&&(div100_count==7'd99))  //当计数器div100_count满99并且计数器div1000_count满999的时候将标志位div100置1
     62         div100<=1'b1;
     63     else
     64         div100<=1'b0;
     65 
     66     end
     67 end
     68 
     69 always @(posedge clk or posedge reset)  //在时钟上升沿或者复位的上升沿的时候执行
     70 begin
     71     if(reset)   //复位
     72     begin
     73         state<=2'b00;   //状态复位为00
     74         data<=4'b0000;   //显示的数据清零
     75         {an3,an2,an1,an0}<=4'b1111;  //所有的数码管都不选通
     76     end
     77     else
     78     begin
     79         case(state)  //根据state不同状态来选通不通的数码管显示数据
     80         2'b00:
     81         begin
     82             data<=datain0; //显示datain0的值
     83             {an3,an2,an1,an0}<=4'b1110; //共阳 an0亮 其余不亮
     84         end
     85         2'b01:
     86         begin
     87             data<=datain1;
     88             {an3,an2,an1,an0}<=4'b1101;
     89         end
     90         2'b10:
     91         begin
     92             data<=datain2;
     93             {an3,an2,an1,an0}<=4'b1011;
     94         end
     95         2'b11:
     96         begin
     97             data<=datain3;
     98             {an3,an2,an1,an0}<=4'b0111;
     99         end
    100         endcase
    101 
    102         if(div100)
    103         begin
    104             state<=state+1'b1; //每计数100000次 state加一 下一个数码管亮 数码管扫描显示
    105         end
    106     end
    107 end
    108 
    109 always @(data)  //always当data发生变化的时候执行
    110 begin
    111     case(data)  //根据显示的数据来译码驱动数码管显示
    112     4'b0000:{cg,cf,ce,cd,cc,cb,ca}<=7'b1000000;  //0
    113     4'b0001:{cg,cf,ce,cd,cc,cb,ca}<=7'b1111001;   //1
    114     4'b0010:{cg,cf,ce,cd,cc,cb,ca}<=7'b0100100;   //2
    115     4'b0011:{cg,cf,ce,cd,cc,cb,ca}<=7'b0110000;   //3
    116     4'b0100:{cg,cf,ce,cd,cc,cb,ca}<=7'b0011001;   //4
    117     4'b0101:{cg,cf,ce,cd,cc,cb,ca}<=7'b0010010;  //5
    118     4'b0110:{cg,cf,ce,cd,cc,cb,ca}<=7'b0000010;   //6
    119     4'b0111:{cg,cf,ce,cd,cc,cb,ca}<=7'b1111000;   //7
    120     4'b1000:{cg,cf,ce,cd,cc,cb,ca}<=7'b0000000;   //8
    121     4'b1001:{cg,cf,ce,cd,cc,cb,ca}<=7'b0010000;   //9
    122     4'b1010:{cg,cf,ce,cd,cc,cb,ca}<=7'b0001000;    //a
    123     4'b1011:{cg,cf,ce,cd,cc,cb,ca}<=7'b0000011;    //b
    124     4'b1100:{cg,cf,ce,cd,cc,cb,ca}<=7'b1000110;    //c
    125     4'b1101:{cg,cf,ce,cd,cc,cb,ca}<=7'b0100001;    //d
    126     4'b1110:{cg,cf,ce,cd,cc,cb,ca}<=7'b0000110;    //e
    127     4'b1111:{cg,cf,ce,cd,cc,cb,ca}<=7'b0001110;    //f
    128     endcase
    129 end
    130 
    131 assign dp=1'b1; //dp(每个数码管的“点”)一直灭
    132 
    133 endmodule

    端口约束文件chufaqi.ucf

    #Inputs
    NET "clk"     LOC="B8";
    #NET "clk"     CLOCK_DEDICATED_ROUTE = FALSE;
    NET "reset"     LOC="F3";     #SW5是复位信号开关
    NET "en"     LOC="G3";     #SW4是使能信号开关
    
    NET "sw1"     LOC="B4";     #SW3和sw2输入被除数和除数
    NET "sw0"     LOC="K3";     
    
    NET "btn3"     LOC="A7";     #BTN3
    NET "btn2"     LOC="M4";     #BTN2
    NET "btn1"     LOC="C11";     #BTN1
    NET "btn0"     LOC="G12";     #BTN0
    
    #Outputs
    
    NET "an0"    LOC="F12";
    NET "an1"    LOC="J12";
    NET "an2"    LOC="M13";
    NET "an3"    LOC="K14";
    
    NET "dp"    LOC="N13";
    NET "cg"    LOC="M12";
    NET "cf"    LOC="L13";
    NET "ce"    LOC="P12";
    NET "cd"    LOC="N11";
    NET "cc"    LOC="N14";
    NET "cb"    LOC="H12";
    NET "ca"    LOC="L14";
  • 相关阅读:
    97. Interleaving String
    96. Unique Binary Search Trees
    95. Unique Binary Search Trees II
    94. Binary Tree Inorder Traversal
    odoo many2many字段 指定打开的form视图
    docker sentry 配置文件位置
    postgres 计算时差
    postgres 字符操作补位,字符切割
    postgres判断字符串是否为时间,数字
    odoo fields_view_get
  • 原文地址:https://www.cnblogs.com/jeavenwong/p/7701056.html
Copyright © 2011-2022 走看看