diff --git a/.github/workflows/check_target_on_pr.yml b/.github/workflows/check_target_on_pr.yml index bbfd3ebc6..b48f31f33 100644 --- a/.github/workflows/check_target_on_pr.yml +++ b/.github/workflows/check_target_on_pr.yml @@ -1,12 +1,11 @@ name: check_target -on: - pull_request: - types: [opened, reopened, edited] +on: [push, pull_request] + jobs: check_target: runs-on: ubuntu-latest steps: - - if: ${{ (github.event.pull_request.head.ref == 'dev' && github.event.pull_request.base.ref == 'master') || github.event.pull_request.base.ref == 'dev' }} + - if: ${{ (github.event.pull_request.head.ref == 'dev' && github.event.pull_request.base.ref == 'master') || github.event.pull_request.base.ref == 'dev' || github.event.push.ref != 'refs/heads/master'}} run: exit 0 - - if: ${{ github.event.pull_request.base.ref != 'dev' }} + - if: ${{ github.event.pull_request.base.ref != 'dev' || github.event.push.ref == 'refs/heads/master'}} run: exit 1 diff --git a/.gitignore b/.gitignore index ef351b194..f21fdf680 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,9 @@ TAGS /build /Bender.lock /Bender.local +golden.src +revised.src +cadence_conformal +golden_reference_design +synopsys_formality +questa_autocheck diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1d15fef3e..60aa591d2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,6 +7,22 @@ The [OpenHW Work Flow](https://github.com/openhwgroup/core-v-docs/blob/master/ve is required reading. You will find information about the implementation and usage of the CORE-V verification environments in the [Verification Strategy](https://github.com/openhwgroup/core-v-docs/blob/master/verif/Common/OpenHWGroup_CORE-V_Verif_Strategy.pdf). +## Updating Copyright +The files in this repository are open-source artifacts licensed under the terms of the Solderpad license, see [LICENSE](LICENSE). +If you modify a file, a new copyright _may_ be added, but the existing copyright and license header _must not_ be removed or modified. +If your contribution uses a newer version of the existing license, you are encouraged to declare that with a one-liner SPDX header. + +In the example below, a new copyright and updated license are added to an existing copyright and license: +``` +// Copyright 2024 OpenHW Group and +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. +// ...remainder of original license header from ETHZ and UniBo. +``` + ## The Mechanics 1. From GitHub: [fork](https://help.github.com/articles/fork-a-repo/) the [cv32e40p](https://github.com/openhwgroup/cv32e40p) repository 2. Clone repository: `git clone https://github.com/[your_github_username]/cv32e40p` diff --git a/bhv/cv32e40p_rvfi.sv b/bhv/cv32e40p_rvfi.sv index 97a9d0d1e..13a5beb00 100644 --- a/bhv/cv32e40p_rvfi.sv +++ b/bhv/cv32e40p_rvfi.sv @@ -1155,7 +1155,7 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; e_dev_commit_rf_to_ex_3, e_dev_commit_rf_to_ex_4, e_dev_commit_rf_to_ex_5; - event e_if_2_id_1, e_if_2_id_2, e_if_2_id_3; + event e_if_2_id_1, e_if_2_id_2, e_if_2_id_3, e_if_2_id_4; event e_ex_to_wb_1, e_ex_to_wb_2; event e_id_to_ex_1, e_id_to_ex_2; event e_commit_dpc; @@ -1272,7 +1272,6 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; bit s_id_done; function void if_to_id(); if (trace_id.m_valid) begin - minstret_to_id(); `CSR_FROM_PIPE(id, misa) `CSR_FROM_PIPE(id, tdata1) `CSR_FROM_PIPE(id, tdata2) @@ -1282,12 +1281,12 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; end trace_id.init(trace_if); trace_id.m_trap = ~r_pipe_freeze_trace.minstret; - trace_id.m_is_illegal = r_pipe_freeze_trace.is_illegal; + trace_id.m_is_illegal = trace_id.m_is_illegal | r_pipe_freeze_trace.is_illegal; + `CSR_FROM_PIPE(id, dpc) s_is_pc_set = 1'b0; s_is_irq_start = 1'b0; trace_if.m_valid = 1'b0; s_id_done = 1'b0; - `CSR_FROM_PIPE(id, dpc) endfunction function logic [31:0] be_to_mask(logic [3:0] be); @@ -1320,6 +1319,8 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; bit s_ex_reg_we_adjusted; //ex_reg_we bit s_rf_we_wb_adjusted; // + bit s_dont_override_mstatus_fs_id; + trace_if = new(); trace_id = new(); trace_ex = new(); @@ -1352,6 +1353,8 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; s_ex_reg_we_adjusted = 1'b0; s_rf_we_wb_adjusted = 1'b0; + s_dont_override_mstatus_fs_id = 1'b0; + forever begin wait(e_pipe_monitor_ok.triggered); // event triggered #1; @@ -1368,19 +1371,7 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; end if (r_pipe_freeze_trace.ctrl_fsm_cs == DBG_TAKEN_ID && r_pipe_freeze_trace.ebrk_insn_dec) begin - if (trace_wb.m_valid) begin - send_rvfi(trace_wb); - trace_wb.m_valid = 1'b0; - ->e_send_rvfi_trace_wb_1; - end - if (trace_ex.m_valid) begin - send_rvfi(trace_ex); - trace_ex.m_valid = 1'b0; - ->e_send_rvfi_trace_ex_1; - end if (trace_id.m_valid) begin - - minstret_to_id(); `CSR_FROM_PIPE(id, misa) `CSR_FROM_PIPE(id, tdata1) `CSR_FROM_PIPE(id, tdata2) @@ -1494,10 +1485,11 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; end if (trace_ex.m_valid) begin - - if (!trace_ex.m_csr.got_minstret) begin + if(trace_ex.m_instret_smaple_trigger == 1) begin //time to sample instret minstret_to_ex(); end + trace_ex.m_instret_smaple_trigger = trace_ex.m_instret_smaple_trigger + 1; + `CSR_FROM_PIPE(ex, misa) `CSR_FROM_PIPE(ex, tdata1) `CSR_FROM_PIPE(ex, tdata2) @@ -1524,9 +1516,6 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; trace_ex.m_valid = 1'b0; ->e_send_rvfi_trace_ex_2; end else begin - if (!s_ex_valid_adjusted & !trace_ex.m_csr.got_minstret) begin - minstret_to_ex(); - end if (s_rf_we_wb_adjusted) begin ->e_dev_commit_rf_to_ex_1; @@ -1541,12 +1530,15 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; trace_ex.m_got_first_data = 1'b1; end - if (r_pipe_freeze_trace.csr.fregs_we) begin + if (r_pipe_freeze_trace.csr.fregs_we && !r_pipe_freeze_trace.apu_rvalid) begin //Catching mstatus_fs updates caused by flw `CSR_FROM_PIPE(ex, mstatus_fs) trace_ex.m_csr.mstatus_fs_we = 1'b1; trace_ex.m_csr.mstatus_fs_wmask = '1; if(r_pipe_freeze_trace.csr.we && r_pipe_freeze_trace.csr.mstatus_fs_we) begin //In this specific case, two writes to mstatus_fs happen at the same time. We need to recreate the writes caused by fregs_we trace_ex.m_csr.mstatus_fs_wdata = FS_DIRTY; + end else begin + trace_id.m_csr.mstatus_fs_rdata = trace_ex.m_csr.mstatus_fs_wdata; + s_dont_override_mstatus_fs_id = 1'b1; end ->e_fregs_dirty_3; end @@ -1559,9 +1551,6 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; ->e_ex_to_wb_1; trace_wb.move_down_pipe(trace_ex); end else begin - if (!trace_ex.m_csr.got_minstret) begin - minstret_to_ex(); - end send_rvfi(trace_ex); ->e_send_rvfi_trace_ex_6; end @@ -1584,17 +1573,27 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; end // If mret, we need to keep the instruction in Id during flush_ex because mstatus update happens at that time - s_ex_valid_adjusted = (r_pipe_freeze_trace.ex_valid && r_pipe_freeze_trace.ex_ready) && (s_core_is_decoding || (r_pipe_freeze_trace.ctrl_fsm_cs == DBG_TAKEN_IF) || (r_pipe_freeze_trace.ctrl_fsm_cs == DBG_FLUSH) || ((r_pipe_freeze_trace.ctrl_fsm_cs == FLUSH_EX) && !r_pipe_freeze_trace.mret_insn_dec)) && (!r_pipe_freeze_trace.apu_rvalid || r_pipe_freeze_trace.data_req_ex); + s_ex_valid_adjusted = (r_pipe_freeze_trace.ex_valid && r_pipe_freeze_trace.ex_ready) && (s_core_is_decoding || (r_pipe_freeze_trace.ctrl_fsm_cs == DBG_TAKEN_IF) || (r_pipe_freeze_trace.ctrl_fsm_cs == DBG_TAKEN_ID) || (r_pipe_freeze_trace.ctrl_fsm_cs == DBG_FLUSH) || ((r_pipe_freeze_trace.ctrl_fsm_cs == FLUSH_EX) && !r_pipe_freeze_trace.mret_insn_dec)); //EX_STAGE + if (trace_id.m_valid) begin + if(trace_id.m_instret_smaple_trigger == 1) begin //time to sample instret + minstret_to_id(); + end + trace_id.m_instret_smaple_trigger = trace_id.m_instret_smaple_trigger + 1; if(trace_id.m_sample_csr_write_in_ex && !csr_is_irq && !s_is_irq_start) begin //First cycle after id_ready, csr write is asserted in this cycle `CSR_FROM_PIPE(id, mstatus) - `CSR_FROM_PIPE(id, mstatus_fs) + if(!s_dont_override_mstatus_fs_id) begin + `CSR_FROM_PIPE(id, mstatus_fs) + end `CSR_FROM_PIPE(id, mepc) `CSR_FROM_PIPE(id, mcause) `CSR_FROM_PIPE(id, dscratch0) `CSR_FROM_PIPE(id, dscratch1) + if(r_pipe_freeze_trace.csr.we && (r_pipe_freeze_trace.csr.addr == CSR_DPC)) begin + `CSR_FROM_PIPE(id, dpc) + end ->e_csr_in_ex; end @@ -1614,10 +1613,6 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; `CSR_FROM_PIPE(id, frm) `CSR_FROM_PIPE(id, fcsr) - if (r_pipe_freeze_trace.csr.we) begin - `CSR_FROM_PIPE(id, dpc) - end - if (r_pipe_freeze_trace.csr.dcsr_we) begin dcsr_to_id(); end @@ -1638,6 +1633,15 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; trace_ex.m_csr.frm_wmask = '0; trace_ex.m_csr.fcsr_wmask = '0; + if(r_pipe_freeze_trace.ctrl_fsm_cs == XRET_JUMP) begin //xret exit pipeline + tinfo_to_id(); + `CSR_FROM_PIPE(id, tdata1) + `CSR_FROM_PIPE(id, tdata2) + send_rvfi(trace_id); + trace_id.m_valid = 1'b0; + s_dont_override_mstatus_fs_id = 1'b0; + end + if (r_pipe_freeze_trace.apu_req && r_pipe_freeze_trace.apu_gnt) begin trace_id.m_is_apu = 1'b1; trace_id.m_apu_req_id = cnt_apu_req; @@ -1647,6 +1651,7 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; trace_apu_req.set_to_apu(); apu_trace_q.push_back(trace_apu_req); trace_id.m_valid = 1'b0; + s_dont_override_mstatus_fs_id = 1'b0; if(r_pipe_freeze_trace.apu_rvalid && (cnt_apu_req == cnt_apu_resp)) begin//APU return in the same cycle apu_resp(); @@ -1702,6 +1707,7 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; hwloop_to_id(); trace_ex.move_down_pipe(trace_id); // The instruction moves forward from ID to EX trace_id.m_valid = 1'b0; + s_dont_override_mstatus_fs_id = 1'b0; ->e_id_to_ex_1; end else if (r_pipe_freeze_trace.ex_reg_we && r_pipe_freeze_trace.rf_alu_we_ex) begin @@ -1725,9 +1731,6 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; if (s_new_valid_insn) begin // There is a new valid instruction if (trace_id.m_valid) begin if (trace_ex.m_valid) begin - if (!trace_ex.m_csr.got_minstret) begin - minstret_to_ex(); - end if (trace_wb.m_valid) begin send_rvfi(trace_ex); ->e_send_rvfi_trace_ex_4; @@ -1769,6 +1772,7 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; hwloop_to_id(); trace_ex.move_down_pipe(trace_id); trace_id.m_valid = 1'b0; + s_dont_override_mstatus_fs_id = 1'b0; ->e_id_to_ex_2; end if_to_id(); @@ -1786,18 +1790,24 @@ insn_trace_t trace_if, trace_id, trace_ex, trace_ex_next, trace_wb; end //IF_STAGE - if (r_pipe_freeze_trace.if_valid && r_pipe_freeze_trace.if_ready) begin + if(trace_if.m_valid) begin + if(r_pipe_freeze_trace.is_illegal && r_pipe_freeze_trace.is_decoding) begin + trace_if.m_is_illegal = 1'b1; + end + end + + if (r_pipe_freeze_trace.if_valid && r_pipe_freeze_trace.if_ready && r_pipe_freeze_trace.instr_valid_if) begin if (trace_if.m_valid) begin if (r_pipe_freeze_trace.id_valid && r_pipe_freeze_trace.id_ready && !trace_id.m_valid && r_pipe_freeze_trace.ebrk_insn_dec) begin if_to_id(); trace_id.m_is_ebreak = '1; //trace_if.m_is_ebreak; ->e_if_2_id_2; - end else if (r_pipe_freeze_trace.is_illegal) begin + end else if (trace_if.m_is_illegal) begin if_to_id(); - trace_id.m_is_illegal = 1'b1; ->e_if_2_id_3; end else if (r_pipe_freeze_trace.ecall_insn_dec) begin if_to_id(); + ->e_if_2_id_4; end end diff --git a/bhv/insn_trace.sv b/bhv/insn_trace.sv index 3fe7c1848..71cbaaff4 100644 --- a/bhv/insn_trace.sv +++ b/bhv/insn_trace.sv @@ -65,6 +65,7 @@ bit m_move_down_pipe; int m_instret_cnt; + int m_instret_smaple_trigger; //We need to sample minstret from csr 2 cycle after id is doen bit m_sample_csr_write_in_ex; @@ -173,6 +174,7 @@ this.m_frm_we_non_apu = 1'b0; this.m_fcsr_we_non_apu = 1'b0; this.m_instret_cnt = 0; + this.m_instret_smaple_trigger = 0; this.m_sample_csr_write_in_ex = 1'b1; endfunction @@ -896,6 +898,7 @@ this.m_got_regs_write = 1'b0; this.m_move_down_pipe = 1'b0; this.m_instret_cnt = 0; + this.m_instret_smaple_trigger = 0; this.m_sample_csr_write_in_ex = 1'b1; this.m_rd_addr[0] = '0; this.m_rd_addr[1] = '0; @@ -970,6 +973,7 @@ this.m_is_illegal = m_source.m_is_illegal; this.m_is_irq = m_source.m_is_irq; this.m_instret_cnt = m_source.m_instret_cnt; + this.m_instret_smaple_trigger = m_source.m_instret_smaple_trigger; this.m_sample_csr_write_in_ex = m_source.m_sample_csr_write_in_ex; this.m_rs1_addr = m_source.m_rs1_addr; this.m_rs2_addr = m_source.m_rs2_addr; diff --git a/cv32e40p_fpu_manifest.flist b/cv32e40p_fpu_manifest.flist index aca8c41b2..92ddce332 100644 --- a/cv32e40p_fpu_manifest.flist +++ b/cv32e40p_fpu_manifest.flist @@ -82,7 +82,6 @@ ${DESIGN_RTL_DIR}/vendor/pulp_platform_fpnew/src/fpnew_noncomp.sv ${DESIGN_RTL_DIR}/vendor/pulp_platform_fpnew/src/fpnew_opgroup_fmt_slice.sv ${DESIGN_RTL_DIR}/vendor/pulp_platform_fpnew/src/fpnew_opgroup_multifmt_slice.sv ${DESIGN_RTL_DIR}/vendor/pulp_platform_fpnew/src/fpnew_opgroup_block.sv -${DESIGN_RTL_DIR}/vendor/pulp_platform_fpnew/src/fpnew_divsqrt_multi.sv ${DESIGN_RTL_DIR}/vendor/pulp_platform_fpnew/src/fpnew_top.sv ${DESIGN_RTL_DIR}/cv32e40p_fp_wrapper.sv diff --git a/docs/source/corev_hw_loop.rst b/docs/source/corev_hw_loop.rst index 6d0cbf64d..e51b0fe8f 100644 --- a/docs/source/corev_hw_loop.rst +++ b/docs/source/corev_hw_loop.rst @@ -57,6 +57,8 @@ The HWLoop constraints are: - End Address must be strictly greater than Start Address. +- HWLoop #0 (resp. #1) start and end addresses **must not be modified** if HWLoop #0 (resp. #1) count is different than 0. + - End address of an HWLoop must point to the instruction just after the last one of the HWLoop body. - HWLoop body must contain at least 3 instructions. @@ -64,8 +66,6 @@ The HWLoop constraints are: - When both loops are nested, the End address of the outermost HWLoop (must be #1) must be at least 2 instructions further than the End address of the innermost HWLoop (must be #0), i.e. HWLoop[1].endaddress >= HWLoop[0].endaddress + 8. - Remark: To avoid to add 2 NOPs in case nothing can be put there by the compiler, lpcount setting of the the inner loop could be moved after it - without forgetting to add the same in the preamble before the outer loop start address. - HWLoop must always be entered from its start location (no branch/jump to a location inside a HWLoop body). @@ -103,37 +103,39 @@ Below an assembly code example of a nested HWLoop that computes a matrix additio asm volatile ( "add %[i],x0, x0;" "add %[j],x0, x0;" - "cv.count 1, %[N];" ".balign 4;" - "cv.endi 1, endO;" - "cv.starti 1, startO;" + "cv.starti 1, start1;" + "cv.endi 1, end1;" + "cv.count 1, %[N];" "any instructions here" ".balign 4;" - "cv.endi 0, endZ;" - "cv.starti 0, startZ;" - "cv.count 0, %[N];" + "cv.starti 0, start0;" + "cv.endi 0, end0;" "any instructions here" ".balign 4;" ".option norvc;" - "startO:;" - " startZ:;" + "start1:;" + " cv.count 0, %[N];" + " start0:;" " addi %[i], %[i], 1;" " addi %[i], %[i], 1;" " addi %[i], %[i], 1;" - " endZ:;" - " cv.count 0, %[N];" + " end0:;" + " addi %[j], %[j], 2;" " addi %[j], %[j], 2;" - "endO:;" + "end1:;" : [i] "+r" (i), [j] "+r" (j) : [N] "r" (10) ); +As HWLoop feature is enabled as soon as lpcountX > 0, lpstartX and lpendX **must** be programmed **before** lpcountX to avoid unexpected behavior. +For HWLoop where body contains up to 30 instructions, it is always better to use cv.setup* instructions which are updating all 3 HWLoop CSRs in the same cycle. At the beginning of the HWLoop, the registers %[i] and %[j] are 0. -The innermost loop, from startZ to (endZ - 4), adds to %[i] three times 1 and -it is executed 10x10 times. Whereas the outermost loop, from startO to (endO - 4), -executes 10 times the innermost loop and adds 2 to the register %[j]. -At the end of the loop, the register %[i] contains 300 and the register %[j] contains 20. +The innermost loop, from start0 to (end0 - 4), adds to %[i] three times 1 and +it is executed 10x10 times. Whereas the outermost loop, from start1 to (end1 - 4), +executes 10 times the innermost loop and adds two times 2 to the register %[j]. +At the end of the loop, the register %[i] contains 300 and the register %[j] contains 40. .. _hwloop-exceptions_handlers: @@ -148,13 +150,13 @@ Those handlers should manage MEPC and lpcountX CSRs updates because an hw loop e At the end of the handlers after restoring the context/CSRs, a piece of smart code should be added with following highest to lowest order of priority: -1. if MEPC = lpend0 - 4 and lpcount0 > 1 then MPEC should be set to lpstart0 and lpcount0 should be decremented by 1, -2. else if MEPC = lpend0 - 4 and lpcount0 = 1 then MPEC should be incremented by 4 and lpcount0 should be decremented by 1, -3. else if MEPC = lpend1 - 4 and lpcount1 > 1 then MPEC should be set to lpstart1 and lpcount1 should be decremented by 1, -4. else if MEPC = lpend1 - 4 and lpcount1 = 1 then MPEC should be incremented by 4 and lpcount1 should be decremented by 1, -5. else if (lpstart0 <= MEPC < lpend0 - 4) or (lpstart1 <= MEPC < lpend1 - 4) then MPEC should be incremented by 4, -6. else if instruction at MEPC location is either ecall or ebreak then MPEC should be incremented by 4, -7. else if instruction at MEPC location location is c.ebreak then MPEC should be incremented by 2. +1. if MEPC = lpend0 - 4 and lpcount0 > 1 then MEPC should be set to lpstart0 and lpcount0 should be decremented by 1, +2. else if MEPC = lpend0 - 4 and lpcount0 = 1 then MEPC should be incremented by 4 and lpcount0 should be decremented by 1, +3. else if MEPC = lpend1 - 4 and lpcount1 > 1 then MEPC should be set to lpstart1 and lpcount1 should be decremented by 1, +4. else if MEPC = lpend1 - 4 and lpcount1 = 1 then MEPC should be incremented by 4 and lpcount1 should be decremented by 1, +5. else if (lpstart0 <= MEPC < lpend0 - 4) or (lpstart1 <= MEPC < lpend1 - 4) then MEPC should be incremented by 4, +6. else if instruction at MEPC location is either ecall or ebreak then MEPC should be incremented by 4, +7. else if instruction at MEPC location location is c.ebreak then MEPC should be incremented by 2. The 2 last cases are the standard ones when ebreak/ecall are not inside an HWLopp. diff --git a/docs/source/instruction_set_extensions.rst b/docs/source/instruction_set_extensions.rst index c7becb7f3..de3dc3ea8 100644 --- a/docs/source/instruction_set_extensions.rst +++ b/docs/source/instruction_set_extensions.rst @@ -788,12 +788,16 @@ General ALU operations | | else if rs1 >=rs2, rD = rs2, | | | | | | else rD = rs1 | + | | | + | | Note: rs2 is unsigned. | +-------------------------------------------+------------------------------------------------------------------------+ | **cv.clipur rD, rs1, rs2** | if rs1 <= 0, rD = 0, | | | | | | else if rs1 >= rs2, rD = rs2, | | | | | | else rD = rs1 | + | | | + | | Note: rs2 is unsigned. | +-------------------------------------------+------------------------------------------------------------------------+ | **cv.addN rD, rs1, rs2, Is3** | rD = (rs1 + rs2) >>> Is3 | | | |