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
标签:begin,WIDTH,传输,clk,1ns,牛客,input,reg,时钟 From: https://www.cnblogs.com/magnolia666/p/17180909.html