{"id":3666,"date":"2026-07-15T11:30:41","date_gmt":"2026-07-15T03:30:41","guid":{"rendered":"http:\/\/manufacturing.wiki\/?p=3666"},"modified":"2026-07-15T11:30:41","modified_gmt":"2026-07-15T03:30:41","slug":"on-site-programmable-gate-array-engineering-file-structure-management","status":"publish","type":"post","link":"http:\/\/manufacturing.wiki\/index.php\/2026\/07\/15\/on-site-programmable-gate-array-engineering-file-structure-management\/","title":{"rendered":"On-site programmable gate array engineering file structure management"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">FPGA Project File Structure Management: How to Organize Your Design So Nobody Loses Their Mind<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Every FPGA engineer has opened a project directory and felt immediate dread. Files scattered everywhere, three copies of the same constraint file, bitstreams mixed with source code, and nobody remembers which folder the testbench lives in. File structure is not about aesthetics. It is about whether you can find the right file in 30 seconds or spend an hour digging through junk.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A clean file structure saves time during development, makes collaboration possible, and lets your CI pipeline run without breaking. This guide covers the folder layout that actually works in real projects \u2014 not the textbook ideal, but the one that survives contact with deadline pressure.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Core Principle: Separate What Changes From What Does Not<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Before creating a single folder, understand the split. Your project has two categories of files. Source files change constantly \u2014 you edit HDL, tweak constraints, rewrite testbenches. Generated files change rarely and should never be edited by hand \u2014 bitstreams, synthesis logs, IP core outputs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mix these two categories and you get chaos. Someone accidentally commits a 2 GB bitstream. Another engineer overwrites a generated constraint file with a manual edit and breaks timing. The fix is simple: never let generated files live next to source files.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A Folder Layout That Scales<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Source Code Lives Under src\/<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">All human-written code goes under a&nbsp;<code>src\/<\/code>&nbsp;directory. Inside that, split by language and function:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1src\/\n2\u251c\u2500\u2500 hdl\/\n3\u2502   \u251c\u2500\u2500 rtl\/\n4\u2502   \u2502   \u251c\u2500\u2500 top_module.v\n5\u2502   \u2502   \u251c\u2500\u2500 uart_tx.v\n6\u2502   \u2502   \u2514\u2500\u2500 spi_master.v\n7\u2502   \u2514\u2500\u2500 tb\/\n8\u2502       \u251c\u2500\u2500 tb_top.v\n9\u2502       \u2514\u2500\u2500 tb_uart.v\n10\u251c\u2500\u2500 constraints\/\n11\u2502   \u251c\u2500\u2500 top.xdc\n12\u2502   \u2514\u2500\u2500 board_pinout.xdc\n13\u2514\u2500\u2500 scripts\/\n14    \u251c\u2500\u2500 build.tcl\n15    \u2514\u2500\u2500 run_sim.sh\n16<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The&nbsp;<code>hdl\/rtl\/<\/code>&nbsp;folder holds synthesizable code. The&nbsp;<code>hdl\/tb\/<\/code>&nbsp;folder holds testbenches and simulation-only code. Constraints get their own folder because they are not code \u2014 they are configuration. Scripts get their own folder because they are not part of the design \u2014 they are part of the workflow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This separation matters when you hand the project to someone else. They open&nbsp;<code>src\/hdl\/rtl\/<\/code>&nbsp;and immediately see every module. They open&nbsp;<code>src\/constraints\/<\/code>&nbsp;and see every pin assignment. No guessing, no hunting.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Generated Files Live Under build\/<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Everything the tool produces goes under&nbsp;<code>build\/<\/code>. Synthesis logs, implementation reports, timing summaries, bitstreams, utilization reports \u2014 all of it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1build\/\n2\u251c\u2500\u2500 synth\/\n3\u2502   \u251c\u2500\u2500 top.dcp\n4\u2502   \u2514\u2500\u2500 synth_report.txt\n5\u251c\u2500\u2500 impl\/\n6\u2502   \u251c\u2500\u2500 top_routed.dcp\n7\u2502   \u251c\u2500\u2500 top.bit\n8\u2502   \u2514\u2500\u2500 impl_report.txt\n9\u2514\u2500\u2500 sim\/\n10    \u251c\u2500\u2500 waveform.vcd\n11    \u2514\u2500\u2500 sim.log\n12<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The&nbsp;<code>build\/<\/code>&nbsp;directory is never committed to version control. Add it to your&nbsp;<code>.gitignore<\/code>&nbsp;on day one. If it is not in version control, it cannot pollute your repository or create merge conflicts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When you need to archive a bitstream for a release, copy it out of&nbsp;<code>build\/<\/code>&nbsp;into a&nbsp;<code>releases\/<\/code>&nbsp;folder with a version tag. The&nbsp;<code>releases\/<\/code>&nbsp;folder is the only place bitstreams should ever live.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Documentation Lives Under docs\/<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Most teams skip this folder until it is too late. Then they lose the design rationale and spend days reverse-engineering why a decision was made six months ago.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1docs\/\n2\u251c\u2500\u2500 architecture.md\n3\u251c\u2500\u2500 pin_assignment_notes.md\n4\u251c\u2500\u2500 timing_closure_notes.md\n5\u2514\u2500\u2500 known_issues.md\n6<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Keep these files short and specific. Do not write a 50-page spec nobody reads. Write a one-page architecture note that explains the clock domains, the data flow, and the bottlenecks. Write a timing notes file that records which paths failed closure and how you fixed them. Future you will thank present you.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Handling IP Cores Without Polluting the Repo<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">IP cores are the messiest part of any FPGA project. They come as generated files, they are huge, and they change only when you update the core \u2014 which might happen once a year.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Do not commit IP core source files to version control. Instead, store the Tcl script that regenerates them. Most IP cores can be recreated from a configuration file using a Tcl command. Keep that Tcl file in&nbsp;<code>src\/scripts\/ip_config.tcl<\/code>&nbsp;and run it as part of your build script.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The generated IP output goes into&nbsp;<code>build\/ip\/<\/code>&nbsp;and gets ignored by Git. When a new engineer clones the repo, they run the build script, the Tcl regenerates the IP cores, and the project builds from scratch. No manual file copying. No &#8220;check the shared drive for the IP folder.&#8221;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you must commit pre-generated IP for some reason, put it in a separate Git submodule or a dedicated&nbsp;<code>ip\/<\/code>&nbsp;folder with a clear README explaining where it came from and how to update it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Managing Multiple Board Revisions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Eventually your design will run on more than one board. The pinout changes. The clock frequency changes. The constraint file changes. If you dump all of this into one folder, you will overwrite the wrong file and brick a board.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Create a&nbsp;<code>boards\/<\/code>&nbsp;directory with one subfolder per revision:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1boards\/\n2\u251c\u2500\u2500 rev_a\/\n3\u2502   \u2514\u2500\u2500 rev_a.xdc\n4\u251c\u2500\u2500 rev_b\/\n5\u2502   \u2514\u2500\u2500 rev_b.xdc\n6\u2514\u2500\u2500 rev_c\/\n7    \u2514\u2500\u2500 rev_c.xdc\n8<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Your build script selects the right constraint file based on a parameter or an environment variable. Never hardcode a board revision into the source. The moment you do, you have created a bug that only shows up when someone builds for the wrong board.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What Goes Into Version Control and What Does Not<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is where most teams get it wrong. They either commit everything (bloating the repo with gigabytes of junk) or commit nothing useful (losing the ability to reproduce a build).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Commit these files:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>All HDL source under\u00a0<code>src\/hdl\/<\/code><\/li>\n\n\n\n<li>All constraint files under\u00a0<code>src\/constraints\/<\/code><\/li>\n\n\n\n<li>All Tcl and shell scripts under\u00a0<code>src\/scripts\/<\/code><\/li>\n\n\n\n<li>Documentation under\u00a0<code>docs\/<\/code><\/li>\n\n\n\n<li>The\u00a0<code>.gitignore<\/code>\u00a0file itself (so every clone gets the same rules)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Do not commit these files:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Anything under\u00a0<code>build\/<\/code><\/li>\n\n\n\n<li>Bitstreams (unless you have a\u00a0<code>releases\/<\/code>\u00a0folder with tagged versions)<\/li>\n\n\n\n<li>Log files<\/li>\n\n\n\n<li>Temporary simulation dumps<\/li>\n\n\n\n<li>OS-specific files like\u00a0<code>.DS_Store<\/code>\u00a0or\u00a0<code>Thumbs.db<\/code><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Use Git LFS for large files you must track, like pre-generated IP cores or reference waveforms. But even with LFS, ask yourself whether you really need to version that file or whether regenerating it from source is safer.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Naming Conventions That Prevent Confusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A good file structure fails if your files are named&nbsp;<code>top_module_v2_final_REAL.v<\/code>. Enforce a naming convention from the start.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Module files:&nbsp;<code>module_name.v<\/code>&nbsp;or&nbsp;<code>module_name.vhd<\/code>. No version numbers in the filename \u2014 that is what Git is for.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Constraint files:&nbsp;<code>boardname.xdc<\/code>&nbsp;or&nbsp;<code>boardname.sdc<\/code>. Include the board name, not the date.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Testbench files:&nbsp;<code>tb_module_name.v<\/code>. The&nbsp;<code>tb_<\/code>&nbsp;prefix makes it instantly clear this is simulation-only code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Script files:&nbsp;<code>verb_action.tcl<\/code>&nbsp;or&nbsp;<code>verb_action.sh<\/code>.&nbsp;<code>build_project.tcl<\/code>,&nbsp;<code>run_simulation.sh<\/code>,&nbsp;<code>generate_bitstream.tcl<\/code>. The name tells you what it does without opening it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Stick to lowercase with underscores. No spaces. No special characters. This is not about style \u2014 it is about avoiding bugs when a script tries to parse a filename with a space in it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The One Rule That Makes Everything Work<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you remember nothing else from this guide, remember this: the source directory is for humans, the build directory is for machines, and never the twain shall meet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When every file lives in the right place, builds become reproducible, collaborators stop asking &#8220;where is the constraint file,&#8221; and your CI pipeline runs without throwing errors at 2 AM. File structure is boring. It is also the single biggest productivity gain you will make this quarter.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">ChipApex is a global distributor of electronic components: ICs, semiconductors, passives &amp; interconnects. Source active &amp; obsolete parts with wholesale pricing, fast RFQ response, and worldwide delivery.Official website address:chipapex.com<\/p>\n","protected":false},"excerpt":{"rendered":"<p>FPGA Project File Structure Management: How to Organize &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-3666","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/posts\/3666","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/comments?post=3666"}],"version-history":[{"count":1,"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/posts\/3666\/revisions"}],"predecessor-version":[{"id":3667,"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/posts\/3666\/revisions\/3667"}],"wp:attachment":[{"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/media?parent=3666"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/categories?post=3666"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/manufacturing.wiki\/index.php\/wp-json\/wp\/v2\/tags?post=3666"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}