08 SCLSDA,类IIC协议(附源码)
虚拟机:VMware -14.0.0.24051
环 境:ubuntu 18.04.1
脚 本:makefile(点击直达)
应用工具:vcs 和 verdi
文章目录
- 一、Overview
- (1)Theory
- (2)Demand
- 二、Interface Description
- 三、Timeing
- (1)M1-Timing
- (2)M2-Timing
- 四、Design and Functional Verification
- (1)M1-RTL
- (2)M1-Test Bench
- (3)M2-RTL
- (4)M2-Test Bench
- 五、Result
- (1)M1
- (2)M2
- (3)Top
一、Overview
(1)Theory
虽然使用了SCL、SDA和ACK,但这真的不是IIC协议呀,这个模块功能是实现并行数据流转换为一种特殊串行数据流模块的设计。
- 模块框图:
- 通信协议:
- 模块时序:
(2)Demand
- 设计两个可综合的电路模块:
第一个模块(M1)能把4位的并行数据转换为符合以下协议的串行数据流,数据流用scl和sda两条线传输,sclk为输入的时钟信号,data[3:0]为输入数据,ack为数据输入的使能信号。
第二个模块(M1)能把串行数据流内的信息接收到,并转换为相应16条信号线的高电平,即若数据为1,则第一条线路为高电平,数据为n,则第N条线路为高电平。
M0为测试用信号模块。该模块接收M1发出的ack信号,产生新的测试数据data[3::0]。 - 通信协议:
scl为不断输出的时钟信号,如果scl为高电平时,sda由高变低时刻,串行数据流开始;如果scl为高电平时,sda由低变高,申行数据结束。sda信号的串行数据位必须在scl为低电平时变化,若变为高则为1,否则为零。
二、Interface Description
Signal Name | Width | Direction | Description |
---|---|---|---|
sclk | 1 | input | System clk signal, xxMhz |
rst_n | 1 | input | System reset signal |
data_i | 4 | input | Sent data bus |
ack | 1 | output | Request send signal |
scl | 1 | output | Two divided-frequency signal |
sda | 1 | output | Data signal |
outhigh | 16 | output | Out signal |
三、Timeing
(1)M1-Timing
(2)M2-Timing
四、Design and Functional Verification
(1)M1-RTL
//-- modified by xlinxdu, 2022/05/06
module ptosda (input sclk_i ,input rst_n_i,input [3:0] data_i ,output reg ack_o ,output reg scl_o ,output reg sda_o
);parameter IDLE = 7'b000_0001;
parameter START = 7'b000_0010;
parameter BIT1 = 7'b000_0100;
parameter BIT2 = 7'b000_1000;
parameter BIT3 = 7'b001_0000;
parameter BIT4 = 7'b010_0000;
parameter STOP = 7'b100_0000;reg sda_en;
reg [3:0] data_buf;
reg [6:0] cur_state;
reg [6:0] nxt_state;/*-----------------------------------------------\------------ set flag --> sda_en ---------------
\-----------------------------------------------*/
always @ (posedge sclk_i or negedge rst_n_i) beginif (!rst_n_i) beginsda_en <= 1'b0;endelse if (ack_o) beginsda_en <= 1'b1;endelse if((cur_state == STOP) && (ack_o == 1'b0)) beginsda_en <= 1'b0;end
end/*-----------------------------------------------\---------------- updata data_buf -------------
\-----------------------------------------------*/
always @ (posedge sclk_i or negedge rst_n_i) beginif (!rst_n_i) begindata_buf <= 4'b0;endelse if (ack_o) begindata_buf <= data_i;end
end/*-----------------------------------------------\----------------- sclk --> scl -----------------
\-----------------------------------------------*/
always @ (posedge sclk_i or negedge rst_n_i) beginif (!rst_n_i) beginscl_o <= 1'b0;endelse beginscl_o <= ~scl_o;end
end/*-----------------------------------------------\-------------------- ack signal ----------------
\-----------------------------------------------*/always @ (posedge sclk_i or negedge rst_n_i) beginif (!rst_n_i) beginack_o <= 1'b0;endelse if ((cur_state == IDLE) && (sda_en == 1'b0)) beginack_o <= 1'b1;endelse beginack_o <= 1'b0;end
end/*-----------------------------------------------\--------- updata current state of FSM ----------
\-----------------------------------------------*/
always @ (posedge sclk_i or negedge rst_n_i) beginif (!rst_n_i) begincur_state <= IDLE;endelse begincur_state <= nxt_state;end
endalways @ (*) begincase(cur_state)IDLE: if(sda_en && (scl_o == 1'b1))beginsda_o = 1'b0;nxt_state = START;endelse beginsda_o = 1'b1;nxt_state = IDLE;endSTART:if(sda_en && (scl_o == 1'b0))beginsda_o = data_buf[0];nxt_state = BIT1;endelse beginsda_o = 1'b0;nxt_state = START;endBIT1: if(sda_en && (scl_o == 1'b0))beginsda_o = data_buf[1];nxt_state = BIT2;endelse beginsda_o = data_buf[0];nxt_state = BIT1;endBIT2: if(sda_en && (scl_o == 1'b0))beginsda_o = data_buf[2];nxt_state = BIT3;endelse beginsda_o = data_buf[1];nxt_state = BIT2;endBIT3: if(sda_en && (scl_o == 1'b0))beginsda_o = data_buf[3];nxt_state = BIT4;endelse beginsda_o = data_buf[2];nxt_state = BIT3;endBIT4: if(sda_en && (scl_o == 1'b0))beginsda_o = 1'b0;nxt_state = STOP;endelse beginsda_o = data_buf[3];nxt_state = BIT4;endSTOP: if(!sda_en && (scl_o == 1'b1))beginsda_o = 1'b1;nxt_state = IDLE;endelse beginsda_o = 1'b0;nxt_state = STOP;enddefault:beginsda_o = 1'b1;nxt_state = IDLE;endendcase
endendmodule
(2)M1-Test Bench
module tb_ptosda;reg sclk_i ;reg rst_n_i;reg [3:0] data_i ;wire ack_o ;wire scl_o ;wire sda_o ;initial beginsclk_i = 0 ;rst_n_i = 1;data_i = 4'b0;#10 rst_n_i = 0;#10 rst_n_i = 1;data_i = 4'b1011;
endalways begin#5 sclk_i = ~sclk_i;
endptosda tb_ptosda(.sclk_i (sclk_i ),.rst_n_i(rst_n_i),.data_i (data_i ),.ack_o (ack_o ),.scl_o (scl_o ),.sda_o (sda_o )
);initial begin#1000 $finish;$fsdbDumpfile("ptosda.fsdb");$fsdbDumpvars ;$fsdbDumpMDA ;
end
endmodule
(3)M2-RTL
//-- modified by xlinxdu, 2022/05/06module out16hi
(input clk_i ,input rst_n_i ,input data_i ,output reg [15:0] outhigh_o
);reg [1:0] dly_data;
wire negedge_data_i;reg [3:0] data_buf;reg cnt_en;
reg [2:0] cnt;always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) begindly_data <= 2'b1;endelse begindly_data <= {dly_data[0],data_i};end
end
assign negedge_data_i = (dly_data == 2'b10)?1'b1:1'b0; always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) begincnt_en <= 1'b0;endelse if (negedge_data_i) begincnt_en <= 1'b1;endelse if(cnt == 3'd4)begincnt_en <= 1'b0;end
endalways @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) begincnt <= 3'b0;endelse if (cnt == 3'd4) begincnt <= 3'b0;endelse if (cnt_en) begincnt <= cnt + 1'b1;end
endalways @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) begindata_buf <= 4'b0;endelse if(cnt == 3'd4) begindata_buf <= 4'b0;endelse if (cnt_en) begindata_buf <= {data_buf[2:0],data_i};end
endalways @ (*)begincase(data_buf)4'b0001: outhigh_o = 16'b0000_0000_0000_0001;4'b0010: outhigh_o = 16'b0000_0000_0000_0010;4'b0011: outhigh_o = 16'b0000_0000_0000_0100;4'b0100: outhigh_o = 16'b0000_0000_0000_1000;4'b0101: outhigh_o = 16'b0000_0000_0001_0000;4'b0110: outhigh_o = 16'b0000_0000_0010_0000;4'b0111: outhigh_o = 16'b0000_0000_0100_0000;4'b1000: outhigh_o = 16'b0000_0000_1000_0000;4'b1001: outhigh_o = 16'b0000_0001_0000_0000;4'b1010: outhigh_o = 16'b0000_0010_0000_0000;4'b1011: outhigh_o = 16'b0000_0100_0000_0000;4'b1100: outhigh_o = 16'b0000_1000_0000_0000;4'b1101: outhigh_o = 16'b0001_0000_0000_0000;4'b1110: outhigh_o = 16'b0010_0000_0000_0000; 4'b1111: outhigh_o = 16'b0100_0000_0000_0000;4'b0000: outhigh_o = 16'b1000_0000_0000_0000;default: beginouthigh_o = 16'b0000_0000_0000_0000;endendcase
end
(4)M2-Test Bench
module tb_ptosda;reg sclk_i ;reg rst_n_i;reg [3:0] data_i ;wire ack_o ;wire scl_o ;wire sda_o ;wire [15:0]outhigh_o;initial beginsclk_i = 0 ;rst_n_i = 1;data_i = 4'b0;#10 rst_n_i = 0;#10 rst_n_i = 1;data_i = 4'b1011;
endalways begin#5 sclk_i = ~sclk_i;
endptosda tb_ptosda(.sclk_i (sclk_i ),.rst_n_i(rst_n_i),.data_i (data_i ),.ack_o (ack_o ),.scl_o (scl_o ),.sda_o (sda_o )
);out16hi tb_out16hi(.clk_i (scl_o ),.rst_n_i (rst_n_i ),.data_i (sda_o ),.outhigh_o(outhigh_o)
);initial begin#1000 $finish;$fsdbDumpfile("ptosda.fsdb");$fsdbDumpvars ;$fsdbDumpMDA ;
end
endmodule
五、Result
(1)M1
bug1:由仿真波形可以看出,请求信号占了时钟两个周期,与实际时序(一个周期)不符,定位到ack部分代码,分析是由sda_en使能信号延迟了一拍导致。
/*-----------------------------------------------\-------------------- ack signal ----------------
\-----------------------------------------------*/always @ (posedge sclk_i or negedge rst_n_i) beginif (!rst_n_i) beginack_o <= 1'b0;endelse if ((cur_state == IDLE) && (sda_en == 1'b0)) beginack_o <= 1'b1;endelse beginack_o <= 1'b0;end
end
继续定位到sda_en代码块,发现sda_en是由ack_o控制拉高的,所以在ack_o在第一次被拉高时,使能信号是无法立刻被拉高的,依据实际功能,这里sda_en应该和请求信号ack同一拍产生。
/*-----------------------------------------------\------------ set flag --> sda_en ---------------
\-----------------------------------------------*/
always @ (posedge sclk_i or negedge rst_n_i) beginif (!rst_n_i) beginsda_en <= 1'b0;endelse if (ack_o) beginsda_en <= 1'b1;endelse if((cur_state == STOP) && (ack_o == 1'b0)) beginsda_en <= 1'b0;end
end
更改:把产生高电平的条件改一下
/*-----------------------------------------------\------------ set flag --> sda_en ---------------
\-----------------------------------------------*/
always @ (posedge sclk_i or negedge rst_n_i) beginif (!rst_n_i) beginsda_en <= 1'b0;endelse if ((cur_state == IDLE) && (ack_o == 1'b0)) beginsda_en <= 1'b1;endelse if((cur_state == STOP) && (ack_o == 1'b0)) beginsda_en <= 1'b0;end
end
结果:在更改后,请求信号和使能信号均正常
bug2:由通信协议可知道,scl为高电平时,sda由低变高时刻,串行数据结束。由仿真波形可以看到,红框是已经发送完4bit的数据,产生stop信号时刻。在红框内,scl第一次为高电平时,stop信号没有产生,而是在scl的下一拍产生,多了一拍,时序错误。由仿真波形可以看到,产生这个bug的原因是sda_en拉低的时刻晚了一拍。定位到sda_en代码块。
/*-----------------------------------------------\------------ set flag --> sda_en ---------------
\-----------------------------------------------*/
always @ (posedge sclk_i or negedge rst_n_i) beginif (!rst_n_i) beginsda_en <= 1'b0;endelse if ((cur_state == IDLE) && (ack_o == 1'b0)) beginsda_en <= 1'b1;endelse if((cur_state == STOP) && (ack_o == 1'b0)) beginsda_en <= 1'b0;end
end
//第二处
BIT4: if(sda_en && (scl_o == 1'b0))beginsda_o = 1'b0;nxt_state = STOP;endelse beginsda_o = data_buf[3];nxt_state = BIT4;end
更改:把sda_en在发送完第四个bit数据时产生,同时把状态机里面的BIT4条件改一下。因为sda_en发生了改变。
/*-----------------------------------------------\------------ set flag --> sda_en ---------------
\-----------------------------------------------*/
always @ (posedge sclk_i or negedge rst_n_i) beginif (!rst_n_i) beginsda_en <= 1'b0;endelse if ((cur_state == IDLE) && (ack_o == 1'b0)) beginsda_en <= 1'b1;endelse if((cur_state == BIT4) && (ack_o == 1'b0)) beginsda_en <= 1'b0;end
end
//第二处
BIT4: if(!sda_en && (scl_o == 1'b0))beginsda_o = 1'b0;nxt_state = STOP;endelse beginsda_o = data_buf[3];nxt_state = BIT4;end
结果:更改后,仿真波形与时序一致。
(2)M2
bug1:由仿真波形可以看到,传输的数据结束之后,data_buf,依然在缓冲数据,所以输出一直在变,不符合时序要求,问题定位到data_buf更新代码块。
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) begindata_buf <= 4'b0;endelse if (cnt_en) begindata_buf <= {data_buf[2:0],data_i};endelse begindata_buf <= 4'b0;end
end
更正:在一次数据传输结束之后,data_buf清零,可解决上述bug。
always @ (posedge clk_i or negedge rst_n_i) beginif (!rst_n_i) begindata_buf <= 4'b0;endelse if(cnt == 3'd4) begindata_buf <= 4'b0;endelse if (cnt_en) begindata_buf <= {data_buf[2:0],data_i};end
end
bug2:在解决bug1后,发现清零会带来一个问题,就是改数据被误认为是M1传输过来的,定位到输出代码块
always @ (*)begincase(data_buf)4'b0001: outhigh_o = 16'b0000_0000_0000_0001;4'b0010: outhigh_o = 16'b0000_0000_0000_0010;4'b0011: outhigh_o = 16'b0000_0000_0000_0100;4'b0100: outhigh_o = 16'b0000_0000_0000_1000;4'b0101: outhigh_o = 16'b0000_0000_0001_0000;4'b0110: outhigh_o = 16'b0000_0000_0010_0000;4'b0111: outhigh_o = 16'b0000_0000_0100_0000;4'b1000: outhigh_o = 16'b0000_0000_1000_0000;4'b1001: outhigh_o = 16'b0000_0001_0000_0000;4'b1010: outhigh_o = 16'b0000_0010_0000_0000;4'b1011: outhigh_o = 16'b0000_0100_0000_0000;4'b1100: outhigh_o = 16'b0000_1000_0000_0000;4'b1101: outhigh_o = 16'b0001_0000_0000_0000;4'b1110: outhigh_o = 16'b0010_0000_0000_0000; 4'b1111: outhigh_o = 16'b0100_0000_0000_0000;4'b0000: outhigh_o = 16'b1000_0000_0000_0000;default: beginouthigh_o = 16'b0000_0000_0000_0000;endendcase
end
更正:加入计数控制条件,只有在数据传输过程(cnt=1~4)时,data_buf的0才有效,否则输出16‘b0。
always @ (*)begincase(data_buf)4'b0001: if(cnt !== 3'd0) begin outhigh_o = 16'b0000_0000_0000_0001;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b0010: if(cnt !== 3'd0) begin outhigh_o = 16'b0000_0000_0000_0010;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b0011: if(cnt !== 3'd0) begin outhigh_o = 16'b0000_0000_0000_0100;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b0100: if(cnt !== 3'd0) begin outhigh_o = 16'b0000_0000_0000_1000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b0101: if(cnt !== 3'd0) begin outhigh_o = 16'b0000_0000_0001_0000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b0110: if(cnt !== 3'd0) begin outhigh_o = 16'b0000_0000_0010_0000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b0111: if(cnt !== 3'd0) begin outhigh_o = 16'b0000_0000_0100_0000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b1000: if(cnt !== 3'd0) begin outhigh_o = 16'b0000_0000_1000_0000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b1001: if(cnt !== 3'd0) begin outhigh_o = 16'b0000_0001_0000_0000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b1010: if(cnt !== 3'd0) begin outhigh_o = 16'b0000_0010_0000_0000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b1011: if(cnt !== 3'd0) begin outhigh_o = 16'b0000_0100_0000_0000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b1100: if(cnt !== 3'd0) begin outhigh_o = 16'b0000_1000_0000_0000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b1101: if(cnt !== 3'd0) begin outhigh_o = 16'b0001_0000_0000_0000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b1110: if(cnt !== 3'd0) begin outhigh_o = 16'b0010_0000_0000_0000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b1111: if(cnt !== 3'd0) begin outhigh_o = 16'b0100_0000_0000_0000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;end4'b0000: if(cnt !== 3'd0) begin outhigh_o = 16'b1000_0000_0000_0000;endelse begin outhigh_o = 16'b0000_0000_0000_0000;enddefault: beginouthigh_o = 16'b0000_0000_0000_0000;endendcase
end
(3)Top
在进行M1-M2-M0联调时,波形如上,输出符合预期时序要求,前仿真功能正常。
作者:xlinxdu
版权:本文是作者原创,版权归作者所有。
转载:未经作者允许,禁止转载,转载必须保留此段声明,必须在文章中给出原文连接。
08 SCLSDA,类IIC协议(附源码)相关推荐
- 单机redis工具类的使用附源码
单机redis工具类的使用附源码 问题背景 项目搭建 代码测试 总结 Lyric: 怎么隐藏我的悲伤 问题背景 redis常用的工具类 注意事项: 默认已安装redis,可以使用安装包安装看这篇文章, ...
- 计算机毕业设计SSM餐饮类网站【附源码数据库】
项目运行 环境配置: Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclis ...
- SpringBoot实战:整合Redis、mybatis,封装RedisUtils工具类等(附源码)
创建SpringBoot项目 在线创建方式 网址:https://start.spring.io/ 然后创建Controller.Mapper.Service包 SpringBoot整合Redis 引 ...
- 如何绘制一个类甘特图 (附源码)
作者简介 Kid 蚂蚁金服·数据体验技术团队 本文介绍如何从头绘制一个业务图表以及对于通用性上的一些思考.代码在最后也会给出. 效果演示 先看下组件的最后效果 要画图,当然是先找找看有没有能够直接拿来 ...
- Python大法之告别脚本小子系列——信息资产收集类脚本编写附源码
关注头条号,私信回复资料会有意外惊喜呦------最后一张照片有资料呦. 前言 在采集到URL之后,要做的就是对目标进行信息资产收集了,收集的越好,你挖到洞也就越多了............当然这一切 ...
- devexpress html编辑器,图解DevExpress RichEditControl富文本的使用,附源码及官方API
9点半了,刚写到1.2. 该回家了,明天继续写完. 大家还需要什么操作,留言说一下,没有的我明天继续加. 好久没有玩DevExpress了,今天下载了一个玩玩,发现竟然更新到14.2.5了..我去 ...
- 04【Verilog实战】SPI协议底层硬件接口设计(附源码RTL/TB)
脚 本:makefile 工 具:vcs 和 verdi 写在前面 这个专栏的内容记录的是个人学习过程,博文中贴出来的代码是调试前的代码,方便bug重现. 调试后的程序提供下载,[下载地址] 发现 ...
- 客户端服务器通信demo(续) -- 使用二进制协议 (附源码)
转载连接: http://blog.csdn.net/zhuweisky/article/details/11827797 在网络上,交互的双方基于TCP或UDP进行通信,通信协议的格式通常分为两类: ...
- java qq聊天界面_【附源码】用Java写了一个类QQ界面聊天小项目,可在线聊天!...
原标题:[附源码]用Java写了一个类QQ界面聊天小项目,可在线聊天! 目录: 1.功能实现 2.模块划分 3.使用到知识 4.部分代码实现 5.运行例图 1.功能实现 1.修改功能(密码.昵称.个性 ...
- 生成对抗网络(GANs)的资料小结,另附:资源|17类对抗网络经典论文及开源代码(附源码)
1.GANs的一些资料链接 ************************************************** *********************************** ...
最新文章
- Part10 泛型程序设计与C++标准模板库 10.1泛型程序设计及STL的结构
- iOS 11开发教程(十九)iOS11应用视图美化按钮之设置按钮的外观
- 关于客户端无法获得服务器端GP服务(Geoprocessing Service)结果的解决办法
- 如何使用Native Messaging API 打开window程序
- Boost笔记--Thread--Ubuntu上初次使用时遇到的问题
- 谷歌公布4个0day详情,其中3个被滥用于攻击亚美尼亚
- hdu 1018 Big Number(n!求位数)
- Mysql 存储过程实现订单流水号
- 0基础学python做什么工作好-零基础学Python,越早明白这些,越快找到好工作!...
- 【编程开发】 C与C++中的关于函数指针的强制类型转换与指针函数的关系
- chainmaker 交易执行失败也会打包到区块 落盘
- 如何使用RDP报表工具预览页面可以出现完整数据而不是只显示一条
- 【WEB搜索技术】课程学习大纲与学习感悟
- C++类的定义和声明怎么写
- python对sqlite增删改查_Python操作sqlite3数据库 增删改查
- Java微信支付API文档测试
- 有哪些好用的在线电影影视导航网站
- l那是计算机房吗,机房设计常用计算公式
- 中美自动驾驶最新融资情况:千万级与十亿级美元的距离
- 在可达性分析中存在的问题