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";
原文地址:https://www.cnblogs.com/jeavenwong/p/7701056.html