FPGA I²C总线
仿真主程序完成主节点数据到从节点的控制,代码如下:
- `include "timescale.v"
- //模块定义
- module tst_bench_top();
- //连线和寄存器
- reg clk;
- reg rstn;
- wire [31:0] adr;
- wire [ 7:0] dat_i, dat_o;
- wire we;
- wire stb;
- wire cyc;
- wire ack;
- wire inta;
- //q 保存状态寄存器内容
- reg [7:0] q, qq;
- wire scl, scl_o, scl_oen;
- wire sda, sda_o, sda_oen;
- //寄存器地址
- parameter PRER_LO = 3'b000; //分频寄存器低位地址
- parameter PRER_HI = 3'b001; //高位地址
- parameter CTR = 3'b010; //控制寄存器地址,(7)使能位|6 中断使能位|5-0其余保留位
- parameter RXR = 3'b011; //接收寄存器地址,(7)接收到的最后一个字节的数据
- parameter TXR = 3'b011; //传输寄存器地址,(7)传输地址时最后一位为读写位,1 为读
- parameter CR = 3'b100; //命令寄存器地址,
- //(7)开始|6 结束|5 读|4 写|3 应答(作为接收方时,发送应答信号,“0”为应答,“1”为不应答)|2 保留位|1 保留位|0 中断应答位,这八位自动清除
- parameter SR = 3'b100; //状态寄存器地址,(7)接收应答位(“0”为接收到应答)|6 忙位(产生开始信号后变为 1,结束信号后变为 0)|5 仲裁位|4-2 保留位|1 传输中位(1 表示正在传输数据,0 表示传输结束)|中断标志位
- parameter TXR_R = 3'b101;
- parameter CR_R = 3'b110;
- // 产生时钟信号,一个时间单位为 1ns,周期为 10ns,频率为 100MHz。
- always #5 clk = ~clk;
- //连接 master 模拟模块
- wb_master_model #(8, 32) u0 (
- .clk(clk), //时钟
- .rst(rstn), //重起
- .adr(adr), //地址
- .din(dat_i), //输入的数据
- .dout(dat_o), //输出的数据
- .cyc(cyc),
- .stb(stb),
- .we(we),
- .sel(),
- .ack(ack), //应答
- .err(1'b0),
- .rty(1'b0)
- );
- //连接 i2c 接口
- i2c_master_top i2c_top (
- //连接到 master 模拟模块部分
- .wb_clk_i(clk), //时钟
- .wb_rst_i(1'b0), //同步重起位
- .arst_i(rstn), //异步重起
- .wb_adr_i(adr[2:0]), //地址输入
- .wb_dat_i(dat_o), //数据输入接口
- .wb_dat_o(dat_i), //数据从接口输出
- .wb_we_i(we), //写使能信号
- .wb_stb_i(stb), //片选信号,应该一直为高
- .wb_cyc_i(cyc),
- .wb_ack_o(ack), //应答信号输出到 master 模拟模块
- .wb_inta_o(inta), //中断信号输出到 master 模拟模块
- 第 4 章 模拟 I
- 2
- C 协议
- ·121·
- //输出的 i2c 信号,连接到 slave 模拟模块
- .scl_pad_i(scl),
- .scl_pad_o(scl_o),
- .scl_padoen_o(scl_oen),
- .sda_pad_i(sda),
- .sda_pad_o(sda_o),
- .sda_padoen_o(sda_oen)
- );
- //连接到 slave 模拟模块
- i2c_slave_model #(7'b1010_000) i2c_slave (
- .scl(scl),
- .sda(sda)
- );
- //为 master 模拟模块产生 scl 和 sda 的三态缓冲
- assign scl = scl_oen ? 1'bz : scl_o; // create tri-state buffer for i2c_master scl line
- assign sda = sda_oen ? 1'bz : sda_o; // create tri-state buffer for i2c_master sda line
- //上拉
- pullup p1(scl); // pullup scl line
- pullup p2(sda); // pullup sda line
- //初始化
- initial
- begin
- $display("n 状态: %t I2C 接口测试开始!nn", $time);
- // 初始值
- clk = 0;
- //重起系统
- rstn = 1'b1; // negate reset
- #2;
- rstn = 1'b0; // assert reset
- repeat(20) @(posedge clk);
- rstn = 1'b1; // negate reset
- $display("状态: %t 完成系统重起!", $time);
- @(posedge clk);
- // 对接口编程
- // 写内部寄存器
- // 分频 100M/100K*5=O'200=h'C8
- u0.wb_write(1, PRER_LO, 8'hc7);
- u0.wb_write(1, PRER_HI, 8'h00);
- $display("状态: %t 完成分频寄存器操作!", $time);
- //读分频寄存器内容
- u0.wb_cmp(0, PRER_LO, 8'hc8);
- u0.wb_cmp(0, PRER_HI, 8'h00);
- $display("状态: %t 完成分频寄存器确认操作!", $time);
- //接口使能
- u0.wb_write(1, CTR, 8'h80);
- $display("状态: %t 完成接口使能!", $time);
- // 驱动 slave 地址
- // h'a0=b'1010_0000,地址+写状态,写入的地址为 h'50
- u0.wb_write(1, TXR, 8'ha0);
- //命令内容为 b'1001_0000,产生开始位,并设置写状态
- u0.wb_write(0, CR, 8'h90);
- $display("状态: %t 产生开始位, 然后写命令 a0(地址+写),命令开始!", $time);
- // 检查状态位信息
- // 检查传输是否结束
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(0, SR, q);
- $display("状态: %t 地址驱动写操作完成!", $time);
- // 待写的地址为 h'01
- u0.wb_write(1, TXR, 8'h01);
- // 产生写命令 b'0001_0000
- u0.wb_write(0, CR, 8'h10);
- $display("状态: %t 待写地址为 01,命令开始!", $time);
- // 检查状态位
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(0, SR, q);
- $display("状态: %t 写操作完成!", $time);
- // 写入内容
- u0.wb_write(1, TXR, 8'ha5);
- u0.wb_write(0, CR, 8'h10);
- $display("状态: %t 写入内容为 a5,开始写入过程!", $time);
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(1, SR, q);
- $display("状态: %t 写 a5 到地址 h'01 中完成!", $time);
- // 写入下一个地址 5a
- u0.wb_write(1, TXR, 8'h5a); // present data
- // 写入并停止
- u0.wb_write(0, CR, 8'h50); // set command (stop, write)
- $display("状态: %t 写 5a 到下一个地址,产生停止位!", $time);
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(1, SR, q); // poll it until it is zero
- $display("状态: %t 写第二个地址结束!", $time);
- // 读
- // 驱动 slave 地址
- u0.wb_write(1, TXR, 8'ha0);
- u0.wb_write(0, CR, 8'h90);
- $display("状态: %t 产生开始位,写命令 a0 (slave 地址+write)", $time);
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(1, SR, q); // poll it until it is zero
- $display("状态: %t slave 地址驱动完成!", $time);
- // 发送地址
- u0.wb_write(1, TXR, 8'h01);
- u0.wb_write(0, CR, 8'h10);
- $display("状态: %t 发送地址 01!", $time);
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(1, SR, q);
- $display("状态: %t 地址发送完成!", $time);
- // 驱动 slave 地址,1010_0001,h'50+read
- u0.wb_write(1, TXR, 8'ha1);
- u0.wb_write(0, CR, 8'h90);
- $display("状态: %t 产生重复开始位, 读地址+开始位", $time);
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(1, SR, q);
- $display("状态: %t 命令结束!", $time);
- // 读数据
- u0.wb_write(1, CR, 8'h20);
- $display("状态: %t 读+应答命令", $time);
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(1, SR, q);
- $display("状态: %t 读结束!", $time);
- // 检查读的内容
- u0.wb_read(1, RXR, qq);
- if(qq !== 8'ha5)
- $display("n 错误: 需要的是 a5, received %x at time %t", qq, $time);
- // 读下一个地址内容
- u0.wb_write(1, CR, 8'h20);
- $display("状态: %t 读+ 应答", $time);
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(1, SR, q);
- $display("状态: %t 第二个地址读结束!", $time);
- u0.wb_read(1, RXR, qq);
- if(qq !== 8'h5a)
- $display("n 错误: 需要的是 5a, received %x at time %t", qq, $time);
- // 读
- u0.wb_write(1, CR, 8'h20);
- $display("状态: %t 读 + 应答", $time);
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(1, SR, q);
- $display("状态: %t 第三个地址读完成!", $time);
- u0.wb_read(1, RXR, qq);
- $display("状态: %t 第三个地址内容是 %x !", $time, qq);
- // 读
- u0.wb_write(1, CR, 8'h28);
- $display("状态: %t 读 + 不应答!", $time);
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(1, SR, q);
- $display("状态: %t 第四个地址读完成!", $time);
- u0.wb_read(1, RXR, qq);
- $display("状态: %t 第四个地址内容为 %x !", $time, qq);
- // 检查不存在的 slave 地址
- // drive slave address
- u0.wb_write(1, TXR, 8'ha0);
- u0.wb_write(0, CR, 8'h90);
- $display("状态: %t 产生开始位, 发送命令 a0 (slave 地址+写). 检查非法地址!",$time);
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(1, SR, q); // poll it until it is zero
- $display("状态: %t 命令结束!", $time);
- // 发送内存地址
- u0.wb_write(1, TXR, 8'h10);
- u0.wb_write(0, CR, 8'h10);
- $display("状态: %t 发送 slave 内存地址 10!", $time);
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(1, SR, q);
- $display("状态: %t 地址发送完毕!", $time);
- // slave 发送不应答
- $display("状态: %t 检查不应答位!", $time);
- if(!q[7])
- $display("n 错误: 需要 NACK, 接收到 ACKn");
- // 从 slave 读数据
- u0.wb_write(1, CR, 8'h40);
- $display("状态: %t 产生'stop'位", $time);
- u0.wb_read(1, SR, q);
- while(q[1])
- u0.wb_read(1, SR, q); // poll it until it is zero
- $display("状态: %t 结束!", $time);
- #25000; // wait 25us
- $display("nn 状态: %t 测试结束!", $time);
- $finish;
- end
- endmodule
更多回帖