Possible to synthesize a "byte-enable" function for the RAM blocks?


earlz

Recommended Posts

Hello, I'm trying to synthesize a single port RAM block that has 2 write-enable bits. This is so that I can choose to either write 16 bits or 8 bits at a time. My VHDL code is as follows:



library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_arith.all;
use IEEE.NUMERIC_STD.ALL;
use ieee.std_logic_unsigned.all;



entity blockram is
  port(
    Address: in std_logic_vector(7 downto 0); --memory address
    WriteEnable: in std_logic_vector(1 downto 0); --write 1 byte at a time option
    Enable: in std_logic;
    Clock: in std_logic;
    DataIn: in std_logic_vector(15 downto 0);
    DataOut: out std_logic_vector(15 downto 0)
  );
end blockram;

architecture Behavioral of blockram is
    type ram_type is array (255 downto 0) of std_logic_vector (15 downto 0);
    signal RAM: ram_type; attribute ram_style: string;
    attribute ram_style of RAM: signal is "block";
    signal di0, di1, do0, do1: std_logic_vector(7 downto 0); --data inputs and outputs for byte-enable
begin
  process (WriteEnable,DataIn)
  begin
    if WriteEnable(0) = '1' then
      di0 <= DataIn(7 downto 0);
    else
      di0 <= RAM(conv_integer(Address))(7 downto 0);
      do0 <= RAM(conv_integer(Address))(7 downto 0);
    end if;
    if WriteEnable(1)= '1' then
      di1 <= DataIn(15 downto 8);
    else
      di1 <= RAM(conv_integer(Address))(15 downto 8);
      do1 <= RAM(conv_integer(Address))(15 downto 8);
    end if;
  end process;
  process (Clock)
  begin
    if rising_edge(Clock) then
      if Enable = '1' then
          DataOut <= do1 & do0;
          RAM(conv_integer(Address)) <= di1 & di0;
      end if;
    end if;
  end process;

end Behavioral;

When I try synthesizing this however I get an error/warning: "Cannot use block RAM resources for signal <Mram_RAM>. Please check that the RAM contents is read synchronously."

Am I doing something wrong? I'm trying to follow the template provided inside WebPack, but it always synthesizes to use LUTs instead of RAM. Does the Spartan3E 250K RAM blocks support this kind of thing?

Link to comment
Share on other sites

Hello, I'm trying to synthesize a single port RAM block that has 2 write-enable bits. This is so that I can choose to either write 16 bits or 8 bits at a time. My VHDL code is as follows:



library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_arith.all;
use IEEE.NUMERIC_STD.ALL;
use ieee.std_logic_unsigned.all;



entity blockram is
  port(
    Address: in std_logic_vector(7 downto 0); --memory address
    WriteEnable: in std_logic_vector(1 downto 0); --write 1 byte at a time option
    Enable: in std_logic;
    Clock: in std_logic;
    DataIn: in std_logic_vector(15 downto 0);
    DataOut: out std_logic_vector(15 downto 0)
  );
end blockram;

architecture Behavioral of blockram is
    type ram_type is array (255 downto 0) of std_logic_vector (15 downto 0);
    signal RAM: ram_type; attribute ram_style: string;
    attribute ram_style of RAM: signal is "block";
    signal di0, di1, do0, do1: std_logic_vector(7 downto 0); --data inputs and outputs for byte-enable
