<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>FPGA on jairrs</title><link>https://jairsantana.com/tags/fpga/</link><description>Recent content in FPGA on jairrs</description><generator>Hugo -- 0.156.0</generator><language>en-us</language><atom:link href="https://jairsantana.com/tags/fpga/index.xml" rel="self" type="application/rss+xml"/><item><title>Flock Herder: Interactive Boids on FPGAs</title><link>https://jairsantana.com/portfolio/flock-herder/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://jairsantana.com/portfolio/flock-herder/</guid><description>Stereo vision, fixed-point motion, and a live camera overlay on a pair of FPGAs.</description><content:encoded><![CDATA[<p>This project entailed the construction of an interactive visual program where the user can herd flocks of virtual birds known as &ldquo;boids&rdquo; — on-screen models which behave as birds in a flock, fish in a school, or mammals in a herd — which follow a simple behavioral model based on what each individual on-screen boid can perceive around them. The intended full model includes alignment with nearby flock mates as well as movement towards and around a target in the user&rsquo;s hand.</p>
<p>Updating a singular boid&rsquo;s position requires the normalization of several vectors, which presents the need to develop a resource-efficient and low-latency module for vector normalizations. To acquire the three-dimensional position of the target, we implemented stereo vision using a pair of FPGAs — each with their own camera — which communicate with each other through the SPI protocol. Using the target&rsquo;s positional information, we computed the positions of the boids every frame in three dimensions and overlaid them on the camera footage via a double frame buffer.</p>
<p>The double frame buffer also presented a unique challenge since, due to overlaying the boids over the camera feed, there was a limited amount of additional pixels/DRAM requests that could be made every frame, amounting to roughly 25% of the screen&rsquo;s area.</p>
<p>For this class project, we committed to having multiple boids follow the target, moving every frame and being rendered at their computed positions. To demonstrate proper depth calculation, we modified every boid&rsquo;s size based on its depth. The implemented boid update module includes only the target-following force; alignment, cohesion, and separation remain future work.</p>
<figure>
    <img loading="lazy" src="/images/flock-herder/stereo-setup.png"
         alt="Physical setup showing a main FPGA and a stereo FPGA, each with a camera pointed at a shared tracking object"/> <figcaption>
            <p>The physical setup: two cameras attached to a pair of Urbana FPGAs.</p>
        </figcaption>
</figure>

