{"title":"Georg's Log - programming","link":[{"@attributes":{"href":"https:\/\/gms.tf\/","rel":"alternate"}},{"@attributes":{"href":"https:\/\/gms.tf\/feeds\/programming.atom.xml","rel":"self"}}],"id":"https:\/\/gms.tf\/","updated":"2020-01-25T00:00:00+01:00","entry":{"title":"Programming with RISC-V Vector Instructions","link":{"@attributes":{"href":"https:\/\/gms.tf\/riscv-vector.html","rel":"alternate"}},"published":"2020-01-25T00:00:00+01:00","updated":"2020-01-25T00:00:00+01:00","author":{"name":"Georg Sauthoff"},"id":"tag:gms.tf,2020-01-25:\/riscv-vector.html","summary":"<p>Perhaps the most interesting part of the open <a href=\"https:\/\/en.wikipedia.org\/wiki\/RISC-V\">RISC-V<\/a> <a href=\"https:\/\/en.wikipedia.org\/wiki\/Instruction_set_architecture\">instruction set architecture (ISA)<\/a> is the vector extension (RISC-V &quot;V&quot;).\nIn contrast to the average <a href=\"https:\/\/en.wikipedia.org\/wiki\/SIMD\">single-instruction multipe-data (SIMD)<\/a> instruction set, RISC-V vector instructions are vector length agnostic (VLA).\nThus, a RISC-V &quot;V&quot; CPU is flexible in choosing a vector register size \u2026<\/p>","content":"<p>Perhaps the most interesting part of the open <a href=\"https:\/\/en.wikipedia.org\/wiki\/RISC-V\">RISC-V<\/a> <a href=\"https:\/\/en.wikipedia.org\/wiki\/Instruction_set_architecture\">instruction set architecture (ISA)<\/a> is the vector extension (RISC-V &quot;V&quot;).\nIn contrast to the average <a href=\"https:\/\/en.wikipedia.org\/wiki\/SIMD\">single-instruction multipe-data (SIMD)<\/a> instruction set, RISC-V vector instructions are vector length agnostic (VLA).\nThus, a RISC-V &quot;V&quot; CPU is flexible in choosing a vector register size while RISC-V &quot;V&quot; binary code is portable between different CPU implementations.<\/p>\n<p>This articles compares the two main different styles of vector ISAs, discusses a string processing  example that is implemented using <a href=\"https:\/\/github.com\/riscv\/riscv-v-spec\/releases\/tag\/0.8\">RISC-V &quot;V&quot; draft version 0.8<\/a> (current as of early 2020) vector instructions and details how to set up a RISC-V &quot;V&quot; development environment under Linux.<\/p>\n<nav>\n<h3>Contents<\/h3>\n<ul>\n<li><a href=\"#simd-challenges\" id=\"toc-entry-1\">SIMD Challenges<\/a><\/li>\n<li><a href=\"#the-solution-agnosticism\" id=\"toc-entry-2\">The Solution: Agnosticism<\/a><\/li>\n<li><a href=\"#example\" id=\"toc-entry-3\">Example<\/a><ul>\n<li><a href=\"#shuffling-nibbles\" id=\"toc-entry-4\">Shuffling Nibbles<\/a><\/li>\n<li><a href=\"#converting-bytes\" id=\"toc-entry-5\">Converting  Bytes<\/a><\/li>\n<li><a href=\"#storing-the-result\" id=\"toc-entry-6\">Storing the Result<\/a><\/li>\n<li><a href=\"#concluding-remarks\" id=\"toc-entry-7\">Concluding Remarks<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#getting-started\" id=\"toc-entry-8\">Getting Started<\/a><ul>\n<li><a href=\"#spike\" id=\"toc-entry-9\">Spike<\/a><\/li>\n<li><a href=\"#gnu-toolchain\" id=\"toc-entry-10\">GNU Toolchain<\/a><\/li>\n<li><a href=\"#proxy-kernel\" id=\"toc-entry-11\">Proxy-Kernel<\/a><\/li>\n<li><a href=\"#binutils\" id=\"toc-entry-12\">Binutils<\/a><\/li>\n<li><a href=\"#assembling\" id=\"toc-entry-13\">Assembling<\/a><\/li>\n<li><a href=\"#emulating\" id=\"toc-entry-14\">Emulating<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#see-also\" id=\"toc-entry-15\">See Also<\/a><\/li>\n<\/ul>\n<\/nav>\n<section id=\"simd-challenges\">\n<h2><a href=\"#toc-entry-1\">SIMD Challenges<\/a><\/h2>\n<p>With a vector length specific (VLS) <a href=\"https:\/\/en.wikipedia.org\/wiki\/SIMD\">SIMD<\/a> instruction set the main problem is to pick the right vector register size.\nOf course there is a trade-off between the amount of data-level parallelism and hardware costs.\nDue to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Moore%27s_law\">Moore's law<\/a>, vector register sizes can be increased over time without making the CPU chip more expensive.\nAlso, some users are interested in powerful CPUs with wider vector registers while the average user is fine with averagely sized register.\nThus, there is no one right vector register size.\nThis shows for example with x86, where the answer is to provide one VLS ISA after the other, such as <a href=\"https:\/\/en.wikipedia.org\/wiki\/MMX_(instruction_set)\">MMX<\/a> (64 bit registers), <a href=\"https:\/\/en.wikipedia.org\/wiki\/Streaming_SIMD_Extensions\">SSE<\/a> (128 bit), <a href=\"https:\/\/en.wikipedia.org\/wiki\/Advanced_Vector_Extensions\">AVX<\/a> (256 bit) and <a href=\"https:\/\/en.wikipedia.org\/wiki\/AVX-512\">AVX512<\/a> (512 bit).<\/p>\n<p>Because of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Backward_compatibility\">backward compatibility<\/a>, each CPU that adds a new VLS ISA also has to support all existing ones.\nThis leads to a waste of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Opcode\">opcode<\/a> space and increases the complexity of the CPU's <a href=\"https:\/\/en.wikipedia.org\/wiki\/Instruction_cycle#Decode_stage\">instruction decoder<\/a>.\nOf course this also increases the complexity for the programmer who has then remember (or look up all the time) syntactic and functional differences between all the VLS ISAs.<\/p>\n<p>That means that while VLS code written for smaller vector registers runs on newer CPUs, it can't make use of the wider vector registers.\nThus, existing code has to be reimplemented again and again to make use of new VLS ISAs.\nSimilarly, code written for high-end CPUs doesn't run on middle-end CPUs (because it requires the VLS-ISA with wider vector registers).\nThus one either has to target some older (hopefully widely available) VSL-ISA or has to provide multiple implementations for different VSL-ISAs.<\/p>\n<\/section>\n<section id=\"the-solution-agnosticism\">\n<h2><a href=\"#toc-entry-2\">The Solution: Agnosticism<\/a><\/h2>\n<p>The solution to all this is to design a variable length vector instruction set.\nIn that way the instructions are then agnostic to the vector register size of a concrete CPU implementation.\nThus, the binary code is portable between low, middle and high-end CPUs, and automatically makes use of wider registers in newer CPUs.<\/p>\n<p>The <a href=\"https:\/\/github.com\/riscv\/riscv-v-spec\/releases\/tag\/0.8\">RISC-V vector extension &quot;V&quot;<\/a> implements such vector instruction set.\nAs of early 2020, the <a href=\"https:\/\/github.com\/riscv\/riscv-v-spec\/releases\/tag\/0.8\">RISC-V &quot;V&quot; specification<\/a> is at version 0.8 and has draft status.<\/p>\n<p>RISC-V &quot;V&quot; adds 32 vector registers, where the first register can be used as mask register and up to 8 registers can be grouped together.\nThe operands of a vector instruction such as <code>vadd.vv<\/code> are single vector registers or vector register groups.<\/p>\n<p>Since vector registers are of variable length, RISC-V &quot;V&quot; code has to indicate the maximum vector length it wants to work with, e.g.:<\/p>\n<pre class=\"m-code\"><span class=\"nf\">vsetvli<\/span><span class=\"w\"> <\/span><span class=\"no\">t0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">e8<\/span><\/pre>\n<p>Meaning that a vector length (vl) of up to <code>a2<\/code> 8 bit wide (<code>e8<\/code>) elements is requested while the instruction returns the resulting length in register <code>t0<\/code>.\nThus, if the <code>a2<\/code> register is set to - say - <code>4096<\/code>, on a CPU with a vector register length (VLEN) of 128 bits, the following vector instructions work on 16 element wide vectors and <code>t0<\/code> is thus set to <code>16<\/code>, while on a CPU with 512 bit registers the vectors are configured to be 64 elements wide and <code>t0<\/code> is set to <code>64<\/code>.<\/p>\n<p>This approach also simplifies loops that iterate over an input array in vector length chunks.\nFor example (where <code>a1<\/code> contains the address of an array of <code>a2<\/code> times 4 bytes):<\/p>\n<pre class=\"m-code\"><span class=\"nl\">.Loop:<\/span><span class=\"w\">                        <\/span><span class=\"c1\"># local symbol name because of .L prefix<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">vsetvli<\/span><span class=\"w\"> <\/span><span class=\"no\">t0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">e32<\/span><span class=\"w\">       <\/span><span class=\"c1\"># configure vectors of 32 bit elements<\/span>\n\n<span class=\"w\">    <\/span><span class=\"nf\">vlw.v<\/span><span class=\"w\">   <\/span><span class=\"no\">v4<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"no\">a1<\/span><span class=\"p\">)<\/span><span class=\"w\">          <\/span><span class=\"c1\"># Load t0 elements into v4,<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># starting at the address stored in a1<\/span>\n\n<span class=\"w\">    <\/span><span class=\"na\">...<\/span><span class=\"w\">                       <\/span><span class=\"c1\"># work with that chunk<\/span>\n\n<span class=\"w\">    <\/span><span class=\"nf\">slli<\/span><span class=\"w\">    <\/span><span class=\"no\">t1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"w\">         <\/span><span class=\"c1\"># shift-left logical, i.e. times 4<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">add<\/span><span class=\"w\">     <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t1<\/span><span class=\"w\">        <\/span><span class=\"c1\"># increment src by read elements<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">sub<\/span><span class=\"w\">     <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t0<\/span><span class=\"w\">        <\/span><span class=\"c1\"># decrement n<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">bnez<\/span><span class=\"w\">    <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">.Loop<\/span><span class=\"w\">         <\/span><span class=\"c1\"># branch to loop head if not equal to zero<\/span>\n\n<span class=\"w\">    <\/span><span class=\"na\">...<\/span><span class=\"w\">                       <\/span><span class=\"c1\"># continue<\/span><\/pre>\n<p>In cases where <code>a2<\/code> isn't a multiple of the maximum vector length, the last iteration\nsets the vector length to a smaller value and the following vector instructions ignore\nthe unused trailing elements.\nThis implicit masking mechanism is orthogonal to the optional mask operand that is supported by most RISC-V vector instructions.<\/p>\n<p>In contrast to that, with a vector length specific ISA, the main loop usually has to be followed by some finalization code block to explicitly deal with the last elements that don't fill a complete register, e.g.:<\/p>\n<pre class=\"m-code\"><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">unsigned<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"n\">p<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">inp<\/span><span class=\"p\">;<\/span>\n<span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">l<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"w\"> <\/span><span class=\"o\">\/<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"n\">VECTOR_LENGTH<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">ELEMENT_BYTES<\/span><span class=\"p\">);<\/span>\n<span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">l<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">p<\/span><span class=\"w\"> <\/span><span class=\"o\">+=<\/span><span class=\"w\"> <\/span><span class=\"n\">VECTOR_LENGTH<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">ELEMENT_BYTES<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">...<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ load p into a vector register<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">...<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ execute some vector instructions<\/span>\n<span class=\"p\">}<\/span>\n<span class=\"c1\">\/\/ deal with some remaining bytes<\/span>\n<span class=\"c1\">\/\/ e.g. by setting up a mask or work on single elements<\/span>\n<span class=\"k\">for<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"n\">l<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"n\">i<\/span><span class=\"w\"> <\/span><span class=\"o\">&lt;<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"p\">;<\/span><span class=\"w\"> <\/span><span class=\"o\">++<\/span><span class=\"n\">i<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">p<\/span><span class=\"w\"> <\/span><span class=\"o\">+=<\/span><span class=\"w\"> <\/span><span class=\"n\">ELEMENT_BYTES<\/span><span class=\"p\">)<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"p\">...<\/span><span class=\"w\"> <\/span><span class=\"c1\">\/\/ work on the next element located at p<\/span>\n<span class=\"p\">}<\/span><\/pre>\n<\/section>\n<section id=\"example\">\n<h2><a href=\"#toc-entry-3\">Example<\/a><\/h2>\n<p>To illustrate RISC-V &quot;V&quot; with a real example, this section shows how to implement a vectorized function that converts a string of <a href=\"https:\/\/en.wikipedia.org\/wiki\/Binary-coded_decimal\">binary coded decimals (BCD)<\/a> into an <a href=\"https:\/\/en.wikipedia.org\/wiki\/ASCII\">ASCII<\/a> string.\nWhy BCD to ASCII conversion?\nThe task is complex enough such that most of the different vector instructions are used.\nOn the other hand, it's simple enough to fit into a small article and doesn't require domain specific knowledge.\nIt also demonstrates some perhaps not entirely obvious ways how vector instructions are used for string processing where those instruction could be assumed to only be useful for calculations.<\/p>\n<p>With BCD, a byte (8 bits) is divided into two <a href=\"https:\/\/en.wikipedia.org\/wiki\/Nibble\">nibbles<\/a> (4 bits) such that each nibble stores a (hexa-)decimal digit.\nNote that 4 bits allow to exactly encode <span class=\"formula\">2<sup>4<\/sup><\/span> values, thus when using it just for storing decimal digits it's not a very efficient encoding.<\/p>\n<p>For the purpose of our example, the exercise is to write vector code that efficiently converts a BCD string such as <code>{ 0x12, 0x34, ..., 0xcd, 0xef }<\/code> to a corresponding ASCII string (e.g. <code>{ '1', '2', '3', '4', ..., 'c', 'd', 'e', 'f' }<\/code>). On a high-level, a solution involves separating the nibbles into single bytes and then converting each byte to the matching ASCII value.<\/p>\n<p>The complete example source code is available in <a href=\"https:\/\/github.com\/gsauthof\/riscv\">my github repository<\/a>.<\/p>\n<section id=\"shuffling-nibbles\">\n<h3><a href=\"#toc-entry-4\">Shuffling Nibbles<\/a><\/h3>\n<p>Our function has the following function signature:<\/p>\n<pre class=\"m-code\"><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">bcd2ascii<\/span><span class=\"p\">(<\/span><span class=\"kt\">void<\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">dst<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"p\">);<\/span><\/pre>\n<p>Meaning that <code>n<\/code> input bytes are read from <code>src<\/code> and the conversion writes <code>2*n<\/code> bytes into the <code>dst<\/code> output buffer.\nUnder the RISC-V calling conventions, <code>dst<\/code> is passed in register <code>a0<\/code>, <code>src<\/code> in register <code>a1<\/code> and <code>n<\/code> in register <code>a2<\/code>.<\/p>\n<pre class=\"m-code\"><span class=\"nl\">.Loop:<\/span><span class=\"w\">                        <\/span><span class=\"c1\"># local symbol name because of .L prefix<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">vsetvli<\/span><span class=\"w\"> <\/span><span class=\"no\">a3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">e16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">m8<\/span><span class=\"w\">   <\/span><span class=\"c1\"># switch to 16 bit element size,<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># 4 groups of 8 registers<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\"># --&gt; a3 = min(a2, 8*vlenb\/2)<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">vlbu.v<\/span><span class=\"w\"> <\/span><span class=\"no\">v16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"no\">a1<\/span><span class=\"p\">)<\/span><span class=\"w\">          <\/span><span class=\"c1\"># Load a3 unsigned bytes,<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># one byte per 16 bit element, zero-extend,<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># starting at addr stored in a1<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\"># --&gt; v16 = | 0, a1[vlenb\/2-1], ..., 0, a1[1], 0, a1[0] |, ...,<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">#     v23 = | 0, a1[a3-1],       ...,  0, a1[7*vlenb\/2] |<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\"># --&gt; v16 = | ... 00mn 00kl 00ij 00gh |<\/span>\n\n<span class=\"w\">    <\/span><span class=\"nf\">add<\/span><span class=\"w\"> <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a3<\/span><span class=\"w\">            <\/span><span class=\"c1\"># increment src by read elements<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">sub<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a3<\/span><span class=\"w\">            <\/span><span class=\"c1\"># decrement n<\/span><\/pre>\n<p>The main loop starts with configuring a vector element size of 16 bit (<code>e16<\/code>), grouping 8 registers together (<code>m8<\/code>) and requesting a vector length that equals the number of remaining source bytes or the CPU maximum.\nWith this grouping, each register group is accessed by using a vector register with a number that is dividable by 8.\nThat means <code>v0<\/code> identifies the group consisting of <code>v0, v1, ..., v7<\/code>, <code>v8<\/code> identifies <code>v8, ..., v15<\/code>, etc.<\/p>\n<p>The <code>vl*.v<\/code> load instruction comes in different variants.\nHere, the <code>vlbu.v<\/code> variant zero extends each input byte per 16 bit element which is useful in our example because this directly leaves room for shuffling the nibbles.\nIn other words, it's a widening load and thus saves a separate widening operation such as <code>vwaddu.vx<\/code>.<\/p>\n<p>That means on CPUs with 256 bit vector registers, this code loads up to 128 input bytes into the <code>v16<\/code> register group.<\/p>\n<p>Note that register content in the comments is enclosed in <code>| |<\/code> and written right to left, starting with the least significant element.\nArbitrary nibbles are denoted sometimes by placeholder variables such as <code>g, h, ...<\/code>.<\/p>\n<p>The actual nibble shuffling:<\/p>\n<pre class=\"m-code\"><span class=\"nf\">vsll.vi<\/span><span class=\"w\"> <\/span><span class=\"no\">v24<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">8<\/span><span class=\"w\">       <\/span><span class=\"c1\"># shift-left-logical each element by 8 bits<\/span>\n<span class=\"c1\"># --&gt; v24 = | ... mn00 kl00 ij00 gh00 |<\/span>\n\n<span class=\"nf\">vsrl.vi<\/span><span class=\"w\"> <\/span><span class=\"no\">v16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">4<\/span><span class=\"w\">       <\/span><span class=\"c1\"># shift-right-logical each element by 4 bits<\/span>\n<span class=\"c1\"># --&gt; v16 = | ... 000m 000k 000i 000g |<\/span>\n\n<span class=\"nf\">slli<\/span><span class=\"w\"> <\/span><span class=\"no\">a3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"w\">            <\/span><span class=\"c1\"># shift left logical by immediate,<\/span>\n<span class=\"w\">                          <\/span><span class=\"c1\"># i.e. to double the number of vector elements<\/span>\n<span class=\"nf\">vsetvli<\/span><span class=\"w\"> <\/span><span class=\"no\">t4<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">e8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">m8<\/span><span class=\"w\">    <\/span><span class=\"c1\"># switch to 8 bit element size,<\/span>\n<span class=\"w\">                          <\/span><span class=\"c1\"># 4 groups of 8 registers<\/span>\n\n<span class=\"nf\">vand.vx<\/span><span class=\"w\"> <\/span><span class=\"no\">v24<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v24<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t2<\/span><span class=\"w\">      <\/span><span class=\"c1\"># and each element with 0x0f,<\/span>\n<span class=\"w\">                          <\/span><span class=\"c1\"># i.e. zero-out the high nibbles<\/span>\n<span class=\"c1\"># --&gt; v24 = | ... 0n 00 0l 00 0j 00 0h 00 |<\/span>\n<span class=\"nf\">vor.vv<\/span><span class=\"w\">  <\/span><span class=\"no\">v16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v16<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v24<\/span><span class=\"w\">     <\/span><span class=\"c1\"># or each element<\/span>\n<span class=\"c1\"># --&gt; v16 = | ... 0n 0m 0l 0k 0j 0i 0h 0g |<\/span><\/pre>\n<p>So far the example shows most of the syntactic conventions of the &quot;V&quot; ISA.\nVector instructions start with <code>v<\/code> and a suffix such as <code>.vi<\/code>, <code>.vx<\/code> and <code>.vv<\/code> describe the source operand types, i.e. vector-immediate, vector-scalar and vector-vector.<\/p>\n<p>The bit-shift instructions don't cross element boundaries.\nThus, just vector group <code>v24<\/code> has to be zero-masked and not <code>v16<\/code>.\nThe mask is located in register <code>t2<\/code> which is set before the loop start.<\/p>\n<p>Switching the vector register configuration to 8 bit elements (<code>e8<\/code>) at this\npoint allows to use <code>0xf<\/code> as mask value instead of the larger <code>0xf00<\/code>.\nThus, it fits into the immediate operand of the load immediate instruction such\nthat one additional instruction is saved (i.e. <code>addi t2,zero,15<\/code>).  It even\nfits into the immediate operand of the compressed load immediate instruction,\nwhich just encodes into two bytes (i.e. <code>c.li<\/code>) instead of the regular four.<\/p>\n<p>The final clean result of separated digits is located in vector group <code>v16<\/code>.<\/p>\n<\/section>\n<section id=\"converting-bytes\">\n<h3><a href=\"#toc-entry-5\">Converting  Bytes<\/a><\/h3>\n<p>The actual conversion is done in one instruction:<\/p>\n<pre class=\"m-code\"><span class=\"nf\">vrgather.vv<\/span><span class=\"w\"> <\/span><span class=\"no\">v24<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v16<\/span>\n<span class=\"c1\"># --&gt; v24[i] = (v16[i] &gt;= VLMAX) ? 0 : v8[v16[i]]<\/span><\/pre>\n<p>Here, vector group <code>v8<\/code> is used as table to look up the <a href=\"https:\/\/en.wikipedia.org\/wiki\/ASCII\">ASCII<\/a> values.\nThat means the <code>v8<\/code> lookup table maps the integers <code>{0, 1, 2, ..., 0xd, 0xe, 0xf }<\/code> to the ASCII characters <code>{ '0', '1', '2', ..., 'd', 'e', 'f' }<\/code>.<\/p>\n<p>Of course, this lookup table has to be constructed before the loop is entered:<\/p>\n<pre class=\"m-code\"><span class=\"nf\">li<\/span><span class=\"w\"> <\/span><span class=\"no\">a6<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">16<\/span><span class=\"w\">                 <\/span><span class=\"c1\"># load immediate (pseudo instruction)<\/span>\n<span class=\"nf\">vsetvli<\/span><span class=\"w\"> <\/span><span class=\"no\">t0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a6<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">e8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">m8<\/span><span class=\"w\">    <\/span><span class=\"c1\"># switch to 8 bit element size,<\/span>\n<span class=\"w\">                          <\/span><span class=\"c1\"># i.e. 4 groups of 8 registers<\/span>\n\n<span class=\"nf\">vid.v<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"w\">                  <\/span><span class=\"c1\"># store Vector Element Indices,<\/span>\n<span class=\"w\">                          <\/span><span class=\"c1\"># i.e. v8 = | 16, ..., 2, 1, 0 |<\/span>\n<span class=\"nf\">vmsgtu.vi<\/span><span class=\"w\"> <\/span><span class=\"no\">v0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">9<\/span><span class=\"w\">       <\/span><span class=\"c1\"># set mask-bit if greater than unsigned immediate<\/span>\n<span class=\"c1\"># --&gt; v0 = | 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |<\/span>\n\n<span class=\"nf\">li<\/span><span class=\"w\"> <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">48<\/span><span class=\"w\">                 <\/span><span class=\"c1\"># load immediate, i.e. &#39;0&#39;<\/span>\n<span class=\"nf\">vadd.vx<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a7<\/span><span class=\"w\">        <\/span><span class=\"c1\"># add that scalar to each element<\/span>\n\n<span class=\"nf\">addi<\/span><span class=\"w\"> <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">-9<\/span><span class=\"w\">           <\/span><span class=\"c1\"># add immediate, i.e. set to 39 == &#39;a&#39;-&#39;0&#39;-10,<\/span>\n<span class=\"w\">                          <\/span><span class=\"c1\"># i.e. to arrive at &#39;a&#39;, &#39;b&#39;, ...<\/span>\n<span class=\"nf\">vadd.vx<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v8<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v0.t<\/span><span class=\"w\">  <\/span><span class=\"c1\"># masked add for the additional offset<\/span><\/pre>\n<p>Configuring a grouping of 8 registers for a vector of 16 elements might look like overkill because 128 bit vector registers are sufficient and should be widely available.\nOn the other hand, there might be a CPU with &quot;V&quot; support that just implements - say - 64 bit vector registers where we would need to group 2 registers.\nSince a grouping thus may be needed it really doesn't hurt to configure the maximum here.<\/p>\n<p>The <code>v0.t<\/code> syntax is just a marker that <code>v0<\/code> is used as mask.\nNote that masks always just consist of one vector register, even if register groups are configured.\nWith the current &quot;V&quot; 0.8 draft, the <code>v0<\/code> register is the only valid choice for a mask operand.<\/p>\n<p>Similar to before, the value <code>39<\/code> is constructed with <code>addi<\/code> instead of directly loading it with the pseudo-instruction <code>li<\/code> into another register because <code>-9<\/code> fits into the immediate operand of the compressed <code>c.addi<\/code> instruction.<\/p>\n<\/section>\n<section id=\"storing-the-result\">\n<h3><a href=\"#toc-entry-6\">Storing the Result<\/a><\/h3>\n<pre class=\"m-code\"><span class=\"nf\">vsb.v<\/span><span class=\"w\"> <\/span><span class=\"no\">v24<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">(<\/span><span class=\"no\">a0<\/span><span class=\"p\">)<\/span><span class=\"w\">           <\/span><span class=\"c1\"># write result to dst<\/span>\n<span class=\"c1\"># --&gt; a0[0] = v24[0], a0[1] = v24[1], ..., a0[vl-1] = v24[vlenb-1], ...,<\/span>\n<span class=\"c1\">#     a0[vlenb*7] = v31[0],           ..., a0[t0-1] = v31[vlenb-1]<\/span>\n<span class=\"c1\"># --&gt; a0[0..t0-1] = [ &#39;g&#39;, &#39;h&#39;, &#39;i&#39;, &#39;j&#39;, &#39;k&#39;, &#39;l&#39;, &#39;m&#39;, &#39;n&#39; ]<\/span>\n<span class=\"nf\">add<\/span><span class=\"w\">  <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t3<\/span><span class=\"w\">           <\/span><span class=\"c1\"># increment dst<\/span>\n<span class=\"nf\">bnez<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">.Loop<\/span><span class=\"w\">            <\/span><span class=\"c1\"># branch to loop head if not equal to zero<\/span>\n<span class=\"nf\">ret<\/span><\/pre>\n<p>The loop and function is left if the complete input buffer is processed.\nNote that while the syntax of most RISC-V instructions follows the destination-source order, store instructions <a href=\"https:\/\/stackoverflow.com\/q\/59802318\/427158\">have this order inverted<\/a>.<\/p>\n<\/section>\n<section id=\"concluding-remarks\">\n<h3><a href=\"#toc-entry-7\">Concluding Remarks<\/a><\/h3>\n<p>The <a href=\"https:\/\/github.com\/riscv\/riscv-v-spec\/releases\/tag\/0.8\">RISC-V &quot;V&quot; vector extension ISA<\/a> is sufficiently diverse as it contain useful bit and byte-shuffling instructions, instructions that allow the masking of elements and instructions implementing operations that are useful for string processing such as element gathering and widening.<\/p>\n<p>The available instructions in combination with the vector length agnostic (VLA) design leads to compact code.\nFor example, each iteration of the main loop just executes 14 instructions and there is no extra code necessary to deal with trailing bytes.<\/p>\n<p>The thus realized throughput is excellent, i.e. the resulting binary code automatically utilizes the complete vector register size on each CPU, be it low or high-end.\nIn addition, the grouping of vector registers allows to increase the throughput since there are many registers available.\nFor example, on a CPU with 128 bit vector registers, the loop has a throughput of 9 digits per instruction.<\/p>\n<p>Since each regular RISC-V instruction encodes into 4 bytes, the density of the assembled binary code is also good.\nFor example, the presented <code>bcd2ascii<\/code> function has a size of 96 bytes.\nWhen enabling the &quot;C&quot; compressed instructions extension during assembling (such that certain instructions can be replaces by compressed 2 byte versions), the size drops by 20 per cent down to 76 bytes.\nWhich is fine, especially given that most instructions of that function are vector ones and there are no compressed variants of the vector instructions.<\/p>\n<p>This can be contrasted by x86-64, where for example the SSSE 3 shuffle instruction encodes into 5 bytes and some moves encode into 7 bytes. Plus of course, the vector length is fixed to 128 bit when using SSSE 3 as lowest common denominator SIMD ISA.<\/p>\n<p>Emitting compressed RISC-V instruction is kind of transparent to the assembly programmer, one has just to set a assembler command line option.\nBut of course, since compressed instructions implement compromises (otherwise why wouldn't be all instructions compressed?!), the programmer has to take care to write instructions in a way such that they are compressible, where possible.\nFor example, some compressed instructions only work on a register subset (e.g. <code>s0..s1, a0..5<\/code>), one source operand is implicit, there are less bits for an immediate operand, there is just one variant that sign-extends the immediate etc.<\/p>\n<p>See also my github repository that contains the <a href=\"https:\/\/github.com\/gsauthof\/riscv\">complete example code<\/a>.<\/p>\n<\/section>\n<\/section>\n<section id=\"getting-started\">\n<h2><a href=\"#toc-entry-8\">Getting Started<\/a><\/h2>\n<p>Since as of early 2020, the &quot;V&quot; vector extension still has draft status and <a href=\"https:\/\/github.com\/riscv\/riscv-v-spec\/releases\/tag\/0.8\">version 0.8<\/a> was just released recently, support for it isn't widely available.\nThat means there is no hardware with a RISC-V &quot;V&quot; CPU available, but also some well-known RISC-V emulators such as Qemu don't support the &quot;V&quot; extension or just support an older version of the &quot;V&quot; extension.\nSimilarly, support for &quot;V&quot; version 0.8 for the standard development toolchain (binutils, gcc) is available, but not yet upstreamed.\nMeaning that one has to hunt down repositories, identify the right branches and compile those with the right flags, instead of just being able to use distro packages.<\/p>\n<p>Another pitfall is that the &quot;V&quot; extension (similar to &quot;F&quot; and &quot;D&quot; floating point extensions) has to be enabled in the running system by setting a status register.\nSince the status register can only be accessed in machine-\/system-mode that means that one also needs kernel support for the &quot;V&quot; extension.<\/p>\n<p>This section details how to build the different components required for a RISC-V &quot;V&quot; 0.8 toolchain and an emulator.<\/p>\n<section id=\"spike\">\n<h3><a href=\"#toc-entry-9\">Spike<\/a><\/h3>\n<p>The <a href=\"https:\/\/github.com\/riscv\/riscv-isa-sim\">Spike RISC-V emulator<\/a> does have &quot;V&quot; version 0.8 support.\nAs of early 2020, there is one other emulator with &quot;V&quot; 0.8 support but it isn't open source.<\/p>\n<p>Building Spike is straight forward:<\/p>\n<pre class=\"m-code\">sudo<span class=\"w\"> <\/span>dnf<span class=\"w\"> <\/span>install<span class=\"w\"> <\/span>dtc<span class=\"w\">  <\/span><span class=\"c1\"># i.e. device-tree-compiler<\/span>\ngit<span class=\"w\"> <\/span>clone<span class=\"w\"> <\/span>https:\/\/github.com\/riscv\/riscv-isa-sim.git<span class=\"w\"> <\/span>--depth<span class=\"w\"> <\/span><span class=\"m\">1<\/span>\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>riscv-isa-sim\nmkdir<span class=\"w\"> <\/span>build\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>build\n..\/configure<span class=\"w\"> <\/span>--prefix<span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/spike\nmake\nmake<span class=\"w\"> <\/span>install<\/pre>\n<p>Of course, the <code>--depth 1<\/code> switch is optional, it just saves some disk space.<\/p>\n<p>Make sure to a have a fresh clone that has &quot;V&quot; support <a href=\"https:\/\/github.com\/riscv\/riscv-isa-sim\/issues\/381\">fixed<\/a>.<\/p>\n<p>By default Spike enables the <code>RV64IMAFDC<\/code> ISAs, but this default can be changed at runtime (or even configure time). For example when we call spike like this:<\/p>\n<pre class=\"m-code\">spike<span class=\"w\"> <\/span>--isa<span class=\"o\">=<\/span>RV64IMAFDCV<span class=\"w\"> <\/span>...\nspike<span class=\"w\"> <\/span>--isa<span class=\"o\">=<\/span>RV64gcV<span class=\"w\">     <\/span>...<span class=\"w\">    <\/span><span class=\"c1\"># equivalent<\/span><\/pre>\n<p>For executing user-space programs such as our example, spike needs the Proxy-Kernel (pk).<\/p>\n<\/section>\n<section id=\"gnu-toolchain\">\n<h3><a href=\"#toc-entry-10\">GNU Toolchain<\/a><\/h3>\n<p>Technically, binutils with &quot;V&quot; extension support is sufficient to assemble our example.\nHowever, building the Proxy-Kernel requires the full <a href=\"https:\/\/github.com\/riscv\/riscv-gnu-toolchain\">GNU toolchain<\/a>.<\/p>\n<pre class=\"m-code\">git<span class=\"w\"> <\/span>clone<span class=\"w\"> <\/span>https:\/\/github.com\/riscv\/riscv-gnu-toolchain.git<span class=\"w\"> <\/span>--branch<span class=\"w\"> <\/span>rvv-0.8.x<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">          <\/span>--single-branch<span class=\"w\"> <\/span>--depth<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>riscv-gnu-toolchain_rvv-0.8.x\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>riscv-gnu-toolchain_rvv-0.8.x\ngit<span class=\"w\"> <\/span>submodule<span class=\"w\"> <\/span>update<span class=\"w\"> <\/span>--init<span class=\"w\"> <\/span>--recursive<span class=\"w\"> <\/span>--depth<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>riscv-binutils<span class=\"w\"> <\/span>riscv-gcc<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">                        <\/span>riscv-glibc<span class=\"w\"> <\/span>riscv-dejagnu<span class=\"w\"> <\/span>riscv-newlib<span class=\"w\"> <\/span>riscv-gdb\nmkdir<span class=\"w\"> <\/span>build\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>build\n..\/configure<span class=\"w\"> <\/span>--prefix<span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/gnu<span class=\"w\"> <\/span>--enable-multilib\nmake\nmake<span class=\"w\"> <\/span>install<\/pre>\n<p>The explicit submodule update is done like this to skip the optional Qemu module.\nBesides Qemu doesn't supporting the &quot;V&quot; extension, it would also require a deeper clone and take up some disk space and waste some compile time.<\/p>\n<p>Note that the <code>make install<\/code> step is superfluous because the previous <code>make<\/code> call already installs everything.<\/p>\n<\/section>\n<section id=\"proxy-kernel\">\n<h3><a href=\"#toc-entry-11\">Proxy-Kernel<\/a><\/h3>\n<p>The <a href=\"https:\/\/github.com\/riscv\/riscv-pk\">RISC-V Proxy-Kernel (pk)<\/a> implements enough to get a user-space program in Spike running, i.e. including setting up some status registers in machine-mode, switching to user-mode and implementing some syscalls.\nThat means that calling the write syscall to write to stdout then just works in Spike and the text is printed to the console.<\/p>\n<p>The pk needs to be cross-compiled with the GNU Toolchain (see previous Section).<\/p>\n<pre class=\"m-code\">git<span class=\"w\"> <\/span>clone<span class=\"w\"> <\/span>--depth<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>https:\/\/github.com\/riscv\/riscv-pk.git\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>riscv-pk\nmkdir<span class=\"w\"> <\/span>build\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>build\n<span class=\"nv\">PATH<\/span><span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/gnu\/bin:<span class=\"nv\">$PATH<\/span><span class=\"w\"> <\/span>..\/configure<span class=\"w\"> <\/span>--prefix<span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/pk<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">                                                     <\/span>--host<span class=\"o\">=<\/span>riscv64-unknown-elf\n<span class=\"nv\">PATH<\/span><span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/gnu\/bin:<span class=\"nv\">$PATH<\/span><span class=\"w\"> <\/span>make\n<span class=\"nv\">PATH<\/span><span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/gnu\/bin:<span class=\"nv\">$PATH<\/span><span class=\"w\"> <\/span>make<span class=\"w\"> <\/span>install<\/pre>\n<p>Again make sure to get a recent pk clone with <a href=\"https:\/\/github.com\/riscv\/riscv-isa-sim\/issues\/381\">fixed &quot;V&quot; support<\/a>.<\/p>\n<\/section>\n<section id=\"binutils\">\n<h3><a href=\"#toc-entry-12\">Binutils<\/a><\/h3>\n<p>If you already have the GNU Toolchain you can skip this (as it already contains the <a href=\"https:\/\/github.com\/riscv\/riscv-binutils-gdb\">binutils with &quot;V&quot; support<\/a>).\nThis is just relevant if you have obtained the Proxy-Kernel with &quot;V&quot; support in binary form and want to skip building the GNU Toolchain.<\/p>\n<pre class=\"m-code\">git<span class=\"w\"> <\/span>clone<span class=\"w\"> <\/span>https:\/\/github.com\/riscv\/riscv-binutils-gdb.git<span class=\"w\"> <\/span>--branch<span class=\"w\"> <\/span>rvv-0.8.x<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">          <\/span>--single-branch<span class=\"w\"> <\/span>--depth<span class=\"w\"> <\/span><span class=\"m\">1<\/span><span class=\"w\"> <\/span>risv-binutils-gdb_rvv-0.8.x\nmkdir<span class=\"w\"> <\/span>build\n<span class=\"nb\">cd<\/span><span class=\"w\"> <\/span>build\n..\/configure<span class=\"w\"> <\/span>--prefix<span class=\"o\">=<\/span><span class=\"nv\">$HOME<\/span>\/local\/riscvv08\/binutils<span class=\"w\"> <\/span>--target<span class=\"w\"> <\/span>riscv64-unknown-elf<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">             <\/span>--enable-multilib\nmake\nmake<span class=\"w\"> <\/span>install<\/pre>\n<\/section>\n<section id=\"assembling\">\n<h3><a href=\"#toc-entry-13\">Assembling<\/a><\/h3>\n<p>Finally, to actually execute our example, a small test program is needed that calls the <code>bcd2ascii()<\/code> function with some sample input and prints the results.\nIf the complete GNU toolchain is available the simplest thing is to write that part in C, e.g.:<\/p>\n<pre class=\"m-code\"><span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;stddef.h&gt;<\/span>\n\n<span class=\"kt\">void<\/span><span class=\"w\"> <\/span><span class=\"nf\">bcd2ascii<\/span><span class=\"p\">(<\/span><span class=\"kt\">void<\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">dst<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">void<\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"n\">src<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"kt\">size_t<\/span><span class=\"w\"> <\/span><span class=\"n\">n<\/span><span class=\"p\">);<\/span>\n\n<span class=\"k\">static<\/span><span class=\"w\"> <\/span><span class=\"k\">const<\/span><span class=\"w\"> <\/span><span class=\"kt\">unsigned<\/span><span class=\"w\"> <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">inp<\/span><span class=\"p\">[]<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"mh\">0x01<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x23<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x45<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x67<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x89<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xab<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xcd<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xef<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">    <\/span><span class=\"mh\">0xfe<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xdc<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xba<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x98<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x76<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x54<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x32<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x10<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">    <\/span><span class=\"mh\">0x01<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x23<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x45<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x67<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x89<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xab<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xcd<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xef<\/span><span class=\"p\">,<\/span>\n<span class=\"w\">    <\/span><span class=\"mh\">0xfe<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xdc<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0xba<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x98<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x76<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x54<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x32<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mh\">0x10<\/span>\n<span class=\"p\">};<\/span>\n\n<span class=\"cp\">#include<\/span><span class=\"w\"> <\/span><span class=\"cpf\">&lt;stdio.h&gt;<\/span>\n\n<span class=\"kt\">int<\/span><span class=\"w\"> <\/span><span class=\"nf\">main<\/span><span class=\"p\">()<\/span>\n<span class=\"p\">{<\/span>\n<span class=\"w\">    <\/span><span class=\"kt\">char<\/span><span class=\"w\"> <\/span><span class=\"n\">out<\/span><span class=\"p\">[<\/span><span class=\"k\">sizeof<\/span><span class=\"w\"> <\/span><span class=\"n\">inp<\/span><span class=\"w\"> <\/span><span class=\"o\">*<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"w\"> <\/span><span class=\"o\">+<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"p\">]<\/span><span class=\"w\"> <\/span><span class=\"o\">=<\/span><span class=\"w\"> <\/span><span class=\"p\">{<\/span><span class=\"mi\">0<\/span><span class=\"p\">};<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ expected output:<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">\/\/ out = { &#39;0&#39;, &#39;1&#39;, &#39;2&#39;, &#39;3&#39;, ... }<\/span>\n\n<span class=\"w\">    <\/span><span class=\"n\">bcd2ascii<\/span><span class=\"p\">(<\/span><span class=\"n\">out<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"n\">inp<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"k\">sizeof<\/span><span class=\"w\"> <\/span><span class=\"n\">inp<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"n\">puts<\/span><span class=\"p\">(<\/span><span class=\"n\">out<\/span><span class=\"p\">);<\/span>\n<span class=\"w\">    <\/span><span class=\"k\">return<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"p\">;<\/span>\n<span class=\"p\">}<\/span><\/pre>\n<p>Everything can then be cross-assembled, cross-compiled and linked with:<\/p>\n<pre class=\"m-code\">~\/local\/riscvv08\/gnu\/bin\/riscv64-unknown-elf-as<span class=\"w\"> <\/span>-march<span class=\"o\">=<\/span>rv64gcv<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>bcd2ascii.o<span class=\"w\"> <\/span>bcd2ascii.s\n~\/local\/riscvv08\/gnu\/bin\/riscv64-unknown-elf-gcc<span class=\"w\"> <\/span>-Wall<span class=\"w\">  <\/span>main_bcd2a.c<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>bcd2a<span class=\"w\"> <\/span>bcd2ascii.o<\/pre>\n<p>Supplying just <code>-march=rv64gv<\/code> disables the use of compressed instructions.<\/p>\n<p>Alternatively, without a C cross compiler but cross binutils, we need an assembly test program such as:<\/p>\n<pre class=\"m-code\"><span class=\"w\">    <\/span><span class=\"na\">.text<\/span><span class=\"w\">                     <\/span><span class=\"c1\"># Start text section<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.balign<\/span><span class=\"w\"> <\/span><span class=\"mi\">4<\/span><span class=\"w\">                 <\/span><span class=\"c1\"># align 4 byte instructions by 4 bytes<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.global<\/span><span class=\"w\"> <\/span><span class=\"no\">_start<\/span><span class=\"w\">            <\/span><span class=\"c1\"># global<\/span>\n<span class=\"nl\">_start:<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># check if vector extension is enabled<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># user-mode doesn&#39;t have privileges to<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># read mstatus\/sstatus\/misa CSRs<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># thus, unclear how to check for V support<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">t1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x1800000<\/span><span class=\"w\">       <\/span><span class=\"c1\"># disable this check for now<\/span>\n<span class=\"w\">    <\/span><span class=\"c1\">#csrr  t1, mstatus        # control and status register, i.e. read the<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># mstatus register<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">t2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"no\">b11<\/span><span class=\"w\">            <\/span><span class=\"c1\"># load immediate mask<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">slli<\/span><span class=\"w\">  <\/span><span class=\"no\">t2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">23<\/span><span class=\"w\">          <\/span><span class=\"c1\"># shift left logical immediate by 23 bits<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># because &quot;V&quot; draft 0.8 defines the vector<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># context status field VS as mstatus[24:23]<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># (0b00 -&gt; off, 0b01 -&gt; initial, 0b10 -&gt; clean,<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\">#  0b11 -&gt; dirty)<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">and<\/span><span class=\"w\">   <\/span><span class=\"no\">t3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">t2<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">beqz<\/span><span class=\"w\">  <\/span><span class=\"no\">t3<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">v_disabled_error<\/span>\n\n<span class=\"w\">                              <\/span><span class=\"c1\"># Prepare calling bcd2ascii()<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">addi<\/span><span class=\"w\">  <\/span><span class=\"no\">sp<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">sp<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">-68<\/span><span class=\"w\">         <\/span><span class=\"c1\"># grow stack by 64+4 bytes, some additional<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># space but keep it 4 byte aligned<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">mv<\/span><span class=\"w\">    <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">sp<\/span><span class=\"w\">              <\/span><span class=\"c1\"># store output on stack<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">lui<\/span><span class=\"w\">   <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%hi<\/span><span class=\"p\">(<\/span><span class=\"no\">inp<\/span><span class=\"p\">)<\/span><span class=\"w\">        <\/span><span class=\"c1\"># load start address of<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">addi<\/span><span class=\"w\">  <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%lo<\/span><span class=\"p\">(<\/span><span class=\"no\">inp<\/span><span class=\"p\">)<\/span><span class=\"w\">    <\/span><span class=\"c1\"># the input string<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">32<\/span><span class=\"w\">              <\/span><span class=\"c1\"># load immediate: sizeof inp<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">call<\/span><span class=\"w\">  <\/span><span class=\"no\">bcd2ascii<\/span><span class=\"w\">           <\/span><span class=\"c1\"># we don&#39;t need to save\/restore our<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># return address because we don&#39;t return ...<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">t0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xa<\/span><span class=\"w\">             <\/span><span class=\"c1\"># load immediate: newline<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">sb<\/span><span class=\"w\">    <\/span><span class=\"no\">t0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">64<\/span><span class=\"p\">(<\/span><span class=\"no\">sp<\/span><span class=\"p\">)<\/span><span class=\"w\">          <\/span><span class=\"c1\"># store byte<\/span>\n<span class=\"w\">                              <\/span><span class=\"c1\"># i.e. terminate output string with &#39;\\n&#39;<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"w\">               <\/span><span class=\"c1\"># stdout<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">mv<\/span><span class=\"w\">    <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">sp<\/span><span class=\"w\">              <\/span><span class=\"c1\"># read output located on the stack<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">65<\/span><span class=\"w\">              <\/span><span class=\"c1\"># i.e. 64+1 characters<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">64<\/span><span class=\"w\">              <\/span><span class=\"c1\"># write syscall number<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">ecall<\/span><span class=\"w\">                     <\/span><span class=\"c1\"># call write(2)<\/span>\n\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0<\/span><span class=\"w\">               <\/span><span class=\"c1\"># set exit status to zero<\/span>\n<span class=\"nl\">exit:<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">93<\/span><span class=\"w\">              <\/span><span class=\"c1\"># exit syscall number<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">ecall<\/span><span class=\"w\">                     <\/span><span class=\"c1\"># call exit(2)<\/span>\n<span class=\"err\">1:<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">j<\/span><span class=\"w\">     <\/span><span class=\"mi\">1<\/span><span class=\"no\">b<\/span><span class=\"w\">                  <\/span><span class=\"c1\"># loop forever in case exit failed ...<\/span>\n\n<span class=\"nl\">v_disabled_error:<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">2<\/span><span class=\"w\">               <\/span><span class=\"c1\"># stderr<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">lui<\/span><span class=\"w\">   <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%hi<\/span><span class=\"p\">(<\/span><span class=\"no\">err_msg<\/span><span class=\"p\">)<\/span><span class=\"w\">    <\/span><span class=\"c1\"># load error message start address<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">addi<\/span><span class=\"w\">  <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a1<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%lo<\/span><span class=\"p\">(<\/span><span class=\"no\">err_msg<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">lui<\/span><span class=\"w\">   <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%hi<\/span><span class=\"p\">(<\/span><span class=\"no\">err_msg_size<\/span><span class=\"p\">)<\/span><span class=\"w\">     <\/span><span class=\"c1\"># load error message size<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">addi<\/span><span class=\"w\">  <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"no\">a2<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"nv\">%lo<\/span><span class=\"p\">(<\/span><span class=\"no\">err_msg_size<\/span><span class=\"p\">)<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a7<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">64<\/span><span class=\"w\">              <\/span><span class=\"c1\"># write syscall number<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">ecall<\/span><span class=\"w\">                     <\/span><span class=\"c1\"># call write(2)<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">li<\/span><span class=\"w\">    <\/span><span class=\"no\">a0<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">1<\/span><span class=\"w\">               <\/span><span class=\"c1\"># load immediate exit argument<\/span>\n<span class=\"w\">    <\/span><span class=\"nf\">j<\/span><span class=\"w\">     <\/span><span class=\"no\">exit<\/span>\n\n\n<span class=\"w\">    <\/span><span class=\"na\">.section<\/span><span class=\"w\"> <\/span><span class=\"no\">.rodata<\/span><span class=\"w\">          <\/span><span class=\"c1\"># Start read-only data section<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.balign<\/span><span class=\"w\"> <\/span><span class=\"mi\">4<\/span><span class=\"w\">                 <\/span><span class=\"c1\"># align to 4 bytes<\/span>\n<span class=\"nl\">inp:<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.byte<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x01<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x23<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x45<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x67<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x89<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xab<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xcd<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xef<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.byte<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xfe<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xdc<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xba<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x98<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x76<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x54<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x32<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x10<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.byte<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x01<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x23<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x45<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x67<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x89<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xab<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xcd<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xef<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.byte<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xfe<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xdc<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0xba<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x98<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x76<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x54<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x32<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"mi\">0x10<\/span>\n<span class=\"nl\">err_msg:<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.string<\/span><span class=\"w\"> <\/span><span class=\"s\">&quot;ERROR: RISC-V &#39;V&#39; vector extension is disabled!\\n&quot;<\/span>\n<span class=\"w\">    <\/span><span class=\"na\">.set<\/span><span class=\"w\"> <\/span><span class=\"no\">err_msg_size<\/span><span class=\"p\">,<\/span><span class=\"w\"> <\/span><span class=\"p\">.<\/span><span class=\"w\"> <\/span><span class=\"p\">-<\/span><span class=\"w\"> <\/span><span class=\"no\">err_msg<\/span><\/pre>\n<p>Cross-assembling and linking everything:<\/p>\n<pre class=\"m-code\">~\/local\/riscvv08\/riscv64-unknown-elf\/bin\/as<span class=\"w\"> <\/span>-march<span class=\"o\">=<\/span>rv64gcv<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>bcd2ascii.o<span class=\"w\"> <\/span>bcd2ascii.s\n~\/local\/riscvv08\/riscv64-unknown-elf\/bin\/as<span class=\"w\"> <\/span>-march<span class=\"o\">=<\/span>rv64gcv<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>start_bcd2a.o<span class=\"w\"> <\/span>start_bcd2a.s\n~\/local\/riscvv08\/riscv64-unknown-elf\/bin\/ld<span class=\"w\"> <\/span>start_bcd2a.o<span class=\"w\"> <\/span>bcd2ascii.o<span class=\"w\"> <\/span>-o<span class=\"w\"> <\/span>bcd2a<\/pre>\n<p>Of course, my repository also contains a <a href=\"https:\/\/github.com\/gsauthof\/riscv\/blob\/master\/makefile\">makefile<\/a> to simplify building the example.<\/p>\n<\/section>\n<section id=\"emulating\">\n<h3><a href=\"#toc-entry-14\">Emulating<\/a><\/h3>\n<p>Example emulating session:<\/p>\n<pre class=\"m-console\"><span class=\"gp\">$ <\/span>~\/local\/riscvv08\/spike\/bin\/spike<span class=\"w\"> <\/span>--isa<span class=\"o\">=<\/span>RV64gcV<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">        <\/span>~\/local\/riscvv08\/riscv64-unknown-elf\/bin\/pk<span class=\"w\"> <\/span>bcd2a\n<span class=\"go\">bbl loader<\/span>\n<span class=\"go\">0123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210<\/span><\/pre>\n<p>Spike also has an interactive mode that allows to step through the instructions, inspect registers etc. For example:<\/p>\n<pre class=\"m-console\"><span class=\"gp\">$ <\/span>~\/local\/riscvv08\/spike\/bin\/spike<span class=\"w\"> <\/span>-d<span class=\"w\"> <\/span>--isa<span class=\"o\">=<\/span>RV64gcV<span class=\"w\"> <\/span><span class=\"se\">\\<\/span>\n<span class=\"w\">        <\/span>~\/local\/riscvv08\/riscv64-unknown-elf\/bin\/pk<span class=\"w\"> <\/span>bcd2a\n<span class=\"go\">: until pc 0 100e2<\/span>\n<span class=\"go\">bbl loader<\/span>\n<span class=\"go\">: vreg 0 8<\/span>\n<span class=\"go\">VLEN=128 bits; ELEN=32 bits<\/span>\n<span class=\"go\">v8  : [3]: 0x00000000  [2]: 0x020ae6a0  [1]: 0x00000000  [0]: 0x020ae630<\/span>\n<span class=\"go\">:<\/span>\n<span class=\"go\">core   0: 0x00000000000100e2 (0x5208a457) vid.v   v8<\/span>\n<span class=\"go\">: vreg 0 8<\/span>\n<span class=\"go\">VLEN=128 bits; ELEN=32 bits<\/span>\n<span class=\"go\">v8  : [3]: 0x0f0e0d0c  [2]: 0x0b0a0908  [1]: 0x07060504  [0]: 0x03020100<\/span>\n<span class=\"go\">: q<\/span><\/pre>\n<p>In comparison with GDB the interactive prompt is a bit spartanic and doesn't really report syntactic errors in the interactive commands, but it's sufficient.\nThe help can be displayed with <code>h<\/code>, <code>&lt;ENTER&gt;<\/code> steps to the next instruction and <code>q<\/code> quits it.<\/p>\n<p>The address <code>100e2<\/code> in the above example session comes from the disassembled <code>bcd2a<\/code> executable (i.e. using objdump).<\/p>\n<\/section>\n<\/section>\n<section id=\"see-also\">\n<h2><a href=\"#toc-entry-15\">See Also<\/a><\/h2>\n<p>The <a href=\"https:\/\/github.com\/riscv\/riscv-v-spec\/releases\/tag\/0.8\">RISC-V &quot;V&quot; vector specification version 0.8<\/a> contains some examples (such as <code>memcpy()<\/code> and SGEMM) in Appendix A and some more interleaved in the main sections.\nFor example, <code>strcpy()<\/code> and <code>strncpy()<\/code> are listed in Section 16 (Vector Mask Instructions), Subsection 16.7, page 76.<\/p>\n<p>Another project of mine contains a heavily commented <a href=\"https:\/\/github.com\/gsauthof\/libxfsx\/blob\/master\/xfsx\/bcd\/decode.hh\">collection of BCD decoding\/encoding functions<\/a> with optimized portable variants (i.e. just requiring C\/C++) down to variants that invoke x86 SIMD vector intrinsics and other x86 specific intrinsics.<\/p>\n<\/section>\n","category":[{"@attributes":{"term":"programming"}},{"@attributes":{"term":"riscv"}},{"@attributes":{"term":"assembly"}},{"@attributes":{"term":"vector"}}]}}