module counter( input clk, input rst_n, output reg[7:0] data_out ); //reg define reg [3:0] cnt; //假設計數器每計數十次,溢滿一次 reg [2:0] lsm_cnt; //計數器每溢滿一次,lsm_cnt寄存器自加1 //parameter [7:0] num = 8'b10110101;此句和下面兩句功能等效 wire [7:0] num; //定義一個八位數據 assign num = 8'b10110101; initial data_out <= 8'b0000_0000; //如果此處沒有給初值,那么在仿真中不知道每一位是0還是1,data_out的值將呈現高阻態為8‘dX, //只有后面case語句執行8次將寄存器的每一位都寫入值后,仿真中data_out才有值顯示出來 always@(posedge clk or negedge rst_n)begin if(!rst_n) cnt <= 4'd0; else if(cnt < 9) //如果是發送數據則需要一個標志位去啟動計數器開始計數 cnt <= cnt + 1'b1; else cnt <= 4'd0; end always@(posedge clk or negedge rst_n)begin if(!rst_n) lsm_cnt <= 3'd0; else if(cnt == 9) lsm_cnt <= lsm_cnt + 1'b1; else lsm_cnt <= lsm_cnt; end always@(posedge clk )begin if(cnt == 1) //注意此處的作用是為了防止系統在空閑狀態下還繼續進行發送數據 case(lsm_cnt) 0: data_out[0] <= num[0]; 1: data_out[1] <= num[1]; 2: data_out[2] <= num[2]; 3: data_out[3] <= num[3]; 4: data_out[4] <= num[4]; 5: data_out[5] <= num[5]; 6: data_out[6] <= num[6]; 7: data_out[7] <= num[7]; default : data_out <= 8'd0; endcase // else // data_out <= data_out; end endmodule
仿真代碼:
`timescale 1ns/1ps module counter_tb; reg sys_clk; reg sys_rst_n; wire [7:0] data_out; //例化 counter u_counter( .clk(sys_clk), .rst_n(sys_rst_n), .data_out(data_out) ); initial begin sys_clk = 1'b1; sys_rst_n = 1'b0; #100; sys_rst_n = 1'b1; #900 $stop; end always #10 sys_clk = ~sys_clk; endmodule
非阻塞(non-blocking)賦值方式 ( b<= a): b的值被賦成新值a的操作, 並不是立刻完成的,而是在單個塊結束時才完成; 塊內的多條賦值語句在單個塊結束時同時賦值; 硬件有對應的電路。
阻塞(blocking)賦值方式 ( b = a): b的值立刻被賦成新值a; 完成該賦值語句后才能執行下一句的操作; 硬件沒有對應的電路,因而綜合結果未知。
這里還要特別注意是‘“塊內的多條賦值語句在塊結束時同時賦值”不能考慮成這個時鍾周期末尾到下個時鍾上升沿完成賦值,因為always塊語句的執行時間很短,要不了一個時鍾周期的時間,即真是的賦值是在當前的時鍾上升沿延遲一點點時間就完成了新的賦值操作(即時序仿真)。結合程序這就解釋了,lsm_cnt和data_out寄存器中的值更新為什么延遲了cnt寄存器一個時鍾周期;而cnt寄存器的並未延時一個時鍾周期才改變(實際上cnt的值在上升沿到來后,延遲了一點時間就更新了,所以門級仿真就等效在當前上升沿變化了)。
在不同always塊里對同一個寄存器進行賦值是不被允許的,會發生數據競爭。
如果在一個always塊中調用另一個always塊中的寄存器的某個值作為該always塊的判斷條件時,則該always塊的仿真時序圖會延遲一個時鍾周期才發生變化。代碼如下:
always@(posedge clk or negedge rst_n)begin if(!rst_n) lsm_cnt <= 3'd0; else if(cnt == 9) lsm_cnt <= lsm_cnt + 1'b1; else lsm_cnt <= lsm_cnt; end
lsm_寄存器既可以用於數據發送時,發送到第幾位的依據標志;也可以用於序列機產生時鍾分頻時,分頻時鍾的高低電平變化的依據標志。