I2C
Shakti processors, which are based on the RISC-V architecture, the I2C (Inter-Integrated Circuit) interface is commonly used to connect low-speed devices within embedded systems. The I2C module in Shakti processors typically includes several registers that control and manage the communication on the I2C bus. Here's a general overview of the I2C module for Shakti:
I2C Registers:
-
Prescale Register (Prescale - Offset 0x00): Size: 8 bits Accessible: Read/Write Description: Configures the I2C prescaler, which divides the system clock to determine the I2C clock frequency.
-
Control Register (Control - Offset 0x08): Size: 8 bits Accessible: Read/Write Description: Configures and controls the I2C data transfer. Key bits include: I2C_PIN: Pending Interrupt Not, Used as a software reset. I2C_ENI: Enables the external interrupt output. I2C_STA: Transmits Start condition + Slave address. I2C_STO: Transmits the stop condition. I2C_ACK: Acknowledgement bit.
-
Data Shift Register (Data - Offset 0x10): Size: 8 bits Accessible: Read/Write Description: Holds the data to be transmitted or received during I2C communication.
-
Status Register (Status - Offset 0x18): Size: 8 bits Accessible: Read Description: Provides the status of I2C data transfer. Key bits include: I2C_INI: High when I2C communication is in progress. I2C_STS: Status flag in slave receiver mode for externally generated STOP condition. I2C_BER: Bus error flag for misplaced START or STOP condition. I2C_AD0/I2C_LRB: Last Received Bit and Address 0 in slave mode. I2C_AAS: Addressed As Slave bit. I2C_LAB: Lost Arbitration Bit. I2C_BB: Bus Busy bit.
-
Clock Register (SCL - Offset 0x38): Size: 8 bits Accessible: Read/Write Description: Divides the I2C Prescaler clock to determine the I2C SCL (Serial Clock Line) frequency.
-
General Overview:
- I2C is used for connecting low-speed devices in embedded systems.
- It facilitates communication between a single master and multiple slave devices.
- Two-wire interface: SDA (Data) and SCL (Clock).
- Each device has a unique address.
- Synchronous communication with master controlling the clock.
- Registers control prescaling, data transfer, status, and clock frequency.
Rust I2C Register implmentation
use crate::common::MMIODerefWrapper;
use riscv::{
asm::{delay, nop},
register,
};
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
pub const I2C_OFFSET: usize = 0x0004_0000;
pub const I2C_INI: u8 = 1 << 7;
pub const I2C_STS: u8 = 1 << 5;
pub const I2C_BER: u8 = 1 << 4;
pub const I2C_AD0_LRB: u8 = 1 << 3;
pub const I2C_AAS: u8 = 1 << 2;
pub const I2C_LAB: u8 = 1 << 1;
pub const I2C_BB: u8 = 1 << 0;
pub const I2C_PIN: u8 = 1 << 7;
pub const I2C_ES0: u8 = 1 << 6;
pub const I2C_ENI_LRB: u8 = 1 << 3;
pub const I2C_STA: u8 = 1 << 2;
pub const I2C_STO: u8 = 1 << 1;
pub const I2C_ACK: u8 = 1 << 0;
register_structs! {
#[allow(non_snake_case)]
pub RegistersBlock{
(0x00 => PRESCALE: ReadWrite<u16>),
(0x02 => _reserved0),
(0x08 => CONTROL: ReadWrite<u8>),
(0x09 => _reserved1),
(0x10 => DATA: ReadWrite<u8>),
(0x11 => _reserved2),
(0x18 => STATUS : ReadWrite<u8>),
(0x19 => _reserved3),
(0x38 => SCL : ReadWrite<u8>),
(0x39 => _reserved4)
, (0x3C => @END),
}
}
register_bitfields! {
u32,
///I2C Prescale Register divides the System clock by (Prescale value + 1). This clock is used as
///clock input for I2C Serial Clock.
///I2C Prescaler clock = System Clock / (Prescaler Value + 1)
PRESCALE [
PRESCALE_VALUE OFFSET(0) NUMBITS(8) []
],
///I2C SCL Register divides the I2C Prescaler clock by (SCL value + 1). This clock is used as
///I2C SCL clock = I2C Prescaler Clock / (SCL COUNT + 1).
SCL[
SCL_COUNT OFFSET(0) NUMBITS(8) []
],
STATUS [
///High when I2C communication in progress. Becomes low once I2C
///communication is complete.
I2C_INI OFFSET(7) NUMBITS(1) [],
/// When in slave receiver mode, this flag is asserted when an
/// externally generated STOP condition is detected (used only in
/// slave receiver mode).
I2C_STS OFFSET(5) NUMBITS(1) [],
///Bus error; a misplaced START or STOP condition has been
///detected
I2C_BER OFFSET(4) NUMBITS(1) [],
/* AD0(Address 0) - General Call bit used for Broadcast
LRB - Last Received Bit through I2C Bus
This status bit serves a dual function, and is valid only while
PIN = 0:
1. LRB holds the value of the last received bit over the
I2C-bus while AAS = 0 (not addressed as slave). Normally
this will be the value of the slave acknowledgement; thus
checking for slave acknowledgement is done via testing of the
LRB.
2. AD0; when AAS = 1 (‘Addressed As Slave’ condition), the
I2C-bus controller has been addressed as a slave. Under this
condition, this bit becomes the ‘AD0’ bit and will be set to
logic 1 if the slave address received was the ‘general call’
(00H) address, or logic 0 if it was the I2C-bus controller’s own
slave address.
*/
I2C_AD0_LRB OFFSET(3) NUMBITS(1) [],
///Addressed As Slave’ bit. Valid only when PIN = 0. When
///acting as slave receiver, this flag is set when an incoming
///address over the I2C-bus matches the value in own address
//register
I2C_AAS OFFSET(2) NUMBITS(1) [],
///Lost Arbitration Bit. This bit is set when, in multi-master
///operation, arbitration is lost to another master on the I2C-bus
I2C_LAB OFFSET(1) NUMBITS(1) [],
///Bus Busy bit. This is a read-only flag indicating when the
///I2C-bus is in use. A zero indicates that the bus is busy, and
///access is not possible
I2C_BB OFFSET(0) NUMBITS(1) []
],
CONTROL [
///Pending Interrupt Not, Used as a software reset. If set to 1, all
I2C_PIN OFFSET(7) NUMBITS(1) [],
/// Enable Serial Output.
///0 - Registers can be initialized.
///1 - I2C Serial Transmission
I2C_ES0 OFFSET(6) NUMBITS(1) [
REGISTER_INITIALIZED = 0,
I2C_SERIAL_TRANSMISSION = 1,
],
///Enables the external interrupt output, which is generated when
///the PIN is active (Low)
I2C_ENI OFFSET(3) NUMBITS(1) [],
///Transmits Start condition + Slave address..
I2C_STA OFFSET(2) NUMBITS(1) [],
///Transmits the stop condition.
I2C_STO OFFSET(1) NUMBITS(1) [],
///Acknowledgement bit.
///1: I2C automatically sends an acknowledgement after a
///read/write transaction.
///0: I2C Master sends Negative Acknowledge to stop the I2C
///transfer
I2C_ACK OFFSET(0) NUMBITS(1) [
NAK = 0,
ACK = 1,
]
],
}
type Registers = MMIODerefWrapper<RegistersBlock>;
pub struct I2CInner {
registers: Registers,
}
Rust implementation for interacting with the I2C (Inter-Integrated Circuit) module on Shakti processors.
-
Constants:
- I2C_OFFSET: Specifies the offset address for the I2C module in memory.
- Various constants (I2C_INI, I2C_STS, etc.) represent bit positions in status and control registers.
-
Register Structures
- RegistersBlock : Defines the memory-mapped I2C registers.
- PRESCALE : Configures the I2C prescaler for clock frequency.
- CONTROL : Configures and controls I2C data transfer.
- DATA : Holds data to be transmitted or received.
- STATUS : Provides status information about I2C communication.
- SCL : Configures the I2C SCL (Serial Clock Line) clock. -Register Bitfields:
- register_bitfields! macro defines specific bits within registers.
- PRESCALE: PRESCALE_VALUE represents the prescaler value.
- SCL : SCL_COUNT represents the count for dividing the I2C Prescaler clock.
- STATUS : Defines various status bits such as **I2C_INI, I2C_STS, I2C_BER, **etc.
- CONTROL : Defines control bits such as **I2C_PIN, I2C_ES0, I2C_ENI, **etc.
-
MMIODerefWrapper and Structs:
- MMIODerefWrapper : Represents a memory-mapped I/O (MMIO) wrapper for safe interaction with hardware registers.
- Registers : A wrapper for the RegistersBlock, ensuring safe access to I2C registers.
-
I2CInner Struct:
- I2CInner: A struct encapsulating the I2C module, containing an instance of the Registers wrapper.