AlteraFPGA學習筆記
Verilog語法
對於數字邏輯電路,主要有組合邏輯和時序邏輯
組合邏輯:多路選擇器、譯碼器、加法器、乘法器等等
時序邏輯:計數器
module模塊
module mux2(a,b,sel,out);
endmodule
module: 模塊聲明(表示module到endmodule之間的內容是描述模塊的代碼)
mux2: 模塊名字(自定義,相當於此模塊的名稱)
(a,b,sel,out): 括號內是端口列表(類似芯片的引腳定義)
端口類型與位寬
module mux2(a,b,sel,out,io);
input a;
input b;
input sel;
output out;
inout io;
endmodule
input a: 表示端口a是輸入端口(類似芯片的輸入引腳)
output b: 表示端口b是輸出端口(類似芯片的輸出引腳)
inout io: 表示端口io是雙向端口(類似芯片的雙向引腳)
module mux2(
input [7:0] a,
input [7:0] b,
input sel,
output [7:0] out,
inout io
);
endmodule
與前兩個例子功能相同,只是另一種寫法,將端口的輸入輸出描述放入括號內完成
注:input [7:0] a 表示輸入端口是一個8bit的總線,相當於8根導線,並且編號爲:7 6 5 4 3 2 1 0
內部信號
module mux2(
input [7:0] a,
input [7:0] b,
input sel,
output [7:0] out,
inout io
);
wire oe;
reg ow;
endmodule
wire oe; 表示名稱爲oe內部信號(相當於芯片內部的一根導線)
reg ow; 表示名稱爲ow內部信號(相當於芯片內部的一位寄存器)
注:導線不存在存儲功能,只有連接功能,所以對於時序邏輯電路無法使用wire型的信號,而組合邏輯電路無法使用reg型變量,但在Verilog語法中任何處於always中的信號均應爲reg型
連續賦值語句
module mux2(
input [7:0] a,
input [7:0] b,
input sel,
output [7:0] out,
inout io
);
wire oe;
assign out = sel ? a : b;
assign oe = sel;
assign io = oe ? out[0] : 1'bz;
endmodule
assign out = xxx: 將等號右邊組合邏輯(xxx)的輸出與導線out相連接(對於連續賦值語句assign,只能是wire型的信號)
sel ? a : b: 組合邏輯,sel 爲1 輸出a 反之輸出b
1’bz: 一個高阻態(z)信號
4’b1001; 表示4位二進制1001
4’d9; 表示4位十進制9
4’hc; 表示4位十六進制c
32’hff_00_ff_00 表示32位十六進制ff00ff00
位操作
module mux2(
input [7:0] a,
input [7:0] b,
input sel,
output [7:0] out,
inout io
);
wire oe;
assign out = sel ? a : b;
assign oe = sel;
assign io = oe ? out[0] : 1'bz;
wire [2:0]m;
assign m = out[5:3];
//reg [7:0]shift_a;
//shift_a <= {shift_a[0],shift_a[7:1]};
wire [3:0] x;
wire [3:0] y;
wire [7:0] z;
z = {x,y};
z = {2{x}};
endmodule
截取
assign m = out[5:3]; 將out總線編號爲:5 4 3的導線連接到總線m上(默認連接到m的 2 1 0號導線上)
移位
shift_a <= {shift_a[0],shift_a[7:1]}; 時序邏輯內的操作,每次右移一位(循環右移)。
將shift_a總線重新拼接
右移0次shift_a的導線編號:7 6 5 4 3 2 1 0
右移1次shift_a的導線編號:0 7 6 5 4 3 2 1
右移2次shift_a的導線編號:1 0 7 6 5 4 3 2
.
.
.
拼接
z = {x,y}; 將x與y總線進行拼接
拼接前: x( 3 2 1 0 ) y( 3 2 1 0 )
拼接後: z( 7 6 5 4 3 2 1 0 )
其中z[7]與x[3]相接
z[3]與y[3]相接,以此類推
z = {2{x}}; 等效於z = {x,x};
同理z = {3{x}};等效於z = {x,x,x};
運算
對於語法來說+ - * / 四則運算是可以通過的
但是在實際應用中由於資源有限,對於乘法和除法儘量使用加減法和移位代替,尤其是除法操作,可以使用IP核
邏輯運算
&& 邏輯與
|| 邏輯或
! 邏輯非
& 按位與
| 按位或
~ 按位非
時序邏輯
module counter(
clk,
en,
clr,
cnt_value
);
input clk; //時鐘信號
input en; //使能信號
input clr; //清零信號
output [3:0] cnt_value;
reg [3:0] cnt; //4位計數用寄存器
always@(posedge clk or posedge clr) //clk上升沿觸發 或 clr上升沿觸發
if(clr)
cnt <= 4'b0;
else if(en) begin
cnt <= cnt + 1'b1;
end
assign cnt_value = cnt;
endmodule
always@(posedge clk or posedge clr) 表示時序邏輯部分,posedge爲上升沿,negedge爲下降沿
注:同一個信號禁止同時出現上升與下降的觸發
Test Bench
用於對編寫好的moudle進行功能測試所編寫的邏輯,僅用在仿真驗證環節
`timescale 1ns/1ns
`define clk_period 10
module counter_tb;
reg clk_t;
reg en_t;
reg clr_t;
wire [7:0]cnt_value_t;
counter counter_1(
.clk(clk_t),
.en(en_t),
.clr(clr_t),
.cnt_value(cnt_value_t[7:4])
);
counter counter_2(
.clk(clk_t),
.en(en_t),
.clr(clr_t),
.cnt_value(cnt_value_t[3:0])
);
initial clk_t = 1; //初始化clk_t的值
always #(`clk_period/2) clk_t = ~clk_t;
//always表示循環
initial begin
en_t = 0;
clr_t = 0;
#(`clk_period*20) //延時
ent_t = 1;
#(`clk_period*200)
ent_t = 0;
#(`clk_period*200)
clr_t = 1;
#(`clk_period*200)
clr_t = 0;
#(`clk_period*200)
$stop;
end
endmodule
timescale 第一個參數表示步進,第二個表示精度,一般寫成相同的
`define clk_period 10 重定義,與C語言相同
counter counter_1 類似C++中類的實例化,counter是類名,counter_1是實例化後的名稱
隨機數
myrand = {$random};
myrand2 = {$random} % 65536; // 0 - 65535
myrand3 = $random % 65536; //-65535 - 65535
花括號類似絕對值
測試Task
task press_key
begin
...
end
end task
類似C語言中的函數,press_key是函數名
調用的時候很簡單,如下:
always@(posedge press)
press_key;
循環repeat
repeat(50) begin
myrand = {$random} % 65536;
#myrand key = ~key;
end
循環指定次數,例子中是循環50次
按鍵消抖
工程地址:
學習重點:
1、多模塊之間的連接
2、消抖原理
3、亞穩態處理
多模塊之間的連接
module key_cnt_led(
led1,
led2,
led3,
led4,
key,
clk,
rst
);
input wire clk,rst;
input key;
reg key_1;
reg key_2;
output wire led1,led2,led3,led4;
wire outkey;
pushkey pushkey_01(
.clk(clk),
.key(key_2),
.out(outkey)
);
count_led count_led_01(
.led1(led1),
.led2(led2),
.led3(led3),
.led4(led4),
.clk(outkey),
.rst(rst)
);
always@(posedge clk) begin
key_1 <= key;
key_2 <= key_1;
end
endmodule
就像用烙鐵焊接元器件一樣,使用wire將各個module連接起來
消抖原理
module pushkey(
clk,
key,
out
);
input clk,key;
output reg out;
reg flag;
reg [20:0]cnt;
localparam PUSH = 1'b1;
localparam NO_PUSH = 1'b0;
always@(posedge clk) begin
if(!key)
flag <= PUSH;
else begin
cnt[20:0] <= 21'd0;
flag <= NO_PUSH;
end
case(flag)
NO_PUSH:
out <= 1'b0;
PUSH: if(cnt[20:0] == 21'd999999)
out <= 1'b1;
else
cnt <= cnt + 21'b1;
endcase
end
endmodule
使用定時器對按鍵的狀態進行檢測,若20ms內沒有出現波動,則認爲按下了按鍵
亞穩態處理
亞穩態是由於異步系統信號的不確定性造成的。亞穩態的狀態會對次級系統造成嚴重的影響,導致系統崩潰
處理的辦法,使用級聯DQ觸發器即可解決
always@(posedge clk) begin
key_1 <= key;
key_2 <= key_1;
end
BCD數碼管
實現8個8位數碼管,根據分頻後的時鐘自動計數(16進制)
硬件結構
開發板:AC620
整體框架
BCDDisplay模塊
根據輸入的data每4位爲1組,共8組,對應8個數碼管,輪詢進行刷新
device_74hc595模塊
根據輸入的並行數據輸出串行數據,每次輸出後會產生鎖存脈衝(lock),在輸出過程中會產生busy高電平信號,等傳輸完畢後恢復低電平。
代碼部分
device_74hc595
module device_74hc595(
clk,
rst,
data,
busy,
lock,
lock595,
out595,
clk595
);
input clk,rst,lock;
input [15:0]data;
output reg lock595,clk595,busy,out595;
reg [4:0]send_cnt;
reg [15:0]data_buffer;
reg [1:0]flag;
localparam SEND_STATE = 2'b00;
localparam LOCK0_STATE = 2'b01;
localparam LOCK1_STATE = 2'b11;
localparam IDLE_STATE = 2'b10;
always@(posedge clk,negedge rst)
if(!rst)begin
send_cnt <= 5'b0;
busy <= 1'b0;
data_buffer <= 16'b0;
flag <= IDLE_STATE;
lock595 <= 1'b0;
clk595 <= 1'b1;
out595 <= 1'b0;
end
else
case(flag)
SEND_STATE:
if(clk595) begin
if(send_cnt == 5'd16)
flag <= LOCK0_STATE;
else begin
send_cnt <= send_cnt + 5'b1;
out595 <= data_buffer[5'd15-send_cnt];
clk595 <= 1'b0;
end
end
else begin
clk595 <= 1'b1;
end
LOCK0_STATE: begin
lock595 <= 1'b1;
flag <= LOCK1_STATE;
end
LOCK1_STATE: begin
lock595 <= 1'b0;
flag <= IDLE_STATE;
end
IDLE_STATE:
if(lock)begin
send_cnt <= 5'b0;
data_buffer <= data;
busy <= 1'b1;
flag <= SEND_STATE;
end
else
busy <= 1'b0;
default:
flag <= IDLE_STATE;
endcase
endmodule
BCDdecoder
module BCDdecoder(
data,
out
);
input [3:0]data;
output reg [7:0]out;
always@(*)
case(data)
4'h0:out = 7'b1000000;
4'h1:out = 7'b1111001;
4'h2:out = 7'b0100100;
4'h3:out = 7'b0110000;
4'h4:out = 7'b0011001;
4'h5:out = 7'b0010010;
4'h6:out = 7'b0000010;
4'h7:out = 7'b1111000;
4'h8:out = 7'b0000000;
4'h9:out = 7'b0010000;
4'ha:out = 7'b0001000;
4'hb:out = 7'b0000011;
4'hc:out = 7'b1000110;
4'hd:out = 7'b0100001;
4'he:out = 7'b0000110;
4'hf:out = 7'b0001110;
endcase
endmodule
frequencydivider
module frequencydivider(
clk,
rst,
fclk
);
input clk,rst;
output reg fclk;
reg [31:0]cnt;
always@(posedge clk,negedge rst)
if(!rst) begin
cnt <= 0;
fclk <= 0;
end
else if(cnt == 500) begin
cnt <= 0;
fclk <= ~fclk;
end
else
cnt <= cnt + 1;
endmodule
BCDDisplay
module BCDDisplay(
clk,
rst,
data,
lock595,
out595,
clk595
);
wire fclk;
input clk,rst;
input [31:0]data;
output lock595,out595,clk595;
reg flag;
reg [7:0]selecter;
reg [7:0]srcdata;
wire [7:0]dstdata;
reg [31:0]data_buffer;
reg lock;
wire busy;
reg doonce;
reg [7:0]cnt;
localparam Wait_Busy = 1'b0;
localparam Circle_Play = 1'b1;
BCDdecoder BCDdecoder_01(
.data(srcdata),
.out(dstdata)
);
frequencydivider frequencydivider_01(
.clk(clk),
.rst(rst),
.fclk(fclk)
);
device_74hc595 device_74hc595_01(
.clk(fclk),
.rst(rst),
.data({dstdata,selecter}),
.busy(busy),
.lock(lock),
.lock595(lock595),
.out595(out595),
.clk595(clk595)
);
always@(posedge clk,negedge rst)
if(!rst) begin
selecter <= 8'b0000_0001;
data_buffer <= 32'b0;
flag <= Circle_Play;
srcdata <= 8'b0;
lock <= 0;
cnt <= 8'd4;
doonce <= 0;
end
else begin
data_buffer <= data;
case(flag)
Circle_Play: begin
srcdata <= data_buffer >> cnt;
selecter <= {selecter[6:0],selecter[7]};
flag <= Wait_Busy;
doonce <= 0;
if(cnt == 8'd28)
cnt <= 8'b0;
else
cnt <= cnt + 4;
end
Wait_Busy:
if(busy) begin
lock <= 0;
doonce <= 1;
end
else if(doonce)
flag <= Circle_Play;
else
lock <= 1;
endcase
end
endmodule
頂層代碼
module test_74hc595(
clk,
rst,
key,
lock595,
out595,
clk595
);
wire lock;
input clk;
input wire rst,key;
wire rst2;
reg [31:0]data;
output lock595,out595,clk595;
wire rst3;
wire clk2;
inkey inkey_1(
.in(key),
.out(lock),
.clk(clk)
);
inkey inkey_2(
.in(rst),
.out(rst2),
.clk(clk)
);
BCDDisplay BCDDisplay_1(
.clk(clk),
.rst(rst3),
.data(data),
.lock595(lock595),
.out595(out595),
.clk595(clk595)
);
frequencydivider frequencydivider2(
.clk(clk),
.rst(rst3),
.fclk(clk2)
);
always @(negedge rst3,posedge clk2)
begin
if(!rst3)
data <= 0;
else
data <= data + 1;
end
assign rst3 = ~rst2;
endmodule