<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Ernests Rudzitis - Blog</title><description>Blog posts about software development, AI, and technology</description><link>https://ernestsrudzitis.com/</link><language>en-us</language><item><title>Teaching Drones to Ignore Bad Advice: Building a &apos;Gut-Feeling&apos; for an AI Swarm</title><link>https://ernestsrudzitis.com/blog/teaching-drones-to-ignore-bad-advice-building-a-gut-feeling-for-an-ai-swarm/</link><guid isPermaLink="true">https://ernestsrudzitis.com/blog/teaching-drones-to-ignore-bad-advice-building-a-gut-feeling-for-an-ai-swarm/</guid><pubDate>Tue, 08 Jul 2025 20:36:51 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction: A Single Lie Can Break the Team&lt;/h2&gt;&lt;p id=&quot;isPasted&quot;&gt;Ever had that gut feeling that something isn&amp;#39;t quite right? That a friend is telling you a tall tale? As it turns out, AI needs that same intuition, especially when they work in teams. For my bachelor&amp;#39;s thesis, I decided to tackle this very problem: &lt;strong&gt;how do you teach a robot to be skeptical?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;A drone swarm&amp;#39;s greatest strength is its teamwork, which relies on constant, trustworthy communication to perform complex tasks like search and rescue or coordinated surveillance. They are continuously informing each other of their location, speed, and intentions to fly in perfect, coordinated formations.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;fr-img-caption fr-fil fr-dib&quot; style=&quot;width: 738px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; src=&quot;https://imgix.cosmicjs.com/75112260-59e7-11f0-aace-355b4061391c-TIF_slide_3_anim_optimize.gif&quot; alt=&quot;TIF_slide_3_anim_optimize.gif&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;Drone swarms constantly share location and velocity data to maintain coordinated flight patterns&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;But here&amp;#39;s the vulnerability: &lt;strong&gt;these AI-controlled teams trust everything they hear&lt;/strong&gt;. What happens when that trust is broken?&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;width: 652px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;TIF_slide_3to5_anim_optimize.gif&quot; src=&quot;https://cdn.cosmicjs.com/6649d990-59f9-11f0-aace-355b4061391c-TIF_slide_4to5_anim_optimize.gif&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;A single compromised message can cascade through the entire swarm, breaking formation and potentially causing mission failure&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2&gt;The Problem: When Communication Goes Wrong&lt;/h2&gt;&lt;p id=&quot;isPasted&quot;&gt;Drone swarms are basically teams of robots that constantly chat with each other. &amp;quot;I&amp;#39;m here,&amp;quot; says Drone A. &amp;quot;I&amp;#39;m moving this way,&amp;quot; reports Drone B. &amp;quot;Target spotted at these coordinates,&amp;quot; announces Drone C. This constant communication allows them to fly in perfect formation, avoid collisions, and accomplish complex missions.&lt;/p&gt;&lt;p&gt;But here&amp;#39;s the catch: what happens when that communication becomes unreliable?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong id=&quot;isPasted&quot;&gt;Sensor malfunction&lt;/strong&gt;: A drone&amp;#39;s GPS starts reporting wrong locations&lt;/li&gt;&lt;li&gt;&lt;strong id=&quot;isPasted&quot;&gt;Communication noise&lt;/strong&gt;: Radio interference corrupts the messages&lt;/li&gt;&lt;li&gt;&lt;strong id=&quot;isPasted&quot;&gt;Adversarial attacks&lt;/strong&gt;: An enemy deliberately sends false information or jams communication links&lt;/li&gt;&lt;li&gt;&lt;strong id=&quot;isPasted&quot;&gt;Hardware failures&lt;/strong&gt;: Equipment starts sending stale, outdated data&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Any of these scenarios can cause the entire swarm to break formation, crash into each other, or fail their mission entirely.&lt;/p&gt;&lt;h2&gt;The Traditional Solutions (And Why They&amp;#39;re Not Enough)&lt;/h2&gt;&lt;p id=&quot;isPasted&quot;&gt;Most existing approaches to this problem are heavy-handed:&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Option 1: Rebuild Everything from Scratch.&amp;nbsp;&lt;/strong&gt;Train your AI systems to handle bad communication from day one. This works, but it&amp;#39;s expensive, time-consuming, and means you can&amp;#39;t use any of the excellent pre-trained models that already exist.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Option 2: Use Fixed Security Protocols.&amp;nbsp;&lt;/strong&gt;Implement cryptographic security and rigid verification systems. This can work for some scenarios, but it&amp;#39;s inflexible and can&amp;#39;t adapt to new types of problems.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Both approaches are like replacing your entire smartphone just because you occasionally get spam calls - a massive overreaction to what should be a manageable problem.&lt;/p&gt;&lt;h2&gt;A Better Way: The Trust-Based Information Filtering (TIF) System&lt;/h2&gt;&lt;p&gt;&lt;span class=&quot;fr-img-caption fr-fil fr-dib&quot; style=&quot;width: 652px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; src=&quot;https://imgix.cosmicjs.com/6be6db40-59f0-11f0-aace-355b4061391c-Screenshot-2025-07-06-003525.png&quot; alt=&quot;Screenshot-2025-07-06-003525.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;The three-step research approach: identify reliability checks, enable self-learning, and improve robustness&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p id=&quot;isPasted&quot;&gt;What if instead of retraining the entire system, we just gave each drone a smart assistant that could whisper, &amp;quot;Hey, that message seems wrong &amp;ndash; maybe don&amp;#39;t trust it&amp;quot;? That&amp;#39;s essentially what the Trust-Based Information Filtering (TIF) system does. It&amp;#39;s a lightweight &amp;quot;trust layer&amp;quot; that sits between incoming messages and the drone&amp;#39;s decision-making brain.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;It works in the following way:&lt;/p&gt;&lt;h3&gt;Step 1: Learn What &amp;quot;Normal&amp;quot; Looks Like&lt;/h3&gt;&lt;p&gt;&lt;span style=&quot;width: 652px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;TIF_slide_8_anim_optimize.gif&quot; src=&quot;https://imgix.cosmicjs.com/1d4b7440-59f1-11f0-aace-355b4061391c-TIF_slide_8_anim_optimize.gif&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;The TIF system records thousands of interactions during normal operations to build a baseline of trustworthy behavior&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;The system watches the drone swarm during normal, ideal operations and builds a detailed profile of what trustworthy communication patterns look like. To put it in perspective, It&amp;#39;s equivalent would be learning the rhythm and flow of good teamwork.&lt;/p&gt;&lt;h3&gt;Step 2: Spot the Anomalies&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;fr-img-caption fr-fil fr-dib&quot; style=&quot;width: 652px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; src=&quot;https://imgix.cosmicjs.com/8c771450-59f1-11f0-aace-355b4061391c-Screenshot-2025-07-06-004326-min.png&quot; alt=&quot;Screenshot-2025-07-06-004326-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;The system analyzes multiple types of features to assess message trustworthiness&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;When new messages come in, the system checks them against learned patterns using sophisticated anomaly detection algorithms. It looks into the following indicators:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Sudden, (proximally) impossible changes in reported positions&lt;/li&gt;&lt;li&gt;Messages that don&amp;#39;t match what neighboring drones are reporting&lt;/li&gt;&lt;li&gt;Communication patterns that violate physical laws&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span style=&quot;width: 652px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;Screenshot-2025-07-06-004536-min.png&quot; src=&quot;https://imgix.cosmicjs.com/d88e9a20-59f1-11f0-aace-355b4061391c-Screenshot-2025-07-06-004536-min.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;Building a &amp;quot;zone of trust&amp;quot; - messages falling within normal patterns are trusted, outliers are flagged&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;h3&gt;Step 3: Plausible Recovery&lt;/h3&gt;&lt;p&gt;&lt;span class=&quot;fr-img-caption fr-fil fr-dib&quot; style=&quot;width: 652px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; src=&quot;https://imgix.cosmicjs.com/0e99bf00-59f2-11f0-aace-355b4061391c-Screenshot-2025-07-06-004719-min.png&quot; alt=&quot;Screenshot-2025-07-06-004719-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;The TIF system in action: trusted messages pass through, untrusted ones trigger recovery mechanisms&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;When bad information is detected, instead of just throwing it away, the system tries to reconstruct plausible replacement data. It might use recent history to estimate where a drone probably is, or smooth out obvious noise.&lt;/p&gt;&lt;h2&gt;The Results: Small Improvements, Big Impact&lt;/h2&gt;&lt;p id=&quot;isPasted&quot;&gt;To properly evaluate the TIF system, I needed to simulate the kinds of communication problems that real drone swarms might encounter in the field. So I introduced three types of deliberate &amp;quot;sabotage&amp;quot; into the simulation environment:&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Message Freezing&lt;/strong&gt;: This simulates scenarios like replay attacks or connection issues where the last known position keeps being broadcast even though the drone has moved.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Message Offset&lt;/strong&gt;: This adds a consistent error to all reported values - like a drone whose GPS sensor has developed a persistent bias, always reporting positions that are off by a fixed amount in a particular direction.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Random Noise Injection&lt;/strong&gt;: The most common real-world problem - Gaussian noise gets added to transmitted data, simulating everything from radio interference to minor sensor inaccuracies.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Each of these represents a different challenge for the trust system to detect and handle. Testing this system on drone formation flying tasks under these corrupted conditions showed promising results:&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;width: 502px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;Screenshot-2025-07-06-005224-min.png&quot; src=&quot;https://imgix.cosmicjs.com/3a79ef40-59f3-11f0-aace-355b4061391c-Screenshot-2025-07-06-005224-min.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;Mean formation error of the swarm formation with the baseline policy versus the policy enhanced by TIF system. Results are averaged across three distinct communication compromise types: noise, offset, and freeze. The TIF system consistently reduces formation error in all scenarios. (Lower is better).&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;fr-img-caption fr-fil fr-dib&quot; style=&quot;width: 502px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; src=&quot;https://imgix.cosmicjs.com/c7022690-59f2-11f0-aace-355b4061391c-Screenshot-2025-07-06-005158-min.png&quot; alt=&quot;Screenshot-2025-07-06-005158-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;Percentage improvement in mean formation error achieved by the TIF system, categorized by compromise type.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p id=&quot;isPasted&quot;&gt;&lt;em&gt;6.8% overall improvement in formation accuracy, with best performance against random noise&lt;/em&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;6.8% overall improvement&lt;/strong&gt; in formation accuracy during communication failures&lt;/li&gt;&lt;li&gt;&lt;strong&gt;9.5% improvement&lt;/strong&gt; against random noise (the most common real-world issue)&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Consistent protection&lt;/strong&gt; even as the percentage of bad messages increased&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;These might seem like small and modest numbers, but in the world of coordinated robotics, I would like to argue that they&amp;#39;re significant. A 6.8% improvement in formation accuracy could mean the difference between mission success and collision, between a successful rescue operation and a catastrophic failure.&lt;/p&gt;&lt;h2&gt;Why This Matters&lt;/h2&gt;&lt;p id=&quot;isPasted&quot;&gt;What makes this approach particularly exciting is its &lt;strong&gt;plug-and-play nature&lt;/strong&gt;. You don&amp;#39;t need to retrain your expensive, carefully-tuned AI models. You don&amp;#39;t need to redesign your entire communication system. You just add this trust layer, let it learn from normal operations for a while, and it starts protecting your swarm.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;This is especially important because:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Training MARL systems is expensive&lt;/strong&gt; &amp;ndash; we&amp;#39;re talking weeks of computation and thousands of dollars&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Many organizations already have working systems&lt;/strong&gt; they&amp;#39;d rather enhance than replace&lt;/li&gt;&lt;li&gt;&lt;strong&gt;New types of attacks and failures emerge constantly&lt;/strong&gt; &amp;ndash; a system that can learn and adapt is more valuable than one with fixed defenses&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;The Broader Picture&lt;/h2&gt;&lt;p id=&quot;isPasted&quot;&gt;While this research focused specifically on drone swarms, the underlying principles could apply to many other multi-agent AI systems:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Autonomous vehicle fleets&lt;/strong&gt; sharing traffic information&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Robot teams&lt;/strong&gt; in warehouses or factories&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Distributed AI systems&lt;/strong&gt; making collective decisions&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Smart city infrastructure&lt;/strong&gt; coordinating traffic lights, sensors, and services&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Anywhere where you have AI agents that need to communicate and coordinate, you potentially need mechanisms to ensure that communication is trustworthy.&lt;/p&gt;&lt;h2&gt;The Journey&amp;#39;s End and What&amp;#39;s Next?&lt;/h2&gt;&lt;p&gt;&lt;span style=&quot;width: 652px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;Screenshot-2025-07-06-005848-min.png&quot; src=&quot;https://imgix.cosmicjs.com/ab39eaf0-59f3-11f0-aace-355b4061391c-Screenshot-2025-07-06-005848-min.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;The roadmap ahead: from 2D simulations to 3D reality, with smarter adaptation and recovery&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p id=&quot;isPasted&quot;&gt;This work represents a promising first prototype, but there&amp;#39;s more to explore:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Testing in high-fidelity environments&lt;/strong&gt; and eventually on real hardware&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Continuous adaptation&lt;/strong&gt; to handle dynamically changing missions&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Handling more sophisticated adversaries&lt;/strong&gt; that try to mimic normal behavior&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Developing better recovery mechanisms&lt;/strong&gt; using more advanced techniques, such as, generative networks, temporal memory&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The vision is a future where AI teams can maintain their coordination and effectiveness even in the face of unreliable, noisy, or malicious communication &amp;ndash; where a single lie, quite literally, doesn&amp;#39;t break the team.&lt;/p&gt;&lt;h2&gt;The Takeaway&lt;/h2&gt;&lt;p&gt;In our increasingly connected world of AI agents, the ability to distinguish trustworthy information from bad data isn&amp;#39;t just a nice-to-have feature, rather it&amp;#39;s essential for safety and mission success. The Trust-Based Information Filtering (TIF) system shows that we don&amp;#39;t always need to start from scratch to build more robust AI systems. Sometimes, the best solution is teaching our AI agents the same skill humans have been developing for millennia: &lt;strong id=&quot;isPasted&quot;&gt;knowing when not to trust what they hear&lt;/strong&gt;.&lt;/p&gt;</content:encoded></item><item><title>Color Meets Shape: Using Histograms of Oriented Gradients and Colors to Classify Flowers</title><link>https://ernestsrudzitis.com/blog/color-meets-shape-using-histograms-of-gradients-and-colors-to-classify-flowers/</link><guid isPermaLink="true">https://ernestsrudzitis.com/blog/color-meets-shape-using-histograms-of-gradients-and-colors-to-classify-flowers/</guid><description>Discover how to use Histograms of Oriented Gradients (HOG) and color histograms to build a 94% accurate flower classifier. Explore feature extraction, implementation, and a practical application in image recognition!</description><pubDate>Mon, 30 Dec 2024 21:27:56 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;&lt;p&gt;In this blog post, we will explore fundamental object detection technique called &lt;strong&gt;Histograms of Oriented Gradients&lt;/strong&gt; (HOGs). Interestingly, I came across this method in a sort of unconventional way, that is while browsing the comment section of a completely unrelated YouTube video. What began as a casual scroll quickly turned into an unexpected discovery, sparking my curiosity to dive deeper into this subject. After having done a reasonable amount of exploring the concepts, applications, and implementation, I am excited to share my findings with you.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;While the primary focus will be on understanding HOGs and their role in object detection, we will also take things a step further by applying this knowledge to a practical task: &lt;em&gt;classifying some of the most common flowers found in the UK&amp;nbsp;&lt;/em&gt;(&lt;a href=&quot;https://www.robots.ox.ac.uk/~vgg/data/flowers/17/&quot;&gt;17 flowers dataset&lt;/a&gt;). This choice of application is not arbitrary. After doing a quick search on Google Scholar and the Web, I noticed that while both HOG features and color histograms have been used individually or in combination with other techniques for plant and flower classification, there are relatively few articles that specifically explore the combination of HOG and color histograms for this purpose.&lt;/p&gt;&lt;p id=&quot;isPasted&quot;&gt;This pairing is particularly compelling because it allows us to capture both the &lt;strong&gt;shape&amp;nbsp;&lt;/strong&gt;and &lt;strong&gt;structural details&lt;/strong&gt; of the flowers (by means of HOGs) alongside their rich and varied &lt;strong&gt;color patterns&lt;/strong&gt; (via color histograms).&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;We will begin by exploring the implementation details with the help of visual elements (animations, images) and then proceed to build the functionality from scratch without relying on any third-party libraries. Finally, we will wrap up by creating the flower classifier.&lt;/p&gt;&lt;p&gt;By the time you finish this post, we will have built an image classifier that achieves an impressive 94% test accuracy!&lt;/p&gt;&lt;h2&gt;Histograms of Oriented Gradients&lt;/h2&gt;&lt;p&gt;&lt;a href=&quot;https://ieeexplore.ieee.org/document/1467360&quot;&gt;Introduced by Navneet Dalal and Bill Triggs in 2005&lt;/a&gt;. It became particularly famous for human detection applications and served as a foundational technique that influenced many modern computer vision approaches.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Histograms of Oriented Gradients are like edge-detectors but on steroids, they not only extract &lt;strong&gt;gradients&amp;nbsp;&lt;/strong&gt;(that tell use information about pixel intensity), but also &lt;strong&gt;orientation&lt;/strong&gt; (the direction of the change in pixel intensity). These gradients and orientations are computed for local regions of an image, often called cells, and for each cell a &lt;strong&gt;histogram&amp;nbsp;&lt;/strong&gt;is calculated. Hence the name Histograms of Oriented Gradients. Details on this are covered in the following sections.&lt;/p&gt;&lt;h3&gt;Dataset in question&lt;/h3&gt;&lt;p&gt;The following image showcases a sample from our dataset which we will use throughout this post to illustrate key concepts. The example features a Daffodil flower, one of the most recognizable and vibrant blooms commonly found in the UK.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;width: 292px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; src=&quot;https://imgix.cosmicjs.com/ca905ee0-c36f-11ef-8a63-eb57d6c77a36-image_0006.jpg&quot; alt=&quot;image_0006.jpg&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;h3&gt;Pre-processing&lt;/h3&gt;&lt;p&gt;As per any task in computer vision, or machine learning in general, we should start of by applying pre-processing steps. The &lt;a href=&quot;https://ieeexplore.ieee.org/document/1467360&quot;&gt;original paper&lt;/a&gt; (that studied HOGs performance for Human detection) mentions the following, citing.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Our 64&amp;times;128 detection window includes about 16 pixels of margin around the person on all four sides.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;The exact 64&amp;times;128 size seems to have been chosen empirically, for the specific task in hand, to include a reasonable margin around the pedestrian while matching the general scale of pedestrians in their dataset. The paper does not provide a theoretical justification for this particular window size, therefore for our task, considering the varied shapes and close-up nature of the flower images, I opted to resize all images to a uniform size of 256&amp;times;256 pixels, ensuring consistency and better suitability for flower classification.&lt;/p&gt;&lt;p&gt;&lt;img style=&quot;width: 200px;&quot; alt=&quot;Gradients-Blog-Preprocessing-1.png&quot; src=&quot;https://imgix.cosmicjs.com/4c8c9b30-c383-11ef-8a63-eb57d6c77a36-Gradients-Blog-Preprocessing-1.png&quot; class=&quot;fr-dib fr-rounded&quot;&gt;&lt;/p&gt;&lt;h3&gt;Gradient computation&lt;/h3&gt;&lt;p&gt;The succeeding step is to compute vertical and horizontal gradients, that is the pixel intensity changes both in x and y directions. Gradient computation is a crucial step in the HOG feature extraction process. To calculate the gradients, we will not re-invent the wheel, rather we use specially selected kernels whose main purpose is to detect such pixel intensity changes in either of directions. The most commonly used kernels for this purpose are the &lt;strong&gt;[-1, 0, 1]&lt;/strong&gt; kernel for the horizontal gradient and its transpose, &lt;strong&gt;[-1, 0, 1]ᵀ&lt;/strong&gt;, for the vertical gradient. To apply these kernels, they are slid across the image in a process called convolution. At each pixel location, the kernel is centered, and the pixel values are multiplied by the corresponding kernel values. The results are then summed up to obtain the gradient value at that particular pixel. This process is repeated for every pixel in the image, resulting in two gradient maps &amp;ndash; one for the horizontal gradient and another for the vertical gradient.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Important note!&amp;nbsp;&lt;/strong&gt;Before performing this step, a decision must be made about whether to convert the input image to grayscale or keep it in its original multichannel form. If the image is converted to grayscale, the gradient computation is straightforward and performed on a single channel. However, if the image remains multichannel, gradients are computed separately for each channel, resulting in multiple gradient maps (one for each channel). Keeping the color information can lead to improved performance later down the line.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;fr-img-caption fr-dib fr-rounded&quot; style=&quot;width: 292px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;SlidingGradientAnimation-ezgif-com-video-to-gif-converter.gif&quot; src=&quot;https://imgix.cosmicjs.com/ab20fc20-c38f-11ef-8a63-eb57d6c77a36-SlidingGradientAnimation-ezgif-com-video-to-gif-converter.gif&quot; class=&quot;fr-dib fr-rounded&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;The animation demonstrates this process with horizontal kernel, but the same idea would apply for horizontal gradient computation (on a small patch extracted from the sample image).&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;Notably, as visible in animation, regions with significant changes in pixel intensity tend to exhibit larger gradient values. These areas correspond to edges, boundaries, or transitions within the image. In our case, these changes often occur due to the distinct edges of flower petals, where the shape of the petal creates sharp contrasts against the background or adjacent petals.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;width: 285px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; src=&quot;https://imgix.cosmicjs.com/29334520-c384-11ef-8a63-eb57d6c77a36-Gradients-Blog-Gradient-computation-1.png&quot; alt=&quot;Gradients-Blog-Gradient-computation-1.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;Final gradient (horizontal and vertical) gradient computation. Fun fact, that might seem counterintuitive at first, but the horizontal kernel detects vertical changes, similarly, the vertical kernel is aligned vertically, but it detects horizontal changes&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;h3&gt;Magnitude and orientation computation&lt;/h3&gt;&lt;p&gt;With the gradient computation in place, we can now finally determine the magnitude value for each pixel. Now you might wonder, &lt;strong&gt;why bother calculating magnitude when we already have the horizontal and vertical gradients?&lt;/strong&gt; The answer is plain and simple: relying on just one gradient can miss the bigger picture, especially for edges that are angled or diagonal. To fully capture the strength of an edge, we combine the horizontal and vertical gradients using the good old Pythagorean theorem. Might have sounded harder than it really is, however it is as elementary as applying a bit of high school math.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Important note! I&amp;nbsp;&lt;/strong&gt;chose to preserve all of the RGB channels of the image, therefore the final magnitude is the largest one amongst all of the image channels for a particular pixel.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;fr-img-caption fr-dib fr-rounded&quot; style=&quot;width: 292px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;GradientMagnitudeVisualization-ezgif-com-video-to-gif-converter.gif&quot; src=&quot;https://imgix.cosmicjs.com/69a24e50-c396-11ef-8a63-eb57d6c77a36-GradientMagnitudeVisualization-ezgif-com-video-to-gif-converter.gif&quot; class=&quot;fr-dib fr-rounded&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;After calculating the magnitude of the gradient, the next step is to compute the orientation of the gradient at each pixel. The orientation represents the direction of the edge and provides additional information about the structure and shape of the objects in the image.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;fr-img-caption fr-fil fr-dib&quot; style=&quot;width: 292px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;GradientOrientationVisualization-ezgif-com-optimize.gif&quot; src=&quot;https://imgix.cosmicjs.com/a36a7b00-c5e2-11ef-8a63-eb57d6c77a36-GradientOrientationVisualization-ezgif-com-optimize.gif&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;The resulting angle is then typically converted to degrees for easier interpretation, in addition a crucial decision must be made regarding whether to keep the angle signed (-180&amp;deg; to 180&amp;deg;) or unsigned (0&amp;deg; to 360&amp;deg;). In the original paper the authors experimented with various approaches and found that for human detection, using unsigned gradients over angles of 0&amp;deg; to 180&amp;deg; provided the best performance. I experienced no perceptible accuracy improvements using either of the approaches for flower classification.&lt;/p&gt;&lt;h3&gt;Histogram computation&lt;/h3&gt;&lt;p&gt;The next step in HOG feature extraction pipeline is to create histogram representations of these gradients. To begin, the image is to be divided into smaller, local regions called &amp;#39;&lt;em&gt;cells&lt;/em&gt;&amp;#39;, as we remember from introduction.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p id=&quot;isPasted&quot;&gt;In the original paper, the authors used 8x8 pixel cells, their justification was that &amp;#39;&lt;em&gt;relatively coarse spatial quantization suffices (8&amp;times;8 pixel cells / one limb width)&lt;/em&gt;&amp;#39;. This reasoning suggests that the cell size should be chosen to roughly correspond to the size of meaningful parts or features of the objects being detected, which aligns with my discoveries, more specifically in my experiments with flower classification, I found that using a larger cell size of &lt;strong&gt;16&amp;times;16&lt;/strong&gt; provided better results for the task in hand. It is important to emphasize that the cell size can be adjusted based on the characteristics of the objects being detected and the resolution of the images. When selecting the cell size, it is significant to ensure that the cells evenly divide the image. In other words, the image dimensions should be divisible by the cell size without leaving any remainder, which ensures that all cells have the same size and that there are no partial or incomplete cells at the edges of the image. For example, as in our case the images are resized to have dimensions of 256&amp;times;256 pixels, we could have chosen cell sizes of 8&amp;times;8, 16&amp;times;16, 32&amp;times;32, or 64&amp;times;64, as all of these sizes evenly divide the image. On the other hand, lets say if the image has dimensions of 150&amp;times;150 pixels, a cell size of 16&amp;times;16 would not be suitable, as it would result in uneven cells at the image boundaries.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;width: 285px;&quot; class=&quot;fr-img-caption fr-fil fr-dib fr-rounded&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; src=&quot;https://imgix.cosmicjs.com/38ecbf10-c3d3-11ef-8a63-eb57d6c77a36-Gradients-Blog-Histogram-computation-1.png&quot; alt=&quot;Gradients-Blog-Histogram-computation-1.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;Illustration of target image being divided into 16x16 cells&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Great, now that we have divided our image into cells, we end up with 16&amp;times;16&amp;times;2 = 512 total values for each cell. This comes from the fact that each cell consists of 16&amp;times;16 = 256 pixels, and for each pixel, we have calculated two key values - magnitude and orientation. With these values in hand, we can now move on to the more exciting part of calculating the histograms of oriented gradients for each cell!&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;A histogram is a visual representation of the distribution of quantitative data. To construct a histogram, the first step is to &amp;quot;bin&amp;quot; the range of values&amp;mdash; divide the entire range of values into a series of intervals&amp;mdash;and then count how many values fall into each interval&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;In HOGs histograms represent the distribution of gradient orientations within a particular cell. The gradient orientations are typically quantized into a fixed number of bins, commonly 9, as suggested in the original paper: &lt;em id=&quot;isPasted&quot;&gt;&amp;#39;increasing the number of orientation bins improves performance significantly up to about 9 bins.&amp;#39;&lt;/em&gt;&amp;nbsp; With 180&amp;deg; divided into 9 bins, each bin covers a span of 20&amp;deg;. The construction of the histogram is the following, we directly take each pixels gradients magnitude and add it to the corresponding orientation bin. The resulting histogram for each cell provides a compact representation of dominant edges while abstracting away the exact spatial locations of the gradients within the cell.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;width: 285px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;HistogramGridAnimation-ezgif-com-optimize.gif&quot; src=&quot;https://imgix.cosmicjs.com/4a9bff00-c4a5-11ef-8a63-eb57d6c77a36-HistogramGridAnimation-ezgif-com-optimize.gif&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;Illustrative animation of the histogram creating process by &amp;#39;binning&amp;#39; (on a 6x6 patch for visualization purposes)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;h4&gt;&lt;span style=&quot;width: 285px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; src=&quot;https://imgix.cosmicjs.com/cd0f0dd0-c50c-11ef-8a63-eb57d6c77a36-Gradients-Blog-Histogram-computation-2-min.png&quot; alt=&quot;Gradients-Blog-Histogram-computation-2-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;Resulting visualization constructed from histograms&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;&lt;h3&gt;Block normalization&lt;/h3&gt;&lt;p id=&quot;isPasted&quot;&gt;We have arrived at the final finishing touch for HOG computation, which is to perform block normalization. Block normalization is a crucial technique used to further improve invariance of the HOG to changes in illumination and contrast.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;The need for block normalization arises from the fact that the gradient magnitudes can vary significantly across different regions of an image due to variations in lighting conditions, shadows, and local contrast. The above mentioned variations can adversely affect the performance of a classifier because of the inconsistent object description. Block normalization helps to mitigate such an issue by normalizing the histogram values across larger spatial regions called &amp;#39;&lt;strong&gt;blocks&lt;/strong&gt;&amp;#39;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;A block is a group of adjacent cells, typically 2&amp;times;2 or 3&amp;times;3 cells, that are treated as a single unit for the means of normalization. In simple words, this means that we concatenate 4 or 9 histograms together in one large list (respectively, forming 4&amp;times;9 = 36 or 9&amp;times;9 = 81 histogram values). The block size is usually larger than the cell size to capture a wider spatial context. The blocks are overlapped, meaning that each cell contributes to multiple blocks. The normalization procedure involves computing L2 normalization (essentially normalization based off of Euclidean distance) or L2-Hys normalization of the block. The study shows that both methods display close performance.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;After block normalization is finished we have the final feature set that describes our object, the authors in the paper introduce them as HOG descriptors: &amp;#39;&lt;em&gt;We will refer to the normalized descriptor blocks as Histogram of Oriented Gradient (HOG) descriptors&lt;/em&gt;&amp;#39;.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;width: 285px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; src=&quot;https://imgix.cosmicjs.com/cfa9ab50-c50b-11ef-8a63-eb57d6c77a36-BlockNormalizationAnimation-ezgif-com-optimize.gif&quot; alt=&quot;BlockNormalizationAnimation-ezgif-com-optimize.gif&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;Illustration of block normalization process. The final feature count is 15&amp;times;15&amp;times;4&amp;times;9 = 8100 (256/32-1 = 15 blocks that fit width and height wise, each block is made up of 4 cells, each cell has 9 histogram values)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Important note!&lt;/strong&gt; While raw histograms create intuitive visualizations of gradient directions, the normalized features are essential for machine learning tasks. Visualizing normalized features is less common since they represent abstract, high-dimensional block-wise patterns optimized for classification rather than human interpretation.&lt;/p&gt;&lt;h2&gt;Color histograms&lt;/h2&gt;&lt;p&gt;Although color histograms are not the main focus of this blog post, they do however expand our feature space with additional valuable characteristics. The process of creating color histograms closely mirrors the approach used for HOG. Here, instead of gradients, we &amp;#39;bin&amp;#39; the pixel values from each image channel separately based on intensity ranges. In result we are left with 3 histograms, each for Red, Green and Blue channels, that provide a detailed representation of the color distribution withing the image. For the task of flower classification I found that 64 bins per channel yielded the highest performance enhancement, and any additional increase had little to no effect.&lt;/p&gt;&lt;p&gt;&lt;img class=&quot;fr-fil fr-dib&quot; src=&quot;https://imgix.cosmicjs.com/452c2c90-c516-11ef-8a63-eb57d6c77a36-Gradients-Blog-Color-Histogram-computation-min.png&quot; alt=&quot;Gradients-Blog-Color-Histogram-computation-min.png&quot;&gt;&lt;/p&gt;&lt;h2 id=&quot;isPasted&quot;&gt;Code implementation&lt;/h2&gt;&lt;p&gt;There are countless libraries that implement the above discussed functionality for us, however, I believe implementing it yourself adds that extra layer of understanding and truly solidifies the concept. In the first subsection I present you a straightforward hands on implementation that is easy to follow. In the second subsection we will walk through an implementation that uses third party libraries that have much more optimized solution utilizing smart matrix multiplications - this is the recommended choice for practical applications.&lt;/p&gt;&lt;h3&gt;Building it ourselves&lt;/h3&gt;&lt;pre data-prompt=&quot;C:\User\...&gt;&quot; id=&quot;isPasted&quot;&gt;pip install numpy Pillow matplotlib scipy&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;The following code demonstrates how to implement HOG feature extraction from scratch using Python.&lt;/p&gt;&lt;p id=&quot;isPasted&quot;&gt;This implementation is encapsulated in a &lt;code&gt;HOGExtractor&lt;/code&gt; class, which:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Initializes Parameters&lt;/strong&gt;: Sets up image size, cell size, block size, and the number of orientation bins required for HOG computation&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Loads and Preprocesses the Image&lt;/strong&gt;: Handles resizing and normalization to ensure consistent input&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Computes Gradients&lt;/strong&gt;: Uses kernels to calculate horizontal and vertical gradients, from which gradient magnitudes and orientations are derived&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Builds Cell Histograms&lt;/strong&gt;: Divides the image into smaller regions (cells) and computes a histogram of gradient orientations for each region&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Performs Block Normalization&lt;/strong&gt;: Slides across overlapping blocks of cells and performs normalization&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Generates the Final Descriptor&lt;/strong&gt;: Concatenates all normalized block histograms into a feature vector that represents the image and objects inside of it&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;pre id=&quot;isPasted&quot; data-filename=&quot;hog.py&quot; data-language=&quot;py&quot;&gt;import numpy as np
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
from scipy.signal import convolve2d

