最近開始入門FPGA,斷斷續續做了些筆記。
今天裝了lattice開發環境,嘗試模仿歷程,把LED_shining 歷程敲一遍,進行編譯。報錯: 
提示為23行有語法錯誤,原因是module最后一個參數不需要逗號 
改掉這里再編譯 提示25行錯誤 
定位實際是24行語句少寫了分隔符導致,加上之后再編譯就成功了。 
管腳分配,結合硬件手測分配管腳 兩個輸入 clk_in ,rst_n_in 兩個輸出 led1,led2 
 2019/2/28
三個開關控制RGB led實驗。
module LED (sw,led);
input [2:0] sw;//開關輸入信號,利用了其中3個開關
output [2:0] led;//輸出信號到RGB LED
assign led = sw;/assign連續賦值。
endmodule
我想用3個開關控制兩個RGB led燈: 
代碼改為:
module RGB_LED1(sw,led1,led2);
input [2:0] sw;
output [2:0] led1;
output [2:0] led2;
assign led1 = sw;
assign led2 = sw;
endmodule
改為非阻塞賦值的方式:
需要注意的有兩點,
1.輸入port只能是wire型不能定義成reg型。
2.非阻塞賦值必須用在always塊中執行
3.if 語句或者case 語句 后面只能跟一句賦值語句,若要跟多句,需要用到begin end語句
4.always塊要定義觸發信號
module RGB_LED(clk,rst,sw,led1,led2);
input clk,rst;
input [2:0] sw;
output led1,led2;
reg [2:0] led1;
reg [2:0] led2;
always@(posedge clk or negedge rst)
if(!rst)
begin
led1<=0;
led2<=0;
end
else
begin
led1<=sw;
led2<=sw;
end
endmodule
2019/3/1
開關控制數碼管顯示0~F;原例程為:
module LED (seg_data_1,seg_data_2,seg_led_1,seg_led_2);
input[3:0] seg_data_1;
input[3:0] seg_data_2;//小腳丫上第二個數碼管
output[8:0] seg_led_1;//控制一個數碼管需要9個信號 MSB~LSB=DIG、DP、G、F、E、D、C、B、A
output [8:0] seg_led_2;//在小腳丫上第二個數碼管的控制信號 MSB~LSB=DIG、DP、G、F、E、D、C、B、A
reg[8:0] seg [9:0];//定義了一個reg型的數組變量,相當于一個10*9的存儲器,存儲器一共有10個數,每個數有9位寬
initial//在過程塊中只能給reg型變量賦值,Verilog中有兩種過程塊always和initial
//initial和always不同,其中語句只執行一次
begin
seg[0]=9'h3f;//對存儲器中第一個數賦值9'b00_0011_1111,相當于共陰極接地,DP點變低不亮,7段顯示數字 0
seg[1]=9'h06;//7段顯示數字 1
seg[2]=9'h5b;//7段顯示數字 2
seg[3]=9'h4f;//7段顯示數字 3
seg[4]=9'h66;//7段顯示數字 4
seg[5]=9'h6d;//7段顯示數字 5
seg[6]=9'h7d;//7段顯示數字 6
seg[7]=9'h07;//7段顯示數字 7
seg[8]=9'h7f;//7段顯示數字 8
seg[9]=9'h6f;//7段顯示數字 9
end
assign seg_led_1 = seg[seg_data_1];//連續賦值,這樣輸入不同四位數,就能輸出對于譯碼的9位輸出
assign seg_led_2 = seg[seg_data_2];
endmodule
原例程用的是數據流描述方式(assign)實現的,我改為行為描述方式(initial+always)
并從顯示0~9擴展為顯示0~F
注意的是 initial后面是不需要“:”
Initial 和always后都可以用非阻塞賦值,多個要用包含在begin...end 里。
module SHUMAGUAN(sw,key,led1,led2);
input [3:0] sw;
input [3:0] key;
output led1,led2;
reg [8:0] led1;
reg [8:0] led2;
reg [8:0] seg[16:0];
initial
begin
seg[0] <= 9'h0x3F;
seg[1] <= 9'h0x06;
seg[2] <= 9'h0x5B;
seg[3] <= 9'h0x4F;
seg[4] <= 9'h0x66;
seg[5] <= 9'h0x6D;
seg[6] <= 9'h0x7D;
seg[7] <= 9'h0x07;
seg[8] <= 9'h0x7F;
seg[9] <= 9'h0x6F;
seg[10]<= 9'h0x77;
seg[11]<= 9'h0x7C;
seg[12]<= 9'h0x39;
seg[13]<= 9'h0x5E;
seg[14]<= 9'h0x79;
seg[15]<= 9'h0x71;
end
always@(sw or key)
begin
case(sw)
4'b0000:led1 <= seg[0];
4'b0001:led1 <= seg[1];
4'b0010:led1 <= seg[2];
4'b0011:led1 <= seg[3];
4'b0100:led1 <= seg[4];
4'b0101:led1 <= seg[5];
4'b0110:led1 <= seg[6];
4'b0111:led1 <= seg[7];
4'b1000:led1 <= seg[8];
4'b1001:led1 <= seg[9];
4'b1010:led1 <= seg[10];
4'b1011:led1 <= seg[11];
4'b1100:led1 <= seg[12];
4'b1101:led1 <= seg[13];
4'b1110:led1 <= seg[14];
4'b1111:led1 <= seg[15];
default:led1 <= seg[0];
endcase
case(key)
4'b0000:led2 <= seg[0];
4'b0001:led2 <= seg[1];
4'b0010:led2 <= seg[2];
4'b0011:led2 <= seg[3];
4'b0100:led2 <= seg[4];
4'b0101:led2 <= seg[5];
4'b0110:led2 <= seg[6];
4'b0111:led2 <= seg[7];
4'b1000:led2 <= seg[8];
4'b1001:led2 <= seg[9];
4'b1010:led2 <= seg[10];
4'b1011:led2 <= seg[11];
4'b1100:led2 <= seg[12];
4'b1101:led2 <= seg[13];
4'b1110:led2 <= seg[14];
4'b1111:led2 <= seg[15];
default:led2 <= seg[0];
endcase
end
Endmodule
2019/3/5
按鍵消抖:
需要掌握兩個概念:
脈沖邊沿檢測:用一個頻率更高的時鐘去觸發待檢測的信號,用兩個寄存器去存儲相鄰兩個時鐘采集的值,然后進行異或運算,如果不為0,則代表了上升沿或者下降沿。
消抖方法,第一次檢測脈沖,delay20ms,再檢測key是否為低,如果為低則正面按鍵按下。否則認為是抖動。
我寫的代碼如下(有問題):
module key(clk,key,rst,led);
input key,rst,clk;
output reg led;
reg [17:0] count;
wire key_edge,key_pulse;
reg key_pre,key_now;
reg key2_pre,key2_now;
always@(posedge clk or negedge rst) //檢測第一次脈沖發生 key_edge
begin
if(!rst)
begin
key_now=1'b1;
key_pre=1'b1;
end
else
begin
key_now <= key;
key_pre <= key_now;
end
end
assign key_edge = key_pre&(~key_now); //按鍵按下產生一個周期正脈沖
always@(posedge clk or negedge rst) //定時器初始化
begin
if(!rst)
count<=18'h0;
else if(key_edge)
count<=18'h0;
else
count<=count+1'h1;
end
always@(posedge clk or negedge rst) //如果有按鍵按下,延時21ms 再檢測一次key
begin
if(!rst)
begin
key2_now<=1'b1;
end
else if(count==18'h3ffff)
begin
key2_now<=key;
key2_pre<=key2_now; //這里寫錯了
end
end
//always@(posedge clk or negedge rst)
//begin
//if(!rst)
//key2_pre <=1'b1;
//else
//key2_pre <=key2_now;
//end
assign key_pulse = key2_pre&(~key2_now);
always@(posedge clk or negedge rst)
begin
if(!rst)
led<=1'b1;
else if(key_pulse)
led<=~led;
else
led<=led;
end
endmodule
以上代碼運行后并沒有得到我想要的結果,按鍵按下亮,松掉熄滅。原因是延時結束后key2_pre 不應該在這里賦值。因為下一個時鐘clk進來時count已經不等于18‘h3ffff了,則不會進入賦值

需要改為:
always@(posedge clk or negedge rst) //如果有按鍵按下,延時21ms 再檢測一次key
begin
if(!rst)
begin
key2_now<=1'b1;
end
else if(count==18'h3ffff)
begin
key2_now<=key;
end
end
always@(posedge clk or negedge rst)
begin
if(!rst)
key2_pre <=1'b1;
else
key2_pre <=key2_now;
end
這里比較難理解,key2_now 是等待21ms后真正的按鍵狀態,而key2_pre是key2_now前一個周期的狀態,即為高,所以這是如果真正按鍵按下了,會輸出一個周期的高電平脈沖。如果檢測到這個脈沖,則認為按鍵是真的按下了。
2019/4/9
通過移位寄存器 練習編寫測試文件,并對仿真結果進行分析:
移位寄存器源碼:clk 來一次,高位向低位移位,rst 時賦初值
module SHIFT(clk,rst,datain,dataout);
input clk,rst;
input [6:0] datain;
output dataout;
wire dataout;
reg [6:0] data;
always@(posedge clk)
if(!rst)
data <= datain;
else
begin
data[6] <= 1'b0;
data[5] <= data[6];
data[4] <= data[5];
data[3] <= data[4];
data[2] <= data[3];
data[1] <= data[2];
data[0] <= data[1];
end
assign dataout = data[0];
endmodule
測試用例:
測試需要給條件為 clk 翻轉周期,rst 持續時間,以及rst 時給datain 賦的初值
`timescale 1ns/100ps //
module SHIFT_TEST;
reg clk,rst;
reg [6:0] datain;
wire dataout;
initial
begin
clk = 0;
rst = 1;
datain = 7'b1110101;
#50
rst = 0;
#100
rst = 1;
end
always #10 clk = ~clk;
SHIFT u1(
.clk(clk),
.rst(rst),
.datain(datain),
.dataout(dataout)
);
Endmodule
仿真結果如下: 
把clk 周期改為20ns 看起來兩個圖有差別,實際上是一樣的,rst 變高后 clk第一個上一個升沿為移出的數據,從clk上升沿往前看一個周期,其實都是1,移出的數據為1010111, 實現了移位的功能。RST 為高的時候數據才是有效的,有意思的是在rst為低時,輸出是保持賦值的最低位不變的。 
比如改賦值為1110100,clk 周期為20ns 波形如下: 
完整的Word格式文檔51黑下載地址:
小腳丫FPGA學習筆記.docx
(572.01 KB, 下載次數: 22)
2019-4-9 18:50 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|