集成电路技术分享

 找回密码
 我要注册

QQ登录

只需一步,快速开始

搜索
查看: 995|回复: 1

!!求助 i2c verilog 程序是否成功发送接收数据 求详解

[复制链接]
zxopen08 发表于 2016-6-18 10:36:38 | 显示全部楼层 |阅读模式

/////////////////////////////////////////////////////////////////////
////                                                             ////
////  I2C slave RTL code [interfaced with register file]  ////
////                                                             ////


module I2Cslave(rst, sysclk, scl, sda, RAM_DO, RAM_DI, RAM_Addr, RAM_RW, RAM_EN);
// system interface
input rst;
input sysclk;
// I2C interface
input scl;
inout sda;
// RAM port interface
input [7:0] RAM_DO;
output [3:0] RAM_Addr;
output [7:0] RAM_DI;
output RAM_RW;
output RAM_EN;
parameter I2C_ADR = 7'b0010000;  // The 7-bits address for our I2C slave
//////////////////////////
// varialbes declaration
//////////////////////////
reg scl_r, sda_r;   // scl/sda input register
reg scl_r2, sda_r2;   // scl/sda input register second stage
reg start_con, stop_con;  // start / stop condition pulse
reg [7:0] sr;    // shift register to store the input bits
reg ld;     // bit down counter sync load signal
reg cycle_pulse;   // cycle positive pulse clock enalbe
reg cycle_pulse_n;   // cycle negative pulse clock enalbe
reg operation_dir;   // read operation ( =1 ) or write operation ( =0 )
reg [3:0] bit_cnt;   // down counter
reg sda_in;    // registed type of sda
reg sda_oe;    // sda output enable (for tristate, low active)
reg [3:0] mem_adr;      // memory address register
reg [7:0] mem_do;      // memory output register
reg memory_rw;    // memory read enable
reg ram_clken,ram_clken_r;  // SRAM clock enable
reg rw_reg_en;    // clock enable for register operation_dir
reg mem_load, mem_acc, mem_acc_r; // signals for register mem_adr
reg mem_update;
wire sda_rise, sda_fall;
wire byte_done;

// statemachine declaration
parameter start   = 3'b001;
parameter slave_addr  = 3'b010;
parameter slave_addr_ack = 3'b011;
parameter mem_addr  = 3'b100;
parameter mem_addr_ack  = 3'b101;
parameter data   = 3'b110;
parameter data_ack  = 3'b111;
reg [2:0] cs,ns;
////////////////////////////////////////////////////////////////////////////////
// rtl body
////////////////////////////////////////////////////////////////////////////////
//////////////////////////
// I2C slave <--> RAM interface logic
//////////////////////////
assign RAM_Addr = mem_adr;
assign RAM_RW = operation_dir;
assign RAM_DI = sr;
assign RAM_EN = ram_clken && ~ram_clken_r;
always @(posedge sysclk)
  ram_clken_r <= ram_clken;
always @(posedge sysclk)
if (mem_update)
  mem_do <= RAM_DO;