class HOGExtractor:
    def __init__(self, image_size=(256, 256), cell_size=(16, 16), block_size=(2, 2), band_count=9):
        self.IMAGE_RESIZE_SIZE = image_size
        self.CELL_SIZE = cell_size
        self.BLOCK_SIZE = block_size
        self.BAND_COUNT = band_count
        self.BIN_WIDTH = 180 / band_count
        
        # Sobel operators for gradient computation
        self.horizontal_kernel = np.array([[-1, 0, 1]])
        self.vertical_kernel = np.array(self.horizontal_kernel.T)
        
        # Initialize computed attributes
        self.input_image = None
        self.resized_image = None
        self.gradient_magnitude = None
        self.gradient_orientation = None
        self.cell_histograms = None
        self.hog_descriptor = None
        
    def _load_image(self, pil_image):
        # The authors of HOG found an increase in accuracy
        # by taking into consideration all RGB channels, however we convert the image to grayscale for convenience of this example
        self.input_image = pil_image.convert(&amp;#39;L&amp;#39;)
        self.resized_image = np.array(self.input_image.resize(self.IMAGE_RESIZE_SIZE))
        self.resized_image = self.resized_image.astype(float)
        # Normalize the image pixel values to [0, 1]
        self.resized_image = (self.resized_image - self.resized_image.min()) / (self.resized_image.max() - self.resized_image.min())
        
    def _compute_gradients(self):
        # Apply sobel kernels using convolution
        # &amp;#39;same&amp;#39; property ensures that the output has the same dimensions as the input image by automatically adding appropriate padding
        horizontal_gradient = convolve2d(self.resized_image, self.horizontal_kernel, mode=&amp;#39;same&amp;#39;)
        vertical_gradient = convolve2d(self.resized_image, self.vertical_kernel, mode=&amp;#39;same&amp;#39;)
        
        # Calculate gradient magnitude and orientation
        self.gradient_magnitude = np.sqrt(horizontal_gradient**2 + vertical_gradient**2)
        self.gradient_orientation = np.arctan2(vertical_gradient, horizontal_gradient) * (180 / np.pi) % 180
        
    def compute_cell_histograms(self):
        # Calculate number of cells in each dimension
        cells_y = self.IMAGE_RESIZE_SIZE[1] // self.CELL_SIZE[1]
        cells_x = self.IMAGE_RESIZE_SIZE[0] // self.CELL_SIZE[0]
        
        # Initialize histogram array for all cells
        self.cell_histograms = np.zeros((cells_y, cells_x, self.BAND_COUNT))
        
        # Compute histograms for each cell
        for y in range(cells_y):
            for x in range(cells_x):
                # Get current cell coordinates
                y_start = y * self.CELL_SIZE[1]
                y_end = (y + 1) * self.CELL_SIZE[1]
                x_start = x * self.CELL_SIZE[0]
                x_end = (x + 1) * self.CELL_SIZE[0]
                
                # Get magnitudes and orientations for current cell
                cell_magnitudes = self.gradient_magnitude[y_start:y_end, x_start:x_end]
                cell_orientations = self.gradient_orientation[y_start:y_end, x_start:x_end]
                
                # Create histogram for current cell
                histogram = np.zeros(self.BAND_COUNT)
                
                # Go over each pixel in the cell
                for i in range(self.CELL_SIZE[1]):
                    for j in range(self.CELL_SIZE[0]):
                        orientation = cell_orientations[i, j]
                        magnitude = cell_magnitudes[i, j]
                        
                        # Compute bin index for current orientation, and add magnitude to corresponding bin
                        bin_index = int(orientation // self.BIN_WIDTH)
                        histogram[bin_index] += magnitude
                        
                self.cell_histograms[y, x] = histogram
                
    def compute_hog_descriptor(self):
        # Calculate number of blocks
        cells_y = self.IMAGE_RESIZE_SIZE[1] // self.CELL_SIZE[1]
        cells_x = self.IMAGE_RESIZE_SIZE[0] // self.CELL_SIZE[0]
        blocks_y = cells_y - self.BLOCK_SIZE[0] + 1
        blocks_x = cells_x - self.BLOCK_SIZE[1] + 1
        
        # Initialize final HOG descriptor
        hog_descriptor = []
        
        # Slide the block window across cells
        for y in range(blocks_y):
            for x in range(blocks_x):
                # Get histograms for current block (2x2 cells)
                block_histograms = []
                for cell_y in range(self.BLOCK_SIZE[0]):
                    for cell_x in range(self.BLOCK_SIZE[1]):
                        cell_histogram = self.cell_histograms[y + cell_y, x + cell_x]
                        block_histograms.extend(cell_histogram)
                
                # Normalize block using L2 norm
                # Small epsilon value prevents division by zero
                block_histograms = np.array(block_histograms)
                l2_norm = np.sqrt(np.sum(block_histograms ** 2) + 1e-6)
                normalized_block = block_histograms / l2_norm
                
                # Add normalized block histograms to final descriptor
                hog_descriptor.extend(normalized_block)
                
        self.hog_descriptor = np.array(hog_descriptor)
        return self.hog_descriptor
    
    def extract_features(self, pil_image):
        self._load_image(pil_image)
        self._compute_gradients()
        self.compute_cell_histograms()
        return self.compute_hog_descriptor()
    
    def visualize(self):
        self._visualize_hog()
        
    def _visualize_hog(self):
        # Calculate dimensions
        cells_y = self.IMAGE_RESIZE_SIZE[1] // self.CELL_SIZE[1]
        cells_x = self.IMAGE_RESIZE_SIZE[0] // self.CELL_SIZE[0]
        
        # Create visualization
        vis_image = Image.new(&amp;#39;RGB&amp;#39;, self.IMAGE_RESIZE_SIZE, &amp;#39;black&amp;#39;)
        draw = ImageDraw.Draw(vis_image)
        
        cell_height, cell_width = self.CELL_SIZE
        line_length = min(cell_height, cell_width) // 2
        
        # Draw lines using raw cell histograms directly
        for y in range(cells_y):
            for x in range(cells_x):
                # Use raw cell histograms instead of normalized ones
                raw_histogram = self.cell_histograms[y, x]
                self._draw_cell_visualization(draw, x, y, cell_width, cell_height, 
                                        line_length, raw_histogram)
        
        self._show_visualization(vis_image, &amp;#39;Raw HOG Visualization&amp;#39;)
    
    # Private helper  function to draw cell visualization
    def _draw_cell_visualization(self, draw, x, y, cell_width, cell_height, line_length, histogram):
        cell_center_y = (y + 0.5) * cell_height
        cell_center_x = (x + 0.5) * cell_width
        
        for orientation_bin in range(self.BAND_COUNT):
            orientation = orientation_bin * (180 / self.BAND_COUNT)
            magnitude = histogram[orientation_bin]
        
            
            radian = np.deg2rad(orientation)
            dx = line_length * np.cos(radian) * magnitude / np.max(histogram)
            dy = line_length * np.sin(radian) * magnitude / np.max(histogram)
            
            draw.line([
                (cell_center_x - dx, cell_center_y - dy),
                (cell_center_x + dx, cell_center_y + dy)
            ], fill=&amp;#39;white&amp;#39;, width=1)
            
    # Private helper function to show visualization
    def _show_visualization(self, vis_image, title):
        plt.figure(figsize=(10, 15))
        plt.subplot(311)
        plt.title(&amp;#39;Original Image&amp;#39;)
        plt.imshow(self.input_image, cmap=&amp;#39;gray&amp;#39;)
        
        plt.subplot(312)
        plt.title(title)
        plt.imshow(vis_image)
        
        plt.tight_layout()
        plt.show()&lt;/pre&gt;&lt;h3&gt;Leveraging the Pros&lt;/h3&gt;&lt;pre id=&quot;isPasted&quot; data-prompt=&quot;C:\User\...&gt;&quot;&gt;pip install scikit-image numpy matplotlib&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Why Use a Library?&amp;nbsp;&lt;/strong&gt;While implementing HOG from scratch deepens understanding, third-party libraries like scikit-image offer optimized implementations (from my testing the feature extraction process was approximately 10 times faster)&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;pre data-filename=&quot;hog.py&quot; data-language=&quot;py&quot; id=&quot;isPasted&quot;&gt;from skimage.feature import hog
from skimage.transform import resize
import numpy as np
import matplotlib.pyplot as plt

class HOGExtractor:
   def __init__(self, image_size=(256, 256), cell_size=(16, 16), block_size=(2, 2), band_count=9):
        self.image_size = image_size
        self.input_image = None
        self.cell_size = cell_size
        self.block_size = block_size
        self.band_count = band_count
        
    def extract_features(self, image):
        self.input_image = image
        img_array = np.array(image)
        img_array = resize(img_array, self.image_size)
        
        features, hog_image = hog(
            img_array,
            orientations=self.band_count,
            pixels_per_cell=self.cell_size,
            cells_per_block=self.block_size,
            visualize=True,
            channel_axis=-1
        )
        self.hog_image = hog_image
        return features
    
    def visualize(self):
        if self.input_image is None or self.hog_image is None:
            return
        
        plt.figure(figsize=(10, 5))
        
        plt.subplot(121)
        plt.title(&amp;#39;Original Image&amp;#39;)
        plt.imshow(self.input_image)
        plt.axis(&amp;#39;off&amp;#39;)
        
        plt.subplot(122)
        plt.title(&amp;#39;HOG Visualization&amp;#39;)
        plt.imshow(self.hog_image, cmap=&amp;#39;gray&amp;#39;)
        plt.axis(&amp;#39;off&amp;#39;)
        
        plt.tight_layout()
        plt.show()&lt;/pre&gt;&lt;pre data-filename=&quot;color_histogram.py&quot; data-language=&quot;py&quot; id=&quot;isPasted&quot;&gt;import numpy as np
import matplotlib.pyplot as plt

class ColorHistogramExtractor:
    def __init__(self, bins=256, channels=3):
        self.bins = bins
        self.channels = channels
        self.image = None
        self.histograms = None
        self.colors = [&amp;#39;red&amp;#39;, &amp;#39;green&amp;#39;, &amp;#39;blue&amp;#39;]
        self.channel_names = [&amp;#39;Red&amp;#39;, &amp;#39;Green&amp;#39;, &amp;#39;Blue&amp;#39;]
        
    def load_image(self, pil_image):
        self.image = np.array(pil_image)
        return self
        
    def extract_features(self, image_array=None, normalize=True):
        if image_array is not None:
            self.load_image(image_array)
            
        self.histograms = []
        for channel in range(self.channels):
            histogram, _ = np.histogram(
                # Selects all pixels for a specific color channel (R, G, or B) using numpy&amp;#39;s ellipsis notation
                # and flattens the 2D array of pixel values into a 1D array
                self.image[..., channel].ravel(),
                bins=self.bins, # divides the range into equal-width bins
                range=(0, 256)
            )
            
            if normalize:
                histogram = histogram / histogram.sum()
                
            self.histograms.append(histogram)
            
        return np.concatenate(self.histograms)

    def visualize(self):
        plt.figure(figsize=(10, 6))
        
        # Plot original image
        plt.subplot(2, 1, 1)
        plt.title(&amp;#39;Original Image&amp;#39;)
        plt.imshow(self.image)
        plt.axis(&amp;#39;off&amp;#39;)
        
        # Plot histograms as bars
        plt.subplot(2, 1, 2)
        plt.title(&amp;#39;Color Histograms&amp;#39;)
        
        x = np.linspace(0, 1, self.bins)  # Normalized x-axis [0,1]
        bar_width = 1.0 / self.bins
        
        for channel in range(self.channels):
            plt.bar(x, self.histograms[channel], 
                color=self.colors[channel], 
                label=self.channel_names[channel],
                alpha=0.3,
                width=bar_width)
            
        plt.xlabel(&amp;#39;Pixel Intensity&amp;#39;)
        plt.ylabel(&amp;#39;Frequency&amp;#39;)
        plt.legend()
        plt.grid(True, alpha=0.3)
        plt.xlim(0, 1)  # Set x-axis limits to [0,1]
        
        plt.tight_layout()
        plt.show()&lt;/pre&gt;&lt;h2&gt;Flower classifier&lt;/h2&gt;&lt;p&gt;In this last section, we implement a flower classifier using HOG features, color histograms, and a Random Forest Classifier. The classifier is trained on the above mentioned &lt;strong id=&quot;isPasted&quot;&gt;17 Category Flower Dataset&lt;/strong&gt;, which consists of 17 flower classes. The following is a short overview of the workflow:&amp;nbsp;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Feature Extraction&lt;/strong&gt;: We use a combination of &lt;strong&gt;HOG features&lt;/strong&gt; (to capture shape and texture information) and &lt;strong&gt;color histograms&lt;/strong&gt; (to leverage color distribution in the images). These features provide a rich representation of each image containing a flower&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Training and Testing Splits&lt;/strong&gt;: The original dataset was split into 3 different training, validation and test sets. For our use case all of the different training sets and test sets were combined to form a complete training and testing set, containing 2040 and 1040 samples respectively&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Model Pipeline&lt;/strong&gt;: A pipeline is constructed with a &lt;strong&gt;Standard Scaler&lt;/strong&gt; to normalize the feature values to a similar scale in order to remove any potential bias and a &lt;strong&gt;Random Forest Classifier&lt;/strong&gt;&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;pre id=&quot;isPasted&quot; data-prompt=&quot;C:\User\...&gt;&quot;&gt;pip install scikit-learn scipy numpy pillow tqdm&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;pre data-filename=&quot;main.py&quot; data-language=&quot;py&quot; id=&quot;isPasted&quot;&gt;from hog import HOGExtractor
from color_histogram import ColorHistogramExtractor
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
import os
import scipy.io
import numpy as np
from PIL import Image
from tqdm import tqdm

# Constants
IMAGE_SIZE = (256, 256)
BIN_COUNT = 9

# Load dataset splits
datasplits = scipy.io.loadmat(&amp;#39;datasplits.mat&amp;#39;)
dataset_path = &amp;#39;./17flowers/jpg&amp;#39;
image_files = sorted([f for f in os.listdir(dataset_path) if f.endswith(&amp;#39;.jpg&amp;#39;)])

# Initialize feature extractors
hog_extractor = HOGExtractor(image_size=IMAGE_SIZE)
color_extractor = ColorHistogramExtractor(bins=BIN_COUNT)

def load_and_extract_features(indices):
    features_list = []
    labels = []
    
    for idx in tqdm(indices, desc=&amp;quot;Extracting features&amp;quot;):
        # Load image
        img_path = os.path.join(dataset_path, image_files[idx-1])
        img = Image.open(img_path)
        
        # Create label (17 classes, indexed 0-16)
        label = (idx-1) // 80  # Each class has 80 images
        labels.append(label)
        
        # Extract features
        hog_features = hog_extractor.extract_features(img)
        color_features = color_extractor.extract_features(img)
        
        # Combine features
        combined_features = np.concatenate([hog_features, color_features])
        features_list.append(combined_features)
    
    return np.array(features_list), np.array(labels)

# The original dataset has 3 training, testing, validation splits
# We combine all of the training and testing data into a single set
all_train_indices = np.concatenate([
    datasplits[f&amp;#39;trn{split}&amp;#39;].ravel() 
    for split in range(1, 4)
])
all_test_indices = np.concatenate([
    datasplits[f&amp;#39;tst{split}&amp;#39;].ravel() 
    for split in range(1, 4)
])
    
# Extract features for current split
X_train, y_train = load_and_extract_features(all_train_indices)
X_test, y_test = load_and_extract_features(all_test_indices)

pipeline = Pipeline([
    (&amp;#39;scaler&amp;#39;, StandardScaler()),
    (&amp;#39;classifier&amp;#39;, RandomForestClassifier(
        n_estimators=300,
        max_depth=50,
        min_samples_split=5,
        min_samples_leaf=2,
        max_features=&amp;#39;sqrt&amp;#39;,
        class_weight=&amp;#39;balanced_subsample&amp;#39;,
    ))
])

# Fit and evaluate
pipeline.fit(X_train, y_train)
train_accuracy = pipeline.score(X_train, y_train)
test_accuracy = pipeline.score(X_test, y_test)
print(f&amp;quot;Train accuracy: {train_accuracy:.3f}, Test accuracy: {test_accuracy:.3f}&amp;quot;)&lt;/pre&gt;&lt;p&gt;&lt;img alt=&quot;Gradients-Blog-Classifier-results.png&quot; src=&quot;https://imgix.cosmicjs.com/40fb97a0-c5d9-11ef-8a63-eb57d6c77a36-Gradients-Blog-Classifier-results.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;The training accuracy of 1.0 (100%) with test accuracy of 0.946 (94.6%) indicates some overfitting, but it is not necessarily problematic in this case, because of the nature of Random Forests. Random Forests could achieve 100% training accuracy through their tree structure and ensemble nature, especially since the chosen model parameters create many deep trees, which eventually have reaching leaf nodes that contain samples from just one class.&lt;/p&gt;&lt;h2&gt;Wrapping up&lt;/h2&gt;&lt;p id=&quot;isPasted&quot;&gt;And that is a wrap! By now you should have a solid grasp of inner workings and/or implementation of HOG and how it can be combined with color histograms to build a feature-rich representation of images. These techniques are fantastic for tasks like object detection and classification. The Random Forest classifier performed pretty well on the flower dataset, but there is always room for improvement, maybe experimenting with other classifiers or tuning hyperparameters could get even better results.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p id=&quot;isPasted&quot;&gt;For those interested in exploring more, &lt;strong&gt;Local Binary Patterns&lt;/strong&gt; (LBP) is another powerful texture descriptor worth looking into. It is a simple yet effective technique that can complement HOG in certain scenarios.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Ultimately, the beauty of these methods lies in their adaptability. There is always more to uncover, more to discover, and sometimes the most interesting results come from just trying out new combinations!&lt;/p&gt;</content:encoded></item><item><title>From Code to Silicon: A Haskell and Clash Odyssey</title><link>https://ernestsrudzitis.com/blog/from-code-to-silicon-a-haskell-and-clash-odyssey/</link><guid isPermaLink="true">https://ernestsrudzitis.com/blog/from-code-to-silicon-a-haskell-and-clash-odyssey/</guid><description>Unlock the power of FPGAs with Clash. This tutorial series covers the fundamentals of hardware development and provides practical examples.</description><pubDate>Sun, 01 Sep 2024 16:58:03 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;&lt;p&gt;Round two: FIGHT! Ready to clash again? If you have been following along, you already know we have covered some important ground. If not, no worries, feel free to hop back to &lt;a href=&quot;https://ernestsrudzitis.com/blog/getting-started-with-clash-haskells-hardware-description-language-for-modern-hardware-design/&quot;&gt;part one&lt;/a&gt; to get up to speed, before joining the action. It will make everything we do here a lot easier to follow.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Anyways, this is the second installment of the series. As promised previously, we will start off by quickly diving into some fundamental Haskell concepts - nothing too heavy, just enough to make sure we are all on the same page, and then move on to the well anticipated hardware development with Clash.&lt;/p&gt;&lt;h2&gt;Haskell fundamentals&lt;/h2&gt;&lt;p&gt;In imperative programming languages (C/C++, Java, Python, et cetera), we accomplish tasks by providing a sequence of code statements to execute. We can have a global state, we can define and re-define variables and utilized various control flow statements. Where as in purely functional programming languages we are much more limited in above mentioned aspects, and we &lt;strong&gt;focus on defining what things are&lt;/strong&gt;, rather than specifying step-by-step actions for the computer to perform. For instance, &lt;em&gt;instead of telling a computer how to calculate each Fibonacci number, in Haskell we would define a Fibonacci number as the sum of the two preceding ones, starting with 0 and 1.&lt;/em&gt;&lt;/p&gt;&lt;h3&gt;Interactive environment&lt;/h3&gt;&lt;p&gt;Assuming you have Haskell installed and ready to go, let&amp;#39;s launch &lt;strong&gt;GHCI&lt;/strong&gt;, the interactive environment of Haskell&amp;#39;s compiler. To launch the interpreter, type&lt;/p&gt;&lt;pre data-prompt=&quot;C:\clash-tutorial&gt;&quot;&gt;ghci&lt;/pre&gt;&lt;p&gt;in command prompt.&lt;/p&gt;&lt;p&gt;You should be greeted with the following output (independent of the version).&lt;img alt=&quot;Screenshot-2024-08-13-181930.png&quot; src=&quot;https://imgix.cosmicjs.com/81794480-5987-11ef-bbaa-af49ecc8228f-Screenshot-2024-08-13-181930.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;/p&gt;&lt;p&gt;Now, at this point, we could directly write and execute haskell code there, but for ease of management and future usage, let&amp;#39;s create a dedicated file. &lt;strong&gt;This is not mandatory, but recommended!&lt;/strong&gt; (Otherwise skip over to the next sub-section).&amp;nbsp;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;I called it &lt;em&gt;&amp;#39;prog.hs&amp;#39;&lt;/em&gt;, the naming/location does not really matter, just make sure to add the appropriate file extension &amp;#39;.hs&amp;#39; and remember the file path.&lt;/li&gt;&lt;li&gt;Now having this file, we can compile it and execute it with a single command.&lt;pre data-prompt=&quot;ghci&gt;&quot;&gt;:load path_to_file/prog.hs&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Having this in place, further on we only have to &amp;#39;reload&amp;#39; the file after every modification we make.&lt;pre data-prompt=&quot;ghci&gt;&quot;&gt;:reload&lt;/pre&gt;&lt;img class=&quot;fr-fil fr-dib&quot; src=&quot;https://imgix.cosmicjs.com/a767e770-59b2-11ef-bbaa-af49ecc8228f-ghci_interpreter.png&quot; alt=&quot;Image of GHCI Interpreter&quot;&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;Type system&lt;/h3&gt;&lt;p&gt;As mentioned in the first series, Haskell has a strict static type system, which means that &lt;em&gt;the compiler knowns the type of every expression during compile time&lt;/em&gt;, thus eliminating any rule violations. So, &lt;em&gt;while it&amp;#39;s not mandatory&lt;/em&gt; to write types ourselves, since the compiler can reason about our code and deduce the most generic types itself, &lt;em&gt;it definitely is a good practice and will be needed later on&lt;/em&gt;, when we start working with Clash.&lt;/p&gt;&lt;p&gt;Let&amp;#39;s try it ourselves!&lt;/p&gt;&lt;pre data-prompt=&quot;ghci&gt;&quot;&gt;:type 1
(con)1 :: Num a =&amp;gt; a&lt;/pre&gt;&lt;p&gt;As we can see, the compiler has deduced the type, and the format is as follows &lt;strong&gt;[Value] :: [Type].&lt;/strong&gt; Here&amp;#39;s a brief breakdown of what it means:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;quot;&lt;em&gt;::&lt;/em&gt;&amp;quot; This symbol is read as &amp;#39;has the type&amp;#39; and is used to specify the type of an expression.&lt;/li&gt;&lt;li&gt;&amp;quot;&lt;em&gt;Num a =&amp;gt; a&lt;/em&gt;&amp;quot; The first part before that arrow (Num a) is a type constraint.&amp;nbsp;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Put together, &lt;em&gt;it simply means that number &amp;#39;1&amp;#39; can be of any type &amp;#39;a&amp;#39;, as long as &amp;#39;a&amp;#39; is part of type class &amp;#39;Num&amp;#39;&lt;/em&gt;. We will get into type classes in the next subsection.&lt;/p&gt;&lt;h3&gt;Classes, primitives &amp;amp; functions&lt;/h3&gt;&lt;p&gt;In Haskell, classes are a way to group together types that share common set of functions or operations. This concept is analogue to interface data type in object-oriented languages. For example, already mentioned above, the &amp;#39;&lt;strong&gt;Num&lt;/strong&gt;&amp;#39; type class includes types that behave like numbers (&lt;em&gt;&amp;#39;Int&amp;#39;, &amp;#39;Float&amp;#39;, &amp;#39;Integer&amp;#39;, &amp;#39;Double&amp;#39;&lt;/em&gt;), and it defines operations such as addition, multiplication, et cetera. &lt;strong&gt;Note!&lt;/strong&gt; Even though types such as &amp;#39;&lt;strong&gt;Int&lt;/strong&gt;&amp;#39; and &amp;#39;&lt;strong&gt;Float&lt;/strong&gt;&amp;#39; are of the same type class, they are still distinct and &lt;strong&gt;can&amp;#39;t&lt;/strong&gt; be mixed directly in operations, &lt;em&gt;you must convert the values to a common type&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Every great language starts with its &amp;#39;alphabet&amp;#39;, Haskell also has it&amp;#39;s primitives, which generally are very similar to those proposed in other languages. To name a few, &lt;em&gt;&amp;#39;Bool&amp;#39;, &amp;#39;Char&amp;#39;, &amp;#39;Int&amp;#39;, et cetera&lt;/em&gt;. While primitive types tend to cover many common needs, we can end up in a situation where defining our own data types is necessary for modeling complex or domain specific concepts. We can define a custom data type using the &amp;#39;&lt;strong&gt;data&lt;/strong&gt;&amp;#39; keyword.&lt;/p&gt;&lt;pre data-language=&quot;haskell&quot; data-filename=&quot;prog.hs&quot;&gt;data CarBrand = Ferrari | Bugatti | Toyota | Ford&lt;/pre&gt;&lt;p&gt;In such a definition, &amp;#39;&lt;strong&gt;CarBrand&lt;/strong&gt;&amp;#39; is the name of the newly introduced data type (&lt;em&gt;on type level&lt;/em&gt;), where as &amp;#39;&lt;strong&gt;Ferrari&lt;/strong&gt;&amp;#39;, &amp;#39;&lt;strong&gt;Bugatti&lt;/strong&gt;&amp;#39;, &amp;#39;&lt;strong&gt;Toyota&lt;/strong&gt;&amp;#39;, and &amp;#39;&lt;strong&gt;Ford&lt;/strong&gt;&amp;#39; are the constructors (&lt;em&gt;on value level&lt;/em&gt;, each of them represent a different value of &amp;#39;CarBrand&amp;#39;).&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Haskell proposes quite a flexible approach when it comes to functions. Functions can be called either by infix or prefix statement.&lt;/p&gt;&lt;p&gt;&lt;img alt=&quot;Image of prefix and infix functions&quot; src=&quot;https://imgix.cosmicjs.com/4be8f730-5b43-11ef-bbaa-af49ecc8228f-Screenshot-2024-08-15-231624.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;Let&amp;#39;s define our own function called &amp;#39;successor&amp;#39; that returns the successor value of a given number.&lt;/p&gt;&lt;pre data-language=&quot;haskell&quot; data-filename=&quot;prog.hs&quot;&gt;successor :: Num a =&amp;gt; a -&amp;gt; a
successor val = val + 1&lt;/pre&gt;&lt;pre data-prompt=&quot;ghci&gt;&quot;&gt;successor 5
(con)6&lt;/pre&gt;&lt;p&gt;Don&amp;#39;t be confused! As visible, haskell separates it&amp;#39;s function arguments by white space rather than having comma separated and enclosed with curly braces.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Let&amp;#39;s turn our attention to pattern matching. &amp;nbsp;that we are running a car rental dealership (we will reuse the already introduced custom type &amp;#39;CarBrand&amp;#39;). We are grateful to own two different supercars, whose rental price greatly varies from the standard rentable cars. A solution we could come up with would be the following:&lt;/p&gt;&lt;pre data-language=&quot;haskell&quot; data-filename=&quot;prog.hs&quot; id=&quot;isPasted&quot;&gt;data CarBrand = Ferrari | Bugatti | Toyota | Ford

rentCost :: CarBrand -&amp;gt; Int 
rentCost Ferrari = 150
rentCost Bugatti = 800
rentCost anyOtherCar = 30&lt;/pre&gt;&lt;p&gt;The function parameters are pattern matched in top-to-down manner, it simply means that provided car brand being &amp;#39;Ferrari&amp;#39; would return the cost as 150, &amp;#39;Bugatti&amp;#39; - 180, where as all of the remaining brands, &amp;#39;Toyota&amp;#39;, &amp;#39;Ford&amp;#39;, would yield the cost 30. While this works, we might run into a problem whose solution requires a different approach - not the regular pattern matching. In such an occasion we would turn towards &lt;em&gt;case expressions&lt;/em&gt; or &lt;em&gt;guards&lt;/em&gt; (examples attached below, respectively).&lt;/p&gt;&lt;pre data-language=&quot;haskell&quot; data-filename=&quot;prog.hs&quot;&gt;rentCost :: CarBrand -&amp;gt; Int 
rentCost car = case car of
    Ferrari -&amp;gt; 150
    Bugatti -&amp;gt; 800
    _ -&amp;gt; 30&lt;/pre&gt;&lt;pre data-language=&quot;haskell&quot; data-filename=&quot;prog.hs&quot;&gt;data CarBrand = Ferrari | Bugatti | Toyota | Ford &lt;span style=&quot;color: rgb(247, 218, 100);&quot;&gt;deriving (Eq)&lt;/span&gt;

rentCost :: CarBrand -&amp;gt; Int 
rentCost car 
    | car == Ferrari = 150
    | car == Bugatti = 800
    | otherwise = 30&lt;/pre&gt;&lt;p&gt;(The last code block that uses guard approach introduces &amp;#39;deriving (Eq)&amp;#39;. &lt;em&gt;Haskell doesn&amp;#39;t know how to evaluate the equivalence of the values for our custom data type&lt;/em&gt;, therefore either we must implement such equivalence class instance ourselves or we can derive from &amp;#39;Eq&amp;#39;, which in simple terms introduces the comparison logic for us).&lt;/p&gt;&lt;h3&gt;Recursion &amp;amp; Higher-order functions&lt;/h3&gt;&lt;p&gt;Recursion and higher-order functions are &lt;em&gt;crucial strategies&lt;/em&gt; to understand in order to solve problems in functional programming approach. The term &amp;#39;recursion&amp;#39; when applied to functions merely refers to the idea that the function has the ability to invoke itself various times, breaking down problems into smaller sub-problems, and terminate when a base condition is met.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Higher-order function&lt;/strong&gt; is a function that takes other functions as arguments or returns a function as result.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;You might ask, what does recursion have to do with higher order functions? Well, these two concepts often work together, with higher-order functions providing a &amp;#39;&lt;em&gt;framework&lt;/em&gt;&amp;#39; (abstraction) for recursive solutions. Let&amp;#39;s put these ideas into action with some concrete examples!&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Summing elements in a list (recursive approach)&lt;/strong&gt;.&lt;/p&gt;&lt;pre data-filename=&quot;prog.hs&quot; data-language=&quot;haskell&quot; id=&quot;isPasted&quot;&gt;sumList :: Num a =&amp;gt; [a] -&amp;gt; a
sumList [] = 0
sumList (x:xs) = x + sumList xs

&lt;/pre&gt;&lt;pre data-prompt=&quot;ghci&gt;&quot;&gt;sumList [1, 2, 3] =
(con)1 + sumList [2, 3] =
(con)1 + (2 + sumList [3]) =
(con)1 + (2 + (3 + sumList [])) =
(con)1 + (2 + (3 + 0)) =
(con)1 + (2 + 3) = 1 + 5 =
(con)6&lt;/pre&gt;&lt;p&gt;Here&amp;#39;s a short breakdown of what is happening:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&amp;quot;&lt;em&gt;Num a =&amp;gt; [a] -&amp;gt; a&lt;/em&gt;&amp;quot; indicates that the function takes a list of numbers, denoted by &lt;strong&gt;[a]&lt;/strong&gt;, and returns a number.&lt;/li&gt;&lt;li&gt;&amp;quot;&lt;em&gt;sumList [] = 0&lt;/em&gt;&amp;quot; defines the base case, which will act as recursion terminator upon being pattern matched to.&lt;/li&gt;&lt;li&gt;&amp;quot;&lt;em&gt;(x:xs)&lt;/em&gt;&amp;quot; pattern matches &lt;strong&gt;x&lt;/strong&gt; to be the list &lt;strong&gt;head&amp;nbsp;&lt;/strong&gt;element and &lt;strong&gt;xs&lt;/strong&gt; to be the &lt;strong&gt;remaining list&lt;/strong&gt;.&lt;/li&gt;&lt;li&gt;&amp;quot;&lt;em&gt;sumList (x:xs) = x + sumList xs&lt;/em&gt;&amp;quot; recursively calculates the sum of the numbers in a list by breaking the problem into sub-problems (first element in the list + the first element of the remaining list, and so on...)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong id=&quot;isPasted&quot;&gt;Summing elements in a list (higher-order function approach)&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;An alternative, often more elegant approach would be to use built in higher-order functions, for example, &lt;a href=&quot;https://zvon.org/other/haskell/Outputprelude/foldl_f.html&quot;&gt;foldl&lt;/a&gt;.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Foldl takes the second argument and the first item of the list and applies the function to them, then feeds the function with this result and the second argument and so on.&lt;/p&gt;&lt;/blockquote&gt;&lt;pre data-filename=&quot;prog.hs&quot; data-language=&quot;haskell&quot; id=&quot;isPasted&quot;&gt;sumListHigherOrder :: Num a =&amp;gt; [a] -&amp;gt; a
sumListHigherOrder x = foldl (+) 0 x

&lt;/pre&gt;&lt;p&gt;To have a clearer understanding of &amp;#39;&lt;strong&gt;foldl&lt;/strong&gt;&amp;#39;, let&amp;#39;s see the arguments and their types it expects.&lt;/p&gt;&lt;pre data-prompt=&quot;ghci&gt;&quot; id=&quot;isPasted&quot;&gt;:type foldl
(con)foldl :: Foldable t =&amp;gt; (b -&amp;gt; a -&amp;gt; b) -&amp;gt; b -&amp;gt; t a -&amp;gt; b&lt;/pre&gt;&lt;p&gt;Essentially, &amp;#39;&lt;strong&gt;foldl&lt;/strong&gt;&amp;#39; works on any &amp;#39;&lt;em&gt;Foldable&lt;/em&gt;&amp;#39; structure, such as lists, trees, et cetera. The first argument &amp;#39;&lt;em&gt;(b -&amp;gt; a -&amp;gt; b)&lt;/em&gt;&amp;#39; is a binary function (in our case an accumulator function), that takes an accumulated value &amp;#39;&lt;em&gt;b&lt;/em&gt;&amp;#39; (initially 0), an element from a Foldable typeclass (in our case an element from a list), and returns a new accumulated value &amp;#39;&lt;em&gt;b&lt;/em&gt;&amp;#39; (performed by the &amp;#39;+&amp;#39; function). Argument &amp;#39;&lt;em&gt;b&lt;/em&gt;&amp;#39; is the initial accumulator value, &amp;#39;&lt;em&gt;t a&lt;/em&gt;&amp;#39; represents a foldable element (list) of type &amp;#39;a&amp;#39;.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Why bother with &amp;#39;foldl&amp;#39; or higher-order functions in particular?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Consider them your hidden weapon in functional programming, akin to upgrading from a sedan to a sleek sports car. They make your code more concise, and in addition can introduce performance optimizations when working with complex or large data.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;But there&amp;#39;s more to the story!&lt;/strong&gt; Here&amp;#39;s the real kicker, &lt;strong&gt;Clash struggles with recursion&lt;/strong&gt; when translating Haskell code into hardware description languages, but &lt;em&gt;we are allowed to use higher-order functions&lt;/em&gt; that capture our recursive function patterns.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;We&amp;#39;ll dive deeper into that in the following section, but as a conclusion to take home, just know that using higher-order functions isn&amp;#39;t just about writing elegant Haskell.&lt;/p&gt;&lt;h2&gt;Introducing Clash&lt;/h2&gt;&lt;p&gt;&lt;em&gt;You&amp;#39;ve trained, you&amp;#39;ve prepared, now it&amp;#39;s time to Clash!&lt;/em&gt; The main event starts now. Anyways, after all of the pre-requisites we have finally arrived at the core of the clash series - the part where we actually start working on hardware design. Let&amp;#39;s not beat around the bush, and jump straight into some action!&lt;/p&gt;&lt;h3&gt;Interactive environment&lt;/h3&gt;&lt;p&gt;In a similar manner, let&amp;#39;s proceed to launch &lt;strong&gt;clashi&lt;/strong&gt; - the interactive environment of Clash compiler.&lt;/p&gt;&lt;pre data-prompt=&quot;C:\clash-tutorial&gt;&quot;&gt;clashi&lt;/pre&gt;&lt;p&gt;We should be greeted with the following message (ignore the warning).&lt;/p&gt;&lt;p&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;Image of clashi interactive environment&quot; src=&quot;https://imgix.cosmicjs.com/87398440-6092-11ef-a492-5bdc7520fe60-Screenshot-2024-08-22-172608.png&quot;&gt;It might take a moment for &lt;strong&gt;clashi&amp;nbsp;&lt;/strong&gt;to load. Be patient, the fun&amp;#39;s almost here.&lt;/p&gt;&lt;h3&gt;Combinational design&lt;/h3&gt;&lt;p&gt;We will start off simple by creating a combinational circuit design. I&amp;#39;ve included a definition of combinational logic for those of you who might not be too familiar.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Combinational logic is a type of digital logic that is implemented by Boolean circuits, where the output is a pure function of the &lt;strong&gt;present input only&lt;/strong&gt;. This is in contrast to sequential logic, in which the output depends not only on the present input but also on the history of the input.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;If this still puzzles your mind, don&amp;#39;t fret, a practical demonstration will shed some light.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Let&amp;#39;s build our first digital circuit together!&lt;/strong&gt; We will start off with a primitive yet fundamental building block - the &amp;#39;&lt;strong&gt;AND&lt;/strong&gt;&amp;#39; gate. It produces a 1 only when both of its inputs are 1. Create a file named &amp;#39;&lt;strong&gt;ANDGate.hs&lt;/strong&gt;&amp;#39; with the following initial code:&lt;/p&gt;&lt;pre data-filename=&quot;ANDGate.hs&quot; data-language=&quot;haskell&quot;&gt;module ANDGate where
import Clash.Prelude&lt;/pre&gt;&lt;p&gt;These preliminaries purely help preventing naming conflicts with other modules or the main program and imports the main clash library - Prelude.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;We can define our &amp;#39;AND&amp;#39; gate in the following way:&lt;/p&gt;&lt;pre data-filename=&quot;ANDGate.hs&quot; data-language=&quot;haskell&quot;&gt;module ANDGate where
import Clash.Prelude

andBit :: Bit -&amp;gt; Bit -&amp;gt; Bit
andBit x y = x .&amp;amp;. y&lt;/pre&gt;&lt;p&gt;We observe something new. The function &amp;#39;&lt;em&gt;andBit&lt;/em&gt;&amp;#39; takes two &amp;#39;&lt;em&gt;Bit&lt;/em&gt;&amp;#39; values as input and returns a resulting &amp;#39;&lt;em&gt;Bit&lt;/em&gt;&amp;#39; value (0 or 1). It uses the bitwise AND operator &amp;#39;&lt;em&gt;.&amp;amp;.&lt;/em&gt;&amp;#39; to perform the operation on both input bits.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Load your file in the same manner as before and test the functionality.&lt;/p&gt;&lt;pre data-prompt=&quot;clashi&gt;&quot;&gt;:load path_to_file/prog.hs&lt;/pre&gt;&lt;pre data-prompt=&quot;clashi&gt;&quot;&gt;andBit 1 0
(con)0&lt;/pre&gt;&lt;pre data-prompt=&quot;clashi&quot;&gt;andBit 1 1
(con)1&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;That&amp;#39;s it&lt;/strong&gt;, you have just created your first digital circuit. Let&amp;#39;s bring it to life by &lt;em&gt;visualizing and analyzing its inner workings&lt;/em&gt; in the following subsection.&lt;/p&gt;&lt;h3&gt;Visualizing circuits&lt;/h3&gt;&lt;p&gt;Remember that AMD Xilinx tool we installed in the first series? Now it&amp;#39;s time to put it to work! But let&amp;#39;s not get ahead of ourselves, we have couples of things to do first:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;We need to tell Clash which function should be compiled down to HDL&lt;/li&gt;&lt;li&gt;We need to compile the Clash code to our preferred HDL (either Verilog or VHDL)&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;To tackle the first endeavour let&amp;#39;s modify &amp;#39;ANDGate.hs&amp;#39; file.&lt;/p&gt;&lt;pre data-language=&quot;haskell&quot; data-filename=&quot;ANDGate.hs&quot;&gt;module ANDGate where
import Clash.Prelude

andBit :: Bit -&amp;gt; Bit -&amp;gt; Bit
andBit x y = x .&amp;amp;. y

topEntity :: Bit -&amp;gt; Bit -&amp;gt; Bit
topEntity = andBit&lt;/pre&gt;&lt;p&gt;The new addition is visible in line&amp;#39;s 7 and 8. We have introduced a new function called &amp;#39;&lt;strong&gt;topEntity&lt;/strong&gt;&amp;#39;, which serves as the entry point for our circuit.&amp;nbsp;&lt;/p&gt;&lt;p&gt;When Clash is compiling our code to hardware description language, it needs to be aware of which function represents the top-level circuit (in our case &amp;#39;&lt;strong&gt;andBit&lt;/strong&gt;&amp;#39;) - the circuit that connects to the &amp;#39;external world&amp;#39; (by means of IO, et cetera). In essence, &amp;#39;&lt;em&gt;topEntity&lt;/em&gt;&amp;#39; informs Clash: &amp;quot;This is the function you should focus on when generating the hardware description language code&amp;quot;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;To tackle the second step, we have to exit the &amp;#39;&lt;em&gt;clashi&lt;/em&gt;&amp;#39; environment (or open up a new command prompt window). Having done that, let&amp;#39;s execute the following command:&lt;/p&gt;&lt;pre data-prompt=&quot;C:\clash-tutorial&gt;&quot;&gt;clash --verilog path_to_file/ANDGate.hs&lt;/pre&gt;&lt;p&gt;Give it a moment to complete. Once done, you will find a new folder named &amp;#39;&lt;em&gt;verilog/ANDGate&lt;/em&gt;&amp;#39; containing file &amp;#39;&lt;strong&gt;topEntity.v&lt;/strong&gt;&amp;#39;. This file contains our clash code in the format of hardware description. You can briefly examine it and try to identify the corresponding statements that implement the &amp;#39;&lt;em&gt;andBit&lt;/em&gt;&amp;#39; function, but we will not delve into specifics, however &lt;em&gt;keep this file handy, as we will need it shortly&lt;/em&gt;.&lt;/p&gt;&lt;pre data-language=&quot;verilog&quot; data-filename=&quot;topEntity.v&quot;&gt;/* AUTOMATICALLY GENERATED VERILOG-2001 SOURCE CODE.
** GENERATED BY CLASH 1.8.1. DO NOT MODIFY.
*/
`default_nettype none
`timescale 100fs/100fs
module topEntity
    ( // Inputs
      input wire  c$arg
    , input wire  c$arg_0

      // Outputs
    , output wire  result
    );

  assign result = c$arg &amp;amp; c$arg_0;

endmodule&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Great, now let&amp;#39;s open &amp;#39;&lt;strong&gt;AMD Vivado&lt;/strong&gt;&amp;#39; and load our design. &lt;em&gt;Use the image below as a guide for the next steps&lt;/em&gt;. We will be creating a new project, adding our Verilog file (&lt;strong&gt;topEntity.v&lt;/strong&gt;), and setting it as the top-level module. These steps will configure &amp;#39;&lt;em&gt;Vivado&lt;/em&gt;&amp;#39; to let us work with our AND gate design.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;width: 222px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;Creating a project in AMD Vivado&quot; src=&quot;https://imgix.cosmicjs.com/a408d8e0-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_1-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Create a new project&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;width: 222px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;Proceeding with AMD Vivado project creation&quot; src=&quot;https://imgix.cosmicjs.com/a2cc8850-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_2-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Proceed&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;width: 222px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;Naming and location of project&quot; src=&quot;https://imgix.cosmicjs.com/a396b620-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_3_edited-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 3&lt;/strong&gt;: Give your project a recognizable name, the default location is fine&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;width: 222px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;Project type selection step&quot; src=&quot;https://imgix.cosmicjs.com/a323a900-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_4-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 4&lt;/strong&gt;: Select the type to be RTL Project&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;width: 736px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;Adding source files to our project&quot; src=&quot;https://imgix.cosmicjs.com/a32110f0-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_5-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 5&lt;/strong&gt;: From the dropdown select &amp;#39;Add Files...&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;width: 736px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;Locating and selecting the &apos;topEntity.v&apos; file&quot; src=&quot;https://imgix.cosmicjs.com/a3b98060-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_6_edited-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 6&lt;/strong&gt;: Locate the &amp;#39;topEntity.v&amp;#39; file&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;width: 736px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;vivado_blog_2_project_walkthrough_8-min.png&quot; src=&quot;https://imgix.cosmicjs.com/a375e7b0-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_8-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 7&lt;/strong&gt;: Project summary, proceed&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;width: 736px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;Selecting default development board&quot; src=&quot;https://imgix.cosmicjs.com/a3489620-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_7-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 8&lt;/strong&gt;: Select the FPGA development board. This tutorial doesn&amp;#39;t assume you have a physical board, we will be only simulating our designs. So, in this case the selected board choice does not really matter, but the board we opt for is &amp;#39;Kria KV260 Vision AI Starter Kit SOM&amp;#39;.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;width: 736px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;Generating RTL design&quot; src=&quot;https://imgix.cosmicjs.com/a4251370-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_9_edited-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 9&lt;/strong&gt;: Click on &amp;#39;Open Elaborated Design&amp;#39; and wait for the process to finish&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;width: 736px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot; id=&quot;figure-rtl-schematic-1&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;Schematic window with the RTL design&quot; src=&quot;https://imgix.cosmicjs.com/a3648290-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_10-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 10&lt;/strong&gt;: New &amp;#39;Schematic&amp;#39; window appears that contains our design in RTL (Register-transfer level)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;width: 736px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;Optional part of synthesizing our design&quot; src=&quot;https://imgix.cosmicjs.com/a4358e30-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_11_edited-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 11&lt;/strong&gt;: (Optional) Run Synthesis&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;width: 552px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img class=&quot;fr-fil fr-dib&quot; alt=&quot;Optional part of synthesizing our design&quot; src=&quot;https://imgix.cosmicjs.com/a3879af0-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_12-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 12&lt;/strong&gt;: (Optional) Press &amp;#39;OK&amp;#39;. After finished, choose &amp;#39;Schematic&amp;#39; under &amp;#39;Open Synthesized Design&amp;#39; dropdown&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;width: 736px;&quot; class=&quot;fr-img-caption fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img id=&quot;figure-rtl-schematic-2&quot; class=&quot;fr-fil fr-dib&quot; alt=&quot;Schematic window of synthesized design&quot; src=&quot;https://imgix.cosmicjs.com/a3809610-6200-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_13-min.png&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 13&lt;/strong&gt;: New &amp;#39;Schematic&amp;#39; window appears that contains our synthesized design (Note the difference, and remember, FPGA&amp;#39;s don&amp;#39;t contain AND gates!)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;h3&gt;Analyzing results&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;#figure-rtl-schematic-1&quot;&gt;Figure 1&lt;/a&gt; shows the Register Transfer Level (&lt;strong&gt;RTL&lt;/strong&gt;) schematic of our design. RTL schematic is a high-level abstraction of a circuit that models the flow between various components. In our case, it&amp;#39;s made up of two input signals, whose names clash had generated based on the context it could perceive. These inputs are fed into an &amp;#39;AND&amp;#39; gate, and the output of the AND gate represents the result of logical operation by our &amp;#39;andBit&amp;#39; function.&amp;nbsp;&lt;/p&gt;&lt;p&gt;While the RTL schematic depicts an AND gate for clarity and simplicity purposes, FPGAs actually implement logic functions using Look-up Tables (LUTs) instead (read more about it in the &lt;a href=&quot;http://ernestsrudzitis.com/blog/getting-started-with-clash-haskells-hardware-description-language-for-modern-hardware-design#what-is-an-fpga&quot; rel=&quot;noopener noreferrer noopener noreferrer&quot;&gt;part one&lt;/a&gt; of the series). &lt;a href=&quot;#figure-rtl-schematic-2&quot;&gt;Figure 2&lt;/a&gt; portrays that, it shows the corresponding LUT, named &amp;#39;result_OBUF_instr_i_1&amp;#39;, which is configured to perform the AND operation.&lt;/p&gt;&lt;h3&gt;Sequential design&lt;/h3&gt;&lt;p&gt;While combinatorial designs are great for certain applications, the world of digital circuits quite often demands more dynamic and sophisticated solutions. On this note, I welcome you to &lt;strong&gt;sequential designs&lt;/strong&gt;. Sequential designs introduce circuit elements like &lt;em&gt;registers&amp;nbsp;&lt;/em&gt;(memory units) and a &lt;em&gt;clock signal&lt;/em&gt; (periodic signal that allows us to synchronize the circuit). With these elements established we empower our circuits to keep track of past states and react to changes over time. This in turn opens up a vast array of possibilities - from creating counters and timers to implementing complex state machines.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;To better understand sequential design development, let&amp;#39;s create a &lt;strong&gt;counter&amp;nbsp;&lt;/strong&gt;that counts up or down based on the provided input control signal. We will employ a single register for keeping track of the current counter value, and a clock signal to ensure precise timing. Let&amp;#39;s create a file named &amp;#39;&lt;strong&gt;Counter.hs&lt;/strong&gt;&amp;#39; and populate it with the following contents.&lt;/p&gt;&lt;pre id=&quot;isPasted&quot; data-language=&quot;haskell&quot; data-filename=&quot;Counter.hs&quot;&gt;module Counter where
import Clash.Prelude

type Val = Unsigned 3

incrementer :: Val -&amp;gt; Val
incrementer v = v + 1

decrementer :: Val -&amp;gt; Val 
decrementer v = v - 1

counter :: (HiddenClockResetEnable dom) =&amp;gt; Signal dom Bool -&amp;gt; Signal dom Val 
counter incr = state
    where 
        state = register 0 (mux incr (incrementer &amp;lt;$&amp;gt; state) (decrementer &amp;lt;$&amp;gt; state))&lt;/pre&gt;&lt;p&gt;&lt;em&gt;Don&amp;#39;t worry if this feels a bit overwhelming at first&lt;/em&gt;, we will break down the code step by step.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;At &lt;strong&gt;line 4&lt;/strong&gt;, we start off by defining a type alias &amp;#39;Val&amp;#39; that simply represents an unsigned 3-bit integer, meaning that it can hold values from 0 to 2^3-1. It mainly serves its purpose as a pseudonym making our code simpler and easier to read.&lt;/p&gt;&lt;p&gt;At &lt;strong&gt;line 6&lt;/strong&gt; through &lt;strong&gt;10&lt;/strong&gt;, we define a simple helper functions that increment or decrement our value.&lt;/p&gt;&lt;p&gt;At &lt;strong&gt;line 12&lt;/strong&gt;, we have arrived at the type signature of our counter function. This is where things get exciting.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;(HiddenClockResetEnable dom)&lt;/strong&gt;: This is a type constraint that ensures that the function will operate in a special domain where the clock, reset and enable signals are &lt;em&gt;automatically handled for us&lt;/em&gt;. Which means that we don&amp;#39;t have to worry about manually managing these signals.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Signal dom Bool -&amp;gt; Signal dom Val&lt;/strong&gt;: This indicates that our function accepts a boolean signal as an input and returns a signal of type &amp;#39;Val&amp;#39; while operating under the special domain.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;At &lt;strong&gt;line 13&lt;/strong&gt; through &lt;strong&gt;15&lt;/strong&gt;, we encounter the body of our counter function. The &amp;#39;&lt;strong&gt;state&lt;/strong&gt;&amp;#39; is stored in a &lt;strong&gt;register&lt;/strong&gt;, just as we had planned. Let&amp;#39;s explore the type signature of a register element.&lt;/p&gt;&lt;pre data-prompt=&quot;clashi&gt;&quot;&gt;:type register
(con)register
(con)  :: (HiddenClockResetEnable dom, NFDataX a) =&amp;gt;
(con)     a -&amp;gt; Signal dom a -&amp;gt; Signal dom a&lt;/pre&gt;&lt;p&gt;As visible, a register expects an initial value and a signal as input of the same type. Navigating by this guide, we &lt;em&gt;initialize our register with a&lt;/em&gt; &lt;strong&gt;0&lt;/strong&gt;.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Furthermore in our case, the input signal to the register is determined by a multiplexer &amp;#39;&lt;strong&gt;mux&lt;/strong&gt;&amp;#39; that selects the first part &amp;#39;&lt;em&gt;(incrementer &amp;lt;$&amp;gt; state)&lt;/em&gt;&amp;#39; given that boolean signal &amp;#39;&lt;strong&gt;incr&lt;/strong&gt;&amp;#39; is True, otherwise the second part &amp;#39;&lt;em&gt;(decrementer &amp;lt;$&amp;gt; state)&lt;/em&gt;&amp;#39; is selected.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;However, the current &amp;#39;state&amp;#39; is on&amp;nbsp;&lt;/strong&gt;&lt;strong&gt;signal level&lt;/strong&gt;, where as our &amp;#39;&lt;em&gt;incrementer&lt;/em&gt;&amp;#39; and &amp;#39;&lt;em&gt;decrementer&lt;/em&gt;&amp;#39; helper functions work with raw values, thus we we employ &lt;strong&gt;fmap&lt;/strong&gt; (&lt;strong&gt;&amp;lt;$&amp;gt;&lt;/strong&gt;), which applies a function to the values inside the signal, to correctly update it.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Great, let&amp;#39;s simulate our implementation. Add the following line of code at the bottom of the file.&lt;/p&gt;&lt;pre data-language=&quot;haskell&quot; id=&quot;isPasted&quot;&gt;simulateCounter = simulate @System counter [True, True, True, False, False, False]&lt;/pre&gt;&lt;p&gt;With that in place, load the file and run the simulation.&lt;/p&gt;&lt;pre data-prompt=&quot;clashi&gt;&quot;&gt;:load Counter.hs&lt;/pre&gt;&lt;pre data-prompt=&quot;clashi&gt;&quot;&gt;simulateCounter
(con)[0,1,2,3,2,1,0,*** Exception: X: finite list
(con)CallStack (from HasCallStack):
(con)...&lt;/pre&gt;&lt;p&gt;As visible, we can observe that our sequential design has the expected behavour. The counter was incremented three times and then decremented three times to end up at result 0.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Please not that the initial 0 in the output is a known artifact and can be ignored&lt;/strong&gt;. It can be thought of as some kind of noise being spat out just as the &amp;#39;system starts up&amp;#39;.&lt;/p&gt;&lt;h3&gt;Visualization &amp;amp; analysis of the counter&lt;/h3&gt;&lt;p&gt;To visualize our sequential circuit, we will follow a process similar to that of our combinatorial design. First and foremost, let&amp;#39;s add the &amp;#39;&lt;strong&gt;topEntity&lt;/strong&gt;&amp;#39; definition. The final code looks as following:&lt;/p&gt;&lt;pre data-filename=&quot;Counter.hs&quot; data-language=&quot;haskell&quot; id=&quot;isPasted&quot;&gt;module Counter where
import Clash.Prelude

type Val = Unsigned 3

incrementer :: Val -&amp;gt; Val
incrementer v = v + 1

decrementer :: Val -&amp;gt; Val 
decrementer v = v - 1

counter :: (HiddenClockResetEnable dom) =&amp;gt; Signal dom Bool -&amp;gt; Signal dom Val 
counter incr = state
    where 
        state = register 0 (mux incr (incrementer &amp;lt;$&amp;gt; state) (decrementer &amp;lt;$&amp;gt; state))

simulateCounter = simulate @System counter [True, True, True, False, False, False]

topEntity :: Clock System -&amp;gt; Reset System -&amp;gt; Signal System Bool -&amp;gt; Signal System Val
topEntity clk rst incr = withClockResetEnable clk rst enableGen counter incr&lt;/pre&gt;&lt;p&gt;With that in place, in a separate command prompt lets compile our Clash code into Verilog.&lt;/p&gt;&lt;pre data-prompt=&quot;C:\clash-tutorial&gt;&quot;&gt;clash --verilog path_to_file/Counter.hs&lt;/pre&gt;&lt;p&gt;A new folder is created &amp;#39;&lt;em id=&quot;isPasted&quot;&gt;verilog/Counter&lt;/em&gt;&amp;#39; containing &amp;#39;&lt;strong&gt;topEntity.v&lt;/strong&gt;&amp;#39; that contains the translated code.&lt;/p&gt;&lt;pre data-filename=&quot;topEntity.v&quot; data-language=&quot;verilog&quot; id=&quot;isPasted&quot;&gt;/* AUTOMATICALLY GENERATED VERILOG-2001 SOURCE CODE.
** GENERATED BY CLASH 1.8.1. DO NOT MODIFY.
*/
`default_nettype none
`timescale 100fs/100fs
module topEntity
    ( // Inputs
      input wire  clk // clock
    , input wire  rst // reset
    , input wire  incr

      // Outputs
    , output wire [2:0] state
    );
  // Counter.hs:12:1-76
  reg [2:0] state_1 = 3&amp;#39;d0;
  // Counter.hs:12:1-76
  wire [2:0] t;
  // Counter.hs:12:1-76
  wire [2:0] f1;
  wire [2:0] result;

  // register begin
  always @(posedge clk or  posedge  rst) begin : state_1_register
    if ( rst) begin
      state_1 &amp;lt;= 3&amp;#39;d0;
    end else begin
      state_1 &amp;lt;= result;
    end
  end
  // register end

  assign t = state_1 + 3&amp;#39;d1;
  assign f1 = state_1 - 3&amp;#39;d1;
  assign result = incr ? t : f1;
  assign state = state_1;

endmodule&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;To visualize it we have to return back to &amp;#39;&lt;strong&gt;AMD Vivado&lt;/strong&gt;&amp;#39;. Luckily, we don&amp;#39;t have to go through all of the steps again, instead we can simply replace the previous &amp;#39;topEntity.v&amp;#39; file in our project with the new one.&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;fr-img-caption fr-fil fr-dib&quot; style=&quot;width: 512px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;Removal of existing &apos;topEntity.v&apos; file&quot; src=&quot;https://imgix.cosmicjs.com/d85ad4c0-6621-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_2_1-min.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Remove the existing &amp;#39;topEntity.v&amp;#39; file from sources&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;fr-img-caption fr-fil fr-dib&quot; style=&quot;width: 510px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;Adding a new design source&quot; src=&quot;https://imgix.cosmicjs.com/d86c12d0-6621-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_2_2-min.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 2&lt;/strong&gt;: Choose option to add new sources&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;fr-img-caption fr-fil fr-dib&quot; style=&quot;width: 581px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;Adding files&quot; src=&quot;https://imgix.cosmicjs.com/d86c8800-6621-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_2_3-min.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 3&lt;/strong&gt;: Choose &amp;#39;Add Files&amp;#39; option&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;fr-img-caption fr-fil fr-dib&quot; style=&quot;width: 581px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;Selecting the new &apos;topEntity.v&apos; file&quot; src=&quot;https://imgix.cosmicjs.com/d85c3450-6621-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_2_4-min.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 4&lt;/strong&gt;: Select the &amp;#39;topEntity.v&amp;#39; source file for the counter circuit design&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;fr-img-caption fr-fil fr-dib&quot; style=&quot;width: 581px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img id=&quot;figure-rtl-schematic-2-1&quot; alt=&quot;Schematic window of the counter circuit&quot; src=&quot;https://imgix.cosmicjs.com/940dde00-6623-11ef-a492-5bdc7520fe60-vivado_blog_2_project_walkthrough_2_5-min.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;&lt;strong&gt;Step 5&lt;/strong&gt;: Click on &amp;#39;Open Elaborated Design&amp;#39; and wait for the process to finish. New &amp;#39;Schematic&amp;#39; window appears that contains our design in RTL (Register-transfer level)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;In &lt;a href=&quot;#figure-rtl-schematic-2-1&quot;&gt;Figure 3&lt;/a&gt;, you can see the corresponding design of our developed circuit. It contains three input signals - reset, clock and increment, a multiplexer and a register.&lt;/p&gt;&lt;h2&gt;Wrapping up&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Congratulations!&lt;/strong&gt; You have successfully completed the second and final part of our Clash journey. We have delved into the fundamentals of Clash, explored combinatorial and sequential designs, and even created our own counter circuit.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;However, the journey does not necessarily end here&lt;/strong&gt;... There&amp;#39;s a lot more to explore both in semantics and features of Clash and hardware development itself.&lt;/p&gt;&lt;p&gt;If you have purchased a physical FPGA board (which tend to be on the pricier end), you can generate a &lt;strong&gt;bitstream&lt;/strong&gt; (a configuration file that tells the FPGA how to configure its internal circuitry) and &lt;em&gt;load it onto the board&lt;/em&gt;. This is where you would truly see your designs in action.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;Imaging building &lt;strong&gt;smart IoT devices&lt;/strong&gt; that control your household, custom &lt;strong&gt;hardware accelerators&lt;/strong&gt;, or even &lt;strong&gt;embedded systems&lt;/strong&gt; that power anything from cards to industrial machines. The possibilities are endless!&lt;/p&gt;</content:encoded></item><item><title>Getting Started with Clash: Haskell’s Hardware Description Language for Modern Hardware Design</title><link>https://ernestsrudzitis.com/blog/getting-started-with-clash-haskells-hardware-description-language-for-modern-hardware-design/</link><guid isPermaLink="true">https://ernestsrudzitis.com/blog/getting-started-with-clash-haskells-hardware-description-language-for-modern-hardware-design/</guid><description>Embark on your FPGA development journey with Clash. This introductory blog post provides a foundation for exploring functional hardware development.</description><pubDate>Sun, 01 Sep 2024 16:57:01 GMT</pubDate><content:encoded>&lt;h2 id=&quot;isPasted&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;Hardware development tends to be more specialized, and compared to software engineers, there are fewer professionals in this field. The development process itself imposes various challenges, such as &lt;em&gt;high manufacturing&lt;/em&gt; and &lt;em&gt;development costs&lt;/em&gt;, &lt;em&gt;scalability&lt;/em&gt;, &lt;em&gt;reliability&amp;nbsp;&lt;/em&gt;&lt;em&gt;validation&lt;/em&gt;, &lt;em&gt;intellectual property protection&lt;/em&gt;, et cetera. Despite these challenges, (experienced) professionals in hardware development will remain in high demand for many years to come, as many companies and individuals will continue to rely on existing technology, and engineers will remain crucial as hardware adapts and improves.&amp;nbsp;&lt;/p&gt;&lt;p&gt;The hardware field is indeed expansive, with various solutions and architectures available for different challenges. In this series, we&amp;rsquo;ll concentrate on development specifically for &lt;em&gt;FPGA&amp;nbsp;&lt;/em&gt;(Field Programmable Gate Array) boards.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Does this sound intriguing to you? Are you ready to take on the challenge? Then follow me, as we together, explore the realm of hardware development.&lt;/strong&gt;&lt;/p&gt;&lt;h2&gt;What is an FPGA?&lt;/h2&gt;&lt;p&gt;You are likely familiar with CPUs (Central Processing Units), the workhorses of modern computing. CPU&amp;#39;s are hardware components that are &lt;em&gt;general purpose&amp;nbsp;&lt;/em&gt;and &lt;em&gt;often excel in performance&lt;/em&gt;, essentially they are structured in a way that would allow firmware or operating system to utilize it in any way necessary to solve various (extensive) tasks. However, for solving a certain problem we might not need such a versatile or performance efficient hardware component. When looking at problem specific available options in the market, we arrive at couple alternatives - ASIC (Application Specific Integrated Circuit), MPSoC (Multi Processor System On Chip), &lt;strong&gt;FPGA&amp;nbsp;&lt;/strong&gt;(Field Programmable Gate Array). &lt;strong&gt;Note!&lt;/strong&gt; If you are interested in the distinctive benefits each technology introduces, feel free to explore more, unfortunately it&amp;#39;s out of scope for these series.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;At this point you have been told that FPGAs are used to solve problem specific tasks. That indeed is quite vague, let&amp;#39;s expand on that. First and foremost, the hardware structure of FPGAs greatly differs to that compared to a standard CPU. The basic architecture of an FPGA is made up of &lt;strong&gt;Configurable Logic Blocks&lt;/strong&gt; (CLB&amp;#39;s). Furthermore, these CLB&amp;#39;s are made up of smaller components, such as &lt;strong&gt;Flip-Flops&lt;/strong&gt; (memory units), &lt;strong&gt;Look-up Tables&lt;/strong&gt; (containing hardwired outputs for every combination of inputs), &lt;strong&gt;Multiplexers&lt;/strong&gt;. However, &lt;em&gt;pay careful notice&lt;/em&gt;, we did &lt;strong&gt;NOT&amp;nbsp;&lt;/strong&gt;mention any logic gates (AND, OR, XOR, NOT, NAND, NOR and XNOR). As a matter of fact, that is correct, FPGA fabric contains no such basic gates, instead the required logic is performed by means of &lt;strong&gt;Look-up tables&lt;/strong&gt;. An FPGA would contain hundreds or (usually) thousands of such &lt;strong&gt;CLB&amp;#39;s&lt;/strong&gt;, that when linked together solve complex logic functions.&lt;/p&gt;&lt;p&gt;Moreover, more expensive and advanced boards contain &lt;strong&gt;Hard IP&amp;#39;s&lt;/strong&gt; (Hard Intellectual Properties) - chip blocks that are etched into the silicon and perform a certain task very well (in a way better than the same solution made of CLB&amp;#39;s). Some examples to mention: ARM CPU, Block RAM, et cetera.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;FPGAs have a wide range of applications, they particularly excel in parallelism. To name few examples:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;High-performance computing&lt;/strong&gt;: Accelerating scientific simulations, data analysis, and machine learning tasks.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Image/audio processing&lt;/strong&gt;: Accelerating image/audio processing, encoding or decoding.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Embedded systems&lt;/strong&gt;: Complex control units in industrial automation, aerospace.&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Why Haskell &amp;amp; Clash?&lt;/h2&gt;&lt;p&gt;These days, &amp;#39;&lt;em&gt;Hardware Description Language&lt;/em&gt;&amp;#39; (HDL), is used to write the great majority of hardware projects. These are declarative languages that let circuit designers explain how their designs behave. Although there are many HDLs available, &lt;em&gt;Verilog&amp;nbsp;&lt;/em&gt;and &lt;em&gt;VHDL&amp;nbsp;&lt;/em&gt;are the most widely used.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;fr-img-caption fr-fil fr-dii&quot; style=&quot;width: 736px;&quot;&gt;&lt;span class=&quot;fr-img-wrap&quot;&gt;&lt;img alt=&quot;vhdl_and_verilog.png&quot; src=&quot;https://imgix.cosmicjs.com/1084e800-54c9-11ef-bbaa-af49ecc8228f-vhdl_and_verilog.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;span class=&quot;fr-inner&quot;&gt;This figure displays a simple implementation of an &amp;#39;AND&amp;#39; gate in VHDL and Verilog, respectively.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;I&amp;#39;ve included a short example of both of the descriptive languages above. I will not dive into too much detail, that would be beyond the scope of these series, for this there are plentiful of documentation and tutorials. Coming from a software development background, such HDL languages &lt;em&gt;did not necessarily tickle my fancy.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Looking at alternatives, I came across a functional hardware description language - &lt;a href=&quot;https://clash-lang.org/&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;strong&gt;Clash&lt;/strong&gt;&lt;/a&gt;. &lt;strong&gt;Clash&amp;nbsp;&lt;/strong&gt;is built upon the ideology of &lt;em&gt;Haskell&lt;/em&gt;, from which both the syntax and semantics are borrowed. Functional properties of Clash, such as &lt;em&gt;purity&amp;nbsp;&lt;/em&gt;(no implied side effects, function output depends only on its input values, and no global state that could be altered), &lt;em&gt;polymorphism&lt;/em&gt;, &lt;em&gt;strict typing&lt;/em&gt;, and &lt;em&gt;higher-order functions&lt;/em&gt; are the driving factors that enable developers to write concise and maintainable code both for combinational and sequential circuits.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;How awesome is that?&lt;/strong&gt;&lt;/p&gt;&lt;h2&gt;Installation &amp;amp; Setup&lt;/h2&gt;&lt;p&gt;There are couple tools we need to install to get started. The following steps will guide you to set everything up for a windows machine.&lt;/p&gt;&lt;h3&gt;GHC &amp;amp; Cabal&lt;/h3&gt;&lt;p&gt;GHC is a Haskell compiler, where as Cabal is a tool for building and packaging Haskell libraries and applications. The &lt;a href=&quot;https://www.haskell. org/downloads/&quot; rel=&quot;noopener noreferrer&quot;&gt;Haskell Downloads Page&lt;/a&gt; provides an installation guide, or &lt;a href=&quot;https://www.haskell.org/ghcup/&quot; rel=&quot;noopener noreferrer&quot;&gt;GHCup&amp;nbsp;&lt;/a&gt;which proposes a straight-forward and user friendly textual user interface. If you do decide to take the manual route, rather than utilizing &lt;em&gt;GHCup&lt;/em&gt;, I can recommend the stable release version &lt;strong&gt;9.8.2&lt;/strong&gt; for GHC, and &lt;strong&gt;3.10.3.0&lt;/strong&gt; for Cabal.&lt;/p&gt;&lt;h3&gt;Clash&lt;/h3&gt;&lt;p&gt;Having completed the first step, proceed with clash installation.&lt;/p&gt;&lt;p&gt;Install clash-ghc by running the following command in CMD: &lt;strong&gt;cabal v1-install clash-ghc&lt;/strong&gt;. Occasionally, this will require several runs of the installation command before everything compiles. Hence, if Cabal reports an error, check if the command continues past the error point by running it again. (Try &lt;strong&gt;cabal install clash-ghc&lt;/strong&gt;, if the command v1-install is not present).&lt;/p&gt;&lt;p&gt;Validate that everything was successfully installed by entering &lt;strong&gt;clashi&amp;nbsp;&lt;/strong&gt;in CMD. You should be greeted with the clash interactive environment.&lt;/p&gt;&lt;p&gt;&lt;img alt=&quot;clashi_interactive_prompt_b.png&quot; src=&quot;https://imgix.cosmicjs.com/0f482570-54cd-11ef-bbaa-af49ecc8228f-clashi_interactive_prompt_b.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;/p&gt;&lt;h3&gt;Xilinx Vivado&lt;/h3&gt;&lt;p&gt;This tool is more demanding in terms of system requirements. It will come in handy in future series when we are going to be visualizing our circuit RTL (Register Transfer Language) schematics. For now, let&amp;#39;s install it and set it aside for later use.&amp;nbsp;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;To install the necessary software, you&amp;#39;ll need to create an AMD account. You can sign up here: &lt;a href=&quot;https://www.amd.com/en/registration/create-account.html&quot; rel=&quot;noopener noreferrer&quot;&gt;AMD Signup Page&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;Head to the downloads page: &lt;a href=&quot;https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vivado-design-tools.html&quot; rel=&quot;noopener noreferrer&quot;&gt;AMD Xilinx Vivado Download&lt;/a&gt;. The version of the software you want to download is 2024.1, namely the &lt;em&gt;AMD Unified Installer for FPGAs &amp;amp; Adaptive SoCs 2024.1: Windows Self Extracting Web Installer&lt;/em&gt;.&amp;nbsp;&lt;/li&gt;&lt;li&gt;Follow the Setup Wizard (I have attached couple of images below to aid the installation process).&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;img alt=&quot;vivado_setup_wizard_grid.png&quot; src=&quot;https://imgix.cosmicjs.com/12957880-54c9-11ef-bbaa-af49ecc8228f-vivado_setup_wizard_grid.png&quot; class=&quot;fr-fil fr-dib&quot;&gt;&lt;/p&gt;&lt;p&gt;The final installation size is quite significant, at least 18GB. It is recommended to use stable and quick internet connection.&lt;/p&gt;&lt;h2&gt;Wrapping up&lt;/h2&gt;&lt;p&gt;The &lt;em&gt;first section&lt;/em&gt; of the series is now complete. By talking about the particular difficulties in hardware development, we have set the foundation. Additionally, I briefly highlighted the vastness of the hardware field, emphasizing the upcoming focus on FPGA boards and the modern alternatives to traditional HDLs, particularly Clash, which leverages Haskells powerful functional programming paradigm.&lt;/p&gt;&lt;p&gt;We managed to also cover the essential setup steps, from installing GHC, Cabal and Clash to setting up Xilinx Vivado.&lt;/p&gt;&lt;p&gt;In the following series we will begin with a &lt;em&gt;brief overview of Haskell&lt;/em&gt;, learn the &lt;em&gt;fundamentals of Clash&lt;/em&gt;, and then &lt;strong&gt;work on implementing and analyzing various circuit designs&lt;/strong&gt;.&lt;/p&gt;</content:encoded></item></channel></rss>