1
\$\begingroup\$

I'm getting an empty netlist after running the synthesis on the I2C Slave. The inputs (SCL, RESET) and the inout SDA line are defined in the top module, and also the behavioural simulation is running properly. Is the reason because of deletion of a source file? If so, how can I check if it has been deleted? Or is it because it's doing some sort of optimization? I'm attaching the codes so it can be checked by others and it can help me figure out that if there is a problem with my vivado itself.

I2C SLAVE:

`timescale 1ns / 1ps

module I2C_SLAVE #(parameter SLAVE_ADDR = 7'b1101010)    // Slave Address for this module is 0x6A
    (
     input Reset_L,    // (WIRE) Active Low Reset to clear the data within slave and reset the registers
     input SCL,        // (WIRE) Serial Clock 
     inout SDA         // (WIRE) Serial Data
    );
    
    // STATES OF STATE MACHINE BETWEEN START AND STOP CONDITION
    localparam ADDRESS   = 3'b000;     // Address from the master is read and checked with slave address
    localparam ADDR_ACKN = 3'b001;     // Acknowledgement from slave to master is sent from if address matches
    localparam RECEIVE   = 3'b010;     // If Address last bit is 0 then data is received from master to slave (WRITE OPERATION)
    localparam RX_ACKN   = 3'b011;     // Sends an Acknowledgement to master after the data is received
    localparam TRANSMIT  = 3'b100;     // If Address last bit is 1 then data is transmitted from slave to master (READ OPERATION)
    localparam TX_ACKN   = 3'b101;     // Waits for Acknowledgement from master after the data is transmitted
    localparam HOLD      = 3'b110;     // It is the rest state which implies slave is not active
    
    // STATE MACHINE VARIABLE
    reg [2:0] state_machine;
    
    // REGISTERS FOR INTERNAL OPERATIONS
    reg       busy = 1'b0;                    // Used to activate and deactivate FSM (SLAVE)
    
    reg [7:0] address;                 // Used to hold the input serial data
    reg [2:0] bit_index;               // Used in accessing each bit of address and data register
    
    reg [7:0] slave_data;             // Data held by the slave
    
    // CONTROL REGISTERS TO ACTIVATE OUTPUT FUNCTION OF SDA
    reg       SDA_EN;                  // Controls when the SDA should act as output
    reg       SDA_OUT;                 // Holds the serial data to be transmitted from slave
    
    // SDA INPUT AND OUTPUT TRANSITION
    assign SDA = (SDA_EN) ? SDA_OUT : 1'bz;    // [SDA_EN == 1] => O/P pin and [SDA_EN == 0] => I/P pin
    
    // START CONDITION
    always @(negedge SDA)              // If SDA goes low when SCL is high then start condition occurs
    begin
        if (SCL == 1)                  // If busy is already high then repeated start condition occurs
        begin
            address       <= 8'b0;     // Address reg is reset to store the new address transmitted from master
            bit_index     <= 3'b111;   // Bit Index reg is preset as the first data transmitted will be MSB
            state_machine <= ADDRESS;  // SM variable is set to first state where address is obtained
            if (busy == 0)
                busy <= 1'b1;          // Busy reg is made high indicating the slave FSM is active
        end
    end
    
     // STOP CONDITION
    always @(posedge SDA)              // If SDA goes high when SCL is high then stop condition occurs
    begin
        if ((busy == 1) && (SCL == 1))
            busy <= 1'b0;              // Busy reg is made low indicating the slave FSM is inactive
    end
    
    // FSM FOR OPERATIONS
    always @(posedge SCL)              // SDA will be checked during SCL high condition
    begin
        if (~Reset_L)                  // Clears the data held by slave
        begin
            SDA_EN     <= 1'b0;
            SDA_OUT    <= 1'b0;
            slave_data <= 8'b0;
        end
        
        if (busy == 1)
        begin
            case(state_machine)
                ADDRESS:
                begin
                    if (bit_index > 0)
                    begin
                        address[bit_index] <= SDA;
                        bit_index <= bit_index - 1;  // Bit index reg is decremented until last bit is received 
                     end
                     else
                       state_machine <= ADDR_ACKN;               
                end
                
                ADDR_ACKN:
                begin
                    if (address[7:1] == SLAVE_ADDR)          // Compares the address transmitted by master with the slave's address
                    begin
                    // If address matched, based on operation to be performed the FSM moves to next state
                        bit_index <= 3'b111;                 // Presets the bit index reg
                        if(address[0] == 0)                  // If Address Bit[0] == 0 then slave becomes data receiver
                            state_machine <= RECEIVE;        // WRITE OPERATION
                        else                                 // If Address Bit[0] == 1 then slave becomes data transmitter
                            state_machine <= TRANSMIT;       // READ OPERATION
                    end
                    else
                        state_machine <= HOLD;               // If address does not match slave moves to HOLD state
                end
                
                RECEIVE:
                begin
                    slave_data[bit_index] <= SDA;            // Serial data is written into to the slave data buffer reg
                    if (bit_index == 0)
                        state_machine <= RX_ACKN;            // FSM moves to acknowledgement state where slave sends to master
                    else
                        bit_index <= bit_index - 1;          // Bit index reg is decremented until last bit is received 
                end
                
                RX_ACKN:
                begin
                    bit_index     <= 3'b111;                 // Presets the bit index reg
                    state_machine <= ADDRESS;                // FSM moves to ADDRESS state after sending an acknowledgement so that master can perform operations on other slave if interested
                end                                          
                
                TRANSMIT:
                begin
                    if (bit_index == 0)
                        state_machine <= TX_ACKN;            // FSM moves to acknowledgement state where the master sends to slave
                    else
                        bit_index <= bit_index - 1;          // Bit index reg is decremented until last bit is received 
                end
                
                TX_ACKN:
                begin
                    if (SDA == 0)                            // After getting acknowledgement from master the FSM moves to ADDRESS state
                    begin
                        bit_index     <= 3'b111;             // Presets the bit index reg
                        state_machine <= ADDRESS;            // Master can perform operations on other slaves or stop
                    end
                    else                                     // If acknowledgement is not received from the master then slave enters HOLD state
                        state_machine <= HOLD;
                end
                
                default:                                     //  The default state will be HOLD state where the slave will not respond or interact with the master
                    state_machine <= HOLD;
            endcase
        end
    end
    
    // ACKNOWLEDGEMENT AND DATA IS TRANSMITTED BY ENABLING SDA AS OUTPUT PIN
    always @(negedge SCL)              // Change in SDA needs to take place only during SCL low condition
    begin
        case(state_machine)         
            ADDR_ACKN:
            begin                                    // SDA will become output pin as slave needs to send acknowledgement to master
                SDA_EN  <= 1'b1;
                SDA_OUT <= 1'b0;    
            end
            
            RX_ACKN:                                 // Write Acknowledgement
            begin                                    // SDA will become output pin as slave needs to send acknowledgement to master
                SDA_EN  <= 1'b1;
                SDA_OUT <= 1'b0;    
            end
            
            TRANSMIT:                                // Read Acknowledgement
            begin                                    // SDA will become output pin as data held by slave needs to be trasmitted to master
                SDA_EN  <= 1'b1;
                SDA_OUT <= slave_data[bit_index];    // Data is fed serially to SDA output pin
            end
            
            default:
                SDA_EN <= 1'b0;                      // By default SDA will be kept as input pin unless SDA should act as output pin
        endcase
    end
endmodule

I2C_SLAVE_TB:

`timescale 1ns / 1ps

