Skip to content

Latest commit

 

History

History
executable file
·
247 lines (169 loc) · 8.44 KB

content.asc

File metadata and controls

executable file
·
247 lines (169 loc) · 8.44 KB

Using the FPGA’s internal RAM

As well as the resources required for implementing digital logic, FPGAs also have a small amount of RAM built in. This RAM is very useful and can meet the entire RAM needs of many projects.

Each Vendor’s RAM blocks have differing capabilities and is configured differently, so it makes sense to use this as a way of introducing the IP Core Generator.

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?

On the Spartan 3E each RAM block has 18 kilobits of RAM, and can be presented to the system in different widths. Eighteen kilobits is an odd size, but it is designed that way to allow 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 simplify 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 building 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:

m11s1

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

m11s2

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

m11s3

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

m11s4

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:

m11s5

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":

m11s6

Copy and paste the useful 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(30 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:

m11s7

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:

m11s8

Select that we want a Single Port ROM, then click "Next":

m11s9

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

m11s10

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 its instantiation template.

m11s11

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:

m11s12

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,

Create a sample file of 8 bit binary values - make the '1' bits zig-zag from left to right, or some other pattern - the more lines the merrier. Call it "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.

m11s13

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 lengthy 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. Search for "Xilinx data2mem" on Google.

The finishing touches

NET LEDs(7) LOC = "P5"  | IOSTANDARD=LVCMOS25;
NET LEDs(6) LOC = "P9"  | IOSTANDARD=LVCMOS25;
NET LEDs(5) LOC = "P10" | IOSTANDARD=LVCMOS25;
NET LEDs(4) LOC = "P11" | IOSTANDARD=LVCMOS25;
NET LEDs(3) LOC = "P12" | IOSTANDARD=LVCMOS25;
NET LEDs(2) LOC = "P15" | IOSTANDARD=LVCMOS25;
NET LEDs(1) LOC = "P16" | IOSTANDARD=LVCMOS25;
NET LEDs(0) LOC = "P17" | IOSTANDARD=LVCMOS25;

NET "clk" LOC="P89" | IOSTANDARD=LVCMOS25 | PERIOD=31.25ns;

Rebuild the project, download it and watch the lights!