如何建立自己的RISC-V编译环境--汇编?
本帖最后由 新ちゃん 于 2020-8-20 22:18 编辑如何建立自己的RISC-V编译环境–汇编?
1.RISC-V编译环境框架
这是我RISC-V编译环境的架构:buildcasecommontoolchain
一级目录二级目录说明
build-xx.hex、xx.bin、xx.dump & xx.elf生成的目录。
-Makefile编译脚本。
-add(例子)生成文件,根据test.c生成的xx.hex、xx.bin、xx.dump & xx.elf
project-各个目录的源代码。
-add(例子)放add项目的S文件。
common-各个项目公用的文件。
-encoding.h/riscv-tests/env目录中的H文件。
-asm.h/riscv-tests/env/p/riscv_test.h文件,只是我改为asm.h。
-asm.ld/riscv-tests/env/p/link.ld文件,用于链接物理地址和汇编,我改名字为asm.ld了。
toolchain-工具链。我在/riscv-tools/riscv-gnu-toolchain/的工具链上提取了对我有用的部分。
2.各目录内容介绍
2.1 toolchain:工具链目录我不打算介绍,如果有不明白的,可以看 rocket-chip工具链的编译与使用
2.2 project:这个目录就是放你的项目代码的。每个项目建立一个目录。在这个例子中,我放的是add目录,add的源代码如下(全为汇编代码):
此外,RISC-V汇编不熟悉的可以参考以下链接:
https://github.com/riscv/riscv-asm-manual/blob/master/riscv-asm.md
https://mp.weixin.qq.com/s/-syKN0DibKGGPCllaeNqMg
https://mp.weixin.qq.com/s/jyI-SSm_5Gg-KQyjKsIj5Q
https://mp.weixin.qq.com/s/3RHss3vhfK004-TtM8fpeA
https://mp.weixin.qq.com/s/Ln4qBYvSsgRvdiK1IJqI6Q
下面贴图为.section的说明。
注意,下文中使用“###”形式的均是我添加的注释,原代码中没有。
<font size="3">#*****************************************************************************
# add.S
#-----------------------------------------------------------------------------
#
# Test add instructions.
#
#屏蔽了原来的include "riscv_test.h"
######include "riscv_test.h"
#使用我自己的asm.h
#include "asm.h"
###使用RVTEST_RV32U段内容
RVTEST_RV32U
###使用RVTEST_CODE_BEGIN段内容
RVTEST_CODE_BEGIN
###按2^2字节(4字节)数据对齐
.align 2
###“.option push”伪操作暂时将当前的选项设置保存起来,
###从而允许之后使用.option伪操作指定新的选项;
###而“.option pop”伪操作将最近保存的选项设置恢复出来重新生效。
###通过“.option push”和“.option pop”的组合,便可以在汇编程序中在不影响全
###局选项设置的情况下,为其中嵌入的某一段代码特别地设置不同的选项。
###意思就是:
###在add.S这个汇编文件中,将前面其他.S/.h文件中的配置选项保存起来,
###然后在add.S文件中设置一些新的选项,新的选项就是下面的.option norvc
.option push
###伪操作表示接下来的汇编程序不可以被汇编生成16位宽的压缩指令。
.option norvc
###定义标签asm_start
asm_start:
###将0xAAAAAAAA赋给a1
li a1, 0xAAAAAAAA
###将0x22222222赋给a1
li a0, 0x22222222
###将a0和a1相加,并出入a2中
adda2, a1, a0
###将0x7000_0000存入a4中
luia4, 0x70000
###将a4的值+4并存入a6中
addi a6, a4, 4
###将a2的结果存到总线为0x7000_0004的memory中
sw a2, 0(a6)
###将0xFF存到a5中
li a5, 255
###将a5的结果存到总线为0x7000_0000的memory中,用于停止VCS仿真
sw a5, 0(a4)
###恢复原选项配置
.option pop
###使用RVTEST_CODE_END段内容
RVTEST_CODE_END
###数据段.data:已初始化的C程序全局变量和静态局部变量。
.data
###使用RVTEST_DATA_BEGIN段内容
RVTEST_DATA_BEGIN
###使用RVTEST_DATA_END段内容
RVTEST_DATA_END</font>
以下的定义在asm.h中有详细的说明。
RVTEST_RV32U
RVTEST_CODE_BEGIN
RVTEST_CODE_END
RVTEST_DATA_BEGIN
RVTEST_DATA_END
2.3 common:这个目录放的是通用的内容,例如asm.h和asm.lk。
2.3.1 encoding.h:这个文件不解释,就是RISC-V各重要CSR寄存器的定义。
2.3.2 asm.h:这个我是根据/riscv-tests/env/p/riscv_test.h文件进行修改的,下面简单说明一下里面各汇编的含义。
此外,我修改的是p目录下的文件,p目录下是单核的,而且是没有MMU的。具体env中各目录的差异大家可以看:
https://github.com/riscv/riscv-tests/tree/7ef425b4aea0a93568c788e68acebcc2f08da8d6
// See LICENSE for license details.
#ifndef _ENV_PHYSICAL_SINGLE_CORE_H
#define _ENV_PHYSICAL_SINGLE_CORE_H
#include "./encoding.h"
//-----------------------------------------------------------------------
// Begin Macro
//-----------------------------------------------------------------------
###定义宏init
#define RVTEST_RV64U \
.macro init; \
.endm
###定义宏init,同时运行RVTEST_FP_ENABLE段
#define RVTEST_RV64UF \
.macro init; \
RVTEST_FP_ENABLE; \
.endm
###定义宏init
#define RVTEST_RV32U \
.macro init; \
.endm
###定义宏init,同时运行RVTEST_FP_ENABLE段
#define RVTEST_RV32UF \
.macro init; \
RVTEST_FP_ENABLE; \
.endm
###定义宏init
#define RVTEST_RV64M \
.macro init; \
RVTEST_ENABLE_MACHINE; \
.endm
###定义宏init,同时运行RVTEST_ENABLE_SUPERVISOR段
#define RVTEST_RV64S \
.macro init; \
RVTEST_ENABLE_SUPERVISOR; \
.endm
###定义宏init,同时运行RVTEST_ENABLE_MACHINE段
#define RVTEST_RV32M \
.macro init; \
RVTEST_ENABLE_MACHINE; \
.endm
###定义宏init,同时运行RVTEST_ENABLE_SUPERVISOR段
#define RVTEST_RV32S \
.macro init; \
RVTEST_ENABLE_SUPERVISOR; \
.endm
###运行CHECK_XLEN段
#if __riscv_xlen == 64
###将1装入a0;将a0的值进行逻辑左移31位;如果a0的值≥0,跳转至标志为1的地方;
###运行RVTEST_PASS段;标志1的位置。
###1f的意思是向前找第一个数字为1的标志,即往下面的行找
# define CHECK_XLEN li a0, 1; slli a0, a0, 31; bgez a0, 1f; RVTEST_PASS; 1:
#else
###将1装入a0;将a0的值进行逻辑左移31位;如果a0的值<0,跳转至标志为1的地方;
###运行RVTEST_PASS段;标志1的位置。
# define CHECK_XLEN li a0, 1; slli a0, a0, 31; bltz a0, 1f; RVTEST_PASS; 1:
#endif
###定义INIT_PMP段的内容
#define INIT_PMP \
###将标志1的PC赋值给t0
la t0, 1f; \
###将t0的值赋值给mtvec
csrw mtvec, t0; \
###将-1赋值给t0,实际上是赋0xFFFF_FFFF给t0
li t0, -1; /* Set up a PMP to permit all accesses */ \
###将t0的值写入pmpaddr0 CSR寄存器中
csrw pmpaddr0, t0; \
###将PMP_NAPOT | PMP_R | PMP_W | PMP_X的值赋给t0,这些值在encoding.h中定义
li t0, PMP_NAPOT | PMP_R | PMP_W | PMP_X; \
###将t0的值写入pmpcfg0 CSR寄存器中
csrw pmpcfg0, t0; \
###按2^2字节(4字节)数据对齐
.align 2; \
###标志1的具体位置
1:
/*Configure MMU*/
/*SATP:Supervisor Address Translation and Protection (satp) Register*/
#define INIT_SATP \
###将标志1的PC赋值给t0
la t0, 1f; \
###t0的值赋给mtvec
csrw mtvec, t0; \
###写CSR寄存器sptrb为0
csrwi sptbr, 0; \
###按2^2字节(4字节)数据对齐
.align 2; \
###标志1的具体位置
1:
###定义DELEGATE_NO_TRAPS段的内容,这是对应多模式情况下的
###将中断权限分配到其他模式中,不单单机器模式可以处理中断
#define DELEGATE_NO_TRAPS \
###将标志1的PC赋值给t0
la t0, 1f; \
###t0的值赋给mtvec
csrw mtvec, t0; \
###写CSR寄存器medeleg(异常)为0,如果没有这个CSR寄存器会产生异常
csrwi medeleg, 0; \
###写CSR寄存器mideleg(中断)为0,如果没有这个CSR寄存器会产生异常
csrwi mideleg, 0; \
###写CSR寄存器mie为0,清除中断使能
csrwi mie, 0; \
###按2^2字节(4字节)数据对齐
.align 2; \
###标志1的具体位置
1:
###定义RVTEST_ENABLE_SUPERVISOR段的内容
###SUPERVISOR模式的定义
#define RVTEST_ENABLE_SUPERVISOR \
li a0, MSTATUS_MPP & (MSTATUS_MPP >> 1); \
csrs mstatus, a0; \
###将SIP_SSIP | SIP_STIP赋给a0
li a0, SIP_SSIP | SIP_STIP; \
###将a0赋值给mideleg CSR寄存器,用于授权SUPERVISOR模式可以处理中断
csrs mideleg, a0; \
###定义RVTEST_ENABLE_MACHINE段的内容
### MACHINE模式的定义
#define RVTEST_ENABLE_MACHINE \
###将MSTATUS_MPP赋给a0,MSTATUS_MPP在encoding.h中定义
li a0, MSTATUS_MPP; \
###将a0的值赋给mstatus
csrs mstatus, a0; \
###定义VTEST_FP_ENABLE段的内容
###浮点模式的定义
#define RVTEST_FP_ENABLE \
###将MSTATUS_FS & (MSTATUS_FS >> 1)赋给a0
li a0, MSTATUS_FS & (MSTATUS_FS >> 1); \
###将a0赋给mstatus
csrs mstatus, a0; \
###将0写入到fcsr CSR寄存器中,如果核没有支持浮点功能,则不会存在fcsr,
###写fcsr的行为会报异常
csrwi fcsr, 0
###定义RISCV_MULTICORE_DISABLE段的内容
###不使用多核的配置
#define RISCV_MULTICORE_DISABLE \
###读mhartid CSR寄存器的值,并赋给a0
csrr a0, mhartid; \
###判断a0的值,如果不等于0,则一直保持在标志1的位置,即多核情况下,
###一直保持在标志1的位置,1b为向后找第一个数字为1的标志,也就是这条指令
###若等于0,则为单核,可以进行下面的指令执行
1: bnez a0, 1b
###定义EXTRA_TVEC_USER、EXTRA_TVEC_MACHINE、
###EXTRA_INIT和EXTRA_INIT_TIMER,这些段虽然定义了,但没有内容,
###需要用到的自己补充汇编内容
#define EXTRA_TVEC_USER
#define EXTRA_TVEC_MACHINE
#define EXTRA_INIT
#define EXTRA_INIT_TIMER
###定义NTERRUPT_HANDLER段内容,将跳到other_exception函数中
#define INTERRUPT_HANDLER j other_exception /* No interrupts should occur */
###定义RVTEST_CODE_BEGIN段的内容
#define RVTEST_CODE_BEGIN \
###指令代码段.text
.section .text.init; \
###按2^6(64字节)对齐
.align6; \
###占个位置,后面有对stvec_handler定义的,就会覆盖
.weak stvec_handler; \
###占个位置,后面有对mtvec_handler定义的,就会覆盖
.weak mtvec_handler; \
### .global和.globl伪操作用于定义一个全局的符号,使得链接器能够全
###局识别它,即一个程序文件中定义的符号能够被所有
###其他程序文件可见。指定汇编程序入口为_start,即代码的最开始地方。
.globl _start; \
_start: \
/* reset vector */ \
###跳到reset_vector函数中
j reset_vector; \
###跳到asm_start函数中,这是我自己改的,不用那么多繁琐的步骤,直接跳
/* j asm_start; */ \
###按2^2(4字节)对齐
.align 2; \
trap_vector: \
/* test whether the test came from pass/fail */ \
###将mcasue的值赋给t5
csrr t5, mcause; \
###将CAUSE_USER_ECALL值赋给t6
li t6, CAUSE_USER_ECALL; \
###如果t5和t6相等,则跳到write_tohost函数中
beq t5, t6, write_tohost; \
###将CAUSE_SUPERVISOR_ECALL值赋给t6
li t6, CAUSE_SUPERVISOR_ECALL; \
###如果t5和t6相等,则跳到write_tohost函数中
beq t5, t6, write_tohost; \
###将CAUSE_MACHINE_ECALL值赋给t6
li t6, CAUSE_MACHINE_ECALL; \
###如果t5和t6相等,则跳到write_tohost函数中
beq t5, t6, write_tohost; \
/* if an mtvec_handler is defined, jump to it */ \
la t5, mtvec_handler; \
###如果没有定义mtvec_handler函数,则mtvec_handler为0,等于0则跳到标志1
beqz t5, 1f; \
jr t5; \
/* was it an interrupt or an exception? */ \
###前面的异常/中断都不符合了,才跳到这,对mcause的值到t5中
1: csrr t5, mcause; \
###如果t5≥0,则跳到handle_exception中
bgez t5, handle_exception; \
###如果连handle_exception都不是,那就跳到other_exception中
INTERRUPT_HANDLER; \
handle_exception: \
/* we don't know how to handle whatever the exception was */ \
###意思就是,这里需要你自己补充,如果想用的话
other_exception: \
/* some unhandlable exception occurred */ \
###意思就是,这里需要你自己补充,如果想用的话
###利用TESTNUM和立即数1337进行或操作
1: ori TESTNUM, TESTNUM, 1337; \
### write_tohost函数
write_tohost: \
###将TESTNUM的值存到tohost的位置上
sw TESTNUM, tohost, t5; \
###PC一直保持在当前位置,进入死循环
j write_tohost; \
reset_vector: \
###运行RISCV_MULTICORE_DISABLE的内容
RISCV_MULTICORE_DISABLE; \
###运行INIT_SATP的内容,因为我生成的没有MMU,所以屏蔽了这句
###如果不屏蔽,会产生异常,但也没有影响,因为异常处理的PC就是接下来的指令
/*INIT_SATP;*/ \
###运行INIT_PMP的内容
INIT_PMP; \
###运行DELEGATE_NO_TRAPS的内容
DELEGATE_NO_TRAPS; \
###因为运行DELEGATE_NO_TRAPS时产生了异常,所以下一句清除异常
csrw mcause, 0; \
###将0赋值给TESTNUM(gp)
li TESTNUM, 0; \
###将trap_vector的物理地址赋值给t0
la t0, trap_vector; \
###将t0赋值给mtvec CSR寄存器
csrw mtvec, t0; \
###运行CHECK_XLEN的内容
CHECK_XLEN; \
/* if an stvec_handler is defined, delegate exceptions to it */ \
###这里如果有定义stvec_handler就会进入下面的指令,没有就跳到标志1
la t0, stvec_handler; \
beqz t0, 1f; \
csrw stvec, t0; \
###将一些特殊的中断或者异常,MMU相关,授权给其他模式处理
li t0, (1 << CAUSE_LOAD_PAGE_FAULT) | \
(1 << CAUSE_STORE_PAGE_FAULT) | \
(1 << CAUSE_FETCH_PAGE_FAULT) | \
(1 << CAUSE_MISALIGNED_FETCH) | \
(1 << CAUSE_USER_ECALL) | \
(1 << CAUSE_BREAKPOINT); \
csrw medeleg, t0; \
csrr t1, medeleg; \
bne t0, t1, other_exception; \
###将0写入mstatus CSR寄存器中
1: csrwi mstatus, 0; \
###这句编译出来的是auipc t0,0x0
init; \
###这个没有定义,所以是空的
EXTRA_INIT; \
###这个没有定义,所以是空的
EXTRA_INIT_TIMER; \
###跳到标志1的位置赋值给t0
la t0, 1f; \
###将t0赋值给mepc
csrw mepc, t0; \
###将mhartid的值赋值给a0
csrr a0, mhartid; \
###M模式return
mret; \
1:
//-----------------------------------------------------------------------
// End Macro
//-----------------------------------------------------------------------
#define RVTEST_CODE_END \
###UNIMP : C0001073. This is an alias for CSRRW x0, cycle, x0. Since cycle is a
###read-only CSR, then #(whether this CSR exists or not) an attempt to write into
###it will generate an illegal instruction #exception. This 32-bit form of UNIMP is
###emitted when targeting a system without the C #extension, or when the .option
###norvc directive is used.
unimp
//-----------------------------------------------------------------------
// Pass/Fail Macro
//-----------------------------------------------------------------------
#define RVTEST_PASS \
###执行fence指令,可以清楚ICache内容;将1赋值给gp;最后执行ecall
fence; \
li TESTNUM, 1; \
ecall
#define TESTNUM gp
#define RVTEST_FAIL \
###执行fence指令
fence; \
###如何TESTNUM等于0,则一直停在这,不为0则执行下面的程序
1: beqz TESTNUM, 1b; \
###将TESTNUM的值逻辑左移1位,并重新赋值给TESTNUM
sll TESTNUM, TESTNUM, 1; \
###将TESTNUM的值与1或,并重新赋值给TESTNUM
or TESTNUM, TESTNUM, 1; \
ecall
//-----------------------------------------------------------------------
// Data Section Macro
//-----------------------------------------------------------------------
###定义了EXTRA_DATA,但是是空的
#define EXTRA_DATA
###此段基本是空的,需要用到的,要自己添加内容
#define RVTEST_DATA_BEGIN \
EXTRA_DATA \
###将之前的段设置保存,并将当前的段设置改名.tohost
.pushsection .tohost,"aw",@progbits; \
.align 6; .global tohost; tohost: .dword 0; \
.align 6; .global fromhost; fromhost: .dword 0; \
###恢复之前的段设置
.popsection; \
.align 4; .global begin_signature; begin_signature:
#define RVTEST_DATA_END .align 4; .global end_signature; end_signature:
#endif
2.3.3 asm.lk:这个文件就是/riscv-tests/env/p/link.ld文件,只是我改了名字。SECTIONS
{
/*. = 0x80000000;*/
###这里我修改了,因为我想它从我的ROM直接执行,我的ROM地址是0x10000
. = 0x00010000;
.text.init : { *(.text.init) }
. = ALIGN(0x1000);
.tohost : { *(.tohost) }
. = ALIGN(0x1000);
.text : { *(.text) }
. = ALIGN(0x1000);
.data : { *(.data) }
.bss : { *(.bss) }
_end = .;
}
2.4 bulid:这个目录最主要的是Makefile脚本,用于调用工具链编译asm。我只贴部分代码,是生成elf的代码,生成bin、hex和dump的方法大家参考/riscv-tests/benchmarks/Makefile。
部分代码:
#--------------------------------------------------------------------
# author : x
# data : 2019.4.2
# name : project compiler for rocket-chip
#--------------------------------------------------------------------
proj_name ?= add
#--------------------------------------------------------------------
# Build rules
#--------------------------------------------------------------------
RISCV_PREFIX ?= ../toolchain/bin/riscv32-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_GCC_OPTS ?= -DPREALLOCATE=1 -mcmodel=medany -static -std=gnu99 -O2 -ffast-math -fno-common -fno-builtin-printf
RISCV_LINK_OPTS ?= -static -nostdlib -nostartfiles -lm -lgcc -T ../common/asm.ld
SOURCE_FILE ?= ../common ../asm/$(proj_name)/*.S
#--------------------------------------------------------------------
# Object
#--------------------------------------------------------------------
default:
make all
elf:
$(RISCV_GCC) $(RISCV_GCC_OPTS) -o $(proj_name)/$(proj_name).elf $(SOURCE_FILE) $(RISCV_LINK_OPTS)
all: elf
3.反汇编文件
add/add.elf: file format elf32-littleriscv
Disassembly of section .text.init:
00010000 <_start>:
_start():
10000: a081 j 10040 <reset_vector>
10002: 0001 nop
00010004 <trap_vector>:
trap_vector():
10004: 34202f73 csrr t5,mcause
10008: 4fa1 li t6,8
1000a: 03ff0663 beq t5,t6,10036 <write_tohost>
1000e: 4fa5 li t6,9
10010: 03ff0363 beq t5,t6,10036 <write_tohost>
10014: 4fad li t6,11
10016: 03ff0063 beq t5,t6,10036 <write_tohost>
1001a: ffff0f17 auipc t5,0xffff0
1001e: fe6f0f13 addi t5,t5,-26 # 0 <_start-0x10000>
10022: 000f0363 beqz t5,10028 <trap_vector+0x24>
10026: 8f02 jr t5
10028: 34202f73 csrr t5,mcause
1002c: 000f5363 bgez t5,10032 <handle_exception>
10030: a009 j 10032 <handle_exception>
00010032 <handle_exception>:
handle_exception():
10032: 5391e193 ori gp,gp,1337
00010036 <write_tohost>:
write_tohost():
10036: 00001f17 auipc t5,0x1
1003a: fc3f2523 sw gp,-54(t5) # 11000 <tohost>
1003e: bfe5 j 10036 <write_tohost>
00010040 <reset_vector>:
reset_vector():
10040: f1402573 csrr a0,mhartid
10044: e101 bnez a0,10044 <reset_vector+0x4>
10046: 00000297 auipc t0,0x0
1004a: 01a28293 addi t0,t0,26 # 10060 <reset_vector+0x20>
1004e: 30529073 csrw mtvec,t0
10052: 52fd li t0,-1
10054: 3b029073 csrw pmpaddr0,t0
10058: 42fd li t0,31
1005a: 3a029073 csrw pmpcfg0,t0
1005e: 0001 nop
10060: 00000297 auipc t0,0x0
10064: 01828293 addi t0,t0,24 # 10078 <reset_vector+0x38>
10068: 30529073 csrw mtvec,t0
1006c: 30205073 csrwi medeleg,0
10070: 30305073 csrwi mideleg,0
10074: 30405073 csrwi mie,0
10078: 34205073 csrwi mcause,0
1007c: 4181 li gp,0
1007e: 00000297 auipc t0,0x0
10082: f8628293 addi t0,t0,-122 # 10004 <trap_vector>
10086: 30529073 csrw mtvec,t0
1008a: 4505 li a0,1
1008c: 057e slli a0,a0,0x1f
1008e: 00054763 bltz a0,1009c <reset_vector+0x5c>
10092: 0ff0000f fence
10096: 4185 li gp,1
10098: 00000073 ecall
1009c: ffff0297 auipc t0,0xffff0
100a0: f6428293 addi t0,t0,-156 # 0 <_start-0x10000>
100a4: 00028e63 beqz t0,100c0 <reset_vector+0x80>
100a8: 10529073 csrw stvec,t0
100ac: 0000b2b7 lui t0,0xb
100b0: 10928293 addi t0,t0,265 # b109 <_start-0x4ef7>
100b4: 30229073 csrw medeleg,t0
100b8: 30202373 csrr t1,medeleg
100bc: f6629be3 bne t0,t1,10032 <handle_exception>
100c0: 30005073 csrwi mstatus,0
100c4: 00000297 auipc t0,0x0
100c8: 01428293 addi t0,t0,20 # 100d8 <asm_start>
100cc: 34129073 csrw mepc,t0
100d0: f1402573 csrr a0,mhartid
100d4: 30200073 mret
000100d8 <asm_start>:
asm_start():
100d8: aaaab5b7 lui a1,0xaaaab
100dc: aaa58593 addi a1,a1,-1366 # aaaaaaaa <_end+0xaaa98aaa>
100e0: 22222537 lui a0,0x22222
100e4: 22250513 addi a0,a0,546 # 22222222 <_end+0x22210222>
100e8: 00a58633 add a2,a1,a0
100ec: 70000737 lui a4,0x70000
100f0: 00470813 addi a6,a4,4 # 70000004 <_end+0x6ffee004>
100f4: 00c82023 sw a2,0(a6)
100f8: 0ff00793 li a5,255
100fc: 00f72023 sw a5,0(a4)
...
Disassembly of section .tohost:
00011000 <tohost>:
...
00011040 <fromhost>:
4.仿真测试
在说明仿真前,先贴部分TB中的代码,此代码用于自动停止VCS仿真。
具体的操作是CPU利用总线往0x7000_0000的地址写0xFF的值,然后TB就会执行$finish。
TB的代码是:/**************************/
/* download memory */
/**************************/
initial begin
#100 $readmemh("../compiler/build/add/add.hex",ldut.bootrom.rom);
end
/**************************/
/* capture finish */
/**************************/
always @* begin
if((mmio_axi4_0_aw_bits_addr == 31'h70000000) && mmio_axi4_0_aw_ready && mmio_axi4_0_aw_valid && (mmio_axi4_0_w_bits_data == 32'hFF)) begin
#10 $finish(2);
end
end
汇编中对应的代码是: luia4, 0x70000
li a5, 255
sw a5, 0(a4)
接下来是仿真的波形分析。
本篇完,感谢关注:RISC-V单片机中文网
页:
[1]