else if (cycle_pulse_n)
  if(!byte_done && operation_dir)
   mem_do <= {mem_do[6:0], 1'b1};  // insert 1'b1 for host ack generation

//////////////////////////
// I2C input logic
//////////////////////////
// synchronize scl and sda inputs into registers
always @(posedge sysclk or posedge rst)
   if (rst)
     begin
         scl_r <= 1'b1;
         sda_r <= 1'b1;
         scl_r2 <= 1'b1;
         sda_r2 <= 1'b1;
     end
   else
     begin
         scl_r <= scl;
         sda_r <= sda;
         scl_r2 <= scl_r;
         sda_r2 <= sda_r;
     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
assign sda_rise = sda_r && ~sda_r2;
assign sda_fall = sda_r2 && ~sda_r;
always @(posedge sysclk or posedge rst)
   if (rst)
     begin
         start_con <= 1'b0;
         stop_con <= 1'b0;
     end
   else
     begin
         start_con <= sda_fall && scl_r;
         stop_con <= sda_rise && scl_r;
     end
// generate cycle pulse signal to pace the procedure in low operation speed
always @(posedge sysclk)
   if (scl_r && ~scl_r2)
  cycle_pulse <= 1'b1;
   else
  cycle_pulse <= 1'b0;

always @(posedge sysclk)
   if (~scl_r && scl_r2)
  cycle_pulse_n <= 1'b1;
   else
  cycle_pulse_n <= 1'b0;
// store sda input into register
always @(posedge sysclk) sda_in <= sda;
//////////////////////////
// I2C incoming bits down counter & shift register
//////////////////////////

// one byte transmission done signal
assign byte_done = !(|bit_cnt);
always @(posedge sysclk)
   if (ld)
      bit_cnt <= 4'b1000;
   else
  if(cycle_pulse) bit_cnt <= bit_cnt - 4'h1;
// shift register
always @(posedge sysclk)
   if(cycle_pulse)
   sr <= {sr[6:0],sda_in};
//////////////////////////
// I2C slave address comparasion
//////////////////////////
wire addr_match = (sr[7:1] == I2C_ADR);
//////////////////////////
// register space address validation
//////////////////////////
wire addr_valid = (mem_adr <= 15);
//////////////////////////
// I2C slave tri-states sda line
//////////////////////////
assign sda = sda_oe ? 1'bz : 1'b0;
//////////////////////////
// misc logic derived from statemachine
//////////////////////////
// operation_dir logic
always @(posedge sysclk or posedge rst)
   if (rst)
  operation_dir <= 1'b0;  // default : write
   else if (rw_reg_en)
  operation_dir <= memory_rw; // store the current session r/w (r=1,w=0)
// mem_adr logic
always @(posedge sysclk)
  mem_acc_r <= mem_acc;

always @(posedge sysclk or posedge rst)
   if (rst)
  mem_adr <= 4'h0;
   else if (mem_load)
  mem_adr <= sr;   // mem address storage
   else if (mem_acc && ~mem_acc_r)
  mem_adr <= mem_adr + 8'h1; // mem internal accumulator
//////////////////////////
// I2C slave statemachine
//////////////////////////
// state machine current state flip-flop
always @(posedge sysclk or posedge rst)
   if (rst)
  cs <= start;
   else if (start_con || stop_con)
  cs <= start;
   else if (cycle_pulse_n)
  cs <= ns;
wire master_nack = sr[0];
// state machine next state codec
always @(cs, byte_done, addr_match, operation_dir, master_nack)
   case(cs) // synopsys full_case parallel_case
  start:
   ns <= slave_addr;
  slave_addr:
   if (byte_done && addr_match)
    ns <= slave_addr_ack;
   else
    ns <= slave_addr;

  slave_addr_ack:
   if(operation_dir)
    ns <= data;
   else
    ns <= mem_addr;
  mem_addr:
   if(byte_done)
    ns <= mem_addr_ack;
   else
    ns <= mem_addr;
  mem_addr_ack:
   ns <= data;
   
  data:
   if(byte_done)
    ns <= data_ack;
   else
    ns <= data;

  data_ack:
   if(operation_dir)
    if(master_nack)    // read operation && master send NACK
     ns <= start;
    else
     ns <= data;
   else
    ns <= data;
   endcase

// state machine output codec
always @(rst, cs, byte_done, addr_match, operation_dir, master_nack, addr_valid, mem_do[7], sr[0])
   if (rst)
     begin
         ld <= 1'b1;   // down counter load disable
         sda_oe <= 1'b1;   // sda output disabled
  ram_clken <= 1'b0;  // ram clock disabled  
  memory_rw <= 1'b0;
  rw_reg_en <= 1'b0;
  mem_load <= 1'b0;
  mem_acc <= 1'b0;
  mem_update <= 1'b0;
     end   
   else begin
  // state machine default output
         ld <= 1'b0;   // down counter load disable
         sda_oe <= 1'b1;   // sda output disabled
  ram_clken <= 1'b0;  // ram clock disabled  
  memory_rw <= 1'b0;
  rw_reg_en <= 1'b0;
  mem_load <= 1'b0;
  mem_acc <= 1'b0;
  mem_update <= 1'b0;
  case (cs)
             start:
   ld <= 1'b1;
             slave_addr:
               if (byte_done && addr_match)
                 begin
       rw_reg_en <= 1'b1;
       if(sr[0]) memory_rw <= 1'b1;
                 end
             slave_addr_ack:
               begin     
                   ld <= 1'b1;   // reset the down counter
     sda_oe <= 1'b0;  // generate i2c_ack
     if (operation_dir) begin
      mem_update <= 1'b1;
      ram_clken <= 1'b1;
     end
               end
             mem_addr:     // wait for memory address
        if(byte_done) mem_load <= 1'b1;  // store memory address
             mem_addr_ack:
        begin
     sda_oe <= !addr_valid;  // generate i2c_ack, for valid address                 
     ld <= 1'b1;   // reset the down counter
        end
             data:     // receive or drive data
        begin
     // read operation
                   if(operation_dir) sda_oe <= mem_do[7];  
     if(operation_dir && byte_done) mem_acc <= 1'b1;
        end
             data_ack:
               begin
     // write operation
     if(!operation_dir) mem_acc <= 1'b1;
     ram_clken <= 1'b1;
     mem_update <= 1'b1;
     
     ld <= 1'b1;
                  
     if(operation_dir) // receive ack on read
    sda_oe <= 1'b1;  
     else   // send ack on write,
    sda_oe <= 1'b0;
        end
         endcase
     end
endmodule
至芯兴洪 发表于 2016-6-22 00:01:20 | 显示全部楼层
这个自己仿真比较靠谱,别人给你说的都茫然
您需要登录后才可以回帖 登录 | 我要注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|小黑屋|手机版|Archiver|fpga论坛|fpga设计论坛 ( 京ICP备20003123号-1 )

GMT+8, 2025-5-6 07:30 , Processed in 0.055966 second(s), 20 queries .

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表