草庐IT

【牛客】6 跨时钟域传输

magnolia666 2023-03-28 原文

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

 

有关【牛客】6 跨时钟域传输的更多相关文章

  1. 牛客网专项练习30天Pytnon篇第02天 - 2

    1.在Python3中,下列关于数学运算结果正确的是:(B)a=10b=3print(a//b)print(a%b)print(a/b)A.3,3,3.3333...B.3,1,3.3333...C.3.3333...,3.3333...,3D.3.3333...,1,3.3333...解析:    在Python中,//表示地板除(向下取整),%表示取余,/表示除(Python2向下取整返回3)2.如下程序Python2会打印多少个数:(D)k=1000whilek>1:    print(k)k=k/2A.1000 B.10C.11D.9解析:    按照题意每次循环K/2,直到K值小于等

  2. ruby - 使用 Ruby 将 HTTP GET 的响应主体流式传输到 HTTP POST - 2

    我正在尝试下载一个大文件,然后使用Ruby将该文件发布到REST端点。该文件可能非常大,即超过可以存储在内存中甚至磁盘上的临时文件中的容量。我一直在用Net::HTTP尝试这个,但我愿意接受任何其他库(rest-client等)的解决方案,只要他们做我想做的事情。这是我尝试过的:require'net/http'source_uri=URI("https://example.org/very_large_file")source_request=Net::HTTP::Get.new(source_uri)source_http=Net::HTTP.start(source_uri.ho

  3. ruby-on-rails - 将 Heroku 环境变量传输到 Docker 实例 - 2

    我在Heroku上构建了一个必须在Docker容器内运行的RoR应用程序。为此,我使用officialDockerfile.因为它在Heroku中很常见,所以我需要一些附加组件才能使这个应用程序完全运行。在生产中,变量DATABASE_URL在我的应用程序中可用。但是,如果我尝试其他一些使用环境变量(在我的例子中是Mailtrap)的加载项,变量不会在运行时复制到实例中。所以我的问题很简单:如何让docker实例在Heroku上执行时知道环境变量?您可能会问,我已经知道我们可以在docker-compose.yml中指定一个environment指令。我想避免这种情况,以便能够通过项目

  4. FPGA 之 时钟,时钟域, 以及复位系统的设计 - 2

    FPGA时钟和时钟域时钟树所谓时钟树为FPGA内部资源,分:全局时钟树,区域时钟树,IO时钟树原则上优先使用全局时钟树,在GT接口上使用IO时钟树,一般工具也会对GT时钟加以限制;时钟树使用方式正确的物理连接FPGA会由物理管脚专门用于全局时钟设置,通过查询数据手册可以在PCB设计阶段进行确认,当外部时钟接入此管脚时,工具会自动占有全局时钟树资源,当接入普通信号时不会分配时钟树资源;恰当的代码描述原语的使用,即BUFG的使用,可以将PLL的输出等内部时钟进行全局时钟资源的分配;IO时钟资源需要参考相应接口手册,以ultrascale的GTH为例,其JESD204的时钟方案针对不同的子类会由不同

  5. ruby - Capistrano 3.0 文件传输? - 2

    似乎Capistrano曾经有anextensivefiletransferpackage.但是在3.0版本重写后好像没有了。知道是否还有其他方法可以将文件传输到服务器/从服务器传输文件吗? 最佳答案 下载:desc"downloadfoobar.txtintolocal_dir/"task:foobardodownload!"some_remote_path/foobar.txt","local_dir/"end我知道这从Cap3.2.1开始有效,因为我现在正在使用它。 关于ruby-

  6. ruby-on-rails - 如何将数据从我的生产数据库传输到 heroku 中的登台数据库? - 2

    我正在尝试将数据从我的生产数据库传输到我的登台数据库,但没有成功。我正在关注heroku的相关文档:http://devcenter.heroku.com/articles/pgbackups#transfers这些是我运行的命令...$herokuaddons:addpgbackups--remotestaging$herokuaddons:addpgbackups--remoteproduction$herokupgbackups:capture--remoteproduction$herokupgbackups:restoreDATABASE`herokupgbackups:ur

  7. ruby - Ruby 和 Net::SCP 传输(套接字)的性能问题 - 2

    从命令行scp实用程序的功能来看,SCP上传速度似乎在库中受到很大限制。我知道这是Ruby(1.9.2-p0),但是Net::SCP比Linux实用程序慢大约8倍(使用大文件看到...见下文)。我很好奇知道(我快速查看了代码)这是否是Ruby中套接字的方式,或者是否可以更好地多路复用Net::SCP套接字?我注意到无论我尝试哪种上传方式(串行上传、异步操作channel、使用scp对象的多个实例)我都无法在SCP上传上获得超过9兆字节/秒的传输速度。现在……让我解释一下我调查的细节:1)尝试不同的加密算法我使用了不同类型的加密,速度没有太大变化示例:我可以使用命令行scp(加密算法=a

  8. ruby - 从 Amazon S3 流式传输动态 zip - 2

    我正在寻找一种从AmazonS3动态流式下载zip文件的方法。应用程序托管在EC2上,文件存储在S3上。需要让用户能够从一组文件中进行选择,然后将这些文件打包并下载给他们。听说过一些可能可行的Actionscript库(aszip和fzip),或者可以在Ruby或什至PHP中执行此操作。文件不需要任何压缩,zip只是用于将文件捆绑到一个下载中.... 最佳答案 我使用NginxZipModule流式传输本地文件,但可以选择从远程位置流式传输。否则,您可以将它与VFS安装的S3存储一起用作本地文件系统。支持seek-断点续传和加速下载

  9. 基于51单片机、DS1302时钟模块的电子闹钟设计 - 2

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录一、设计原理1.DS1302介绍2.闹钟音乐播放原理二、程序设计1.DS1302.h2.ds1302.c3.music.h4.main.c三、电路图四、运行结果1.proteus仿真2.开发板实验五、总结六、附件提示:以下是本篇文章正文内容,下面案例可供参考一、设计原理1.DS1302介绍DS1302是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.0V~5.5V。该芯片采用普通32.768kHz晶振,DS1302工作时功耗很

  10. ruby-on-rails - Curl::Error::Partial File Error: 传输部分文件错误 - 2

    我正在尝试在Rails控制台中执行以下代码:ce=Curl::Easy.new("http://www.homestolove.com.au/bathroom-profile-fresh-approach-2391")ce.verbose=truece.perform但我收到以下错误。谁能建议如何解决这个问题?看起来这个url返回响应的速度很慢。在文件传输完成之前,我们可以做些什么来阻止连接终止吗?*Addinghandle:conn:0x95f3210*Addinghandle:send:0*Addinghandle:recv:0*Curl_addHandleToPipeline:l

随机推荐