V 확장: 벡터 연산
배열 원소 수천 개에 같은 계산을 하는 일은 그래픽, 신호 처리, 머신러닝, 심지어 memcpy와 strlen에도 흔하다. 한 명령으로 여러 원소를 처리하는 방법으로 x86과 Arm은 128·256·512비트처럼 폭이 정해진 SIMD를 세대마다 새로 추가했다. RISC-V V 확장은 레지스터 길이를 구현에 맡기고, 프로그램은 "몇 개 남았는지"만 하드웨어에 알려 주는 벡터 길이 무관(vector-length agnostic) 방식을 택했다. 같은 바이너리가 128비트 칩에서도 1024비트 칩에서도 돌아간다. 이 장에서는 그 비밀인 VLEN·SEW·LMUL·vl과 vsetvli를 그림과 시뮬레이터로 해부한다.
- 고정 폭 SIMD와 벡터 길이 무관(VLA) 프로그래밍의 차이를 설명한다.
- v0–v31, VLEN, ELEN과 일곱 개의 벡터 CSR(주소 포함)을 안다.
- vtype의 vsew·vlmul·vta·vma·vill 필드와 VLMAX = LMUL × VLEN / SEW를 계산한다.
vsetvli가 AVL로부터 vl을 정하는 규칙과 인코딩을 이해한다.- 스트립 마이닝 루프가 서로 다른 VLEN에서 어떻게 동작하는지 눈으로 확인한다.
- 마스크(v0)와 꼬리·비활성 원소의 undisturbed/agnostic 정책을 구별한다.
- 단위·상수 보폭·인덱스·세그먼트·fault-only-first 메모리 접근과 넓히기·리덕션·순열 연산을 익힌다.
- Zve*·Zvl* 부분 확장과 벡터 암호 확장의 위치를 안다.
왜 벡터인가: 고정 폭 SIMD와 길이 무관 벡터
스칼라 코드로 z[i] = x[i] + y[i]를 n번 계산하면 매 원소마다 로드 두 번, 덧셈 한 번, 저장 한 번, 포인터 증가와 분기가 따라온다. 계산 자체보다 명령을 가져오고 해독하는 비용이 더 크다. 한 명령에 여러 원소를 실으면 이 비용이 원소 수만큼 나뉜다. 여기까지는 SIMD와 벡터가 같다. 차이는 몇 개를 싣느냐를 누가 정하느냐에 있다.
RISC-V V 확장은 1970년대 Cray-1에서 시작한 벡터 머신의 전통을 잇는다. 레지스터 하나의 길이 VLEN은 구현이 정하는 상수이고, 프로그램은 처리할 원소 수(응용 벡터 길이(AVL, application vector length))를 vsetvli로 건네 하드웨어가 이번 반복에 처리할 개수 vl을 돌려받는다. 이렇게 짠 코드를 벡터 길이 무관(VLA, vector-length agnostic) 코드라 한다.
| 고정 폭 SIMD (예: SSE/AVX) | RISC-V V | |
|---|---|---|
| 레지스터 폭 | 명령 집합마다 고정 (128/256/512) | 구현마다 다른 VLEN (2의 거듭제곱, 최대 216비트) |
| 원소 폭 | 명령마다 따로 (addps, addpd …) | vtype의 SEW가 정한다. 명령은 하나 (vadd.vv) |
| 남은 원소 처리 | 별도의 스칼라 꼬리 루프 | 마지막 반복에서 vl이 줄어들 뿐, 같은 루프 |
| 하드웨어가 넓어지면 | 새 명령으로 다시 컴파일 | 같은 바이너리가 그대로 빨라진다 |
| 명령 인코딩 | 폭·형식 정보가 opcode에 들어간다 | 32비트 명령 안에 다 못 넣는 정보는 vtype CSR로 뺐다 |
RISC-V 벡터 확장은 여러 해에 걸쳐 여러 초안(v0.7, v0.8, v0.9 …)을 거친 끝에 2021년 v1.0으로 비준되었다. 초안 v0.7.1을 구현한 칩이 먼저 시장에 나오기도 해서, "RVV 0.7.1"과 "RVV 1.0"은 서로 호환되지 않는다는 점을 기억해 두면 좋다. 이 장은 비준된 v1.0만 다룬다.
벡터 레지스터와 CSR
V 확장은 정수·부동소수점 레지스터와 별도로 32개의 벡터 레지스터 v0–v31을 더한다. 각 레지스터는 VLEN비트다. 규격은 구현마다 두 상수를 정하게 한다.
- ELEN — 어떤 연산이든 만들거나 쓸 수 있는 원소의 최대 폭. 8 이상, 2의 거듭제곱. V 확장은 64다.
- VLEN — 벡터 레지스터 하나의 비트 수. ELEN 이상, 2의 거듭제곱, 216 이하. V 확장은 최소 128이다.
vset{i}vl{i} 명령으로 바뀌고(vl은 fault-only-first 로드로도 줄어든다), vlenb는 설계 시점 상수다.| CSR | 주소 | 권한 | 설명 |
|---|---|---|---|
vstart | 0x008 | URW | 벡터 명령이 시작할 원소 번호. 트랩이 난 원소를 기록해 재개에 쓴다. 모든 벡터 명령은 끝나면 0으로 되돌린다. |
vxsat | 0x009 | URW | 고정소수점 연산이 포화(saturate)되었는지 표시하는 1비트 플래그. |
vxrm | 0x00A | URW | 고정소수점 반올림 모드 2비트(rnu, rne, rdn, rod). |
vcsr | 0x00F | URW | vxrm(비트 2:1)과 vxsat(비트 0)을 한꺼번에 읽고 쓰는 창. |
vl | 0xC20 | URO | 이번 명령이 갱신할 원소 수. |
vtype | 0xC21 | URO | 원소 폭(SEW), 레지스터 묶음(LMUL), 꼬리·마스크 정책, vill. |
vlenb | 0xC22 | URO | VLEN/8. 레지스터 하나의 바이트 수. 설계 시점 상수다. |
문맥 교환 코드는 벡터 레지스터를 저장할 스택 공간을 계산해야 한다. vlenb가 없으면 vsetvli로 VLMAX를 알아내야 하는데, 그러면 저장하려던 vl과 vtype이 망가진다. 그래서 규격은 VLEN/8을 그냥 읽을 수 있는 CSR을 두었다. 운영체제가 쓰는 mstatus.VS 필드(Off/Initial/Clean/Dirty)는 부동소수점의 FS와 같은 방식으로, 벡터 상태가 더러워졌을 때만 저장하게 해 문맥 교환 비용을 줄인다(17장).
SEW·LMUL·vtype: 레지스터를 어떻게 나누고 묶을까
벡터 명령의 32비트 인코딩에는 원소 폭이나 레지스터 묶음 정보를 넣을 자리가 없다. 그래서 RISC-V는 이 정보를 vtype CSR로 빼냈다. vadd.vv는 하나뿐이지만, 직전에 vsetvli로 정한 vtype에 따라 8비트 덧셈이 되기도 하고 64비트 덧셈이 되기도 한다.
| vsew[2:0] | SEW | 어셈블러 |
|---|---|---|
000 | 8 | e8 |
001 | 16 | e16 |
010 | 32 | e32 |
011 | 64 | e64 |
1xx | 예약 |
| vlmul[2:0] | LMUL | VLMAX |
|---|---|---|
100 | 예약 | — |
101 | mf8 1/8 | VLEN/SEW/8 |
110 | mf4 1/4 | VLEN/SEW/4 |
111 | mf2 1/2 | VLEN/SEW/2 |
000 | m1 1 | VLEN/SEW |
001 | m2 2 | 2·VLEN/SEW |
010 | m4 4 | 4·VLEN/SEW |
011 | m8 8 | 8·VLEN/SEW |
vlmul은 부호 있는 3비트 값으로, LMUL = 2vlmul이다. 그래서 111(−1)이 1/2, 110(−2)이 1/4, 101(−3)이 1/8이 된다. 여기서 이 장의 핵심 공식이 나온다.
LMUL: 레지스터 여러 개를 하나처럼
LMUL(length multiplier)이 1보다 크면 연속된 레지스터 여러 개가 레지스터 그룹(vector register group) 하나가 된다. 명령에는 그룹의 첫 레지스터 번호만 적고, 원소는 그 레지스터부터 차례로 채워진 뒤 다음 번호로 넘어간다. 그룹 시작 번호는 LMUL의 배수여야 한다. 아니면 그 인코딩은 예약(reserved)이다.
LMUL을 키우는 첫째 이유는 폭이 다른 데이터를 같은 원소 수로 맞추기 위해서다. 16비트 값을 32비트로 넓히면 같은 원소 수를 담는 데 레지스터가 두 배 필요하다. 그래서 규격은 루프 안에서 SEW/LMUL 비율을 일정하게 유지하라고 권한다. 비율이 같으면 VLMAX도 같아서, 한 번 정한 vl을 모든 폭에 그대로 쓸 수 있다. 분수 LMUL은 반대로 좁은 데이터가 레지스터 하나를 통째로 차지하지 않게 해, 32개 레지스터 이름을 더 많이 쓰게 해 준다. ELEN=64인 구현은 1/2, 1/4, 1/8을 반드시 지원해야 한다.
| SEW/LMUL → | 1 | 2 | 4 | 8 | 16 | 32 | 64 |
|---|---|---|---|---|---|---|---|
| SEW=8 | 8 | 4 | 2 | 1 | 1/2 | 1/4 | 1/8 |
| SEW=16 | 8 | 4 | 2 | 1 | 1/2 | 1/4 | |
| SEW=32 | 8 | 4 | 2 | 1 | 1/2 | ||
| SEW=64 | 8 | 4 | 2 | 1 |
위 표의 한 열에 있는 설정들은 VLMAX가 모두 같다(규격 표를 그대로 옮겼다). 예를 들어 SEW/LMUL=16 열에서는 e8·mf2, e16·m1, e32·m2, e64·m4가 같은 원소 수를 다룬다.
VLEN · SEW · LMUL 탐색기
vsetvli: 하드웨어와 길이를 협상하기
설정 명령은 세 가지다. 셋 다 새 vtype을 쓰고, AVL로부터 vl을 정해 vl CSR과 rd에 함께 쓴다. 모두 OP-V 주 opcode(1010111)의 funct3=111(OPCFG)을 쓴다.
AVL은 어디서 오나
| rd | rs1 | AVL | 효과 |
|---|---|---|---|
| 아무거나 | x0 아님 | x[rs1]의 값 | 보통의 스트립 마이닝 |
| x0 아님 | x0 | ~0 (가장 큰 부호 없는 수) | vl = VLMAX로 설정하고 rd에도 쓴다 |
| x0 | x0 | 현재 vl 값 | vl은 그대로 두고 vtype만 바꾼다. 새 SEW/LMUL 비율 때문에 VLMAX가 바뀌면 예약 |
vl을 정하는 규칙
AVL이 VLMAX 이하면 vl = AVL이다. AVL이 VLMAX의 두 배 이상이면 vl = VLMAX다. 그 사이, 즉 VLMAX < AVL < 2·VLMAX인 경우에만 구현에 재량이 있다. vl은 ceil(AVL/2) 이상 VLMAX 이하 중 아무 값이나 될 수 있다. 같은 구현에서 같은 입력이면 늘 같은 값이어야 한다.
"vl = AVL if AVL ≤ VLMAX; ceil(AVL / 2) ≤ vl ≤ VLMAX if AVL < (2 * VLMAX); vl = VLMAX if AVL ≥ (2 * VLMAX)" … "this permits an implementation to set vl = ceil(AVL / 2) for VLMAX < AVL < 2*VLMAX in order to evenly distribute work over the last two iterations of a strip-mine loop."
Volume I, "V" Extension — Constraints on Setting vl — 마지막 두 반복에 일을 고르게 나누는 구현을 허용하려는 규칙이다. 소프트웨어는 vl이 VLMAX라고 가정하지 말고 반드시 돌려받은 값을 써야 한다.
이 규칙에서 유용한 성질이 따라 나온다. AVL=0이면 vl=0, AVL>0이면 vl>0, 그리고 vl은 VLMAX와 AVL을 넘지 않는다. 또 첫 반복의 vl이 모든 반복 중 가장 크다는 것이 보장되므로, 리덕션 루프가 vl의 최댓값을 따로 기억할 필요가 없다.
vsetvli 인코딩 만들기
vsetvli t0, a0, e32, m4, ta, ma
vsetvli t0, a0, e32, m1, ta, ma(LMUL을 m1로)가 0x0d0572d7이 되는지 확인해 보자. rd와 rs1을 모두 zero로 두면 어떤 뜻이 되는지, vsetivli에서 uimm이 비트 19:15에 들어가는 모습도 살펴보자.스트립 마이닝: 길이 무관 루프
n개짜리 배열을 VLMAX개씩 잘라 처리하는 기법을 스트립 마이닝(strip mining)이라 한다. RISC-V에서는 vsetvli가 자르는 일을 맡는다. 아래는 규격 부록의 32비트 정수 벡터 덧셈 예제다. 반복 횟수도, 마지막 꼬리 처리도 코드에 드러나지 않는다. t0에 돌아온 vl만큼 포인터를 옮기고 남은 수 a0를 줄일 뿐이다.
t0에 돌아온 값만 쓰므로 어느 쪽이든 결과가 같다.스트립 마이닝 루프 돌려 보기
마스크와 꼬리 정책
if가 들어 있는 루프를 벡터로 바꾸려면 원소마다 연산을 할지 말지 고를 수 있어야 한다. V 확장은 이를 마스크(mask)로 한다. 마스크는 항상 v0에서 오고, 원소 i의 마스크 비트는 SEW·LMUL과 상관없이 v0의 비트 i다. 명령의 비트 25인 vm이 0이면 마스크를 쓰고(어셈블리에서 v0.t), 1이면 쓰지 않는다.
한 명령이 다루는 원소 번호는 네 부류로 나뉜다. vstart보다 앞은 prestart, vstart부터 vl 앞까지는 body, vl부터 끝까지는 tail이다. body는 다시 마스크가 켜진 active와 꺼진 inactive로 나뉜다.
그렇다면 inactive와 tail 자리의 목적지 레지스터에는 무엇이 남을까? vtype의 두 비트가 정한다.
| vta | vma | 어셈블러 | tail 원소 | inactive 원소 |
|---|---|---|---|---|
0 | 0 | tu, mu | undisturbed (이전 값 유지) | undisturbed |
0 | 1 | tu, ma | undisturbed | agnostic |
1 | 0 | ta, mu | agnostic | undisturbed |
1 | 1 | ta, ma | agnostic | agnostic |
"When a set is marked agnostic, the corresponding set of destination elements in any vector destination operand can either retain the value they previously held, or are overwritten with 1s."
Volume I, "V" Extension — vta and vma — agnostic은 "아무 값"이 아니라 "이전 값 또는 모두 1"이다. 원소마다 둘 중 어느 쪽이 될지는 정해지지 않으며, 같은 입력으로 다시 실행해도 달라질 수 있다.
레지스터 이름 바꾸기(renaming)를 하는 비순차 코어에서 결과는 새 물리 레지스터에 쓰인다. undisturbed라면 쓰지 않는 원소까지 옛 물리 레지스터에서 읽어 복사해야 한다. agnostic이면 그 복사를 생략할 수 있다. 0이 아니라 "모두 1"로 정한 것은 소프트웨어가 그 값에 기대지 않게 하려는 의도다. 단순한 순차 코어는 설정을 무시하고 늘 undisturbed로 동작해도 된다. 마스크를 만드는 명령의 tail은 vta와 상관없이 항상 agnostic이다.
마스크와 꼬리 정책 시각화
벡터 메모리 접근
벡터 로드·저장은 새 opcode를 만들지 않고 스칼라 부동소수점 로드·저장 opcode(LOAD-FP, STORE-FP) 안의 남는 width 값을 썼다. 메모리 원소 폭 EEW는 명령에 직접 적혀 있어서(vle8, vle16, vle32, vle64) 폭이 섞인 루프에서도 vtype을 덜 바꿔도 된다. 주소 지정 방식은 mop[1:0] 필드가 정한다.
| mop[1:0] | 로드 | 저장 | 설명 |
|---|---|---|---|
00 | vle<EEW>.v | vse<EEW>.v | 단위 보폭. lumop로 마스크 로드(vlm.v), 통째 레지스터 로드, fault-only-first를 구별 |
01 | vluxei<EEW>.v | vsuxei<EEW>.v | 인덱스, 순서 보장 없음 (EEW는 인덱스 폭, 데이터는 SEW) |
10 | vlse<EEW>.v | vsse<EEW>.v | 상수 보폭 |
11 | vloxei<EEW>.v | vsoxei<EEW>.v | 인덱스, 원소 순서대로 접근 (입출력 장치 영역용) |
세그먼트 로드: 구조체 배열을 필드별로
RGB 픽셀처럼 필드 여러 개가 반복되는 "구조체의 배열"은 세그먼트(segment) 로드로 한 번에 풀 수 있다. nf 필드가 세그먼트당 필드 수(2–8)를 정하고, 각 필드가 연속된 번호의 레지스터(그룹)로 들어간다. EMUL × 필드 수는 8을 넘을 수 없다.
vsseg3e8.v는 반대 방향이다.fault-only-first: 끝을 모르는 루프
strlen처럼 어디서 끝날지 모르는 루프를 벡터로 돌리면, 문자열 끝 뒤의 바이트를 미리 읽다가 매핑되지 않은 페이지를 건드릴 수 있다. vle8ff.v 같은 fault-only-first 로드는 0번 원소의 예외만 트랩으로 처리한다. 1번 이후 원소에서 예외가 날 자리라면 트랩 없이 vl을 그 원소 번호로 줄이고 끝낸다. 아래는 규격의 예제다.
트랩 없이 "어디까지 읽을 수 있는지"를 알려 주므로, 주소 공간을 몰래 탐색하는 데 쓰일 수 있다. 그래서 규격은 이 형태를 단위 보폭 로드에만 두었다. 이미 아는 영역 바로 뒤만 탐색할 수 있게 해 위험을 줄인 것이다. 14장의 orc.b를 쓴 스칼라 strlen(14장)과 비교해 보면 같은 문제를 푸는 두 가지 방법이 보인다.
넓히기·리덕션·순열
산술 명령은 OP-V opcode에 있고, funct3가 피연산자 종류를 정한다. .vv(벡터-벡터), .vx(벡터-정수 레지스터), .vi(벡터-5비트 즉시값), 부동소수점의 .vf다. 대부분은 이름만 보면 뜻을 알 수 있으니, 여기서는 벡터만의 독특한 무리 셋을 본다.
넓히기와 좁히기
vw로 시작하는 넓히기(widening) 명령은 SEW 원소 둘로 2·SEW 결과를 만든다. 원소 수는 그대로이므로 결과는 레지스터 그룹이 두 배로 커진다. 규격의 표현으로 목적지의 EEW = 2·SEW, EMUL = 2·LMUL이다. 그래서 LMUL=8에서의 넓히기는 EMUL=16이 되어 예약이다. 반대로 vn으로 시작하는 좁히기(narrowing)는 2·SEW 원본을 SEW로 줄인다.
vwadd.vv. 원소 i의 결과는 원소 i 자리에 그대로 놓이지만, 폭이 두 배여서 앞의 넷은 v8, 뒤의 넷은 v9에 들어간다. 금색 점은 a2·b2, a5·b5, a7·b7의 결과가 가는 길이다.리덕션: 벡터를 스칼라 하나로
vredsum.vs vd, vs2, vs1은 vs2의 active 원소를 모두 더하고 vs1[0]을 보탠 값을 vd[0]에 쓴다. 스칼라 입력과 출력이 x 레지스터가 아니라 벡터 레지스터의 0번 원소에 있는 것이 특징이다. 벡터 유닛과 스칼라 유닛을 느슨하게 분리해 두려는 설계다.
순열: 원소의 자리를 옮기기
원소의 위치를 바꾸는 명령은 vslideup/vslidedown(정해진 칸 수만큼 밀기), vslide1up/vslide1down(한 칸 밀며 스칼라 끼워 넣기), vrgather(인덱스 벡터로 아무 원소나 골라 모으기. 인덱스가 VLMAX 이상이면 0), vcompress(마스크로 고른 원소를 앞으로 모으기)가 있다.
vslideup은 원소를 OFFSET만큼 뒤로 민다. 오른쪽: 규격의 vcompress 예제를 0번 원소가 왼쪽에 오도록 다시 그렸다. 마스크가 1인 원소 0·2·5·7·8이 결과의 앞쪽 다섯 칸에 모이고, 남은 네 칸은 tu 정책이라 실행 전 v2 값이 남는다.마스크끼리의 논리 연산(vmand.mm, vmnot.m 등), 마스크의 1 개수(vcpop.m), 첫 1의 위치(vfirst.m), 첫 1 앞까지 켜기(vmsbf.m), 누적 번호(viota.m) 같은 명령이 조건문과 while 루프의 벡터화를 돕는다. 앞의 strlen 예제의 vfirst.m이 그 예다.
Zve*·Zvl*와 벡터 암호 확장
V 확장 전체는 작은 마이크로컨트롤러에게 무겁다. 64비트 원소, 배정밀도 부동소수점, VLEN 128비트를 모두 갖추어야 하기 때문이다. 그래서 규격은 임베디드용 Zve* 다섯 가지와 최소 VLEN을 밝히는 Zvl*를 따로 정의한다.
| 확장 | 최소 VLEN | 지원 EEW | FP32 | FP64 |
|---|---|---|---|---|
| Zve32x | 32 | 8, 16, 32 | N | N |
| Zve32f | 32 | 8, 16, 32 | Y | N |
| Zve64x | 64 | 8, 16, 32, 64 | N | N |
| Zve64f | 64 | 8, 16, 32, 64 | Y | N |
| Zve64d | 64 | 8, 16, 32, 64 | Y | Y |
| V | 128 | 8, 16, 32, 64 | Y | Y |
Zve64*에는 EEW=64의 vmulh 계열과 vsmul이 빠져 있다. 64비트 곱의 상위 절반을 만드는 데 게이트가 많이 들기 때문이다. Zvl 확장은 Zvl32b, Zvl64b, Zvl128b, Zvl256b, Zvl512b, Zvl1024b처럼 최소 VLEN을 밝히며, 긴 것은 짧은 것을 모두 포함한다. 프로파일은 이것으로 "이 플랫폼의 VLEN은 적어도 얼마"를 요구할 수 있다(23장).
RV64GCV와 RV64GCV_Zve64f는 같은 뜻이다. V가 이미 Zve64f를 포함하기 때문이다. 규격은 이렇게 겹치는 확장을 함께 적는 것을 허용한다. 반대로 RV32IMC_Zve32x라면 정수 벡터만 있는 작은 MCU다.
벡터 암호 확장
14장의 스칼라 암호(Zk*)처럼, 벡터에도 암호 확장(Vector Cryptography v1.0)이 있다. AES 한 블록은 128비트이므로 이 명령들은 SEW=32 원소 네 개를 묶은 원소 그룹(element group, EGW=128) 단위로 동작한다. VLEN이 128보다 작으면 LMUL로 레지스터를 묶어 그룹을 만든다.
| 확장 | 내용 |
|---|---|
| Zvbb · Zvkb | 벡터 기본 비트 조작(vandn, vbrev8, vrev8, 회전 등). Zvkb는 Zvbb의 암호용 부분집합 |
| Zvbc | 벡터 캐리 없는 곱셈 vclmul/vclmulh (SEW=64 전용, Zve64x 필요) |
| Zvkg | GCM/GMAC의 GHASH |
| Zvkned | AES 암·복호화와 키 스케줄 (vaesef, vaesem …) |
| Zvknha · Zvknhb | SHA-256 / SHA-256 + SHA-512 (Zvknhb는 Zve64x 필요) |
| Zvksed · Zvksh | 중국 표준 SM4 블록 암호 / SM3 해시 |
| Zvkt | 나열된 벡터 명령의 데이터 무관 실행 시간 보장 |
| Zvkn · Zvks | 묶음 이름. Zvkn = Zvkned + Zvknhb + Zvkb + Zvkt, Zvks = Zvksed + Zvksh + Zvkb + Zvkt (뒤에 c·g가 붙으면 Zvbc·Zvkg 추가) |
핵심 정리
- V 확장은 벡터 길이 무관(VLA) 방식이다. 프로그램은 AVL을 건네고 하드웨어가 돌려준 vl만큼 처리하므로, 같은 바이너리가 VLEN이 다른 칩에서 모두 돈다.
- 레지스터는 v0–v31(각 VLEN비트), CSR은 vstart(0x008), vxsat(0x009), vxrm(0x00A), vcsr(0x00F), vl(0xC20), vtype(0xC21), vlenb(0xC22)다.
- vtype은 vill(XLEN-1), vma(7), vta(6), vsew(5:3), vlmul(2:0)로 이루어지며, VLMAX = LMUL × VLEN / SEW이다. LMUL>1이면 시작 번호가 LMUL의 배수인 레지스터 그룹을 쓴다.
vsetvli는 AVL ≤ VLMAX면 vl=AVL, AVL ≥ 2·VLMAX면 vl=VLMAX, 그 사이면 ceil(AVL/2)~VLMAX 중 하나를 고른다. 코드는 늘 돌려받은 vl을 써야 한다.- 마스크는 v0에서 오고(vm=0), 원소는 prestart·active·inactive·tail로 나뉜다. agnostic 정책은 "이전 값 또는 모두 1"이다.
- 메모리 접근은 단위 보폭·상수 보폭·인덱스(순서 보장/비보장)·세그먼트가 있고, fault-only-first 로드로 strlen 같은 while 루프를 벡터화한다.
- 넓히기 결과는 EEW=2·SEW, EMUL=2·LMUL이다. 리덕션은 벡터 레지스터 0번 원소를 스칼라로 쓴다. V는 Zve64d + Zvl128b에 의존하고, 임베디드용 Zve32x~Zve64d가 따로 있다.
확인 퀴즈
VLEN=256, SEW=16, LMUL=2일 때 VLMAX는?
LMUL=4일 때 레지스터 그룹의 시작으로 쓸 수 있는 것은?
VLMAX=16인 설정에서 AVL=20으로 vsetvli를 실행했다. 규격이 허용하지 않는 vl은?
vta=1(tail agnostic)일 때 tail 원소의 목적지 값은?
vle8ff.v를 실행하는데 5번 원소의 주소에서 페이지 폴트가 날 상황이다. 무슨 일이 일어나나?
LMUL=8에서 넓히기 명령 vwadd.vv를 쓸 수 없는 이유는?
vill 비트가 vtype의 맨 위 비트(XLEN-1)에 있는 이유로 가장 알맞은 것은?
bltz 같은 부호 분기 하나로 확인할 수 있다.응용 프로세서용 V 확장이 의존하는 확장 조합은?
더 읽을거리
- The RISC-V Instruction Set Manual, Volume I — "V" Standard Extension for Vector Operations, Version 1.0 (v20260120)
- Volume I — Vector Assembly Code Examples (vvaddint32, memcpy, saxpy, sgemm 등)
- Volume I — Cryptography Extensions: Vector Instructions, Version 1.0
- RISC-V Vector C Intrinsics — C에서 벡터 명령을 쓰는 표준 내장 함수
- Richard M. Russell, "The CRAY-1 Computer System", Communications of the ACM 21(1), 1978 — 벡터 레지스터 머신의 원형
- 이 책의 14장(스칼라 비트 조작과 암호), 12장(부동소수점), 23장(RVA23에서의 V)