Module 12

From Hamsterworks Wiki!

Jump to: navigation, search

Please make sure that you have completed the earlier modules of FPGA course.

Having problems with your code? Do I need to change something? Want a reference solution to the project? Email me at "nospam-hamster@snap.net.nz", after removing the "nospam-" bit. I'll try to get back to you in a day or so.

Contents

Aims of module

  • Use the IP Core Generator to create two components - a counter and a block RAM acting as a ROM.
  • Wire them together and to the LEDs
  • Set predefined data values into the ROM

This project is very "GUI" based - unlike the last module it is very much a walk through.

What is Block RAM? What can it do?

The Spartan 3 FPGAs (and I assume most others) have super-flexible RAM blocks. On the Spartan 3E each of these have 18 kilobits of RAM, and can be presented to the system in different widths.

Eighteen kilobits is an odd size - but it is used as it allows for either parity or ECC bits to be stored.

The most common configuration I've used is 2048 words of 8 bits, but it can be configured as one of either 16k x 1bit, 8k x 2 bits, 4k x 4 bits, 2k x 8 bits, 2k x 9 bits, 1k x 16 bits, 1k x 18 bits, 512 x 32 bits, 512 x 36 bits or 256 x 72 bits.

The blocks are especially useful as they are dual-port - there are two independent address, read and write ports that simplifies many designs (such as building FIFOs).

See http://www.xilinx.com/support/documentation/application_notes/xapp463.pdf for complete documentation and some very cunning uses for BRAM.

Using the Core Generator with BRAM

Using the Core generator makes build BRAM components very simple - and if required it also transparently constructs larger memories out of multiple primitives. In the project you will use the core generator as creating them directly in VHDL is quite cumbersome and complex.

Preparing the project

  • Create a new project - I called mine "flashylights".
  • Add a module which has the clock signal as the only input and the eight LEDs as the output

You should get a module that looks like this:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity FlashyLights is
    Port ( clk : in  STD_LOGIC;
           LEDs : out  STD_LOGIC_VECTOR (7 downto 0));
end FlashyLights;

architecture Behavioral of FlashyLights is
begin

end Behavioral;

We now need to add a couple of wizard generated components.

Using the IP core generator

Add a new source file to the project:

M12s1.png

Select "IP" and call the module counter30 - it will be a 30 bit counter

M12s2.png

You will be presented with the "Select IP" dialogue box. Tick the "Only IP compatible with chosen part" tickbox:

M12s3.png

Navigate down into "Basic Elements"/"Binary Counter" and click next

M12s4.png

After a long delay the options for Binary Counter will appear. Set the "Output Width" to 30 - and if you want, click on the "Datasheet" button:

M12s5.png

Then click "Generate".

In the Hierarchy window you will now have a "counter30" component. Click on it and then under the Processes tree select "View HDL Instantiation Template":

M12s6.png

Copy and paste the userful bits into your top level project - add a signal "counter" to be connected to the output of the counter. Here's the completed source:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity FlashyLights is
    Port ( clk : in  STD_LOGIC;
           LEDs : out  STD_LOGIC_VECTOR (7 downto 0));
end FlashyLights;

architecture Behavioral of FlashyLights is
  COMPONENT counter30
    PORT (
      clk : IN STD_LOGIC;
      q : OUT STD_LOGIC_VECTOR(29 DOWNTO 0)
    );
  END COMPONENT;

  signal count : STD_LOGIC_VECTOR(29 downto 0);  
begin

addr_counter : counter30
  PORT MAP (
    clk => clk,
    q => count
  );
 
end Behavioral;

Adding the ROM component

Add another new IP module called "memory", but this time select the Block Memory Generator:

M12s7.png

The block memory generator has 6 pages of settings - at the moment we only need to enter things on the first three.

Just click "next" on the first screen:

M12s8.png

Select that we want a single port ROM, then click "Next":

M12s9.png

Set "Read Width" to 8 - we have eight LEDs to light. Set the "Read Depth" to 1024. Click "Next":

M12s10.png

Don't bother going through the rest of the screens - they don't apply at the moment - just click "Generate"

You will now have another component, and you can view it's instantiation template.

M12s11.png

Add it to the source, connecting the top 10 bits of the counter to the ROM's address bus (addra), and the data bus (douta) to the LEDs:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity FlashyLights is
    Port ( clk : in  STD_LOGIC;
           LEDs : out  STD_LOGIC_VECTOR (7 downto 0));
end FlashyLights;

architecture Behavioral of FlashyLights is
  COMPONENT counter30
    PORT (
      clk : IN STD_LOGIC;
      q : OUT STD_LOGIC_VECTOR(29 DOWNTO 0)
    );
  END COMPONENT;
  
  COMPONENT memory
  PORT (
    clka : IN STD_LOGIC;
    addra : IN STD_LOGIC_VECTOR(9 DOWNTO 0);
    douta : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
  );
  END COMPONENT;

  signal count : STD_LOGIC_VECTOR(29 downto 0);  
begin

addr_counter : counter30
  PORT MAP (
    clk => clk,
    q => count
  );
 
rom_memory: memory
  PORT MAP (
    clka => clk,
    addra => count(29 downto 20),
    douta => LEDs
  );
end Behavioral;

Once built, you can view the RTL schematic - looks as you would expect:

M12s12.png

Setting the contents of the ROM

At the moment the ROM is blank (all '0's). When the FPGA is configured the contents of the block RAM can be set to values that are predefined in the configuration bit stream.

Page 4 of the block memory generator gives you the option to set the contents of the ROM using a ".coe" file. Here's enough of the file that you will be able to write your own from scratch:

memory_initialization_radix=10;
memory_initialization_vector=
128,
128,
127,
127,
127,

Here's another, using binary (as memory_initialization_radix=2) for a memory with a data width of 15:

memory_initialization_radix=2;
memory_initialization_vector=
001110000000001,
010110000000010,
000010000000011,
000010000000100,
000010000000101,
000010000000110,

But for this project, here is a sample file that you can download and save File:Flashy.coe

Edit the "memory" component (just double click it in the Hierarchy tree) and skip through to Page 4. Set the initialisation file to flashy.coe.

M12s13.png

It is always a good idea to click on the "show" button - it will give you a warning if your '.coe' file is not correct. Click the 'Generate' button to update the IP module.

As an aside there are other ways to do this, allowing you to inject contents (e.g. maybe bootloader) after the '.bit' file is built. This allows you to avoid a length rebuild of a whole project just to change the initial values in a BRAM. It is also a good way to allow an end-user to customise the '.bit' file without providing access to your source code.

The finishing touches

Now add the Implementation Constraints file (this file is for the Nexys2 - I will update with Basys2 constraints soon):

NET "clk"   LOC = "B8"; 

NET "Leds<0>"  LOC = "J14"; 
NET "Leds<1>"  LOC = "J15"; 
NET "Leds<2>"  LOC = "K15"; 
NET "Leds<3>"  LOC = "K14"; 
NET "Leds<4>"  LOC = "E17";
NET "Leds<5>"  LOC = "P15"; 
NET "Leds<6>"  LOC = "F4";
NET "Leds<7>"  LOC = "R4";

Rebuild the project, download it and watch the lights!

Ready to carry on?

Click here to carry on to the next module.

Personal tools