Assembly examples for Red-V Thing Plus (SiFive FE310-G002)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

163 lines
5.0 KiB

#
# p_i2c0.s: basic i2c driver
#
# 2021 flabbergast@drak.xyz
# Unlicense: https://unlicense.org/
#
# no description in the manual, refers to https://opencores.org/projects/i2c
# (that requires registration which requires manual approval from someone)
# here's a copy of i2cspecs.pdf:
# https://pilvi.disasm.info/index.php/s/MqvdhL3lBqFxY0P/download
#
# prescaler:
# - max 0xFFFF
# - (clock_rate/(5*baud)) - 1
# - on 16MHz core, 400kHz I2C => 7
# - on 16MHz core, 100kHz I2C => 31
# - set when CONTROL_EN is off
# sample usage:
# jal i2c_init (somewhere at the beginning)
#
# ## i2c bus scanner
# li s1, 1 # start with address
# li s2, 127 # end with address
# 2:
# mv a0, s1
# li a2, 0
# li a4, 0
# jal i2c_rw # try address
# bltz a0, 3f # no response=>skip
# pushda s1 # print message
# puts "Found a device at %B\n"
# 3:
# li a0, 2 # wait a little
# jal delay # ... seems to be too fast otherwise
# addi s1, s1, 1 # inc counter
# bleu s1, s2, 2b # repeat
.if compressed_isa
.balign 2, 0
.else
.balign 4, 0
.endif
.include "platform_regs.inc"
i2c_init:
li t0, GPIO_BASE # UART0 RX/TX are selected in IOF_SEL (IOF0) on Reset.
li t2, (1<<12)|(1<<13) # which bits
lw t1, GPIO_IOF_EN(t0) # set IOF_EN bits ... read existing
or t1, t1, t2 # twiddle bits
sw t1, GPIO_IOF_EN(t0) # write back
not t2, t2 # invert bits
lw t1, GPIO_IOF_SEL(t0) # clear IOF_SEL bits (tho they should be fine on Reset)
and t1, t1, t2 # twiddle bits
sw t1, GPIO_IOF_SEL(t0) # write back
li t0, I2C0_BASE
sb zero, I2C_CTR(t0) # control: disable en, ie
sb zero, I2C_PRERhi(t0) # prescaler MSB
li t1, 31
sb t1, I2C_PRERlo(t0) # prescaler LSB
li t1, I2C_CTR_EN
sb t1, I2C_CTR(t0) # enable module; irq disabled
ret
.macro _wait_for_tip
1: # wait-for-transfer loop
lbu t1, I2C_SR(t0) # status register
andi t1, t1, I2C_SR_TIP # "transfer-in-progress" bit
bnez t1, 1b # wait for TIP to clear
.endm
.macro _check_ack
# check ack
lbu t1, I2C_SR(t0) # status again
andi t1, t1, I2C_SR_RXACK # ack received?
beqz t1, 1f # should be 0
li t1, I2C_CR_STOP # slave did not respond, so...
sb t1, I2C_CR(t0) # set stop condition on the bus
li a0, -1 # set 'fail' return value
ret
1:
.endm
# read/write bytes through I2C
# expects:
# - a0: I2C address
# - a1: pointer to data to write
# - a2: how many bytes to write
# - a3: pointer to data to read
# - a4: how many bytes to read
# returns: a0: 0 success, -1 problem
# a1 and a3 point to 1 byte after the last used
# note: both r/w can have 0 bytes to r/w
# in which case that part is skipped
# note: does not jal anything, so does not push/pop ra!
# if you want to e.g. puts, need to add this,
# including in the macros above!
i2c_rw:
li t0, I2C0_BASE
slli a0, a0, 1
# if zero write nonzero read, goto read
bnez a2, 1f # writing some bytes=>skip
bnez a4, 5f # reading=>goto reading part
1: # writing
sb a0, I2C_TXR(t0) # write the address to tx register
li t1, I2C_CR_START|I2C_CR_WRITE
sb t1, I2C_CR(t0) # start cond, write to bus
_wait_for_tip
_check_ack
add t2, a1, a2 # t2: end of loop pointer
bnez a2, 3f # writing some bytes=>skip
li t1, I2C_CR_STOP # here: 0 write, 0 read
sb t1, I2C_CR(t0) # just set stop
j 9f # and exit
3: # write loop
lbu t1, 0(a1) # read current byte to send
sb t1, I2C_TXR(t0) # write data to tx register
li t1, I2C_CR_WRITE
addi a1, a1, 1 # inc address
bltu a1, t2, 1f # if not the last byte, skip
bnez a4, 1f # subsequent read => do not set stop cond
ori t1, t1, I2C_CR_STOP # last byte => stop condition
1:
sb t1, I2C_CR(t0) # do the deed
_wait_for_tip
_check_ack
bltu a1, t2, 3b # repeat for the next byte
beqz a4, 9f # nothing to read=>goto end
5: # reading
ori t1, a0, 1 # address | read_bit
sb t1, I2C_TXR(t0) # write the address to tx register
li t1, I2C_CR_START|I2C_CR_WRITE
sb t1, I2C_CR(t0) # start cond, write to bus
_wait_for_tip
_check_ack
add t2, a3, a4 # t2: end of loop pointer
addi t2, t2, -1 # one less (to detect the last byte)
6: # read loop
li t1, I2C_CR_READ
bne a3, t2, 1f # if not the last byte=>skip
ori t1, t1, I2C_CR_STOP|I2C_CR_ACK # last byte=>stop and nack
1:
sb t1, I2C_CR(t0)
_wait_for_tip
lbu t1, I2C_RXR(t0) # read the received byte
sb t1, 0(a3) # store it
addi a3, a3, 1 # inc pointer
bleu a3, t2, 6b # repeat for the next byte
9: # end
li a0, 0
ret