<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Parth Sinha — Notes]]></title><description><![CDATA[Parth Sinha — Notes]]></description><link>https://eduparthsinha.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 13 Nov 2026 19:32:23 GMT</lastBuildDate><atom:link href="https://eduparthsinha.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[What's inside an FPGA, explained with a Tang Nano 9K]]></title><description><![CDATA[How an FPGA is built from IOBs, CLBs, LUTs and configuration flash, using the Tang Nano 9K and a Verilog blinker as the running example.]]></description><link>https://eduparthsinha.hashnode.dev/what-s-inside-an-fpga-explained-with-a-tang-nano-9k</link><guid isPermaLink="true">https://eduparthsinha.hashnode.dev/what-s-inside-an-fpga-explained-with-a-tang-nano-9k</guid><category><![CDATA[fpga]]></category><category><![CDATA[verilog]]></category><category><![CDATA[hardware]]></category><category><![CDATA[embedded]]></category><category><![CDATA[Electronics]]></category><dc:creator><![CDATA[Parth Sinha]]></dc:creator><pubDate>Fri, 13 Nov 2026 12:59:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6af6f3d373c3f84def790d5b/c0ab62ae-e537-4fb4-99b7-1ccb59d15347.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Coming to FPGAs from an Arduino or ESP32, the hardest part is the change in mindset. You aren't writing a program for a CPU to run one line at a time. You're describing a circuit, and the tools build it out of blocks that are already sitting on the chip. I wrote up notes on what those blocks are, partly from EEVblog's "What Is An FPGA?" video, with the Tang Nano 9K as the running example.</p>
<h3>The blocks</h3>
<p>IOBs (input/output blocks) sit behind each physical pin and form a ring around the edge of the chip. They set the pin's direction, drive strength, slew rate and voltage standard, and each one has its own flip-flops, so a signal can be latched right at the pin. Drive strength isn't voltage, though. A 3.3 V pin driving a 5 V part like a WS2812 LED strip still needs a level shifter.</p>
<p>CLBs (configurable logic blocks) are where the logic happens, and there are no AND or OR gates in them as such. A LUT is a tiny truth table that stores the output for every input combination, so one LUT4 can implement any boolean function of four inputs. Next to the LUTs are D flip-flops for state and a carry chain that lets adders ripple between neighbouring cells without going out to the slower general routing.</p>
<p>All of that configuration lives in SRAM, which forgets everything when the power goes off. The compiled bitstream is kept in flash and loaded into the SRAM at power-up. The Tang Nano 9K's Gowin GW1NR-9 has the flash built in, so a design is running almost as soon as the board gets power.</p>
<h3>What a blinker turns into</h3>
<pre><code class="language-verilog">module led_blink (
    input  wire clk,
    output reg  led
);
    reg [23:0] counter = 0;

    always @(posedge clk) begin
        counter &lt;= counter + 1'b1;
        led     &lt;= counter[23];
    end
