I2C协议master设备的FPGA实现
来源:互联网 发布:编程需要数学计算吗 编辑:程序博客网 时间:2024/04/20 18:32
- 需求由来
- 时序协议分析
- 架构设计
- 设计代码
- 代码说明
- STG
- 验证结果
- 小结
需求由来
I2C协议广泛用于短距板级低速通信,尤其是处理器与各类芯片诸如传感器的配置等功能。本人由于需要使用MT9V034完成图像采集,以及驱动ZedBoard板子上的ADV7511用于HDMI显示,需要用到I2C通信接口。可供选择的方案是使用PS端的I2C硬件接口、IP Catalog中的AXI I2C IP核,以及自己编写HDL。考虑到手头上的两个MT9V034模块地址相同、版权、开发时间等问题,最终选择在搜集到资料基础上自己改写。
时序协议分析
言归正传,这里上MT9V034数据手册上的时序:
协议这里就不再详述,这里主要讲讲HDL的实现思路。可以看到,时序可以划分成4种操作:起始位start、停止位stop、写Byte和读Byte(包括应答信号),进一步划分可以到相应的位操作上。所以这里采用”Bit-Byte-Command”的设计思路,由低层次到高层次设计:先设计位操作级控制器(包括start-bit、restart-bit、stop-bit、write-bit、read-bit操作),然后设计字节操作级控制器,最后是命令级控制器,控制器借助FSM。
架构设计
这里首先需要考虑的是输入和输出。输出不必说,输入应该是AXI总线,然后总线连接控制器。这里将输入端口设计成TX/RX-FIFO形式,主要考虑时钟同步——使用异步FIFO不必考虑总线时钟与模块时钟的数据同步,而寄存器表虽然控制容易且规范但是却无法避免此问题。下面简单给出了框架:
FIFO端口连接到AXI-Stream需要注意,单次发送包大小需要处理一下(或者在应用端处理一下),避免应用端写1Byte而硬件部分没有执行操作。
设计代码
i2c_master_defines.vh
`define I2C_CMD_NOP 4'b0000`define I2C_CMD_START 4'b0001`define I2C_CMD_STOP 4'b0010`define I2C_CMD_WRITE 4'b0100`define I2C_CMD_READ 4'b1000
i2c_master_bit_ctrl.v
`timescale 1ns / 10ps///////////////////////////////////////// Bit controller section///////////////////////////////////////// Translate simple commands into SCL/SDA transitions// Each command has 5 states, A/B/C/D/idle//// start: SCL ~~~~~~~~~~\____// SDA ~~~~~~~~\______// x | A | B | C | D | i//// repstart SCL ____/~~~~\___// SDA __/~~~\______// x | A | B | C | D | i//// stop SCL ____/~~~~~~~~// SDA ==\____/~~~~~// x | A | B | C | D | i////- write SCL ____/~~~~\____// SDA ==X=========X=// x | A | B | C | D | i////- read SCL ____/~~~~\____// SDA XXXX=====XXXX// x | A | B | C | D | i//// Timing: Normal mode Fast mode///////////////////////////////////////////////////////////////////////// Fscl 100KHz 400KHz// Th_scl 4.0us 0.6us High period of SCL// Tl_scl 4.7us 1.3us Low period of SCL// Tsu:sta 4.7us 0.6us setup time for a repeated start condition// Tsu:sto 4.0us 0.6us setup time for a stop conditon// Tbuf 4.7us 1.3us Bus free time between a stop and start condition//// synopsys translate_off//`include "timescale.v"// synopsys translate_on`include "i2c_master_defines.vh"module i2c_master_bit_ctrl( clk, rst, clk_cnt, ena, cmd, cmd_ack, busy, al, din, dout, scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen ); // // inputs & outputs // input clk; input rst; input ena; // core enable signal input [15:0] clk_cnt; // clock prescale value input [3:0] cmd; output cmd_ack; // command complete acknowledge reg cmd_ack; output busy; // i2c bus busy reg busy; output al; // i2c bus arbitration lost reg al; input din; output dout; reg dout; // I2C lines input scl_i; // i2c clock line input output scl_o; // i2c clock line output output scl_oen; // i2c clock line output enable (active low) reg scl_oen = 1'b1; input sda_i; // i2c data line input output sda_o; // i2c data line output output sda_oen; // i2c data line output enable (active low) reg sda_oen = 1'b1; // // variable declarations // reg sSCL, sSDA; // synchronized SCL and SDA inputs reg dscl_oen; // delayed scl_oen reg sda_chk; // check SDA output (Multi-master arbitration) reg clk_en; // clock generation signals wire slave_wait;// reg [15:0] cnt = clk_cnt; // clock divider counter (simulation) reg [15:0] cnt; // clock divider counter (synthesis) // // module body // // whenever the slave is not ready it can delay the cycle by pulling SCL low // delay scl_oen always @(posedge clk) dscl_oen <= #1 scl_oen; assign slave_wait = dscl_oen && !sSCL; // generate clk enable signal always @(posedge clk) if (rst) begin cnt <= #1 16'h0; clk_en <= #1 1'b1; end else if ( ~|cnt || ~ena) if (~slave_wait) begin cnt <= #1 clk_cnt; clk_en <= #1 1'b1; end else begin cnt <= #1 cnt; clk_en <= #1 1'b0; end else begin cnt <= #1 cnt - 16'h1; clk_en <= #1 1'b0; end // generate bus status controller reg dSCL, dSDA; reg sta_condition; reg sto_condition; // synchronize SCL and SDA inputs // reduce metastability risc always @(posedge clk) if (rst) begin sSCL <= #1 1'b1; sSDA <= #1 1'b1; dSCL <= #1 1'b1; dSDA <= #1 1'b1; end else begin sSCL <= #1 scl_i; sSDA <= #1 sda_i; dSCL <= #1 sSCL; dSDA <= #1 sSDA; end // detect start condition => detect falling edge on SDA while SCL is high // detect stop condition => detect rising edge on SDA while SCL is high always @(posedge clk) if (rst) begin sta_condition <= #1 1'b0; sto_condition <= #1 1'b0; end else begin sta_condition <= #1 ~sSDA & dSDA & sSCL; sto_condition <= #1 sSDA & ~dSDA & sSCL; end // generate i2c bus busy signal always @(posedge clk) if (rst) busy <= #1 1'b0; else busy <= #1 (sta_condition | busy) & ~sto_condition; // generate arbitration lost signal // aribitration lost when: // 1) master drives SDA high, but the i2c bus is low // 2) stop detected while not requested reg cmd_stop, dcmd_stop; always @(posedge clk) if (rst) begin cmd_stop <= #1 1'b0; dcmd_stop <= #1 1'b0; al <= #1 1'b0; end else begin cmd_stop <= #1 cmd == `I2C_CMD_STOP; dcmd_stop <= #1 cmd_stop; al <= #1 (sda_chk & ~sSDA & sda_oen) | (sto_condition & ~dcmd_stop); end // generate dout signal (store SDA on rising edge of SCL) always @(posedge clk) if(sSCL & ~dSCL) dout <= #1 sSDA; // generate statemachine // nxt_state decoder parameter [17:0] idle = 18'b0_0000_0000_0000_00000; parameter [17:0] start_a = 18'b0_0000_0000_0000_00010; parameter [17:0] start_b = 18'b0_0000_0000_0000_00100; parameter [17:0] start_c = 18'b0_0000_0000_0000_01000; parameter [17:0] start_d = 18'b0_0000_0000_0000_10000; parameter [17:0] start_e = 18'b0_0000_0000_0001_00000; parameter [17:0] stop_a = 18'b0_0000_0000_0010_00000; parameter [17:0] stop_b = 18'b0_0000_0000_0100_00000; parameter [17:0] stop_c = 18'b0_0000_0000_1000_00000; parameter [17:0] stop_d = 18'b0_0000_0001_0000_00000; parameter [17:0] rd_a = 18'b0_0000_0010_0000_00000; parameter [17:0] rd_b = 18'b0_0000_0100_0000_00000; parameter [17:0] rd_c = 18'b0_0000_1000_0000_00000; parameter [17:0] rd_d = 18'b0_0001_0000_0000_00000; parameter [17:0] wr_a = 18'b0_0010_0000_0000_00000; parameter [17:0] wr_b = 18'b0_0100_0000_0000_00000; parameter [17:0] wr_c = 18'b0_1000_0000_0000_00000; parameter [17:0] wr_d = 18'b1_0000_0000_0000_00000; reg [17:0] c_state; // synopsis enum_state always @(posedge clk) if (rst | al) begin c_state <= #1 idle; cmd_ack <= #1 1'b0; scl_oen <= #1 1'b1; sda_oen <= #1 1'b1; sda_chk <= #1 1'b0; end else begin cmd_ack <= #1 1'b0; // default no command acknowledge + assert cmd_ack only 1clk cycle if (clk_en) case (c_state) // synopsis full_case parallel_case // idle state idle: begin case (cmd) // synopsis full_case parallel_case `I2C_CMD_START: c_state <= #1 start_a; `I2C_CMD_STOP: c_state <= #1 stop_a; `I2C_CMD_WRITE: c_state <= #1 wr_a; `I2C_CMD_READ: c_state <= #1 rd_a; default: c_state <= #1 idle; endcase scl_oen <= #1 scl_oen; // keep SCL in same state sda_oen <= #1 sda_oen; // keep SDA in same state sda_chk <= #1 1'b0; // don't check SDA output end // start start_a: begin c_state <= #1 start_b; scl_oen <= #1 scl_oen; // keep SCL in same state sda_oen <= #1 1'b1; // set SDA high sda_chk <= #1 1'b0; // don't check SDA output end start_b: begin c_state <= #1 start_c; scl_oen <= #1 1'b1; // set SCL high sda_oen <= #1 1'b1; // keep SDA high sda_chk <= #1 1'b0; // don't check SDA output end start_c: begin c_state <= #1 start_d; scl_oen <= #1 1'b1; // keep SCL high sda_oen <= #1 1'b0; // set SDA low sda_chk <= #1 1'b0; // don't check SDA output end start_d: begin c_state <= #1 start_e; scl_oen <= #1 1'b1; // keep SCL high sda_oen <= #1 1'b0; // keep SDA low sda_chk <= #1 1'b0; // don't check SDA output end start_e: begin c_state <= #1 idle; cmd_ack <= #1 1'b1; scl_oen <= #1 1'b0; // set SCL low sda_oen <= #1 1'b0; // keep SDA low sda_chk <= #1 1'b0; // don't check SDA output end // stop stop_a: begin c_state <= #1 stop_b; scl_oen <= #1 1'b0; // keep SCL low sda_oen <= #1 1'b0; // set SDA low sda_chk <= #1 1'b0; // don't check SDA output end stop_b: begin c_state <= #1 stop_c; scl_oen <= #1 1'b1; // set SCL high sda_oen <= #1 1'b0; // keep SDA low sda_chk <= #1 1'b0; // don't check SDA output end stop_c: begin c_state <= #1 stop_d; scl_oen <= #1 1'b1; // keep SCL high sda_oen <= #1 1'b0; // keep SDA low sda_chk <= #1 1'b0; // don't check SDA output end stop_d: begin c_state <= #1 idle; cmd_ack <= #1 1'b1; scl_oen <= #1 1'b1; // keep SCL high sda_oen <= #1 1'b1; // set SDA high sda_chk <= #1 1'b0; // don't check SDA output end // read rd_a: begin c_state <= #1 rd_b; scl_oen <= #1 1'b0; // keep SCL low sda_oen <= #1 1'b1; // tri-state SDA sda_chk <= #1 1'b0; // don't check SDA output end rd_b: begin c_state <= #1 rd_c; scl_oen <= #1 1'b1; // set SCL high sda_oen <= #1 1'b1; // keep SDA tri-stated sda_chk <= #1 1'b0; // don't check SDA output end rd_c: begin c_state <= #1 rd_d; scl_oen <= #1 1'b1; // keep SCL high sda_oen <= #1 1'b1; // keep SDA tri-stated sda_chk <= #1 1'b0; // don't check SDA output end rd_d: begin c_state <= #1 idle; cmd_ack <= #1 1'b1; scl_oen <= #1 1'b0; // set SCL low sda_oen <= #1 1'b1; // keep SDA tri-stated sda_chk <= #1 1'b0; // don't check SDA output end // write wr_a: begin c_state <= #1 wr_b; scl_oen <= #1 1'b0; // keep SCL low sda_oen <= #1 din; // set SDA sda_chk <= #1 1'b0; // don't check SDA output (SCL low) end wr_b: begin c_state <= #1 wr_c; scl_oen <= #1 1'b1; // set SCL high sda_oen <= #1 din; // keep SDA sda_chk <= #1 1'b1; // check SDA output end wr_c: begin c_state <= #1 wr_d; scl_oen <= #1 1'b1; // keep SCL high sda_oen <= #1 din; sda_chk <= #1 1'b1; // check SDA output end wr_d: begin c_state <= #1 idle; cmd_ack <= #1 1'b1; scl_oen <= #1 1'b0; // set SCL low sda_oen <= #1 din; sda_chk <= #1 1'b0; // don't check SDA output (SCL low) end endcase end // assign scl and sda output (always gnd) assign scl_o = 1'b0; assign sda_o = 1'b0;endmodule
i2c_master_byte_ctrl.v
`timescale 1ns / 10ps`include "i2c_master_defines.vh"module i2c_master_byte_ctrl ( clk, rst, ena, clk_cnt, start, stop, read, write, ack_in, din, cmd_ack, ack_out, dout, i2c_busy, i2c_al, scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen ); // // inputs & outputs // input clk; // master clock input rst; // synchronous active high reset input ena; // core enable signal input [15:0] clk_cnt; // 4x SCL // control inputs input start; input stop; input read; input write; input ack_in; input [7:0] din; // status outputs output cmd_ack; reg cmd_ack; output ack_out; reg ack_out; output i2c_busy; output i2c_al; output [7:0] dout; // I2C signals input scl_i; output scl_o; output scl_oen; input sda_i; output sda_o; output sda_oen; // // Variable declarations // // statemachine parameter [5:0] ST_IDLE = 6'b0_00001; parameter [5:0] ST_START = 6'b0_00010; parameter [5:0] ST_READ = 6'b0_00100; parameter [5:0] ST_WRITE = 6'b0_01000; parameter [5:0] ST_ACK = 6'b0_10000; parameter [5:0] ST_STOP = 6'b1_00000; // signals for bit_controller reg [3:0] core_cmd; reg core_txd; wire core_ack, core_rxd; // signals for shift register reg [7:0] sr; //8bit shift register reg shift, ld; // signals for state machine wire go; reg [2:0] dcnt; wire cnt_done; // // Module body // // hookup bit_controller i2c_master_bit_ctrl bit_controller ( .clk ( clk ), .rst ( rst ), .ena ( ena ), .clk_cnt ( clk_cnt ), .cmd ( core_cmd ), .cmd_ack ( core_ack ), .busy ( i2c_busy ), .al ( i2c_al ), .din ( core_txd ), .dout ( core_rxd ), .scl_i ( scl_i ), .scl_o ( scl_o ), .scl_oen ( scl_oen ), .sda_i ( sda_i ), .sda_o ( sda_o ), .sda_oen ( sda_oen ) ); // generate go-signal assign go = (read | write | stop) & ~cmd_ack; // assign dout output to shift-register assign dout = sr; // generate shift register always @(posedge clk) if (rst) sr <= #1 8'h0; else if (ld) sr <= #1 din; else if (shift) sr <= #1 {sr[6:0], core_rxd}; // generate counter always @(posedge clk) if (rst) dcnt <= #1 3'h0; else if (ld) dcnt <= #1 3'h7; else if (shift) dcnt <= #1 dcnt - 3'h1; assign cnt_done = ~(|dcnt); // // state machine // reg [5:0] c_state; // synopsis enum_state always @(posedge clk) if (rst | i2c_al) begin core_cmd <= #1 `I2C_CMD_NOP; core_txd <= #1 1'b0; shift <= #1 1'b0; ld <= #1 1'b0; cmd_ack <= #1 1'b0; c_state <= #1 ST_IDLE; ack_out <= #1 1'b0; end else begin // initially reset all signals core_txd <= #1 sr[7]; shift <= #1 1'b0; ld <= #1 1'b0; cmd_ack <= #1 1'b0; case (c_state) // synopsis full_case parallel_case ST_IDLE: if (go) begin if (start) begin c_state <= #1 ST_START; core_cmd <= #1 `I2C_CMD_START; end else if (read) begin c_state <= #1 ST_READ; core_cmd <= #1 `I2C_CMD_READ; end else if (write) begin c_state <= #1 ST_WRITE; core_cmd <= #1 `I2C_CMD_WRITE; end else // stop begin c_state <= #1 ST_STOP; core_cmd <= #1 `I2C_CMD_STOP; // generate command acknowledge signal cmd_ack <= #1 1'b1; end ld <= #1 1'b1; end ST_START: if (core_ack) begin if (read) begin c_state <= #1 ST_READ; core_cmd <= #1 `I2C_CMD_READ; end else begin c_state <= #1 ST_WRITE; core_cmd <= #1 `I2C_CMD_WRITE; end ld <= #1 1'b1; end ST_WRITE: if (core_ack) if (cnt_done) begin c_state <= #1 ST_ACK; core_cmd <= #1 `I2C_CMD_READ; end else begin c_state <= #1 ST_WRITE; // stay in same state core_cmd <= #1 `I2C_CMD_WRITE; // write next bit shift <= #1 1'b1; end ST_READ: if (core_ack) begin if (cnt_done) begin c_state <= #1 ST_ACK; core_cmd <= #1 `I2C_CMD_WRITE; end else begin c_state <= #1 ST_READ; // stay in same state core_cmd <= #1 `I2C_CMD_READ; // read next bit end shift <= #1 1'b1; core_txd <= #1 ack_in; end ST_ACK: if (core_ack) begin if (stop) begin c_state <= #1 ST_STOP; core_cmd <= #1 `I2C_CMD_STOP; end else begin c_state <= #1 ST_IDLE; core_cmd <= #1 `I2C_CMD_NOP; end // assign ack_out output to bit_controller_rxd (contains last received bit) ack_out <= #1 core_rxd; // generate command acknowledge signal cmd_ack <= #1 1'b1; core_txd <= #1 1'b1; end else core_txd <= #1 ack_in; ST_STOP: if (core_ack) begin c_state <= #1 ST_IDLE; core_cmd <= #1 `I2C_CMD_NOP; end endcase endendmodule
i2c_master_cmd_ctrl.v
`timescale 1ns / 10psmodule i2c_master_cmd_ctrl( input clk, input rst, input ena, //commands output reg start, output reg stop, output reg write, output reg read, output reg ack_out, //data output reg [7:0] txd, input wire [7:0] rxd, //status input wire cmd_ack, input wire ack_in, input wire i2c_busy, input wire i2c_al, //tx_fifo output wire rd_en, input wire empty, input wire [7:0] dout, //rx_fifo output reg wr_en, input wire full, output reg [7:0] din, // output reg missed_ack ); //parameter localparam [10:0] ST_IDLE = 11'b000_0000_0001, ST_CFG = 11'b000_0000_0010, ST_TXDEVICE = 11'b000_0000_0100, ST_TXDEVICEW = 11'b000_0000_1000, ST_TXADDR = 11'b000_0001_0000, ST_TXADDRW = 11'b000_0010_0000, ST_TXDATA = 11'b000_0100_0000, ST_TXDATAW = 11'b000_1000_0000, ST_RXDATA = 11'b001_0000_0000, ST_RXDATAW = 11'b010_0000_0000, ST_STOP = 11'b100_0000_0000; //variables reg [10:0] CS,NS; reg [1:0] channel; reg [5:0] length; reg [7:0] device_addr; reg device_addr_resend; //state_machine //current state always @(posedge clk) begin if(rst) CS<= #1 ST_IDLE; else if(ena) CS<= #1 NS; end //next state always @(*) begin case(CS) ST_IDLE: NS<=(empty)? ST_IDLE:ST_CFG; ST_CFG: NS<=ST_TXDEVICE; ST_TXDEVICE:NS<=ST_TXDEVICEW; ST_TXDEVICEW:NS<=(cmd_ack)? ((device_addr_resend)? ST_RXDATA:ST_TXADDR):ST_TXDEVICEW; ST_TXADDR: NS<=ST_TXADDRW; ST_TXADDRW: NS<=(cmd_ack)? ((device_addr[0])? ST_TXDEVICE:ST_TXDATA):ST_TXADDRW; ST_TXDATA: NS<=ST_TXDATAW; ST_TXDATAW: NS<=(cmd_ack)? ((length==6'd0)? ST_STOP:ST_TXDATA):ST_TXDATAW; ST_RXDATA: NS<=ST_RXDATAW; ST_RXDATAW: NS<=(cmd_ack)? ((length==6'd0)? ST_STOP:ST_RXDATA):ST_RXDATAW; ST_STOP: NS<=ST_IDLE; default: NS<=ST_IDLE; endcase end //outputs //start always @(posedge clk) begin if(rst) start<= #1 1'b0; else if(ena) start<= #1 (CS==ST_TXDEVICE)? 1'b1:1'b0; end //stop always @(posedge clk) begin if(rst) stop<= #1 1'b0; else if(ena) stop<= #1 (CS==ST_STOP)? 1'b1:1'b0; end //write always @(posedge clk) begin if(rst) write<= #1 1'b0; else if(ena) write<= #1 ((CS==ST_TXDEVICE)||(CS==ST_TXADDR)||(CS==ST_TXDATA))? 1'b1:1'b0; end //read always @(posedge clk) begin if(rst) read<= #1 1'b0; else if(ena) read<= #1 (CS==ST_RXDATA)? 1'b1:1'b0; end //ack_out always @(posedge clk) begin if(rst) ack_out<= #1 1'b0; else if(ena) ack_out<= #1 ((CS==ST_RXDATAW)&&(length!=0))? 1'b0:1'b1; end //length always @(posedge clk) begin if(rst) length<= #1 6'd0; else if(ena) begin if(CS==ST_CFG) length<= #1 dout[7:2]; else if((CS==ST_TXDATA)||(CS==ST_RXDATA)) length<= #1 length-6'd1; end end //channel always @(posedge clk) begin if(rst) channel<= #1 2'd0; else if(ena) begin if(CS==ST_CFG) channel<= #1 dout[1:0]; end end //device_addr always @(posedge clk) begin if(rst) device_addr<= #1 8'd0; else if(ena) begin if(CS==ST_STOP) device_addr<= #1 8'd0; else if((CS==ST_TXDEVICE)&&(device_addr_resend==0)) device_addr<= #1 dout; end end //device_addr_resend always @(posedge clk) begin if(rst) device_addr_resend<= #1 1'd0; else if(ena) begin if(CS==ST_STOP) device_addr_resend<= #1 1'd0; else if((CS==ST_TXADDR)&&(device_addr[0]==1)) device_addr_resend<= #1 1'd1; end end //missed_ack always @(posedge clk) begin if(rst) missed_ack<= #1 1'b0; else if(ena) begin if(((CS==ST_TXDEVICEW)||(CS==ST_TXADDRW)||(CS==ST_TXDATAW))&&cmd_ack) missed_ack<= #1 ack_in; else missed_ack<= #1 1'b0; end end //fifo_tx //rd_en assign rd_en=((NS==ST_CFG)||(NS==ST_TXDEVICE)||(NS==ST_TXADDR)|| (NS==ST_TXDATA))? 1'b1:1'b0; always @(posedge clk) begin if(rst) txd<= #1 8'd0; else if(ena) begin if(CS==ST_TXDEVICE) txd<= #1 (device_addr_resend)? device_addr:(dout&8'hfe); else if((CS==ST_TXADDR)||(CS==ST_TXDATA)) txd<= #1 dout; end//txd<= #1 ((CS==ST_TXDEVICE)||(CS==ST_TXADDR)||(CS==ST_TXDATA))? dout:txd; end //fifo_rx //wr_en always @(posedge clk) begin if(rst) wr_en<= #1 1'b0; else if(ena) wr_en<= #1 ((CS==ST_RXDATAW) && cmd_ack && ~i2c_al)? 1'b1:1'b0; end always @(posedge clk) begin if(rst) din<= #1 8'd0; else if(ena) din<= #1 rxd; endendmodule
i2c_master.v
/* * An i2c master controller implementation. 7-bit address 8-bit data, r/w. * * Copyright (c) 2015 Joel Fernandes <joel@linuxinternals.org> * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */`timescale 1ns / 10psmodule i2c_master( input wire clk, input wire rst, input wire ena, // Xillybus input wire bus_clk, input wire quiesce, // Wires related to /dev/xillybus_read_8 input wire user_r_read_8_rden, output wire user_r_read_8_empty, output wire [7:0] user_r_read_8_data, input wire user_r_read_8_open, // Wires related to /dev/xillybus_write_8 input wire user_w_write_8_wren, output wire user_w_write_8_full, input wire [7:0] user_w_write_8_data, input wire user_w_write_8_open, // Wires of i2c inout wire scl_pin, inout wire sda_pin, //output wire scl_oen, //output wire sda_oen, //missed_ack output wire missed_ack ); /* variables */ wire tx_fifo_rd_en,rx_fifo_wr_en; wire tx_fifo_empty,rx_fifo_full; wire [7:0] tx_fifo_dout,rx_fifo_din; wire start,stop,read,write; wire ack_out,ack_in; wire cmd_ack; wire [7:0] txd,rxd; wire i2c_busy,i2c_al; wire scl_o,scl_oen; wire sda_o,sda_oen; /* connection */ assign scl_pin = (scl_oen==1)? 1'bz:scl_o; assign scl_i = scl_pin; assign sda_pin = (sda_oen==1)? 1'bz:sda_o; assign sda_i = sda_pin; /* module initialization */ // hookup the tx_fifo block i2c_master_fifo FIFO_TX ( .rst ( quiesce&~user_w_write_8_open), // input wire rst .wr_clk ( bus_clk ), // input wire wr_clk .rd_clk ( clk ), // input wire rd_clk .din ( user_w_write_8_data), // input wire [7 : 0] din .wr_en ( user_w_write_8_wren), // input wire wr_en .rd_en ( tx_fifo_rd_en ), // input wire rd_en .dout ( tx_fifo_dout ), // output wire [7 : 0] dout .full ( user_w_write_8_full), // output wire full .empty ( tx_fifo_empty ) // output wire empty ); // hookup the rx_fifo block i2c_master_fifo FIFO_RX ( .rst ( quiesce&~user_r_read_8_open), // input wire rst .wr_clk ( clk ), // input wire wr_clk .rd_clk ( bus_clk ), // input wire rd_clk .din ( rx_fifo_din ), // input wire [7 : 0] din .wr_en ( rx_fifo_wr_en ), // input wire wr_en .rd_en ( user_r_read_8_rden ), // input wire rd_en .dout ( user_r_read_8_data ), // output wire [7 : 0] dout .full ( rx_fifo_full ), // output wire full .empty ( user_r_read_8_empty) // output wire empty ); // hookup the command controller block i2c_master_cmd_ctrl DUT_CMD( .clk ( clk ), .rst ( rst ), .ena ( ena ), .start ( start ), .stop ( stop ), .write ( write ), .read ( read ), .ack_out ( ack_out ), .txd ( txd ), .rxd ( rxd ), .cmd_ack ( cmd_ack ), .ack_in ( ack_in ), .i2c_busy ( i2c_busy ), .i2c_al ( i2c_al ), .rd_en ( tx_fifo_rd_en), .empty ( tx_fifo_empty), .dout ( tx_fifo_dout ), .wr_en ( rx_fifo_wr_en), .full ( rx_fifo_full ), .din ( rx_fifo_din ), .missed_ack(missed_ack ) ); // hookup byte controller block i2c_master_byte_ctrl DUT_BYTE ( .clk ( clk ), .rst ( rst ), .ena ( ena ), .clk_cnt ( 16'h0003 ), .start ( start ), .stop ( stop ), .read ( read ), .write ( write ), .ack_in ( ack_out ), .din ( txd ), .cmd_ack ( cmd_ack ), .ack_out ( ack_in ), .dout ( rxd ), .i2c_busy ( i2c_busy ), .i2c_al ( i2c_al ), .scl_i ( scl_i ), .scl_o ( scl_o ), .scl_oen ( scl_oen ), .sda_i ( sda_i ), .sda_o ( sda_o ), .sda_oen ( sda_oen ) );endmodule
代码说明
这里用到了一个FIFO IP核,Vivado中调用并配置:
模块输入时钟为8MHz;
写操作方法按照如下顺序依次向FIFO_TX写入字节:
配置字节(1Byte)->从设备地址(1Byte)->寄存器地址(1Byte)->数据(nBytes);
读操作方法按照写操作的顺序写入FIFO_RX,省去最后的数据部分,逻辑完成操作后会把指定字节数据写入FIFO_RX中,用户可以从中读取期望的数据;
需要说明的是,以上配置字节的高6位为本次操作的数据长短,低2位用于切换i2c通道(reserved),从设备地址的第0位指示本次是读还是写操作。
STG :
篇幅限制,这里简单给出i2c_master_cmd_ctrl的状态转移图:
验证结果
这里设计的功能是单主设备的读写操作,注意并不支持多主设备总线操作;验证时,写操作用功能仿真的方法很容易,写操作还要建立i2c_slave模型。为了简化,直接使用Chipscope的逻辑分析仪功能板上调试:
write测试:
read测试:
以上写测试时依次向FIFO_TX写入0x09-0x90-0x01-0x00-0xaa
从设备地址:0x90
存储地址:0x01
写入数据:0x00aa
然后读出向FIFO_RX写入0x09-0x91-0x00
从设备地址:0x90
存储地址:0x00
读出数据:0x1324
这符合MT9V034的默认数值:
我用的测试办法是将逻辑接入xillybus测试的,实际上也可以组织成AXI-stream总线,在SDK中裸机测试。
小结
(1)本文工作的部分代码取自网站电子发烧友,这里将根据需求将原来的wishbone总线去掉并改写成FIFO的接口模式,便于连接其他总线
(2)工程通过了板上测试,具备master端的基本读写能力,操作简单,需要的可以借鉴一下
- I2C协议master设备的FPGA实现
- I2C总线通讯协议中主机模块的FPGA实现
- I2C FPGA实现
- FPGA的I2C程序
- 千兆以太网TCP协议的FPGA实现。
- I2C串行总线协议的VHDL实现
- I2C总线协议的verilog实现
- I2C设备的创建
- 基于ARM的I2C设备控制方法的实现
- FPGA协议的写法
- I2C 设备驱动程序 标准实现
- I2C协议的理解
- 嵌入式Linux中I2C设备驱动程序的研究与实现
- 学习笔记 --- LINUX I2C设备驱动的实现
- c51-i2c协议代码实现
- amlogic平台android uboot中添加i2c设备实现i2c的读写
- amlogic平台android 系统linux内核中添加i2c设备实现i2c的读写
- PCI总线协议的FPGA实现及驱动设计
- 通过了解JavaScript来更好的编写代码与程序
- 【hpu oj 1401 多少个元素 set】
- 使用sql语句复制表
- nginx配置详解
- 【JQuery】基础整理
- I2C协议master设备的FPGA实现
- 快速使用FileProvider解决Android7.0文件权限问题
- POJ2159
- 迈向全栈开发学习(2)
- 黑客与画家
- 51nod1069 Nim游戏
- JPEG标准DCT优化实现
- 仿支付宝支付 密码输入
- Cannot find module “angular-in-memory-web-api”