【IC每日一题:CDC多bits跨时钟传输+handShake】

IC每日一题:CDC多bits跨时钟传输+handShake

  • 1 八股题:CDC多bits跨时钟传输
    • 1.1 慢到快:MUX enbale同步器法
      • 1.1.1 分析
      • 1.1.2 时序图
      • 1.1.3 代码
    • 1.2 快到慢:握手协议
      • 1.2.1 原理分析
      • 1.2.2 时序图
      • 1.2.3 代码题
  • 2 手撕题:使用握手信号实现跨时钟数据传输问题
    • 2.1 步骤
    • 2.2 波形图
    • 2.2 代码

1 八股题:CDC多bits跨时钟传输

上篇文章介绍CDC单bit跨时钟传输的解决办法:

  • 当慢到快:采用两级同步打拍;
  • 当快到慢:1.信号展宽+边沿检测;2.握手(request + ack);
    本篇文章介绍CDC多bits跨时钟数据传输的解决办法;
  • 慢到快:MUX同步器法
  • 快到慢:握手协议
  • 异步FIFO通杀:放在另一个专题博客中;

1.1 慢到快:MUX enbale同步器法

1.1.1 分析

分 析:慢到快这种情况在快时钟接收端是一定能够采样得到的,多bits数据在传输过程中有多位同时变化,那么有什么解决办法呢?解决办法是在传输过程中不变化!所以必须在写入使能信号有效时传输!
解决办法:传输非同步数据到接收时钟域时配上一个同步的控制信号,数据和控制信号被同时发送到接收时钟域,同时控制信号在接收时钟域使用两级寄存器同步到接收时钟域,使用此同步后的控制信号来加载数据,这样数据就可以在目的寄存器被安全加载。


1.1.2 时序图

dout_en多打几拍din_en,这样能够保持din这段时间不变化;
在这里插入图片描述

1.1.3 代码

//==========================================================
//--Author  : colonel
//--Date    : 11-01
//--Module  : sync_multi_bits_slow_2_fast
//--Function: sync multi bits from slow_clk to fast_clk using MUX
//--            Only considering the yaWenTai;
//==========================================================
module sync_multi_bits_slow_2_fast (
//==========================< 端口 >=========================
    input wire              slow_clk,
    input wire              rst_n,
    input wire              din_en,
    input wire [32 -1:0]    din,

    input wire              fast_clk,
    output wire             dout_en,
    output wire [32 -1:0]   dout
);
//==========================< 信号 >=========================
reg din_en_r1;
reg din_en_r2;
reg din_en_r3;

//=========================================================
//-- din_en_r1/r2/r3: sync din_en to fast_clk
//=========================================================
always @(posedge fast_clk or negedge rst_n) begin
    if (!rst_n) begin
        din_en_r1 <= 1'b0;
        din_en_r2 <= 1'b0;
        din_en_r3 <= 1'b0;
    end else begin
        din_en_r1 <= din_en;
        din_en_r2 <= din_en_r1;
        din_en_r3 <= din_en_r2;
    end
end

wire din_en_pos = din_en_r2 && !din_en_r3;

//==========================< 信号 >=========================
reg [32 -1:0]   dout_r;
reg             dout_en_r;

//=========================================================
//-- dout_r, dout_en_r
//=========================================================
always @(posedge fast_clk or negedge rst_n) begin
    if (!rst_n) begin
        dout_r <= 'b0;
    end else if(din_en_pos) begin
        dout_r <= din;
    end else begin
        dout_r <= dout_r;
    end
end

always @(posedge fast_clk or negedge rst_n) begin
    if (!rst_n) begin
        dout_en_r <= 'b0;
    end else begin
        dout_en_r <= din_en_pos;
    end
end

//=========================================================
//-- dout,dout_en
//=========================================================
assign dout_en  = dout_en_r;
assign dout     = dout_r;


endmodule

1.2 快到慢:握手协议

