def gen_data_page(self, hart_id, pattern, is_kernel=0, amo=0): temp_data = [] self.data_page_str.clear() if is_kernel: self.mem_region_setting = cfg.s_mem_region elif amo: self.mem_region_setting = cfg.amo_region else: self.mem_region_setting = cfg.mem_region for i in range(len(self.mem_region_setting)): logging.info("Generate data section: {} size:0x{} xwr:0x{}".format( self.mem_region_setting[i].name, self.mem_region_setting[i].size_in_bytes, self.mem_region_setting[i].xwr)) if amo: if cfg.use_push_data_section: self.data_page_str.append( ".pushsection .{},\"aw\",@progbits;".format( self.mem_region_setting[i].name)) else: self.data_page_str.append( ".section .{},\"aw\",@progbits;".format( self.mem_region_setting[i].name)) self.data_page_str.append("{}:".format( self.mem_region_setting[i].name)) else: if cfg.use_push_data_section: self.data_page_str.append( ".pushsection .{},\"aw\",@progbits;".format( pkg_ins.hart_prefix(hart_id) + self.mem_region_setting[i].name)) else: self.data_page_str.append( ".section .{},\"aw\",@progbits;".format( pkg_ins.hart_prefix(hart_id) + self.mem_region_setting[i].name)) self.data_page_str.append("{}:".format( pkg_ins.hart_prefix(hart_id) + self.mem_region_setting[i].name)) page_size = self.mem_region_setting[i].size_in_bytes for i in range(0, page_size, 32): if page_size - 1 >= 32: temp_data = self.gen_data(idx=i, pattern=pattern, num_of_bytes=32, data=temp_data) else: temp_data = self.gen_data(idx=i, pattern=pattern, num_of_bytes=page_size - 1, data=temp_data) tmp_str = pkg_ins.format_string( ".word {}".format(pkg_ins.format_data(temp_data)), pkg_ins.LABEL_STR_LEN) self.data_page_str.append(tmp_str) if cfg.use_push_data_section: self.data_page_str.append(".popsection")
def setup_epc(self, hart): instr = [] instr.append("la x{}, {}init".format(cfg.gpr[0].value, pkg_ins.hart_prefix(hart))) # if(cfg.virtual_addr_translation_on): # instr.append("slli x{}, x{}") # TODO mode_name = cfg.init_privileged_mode instr.append("csrw mepc, x{}".format(cfg.gpr[0].value)) if(not rcs.support_pmp): # TODO instr.append("j {}init_{}".format(pkg_ins.hart_prefix(hart), mode_name.name.lower())) self.gen_section(pkg_ins.get_label("mepc_setup", hart), instr)
def add_rs1_init_la_instr(self, gpr, idx, base = 0): la_instr = riscv_pseudo_instr() la_instr.pseudo_instr_name = riscv_pseudo_instr_name_t.LA la_instr.rd = gpr if self.load_store_shared_memory: la_instr.imm_str = "{}+{}".format(cfg.amo_region[idx].name, base) elif self.kernel_mode: la_instr.imm_str = "{}{}+{}".format(pkg_ins.hart_prefix(self.hart), cfg.s_mem_region[idx].name, base) else: la_instr.imm_str = "{}{}+{}".format(pkg_ins.hart_prefix(self.hart), cfg.mem_region[idx].name, base) self.instr_list.insert(0, la_instr)
def setup_epc(self, hart): instr = [] instr.append("la x{}, {}init".format(cfg.gpr[0], pkg_ins.hart_prefix(hart))) if cfg.virtual_addr_translation_on: # For supervisor and user mode, use virtual address instead of physical address. # Virtual address starts from address 0x0, here only the lower 12 bits are kept # as virtual address offset. instr.append("slli x{}, x{}, {}".format(cfg.gpr[0], cfg.gpr[0], rcs.XLEN - 12) + "srli x{}, x{}, {}".format(cfg.gpr[0], cfg.gpr[0], rcs.XLEN - 12)) mode_name = cfg.init_privileged_mode.name instr.append("csrw {}, x{}".format(hex(privileged_reg_t.MEPC), cfg.gpr[0])) if not rcs.support_pmp: instr.append("j {}init_{}".format(pkg_ins.hart_prefix(hart), mode_name.lower())) self.gen_section(pkg_ins.get_label("mepc_setup", hart), instr)
def trap_vector_init(self, hart): instr = [] for items in rcs.supported_privileged_mode: if(items == "MACHINE_MODE"): trap_vec_reg = privileged_reg_t.MTVEC elif(items == "SUPERVISOR_MODE"): trap_vec_reg = privileged_reg_t.STVEC elif(items == "USER_MODE"): trap_vec_reg = privileged_reg_t.UTVEC else: logging.critical( "[riscv_asm_program_gen] Unsupported privileged_mode {}".format(items)) if(items == "USER_MODE" and not (rcs.support_umode_trap)): continue if(items < cfg.init_privileged_mode.name): continue tvec_name = trap_vec_reg.name tvec_name = tvec_name.lower() instr.append("la x{}, {}{}_handler".format( cfg.gpr[0].value, pkg_ins.hart_prefix(hart), tvec_name)) if(rcs.SATP_MODE != "BARE" and items != "MACHINE_MODE"): instr.append("slli x{}, x{}, {}\n".format(cfg.gpr[0].value, cfg.gpr[0].value, rcs.XLEN - 20) + "srli x{}, x{}, {}".format(cfg.gpr[0].value, cfg.gpr[0].value, rcs.XLEN - 20)) instr.append("ori x{}, x{}, {}".format( cfg.gpr[0].value, cfg.gpr[0].value, cfg.mtvec_mode.value)) instr.append("csrw {}, x{} # {}".format( hex(trap_vec_reg.value), cfg.gpr[0].value, trap_vec_reg.name)) self.gen_section(pkg_ins.get_label("trap_vec_init", hart), instr)
def trap_vector_init(self, hart): instr = [] for mode in rcs.supported_privileged_mode: if mode == privileged_mode_t.MACHINE_MODE: trap_vec_reg = privileged_reg_t.MTVEC elif mode == privileged_mode_t.SUPERVISOR_MODE: trap_vec_reg = privileged_reg_t.STVEC elif mode == privileged_mode_t.USER_MODE: trap_vec_reg = privileged_reg_t.UTVEC else: logging.critical("Unsupported privileged_mode {}".format(mode.name)) sys.exit(1) if(mode == privileged_mode_t.USER_MODE and not (rcs.support_umode_trap)): continue if mode < cfg.init_privileged_mode: continue tvec_name = trap_vec_reg.name tvec_name = tvec_name.lower() instr.append("la x{}, {}{}_handler".format( cfg.gpr[0], pkg_ins.hart_prefix(hart), tvec_name)) if(rcs.SATP_MODE != satp_mode_t.BARE and mode != privileged_mode_t.MACHINE_MODE): instr.append("slli x{}, x{}, {}\n".format(cfg.gpr[0], cfg.gpr[0], rcs.XLEN - 20) + "srli x{}, x{}, {}".format(cfg.gpr[0], cfg.gpr[0], rcs.XLEN - 20)) instr.append("ori x{}, x{}, {}".format(cfg.gpr[0], cfg.gpr[0], cfg.mtvec_mode)) instr.append("csrw {}, x{} # {}".format( hex(trap_vec_reg), cfg.gpr[0], trap_vec_reg.name)) self.gen_section(pkg_ins.get_label("trap_vec_init", hart), instr)
def gen_interrupt_vector_table(self, hart, mode, status, cause, ie, ip, scratch, instr): '''In vector mode, the BASE address is shared between interrupt 0 and exception handling. When vectored interrupts are enabled, interrupt cause 0, which corresponds to user-mode software interrupts, are vectored to the same location as synchronous exceptions. This ambiguity does not arise in practice, since user-mode software interrupts are either disabled or delegated''' instr.extend( (".option norvc;", "j {}{}mode_exception_handler".format(pkg_ins.hart_prefix(hart), mode))) # Redirect the interrupt to the corresponding interrupt handler for i in range(1, rcs.max_interrupt_vector_num): instr.append("j {}{}mode_intr_vector_{}".format( pkg_ins.hart_prefix(hart), mode, i)) if not cfg.disable_compressed_instr: instr.append(".option rvc;") for i in range(1, rcs.max_interrupt_vector_num): intr_handler = [] pkg_ins.push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, cfg.sp, cfg.tp, intr_handler) self.gen_signature_handshake( instr=intr_handler, signature_type=signature_type_t.CORE_STATUS, core_status=core_status_t.HANDLING_IRQ) intr_handler.extend(( "csrr x{}, {} # {}".format(cfg.gpr[0], hex(cause), cause.name), # Terminate the test if xCause[31] != 0 (indicating exception) "srli x{}, x{}, {}".format(cfg.gpr[0], cfg.gpr[0], hex(rcs.XLEN - 1)), "beqz x{}, 1f".format(cfg.gpr[0]))) csr_list = [status, cause, ie, ip] for csr_t in csr_list: self.gen_signature_handshake( instr=intr_handler, signature_type=signature_type_t.WRITE_CSR, csr=csr_t) # Jump to commmon interrupt handling routine intr_handler.extend( ("j {}{}mode_intr_handler".format(pkg_ins.hart_prefix(hart), mode), "1: la x{}, test_done".format(cfg.scratch_reg), "jalr x0, x{}, 0".format(cfg.scratch_reg))) self.gen_section( pkg_ins.get_label("{}mode_intr_vector_{}".format(mode, i), hart), intr_handler)
def pre_enter_privileged_mode(self, hart): instr = [] string = [] string.append("la x{}, {}kernel_stack_end".format(cfg.tp.value, pkg_ins.hart_prefix(hart))) self.gen_section(pkg_ins.get_label("kernel_sp", hart), string) if not cfg.no_delegation and (cfg.init_privileged_mode != privileged_mode_t.MACHINE_MODE): self.gen_delegation(hart) self.trap_vector_init(hart) self.setup_pmp(hart) if(cfg.virtual_addr_translation_on): self.page_table_list.process_page_table(instr) self.gen_section(pkg_ins.get_label("process_pt", hart), instr) self.setup_epc(hart) self.gen_privileged_mode_switch_routine(hart)
def gen_init_section(self, hart): string = pkg_ins.format_string("init:", pkg_ins.LABEL_STR_LEN) self.instr_stream.append(string) if (cfg.enable_floating_point): self.init_floating_point_gpr() self.init_gpr() # Init stack pointer to point to the end of the user stack string = "{}la x{}, {}user_stack_end".format( pkg_ins.indent, cfg.sp.value, pkg_ins.hart_prefix(hart)) self.instr_stream.append(string) if (cfg.enable_vector_extension): self.init_vector_engine() self.core_is_initialized() self.gen_dummy_csr_write() if (rcs.support_pmp): string = pkg_ins.indent + "j main" self.instr_stream.append(string)
def gen_trap_handler_section(self, hart, mode, cause, tvec, tval, epc, scratch, status, ie, ip): is_interrupt = 1 tvec_name = "" instr = [] if cfg.mtvec_mode == mtvec_mode_t.VECTORED: self.gen_interrupt_vector_table(hart, mode, status, cause, ie, ip, scratch, instr) else: # Push user mode GPR to kernel stack before executing exception handling, # this is to avoid exception handling routine modify user program state # unexpectedly # TODO pkg_ins.push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr) # Checking xStatus can be optional if ISS (like spike) has different implementation of # certain fields compared with the RTL processor. if cfg.check_xstatus: instr.append("csrr x{}, {} # {}".format( cfg.gpr[0].value, hex(status.value), status.name)) instr.append("csrr x{}, {} # {}\n".format( cfg.gpr[0].value, hex(cause.value), cause.name) + "{}srli x{}, x{}, {}\n".format( pkg_ins.indent, cfg.gpr[0].value, cfg.gpr[0].value, rcs.XLEN - 1) + "{}bne x{}, x0, {}{}mode_instr_handler".format( pkg_ins.indent, cfg.gpr[0].value, pkg_ins.hart_prefix(hart), mode)) # The trap handler will occupy one 4KB page, it will be allocated one entry in # the page table with a specific privileged mode. if rcs.SATP_MODE != satp_mode_t.BARE: self.instr_stream.append(".align 12") else: self.instr_stream.append(".align {}".format(cfg.tvec_alignment)) tvec_name = tvec.name self.gen_section( pkg_ins.get_label("{}_handler".format(tvec_name.lower()), hart), instr) # TODO Exception handler instr = [] if cfg.mtvec_mode == mtvec_mode_t.VECTORED: pkg_ins.push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr)
def enter_privileged_mode(self, mode, instrs): label = pkg_ins.format_string("{}init_{}:" .format(pkg_ins.hart_prefix(self.hart), mode), pkg_ins.LABEL_STR_LEN) ret_instr = ["mret"] regs = vsc.list_t(vsc.attr(riscv_privil_reg())) label = label.lower() self.setup_mmode_reg(mode, regs) if mode == "SUPERVISOR_MODE": self.setup_smode_reg(mode, regs) if mode == "USER_MODE": self.setup_umode_reg(mode, regs) if cfg.virtual_addr_translation_on: self.setup_satp(instrs) self.gen_csr_instr(regs, instrs) # Use mret/sret to switch to the target privileged mode instrs.append(ret_instr[0]) for i in range(len(instrs)): instrs[i] = pkg_ins.indent + instrs[i] instrs.insert(0, label)
def gen_kernel_stack_section(self, hart): hart_prefix_string = pkg_ins.hart_prefix(hart) if(cfg.use_push_data_section): self.instr_stream.append( ".pushsection .{}kernel_stack,\"aw\",@progbits;".format(hart_prefix_string)) else: self.instr_stream.append( ".section .{}kernel_stack,\"aw\",@progbits;".format(hart_prefix_string)) if(rcs.SATP_MODE != "BARE"): self.instr_stream.append(".align 12") else: self.instr_stream.append(".align 2") self.instr_stream.append(pkg_ins.get_label("kernel_stack_start:", hart)) self.instr_stream.append(".rept {}".format(cfg.kernel_stack_len - 1)) self.instr_stream.append(".{}byte 0x0".format(rcs.XLEN // 8)) self.instr_stream.append(".endr") self.instr_stream.append(pkg_ins.get_label("kernel_stack_end:", hart)) self.instr_stream.append(".{}byte 0x0".format(rcs.XLEN // 8)) if (cfg.use_push_data_section): self.instr_stream.push_back(".popsection;")
def gen_trap_handler_section(self, hart, mode, cause, tvec, tval, epc, scratch, status, ie, ip): # is_interrupt = 1 tvec_name = "" instr = [] if cfg.mtvec_mode == mtvec_mode_t.VECTORED: self.gen_interrupt_vector_table(hart, mode, status, cause, ie, ip, scratch, instr) else: # Push user mode GPR to kernel stack before executing exception handling, # this is to avoid exception handling routine modify user program state # unexpectedly # TODO pkg_ins.push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr) # Checking xStatus can be optional if ISS (like spike) has different implementation of # certain fields compared with the RTL processor. if cfg.check_xstatus: instr.append("csrr x{}, {} # {}".format( cfg.gpr[0], hex(status), status.name)) instr.append("csrr x{}, {} # {}\n".format(cfg.gpr[0], hex( cause), cause.name) + "{}srli x{}, x{}, {}\n".format( pkg_ins.indent, cfg.gpr[0], cfg.gpr[0], rcs.XLEN - 1) + "{}bne x{}, x0, {}{}mode_intr_handler".format( pkg_ins.indent, cfg.gpr[0], pkg_ins.hart_prefix(hart), mode)) # The trap handler will occupy one 4KB page, it will be allocated one entry in # the page table with a specific privileged mode. if rcs.SATP_MODE != satp_mode_t.BARE: self.instr_stream.append(".align 12") else: self.instr_stream.append(".align {}".format(cfg.tvec_alignment)) tvec_name = tvec.name self.gen_section( pkg_ins.get_label("{}_handler".format(tvec_name.lower()), hart), instr) # TODO Exception handlers instr = [] if cfg.mtvec_mode == mtvec_mode_t.VECTORED: pkg_ins.push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr) self.gen_signature_handshake(instr, signature_type_t.CORE_STATUS, core_status_t.HANDLING_EXCEPTION) # The trap is caused by an exception, read back xCAUSE, xEPC to see if these # CSR values are set properly. The checking is done by comparing against the log # generated by ISA simulator (spike). instr.extend(( "csrr x{}, 0x{} # {}".format(cfg.gpr[0], epc, epc.name), "csrr x{}, 0x{} # {}".format(cfg.gpr[0], cause, cause.name), # Illegal instruction exception "li x{}, {} # ILLEGAL_INSTRUCTION".format( cfg.gpr[1], hex(exception_cause_t.ILLEGAL_INSTRUCTION)), "beq x{}, x{}, {}illegal_instr_handler".format( cfg.gpr[0], cfg.gpr[1], pkg_ins.hart_prefix(hart)), # Skip checking tval for illegal instruction as it's implementation specific "csrr x{}, {} # {}".format(cfg.gpr[1], hex(tval), tval.name), # use JALR to jump to test_done. "1: la x{}, test_done".format(cfg.scratch_reg), "jalr x1, x{}, 0".format(cfg.scratch_reg))) self.gen_section( pkg_ins.get_label("{}mode_exception_handler".format(mode), hart), instr)
def gen_program(self): # Generate program header self.instr_stream.clear() self.gen_program_header() for hart in range(cfg.num_of_harts): # Commenting out for now # sub_program_name = [] self.instr_stream.append(f"h{int(hart)}_start:") if not cfg.bare_program_mode: self.setup_misa() # Create all page tables self.create_page_table(hart) # Setup privileged mode registers and enter target privileged mode self.pre_enter_privileged_mode(hart) # Init section self.gen_init_section(hart) # To DO ''' If PMP is supported, we want to generate the associated trap handlers and the test_done section at the start of the program so we can allow access through the pmpcfg0 CSR ''' if (rcs.support_pmp and not (cfg.bare_program_mode)): self.gen_trap_handlers(hart) # Ecall handler self.gen_ecall_handler(hart) # Instruction fault handler self.gen_instr_fault_handler(hart) # Load fault handler self.gen_load_fault_handler(hart) # Store fault handler self.gen_store_fault_handler(hart) if hart == 0: self.gen_test_done() # Generate main program gt_lbl_str = pkg_ins.get_label("main", hart) label_name = gt_lbl_str gt_lbl_str = riscv_instr_sequence() self.main_program.append(gt_lbl_str) self.main_program[hart].instr_cnt = cfg.main_program_instr_cnt self.main_program[hart].is_debug_program = 0 self.main_program[hart].label_name = label_name self.generate_directed_instr_stream( hart=hart, label=self.main_program[hart].label_name, original_instr_cnt=self.main_program[hart].instr_cnt, min_insert_cnt=1, instr_stream=self.main_program[hart].directed_instr) self.main_program[hart].gen_instr(is_main_program=1, no_branch=cfg.no_branch_jump) self.main_program[hart].post_process_instr() self.main_program[hart].generate_instr_stream() logging.info("Generating main program instruction stream...done") self.instr_stream.extend(self.main_program[hart].instr_string_list) """ If PMP is supported, need to jump from end of main program to test_done section at the end of main_program, as the test_done will have moved to the beginning of the program """ self.instr_stream.append("{}j test_done".format(pkg_ins.indent)) ''' Test done section If PMP isn't supported, generate this in the normal location ''' if (hart == 0 and not (rcs.support_pmp)): self.gen_test_done() logging.info("Main/sub program generation...done") # program end self.gen_program_end(hart) if not cfg.bare_program_mode: # Generate debug rom section if rcs.support_debug_mode: self.gen_debug_rom(hart) self.gen_section( pkg_ins.hart_prefix(hart) + "instr_end", ["nop"]) for hart in range(cfg.num_of_harts): # Starting point of data section self.gen_data_page_begin(hart) if not cfg.no_data_page: # User data section self.gen_data_page(hart) # AMO memory region if (hart == 0 and riscv_instr_group_t.RV32A in rcs.supported_isa): self.gen_data_page(hart, amo=1) self.gen_stack_section(hart) if not cfg.bare_program_mode: # Generate kernel program/data/stack section self.gen_kernel_sections(hart) # Page table self.gen_page_table_section(hart)