{"id":1091,"date":"2015-07-25T22:48:00","date_gmt":"2015-07-25T22:48:00","guid":{"rendered":"http:\/\/retroramblings.net\/?p=1091"},"modified":"2015-07-25T22:48:00","modified_gmt":"2015-07-25T22:48:00","slug":"building-for-multiple-targets","status":"publish","type":"post","link":"https:\/\/retroramblings.net\/?p=1091","title":{"rendered":"Building for multiple targets"},"content":{"rendered":"<p>One of the challenges I&#8217;ve faced in the ZPUDemos project is keeping the various targets up to date.\u00a0 When I add a peripheral to &#8211; for example &#8211; the SDBootstrap SOC, I have to modify each and every target&#8217;s project file to match, and it&#8217;s very easy to lose track of which ones have been updated and which ones haven&#8217;t.<\/p>\n<p>ZPUDemos currently supports no fewer than eight different target boards, and contains eleven different projects &#8211; that&#8217;s a lot of project files!<\/p>\n<p>In an attempt to make this more manageable, I&#8217;ve written some scripts to generate project files automatically, from a list of RTL files, and a board-specific template file.\u00a0 I&#8217;ve taken the opportunity to clean up the whole project, too, so the directory structure is more logical.<!--more--><br \/>\nThe directories are now as follows:<\/p>\n<ul>\n<li><strong>Board<\/strong>:\u00a0 Contains a subdirectory for each target board which contains a toplevel file along with any board-specific supporting files, for instance the i2c drivers needed to talk to the DE1&#8217;s audio codec, or the low-level SD card and PS\/2 emulation for the MIST board.\u00a0 Also includes any constraint files, and a template project file which references just the RTL files within the board&#8217;s own directory.\u00a0 Project-specific files are added by the scripts.<\/li>\n<li><strong>RTL<\/strong>:\u00a0 The files within this directory are no longer loose, but categorised into subdirectories:\n<ul>\n<li><strong>DMA<\/strong>: The DMA controller and DMACache block RAM definition<\/li>\n<li><strong>Peripherals<\/strong>: Basic IO controllers for RS232 serial, PS\/2, SD card, etc.<\/li>\n<li><strong>RAM<\/strong>: Anything memory-related: simple dual-port RAM definition, SDRAM controller, Cache, etc.<\/li>\n<li><strong>Sound<\/strong>:\u00a0 Audio controller, providing very similar facilities to the Amiga&#8217;s sound chip.<\/li>\n<li><strong>Util<\/strong>: Supporting circuits for debouncing switches and dividing clocks, etc.<\/li>\n<li><strong>Video<\/strong>:\u00a0 VGA controller plus dither component.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Apps<\/strong>:\u00a0 Various demo applications which can either be placed on SD card or uploaded to the ZPU over RS232.\u00a0 Many of these can be simulated too.<\/li>\n<li><strong>Common_Firmware<\/strong>:\u00a0 C files and headers providing some barebones C library features, and access to hardware registers.\u00a0 Both the Apps and individual projects&#8217; Firmware files make use of these.<\/li>\n<li><strong>ZPUSim<\/strong>:\u00a0 A submodule containing a rudimentary simulator for the ZPU.<\/li>\n<li><strong>ZPUFlex<\/strong>: The CPU itself (as a submodule), also contains ROM prologue \/ epilogue, startup code and linkscripts.<\/li>\n<li><strong>Scripts<\/strong>: Build scripts which are used to create project files.<\/li>\n<\/ul>\n<p>As well as these, each project has its own directory, containing the following:<\/p>\n<ul>\n<li><strong>Firmware<\/strong>: The ROM(s) used by this project<\/li>\n<li><strong>RTL<\/strong>: A &lt;project&gt;_Virtual_Toplevel file which is wrapped by the board-specific toplevel, as well as a Toplevel_Config file which is used by the board-specific toplevel to include or omit such things as audio support or VGA dithering, as appropriate.\u00a0 Any other project-specific RTL files go here, unless they could conceivably be useful in other projects, in which case they go in the root RTL directory.<\/li>\n<li><strong>fpga<\/strong>: Each target board has its own directory in here, containing project files and any generated bitstreams.<\/li>\n<li><strong>manifest.rtl<\/strong>: A file listing the RTL files that need to be included in the project.\u00a0 These will be merged with the board-specific project template.<\/li>\n<li><strong>Makefile<\/strong>: this is basically the same for each project, all that varies is the project name.<\/li>\n<\/ul>\n<p>The root contains a top level Makefile which, if the ZPU toolchain is correctly installed will generate project files for every board and demo, build each demo&#8217;s firmware, and build the demo Apps.<\/p>\n<p>In writing the scripts to create the project files I learned quite a bit about bash scripting and Makefiles, including some subtleties of escaping and using shell variables within makefiles that I hadn&#8217;t encountered before.<\/p>\n<p>The Scripts directory contains two bash scripts and three makefiles.\u00a0 Of the latter, each project calls the makefile called standard.mak, which in turn calls quartus.mak to build project files for Altera-based boards, and ise.mak to build projects for Xilinx-based boards.\u00a0 The vendor-specific makefiles are called once for each board, in a for loop.<\/p>\n<p>The first subtlety I encountered was in getting this for loop to work within a makefile.\u00a0 It turns out a new shell is spawned for every command execute by a makefile, which means that no environment is preserved from line to line.\u00a0 Thus the obvious approach&#8230;<\/p>\n<pre>for BOARD in $(BOARDS)\r\ndo\r\n    do something\r\ndone<\/pre>\n<p>&#8230;won&#8217;t work, because the four lines will be executed by different shells!\u00a0 To solve this, we use semi-colons after every command, and escape the newlines with a \\ character, to make the loop appear as a single line which will be executed by a single shell, like so:<\/p>\n<pre>for BOARD in $(BOARDS); do \\\r\n    do something; \\\r\ndone<\/pre>\n<p>The second subtlety is in how to pass the BOARD variable to the executed command.\u00a0 Normally you&#8217;d just use $BOARD &#8211; but if you do that, make will expand the variable, and since it&#8217;s not defined until the shell executes the for loop (and never defined in a way that&#8217;s visible to make), the parameter doesn&#8217;t reach the command.\u00a0 Instead we have to escape the $ with a second $ &#8211; so the command inside the loop becomes<\/p>\n<pre>do something with $$BOARD; \\<\/pre>\n<p>The actual makefile looks like this:<\/p>\n<pre>PROJECT=\r\nMANIFEST=manifest.rtl\r\nBOARDS_ALTERA = # Passed in from parent makefile\r\nBOARDS_XILINX = # Passed in from parent makefile\r\n\r\nALL: fpga\r\n\tmake -C Firmware\r\n\tfor BOARD in ${BOARDS_ALTERA}; do \\\r\n\t\tmake -f ..\/Scripts\/quartus.mak PROJECT=$(PROJECT) MANIFEST=$(MANIFEST) BOARD=$$BOARD; \\\r\n\tdone\r\n\tfor BOARD in ${BOARDS_XILINX}; do \\\r\n\t\tmake -f ..\/Scripts\/ise.mak PROJECT=$(PROJECT) MANIFEST=$(MANIFEST) BOARD=$$BOARD; \\\r\n\tdone\r\n\r\nclean:\r\n\tmake -C Firmware clean\r\n\tfor BOARD in ${BOARDS_ALTERA}; do \\\r\n\t\tmake -f ..\/Scripts\/quartus.mak PROJECT=$(PROJECT) MANIFEST=$(MANIFEST) BOARD=$$BOARD clean; \\\r\n\tdone\r\n\tfor BOARD in ${BOARDS_XILINX}; do \\\r\n\t\tmake -f ..\/Scripts\/quartus.mak PROJECT=$(PROJECT) MANIFEST=$(MANIFEST) BOARD=$$BOARD clean; \\\r\n\tdone\r\n\r\nfpga:\r\n\tmkdir fpga\r\n<\/pre>\n<p>Note that we treat the two vendors separately.\u00a0 Generating a Quartus project file is very easy, since the .qsf file is just a text file that contains (among a great many other things) a list of files with no metadata beyond the file&#8217;s type &#8211; so we can copy a template .qsf and tack the project&#8217;s own files onto the end of it; this is what the expandtemplate_quartus.sh script does, like so:<\/p>\n<pre>#!\/bin\/bash\r\n\r\ncat $1 | while read a; do\r\n\tb=${a,,}\r\n\tif [ \"${b: -4}\" = \".vhd\" ]; then\r\n\t\techo set_global_assignment -name VHDL_FILE ..\/..\/${a}\r\n\tfi\r\n\tif [ \"${b: -4}\" = \".qip\" ]; then\r\n\t\techo set_global_assignment -name QIP_FILE ..\/..\/${a}\r\n\tfi\r\n\tif [ \"${b: -2}\" = \".v\" ]; then\r\n\t\techo set_global_assignment -name VERILOG_FILE ..\/..\/${a}\r\n\tfi\r\ndone\r\n<\/pre>\n<p>This script loops through the manifest file, line by line, generating file entries for the .qsf file, detecting the file type as it goes. The script is bash-specific, and uses some constructs I&#8217;d not come across before until looking for a solution to this particular problem: Firstly, the b=${a,,}, which converts the current line to lower case which makes comparison easier. Secondly, the ${b: -4} which specifies a substring, the last four characters of the line, which we can then compare against &#8220;.vhd&#8221;, etc.<\/p>\n<p>Things aren&#8217;t so simple for Xilinx chips; the .xise file is also a text file but each file listed within also has some metadata which we can&#8217;t easily generate in a shell script. Luckily there&#8217;s a shell command as part of ISE, called xtclsh which will execute a tcl script, and we can use this to add files to a template project. The principle is the same &#8211; the difference is in the echo commands; instead of emitting file names directly into a project file, the expandtemplate.ise script builds a .tcl script which is subsequently executed using xtclsh. The tcl script, once generated, looks like this:<\/p>\n<pre>project open RS232Bootstrap.xise\r\nxfile add \"..\/..\/..\/RTL\/RAM\/DualPortRAM.vhd\"\r\nxfile add \"..\/..\/..\/RTL\/RAM\/TwoWayCache.v\"\r\n...\r\nxfile add \"..\/..\/Firmware\/RS232Bootstrap_ROM.vhd\"\r\nproject save\r\nproject close\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>One of the challenges I&#8217;ve faced in the ZPUDemos project is keeping the various targets up to date.\u00a0 When I add a peripheral to &#8211; for example &#8211; the SDBootstrap SOC, I have to modify each and every target&#8217;s project &hellip; <a href=\"https:\/\/retroramblings.net\/?p=1091\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,8],"tags":[],"class_list":["post-1091","post","type-post","status-publish","format-standard","hentry","category-fpga","category-hardware"],"_links":{"self":[{"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/1091","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1091"}],"version-history":[{"count":5,"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/1091\/revisions"}],"predecessor-version":[{"id":1097,"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/1091\/revisions\/1097"}],"wp:attachment":[{"href":"https:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1091"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1091"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1091"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}