VL45 異步FIFO
很經典的手撕題,這道題要求產生的格雷碼要在本時鐘域中打一拍,其實不打也沒關系,

主要要記住
1、bin2gray的方法:右移一位與移位前異或;
2、格雷碼比較方法:空:讀指標格雷碼和寫指標同步過來的格雷碼相同;滿:寫指標格雷碼高兩位與讀指標同步過來的格雷碼正好相反,低位相同,
`timescale 1ns/1ns /***************************************RAM*****************************************/ module dual_port_RAM #(parameter DEPTH = 16, parameter WIDTH = 8)( input wclk ,input wenc ,input [$clog2(DEPTH)-1:0] waddr //深度對2取對數,得到地址的位寬, ,input [WIDTH-1:0] wdata //資料寫入 ,input rclk ,input renc ,input [$clog2(DEPTH)-1:0] raddr //深度對2取對數,得到地址的位寬, ,output reg [WIDTH-1:0] rdata //資料輸出 ); reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1]; always @(posedge wclk) begin if(wenc) RAM_MEM[waddr] <= wdata; end always @(posedge rclk) begin if(renc) rdata <= RAM_MEM[raddr]; end endmodule /***************************************AFIFO*****************************************/ module asyn_fifo#( parameter WIDTH = 8, parameter DEPTH = 16 )( input wclk , input rclk , input wrstn , input rrstn , input winc , input rinc , input [WIDTH-1:0] wdata , output wire wfull , output wire rempty , output wire [WIDTH-1:0] rdata ); parameter ADDR_WIDTH = $clog2(DEPTH); reg [ADDR_WIDTH:0]waddr,raddr;//多一位用于比較空滿 always@(posedge wclk or negedge wrstn) begin if(~wrstn) waddr <= 'd0; else if(winc&&~wfull) waddr <= waddr + 1; end always@(posedge rclk or negedge rrstn) begin if(~rrstn) raddr <= 'd0; else if(rinc&&~rempty) raddr <= raddr + 1; end reg [ADDR_WIDTH:0]waddr_gray,raddr_gray;//格雷碼 reg [ADDR_WIDTH:0]waddr_gray_sync1,raddr_gray_sync1;//打一拍后 reg [ADDR_WIDTH:0]waddr_gray_sync2,raddr_gray_sync2;//打兩拍后 /*bin2gray*/ // always@(*) // begin // waddr_gray = (waddr>>1)^waddr; // raddr_gray = (raddr>>1)^raddr; // end always@(posedge wclk or negedge wrstn) begin if(~wrstn) waddr_gray <= 'd0; else waddr_gray <= (waddr>>1)^waddr; end always@(posedge rclk or negedge rrstn) begin if(~rrstn) raddr_gray <= 'd0; else raddr_gray <= (raddr>>1)^raddr; end /*把格雷碼打兩拍傳輸*/ always@(posedge wclk or negedge wrstn) begin if(~wrstn)begin raddr_gray_sync1 <= 'd0; raddr_gray_sync2 <= 'd0; end else begin raddr_gray_sync1 <= raddr_gray; raddr_gray_sync2 <= raddr_gray_sync1; end end always@(posedge rclk or negedge rrstn) begin if(~rrstn)begin waddr_gray_sync1 <= 'd0; waddr_gray_sync2 <= 'd0; end else begin waddr_gray_sync1 <= waddr_gray; waddr_gray_sync2 <= waddr_gray_sync1; end end /*空滿判斷*/ assign wfull = (waddr_gray[ADDR_WIDTH:ADDR_WIDTH-1]==~raddr_gray_sync2[ADDR_WIDTH:ADDR_WIDTH-1])&&(waddr_gray[ADDR_WIDTH-2:0]==raddr_gray_sync2[ADDR_WIDTH-2:0]); assign rempty = (raddr_gray==waddr_gray_sync2); dual_port_RAM #(.WIDTH(WIDTH),.DEPTH(DEPTH)) U0( .wclk(wclk), .wenc(winc&&~wfull), .waddr(waddr[ADDR_WIDTH-1:0]), .wdata(wdata), .rclk(rclk), .renc(rinc&&~rempty), .raddr(raddr[ADDR_WIDTH-1:0]), .rdata(rdata) ); endmodule
VL46 同步FIFO
比異步FIFO簡單多了,不過感覺有問題,滿之后過一個周期才會輸出滿,
`timescale 1ns/1ns /**********************************RAM************************************/ module dual_port_RAM #(parameter DEPTH = 16, parameter WIDTH = 8)( input wclk ,input wenc ,input [$clog2(DEPTH)-1:0] waddr //深度對2取對數,得到地址的位寬, ,input [WIDTH-1:0] wdata //資料寫入 ,input rclk ,input renc ,input [$clog2(DEPTH)-1:0] raddr //深度對2取對數,得到地址的位寬, ,output reg [WIDTH-1:0] rdata //資料輸出 ); reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1]; always @(posedge wclk) begin if(wenc) RAM_MEM[waddr] <= wdata; end always @(posedge rclk) begin if(renc) rdata <= RAM_MEM[raddr]; end endmodule /**********************************SFIFO************************************/ module sfifo#( parameter WIDTH = 8, parameter DEPTH = 16 )( input clk , input rst_n , input winc , input rinc , input [WIDTH-1:0] wdata , output reg wfull , output reg rempty , output wire [WIDTH-1:0] rdata ); parameter ADDR_WIDTH = $clog2(DEPTH); reg [ADDR_WIDTH:0]waddr,raddr;//多一位用于比較空滿 always@(posedge clk or negedge rst_n) begin if(~rst_n) waddr <= 'd0; else if(winc&&~wfull) waddr <= waddr + 1; end always@(posedge clk or negedge rst_n) begin if(~rst_n) raddr <= 'd0; else if(rinc&&~rempty) raddr <= raddr + 1; end /*空滿判斷*/ always@(posedge clk or negedge rst_n) begin if(~rst_n)begin wfull <= 'd0; rempty <= 'd0; end else begin wfull <= (waddr[ADDR_WIDTH]==~raddr[ADDR_WIDTH])&&(waddr[ADDR_WIDTH-1:0]==raddr[ADDR_WIDTH-1:0]); rempty <= (raddr==waddr); end end dual_port_RAM #(.WIDTH(WIDTH),.DEPTH(DEPTH)) U0( .wclk(clk), .wenc(winc&&~wfull), .waddr(waddr[ADDR_WIDTH-1:0]), .wdata(wdata), .rclk(clk), .renc(rinc&&~rempty), .raddr(raddr[ADDR_WIDTH-1:0]), .rdata(rdata) ); endmodule
參考之前看《硬體架構的藝術》寫的代碼【《硬體架構的藝術》讀書筆記】03 處理多個時鐘(2) - Magnolia666 - 博客園 (cnblogs.com)
可以提前一個周期輸出空滿,更合理,
VL47 格雷碼計數器
垃圾題目,每兩個周期加一次,看了一下題解,感覺確實用狀態機更有道理,我這樣寫本質還是一個二進制計數器,
`timescale 1ns/1ns module gray_counter( input clk, input rst_n, output reg [3:0] gray_out ); reg [3:0]bin; reg flag; always@(posedge clk or negedge rst_n) begin if(~rst_n) flag <= 0; else flag <= ~flag; end always@(posedge clk or negedge rst_n) begin if(~rst_n) bin <= 0; else bin <= flag?bin + 1:bin; end always@(*) begin gray_out = (bin>>1)^bin; end endmodule
VL48 多bit MUX同步器
題目大概意思是通過MUX把四位資料從時鐘域a同步到時鐘域b,感覺怪怪的,,,,
看了一下要把資料和使能在時鐘域a打一拍,再把使能信號在時鐘域b打兩拍控制MUX選通data信號,
`timescale 1ns/1ns module mux( input clk_a , input clk_b , input arstn , input brstn , input [3:0] data_in , input data_en , output reg [3:0] dataout ); reg [3:0]data_in_rega; reg data_en_rega; always@(posedge clk_a or negedge arstn) begin if(~arstn)begin data_en_rega <= 0; data_in_rega <= 0; end else begin data_en_rega <= data_en; data_in_rega <= data_in; end end reg data_en_regb1; reg data_en_regb2; always@(posedge clk_b or negedge brstn) begin if(~brstn)begin data_en_regb1 <= 0; data_en_regb2 <= 0; end else begin data_en_regb1 <= data_en_rega; data_en_regb2 <= data_en_regb1; end end always@(posedge clk_b or negedge brstn) begin if(~brstn)begin dataout <= 0; end else begin dataout <= data_en_regb2?data_in_rega:dataout; end end endmodule
VL49 脈沖同步電路
邸志雄的課里看到過,參考芯動力——硬體加速設計方法_西南交通大學_中國大學MOOC(慕課) (icourse163.org)

`timescale 1ns/1ns module pulse_detect( input clk_fast , input clk_slow , input rst_n , input data_in , output dataout ); reg toggle; always@(posedge clk_fast or negedge rst_n) begin if(~rst_n) toggle <= 1'b0; else toggle <= data_in?(~toggle):toggle; end reg data_syn0,data_syn1,data_syn2; always@(posedge clk_slow or negedge rst_n) begin if(~rst_n)begin data_syn0 <= 1'b0; data_syn1 <= 1'b0; data_syn2 <= 1'b0; end else begin data_syn0 <= toggle; data_syn1 <= data_syn0; data_syn2 <= data_syn1; end end assign dataout = data_syn2^data_syn1; endmodule
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/545839.html
標籤:其他
