首页 > 其他分享 >【牛客】6 跨时钟域传输

【牛客】6 跨时钟域传输

时间:2023-03-05 20:47:16浏览次数:42  
标签:begin WIDTH 传输 clk 1ns 牛客 input reg 时钟

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

相关文章

  • 洛谷P1213 [USACO1.4][IOI1994]时钟 The Clocks
    这是一个暴力枚举题有两种解决方法,第一种用九重for循环(有点麻烦,尽量别用),第二种简化版(虽然行数少了,但难理解),先来看看 题目!!!题目描述考虑将如此安排在一个 3*3 行......
  • 串口传输和校验的两种方法
    一、累加和取反加一1/*2*取反加一原理:3*0xFE(0b11111110)4*取反得0x01(0b00000001)5*二者相加得0xFF(0b11111111)6*再加一得0x00(0b00000000)7......
  • 流量分析一之数据包传输过程
    封装与解封装封装报文是从上层到下层(应用层-->传输层-->网络层-->数据链路层-->物理层),解封装报文是从下层到上层。应用数据需要经过TCP/IP每一层处理后才能通过网络传......
  • 常见传输数据格式Content-Type
    在响应中,Content-Type标头告诉客户端实际返回的内容的内容类型。浏览器会在某些情况下进行MIME查找,并不一定遵循此标题的值;为了防止这种行为,可以将标题X-Content-Ty......
  • Linux时钟子系统分析
    梦开始的地方X86硬件时钟首先我们需要了解一下,目前有哪些时钟PITpit是最古老的pc时钟设备。Intel8253/8254PIT是具有3个16位计数器通道的可编程计数/定时器芯片,晶振......
  • 牛客网 SQL 83-95
    SQL83确定已售出产品的总数selectsum(quantity)asitems_orderedfromOrderItems;SQL84确定已售出产品项BR01的总数selectsum(quantity)asitems_orderedfro......
  • 牛客-NOIP2000-进制转换
    题目链接:https://ac.nowcoder.com/acm/contest/19305/1028#include<iostream>usingnamespacestd;intmain(){ intn; cin>>n; intr; cin>>r; inta[20]......
  • 牛客小白月赛67—— 一刀二分三角(数学)
    https://ac.nowcoder.com/acm/contest/51458/C题目大意:给定一个三角形,三个点分别是(0,0)(xc,yc)(xb,0)。​问我们是否可以将三角形沿着x=某个数字切开,得到的两个平面图形面......
  • 牛客网 SQL 195-204
    195:请你查找employees里最晚入职员工的所有信息select*fromemployeeswherehire_date=(selectmax(hire_date)fromemployees);解题思路:使用子查询的方法,先找出......
  • iPhone与win10传输大文件,使用局域网
    使用微信、qq之类的只能传输小文件,毕竟这个不是局域网。最快的方式,iPhone直接使用数据线连接win10传文件第二快的方式,iPhone与win10连接的同一个路由器,通过局域网建立连接,然......