Hi <@U07JC2KT0RX>, I replied to you in a DM, but I...
# clear
a
Hi @Elvis Dowson, I replied to you in a DM, but I'll repeat it here for the benefit of other people who might be using CLEAR also (or who can provide their own additional advice): I don't believe CLEAR has an I2C interface built-in. However, do you mean you want to create an I2C controller? As for SPI, this could mean one of several things: 1. There is an SPI Controller that is built into the Caravel SoC, permanently on the chip. This can be used from firmware. That is, it can be activated to take over 4 pins that are normally GPIO pins. 2. The Caravel RISC-V CPU has its own SPI interface that reads and executes firmware code from an external flash ROM chip. You can't control this one; rather, this is where your compiled C code is placed. 3. There's what's known as the "HKSPI" (Housekeeping SPI) interface, which can be controlled by an external device, effectively as a form of full-chip debug function. You wouldn't normally touch this. I'm guessing you mean no. 1. There is some information *here *about its registers that can be accessed from firmware, and an example C firmware and Verilog testbench representing the physical chip and its connection to an SPI memory. Note the GPIO pins it uses (IO[35:32]). Note that all of the answers I've given are based on the standard 'Caravel' harness (on which CLEAR is based). It is the same one used in the Efabless chipIgnite 'Caravel' and 'Caravan' templates, and I believe it is consistent with CLEAR.
e
Hi Anton, I am using the Caravel Harness as a reference. I see that it has an SPI controller. I want to get an I2C controller as well. As I understand, I have two options: a. Add an I2C verilog hardware module. b. Emulate I2C controller using software bit-banging and get that to work with the I2C peripheral. I suppose I have to first configure the GPIO pins so that it is compatible with the I2C protocol, and pull up the SDA and SCL lines, and implement the required function to handle start, stop, ack, read and write operations. Would you have some pointers to: • existing c-code for configuring the GPIO pins, or • a full I2C software bit bang implementation for I2C communications? If not, I will have to write one. I couldn't find an open-source example for an I2C controller implementation for the Caravel Harness. Elvis
a
Hi @Elvis Dowson, I don't think we have existing I2C examples, specifically, but probably any existing free I2C Verilog implementation will do if you wanted a hardware implementation in CLEAR's FPGA. The I2C design wouldn't have to be Caravel-specific, other than doing the pin-mapping (which is true of any design). I've not reviewed this one, but it appears to be free and potentially suitable: https://github.com/alexforencich/verilog-i2c -- in particular, i2c_master.v
Amazingly, AI is not too bad at writing basic Verilog modules these days. Untested, unchecked, and provided with all the usual disclaimers ("verify this yourself", etc.) this is the result for the prompt "_Write Verilog code synthesizable for an ASIC that implements an I2C master that writes the byte '0xC9' to address '0x123'_":
Copy code
module i2c_master (
  input wire clk,  // System clock
  input wire rst,  // System reset

  output wire sda, // I2C data line (bidirectional)
  output wire scl  // I2C clock line
);

  // I2C signals (internal)
  reg sda_out;
  wire sda_in;

  reg scl_out;

  // I2C control signals
  reg [2:0] state;
  reg [7:0] data_to_write;
  reg [9:0] address_to_write;
  reg [3:0] bit_counter;

  // I2C states
  parameter IDLE        = 3'b000;
  parameter START       = 3'b001;
  parameter ADDRESS     = 3'b010;
  parameter DATA        = 3'b011;
  parameter STOP        = 3'b100;

  // Assign I2C signals
  assign sda = sda_out ? 1'bz : 1'b0;
  assign sda_in = sda; 
  assign scl = scl_out ? 1'bz : 1'b0;

  // I2C state machine
  always @(posedge clk or posedge rst) begin
    if (rst) begin
      state <= IDLE;
      sda_out <= 1'b1;
      scl_out <= 1'b1;
      bit_counter <= 4'b0;
      data_to_write <= 8'b0;
      address_to_write <= 10'b0;
    end else begin
      case (state)
        IDLE: begin
          if(/* Some start condition */) begin
            state <= START;
            sda_out <= 1'b0; // Start condition
            bit_counter <= 4'b0;
            data_to_write <= 8'hC9; // Data to write
            address_to_write <= 10'h123; // 7-bit address with R/W bit
          end
        end

        START: begin
          if (scl_out == 1'b0) begin
            scl_out <= 1'b1;
          end else begin
            state <= ADDRESS;
            bit_counter <= 4'b0;
          end
        end

        ADDRESS: begin
          sda_out <= address_to_write[9 - bit_counter];
          if (scl_out == 1'b0) begin
            scl_out <= 1'b1;
            bit_counter <= bit_counter + 1'b1;
          end else begin
            if (bit_counter == 4'd9) begin
              // Check for ACK
              if (sda_in == 1'b0) begin 
                state <= DATA;
              end else begin
                // Handle NACK
                state <= STOP; 
              end
              bit_counter <= 4'b0;
            end else begin
              scl_out <= 1'b0;
            end
          end
        end

        DATA: begin
          sda_out <= data_to_write[7 - bit_counter];
          if (scl_out == 1'b0) begin
            scl_out <= 1'b1;
            bit_counter <= bit_counter + 1'b1;
          end else begin
            if (bit_counter == 4'd7) begin
              // Check for ACK (optional for write)
              state <= STOP;
              bit_counter <= 4'b0;
            end else begin
              scl_out <= 1'b0;
            end
          end
        end

        STOP: begin
          sda_out <= 1'b0; 
          if (scl_out == 1'b0) begin
            scl_out <= 1'b1;
          end else begin
            sda_out <= 1'b1; // Stop condition
            state <= IDLE;
          end
        end

        default: begin
          state <= IDLE;
        end
      endcase
    end
  end

endmodule
Note that one variation would be that you probably need to replace
1'bz
(Hi-Z, i.e. tristate) with some other approach, and clearly this example is only writing data... not reading.
The former example I gave (i2c_master.v) looks like it has dedicated input, output, and direction (tristate control) paths, and some explanation of it.
Caravel pins have various modes that you can put them in, including dedicated output, and dedicated input (with either pull-up, pull-down, or neither)... but also a bidirectional mode, in which case there are separate io_in, io_out, and io_oeb paths... where io_oeb is a signal you control to set direction: making it low (
0
=0utput) or high (
1
=1nput)
I would expect these to be compatible with i2c_master.v