Menu Close

RISC-V assembly language programming (2) assembler asm_run_led

Use RISC-V assembly language to write a led marquee experiment

Related reference articles:

RISC-V teaching plan


        li      x8, 0xf0000000; # set gpio address
        addi    x6, x0, 0;      # initial x6 = 0
        li      x7, 0x00400000; # set x7 as delay counter
        addi    x10, x0, 0x80;  # x10 = 0x80, set gpio bit 7
        not     x18, x10;       # x18 = ~x10 
        sh      x18, 0(x8);     # addr[0xf000_0000] = val[0x18]
        sh      x0,  4(x8);     # addr[0xf000_0004] = 0 as output
        addi    x6,  x6 ,1;     # x6 = x6 + 1
        bne     x6,  x7,LOOP;   # if(x6 != x7) goto LOOP
        addi    x6,  x0,0;      # x6 = 0;
        srli    x10, x10,1;     # x10 >> 1 logic right shift 1 bit
        beq     x10, x0,START;  # if(x10 == 0x00) goto START
        not     x18, x10;       # x18 = ~x10
        sh      x18, 0(x8);     # addr[0xf000_0000] = x18
        jal     LOOP;           # pc = LOOP


Program Analysis:

LI x8, 0xf0000000;                                # set gpio address;

LI is a pseudo-instruction that can be translated into a LUI instruction by the compiler. This instruction stores the value 0xf000_0000 in the register x8, and the purpose is to store the address of the gpio in the x8 register for later use.

ADDI x6,x0,0 ;                                          # Initialize variable x6 = 0;

The value of x0 is added to 0 and the sum is stored in the x6 register. Means to clear the x6 register. There is no special instruction for clearing in the risc-v assembly instruction set.

LI x7, 0x00400000;                               # x7 set delay counter

Store the value 0x0040_0000 into the x7 register. The x7 register is used as a delay counter timer. Use the delay generated by the program’s own loop to delay the LED for 1s. It takes about 1s to run 40_0000 loops.

START: ADDI x10, x0, 0x80;        # x10 = 0x80, set gpio bit 7

START is the address label, followed by “:”, ADDI is the instruction code, and the register and the register or immediate data should be separated by “,”. Use “;” at the end of the command. Use “#” if you want to add a comment. The compiler will not compile the statement after “#”.

This instruction adds the value of x0 to 0x80 and stores the sum in x10. Since the value of x0 is 0, the actual effect is to store the value 0x80 in the x10 register. The X10 register is used to turn the LED on and off. The binary number corresponding to 0x80 is 1000_0000. But at present, the value in x10 cannot be directly output to the address of gpio. Because the led is a common anode design, one end is already 3.3V high level, if the GPIO interface corresponding to led7 is 1, led7 will not be lit. Instead, other LEDs will be lit, because LED6-0 is low at this time. So you need to invert the value of x10 as a whole.

NOT x18, x10;                                            # x18 = ~x10 negated output

The NOT instruction is a pseudo-instruction, which inverts the value stored in x10 and stores it in x18. It means that the last output value is stored in the x18 register.

SH x18, 0(x8);                                           # addr[0xf000_0000] = val[0x18]

Store the value of x18 at an address, which is obtained by adding the value of x8 and the immediate value 0. Because the value previously stored in X8 is the address of gpio, the value of x18 is output to gpio here.

SH x0, 4(x8);                                             # addr[0xf000_0004] = 0 as output

Add 4 to the gpio address to generate the direction control register address f000_0004. Use the store instruction to store the value of x0 at address f000_0004. In this way, the value of the direction control register is set to 0, and LED7 is lit.

After the light is lit, it needs to be kept for 1s to perform a delayed operation. The program is implemented by loop loop.

ADDI x6, x6, 1;                                          # x6 = x6 + 1

x6 acts as an accumulator, adding 1 once per loop

BNE x6, x7, LOOP;                                  # if(x6 != x7) goto loop

It is judged that when x6 is not equal to x7, the program jumps to loop. That is, x6 continues to accumulate until x6 is equal to x7, and the loop is completed. Proceed to the next instruction.

ADDI x6, x0, 0;                                         # x6 = 0

Clear x6, because the x6 accumulator will be used after lighting other lights and delaying 1s.

SRLI x10, x10,1;                                        # x10 >> 1 , right shift 1 bit

Use the srli command, which shifts the value in x10 to the right by 1 bit to turn on the next light.

BEQ x10, x0, START;                             # if(x10 == 0x0) goto start

After moving, the value of x10 changes from 0x80 (1000_0000) to 0x40 (0100_0000), then moves right to 0x20, and finally becomes 0. At this point, all LEDs are turned off after inversion. The BEQ statement is needed to deal with all zeros. When x10 is all 0, jump to start, which is the position at the beginning of the program. The value of X10 is assigned the value 0x80.

NOT x18, x10;                                            # x18 = ~x10

SH x18, 0(x8);                                           # addr[0xf000_0000] = x18

JAL LOOP;                                                  # pc = loop

If x10 is not equal to 0, prove that x10 contains 1 in binary representation. It is not equal to 0. At this time, x10 is inverted and then output, which is equivalent to lighting the next lamp. Then jal jumps to the loop to delay 1s.

Posted in FPGA, RISC-V, RISC-V Textbook, Textbook and Training Project

Related Articles

Leave a Reply

Your email address will not be published.

Leave the field below empty!