快到慢涉及考虑漏采的风险,在多bits传输过程中,这里采用握手的方式;

1.2.1 原理分析

完全握手的特点是发送域和接收域两端在发送请求或者终止请求之前都需要等待对端的回应。完全握手在收发两侧都使用的是电平同步器。
在这里插入图片描述

1.2.2 时序图

在这里插入图片描述

对时序图解释:
用前缀t_表示发送域,用前缀r_表示接收域,发送时钟用t_clk表示,接收时钟用r_clk表示。数据由发送域向接收域传输;以下是完全握手的工作步骤:

1.当发送域数据准备好后,发送域将t_req信号置为有效;
2.接收域采用两级电平同步器同步t_req信号,并把同步后的信号命名为r_req_sync2;
3.接收域检测到t_req_sync2信号有效时, 在下一个r_clk周期中,接收域将r_ack信号置为有效,并使用r_clk对发送域数据进行采样,可以得到r_data_out;
4.发送域采用两级电平同步器同步r_ack信号,并把同步后的信号命名为t_ack_sync2;
5.发送域检测到t_ack_sync2信号有效时,接收域将t_req信号置为无效;
6.接收域在经过两个r_clk周期后检测到r_req_sync2信号无效;
7.在下一个r_clk周期中,接收域将r_ack置为无效
8.发送域在通过两级电平同步器后检测到r_ack信号无效时,完成一次完全握手,发送域可以输入新的数据。
   这里,全部握手在发送域中最多需要5个周期,在接收域中最多需要6个周期。
  全握手协议在异步数据跨时钟的安全性方面很强,因为通过检测请求与响应信号,每一侧电路都清楚地知道对端的状态。但是这种方式也有其不足,那就是完成所有交互的整个过程要花费很多个时钟周期,数据信号跨时钟的延时比较大。

1.2.3 代码题

根据上述时序图描述进行写代码,逻辑很清晰;
根据上述时序图进行写个握手协议;

//==========================================================
//--Author  : colonel
//--Date    : 11-01
//--Module  : sync_multi_bits_fast_2_slow
//--Function: sync multi bits from slow_clk to fast_clk using MUX
//--            Only considering the yaWenTai;
//==========================================================
module sync_multi_bits_fast_2_slow(
//==========================< 端口 >=========================
    input wire t_clk,
    input wire rst_n,
    input wire t_din_en,
    input wire [32 -1:0] t_din,
    output wire o_t_ready,

    input wire  r_clk,
    output wire r_dout_en,
    output wire [32 -1:0] r_dout
);
//==========================< 端口 >=========================
reg t_req;
reg r_req_sync1;
reg r_req_sync2;
reg r_ack;

//=========================================================
//-- t_req, r_req_sync1/2
//=========================================================
always @(posedge t_clk or negedge rst_n) begin
    if (!rst_n) begin
        t_req <= 1'b0;
    end else begin
        if(t_din_en) begin
            t_req <= 1'b1;
        end else if (t_ack_sync2) begin
            t_req <= 1'b0;
        end else begin
            t_req <= t_req;
        end
    end
end

always @(posedge r_clk or negedge rst_n) begin
    if (!rst_n) begin
        r_req_sync1 <= 1'b0;
        r_req_sync2 <= 1'b0;
    end else begin
        r_req_sync1 <= t_req;
        r_req_sync2 <= r_req_sync1;
    end
end

always @(posedge r_clk or negedge rst_n) begin
    if (!rst_n) begin
        r_ack <= 1'b0;
    end else begin
        r_ack <= r_req_sync2;
    end
end

//==========================< 端口 >=========================
reg r_dout_en_r;
reg r_dout_r;
wire r_req_sync_2_pos = r_req_sync2 & !r_ack;

//=========================================================
//-- r_dout_en_r,r_dout_r
//=========================================================
always @(posedge r_clk or negedge rst_n) begin
    if (!rst_n) begin
        r_dout_en_r <= 1'b0;
    end else begin
        if (r_req_sync_2_pos) begin
            r_dout_en_r <= 1'b1;
        end else begin
            r_dout_en_r <= 1'b0;
        end
    end
