zxopen08 发表于 2016-6-18 10:36:38

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


/////////////////////////////////////////////////////////////////////
////                                                             ////
////I2C slave RTL code ////
////                                                             ////


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 RAM_DO;
output RAM_Addr;
output 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 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 bit_cnt;   // down counter
reg sda_in;    // registed type of sda
reg sda_oe;    // sda output enable (for tristate, low active)
reg mem_adr;      // memory address register
reg 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 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, 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,sda_in};
//////////////////////////
// I2C slave address comparasion
//////////////////////////
wire addr_match = (sr == 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;
// 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, sr)
   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) 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;
   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]
查看完整版本: !!求助 i2c verilog 程序是否成功发送接收数据 求详解