// TIME PERIOD OF SCL = 4 microseconds
// TIME PERIOD OF FPGA CLOCK = 10 nanoseconds

module I2C_SLAVE_TB();

reg clock, reset_l, reset_l_generator;

wire o_scl_gen, SCL;
wire SDA;

reg sda_tb, sda_drive, scl_en, scl_tb;

SCL_GENERATOR scl_generator_inst(clock, reset_l_generator, o_scl_gen);
I2C_SLAVE i2c_slave_inst(reset_l, SCL, SDA);

assign SCL = (scl_en) ? scl_tb : o_scl_gen;
assign SDA = (sda_drive) ? 1'bz : sda_tb;
pullup(SDA);

initial
begin
    reset_l = 1'b0;
    reset_l_generator = 1'b0;
    scl_en =1'b1;
    scl_tb = 1'b1;
    clock = 1'b0;
    forever #5 clock = ~clock;
end

initial
begin
    sda_drive = 1'b0;
    sda_tb = 1;
    #2000 sda_tb = 1'b0;
end
    
initial
begin

    #1000 reset_l_generator = 1'b1;
    #2500 reset_l = 1'b1;

          scl_en = 1'b0;
    #2500 sda_tb = 1'b1;
    #3995 sda_tb = 1'b1;
    #3995 sda_tb = 1'b0;
    #3995 sda_tb = 1'b1;
    #3995 sda_tb = 1'b0;
    #3995 sda_tb = 1'b1;
    
    #3995 sda_tb = 1'b0; // bit represents read write bit (write = 0, read = 1)
    #3995 sda_drive = 1'b1;     
    
    // TESTING RECEIVE(WRITE) OPERATION OF SLAVE:
    #3995 sda_drive = 1'b0;
    #3995 sda_tb = 1'b1;  
    #3995 sda_tb = 1'b0;
    #3995 sda_tb = 1'b0;
    #3995 sda_tb = 1'b1;
    #3995 sda_tb = 1'b0;
    #3995 sda_tb = 1'b1;
    #3995 sda_tb = 1'b1;
    #3995 sda_tb = 1'b0;
          sda_drive = 1'b1;
    
        #5000 scl_en = 1'b1;  // making the SCL line high till next operation is performed on it.
        scl_tb = 1'b1;
end
endmodule