begin
  process (WriteEnable,DataIn)
  begin
    if WriteEnable(0) = '1' then
      di0 <= DataIn(7 downto 0);
    else
      di0 <= RAM(conv_integer(Address))(7 downto 0);
      do0 <= RAM(conv_integer(Address))(7 downto 0);
    end if;
    if WriteEnable(1)= '1' then
      di1 <= DataIn(15 downto;
    else
      di1 <= RAM(conv_integer(Address))(15 downto;
      do1 <= RAM(conv_integer(Address))(15 downto;
    end if;
  end process;
  process (Clock)
  begin
    if rising_edge(Clock) then
      if Enable = '1' then
          DataOut <= do1 & do0;
          RAM(conv_integer(Address)) <= di1 & di0;
      end if;
    end if;
  end process;

end Behavioral;

When I try synthesizing this however I get an error/warning: "Cannot use block RAM resources for signal <Mram_RAM>. Please check that the RAM contents is read synchronously."

Am I doing something wrong? I'm trying to follow the template provided inside WebPack, but it always synthesizes to use LUTs instead of RAM. Does the Spartan3E 250K RAM blocks support this kind of thing?

Hi Earlz,

Your process that reads the BRAM values isn't reading them synchronously, but rather concurrently; you want to read data out of the BRAM in a process that only has the clock in its sensitivity list. Thats what the error is warning you about.

More importantly, though, you can't implement byte enable natively in BRAMS on a Virtex 3e, as far as I can tell; from the Xilinx BRAM Data Sheet:

  • RAMB primitive in architectures prior to Virtex-4 only have a single write enable per port. Thus if byte-write enable is required on a 32 bit data port (C_NUM_WE=4), these architectures will use a minimum of 4 BRAM primitives.

If you can deal with a few cycle latency between reads and writes, you can implement byte-enable in logic around the BRAM, but it would really matter on your application design - let us know more details, and we can try and help you with a good fix.

-Adrian

Link to comment
Share on other sites

Additionally, looking at the code again, do0 and do1 are both going to implemented as latches - they aren't set on every branch of the conditions. You are going to want to set them to some value when WriteEnable(0) and WriteEnable(1) are true, not just when they are false.

Link to comment
Share on other sites

I've changed my code quite a bit and no warning when synthesizing now :)  I've been trying at this all day only to discover I'm trying to implement the impossible :P

Anyway, I really can't have the latency in my application (a tiny 8-bit CPU) so I decided to implement it as 2 banks of 8-bit block RAM and writing to them selectively on WriteEnable(x). I believe this is the best solution in my case as I don't plan on using many of my block RAMs anyway (and 2 out of 12 isn't too much in my eyes)

Edit:

Also, just for reference, here is my working code, though I plan on changing how DataOut synchronizes with Address and WriteEnables


entity blockram is
  port(
    Address: in std_logic_vector(7 downto 0); --memory address
    WriteEnable: in std_logic_vector(1 downto 0); --write 1 byte at a time option
    Enable: in std_logic;
    Clock: in std_logic;
    DataIn: in std_logic_vector(15 downto 0);
    DataOut: out std_logic_vector(15 downto 0)
  );
end blockram;

architecture Behavioral of blockram is
    type ram_type is array (255 downto 0) of std_logic_vector (7 downto 0);
    signal RAM0: ram_type; --Spartan 3Es don't natively support byte-wide write enables, so we'll just emulate it with 2 banks of RAM
    signal RAM1: ram_type;
    signal di0, di1: std_logic_vector(7 downto 0);
    signal do : std_logic_vector(15 downto 0);
begin
  di0 <= DataIn(7 downto 0) when WriteEnable(0)='1' else do(7 downto 0);
    di1 <= DataIn(15 downto 8) when WriteEnable(1)='1' else do(15 downto 8);
  process (Clock)
  begin
    if rising_edge(Clock) then
      if Enable = '1' then
        if WriteEnable(0)='1' then
          RAM0(conv_integer(Address)) <= di0;
        else
          do(7 downto 0) <= RAM0(conv_integer(Address)) ;
        end if;
        if WriteEnable(1)='1' then
          RAM1(conv_integer(Address)) <= di1;
        else
          do(15 downto 8) <= RAM1(conv_integer(Address));
        end if;
      end if;
    end if;
  end process;
  DataOut <= do;
end Behavioral;

Link to comment
Share on other sites


begin
  di0 <= DataIn(7 downto 0) when WriteEnable(0)='1' else do(7 downto 0);
    di1 <= DataIn(15 downto when WriteEnable(1)='1' else do(15 downto;
  process (Clock)
  begin
    if rising_edge(Clock) then
      if Enable = '1' then
        if WriteEnable(0)='1' then
          RAM0(conv_integer(Address)) <= di0;
        else
          do(7 downto 0) <= RAM0(conv_integer(Address)) ;
        end if;
        if WriteEnable(1)='1' then
          RAM1(conv_integer(Address)) <= di1;
        else
          do(15 downto <= RAM1(conv_integer(Address));
        end if;
      end if;
    end if;
  end process;
  DataOut <= do;
end Behavioral;

The following seems a little clearer, at least to me:



begin
  process (Clock)
  begin
    if rising_edge(Clock) then
      if Enable = '1' then
        if WriteEnable(0)='1' then
          RAM0(conv_integer(Address)) <= DataIn(7 downto 0);
        else
          DataOut(7 downto 0) <= RAM0(conv_integer(Address)) ;
        end if;
        if WriteEnable(1)='1' then
          RAM1(conv_integer(Address)) <= DataIn(15 downto 8);
        else
          DataOut(15 downto 8) <= RAM1(conv_integer(Address));
        end if;
      end if;
    end if;
  end process;
end Behavioral;

Link to comment
Share on other sites

earlz,

It might be easier to use the memory wizard to generate a memory block for you at first.

To use the memory wizard:

  • Add "New Source" to your project
  • Choose "IP (CORE Generator & Architecture Wizard)
  • Choose "Memories & Storage Elements\RAMs & ROMs\Block Memory Generator"

That will get you started with the wizard.

Jack.

Link to comment
Share on other sites

  • 2 weeks later...

You can construct something that looks like a 16 bit RAM with byte enable using the dual-port feature of Block RAM.

Create a dual-port block ram, with a data width of 8 bits. Use the "Port A" to handle the lower byte, and "Port B" for the upper byte.

Set the address for port A to be  'your_address & "0''. Set the address for port B to be 'your_address & "1"'.

Concatenate the data outputs for portA and portB to form your 16 bit "dout" word.

Assign the  lower and upper  bytes of "din" (your data to be written) to the DIN ports of Port A and Port B.

Set the "WE" of Port A to be "WriteEnable and LowerByteEnable", and use "WriteEnable and UpperByteEnable" for the "WE" on Port B.

Link to comment
Share on other sites

earlz,

It might be easier to use the memory wizard to generate a memory block for you at first.

To use the memory wizard:

  • Add "New Source" to your project
  • Choose "IP (CORE Generator & Architecture Wizard)
  • Choose "Memories & Storage Elements\RAMs & ROMs\Block Memory Generator"

That will get you started with the wizard.

Jack.

But then simulation without Xilinx tools is impossible. MY workflow is like this:

1. Write VHDL code in Kate

2. Simulate VHDL code using ghdl and gtkwave

3. Repeat

...

And then every once in a while I'll put all of my code into xilinx tools so I can test that they synthesize correctly. I find the Xilinx tools extremely cumbersome so this is why I'd prefer not to use them when I don't have to

You can construct something that looks like a 16 bit RAM with byte enable using the dual-port feature of Block RAM.

Create a dual-port block ram, with a data width of 8 bits. Use the "Port A" to handle the lower byte, and "Port B" for the upper byte.

Set the address for port A to be  'your_address & "0''. Set the address for port B to be 'your_address & "1"'.

Concatenate the data outputs for portA and portB to form your 16 bit "dout" word.

Assign the  lower and upper  bytes of "din" (your data to be written) to the DIN ports of Port A and Port B.

Set the "WE" of Port A to be "WriteEnable and LowerByteEnable", and use "WriteEnable and UpperByteEnable" for the "WE" on Port B.

I tried to get something like this to work, but I never could get XST to synthesize it correctly, so I gave up and instead used 2 8-bit block RAMs

Link to comment
Share on other sites

I tried to get something like this to work, but I never could get XST to synthesize it correctly, so I gave up and instead used 2 8-bit block RAMs

Currently, as far as I know, you cannot infer the following constructs with BRAMs - dual clock domains, byte enables, and different word sizes between reads and writes. If you were trying to infer a BRAM with a byte enable, the synthesis tool may have been unable to construct it properly in order to replace it with a BRAM primitive. Have you thought about have a different file for simulation / testing then you have for synthesis / P&R? Test your code with your inferred BRAM with byte enable, then switch over to using a primitive instantiation / pcore when you actually need to synthesis.

I agree with you about ISE, by the way, it is quite clunky.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.