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";

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM