diff --git a/verif/env/corev-dv/cva6_asm_program_gen.sv b/verif/env/corev-dv/cva6_asm_program_gen.sv index 0fb37d34b9..d3a6f9a673 100644 --- a/verif/env/corev-dv/cva6_asm_program_gen.sv +++ b/verif/env/corev-dv/cva6_asm_program_gen.sv @@ -175,7 +175,7 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen; end else begin // Push user mode GPR to kernel stack before executing exception handling, this is to avoid // exception handling routine modify user program state unexpectedly - push_used_gpr_to_kernel_stack(status, scratch, 4, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr); + push_used_gpr_to_kernel_stack(status, scratch, 3, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr); // Checking xStatus can be optional if ISS (like spike) has different implementation of // certain fields compared with the RTL processor. if (cfg_cva6.check_xstatus) begin @@ -187,16 +187,19 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen; // Check if the exception is caused by an interrupt, if yes, jump to interrupt // handler Interrupt is indicated by xCause[XLEN-1] $sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], cause, cause.name()), + $sformatf("srli x%0d, x%0d, %0d", cfg_cva6.gpr[0], cfg_cva6.gpr[0], XLEN-1), + $sformatf("bne x%0d, x0, %0s%0s_intr_handler", + cfg_cva6.gpr[0], hart_prefix(hart), mode), $sformatf("csrr x%0d, mepc", cfg_cva6.gpr[0]), - $sformatf("lbu x%0d, 0(x%0d)", cfg_cva6.gpr[3],cfg_cva6.gpr[0]), + $sformatf("lbu x%0d, 0(x%0d)", cfg_cva6.gpr[2],cfg_cva6.gpr[0]), $sformatf("li x%0d, 0x3", cfg_cva6.gpr[1]), - $sformatf("and x%0d, x%0d, x%0d", cfg_cva6.gpr[3], cfg_cva6.gpr[3], cfg_cva6.gpr[1]), - $sformatf("bne x%0d, x%0d, exception_handler_incr_mepc2", cfg_cva6.gpr[3], cfg_cva6.gpr[1]), + $sformatf("and x%0d, x%0d, x%0d", cfg_cva6.gpr[2], cfg_cva6.gpr[2], cfg_cva6.gpr[1]), + $sformatf("bne x%0d, x%0d, exception_handler_incr_mepc2", cfg_cva6.gpr[2], cfg_cva6.gpr[1]), $sformatf("addi x%0d, x%0d, 2", cfg_cva6.gpr[0], cfg_cva6.gpr[0]), str, $sformatf("addi x%0d, x%0d, 2", cfg_cva6.gpr[0], cfg_cva6.gpr[0]), $sformatf("csrw mepc, x%0d", cfg_cva6.gpr[0])}; - pop_used_gpr_from_kernel_stack(MSTATUS, MSCRATCH, 4, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr); + pop_used_gpr_from_kernel_stack(MSTATUS, MSCRATCH, 3, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr); instr.push_back("mret"); end // The trap handler will occupy one 4KB page, it will be allocated one entry in the page table @@ -213,7 +216,49 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen; if (cfg_cva6.mtvec_mode == VECTORED) begin push_gpr_to_kernel_stack(status, scratch, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr); end - gen_signature_handshake(instr, CORE_STATUS, HANDLING_EXCEPTION); + //~ push_used_gpr_to_kernel_stack(status, scratch, 3, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr); + instr = {instr, + // The trap is caused by an exception, read back xCAUSE, xEPC to see if these + // CSR values are set properly. + $sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], epc, epc.name()), + $sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], cause, cause.name()), + $sformatf("li x%0d, 0x8000000b", cfg_cva6.gpr[1]), + $sformatf("li x%0d, 0x80000007", cfg_cva6.gpr[2]), + $sformatf("beq x%0d, x%0d, ext_interrupt_handler", cfg_cva6.gpr[0], cfg_cva6.gpr[1]), + $sformatf("beq x%0d, x%0d, timer_interrupt_handler", cfg_cva6.gpr[0], cfg_cva6.gpr[2]), + $sformatf("j test_done") + }; + gen_section(get_label($sformatf("%0s_intr_handler", mode), hart), instr); + + instr = {}; + instr = {instr, + // The trap is caused by an external interrupt, read back xIP + // Write into int_ack 0x1 value + $sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], epc, ip.name()), + $sformatf("li x%0d, 0", cfg_cva6.gpr[0]), + $sformatf("addi x%0d, x%0d, 1", cfg_cva6.gpr[0], cfg_cva6.gpr[0]), + // Clean external pending interrupt + $sformatf("sw x%0d, int_ack, x%0d # %0s;", + cfg_cva6.gpr[0], cfg_cva6.gpr[1], ip.name()) + }; + pop_used_gpr_from_kernel_stack(MSTATUS, MSCRATCH, 3, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr); + instr.push_back("mret"); + gen_section(get_label($sformatf("ext_interrupt_handler"), hart), instr); + + instr = {}; + instr = {instr, + // The trap is caused by a timer interrupt, read back xIP + // Write into int_ack 0x2 value + $sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], epc, ip.name()), + $sformatf("li x%0d, 0", cfg_cva6.gpr[0]), + $sformatf("addi x%0d, x%0d, 2", cfg_cva6.gpr[0], cfg_cva6.gpr[0]), + // Clean timer pending interrupt + $sformatf("sw x%0d, int_ack, x%0d", + cfg_cva6.gpr[0], cfg_cva6.gpr[1]) + }; + pop_used_gpr_from_kernel_stack(MSTATUS, MSCRATCH, 3, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr); + instr.push_back("mret"); + gen_section(get_label($sformatf("timer_interrupt_handler"), hart), instr); endfunction // Push used general purpose register to stack, this is needed before trap handling @@ -314,31 +359,31 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen; test_result_t test_result = TEST_FAIL, privileged_reg_t csr = MSCRATCH, string addr_label = ""); - if (cfg.require_signature_addr) begin + if (cfg_cva6.require_signature_addr) begin string str[$]; - str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[1], cfg.signature_addr)}; + str = {$sformatf("li x%0d, 0x%0h", cfg_cva6.gpr[1], cfg_cva6.signature_addr)}; instr = {instr, str}; case (signature_type) // A single data word is written to the signature address. // Bits [7:0] contain the signature_type of CORE_STATUS, and the upper // XLEN-8 bits contain the core_status_t data. CORE_STATUS: begin - str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[0], core_status), - $sformatf("slli x%0d, x%0d, 8", cfg.gpr[0], cfg.gpr[0]), - $sformatf("addi x%0d, x%0d, 0x%0h", cfg.gpr[0], - cfg.gpr[0], signature_type), - $sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1])}; + str = {$sformatf("li x%0d, 0x%0h", cfg_cva6.gpr[0], core_status), + $sformatf("slli x%0d, x%0d, 8", cfg_cva6.gpr[0], cfg_cva6.gpr[0]), + $sformatf("addi x%0d, x%0d, 0x%0h", cfg_cva6.gpr[0], + cfg_cva6.gpr[0], signature_type), + $sformatf("sw x%0d, 0(x%0d)", cfg_cva6.gpr[0], cfg_cva6.gpr[1])}; instr = {instr, str}; end // A single data word is written to the signature address. // Bits [7:0] contain the signature_type of TEST_RESULT, and the upper // XLEN-8 bits contain the test_result_t data. TEST_RESULT: begin - str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[0], test_result), - $sformatf("slli x%0d, x%0d, 8", cfg.gpr[0], cfg.gpr[0]), - $sformatf("addi x%0d, x%0d, 0x%0h", cfg.gpr[0], - cfg.gpr[0], signature_type), - $sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1])}; + str = {$sformatf("li x%0d, 0x%0h", cfg_cva6.gpr[0], test_result), + $sformatf("slli x%0d, x%0d, 8", cfg_cva6.gpr[0], cfg_cva6.gpr[0]), + $sformatf("addi x%0d, x%0d, 0x%0h", cfg_cva6.gpr[0], + cfg_cva6.gpr[0], signature_type), + $sformatf("sw x%0d, 0(x%0d)", cfg_cva6.gpr[0], cfg_cva6.gpr[1])}; instr = {instr, str}; end // The first write to the signature address contains just the @@ -347,11 +392,11 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen; // each writing the data contained in one GPR, starting from x0 as the // first write, and ending with x31 as the 32nd write. WRITE_GPR: begin - str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[0], signature_type), - $sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1])}; + str = {$sformatf("li x%0d, 0x%0h", cfg_cva6.gpr[0], signature_type), + $sformatf("sw x%0d, 0(x%0d)", cfg_cva6.gpr[0], cfg_cva6.gpr[1])}; instr = {instr, str}; for(int i = 0; i < 32; i++) begin - str = {$sformatf("sw x%0x, 0(x%0d)", i, cfg.gpr[1])}; + str = {$sformatf("sw x%0x, 0(x%0d)", i, cfg_cva6.gpr[1])}; instr = {instr, str}; end end @@ -364,13 +409,13 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen; if (!(csr inside {implemented_csr})) begin return; end - str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[0], csr), - $sformatf("slli x%0d, x%0d, 8", cfg.gpr[0], cfg.gpr[0]), - $sformatf("addi x%0d, x%0d, 0x%0h", cfg.gpr[0], - cfg.gpr[0], signature_type), - $sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1]), - $sformatf("csrr x%0d, 0x%0h", cfg.gpr[0], csr), - $sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1])}; + str = {$sformatf("li x%0d, 0x%0h", cfg_cva6.gpr[0], csr), + $sformatf("slli x%0d, x%0d, 8", cfg_cva6.gpr[0], cfg_cva6.gpr[0]), + $sformatf("addi x%0d, x%0d, 0x%0h", cfg_cva6.gpr[0], + cfg_cva6.gpr[0], signature_type), + $sformatf("sw x%0d, 0(x%0d)", cfg_cva6.gpr[0], cfg_cva6.gpr[1]), + $sformatf("csrr x%0d, 0x%0h", cfg_cva6.gpr[0], csr), + $sformatf("sw x%0d, 0(x%0d)", cfg_cva6.gpr[0], cfg_cva6.gpr[1])}; instr = {instr, str}; end default: begin @@ -385,9 +430,19 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen; instr_stream.push_back(str); instr_stream.push_back({indent, "li gp, 1"}); instr_stream.push_back({indent, "sw gp, tohost, t5"}); - instr_stream.push_back({indent, "wfi"}); + instr_stream.push_back({indent, "end_of_test: j end_of_test"}); endfunction + + virtual function void gen_data_page_begin(int hart); + instr_stream.push_back(".section .data"); + if (hart == 0) begin + instr_stream.push_back(".align 6; .global tohost; tohost: .dword 0;"); + instr_stream.push_back(".align 6; .global fromhost; fromhost: .dword 0;"); + instr_stream.push_back(".align 6; .global int_ack; int_ack: .dword 0;"); + end + endfunction + endclass : cva6_asm_program_gen_c `endif // __CVA6_ASM_PROGRAM_GEN_SV__ diff --git a/verif/env/uvme/cov/uvme_interrupt_covg.sv b/verif/env/uvme/cov/uvme_interrupt_covg.sv index 70e777a5eb..e14097de6d 100644 --- a/verif/env/uvme/cov/uvme_interrupt_covg.sv +++ b/verif/env/uvme/cov/uvme_interrupt_covg.sv @@ -19,7 +19,8 @@ // Original Author: Ayoub JALALI (ayoub.jalali@external.thalesgroup.com) covergroup cg_interrupt( - string name + string name, + bit sw_int_supported ) with function sample ( uvma_isacov_instr_c instr ); @@ -29,6 +30,7 @@ covergroup cg_interrupt( cp_interrupt: coverpoint instr.rvfi.name_csrs["mcause"].wdata { bins NO_INTERRUPT = {0} iff (!instr.trap); + ignore_bins IGN_SOFTWARE_INTERRUPT = {32'h80000003} iff (!sw_int_supported); bins MACHINE_MODE_EXTERNAL_INTERRUPT = {32'h8000000b} iff (instr.trap); bins MACHINE_MODE_SOFTWARE_INTERRUPT = {32'h80000003} iff (instr.trap); bins MACHINE_MODE_TIMER_INTERRUPT = {32'h80000007} iff (instr.trap); @@ -40,6 +42,7 @@ covergroup cg_interrupt( } cp_msie: coverpoint instr.rvfi.name_csrs["mie"].wdata[3] { + ignore_bins IGN_MSIE = {1'h1} iff (!sw_int_supported); bins MSIE = {1'h1}; } @@ -52,6 +55,7 @@ covergroup cg_interrupt( } cp_msip: coverpoint instr.rvfi.name_csrs["mip"].wdata[3] { + ignore_bins IGN_MSIP = {1'h1} iff (!sw_int_supported); bins MSIP = {1'h1}; } @@ -106,8 +110,8 @@ function void uvme_interrupt_covg_c::build_phase(uvm_phase phase); end if (!cfg.disable_all_csr_checks) - interrupt_cg = new("interrupt_cg"); - else + interrupt_cg = new("interrupt_cg", + .sw_int_supported(cfg.sw_int_supported)); else `uvm_warning(get_type_name(), "Interrupt coverage will not be scored since config disable_all_csr_checks is true") mon_trn_fifo = new("mon_trn_fifo" , this); @@ -127,4 +131,3 @@ task uvme_interrupt_covg_c::run_phase(uvm_phase phase); end endtask : run_phase - diff --git a/verif/env/uvme/uvma_interrupt/cov/uvma_interrupt_cov_model.sv b/verif/env/uvme/uvma_interrupt/cov/uvma_interrupt_cov_model.sv index b4c152223b..d9dfabe362 100644 --- a/verif/env/uvme/uvma_interrupt/cov/uvma_interrupt_cov_model.sv +++ b/verif/env/uvme/uvma_interrupt/cov/uvma_interrupt_cov_model.sv @@ -12,14 +12,17 @@ `define __UVMA_INTERRUPT_COV_MODEL_SV__ covergroup cg_interrupt( - string name + string name, + int unsigned num_irq_supported ) with function sample(uvma_interrupt_seq_item_c req_item); option.per_instance = 1; option.name = name; - cp_interrupt_req : coverpoint req_item.interrupt_valid; + cp_interrupt_req : coverpoint req_item.interrupt_vector { + bins INTERRUPTS[] = {[0:$]} with (item inside {[0:(2**(num_irq_supported))-1]}); + } endgroup: cg_interrupt @@ -77,15 +80,16 @@ function void uvma_interrupt_cov_model_c::build_phase(uvm_phase phase); void'(uvm_config_db#(uvma_interrupt_cfg_c)::get(this, "", "cfg", cfg)); if (cfg == null) begin - `uvm_fatal("CFG", "Configuration handle is null") + `uvm_fatal(get_type_name(), "Configuration handle is null") end void'(uvm_config_db#(uvma_interrupt_cntxt_c)::get(this, "", "cntxt", cntxt)); if (cntxt == null) begin - `uvm_fatal("CNTXT", "Context handle is null") + `uvm_fatal(get_type_name(), "Context handle is null") end - interrupt_cg = new("interrupt_cg"); + interrupt_cg = new("interrupt_cg", + .num_irq_supported(cfg.num_irq_supported)); seq_item_fifo = new("seq_item_fifo", this); diff --git a/verif/env/uvme/uvma_interrupt/seq/uvma_interrupt_seq.sv b/verif/env/uvme/uvma_interrupt/seq/uvma_interrupt_seq.sv index 93099dacc6..29264bdecf 100644 --- a/verif/env/uvme/uvma_interrupt/seq/uvma_interrupt_seq.sv +++ b/verif/env/uvme/uvma_interrupt/seq/uvma_interrupt_seq.sv @@ -11,25 +11,47 @@ `ifndef __UVMA_INTERRUPT_SEQ_SV__ `define __UVMA_INTERRUPT_SEQ_SV__ - -/** - * Abstract object from which all other Interrupt agent sequences must extend. - * Subclasses must be run on Interrupt sequencer (uvma_interrupt_sqr_c) instance. - */ class uvma_interrupt_seq_c extends uvma_interrupt_base_seq_c; `uvm_object_utils(uvma_interrupt_seq_c) `uvm_declare_p_sequencer(uvma_interrupt_sqr_c) + bit [XLEN-1:0] IRQ_ACK_VALUE = 'h0; + int unsigned IRQ_TIMEOUT; + /** * Default constructor. */ extern function new(string name="uvma_interrupt_seq"); + extern virtual task automatic clear_irq_channel(int channel, uvma_interrupt_seq_item_c req_item); + extern virtual task body(); endclass : uvma_interrupt_seq_c +task automatic uvma_interrupt_seq_c::clear_irq_channel(int channel, uvma_interrupt_seq_item_c req_item); + + IRQ_TIMEOUT = cfg.irq_timeout; + while(1) begin + IRQ_ACK_VALUE = cntxt.mem.read(cfg.irq_addr); + if (IRQ_ACK_VALUE[channel]) begin + req_item.interrupt_vector[channel] = 1'h0; + `uvm_info(get_type_name(), $sformatf("Clear interrupt channel N-%2d -> mem = 0x%x",channel, IRQ_ACK_VALUE), UVM_NONE); + IRQ_TIMEOUT = cfg.irq_timeout; + break; + end + else begin + if (IRQ_TIMEOUT == 0) begin + `uvm_fatal(get_type_name(), $sformatf("Timeout : failed to write into irq_add to clear pending interrupts")); + end + IRQ_TIMEOUT = IRQ_TIMEOUT - 1; + end + @(posedge cntxt.interrupt_vif.clk); + end + +endtask : clear_irq_channel + function uvma_interrupt_seq_c::new(string name="uvma_interrupt_seq"); super.new(name); @@ -38,22 +60,47 @@ endfunction : new task uvma_interrupt_seq_c::body(); - forever begin - req_item = uvma_interrupt_seq_item_c::type_id::create("req_item"); - - start_item(req_item); - assert(req_item.randomize() with { - if(!cfg.enable_interrupt){ - req_item.interrupt_valid == 'h0; - } - else { - req_item.irq_cntrl != UVMA_INTERRUPT_RANDOMIZE; - } - }) - cfg.calc_random_req_latency(); - - finish_item(req_item); - end + if (cfg.enable_interrupt) begin + for (int i = 0; i < cfg.num_irq_supported; i++) begin + automatic int ii = i; + automatic uvma_interrupt_seq_item_c req_item_c; + fork begin + forever begin + // Set interrupt request per channel + req_item_c = uvma_interrupt_seq_item_c::type_id::create("req_item_c"); + start_item(req_item_c); + if (!req_item_c.randomize() with { + req_item_c.interrupt_vector[ii] inside {0 , 1}; + req_item_c.interrupt_channel_mask == 1< MEPC hasn't the next instruction's PC 2")) + `uvm_info(get_type_name(), $sformatf("BE CAREFUL -> MEPC hasn't the next instruction's PC 2"), UVM_DEBUG) end end else begin if (mepc_value != trap_pc + 'h4) begin - `uvm_warning(get_type_name(), $sformatf("BE CAREFUL -> MEPC hasn't the next instruction's PC 4")) + `uvm_info(get_type_name(), $sformatf("BE CAREFUL -> MEPC hasn't the next instruction's PC 4"), UVM_DEBUG) end end end else begin - `uvm_warning(get_type_name(), $sformatf("BE CAREFUL -> MEPC still has the trap pc, this could create an infinite loop ")) + `uvm_info(get_type_name(), $sformatf("BE CAREFUL -> MEPC still has the trap pc, this could create an infinite loop if the trap has been raised by an exception"), UVM_DEBUG) end end end diff --git a/verif/tb/uvmt/cva6_tb_wrapper.sv b/verif/tb/uvmt/cva6_tb_wrapper.sv index 588201ec2c..9b010f51de 100644 --- a/verif/tb/uvmt/cva6_tb_wrapper.sv +++ b/verif/tb/uvmt/cva6_tb_wrapper.sv @@ -72,7 +72,7 @@ module cva6_tb_wrapper import uvmt_cva6_pkg::*; #( output logic [31:0] tb_exit_o, output rvfi_instr_t [CVA6Cfg.NrCommitPorts-1:0] rvfi_o, output rvfi_csr_t rvfi_csr_o, - input logic [2:0] irq_i, + input logic [15:0] irq_i, uvma_debug_if debug_if, uvma_axi_intf axi_slave, uvmt_axi_switch_intf axi_switch_vif, @@ -105,8 +105,8 @@ module cva6_tb_wrapper import uvmt_cva6_pkg::*; #( .boot_addr_i ( boot_addr_i ),//Driving the boot_addr value from the core control agent .hart_id_i ( default_inputs_vif.hart_id ), .irq_i ( {1'b0, irq_i[0]} ), - .ipi_i ( irq_i[1] ), - .time_irq_i ( irq_i[2] ), + .ipi_i ( 1'b0 ), + .time_irq_i ( irq_i[1] ), .debug_req_i ( debug_if.debug_req ), .rvfi_probes_o ( rvfi_probes ), .cvxif_req_o ( cvxif_req ), diff --git a/verif/tb/uvmt/uvmt_cva6_tb.sv b/verif/tb/uvmt/uvmt_cva6_tb.sv index 79c2de73d7..5955f53697 100644 --- a/verif/tb/uvmt/uvmt_cva6_tb.sv +++ b/verif/tb/uvmt/uvmt_cva6_tb.sv @@ -65,9 +65,10 @@ module uvmt_cva6_tb; .rst_n(clknrst_if.reset_n) ); - uvma_interrupt_if - interrupt_vif( - ); + uvma_interrupt_if interrupt_vif( + .clk(clknrst_if.clk), + .reset_n(clknrst_if.reset_n) + ); uvmt_axi_switch_intf axi_switch_vif(); uvme_cva6_core_cntrl_if core_cntrl_if(); diff --git a/verif/tests/testlist_interrupt.yaml b/verif/tests/testlist_interrupt.yaml index a3cd8ab8b3..00255d8785 100644 --- a/verif/tests/testlist_interrupt.yaml +++ b/verif/tests/testlist_interrupt.yaml @@ -28,5 +28,5 @@ - test: jump_to_zero iterations: 1 path_var: TESTS_PATH - gcc_opts: "-static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles ../tests/custom/common/syscalls.c ../tests/custom/common/crt.S -I../tests/custom/env -I../tests/custom/common -T ../tests/custom/common/test.ld -lgcc" + gcc_opts: "-static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles ../tests/custom/common/syscalls.c ../tests/custom/common/crt.S -I../tests/custom/env -I../tests/custom/common -lgcc" asm_tests: /custom/interrupt/jump_to_zero.S