Skip to content

Commit

Permalink
🔧 Avoid allocations during SPI transfers
Browse files Browse the repository at this point in the history
The function `spi_transfer!` does not allocate if the number of
messages to be sent is lower than the constant `_SPI_BUFFER_SIZE`.
  • Loading branch information
ronisbr committed Jun 24, 2020
1 parent 0410f42 commit 24a66d3
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 25 deletions.
28 changes: 16 additions & 12 deletions src/cmacros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@ _IOC(dir,type,nr,size) = (dir << _IOC_DIRSHIFT) |
(nr << _IOC_NRSHIFT) |
(size << _IOC_SIZESHIFT)

_IOC_TYPECHECK(t) = sizeof(t)
_IOW(type,nr,size) = _IOC(_IOC_WRITE, type, nr, _IOC_TYPECHECK(size))
_IOR(type,nr,size) = _IOC(_IOC_READ, type, nr, _IOC_TYPECHECK(size))
_IOW(type,nr,size) = _IOC(_IOC_WRITE, type, nr, size)
_IOR(type,nr,size) = _IOC(_IOC_READ, type, nr, size)

################################################################################
# I2C
Expand Down Expand Up @@ -145,16 +144,21 @@ SPI_MSGSIZE(N) =
( N*sizeof(struct_spi_ioc_transfer) < (1 << _IOC_SIZEBITS) ) ?
N*sizeof(struct_spi_ioc_transfer) : 0

SPI_IOC_MESSAGE(N) = _IOW(SPI_IOC_MAGIC, 0, NTuple{SPI_MSGSIZE(N), Cchar})
# In the past, we were using `NTuple{SPI_MSGSIZE(N), Cchar}` to create a type so
# that the function `_IOC_TYPECHECK(t) = sizeof(t)` could retrieve the correct
# size. However, this was causing 3 allocations. Thus, the system was simplified
# to avoid this. Now, the functions `_IOR` and `_IOW` must receive the size
# instead of the type as it was coded in the Kernel source.
SPI_IOC_MESSAGE(N) = _IOW(SPI_IOC_MAGIC, 0, sizeof(Cchar)*SPI_MSGSIZE(N))

const SPI_IOC_RD_MODE = _IOR(SPI_IOC_MAGIC, 1, __u8)
const SPI_IOC_WR_MODE = _IOW(SPI_IOC_MAGIC, 1, __u8)
const SPI_IOC_RD_MODE = _IOR(SPI_IOC_MAGIC, 1, sizeof(__u8))
const SPI_IOC_WR_MODE = _IOW(SPI_IOC_MAGIC, 1, sizeof(__u8))

const SPI_IOC_RD_LSB_FIRST = _IOR(SPI_IOC_MAGIC, 2, __u8)
const SPI_IOC_WR_LSB_FIRST = _IOW(SPI_IOC_MAGIC, 2, __u8)
const SPI_IOC_RD_LSB_FIRST = _IOR(SPI_IOC_MAGIC, 2, sizeof(__u8))
const SPI_IOC_WR_LSB_FIRST = _IOW(SPI_IOC_MAGIC, 2, sizeof(__u8))

const SPI_IOC_RD_BITS_PER_WORD = _IOR(SPI_IOC_MAGIC, 3, __u8)
const SPI_IOC_WR_BITS_PER_WORD = _IOW(SPI_IOC_MAGIC, 3, __u8)
const SPI_IOC_RD_BITS_PER_WORD = _IOR(SPI_IOC_MAGIC, 3, sizeof(__u8))
const SPI_IOC_WR_BITS_PER_WORD = _IOW(SPI_IOC_MAGIC, 3, sizeof(__u8))

const SPI_IOC_RD_MAX_SPEED_HZ = _IOR(SPI_IOC_MAGIC, 4, __u32)
const SPI_IOC_WR_MAX_SPEED_HZ = _IOW(SPI_IOC_MAGIC, 4, __u32)
const SPI_IOC_RD_MAX_SPEED_HZ = _IOR(SPI_IOC_MAGIC, 4, sizeof(__u32))
const SPI_IOC_WR_MAX_SPEED_HZ = _IOW(SPI_IOC_MAGIC, 4, sizeof(__u32))
6 changes: 6 additions & 0 deletions src/constants.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@ const GPIO_MODE_SET = Dict(:in => 0b000,
:alt3 => 0b111,
:alt4 => 0b011,
:alt5 => 0b010)

################################################################################
# SPI
################################################################################

const _SPI_BUFFER_SIZE = 16
24 changes: 17 additions & 7 deletions src/spi.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ or a vector of vectors of `Integer`, in which multiple messages will be sent.
The result is returned in an object with the same type of `tx_buf` together with
the number of words received.
!!! note "Allocations"
This function will perform allocations because they create the vector that
will be returned. If this is not desired, then use the in-place version
`spi_transfer!`.
# Keywords
The same keywords of `spi_transfer!` can be used.
Expand Down Expand Up @@ -183,11 +189,12 @@ The received data will be stored in `rx_buf` that must have the same type of
This function returns the number of bytes received.
!!! warning
!!! note "Allocations"
When only one message is transmitted, then this function does not allocate.
On the other hand, if multiple messages are transmitted, then this function
must allocate a vector of `struct_spi_ioc_transfer`.
This function will not allocate only if the number of messages sent is lower
than the constant `BaremetalPi._SPI_BUFFER_SIZE`. Otherwise, it will perform
an allocation because it must allocate a vector of
`struct_spi_ioc_transfer`.
# Keywords
Expand Down Expand Up @@ -223,8 +230,12 @@ function spi_transfer!(devid::Integer,
delay_usecs < 0 && (delay_usecs = 0)
bits_per_word 0 && (bits_per_word = spidev.bits_per_word)

# Allocate the vector with the description of the transfer.
descs = Vector{struct_spi_ioc_transfer}(undef, num_msgs)
# Check if we can use the allocated buffer to transfer the message.
if num_msgs > _SPI_BUFFER_SIZE
descs = Vector{struct_spi_ioc_transfer}(undef, num_msgs)
else
descs = spidev.bdescs
end

@inbounds for i = 1:num_msgs
msg_size = length(tx_buf[i])
Expand All @@ -243,7 +254,6 @@ function spi_transfer!(devid::Integer,
delay_usecs,
bits_per_word,
cs_change)

end

# Execute the transfer.
Expand Down
18 changes: 12 additions & 6 deletions src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ end
# SPI
################################################################################

mutable struct SPIDEV
io::IOStream
max_speed_hz::Int
bits_per_word::Int
end

struct struct_spi_ioc_transfer
tx_buf::__u64
rx_buf::__u64
Expand All @@ -65,6 +59,18 @@ struct_spi_ioc_transfer(tx_buf, rx_buf, len, speed_hz, delay_usecs,
__u32(speed_hz), __u16(delay_usecs),
__u8(bits_per_word), __u8(cs_change), __u8(0),
__u8(0), __u16(0))
mutable struct SPIDEV
io::IOStream
max_speed_hz::Int
bits_per_word::Int

# Buffers to reduce the allocation.
bdescs::Vector{struct_spi_ioc_transfer}
end

SPIDEV(io, max_speed_hz, bits_per_word) =
SPIDEV(io, max_speed_hz, bits_per_word,
Vector{struct_spi_ioc_transfer}(undef, _SPI_BUFFER_SIZE))

################################################################################
# Global
Expand Down

0 comments on commit 24a66d3

Please sign in to comment.