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";
  • 相关阅读:
    Python基础:数据类型-列表与元组(6)
    Python基础:数据类型-数字(5)
    Python基础:编码规范(4)
    Python基础:语法基础(3)
    Python基础:第一个Python程序(2)
    Python基础:搭建开发环境(1)
    Vue.js 2.x笔记:服务请求axios(8)
    Vue.js 2.x笔记:状态管理Vuex(7)
    一位资深传统型软件开发者的思考:传统软件企业危机四伏【转】
    基于cesium的GIS洪水淹没三维模拟系统
  • 原文地址:https://www.cnblogs.com/jeavenwong/p/7701056.html
Copyright © 2011-2022 走看看