Vamos zombar do microcircuito GD32VF103CBT6, que é um análogo do conhecido STM32F103, com uma pequena mas importante diferença: em vez do núcleo ARM, ele usa o núcleo RISC-V. Como isso nos ameaça, como programadores, vamos tentar descobrir.
Listarei resumidamente as características do controlador:
• Tensão de alimentação: 2,6 - 3,6 V
• Frequência máxima do clock: 108 MHz
• Tamanho da ROM (flash): 128 kB
• Tamanho da RAM (ram): 32 kB
• Tamanho dos registros de backup (salvo após a reinicialização): 42 x 16 bits = 84 bytes.
• ADC + DAC: 2 peças de ADC com 10 canais e 12 bits cada mais 2 DACs de 12 bits.
• Claro, um monte de outros periféricos como temporizadores, SPI, I2C, UART, etc.
, — … , , .
, , : https://github.com/COKPOWEHEU/GD32VF103_tutor
-1.
— . -, : , , ? . -, . , UART. , , .
— , , , ( PA0 — PA7 , PB8 — PB15 ). usb (, usb-uart), , , 3.3 .
UART, . «» , , Rx Tx. « » , .
, Boot0 Boot1 .
.
0.
IDE. : , .
. , , GigaDevice .
gcc-riscv64-unknown-elf | |
stm32flash, dfu-util | bootloader |
kicad | |
screen | UART |
. :
(1). JTAG — , .
(2). Bootloader.UART — Boot0 , ( , ), stm32flash (, , !)
$ stm32flash /dev/ttyUSB0 -w firmware.bin
Boot0 , ( )
(3). Bootloader.USB — , stm32flash dfu-util:
$ dfu-util -a 0 -d 28e9:0189 -s 0x08000000 -D firmware.bin
, USB , RC-, USB .
, dfu-util . - . , . Boot0 , Bootloader, Boot1. , .
0,5. ?
stm32f103 . , SPI DMA ( !) .
, GigaDevice , STMicroelectronics, . . :
GD32VF103 | STM32F103 |
---|---|
RCU_APB2EN |= RCU_APB2EN_SPI0EN; | RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; |
SPI_DATA(SPI_NAME) = data; | SPI1->DR = data; |
DMA_CHCNT(LCD_DMA, LCD_DMA_CHAN) = size; | DMA1_Channel3->CNDTR = size; |
RISCV SPI_DATA , SPI. ! - SPI0, DMA0 2 .
(https://habr.com/ru/post/496046/ : https://github.com/COKPOWEHEU/stm32f103_ili9341_models3D) :
: https://github.com/COKPOWEHEU/RISCV-ili9341-3D
1.
, -, — , — . ( , !).
. , -, . RCU_APB2EN ( 0x40021018) RCU_APB2EN_PxEN, x — . , PB5 — PB7 RCU_APB2EN_PBEN (3- , 0x8). .
la a5, 0x40021018 lw a4, 0(a5) ori a4, a4, 8 sw a4, 0(a5)
a4, a5 , . .
, . , :
.equ RCU_APB2EN, 0x40021018 .equ RCU_APB2EN_PBEN, (1<<3) //RCU_APB2EN |= RCU_APB2EN_PBEN la a5, RCU_APB2EN lw a4, 0(a5) ori a4, a4, RCU_APB2EN_PBEN sw a4, 0(a5)
. - , - , . , GPIO_CTL. - 16, 64 , 32-. STmicroelectronics . B GPIOB_CTL0 GPIOB_CTL1: PB0 — PB7, PB8 — PB15. 16 , ( ). , 5 — 7 , 0 1:
.equ GPIOB_CTL0, 0x40010C00 .equ GPIO_MASK, 0b1111 .equ GPIO_PP_50MHz, 0b0011 .equ RLED, 5 .equ YLED, 6 .equ GLED, 7 .equ SBTN, 0 .equ RBTN, 1
, 4 GPIOB_CTL0: [0, 1, 2, 3], [4, 5, 6, 7]. , 5- , , [20, 21, 22, 23]. , . , :
GPIOB_CTL0 = (GPIOB_CTL0 &~(0b1111<<(RLED*4))) | 0b0011 << (RLED*4);
4 , . , , :
la a5, GPIOB_CTL0 lw a4, 0(a5) la a6, ~(GPIO_MASK << (RLED*4)) and a3, a4, a6 la a4, (GPIO_PP_50MHz << (RLED*4)) or a4, a4, a3 sw a4, 0(a5)
. , . 0 1, - . GPIOB_OCTL, , XOR` 5- . .
.equ RCU_APB2EN, 0x40021018 .equ RCU_APB2EN_PBEN, (1<<3) .equ GPIOB_CTL0, 0x40010C00 .equ GPIO_MASK, 0b1111 .equ GPIO_PP_50MHz, 0b0011 .equ GPIOB_OCTL, 0x40010C0C .equ RLED, 5 .equ YLED, 6 .equ GLED, 7 .equ SBTN, 0 .equ RBTN, 1 .text .global _start _start: //RCU_APB2EN |= RCU_APB2EN_PBEN la a5, RCU_APB2EN lw a4, 0(a5) ori a4, a4, RCU_APB2EN_PBEN sw a4, 0(a5) //GPIOB_CTL0 = (GPIOB_CTL0 & (0b1111<<RLED*4)) | 0b0011 << (RLED*4) la a5, GPIOB_CTL0 lw a4, 0(a5) la a6, ~(GPIO_MASK << (RLED*4)) and a3, a4, a6 la a4, (GPIO_PP_50MHz << (RLED*4)) or a4, a4, a3 sw a4, 0(a5) MAIN_LOOP: //GPIO_OCTL(GPIOB) ^= (1<<RLED) la a5, GPIOB_OCTL lw a4, 0(a5) xori a4, a4, (1<<RLED) sw a4, 0(a5) //sleep la a5, 200000 sleep: addi a5, a5, -1 bnez a5, sleep j MAIN_LOOP
200000 . .
, OCTL , --. ( ), . GPIOx_BOP: 16 OCTL 0, — 1. GPIOx_BC, BOP, . . , . .
.equ GPIOB_BOP, 0x40010C10 … la a5, GPIOB_BOP la a4, (1<<YLED) | (1<<RLED*16) sw a4, 0(a5)
, .
, , OCTL`.
gcc:
$ riscv64-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -mcmodel=medany -nostdlib main.S -o main.elf
, -nostdlib ( ) ` . . , , ( , ), :
$ riscv64-unknown-elf-objcopy -O binary main.elf main.bin $ riscv64-unknown-elf-objdump -D -S main.elf > main.lss $ stm32flash /dev/ttyUSB0 -w main.bin
makefile.
, COM USB . , , dialout. USB dfu-utils, udev 28e9:0189.
2.
- , GPIOB_ISTAT, , . . , :
.equ GPIO_MASK, 0b1111 # #input .equ GPIO_ANALOG, 0b0000 # .equ GPIO_HIZ, 0b0100 # .equ GPIO_PULL, 0b1000 # .equ GPIO_RESERVED, 0b1100 # , #output, GPIO, .equ GPIO_PP10, 0b0001 # push-pull , 10 .equ GPIO_PP2, 0b0010 # -//- 2 .equ GPIO_PP50, 0b0011 # -//- 50 .equ GPIO_OD10, 0b0101 # open-drain , 10 .equ GPIO_OD2, 0b0110 # -//- 2 .equ GPIO_OD50, 0b0111 # -//- 50 #output, AFIO — , .equ GPIO_APP10, 0b1001 # push-pull , 10 .equ GPIO_APP2, 0b1010 # -//- 2 .equ GPIO_APP50, 0b1011 # -//- 50 .equ GPIO_AOD10, 0b1101 # open-drain , 10 .equ GPIO_AOD2, 0b1110 # -//- 2 .equ GPIO_AOD50, 0b1111 # -//- 50
push-pull , , . 0, 1 GPIOx_OCTL. open-drain , , . 0, . « » , , I2C . OCTL. pull-up, pull-down , (pull-up), (pull-down). GPIOx_OCTL. . , . , . .
3.
. . , . , . :
zero | x0 | n/a | |
ra | x1 | ||
sp | x2 | Stack pointer, | |
gp, tp | x3, x4 | . | n/a |
t0-t6 | x5-x7, x28-x31 | ||
s0-s11 | x8, x9, x18-x27 | ||
a0-a7 | x10-x17 | ||
a0, a1 | x10, x11 |
zero , , . /dev/zero /dev/null
ra sp - .
gp tp . , . , .
t0 — t6 . , .
s0 — s11 . — - .
a0 — a7 . , , . , a0 a1, .
, , . 200`000 , . . ( ) . , a0. a1 — a7 t0 — t6, . , .
, , . ? ra, (jal, jalr call, ) , . , , , ra jr ra ret. , a5 a0:
... la a0, 200000 call sleep ... sleep: addi a0, a0, -1 bnez a0, sleep ret
4.
, ? , . , ?
, , , . , , - . : , , , , — , . . , , .
, .
: , . sp. GD32VF103 0x2000'0000 32 , 0x2000'8000, .
, 0x12, 0x34 0x56, . :
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0x2000`8000 | ← sp | ||||
0x2000`7FFF | 0x12 ← sp | 0x12 | 0x12 | 0x12 | |
0x2000`7FFE | 0x34 ← sp | 0x34 | 0x34 ← sp | ||
0x2000`7FFD | 0x56 ← sp |
, 4 0x56 , «» .
, . , RISC-V , . , , 4- 0x2000'0002 — 0x2000'0000 0x2000'0004. , — , 4-, 4- .
— . , . , , , . , sp. , ( ) . , , , , . : ( sp) . : — sp.
, :
.macro push val addi sp, sp, -4 sw \val, 0(sp) .endm .macro pop val lw \val, 0(sp) addi sp, sp, 4 .endm
, sp :
la sp, 0x20008000
, sleep -, :
sleep: push ra push s0 mv s0, a0 sleep_loop: addi s0, s0, -1 bnez s0, sleep_loop pop s0 pop ra ret
, sleep : a0. … , ! - . , , . , .
push pop, , sp 4 . ! - . , , , , sp. , :
func: addi sp, sp, -16 sw ra, 12(sp) sw s0, 8(sp) sw s1, 4(sp) sw s2, 0(sp) … lw s2, 0(sp) lw s1, 4(sp) lw s0, 8(sp) lw ra, 12(sp) addi sp, sp, 16 ret
, «» , , , , , . — sp , sp . , fp — frame pointer ( s0, ). sp, . sp, , , fp , .
, 5 ra, fp , , s1, s2 s3. :
func: addi sp, sp, -10*4 sw fp, 0(sp) addi fp, sp, 10*4 sw ra, -9*4(fp) sw s1, -8*4(fp) sw s2, -7*4(fp) sw s3, -6*4(fp) sw zero, -5*4(fp) # — data[0] sw zero, -4*4(fp) # — data[1] sw zero, -3*4(fp) # — data[2] sw zero, -2*4(fp) # — data[3] sw zero, -1*4(fp) # — data[4] … lw s3, -6*4(fp) lw s2, -7*4(fp) lw s1, -8*4(fp) lw ra, -9*4(fp) addi sp, fp, -10*4 lw fp, 0(sp) addi sp, sp, 10*4 ret
- ( ), . sp fp, . , push' pop' , , .
, fp . sp, fp s0.
5.
, . , .
— . -, . .rodata, . 4 :
.text led_arr: .short (0<<GLED | 0<<YLED | 1<<RLED) .short (0<<GLED | 1<<YLED | 0<<RLED) .short (1<<GLED | 0<<YLED | 0<<RLED) .short (0<<GLED | 1<<YLED | 0<<RLED) led_arr_end:
.short , — 2 . .
:
MAIN_LOOP: la s0, GPIOB_OCTL lh s1, 0(s0) la s2, ~(1<<GLED | 1<<YLED | 1<<RLED) la s3, led_arr la s4, led_arr_end led_loop: lh t0, 0(s3) and s1, s1, s2 or s1, s1, t0 sh s1, 0(s0) la a0, 300000 call sleep addi s3, s3, 2 bltu s3, s4, led_loop j MAIN_LOOP
. -, lw lh GPIOB_OCTL. 2-, GPIOB_OCTL, , . -, 1, . 32- , 4 , — 1.
6.
.rodata, .text. : .data .bss. , , — . .bss : , — , . , .
, .text .data, . res/firmware.lss , 0x2000'0000:
000110ea <__DATA_BEGIN__>: 110ea: 0020
, - . , . , lib/gd32vf103cbt6.ld, :
MEMORY{ flash (rxai!w) : ORIGIN = 0x00000000, LENGTH = 128K ram (wxa!ri) : ORIGIN = 0x20000000, LENGTH = 32K } SECTIONS{ .text : { } > flash .data : { } > ram .bss : { } > ram }
, . (, ) -T:
riscv64-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -mcmodel=medany -nostdlib -T lib/gd32vf103cbt6.ld src/main.S -o res/main.elf
, :
20000000 <led_arr>: 20000000: 0020
, , , . , . .text, res/firmware.hex .
:08 0000 00 2000 4000 8000 4000 D8
.ld-
MEMORY{ flash (rxai!w) : ORIGIN = 0x00000000, LENGTH = 128K ram (wxa!ri) : ORIGIN = 0x20000000, LENGTH = 32K } SECTIONS{ .text : { *(.text*) *(.rodata*) . = ALIGN(4); } > flash .data : AT(ADDR(.text) + SIZEOF(.text)){ _data_start = .; *(.data*) . = ALIGN(4); _data_end = .; } > ram .bss : { _bss_start = .; *(.bss*) . = ALIGN(4); _bss_end = .; } > ram } PROVIDE(_stack_end = ORIGIN(ram) + LENGTH(ram)); PROVIDE(_data_load = LOADADDR(.data));
- _data_load . , , :
_start: la sp, _stack_end #copy data section la a0, _data_load la a1, _data_start la a2, _data_end bgeu a1, a2, copy_data_end copy_data_loop: lw t0, (a0) sw t0, (a1) addi a0, a0, 4 addi a1, a1, 4 bltu a1, a2, copy_data_loop copy_data_end: # Clear [bss] section la a0, _bss_start la a1, _bss_end bgeu a0, a1, clear_bss_end clear_bss_loop: sw zero, (a0) addi a0, a0, 4 bltu a0, a1, clear_bss_loop clear_bss_end:
Agora, finalmente, nosso array será lido corretamente da RAM.
Ao criar variáveis na seção .bss, seria estranho atribuir-lhes quaisquer valores (embora ninguém os proíba, eles simplesmente não serão usados). Você pode usar a diretiva .comm arr, 10 placeholder (para uma variável arr de 10 bytes). Deve-se observar que ele pode ser usado em qualquer seção e fará backup de dados apenas em .bss. Abaixo estão mais alguns exemplos de declaração de variáveis de vários tamanhos:
.byte 1, 2, 3 # 0x01, 0x02 0x03 .short 4, 5 # 0x0004 0x0005 .word 6, 7 # 0x0000'0006 0x0000'0007 .quad 100500 # 0x0000'0000'0001'8894 .ascii "abcd", "efgh" # 4 ( ! ) .asciz "1234" # "1234\0" - . 'i' .space 10, 20 # 10 , 20. ,
Fim repentino
Eu não queria dividir o artigo em dois, mas o que fazer. Na próxima parte, veremos como trabalhar com a porta de depuração UART, com interrupções, e como combinar linguagem assembly e código C.