endmodule
</code></pre>
<p>When the tools synthesize this, <code>counter</code> becomes 24 flip-flops, the <code>+ 1'b1</code> becomes LUTs plus the carry chain, and <code>led</code> is a register driving a pin, so it can go in that pin's IOB flip-flop. Which pin, and at what voltage, comes from a separate constraints file.</p>
<p>The post also lists the GW1NR-9's resources (8,640 LUT4s, 6,480 flip-flops, 26 block RAMs and 10 DSP multipliers), links the two Gowin documents I used, and looks at what has changed since that EEVblog video came out in 2013: power, $15 to $20 boards, free and open-source tools such as Yosys and nextpnr, and hard IP in the silicon.</p>
<p><a href="https://edu.parthsinha.com/posts/understanding-fpga/">https://edu.parthsinha.com/posts/understanding-fpga/</a></p>
<p>I later used the same board to build a small RISC-V computer, written up as a five-part series starting here: <a href="https://edu.parthsinha.com/posts/riscv-soc-1-toolchain-and-blink/">https://edu.parthsinha.com/posts/riscv-soc-1-toolchain-and-blink/</a></p>
<p>If you came to FPGAs from microcontrollers, what took you longest to unlearn?</p>
]]></content:encoded></item><item><title><![CDATA[I built a small RISC-V computer inside a Tang Nano 9K FPGA]]></title><description><![CDATA[A PicoRV32 CPU, a hand-written UART, a 46× hardware accelerator and an HDMI console on a Tang Nano 9K, built with open-source tools.]]></description><link>https://eduparthsinha.hashnode.dev/i-built-a-small-risc-v-computer-inside-a-tang-nano-9k-fpga</link><guid isPermaLink="true">https://eduparthsinha.hashnode.dev/i-built-a-small-risc-v-computer-inside-a-tang-nano-9k-fpga</guid><category><![CDATA[fpga]]></category><category><![CDATA[verilog]]></category><category><![CDATA[embedded]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[risc-v]]></category><dc:creator><![CDATA[Parth Sinha]]></dc:creator><pubDate>Fri, 13 Nov 2026 12:56:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6af6f3d373c3f84def790d5b/662301f0-147a-463c-872c-c88f8a30401a.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I built a small RISC-V computer inside the Gowin GW1NR-9 FPGA on a Tang Nano 9K, a board that costs around $15 to $20, using only open-source tools: Yosys, nextpnr, Apicula and openFPGALoader. It ended up with a PicoRV32 CPU, 16 KB of RAM, a hand-written UART, a custom hardware accelerator and an HDMI text console. I wrote it up as five parts. Each one adds one block, explains the ideas it needs, and then shows what I built and measured.</p>
<h3>The five parts</h3>
<ul>
<li><a href="https://edu.parthsinha.com/posts/riscv-soc-1-toolchain-and-blink/">Part 1: an open-source toolchain and a blinking counter</a>. What synthesis, place and route, bitstreams and constraint files are, then a six-line counter that drives six LEDs. It uses 33 of the chip's 8,640 LUT4s.</li>
<li><a href="https://edu.parthsinha.com/posts/riscv-soc-2-uart/">Part 2: a UART from scratch</a>. Baud rates, mid-bit sampling and synchronizers, then a transmitter and receiver in about 40 lines of Verilog, tested in simulation first and then echoing typed characters on the board.</li>
<li><a href="https://edu.parthsinha.com/posts/riscv-soc-3-picorv32/">Part 3: a PicoRV32 CPU running C</a>. RV32IMC, memory-mapped I/O and linker scripts. The firmware is baked into the bitstream, so there's no bootloader. CPU, RAM and UART together take 47% of the LUTs and close timing at 51 MHz against a 27 MHz clock.</li>
<li><a href="https://edu.parthsinha.com/posts/riscv-soc-4-accelerator/">Part 4: a hardware accelerator, 46× faster than C</a>. A memory-mapped xorshift32 block that runs one step per clock, plus a small shell with peek, poke and bench commands.</li>
<li><a href="https://edu.parthsinha.com/posts/riscv-soc-5-hdmi/">Part 5: an HDMI text console</a>. Pixel clocks, video timing and TMDS encoding, then a 90×30 text display that mirrors the shell. It scrolls by changing one register instead of copying the screen.</li>
</ul>
<h3>The 46× number</h3>
<p>On the board, <code>bench 1000</code> took 50,015 cycles in C and 1,066 cycles on the accelerator, with identical results. The accelerator itself does one step per clock, so about 66 of those cycles are the cost of talking to it over the bus. That also means a single step at a time would be slower in hardware than in software. The speedup only shows up when each call does enough work.</p>
<h3>What's not done yet</h3>
<p>Parts 1 to 4 run on the board. Part 5 passes its simulation tests and builds a bitstream, but I haven't connected it to a monitor yet, so how it looks on a real screen is still unknown.</p>
<h3>Things that went wrong</h3>
<p>Each part has the problems I hit along the way. A few of them: on Linux, ModemManager kept probing the board's serial port, so the shell received its own output back as typed commands. A testbench passed even though the design was broken, and I only found out by feeding it bad input on purpose. And the first HDMI bitstream failed to pack until I switched from true LVDS buffers to emulated ones.</p>
<p>The code is on GitHub, with one folder per part: <a href="https://github.com/sinhaparth5/tangnano9k-riscv-soc">https://github.com/sinhaparth5/tangnano9k-riscv-soc</a></p>
<p>If LUTs and flip-flops are new to you, start with my notes on what's inside an FPGA: <a href="https://edu.parthsinha.com/posts/understanding-fpga/">https://edu.parthsinha.com/posts/understanding-fpga/</a></p>
<p>If you've put a soft CPU on a small FPGA, what did you add after the UART?</p>
]]></content:encoded></item><item><title><![CDATA[Why AES does its byte math in GF(2⁸), with a step-through multiplier]]></title><description><![CDATA[Why AES can't use integers mod 256, how GF(2⁸) treats bytes as polynomials, and an interactive multiplier that shows each shift and XOR with 0x11B]]></description><link>https://eduparthsinha.hashnode.dev/why-aes-does-its-byte-math-in-gf-2-with-a-step-through-multiplier</link><guid isPermaLink="true">https://eduparthsinha.hashnode.dev/why-aes-does-its-byte-math-in-gf-2-with-a-step-through-multiplier</guid><category><![CDATA[Cryptography]]></category><category><![CDATA[aes]]></category><category><![CDATA[Mathematics]]></category><category><![CDATA[Security]]></category><category><![CDATA[algorithms]]></category><dc:creator><![CDATA[Parth Sinha]]></dc:creator><pubDate>Fri, 13 Nov 2026 12:03:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6af6f3d373c3f84def790d5b/93003d7a-795c-496a-b748-aa8b1b551e8c.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most AES tutorials either wave at the math ("shift left and XOR with 0x1B") or jump straight into ring theory without tying it back to bytes. I wrote up notes from Christof Paar's AES lectures that try to sit in between.</p>
<h3>Why ordinary integer math doesn't work</h3>
<p>AES needs two things from byte arithmetic. Every result has to be another byte (0x00 to 0xFF), and every nonzero byte needs a multiplicative inverse, because the S-box is built from inverses. Integers mod 256 give you the first but not the second. No even number has an inverse mod 256, since 2·x is always even and so never equals 1.</p>
<p>A field with 256 elements has both properties, and GF(2⁸) is that field.</p>
<h3>What GF(2⁸) does with a byte</h3>
<p>Each byte is read as a polynomial whose coefficients are its bits. 0x57 = 01010111 is x⁶ + x⁴ + x² + x + 1.</p>
<p>Addition works coefficient by coefficient mod 2, which is just XOR: 0xD1 + 0x15 = 0xC4, with no carries.</p>
<p>Multiplication is polynomial multiplication followed by reduction modulo the AES polynomial x⁸ + x⁴ + x³ + x + 1 (0x11B). This is where xtime comes from. Multiplying by x shifts left one bit, and if a bit falls off the top, x⁸ gets replaced by x⁴ + x³ + x + 1, which means XOR with 0x1B.</p>
<p>To multiply 0x57 by 0x83, you keep doubling 0x57 and XOR in the copies where 0x83 has a 1 bit:</p>
<table>
<thead>
<tr>
<th>i</th>
<th>bit i of 0x83</th>
<th>0x57 · xⁱ</th>
<th>running XOR</th>
</tr>
</thead>
<tbody><tr>
<td>0</td>
<td>1</td>
<td>0x57</td>
<td>0x57</td>
</tr>
<tr>
<td>1</td>
<td>1</td>
<td>0xAE</td>
<td>0xF9</td>
</tr>
<tr>
<td>2</td>
<td>0</td>
<td>0x15C ⊕ 0x11B = 0x47</td>
<td>0xF9</td>
</tr>
<tr>
<td>3</td>
<td>0</td>
<td>0x8E</td>
<td>0xF9</td>
</tr>
<tr>
<td>4</td>
<td>0</td>
<td>0x11C ⊕ 0x11B = 0x07</td>
<td>0xF9</td>
</tr>
<tr>
<td>5</td>
<td>0</td>
<td>0x0E</td>
<td>0xF9</td>
</tr>
<tr>
<td>6</td>
<td>0</td>
<td>0x1C</td>
<td>0xF9</td>
</tr>
<tr>
<td>7</td>
<td>1</td>
<td>0x38</td>
<td>0xC1</td>
</tr>
</tbody></table>
<p>So 0x57 · 0x83 = 0xC1. The two rows with ⊕ 0x11B are the reductions: the shift pushed a 1 into bit 8, and XORing with 0x11B clears it.</p>
<p><img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/1zdf2twsyo7c189htq8d.gif" alt="Step-through GF(2⁸) multiplier showing 0x57 · 0x83 one bit at a time" /></p>
<p>The post has this as an interactive widget. You type any two bytes, then play it or step through one bit at a time, and a bit strip shows each shift, each XOR with 0x11B and each update to the result.</p>
<p>The rest of the post builds groups and fields from scratch. It has GF(7) and GF(2³) tables you can check by hand, the 0x57 · 0x83 long division, extended Euclid for inverses (0x53⁻¹ = 0xCA), and a short Python gf_mul. Part 2 uses all of this to build AES-128 layer by layer and checks it against the FIPS 197 test vector.</p>
<p>Part 1, Galois fields: <a href="https://edu.parthsinha.com/posts/galois-fields-for-aes/">https://edu.parthsinha.com/posts/galois-fields-for-aes/</a></p>
<p>Part 2, AES layer by layer: <a href="https://edu.parthsinha.com/posts/aes-structure-and-layers/">https://edu.parthsinha.com/posts/aes-structure-and-layers/</a></p>
<p>When you learned AES, which took longer to click: MixColumns or the S-box?</p>
]]></content:encoded></item></channel></rss>