I2C控制器的Verilog建模之二
前言:接着上一篇的I2C写操作,今天要实现一个I2C的读操作。虽然在ADV7181B配置内部寄存器时没有必要使用到读操作,但是为了进一步确认寄存器是否在I2C写模块下被正确配置,这一步是必不可少的。
设计思路:由于最终的应用里I2C读模块在调试结束后还是要被剔除,因此决定还是另外建一个读的状态机独立于之前的写状态机。读状态机的思路基本和写状态机的思路一样,需要注意的是一次写操作需要两次的START信号和最后一字节传输结束后的NON-ACKNOWLEDGE。
改进和注意点:相比之前的写模块,读模块完善了以下这些
(a)时钟信号在一系列写操作完毕之后拉高,不再跳变;
(b)添加了使能信号,便于安排模块在写模块完成一些了写操作后才开始被激励工作;
(c)三态口:修改了SDAT_R,使之在LINK为0,即三态口读方向时候SDAR_T保持1。以及LINK信号在空闲状态下释放总线;
未解决的问题:因为读写两个独立的模块各自用到一个三态口,即各自有一对I2C_SCLK和I2C_SDAT,因此在顶层就驱动同一个芯片的引脚之前,还需要一个复用的逻辑开关分时切换这两个驱动源。因为之前没有这样用多个三态口驱动一个同一个三态口,综合是否可行将在下一篇I2C测试里给出答案。p.s.当然如果整合两个状态机到同一个模块下就不存在这样的问题。
源码:
`timescale ns / ps
`define SIM
`define SYS_CLK
`define I2C_CLK
`define I2C_DIV `SYS_CLK/`I2C_CLK
`define ADV7180
`define SCLK_CNT_WIDTH
//version:v1.0
//mend bug: WHEN IN IDLE SET SCLK HIGH;
module i2c_read_controller(
sys_clk,
sys_rst_n,
sys_rreq_i,
sys_rd_en,
rd_reg_addr_i,
sys_data_o,
i2c_rd_idle_o,
i2c_rd_ack_o,
i2c_sclk,
i2c_sdat
); input sys_clk;
input sys_rst_n;
input sys_rreq_i;
input sys_rd_en; //由其他信号激励使能
input [:] rd_reg_addr_i; //从机寄存器地址
output [:] sys_data_o; //待写的数据
output i2c_rd_idle_o; //模块空闲
output i2c_rd_ack_o; //非I2C的ACK,模块的ack
output i2c_sclk;
inout i2c_sdat;
`ifdef ADV7180
parameter DEVICE_READ = 'h40; //器件读操作地址
parameter DEVICE_WRITE = 'h41; //器件写操作地址
`endif `ifdef SIM
parameter ST_WIDTH = ;
parameter IDLE = "IDLE...",
START1 = "START1.",
WR_SLAVE = "WR_SLAV",
ACK1 = "ACK1...",
SET_REG = "SET_REG",
ACK2 = "ACK2...",
START2 = "START2",
RD_SLAVE = "RD_SLAV",
ACK3 = "ACK3...",
DATA = "DATA...",
NACK4 = "NACK4..",
STOP = "STOP..."; `else
`define FSM
parameter ST_WIDTH = ;
parameter IDLE = `FSM'b0000_0000_0001,
START1 = `FSM'b0000_0000_0010, //写操作一共有1个start,读操作一共2个start
WR_SLAVE = `FSM'b0000_0000_0100,
ACK1 = `FSM'b0000_0000_1000,
SET_REG = `FSM'b0000_0001_0000,
ACK2 = `FSM'b0000_0010_0000,
START2 = `FSM'b0000_0100_0000,
RD_SLAVE = `FSM'b0000_1000_0000,
ACK3 = `FSM'b0001_0000_0000,
DATA = `FSM'b0010_0000_0000,
NACK4 = `FSM'b0100_0000_0000,
STOP = `FSM'b1000_0000_0000;
`endif
//caputre the posedge of sys_rreq_i;
reg sys_rreq_r0 = ;
always @ (posedge sys_clk) begin
if(sys_rst_n == 'b0) sys_rreq_r0 <= 0;
else sys_rreq_r0 <= sys_rreq_i;
end
wire do_rreq = sys_rreq_i & ~sys_rreq_r0 & sys_rd_en;
//generate the rd_start;
reg rd_start = ;
always @ (posedge sys_clk) begin
if(sys_rst_n == 'b0) rd_start <= 0;
else if(i2c_rd_ack_o == 'b1) rd_start <= 0;
else if(do_rreq) rd_start <= ;
else rd_start <= rd_start;
end
//GENERATE SCLK_R
reg [`SCLK_CNT_WIDTH-:] sclk_cnt = ;
always @ (posedge sys_clk) begin
if('b0 == sys_rst_n) sclk_cnt <= 0;
else if((sclk_cnt < `I2C_DIV-)&&(sys_rd_en == 'b1)&&(rd_start == 1'b1)) sclk_cnt <= sclk_cnt + 'd1;
else sclk_cnt <= ;
end `define SCLK_POS (sclk_cnt == `SCLK_CNT_WIDTH'd499)
`define SCLK_HIGH (sclk_cnt == `SCLK_CNT_WIDTH'd124)
`define SCLK_NEG (sclk_cnt == `SCLK_CNT_WIDTH'd249)
`define SCLK_LOW (sclk_cnt == `SCLK_CNT_WIDTH'd374)
wire i2c_sclk_w;
assign i2c_sclk_w = ((sys_rd_en == 'b1)&&(sclk_cnt <= `SCLK_CNT_WIDTH'd249))?'b1:1'b0; //FSM
reg [:] data2host = ;
reg [:] sys_data_o = ;
reg sdat_r = ;
reg link = ; //控制三态口读写方向,默认为读方向0,写时为1
reg [:] bit_cnt = 'd0;
reg [ST_WIDTH-:] c_st = IDLE;
reg [ST_WIDTH-:] n_st = IDLE;
//FSM-1
always @ (posedge sys_clk) begin
if('b0 == sys_rst_n) c_st <= IDLE;
else c_st <= n_st;
end
//fsm-2
//实际的状态转移中ack[2:0]比物理等待的ack少四分之一
always @ (*) begin
n_st = IDLE;
case(c_st)
IDLE:begin
n_st = (rd_start == 'b1)?START1:IDLE;end
START1:begin
n_st = (`SCLK_LOW)?WR_SLAVE:START1;end //sclk为高电平中心时转移
WR_SLAVE:begin
n_st = ((`SCLK_LOW)&&(bit_cnt == 'd8))?ACK1:WR_SLAVE;end//数据在低电平是更新
ACK1:begin
n_st = (`SCLK_NEG)?SET_REG:ACK1;end//为保证下一步设置寄存器,提前1/4进入下一个状态
SET_REG:begin
n_st = ((`SCLK_LOW)&&(bit_cnt == 'd8))?ACK2:SET_REG;end//数据在低电平是更新
ACK2:begin
n_st = (`SCLK_NEG)?START2:ACK2;end
START2:begin
n_st = (`SCLK_NEG)?RD_SLAVE:START2;end
RD_SLAVE:begin
n_st = ((`SCLK_LOW)&&(bit_cnt == 'd8))?ACK3:RD_SLAVE;end
//为保证下一步设置寄存器,提前1/4进入下一个状态
ACK3:begin
n_st = (`SCLK_NEG)?DATA:ACK3;end
DATA:begin
n_st = ((`SCLK_LOW)&&(bit_cnt == 'd8))?NACK4:DATA;end
NACK4:begin
n_st = (`SCLK_NEG)?STOP:NACK4;end
STOP:begin
n_st = (`SCLK_NEG)?IDLE:STOP;end
default:begin
n_st = IDLE;end
endcase
end
//FSM-3
always @ (posedge sys_clk) begin
if(sys_rst_n == 'b0) begin
link <= 'd0; //释放总线
data2host <= 'd0;
bit_cnt <= 'd0;
sdat_r <= 'd1;
sys_data_o <= ;
end
else begin
case(c_st)
IDLE:begin
link <= 'd0;
data2host <= DEVICE_WRITE;
bit_cnt <= 'd0;
sdat_r <= 'd1;
sys_data_o <= sys_data_o;
end
START1:begin
link <= 'd1;
sys_data_o <= sys_data_o;
bit_cnt <= 'd1;
data2host <= (`SCLK_LOW)?data2host<<:data2host;
if(`SCLK_HIGH) begin
sdat_r <= 'b0;end
else if(`SCLK_LOW) begin
sdat_r <= data2host[];end //pull down,由于data2host缓存一级的缘故,需要提前在START里输出第MSB位
else begin
sdat_r <= sdat_r;end
end
WR_SLAVE:begin
sys_data_o <= sys_data_o;
if(`SCLK_LOW) begin
link <= (bit_cnt == 'd8)?1'b0:'b1; //释放数据总线
bit_cnt <= (bit_cnt == 'd8)?4'd0:bit_cnt+'d1;
data2host <= {data2host[:],'d0};//左移一位
sdat_r <= (bit_cnt == 'd8)?1'd1:data2host[];end
else begin
link <= link;
bit_cnt <= bit_cnt;
data2host <= data2host;
sdat_r <= sdat_r;end
end
ACK1:begin
link <= 'd0;
sys_data_o <= sys_data_o;
data2host <= (`SCLK_POS)?rd_reg_addr_i:data2host; //读入待写的寄存器地址
bit_cnt <= 'd0;
sdat_r <= 'd1;
end
SET_REG:begin
sys_data_o <= sys_data_o;
if(`SCLK_LOW) begin
link <= (bit_cnt == 'd8)?1'b0:'b1; //释放数据总线
bit_cnt <= (bit_cnt == 'd8)?4'd0:bit_cnt+'d1;
data2host <= {data2host[:],'d0};//左移一位
sdat_r <= (bit_cnt == 'd8)?1'd1:data2host[];end
else begin
link <= link;
bit_cnt <= bit_cnt;
data2host <= data2host;
sdat_r <= sdat_r;end
end
ACK2:begin
link <= 'd0;
sys_data_o <= sys_data_o;
data2host <= (`SCLK_POS)?DEVICE_READ:data2host; //读入待写的寄存器地址
bit_cnt <= 'd0;
sdat_r <= 'd1;
end
START2:begin
link <= (`SCLK_LOW)?'b1:link;
sys_data_o <= sys_data_o;
data2host <= data2host;
bit_cnt <= bit_cnt;
sdat_r <= (`SCLK_HIGH)?'b0:sdat_r;
end
RD_SLAVE:begin
sys_data_o <= sys_data_o;
if(`SCLK_LOW) begin
link <= (bit_cnt == 'd8)?1'b0:'b1;
bit_cnt <= (bit_cnt == 'd8)?4'd0:bit_cnt+'d1;
data2host <= {data2host[:],'d0};//左移一位
sdat_r <= (bit_cnt == 'd8)?1'd1:data2host[];end
else begin
link <= link;
bit_cnt <= bit_cnt;
data2host <= data2host;
sdat_r <= sdat_r;end
end
ACK3:begin
link <= 'b0;
bit_cnt <= 'd0;
sys_data_o <= sys_data_o;
data2host <= ;
sdat_r <= 'd1;end
DATA:begin
sys_data_o <= sys_data_o;
if(`SCLK_HIGH) begin
link <= (bit_cnt == 'd8)?1'b1:'b0; //为主设备产生NACK准备
bit_cnt <= (bit_cnt == 'd8)?4'd0:bit_cnt+'d1;
data2host[:] <= data2host[:];//左移一位
data2host[] <= sdat_r;end
else begin
link <= link;
bit_cnt <= bit_cnt;
data2host <= data2host;
sdat_r <= sdat_r;end
end
NACK4:begin
link <= 'd1;
sdat_r <= 'd1;//预先拉低
bit_cnt <= bit_cnt;
sys_data_o <= data2host;end
STOP:begin
link <= 'b1;
bit_cnt <= bit_cnt;
sys_data_o <= sys_data_o;
data2host <= data2host;
if(`SCLK_LOW) begin
sdat_r <= 'b0;end
else if(`SCLK_HIGH) begin
sdat_r <= 'b1;end
else begin
sdat_r <= sdat_r;end
end
default:begin
link <= 'd0;
data2host <= 'd0;
sys_data_o <= sys_data_o;
bit_cnt <= 'd0;
sdat_r <= 'd1;
end
endcase
end
end
//assign
assign i2c_sdat = (link == 'b1)?sdat_r:8'hzz;
assign i2c_rd_idle_o = (c_st == IDLE)?'b1:1'b0;
assign i2c_rd_ack_o = ((c_st == STOP)&&(`SCLK_NEG))?'b1:1'b0;
assign i2c_sclk = (c_st != IDLE)?i2c_sclk_w:'b1; endmodule
控制读模块源码2:
`timescale ns / ps
`define LUT_WIDTH
module adv7181_read_back(
sys_clk,
sys_rst_n,
i2c_rd_ack_i,
rd_back_done_o,
sys_rreq_o,
rd_reg_addr_o
);
input sys_clk;
input sys_rst_n;
input i2c_rd_ack_i;
output rd_back_done_o;
output sys_rreq_o;
output [:] rd_reg_addr_o; //generate rreq_o
reg sys_rreq_o = ;
reg [`LUT_WIDTH-:] lut_index = ;
reg [:] lut_data = ;
always @ (posedge sys_clk) begin
if('b0 == sys_rst_n) begin
sys_rreq_o <= ;
lut_index <= ;end
else if((i2c_rd_ack_i == 'b1)&&(rd_back_done_o == 1'b0)) begin
sys_rreq_o <= ;
lut_index <= lut_index + 'd1;end
else begin
sys_rreq_o <= ;
lut_index <= lut_index;end
end
//assign
assign rd_back_done_o = (lut_index == `LUT_WIDTH'd15)?1'b1:'b0;
assign rd_reg_addr_o = lut_data;
//lut
always @ (*) begin
case(lut_index)
`LUT_WIDTH'd0:lut_data <= 8'h23;
`LUT_WIDTH'd1:lut_data <= 8'h41;
`LUT_WIDTH'd2:lut_data <= 8'hf2;
`LUT_WIDTH'd3:lut_data <= 8'ha3;
`LUT_WIDTH'd4:lut_data <= 8'h43;
`LUT_WIDTH'd5:lut_data <= 8'h13;
`LUT_WIDTH'd6:lut_data <= 8'h65;
`LUT_WIDTH'd7:lut_data <= 8'h76;
`LUT_WIDTH'd8:lut_data <= 8'h85;
`LUT_WIDTH'd9:lut_data <= 8'h93;
`LUT_WIDTH'd10:lut_data <= 8'h14;
`LUT_WIDTH'd11:lut_data <= 8'h13;
`LUT_WIDTH'd12:lut_data <= 8'h15;
`LUT_WIDTH'd13:lut_data <= 8'h11;
`LUT_WIDTH'd14:lut_data <= 8'h11;
`LUT_WIDTH'd15:lut_data <= 8'h19;
endcase
end endmodule
仿真源码3:
`timescale ns / ps
module tb_read();
reg sys_clk;
reg sys_rst_n; initial begin
sys_clk=;
sys_rst_n=;
# sys_rst_n=;
end always begin
# sys_clk=~sys_clk;end wire i2c_rd_ack;
wire sys_rreq;
wire [:] rd_reg_addr; wire i2c_sclk;
wire i2c_sdat;
wire i2c_rd_idle;
wire [:] sys_data_out;
i2c_read_controller u0(
.sys_clk( sys_clk ),
.sys_rst_n( sys_rst_n ),
.sys_rreq_i( sys_rreq ),
.sys_rd_en( 'b1 ),
.rd_reg_addr_i( rd_reg_addr ),
.sys_data_o( sys_data_out ),
.i2c_rd_idle_o( i2c_rd_idle ),
.i2c_rd_ack_o( i2c_rd_ack ),
.i2c_sclk( i2c_sclk ),
.i2c_sdat( i2c_sdat )
); wire rd_back_done; adv7181_read_back u1(
.sys_clk( sys_clk ),
.sys_rst_n( sys_rst_n ),
.i2c_rd_ack_i( i2c_rd_ack ),
.rd_back_done_o( rd_back_done ),
.sys_rreq_o( sys_rreq ),
.rd_reg_addr_o( rd_reg_addr )
);
endmodule
I2C控制器的Verilog建模之二的更多相关文章
- Norflash控制器的Verilog建模之二(仿真)
前言:经过几天修改,norflash控制器基本已经完成,通过仿真.完整的norflash包含2个模块:直接操作硬件的norflash_ctrl.v与控制ctrl模块的驱动norflash_driver ...
- I2C控制器的Verilog建模之一
前言:之前申请了ADI公司的一款ADV7181CBSTZ的视频解码芯片,正好原装DE2板子安的是同系列的ADV7181BBSTZ.虽然都是ADV7181的宗出,但是寄存器配置等等还是有些诧异,引脚也不 ...
- I2C控制器的Verilog建模之三(完结版)
前言:终于到了测试篇,不过悲剧了一下.按照之前<二>里面的思路,在顶层用一个复用器驱动读写独立模块的I2C总线确实失败.虽然综合过去了,不过警告里已经说明:底层的2个原本是inout三态口 ...
- Norflash控制器的Verilog建模之一
摘要:今天驱动一款SPANSION公司生产的norflash——S29AL032D70,没有别的参考资料,大致了解一下norflash的内部cmos电路架构以及其用途之后,直接看手册吧. 如何看手册: ...
- SDRAM控制器的Verilog建模之一
前言:作为经典存储器的三剑客中的flash和sram已经建模测试过了,虽然现在都已经ddr2,ddr3,667MHZ.1333MHZ的天下了,但是接下这周来准备写一下sdram的controller. ...
- 异步SRAM控制器的Verilog建模
前言:sram顾名思义静态随机存储器,分为asram异步型和ssram同步型.这里驱动DE2上一块ISSI公司的512KB的asram. 设计思路:因为实际应用中单字节读写效率不高,所以本设计中仿照s ...
- Norflash控制器的Verilog建模之三(測試)
前言:回校了,辦好手續就著手寫測試篇.初步的norflash控制器已經完成,通過硬件測試.目前的norflash完成扇区块擦除.单字节写.单字节读3个功能.博文最后附上源码. 总结:和之前的博文一样, ...
- VGA逐行扫描控制器的Verilog建模
前言:因为VGA是一种模拟图像传输数据接口,所要将数字信号用DAC转换成模拟量.本文用的一款ADI公司高精度的视频IC,实则一款高带宽的视频DAC.因为VGA时序较为简单,并且网上的VGA驱动基本大同 ...
- Linux i2c子系统(四) _从i2c-s3c24xx.c看i2c控制器驱动的编写
"./drivers/i2c/busses/i2c-s3c2410.c"是3.14.0内核中三星SoC的i2c控制器驱动程序, 本文试图通过对这个程序的分析, 剥离繁复的细节, 总 ...
随机推荐
- ASP.NET Web API 入门示例详解
REST服务已经成为最新的服务端开发趋势,ASP.NET Web API即为.NET平台的一种轻量级REST架构. ASP.NET Web API直接借鉴了ASP.NET MVC的设计,两者具有非常类 ...
- having和where的区别
HAVING子句的使用. 例子: SELECT mob_belong_to,user_id,MAX(BIRTH_DATE) FROM user_base_info WHERE sign_in_date ...
- RFID Hacking④:使用ProxMark3 破解门禁
文中提及的部分技术可能带有一定攻击性,仅供安全学习和教学用途,禁止非法使用! 0×00 前言 国际黑客大会Defcon传统之一:开锁!因为黑客认为锁也是一种安全挑战.我们在黑客题材电影.电视剧中也常常 ...
- UIkit框架之UIwebview
1.继承链:UIview:UIResponder:NSObject 2.使用loadHTMLString:baseURL:方法来加载本地的HTML文件,或者使用 loadRequest:方法来加载网络 ...
- 从零开始学习Node.js例子二 文本提交与显示
index.js var server = require("./server"); var router = require("./router"); var ...
- framework各版本对比
本对比只是粗略的让大家了解到大概有什么变化 .net 1.0 2002年2月.net 2.0 2006年1月.net 3.0 2006年11月 .net 3.5 2007年11月.net 3.5 sp ...
- sublime text3 快捷键设置
//插入到key binding user 里面,浏览器安装路径修改成自己的路径 1[ //firefox测试快捷键 { "keys":["f3"], &quo ...
- 解决eclipse spring配置报错:cvc-elt.1: Cannot find the declaration of element
解决eclipse spring配置报错:cvc-elt.1: Cannot find the declaration of element 'beans'.Referenced file conta ...
- (基础篇)php中理解print EOT分界符和echo EOT的用法区别
html与php编写中echo可以同时输出多个字符串,并不需要圆括号. print只可以同时输出一个字符串,需要圆括号. print的用法和C语言很像,所以会对输出内容里的%做特殊解释. echo无返 ...
- 转 未能加载类型 xxxx
未能加载类型 分析器错误 说明: 在分析向此请求提供服务所需资源时出错.请检查下列特定分析错误详细信息并适当地修改源文件. 分析器错误信息: 未能加载类型“xxxxx”. 错误的 < ...