<h2 id="contributions">Contributions</h2>
<p>This was a final project for MIT&rsquo;s 6.2050 Digital Systems Laboratory, built with Sreeja Thipireddy. I was responsible for the implementation of the normalizer, boid manager, and boid update modules. Sreeja was responsible for the implementation of the inter-FPGA communication protocol, depth calculation module, double frame buffer, and modifications to the traffic generator. The renderer module was implemented in collaboration.</p>
<h2 id="vector-normalization">Vector Normalization</h2>
<p>To compute the necessary vectors in the boid computations, it is necessary to normalize vectors. To normalize a vector, it&rsquo;s sufficient to multiply the vector&rsquo;s components by the reciprocal of the vector&rsquo;s magnitude.</p>
<p>For a vector <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="bold">v</mi><mo>=</mo><mo stretchy="false">(</mo><msub><mi>v</mi><mi>x</mi></msub><mo separator="true">,</mo><msub><mi>v</mi><mi>y</mi></msub><mo separator="true">,</mo><msub><mi>v</mi><mi>z</mi></msub><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">\mathbf{v} = (v_x, v_y, v_z)</annotation></semantics></math></span>, we first compute its squared magnitude:</p>
<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>x</mi><mo>=</mo><msubsup><mi>v</mi><mi>x</mi><mn>2</mn></msubsup><mo>+</mo><msubsup><mi>v</mi><mi>y</mi><mn>2</mn></msubsup><mo>+</mo><msubsup><mi>v</mi><mi>z</mi><mn>2</mn></msubsup><mo separator="true">,</mo><mspace width="2em"/><mover accent="true"><mi mathvariant="bold">v</mi><mo>^</mo></mover><mo>=</mo><mi mathvariant="bold">v</mi><mfrac><mn>1</mn><msqrt><mi>x</mi></msqrt></mfrac><mi mathvariant="normal">.</mi></mrow><annotation encoding="application/x-tex">
x = v_x^2 + v_y^2 + v_z^2, \qquad \hat{\mathbf{v}} = \mathbf{v}\frac{1}{\sqrt{x}}.
</annotation></semantics></math></span><p>This lets us compute one reciprocal square root and reuse it for all three components.</p>
<h3 id="initial-approximation">Initial Approximation</h3>
<p>We can compute an initial approximation to the reciprocal of the square root via the position of the number&rsquo;s most significant bit.</p>
<p>Using <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><mo>≈</mo><msup><mn>2</mn><mi>b</mi></msup></mrow><annotation encoding="application/x-tex">x \approx 2^b</annotation></semantics></math></span>, we obtain the initial approximation
</p>
<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mfrac><mn>1</mn><msqrt><mi>x</mi></msqrt></mfrac><mo>≈</mo><msup><mn>2</mn><mrow><mo>−</mo><mi>b</mi><mi mathvariant="normal">/</mi><mn>2</mn></mrow></msup><mi mathvariant="normal">.</mi></mrow><annotation encoding="application/x-tex">
\frac{1}{\sqrt{x}} \approx 2^{-b/2}.
</annotation></semantics></math></span><p>For a nonzero integer <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span>, the position <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi></mrow><annotation encoding="application/x-tex">b</annotation></semantics></math></span> of its most significant bit is
</p>
<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mi>b</mi><mo>=</mo><mrow><mi mathvariant="normal">B</mi><mi mathvariant="normal">I</mi><mi mathvariant="normal">T</mi><mi mathvariant="normal">_</mi><mi mathvariant="normal">C</mi><mi mathvariant="normal">O</mi><mi mathvariant="normal">U</mi><mi mathvariant="normal">N</mi><mi mathvariant="normal">T</mi></mrow><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mo>−</mo><mn>1</mn><mo>−</mo><mrow><mi mathvariant="normal">C</mi><mi mathvariant="normal">L</mi><mi mathvariant="normal">Z</mi></mrow><mo stretchy="false">(</mo><mi>x</mi><mo stretchy="false">)</mo><mi mathvariant="normal">.</mi></mrow><annotation encoding="application/x-tex">
b = \mathrm{BIT\_COUNT}(x) - 1 - \mathrm{CLZ}(x).
</annotation></semantics></math></span><p>I implemented a custom combinational module with a tree-based approach which computes the count of leading zeroes. The result for any n-bit number can be derived from the result of its left and right halves. If the upper half of the number contains a 1 bit, the result comes from that half. Otherwise, the result is the width of the upper half plus the leading-zero count of the lower half. This gives us a combinational tree for finding the CLZ. The implementation starts with 32 two-bit pairs: <code>00</code> has two leading zeroes, <code>01</code> has one, and a pair starting with <code>1</code> has none. Five merge layers combine these counts into the result for the full 64-bit input. In <code>clz_merge</code>, the highest bit indicates that a whole block is zero; the remaining bits encode its leading-zero count.
<details class="article-details">
    <summary>Code: tree-structured leading-zero counter</summary>
    <div class="article-details-content">
        <p>From <code>main/hdl/vec.sv</code>. The source references this  <a href="https://electronics.stackexchange.com/questions/196914/verilog-synthesize-high-speed-leading-zero-count">leading-zero count tree discussion</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-systemverilog" data-lang="systemverilog"><span style="display:flex;"><span><span style="color:#ff7b72">module</span> clz_merge #(
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">parameter</span> WIDTH
</span></span><span style="display:flex;"><span>) (
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">input</span> <span style="color:#ff7b72">wire</span> [WIDTH<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">0</span>] left,
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">input</span> <span style="color:#ff7b72">wire</span> [WIDTH<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">0</span>] right,
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">output</span> <span style="color:#ff7b72">logic</span> [<span style="color:#79c0ff;font-weight:bold">WIDTH:</span><span style="color:#a5d6ff">0</span>] out
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">logic</span> both_full, left_not_full;
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">assign</span> both_full <span style="color:#ff7b72;font-weight:bold">=</span> left[WIDTH<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span>] <span style="color:#ff7b72;font-weight:bold">&amp;</span> right[WIDTH<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span>];
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">assign</span> left_not_full <span style="color:#ff7b72;font-weight:bold">=</span> left[WIDTH<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span>] <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">1</span><span style="color:#a5d6ff">&#39;b0</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">logic</span> [WIDTH<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">2</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">0</span>] right_chopped;
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">assign</span> right_chopped <span style="color:#ff7b72;font-weight:bold">=</span> right[WIDTH<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">2</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">0</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">logic</span> [<span style="color:#79c0ff;font-weight:bold">WIDTH:</span><span style="color:#a5d6ff">0</span>] result;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">always_comb</span> <span style="color:#ff7b72">begin</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">if</span>(both_full) <span style="color:#ff7b72">begin</span>
</span></span><span style="display:flex;"><span>            result <span style="color:#ff7b72;font-weight:bold">=</span> {<span style="color:#a5d6ff">1</span><span style="color:#a5d6ff">&#39;b1</span>, {WIDTH{<span style="color:#a5d6ff">1</span><span style="color:#a5d6ff">&#39;b0</span>}}};
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">end</span> <span style="color:#ff7b72">else</span> <span style="color:#ff7b72">if</span>(left_not_full) <span style="color:#ff7b72">begin</span>
</span></span><span style="display:flex;"><span>            result <span style="color:#ff7b72;font-weight:bold">=</span> {<span style="color:#a5d6ff">1</span><span style="color:#a5d6ff">&#39;b0</span>, left};
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">end</span> <span style="color:#ff7b72">else</span> <span style="color:#ff7b72">begin</span>
</span></span><span style="display:flex;"><span>            result <span style="color:#ff7b72;font-weight:bold">=</span> {<span style="color:#a5d6ff">2</span><span style="color:#a5d6ff">&#39;b01</span>, right_chopped};
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">end</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">assign</span> out <span style="color:#ff7b72;font-weight:bold">=</span> result;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">endmodule</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">module</span> clz_64 (
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">input</span> <span style="color:#ff7b72">wire</span> [<span style="color:#a5d6ff">63</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">0</span>] data,
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">output</span> <span style="color:#ff7b72">logic</span> [<span style="color:#a5d6ff">6</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">0</span>] out
</span></span><span style="display:flex;"><span>);
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">logic</span> [<span style="color:#a5d6ff">1</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">0</span>] bit_pair [<span style="color:#a5d6ff">31</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">0</span>];
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">logic</span> [<span style="color:#a5d6ff">6</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">0</span>] assembly_layer [<span style="color:#a5d6ff">5</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">0</span>] [<span style="color:#a5d6ff">31</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">0</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">genvar</span> j;
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">generate</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">for</span> (j <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">0</span>; j <span style="color:#ff7b72;font-weight:bold">&lt;</span> <span style="color:#a5d6ff">32</span>; j<span style="color:#ff7b72;font-weight:bold">++</span>) <span style="color:#ff7b72">begin</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">assign</span> bit_pair[j] <span style="color:#ff7b72;font-weight:bold">=</span> data[<span style="color:#a5d6ff">2</span><span style="color:#ff7b72;font-weight:bold">*</span>j<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">2</span><span style="color:#ff7b72;font-weight:bold">*</span>j];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">assign</span> assembly_layer[<span style="color:#a5d6ff">0</span>][j] <span style="color:#ff7b72;font-weight:bold">=</span>
</span></span><span style="display:flex;"><span>                (bit_pair[j] <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">2</span><span style="color:#a5d6ff">&#39;b00</span>) <span style="color:#ff7b72;font-weight:bold">?</span> <span style="color:#a5d6ff">6</span><span style="color:#a5d6ff">&#39;b000010</span> <span style="color:#ff7b72;font-weight:bold">:</span>
</span></span><span style="display:flex;"><span>                (bit_pair[j] <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">2</span><span style="color:#a5d6ff">&#39;b01</span>) <span style="color:#ff7b72;font-weight:bold">?</span> <span style="color:#a5d6ff">6</span><span style="color:#a5d6ff">&#39;b000001</span> <span style="color:#ff7b72;font-weight:bold">:</span>
</span></span><span style="display:flex;"><span>                                         <span style="color:#a5d6ff">6</span><span style="color:#a5d6ff">&#39;b000000</span> ;
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">end</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">endgenerate</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">genvar</span> layer, i;
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">generate</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">for</span> (layer <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">1</span>; layer <span style="color:#ff7b72;font-weight:bold">&lt;</span> <span style="color:#a5d6ff">6</span>; layer<span style="color:#ff7b72;font-weight:bold">++</span>) <span style="color:#ff7b72">begin</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">for</span> (i <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">0</span>; i <span style="color:#ff7b72;font-weight:bold">&lt;</span> (<span style="color:#a5d6ff">32</span> <span style="color:#ff7b72;font-weight:bold">&gt;&gt;</span> (layer <span style="color:#ff7b72;font-weight:bold">-</span> <span style="color:#a5d6ff">1</span>)); i <span style="color:#ff7b72;font-weight:bold">+=</span> <span style="color:#a5d6ff">2</span>) <span style="color:#ff7b72">begin</span>
</span></span><span style="display:flex;"><span>                clz_merge #(
</span></span><span style="display:flex;"><span>                    .WIDTH(layer <span style="color:#ff7b72;font-weight:bold">+</span> <span style="color:#a5d6ff">1</span>)
</span></span><span style="display:flex;"><span>                ) m_clz_merge (
</span></span><span style="display:flex;"><span>                    .left(assembly_layer[layer <span style="color:#ff7b72;font-weight:bold">-</span> <span style="color:#a5d6ff">1</span>][i <span style="color:#ff7b72;font-weight:bold">+</span> <span style="color:#a5d6ff">1</span>][<span style="color:#79c0ff;font-weight:bold">layer:</span><span style="color:#a5d6ff">0</span>]),
</span></span><span style="display:flex;"><span>                    .right(assembly_layer[layer <span style="color:#ff7b72;font-weight:bold">-</span> <span style="color:#a5d6ff">1</span>][i][<span style="color:#79c0ff;font-weight:bold">layer:</span><span style="color:#a5d6ff">0</span>]),
</span></span><span style="display:flex;"><span>                    .out(assembly_layer[layer][i <span style="color:#ff7b72;font-weight:bold">&gt;&gt;</span> <span style="color:#a5d6ff">1</span>][layer<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span><span style="color:#ff7b72;font-weight:bold">:</span><span style="color:#a5d6ff">0</span>])
</span></span><span style="display:flex;"><span>                );
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">end</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">end</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">endgenerate</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">assign</span> out <span style="color:#ff7b72;font-weight:bold">=</span> assembly_layer[<span style="color:#a5d6ff">5</span>][<span style="color:#a5d6ff">0</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">endmodule</span>
</span></span></code></pre></div>
    </div>
</details>
</p>
<p>The squared components of the vector are shifted right by <code>FP_SHIFT</code> before being added, so <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span> still has <code>FP_SHIFT</code> fractional bits. If <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>F</mi><mo>=</mo><mrow><mi mathvariant="normal">F</mi><mi mathvariant="normal">P</mi><mi mathvariant="normal">_</mi><mi mathvariant="normal">S</mi><mi mathvariant="normal">H</mi><mi mathvariant="normal">I</mi><mi mathvariant="normal">F</mi><mi mathvariant="normal">T</mi></mrow></mrow><annotation encoding="application/x-tex">F = \mathrm{FP\_SHIFT}</annotation></semantics></math></span> and <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>X</mi></mrow><annotation encoding="application/x-tex">X</annotation></semantics></math></span> is the stored squared magnitude, its real value is <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>X</mi><mi mathvariant="normal">/</mi><msup><mn>2</mn><mi>F</mi></msup></mrow><annotation encoding="application/x-tex">X/2^F</annotation></semantics></math></span>. The stored reciprocal-square-root estimate therefore needs the scale factor</p>
<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msup><mn>2</mn><mi>F</mi></msup><mfrac><mn>1</mn><msqrt><mrow><mi>X</mi><mi mathvariant="normal">/</mi><msup><mn>2</mn><mi>F</mi></msup></mrow></msqrt></mfrac><mo>≈</mo><msup><mn>2</mn><mrow><mo stretchy="false">(</mo><mn>3</mn><mi>F</mi><mo>−</mo><mi>b</mi><mo stretchy="false">)</mo><mi mathvariant="normal">/</mi><mn>2</mn></mrow></msup><mi mathvariant="normal">.</mi></mrow><annotation encoding="application/x-tex">
2^F\frac{1}{\sqrt{X/2^F}} \approx 2^{(3F-b)/2}.
</annotation></semantics></math></span><p>The code uses <code>e = (3*FP_SHIFT - b) &gt;&gt; 1</code> for this exponent. It zero-pads the squared magnitude to 64 bits before counting leading zeroes, then uses <code>b = 63 - len_sqr_clz</code> to find its most significant bit. The clocked assignment <code>y_pipe[2] &lt;= (1 &lt;&lt; e) &lt;&lt; FP_SHIFT</code> gives the estimate an extra <code>FP_SHIFT</code> bits, which are removed by <code>y_pipe_shifted</code> before it is used in the next multiplication.</p>
<details class="article-details">
    <summary>Code: fixed-point initial estimate</summary>
    <div class="article-details-content">
        <div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-systemverilog" data-lang="systemverilog"><span style="display:flex;"><span>clz_64 m_clz_64 (
</span></span><span style="display:flex;"><span>        .data({{(<span style="color:#a5d6ff">64</span><span style="color:#ff7b72;font-weight:bold">-</span>DOT_PRODUCT_WIDTH){<span style="color:#a5d6ff">1</span><span style="color:#a5d6ff">&#39;b0</span>}},len_sqr_pipe[<span style="color:#a5d6ff">1</span>]}),
</span></span><span style="display:flex;"><span>        .out(len_sqr_clz)
</span></span><span style="display:flex;"><span>    ); <span style="color:#8b949e;font-style:italic">// 1
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">assign</span> b <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">63</span> <span style="color:#ff7b72;font-weight:bold">-</span> len_sqr_clz; <span style="color:#8b949e;font-style:italic">// 1
</span></span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">assign</span> e <span style="color:#ff7b72;font-weight:bold">=</span> (<span style="color:#a5d6ff">3</span><span style="color:#ff7b72;font-weight:bold">*</span>FP_SHIFT <span style="color:#ff7b72;font-weight:bold">-</span> b) <span style="color:#ff7b72;font-weight:bold">&gt;&gt;</span> <span style="color:#a5d6ff">1</span>; <span style="color:#8b949e;font-style:italic">// 1
</span></span></span></code></pre></div>
    </div>
</details>

<h3 id="newton-raphson-refinement">Newton-Raphson Refinement</h3>
<p>To improve this initial guess, I computed several iterations of a Newton-Raphson approximation scheme. Through a C simulation I found that 4 iterations was enough to achieve visually satisfactory results.</p>
<p>For an estimate <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mi>n</mi></msub></mrow><annotation encoding="application/x-tex">y_n</annotation></semantics></math></span> of <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>1</mn><mi mathvariant="normal">/</mi><msqrt><mi>x</mi></msqrt></mrow><annotation encoding="application/x-tex">1/\sqrt{x}</annotation></semantics></math></span>, the iteration is</p>
<span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><msub><mi>y</mi><mrow><mi>n</mi><mo>+</mo><mn>1</mn></mrow></msub><mo>=</mo><mfrac><msub><mi>y</mi><mi>n</mi></msub><mn>2</mn></mfrac><mrow><mo fence="true">(</mo><mn>3</mn><mo>−</mo><mi>x</mi><msubsup><mi>y</mi><mi>n</mi><mn>2</mn></msubsup><mo fence="true">)</mo></mrow><mi mathvariant="normal">.</mi></mrow><annotation encoding="application/x-tex">
y_{n+1} = \frac{y_n}{2}\left(3 - xy_n^2\right).
</annotation></semantics></math></span><p>At the desired value, <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>x</mi><msubsup><mi>y</mi><mi>n</mi><mn>2</mn></msubsup><mo>=</mo><mn>1</mn></mrow><annotation encoding="application/x-tex">xy_n^2 = 1</annotation></semantics></math></span>, so the correction factor is one. Each iteration employs only multiplication and substraction operations. In fixed-point arithmetic, the products also need to be shifted to account for the fractional bits. I split each iteration across three clock cycles: square the current estimate, multiply by the squared magnitude, and apply the correction. <code>THREE_HALFS</code> stores 1.5 in fixed-point format. The expression <code>half_ay2_pipe[i] &gt;&gt; (2 * FP_SHIFT + 1)</code> accounts for the product&rsquo;s extra fractional bits and divides it by two. The generated stages implement the equivalent form <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msub><mi>y</mi><mrow><mi>n</mi><mo>+</mo><mn>1</mn></mrow></msub><mo>=</mo><msub><mi>y</mi><mi>n</mi></msub><mo stretchy="false">(</mo><mn>1.5</mn><mo>−</mo><mi>x</mi><msubsup><mi>y</mi><mi>n</mi><mn>2</mn></msubsup><mi mathvariant="normal">/</mi><mn>2</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">y_{n+1} = y_n(1.5 - xy_n^2/2)</annotation></semantics></math></span>.
<details class="article-details">
    <summary>Code: three-cycle Newton-Raphson iteration</summary>
    <div class="article-details-content">
        <div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-systemverilog" data-lang="systemverilog"><span style="display:flex;"><span><span style="color:#ff7b72">genvar</span> pipe_idx;
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">generate</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">for</span>(pipe_idx <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">3</span>; pipe_idx <span style="color:#ff7b72;font-weight:bold">&lt;</span> <span style="color:#a5d6ff">3</span> <span style="color:#ff7b72;font-weight:bold">+</span> <span style="color:#a5d6ff">3</span><span style="color:#ff7b72;font-weight:bold">*</span>ITERATIONS; pipe_idx <span style="color:#ff7b72;font-weight:bold">+=</span> <span style="color:#a5d6ff">3</span>) <span style="color:#ff7b72">begin</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">always_ff</span> @( <span style="color:#ff7b72">posedge</span> clk ) <span style="color:#ff7b72">begin</span>
</span></span><span style="display:flex;"><span>                y2_pipe[pipe_idx] <span style="color:#ff7b72;font-weight:bold">&lt;=</span> y_pipe_shifted[pipe_idx<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span>] <span style="color:#ff7b72;font-weight:bold">*</span> y_pipe_shifted[pipe_idx<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span>]; <span style="color:#8b949e;font-style:italic">// 3*i
</span></span></span><span style="display:flex;"><span>                <span style="color:#8b949e;font-style:italic">// y2_pipe[pipe_idx+1] &lt;= y2_pipe[pipe_idx];
</span></span></span><span style="display:flex;"><span>                <span style="color:#8b949e;font-style:italic">// y2_pipe[pipe_idx+2] &lt;= y2_pipe[pipe_idx+1];
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#8b949e;font-style:italic">// half_ay2_pipe[pipe_idx] &lt;= half_ay2_pipe[pipe_idx-1];
</span></span></span><span style="display:flex;"><span>                half_ay2_pipe[pipe_idx<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>] <span style="color:#ff7b72;font-weight:bold">&lt;=</span> (len_sqr_pipe[pipe_idx] <span style="color:#ff7b72;font-weight:bold">*</span> y2_pipe[pipe_idx]); <span style="color:#8b949e;font-style:italic">// 3*i + 1
</span></span></span><span style="display:flex;"><span>                <span style="color:#8b949e;font-style:italic">// half_ay2_pipe[pipe_idx+2] &lt;= half_ay2_pipe[pipe_idx+1];
</span></span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                y_pipe[pipe_idx] <span style="color:#ff7b72;font-weight:bold">&lt;=</span> y_pipe[pipe_idx<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span>];
</span></span><span style="display:flex;"><span>                y_pipe[pipe_idx<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>] <span style="color:#ff7b72;font-weight:bold">&lt;=</span> y_pipe[pipe_idx];
</span></span><span style="display:flex;"><span>                y_pipe[pipe_idx<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">2</span>] <span style="color:#ff7b72;font-weight:bold">&lt;=</span> ($signed(y_pipe_shifted[pipe_idx<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>]) <span style="color:#ff7b72;font-weight:bold">*</span> ($signed({<span style="color:#a5d6ff">1</span><span style="color:#a5d6ff">&#39;b0</span>, THREE_HALFS}) <span style="color:#ff7b72;font-weight:bold">-</span> $signed({<span style="color:#a5d6ff">1</span><span style="color:#a5d6ff">&#39;b0</span>, half_ay2_pipe_shifted[pipe_idx<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>]}))); <span style="color:#8b949e;font-style:italic">// 3*i + 2
</span></span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">end</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">end</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">endgenerate</span>
</span></span></code></pre></div>
    </div>
</details>
</p>
<h3 id="precision-and-pipeline-cost">Precision and Pipeline Cost</h3>
<p>In the evaluation described in our final report, using an 11-bit integer range and an <code>FP_SHIFT</code> of 12, the computed vectors were within 10% of the desired vectors, which was enough accuracy for our application. This is an observed result for the evaluated inputs rather than a guarantee for every fixed-point input; sufficiently small vectors can have their squared components truncated to zero.</p>
<p>When synthesized for an 11-bit integer range and 12 bits of precision, the normalizer consumes 48 DSP slices. The latency of the normalizer is <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>3</mn><mo>+</mo><mn>3</mn><mi>I</mi></mrow><annotation encoding="application/x-tex">3 + 3I</annotation></semantics></math></span>, where <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>I</mi></mrow><annotation encoding="application/x-tex">I</annotation></semantics></math></span> is the number of iterations used for the inverse square root solver. While it is fully pipelined, all multiplications are assumed to take one cycle, which limits the range and precision of the input vectors due to the volume of operations required between frames. Too high a latency and we would not be able to finish computing updates for all boids.</p>
<h2 id="boid-computations">Boid Computations</h2>
<p>Every boid is associated with a position and velocity vector. Every frame, we add the velocity vector multiplied by a small <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi mathvariant="normal">Δ</mi><mi>t</mi></mrow><annotation encoding="application/x-tex">\Delta t</annotation></semantics></math></span> to its position. The intended full behavioral model calculates the acceleration of the boid based on:</p>
<ol>
<li>Normalized offset to the target&rsquo;s position. Used in computing the target force.</li>
<li>Average direction of other boids in the perception radius. Also known as the average flock heading. Used in computing the alignment force.</li>
<li>Normalized offset to the average position of other boids in the perception radius. Also known as the flock center. Used in computing the cohesion force.</li>
<li>Average of the negative offset to the position of other boids. Also known as the separation heading. In essence, this force makes sure that boids don&rsquo;t fly over each other. Used in computing the separation force.</li>
</ol>
<p>In the full model, each of the above results is computed into a steering force by subtracting the boid&rsquo;s velocity from the influencing vector. These results are then clamped to have a maximum magnitude equal to a constant maximum steering force. These contributions to the acceleration are then summed and added to the boid&rsquo;s velocity.</p>
<p>The velocity&rsquo;s magnitude is then clamped between a constant minimum and maximum speed. This new velocity is then used next frame to update the boid&rsquo;s position.</p>
<h3 id="boid-update">Boid Update</h3>
<p>The <code>boid_update</code> module computes the boid&rsquo;s new velocity, which is passed back to the <code>boid_manager</code> module. In the submitted implementation, it only implemented the first of the four steering forces, which is responsible for leading the boid towards the target. To do this, it implements an FSM comprised of the following states:</p>
<ol>
<li><code>IDLE</code></li>
<li><code>NORMALIZE_OFFSET</code></li>
<li><code>GET_STEER</code></li>
<li><code>NORMALIZE_STEER_TARGET</code></li>
<li><code>CLAMP_STEER</code></li>
<li><code>GET_ACCELERATION</code></li>
<li><code>GET_VELOCITY</code></li>
<li><code>NORMALIZE_VELOCITY</code></li>
<li><code>CLAMP_VELOCITY</code></li>
</ol>
<p>Of relevance here is that, to clamp a vector between given minimum and maximum magnitudes, it is necessary to normalize it beforehand. Due to resource limitations, it was impossible to instantiate a separate normalizer module for each of these operations, so instead we opted for a blocking implementation during which the <code>boid_update</code> module changes the inputs to its normalizer submodule and waits for results.</p>
<p>To implement the rest of the steering forces, it would then be necessary to add stages for the computation of the necessary data as well as the necessary normalization stages before each steering force computation and subsequent clamping of the output value.</p>
<p>Our final report recorded a 51-cycle latency for <code>boid_update</code> and estimated at least 19,000 boid updates per frame on the pixel clock, including <code>boid_manager</code> overhead. In the supplied source version, the FSM takes 56 cycles after accepting an update request, before manager overhead: three 16-cycle normalization states, three two-cycle steering or clamping states, and two single-cycle states. The report&rsquo;s throughput estimate therefore needs to be recalculated for this version.</p>
<h3 id="boid-manager">Boid Manager</h3>
<p>The <code>boid_manager</code> module responsible for passing the necessary parameters to <code>boid_update</code> for every boid implements an FSM comprised of the following states:</p>
<ol>
<li><code>IDLE</code></li>
<li><code>POS_UPDATE</code></li>
<li><code>POS_UPDATE2</code></li>
<li><code>UPDATE_START</code></li>
<li><code>UPDATE_WAIT</code></li>
</ol>
<p>During the <code>IDLE</code> state, the module awaits the <code>new_frame</code> signal before saving the currently inputted target coordinates and transitioning to the <code>POS_UPDATE</code> state. In this state, the module updates all boid positions by adding their respective velocities scaled by a small positive number. Then, during <code>POS_UPDATE2</code>, all positions are boundary-checked and wrapped around the screen if necessary.</p>
<p>During <code>UPDATE_START</code>, the manager asserts the <code>data_in_valid</code> signal to the <code>boid_update</code> module and then transitions to the waiting state, where the current boid index is incremented after the <code>boid_update</code> module&rsquo;s <code>busy</code> signal goes low. This continues until all boids&rsquo; velocities have been updated, at which point the module returns to the <code>IDLE</code> state.</p>
<h2 id="design-evaluation">Design Evaluation</h2>
<h3 id="double-frame-buffer-and-rendering-bottleneck">Double Frame Buffer and Rendering Bottleneck</h3>
<p>The objective of the submitted implementation was to be able to render as many boids as possible and have them change sizes appropriately. Writing camera pixels into the frame buffer consumes most of the frame time, and the system cannot write an amount of boid pixels that surpasses the amount of cycles in the remaining frame time. This amount of pixels is roughly equal to the amount of cycles in the v-sync period.</p>
<p>This upper bounds the size and quantity of the boids by the amount of pixels for each boid when they&rsquo;re closest to the screen. In the configuration evaluated in our final report, we found that we could comfortably render 16 boids as 96 × 96 squares — or 147,456 pixels — whilst remaining under the frame time constraint. This measurement differs from the supplied renderer&rsquo;s current settings, which produce nominal square sizes of 4–49 pixels before eight-pixel width packing. This was the design&rsquo;s bottleneck and would be the first area to be improved upon in the next iteration as both the depth calculation and boid updates can handle increased throughput.</p>
<p>Additionally, we could cut off the additional pixels sent via the eight-pixel packing by modifying the strobe signal to the DDR3 controller to only write pixels included in the square region. This would remove the current rendering artifact where box widths change only in increments of eight.</p>
<h3 id="communication-protocol-and-depth-calculation">Communication Protocol and Depth Calculation</h3>
<p>Since the communication protocol only needs to communicate 22 bits for the center of mass information every frame regardless of any changes to the rest of the design, it would not need any changes or improvements. Similarly, the depth calculation module deals with known constants and is mostly a wrapper for a fixed-point divide. Even if a different camera were to be used, it would only require constant values and/or bit widths to be changed in this module.</p>
<h3 id="vector-normalization-and-boid-updates">Vector Normalization and Boid Updates</h3>
<p>The biggest source of resource allocation in the design comes from the normalizer instanced by the <code>boid_update</code> module. Forty-eight of the sixty DSP slices in the module come from the normalizer instance. Most of these come from the pipelined Newton-Raphson iterations, which instance multiplications between numbers with result bit widths of up to <span class="katex"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mn>4</mn><mtext> </mtext><mrow><mi mathvariant="normal">F</mi><mi mathvariant="normal">P</mi><mi mathvariant="normal">_</mi><mi mathvariant="normal">R</mi><mi mathvariant="normal">A</mi><mi mathvariant="normal">N</mi><mi mathvariant="normal">G</mi><mi mathvariant="normal">E</mi></mrow><mo>+</mo><mn>3</mn><mtext> </mtext><mrow><mi mathvariant="normal">F</mi><mi mathvariant="normal">P</mi><mi mathvariant="normal">_</mi><mi mathvariant="normal">S</mi><mi mathvariant="normal">H</mi><mi mathvariant="normal">I</mi><mi mathvariant="normal">F</mi><mi mathvariant="normal">T</mi></mrow><mo>+</mo><mn>2</mn></mrow><annotation encoding="application/x-tex">4\,\mathrm{FP\_RANGE} + 3\,\mathrm{FP\_SHIFT} + 2</annotation></semantics></math></span>. This results in timing and resource utilization issues for higher integer ranges and precision widths.</p>
<p>This problem could be solved by splitting each multiplication into different steps — each of them fitting into a single DSP slice. This would drastically reduce resource utilization and improve throughput at the cost of increased latency. Increased latency, however, directly influences the amount of boids that can be updated every frame due to the boid manager&rsquo;s blocking implementation of updates and the choice to instance only one normalizer for <code>boid_update</code>.</p>
<p><a href="https://github.mit.edu/6205F25/fa25-6205-team28">Project source (MIT GitHub)</a></p>
]]></content:encoded></item></channel></rss>