{"id":543,"date":"2019-04-28T15:12:47","date_gmt":"2019-04-28T15:12:47","guid":{"rendered":"https:\/\/justanotherelectronicsblog.com\/?p=543"},"modified":"2019-04-28T15:12:49","modified_gmt":"2019-04-28T15:12:49","slug":"a-one-page-cpu-in-spinalhdl","status":"publish","type":"post","link":"https:\/\/justanotherelectronicsblog.com\/?p=543","title":{"rendered":"A one page CPU in SpinalHDL"},"content":{"rendered":"\n<p>FPGA&#8217;s are fun and interesting devices, sadly the usual ways of programming them, Verilog and VHDL, are tedious and annoying to work with. Sadly most FPGA tools support only these languages. Luckily a few projects exist to make a nicer and more fun language to work with, which are translated to either Verilog or VHDL so they can be used with all normal FPGA tools. One of these languages is <a href=\"https:\/\/spinalhdl.github.io\/SpinalDoc-RTD\/\">SpinalHDL<\/a>, a Scala based language that promises more abstraction, less boilerplate code and also has a good amount of already existing libraries. The only downside is the long name, SpinalHDL, so I&#8217;ll short it to Spinal for now :<\/p>\n\n\n\n<p>To try out and learn Spinal a bit, I decided to make a CPU, an always  popular project for FPGA&#8217;s. To make it a bit more interesting, I wanted  to make a CPU which code fits on a single page of paper, inspired by the  fun CPU&#8217;s made by Revaldinho, which can be found <a href=\"https:\/\/github.com\/revaldinho\/opc\">here<\/a>.  Those CPU&#8217;s also have the added rule of having a emulator and assembler  also fit in a single page of paper, which I choose not to stick to.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>The CPU designed is an 8 bit Harvard CPU, meaning it has a separate interface for the instruction memory and the data memory. It has a 13 bit instruction memory bus and a 10 bit data memory bus, meaning it can access 8Kwords of instruction memory and 1Kword of RAM. The CPU is a accumulator CPU, meaning it has a single register called ACC and all calculation results are stored in that. Instructions operate on the ACC and an immediate value or on the ACC and a value from RAM. The CPU also has an interrupt input. When this input is high, the CPU will jump to address 256 on the next cycle. The IRQ pin must be cleared with the instruction on address 256, else the CPU will never continue operation.<\/p>\n\n\n\n<p>All instructions are 16 bit wide and every instruction takes 3 clock cycles to complete. Pipelining or having some instructions be done in less cycles then others is a bit much when you are limited to 66 lines of code :)<br><br>The first cycle is an instruction fetch, the second cycle executes the instruction and alters the program counter. The third cycle is for RAM writeback<\/p>\n\n\n\n<p>So on to the instructions,  which there are plenty of for such a small amount of code. <br><strong>ADDI<\/strong> imm, add ACC with IMM, store in ACC<br><strong>SUBI<\/strong> imm, substract ACC,  IMM, store in ACC<br><strong>ANDI<\/strong> imm, AND ACC with IMM, store in ACC<br><strong>OR<\/strong> imm, OR ACC with IMM, store in ACC<br><strong>XORI<\/strong> imm, XOR ACC with IMM, store in ACC<br><strong>SRLI<\/strong> imm, Shift ACC right logical with IMM, store in ACC<br><strong>SLLI<\/strong> imm, Shift ACC left logical with IMM, store in ACC<br><strong>CMPI<\/strong> imm, If IMM is equal or greater then ACC, ACC is 1, else 0<\/p>\n\n\n\n<p>All these instructions also have a ACC with RAM variant: <strong>ADD<\/strong>, <strong>SUB<\/strong>, <strong>AND<\/strong>, <strong>OR<\/strong>, <strong>XOR<\/strong>, <strong>SRL<\/strong>, <strong>SLL<\/strong> and <strong>CMP<\/strong><\/p>\n\n\n\n<p>There are also a few IMM only instructions:<br><strong>SRAI<\/strong> imm, Shift ACC right arithmetic with IMM<br><strong>CMPSI<\/strong> imm, If signed IMM is equal or greater then signed ACC, ACC is 1, else 0<br><strong>SCPU<\/strong>, Store upper 5 bits of the program counter in ACC (IMM not used)<br><strong>SPCL<\/strong> imm, Add imm to lower 8 bits of program counter, store in AC<\/p>\n\n\n\n<p>Several instructions can change the program counter:<br><strong>JMPZI<\/strong> imm, if ACC is zero, jump to imm<br><strong>JMP<\/strong> ram, jump to acc shifted left 8 places + value from RAM<br><br>A short PC relative jump can be achieved with an IMM operation. These instructions substract 2 to 16 (only even numbers) from the program counter is ACC is 0 after the calculation.<br><strong>JADDI<\/strong> imm, jmpoffset<br><strong>JSUBI<\/strong> imm, jmpoffset<br><strong>JANDI<\/strong> imm, jmpoffset<br><strong>JORI<\/strong> imm, jmpoffset<br><strong>JXORI<\/strong> imm, jmpoffset<br><strong>JSLRI<\/strong> imm, jmpoffset<br><strong>JSLLI<\/strong> imm, jmpoffset<br><strong>JCMPI<\/strong> imm, jmpoffse<\/p>\n\n\n\n<p>No CPU is complete without being able to store data to RAM:<br><strong>STA<\/strong> ramaddr, store ACC into RAM<\/p>\n\n\n\n<p>So then finally, the code for the CPU, GPLv3 licensed. A small SoC with GPIO, a timer and UART will follow soon as well.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">package mylib\nimport spinal.core._\nclass OPC extends Component {\n  val io = new Bundle {\n    val din_instr = in  Bits (16 bits); val addr_instr = out Bits(13 bits)\n\t\tval din_ram = in  Bits (8 bits); val dout_ram = out  Bits (8 bits)\n\t\tval addr_ram = out  Bits (10 bits); val we_ram = out Bool; val IRQ = in Bool\n  }\n\tval r_addr = Reg(Bits(10 bits)) init(\"0000000000\")\n\tval r_pc, r_pcIRQ = Reg(UInt(13 bits)) init(0)\n\tval r_acc, r_accIRQ = Reg(UInt(8 bits)) init(0)\n\tval state = Reg(UInt(2 bits)) init(2)\n\tval instr = Reg(UInt(16 bits)) init(0)\n\tio.addr_instr := r_pc.asBits\n\tio.dout_ram := \"x00\"\n\tio.addr_ram := io.din_instr(9 downto 0).asBits\n\tio.we_ram := False\n\twhen(state === 0){\t\t\/\/fetch new instruction stage\n\t\tinstr := io.din_instr.asUInt\n\t\tstate := 1\n\t}.elsewhen(state === 1){\t\t\/\/Stage for ALU part for instruction, and handle PC (increase or do a jump)\n\t\tr_pc := r_pc + 1\n\t\tval input = Mux(instr(13) &amp;&amp; !(!instr(15) &amp;&amp; instr(14)), io.din_ram.asUInt, instr(7 downto 0))\n\t\twhen(instr(15) === False) {\n\t\t\tval tr_acc = instr(12 downto 10).mux(\t\t\/\/ACC with IMM\/RAM instructions\n\t\t\t\t0 -> (r_acc + input),\n\t\t\t\t1 -> (r_acc - input),\n\t\t\t\t2 -> (r_acc &amp; input),\n\t\t\t\t3 -> (r_acc | input),\n\t\t\t\t4 -> (r_acc ^ input),\n\t\t\t\t5 -> (r_acc |>> input).resize(8),\n\t\t\t\t6 -> (r_acc |&lt;&lt; input).resize(8),\n\t\t\t\t7 -> Mux((r_acc &lt; input), U(1, 8 bits), U(0, 8 bits)))\n\t\t\twhen((instr(15) === False) &amp;&amp; (instr(14) === True) &amp;&amp; (tr_acc === 0)){\n\t\t\t\tr_pc := r_pc - ((((instr(13).asUInt &lt;&lt; 2) + instr(9 downto 8))+1)&lt;&lt;1).resized\n\t\t\t}\n\t\t\tr_acc := tr_acc\n\t\t}.elsewhen(instr(15) === True &amp;&amp; instr(14) === False &amp;&amp; instr(13) === False){\n\t\t\tr_acc := instr(12 downto 10).mux(\t\t\/\/needed some more, ACC with IMM for signed cmp and shift arith\n\t\t\t  0 -> (r_pc(12 downto 8) >> 8).resized,\n\t\t\t\t1 -> (r_pc(7 downto 0) + input),\n\t\t\t\t5 -> (r_acc >> input).resize(8),\n\t\t\t\t7 -> Mux(r_acc.asSInt &lt; input.asSInt, U(1, 8 bits), U(0, 8 bits)),\n\t\t\t\tdefault -> r_acc)\n\t\t}\n\t\twhen(instr(15) === True &amp;&amp; instr(14) === True &amp;&amp; instr(13) === False &amp;&amp; r_acc === 0){\n\t\t\tr_pc := instr(12 downto 0)\n\t\t}.elsewhen(instr(15) === True &amp;&amp; instr(14) === True &amp;&amp; instr(13) === True){\n\t\t\tr_pc := ((r_acc &lt;&lt; 8) + io.din_ram.asUInt).resized\n\t\t}.elsewhen(io.IRQ === True &amp;&amp; r_pc =\/= \"0000100000000\"){\t\t\/\/IRQ, store PC and acc\n\t\t\tr_pcIRQ := r_pc\n\t\t\tr_accIRQ := r_acc\n\t\t\tr_pc := \"0000100000000\"\n\t\t}.elsewhen(instr === \"x8800\"){\n\t\t\tr_pc := r_pcIRQ\n\t\t\tr_acc := r_accIRQ\n\t\t}\n\t\tstate := 2\n\t}.elsewhen(state === 2){\t\t\/\/RAM writeback stage\n\t\twhen(instr(15) === True &amp;&amp; instr(14) === False &amp;&amp; instr(13) === True){\n\t\t\tio.we_ram := True\n\t\t\tio.dout_ram := r_acc.asBits\n\t\t}\n\t\tstate := 0\n\t}\n}<\/pre>\n\n\n\n<p><br><\/p>\n","protected":false},"excerpt":{"rendered":"<p>FPGA&#8217;s are fun and interesting devices, sadly the usual ways of programming them, Verilog and VHDL, are tedious and annoying to work with. Sadly most FPGA tools support only these languages. Luckily a few projects exist to make a nicer and more fun language to work with, which are translated to either Verilog or VHDL [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-543","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>A one page CPU in SpinalHDL - jaeblog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/justanotherelectronicsblog.com\/?p=543\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"A one page CPU in SpinalHDL - jaeblog\" \/>\n<meta property=\"og:description\" content=\"FPGA&#8217;s are fun and interesting devices, sadly the usual ways of programming them, Verilog and VHDL, are tedious and annoying to work with. Sadly most FPGA tools support only these languages. Luckily a few projects exist to make a nicer and more fun language to work with, which are translated to either Verilog or VHDL [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/justanotherelectronicsblog.com\/?p=543\" \/>\n<meta property=\"og:site_name\" content=\"jaeblog\" \/>\n<meta property=\"article:published_time\" content=\"2019-04-28T15:12:47+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2019-04-28T15:12:49+00:00\" \/>\n<meta name=\"author\" content=\"riktw\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"riktw\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/justanotherelectronicsblog.com\/?p=543#article\",\"isPartOf\":{\"@id\":\"https:\/\/justanotherelectronicsblog.com\/?p=543\"},\"author\":{\"name\":\"riktw\",\"@id\":\"https:\/\/justanotherelectronicsblog.com\/#\/schema\/person\/d77e39721321c4a472b49909a8f1982b\"},\"headline\":\"A one page CPU in SpinalHDL\",\"datePublished\":\"2019-04-28T15:12:47+00:00\",\"dateModified\":\"2019-04-28T15:12:49+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/justanotherelectronicsblog.com\/?p=543\"},\"wordCount\":685,\"commentCount\":2,\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/justanotherelectronicsblog.com\/?p=543#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/justanotherelectronicsblog.com\/?p=543\",\"url\":\"https:\/\/justanotherelectronicsblog.com\/?p=543\",\"name\":\"A one page CPU in SpinalHDL - jaeblog\",\"isPartOf\":{\"@id\":\"https:\/\/justanotherelectronicsblog.com\/#website\"},\"datePublished\":\"2019-04-28T15:12:47+00:00\",\"dateModified\":\"2019-04-28T15:12:49+00:00\",\"author\":{\"@id\":\"https:\/\/justanotherelectronicsblog.com\/#\/schema\/person\/d77e39721321c4a472b49909a8f1982b\"},\"breadcrumb\":{\"@id\":\"https:\/\/justanotherelectronicsblog.com\/?p=543#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/justanotherelectronicsblog.com\/?p=543\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/justanotherelectronicsblog.com\/?p=543#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/justanotherelectronicsblog.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"A one page CPU in SpinalHDL\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/justanotherelectronicsblog.com\/#website\",\"url\":\"https:\/\/justanotherelectronicsblog.com\/\",\"name\":\"jaeblog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/justanotherelectronicsblog.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/justanotherelectronicsblog.com\/#\/schema\/person\/d77e39721321c4a472b49909a8f1982b\",\"name\":\"riktw\",\"url\":\"https:\/\/justanotherelectronicsblog.com\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"A one page CPU in SpinalHDL - jaeblog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/justanotherelectronicsblog.com\/?p=543","og_locale":"en_US","og_type":"article","og_title":"A one page CPU in SpinalHDL - jaeblog","og_description":"FPGA&#8217;s are fun and interesting devices, sadly the usual ways of programming them, Verilog and VHDL, are tedious and annoying to work with. Sadly most FPGA tools support only these languages. Luckily a few projects exist to make a nicer and more fun language to work with, which are translated to either Verilog or VHDL [&hellip;]","og_url":"https:\/\/justanotherelectronicsblog.com\/?p=543","og_site_name":"jaeblog","article_published_time":"2019-04-28T15:12:47+00:00","article_modified_time":"2019-04-28T15:12:49+00:00","author":"riktw","twitter_card":"summary_large_image","twitter_misc":{"Written by":"riktw","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/justanotherelectronicsblog.com\/?p=543#article","isPartOf":{"@id":"https:\/\/justanotherelectronicsblog.com\/?p=543"},"author":{"name":"riktw","@id":"https:\/\/justanotherelectronicsblog.com\/#\/schema\/person\/d77e39721321c4a472b49909a8f1982b"},"headline":"A one page CPU in SpinalHDL","datePublished":"2019-04-28T15:12:47+00:00","dateModified":"2019-04-28T15:12:49+00:00","mainEntityOfPage":{"@id":"https:\/\/justanotherelectronicsblog.com\/?p=543"},"wordCount":685,"commentCount":2,"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/justanotherelectronicsblog.com\/?p=543#respond"]}]},{"@type":"WebPage","@id":"https:\/\/justanotherelectronicsblog.com\/?p=543","url":"https:\/\/justanotherelectronicsblog.com\/?p=543","name":"A one page CPU in SpinalHDL - jaeblog","isPartOf":{"@id":"https:\/\/justanotherelectronicsblog.com\/#website"},"datePublished":"2019-04-28T15:12:47+00:00","dateModified":"2019-04-28T15:12:49+00:00","author":{"@id":"https:\/\/justanotherelectronicsblog.com\/#\/schema\/person\/d77e39721321c4a472b49909a8f1982b"},"breadcrumb":{"@id":"https:\/\/justanotherelectronicsblog.com\/?p=543#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/justanotherelectronicsblog.com\/?p=543"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/justanotherelectronicsblog.com\/?p=543#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/justanotherelectronicsblog.com\/"},{"@type":"ListItem","position":2,"name":"A one page CPU in SpinalHDL"}]},{"@type":"WebSite","@id":"https:\/\/justanotherelectronicsblog.com\/#website","url":"https:\/\/justanotherelectronicsblog.com\/","name":"jaeblog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/justanotherelectronicsblog.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/justanotherelectronicsblog.com\/#\/schema\/person\/d77e39721321c4a472b49909a8f1982b","name":"riktw","url":"https:\/\/justanotherelectronicsblog.com\/?author=1"}]}},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/justanotherelectronicsblog.com\/index.php?rest_route=\/wp\/v2\/posts\/543","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/justanotherelectronicsblog.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/justanotherelectronicsblog.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/justanotherelectronicsblog.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/justanotherelectronicsblog.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=543"}],"version-history":[{"count":13,"href":"https:\/\/justanotherelectronicsblog.com\/index.php?rest_route=\/wp\/v2\/posts\/543\/revisions"}],"predecessor-version":[{"id":556,"href":"https:\/\/justanotherelectronicsblog.com\/index.php?rest_route=\/wp\/v2\/posts\/543\/revisions\/556"}],"wp:attachment":[{"href":"https:\/\/justanotherelectronicsblog.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=543"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/justanotherelectronicsblog.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=543"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/justanotherelectronicsblog.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=543"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}