{"id":1008,"date":"2014-12-05T20:21:13","date_gmt":"2014-12-05T20:21:13","guid":{"rendered":"http:\/\/retroramblings.net\/?p=1008"},"modified":"2014-12-05T23:21:20","modified_gmt":"2014-12-05T23:21:20","slug":"a-closer-look-at-the-osdcontrol-module-2","status":"publish","type":"post","link":"http:\/\/retroramblings.net\/?p=1008","title":{"rendered":"A closer look at the OSD\/Control Module"},"content":{"rendered":"<p><strong>Part 2 &#8211; a simple test core<\/strong><\/p>\n<p>To demonstrate how the control module is built, we need a core to which we can add the control module.\u00a0 In the interests of keeping the project as simple as possible and avoiding needless distractions, I&#8217;ve started a new project for this purpose, which can be found on github at <a href=\"https:\/\/github.com\/robinsonb5\/CtrlModuleTutorial\">https:\/\/github.com\/robinsonb5\/CtrlModuleTutorial<\/a><\/p>\n<p>I shall tag this at key points, and at the time of writing there are two tags in place.<br \/>\nTo play with this, check out a local copy of the core, like so:<\/p>\n<pre>\r\n> git clone https:\/\/github.com\/robinsonb5\/CtrlModuleTutorial.git\r\n> cd CtrlModuleTutorial\r\n> git submodule init\r\n> git submodule update\r\n> git checkout &lt;tag name&gt;\r\n<\/pre>\n<p>The first tag, called &#8220;StartingPoint&#8221; contains a VGA test pattern generator for the DE1 board, which has four slightly different test patterns selectable by the DE1&#8217;s switches.\u00a0 In the coming parts I shall show how to eliminate the switches and replace them with an On Screen Display.<br \/>\n<!--more--><br \/>\nThe project directory contains DE1-specific files in Board\/de1, Project-specific HDL files in RTL\/, and Quartus project files in fpga\/de1<\/p>\n<p>The second tag, called &#8220;Step1&#8221; adds a simplified control module, and all files specific to the Control Module will be in the directory CtrlModule.<\/p>\n<p>The module is instantiated like so:<\/p>\n<pre>MyCtrlModule : entity work.CtrlModule\r\n\tport map (\r\n\t\tclk =&gt; CLK,\r\n\t\treset_n =&gt; reset,\r\n\r\n\t\t-- DIP switches\r\n\t\tdipswitches(1 downto 0) =&gt; testpattern -- Replaces previous binding from the physical DIP switches\r\n\t);\r\n<\/pre>\n<p>In this simplified form the control module requires only a clock and reset signal, and supplies an output signal which is under CPU control.\u00a0 The control module in its simplified form looks like this:<\/p>\n<pre>\r\nlibrary IEEE;\r\nuse IEEE.STD_LOGIC_1164.ALL;\r\nuse IEEE.numeric_std.ALL;\r\n\r\nlibrary work;\r\nuse work.zpupkg.ALL;\r\n\r\nentity CtrlModule is\r\n\tgeneric (\r\n\t\tsysclk_frequency : integer := 1000 -- Sysclk frequency * 10\r\n\t);\r\n\tport (\r\n\t\tclk \t\t\t: in std_logic;\r\n\t\treset_n \t: in std_logic;\r\n\r\n\t\t-- DIP switches\r\n\t\tdipswitches : out std_logic_vector(15 downto 0)\r\n\t);\r\nend entity;\r\n\r\narchitecture rtl of CtrlModule is\r\n\r\n-- ZPU signals\r\nconstant maxAddrBit : integer := 20; -- Optional - defaults to 32 - but helps keep the logic element count down.\r\nsignal mem_busy           : std_logic;\r\nsignal mem_read             : std_logic_vector(wordSize-1 downto 0);\r\nsignal mem_write            : std_logic_vector(wordSize-1 downto 0);\r\nsignal mem_addr             : std_logic_vector(maxAddrBit downto 0);\r\nsignal mem_writeEnable      : std_logic; \r\nsignal mem_readEnable       : std_logic;\r\nsignal mem_hEnable      : std_logic; \r\nsignal mem_bEnable      : std_logic; \r\n\r\nsignal zpu_to_rom : ZPU_ToROM;\r\nsignal zpu_from_rom : ZPU_FromROM;\r\n\r\nbegin\r\n<\/pre>\n<p>The interface between the ZPUFlex and its ROM is encapsulated in the ZPU_[To|From]ROM types, so we don&#8217;t have to worry about those, apart from making sure that they&#8217;re connected to the ROM &#8211; but any accesses that fall outside the ROM will be notified by the CPU using the mem_* signals.  At this stage we won&#8217;t add any SDRAM access or suchlike, but we do need to implement some hardware registers for the ZPU&#8217;s ROM to poke.  When the ZPU needs to access a register, it will place the address on the mem_addr signal, then assert either mem_readEnable or mem_writeEnable for a single cycle.  Our circuitry needs to respond by acting upon the data on mem_write or placing data on mem_read, then driving mem_busy low for a single cycle.<\/p>\n<p>The mem_hEnable and mem_bEnable are asserted as well as the read\/writeEnable signals if the CPU&#8217;s performing a halfword (16-bit) or byte access.  At this stage we can ignore those.<\/p>\n<p>Here we instantiate the ROM itself, which is built by the makefile in CtrlROM\/Firmware.  CtrlROM\/Firmware\/CtrlROM_ROM.vhd will need to be added to the project.<br \/>\nThe ROM as it stands simply cycles between writing &#8220;00&#8221;, &#8220;01&#8221;, &#8220;10&#8221; and &#8220;11&#8221; to a hardware register, which sends the written value to the host core, changing which test pattern is displayed.<\/p>\n<pre>\r\n-- ROM\r\n\r\n\tmyrom : entity work.CtrlROM_ROM\r\n\tgeneric map\r\n\t(\r\n\t\tmaxAddrBitBRAM => 13 -- This needs to match the signal of the same name in the ZPU's instantiation.\r\n\t)\r\n\tport map (\r\n\t\tclk => clk,\r\n\t\tfrom_zpu => zpu_to_rom,\r\n\t\tto_zpu => zpu_from_rom\r\n\t);\r\n<\/pre>\n<p>Now we instantiate the ZPU itself.  (The ZPUFlex requires that ZPUFlex\/RTL\/zpu_core_flex.vhd and ZPUFlex\/RTL\/zpu_pkg.vhd are added to the project.)<br \/>\nThere are a number of options that can be set here, mostly relating to which optional instructions are enabled.  If we want to reduce the logic-element footprint of the ZPU we can do so by disabling these &#8211; but if we do, then we have to include emulation code in the ROM, adding to the block RAM footprint, so it&#8217;s a case of having to strike a balance.<\/p>\n<pre>\t\r\n-- Main CPU\r\n-- We instantiate the CPU with the optional instructions enabled, which allows us to reduce\r\n-- the size of the ROM by leaving out emulation code.\r\n\tzpu: zpu_core_flex\r\n\tgeneric map (\r\n\t\tIMPL_MULTIPLY => true,\r\n\t\tIMPL_COMPARISON_SUB => true,\r\n\t\tIMPL_EQBRANCH => true,\r\n\t\tIMPL_STOREBH => true,\r\n\t\tIMPL_LOADBH => true,\r\n\t\tIMPL_CALL => true,\r\n\t\tIMPL_SHIFT => true,\r\n\t\tIMPL_XOR => true,\r\n\t\tREMAP_STACK => false, -- We're not using SDRAM so no need to remap the Boot ROM \/ Stack RAM\r\n\t\tEXECUTE_RAM => false, -- We don't need to execute code from external RAM.\r\n\t\tmaxAddrBit => maxAddrBit,\r\n\t\tmaxAddrBitBRAM => 13\r\n\t)\r\n\tport map (\r\n\t\tclk                 => clk,\r\n\t\treset               => not reset_n,\r\n\t\tin_mem_busy         => mem_busy,\r\n\t\tmem_read            => mem_read,\r\n\t\tmem_write           => mem_write,\r\n\t\tout_mem_addr        => mem_addr,\r\n\t\tout_mem_writeEnable => mem_writeEnable,\r\n\t\tout_mem_hEnable     => mem_hEnable,\r\n\t\tout_mem_bEnable     => mem_bEnable,\r\n\t\tout_mem_readEnable  => mem_readEnable,\r\n\t\tfrom_rom => zpu_from_rom,\r\n\t\tto_rom => zpu_to_rom\r\n\t);\r\n<\/pre>\n<p>So far so good.<br \/>\nNow to define our hardware register.  We&#8217;re actually fairly free to place this anywhere we like in the memory map, provided we don&#8217;t clash with the program ROM, but there&#8217;s a ZPU convention that IO should happen in the top half of the memory map.  Because of the way the instruction set works, it&#8217;s more efficient for registers to be at the very top of the memory map, so we&#8217;ll place our &#8220;dipswitch&#8221; register at 0xfffffffc.  (We&#8217;ve actually configured the ZPU to use fewer than 32-bits for addressing, and we&#8217;re not going to do a complete address decoding anyway, but the ROM itself won&#8217;t care, so the ROM&#8217;s source code defines the register as 0xfffffffc.)<\/p>\n<p>I&#8217;m actually going to divide the upper memory space in 256-byte chunks and decode them separately, which will make life easier in future parts.  For now it might look a bit odd to be decoding the first &#8220;F&#8221; separately from the &#8220;FC&#8221;, but&#8230;<\/p>\n<pre>\r\nprocess(clk)\r\nbegin\r\n\tif reset_n='0' then\r\n\r\n\telsif rising_edge(clk) then\r\n\t\tmem_busy<='1';\r\n\t\t\r\n\t\t-- Write from CPU?\r\n\t\tif mem_writeEnable='1' then\r\n\t\t\t-- we decode just a partial address to save logic elements.\r\n\t\t\tcase mem_addr(maxAddrBit)&#038;mem_addr(10 downto 8) is\r\n\t\t\t\twhen X\"F\" =>\t-- Peripherals at 0xFFFFFF00\r\n\t\t\t\t\tcase mem_addr(7 downto 0) is\t\t\t\t\t\t\t\r\n\t\t\t\t\t\twhen X\"FC\" => -- Host SW\r\n\t\t\t\t\t\t\tmem_busy<='0';\r\n\t\t\t\t\t\t\tdipswitches<=mem_write(15 downto 0);\r\n\r\n\t\t\t\t\t\twhen others => -- Prevent a hang if we accidentally access a register that doesn't exist.\r\n\t\t\t\t\t\t\tmem_busy<='0';\r\n\t\t\t\t\t\t\tnull;\r\n\t\t\t\t\tend case;\r\n\t\t\t\twhen others =>\r\n\t\t\t\t\tmem_busy<='0';\r\n\t\t\tend case;\r\n\r\n\t\t-- Read from CPU?\r\n\t\telsif mem_readEnable='1' then\r\n\t\t\tcase mem_addr(maxAddrBit)&#038;mem_addr(10 downto 8) is\r\n\t\t\t\twhen X\"F\" =>\t-- Peripherals\r\n\t\t\t\t\tcase mem_addr(7 downto 0) is\r\n\t\t\t\t\t\t-- We don't have any readable registers yet.\r\n\t\t\t\t\t\twhen others =>  -- Prevent a hang if we accidentally access a register that doesn't exist.\r\n\t\t\t\t\t\t\tmem_busy<='0';\r\n\t\t\t\t\t\t\tnull;\r\n\t\t\t\t\tend case;\r\n\r\n\t\t\t\twhen others =>\r\n\t\t\t\t\tmem_busy<='0';\r\n\t\t\tend case;\r\n\t\tend if;\r\n\t\t\r\n\tend if; -- rising-edge(clk)\r\n\r\nend process;\r\n\t\r\nend architecture;\r\n<\/pre>\n<p>The complete project displays one of four test patterns, and cycles rapidly between them under the control of the ZPU.<\/p>\n<p>In the coming parts, we'll add interrupt handling, PS\/2 keyboard control, the On-screen Display itself and add a menu system.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Part 2 &#8211; a simple test core To demonstrate how the control module is built, we need a core to which we can add the control module.\u00a0 In the interests of keeping the project as simple as possible and avoiding &hellip; <a href=\"http:\/\/retroramblings.net\/?p=1008\">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-1008","post","type-post","status-publish","format-standard","hentry","category-fpga","category-hardware"],"_links":{"self":[{"href":"http:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/1008","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1008"}],"version-history":[{"count":6,"href":"http:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/1008\/revisions"}],"predecessor-version":[{"id":1014,"href":"http:\/\/retroramblings.net\/index.php?rest_route=\/wp\/v2\/posts\/1008\/revisions\/1014"}],"wp:attachment":[{"href":"http:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1008"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1008"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/retroramblings.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1008"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}