end

always @(posedge r_clk or negedge rst_n) begin
    if (!rst_n) begin
        r_dout_r    <= 'b0;
    end else begin
        if (r_req_sync2) begin
            r_dout <= t_din;
        end else begin
            r_dout <= r_dout;
        end
    end
end

//=========================================================
//-- r_dout_en,r_dout
//=========================================================
assign r_dout_en= r_dout_en_r;
assign r_dout   = r_dout_r;

//==========================< 端口 >=========================
reg t_ack_sync1;
reg t_ack_sync2;

//=========================================================
//-- t_ack_sync1/2
//=========================================================
always @(posedge t_clk or negedge rst_n) begin
    if (!rst_n) begin
        t_ack_sync1 <= 1'b0;
        t_ack_sync2 <= 1'b0;
    end else begin
        t_ack_sync1 <= r_ack;
        t_ack_sync2 <= t_ack_sync1;
    end
end

//==========================< 端口 >=========================
reg o_t_ready_r;
wire t_ack_sync2_neg = t_ack_sync2 & ~t_ack_sync1;

//=========================================================
//-- o_t_ready_r
//=========================================================
always @(posedge t_clk or negedge rst_n) begin
    if (!rst_n) begin
        o_t_ready_r <= 1'b1;
    end else begin
        if (t_req) begin
            o_t_ready_r <= 1'b0;
        end else if (t_ack_sync2_neg) begin
            o_t_ready_r <= 1'b1;
        end else begin
            o_t_ready_r <= o_t_ready_r;
        end
    end
end

assign o_t_ready = o_t_ready_r;

endmodule

2 手撕题:使用握手信号实现跨时钟数据传输问题

题目:分别编写一个数据发送模块和一个数据接收模块,模块的时钟信号分别为clk_a,clk_b。两个时钟的频率不相同。数据发送模块循环发送0-7,在每个数据传输完成之后,间隔5个时钟,发送下一个数据。请在两个模块之间添加必要的握手信号,保证数据传输不丢失。
模块的接口信号如下:
在这里插入图片描述

2.1 步骤

信号进一步描述:

  • data_req:data_req表示数据请求接受信号。当data_out发出时,该信号拉高,在确认数据被成功接收之前,保持为高,期间data应该保持不变,等待接收端接收数据。
    当数据发送端检测到data_ack,表示上一个发送的数据已经被接收。撤销data_req,然后可以改变数据data。等到下次发送时,再一次拉高data_req。
  • data_ack:当数据接收端检测到data_req为高,表示该时刻的信号data有效,保存数据,并拉高data_ack.
  • 当数据发送端检测到data_ack,表示上一个发送的数据已经被接收。撤销data_req,然后可以改变数据data。等到下次发送时,再一次拉高data_req。

2.2 波形图

在这里插入图片描述

2.2 代码

//==========================================================
//--Author  : colonel
//--Date    : 11-01
//--Module  : sync_multi_bits_using_handShake
//--Function: sync multi bits of newcoder  
//==========================================================
module data_driver (
//==========================< 端口 >=========================
    input clk_a,
    input rst_n,
    input data_ack,
    output [4 -1:0] data,
    output data_req
);
//==========================< 信号 >=========================
reg data_ack_sync1;
reg data_ack_sync2;
    
//=========================================================
//-- data_ack_sync1/2:sync data_ack to clk_a
//=========================================================
always @(posedge clk_a or negedge rst_n) begin
    if (!rst_n) begin
        data_ack_sync1 <= 1'b0;
        data_ack_sync2 <= 1'b0;
    end else begin
        data_ack_sync1 <= data_ack;
        data_ack_sync2 <= data_ack_sync1;
    end
end

wire data_ack_sync1_pos = data_ack_sync1 && !data_ack_sync2;    