SCL_GENERATOR:

`timescale 1ns / 1ps

// Parameters: I2C SCL clock frequency for Magnetometer is within range 100 - 400 kHz
//             Considering SCL of 250 kHz and FPGA Master Clock of 100 Mhz
//             1 SCL Clock Cycle = 400 Master Clock Cycles

module SCL_GENERATOR #(parameter MASTER_CLKS = 200)
    (
     input wire Clock,          // FPGA Main Clock is given as input for Clock generator
     input wire Reset_L,        // Active low Reset used in initializing the Clock generator
     output reg SCL_Clk         // Used as Serial Clock input in I2C protocol communication
    );
    
    reg [$clog2(MASTER_CLKS)-1:0] Clk_Count;     // Used to Count the number of clock pulses from Main Clock
    
    always @(posedge Clock)
    begin
        if (~Reset_L)                 // Useful in reseting the count register and initializing the SCL value as 0
        begin
            SCL_Clk   <= 1'b0;
            Clk_Count <= 8'b0;
        end
        
        else
        begin
            if (Clk_Count < MASTER_CLKS - 1)      // Counts 400 Main Clock pulses
                Clk_Count <= Clk_Count + 1;
            else
            begin                              // Complements the SCL_Clock to create pulse train every 400th Clock Pulse
                SCL_Clk   <= ~SCL_Clk;         // Useful in maintaining the 250 kHZ SCL Frequency
                Clk_Count <= 0;
            end
        end
    end
endmodule
\$\endgroup\$
0

2 Answers 2

2
\$\begingroup\$

The netlist is empty because of several critical warnings pertaining to multi-driven nets.

The first message produced by Vivado is here:

[Synth 8-6859] multi-driven net on pin Q with 1st driver pin 'SDA_OUT_reg__0/Q' ["...myproj/project_1/project_1.srcs/sources_1/new/I2C_SLAVE.v":35]

Line 35 :

assign SDA = (SDA_EN) ? SDA_OUT : 1'bz;

The message tells what is wrong; you can't drive the same variable (in this case SDA_OUT) from more than one always block.

Other variables in the design have the same issue including address and state_machine.

The oblivious follow up is 'How do I fix this?'

There are multiple ways. This is a separate question to discuss and converge on the best answer.

I don't see a need for the use of the negative edge in this design. I recommend moving everything to the positive edge so that it can be coded in one positive edge triggered always block and adjust the logic to behave as needed.

If you can't make that work, I would study how others have architected I2C slave modules in Verilog. Use internet search and find one written in a style you like and understand, then leverage those concepts. There are several examples available on the internet.

Yet, another possibility is to start a new question, "How can I modify this design to operate on a single edge and one always block so that its goes thru synthesis in Vivado'. Refer to this question.

A general recommendation when coding RTL is to use a single edge of the clock. There are exceptions (a DDR interface comes to mind). You will have less troubles on one clock edge.

No synthesizable design will drive the same variable from two always blocks which is the root cause of the synthesis failure reported in this question.

\$\endgroup\$
4
  • \$\begingroup\$ Can you elaborate on "edge detect I2C clock onto the FPGA clk" and "double flop". I didn't understand what that means. Can you just modify a part of my code according to your answer and post? \$\endgroup\$ Aug 29 at 17:50
  • \$\begingroup\$ Do you know what it means to edge detect a signal? \$\endgroup\$
    – Mikef
    Aug 29 at 18:38
  • \$\begingroup\$ No, not really. I'm a beginner in this domain trying to learn through this I2C Slave project. \$\endgroup\$ Aug 30 at 4:19
  • \$\begingroup\$ I modified the answer to meet you where you are as a beginner in this. My first try assumed understanding of several intermediate level concepts and we really can't overcome that gap here. \$\endgroup\$
    – Mikef
    Aug 30 at 13:56
1
\$\begingroup\$

The first step is to look for error or warning messages. Generally, these will appear in output log files and/or the output of your terminal or console, depending on how you are running synthesis. Refer to the Vivado documentation if you are new to the tool to see how to generate log files.

If you do not see errors or warnings, you should carefully scrutinize the output logs for any informational messages as well. Many tools display progress during a run.

If nothing looks odd there, the next step if to focus on the bi-directional SDA module port. inout ports can be tricky. Perhaps Vivado requires some special configuration; again, refer to the docs to see if there is anything special about inout ports. You mentioned optimization, and synthesis tools can optimize logic away if output ports are not driven internally by any logic.

You could try to split the inout port into 3 separate ports:

  • input sda_in
  • output sda_out
  • output sda_out_enable

This is a common practice in IC synthesis, where the bi-directional signal is handled with a hard macro pad cell, which is not synthesized.

If that still fails, try to run through the whole synthesis flow with a very basic design, say a single flip-flop. This will be a good sanity check of your flow.

Finally, see the Vivado docs for recommended Verilog coding style for synthesis. Perhaps your code does not match any supported synthesis constructs.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.