我再換種更簡單的描述
二進制數 1 0 1 1 0
二進制數右移1位,空位補0 0 1 0 1 1
異或運算 1 1 1 0 1
這樣就可以實現二進制到格雷碼的轉換了,總結就是移位并且異或,verilog代碼實現就一句:assign wgraynext = (wbinnext>>1) ^ wbinnext;
是不是非常簡單。
三、代碼解析
異步FIFO的信號接口:
wclk wrst_n winc wdata //寫時鐘、寫復位、寫請求、寫數據這幾個與寫有關的全部與wclk同步
rclk rrst_n rinc rdata //讀時鐘、讀 復位、讀 請求、讀 數據 這幾個與讀有關的全部與rclk同步
wfull //寫滿 與wclk同步
rempty // 讀空 與rclk同步
本次代碼共分為6個module
1、fifo.v 是頂層模塊,作用是將各個小模塊例化聯(lián)系起來
module fifo
#(
parameter DSIZE = 8,
parameter ASIZE = 4
)
(
output [DSIZE-1:0] rdata,
output wfull,
output rempty,
input [DSIZE-1:0] wdata,
input winc, wclk, wrst_n,
input rinc, rclk, rrst_n
);
wire [ASIZE-1:0] waddr, raddr;
wire [ASIZE:0] wptr, rptr, wq2_rptr, rq2_wptr;
// synchronize the read pointer into the write-clock domain
sync_r2w sync_r2w
(
.wq2_rptr (wq2_rptr),
.rptr (rptr ),
.wclk (wclk ),
.wrst_n (wrst_n )
);
// synchronize the write pointer into the read-clock domain
sync_w2r sync_w2r
(
.rq2_wptr(rq2_wptr),
.wptr(wptr),
.rclk(rclk),
.rrst_n(rrst_n)
);
//this is the FIFO memory buffer that is accessed by both the write and read clock domains.
//This buffer is most likely an instantiated, synchronous dual-port RAM.
//Other memory styles can be adapted to function as the FIFO buffer.
fifomem
#(DSIZE, ASIZE)
fifomem
(
.rdata(rdata),
.wdata(wdata),
.waddr(waddr),
.raddr(raddr),
.wclken(winc),
.wfull(wfull),
.wclk(wclk)
);
//this module is completely synchronous to the read-clock domain and contains the FIFO read pointer and empty-flag logic.
rptr_empty
#(ASIZE)
rptr_empty
(
.rempty(rempty),
.raddr(raddr),
.rptr(rptr),
.rq2_wptr(rq2_wptr),
.rinc(rinc),
.rclk(rclk),
.rrst_n(rrst_n)
);
//this module is completely synchronous to the write-clock domain and contains the FIFO write pointer and full-flag logic
wptr_full
#(ASIZE)
wptr_full
(
.wfull(wfull),
.waddr(waddr),
.wptr(wptr),
.wq2_rptr(wq2_rptr),
.winc(winc),
.wclk(wclk),
.wrst_n(wrst_n)
);
endmodule
2、fifomem.v 生成存儲實體,FIFO 的本質是RAM,因此在設計存儲實體的時候有兩種方法:用數組存儲數據或者調用RAM的IP核
module fifomem
#(
parameter DATASIZE = 8, // Memory data word width
parameter ADDRSIZE = 4 // 深度為8即地址為3位即可,這里多定義一位的原因是用來判斷是空還是滿,詳細在后文講到
) // Number of mem address bits
(
output [DATASIZE-1:0] rdata,
input [DATASIZE-1:0] wdata,
input [ADDRSIZE-1:0] waddr, raddr,
input wclken, wfull, wclk
);
`ifdef RAM //可以調用一個RAM IP核
// instantiation of a vendor's dual-port RAM
my_ram mem
(
.dout(rdata),
.din(wdata),
.waddr(waddr),
.raddr(raddr),
.wclken(wclken),
.wclken_n(wfull),
.clk(wclk)
);
`else //用數組生成存儲體
// RTL Verilog memory model
localparam DEPTH = 1< reg [DATASIZE-1:0] mem [0:DEPTH-1]; //生成2^4個位寬位8的數組
assign rdata = mem[raddr];
always @(posedge wclk) //當寫使能有效且還未寫滿的時候將數據寫入存儲實體中,注意這里是與wclk同步的
if (wclken && !wfull)
mem[waddr] <= wdata;
`endif
endmodule
3、sync_r2w.v 將 rclk 時鐘域的格雷碼形式的讀指針同步到 wclk 時鐘域,簡單來講就是用兩級寄存器同步,即打兩拍
module sync_r2w
#(
parameter ADDRSIZE = 4
)
(
output reg [ADDRSIZE:0] wq2_rptr, //讀指針同步到寫時鐘域
input [ADDRSIZE:0] rptr, // 格雷碼形式的讀指針,格雷碼的好處后面會細說
input wclk, wrst_n
);
reg [ADDRSIZE:0] wq1_rptr;
always @(posedge wclk or negedge wrst_n)
if (!wrst_n) begin
wq1_rptr <= 0;
wq2_rptr <= 0;
end
else begin
wq1_rptr<= rptr;
wq2_rptr<=wq1_rptr;
end
endmodule
評論
查看更多