介紹一下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用於顯示被除數和除數
btn1和btn0和數碼管配合,用於改變准備輸入的數據
btn2用於將數碼管顯示的數據輸入到相應的地方,同時切換狀態。btn3 的功能也是切換狀態,如果用btn3切換狀態,則不改變原來的數據。
sw6如果為1表示允許進行除法運算,為0則表示不允許。
因為LD2-LD0為100和101時需要顯示6位16進制數,而數碼管只有4個,所以用sw7進行切換。LD2-LD0為100時,sw7為0時顯示商,為1時顯示余數,LD2-LD0為101時,sw7為0時顯示被除數,為1時顯示除數。
設計一個除法器,能在Basys2開發板上實際運行。
被除數為16位,除數為8位,被除數和除數都用按鍵輸入,結果用數碼管顯示,設置一個使能開關,開關朝上撥時才進行運算。由於數碼管和按鍵等資源數量較少,因此可以考慮采取下面的方案實現。
使用2個開關決定狀態,例如SW1和SW0,SW1-SW0為00時用於輸入被除數,通過4個按鍵輸入4位16進制數,輸入的數通過數碼管顯示;01時用於輸入除數,通過2個按鍵輸入2位16進制數,輸入的數通過數碼管顯示;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";