Basys3 MicroBlaze汇编冒泡排序代码故障排查请求
MicroBlaze冒泡排序故障排查
问题背景
为Basys 3开发MicroBlaze汇编程序,基于Vivado 2019.1的Xilinx SDK。程序可通过UART接收10个字符存入数组,但实现的冒泡排序sort函数无法正常工作。
原始代码
#Equates .set NUMLOOPS, 10 .set SWITCH_DATA, 0x40000000 .set SEV_SEG_DATA, 0x40010000 .set UART, 0x40600000 #Memory Section $msgBegin: .asciz "\r\n Program Start.\r\n" .text .align 2 $msgWithChar: .asciz "Character %d - %c\r\n" .text .align 2 $Nums: .fill 10, 4, 65 .data .align 4 $msgLoop: .asciz "\r\n Embedded Systems Loop #%2d.\r\n" .text .align 2 $msgEnd: .asciz "\r\n Program Stop.\r\n" .text .align 2 $msgTest1: .asciz "\r\n Test 1." .text .align 2 $msgTest2: .asciz "\r\n Test 2." .text .align 2 $msgTest3: .asciz "\r\n Test 3." .text .align 2 $msgSpace: .asciz "\r\n" .text .align 2 #Main Program .globl main main: addi r5, r0, $msgBegin # Store string to print addi r1, r1, -4 # Push r15 onto stack swi r15,r1, 0 brlid r15,xil_printf # Call print func; retn addr in r15 nop # Unfilled delay slot lwi r15,r1, 0 # Pop r15 off the stack addi r1, r1, 4 addi r19, r0, NUMLOOPS # Initialize R19 to NUMLOOPS addi r20, r0, 1 # Initialize R20 to one addi r22, r0, 0 # Initialize R22 to 0 .globl loop loop: beqi r19, sort # If R19==0, branch to done; stop program nop rsub r19,r20,r19 # Decrement loop counter (R19) by 1 (R20) lwi r11, r0, SWITCH_DATA # Read data from switches swi r11, r0, SEV_SEG_DATA # Write switch data to 7-segment disp addi r5, r0, UART # Set R5 arg to UART memory address addi r1, r1, -4 # Push r15 onto stack swi r15, r1, 0 brlid r15, XUartLite_RecvByte # Call UART Receive function nop lwi r15, r1, 0 # Pop r15 off stack addi r1, r1, 4 swi r3, r22, $Nums # Store value into $Nums array at offset R22 add r6, r0, r3 # Move char R3 into R6 to display to UART addi r1, r1, -4 # Push r15 on stack swi r15, r1, 0 brlid r15, XUartLite_SendByte # Call Uart Send function nop lwi r15, r1, 0 # Pop r15 off stack addi r1, r1, 4 addi r22, r22, 4 # Increment R22 by 4 bytes bri loop nop .globl sort sort: addi r23, r0, 10 # Initialize R23 for outer loop counter addi r24, r0, 10 # Initialize R24 for inner loop counter addi r22, r0, 0 # Reinitialize R22 to zero addi r29, r0, 0 # Initialize R29 for address offset of a[0] for1: #Print Test addi r5, r0, $msgTest1 # Store string to print addi r1, r1, -4 # Push r15 onto stack swi r15,r1, 0 brlid r15,xil_printf # Call print func; retn addr in r15 nop # Unfilled delay slot lwi r15,r1, 0 # Pop r15 off the stack addi r1, r1, 4 beqid r23, disp # If R23==0, branch to disp nop addi r23, r23, -1 # Decrement outer loop counter (R23) by 1 addi r24, r0, 10 # Initialize R24 for inner loop counter addi r24, r24, -1 # Subtract 1 from R24 (since we are comparing i and i+1) for2: #Print Test addi r5, r0, $msgTest2 # Store string to print addi r1, r1, -4 # Push r15 onto stack swi r15,r1, 0 brlid r15,xil_printf # Call print func; retn addr in r15 nop # Unfilled delay slot lwi r15,r1, 0 # Pop r15 off the stack addi r1, r1, 4 beqid r24, for1 # If R24==0, branch to for1 nop addi r24, r24, -1 # Decrement inner loop counter (R24) by 1 addi r25, r0, 0 # Initialize R25 for address offset of a[i] muli r25, r24, 4 # Calculate address offset for a[i] lwi r26, r25, $Nums # Load a[i] into R26 addi r25, r25, 4 # Increment R25 for address offset of a[i+1] lwi r27, r25, $Nums # Load a[i+1] into R27 addi r28, r0, 0 cmp r28, r26, r27 bgtid r28, for2 # If a[i] > a[i+1], skip the swap nop swap: #Print Test addi r5, r0, $msgTest3 # Store string to print addi r1, r1, -4 # Push r15 onto stack swi r15,r1, 0 brlid r15,xil_printf # Call print func; retn addr in r15 nop # Unfilled delay slot lwi r15,r1, 0 # Pop r15 off the stack addi r1, r1, 4 swi r26, r25, $Nums # Store R26 (a[i]) into a[i+1] location swi r27, r25, $Nums # Store R27 (a[i+1]) into a[i] location (using R25 - ARRAY_OFFSET) rsubi r25, r25, -4 bri for2 nop .globl disp disp: addi r19, r0, NUMLOOPS # Reinitialize R19 to NUMLOOPS addi r22, r0, 0 # Reinitialize R22 to zero addi r5, r0, $msgSpace # 1st arg R5 is format string add r6, r0, r22 # 2nd arg R6 is address offset lwi r7, r22, $Nums # 3rd arg R7 is array value at this offset addi r1, r1, -4 # Push r15 on stack swi r15, r1, 0 brlid r15, xil_printf # Call printf nop lwi r15, r1, 0 # Pop r15 off stack addi r1, r1, 4 .globl loop2 loop2: beqi r19, done # If R19==0, branch to done; stop program nop rsub r19,r20,r19 # Decrement loop counter (R19) by 1 (R20) addi r5, r0, $msgWithChar # 1st arg R5 is format string add r6, r0, r22 # 2nd arg R6 is address offset lwi r7, r22, $Nums # 3rd arg R7 is array value at this offset addi r1, r1, -4 # Push r15 on stack swi r15, r1, 0 brlid r15, xil_printf # Call printf nop lwi r15, r1, 0 # Pop r15 off stack addi r1, r1, 4 addi r22, r22, 4 # Increment R22 by 4 bytes bri loop2 .globl done done: addi r5, r0, $msgEnd # Store string to print addi r1, r1, -4 # Push r15 onto stack swi r15,r1, 0 brlid r15,xil_printf # Call print func; retn addr in r15 nop # Unfilled delay slot lwi r15,r1, 0 # Pop r15 off the stack addi r1, r1, 4
故障点分析及修复
1. 比较分支逻辑完全反转
冒泡排序的核心规则是当a[i] > a[i+1]时执行交换,但现有代码中:
cmp r28, r26, r27 bgtid r28, for2 # If a[i] > a[i+1], skip the swap
bgtid r28, for2表示当r26 > r27(即a[i] > a[i+1])时跳转到for2,跳过交换操作,完全违背排序逻辑。
修复:将分支指令改为bleid r28, for2,即当a[i] <= a[i+1]时跳过交换:
cmp r28, r26, r27 bleid r28, for2 # If a[i] <= a[i+1], skip the swap
2. 交换操作的地址错误
现有交换代码中,两次swi都使用r25(a[i+1]的偏移)作为目标地址,且后续的rsubi r25, r25, -4是给地址加4,完全错误:
swi r26, r25, $Nums # Store R26 (a[i]) into a[i+1] location swi r27, r25, $Nums # Store R27 (a[i+1]) into a[i] location (using R25 - ARRAY_OFFSET) rsubi r25, r25, -4
修复:先将r25减4回到a[i]的偏移,再执行第二次存储:
swi r26, r25, $Nums # 将a[i]存入a[i+1] addi r25, r25, -4 # 回到a[i]的偏移地址 swi r27, r25, $Nums # 将a[i+1]存入a[i]
3. 外层循环计数冗余
外层循环r23初始值为10,执行10次,但冒泡排序对n个元素只需要执行n-1次外层循环(9次)。虽然多执行一次不会导致功能错误,但可以优化:
sort: addi r23, r0, 9 # 外层循环初始值改为9,执行9次 ...
4. 内层循环计数逻辑优化(可选)
现有内层循环每次都从9开始遍历,冒泡排序可以优化内层循环次数(每次外层循环后,末尾的k个元素已排序,无需再比较),修改后可以提升效率:
for1: ... addi r23, r23, -1 # 外层循环计数器减1 addi r24, r0, 9 # 内层循环初始值为9 sub r24, r24, r23 # 内层循环次数 = 9 - 外层循环已执行次数
修正后的sort函数示例
.globl sort sort: addi r23, r0, 9 # 外层循环执行9次(n-1) addi r22, r0, 0 # Reinitialize R22 to zero for1: #Print Test addi r5, r0, $msgTest1 # Store string to print addi r1, r1, -4 # Push r15 onto stack swi r15,r1, 0 brlid r15,xil_printf # Call print func; retn addr in r15 nop # Unfilled delay slot lwi r15,r1, 0 # Pop r15 off the stack addi r1, r1, 4 beqid r23, disp # If R23==0, branch to disp nop addi r24, r0, 9 # 内层循环初始值为9 sub r24, r24, r23 # 内层循环次数随外层循环递减 addi r23, r23, -1 # Decrement outer loop counter (R23) by 1 for2: #Print Test addi r5, r0, $msgTest2 # Store string to print addi r1, r1, -4 # Push r15 onto stack swi r15,r1, 0 brlid r15,xil_printf # Call print func; retn addr in r15 nop # Unfilled delay slot lwi r15,r1, 0 # Pop r15 off the stack addi r1, r1, 4 beqid r24, for1 # If R24==0, branch to for1 nop muli r25, r24, 4 # Calculate address offset for a[i] lwi r26, r25, $Nums # Load a[i] into R26 addi r25, r25, 4 # Increment R25 for address offset of a[i+1] lwi r27, r25, $Nums # Load a[i+1] into R27 addi r28, r0, 0 cmp r28, r26, r27 bleid r28, for2 # If a[i] <= a[i+1], skip the swap nop swap: #Print Test addi r5, r0, $msgTest3 # Store string to print addi r1, r1, -4 # Push r15 onto stack swi r15,r1, 0 brlid r15,xil_printf # Call print func; retn addr in r15 nop # Unfilled delay slot lwi r15,r1, 0 # Pop r15 off the stack addi r1, r1, 4 sw
相关产品推荐
相关产品推荐

