Livt packages
Livt.IO
Version 1.8.1
Memory, UART, I2C, SPI, and Ethernet input/output components for Livt hardware designs.
Published files (126)
src/i2c/I2CRegisterSlave.lvt
namespace Livt.IO
/**
* Register-file helper built on top of `I2CSlave`.
*
* The first byte written after an address match selects the register pointer.
* Later write bytes store into the selected register and advance the pointer.
* A read-address request loads the current register value and preloads the next
* register value so ACKed multi-byte reads can auto-increment.
*/
component I2CRegisterSlave
{
// Register-file address space.
public const REGISTER_COUNT: int = 256
// Owned byte-level target and register storage.
slave: I2CSlave
registers: byte[256]
// Current register selection and first-write-byte interpretation.
pointer: int
pointerValid: bool
expectingPointer: bool
// Last application-visible register write and its notification.
writtenRegister: byte
writtenValue: byte
hasWrittenRegister: bool
/**
* Owns a byte-level I2C target on the borrowed bus and a 256-byte register bank. Starts
* with no valid pointer or write notification; address is masked to seven bits.
*/
new(bus: flip I2CBus, address: byte)
{
this.slave = new I2CSlave(bus, address)
this.pointer = 0
this.pointerValid = false
this.expectingPointer = false
this.writtenRegister = 0x00
this.writtenValue = 0x00
this.hasWrittenRegister = false
}
/**
* Writes the byte-addressed register without changing the bus pointer or generating a bus-
* write notification. Coordinate application writes with bus activity.
*/
public fn SetRegister(address: byte, value: byte)
{
var index: int = address as int
state {
this.registers[index] = value
}
}
/**
* Returns the current byte at address without moving the pointer or clearing events.
*/
public fn GetRegister(address: byte) byte
{
var index: int = address as int
return this.registers[index]
}
/**
* Selects address, marks the pointer valid, and preloads current/next transmit bytes.
* Coordinate with the bus service process.
*/
public fn SetPointer(address: byte)
{
var index: int = address as int
state {
this.pointer = index
this.pointerValid = true
}
this.LoadTransmitWindow(index)
}
/**
* Returns the retained register pointer; HasPointer distinguishes a valid selection.
*/
public fn GetPointer() byte
{
return this.pointer as byte
}
/**
* Reports whether a pointer has been selected; this snapshot does not reserve it against
* bus activity.
*/
public fn HasPointer() bool
{
return this.pointerValid
}
/**
* Reports a retained bus register-write notification. Later writes can replace its
* address/value before it is cleared.
*/
public fn HasWrittenRegister() bool
{
return this.hasWrittenRegister
}
/**
* Returns the address of the latest notified bus write; valid with HasWrittenRegister.
*/
public fn GetWrittenRegister() byte
{
return this.writtenRegister
}
/**
* Returns the value of the latest notified bus write; valid with HasWrittenRegister.
*/
public fn GetWrittenValue() byte
{
return this.writtenValue
}
/**
* Clears the write notification without modifying registers or the retained address/value.
*/
public fn ClearWrittenRegister()
{
state {
this.hasWrittenRegister = false
}
}
/**
* Resets the pointer to zero and invalidates its selection without clearing register
* contents.
*/
public fn ClearPointer()
{
state {
this.pointer = 0
this.pointerValid = false
this.expectingPointer = false
}
}
/**
* Returns the byte at the retained pointer, including register zero after ClearPointer;
* does not establish pointer validity.
*/
public fn GetCurrentRegister() byte
{
return this.registers[this.pointer]
}
/**
* Treats the first byte after write addressing, or an invalid pointer, as the pointer.
* Otherwise stores the value, records a write event, and advances with wrap at 256. Used
* by the service process; serialize external calls with it.
*/
public fn AcceptWriteByte(value: byte)
{
if (this.pointerValid == false || this.expectingPointer == true) {
var index: int = value as int
state {
this.pointer = index
this.pointerValid = true
this.expectingPointer = false
}
this.LoadTransmitWindow(index)
} else {
state {
this.registers[this.pointer] = value
this.writtenRegister = this.pointer as byte
this.writtenValue = value
this.hasWrittenRegister = true
}
this.AdvancePointer()
}
}
/**
* Loads the current and following register into the target transmit window without
* advancing the pointer.
*/
public fn PrepareReadByte()
{
this.LoadTransmitWindow(this.pointer)
}
/**
* Advances the pointer and refills the following byte only after controller ACK. NACK
* leaves the pointer unchanged; used by the service process.
*/
public fn HandleTransmittedByte(acked: bool)
{
if (acked) {
var nextPointer: int = this.NextIndex(this.pointer)
var followingPointer: int = this.NextIndex(nextPointer)
var followingValue: byte = this.registers[followingPointer]
state {
this.pointer = nextPointer
}
this.slave.SetNextTransmitByte(followingValue)
}
}
/**
* Preloads current and next register bytes for index in 0..255, wrapping the next index at
* the bank end.
*/
fn LoadTransmitWindow(index: int)
{
var nextIndex: int = this.NextIndex(index)
var transmitValue: byte = this.registers[index]
var nextValue: byte = this.registers[nextIndex]
this.slave.SetTransmitByte(transmitValue)
this.slave.SetNextTransmitByte(nextValue)
}
/**
* Returns the following register index with wrap at 256; requires index in 0..255.
*/
fn NextIndex(index: int) int
{
if (index >= REGISTER_COUNT - 1) {
return 0
}
return index + 1
}
/**
* Advances the current valid-range pointer with wrap at the register-bank boundary.
*/
fn AdvancePointer()
{
var nextPointer: int = this.NextIndex(this.pointer)
state {
this.pointer = nextPointer
}
}
/**
* Consumes address, STOP, receive, and transmit events from the owned target. Maintains
* pointer selection and prefetch data; application access must be coordinated with bus
* activity.
*/
process ServiceSlave()
{
if (this.slave.HasAddressMatch()) {
if (this.slave.WasWriteAddressed()) {
state {
this.expectingPointer = true
}
this.slave.ClearAddressMatch()
} elif (this.slave.WasReadAddressed()) {
this.PrepareReadByte()
this.slave.ClearReadRequested()
this.slave.ClearAddressMatch()
}
}
if (this.slave.HasStopDetected()) {
this.slave.ClearStopDetected()
this.slave.ClearAddressMatch()
}
if (this.slave.HasReceivedByte()) {
var value: byte = this.slave.GetReceivedByte()
this.slave.ClearReceivedByte()
this.AcceptWriteByte(value)
}
if (this.slave.HasTransmittedByte()) {
var acked: bool = this.slave.WasTransmitAcked()
this.HandleTransmittedByte(acked)
this.slave.ClearTransmittedByte()
if (acked == false) {
this.slave.ClearAddressMatch()
}
}
}
}