{"id":557,"date":"2013-06-14T00:04:32","date_gmt":"2013-06-14T00:04:32","guid":{"rendered":"http:\/\/retroramblings.net\/?p=557"},"modified":"2013-07-06T11:18:46","modified_gmt":"2013-07-06T11:18:46","slug":"generalizing-the-boot-rom","status":"publish","type":"post","link":"https:\/\/retroramblings.net\/?p=557","title":{"rendered":"Generalizing the Boot ROM"},"content":{"rendered":"<p>With its tiny size the ZPU would seem an ideal candidate for a multi-core design.\u00a0 Unfortunately, instantiating more than one of the traditional zpu_small cores in a single project is complicated by the ROM \/ Stack RAM entity being instantiated within the CPU core itself.\u00a0 If you want multiple ZPUs, doing different jobs, then their ROMs must differ!\u00a0 To solve this, I&#8217;ve removed the internal ROM and defined a new interface (using VHDL techniques I&#8217;ve picked up from the PACE project) for connecting the ZPU to a separate ROM \/ Stack RAM entity. The biggest advantage of this is that it makes it much simpler to swap between different firmwares.<!--more--><\/p>\n<p>The signals required for the ZPU&#8217;s ROM \/ Stack RAM entity are as follows:<\/p>\n<pre>\u00a0\u00a0\u00a0\u00a0\u00a0 memAWriteEnable : in\u00a0 std_logic;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 memAAddr\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : in\u00a0 std_logic_vector(maxAddrBitBRAM downto minAddrBit);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 memAWrite\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : in\u00a0 std_logic_vector(wordSize-1 downto 0);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 memARead\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : out std_logic_vector(wordSize-1 downto 0);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 memBWriteEnable : in\u00a0 std_logic;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 memBAddr\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : in\u00a0 std_logic_vector(maxAddrBitBRAM downto minAddrBit);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 memBWrite\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : in\u00a0 std_logic_vector(wordSize-1 downto 0);\r\n\u00a0\u00a0\u00a0\u00a0\u00a0 memBRead\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 : out std_logic_vector(wordSize-1 downto 0);<\/pre>\n<p>In order to avoid having to connect all these individually, and to create signals for them in the toplevel, I&#8217;ve defined some new types in zpupkg.vhd:<\/p>\n<pre>\u00a0\u00a0\u00a0 type ZPU_ToROM is record\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0memAWriteEnable : std_logic;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0memAAddr : std_logic_vector(maxAddrBitBRAM downto minAddrBit);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0memAWrite : std_logic_vector(wordSize-1 downto 0);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0memBWriteEnable : std_logic;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0memBAddr : std_logic_vector(maxAddrBitBRAM downto minAddrBit);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0memBWrite : std_logic_vector(wordSize-1 downto 0);\r\n\u00a0\u00a0 \u00a0end record;\r\n\r\n\u00a0\u00a0 \u00a0type ZPU_FromROM is record\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0memARead : std_logic_vector(wordSize-1 downto 0);\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0memBRead : std_logic_vector(wordSize-1 downto 0);\r\n\u00a0\u00a0 \u00a0end record;<\/pre>\n<p>This means the relevant part of the ZPU&#8217;s interface can be reduced to:<\/p>\n<pre>\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0 from_rom : in ZPU_FromROM;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0to_rom : out ZPU_ToROM<\/pre>\n<p>and the toplevel merely has to do this:<\/p>\n<pre>signal zpu_to_rom : ZPU_ToROM; \r\nsignal zpu_from_rom : ZPU_FromROM; \r\n...\r\nmyrom : entity work.SDBootstrap_ROM \r\n\u00a0\u00a0 \u00a0port map ( \r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0clk =&gt; clk, \r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0from_zpu =&gt; zpu_to_rom, \r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0to_zpu =&gt; zpu_from_rom \r\n\u00a0\u00a0 \u00a0);<\/pre>\n<p>To simplify creating the ROM entities from compiled code, I&#8217;m doing some Makefile magic.\u00a0 (Previously I&#8217;ve been using the 2-port RAM megafunction wizard to generate the ROMs &#8211; and using srec_cat to generate the required .mif files.)<\/p>\n<p>Luckily the ZPU project includes a ROM generating program, which takes a raw binary file as input and outputs VHDL code like so:<\/p>\n<pre>\u00a0\u00a0\u00a0\u00a0 0 =&gt; x\"a08087fe\",\r\n\u00a0\u00a0\u00a0\u00a0 1 =&gt; x\"04000000\",\r\n\u00a0\u00a0\u00a0\u00a0 2 =&gt; x\"800b810b\",\r\n\u00a0\u00a0\u00a0\u00a0 3 =&gt; x\"ff8c0c04\",\r\n\u00a0\u00a0\u00a0\u00a0 4 =&gt; x\"0b0b0ba0\",\r\n\u00a0\u00a0\u00a0\u00a0 5 =&gt; x\"80809d0b\",\r\n     ...<\/pre>\n<p>With a bit of shell scripting and redirection it&#8217;s easy enough to combine this with a VHDL prologue and epilogue to build a complete VHDL file defining the ROM.\u00a0 My prologue and epilogue use the name dualportram, and the makefile uses sed to change this to something more suitable, like so:<\/p>\n<pre>\u00a0\u00a0 make -C SDBootstrap\/\r\n\u00a0\u00a0 sed 's\/dualportram\/sdbootstrap_rom\/' &gt;sdbootstrap_rom.vhd &lt;rom_prologue\r\n\u00a0\u00a0 .\/zpuromgen SDBootstrap\/boot.bin &gt;&gt;sdbootstrap_rom.vhd\r\n\u00a0\u00a0 cat &gt;&gt;sdbootstrap_rom.vhd rom_epilogue<\/pre>\n<p>If the directories, files and entities are named carefully, we can generalize this even further, and my toplevel firmware makefile now looks like this:<\/p>\n<pre>all: zpuromgen Dhrystone.gen SDBootstrap.gen RS232Bootstrap.gen\r\n\r\nclean:\r\n    make -C SDBootstrap clean\r\n    make -C RS232Bootstrap clean\r\n    make -C Dhrystone clean\r\n    rm *.vhd\r\n\r\nzpuromgen: zpuromgen.c\r\n    gcc -o zpuromgen zpuromgen.c\r\n\r\n%.gen:\r\n    make -C $*\/\r\n    sed 's\/dualportram\/$*_ROM\/' &gt;$*_ROM.vhd &lt;rom_prologue\r\n    .\/zpuromgen $*\/$*.bin &gt;&gt;$*_ROM.vhd\r\n    cat &gt;&gt;$*_ROM.vhd rom_epilogue<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>With its tiny size the ZPU would seem an ideal candidate for a multi-core design.\u00a0 Unfortunately, instantiating more than one of the traditional zpu_small cores in a single project is complicated by the ROM \/ Stack RAM entity being instantiated &hellip; <a href=\"https:\/\/retroramblings.net\/?p=557\">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],"tags":[],"class_list":["post-557","post","type-post","status-publish","format-standard","hentry","category-fpga"],"_links":{"self":[{"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/557","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=557"}],"version-history":[{"count":7,"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/557\/revisions"}],"predecessor-version":[{"id":595,"href":"https:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/557\/revisions\/595"}],"wp:attachment":[{"href":"https:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=557"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=557"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=557"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}