//==========================< 信号 >=========================
reg [4 -1:0] data_r;

//=========================================================
//-- data: can change when ack is ok
//=========================================================
always @(posedge clk_a or negedge rst_n) begin
    if (!rst_n) begin
        data_r <= 0;
    end else begin
        if (data_ack_sync1_pos) begin
            data_r <= data_r + 1;
        end else begin
            data_r <= data_r;
        end
    end
end

assign data = data_r;

//==========================< 信号 >=========================
reg [3 -1:0] cnt;

//=========================================================
//-- cnt: cnt is 0 when ack is ok,and keep when data_req 
//=========================================================
always @(posedge clk_a or negedge rst_n) begin
    if (!rst_n) begin
        cnt <= 0;
    end else begin
        if (data_ack_sync1_pos) begin
            cnt <= 0;
        end else if (data_req) begin
            cnt <= cnt;
        end else begin
            cnt <= cnt + 1;
        end
    end
end

//==========================< 信号 >=========================
reg data_req_r;

//=========================================================
//-- data_req_r: when is 1'b1 and when is 1'b0
//=========================================================
always @(posedge clk_a or negedge rst_n) begin
    if (!rst_n) begin
        data_req_r <= 1'b0;
    end else begin
        if (data_ack_sync1_pos) begin
            data_req_r <= 1'b0;
        end else if (cnt==3'd4) begin
            data_req_r <= 1'b1;
        end else begin
            data_req_r <= data_req_r;
        end
    end
end

assign data_req = data_req_r;

endmodule



module data_receiver (
//==========================< 端口 >=========================
    input clk_b,
    input rst_n,
    input data_req,
    input [4 -1:0] data,

    output data_ack,
    output data_sync
);
//==========================< 端口 >=========================
reg data_req_sync1;
reg data_req_sync2;

//=========================================================
//-- data_req_sync1/2
//=========================================================
always @(posedge clk_b or negedge rst_n) begin
    if (!rst_n) begin
        data_req_sync1 <= 1'b0;
        data_req_sync2 <= 1'b0;
    end else begin
        data_req_sync1 <= data_req;
        data_req_sync2 <= data_req_sync1;
    end
end

wire data_req_sync1_pos = data_req_sync1 && !data_req_sync2;

//==========================< 端口 >=========================
reg data_ack_r;

//=========================================================
//-- data_ack_r
//=========================================================
always @(posedge clk_b or negedge rst_n) begin
    if (!rst_n) begin
        data_ack_r <= 1'b0;
    end else begin
        if (data_req_sync1_pos) begin
            data_ack_r <= 1'b1;
        end else begin
            data_ack_r <= 1'b0;
        end
    end
end

//==========================< 端口 >=========================
reg [4 -1:0] data_sync_r;

always @(posedge clk_b or negedge rst_n) begin
    if (!rst_n) begin
        data_sync_r <= 'b0;
    end else begin
        if (data_req_sync1_pos) begin
            data_sync_r <= data;
        end else begin
            data_sync_r <= data_sync_r;
        end
    end
end

assign data_sync = data_sync_r;
    
endmodule


这是一个非常好的练习 handshake的好练习例子

【Ref】

  1. https://blog.csdn.net/Loudrs/article/details/131076559
  2. https://blog.csdn.net/qq_32355037/article/details/124571258?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168612550516800222811622%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=168612550516800222811622&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-124571258-null-null.142%5Ev88%5Ekoosearch_v1,239%5Ev2%5Einsert_chatgpt&utm_term=%E5%A4%9AbitCDC&spm=1018.2226.3001.4187
    3.https://www.nowcoder.com/practice/2bf1b28a4e634d1ba447d3495134baac?tab=note

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/908814.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何将MySQL彻底卸载干净

目录 背景&#xff1a; MySQL的卸载 步骤1&#xff1a;停止MySQL服务 步骤2&#xff1a;软件的卸载 步骤3&#xff1a;残余文件的清理 步骤4&#xff1a;清理注册表 步骤五:删除环境变量配置 总结&#xff1a; 背景&#xff1a; MySQL卸载不彻底往往会导致重新安装失败…

完美解决 vscode 多设备通过ssh登录远程服务器免密登录

文章目录 问题解决分支一&#xff1a;只需一个设备连服务器分支二&#xff1a;需要多台设备连服务器&#xff08;即分支一成立的情况下&#xff0c;即已经有一个设备连接服务器了&#xff09;确保权限正确注意 问题 麻烦&#xff0c;每次打开vscode都要输入密码才能连接服务器&…

mac|安装redis及RedisDesk可视化软件

一、安装 通过Homebrew安装 brew install redis 在安装过程可以得到以下信息&#xff1a; 1、启动redis或重新登陆redis brew services start redis 如果只想在前端运行&#xff0c;而不是在后端&#xff0c;则使用以下命令 /opt/homebrew/opt/redis/bin/redis-server /opt…

大学适合学C语言还是Python?

在大学学习编程时&#xff0c;选择C语言还是Python&#xff0c;这主要取决于你的学习目标、专业需求以及个人兴趣。以下是对两种语言的详细比较&#xff0c;帮助你做出更明智的选择&#xff1a; C语言 优点&#xff1a; 底层编程&#xff1a;C语言是一种底层编程语言&#x…

3大关键点教你用Java和Spring Boot快速构建微服务架构:从零开发到高效服务注册与发现的逆袭之路

你好&#xff0c;我是忆~遂愿&#xff0c;全网3w粉丝&#xff0c;《遂愿盈创》社群主理人。 副业启航① | 遂愿盈创&#xff08;对副业感兴趣免费可入&#xff0c;多种赚钱实战项目等你来&#xff0c;一起探寻副业快速变现的途径&#xff1b;以及对接互联网大厂商务合作&#x…

ubuntu下使用pocketsphinx进行语音识别

文章目录 前言一、pocketsphinx的介绍二、ubuntu下编译三、使用示例1.模型选择2.代码示例3.自定义字典 四、交叉编译总结 前言 由于工作需要语音识别的功能&#xff0c;环境是在linux arm版上&#xff0c;所以想先在ubuntu上跑起来看一看&#xff0c;就找了一下语音识别的开源…

Redis的持久化以及性能管理

目录 一、Redis持久化概述 1.什么是Redis持久化 2.持久化方式 3.RDB持久化 3.1概念 3.2触发条件 3.3执行流程 3.4启动时加载 4. AOF持久化 4.1概念 4.2启动AOF 4.3执行流程 4.4启动时加载 5.RDB和AOF的优缺点 二、Redis性能管理 1.查看Redis内存使用 2…

科研绘图系列:R语言组合连线图和箱线图(linechart+boxplot)

文章目录 介绍加载R包数据数据预处理画图1画图2系统信息介绍 连线图(Line Chart)是一种常用的数据可视化图表,它通过将一系列数据点用直线段连接起来来展示数据随时间或有序类别变化的趋势。以下是连线图可以表示的一些内容: 时间序列数据:展示数据随时间变化的趋势,例如…

Webserver(3.1)线程

目录 创建线程终止线程连接已终止的进程二级指针 线程分离线程取消线程属性线程同步多线程卖票 创建线程 编译时需要加-pthread gcc pthread_create.c -o create -pthread#include<pthread.h> #include<stdio.h> #include<string.h> #include<unistd.h&…

WPS实现手机电脑同步文件

复制链接&#xff0c;发送微信&#xff0c;即可在手机端打开同步编辑文档。 当手机编辑文档后&#xff0c;会有如下提示: 根据提示&#xff0c;可选择文件更新版本。

ES海量数据插入如何优化性能?

2024年10月NJSD技术盛典暨第十届NJSD软件开发者大会、第八届IAS互联网架构大会在南京召开。百度文心快码总经理臧志分享了《AI原生研发新范式的实践与思考》&#xff0c;探讨了大模型赋能下的研发变革及如何在公司和行业中落地&#xff0c;AI原生研发新范式的内涵和推动经验。 …

【linux】HTTPS 协议原理

1. 了解 HTTPS 协议原理 &#xff08;一&#xff09;认识 HTTPS HTTPS 也是一种应用层协议&#xff0c;是在 HTTP 协议的基础上引入了一个加密层 因为 HTTP协议的内容都是按照文本的方式进行传输的&#xff0c;这个过程中&#xff0c;可能会出现一些篡改的情况 &#xff08;…

labview学习总结

labview学习总结 安装labview的特点一、图形化编程范式二、并行执行机制三、硬件集成能力四、应用领域优势五、开发效率六、系统集成能力**labview基本组成示意图****常用程序结构图解**结语 基础知识介绍界面前后面板的概念平铺式和层叠式 帧的概念结构类型顺序结构for循环whi…

PostgreSQL技术内幕17:PG分区表

文章目录 0.简介1.概念介绍2.分区表技术产生的背景3.分区类型及使用方式4.实现原理4.1 分区表创建4.2 分区表查询4.3 分区表写入4.4 分区表删除 0.简介 本文主要介绍PG中分区表的概念&#xff0c;产生分区表技术的原因&#xff0c;使用方式和其内部实现原理&#xff0c;旨在能…

RHCSA课后练习3(网络与磁盘)

1、配置网络&#xff1a;为网卡添加一个本网段IPV4地址&#xff0c;x.x.x.123 涉及的知识点 配置网络&#xff1a; ens160&#xff1a;en---表示以太网 wl---表示无线局域网 ww---表示无线广域网 注意&#xff1a;一个网络接口&#xff0c;可以有多个网络连接&#xff0c;但…

开发人员需要知道的 20个Git命令行技巧

前言 大多数开发人员每天都会使用 Git&#xff0c;但许多人只是对其功能略知一二。 学习一些 git 命令行技巧可以改变游戏规则&#xff0c;让你更高效、更有成效&#xff0c;对版本控制更有信心。 那么&#xff0c;让我们深入了解每个开发人员工具包中都应该有的 20 个 Git …

第十一章 综合案例--“精品课程网站“开发

1.网站的开发流程 网站开发流程通常分为几个关键阶段&#xff0c;每个阶段都有其特定的任务和目标。以下是一个典型的网站开发流程&#xff1a; 1. 需求分析 目标设定&#xff1a;明确网站的目标和目的。 受众研究&#xff1a;确定目标用户&#xff0c;了解他们的需求和偏好。…

VSCode 1.82之后的vscode server离线安装

概述 因为今天在公司开发项目的时候&#xff0c;需要离线配置vscode远程开发环境&#xff0c; 根据参考链接1配置了一遍&#xff0c;不管怎么重启&#xff0c;VSCODE都还是提示下载vscode server&#xff0c;后面在官方issue上找到了解决方案 解决方案 修改Remote SSH的配置…

Linux和,FreeRTOS 任务调度原理,r0-r15寄存器,以及移植freertos(一)

目录、 1、r0-r15寄存器&#xff0c;保护现场&#xff0c;任务切换的原理 2、freertos移植 3、freertos的任务管理。 一、前言 写这篇文章的目的&#xff0c;是之前面试官&#xff0c;刚好问到我&#xff0c;移植FreeRTOS 到mcu&#xff0c;需要做哪些步骤&#xff0c;当时回…

「Mac畅玩鸿蒙与硬件28」UI互动应用篇5 - 滑动选择器实现

本篇将带你实现一个滑动选择器应用&#xff0c;用户可以通过滑动条选择不同的数值&#xff0c;并实时查看选定的值和提示。这是一个学习如何使用 Slider 组件、状态管理和动态文本更新的良好实践。 关键词 UI互动应用Slider 组件状态管理动态数值更新用户交互 一、功能说明 在…